diff --git a/.github/scripts/deploy-website.sh b/.github/scripts/deploy-website.sh deleted file mode 100755 index 9528d8c8..00000000 --- a/.github/scripts/deploy-website.sh +++ /dev/null @@ -1,166 +0,0 @@ -#!/usr/bin/env bash -# Lädt den Ordner website/ per FTPS (oder SFTP) zu All-Inkl hoch. -# -# Auf dem Server wird nur gelöscht, was ein früherer Lauf dieses Skripts selbst -# hochgeladen hat (Liste in .visustruct-deploy-manifest.txt im Zielordner), und -# veraltete Build-Dateien in app/assets/. -# Fremde Dateien im Zielordner und alles außerhalb davon bleiben unangetastet. -# -# Umgebung: -# DEPLOY_HOST, DEPLOY_USER, LFTP_PASSWORD Pflicht -# DEPLOY_PROTOCOL auto (Standard: erst SFTP, dann FTPS), sftp oder ftps -# DEPLOY_DIR Zielordner relativ zum Login; ohne Angabe visustruct.org -# oder der Startordner, je nachdem, wo die Webseite liegt -# SOURCE_DIR Standard website -# DRY_RUN=1 nur anzeigen, nichts hochladen oder löschen -set -euo pipefail - -# Beim Einfügen ins Secret-Feld rutscht leicht ein Zeilenumbruch mit hinein -DEPLOY_USER="$(printf '%s' "${DEPLOY_USER:-}" | tr -d '\r\n' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')" -LFTP_PASSWORD="$(printf '%s' "${LFTP_PASSWORD:-}" | tr -d '\r\n')" -: "${DEPLOY_HOST:?DEPLOY_HOST fehlt}" -: "${DEPLOY_USER:?DEPLOY_USER fehlt}" -: "${LFTP_PASSWORD:?LFTP_PASSWORD fehlt}" -export LFTP_PASSWORD - -PROTOCOL="${DEPLOY_PROTOCOL:-auto}" -REMOTE_DIR="${DEPLOY_DIR:-}" -SOURCE_DIR="${SOURCE_DIR:-website}" -DRY_RUN="${DRY_RUN:-0}" -MANIFEST=".visustruct-deploy-manifest.txt" - -case "$REMOTE_DIR" in - "/"|"~"|*..*|*'"'*) echo "Ungültiger Zielordner: '$REMOTE_DIR'" >&2; exit 1 ;; -esac -[ -f "$SOURCE_DIR/index.html" ] || { echo "$SOURCE_DIR/index.html fehlt" >&2; exit 1; } - -work="$(mktemp -d)" -trap 'rm -rf "$work"' EXIT - -connect_for() { - local url settings - case "$1" in - sftp) url="sftp://$DEPLOY_HOST" - settings="set sftp:auto-confirm yes" ;; - ftps) url="ftp://$DEPLOY_HOST" - settings="set ftp:ssl-force true; set ftp:ssl-protect-data true; set ssl:verify-certificate ${DEPLOY_VERIFY_CERT:-yes}" ;; - *) echo "DEPLOY_PROTOCOL muss auto, sftp oder ftps sein" >&2; exit 1 ;; - esac - # use-temp-file: erst unter einem Hilfsnamen hochladen, dann umbenennen. So sieht niemand halb hochgeladene Seiten. - printf '%s' "$settings; set net:max-retries 3; set net:timeout 30; set cmd:fail-exit yes; set xfer:use-temp-file yes -open --env-password -u \"$DEPLOY_USER\" $url" -} - -# auto: SFTP versuchen, bei All-Inkl geht das nur mit SSH-Zugang; sonst FTPS -if [ "$PROTOCOL" = "auto" ]; then - for p in sftp ftps; do - if lftp -c "$(connect_for $p); cls -1 >/dev/null" 2> "$work/probe-$p"; then - PROTOCOL=$p; break - fi - echo "$p: $(grep -v '^$' "$work/probe-$p" | tail -n 1)" >&2 - done - if [ "$PROTOCOL" = "auto" ]; then - echo "::error::Anmeldung fehlgeschlagen. SFTP: $(grep -v '^$' "$work/probe-sftp" | tail -n 1) FTPS: $(grep -v '^$' "$work/probe-ftps" | tail -n 1)" - exit 1 - fi -fi -echo "Verbindung über $PROTOCOL" - -# Zielordner: ohne DEPLOY_DIR erst visustruct.org, dann der Startordner des -# Logins (FTP-Benutzer, die direkt in visustruct.org landen). Hochgeladen wird -# nur in einen Ordner, in dem schon VisuStruct liegt (index.html nennt -# VisuStruct oder ein früherer Lauf hat seine Dateiliste hinterlassen). -looks_like_site() { - lftp -c "$(connect_for "$PROTOCOL"); set cmd:fail-exit no; cd \"$1\" || exit 1; cat index.html" 2>/dev/null | grep -q VisuStruct \ - || lftp -c "$(connect_for "$PROTOCOL"); cd \"$1\"; cls -1 $MANIFEST" >/dev/null 2>&1 -} -if [ -z "$REMOTE_DIR" ]; then - for d in visustruct.org .; do - if looks_like_site "$d"; then REMOTE_DIR="$d"; break; fi - done - if [ -z "$REMOTE_DIR" ]; then - echo "::error::Kein Zielordner gefunden: weder visustruct.org noch der Startordner des Logins enthalten die VisuStruct-Webseite. Bitte die Variable DEPLOY_DIR setzen." - exit 1 - fi -elif ! looks_like_site "$REMOTE_DIR"; then - echo "::error::Im Zielordner '$REMOTE_DIR' liegt keine VisuStruct-Webseite (index.html), es wird nichts hochgeladen." - exit 1 -fi -echo "Zielordner: $REMOTE_DIR" -connect="$(connect_for "$PROTOCOL") -cd \"$REMOTE_DIR\"" - -# Fehlermeldungen von lftp zusätzlich als Annotation ausgeben (in der Übersicht -# des Laufs sichtbar, ohne das Log zu öffnen) -fail() { - local msg - msg="$(grep -v '^$' "$work/err" | tail -n 3 | tr '\n' ' ')" - echo "::error::Upload fehlgeschlagen: ${msg:-unbekannter Fehler}" - exit 1 -} - -# Neue Dateiliste -(cd "$SOURCE_DIR" && find . -type f ! -name .DS_Store ! -name "$MANIFEST" | sed 's|^\./||' | LC_ALL=C sort) > "$work/new" - -# Alte Dateiliste vom Server (fehlt beim ersten Lauf) -if ! lftp -c "$connect; get $MANIFEST -o \"$work/old\"" 2>/dev/null; then - echo "Keine frühere Dateiliste auf dem Server gefunden, es wird nichts gelöscht." - : > "$work/old" -fi - -# Die Assets des vorigen Builds bleiben eine Version lang liegen: Ein Browser, der noch -# die alte Seite offen oder im Cache hat, findet so seine Skripte (sonst bleibt sie leer). -grep '^app/assets/' "$work/old" | LC_ALL=C sort -u > "$work/keep" || true - -# app/assets/ wird von Vite bei jedem Build komplett neu erzeugt (Dateinamen mit -# Hash). Was dort auf dem Server liegt und nicht mehr zum Build gehört, ist veraltet, -# auch wenn es noch von Hand hochgeladen wurde. -lftp -c "$connect; cls -1 app/assets/" 2>/dev/null | sed 's|^.*/||; s|^|app/assets/|' | grep -v '/$' >> "$work/old" || true - -# Dateien, die früher hochgeladen wurden und jetzt nicht mehr dazugehören -LC_ALL=C sort -u "$work/old" | LC_ALL=C comm -23 - "$work/new" | LC_ALL=C comm -23 - "$work/keep" > "$work/stale" -: > "$work/rm.lftp" -while IFS= read -r f; do - case "$f" in - ""|/*|*..*|*'"'*|*'\'*) echo "Überspringe verdächtigen Eintrag: '$f'" >&2; continue ;; - esac - echo "rm -f \"$f\"" >> "$work/rm.lftp" -done < "$work/stale" - -echo "Dateien: $(wc -l < "$work/new") hochladen, $(wc -l < "$work/rm.lftp") veraltete löschen (Ziel: $REMOTE_DIR)" -sed 's/^rm -f / löschen: /' "$work/rm.lftp" - -# .htaccess: Ein vorhandenes (z. B. mit HTTPS-Weiterleitung) bleibt erhalten, -# nur unser Block zwischen den BEGIN/END-Zeilen wird ersetzt bzw. angehängt. -cache_conf="$(dirname "$0")/htaccess-cache.conf" -if ! lftp -c "$connect; get .htaccess -o \"$work/htaccess.old\"" 2>/dev/null; then - : > "$work/htaccess.old" -fi -sed '/^# BEGIN VisuStruct-Cache/,/^# END VisuStruct-Cache/d' "$work/htaccess.old" > "$work/htaccess" -[ -s "$work/htaccess" ] && [ -n "$(tail -c 1 "$work/htaccess")" ] && echo >> "$work/htaccess" -echo ".htaccess: $(grep -vc '^#\|^$' "$work/htaccess" || true) vorhandene Zeilen bleiben erhalten, der Cache-Block wird gesetzt." -cat "$cache_conf" >> "$work/htaccess" - -if [ "$DRY_RUN" = "1" ]; then - lftp -c "$connect; mirror -R --dry-run --no-perms --exclude-glob .DS_Store \"$SOURCE_DIR\" ." 2> >(tee "$work/err" >&2) || fail - echo "Trockenlauf: nichts geändert." - exit 0 -fi - -# Erst alles außer HTML (Skripte, Bilder, CSS), dann die Seiten selbst, -# damit keine Seite live ist, deren Assets noch fehlen. -lftp 2> >(tee "$work/err" >&2) < - - Header set Cache-Control "no-cache" - - - Header set Cache-Control "public, max-age=31536000, immutable" - - -# END VisuStruct-Cache diff --git a/.github/workflows/core-tests.yml b/.github/workflows/core-tests.yml index 18f50a5b..db26bad1 100644 --- a/.github/workflows/core-tests.yml +++ b/.github/workflows/core-tests.yml @@ -32,25 +32,3 @@ jobs: -destination 'generic/platform=iOS Simulator' \ CODE_SIGNING_ALLOWED=NO - web-test: - runs-on: ubuntu-latest - defaults: - run: - working-directory: web - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - cache-dependency-path: web/package-lock.json - - run: npm ci - - name: Test (Dateiformat gegen VisuStruct-Dateien) - run: npm test - - name: Build - run: npm run build - - name: website/app ist aktuell - run: | - npm run build:site - git diff --exit-code -- ../website/app || (echo "website/app ist veraltet: in web/ 'npm run build:site' ausführen und committen." && exit 1) - test -z "$(git status --porcelain -- ../website/app)" diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml deleted file mode 100644 index 8d498652..00000000 --- a/.github/workflows/deploy-website.yml +++ /dev/null @@ -1,76 +0,0 @@ -name: Webseite veröffentlichen - -# Lädt website/ nach All-Inkl (visustruct.org) hoch. -# Start: Tag web-v… anlegen (z. B. web-v1.0, auch über „Draft a new release“) -# oder von Hand unter Actions → „Webseite veröffentlichen“ → „Run workflow“. -# Ändert ein PR den Upload selbst, läuft nur ein Trockenlauf gegen den Server. -on: - pull_request: - paths: - - '.github/workflows/deploy-website.yml' - - '.github/scripts/deploy-website.sh' - push: - tags: ['web-v*'] - workflow_dispatch: - inputs: - dry_run: - description: 'Trockenlauf: nur anzeigen, was hochgeladen und gelöscht würde' - type: boolean - default: false - -permissions: - contents: read - -concurrency: - group: deploy-website - cancel-in-progress: false - -jobs: - check: - runs-on: ubuntu-latest - defaults: - run: - working-directory: web - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: npm - cache-dependency-path: web/package-lock.json - - run: npm ci - - name: Test - run: npm test - - name: website/app ist aktuell - run: | - npm run build:site - git diff --exit-code -- ../website/app || (echo "website/app ist veraltet: in web/ 'npm run build:site' ausführen und committen." && exit 1) - test -z "$(git status --porcelain -- ../website/app)" - - deploy: - needs: check - # PRs aus Forks bekommen keine Secrets - if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Zugangsdaten vorhanden? - env: - SFTP_USER: ${{ secrets.SFTP_USER }} - SFTP_PASSWORD: ${{ secrets.SFTP_PASSWORD }} - run: | - if [ -z "$SFTP_USER" ] || [ -z "$SFTP_PASSWORD" ]; then - echo "::error::Die Secrets SFTP_USER und SFTP_PASSWORD fehlen (Settings → Secrets and variables → Actions)." - exit 1 - fi - - name: lftp installieren - run: sudo apt-get update -q && sudo apt-get install -y -q lftp - - name: Hochladen - env: - DEPLOY_HOST: ${{ vars.DEPLOY_HOST || 'w0103087.kasserver.com' }} - DEPLOY_PROTOCOL: ${{ vars.DEPLOY_PROTOCOL || 'ftps' }} - DEPLOY_DIR: ${{ vars.DEPLOY_DIR }} - DEPLOY_USER: ${{ secrets.SFTP_USER }} - LFTP_PASSWORD: ${{ secrets.SFTP_PASSWORD }} - DRY_RUN: ${{ (inputs.dry_run || github.event_name == 'pull_request') && '1' || '0' }} - run: .github/scripts/deploy-website.sh diff --git a/README.md b/README.md index adc7c5af..ea5bd3e8 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@

- VisuStruct + VisuStruct

# VisuStruct (SwiftUI) -Visual **Nassi–Shneiderman** structograms for **Mac, iPad, and iPhone**, and as a web app in the browser (`web/`, with VisuBot for the mBot2 robot, including a Scratch-like block view). Same **`.visustruct`** / XML as the open-source **[Java editor](https://github.com/code4teaching/VisuStruct)** — drag-and-drop. +Visual **Nassi–Shneiderman** structograms for **Mac, iPad, and iPhone**. Same **`.visustruct`** / XML as the open-source **[Java editor](https://github.com/code4teaching/VisuStruct)** — drag-and-drop. **Localization:** The app ships a large set of UI translations (strings + Settings), with English and German treated as primary “source of truth” strings during development. @@ -28,9 +28,7 @@ git clone https://github.com/code4teaching/VisuStruct-SwiftUI.git ## Web app and website -The web app in `web/` (VisuStruct, VisuBot, VisuObject, VisuFlow) is built with `npm run build:site` into `website/app/`. Since 2026-11-03 it is published on **[visucode.de](https://visucode.de)**, one subdomain per world: [visustruct.visucode.de](https://visustruct.visucode.de), [visubot.visucode.de](https://visubot.visucode.de), [visuobject.visucode.de](https://visuobject.visucode.de). The website itself lives in a separate GitLab repository (`visucode/website`); its script `app-aktualisieren.sh` copies the built app from here, and a push to its `main` branch deploys it. - -The workflow **Webseite veröffentlichen** (`.github/workflows/deploy-website.yml`) for the old site visustruct.org is no longer used for new releases. +This repository contains only the SwiftUI app (iPad, iPhone, Mac). The web app (VisuStruct, VisuBot, VisuObject, VisuFlow), its lessons and the websites moved on 2026-11-05 to GitLab (`gitlab.lgsit.de/visucode/visustruct-web` and `visucode/website`) and are published on **[visucode.de](https://visucode.de)**. ## Changelog / release notes diff --git a/VisuStruct-iOS/README.md b/VisuStruct-iOS/README.md index 5d925de9..6710e3bc 100644 --- a/VisuStruct-iOS/README.md +++ b/VisuStruct-iOS/README.md @@ -1,6 +1,6 @@

- VisuStruct + VisuStruct

diff --git a/website/assets/VisuStruct-icon.png b/assets/VisuStruct-icon.png similarity index 100% rename from website/assets/VisuStruct-icon.png rename to assets/VisuStruct-icon.png diff --git a/web/.gitignore b/web/.gitignore deleted file mode 100644 index 8806551e..00000000 --- a/web/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -node_modules/ -dist/ -dist-single/ diff --git a/web/README.md b/web/README.md deleted file mode 100644 index 162730b2..00000000 --- a/web/README.md +++ /dev/null @@ -1,112 +0,0 @@ -# VisuStruct Web - -VisuStruct im Browser: `.visustruct`-Dateien öffnen, als Struktogramm zeichnen, -Elemente per Drag & Drop einfügen oder verschieben, Texte im Objektinspektor oder per Doppelklick/Doppeltipp direkt im -Struktogramm bearbeiten (`src/ui/inlineEdit.ts`), Schritt für Schritt simulieren, -Java-, Python- oder JavaScript-Code erzeugen (JavaScript direkt im Browser ausführen) und wieder speichern. -Läuft komplett im Browser, ohne Server. - -## Ausprobieren - -```bash -cd web -npm install -npm run dev # http://localhost:5173 -npm test # Dateiformat-, Bearbeitungs-, Simulations- und Codegenerator-Tests -npm run build # statische Dateien in web/dist/ (relative Pfade, z. B. für website/app/) -npm run build:single # eine einzelne, eigenständige index.html in web/dist-single/ -npm run build:site # baut die App nach website/app/ (wird mit der Webseite veröffentlicht) -``` - -## Aufbau - -Der Kern ist eine Datei-für-Datei-Übertragung von `VisuStruct-iOS/VisuStructCore` nach TypeScript, -damit Änderungen in Swift leicht nachgezogen werden können: - -| TypeScript | Swift-Vorlage | -| --- | --- | -| `src/core/model.ts` | `VisuStructElement.swift`, `VisuStructDocument.swift` | -| `src/core/javaStringCoding.ts` | `VisuStructJavaStringCoding.swift` | -| `src/core/xmlCodec.ts` | `VisuStructXMLCodec.swift` | -| `src/core/layout.ts` | `VisuStructLayout.swift`, `Layout/VisuStructLayoutEngineMetrics.swift` | -| `src/core/diagramText.ts` | `Layout/VisuStructDiagramText.swift`, `VisuStructLayoutTextMeasurement.swift` | -| `src/core/editing.ts` | `VisuStructDocumentEditing.swift` | -| `src/core/lineEditorText.ts` | `Editing/VisuStructLineEditorText.swift`, `Inspector/StatementIOMode.swift` | -| `src/core/palette.ts` | `Palette/ElementPalette.swift` | -| `src/core/simulation.ts` | `VisuStructSimulationEngine.swift` | -| `src/core/codeGenerator.ts` | `VisuStructCodeGenerator.swift`, `CodeGeneration/*.swift` | -| `src/ui/renderer.ts` | `Canvas/DiagramCanvasRenderer.swift` (als SVG) | -| `src/ui/exporter.ts` | `Export/DiagramSnapshotAndExport.swift`, `Export/DiagramExporter.swift` | -| `src/ui/i18n.ts` | `AppLanguage.swift` mit `*.lproj/Localizable.strings` und `Java.strings` | -| `src/ui/codePanel.ts` | `CodeGeneration/CodeGeneratorPanels.swift`, `Runner/JavaScriptRunner.swift` | -| `src/ui/simulationPanel.ts` | `Simulation/SimulationController.swift`, `Simulation/SimulationPanels.swift` | - -`test/editingParity.test.ts` überträgt `MultiwayPathTests.swift` und `LineEditorTextTests.swift`, -`test/simulation.test.ts` überträgt `SimulationEngineTests.swift` und `SimulationConditionTests.swift`, -`test/codeGenerator.test.ts` überträgt `CodeGeneratorTests.swift` (die erzeugten `.java`-Dateien für Collatz und -Fibonacci müssen byte-gleich zu denen in `VisuStruct-Dateien` sein). - -JavaScript läuft beim Testen in einem Web Worker: `prompt()` wird zu einem Eingabefeld, `while (true)` bricht wie in der -App nach 100 000 Durchläufen ab, und „Stopp“ beendet jeden Lauf. - -`test/xmlCodec.test.ts` lädt jede Datei aus `VisuStruct-Dateien`, speichert sie wieder und verlangt -**byte-gleiche** Ausgabe zum Swift-Encoder (nur CRLF aus Windows wird zu LF, wie in Swift). - -Mehrere Diagramme sind als Reiter offen (wie `Tabs/` in der App); jeder Reiter hat eigenes Rückgängig. -Kopieren/Ausschneiden/Einfügen, Textgröße und das Feld für Fallnamen folgen der Mac-App. - -„Exportieren…“ speichert das Diagramm mit Titel als PNG, SVG oder einseitiges A4-PDF (Hoch- oder Querformat wie in -der App, ohne zusätzliche Bibliothek) oder druckt es (Strg+P). `test/exporter.test.ts` prüft Dateinamen, Seitenformat -und den Aufbau der PDF-Datei. - -Die Oberfläche spricht dieselben 21 Sprachen wie die App (plus „Java (Keywords)“). Die Texte kommen direkt aus den -`Localizable.strings` der App; nur Texte, die es nur im Web gibt, stehen in `src/i18n/.strings`. Fehlt ein Text, -gilt wie in der App der englische. Beim ersten Start wird die Browsersprache gewählt; Arabisch und Hebräisch laufen von -rechts nach links, das Struktogramm und der Code bleiben links nach rechts. `test/i18n.test.ts` prüft, dass jeder im Code -benutzte Schlüssel einen englischen Text hat und jede Sprache alle Web-Texte mit denselben Platzhaltern enthält. - -## Lernbereich - -Neben dem Editor liegt ein Lernbereich mit **Lektionen**, **Aufgaben** und **Nachschlagen** (Vorbild: PicoJavaGoat). -Die Inhalte sind Markdown-Dateien in `lessons/` und werden beim Bauen eingebunden; das Format steht in -[`lessons/README.md`](lessons/README.md). Übungen werden automatisch geprüft: `src/core/exerciseCheck.ts` simuliert das -Diagramm mit den Eingaben der Übung und vergleicht die Ausgabe. Der Fortschritt liegt nur im `localStorage` des Browsers. -`test/lessons.test.ts` prüft alle Lektionen: Format, vorhandene Diagramme und dass jede Musterlösung ihre Tests besteht. -`…/app/?learn` öffnet die App mit eingeblendetem Lernbereich (Link „Zu den Lektionen“ der Webseite). - -## Veröffentlichen - -Die App liegt fertig gebaut in `website/app/`. Veröffentlicht wird sie über das GitLab-Repo `visucode/website` -(Skript `app-aktualisieren.sh`, Push auf `main` = live): unter `visustruct.visucode.de/app/`, `visubot.visucode.de/app/` -und `visuobject.visucode.de/app/`. Auf diesen Adressen öffnet die App immer die Welt der Seite. -Nach Änderungen in `web/` oder an den `Localizable.strings` der App `npm run build:site` ausführen und `website/app/` -mitcommitten; der CI-Schritt „website/app ist aktuell“ schlägt sonst fehl. Es gibt keinen Server: Dateien werden nur im -Browser geöffnet und gespeichert. - -Drag & Drop nutzt Zeiger-Ereignisse statt HTML5-Drag-and-Drop, damit Maus, Stift und Finger gleich funktionieren. - -## Noch nicht enthalten - -Verschieben vorhandener Elemente mit dem Finger im Struktogramm. - -## VisuBot (Welt `?visubot`) - -Den Lernroboter mBot2 programmieren: mBot2-Befehle und Sensoren in „Elemente“, 3D-Simulator (`src/ui/robot3d.ts`), -echter mBot2 per USB in Chrome oder Edge (`src/ui/mbotUpload.ts`), eigene Lektionen in `lessons//visubot/`. -Der Knopf **Blöcke** in den Werkzeugen zeigt dasselbe Struktogramm als Blöcke wie in Scratch (`src/ui/blockView.ts`, -Test `blockView.test.ts`): Blöcke ziehen, verschieben, in den Papierkorb werfen, Werte direkt in den weißen Feldern -ändern, Sensoren auf Felder oder Bedingungen ziehen. Die Regeln für kinderfreundliche Blöcke (z. B. die Zählschleife -„zähle mit i von [0] bis unter [10] in Schritten von [1]“) kommen aus der gemeinsamen Bibliothek -[VisuBlocks](https://github.com/code4teaching/visublocks) (MIT), eingebunden als Paket `visublocks` mit fester Commit-Nummer. - -## VisuObject (Welt `?visuobject`) - -Dritte Welt neben VisuStruct und VisuBot, für Objektorientierung ab Klasse 12 (Plan: `docs/visuobject/PLAN.md`). -`src/ui/objectApp.ts` zeigt links den Java-Code (CodeMirror wie in „Code“) und rechts das Klassendiagramm. -`src/core/classModel.ts` liest Klassen, Attribute, Methoden-Köpfe, `extends` und `implements` aus dem Code und schreibt -Änderungen im Diagramm an genau diese Stelle zurück (Rümpfe, Startwerte und Kommentare bleiben). Tests: `test/classModel.test.ts`. -Ein Projekt hat mehrere Dateien in Packages (`src/core/javaProject.ts`, eine Datei pro Klasse, die Zeile `package …;` folgt -dem Ordner). Links zeigt der Package Explorer Projekt › src › Packages › Dateien › Klassen › Attribute/Methoden; jede Ebene -und die ganze Spalte lassen sich einklappen. Das Klassendiagramm zeigt alle Klassen, jedes Package als UML-Rahmen. -Öffnen und Speichern als ZIP wie aus Eclipse oder BlueJ (`Name/src/package/Klasse.java`, `src/ui/projectZip.ts`, fflate). -Tests: `test/javaProject.test.ts`. diff --git a/web/index.html b/web/index.html deleted file mode 100644 index 38a4ff63..00000000 --- a/web/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - VisuStruct Web - - - - -
- - - diff --git a/web/laptop2.png b/web/laptop2.png deleted file mode 100644 index 0a46c586..00000000 Binary files a/web/laptop2.png and /dev/null differ diff --git a/web/lessons/README.md b/web/lessons/README.md deleted file mode 100644 index f5a539b8..00000000 --- a/web/lessons/README.md +++ /dev/null @@ -1,422 +0,0 @@ -# Lektionen und Aufgaben - -Alles in diesem Ordner erscheint im Lernbereich der Web-App. Nach einer Änderung `npm test` (prüft alle Dateien) und -`npm run build:site` ausführen. - -``` -lessons/ - de/01-was-ist-ein-struktogramm.md Lektionen, die Nummer am Anfang bestimmt die Reihenfolge - de/nachschlagen.md Seite „Nachschlagen“ - de/begriffe.md Fachbegriffe mit Erklärung (Hovern oder Antippen im Lektionstext) - de/aufgaben/summe-bilden.md Aufgaben - diagramme/l1-tee.visustruct Diagramme für Lektionen, Startdiagramme und Musterlösungen -``` - -Diagramme sind normale `.visustruct`-Dateien: in VisuStruct bauen, speichern und hier ablegen. - -## Lektion - -```markdown ---- -titel: Der Editor -bereich: Erste Schritte -kurz: Elemente einfügen, Text eingeben, verschieben und löschen. ---- - -## Die Oberfläche - -Normaler Text mit **fett**, *kursiv*, `Code`, Listen und [Links](https://…). - -> Ein Zitat erscheint als hervorgehobener Hinweis. - -## Nächster Schritt -… -``` - -Jede `## Überschrift` beginnt einen neuen Schritt (eine Seite mit „Weiter“). `bereich` gruppiert die Lektionen in der -Übersicht. Innerhalb eines Schritts gehen `###`-Zwischenüberschriften und Codeblöcke mit drei Backticks. - -## Bausteine - -Ein Diagramm mit Vorschau und Knopf „Im Editor öffnen“, die Zeilen bis `:::` sind die Bildunterschrift: - -``` -::: diagramm l2-ausgabe.visustruct -Eine Ausgabe mit Text in Anführungszeichen. -::: -``` - -Ein Java-Beispiel im Text mit Knopf „Im Editor öffnen“: Statt ```` ```java ```` schreibt man ```` ```java editor ````. Der Knopf -öffnet das Beispiel in einem neuen Reiter, die Spalte Code auf Java; dort kann man es ausführen und verändern. -Der Test prüft, dass jedes solche Beispiel ohne Fehler geparst wird. - -Eine Übung, die VisuStruct beim Klick auf „Prüfen“ automatisch kontrolliert: - -``` -::: übung l2-hallo -titel: Hallo Welt -auftrag: Baue ein Struktogramm, das „Hallo Welt“ ausgibt. - Weitere Zeilen gehören noch zum Auftrag. -start: l2-start.visustruct -test: - -> Hallo Welt -test: 3, 4 -> 7 | fertig -braucht: verzweigung, whileSchleife -tipp: Du brauchst nur einen einzigen Kasten. -tipp: Ziehe eine Anweisung auf den Platzhalter und wähle oben „Ausgabe“. -lösung: l2-hallo-loesung.visustruct -::: -``` - -- `test:` Eingaben durch Kommas getrennt, dann `->`, dann die erwarteten Ausgabezeilen durch `|` getrennt. - `-` heißt „keine Eingabe“. Leerzeichen am Rand zählen nicht, `2.0` gilt wie `2`. Mehrere `test:`-Zeilen sind erlaubt. -- `start:` (optional) Diagramm, mit dem die Übung beginnt; sonst ein leeres Diagramm. -- `braucht:` (optional) Elemente, die vorkommen müssen: `anweisung`, `verzweigung`, `fallauswahl`, `forSchleife`, - `whileSchleife`, `doUntilSchleife`, `endlosschleife`, `aussprung`, `aufruf`. -- `tipp:` gestufte Tipps, eine Zeile je Tipp, vom allgemeinen zum genauen. Jeder Klick auf **Tipp** deckt den nächsten auf. Jede Übung hat mindestens zwei. -- `lösung:` (optional) Musterlösung. Sichtbar ist sie nur im Lehrermodus (Lehrercode unten in der Lektions- oder Aufgabenliste, im Code steht nur sein SHA-256-Hash in `src/learn/teacher.ts`). Der Test prüft, dass sie alle Tests besteht. -- `unterprogramm:` (optional) Unterprogramme zur Musterlösung, durch Komma getrennt. Ihr Titel ist der Name im Aufruf. Beim Prüfen findet VisuStruct Unterprogramme in den anderen offenen Reitern. -- `datei:` (optional, nach einem `test:`) Diese Datei soll das Programm in diesem Test speichern, zum Beispiel - `datei: einkauf-neu.txt -> Heu | Äpfel | Kuchen`. Verglichen wird Zeile für Zeile wie bei den Ausgaben. -- `code: java` (optional) Die Übung wird im Bereich Code in Java gelöst. „Im Editor öffnen“ öffnet die Spalte gleich mit und stellt sie auf Java. Geprüft wird wie sonst das Struktogramm, das beim Tippen entsteht. Eine Musterlösung ist Pflicht, der Test tippt sie als Java und prüft sie. -- `bereich: struktogramm | code | beide` (optional) Wo man die Übung löst. Die Karte zeigt es farbig an (Struktogramm grün, Code violett), und „Im Editor öffnen“ zeigt genau diese Bereiche: Lektionen bleiben offen, alles andere (Dateien, mBot2 live, bei `struktogramm` der Code, bei `code` das Struktogramm) wird geschlossen. Ohne Angabe gilt `code` bei `code: java`, sonst `struktogramm`. Für Übungen, bei denen man Code tippt und das Struktogramm liest (oder umgekehrt), `beide` nehmen. -- Die Kennung (`l2-hallo`) muss überall eindeutig sein, denn unter ihr wird der Fortschritt gespeichert. - -Eine Checkliste zum Abhaken: - -``` -::: checkliste -- Ich kann ein Element einfügen. -- Ich kann einen Kasten löschen. → Verschieben, kopieren, löschen -::: -``` - -Steht hinter einem Punkt `→` und der Titel eines Schritts dieser Lektion, zeigt die Checkliste daneben **Nachlesen**. Ein Klick springt zu diesem Schritt. Ein falscher Titel fällt beim Bauen und in den Tests auf. - -Die Punkte sind kompetenzorientiert formuliert („Ich kann …“). Rechts neben jedem Punkt schätzt man sich ein: ☂️ noch nicht, -⛅ teilweise, ☀️ kann ich sicher; danach folgt **Nachlesen**. Die Wahl steht wie der übrige Lernstand in `learnProgress.json` (ältere Häkchen gelten als ☀️). - -Am Anfang einer Lektion stehen die Kompetenzen, die man erwirbt, als eigener erster Schritt „Diese Kompetenzen erwerbe -ich jetzt“; die Lektion selbst beginnt mit Schritt 2. Der Kasten beginnt mit „Am Ende dieser Lektion kann ich …“, die Punkte -also so formulieren, dass sie daran anschließen: - -``` -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Algorithmus ist -- ein Struktogramm lesen -::: -``` - -Eine Quizfrage kann mit `→ Titel eines Schritts` sagen, was sie prüft. Ist der Wissenstest fertig, zeigt er darunter die -Checkliste noch einmal mit den Abweichungen: ☀️ gewählt, aber eine Frage zu diesem Schritt falsch (mit **Nachlesen**), -oder ⛅/☂️ gewählt und alle Fragen dazu richtig. Zu jedem Schritt aus `→` muss es einen Checklistenpunkt geben. - -``` -::: quiz -? In welcher Richtung wird ein Struktogramm gelesen? -→ So liest du ein Struktogramm -- [x] Von oben nach unten -- [ ] Von links nach rechts -::: -``` - -Ein oder mehrere Bilder aus `lessons/bilder/` nebeneinander, die Zeilen bis `:::` sind die Bildunterschrift (dort auch den -Bildnachweis angeben): - -``` -::: bild ike-nassi.jpg ben-shneiderman.jpg -Links Ike Nassi, rechts Ben Shneiderman (Fotos: …). -::: -``` - -Eine Erklärgrafik in voller Breite aus `lessons/bilder/`, am besten als SVG (bleibt auf jedem Bildschirm scharf). Die -Zeilen bis `:::` sind die Bildunterschrift: - -``` -::: grafik l14-array.svg -Fünf Fächer mit den Indizes 0 bis 4. -::: -``` - -Steht hinter der Grafik eine zweite Datei, ist das eine beschriftete Fassung. Ein Klick auf die Grafik blendet sie ein und -wieder aus, darunter steht ein Hinweis darauf: - -``` -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -So sah eine Lochkarte aus. -::: -``` - -Tabellen schreibst du mit senkrechten Strichen, die zweite Zeile trennt die Überschrift ab: - -``` -| Schritt | summe | i | -|---|---|---| -| `int summe = 0` | 0 | – | -``` - -Ein Zeiger auf den Editor ist ein Link auf `#zeige-…`, zum Beispiel `[Elemente](#zeige-elemente)`. Beim Hovern oder Antippen -leuchtet der Bereich im Editor auf. Bekannte Bereiche: leiste, elemente, struktogramm, objektinspektor, reiter, titel, neu, -oeffnen, speichern, exportieren, rueckgaengig, textgroesse, einstellungen, lernen, code, simulation (Liste in `src/ui/termTips.ts`). - -Ein Kasten „Angeberwissen mit Ludwig“, in dem unser Maskottchen Ludwig der Geißbock eine Hintergrundgeschichte erzählt. -Der Text darf Markdown und Links enthalten: - -``` -::: angeberwissen -Ben Shneiderman hatte die Idee 1972. [Mehr dazu](https://visustruct.visucode.de/de/angeberwissen.html) -::: -``` - -Ein Spiel „Wahr oder falsch?“: oben die Variablen (`name: von..bis` für ganze Zahlen, `name: boolean`), darunter mit `?` die -Bedingungen vom leichten zum schweren. Zehn Runden; Werte knapp an den Grenzen der Vergleiche kommen öfter vor: - -``` -::: spiel -alter: 8..20 -schueler: boolean -? alter >= 18 -? alter >= 12 && alter < 18 || schueler -::: -``` - -Rundenspiele `::: spiel zuhoerer`, `vertrag` und `layout` (Swing) sind in `src/learn/roundGame.ts` beschrieben. Dort steht -auch „Bombe entschärfen“: Code mit genau einem Fehler, man tippt vor Ablauf der Zeit auf die falsche Zeile. - -In `zuhoerer` darf ein Zuhörer auch als Variable stehen (`ActionListener z = e -> …;` oder als Block bis `};`) und mit -`knopf.addActionListener(z);` an mehreren Knöpfen hängen. Im Zuhörer gehen `if`/`else` (auch verschachtelt), -`e.getActionCommand()` (Beschriftung des geklickten Knopfs) und `e.getSource() == knopf`. - -`::: spiel reihenfolge` („Wer ist dran?“): Die Antworten jeder Runde stehen in der richtigen Reihenfolge da, ohne `[x]`, -mindestens drei. Das Spiel mischt sie, man tippt sie der Reihe nach an. - -``` -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt genau ein Fehler. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Soll 1, 2 und 3 ausgeben. -fehler: 2 -besser: System.out.println(i); -> Am Ende der Anweisung fehlt das Semikolon. -::: -``` - -`fehler:` ist die Nummer der falschen Zeile (ab 1), `besser:` die richtige Zeile. Der Test setzt sie ein und prüft, dass -Java dann keinen Fehler mehr meldet. - -Mit `stelle:` (nach `fehler:`) tippt man in dieser Runde nicht ganze Zeilen an, sondern Teile davon: die drei Felder -einer `for`-Schleife, die Bedingung von `if`/`while`, Ziel und Wert einer Zuweisung, Aufruf und Argumente (an `+` -geteilt). `stelle:` ist genau der falsche Teil, z. B. `stelle: i++`. Ist es kein Teil der Zeile, nennt die Fehlermeldung -die möglichen Teile. Fehler wie ein fehlendes `;` bleiben ohne `stelle:`. - -Mit `daten: csv` (vor der ersten Runde) ist der Code einer Bombe eine CSV-Datei. Der Test prüft dann, dass jede Zeile mit -`besser:` so viele Felder hat wie die Kopfzeile und die falsche Zeile nicht. - -`::: spiel frage` zeigt in jeder Runde Code (er darf auch fehlen) und eine Frage mit Antworten. `\n` in einer Antwort -beginnt eine neue Zeile, solche Antworten erscheinen als kleine Datei. Mit `antworten: txt | CSV | JSON` vor der ersten -Runde gelten die Antworten für alle Runden, je Runde steht dann `richtig: CSV`: - -``` -::: spiel frage -titel: Spiel: Was steht in der Datei? -intro: Sag voraus, was danach in der Datei steht. -frage: Was steht danach in der Datei? - -runde: - PrintWriter aus = new PrintWriter("gruss.txt"); - aus.println("Hallo"); - aus.println("Zora"); - aus.close(); -- [x] Hallo\nZora -- HalloZora -> `println` beginnt nach jedem Text eine neue Zeile. -::: -``` - -„Prinzessin retten“ (`::: spiel prinzessin`, beschrieben in `src/learn/rescueGame.ts`): An jeder Station kommt eine Zahl -heraus, `alphabet.charAt(zahl)` füllt damit ein Feld im Kreuzworträtsel. Die Felder bei `lösung:` ergeben das Wort, das am -Ende in den Code bei `tor:` (statt `___`) eingetippt wird. Der Test prüft, dass jeder Buchstabe ins Rätsel passt. - -``` -::: spiel prinzessin -titel: Spiel: Prinzessin retten -intro: … - -station: Am Burggraben - int zahl = " Zora ".trim().length(); -frage: Welche Zahl steht in `zahl`? -zahl: 4 -feld: 5 1 -tipp: `trim()` schneidet die Leerzeichen ab. -> Übrig bleibt "Zora" mit 4 Zeichen. - -kreuzwort: Fülle die Felder mit Fachbegriffen aus Java. -- → 4 1 STRING: Datentyp für Text -- ↓ 2 1 ELSE: Der „sonst“-Teil einer Verzweigung -lösung: 4 1, 2 1 - -tor: Trag das geheime Wort ein. - String wort = "___"; -ende: Zora ist frei! -::: -``` - -`feld:` und die Wörter haben Zeile und Spalte ab 1, `→` ist waagrecht, `↓` senkrecht. - -Ein Kasten „Ludwigs Tipp“: Ludwig gibt einen Hinweis zum Vorgehen, z. B. eine Brücke zu einer früheren Lektion: - -``` -::: ludwig -Wie beim Tauschen gilt: Überschreibe keinen Wert, den du noch brauchst. -::: -``` - -Eine digitale Caesar-Scheibe zum Drehen: außen der Klartext, innen der Geheimtext, darunter ein Eingabefeld, das live ver- oder -entschlüsselt. Hinter `caesar` steht der Startschlüssel (0 bis 25), die Zeile darin ist der Starttext: - -``` -::: caesar 3 -LUDWIG -::: -``` - -In der Simulation rechnet ein `char` wie in Java mit seinem Zeichencode: `(int) wort.charAt(i)` liefert z. B. 65 für A, -`'A'` zählt in einer Rechnung als 65 und `(char) 68` ergibt `'D'`. - -Eine Textdatei zum Einlesen. Der Inhalt bleibt Zeile für Zeile so, wie er dasteht. Java liest ihn mit -`Files.readString(Path.of("brief.txt"))`, `new Scanner(new File(…))` oder `new BufferedReader(new FileReader(…))`, in der -Simulation, im Python- und JavaScript-Export und im echten Java. Was ein Programm mit `PrintWriter` speichert, zeigt -VisuStruct nach dem Lauf unter der Ausgabe: - -``` -::: datei brief.txt -Lieber Ludwig, -der Fuchs hat mich in seinen Turm gesperrt. -::: -``` - -Der Name endet auf `.txt`, `.csv` oder `.json` und gilt für alle Lektionen einer Sprache. Verschiedene Dateien brauchen deshalb verschiedene Namen. - -Ein Lückentext: fertiger Java-Code, in dem einzelne Wörter fehlen. Jede Lücke `[[…]]` wird ein Eingabefeld, „Prüfen“ -färbt richtige Lücken grün und falsche rot, nach einem Fehlversuch zeigt „Lösung zeigen“ die Antworten. Weitere richtige -Antworten stehen mit ` | ` (Leerzeichen davor und danach) in derselben Lücke. Leerzeichen zählen beim Vergleich nicht, -Groß- und Kleinschreibung schon. `erklärung:` erscheint, wenn alles stimmt. Ein Test prüft, dass der Code mit jeder -richtigen Antwort fehlerfreies Java ist: - -```` -::: lückentext -titel: Summe von 1 bis 10 -auftrag: Ergänze die fehlenden Wörter. -```java -int summe = 0; -for ([[int]] i = 1; i [[<=]] 10; [[i++ | i += 1 | i = i + 1]]) { - summe = summe + i; -} -``` -erklärung: Ausgabe: Summe: 55 -::: -```` - -Mehrere ```java-Blöcke stehen untereinander, z. B. eine Methode und das Hauptprogramm wie in eigenen Reitern; jeder wird -für sich geprüft. - -Für „Fehler finden“ eignet sich eine normale Übung mit `code: java`, deren `start:`-Diagramm einen Fehler enthält. - -„PAP bauen“: Ein Programmablaufplan mit leeren Plätzen, darunter liegen die fertigen Elemente gemischt. Man tippt ein -Element und dann seinen Platz an oder zieht es hin. `start:`/`ende:` sind Ovale, `anweisung:` Rechtecke, `eingabe:`/ -`ausgabe:` Parallelogramme, `frage:` Rauten. Die Zeilen stehen von oben nach unten. Eine Frage braucht nach ` | ` ihren -zweiten Ausgang: `nein -> Text` ist ein Pfeil zurück nach oben zum Element mit diesem Text, `nein: Text` eine Anweisung -rechts daneben, von der ein Pfeil zurück zur Raute führt. Statt `nein` geht auch `ja`. `legende: ja` zeigt unter dem Plan die vier Symbole mit -ihrer Bedeutung. Die Schlüsselwörter bleiben auch in -en/pt deutsch, die Beschriftung ja/nein kommt aus der Oberflächensprache. - -``` -::: pap -titel: Übung: Kuchen backen als PAP -auftrag: Lege jedes Element an seinen Platz. -start: Start -anweisung: Teig rühren -frage: Teig glatt? | nein -> Teig rühren -frage: Stäbchen sauber? | nein: 5 Minuten weiterbacken -ausgabe: „Der Kuchen ist fertig!“ -ende: Ende -erklärung: Wird gezeigt, wenn alles stimmt. -::: -``` - -Ein Quiz, `[x]` markiert richtige Antworten, `>` ist die Erklärung nach dem Antworten: - -``` -::: quiz -? Was bedeutet ø? -- [x] Ein leerer Platzhalter -- [ ] Eine Ausgabe -> Den Platzhalter ersetzt du durch ein Element. -::: -``` - -Eine Quizfrage kann statt Antworten auch eine Zuordnung sein: jede Zeile `= links -> rechts`. Neben jedem linken Begriff -erscheint eine Auswahl mit allen rechten Begriffen (alphabetisch). Die rechten Seiten müssen verschieden sein. - -``` -::: quiz -? Ordne jedem Begriff den anderen Namen zu. -= Fallauswahl -> Mehrfachauswahl -= Schleife -> Wiederholungsstruktur -::: -``` - -## Aufgabe - -Wie eine Lektion, aber ohne Schritte. `nach` ist die Nummer der Lektion, die man vorher kennen sollte, `stufe` die -Schwierigkeit von 1 bis 3 (Sterne). - -```markdown ---- -titel: Summe bilden -nach: 2 -stufe: 1 ---- - -Kurze Einleitung. - -::: übung aufgabe-summe -… -::: -``` - -## Hinweis zur Ausgabe - -In einer Ausgabe hängt `+` Texte aneinander, auch bei Zahlen: `zahl1 + zahl2` gibt bei 3 und 4 „34“ aus. Für eine -Summe zuerst rechnen (`int summe = zahl1 + zahl2`) und dann `summe` ausgeben. - -## Fachbegriffe - -In `de/begriffe.md` steht jeder Begriff als `### Begriff`, darunter optional `auch: Form, Form` und die Erklärung in ein bis -zwei Sätzen. In den Lektionen und Aufgaben wird jeder Begriff einmal pro Schritt markiert und zeigt beim Hovern oder -Antippen seine Erklärung. Steht der Begriff im Schritt **fett**, wird er dort gerade erklärt und bleibt unmarkiert. Code, -Überschriften und Links werden nie markiert. Unter „Nachschlagen“ erscheinen alle Begriffe als Liste. - -## Englisch - -Die englische Fassung liegt unter gleichen Dateinamen in `en/` (Lektionen, `begriffe.md`, `nachschlagen.md`, -`aufgaben/`). Diagramme mit englischen Variablen und Texten liegen in `diagramme/en/`, Grafiken in `bilder/en/`. -Deutsch zeigt `de/`, Portugiesisch (pt-PT) zeigt `pt/`, alle anderen Oberflächensprachen zeigen `en/`. Fehlt eine Datei in einer Sprache, gilt die englische (Rückfall ist immer Englisch). -Kennungen von Übungen und die Blöcke (`::: übung`, `titel:`, `tipp:` …) bleiben gleich, nur der Inhalt wird übersetzt. -In `begriffe.md` darf statt `auch:` auch `also:` stehen. Die Lektionen verwenden die englischen Wörter der Oberfläche -(Elements, Object Inspector, Statement, Branch, Switch …). Wer eine deutsche Lektion ändert, sollte die englische -mitziehen; die Tests prüfen, dass es jede Datei in allen Sprachen gibt und jede Musterlösung ihre Tests besteht. - -## Portugiesisch (Portugal) - -Die pt-PT-Fassung liegt in `pt/`, `diagramme/pt/` und `bilder/pt/` (Diagramme, die mit `en/` identisch wären, fehlen dort -und kommen per Rückfall aus `en/`). Die Lektionen verwenden die Wörter aus `pt-PT.lproj/Localizable.strings` -(Elementos, Inspetor de objetos, Instrução, Decisão, Seleção, Ciclo while …) und sprechen mit „tu“ an. -Lokal angepasst: Noten auf der portugiesischen Skala 1–5 (5 ist die beste), IVA 23 %, Wahlalter 18. diff --git a/web/lessons/bilder/ada-lovelace.jpg b/web/lessons/bilder/ada-lovelace.jpg deleted file mode 100644 index 1c5802bd..00000000 Binary files a/web/lessons/bilder/ada-lovelace.jpg and /dev/null differ diff --git a/web/lessons/bilder/aufgabe-mehrwertsteuer.svg b/web/lessons/bilder/aufgabe-mehrwertsteuer.svg deleted file mode 100644 index 57094a66..00000000 --- a/web/lessons/bilder/aufgabe-mehrwertsteuer.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - zu: Handy - - - - aufklappen - - - - - - - - - - - - - - - NEU - - auf: Tablet - - - - - KASSENBON - Falt-Handy - netto1000,00 € - +19% MwSt190,00 € - - SUMME1190,00 € - - - - So setzt sich der Preis zusammen: - - netto 1000 € - - 190 € - - - - - - - - - - - Finanzamt - - - - - - brutto 1190 € - Das zahlst du an der Kasse. - diff --git a/web/lessons/bilder/ben-shneiderman.jpg b/web/lessons/bilder/ben-shneiderman.jpg deleted file mode 100644 index 369a8ef0..00000000 Binary files a/web/lessons/bilder/ben-shneiderman.jpg and /dev/null differ diff --git a/web/lessons/bilder/caesar-ascii.svg b/web/lessons/bilder/caesar-ascii.svg deleted file mode 100644 index d6beace8..00000000 --- a/web/lessons/bilder/caesar-ascii.svg +++ /dev/null @@ -1,98 +0,0 @@ - - -A -65 - -B -66 - -C -67 - -D -68 - -E -69 - -F -70 - -G -71 - -H -72 - -I -73 - -J -74 - -K -75 - -L -76 - -M -77 - -N -78 - -O -79 - -P -80 - -Q -81 - -R -82 - -S -83 - -T -84 - -U -85 - -V -86 - -W -87 - -X -88 - -Y -89 - -Z -90 -(int) 'X' -→ -88 -88 - 65 -→ -23 -23 + 3 -→ -26 -26 % 26 -→ -0 -0 + 65 -→ -65 -(char) 65 -→ -'A' - \ No newline at end of file diff --git a/web/lessons/bilder/caesar-scheibe.svg b/web/lessons/bilder/caesar-scheibe.svg deleted file mode 100644 index 8d8484de..00000000 --- a/web/lessons/bilder/caesar-scheibe.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - -A -B -C -D -E -F -G -H -I -J -K -L -M -N -O -P -Q -R -S -T -U -V -W -X -Y -Z - -A -B -C -D -E -F -G -H -I -J -K -L -M -N -O -P -Q -R -S -T -U -V -W -X -Y -Z - - - - \ No newline at end of file diff --git a/web/lessons/bilder/caesar-verschiebung.svg b/web/lessons/bilder/caesar-verschiebung.svg deleted file mode 100644 index 81035fc3..00000000 --- a/web/lessons/bilder/caesar-verschiebung.svg +++ /dev/null @@ -1,133 +0,0 @@ - - - -A - -B - -C - -D - -E - -F - -G - -H - -I - -J - -K - -L - -M - -D - -E - -F - -G - -H - -I - -J - -K - -L - -M - -N - -O - -P - - - - - - - - - - - - - - -N - -O - -P - -Q - -R - -S - -T - -U - -V - -W - -X - -Y - -Z - -Q - -R - -S - -T - -U - -V - -W - -X - -Y - -Z - -A - -B - -C - - - - - - - - - - - - - - \ No newline at end of file diff --git a/web/lessons/bilder/en/aufgabe-mehrwertsteuer.svg b/web/lessons/bilder/en/aufgabe-mehrwertsteuer.svg deleted file mode 100644 index 920b4411..00000000 --- a/web/lessons/bilder/en/aufgabe-mehrwertsteuer.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - phone - - - - unfold - - - - - - - - - - - - - - - NEW - - tablet - - - - - RECEIPT - Folding phone - net€1,000.00 - +19% VAT€190.00 - - TOTAL€1,190.00 - - - - This is how the price is made up: - - net €1,000 - - €190 - - - - - - - - - - - Tax office - - - - - - gross €1,190 - You pay this at the till. - diff --git a/web/lessons/bilder/en/kino-saal.svg b/web/lessons/bilder/en/kino-saal.svg deleted file mode 100644 index 4e49fc22..00000000 --- a/web/lessons/bilder/en/kino-saal.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Screen -Seat (second index) -0 -1 -2 -3 -4 -Row(firstindex) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Dashed: seats[1][2], row 1, seat 2 -sold = truefree = false - diff --git a/web/lessons/bilder/en/kino-schleifen.svg b/web/lessons/bilder/en/kino-schleifen.svg deleted file mode 100644 index 2698fc0a..00000000 --- a/web/lessons/bilder/en/kino-schleifen.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -outer loop: row = 0, 1, 2 -inner loop: seat = 0 … 4 -seats[row][seat]look at -seat -0 -1 -2 -3 -4 -row0 - -1 - -2 - -3 - -4 - -5 - -row1 - -6 - -7 - -8 - -9 - -10 - -row2 - -11 - -12 - -13 - -14 - -15 -Inside, seat runs through, only then row moves on by one. - diff --git a/web/lessons/bilder/en/l01-pap.svg b/web/lessons/bilder/en/l01-pap.svg deleted file mode 100644 index fc2912ae..00000000 --- a/web/lessons/bilder/en/l01-pap.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Start - Put a cup on the table - Sugar wanted? - Add sugar - Tea still too hot? - Wait 1 minute - Drink the tea - End - - - yes - no - yes - no - - - - - - - - - - - Start or end - Statement - Decision - Input or output - - diff --git a/web/lessons/bilder/en/l02-einfuegen.svg b/web/lessons/bilder/en/l02-einfuegen.svg deleted file mode 100644 index 3891d11a..00000000 --- a/web/lessons/bilder/en/l02-einfuegen.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - Where does the element land? - - - - - top half - bottom half - → goes in front - → goes after - - ø - → replaces it - diff --git a/web/lessons/bilder/en/l03-lochkarte-beschriftet.svg b/web/lessons/bilder/en/l03-lochkarte-beschriftet.svg deleted file mode 100644 index b5080535..00000000 --- a/web/lessons/bilder/en/l03-lochkarte-beschriftet.svg +++ /dev/null @@ -1,1019 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Cut corner: -so all cards lie the right way round - -Printed text to read along - - - - -Rows 12 and 11: extra holes for letters -1 column = 1 character -here P: holes in 11 and 7 - -punched hole - - -80 columns = at most 80 characters per line - diff --git a/web/lessons/bilder/en/l03-lochkarte.svg b/web/lessons/bilder/en/l03-lochkarte.svg deleted file mode 100644 index 8eea3c50..00000000 --- a/web/lessons/bilder/en/l03-lochkarte.svg +++ /dev/null @@ -1,998 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/web/lessons/bilder/en/l04-camelcase.svg b/web/lessons/bilder/en/l04-camelcase.svg deleted file mode 100644 index 82c4ff01..00000000 --- a/web/lessons/bilder/en/l04-camelcase.svg +++ /dev/null @@ -1,31 +0,0 @@ - - -priceWithTax - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/en/l04-variable.svg b/web/lessons/bilder/en/l04-variable.svg deleted file mode 100644 index 05bbc57c..00000000 --- a/web/lessons/bilder/en/l04-variable.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - int age = 14 - - - 14 - - - - age - - int - - - - - - Value - Name - Data type - The name gets you to the value. - diff --git a/web/lessons/bilder/en/l04-zuweisung.svg b/web/lessons/bilder/en/l04-zuweisung.svg deleted file mode 100644 index e77c3776..00000000 --- a/web/lessons/bilder/en/l04-zuweisung.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - points - = - points + 5 - - - ① work out the right: 10 + 5 = 15 - - ② store the result on the left - - points before: 10 → points after: 15 - diff --git a/web/lessons/bilder/en/l05-tauschen.svg b/web/lessons/bilder/en/l05-tauschen.svg deleted file mode 100644 index 3bc0bd56..00000000 --- a/web/lessons/bilder/en/l05-tauschen.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - left - juice - temp - empty - right - water - - - - - - - 1 - 2 - 3 - - - 1: temp = left - 2: left = right - 3: right = temp - - diff --git a/web/lessons/bilder/en/l08-zahlenstrahl.svg b/web/lessons/bilder/en/l08-zahlenstrahl.svg deleted file mode 100644 index d564b7a8..00000000 --- a/web/lessons/bilder/en/l08-zahlenstrahl.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - - valid - invalidinvalid - - grade >= 1 && grade <= 6 - grade < 1 || grade > 6 - - diff --git a/web/lessons/bilder/en/l10-kopfgesteuert.svg b/web/lessons/bilder/en/l10-kopfgesteuert.svg deleted file mode 100644 index 681b1b15..00000000 --- a/web/lessons/bilder/en/l10-kopfgesteuert.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - How the while loop runs - - ① check the condition - - ② run the inside - - - - - - true - back - to the top - false - continue below the loop - diff --git a/web/lessons/bilder/en/l11-vergleich.svg b/web/lessons/bilder/en/l11-vergleich.svg deleted file mode 100644 index 8ba822a0..00000000 --- a/web/lessons/bilder/en/l11-vergleich.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - pre-test (while) - post-test (do-while) - - - ① condition - - ② inside - - ① inside - - ② condition - - - - - - - true - true - If the condition is false at once, - the inside runs never. - The inside always runs - at least once. - diff --git a/web/lessons/bilder/en/l12-for-ablauf.svg b/web/lessons/bilder/en/l12-for-ablauf.svg deleted file mode 100644 index 83185841..00000000 --- a/web/lessons/bilder/en/l12-for-ablauf.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - ① Start (only once) - - ② check the condition - - ③ run the inside - - ④ step, e.g. i++ - - - - - - - - true - back - to ② - false - done - diff --git a/web/lessons/bilder/en/l12-while-zu-for.svg b/web/lessons/bilder/en/l12-while-zu-for.svg deleted file mode 100644 index 6b2f4ede..00000000 --- a/web/lessons/bilder/en/l12-while-zu-for.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Init - Condition - Step - InitConditionStep - diff --git a/web/lessons/bilder/en/l13-verschachtelt-tabelle.svg b/web/lessons/bilder/en/l13-verschachtelt-tabelle.svg deleted file mode 100644 index 6065b866..00000000 --- a/web/lessons/bilder/en/l13-verschachtelt-tabelle.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String text = "" - for int j = 1; j <= 4; j++ - text = text + i * j + " " - output: text - - VARIABLESCONSOLE - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - text = - "" - text = - "1 " - text = - "1 2 " - text = - "1 2 3 " - text = - "1 2 3 4 " - text = - "2 " - text = - "2 4 " - text = - "2 4 6 " - text = - "2 4 6 8 " - text = - "3 " - text = - "3 6 " - text = - "3 6 9 " - text = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/web/lessons/bilder/en/l14-array.svg b/web/lessons/bilder/en/l14-array.svg deleted file mode 100644 index 33e751c5..00000000 --- a/web/lessons/bilder/en/l14-array.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - int[] numbers = {2, 4, 6, 8, 10} - Index - 02 - 14 - 26 - 38 - 410 - Value - - numbers[0] is 2numbers[4] is 10 - 5 slots, counting starts at 0: the last index is 4. - diff --git a/web/lessons/bilder/en/l20-cpp.svg b/web/lessons/bilder/en/l20-cpp.svg deleted file mode 100644 index e8b890e7..00000000 --- a/web/lessons/bilder/en/l20-cpp.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - calculator.cpp - source code in C++ - - - - - - - - - - compiler - - machine code - - Windows - - compiler - - machine code - - macOS - - compiler - - machine code - - Linux - Compile again - for every system - diff --git a/web/lessons/bilder/en/l20-jvm.svg b/web/lessons/bilder/en/l20-jvm.svg deleted file mode 100644 index e15359be..00000000 --- a/web/lessons/bilder/en/l20-jvm.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - TimesTable.java - source code, for people - - TimesTable.class - bytecode: CA FE BA BE … - - - - - - - compiler javac - - JVM - - Windows - - JVM - - macOS - - JVM - - Linux - Write once, - run anywhere - diff --git a/web/lessons/bilder/en/mvc-klick.svg b/web/lessons/bilder/en/mvc-klick.svg deleted file mode 100644 index a885cc70..00000000 --- a/web/lessons/bilder/en/mvc-klick.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - the display - - Controller - the listener - - Model - Counter - - 0 - 1 - 2 - 3 - - +1 - - value = 0 - value = 1 - value = 2 - value = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 increase() - 2 increase() - - - 3 getValue() - 3 getValue() - - - 4 update(value) - 4 update(value) - - The model knows neither the view nor the controller. - diff --git a/web/lessons/bilder/en/ss-binaere-suche.svg b/web/lessons/bilder/en/ss-binaere-suche.svg deleted file mode 100644 index e5250f36..00000000 --- a/web/lessons/bilder/en/ss-binaere-suche.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Looking for the 27. The cards are sorted. -1st middle: 13 < 27, drop the left -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2nd middle: 21 < 27, drop the left -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3rd middle: found after 3 comparisons -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/web/lessons/bilder/en/ss-bubblesort.svg b/web/lessons/bilder/en/ss-bubblesort.svg deleted file mode 100644 index a584b009..00000000 --- a/web/lessons/bilder/en/ss-bubblesort.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -One round: compare neighbours -7♥ -3♠ -10♦ -5♣ -2♥ -Start -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: swap -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: fine -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: swap -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: swap -The largest card rises to the right like a bubble. - diff --git a/web/lessons/bilder/en/ss-lineare-suche.svg b/web/lessons/bilder/en/ss-lineare-suche.svg deleted file mode 100644 index fe95a205..00000000 --- a/web/lessons/bilder/en/ss-lineare-suche.svg +++ /dev/null @@ -1,23 +0,0 @@ - - -Looking for the 9. The cards are mixed up. -7♥ -✗ -1. -3♠ -✗ -2. -10♦ -✗ -3. -5♣ -✗ -4. -9♠ -✓ -5. - - - -Turn them over from left to right:After 5 comparisons the 9 is found. - diff --git a/web/lessons/bilder/en/ss-vergleiche.svg b/web/lessons/bilder/en/ss-vergleiche.svg deleted file mode 100644 index 57a87c8a..00000000 --- a/web/lessons/bilder/en/ss-vergleiche.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Comparisons in the worst case -Linear searchBinary search -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Number of cards -Comparisons - - -100 comparisons -7 comparisons - diff --git a/web/lessons/bilder/en/uebergabe.svg b/web/lessons/bilder/en/uebergabe.svg deleted file mode 100644 index 6b311d36..00000000 --- a/web/lessons/bilder/en/uebergabe.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - -Call by value: a copy - -Main program -number - -5 - -doubleIt -x - -5 10 - -copy -x becomes 10, but number stays 5. -Like a photocopy: writing on the copy -does not change the original. - -Call by reference: the same array - -Main program -row - - -clear -seats - - - - - - - -false -false -false -false -There is only one array. What clear changes through seats, -the main program sees under row. - diff --git a/web/lessons/bilder/en/zk-zeichen.svg b/web/lessons/bilder/en/zk-zeichen.svg deleted file mode 100644 index 0eb01ebf..00000000 --- a/web/lessons/bilder/en/zk-zeichen.svg +++ /dev/null @@ -1,29 +0,0 @@ - - -String word = "Billygoat" -0 -B -1 -i -2 -l -3 -l -4 -y -5 -g -6 -o -7 -a -8 -t -substring(0, 5) -→ "Billy" -substring(5) -→ "goat" -charAt(0) → 'B'length() → 9 -indexOf("goat") → 5 -The end of substring is not included. - diff --git a/web/lessons/bilder/grace-hopper.jpg b/web/lessons/bilder/grace-hopper.jpg deleted file mode 100644 index d767c68d..00000000 Binary files a/web/lessons/bilder/grace-hopper.jpg and /dev/null differ diff --git a/web/lessons/bilder/hedy-lamarr.jpg b/web/lessons/bilder/hedy-lamarr.jpg deleted file mode 100644 index e304bd1e..00000000 Binary files a/web/lessons/bilder/hedy-lamarr.jpg and /dev/null differ diff --git a/web/lessons/bilder/ike-nassi.jpg b/web/lessons/bilder/ike-nassi.jpg deleted file mode 100644 index a0a78f37..00000000 Binary files a/web/lessons/bilder/ike-nassi.jpg and /dev/null differ diff --git a/web/lessons/bilder/katherine-johnson.jpg b/web/lessons/bilder/katherine-johnson.jpg deleted file mode 100644 index c2df60c5..00000000 Binary files a/web/lessons/bilder/katherine-johnson.jpg and /dev/null differ diff --git a/web/lessons/bilder/kino-saal.svg b/web/lessons/bilder/kino-saal.svg deleted file mode 100644 index 18c5a30a..00000000 --- a/web/lessons/bilder/kino-saal.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Leinwand -Platz (zweiter Index) -0 -1 -2 -3 -4 -Reihe(ersterIndex) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Gestrichelt: sitze[1][2], Reihe 1, Platz 2 -verkauft = truefrei = false - diff --git a/web/lessons/bilder/kino-schleifen.svg b/web/lessons/bilder/kino-schleifen.svg deleted file mode 100644 index 486b7ca5..00000000 --- a/web/lessons/bilder/kino-schleifen.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -äußere Schleife: reihe = 0, 1, 2 -innere Schleife: platz = 0 … 4 -sitze[reihe][platz]ansehen -platz -0 -1 -2 -3 -4 -reihe0 - -1 - -2 - -3 - -4 - -5 - -reihe1 - -6 - -7 - -8 - -9 - -10 - -reihe2 - -11 - -12 - -13 - -14 - -15 -Innen läuft platz durch, erst dann geht reihe eins weiter. - diff --git a/web/lessons/bilder/l01-pap.svg b/web/lessons/bilder/l01-pap.svg deleted file mode 100644 index db333bfd..00000000 --- a/web/lessons/bilder/l01-pap.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Start - Tasse auf den Tisch stellen - Zucker gewünscht? - Zucker hineingeben - Tee noch zu heiß? - 1 Minute warten - Tee trinken - Ende - - - ja - nein - ja - nein - - - - - - - - - - - Start oder Ende - Anweisung - Verzweigung - Ein- oder Ausgabe - - diff --git a/web/lessons/bilder/l02-einfuegen.svg b/web/lessons/bilder/l02-einfuegen.svg deleted file mode 100644 index b33a3b6e..00000000 --- a/web/lessons/bilder/l02-einfuegen.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - Wohin landet das Element? - - - - - obere Hälfte - untere Hälfte - → kommt davor - → kommt dahinter - - ø - → ersetzt den Platzhalter - diff --git a/web/lessons/bilder/l03-lochkarte-beschriftet.svg b/web/lessons/bilder/l03-lochkarte-beschriftet.svg deleted file mode 100644 index ac9ff74d..00000000 --- a/web/lessons/bilder/l03-lochkarte-beschriftet.svg +++ /dev/null @@ -1,1016 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Ecke abgeschnitten: -so liegen alle Karten richtig herum - -Aufdruck zum Mitlesen - - - - -Zeilen 12 und 11: Extralöcher für Buchstaben -1 Spalte = 1 Zeichen -hier P: Löcher in 11 und 7 - -gestanztes Loch - - -80 Spalten = höchstens 80 Zeichen pro Zeile - diff --git a/web/lessons/bilder/l03-lochkarte.svg b/web/lessons/bilder/l03-lochkarte.svg deleted file mode 100644 index a5cbccfb..00000000 --- a/web/lessons/bilder/l03-lochkarte.svg +++ /dev/null @@ -1,995 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/web/lessons/bilder/l03-zuse-lochstreifen.svg b/web/lessons/bilder/l03-zuse-lochstreifen.svg deleted file mode 100644 index d379feb7..00000000 --- a/web/lessons/bilder/l03-zuse-lochstreifen.svg +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -1 -0 -1 -1 -0 -0 -1 -0 - - - -= 1 - -= 0 - diff --git a/web/lessons/bilder/l04-camelcase.svg b/web/lessons/bilder/l04-camelcase.svg deleted file mode 100644 index 49bfc6c8..00000000 --- a/web/lessons/bilder/l04-camelcase.svg +++ /dev/null @@ -1,31 +0,0 @@ - - -preisMitSteuer - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/l04-variable.svg b/web/lessons/bilder/l04-variable.svg deleted file mode 100644 index fa80702f..00000000 --- a/web/lessons/bilder/l04-variable.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - int alter = 14 - - - 14 - - - - alter - - int - - - - - - Wert - Name - Datentyp - Über den Namen kommst du an den Wert. - diff --git a/web/lessons/bilder/l04-zuweisung.svg b/web/lessons/bilder/l04-zuweisung.svg deleted file mode 100644 index 1fce2904..00000000 --- a/web/lessons/bilder/l04-zuweisung.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - punkte - = - punkte + 5 - - - ① rechts ausrechnen: 10 + 5 = 15 - - ② Ergebnis links speichern - - punkte vorher: 10 → punkte nachher: 15 - diff --git a/web/lessons/bilder/l05-tauschen.svg b/web/lessons/bilder/l05-tauschen.svg deleted file mode 100644 index 1658c6b3..00000000 --- a/web/lessons/bilder/l05-tauschen.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - links - Saft - hilf - leer - rechts - Wasser - - - - - - - 1 - 2 - 3 - - - 1: hilf = links - 2: links = rechts - 3: rechts = hilf - - diff --git a/web/lessons/bilder/l07-schreibtischtest-de.svg b/web/lessons/bilder/l07-schreibtischtest-de.svg deleted file mode 100644 index 8cd90476..00000000 --- a/web/lessons/bilder/l07-schreibtischtest-de.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - -a -b -hilf -3 -– -– -3 -5 -– -3 -5 -3 -5 -5 -3 -5 -3 -3 - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/l07-schreibtischtest-en.svg b/web/lessons/bilder/l07-schreibtischtest-en.svg deleted file mode 100644 index ec89eb8e..00000000 --- a/web/lessons/bilder/l07-schreibtischtest-en.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - -a -b -temp -3 -– -– -3 -5 -– -3 -5 -3 -5 -5 -3 -5 -3 -3 - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/l07-schreibtischtest-pt.svg b/web/lessons/bilder/l07-schreibtischtest-pt.svg deleted file mode 100644 index b60eafe9..00000000 --- a/web/lessons/bilder/l07-schreibtischtest-pt.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - -a -b -aux -3 -– -– -3 -5 -– -3 -5 -3 -5 -5 -3 -5 -3 -3 - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/l08-zahlenstrahl.svg b/web/lessons/bilder/l08-zahlenstrahl.svg deleted file mode 100644 index 8381d353..00000000 --- a/web/lessons/bilder/l08-zahlenstrahl.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - - gültig - ungültigungültig - - note >= 1 && note <= 6 - note < 1 || note > 6 - - diff --git a/web/lessons/bilder/l09-wahrheitstabelle.svg b/web/lessons/bilder/l09-wahrheitstabelle.svg deleted file mode 100644 index 3f80291e..00000000 --- a/web/lessons/bilder/l09-wahrheitstabelle.svg +++ /dev/null @@ -1,29 +0,0 @@ - - - a - b - a && b - a || b - !a - true - true - true - true - false - true - false - false - true - false - false - true - false - true - true - false - false - false - false - true - - diff --git a/web/lessons/bilder/l10-kopfgesteuert.svg b/web/lessons/bilder/l10-kopfgesteuert.svg deleted file mode 100644 index 48281954..00000000 --- a/web/lessons/bilder/l10-kopfgesteuert.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - Ablauf der While-Schleife - - ① Bedingung prüfen - - ② Inhalt ausführen - - - - - - true - wieder - nach oben - false - weiter unter der Schleife - diff --git a/web/lessons/bilder/l11-vergleich.svg b/web/lessons/bilder/l11-vergleich.svg deleted file mode 100644 index 48d918dd..00000000 --- a/web/lessons/bilder/l11-vergleich.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - kopfgesteuert (While) - fußgesteuert (Do-while) - - - ① Bedingung - - ② Inhalt - - ① Inhalt - - ② Bedingung - - - - - - - true - true - Ist die Bedingung gleich false, - läuft der Inhalt nie. - Der Inhalt läuft immer - mindestens einmal. - diff --git a/web/lessons/bilder/l12-for-ablauf.svg b/web/lessons/bilder/l12-for-ablauf.svg deleted file mode 100644 index 4cbb5765..00000000 --- a/web/lessons/bilder/l12-for-ablauf.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - ① Start (nur einmal) - - ② Bedingung prüfen - - ③ Inhalt ausführen - - ④ Schritt, z. B. i++ - - - - - - - - true - zurück - zu ② - false - fertig - diff --git a/web/lessons/bilder/l12-while-zu-for.svg b/web/lessons/bilder/l12-while-zu-for.svg deleted file mode 100644 index 8aa5bd82..00000000 --- a/web/lessons/bilder/l12-while-zu-for.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Start - Bedingung - Schritt - StartBedingungSchritt - diff --git a/web/lessons/bilder/l13-verschachtelt-tabelle.svg b/web/lessons/bilder/l13-verschachtelt-tabelle.svg deleted file mode 100644 index dd830d43..00000000 --- a/web/lessons/bilder/l13-verschachtelt-tabelle.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String text = "" - for int j = 1; j <= 4; j++ - text = text + i * j + " " - output: text - - VARIABLENKONSOLE - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - text = - "" - text = - "1 " - text = - "1 2 " - text = - "1 2 3 " - text = - "1 2 3 4 " - text = - "2 " - text = - "2 4 " - text = - "2 4 6 " - text = - "2 4 6 8 " - text = - "3 " - text = - "3 6 " - text = - "3 6 9 " - text = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/web/lessons/bilder/l14-array.svg b/web/lessons/bilder/l14-array.svg deleted file mode 100644 index f1fbf46a..00000000 --- a/web/lessons/bilder/l14-array.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - int[] zahlen = {2, 4, 6, 8, 10} - Index - 02 - 14 - 26 - 38 - 410 - Wert - - zahlen[0] ist 2zahlen[4] ist 10 - 5 Fächer, gezählt wird ab 0: der letzte Index ist 4. - diff --git a/web/lessons/bilder/l20-cpp.svg b/web/lessons/bilder/l20-cpp.svg deleted file mode 100644 index e1261a7d..00000000 --- a/web/lessons/bilder/l20-cpp.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - rechner.cpp - Quellcode in C++ - - - - - - - - - - Compiler - - Maschinencode - - Windows - - Compiler - - Maschinencode - - macOS - - Compiler - - Maschinencode - - Linux - Für jedes System - neu übersetzen - diff --git a/web/lessons/bilder/l20-jvm.svg b/web/lessons/bilder/l20-jvm.svg deleted file mode 100644 index b8a3d152..00000000 --- a/web/lessons/bilder/l20-jvm.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Einmaleins.java - Quellcode, für Menschen - - Einmaleins.class - Bytecode: CA FE BA BE … - - - - - - - Compiler javac - - JVM - - Windows - - JVM - - macOS - - JVM - - Linux - Einmal schreiben, - überall ausführen - diff --git a/web/lessons/bilder/margaret-hamilton.jpg b/web/lessons/bilder/margaret-hamilton.jpg deleted file mode 100644 index 9a5ae22d..00000000 Binary files a/web/lessons/bilder/margaret-hamilton.jpg and /dev/null differ diff --git a/web/lessons/bilder/mvc-klick.svg b/web/lessons/bilder/mvc-klick.svg deleted file mode 100644 index 47aa440e..00000000 --- a/web/lessons/bilder/mvc-klick.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - die Anzeige - - Controller - der Zuhörer - - Model - Zaehler - - 0 - 1 - 2 - 3 - - +1 - - wert = 0 - wert = 1 - wert = 2 - wert = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 erhoehen() - 2 erhoehen() - - - 3 getWert() - 3 getWert() - - - 4 aktualisieren(wert) - 4 aktualisieren(wert) - - Das Model kennt weder die View noch den Controller. - diff --git a/web/lessons/bilder/pt/aufgabe-mehrwertsteuer.svg b/web/lessons/bilder/pt/aufgabe-mehrwertsteuer.svg deleted file mode 100644 index 70b4b6bb..00000000 --- a/web/lessons/bilder/pt/aufgabe-mehrwertsteuer.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - telemóvel - - - - abrir - - - - - - - - - - - - - - - NOVO - - tablet - - - - - TALÃO - Telem. dobrável - s/ IVA1000,00 € - +23% IVA230,00 € - - TOTAL1230,00 € - - - - Assim se forma o preço: - - sem IVA 1000 € - - 230 € - - - - - - - - - - - Finanças - - - - - - com IVA 1230 € - É isto que pagas na caixa. - diff --git a/web/lessons/bilder/pt/kino-saal.svg b/web/lessons/bilder/pt/kino-saal.svg deleted file mode 100644 index 65dc50de..00000000 --- a/web/lessons/bilder/pt/kino-saal.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Ecrã -Lugar (segundo índice) -0 -1 -2 -3 -4 -Fila(primeiroíndice) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Tracejado: lugares[1][2], fila 1, lugar 2 -vendido = truelivre = false - diff --git a/web/lessons/bilder/pt/kino-schleifen.svg b/web/lessons/bilder/pt/kino-schleifen.svg deleted file mode 100644 index c9788f21..00000000 --- a/web/lessons/bilder/pt/kino-schleifen.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -ciclo exterior: fila = 0, 1, 2 -ciclo interior: lugar = 0 … 4 -lugares[fila][lugar]ver -lugar -0 -1 -2 -3 -4 -fila0 - -1 - -2 - -3 - -4 - -5 - -fila1 - -6 - -7 - -8 - -9 - -10 - -fila2 - -11 - -12 - -13 - -14 - -15 -Lá dentro, lugar percorre tudo; só depois fila avança um. - diff --git a/web/lessons/bilder/pt/l01-pap.svg b/web/lessons/bilder/pt/l01-pap.svg deleted file mode 100644 index 21554373..00000000 --- a/web/lessons/bilder/pt/l01-pap.svg +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Início - Pôr uma chávena na mesa - Quer açúcar? - Juntar açúcar - Chá muito quente? - Esperar 1 minuto - Beber o chá - Fim - - - sim - não - sim - não - - - - - - - - - - - Início ou fim - Instrução - Decisão - Entrada ou saída - - diff --git a/web/lessons/bilder/pt/l02-einfuegen.svg b/web/lessons/bilder/pt/l02-einfuegen.svg deleted file mode 100644 index 135f76b4..00000000 --- a/web/lessons/bilder/pt/l02-einfuegen.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - Onde fica o elemento? - - - - - metade de cima - metade de baixo - → fica antes - → fica depois - - ø - → substitui-o - diff --git a/web/lessons/bilder/pt/l03-lochkarte-beschriftet.svg b/web/lessons/bilder/pt/l03-lochkarte-beschriftet.svg deleted file mode 100644 index 89cefb22..00000000 --- a/web/lessons/bilder/pt/l03-lochkarte-beschriftet.svg +++ /dev/null @@ -1,1015 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -M - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -U - -1 -2 -3 - -5 -6 -7 -8 -9 -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Canto cortado: -assim todos os cartões ficam na posição certa - -Texto impresso para ler - - - - -Linhas 12 e 11: furos extra para letras -1 coluna = 1 carácter -aqui P: furos em 11 e 7 - -furo perfurado - - -80 colunas = no máximo 80 caracteres por linha - diff --git a/web/lessons/bilder/pt/l03-lochkarte.svg b/web/lessons/bilder/pt/l03-lochkarte.svg deleted file mode 100644 index 1b08640f..00000000 --- a/web/lessons/bilder/pt/l03-lochkarte.svg +++ /dev/null @@ -1,994 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -M - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -U - -1 -2 -3 - -5 -6 -7 -8 -9 -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/web/lessons/bilder/pt/l04-camelcase.svg b/web/lessons/bilder/pt/l04-camelcase.svg deleted file mode 100644 index 282a1364..00000000 --- a/web/lessons/bilder/pt/l04-camelcase.svg +++ /dev/null @@ -1,31 +0,0 @@ - - -precoComIva - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/web/lessons/bilder/pt/l04-variable.svg b/web/lessons/bilder/pt/l04-variable.svg deleted file mode 100644 index adcbe73b..00000000 --- a/web/lessons/bilder/pt/l04-variable.svg +++ /dev/null @@ -1,22 +0,0 @@ - - - int idade = 14 - - - 14 - - - - idade - - int - - - - - - Valor - Nome - Tipo de dados - Pelo nome chegas ao valor. - diff --git a/web/lessons/bilder/pt/l04-zuweisung.svg b/web/lessons/bilder/pt/l04-zuweisung.svg deleted file mode 100644 index 509b8fa9..00000000 --- a/web/lessons/bilder/pt/l04-zuweisung.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - pontos - = - pontos + 5 - - - ① calcular a direita: 10 + 5 = 15 - - ② guardar o resultado à esquerda - - pontos antes: 10 → pontos depois: 15 - diff --git a/web/lessons/bilder/pt/l05-tauschen.svg b/web/lessons/bilder/pt/l05-tauschen.svg deleted file mode 100644 index 26136056..00000000 --- a/web/lessons/bilder/pt/l05-tauschen.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - esquerda - sumo - aux - vazio - direita - água - - - - - - - 1 - 2 - 3 - - - 1: aux = esquerda - 2: esquerda = direita - 3: direita = aux - - diff --git a/web/lessons/bilder/pt/l08-zahlenstrahl.svg b/web/lessons/bilder/pt/l08-zahlenstrahl.svg deleted file mode 100644 index 7c20196b..00000000 --- a/web/lessons/bilder/pt/l08-zahlenstrahl.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - - válida - inválidainválida - - nota >= 1 && nota <= 5 - nota < 1 || nota > 5 - - diff --git a/web/lessons/bilder/pt/l10-kopfgesteuert.svg b/web/lessons/bilder/pt/l10-kopfgesteuert.svg deleted file mode 100644 index bc6c0131..00000000 --- a/web/lessons/bilder/pt/l10-kopfgesteuert.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - Como funciona o ciclo while - - ① verificar a condição - - ② executar o interior - - - - - - true - voltar - para cima - false - continuar por baixo do ciclo - diff --git a/web/lessons/bilder/pt/l11-vergleich.svg b/web/lessons/bilder/pt/l11-vergleich.svg deleted file mode 100644 index 32376a16..00000000 --- a/web/lessons/bilder/pt/l11-vergleich.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - pré-teste (while) - pós-teste (do-while) - - - ① condição - - ② interior - - ① interior - - ② condição - - - - - - - true - true - Se a condição for logo falsa, - o interior nunca é executado. - O interior é sempre executado - pelo menos uma vez. - diff --git a/web/lessons/bilder/pt/l12-for-ablauf.svg b/web/lessons/bilder/pt/l12-for-ablauf.svg deleted file mode 100644 index 7436cda1..00000000 --- a/web/lessons/bilder/pt/l12-for-ablauf.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - ① Início (só uma vez) - - ② verificar a condição - - ③ executar o interior - - ④ passo, p. ex. i++ - - - - - - - - true - voltar - ao ② - false - fim - diff --git a/web/lessons/bilder/pt/l12-while-zu-for.svg b/web/lessons/bilder/pt/l12-while-zu-for.svg deleted file mode 100644 index ffba5bc5..00000000 --- a/web/lessons/bilder/pt/l12-while-zu-for.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Início - Condição - Passo - InícioCondiçãoPasso - diff --git a/web/lessons/bilder/pt/l13-verschachtelt-tabelle.svg b/web/lessons/bilder/pt/l13-verschachtelt-tabelle.svg deleted file mode 100644 index 070abbc1..00000000 --- a/web/lessons/bilder/pt/l13-verschachtelt-tabelle.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String texto = "" - for int j = 1; j <= 4; j++ - texto = texto + i * j + " " - output: texto - - VARIÁVEISCONSOLA - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - texto = - "" - texto = - "1 " - texto = - "1 2 " - texto = - "1 2 3 " - texto = - "1 2 3 4 " - texto = - "2 " - texto = - "2 4 " - texto = - "2 4 6 " - texto = - "2 4 6 8 " - texto = - "3 " - texto = - "3 6 " - texto = - "3 6 9 " - texto = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/web/lessons/bilder/pt/l14-array.svg b/web/lessons/bilder/pt/l14-array.svg deleted file mode 100644 index 9e19b6bc..00000000 --- a/web/lessons/bilder/pt/l14-array.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - int[] numeros = {2, 4, 6, 8, 10} - Índice - 02 - 14 - 26 - 38 - 410 - Valor - - numeros[0] é 2numeros[4] é 10 - 5 posições, conta-se a partir de 0: o último índice é 4. - diff --git a/web/lessons/bilder/pt/l20-cpp.svg b/web/lessons/bilder/pt/l20-cpp.svg deleted file mode 100644 index 96ebab28..00000000 --- a/web/lessons/bilder/pt/l20-cpp.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - calculadora.cpp - código-fonte em C++ - - - - - - - - - - compilador - - código máquina - - Windows - - compilador - - código máquina - - macOS - - compilador - - código máquina - - Linux - Compilar de novo - para cada sistema - diff --git a/web/lessons/bilder/pt/l20-jvm.svg b/web/lessons/bilder/pt/l20-jvm.svg deleted file mode 100644 index 87866b70..00000000 --- a/web/lessons/bilder/pt/l20-jvm.svg +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Tabuada.java - código-fonte, para pessoas - - Tabuada.class - bytecode: CA FE BA BE … - - - - - - - compilador javac - - JVM - - Windows - - JVM - - macOS - - JVM - - Linux - Escrever uma vez, - executar em qualquer lado - diff --git a/web/lessons/bilder/pt/mvc-klick.svg b/web/lessons/bilder/pt/mvc-klick.svg deleted file mode 100644 index 08d276a8..00000000 --- a/web/lessons/bilder/pt/mvc-klick.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - o visor - - Controller - o ouvinte - - Model - Contador - - 0 - 1 - 2 - 3 - - +1 - - valor = 0 - valor = 1 - valor = 2 - valor = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 aumentar() - 2 aumentar() - - - 3 getValor() - 3 getValor() - - - 4 atualizar(valor) - 4 atualizar(valor) - - O model não conhece nem a view nem o controller. - diff --git a/web/lessons/bilder/pt/schiffe-versenken.svg b/web/lessons/bilder/pt/schiffe-versenken.svg deleted file mode 100644 index 8aeefaab..00000000 --- a/web/lessons/bilder/pt/schiffe-versenken.svg +++ /dev/null @@ -1,55 +0,0 @@ - - -A -0 -B -1 -C -2 -D -3 -E -4 -1 -0 - - - - - -2 -1 - - - - - -3 -2 - - - - - -4 -3 - - - - - -5 -4 - - - - - - - - - - - -Índice - diff --git a/web/lessons/bilder/pt/ss-binaere-suche.svg b/web/lessons/bilder/pt/ss-binaere-suche.svg deleted file mode 100644 index 8caadd8c..00000000 --- a/web/lessons/bilder/pt/ss-binaere-suche.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Procura-se o 27. As cartas estão ordenadas. -1.º meio: 13 < 27, a esquerda sai -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2.º meio: 21 < 27, a esquerda sai -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3.º meio: encontrado após 3 comparações -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/web/lessons/bilder/pt/ss-bubblesort.svg b/web/lessons/bilder/pt/ss-bubblesort.svg deleted file mode 100644 index 9b13f206..00000000 --- a/web/lessons/bilder/pt/ss-bubblesort.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -Uma ronda: comparar vizinhos -7♥ -3♠ -10♦ -5♣ -2♥ -Início -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: trocar -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: certo -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: trocar -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: trocar -A carta maior sobe para a direita como uma bolha. - diff --git a/web/lessons/bilder/pt/ss-lineare-suche.svg b/web/lessons/bilder/pt/ss-lineare-suche.svg deleted file mode 100644 index 02ac3cd4..00000000 --- a/web/lessons/bilder/pt/ss-lineare-suche.svg +++ /dev/null @@ -1,23 +0,0 @@ - - -Procura-se o 9. As cartas estão baralhadas. -7♥ -✗ -1. -3♠ -✗ -2. -10♦ -✗ -3. -5♣ -✗ -4. -9♠ -✓ -5. - - - -Virar da esquerda para a direita:Após 5 comparações, o 9 é encontrado. - diff --git a/web/lessons/bilder/pt/ss-vergleiche.svg b/web/lessons/bilder/pt/ss-vergleiche.svg deleted file mode 100644 index 672279b3..00000000 --- a/web/lessons/bilder/pt/ss-vergleiche.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Comparações no pior caso -Pesquisa linearPesquisa binária -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Número de cartas -Comparações - - -100comparações -7 comparações - diff --git a/web/lessons/bilder/pt/uebergabe.svg b/web/lessons/bilder/pt/uebergabe.svg deleted file mode 100644 index 2fd20235..00000000 --- a/web/lessons/bilder/pt/uebergabe.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - -Passagem por valor: uma cópia - -Programa principal -numero - -5 - -duplicar -x - -5 10 - -cópia -x passa a 10, mas numero fica 5. -Como uma fotocópia: escrever na cópia -não altera o original. - -Passagem por referência: o mesmo array - -Programa principal -fila - - -limpar -lugares - - - - - - - -false -false -false -false -Só há um array. O que limpar altera através de lugares, -o programa principal vê em fila. - diff --git a/web/lessons/bilder/pt/zk-zeichen.svg b/web/lessons/bilder/pt/zk-zeichen.svg deleted file mode 100644 index eac69023..00000000 --- a/web/lessons/bilder/pt/zk-zeichen.svg +++ /dev/null @@ -1,31 +0,0 @@ - - -String palavra = "Passatempo" -0 -P -1 -a -2 -s -3 -s -4 -a -5 -t -6 -e -7 -m -8 -p -9 -o -substring(0, 5) -→ "Passa" -substring(5) -→ "tempo" -charAt(0) → 'P'length() → 10 -indexOf("tempo") → 5 -O fim de substring não está incluído. - diff --git a/web/lessons/bilder/schiffe-versenken.svg b/web/lessons/bilder/schiffe-versenken.svg deleted file mode 100644 index 289e2fd5..00000000 --- a/web/lessons/bilder/schiffe-versenken.svg +++ /dev/null @@ -1,55 +0,0 @@ - - -A -0 -B -1 -C -2 -D -3 -E -4 -1 -0 - - - - - -2 -1 - - - - - -3 -2 - - - - - -4 -3 - - - - - -5 -4 - - - - - - - - - - - -Index - diff --git a/web/lessons/bilder/ss-binaere-suche.svg b/web/lessons/bilder/ss-binaere-suche.svg deleted file mode 100644 index c05c4a47..00000000 --- a/web/lessons/bilder/ss-binaere-suche.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Gesucht: die 27. Die Karten sind sortiert. -1. Mitte: 13 < 27, links fällt weg -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2. Mitte: 21 < 27, links fällt weg -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3. Mitte: gefunden nach 3 Vergleichen -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/web/lessons/bilder/ss-bubblesort.svg b/web/lessons/bilder/ss-bubblesort.svg deleted file mode 100644 index 6c47eef5..00000000 --- a/web/lessons/bilder/ss-bubblesort.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -Ein Durchgang: Nachbarn vergleichen -7♥ -3♠ -10♦ -5♣ -2♥ -Start -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: tauschen -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: passt -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: tauschen -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: tauschen -Die größte Karte steigt wie eine Blase nach rechts. - diff --git a/web/lessons/bilder/ss-lineare-suche.svg b/web/lessons/bilder/ss-lineare-suche.svg deleted file mode 100644 index edc27702..00000000 --- a/web/lessons/bilder/ss-lineare-suche.svg +++ /dev/null @@ -1,23 +0,0 @@ - - -Gesucht: die 9. Die Karten sind gemischt. -7♥ -✗ -1. -3♠ -✗ -2. -10♦ -✗ -3. -5♣ -✗ -4. -9♠ -✓ -5. - - - -Von links nach rechts umdrehen:Nach 5 Vergleichen ist die 9 gefunden. - diff --git a/web/lessons/bilder/ss-vergleiche.svg b/web/lessons/bilder/ss-vergleiche.svg deleted file mode 100644 index 1cf631a0..00000000 --- a/web/lessons/bilder/ss-vergleiche.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Vergleiche im schlimmsten Fall -Lineare SucheBinäre Suche -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Anzahl der Karten -Vergleiche - - -100 Vergleiche -7 Vergleiche - diff --git a/web/lessons/bilder/uebergabe.svg b/web/lessons/bilder/uebergabe.svg deleted file mode 100644 index 7e5b81f5..00000000 --- a/web/lessons/bilder/uebergabe.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - -Wertübergabe: eine Kopie - -Hauptprogramm -zahl - -5 - -verdopple -x - -5 10 - -Kopie -x wird 10, aber zahl bleibt 5. -Wie bei einer Fotokopie: Wer auf der Kopie schreibt, -ändert das Original nicht. - -Referenzübergabe: dasselbe Array - -Hauptprogramm -reihe - - -leeren -plaetze - - - - - - - -false -false -false -false -Es gibt nur ein Array. Was leeren über plaetze ändert, -sieht das Hauptprogramm unter reihe. - diff --git a/web/lessons/bilder/zk-zeichen.svg b/web/lessons/bilder/zk-zeichen.svg deleted file mode 100644 index 030e9d81..00000000 --- a/web/lessons/bilder/zk-zeichen.svg +++ /dev/null @@ -1,27 +0,0 @@ - - -String wort = "Geißbock" -0 -G -1 -e -2 -i -3 -ß -4 -b -5 -o -6 -c -7 -k -substring(0, 4) -→ "Geiß" -substring(4) -→ "bock" -charAt(0) → 'G'length() → 8 -indexOf("bock") → 4 -Das Ende von substring gehört nicht dazu. - diff --git a/web/lessons/de/01-was-ist-ein-struktogramm.md b/web/lessons/de/01-was-ist-ein-struktogramm.md deleted file mode 100644 index ccaf69d1..00000000 --- a/web/lessons/de/01-was-ist-ein-struktogramm.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -titel: Was ist ein Struktogramm? -bereich: Erste Schritte -kurz: Algorithmen als Folge klarer Schritte darstellen und lesen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Algorithmus ist -- ein Struktogramm lesen -- die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen -::: - -## Ein Algorithmus ist eine genaue Anleitung - -Ein **Algorithmus** beschreibt Schritt für Schritt, wie man eine Aufgabe löst. Jeder Schritt muss so eindeutig sein, dass ihn jemand ohne Nachfragen ausführen kann, ein Mensch genauso wie ein Computer. - -Du kennst Algorithmen aus dem Alltag: ein Kochrezept, eine Wegbeschreibung oder die Anleitung für einen Schrank. - -::: diagramm l1-tee.visustruct -Tee kochen als Struktogramm: Jeder Kasten ist ein Schritt. -::: - -::: angeberwissen -Määäh, wer hat das erste Programm geschrieben? Eine Frau, und zwar schon 1843, lange vor dem ersten Computer! **Ada Lovelace** beschrieb Schritt für Schritt, wie die *Analytical Engine* (Analytische Maschine) von Charles Babbage die sogenannten Bernoulli-Zahlen ausrechnen sollte. Das war ein echter Algorithmus für eine Maschine. Die Befehle sollten auf Lochkarten stehen (mehr dazu in Lektion 3). Gebaut wurde die Maschine allerdings nie. - -Ada hatte noch eine geniale Idee: Eine solche Maschine kann mehr als rechnen. Sie könnte auch mit Buchstaben, Zeichen oder sogar Musik arbeiten. Genau das machen Computer heute. Ihr zu Ehren heißt eine Programmiersprache **Ada**. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), Mathematikerin (Bild: Aquarell von Alfred Edward Chalon, um 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), gemeinfrei). -::: - -## So liest du ein Struktogramm - -Ein Struktogramm wird **von oben nach unten** gelesen. Jeder Kasten ist ein Schritt. Erst wenn ein Schritt fertig ist, kommt der nächste dran. - -Die Darstellung stammt von **Isaac Nassi und Ben Shneiderman** (1973). Deshalb heißt sie auch Nassi-Shneiderman-Diagramm. In Deutschland ist sie als DIN 66261 genormt. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -Links Isaac „Ike“ Nassi (Foto: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/deed.de), zugeschnitten), rechts Ben Shneiderman (Foto: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> Ein Struktogramm hat immer genau einen Anfang (oben) und ein Ende (unten). Es gibt keine Pfeile und keine Sprünge kreuz und quer. Programme, die kreuz und quer springen, nennt man **Spaghetticode**, weil sich ihr Ablauf verheddert wie Nudeln auf dem Teller. Mehr dazu in der nächsten Lektion. - -::: angeberwissen -Määäh, aufgepasst! 1972 waren Nassi und Shneiderman noch Doktoranden an der Stony Brook University bei New York, Shneiderman etwa 25 und Nassi etwa 23. Also quasi zwei Studis, die nebenbei Informatikgeschichte geschrieben haben. -::: - -## Baustein 1: Die Sequenz - -Jeder Algorithmus lässt sich aus nur **drei Grundbausteinen** zusammensetzen: **Sequenz**, [**Verzweigung**]{.blau} und [**Wiederholung**]{.rosa}. Die Farben helfen dir beim Zuordnen: [Anweisungen]{.gelb} sind gelb, Verzweigungen blau, Schleifen rosa. Die Sequenz hat keine eigene Farbe, sie ist einfach die Reihe von Anweisungen untereinander. Dieselben Farben siehst du im Editor und später im Code. Du kannst sie in den Einstellungen unter „Blöcke farbig“ ausschalten. In diesem und den nächsten beiden Schritten lernst du sie einzeln kennen. - -Die **Sequenz** ist der einfachste Baustein: Mehrere Anweisungen werden **nacheinander** ausgeführt, jede genau einmal. - -So sieht sie aus: - -- Jede Anweisung ist ein **einfaches Rechteck**. -- Die Rechtecke stehen ohne Lücke **untereinander**. -- Ausgeführt wird von oben nach unten, eine Anweisung nach der anderen. - -::: diagramm l1-sequenz.visustruct -Vier Anweisungen als Sequenz. Erst wird aufgestanden, dann Zähne geputzt und so weiter. -::: - -> Auch das Tee-Beispiel von oben ist eine Sequenz. - -::: angeberwissen -15 Minuten! So lange hat Shneiderman für die erste Skizze mit Sequenz, Verzweigung und Schleife gebraucht. Ich brauche länger, um mich zwischen Heu und Klee zu entscheiden. -::: - -## Baustein 2: Die Verzweigung - -Oft hängt der nächste Schritt von einer **Bedingung** ab. Eine Bedingung ist eine Frage, die man nur mit **ja oder nein** beantworten kann, zum Beispiel *Regnet es?* In VisuStruct heißt ja **true** (wahr) und nein **false** (falsch). - -Für solche Entscheidungen gibt es die **Verzweigung**. So sieht sie aus: - -- Oben steht die Bedingung in einem **Dreieck**. -- Darunter teilt sich der Kasten in **zwei Spalten**: links der Weg für **true**, rechts der Weg für **false**. -- Es wird **immer nur einer** der beiden Wege ausgeführt. Danach geht es unter der Verzweigung für beide gemeinsam weiter. - -::: diagramm l1-verzweigung.visustruct -Regnet es, wird der linke Weg ausgeführt, sonst der rechte. Danach geht es in beiden Fällen mit „Losgehen“ weiter. -::: - -> Soll in einem Fall nichts passieren, bleibt dieser Weg leer. Das zeigt das Zeichen **ø**. - -::: angeberwissen -Rechtschreib-Alarm! Es heißt Shneiderman, ohne c. Wer „Schneiderman“ schreibt, bekommt von mir einen sanften Hornstupser. Er selbst weist übrigens darauf hin, dass ihm ständig ein c untergeschmuggelt wird. -::: - -## Baustein 3: Die Wiederholung - -Soll etwas **mehrmals** passieren, schreibt man es nicht mehrmals hin. Stattdessen benutzt man eine **Wiederholung**, auch **Schleife** genannt. - -So sieht sie aus: - -- Oben steht die Bedingung, zum Beispiel *Teller nicht leer*. -- Die Schritte, die wiederholt werden, stehen **eingerückt** darunter. Oben und links bildet der Kasten ein **gekipptes L** um sie. -- Vor jedem Durchlauf wird die Bedingung geprüft. Solange sie **true** ist, werden die Schritte im gekippten L noch einmal ausgeführt. Ist sie **false**, geht es unter der Schleife weiter. - -::: diagramm l1-wiederholung.visustruct -Solange der Teller nicht leer ist, wird ein Bissen gegessen. Danach wird der Teller abgeräumt. -::: - -> Es gibt noch weitere Arten von Schleifen. Du lernst sie in späteren Lektionen kennen. - -## Alle drei zusammen - -Richtig nützlich werden die Bausteine, wenn man sie kombiniert. Das Diagramm enthält alle drei: eine Sequenz von oben nach unten, eine [Verzweigung]{.blau} und eine [Schleife]{.rosa}. Gelb sind die einfachen [Anweisungen]{.gelb}. Auch die Anweisungen in einer Verzweigung oder Schleife sind gelb. Genau genommen ist das ganze Diagramm eine Sequenz, denn auch Verzweigung und Schleife stehen in der Reihe von oben nach unten. - -::: diagramm l1-grundbausteine.visustruct -Die Verzweigung erkennst du am Dreieck, die Schleife am gekippten L. Alles zusammen steht in einer Sequenz. -::: - -Die Kästen können auch **ineinander geschachtelt** werden: In einer Schleife oder einem Zweig können wieder Sequenzen, Verzweigungen und Schleifen stehen. - -::: angeberwissen -Trostpflaster für alle mit einer verpatzten Arbeit: Die berühmte Fachzeitschrift *Communications of the ACM* hat den Struktogramm-Artikel abgelehnt. Laut Nassi meinte der Gutachter sogar, sie sollten alle Exemplare einsammeln und verbrennen. Autsch! Er erschien 1973 dann in einem kleineren Heft und wird heute tausendfach zitiert. Ätsch! -::: - -## Erst die Struktur, dann der Code - -Beim Programmieren passieren zwei Dinge gleichzeitig: Du musst dir den Ablauf überlegen und die Schreibweise der Programmiersprache beachten. Mit einem Struktogramm trennst du beides. - -1. Du denkst zuerst über die **Logik** nach: Welche Schritte, welche Bedingungen, welche Wiederholungen? -2. Danach übersetzt du die fertige Struktur in **Java, Python oder JavaScript**. VisuStruct kann dir den Code sogar erzeugen. - -Mit der **Simulation** kannst du dein Struktogramm außerdem Schritt für Schritt ausführen und prüfen, ob es wirklich tut, was es soll. - -## Spiel: Welcher Baustein? - -::: spiel frage -titel: Spiel: Welcher Baustein? -intro: Ludwig beschreibt Dinge aus seinem Alltag. Welcher der drei Bausteine steckt darin? Acht Runden. -antworten: Sequenz | Verzweigung | Wiederholung - -runde: -frage: „Zahnpasta auf die Bürste, Zähne putzen, Mund ausspülen.“ -richtig: Sequenz -> Drei Schritte, jeder genau einmal und immer in dieser Reihenfolge. - -runde: -frage: „Wenn es regnet, nimm den Schirm mit, sonst die Sonnenbrille.“ -richtig: Verzweigung -> Eine Bedingung entscheidet, welcher der zwei Wege genommen wird. - -runde: -frage: „Rühre, bis der Teig glatt ist.“ -richtig: Wiederholung -> Das Rühren wird wiederholt, solange der Teig noch nicht glatt ist. - -runde: -frage: „Laufe zehn Runden um den Sportplatz.“ -richtig: Wiederholung -> Dieselbe Runde wird zehnmal gelaufen. - -runde: -frage: „Schuhe an, Jacke an, Tür auf, losgehen.“ -richtig: Sequenz -> Ein Schritt nach dem anderen, nichts wird übersprungen oder wiederholt. - -runde: -frage: „Ist die Ampel grün, gehst du über die Straße.“ -richtig: Verzweigung -> Auch ohne „sonst“ ist es eine Verzweigung. Der Sonst-Zweig ist einfach leer. - -runde: -frage: „Würfle so lange, bis du eine Sechs hast.“ -richtig: Wiederholung -> Du weißt vorher nicht, wie oft. Gewürfelt wird, bis die Bedingung erfüllt ist. - -runde: -frage: „Sagt jemand ‚Herein‘, gehst du hinein. Sonst wartest du vor der Tür.“ -richtig: Verzweigung -> Die Antwort entscheidet zwischen zwei Wegen: hineingehen oder warten. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Algorithmus ist. → Ein Algorithmus ist eine genaue Anleitung -- Ich kann ein Struktogramm von oben nach unten lesen. → So liest du ein Struktogramm -- Ich kann die drei Grundbausteine Sequenz, Verzweigung und Wiederholung nennen. → Baustein 1: Die Sequenz -- Ich kann jeden Baustein im Struktogramm an seiner Form erkennen. → Alle drei zusammen -::: - -## Wissenstest - -::: quiz -? In welcher Richtung wird ein Struktogramm gelesen? -→ So liest du ein Struktogramm -- [ ] Von links nach rechts -- [x] Von oben nach unten -- [ ] Von unten nach oben -> Jeder Kasten ist ein Schritt, und die Schritte folgen von oben nach unten aufeinander. - -? Welcher Baustein gehört NICHT zu den drei Grundbausteinen? -→ Baustein 1: Die Sequenz -- [ ] Sequenz -- [ ] Verzweigung -- [x] Sprung -- [ ] Wiederholung -> Sprünge gibt es im Struktogramm nicht. Sequenz, Verzweigung und Wiederholung reichen für jeden Algorithmus. - -? Woran erkennst du im Struktogramm eine Verzweigung? -→ Alle drei zusammen -- [ ] An einem gekippten L -- [x] An einem Dreieck mit der Bedingung und zwei Spalten darunter -- [ ] An mehreren Rechtecken untereinander -> Das gekippte L gehört zur Schleife, Rechtecke untereinander sind eine Sequenz. - -? Wann wird der Inhalt einer Schleife noch einmal ausgeführt? -→ Alle drei zusammen -- [x] Solange die Bedingung true ist -- [ ] Nur, wenn die Bedingung false ist -- [ ] Immer genau zweimal -> Vor jedem Durchlauf wird die Bedingung geprüft. Ist sie false, geht es unter der Schleife weiter. - -? Was ist ein Algorithmus? -→ Ein Algorithmus ist eine genaue Anleitung -- [x] Eine eindeutige Schritt-für-Schritt-Anleitung zum Lösen einer Aufgabe -- [ ] Ein bestimmtes Programm in Java -- [ ] Ein Bild, das ein Programm erklärt -> Ein Algorithmus ist unabhängig von der Programmiersprache. Ein Struktogramm ist eine Möglichkeit, ihn aufzuschreiben. -::: diff --git a/web/lessons/de/01-weitere-darstellungen.md b/web/lessons/de/01-weitere-darstellungen.md deleted file mode 100644 index 950d0286..00000000 --- a/web/lessons/de/01-weitere-darstellungen.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: PAP und Pseudocode -bereich: Erste Schritte -kurz: Algorithmen als Programmablaufplan und als Pseudocode darstellen und mit dem Struktogramm vergleichen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen PAP (englisch *flowchart*) lesen und selbst zusammenbauen -- erklären, warum Sprünge im PAP zu Fehlern führen -- einen einfachen Algorithmus als Pseudocode lesen -::: - -## Andere Darstellungen: Der PAP - -Es gibt noch zwei weitere Arten, Algorithmen darzustellen: den **Programmablaufplan**, kurz **PAP**, und den **Pseudocode**. Beide wirst du im Unterricht treffen. Auf Englisch heißt der PAP **flowchart**, auf Deutsch auch Flussdiagramm. Das englische Wort begegnet dir oft, zum Beispiel in Programmen und im Internet. - -Hier siehst du noch einmal unser Struktogramm zum Teetrinken aus der letzten Lektion. Als Nächstes schauen wir uns den PAP an, und zwar mit demselben Beispiel. - -::: diagramm l1-grundbausteine.visustruct -Das Tee-Beispiel als Struktogramm: erst die Tasse, dann die Verzweigung für den Zucker, dann die Schleife. -::: - -### Derselbe Algorithmus als PAP - -::: grafik l01-pap.svg -Das Tee-Beispiel als Programmablaufplan. Der Pfeil von „1 Minute warten“ zurück nach oben bildet die Schleife. -::: - -### Die Elemente - -Ein PAP besteht aus wenigen Symbolen, die mit **Pfeilen** verbunden sind. Er ist nach DIN 66001 genormt. - -- **Oval:** Start oder Ende. Jeder PAP hat genau einen Start und ein Ende. -- **Rechteck:** eine Anweisung, zum Beispiel *Tasse auf den Tisch stellen*. -- **Raute:** eine Verzweigung. In der Raute steht eine Frage, heraus führen zwei Pfeile: **ja** und **nein**. -- **Parallelogramm:** eine Ein- oder Ausgabe. Im Tee-Beispiel kommt sie nicht vor. -- **Pfeil:** zeigt, welcher Schritt als Nächstes kommt. - -Eine Schleife hat im PAP kein eigenes Symbol. Du erkennst sie daran, dass ein Pfeil **zurück nach oben** führt. - -### Die Vorteile - -- **Sehr gut lesbar**, auch für Leute, die noch nie einen PAP gesehen haben. Man folgt einfach den Pfeilen mit dem Finger. -- Flussdiagramme benutzt man nicht nur beim Programmieren, sondern überall, wo ein Ablauf erklärt wird: in Bedienungsanleitungen, bei der Fehlersuche („Leuchtet die Lampe? Nein: Steckt der Stecker?“) oder in Firmen für Arbeitsabläufe. - -## Übung: Kuchen backen als PAP - -Jetzt baust du selbst einen PAP. Ludwig backt einen Kuchen und geht so vor: - -Er gibt die Zutaten in die Schüssel und rührt den Teig. Ist der Teig noch nicht glatt, rührt er weiter. Ist er glatt, füllt er den Teig in die Form und backt den Kuchen 30 Minuten. Dann prüft er mit einem Holzstäbchen: Klebt noch Teig daran, backt er 5 Minuten weiter und prüft noch einmal. Ist das Stäbchen sauber, nimmt er den Kuchen heraus und ruft: „Der Kuchen ist fertig!“ - -::: pap -titel: Übung: Kuchen backen als PAP -auftrag: Die Plätze im Plan sind leer. Lege jedes Element an seinen Platz. Die Form des Platzes hilft dir. -start: Start -anweisung: Zutaten in die Schüssel geben -anweisung: Teig rühren -frage: Teig glatt? | nein -> Teig rühren -anweisung: Teig in die Form füllen -anweisung: Kuchen 30 Minuten backen -frage: Stäbchen sauber? | nein: 5 Minuten weiterbacken -anweisung: Kuchen herausnehmen -ausgabe: „Der Kuchen ist fertig!“ -ende: Ende -erklärung: Zwei Schleifen, zwei Pfeile zurück nach oben: Beim Rühren führt der Pfeil zurück zu „Teig rühren“, beim Backen von „5 Minuten weiterbacken“ zurück zur Frage. Die Ausgabe steht im Parallelogramm. -legende: ja -::: - -**Eigene PAPs zeichnen:** Mit **VisuFlow** zeichnest du deinen PAP ganz leicht selbst. Die Symbole liegen fertig in einer Leiste: Antippen hängt das nächste Symbol an und verbindet es gleich mit einem Pfeil. Zum Start kannst du das Tee-Beispiel laden. Deinen PAP speicherst du als Datei und exportierst ihn als Bild oder PDF, zum Beispiel für dein Heft oder eine Präsentation. [VisuFlow öffnen](https://visustruct.visucode.de/app/?visuflow) - -## Nachteile des PAP: Sprünge - -Ein Pfeil im PAP darf von jedem Symbol zu jedem anderen führen. Das klingt praktisch, ist aber gefährlich. - -Früher hat man Programme genau so geschrieben. Jede Zeile hatte eine **Nummer**, und der Befehl **GOTO**, auf Deutsch „gehe zu“, sprang zu einer Zeilennummer. Jedes GEHE ZU ist ein Pfeil aus dem PAP. Unser Tee-Beispiel sähe so aus: - -``` -10 Tasse auf den Tisch stellen -20 WENN kein Zucker gewünscht, GEHE ZU 40 -30 Zucker hineingeben -40 WENN Tee nicht zu heiß, GEHE ZU 70 -50 1 Minute warten -60 GEHE ZU 40 -70 Tee trinken -``` - -### Eine Zeile mehr, und schon springt es falsch - -Jetzt verbesserst du den Algorithmus: Nach dem Zucker soll der Teebeutel heraus. Du fügst die neue Zeile 40 ein, und alle Zeilen darunter rutschen eine Nummer weiter. Die Sprünge zeigen aber noch auf die alten Nummern: - -``` -10 Tasse auf den Tisch stellen -20 WENN kein Zucker gewünscht, GEHE ZU 40 -30 Zucker hineingeben -40 Teebeutel herausnehmen -50 WENN Tee nicht zu heiß, GEHE ZU 70 -60 1 Minute warten -70 GEHE ZU 40 -80 Tee trinken -``` - -Spiel es durch: Zeile 70 schickt dich nicht mehr zur Prüfung in Zeile 50, sondern zum Teebeutel. Und wenn der Tee kühl genug ist, springt Zeile 50 zu Zeile 70 und nicht mehr zu *Tee trinken*. Du nimmst also immer wieder den Teebeutel heraus und trinkst nie. Bei jeder Änderung musst du jedes GEHE ZU prüfen. In einem großen Programm mit Hunderten Sprüngen geht dabei leicht etwas schief. - -### Kreuz und quer: Spaghetticode - -Mit Pfeilen kann man auch mitten aus einer Schleife herausspringen oder von außen mitten in eine Schleife hinein. Solche **Aussprünge** und Einsprünge machen den Plan schnell unübersichtlich. Bei vielen Sprüngen kreuzen sich die Pfeile, bis der Plan aussieht wie ein Teller Spaghetti. Deshalb nennt man das **Spaghetticode**. Den Ablauf kann man kaum noch verfolgen, und Fehler verstecken sich gut. - -Im Struktogramm gibt es keine Pfeile. Fügst du einen Kasten ein, rutscht alles darunter einfach mit, und kein Sprung kann auf die falsche Stelle zeigen. Für das vorzeitige Verlassen einer Schleife gibt es dort einen eigenen, ordentlichen Kasten, den Aussprung. Den lernst du in Lektion 18 kennen. - -::: angeberwissen -1968 schrieb der Informatiker Edsger Dijkstra einen berühmten Brief: „Go To Statement Considered Harmful“, auf Deutsch „GOTO gilt als schädlich“. Seitdem baut man Programme nur noch aus Sequenz, Verzweigung und Wiederholung. In Java ist `goto` bis heute ein reserviertes Wort, benutzen darf man es aber nicht. Mäh, Spaghetti esse ich lieber, als sie zu programmieren. -::: - -## Pseudocode und Vergleich - -**Pseudocode** ist Text, der wie ein Programm aussieht, aber in normaler Sprache geschrieben ist. Feste Wörter wie WENN und SOLANGE zeigen die Bausteine, die **Einrückung** zeigt, was dazugehört. Sprünge gibt es auch hier nicht. Das ist unser Tee-Beispiel: - -``` -Tasse auf den Tisch stellen -WENN Zucker gewünscht DANN - Zucker hineingeben -ENDE WENN -SOLANGE Tee noch zu heiß - 1 Minute warten -ENDE SOLANGE -Tee trinken -``` - -Pseudocode hat **keine feste Schreibweise**. Manche schreiben FALLS statt WENN, andere setzen einen Pfeil für die Zuweisung (`zahl ← 5`) oder schreiben ganz ohne ENDE-Zeilen. Das ist alles erlaubt. Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. Pseudocode ist also keine weitere Programmiersprache, die man lernen muss. - -::: angeberwissen -Kommt dir Pseudocode bekannt vor? Die Programmiersprache Python sieht ihm sehr ähnlich, und das ist Absicht: Ihr Erfinder Guido van Rossum wollte, dass sich Programme fast wie normaler Text lesen. Auch Python zeigt mit der Einrückung, was zusammengehört. Aber Vorsicht: Python ist eine echte Programmiersprache mit festen Regeln. Ein Leerzeichen zu viel, und der Computer meckert. Pseudocode ist da großzügiger, so wie ich beim Frühstück. -::: - -### Im Vergleich - -- **Struktogramm:** Kästen ineinander, die Schleife ist ein gekipptes L. Die Struktur siehst du auf einen Blick, wilde Sprünge sind unmöglich. -- **PAP:** Symbole mit Pfeilen, die Schleife ist ein Pfeil zurück nach oben. Sehr anschaulich, aber bei großen Plänen schnell unübersichtlich, und Sprünge kreuz und quer sind möglich. -- **Pseudocode:** Text mit Einrückung, die Schleife steht zwischen SOLANGE und ENDE SOLANGE. Schnell geschrieben und schon nah am Programmcode. - -> Alle drei beschreiben denselben Algorithmus. Wenn du eine Form lesen kannst, lernst du die anderen schnell. - -::: angeberwissen -Nassi und Shneiderman wollten den Pfeilen an den Kragen! Ihr Artikel von 1973 hieß „Flowchart Techniques for Structured Programming“. Flowchart ist das englische Wort für Ablaufplan. Mit Pfeilen kann man nämlich kreuz und quer springen, bis der Plan aussieht wie ein Teller Spaghetti. Im Struktogramm geht das gar nicht. Mäh, so mag ich das: ordentlich, wie mein Stall. -::: - -## Spiel: Welche Darstellung? - -::: spiel frage -titel: Spiel: Welche Darstellung? -intro: Ludwig beschreibt eine Darstellung, ohne ihren Namen zu verraten. Ist es das Struktogramm, der PAP oder der Pseudocode? Acht Runden. -antworten: Struktogramm | PAP | Pseudocode - -runde: -frage: „Bei mir zeigt ein Pfeil zurück nach oben, wenn etwas wiederholt wird.“ -richtig: PAP -> Im PAP hat die Schleife kein eigenes Symbol. Ein Pfeil führt zurück nach oben. - -runde: -frage: „Meine Schleife sieht aus wie ein gekipptes L.“ -richtig: Struktogramm -> Oben und links umschließt der Kasten die Schritte, die wiederholt werden. - -runde: -frage: „Ich bestehe aus Wörtern wie WENN und SOLANGE, und die Einrückung zeigt, was dazugehört.“ -richtig: Pseudocode -> Pseudocode ist Text in normaler Sprache, der wie ein Programm aufgebaut ist. - -runde: -frage: „Meine Verzweigung ist eine Raute mit den Ausgängen ja und nein.“ -richtig: PAP -> Im Struktogramm steht die Bedingung dagegen in einem Dreieck. - -runde: -frage: „Bei mir können sich die Pfeile kreuz und quer verheddern wie Spaghetti.“ -richtig: PAP -> Weil Pfeile überallhin führen dürfen, ist im PAP Spaghetticode möglich. - -runde: -frage: „Ich habe keine feste Schreibweise. FALLS statt WENN ist bei mir auch erlaubt.“ -richtig: Pseudocode -> Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. - -runde: -frage: „Die Bedingung steht oben in einem Dreieck, darunter teile ich mich in zwei Spalten.“ -richtig: Struktogramm -> So sieht die Verzweigung im Struktogramm aus: links true, rechts false. - -runde: -frage: „Fügst du einen Kasten ein, rutscht alles darunter einfach mit. Kein Sprung kann danebengehen.“ -richtig: Struktogramm -> Im Struktogramm gibt es keine Pfeile und keine Sprünge, deshalb kann nichts auf die falsche Stelle zeigen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Elemente eines PAP benennen und einen einfachen PAP lesen. → Andere Darstellungen: Der PAP -- Ich kann einen einfachen Algorithmus selbst als PAP zusammenbauen. → Übung: Kuchen backen als PAP -- Ich kann erklären, warum Sprünge mit GOTO zu Fehlern und Spaghetticode führen. → Nachteile des PAP: Sprünge -- Ich kann einen einfachen Algorithmus als Pseudocode lesen. → Pseudocode und Vergleich -::: - -## Wissenstest - -::: quiz -? Woran erkennst du in einem Programmablaufplan (PAP) eine Schleife? -→ Andere Darstellungen: Der PAP -- [ ] An einem gekippten L -- [x] An einem Pfeil, der zurück nach oben führt -- [ ] Am Wort SOLANGE -> Das gekippte L gehört zum Struktogramm, SOLANGE zum Pseudocode. - -? Ordne jedem Symbol im PAP seine Bedeutung zu. -→ Andere Darstellungen: Der PAP -= Oval -> Start oder Ende -= Rechteck -> Anweisung -= Raute -> Verzweigung -= Parallelogramm -> Ein- oder Ausgabe -> Die Raute hat zwei Ausgänge: ja und nein. - -? Wie heißt der Programmablaufplan auf Englisch? -→ Andere Darstellungen: Der PAP -- [x] Flowchart -- [ ] Structure chart -- [ ] Pseudocode -> Auf Deutsch sagt man auch Flussdiagramm. Structure chart ist das Struktogramm. - -? Du fügst in ein Programm mit GOTO eine neue Zeile ein. Was kann passieren? -→ Nachteile des PAP: Sprünge -- [x] Die Sprünge zeigen auf die falschen Zeilen, weil sich die Nummern verschoben haben -- [ ] Nichts, die Sprünge passen sich von selbst an -- [ ] Das Programm wird automatisch schneller -> Jedes GEHE ZU zeigt auf eine feste Zeilennummer. Rutschen die Zeilen weiter, springt das Programm an die falsche Stelle. - -? Was zeigt im Pseudocode, welche Zeilen zu einer Schleife gehören? -→ Pseudocode und Vergleich -- [ ] Die Farbe -- [x] Die Einrückung und das ENDE SOLANGE -- [ ] Ein Pfeil -> Alles zwischen SOLANGE und ENDE SOLANGE ist eingerückt und wird wiederholt. -::: diff --git a/web/lessons/de/02-der-editor.md b/web/lessons/de/02-der-editor.md deleted file mode 100644 index b0980a0d..00000000 --- a/web/lessons/de/02-der-editor.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -titel: Der Editor -bereich: Erste Schritte -kurz: Elemente einfügen, Text eingeben, verschieben und löschen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Element aus dem Bereich Elemente einfügen und einen Platzhalter ersetzen -- den Text eines Kastens ändern und zwischen Anweisung, Eingabe und Ausgabe umschalten -- Kästen verschieben, kopieren, löschen und Schritte rückgängig machen -::: - -## Die Oberfläche - -VisuStruct hat zwei Editoren: den [Struktogramm-Editor](#zeige-editor), um den es in dieser Lektion geht, und rechts den Bereich [Code](#zeige-codelive) für Java-Code (ab Lektion 4). Ist der Struktogramm-Editor eingeklappt, öffnet ihn diese Lektion von selbst. Die blauen Wörter zeigen dir die Bereiche, auch wenn sie gerade eingeklappt sind: Fährst du mit der Maus darüber, öffnet sich der Bereich neben dem Lernbereich und leuchtet auf. Klickst du darauf (oder tippst es an), bleibt er offen. - -Der Struktogramm-Editor besteht aus mehreren Bereichen. Fahre mit der Maus über ein blaues Wort oder tippe es an, dann leuchtet der Bereich im Editor auf, eingeklappte Bereiche wie die Elemente öffnen sich dabei. - -- **Ganz oben die [Leiste](#zeige-leiste):** Neu, Öffnen und Speichern. Ganz rechts das Zahnrad für die [Einstellungen](#zeige-einstellungen): Textgröße und Sprache. -- **Darunter die [Werkzeuge](#zeige-werkzeuge)** über den Elementen und dem Struktogramm: Rückgängig, Ausschneiden, Kopieren, Einfügen, Löschen, Exportieren, Code und Simulation. Die Knöpfe zeigen nur Symbole. Hältst du die Maus darüber, erscheint ihr Name. -- **Links die [Elemente](#zeige-elemente):** Hier liegen alle Bausteine, die du ins Struktogramm ziehen kannst. Manche nennen diesen Bereich auch Palette. -- **In der Mitte das [Struktogramm](#zeige-struktogramm):** Ein neues Diagramm enthält nur einen leeren Platzhalter, erkennbar an **ø**. -- **Unten der [Objektinspektor](#zeige-objektinspektor):** Hier bearbeitest du den Text des ausgewählten Elements. Ist er dir zu klein oder zu groß, ziehst du die Linie mit dem Griff darüber nach oben oder unten. - -> Diesen Lernbereich kannst du jederzeit mit dem [Seitenleisten-Symbol](#zeige-lernen) oben in diesem Bereich einklappen. Dann bleibt links nur die schmale Leiste „🎓 Lernen“, ein Klick darauf öffnet ihn wieder. Er bleibt neben dem Editor offen, damit du gleich ausprobieren kannst, was du liest. Genauso gibt es links die Spalte [📁 Dateien](#zeige-dateien) für deine gespeicherten Programme (Lektion 3) und rechts den Bereich [Code](#zeige-codelive) für Java (ab Lektion 4). - -## Ein Element einfügen - -Ziehe ein Element aus dem Bereich **Elemente** auf das Struktogramm. Beim Ziehen zeigt eine blaue Linie, wo es landet: - -- **Auf den Platzhalter ø:** Das Element ersetzt den Platzhalter. -- **Auf die obere Hälfte eines Kastens:** Das Element kommt davor. -- **Auf die untere Hälfte eines Kastens:** Das Element kommt dahinter. - -::: grafik l02-einfuegen.svg -Wohin du ziehst, entscheidet, wo das neue Element landet. -::: - -Es geht auch ohne Ziehen: **Klicke** auf ein Element im Bereich Elemente (oder tippe es an). Dann wird es hinter dem ausgewählten Kasten eingefügt. Ist der Platzhalter **ø** ausgewählt, ersetzt es ihn. - -Mit der Tastatur springst du mit der **Tab**-Taste zu einem Element und drückst **Enter**. Das neue Element landet dann ebenfalls hinter dem ausgewählten Kasten. - -::: angeberwissen -Kästen packen und herumziehen, so wie du gerade, hat einen schicken Namen: **direkte Manipulation**. Den Begriff hat 1982 Ben Shneiderman geprägt, genau, der vom Struktogramm. Der Mann war fleißiger als ich beim Grasen. -::: - -## Text eingeben - -Klicke einen Kasten an. Er wird blau umrandet, und im Objektinspektor unter dem Diagramm erscheint sein Text. Was du dort tippst, siehst du sofort im Diagramm. - -Schneller geht es mit einem **Doppelklick** auf den Kasten, auf dem Tablet mit einem **Doppeltipp**. Dann tippst du den Text direkt im Struktogramm. **Enter** übernimmt ihn, **Esc** bricht ab. Mit der Tastatur wählst du den Kasten aus und drückst **Enter**. - -Der Kasten aus dem Bereich Elemente heißt **Anweisung**. Oben wählst du, welche Art von Anweisung er sein soll: - -- **Anweisung:** etwas berechnen oder festlegen, z. B. `int zahl = 5`. Wie das geht, lernst du in Lektion 4. -- **Eingabe:** beim Ausführen einen Wert abfragen. Das kommt in Lektion 6. -- **Ausgabe:** etwas anzeigen. Im Diagramm steht dann `output:` davor, z. B. `output: "Hallo"`. - -Für den Anfang brauchst du nur die Ausgabe. Texte, die genau so angezeigt werden sollen, schreibst du in **Anführungszeichen**. - -::: diagramm l2-ausgabe.visustruct -Zwei Ausgaben nacheinander. Öffne das Diagramm im Editor und starte die [Simulation](#zeige-simulation) in den Werkzeugen, um die Ausgabe zu sehen. Genauer lernst du sie in Lektion 7 kennen. -::: - -## Verschieben, kopieren, löschen - -- **Verschieben:** Einen vorhandenen Kasten mit der Maus an eine neue Stelle ziehen. -- **Kopieren und Einfügen:** Kasten auswählen, dann Strg+C und Strg+V (am Mac ⌘C und ⌘V). Die Kopie kommt hinter den ausgewählten Kasten. Ohne Tastatur, zum Beispiel auf dem Tablet, tippst du in den [Werkzeugen](#zeige-werkzeuge) auf **Kopieren** und **Einfügen**. -- **Löschen:** Kasten auswählen und Entf drücken oder in den Werkzeugen auf den [Mülleimer](#zeige-loeschen) „Auswahl löschen“ klicken. -- **Rückgängig:** Strg+Z macht den letzten Schritt rückgängig, Strg+Umschalt+Z stellt ihn wieder her (am Mac ⌘Z und ⌘⇧Z). Ohne Tastatur tippst du in den Werkzeugen auf [Rückgängig](#zeige-rueckgaengig). - -## Übung: Hallo Welt - -::: übung l2-hallo -titel: Hallo Welt -auftrag: Baue ein Struktogramm, das **Hallo Welt** ausgibt. -Ziehe eine **Anweisung** aus den Elementen auf den Platzhalter **ø** und klicke den Kasten an. Im Objektinspektor unter dem Struktogramm stehen oben drei Knöpfe: Anweisung, Eingabe und Ausgabe. Klicke auf **Ausgabe**. Schreibe dann in das Textfeld darunter `"Hallo Welt"`, mit Anführungszeichen. -test: - -> Hallo Welt -braucht: anweisung -tipp: Du brauchst nur einen einzigen Kasten. Der Text, der erscheinen soll, steht im Objektinspektor im Eingabefeld. -tipp: Achte auf die Anführungszeichen. Ohne sie sucht VisuStruct nach einer Variablen mit dem Namen Hallo. -lösung: l2-hallo-loesung.visustruct -::: - -Klicke auf **Prüfen**, wenn du fertig bist. VisuStruct führt dein Struktogramm aus und vergleicht die Ausgabe. - -## Übung: Zwei Zeilen - -::: übung l2-zwei-zeilen -titel: Zwei Zeilen -auftrag: Gib zuerst **Hallo** und danach **Ich lerne Struktogramme** aus, jeweils in einer eigenen Zeile. -test: - -> Hallo | Ich lerne Struktogramme -tipp: Zwei Zeilen heißt: zwei Ausgaben. Die Kästen stehen untereinander und werden von oben nach unten ausgeführt. -tipp: Jede Ausgabe ist ein eigener Kasten. Ziehe die zweite Anweisung auf die untere Hälfte der ersten. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Spiel: Editor-Profi - -::: spiel frage -titel: Spiel: Editor-Profi -intro: Ludwig testet, ob du den Editor im Griff hast. Sieben Runden. - -runde: -frage: Du ziehst eine Anweisung auf den Platzhalter **ø**. Was passiert? -- [x] Die Anweisung ersetzt den Platzhalter. -- Die Anweisung kommt hinter den Platzhalter. -- Nichts, auf ø darf man nichts ziehen. -> Der Platzhalter hält nur den Platz frei. Das neue Element nimmt ihn ein. - -runde: -frage: Du ziehst ein Element auf die **obere** Hälfte eines Kastens. Wo landet es? -- [x] davor -- dahinter -- Es ersetzt den Kasten. -> Obere Hälfte heißt davor, untere Hälfte heißt dahinter. Die blaue Linie zeigt es dir beim Ziehen. - -runde: -frage: Du ziehst ein Element auf die **untere** Hälfte eines Kastens. Wo landet es? -- davor -- [x] dahinter -- ganz am Ende des Struktogramms -> Untere Hälfte heißt: direkt hinter diesen Kasten. - -runde: -frage: Du hast aus Versehen einen Kasten gelöscht. Was holt ihn zurück? -- Strg+C / ⌘C -- [x] Strg+Z / ⌘Z -- Strg+V / ⌘V -> Strg+Z / ⌘Z macht den letzten Schritt rückgängig. Strg+C / ⌘C kopiert, Strg+V / ⌘V fügt ein. - -runde: -frage: Beim Ausführen soll genau **Hallo** erscheinen. Was steht im Kasten? -- [x] output: "Hallo" -- output: Hallo -- "Hallo" -> Text, der genau so angezeigt werden soll, steht in Anführungszeichen. Das `output:` setzt der Editor davor, wenn du die Art Ausgabe wählst. - -runde: -frage: Wo änderst du den Text des ausgewählten Kastens? -- in den Elementen -- [x] im Objektinspektor -- in der Leiste ganz oben -> Der Objektinspektor unter dem Diagramm zeigt den Text des ausgewählten Kastens. - -runde: -frage: Du klickst im Bereich Elemente auf ein Element, ohne zu ziehen. Wo landet es? -- [x] hinter dem ausgewählten Kasten -- ganz am Anfang -- ganz am Ende -> Ohne Ziehen (Klick oder Tab und Enter) kommt das neue Element hinter den Kasten, der gerade ausgewählt ist. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Element aus dem Bereich Elemente einfügen und einen Platzhalter ersetzen. → Ein Element einfügen -- Ich kann den Text eines Kastens ändern und zwischen Anweisung, Eingabe und Ausgabe umschalten. → Text eingeben -- Ich kann Kästen verschieben, kopieren, löschen und Schritte rückgängig machen. → Verschieben, kopieren, löschen -::: - -## Wissenstest - -::: quiz -? Was bedeutet das Zeichen ø in einem Kasten? -→ Ein Element einfügen -- [ ] Der Kasten enthält einen Fehler. -- [x] Hier ist noch ein leerer Platzhalter. -- [ ] Das Programm endet hier. -> Ein Platzhalter wird ersetzt, wenn du ein Element darauf ziehst. - -? Du ziehst eine Anweisung auf die untere Hälfte eines Kastens. Wo landet sie? -→ Ein Element einfügen -- [ ] Davor -- [x] Dahinter -- [ ] Der Kasten wird ersetzt. - -? Wie gibst du den Text Guten Tag genau so aus? -→ Text eingeben -- [ ] output: Guten Tag -- [x] output: "Guten Tag" -- [ ] input: "Guten Tag" -> Ohne Anführungszeichen wird Guten Tag als Name einer Variablen verstanden. -::: diff --git a/web/lessons/de/03-speichern-oeffnen-exportieren.md b/web/lessons/de/03-speichern-oeffnen-exportieren.md deleted file mode 100644 index a7e15afe..00000000 --- a/web/lessons/de/03-speichern-oeffnen-exportieren.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -titel: Speichern, Öffnen, Exportieren -bereich: Erste Schritte -kurz: Dateien sichern, deinen Workspace nutzen, mehrere Diagramme öffnen und als Bild oder PDF weitergeben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einem Diagramm einen Titel geben -- ein Diagramm als .visustruct-Datei speichern und wieder öffnen -- ein Diagramm als Bild (PNG) oder PDF exportieren -- ein zweites Diagramm in einem neuen Reiter öffnen -- meinen Workspace `VisuStruct-Dateien` anlegen -- ein Programm in meinem Workspace speichern -::: - -## Die Datei .visustruct - -Struktogramme werden als **.visustruct**-Datei gespeichert. Dasselbe Format nutzen alle VisuStruct-Versionen: im Browser, auf Mac, iPad und iPhone und die Java-App am Rechner. Du kannst also in der Schule im Browser anfangen und zu Hause in der App weitermachen. - -::: angeberwissen -Zu Nassis Studienzeit gab es keine Dateien zum Speichern, sondern Lochkarten. Nassi erzählt, dass ein Date am Samstagabend so aussah: seine spätere Frau saß mit ihm im Rechenzentrum und stanzte seine Lochkarten. Romantik, Informatik-Style! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -So sah eine Lochkarte aus. Eine Karte war eine Zeile Programm, hier `PRINT *, 'HALLO WELT'` in der Sprache Fortran. Jede Spalte ist ein Zeichen, die Löcher darin sind sein Code. Ein ganzes Programm war ein Stapel solcher Karten. Fiel er herunter, musste man alles wieder sortieren. -::: - -::: angeberwissen -Ein Computerpionier aus unserer Gegend: **Konrad Zuse**. Am 12. Mai 1941 stellte er in Berlin die **Z3** vor, den ersten funktionsfähigen programmgesteuerten Digitalrechner der Welt. Sie rechnete schon im **Binärsystem**, also nur mit 0 und 1, mit rund 2000 Relais (elektrischen Schaltern). Ihr Programm stand nicht auf Lochkarten, sondern als Löcher in einem gebrauchten Kinofilm. Danach erfand Zuse den **Plankalkül**, eine der ersten Programmiersprachen überhaupt. Später lebte er bis zu seinem Tod 1995 in **Hünfeld** bei Fulda. Im Konrad-Zuse-Museum dort stehen Nachbauten der Z3. Mein Ausflugstipp für eure Klasse! Wann es offen hat, steht auf der Webseite der Stadt Hünfeld. -::: - -::: grafik l03-zuse-lochstreifen.svg -So ungefähr sah Zuses Programmstreifen aus: ein alter Kinofilm mit gestanzten Löchern. Jede Spalte ist ein Befehl aus 8 Stellen. Ein Loch heißt 1, kein Loch heißt 0. Die Maschine las den Streifen Befehl für Befehl. -::: - -## Speichern und Öffnen - -- **[Speichern](#zeige-speichern)** (Strg+S, am Mac ⌘S): Das Diagramm wird als Datei gespeichert. Ist die Spalte **Dateien** eingerichtet (siehe unten), landet es dort. Sonst fragt der Browser nach dem Ort oder legt die Datei in den Download-Ordner. -- **[Öffnen](#zeige-oeffnen)** (Strg+O, am Mac ⌘O): Eine gespeicherte Datei wieder laden. Du kannst eine Datei auch einfach ins Browserfenster ziehen. - -> Deine Übungen merkt sich der Browser automatisch. Das klappt aber nur in diesem Browser auf diesem Gerät. Wichtige Arbeiten solltest du deshalb immer zusätzlich als Datei speichern. - -## Die Spalte Dateien - -Links neben dem Lernbereich findest du die Spalte [📁 Dateien](#zeige-dateien). Dort sammelst du alle deine Programme an einem Ort, deinem **Workspace** (Arbeitsbereich). - -**Jetzt du:** Richte deinen Workspace jetzt gleich ein, bevor du weiterliest. Ab heute speicherst du alle deine Programme dort. - -1. Lege einen Workspace an: Öffne die Spalte und klicke auf **Ordner wählen**. Gehe im Fenster zu deinen **Dokumenten**, lege dort mit **Neuer Ordner** einen Ordner namens `VisuStruct-Dateien` an und wähle ihn aus. Erlaube VisuStruct, den Ordner zu benutzen. Das geht in Chrome und Edge. Auf dem iPad und in anderen Browsern klickst du stattdessen auf **Im Browser speichern**. -2. Speichere wie gewohnt. VisuStruct legt in deinem Workspace automatisch einen Ordner mit dem heutigen Datum an, zum Beispiel `2026-10-23`, und speichert das Programm darin. Der Dateiname kommt aus dem Titel. - -So sieht dein Workspace danach aus: - -```text -VisuStruct-Dateien ← Workspace -└── 2026-10-23 ← Tag - └── Fakultaet.visustruct -``` - -Ein Klick auf ein Programm in der Spalte öffnet es wieder. So findest du später jede Stunde schnell wieder. - -Auch dein **Lernstand** landet im Workspace: in der Datei `learnProgress.json` stehen deine erledigten Lektionen und Übungen. Öffnest du den Workspace in einem anderen Browser, ist dein Lernstand wieder da. - -> Warum ein echter Ordner? Dein Lernstand und deine Programme liegen dann als Dateien darin. Den Ordner kannst du mitnehmen, zum Beispiel auf einem USB-Stick oder in deinem Cloud-Ordner wie OneDrive. So machst du zu Hause an einem anderen Rechner genau da weiter, wo du aufgehört hast. Speicherst du nur im Browser, bleibt alles in diesem einen Browser auf diesem Gerät. Löscht jemand die Browserdaten, ist auch dein Lernstand weg. - -## Mehrere Diagramme in Reitern - -Über dem Struktogramm siehst du die [Reiter](#zeige-reiter). Mit **+** oder [Neu](#zeige-neu) öffnest du ein weiteres, leeres Diagramm. Ein Punkt • vor dem Namen zeigt, dass es ungespeicherte Änderungen gibt. - -Den [Titel](#zeige-titel) eines Diagramms trägst du in das Feld über dem Struktogramm ein. Er erscheint auch beim Exportieren und wird als Dateiname vorgeschlagen. - -## Weitergeben: Bild, PDF oder Datei - -Über [Exportieren](#zeige-exportieren) bei den Werkzeugen (Symbol mit Pfeil nach oben) gibst du dein Struktogramm weiter: - -- **Bild (PNG):** zum Einfügen in ein Arbeitsblatt oder eine Präsentation -- **Vektorgrafik (SVG):** bleibt in jeder Größe scharf -- **PDF (A4):** eine Seite zum Abgeben oder Ausdrucken -- **VisuStruct-Datei (.visustruct):** zum Weitergeben an jemanden, der dein Struktogramm in VisuStruct öffnen und weiterbearbeiten soll, zum Beispiel per Mail oder über die Lernplattform -- **Drucken** (Strg+P, am Mac ⌘P): direkt auf den Drucker - -Unter dem [Zahnrad](#zeige-textgroesse) oben rechts stellst du bei Textgröße die Schriftgröße ein. Ein größerer Text wirkt auch beim Exportieren als Bild. - -## Ausprobieren - -Öffne eine deiner Übungen aus Lektion 2 und probiere Folgendes aus: - -1. Gib dem Diagramm im Feld [Titel](#zeige-titel) über dem Struktogramm einen Namen, zum Beispiel `Hallo Welt`. -2. [Speichere](#zeige-speichern) es in deinem Workspace. Schau in der Spalte [📁 Dateien](#zeige-dateien) nach: Steht es im Ordner von heute? -3. Öffne mit **+** einen zweiten Reiter und klicke dort in der Spalte Dateien auf dein Programm. Es öffnet sich wieder. -4. [Exportiere](#zeige-exportieren) es als **Bild (PNG)** und als **PDF (A4)**. Öffne beide Dateien und vergleiche sie. - -Klappt alles? Dann weißt du, wo deine Programme liegen, und kannst sie weitergeben. - -## Spiel: Wie gibst du es weiter? - -::: spiel frage -titel: Spiel: Wie gibst du es weiter? -intro: Ludwig will sein Struktogramm weitergeben. Welches Format passt am besten? Sechs Runden. -frage: Welches Format passt? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: Du willst morgen an deinem Struktogramm weiterarbeiten. -richtig: .visustruct -> Nur die .visustruct-Datei kann VisuStruct wieder öffnen und bearbeiten. - -runde: -frage: Das Struktogramm soll als Bild in eine Präsentation. -richtig: PNG -> Ein PNG-Bild lässt sich in jede Präsentation und jedes Arbeitsblatt einfügen. - -runde: -frage: Die Lehrkraft will deine Abgabe als eine A4-Seite ausdrucken. -richtig: PDF -> Das PDF ist genau eine A4-Seite zum Abgeben oder Ausdrucken. - -runde: -frage: Das Struktogramm kommt auf ein Plakat und soll auch riesig noch scharf sein. -richtig: SVG -> Eine Vektorgrafik bleibt in jeder Größe scharf. Ein PNG wird beim Vergrößern pixelig. - -runde: -frage: Deine Freundin soll das Struktogramm in ihrer VisuStruct-App auf dem iPad ändern. -richtig: .visustruct -> Alle VisuStruct-Versionen öffnen dieselbe .visustruct-Datei: im Browser, auf Mac, iPad und iPhone. - -runde: -frage: Du willst ein Bild schnell in einen Chat schicken. -richtig: PNG -> Ein PNG kann jeder sofort ansehen, ohne VisuStruct. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einem Diagramm einen Titel geben. → Ausprobieren -- Ich kann ein Diagramm als .visustruct-Datei speichern und wieder öffnen. → Speichern und Öffnen -- Ich kann ein Diagramm als Bild (PNG) oder PDF exportieren. → Weitergeben: Bild, PDF oder Datei -- Ich kann ein zweites Diagramm in einem neuen Reiter öffnen. → Mehrere Diagramme in Reitern -- Ich kann meinen Workspace `VisuStruct-Dateien` anlegen. → Die Spalte Dateien -- Ich kann ein Programm in meinem Workspace speichern. → Die Spalte Dateien -::: - -## Wissenstest - -::: quiz -? Welche Datei kannst du in der Mac-App und in der Java-App wieder öffnen? -→ Speichern und Öffnen -- [x] Die .visustruct-Datei -- [ ] Das exportierte PNG-Bild -- [ ] Das exportierte PDF -> Bilder und PDFs sind nur zum Ansehen. Weiterbearbeiten kannst du nur die .visustruct-Datei. - -? Deine Übungen werden im Browser gemerkt. Warum solltest du trotzdem speichern? -→ Speichern und Öffnen -- [ ] Weil die Übungen sonst nicht geprüft werden -- [x] Weil der Browser die Daten nur auf diesem Gerät behält und sie verloren gehen können -- [ ] Weil man ohne Speichern nicht exportieren kann - -? Du speicherst heute ein Programm in deinem Workspace. Wo landet es? -→ Die Spalte Dateien -- [ ] Im Download-Ordner -- [x] Im Workspace in einem Ordner mit dem heutigen Datum -- [ ] Direkt im Workspace ohne Unterordner -> VisuStruct legt für jeden Tag automatisch einen Datumsordner an. So bleiben die Programme nach Stunden sortiert. -::: diff --git a/web/lessons/de/04-variablen-und-datentypen.md b/web/lessons/de/04-variablen-und-datentypen.md deleted file mode 100644 index c98f4a68..00000000 --- a/web/lessons/de/04-variablen-und-datentypen.md +++ /dev/null @@ -1,407 +0,0 @@ ---- -titel: Variablen und Datentypen -bereich: Grundlagen -kurz: Werte speichern, die Datentypen int, double, String und boolean kennen und rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable mit Datentyp, Namen und Wert anlegen -- erklären, dass Java Groß- und Kleinschreibung unterscheidet (`zahl` ist nicht `Zahl`) -- die Datentypen int, double, String und boolean unterscheiden -- erklären, dass = eine Zuweisung ist und kein Vergleich -- mit + - * / % rechnen und die ganzzahlige Division anwenden -- Variablen in Java anlegen und ausgeben -::: - -## Was ist eine Variable? - -Eine **Variable** ist wie eine beschriftete Kiste. Sie hat einen **Namen**, einen **Datentyp** und einen **Wert**. - -``` -int alter = 14 -``` - -Hier heißt die Variable `alter`, ihr Datentyp ist `int` (ganze Zahl), und ihr Wert ist 14. Über den Namen kannst du den Wert später wieder benutzen. - -::: grafik l04-variable.svg -Die Variable als Kiste: Name auf dem Etikett, Datentyp als Aufkleber, Wert darin. -::: - -> Variablennamen beginnen mit einem **Kleinbuchstaben** und enthalten keine Leerzeichen. Gute Namen sagen, was drinsteht: `preis` ist besser als `p`. - -::: angeberwissen -Im September 1999 ging die Mars-Sonde Mars Climate Orbiter verloren. Ein Team rechnete die Schubkraft in amerikanischen Einheiten (Pfund), das andere erwartete metrische Einheiten (Newton). In den Daten stand nur die Zahl, nicht die Einheit. Die Sonde flog zu tief in die Mars-Atmosphäre und verglühte, Schaden rund 125 Millionen Dollar. Ein sprechender Name wie `impulsInNewtonSekunden` hätte gesagt, was drinsteht. Ich schreibe auf mein Heufach übrigens immer `heuInKilo`. -::: - -Besteht ein Name aus mehreren Wörtern, schreibst du sie zusammen und beginnst jedes weitere Wort mit einem Großbuchstaben: `preisMitSteuer`, `anzahlSchueler`. Diese Schreibweise heißt **CamelCase**. - -::: angeberwissen -CamelCase heißt so, weil die Großbuchstaben in der Mitte wie die Höcker eines Kamels aus dem Wort ragen: `preisMitSteuer` hat zwei Höcker. Ich als Geißbock habe übrigens null Höcker, dafür zwei Hörner. Leider hat noch niemand die GeißbockSchreibweise erfunden. -::: - -::: grafik l04-camelcase.svg -Die Großbuchstaben M und S ragen heraus wie die zwei Höcker des Kamels. -::: - -## Groß- und Kleinschreibung - -Java unterscheidet **Groß- und Kleinschreibung**. Auf Englisch heißt das **case-sensitive**. Für Java sind `zahl`, `Zahl` und `ZAHL` drei verschiedene Namen, also drei verschiedene Kisten: - -``` -int zahl = 4 -int Zahl = 6 -``` - -Hier gibt es zwei Variablen. Schreibst du später `ZAHL`, findet Java keine Variable mit diesem Namen. Das Programm läuft nicht, und du bekommst eine Fehlermeldung. - -> Das gilt auch für Java-Wörter: `String` und `System` schreibt man groß, `int` und `double` klein. Lege in deinen eigenen Programmen nie zwei Variablen an, die sich nur in der Groß- und Kleinschreibung unterscheiden. Das verwirrt dich und alle, die deinen Code lesen. - -## Übung: Drei Namen - -::: übung l04-gross-klein -titel: Drei Namen -auftrag: Ludwig hat zwei Variablen angelegt: `zahl` mit dem Wert 4 und `Zahl` mit dem Wert 6. Das Struktogramm soll beide malnehmen und **Ergebnis: 24** ausgeben. Es läuft aber nicht. - Klicke auf „Im Editor öffnen“, dann auf Prüfen, und lies die Fehlermeldung. Repariere die Anweisung und klicke noch einmal auf Prüfen. -start: l04-gross-klein-start.visustruct -test: - -> Ergebnis: 24 -braucht: anweisung -tipp: Wie viele verschiedene Namen stehen im Struktogramm? `zahl`, `Zahl` und `ZAHL` sind für den Computer drei verschiedene Namen. -tipp: `ZAHL` gibt es nicht. Für 24 brauchst du 4 mal 6, also `zahl` mal `Zahl`: `int ergebnis = zahl * Zahl`. Mit `zahl * zahl` kommt 16 heraus. -lösung: l04-gross-klein-loesung.visustruct -::: - -## Die vier Datentypen - -Der Datentyp legt fest, welche Art von Wert in der Variablen stehen darf. - -- **int:** ganze Zahlen, z. B. `int anzahl = 3` oder `int temperatur = -5` -- **double:** Kommazahlen, z. B. `double preis = 2.5`. Statt Komma schreibst du einen **Punkt**. -- **String:** Text, z. B. `String name = "Mia"`. Text steht in Anführungszeichen. -- **boolean:** Wahrheitswerte, nur `true` (wahr) oder `false` (falsch) - -Es gibt noch mehr Datentypen. Einen wirst du bald brauchen: `char` für ein **einzelnes Zeichen**, z. B. `char note = 'C'`. Ein `char` steht in **einfachen** Anführungszeichen. Genauer lernst du ihn in Lektion 17 kennen. - -::: diagramm l05-datentypen.visustruct -Drei Variablen mit verschiedenen Datentypen. Öffne das Diagramm und starte die [Simulation](#zeige-simulation). -::: - -Wahrheitswerte begegnen dir vor allem in **Bedingungen**: `alter >= 18` ist entweder `true` oder `false`. Darum geht es in Lektion 8. Eine Variable vom Typ `boolean` bekommt einfach `true` oder `false`: - -``` -boolean volljaehrig = false -``` - -::: angeberwissen -Am 4. Juni 1996 explodierte die Rakete Ariane 5 schon 37 Sekunden nach dem Start. Schuld war eine einzige Zeile: Eine Kommazahl für die Geschwindigkeit wurde in eine Ganzzahl-Variable gesteckt, die für so große Werte zu klein war. Der Datentyp ist also keine Nebensache, er kann eine halbe Milliarde Euro kosten. -::: - -## Zuweisung mit = - -Das Zeichen `=` heißt **Zuweisung**. Es bedeutet nicht „ist gleich“ wie in Mathe, sondern: Berechne die rechte Seite und speichere das Ergebnis in der Variablen links. - -``` -int punkte = 10 -punkte = punkte + 5 -``` - -In Mathe wäre die zweite Zeile falsch, denn keine Zahl ist gleich sich selbst plus 5. Beim Programmieren liest du sie so: Nimm den alten Wert 10, rechne plus 5 und speichere 15 in `punkte`. Den Datentyp schreibst du nur beim ersten Mal, wenn die Variable angelegt wird. - -::: grafik l04-zuweisung.svg -Erst wird rechts gerechnet, dann landet das Ergebnis in der Variablen links. -::: - -## Rechnen - -Mit Zahlen kannst du rechnen: - -- `+` plus, `-` minus, `*` mal, `/` geteilt -- `%` Rest beim Teilen (gesprochen „Modulo“), z. B. ist `17 % 5` gleich 2 - -**Teilen mit Rest** kennst du aus der Grundschule, als du noch keine Kommazahlen hattest: 17 : 5 = 3 Rest 2. Die 5 passt dreimal in die 17, das sind 15. Bis 17 fehlen dann noch 2, das ist der Rest. Genau so rechnet der Computer mit ganzen Zahlen: `/` liefert das Ergebnis ohne Rest, `%` nur den Rest. - -| Grundschule | mit `/` | mit `%` | -|---|---|---| -| 17 : 5 = 3 Rest 2 | `17 / 5` ergibt 3 | `17 % 5` ergibt 2 | -| 20 : 5 = 4 Rest 0 | `20 / 5` ergibt 4 | `20 % 5` ergibt 0 | -| 3 : 5 = 0 Rest 3 | `3 / 5` ergibt 0 | `3 % 5` ergibt 3 | - -Ist der Rest 0, geht die Teilung auf. So prüfst du später, ob eine Zahl gerade ist: Bei geraden Zahlen ist `zahl % 2` gleich 0. - -Es gilt **Punkt vor Strich**, und Klammern werden zuerst ausgerechnet: `2 * (3 + 4)` ist 14. - -### Text und Ergebnis zusammen ausgeben - -Ein Ergebnis allein sagt wenig. Mit `+` hängst du einen Text und den Wert einer Variablen aneinander: - -``` -int alter = 14 -output: "Alter: " + alter -``` - -Das zeigt **Alter: 14**. Der Text in Anführungszeichen erscheint genau so, für `alter` setzt der Computer den Wert ein. Das Leerzeichen nach dem Doppelpunkt gehört zum Text, sonst steht dort `Alter:14`. - -## Übung: Rechteck - -::: übung l05-rechteck -titel: Rechteck berechnen -auftrag: Ein Rechteck ist 7 lang und 4 breit. Lege die Variablen `laenge` und `breite` an. Berechne die Fläche und den Umfang in eigenen Variablen und gib beide aus, so wie hier: -`Fläche: 28` und in der nächsten Zeile `Umfang: 22`. -test: - -> Fläche: 28 | Umfang: 22 -braucht: anweisung -tipp: Überlege zuerst, welche Variablen du brauchst: zwei für die Maße und zwei für die Ergebnisse. Alle sind ganze Zahlen. -tipp: Die Fläche ist Länge mal Breite, der Umfang ist 2 mal (Länge plus Breite). Für die Ausgabe schreibst du z. B. `output: "Fläche: " + flaeche`. Das `+` hängt dabei den Text und den Wert der Variablen aneinander. -lösung: l05-rechteck-loesung.visustruct -::: - -## Ganzzahlige Division - -Teilst du zwei ganze Zahlen und speicherst das Ergebnis in einer `int`-Variablen, fällt alles nach dem Komma weg. Es wird **nicht gerundet**. - -``` -int x = 7 / 2 ergibt 3 -int r = 7 % 2 ergibt 1 (der Rest) -double d = 7.0 / 2 ergibt 3.5 -``` - -Mit `/` und `%` zusammen kannst du gut umrechnen, zum Beispiel Minuten in Stunden und Minuten: - -::: diagramm l05-division.visustruct -135 Minuten sind 2 Stunden (135 / 60) und 15 Minuten Rest (135 % 60). -::: - -## Übung: Sekunden umrechnen - -::: übung l05-sekunden -titel: Sekunden umrechnen -auftrag: 200 Sekunden sollen als Minuten und Sekunden ausgegeben werden. Die erste und die letzte Anweisung sind schon da. Füge dazwischen Anweisungen ein, die `minuten` und `rest` berechnen. -start: l05-sekunden-start.visustruct -test: - -> 3 Minuten und 20 Sekunden -braucht: anweisung -tipp: Wie viele ganze Minuten passen in 200 Sekunden, und wie viele Sekunden bleiben übrig? Rechne es erst im Kopf aus. -tipp: Eine Minute hat 60 Sekunden. Nutze `/` für die ganzen Minuten und `%` für den Rest. Beide Variablen sind vom Typ `int`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Übung: Einkauf - -::: übung l05-einkauf -titel: Einkauf -auftrag: Du kaufst 3 Hefte zu je 2.5 Euro. Lege `anzahl` als `int` und `preis` als `double` an, berechne den Gesamtpreis in einer Variablen `gesamtPreis` und gib `Gesamt: 7.5 Euro` aus. -test: - -> Gesamt: 7.5 Euro -braucht: anweisung -tipp: Anzahl mal Preis ergibt den Gesamtpreis. Überlege für jede Variable, ob sie eine ganze Zahl oder eine Kommazahl ist. -tipp: Der Gesamtpreis ist eine Kommazahl, also braucht `gesamtPreis` den Typ `double`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Jetzt in Java - -Ein Struktogramm ist ein Plan. In **Java** wird daraus ein Programm, das der Computer ausführt. Dein Einkauf aus der Übung sieht so aus: - -```java editor -int anzahl = 3; -double preis = 2.5; -double gesamtPreis = anzahl * preis; -System.out.println("Gesamt: " + gesamtPreis + " Euro"); -``` - -- Erst der Datentyp, dann der Name, dann der Wert, genau wie im Kasten. -- Jede Anweisung endet mit einem **Semikolon** `;`. -- Eine Ausgabe heißt in Java `System.out.println(…)`. - -Wie der Computer deinen Java-Code versteht und warum er auf Windows, macOS und Linux gleich läuft, erfährst du in der Lektion „Struktogramm und Code“. - -::: angeberwissen -Java hieß zuerst **Oak** (Eiche), nach dem Baum vor dem Bürofenster von James Gosling. Sein Team bei Sun Microsystems wollte ab 1991 kleine Geräte wie Fernseh-Boxen programmieren. Den Namen Oak hatte aber schon eine andere Firma. Also wurde es 1995 **Java**, wie der Kaffee von der Insel Java. -::: - -## Text in Java - -Variablen speichern nicht nur Zahlen. Für Text gibt es den Datentyp `String`: - -```java editor -String name = "Mia"; -int alter = 14; -double groesse = 1.62; -System.out.println(name + " ist " + alter + " Jahre alt."); -System.out.println("Größe: " + groesse + " m"); -``` - -- Text steht in Anführungszeichen. -- `String` schreibst du groß, `int`, `double` und `boolean` klein. Java unterscheidet das. -- Mit `+` hängst du Text und Zahlen aneinander. - -Java tippst du im Bereich [Code](#zeige-codelive). Das Struktogramm entsteht dabei von selbst. „Im Editor öffnen“ holt das Beispiel in den Bereich Code. Klicke dort auf „Ausführen“, ändere dann Name, Alter oder Größe und probiere es noch einmal. - -::: übung l04-java-steckbrief -titel: Steckbrief in Java -bereich: beide -code: java -auftrag: Schreibe einen Steckbrief für Ludwig den Geißbock. Lege drei Variablen an: den Namen **Ludwig**, das Alter **7** und die Größe **1.2**. Gib dann diese zwei Zeilen aus: - **Ludwig ist 7 Jahre alt.** und **Größe: 1.2 m** - „Im Editor öffnen“ öffnet rechts auch den Bereich Code. Tippe dort deinen Code, das Struktogramm entsteht dabei von selbst. Klicke dann auf Prüfen. -test: - -> Ludwig ist 7 Jahre alt. | Größe: 1.2 m -braucht: anweisung -tipp: Du brauchst einen `String`, einen `int` und einen `double`. Jede Zeile endet mit einem Semikolon. -tipp: `String name = "Ludwig";`, `int alter = 7;` und `double groesse = 1.2;`. Die Ausgabe setzt du mit `+` zusammen: `System.out.println(name + " ist " + alter + " Jahre alt.");` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Lückentext: Datentypen - -::: lückentext -titel: Steckbrief in Java -auftrag: Ergänze die fehlenden Wörter. Ganz außen steht der Rahmen, den jedes Java-Programm in einer eigenen Datei braucht. Darin stehen die Variablen. -```java -[[public]] class Steckbrief { - public static [[void]] main(String[] args) { - [[int]] alter = 14; - [[double]] groesse = 1.62; - [[String]] name = "Mia"; - [[boolean]] hatHaustier = true; - System.out.[[println]](name + " ist " + alter + " Jahre alt."); - } -} -``` -erklärung: Ganze Zahlen sind `int`, Kommazahlen `double`, Texte `String` (groß geschrieben) und Wahrheitswerte `boolean`. `public class` und `public static void main` bilden den Rahmen. Im Bereich Code darfst du ihn weglassen, mehr dazu in der Lektion „Struktogramm und Code“. -::: - -## Fehler finden: Sekunden - -::: übung l04-java-fehler -titel: Sekunden reparieren -code: java -auftrag: Das Programm soll 200 Sekunden in Minuten und Sekunden umrechnen und **3 min 20 s** ausgeben. Es gibt aber **20 min 3 s** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: `/` teilt ganzzahlig, `%` liefert den Rest. Welches Zeichen brauchst du für die Minuten, welches für die übrigen Sekunden? -tipp: Die beiden Zeichen sind vertauscht: `int minuten = sekunden / 60;` und `int rest = sekunden % 60;` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l04-java-laeuft-nicht -titel: Steckbrief reparieren -code: java -auftrag: Das Programm soll **Ludwig ist 7 Jahre alt.** ausgeben. Es läuft aber nicht, denn zwei Wörter sind falsch geschrieben. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig ist 7 Jahre alt. -braucht: anweisung -tipp: Java unterscheidet Groß- und Kleinschreibung. Vergleiche jedes Wort in der Ausgabe genau mit dem, was oben angelegt ist. -tipp: `System` schreibt man mit großem S. Die Variable heißt `alter` mit kleinem a: `System.out.println(name + " ist " + alter + " Jahre alt.");` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Datentyp? - -::: spiel frage -titel: Spiel: Welcher Datentyp? -intro: Ludwig hat Werte gesammelt. Welcher Datentyp passt zu jedem Wert? Acht Runden. -antworten: int | double | String | boolean - -runde: -frage: Welcher Datentyp passt zu `17`? -richtig: int -> Eine ganze Zahl ohne Komma ist ein `int`. - -runde: -frage: Welcher Datentyp passt zu `3.5`? -richtig: double -> Kommazahlen sind `double`. In Java steht statt des Kommas ein Punkt. - -runde: -frage: Welcher Datentyp passt zu `"Hallo"`? -richtig: String -> Text in Anführungszeichen ist ein `String`. - -runde: -frage: Welcher Datentyp passt zu `true`? -richtig: boolean -> `true` und `false` sind die beiden Wahrheitswerte, Datentyp `boolean`. - -runde: -frage: Welcher Datentyp passt zu `"17"`? -richtig: String -> Vorsicht, Anführungszeichen! Hier ist 17 ein Text, mit dem man nicht rechnen kann. - -runde: -frage: Welcher Datentyp passt zu `-4`? -richtig: int -> Auch negative ganze Zahlen sind `int`. - -runde: -frage: Welcher Datentyp passt zu `0.0`? -richtig: double -> Der Punkt macht es zur Kommazahl, auch wenn hinten nur eine 0 steht. - -runde: -frage: Welcher Datentyp passt zu `"false"`? -richtig: String -> In Anführungszeichen ist auch false nur ein Text. Der Wahrheitswert steht ohne Anführungszeichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable mit Datentyp, Namen und Wert anlegen. → Was ist eine Variable? -- Ich kann erklären, dass Java Groß- und Kleinschreibung unterscheidet (`zahl` ist nicht `Zahl`). → Groß- und Kleinschreibung -- Ich kann die Datentypen int, double, String und boolean unterscheiden. → Die vier Datentypen -- Ich kann erklären, dass = eine Zuweisung ist und kein Vergleich. → Zuweisung mit = -- Ich kann mit + - * / % rechnen und die ganzzahlige Division anwenden. → Rechnen -- Ich kann Variablen in Java anlegen und ausgeben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welcher Datentyp passt zu dem Wert 3.75? -→ Die vier Datentypen -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Kommazahlen haben den Typ double. In VisuStruct schreibst du sie mit Punkt. - -? Wie viele Variablen legen `int zahl = 1`, `int Zahl = 2` und `int ZAHL = 3` an? -→ Groß- und Kleinschreibung -- [ ] eine -- [ ] zwei -- [x] drei -> Java unterscheidet Groß- und Kleinschreibung. Die drei Namen sind verschieden, also gibt es drei Variablen. - -? Welchen Wert hat x nach `int x = 17 / 5`? -→ Rechnen -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> Bei ganzen Zahlen fällt alles nach dem Komma weg. Es wird nicht gerundet. - -? Was ergibt `17 % 5`? -→ Rechnen -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 geteilt durch 5 ist 3, Rest 2. Der Operator % liefert den Rest. - -? Am Anfang ist `int punkte = 10`. Was steht nach `punkte = punkte * 2` in punkte? -→ Zuweisung mit = -- [ ] 10 -- [ ] 12 -- [x] 20 -> Rechts wird mit dem alten Wert 10 gerechnet, das Ergebnis 20 wird in punkte gespeichert. - -? Welche Zeile legt in Java richtig eine Kommazahl an? -→ Jetzt in Java -- [ ] double preis = 2,5; -- [x] double preis = 2.5; -- [ ] Double preis = 2.5 -> Kommazahlen schreibst du mit Punkt, und am Ende steht ein Semikolon. -::: diff --git a/web/lessons/de/05-sequenz.md b/web/lessons/de/05-sequenz.md deleted file mode 100644 index be834a66..00000000 --- a/web/lessons/de/05-sequenz.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -titel: Sequenz -bereich: Grundlagen -kurz: Schritte nacheinander ausführen, auf die Reihenfolge achten und Werte tauschen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass eine Sequenz von oben nach unten ausgeführt wird -- erklären, warum die Reihenfolge der Schritte wichtig ist -- zwei Werte mit einer Hilfsvariablen tauschen -- eine Sequenz in Java schreiben -::: - -## Schritt für Schritt - -Eine **Sequenz** ist eine Folge von Schritten, die genau einmal und genau in dieser Reihenfolge ausgeführt werden. Im Struktogramm stehen die Kästen einfach untereinander. - -Jeder Kasten wird erst begonnen, wenn der Kasten darüber fertig ist. Es wird nichts übersprungen und nichts wiederholt. - -::: diagramm l05-sequenz.visustruct -Eine Sequenz aus vier Kästen: Zuerst bekommen `laenge` und `breite` ihre Werte, dann wird die Fläche berechnet, und zum Schluss wird sie ausgegeben. Jeder Kasten läuft genau einmal, von oben nach unten. Ausgabe: 15. -::: - -> Die Sequenz ist der einfachste der drei Grundbausteine. Fast jedes Struktogramm besteht zum größten Teil aus Sequenzen. - -## Die Reihenfolge zählt - -Beim Tee kochen merkst du sofort, wenn die Reihenfolge falsch ist. Bei Rechnungen ist es genauso, nur fällt es weniger auf. - -::: diagramm l04-rechnen.visustruct -Erst verdoppeln, dann 1 addieren: Das Ergebnis ist 7. Tauschst du die beiden mittleren Kästen, kommt 8 heraus. -::: - -Die Anweisung `zahl = zahl * 2` bedeutet: Nimm den Wert von `zahl`, rechne mal 2 und speichere das Ergebnis wieder in `zahl`. Was in `zahl` steht, hängt also davon ab, was vorher passiert ist. - -::: angeberwissen -Struktogramme sind kein reiner Schulkram. Laut Shneiderman haben auch Firmen wie IBM und Siemens damit ihre Programme geplant. Du lernst hier also Profi-Zeug. Määäh-ga! -::: - -## Übung: Reihenfolge ordnen - -::: übung l04-reihenfolge -titel: Reihenfolge ordnen -auftrag: Im Startdiagramm stehen die richtigen Kästen, aber in der falschen Reihenfolge. Verschiebe sie so, dass am Ende **20** ausgegeben wird. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Lies jeden Kasten und frage dich: Welche Variable braucht er, und wo bekommt diese Variable ihren Wert? -tipp: Eine Variable muss zuerst angelegt werden, bevor du mit ihr rechnest. Die Ausgabe kommt ganz zum Schluss. Rechne im Kopf nach: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Zwei Werte tauschen - -Stell dir zwei Gläser vor: eins mit Saft, eins mit Wasser. Willst du die Inhalte tauschen, brauchst du ein drittes, leeres Glas. - -Mit Variablen ist es genauso. Schreibst du einfach `a = b` und danach `b = a`, ist der alte Wert von `a` schon verloren. Am Ende haben beide Variablen denselben Wert. - -Die Lösung ist eine **Hilfsvariable**, die den alten Wert kurz aufbewahrt: - -::: grafik l05-tauschen.svg -Drei Schritte mit einem leeren Glas: erst Saft in hilf, dann Wasser nach links, dann den Saft aus hilf nach rechts. -::: - -::: diagramm l04-tauschen.visustruct -Zuerst wird a in hilf gemerkt. Dann bekommt a den Wert von b, und b bekommt den gemerkten Wert aus hilf. -::: - -## Übung: Gläser tauschen - -::: übung l04-glaeser-tauschen -titel: Gläser tauschen -auftrag: Im linken Glas ist Saft, im rechten Wasser. Tausche die Inhalte der Variablen `links` und `rechts`, bevor sie ausgegeben werden. -Ändere dazu nicht die ersten beiden Kästen, sondern füge dazwischen neue Anweisungen ein. -start: l04-tauschen-start.visustruct -test: - -> Links: Wasser | Rechts: Saft -braucht: anweisung -tipp: Wie tauschst du zwei volle Gläser im echten Leben? Du brauchst ein drittes, leeres Glas. -tipp: Lege mit `String hilf = links` eine Hilfsvariable an. Danach brauchst du noch zwei Zuweisungen. -lösung: l04-tauschen-loesung.visustruct -::: - -## Jetzt in Java - -Wie in Lektion 4 kannst du auch eine Sequenz direkt in Java schreiben. Das Tauschen von oben sieht so aus: - -```java editor -int a = 5; -int b = 9; -int hilf = a; -a = b; -b = hilf; -System.out.println("a = " + a); -System.out.println("b = " + b); -``` - -- Jeder Kasten wird zu einer Zeile. Die Zeilen laufen von oben nach unten, genau wie die Kästen. -- Am Ende jeder Anweisung steht ein **Semikolon** `;`. -- Eine Ausgabe heißt in Java `System.out.println(…)`. Achte auf das große S bei `System`, Java unterscheidet Groß- und Kleinbuchstaben. - -Java tippst du im Bereich [Code](#zeige-codelive) rechts neben dem Struktogramm. Während du tippst, baut VisuStruct das Struktogramm mit. So siehst du sofort, welcher Kasten zu welcher Zeile gehört. - -::: übung l05-java-kino -titel: Kinokarten in Java -bereich: beide -code: java -auftrag: Eine Kinokarte kostet 9 Euro. Schreibe ein Java-Programm, das den Preis für 3 Karten ausrechnet und **Gesamt: 27** ausgibt. - „Im Editor öffnen“ öffnet rechts auch den Bereich Code. Tippe dort deinen Code, das Struktogramm entsteht dabei von selbst. Klicke dann auf Prüfen. -test: - -> Gesamt: 27 -braucht: anweisung -tipp: Du brauchst drei Variablen: den Preis, die Anzahl und das Ergebnis. Jede Zeile endet mit einem Semikolon. -tipp: `int preis = 9;` und `int anzahl = 3;` legen die Variablen an. `int gesamt = preis * anzahl;` rechnet, und `System.out.println("Gesamt: " + gesamt);` gibt das Ergebnis aus. -lösung: l05-java-kino-loesung.visustruct -::: - -## Lückentext: Gläser tauschen - -::: lückentext -titel: Zwei Werte tauschen -auftrag: Die Werte von `a` und `b` sollen getauscht werden. Ergänze die fehlenden Wörter. -```java -int a = 3; -int b = 8; -[[int]] hilf = [[a]]; -a = [[b]]; -b = [[hilf]]; -System.out.println("a = " + a + ", b = " + b); -``` -erklärung: Erst rettest du `a` in die Hilfsvariable, dann bekommt `a` den Wert von `b`, und zuletzt bekommt `b` den geretteten Wert. Ausgabe: **a = 8, b = 3** -::: - -## Fehler finden: Guthaben - -::: übung l05-java-fehler -titel: Guthaben reparieren -code: java -auftrag: Auf der Mensakarte sind 20 Euro. Ein Essen kostet 7 Euro. Das Programm soll das Guthaben **nach** dem Bezahlen ausgeben, also **Übrig: 13**. Es gibt aber **Übrig: 20** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l05-java-fehler-start.visustruct -test: - -> Übrig: 13 -braucht: anweisung -tipp: Alle Zeilen stimmen, nur die Reihenfolge nicht. Wann wird das Guthaben ausgegeben, wann wird bezahlt? -tipp: Tausche die letzten beiden Zeilen: erst `guthaben = guthaben - preis;`, dann die Ausgabe. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l05-java-laeuft-nicht -titel: Pizza teilen -code: java -auftrag: Eine Pizza hat 8 Stücke, 4 Freunde teilen sie. Das Programm soll **Jeder bekommt 2 Stücke.** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Jeder bekommt 2 Stücke. -braucht: anweisung -tipp: Vergleiche den Namen in der Ausgabe Buchstabe für Buchstabe mit dem Namen, der in der dritten Zeile angelegt wird. -tipp: Die Variable heißt `proPerson` mit großem P (CamelCase). Schreibe sie in der Ausgabe genauso. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Kästen sortieren - -::: spiel reihenfolge -titel: Spiel: Kästen sortieren -intro: Ludwig hat die Kästen durcheinandergeworfen. Bring sie in die richtige Reihenfolge. Vier Runden. - -runde: -frage: Am Ende soll **7** ausgegeben werden. -- `int zahl = 3` -- `zahl = zahl * 2` -- `zahl = zahl + 1` -- `output: zahl` -> Erst verdoppeln, dann 1 dazu: 3 · 2 + 1 = 7. Andersherum käme (3 + 1) · 2 = 8 heraus. - -runde: -frage: Am Ende soll **8** ausgegeben werden. -- `int zahl = 3` -- `zahl = zahl + 1` -- `zahl = zahl * 2` -- `output: zahl` -> Dieselben Kästen, andere Reihenfolge, anderes Ergebnis: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a und b sollen ihre Werte tauschen. -- `int hilf = a` -- `a = b` -- `b = hilf` -- `output: a + " " + b` -> Zuerst wird a in hilf gerettet. Sonst ist der alte Wert weg, sobald a den Wert von b bekommt. - -runde: -frage: Ludwig kocht Tee. -- Wasser in den Wasserkocher füllen -- Wasserkocher einschalten -- Warten, bis das Wasser kocht -- Wasser über den Teebeutel gießen -- Tee ziehen lassen -> Jeder Schritt braucht den vorherigen. Genau wie im Struktogramm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass eine Sequenz von oben nach unten ausgeführt wird. → Schritt für Schritt -- Ich kann erklären, warum die Reihenfolge der Schritte wichtig ist. → Die Reihenfolge zählt -- Ich kann zwei Werte mit einer Hilfsvariablen tauschen. → Zwei Werte tauschen -- Ich kann eine Sequenz in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie oft wird jeder Kasten einer Sequenz ausgeführt? -→ Schritt für Schritt -- [ ] So oft, bis eine Bedingung falsch ist -- [x] Genau einmal -- [ ] Gar nicht, wenn er leer ist -> In einer Sequenz wird nichts wiederholt und nichts übersprungen. Für Wiederholungen gibt es Schleifen. - -? Am Anfang ist a = 5 und b = 9. Danach wird `a = b` und dann `b = a` ausgeführt. Was steht am Ende in den Variablen? -→ Zwei Werte tauschen -- [ ] a = 9 und b = 5 -- [x] a = 9 und b = 9 -- [ ] a = 5 und b = 5 -> Nach `a = b` ist der alte Wert 5 verloren. `b = a` kopiert dann nur die 9 zurück. - -? Wozu brauchst du beim Tauschen eine Hilfsvariable? -→ Zwei Werte tauschen -- [x] Damit ein Wert nicht überschrieben wird, bevor er gebraucht wird -- [ ] Damit das Struktogramm länger wird -- [ ] Weil man Variablen sonst nicht ausgeben kann - -? Was gibt die Sequenz `int x = 4`, `x = x + 1`, `x = x * 3`, `output: x` aus? -→ Die Reihenfolge zählt -- [ ] 13 -- [x] 15 -- [ ] 4 -> Erst wird x zu 5, dann zu 15. - -? Womit endet in Java jede Anweisung? -→ Jetzt in Java -- [ ] Mit einem Punkt . -- [x] Mit einem Semikolon ; -- [ ] Mit einem Doppelpunkt : -> Fehlt das Semikolon, zeigt VisuStruct unter dem Code einen Hinweis. -::: diff --git a/web/lessons/de/06-eingabe-und-ausgabe.md b/web/lessons/de/06-eingabe-und-ausgabe.md deleted file mode 100644 index 9858d258..00000000 --- a/web/lessons/de/06-eingabe-und-ausgabe.md +++ /dev/null @@ -1,432 +0,0 @@ ---- -titel: Eingabe und Ausgabe -bereich: Grundlagen -kurz: Werte abfragen, Ergebnisse anzeigen und das EVA-Prinzip anwenden. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- das EVA-Prinzip erklären -- eine Eingabe mit Datentyp, Namen und Frage schreiben -- Text und Variablen in einer Ausgabe mit + verbinden -- ein Ergebnis zuerst ausrechnen und in einer Variablen speichern, dann diese Variable ausgeben, z. B. `int summe = a + b`, danach `output: summe` -- aus einem Aufgabentext Eingabe, Verarbeitung und Ausgabe herauslesen -- in Java mit dem Scanner Zahlen einlesen -- Kommentare mit `//` und `/* */` schreiben und damit meinen Code einteilen und erklären -::: - -## Das EVA-Prinzip - -Viele Programme arbeiten in drei Teilen: - -1. **Eingabe:** Das Programm fragt Werte ab, z. B. zwei Zahlen. -2. **Verarbeitung:** Es rechnet mit den Werten. -3. **Ausgabe:** Es zeigt das Ergebnis an. - -Die Anfangsbuchstaben ergeben **EVA**. Bisher standen die Werte fest im Struktogramm. Mit einer Eingabe kann bei jedem Durchlauf ein anderer Wert verwendet werden. - -::: diagramm l06-eva.visustruct -Oben die Eingabe, in der Mitte die Verarbeitung, unten die Ausgabe. Starte die Simulation und probiere verschiedene Zahlen aus. -::: - -## Eingabe mit input: - -Eine Eingabe fragt beim Ausführen einen Wert ab und speichert ihn in einer Variablen. Du schreibst den **Datentyp**, den **Namen** und eine **Frage** in Anführungszeichen: - -``` -input: int alter "Wie alt bist du?" -input: double preis "Preis in Euro?" -input: String name "Wie heißt du?" -``` - -Im Editor wählst du bei einer Anweisung oben die Art **Eingabe**. Das Wort `input:` steht dann schon davor, und du tippst nur noch `int alter "Wie alt bist du?"`. - -> Der Datentyp entscheidet, was eingegeben werden darf. Bei `int` meldet die Simulation einen Fehler, wenn jemand 2.5 oder Hallo eintippt. - -## Ausgabe mit output: - -Eine Ausgabe zeigt Text und Werte an. Text steht in Anführungszeichen, Variablen ohne. Mit `+` hängst du die Teile **aneinander**: - -``` -output: "Hallo " + name + "!" -output: "Du bist " + alter + " Jahre alt." -``` - -Achte auf die Leerzeichen innerhalb der Anführungszeichen. Ohne sie klebt alles zusammen, und du siehst `HalloMia!` statt `Hallo Mia!`. - -## Übung: Begrüßung - -::: übung l06-begruessung -titel: Begrüßung -auftrag: Frage nach dem Namen und begrüße die Person. Bei der Eingabe Ada soll **Hallo Ada!** ausgegeben werden. -test: Ada -> Hallo Ada! -test: Ben -> Hallo Ben! -braucht: anweisung -tipp: Zuerst kommt eine Eingabe für den Namen, danach eine Ausgabe, die den Namen benutzt. -tipp: Der Name ist Text, die Eingabe braucht also den Typ `String`. In der Ausgabe verbindest du `"Hallo "`, die Variable und `"!"` mit `+`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Vorsicht mit + in der Ausgabe - -In einer Ausgabe hängt `+` die Teile **immer** aneinander, auch bei Zahlen. Sind `a` gleich 3 und `b` gleich 4, dann zeigt - -``` -output: a + b -``` - -nicht 7, sondern **34**. Rechne deshalb vorher in einer Variablen und gib dann die Variable aus: - -``` -int summe = a + b -output: "Summe: " + summe -``` - -Das passt auch zum EVA-Prinzip: Gerechnet wird in der Verarbeitung, nicht in der Ausgabe. - -## Vom Aufgabentext zum Struktogramm - -Bevor du den ersten Kasten ziehst, liest du die Aufgabe genau und zerlegst sie. Das nennt man **Problemanalyse**. Drei Fragen helfen dir dabei: - -1. **Was ist gegeben?** Diese Werte werden zur **Eingabe**. Überlege auch den Datentyp. -2. **Was ist gesucht?** Das wird zur **Ausgabe**. -3. **Wie kommt man vom Gegebenen zum Gesuchten?** Das ist die **Verarbeitung**, oft eine Formel. - -Ein Beispiel: *Eine Kinokarte kostet 9 €. Das Programm fragt, wie viele Karten jemand kauft, und zeigt den Gesamtpreis an.* - -- Gegeben: die Anzahl der Karten → `input: int anzahl "Wie viele Karten?"` -- Gesucht: der Gesamtpreis → `output: "Gesamtpreis: " + gesamt` -- Weg: Anzahl mal 9 → `int gesamt = anzahl * 9` - -Jetzt musst du die drei Teile nur noch in der Reihenfolge E, V, A untereinander setzen. - -> Achte im Aufgabentext auf Signalwörter: *fragt*, *liest ein* oder *gibt ein* deuten auf eine Eingabe. *Zeigt*, *gibt aus* oder *meldet* deuten auf eine Ausgabe. - -## Übung: Rechner - -::: übung l06-rechner -titel: Summe und Produkt -auftrag: Frage zwei ganze Zahlen ab. Gib danach ihre Summe und ihr Produkt aus, jeweils in einer eigenen Zeile. -Bei 3 und 4 sieht das so aus: `Summe: 7` und `Produkt: 12`. -test: 3, 4 -> Summe: 7 | Produkt: 12 -test: 10, -2 -> Summe: 8 | Produkt: -20 -test: 0, 5 -> Summe: 5 | Produkt: 0 -braucht: anweisung -tipp: Plane die Reihenfolge: erst beide Zahlen einlesen, dann rechnen, dann ausgeben. -tipp: Du brauchst zwei Eingaben, zwei Anweisungen zum Rechnen und zwei Ausgaben. Rechne die Summe in einer eigenen Variablen, sonst hängt `+` die Zahlen aneinander. -lösung: l06-rechner-loesung.visustruct -::: - -## Übung: Mittelwert - -::: übung l06-mittelwert -titel: Mittelwert -auftrag: Frage zwei Zahlen ab, die auch Kommazahlen sein dürfen. Gib ihren Mittelwert aus, bei 2 und 3 also `Mittelwert: 2.5`. -test: 2, 3 -> Mittelwert: 2.5 -test: 4, 6 -> Mittelwert: 5.0 -test: 1.5, 2 -> Mittelwert: 1.75 -braucht: anweisung -tipp: Den Mittelwert zweier Zahlen bekommst du, wenn du sie addierst und das Ergebnis halbierst. -tipp: Für Kommazahlen nimmst du den Typ `double`. Der Mittelwert ist die Summe geteilt durch 2. Denk an die Klammern: `(x + y) / 2`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Jetzt in Java - -So sieht das EVA-Beispiel vom Anfang in Java aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Erste Zahl?"); -int a = scanner.nextInt(); -System.out.println("Zweite Zahl?"); -int b = scanner.nextInt(); -int summe = a + b; -System.out.println("Summe: " + summe); -``` - -- Für Eingaben braucht Java einen **Scanner**. Die ersten beiden Zeilen schreibst du einmal ganz oben, dann kannst du beliebig oft einlesen. -- `scanner.nextInt()` liest eine ganze Zahl, `scanner.nextDouble()` eine Kommazahl und `scanner.next()` ein Wort. -- Die Frage an den Benutzer ist eine normale Ausgabe direkt davor. VisuStruct macht aus beiden Zeilen zusammen einen Eingabe-Kasten. - -::: angeberwissen -Java sollte ursprünglich kleine Geräte steuern, und das tut es heute noch: Auf vielen SIM-Karten im Handy und auf Bankkarten läuft eine Mini-Version namens **Java Card**. Gut möglich, dass du gerade Java in der Hosentasche hast. -::: - -::: übung l06-java-tage -titel: Alter in Tagen -code: java -auftrag: Schreibe ein Java-Programm, das nach dem Alter in Jahren fragt und ausgibt, wie viele Tage das ungefähr sind. Rechne mit 365 Tagen pro Jahr. Bei 2 Jahren erscheint **Das sind etwa 730 Tage.** -test: 2 -> Das sind etwa 730 Tage. -test: 10 -> Das sind etwa 3650 Tage. -test: 1 -> Das sind etwa 365 Tage. -braucht: anweisung -tipp: Erst die beiden Scanner-Zeilen, dann Frage und Eingabe, dann rechnen, dann ausgeben. -tipp: `int jahre = scanner.nextInt();` liest ein. `int tage = jahre * 365;` rechnet, und `System.out.println("Das sind etwa " + tage + " Tage.");` gibt aus. -lösung: l06-java-tage-loesung.visustruct -::: - -## Kommentare im Code - -Im Code darfst du dir Notizen machen. Sie heißen **Kommentare** (englisch *comments*). Java überspringt sie beim Ausführen, das Programm und das Struktogramm bleiben also gleich. Es gibt zwei Arten: - -- `//` macht den Rest der Zeile zum Kommentar. Das ist ein **einzeiliger Kommentar**. -- `/*` beginnt einen **mehrzeiligen Kommentar**, `*/` beendet ihn. Alles dazwischen gehört zum Kommentar, egal wie viele Zeilen. Die Sternchen am Anfang der Zeilen dazwischen braucht Java nicht, sie sehen nur ordentlich aus. - -In fertigen Programmen siehst du manchmal auch `/** … */` mit zwei Sternchen am Anfang. Das ist ein **Javadoc-Kommentar**, aus dem Java eine Beschreibung des Programms erzeugen kann. Für dich reichen `//` und `/* */`. - -So sieht das EVA-Beispiel mit Kommentaren aus: - -```java editor -/* - * Aufgabe: Das Programm fragt nach zwei Zahlen - * und gibt ihre Summe aus. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Eingabe -System.out.println("Erste Zahl?"); -int a = scanner.nextInt(); -System.out.println("Zweite Zahl?"); -int b = scanner.nextInt(); - -// Verarbeitung -int summe = a + b; // die beiden Zahlen addieren - -// Ausgabe -System.out.println("Summe: " + summe); // bei 3 und 4 erwarte ich: Summe: 7 - -// Nächstes Mal: auch Kommazahlen erlauben -``` - -Wofür Kommentare gut sind: - -- **Aufgabenstellung:** Oben im mehrzeiligen Kommentar steht, was das Programm können soll. -- **Einteilung:** `// Eingabe`, `// Verarbeitung` und `// Ausgabe` teilen den Code nach dem EVA-Prinzip ein. -- **Erklärung:** Ein Kommentar hinter einer Zeile sagt, was dein Algorithmus dort macht. -- **Erwartung:** Schreib auf, welche Ausgabe du erwartest. Beim Testen vergleichst du dann. -- **Klammern zuordnen:** Später kommen Blöcke in geschweiften Klammern `{ }` dazu. Dann zeigt z. B. `} // Ende for`, welche Klammer was schließt. -- **Gedanken festhalten:** Woran musst du denken, wenn du nächstes Mal weiterarbeitest? - -Klicke auf **Im Editor öffnen**, ändere einen Kommentar oder schreib einen neuen dazu. Das Struktogramm bleibt gleich, und VisuStruct behält deine Kommentare. - -> Schreib nicht hinter jede Zeile einen Kommentar. Erkläre, was dein Algorithmus macht und warum, nicht jedes einzelne Zeichen. - -::: angeberwissen -Beide Arten sind viel älter als Java: `/* … */` stammt aus der Sprache PL/I von 1964, `//` aus BCPL von 1967. Java hat sie über C und C++ geerbt. -::: - -## Lückentext: Kommentare - -::: lückentext -titel: Kommentare ergänzen -auftrag: Ergänze die Zeichen, damit oben ein mehrzeiliger Kommentar mit der Aufgabe steht und die anderen beiden Notizen einzeilige Kommentare sind. -```java -[[/*]] - * Aufgabe: Das Programm rechnet Euro in Cent um. - [[*/]] -int euro = 3; -[[//]] Verarbeitung -int cent = euro * 100; -[[//]] Ausgabe, erwartet: 300 -System.out.println(cent); -``` -erklärung: `/*` öffnet den mehrzeiligen Kommentar und `*/` schließt ihn. `//` gilt nur bis zum Ende der Zeile, darum braucht jede Notiz ihr eigenes `//`. -::: - -## Lückentext: Eingabe - -::: lückentext -titel: Name und Alter einlesen -auftrag: Ergänze die fehlenden Wörter, damit das Programm einen Namen und ein Alter einliest. -```java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("Wie heißt du?"); -[[String]] name = scanner.[[next]](); -System.out.println("Wie alt bist du?"); -int alter = scanner.[[nextInt]](); -System.out.println("Hallo " + name + ", nächstes Jahr bist du " + [[(alter + 1)]] + "."); -``` -erklärung: `scanner.next()` liest ein Wort, `scanner.nextInt()` eine ganze Zahl. Die Klammern um `alter + 1` sind wichtig: Ohne sie hängt Java erst `alter` und dann `1` an den Text an, aus 14 würde **141**. -::: - -## Fehler finden: Summe - -::: übung l06-java-fehler -titel: Summe reparieren -code: java -auftrag: Das Programm liest zwei Zahlen ein und soll ihre Summe ausgeben. Bei 3 und 4 erscheint aber **Summe: 34** statt **Summe: 7**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Summe: 7 -test: 10, 5 -> Summe: 15 -test: 0, 9 -> Summe: 9 -braucht: anweisung -tipp: Lies die Ausgabe von links nach rechts: Was ergibt `"Summe: " + a`? Und was passiert, wenn danach noch `+ b` kommt? -tipp: Setze Klammern, damit Java zuerst rechnet: `System.out.println("Summe: " + (a + b));` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l06-java-laeuft-nicht -titel: Alter im nächsten Jahr -code: java -auftrag: Das Programm fragt nach deinem Alter und soll sagen, wie alt du nächstes Jahr bist. Bei 14 also **Nächstes Jahr bist du 15**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> Nächstes Jahr bist du 15 -test: 9 -> Nächstes Jahr bist du 10 -braucht: anweisung -tipp: Die Eingabe landet in einer Variablen. Wie genau heißt sie, und wie heißt sie in der Ausgabe? -tipp: Die Variable heißt `alter` mit kleinem a: `System.out.println("Nächstes Jahr bist du " + (alter + 1));` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Was wird ausgegeben? - -::: spiel frage -titel: Spiel: Was wird ausgegeben? -intro: Ludwig hat Ein- und Ausgaben gebaut. Sag vorher, was passiert. Sieben Runden. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: Was wird ausgegeben? -- 7 -- [x] 34 -- a + b -> In einer Ausgabe hängt `+` die Teile immer aneinander, auch bei Zahlen. - -runde: - int a = 3 - int b = 4 - int summe = a + b - output: "Summe: " + summe -frage: Was wird ausgegeben? -- [x] Summe: 7 -- Summe: 34 -- Summe: summe -> Gerechnet wird vorher in einer Variablen. Die Ausgabe hängt nur noch Text und Ergebnis aneinander. - -runde: - input: String name "Wie heißt du?" - output: "Hallo" + name + "!" -frage: Du tippst Mia. Was wird ausgegeben? -- Hallo Mia! -- [x] HalloMia! -- Hallo name! -> Im Text `"Hallo"` fehlt das Leerzeichen am Ende. Darum klebt alles zusammen. - -runde: - input: int alter "Wie alt bist du?" -frage: Du tippst 2.5. Was passiert? -- alter ist 2. -- alter ist 2.5. -- [x] Die Simulation meldet einen Fehler. -> Der Datentyp `int` erlaubt nur ganze Zahlen. - -runde: - input: double preis "Preis in Euro?" -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- [x] Eingabe -- Verarbeitung -- Ausgabe -> `input:` fragt einen Wert ab. Das ist die Eingabe. - -runde: - double mittel = (a + b) / 2.0 -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- Eingabe -- [x] Verarbeitung -- Ausgabe -> Hier wird gerechnet. Das ist die Verarbeitung. - -runde: - output: "Mittelwert: " + mittel -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- Eingabe -- Verarbeitung -- [x] Ausgabe -> `output:` zeigt das Ergebnis an. Das ist die Ausgabe. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann das EVA-Prinzip erklären. → Das EVA-Prinzip -- Ich kann eine Eingabe mit Datentyp, Namen und Frage schreiben. → Eingabe mit input: -- Ich kann Text und Variablen in einer Ausgabe mit + verbinden. → Vorsicht mit + in der Ausgabe -- Ich kann ein Ergebnis zuerst ausrechnen und in einer Variablen speichern, dann diese Variable ausgeben, z. B. `int summe = a + b`, danach `output: summe`. → Vorsicht mit + in der Ausgabe -- Ich kann aus einem Aufgabentext Eingabe, Verarbeitung und Ausgabe herauslesen. → Vom Aufgabentext zum Struktogramm -- Ich kann in Java mit dem Scanner Zahlen einlesen. → Jetzt in Java -- Ich kann Kommentare mit `//` und `/* */` schreiben und damit meinen Code einteilen und erklären. → Kommentare im Code -::: - -## Wissenstest - -::: quiz -? Wofür steht EVA? -→ Das EVA-Prinzip -- [x] Eingabe, Verarbeitung, Ausgabe -- [ ] Eingabe, Variable, Anweisung -- [ ] Erst Variablen anlegen -> Viele Programme fragen erst Werte ab, verarbeiten sie und zeigen dann das Ergebnis. - -? Die Variablen a und b haben die Werte 3 und 4. Was zeigt `output: a + b`? -→ Vorsicht mit + in der Ausgabe -- [ ] 7 -- [x] 34 -- [ ] a + b -> In der Ausgabe hängt + die Teile aneinander. Rechne die Summe vorher in einer Variablen aus. - -? Welche Eingabe passt, wenn ein Preis wie 4.99 eingegeben werden soll? -→ Eingabe mit input: -- [ ] input: int preis "Preis?" -- [x] input: double preis "Preis?" -- [ ] input: String "Preis?" -> 4.99 ist eine Kommazahl, also double. Außerdem braucht jede Eingabe einen Variablennamen. - -? Was zeigt `output: "Alter: " + alter`, wenn alter den Wert 12 hat? -→ Vorsicht mit + in der Ausgabe -- [x] Alter: 12 -- [ ] "Alter: " + 12 -- [ ] Alter: alter - -? Aufgabe: *Das Programm fragt nach dem Radius eines Kreises und zeigt seinen Umfang an.* Ordne jedem Teil des Textes zu, wohin er im EVA-Prinzip gehört. -→ Vom Aufgabentext zum Struktogramm -= fragt nach dem Radius -> Eingabe -= Umfang = 2 * 3.14 * Radius -> Verarbeitung -= zeigt seinen Umfang an -> Ausgabe -> Gegeben ist der Radius, gesucht der Umfang. Die Formel verbindet beides. - -? Was liest `scanner.nextInt()` ein? -→ Jetzt in Java -- [x] Eine ganze Zahl -- [ ] Eine Kommazahl -- [ ] Einen ganzen Satz -> Für Kommazahlen gibt es `scanner.nextDouble()`, für ein Wort `scanner.next()`. - -? Was macht Java mit einem Kommentar? -→ Kommentare im Code -- [ ] Es gibt ihn als Text aus. -- [x] Es überspringt ihn. -- [ ] Es meldet einen Fehler. -> Kommentare sind Notizen für Menschen. Das Programm läuft mit und ohne sie genau gleich. - -? Wo endet ein Kommentar, der mit `/*` beginnt? -→ Kommentare im Code -- [x] Bei `*/` -- [ ] Am Ende der Zeile -- [ ] Bei der nächsten Leerzeile -> Darum kann ein Kommentar mit `/*` über mehrere Zeilen gehen. Ein Kommentar mit `//` endet dagegen am Zeilenende. -::: diff --git a/web/lessons/de/07-simulation-und-schreibtischtest.md b/web/lessons/de/07-simulation-und-schreibtischtest.md deleted file mode 100644 index 2f0ee800..00000000 --- a/web/lessons/de/07-simulation-und-schreibtischtest.md +++ /dev/null @@ -1,364 +0,0 @@ ---- -titel: Simulation und Schreibtischtest -bereich: Grundlagen -kurz: Ein Struktogramm Schritt für Schritt ausführen, von Hand nachrechnen und Fehler finden. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Simulation starten, anhalten, einzelne Schritte ausführen und stoppen -- während der Simulation Variablen, Ausgabe und den letzten Schritt ablesen -- einen Schreibtischtest als Tabelle von Hand durchführen -- Syntaxfehler, Laufzeitfehler und Logikfehler unterscheiden -- einen Fehler finden, indem ich die Werte mit meiner Erwartung vergleiche -- Java-Code mit dem Einzelschritt und Haltepunkten verfolgen -::: - -## Die Simulation starten - -Bevor es mit Verzweigungen und Schleifen weitergeht, lernst du dein wichtigstes Werkzeug kennen. Mit der Simulation prüfst du ab jetzt jede Übung selbst. - -Mit dem Knopf [▶︎ Simulation starten](#zeige-simulation) rechts bei den Werkzeugen führt VisuStruct dein Struktogramm aus. Solange die Simulation läuft, kannst du das Diagramm nicht bearbeiten. Derselbe Knopf heißt dann **■ Simulation beenden**, damit kehrst du zum Bearbeiten zurück. - -Rechts erscheint die **Steuerung**: - -- **Start** führt das Diagramm automatisch Schritt für Schritt aus. Wie lange zwischen zwei Schritten gewartet wird, stellst du bei **Pause pro Schritt** ein. -- **Pause** hält an. Danach geht es mit Start oder Schritt weiter. -- **Schritt** führt genau einen Schritt aus. So kannst du in Ruhe zuschauen. -- **Stopp** bricht ab und setzt alles auf den Anfang zurück. - -Öffne das folgende Diagramm im Editor und gehe es mit **Schritt** durch: - -::: diagramm sim-tausch.visustruct -Die Werte von a und b werden vertauscht. Die Variable hilf merkt sich dabei einen Wert. -::: - -## Was die Simulation anzeigt - -- **Markierung:** Ein Kasten im Diagramm ist farbig hervorgehoben. Bei **Letzter Schritt** ist es der Kasten, der gerade ausgeführt wurde. Bei **Nächster Schritt** ist es der Kasten, der als Nächstes drankommt. -- **Eingabe:** Fragt das Diagramm nach einem Wert, tippst du ihn hier ein und bestätigst mit **OK**. -- **Letzter Schritt:** Hier steht, was gerade passiert ist, z. B. `a = 5 -> 5` nach dem Kasten `a = b`. Links sind die Variablen schon durch ihre Werte ersetzt, rechts vom Pfeil steht das Ergebnis. -- **Variablen:** Alle Variablen mit ihren aktuellen Werten. -- **Ausgabe:** Alles, was mit `output:` ausgegeben wurde. - -Ist rechts der Bereich [Code](#zeige-codelive) geöffnet, läuft dort zusätzlich die [Konsole](#zeige-konsole) mit. Sie wird beim Start geleert und zeigt dann die Ausgaben und Eingaben so, wie sie auch beim echten Programm erscheinen würden. - -Am Ende erscheint **Simulation beendet**. Geht etwas schief, bleibt die Simulation auf dem fehlerhaften Kasten stehen und zeigt eine Meldung, z. B. **Unbekannte Variable: gesamt**. - -## Der Schreibtischtest - -Beim **Schreibtischtest** spielst du selbst den Computer. Du gehst das Struktogramm mit Stift und Papier durch und schreibst nach jedem Schritt die Werte der Variablen auf. Am besten machst du eine Tabelle: eine Spalte für den Schritt, eine für jede Variable und eine für die Ausgabe. - -::: grafik l07-schreibtischtest-de.svg -Ein Blatt Papier und ein Stift genügen: Du schreibst nach jedem Schritt die neuen Werte in eine neue Zeile. -::: - -Für das Diagramm oben sieht die Tabelle so aus: - -| Schritt | a | b | hilf | Ausgabe | -|---|---|---|---|---| -| `int a = 3` | 3 | – | – | | -| `int b = 5` | 3 | 5 | – | | -| `int hilf = a` | 3 | 5 | 3 | | -| `a = b` | 5 | 5 | 3 | | -| `b = hilf` | 5 | 3 | 3 | | -| `output: "a = " + a` | 5 | 3 | 3 | a = 5 | -| `output: "b = " + b` | 5 | 3 | 3 | b = 3 | - -Vergleiche danach deine Tabelle mit der Simulation. Stimmen die Werte unter **Variablen** nach jedem Schritt mit deiner Zeile überein? - -> Der Schreibtischtest lohnt sich besonders in Prüfungen. Dort hast du keinen Computer, aber du kannst trotzdem zeigen, was dein Struktogramm tut. - -::: angeberwissen -Früher war „Computer“ ein Beruf: Menschen, die mit Stift, Papier und Rechenmaschine rechneten. Eine der besten war die Mathematikerin Katherine Johnson bei der NASA. Sie berechnete Flugbahnen für Raumkapseln, später auch für den Flug von Apollo 11 zum Mond. - -1962 sollte John Glenn als erster Amerikaner die Erde umkreisen. Die Flugbahn hatte ein neuer IBM-Computer berechnet. Glenn traute der Maschine nicht ganz und sagte: „Holt das Mädchen, sie soll die Zahlen prüfen. Wenn sie sagt, sie stimmen, fliege ich.“ Katherine Johnson rechnete alles von Hand nach, also ein echter Schreibtischtest. Ihre Zahlen stimmten, und Glenn flog. - -Katherine Johnson war Schwarz, und im US-Bundesstaat Virginia trennten damals Gesetze Schwarze und Weiße. Im Film „Hidden Figures“ (2016) muss sie für jeden Toilettengang quer über das Gelände laufen. Das ist für den Film zugespitzt: Getrennte Toiletten gab es wirklich, aber Katherine Johnson hat das Schild nach eigener Aussage einfach ignoriert. - -Ihre Kollegin Dorothy Vaughan merkte früh, dass die IBM-Computer die Rechenarbeit übernehmen würden. Sie brachte sich und ihrem ganzen Team die Programmiersprache FORTRAN bei. So wurden aus menschlichen Computern Programmiererinnen. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), Mathematikerin bei der NASA (Foto: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), gemeinfrei). -::: - -## Drei Arten von Fehlern - -Fehler gehören zum Programmieren dazu, auch bei Profis. Man unterscheidet drei Arten, denn jede findest du auf einem anderen Weg: - -- **Syntaxfehler** (syntax error): Der Text hält sich nicht an die Regeln der Sprache, z. B. fehlt am Ende ein `;`. Das Programm startet gar nicht. -- **Laufzeitfehler** (runtime error): Das Programm startet, bricht aber an einer Stelle ab, z. B. beim Teilen durch 0. -- **Logikfehler** (logic error): Das Programm läuft bis zum Ende, aber das Ergebnis ist falsch, z. B. weil Klammern fehlen: `a + b / 2` statt `(a + b) / 2`. - -So zeigt dir VisuStruct die Fehler: - -- **Syntaxfehler:** Im Struktogramm passieren sie seltener, weil die Kästen die Form vorgeben. Im Bereich [Code](#zeige-codelive) wird die Stelle rot unterstrichen, die Zeilennummer wird rot, und dazu steht z. B. **Zeile 2: Hier fehlt noch: ;**. -- **Laufzeitfehler:** Die Simulation bleibt auf dem Kasten stehen und meldet z. B. **Division durch null.** Im Bereich Code wird die Zeilennummer rot, und die [Konsole](#zeige-konsole) nennt die Zeile: **Zeile 3: Division durch null.** -- **Logikfehler:** Hier meldet niemand etwas. Der Computer weiß nicht, was du eigentlich wolltest. Nur du kennst das richtige Ergebnis, darum hilft hier der Schreibtischtest. - -> Eine rote Zeilennummer zeigt einen Fehler. Der runde rote Punkt vor der Zeilennummer ist etwas anderes: ein Haltepunkt. Den lernst du unten bei „Jetzt in Java“ kennen. - -## Fehler finden - -Gibt ein Struktogramm etwas Falsches aus, ist das ein Logikfehler. Dann gehst du so vor: - -1. **Erwartung aufschreiben:** Was soll bei einer bestimmten Eingabe herauskommen? -2. **Schritt für Schritt ausführen:** Starte die Simulation mit **Schritt** und beobachte die Variablen. -3. **Die Stelle finden:** Wo weicht ein Wert zum ersten Mal von deiner Erwartung ab? Genau dort steckt der Fehler. - -Häufige Fehler in einer Sequenz sind: - -- Die **Reihenfolge** stimmt nicht: Eine Variable wird benutzt, bevor sie einen Wert hat. -- Ein Wert wird **überschrieben**, bevor er gebraucht wird. -- **Klammern** fehlen: `a + b / 2` ist etwas anderes als `(a + b) / 2`. -- Zwei ganze Zahlen werden geteilt: `7 / 2` ergibt `3`, weil bei `int` die Nachkommastellen wegfallen. - -> Später kommen Verzweigungen und Schleifen dazu. Dann zeigt dir die Simulation auch, welcher Weg genommen wird und wie oft eine Schleife läuft. - -::: angeberwissen -Am 9. September 1947 fand das Team von Grace Hopper im Rechner Harvard Mark II eine echte Motte, die zwischen zwei Kontakten klemmte. Sie klebten sie ins Logbuch und schrieben dazu: „Erster echter Fall, dass ein Bug gefunden wurde.“ Die Motte liegt heute in einem Museum in Washington. - -Grace Hopper war eine der ersten Programmiererinnen der Welt. 1952 baute sie einen der ersten **Compiler**: ein Programm, das Code in Maschinencode übersetzt. Sie fand, dass man Computern Befehle in fast normalem Englisch geben sollte. Daraus entstand die Programmiersprache COBOL, mit der viele Banken bis heute rechnen. Bei der US-Marine wurde sie sogar Admiralin. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), Informatikerin und Admiralin (Foto: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), gemeinfrei). -::: - -## Übung: Tauschen reparieren - -::: übung sim-tausch -titel: Tauschen reparieren -auftrag: Das Struktogramm soll zwei Zahlen einlesen und sie vertauscht wieder ausgeben. Bei 3 und 5 soll also erst **5** und dann **3** erscheinen. Es gibt aber zweimal 5 aus. - Mache einen Schreibtischtest oder nutze **Schritt** und repariere den Fehler. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Gehe das Diagramm mit **Schritt** durch und schreibe nach jedem Schritt die Werte von a und b auf. Wo verschwindet die 3? -tipp: Schau dir a nach dem Schritt `a = b` an. Wo ist der alte Wert von a geblieben? Merke ihn dir vorher in einer Hilfsvariablen, wie im Beispiel oben. -lösung: sim-tausch-loesung.visustruct -::: - -## Übung: Kinokarten reparieren - -::: übung sim-kino -titel: Kinokarten reparieren -auftrag: Eine Kinokarte kostet 8.50 €. Das Struktogramm soll nach der Anzahl fragen und den Gesamtpreis ausgeben, bei 2 Karten also `Gesamt: 17.0`. - Starte die Simulation. Sie bleibt mit einer Fehlermeldung stehen. Finde heraus, warum, und repariere das Struktogramm. -start: sim-kino-start.visustruct -test: 2 -> Gesamt: 17.0 -test: 3 -> Gesamt: 25.5 -test: 0 -> Gesamt: 0.0 -braucht: anweisung -tipp: Lies die Fehlermeldung der Simulation genau. Welche Variable kennt sie noch nicht? -tipp: Die Simulation führt die Kästen von oben nach unten aus. Hat gesamt schon einen Wert, wenn die Ausgabe drankommt? Du kannst Kästen mit der Maus verschieben. -lösung: sim-kino-loesung.visustruct -::: - -> Läuft eine Simulation endlos weiter, klicke auf **Stopp**. Beim **Prüfen** einer Übung bricht VisuStruct nach sehr vielen Schritten von selbst ab. - -## Jetzt in Java - -Auch Java-Code kannst du Schritt für Schritt ausführen. Im Bereich [Code](#zeige-codelive) heißt das Werkzeug dafür **Debugger**: - -- Klicke links auf eine Zeilennummer. Ein roter Punkt erscheint, das ist ein **Haltepunkt**. [▶︎ Ausführen](#zeige-ausfuehren) hält vor dieser Zeile an. -- Der Knopf [Einzelschritt](#zeige-einzelschritt) führt genau eine Zeile aus, wie **Schritt** in der Simulation. -- Unter dem Code steht, welchen Wert jede Variable gerade hat. Das ist dein Schreibtischtest, nur dass der Computer mitschreibt. - -::: übung l07-java-pizza -titel: Pizzabestellung reparieren -code: java -auftrag: Eine Pizza kostet 8 Euro, Extra-Käse 2 Euro. Für 3 Pizzen mit Extra-Käse soll das Programm **Gesamt: 30** ausgeben. Es gibt aber 14 aus. - Gehe den Java-Code mit dem Einzelschritt durch und beobachte `gesamt`. Finde den Fehler und repariere ihn im Code. -start: l07-java-pizza-start.visustruct -test: - -> Gesamt: 30 -braucht: anweisung -tipp: Welcher Wert steht nach der vierten Zeile in `gesamt`? Rechne im Kopf nach, was Java zuerst ausrechnet. -tipp: Wie in Mathe gilt Punkt vor Strich: `preis + kaese * anzahl` ist `8 + 6`. Setze Klammern: `(preis + kaese) * anzahl`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Lückentext: Schreibtischtest - -::: lückentext -titel: Was steht in den Variablen? -auftrag: Mach einen Schreibtischtest im Kopf. Trage hinter jeder Zeile ein, welchen Wert die Variable danach hat, und in der letzten Zeile, was ausgegeben wird. -```java -int a = 5; -int b = 2; -a = a + b; // a ist jetzt [[7]] -b = a * 2; // b ist jetzt [[14]] -a = b - a; // a ist jetzt [[7]] -System.out.println(a + b); // Ausgabe: [[21]] -``` -erklärung: Jede Zeile rechnet mit den Werten, die gerade in den Variablen stehen: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, Ausgabe 7 + 14 = 21. Mit dem Einzelschritt im Bereich Code kannst du es nachprüfen. -::: - -## Fehler finden: Spielstand - -::: übung l07-java-fehler -titel: Spielstand reparieren -code: java -auftrag: Ludwig bekommt in einem Spiel erst 10 Punkte, dann 5 Punkte, dann verliert er 3 Punkte. Am Ende soll **Punkte: 12** erscheinen. Es erscheint aber **Punkte: 3**. - Gehe den Code mit dem Einzelschritt durch und beobachte `punkte`. Repariere die falsche Zeile und klicke auf Prüfen. -start: l07-java-fehler-start.visustruct -test: - -> Punkte: 12 -braucht: anweisung -tipp: Nach welcher Zeile hat `punkte` plötzlich den Wert 3? Was soll in dieser Zeile eigentlich passieren? -tipp: Ludwig verliert 3 Punkte, also wird vom alten Wert abgezogen: `punkte = punkte - 3;` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l07-java-laeuft-nicht -titel: Bonus reparieren -code: java -auftrag: Ludwig hat 10 Punkte und bekommt 5 Bonuspunkte. Am Ende soll **Punkte: 15** erscheinen. Der Code läuft aber nicht. Gehe ihn auch mit dem Einzelschritt durch: In welcher Zeile bleibt er stehen? - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Punkte: 15 -braucht: anweisung -tipp: Java arbeitet den Code von oben nach unten ab. Gibt es `bonus` schon, wenn damit gerechnet wird? -tipp: Lege `bonus` vor der Rechnung an: erst `int bonus = 5;`, dann `punkte = punkte + bonus;` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Mach im Kopf den Schreibtischtest und tippe auf die falsche Zeile, bevor die Zeit abläuft. Die Programme sind in Java, aber jede Zeile ist ein Kasten wie im Struktogramm. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int hilf = a; - a = b; - b = a; -frage: Soll die Werte von a und b tauschen. -fehler: 5 -besser: b = hilf; -> In a steht schon der Wert von b. Der alte Wert von a steckt in hilf. - -runde: - int a = 4; - int b = 6; - double mittel = a + b / 2; - System.out.println(mittel); -frage: Soll den Mittelwert 5.0 ausgeben. -fehler: 3 -besser: double mittel = (a + b) / 2.0; -> Ohne Klammern wird zuerst geteilt: 4 + 6 / 2 = 7. - -runde: - int karten = 3; - double preis = 8.5; - double gesamt = karten + preis; - System.out.println("Gesamt: " + gesamt); -frage: Soll den Preis für 3 Kinokarten zu je 8,50 € ausgeben. -fehler: 3 -besser: double gesamt = karten * preis; -> Für den Gesamtpreis wird malgenommen, nicht addiert. - -runde: - int minuten = 135; - int stunden = minuten / 60; - int rest = minuten / 60; - System.out.println(stunden + " h " + rest + " min"); -frage: Soll 2 h 15 min ausgeben. -fehler: 3 -besser: int rest = minuten % 60; -> Den Rest beim Teilen liefert `%`. Mit `/` stünde auch in rest eine 2. - -runde: - String name = "Ada"; - System.out.println("Hallo " + "name" + "!"); -frage: Soll „Hallo Ada!“ ausgeben. -fehler: 2 -besser: System.out.println("Hallo " + name + "!"); -> In Anführungszeichen ist name nur ein Text. Die Variable steht ohne Anführungszeichen. - -runde: - double preis = 100; - preis = preis * 0.1; - System.out.println(preis); -frage: Soll den Preis um 10 % erhöhen, also 110.0 ausgeben. -fehler: 2 -besser: preis = preis + preis * 0.1; -> Mal 0.1 ergibt nur die 10 %, also 10.0. Die müssen zum alten Preis dazukommen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Simulation starten, anhalten, einzelne Schritte ausführen und stoppen. → Die Simulation starten -- Ich kann während der Simulation Variablen, Ausgabe und den letzten Schritt ablesen. → Was die Simulation anzeigt -- Ich kann einen Schreibtischtest als Tabelle von Hand durchführen. → Der Schreibtischtest -- Ich kann Syntaxfehler, Laufzeitfehler und Logikfehler unterscheiden. → Drei Arten von Fehlern -- Ich kann einen Fehler finden, indem ich die Werte mit meiner Erwartung vergleiche. → Fehler finden -- Ich kann Java-Code mit dem Einzelschritt und Haltepunkten verfolgen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Mit welchem Knopf führst du genau einen Schritt aus? -→ Die Simulation starten -- [ ] Start -- [x] Schritt -- [ ] Stopp -> Start läuft automatisch weiter, Schritt führt nur einen Schritt aus. - -? Was schreibst du beim Schreibtischtest nach jedem Schritt auf? -→ Der Schreibtischtest -- [ ] Nur die Ausgabe am Ende -- [x] Die aktuellen Werte aller Variablen -- [ ] Den erzeugten Java-Code -> So siehst du genau, an welcher Stelle ein Wert anders ist als erwartet. - -? Erst `int a = 3`, dann `int b = 5`, dann `a = b`. Welche Werte haben a und b danach? -→ Der Schreibtischtest -- [ ] a = 5 und b = 3 -- [x] a = 5 und b = 5 -- [ ] a = 3 und b = 5 -> Der alte Wert von a ist überschrieben. Darum braucht man beim Tauschen eine Hilfsvariable. - -? Im Feld Letzter Schritt steht `preis = 4 * 2.5 -> 10.0`. Was bedeutet das? -→ Was die Simulation anzeigt -- [ ] Die Variable preis hat jetzt den Wert 4. -- [x] 4 * 2.5 wurde ausgerechnet und das Ergebnis 10.0 in preis gespeichert. -- [ ] Die Simulation hat einen Fehler gefunden. - -? Die Simulation meldet **Unbekannte Variable: summe**. Was ist ein wahrscheinlicher Grund? -→ Fehler finden -- [x] summe wird benutzt, bevor sie einen Wert bekommen hat. -- [ ] summe ist zu groß. -- [ ] Das Struktogramm hat zu viele Kästen. - -? Was macht ein roter Punkt vor einer Zeilennummer im Bereich Code? -→ Jetzt in Java -- [ ] Er markiert einen Fehler. -- [x] Das Programm hält vor dieser Zeile an. -- [ ] Die Zeile wird übersprungen. -> Das ist ein Haltepunkt. Mit ▶︎ oder dem Einzelschritt geht es weiter. -? Ordne jedem Fehler seine Fehlerart zu. -→ Drei Arten von Fehlern -= Am Zeilenende fehlt das Semikolon. -> Syntaxfehler -= Das Programm teilt durch 0 und bricht ab. -> Laufzeitfehler -= Der Durchschnitt ist zu groß, weil Klammern fehlen. -> Logikfehler - -? Das Programm läuft ohne Meldung bis zum Ende, gibt aber 12 statt 21 aus. Welche Fehlerart ist das? -→ Drei Arten von Fehlern -- [ ] Syntaxfehler -- [ ] Laufzeitfehler -- [x] Logikfehler -> Es gibt keine Meldung, das Ergebnis ist trotzdem falsch. So einen Fehler findest du nur mit deiner Erwartung und dem Schreibtischtest. -::: diff --git a/web/lessons/de/08-verzweigung.md b/web/lessons/de/08-verzweigung.md deleted file mode 100644 index 094d8f14..00000000 --- a/web/lessons/de/08-verzweigung.md +++ /dev/null @@ -1,368 +0,0 @@ ---- -titel: Verzweigung -bereich: Grundlagen -kurz: Mit einer Bedingung entscheiden, welcher von zwei Wegen ausgeführt wird. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass bei einer Verzweigung genau ein Zweig ausgeführt wird -- die Vergleichsoperatoren == != < <= > >= anwenden -- == und = unterscheiden -- eine Verzweigung mit einem leeren Zweig bauen -- eine Verzweigung mit if und else in Java schreiben -::: - -## Wenn, dann, sonst - -Bisher wurden alle Kästen immer ausgeführt. Oft soll ein Programm aber **entscheiden**: Wenn eine Bedingung erfüllt ist, tue das eine, sonst das andere. - -::: diagramm l07-alter-pruefen.visustruct -Je nach eingegebenem Alter erscheint die eine oder die andere Meldung. Probiere in der Simulation 17 und 18 aus. -::: - -Dafür gibt es die **Verzweigung**. Oben im Dreieck steht die Bedingung. Darunter teilt sich das Struktogramm in zwei Zweige: - -- **true** (links): wird ausgeführt, wenn die Bedingung wahr ist -- **false** (rechts): wird ausgeführt, wenn die Bedingung falsch ist - -Es wird immer **genau einer** der beiden Zweige ausgeführt. Danach geht es unter der Verzweigung weiter. - -## Eine Verzweigung einfügen - -Ziehe die **Verzweigung** aus dem Bereich Elemente ins Struktogramm. Klicke das Dreieck an und schreibe im Objektinspektor die Bedingung, z. B. `alter >= 18`. Das Wort `if` steht schon davor. - -In jedem Zweig liegt zuerst ein Platzhalter ø. Ziehe dort Anweisungen hinein. In einem Zweig dürfen auch mehrere Kästen untereinander stehen. - -## Vergleichsoperatoren - -Eine Bedingung ist meistens ein **Vergleich**. Sein Ergebnis ist immer `true` oder `false`. - -- `==` gleich, z. B. `zahl == 0` -- `!=` ungleich, z. B. `antwort != 42` -- `<` kleiner, `<=` kleiner oder gleich -- `>` größer, `>=` größer oder gleich - -> Verwechsle `==` nicht mit `=`. Ein einzelnes `=` ist eine Zuweisung und speichert einen Wert. Zum Vergleichen brauchst du zwei Gleichheitszeichen. - -Mit `%` prüfst du, ob eine Zahl durch eine andere teilbar ist: `zahl % 2 == 0` ist wahr, wenn `zahl` gerade ist. - -## Übung: Gerade oder ungerade - -::: übung l07-gerade -titel: Gerade oder ungerade -auftrag: Frage eine ganze Zahl ab. Gib **gerade** aus, wenn sie gerade ist, und sonst **ungerade**. -test: 4 -> gerade -test: 7 -> ungerade -test: 0 -> gerade -braucht: verzweigung -tipp: Hier gibt es zwei Möglichkeiten, also brauchst du eine Verzweigung. Woran erkennst du eine gerade Zahl? -tipp: Eine Zahl ist gerade, wenn beim Teilen durch 2 kein Rest bleibt. Die Bedingung lautet `zahl % 2 == 0`. -lösung: l07-gerade-loesung.visustruct -::: - -## Übung: Die größere Zahl - -::: übung l07-maximum -titel: Die größere Zahl -auftrag: Frage zwei ganze Zahlen ab und gib nur die größere von beiden aus. Sind beide gleich, gib diese Zahl aus. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: Eine Verzweigung vergleicht die beiden Zahlen. In jedem Zweig steht genau eine Ausgabe. -tipp: Vergleiche mit `a > b`. Im true-Zweig gibst du `a` aus, im false-Zweig `b`. -lösung: l07-maximum-loesung.visustruct -::: - -## Ein leerer Zweig - -Manchmal soll nur dann etwas passieren, wenn die Bedingung wahr ist, und sonst gar nichts. Dann bleibt der false-Zweig **leer**. Im Struktogramm steht dort der Platzhalter **ø**. - -::: diagramm l07-rabatt.visustruct -Ab 50 Euro gibt es 5 Euro Rabatt. Sonst passiert nichts, der false-Zweig ist leer. Die Ausgabe darunter wird in beiden Fällen ausgeführt. -::: - -::: angeberwissen -Eine einzige vergessene Verzweigung hat 2014 halb das Internet in Aufregung versetzt: die Sicherheitslücke **Heartbleed** in OpenSSL, einem Programm, das viele Webseiten für die Verschlüsselung benutzen. Ein Computer konnte dem Server sagen: „Schick mir das Wort *Hut* zurück, es hat 500 Buchstaben.“ Der Server hat nicht geprüft, ob die 500 zur echten Länge 3 passen, und einfach 500 Zeichen aus seinem Speicher zurückgeschickt. Darin standen manchmal Passwörter anderer Leute. Gefehlt hat nur ein if: Wenn die angegebene Länge größer ist als die echte, dann nichts zurückschicken. Mit Klammern hatte das übrigens nichts zu tun. Mäh, prüf deine Eingaben, bevor du ihnen glaubst. -::: - -## Übung: Betrag - -::: übung l07-betrag -titel: Betrag einer Zahl -auftrag: Frage eine ganze Zahl ab. Ist sie negativ, mache sie positiv. Gib danach die Zahl aus. Aus -5 wird also 5, und 7 bleibt 7. -Nutze eine Verzweigung mit einem leeren Zweig und nur **einer** Ausgabe unter der Verzweigung. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Nur negative Zahlen müssen verändert werden. Was passiert mit einer Zahl, wenn du sie mal -1 nimmst? -tipp: Die Bedingung ist `zahl < 0`. Im true-Zweig drehst du das Vorzeichen um, z. B. mit `zahl = 0 - zahl`. Der false-Zweig bleibt leer. -lösung: l07-betrag-loesung.visustruct -::: - -## Jetzt in Java - -So sieht die Altersprüfung vom Anfang dieser Lektion in Java aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie alt bist du?"); -int alter = scanner.nextInt(); -if (alter >= 18) { - System.out.println("Du bist volljährig."); -} else { - System.out.println("Du bist noch nicht volljährig."); -} -``` - -- Die Bedingung steht nach `if` in **runden Klammern** `( )`. -- Der true-Zweig steht in **geschweiften Klammern** `{ }`, der false-Zweig folgt nach `else` in eigenen geschweiften Klammern. Hinter einer geschweiften Klammer steht kein Semikolon. -- Ist der false-Zweig leer, lässt du `else { }` einfach weg. -- Die ersten beiden Zeilen braucht dein Programm einmal, sobald es etwas einliest. Danach liest `scanner.nextInt()` eine ganze Zahl ein. Die Frage davor ist eine normale Ausgabe. - -::: angeberwissen -Java hat auch ein Maskottchen: **Duke**, ein kleiner schwarz-weißer Kerl mit großer roter Nase. Er wurde 1992 im Team von Java gezeichnet und winkt seitdem von Konferenzen und T-Shirts. Ludwig findet, ein Geißbock hätte besser gepasst. -::: - -::: übung l08-java-bestanden -titel: Bestanden in Java -code: java -auftrag: Schreibe ein Java-Programm, das nach der Punktzahl fragt. Ab 50 Punkten gibt es **bestanden** aus, sonst **nicht bestanden**. - Tippe den Code im Bereich Code und klicke dann auf Prüfen. -test: 50 -> bestanden -test: 49 -> nicht bestanden -test: 100 -> bestanden -test: 0 -> nicht bestanden -braucht: verzweigung -tipp: Fang mit den beiden Zeilen für den Scanner an. Dann fragst du nach den Punkten, liest sie ein und vergleichst sie mit `if`. -tipp: Die Bedingung lautet `punkte >= 50`. In den Zweigen steht `System.out.println("bestanden");` und nach `else` `System.out.println("nicht bestanden");`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Lückentext: Gerade oder ungerade - -::: lückentext -titel: Gerade oder ungerade -auftrag: Ergänze die fehlenden Wörter und Zeichen. Das Programm soll ausgeben, ob eine Zahl gerade ist. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Zahl?"); -int zahl = scanner.nextInt(); -[[if]] (zahl [[%]] 2 [[==]] 0) [[{]] - System.out.println("gerade"); -[[}]] [[else]] { - System.out.println("ungerade"); -} -``` -erklärung: `zahl % 2` ist der Rest beim Teilen durch 2. Ist er 0, ist die Zahl gerade. Zum Vergleichen brauchst du `==`, ein einzelnes `=` ist eine Zuweisung. Beide Zweige stehen in geschweiften Klammern. -::: - -::: angeberwissen -Steht im Zweig nur eine einzige Anweisung, darf man in Java die geschweiften Klammern sogar weglassen. Wir machen das trotzdem nie, und zwar aus diesem Grund: - -```java -if (alter >= 18) - System.out.println("Du bist volljährig."); - System.out.println("Du darfst den Film sehen."); -``` - -Ohne Klammern gehört nur die erste Zeile zum if. Die zweite läuft immer, auch für Zwölfjährige. Die Einrückung täuscht, denn Java achtet nicht auf Leerzeichen. - -Genau so ein Fehler steckte 2014 in iPhones und Macs, bekannt als **„goto fail“**. In der Sprache C stand unter einem if ohne Klammern die Zeile `goto fail;` aus Versehen zweimal. Die zweite lief immer und übersprang die Prüfung der Sicherheitszertifikate. In einem offenen WLAN hätte sich ein Angreifer so zum Beispiel als deine Bank ausgeben können. Mäh, Klammern kosten zwei Zeichen, eine Sicherheitslücke kostet viel mehr. -::: - -## Fehler finden: Eintritt - -::: übung l08-java-fehler -titel: Eintritt reparieren -code: java -auftrag: Im Zoo haben Kinder **unter** 6 Jahren freien Eintritt, alle anderen zahlen 4 Euro. Das Programm lässt aber auch Sechsjährige umsonst hinein. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l08-java-fehler-start.visustruct -test: 5 -> Eintritt frei -test: 6 -> 4 Euro -test: 10 -> 4 Euro -test: 0 -> Eintritt frei -braucht: verzweigung -tipp: Probiere das Programm mit 6 aus. Welcher Vergleichsoperator lässt die 6 noch durch? -tipp: „Unter 6“ heißt `alter < 6`. `<=` bedeutet „kleiner oder gleich“. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l08-java-laeuft-nicht -titel: Film ab 16 -code: java -auftrag: Der Film ist ab 16. Ludwig ist 15, also soll das Programm **Kein Einlass** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> Kein Einlass -braucht: verzweigung -tipp: Schau dir die Bedingung in der Klammer nach `if` genau an. Gibt es diese Variable? -tipp: Die Variable heißt `alter` mit kleinem a: `if (alter >= 16) {` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Weg? - -::: spiel frage -titel: Spiel: Welcher Weg? -intro: Ludwig hat Verzweigungen in Java geschrieben. Rechne die Bedingung im Kopf aus und sag vorher, was ausgegeben wird. Sieben Runden. - -runde: - int x = 5; - if (x > 5) { - System.out.println("groß"); - } else { - System.out.println("klein"); - } -frage: Was wird ausgegeben? -- groß -- [x] klein -- nichts -> 5 > 5 ist falsch, denn 5 ist nicht größer als 5. Also der false-Zweig. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("groß"); - } else { - System.out.println("klein"); - } -frage: Was wird ausgegeben? -- [x] groß -- klein -- groß und klein -> `>=` heißt „größer oder gleich“. 5 >= 5 ist wahr. - -runde: - int zahl = 7; - if (zahl % 2 == 0) { - System.out.println("gerade"); - } else { - System.out.println("ungerade"); - } -frage: Was wird ausgegeben? -- gerade -- [x] ungerade -- 1 -> 7 % 2 ist der Rest 1, also nicht 0. Die Zahl ist ungerade. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: Was wird ausgegeben? -- -4 -- [x] 4 -- nichts -> -4 < 0 ist wahr, also wird das Vorzeichen umgedreht. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: Was wird ausgegeben? -- -4 -- [x] 4 -- nichts -> 4 < 0 ist falsch. Der false-Zweig ist leer, das Programm macht einfach unten weiter. - -runde: - int note = 3; - if (note != 3) { - System.out.println("anders"); - } else { - System.out.println("drei"); - } -frage: Was wird ausgegeben? -- anders -- [x] drei -- nichts -> `!=` heißt „ungleich“. 3 != 3 ist falsch. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: Was wird ausgegeben? -- 8 -- [x] 12 -- 20 -> Das Programm gibt die größere der beiden Zahlen aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass bei einer Verzweigung genau ein Zweig ausgeführt wird. → Wenn, dann, sonst -- Ich kann die Vergleichsoperatoren == != < <= > >= anwenden. → Vergleichsoperatoren -- Ich kann == und = unterscheiden. → Vergleichsoperatoren -- Ich kann eine Verzweigung mit einem leeren Zweig bauen. → Ein leerer Zweig -- Ich kann eine Verzweigung mit if und else in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welcher Zweig wird ausgeführt, wenn die Bedingung falsch ist? -→ Wenn, dann, sonst -- [ ] Der true-Zweig -- [x] Der false-Zweig -- [ ] Beide Zweige -- [ ] Keiner, das Programm endet -> Bei false wird der rechte Zweig ausgeführt. Danach geht es unter der Verzweigung weiter. - -? Mit welcher Bedingung prüfst du, ob alter genau 18 ist? -→ Vergleichsoperatoren -- [ ] alter = 18 -- [x] alter == 18 -- [ ] alter >= 18 -> Ein einzelnes = ist eine Zuweisung. Zum Vergleichen brauchst du ==. - -? Was bedeutet ø in einem Zweig? -→ Ein leerer Zweig -- [x] In diesem Fall passiert nichts. -- [ ] Das Programm endet hier. -- [ ] Die Bedingung ist falsch. - -? Die Bedingung lautet `punkte >= 50`. Bei welchem Wert ist sie wahr? -→ Vergleichsoperatoren -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= heißt größer oder gleich. 50 gehört also dazu. - -? In Schulbüchern hat die Verzweigung oft noch einen anderen Namen. Welchen? -→ Wenn, dann, sonst -- [ ] Mehrfachauswahl -- [x] Bedingte Anweisung -- [ ] Wiederholungsstruktur -> Die Anweisungen in einem Zweig werden nur unter einer Bedingung ausgeführt. Darum heißt sie auch bedingte Anweisung. - -? Wo steht in Java die Bedingung einer Verzweigung? -→ Jetzt in Java -- [x] In runden Klammern direkt nach if -- [ ] In geschweiften Klammern nach else -- [ ] Hinter einem Semikolon -> `if (alter >= 18) { … }`: Die runden Klammern gehören zur Bedingung, die geschweiften zum Zweig. -::: diff --git a/web/lessons/de/09-bedingungen-verknuepfen.md b/web/lessons/de/09-bedingungen-verknuepfen.md deleted file mode 100644 index 0ecd38fc..00000000 --- a/web/lessons/de/09-bedingungen-verknuepfen.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -titel: Bedingungen verknüpfen -bereich: Grundlagen -kurz: Mehrere Bedingungen mit und, oder und nicht verbinden und Wertebereiche prüfen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, wann eine Verknüpfung mit && wahr ist -- erklären, wann eine Verknüpfung mit || wahr ist -- eine Bedingung mit ! umdrehen -- prüfen, ob ein Wert in einem Bereich wie 1 bis 6 liegt -- die Wahrheitstabelle für &&, || und ! lesen -- das Ergebnis einer Bedingung in einer boolean-Variablen speichern -- zusammengesetzte Bedingungen sicher auswerten -- Bedingungen in Java mit && und || verknüpfen -::: - -## Und: && - -Manchmal reicht ein einzelner Vergleich nicht. Ein Jugendticket gibt es zum Beispiel nur, wenn jemand **mindestens 12 und jünger als 18** ist. - -Mit `&&` (und) verbindest du zwei Bedingungen. Das Ergebnis ist nur dann `true`, wenn **beide** Bedingungen wahr sind. - -::: diagramm l08-jugendticket.visustruct -Beide Vergleiche müssen stimmen. Probiere in der Simulation 11, 12, 17 und 18 aus. -::: - -## Oder: || - -Mit `||` (oder) ist das Ergebnis `true`, wenn **mindestens eine** der Bedingungen wahr ist. Es dürfen auch beide wahr sein. - -``` -tag == 6 || tag == 7 -``` - -Diese Bedingung ist wahr, wenn `tag` den Wert 6 oder den Wert 7 hat, also am Samstag oder Sonntag. - -> Die beiden senkrechten Striche `|` tippst du auf vielen Tastaturen mit AltGr und der Taste `<`, am Mac mit Option und 7. - -## Übung: Wochenende - -::: übung l08-wochenende -titel: Wochenende -auftrag: Frage einen Wochentag als Zahl ab (1 ist Montag, 7 ist Sonntag). Gib **Wochenende** aus, wenn es Samstag oder Sonntag ist, sonst **Werktag**. -test: 6 -> Wochenende -test: 7 -> Wochenende -test: 1 -> Werktag -test: 5 -> Werktag -braucht: verzweigung -tipp: Wochenende ist, wenn der Tag 6 **oder** 7 ist. Dafür brauchst du zwei Vergleiche in einer Bedingung. -tipp: Samstag ist 6, Sonntag ist 7. Verbinde die beiden Vergleiche mit `||`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Wertebereiche prüfen - -Schulnoten gehen von 1 bis 6. Ob eine Note **im Bereich** liegt, prüfst du mit zwei Vergleichen und `&&`: - -``` -note >= 1 && note <= 6 -``` - -::: grafik l08-zahlenstrahl.svg -Gültig ist der grüne Bereich von 1 bis 6, beide Ränder gehören dazu. -::: - -Umgekehrt ist eine Note **außerhalb** des Bereichs, wenn sie zu klein **oder** zu groß ist: - -::: diagramm l08-note.visustruct -Eine Note kleiner als 1 oder größer als 6 gibt es nicht. -::: - -> Die Schreibweise aus Mathe `1 <= note <= 6` funktioniert nicht. Du musst sie in zwei Vergleiche aufteilen und mit `&&` verbinden. - -## Übung: Gültige Note - -::: übung l08-note-gueltig -titel: Gültige Note -auftrag: Frage eine Note als ganze Zahl ab. Gib **gültig** aus, wenn sie zwischen 1 und 6 liegt (1 und 6 gehören dazu), sonst **ungültig**. -test: 1 -> gültig -test: 6 -> gültig -test: 3 -> gültig -test: 0 -> ungültig -test: 7 -> ungültig -braucht: verzweigung -tipp: Eine gültige Note muss zwei Dinge gleichzeitig erfüllen: nicht zu klein **und** nicht zu groß. -tipp: Die Note muss größer oder gleich 1 und gleichzeitig kleiner oder gleich 6 sein. -lösung: l08-gueltig-loesung.visustruct -::: - -## Nicht und Klammern - -Mit `!` (nicht) drehst du eine Bedingung um: Aus `true` wird `false` und umgekehrt. - -``` -!(note >= 1 && note <= 6) -``` - -Das ist wahr, wenn die Note **nicht** im Bereich liegt, und bedeutet dasselbe wie `note < 1 || note > 6`. - -Kommen `&&` und `||` zusammen vor, wird `&&` zuerst ausgewertet, so wie Punkt vor Strich. Setze lieber **Klammern**, dann ist klar, was zusammengehört: - -``` -(alter >= 12 && alter < 18) || schueler == 1 -``` - -## Die Wahrheitstabelle - -Eine **Wahrheitstabelle** zeigt auf einen Blick, was bei `&&`, `||` und `!` herauskommt. Links stehen alle vier Möglichkeiten für zwei Bedingungen `a` und `b`, rechts das Ergebnis: - -::: grafik l09-wahrheitstabelle.svg -Grün ist true, rot ist false. Bei && gibt es nur eine grüne Zeile, bei || nur eine rote. -::: - -Merksätze: - -- `a && b` ist nur wahr, wenn **beide** wahr sind. -- `a || b` ist nur falsch, wenn **beide** falsch sind. -- `!a` dreht den Wert um. - -::: angeberwissen -Der Datentyp `boolean` heißt nach **George Boole**. Der Engländer hat schon 1847 gezeigt, dass man mit wahr und falsch rechnen kann wie mit Zahlen, fast hundert Jahre bevor es den ersten Computer gab. Heute steckt seine Logik in jedem Chip. -::: - -## Wahrheitswerte in Variablen - -Das Ergebnis einer Bedingung ist ein **Wahrheitswert** vom Typ `boolean`. Du kannst ihn wie eine Zahl in einer Variablen speichern und später benutzen: - -``` -boolean wochenende = tag == 6 || tag == 7 -``` - -In der Verzweigung steht dann nur noch `wochenende`. Das liest sich fast wie ein Satz: „Wenn Wochenende, dann …“. Schreibe nicht `wochenende == true`, denn `wochenende` ist ja schon true oder false. - -Mit `!wochenende` fragst du nach dem Gegenteil, also nach einem Werktag. - -::: ludwig -Lange Bedingungen werden übersichtlich, wenn du Teile davon in boolean-Variablen mit sprechenden Namen packst, zum Beispiel `boolean jugendlich = alter >= 12 && alter < 18`. -::: - -## Spiel: Wahr oder falsch? - -Jetzt bist du dran. Rechne jede Bedingung im Kopf aus. Nach jeder Antwort siehst du, wie VisuStruct sie Schritt für Schritt auswertet. - -::: spiel -alter: 8..20 -tag: 1..7 -note: 0..7 -schueler: boolean -wochenende: boolean -? alter >= 18 -? tag == 6 -? note != 3 -? alter >= 12 && alter < 18 -? tag == 6 || tag == 7 -? schueler && wochenende -? !schueler -? note >= 1 && note <= 6 -? !(alter < 18) -? schueler || alter >= 18 -? (alter >= 12 && alter < 18) || schueler -? !(note >= 1 && note <= 6) -? !wochenende && tag != 3 -? alter >= 12 && alter < 18 && !schueler -::: - -## Übung: Schaltjahr - -::: übung l08-schaltjahr -titel: Schaltjahr -auftrag: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist, aber nicht durch 100. Jahre, die durch 400 teilbar sind, sind trotzdem Schaltjahre. -Frage eine Jahreszahl ab und gib **Schaltjahr** oder **kein Schaltjahr** aus. -test: 2024 -> Schaltjahr -test: 2023 -> kein Schaltjahr -test: 1900 -> kein Schaltjahr -test: 2000 -> Schaltjahr -braucht: verzweigung -tipp: Prüfe die Regel zuerst an Beispielen: 2024 ist ein Schaltjahr, 1900 nicht, 2000 schon. Teilbar heißt: Der Rest ist 0. -tipp: Teilbar durch 4 heißt `jahr % 4 == 0`, nicht teilbar durch 100 heißt `jahr % 100 != 0`. Verbinde diese beiden mit `&&`, setze Klammern darum und hänge mit `||` den Fall für 400 an. -tipp: Fast die Lösung: Eine einzige Verzweigung mit der Bedingung `(jahr % 4 == 0 && jahr % 100 != 0) || jahr % 400 == 0`. Im true-Zweig steht `output: "Schaltjahr"`, im false-Zweig `output: "kein Schaltjahr"`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Jetzt in Java - -Das Jugendticket vom Anfang sieht in Java so aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie alt bist du?"); -int alter = scanner.nextInt(); -if (alter >= 12 && alter < 18) { - System.out.println("Jugendticket"); -} else { - System.out.println("Kein Jugendticket"); -} -``` - -Die Zeichen `&&`, `||` und `!` sind genau die, die du schon im Struktogramm benutzt. Sie kommen aus Java. Die ganze Bedingung steht wie immer in den runden Klammern nach `if`. - -::: übung l09-java-schule -titel: Schule oder Freizeit -code: java -auftrag: Schreibe ein Java-Programm, das nach der Stunde fragt (0 bis 23). Von 8 bis einschließlich 15 Uhr gibt es **Schule** aus, sonst **Freizeit**. -test: 8 -> Schule -test: 15 -> Schule -test: 16 -> Freizeit -test: 7 -> Freizeit -braucht: verzweigung -tipp: Die Stunde muss zwei Bedingungen gleichzeitig erfüllen. Welcher Operator heißt „und“? -tipp: `if (stunde >= 8 && stunde <= 15) { … } else { … }` -lösung: l09-java-schule-loesung.visustruct -::: - -## Lückentext: Bedingungen - -::: lückentext -titel: Note und Wochenende -auftrag: Ergänze die fehlenden Operatoren und Wörter. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Note?"); -int note = scanner.nextInt(); -if (note >= 1 [[&&]] note [[<=]] 6) { - System.out.println("gültige Note"); -} else { - System.out.println("ungültige Note"); -} -System.out.println("Welcher Tag (1 bis 7)?"); -int tag = scanner.nextInt(); -[[boolean]] wochenende = tag == 6 [[||]] tag == 7; -if ([[!]]wochenende) { - System.out.println("Schultag"); -} -``` -erklärung: Eine Note ist gültig, wenn **beide** Bedingungen stimmen, also `&&`. Wochenende ist, wenn **eine** der beiden stimmt, also `||`. Das Ergebnis passt in eine `boolean`-Variable, und `!` dreht es um. -::: - -## Fehler finden: Schulfrei - -::: übung l09-java-fehler -titel: Schulfrei reparieren -code: java -auftrag: Das Programm soll bei Tag 6 (Samstag) und Tag 7 (Sonntag) **schulfrei** ausgeben, sonst **Schule**. Es gibt aber immer **Schule** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l09-java-fehler-start.visustruct -test: 6 -> schulfrei -test: 7 -> schulfrei -test: 3 -> Schule -test: 1 -> Schule -braucht: verzweigung -tipp: Kann `tag` gleichzeitig 6 **und** 7 sein? -tipp: Es reicht, wenn eine der beiden Bedingungen stimmt: `tag == 6 || tag == 7`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l09-java-laeuft-nicht -titel: Wochenende reparieren -code: java -auftrag: Tag 6 ist Samstag, Tag 7 ist Sonntag. Heute ist Tag 7, also soll **schulfrei** erscheinen. Das Programm läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> schulfrei -braucht: verzweigung -tipp: Die Bedingung besteht aus zwei Teilen. Vergleiche in beiden Teilen den Namen der Variablen. -tipp: Auch im zweiten Teil heißt die Variable `tag` mit kleinem t: `if (tag == 6 || tag == 7) {` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, wann eine Verknüpfung mit && wahr ist. → Und: && -- Ich kann erklären, wann eine Verknüpfung mit || wahr ist. → Oder: || -- Ich kann eine Bedingung mit ! umdrehen. → Nicht und Klammern -- Ich kann prüfen, ob ein Wert in einem Bereich wie 1 bis 6 liegt. → Wertebereiche prüfen -- Ich kann die Wahrheitstabelle für &&, || und ! lesen. → Die Wahrheitstabelle -- Ich kann das Ergebnis einer Bedingung in einer boolean-Variablen speichern. → Wahrheitswerte in Variablen -- Ich kann zusammengesetzte Bedingungen sicher auswerten (im Spiel mindestens 8 von 10 richtig). → Spiel: Wahr oder falsch? -- Ich kann Bedingungen in Java mit && und || verknüpfen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wann ist `a && b` wahr? -→ Und: && -- [ ] Wenn a oder b wahr ist -- [x] Nur wenn a und b beide wahr sind -- [ ] Wenn genau eins von beiden wahr ist -> Bei && müssen beide Teile stimmen. Ist einer falsch, ist das Ganze falsch. - -? Die Variable tag hat den Wert 6. Was ergibt `tag == 6 || tag == 7`? -→ Oder: || -- [x] true -- [ ] false -> Bei || reicht es, wenn eine Seite wahr ist. - -? Mit welcher Bedingung prüfst du, ob zahl zwischen 10 und 20 liegt (beide eingeschlossen)? -→ Wertebereiche prüfen -- [ ] 10 <= zahl <= 20 -- [ ] zahl >= 10 || zahl <= 20 -- [x] zahl >= 10 && zahl <= 20 -> Mit || wäre jede Zahl erlaubt, denn jede Zahl ist größer als 10 oder kleiner als 20. - -? Was ergibt `!(5 > 3)`? -→ Nicht und Klammern -- [ ] true -- [x] false -> 5 > 3 ist wahr, und ! dreht das um. - -? Wie schreibst du in Java „alter ist mindestens 12 und kleiner als 18“? -→ Jetzt in Java -- [ ] if (12 <= alter < 18) -- [x] if (alter >= 12 && alter < 18) -- [ ] if (alter >= 12 || alter < 18) -> Jeder Vergleich steht für sich, verbunden mit `&&`. -::: diff --git a/web/lessons/de/10-fallauswahl.md b/web/lessons/de/10-fallauswahl.md deleted file mode 100644 index e1ceb0b1..00000000 --- a/web/lessons/de/10-fallauswahl.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -titel: Fallauswahl -bereich: Grundlagen -kurz: Je nach Wert einer Variablen einen von vielen Fällen ausführen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Fallauswahl einfügen, Fälle hinzufügen und Fallnamen ändern -- erklären, wann der Fall default ausgeführt wird -- entscheiden, ob eine Fallauswahl oder eine Verzweigung besser passt -- eine Fallauswahl in Java mit switch schreiben -::: - -## Viele Möglichkeiten, eine Variable - -Ein Getränkeautomat hat drei Tasten: 1 für Wasser, 2 für Saft, 3 für Kakao. Mit Verzweigungen brauchst du dafür drei ineinander geschachtelte Kästen. Das wird schnell breit und unübersichtlich. - -::: diagramm l09-verschachtelt.visustruct -Der Automat mit drei verschachtelten Verzweigungen. Jede Verzweigung prüft wieder dieselbe Variable wahl. -::: - -Immer wenn du **eine Variable** mit **mehreren festen Werten** vergleichst, geht es einfacher: mit der **Fallauswahl**. - -## So ist die Fallauswahl aufgebaut - -Oben im Kasten steht die Variable, nach der ausgewählt wird. Darunter steht für jeden Fall eine eigene Spalte. Über jeder Spalte steht der Wert, zu dem sie gehört. - -::: diagramm l09-automat.visustruct -Derselbe Automat als Fallauswahl. Hat wahl den Wert 2, wird nur die Spalte 2 ausgeführt. -::: - -- Die Simulation sucht den Fall, dessen Name genau zum Wert passt. -- Passt kein Fall, wird **default** ausgeführt. Dieser Fall steht immer ganz rechts. -- Danach geht es unter der Fallauswahl weiter. Es wird immer nur **ein** Fall ausgeführt. - -> Eine neue Fallauswahl hat die Fälle 1, 2 und default. Klicke einen Fall an, um im Objektinspektor seinen **Fallnamen** zu ändern. Mit **Weiteren Fall hinzufügen** bekommst du eine neue Spalte vor default. - -## Fälle mit Texten - -Die Fälle müssen keine Zahlen sein. Du kannst auch nach dem Inhalt einer `String`-Variablen auswählen. Als Fallname schreibst du dann den Text in Anführungszeichen, zum Beispiel `"rot"`. So erkennt man wie im Code, dass es ein Text ist. - -::: diagramm l09-ampel.visustruct -Eine Ampel: Je nach eingegebener Farbe erscheint ein anderer Hinweis. Probiere in der Simulation auch eine Farbe aus, die es nicht gibt. -::: - -Groß- und Kleinschreibung zählt: `Rot` passt nicht zum Fall `"rot"`. - -## Übung: Note in Worten - -::: übung l09-note -titel: Note in Worten -auftrag: Frage eine Note als ganze Zahl ab und gib sie in Worten aus: 1 sehr gut, 2 gut, 3 befriedigend, 4 ausreichend, 5 mangelhaft, 6 ungenügend. - Bei jeder anderen Zahl soll **Keine gültige Note** erscheinen. -test: 1 -> sehr gut -test: 4 -> ausreichend -test: 6 -> ungenügend -test: 9 -> Keine gültige Note -braucht: fallauswahl -tipp: Für viele feste Werte ist die Fallauswahl übersichtlicher als viele Verzweigungen. Jede Note bekommt ihren eigenen Fall. -tipp: Füge mit „Weiteren Fall hinzufügen“ so lange Fälle hinzu, bis du die Fälle 1 bis 6 und default hast. Oben in die Fallauswahl schreibst du `note`. -lösung: l09-note-loesung.visustruct -::: - -## Übung: Taschenrechner - -::: übung l09-rechner -titel: Taschenrechner -auftrag: Frage nacheinander eine ganze Zahl `a`, ein Rechenzeichen als `String` und eine zweite ganze Zahl `b` ab. - Bei `+`, `-` und `*` gibst du das Ergebnis aus. Bei jedem anderen Zeichen erscheint **Unbekanntes Rechenzeichen**. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Lies erst alle drei Werte ein. Danach entscheidet eine Fallauswahl über das Rechenzeichen, was gerechnet wird. -tipp: Lege vor der Fallauswahl `int ergebnis = 0` an. In jedem Fall rechnest du zum Beispiel `ergebnis = a + b` und gibst danach `ergebnis` aus. Die Fallnamen sind `"+"`, `"-"` und `"*"` in Anführungszeichen, weil das Rechenzeichen ein `String` ist. -tipp: Das Gerüst von oben nach unten: drei Eingaben (`int a`, `String zeichen`, `int b`), dann `int ergebnis = 0`, dann eine Fallauswahl über `zeichen`. Im Fall `"+"` stehen `ergebnis = a + b` und `output: ergebnis`, bei `"-"` und `"*"` genauso. Im Sonst-Fall steht `output: "Unbekanntes Rechenzeichen"`. -lösung: l09-rechner-loesung.visustruct -::: - -## Fallauswahl oder Verzweigung? - -Die Fallauswahl prüft nur, ob ein Wert **genau gleich** einem Fallnamen ist. Daraus folgt: - -- **Fallauswahl:** eine Variable, mehrere feste Werte. Beispiele: Menüauswahl, Wochentag als Zahl, Noten. -- **Verzweigung:** Bereiche und verknüpfte Bedingungen. Beispiele: `alter >= 18`, `temperatur < 0 || temperatur > 30`. - -Für „Alter ab 18“ brauchst du also weiter die Verzweigung. Du kannst nicht für jedes mögliche Alter einen eigenen Fall anlegen. - -## Jetzt in Java - -Der Getränkeautomat sieht in Java so aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 Wasser, 2 Saft, 3 Kakao"); -int wahl = scanner.nextInt(); -switch (wahl) { - case 1 -> { - System.out.println("Hier ist dein Wasser."); - } - case 2 -> { - System.out.println("Hier ist dein Saft."); - } - case 3 -> { - System.out.println("Hier ist dein Kakao."); - } - default -> { - System.out.println("Diese Taste gibt es nicht."); - } -} -``` - -- Nach `switch` steht in runden Klammern die Variable, die verglichen wird. -- Jeder Fall beginnt mit `case`, dem Wert und einem Pfeil `->`. Dahinter steht in geschweiften Klammern, was passieren soll. -- `default` ist der Sonst-Fall. Er läuft, wenn kein Wert passt. - -::: angeberwissen -Den Pfeil `case 1 ->` gibt es in Java erst seit 2020 (Java 14). Davor schrieb man `case 1:` und musste jeden Fall mit `break;` beenden. Wer das vergaß, lief einfach in den nächsten Fall weiter. Der Pfeil hat also sehr vielen Programmierern Ärger erspart. -::: - -::: übung l10-java-wuerfel -titel: Würfel in Worten -code: java -auftrag: Schreibe ein Java-Programm mit `switch`, das eine Zahl einliest. Bei 1 gibt es **eins** aus, bei 2 **zwei**, bei 3 **drei** und sonst **kenne ich nicht**. -test: 1 -> eins -test: 2 -> zwei -test: 3 -> drei -test: 7 -> kenne ich nicht -braucht: fallauswahl -tipp: Du brauchst drei `case`-Fälle und einen `default`-Fall. -tipp: `switch (zahl) { case 1 -> { System.out.println("eins"); } … default -> { System.out.println("kenne ich nicht"); } }` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Lückentext: switch - -::: lückentext -titel: Monate in Worten -auftrag: Ergänze die fehlenden Wörter und Zeichen der Fallauswahl. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welcher Monat (1 bis 3)?"); -int monat = scanner.nextInt(); -[[switch]] (monat) { - [[case]] 1 -> { - System.out.println("Januar"); - } - case 2 [[->]] { - System.out.println("Februar"); - } - [[case]] 3 -> { - System.out.println("März"); - } - [[default]] -> { - System.out.println("Kenne ich nicht"); - } -} -``` -erklärung: Nach `switch` steht die Variable in runden Klammern. Jeder Fall beginnt mit `case`, dem Wert und dem Pfeil `->`. `default` läuft, wenn kein Fall passt. -::: - -## Fehler finden: Rechner - -::: übung l10-java-fehler -titel: Rechner reparieren -code: java -auftrag: Der Rechner liest zwei Zahlen und eine Wahl ein: 1 plus, 2 minus, 3 mal. Bei 7, 3 und Wahl 2 soll **4** herauskommen, er rechnet aber **-4**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Gibt es nicht -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Plus und mal stimmen. Schau dir den Fall `case 2` genau an. -tipp: Beim Minus zählt die Reihenfolge: `a - b` statt `b - a`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l10-java-laeuft-nicht -titel: Note reparieren -code: java -auftrag: Ludwig hat die Note 2 geschrieben. Das Programm soll **gut** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> gut -braucht: fallauswahl -tipp: Welche Variable prüft die Fallauswahl? Steht sie genauso oben? -tipp: In der Klammer nach `switch` muss `note` mit kleinem n stehen: `switch (note) {` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Fall? - -::: spiel frage -titel: Spiel: Welcher Fall? -intro: Ludwig drückt Tasten am Getränkeautomaten und an anderen Fallauswahlen. Was passiert? Sechs Runden. - -runde: - int wahl = 2; - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - case 2 -> { System.out.println("Saft"); } - case 3 -> { System.out.println("Kakao"); } - default -> { System.out.println("Diese Taste gibt es nicht."); } - } -frage: Was wird ausgegeben? -- Wasser -- [x] Saft -- Diese Taste gibt es nicht. -> wahl ist 2, also läuft genau der Fall `case 2`. - -runde: - int wahl = 4; - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - case 2 -> { System.out.println("Saft"); } - case 3 -> { System.out.println("Kakao"); } - default -> { System.out.println("Diese Taste gibt es nicht."); } - } -frage: Was wird ausgegeben? -- Kakao -- nichts -- [x] Diese Taste gibt es nicht. -> Kein Fall passt zu 4, also läuft `default`. - -runde: - String tag = "montag"; - switch (tag) { - case "Montag" -> { System.out.println("Mathe"); } - case "Dienstag" -> { System.out.println("Sport"); } - default -> { System.out.println("frei"); } - } -frage: Was wird ausgegeben? -- Mathe -- Sport -- [x] frei -> Bei Texten zählt jeder Buchstabe: "montag" mit kleinem m ist nicht "Montag". - -runde: - int note = 0; - switch (note) { - case 1 -> { System.out.println("sehr gut"); } - case 2 -> { System.out.println("gut"); } - case 3 -> { System.out.println("befriedigend"); } - } -frage: Was wird ausgegeben? -- sehr gut -- [x] nichts -- befriedigend -> Kein Fall passt, und es gibt kein `default`. Dann passiert einfach nichts. - -runde: -frage: Du willst prüfen, ob `alter >= 18` ist. Was passt besser? -- [x] eine Verzweigung -- eine Fallauswahl -> Eine Fallauswahl vergleicht nur mit festen Werten. Für „größer oder gleich“ brauchst du eine Verzweigung. - -runde: -frage: Ein Würfel zeigt 1 bis 6, und für jede Zahl soll ein anderes Wort erscheinen. Was passt besser? -- eine Verzweigung -- [x] eine Fallauswahl -> Eine Variable, sechs feste Werte: genau dafür ist die Fallauswahl da. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Fallauswahl einfügen, Fälle hinzufügen und Fallnamen ändern. → So ist die Fallauswahl aufgebaut -- Ich kann erklären, wann der Fall default ausgeführt wird. → So ist die Fallauswahl aufgebaut -- Ich kann entscheiden, ob eine Fallauswahl oder eine Verzweigung besser passt. → Fallauswahl oder Verzweigung? -- Ich kann eine Fallauswahl in Java mit switch schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Die Variable tag hat den Wert 5. Es gibt die Fälle 1, 2, 3 und default. Was wird ausgeführt? -→ So ist die Fallauswahl aufgebaut -- [ ] Gar nichts -- [ ] Der Fall 3 -- [x] Der Fall default -> Kein Fallname passt zu 5, deshalb greift default. - -? Wie viele Fälle werden bei einem Durchlauf der Fallauswahl ausgeführt? -→ So ist die Fallauswahl aufgebaut -- [x] Genau einer -- [ ] Alle Fälle ab dem passenden -- [ ] Alle Fälle -> Nach dem passenden Fall geht es direkt unter der Fallauswahl weiter. - -? Wofür ist eine Fallauswahl NICHT geeignet? -→ Fallauswahl oder Verzweigung? -- [ ] Menüpunkt 1, 2 oder 3 auswählen -- [x] Prüfen, ob eine Temperatur über 30 Grad liegt -- [ ] Eine Farbe wie rot, gelb oder grün auswerten -> „Über 30“ ist ein Bereich. Dafür brauchst du eine Verzweigung mit einem Vergleich. - -? Du gibst bei der Ampel `Rot` ein, der Fall heißt `"rot"`. Was passiert? -→ So ist die Fallauswahl aufgebaut -- [ ] Der Fall `"rot"` wird ausgeführt. -- [x] Der Fall default wird ausgeführt. -- [ ] Die Simulation meldet einen Fehler. -> Groß- und Kleinschreibung zählt. Rot und rot sind verschiedene Texte. - -? Unter welchem Namen findest du die Fallauswahl auch in Schulbüchern? -→ So ist die Fallauswahl aufgebaut -- [ ] Bedingte Anweisung -- [ ] Sequenz -- [x] Mehrfachauswahl -> Aus vielen Fällen wird einer ausgewählt. Darum heißt sie auch Mehrfachauswahl. - -? Wie heißt in Java der Sonst-Fall einer Fallauswahl? -→ Jetzt in Java -- [ ] else -- [x] default -- [ ] case 0 -::: diff --git a/web/lessons/de/11-while-schleife.md b/web/lessons/de/11-while-schleife.md deleted file mode 100644 index 409538ba..00000000 --- a/web/lessons/de/11-while-schleife.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -titel: While-Schleife -bereich: Grundlagen -kurz: Schritte wiederholen, solange eine Bedingung wahr ist. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die While-Schleife ihre Bedingung vor jedem Durchlauf prüft -- eine Zählvariable mit Startwert, Bedingung und Veränderung verwenden -- erkennen, wenn eine Schleife nie endet, und den Fehler beheben -- eine Schleife im Schreibtischtest Durchlauf für Durchlauf verfolgen -- eine While-Schleife in Java schreiben -::: - -## Wiederholen, solange etwas gilt - -Oft soll ein Programm etwas mehrmals tun. Statt denselben Kasten fünfmal hinzuschreiben, nimmst du eine **Schleife**. - -Die **While-Schleife** wiederholt ihren Inhalt, **solange** ihre Bedingung wahr ist. Die Bedingung steht oben im Kopf des Kastens. Der Inhalt steht eingerückt darunter, links von einer Klammer eingefasst. - -::: diagramm l10-countdown.visustruct -Ein Countdown: Solange zahl größer als 0 ist, wird zahl ausgegeben und um 1 kleiner. Danach erscheint Start! -::: - -## Die Bedingung kommt zuerst - -Die While-Schleife ist **kopfgesteuert**. Vor **jedem** Durchlauf wird die Bedingung geprüft: - -1. Bedingung prüfen. -2. Ist sie wahr, den Inhalt einmal ausführen und wieder bei 1 anfangen. -3. Ist sie falsch, geht es unter der Schleife weiter. - -::: grafik l10-kopfgesteuert.svg -Nach jedem Durchlauf geht es zurück zur Bedingung. Erst wenn sie false ist, geht es unter der Schleife weiter. -::: - -Im Countdown wird die Bedingung viermal geprüft: bei 3, 2 und 1 ist sie wahr, bei 0 ist sie falsch. - -> Ist die Bedingung schon beim ersten Prüfen falsch, wird der Inhalt **kein einziges Mal** ausgeführt. Starte den Countdown mit `int zahl = 0`, dann erscheint nur Start! - -## Eine Zählvariable - -Viele Schleifen zählen mit. Dafür brauchst du drei Dinge: - -- **Startwert vor der Schleife**, z. B. `int i = 1` -- **Bedingung im Kopf**, z. B. `i <= 5` -- **Veränderung im Inhalt**, z. B. `i = i + 1` (kurz `i++`) - -Fehlt einer dieser Teile, zählt die Schleife falsch oder gar nicht. - -::: angeberwissen -Zählschleifen sind uralt! Im Artikel von 1973 haben Nassi und Shneiderman ihre Idee unter anderem an der **Fakultät** gezeigt: 5! = 1 · 2 · 3 · 4 · 5 = 120. Hier ist meine Ziegen-Version davon. Probier sie mit **Schritt** in der Simulation aus und schau zu, wie `fakultaet` wächst. Määäh, das geht schnell! -::: - -::: diagramm l10-fakultaet.visustruct -Die Fakultät mit einer Zählvariable `i`, nachgebaut nach einem Beispiel aus dem Artikel von 1973. -::: - -## Übung: Zählen bis n - -::: übung l10-bis-n -titel: Zählen bis n -auftrag: Frage eine ganze Zahl `n` ab und gib alle Zahlen von 1 bis `n` aus, jede in einer eigenen Zeile. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: Du brauchst eine Zählvariable, die bei 1 beginnt und in jedem Durchlauf um 1 wächst. -tipp: Lege vor der Schleife `int i = 1` an. Die Bedingung ist `i <= n`. Im Inhalt gibst du `i` aus und erhöhst danach `i` um 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Vorsicht, Endlosschleife - -Wenn sich im Inhalt nichts an der Bedingung ändert, bleibt sie für immer wahr. Die Schleife hört nie auf. Das nennt man **Endlosschleife**. - -::: diagramm l10-endlos.visustruct -Hier fehlt zahl = zahl - 1. Die Schleife gibt immer wieder 3 aus und erreicht Start! nie. -::: - -Frage dich bei jeder Schleife: **Was im Inhalt sorgt dafür, dass die Bedingung irgendwann falsch wird?** Beim Prüfen einer Übung bricht VisuStruct nach sehr vielen Schritten ab und meldet eine mögliche Endlosschleife. - -## Übung: Verdoppeln bis 100 - -::: übung l10-verdoppeln -titel: Verdoppeln bis 100 -auftrag: Frage eine ganze Zahl ab. Verdopple sie so lange, bis sie mindestens 100 ist. - Gib am Ende aus, **wie oft** du verdoppelt hast. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: Die Schleife läuft, solange die Zahl noch zu klein ist. Zähle nebenbei mit, wie oft sie gelaufen ist. -tipp: Du brauchst eine zweite Variable `int schritte = 0`. Die Bedingung lautet `zahl < 100`. Im Inhalt verdoppelst du die Zahl mit `zahl = zahl * 2` und erhöhst `schritte` um 1. Ist die Zahl schon 100, läuft die Schleife gar nicht. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Schleifen im Schreibtischtest - -Bei einer Schleife wird der **Schreibtischtest** besonders wertvoll. Du siehst genau, wie oft die Schleife läuft und mit welchem Wert sie aufhört. Jede Prüfung der Bedingung bekommt eine eigene Zeile. - -::: diagramm l16-schreibtisch.visustruct -Eine While-Schleife zählt die Zahlen 1, 2 und 3 zusammen. -::: - -| Schritt | summe | i | Ausgabe | -|---|---|---|---| -| `int summe = 0` | 0 | – | | -| `int i = 1` | 0 | 1 | | -| `i <= 3` → wahr | 0 | 1 | | -| `summe = summe + i` | 1 | 1 | | -| `i = i + 1` | 1 | 2 | | -| `i <= 3` → wahr | 1 | 2 | | -| `summe = summe + i` | 3 | 2 | | -| `i = i + 1` | 3 | 3 | | -| `i <= 3` → wahr | 3 | 3 | | -| `summe = summe + i` | 6 | 3 | | -| `i = i + 1` | 6 | 4 | | -| `i <= 3` → falsch | 6 | 4 | | -| `output: summe` | 6 | 4 | 6 | - -Häufige Fehler in Schleifen sind: - -- Die Schleife läuft **einmal zu oft oder zu wenig**, z. B. `<` statt `<=`. -- Eine Variable wird in der Schleife **nicht verändert** oder in die falsche Richtung. Dann endet die Schleife nie. -- Eine Variable bekommt ihren Startwert **in** der Schleife statt davor und wird bei jedem Durchlauf zurückgesetzt. - -## Übung: Countdown reparieren - -::: übung l16-countdown -titel: Countdown reparieren -auftrag: Der Countdown soll vom Startwert herunterzählen und am Ende **Los!** ausgeben. Bei 3 also 3, 2, 1, Los! - Im Startdiagramm stecken zwei Fehler. Finde und behebe beide. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Los! -test: 1 -> 1 | Los! -test: 0 -> Los! -tipp: Es gibt zwei Fehler. Einer betrifft das Zählen, der andere die Ausgabe am Ende. -tipp: Starte die Simulation mit Startwert 3 und beobachte i unter Variablen. Wird i kleiner? Vergleiche außerdem die letzte Ausgabe genau mit dem Auftrag. -lösung: l16-countdown-loesung.visustruct -::: - -## Jetzt in Java - -Der Countdown vom Anfang dieser Lektion sieht in Java so aus: - -```java editor -int zahl = 3; -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -``` - -- Nach `while` steht die Bedingung in runden Klammern, wie bei `if`. -- Alles, was wiederholt wird, steht in geschweiften Klammern. Das ist der Rumpf der Schleife. -- Der Rumpf ist eingerückt. So erkennst du wie im Struktogramm, was zur Schleife gehört. - -::: übung l11-java-gerade -titel: Gerade Zahlen in Java -code: java -auftrag: Schreibe ein Java-Programm, das eine ganze Zahl `n` einliest und mit einer While-Schleife alle geraden Zahlen von 2 bis höchstens `n` ausgibt. Bei 7 also 2, 4 und 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: Du brauchst eine Zählvariable, die bei 2 beginnt. Die Schleife läuft, solange sie nicht größer als `n` ist. -tipp: `int zahl = 2;` vor der Schleife, dann `while (zahl <= n) { … }`. Im Rumpf gibst du `zahl` aus und erhöhst sie mit `zahl = zahl + 2;`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Lückentext: while - -::: lückentext -titel: Summe von 1 bis 10 -auftrag: Das Programm soll die Zahlen von 1 bis 10 zusammenzählen. Ergänze die fehlenden Wörter und Zeichen. -```java -int summe = 0; -int zahl = 1; -[[while]] (zahl [[<=]] 10) [[{]] - summe = summe + [[zahl]]; - [[zahl]] = zahl + 1; -} -System.out.println("Summe: " + summe); -``` -erklärung: Die Schleife läuft, solange `zahl` höchstens 10 ist. In jedem Durchlauf kommt `zahl` zur Summe dazu, danach wird `zahl` um 1 größer. Ohne diese Zeile wäre es eine Endlosschleife. Ausgabe: **Summe: 55** -::: - -## Fehler finden: Sparen - -::: übung l11-java-fehler -titel: Sparplan reparieren -code: java -auftrag: Ludwig spart jede Woche 5 Euro. Das Programm fragt nach dem Sparziel und soll ausgeben, nach wie vielen Wochen er es erreicht hat. Bei 50 Euro sind das 10 Wochen, das Programm sagt aber **Wochen: 11**. - Gehe den Code mit dem Einzelschritt durch und beobachte `geld` und `wochen`. Repariere den Fehler und klicke auf Prüfen. -start: l11-java-fehler-start.visustruct -test: 50 -> Wochen: 10 -test: 12 -> Wochen: 3 -test: 5 -> Wochen: 1 -test: 1 -> Wochen: 1 -braucht: whileSchleife -tipp: Wie viel Geld hat Ludwig, wenn die Schleife zum letzten Mal läuft? Muss er dann wirklich noch eine Woche sparen? -tipp: Gespart wird nur, solange das Ziel noch **nicht erreicht** ist: `while (geld < ziel)`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l11-java-laeuft-nicht -titel: Countdown reparieren -code: java -auftrag: Der Countdown soll **3**, **2**, **1** und dann **Start!** ausgeben. Er läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Start! -braucht: whileSchleife -tipp: Der Fehler steckt in der Schleife. Welche Variable soll ausgegeben werden, und wie heißt sie wirklich? -tipp: Gib `zahl` mit kleinem z aus: `System.out.println(zahl);` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Wie oft? - -::: spiel frage -titel: Spiel: Wie oft? -intro: Ludwig hat While-Schleifen geschrieben. Mach im Kopf den Schreibtischtest. Sechs Runden. - -runde: - int zahl = 3; - while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; - } - System.out.println("Start!"); -frage: Was wird ausgegeben? -- [x] 3\n2\n1\nStart! -- 3\n2\n1\n0\nStart! -- Start! -> Bei zahl gleich 0 ist `zahl > 0` falsch, die 0 wird also nicht mehr ausgegeben. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: Was wird ausgegeben? -- [x] nichts -- 10 -- 10 bis 4 -> Die Bedingung wird zuerst geprüft. 10 < 5 ist falsch, der Rumpf läuft kein einziges Mal. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: Was passiert? -- 1, 2 und 3 werden ausgegeben. -- [x] Die Schleife hört nie auf. -- Es wird nichts ausgegeben. -> i wird im Rumpf nie größer. Die Bedingung bleibt immer wahr: eine Endlosschleife. - -runde: - int zahl = 1; - while (zahl < 100) { - zahl = zahl * 2; - } - System.out.println(zahl); -frage: Was wird ausgegeben? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Erst bei 128 ist `zahl < 100` falsch. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: Was wird ausgegeben? -- 4 -- 5 -- [x] 6 -> i läuft 0, 2, 4, 6. Bei 6 ist `i < 5` falsch. Die 5 wird übersprungen. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: Wie oft läuft der Rumpf? -- 3-mal -- [x] 4-mal -- 10-mal -> Der Rumpf läuft bei i = 1, 4, 7 und 10. Danach ist i 13. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die While-Schleife ihre Bedingung vor jedem Durchlauf prüft. → Die Bedingung kommt zuerst -- Ich kann eine Zählvariable mit Startwert, Bedingung und Veränderung verwenden. → Eine Zählvariable -- Ich kann erkennen, wenn eine Schleife nie endet, und den Fehler beheben. → Vorsicht, Endlosschleife -- Ich kann eine Schleife im Schreibtischtest Durchlauf für Durchlauf verfolgen. → Schleifen im Schreibtischtest -- Ich kann eine While-Schleife in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet kopfgesteuert? -→ Die Bedingung kommt zuerst -- [x] Die Bedingung wird vor jedem Durchlauf geprüft. -- [ ] Die Bedingung wird nach jedem Durchlauf geprüft. -- [ ] Die Schleife läuft immer genau einmal. -> Der Kopf mit der Bedingung steht oben und wird zuerst ausgewertet. - -? `int i = 5`, danach eine While-Schleife mit der Bedingung `i < 3`. Wie oft läuft der Inhalt? -→ Die Bedingung kommt zuerst -- [x] Keinmal -- [ ] Einmal -- [ ] Dreimal -> Schon beim ersten Prüfen ist 5 < 3 falsch. - -? Warum endet eine Schleife mit der Bedingung `zahl > 0` nie, wenn im Inhalt nur `output: zahl` steht? -→ Vorsicht, Endlosschleife -- [ ] Weil output die Zahl vergrößert -- [x] Weil sich zahl nie ändert und die Bedingung wahr bleibt -- [ ] Weil die Bedingung falsch geschrieben ist -> Im Inhalt muss etwas passieren, das die Bedingung irgendwann falsch macht. - -? Wie nennt man Schleifen in Schulbüchern auch? -→ Die Bedingung kommt zuerst -- [x] Wiederholungsstrukturen -- [ ] Verzweigungen -- [ ] Unterprogramme -> Eine Schleife wiederholt Anweisungen. Darum heißt sie auch Wiederholungsstruktur. - -? Eine Schleife soll von 1 bis 5 zählen, läuft aber nur bis 4. Was ist der wahrscheinliche Fehler? -→ Eine Zählvariable -- [x] In der Bedingung steht i < 5 statt i <= 5. -- [ ] Die Schleife ist eine While-Schleife. -- [ ] Die Variable i heißt falsch. - -? Welche Zeile startet in Java eine While-Schleife richtig? -→ Jetzt in Java -- [ ] while zahl > 0 { -- [x] while (zahl > 0) { -- [ ] while (zahl > 0); -> Die Bedingung steht in runden Klammern, danach öffnet `{` den Rumpf. -::: diff --git a/web/lessons/de/12-do-while-schleife.md b/web/lessons/de/12-do-while-schleife.md deleted file mode 100644 index 2953f0b4..00000000 --- a/web/lessons/de/12-do-while-schleife.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -titel: Do-while-Schleife -bereich: Grundlagen -kurz: Erst ausführen, dann prüfen: Schleifen mit mindestens einem Durchlauf. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft -- erklären, warum der Inhalt mindestens einmal ausgeführt wird -- eine Eingabe so lange wiederholen lassen, bis sie gültig ist -- eine Do-while-Schleife in Java schreiben -::: - -## Erst machen, dann prüfen - -Die **Do-while-Schleife** ist **fußgesteuert**. Die Bedingung steht unten im Fuß des Kastens. Der Ablauf ist: - -1. Den Inhalt einmal ausführen. -2. Die Bedingung prüfen. -3. Ist sie wahr, wieder bei 1 anfangen. Ist sie falsch, geht es unter der Schleife weiter. - -::: grafik l11-vergleich.svg -Links prüft die While-Schleife zuerst, rechts führt die Do-while-Schleife zuerst aus. -::: - -Oben im Kasten steht `do`, unten im Fuß die Bedingung. Wenn du die Schleife anklickst, steht im Objektinspektor vor dem Textfeld das Wort `while`: Die Bedingung bedeutet „wiederhole, solange …“. - -> Die Bedingung sagt wie bei der While-Schleife, **wann wiederholt wird**. Solange sie wahr ist, läuft die Schleife weiter. - -::: angeberwissen -Python hat gar keine Do-while-Schleife. Wer dort „erst machen, dann prüfen“ will, schreibt `while True:` und verlässt die Schleife mit `break`. Java, C und JavaScript haben `do … while` dagegen schon seit Jahrzehnten. -::: - -## Mindestens ein Durchlauf - -Weil die Bedingung erst am Ende geprüft wird, läuft der Inhalt **immer mindestens einmal**. Das ist der wichtigste Unterschied zur While-Schleife. - -::: diagramm l11-einmal.visustruct -i ist 10 und damit nicht kleiner als 5. Trotzdem wird 10 einmal ausgegeben, denn geprüft wird erst danach. -::: - -Mit einer While-Schleife und derselben Bedingung `i < 5` würde hier gar nichts ausgegeben. - -## Typisch: Eingabe wiederholen - -Die Do-while-Schleife passt immer dann, wenn etwas zuerst passieren muss, bevor man es prüfen kann. Das beste Beispiel ist eine Eingabe: Erst wenn jemand etwas eingegeben hat, weißt du, ob es gültig ist. - -::: diagramm l11-eingabe-pruefen.visustruct -Die Frage kommt so lange wieder, bis eine Zahl von 1 bis 10 eingegeben wurde. Probiere in der Simulation zuerst 0 und dann 12 aus. -::: - -Die Bedingung beschreibt hier die **ungültige** Eingabe: `zahl < 1 || zahl > 10`. Solange die Eingabe ungültig ist, wird noch einmal gefragt. - -## Übung: Gültige Note - -::: übung l11-note -titel: Gültige Note -auftrag: Frage eine Note ab. Ist sie nicht zwischen 1 und 6, frage noch einmal. Ist sie gültig, gib sie aus, zum Beispiel **Deine Note: 3**. -test: 3 -> Deine Note: 3 -test: 0, 7, 2 -> Deine Note: 2 -test: 9, 6 -> Deine Note: 6 -test: -1, 1 -> Deine Note: 1 -braucht: doUntilSchleife -tipp: Die Note wird mindestens einmal abgefragt. Danach entscheidet die Bedingung, ob noch einmal gefragt wird. -tipp: Lege vor der Schleife `int note = 0` an. In die Schleife kommt die Eingabe `input: note "Note von 1 bis 6"`. Die Bedingung lautet `note < 1 || note > 6`. -lösung: l11-note-loesung.visustruct -::: - -## Übung: Summe bis 0 - -::: übung l11-summe -titel: Summe bis 0 -auftrag: Frage so lange Zahlen ab, bis eine 0 eingegeben wird. Gib danach die Summe aller eingegebenen Zahlen aus. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: Du brauchst eine Variable für die Summe, die vor der Schleife bei 0 beginnt. In jedem Durchlauf kommt die neue Zahl dazu. -tipp: Lege vor der Schleife `int summe = 0` und `int zahl = 0` an. In der Schleife fragst du `zahl` ab und rechnest `summe = summe + zahl`. Die 0 zu addieren schadet nicht. Die Schleife läuft weiter, solange `zahl != 0` ist. -tipp: Das Gerüst: `int summe = 0` und `int zahl = 0`, dann eine Do-while-Schleife mit der Bedingung `zahl != 0`. In ihr stehen die Eingabe von `zahl` und `summe = summe + zahl`. Unter der Schleife steht `output: summe`. -lösung: l11-summe-loesung.visustruct -::: - -## Jetzt in Java - -Die Schleife, die trotzdem einmal läuft, sieht in Java so aus: - -```java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -``` - -- Der Rumpf steht zwischen `do {` und `}`, die Bedingung kommt erst danach. -- Hinter `while (…)` steht hier ein **Semikolon**. Das ist anders als bei der While-Schleife. - -::: übung l12-java-verdoppeln -titel: Mindestens einmal verdoppeln -code: java -auftrag: Schreibe ein Java-Programm, das eine Startzahl einliest. Mit einer Do-while-Schleife wird sie verdoppelt und ausgegeben, solange sie kleiner als 100 ist. Auch eine Zahl ab 100 wird mindestens einmal verdoppelt: Aus 200 wird 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: Im Rumpf verdoppelst du die Zahl und gibst sie aus. Die Bedingung steht am Ende. -tipp: `do { zahl = zahl * 2; System.out.println(zahl); } while (zahl < 100);` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Lückentext: do-while - -::: lückentext -titel: Verdreifachen -auftrag: Die Zahl wird verdreifacht und ausgegeben, solange sie kleiner als 100 ist. Ergänze die fehlenden Wörter und Zeichen. -```java -int zahl = 1; -[[do]] { - zahl = zahl * 3; - System.out.println(zahl); -} [[while]] (zahl [[<]] 100)[[;]] -``` -erklärung: Der Rumpf steht zwischen `do {` und `}`, die Bedingung erst danach. Hinter `while (…)` steht bei der Do-while-Schleife ein Semikolon. Ausgabe: 3, 9, 27, 81 und 243. -::: - -## Fehler finden: Halbieren - -::: übung l12-java-fehler -titel: Halbieren reparieren -code: java -auftrag: Das Programm liest eine Startzahl ein und teilt sie mit einer Do-while-Schleife immer wieder ganzzahlig durch 2. Jedes Ergebnis wird ausgegeben, bis **0** erreicht ist. Bei 10 also 5, 2, 1 und 0. Die 0 fehlt aber. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: Welchen Wert hat `zahl`, nachdem die 1 ausgegeben wurde? Läuft die Schleife dann noch einmal? -tipp: Die Schleife soll weiterlaufen, solange die Zahl größer als 0 ist: `while (zahl > 0);` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l12-java-laeuft-nicht -titel: Verdoppeln reparieren -code: java -auftrag: Das Programm soll ab 1 immer verdoppeln, solange die Zahl kleiner als 20 ist: **1**, **2**, **4**, **8**, **16**. Es gibt nur die 1 aus und bleibt dann stehen. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: Die erste Runde läuft noch. Der Fehler kommt erst, wenn die Bedingung unten zum ersten Mal geprüft wird. -tipp: In der Bedingung muss `grenze` mit kleinem g stehen: `} while (zahl < grenze);` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Mindestens einmal? - -::: spiel frage -titel: Spiel: Mindestens einmal? -intro: While oder do-while? Ludwig zeigt dir Schleifen. Sag vorher, was passiert. Sechs Runden. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: Was wird ausgegeben? -- nichts -- [x] 10 -- 10 bis 4 -> Erst machen, dann prüfen: Der Rumpf läuft einmal, danach ist 11 < 5 falsch. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: Was wird ausgegeben? -- [x] nichts -- 10 -- 11 -> Bei der While-Schleife kommt die Bedingung zuerst. 10 < 5 ist falsch, also läuft gar nichts. - -runde: - int zahl = 200; - do { - zahl = zahl * 2; - } while (zahl < 100); - System.out.println(zahl); -frage: Was wird ausgegeben? -- 200 -- [x] 400 -- 800 -> Einmal wird auf jeden Fall verdoppelt. Danach ist 400 < 100 falsch. - -runde: - int zahl = 30; - do { - zahl = zahl * 2; - System.out.println(zahl); - } while (zahl < 100); -frage: Was wird ausgegeben? -- 60 -- [x] 60\n120 -- 60\n120\n240 -> Nach 60 ist 60 < 100 wahr, es geht weiter. Nach 120 ist Schluss. - -runde: -frage: Welche Zeile beendet eine Do-while-Schleife in Java richtig? -- [x] `} while (i < 5);` -- `} while (i < 5)` -- `while (i < 5) {` -> Hinter dem `while (…)` der Do-while-Schleife steht ein Semikolon. - -runde: -frage: Wofür nimmt man typischerweise eine Do-while-Schleife? -- [x] eine Eingabe wiederholen, bis sie gültig ist -- von 1 bis 10 zählen -- zwei Werte tauschen -> Gefragt wird mindestens einmal. Erst danach weiß man, ob die Eingabe gültig war. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft. → Erst machen, dann prüfen -- Ich kann erklären, warum der Inhalt mindestens einmal ausgeführt wird. → Mindestens ein Durchlauf -- Ich kann eine Eingabe so lange wiederholen lassen, bis sie gültig ist. → Typisch: Eingabe wiederholen -- Ich kann eine Do-while-Schleife in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet fußgesteuert? -→ Erst machen, dann prüfen -- [ ] Die Bedingung wird vor jedem Durchlauf geprüft. -- [x] Die Bedingung wird nach jedem Durchlauf geprüft. -- [ ] Die Schleife hat keine Bedingung. -> Die Bedingung steht im Fuß, also unten, und wird nach dem Inhalt ausgewertet. - -? Die Bedingung einer Do-while-Schleife ist von Anfang an falsch. Wie oft läuft der Inhalt? -→ Mindestens ein Durchlauf -- [ ] Keinmal -- [x] Genau einmal -- [ ] Endlos -> Der Inhalt läuft zuerst. Erst danach merkt die Schleife, dass sie aufhören soll. - -? Du willst eine Zahl von 1 bis 10 abfragen. Welche Bedingung gehört in den Fuß? -→ Typisch: Eingabe wiederholen -- [ ] `zahl >= 1 && zahl <= 10` -- [x] `zahl < 1 || zahl > 10` -- [ ] `zahl == 10` -> Wiederholt wird, solange die Eingabe ungültig ist, also kleiner als 1 oder größer als 10. - -? Wann ist eine Do-while-Schleife besser als eine While-Schleife? -→ Mindestens ein Durchlauf -- [x] Wenn der Inhalt auf jeden Fall einmal laufen muss, z. B. bei einer Eingabe -- [ ] Wenn die Schleife vielleicht gar nicht laufen soll -- [ ] Wenn man genau bis 10 zählen will - -? Was steht in Java am Ende einer Do-while-Schleife? -→ Jetzt in Java -- [ ] } while (bedingung) { -- [x] } while (bedingung); -- [ ] } until (bedingung); -> Nach der Bedingung kommt ein Semikolon. -::: diff --git a/web/lessons/de/13-for-schleife.md b/web/lessons/de/13-for-schleife.md deleted file mode 100644 index 231e7575..00000000 --- a/web/lessons/de/13-for-schleife.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -titel: For-Schleife -bereich: Grundlagen -kurz: Mit Start, Bedingung und Schritt eine feste Anzahl von Durchläufen zählen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die drei Teile Start, Bedingung und Schritt der For-Schleife benennen -- erklären, in welcher Reihenfolge die drei Teile ausgeführt werden -- mit einer For-Schleife vorwärts, rückwärts und in größeren Schritten zählen -- eine For-Schleife in Java schreiben und ihre drei Felder erklären -::: - -## Zählen mit festem Plan - -In der While-Schleife hast du schon mitgezählt: Startwert vor der Schleife, Bedingung im Kopf, Veränderung im Inhalt. Die **For-Schleife** schreibt diese drei Teile zusammen in den Kopf. So siehst du auf einen Blick, wie gezählt wird. - -::: grafik l12-while-zu-for.svg animiert -Aus der While-Schleife wird eine For-Schleife: Start, Bedingung und Schritt wandern zusammen in den Kopf. -::: - -Wenn du eine For-Schleife anklickst, hat sie rechts drei Felder: - -- **Start:** `int i = 1` legt die Zählvariable an. -- **Bedingung:** `i <= 5` sagt, wie lange gezählt wird. -- **Schritt:** `i++` erhöht `i` nach jedem Durchlauf um 1. - -::: diagramm l12-eins-bis-fuenf.visustruct -Eine For-Schleife, die die Zahlen von 1 bis 5 ausgibt. -::: - -::: angeberwissen -Warum heißt die Zählvariable fast immer `i`? In der Mathematik steht `i` schon lange für einen Index. Und in FORTRAN, einer der ersten Programmiersprachen von 1957, waren alle Variablen, die mit I bis N anfingen, automatisch ganze Zahlen. Programmierer haben sich das bis heute nicht abgewöhnt. -::: - -## In welcher Reihenfolge? - -Die drei Teile kommen zu unterschiedlichen Zeiten dran: - -1. **Start** wird genau einmal ausgeführt, ganz am Anfang. -2. **Bedingung** wird vor jedem Durchlauf geprüft. Ist sie falsch, ist die Schleife fertig. -3. Nach dem Inhalt kommt der **Schritt**, dann wieder die Bedingung. - -::: grafik l12-for-ablauf.svg -Der Start kommt nur einmal dran. Danach wiederholen sich Bedingung, Inhalt und Schritt. -::: - -Die For-Schleife ist also kopfgesteuert wie die While-Schleife. Gehe das Beispiel in der Simulation mit **Schritt** durch und beobachte, wie sich `i` verändert. - -> Nimm die For-Schleife, wenn du schon vorher weißt, wie oft etwas passieren soll. Hängt das Ende von etwas ab, das erst während der Schleife passiert, zum Beispiel einer Eingabe, ist die While- oder Do-while-Schleife besser. - -## Beispiel: Summe von 1 bis n - -Eine For-Schleife eignet sich gut, um Zahlen aufzusummieren. Vor der Schleife steht `int summe = 0`. In jedem Durchlauf kommt die aktuelle Zahl `i` dazu. - -::: diagramm l12-summe.visustruct -Bei n = 4 rechnet die Schleife 0 + 1 + 2 + 3 + 4. Am Ende wird 10 ausgegeben. -::: - -## Andere Schritte - -Der Schritt muss nicht immer `i++` sein: - -- `i--` zählt rückwärts, zum Beispiel mit Start `int i = 10` und Bedingung `i >= 1`. -- `i = i + 2` springt in Zweierschritten, zum Beispiel 0, 2, 4, 6. - -Achte darauf, dass Start, Bedingung und Schritt zusammenpassen. Mit Start `int i = 1`, Bedingung `i >= 1` und Schritt `i++` wird die Bedingung nie falsch. - -::: übung l12-countdown -titel: Countdown -auftrag: Frage eine ganze Zahl `n` ab. Zähle mit einer For-Schleife von `n` rückwärts bis 1 und gib jede Zahl aus. Gib danach **Los!** aus. -test: 3 -> 3 | 2 | 1 | Los! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Los! -test: 1 -> 1 | Los! -braucht: forSchleife -tipp: Diesmal zählt die For-Schleife rückwärts. Überlege, wo sie beginnt, wann sie aufhört und wie sie zählt. -tipp: Start ist `int i = n`, die Bedingung `i >= 1` und der Schritt `i--`. Die Ausgabe Los! steht unter der Schleife, nicht darin. -lösung: l12-countdown-loesung.visustruct -::: - -## Übung: Einmaleins - -::: übung l12-einmaleins -titel: Einmaleins -auftrag: Frage eine Zahl ab und gib ihre Einmaleins-Reihe von 1 bis 10 aus. Jede Zeile sieht so aus: **3 x 1 = 3**, **3 x 2 = 6** und so weiter. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: Die Schleife zählt von 1 bis 10. In jedem Durchlauf gibst du eine Zeile der Reihe aus. -tipp: Rechne im Inhalt zuerst `int produkt = zahl * i`. Danach gibst du `zahl + " x " + i + " = " + produkt` aus. Denk an die Leerzeichen innerhalb der Anführungszeichen. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Übung: Summe reparieren - -::: übung l16-summe -titel: Summe reparieren -auftrag: Das Struktogramm soll alle Zahlen von 1 bis n zusammenzählen. Bei n = 4 soll also 1 + 2 + 3 + 4 = **10** herauskommen. Es gibt aber 6 aus. - Mache einen Schreibtischtest oder nutze **Schritt** und repariere den Fehler. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Gehe die Schleife mit n = 4 im Kopf oder mit **Schritt** durch. Welche Zahlen werden wirklich addiert? -tipp: Schau, mit welchem Wert von i die Schleife zuletzt läuft. Bei n = 4 muss auch i = 4 noch dazugezählt werden. -lösung: l16-summe-loesung.visustruct -::: - -## Jetzt in Java - -Die Summe von 1 bis n sieht in Java so aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Bis zu welcher Zahl?"); -int n = scanner.nextInt(); -int summe = 0; -for (int i = 1; i <= n; i++) { - summe = summe + i; -} -System.out.println(summe); -``` - -In den runden Klammern nach `for` stehen die drei Felder der For-Schleife, getrennt durch Semikolons: - -| Feld | In Java | Bedeutung | -|---|---|---| -| Startwert | `int i = 1` | `i` beginnt bei 1. | -| Bedingung | `i <= n` | Die Schleife läuft, solange das gilt. | -| Schritt | `i++` | Nach jedem Durchlauf wird `i` um 1 größer. | - -`i++` ist die Kurzform von `i = i + 1`. Rückwärts zählst du mit `i--`. - -::: übung l13-java-quadrate -titel: Quadratzahlen in Java -code: java -auftrag: Schreibe ein Java-Programm, das eine ganze Zahl `n` einliest und mit einer For-Schleife die Quadratzahlen von 1 bis `n` ausgibt, jede in einer eigenen Zeile. Bei 3 also 1, 4 und 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: Die Schleife zählt `i` von 1 bis `n`. Die Quadratzahl von `i` ist `i * i`. -tipp: `for (int i = 1; i <= n; i++) { … }` und im Rumpf `System.out.println(i * i);`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Lückentext: for - -::: lückentext -titel: Hoch- und runterzählen -auftrag: Die erste Schleife zählt die Zahlen von 1 bis 5 zusammen, die zweite zählt von 10 bis 0 rückwärts. Ergänze die fehlenden Wörter und Zeichen. -```java -int summe = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -``` -erklärung: In den runden Klammern stehen Startwert, Bedingung und Schritt, getrennt durch Semikolons. `i++` zählt hoch, `i--` zählt runter. Beim Rückwärtszählen bis 0 heißt die Bedingung `i >= 0`. -::: - -## Fehler finden: Einmaleins - -::: übung l13-java-fehler -titel: Einmaleins reparieren -code: java -auftrag: Das Programm fragt nach einer Reihe und soll sie vom 1-fachen bis zum 10-fachen ausgeben, bei 7 also 7, 14, … bis 70. Die 70 fehlt aber. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: Wie oft läuft die Schleife? Zähle nach, welche Werte `i` annimmt. -tipp: `i` soll auch den Wert 10 annehmen: `i <= 10` statt `i < 10`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l13-java-laeuft-nicht -titel: Treppe reparieren -code: java -auftrag: Ludwig klettert 5 Stufen hoch, jede ist 20 cm hoch. Das Programm soll **Höhe: 100 cm** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Höhe: 100 cm -braucht: forSchleife -tipp: Mit `hoehe` wird gerechnet, aber wo wird die Variable angelegt? Und mit welchem Wert startet Ludwig? -tipp: Lege `hoehe` vor der Schleife an: `int hoehe = 0;` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was das Programm tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sind die Teile einer Zeile umrandet, tippst du genau auf den falschen Teil, zum Beispiel nur auf `i++`. Acht Runden, je 25 Sekunden. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Soll 1, 2 und 3 ausgeben. -fehler: 2 -besser: System.out.println(i); -> Am Ende der Anweisung fehlt das Semikolon. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Soll 1 bis 5 ausgeben. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> Die drei Felder der For-Schleife werden mit Semikolons getrennt, nicht mit Kommas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Soll von 10 bis 1 rückwärts zählen. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> Mit `i++` wird `i` immer größer, die Bedingung `i >= 1` bleibt wahr: eine Endlosschleife. Rückwärts zählst du mit `i--`. - -runde: - int summe = 0; - for (int i = 1; i <= 4; i++) { - summe = i; - } - System.out.println(summe); -frage: Soll die Summe 1 + 2 + 3 + 4 = 10 ausgeben. -fehler: 3 -stelle: i -besser: summe = summe + i; -> `summe = i` überschreibt die Summe in jedem Durchlauf. Am Ende steht dort nur 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Määäh"); - } -frage: Soll genau fünfmal Määäh ausgeben. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> Von 0 bis einschließlich 5 sind es sechs Durchläufe. Wer bei 0 anfängt, nimmt `<`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Soll 1, 2 und 3 ausgeben. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> Java unterscheidet Groß- und Kleinbuchstaben: `System` schreibt man mit großem S. - -runde: - int zahl = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(zahl + " x " + i + " = " + zahl * zahl); - } -frage: Soll die Siebenerreihe ausgeben: 7 x 1 = 7, 7 x 2 = 14 und so weiter. -fehler: 3 -stelle: zahl * zahl -besser: System.out.println(zahl + " x " + i + " = " + zahl * i); -> `zahl * zahl` ist immer 49. Gerechnet werden muss `zahl * i`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Soll die geraden Zahlen 2, 4, 6, 8 und 10 ausgeben. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> `i + 2` rechnet nur, speichert aber nichts. Damit `i` wirklich wächst, braucht es `i = i + 2`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die drei Teile Start, Bedingung und Schritt der For-Schleife benennen. → Zählen mit festem Plan -- Ich kann erklären, in welcher Reihenfolge die drei Teile ausgeführt werden. → In welcher Reihenfolge? -- Ich kann mit einer For-Schleife vorwärts, rückwärts und in größeren Schritten zählen. → Andere Schritte -- Ich kann eine For-Schleife in Java schreiben und ihre drei Felder erklären. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie oft wird der Start einer For-Schleife ausgeführt? -→ In welcher Reihenfolge? -- [x] Genau einmal -- [ ] Vor jedem Durchlauf -- [ ] Nach jedem Durchlauf -> Der Start legt die Zählvariable einmal am Anfang an. - -? Start `int i = 0`, Bedingung `i < 3`, Schritt `i++`. Welche Werte hat i im Inhalt? -→ Zählen mit festem Plan -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> Bei i = 3 ist die Bedingung falsch, deshalb läuft der Inhalt dann nicht mehr. - -? Start `int i = 2`, Bedingung `i <= 10`, Schritt `i = i + 2`. Wie oft läuft der Inhalt? -→ Andere Schritte -- [ ] 4-mal -- [x] 5-mal -- [ ] 10-mal -> i nimmt die Werte 2, 4, 6, 8 und 10 an. - -? Wann passt eine For-Schleife am besten? -→ Zählen mit festem Plan -- [x] Wenn vorher feststeht, wie oft wiederholt wird -- [ ] Wenn so lange gefragt wird, bis eine Eingabe gültig ist -- [ ] Wenn nur ein einziger Wert geprüft wird - -? Was bedeutet `i++` in einer For-Schleife? -→ Zählen mit festem Plan -- [ ] i wird verdoppelt. -- [x] i wird um 1 größer. -- [ ] i wird ausgegeben. -> `i++` ist die Kurzform von `i = i + 1`. -::: diff --git a/web/lessons/de/14-verschachteln.md b/web/lessons/de/14-verschachteln.md deleted file mode 100644 index 62b0050d..00000000 --- a/web/lessons/de/14-verschachteln.md +++ /dev/null @@ -1,305 +0,0 @@ ---- -titel: Verschachteln -bereich: Grundlagen -kurz: Verzweigungen in Schleifen und Schleifen in Schleifen kombinieren. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Verzweigung in eine Schleife setzen -- eine Schleife in eine andere Schleife setzen -- erklären, dass die innere Schleife bei jedem Durchlauf der äußeren komplett durchläuft -- verschachtelte Schleifen und Verzweigungen in Java schreiben -::: - -## Bausteine ineinander - -Du kennst jetzt alle wichtigen Bausteine: Sequenz, Verzweigung, Fallauswahl und drei Arten von Schleifen. Richtig stark werden sie erst zusammen. - -Jeder Zweig und jeder Schleifeninhalt ist selbst wieder eine Sequenz. Dort darfst du jedes Element einsetzen, auch weitere Verzweigungen und Schleifen. Das nennt man **Verschachteln**. - -> Ziehe dazu ein Element einfach auf den Platzhalter ø im Inneren einer Schleife oder eines Zweigs. - -## Verzweigung in der Schleife - -Die Schleife geht alle Zahlen durch, die Verzweigung entscheidet bei jeder einzelnen Zahl, ob etwas passiert. So kannst du zum Beispiel zählen, wie viele gerade Zahlen es von 1 bis n gibt. - -::: diagramm l13-gerade-zaehlen.visustruct -Bei jedem Durchlauf prüft die Verzweigung mit i % 2 == 0, ob i gerade ist. Nur dann wird anzahl um 1 erhöht. Der false-Zweig bleibt leer. -::: - -Bei n = 7 sind das die Zahlen 2, 4 und 6, also wird 3 ausgegeben. - -## Übung: Gerade Eingaben zählen - -::: übung l13-gerade-eingaben -titel: Gerade Eingaben zählen -auftrag: Frage nacheinander genau **fünf** ganze Zahlen ab. Gib danach aus, wie viele davon gerade waren. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Fünfmal dasselbe tun heißt: Schleife. In jedem Durchlauf wird eine Zahl eingelesen und geprüft. -tipp: Eine For-Schleife von 1 bis 5. Im Inhalt steht zuerst die Eingabe `input: int zahl "Zahl eingeben"`, darunter eine Verzweigung mit `zahl % 2 == 0`. Im true-Zweig erhöhst du `anzahl` um 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## Schleife in der Schleife - -Steht eine Schleife in einer anderen, läuft die **innere Schleife bei jedem Durchlauf der äußeren komplett durch**. Damit kannst du Muster aus Zeilen und Spalten bauen. - -::: diagramm l13-dreieck.visustruct -Die äußere Schleife zählt die Zeilen. Die innere hängt so viele Sterne an den Text, wie die Zeilennummer angibt. Bei n = 3 entsteht ein Dreieck aus 1, 2 und 3 Sternen. -::: - -Mit `String text = ""` beginnt jede Zeile leer. `text = text + "*"` hängt einen Stern an. Erst wenn die Zeile fertig ist, wird sie ausgegeben. - -Erfahrene Programmierer nennen die Zählvariablen kurz `i` und `j`: außen `i`, innen `j`. Dann sieht es so aus, hier mit einer kleinen Einmaleins-Tabelle: - -::: grafik l13-verschachtelt-tabelle.svg animiert -Die äußere Schleife zählt mit `i` die Zeilen, die innere mit `j` die Zahlen in einer Zeile. Bei jeder neuen Zeile fängt `j` wieder bei 1 an. -::: - -## Übung: Rechteck - -::: übung l13-rechteck -titel: Rechteck -auftrag: Frage eine Breite und eine Höhe ab. Gib ein Rechteck aus `#` aus: so viele Zeilen wie die Höhe, und in jeder Zeile so viele `#` wie die Breite. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: Du brauchst zwei Schleifen ineinander: eine für die Zeilen und darin eine für die Zeichen einer Zeile. -tipp: Baue es wie das Dreieck. Der Unterschied: Die innere Schleife zählt immer bis `breite`, nicht bis zur Zeilennummer. Die äußere Schleife zählt bis `hoehe`. -tipp: Das Gerüst: Eingaben `breite` und `hoehe`. Äußere For-Schleife `int zeile = 1`, `zeile <= hoehe`, `zeile++`. Darin zuerst `String text = ""`, dann die innere For-Schleife `int spalte = 1`, `spalte <= breite`, `spalte++` mit `text = text + "#"`, und unter der inneren Schleife `output: text`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Den Überblick behalten - -Bei verschachtelten Diagrammen hilft dir die Darstellung: Was weiter innen steht, ist eingerückt und von seiner Schleife oder seinem Zweig umschlossen. - -- Lies von außen nach innen: Was wiederholt sich? Was wird bei jedem Durchlauf entschieden? -- Gib Zählvariablen sprechende Namen wie `zeile` und `spalte`. Zwei Schleifen dürfen nicht dieselbe Zählvariable verändern. -- Gehe ein Beispiel in der Simulation mit **Schritt** durch und beobachte die Variablen. - -::: angeberwissen -Genau dafür wurde das Struktogramm erfunden. Shneiderman fragte sich: Wenn man auf GOTO verzichten soll, warum dann nicht gleich auf die Pfeile im Flussdiagramm? Ergebnis: kein Pfeil-Spaghetti, und jeder Block bleibt brav in seinem Gatter. -::: - -## Jetzt in Java - -Das Zählen der geraden Zahlen sieht in Java so aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Bis zu welcher Zahl?"); -int n = scanner.nextInt(); -int anzahl = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - anzahl++; - } // Ende if -} // Ende for -System.out.println(anzahl); -``` - -Was im Struktogramm ineinander steckt, steht in Java in geschweiften Klammern ineinander. Jede Ebene ist vier Leerzeichen weiter eingerückt. Ist dein Code durcheinander, hilft der Knopf **Einrücken** oben im Bereich Code. - -Bei vielen schließenden Klammern `}` verliert man leicht den Überblick: Welche Klammer gehört zu welchem Block? Ein Kommentar dahinter sagt es dir, z. B. `} // Ende if` und `} // Ende for`. Das hilft dir auch, wenn du später weiterarbeitest. - -`anzahl++` ist die Kurzform von `anzahl = anzahl + 1`. - -::: übung l14-java-treppe -titel: Treppe aus Sternen -code: java -auftrag: Schreibe ein Java-Programm, das die Anzahl der Stufen einliest und eine Treppe aus Sternchen ausgibt. Bei 3 Stufen also `*`, `**` und `***`, jede Stufe in einer eigenen Zeile. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: Eine äußere Schleife zählt die Stufen. In jeder Stufe baut eine innere Schleife einen Text aus Sternchen zusammen. -tipp: In der äußeren Schleife: `String zeile = "";`, dann `for (int j = 1; j <= i; j++) { zeile = zeile + "*"; }` und danach `System.out.println(zeile);`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Lückentext: verschachteln - -::: lückentext -titel: Rechteck aus Sternen -auftrag: Das Programm gibt ein Rechteck aus 3 Zeilen mit je 4 Sternchen aus. Ergänze die fehlenden Wörter und Zeichen. -```java -for (int zeile = 1; zeile <= 3; zeile++) { - String text = [[""]]; - [[for]] (int spalte = 1; spalte [[<=]] 4; [[spalte++ | spalte += 1 | spalte = spalte + 1]]) { - text = text [[+]] "*"; - } - System.out.[[println]](text); -[[}]] -``` -erklärung: Die innere Schleife baut eine Zeile aus 4 Sternchen, die äußere wiederholt das dreimal. Jede Zeile beginnt mit einem leeren Text `""`. Die letzte Klammer schließt die äußere Schleife. -::: - -## Fehler finden: durch 3 teilbar - -::: übung l14-java-fehler -titel: Zählen reparieren -code: java -auftrag: Das Programm soll zählen, wie viele Zahlen von 1 bis `n` durch 3 teilbar sind, und **einmal** das Ergebnis ausgeben. Bei 10 also nur **Anzahl: 3**. Es gibt aber viel zu viele Zeilen aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l14-java-fehler-start.visustruct -test: 10 -> Anzahl: 3 -test: 3 -> Anzahl: 1 -test: 2 -> Anzahl: 0 -braucht: forSchleife, verzweigung -tipp: Achte auf die geschweiften Klammern: Steht die Ausgabe in der Schleife oder dahinter? -tipp: Verschiebe die Zeile `System.out.println("Anzahl: " + anzahl);` hinter die schließende Klammer der Schleife. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l14-java-laeuft-nicht -titel: Gerade Zahlen reparieren -code: java -auftrag: Das Programm soll die geraden Zahlen von 1 bis 10 ausgeben: **2**, **4**, **6**, **8**, **10**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: Der Fehler steckt ganz innen, in der Ausgabe. Wie heißt die Zählvariable der Schleife? -tipp: Die Zählvariable heißt `i` mit kleinem i: `System.out.println(i);` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Innen und außen - -::: spiel frage -titel: Spiel: Innen und außen -intro: Ludwig hat Bausteine ineinander gesteckt. Lies von außen nach innen und sag vorher, was passiert. Sechs Runden. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: Wie viele Sterne werden ausgegeben? -- 3 -- 5 -- [x] 6 -> Die innere Schleife läuft bei jedem der 3 Durchläufe der äußeren komplett durch: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Welche Zeile wird als vierte ausgegeben? -- 1 4 -- [x] 2 1 -- 2 2 -> Erst läuft j für i = 1 durch: 1 1, 1 2, 1 3. Danach beginnt j wieder bei 1, jetzt mit i = 2. - -runde: - int anzahl = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - anzahl++; - } - } - System.out.println(anzahl); -frage: Was wird ausgegeben? -- [x] 3 -- 10 -- 4 -> Die Verzweigung zählt nur 3, 6 und 9 mit. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("gerade"); - } else { - System.out.println(i); - } - } -frage: Was wird ausgegeben? -- 1\n2\n3\n4 -- [x] 1\ngerade\n3\ngerade -- gerade\ngerade -> Bei jeder Zahl entscheidet die Verzweigung neu: ungerade Zahlen selbst, gerade als Wort. - -runde: - for (int i = 1; i <= 3; i++) { - String zeile = ""; - for (int j = 1; j <= i; j++) { - zeile = zeile + "*"; - } - System.out.println(zeile); - } -frage: Was wird ausgegeben? -- ***\n***\n*** -- [x] *\n**\n*** -- ***\n**\n* -> Die innere Schleife läuft bis i. Darum wird jede Zeile einen Stern länger. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: Was passiert? -- 1 bis 3 wird dreimal ausgegeben. -- 1 bis 3 wird einmal ausgegeben. -- [x] Java meldet einen Fehler. -> Zwei Schleifen ineinander brauchen zwei verschiedene Zählvariablen, zum Beispiel `zeile` und `spalte`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Verzweigung in eine Schleife setzen. → Verzweigung in der Schleife -- Ich kann eine Schleife in eine andere Schleife setzen. → Schleife in der Schleife -- Ich kann erklären, dass die innere Schleife bei jedem Durchlauf der äußeren komplett durchläuft. → Schleife in der Schleife -- Ich kann verschachtelte Schleifen und Verzweigungen in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Die äußere Schleife läuft 3-mal, die innere 4-mal. Wie oft wird der Inhalt der inneren Schleife insgesamt ausgeführt? -→ Schleife in der Schleife -- [ ] 7-mal -- [x] 12-mal -- [ ] 4-mal -> Bei jedem der 3 äußeren Durchläufe läuft die innere Schleife 4-mal, also 3 · 4 = 12. - -? Woran erkennst du, ob eine Zahl gerade ist? -→ Verzweigung in der Schleife -- [ ] `zahl / 2 == 0` -- [x] `zahl % 2 == 0` -- [ ] `zahl == 2` -> % liefert den Rest beim Teilen. Bei geraden Zahlen ist der Rest beim Teilen durch 2 null. - -? In einer For-Schleife steht eine Verzweigung. Wie oft wird die Bedingung der Verzweigung geprüft? -→ Verzweigung in der Schleife -- [ ] Einmal -- [x] Einmal pro Durchlauf der Schleife -- [ ] Nur wenn sie wahr ist -> Die Verzweigung gehört zum Inhalt und kommt bei jedem Durchlauf dran. - -? Woran erkennst du in Java, was zu einer Schleife gehört? -→ Jetzt in Java -- [ ] An der Farbe -- [x] An den geschweiften Klammern und der Einrückung -- [ ] An den Semikolons -::: diff --git a/web/lessons/de/15-arrays.md b/web/lessons/de/15-arrays.md deleted file mode 100644 index ac126a3f..00000000 --- a/web/lessons/de/15-arrays.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -titel: Arrays -bereich: Grundlagen -kurz: Viele Werte unter einem Namen speichern, mit dem Index darauf zugreifen und sie mit einer Schleife durchlaufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Array mit Werten oder mit `new int[5]` anlegen -- erklären, dass der Index bei 0 beginnt und der letzte Index `length - 1` ist -- ein Array mit einer For-Schleife durchlaufen und füllen -- die Summe und das Maximum eines Arrays berechnen -- in Java ein Array anlegen und mit einer Schleife durchlaufen -::: - -## Viele Werte unter einem Namen - -Stell dir vor, du willst die Punkte von fünf Tests speichern. Mit fünf Variablen `punkte1` bis `punkte5` wird das schnell unübersichtlich. Ein **Array** ist eine Reihe von Fächern, die alle denselben Namen und denselben Datentyp haben. - -Du legst ein Array mit einer Anweisung an. Hinter den Datentyp schreibst du eckige Klammern `[]`: - -``` -int[] zahlen = {2, 4, 6, 8, 10} -String[] namen = {"Ada", "Ben", "Cem"} -``` - -Die Werte in den geschweiften Klammern stehen gleich in den Fächern. Du kannst auch ein leeres Array mit einer festen Anzahl von Fächern anlegen: - -``` -int[] punkte = new int[5] -String[] namen = new String[3] -``` - -Bei `new int[5]` stehen in allen fünf Fächern zuerst Nullen, bei `new String[3]` leere Texte. - -## Der Index beginnt bei 0 - -Jedes Fach hat eine Nummer, den **Index**. Gezählt wird ab **0**. Bei `int[] zahlen = {2, 4, 6, 8, 10}` gilt also: - -- `zahlen[0]` ist 2 -- `zahlen[1]` ist 4 -- `zahlen[4]` ist 10 - -::: grafik l14-array.svg -Fünf Fächer mit den Indizes 0 bis 4. Der Index steht über dem Fach, der Wert darin. -::: - -**Warum beginnt man bei 0?** In der Informatik ist der Index kein „wievieltes Fach“, sondern der **Abstand vom Anfang**. Das erste Fach liegt direkt am Anfang, also 0 Schritte entfernt. Das zweite liegt 1 Schritt entfernt, das dritte 2 Schritte. Du kennst das aus dem Aufzug: Das Erdgeschoss ist die **0**, der erste Stock liegt eine Etage darüber. - -Daraus folgt eine Regel, die du dir merken solltest: Bei 5 Fächern ist der **letzte Index 4**, also immer eins weniger als die Anzahl. - -::: angeberwissen -Dass ab 0 gezählt wird, ist ziemlich alt. Schon die Programmiersprache C von 1972 rechnet so: Der Computer kennt die Adresse des ersten Fachs und rechnet für `zahlen[i]` einfach „Anfang plus i Fächer weiter“. Bei i = 0 bleibt er am Anfang stehen, das spart eine Rechnung. Java, Python und JavaScript haben das übernommen. Ich zähle meine Hörner übrigens auch ab 0: Horn 0 links, Horn 1 rechts. -::: - -Mit dem Index liest du einen Wert oder schreibst einen neuen hinein: - -``` -output: zahlen[0] -zahlen[1] = 99 -``` - -> Ein Array mit 5 Fächern hat die Indizes 0 bis 4. `zahlen[5]` gibt es nicht. Die Simulation meldet dann einen ungültigen Ausdruck. - -## Die Länge eines Arrays - -Wie viele Fächer ein Array hat, verrät `zahlen.length`. Bei fünf Werten ist das 5. Der **letzte** Index ist deshalb immer `zahlen.length - 1`: - -``` -output: zahlen.length -output: zahlen[zahlen.length - 1] -``` - -Die zweite Zeile gibt den letzten Wert aus, egal wie lang das Array ist. VisuStruct versteht auch die Schreibweise `zahlen.length()`. - -::: übung l14-erster-letzter -titel: Erster und letzter Wert -auftrag: Lege das Array `int[] werte = {4, 8, 15, 16, 23, 42}` an. Gib danach in drei Zeilen aus: den ersten Wert, den letzten Wert und die Anzahl der Werte. - Für den letzten Wert sollst du die Länge benutzen und nicht einfach den Index 5 hinschreiben. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Gezählt wird ab 0. Das erste Fach hat also den Index 0. Und wie heißt der Index des letzten Fachs? -tipp: Der erste Wert ist `werte[0]`. Der letzte ist `werte[werte.length - 1]`. Die Anzahl ist `werte.length`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Ein Array mit der Schleife durchlaufen - -Seine Stärke zeigt ein Array zusammen mit einer **For-Schleife**. Die Zählvariable `i` läuft von 0 bis zum letzten Index und dient als Index: - -``` -int i = 0 -i < zahlen.length -i++ -``` - -Im ersten Durchlauf ist `i` gleich 0, im letzten gleich 4. Weil die Bedingung `i < zahlen.length` lautet, passt die Schleife zu jeder Länge. - -::: diagramm l14-array-durchlaufen.visustruct -Die For-Schleife gibt alle fünf Werte nacheinander aus. Schau in der Simulation unter Variablen zu, wie i von 0 bis 4 zählt. -::: - -So füllst du ein leeres Array auch mit Eingaben. Als Ziel der Eingabe schreibst du das Fach `namen[i]`: - -::: diagramm l14-namen-eingeben.visustruct -Drei Namen werden abgefragt und in die Fächer 0, 1 und 2 geschrieben. -::: - -::: übung l14-rueckwaerts -titel: Rückwärts ausgeben -auftrag: Lege ein Array für 4 ganze Zahlen an und lies die 4 Zahlen mit einer Schleife ein. Gib sie danach in **umgekehrter** Reihenfolge aus, jede in einer eigenen Zeile. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: Du brauchst zwei Schleifen: eine zum Einlesen von vorn nach hinten und eine zum Ausgeben von hinten nach vorn. -tipp: Mit `int[] zahlen = new int[4]` legst du das Array an. Die Eingabe lautet `input: zahlen[i] "Zahl eingeben"`. Für die zweite Schleife beginnst du bei `int i = zahlen.length - 1`, prüfst `i >= 0` und zählst mit `i--` herunter. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr** aus Wien war in den 1940er-Jahren ein berühmter Hollywood-Filmstar. In ihrer Freizeit erfand sie gern. 1942 bekam sie zusammen mit dem Komponisten George Antheil ein Patent auf eine Funkverbindung, die der Feind nicht stören sollte: Sender und Empfänger springen gemeinsam durch eine Liste von **88 Frequenzen**, so viele, wie ein Klavier Tasten hat. Die Liste stand auf einem Lochstreifen wie bei einem selbstspielenden Klavier. Beide gehen dieselbe Liste in derselben Reihenfolge durch, so wie deine Schleife mit `i` durch ein Array. Wer nur eine Frequenz abhört oder stört, bekommt fast nichts mit. - -Dieses **Frequenzsprungverfahren** (englisch *frequency hopping*) steckt heute zum Beispiel in Bluetooth. Ganz allein erfunden hat sie es nicht, es gab schon frühere Ideen. Ihr Patent gilt aber als wichtiger Vorläufer, und 1997 wurde sie dafür ausgezeichnet. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), Schauspielerin und Erfinderin, 1944 (Pressefoto: [unbekannt](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), gemeinfrei). -::: - -## Summe und Maximum - -Zwei Aufgaben kommen bei Arrays immer wieder vor: - -- **Summe:** Du beginnst mit `summe = 0` und zählst in der Schleife jeden Wert dazu. -- **Maximum:** Du merkst dir zuerst den Wert im Fach 0 als bisher größten. In der Schleife prüfst du jeden Wert. Ist er größer, wird er das neue Maximum. - -::: diagramm l14-summe-maximum.visustruct -Summe und Maximum in einem einzigen Durchlauf. Das Maximum beginnt mit punkte[0] und nicht mit 0, damit es auch bei negativen Zahlen stimmt. -::: - -::: übung l14-summe-max -titel: Summe und Maximum -auftrag: Lies 5 ganze Zahlen in ein Array ein. Gib danach **Summe: …** und in der nächsten Zeile **Maximum: …** aus. -test: 3, 9, 2, 9, 1 -> Summe: 24 | Maximum: 9 -test: 10, 20, 30, 40, 50 -> Summe: 150 | Maximum: 50 -test: -5, -2, -9, -1, -3 -> Summe: -20 | Maximum: -1 -braucht: forSchleife, verzweigung -tipp: Gehe das Array mit einer Schleife durch. Zähle jeden Wert zur Summe und merke dir den größten bisher gesehenen Wert. -tipp: Rechne Summe und Maximum in eigenen Variablen aus und gib sie erst am Ende aus, z. B. mit `output: "Summe: " + summe`. Beginnt dein Maximum bei 0, scheitert der dritte Test. -tipp: Das Gerüst: Array anlegen und mit einer Schleife einlesen. Dann `int summe = 0` und `int max = zahlen[0]`. Eine zweite Schleife rechnet `summe = summe + zahlen[i]`, und eine Verzweigung `zahlen[i] > max` setzt im true-Zweig `max = zahlen[i]`. Zum Schluss die beiden Ausgaben. -lösung: l14-summe-max-loesung.visustruct -::: - -## Jetzt in Java - -Das Durchlaufen eines Arrays sieht in Java so aus: - -```java editor -int[] zahlen = {2, 4, 6, 8, 10}; -for (int i = 0; i < zahlen.length; i++) { - System.out.println(zahlen[i]); -} -``` - -- `int[]` heißt „Array aus ganzen Zahlen“. Die Werte stehen in geschweiften Klammern. -- Ein leeres Array mit 5 Fächern legst du mit `new int[5]` an. -- `zahlen[i]` ist das Fach mit dem Index `i`, `zahlen.length` die Länge. - -Gibst du das ganze Array aus, also `System.out.println(zahlen);`, zeigt Java nicht die Zahlen. Du siehst nur einen Text wie `[I@1b6d3586`. Das ist die **Referenz**, also die Stelle, an der das Array im Speicher liegt. Den Inhalt zeigt erst `Arrays.toString(zahlen)`, das ergibt `[2, 4, 6, 8, 10]`. Dafür schreibst du ganz oben `import java.util.Arrays;`. - -::: übung l15-java-noten -titel: Notensumme in Java -code: java -auftrag: Lege in Java das Array `noten` mit den Werten 2, 3, 1 und 4 an. Zähle alle Noten mit einer For-Schleife zusammen und gib **Summe: 10** aus. -test: - -> Summe: 10 -braucht: forSchleife -tipp: Lege vor der Schleife `int summe = 0;` an. Die Schleife läuft von 0 bis `noten.length - 1`. -tipp: `int[] noten = {2, 3, 1, 4};`, dann `for (int i = 0; i < noten.length; i++) { summe = summe + noten[i]; }` und zum Schluss `System.out.println("Summe: " + summe);`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Lückentext: Arrays - -::: lückentext -titel: Das Maximum suchen -auftrag: Das Programm sucht den größten Wert im Array. Ergänze die fehlenden Wörter und Zeichen. -```java -[[int]][] werte = {3, 7, 1, 9, 4}; -int max = werte[0]; -[[for]] (int i = 1; i < werte.[[length]]; i++) { - if (werte[i] [[>]] max) { - max = werte[i]; - } -} -System.out.println("Maximum: " + max); -``` -erklärung: `int[]` ist ein Array aus ganzen Zahlen, `werte.length` seine Länge. `max` beginnt mit dem ersten Wert, die Schleife vergleicht alle weiteren damit. Ausgabe: **Maximum: 9** -::: - -## Fehler finden: Temperaturen - -::: übung l15-java-fehler -titel: Höchste Temperatur reparieren -code: java -auftrag: Im Januar wurden die Temperaturen -5, -2, -8 und -3 Grad gemessen. Das Programm soll die höchste ausgeben, also **Höchste Temperatur: -2**. Es behauptet aber 0 Grad, obwohl es so warm nie war. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l15-java-fehler-start.visustruct -test: - -> Höchste Temperatur: -2 -braucht: forSchleife, verzweigung -tipp: Mit welchem Wert beginnt `max`? Ist irgendeine der Temperaturen größer als dieser Startwert? -tipp: `max` muss mit einem Wert aus dem Array beginnen: `int max = temperaturen[0];` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l15-java-laeuft-nicht -titel: Punkte zusammenzählen -code: java -auftrag: Im Array `punkte` stehen 4, 7, 2 und 5. Das Programm soll alle zusammenzählen und **Summe: 18** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Summe: 18 -braucht: forSchleife -tipp: Der Code-Bereich unterstreicht das falsche Wort rot. Wie schreibt man die Länge eines Arrays? -tipp: `length` schreibt man ganz klein: `i < punkte.length` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler** rund um Arrays, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Sind die Teile einer Zeile umrandet, tippst du genau auf den falschen Teil. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - for (int i = 0; i <= zahlen.length; i++) { - System.out.println(zahlen[i]); - } -frage: Soll alle fünf Werte ausgeben. -fehler: 2 -stelle: i <= zahlen.length -besser: for (int i = 0; i < zahlen.length; i++) { -> Mit `<=` läuft i bis 5. Das Fach `zahlen[5]` gibt es nicht. - -runde: - String[] namen = {"Ada", "Ben", "Cem"}; - System.out.println(namen[1]); -frage: Soll den ersten Namen ausgeben, also Ada. -fehler: 2 -stelle: namen[1] -besser: System.out.println(namen[0]); -> Gezählt wird ab 0. `namen[1]` ist schon Ben. - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - System.out.println(zahlen[zahlen.length]); -frage: Soll den letzten Wert ausgeben, also 10. -fehler: 2 -stelle: zahlen[zahlen.length] -besser: System.out.println(zahlen[zahlen.length - 1]); -> Der letzte Index ist immer eins weniger als die Länge. - -runde: - int[] punkte = {3, 9, 2}; - int summe = 0; - for (int i = 0; i < punkte.length; i++) { - summe = punkte[i]; - } - System.out.println(summe); -frage: Soll die Summe 14 ausgeben. -fehler: 4 -stelle: punkte[i] -besser: summe = summe + punkte[i]; -> So überschreibt jeder Wert den vorigen, am Ende steht nur 2 in summe. - -runde: - int[] punkte = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < punkte.length; i++) { - if (punkte[i] > max) { - max = punkte[i]; - } - } - System.out.println(max); -frage: Soll das Maximum -2 ausgeben. -fehler: 2 -stelle: 0 -besser: int max = punkte[0]; -> Keine der Zahlen ist größer als 0, also bliebe max bei 0. Das Maximum beginnt deshalb mit dem ersten Fach. - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - for (int i = zahlen.length; i >= 0; i--) { - System.out.println(zahlen[i]); - } -frage: Soll die Werte rückwärts ausgeben, von 10 bis 2. -fehler: 2 -stelle: int i = zahlen.length -besser: for (int i = zahlen.length - 1; i >= 0; i--) { -> Rückwärts beginnt man beim letzten Index, und der ist `zahlen.length - 1`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Array mit Werten oder mit `new int[5]` anlegen. → Viele Werte unter einem Namen -- Ich kann erklären, dass der Index bei 0 beginnt und der letzte Index `length - 1` ist. → Der Index beginnt bei 0 -- Ich kann ein Array mit einer For-Schleife durchlaufen und füllen. → Ein Array mit der Schleife durchlaufen -- Ich kann die Summe und das Maximum eines Arrays berechnen. → Summe und Maximum -- Ich kann in Java ein Array anlegen und mit einer Schleife durchlaufen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was gibt `output: zahlen[2]` bei `int[] zahlen = {5, 10, 15, 20}` aus? -→ Der Index beginnt bei 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Gezählt wird ab 0: zahlen[0] ist 5, zahlen[1] ist 10, zahlen[2] ist 15. - -? Ein Array hat 8 Fächer. Welcher Index ist der letzte? -→ Der Index beginnt bei 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> Der letzte Index ist immer length - 1. - -? Welche Bedingung passt in eine For-Schleife, die alle Werte von `zahlen` durchläuft und bei `int i = 0` beginnt? -→ Ein Array mit der Schleife durchlaufen -- [x] i < zahlen.length -- [ ] i <= zahlen.length -- [ ] i > zahlen.length -> Mit <= würde die Schleife auch zahlen[length] lesen, und dieses Fach gibt es nicht. - -? Womit sollte das Maximum beginnen, bevor die Schleife startet? -→ Summe und Maximum -- [ ] Immer mit 0 -- [x] Mit dem ersten Wert des Arrays -- [ ] Mit der Länge des Arrays -> Beginnt es mit 0, wäre bei lauter negativen Zahlen 0 das Ergebnis, obwohl 0 gar nicht im Array steht. - -? Welches deutsche Wort wird oft für Array benutzt? -→ Viele Werte unter einem Namen -- [ ] Zweig -- [x] Feld -- [ ] Fall -> Ein Array heißt auf Deutsch oft Feld. Gemeint ist dasselbe: viele Fächer unter einem Namen. - -? Welche Zeile legt in Java ein Array mit drei Zahlen an? -→ Jetzt in Java -- [x] int[] zahlen = {1, 2, 3}; -- [ ] int zahlen = [1, 2, 3]; -- [ ] array zahlen = (1, 2, 3); -::: diff --git a/web/lessons/de/16-zweidimensionale-arrays.md b/web/lessons/de/16-zweidimensionale-arrays.md deleted file mode 100644 index 8e919c66..00000000 --- a/web/lessons/de/16-zweidimensionale-arrays.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -titel: Zweidimensionale Arrays -bereich: Grundlagen -kurz: Werte in Zeilen und Spalten speichern, wie die Sitze in einem Kinosaal, und mit zwei verschachtelten Schleifen durchlaufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein 2D-Array anlegen und erklären, was die beiden Zahlen bedeuten -- ein Fach mit Reihe und Platz lesen und schreiben -- `sitze.length` und `sitze[0].length` unterscheiden -- erklären, in welcher Reihenfolge zwei verschachtelte Schleifen die Fächer besuchen -- in Java ein zweidimensionales Array anlegen und durchlaufen -::: - -## Ein Kinosaal als Tabelle - -Ein Kinosaal hat mehrere **Reihen**, und jede Reihe hat mehrere **Plätze**. Für jeden Sitz wollen wir uns merken, ob die Karte schon verkauft ist. Das ist eine Ja-oder-nein-Frage, also passt der Datentyp `boolean`: `true` heißt verkauft, `false` heißt frei. - -Ein normales Array ist eine einzige Reihe von Fächern. Für den Saal brauchen wir eine **Tabelle** mit Zeilen und Spalten. Das ist ein **zweidimensionales Array**, kurz 2D-Array. Du schreibst dafür zwei Paar eckige Klammern: - -``` -boolean[][] sitze = new boolean[3][5] -``` - -Das ergibt 3 Reihen mit je 5 Plätzen, also 15 Fächer. Am Anfang steht in allen Fächern `false`: Alle Plätze sind frei. - -::: grafik kino-saal.svg -Der erste Index ist die Reihe, der zweite der Platz. Beide werden ab 0 gezählt. -::: - -## Ein Fach ansprechen - -Für ein Fach brauchst du jetzt **zwei** Indizes: zuerst die Reihe, dann den Platz. - -``` -sitze[1][2] = true -``` - -Damit ist in Reihe 1 der Platz 2 verkauft. Gelesen wird genauso: `sitze[1][2]` ist jetzt `true`. - -Auch die Größe kannst du abfragen: - -- `sitze.length` ist die Anzahl der **Reihen**, hier 3. -- `sitze[0].length` ist die Anzahl der **Plätze** in Reihe 0, hier 5. - -::: diagramm kino-verkaufen.visustruct -Drei Karten werden verkauft. Danach gibt das Diagramm die Größe des Saals aus und ob Reihe 1, Platz 2 verkauft ist. Schau in der Simulation unter Variablen zu: sitze wird Zeile für Zeile angezeigt. -::: - -> Wie beim normalen Array gibt es kein Fach `sitze[3][0]`, denn die Reihen heißen 0, 1 und 2. Die Simulation meldet dann einen ungültigen Ausdruck. - -## Zwei Schleifen ineinander - -Um **alle** Plätze anzusehen, brauchst du zwei For-Schleifen ineinander: - -- Die **äußere Schleife** zählt die Reihe: `reihe` läuft von 0 bis 2. -- Die **innere Schleife** zählt den Platz: `platz` läuft von 0 bis 4. - -::: grafik kino-schleifen.svg -Blau ist die äußere Schleife, Orange die innere. Die Zahlen zeigen, in welcher Reihenfolge die Plätze drankommen. -::: - -Für jede Reihe läuft die innere Schleife einmal **komplett** durch. Erst wenn `platz` alle fünf Plätze besucht hat, zählt die äußere Schleife `reihe` um eins weiter. Danach beginnt `platz` wieder bei 0. So kommen alle 3 · 5 = 15 Plätze genau einmal dran. - -Die Bedingungen lauten `reihe < sitze.length` und `platz < sitze[reihe].length`. So passen die Schleifen zu jeder Saalgröße. - -::: diagramm kino-saalplan.visustruct -Der Saalplan: Für jede Reihe wird eine Zeile aus X (verkauft) und O (frei) zusammengesetzt und ausgegeben. Gehe es mit Schritt durch und beobachte, wie platz immer wieder von vorn beginnt, während reihe nur langsam weiterzählt. -::: - -::: angeberwissen -Jedes Handyfoto ist ein 2D-Array! Ein Foto mit 4000 · 3000 Pixeln hat 3000 Zeilen mit je 4000 Fächern, also 12 Millionen Pixel. In jedem Fach steht die Farbe eines Punkts. Wenn du ein Foto heller machst, läuft das Handy mit zwei verschachtelten Schleifen über alle Pixel. Mein Profilbild hat übrigens nur ein Fach: braun. -::: - -## Übung: Freie Plätze zählen - -::: übung kino-frei -titel: Freie Plätze zählen -auftrag: Im Startdiagramm sind schon drei Karten verkauft. Zähle mit zwei verschachtelten Schleifen, wie viele Plätze noch **frei** sind, und gib z. B. `Frei: 12` aus. -start: kino-frei-start.visustruct -test: - -> Frei: 12 -braucht: forSchleife, verzweigung -tipp: Du gehst wie beim Saalplan mit zwei Schleifen über alle Plätze. Statt etwas auszugeben, zählst du mit. -tipp: Lege vor den Schleifen `int frei = 0` an. Ein Platz ist frei, wenn `sitze[reihe][platz]` falsch ist. Die Bedingung dafür lautet `!sitze[reihe][platz]`. -lösung: kino-frei-loesung.visustruct -::: - -## Übung: Karte verkaufen - -::: übung kino-karte -titel: Karte verkaufen -auftrag: Das Startdiagramm fragt nach Reihe und Platz. Ist der Platz schon verkauft, gib `Schon verkauft` aus. Sonst markiere ihn als verkauft und gib z. B. `Verkauft: Reihe 0, Platz 4` aus. -start: kino-karte-start.visustruct -test: 1, 2 -> Schon verkauft -test: 0, 4 -> Verkauft: Reihe 0, Platz 4 -test: 2, 0 -> Verkauft: Reihe 2, Platz 0 -braucht: verzweigung -tipp: Zuerst wird geprüft, ob der Platz verkauft ist. Je nach Antwort passiert etwas anderes. -tipp: Hier brauchst du keine Schleife. Eine Verzweigung mit der Bedingung `sitze[reihe][platz]` genügt. Im false-Zweig setzt du das Fach auf `true`. -lösung: kino-karte-loesung.visustruct -::: - -## Übung: Einmaleins-Tabelle - -::: übung kino-einmaleins -titel: Einmaleins-Tabelle -auftrag: Lege `int[][] tabelle = new int[10][10]` an und fülle sie mit zwei Schleifen mit dem kleinen Einmaleins. Frage danach zwei Zahlen von 1 bis 10 ab und lies das Ergebnis aus der Tabelle, z. B. `3 · 4 = 12`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: Erst füllen zwei verschachtelte Schleifen die Tabelle, danach liest du nur ein einziges Fach aus. -tipp: Die Indizes laufen von 0 bis 9, die Zahlen aber von 1 bis 10. Darum steht im Fach `tabelle[i][j]` der Wert `(i + 1) * (j + 1)`, und für 3 · 4 liest du `tabelle[a - 1][b - 1]`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Jetzt in Java - -Der Kinosaal sieht in Java so aus: - -```java editor -boolean[][] sitze = new boolean[3][5]; -sitze[0][2] = true; -sitze[1][1] = true; -sitze[1][2] = true; -System.out.println("Reihen: " + sitze.length); -System.out.println("Plätze pro Reihe: " + sitze[0].length); -System.out.println("Reihe 1, Platz 2 verkauft? " + sitze[1][2]); -``` - -- Zwei Paar eckige Klammern `[][]` stehen für eine Tabelle: erst die Reihe, dann der Platz. -- `sitze.length` ist die Zahl der Reihen, `sitze[0].length` die Zahl der Plätze in einer Reihe. -- Mit festen Werten schreibst du jede Reihe in eigene geschweifte Klammern: `int[][] t = {{1, 2}, {3, 4}};` - -::: übung l16-java-punkte -titel: Punkte zusammenzählen -code: java -auftrag: Drei Spieler haben je zwei Runden gespielt. Lege in Java die Tabelle `punkte` mit den Reihen {3, 5}, {4, 1} und {2, 2} an. Zähle mit zwei verschachtelten For-Schleifen alle Punkte zusammen und gib **Summe: 17** aus. -test: - -> Summe: 17 -braucht: forSchleife -tipp: Die äußere Schleife geht durch die Reihen, die innere durch die Plätze einer Reihe. -tipp: `int[][] punkte = {{3, 5}, {4, 1}, {2, 2}};` und in der inneren Schleife `summe = summe + punkte[i][j];`. Die innere Schleife läuft bis `punkte[i].length`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Lückentext: Tabellen - -::: lückentext -titel: Alle Felder zusammenzählen -auftrag: Das Programm zählt alle Zahlen in der Tabelle zusammen. Ergänze die fehlenden Wörter und Zeichen. -```java -[[int]][][] feld = {{1, 2, 3}, {4, 5, 6}}; -int summe = 0; -for (int r = 0; r < feld.[[length]]; r++) { - for (int s = 0; s < feld[r].[[length]]; s++) { - summe = summe [[+]] feld[r][s]; - } -} -System.out.println("Summe: " + summe); -``` -erklärung: `int[][]` ist eine Tabelle. `feld.length` ist die Zahl der Reihen (2), `feld[r].length` die Zahl der Felder in Reihe `r` (3). Ausgabe: **Summe: 21** -::: - -## Fehler finden: Punkte - -::: übung l16-java-fehler -titel: Spieler 2 reparieren -code: java -auftrag: In der Tabelle `punkte` steht jede Reihe für einen Spieler. Das Programm soll die Punkte von Spieler 2 (Reihe 1) zusammenzählen, also **Spieler 2: 11**. Stattdessen meldet es einen Fehler. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l16-java-fehler-start.visustruct -test: - -> Spieler 2: 11 -braucht: forSchleife -tipp: Bei `punkte[a][b]` ist `a` die Reihe und `b` der Platz in der Reihe. Welche Zahl steht für die Reihe von Spieler 2? -tipp: Die Indizes sind vertauscht: `punkte[1][j]` statt `punkte[j][1]`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l16-java-laeuft-nicht -titel: Spielstand ablesen -code: java -auftrag: In der Tabelle `punkte` steht jede Reihe für einen Spieler, jede Spalte für eine Runde. Das Programm soll die Punkte von Spieler 2 in Runde 1 ausgeben, also **Spieler 2, Runde 1: 4**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Spieler 2, Runde 1: 4 -braucht: anweisung -tipp: Die Fächer `[1][0]` stimmen. Schau dir den Namen der Tabelle an. -tipp: Die Tabelle heißt `punkte` mit kleinem p: `punkte[1][0]` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Platz? - -::: spiel frage -titel: Spiel: Welcher Platz? -intro: Ludwig verkauft Kinokarten. Erst die Reihe, dann der Platz, beide ab 0. Sieben Runden. - -runde: - boolean[][] sitze = new boolean[3][5]; - System.out.println(sitze.length); -frage: Was wird ausgegeben? -- [x] 3 -- 5 -- 15 -> `sitze.length` ist die Anzahl der Reihen. - -runde: - boolean[][] sitze = new boolean[3][5]; - System.out.println(sitze[0].length); -frage: Was wird ausgegeben? -- 3 -- [x] 5 -- 15 -> `sitze[0]` ist die Reihe 0. Ihre Länge ist die Anzahl der Plätze in dieser Reihe. - -runde: - int[][] tabelle = new int[4][6]; -frage: Wie viele Fächer hat die Tabelle? -- 10 -- [x] 24 -- 35 -> 4 Reihen mit je 6 Plätzen: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: Was wird ausgegeben? -- 2 -- [x] 3 -- 4 -> Reihe 1 ist `{3, 4}`, darin Platz 0 die 3. - -runde: - boolean[][] sitze = new boolean[3][5]; - sitze[3][0] = true; -frage: Was passiert? -- Der erste Platz in Reihe 3 ist verkauft. -- Der erste Platz in der letzten Reihe ist verkauft. -- [x] Es gibt einen Fehler, denn die Reihen heißen 0, 1 und 2. -> Wie beim normalen Array ist der letzte Index eins weniger als die Länge. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int summe = 0; - for (int reihe = 0; reihe < t.length; reihe++) { - for (int platz = 0; platz < t[reihe].length; platz++) { - summe = summe + t[reihe][platz]; - } - } - System.out.println(summe); -frage: Was wird ausgegeben? -- 6 -- 15 -- [x] 21 -> Die zwei Schleifen besuchen alle sechs Fächer: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Zwei Schleifen gehen den Saal `new boolean[3][5]` durch. Welches Fach kommt direkt nach `sitze[0][4]`? -- `sitze[0][5]` -- [x] `sitze[1][0]` -- `sitze[1][4]` -> Reihe 0 ist fertig. Die äußere Schleife zählt weiter, und der Platz beginnt wieder bei 0. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein 2D-Array anlegen und erklären, was die beiden Zahlen bedeuten. → Ein Kinosaal als Tabelle -- Ich kann ein Fach mit Reihe und Platz lesen und schreiben. → Ein Fach ansprechen -- Ich kann `sitze.length` und `sitze[0].length` unterscheiden. → Ein Fach ansprechen -- Ich kann erklären, in welcher Reihenfolge zwei verschachtelte Schleifen die Fächer besuchen. → Zwei Schleifen ineinander -- Ich kann in Java ein zweidimensionales Array anlegen und durchlaufen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie viele Fächer hat `boolean[][] sitze = new boolean[4][6]`? -→ Ein Kinosaal als Tabelle -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 Reihen mit je 6 Plätzen: 4 · 6 = 24. - -? Was steht nach `new boolean[3][5]` in allen Fächern? -→ Ein Kinosaal als Tabelle -- [ ] true -- [x] false -- [ ] Nichts, die Fächer sind leer -> Ein neues boolean-Array ist mit false gefüllt. Im Kino heißt das: alle Plätze frei. - -? Zwei verschachtelte Schleifen laufen über 3 Reihen mit 5 Plätzen. Wie oft wird der Inhalt der **inneren** Schleife ausgeführt? -→ Zwei Schleifen ineinander -- [ ] 3-mal -- [ ] 8-mal -- [x] 15-mal -> Für jede der 3 Reihen läuft die innere Schleife 5-mal: 3 · 5 = 15. - -? Ordne zu, was der Ausdruck im Kinosaal bedeutet. -→ Ein Fach ansprechen -= sitze.length -> Anzahl der Reihen -= sitze[0].length -> Anzahl der Plätze in Reihe 0 -= sitze[2][4] -> Reihe 2, Platz 4 -= sitze[1][0] = true -> In Reihe 1 ist Platz 0 verkauft - -? Was gibt `sitze.length` bei `new boolean[3][5]` an? -→ Ein Fach ansprechen -- [x] 3, die Zahl der Reihen -- [ ] 5, die Zahl der Plätze -- [ ] 15, alle Plätze zusammen -::: diff --git a/web/lessons/de/17-zeichenketten.md b/web/lessons/de/17-zeichenketten.md deleted file mode 100644 index cf127b50..00000000 --- a/web/lessons/de/17-zeichenketten.md +++ /dev/null @@ -1,528 +0,0 @@ ---- -titel: Zeichenketten -bereich: Grundlagen -kurz: Mit Texten arbeiten, also einzelne Zeichen lesen, Teile herausschneiden, suchen, vergleichen und zerlegen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass auch die Zeichen eines Strings ab 0 gezählt werden -- mit `length()` und `charAt(i)` arbeiten -- einen `char` von einem String unterscheiden und mit `(int)` und `(char)` den Zeichencode nutzen -- mit `substring` einen Teil aus einem Text schneiden -- `indexOf`, `contains`, `toUpperCase` und `equals` anwenden -- einen Text mit einer Schleife Zeichen für Zeichen durchgehen -- einen Text mit `split` in ein String-Array zerlegen -- in Java einen Text einlesen und mit `equals` vergleichen -- weitere Methoden wie `lastIndexOf`, `replace`, `compareTo` und `Integer.parseInt` anwenden -- einen Text aus einer Datei mit `split("\n")` in Zeilen zerlegen -- eine Methode in der Java-API nachschlagen und ihre Beschreibung lesen -::: - -## Text ist eine Reihe von Zeichen - -Einen `String` kennst du schon: `String name = "Ludwig"`. In der Informatik heißt so ein Text auch **Zeichenkette**, weil die Zeichen wie Perlen an einer Kette hintereinander hängen. Jedes Zeichen hat einen Platz mit einem Index, genau wie bei einem Array. Auch hier wird **ab 0 gezählt**: Das erste Zeichen ist 0 Schritte vom Anfang entfernt. - -::: grafik zk-zeichen.svg -Die acht Zeichen von „Geißbock“ mit ihren Indizes 0 bis 7. -::: - -Ein String bringt **Methoden** mit. Du schreibst den Namen der Variablen, einen Punkt und die Methode: - -- `wort.length()` liefert die Anzahl der Zeichen, hier 8. -- `wort.charAt(0)` liefert das Zeichen mit dem Index 0, hier `'G'`. - -> Bei Strings schreibst du `length()` **mit** Klammern, bei Arrays `length` **ohne**. Das ist in Java leider so. - -Das letzte Zeichen hat wie beim Array den Index `length() - 1`. - -## Ein einzelnes Zeichen: char - -Für ein einzelnes Zeichen hat Java einen eigenen Datentyp: **char**, kurz für englisch *character* (Zeichen). Ein `char` steht in **einfachen** Anführungszeichen, ein String in doppelten: - -- `'C'` ist ein `char`: genau ein Zeichen. -- `"C"` ist ein String: ein Text, der nur ein Zeichen lang ist. -- Ein `char` hat immer genau ein Zeichen. Einen leeren `char` wie `''` gibt es nicht, einen leeren String `""` schon. - -`charAt` liefert dir so ein Zeichen aus einem Text: - -``` -char erster = wort.charAt(0) -char letzter = wort.charAt(wort.length() - 1) -``` - -Zwei `char` vergleichst du mit `==`: `wort.charAt(0) == 'G'` ist `true`. - -Hinter jedem Zeichen steckt eine Zahl, sein **Zeichencode**. Welche Zahl zu welchem Zeichen gehört, steht in der ASCII-Tabelle: `'A'` hat den Code 65, `'B'` 66, `'C'` 67 und so weiter bis `'Z'` mit 90. Mit `(int)` bekommst du den Code eines Zeichens, mit `(char)` wird aus einem Code wieder ein Zeichen: - -``` -int code = (int) 'C' -code = code + 1 -char naechstes = (char) code -``` - -Danach ist `code` 68 und `naechstes` ist `'D'`. So verschiebst du einen Buchstaben im Alphabet, und genau das brauchst du später bei der Caesar-Verschlüsselung. - -## Teile herausschneiden - -Mit `substring` schneidest du einen Teil aus dem Text: - -- `wort.substring(0, 4)` liefert die Zeichen von Index 0 **bis vor** Index 4, also `"Geiß"`. -- `wort.substring(4)` liefert alles ab Index 4 bis zum Ende, also `"bock"`. - -Das Ende gehört **nicht** mehr dazu. Praktisch ist: `substring(0, 4)` liefert genau 4 Zeichen. - -::: diagramm zk-zeichen.visustruct -Alle Methoden aus dieser Lektion an einem Beispiel. Gehe es in der Simulation durch und vergleiche jede Ausgabe mit der Grafik. -::: - -## Suchen und vergleichen - -Weitere Methoden, die du oft brauchst: - -| Methode | Bedeutung | Beispiel mit `"Geißbock"` | -|---|---|---| -| `indexOf("bock")` | Wo beginnt der Text? | 4 | -| `indexOf("x")` | nicht gefunden | -1 | -| `contains("eiß")` | Kommt der Text vor? | true | -| `toUpperCase()` | alles groß | GEISSBOCK | -| `toLowerCase()` | alles klein | geißbock | -| `equals("Geißbock")` | gleicher Text? | true | - -> Zwei Strings vergleichst du in Java mit `equals`, nicht mit `==`. Zwei einzelne Zeichen (`char`) vergleichst du dagegen mit `==`, z. B. `wort.charAt(i) == 'e'`. - -::: angeberwissen -Probier mal `"Geißbock".toUpperCase()` aus. Heraus kommt GEISSBOCK, und das Wort ist plötzlich ein Zeichen **länger**! Ein großes ß gab es auf Schreibmaschinen nie, darum wird es zu SS. Erst seit 2017 ist das große ẞ offiziell erlaubt, aber Java bleibt beim SS. In meinem Namen kommt zum Glück kein ß vor, nur im Wort Geißbock. -::: - -## Zeichen für Zeichen - -Mit einer For-Schleife gehst du einen Text Zeichen für Zeichen durch. Der Index `i` läuft von 0 bis `wort.length() - 1`, genau wie bei einem Array: - -::: diagramm zk-zaehlen.visustruct -Zählt, wie oft der Buchstabe e vorkommt. Probiere in der Simulation „Kaffeebecher“ aus und beobachte i und anzahl. -::: - -## Zerlegen mit split - -Oft stehen mehrere Angaben in einer einzigen Zeile, getrennt durch ein Zeichen, z. B. `"Ludwig;Geißbock;7"`. Mit `split` zerlegst du die Zeile an diesem Zeichen. Heraus kommt ein **String-Array**: - -``` -String[] teile = zeile.split(";") -``` - -Danach ist `teile[0]` gleich `"Ludwig"`, `teile[1]` gleich `"Geißbock"` und `teile[2]` gleich `"7"`. Mit `teile.length` erfährst du, wie viele Teile es sind. - -::: diagramm zk-split.visustruct -Die Zeile wird am Semikolon zerlegt und jeder Teil mit seinem Index ausgegeben. -::: - -> So sind auch CSV-Dateien aufgebaut, die du aus Tabellenkalkulationen kennst: eine Zeile pro Datensatz, die Werte durch Semikolon getrennt. - -## Übung: Initialen - -::: übung zk-initialen -titel: Initialen -auftrag: Frage nach Vorname und Nachname und gib die Initialen aus. Bei Ludwig Geißbock also `L. G.` -test: Ludwig, Geißbock -> L. G. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: Die Initiale ist der erste Buchstabe eines Namens. Welche Methode liefert ein Zeichen an einer bestimmten Stelle? -tipp: Den ersten Buchstaben liefert `vorname.charAt(0)`. Die Ausgabe ist dann `vorname.charAt(0) + ". " + nachname.charAt(0) + "."` -lösung: zk-initialen-loesung.visustruct -::: - -## Übung: Rückwärts - -::: übung zk-rueckwaerts -titel: Rückwärts -auftrag: Frage nach einem Wort und gib es rückwärts aus. Aus Ludwig wird `giwduL`. -test: Ludwig -> giwduL -test: Anna -> annA -test: a -> a -braucht: forSchleife -tipp: Gehe das Wort Zeichen für Zeichen durch, aber von hinten nach vorn, und baue dabei einen neuen Text zusammen. -tipp: Lege `String rueckwaerts = ""` an. Die Schleife beginnt beim letzten Index `wort.length() - 1` und zählt mit `i--` bis 0 herunter. In jedem Durchlauf hängst du `wort.charAt(i)` hinten an. -tipp: Das Gerüst: Eingabe `wort`, dann `String rueckwaerts = ""`. For-Schleife mit Start `int i = wort.length() - 1`, Bedingung `i >= 0` und Schritt `i--`. Darin `rueckwaerts = rueckwaerts + wort.charAt(i)`. Unter der Schleife `output: rueckwaerts`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Übung: Wörter zählen - -::: übung zk-woerter -titel: Wörter zählen -auftrag: Frage nach einem Satz und gib aus, wie viele Wörter er hat, z. B. `Wörter: 4` bei „Ludwig ist ein Geißbock“. Die Wörter sind durch genau ein Leerzeichen getrennt. -test: Ludwig ist ein Geißbock -> Wörter: 4 -test: Hallo -> Wörter: 1 -test: Struktogramme machen Spaß -> Wörter: 3 -braucht: anweisung -tipp: Zwischen den Wörtern steht immer ein Leerzeichen. Welche Methode zerlegt einen Text an einem Zeichen? -tipp: Zerlege den Satz mit `satz.split(" ")` in ein String-Array. Seine Länge ist die Anzahl der Wörter. -tipp: Das ganze Programm hat drei Kästen: die Eingabe `satz`, dann `String[] woerter = satz.split(" ")` und zum Schluss `output: "Wörter: " + woerter.length`. -lösung: zk-woerter-loesung.visustruct -::: - -## Jetzt in Java - -Die Methoden in dieser Lektion sind schon Java. Ein ganzes Programm mit einem Text sieht so aus: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie heißt du?"); -String name = scanner.next(); -System.out.println("Hallo " + name.toUpperCase() + "!"); -System.out.println("Dein Name hat " + name.length() + " Buchstaben."); -``` - -- `scanner.next()` liest ein Wort, also Text bis zum nächsten Leerzeichen. Für Zahlen nimmst du weiter `scanner.nextInt()`. -- Text steht in **doppelten** Anführungszeichen `"Ludwig"`, ein einzelnes Zeichen in **einfachen** `'L'`. -- Hinter `length()`, `toUpperCase()` und den anderen Methoden stehen immer runde Klammern, auch wenn nichts darin steht. -- Zwei Texte vergleichst du mit `equals`. Dabei zählt jeder Buchstabe: `"Ludwig".equals("ludwig")` ist false. - -::: übung l17-java-passwort -titel: Passwort prüfen -code: java -auftrag: Schreibe in Java ein Programm, das nach dem Passwort fragt. Ist es **Ludwig**, gibt es **Willkommen!** aus, sonst **Falsches Passwort**. - Achte auf Groß- und Kleinschreibung: `ludwig` ist falsch. -test: Ludwig -> Willkommen! -test: ludwig -> Falsches Passwort -test: Geißbock -> Falsches Passwort -braucht: verzweigung -tipp: Lies das Passwort mit `scanner.next()` in eine String-Variable. Dann vergleichst du in einer Verzweigung. -tipp: Die Bedingung heißt `eingabe.equals("Ludwig")`. Mit `==` klappt der Vergleich von Texten in Java nicht zuverlässig. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Lückentext: Texte - -::: lückentext -titel: Ein Wort untersuchen -auftrag: Ergänze die fehlenden Methoden und Wörter. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Ein Wort?"); -[[String]] wort = scanner.next(); -System.out.println("Länge: " + wort.[[length]]()); -System.out.println("Groß: " + wort.[[toUpperCase]]()); -System.out.println("Erster Buchstabe: " + wort.[[charAt]](0)); -if (wort.[[equals]]("Ludwig")) { - System.out.println("Hallo Ludwig!"); -} -``` -erklärung: `length()` zählt die Zeichen, `toUpperCase()` macht alles groß, `charAt(0)` liefert das erste Zeichen. Texte vergleichst du mit `equals`, nicht mit `==`. -::: - -## Fehler finden: Initialen - -::: übung l17-java-fehler -titel: Initialen reparieren -code: java -auftrag: Das Programm liest Vor- und Nachnamen ein und soll die Initialen ausgeben. Bei Ludwig Geißbock also **L. G.** Es gibt aber **u. e.** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: Bei welchem Index beginnt Java zu zählen? -tipp: Das erste Zeichen hat den Index 0: `vorname.charAt(0)` und `nachname.charAt(0)`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l17-java-laeuft-nicht -titel: Buchstaben zählen -code: java -auftrag: Das Programm soll den ersten Buchstaben und die Länge von **Ludwig** ausgeben, also **L hat 6 Buchstaben**. Es läuft aber nicht, denn zwei Methoden sind falsch geschrieben. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L hat 6 Buchstaben -braucht: anweisung -tipp: Methodennamen sind auch CamelCase: Sie beginnen klein, jedes weitere Wort beginnt groß. -tipp: Richtig sind `name.charAt(0)` mit großem A und `name.length()` ganz klein. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Noch mehr Methoden - -Ein String kann noch viel mehr. Diese Methoden brauchst du auch oft: - -| Methode | Bedeutung | Beispiel | Ergebnis | -|---|---|---|---| -| `lastIndexOf("e")` | Wo steht das letzte e? | `"Ziegenbock".lastIndexOf("e")` | 4 | -| `isEmpty()` | Ist der Text leer? | `"".isEmpty()` | true | -| `equalsIgnoreCase(…)` | gleicher Text, egal ob groß oder klein? | `"Ziegenbock".equalsIgnoreCase("ZIEGENBOCK")` | true | -| `replace(alt, neu)` | ersetzt jede Stelle | `"Ziegenbock".replace("bock", "käse")` | Ziegenkäse | -| `repeat(n)` | den Text n-mal hintereinander | `"Mäh".repeat(3)` | MähMähMäh | -| `concat(…)` | hängt Text an, wie `+` | `"Ziegen".concat("bock")` | Ziegenbock | -| `compareTo(…)` | Wer kommt im Alphabet zuerst? | `"Anna".compareTo("Ben")` | -1 | - -`compareTo` liefert eine Zahl: **negativ**, wenn der Text im Alphabet vorher kommt, **0**, wenn beide gleich sind, und **positiv**, wenn er danach kommt. Damit kannst du Namen sortieren. - -Zwei Methoden gehören nicht zu einem Text, sondern zu einer Klasse. Davor steht deshalb der Name der Klasse: - -- `Integer.parseInt("7")` macht aus dem Text "7" die Zahl 7. Mit ihr kannst du rechnen. -- `String.valueOf(7)` macht aus der Zahl 7 den Text "7". - -::: diagramm zk-mehr.visustruct -Alle neuen Methoden an einem Beispiel. Gehe es in der Simulation durch und vergleiche jede Ausgabe mit der Tabelle. -::: - -## Text aus einer Datei - -Programme bekommen Text oft aus einer **Datei**. Prinzessin Zora hat Ludwig einen Brief geschrieben. Er liegt als Datei `brief.txt` bereit: - -::: datei brief.txt -Lieber Ludwig, -der Fuchs hat mich in seinen Turm gesperrt. -Die Tür hat ein Schloss mit einem geheimen Wort. -Das Wort findest du nur mit Java. -Bitte hilf mir! -Deine Prinzessin Zora -::: - -Diese Zeilen lesen die ganze Datei in einen String: - -``` -import java.nio.file.*; - -String brief = Files.readString(Path.of("brief.txt")); -``` - -Wie das Lesen genau funktioniert, lernst du später in einer eigenen Lektion über Dateien. Hier reicht: Danach steht der ganze Brief in `brief`, mit allen Zeilen. - -Am Ende jeder Zeile steht ein unsichtbares Zeichen für den **Zeilenumbruch**. In Java schreibst du es als `"\n"`. Mit `brief.split("\n")` zerlegst du den Brief deshalb in seine Zeilen. - -::: übung l17-java-brief -titel: Wo steckt der Fuchs? -code: java -auftrag: Im Startcode wird der Brief schon gelesen. Zerlege ihn in Zeilen und gib **Zeilen:** und die Anzahl aus. Suche dann die Zeile, in der **Fuchs** steht, und gib **Fuchs in Zeile** und ihre Nummer aus. Die erste Zeile hat die Nummer 1. -start: l17-java-brief-start.visustruct -test: - -> Zeilen: 6 | Fuchs in Zeile 2 -braucht: forSchleife, verzweigung -tipp: `String[] zeilen = brief.split("\n");` zerlegt den Brief. Mit einer For-Schleife gehst du dann alle Zeilen durch. -tipp: In der Schleife prüfst du `zeilen[i].contains("Fuchs")`. Der Index beginnt bei 0, deshalb gibst du `(i + 1)` aus. Die Klammern brauchst du, damit Java rechnet und nicht 1 an den Text hängt. -lösung: l17-java-brief-loesung.visustruct -::: - -## Die Java-API - -Niemand kennt alle Methoden auswendig, auch Profis nicht. Sie schlagen nach, in der **Java-API**. API heißt Application Programming Interface. Gemeint ist die Beschreibung von allem, was Java schon mitbringt. - -Alle Methoden von String findest du hier: [String in der Java-API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Scrolle dort zur Tabelle **Method Summary**. Jede Zeile beschreibt eine Methode: - -| Spalte | Beispiel | Bedeutung | -|---|---|---| -| Modifier and Type | `int` | Was die Methode zurückgibt | -| Method | `lastIndexOf(String str)` | Name und Parameter mit ihrem Datentyp | -| Description | Returns the index within this string of the last occurrence … | Was die Methode macht | - -Die API ist auf Englisch. Dein Browser kann die Seite übersetzen. Die Namen der Methoden bleiben englisch, denn so schreibst du sie auch in Java. - -::: ludwig -Die Seite ist sehr lang. Mit Strg+F (am Mac ⌘+F) suchst du nach dem Namen der Methode und bist sofort da. -::: - -Schlag diese Methoden nach. Sie stehen nicht in dieser Lektion: - -::: quiz -? Schlag `strip()` nach. Was macht die Methode? -- [ ] Sie entfernt alle Leerzeichen im Text. -- [x] Sie entfernt die Leerzeichen am Anfang und am Ende. -- [ ] Sie schneidet das letzte Zeichen ab. -> Wie `trim()`. `strip()` kennt aber auch besondere Leerzeichen aus anderen Schriften. - -? Schlag `isBlank()` nach. Was liefert `" ".isBlank()`? -- [x] true -- [ ] false -- [ ] 3 -> isBlank prüft, ob der Text leer ist oder nur aus Leerzeichen besteht. `" ".isEmpty()` wäre dagegen false. - -? Schlag `toCharArray()` nach. Welchen Datentyp liefert die Methode? -- [ ] String -- [ ] String[] -- [x] char[] -> Ein Array mit allen Zeichen des Textes, für jedes Zeichen ein Fach. - -? Schlag `indexOf` mit zwei Parametern nach. Was liefert `"Ziegenbock".indexOf("e", 3)`? -- [ ] 2 -- [x] 4 -- [ ] -1 -> Die Suche beginnt erst bei Index 3. Das erste e ab dort steht bei Index 4. - -? Schlag `startsWith` nach. Welchen Datentyp liefert die Methode? -- [x] boolean -- [ ] int -- [ ] String -> In der Spalte „Modifier and Type“ steht boolean: Die Antwort ist true oder false. -::: - -## Spiel: Prinzessin retten - -Jetzt hilfst du Ludwig. Der Fuchs hat Prinzessin Zora in seinen Turm gesperrt, und das Tor öffnet sich nur mit einem geheimen Wort. - -::: spiel prinzessin -titel: Spiel: Prinzessin retten -intro: Auf dem Weg zum Turm kommt Ludwig an **sechs Stationen**. An jeder steht ein Stück Java-Code, und heraus kommt eine Zahl. Mit `alphabet.charAt(zahl)` wird aus der Zahl ein Buchstabe, der im **Kreuzworträtsel** landet. Löst du das Rätsel, ergeben die gelben Felder das geheime Wort für das Tor. - -station: Am Burggraben - String name = " Zora "; - int zahl = name.trim().length(); -frage: Welche Zahl steht in `zahl`? -zahl: 4 -feld: 5 1 -tipp: `trim()` schneidet die Leerzeichen vorne und hinten ab. Zähle danach die Zeichen. -> Nach `trim()` bleibt `"Zora"` übrig, und das sind 4 Zeichen. - -station: Im Fuchswald - String ort = "Fuchsbau"; - int zahl = ort.length(); -frage: Welche Zahl steht in `zahl`? -zahl: 8 -feld: 4 4 -tipp: `length()` zählt alle Zeichen. Zähle F-u-c-h-s-b-a-u. -> „Fuchsbau“ hat 8 Zeichen. - -station: Am Ziegenfelsen - String wort = "Ziegenbock"; - int zahl = wort.indexOf("bock"); -frage: Welche Zahl steht in `zahl`? -zahl: 6 -feld: 4 6 -tipp: `indexOf` gibt die Stelle zurück, an der „bock“ anfängt. Das Z hat den Index 0. -> Z i e g e n hat die Indizes 0 bis 5, also beginnt „bock“ bei Index 6. - -station: Auf der Turmtreppe - String wort = "Prinzessin"; - int zahl = wort.indexOf("s", 7); -frage: Welche Zahl steht in `zahl`? Schlag `indexOf(String str, int fromIndex)` in der Java-API nach. -zahl: 7 -feld: 6 6 -tipp: Die Suche beginnt bei Index 7, und Index 7 zählt schon mit. Schreib dir die Indizes unter die Buchstaben. -> P r i n z e s s i n hat die Indizes 0 bis 9. Ab Index 7 gesucht, steht das erste „s“ genau bei 7. - -station: Vor der Fuchshöhle - int zahl = "Zora".compareTo("Zoe"); -frage: Welche Zahl steht in `zahl`? -zahl: 13 -feld: 1 9 -tipp: `compareTo` sucht die erste Stelle, an der sich die Texte unterscheiden, und rechnet die Differenz der Zeichencodes aus. `'r'` ist 114, `'e'` ist 101. -> „Zo“ ist gleich, dann kommt `'r'` gegen `'e'`: 114 − 101 = 13. - -station: Am Turmtor - int zahl = Integer.parseInt("1" + "1") / 5; -frage: Welche Zahl steht in `zahl`? -zahl: 2 -feld: 6 5 -tipp: Zuerst werden die Texte verbunden, dann wird daraus eine Zahl. Bei `int` wird beim Teilen der Rest weggelassen. -> `"1" + "1"` ist `"11"`, `parseInt` macht daraus 11, und 11 / 5 ist bei ganzen Zahlen 2. - -kreuzwort: Die blauen Buchstaben hast du schon. Fülle die anderen Felder mit Fachbegriffen aus Java. Klick auf einen Hinweis, um zum Wort zu springen. -- → 1 3 BOOLEAN: Datentyp für wahr oder falsch -- → 4 1 STRING: Datentyp für Text, also für eine Zeichenkette -- → 6 5 CHAR: Datentyp für ein einzelnes Zeichen -- ↓ 1 6 LENGTH: Methode, die die Zeichen eines Strings zählt -- ↓ 2 1 ELSE: Der „sonst“-Teil einer Verzweigung -- ↓ 3 3 BREAK: Springt sofort aus einer Schleife heraus -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: Ludwig steht vor dem Tor. Das Schloss vergleicht dein Wort mit `equals`. Trag das geheime Wort ein. - String wort = "___"; - if (wort.equals(geheimesWort)) { - tor.oeffnen(); - } -ende: Zora ist frei, und der Fuchs ist schnell davongelaufen. Das geheime Wort war `BREAK`: Mit `break` kommt man aus jeder Schleife heraus, und Zora aus dem Turm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass auch die Zeichen eines Strings ab 0 gezählt werden. → Text ist eine Reihe von Zeichen -- Ich kann mit `length()` und `charAt(i)` arbeiten. → Text ist eine Reihe von Zeichen -- Ich kann einen `char` von einem String unterscheiden und mit `(int)` und `(char)` den Zeichencode nutzen. → Ein einzelnes Zeichen: char -- Ich kann mit `substring` einen Teil aus einem Text schneiden. → Teile herausschneiden -- Ich kann `indexOf`, `contains`, `toUpperCase` und `equals` anwenden. → Suchen und vergleichen -- Ich kann einen Text mit einer Schleife Zeichen für Zeichen durchgehen. → Zeichen für Zeichen -- Ich kann einen Text mit `split` in ein String-Array zerlegen. → Zerlegen mit split -- Ich kann in Java einen Text einlesen und mit `equals` vergleichen. → Jetzt in Java -- Ich kann weitere Methoden wie `lastIndexOf`, `replace`, `compareTo` und `Integer.parseInt` anwenden. → Noch mehr Methoden -- Ich kann einen Text aus einer Datei mit `split("\n")` in Zeilen zerlegen. → Text aus einer Datei -- Ich kann eine Methode in der Java-API nachschlagen und ihre Beschreibung lesen. → Die Java-API -::: - -## Wissenstest - -::: quiz -? Was liefert `"Ludwig".charAt(1)`? -→ Text ist eine Reihe von Zeichen -- [ ] L -- [x] u -- [ ] d -> Gezählt wird ab 0: L hat den Index 0, u den Index 1. - -? Was ist `'C'`? -→ Ein einzelnes Zeichen: char -- [x] ein `char`, also genau ein Zeichen -- [ ] ein String -- [ ] eine Variable mit dem Namen C -> Einfache Anführungszeichen stehen für ein einzelnes Zeichen, also einen `char`. `"C"` in doppelten wäre ein String. - -? `'A'` hat den Zeichencode 65. Was ergibt `(char) 67`? -→ Ein einzelnes Zeichen: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 ist A, 66 ist B, 67 ist C. Mit `(char)` wird aus dem Code wieder ein Zeichen. - -? Was liefert `"Struktogramm".substring(0, 6)`? -→ Teile herausschneiden -- [x] Strukt -- [ ] Struktog -- [ ] truktog -> Die Zeichen mit den Indizes 0 bis 5. Das Ende 6 gehört nicht mehr dazu. - -? Ein Wort hat 7 Zeichen. Welchen Index hat das letzte Zeichen? -→ Text ist eine Reihe von Zeichen -- [ ] 7 -- [x] 6 -- [ ] 8 -> Wie beim Array: immer eins weniger als die Länge. - -? Wie vergleichst du in Java zwei Strings auf gleichen Inhalt? -→ Suchen und vergleichen -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Ordne jeder Methode zu, was sie bei `"Geißbock"` liefert. -→ Suchen und vergleichen -= length() -> 8 -= charAt(0) -> G -= indexOf("bock") -> 4 -= substring(4) -> bock - -? Welche Bedingung prüft in Java, ob in `eingabe` der Text Ludwig steht? -→ Jetzt in Java -- [ ] eingabe = "Ludwig" -- [ ] eingabe == 'Ludwig' -- [x] eingabe.equals("Ludwig") -> Texte vergleichst du mit equals. Einfache Anführungszeichen sind nur für ein einzelnes Zeichen. - -? Was liefert `Integer.parseInt("12") + 1`? -→ Noch mehr Methoden -- [ ] "121" -- [x] 13 -- [ ] eine Fehlermeldung -> parseInt macht aus dem Text eine Zahl. Danach rechnet Java ganz normal. -::: diff --git a/web/lessons/de/18-aufruf-und-aussprung.md b/web/lessons/de/18-aufruf-und-aussprung.md deleted file mode 100644 index 1fca1042..00000000 --- a/web/lessons/de/18-aufruf-und-aussprung.md +++ /dev/null @@ -1,483 +0,0 @@ ---- -titel: Aufruf und Aussprung -bereich: Grundlagen -kurz: Große Aufgaben in Unterprogramme zerlegen und Schleifen vorzeitig verlassen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine große Aufgabe in Teilprobleme zerlegen -- einen Aufruf einfügen und ein Unterprogramm in einem eigenen Reiter bauen -- einem Unterprogramm mit Parametern Werte übergeben -- mit return einen Rückgabewert liefern und ihn beim Aufruf speichern -- erklären, warum ein Unterprogramm eine Zahl nicht verändern kann, ein Array aber schon -- erklären, was ein Aussprung macht und warum er in einer Verzweigung steht -- eine Schleife mit einem Aussprung oder über ihre Bedingung beenden -- in Java eine Methode erkennen und eine Schleife mit `break` verlassen -::: - -## Große Aufgaben zerlegen - -Lange Struktogramme werden schnell unübersichtlich. Deshalb zerlegst du eine große Aufgabe in **Teilprobleme**. Jedes Teilproblem bekommt einen eigenen Namen und ein eigenes Struktogramm, ein **Unterprogramm**. - -Ein Quiz besteht zum Beispiel aus drei Teilen: begrüßen, Fragen stellen, Ergebnis zeigen. Das Hauptprogramm nennt nur noch diese drei Teile: - -::: diagramm l15-hauptprogramm.visustruct -Das Hauptprogramm besteht nur aus drei Aufrufen. Was in jedem Teil passiert, steht in einem eigenen Struktogramm. -::: - -So siehst du auf einen Blick, wie das Programm aufgebaut ist. Die Einzelheiten stehen woanders. - -## Der Aufruf - -Ein **Aufruf** ist ein Kasten mit doppelten senkrechten Linien links und rechts. Er bedeutet: Führe hier das Unterprogramm mit diesem Namen aus und mache danach weiter. - -- Du ziehst **Aufruf** aus dem Bereich Elemente und schreibst den Namen hinein, z. B. `zeigeBegruessung()`. -- Die runden Klammern am Ende zeigen, dass es ein Unterprogramm ist. So schreibt man es auch in Java, Python und JavaScript. -- Das Unterprogramm selbst baust du als eigenes Struktogramm in einem neuen Reiter. Als Titel trägst du denselben Namen ein. - -::: diagramm l15-unterprogramm.visustruct -Das Unterprogramm zeigeBegruessung() als eigenes Struktogramm. -::: - -> **In der Simulation:** Erreicht die Simulation einen Aufruf, sucht sie unter den offenen Reitern das Struktogramm mit diesem Titel und führt es aus. Danach macht sie im Hauptprogramm weiter. Solange das Unterprogramm läuft, bleibt der Aufruf-Kasten markiert. Ein Unterprogramm wie dieses, nur mit Namen als Titel, teilt sich die Variablen mit dem Hauptprogramm. Wie ein Unterprogramm eigene Variablen bekommt, lernst du gleich. Gibt es keinen passenden Reiter, überspringt die Simulation den Aufruf und zeigt einen Hinweis. - -::: angeberwissen -Warum sagt dein Lehrer bei Java immer „Methode“ und nie „Funktion“ oder „Prozedur“? Weil in Java jedes Unterprogramm zu einer Klasse gehören muss, und Unterprogramme einer Klasse heißen Methoden. Den Unterschied gibt es trotzdem: Liefert eine Methode ein Ergebnis zurück, ist sie eigentlich eine Funktion. Steht `void` davor, liefert sie nichts und ist eigentlich eine Prozedur. Java ist da einfach bequem und nennt beide Methode. Ich auch: Meine Methode heißt Fressen. -::: - -## Übung: Aufruf einbauen - -::: übung l15-titel -titel: Begrüßung mit Aufruf -auftrag: Stell dir vor, das Unterprogramm `zeigeTitel()` gibt es schon. Baue das Hauptprogramm: Es ruft zuerst `zeigeTitel()` auf. Danach fragt es nach dem Namen und gibt **Hallo** und den Namen aus. - Bei der Eingabe Ada erscheint also **Hallo Ada**. -test: Ada -> Hallo Ada -test: Ben -> Hallo Ben -braucht: aufruf -tipp: Das Hauptprogramm hat drei Teile: den Aufruf, die Eingabe und die Ausgabe. -tipp: Ziehe einen Aufruf aus dem Bereich Elemente ganz nach oben und schreibe `zeigeTitel()` hinein. Darunter kommen die Eingabe `input: String name "Wie heißt du?"` und die Ausgabe. -lösung: l15-titel-loesung.visustruct -::: - -## Werte übergeben: Parameter - -Oft soll ein Unterprogramm mit Werten arbeiten, die erst beim Aufruf feststehen. Dann bekommt es als Titel eine **Kopfzeile**: - -``` -void zeigeSumme(int a, int b) -``` - -- `void` heißt: Das Unterprogramm liefert kein Ergebnis zurück. -- `zeigeSumme` ist der Name. -- In den Klammern stehen die **Parameter**: Variablen mit Datentyp, die beim Aufruf ihre Werte bekommen. Mehrere trennst du mit Komma. - -::: diagramm l15-zeige-summe.visustruct -Das Unterprogramm mit der Kopfzeile als Titel. Es rechnet mit a und b, als wären es normale Variablen. -::: - -Im Aufruf-Kasten schreibst du die Werte in die Klammern, z. B. `zeigeSumme(3, 4)`. Der erste Wert kommt in `a`, der zweite in `b`. Statt Zahlen gehen auch Variablen und Rechnungen wie `zeigeSumme(x, x + 1)`. - -::: diagramm l15-zeige-summe-haupt.visustruct -Das Hauptprogramm ruft zeigeSumme zweimal mit anderen Werten auf. Öffne beide Diagramme und starte hier die Simulation. -::: - -> **Lokale Variablen:** Die Parameter und alle Variablen, die im Unterprogramm entstehen, gehören nur dem Unterprogramm. Es bekommt **Kopien** der Werte, mehr dazu weiter unten bei der Wertübergabe (call by value). In der Simulation siehst du während des Aufrufs nur die Variablen des Unterprogramms. - -## Ergebnis zurückgeben: return - -Ein Unterprogramm kann auch ein Ergebnis liefern, den **Rückgabewert**. Dann steht in der Kopfzeile statt `void` der Datentyp des Ergebnisses: - -``` -int quadrat(int x) -``` - -Die Anweisung `return ergebnis` gibt den Wert zurück und beendet das Unterprogramm sofort. Beim Aufruf speicherst du das Ergebnis in einer Variablen: Nach `int q = quadrat(5)` steht in `q` der Wert 25. - -::: diagramm l15-quadrat.visustruct -Das Unterprogramm quadrat rechnet und gibt das Ergebnis mit return zurück. -::: - -::: diagramm l15-quadrat-haupt.visustruct -Das Hauptprogramm speichert den Rückgabewert in q. Öffne beide Diagramme und probiere die Simulation aus. -::: - -> Ein Unterprogramm mit Rückgabewert muss mit `return` enden. Fehlt das return, meldet die Simulation einen Fehler. - -In der **Code-Ansicht** wird aus der Kopfzeile eine Java-Methode (`public static int quadrat(int x)`), in Python ein `def` und in JavaScript eine `function`. Sind die Unterprogramme in anderen Reitern offen, stehen sie beim Hauptprogramm mit im Code. - -## Übung: Fläche berechnen - -::: übung l15-flaeche -titel: Fläche berechnen -auftrag: Baue zwei Diagramme. Das Unterprogramm `int flaeche(int a, int b)` gibt die Fläche eines Rechtecks zurück, also a mal b. Das Hauptprogramm fragt nach Länge und Breite, ruft `flaeche` auf und gibt **Fläche:** und das Ergebnis aus. - Bei 3 und 4 erscheint **Fläche: 12**. -test: 3, 4 -> Fläche: 12 -test: 5, 5 -> Fläche: 25 -test: 7, 0 -> Fläche: 0 -braucht: aufruf -tipp: Du baust zwei Diagramme: ein Unterprogramm, das nur rechnet und das Ergebnis zurückgibt, und ein Hauptprogramm für Eingabe und Ausgabe. -tipp: Lege mit **+** einen neuen Reiter an und gib ihm den Titel `int flaeche(int a, int b)`. Darin reicht eine Anweisung: `return a * b`. Im Hauptprogramm schreibst du in den Aufruf-Kasten `int f = flaeche(laenge, breite)`. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Kopie oder Original: call by value und call by reference - -Was passiert, wenn ein Unterprogramm seinen Parameter verändert? Das hängt davon ab, **was** übergeben wird. - -**Wertübergabe**, englisch **call by value**: Bei `int`, `double`, `boolean`, `char` und `String` bekommt das Unterprogramm eine **Kopie** des Werts. Ändert es seinen Parameter, bleibt die Variable des Aufrufers, wie sie war. - -**Referenzübergabe**, englisch **call by reference**: Bei einem **Array** bekommt das Unterprogramm keine Kopie, sondern einen Verweis auf dasselbe Array. Ändert es ein Fach, ist das Fach auch beim Aufrufer geändert. Die Kopfzeile dafür sieht so aus: - -``` -void leeren(boolean[] plaetze) -``` - -::: grafik uebergabe.svg -Oben bekommt verdopple eine Kopie von zahl. Unten zeigen reihe und plaetze auf dasselbe Array. -::: - -Probiere beides aus. Öffne die drei Diagramme und starte im Hauptprogramm die Simulation: - -::: diagramm uebergabe-verdopple.visustruct -verdopple ändert nur seine Kopie x. -::: - -::: diagramm uebergabe-leeren.visustruct -leeren setzt alle Plätze auf false, zum Beispiel wenn eine Vorstellung ausfällt. -::: - -::: diagramm uebergabe-haupt.visustruct -zahl ist nach dem Aufruf immer noch 5. Die Plätze in reihe sind nach dem Aufruf dagegen frei. -::: - -> Willst du mit einer Zahl aus einem Unterprogramm etwas zurückbekommen, nimmst du `return`, z. B. `zahl = verdopple(zahl)` mit der Kopfzeile `int verdopple(int x)`. Ein Array dagegen kann das Unterprogramm direkt verändern. - -::: angeberwissen -Ganz genau genommen kennt Java nur die Wertübergabe, also call by value. Bei einem Array wird aber nicht das ganze Array kopiert, sondern nur seine **Adresse** im Speicher. Mit der kopierten Adresse findet das Unterprogramm dasselbe Array. Das ist so, als würde ich dir eine Kopie meines Schließfachschlüssels geben: Der Schlüssel ist kopiert, das Heu im Fach gibt es nur einmal. -::: - -## Übung: Bonuspunkte - -::: übung uebergabe-bonus -titel: Bonuspunkte -auftrag: Das Hauptprogramm ruft `bonus(punkte)` auf und gibt danach alle Punkte aus. Baue das Unterprogramm `void bonus(int[] punkte)`, das jede Punktzahl im Array um 1 erhöht. Statt 3, 5 und 8 erscheinen dann **4**, **6** und **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: Das Hauptprogramm ist schon fertig. Du baust nur das Unterprogramm in einem neuen Reiter. -tipp: Lege mit **+** einen neuen Reiter mit dem Titel `void bonus(int[] punkte)` an. Darin läuft eine For-Schleife über alle Fächer und rechnet `punkte[i] = punkte[i] + 1`. Ein return brauchst du nicht, denn das Array wird direkt geändert. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## Der Aussprung - -Manchmal soll eine Schleife **vorzeitig** enden, mitten im Rumpf. Dafür gibt es den **Aussprung**. Er verlässt sofort die Schleife, in der er steht. Das Programm macht mit dem ersten Kasten nach der Schleife weiter. In Java, Python und JavaScript heißt das `break`. - -Den Aussprung erkennst du an der Spitze am linken Rand. In den Kasten kannst du einen kurzen Kommentar schreiben, z. B. `Schleife verlassen`. Er erscheint im Code als Kommentar hinter `break`. - -Ein Aussprung steht fast immer in einer **Verzweigung**: Nur wenn eine Bedingung zutrifft, wird die Schleife verlassen. - -Typisch ist die **Endlosschleife mit Aussprung**. Die Schleife selbst hat keine Bedingung. Sie läuft so lange, bis der Aussprung erreicht wird: - -::: diagramm l15-endlos-aussprung.visustruct -Die Schleife fragt immer wieder nach einer Zahl. Bei 0 springt sie heraus, sonst zählt sie die Zahl zur Summe. -::: - -Ohne Aussprung würde eine Endlosschleife wirklich nie enden. - -## Aussprung in der Simulation - -Die Simulation führt den Aussprung aus wie `break`: Sie verlässt die innerste Schleife und macht nach ihr weiter. Steht ein Aussprung außerhalb jeder Schleife, endet das Unterprogramm, im Hauptprogramm das ganze Programm. Dasselbe macht `return`. - -Oft geht es auch ganz ohne Aussprung: Du schreibst die Abbruchbedingung direkt in die Schleife. Das ist meist sogar übersichtlicher. - -## Übung: Summe bis 0 - -::: übung l15-bis-null -titel: Summe bis 0 -auftrag: Baue das Programm „Summe bis 0“ **ohne** Aussprung nach. Es fragt so lange nach ganzen Zahlen, bis jemand 0 eingibt. Danach gibt es **Summe:** und die Summe aller eingegebenen Zahlen aus. -test: 3, 4, 0 -> Summe: 7 -test: 0 -> Summe: 0 -test: 10, -2, 5, 0 -> Summe: 13 -tipp: Ohne Aussprung muss die Bedingung der Schleife selbst prüfen, ob die 0 eingegeben wurde. -tipp: Frage die erste Zahl schon vor der Schleife ab. Die While-Schleife läuft mit der Bedingung `zahl != 0`. Im Rumpf zählst du die Zahl zur Summe und fragst danach die nächste Zahl ab. Bei der zweiten Eingabe schreibst du nur `input: zahl "…"`, weil es `zahl` schon gibt. -lösung: l15-bis-null-loesung.visustruct -::: - -## Jetzt in Java - -Ein Unterprogramm heißt in Java **Methode**. Das Quadrat von oben sieht so aus: - -```java -public static int quadrat(int x) { - int ergebnis = x * x; - return ergebnis; -} -``` - -Das Hauptprogramm ruft die Methode mit ihrem Namen auf und gibt ihr den Wert in runden Klammern mit: - -```java -int q = quadrat(zahl); -System.out.println("Das Quadrat ist " + q); -``` - -- In der Kopfzeile steht vor dem Namen, was die Methode zurückgibt (`int`). Gibt sie nichts zurück, steht dort `void`. -- Die Parameter stehen mit Datentyp in den runden Klammern: `(int x)`. Mehrere trennst du mit Komma. -- Im Bereich Code schreibst du jede Methode in einen eigenen Reiter, so wie jedes Unterprogramm ein eigenes Struktogramm hat. -- Der Aussprung heißt in Java `break`. Mit `while (true)` baust du eine Schleife, die nur durch `break` endet. - -::: übung l18-java-kasse -titel: Kasse mit break -code: java -auftrag: Schreibe in Java eine Kasse. Sie fragt immer wieder nach einem Preis in ganzen Euro. Bei 0 verlässt sie die Schleife mit `break`. Danach gibt sie aus, wie viele Artikel es waren und was alles zusammen kostet. - Bei 3, 4 und dann 0 erscheint **Artikel: 2** und **Gesamt: 7**. -test: 3, 4, 0 -> Artikel: 2 | Gesamt: 7 -test: 0 -> Artikel: 0 | Gesamt: 0 -test: 10, 2, 5, 0 -> Artikel: 3 | Gesamt: 17 -braucht: endlosschleife, aussprung -tipp: Du brauchst zwei Variablen, die vor der Schleife mit 0 anfangen: eine für die Summe und eine für die Anzahl. -tipp: In `while (true) { … }` liest du den Preis ein. Gleich danach prüfst du `if (preis == 0) { break; }`. Erst dahinter zählst du den Preis zur Summe und die Anzahl um 1 hoch. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Lückentext: Methoden und break - -::: lückentext -titel: Methode und Aussprung -auftrag: Ergänze die fehlenden Wörter. Oben steht die Methode, die ein Quadrat ausrechnet. Darunter steht das Hauptprogramm, das sie aufruft. Im Bereich Code stehen beide in eigenen Reitern. -```java -public static [[int]] quadrat(int x) { - [[return]] x * x; -} -``` -```java -int zahl = 1; -while ([[true]]) { - int q = [[quadrat]](zahl); - if (q > 50) { - [[break]]; - } - System.out.println(q); - zahl = zahl + 1; -} -``` -erklärung: `int` vor dem Methodennamen heißt: Die Methode gibt eine ganze Zahl zurück, und zwar mit `return`. Gibt eine Methode nichts zurück, steht dort `void`. `while (true)` läuft endlos, bis `break` die Schleife verlässt. Ausgabe: 1, 4, 9, 16, 25, 36 und 49. -::: - -## Fehler finden: Raten - -::: übung l18-java-fehler -titel: Versuche zählen reparieren -code: java -auftrag: Beim Zahlenraten ist die gesuchte Zahl 7. Das Programm fragt so lange nach einem Tipp, bis die 7 kommt, und gibt dann die Zahl der Versuche aus. Bei 3, 5 und 7 waren es 3 Versuche, das Programm sagt aber **Versuche: 2**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Versuche: 3 -test: 7 -> Versuche: 1 -test: 1, 2, 3, 7 -> Versuche: 4 -braucht: endlosschleife, aussprung -tipp: Was passiert beim richtigen Tipp? Wird er noch mitgezählt, bevor `break` die Schleife verlässt? -tipp: Zähle zuerst hoch und prüfe danach: `versuche = versuche + 1;` gehört vor das `if` mit dem `break`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Spiel: Was kommt zurück? - -::: spiel frage -titel: Spiel: Was kommt zurück? -intro: Ludwig ruft Methoden auf und springt aus Schleifen. Sag vorher, was ausgegeben wird. Sieben Runden. - -runde: - public static int quadrat(int x) { - return x * x; - } - - int q = quadrat(5); - System.out.println(q); -frage: Was wird ausgegeben? -- 5 -- 10 -- [x] 25 -> Der Aufruf gibt 5 in x, return liefert 5 · 5 zurück. - -runde: - public static void zeigeSumme(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - zeigeSumme(x, x + 1); -frage: Was wird ausgegeben? -- [x] 5 -- 23 -- 3 -> Der erste Wert 2 kommt in a, der zweite 3 in b. In Java rechnet `a + b` mit Zahlen ganz normal. - -runde: -frage: Was liefert eine Methode zurück, vor deren Namen `void` steht? -- eine ganze Zahl -- [x] nichts -- immer 0 -> `void` heißt: kein Rückgabewert. Steht dort `int`, kommt eine ganze Zahl zurück. - -runde: - public static void verdopple(int x) { - x = x * 2; - } - - int zahl = 5; - verdopple(zahl); - System.out.println(zahl); -frage: Was wird ausgegeben? -- [x] 5 -- 10 -- 0 -> Call by value: verdopple bekommt eine Kopie. zahl bleibt 5. - -runde: - public static void leeren(boolean[] plaetze) { - for (int i = 0; i < plaetze.length; i++) { - plaetze[i] = false; - } - } - - boolean[] reihe = {true, true}; - leeren(reihe); - System.out.println(reihe[0]); -frage: Was wird ausgegeben? -- true -- [x] false -> Bei einem Array zeigen reihe und plaetze auf dasselbe Array. Die Änderung sieht auch der Aufrufer. - -runde: - int summe = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - summe = summe + i; - } - System.out.println(summe); -frage: Was wird ausgegeben? -- [x] 6 -- 10 -- 55 -> Bei i = 4 springt `break` sofort aus der Schleife. Gezählt sind nur 1 + 2 + 3. - -runde: - int zahl = 1; - while (true) { - zahl = zahl * 3; - if (zahl > 20) { - break; - } - } - System.out.println(zahl); -frage: Was wird ausgegeben? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Erst 27 ist größer als 20, dann endet die Endlosschleife mit break. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine große Aufgabe in Teilprobleme zerlegen. → Große Aufgaben zerlegen -- Ich kann einen Aufruf einfügen und ein Unterprogramm in einem eigenen Reiter bauen. → Der Aufruf -- Ich kann einem Unterprogramm mit Parametern Werte übergeben. → Werte übergeben: Parameter -- Ich kann mit return einen Rückgabewert liefern und ihn beim Aufruf speichern. → Ergebnis zurückgeben: return -- Ich kann erklären, warum ein Unterprogramm eine Zahl nicht verändern kann, ein Array aber schon. → Kopie oder Original: call by value und call by reference -- Ich kann erklären, was ein Aussprung macht und warum er in einer Verzweigung steht. → Der Aussprung -- Ich kann eine Schleife mit einem Aussprung oder über ihre Bedingung beenden. → Aussprung in der Simulation -- Ich kann in Java eine Methode erkennen und eine Schleife mit `break` verlassen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet ein Aufruf-Kasten mit dem Text `zeigeErgebnis()`? -→ Der Aufruf -- [ ] Das Programm endet hier. -- [x] Hier wird das Unterprogramm zeigeErgebnis() ausgeführt, danach geht es weiter. -- [ ] Hier wird das Wort zeigeErgebnis() ausgegeben. - -? Wo geht es nach einem Aussprung weiter? -→ Der Aussprung -- [ ] Am Anfang der Schleife -- [x] Beim ersten Kasten nach der Schleife -- [ ] Ganz am Anfang des Programms -> Der Aussprung verlässt die Schleife, in der er steht. In Java, Python und JavaScript heißt er break. - -? Warum steht ein Aussprung meistens in einer Verzweigung? -→ Der Aussprung -- [x] Damit die Schleife nur verlassen wird, wenn eine Bedingung zutrifft -- [ ] Weil ein Aussprung sonst nicht gespeichert werden kann -- [ ] Weil eine Verzweigung selbst eine Schleife ist -> Ohne Verzweigung würde die Schleife schon im ersten Durchlauf verlassen. - -? Wo findet die Simulation das Unterprogramm zu einem Aufruf `zeigeErgebnis()`? -→ Der Aufruf -- [ ] Es muss im selben Struktogramm stehen. -- [x] In einem offenen Reiter mit dem Titel zeigeErgebnis() -- [ ] Die Simulation kann keine Unterprogramme ausführen. -> Baue jedes Unterprogramm in einem eigenen Reiter und gib ihm als Titel den Namen aus dem Aufruf. - -? Das Unterprogramm hat die Kopfzeile `void zeigeSumme(int a, int b)`. Welcher Aufruf passt? -→ Werte übergeben: Parameter -- [ ] zeigeSumme() -- [x] zeigeSumme(3, 4) -- [ ] int s = zeigeSumme(3, 4) -> Zwei Parameter brauchen zwei Werte. Und `void` heißt: Es kommt nichts zurück, das man speichern könnte. - -? Nach `int q = quadrat(5)` steht in q die 25. Was steht im Unterprogramm quadrat am Ende? -→ Ergebnis zurückgeben: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> Mit return gibt das Unterprogramm den Wert an den Aufruf zurück. Eine Ausgabe würde ihn nur anzeigen. - -? `void verdopple(int x)` rechnet `x = x * 2`. Im Hauptprogramm steht `int zahl = 5` und dann der Aufruf `verdopple(zahl)`. Welchen Wert hat zahl danach? -→ Kopie oder Original: call by value und call by reference -- [x] 5 -- [ ] 10 -- [ ] 0 -> Wertübergabe (call by value): verdopple hat nur eine Kopie von zahl verändert. - -? `void leeren(boolean[] plaetze)` setzt alle Fächer auf false. Was gilt nach `leeren(reihe)` für das Array reihe im Hauptprogramm? -→ Kopie oder Original: call by value und call by reference -- [ ] Es ist unverändert, weil das Unterprogramm eine Kopie bekommt. -- [x] Alle Fächer sind false, weil plaetze und reihe dasselbe Array sind. -- [ ] Das Array ist gelöscht. -> Referenzübergabe (call by reference): Ein Array wird als Referenz übergeben. Änderungen an den Fächern sieht auch der Aufrufer. - -? Wie heißt ein Unterprogramm in Java? -→ Jetzt in Java -- [ ] Klasse -- [x] Methode -- [ ] Schleife -> In Java ist ein Unterprogramm eine Methode, zum Beispiel die `main`-Methode. Je nach Art sagt man auch Prozedur oder Funktion. - -? Viele Bausteine haben in Schulbüchern einen zweiten Namen. Ordne jedem Begriff aus VisuStruct den anderen Namen zu. -→ Jetzt in Java -= Verzweigung -> Bedingte Anweisung -= Fallauswahl -> Mehrfachauswahl -= Schleife -> Wiederholungsstruktur -= Array -> Feld -= Unterprogramm -> Methode -> Beide Namen meinen dasselbe. Wenn du einen davon kennst, verstehst du auch den anderen. - -? Was steht in Java in der Kopfzeile einer Methode, die nichts zurückgibt? -→ Ergebnis zurückgeben: return -- [ ] int -- [x] void -- [ ] return -> void heißt „leer“: Die Methode macht etwas, gibt aber keinen Wert zurück. -::: diff --git a/web/lessons/de/19-suchen-und-sortieren.md b/web/lessons/de/19-suchen-und-sortieren.md deleted file mode 100644 index c72fe26e..00000000 --- a/web/lessons/de/19-suchen-und-sortieren.md +++ /dev/null @@ -1,352 +0,0 @@ ---- -titel: Suchen und Sortieren -bereich: Grundlagen -kurz: Mit Spielkarten verstehen, wie lineare und binäre Suche und Bubblesort arbeiten und warum manche Verfahren viel schneller sind. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, wie die lineare Suche ein Array durchsucht -- eine lineare Suche als Struktogramm bauen -- erklären, warum die binäre Suche nur bei sortierten Werten funktioniert -- begründen, warum die binäre Suche bei vielen Werten viel schneller ist -- einen Durchgang von Bubblesort mit Spielkarten vormachen -- in Java ein Array durchsuchen und Treffer zählen -::: - -## Eine Karte suchen - -Du hast einen Stapel Spielkarten verdeckt in einer Reihe vor dir liegen. Gesucht ist die 9. Was machst du? Du drehst eine Karte nach der anderen um, bis du die 9 findest oder alle Karten umgedreht hast. - -::: grafik ss-lineare-suche.svg -Die Karten liegen durcheinander. Jede umgedrehte Karte ist ein Vergleich: Ist das die 9? -::: - -Genau so arbeitet die **lineare Suche**: Sie geht das Array vom Index 0 bis zum Ende durch und vergleicht jedes Fach mit dem gesuchten Wert. Sie funktioniert immer, auch wenn die Werte durcheinander liegen. - -Im Array stehen unsere Karten als Zahlen. Bube, Dame und König lassen wir weg. - -::: diagramm ss-lineare-suche.visustruct -Die Schleife läuft, bis die Karte gefunden ist oder kein Fach mehr übrig ist. Die Variable vergleiche zählt mit, wie oft verglichen wurde. Suche in der Simulation die 2, die 27 und die 4. -::: - -> Ist die Karte nicht dabei, bleibt `position` bei **-1**. Einen Index -1 gibt es nicht, darum bedeutet -1 hier „nicht gefunden“. - -## Übung: Karte suchen - -::: übung ss-karte-suchen -titel: Karte suchen -auftrag: Lege das Array `int[] karten = {8, 4, 11, 4, 6}` an und frage nach einer Karte. Gib die Position der **ersten** passenden Karte aus, z. B. `Position: 1` bei der 4. Ist die Karte nicht dabei, gib `Nicht gefunden` aus. -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 8 -> Position: 0 -test: 9 -> Nicht gefunden -braucht: whileSchleife, verzweigung -tipp: Gehe das Array von vorn nach hinten durch und vergleiche jede Karte mit der gesuchten. -tipp: Baue das Beispiel oben nach. Die Variable vergleiche brauchst du hier nicht. Die Schleife muss aufhören, sobald `position` nicht mehr -1 ist, sonst findest du bei der 4 die zweite statt der ersten. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Binäre Suche: sortiert geht es schneller - -Jetzt liegen die Karten **sortiert** vor dir, von klein nach groß. Gesucht ist die 27. Du drehst nicht mehr vorne die erste Karte um, sondern die **mittlere**: - -- Ist sie die gesuchte, bist du fertig. -- Ist sie kleiner, kann die gesuchte Karte nur noch rechts liegen. Die ganze linke Hälfte fällt weg. -- Ist sie größer, fällt die rechte Hälfte weg. - -Mit dem Rest machst du dasselbe noch einmal. Bei jedem Vergleich halbiert sich der Stapel. - -::: grafik ss-binaere-suche.svg -Drei Vergleiche statt vierzehn: Mit jeder Mitte fällt die Hälfte der übrigen Karten weg. -::: - -Im Struktogramm merken sich zwei Variablen den Bereich, in dem noch gesucht wird: `links` und `rechts`. Die Mitte ist `(links + rechts) / 2`. Weil das eine Ganzzahldivision ist, kommt immer ein ganzer Index heraus. - -::: diagramm ss-binaere-suche.visustruct -Dasselbe Array wie bei der linearen Suche. Suche in der Simulation wieder die 27 und vergleiche die Ausgabe Vergleiche mit der linearen Suche. -::: - -> Die binäre Suche funktioniert **nur bei sortierten Werten**. Bei durcheinander liegenden Karten weißt du nach dem Blick auf die Mitte nicht, in welcher Hälfte du weitersuchen musst. - -::: angeberwissen -Ich denke mir eine Zahl von 1 bis 100, du darfst raten und ich sage nur „größer“ oder „kleiner“. Mit der binären Suche brauchst du höchstens **7** Versuche. Bei einer Zahl bis eine Million sind es höchstens 20, bei einer Milliarde 30. Jede Verdopplung kostet nur einen einzigen Versuch mehr. So suchen auch Datenbanken in Millionen von Einträgen in Sekundenbruchteilen. Ich suche übrigens linear: Ich fresse die Wiese von links nach rechts ab. -::: - -## Wie schnell ist das? - -Wie gut ein Verfahren ist, misst man an der Zahl der Vergleiche, die es **im schlimmsten Fall** braucht. Bei der linearen Suche ist das der Fall, dass die Karte ganz hinten liegt oder fehlt. - -::: grafik ss-vergleiche.svg -Bei 100 Karten braucht die lineare Suche bis zu 100 Vergleiche, die binäre höchstens 7. -::: - -| Anzahl Karten | Lineare Suche | Binäre Suche | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1 000 | 1 000 | 10 | -| 1 000 000 | 1 000 000 | 20 | - -Die lineare Suche wächst im gleichen Maß wie die Kartenzahl. Bei der binären Suche kommt bei doppelt so vielen Karten nur **ein** Vergleich dazu. - -## Sortieren mit Bubblesort - -Für die binäre Suche müssen die Werte sortiert sein. Eines der einfachsten Sortierverfahren ist **Bubblesort**. Du gehst die Reihe von links nach rechts durch und vergleichst immer **zwei Nachbarn**. Steht die größere Karte links, tauschst du die beiden. - -::: grafik ss-bubblesort.svg -Nach einem Durchgang steht die größte Karte ganz rechts. -::: - -Nach dem ersten Durchgang steht die größte Karte ganz rechts, wie eine Luftblase, die nach oben steigt. Daher kommt der Name. Danach wiederholst du den Durchgang für die übrigen Karten. Bei 5 Karten brauchst du 4 Durchgänge. - -Im Struktogramm stecken zwei For-Schleifen ineinander. Die äußere zählt die Durchgänge, die innere vergleicht die Nachbarn `karten[i]` und `karten[i + 1]`. Das Tauschen kennst du schon: Es braucht eine **Hilfsvariable**. - -::: diagramm ss-bubblesort.visustruct -Die innere Schleife wird mit jedem Durchgang kürzer, weil rechts schon die größten Karten richtig stehen. Gehe es in der Simulation mit Schritt durch und beobachte das Array unter Variablen. -::: - -Bubblesort ist leicht zu verstehen, aber langsam. Bei n Karten braucht es bis zu n · (n − 1) / 2 Vergleiche: bei 10 Karten 45, bei 100 Karten schon 4 950 und bei 1 000 Karten fast eine halbe Million. Schnellere Verfahren wie Quicksort lernst du vielleicht später kennen. - -## Übung: Absteigend sortieren - -::: übung ss-absteigend -titel: Absteigend sortieren -auftrag: Das Startdiagramm liest 5 Karten ein und sortiert sie mit Bubblesort **aufsteigend**, also von klein nach groß. Ändere es so, dass die Karten **absteigend** ausgegeben werden, die größte zuerst. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Schau dir die Bedingung in der Verzweigung an. Sie entscheidet, wann zwei Nachbarn getauscht werden. -tipp: Du musst nur ein einziges Zeichen ändern. Wann sollen zwei Nachbarn getauscht werden, wenn die größere Karte nach links soll? -tipp: Ändere in der Verzweigung `karten[i] > karten[i + 1]` in `karten[i] < karten[i + 1]`. Dann wird getauscht, wenn rechts die größere Karte liegt, und die große Karte wandert nach links. -lösung: ss-absteigend-loesung.visustruct -::: - -## Jetzt in Java - -Die lineare Suche aus der Übung sieht in Java so aus: - -```java editor -int[] karten = {8, 4, 11, 4, 6}; -int gesucht = 4; -int position = -1; -int i = 0; -while (position == -1 && i < karten.length) { - if (karten[i] == gesucht) { - position = i; - } - i = i + 1; -} -System.out.println("Position: " + position); -``` - -- `&&` heißt „und“: Die Schleife läuft nur, solange noch nichts gefunden ist **und** es noch Karten gibt. -- Ist der false-Zweig leer, lässt du `else` einfach weg. -- Der Tausch beim Bubblesort braucht auch in Java eine Hilfsvariable: `int hilf = karten[i];` - -::: übung l19-java-zaehlen -titel: Karten zählen -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Schreibe in Java ein Programm, das nach einer Karte fragt und zählt, wie oft sie vorkommt. Es gibt **Anzahl:** und die Zahl aus. - Bei 4 erscheint **Anzahl: 2**. -test: 4 -> Anzahl: 2 -test: 8 -> Anzahl: 1 -test: 9 -> Anzahl: 0 -braucht: forSchleife, verzweigung -tipp: Anders als bei der Suche hörst du beim ersten Treffer nicht auf. Eine For-Schleife geht alle Karten durch. -tipp: `for (int i = 0; i < karten.length; i++)` und darin `if (karten[i] == gesucht) { anzahl = anzahl + 1; }`. `anzahl` startet vor der Schleife mit 0. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Lückentext: Bubblesort - -::: lückentext -titel: Karten sortieren -auftrag: Das Programm sortiert die Karten mit Bubblesort aufsteigend. Ergänze die fehlenden Wörter und Zeichen. -```java -int[] karten = {5, 2, 8, 1}; -for (int runde = 0; runde < karten.[[length]] - 1; runde++) { - for (int i = 0; i < karten.length - 1; i++) { - if (karten[i] [[>]] karten[i + 1]) { - [[int]] hilf = karten[i]; - karten[i] = karten[i + 1]; - karten[i + 1] = [[hilf]]; - } - } -} -for (int i = 0; i < karten.length; i++) { - System.out.println(karten[i]); -} -``` -erklärung: Ist eine Karte größer als ihre rechte Nachbarin, werden die beiden getauscht, mit einer Hilfsvariablen wie beim Gläsertauschen. Nach `length - 1` Runden ist alles sortiert: 1, 2, 5, 8. -::: - -## Fehler finden: Suche - -::: übung l19-java-fehler -titel: Lineare Suche reparieren -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Das Programm soll die **erste** Position der gesuchten Karte ausgeben, bei 4 also **Position: 1**. Es gibt aber **Position: 3** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l19-java-fehler-start.visustruct -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 9 -> Position: -1 -test: 8 -> Position: 0 -braucht: whileSchleife, verzweigung -tipp: Die Schleife läuft immer bis zur letzten Karte. Was passiert, wenn die 4 ein zweites Mal kommt? -tipp: Die Schleife soll aufhören, sobald etwas gefunden ist: `while (position == -1 && i < karten.length)`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l19-java-laeuft-nicht -titel: Karte suchen -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Das Programm sucht die 11 und soll **Position: 2** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Position: 2 -braucht: forSchleife, verzweigung -tipp: Vergleiche die Namen in der Bedingung mit den Namen, die oben angelegt werden. -tipp: Die gesuchte Karte steht in `gesucht` mit kleinem g: `if (karten[i] == gesucht) {` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Suchprofi - -::: spiel frage -titel: Spiel: Suchprofi -intro: Ludwig sucht und sortiert Karten. Wie schnell geht das, und was kommt heraus? Sieben Runden. - -runde: - int[] karten = {8, 4, 11, 4, 6}; - int gesucht = 4; - int position = -1; - int i = 0; - while (position == -1 && i < karten.length) { - if (karten[i] == gesucht) { - position = i; - } - i = i + 1; - } - System.out.println("Position: " + position); -frage: Was wird ausgegeben? -- [x] Position: 1 -- Position: 3 -- Position: 2 -> Die Suche hört bei der ersten 4 auf. Sie liegt im Fach 1, gezählt wird ab 0. - -runde: - int[] karten = {8, 4, 11, 4, 6}; - int gesucht = 7; -frage: Mit derselben linearen Suche: Was steht danach in position? -- 0 -- 5 -- [x] -1 -> Die 7 ist nicht dabei. position bleibt bei -1, das bedeutet „nicht gefunden“. - -runde: -frage: Bei welchen Karten funktioniert die binäre Suche? -- bei allen -- [x] nur bei sortierten -- nur bei weniger als 100 -> Nur wenn die Karten sortiert sind, weißt du nach dem Blick auf die Mitte, in welcher Hälfte du weitersuchen musst. - -runde: -frage: Wie viele Vergleiche braucht die binäre Suche bei 100 Karten höchstens? -- 50 -- [x] 7 -- 100 -> Jeder Vergleich halbiert den Stapel. 7-mal halbieren reicht bis 128 Karten, also auch für 100. - -runde: -frage: Statt 1 000 Karten sind es jetzt 2 000. Wie viele Vergleiche braucht die binäre Suche höchstens mehr? -- [x] einen -- doppelt so viele -- 1 000 -> Der erste Vergleich halbiert 2 000 auf 1 000. Danach geht es weiter wie vorher. - -runde: - int[] karten = {5, 3, 8, 1}; -frage: Ein Durchgang Bubblesort vergleicht von links nach rechts immer zwei Nachbarn. Wie liegen die Karten danach? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 und 3 tauschen, 5 und 8 bleiben, 8 und 1 tauschen. Die größte Karte ist ganz rechts. - -runde: -frage: Bubblesort tauscht `karten[i]` und `karten[i + 1]`. Was braucht es dafür? -- [x] eine Hilfsvariable -- ein zweites Array -- eine binäre Suche -> Wie beim Tauschen zweier Variablen: `int hilf = karten[i];`, sonst geht ein Wert verloren. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, wie die lineare Suche ein Array durchsucht. → Eine Karte suchen -- Ich kann eine lineare Suche als Struktogramm bauen. → Übung: Karte suchen -- Ich kann erklären, warum die binäre Suche nur bei sortierten Werten funktioniert. → Binäre Suche: sortiert geht es schneller -- Ich kann begründen, warum die binäre Suche bei vielen Werten viel schneller ist. → Wie schnell ist das? -- Ich kann einen Durchgang von Bubblesort mit Spielkarten vormachen. → Sortieren mit Bubblesort -- Ich kann in Java ein Array durchsuchen und Treffer zählen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welche Voraussetzung braucht die binäre Suche? -→ Binäre Suche: sortiert geht es schneller -- [ ] Das Array muss genau 100 Fächer haben. -- [x] Die Werte müssen sortiert sein. -- [ ] Der gesuchte Wert muss vorkommen. -> Nur bei sortierten Werten weißt du nach dem Blick auf die Mitte, in welcher Hälfte du weitersuchst. - -? Ein sortiertes Array hat 1 000 Werte. Wie viele Vergleiche braucht die binäre Suche höchstens? -→ Wie schnell ist das? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1 000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: Nach etwa 10 Halbierungen bleibt ein Wert übrig. - -? Was vergleicht Bubblesort in jedem Schritt? -→ Sortieren mit Bubblesort -- [ ] Die erste und die letzte Karte -- [x] Zwei benachbarte Karten -- [ ] Jede Karte mit der Mitte -> Steht die größere Karte links, werden die Nachbarn getauscht. - -? Wo steht nach dem ersten Durchgang von Bubblesort die größte Karte? -→ Sortieren mit Bubblesort -- [ ] Ganz links -- [ ] In der Mitte -- [x] Ganz rechts -> Sie wird bei jedem Vergleich weitergetauscht und steigt wie eine Blase nach rechts. - -? Was bedeutet `position = -1` in der linearen Suche? -→ Eine Karte suchen -- [x] Die Karte wurde (noch) nicht gefunden. -- [ ] Die Karte liegt ganz hinten. -- [ ] Das Array ist leer. - -? Ordne jedem Verfahren die passende Beschreibung zu. -→ Eine Karte suchen -= Lineare Suche -> eine Karte nach der anderen ansehen -= Binäre Suche -> die Mitte ansehen und eine Hälfte weglassen -= Bubblesort -> Nachbarn vergleichen und tauschen - -? Warum nimmst du zum Zählen eine For-Schleife und keine Suche, die beim ersten Treffer aufhört? -→ Jetzt in Java -- [ ] Eine For-Schleife ist immer schneller. -- [x] Du musst jede Karte ansehen, sonst fehlen spätere Treffer. -- [ ] Eine While-Schleife kann keine Arrays durchlaufen. -> Die Karte kann mehrmals vorkommen. Darum gehst du alle Karten bis zum Ende durch. -::: diff --git a/web/lessons/de/20-java.md b/web/lessons/de/20-java.md deleted file mode 100644 index c3a0706e..00000000 --- a/web/lessons/de/20-java.md +++ /dev/null @@ -1,472 +0,0 @@ ---- -titel: Struktogramm und Code -bereich: Vom Struktogramm zum Code -kurz: Alle Elemente in Java auf einen Blick, Code erzeugen, im Bereich Code Java schreiben, ausführen und Fehler suchen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Code-Ansicht öffnen und Java als Sprache wählen -- Ausgabe, Eingabe, Verzweigung und Schleifen in Java erkennen -- den Unterschied zwischen Snippet und Class erklären -- erklären, wie Java-Code mit Compiler, Bytecode und JVM auf jedem Betriebssystem läuft -- JDK und JRE unterscheiden -- erklären, warum man ein C++-Programm für jedes Betriebssystem neu kompilieren muss, ein Java-Programm aber nicht -- ein kurzes Java-Programm als Struktogramm nachbauen -- im Bereich Code Java tippen und das entstehende Struktogramm lesen -- ein Programm mit ▶︎ ausführen und Ein- und Ausgaben in der Konsole nutzen -- an der roten Zeilennummer einen Syntaxfehler oder Laufzeitfehler finden -- Haltepunkte setzen und ein Programm im Einzelschritt verfolgen -::: - -## Die Code-Ansicht - -In den Lektionen 4 bis 19 hast du jedes Element schon einmal in Java geschrieben. Hier siehst du alles auf einen Blick und lernst, wie VisuStruct aus einem ganzen Struktogramm ein Java-Programm macht. - -Ein Struktogramm beschreibt einen Ablauf. Damit ein Computer ihn ausführt, brauchst du ein Programm in einer Programmiersprache. VisuStruct schreibt diesen Code für dich. - -Klicke bei den Werkzeugen auf [Code](#zeige-code) (Symbol mit spitzen Klammern). Rechts erscheint der **Code-Generator**: - -- Oben wählst du die Sprache: **Java**, **Python** oder **JavaScript**. -- Bei Java wählst du zusätzlich **Snippet** (nur die Anweisungen) oder **Class** (eine vollständige Java-Datei). -- **Diagramm-Text als Kommentare** schreibt den Text jedes Kastens als Kommentar über den Code. -- **Kopieren** legt den Code in die Zwischenablage, **Java speichern …** speichert eine `.java`-Datei. - -Der Code ändert sich sofort mit, wenn du am Struktogramm etwas änderst. - -::: angeberwissen -Nassi hat später an der Programmiersprache Ada mitgearbeitet und war bei Apple für Software zuständig. Wer Struktogramme erfindet, landet also nicht im Ziegenstall, sondern bei Apple. -::: - -::: diagramm l17-beispiel.visustruct -Ein kleines Programm mit Eingabe, For-Schleife und Verzweigung. Öffne es und schau dir den Java-Code dazu an. -::: - -## So sieht das Beispiel in Java aus - -Für das Diagramm oben erzeugt VisuStruct als **Snippet** diesen Code: - -``` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Reihe?"); -int zahl = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int ergebnis = zahl * i; - System.out.println(i + " mal " + zahl + " = " + ergebnis); -} -if (zahl > 10) { - System.out.println("Das war schwer!"); -} else { - System.out.println("Das war leicht."); -} -``` - -Jede Anweisung endet in Java mit einem **Semikolon** `;`. Was in einem Kasten steht, steht in Java in **geschweiften Klammern** `{ }`. Die Einrückung zeigt dir, was zusammengehört. - -## Die Elemente in Java - -| Element | Java | Lektion | -|---|---|---| -| Anweisung | `int summe = 0;` | 4, 5 | -| Ausgabe | `System.out.println(summe);` | 6 | -| Eingabe | `int zahl = scanner.nextInt();` | 6 | -| Verzweigung | `if (bedingung) { … } else { … }` | 8, 9 | -| Fallauswahl | `switch (wert) { case 1 -> { … } default -> { … } }` | 10 | -| While-Schleife | `while (bedingung) { … }` | 11 | -| Do-while-Schleife | `do { … } while (bedingung);` | 12 | -| For-Schleife | `for (int i = 0; i < 5; i++) { … }` | 13 | -| Endlosschleife | `while (true) { … }` | 11, 18 | -| Aussprung | `break;` | 18 | -| Aufruf | `berechne();` | 18 | -| Array | `int[] zahlen = new int[5];` | 15, 16 | -| Text (String) | `String name = scanner.next();` | 17 | - -Für Eingaben steht ganz oben einmal `import java.util.Scanner;` und `Scanner scanner = new Scanner(System.in);`. Vor jeder Eingabe gibt VisuStruct die Frage mit `System.out.println(…)` aus. - -Ist der Nein-Zweig einer Verzweigung leer, erzeugt VisuStruct trotzdem einen `else`-Teil, in dem nur der Kommentar `// empty element` steht. Wenn du **Leeren else-Zweig im Code ausgeben** ausschaltest, fällt der leere `else`-Teil weg. - -## Snippet oder Class - -Ein **Snippet** kannst du in ein vorhandenes Java-Programm in die `main`-Methode einfügen. Mit **Class** bekommst du eine vollständige Datei, die du sofort übersetzen und starten kannst: - -``` -import java.util.Scanner; - -public class Einmaleins { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Welche Reihe?"); - int zahl = scanner.nextInt(); - ... - } // Ende main -} // Ende Klasse -``` - -Die Kommentare `// Ende main` und `// Ende Klasse` hinter den letzten beiden Klammern sind nicht nötig, aber sehr hilfreich: So siehst du sofort, welche Klammer was schließt, und vergisst keine. - -Der Name der Klasse kommt aus dem **Titel** des Diagramms. Die Datei muss genauso heißen, hier also `Einmaleins.java`. Das erledigt **Java speichern …** für dich. - -> Der erzeugte Code ist ein guter Anfang, aber prüfe ihn. Legst du eine Variable zuerst an und liest sie später mit einer Eingabe neu ein, schreibt VisuStruct trotzdem `int pin = scanner.nextInt();`. Java meldet dann, dass es `pin` schon gibt. Lösche in dieser Zeile einfach das `int`. - -## Wie Java auf jedem Computer läuft - -Deine Datei `Einmaleins.java` ist **Quellcode**: Text für Menschen. Der Prozessor deines Computers versteht ihn nicht direkt. Deshalb läuft Java in zwei Schritten: - -1. Der **Compiler** `javac` übersetzt den Quellcode in **Bytecode**. Heraus kommt die Datei `Einmaleins.class`. -2. Die **JVM** (Java Virtual Machine) liest den Bytecode und übersetzt ihn beim Ausführen in **Maschinencode**, also in Befehle für den Prozessor deines Computers. - -::: grafik l20-jvm.svg -Einmal kompilieren: Derselbe Bytecode läuft auf jeder JVM. -::: - -Den Bytecode gibt es nur einmal. Die JVM gibt es dagegen für Windows, macOS, Linux und viele andere Systeme. Deshalb läuft dieselbe `.class`-Datei auf jedem Computer, auf dem eine JVM installiert ist. Das nennt man **plattformunabhängig**. Der Werbespruch dazu heißt **„Write once, run anywhere“**: einmal schreiben, überall ausführen. - -::: angeberwissen -Einen der ersten Compiler hat 1952 **Grace Hopper** gebaut, die du schon aus Lektion 7 kennst. Viele sagten ihr damals, Computer könnten nur rechnen und keine Programme übersetzen. Sie hat es trotzdem gemacht. -::: - -Zum Vergleich **C++**: Dort übersetzt der Compiler den Quellcode direkt in Maschinencode für genau einen Prozessor und ein Betriebssystem. Das fertige Programm läuft nur dort. Für Windows, macOS und Linux musst du es jeweils neu kompilieren. - -::: grafik l20-cpp.svg -C++ wird für jedes System einzeln zu Maschinencode kompiliert. -::: - -| | C++ | Java | -|---|---|---| -| Der Compiler erzeugt | Maschinencode | Bytecode | -| Wie oft kompilieren? | für jedes System neu | einmal | -| Was braucht der Computer zum Ausführen? | nichts weiter | eine JVM | - -Ist Java dadurch langsamer? Kaum. Programmteile, die oft laufen, zum Beispiel Schleifen, übersetzt der **JIT-Compiler** der JVM („just in time“, gerade rechtzeitig) einmal komplett in Maschinencode. Danach laufen sie fast so schnell wie C++. - -> Stell dir die JVM wie einen kleinen Computer aus Software vor, fast wie ein eigenes Betriebssystem nur für Java-Programme. Genau genommen ist sie aber kein Betriebssystem, sondern eine **virtuelle Maschine**: ein Programm, das selbst auf Windows, macOS oder Linux läuft und darin einen gedachten Computer nachspielt. - -Beim Installieren begegnen dir zwei Abkürzungen: - -| Abkürzung | Was steckt drin? | Wofür? | -|---|---|---| -| **JRE** (Java Runtime Environment) | die JVM und fertige Bausteine wie `Scanner` | Java-Programme ausführen | -| **JDK** (Java Development Kit) | die JRE und dazu den Compiler `javac` und weitere Werkzeuge | Java-Programme schreiben und übersetzen | - -Zum Programmieren brauchst du also das JDK. Heute lädt man meist nur noch das JDK herunter, die JRE steckt darin. Für VisuStruct im Browser musst du nichts davon installieren. - -::: angeberwissen -Mit einem JDK kannst du alles auch selbst in der Konsole machen, also im Terminal oder in der Eingabeaufforderung: - -``` -java -version -javac Einmaleins.java -java Einmaleins -``` - -- `java -version` zeigt, welche Java-Version installiert ist. Kommt eine Fehlermeldung, fehlt Java noch. -- `javac Einmaleins.java` startet den Compiler. Danach hast du zwei Dateien im Ordner: `Einmaleins.java` mit deinem Quellcode und neu `Einmaleins.class` mit dem Bytecode. -- `java Einmaleins` startet die JVM mit deinem Bytecode. Den Klassennamen schreibst du hier ohne `.class`. - -Klappt `java -version`, aber `javac -version` nicht, ist nur eine JRE installiert. Dann fehlt dir das JDK. -::: - -::: angeberwissen -Jede übersetzte Java-Datei (`.class`) beginnt mit denselben vier Bytes, in Hexadezimal geschrieben: `CA FE BA BE`. Daran erkennt der Computer, dass es Java ist. Das „Kaffee-Baby“ hat sich James Gosling ausgedacht, passend zum Kaffee im Namen. -::: - -## Der Java-Sprachmodus - -Unter dem [Zahnrad](#zeige-einstellungen) oben rechts kannst du **Code-Keywords** einschalten. Dann heißen die Bausteine im Bereich Elemente so wie die Befehle in Java: `if`, `switch`, `for`, `while`, `do`, `while(true)`, `break` und `call`. Die übrige Oberfläche bleibt in deiner Sprache. - -Das hilft dir, die Kästen und die Java-Befehle einander zuzuordnen. Dein Diagramm selbst bleibt dabei unverändert. - -## Java direkt schreiben - -Bisher hast du zuerst das Struktogramm gezeichnet und dann den Code angesehen. Im Bereich [Code](#zeige-codelive) geht es auch andersherum. Klicke rechts auf die schmale Leiste **Code**. Neben dem Struktogramm erscheint eine Spalte, in die du Java selbst tippst. - -- **Struktogramm zu Code:** Änderst du etwas im Struktogramm, ändert sich der Java-Code sofort mit. -- **Code zu Struktogramm:** Tippst du Java, baut VisuStruct beim Tippen das Struktogramm dazu. Fehlt noch etwas, zum Beispiel eine schließende Klammer `}`, steht unter dem Code ein Hinweis. -- **Vorschläge:** Beim Tippen schlägt VisuStruct Vorlagen vor, etwa für `if`, `for` oder `while`. Mit der Eingabetaste übernimmst du eine Vorlage. Grau geschriebene Platzhalter ersetzt du einfach durch deinen Text. -- **Einrücken:** Ist dein Code durcheinandergeraten, klicke oben im Bereich Code auf den Knopf **Einrücken**. Jede Ebene steht dann vier Leerzeichen tiefer, wie in Eclipse oder IntelliJ. Hast du Zeilen markiert, rückt VisuStruct nur diese ein. - -So kannst du beide Wege ausprobieren. Erst das Struktogramm zeichnen ist gut, um einen Ablauf zu planen. Direkt Code schreiben ist schneller, wenn du schon weißt, was du willst. Das Struktogramm hilft dir dann, deinen Code zu kontrollieren. - -## Das Programm ausführen - -Oben im Bereich Code startet [▶︎ Ausführen](#zeige-ausfuehren) dein Programm, **■ Stopp** bricht es ab, zum Beispiel bei einer Endlosschleife. Was dein Programm ausgibt, erscheint unten in der [Konsole](#zeige-konsole). Fragt es nach einer Eingabe, tippst du sie dort ein und drückst die Eingabetaste. - -Die Grenze zwischen Code und Konsole kannst du mit der Maus nach oben oder unten ziehen. - -Geht etwas schief, wird die Zeilennummer rot. Bei einem Syntaxfehler ist die Stelle zusätzlich rot unterstrichen, und es steht z. B. **Zeile 2: Hier fehlt noch: ;** darunter. Dann startet ▶︎ das Programm gar nicht erst. Bricht das Programm beim Ausführen ab, ist es ein Laufzeitfehler, und die Konsole nennt die Zeile, z. B. **Zeile 3: Division durch null.** Die drei Fehlerarten kennst du aus der Lektion „Simulation und Schreibtischtest“. - -Weiß VisuStruct genau, was fehlt, steht am Ende der Zeile eine 💡. Fährst du mit der Maus darauf, siehst du den Vorschlag, z. B. **Vorschlag: import java.util.Scanner; einfügen**. Ein Klick übernimmt ihn. Lies trotzdem nach, was eingefügt wurde, damit du es beim nächsten Mal selbst weißt. - -> Der Bereich Code versteht die Java-Befehle, die es auch als Kasten im Struktogramm gibt. Eigene Klassen und Objekte kennt es noch nicht. Fenster mit Swing gehen trotzdem: Schalte dafür im Zahnrad **Echtes Java (mit Swing)** ein (Lektion „Dein erstes Fenster“). - -::: angeberwissen -Profis nennen ein Programm, in dem man Code schreibt, ausführt und Fehler sucht, eine **IDE** (Integrated Development Environment). Mit dem Bereich Code ist VisuStruct eine kleine IDE mit eingebautem Struktogramm. -::: - -## Fehler suchen mit dem Debugger - -Manchmal läuft ein Programm, rechnet aber falsch. Das ist ein Logikfehler. Dann hilft der **Debugger**: Er führt dein Programm langsam aus, und du schaust ihm dabei zu. - -- **Haltepunkt:** Klicke links auf eine Zeilennummer. Ein roter Punkt erscheint. [▶︎ Ausführen](#zeige-ausfuehren) hält vor dieser Zeile an. Ein zweiter Klick nimmt den Punkt wieder weg. -- **Gelbe Zeile:** Sie wird als Nächstes ausgeführt. Im Struktogramm ist der passende Kasten markiert. -- **Variablen:** Unter dem Code steht, welchen Wert jede Variable gerade hat. Was sich seit dem letzten Anhalten geändert hat, leuchtet kurz gelb. -- **Einzelschritt:** Der Knopf [Einzelschritt](#zeige-einzelschritt) neben ▶︎ führt genau eine Zeile aus und hält wieder an. Ohne Haltepunkt startet er das Programm und hält vor der ersten Zeile. -- **Weiter:** ▶︎ läuft bis zum nächsten Haltepunkt oder bis zum Ende. **■ Stopp** bricht ab. - -Probier es mit diesem Programm aus. Setze einen Haltepunkt in Zeile 3 und drücke mehrmals ▶︎. Wie wachsen `i` und `summe`? - -``` -int summe = 0; -for (int i = 1; i <= 4; i++) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -``` - -::: ludwig -Das ist ein Schreibtischtest, nur dass der Computer für dich mitschreibt. Überleg dir vor jedem Schritt, welchen Wert die Variable gleich haben müsste. Stimmt die Tabelle nicht mit deiner Vermutung überein, hast du die Stelle mit dem Fehler gefunden. -::: - -## Übung: Erst Code, dann Struktogramm - -Diesmal zeichnest du nicht. Öffne den Bereich Code und tippe diesen Java-Code ab: - -``` -System.out.println("Startzahl?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -``` - -::: übung l20-countdown -titel: Countdown -bereich: beide -auftrag: Tippe den Java-Code oben im Bereich Code ab. Führe ihn mit ▶︎ aus und gib in der Konsole eine Startzahl ein. - Sieh dir dann das Struktogramm an, das dabei entstanden ist, und klicke auf Prüfen. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Öffne den Bereich Code mit der schmalen Leiste ganz rechts und tippe einfach los. -tipp: Achte auf das Semikolon am Ende jeder Anweisung und auf die geschweiften Klammern der While-Schleife. Unter dem Code steht ein Hinweis, wenn noch etwas fehlt. -lösung: l20-countdown-loesung.visustruct -::: - -## Übung: Java als Struktogramm - -Jetzt andersherum: Hier ist ein Stück Java-Code. Baue es als Struktogramm nach. - -``` -System.out.println("Zahl eingeben"); -int n = scanner.nextInt(); -int fakultaet = 1; -for (int i = 1; i <= n; i++) { - fakultaet = fakultaet * i; -} -System.out.println(fakultaet); -``` - -::: übung l17-fakultaet -titel: Fakultät aus Java -bereich: beide -auftrag: Baue den Java-Code oben als Struktogramm nach. Das Programm liest eine Zahl n ein und gibt das Produkt 1 · 2 · … · n aus. - Kontrolliere am Ende in der Code-Ansicht, ob dein Java-Code genauso aussieht. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Lies den Java-Code Zeile für Zeile und überlege, welcher Kasten zu jeder Zeile passt. -tipp: Die beiden ersten Java-Zeilen sind zusammen eine einzige Eingabe: `input: int n "Zahl eingeben"`. Die For-Schleife hat die drei Felder `int i = 1`, `i <= n` und `i++`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Übung: else if - -In Java schreibt man mehrere Fälle oft als `else if`: - -``` -if (alter < 6) { - System.out.println("Eintritt frei"); -} else if (alter < 18) { - System.out.println("Kinderticket: 4 Euro"); -} else { - System.out.println("Erwachsenenticket: 8 Euro"); -} -``` - -Im Struktogramm gibt es kein `else if`. Dort steckt eine zweite Verzweigung im Nein-Zweig der ersten. Steht im Nein-Zweig nur diese eine Verzweigung, macht VisuStruct beim Erzeugen des Codes daraus wieder `else if`, in Python `elif`. - -::: übung l17-ticket -titel: Ticketpreis -bereich: beide -auftrag: Frage nach dem Alter als ganze Zahl und baue den Java-Code oben als Struktogramm nach. -test: 5 -> Eintritt frei -test: 6 -> Kinderticket: 4 Euro -test: 17 -> Kinderticket: 4 Euro -test: 18 -> Erwachsenenticket: 8 Euro -braucht: verzweigung -tipp: Im Java-Code steckt in einem `else` ein weiteres `if`. Im Struktogramm ist das eine Verzweigung in einem Zweig. -tipp: Ziehe in den false-Zweig der ersten Verzweigung eine zweite Verzweigung mit der Bedingung `alter < 18`. -lösung: l17-ticket-loesung.visustruct -::: - -## Spiel: Welcher Kasten? - -::: spiel frage -titel: Spiel: Welcher Kasten? -intro: Ludwig zeigt dir Java. Welcher Kasten aus dem Struktogramm steckt dahinter? Acht Runden. -frage: Welcher Kasten ist das? - -runde: - do { - zahl = zahl * 2; - } while (zahl < 100); -- While-Schleife -- [x] Do-while-Schleife -- For-Schleife -> Die Bedingung steht unten. Der Rumpf läuft also mindestens einmal. - -runde: - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - default -> { System.out.println("Gibt es nicht."); } - } -- Verzweigung -- [x] Fallauswahl -- Aufruf -> Eine Variable wird mit festen Werten verglichen: Das ist die Fallauswahl. - -runde: - break; -- Aufruf -- Endlosschleife -- [x] Aussprung -> `break` verlässt sofort die Schleife, in der es steht. - -runde: - berechne(); -- [x] Aufruf -- Ausgabe -- Anweisung -> Name mit runden Klammern: Hier wird ein Unterprogramm aufgerufen. - -runde: - int zahl = scanner.nextInt(); -- Ausgabe -- [x] Eingabe -- Aufruf -> Der Scanner liest einen Wert ein. Das ist eine Eingabe. - -runde: - while (true) { - System.out.println("Hallo"); - } -- While-Schleife mit Bedingung -- [x] Endlosschleife -- Do-while-Schleife -> `while (true)` hat keine echte Bedingung. Nur ein break könnte sie beenden. - -runde: -frage: Du willst eine vollständige Java-Datei, die du sofort übersetzen und starten kannst. Was wählst du in der Code-Ansicht? -- Snippet -- [x] Class -> Ein Snippet sind nur die Anweisungen für die `main`-Methode. Class erzeugt die ganze Datei. - -runde: -frage: Im Debugger ist eine Zeile gelb. Was bedeutet das? -- Sie hat einen Fehler. -- [x] Sie wird als Nächstes ausgeführt. -- Dort steht ein Haltepunkt. -> Gelb ist die nächste Zeile. Ein Haltepunkt ist ein roter Punkt an der Zeilennummer. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Code-Ansicht öffnen und Java als Sprache wählen. → Die Code-Ansicht -- Ich kann Ausgabe, Eingabe, Verzweigung und Schleifen in Java erkennen. → Die Elemente in Java -- Ich kann den Unterschied zwischen Snippet und Class erklären. → Snippet oder Class -- Ich kann erklären, wie Java-Code mit Compiler, Bytecode und JVM auf jedem Betriebssystem läuft. → Wie Java auf jedem Computer läuft -- Ich kann JDK und JRE unterscheiden. → Wie Java auf jedem Computer läuft -- Ich kann erklären, warum man ein C++-Programm für jedes Betriebssystem neu kompilieren muss, ein Java-Programm aber nicht. → Wie Java auf jedem Computer läuft -- Ich kann ein kurzes Java-Programm als Struktogramm nachbauen. → Übung: Java als Struktogramm -- Ich kann im Bereich Code Java tippen und das entstehende Struktogramm lesen. → Java direkt schreiben -- Ich kann ein Programm mit ▶︎ ausführen und Ein- und Ausgaben in der Konsole nutzen. → Das Programm ausführen -- Ich kann an der roten Zeilennummer einen Syntaxfehler oder Laufzeitfehler finden. → Das Programm ausführen -- Ich kann Haltepunkte setzen und ein Programm im Einzelschritt verfolgen. → Fehler suchen mit dem Debugger -::: - -## Wissenstest - -::: quiz -? Wie sieht `output: "Hallo"` in Java aus? -→ Die Elemente in Java -- [ ] print("Hallo") -- [x] System.out.println("Hallo"); -- [ ] console.log("Hallo"); -> print ist Python, console.log ist JavaScript. - -? Woher kommt der Klassenname bei Class? -→ Snippet oder Class -- [ ] Er heißt immer Main. -- [x] Aus dem Titel des Diagramms -- [ ] Aus dem ersten Kasten -> Ohne Titel heißt die Klasse Main. - -? Welcher Java-Code gehört zu einer Do-while-Schleife? -→ Die Elemente in Java -- [ ] while (bedingung) { … } -- [x] do { … } while (bedingung); -- [ ] for (;;) { … } -> Bei do-while steht die Bedingung am Ende, deshalb läuft der Rumpf mindestens einmal. - -? Was wird aus einem Aussprung im Java-Code? -→ Die Elemente in Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? Was passiert an einem roten Punkt vor der Zeilennummer? -→ Fehler suchen mit dem Debugger -- [ ] Die Zeile wird übersprungen. -- [x] Das Programm hält vor dieser Zeile an. -- [ ] Die Zeile enthält einen Fehler. -> Das ist ein Haltepunkt. Danach geht es mit ▶︎ oder dem Einzelschritt weiter. - -? Warum läuft dieselbe `.class`-Datei auf Windows, macOS und Linux? -→ Wie Java auf jedem Computer läuft -- [ ] Der Compiler erzeugt für jedes Betriebssystem eine eigene Datei. -- [x] Auf jedem Betriebssystem führt eine JVM denselben Bytecode aus. -- [ ] Java-Programme brauchen kein Betriebssystem. -> Der Bytecode bleibt gleich. Nur die JVM gibt es für jedes System extra. - -? Was brauchst du, um Java-Programme zu schreiben und zu übersetzen? -→ Wie Java auf jedem Computer läuft -- [ ] nur die JRE -- [x] das JDK -- [ ] nur die JVM -> Die JRE reicht zum Ausführen. Den Compiler `javac` gibt es nur im JDK. - -? Du willst dein Programm auf Windows und auf dem Mac nutzen. Was stimmt? -→ Wie Java auf jedem Computer läuft -- [ ] Java musst du für jedes System neu kompilieren, C++ nicht. -- [x] C++ musst du für jedes System neu kompilieren, Java nicht. -- [ ] Beide musst du nur einmal kompilieren. -> C++ wird direkt zu Maschinencode für ein System. Java wird zu Bytecode, und den führt auf jedem System die JVM aus. - -? Die Zeilennummer 3 im Bereich Code ist rot. Was heißt das? -→ Das Programm ausführen -- [ ] In Zeile 3 ist ein Haltepunkt. -- [x] In Zeile 3 steckt ein Fehler. -- [ ] Zeile 3 wird als Nächstes ausgeführt. -> Der Haltepunkt ist ein runder roter Punkt, die nächste Zeile ist gelb. Eine rote Zeilennummer zeigt einen Syntaxfehler oder einen Laufzeitfehler. -::: diff --git a/web/lessons/de/21-python-und-javascript.md b/web/lessons/de/21-python-und-javascript.md deleted file mode 100644 index d73a08d3..00000000 --- a/web/lessons/de/21-python-und-javascript.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -titel: Python und JavaScript -bereich: Vom Struktogramm zum Code -kurz: Python- und JavaScript-Code aus dem Struktogramm erzeugen und JavaScript direkt im Browser ausführen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Python und JavaScript in der Code-Ansicht auswählen und vergleichen -- erklären, dass Python Blöcke durch Einrückung bildet und keine Datentypen braucht -- erklären, was VisuStruct beim Übersetzen nach Python anpasst -- JavaScript mit Im Browser testen ausführen -::: - -## Dasselbe Diagramm, drei Sprachen - -In der Code-Ansicht ([Code](#zeige-code) bei den Werkzeugen) wählst du oben die Sprache. Das Struktogramm bleibt gleich, nur die Schreibweise ändert sich. So kannst du die Sprachen gut vergleichen. - -::: diagramm l18-beispiel.visustruct -Eine Zahl wird so lange um 3 verkleinert, bis sie nicht mehr größer als 0 ist. -::: - -## Python - -Für das Diagramm oben erzeugt VisuStruct diesen Python-Code: - -``` -zahl = int(input("Startwert?")) -while zahl > 0: - print(zahl) - zahl = zahl - 3 -print("Ende") -``` - -Was in Python anders ist als in Java: - -- Es gibt **keine Semikolons** und **keine geschweiften Klammern**. Was zu einer Schleife oder Verzweigung gehört, erkennt Python allein an der **Einrückung**. Hinter `while`, `if` und `else` steht ein Doppelpunkt. -- Variablen bekommen **keinen Datentyp**: aus `int zahl = 5` wird `zahl = 5`. -- Die Ausgabe heißt `print(…)`, die Eingabe `input(…)`. Für eine Zahl steht `int(…)` darum herum. -- Arrays heißen in Python **Listen**: aus `int[] zahlen = {2, 4, 6}` wird `zahlen = [2, 4, 6]`, und aus `zahlen.length` wird `len(zahlen)`. -- Eine einfache For-Schleife wie `int i = 0`, `i < 5`, `i++` wird zu `for i in range(5):`. - -## Worauf du bei Python achten musst - -Python ist an einigen Stellen strenger als Java. VisuStruct übersetzt die Java-Schreibweise beim Export deshalb gleich mit: - -- **Text und Zahl mit +:** `"Summe: " + summe` wird zu `"Summe: " + str(summe)`. Ohne `str(…)` bricht Python ab. -- **i++ gibt es nicht:** daraus wird `i += 1`. -- **&&, || und !** heißen in Python `and`, `or` und `not`, `true` und `false` werden zu `True` und `False`. -- **Ganzzahlige Division:** In Python ergibt `7 / 2` immer 3.5. Teilt das Struktogramm zwei `int`-Werte, schreibt VisuStruct deshalb `7 // 2`, das ergibt wie in Java 3. -- **For-Schleifen:** Zählschleifen werden zu `for … in range(…)`, auch mit `i <= n`, Rückwärtszählen oder Schritten wie `i = i + 2`. Passt eine Schleife nicht dazu, wird daraus eine `while`-Schleife. -- **do-while gibt es nicht:** daraus wird `while True:` mit `if not (…): break` am Ende. - -Ist ein Diagramm als **Pseudocode** markiert, steht nur ein Hinweis über dem Code. Dann ist der Text eine Skizze und kein lauffähiges Programm. - -## JavaScript - -Dasselbe Diagramm in JavaScript: - -``` -"use strict"; - -let zahl = parseInt((prompt("Startwert?") ?? "0"), 10); -while (zahl > 0) { - console.log(zahl); - zahl = zahl - 3; -} -console.log("Ende"); -``` - -JavaScript sieht Java ähnlich: geschweifte Klammern, Semikolons, `while (…)` und `if (…)`. Die Unterschiede: - -- Variablen werden mit `let` angelegt, ohne Datentyp. Konstanten mit `const`. -- Die Ausgabe heißt `console.log(…)`, die Eingabe `prompt(…)`. Mit `parseInt(…)` wird aus der Eingabe eine ganze Zahl. -- Arrays stehen in eckigen Klammern: `let zahlen = [2, 4, 6];`. -- `"use strict";` ganz oben sorgt dafür, dass JavaScript bei Tippfehlern in Variablennamen meckert. -- JavaScript kennt nur einen Zahlentyp. Teilt das Struktogramm zwei `int`-Werte, schreibt VisuStruct `Math.trunc(a / b)`. Dann fallen die Nachkommastellen wie in Java weg. - -::: angeberwissen -JavaScript hat mit Java fast nichts zu tun. Brendan Eich hat es 1995 in nur zehn Tagen für den Browser Netscape gebaut. Erst hieß es Mocha, dann LiveScript. Weil Java gerade alle begeisterte, wurde es kurz vor dem Start aus Werbegründen in JavaScript umbenannt. Seitdem werden die beiden ständig verwechselt, bis heute. Programmierer sagen dazu: Java und JavaScript haben so viel gemeinsam wie Auto und Autogramm. -::: - -## JavaScript im Browser ausführen - -JavaScript ist die Sprache der Browser. Deshalb kann VisuStruct den Code **sofort ausführen**: - -::: angeberwissen -Warum sind Links im Browser eigentlich blau? Ben Shneidermans Team hat 1985 untersucht, welche Farbe man gut sieht und die beim Lesen nicht stört. Gewonnen hat Hellblau. Rot fiel zwar mehr auf, aber man konnte sich den Text danach schlechter merken. -::: - -1. Wähle in der Code-Ansicht **JavaScript**. -2. Klicke auf **Im Browser testen**. -3. Unter **Ausgabe** erscheint, was `console.log` ausgibt. Fragt das Programm mit `prompt` nach einem Wert, tippst du ihn ein und klickst auf **OK**. -4. Mit **Stopp** brichst du das Programm ab. - -Eine Endlosschleife `while (true)` hält VisuStruct nach 100 000 Durchläufen von selbst an und meldet **Ausführung gestoppt: mögliche Endlosschleife.** - -> Ein **Aufruf** braucht im JavaScript-Code ein Unterprogramm mit diesem Namen, sonst meldet JavaScript einen Fehler. Anders als die Simulation sucht der Browser nicht in den anderen Reitern. - -## Übung: Python als Struktogramm - -Dieses Python-Programm gibt alle geraden Zahlen von 0 bis zu einer Grenze aus: - -``` -grenze = int(input("Bis wohin?")) -for i in range(0, grenze + 1, 2): - print(i) -``` - -`range(0, grenze + 1, 2)` heißt: bei 0 anfangen, vor `grenze + 1` aufhören, in Zweierschritten zählen. - -::: übung l18-python -titel: Gerade Zahlen -bereich: beide -auftrag: Baue das Python-Programm oben als Struktogramm mit einer For-Schleife nach. Bei der Grenze 7 gibt es 0, 2, 4 und 6 aus. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: `range(0, grenze + 1, 2)` zählt von 0 bis zur Grenze in Zweierschritten. Das ist eine For-Schleife. -tipp: Die drei Felder der For-Schleife sind `int i = 0`, `i <= grenze` und `i = i + 2`. -lösung: l18-python-loesung.visustruct -::: - -## Übung: JavaScript als Struktogramm - -``` -let alter = parseInt((prompt("Wie alt bist du?") ?? "0"), 10); -if (alter >= 16) { - console.log("Du darfst wählen."); -} else { - let jahre = 16 - alter; - console.log("Noch " + jahre + " Jahre warten."); -} -``` - -::: übung l18-js -titel: Wählen ab 16 -bereich: beide -auftrag: Baue den JavaScript-Code oben als Struktogramm nach. Teste danach in der Code-Ansicht mit **JavaScript** und **Im Browser testen**, ob dein Code dasselbe ausgibt wie die Simulation. -test: 12 -> Noch 4 Jahre warten. -test: 16 -> Du darfst wählen. -test: 13 -> Noch 3 Jahre warten. -test: 40 -> Du darfst wählen. -braucht: verzweigung -tipp: Lies den Code Zeile für Zeile. `if` und `else` werden zu einer Verzweigung. -tipp: `prompt` und `parseInt` sind zusammen eine Eingabe: `input: int alter "Wie alt bist du?"`. Im false-Zweig stehen zwei Kästen: erst `int jahre = 16 - alter`, dann die Ausgabe. -lösung: l18-js-loesung.visustruct -::: - -## Spiel: Welche Sprache? - -::: spiel frage -titel: Spiel: Welche Sprache? -intro: Ludwig hat Zeilen aus drei Sprachen gemischt. Erkennst du, woher jede stammt? Acht Runden. -frage: In welcher Sprache ist das geschrieben? -antworten: Java | Python | JavaScript - -runde: - print(zahl) -richtig: Python -> Die Ausgabe heißt in Python `print(…)`, ohne Semikolon. - -runde: - console.log(zahl); -richtig: JavaScript -> `console.log` ist die Ausgabe in JavaScript. - -runde: - System.out.println(zahl); -richtig: Java -> Die lange Ausgabe mit `System.out` gibt es nur in Java. - -runde: - let zahlen = [2, 4, 6]; -richtig: JavaScript -> `let` und eckige Klammern: ein Array in JavaScript. - -runde: - for i in range(5): -richtig: Python -> Doppelpunkt statt geschweifter Klammern und `range`: typisch Python. - -runde: - int[] zahlen = {2, 4, 6}; -richtig: Java -> Nur Java schreibt den Datentyp `int[]` davor. - -runde: - zahl = int(input("Startwert?")) -richtig: Python -> Kein Datentyp vor der Variablen, kein Semikolon, und `int(…)` um die Eingabe. - -runde: - let zahl = parseInt(prompt("Startwert?"), 10); -richtig: JavaScript -> `prompt` fragt im Browser nach einem Wert, `parseInt` macht eine Zahl daraus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Python und JavaScript in der Code-Ansicht auswählen und vergleichen. → Dasselbe Diagramm, drei Sprachen -- Ich kann erklären, dass Python Blöcke durch Einrückung bildet und keine Datentypen braucht. → Python -- Ich kann erklären, was VisuStruct beim Übersetzen nach Python anpasst. → Worauf du bei Python achten musst -- Ich kann JavaScript mit Im Browser testen ausführen. → JavaScript im Browser ausführen -::: - -## Wissenstest - -::: quiz -? Woran erkennt Python, welche Zeilen zu einer Schleife gehören? -→ Python -- [ ] An den geschweiften Klammern -- [x] An der Einrückung -- [ ] Am Semikolon am Zeilenende - -? Wie heißt `output: summe` in JavaScript? -→ Dasselbe Diagramm, drei Sprachen -- [ ] print(summe) -- [ ] System.out.println(summe); -- [x] console.log(summe); - -? Welche Sprache kann VisuStruct direkt im Browser ausführen? -→ JavaScript im Browser ausführen -- [ ] Java -- [ ] Python -- [x] JavaScript -> Jeder Browser kann JavaScript ausführen. Für Java und Python brauchst du ein eigenes Programm. - -? Was passiert in Python bei `print("Summe: " + summe)`, wenn summe die Zahl 7 ist? -→ Worauf du bei Python achten musst -- [ ] Es wird Summe: 7 ausgegeben. -- [x] Python meldet einen Fehler, weil Text und Zahl nicht mit + verbunden werden können. -- [ ] Es wird Summe: ausgegeben. -> Mit str(summe) wird aus der Zahl ein Text. -::: diff --git a/web/lessons/de/22-try-und-catch.md b/web/lessons/de/22-try-und-catch.md deleted file mode 100644 index dd0444fc..00000000 --- a/web/lessons/de/22-try-und-catch.md +++ /dev/null @@ -1,293 +0,0 @@ ---- -titel: Wenn das Programm abstürzt: try und catch -bereich: Fehler abfangen -kurz: Mit echtem Java Fehlermeldungen lesen und Abstürze mit try und catch abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- echtes Java im Bereich Code einschalten -- aus einer Fehlermeldung die Exception und die Zeile ablesen -- eine Exception mit try und catch auffangen -- Text mit Integer.parseInt in eine Zahl umwandeln und die NumberFormatException abfangen -- ein Programm schreiben, das sicher teilt, auch durch 0 -::: - -## Abgestürzt! - -Dieses Programm soll drei Noten ausgeben. Tippe es im Bereich [Code](#zeige-codelive) ab und klicke auf [▶︎ Ausführen](#zeige-ausfuehren): - -```java -int[] noten = {2, 3, 1}; -System.out.println("Erste Note: " + noten[0]); -System.out.println("Vierte Note: " + noten[3]); -System.out.println("Fertig!"); -``` - -Die erste Note erscheint, dann ist Schluss. VisuStruct meldet „Ungültiger Ausdruck: noten[3]“, und „Fertig!“ kommt nie an. Das Array hat nur die Fächer 0, 1 und 2, ein Fach 3 gibt es nicht. - -::: ludwig -Ist dir das auch schon passiert? Ein kleiner Fehler, und das ganze Programm ist weg. Mir ist das so oft passiert, dass ich vor dem Bildschirm geweint habe. Aber es gibt eine Lösung, und die heißt **try** und **catch**. -::: - -::: angeberwissen -1947 fand das Team von Grace Hopper im Rechner Mark II an der Universität Harvard eine Motte, die einen Fehler verursacht hatte. Sie klebten sie ins Logbuch und schrieben dazu: „First actual case of bug being found“. Seitdem heißen Programmfehler **Bugs** (englisch für Käfer). -::: - -## Echtes Java einschalten - -Der Bereich Code rechnet normalerweise mit derselben Simulation wie das Struktogramm. Die hält bei einem Fehler einfach an. Um zu sehen, was echtes Java macht, schaltest du echtes Java ein: - -1. Klicke oben rechts auf das [Zahnrad](#zeige-einstellungen) und wähle als **Programmiersprache** Java. -2. Setze im Abschnitt Java den Haken bei **Echtes Java (mit Swing)**. - -Beim ersten Ausführen lädt VisuStruct einmalig etwa 40 MB. Danach wird dein Code wie in einer richtigen Java-Entwicklungsumgebung übersetzt und ausgeführt. - -> Mit echtem Java funktionieren Eingaben über die Konsole (`Scanner`) noch nicht. Unsere Beispiele kommen deshalb ohne Eingabe aus. - -Schreib das Programm jetzt als ganze Klasse, damit die Zeilennummern stimmen, und führe es aus: - -```java -public class Noten { - public static void main(String[] args) { - int[] noten = {2, 3, 1}; - System.out.println("Erste Note: " + noten[0]); - System.out.println("Vierte Note: " + noten[3]); - System.out.println("Fertig!"); - } -} -``` - -In der [Konsole](#zeige-konsole) steht jetzt: - -``` -Erste Note: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Noten.main(Noten.java:5) -``` - -## Die Fehlermeldung lesen - -So eine Meldung sieht erst einmal erschreckend aus. Sie verrät dir aber genau, was los ist: - -| Teil | Bedeutung | -|---|---| -| `ArrayIndexOutOfBoundsException` | Die Art des Fehlers: Der Index liegt außerhalb des Arrays. | -| `3` | Der falsche Index. | -| `Noten.java:5` | Die Zeile, in der es passiert ist: Zeile 5. | - -So einen Laufzeitfehler nennt Java eine **Exception** (englisch für Ausnahme). Man sagt: Java *wirft* eine Exception. Fängt sie niemand auf, bricht das Programm ab. Deshalb erscheint „Fertig!“ nicht. - -::: ludwig -Lies Fehlermeldungen immer von oben: Erst der Name der Exception, dann die Zeilennummer. Die Zeilennummern stehen im Bereich Code links neben dem Code. -::: - -## try und catch - -Mit **try** und **catch** fängst du die Exception auf. Ändere die Zeile mit `noten[3]` so: - -```java -try { - System.out.println("Vierte Note: " + noten[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Diese Note gibt es nicht."); -} -System.out.println("Fertig!"); -``` - -- `try` heißt „versuche“. Java führt den Block in den geschweiften Klammern ganz normal aus. -- Wirft eine Zeile darin eine Exception, springt Java sofort in den `catch`-Block. Der Rest des `try`-Blocks wird übersprungen. -- In der Klammer hinter `catch` steht, welche Exception aufgefangen wird, und ein Name dafür, meistens `e`. -- Danach geht das Programm normal weiter. Jetzt erscheint auch „Fertig!“. - -Läuft im `try`-Block alles gut, wird der `catch`-Block einfach übersprungen. - -::: ludwig -try und catch sind wie das Netz unter dem Trapez im Zirkus: Solange alles klappt, merkst du nichts davon. Fällt jemand, fängt das Netz ihn auf, und die Vorstellung geht weiter. -::: - -> Im Struktogramm gibt es keinen Kasten für try und catch. VisuStruct zeigt den ganzen Block deshalb als eine Anweisung. Ausführen kannst du ihn nur mit echtem Java. - -::: angeberwissen -Im Juli 1969 landete Apollo 11 auf dem Mond. Kurz vor der Landung meldete der Bordcomputer plötzlich die Fehler **1202** und **1201**: Er hatte mehr zu tun, als er schaffen konnte. Abgestürzt ist er trotzdem nicht. Das Team von **Margaret Hamilton** hatte die Software so gebaut, dass sie solche Fehler erkennt, Unwichtiges liegen lässt und nur das Wichtigste weiterrechnet. Die Landung klappte. - -Margaret Hamilton leitete am MIT die Gruppe, die die Software für die Apollo-Raumschiffe schrieb. Sie plante immer ein, dass etwas schiefgehen kann, so wie du mit `try` und `catch`. Damit Software genauso ernst genommen wird wie Hardware, nannte sie ihre Arbeit **Software Engineering**. Heute heißt so ein ganzes Fach der Informatik. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (geb. 1936) im Jahr 1969 neben den ausgedruckten Programmen, die ihr Team für Apollo geschrieben hat (Foto: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), gemeinfrei). -::: - -## Text in eine Zahl umwandeln - -Oft steht eine Zahl zuerst als Text da, zum Beispiel später in einem Textfeld im Fenster. `Integer.parseInt` wandelt Text in eine ganze Zahl um: - -```java -String eingabe = "zwölf"; -try { - int zahl = Integer.parseInt(eingabe); - System.out.println("Doppelt: " + zahl * 2); -} catch (NumberFormatException e) { - System.out.println("Das ist keine Zahl: " + eingabe); -} -``` - -„zwölf“ kann Java nicht umwandeln und wirft eine `NumberFormatException`. Das Programm gibt „Das ist keine Zahl: zwölf“ aus. Ändere die erste Zeile in `String eingabe = "12";` und führe es noch einmal aus: Jetzt erscheint „Doppelt: 24“. - -## Exceptions sind Objekte - -Das steht nicht im Lehrplan, ist aber praktisch zu wissen. - -::: angeberwissen -Jede Exception ist ein Objekt einer eigenen Klasse, zum Beispiel `ArithmeticException` für das Teilen durch 0. `e.getMessage()` liefert ihre Meldung, etwa `/ by zero`. - -Hinter einem `try` dürfen mehrere `catch`-Blöcke stehen, für jede Art von Fehler einer. `catch (Exception e)` fängt alle Exceptions auf, verrät dir dann aber weniger darüber, was passiert ist. Und ein Block `finally { … }` am Ende läuft immer, egal ob es einen Fehler gab oder nicht. -::: - -## Spiel: Fängt das Netz? - -::: spiel frage -titel: Spiel: Fängt das Netz? -intro: Ludwig turnt am Trapez. Fängt try und catch ihn auf, oder stürzt das Programm ab? Sieben Runden. - -runde: - int[] noten = {2, 3, 1}; - System.out.println(noten[3]); - System.out.println("Fertig!"); -frage: Was passiert mit echtem Java? -- Es erscheint 1 und Fertig! -- Es erscheint nur Fertig! -- [x] Das Programm stürzt mit einer ArrayIndexOutOfBoundsException ab. -> Ein Fach 3 gibt es nicht. Niemand fängt die Exception auf, also kommt Fertig! nie an. - -runde: - int[] noten = {2, 3, 1}; - try { - System.out.println(noten[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Diese Note gibt es nicht."); - } - System.out.println("Fertig!"); -frage: Was wird ausgegeben? -- Fertig! -- [x] Diese Note gibt es nicht.\nFertig! -- Diese Note gibt es nicht. -> Der catch-Block fängt die Exception auf, danach geht das Programm normal weiter. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: Was wird ausgegeben? -- A\nB\nC -- [x] A\nC -- C -> Bei 10 / 0 springt Java sofort in den catch-Block. Der Rest des try-Blocks, also B, wird übersprungen. - -runde: - String eingabe = "12"; - try { - int zahl = Integer.parseInt(eingabe); - System.out.println("Doppelt: " + zahl * 2); - } catch (NumberFormatException e) { - System.out.println("Das ist keine Zahl: " + eingabe); - } -frage: Was wird ausgegeben? -- [x] Doppelt: 24 -- Das ist keine Zahl: 12 -- Doppelt: 1212 -> „12“ lässt sich umwandeln. Läuft im try-Block alles gut, wird catch übersprungen. - -runde: -frage: Welche Exception wirft `Integer.parseInt("2.5")`? -- ArithmeticException -- [x] NumberFormatException -- keine, das Ergebnis ist 2 -> parseInt wandelt nur ganze Zahlen um. Mit Komma oder Punkt ist es kein gültiges Format. - -runde: -frage: Welche Exception wirft `12 / 0` mit `int`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Durch 0 teilen geht mit ganzen Zahlen nicht. Die Meldung lautet `/ by zero`. - -runde: - try { - int stuecke = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Fehler!"); - } - System.out.println("Fertig!"); -frage: Was passiert? -- Es erscheint Fehler! und Fertig! -- Es erscheint nur Fertig! -- [x] Das Programm stürzt ab. -> catch fängt nur die Exception, die in der Klammer steht. Eine ArithmeticException fällt durch das Netz. -::: - -## Aufgabe: Sicher teilen - -Dieses Programm stürzt ab: - -```java -int kuchen = 12; -int personen = 0; -System.out.println("Jeder bekommt " + kuchen / personen + " Stücke."); -System.out.println("Guten Appetit!"); -``` - -1. Führe es mit echtem Java aus und lies die Fehlermeldung. Welche Exception ist es? -2. Fange sie mit try und catch ab. Statt des Absturzes soll „Ohne Personen kann man nicht teilen.“ erscheinen, und „Guten Appetit!“ soll trotzdem kommen. -3. Setze `personen` auf 4 und prüfe, dass dann „Jeder bekommt 3 Stücke.“ erscheint. - -::: ludwig -Die Exception heißt `ArithmeticException`. Schreib in den `try`-Block nur die Zeile, die abstürzen kann. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann echtes Java im Bereich Code einschalten. → Echtes Java einschalten -- Ich kann aus einer Fehlermeldung die Exception und die Zeile ablesen. → Die Fehlermeldung lesen -- Ich kann eine Exception mit try und catch auffangen. → try und catch -- Ich kann Text mit Integer.parseInt in eine Zahl umwandeln und die NumberFormatException abfangen. → Text in eine Zahl umwandeln -- Ich kann ein Programm schreiben, das sicher teilt, auch durch 0. → Aufgabe: Sicher teilen -::: - -## Wissenstest - -::: quiz -? Was passiert, wenn eine Exception nicht aufgefangen wird? -→ Die Fehlermeldung lesen -- [ ] Java überspringt die Zeile und macht weiter. -- [x] Das Programm bricht ab. -- [ ] Der Computer startet neu. -> Deshalb erscheint im ersten Beispiel „Fertig!“ nicht. - -? Was passiert im try-Block nach der Zeile, die eine Exception wirft? -→ try und catch -- [ ] Die restlichen Zeilen laufen trotzdem. -- [x] Der Rest wird übersprungen, Java springt in den catch-Block. -- [ ] Java beginnt den try-Block von vorne. - -? Welche Exception wirft Integer.parseInt("zwölf")? -→ Text in eine Zahl umwandeln -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> „zwölf“ hat kein Zahlenformat, das Java versteht. - -? Ordne jeder Exception ihre Ursache zu. -→ Die Fehlermeldung lesen -= ArrayIndexOutOfBoundsException -> Index außerhalb des Arrays -= ArithmeticException -> Teilen durch 0 -= NumberFormatException -> Text ist keine Zahl -::: diff --git a/web/lessons/de/23-text-einlesen-und-speichern.md b/web/lessons/de/23-text-einlesen-und-speichern.md deleted file mode 100644 index 62429a1b..00000000 --- a/web/lessons/de/23-text-einlesen-und-speichern.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -titel: Text einlesen und speichern -bereich: Dateien -kurz: Mit Scanner eine Textdatei Zeile für Zeile lesen und mit PrintWriter eine Datei speichern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, warum Programme Daten in Dateien speichern -- eine Textdatei mit Scanner, hasNextLine und nextLine Zeile für Zeile lesen -- erklären, warum Java bei Dateien try und catch oder throws verlangt -- mit PrintWriter eine Datei schreiben und sie mit close() abschließen -- eine Datei lesen, ergänzen und nummeriert wieder speichern -::: - -## Warum Dateien? - -Alles, was in Variablen steht, ist weg, sobald das Programm endet. Soll sich ein Programm etwas merken, zum Beispiel einen Spielstand, eine Liste oder Noten, speichert es die Daten in einer **Datei**. Beim nächsten Start liest es die Datei wieder ein. - -Zora ist frei, und Ludwig plant ein Fest. Seine Einkaufsliste liegt als Datei `einkauf.txt` bereit: - -::: datei einkauf.txt -Heu -Äpfel -Karotten -Löwenzahn -::: - -Eine Datei mit der Endung `.txt` enthält nur Text, Zeile für Zeile. Du kannst sie mit jedem Editor öffnen, zum Beispiel mit dem Editor von Windows oder TextEdit am Mac. - -## Eine Datei lesen mit Scanner - -Den `Scanner` kennst du schon: `new Scanner(System.in)` liest von der Tastatur. Gibst du ihm statt `System.in` eine Datei, liest er aus der Datei: - -```java -import java.io.*; -import java.util.Scanner; - -Scanner datei = new Scanner(new File("einkauf.txt")); -while (datei.hasNextLine()) { - String zeile = datei.nextLine(); - System.out.println(zeile); -} -datei.close(); -``` - -| Methode | Bedeutung | -|---|---| -| `new File("einkauf.txt")` | die Datei mit diesem Namen | -| `hasNextLine()` | Gibt es noch eine Zeile? `true` oder `false` | -| `nextLine()` | liefert die nächste Zeile und rückt eine Zeile weiter | -| `close()` | schließt die Datei, wenn du fertig bist | - -Die Schleife läuft, solange es noch eine Zeile gibt. In jedem Durchlauf holt `nextLine()` die nächste Zeile. `File` steht im Paket `java.io`, deshalb brauchst du `import java.io.*;`. - -::: diagramm dt-lesen.visustruct -So sieht das Lesen im Struktogramm aus. Probier es in der Simulation aus. -::: - -::: ludwig -Stell dir den Scanner wie einen Finger vor, der auf eine Zeile zeigt. `nextLine()` liest die Zeile unter dem Finger und schiebt ihn eine Zeile weiter. Ist der Finger unter der letzten Zeile, liefert `hasNextLine()` `false`. -::: - -## Was, wenn die Datei fehlt? - -Gibt es die Datei nicht, kann Java sie nicht lesen und wirft eine `FileNotFoundException`. Java verlangt, dass dein Programm damit rechnet. Entweder du fängst sie mit try und catch auf, wie in Lektion 22: - -```java -try { - Scanner datei = new Scanner(new File("einkauf.txt")); - // ... lesen ... - datei.close(); -} catch (FileNotFoundException e) { - System.out.println("Die Datei einkauf.txt fehlt!"); -} -``` - -Oder du schreibst hinter `main` den Zusatz `throws Exception`. Das heißt: „Diese Methode kann eine Exception werfen, ich kümmere mich nicht darum.“ Dann bricht das Programm ab, wenn die Datei fehlt. - -```java -public static void main(String[] args) throws Exception { -``` - -> VisuStruct schreibt `throws Exception` automatisch hinter `main`, sobald dein Programm mit Dateien arbeitet. In der Simulation meldet VisuStruct eine fehlende Datei mit „Es gibt keine Datei …“. - -## Übung: Einkaufsliste nummerieren - -::: übung l23-java-nummern -titel: Einkaufsliste nummerieren -code: java -auftrag: Lies `einkauf.txt` Zeile für Zeile und gib jede Zeile mit ihrer Nummer aus: **1. Heu**, **2. Äpfel** und so weiter. -start: l23-java-nummern-start.visustruct -test: - -> 1. Heu | 2. Äpfel | 3. Karotten | 4. Löwenzahn -braucht: whileSchleife -tipp: Lege vor der Schleife `int nummer = 1;` an und erhöhe die Nummer am Ende jedes Durchlaufs. -tipp: Die Ausgabe ist `System.out.println(nummer + ". " + zeile);` -lösung: l23-java-nummern-loesung.visustruct -::: - -## In eine Datei schreiben mit PrintWriter - -Zum Schreiben gibt es den `PrintWriter`. Seine Methoden heißen wie bei `System.out`: - -```java -PrintWriter aus = new PrintWriter("gruss.txt"); -aus.println("Hallo Zora!"); -aus.println("Bis morgen, Ludwig"); -aus.close(); -``` - -Danach steht in `gruss.txt`: - -``` -Hallo Zora! -Bis morgen, Ludwig -``` - -- `println` schreibt den Text und beginnt danach eine neue Zeile, `print` schreibt ohne neue Zeile. -- Gibt es die Datei schon, wird sie **überschrieben**. Der alte Inhalt ist dann weg. -- Nach dem Lauf zeigt VisuStruct unter der Ausgabe, was in der Datei gespeichert wurde. - -::: ludwig -Vergiss `close()` nicht! Der PrintWriter sammelt den Text erst in einem Zwischenspeicher und schreibt ihn beim Schließen in die Datei. Ohne `close()` bleibt die Datei leer. Mir ist so schon eine ganze Geschichte verloren gegangen. -::: - -## Übung: Einkaufsliste ergänzen - -::: übung l23-java-ergaenzen -titel: Einkaufsliste ergänzen -code: java -auftrag: Ludwig braucht noch Kuchen. Lies `einkauf.txt` und schreibe jede Zeile in die neue Datei `einkauf-neu.txt`. Schreibe danach **Kuchen** als letzte Zeile dazu. Gib am Ende aus, wie viele Zeilen gespeichert wurden: **5 Zeilen gespeichert**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 Zeilen gespeichert -datei: einkauf-neu.txt -> Heu | Äpfel | Karotten | Löwenzahn | Kuchen -braucht: whileSchleife -tipp: In der Schleife liest du eine Zeile mit `datei.nextLine()` und schreibst sie mit `aus.println(zeile)` gleich wieder hinaus. -tipp: Nach der Schleife kommt `aus.println("Kuchen");`. Schließe beide Dateien mit `close()`, sonst bleibt `einkauf-neu.txt` leer. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Spiel: Was steht in der Datei? - -::: spiel frage -titel: Spiel: Was steht in der Datei? -intro: Ludwig schreibt Dateien mit dem PrintWriter. Lies den Code und sag voraus, was danach in der Datei steht. Sechs Runden. -frage: Was steht danach in der Datei? - -runde: - PrintWriter aus = new PrintWriter("gruss.txt"); - aus.println("Hallo"); - aus.println("Zora"); - aus.close(); -- [x] Hallo\nZora -- HalloZora -- Zora -> `println` beginnt nach jedem Text eine neue Zeile. - -runde: - PrintWriter aus = new PrintWriter("wort.txt"); - aus.print("Ha"); - aus.print("llo"); - aus.println("!"); - aus.close(); -- Ha\nllo\n! -- [x] Hallo! -- Ha llo ! -> `print` schreibt ohne neue Zeile weiter, die Teile hängen direkt aneinander. - -runde: - PrintWriter aus = new PrintWriter("geheim.txt"); - aus.println("Der Schatz liegt im Turm"); -- Der Schatz liegt im Turm -- [x] Nichts, die Datei bleibt leer. -- Java meldet einen Fehler. -> Ohne `close()` landet der Text nie in der Datei. Er bleibt im Zwischenspeicher. - -runde: - // In notiz.txt steht vorher: Alt - PrintWriter aus = new PrintWriter("notiz.txt"); - aus.println("Neu"); - aus.close(); -- Alt\nNeu -- [x] Neu -- Neu\nAlt -> Ein neuer PrintWriter überschreibt die Datei. Der alte Inhalt ist weg. - -runde: - PrintWriter aus = new PrintWriter("zahlen.txt"); - for (int i = 1; i <= 3; i++) { - aus.println(i * 2); - } - aus.close(); -- 1\n2\n3 -- 246 -- [x] 2\n4\n6 -> Die Schleife schreibt 2, 4 und 6, jede Zahl in eine eigene Zeile. - -runde: - // In einkauf.txt steht: Heu, Äpfel (je eine Zeile) - Scanner datei = new Scanner(new File("einkauf.txt")); - PrintWriter aus = new PrintWriter("gross.txt"); - while (datei.hasNextLine()) { - aus.println(datei.nextLine().toUpperCase()); - } - datei.close(); - aus.close(); -- Heu\nÄpfel -- [x] HEU\nÄPFEL -- HEUÄPFEL -> Jede Zeile wird gelesen, in Großbuchstaben verwandelt und in eine eigene Zeile geschrieben. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, warum Programme Daten in Dateien speichern. → Warum Dateien? -- Ich kann eine Textdatei mit Scanner, hasNextLine und nextLine Zeile für Zeile lesen. → Eine Datei lesen mit Scanner -- Ich kann erklären, warum Java bei Dateien try und catch oder throws verlangt. → Was, wenn die Datei fehlt? -- Ich kann mit PrintWriter eine Datei schreiben und sie mit close() abschließen. → In eine Datei schreiben mit PrintWriter -- Ich kann eine Datei lesen, ergänzen und nummeriert wieder speichern. → Übung: Einkaufsliste ergänzen -::: - -## Wissenstest - -::: quiz -? Was liefert hasNextLine()? -→ Eine Datei lesen mit Scanner -- [ ] die nächste Zeile -- [x] true, wenn es noch eine Zeile gibt, sonst false -- [ ] die Anzahl der Zeilen -> Mit hasNextLine() prüft die Schleife, ob es weitergeht. - -? Was passiert, wenn du new PrintWriter("liste.txt") für eine Datei aufrufst, die es schon gibt? -→ In eine Datei schreiben mit PrintWriter -- [ ] Der neue Text wird hinten angehängt. -- [x] Die Datei wird überschrieben. -- [ ] Java meldet einen Fehler. - -? Warum bleibt eine Datei leer, obwohl println aufgerufen wurde? -→ In eine Datei schreiben mit PrintWriter -- [ ] Weil println nur in die Konsole schreibt. -- [x] Weil close() fehlt. -- [ ] Weil der Dateiname auf .txt endet. -> Erst close() schreibt den Zwischenspeicher in die Datei. - -? Ordne jeder Methode ihre Aufgabe zu. -→ Eine Datei lesen mit Scanner -= nextLine() -> liest die nächste Zeile -= println(text) -> schreibt eine Zeile -= close() -> schließt die Datei -::: diff --git a/web/lessons/de/24-zeile-fuer-zeile.md b/web/lessons/de/24-zeile-fuer-zeile.md deleted file mode 100644 index 385844ed..00000000 --- a/web/lessons/de/24-zeile-fuer-zeile.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -titel: Zeile für Zeile mit BufferedReader -bereich: Dateien -kurz: Mit BufferedReader und readLine lesen, bis null kommt, und mit Files eine ganze Datei auf einmal lesen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Datei mit BufferedReader und readLine lesen -- das Dateiende erkennen, weil readLine dort null liefert -- zählen, wie oft ein Wort in einer Datei vorkommt -- eine ganze Datei mit Files.readString lesen und mit split in Zeilen zerlegen -- den längsten Eintrag einer Datei finden -::: - -## Der Kuchen ist weg! - -Am Morgen nach dem Fest ist Ludwigs Kuchen verschwunden. Zum Glück führt die Burg ein Logbuch. Jede Zeile beginnt mit dem Tag und der Uhrzeit: - -::: datei logbuch.txt -Mo 08:15 Ludwig füttert die Ziegen -Mo 12:30 Zora liest im Turm -Mo 18:00 Fuchs schleicht am Tor vorbei -Di 09:00 Ludwig backt einen Kuchen -Di 14:45 Hase bringt Karotten -Di 16:20 Fuchs fragt nach dem Rezept -Di 23:55 Der Kuchen ist weg -Mi 07:00 Zora findet Krümel vor dem Fuchsbau -::: - -Mit dem Scanner aus Lektion 23 könntest du es schon lesen. Profis nehmen für Dateien aber oft den **BufferedReader**. - -## Lesen mit BufferedReader - -```java -import java.io.*; - -BufferedReader leser = new BufferedReader(new FileReader("logbuch.txt")); -String zeile = leser.readLine(); -while (zeile != null) { - System.out.println(zeile); - zeile = leser.readLine(); -} -leser.close(); -``` - -- `new FileReader("logbuch.txt")` öffnet die Datei, `new BufferedReader(…)` legt einen Puffer drumherum. Er holt die Datei in großen Stücken und ist deshalb bei langen Dateien schneller. -- `readLine()` liefert die nächste Zeile. Gibt es keine Zeile mehr, liefert es **null**. -- Die Schleife läuft deshalb, solange `zeile != null` ist. Am Ende jedes Durchlaufs liest sie die nächste Zeile. - -::: diagramm dt-buffered.visustruct -Erst eine Zeile lesen, dann prüfen, dann am Ende des Durchlaufs die nächste lesen. -::: - -**null** heißt „gar nichts“. Es ist nicht der leere Text `""`, sondern kein Text. Eine Variable mit `null` hat keine Methoden: `zeile.length()` bricht dann mit einer `NullPointerException` ab. Prüfe deshalb immer zuerst `zeile != null`. - -| | Scanner | BufferedReader | -|---|---|---| -| Anlegen | `new Scanner(new File(…))` | `new BufferedReader(new FileReader(…))` | -| Gibt es noch eine Zeile? | `hasNextLine()` | `readLine()` liefert `null` | -| Nächste Zeile | `nextLine()` | `readLine()` | -| Stärke | kann auch Zahlen lesen (`nextInt()`) | schnell, auch bei großen Dateien | - -::: ludwig -Die Schleife sieht anders aus als beim Scanner: Die erste Zeile wird **vor** der Schleife gelesen, jede weitere **am Ende** des Durchlaufs. Vergisst du das Lesen am Ende, liest das Programm immer wieder dieselbe Zeile und hört nie auf. -::: - -::: angeberwissen -Wo eine Zeile endet, ist nicht überall gleich: Windows schreibt zwei unsichtbare Zeichen, „Wagenrücklauf“ und „Zeilenvorschub“, Linux und macOS nur den Zeilenvorschub. Die Namen stammen von der Schreibmaschine, bei der der Papierwagen zurückfuhr und das Papier eine Zeile weiterrückte. `readLine()` kommt mit beidem klar. -::: - -## Übung: Wie oft kommt der Fuchs vor? - -::: übung l24-java-fuchs -titel: Wie oft kommt der Fuchs vor? -code: java -auftrag: Lies das Logbuch mit dem BufferedReader und zähle die Zeilen, in denen **Fuchs** vorkommt. Gib am Ende **Fuchs:** und die Anzahl aus. -start: l24-java-fuchs-start.visustruct -test: - -> Fuchs: 3 -braucht: whileSchleife, verzweigung -tipp: Lege vor der Schleife `int anzahl = 0;` an. In der Schleife prüfst du `zeile.contains("Fuchs")`. -tipp: Das `zeile = leser.readLine();` gehört ans Ende der Schleife, nach dem if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Automatisch schließen - -Damit `close()` auch nach einer Exception nicht vergessen wird, gibt es eine Kurzform von try: Was in den runden Klammern hinter `try` angelegt wird, schließt Java am Ende von selbst. - -```java -try (BufferedReader leser = new BufferedReader(new FileReader("logbuch.txt"))) { - String zeile = leser.readLine(); - while (zeile != null) { - System.out.println(zeile); - zeile = leser.readLine(); - } -} catch (IOException e) { - System.out.println("Das Logbuch fehlt."); -} -``` - -Das heißt **try-with-resources**. `IOException` fängt alle Fehler beim Lesen und Schreiben auf, auch eine fehlende Datei. - -> Wie try und catch läuft auch try-with-resources nur mit echtem Java (Lektion 22). In der Simulation schreibst du `close()` selbst. - -## Die Abkürzung: Files - -Seit Java 11 geht es noch kürzer. `Files.readString` liest die ganze Datei auf einmal, das kennst du aus Lektion 17. Mit `split("\n")` zerlegst du den Text in ein Array von Zeilen: - -```java -import java.nio.file.*; - -String text = Files.readString(Path.of("logbuch.txt")); -String[] zeilen = text.split("\n"); -for (int i = 0; i < zeilen.length; i++) { - System.out.println(zeilen[i]); -} -``` - -Jetzt stehen alle Zeilen im Array. Du kannst sie in beliebiger Reihenfolge ansehen, zum Beispiel rückwärts oder nur jede zweite. - -| Weg | Gut für | -|---|---| -| `Scanner` | kleine Dateien, Zahlen und Wörter lesen | -| `BufferedReader` | große Dateien, Zeile für Zeile | -| `Files.readString` | kleine Dateien, die ganz ins Programm passen | - -::: ludwig -Eine Datei mit einer Million Zeilen liest du besser mit dem BufferedReader Zeile für Zeile. Mit `Files.readString` müsste die ganze Datei auf einmal in den Speicher. -::: - -## Übung: Der längste Eintrag - -::: übung l24-java-laengster -titel: Der längste Eintrag -code: java -auftrag: Im Startcode stehen die Zeilen des Logbuchs schon im Array `zeilen`. Finde die längste Zeile und gib sie so aus: **Längster Eintrag:** und dann die Zeile. -start: l24-java-laengster-start.visustruct -test: - -> Längster Eintrag: Mi 07:00 Zora findet Krümel vor dem Fuchsbau -braucht: forSchleife, verzweigung -tipp: Merke dir die bisher längste Zeile in `String laengster = zeilen[0];` und gehe die anderen Zeilen mit einer for-Schleife durch. -tipp: Ist `zeilen[i].length() > laengster.length()`, wird `zeilen[i]` der neue Längste. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Spiel: Datei-Detektiv - -::: spiel frage -titel: Spiel: Datei-Detektiv -intro: Wer hat den Kuchen genommen? Jedes Programm durchsucht das **Logbuch** von oben. Sag voraus, was es ausgibt, und sammle so die Hinweise. Fünf Runden. -frage: Was gibt das Programm aus? - -runde: - String zeile = leser.readLine(); - zeile = leser.readLine(); - System.out.println(zeile.substring(9)); -- Ludwig füttert die Ziegen -- [x] Zora liest im Turm -- Mo 12:30 Zora liest im Turm -> Das zweite `readLine()` liefert die zweite Zeile. Ab Index 9 beginnt der Text nach der Uhrzeit. - -runde: - int n = 0; - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.startsWith("Di")) n++; - zeile = leser.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Vier Einträge stammen vom Dienstag. - -runde: - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.contains("Kuchen")) { - System.out.println(zeile.substring(3, 8)); - } - zeile = leser.readLine(); - } -- [x] 09:00\n23:55 -- 09:00 -- Di\nDi -> Zwei Zeilen enthalten „Kuchen“. `substring(3, 8)` schneidet die Uhrzeit heraus. - -runde: - String zeile = leser.readLine(); - String letzte = ""; - while (zeile != null) { - if (zeile.contains("Fuchs")) letzte = zeile; - zeile = leser.readLine(); - } - System.out.println(letzte); -- Di 16:20 Fuchs fragt nach dem Rezept -- [x] Mi 07:00 Zora findet Krümel vor dem Fuchsbau -- Mo 18:00 Fuchs schleicht am Tor vorbei -> `letzte` wird bei jedem Treffer überschrieben. Am Ende steht der letzte Treffer darin. „Fuchsbau“ enthält auch „Fuchs“. - -runde: - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.contains("Krümel")) { - int bei = zeile.indexOf("vor dem "); - System.out.println(zeile.substring(bei + 8)); - } - zeile = leser.readLine(); - } -- [x] Fuchsbau -- vor dem Fuchsbau -- Krümel -> Die Krümel liegen vor dem Fuchsbau. Der Fuchs hat nach dem Rezept gefragt, und um 23:55 war der Kuchen weg. Fall gelöst! -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Datei mit BufferedReader und readLine lesen. → Lesen mit BufferedReader -- Ich kann das Dateiende erkennen, weil readLine dort null liefert (zeile != null). → Lesen mit BufferedReader -- Ich kann zählen, wie oft ein Wort in einer Datei vorkommt. → Übung: Wie oft kommt der Fuchs vor? -- Ich kann eine ganze Datei mit Files.readString lesen und mit split in Zeilen zerlegen. → Die Abkürzung: Files -- Ich kann den längsten Eintrag einer Datei finden. → Übung: Der längste Eintrag -::: - -## Wissenstest - -::: quiz -? Was liefert readLine(), wenn die Datei zu Ende ist? -→ Lesen mit BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null heißt: Es gibt keinen Text mehr. - -? Warum steht in der Schleife am Ende noch einmal zeile = leser.readLine()? -→ Lesen mit BufferedReader -- [ ] Damit die Datei geschlossen wird. -- [x] Sonst bleibt zeile immer gleich, und die Schleife hört nie auf. -- [ ] Damit die erste Zeile doppelt ausgegeben wird. - -? Was macht try-with-resources? -→ Lesen mit BufferedReader -- [ ] Es liest die Datei schneller. -- [x] Es schließt die Datei am Ende von selbst. -- [ ] Es verhindert jede Exception. - -? Ordne jedem Weg zu, wofür er gut ist. -→ Die Abkürzung: Files -= BufferedReader -> große Dateien, Zeile für Zeile -= Files.readString -> kleine Dateien auf einmal -= Scanner -> Zahlen und Wörter lesen -::: diff --git a/web/lessons/de/25-tabellen-als-csv.md b/web/lessons/de/25-tabellen-als-csv.md deleted file mode 100644 index c4421843..00000000 --- a/web/lessons/de/25-tabellen-als-csv.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -titel: Tabellen als CSV -bereich: Dateien -kurz: Eine Tabelle als CSV-Datei lesen, jede Zeile mit split zerlegen und mit den Zahlen rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- den Aufbau einer CSV-Datei erklären: Kopfzeile, Datensätze, Trennzeichen -- eine Zeile mit split zerlegen und mit Integer.parseInt eine Zahl daraus machen -- die Kopfzeile überspringen, bevor ich rechne -- einen Durchschnitt aus einer Spalte einer CSV-Datei berechnen -- den besten Wert in einer Spalte einer CSV-Datei finden -- eine Stärke und eine Schwäche von CSV nennen -::: - -## Eine Tabelle in einer Textdatei - -Frau Ziege, die Lehrerin der Burgschule, hat die Noten ihrer Klasse als Tabelle gespeichert: - -| Name | Mathe | Deutsch | Englisch | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Fuchs | 4 | 3 | 5 | -| Hase | 2 | 1 | 3 | - -In der Datei sieht die Tabelle so aus: - -::: datei klasse.csv -Name;Mathe;Deutsch;Englisch -Ludwig;2;3;1 -Zora;1;2;2 -Fuchs;4;3;5 -Hase;2;1;3 -::: - -Das ist eine **CSV-Datei**. CSV heißt „Comma-Separated Values“, also „durch Kommas getrennte Werte“. Jede Zeile ist eine Zeile der Tabelle, die Spalten sind durch ein Trennzeichen getrennt. In Deutschland nimmt man meist das **Semikolon** `;`, weil das Komma schon in Zahlen wie `2,5` steckt. - -- Die erste Zeile ist die **Kopfzeile**. Sie sagt, was in den Spalten steht. -- Jede weitere Zeile ist ein **Datensatz**, hier eine Schülerin oder ein Schüler. -- Excel, LibreOffice und Numbers öffnen CSV-Dateien direkt als Tabelle. - -## Eine Zeile zerlegen mit split - -`split(";")` kennst du aus Lektion 17. Es zerlegt einen Text an jedem Semikolon und liefert ein Array: - -```java -String zeile = "Zora;1;2;2"; -String[] teile = zeile.split(";"); -// teile[0] ist "Zora", teile[1] ist "1", teile[2] ist "2", teile[3] ist "2" -``` - -Die Noten sind danach noch **Text**. Rechnen kannst du erst, wenn du sie mit `Integer.parseInt` in eine Zahl verwandelst: - -```java -int mathe = Integer.parseInt(teile[1]); -``` - -::: diagramm dt-csv.visustruct -Eine CSV-Zeile zerlegen und die Mathenote als Zahl lesen. -::: - -## Die ganze Datei lesen - -Für die ganze Datei kombinierst du den BufferedReader aus Lektion 24 mit `split`. Die Kopfzeile enthält keine Noten, deshalb liest du sie vor der Schleife und überspringst sie: - -```java -BufferedReader leser = new BufferedReader(new FileReader("klasse.csv")); -String zeile = leser.readLine(); // Kopfzeile -zeile = leser.readLine(); // erster Datensatz -while (zeile != null) { - String[] teile = zeile.split(";"); - System.out.println(teile[0] + ": Mathe " + teile[1]); - zeile = leser.readLine(); -} -leser.close(); -``` - -::: ludwig -Ohne das zweite `readLine()` vor der Schleife landet die Kopfzeile in der Schleife. Dann versucht `Integer.parseInt("Mathe")` aus dem Wort eine Zahl zu machen, und Java bricht mit einer `NumberFormatException` ab. -::: - -## Übung: Notenschnitt in Mathe - -::: übung l25-java-schnitt -titel: Notenschnitt in Mathe -code: java -auftrag: Berechne den Notenschnitt in Mathe. Überspringe die Kopfzeile, addiere die Mathenoten (Spalte 1) und teile durch die Anzahl der Datensätze. Gib **Schnitt Mathe:** und den Schnitt aus. -start: l25-java-schnitt-start.visustruct -test: - -> Schnitt Mathe: 2.25 -braucht: whileSchleife -tipp: Lege vor der Schleife `double summe = 0;` und `int anzahl = 0;` an. Mit `double` wird beim Teilen nichts abgeschnitten. -tipp: In der Schleife: `String[] teile = zeile.split(";");`, dann `summe = summe + Integer.parseInt(teile[1]);` und `anzahl++;`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Übung: Beste Note in Englisch - -::: übung l25-java-beste -titel: Beste Note in Englisch -code: java -auftrag: Wer hat die beste Note in Englisch (Spalte 3)? Gib **Beste Note in Englisch:**, den Namen und in Klammern die Note aus. -start: l25-java-beste-start.visustruct -test: - -> Beste Note in Englisch: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Merke dir die bisher beste Note in `int besteNote = 7;` und den Namen in `String besterName = "";`. Jede echte Note ist besser als 7. -tipp: Ist `note < besteNote`, merkst du dir die neue Note und `teile[0]` als Namen. -lösung: l25-java-beste-loesung.visustruct -::: - -## Stärken und Schwächen von CSV - -| Gut | Schwierig | -|---|---| -| Tabellen brauchen wenig Platz. | Steht das Trennzeichen im Text, zum Beispiel `Hase;sehr gut; fleißig`, zerfällt die Zeile in zu viele Teile. | -| Excel und andere Programme öffnen die Datei direkt. | Alles ist eine flache Tabelle. Hat ein Tier mehrere Lieblingsessen, passt das nicht gut in eine Zelle. | -| Jedes Programm kann sie mit `split` lesen. | Die Datei sagt nicht, ob `1` eine Zahl oder ein Text ist. | - -::: ludwig -Excel speichert CSV-Dateien in Deutschland mit Semikolon, in England und den USA mit Komma. Wer eine Datei aus dem Ausland öffnet, sieht deshalb manchmal alles in einer einzigen Spalte. -::: - -## Spiel: Kaputte Zeile - -::: spiel bombe -titel: Spiel: Kaputte Zeile -intro: In jeder CSV-Datei ist **genau eine Zeile kaputt**, und die Bombe tickt. Vergleiche jede Zeile mit der Kopfzeile und tippe auf die kaputte Zeile, bevor die Zeit abläuft. Fünf Runden, je 20 Sekunden. -zeit: 20 -daten: csv - -runde: - Name;Mathe;Deutsch;Englisch - Ludwig;2;3;1 - Zora;1 - Fuchs;4;3;5 -frage: Jede Zeile braucht vier Spalten. -fehler: 3 -besser: Zora;1;2;2 -> Bei Zora fehlen zwei Noten. `teile[3]` gibt es dann nicht, und Java bricht mit einer `ArrayIndexOutOfBoundsException` ab. - -runde: - Name;Note;Bemerkung - Ludwig;2;arbeitet gut mit - Hase;1;sehr gut; fleißig - Zora;1;liest viel -frage: Jede Zeile braucht drei Spalten. -fehler: 3 -besser: Hase;1;sehr gut, fleißig -> Das Semikolon in der Bemerkung trennt eine vierte Spalte ab. Im Text darf das Trennzeichen nicht vorkommen. - -runde: - Name;Mathe;Deutsch;Englisch - Ludwig;2;3;1 - Zora;1;2;2 - Fuchs,4,3,5 -frage: Getrennt wird mit Semikolon. -fehler: 4 -besser: Fuchs;4;3;5 -> Hier stehen Kommas statt Semikolons. `split(";")` findet kein Semikolon und liefert die ganze Zeile als ein einziges Teil. - -runde: - Name;Mathe;Deutsch;Englisch - Hase;2;1;3 - Fuchs;vier;3;5 - Ludwig;2;3;1 -frage: Noten sind Zahlen. -fehler: 3 -besser: Fuchs;4;3;5 -> `Integer.parseInt("vier")` kann aus dem Wort keine Zahl machen und wirft eine `NumberFormatException`. - -runde: - Name;Mathe;Deutsch;Englisch - Zora;1;2;2 - Hase;2;1;3 - Ludwig;2;;1 -frage: Kein Feld darf leer sein. -fehler: 4 -besser: Ludwig;2;3;1 -> Zwischen den beiden Semikolons steht nichts. `Integer.parseInt("")` wirft eine `NumberFormatException`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann den Aufbau einer CSV-Datei erklären: Kopfzeile, Datensätze, Trennzeichen. → Eine Tabelle in einer Textdatei -- Ich kann eine Zeile mit split zerlegen und mit Integer.parseInt eine Zahl daraus machen. → Eine Zeile zerlegen mit split -- Ich kann die Kopfzeile überspringen, bevor ich rechne. → Die ganze Datei lesen -- Ich kann einen Durchschnitt aus einer Spalte einer CSV-Datei berechnen. → Übung: Notenschnitt in Mathe -- Ich kann den besten Wert in einer Spalte einer CSV-Datei finden. → Übung: Beste Note in Englisch -- Ich kann eine Stärke und eine Schwäche von CSV nennen. → Stärken und Schwächen von CSV -::: - -## Wissenstest - -::: quiz -? Was liefert "Hase;2;1;3".split(";")[2]? -→ Eine Zeile zerlegen mit split -- [ ] "2" -- [x] "1" -- [ ] 1 -> Die Zählung beginnt bei 0. Und das Ergebnis ist ein Text, keine Zahl. - -? Warum liest das Programm vor der Schleife zweimal readLine()? -→ Die ganze Datei lesen -- [ ] Damit die Datei schneller gelesen wird. -- [x] Die erste Zeile ist die Kopfzeile ohne Noten. Sie wird übersprungen. -- [ ] Weil readLine immer zwei Zeilen braucht. - -? Was passiert bei Integer.parseInt("Mathe")? -→ Eine Zeile zerlegen mit split -- [ ] Es liefert 0. -- [x] Java wirft eine NumberFormatException. -- [ ] Es liefert die Länge des Wortes. - -? Ordne zu. -→ Stärken und Schwächen von CSV -= Kopfzeile -> sagt, was in den Spalten steht -= Datensatz -> eine Zeile mit Werten -= Trennzeichen -> trennt die Spalten, meist ; -::: diff --git a/web/lessons/de/26-json.md b/web/lessons/de/26-json.md deleted file mode 100644 index d6384c18..00000000 --- a/web/lessons/de/26-json.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -titel: Daten als JSON -bereich: Dateien -kurz: JSON-Dateien verstehen, Werte mit indexOf und substring herausholen und selbst JSON schreiben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Objekte, Schlüssel, Werte und Listen in JSON erkennen -- mit indexOf und substring einen Wert aus einer JSON-Zeile holen -- alle Werte zu einem Schlüssel aus einer JSON-Datei ausgeben -- mit + und \" einen JSON-Eintrag bauen und speichern -- Daten als JSON-Datei speichern -- eine Stärke und eine Schwäche von JSON nennen -::: - -## Daten mit Namen - -Der Burgzoo speichert seine Tiere in einer Datei `zoo.json`: - -::: datei zoo.json -{ - "zoo": "Burgzoo", - "tiere": [ - {"name": "Ludwig", "art": "Geißbock", "alter": 7}, - {"name": "Zora", "art": "Ziege", "alter": 6}, - {"name": "Fuchs", "art": "Rotfuchs", "alter": 4}, - {"name": "Hase", "art": "Feldhase", "alter": 2} - ] -} -::: - -Das ist **JSON**, die „JavaScript Object Notation“. Fast jede Web-App und jedes Spiel schickt und speichert Daten so. Anders als in einer CSV-Datei steht vor jedem Wert sein **Name**, der **Schlüssel**. - -| Zeichen | Bedeutung | Beispiel | -|---|---|---| -| `{ … }` | ein **Objekt**: Paare aus Schlüssel und Wert | `{"name": "Zora", "alter": 6}` | -| `"schlüssel": wert` | ein Paar, getrennt durch einen Doppelpunkt | `"alter": 6` | -| `[ … ]` | eine **Liste** von Werten, wie ein Array | `"tiere": [ … ]` | -| `,` | trennt die Paare und die Einträge einer Liste | | -| `"…"` | ein Text. Zahlen und `true`/`false` stehen ohne Anführungszeichen. | `"Ziege"`, `6` | - -Objekte und Listen lassen sich **verschachteln**: Das ganze Zoo-Objekt enthält die Liste `tiere`, und jedes Tier ist wieder ein Objekt. Wie bei Arrays zählt die Liste ab 0: `tiere[1].name` ist `"Zora"`. - -::: angeberwissen -JSON hat Douglas Crockford Anfang der 2000er bekannt gemacht. In die Lizenz seines JSON-Programms schrieb er: „Die Software soll für Gutes verwendet werden, nicht für Böses.“ Die Firma IBM fragte deshalb nach, ob sie es trotzdem benutzen darf. Er antwortete, IBM und seine Kunden dürften es auch für Böses verwenden. -::: - -## Einen Wert herausholen - -Profis lesen JSON mit einer Bibliothek, zum Beispiel **Gson**. Sie gehört nicht zu Java und muss erst eingebunden werden. Für unsere Zoo-Datei reichen die String-Methoden aus Lektion 17, denn jedes Tier steht in einer eigenen Zeile. - -Ein Anführungszeichen im Text schreibst du in Java als `\"`. Der Text `"\"name\": \""` ist also `"name": "`, genau das, was vor jedem Namen steht. - -```java -String zeile = "{\"name\": \"Zora\", \"art\": \"Ziege\", \"alter\": 6},"; -int start = zeile.indexOf("\"name\": \"") + 9; -int ende = zeile.indexOf("\"", start); -String name = zeile.substring(start, ende); -``` - -- `indexOf("\"name\": \"")` findet den Anfang von `"name": "`. Das sind 9 Zeichen, also beginnt der Name 9 Zeichen weiter. -- `indexOf("\"", start)` sucht **ab** `start` das nächste Anführungszeichen. Dort endet der Name. -- `substring(start, ende)` schneidet den Namen heraus: `Zora`. - -::: diagramm dt-json.visustruct -Den Namen aus einer JSON-Zeile holen. -::: - -## Übung: Alle Namen aus dem Zoo - -::: übung l26-java-namen -titel: Alle Namen aus dem Zoo -code: java -auftrag: Lies `zoo.json` Zeile für Zeile. Gib in jeder Zeile, die `"name"` enthält, nur den Namen aus. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Fuchs | Hase -braucht: whileSchleife, verzweigung -tipp: Prüfe mit `zeile.contains("\"name\"")`, ob in der Zeile ein Name steht. Die anderen Zeilen überspringst du. -tipp: Im if: `int start = zeile.indexOf("\"name\": \"") + 9;`, dann `int ende = zeile.indexOf("\"", start);` und `System.out.println(zeile.substring(start, ende));`. -lösung: l26-java-namen-loesung.visustruct -::: - -## JSON schreiben - -Zum Schreiben setzt du den Text mit `+` zusammen und schreibst ihn mit dem PrintWriter aus Lektion 23 in die Datei. Texte im JSON brauchen Anführungszeichen, also `\"` im Java-Text, Zahlen nicht: - -```java -String name = "Zora"; -int punkte = 15; -String eintrag = "{\"name\": \"" + name + "\", \"punkte\": " + punkte + "}"; -// eintrag ist {"name": "Zora", "punkte": 15} -``` - -Bei einer Liste steht zwischen den Einträgen ein Komma, hinter dem letzten aber **keins**. Das prüfst du mit einem if. - -::: ludwig -Ein einziges vergessenes Anführungszeichen oder ein Komma zu viel, und kein Programm kann die Datei mehr lesen. JSON ist streng. Darum benutzen Profis zum Schreiben auch eine Bibliothek. -::: - -## Übung: Punkte als JSON speichern - -::: übung l26-java-schreiben -titel: Punkte als JSON speichern -code: java -auftrag: Speichere die Punkte aus dem Quiz-Turnier als Liste in `punkte.json`. Jede Zeile enthält ein Objekt mit **name** und **punkte**, zwischen den Objekten steht ein Komma. Gib am Ende **3 Einträge gespeichert** aus. -start: l26-java-schreiben-start.visustruct -test: - -> 3 Einträge gespeichert -datei: punkte.json -> [ | {"name": "Ludwig", "punkte": 12}, | {"name": "Zora", "punkte": 15}, | {"name": "Fuchs", "punkte": 9} | ] -braucht: forSchleife, verzweigung -tipp: Gehe mit `for (int i = 0; i < namen.length; i++)` durch die Arrays. Baue den Eintrag mit `" {\"name\": \"" + namen[i] + "\", \"punkte\": " + punkte[i] + "}"`. -tipp: Ist `i < namen.length - 1`, hängst du noch `","` an. Schreibe nach der Schleife `"]"` und schließe die Datei. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Stärken und Schwächen von JSON - -| Gut | Schwierig | -|---|---| -| Jeder Wert hat einen Namen. Man sieht sofort, was `6` bedeutet. | Die Datei ist länger als eine CSV-Datei, weil die Schlüssel in jeder Zeile stehen. | -| Daten lassen sich verschachteln, zum Beispiel eine Liste in einem Objekt. | Java kann JSON nicht von selbst lesen. Man braucht eine Bibliothek wie Gson. | -| JSON ist der Standard im Web. Fast jede Programmiersprache kennt es. | Ein Zeichen zu viel oder zu wenig macht die ganze Datei unlesbar. | - -## Spiel: Welcher Schlüssel? - -::: spiel frage -titel: Spiel: Welcher Schlüssel? -intro: Ludwig sucht Werte in JSON-Dateien. Lies die Datei und sag, welcher Wert gemeint ist. Fünf Runden. - -runde: - {"name": "Zora", "art": "Ziege", "alter": 6} -frage: Was ist der Wert von **alter**? -- "alter" -- [x] 6 -- "Ziege" -> Vor dem Doppelpunkt steht der Schlüssel, dahinter der Wert. Zahlen stehen ohne Anführungszeichen. - -runde: - { - "tiere": [ - {"name": "Ludwig", "alter": 7}, - {"name": "Zora", "alter": 6}, - {"name": "Fuchs", "alter": 4} - ] - } -frage: Was ist **tiere[1].name**? -- Ludwig -- [x] Zora -- Fuchs -> Die Liste zählt wie ein Array ab 0. `tiere[1]` ist das zweite Tier. - -runde: - { - "zoo": "Burgzoo", - "chef": {"name": "Ludwig", "art": "Geißbock"} - } -frage: Wie kommst du an **Geißbock**? -- [x] chef.art -- art -- zoo.chef.art -> `art` steckt im Objekt `chef`. Darum geht der Weg über `chef`. - -runde: - { - "name": "Hase", - "lieblingsessen": ["Karotten", "Klee", "Löwenzahn"] - } -frage: Was ist **lieblingsessen[2]**? -- Klee -- Karotten -- [x] Löwenzahn -> Index 0 ist Karotten, 1 ist Klee, 2 ist Löwenzahn. So etwas passt in CSV nur schwer in eine Zelle. - -runde: - {"name": "Fuchs", "alter": 4, "hungrig": true} -frage: Welcher Schlüssel hat einen Wahrheitswert? -- name -- alter -- [x] hungrig -> `true` und `false` stehen wie Zahlen ohne Anführungszeichen. In Java wäre das ein `boolean`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Objekte, Schlüssel, Werte und Listen in JSON erkennen. → Daten mit Namen -- Ich kann mit indexOf und substring einen Wert aus einer JSON-Zeile holen. → Einen Wert herausholen -- Ich kann alle Werte zu einem Schlüssel aus einer JSON-Datei ausgeben. → Übung: Alle Namen aus dem Zoo -- Ich kann mit + und \" einen JSON-Eintrag bauen und speichern. → JSON schreiben -- Ich kann Daten als JSON-Datei speichern. → Übung: Punkte als JSON speichern -- Ich kann eine Stärke und eine Schwäche von JSON nennen. → Stärken und Schwächen von JSON -::: - -## Wissenstest - -::: quiz -? Was steht in JSON vor jedem Wert? -→ Daten mit Namen -- [ ] seine Zeilennummer -- [x] sein Schlüssel, zum Beispiel "name" -- [ ] ein Semikolon -> Schlüssel und Wert werden durch einen Doppelpunkt getrennt. - -? Wie schreibst du ein Anführungszeichen in einem Java-Text? -→ JSON schreiben -- [ ] "" -- [x] \" -- [ ] '"' - -? Was ist bei {"tiere": [{"name": "Ludwig"}, {"name": "Zora"}]} der Wert von tiere[0].name? -→ Daten mit Namen -- [x] Ludwig -- [ ] Zora -- [ ] tiere -> Die Liste zählt ab 0. - -? Ordne zu. -→ Stärken und Schwächen von JSON -= { } -> ein Objekt mit Schlüsseln und Werten -= [ ] -> eine Liste -= : -> trennt Schlüssel und Wert -::: diff --git a/web/lessons/de/27-formate-im-vergleich.md b/web/lessons/de/27-formate-im-vergleich.md deleted file mode 100644 index e671a2ba..00000000 --- a/web/lessons/de/27-formate-im-vergleich.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -titel: Formate im Vergleich -bereich: Dateien -kurz: Dieselben Daten als txt, CSV und JSON vergleichen, das passende Format wählen und eine Datei in ein anderes Format umwandeln. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- dieselben Daten als txt, CSV und JSON erkennen -- Stärken und Schwächen von txt, CSV und JSON vergleichen -- eine CSV-Datei in Sätze umwandeln -- eine CSV-Datei in JSON umwandeln -::: - -## Dreimal dieselben Tiere - -Ludwig hat die Tiere der Burg in drei Dateien gespeichert. In allen steht dasselbe, nur anders aufgeschrieben. - -Als Text, so wie man es einem Menschen schreiben würde: - -::: datei tiere.txt -Ludwig (Geißbock) ist 7 Jahre alt. -Zora (Ziege) ist 6 Jahre alt. -Fuchs (Rotfuchs) ist 4 Jahre alt. -::: - -Als Tabelle in CSV: - -::: datei tiere.csv -Name;Art;Alter -Ludwig;Geißbock;7 -Zora;Ziege;6 -Fuchs;Rotfuchs;4 -::: - -Als JSON: - -::: datei tiere.json -[ - {"name": "Ludwig", "art": "Geißbock", "alter": 7}, - {"name": "Zora", "art": "Ziege", "alter": 6}, - {"name": "Fuchs", "art": "Rotfuchs", "alter": 4} -] -::: - -## Was kann welches Format? - -| | txt | CSV | JSON | -|---|---|---|---| -| Für Menschen gut lesbar | ja | geht so | geht so | -| Feste Ordnung für Programme | nein | ja, Spalten | ja, Schlüssel | -| Was bedeutet ein Wert? | steht im Satz | steht in der Kopfzeile | steht direkt davor | -| Verschachteln (Liste in einem Eintrag) | nein | schwer | ja | -| Öffnen in Excel | nein | ja | nein | -| Lesen in Java | Scanner, BufferedReader | dazu `split` | String-Methoden oder eine Bibliothek | -| Größe | klein | am kleinsten | am größten | - -Kurz gesagt: - -- **txt** für Texte, die Menschen lesen: Briefe, Notizen, ein Logbuch. -- **CSV** für Tabellen: Noten, Messwerte, eine Bestenliste. Jede Zeile hat dieselben Spalten. -- **JSON** für Daten mit Struktur: Spielstände, Einstellungen, alles, was zwischen Programmen und im Web hin- und hergeht. - -::: ludwig -Wenn deine Wetter-App die Temperatur holt, kommt die Antwort fast immer als JSON. Und wenn die Lehrkraft die Noten aus dem Schulprogramm exportiert, bekommt sie oft eine CSV-Datei. -::: - -## Übung: Aus CSV werden Sätze - -::: übung l27-java-saetze -titel: Aus CSV werden Sätze -code: java -auftrag: Wandle `tiere.csv` in Text um. Schreibe für jedes Tier einen Satz wie **Ludwig (Geißbock) ist 7 Jahre alt.** in die Datei `saetze.txt`. Gib am Ende **3 Sätze gespeichert** aus. -start: l27-java-saetze-start.visustruct -test: - -> 3 Sätze gespeichert -datei: saetze.txt -> Ludwig (Geißbock) ist 7 Jahre alt. | Zora (Ziege) ist 6 Jahre alt. | Fuchs (Rotfuchs) ist 4 Jahre alt. -braucht: whileSchleife -tipp: Die Kopfzeile ist im Startcode schon übersprungen. Zerlege jede Zeile mit `String[] teile = zeile.split(";");`. -tipp: Der Satz ist `teile[0] + " (" + teile[1] + ") ist " + teile[2] + " Jahre alt."`. Zähle mit `anzahl++` mit und lies am Ende der Schleife die nächste Zeile. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Übung: Aus CSV wird JSON - -::: übung l27-java-json -titel: Aus CSV wird JSON -code: java -auftrag: Wandle `tiere.csv` in JSON um und speichere es in `tiere-neu.json`, genau wie `tiere.json` oben. Gib am Ende **3 Tiere gespeichert** aus. -start: l27-java-json-start.visustruct -test: - -> 3 Tiere gespeichert -datei: tiere-neu.json -> [ | {"name": "Ludwig", "art": "Geißbock", "alter": 7}, | {"name": "Zora", "art": "Ziege", "alter": 6}, | {"name": "Fuchs", "art": "Rotfuchs", "alter": 4} | ] -braucht: forSchleife, verzweigung -tipp: `zeilen[0]` ist die Kopfzeile. Beginne die for-Schleife deshalb bei `i = 1` und zerlege `zeilen[i]` mit `split(";")`. -tipp: Baue den Eintrag wie in Lektion 26 mit `\"`. Das Alter ist eine Zahl und steht ohne Anführungszeichen. Ein Komma hängst du nur an, wenn `i < zeilen.length - 1` ist. -lösung: l27-java-json-loesung.visustruct -::: - -## Spiel: Welches Format passt? - -::: spiel frage -titel: Spiel: Welches Format passt? -intro: Ludwig will Daten speichern und weiß nicht, welches Format er nehmen soll. Hilf ihm. Sechs Runden. -antworten: txt | CSV | JSON - -runde: -frage: Die Noten der ganzen Klasse sollen in Excel geöffnet werden. -richtig: CSV -> Noten sind eine Tabelle, und Excel öffnet CSV-Dateien direkt. - -runde: -frage: Ludwig schreibt Zora einen Brief. -richtig: txt -> Ein Brief ist Text für Menschen. Er braucht keine Spalten und keine Schlüssel. - -runde: -frage: Ein Spielstand mit Name, Level und einer Liste aller gesammelten Schlüssel. -richtig: JSON -> Eine Liste in einem Eintrag lässt sich in JSON verschachteln. In CSV passt sie nur schwer in eine Zelle. - -runde: -frage: Ein Messgerät speichert jede Minute Uhrzeit und Temperatur, ein ganzes Jahr lang. -richtig: CSV -> Viele gleiche Zeilen mit denselben Spalten: CSV ist dafür am kleinsten. JSON würde die Schlüssel in jeder Zeile wiederholen. - -runde: -frage: Eine Wetter-App holt die Vorhersage von einem Server im Internet. -richtig: JSON -> JSON ist der Standard im Web. Fast jede App bekommt ihre Daten so. - -runde: -frage: Das Logbuch der Burg, in dem jeder Eintrag ein kurzer Satz ist. -richtig: txt -> Jede Zeile ist ein Satz für Menschen. Gelesen wird sie Zeile für Zeile, wie in Lektion 24. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann dieselben Daten als txt, CSV und JSON erkennen. → Dreimal dieselben Tiere -- Ich kann Stärken und Schwächen von txt, CSV und JSON vergleichen. → Was kann welches Format? -- Ich kann eine CSV-Datei in Sätze umwandeln. → Übung: Aus CSV werden Sätze -- Ich kann eine CSV-Datei in JSON umwandeln. → Übung: Aus CSV wird JSON -::: - -## Wissenstest - -::: quiz -? Welches Format öffnet Excel direkt als Tabelle? -→ Was kann welches Format? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Warum ist eine JSON-Datei meist größer als eine CSV-Datei mit denselben Daten? -→ Was kann welches Format? -- [ ] Weil JSON mehr Zahlen speichert. -- [x] Weil die Schlüssel in jedem Eintrag wiederholt werden. -- [ ] Weil JSON keine Kommas benutzt. - -? In welchem Format sagt jeder Wert selbst, was er bedeutet? -→ Was kann welches Format? -- [ ] txt -- [ ] CSV -- [x] JSON -> In JSON steht der Schlüssel direkt vor dem Wert, zum Beispiel "alter": 7. - -? Ordne jedem Zweck ein Format zu. -→ Was kann welches Format? -= ein Brief an Zora -> txt -= die Notenliste der Klasse -> CSV -= ein Spielstand mit Listen -> JSON -::: diff --git a/web/lessons/de/28-erstes-fenster.md b/web/lessons/de/28-erstes-fenster.md deleted file mode 100644 index 72760ef5..00000000 --- a/web/lessons/de/28-erstes-fenster.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -titel: Dein erstes Fenster -bereich: Fenster mit Swing -kurz: Mit Java und Swing ein eigenes Fenster öffnen, Zeile für Zeile erklärt. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- im Bereich Code „Echtes Java“ einschalten -- ein Fenster mit Titel und Größe öffnen -- erklären, was jede Zeile des Fensterprogramms macht -- erklären, warum setVisible(true) am Ende steht -::: - -## Programme mit Fenstern - -Bisher haben deine Programme Text in die Konsole geschrieben. Die meisten Programme, die du kennst, haben aber **Fenster** mit Knöpfen und Textfeldern. Das nennt man eine grafische Oberfläche. - -Java bringt dafür einen Werkzeugkasten mit: **Swing**. In VisuStruct schaltest du ihn so ein: - -1. Öffne rechts den Bereich [Code](#zeige-codelive). -2. Klicke oben rechts auf das [Zahnrad](#zeige-einstellungen) und wähle als **Programmiersprache** Java. -3. Setze darunter im Abschnitt Java den Haken bei **Echtes Java (mit Swing)**. - -Beim ersten [▶︎ Ausführen](#zeige-ausfuehren) lädt VisuStruct einmalig etwa 40 MB, das dauert einen Moment. Danach läuft dein Code als echtes Java, und deine Fenster erscheinen im Bereich Code im Feld **Java-Fenster** über der Konsole. - -> Mit „Echtes Java“ funktionieren Eingaben über die Konsole (`Scanner`) noch nicht. Für Programme mit Eingabe schaltest du den Haken wieder aus. - -::: angeberwissen -Swing gibt es seit 1998. Davor hatte Java nur das ältere AWT mit einer Klasse `Frame`. Damit man die neuen Bausteine nicht verwechselt, beginnen in Swing fast alle Namen mit **J**: `JFrame`, `JButton`, `JLabel`. Am J erkennst du Swing also sofort. -::: - -## Das ganze Programm - -So sieht das kleinste Programm aus, das ein Fenster öffnet. Tippe es im Bereich Code ab und klicke auf [▶︎ Ausführen](#zeige-ausfuehren): - -```java -import javax.swing.*; - -public class MeinFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Mein erstes Fenster"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); - } -} -``` - -Ein leeres graues Fenster mit dem Titel „Mein erstes Fenster“ erscheint. Mehr nicht, aber es ist dein eigenes Fenster! - -Den Rahmen mit `public class` und `main` kennst du schon aus der Lektion Java. Neu sind die erste Zeile und die vier Zeilen in `main`. Die schauen wir uns jetzt einzeln an. - -::: ludwig -Schau dir das Struktogramm neben dem Code an: Jede Zeile in `main` ist ein Kasten, alle untereinander. Ein Fensterprogramm ist am Anfang also nur eine Sequenz. -::: - -## Zeile für Zeile - -| Zeile | Was sie macht | -|---|---| -| `import javax.swing.*;` | Holt den Werkzeugkasten Swing ins Programm. Ohne diese Zeile kennt Java `JFrame` nicht. | -| `JFrame fenster = new JFrame("Mein erstes Fenster");` | Baut ein neues Fenster. Der Text in Klammern ist der Titel oben im Fensterrahmen. Über die Variable `fenster` sprichst du es danach an. | -| `fenster.setSize(300, 200);` | Das Fenster wird 300 Pixel breit und 200 Pixel hoch. | -| `fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);` | Schließt jemand das Fenster, endet auch das Programm. | -| `fenster.setVisible(true);` | Jetzt erst wird das Fenster sichtbar. | - -Der Punkt in `fenster.setSize(…)` bedeutet: „Fenster, mach das!“ Vor dem Punkt steht, wer etwas tun soll, dahinter, was es tun soll. - -> Ohne `setDefaultCloseOperation` versteckt Java das Fenster beim Schließen nur. Das Programm läuft dann unsichtbar weiter. - -::: quiz -? Was bedeutet der Punkt in `fenster.setSize(300, 200)`? -- [x] „Fenster, mach das!“: Vor dem Punkt steht, wer etwas tut, dahinter, was es tut. -- [ ] Er trennt die beiden Zahlen. -- [ ] Er beendet die Zeile. -> Vor dem Punkt steht das Objekt `fenster`, dahinter seine Methode `setSize`. -::: - -## Warum setVisible am Ende steht - -`setVisible(true)` zeigt das Fenster so, wie es in diesem Moment ist. Deshalb richtest du zuerst alles ein: Titel, Größe, später auch Knöpfe. Erst ganz am Ende machst du das Fenster sichtbar. - -Was du erst nach `setVisible(true)` ins Fenster legst, erscheint oft nicht. Java zeichnet das Fenster nämlich nicht von selbst neu. Manchmal taucht es erst auf, wenn man die Größe des Fensters ändert. Solche Fehler sind schwer zu finden. - -::: ludwig -Erst einrichten, dann zeigen. Wie im Theater: Der Vorhang geht erst auf, wenn die Bühne fertig aufgebaut ist. -::: - -Probier es aus: Lösche die Zeile mit `setVisible(true)` und führe das Programm noch einmal aus. Das Programm läuft, aber du siehst kein Fenster. - -## Klassen und Objekte - -Das steht nicht im Lehrplan, hilft dir aber, Swing zu verstehen. - -::: angeberwissen -Java ist eigentlich eine **objektorientierte** Sprache. `JFrame` ist eine Klasse, also ein Bauplan für Fenster. Mit `new JFrame(…)` baut Java nach diesem Bauplan ein echtes Fenster, ein **Objekt**. Du könntest mit zwei `new` auch zwei Fenster bauen, beide nach demselben Bauplan. - -`setSize` und `setVisible` sind Methoden, die jedes Fenster-Objekt kann. Deshalb schreibst du `fenster.setSize(…)`: Das Objekt `fenster` führt seine Methode aus. Auch dein Programm selbst ist eine Klasse, nämlich `MeinFenster`. -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Ludwig hat am Fensterprogramm herumgebastelt. Lies die Zeilen und sag vorher, was passiert. Sechs Runden. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: Was siehst du nach dem Ausführen? -- ein Fenster mit dem Titel „Hallo“ -- [x] gar kein Fenster -- eine Fehlermeldung -> `setVisible(true)` fehlt. Das Fenster ist gebaut, bleibt aber unsichtbar. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(200, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); -frage: Wie sieht das Fenster aus? -- breiter als hoch -- [x] höher als breit -- quadratisch -> Die erste Zahl ist die Breite, die zweite die Höhe: 200 breit, 300 hoch. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(300, 200); - fenster.setVisible(true); -frage: Du schließt das Fenster. Was passiert mit dem Programm? -- Es endet. -- [x] Es läuft unsichtbar weiter. -- Das Fenster geht sofort wieder auf. -> Ohne `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)` versteckt Java das Fenster nur. - -runde: - JFrame fenster = new JFrame(); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); -frage: Was steht oben im Fensterrahmen? -- fenster -- JFrame -- [x] nichts, der Titel ist leer -> Ohne Text in den Klammern hat das Fenster keinen Titel. Der Variablenname `fenster` erscheint nie im Fenster. - -runde: - public class Test { - public static void main(String[] args) { - JFrame fenster = new JFrame("Test"); - fenster.setVisible(true); - } - } -frage: Was meldet javac? -- [x] cannot find symbol: class JFrame -- nichts, das Fenster erscheint -- missing return statement -> Die Zeile `import javax.swing.*;` fehlt. Ohne sie kennt Java `JFrame` nicht. - -runde: - JFrame a = new JFrame("Eins"); - JFrame b = new JFrame("Zwei"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: Was siehst du? -- [x] ein Fenster mit dem Titel „Eins“ -- zwei Fenster -- ein Fenster mit dem Titel „Zwei“ -> Zwei Fenster nach demselben Bauplan, aber nur `a` wird sichtbar gemacht. -::: - -## Aufgabe: Dein eigenes Fenster - -Ändere das Programm so, dass - -- im Titel „Hallo“ und dein Name steht, -- das Fenster 400 Pixel breit und 300 Pixel hoch ist, -- das Programm endet, wenn du das Fenster schließt. - -Führe es aus und prüfe alle drei Punkte. - -::: ludwig -Du willst nicht so viel tippen? Mit „Echtes Java“ darfst du im Bereich Code auch nur die vier Zeilen aus `main` schreiben. VisuStruct packt sie dann selbst in eine Klasse und ergänzt die `import`-Zeilen. In einer richtigen Java-Entwicklungsumgebung brauchst du aber das ganze Programm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann im Bereich Code „Echtes Java“ einschalten. → Programme mit Fenstern -- Ich kann ein Fenster mit Titel und Größe öffnen. → Das ganze Programm -- Ich kann erklären, was jede Zeile des Fensterprogramms macht. → Zeile für Zeile -- Ich kann erklären, warum setVisible(true) am Ende steht. → Warum setVisible am Ende steht -::: - -## Wissenstest - -::: quiz -? Welche Zeile macht das Fenster sichtbar? -→ Warum setVisible am Ende steht -- [ ] fenster.setSize(300, 200); -- [x] fenster.setVisible(true); -- [ ] new JFrame("Hallo"); -> Ohne setVisible(true) bleibt das Fenster unsichtbar. - -? Wo steht fenster.setVisible(true) am besten? -→ Warum setVisible am Ende steht -- [ ] Ganz am Anfang von main -- [x] Am Ende, wenn das Fenster fertig eingerichtet ist -- [ ] Das ist egal. -> Was nach setVisible(true) dazukommt, erscheint oft nicht. - -? Was bewirkt setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)? -→ Zeile für Zeile -- [ ] Das Fenster schließt sich sofort. -- [x] Schließt man das Fenster, endet das Programm. -- [ ] Das Fenster lässt sich nicht mehr schließen. - -? Woran erkennst du Bausteine aus Swing? -→ Zeile für Zeile -- [x] Ihr Name beginnt mit J, z. B. JFrame. -- [ ] Sie stehen in geschweiften Klammern. -- [ ] Sie enden mit Window. -::: diff --git a/web/lessons/de/29-knopf-im-fenster.md b/web/lessons/de/29-knopf-im-fenster.md deleted file mode 100644 index 18d08938..00000000 --- a/web/lessons/de/29-knopf-im-fenster.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -titel: Ein Knopf im Fenster -bereich: Fenster mit Swing -kurz: Einen Knopf ins Fenster legen und mit einem Layout ordentlich anordnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Knopf bauen und mit add ins Fenster legen -- erklären, warum der Knopf vor setVisible(true) hineinkommt -- mit FlowLayout dafür sorgen, dass der Knopf normal groß ist -- ein Fenster mit einem Knopf bauen, den man drücken kann -::: - -## Einen Knopf bauen - -Ein leeres Fenster ist langweilig. Jetzt kommt ein Knopf hinein. In Swing heißt ein Knopf **JButton** (button ist englisch für Knopf). - -Zwei neue Zeilen brauchst du dafür: - -```java -JButton knopf = new JButton("Klick mich"); -fenster.add(knopf); -``` - -- `new JButton("Klick mich")` baut einen Knopf. Der Text in Klammern steht später auf dem Knopf. -- `fenster.add(knopf)` legt den Knopf ins Fenster. Ohne `add` gibt es den Knopf zwar, aber er liegt nirgends und du siehst ihn nicht. - -::: quiz -? Was steht auf dem Knopf nach `new JButton("Start")`? -- [ ] knopf -- [x] Start -- [ ] JButton -> Der Text in Klammern ist die Beschriftung. `knopf` ist nur der Name der Variable. -::: - -## Das ganze Programm - -Beide Zeilen kommen **vor** `setVisible(true)`, denn das Fenster soll erst fertig eingerichtet und dann gezeigt werden: - -```java -import javax.swing.*; - -public class KnopfFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton knopf = new JButton("Klick mich"); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -``` - -Führe das Programm aus. Überraschung: Der Knopf ist riesig und füllt das ganze Fenster! - -::: ludwig -Die Reihenfolge ist wie beim ersten Fenster: einrichten, Knopf bauen, Knopf hineinlegen und ganz zum Schluss zeigen. Die Leerzeilen im Code helfen dir, diese Teile zu sehen. -::: - -## Der Riesenknopf - -Wie ein Fenster seine Teile anordnet, bestimmt sein **Layout**. Ein `JFrame` legt ohne weitere Angabe alles in die Mitte und macht es so groß wie das ganze Fenster. - -Mit einem anderen Layout bekommt der Knopf seine normale Größe. Das **FlowLayout** stellt alles wie Wörter in einer Zeile nebeneinander, so groß wie nötig: - -```java -import javax.swing.*; -import java.awt.*; - -public class KnopfFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new FlowLayout()); - - JButton knopf = new JButton("Klick mich"); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -``` - -`FlowLayout` gehört zum älteren Werkzeugkasten AWT. Deshalb steht oben eine zweite Zeile: `import java.awt.*;`. - -::: angeberwissen -Beim FlowLayout fließen die Bausteine wie Text: Passt ein Knopf nicht mehr in die Zeile, rutscht er in die nächste. Mach das Fenster schmal und schau zu, wie die Knöpfe umbrechen. Flow ist englisch für Fluss. -::: - -## Klicken geht, aber nichts passiert - -Klicke auf den Knopf. Er wird kurz dunkler, wenn du ihn drückst. Swing zeigt also, dass er gedrückt wurde. Sonst passiert aber nichts. - -Das ist kein Fehler: Du hast dem Programm noch nicht gesagt, **was** beim Klick passieren soll. Das lernst du in der nächsten Lektion. - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Fensterprogramm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was das Programm tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sechs Runden, je 25 Sekunden. -zeit: 25 - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - JButton knopf = new JButton(Klick mich); - fenster.add(knopf); - fenster.setVisible(true); -frage: Soll einen Knopf „Klick mich“ zeigen. -fehler: 3 -besser: JButton knopf = new JButton("Klick mich"); -> Die Beschriftung ist ein Text und braucht Anführungszeichen. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - JButton knopf = new JButton("OK"); - fenster.add(fenster); - fenster.setVisible(true); -frage: Soll den Knopf „OK“ ins Fenster legen. -fehler: 4 -besser: fenster.add(knopf); -> Ins Fenster gehört der Knopf, nicht das Fenster selbst. - -runde: - import java.swing.*; - import java.awt.*; -frage: Soll Swing und das FlowLayout ins Programm holen. -fehler: 1 -besser: import javax.swing.*; -> Swing liegt in `javax.swing`, mit x. Das FlowLayout kommt aus `java.awt`, ohne x. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout); - fenster.add(new JButton("OK")); - fenster.setVisible(true); -frage: Soll den Knopf in normaler Größe zeigen. -fehler: 3 -besser: fenster.setLayout(new FlowLayout()); -> Nach `new` und dem Namen kommen immer runde Klammern, auch wenn nichts darin steht. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout()); - JButton knopf = new Jbutton("OK"); - fenster.add(knopf); - fenster.setVisible(true); -frage: Soll einen Knopf „OK“ zeigen. -fehler: 4 -besser: JButton knopf = new JButton("OK"); -> Java unterscheidet Groß- und Kleinschreibung: `JButton` mit großem B. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout()); - fenster.add(new JButton("OK")); - fenster.setVisible(false); -frage: Soll das Fenster mit dem Knopf zeigen. -fehler: 5 -besser: fenster.setVisible(true); -> Mit `false` bleibt das Fenster unsichtbar. -::: - -## Aufgabe: Klick mich - -Baue ein Fenster mit dem Titel „Knopf-Test“, 300 mal 200 Pixel groß, mit einem Knopf „Klick mich“ in normaler Größe. Der Knopf soll sich drücken lassen, auch wenn noch nichts passiert. - -Zusatz: Lege einen zweiten Knopf „Ich auch“ daneben. In welcher Reihenfolge stehen die Knöpfe? Tausche die beiden `add`-Zeilen und probiere es noch einmal. - -**Du bist fertig, wenn …** - -- oben im Fenster „Knopf-Test“ steht, -- das Fenster 300 Pixel breit und 200 Pixel hoch ist, -- der Knopf „Klick mich“ nur so groß ist wie seine Schrift und nicht das ganze Fenster füllt, -- sich der Knopf beim Klicken sichtbar eindrückt. - -::: ludwig -Jeder Knopf braucht eine eigene Variable, zum Beispiel `knopf` und `knopf2`, und ein eigenes `add`. Das FlowLayout stellt die Knöpfe in der Reihenfolge der `add`-Zeilen auf. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Knopf bauen und mit add ins Fenster legen. → Einen Knopf bauen -- Ich kann erklären, warum der Knopf vor setVisible(true) hineinkommt. → Das ganze Programm -- Ich kann mit FlowLayout dafür sorgen, dass der Knopf normal groß ist. → Der Riesenknopf -- Ich kann ein Fenster mit einem Knopf bauen, den man drücken kann. → Aufgabe: Klick mich -::: - -## Wissenstest - -::: quiz -? Was macht fenster.add(knopf)? -→ Einen Knopf bauen -- [ ] Es baut einen neuen Knopf. -- [x] Es legt den Knopf ins Fenster. -- [ ] Es macht den Knopf klickbar. -> Gebaut wird der Knopf mit new JButton(…). - -? Warum füllt der Knopf ohne FlowLayout das ganze Fenster? -→ Der Riesenknopf -- [ ] Weil der Text zu lang ist -- [x] Weil das Fenster ohne eigenes Layout alles so groß wie das Fenster macht -- [ ] Weil setSize fehlt - -? Welche import-Zeile brauchst du für FlowLayout? -→ Der Riesenknopf -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> FlowLayout stammt aus dem älteren AWT. - -? Was passiert beim Klick auf den Knopf in dieser Lektion? -→ Aufgabe: Klick mich -- [ ] Das Programm endet. -- [x] Der Knopf wird kurz dunkler, sonst passiert nichts. -- [ ] Auf dem Knopf steht „Ich wurde geklickt“. -> Was beim Klick passieren soll, lernst du in der nächsten Lektion. -::: diff --git a/web/lessons/de/30-der-knopf-reagiert.md b/web/lessons/de/30-der-knopf-reagiert.md deleted file mode 100644 index 2104a6ee..00000000 --- a/web/lessons/de/30-der-knopf-reagiert.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -titel: Der Knopf reagiert -bereich: Fenster mit Swing -kurz: Mit einem ActionListener festlegen, was beim Klick passiert. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Ereignis ist -- mit addActionListener festlegen, was beim Klick passiert -- beim Klick mehrere Anweisungen ausführen -- einen Knopf bauen, der nach dem Klick seinen Text ändert -::: - -## Ein Klick ist ein Ereignis - -In der letzten Lektion ließ sich der Knopf drücken, aber nichts geschah. Bei einem Fensterprogramm bestimmt nämlich der Mensch, wann etwas passiert: Er klickt, tippt oder schließt ein Fenster. Jede solche Aktion heißt **Ereignis**. - -Dein Programm muss deshalb auf den Klick warten und ihn bemerken. Dafür meldest du beim Knopf einen Zuhörer an, einen **ActionListener** (englisch für „Aktions-Zuhörer“). Bei jedem Klick ruft der Knopf seinen Zuhörer, und der führt deinen Code aus. - -::: ludwig -Bisher lief dein Programm von oben nach unten durch, wie im Struktogramm. Der Code im Zuhörer läuft dagegen erst später, nämlich immer dann, wenn jemand klickt. Wie oft das ist, weiß vorher niemand. -::: - -## Die Zeile mit dem Pfeil - -So meldest du den Zuhörer an: - -```java -knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); -``` - -Von außen nach innen gelesen: - -- `knopf.addActionListener( … )` meldet beim Knopf einen Zuhörer an. -- `e` ist das Ereignis. Darin steht zum Beispiel, wann geklickt wurde. Hier brauchst du es nicht, aber der Name muss dastehen. -- `->` liest du als „dann mach“. -- `knopf.setText("Ich wurde geklickt")` ist das, was beim Klick passiert: Der Knopf bekommt eine neue Beschriftung. - -Die Zeile steht nach `new JButton(…)`, denn den Knopf muss es schon geben. Und wie alles andere kommt sie vor `setVisible(true)`. - -::: quiz -? Wofür steht das `e` in `e -> knopf.setText(…)`? -- [x] Für das Ereignis, also den Klick -- [ ] Für „Ende“ -- [ ] Für einen Fehler -> Hier brauchst du `e` nicht, aber der Name muss dastehen. -::: - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; - -public class KlickMich { - public static void main(String[] args) { - JFrame fenster = new JFrame("Klick mich"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new FlowLayout()); - - JButton knopf = new JButton("Klick mich"); - knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -``` - -Führe es aus und klicke auf den Knopf. Jetzt steht darauf „Ich wurde geklickt“. - -> Ist der neue Text länger als der alte, macht das FlowLayout den Knopf von selbst breiter. - -## Mehrere Anweisungen beim Klick - -Soll beim Klick mehr als eine Sache passieren, setzt du hinter den Pfeil geschweifte Klammern. Darin steht jede Anweisung mit Semikolon, wie in einem Schleifenrumpf: - -```java -knopf.addActionListener(e -> { - knopf.setText("Ich wurde geklickt"); - fenster.setTitle("Danke!"); - System.out.println("Klick!"); -}); -``` - -`System.out.println` schreibt wie gewohnt in die [Konsole](#zeige-konsole). Klicke mehrmals: Bei jedem Klick erscheint eine neue Zeile „Klick!“. So siehst du, dass der Code im Zuhörer wirklich bei jedem Klick läuft. - -::: ludwig -Achte auf das Ende: Erst schließt `}` den Block, dann `)` die Klammer von `addActionListener`, dann kommt das Semikolon. Also `});`. Fehlt etwas davon, steht unter dem Code ein Hinweis. -::: - -## Spiel: Wer hört zu? - -::: spiel zuhoerer -titel: Spiel: Wer hört zu? -intro: Ludwig zeigt dir Code und das Fenster dazu. Darunter steht, welche Knöpfe nacheinander geklickt werden. Was passiert? Nach deiner Antwort spielt Ludwig die Klicks ab: Du siehst, welcher Zuhörer das Ereignis hört und welche Zeilen dann laufen. -frage: Was steht nach den Klicks in der Beschriftung? - -runde: - JFrame fenster = new JFrame("Gruß"); - JLabel text = new JLabel("Start"); - JButton knopf = new JButton("Hallo"); - knopf.addActionListener(e -> text.setText("Hallo Welt")); -klicks: Hallo -- Start -- [x] Hallo Welt -- Hallo -> Der Knopf hat einen Zuhörer. Beim Klick führt er `text.setText("Hallo Welt")` aus, und die Beschriftung zeigt „Hallo Welt“. - -runde: - JFrame fenster = new JFrame("Obst"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apfel")); - b.addActionListener(e -> text.setText("Birne")); -klicks: A, B -- Apfel -- [x] Birne -- Apfel Birne -> Jeder Knopf hat seinen eigenen Zuhörer. Erst schreibt A „Apfel“ hinein, dann überschreibt B das mit „Birne“. `setText` ersetzt den alten Text, es hängt nichts an. - -runde: - JFrame fenster = new JFrame("Obst"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apfel")); -klicks: B -- Apfel -- Birne -- [x] ? -> Beim Knopf B hat sich niemand angemeldet: Es gibt kein `b.addActionListener`. Der Klick ist ein Ereignis, aber niemand hört zu. Deshalb bleibt das Fragezeichen stehen. - -runde: - JFrame fenster = new JFrame("Farben"); - JLabel text = new JLabel("keine"); - JButton rot = new JButton("Rot"); - JButton blau = new JButton("Blau"); - rot.addActionListener(e -> text.setText("Blau")); - blau.addActionListener(e -> text.setText("Rot")); -klicks: Rot -- Rot -- [x] Blau -- keine -> Aufgepasst: Was passiert, entscheidet der Code im Zuhörer, nicht die Beschriftung des Knopfs. Der Zuhörer am Knopf „Rot“ schreibt „Blau“ hinein. - -runde: - JFrame fenster = new JFrame("Knopf"); - JLabel text = new JLabel("Bitte klicken"); - JButton knopf = new JButton("Klick mich"); - knopf.addActionListener(e -> { - knopf.setText("Danke!"); - text.setText("Geklickt"); - }); -klicks: Klick mich -frage: Was steht nach dem Klick auf dem Knopf? -- Klick mich -- [x] Danke! -- Geklickt -> Im Block hinter dem Pfeil stehen zwei Anweisungen. Beide laufen beim Klick nacheinander: Der Knopf bekommt „Danke!“, die Beschriftung „Geklickt“. - -runde: - JFrame fenster = new JFrame("Konsole"); - JButton knopf = new JButton("Los"); - knopf.addActionListener(e -> System.out.println("Klick!")); -klicks: Los, Los, Los -frage: Wie viele Zeilen „Klick!“ stehen danach in der Konsole? -- 1 -- [x] 3 -- 0 -> Der Zuhörer läuft bei **jedem** Klick, nicht nur beim ersten. Drei Klicks, drei Zeilen. -::: - -## ActionListener ist ein Interface - -Das steht nicht im Lehrplan. Wenn du wissen willst, was hinter dem Pfeil steckt, lies weiter. - -::: angeberwissen -`ActionListener` ist ein **Interface**, eine Art Vertrag. Darin steht: Wer ein ActionListener sein will, muss eine Methode `actionPerformed(ActionEvent e)` haben. Der Knopf ruft bei jedem Klick genau diese Methode auf. -::: - -Die Zeile mit dem Pfeil kennst du schon aus Schritt 3: - -```java -knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); -``` - -Ohne Pfeil schreibt man den Zuhörer als Klasse ohne Namen, die den Vertrag erfüllt (eine anonyme Klasse): - -```java -knopf.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - knopf.setText("Ich wurde geklickt"); - } -}); -``` - -Dafür brauchst du oben noch `import java.awt.event.*;`. Beide Schreibweisen machen genau dasselbe. Der Pfeil `e -> …` ist die Kurzform (ein Lambda-Ausdruck, seit Java 8): `e` ist der Parameter von `actionPerformed`, und hinter dem Pfeil steht ihr Rumpf. Die Kurzform geht, weil das Interface nur eine einzige Methode hat. Java weiß also, welche Methode gemeint ist. - -## Spiel: Vertrag erfüllt? - -::: spiel vertrag -titel: Spiel: Vertrag erfüllt? -intro: Der Vertrag `ActionListener` verlangt genau eine Methode: `public void actionPerformed(ActionEvent e)`. Ludwig zeigt dir Code. Erfüllt er den Vertrag, sodass man ihn beim Knopf anmelden kann? Jede Antwort hat Ludwig mit dem echten Java-Compiler geprüft. -frage: Erfüllt das den Vertrag ActionListener? -antworten: erfüllt | nicht erfüllt - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Alles da: `implements ActionListener` unterschreibt den Vertrag, und die Methode heißt genau `actionPerformed`, ist `public`, gibt nichts zurück (`void`) und bekommt ein `ActionEvent`. - -runde: - class Gruss implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Schau genau hin: `actionPerfomed` statt `actionPerformed`, ein r fehlt. Für Java ist das eine ganz andere Methode. Der Compiler meldet, dass `actionPerformed` fehlt. - -runde: - class Gruss implements ActionListener { - public void actionPerformed() { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Der Parameter fehlt. Der Vertrag verlangt `actionPerformed(ActionEvent e)`, denn der Knopf übergibt beim Klick das Ereignis. - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent ereignis) { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Der Name des Parameters ist frei wählbar. Wichtig ist nur der Typ `ActionEvent`. Statt `e` darf er also auch `ereignis` heißen. - -runde: - class Gruss implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Das `public` fehlt. Alle Methoden eines Interfaces sind öffentlich, denn der Knopf muss sie von außen aufrufen können. Ohne `public` meldet der Compiler einen Fehler. - -runde: - class Gruss implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: nicht erfüllt -> Der Vertrag sagt `void`: Die Methode gibt nichts zurück. Mit `int` als Rückgabetyp passt sie nicht mehr. - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - sagHallo(); - } - void sagHallo() { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Zusätzliche Methoden sind erlaubt. Der Vertrag verlangt nur, dass `actionPerformed` da ist, nicht, dass sonst nichts da ist. - -runde: - class Gruss { - public void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } - knopf.addActionListener(new Gruss()); -richtig: nicht erfüllt -> Die Methode stimmt, aber der Vertrag ist nicht unterschrieben: Es fehlt `implements ActionListener`. Java nimmt beim Knopf nur Objekte an, die den Vertrag offiziell erfüllen. - -runde: - knopf.addActionListener(e -> System.out.println("Hallo")); -richtig: erfüllt -> Der Pfeil ist die Kurzform. Weil der Vertrag nur eine Methode hat, weiß Java: `e` ist der Parameter von `actionPerformed`, und hinter dem Pfeil steht ihr Rumpf. - -runde: - knopf.addActionListener((a, b) -> System.out.println("Hallo")); -richtig: nicht erfüllt -> Hier hat der Pfeil zwei Parameter. `actionPerformed` bekommt aber genau einen, das Ereignis. Darum passt diese Kurzform nicht zum Vertrag. -::: - -## Aufgabe: Ich wurde geklickt - -Jetzt machst du die Aufgabe fertig: Baue ein Fenster mit einem Knopf „Klick mich“ in normaler Größe. Nach dem Klick steht auf dem Knopf „Ich wurde geklickt“. - -Zusatz: Baue zwei Knöpfe „Rot“ und „Blau“. Ein Klick auf einen Knopf schreibt seinen Namen in den Titel des Fensters, zum Beispiel „Du hast Rot gewählt“. - -**Du bist fertig, wenn …** - -- vor dem Klick „Klick mich“ auf dem Knopf steht, -- nach dem Klick „Ich wurde geklickt“ darauf steht, -- der Knopf nicht das ganze Fenster füllt. - -::: ludwig -Jeder Knopf bekommt seinen eigenen Zuhörer mit eigenem `addActionListener`. Den Titel änderst du mit `fenster.setTitle(…)`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Ereignis ist. → Ein Klick ist ein Ereignis -- Ich kann mit addActionListener festlegen, was beim Klick passiert. → Die Zeile mit dem Pfeil -- Ich kann beim Klick mehrere Anweisungen ausführen. → Mehrere Anweisungen beim Klick -- Ich kann einen Knopf bauen, der nach dem Klick seinen Text ändert. → Aufgabe: Ich wurde geklickt -::: - -## Wissenstest - -::: quiz -? Was ist ein ActionListener? -→ Ein Klick ist ein Ereignis -- [ ] Ein Knopf mit Text -- [x] Ein Zuhörer, der bei jedem Klick deinen Code ausführt -- [ ] Ein Layout für Fenster -> Du meldest ihn mit addActionListener beim Knopf an. - -? Wann läuft knopf.setText("Ich wurde geklickt") im Zuhörer? -→ Die Zeile mit dem Pfeil -- [ ] Sofort beim Start des Programms -- [x] Jedes Mal, wenn jemand auf den Knopf klickt -- [ ] Nur beim ersten Klick -> Der Code im Zuhörer läuft erst, wenn das Ereignis eintritt. - -? Wie endet ein Zuhörer mit geschweiften Klammern? -→ Die Zeile mit dem Pfeil -- [ ] };) -- [x] }); -- [ ] );} -> Erst schließt der Block, dann die Klammer von addActionListener, dann das Semikolon. - -? Ordne jedem Teil seine Aufgabe zu. -→ Die Zeile mit dem Pfeil -= JButton -> ein Knopf -= addActionListener -> Zuhörer anmelden -= setText -> Beschriftung ändern -= setVisible(true) -> Fenster zeigen -::: diff --git a/web/lessons/de/31-zahl-verdoppeln.md b/web/lessons/de/31-zahl-verdoppeln.md deleted file mode 100644 index a9e9c576..00000000 --- a/web/lessons/de/31-zahl-verdoppeln.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Zahl verdoppeln im Fenster -bereich: Fenster mit Swing -kurz: Beschriftung, Textfeld und Knopf mit einem GridLayout anordnen und mit dem Text im Textfeld rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- JLabel und JTextField ins Fenster legen -- erklären, warum ohne Layout nur der letzte Baustein zu sehen ist -- mit GridLayout(0, 1) alles untereinander anordnen -- mit getText, Integer.parseInt und setText rechnen -- ein Programm schreiben, das bei „abc“ nicht abstürzt -::: - -## Das Ziel - -Wir bauen ein kleines Rechenprogramm mit Fenster: Oben steht eine Beschriftung, darunter ein Textfeld und ein Knopf **Berechne**. Gibst du 2 ein und klickst, steht im Textfeld 4. Klickst du noch einmal, wird daraus 8, dann 16 und so weiter. - -Dafür brauchst du zwei neue Bausteine: - -- **JLabel**: eine Beschriftung, also einfacher Text im Fenster. -- **JTextField**: ein Textfeld, in das man etwas tippen kann. - -Schalte wie in den letzten Lektionen im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Drei Bausteine ins Fenster - -Wir legen alle drei Bausteine ins Fenster, genau wie den Knopf in Lektion 29: - -```java -import javax.swing.*; - -public class Verdoppeln { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahl verdoppeln"); - fenster.setSize(250, 150); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel beschriftung = new JLabel("Gib eine Zahl ein:"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Berechne"); - fenster.add(beschriftung); - fenster.add(feld); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -``` - -Führe das Programm aus. Hoppla: Man sieht nur den riesigen Knopf. Wo sind die Beschriftung und das Textfeld? - -## Warum nur der Knopf zu sehen ist - -::: ludwig -Erinnerst du dich an den Riesenknopf? Ein `JFrame` hat ohne eigenes Layout nur **einen** Platz: die Mitte. Jedes `add` legt den neuen Baustein genau dorthin, so groß wie das ganze Fenster. Die drei Bausteine landen also übereinander auf demselben Platz, und das Fenster zeigt nur den zuletzt hineingelegten: den Knopf. Benutzen kannst du das Programm so nicht. -::: - -Die Lösung ist wieder ein Layout. Diesmal nehmen wir das **GridLayout**. Es teilt das Fenster in gleich große Zellen wie ein Gitter: - -```java -fenster.setLayout(new GridLayout(0, 1)); -``` - -Die beiden Zahlen sind Zeilen und Spalten. `0` Zeilen heißt „so viele wie nötig“, `1` Spalte heißt: alles untereinander. Für `GridLayout` brauchst du oben außerdem `import java.awt.*;`. - -| Layout | So ordnet es an | -|---|---| -| ohne Layout | alles in die Mitte, übereinander | -| `FlowLayout` | nebeneinander wie Wörter, Zeilenumbruch wenn nötig | -| `GridLayout(0, 1)` | untereinander, jede Zeile gleich hoch | - -## Das Fenster mit GridLayout - -```java -import javax.swing.*; -import java.awt.*; - -public class Verdoppeln { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahl verdoppeln"); - fenster.setSize(250, 150); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel beschriftung = new JLabel("Gib eine Zahl ein:"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Berechne"); - fenster.add(beschriftung); - fenster.add(feld); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -``` - -Jetzt stehen alle drei untereinander. Du kannst ins Textfeld tippen und den Knopf drücken. Rechnen tut das Programm aber noch nicht. - -::: angeberwissen -Mit `new GridLayout(2, 3)` bekommst du 2 Zeilen mit je 3 Spalten, also 6 gleich große Zellen. So baut man zum Beispiel die Tasten eines Taschenrechners. Die Bausteine füllen die Zellen in der Reihenfolge der `add`-Zeilen, Zeile für Zeile von links nach rechts. -::: - -## Mit dem Text im Textfeld rechnen - -Beim Klick soll das Programm die Zahl aus dem Textfeld holen, verdoppeln und wieder hineinschreiben. Das machen drei Methoden: - -- `feld.getText()` liefert den Text im Textfeld, zum Beispiel `"2"`. -- `Integer.parseInt(…)` macht daraus die Zahl 2. Das kennst du aus Lektion 22. -- `feld.setText(…)` schreibt einen neuen Text ins Textfeld. - -Zusammen mit dem ActionListener aus Lektion 30 sieht das so aus: - -```java -knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); -}); -``` - -`setText` braucht einen Text. `"" + zahl * 2` hängt das Ergebnis an einen leeren Text und macht so aus der Zahl einen Text. - -## Keine Zahl? Kein Absturz! - -Tippe „abc“ ins Textfeld und klicke auf Berechne. Das Fenster bleibt, aber es passiert nichts, und in der [Konsole](#zeige-konsole) steht eine `NumberFormatException`. Aus Lektion 22 weißt du, was zu tun ist: - -```java -knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } -}); -``` - -Statt eines Fehlers in der Konsole sieht man jetzt einen Hinweis im Fenster. - -::: ludwig -Der Pfeil hat schon ein `e` für das Ereignis. Deshalb braucht die Exception im `catch` einen anderen Namen, hier `fehler`. Zwei Variablen mit demselben Namen erlaubt Java nicht. -::: - -::: quiz -? Warum heißt die Exception im `catch` hier `fehler` und nicht `e`? -- [ ] `catch` verlangt immer den Namen `fehler`. -- [x] `e` ist schon das Ereignis des Pfeils, und zwei Variablen mit demselben Namen erlaubt Java nicht. -- [ ] `e` ist nur für Zahlen erlaubt. -> Den Namen darfst du frei wählen, nur nicht doppelt. -::: - -## Spiel: Was steht danach im Fenster? - -::: spiel frage -titel: Spiel: Was steht danach im Fenster? -intro: Ludwig tippt etwas ins Textfeld und klickt auf Berechne. Lies den Zuhörer und sag vorher, was danach im Fenster steht. Sechs Runden. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - }); -frage: Im Textfeld steht 21. Was steht nach dem Klick darin? -- 2121 -- [x] 42 -- 21 -> `parseInt` macht aus dem Text die Zahl 21. Das Doppelte ist 42. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - }); -frage: Im Textfeld steht 21. Du klickst zweimal. Was steht danach darin? -- 42 -- [x] 84 -- 4242 -> Jeder Klick verdoppelt, was gerade im Feld steht: 21, 42, 84. - -runde: - knopf.addActionListener(e -> { - feld.setText(feld.getText() + feld.getText()); - }); -frage: Im Textfeld steht 21. Was steht nach dem Klick darin? -- 42 -- [x] 2121 -- 21 -> Ohne `parseInt` bleibt es ein Text, und `+` hängt Texte aneinander. - -runde: - knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } - }); -frage: Im Textfeld steht abc. Was passiert beim Klick? -- [x] Die Beschriftung zeigt „Bitte eine ganze Zahl eingeben!“ -- Im Textfeld steht abcabc. -- Das Fenster stürzt ab. -> `parseInt` wirft eine NumberFormatException, und `catch` fängt sie ab. - -runde: - knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } - }); -frage: Im Textfeld steht 2.5. Was passiert beim Klick? -- Im Textfeld steht 5.0. -- Im Textfeld steht 5. -- [x] Die Beschriftung zeigt „Bitte eine ganze Zahl eingeben!“ -> `Integer.parseInt` kennt nur ganze Zahlen. 2.5 ist für ihn keine Zahl. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl + zahl); - }); -frage: Im Textfeld steht 5. Was steht nach dem Klick darin? -- 10 -- [x] 55 -- 5 -> `"" + zahl` ist schon ein Text, und das zweite `+` hängt die 5 noch einmal an. Mit `"" + (zahl + zahl)` wird zuerst gerechnet. -::: - -## Aufgabe: Zahl verdoppeln - -Baue das ganze Programm zusammen: - -- ein Fenster mit dem Titel „Zahl verdoppeln“, 250 Pixel breit und 150 Pixel hoch, -- untereinander eine Beschriftung, ein Textfeld und ein Knopf „Berechne“, -- jeder Klick verdoppelt die Zahl im Textfeld: 2, 4, 8, 16, … -- bei „abc“ erscheint in der Beschriftung ein Hinweis statt eines Absturzes. - -Zusatz: Ein zweiter Knopf „Halbieren“ teilt die Zahl durch 2. Was passiert bei 5? - -**Du bist fertig, wenn …** - -- der Titel „Zahl verdoppeln“ heißt und Beschriftung, Textfeld und Knopf untereinander stehen, -- aus 2 nach drei Klicks nacheinander 4, 8 und 16 werden, -- bei „abc“ ein Hinweis in der Beschriftung steht und das Programm weiterläuft. - -::: ludwig -Der Knopf „Halbieren“ kommt als vierte Zeile ins GridLayout und bekommt seinen eigenen ActionListener. Bei `int` schneidet Java beim Teilen die Nachkommastellen ab. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann JLabel und JTextField ins Fenster legen. → Drei Bausteine ins Fenster -- Ich kann erklären, warum ohne Layout nur der letzte Baustein zu sehen ist. → Warum nur der Knopf zu sehen ist -- Ich kann mit GridLayout(0, 1) alles untereinander anordnen. → Das Fenster mit GridLayout -- Ich kann mit getText, Integer.parseInt und setText rechnen. → Mit dem Text im Textfeld rechnen -- Ich kann ein Programm schreiben, das bei „abc“ nicht abstürzt. → Keine Zahl? Kein Absturz! -::: - -## Wissenstest - -::: quiz -? Warum ist ohne Layout nur der Knopf zu sehen? -→ Warum nur der Knopf zu sehen ist -- [ ] Weil Beschriftung und Textfeld fehlen -- [x] Weil alle Bausteine in der Mitte übereinander liegen und der letzte die anderen verdeckt -- [ ] Weil das Fenster zu klein ist - -? Was bedeutet new GridLayout(0, 1)? -→ Das Fenster mit GridLayout -- [ ] Keine Zeile und eine Spalte, also ist das Fenster leer -- [x] So viele Zeilen wie nötig in einer Spalte, also alles untereinander -- [ ] Eine Zeile, alles nebeneinander - -? Was liefert feld.getText()? -→ Mit dem Text im Textfeld rechnen -- [ ] Immer eine Zahl -- [x] Den Text im Textfeld -- [ ] Den Titel des Fensters -> Für eine Zahl brauchst du noch Integer.parseInt. - -? Ordne jedem Baustein seine Aufgabe zu. -→ Drei Bausteine ins Fenster -= JLabel -> Beschriftung -= JTextField -> Textfeld zum Tippen -= JButton -> Knopf -= GridLayout -> Anordnung im Gitter -::: diff --git a/web/lessons/de/32-klickzaehler.md b/web/lessons/de/32-klickzaehler.md deleted file mode 100644 index 750a8b04..00000000 --- a/web/lessons/de/32-klickzaehler.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Klickzähler mit Reset -bereich: Fenster mit Swing -kurz: Zwei Knöpfe ändern dieselbe Zahl: eine Variable für die ganze Klasse anlegen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, warum ein Zuhörer eine Variable aus main nicht ändern darf -- eine static-Variable in der Klasse anlegen -- zwei Knöpfe dieselbe Zahl ändern lassen -- einen Zähler mit −1 bauen, der nie unter 0 fällt -::: - -## Das Ziel - -Ein Fenster zählt Klicks: Oben steht die Zahl, darunter zwei Knöpfe. **+1** zählt eins hoch, **Reset** setzt die Zahl wieder auf 0. - -Neu ist diesmal: Zwei Knöpfe arbeiten mit **derselben** Zahl. Dafür brauchst du eine Variable, die beide Zuhörer kennen. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Der erste Versuch - -Naheliegend wäre, die Zahl wie gewohnt in `main` anzulegen und im Zuhörer hochzuzählen: - -```java -int anzahl = 0; -knopf.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); -}); -``` - -Führst du das mit echtem Java aus, meldet der Compiler einen Fehler: - -``` -local variables referenced from a lambda expression must be final or effectively final -``` - -::: ludwig -Der Zuhörer läuft erst später, bei jedem Klick, wenn `main` längst fertig ist. Deshalb darf er die Variablen aus `main` zwar lesen, aber nicht verändern. Java besteht darauf: Eine Variable aus `main`, die im Pfeil vorkommt, muss gleich bleiben. Für eine Zahl, die sich bei jedem Klick ändert, brauchen wir einen anderen Platz. -::: - -## Eine Variable für die ganze Klasse - -Die Lösung: Die Variable kommt **nicht** in `main`, sondern direkt in die Klasse, über `main`. Dann gehört sie der ganzen Klasse, und jeder Zuhörer darf sie ändern: - -```java -public class Zaehler { - static int anzahl = 0; - - public static void main(String[] args) { - // hier wie gewohnt das Fenster - } -} -``` - -- `static` heißt: Die Variable gehört zur Klasse selbst. Weil `main` auch `static` ist, braucht es das hier. -- Eine solche Variable nennt man **Attribut** der Klasse. Sie lebt so lange wie das Programm, nicht nur so lange wie `main`. - -::: quiz -? Wie nennt man eine Variable, die direkt in der Klasse steht und nicht in `main`? -- [ ] Parameter -- [x] Attribut -- [ ] Zuhörer -> Ein Attribut lebt so lange wie das Programm, nicht nur so lange wie `main`. -::: - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; - -public class Zaehler { - static int anzahl = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - reset.addActionListener(e -> { - anzahl = 0; - zahl.setText("0"); - }); - - fenster.add(zahl); - fenster.add(plus); - fenster.add(reset); - fenster.setVisible(true); - } -} -``` - -`SwingConstants.CENTER` setzt den Text der Beschriftung in die Mitte. Klicke ein paarmal auf +1 und dann auf Reset. - -::: angeberwissen -Mit `zahl.setFont(new Font("SansSerif", Font.BOLD, 48));` wird die Zahl groß und fett. `Font` kommt aus `java.awt`, das du schon importiert hast. -::: - -## Spiel: Wer zählt mit? - -::: spiel zuhoerer -titel: Spiel: Wer zählt mit? -intro: Jetzt rechnen die Zuhörer mit der Variable aus der Klasse. Ludwig zeigt dir Code, Fenster und die Klicks. Welche Zahl steht am Ende da? Danach spielt Ludwig die Klicks ab. -frage: Was steht nach den Klicks in der Beschriftung? - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> Die Variable steht in der Klasse und behält ihren Wert zwischen den Klicks. Jeder Klick zählt eins hoch: 1, 2, 3. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - reset.addActionListener(e -> { - anzahl = 0; - zahl.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> Nach zwei Klicks steht 2 da. Reset setzt die Variable auf 0, und der letzte Klick zählt wieder auf 1. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> anzahl++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> Die Variable ist danach 2, aber niemand schreibt sie in die Beschriftung. Ohne `zahl.setText(…)` zeigt das Fenster weiter 0. Rechnen und Anzeigen sind zwei verschiedene Dinge. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("Klicks: 0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("Klicks: " + anzahl + 1); - }); -klicks: +1 -- Klicks: 2 -- Klicks: 1 -- [x] Klicks: 11 -> Die Klammerfalle: Java rechnet von links nach rechts. `"Klicks: " + anzahl` ergibt den Text „Klicks: 1“, und `+ 1` hängt eine weitere 1 an. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton eins = new JButton("+1"); - JButton zehn = new JButton("+10"); - eins.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - zehn.addActionListener(e -> { - anzahl += 10; - zahl.setText("" + anzahl); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Beide Zuhörer ändern dieselbe Variable: 10, dann 11, dann 21. `anzahl += 10` ist die Kurzform von `anzahl = anzahl + 10`. -::: - -## Aufgabe: Klickzähler - -Baue den Klickzähler nach und erweitere ihn: - -- ein Knopf **−1**, der eins herunterzählt, -- die Zahl soll nie unter 0 fallen. Steht sie auf 0, passiert bei −1 nichts. - -**Du bist fertig, wenn …** - -- **+1** eins hochzählt und **−1** eins herunterzählt, -- bei 0 ein Klick auf **−1** nichts ändert, -- **Reset** die Zahl wieder auf 0 setzt. - -::: ludwig -Für „nie unter 0“ brauchst du im Zuhörer von −1 ein `if`: Nur wenn `anzahl > 0` ist, wird heruntergezählt. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, warum ein Zuhörer eine Variable aus main nicht ändern darf. → Der erste Versuch -- Ich kann eine static-Variable in der Klasse anlegen. → Eine Variable für die ganze Klasse -- Ich kann zwei Knöpfe dieselbe Zahl ändern lassen. → Das ganze Programm -- Ich kann einen Zähler mit −1 bauen, der nie unter 0 fällt. → Aufgabe: Klickzähler -::: - -## Wissenstest - -::: quiz -? Warum darf der Zuhörer die Variable anzahl aus main nicht verändern? -→ Der erste Versuch -- [ ] Weil Zahlen in Swing nicht erlaubt sind -- [x] Weil der Zuhörer erst später läuft und Java Variablen aus main im Pfeil nicht verändern lässt -- [ ] Weil anzahl zu groß wird - -? Wo steht static int anzahl = 0;? -→ Eine Variable für die ganze Klasse -- [ ] In main, ganz oben -- [x] In der Klasse, außerhalb von main -- [ ] Im Zuhörer - -? Was macht SwingConstants.CENTER bei new JLabel("0", SwingConstants.CENTER)? -→ Das ganze Programm -- [x] Es setzt den Text in die Mitte der Beschriftung. -- [ ] Es setzt das Fenster in die Mitte des Bildschirms. -- [ ] Es macht die Schrift größer. -::: diff --git a/web/lessons/de/33-ampel.md b/web/lessons/de/33-ampel.md deleted file mode 100644 index 479b89a6..00000000 --- a/web/lessons/de/33-ampel.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -titel: Eine Ampel mit Farben -bereich: Fenster mit Swing -kurz: Hintergrundfarben setzen und mit einer Zustandsvariable eine Ampel weiterschalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit setOpaque und setBackground eine Beschriftung färben -- mir mit einer static-Variable einen Zustand merken -- mit BorderLayout etwas groß in die Mitte und etwas schmal nach unten setzen -- erklären, wofür FlowLayout, GridLayout und BorderLayout gut sind -- eine Ampel mit vier Phasen programmieren -::: - -## Das Ziel - -Wir bauen eine Ampel: Eine große Fläche leuchtet rot. Bei jedem Klick auf **Weiter** schaltet sie um: Rot, Grün, Gelb und wieder Rot. - -Neu sind diesmal **Farben** und ein **Zustand**: Das Programm muss sich merken, welche Farbe gerade dran ist. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Farben in Swing - -Farben stehen in der Klasse `Color` aus `java.awt`. Fertige Farben sind zum Beispiel `Color.RED`, `Color.GREEN`, `Color.YELLOW`, `Color.ORANGE` und `Color.WHITE`. - -Eine Beschriftung färbst du so: - -```java -JLabel licht = new JLabel("", SwingConstants.CENTER); -licht.setOpaque(true); -licht.setBackground(Color.RED); -``` - -- `setBackground(…)` legt die Hintergrundfarbe fest. -- `setOpaque(true)` braucht ein `JLabel` dazu. Ohne diese Zeile ist der Hintergrund durchsichtig, und die Farbe bleibt unsichtbar. - -::: ludwig -`opaque` ist englisch für „undurchsichtig“. Eine Beschriftung ist normalerweise durchsichtig wie Schrift auf einer Glasscheibe. Erst mit `setOpaque(true)` bekommt sie eine eigene Fläche, die man anmalen kann. -::: - -::: quiz -? Welche Zeile malt den Hintergrund der Beschriftung `licht` grün an? -- [ ] `licht.setText(Color.GREEN);` -- [x] `licht.setBackground(Color.GREEN);` -- [ ] `licht.setColor("grün");` -> Die Hintergrundfarbe setzt `setBackground`. Damit man sie sieht, braucht ein `JLabel` vorher `setOpaque(true)`. -::: - -## Sich die Farbe merken - -Damit die Ampel weiß, was als Nächstes kommt, merkt sie sich ihren Zustand in einer Zahl: 0 heißt Rot, 1 heißt Grün, 2 heißt Gelb. Wie beim Klickzähler aus Lektion 32 steht die Variable in der Klasse, damit der Zuhörer sie ändern darf: - -```java -static int phase = 0; -``` - -Bei jedem Klick geht die Phase eins weiter. Nach Gelb (2) kommt wieder Rot (0): - -```java -weiter.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - licht.setBackground(Color.RED); - } else if (phase == 1) { - licht.setBackground(Color.GREEN); - } else { - licht.setBackground(Color.YELLOW); - } -}); -``` - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; - -public class Ampel { - static int phase = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Ampel"); - fenster.setSize(200, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new BorderLayout()); - - JLabel licht = new JLabel("", SwingConstants.CENTER); - licht.setOpaque(true); - licht.setBackground(Color.RED); - JButton weiter = new JButton("Weiter"); - - weiter.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - licht.setBackground(Color.RED); - } else if (phase == 1) { - licht.setBackground(Color.GREEN); - } else { - licht.setBackground(Color.YELLOW); - } - }); - - fenster.add(licht, BorderLayout.CENTER); - fenster.add(weiter, BorderLayout.SOUTH); - fenster.setVisible(true); - } -} -``` - -Hier kommt ein neues Layout zum Einsatz, das **BorderLayout**. Es hat fünf Plätze: `NORTH` (oben), `SOUTH` (unten), `WEST` (links), `EAST` (rechts) und `CENTER` (Mitte). Die Mitte bekommt allen Platz, der übrig bleibt. So wird das Licht groß, und der Knopf bleibt unten schmal. - -::: angeberwissen -Das BorderLayout ist das Layout, das ein `JFrame` ohne eigenes Layout benutzt. Ein `add` ohne Platzangabe landet in `CENTER`. Deshalb lagen in Lektion 31 alle Bausteine übereinander. -::: - -::: angeberwissen -Profis schreiben das Weiterschalten in einer Zeile: `phase = (phase + 1) % 3;`. Der Rest-Operator `%` liefert den Rest beim Teilen. 3 % 3 ist 0, also beginnt es nach 2 wieder bei 0. -::: - -## Welches Layout wofür? - -Du kennst jetzt drei Layouts. Jedes ordnet die Bausteine anders an: - -| Layout | So ordnet es an | Gut für | -|---|---|---| -| `FlowLayout` (Lektion 29) | nebeneinander wie Wörter in einer Zeile, jeder Baustein so groß wie nötig | ein paar Knöpfe in einer Reihe | -| `GridLayout` (Lektion 31) | ein Gitter aus gleich großen Zellen, Zeile für Zeile gefüllt | Formulare, Tastenfelder wie beim Taschenrechner | -| `BorderLayout` (diese Lektion) | fünf Plätze: oben, unten, links, rechts und die Mitte, die den Rest bekommt | etwas Großes in der Mitte, z. B. ein Bild oder eine Anzeige, mit Knöpfen am Rand | - -Ohne `setLayout` benutzt ein Fenster das BorderLayout. Darum landet dann alles in der Mitte. - -::: ludwig -Frag dich beim Planen: Sollen alle Teile gleich groß sein? Dann GridLayout. Soll ein Teil groß sein und der Rest schmal am Rand? Dann BorderLayout. Sollen die Teile einfach ihre normale Größe behalten? Dann FlowLayout. -::: - -## Spiel: Wo landet der Knopf? - -::: spiel layout -titel: Spiel: Wo landet der Knopf? -intro: Du kennst jetzt drei Layouts und das Fenster ganz ohne Layout. Ludwig zeigt dir Code mit den Knöpfen A, B, C … Wie sieht das Fenster aus? Tippe auf das richtige Bild. -frage: Wie sieht das Fenster aus? - -runde: - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Ohne eigenes Layout landet jedes `add` in der Mitte, so groß wie das Fenster. Die drei liegen übereinander, man sieht nur den letzten: C. - -runde: - fenster.setLayout(new FlowLayout()); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> FlowLayout setzt die Knöpfe in normaler Größe nebeneinander, wie Wörter in einer Zeile, und zentriert sie. - -runde: - fenster.setLayout(new GridLayout(0, 1)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> Eine Spalte, so viele Zeilen wie nötig: alles untereinander, jede Zeile gleich hoch und so breit wie das Fenster. - -runde: - fenster.setLayout(new GridLayout(0, 2)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); - fenster.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Zwei Spalten. Die Zellen werden Zeile für Zeile gefüllt, immer erst links, dann rechts: A B, dann C D. - -runde: - fenster.setLayout(new GridLayout(2, 3)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); - fenster.add(new JButton("D")); - fenster.add(new JButton("E")); - fenster.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Erst kommen die Zeilen, dann die Spalten: 2 Zeilen mit je 3 Spalten. - -runde: - fenster.setLayout(new GridLayout(0, 2)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Zwei Spalten, drei Knöpfe: Die erste Zeile ist voll, in der zweiten steht C links, und rechts bleibt die Zelle leer. - -runde: - fenster.setLayout(new BorderLayout()); - fenster.add(new JButton("A"), BorderLayout.NORTH); - fenster.add(new JButton("B"), BorderLayout.CENTER); - fenster.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH ist oben, SOUTH unten. Die Mitte bekommt den ganzen übrigen Platz, darum ist B groß. - -runde: - fenster.setLayout(new BorderLayout()); - fenster.add(new JButton("A"), BorderLayout.WEST); - fenster.add(new JButton("B")); - fenster.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST ist links, EAST rechts. B hat keine Platzangabe und landet deshalb in CENTER. -::: - -## Aufgabe: Ampel - -Baue die Ampel nach und mach sie echter. In Deutschland leuchten nach Rot kurz Rot und Gelb zusammen. Nimm deshalb eine vierte Phase dazu: - -- 0: Rot -- 1: Rot-Gelb (nimm dafür `Color.ORANGE`) -- 2: Grün -- 3: Gelb - -Zusatz: Schreib in jeder Phase zusätzlich mit `licht.setText(…)`, was die Ampel bedeutet, zum Beispiel „Stehen“ oder „Gehen“. - -**Du bist fertig, wenn …** - -- jeder Klick die nächste Phase zeigt: Rot, Rot-Gelb (orange), Grün, Gelb, -- nach Gelb wieder Rot kommt, -- im Zusatz zu jeder Phase der passende Text steht. - -::: ludwig -Mit vier Phasen kommt nach 3 wieder 0. Pass also die Zahl in `if (phase > 2)` an. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit setOpaque und setBackground eine Beschriftung färben. → Farben in Swing -- Ich kann mir mit einer static-Variable einen Zustand merken. → Sich die Farbe merken -- Ich kann mit BorderLayout etwas groß in die Mitte und etwas schmal nach unten setzen. → Das ganze Programm -- Ich kann erklären, wofür FlowLayout, GridLayout und BorderLayout gut sind. → Welches Layout wofür? -- Ich kann eine Ampel mit vier Phasen programmieren. → Aufgabe: Ampel -::: - -## Wissenstest - -::: quiz -? Warum bleibt die Farbe ohne setOpaque(true) unsichtbar? -→ Farben in Swing -- [ ] Weil Color.RED nicht existiert -- [x] Weil eine Beschriftung sonst durchsichtig ist -- [ ] Weil das Fenster zu klein ist - -? Welcher Platz im BorderLayout bekommt den übrigen Platz? -→ Das ganze Programm -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? phase ist 2 und der Zuhörer führt phase++ und dann if (phase > 2) { phase = 0; } aus. Welchen Wert hat phase danach? -→ Sich die Farbe merken -- [x] 0 -- [ ] 2 -- [ ] 3 -> Nach phase++ ist sie 3, das ist größer als 2, also wird sie 0. -::: diff --git a/web/lessons/de/34-notenrechner.md b/web/lessons/de/34-notenrechner.md deleted file mode 100644 index f5e7eae5..00000000 --- a/web/lessons/de/34-notenrechner.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -titel: Notenrechner im Fenster -bereich: Fenster mit Swing -kurz: Punkte eingeben, mit else if die Note bestimmen und falsche Eingaben abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit else if Punkte in eine Note umrechnen -- die Note im Fenster anzeigen -- „abc“ und Punkte außerhalb von 0 bis 100 abfangen und melden -- ein Programm an den Grenzen testen, zum Beispiel mit 92 und 91 -::: - -## Das Ziel - -Ein Fenster rechnet Punkte in eine Note um: Du tippst die Punkte einer Klassenarbeit ein (0 bis 100), klickst auf **Note berechnen**, und unten steht die Note. - -Diesmal kommt kein neuer Swing-Baustein dazu. Neu ist, dass du alles Bisherige kombinierst: Textfeld, Knopf, `else if` aus Lektion 20 und try/catch aus Lektion 22. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Der Notenschlüssel - -Wir nehmen den Notenschlüssel der IHK-Prüfungen: - -| Punkte | Note | -|---|---| -| 92 bis 100 | 1 | -| 81 bis 91 | 2 | -| 67 bis 80 | 3 | -| 50 bis 66 | 4 | -| 30 bis 49 | 5 | -| 0 bis 29 | 6 | - -Mit `else if` prüfst du von oben nach unten. Die erste Bedingung, die zutrifft, gewinnt: - -```java -int note; -if (punkte >= 92) { - note = 1; -} else if (punkte >= 81) { - note = 2; -} else if (punkte >= 67) { - note = 3; -} else if (punkte >= 50) { - note = 4; -} else if (punkte >= 30) { - note = 5; -} else { - note = 6; -} -``` - -::: ludwig -Bei 85 Punkten ist `punkte >= 92` falsch, aber `punkte >= 81` wahr. Also Note 2, und die restlichen Bedingungen werden gar nicht mehr geprüft. Deshalb reicht jeweils die untere Grenze. -::: - -Du kannst diesen Teil ohne Fenster im Bereich [Code](#zeige-codelive) ausprobieren. Schreib `int punkte = 85;` davor und `System.out.println("Note: " + note);` dahinter. Das Struktogramm zeigt dir die verschachtelten Verzweigungen. - -::: quiz -? Welche Note ergibt der Schlüssel bei 29 Punkten? -- [ ] 4 -- [ ] 5 -- [x] 6 -> Alle Bedingungen sind falsch, auch `punkte >= 30`. Dann gilt der `else`-Zweig. -::: - -## Das Fenster - -```java -import javax.swing.*; -import java.awt.*; - -public class Notenrechner { - public static void main(String[] args) { - JFrame fenster = new JFrame("Notenrechner"); - fenster.setSize(260, 180); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel frage = new JLabel("Punkte (0 bis 100):"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Note berechnen"); - JLabel ergebnis = new JLabel("Note: ?"); - - knopf.addActionListener(e -> { - int punkte = Integer.parseInt(feld.getText()); - int note; - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else if (punkte >= 50) { - note = 4; - } else if (punkte >= 30) { - note = 5; - } else { - note = 6; - } - ergebnis.setText("Note: " + note); - }); - - fenster.add(frage); - fenster.add(feld); - fenster.add(knopf); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -``` - -Probiere 100, 85, 50 und 12 aus. - -## Falsche Eingaben abfangen - -Zwei Dinge können noch schiefgehen: - -1. Jemand tippt „abc“. Dann wirft `Integer.parseInt` eine `NumberFormatException`. -2. Jemand tippt 150 oder −5. Das ist zwar eine Zahl, aber keine gültige Punktzahl. - -Den ersten Fall fängst du mit try/catch ab, den zweiten mit einem `if` vor der Notenberechnung: - -```java -knopf.addActionListener(e -> { - try { - int punkte = Integer.parseInt(feld.getText()); - if (punkte < 0 || punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - // hier die Notenberechnung von oben - } - } catch (NumberFormatException fehler) { - ergebnis.setText("Das ist keine Zahl."); - } -}); -``` - -::: ludwig -`||` heißt „oder“ (Lektion 9). Die Bedingung ist wahr, wenn die Punkte zu klein **oder** zu groß sind. -::: - -## Spiel: Welche Note? - -::: spiel frage -titel: Spiel: Welche Note? -intro: Ludwig hat Notenrechner gebaut, aber nicht alle sind richtig. Rechne nach, welche Note herauskommt. Sechs Runden. - -runde: - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else { - note = 4; - } -frage: punkte ist 81. Welche Note kommt heraus? -- 1 -- [x] 2 -- 3 -> 81 >= 92 ist falsch, 81 >= 81 ist wahr. Die Grenze gehört noch zur 2. - -runde: - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else if (punkte >= 50) { - note = 4; - } else { - note = 5; - } -frage: punkte ist 66. Welche Note kommt heraus? -- 3 -- [x] 4 -- 5 -> 66 >= 67 ist knapp falsch, 66 >= 50 ist wahr. Also Note 4. - -runde: - if (punkte >= 50) { - note = 4; - } else if (punkte >= 92) { - note = 1; - } else { - note = 6; - } -frage: punkte ist 95. Welche Note kommt heraus? -- 1 -- [x] 4 -- 6 -> Die erste wahre Bedingung gewinnt. 95 >= 50 ist schon wahr, die 1 wird nie erreicht. Deshalb prüft man von den hohen Punkten nach unten. - -runde: - note = 6; - if (punkte >= 30) { - note = 5; - } - if (punkte >= 50) { - note = 4; - } -frage: punkte ist 70. Welche Note steht danach in note? -- 6 -- 5 -- [x] 4 -> Ohne `else` wird jedes `if` geprüft. Beide sind wahr, das letzte setzt die Note 4. - -runde: - // note ist schon berechnet - if (punkte < 0 || punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - ergebnis.setText("Note: " + note); - } -frage: Im Textfeld steht 100. Was zeigt das Fenster? -- [x] Note: 1 -- Bitte 0 bis 100 eingeben. -- Das ist keine Zahl. -> 100 > 100 ist falsch. 100 Punkte sind also gültig und ergeben die Note 1. - -runde: - // note ist schon berechnet - if (punkte < 0 && punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - ergebnis.setText("Note: " + note); - } -frage: Im Textfeld steht 150. Was zeigt das Fenster? -- Bitte 0 bis 100 eingeben. -- [x] eine Note, obwohl 150 ungültig ist -- Das ist keine Zahl. -> Mit `&&` müssten die Punkte gleichzeitig unter 0 und über 100 sein. Das geht nie, also warnt das Programm nie. Richtig ist `||`. -::: - -## Aufgabe: Notenrechner - -Baue den Notenrechner zusammen, mit beiden Prüfungen aus dem letzten Schritt. Teste ihn mit 92, 91, 0, 100, 101 und „zehn“. - -Zusatz: Schreib die Note zusätzlich als Wort hin, zum Beispiel „Note: 2 (gut)“. Die Wörter sind sehr gut, gut, befriedigend, ausreichend, mangelhaft und ungenügend. - -**Du bist fertig, wenn …** - -- 92 und 100 eine 1 ergeben, 91 eine 2 und 0 eine 6, -- bei 101 der Hinweis „Bitte 0 bis 100 eingeben.“ erscheint, -- bei „zehn“ ein Hinweis erscheint und das Programm weiterläuft. - -::: ludwig -Teste immer die Grenzen: 92 muss eine 1 sein, 91 eine 2. An den Grenzen stecken die meisten Fehler, zum Beispiel `>` statt `>=`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit else if Punkte in eine Note umrechnen. → Der Notenschlüssel -- Ich kann die Note im Fenster anzeigen. → Das Fenster -- Ich kann „abc“ und Punkte außerhalb von 0 bis 100 abfangen und melden. → Falsche Eingaben abfangen -- Ich kann ein Programm an den Grenzen testen, zum Beispiel mit 92 und 91. → Aufgabe: Notenrechner -::: - -## Wissenstest - -::: quiz -? Welche Note ergibt der Schlüssel bei 67 Punkten? -→ Der Notenschlüssel -- [ ] 2 -- [x] 3 -- [ ] 4 -> 67 ist die untere Grenze für die 3. - -? Warum reicht in jeder Bedingung die untere Grenze? -→ Der Notenschlüssel -- [x] Weil else if die oberen Fälle schon vorher aussortiert hat -- [ ] Weil Java obere Grenzen nicht kennt -- [ ] Weil die Punkte immer 100 sind - -? Welche Bedingung erkennt ungültige Punkte? -→ Falsche Eingaben abfangen -- [ ] punkte < 0 && punkte > 100 -- [x] punkte < 0 || punkte > 100 -- [ ] punkte >= 0 || punkte <= 100 -> Eine Zahl kann nicht gleichzeitig kleiner als 0 und größer als 100 sein, deshalb braucht es oder. -::: diff --git a/web/lessons/de/35-taschenrechner.md b/web/lessons/de/35-taschenrechner.md deleted file mode 100644 index d281d9e7..00000000 --- a/web/lessons/de/35-taschenrechner.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -titel: Ein Taschenrechner -bereich: Fenster mit Swing -kurz: Zwei Spalten mit GridLayout, vier Rechenknöpfe und die Division durch 0 abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit GridLayout(0, 2) zwei Spalten bauen -- erklären, warum "" + (a + b) Klammern braucht -- alle vier Rechenknöpfe programmieren -- einen Rechner bauen, der bei 0 und bei „abc“ nicht abstürzt -::: - -## Das Ziel - -Wir bauen einen kleinen Taschenrechner: zwei Textfelder für die Zahlen, vier Knöpfe **+**, **-**, **\*** und **/** und eine Zeile für das Ergebnis. - -Neu sind diesmal **zwei Spalten** im GridLayout und **mehrere Knöpfe**, die alle mit denselben Textfeldern rechnen. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Zwei Spalten - -Bisher stand mit `GridLayout(0, 1)` alles untereinander. Mit `GridLayout(0, 2)` gibt es zwei Spalten. Die Bausteine füllen die Zellen in der Reihenfolge der `add`-Zeilen, immer erst links, dann rechts: - -| links | rechts | -|---|---| -| Erste Zahl: | Textfeld | -| Zweite Zahl: | Textfeld | -| + | - | -| \* | / | -| Ergebnis: | ? | - -Die Beschriftungen links brauchst du später nicht mehr. Du kannst sie deshalb direkt in `add` bauen, ohne Variable: - -```java -fenster.add(new JLabel("Erste Zahl:")); -fenster.add(feld1); -``` - -## Das Fenster mit dem ersten Knopf - -```java -import javax.swing.*; -import java.awt.*; - -public class Rechner { - public static void main(String[] args) { - JFrame fenster = new JFrame("Rechner"); - fenster.setSize(280, 220); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 2)); - - JTextField feld1 = new JTextField(); - JTextField feld2 = new JTextField(); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - JButton mal = new JButton("*"); - JButton geteilt = new JButton("/"); - JLabel ergebnis = new JLabel("?"); - - plus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a + b)); - }); - - fenster.add(new JLabel("Erste Zahl:")); - fenster.add(feld1); - fenster.add(new JLabel("Zweite Zahl:")); - fenster.add(feld2); - fenster.add(plus); - fenster.add(minus); - fenster.add(mal); - fenster.add(geteilt); - fenster.add(new JLabel("Ergebnis:")); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -``` - -Tippe 3 und 4 ein und klicke auf +. Unten steht 7. - -::: ludwig -Achte auf die Klammern in `"" + (a + b)`. Ohne sie rechnet Java von links nach rechts: `"" + a` ergibt den Text „3“, und `"3" + b` hängt die 4 hinten an. Heraus kommt „34“ statt 7. Die Klammer sorgt dafür, dass zuerst gerechnet wird. -::: - -## Die anderen drei Knöpfe - -Die Knöpfe -, \* und / funktionieren genauso. Nur das Rechenzeichen in der letzten Zeile ändert sich: - -```java -minus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a - b)); -}); -``` - -Bei / gibt es zwei Besonderheiten: - -- Mit `int` schneidet Java beim Teilen die Nachkommastellen ab. 7 / 2 ergibt 3. -- Bei 0 als zweiter Zahl wirft Java eine `ArithmeticException`, wie bei der Aufgabe „Sicher teilen“ in Lektion 22. - -## Division durch 0 abfangen - -```java -geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } -}); -``` - -::: angeberwissen -Mit `double` und `Double.parseDouble(…)` gibt es Nachkommastellen: 7 / 2 ergibt 3.5. Dafür wirft Java bei Kommazahlen keine Exception beim Teilen durch 0. Das Ergebnis heißt dann `Infinity`, also „unendlich“. -::: - -::: quiz -? Was zeigt der Rechner bei 8 geteilt durch 0? -- [ ] 0 -- [ ] Infinity -- [x] Nicht durch 0! -> Mit `int` wirft `a / b` eine `ArithmeticException`. Das `catch` fängt sie ab und zeigt den Hinweis. -::: - -## Eine eigene Methode - -Das steht nicht im Lehrplan, macht den Code aber kürzer. - -::: angeberwissen -Jeder Zuhörer liest dieselben zwei Felder. Diese Wiederholung kannst du in eine eigene Methode auslagern, ein Unterprogramm wie in Lektion 18. Sie steht in der Klasse neben `main`: - -`static int zahl(JTextField feld) { return Integer.parseInt(feld.getText()); }` - -Im Zuhörer heißt es dann nur noch `ergebnis.setText("" + (zahl(feld1) + zahl(feld2)));`. -::: - -## Spiel: Was zeigt der Rechner? - -::: spiel frage -titel: Spiel: Was zeigt der Rechner? -intro: Ludwig hat am Taschenrechner geschraubt. Lies den Zuhörer und sag vorher, was im Ergebnis steht. Sechs Runden. - -runde: - plus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + a + b); - }); -frage: Du tippst 3 und 4 und klickst auf +. Was steht im Ergebnis? -- 7 -- [x] 34 -- 12 -> Ohne Klammern rechnet Java von links nach rechts: `"" + 3` ist der Text „3“, und `+ 4` hängt die 4 an. - -runde: - mal.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + a * b); - }); -frage: Du tippst 3 und 4 und klickst auf *. Was steht im Ergebnis? -- 34 -- [x] 12 -- 7 -> Punkt vor Strich gilt auch hier: Erst rechnet Java 3 * 4, dann hängt es die 12 an den Text. Beim Plus fehlt dieser Vorrang, darum braucht es dort die Klammern. - -runde: - geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a / b)); - }); -frage: Du tippst 7 und 2 und klickst auf /. Was steht im Ergebnis? -- 3.5 -- [x] 3 -- 4 -> Mit `int` schneidet Java die Nachkommastellen ab. Es wird nicht gerundet. - -runde: - geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } - }); -frage: Du tippst 8 und 0 und klickst auf /. Was steht im Ergebnis? -- 0 -- [x] Nicht durch 0! -- Infinity -> Bei `int` wirft das Teilen durch 0 eine ArithmeticException, und `catch` schreibt den Hinweis. - -runde: - geteilt.addActionListener(e -> { - double a = Double.parseDouble(feld1.getText()); - double b = Double.parseDouble(feld2.getText()); - ergebnis.setText("" + (a / b)); - }); -frage: Du tippst 7 und 2 und klickst auf /. Was steht im Ergebnis? -- 3 -- [x] 3.5 -- 3.0 -> Mit `double` bleiben die Nachkommastellen erhalten. - -runde: - geteilt.addActionListener(e -> { - double a = Double.parseDouble(feld1.getText()); - double b = Double.parseDouble(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } - }); -frage: Du tippst 8 und 0 und klickst auf /. Was steht im Ergebnis? -- Nicht durch 0! -- [x] Infinity -- 0.0 -> Bei `double` gibt es keine Exception. Das Ergebnis ist `Infinity`, also „unendlich“, und das `catch` kommt nie dran. -::: - -## Aufgabe: Taschenrechner - -Baue den Rechner fertig: - -- alle vier Knöpfe rechnen, -- / meldet „Nicht durch 0!“ statt abzustürzen, -- steht in einem Feld „abc“ oder nichts, erscheint „Bitte zwei Zahlen eingeben“. - -Zusatz: Ein fünfter Knopf **C** leert beide Felder und setzt das Ergebnis wieder auf „?“. - -**Du bist fertig, wenn …** - -- 7 und 2 bei +, −, * und / die Ergebnisse 9, 5, 14 und 3 zeigen, -- 7 / 0 „Nicht durch 0!“ meldet, -- bei „abc“ oder einem leeren Feld „Bitte zwei Zahlen eingeben“ erscheint und nichts abstürzt. - -::: ludwig -Für „abc“ brauchst du in jedem Zuhörer ein try/catch mit `NumberFormatException`. Hinter einem `try` dürfen auch zwei `catch` stehen, einer für `NumberFormatException` und einer für `ArithmeticException`. Ein leeres Feld liefert übrigens den Text `""`, und auch der ist keine Zahl. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit GridLayout(0, 2) zwei Spalten bauen. → Zwei Spalten -- Ich kann erklären, warum "" + (a + b) Klammern braucht. → Das Fenster mit dem ersten Knopf -- Ich kann alle vier Rechenknöpfe programmieren. → Die anderen drei Knöpfe -- Ich kann einen Rechner bauen, der bei 0 und bei „abc“ nicht abstürzt. → Aufgabe: Taschenrechner -::: - -## Wissenstest - -::: quiz -? a ist 3 und b ist 4. Was ergibt "" + a + b? -→ Das Fenster mit dem ersten Knopf -- [ ] 7 -- [x] 34 -- [ ] Einen Fehler -> Java rechnet von links nach rechts: Erst entsteht der Text "3", dann wird die 4 angehängt. - -? Was ergibt 7 / 2 mit int? -→ Die anderen drei Knöpfe -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? In welcher Reihenfolge füllt GridLayout(0, 2) die Zellen? -→ Zwei Spalten -- [x] Zeile für Zeile, jeweils erst links, dann rechts -- [ ] Spalte für Spalte, erst ganz links von oben nach unten -- [ ] Zufällig -::: diff --git a/web/lessons/de/36-zahlenraten.md b/web/lessons/de/36-zahlenraten.md deleted file mode 100644 index f315f56e..00000000 --- a/web/lessons/de/36-zahlenraten.md +++ /dev/null @@ -1,242 +0,0 @@ ---- -titel: Zahlenraten -bereich: Fenster mit Swing -kurz: Ein Spiel mit Zufallszahl, Versuchszähler und einem Knopf, der sich am Ende abschaltet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit Math.random eine ganze Zufallszahl von 1 bis 100 erzeugen -- entscheiden, welche Variablen in die Klasse gehören -- einen Knopf mit setEnabled abschalten -- ein Ratespiel mit einem Knopf „Neues Spiel“ bauen -::: - -## Das Ziel - -Jetzt wird gespielt: Der Computer denkt sich eine Zahl von 1 bis 100 aus. Du rätst, und das Fenster sagt dir „zu klein“ oder „zu groß“. Hast du sie gefunden, verrät es dir, wie viele Versuche du gebraucht hast. - -Neu sind diesmal **Zufallszahlen** und ein Knopf, der sich am Ende **abschaltet**. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Eine Zufallszahl - -`Math.random()` liefert eine zufällige Kommazahl von 0 bis knapp unter 1, zum Beispiel 0.4173. Daraus wird in drei Schritten eine ganze Zahl von 1 bis 100: - -```java -int geheim = (int) (Math.random() * 100) + 1; -``` - -| Schritt | Beispiel | -|---|---| -| `Math.random()` | 0.4173 | -| `* 100` | 41.73 | -| `(int)` schneidet die Nachkommastellen ab | 41 | -| `+ 1` | 42 | - -Ohne `+ 1` käme 0 bis 99 heraus. - -Probiere es im Bereich [Code](#zeige-codelive) aus: `int geheim = (int) (Math.random() * 100) + 1;` und `System.out.println(geheim);`. Jedes Ausführen liefert eine andere Zahl. - -::: angeberwissen -Computer können eigentlich gar nicht würfeln. `Math.random()` rechnet mit einer Formel eine lange Folge von Zahlen aus, die nur zufällig **aussehen**. Man nennt sie Pseudozufallszahlen. Für Spiele reicht das völlig. -::: - -## Was sich das Spiel merkt - -Zwei Dinge muss sich das Spiel über alle Klicks hinweg merken: die geheime Zahl und die Zahl der Versuche. Wie beim Klickzähler aus Lektion 32 stehen beide in der Klasse: - -```java -static int geheim = (int) (Math.random() * 100) + 1; -static int versuche = 0; -``` - -::: quiz -? Warum stehen `geheim` und `versuche` in der Klasse und nicht in `main`? -- [ ] Weil `Math.random()` nur in der Klasse funktioniert -- [x] Weil sie über alle Klicks erhalten bleiben und der Zuhörer `versuche` ändern muss -- [ ] Weil `main` keine Zahlen speichern kann -> Wie beim Klickzähler: Variablen aus `main` darf der Zuhörer nicht verändern. -::: - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; - -public class Zahlenraten { - static int geheim = (int) (Math.random() * 100) + 1; - static int versuche = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahlenraten"); - fenster.setSize(320, 180); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel hinweis = new JLabel("Ich denke mir eine Zahl von 1 bis 100."); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Raten"); - - knopf.addActionListener(e -> { - int tipp = Integer.parseInt(feld.getText()); - versuche++; - if (tipp < geheim) { - hinweis.setText(tipp + " ist zu klein."); - } else if (tipp > geheim) { - hinweis.setText(tipp + " ist zu groß."); - } else { - hinweis.setText("Richtig! Du hast " + versuche + " Versuche gebraucht."); - knopf.setEnabled(false); - } - feld.setText(""); - }); - - fenster.add(hinweis); - fenster.add(feld); - fenster.add(knopf); - fenster.setVisible(true); - } -} -``` - -- `feld.setText("")` leert das Textfeld nach jedem Tipp, damit du gleich den nächsten eintippen kannst. -- `knopf.setEnabled(false)` schaltet den Knopf ab. Er wird grau und lässt sich nicht mehr klicken. Das Spiel ist vorbei. - -::: ludwig -Mit `setEnabled(true)` schaltest du einen Knopf wieder ein. So kannst du zum Beispiel einen Knopf erst freigeben, wenn etwas eingegeben wurde. -::: - -## Mit 7 Versuchen gewinnen - -::: angeberwissen -Wer geschickt rät, braucht nie mehr als 7 Versuche. Rate immer die Mitte des Bereichs, der noch übrig ist: erst 50, dann 25 oder 75 und so weiter. Jeder Tipp halbiert den Bereich: 100, 50, 25, 13, 7, 4, 2, 1. Das ist die **binäre Suche** aus Lektion 19. Weil 2 hoch 7 = 128 größer als 100 ist, reichen 7 Tipps. -::: - -## Aufgabe: Zahlenraten - -Baue das Spiel nach und mach es besser: - -- Eine Eingabe wie „abc“ zählt nicht als Versuch und bringt den Hinweis „Bitte eine Zahl eingeben“. -- Ein zweiter Knopf **Neues Spiel** denkt sich eine neue Zahl aus, setzt die Versuche auf 0, schaltet den Knopf Raten wieder ein und setzt den Hinweis zurück. - -Zusatz: Nach 7 Fehlversuchen ist das Spiel verloren. Das Fenster verrät dann die geheime Zahl. - -**Du bist fertig, wenn …** - -- du bei jedem falschen Tipp „zu klein“ oder „zu groß“ siehst, -- „abc“ den Hinweis „Bitte eine Zahl eingeben“ bringt und die Versuche dabei gleich bleiben, -- **Neues Spiel** die Versuche auf 0 setzt, den Knopf Raten wieder einschaltet und den Hinweis zurücksetzt. - -::: ludwig -Für „zählt nicht als Versuch“ muss `versuche++` **hinter** `Integer.parseInt` stehen, beides im `try`-Block. Wirft `parseInt` eine Exception, wird die Zeile mit `versuche++` übersprungen. -::: - -## Spiel: Zu klein oder zu groß? - -::: spiel frage -titel: Spiel: Zu klein oder zu groß? -intro: Ludwig spielt Zahlenraten. Lies den Code und sag vorher, was passiert. Sieben Runden. - -runde: - // geheim ist 42 - if (tipp < geheim) { - hinweis.setText(tipp + " ist zu klein."); - } else if (tipp > geheim) { - hinweis.setText(tipp + " ist zu groß."); - } else { - hinweis.setText("Richtig!"); - } -frage: Ludwig tippt 50. Was steht im Hinweis? -- 50 ist zu klein. -- [x] 50 ist zu groß. -- Richtig! -> 50 ist größer als 42. - -runde: -frage: Die Zahl liegt von 1 bis 100. 50 war zu groß, 25 war zu klein. Welche Zahl rät Ludwig klug als Nächstes? -- 26 -- [x] 37 -- 49 -> Übrig sind 26 bis 49. Die Mitte davon ist 37 (oder 38). So halbiert jeder Tipp den Bereich. - -runde: - int wurf = (int) (Math.random() * 6) + 1; -frage: Welche Zahlen kann `wurf` haben? -- 0 bis 6 -- [x] 1 bis 6 -- 1 bis 7 -> `Math.random() * 6` liegt von 0 bis knapp unter 6. `(int)` macht 0 bis 5 daraus, `+ 1` dann 1 bis 6. Ein Würfel! - -runde: - int zahl = (int) (Math.random() * 10); -frage: Welche Zahlen kann `zahl` haben? -- [x] 0 bis 9 -- 1 bis 10 -- 0 bis 10 -> Ohne `+ 1` fängt es bei 0 an, und die 10 wird nie erreicht. - -runde: - // geheim ist 42 - versuche++; - if (tipp == geheim) { - hinweis.setText("Richtig! Du hast " + versuche + " Versuche gebraucht."); - } -frage: Ludwig tippt 50, dann 25, dann 42. Was steht am Ende im Hinweis? -- Richtig! Du hast 2 Versuche gebraucht. -- [x] Richtig! Du hast 3 Versuche gebraucht. -- Richtig! Du hast 42 Versuche gebraucht. -> `versuche++` zählt bei jedem Klick hoch, auch beim Treffer. - -runde: - knopf.setEnabled(false); -frage: Ludwig hat die Zahl gefunden und klickt noch einmal auf **Raten**. Was passiert? -- Das Spiel beginnt von vorn. -- Es zählt einen Versuch mehr. -- [x] Nichts, der Knopf ist grau und lässt sich nicht klicken. -> `setEnabled(false)` schaltet den Knopf ab. - -runde: -frage: Die Zahl liegt jetzt von 1 bis 1000. Wie viele Tipps braucht man mit der binären Suche höchstens? -- 7 -- [x] 10 -- 500 -> 2 hoch 10 = 1024 ist größer als 1000. Zehn Halbierungen reichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit Math.random eine ganze Zufallszahl von 1 bis 100 erzeugen. → Eine Zufallszahl -- Ich kann entscheiden, welche Variablen in die Klasse gehören. → Was sich das Spiel merkt -- Ich kann einen Knopf mit setEnabled abschalten. → Das ganze Programm -- Ich kann ein Ratespiel mit einem Knopf „Neues Spiel“ bauen. → Aufgabe: Zahlenraten -::: - -## Wissenstest - -::: quiz -? Welche Zahlen liefert (int) (Math.random() * 100) + 1? -→ Eine Zufallszahl -- [ ] 0 bis 100 -- [x] 1 bis 100 -- [ ] 1 bis 101 - -? Was macht knopf.setEnabled(false)? -→ Das ganze Programm -- [ ] Es löscht den Knopf aus dem Fenster. -- [x] Der Knopf wird grau und lässt sich nicht mehr klicken. -- [ ] Es ändert die Beschriftung des Knopfs. - -? Wie viele Versuche braucht man mit der binären Suche höchstens bei 1 bis 100? -→ Aufgabe: Zahlenraten -- [ ] 50 -- [ ] 10 -- [x] 7 -> Jeder Tipp halbiert den Bereich, und 2 hoch 7 = 128 ist größer als 100. -::: diff --git a/web/lessons/de/37-viele-knoepfe-ein-zuhoerer.md b/web/lessons/de/37-viele-knoepfe-ein-zuhoerer.md deleted file mode 100644 index ecdc2b58..00000000 --- a/web/lessons/de/37-viele-knoepfe-ein-zuhoerer.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -titel: Viele Knöpfe, ein Zuhörer -bereich: Fenster mit Swing -kurz: Ein Zuhörer für viele Knöpfe, mit getActionCommand und getSource herausfinden, welcher Knopf geklickt wurde. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Zuhörer in einer Variable vom Typ ActionListener speichern und bei mehreren Knöpfen anmelden -- erklären, was getActionCommand und getSource liefern -- Knöpfe in einer Schleife auf ein JPanel setzen -- ein Zahlenschloss bauen, das sich nur mit dem richtigen Code öffnet -::: - -## Das Ziel - -Seit der Sache mit dem Kuchen will Ludwig seinen Stall abschließen. Er baut ein **Zahlenschloss**: neun Zifferntasten, eine Null, **C** zum Löschen und **OK** zum Prüfen. Nur wer 1234 eintippt, bekommt „Tor offen!“. - -Das sind zwölf Knöpfe. Bisher hatte jeder Knopf seinen eigenen Zuhörer, wie beim Taschenrechner in Lektion 35. Heute lernst du, wie **ein** Zuhörer für alle Knöpfe reicht. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Neun fast gleiche Zuhörer - -So sähe es mit einem Zuhörer pro Taste aus: - -```java -eins.addActionListener(e -> anzeige.setText(anzeige.getText() + "1")); -zwei.addActionListener(e -> anzeige.setText(anzeige.getText() + "2")); -drei.addActionListener(e -> anzeige.setText(anzeige.getText() + "3")); -// ... und so weiter bis 9 -``` - -Die Zeilen unterscheiden sich nur in der Ziffer. Wer eine Kleinigkeit ändern will, muss es neunmal tun und vergisst bestimmt eine Zeile. - -## Ein Zuhörer als Variable - -Ein Zuhörer kann auch in einer **Variable** stehen. Ihr Datentyp ist `ActionListener`, der Vertrag aus Lektion 30. Danach meldest du denselben Zuhörer bei mehreren Knöpfen an: - -```java -ActionListener ziffer = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - -eins.addActionListener(ziffer); -zwei.addActionListener(ziffer); -drei.addActionListener(ziffer); -``` - -`ActionListener` steht im Paket `java.awt.event`. Du brauchst deshalb oben `import java.awt.event.*;`. - -Jetzt gibt es den Code nur einmal. Aber woher weiß der Zuhörer, welche Ziffer er anhängen soll? - -## Wer hat geklickt? - -Das `e` vor dem Pfeil ist das **Ereignis**, ein Objekt vom Typ `ActionEvent`. Der Knopf gibt es bei jedem Klick mit, und darin steht, was passiert ist: - -| Aufruf | Liefert | Beispiel | -|---|---|---| -| `e.getActionCommand()` | die Beschriftung des geklickten Knopfs, als Text | `"7"` | -| `e.getSource()` | den geklickten Knopf selbst | der Knopf `ok` | - -- `getActionCommand()` ist praktisch, wenn die Beschriftung schon sagt, was zu tun ist, wie bei den Ziffern. -- `getSource()` vergleichst du mit `==` mit einem Knopf: `if (e.getSource() == ok)`. Hier ist `==` richtig, denn es geht um genau dieses eine Objekt, nicht um einen Text. - -::: ludwig -Texte vergleichst du mit `equals`, Knöpfe mit `==`. Willst du über die Beschriftung entscheiden, schreibst du `e.getActionCommand().equals("OK")`. Das geht auch, bricht aber, sobald jemand die Beschriftung in „Öffnen“ ändert. `e.getSource() == ok` bleibt richtig. -::: - -## Knöpfe in einer Schleife - -Mit einem gemeinsamen Zuhörer kannst du die Zifferntasten sogar in einer Schleife bauen. Damit sie ordentlich im Raster stehen, kommen sie auf ein **JPanel**. Ein JPanel ist eine Fläche im Fenster mit einem eigenen Layout: - -```java -JPanel tasten = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton ziffer = new JButton("" + i); - ziffer.addActionListener(taste); - tasten.add(ziffer); -} -``` - -- `"" + i` macht aus der Zahl `i` den Text für die Beschriftung. -- In jedem Durchlauf entsteht ein neuer Knopf. Alle bekommen denselben Zuhörer `taste`. -- `fenster.add(tasten, BorderLayout.CENTER)` setzt die ganze Fläche mit allen Tasten ins Fenster. - -::: quiz -? Wie viele Knöpfe und wie viele Zuhörer entstehen in dieser Schleife? -- [ ] 9 Knöpfe und 9 Zuhörer -- [x] 9 Knöpfe und 1 Zuhörer -- [ ] 1 Knopf und 9 Zuhörer -> In jedem Durchlauf kommt ein neuer Knopf dazu. Alle bekommen denselben Zuhörer `taste`. -::: - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Zahlenschloss { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahlenschloss"); - fenster.setSize(240, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new BorderLayout()); - - JLabel anzeige = new JLabel("", SwingConstants.CENTER); - JPanel tasten = new JPanel(new GridLayout(0, 3)); - JButton loeschen = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener taste = e -> { - if (e.getSource() == loeschen) { - anzeige.setText(""); - } else if (e.getSource() == ok) { - if (anzeige.getText().equals("1234")) { - anzeige.setText("Tor offen!"); - } else { - anzeige.setText("Falsch"); - } - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton ziffer = new JButton("" + i); - ziffer.addActionListener(taste); - tasten.add(ziffer); - } - JButton nullTaste = new JButton("0"); - nullTaste.addActionListener(taste); - loeschen.addActionListener(taste); - ok.addActionListener(taste); - tasten.add(loeschen); - tasten.add(nullTaste); - tasten.add(ok); - - fenster.add(anzeige, BorderLayout.NORTH); - fenster.add(tasten, BorderLayout.CENTER); - fenster.setVisible(true); - } -} -``` - -- `loeschen` und `ok` werden **vor** dem Zuhörer angelegt, denn der Zuhörer vergleicht mit ihnen. -- Der Zuhörer prüft erst die beiden besonderen Knöpfe. Alle anderen sind Ziffern und hängen ihre Beschriftung an. -- Die Null heißt `nullTaste`, weil `null` in Java schon ein Wort mit fester Bedeutung ist. - -::: angeberwissen -Das Zahlenfeld am Telefon hat die 1 oben links, das am Taschenrechner und an der Computertastatur hat die 7 oben links. Warum? Als die Tastentelefone kamen, gab es Tests mit Leuten, die damit wählen sollten. Mit der 1 oben ging es schneller, und Rechenmaschinen hatten ihre Anordnung da schon längst. Ludwigs Schloss macht es wie das Telefon. -::: - -## Spiel: Wer hat geklickt? - -::: spiel zuhoerer -titel: Spiel: Wer hat geklickt? -intro: In jeder Runde hört **ein** Zuhörer an mehreren Knöpfen. Ludwig zeigt dir Code, Fenster und die Klicks. Was steht am Ende in der Anzeige? Danach spielt Ludwig die Klicks ab. -frage: Was steht nach den Klicks in der Anzeige? - -runde: - JFrame fenster = new JFrame("Buchstaben"); - JLabel anzeige = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> Ein Zuhörer, zwei Knöpfe. `e.getActionCommand()` liefert jedes Mal die Beschriftung des geklickten Knopfs, und sie wird hinten angehängt. - -runde: - JFrame fenster = new JFrame("Zähler"); - static int zahl = 0; - JLabel anzeige = new JLabel("0"); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == plus) { - zahl++; - } else { - zahl--; - } - anzeige.setText("" + zahl); - }; - plus.addActionListener(z); - minus.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> `e.getSource()` sagt, welcher Knopf es war. Zweimal plus, einmal minus: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame fenster = new JFrame("Buchstaben"); - JLabel anzeige = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> Der Zuhörer ist nur bei A und B angemeldet. Beim Klick auf C hört niemand zu. - -runde: - JFrame fenster = new JFrame("Tasten"); - JLabel anzeige = new JLabel(""); - JButton eins = new JButton("1"); - JButton zwei = new JButton("2"); - JButton weg = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - anzeige.setText(""); - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - eins.addActionListener(z); - zwei.addActionListener(z); - weg.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> Nach 1 und 2 steht „12“ da. C löscht alles, und die letzte 1 steht allein. - -runde: - JFrame fenster = new JFrame("Schloss"); - JLabel anzeige = new JLabel(""); - JButton vier = new JButton("4"); - JButton zwei = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (anzeige.getText().equals("42")) { - anzeige.setText("Tor offen!"); - } else { - anzeige.setText("Falsch"); - } - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - vier.addActionListener(z); - zwei.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Tor offen! -- [x] Falsch -- 24OK -> Eingetippt ist „24“, das Geheimnis ist aber „42“. Die Reihenfolge der Klicks zählt. -::: - -## Aufgabe: Zahlenschloss - -Baue das Zahlenschloss nach und probiere es aus. - -Dann mach es besser: - -- Nach drei falschen Versuchen schaltet sich **OK** ab, wie der Knopf beim Zahlenraten in Lektion 36. Zähle die Versuche in einer `static int`-Variable in der Klasse. -- Zusatz: Die Anzeige zeigt `*` statt der Ziffern. Merke dir die echte Eingabe in einer `static String eingabe = "";` und hänge an die Anzeige nur einen Stern an. - -**Du bist fertig, wenn …** - -- der richtige Code das Schloss öffnet und ein falscher nicht, -- nach drei falschen Versuchen **OK** ausgeschaltet ist, -- im Zusatz die Anzeige nur Sterne zeigt, der Code aber trotzdem geprüft wird. - -::: ludwig -Zähle im Zweig für **OK** bei jedem falschen Code `versuche++` und prüfe danach `if (versuche >= 3)`. Dann schaltet `ok.setEnabled(false)` den Knopf ab, wie beim Zahlenraten. Für die Sterne: `eingabe = eingabe + e.getActionCommand();` und `anzeige.setText(anzeige.getText() + "*");`. Verglichen wird dann `eingabe`, nicht der Text der Anzeige. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Zuhörer in einer Variable vom Typ ActionListener speichern und bei mehreren Knöpfen anmelden. → Ein Zuhörer als Variable -- Ich kann erklären, was getActionCommand und getSource liefern. → Wer hat geklickt? -- Ich kann Knöpfe in einer Schleife auf ein JPanel setzen. → Knöpfe in einer Schleife -- Ich kann ein Zahlenschloss bauen, das sich nur mit dem richtigen Code öffnet. → Aufgabe: Zahlenschloss -::: - -## Wissenstest - -::: quiz -? Was liefert e.getActionCommand() bei einem Klick auf den Knopf new JButton("7")? -→ Wer hat geklickt? -- [ ] die Zahl 7 -- [x] den Text "7" -- [ ] den Knopf selbst - -? Wie prüfst du, ob der Knopf ok geklickt wurde? -→ Wer hat geklickt? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource liefert den Knopf selbst, und den vergleichst du mit ==. - -? Warum steht ein Zuhörer in einer Variable? -→ Ein Zuhörer als Variable -- [ ] Damit er schneller läuft. -- [x] Damit man ihn bei mehreren Knöpfen anmelden kann und den Code nur einmal schreibt. -- [ ] Weil Lambdas sonst nicht erlaubt sind. - -? Ordne zu. -→ Wer hat geklickt? -= ActionListener -> Datentyp eines Zuhörers -= ActionEvent -> das Ereignis e mit den Infos zum Klick -= JPanel -> eine Fläche mit eigenem Layout -::: diff --git a/web/lessons/de/38-innere-klassen.md b/web/lessons/de/38-innere-klassen.md deleted file mode 100644 index d9b376b0..00000000 --- a/web/lessons/de/38-innere-klassen.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Innere Klassen -bereich: Fenster mit Swing -kurz: Einen Zuhörer als benannte innere Klasse, als anonyme Klasse und als Lambda schreiben und wissen, wer was sehen darf. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Zuhörer als benannte innere Klasse schreiben -- einen Zuhörer als anonyme Klasse schreiben -- erklären, was beim Lambda weggelassen wird -- erklären, welche Schreibweise welche Variablen sieht -- einen Zähler mit drei Knöpfen in drei Schreibweisen bauen -::: - -## Das Ziel - -Den Klickzähler aus Lektion 32 kennst du schon. Heute schreibst du seinen Zuhörer auf **drei Arten**, die alle genau dasselbe tun: - -1. als **benannte innere Klasse**, wie man es vor Java 8 meistens gemacht hat, -2. als **anonyme Klasse**, die du aus Lektion 30 („ActionListener ist ein Interface“) kennst, -3. als **Lambda** mit dem Pfeil, so wie bisher. - -In fremdem Code, in Büchern und im Internet triffst du alle drei Schreibweisen. Nach dieser Lektion erkennst du sie. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Erinnerung: der Vertrag - -Ein Zuhörer ist alles, was den Vertrag `ActionListener` erfüllt. Darin steht genau eine Methode: - -``` -public void actionPerformed(ActionEvent e) -``` - -Der Knopf ruft sie bei jedem Klick auf. Die drei Schreibweisen sind drei Wege, diese eine Methode zu liefern. - -## 1. Die benannte innere Klasse - -Eine **innere Klasse** steht innerhalb einer anderen Klasse. Sie hat einen Namen, hier `Plus`, und erfüllt den Vertrag mit `implements ActionListener`: - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Zaehler { - static int anzahl = 0; - static JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - static class Plus implements ActionListener { - public void actionPerformed(ActionEvent e) { - anzahl++; - zahl.setText("" + anzahl); - } - } - - public static void main(String[] args) { - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - JButton nochmal = new JButton("Auch +1"); - plus.addActionListener(new Plus()); - nochmal.addActionListener(new Plus()); - - fenster.add(zahl); - fenster.add(plus); - fenster.add(nochmal); - fenster.setVisible(true); - } -} -``` - -- `new Plus()` erzeugt ein Objekt der Klasse `Plus`. Das ist der Zuhörer, den der Knopf bekommt. -- Weil die Klasse einen Namen hat, kannst du sie bei mehreren Knöpfen benutzen, hier bei `plus` und `nochmal`. -- Die Klasse steht **außerhalb** von `main`. Darum sieht sie nur, was in der Klasse `Zaehler` steht: `anzahl` und `zahl`. Deshalb ist `zahl` diesmal eine Variable der Klasse und nicht von `main`. -- `static class` brauchst du, weil `main` static ist. Ohne `static` meldet javac „non-static variable this cannot be referenced from a static context“. - -## 2. Die anonyme Klasse - -Brauchst du den Zuhörer nur an einer Stelle, kannst du die Klasse direkt dort schreiben, wo das Objekt gebraucht wird. Sie bekommt dann keinen Namen: - -```java -plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - anzahl++; - zahl.setText("" + anzahl); - } -}); -``` - -- `new ActionListener() { … }` heißt: „ein neues Objekt, das den Vertrag ActionListener erfüllt, und zwar so: …“. -- Am Ende steht `});`: die geschweifte Klammer schließt die Klasse, die runde den Aufruf von `addActionListener`. -- Die anonyme Klasse steht **in** `main`. Sie sieht deshalb auch die Variablen von `main`, solange diese sich nicht mehr ändern. - -::: quiz -? Was schließt `});` am Ende der anonymen Klasse? -- [x] `}` schließt die Klasse, `)` den Aufruf von `addActionListener`. -- [ ] `}` schließt `main`, `)` die Klasse. -- [ ] Beides schließt die Methode `actionPerformed`. -> Die Methode `actionPerformed` endet schon eine Zeile davor mit ihrer eigenen `}`. -::: - -## 3. Das Lambda - -Das Lambda ist die Kurzform der anonymen Klasse, seit Java 8: - -```java -plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); -}); -``` - -Weggelassen wird alles, was Java sowieso weiß: `new ActionListener()`, der Name `actionPerformed`, `public void` und der Typ `ActionEvent`. Übrig bleiben der Parameter `e`, der Pfeil und der Rumpf. Das geht nur, weil der Vertrag genau **eine** Methode hat. - -## Die drei im Vergleich - -| | benannte innere Klasse | anonyme Klasse | Lambda | -|---|---|---|---| -| hat einen Namen | ja, z. B. `Plus` | nein | nein | -| steht | in der Klasse, außerhalb von `main` | direkt beim Knopf | direkt beim Knopf | -| für mehrere Knöpfe | ja, `new Plus()` | nein | ja, in einer Variable (Lektion 37) | -| sieht Variablen von `main` | nein | ja, wenn sie sich nicht ändern | ja, wenn sie sich nicht ändern | -| Länge | am längsten | mittel | am kürzesten | - -::: ludwig -Wenn sich eine Variable von `main` im Zuhörer ändern soll, wie `anzahl++`, muss sie in die Klasse, egal welche Schreibweise du nimmst. Das war das Problem aus Lektion 32. -::: - -::: angeberwissen -Schau nach dem Übersetzen in den Ordner mit den `.class`-Dateien. Für jede Klasse legt javac eine Datei an: `Zaehler.class`, für die innere Klasse `Zaehler$Plus.class` und für die anonyme Klasse `Zaehler$1.class`. Weil sie keinen Namen hat, bekommt sie eine Nummer. Für ein Lambda gibt es keine eigene Datei, Java baut es erst beim Start. -::: - -## Spiel: Welche Schreibweise? - -::: spiel frage -titel: Spiel: Welche Schreibweise? -intro: Ludwig zeigt dir Zuhörer aus fremden Programmen. Welche Schreibweise ist das? Sechs Runden. -frage: Welche Schreibweise ist das? -antworten: benannte innere Klasse | anonyme Klasse | Lambda - -runde: - knopf.addActionListener(e -> text.setText("Hallo")); -richtig: Lambda -> Der Pfeil `->` verrät das Lambda. - -runde: - knopf.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - text.setText("Hallo"); - } - }); -richtig: anonyme Klasse -> `new ActionListener() {` und dahinter eine ganze Methode: Hier wird eine Klasse ohne Namen geschrieben. - -runde: - static class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - text.setText("Hallo"); - } - } -richtig: benannte innere Klasse -> Die Klasse heißt `Gruss` und erfüllt den Vertrag mit `implements ActionListener`. - -runde: - knopf.addActionListener(new Gruss()); -richtig: benannte innere Klasse -> `new Gruss()` erzeugt ein Objekt der Klasse `Gruss`. Die Klasse selbst steht woanders, sie hat einen Namen. - -runde: - ActionListener taste = e -> { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - }; -richtig: Lambda -> Ein Lambda in einer Variable, wie beim Zahlenschloss in Lektion 37. Der Pfeil verrät es. - -runde: - ActionListener taste = new ActionListener() { - public void actionPerformed(ActionEvent e) { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; -richtig: anonyme Klasse -> Auch eine anonyme Klasse kann in einer Variable stehen. Dann kann man sie wie das Lambda bei mehreren Knöpfen anmelden. -::: - -## Aufgabe: Ein Zähler, drei Schreibweisen - -Baue den Klickzähler mit drei Knöpfen: - -- **+1** bekommt die benannte innere Klasse `Plus`. -- **-1** bekommt eine anonyme Klasse. -- **Reset** bekommt ein Lambda. - -Probiere aus, ob alle drei Knöpfe wie erwartet zählen. Lass dann bei der Klasse `Plus` das `static` weg und lies die Fehlermeldung von javac. - -**Du bist fertig, wenn …** - -- **+1** hoch-, **-1** herunterzählt und **Reset** auf 0 setzt, -- im Code genau eine benannte innere Klasse, eine anonyme Klasse und ein Lambda stehen, -- du ohne `static` bei `Plus` die Fehlermeldung von javac gelesen hast und erklären kannst, was sie bedeutet. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Zuhörer als benannte innere Klasse schreiben. → 1. Die benannte innere Klasse -- Ich kann einen Zuhörer als anonyme Klasse schreiben. → 2. Die anonyme Klasse -- Ich kann erklären, was beim Lambda weggelassen wird. → 3. Das Lambda -- Ich kann erklären, welche Schreibweise welche Variablen sieht. → Die drei im Vergleich -- Ich kann einen Zähler mit drei Knöpfen in drei Schreibweisen bauen. → Aufgabe: Ein Zähler, drei Schreibweisen -::: - -## Wissenstest - -::: quiz -? Warum geht die Kurzform mit dem Pfeil bei ActionListener? -→ 3. Das Lambda -- [ ] Weil ActionListener eine Klasse ist. -- [x] Weil der Vertrag genau eine Methode hat. -- [ ] Weil der Knopf es so verlangt. - -? Welche Schreibweise kannst du mit new Plus() bei mehreren Knöpfen benutzen, ohne sie in eine Variable zu speichern? -→ 1. Die benannte innere Klasse -- [x] die benannte innere Klasse -- [ ] die anonyme Klasse -- [ ] das Lambda - -? Warum steht static vor class Plus? -→ 1. Die benannte innere Klasse -- [ ] Damit die Klasse schneller ist. -- [x] Weil main static ist und sonst kein Objekt von Plus erzeugen kann. -- [ ] Weil jede Klasse static sein muss. - -? Ordne zu. -→ Die drei im Vergleich -= class Plus implements ActionListener -> benannte innere Klasse -= new ActionListener() { … } -> anonyme Klasse -= der Pfeil hinter e -> Lambda -::: diff --git a/web/lessons/de/39-zwei-reiter.md b/web/lessons/de/39-zwei-reiter.md deleted file mode 100644 index d0842199..00000000 --- a/web/lessons/de/39-zwei-reiter.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Zwei Reiter -bereich: Fenster mit Swing -kurz: Ein Fenster mit zwei Reitern bauen, Daten von einem Reiter zum anderen bringen und die Liste als Textdatei speichern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Fenster mit zwei Reitern bauen -- mit append Zeilen an eine JTextArea anhängen -- erklären, warum ein Zuhörer Bausteine auf beiden Reitern benutzen kann -- im Zuhörer mit try und catch eine Datei speichern -- eine Einkaufsliste bauen, die mitzählt, wie viele Dinge darauf stehen -::: - -## Das Ziel - -Ludwigs Einkaufsliste aus Lektion 23 bekommt ein Fenster. Es hat zwei **Reiter**, wie die Tabs im Browser: - -- Im Reiter **Eingabe** tippst du ein, was du brauchst, und klickst auf **Hinzufügen**. -- Im Reiter **Liste** steht alles, was du eingegeben hast. Ein Knopf **Speichern** schreibt die Liste in eine Textdatei. - -Neu sind die Reiter (`JTabbedPane`), ein Textbereich mit mehreren Zeilen (`JTextArea`) und das Speichern aus einem Zuhörer heraus. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Reiter mit JTabbedPane - -Jeder Reiter zeigt eine eigene Fläche. Die Flächen sind JPanels, wie das Tastenfeld beim Zahlenschloss in Lektion 37: - -```java -JTabbedPane reiter = new JTabbedPane(); -JPanel eingabe = new JPanel(new GridLayout(0, 1)); -JPanel liste = new JPanel(new BorderLayout()); - -reiter.addTab("Eingabe", eingabe); -reiter.addTab("Liste", liste); -fenster.add(reiter); -``` - -| Aufruf | Bedeutung | -|---|---| -| `addTab("Eingabe", eingabe)` | neuer Reiter mit der Beschriftung „Eingabe“, er zeigt das JPanel `eingabe` | -| `setSelectedIndex(1)` | wechselt zum zweiten Reiter, gezählt wird ab 0 | -| `setTitleAt(1, "Liste (3)")` | ändert die Beschriftung des zweiten Reiters | - -Beim Start ist der Reiter offen, der zuerst hinzugefügt wurde. - -## Ein Textbereich mit mehreren Zeilen - -Ein `JTextField` hat nur eine Zeile. Für die Liste brauchst du eine `JTextArea`: - -```java -JTextArea bereich = new JTextArea(); -bereich.setEditable(false); -bereich.append("Heu\n"); -``` - -- `append` hängt Text hinten an. Das `"\n"` beginnt danach eine neue Zeile, wie beim Zerlegen mit `split("\n")` in Lektion 24. -- `setEditable(false)` verhindert, dass man in der Liste selbst herumtippt. -- `getText()` liefert den ganzen Text mit allen Zeilen. - -::: quiz -? Was macht `bereich.append("Heu\n")`? -- [ ] Es ersetzt den ganzen Text durch „Heu“. -- [x] Es hängt „Heu“ hinten an und beginnt danach eine neue Zeile. -- [ ] Es schreibt „Heu\n“ genau so in die Liste. -> `append` hängt an, `"\n"` ist der Zeilenumbruch. -::: - -## Von einem Reiter zum anderen - -Der Knopf **Hinzufügen** steht im Reiter Eingabe, der Textbereich im Reiter Liste. Trotzdem kann der Zuhörer beide benutzen. Alle Bausteine sind Variablen in `main`, und das Lambda sieht sie alle, egal auf welchem Reiter sie später angezeigt werden: - -```java -hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\n"); - info.setText(feld.getText() + " steht auf der Liste."); - feld.setText(""); -}); -``` - -::: ludwig -Ein Baustein kann nur auf **einer** Fläche liegen. Fügst du denselben Knopf erst zu `eingabe` und dann zu `liste` hinzu, nimmt Swing ihn bei `eingabe` wieder weg. Er erscheint nur im Reiter Liste. -::: - -## Speichern im Zuhörer - -Zum Speichern nimmst du den `PrintWriter` aus Lektion 23. Im Zuhörer gibt es aber einen Haken: Den Ausweg `throws Exception` aus Lektion 23 gibt es hier nicht. Das Lambda ist eine eigene Methode, `actionPerformed`, und die darf laut Vertrag keine `FileNotFoundException` weiterwerfen. Darum steht das Speichern in try und catch: - -```java -speichern.addActionListener(e -> { - try { - PrintWriter aus = new PrintWriter("einkaufsliste.txt"); - aus.print(bereich.getText()); - aus.close(); - info.setText("Gespeichert in einkaufsliste.txt"); - reiter.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Speichern ging nicht."); - } -}); -``` - -- Die Exception heißt hier `ex`, weil der Name `e` schon für das Ereignis vergeben ist. -- Nach dem Speichern springt das Fenster mit `setSelectedIndex(0)` zurück zum Reiter Eingabe, wo die Meldung steht. -- VisuStruct zeigt die gespeicherte Datei unter der Konsole, sobald du auf Speichern geklickt hast. - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class Einkaufsliste { - public static void main(String[] args) { - JFrame fenster = new JFrame("Einkaufsliste"); - fenster.setSize(300, 250); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane reiter = new JTabbedPane(); - - JPanel eingabe = new JPanel(new GridLayout(0, 1)); - JTextField feld = new JTextField(); - JButton hinzufuegen = new JButton("Hinzufügen"); - JLabel info = new JLabel("Noch nichts auf der Liste."); - eingabe.add(new JLabel("Was brauchst du?")); - eingabe.add(feld); - eingabe.add(hinzufuegen); - eingabe.add(info); - - JPanel liste = new JPanel(new BorderLayout()); - JTextArea bereich = new JTextArea(); - bereich.setEditable(false); - JButton speichern = new JButton("Speichern"); - liste.add(bereich, BorderLayout.CENTER); - liste.add(speichern, BorderLayout.SOUTH); - - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); - - hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\n"); - info.setText(feld.getText() + " steht auf der Liste."); - feld.setText(""); - }); - - speichern.addActionListener(e -> { - try { - PrintWriter aus = new PrintWriter("einkaufsliste.txt"); - aus.print(bereich.getText()); - aus.close(); - info.setText("Gespeichert in einkaufsliste.txt"); - reiter.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Speichern ging nicht."); - } - }); - - fenster.add(reiter); - fenster.setVisible(true); - } -} -``` - -Tippe Heu ein, klicke auf Hinzufügen, dann Äpfel und noch einmal Hinzufügen. Wechsle zum Reiter Liste und klicke auf Speichern. - -## Spiel: Wo landet es? - -::: spiel frage -titel: Spiel: Wo landet es? -intro: Ludwig baut Fenster mit Reitern. Lies den Code und sag voraus, was du siehst. Sechs Runden. - -runde: - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); -frage: Welcher Reiter ist beim Start offen? -- [x] Eingabe -- Liste -- keiner -> Beim Start ist der Reiter offen, der zuerst hinzugefügt wurde. - -runde: - reiter.addTab("Liste", liste); - reiter.addTab("Eingabe", eingabe); -frage: Welcher Reiter ist beim Start offen? -- Eingabe -- [x] Liste -- beide -> Diesmal kommt Liste zuerst. Die Reihenfolge von `addTab` bestimmt die Reihenfolge der Reiter. - -runde: - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); - reiter.addTab("Hilfe", hilfe); - reiter.setSelectedIndex(1); -frage: Welcher Reiter ist offen? -- Eingabe -- [x] Liste -- Hilfe -> Gezählt wird ab 0: 0 ist Eingabe, 1 ist Liste, 2 ist Hilfe. - -runde: - hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\n"); - feld.setText(""); - }); - // Du tippst Heu, klickst Hinzufügen, tippst Äpfel, klickst Hinzufügen. -frage: Was steht danach im Reiter Liste? -- Äpfel -- HeuÄpfel -- [x] Heu\nÄpfel -> `append` hängt an, und jedes `"\n"` beginnt eine neue Zeile. - -runde: - JButton knopf = new JButton("OK"); - eingabe.add(knopf); - liste.add(knopf); -frage: Wo erscheint der Knopf OK? -- in beiden Reitern -- nur im Reiter Eingabe -- [x] nur im Reiter Liste -> Ein Baustein kann nur auf einer Fläche liegen. Beim zweiten `add` nimmt Swing ihn von `eingabe` weg. - -runde: - speichern.addActionListener(e -> { - bereich.setText(""); - reiter.setTitleAt(1, "Liste (leer)"); - reiter.setSelectedIndex(0); - }); - // Der Knopf speichern liegt im Reiter Liste. -frage: Was siehst du nach dem Klick? -- den leeren Reiter Liste -- [x] den Reiter Eingabe, und der zweite Reiter heißt „Liste (leer)“ -- den Reiter Eingabe, der zweite Reiter heißt weiter „Liste“ -> Der Zuhörer leert den Bereich, benennt den Reiter um und wechselt zu Reiter 0. Alles passiert bei einem Klick. -::: - -## Aufgabe: Einkaufsliste mit Reitern - -Baue das Programm nach und probiere es aus. - -Dann mach es besser: - -- Leere Eingaben kommen nicht auf die Liste. Prüfe vorher `feld.getText().isEmpty()` (Lektion 17). -- Der Reiter Liste zeigt in seinem Titel, wie viele Dinge auf der Liste stehen: „Liste (3)“. Zähle in einer `static int`-Variable mit und benutze `setTitleAt`. -- Zusatz: Ein dritter Reiter **Hilfe** mit einem JLabel, das erklärt, wie das Programm geht. - -**Du bist fertig, wenn …** - -- ein eingegebener Eintrag nach dem Klick im Reiter Liste steht, -- ein leeres Feld nichts zur Liste hinzufügt, -- der Reiter nach drei Einträgen „Liste (3)“ heißt. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Fenster mit zwei Reitern bauen. → Reiter mit JTabbedPane -- Ich kann mit append Zeilen an eine JTextArea anhängen. → Ein Textbereich mit mehreren Zeilen -- Ich kann erklären, warum ein Zuhörer Bausteine auf beiden Reitern benutzen kann. → Von einem Reiter zum anderen -- Ich kann im Zuhörer mit try und catch eine Datei speichern. → Speichern im Zuhörer -- Ich kann eine Einkaufsliste bauen, die mitzählt, wie viele Dinge darauf stehen. → Aufgabe: Einkaufsliste mit Reitern -::: - -## Wissenstest - -::: quiz -? Welcher Reiter ist nach reiter.setSelectedIndex(0) offen? -→ Reiter mit JTabbedPane -- [x] der erste -- [ ] der zweite -- [ ] keiner - -? Warum steht das Speichern im Zuhörer in try und catch? -→ Speichern im Zuhörer -- [ ] Weil PrintWriter nur in try funktioniert. -- [x] Weil actionPerformed keine FileNotFoundException weiterwerfen darf. -- [ ] Weil main kein throws hat. - -? Was ist der Unterschied zwischen JTextField und JTextArea? -→ Ein Textbereich mit mehreren Zeilen -- [ ] Es gibt keinen. -- [x] JTextArea kann mehrere Zeilen zeigen. -- [ ] JTextField kann man nicht beschreiben. - -? Ordne zu. -→ Reiter mit JTabbedPane -= JTabbedPane -> eine Fläche mit Reitern -= JPanel -> die Fläche eines Reiters -= JTextArea -> ein Textbereich mit mehreren Zeilen -::: diff --git a/web/lessons/de/40-model-view-controller.md b/web/lessons/de/40-model-view-controller.md deleted file mode 100644 index b6b6f6c7..00000000 --- a/web/lessons/de/40-model-view-controller.md +++ /dev/null @@ -1,430 +0,0 @@ ---- -titel: Model View Controller -bereich: Fenster mit Swing -kurz: Den Klickzähler in Model, View und Controller zerlegen und verstehen, wer mit wem redet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- sagen, was Model, View und Controller jeweils tun -- ein Model ohne Swing schreiben und ohne Fenster testen -- erklären, was new Zaehler() baut und warum dort kein static steht -- die vier Nachrichten nach einem Klick nennen -- eine zweite View dazubauen, ohne das Model zu ändern -- einen Zähler mit −1, Reset und Grenzen im Model bauen -::: - -## Das Ziel - -Große Programme mit Fenstern haben fast immer denselben Aufbau. Er heißt **Model View Controller**, kurz **MVC**: - -- Das **Model** hält die Daten und kennt ihre Regeln. -- Die **View** zeigt die Daten nur an. -- Der **Controller** reagiert auf Klicks und gibt Bescheid. - -Du lernst MVC am Klickzähler aus Lektion 32. Er wird Schritt für Schritt in drei Teile zerlegt. Am Ende zeigt er denselben Zählerstand zweimal an: als Zahl und als Balken. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Alles in einer Klasse - -So sah der Klickzähler bisher aus, alles steckt in `main` und einer Variable der Klasse: - -```java -static int anzahl = 0; - -public static void main(String[] args) { - // ... Fenster bauen ... - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); -} -``` - -Das läuft. Aber stell dir vor, der Zählerstand soll zusätzlich als Balken erscheinen, später noch in einem zweiten Fenster und in einer Datei. Dann muss jeder Zuhörer an alle Anzeigen denken. Rechnen und Anzeigen sind ineinander verknotet, und mit jeder Anzeige wird der Knoten größer. - -MVC löst den Knoten: Jede Aufgabe bekommt ihre eigene Klasse. - -## Das Model - -Das Model ist der Zähler selbst, ganz ohne Swing: - -```java -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} -``` - -- Diesmal steht vor `wert` und den Methoden kein `static`. Dann gehören sie zu einem **Objekt**: `new Zaehler()` baut einen Zähler mit eigenem `wert`, so wie `new JButton("+1")` einen Knopf mit eigener Beschriftung baut. -- `model.erhoehen()` ruft die Methode dieses einen Zählers auf, genau wie `knopf.setText(…)`. -- Wer den Wert wissen will, fragt mit `getWert()`. Ändern geht nur über `erhoehen()`. - -Weil das Model kein Fenster braucht, kannst du es ganz ohne Fenster ausprobieren: - -```java -public class ZaehlerTest { - public static void main(String[] args) { - Zaehler model = new Zaehler(); - model.erhoehen(); - model.erhoehen(); - System.out.println(model.getWert()); - } -} - -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} -``` - -Die Ausgabe ist 2. Mehrere Klassen dürfen in einer Datei stehen. Nur eine davon ist `public`, und die enthält `main`. - -## Die View - -Die View zeigt den Wert an und macht sonst nichts. Sie rechnet nicht und weiß nicht, woher die Zahl kommt: - -```java -class ZahlAnzeige { - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - void aktualisieren(int wert) { - zahl.setText("" + wert); - } -} -``` - -- Die View bekommt den Wert mit `aktualisieren(int wert)` geliefert und schreibt ihn in ihr JLabel. -- Fenster und Knopf baust du wie immer in `main`. Mit `fenster.add(view.zahl)` kommt das JLabel der View ins Fenster. - -## Der Controller - -Der Controller ist der Zuhörer am Knopf. Er verbindet Model und View: - -```java -Zaehler model = new Zaehler(); -ZahlAnzeige view = new ZahlAnzeige(); - -plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); -}); -``` - -- Erst ändert er das Model, dann holt er den neuen Wert und gibt ihn der View. -- Das Problem aus Lektion 32 ist weg: Die Variable `model` ändert sich nie, sie zeigt immer auf denselben Zähler. Nur der `wert` darin ändert sich. Deshalb darf das Lambda `model` benutzen, ganz ohne `static`. - -## Wer redet mit wem? - -Bei jedem Klick laufen vier Nachrichten, immer in derselben Reihenfolge: - -::: grafik mvc-klick.svg -Die Pfeile leuchten der Reihe nach auf. Das Model zählt beim zweiten Pfeil hoch, die Anzeige erst beim vierten. -::: - -1. Der Knopf meldet den Klick an den Controller (`actionPerformed`). -2. Der Controller sagt dem Model: `erhoehen()`. -3. Der Controller fragt das Model nach dem neuen Wert: `getWert()`. -4. Der Controller gibt den Wert an die View: `aktualisieren(wert)`. - -Das Model redet mit niemandem. Es wartet, bis es gefragt wird. - -::: ludwig -Merk dir: Das Model weiß nichts von Fenstern. In `Zaehler` steht kein einziges `JLabel` und kein `import javax.swing`. Wenn du dort Swing brauchst, gehört der Code in die View. -::: - -::: quiz -? Mit wem redet das Model von sich aus? -- [ ] Mit der View -- [ ] Mit dem Knopf -- [x] Mit niemandem -> Das Model wartet, bis der Controller es fragt. -::: - -## Der Gewinn: eine zweite View - -Jetzt soll der Zählerstand zusätzlich als Balken erscheinen. Dafür kommt eine zweite View dazu. Ein `JProgressBar` ist ein Balken, der sich füllt, hier von 0 bis 10: - -```java -class BalkenAnzeige { - JProgressBar balken = new JProgressBar(0, 10); - - void aktualisieren(int wert) { - balken.setValue(wert); - } -} -``` - -Der Controller bekommt eine Zeile mehr. Das Model bleibt genau, wie es war: - -```java -plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); -}); -``` - -Genau das ist der Gewinn von MVC: Du kannst Anzeigen dazubauen oder austauschen, ohne das Model anzufassen. - -## Das ganze Programm - -```java -import javax.swing.*; -import java.awt.*; - -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} - -class ZahlAnzeige { - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - void aktualisieren(int wert) { - zahl.setText("" + wert); - } -} - -class BalkenAnzeige { - JProgressBar balken = new JProgressBar(0, 10); - - void aktualisieren(int wert) { - balken.setValue(wert); - } -} - -public class KlickZaehler { - public static void main(String[] args) { - Zaehler model = new Zaehler(); - ZahlAnzeige view = new ZahlAnzeige(); - BalkenAnzeige balken = new BalkenAnzeige(); - - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); - }); - - fenster.add(view.zahl); - fenster.add(balken.balken); - fenster.add(plus); - fenster.setVisible(true); - } -} -``` - -Klick ein paar Mal auf +1 und sieh zu, wie Zahl und Balken gemeinsam wachsen. - -::: angeberwissen -MVC hat sich der Norweger Trygve Reenskaug 1979 ausgedacht, im Forschungszentrum Xerox PARC in Kalifornien. Dort standen damals schon Computer mit Fenstern und Maus. Im selben Jahr besuchte Steve Jobs das Labor und war so begeistert, dass Apple danach Computer mit Fenstern und Maus baute. -::: - -## Spiel: Wohin gehört der Code? - -::: spiel frage -titel: Spiel: Wohin gehört der Code? -intro: Ludwig hat Code aus einem MVC-Programm auf einen Haufen geworfen. Sortier jedes Stück ein. Acht Runden. -frage: Wohin gehört dieser Code? -antworten: Model | View | Controller - -runde: - int wert = 0; -richtig: Model -> Der Zählerstand sind die Daten. Daten wohnen im Model. - -runde: - zahl.setText("" + wert); -richtig: View -> Etwas auf dem Bildschirm anzeigen ist die Aufgabe der View. - -runde: - plus.addActionListener(e -> model.erhoehen()); -richtig: Controller -> Auf einen Klick reagieren und dem Model Bescheid geben: Das macht der Controller. - -runde: - void erhoehen() { - wert++; - } -richtig: Model -> Das Model weiß selbst, wie es seine Daten ändert. - -runde: - balken.setValue(wert); -richtig: View -> Ein Balken, der sich füllt, zeigt nur an. Also View. - -runde: - void erhoehen() { - if (wert < 10) { - wert++; - } - } -richtig: Model -> „Höchstens 10“ ist eine Regel für die Daten. Regeln gehören ins Model, dann gelten sie für jeden Knopf und jede View. - -runde: - view.aktualisieren(model.getWert()); -richtig: Controller -> Den Wert beim Model abholen und der View geben: Das ist der Controller in der Mitte. - -runde: - JLabel zahl = new JLabel("0", SwingConstants.CENTER); -richtig: View -> Ein JLabel ist ein Baustein der Oberfläche. Im Model steht kein Swing. -::: - -## Spiel: Wer ist dran? - -::: spiel reihenfolge -titel: Spiel: Wer ist dran? -intro: Nach einem Klick reden Model, View und Controller miteinander, aber nur in einer Reihenfolge funktioniert es. Bring die Schritte in die richtige Reihenfolge. Fünf Runden. - -runde: -frage: Du klickst im Klickzähler auf +1. Was passiert der Reihe nach? -- Du klickst auf +1. -- Der Controller bekommt `actionPerformed`. -- Der Controller ruft `model.erhoehen()` auf. -- Das Model zählt `wert` hoch. -- Der Controller holt `model.getWert()`. -- Die View zeigt die neue Zahl. -> Klick, Controller, Model ändern, Wert holen, anzeigen. Das sind die vier Pfeile aus der Grafik, mit dem Hochzählen in der Mitte. - -runde: - plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); - }); -frage: Was passiert nach einem Klick auf +1 der Reihe nach? -- Der Klick auf +1 -- `model.erhoehen()` -- Die Zahl zeigt den neuen Wert. -- Der Balken wächst. -> Der Zuhörer arbeitet seine Zeilen von oben nach unten ab: erst das Model, dann die Zahl, dann der Balken. - -runde: -frage: Bring den Controller in die richtige Reihenfolge, damit die View die neue Zahl zeigt. -- `plus.addActionListener(e -> {` -- `model.erhoehen();` -- `view.aktualisieren(model.getWert());` -- `});` -> Erst ändern, dann anzeigen. Andersherum zeigt die View noch den alten Wert. - -runde: - reset.addActionListener(e -> { - model.zuruecksetzen(); - view.aktualisieren(model.getWert()); - }); -frage: Der Zähler steht auf 5. Du klickst auf Reset. Was passiert der Reihe nach? -- Du klickst auf Reset. -- Das Model setzt `wert` auf 0. -- `model.getWert()` liefert 0. -- Die View zeigt 0. -> Auch Reset läuft über den Controller ins Model. Die View erfährt den neuen Wert erst ganz am Schluss. - -runde: -frage: Ein Test ohne Fenster soll 1 ausgeben. In welcher Reihenfolge stehen die Zeilen in main? -- `Zaehler model = new Zaehler();` -- `model.erhoehen();` -- `System.out.println(model.getWert());` -> Erst das Objekt bauen, dann damit arbeiten, zum Schluss nachsehen. Das Model lässt sich ganz ohne Fenster testen. -::: - -## Aufgabe: Ein Zähler mit Grenzen - -Baue das ganze Programm und probier es aus. - -Dann mach es besser: - -- Ein Knopf **−1** zählt herunter. Das Model bekommt dafür eine Methode `verringern()`, der Controller einen zweiten Zuhörer. -- Ein Knopf **Reset** setzt den Zähler mit `zuruecksetzen()` auf 0. -- Das Model achtet selbst auf die Grenzen: nie unter 0 und nie über 10, denn dann ist der Balken voll. -- Extra: eine dritte View `KonsolenAnzeige`, deren `aktualisieren` mit `System.out.println` „Neuer Wert: 3“ ausgibt. Eine View muss kein Fenster sein. - -**Du bist fertig, wenn …** - -- **+1**, **−1** und **Reset** richtig zählen, -- der Zähler nie unter 0 und nie über 10 geht, egal wie oft du klickst, -- Zahl und Balken immer denselben Wert zeigen, -- in der Klasse des Model kein `JButton`, `JLabel` oder anderes Swing vorkommt. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann sagen, was Model, View und Controller jeweils tun. → Das Ziel -- Ich kann ein Model ohne Swing schreiben und ohne Fenster testen. → Das Model -- Ich kann erklären, was new Zaehler() baut und warum dort kein static steht. → Das Model -- Ich kann die vier Nachrichten nach einem Klick nennen. → Wer redet mit wem? -- Ich kann eine zweite View dazubauen, ohne das Model zu ändern. → Der Gewinn: eine zweite View -- Ich kann einen Zähler mit −1, Reset und Grenzen im Model bauen. → Aufgabe: Ein Zähler mit Grenzen -::: - -## Wissenstest - -::: quiz -? Wo steht die Regel „Der Zähler geht nie über 10“? -→ Das Model -- [x] im Model -- [ ] in der View -- [ ] im Controller -> Regeln über die Daten gehören ins Model. Dann gelten sie, egal welcher Knopf drückt. - -? Was macht die View? -→ Das Ziel -- [ ] Sie rechnet den neuen Wert aus. -- [x] Sie zeigt den Wert an, den sie bekommt. -- [ ] Sie hört auf Klicks. - -? Warum darf das Lambda model benutzen, obwohl model eine Variable von main ist? -→ Wer redet mit wem? -- [ ] Weil Zaehler public ist. -- [x] Weil model immer auf denselben Zähler zeigt; nur der wert darin ändert sich. -- [ ] Weil Lambdas alle Variablen ändern dürfen. - -? Was musst du am Model ändern, wenn eine zweite View dazukommt? -→ Der Gewinn: eine zweite View -- [x] nichts -- [ ] eine zweite Methode erhoehen -- [ ] ein zweites JLabel - -? Ordne zu. -→ Das Ziel -= Zaehler mit wert und erhoehen() -> Model -= ZahlAnzeige mit aktualisieren(int wert) -> View -= der Zuhörer am Knopf -> Controller -::: diff --git a/web/lessons/de/aufgaben/alter-unterprogramm.md b/web/lessons/de/aufgaben/alter-unterprogramm.md deleted file mode 100644 index 3d1d972e..00000000 --- a/web/lessons/de/aufgaben/alter-unterprogramm.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Alter im Unterprogramm -nach: 18 -stufe: 2 ---- - -Die Altersprüfung aus „Volljährig?“ wird jetzt ein eigenes Unterprogramm. Das Hauptprogramm ruft es nur noch auf. - -::: übung aufgabe-alter-unterprogramm -titel: Alter im Unterprogramm -auftrag: Baue zwei Diagramme. Das Hauptprogramm gibt **Willkommen** aus, ruft dann `pruefeAlter()` auf und gibt zum Schluss **Auf Wiedersehen** aus. - Das Unterprogramm `pruefeAlter` steht in einem eigenen Reiter: Es fragt nach dem Alter und gibt **volljährig** oder **minderjährig** aus. -test: 20 -> Willkommen | volljährig | Auf Wiedersehen -test: 18 -> Willkommen | volljährig | Auf Wiedersehen -test: 12 -> Willkommen | minderjährig | Auf Wiedersehen -braucht: aufruf -tipp: Das Hauptprogramm hat drei Kästen. Die eigentliche Arbeit macht das Unterprogramm. -tipp: Öffne die Übung im Editor und baue dort das Hauptprogramm mit dem Aufruf `pruefeAlter()`. Lege dann mit **+** einen neuen Reiter an und gib ihm den Titel `pruefeAlter`. Darin baust du die Eingabe und die Verzweigung. Prüfen findet das Unterprogramm über den Titel des Reiters. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/ballwurf.md b/web/lessons/de/aufgaben/ballwurf.md deleted file mode 100644 index f01c2f81..00000000 --- a/web/lessons/de/aufgaben/ballwurf.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -titel: Ballwurf auswerten -nach: 25 -stufe: 2 ---- - -Beim Sportfest haben alle ihre Weite beim Ballwurf in eine Tabelle eingetragen: - -::: datei ballwurf.csv -Name;Weite -Ludwig;28 -Zora;42 -Fuchs;35 -Hase;21 -::: - -::: übung aufgabe-ballwurf -titel: Ballwurf auswerten -code: java -auftrag: Lies die Datei ein und überspringe die Kopfzeile. Gib aus, wie viele mitgemacht haben (**Teilnehmer: 4**) und wie weit im Schnitt geworfen wurde (**Schnitt: 31.5 m**). - Speichere den weitesten Wurf in der Datei **sieger.txt**, zum Beispiel **Sieger: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Teilnehmer: 4 | Schnitt: 31.5 m -datei: sieger.txt -> Sieger: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: Du brauchst vier Variablen vor der Schleife: die Anzahl, die Summe als `double`, die beste Weite und den besten Namen. -tipp: In der Schleife: `String[] teile = zeile.split(";");` und `int weite = Integer.parseInt(teile[1]);`. Ist `weite > besteWeite`, merkst du dir Weite und Namen. Zum Speichern: `PrintWriter aus = new PrintWriter("sieger.txt");`, `aus.println(…)` und `aus.close();`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/bestenliste.md b/web/lessons/de/aufgaben/bestenliste.md deleted file mode 100644 index 83eda4c0..00000000 --- a/web/lessons/de/aufgaben/bestenliste.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -titel: Bestenliste -nach: 19 -stufe: 2 ---- - -Fünf Ziegen sind beim Hindernislauf gestartet. Jetzt soll die Bestenliste an die Stalltür: die meisten Punkte oben, die wenigsten unten. - -::: übung aufgabe-bestenliste -titel: Bestenliste -auftrag: Lies 5 Punktzahlen in ein Array ein. Sortiere das Array mit Bubblesort **absteigend**, also die größte Zahl zuerst. Gib danach die Liste mit Platz aus, zum Beispiel **1. 9**, in der nächsten Zeile **2. 9** und so weiter. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Bubblesort aus Lektion 19 sortiert aufsteigend. Für absteigend drehst du nur den Vergleich der Nachbarn um. -tipp: Getauscht wird, wenn `punkte[i] < punkte[i + 1]` ist, mit einer Hilfsvariablen. Für die Ausgabe rechnest du zuerst `int platz = i + 1` aus und gibst dann `platz + ". " + punkte[i]` aus. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-bestenliste-java -titel: Bestenliste in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Lies jede Zahl erst in eine Variable ein und schreibe sie dann ins Array: `int zahl = scanner.nextInt();` und `punkte[i] = zahl;`. -tipp: Der Tausch braucht auch in Java eine Hilfsvariable: `int hilf = punkte[i]; punkte[i] = punkte[i + 1]; punkte[i + 1] = hilf;`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/binaerzahl.md b/web/lessons/de/aufgaben/binaerzahl.md deleted file mode 100644 index d7cbbe1d..00000000 --- a/web/lessons/de/aufgaben/binaerzahl.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -titel: Binärzahl -nach: 11 -stufe: 3 ---- - -Computer rechnen mit nur zwei Ziffern: 0 und 1. So wandelst du eine Zahl ins **Binärsystem** um: - -1. Teile die Zahl durch 2 und merke dir den Rest (0 oder 1). -2. Rechne mit dem Ergebnis ohne Rest weiter. -3. Wiederhole das, bis die Zahl 0 ist. - -Die Reste von **unten nach oben** gelesen ergeben die Binärzahl. Bei 6 sind die Reste 0, 1, 1, also lautet die Binärzahl 110. - -::: übung aufgabe-binaer -titel: Binärzahl -auftrag: Frage nach einer ganzen Zahl größer als 0 und gib sie als Binärzahl aus. Bei 6 lautet die Ausgabe **110**, bei 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Teile die Zahl immer wieder durch 2 und merke dir jeden Rest. Die Reste rückwärts gelesen ergeben die Binärzahl. -tipp: Sammle die Ziffern in einem Text: `String binaer = ""`. In der Schleife berechnest du `int rest = zahl % 2` und hängst den Rest **vorne** an: `binaer = rest + binaer`. Danach `zahl = zahl / 2`. Weil zahl eine ganze Zahl ist, fällt der Rest dabei weg. -lösung: aufgabe-binaer-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-binaer-java -titel: Binärzahl in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Fang mit `String binaer = "";` an. Die Schleife läuft mit `while (zahl > 0)`. -tipp: Im Rumpf: `int rest = zahl % 2;`, dann `binaer = rest + binaer;` und `zahl = zahl / 2;`. Weil `binaer` ein String ist, hängt `+` den Rest vorne an. -lösung: aufgabe-binaer-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/caesar.md b/web/lessons/de/aufgaben/caesar.md deleted file mode 100644 index d1003f7a..00000000 --- a/web/lessons/de/aufgaben/caesar.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -titel: Caesar-Verschlüsselung -nach: 17 -stufe: 3 ---- - -Schon vor über 2000 Jahren schickte der römische Feldherr Julius Caesar geheime Nachrichten an seine Truppen. Sein Trick war einfach: Er ersetzte jeden Buchstaben durch den Buchstaben, der **drei Stellen weiter** im Alphabet steht. Aus A wird D, aus B wird E und so weiter. Die lesbare Nachricht heißt **Klartext**, die verschlüsselte **Geheimtext**. Um wie viele Stellen verschoben wird, ist der **Schlüssel**, oft kurz `k` genannt. - -::: grafik caesar-verschiebung.svg -Verschlüsselung mit dem Schlüssel 3: Jeder Buchstabe oben wird zum Buchstaben darunter. Am Ende geht es von vorn weiter, aus X, Y und Z werden A, B und C. -::: - -Aus `HALLO` wird so `KDOOR`. Wer den Schlüssel kennt, schiebt einfach drei Stellen zurück und kann die Nachricht wieder lesen. - -### Die Chiffrierscheibe - -Damit man nicht jedes Mal abzählen muss, baute man sich eine **Chiffrierscheibe**: zwei Scheiben mit dem Alphabet, eine kleine auf einer großen, in der Mitte drehbar befestigt. Außen steht der Klartext, innen der Geheimtext. Dreht man die innere Scheibe um drei Buchstaben, steht unter jedem Buchstaben außen sein Geheimbuchstabe. Solche Scheiben gibt es seit dem 15. Jahrhundert. Im Amerikanischen Bürgerkrieg (1861–1865) trugen Soldaten kleine Scheiben aus Messing bei sich. - -::: grafik caesar-scheibe.svg -Eine Chiffrierscheibe nach dem Vorbild historischer Messingscheiben. Das blaue Fenster zeigt: Mit dem Schlüssel 3 steht unter A das D. -::: - -::: angeberwissen -Caesar selbst hatte noch gar keine Scheibe! Das wissen wir vom römischen Geschichtsschreiber Sueton: Caesar verschob in seinen Briefen jeden Buchstaben um drei Stellen und zählte das einfach im Kopf ab. Die Scheibe erfand erst Leon Battista Alberti um 1467, also rund 1500 Jahre später. Ich zähle übrigens auch im Kopf, ich habe ja keine Finger, nur Hufe. -::: - -Probiere es selbst aus. Drehe die innere Scheibe, tippe einen Text ein und schau zu, wie der Geheimtext entsteht. Mit **Entschlüsseln** geht es rückwärts. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -Eine echte Scheibe kannst du ganz leicht selbst basteln: zwei Kreise aus Pappe, einer etwas kleiner, je 26 gleich große Felder mit dem Alphabet und in der Mitte eine Musterklammer. Fertig ist dein Geheimdienst-Werkzeug! -::: - -::: quiz -? Was wird mit dem Schlüssel 3 aus dem Buchstaben Y? -- [ ] A -- [x] B -- [ ] C -> Nach Y kommen Z, dann geht es von vorn los mit A und B. Drei Stellen weiter ist also B. - -? Ein Geheimtext wurde mit dem Schlüssel 5 verschlüsselt. Wie entschlüsselst du ihn? -- [ ] Noch einmal 5 Stellen weiter schieben -- [x] 5 Stellen zurück schieben -- [ ] Die Buchstaben rückwärts lesen -> Entschlüsseln ist das Gegenteil vom Verschlüsseln. Fünf Stellen zurück ist übrigens dasselbe wie 21 Stellen weiter, denn 5 + 21 = 26. -::: - -### Das Programm: Es gibt mehr als einen Weg - -Wie bringst du dem Computer bei, einen Buchstaben um `k` Stellen zu verschieben? Dafür gibt es nicht *die eine* richtige Lösung. Wir schauen uns zwei Wege an. Beide gehen den Text mit einer For-Schleife Zeichen für Zeichen durch, so wie in der Lektion Zeichenketten. Der Unterschied ist, wie sie einen einzelnen Buchstaben verschieben. - -In beiden Wegen hilft der Rest-Operator `%`. Er sorgt dafür, dass es nach Z wieder bei A weitergeht: Die Positionen 0 bis 25 bleiben, wie sie sind, aus 26 wird 0, aus 27 wird 1 und so weiter. - -### Weg 1: Das Alphabet in einem Array - -Du legst das Alphabet in ein Array `char[] alphabet = {'A', 'B', …, 'Z'}`. Für jeden Buchstaben des Wortes suchst du im Array seine Position `pos`, also seinen Index. Der Geheimbuchstabe steht dann im selben Array an der Stelle `(pos + k) % 26`. - -Ein Beispiel mit dem Schlüssel 3: X steht an der Position 23. Dann ist `(23 + 3) % 26 = 0`, und an der Position 0 steht A. - -::: übung aufgabe-caesar-array -titel: Caesar mit Array -auftrag: Das Startdiagramm enthält schon das Array `alphabet`. Frage nach einem Wort aus Großbuchstaben und nach dem Schlüssel `k`. Gib dann das verschlüsselte Wort aus. Bei HALLO und 3 also `KDOOR`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Lege einen leeren Text `String geheim = ""` an. Gehe das Wort mit einer For-Schleife Zeichen für Zeichen durch. In jedem Durchlauf hängst du einen Geheimbuchstaben hinten an `geheim` an. -tipp: Um die Position eines Buchstabens zu finden, brauchst du eine zweite, innere Schleife über das Array: Ist `alphabet[j] == zeichen`, merkst du dir `pos = j`. -tipp: Nach der inneren Schleife hängst du `alphabet[(pos + k) % 26]` an: `geheim = geheim + alphabet[(pos + k) % 26]`. Nach der äußeren Schleife gibst du `geheim` aus. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Weg 2: Rechnen mit der ASCII-Tabelle - -Für den Computer ist jedes Zeichen eigentlich eine Zahl. Welche Zahl zu welchem Zeichen gehört, steht in der **ASCII-Tabelle**. Dort hat A den Code 65, B den Code 66 und so weiter bis Z mit 90. In Java wandelst du ein Zeichen mit `(int)` in seinen Code um und einen Code mit `(char)` zurück in ein Zeichen: - -``` -int code = (int) 'A' -char zeichen = (char) 68 -``` - -Danach ist `code` gleich 65 und `zeichen` gleich `'D'`. So eine Umwandlung von einem Datentyp in einen anderen heißt **Typumwandlung** (englisch *cast*). - -Jetzt kannst du mit dem Buchstaben rechnen. Damit der Sprung von Z zurück zu A klappt, ziehst du erst 65 ab. Dann hat A die Position 0 und Z die Position 25, genau wie in Weg 1. Du verschiebst mit `% 26` und zählst am Ende die 65 wieder dazu: - -::: grafik caesar-ascii.svg -Oben die Großbuchstaben mit ihren ASCII-Codes. Unten der Rechenweg für X mit dem Schlüssel 3: Heraus kommt A. -::: - -::: übung aufgabe-caesar-ascii -titel: Caesar mit ASCII -code: java -auftrag: Schreibe ein Java-Programm, das ein Wort aus Großbuchstaben und den Schlüssel `k` einliest und das verschlüsselte Wort ausgibt. Diesmal ohne Array: Rechne mit `(int)` und `(char)`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Gehe das Wort mit einer For-Schleife durch. Den Code des Zeichens an der Stelle `i` bekommst du mit `int code = (int) wort.charAt(i);`. -tipp: Rechne `code = (code - 65 + k) % 26 + 65;` und hänge das Zeichen an: `geheim = geheim + (char) code;`. Vor der Schleife steht `String geheim = "";`, nach der Schleife `System.out.println(geheim);`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Welcher Weg ist besser? - -**Weg 1: Array.** Die Idee ist, die Position im Array zu suchen. -- Vorteil: leicht zu verstehen, und auch eigene Alphabete gehen, z. B. mit Ä, Ö und Ü. -- Nachteil: Für jeden Buchstaben muss gesucht werden. - -**Weg 2: ASCII.** Die Idee ist, mit dem Zeichencode zu rechnen. -- Vorteil: kurz und ohne Suche, darum schneller. -- Nachteil: Man muss die ASCII-Codes kennen. - -Beide Programme liefern dasselbe Ergebnis. Genau so ist es oft beim Programmieren: Es gibt mehrere richtige Lösungen, und du wählst die, die am besten zu deiner Aufgabe passt. - -::: übung aufgabe-caesar-entschluesseln -titel: Caesar entschlüsseln -code: java -auftrag: Jetzt drehst du den Spieß um. Lies einen Geheimtext aus Großbuchstaben und den Schlüssel `k` ein und gib den Klartext aus. Bei KDOOR und 3 also `HALLO`. Welchen Weg du nimmst, entscheidest du. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Entschlüsseln heißt: `k` Stellen zurück statt vor. Aus dem `+ k` wird also ein `- k`. -tipp: Vorsicht bei A: `0 - 3` ergibt -3, und `%` macht daraus in Java keine positive Zahl. Zähle darum noch 26 dazu: `code = (code - 65 - k + 26) % 26 + 65;` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Sicher ist Caesar leider nicht. Es gibt nur 25 Schlüssel, die kann man in ein paar Minuten alle ausprobieren. Noch schneller geht es mit einem Trick: Im Deutschen ist E der häufigste Buchstabe. Kommt im Geheimtext H am häufigsten vor, war der Schlüssel wohl 3. Im Internet gibt es übrigens ROT13, das ist Caesar mit dem Schlüssel 13. Zweimal ROT13 ergibt wieder den Klartext, weil 13 + 13 = 26. -::: diff --git a/web/lessons/de/aufgaben/collatz.md b/web/lessons/de/aufgaben/collatz.md deleted file mode 100644 index 0f52054f..00000000 --- a/web/lessons/de/aufgaben/collatz.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -titel: Collatz-Folge -nach: 11 -stufe: 2 ---- - -Die Collatz-Folge ist ein berühmtes Rätsel der Mathematik. Du beginnst mit einer Zahl und wendest immer wieder dieselbe Regel an: - -- Ist die Zahl **gerade**, teilst du sie durch 2. -- Ist sie **ungerade**, rechnest du mal 3 plus 1. - -Bisher endet die Folge bei jeder ausprobierten Startzahl irgendwann bei 1. Ob das für alle Zahlen gilt, weiß bis heute niemand. - -::: übung aufgabe-collatz -titel: Collatz-Folge -auftrag: Frage nach einer Startzahl. Wende die Regel so lange an, bis die Zahl 1 ist, und gib nach jedem Schritt die neue Zahl aus. Zähle dabei die Schritte mit und gib am Ende **Schritte:** und die Anzahl aus. - Bei der Startzahl 6 lautet die Folge 3, 10, 5, 16, 8, 4, 2, 1. Das sind 8 Schritte. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 7 -test: 1 -> Schritte: 0 -braucht: whileSchleife, verzweigung -tipp: Du weißt nicht, wie viele Schritte es werden. Darum passt eine While-Schleife besser als eine For-Schleife. -tipp: Die While-Schleife läuft, solange `zahl != 1` gilt. Ob eine Zahl gerade ist, prüfst du mit `zahl % 2 == 0`. Die Ausgabe und `schritte++` kommen nach der Verzweigung, aber noch in die Schleife. -lösung: aufgabe-collatz-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-collatz-java -titel: Collatz-Folge in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 7 -test: 1 -> Schritte: 0 -braucht: whileSchleife, verzweigung -tipp: Eine `while`-Schleife läuft, solange `zahl != 1` ist. Darin entscheidet ein `if` mit `zahl % 2 == 0`. -tipp: Im Rumpf: halbieren oder `zahl = zahl * 3 + 1;`, dann `System.out.println(zahl);` und `schritte++;`. Nach der Schleife kommt `"Schritte: " + schritte`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -Mit dieser Aufgabe kann man reich werden! Der Mathematiker Paul Erdős setzte für einen Beweis 500 Dollar aus. 2021 legte die japanische Firma Bakuage noch einiges drauf: 120 Millionen Yen, damals umgerechnet rund eine Million Dollar, für einen Beweis oder ein Gegenbeispiel. Abgeholt hat das Geld bis heute niemand. Erfunden hat das Rätsel übrigens der deutsche Mathematiker Lothar Collatz im Jahr 1937. Ich rechne gleich mal los, für so viel Geld gibt es eine Menge Heu. -::: diff --git a/web/lessons/de/aufgaben/einheitenrechner.md b/web/lessons/de/aufgaben/einheitenrechner.md deleted file mode 100644 index fc8b829a..00000000 --- a/web/lessons/de/aufgaben/einheitenrechner.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -titel: Einheitenrechner -nach: 35 -stufe: 2 ---- - -Baue ein Fenster, das Längen umrechnet. Oben tippst du eine Länge in Metern ein, darunter stehen drei Knöpfe: - -- **in cm** zeigt die Länge in Zentimetern, aus 1.5 wird „150.0 cm“, -- **in mm** zeigt sie in Millimetern, aus 1.5 wird „1500.0 mm“, -- **in km** zeigt sie in Kilometern, aus 1500 wird „1.5 km“. - -Das Ergebnis steht in einem JLabel unter den Knöpfen. Steht im Feld keine Zahl, erscheint dort „Bitte eine Zahl eingeben“, und das Programm stürzt nicht ab. - -So kannst du anfangen: - -```java -import javax.swing.*; -import java.awt.*; - -public class Einheiten { - public static void main(String[] args) { - JFrame fenster = new JFrame("Einheitenrechner"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JTextField meter = new JTextField(); - JButton inCm = new JButton("in cm"); - JLabel ergebnis = new JLabel("?", SwingConstants.CENTER); - - inCm.addActionListener(e -> { - double m = Double.parseDouble(meter.getText()); - ergebnis.setText((m * 100) + " cm"); - }); - - fenster.add(meter); - fenster.add(inCm); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -``` - -**Du bist fertig, wenn …** - -- 1.5 nach **in cm** „150.0 cm“ zeigt, nach **in mm** „1500.0 mm“, -- 1500 nach **in km** „1.5 km“ zeigt, -- „abc“ im Feld „Bitte eine Zahl eingeben“ zeigt und das Fenster offen bleibt. - -Zusatz: Ein vierter Knopf **in Fuß** rechnet in die englische Einheit um. Ein Fuß sind 0.3048 m. - -::: ludwig -Tick im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** an. Für „abc“ im Feld brauchst du in jedem Zuhörer ein try/catch mit `NumberFormatException`, wie beim Taschenrechner. -::: diff --git a/web/lessons/de/aufgaben/fibonacci.md b/web/lessons/de/aufgaben/fibonacci.md deleted file mode 100644 index 795b1487..00000000 --- a/web/lessons/de/aufgaben/fibonacci.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -titel: Fibonacci-Zahlen -nach: 13 -stufe: 2 ---- - -Bei den Fibonacci-Zahlen ist jede Zahl die Summe der beiden Zahlen davor. Die Folge beginnt mit 1 und 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -Diese Zahlen findest du erstaunlich oft in der Natur, zum Beispiel bei der Anordnung von Sonnenblumenkernen. - -::: ludwig -Erinnerst du dich ans **Tauschen** aus Lektion 5? Dort brauchtest du eine Hilfsvariable, weil `a = b` den alten Wert von `a` überschreibt. Hier ist es genauso: Bevor `zahl2` die neue Zahl bekommt, muss ihr alter Wert in `zahl1` gerettet sein. Achte also auf die **Reihenfolge**: erst `zahl1 = zahl2`, dann `zahl2 = zahl3`. Dieses Weiterschieben und Tauschen brauchst du später wieder, zum Beispiel beim Sortieren in Lektion 19. -::: - -::: angeberwissen -Leonardo von Pisa, genannt Fibonacci, stellte 1202 in seinem Rechenbuch eine Aufgabe über Kaninchen: Jedes Kaninchenpaar bekommt ab dem zweiten Monat jeden Monat ein neues Paar. Wie viele Paare gibt es nach einem Jahr? Die Antwort ist diese Zahlenfolge. Ziegen kamen in dem Buch leider nicht vor. -::: - -::: übung aufgabe-fibonacci -titel: Fibonacci-Zahlen -auftrag: Frage, wie viele Fibonacci-Zahlen ausgegeben werden sollen. Gib dann genau so viele aus, jede in einer eigenen Zeile. Du kannst davon ausgehen, dass mindestens 2 eingegeben wird. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Jede neue Zahl ist die Summe der beiden vorherigen. Du musst dir also immer die letzten zwei Zahlen merken. -tipp: Merke dir die beiden letzten Zahlen in `zahl1` und `zahl2` und gib beide vor der Schleife aus. Die Schleife beginnt bei `int i = 2`. Darin berechnest du `int zahl3 = zahl1 + zahl2`, gibst sie aus und schiebst dann weiter: `zahl1 = zahl2` und `zahl2 = zahl3`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-fibonacci-java -titel: Fibonacci-Zahlen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Die ersten beiden Zahlen gibst du vor der Schleife aus. Die `for`-Schleife beginnt dann bei `int i = 2`. -tipp: Im Rumpf: `int zahl3 = zahl1 + zahl2;`, ausgeben, dann `zahl1 = zahl2;` und `zahl2 = zahl3;`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Warum muss in der Schleife `zahl1 = zahl2` vor `zahl2 = zahl3` stehen? -- [ ] Weil Java die Zuweisungen sonst nicht erlaubt -- [x] Weil sonst der alte Wert von zahl2 überschrieben ist, bevor zahl1 ihn bekommt -- [ ] Die Reihenfolge ist egal -> Andersherum hätten zahl1 und zahl2 danach denselben Wert. Wie beim Tauschen gilt: Erst retten, dann überschreiben. -::: diff --git a/web/lessons/de/aufgaben/gerade-ungerade.md b/web/lessons/de/aufgaben/gerade-ungerade.md deleted file mode 100644 index 7e40ff2a..00000000 --- a/web/lessons/de/aufgaben/gerade-ungerade.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Gerade oder ungerade -nach: 8 -stufe: 1 ---- - -Eine Zahl ist gerade, wenn beim Teilen durch 2 kein Rest bleibt. Den Rest berechnest du mit `%`: `8 % 2` ist 0, `7 % 2` ist 1. - -::: übung aufgabe-gerade -titel: Gerade oder ungerade -auftrag: Frage nach einer ganzen Zahl. Gib aus, ob sie gerade oder ungerade ist. Bei 4 lautet die Ausgabe **4 ist gerade**, bei 7 **7 ist ungerade**. -test: 4 -> 4 ist gerade -test: 7 -> 7 ist ungerade -test: 0 -> 0 ist gerade -test: 15 -> 15 ist ungerade -braucht: verzweigung -tipp: Eine Verzweigung entscheidet zwischen gerade und ungerade. Gerade heißt: durch 2 teilbar ohne Rest. -tipp: Die Bedingung lautet `zahl % 2 == 0`. In der Ausgabe verbindest du die Zahl und den Text mit `+`, z. B. `zahl + " ist gerade"`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-gerade-java -titel: Gerade oder ungerade in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 4 -> 4 ist gerade -test: 7 -> 7 ist ungerade -test: 0 -> 0 ist gerade -test: 15 -> 15 ist ungerade -braucht: verzweigung -tipp: Eine Zahl liest du mit `int zahl = scanner.nextInt();` ein. Vorher fragst du mit `System.out.println(…)`. -tipp: `if (zahl % 2 == 0) { … } else { … }`. Die Ausgabe ist `System.out.println(zahl + " ist gerade");`. -lösung: aufgabe-gerade-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/mehrwertsteuer.md b/web/lessons/de/aufgaben/mehrwertsteuer.md deleted file mode 100644 index 7682f7a4..00000000 --- a/web/lessons/de/aufgaben/mehrwertsteuer.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -titel: Mehrwertsteuer -nach: 6 -stufe: 1 ---- - -Auf fast alles, was du kaufst, kommen 19 % **Mehrwertsteuer**. Der Laden rechnet mit dem Nettopreis (ohne Steuer) und schlägt die Steuer drauf. So entsteht der Bruttopreis, den du an der Kasse zahlst. - -Ein Beispiel: Du kaufst ein Falt-Handy, das aufgeklappt zum Tablet wird. Auf dem Preisschild des Händlers stehen 1000 € netto. Dazu kommen 19 %, also 190 €. An der Kasse zahlst du 1190 €. Die 190 € darf der Händler nicht behalten: Er führt sie an das Finanzamt ab. - -::: grafik aufgabe-mehrwertsteuer.svg -Nettopreis plus Mehrwertsteuer ergibt den Bruttopreis. Die Mehrwertsteuer geht an das Finanzamt. -::: - -::: angeberwissen -Mäh, der Händler hat das Handy ja selbst eingekauft: beim Hersteller für 800 € plus 152 € Mehrwertsteuer. Diese 152 € heißen **Vorsteuer**, und die darf er von den 190 € abziehen. Ans Finanzamt überweist er also nur 38 €. Die ganze Steuer zahlt am Ende nur einer: der Kunde an der Kasse. Also du. Ich zahle übrigens gar keine, ich fresse nur Gras. -::: - -Der Steuersatz ändert sich beim Rechnen nie. Darum legst du ihn als **Konstante** an: Das ist eine Variable, deren Wert nach dem Festlegen gleich bleibt. Du schreibst `const` davor, zum Beispiel `const double MWST = 0.19`. Namen von Konstanten schreibt man in Großbuchstaben, dann erkennt man sie sofort. Im Java-Code wird daraus `final double MWST = 0.19;`. - -Wenn sich der Steuersatz einmal ändert, musst du nur diese eine Zeile anpassen. - -::: übung aufgabe-mehrwertsteuer -titel: Mehrwertsteuer -auftrag: Lege die Konstante `MWST` mit dem Wert 0.19 an. Frage dann nach dem Nettopreis. Gib zuerst die Steuer und danach den Bruttopreis aus, jeweils nur die Zahl. - Bei einem Nettopreis von 100 lauten die Ausgaben **19** und **119**. -test: 100 -> 19 | 119 -test: 50 -> 9.5 | 59.5 -test: 0 -> 0 | 0 -tipp: Die Steuer ist 19 Prozent vom Nettopreis. Der Bruttopreis ist das, was man am Ende bezahlt. -tipp: Die Steuer ist `nettopreis * MWST`. Den Bruttopreis bekommst du, wenn du die Steuer zum Nettopreis addierst. Beide Werte sind Kommazahlen, also `double`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Warum legst du den Steuersatz als Konstante an? -- [ ] Weil Konstanten schneller gerechnet werden -- [x] Weil sich sein Wert im Programm nicht ändern soll -- [ ] Weil man Kommazahlen nur als Konstante speichern kann -> Eine Konstante behält ihren Wert. Ändert sich der Steuersatz einmal, passt du nur die eine Zeile mit `const` an. -::: diff --git a/web/lessons/de/aufgaben/notendurchschnitt.md b/web/lessons/de/aufgaben/notendurchschnitt.md deleted file mode 100644 index 5de7647a..00000000 --- a/web/lessons/de/aufgaben/notendurchschnitt.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Notendurchschnitt -nach: 13 -stufe: 2 ---- - -Wie gut war die letzte Zeit? Der Durchschnitt ist die Summe aller Noten geteilt durch ihre Anzahl. - -::: übung aufgabe-durchschnitt -titel: Notendurchschnitt -auftrag: Frage zuerst, wie viele Noten eingegeben werden. Lies dann genau so viele Noten ein und gib den Durchschnitt aus, nur die Zahl. - Bei den Noten 1 und 2 ist der Durchschnitt **1.5**. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: Du brauchst die Summe aller Noten und ihre Anzahl. Der Durchschnitt ist die Summe geteilt durch die Anzahl. -tipp: Die For-Schleife läuft von `int i = 0` bis `i < anzahl`. Darin liest du eine Note ein und zählst sie zur Summe. Den Durchschnitt speicherst du als Kommazahl: `double schnitt = summe * 1.0 / anzahl`. Das `* 1.0` sorgt dafür, dass auch in Java nicht ganzzahlig geteilt wird. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-durchschnitt-java -titel: Notendurchschnitt in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: `for (int i = 0; i < anzahl; i++)` liest genau so viele Noten ein, wie vorher angegeben. -tipp: Teile mit `double schnitt = summe * 1.0 / anzahl;`. Ohne `* 1.0` teilt Java zwei `int` ganzzahlig, und aus 1.5 wird 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/pin-pruefen.md b/web/lessons/de/aufgaben/pin-pruefen.md deleted file mode 100644 index 9b70098e..00000000 --- a/web/lessons/de/aufgaben/pin-pruefen.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -titel: PIN prüfen -nach: 12 -stufe: 2 ---- - -Am Geldautomaten hast du drei Versuche für deine PIN. Gibst du dreimal eine falsche PIN ein, wird die Karte gesperrt. - -::: angeberwissen -John Shepherd-Barron, der 1967 den ersten Geldautomaten in London aufstellte, wollte eigentlich sechsstellige PINs. Seine Frau sagte aber, sie könne sich höchstens vier Ziffern merken. Deshalb hat deine PIN heute vier Stellen. -::: - -::: übung aufgabe-pin -titel: PIN prüfen -auftrag: Die richtige PIN ist 1234. Frage so lange nach der PIN, bis sie richtig ist oder drei Versuche verbraucht sind. Gib danach **PIN richtig** oder **Karte gesperrt** aus. -test: 1234 -> PIN richtig -test: 1111, 1234 -> PIN richtig -test: 1, 2, 1234 -> PIN richtig -test: 1, 2, 3 -> Karte gesperrt -braucht: doUntilSchleife, verzweigung -tipp: Die Schleife endet in zwei Fällen: wenn die PIN stimmt oder wenn drei Versuche verbraucht sind. Zähle die Versuche mit. -tipp: Die PIN wird mindestens einmal abgefragt, deshalb passt eine Do-while-Schleife. Lege vorher `int pin = 0` und `int versuche = 0` an. Die Schleife wiederholt sich, solange `pin != 1234 && versuche < 3` gilt. Nach der Schleife entscheidet eine Verzweigung, was ausgegeben wird. -lösung: aufgabe-pin-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-pin-java -titel: PIN prüfen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 1234 -> PIN richtig -test: 1111, 1234 -> PIN richtig -test: 1, 2, 1234 -> PIN richtig -test: 1, 2, 3 -> Karte gesperrt -braucht: doUntilSchleife, verzweigung -tipp: `do { … } while (…);` fragt mindestens einmal. Hinter `while (…)` steht ein Semikolon. -tipp: Lege vorher `int pin = 0;` und `int versuche = 0;` an. In der Schleife: `int eingabe = scanner.nextInt();`, `pin = eingabe;` und `versuche++;`. Die Bedingung ist `pin != 1234 && versuche < 3`. -lösung: aufgabe-pin-java-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/schiffe-versenken.md b/web/lessons/de/aufgaben/schiffe-versenken.md deleted file mode 100644 index 5209b37b..00000000 --- a/web/lessons/de/aufgaben/schiffe-versenken.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Schiffe versenken -nach: 16 -stufe: 2 ---- - -Beim Spiel Schiffe versenken ist das Meer ein Gitter. Die Spalten heißen A bis E, die Reihen 1 bis 5. Im Startdiagramm steht das Meer als 2D-Array `meer`: `true` heißt, auf diesem Feld liegt ein Teil eines Schiffs. - -::: grafik schiffe-versenken.svg -Grau heißt `true`: Hier liegt ein Schiff. Die kleinen grauen Zahlen sind die Indizes im Array, sie beginnen bei 0. Im Spiel heißen die Felder dagegen A bis E und 1 bis 5. -::: - -::: übung aufgabe-schiffe -titel: Schiffe versenken -auftrag: Gib alle Felder aus, auf denen ein Schiff liegt, jedes Feld in einer eigenen Zeile, z. B. **B1**. Gehe dafür mit zwei verschachtelten Schleifen Reihe für Reihe durch das Meer. - Für das Startdiagramm erscheinen **B1**, **C1**, **D1**, **E3**, **E4** und **A5**. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Gehe das Meer wie den Kinosaal mit zwei verschachtelten Schleifen durch. Gib nur die Felder aus, auf denen `true` steht. -tipp: Die äußere Schleife zählt `reihe`, die innere `spalte`, beide ab 0. Liegt auf `meer[reihe][spalte]` ein Schiff, gibst du den Buchstaben aus `spalten[spalte]` und die Reihennummer aus: `output: spalten[spalte] + (reihe + 1)`. Das `+ 1` braucht es, weil das Spiel bei 1 zu zählen beginnt. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-schiffe-java -titel: Schiffe versenken in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Zwei `for`-Schleifen ineinander: außen die Reihe, innen die Spalte, beide bis `.length`. -tipp: `if (meer[reihe][spalte]) { System.out.println(spalten[spalte] + (reihe + 1)); }`. Die Klammer um `reihe + 1` sorgt dafür, dass Java erst rechnet. -lösung: aufgabe-schiffe-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/speisekarte.md b/web/lessons/de/aufgaben/speisekarte.md deleted file mode 100644 index 2139f210..00000000 --- a/web/lessons/de/aufgaben/speisekarte.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Speisekarte -nach: 10 -stufe: 1 ---- - -In der Mensa bestellst du über eine Nummer. Das Programm sagt dir, welches Gericht zu der Nummer gehört. - -::: übung aufgabe-speisekarte -titel: Speisekarte -auftrag: Frage nach einer Nummer. Gib bei 1 **Griechischer Salat**, bei 2 **Reis-Bowl** und bei 3 **Falafel-Wrap** aus. Bei jeder anderen Nummer gib **Bitte eine gültige Nummer wählen** aus. -test: 1 -> Griechischer Salat -test: 2 -> Reis-Bowl -test: 3 -> Falafel-Wrap -test: 7 -> Bitte eine gültige Nummer wählen -braucht: fallauswahl -tipp: Für mehrere feste Nummern passt eine Fallauswahl. Was passiert mit allen anderen Nummern? -tipp: Lies die Nummer mit `input: int nummer "Welches Gericht?"` ein. Die Fallauswahl prüft `nummer`, die Fälle heißen `1`, `2` und `3`. Alle anderen Nummern landen im Fall `default`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-speisekarte-java -titel: Speisekarte in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 1 -> Griechischer Salat -test: 2 -> Reis-Bowl -test: 3 -> Falafel-Wrap -test: 7 -> Bitte eine gültige Nummer wählen -braucht: fallauswahl -tipp: In Java ist das eine Fallauswahl mit `switch (nummer) { … }`. -tipp: Jeder Fall sieht so aus: `case 1 -> { System.out.println("…"); }`. Für alle anderen Nummern gibt es `default -> { … }`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/summe-bilden.md b/web/lessons/de/aufgaben/summe-bilden.md deleted file mode 100644 index 56fee122..00000000 --- a/web/lessons/de/aufgaben/summe-bilden.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Summe bilden -nach: 2 -stufe: 1 ---- - -Lege zwei Zahlen fest, addiere sie und gib das Ergebnis aus. - -::: übung aufgabe-summe -titel: Summe bilden -auftrag: Lege `zahl1` mit dem Wert 4 und `zahl2` mit dem Wert 3 fest. Berechne die Summe in einer dritten Variablen und gib nur die Summe aus. -test: - -> 7 -tipp: Du brauchst drei Variablen: zwei für die Zahlen und eine für das Ergebnis. -tipp: Eine Variable legst du mit einer Anweisung wie `int zahl1 = 4` fest. Für die Ausgabe schreibst du den Namen der Variablen ohne Anführungszeichen. -lösung: aufgabe-summe-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/tafeldienst.md b/web/lessons/de/aufgaben/tafeldienst.md deleted file mode 100644 index a63ed175..00000000 --- a/web/lessons/de/aufgaben/tafeldienst.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Tafeldienst auslosen -nach: 15 -stufe: 2 ---- - -Wer hat diese Woche Tafeldienst? Fünf Namen kommen in ein Array, dann wird eine Losnummer gezogen. - -::: übung aufgabe-tafeldienst -titel: Tafeldienst auslosen -auftrag: Lies fünf Namen in ein String-Array ein. Frage danach nach einer Losnummer von 1 bis 5 und gib **Tafeldienst:** und den passenden Namen aus. - Die Losnummer 1 gehört zum ersten Namen, die 5 zum letzten. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Tafeldienst: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Tafeldienst: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Tafeldienst: Pia -braucht: forSchleife -tipp: Erst liest eine Schleife die fünf Namen ein. Danach brauchst du keine Schleife mehr, nur noch einen Zugriff auf ein Fach. -tipp: Mit `String[] namen = new String[5]` legst du das Array an. Achtung: Die Losnummer beginnt bei 1, der Index bei 0. Der Name steht also in `namen[nummer - 1]`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-tafeldienst-java -titel: Tafeldienst auslosen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Tafeldienst: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Tafeldienst: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Tafeldienst: Pia -braucht: forSchleife -tipp: Ein Array für fünf Namen legst du mit `String[] namen = new String[5];` an. -tipp: In der Schleife: `namen[i] = scanner.next();`. Die Losnummer 1 gehört zum Fach 0, darum gibst du `namen[nummer - 1]` aus. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> Mit `int nummer = (int) (Math.random() * 5) + 1` kannst du die Losnummer auch vom Zufall ziehen lassen. Für die automatische Prüfung brauchen wir aber eine feste Eingabe. diff --git a/web/lessons/de/aufgaben/taschenrechner.md b/web/lessons/de/aufgaben/taschenrechner.md deleted file mode 100644 index 9777a910..00000000 --- a/web/lessons/de/aufgaben/taschenrechner.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Taschenrechner -nach: 10 -stufe: 2 ---- - -Ein kleiner Taschenrechner: zwei Zahlen und ein Rechenzeichen eingeben, das Ergebnis erscheint. - -::: übung aufgabe-rechner -titel: Taschenrechner -auftrag: Frage nach zwei ganzen Zahlen und danach nach einem Rechenzeichen als Text: `+`, `-` oder `*`. Gib das Ergebnis aus. Bei einem anderen Zeichen gib **Unbekanntes Rechenzeichen** aus. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Lies alle drei Werte ein. Eine Fallauswahl über das Rechenzeichen entscheidet dann, was gerechnet wird. -tipp: Das Rechenzeichen liest du mit `input: String zeichen "Rechenzeichen"` ein. Die Fallauswahl prüft `zeichen`, die Fälle heißen `"+"`, `"-"` und `"*"`, der letzte Fall ist `default`. Rechne in jedem Fall erst in einer Variablen und gib sie dann aus, sonst hängt `+` die Zahlen nur aneinander. -lösung: aufgabe-rechner-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-rechner-java -titel: Taschenrechner in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Das Rechenzeichen ist ein Text. Du liest es mit `String zeichen = scanner.next();` ein. -tipp: `switch (zeichen) { case "+" -> { … } … default -> { … } }`. Bei Texten stehen die Fälle in Anführungszeichen. -lösung: aufgabe-rechner-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/verleih-menue.md b/web/lessons/de/aufgaben/verleih-menue.md deleted file mode 100644 index 3f437eba..00000000 --- a/web/lessons/de/aufgaben/verleih-menue.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Verleih-Menü -nach: 18 -stufe: 2 ---- - -Am Verleih-Automaten kannst du nacheinander mehrere Fahrzeuge buchen. Das Menü erscheint immer wieder, bis du **0** für Ende wählst. - -::: übung aufgabe-verleih -titel: Verleih-Menü -auftrag: Frage immer wieder nach einer Auswahl: 1 = E-Bike, 2 = E-Scooter, 0 = Ende. Gib bei 1 **E-Bike gebucht** und bei 2 **E-Scooter gebucht** aus, bei jeder anderen Zahl **Ungültige Auswahl**. - Bei 0 gibt das Programm **Tschüss** aus und verlässt die Schleife mit einem Aussprung. -test: 0 -> Tschüss -test: 1, 2, 0 -> E-Bike gebucht | E-Scooter gebucht | Tschüss -test: 2, 5, 1, 0 -> E-Scooter gebucht | Ungültige Auswahl | E-Bike gebucht | Tschüss -braucht: endlosschleife, aussprung -tipp: Das Menü läuft immer wieder, bis jemand 0 wählt. Dafür eignet sich eine Endlosschleife mit einem Aussprung. -tipp: Nimm eine Endlosschleife. Darin steht zuerst die Eingabe `input: int wahl "1 = E-Bike, 2 = E-Scooter, 0 = Ende"`, danach eine Fallauswahl über `wahl` mit den Fällen `0`, `1`, `2` und `default`. Im Fall `0` stehen die Ausgabe und darunter der Aussprung. -lösung: aufgabe-verleih-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-verleih-java -titel: Verleih-Menü in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 0 -> Tschüss -test: 1, 2, 0 -> E-Bike gebucht | E-Scooter gebucht | Tschüss -test: 2, 5, 1, 0 -> E-Scooter gebucht | Ungültige Auswahl | E-Bike gebucht | Tschüss -braucht: endlosschleife, aussprung -tipp: `while (true) { … }` läuft endlos, bis ein `break;` kommt. -tipp: Nimm in Java hier `if`/`else if` statt `switch`: Ein `break;` in einem `switch` verlässt nur den `switch`, nicht die Schleife. `if (wahl == 0) { System.out.println("Tschüss"); break; } else if (wahl == 1) { … }`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/vokabeltrainer.md b/web/lessons/de/aufgaben/vokabeltrainer.md deleted file mode 100644 index 187b0bf3..00000000 --- a/web/lessons/de/aufgaben/vokabeltrainer.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Vokabeltrainer -nach: 27 -stufe: 3 ---- - -Ludwig lernt Englisch. Seine Vokabeln stehen in einer CSV-Datei, links Deutsch, rechts Englisch: - -::: datei vokabeln.csv -Deutsch;Englisch -Ziege;goat -Heu;hay -Wiese;meadow -::: - -Weil nur die Datei die Wörter kennt, kannst du später einfach neue Zeilen anhängen, ohne das Programm zu ändern. - -::: angeberwissen -Die Lernkartei mit mehreren Fächern hat Sebastian Leitner 1972 bekannt gemacht: Gewusste Karten wandern ein Fach weiter und kommen seltener dran, falsche wandern zurück ins erste Fach. Viele Vokabel-Apps arbeiten bis heute nach diesem Prinzip. -::: - -::: übung aufgabe-vokabeln -titel: Vokabeltrainer -code: java -auftrag: Frage jede Vokabel aus der Datei ab: Gib **Was heißt Ziege?** aus und lies die Antwort mit `scanner.next()` ein. Ist sie richtig, gib **Richtig!** aus, sonst zum Beispiel **Falsch, richtig ist goat.** - Am Ende steht da, wie viele richtig waren, zum Beispiel **2 von 3 richtig**. -start: aufgabe-vokabeln-start.visustruct -test: goat, hay, meadow -> Was heißt Ziege? | Richtig! | Was heißt Heu? | Richtig! | Was heißt Wiese? | Richtig! | 3 von 3 richtig -test: cow, hay, meadow -> Was heißt Ziege? | Falsch, richtig ist goat. | Was heißt Heu? | Richtig! | Was heißt Wiese? | Richtig! | 2 von 3 richtig -test: goat, heu, wiese -> Was heißt Ziege? | Richtig! | Was heißt Heu? | Falsch, richtig ist hay. | Was heißt Wiese? | Falsch, richtig ist meadow. | 1 von 3 richtig -braucht: whileSchleife, verzweigung -tipp: Überspringe die Kopfzeile. In der Schleife zerlegst du jede Zeile mit `split(";")`: `teile[0]` ist das deutsche Wort, `teile[1]` das englische. -tipp: Texte vergleichst du mit `antwort.equals(teile[1])`, nicht mit `==`. Zähle vor der Schleife mit `int richtig = 0;` und `int anzahl = 0;` mit. -lösung: aufgabe-vokabeln-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/volljaehrig.md b/web/lessons/de/aufgaben/volljaehrig.md deleted file mode 100644 index 64c725ef..00000000 --- a/web/lessons/de/aufgaben/volljaehrig.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Volljährig? -nach: 8 -stufe: 1 ---- - -Im Kino, beim Führerschein oder bei der Wahl: Oft kommt es darauf an, ob jemand schon 18 ist. - -::: übung aufgabe-volljaehrig -titel: Volljährig? -auftrag: Frage nach dem Alter. Ist die Person 18 oder älter, gib **volljährig** aus, sonst **minderjährig**. -test: 18 -> volljährig -test: 35 -> volljährig -test: 17 -> minderjährig -test: 6 -> minderjährig -braucht: verzweigung -tipp: Es gibt zwei mögliche Ausgaben, also brauchst du eine Verzweigung. -tipp: Lies das Alter mit `input: int alter "Wie alt bist du?"` ein. Die Bedingung der Verzweigung lautet `alter >= 18`. Achte darauf, dass 18 schon volljährig ist. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-volljaehrig-java -titel: Volljährig? in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 18 -> volljährig -test: 35 -> volljährig -test: 17 -> minderjährig -test: 6 -> minderjährig -braucht: verzweigung -tipp: Lies das Alter mit `int alter = scanner.nextInt();` ein. -tipp: `if (alter >= 18) { … } else { … }`, in jedem Zweig ein `System.out.println(…)`. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: diff --git a/web/lessons/de/aufgaben/wuerfel-mvc.md b/web/lessons/de/aufgaben/wuerfel-mvc.md deleted file mode 100644 index e3d055bf..00000000 --- a/web/lessons/de/aufgaben/wuerfel-mvc.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -titel: Würfeln mit MVC -nach: 40 -stufe: 3 ---- - -Baue ein Würfelspiel nach Model View Controller. Ein Knopf **Würfeln** würfelt, und zwei Views zeigen das Ergebnis. - -Das Model ist schon fertig. Es würfelt und zählt mit, wie oft gewürfelt wurde und wie viele Sechsen dabei waren: - -```java -class Wuerfel { - int augen = 1; - int wuerfe = 0; - int sechsen = 0; - - void wuerfeln() { - augen = (int) (Math.random() * 6) + 1; - wuerfe++; - if (augen == 6) { - sechsen++; - } - } -} -``` - -Deine Aufgabe: - -1. Teste das Model zuerst ohne Fenster: Würfle in `main` zehnmal und gib danach `wuerfe` und `sechsen` aus. -2. Schreibe die View `AugenAnzeige`. Sie zeigt in einem großen JLabel die gewürfelte Zahl, zum Beispiel „4“. -3. Schreibe die View `StatistikAnzeige`. Sie zeigt zum Beispiel „Sechsen: 2 von 10 Würfen“. -4. Der Controller ist der Zuhörer am Knopf: Er lässt das Model würfeln und gibt beiden Views die neuen Werte. - -**Du bist fertig, wenn …** - -- in der Konsole nach zehn Würfen `wuerfe` gleich 10 ist und `sechsen` zwischen 0 und 10 liegt, -- jeder Klick auf **Würfeln** eine neue Zahl von 1 bis 6 in der `AugenAnzeige` zeigt, -- die `StatistikAnzeige` nach jedem Klick einen Wurf mehr zählt und Sechsen richtig mitzählt, -- im Model kein `JLabel` und in den Views kein `Math.random` steht. - -Zusatz: Ein Knopf **Neues Spiel** setzt alles auf Anfang. Die Methode dafür gehört ins Model. - -::: ludwig -Denk an die Regel aus Lektion 40: Im Model steht kein einziges `JLabel`. Wenn du beim Würfeln die Schriftgröße änderst, gehört das in die View. -::: diff --git a/web/lessons/de/aufgaben/zahl-verdoppeln.md b/web/lessons/de/aufgaben/zahl-verdoppeln.md deleted file mode 100644 index 4bb26cdb..00000000 --- a/web/lessons/de/aufgaben/zahl-verdoppeln.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Zahl verdoppeln -nach: 2 -stufe: 1 ---- - -Eine Variable kann ihren Wert ändern. Hier wird eine Zahl verdoppelt. - -::: übung aufgabe-verdoppeln -titel: Zahl verdoppeln -auftrag: Lege die Variable `zahl` mit dem Wert 4 fest. Verdopple sie in einer zweiten Anweisung, ohne eine neue Variable anzulegen. Gib danach **Verdoppelte Zahl: 8** aus, wobei die 8 aus der Variablen kommt. -test: - -> Verdoppelte Zahl: 8 -tipp: Verdoppeln heißt: mal 2. Das Ergebnis speicherst du wieder in derselben Variablen. -tipp: `zahl = zahl * 2` rechnet mit dem alten Wert und speichert das Ergebnis wieder in `zahl`. Text und Variable verbindest du mit `+`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: diff --git a/web/lessons/de/begriffe.md b/web/lessons/de/begriffe.md deleted file mode 100644 index 83ec1c59..00000000 --- a/web/lessons/de/begriffe.md +++ /dev/null @@ -1,258 +0,0 @@ -Fachbegriffe für den Lernbereich. Kommt ein Begriff in einem Schritt vor, wird er dort einmal markiert und zeigt beim -Hovern oder Antippen seine Erklärung. Steht er im Schritt fett, wird er gerade erklärt und deshalb nicht markiert. -`auch:` nennt weitere Formen, die ebenfalls erkannt werden. Groß- und Kleinschreibung zählt. - -### Algorithmus -auch: Algorithmen -Eine genaue Schritt-für-Schritt-Anleitung, mit der man eine Aufgabe löst. Jeder Schritt ist eindeutig. - -### Struktogramm -auch: Struktogramme, Struktogramms, Struktogrammen -Ein Algorithmus als Bild aus Kästen. Es wird von oben nach unten gelesen. - -### Programmablaufplan -auch: PAP, Programmablaufpläne, Programmablaufplans -Ein Algorithmus als Bild aus Symbolen, die mit Pfeilen verbunden sind (DIN 66001). Eine Raute ist eine Verzweigung. - -### Pseudocode -Ein Algorithmus als Text in normaler Sprache mit festen Wörtern wie WENN und SOLANGE. Die Einrückung zeigt, was zusammengehört. - -### Anweisung -auch: Anweisungen -Ein einzelner Schritt im Struktogramm, zum Beispiel eine Rechnung, eine Eingabe oder eine Ausgabe. - -### Sequenz -auch: Sequenzen -Schritte, die nacheinander von oben nach unten ausgeführt werden, jeder genau einmal. - -### Verzweigung -auch: Verzweigungen, bedingte Anweisung, bedingte Anweisungen -Ein Baustein, der mit einer Bedingung entscheidet, welcher von zwei Wegen ausgeführt wird. Im Lehrplan heißt sie auch bedingte Anweisung. - -### Zweig -auch: Zweige, Zweigen -Einer der beiden Wege einer Verzweigung: links für true, rechts für false. - -### Schleife -auch: Schleifen, Wiederholung, Wiederholungen, Wiederholungsstruktur, Wiederholungsstrukturen -Ein Baustein, der Schritte wiederholt, solange eine Bedingung gilt. Im Lehrplan heißt sie auch Wiederholungsstruktur. - -### Rumpf -Der Inhalt einer Schleife, also die Schritte, die wiederholt werden. - -### Durchlauf -auch: Durchläufe, Durchlaufs, Durchläufen -Ein einmaliges Ausführen des Inhalts einer Schleife. - -### Bedingung -auch: Bedingungen -Eine Frage, die nur mit ja (true) oder nein (false) beantwortet wird, zum Beispiel alter >= 18. - -### Platzhalter -Ein leerer Kasten mit dem Zeichen ø. Ziehst du ein Element darauf, wird er ersetzt. - -### Objektinspektor -Der Bereich unter dem Diagramm. Hier bearbeitest du Text und Einstellungen des Kastens, den du gerade angeklickt hast. - -### Simulation -Führt dein Struktogramm im Editor aus, auf Wunsch Schritt für Schritt. So siehst du, was passiert. - -### Variable -auch: Variablen -Eine beschriftete Kiste für einen Wert. Sie hat einen Namen, einen Datentyp und einen Wert. - -### CamelCase -auch: camelCase -Schreibweise für Namen aus mehreren Wörtern: zusammengeschrieben, jedes weitere Wort beginnt groß, z. B. `preisMitSteuer`. - -### case-sensitive -auch: Groß- und Kleinschreibung -Java unterscheidet Groß- und Kleinbuchstaben: `zahl`, `Zahl` und `ZAHL` sind drei verschiedene Namen. `system` statt `System` ist ein Fehler. - -### Konstante -auch: Konstanten -Eine Variable, deren Wert nach dem Festlegen gleich bleibt. Man schreibt `const` davor, z. B. `const double MWST = 0.19`. - -### Datentyp -auch: Datentypen, Datentyps -Legt fest, welche Art von Wert in eine Variable passt: int (ganze Zahl), double (Kommazahl), String (Text), boolean (true oder false) oder char (ein einzelnes Zeichen). - -### Zuweisung -auch: Zuweisungen -Das Zeichen =. Es rechnet die rechte Seite aus und speichert das Ergebnis in der Variablen links. - -### Hilfsvariable -auch: Hilfsvariablen -Eine zusätzliche Variable, die einen Wert kurz aufbewahrt, zum Beispiel beim Tauschen. - -### EVA-Prinzip -Eingabe, Verarbeitung, Ausgabe: erst Werte abfragen, dann rechnen, dann das Ergebnis anzeigen. - -### Problemanalyse -Du liest eine Aufgabe genau und klärst, was gegeben ist, was gesucht ist und wie man vom einen zum anderen kommt. - -### Vergleichsoperator -auch: Vergleichsoperatoren -Ein Zeichen, das zwei Werte vergleicht, zum Beispiel == oder <. Das Ergebnis ist true oder false. - -### Fallauswahl -auch: Mehrfachauswahl -Ein Baustein, der je nach Wert einer Variablen genau einen von mehreren Fällen ausführt. Im Lehrplan heißt sie auch Mehrfachauswahl. - -### kopfgesteuert -auch: kopfgesteuerte, kopfgesteuerten -Die Schleife prüft ihre Bedingung vor jedem Durchlauf. Ist sie gleich zu Beginn falsch, läuft die Schleife kein einziges Mal. - -### fußgesteuert -auch: fußgesteuerte, fußgesteuerten -Die Schleife prüft ihre Bedingung erst nach jedem Durchlauf. Sie läuft also mindestens einmal. - -### Zählvariable -auch: Zählvariablen -Eine Variable, die in einer Schleife mitzählt, meistens i. - -### Endlosschleife -auch: Endlosschleifen -Eine Schleife, deren Bedingung nie falsch wird. Sie hört nie von selbst auf. - -### Merker -Eine Variable vom Typ boolean, die mit false anfängt und auf true gesetzt wird, sobald etwas passiert ist. - -### Zustandsautomat -auch: Zustandsautomaten, Zustandsvariable, Zustandsvariablen -Ein Programm, das immer in genau einem Zustand ist und nach festen Regeln in den nächsten Zustand wechselt, z. B. eine Ampel. - -### Array -auch: Arrays -Eine Reihe von Fächern mit einem gemeinsamen Namen und demselben Datentyp. Im Lehrplan heißt es auch Feld. - -### Index -Die Nummer eines Fachs in einem Array. Gezählt wird ab 0. - -### char -Der Datentyp für ein einzelnes Zeichen. Er steht in einfachen Anführungszeichen, z. B. 'C'. "C" mit doppelten Anführungszeichen ist dagegen ein String. - -### Zeichencode -auch: Zeichencodes, ASCII-Code, ASCII-Tabelle -Die Zahl, die hinter einem Zeichen steckt. In der ASCII-Tabelle hat A den Code 65, B 66 und so weiter. In Java wird mit (int) aus einem Zeichen sein Code und mit (char) aus einem Code das Zeichen. - -### Zeichenkette -auch: Zeichenketten -Ein Text, also eine Reihe von Zeichen. In Java heißt der Datentyp String. Die Zeichen werden ab 0 gezählt. - -### 2D-Array -auch: 2D-Arrays, zweidimensionales Array, zweidimensionale Arrays -Ein Array mit Zeilen und Spalten, wie eine Tabelle. Ein Fach braucht zwei Indizes, z. B. `sitze[1][2]`. - -### Unterprogramm -auch: Unterprogramme, Unterprogramms, Unterprogrammen, Methode, Methoden, Prozedur, Prozeduren, Funktion, Funktionen -Ein eigenes Struktogramm für eine Teilaufgabe. Es wird mit einem Aufruf ausgeführt. Im Lehrplan und in Java heißt es auch Methode, je nach Art auch Prozedur oder Funktion. - -### Parameter -auch: Parametern -Eine Variable in der Kopfzeile eines Unterprogramms. Beim Aufruf bekommt sie den Wert aus den Klammern. - -### Überladung -auch: überladen, überladene, überladenen, overloading -Mehrere Methoden mit demselben Namen, aber verschiedenen Parametern, z. B. `mbot.forward(30)` und `mbot.forward(2, "Umdrehungen")`. Java wählt beim Aufruf die passende nach Anzahl und Typ der Parameter. - -### Spaghetticode -Ein Programm, das so kreuz und quer springt, dass man den Ablauf kaum noch verfolgen kann. Struktogramme verhindern das. - -### Wertübergabe -auch: call by value -Englisch call by value. Ein Unterprogramm bekommt eine Kopie des Werts. Ändert es seinen Parameter, bleibt die Variable des Aufrufers unverändert. - -### Referenzübergabe -auch: call by reference -Englisch call by reference. Ein Unterprogramm bekommt einen Verweis auf dieselben Daten, z. B. auf ein Array. Ändert es ein Fach, sieht auch der Aufrufer die Änderung. - -### Rückgabewert -auch: Rückgabewerte -Das Ergebnis, das ein Unterprogramm mit return an den Aufruf zurückgibt. - -### Aussprung -Verlässt sofort die Schleife, in der er steht. Danach geht es unter der Schleife weiter. - -### Syntaxfehler -auch: Syntaxfehlern, Syntaxfehlers -Ein Fehler in der Schreibweise, zum Beispiel ein fehlendes `;`. Das Programm startet gar nicht. Englisch: syntax error. - -### Laufzeitfehler -auch: Laufzeitfehlern, Laufzeitfehlers -Ein Fehler, der erst beim Ausführen auftritt, zum Beispiel Teilen durch 0. Das Programm bricht an dieser Stelle ab. Englisch: runtime error. - -### Logikfehler -auch: Logikfehlern, Logikfehlers -Das Programm läuft ohne Meldung, liefert aber ein falsches Ergebnis. Du findest ihn mit dem Schreibtischtest. Englisch: logic error. - -### Schreibtischtest -auch: Schreibtischtests -Du führst ein Struktogramm auf Papier aus und notierst nach jedem Schritt die Werte der Variablen. - -### lineare Suche -auch: Lineare Suche -Ein Suchverfahren, das alle Werte der Reihe nach mit dem gesuchten Wert vergleicht. Es funktioniert auch bei unsortierten Werten. - -### binäre Suche -auch: Binäre Suche -Ein Suchverfahren für sortierte Werte. Es sieht sich die Mitte an und sucht nur in der passenden Hälfte weiter. - -### Bubblesort -Ein einfaches Sortierverfahren. Es vergleicht immer zwei Nachbarn und tauscht sie, wenn sie falsch herum stehen. - -### Ereignis -auch: Ereignisse, Ereignissen, Ereignisses -Etwas, das der Mensch an einem Fensterprogramm tut, zum Beispiel einen Knopf anklicken. Das Programm reagiert darauf mit einem Zuhörer (ActionListener). - -### Interface -auch: Interfaces -Eine Art Vertrag in Java: Er legt fest, welche Methoden eine Klasse haben muss. `ActionListener` ist ein Interface mit der Methode `actionPerformed`. - -### Exception -auch: Exceptions -Ein Fehler, der erst beim Ausführen auftritt, zum Beispiel ein Index außerhalb des Arrays. Fängt ihn kein `catch` auf, bricht das Programm ab. - -### Attribut -auch: Attribute, Attributs -Eine Variable, die direkt in der Klasse steht und nicht in einer Methode. Mit `static` gehört sie zur Klasse selbst und lebt so lange wie das Programm, zum Beispiel ein Klickzähler, den mehrere Zuhörer ändern. - -### Scanner -Ein fertiger Baustein von Java, der Eingaben über die Tastatur liest, z. B. mit `scanner.nextInt()` eine Zahl. Er braucht ganz oben `import java.util.Scanner;`. - -### Kommentar -auch: Kommentare, Kommentaren, Kommentars -Eine Notiz im Code für Menschen. Java überspringt sie beim Ausführen. `//` gilt bis zum Ende der Zeile, `/* … */` kann über mehrere Zeilen gehen. - -### import -Eine Zeile ganz oben im Java-Code, die einen fertigen Baustein aus einem Paket holt, z. B. `import java.util.Scanner;`. Ohne sie kennt Java den Namen nicht. - -### Quellcode -Der Programmtext, den Menschen schreiben und lesen, zum Beispiel in einer Datei `Einmaleins.java`. - -### Compiler -auch: Compilers -Ein Programm, das Quellcode übersetzt. In Java heißt er `javac` und macht aus einer `.java`-Datei Bytecode in einer `.class`-Datei. - -### Bytecode -Die übersetzte Form eines Java-Programms in einer `.class`-Datei. Ihn liest nicht der Mensch, sondern die JVM. - -### JVM -auch: Java Virtual Machine, virtuelle Maschine -Die Java Virtual Machine führt Bytecode aus. Sie ist ein Programm, das wie ein kleiner Computer aus Software auf Windows, macOS oder Linux läuft. Deshalb ist Java plattformunabhängig. - -### JRE -Java Runtime Environment: die JVM und fertige Bausteine. Damit kann man Java-Programme ausführen, aber nicht übersetzen. - -### JDK -Java Development Kit: die JRE und dazu den Compiler `javac` und weitere Werkzeuge. Das braucht man zum Programmieren. - -### plattformunabhängig -Ein Programm läuft ohne Änderung auf verschiedenen Betriebssystemen. Bei Java sorgt die JVM dafür: „Write once, run anywhere“. - -### Maschinencode -Befehle als Zahlen, die der Prozessor direkt ausführt. Jede Prozessorart hat ihren eigenen Maschinencode. - -### JIT-Compiler -auch: JIT -Ein Teil der JVM, der oft benutzten Bytecode während des Ausführens („just in time“) in Maschinencode übersetzt. Dadurch wird Java schneller. diff --git a/web/lessons/de/nachschlagen.md b/web/lessons/de/nachschlagen.md deleted file mode 100644 index 4c461117..00000000 --- a/web/lessons/de/nachschlagen.md +++ /dev/null @@ -1,169 +0,0 @@ -## Grundbausteine - -### Anweisung - -Ein einzelner Schritt. Damit legst du Variablen an oder rechnest. - -``` -int zahl = 5 -double preis = 2.5 -String name = "Ada" -zahl = zahl + 1 -``` - -### Datentypen - -Der Datentyp steht vor dem Namen der Variablen. - -| Datentyp | Bedeutung | Beispiel | -|---|---|---| -| `int` | ganze Zahl | `int anzahl = 3` | -| `double` | Kommazahl (mit Punkt) | `double preis = 2.5` | -| `String` | Text in doppelten Anführungszeichen | `String name = "Ada"` | -| `boolean` | Wahrheitswert | `boolean fertig = false` | -| `char` | ein einzelnes Zeichen in einfachen Anführungszeichen | `char note = 'C'` | - -`'C'` ist ein `char`, `"C"` ein String. Ein Zeichen aus einem Text holst du mit `charAt`: `char erster = name.charAt(0)`. Jedes Zeichen hat einen Zeichencode: `(int) 'A'` ist 65, `(char) 66` ist `'B'`. - -### Eingabe - -Fragt beim Ausführen einen Wert ab und speichert ihn in einer Variablen. Der Text in Anführungszeichen ist die Frage. - -``` -input: int alter "Wie alt bist du?" -input: double preis "Preis eingeben" -input: String name "Wie heißt du?" -``` - -### Ausgabe - -Zeigt einen Text oder einen Wert an. Texte stehen in Anführungszeichen, mit `+` verbindest du Text und Variablen. - -``` -output: "Hallo" -output: summe -output: "Du bist " + alter + " Jahre alt." -``` - -> In der Ausgabe hängt `+` die Teile immer aneinander: `output: a + b` zeigt bei 3 und 4 also 34. Rechne deshalb vorher in einer Variablen, z. B. `int summe = a + b`, und gib dann `summe` aus. - -## Verzweigungen - -### Verzweigung - -Prüft eine Bedingung. Ist sie wahr, wird der linke Zweig (true) ausgeführt, sonst der rechte (false). - -``` -alter >= 18 -zahl % 2 == 0 -note < 1 || note > 6 -``` - -### Fallauswahl - -Wählt je nach Wert einer Variablen einen von mehreren Fällen. Passt kein Fall, gilt `default`. Fälle fügst du rechts mit **Weiteren Fall hinzufügen** hinzu. - -## Schleifen - -### While-Schleife - -Wiederholt den Inhalt, **solange** die Bedingung wahr ist. Die Bedingung wird vor jedem Durchlauf geprüft. - -``` -zahl != 1 -i < 10 -``` - -### Do-while-Schleife - -Führt den Inhalt **mindestens einmal** aus und prüft die Bedingung erst danach. - -### For-Schleife - -Zählt eine Variable von einem Startwert bis zu einer Grenze. Die drei Felder sind Start, Bedingung und Schritt. - -``` -int i = 0 -i < 5 -i++ -``` - -### Endlosschleife - -Wiederholt den Inhalt ohne Bedingung. Mit einem **Aussprung** im Inneren verlässt du sie. - -### Aussprung - -Verlässt die umgebende Schleife (`break`). - -## Unterprogramme - -### Aufruf - -Steht für ein Unterprogramm, z. B. `berechneSumme()`. Mit Parametern und Rückgabewert hat das Unterprogramm eine Kopfzeile als Titel: - -``` -int quadrat(int x) Kopfzeile des Unterprogramms -return x * x gibt das Ergebnis zurück -int q = quadrat(5) Aufruf, q bekommt 25 -zeigeSumme(3, 4) Aufruf eines void-Unterprogramms -``` - -## Rechnen und Vergleichen - -### Rechenzeichen und Vergleiche - -- `+ - * /` Grundrechenarten -- `%` Rest beim Teilen (`7 % 2` ist 1) -- `==` gleich, `!=` ungleich -- `<` `<=` `>` `>=` kleiner, kleiner gleich, größer, größer gleich -- `&&` und, `||` oder - -## Arrays - -### Arrays - -Mehrere Werte unter einem Namen. Gezählt wird ab 0. - -``` -int[] zahlen = {3, 7, 1} -output: zahlen[0] -int[] leer = new int[5] -``` - -Ein **2D-Array** ist eine Tabelle mit Zeilen und Spalten: - -``` -boolean[][] sitze = new boolean[3][5] -sitze[1][4] = true -output: sitze.length + " Reihen, " + sitze[0].length + " Plätze" -``` - -## Java - -### Vom Code zum Programm - -Der Compiler `javac` übersetzt `Datei.java` in Bytecode (`Datei.class`). Die JVM übersetzt den Bytecode beim Ausführen in Maschinencode, auf Windows, macOS und Linux gleich. C++ wird dagegen direkt zu Maschinencode und muss für jedes System neu kompiliert werden. - -| | enthält | wofür | -|---|---|---| -| JRE | JVM und Bibliotheken | ausführen | -| JDK | JRE, Compiler `javac`, Werkzeuge | programmieren | - -### Kommentare - -Notizen im Code. Java überspringt sie, das Struktogramm bleibt gleich. - -```java -/* - * Aufgabe: die Zahlen von 1 bis 10 addieren - * (mehrzeiliger Kommentar) - */ -// Verarbeitung (einzeiliger Kommentar) -int summe = 0; -for (int i = 1; i <= 10; i++) { - summe = summe + i; // Kommentar hinter dem Code -} // Ende for -``` - -Gut für: die Aufgabenstellung, die Einteilung in Eingabe, Verarbeitung und Ausgabe, eine Erklärung, das Ende eines Blocks hinter `}`, die erwartete Ausgabe und Notizen fürs nächste Mal. diff --git a/web/lessons/de/visubot/00-struktogramm-und-editor.md b/web/lessons/de/visubot/00-struktogramm-und-editor.md deleted file mode 100644 index ae945027..00000000 --- a/web/lessons/de/visubot/00-struktogramm-und-editor.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -titel: Struktogramm und Editor -bereich: Losfahren -kurz: Was ein Struktogramm ist, wie du den Editor bedienst und deine Programme speicherst. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Algorithmus und ein Struktogramm ist -- die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen -- im Editor Elemente einfügen, verschieben und löschen -- ein Struktogramm speichern und wieder öffnen -::: - -## Ein Plan für den Roboter - -Ein Roboter denkt nicht mit. Er tut genau das, was in seinem Programm steht, nicht mehr und nicht weniger. Bevor du ihn programmierst, brauchst du deshalb einen genauen Plan. So eine genaue Schritt-für-Schritt-Anleitung heißt **Algorithmus**. - -Ein **Struktogramm** zeichnet den Plan als Kästen. Du liest es von oben nach unten. Jeder Kasten ist ein Schritt. Erfunden haben es 1973 Isaac Nassi und Ben Shneiderman, deshalb heißt es auch **Nassi-Shneiderman-Diagramm**. - -::: diagramm vb-l00-sequenz-loesung.visustruct -Fahre vor, drehe nach rechts, fahre vor: drei Kästen, von oben nach unten. -::: - -::: angeberwissen -Das erste Programm für eine Maschine schrieb Ada Lovelace schon 1843, für eine Rechenmaschine, die nie fertig gebaut wurde. Sie ahnte damals schon, dass solche Maschinen einmal mehr können als rechnen, zum Beispiel Musik machen. Dein mBot2 kann beides. -::: - -## Drei Bausteine - -Jedes Programm, auch das für einen Roboter, besteht aus nur drei Bausteinen: - -| Baustein | Was er tut | Beispiel mit dem mBot2 | -|---|---|---| -| **Sequenz** | Schritte nacheinander | vorfahren, drehen, vorfahren | -| **Verzweigung** | je nach Bedingung das eine oder das andere | Ist die Wand nah? Dann drehen, sonst vorfahren. | -| **Wiederholung** | Schritte mehrmals | viermal das Gleiche tun | - -Im Struktogramm sieht man die Bausteine sofort: Die Verzweigung hat oben ein Dreieck und zwei Spalten: links **true** (wahr), rechts **false** (falsch). Die Wiederholung (Schleife) umschließt ihre Kästen wie ein gekipptes L. - -::: diagramm vb-l00-bausteine-loesung.visustruct -Alle drei Bausteine: erst ein Kasten, dann eine Schleife, darin eine Verzweigung. -::: - -## Der Editor - -Der [Struktogramm-Editor](#zeige-editor) besteht aus mehreren Bereichen. Fahre mit der Maus über ein blaues Wort oder tippe es an, dann leuchtet der Bereich auf: - -- **Links die [Elemente](#zeige-elemente):** alle Bausteine zum Hineinziehen. In der Gruppe **mBot2-Befehle** liegen die Befehle für den mBot2 wie **fahre vor** und **drehe rechts**. -- **In der Mitte das [Struktogramm](#zeige-struktogramm):** Ein neues Diagramm enthält nur einen leeren Platzhalter, erkennbar an **ø**. -- **Unten der [Objektinspektor](#zeige-objektinspektor):** Hier änderst du den Text des ausgewählten Kastens, zum Beispiel `mbot.forward(30)` statt `mbot.forward()`. Die Linie mit dem Griff darüber kannst du nach oben oder unten ziehen. -- **Darüber die [Werkzeuge](#zeige-werkzeuge):** Rückgängig, Kopieren, Löschen, Exportieren und rechts die **Simulation**. - -So baust du: - -- **Einfügen:** Zieh ein Element aus den Elementen auf das Struktogramm. Auf den Platzhalter **ø** ersetzt es ihn. Auf die obere Hälfte eines Kastens kommt es davor, auf die untere Hälfte dahinter. Eine blaue Linie zeigt dir, wo es landet. -- **Text ändern:** Doppelklick auf einen Kasten, auf dem Tablet Doppeltipp. Dann tippst du direkt im Struktogramm, **Enter** übernimmt. -- **Verschieben:** einen Kasten an eine andere Stelle ziehen. -- **Löschen:** Kasten anklicken und Entf drücken oder auf den [Mülleimer](#zeige-loeschen) klicken. -- **Rückgängig:** Strg+Z, am Mac ⌘Z. - -> 🧩 **Lieber Blöcke wie in Scratch?** Klicke in den Werkzeugen auf [Blöcke](#zeige-bloecke). Dann siehst du dasselbe Programm als Blöcke, die du zusammensteckst. Zahlen tippst du direkt in die weißen Felder, Sensoren ziehst du hinein. Mit **Struktogramm** kommst du zurück. Es bleibt dasselbe Programm, du kannst jederzeit wechseln. - -::: übung vb-erster-plan -titel: Um die Ecke -auftrag: Baue ein Struktogramm: Der mBot2 fährt vor, dreht nach rechts, fährt vor, dreht nach links und fährt noch einmal vor. Starte danach die Simulation und schau zu. -fahrt: forward, turnRight, forward, turnLeft, forward -braucht: anweisung -tipp: Zieh **fahre vor** auf den Platzhalter ø. Die nächsten Befehle ziehst du jeweils auf die untere Hälfte des letzten Kastens. -tipp: Von oben nach unten: **fahre vor**, **drehe rechts**, **fahre vor**, **drehe links**, **fahre vor**. -lösung: vb-erster-plan-loesung.visustruct -::: - -> Bist du fertig, klickst du in der Übung auf **Prüfen**. VisuStruct lässt dein Struktogramm dann laufen und schaut nach, ob der mBot2 genau so fährt wie verlangt. Später prüft es dein Programm oft in mehreren Welten, zum Beispiel einmal mit Wand und einmal ohne. Klappt es nicht, helfen dir die **Tipps**. - -## Speichern und Öffnen - -- **[Speichern](#zeige-speichern)** (Strg+S, am Mac ⌘S): Das Struktogramm wird als Datei mit der Endung `.visustruct` gespeichert. Den Dateinamen schlägt der [Titel](#zeige-titel) über dem Struktogramm vor. -- **[Öffnen](#zeige-oeffnen)** (Strg+O, am Mac ⌘O): eine gespeicherte Datei wieder laden. - -In der Spalte [📁 Dateien](#zeige-dateien) richtest du einmal einen Ordner ein, deinen **Workspace**. Dort landen alle deine Programme und auch dein Lernstand. Den Ordner kannst du mitnehmen und zu Hause weitermachen. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektionen [„Was ist ein Struktogramm?“](?visustruct&lektion=01-was-ist-ein-struktogramm), [„Der Editor“](?visustruct&lektion=02-der-editor) und [„Speichern, Öffnen, Exportieren“](?visustruct&lektion=03-speichern-oeffnen-exportieren). Sie öffnen sich in einem neuen Fenster. - -## Spiel: Welcher Baustein? - -::: spiel frage -titel: Spiel: Welcher Baustein? -intro: Ludwig beschreibt, was sein mBot2 tun soll. Ist das eine Sequenz, eine Verzweigung oder eine Wiederholung? Sechs Runden. -antworten: Sequenz | Verzweigung | Wiederholung - -runde: -frage: Der mBot2 fährt vor, dreht sich nach rechts und fährt wieder vor. -richtig: Sequenz -> Drei Schritte nacheinander, jeder genau einmal. - -runde: -frage: Ist die Wand nah, dreht er sich. Sonst fährt er vor. -richtig: Verzweigung -> Je nach Bedingung passiert das eine oder das andere. - -runde: -frage: Er fährt viermal 10 cm vor. -richtig: Wiederholung -> Derselbe Schritt kommt mehrmals dran. - -runde: -frage: Solange keine Linie kommt, fährt er weiter. -richtig: Wiederholung -> „Solange“ heißt: immer wieder, bis die Bedingung nicht mehr stimmt. - -runde: -frage: Steht er auf Rot, piept er. -richtig: Verzweigung -> Nur wenn die Bedingung stimmt, piept er. - -runde: -frage: Er zeigt „Hallo“ auf dem Bildschirm und spielt danach einen Ton. -richtig: Sequenz -> Zwei Schritte nacheinander, ohne Bedingung und ohne Wiederholung. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Algorithmus und ein Struktogramm ist. → Ein Plan für den Roboter -- Ich kann die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen. → Drei Bausteine -- Ich kann im Editor Elemente einfügen, verschieben und löschen. → Der Editor -- Ich kann ein Struktogramm speichern und wieder öffnen. → Speichern und Öffnen -::: - -## Wissenstest - -::: quiz -? Was ist ein Algorithmus? -→ Ein Plan für den Roboter -- [ ] ein Bauteil des mBot2 -- [x] eine genaue Schritt-für-Schritt-Anleitung -- [ ] eine Programmiersprache -> Ein Algorithmus sagt genau, was in welcher Reihenfolge passiert. Das Struktogramm zeichnet ihn. -? Welcher Baustein entscheidet je nach Bedingung? -→ Drei Bausteine -- [ ] die Sequenz -- [x] die Verzweigung -- [ ] die Wiederholung -> Die Verzweigung hat oben ein Dreieck und zwei Spalten für true und false. -? Du ziehst **fahre vor** auf die untere Hälfte eines Kastens. Wo landet es? -→ Der Editor -- [ ] davor -- [x] dahinter -- [ ] es ersetzt den Kasten -> Obere Hälfte heißt davor, untere Hälfte dahinter. -? Welche Endung hat eine gespeicherte Struktogramm-Datei? -→ Speichern und Öffnen -- [ ] `.java` -- [x] `.visustruct` -- [ ] `.png` -> `.png` ist ein Bild zum Weitergeben. Weiterbearbeiten kannst du nur die `.visustruct`-Datei. -::: diff --git a/web/lessons/de/visubot/01-der-mbot2.md b/web/lessons/de/visubot/01-der-mbot2.md deleted file mode 100644 index 17a56684..00000000 --- a/web/lessons/de/visubot/01-der-mbot2.md +++ /dev/null @@ -1,416 +0,0 @@ ---- -titel: Der mBot2 -bereich: Losfahren -kurz: Lerne den Roboter kennen, den du gleich programmierst. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Teile des mBot2 benennen -- erklären, wie der mBot2 Abstände misst -- erklären, wie der mBot2 eine Linie erkennt -- erklären, was Sensoren und Aktoren sind -- den mBot2 mit Elementen aus der Gruppe mBot2-Befehle steuern -- den mBot2 im Simulator fahren lassen -- den echten mBot2 per USB-C verbinden und wieder trennen -- prüfen, ob alle Sensoren und Aktoren funktionieren -::: - -## Hallo mBot2 - -::: mbot2 -fokus: -drehen: ja -::: - -Der **mBot2** ist ein kleiner Roboter der Firma Makeblock. In dieser Lektion machst du eine Tour durch alle seine Teile: - -- den [CyberPi]{teil=cyberpi}, den kleinen Computer hinten oben -- das durchsichtige [Shield mit Akku]{teil=shield} in der Mitte -- zwei [Motoren]{teil=motors} mit [Rädern]{teil=wheels} -- den [Ultraschallsensor]{teil=ultrasonic} vorn, der wie zwei Augen aussieht -- den [Liniensensor]{teil=lineSensor} unten an der Nase -- das türkise [Chassis]{teil=chassis}, das alles zusammenhält - -In VisuBot programmierst du ihn mit Struktogrammen. Jedes Programm kannst du zuerst im Simulator ausprobieren, auch ganz ohne echten Roboter. - -> Fahr mit der Maus über ein gelb unterstrichenes Wort, dann leuchtet das Teil im Modell auf. Tippst du darauf, fährt die Kamera hin. Das Modell kannst du mit der Maus oder dem Finger drehen und mit dem Mausrad heranzoomen. - -## Der CyberPi - -::: mbot2 -fokus: cyberpi, screen, joystick, buttons, leds, lightSensor, microphone -ansicht: hinten -::: - -Der [CyberPi]{teil=cyberpi,screen,joystick,buttons,leds,lightSensor,microphone} ist das Gehirn des mBot2: ein **Mikrocontroller**, also ein kleiner Computer ohne Tastatur. Er führt dein Programm aus. Er liegt quer hinten oben auf dem Roboter und lässt sich auch abnehmen. - -Auf ihm ist schon eine Menge eingebaut: - -- ein farbiger [Bildschirm]{teil=screen}, auf dem Text und Bilder erscheinen -- ein [Joystick]{teil=joystick}, den du in vier Richtungen drücken kannst -- zwei [Tasten]{teil=buttons}, eine mit Dreieck und eine mit Quadrat, und an der Seite neben dem USB-Anschluss die Home-Taste -- fünf bunte [LEDs]{teil=leds} als Lichtleiste an der Rückseite -- ein [Lichtsensor]{teil=lightSensor} und ein [Mikrofon]{teil=microphone} - -Dazu kommen ein Lautsprecher und ein Lagesensor, die du von außen nicht siehst. Der Lagesensor merkt, ob der CyberPi gekippt oder gedreht wird. - -> Das Modell ist nach Fotos nachgebaut. Schau dir deinen eigenen mBot2 an und vergleiche. - -## Bildschirm, LEDs und Lautsprecher - -::: mbot2 -fokus: screen, leds -ansicht: hinten -::: - -Mit dem [Bildschirm]{teil=screen} und den fünf [LEDs]{teil=leds} zeigt dir der mBot2, was er gerade tut. Jede LED kann in fast jeder Farbe leuchten, weil sie Rot, Grün und Blau mischt. Darum heißen sie **RGB-LEDs**. - -Später lässt du die LEDs mit `mbot.setLed(255, 0, 0)` rot leuchten. Die drei Zahlen sind Rot, Grün und Blau, jeweils von 0 bis 255. - -Im [CyberPi]{teil=cyberpi} steckt außerdem ein kleiner **Lautsprecher**. Mit `mbot.playTone(440, 300)` spielt er einen Ton: 440 Schwingungen pro Sekunde (Hertz), 300 Millisekunden lang. - -## Licht und Geräusche messen - -::: mbot2 -fokus: lightSensor, microphone -ansicht: oben -::: - -Der [Lichtsensor]{teil=lightSensor} misst, wie hell es ist. Das [Mikrofon]{teil=microphone} misst, wie laut es ist. So kann der mBot2 zum Beispiel losfahren, wenn du in die Hände klatschst. - -## Tasten und Joystick - -::: mbot2 -fokus: joystick, buttons -ansicht: oben -::: - -Die [Tasten]{teil=buttons} A und B und der [Joystick]{teil=joystick} sind auch Sensoren: Sie merken, ob du sie drückst. Dein Programm kann zum Beispiel warten, bis du A drückst, und erst dann losfahren. Mit dem Joystick lenkst du den mBot2 wie mit einer Fernbedienung. - -## Der Lagesensor - -::: mbot2 -fokus: cyberpi -drehen: ja -::: - -Im [CyberPi]{teil=cyberpi} steckt ein Sensor, den du nicht siehst: der **Lagesensor** (ein Gyroskop mit Beschleunigungssensor). Er merkt, ob der mBot2 nach vorn, nach hinten oder zur Seite geneigt ist, wie weit er sich gedreht hat und ob ihn jemand schüttelt. Dein Handy hat so einen Sensor auch: Darum dreht sich das Bild, wenn du es quer hältst. - -## Strom: das Shield mit Akku - -::: mbot2 -fokus: shield, powerSwitch -ansicht: seite -::: - -Das große durchsichtige Gehäuse ist das [mBot2-Shield]{teil=shield}. Durch den Deckel siehst du den grauen **Akku**, der den Roboter mit Strom versorgt. Hinten am Shield stecken die Kabel der Motoren (M1 und M2) und des Ultraschallsensors. - -Links an der Seite ist der [Ein-Aus-Schalter]{teil=powerSwitch} mit der Aufschrift ON/OFF. Daneben liegen zwei Buchsen S1 und S2 für kleine Servomotoren. Schalte den mBot2 aus, wenn du ihn nicht brauchst, dann hält der Akku länger. Geladen wird er über das USB-Kabel am CyberPi. - -## Motoren und Räder - -::: mbot2 -fokus: motors, wheels -ansicht: seite -::: - -Zwei [Motoren]{teil=motors} treiben die beiden [Räder]{teil=wheels} an, jeder sein eigenes. Du siehst sie von unten: silberne Motoren in hellblauen Getrieben. - -- Drehen sich beide Räder gleich schnell vorwärts, fährt der mBot2 **geradeaus**. -- Dreht sich ein Rad vorwärts und das andere rückwärts, dreht er sich **auf der Stelle**. - -Die Motoren sind **Encoder-Motoren**: Sie zählen mit, wie weit sich das Rad gedreht hat. Darum weiß der mBot2, wann er genau 10 cm gefahren ist oder sich genau um 90° gedreht hat. - -## Der Ultraschallsensor - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Vorn sitzt der schwarze [Ultraschallsensor]{teil=ultrasonic}. Er sieht aus wie zwei Augen, ist aber eher ein Ohr: Der eine Kreis schickt einen sehr hohen Ton los, den wir nicht hören. Der andere wartet auf das Echo. Je länger das Echo braucht, desto weiter ist die Wand weg. - -So misst der mBot2 Abstände in Zentimetern. Die Ringe um die Kreise können außerdem blau leuchten. - -::: angeberwissen -Fledermäuse machen es genau wie der Ultraschallsensor: Sie rufen so hoch, dass wir es nicht hören, und lauschen auf das Echo. Schall ist etwa 343 Meter pro Sekunde schnell. Steht eine Wand einen Meter vor dem mBot2, ist das Echo schon nach knapp 6 Tausendstel Sekunden zurück. -::: - -## Unter dem Roboter - -::: mbot2 -fokus: lineSensor, caster -ansicht: unten -::: - -Jetzt schaust du von unten. Ganz vorn schaut die schwarze Platine des [Liniensensors]{teil=lineSensor} heraus. Er hat **vier** kleine Fühler nebeneinander. Jeder leuchtet den Boden an und misst, wie viel Licht zurückkommt. Auf einer schwarzen Linie kommt wenig zurück, auf hellem Boden viel. So erkennt der mBot2, ob er auf einer Linie fährt. - -Der Sensor heißt **Quad-RGB-Sensor**, weil er noch mehr kann: Er erkennt auch **Farben**, zum Beispiel ein rotes oder grünes Feld auf dem Boden. - -Die [Kugelrolle]{teil=caster} dahinter ist das dritte Bein. Sie hat keinen Motor und rollt einfach in jede Richtung mit. - -## Das Chassis - -::: mbot2 -fokus: chassis -::: - -Das [Chassis]{teil=chassis} ist der türkise Rahmen aus Aluminium. Er hält alle Teile zusammen. Vorn hat er zwei runde „Ohren“, hinten ein großes **M** für Makeblock. Die vielen Löcher sind kein Zufall: Dort kannst du weitere Teile anschrauben. - -## Sensoren und Aktoren - -Ein Roboter hat zwei Arten von Teilen: - -- **Sensoren** messen etwas: den Abstand, eine Linie, eine Farbe, die Helligkeit, die Lautstärke, die Neigung oder ob eine Taste gedrückt ist. -- **Aktoren** tun etwas: Die Motoren bewegen die Räder, die LEDs leuchten, der Bildschirm zeigt etwas an, der Lautsprecher piept. - -Das Wort kommt vom lateinischen *agere*, „handeln“: Aktoren handeln, Sensoren (von *sentire*, „fühlen“) fühlen. - -Dein Programm liest die Sensoren und steuert die Aktoren. Das kennst du schon als Eingabe, Verarbeitung und Ausgabe. - -## Finde die Bauteile - -Jetzt bist du dran: Klicke im Modell auf das gesuchte Teil. Liegst du richtig, kommt die nächste Frage. - -::: mbot2quiz -- ultrasonic: Womit misst der mBot2, wie weit die Wand weg ist? -- screen: Wo erscheint Text, den dein Programm ausgibt? -- wheels: Was bewegt den mBot2 vorwärts? -- lineSensor: Was erkennt eine schwarze Linie auf dem Boden? (Tipp: Dreh das Modell.) -- powerSwitch: Wo schaltest du den mBot2 ein und aus? -- leds: Was leuchtet bei `mbot.setLed(255, 0, 0)` rot? -fertig: Super, du kennst dich am mBot2 aus! -::: - -## Befehle für den mBot2 - -Unter den [Elementen](#zeige-elemente) gibt es in VisuBot eine eigene Gruppe **mBot2-Befehle**. Jeder Kasten darin ist eine Anweisung an den Roboter: - -| Element | Text im Kasten | Was passiert | -|---|---|---| -| fahre vor | `mbot.forward()` | 10 cm vorwärts | -| fahre zurück | `mbot.backward()` | 10 cm rückwärts | -| drehe rechts | `mbot.turnRight()` | 90° nach rechts drehen | -| drehe links | `mbot.turnLeft()` | 90° nach links drehen | -| LEDs an | `mbot.setLed(255, 0, 0)` | die LEDs leuchten rot | - -In die Klammern darfst du auch eine Zahl schreiben: `mbot.forward(30)` fährt 30 cm, `mbot.turnRight(45)` dreht nur 45°. - -> Die Anweisungen sind echtes Java. `mbot` ist der Roboter, hinter dem Punkt steht, was er tun soll. - -## Erste Fahrt - -Starte nach dem Bauen die [Simulation](#zeige-simulation). Rechts erscheint die VisuBot-Welt mit dem mBot2. Das Gitter auf dem Boden hat Kästchen von 10 cm, also genau ein **fahre vor**. - -::: übung vb-gerade -titel: Geradeaus -auftrag: Ziehe fünfmal **fahre vor** in das Struktogramm. Starte dann die Simulation und schau zu, wie der mBot2 50 cm geradeaus fährt. -fahrt: 5 x (forward) -tipp: Die Gruppe mBot2-Befehle findest du unter den normalen Elementen. -tipp: Fünf Kästen `mbot.forward()` untereinander. Jeder fährt 10 cm. -lösung: vb-gerade-loesung.visustruct -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - mbot.forward(60); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,0 -> Zwei Felder nach vorn, nach rechts drehen, ein Feld weiter. - -runde: - mbot.forward(30); - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,1 -> Ein Feld nach vorn, dann schaut er nach links und fährt zwei Felder. - -runde: - mbot.turnRight(); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Zweimal rechts drehen heißt: Er schaut nach hinten. Dann fährt er ein Feld zurück, nach unten. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: .... | .... | .^.. -ziel: 2,2 -> Hoch, nach rechts, und nach der zweiten Drehung schaut er nach unten. -::: - -## Den echten mBot2 verbinden - -::: mbot2 -fokus: cyberpi, powerSwitch, joystick, buttons -ansicht: seite -::: - -Alles, was du im Simulator baust, kann auch der echte mBot2 fahren. So verbindest du ihn: - -1. Steck ein **USB-C-Kabel** in deinen Computer (zum Beispiel ein MacBook) und das andere Ende in den USB-C-Anschluss rechts am [CyberPi]{teil=cyberpi}, neben der Home-Taste. -2. Schieb den [Schiebeschalter ON/OFF]{teil=powerSwitch} links am Shield auf ON. Der Bildschirm des CyberPi leuchtet. -3. Auf dem Bildschirm erscheint ein Menü, in dem **neu starten** markiert ist. Drück den [Joystick]{teil=joystick} wie eine Taste nach unten, um das zu bestätigen. -4. Nach dem Neustart steht dort „Press B to start“. Drück **Taste B**, das ist die [Taste mit dem Dreieck]{teil=buttons}. -5. Öffne VisuStruct in **Chrome** oder **Edge**. Safari und das iPad können keine USB-Geräte ansprechen. -6. Sobald der CyberPi hochgefahren ist, klick oben auf **🔌 mBot2** (oder im Zahnrad auf „Mit mBot2 verbinden“). -7. Chrome zeigt eine Liste mit Anschlüssen. Wähle den Eintrag **USB Serial** (zum Beispiel „USB Serial (cu.usbserial-110)“). Er steht oft ganz unten, eventuell musst du nach unten scrollen. Dann klick auf **Verbinden**. -8. Der CyberPi startet kurz neu (⏳). Dann steht „Hallo VisuBot!“ auf seinem Bildschirm und der Knopf zeigt 🟢. - -Jetzt fährt der mBot2 bei jedem Schritt der Simulation mit. Stell ihn auf den Boden und lass ihm Platz! - -> Fertig? Klick noch einmal auf **🟢 mBot2**. Dann hält er an, zeigt „Bis bald!“ und gibt den Anschluss frei. Erst danach das Kabel ziehen. - -> Ganz ohne Kabel fahren? Das lernst du in Lektion 2 bei **Ohne Kabel fahren**. - -## Den mBot2 testen - -Ist der mBot2 verbunden, erscheint unter der Welt **🔧 mBot2 testen**. Klapp es auf: - -- Mit den Knöpfen probierst du jeden Aktor einmal aus: LEDs, Ton, Text auf dem Bildschirm und die Räder. Heb den mBot2 dafür hoch, damit er nicht vom Tisch fährt. -- Darunter stehen alle Sensoren mit ihren Werten, und die laufen live mit. Halte die Hand vor den Ultraschallsensor, drück Taste A, neige ihn, klatsch in die Hände oder stell ihn auf ein rotes Blatt. - -Ändert sich ein Wert nicht, obwohl du etwas tust, stimmt etwas nicht. Dann sag deiner Lehrkraft Bescheid, bevor du programmierst. - -## Spiel: Sensor oder Aktor? - -::: spiel frage -titel: Spiel: Sensor oder Aktor? -intro: Misst das Bauteil etwas, oder tut es etwas? Zehn Runden. -antworten: Sensor | Aktor - -runde: -frage: Der Ultraschallsensor misst, wie weit die Wand weg ist. -richtig: Sensor -> Er misst etwas: den Abstand. - -runde: -frage: Die Motoren drehen die Räder. -richtig: Aktor -> Sie tun etwas: Sie bewegen den mBot2. - -runde: -frage: Die LEDs leuchten rot. -richtig: Aktor -> Leuchten ist etwas tun, nicht messen. - -runde: -frage: Der Liniensensor erkennt das schwarze Klebeband. -richtig: Sensor -> Er misst, wie viel Licht vom Boden zurückkommt. - -runde: -frage: Der Lautsprecher piept. -richtig: Aktor -> Er gibt einen Ton aus. - -runde: -frage: Das Mikrofon hört dein Klatschen. -richtig: Sensor -> Es misst die Lautstärke. - -runde: -frage: Der Bildschirm zeigt „Hallo VisuBot!“. -richtig: Aktor -> Anzeigen ist eine Ausgabe. - -runde: -frage: Taste A merkt, dass du drückst. -richtig: Sensor -> Tasten sind Sensoren: Sie melden, ob sie gedrückt sind. - -runde: -frage: Der Lagesensor merkt, dass der mBot2 schief steht. -richtig: Sensor -> Er misst die Neigung. - -runde: -frage: Die Ringe am Ultraschallsensor leuchten blau. -richtig: Aktor -> Gemein! Das Bauteil ist ein Sensor, aber das Leuchten der Ringe ist eine Ausgabe. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Teile des mBot2 benennen. → Hallo mBot2 -- Ich kann erklären, wie der mBot2 Abstände misst. → Der Ultraschallsensor -- Ich kann erklären, wie der mBot2 eine Linie erkennt. → Unter dem Roboter -- Ich kann erklären, was Sensoren und Aktoren sind. → Sensoren und Aktoren -- Ich kann den mBot2 mit Elementen aus der Gruppe mBot2-Befehle steuern. → Befehle für den mBot2 -- Ich kann den mBot2 im Simulator fahren lassen. → Erste Fahrt -- Ich kann den echten mBot2 per USB-C verbinden und wieder trennen. → Den echten mBot2 verbinden -- Ich kann prüfen, ob alle Sensoren und Aktoren funktionieren. → Den mBot2 testen -::: - -## Wissenstest - -::: quiz -? Ordne jedes Teil seiner Aufgabe zu. -→ Hallo mBot2 -= Ultraschallsensor -> misst den Abstand -= Motoren -> bewegen die Räder -= CyberPi -> führt das Programm aus -= Liniensensor -> erkennt eine dunkle Linie -? Welche Teile sind Aktoren? -→ Sensoren und Aktoren -- [x] Motoren -- [ ] Ultraschallsensor -- [x] LEDs -- [ ] Liniensensor -> Aktoren tun etwas, Sensoren messen etwas. -? Wie misst der Ultraschallsensor den Abstand? -→ Der Ultraschallsensor -- [ ] Er fotografiert die Wand. -- [x] Er schickt einen sehr hohen Ton los und wartet auf das Echo. -- [ ] Er tastet die Wand mit seinen blauen Ringen ab. -> Je länger das Echo braucht, desto weiter ist die Wand weg. -? Woran erkennt der Liniensensor eine schwarze Linie? -→ Unter dem Roboter -- [x] Von der schwarzen Linie kommt wenig Licht zurück. -- [ ] Die Linie ist wärmer als der Boden. -- [ ] Er hört, wie die Räder über die Linie rollen. -> Jeder Fühler leuchtet den Boden an. Heller Boden wirft viel Licht zurück, eine schwarze Linie wenig. -? Was tut `mbot.turnRight()`? -→ Befehle für den mBot2 -- [ ] Der mBot2 fährt 10 cm nach rechts. -- [x] Der mBot2 dreht sich um 90° nach rechts. -- [ ] Die rechte LED geht an. -> Ohne Zahl in den Klammern dreht er sich um 90°. `mbot.turnRight(45)` dreht nur 45°. -? Im Struktogramm stehen fünf Kästen `mbot.forward()` untereinander. Wie weit fährt der mBot2 in der Simulation? -→ Erste Fahrt -- [ ] 5 cm -- [x] 50 cm -- [ ] 10 cm -> Jedes `mbot.forward()` fährt 10 cm, also ein Kästchen im Gitter. -? Du bist mit dem echten mBot2 fertig. Was machst du, bevor du das Kabel ziehst? -→ Den echten mBot2 verbinden -- [x] Noch einmal auf **🟢 mBot2** klicken und warten, bis „Bis bald!“ dasteht -- [ ] Den Schiebeschalter auf OFF schieben -- [ ] Nichts, das Kabel darf sofort raus -> Erst nach „Bis bald!“ ist der Anschluss frei. -? Du hältst die Hand vor den Ultraschallsensor, aber unter **🔧 mBot2 testen** ändert sich der Wert nicht. Was tust du? -→ Den mBot2 testen -- [ ] Trotzdem losprogrammieren -- [x] Der Lehrkraft Bescheid sagen, bevor du programmierst -- [ ] Den Wert in der Simulation von Hand ändern -> Ändert sich ein Wert nicht, obwohl du etwas tust, stimmt am Roboter etwas nicht. -::: diff --git a/web/lessons/de/visubot/02-das-quadrat.md b/web/lessons/de/visubot/02-das-quadrat.md deleted file mode 100644 index 7e58c265..00000000 --- a/web/lessons/de/visubot/02-das-quadrat.md +++ /dev/null @@ -1,365 +0,0 @@ ---- -titel: Das Quadrat -bereich: Losfahren -kurz: Vom langen Struktogramm zur dreifach verschachtelten Schleife. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Ablauf erst ohne Schleife bauen -- Wiederholungen erkennen und durch eine For-Schleife ersetzen -- zwei Schleifen ineinander schachteln -- drei Schleifen ineinander schachteln -- mein Programm auf den mBot2 übertragen und ohne Kabel mit Taste A starten -::: - -## Ein Quadrat fahren - -Der mBot2 soll ein Quadrat mit 50 cm Seitenlänge fahren. Eine Seite kennst du schon: fünfmal **fahre vor**. Danach dreht er sich nach rechts, und die nächste Seite beginnt. - -::: übung vb-quadrat -titel: Das Quadrat -auftrag: Baue ein Struktogramm, mit dem der mBot2 ein Quadrat fährt: fünfmal **fahre vor**, dann **drehe rechts**. Und das viermal. -fahrt: 4 x (5 x (forward), turnRight) -tipp: Eine Seite besteht aus fünfmal fahre vor und einmal drehe rechts. -tipp: Insgesamt sind es 24 Kästen: viermal fünf `mbot.forward()` und danach jeweils `mbot.turnRight()`. -lösung: vb-quadrat-loesung.visustruct -::: - -Ganz schön viele Kästen, oder? Wenn du dich beim Zählen vertust, fährt der mBot2 kein Quadrat mehr. - -## Immer das Gleiche - -Schau dein Struktogramm genau an: Fünfmal **fahre vor** steht viermal da. Wenn sich etwas wiederholt, nimmt man eine **Schleife**. Wir wissen genau, wie oft gefahren wird, nämlich fünfmal. Dafür passt die For-Schleife. - -Ziehe eine For-Schleife ins Struktogramm und gib ihr diese drei Teile: - -- Start: `int i = 0` -- Bedingung: `i < 5` -- Schritt: `i++` - -In den Inhalt der Schleife kommt ein einziges **fahre vor**. Die Schleife zählt `i` von 0 bis 4, das sind fünf Durchläufe. - -`i` ist ein **Zähler**. Der Computer merkt sich darin, im wievielten Durchlauf er gerade ist. So einen Speicher mit Namen nennt man **Variable**, genauer lernst du sie in Lektion 3 kennen. Die drei Teile bedeuten: - -| Teil | Bedeutung | -|---|---| -| `int i = 0` | Der Zähler `i` ist eine ganze Zahl (`int`) und fängt bei 0 an. | -| `i < 5` | Solange `i` kleiner als 5 ist, läuft die Schleife noch einmal. | -| `i++` | Nach jedem Durchlauf wird `i` um 1 größer. | - -So zählt die Schleife: - -| Durchlauf | 1 | 2 | 3 | 4 | 5 | danach | -|---|---|---|---|---|---|---| -| `i` | 0 | 1 | 2 | 3 | 4 | 5 | -| `i < 5`? | true | true | true | true | true | false, fertig | - -::: übung vb-quadrat-schleife -titel: Seiten mit Schleife -auftrag: Ersetze in deinem Quadrat jede Reihe aus fünf **fahre vor** durch eine For-Schleife, die fünfmal fährt. Der mBot2 fährt danach genau dasselbe Quadrat. -fahrt: 4 x (5 x (forward), turnRight) -braucht: forSchleife -tipp: Start `int i = 0`, Bedingung `i < 5`, Schritt `i++`. Im Inhalt steht nur `mbot.forward()`. -tipp: Viermal untereinander: die For-Schleife und danach `mbot.turnRight()`. -lösung: vb-quadrat-schleife-loesung.visustruct -::: - -## Schleife in der Schleife - -Jetzt steht viermal dasselbe Paar da: eine For-Schleife und **drehe rechts**. Auch das ist eine Wiederholung! Packe beides in eine zweite For-Schleife, die viermal läuft. Eine Schleife in einer Schleife heißt **verschachtelte Schleife**. - -Jede Schleife braucht ihre eigene Zählvariable, sonst kommen sich die beiden in die Quere. Üblich ist: Die äußere Schleife zählt mit `i`, die innere mit `j`. Benenne deine innere Schleife also in `j` um. - -::: übung vb-quadrat-verschachtelt -titel: Quadrat verschachtelt -auftrag: Baue das Quadrat mit zwei ineinander geschachtelten For-Schleifen. Die äußere läuft viermal, einmal für jede Seite. Die innere läuft fünfmal, einmal für jeden Schritt. -fahrt: 4 x (5 x (forward), turnRight) -schleifentiefe: 2 -tipp: Außen: Start `int i = 0`, Bedingung `i < 4`, Schritt `i++`. Innen: `int j = 0`, `j < 5`, `j++`. -tipp: In die äußere Schleife kommen die innere Schleife mit `mbot.forward()` und darunter `mbot.turnRight()`. -lösung: vb-quadrat-verschachtelt-loesung.visustruct -::: - -Aus 24 Kästen sind vier geworden. Gehe das Programm in der Simulation mit **Schritt** durch und beobachte die Variablen `i` und `j`. - -::: quiz -? Wie oft führt der mBot2 im verschachtelten Quadrat `mbot.forward()` aus? -- [ ] 9-mal -- [x] 20-mal -- [ ] 5-mal -> Die innere Schleife läuft fünfmal, und das in jedem der vier Durchläufe der äußeren Schleife: 4 · 5 = 20. -::: - -## Vier Quadrate - -Jetzt wird es richtig schön: Der mBot2 soll vier Quadrate fahren. Nach jedem Quadrat dreht er sich einmal nach rechts. So entsteht ein großes Fenster mit Fensterkreuz. - -Du könntest dein Quadrat viermal untereinander kopieren und nach jedem Quadrat ein **drehe rechts** einfügen. Probier es ruhig aus! Merkst du es? Schon wieder steht dasselbe viermal da. - -::: ludwig -Immer wenn du Kästen kopierst, frag dich: Wiederholt sich hier etwas? Dann passt eine Schleife. -::: - -::: übung vb-vier-quadrate -titel: Vier Quadrate -auftrag: Lass den mBot2 vier Quadrate fahren. Nach jedem Quadrat dreht er sich einmal nach rechts. Benutze drei ineinander geschachtelte Schleifen. -fahrt: 4 x (4 x (5 x (forward), turnRight), turnRight) -schleifentiefe: 3 -tipp: Lege um dein verschachteltes Quadrat noch eine dritte For-Schleife, die viermal läuft. -tipp: Ganz außen zählt jetzt `i` (`int i = 0`, `i < 4`, `i++`). Die Schleifen darin heißen dann `j` und `k`. In die äußere Schleife kommen dein Quadrat und danach `mbot.turnRight()`. -lösung: vb-vier-quadrate-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektionen [„For-Schleife“](?visustruct&lektion=13-for-schleife) und [„Verschachteln“](?visustruct&lektion=14-verschachteln). Sie öffnen sich in einem neuen Fenster. - -::: quiz -? Wie oft fährt der mBot2 bei den vier Quadraten 10 cm vor? -→ Vier Quadrate -- [ ] 20-mal -- [ ] 60-mal -- [x] 80-mal -> 4 Quadrate · 4 Seiten · 5 Schritte = 80. Ohne Schleifen wären das 80 Kästen. -::: - -## Ohne Kabel fahren - -::: mbot2 -fokus: cyberpi, buttons -ansicht: oben -::: - -Bisher hängt der mBot2 am Kabel, solange er fährt. Du kannst dein Programm aber auch **auf den mBot2 übertragen**. Dann merkt er es sich und fährt ganz allein, zum Beispiel durch ein Labyrinth auf dem Boden. - -1. Schließ den mBot2 mit dem USB-Kabel an und schalte ihn ein, wie in Lektion 1. Das geht nur in **Chrome** oder **Edge**. -2. Öffne dein Programm, zum Beispiel die vier Quadrate. -3. Klick oben auf **⬆ Übertragen** und dann auf **Jetzt übertragen**. Wähle wieder **USB Serial**. -4. Warte, bis „Fertig!“ dasteht. Der mBot2 startet neu und zeigt **A = Start**. -5. Zieh das Kabel ab und setz den mBot2 an den Start. -6. Drück **Taste A**, das ist die [Taste mit dem Quadrat]{teil=buttons}. Jetzt fährt dein Programm los. - -Das Programm bleibt gespeichert, auch wenn du den mBot2 aus- und wieder einschaltest. Erst das nächste Übertragen ersetzt es. - -> Fährt er los, bevor du bereit bist? Keine Sorge: Er wartet immer auf Taste A. Drückst du A noch einmal, während er fährt, passiert nichts. Ist er fertig, startet A das Programm von vorn. - -## Spiel: Wie oft fährt er? - -::: spiel frage -titel: Spiel: Wie oft fährt er? -intro: Zähle mit, ohne die Simulation zu starten. Wie oft fährt oder dreht sich der mBot2? Sechs Runden. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(); - mbot.turnRight(); - } -frage: Wie oft fährt der mBot2 vor? -- 3-mal -- [x] 4-mal -- 5-mal -> `i` zählt 0, 1, 2 und 3. Das sind vier Durchläufe. - -runde: - for (int i = 1; i <= 4; i++) { - mbot.forward(); - } -frage: Wie oft fährt der mBot2 vor? -- 3-mal -- [x] 4-mal -- 5-mal -> Diesmal zählt `i` von 1 bis 4, wegen `<=`. Auch das sind vier Durchläufe. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Wie oft fährt der mBot2 vor? -- 8-mal -- [x] 15-mal -- 5-mal -> Die innere Schleife läuft in jedem der drei äußeren Durchläufe fünfmal: 3 · 5 = 15. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Wie oft dreht sich der mBot2 nach rechts? -- [x] 3-mal -- 15-mal -- 5-mal -> `mbot.turnRight()` steht in der äußeren Schleife, nicht in der inneren. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - } - mbot.turnRight(); -frage: Fährt der mBot2 ein Quadrat? -- Ja, mit 50 cm Seitenlänge. -- [x] Nein, er fährt 200 cm geradeaus und dreht sich erst am Ende. -- Ja, aber nur ein halbes. -> `mbot.turnRight()` steht unter der Schleife. Es läuft erst, wenn die Schleife fertig ist. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - mbot.forward(30); - mbot.turnRight(); - } - mbot.turnRight(); - } -frage: Wie viele Quadrate fährt der mBot2? -- [x] 2 -- 4 -- 8 -> Die innere Schleife fährt ein Quadrat. Die äußere wiederholt das zweimal. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 fahren soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(50); - mbot.turnRight(); - } -frage: Soll ein Quadrat mit 50 cm Seitenlänge fahren. -fehler: 1 -besser: for (int i = 0; i < 4; i++) { -> Ein Quadrat hat vier Seiten. Mit `i < 3` fährt er nur drei. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(180); - } -frage: Soll ein Quadrat mit 50 cm Seitenlänge fahren. -fehler: 3 -besser: mbot.turnRight(); -> Mit 180° kehrt er um und fährt immer hin und her. Ein Quadrat braucht 90°. - -runde: - for (int i = 0; i < 4; i++) { - for (int i = 0; i < 5; i++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Soll ein Quadrat aus viermal fünf Schritten fahren. -fehler: 2 -besser: for (int j = 0; j < 5; j++) { -> Die innere Schleife braucht ihre eigene Zählvariable `j`. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnLeft(45); - } -frage: Soll ein Quadrat mit 30 cm Seitenlänge fahren. -fehler: 3 -besser: mbot.turnLeft(); -> Ein Quadrat braucht rechte Winkel, also 90°. Ohne Zahl dreht er sich 90°. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..... | ..^.. -ziel: 2,0 -> Dreimal ein Feld nach vorn. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 1,2 -> Ein ganzes Quadrat: Er kommt genau dort an, wo er losgefahren ist. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 2,2 -> Nur drei Seiten: Die vierte fehlt, also steht er rechts neben dem Start. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - mbot.forward(30); - } - mbot.turnRight(); - } -karte: .... | .... | .... | ^... -ziel: 2,1 -> Die innere Schleife fährt zwei Felder, dann dreht er sich. Das Ganze zweimal: zwei Felder hoch, zwei nach rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Ablauf erst ohne Schleife bauen. → Ein Quadrat fahren -- Ich kann Wiederholungen erkennen und durch eine For-Schleife ersetzen. → Immer das Gleiche -- Ich kann zwei Schleifen ineinander schachteln. → Schleife in der Schleife -- Ich kann drei Schleifen ineinander schachteln. → Vier Quadrate -- Ich kann mein Programm auf den mBot2 übertragen und ohne Kabel mit Taste A starten. → Ohne Kabel fahren -::: - -## Wissenstest - -::: quiz -? Der mBot2 soll noch ohne Schleife eine Seite von 50 cm fahren und sich dann drehen. Welche Kästen brauchst du? -→ Ein Quadrat fahren -- [ ] einmal `mbot.forward()` und einmal `mbot.turnRight()` -- [x] fünfmal `mbot.forward()` und einmal `mbot.turnRight()` -- [ ] fünfmal `mbot.turnRight()` -> Jedes `mbot.forward()` fährt 10 cm, fünf davon sind 50 cm. -? Eine For-Schleife hat den Start `int i = 0`, die Bedingung `i < 5` und den Schritt `i++`. Wie oft läuft sie? -→ Immer das Gleiche -- [ ] 4-mal -- [x] 5-mal -- [ ] 6-mal -> `i` zählt 0, 1, 2, 3, 4. Das sind fünf Durchläufe. -? Warum heißt die Zählvariable der inneren Schleife `j` und nicht auch `i`? -→ Schleife in der Schleife -- [ ] In einem Programm darf jeder Buchstabe nur einmal vorkommen. -- [x] Jede Schleife braucht ihre eigene Zählvariable, sonst kommen sich die beiden in die Quere. -- [ ] `j` zählt schneller als `i`. -> Würden beide mit `i` zählen, würde die innere Schleife die äußere durcheinanderbringen. -? Wie oft führt der mBot2 bei den vier Quadraten `mbot.turnRight()` aus? -→ Vier Quadrate -- [ ] 4-mal -- [ ] 16-mal -- [x] 20-mal -> In jedem Quadrat dreht er sich viermal, das sind 4 · 4 = 16. Dazu kommt nach jedem Quadrat noch eine Drehung: 16 + 4 = 20. -? Dein Programm ist übertragen und das Kabel ist ab. Wie startest du es? -→ Ohne Kabel fahren -- [ ] mit Taste B -- [x] mit Taste A -- [ ] mit **⬆ Übertragen** -> Nach dem Übertragen zeigt der mBot2 **A = Start** und wartet auf Taste A. -::: diff --git a/web/lessons/de/visubot/02-quadrat-als-pap.md b/web/lessons/de/visubot/02-quadrat-als-pap.md deleted file mode 100644 index 3fc9a0b6..00000000 --- a/web/lessons/de/visubot/02-quadrat-als-pap.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -titel: PAP und Pseudocode -bereich: Losfahren -kurz: Den Plan für das Quadrat als Programmablaufplan und als Pseudocode aufschreiben und mit dem Struktogramm vergleichen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen PAP (englisch *flowchart*) für den mBot2 lesen und selbst zusammenbauen -- einen Plan als Pseudocode lesen -- Struktogramm, PAP und Pseudocode vergleichen -::: - -## Drei Arten, einen Plan aufzuschreiben - -Den Plan für das Quadrat kennst du als **Struktogramm**: eine Schleife, die viermal läuft, darin eine Seite fahren und rechts drehen. - -Es gibt noch zwei weitere Arten, denselben Plan aufzuschreiben. Beide wirst du im Unterricht und im Internet oft treffen: - -- den **Programmablaufplan**, kurz **PAP**, auf Englisch **flowchart**, auf Deutsch auch Flussdiagramm, -- den **Pseudocode**, einen Plan aus Text in normaler Sprache. - -Den mBot2 kannst du damit nicht direkt steuern. Aber mit allen drei kannst du einen Plan aufschreiben, bevor du ihn als Struktogramm oder in Java baust. - -## Der PAP - -Ein PAP besteht aus wenigen Symbolen, die mit **Pfeilen** verbunden sind: - -- **Oval:** Start oder Ende. -- **Rechteck:** eine Anweisung, zum Beispiel *50 cm vorwärts fahren*. -- **Raute:** eine Frage mit zwei Ausgängen: **ja** und **nein**. -- **Parallelogramm:** eine Ein- oder Ausgabe, zum Beispiel ein Text auf dem Bildschirm. -- **Pfeil:** zeigt, welcher Schritt als Nächstes kommt. - -Eine Schleife hat im PAP kein eigenes Symbol. Du erkennst sie an einem Pfeil, der **zurück nach oben** führt. So lange die Antwort in der Raute **nein** ist, geht es zurück, und die Schritte laufen noch einmal. - -> Vorsicht: Ein Pfeil darf im PAP überallhin zeigen, auch kreuz und quer. Bei großen Plänen wird das schnell unübersichtlich. Das nennt man **Spaghetticode**. Im Struktogramm kann das nicht passieren, weil es keine Pfeile gibt. - -## Übung: Das Quadrat als PAP - -Der mBot2 fährt eine Seite, dreht sich rechts und zählt die Seite mit. Nach vier Seiten zeigt er auf dem Bildschirm „Quadrat fertig!“. - -::: pap -titel: Übung: Das Quadrat als PAP -auftrag: Die Plätze im Plan sind leer. Lege jedes Element an seinen Platz. Die Form des Platzes hilft dir. -start: Start -anweisung: 50 cm vorwärts fahren -anweisung: rechts drehen -anweisung: Seite mitzählen -frage: 4 Seiten gefahren? | nein -> 50 cm vorwärts fahren -ausgabe: „Quadrat fertig!“ anzeigen -ende: Ende -erklärung: Solange noch keine vier Seiten gefahren sind, führt der Pfeil von der Raute zurück zu „50 cm vorwärts fahren“. Das ist die Schleife. Die Anzeige auf dem Bildschirm ist eine Ausgabe und steht deshalb im Parallelogramm. -legende: ja -::: - -**Eigene PAPs zeichnen:** Mit **VisuFlow** zeichnest du deinen PAP ganz leicht selbst, zum Beispiel für deine nächste Roboterfahrt. Die Symbole liegen fertig in einer Leiste: Antippen hängt das nächste Symbol an und verbindet es gleich mit einem Pfeil. Deinen PAP speicherst du als Datei und exportierst ihn als Bild oder PDF, zum Beispiel für dein Heft oder eine Präsentation. [VisuFlow öffnen](https://visustruct.visucode.de/app/?visuflow) - -## Pseudocode - -**Pseudocode** ist Text, der wie ein Programm aufgebaut ist, aber in normaler Sprache geschrieben wird. Feste Wörter wie WIEDERHOLE, WENN und SOLANGE zeigen die Bausteine, die **Einrückung** zeigt, was dazugehört. Das Quadrat sieht so aus: - -``` -WIEDERHOLE 4-mal - 50 cm vorwärts fahren - rechts drehen -ENDE WIEDERHOLE -"Quadrat fertig!" anzeigen -``` - -Und so sieht derselbe Plan in Java aus: - -```java -for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(); -} -mbot.showText("Quadrat fertig!"); -``` - -Pseudocode hat **keine feste Schreibweise**. Manche schreiben FÜR i VON 1 BIS 4, andere WIEDERHOLE 4-mal. Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. Den Computer kannst du damit nicht steuern, dafür brauchst du Java. - -### Im Vergleich - -- **Struktogramm:** Kästen ineinander, die Schleife ist ein gekipptes L. Die Struktur siehst du auf einen Blick. -- **PAP:** Symbole mit Pfeilen, die Schleife ist ein Pfeil zurück nach oben. Sehr anschaulich, aber bei großen Plänen unübersichtlich. -- **Pseudocode:** Text mit Einrückung, die Schleife steht zwischen WIEDERHOLE und ENDE WIEDERHOLE. Schnell geschrieben und schon nah an Java. - -::: angeberwissen -Määäh, wer hat die Ablaufpläne erfunden? Eine Frau und ihr Mann: **Lillian und Frank Gilbreth** stellten 1921 ihre „Process Charts“ vor, lange vor dem ersten Computer. Damit zeichneten sie Arbeitsabläufe in Fabriken auf, um sie einfacher zu machen. Lillian Gilbreth hatte einen Doktortitel in Psychologie und wurde 1965 als erste Frau in die amerikanische Akademie der Ingenieure gewählt. Programmierer haben die Idee später für ihre Pläne übernommen. -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„PAP und Pseudocode“](?visustruct&lektion=01-weitere-darstellungen) mit Kuchenbacken als PAP und dem Befehl GOTO. Sie öffnet sich in einem neuen Fenster. - -## Spiel: Welche Darstellung? - -::: spiel frage -titel: Spiel: Welche Darstellung? -intro: Ludwig beschreibt einen Plan für den mBot2, ohne zu sagen, wie er aufgeschrieben ist. Ist es das Struktogramm, der PAP oder der Pseudocode? Sechs Runden. -antworten: Struktogramm | PAP | Pseudocode - -runde: -frage: „Ein Pfeil führt von der Raute zurück zu ‚50 cm vorwärts fahren‘.“ -richtig: PAP -> Eine Schleife im PAP erkennst du am Pfeil zurück nach oben. - -runde: -frage: „Die Schleife sieht aus wie ein gekipptes L, darin stehen fahre vor und drehe rechts.“ -richtig: Struktogramm -> Das gekippte L ist die Schleife im Struktogramm. - -runde: -frage: „WIEDERHOLE 4-mal, darunter eingerückt zwei Zeilen.“ -richtig: Pseudocode -> Feste Wörter und Einrückung: Das ist Pseudocode. - -runde: -frage: „‚Quadrat fertig!‘ steht in einem schrägen Kasten.“ -richtig: PAP -> Der schräge Kasten ist das Parallelogramm für Ein- und Ausgabe. - -runde: -frage: „Es gibt keine Pfeile. Fügst du einen Kasten ein, rutscht alles darunter einfach mit.“ -richtig: Struktogramm -> Ohne Pfeile kann im Struktogramm kein Sprung auf die falsche Stelle zeigen. - -runde: -frage: „Manche schreiben FÜR i VON 1 BIS 4, andere WIEDERHOLE 4-mal. Beides ist richtig.“ -richtig: Pseudocode -> Pseudocode hat keine feste Schreibweise. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Elemente eines PAP benennen. → Der PAP -- Ich kann das Quadrat selbst als PAP zusammenbauen. → Übung: Das Quadrat als PAP -- Ich kann einen Plan als Pseudocode lesen. → Pseudocode -- Ich kann Struktogramm, PAP und Pseudocode vergleichen. → Drei Arten, einen Plan aufzuschreiben -::: - -## Wissenstest - -::: quiz -? Woran erkennst du im PAP eine Schleife? -→ Der PAP -- [ ] An einem gekippten L -- [x] An einem Pfeil, der zurück nach oben führt -- [ ] Am Wort WIEDERHOLE -> Das gekippte L gehört zum Struktogramm, WIEDERHOLE zum Pseudocode. - -? Ordne jedem Symbol im PAP seine Bedeutung zu. -→ Der PAP -= Oval -> Start oder Ende -= Rechteck -> Anweisung -= Raute -> Frage mit ja und nein -= Parallelogramm -> Ein- oder Ausgabe -> „50 cm vorwärts fahren“ ist eine Anweisung, „Quadrat fertig!“ anzeigen eine Ausgabe. - -? Im Quadrat-PAP steht „4 Seiten gefahren?“ in der Raute. Wohin führt der Pfeil bei **nein**? -→ Übung: Das Quadrat als PAP -- [x] Zurück zu „50 cm vorwärts fahren“ -- [ ] Zum Ende -- [ ] Zu „Quadrat fertig!“ anzeigen -> Bei nein ist das Quadrat noch nicht fertig, also fährt der mBot2 die nächste Seite. - -? Was zeigt im Pseudocode, welche Zeilen zur Schleife gehören? -→ Pseudocode -- [ ] Ein Pfeil -- [x] Die Einrückung und das ENDE WIEDERHOLE -- [ ] Die Farbe -> Alles zwischen WIEDERHOLE und ENDE WIEDERHOLE ist eingerückt und wird wiederholt. - -? Kann der mBot2 Pseudocode direkt ausführen? -→ Drei Arten, einen Plan aufzuschreiben -- [ ] Ja, wie Java -- [x] Nein, Pseudocode ist nur ein Plan für Menschen -> Ausführen kann der mBot2 das Struktogramm oder Java. Pseudocode hilft beim Planen. -::: diff --git a/web/lessons/de/visubot/03-variablen-und-ausgabe.md b/web/lessons/de/visubot/03-variablen-und-ausgabe.md deleted file mode 100644 index 064d7a4f..00000000 --- a/web/lessons/de/visubot/03-variablen-und-ausgabe.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -titel: Variablen und Ausgabe -bereich: Losfahren -kurz: Werte in Variablen speichern, den mBot2 damit fahren lassen und Ergebnisse auf dem Bildschirm zeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable anlegen und benutzen -- ein kurzes Java-Programm lesen und im Bereich Code tippen -- die Datentypen `int`, `double`, `String` und `boolean` unterscheiden -- mit zwei Variablen ein Rechteck in Java fahren -- mit `mbot.showText` ein Ergebnis auf dem Bildschirm zeigen -::: - -## Eine Variable für die Seite - -Mit `mbot.forward(30)` fährt der mBot2 30 cm am Stück. Fährst du damit ein Quadrat ohne Schleife, steht die 30 aber viermal im Programm. Soll das Quadrat größer werden, musst du sie an vier Stellen ändern. Besser ist es, die Seitenlänge **einmal** zu speichern und dann überall zu benutzen. - -Dafür gibt es **Variablen**. Eine Variable ist ein Kästchen mit einem Namen, in dem ein Wert liegt: - -```java -int seite = 30; -mbot.forward(seite); -``` - -- `int` ist der **Datentyp**: eine ganze Zahl. -- `seite` ist der **Name**. Du darfst ihn selbst wählen. -- `30` ist der **Wert**. Das `=` legt ihn in das Kästchen. - -Wo danach `seite` steht, setzt der Computer den Wert ein. `mbot.forward(seite)` fährt also 30 cm. - -::: übung vb-variable-seite -titel: Quadrat mit Variable -auftrag: Lege in einer Anweisung die Variable `int seite = 30` an. Danach fährt der mBot2 ein Quadrat: viermal `seite` Zentimeter vor und nach rechts drehen. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: Die erste Anweisung ist `int seite = 30`. Darunter kommt eine For-Schleife, die viermal läuft. -tipp: In der Schleife stehen `mbot.forward(seite)` und `mbot.turnRight()`. -lösung: vb-variable-seite-loesung.visustruct -::: - -> Ändere jetzt nur die 30 in eine 60 und starte die Simulation. Das ganze Quadrat wird doppelt so groß. - -## Welche Datentypen gibt es? - -Jede Variable hat einen Datentyp. Er legt fest, welche Werte hineinpassen: - -| Datentyp | Was passt hinein? | Beispiel | -|---|---|---| -| `int` | ganze Zahlen | `int seite = 30;` | -| `double` | Kommazahlen, mit Punkt geschrieben | `double radumfang = 20.4;` | -| `String` | Text in Anführungszeichen | `String name = "Ludwig";` | -| `boolean` | wahr oder falsch | `boolean wandNah = mbot.getDistance() < 20;` | - -Ein Rad des mBot2 kommt mit einer Umdrehung etwa 20.4 cm weit, das ist eine Kommazahl. In `wandNah` steht `true`, wenn die Wand näher als 20 cm ist. Den Abstandssensor lernst du in Lektion 4 kennen. - -Mit Variablen kannst du auch rechnen: `int strecke = 4 * seite;` rechnet die ganze Strecke des Quadrats aus. - -## So sieht Java aus - -Bisher hast du Kästen gezogen. Ab jetzt schreibst du manche Programme auch in **Java**. Die Kästen kennst du schon, denn ihr Text ist bereits echtes Java. Rechts neben dem Struktogramm liegt der Bereich [Code](#zeige-codelive). Dort steht dein Struktogramm als Java-Code, und was du dort tippst, wird sofort zum Struktogramm. - -Das Quadrat mit Variable sieht in Java so aus: - -```java -int seite = 30; -for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); -} -``` - -- Jeder Kasten ist eine Zeile. Am Ende jeder Anweisung steht ein **Semikolon** `;`. -- Die For-Schleife heißt `for`. In den runden Klammern stehen ihre drei Teile Start, Bedingung und Schritt, getrennt durch `;`. -- Was in der Schleife wiederholt wird, steht in **geschweiften Klammern** `{ }`. Rücke es ein, dann siehst du sofort, was dazugehört. Hinter `{` und `}` kommt kein Semikolon. -- Groß- und Kleinschreibung zählt: `mbot.turnRight()` geht, `mbot.turnright()` nicht. - -> So löst du eine Java-Übung: Klicke auf **Im Editor öffnen**. Dann öffnet sich rechts der Bereich Code. Tippe dort dein Programm, sieh in der Simulation zu und klicke zum Schluss auf **Prüfen**. - -## Jetzt in Java: Rechteck - -Ein Rechteck hat zwei verschiedene Seiten. Also brauchst du zwei Variablen. - -::: übung vb-java-rechteck -titel: Rechteck mit Variablen -code: java -auftrag: Schreibe in Java: Lege `int laenge = 60` und `int breite = 30` an. Der mBot2 fährt ein Rechteck: Länge, rechts drehen, Breite, rechts drehen, und das zweimal. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -braucht: forSchleife -tipp: Zwei Variablen oben: `int laenge = 60;` und `int breite = 30;`. Darunter eine For-Schleife, die zweimal läuft. -tipp: In der Schleife: `mbot.forward(laenge);`, `mbot.turnRight();`, `mbot.forward(breite);`, `mbot.turnRight();` -lösung: vb-java-rechteck-loesung.visustruct -::: - -## Ergebnis auf dem Bildschirm - -Der mBot2 kann nicht nur fahren, er kann dir auch etwas zeigen. Mit `mbot.showText(…)` steht ein Text auf dem [Bildschirm]{teil=screen} des CyberPi. Mit `+` hängst du Text und Werte aneinander: - -```java -int seite = 30; -int strecke = 4 * seite; -mbot.showText("Strecke: " + strecke + " cm"); -``` - -Der Bildschirm zeigt dann **Strecke: 120 cm**. Den Bildschirm lernst du in Lektion 18 noch genauer kennen. - -::: übung vb-strecke-anzeigen -titel: Wie weit fährt er? -code: java -auftrag: Lege `int laenge = 60` und `int breite = 30` an und fahre das Rechteck wie eben. Berechne danach in einer Variablen `strecke`, wie weit der mBot2 einmal ganz herum gefahren ist, und zeige **Strecke: 180 cm** auf dem Bildschirm. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -befehle: showText "Strecke: 180 cm" -braucht: forSchleife -tipp: Einmal herum sind zweimal die Länge und zweimal die Breite: `int strecke = 2 * laenge + 2 * breite;` -tipp: Nach der Schleife: `mbot.showText("Strecke: " + strecke + " cm");` -lösung: vb-strecke-anzeigen-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Eingabe und Ausgabe“](?visustruct&lektion=06-eingabe-und-ausgabe). Sie öffnet sich in einem neuen Fenster. Dort gibst du Ergebnisse mit `System.out.println` in der Konsole aus. - -## Spiel: Welcher Datentyp? - -::: spiel frage -titel: Spiel: Welcher Datentyp? -intro: Ludwig hat die Datentypen weggewischt. Welcher passt in die Lücke? Sieben Runden. -antworten: int | double | String | boolean - -runde: - ___ seite = 30; -frage: Welcher Datentyp passt? -richtig: int -> 30 ist eine ganze Zahl. - -runde: - ___ radumfang = 20.4; -frage: Welcher Datentyp passt? -richtig: double -> 20.4 ist eine Kommazahl, in Java mit Punkt geschrieben. - -runde: - ___ name = "Ludwig"; -frage: Welcher Datentyp passt? -richtig: String -> Text in Anführungszeichen ist ein `String`. - -runde: - ___ wandNah = mbot.getDistance() < 20; -frage: Welcher Datentyp passt? -richtig: boolean -> Ein Vergleich ist wahr oder falsch, also `boolean`. - -runde: - ___ abstand = mbot.getDistance(); -frage: Welcher Datentyp passt? -richtig: int -> Der Abstandssensor liefert ganze Zentimeter, zum Beispiel 38. - -runde: - ___ gruss = "Hallo " + "Klasse"; -frage: Welcher Datentyp passt? -richtig: String -> Zwei Texte mit `+` ergeben wieder einen Text. - -runde: - ___ mittelwert = 120 / 5.0; -frage: Welcher Datentyp passt? -richtig: double -> Mit `5.0` rechnet Java mit Kommazahlen. Das Ergebnis ist 24.0. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Achte auf die Werte in den Variablen. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - int seite = 60; - mbot.forward(seite); - mbot.turnRight(); - mbot.forward(seite); -karte: ... | ... | ^.. -ziel: 2,0 -> `seite` ist 60, also zwei Felder hoch und zwei nach rechts. - -runde: - int a = 30; - int b = a * 3; - mbot.forward(b); -karte: ... | ... | ... | ^.. -ziel: 0,0 -> `b` ist 3 · 30 = 90 cm, also drei Felder. - -runde: - int strecke = 30 * 2; - mbot.backward(strecke); -karte: ..^.. | ..... | ..... -ziel: 2,2 -> `backward` fährt rückwärts: Er schaut nach oben, fährt aber 60 cm nach unten. - -runde: - int seite = 30; - for (int i = 0; i < 2; i++) { - mbot.forward(seite); - mbot.turnRight(); - seite = seite + 30; - } -karte: .... | .... | .... | ^... -ziel: 2,2 -> Erst 30 cm hoch, dann wächst `seite` auf 60: zwei Felder nach rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable anlegen und benutzen. → Eine Variable für die Seite -- Ich kann ein kurzes Java-Programm lesen und im Bereich Code tippen. → So sieht Java aus -- Ich kann die Datentypen `int`, `double`, `String` und `boolean` unterscheiden. → Welche Datentypen gibt es? -- Ich kann mit zwei Variablen ein Rechteck in Java fahren. → Jetzt in Java: Rechteck -- Ich kann mit `mbot.showText` ein Ergebnis auf dem Bildschirm zeigen. → Ergebnis auf dem Bildschirm -::: - -## Wissenstest - -::: quiz -? Was fährt `mbot.forward(seite)`, wenn vorher `int seite = 40;` steht? -→ Eine Variable für die Seite -- [ ] 4 cm -- [x] 40 cm -- [ ] gar nichts, `seite` ist kein Befehl -> Wo `seite` steht, setzt Java den Wert 40 ein. -? Welcher Datentyp passt zu `"Ludwig"`? -→ Welche Datentypen gibt es? -- [ ] `int` -- [ ] `double` -- [x] `String` -> Text in Anführungszeichen ist ein `String`. -? Was zeigt `mbot.showText("Seite: " + 30);` auf dem Bildschirm? -→ Ergebnis auf dem Bildschirm -- [x] Seite: 30 -- [ ] "Seite: " + 30 -- [ ] 30 -> Das `+` hängt den Text und die Zahl aneinander. -::: diff --git a/web/lessons/de/visubot/03-zeile-fuer-zeile-testen.md b/web/lessons/de/visubot/03-zeile-fuer-zeile-testen.md deleted file mode 100644 index 622a0f89..00000000 --- a/web/lessons/de/visubot/03-zeile-fuer-zeile-testen.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -titel: Schritt für Schritt testen -bereich: Losfahren -kurz: Mit der Simulation und dem Schreibtischtest herausfinden, was dein Programm wirklich tut. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Simulation mit Start, Schritt und Stopp steuern -- in der Simulation die Variablen beobachten -- einen Schreibtischtest mit einer Tabelle machen -- mit der Simulation einen Fehler finden und reparieren -::: - -## Die Simulation steuern - -Mit dem Knopf [▶︎ Simulation starten](#zeige-simulation) rechts bei den Werkzeugen führt VisuStruct dein Struktogramm aus. In der VisuBot-Welt fährt der mBot2 dabei mit. Rechts erscheint die **Steuerung**: - -- **Start** führt das Programm automatisch Schritt für Schritt aus. Wie schnell, stellst du bei **Pause pro Schritt** ein. -- **Pause** hält an. Danach geht es mit Start oder Schritt weiter. -- **Schritt** führt genau einen Kasten aus. So kannst du in Ruhe zuschauen. -- **Stopp** bricht ab und stellt den mBot2 zurück an den Start. - -Während der Simulation ist ein Kasten farbig markiert. Unter **Letzter Schritt** steht, was gerade passiert ist, unter **Variablen** stehen alle Variablen mit ihren Werten. - -::: diagramm vb-l03-wachsend-loesung.visustruct -Jede Seite wird 10 cm länger. Öffne das Diagramm im Editor und gehe es mit **Schritt** durch. Beobachte dabei `seite` und `i`. -::: - -## Der Schreibtischtest - -Beim **Schreibtischtest** spielst du selbst den Computer. Du gehst das Struktogramm mit Stift und Papier durch und schreibst nach jedem Schritt die Werte der Variablen auf. Für das Diagramm oben sieht die Tabelle so aus: - -| Schritt | `i` | `seite` | Der mBot2 … | -|---|---|---|---| -| `int seite = 20` | – | 20 | | -| `mbot.forward(seite)` | 0 | 20 | fährt 20 cm | -| `mbot.turnRight()` | 0 | 20 | dreht rechts | -| `seite = seite + 10` | 0 | 30 | | -| `mbot.forward(seite)` | 1 | 30 | fährt 30 cm | -| … | … | … | … | - -Vergleiche deine Tabelle danach mit der Simulation. Stimmen die Werte unter **Variablen** nach jedem Schritt? - -::: angeberwissen -Früher war „Computer“ ein Beruf: Menschen, die mit Stift und Papier rechneten. Eine der besten war Katherine Johnson bei der NASA. 1962 wollte der Astronaut John Glenn erst losfliegen, als sie die Flugbahn des neuen IBM-Rechners von Hand nachgerechnet hatte: „Wenn sie sagt, die Zahlen stimmen, fliege ich.“ -::: - -## Fehler finden mit der Simulation - -Ludwig wollte die Spirale von oben bauen: 20 cm, 30 cm, 40 cm. Sein mBot2 fährt aber 30, 40 und 50 cm. Das Programm läuft ohne Fehlermeldung, es tut nur das Falsche. So einen Fehler nennt man **logischen Fehler**. Ihn findest du am besten mit **Schritt** und den Variablen. - -::: übung vb-spirale-falsch -titel: Die Spirale reparieren -auftrag: „Im Editor öffnen“ lädt Ludwigs Struktogramm. Gehe es mit **Schritt** durch und beobachte `seite`. Finde heraus, warum die erste Seite 30 cm lang ist, und repariere es. Der mBot2 soll 20, 30 und 40 cm fahren und nach jeder Seite rechts drehen. -start: vb-spirale-falsch-start.visustruct -fahrt: forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -braucht: forSchleife -tipp: Achte darauf, welchen Wert `seite` hat, wenn `mbot.forward(seite)` drankommt. Ist die Reihenfolge in der Schleife richtig? -tipp: `seite = seite + 10` muss **nach** `mbot.forward(seite)` und `mbot.turnRight()` stehen. Zieh den Kasten ans Ende der Schleife. -lösung: vb-spirale-falsch-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Simulation und Schreibtischtest“](?visustruct&lektion=07-simulation-und-schreibtischtest). Sie öffnet sich in einem neuen Fenster. - -## Spiel: Spiel den Computer - -::: spiel frage -titel: Spiel: Spiel den Computer -intro: Mach in deinem Kopf den Schreibtischtest. Was tut der mBot2, und welche Werte haben die Variablen? Fünf Runden. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite + 10; - } -frage: Welchen Wert hat `seite` nach der Schleife? -- 40 -- [x] 50 -- 30 -> 20, dann 30, 40 und nach dem dritten Durchgang 50. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite + 10; - } -frage: Wie weit fährt der mBot2 beim dritten Mal? -- [x] 40 cm -- 50 cm -- 30 cm -> Er fährt 20, 30 und 40 cm. Die 50 steht erst danach in `seite`. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - seite = seite + 10; - mbot.forward(seite); - } -frage: Wie weit fährt der mBot2 beim ersten Mal? -- 20 cm -- [x] 30 cm -- 10 cm -> `seite` wächst schon vor dem Fahren. Genau das war Ludwigs logischer Fehler. - -runde: - int strecke = 0; - for (int i = 0; i < 4; i++) { - mbot.forward(25); - strecke = strecke + 25; - } - mbot.showText("Strecke: " + strecke); -frage: Was zeigt der Bildschirm am Ende? -- Strecke: 25 -- [x] Strecke: 100 -- Strecke: 0 -> Viermal kommen 25 dazu: 25, 50, 75, 100. - -runde: - int seite = 10; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite * 2; - } -frage: Wie weit fährt der mBot2 insgesamt? -- 60 cm -- [x] 70 cm -- 80 cm -> Er fährt 10, 20 und 40 cm. Zusammen sind das 70 cm. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 8 4 SCHLEIFE: wiederholt Schritte, im Struktogramm wie ein gekipptes L -- ↓ 1 6 ALGORITHMUS: genaue Schritt-für-Schritt-Anleitung -- ↓ 4 8 SEQUENZ: Schritte, die nacheinander kommen -- → 1 1 SIMULATION: führt dein Programm am Bildschirm aus, auch ohne Roboter -- ↓ 8 4 STRUKTOGRAMM: Plan aus Kästen, den du von oben nach unten liest -- → 16 2 VARIABLE: Kästchen mit Namen, in dem ein Wert liegt -- → 13 2 DATENTYP: legt fest, welche Werte in eine Variable passen, z. B. int -- ↓ 4 11 SCHREIBTISCHTEST: du spielst mit Stift und Papier den Computer -lösung: 10 4, 4 6, 16 7, 14 4, 13 4, 13 5, 7 11 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Simulation mit Start, Schritt und Stopp steuern. → Die Simulation steuern -- Ich kann in der Simulation die Variablen beobachten. → Die Simulation steuern -- Ich kann einen Schreibtischtest mit einer Tabelle machen. → Der Schreibtischtest -- Ich kann mit der Simulation einen Fehler finden und reparieren. → Fehler finden mit der Simulation -::: - -## Wissenstest - -::: quiz -? Welcher Knopf führt genau einen Kasten aus? -→ Die Simulation steuern -- [ ] Start -- [x] Schritt -- [ ] Stopp -> Mit Schritt geht es Kasten für Kasten. Start läuft von allein weiter. -? Wie lang fährt der mBot2 im zweiten Durchgang von „Wachsende Seiten“? -→ Der Schreibtischtest -- [ ] 20 cm -- [x] 30 cm -- [ ] 40 cm -> Nach dem ersten Durchgang ist `seite` um 10 gewachsen, also 30. -? Das Programm läuft ohne Meldung, der mBot2 fährt aber falsch. Was für ein Fehler ist das? -→ Fehler finden mit der Simulation -- [ ] ein Tippfehler -- [x] ein logischer Fehler -- [ ] ein Fehler im Roboter -> Das Programm tut genau, was dasteht, nur nicht das, was gemeint war. -::: diff --git a/web/lessons/de/visubot/04-sensoren.md b/web/lessons/de/visubot/04-sensoren.md deleted file mode 100644 index f905b0d8..00000000 --- a/web/lessons/de/visubot/04-sensoren.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -titel: Sensoren abfragen -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 misst etwas, und dein Programm entscheidet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- den Unterschied zwischen Befehl und Sensor erklären -- einen Sensor in eine Bedingung einbauen -::: - -## Befehle und Sensoren - -Bisher hast du dem mBot2 nur **Befehle** gegeben: fahre vor, drehe rechts. Er hat sie ausgeführt, ohne auf seine Umgebung zu achten. - -Jetzt kommen die **Sensoren** dazu. Unter den [Elementen](#zeige-elemente) gibt es dafür die Gruppe **mBot2-Sensoren**: - -| Sensor | Text | Was du bekommst | -|---|---|---| -| Abstand | `mbot.getDistance()` | Abstand bis zur nächsten Wand in cm, z. B. `38` | -| Linie | `mbot.getLine()` | `0`, wenn unter dem Liniensensor alles hell ist, sonst eine Zahl größer als 0 | - -Der mBot2 hat noch mehr Sensoren: Farben, Tasten und Joystick, Licht, Lautstärke und Neigung. Jeden lernst du in den Lektionen 6 bis 13 einzeln kennen, die Motoren in Lektion 20 und Stoppuhr und Akku in Lektion 21. - -Ein Sensor ist **kein eigener Kasten**. Er liefert eine Zahl, und mit der baust du eine **Bedingung**: - -```java -mbot.getDistance() < 20 -``` - -Das heißt: „Ist die Wand näher als 20 cm?“ Die Antwort ist wahr oder falsch. - -> So baust du es: Zieh zuerst eine **Verzweigung** ins Struktogramm. Dann ziehst du den Sensor **Abstand** auf die Verzweigung. In der Bedingung steht jetzt `mbot.getDistance()`. Den Vergleich `< 20` schreibst du selbst dazu. - -## Wand vorn? - -Der mBot2 soll selbst entscheiden: Ist vorn eine Wand, dreht er sich nach rechts. Ist frei, fährt er ein Stück vor. - -Diese Übung hat **zwei Welten**: In der einen steht die Wand direkt vor ihm, in der anderen ist viel Platz. Dein Programm muss in beiden das Richtige tun. - -::: übung vb-wand-vorn -titel: Wand vorn? -auftrag: Baue eine Verzweigung mit der Bedingung `mbot.getDistance() < 20`. Bei **true** dreht sich der mBot2 nach rechts, bei **false** fährt er vor. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und dann den Sensor **Abstand** auf die Verzweigung. Schreib dahinter `< 20`. -tipp: Im true-Zweig steht `mbot.turnRight()`, im false-Zweig `mbot.forward()`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -Im Bereich [Code](#zeige-codelive) siehst du dein Struktogramm auch als Java. Eine Verzweigung heißt dort **if**, der false-Zweig **else**. Die Bedingung steht in runden Klammern, jeder Zweig in geschweiften Klammern: - -```java -if (mbot.getDistance() < 20) { - mbot.turnRight(); -} else { - mbot.forward(); -} -``` - -> Selbst tippen musst du `if` noch nicht. Mehr dazu kommt in der Lektion über den Ultraschallsensor. - -## Was liefert der Sensor? - -Ein Sensor liefert einen **Wert**, so wie eine Variable. Fährt der mBot2 weiter, ändert sich der Wert: Der Abstand wird kleiner, je näher die Wand kommt. Erst ein **Vergleich** macht aus dem Wert eine Frage mit der Antwort `true` (wahr) oder `false` (falsch). - -| Bedingung | Frage | Antwort bei 38 cm Abstand | -|---|---|---| -| `mbot.getDistance() < 20` | Ist die Wand näher als 20 cm? | `false` | -| `mbot.getDistance() > 30` | Ist mehr als 30 cm Platz? | `true` | -| `mbot.getLine() == 0` | Ist unter dem mBot2 alles hell? | hängt vom Boden ab | - -Vergleichen kannst du mit `<` (kleiner), `>` (größer), `<=` (kleiner oder gleich), `>=` (größer oder gleich), `==` (gleich) und `!=` (ungleich). - -> Für „gleich“ schreibst du **zwei** Gleichheitszeichen: `==`. Ein einzelnes `=` legt einen Wert in eine Variable, das ist etwas ganz anderes. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Verzweigung“](?visustruct&lektion=08-verzweigung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-04-sensoren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wand vorn?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-04-sensoren-start.visustruct -welt: wand-nah -> turnRight -welt: wand-frei -> forward -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.turnRight()`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -## Spiel: Befehl oder Sensor? - -::: spiel frage -titel: Spiel: Befehl oder Sensor? -intro: Tut die Zeile etwas, oder liefert sie einen Wert? Acht Runden. -antworten: Befehl | Sensor - -runde: - mbot.forward(30) -frage: Befehl oder Sensor? -richtig: Befehl -> Er fährt 30 cm. Das ist etwas tun. - -runde: - mbot.getDistance() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert den Abstand in cm, zum Beispiel 38. - -runde: - mbot.turnRight() -frage: Befehl oder Sensor? -richtig: Befehl -> Drehen ist etwas tun. - -runde: - mbot.getLine() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert 0 oder eine Zahl größer als 0. - -runde: - mbot.playTone(440, 300) -frage: Befehl oder Sensor? -richtig: Befehl -> Ein Ton ist eine Ausgabe. - -runde: - mbot.isButtonA() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert wahr oder falsch: Wird Taste A gedrückt? - -runde: - mbot.showText("Hallo") -frage: Befehl oder Sensor? -richtig: Befehl -> Etwas anzeigen ist etwas tun. - -runde: - mbot.getBrightness() -frage: Befehl oder Sensor? -richtig: Sensor -> Er misst, wie hell es ist, von 0 bis 100. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann den Unterschied zwischen Befehl und Sensor erklären. → Befehle und Sensoren -- Ich kann einen Sensor in eine Bedingung einbauen. → Wand vorn? -::: - -## Wissenstest - -::: quiz -? Die Wand ist 12 cm entfernt. Ist `mbot.getDistance() < 20` wahr oder falsch? -→ Wand vorn? -- [x] wahr -- [ ] falsch -> 12 ist kleiner als 20, also ist die Bedingung wahr. -? Der mBot2 steht auf hellem Boden. Was liefert `mbot.getLine()`? -→ Befehle und Sensoren -- [x] 0 -- [ ] 15 -- [ ] den Abstand in cm -> 0 heißt: Keiner der vier Fühler sieht etwas Dunkles. -? Warum ist der Sensor kein eigener Kasten im Struktogramm? -→ Wand vorn? -- [x] Er tut nichts, er liefert nur einen Wert. Den braucht man in einer Bedingung. -- [ ] Weil der mBot2 sonst zu langsam wird. -- [ ] Weil Sensoren nur in Java gehen. -> Befehle sind Anweisungen. Sensoren sind Werte, so wie eine Variable. -::: diff --git a/web/lessons/de/visubot/05-bis-zur-wand.md b/web/lessons/de/visubot/05-bis-zur-wand.md deleted file mode 100644 index 4229363b..00000000 --- a/web/lessons/de/visubot/05-bis-zur-wand.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -titel: Bis zur Wand -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 misst den Abstand und hält rechtzeitig an. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit `mbot.getDistance()` den Abstand abfragen -- entscheiden, wann ich eine While-Schleife statt einer For-Schleife nehme -- mit dem Liniensensor ein Ziel finden -::: - -## Wie weit ist die Wand? - -Bisher hast du genau gesagt, wie weit der mBot2 fahren soll. Jetzt soll er selbst merken, wann er anhalten muss. - -Dafür gibt es den Ultraschallsensor. `mbot.getDistance()` liefert den Abstand bis zur nächsten Wand in Zentimetern, zum Beispiel `38`. Du kannst ihn wie eine Zahl benutzen: - -```java -mbot.getDistance() > 15 -``` - -Das ist eine **Bedingung**: Sie ist wahr, solange die Wand mehr als 15 cm entfernt ist. - -> Starte die [Simulation](#zeige-simulation): Unter der Welt steht immer der aktuelle Abstand. Fährt der mBot2 näher an die Wand, wird die Zahl kleiner. - -## Drei Welten, ein Programm - -Diese Übung hat **drei Welten**. In jeder steht die Wand woanders, und der mBot2 soll jedes Mal im Ziel mit der grünen Fahne anhalten, ohne anzustoßen. - -Mit einer festen Anzahl **fahre vor** klappt das nur in einer Welt. Du brauchst eine Schleife, die so lange fährt, wie die Bedingung wahr ist: die **While-Schleife**. - -::: übung vb-bis-wand -titel: Bis zur Wand -auftrag: Der mBot2 soll vorwärts fahren, bis die Wand höchstens 15 cm entfernt ist. Dein Programm muss in allen drei Welten funktionieren. Über der Simulation wählst du, welche Welt du siehst. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Abstand** auf die Schleife. Dann fehlt nur noch der Vergleich. -tipp: Eine While-Schleife mit der Bedingung `mbot.getDistance() > 15`, darin ein `mbot.forward()`. -lösung: vb-bis-wand-loesung.visustruct -::: - -::: ludwig -Eine Schleife, die eine Bedingung prüft, bevor sie fährt, ist wie ich am Futtertrog: Ich frage zuerst, ob noch etwas drin ist, und erst dann fresse ich weiter. -::: - -## Bis zum Ziel - -Jetzt kommt der zweite Sensor dran. Das Ziel ist ein schwarzes Feld mit grüner Fahne. Fährt der mBot2 darauf, sieht es sein Liniensensor: `mbot.getLine()` ist dann nicht mehr `0`. - -::: übung vb-bis-ziel -titel: Fahre bis zum Ziel -auftrag: Der mBot2 soll so lange vorwärts fahren, bis er auf dem Ziel steht. Auch hier gibt es zwei Welten. -welt: ziel-kurz, ziel-lang -braucht: whileSchleife -tipp: Die Schleife soll laufen, solange der Liniensensor **nichts** sieht. Zieh den Sensor **Linie** auf die While-Schleife. -tipp: Die Bedingung heißt `mbot.getLine() == 0`. In der Schleife steht `mbot.forward()`. -lösung: vb-bis-ziel-loesung.visustruct -::: - -## Was prüft die Schleife? - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„While-Schleife“](?visustruct&lektion=11-while-schleife). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-05-bis-zur-wand -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Wand“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-bis-zur-wand-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.forward()`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Spiel: Wie oft fährt er? - -::: spiel frage -titel: Spiel: Wie oft fährt er? -intro: Jede Fahrt ist 10 cm lang. Rechne aus, wie oft der mBot2 vorfährt, bevor die Schleife aufhört. Sechs Runden. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 40 cm entfernt. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- 4-mal -> 40, 30 und 20 sind größer als 15: drei Fahrten. Bei 10 hört die Schleife auf. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 15 cm entfernt. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- 2-mal -> 15 ist nicht größer als 15. Die Bedingung ist gleich am Anfang falsch. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 50 cm entfernt. Wo hält er an? -- 15 cm vor der Wand -- [x] 10 cm vor der Wand -- 0 cm, er stößt an -> 50, 40, 30, 20: viermal vor. Dann ist die Wand 10 cm weg, und die Schleife hört auf. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } -frage: Der mBot2 steht schon auf dem schwarzen Ziel. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- immer weiter -> Auf Schwarz ist `mbot.getLine()` nicht 0. Die Bedingung ist sofort falsch. - -runde: -frage: Der mBot2 soll genau fünfmal vorfahren. Welche Schleife passt am besten? -- [x] eine For-Schleife -- eine While-Schleife mit `mbot.getDistance()` -- gar keine Schleife -> Die Anzahl steht vorher fest. Dafür ist die For-Schleife da. - -runde: -frage: Der mBot2 soll fahren, bis er die Wand fast berührt. Welche Schleife passt? -- eine For-Schleife, die zehnmal läuft -- [x] eine While-Schleife mit `mbot.getDistance()` -- gar keine Schleife -> Wie weit die Wand weg ist, weiß man vorher nicht. Die While-Schleife fragt unterwegs nach. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Grau ist eine Wand. Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß, der Sensor sitzt vorn am mBot2. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, solange vorn mehr als 15 cm frei sind. Direkt vor der Wand hält er an. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,1 -> Egal wie weit die Wand weg ist: Die While-Schleife hält ihn direkt davor an. - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(60); -karte: ##### | ..... | ..... | ..... | ^.... -ziel: 2,2 -> Bei mehr als 45 cm hört er schon ein Feld früher auf, dreht sich und fährt zwei Felder nach rechts. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,0 -> Er fährt, bis sein Liniensensor Schwarz sieht. Dann steht er auf dem schwarzen Feld. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit `mbot.getDistance()` den Abstand abfragen. → Wie weit ist die Wand? -- Ich kann entscheiden, wann ich eine While-Schleife statt einer For-Schleife nehme. → Drei Welten, ein Programm -- Ich kann mit dem Liniensensor ein Ziel finden. → Bis zum Ziel -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht 40 cm vor der Wand. Wie oft fährt er mit `while (mbot.getDistance() > 15) mbot.forward();` vor, wenn jede Fahrt 10 cm ist? -→ Wie weit ist die Wand? -- [ ] gar nicht -- [ ] einmal -- [x] so oft, bis die Wand höchstens 15 cm weg ist -- [ ] immer genau fünfmal -> Die While-Schleife prüft vor jeder Fahrt die Bedingung. Ist die Wand nah genug, hört sie auf. -? Warum reicht hier keine For-Schleife mit fester Anzahl? -→ Drei Welten, ein Programm -- [x] Weil die Wand in jeder Welt anders weit weg ist. -- [ ] Weil eine For-Schleife keine Anweisungen enthalten darf. -- [ ] Weil der mBot2 For-Schleifen nicht versteht. -> Die For-Schleife weiß vorher, wie oft sie läuft. Die While-Schleife entscheidet unterwegs. -::: diff --git a/web/lessons/de/visubot/05-erst-fahren-dann-pruefen.md b/web/lessons/de/visubot/05-erst-fahren-dann-pruefen.md deleted file mode 100644 index f122d8a1..00000000 --- a/web/lessons/de/visubot/05-erst-fahren-dann-pruefen.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -titel: Erst fahren, dann prüfen -bereich: Sensoren und Entscheidungen -kurz: Die Do-while-Schleife fährt immer mindestens einmal und prüft erst danach. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft -- eine While- und eine Do-while-Schleife vergleichen -- eine Do-while-Schleife in Java schreiben -::: - -## Erst fahren, dann prüfen - -Die While-Schleife aus Lektion 5 prüft **zuerst** und fährt dann. Steht die Wand schon direkt vor dem mBot2, fährt er gar nicht los. Sie ist **kopfgesteuert**: Die Bedingung steht oben im Kopf. - -Die **Do-while-Schleife** ist **fußgesteuert**. Die Bedingung steht unten im Fuß des Kastens: - -1. Den Inhalt einmal ausführen. -2. Die Bedingung prüfen. -3. Ist sie wahr, wieder bei 1 anfangen. Ist sie falsch, geht es unter der Schleife weiter. - -Deshalb läuft der Inhalt **immer mindestens einmal**. Wenn du die Schleife anklickst, steht im Objektinspektor vor dem Textfeld `while`: Die Bedingung heißt „wiederhole, solange …“. - -| Abstand am Anfang | While-Schleife | Do-while-Schleife | -|---|---|---| -| 50 cm | fährt bis 15 cm | fährt bis 15 cm | -| 10 cm | fährt **gar nicht** | fährt **einmal** 10 cm | - -::: übung vb-do-while -titel: Mindestens ein Schritt -auftrag: Baue ein Struktogramm mit einer Do-while-Schleife: Der mBot2 fährt vor, solange der Abstand größer als 15 cm ist, aber **mindestens einmal**. Danach spielt er einen Ton. -messwerte: getDistance=10 -> forward, tone -welt: wand-kurz -> tone -braucht: doUntilSchleife -tipp: Zieh die **Do-while-Schleife** aus den Elementen ins Struktogramm. In den Kasten kommt **fahre vor**, in den Fuß die Bedingung `mbot.getDistance() > 15`. -tipp: Unter die Schleife (nicht hinein) kommt `mbot.playTone(440, 300)`. -lösung: vb-do-while-loesung.visustruct -::: - -> Beim Prüfen steht die Wand einmal nur 10 cm vor dem mBot2. Eine While-Schleife würde dann gar nicht fahren. - -## Jetzt in Java: Schritte zählen - -In Java steht `do` oben und `while (…)` mit Semikolon unten: - -```java -do { - mbot.forward(); -} while (mbot.getDistance() > 15); -``` - -::: übung vb-java-schritte-zaehlen -titel: Schritte zählen -code: java -auftrag: Schreibe in Java: Lege `int schritte = 0` an. In einer Do-while-Schleife fährt der mBot2 vor und zählt `schritte` um eins hoch, solange der Abstand größer als 15 cm ist. Danach zeigt der Bildschirm zum Beispiel **Schritte: 3**. -welt: wand-kurz -> screen "Schritte: 3" -messwerte: getDistance=10 -> forward, screen "Schritte: 1" -braucht: doUntilSchleife -tipp: `do { mbot.forward(); schritte = schritte + 1; } while (mbot.getDistance() > 15);` -tipp: Nach der Schleife `mbot.showText("Schritte: " + schritte);`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Do-while-Schleife“](?visustruct&lektion=12-do-while-schleife). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-05-erst-fahren-dann-pruefen -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Schritte zählen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct -welt: wand-kurz -> screen "Schritte: 3" -messwerte: getDistance=10 -> forward, screen "Schritte: 1" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.showText("Schritte: " + schritte)`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -## Spiel: Kopf oder Fuß? - -::: spiel frage -titel: Spiel: Kopf oder Fuß? -intro: While oder Do-while: Wie oft fährt der mBot2 vor? Jede Fahrt ist 10 cm lang. Sechs Runden. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand steht 10 cm vor dem mBot2. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- 2-mal -> Die While-Schleife prüft zuerst. 10 ist nicht größer als 15. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Die Wand steht 10 cm vor dem mBot2. Wie oft fährt er vor? -- gar nicht -- [x] 1-mal -- 2-mal -> Die Do-while-Schleife fährt erst und prüft dann. Einmal fährt sie immer. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Die Wand steht 40 cm vor dem mBot2. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- 4-mal -> Nach der ersten Fahrt sind es 30 cm, dann 20, dann 10. Bei 10 ist Schluss. Hier fahren beide Schleifen gleich oft. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte = schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + schritte); -frage: Die Wand steht 10 cm vor dem mBot2. Was zeigt der Bildschirm? -- Schritte: 0 -- [x] Schritte: 1 -- Schritte: 2 -> Der Inhalt läuft einmal, also zählt `schritte` einmal hoch. - -runde: -frage: Welche Schleife ist fußgesteuert? -- die While-Schleife -- [x] die Do-while-Schleife -- die For-Schleife -> Ihre Bedingung steht unten im Fuß. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15) -frage: Warum läuft dieser Code nicht? -- `do` muss großgeschrieben werden. -- [x] Hinter `while (…)` fehlt das Semikolon. -- Die Bedingung muss oben stehen. -> Die Do-while-Schleife endet mit `} while (Bedingung);`. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() < 15); -frage: Soll mindestens einmal vorfahren und weiter, solange die Wand mehr als 15 cm weg ist. -fehler: 3 -besser: } while (mbot.getDistance() > 15); -> „Mehr als 15 cm“ heißt `> 15`. - -runde: - do - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Soll mindestens einmal vorfahren und weiter, solange die Wand mehr als 15 cm weg ist. -fehler: 1 -besser: do { -> Hinter `do` fehlt die öffnende geschweifte Klammer. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + schritte); -frage: Soll die Schritte zählen und am Ende anzeigen. -fehler: 4 -besser: schritte = schritte + 1; -> `schritte + 1` rechnet nur, speichert aber nichts. Das Ergebnis muss mit `=` zurück in `schritte`. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte = schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + Schritte); -frage: Soll die Schritte zählen und am Ende anzeigen. -fehler: 6 -besser: mbot.showText("Schritte: " + schritte); -> Java unterscheidet Groß- und Kleinschreibung. `Schritte` gibt es nicht. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Grau ist eine Wand. While oder Do-while: Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,2 -> Die Wand ist schon nah. Die While-Schleife prüft zuerst und fährt gar nicht los. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 45); -karte: ##### | ..... | ..^.. -ziel: 2,1 -> Die Do-while-Schleife fährt erst einmal und prüft dann. Ein Feld weiter hört sie auf. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); - mbot.turnLeft(); - mbot.forward(30); -karte: ##### | ..... | ..^.. -ziel: 1,1 -> Ein Feld vor, dann ist die Wand direkt davor. Danach links und ein Feld weiter. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> Ist die Wand weit weg, fahren While und Do-while gleich weit: bis direkt vor die Wand. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft. → Erst fahren, dann prüfen -- Ich kann eine While- und eine Do-while-Schleife vergleichen. → Erst fahren, dann prüfen -- Ich kann eine Do-while-Schleife in Java schreiben. → Jetzt in Java: Schritte zählen -::: - -## Wissenstest - -::: quiz -? Wo steht bei der Do-while-Schleife die Bedingung? -→ Erst fahren, dann prüfen -- [ ] oben im Kopf -- [x] unten im Fuß -- [ ] gar nicht -> Fußgesteuert heißt: erst der Inhalt, dann die Bedingung. -? Die Wand steht 10 cm vor dem mBot2. Wie oft fährt `do { mbot.forward(); } while (mbot.getDistance() > 15);`? -→ Erst fahren, dann prüfen -- [ ] gar nicht -- [x] einmal -- [ ] bis er anstößt -> Der Inhalt läuft immer mindestens einmal. Danach ist die Bedingung falsch. -? Was gehört in Java hinter `while (…)` am Ende der Do-while-Schleife? -→ Jetzt in Java: Schritte zählen -- [x] ein Semikolon `;` -- [ ] eine geschweifte Klammer `{` -- [ ] nichts -> `} while (Bedingung);` endet mit einem Semikolon. -::: diff --git a/web/lessons/de/visubot/05-zaehlen-und-summieren.md b/web/lessons/de/visubot/05-zaehlen-und-summieren.md deleted file mode 100644 index 13a687b2..00000000 --- a/web/lessons/de/visubot/05-zaehlen-und-summieren.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -titel: Zählen und Summieren -bereich: Sensoren und Entscheidungen -kurz: Mit einer Variablen mitzählen und Werte zusammenzählen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Zählvariable anlegen und in einer Schleife hochzählen -- in einer Schleife eine Summe bilden -- das Ergebnis nach der Schleife auf dem Bildschirm zeigen -::: - -## Mitzählen - -Wie viele Schritte fährt der mBot2 bis zur Wand? Das weißt du vorher nicht, die Welt ist jedes Mal anders. Der mBot2 soll deshalb **mitzählen**. Dafür brauchst du drei Kästen: - -1. **Vor** der Schleife: `int schritte = 0`. Am Anfang ist noch nichts gezählt. -2. **In** der Schleife: `schritte = schritte + 1`. Nach jedem Schritt eins mehr. -3. **Nach** der Schleife: das Ergebnis zeigen, zum Beispiel mit `mbot.showText("Schritte: " + schritte)`. - -Lies `schritte = schritte + 1` von rechts nach links: Nimm den alten Wert von `schritte`, zähle 1 dazu und speichere das Ergebnis wieder in `schritte`. So eine Variable heißt **Zählvariable** oder kurz Zähler. - -> Achtung: Steht `int schritte = 0` **in** der Schleife, fängt der Zähler bei jedem Durchgang wieder bei 0 an. Am Ende steht dann immer 1 da. - -::: angeberwissen -Dein Handy zählt deine Schritte genau so: Bei jedem Schritt rechnet es `schritte = schritte + 1`. Ich zähle lieber meine Grashalme. Ich bin bei 4 Millionen. -::: - -::: übung vb-schritte-welt -titel: Schritte bis zur Wand -auftrag: Baue ein Struktogramm: Der mBot2 fährt vor, solange der Abstand größer als 15 ist. Dabei zählt er seine Schritte mit. Danach zeigt er zum Beispiel **Schritte: 3** auf dem Bildschirm. Es gibt drei Welten. -welt: wand-kurz -> screen "Schritte: 3" -welt: wand-mittel -> screen "Schritte: 7" -welt: wand-lang -> screen "Schritte: 12" -braucht: whileSchleife -tipp: Der erste Kasten ist `int schritte = 0`. Danach kommt die While-Schleife mit `mbot.getDistance() > 15`. -tipp: In der Schleife stehen **fahre vor** und `schritte = schritte + 1`. Nach der Schleife steht `mbot.showText("Schritte: " + schritte)`. -lösung: vb-schritte-welt-loesung.visustruct -::: - -## Zusammenzählen - -Ein Zähler wächst immer um 1. Eine **Summe** wächst um verschiedene Werte. Der mBot2 fährt eine Spirale: erst 10 cm, dann 20, dann 30, dann 40. Wie weit ist er insgesamt gefahren? - -| Durchgang | `seite` | `gesamt` danach | -|---|---|---| -| vorher | 10 | 0 | -| 1 | 10 | 0 + 10 = 10 | -| 2 | 20 | 10 + 20 = 30 | -| 3 | 30 | 30 + 30 = 60 | -| 4 | 40 | 60 + 40 = 100 | - -Das Muster ist dasselbe wie beim Zählen. Nur steht statt der 1 jetzt der Wert, der dazukommt: `gesamt = gesamt + seite`. - -## Jetzt in Java: Spirale mit Strecke - -::: übung vb-java-spirale-strecke -titel: Spirale mit Strecke -code: java -auftrag: Schreibe in Java: Lege `int seite = 10` und `int gesamt = 0` an. Fahre viermal: `seite` cm vor, rechts drehen, `seite` zu `gesamt` dazuzählen, `seite` um 10 größer machen. Zeige danach **Strecke: 100 cm** auf dem Bildschirm. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Strecke: 100 cm" -braucht: forSchleife -tipp: In der For-Schleife stehen vier Zeilen: `mbot.forward(seite);`, `mbot.turnRight();`, `gesamt = gesamt + seite;` und `seite = seite + 10;`. -tipp: Nach der Schleife steht `mbot.showText("Strecke: " + gesamt + " cm");`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -::: übung vb-fehler-05-zaehlen-und-summieren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Spirale mit Strecke“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-zaehlen-und-summieren-start.visustruct -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Strecke: 100 cm" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `gesamt = gesamt + seite`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -## Spiel: Was steht am Ende in der Variablen? - -::: spiel frage -titel: Spiel: Was steht am Ende in der Variablen? -intro: Ludwig hat kleine Programme geschrieben. Rechne im Kopf mit. Fünf Runden. - -runde: -frage: `int z = 0`, dann dreimal `z = z + 1`. Was ist `z`? -- 1 -- [x] 3 -- 0 -> Jeder Durchgang zählt 1 dazu: 1, 2, 3. - -runde: -frage: `int summe = 0`, dann `summe = summe + 5` und `summe = summe + 7`. Was ist `summe`? -- 7 -- [x] 12 -- 5 -> 0 + 5 = 5, dann 5 + 7 = 12. - -runde: -frage: Eine Schleife läuft viermal. In ihr stehen `int z = 0` und `z = z + 1`. Was ist `z` am Ende? -- 4 -- [x] 1 -- 0 -> Der Zähler wird in jedem Durchgang wieder auf 0 gesetzt. Er gehört vor die Schleife. - -runde: -frage: `int gesamt = 0`. Der mBot2 fährt 20, 20 und 30 cm und zählt jedes Mal dazu. Was ist `gesamt`? -- 30 -- 20 -- [x] 70 -> 20 + 20 + 30 = 70. - -runde: -frage: `int schritte = 10`, dann zweimal `schritte = schritte + 1`. Was ist `schritte`? -- 2 -- [x] 12 -- 11 -> Der Zähler fängt hier bei 10 an, nicht bei 0. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Zählvariable anlegen und in einer Schleife hochzählen. → Mitzählen -- Ich kann in einer Schleife eine Summe bilden. → Zusammenzählen -- Ich kann das Ergebnis nach der Schleife auf dem Bildschirm zeigen. → Jetzt in Java: Spirale mit Strecke -::: - -## Wissenstest - -::: quiz -? Wo legst du die Zählvariable `int schritte = 0` an? -→ Mitzählen -- [x] Vor der Schleife -- [ ] In der Schleife -- [ ] Nach der Schleife -> In der Schleife würde sie bei jedem Durchgang wieder 0. -? Was macht `schritte = schritte + 1`? -→ Mitzählen -- [ ] Es vergleicht schritte mit schritte + 1. -- [x] Es nimmt den alten Wert, zählt 1 dazu und speichert das Ergebnis. -- [ ] Es setzt schritte auf 1. -? Wie zählst du die Seitenlänge `seite` zur Summe `gesamt` dazu? -→ Zusammenzählen -- [ ] `gesamt = seite` -- [ ] `seite = gesamt + seite` -- [x] `gesamt = gesamt + seite` -::: diff --git a/web/lessons/de/visubot/06-abstand.md b/web/lessons/de/visubot/06-abstand.md deleted file mode 100644 index 8c14ba25..00000000 --- a/web/lessons/de/visubot/06-abstand.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -titel: Der Ultraschallsensor -bereich: Sensoren und Entscheidungen -kurz: Abstand messen, Abstand halten und das Ganze in Java. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was `mbot.getDistance()` misst -- zwei Verzweigungen ineinander bauen -- eine While-Schleife mit dem Abstand in Java schreiben -::: - -## Wie misst der mBot2 den Abstand? - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Der [Ultraschallsensor]{teil=ultrasonic} schickt einen sehr hohen Ton los und wartet auf das Echo. Je länger das Echo braucht, desto weiter ist die Wand weg. `mbot.getDistance()` liefert den Abstand in **Zentimetern**, von etwa 3 bis 300. - -| Sensor | Text | Wert | -|---|---|---| -| Abstand | `mbot.getDistance()` | ganze Zahl in cm, z. B. `38` | - -> In der [Simulation](#zeige-simulation) steht unter der Welt immer der aktuelle Abstand. Mit einem echten mBot2 kannst du die Hand vor den Sensor halten und zusehen, wie sich die Zahl ändert. - -## Abstand halten im Struktogramm - -Der mBot2 soll wie ein Hund an der Leine sein: Ist deine Hand zu nah (unter 10 cm), weicht er zurück. Ist sie zu weit weg (über 20 cm), kommt er näher. Dazwischen bleibt er stehen. - -Dafür brauchst du zwei Verzweigungen ineinander. Die äußere prüft „zu nah?“, die innere im false-Zweig prüft „zu weit?“. - -::: übung vb-abstand-halten -titel: Abstand halten -auftrag: Baue ein Struktogramm: Ist der Abstand kleiner als 10, fährt der mBot2 zurück. Sonst: Ist der Abstand größer als 20, fährt er vor. Sonst tut er nichts. -messwerte: getDistance=5 -> backward -messwerte: getDistance=35 -> forward -messwerte: getDistance=15 -> none -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und den Sensor **Abstand** darauf. Schreib `< 10` dahinter. Im true-Zweig steht **fahre zurück**. -tipp: In den false-Zweig kommt eine zweite Verzweigung mit `mbot.getDistance() > 20` und **fahre vor** im true-Zweig. Ihr false-Zweig bleibt leer. -lösung: vb-abstand-halten-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand nacheinander auf 5, 35 und 15 cm und schaut, was dein mBot2 tut. - -## Jetzt in Java - -Im Bereich [Code](#zeige-codelive) siehst du dein Struktogramm schon als Java-Code. Eine Verzweigung heißt dort `if`, der false-Zweig `else`: - -```java -if (mbot.getDistance() < 10) { - mbot.backward(); -} else { - ... -} -``` - -Den Sensor tippst du wie einen Befehl: `mbot.` und dann `getDistance()`. Nach dem Punkt schlägt dir der Editor alle Befehle und Sensoren vor. - -::: übung vb-java-wand-piep -titel: Bis zur Wand und piepen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange die Wand mehr als 15 cm entfernt ist. Danach spielt er einen Ton. Es gibt drei Welten. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: Eine While-Schleife heißt in Java `while (Bedingung) { … }`. Die Bedingung ist `mbot.getDistance() > 15`. -tipp: In der Schleife steht `mbot.forward();`, nach der Schleife `mbot.playTone(440, 300);`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -::: übung vb-fehler-06-abstand -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Wand und piepen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-abstand-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es `mbot.playTone(440, 300)`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - if (mbot.getDistance() < 10) { - mbot.forward(); - } - if (mbot.getDistance() > 20) { - mbot.forward(); - } -frage: Unter 10 cm soll er zurückfahren, über 20 cm vorfahren. -fehler: 2 -besser: mbot.backward(); -> Ist die Hand zu nah, muss er zurück, nicht vor. - -runde: - while (mbot.getDistance() < 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll vorfahren, solange die Wand mehr als 15 cm weg ist, und dann piepen. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> „Mehr als 15 cm weg“ heißt `> 15`. - -runde: - while (mbot.getDistance > 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll bis zur Wand fahren und dann piepen. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> Ein Sensor ist eine Methode. Ohne Klammern findet Java ihn nicht. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } els { - mbot.forward(); - } -frage: Unter 10 cm soll er zurückfahren, sonst vorfahren. -fehler: 3 -besser: } else { -> Der false-Zweig heißt `else`. - -runde: - while (mbot.getDistance() > 15) { - mbot.turnRight(); - } - mbot.playTone(440, 300); -frage: Soll bis zur Wand fahren und dann piepen. -fehler: 2 -besser: mbot.forward(); -> Er soll vorfahren, nicht sich drehen. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } else { - if (mbot.getDistance() < 20) { - mbot.forward(); - } - } -frage: Unter 10 cm zurück, über 20 cm vor, dazwischen stehen bleiben. -fehler: 4 -besser: if (mbot.getDistance() > 20) { -> Vorfahren soll er nur, wenn die Hand weiter als 20 cm weg ist. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was `mbot.getDistance()` misst. → Wie misst der mBot2 den Abstand? -- Ich kann zwei Verzweigungen ineinander bauen. → Abstand halten im Struktogramm -- Ich kann eine While-Schleife mit dem Abstand in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? `mbot.getDistance()` liefert `8`. Was tut das Programm aus „Abstand halten“? -→ Abstand halten im Struktogramm -- [x] Es fährt zurück. -- [ ] Es fährt vor. -- [ ] Es bleibt stehen. -> 8 ist kleiner als 10, also ist schon die erste Bedingung wahr. -? Wie heißt der false-Zweig einer Verzweigung in Java? -→ Jetzt in Java -- [ ] `then` -- [x] `else` -- [ ] `otherwise` -> `if` ist der Anfang, `else` der false-Zweig. -::: diff --git a/web/lessons/de/visubot/06-konstanten-final.md b/web/lessons/de/visubot/06-konstanten-final.md deleted file mode 100644 index 5f223b97..00000000 --- a/web/lessons/de/visubot/06-konstanten-final.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -titel: Feste Werte mit final -bereich: Sensoren und Entscheidungen -kurz: Eine Konstante anlegen, damit eine wichtige Zahl nur an einer Stelle steht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was eine Konstante ist -- eine Konstante im Struktogramm und in Java anlegen -- eine Konstante in einer Bedingung und in einem Befehl benutzen -::: - -## Eine Zahl an vielen Stellen - -Ludwig hat ein Programm für den Flur geschrieben: Der mBot2 fährt bis 20 cm vor die Wand, dreht sich um und fährt bis 20 cm vor die andere Wand. - -::: diagramm vb-l06k-ohne.visustruct -Hin und zurück. Die Zahl 20 steht an zwei Stellen. -::: - -Jetzt soll der mBot2 schon bei 30 cm anhalten. Ludwig muss jede 20 suchen und ändern. Vergisst er eine, hält der mBot2 einmal bei 30 und einmal bei 20 cm. In langen Programmen passiert so etwas schnell. - -::: angeberwissen -Profis nennen eine Zahl, die einfach so im Programm steht, eine **magische Zahl**. Magisch, weil niemand mehr weiß, was sie bedeutet. Ich mag nur magische Grashalme. -::: - -## Eine Konstante anlegen - -Besser ist eine **Konstante**. Das ist eine Variable, deren Wert nach dem Festlegen gleich bleibt. Du legst sie einmal ganz oben an und benutzt danach nur noch ihren Namen. - -| | Struktogramm | Java | -|---|---|---| -| anlegen | `const int ABSTAND = 20` | `final int ABSTAND = 20;` | -| benutzen | `mbot.getDistance() > ABSTAND` | `mbot.getDistance() > ABSTAND` | - -Im Struktogramm schreibst du `const` davor, in Java `final`. Beides heißt: Dieser Wert ändert sich nicht mehr. Namen von Konstanten schreibt man in **Großbuchstaben**, dann erkennt man sie sofort. - -::: diagramm vb-l06k-mit.visustruct -Dasselbe Programm mit der Konstanten ABSTAND. Für 30 cm änderst du nur die erste Zeile. -::: - -::: übung vb-konstante-wand -titel: Wand mit Konstante -auftrag: Lege die Konstante `ABSTAND` mit dem Wert 20 an. Der mBot2 fährt vor, solange der Abstand größer als `ABSTAND` ist. Danach spielt er einen Ton. Es gibt drei Welten. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: Der erste Kasten ist eine Anweisung mit `const int ABSTAND = 20`. Darunter kommt eine While-Schleife. -tipp: Die Bedingung der Schleife ist `mbot.getDistance() > ABSTAND`. In der Schleife steht **fahre vor**, nach der Schleife **spiele Ton**. -lösung: vb-konstante-wand-loesung.visustruct -::: - -## Jetzt in Java: Quadrat mit Konstante - -Eine Konstante passt auch in einen Befehl. Mit `final int SEITE = 30;` fährt `mbot.forward(SEITE);` genau 30 cm. Soll das Quadrat größer werden, änderst du nur die eine Zeile. - -::: übung vb-java-konstante-seite -titel: Quadrat mit Konstante -code: java -auftrag: Schreibe in Java: Lege die Konstante `SEITE` mit dem Wert 30 an. Fahre dann mit einer For-Schleife ein Quadrat mit der Seitenlänge `SEITE`. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: Die erste Zeile ist `final int SEITE = 30;`. Danach kommt `for (int i = 0; i < 4; i++) { … }`. -tipp: In der Schleife stehen `mbot.forward(SEITE);` und `mbot.turnRight();`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -::: übung vb-fehler-06-konstanten-final -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Quadrat mit Konstante“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-konstanten-final-start.visustruct -fahrt: 4 x (forward 30, turnRight) -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Konstante dort, wo sie angelegt wird? Konstanten schreibt man in Großbuchstaben. -tipp: Richtig heißt es `mbot.forward(SEITE)`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -## Spiel: Konstante oder Variable? - -::: spiel frage -titel: Spiel: Konstante oder Variable? -intro: Ludwig sortiert Werte. Bleibt der Wert im ganzen Programm gleich, ist er eine Konstante. Ändert er sich, ist er eine Variable. Sechs Runden. -frage: Konstante oder Variable? -antworten: Konstante | Variable - -runde: -frage: Die Anzahl der Ecken eines Quadrats -richtig: Konstante -> Ein Quadrat hat immer vier Ecken. - -runde: -frage: Der Abstand, den der Ultraschallsensor gerade misst -richtig: Variable -> Fährt der mBot2, ändert sich der Abstand ständig. - -runde: -frage: Der Mindestabstand, bei dem der mBot2 immer anhalten soll -richtig: Konstante -> Du legst ihn einmal fest, zum Beispiel `final int ABSTAND = 20;`. - -runde: -frage: Die Zahl der Schritte, die der mBot2 schon gefahren ist -richtig: Variable -> Nach jedem Schritt wird sie um 1 größer. - -runde: -frage: Die Tonhöhe des Warntons, immer 440 Hz -richtig: Konstante -> Der Warnton klingt immer gleich. - -runde: -frage: Der Akkustand in Prozent -richtig: Variable -> Der Akku wird beim Fahren langsam leer. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was eine Konstante ist. → Eine Konstante anlegen -- Ich kann eine Konstante im Struktogramm und in Java anlegen. → Eine Konstante anlegen -- Ich kann eine Konstante in einer Bedingung und in einem Befehl benutzen. → Jetzt in Java: Quadrat mit Konstante -::: - -## Wissenstest - -::: quiz -? Was ist eine Konstante? -→ Eine Konstante anlegen -- [ ] Eine Variable, die sich in jedem Durchgang ändert -- [x] Eine Variable, deren Wert nach dem Festlegen gleich bleibt -- [ ] Ein Sensor, der immer denselben Wert misst -> Eine Konstante legst du einmal fest. Danach benutzt du nur ihren Namen. -? Wie legst du in Java die Konstante `ABSTAND` mit dem Wert 20 an? -→ Eine Konstante anlegen -- [ ] `const ABSTAND = 20;` -- [x] `final int ABSTAND = 20;` -- [ ] `int final = 20;` -> In Java steht `final` vor dem Datentyp. Im Struktogramm schreibst du `const`. -? Warum ist eine Konstante besser als die Zahl 20 an vielen Stellen? -→ Eine Konstante anlegen -- [ ] Weil das Programm dann schneller fährt -- [x] Weil du den Wert nur an einer Stelle ändern musst -- [ ] Weil der Sensor dann genauer misst -::: diff --git a/web/lessons/de/visubot/06-nah-mittel-weit.md b/web/lessons/de/visubot/06-nah-mittel-weit.md deleted file mode 100644 index 21152384..00000000 --- a/web/lessons/de/visubot/06-nah-mittel-weit.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -titel: Nah, mittel oder weit? -bereich: Sensoren und Entscheidungen -kurz: Eine Verzweigung mit drei Fällen bauen und in Java mit else if schreiben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Verzweigung mit drei Fällen bauen -- `else if` in Java lesen und schreiben -- die Bedingungen in der richtigen Reihenfolge prüfen -::: - -## Drei Fälle statt zwei - -Viele Autos haben eine **Einparkhilfe**. Ist die Wand weit weg, ist alles frei. Kommt sie näher, heißt es: langsam. Ist sie ganz nah: Stopp! - -Eine Verzweigung hat nur zwei Zweige, Ja und Nein. Für drei Fälle setzt du eine zweite Verzweigung in den Nein-Zweig der ersten: - -1. Ist der Abstand kleiner als 10? Dann **Stopp**. -2. Sonst: Ist der Abstand kleiner als 30? Dann **Langsam**. -3. Sonst: **Frei**. - -::: diagramm vb-l06n-einparkhilfe.visustruct -Die Einparkhilfe. Die zweite Frage wird nur gestellt, wenn die Antwort auf die erste Nein war. -::: - -Probiere es in der [Simulation](#zeige-simulation) aus: Öffne das Diagramm im Editor und stelle den Abstand auf 5, 20 und 50 cm. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Verzweigung“](?visustruct&lektion=08-verzweigung). Sie öffnet sich in einem neuen Fenster. - -## Tempo nach Abstand im Struktogramm - -::: übung vb-tempo-abstand -titel: Tempo nach Abstand -auftrag: Baue ein Struktogramm mit drei Fällen: Ist der Abstand kleiner als 10, fährt der mBot2 5 cm zurück. Sonst: Ist der Abstand kleiner als 30, fährt er 5 cm vor. Sonst fährt er 20 cm vor. -messwerte: getDistance=5 -> backward 5 -messwerte: getDistance=20 -> forward 5 -messwerte: getDistance=50 -> forward 20 -braucht: verzweigung -tipp: Baue es wie die Einparkhilfe: eine Verzweigung mit `mbot.getDistance() < 10` und im Nein-Zweig eine zweite mit `mbot.getDistance() < 30`. -tipp: Die Befehle sind `mbot.backward(5)`, `mbot.forward(5)` und `mbot.forward(20)`. Der letzte steht im Nein-Zweig der zweiten Verzweigung. -lösung: vb-tempo-abstand-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand nacheinander auf 5, 20 und 50 cm. - -## Jetzt in Java: else if - -Schau in den Bereich [Code](#zeige-codelive). Die Verzweigung im Nein-Zweig sieht so aus: - -```java -if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} else { - if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); - } else { - mbot.showText("Frei"); - } -} -``` - -In Java darfst du das kürzer schreiben. Das `if` rutscht direkt hinter das `else`: - -```java -if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} else if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); -} else { - mbot.showText("Frei"); -} -``` - -Beides bedeutet genau dasselbe. Mit `else if` ist es aber besser zu lesen. Die geschweiften Klammern `{ }` schreibst du trotzdem immer. - -::: übung vb-java-tageszeit -titel: Tag oder Nacht? -code: java -auftrag: Schreibe in Java mit `else if`: Ist die Helligkeit kleiner als 20, zeigt der mBot2 **Nacht**. Sonst: Ist sie kleiner als 60, zeigt er **Dämmerung**. Sonst zeigt er **Tag**. -messwerte: getBrightness=10 -> none, screen "Nacht" -messwerte: getBrightness=40 -> none, screen "Dämmerung" -messwerte: getBrightness=80 -> none, screen "Tag" -braucht: verzweigung -tipp: Die Helligkeit liest du mit `mbot.getBrightness()`. Die erste Bedingung ist `mbot.getBrightness() < 20`. -tipp: `if (…) { mbot.showText("Nacht"); } else if (mbot.getBrightness() < 60) { mbot.showText("Dämmerung"); } else { mbot.showText("Tag"); }` -lösung: vb-java-tageszeit-loesung.visustruct -::: - -::: übung vb-fehler-06-nah-mittel-weit -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Tag oder Nacht?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-nah-mittel-weit-start.visustruct -messwerte: getBrightness=10 -> none, screen "Nacht" -messwerte: getBrightness=40 -> none, screen "Dämmerung" -messwerte: getBrightness=80 -> none, screen "Tag" -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es `mbot.showText("Nacht")`. -lösung: vb-java-tageszeit-loesung.visustruct -::: - -## Auf die Reihenfolge kommt es an - -Ludwig hat die Einparkhilfe umgebaut und die Fragen getauscht: - -```java -if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); -} else if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} -``` - -Bei 5 cm zeigt sein mBot2 **Langsam** statt **Stopp**. Denn 5 ist auch kleiner als 30. Die erste Bedingung ist schon wahr, die zweite wird gar nicht mehr geprüft. **Stopp** erscheint nie. - -> Merke: Bei `<` prüfst du zuerst den kleinsten Wert, dann den nächstgrößeren. - -::: spiel frage -titel: Spiel: Was zeigt die Einparkhilfe? -intro: Ludwigs Einparkhilfe von oben: unter 10 cm **Stopp**, unter 30 cm **Langsam**, sonst **Frei**. Was zeigt sie? Sechs Runden. -frage: Was zeigt die Einparkhilfe? -antworten: Stopp | Langsam | Frei - -runde: -frage: Der Abstand ist **5 cm**. -richtig: Stopp -> 5 ist kleiner als 10. Schon die erste Frage ist wahr. - -runde: -frage: Der Abstand ist **25 cm**. -richtig: Langsam -> 25 ist nicht kleiner als 10, aber kleiner als 30. - -runde: -frage: Der Abstand ist **80 cm**. -richtig: Frei -> Beide Fragen sind falsch, also bleibt der letzte Nein-Zweig. - -runde: -frage: Der Abstand ist genau **10 cm**. -richtig: Langsam -> 10 ist nicht kleiner als 10. Die erste Frage ist falsch, die zweite wahr. - -runde: -frage: Der Abstand ist genau **30 cm**. -richtig: Frei -> 30 ist nicht kleiner als 30. Beide Fragen sind falsch. - -runde: -frage: Der Abstand ist **9 cm**. -richtig: Stopp -> 9 ist kleiner als 10. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Verzweigung mit drei Fällen bauen. → Drei Fälle statt zwei -- Ich kann `else if` in Java lesen und schreiben. → Jetzt in Java: else if -- Ich kann die Bedingungen in der richtigen Reihenfolge prüfen. → Auf die Reihenfolge kommt es an -::: - -## Wissenstest - -::: quiz -? Wie viele Verzweigungen brauchst du im Struktogramm für drei Fälle? -→ Drei Fälle statt zwei -- [ ] Eine -- [x] Zwei, die zweite im Nein-Zweig der ersten -- [ ] Drei nebeneinander -> Jede Verzweigung teilt in zwei Zweige. Die zweite teilt den Nein-Zweig noch einmal. -? Was bedeutet `else if` in Java? -→ Jetzt in Java: else if -- [x] Sonst prüfe noch eine Bedingung -- [ ] Wiederhole die Bedingung -- [ ] Prüfe beide Bedingungen gleichzeitig -> `else if` ist die kurze Schreibweise für ein `if` im `else`-Zweig. -? Ludwig prüft zuerst `< 30` und danach `< 10`. Was passiert bei 5 cm? -→ Auf die Reihenfolge kommt es an -- [ ] Es erscheint Stopp. -- [x] Es erscheint Langsam. -- [ ] Es erscheint gar nichts. -> 5 ist auch kleiner als 30. Die zweite Bedingung wird nicht mehr geprüft. -::: diff --git a/web/lessons/de/visubot/07-linie.md b/web/lessons/de/visubot/07-linie.md deleted file mode 100644 index 58c29276..00000000 --- a/web/lessons/de/visubot/07-linie.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -titel: Der Liniensensor -bereich: Sensoren und Entscheidungen -kurz: Dunkle Linien auf dem Boden erkennen und nicht vom Tisch fallen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was die Zahl von `mbot.getLine()` bedeutet -- bis zu einer Linie fahren -- in Java mit einer Variable Schritte zählen -::: - -## Vier Fühler unter der Nase - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der [Liniensensor]{teil=lineSensor} hat vier Fühler nebeneinander. Jeder leuchtet den Boden an und misst, wie viel Licht zurückkommt. Schwarz schluckt das Licht, Weiß wirft es zurück. - -| Sensor | Text | Wert | -|---|---|---| -| Linie | `mbot.getLine()` | `0` = alles hell, größer als `0` = mindestens ein Fühler sieht Schwarz | - -Für den Anfang reicht dir: **`mbot.getLine() == 0` heißt „keine Linie“**, `mbot.getLine() != 0` heißt „Linie gefunden“. - -::: angeberwissen -`mbot.getLine()` ist eigentlich eine Binärzahl mit vier Stellen, eine für jeden Fühler: `1111` (also 15) heißt „alle vier sehen Schwarz“, `0110` (6) „nur die mittleren“. Damit kann ein mBot2 sogar einer Linie folgen. -::: - -## Am Rand umkehren im Struktogramm - -Stell dir einen Tisch mit schwarzem Klebeband am Rand vor. Der mBot2 soll vorfahren, bis er das Band sieht, und dann umkehren, damit er nicht herunterfällt. - -::: übung vb-rand -titel: Am Rand umkehren -auftrag: Der mBot2 fährt vor, solange `mbot.getLine() == 0` ist. Danach dreht er sich um 180°. Es gibt zwei Welten mit unterschiedlich weitem Weg. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Linie** darauf. Schreib `== 0` dahinter. In die Schleife kommt **fahre vor**. -tipp: Nach der Schleife (darunter, nicht darin) steht `mbot.turnRight(180)`. -lösung: vb-rand-loesung.visustruct -::: - -## Jetzt in Java: hin und zurück - -Jetzt soll der mBot2 nach dem Umkehren genau bis zum Start zurückfahren. Dafür zählt er mit einer **Variable**, wie oft er vorgefahren ist. - -```java -int schritte = 0; -while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; -} -``` - -::: übung vb-java-rand -titel: Bis zur Linie und zurück -code: java -auftrag: Schreibe in Java: Der mBot2 fährt bis zur Linie und zählt dabei seine Schritte. Dann dreht er sich um 180° und fährt mit einer For-Schleife genauso viele Schritte zurück. -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -braucht: whileSchleife, forSchleife -tipp: Nach der While-Schleife: `mbot.turnRight(180);` und dann `for (int i = 0; i < schritte; i++) { … }`. -tipp: In der For-Schleife steht nur `mbot.forward();`. -lösung: vb-java-rand-loesung.visustruct -::: - -::: übung vb-fehler-07-linie -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Linie und zurück“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-07-linie-start.visustruct -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `schritte = schritte + 1`. -lösung: vb-java-rand-loesung.visustruct -::: - -## Spiel: Linie oder nicht? - -::: spiel frage -titel: Spiel: Linie oder nicht? -intro: Wo steht der mBot2, und ist die Bedingung wahr oder falsch? Sechs Runden. -antworten: wahr | falsch - -runde: - mbot.getLine() == 0 -frage: Der mBot2 steht auf weißem Papier. Ist die Bedingung wahr? -richtig: wahr -> Kein Fühler sieht Schwarz, also liefert `mbot.getLine()` 0. - -runde: - mbot.getLine() == 0 -frage: Die beiden mittleren Fühler stehen auf dem schwarzen Klebeband. Ist die Bedingung wahr? -richtig: falsch -> Zwei Fühler sehen Schwarz, also ist `mbot.getLine()` größer als 0. - -runde: - mbot.getLine() != 0 -frage: Nur der äußere linke Fühler sieht Schwarz. Ist die Bedingung wahr? -richtig: wahr -> Ein Fühler reicht schon, dann ist der Wert nicht mehr 0. - -runde: - mbot.getLine() != 0 -frage: Der mBot2 steht auf weißem Boden. Ist die Bedingung wahr? -richtig: falsch -> Auf Weiß ist der Wert 0, und `0 != 0` ist falsch. - -runde: - mbot.getLine() == 15 -frage: Eine Querlinie: Alle vier Fühler sehen Schwarz. Ist die Bedingung wahr? -richtig: wahr -> Alle vier Fühler ergeben die Binärzahl `1111`, also 15. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; - } -frage: Die Linie ist 30 cm entfernt, jede Fahrt ist 10 cm lang. Ist danach `schritte == 3`? -richtig: wahr -> Nach drei Fahrten steht er auf der Linie, und die Schleife hört auf. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getLine() != 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Soll bis zum schwarzen Rand fahren und dann umkehren. -fehler: 1 -besser: while (mbot.getLine() == 0) { -> Er soll fahren, solange **keine** Linie da ist, also `== 0`. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(); -frage: Soll bis zum schwarzen Rand fahren und dann umkehren. -fehler: 4 -besser: mbot.turnRight(180); -> Umkehren heißt 180°. Ohne Zahl dreht er sich nur 90°. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte - 1; - } -frage: Soll bis zur Linie fahren und die Schritte zählen. -fehler: 4 -besser: schritte = schritte + 1; -> Zählen heißt eins dazu, nicht eins weg. - -runde: - int schritte = "0"; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; - } -frage: Soll bis zur Linie fahren und die Schritte zählen. -fehler: 1 -besser: int schritte = 0; -> In Anführungszeichen ist die 0 ein Text. In ein `int` passt nur eine Zahl. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schwarz ist Klebeband. Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ..=.. | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, bis er auf dem schwarzen Feld steht. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } - mbot.turnRight(180); - mbot.forward(60); -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> Bis zum Rand, umkehren und zwei Felder zurück. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(30); - schritte = schritte + 1; - } - mbot.turnRight(180); - for (int i = 0; i < schritte; i++) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..^.. -ziel: 2,3 -> Er zählt drei Schritte bis zur Linie und fährt genau drei zurück: wieder am Start. - -runde: - mbot.turnRight(); - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ^...= | ..... -ziel: 4,1 -> Erst nach rechts drehen, dann bis zum schwarzen Feld ganz rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was die Zahl von `mbot.getLine()` bedeutet. → Vier Fühler unter der Nase -- Ich kann bis zu einer Linie fahren. → Am Rand umkehren im Struktogramm -- Ich kann in Java mit einer Variable Schritte zählen. → Jetzt in Java: hin und zurück -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf weißem Papier. Was liefert `mbot.getLine()`? -→ Vier Fühler unter der Nase -- [x] 0 -- [ ] 15 -- [ ] 1 -> Kein Fühler sieht Schwarz, also 0. -? Welche Bedingung heißt „Linie gefunden“? -→ Am Rand umkehren im Struktogramm -- [ ] `mbot.getLine() == 0` -- [x] `mbot.getLine() != 0` -- [ ] `mbot.getLine() < 0` -> `!=` heißt „ungleich“. -::: diff --git a/web/lessons/de/visubot/07-und-oder.md b/web/lessons/de/visubot/07-und-oder.md deleted file mode 100644 index c08d55ee..00000000 --- a/web/lessons/de/visubot/07-und-oder.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -titel: Und, oder, nicht -bereich: Sensoren und Entscheidungen -kurz: Zwei Sensoren in einer Bedingung prüfen: und, oder, nicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- zwei Bedingungen mit `&&` (und) verknüpfen -- zwei Bedingungen mit `||` (oder) verknüpfen -- eine Bedingung mit `!` (nicht) umdrehen -- eine verknüpfte Bedingung in einer Java-Schleife schreiben -::: - -## Wand und Linie: und - -Der mBot2 soll nur vorfahren, wenn vorn frei ist **und** unter ihm keine Linie ist. Zwei Sensoren, eine Bedingung. In Java heißt **und** `&&`: - -```java -mbot.getDistance() > 15 && mbot.getLine() == 0 -``` - -Die ganze Bedingung ist nur wahr, wenn **beide** Teile wahr sind: - -| vorn frei? | keine Linie? | frei `&&` keine Linie | -|---|---|---| -| wahr | wahr | **wahr** | -| wahr | falsch | falsch | -| falsch | wahr | falsch | -| falsch | falsch | falsch | - -So eine Tabelle heißt **Wahrheitstabelle** (englisch *truth table*). - -::: übung vb-und -titel: Frei und keine Linie -auftrag: Baue ein Struktogramm mit einer Verzweigung: Ist der Abstand größer als 15 **und** `mbot.getLine() == 0`, fährt der mBot2 vor. Sonst tut er nichts. -messwerte: getDistance=50, getLine=0 -> forward -messwerte: getDistance=10, getLine=0 -> none -messwerte: getDistance=50, getLine=6 -> none -braucht: verzweigung -tipp: Zieh den Sensor **Abstand** auf die Verzweigung und schreib `> 15` dahinter. Dann tippst du ` && ` und den zweiten Teil `mbot.getLine() == 0`. -tipp: Im true-Zweig steht **fahre vor**, der false-Zweig bleibt leer. -lösung: vb-und-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand und die Linie auf verschiedene Werte. Nur wenn beides passt, darf der mBot2 fahren. - -## Taste A oder Taste B: oder - -Jetzt soll der mBot2 piepen, wenn du Taste A **oder** Taste B drückst. Es reicht, wenn eine der beiden gedrückt ist. In Java heißt **oder** `||`. Die beiden Striche findest du auf der Tastatur meist mit **Alt Gr** und **<** (Windows) oder **Option** und **7** (Mac). - -| Taste A? | Taste B? | A oder B | -|---|---|---| -| wahr | wahr | **wahr** | -| wahr | falsch | **wahr** | -| falsch | wahr | **wahr** | -| falsch | falsch | falsch | - -::: übung vb-oder -titel: Taste A oder B -auftrag: Baue ein Struktogramm: Ist Taste A **oder** Taste B gedrückt, spielt der mBot2 einen Ton. Sonst tut er nichts. -messwerte: isButtonA=true, isButtonB=false -> tone -messwerte: isButtonA=false, isButtonB=true -> tone -messwerte: isButtonA=false, isButtonB=false -> noTone -braucht: verzweigung -tipp: Die Bedingung der Verzweigung ist `mbot.isButtonA() || mbot.isButtonB()`. -tipp: Im true-Zweig steht `mbot.playTone(440, 300)`, der false-Zweig bleibt leer. -lösung: vb-oder-loesung.visustruct -::: - -## Nicht: das Gegenteil - -Manchmal willst du das Gegenteil einer Bedingung. Dafür gibt es `!`, gesprochen **nicht**. `mbot.isButtonA()` ist wahr, wenn Taste A gedrückt ist. `!mbot.isButtonA()` ist wahr, wenn sie **nicht** gedrückt ist. - -```java -while (!mbot.isButtonA()) { - mbot.pause(100); -} -mbot.forward(30); -``` - -Der mBot2 wartet, solange Taste A nicht gedrückt ist. Danach fährt er los. - -## Jetzt in Java: Sicher fahren - -Jetzt kommt alles zusammen. Der mBot2 fährt, solange vorn frei ist **und** keine Linie kommt. Egal, was zuerst kommt, die Wand oder der Rand: Er hält an und piept. - -::: übung vb-java-sicher-fahren -titel: Sicher fahren -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange der Abstand größer als 15 ist **und** `mbot.getLine() == 0` ist. Danach spielt er einen Ton. In zwei Welten wartet eine Wand, in zwei Welten ein schwarzer Rand. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -braucht: whileSchleife -tipp: `while (mbot.getDistance() > 15 && mbot.getLine() == 0) { … }` -tipp: In der Schleife steht `mbot.forward();`, nach der Schleife `mbot.playTone(440, 300);`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Bedingungen verknüpfen“](?visustruct&lektion=09-bedingungen-verknuepfen). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-07-und-oder -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Sicher fahren“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-07-und-oder-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.playTone(440, 300)`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -## Spiel: Wahr oder falsch? - -::: spiel frage -titel: Spiel: Wahr oder falsch? -intro: Ludwig stellt den mBot2 in eine Lage. Ist die Bedingung wahr oder falsch? Acht Runden. -antworten: wahr | falsch - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: Die Wand ist 50 cm weg, unter ihm ist keine Linie. -richtig: wahr -> Beide Teile sind wahr, also ist auch das Ganze wahr. - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: Die Wand ist 10 cm weg, unter ihm ist keine Linie. -richtig: falsch -> Bei `&&` müssen beide Teile wahr sein. Der Abstand stimmt nicht. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Nur Taste B ist gedrückt. -richtig: wahr -> Bei `||` reicht ein wahrer Teil. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Keine Taste ist gedrückt. -richtig: falsch -> Beide Teile sind falsch, dann ist auch `||` falsch. - -runde: - !mbot.isButtonA() -frage: Taste A ist gedrückt. -richtig: falsch -> `mbot.isButtonA()` ist wahr, das `!` macht daraus falsch. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: Die Wand ist 10 cm weg, und er steht auf einer Linie. -richtig: falsch -> Kein Teil ist wahr, also ist auch `||` falsch. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: Die Wand ist 50 cm weg, und er steht auf einer Linie. -richtig: wahr -> Der erste Teil ist wahr, das reicht bei `||`. - -runde: - !mbot.isButtonA() && mbot.getDistance() > 15 -frage: Niemand drückt eine Taste, die Wand ist 30 cm weg. -richtig: wahr -> A ist nicht gedrückt, also ist `!mbot.isButtonA()` wahr. Der Abstand stimmt auch. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getDistance() > 15 || mbot.getLine() == 0) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll fahren, solange vorn frei ist **und** keine Linie kommt. -fehler: 1 -besser: while (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> Mit `||` fährt er weiter, wenn nur einer der Teile stimmt, zum Beispiel über den Rand. - -runde: - if (mbot.isButtonA() && mbot.isButtonB()) { - mbot.playTone(440, 300); - } -frage: Soll piepen, wenn Taste A **oder** Taste B gedrückt ist. -fehler: 1 -besser: if (mbot.isButtonA() || mbot.isButtonB()) { -> „Oder“ heißt in Java `||`. Mit `&&` piept er nur, wenn beide gedrückt sind. - -runde: - while (mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Soll warten, bis Taste A gedrückt wird, und dann losfahren. -fehler: 1 -besser: while (!mbot.isButtonA()) { -> Warten heißt: Schleife, solange A **nicht** gedrückt ist. Das `!` fehlt. - -runde: - if (mbot.getDistance() > 15 & mbot.getLine() == 0) { - mbot.forward(); - } -frage: Soll vorfahren, wenn vorn frei ist und keine Linie da ist. -fehler: 1 -besser: if (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> „Und“ schreibt man mit zwei Zeichen: `&&`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann zwei Bedingungen mit `&&` (und) verknüpfen. → Wand und Linie: und -- Ich kann zwei Bedingungen mit `||` (oder) verknüpfen. → Taste A oder Taste B: oder -- Ich kann eine Bedingung mit `!` (nicht) umdrehen. → Nicht: das Gegenteil -- Ich kann eine verknüpfte Bedingung in einer Java-Schleife schreiben. → Jetzt in Java: Sicher fahren -::: - -## Wissenstest - -::: quiz -? Der Abstand ist 50 und `mbot.getLine()` liefert 6. Fährt der mBot2 bei „Frei und keine Linie“? -→ Wand und Linie: und -- [ ] Ja, vorn ist ja frei. -- [x] Nein, denn bei `&&` müssen beide Teile wahr sein. -- [ ] Nur halb so weit. -> Er steht auf einer Linie. Damit ist ein Teil falsch, also die ganze Bedingung. -? Nur Taste B ist gedrückt. Ist `mbot.isButtonA() || mbot.isButtonB()` wahr? -→ Taste A oder Taste B: oder -- [x] Ja -- [ ] Nein -> Bei `||` reicht es, wenn ein Teil wahr ist. -? Taste A ist gedrückt. Was ist `!mbot.isButtonA()`? -→ Nicht: das Gegenteil -- [ ] wahr -- [x] falsch -> `!` dreht den Wert um: aus wahr wird falsch. -? Welche Bedingung hält den mBot2 an der Wand **und** am Rand an? -→ Jetzt in Java: Sicher fahren -- [x] `while (mbot.getDistance() > 15 && mbot.getLine() == 0)` -- [ ] `while (mbot.getDistance() > 15 || mbot.getLine() == 0)` -- [ ] `while (mbot.getDistance() > 15)` -> Mit `||` würde er weiterfahren, solange nur einer der Teile stimmt, also zum Beispiel über den Rand hinaus. -::: diff --git a/web/lessons/de/visubot/08-farben.md b/web/lessons/de/visubot/08-farben.md deleted file mode 100644 index 6ea8bd77..00000000 --- a/web/lessons/de/visubot/08-farben.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -titel: Farben erkennen -bereich: Sensoren und Entscheidungen -kurz: Der Quad-RGB-Sensor sagt, auf welcher Farbe der mBot2 steht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit `mbot.getColor().equals(…)` eine Farbe abfragen -- mit `!` eine Bedingung umdrehen -- eine Verzweigung in eine Schleife schreiben -::: - -## Ein Sensor, der Farben sieht - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der [Liniensensor]{teil=lineSensor} heißt eigentlich **Quad-RGB-Sensor**. „Quad“ heißt vier (vier Fühler), „RGB“ steht für Rot, Grün und Blau. Er leuchtet den Boden in diesen drei Farben an und misst, welche Farbe zurückkommt. So erkennt er farbige Felder. - -`mbot.getColor()` liefert den Namen der Farbe unter dem mBot2, und zwar als **Text**: - -| Boden | `mbot.getColor()` | -|---|---| -| rotes Feld | `"rot"` | -| grünes Feld | `"grün"` | -| blaues Feld | `"blau"` | -| gelbes Feld | `"gelb"` | -| schwarzes Feld oder Linie | `"schwarz"` | -| heller Boden | `"weiß"` | - -Ob es eine bestimmte Farbe ist, fragst du mit `.equals(…)`: `mbot.getColor().equals("rot")` ist **wahr**, wenn der mBot2 auf Rot steht, sonst **falsch**. Das ist also schon eine fertige Bedingung. - -> Texte vergleichst du in Java immer mit `.equals(…)`, **nicht** mit `==`. Das `==` sieht richtig aus, klappt bei Texten aber oft nicht. - -> Mit dem `!` davor drehst du eine Bedingung um: `!mbot.getColor().equals("rot")` heißt „**nicht** rot“. - -## Fahre bis Rot im Struktogramm - -::: übung vb-bis-rot -titel: Fahre bis Rot -auftrag: Der mBot2 fährt vor, solange er **nicht** auf einem roten Feld steht. In der zweiten Welt liegt vorher ein grünes Feld, das soll ihn nicht stören. -welt: farbe-1 -> forward 20 -welt: farbe-2 -> forward 50 -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Farbe ist rot?** darauf. Schreib ein `!` vor `mbot.getColor().equals("rot")`. -tipp: Die Bedingung heißt `!mbot.getColor().equals("rot")`, in der Schleife steht **fahre vor**. -lösung: vb-bis-rot-loesung.visustruct -::: - -## Jetzt in Java: bei Grün piepen - -Jetzt kombinierst du eine Schleife mit einer Verzweigung: Der mBot2 fährt bis Blau. Kommt er unterwegs über ein grünes Feld, piept er. - -::: übung vb-java-blau-gruen -titel: Bis Blau, bei Grün piepen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange er nicht auf Blau steht. Nach jedem Schritt prüft er: Steht er auf Grün, spielt er einen Ton. In der zweiten Welt gibt es kein grünes Feld, da darf er nicht piepen. -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -braucht: whileSchleife, verzweigung -tipp: Außen steht `while (!mbot.getColor().equals("blau")) { … }`. Darin zuerst `mbot.forward();`. -tipp: Danach, noch in der Schleife: `if (mbot.getColor().equals("grün")) { mbot.playTone(880, 200); }`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -::: übung vb-fehler-08-farben -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis Blau, bei Grün piepen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-08-farben-start.visustruct -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es `equals("grün")`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Fünf Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getColor().equals("rot")) { - mbot.forward(); - } -frage: Soll vorfahren, bis er auf Rot steht. -fehler: 1 -besser: while (!mbot.getColor().equals("rot")) { -> Er soll fahren, solange er **nicht** auf Rot steht. Dafür fehlt das `!`. - -runde: - if (mbot.getColor() == "grün") { - mbot.playTone(440, 300); - } -frage: Soll piepen, wenn er auf Grün steht. -fehler: 1 -besser: if (mbot.getColor().equals("grün")) { -> Texte vergleichst du mit `.equals(…)`, nicht mit `==`. - -runde: - while (!mbot.getColor().equals("blau")) { - mbot.forward(); - if (mbot.getColor().equals("grün")) { - mbot.turnRight(); - } - } -frage: Soll bis Blau fahren und bei Grün piepen. -fehler: 4 -besser: mbot.playTone(440, 300); -> Bei Grün soll er piepen, nicht abbiegen. - -runde: - if (mbot.getColor().equals(rot)) { - mbot.turnRight(); - } -frage: Soll sich auf Rot nach rechts drehen. -fehler: 1 -besser: if (mbot.getColor().equals("rot")) { -> Ohne Anführungszeichen sucht Java eine Variable `rot`. Der Farbname ist ein Text. - -runde: - while (!mbot.getColor().equals("rot")) { - mbot.forward(); - } - mbot.playTone(440, 300) -frage: Soll bis Rot fahren und dann piepen. -fehler: 4 -besser: mbot.playTone(440, 300); -> Am Ende der Anweisung fehlt das Semikolon. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit `mbot.getColor().equals(…)` eine Farbe abfragen. → Ein Sensor, der Farben sieht -- Ich kann mit `!` eine Bedingung umdrehen. → Fahre bis Rot im Struktogramm -- Ich kann eine Verzweigung in eine Schleife schreiben. → Jetzt in Java: bei Grün piepen -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf einem roten Feld. Was ist `!mbot.getColor().equals("rot")`? -→ Fahre bis Rot im Struktogramm -- [ ] wahr -- [x] falsch -> `mbot.getColor().equals("rot")` ist wahr, das `!` macht daraus falsch. Die Schleife hört also auf. -? Womit vergleichst du die Farbe mit `"grün"`? -→ Ein Sensor, der Farben sieht -- [x] `mbot.getColor().equals("grün")` -- [ ] `mbot.getColor() == "grün"` -- [ ] `mbot.getColor() = "grün"` -> `mbot.getColor()` liefert einen Text, und Texte vergleicht man mit `.equals(…)`. Ein einzelnes `=` ist eine Zuweisung, gar kein Vergleich. -::: diff --git a/web/lessons/de/visubot/08-groesster-wert.md b/web/lessons/de/visubot/08-groesster-wert.md deleted file mode 100644 index 561ff125..00000000 --- a/web/lessons/de/visubot/08-groesster-wert.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -titel: Der größte Wert -bereich: Sensoren und Entscheidungen -kurz: Rundum messen und sich den größten Wert merken: Wo ist am meisten Platz? ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit einer Variablen den bisher größten Wert merken -- in einer Schleife jeden neuen Messwert mit dem bisher größten vergleichen -- mit dem mBot2 herausfinden, in welcher Richtung am meisten Platz ist -::: - -## Den Rekord merken - -Beim Weitsprung gibt es eine Tafel mit dem **Rekord**. Nach jedem Sprung schaut die Schiedsrichterin: Ist der neue Sprung weiter als der Rekord? Dann kommt er an die Tafel. Sonst bleibt der alte Rekord stehen. - -Genau so findet ein Programm den größten Wert. Die Tafel ist eine Variable `max`: - -1. **Vor** der Schleife: `int max = 0`. Noch steht nichts an der Tafel. -2. **In** der Schleife: Ist der neue Wert größer als `max`? Dann `max = wert`. -3. **Nach** der Schleife steht in `max` der größte Wert. - -Der mBot2 dreht sich viermal und misst jedes Mal den Abstand. So sieht das im Schreibtischtest aus: - -| Richtung | gemessen | größer als `max`? | `max` danach | -|---|---|---|---| -| vorher | | | 0 | -| vorn | 16 | ja | 16 | -| rechts | 36 | ja | 36 | -| hinten | 6 | nein | 36 | -| links | 26 | nein | 36 | - -> Wichtig: Die Verzweigung hat nur einen Ja-Zweig. Ist der Wert nicht größer, passiert nichts. Der alte Rekord bleibt. - -## Wo ist am meisten Platz? - -::: übung vb-meister-platz -titel: Wo ist am meisten Platz? -auftrag: Baue ein Struktogramm: Lege `int max = 0` an. Der mBot2 misst viermal den Abstand und dreht sich danach jeweils nach rechts. Ist ein Abstand größer als `max`, merkt er ihn sich in `max`. Zum Schluss zeigt er zum Beispiel **Am weitesten: 36 cm**. Es gibt zwei Welten. -welt: platz-1 -> screen "Am weitesten: 36 cm" -welt: platz-2 -> screen "Am weitesten: 46 cm" -braucht: forSchleife, verzweigung -tipp: Die For-Schleife läuft viermal. In ihr stehen drei Kästen: `int abstand = mbot.getDistance()`, eine Verzweigung und **drehe rechts**. -tipp: Die Verzweigung prüft `abstand > max`. Im Ja-Zweig steht `max = abstand`, der Nein-Zweig bleibt leer. Nach der Schleife: `mbot.showText("Am weitesten: " + max + " cm")`. -lösung: vb-meister-platz-loesung.visustruct -::: - -::: angeberwissen -Viele Saugroboter haben oben einen kleinen Turm mit einem Laser. Er dreht sich und misst ringsum die Abstände. So zeichnet der Roboter eine Karte der Wohnung. Bei mir zu Hause misst niemand. Ich fresse mir einfach den Weg frei. -::: - -## Jetzt in Java: Die Richtung merken - -Jetzt weiß der mBot2, **wie viel** Platz es höchstens gibt. Aber **wo**? Dafür merkt er sich in einer zweiten Variablen auch die Richtung. Die Zählvariable `i` sagt dir, die wievielte Messung es war: 0 ist vorn, 1 ist rechts, 2 ist hinten und 3 ist links. - -```java -if (abstand > max) { - max = abstand; - richtung = i; -} -``` - -Beide Zeilen stehen im Ja-Zweig. Nur wenn es einen neuen Rekord gibt, ändert sich auch die Richtung. - -::: übung vb-java-richtung -titel: Die Richtung merken -code: java -auftrag: Schreibe „Wo ist am meisten Platz?“ in Java. Merke dir zusätzlich in `int richtung = 0` die Nummer der Messung mit dem größten Abstand. Zeige zum Schluss zum Beispiel **Richtung: 1**. -welt: platz-1 -> screen "Richtung: 1" -welt: platz-2 -> screen "Richtung: 0" -braucht: forSchleife, verzweigung -tipp: Vor der Schleife stehen `int max = 0;` und `int richtung = 0;`. In der Schleife: `int abstand = mbot.getDistance();`, die Verzweigung und `mbot.turnRight();`. -tipp: Im Ja-Zweig stehen `max = abstand;` und `richtung = i;`. Nach der Schleife: `mbot.showText("Richtung: " + richtung);` -lösung: vb-java-richtung-loesung.visustruct -::: - -::: übung vb-fehler-08-groesster-wert -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Die Richtung merken“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-08-groesster-wert-start.visustruct -welt: platz-1 -> screen "Richtung: 1" -welt: platz-2 -> screen "Richtung: 0" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `richtung = i`. -lösung: vb-java-richtung-loesung.visustruct -::: - -## Spiel: Neuer Rekord? - -::: spiel frage -titel: Spiel: Neuer Rekord? -intro: Der mBot2 misst nacheinander mehrere Abstände. Was steht am Ende in `max`? Fünf Runden. - -runde: -frage: Gemessen: 20, 50, 30 -- 30 -- [x] 50 -- 100 -> 50 ist der größte Wert. Die 30 danach ist kein neuer Rekord. - -runde: -frage: Gemessen: 10, 20, 30, 40 -- 10 -- [x] 40 -- 100 -> Jeder Wert ist ein neuer Rekord. Am Ende steht der letzte da. - -runde: -frage: Gemessen: 90, 10, 20, 30 -- 30 -- [x] 90 -- 10 -> Der Rekord kommt gleich am Anfang. Danach ist kein Wert größer. - -runde: -frage: Gemessen: 15, 15, 15 -- 45 -- 0 -- [x] 15 -> 15 ist nicht größer als 15. `max` bleibt 15. - -runde: -frage: Gemessen: 25, 60, 60, 5. In welcher Richtung (`i`) ist der Rekord? -- 0 -- [x] 1 -- 2 -> Bei `i = 1` kommt die 60. Die zweite 60 ist nicht **größer**, also bleibt `richtung` bei 1. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit einer Variablen den bisher größten Wert merken. → Den Rekord merken -- Ich kann in einer Schleife jeden neuen Messwert mit dem bisher größten vergleichen. → Wo ist am meisten Platz? -- Ich kann mit dem mBot2 herausfinden, in welcher Richtung am meisten Platz ist. → Jetzt in Java: Die Richtung merken -::: - -## Wissenstest - -::: quiz -? Womit fängt `max` vor der Schleife an? -→ Den Rekord merken -- [x] Mit 0, weil noch nichts gemessen ist -- [ ] Mit dem größten Wert -- [ ] Mit 100 -> Jeder Abstand ist größer als 0, also wird schon der erste Messwert der Rekord. -? Was steht im Nein-Zweig der Verzweigung `abstand > max`? -→ Den Rekord merken -- [ ] `max = 0` -- [ ] `max = abstand` -- [x] Nichts, der alte Rekord bleibt. -? Wann ändert sich `richtung`? -→ Jetzt in Java: Die Richtung merken -- [ ] In jedem Durchgang -- [x] Nur wenn es einen neuen Rekord gibt -- [ ] Nur in der letzten Runde -::: diff --git a/web/lessons/de/visubot/09-fallauswahl-mbot2.md b/web/lessons/de/visubot/09-fallauswahl-mbot2.md deleted file mode 100644 index d24c0ba3..00000000 --- a/web/lessons/de/visubot/09-fallauswahl-mbot2.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -titel: Fallauswahl mit Farben -bereich: Sensoren und Entscheidungen -kurz: Je nach Farbe oder Linie etwas anderes tun, mit einer Fallauswahl statt vieler Verzweigungen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Fallauswahl mit Texten bauen -- eine Fallauswahl mit Zahlen in Java schreiben -- je nach Zahl einen anderen Text auf dem Bildschirm zeigen -::: - -## Viele Farben, viele Fälle - -Der mBot2 soll auf jede Farbe anders reagieren: bei Rot rechts abbiegen, bei Grün ein Stück vorfahren, bei Blau piepen. Mit Verzweigungen bräuchtest du drei ineinander. Übersichtlicher ist die **Fallauswahl**, in Java `switch`: - -```java -switch (mbot.getColor()) { - case "rot" -> { - mbot.turnRight(); - } - case "grün" -> { - mbot.forward(20); - } - default -> { - mbot.forward(10); - } -} -``` - -- Oben in den Klammern steht der Wert, der geprüft wird, hier die Farbe. -- Jeder `case` ist ein **Fall**. Passt der Wert, läuft genau dieser Fall. -- `default` ist der Fall für **alles andere**. - -Im Struktogramm ist die Fallauswahl ein Kasten mit mehreren Spalten, für jeden Fall eine. - -::: übung vb-farbweiche -titel: Farbweiche -auftrag: Baue eine Fallauswahl über `mbot.getColor()`. Bei `"rot"` dreht sich der mBot2 nach rechts. Bei `"grün"` fährt er 20 cm vor. Bei `"blau"` spielt er einen Ton mit 880 Hertz, 200 Millisekunden lang. In allen anderen Fällen fährt er 10 cm vor. -messwerte: getColor=rot -> turnRight -messwerte: getColor=grün -> forward 20 -messwerte: getColor=blau -> none, tone -messwerte: getColor=weiß -> forward 10 -braucht: fallauswahl -tipp: Zieh eine Fallauswahl ins Struktogramm. Oben steht `mbot.getColor()`, die Fälle heißen `"rot"`, `"grün"` und `"blau"`, dazu der Fall für alles andere. -tipp: Bei Rot `mbot.turnRight()`, bei Grün `mbot.forward(20)`, bei Blau `mbot.playTone(880, 200)`, sonst `mbot.forward(10)`. -lösung: vb-farbweiche-loesung.visustruct -::: - -## Jetzt in Java: Linienmuster - -Auch eine Zahl kann der Wert einer Fallauswahl sein. `mbot.getLine()` aus Lektion 7 liefert zum Beispiel: - -| Wert | Bedeutung | -|---|---| -| `0` | keine Linie | -| `6` | die beiden mittleren Fühler sehen die Linie: Er steht genau drauf | -| `15` | alle vier sehen Schwarz: eine Querlinie | - -Bei Zahlen schreibst du den Fall ohne Anführungszeichen: `case 6 -> { … }`. - -::: übung vb-linienmuster -titel: Linienmuster -code: java -auftrag: Schreibe in Java eine Fallauswahl über `mbot.getLine()`. Bei 6 fährt der mBot2 10 cm vor. Bei 0 dreht er sich nach rechts. Bei 15 zeigt er **Querlinie!** auf dem Bildschirm. In allen anderen Fällen fährt er 5 cm vor. -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Querlinie!" -messwerte: getLine=4 -> forward 5 -braucht: fallauswahl -tipp: `switch (mbot.getLine()) { case 6 -> { … } case 0 -> { … } case 15 -> { … } default -> { … } }` -tipp: Bei 6 `mbot.forward(10);`, bei 0 `mbot.turnRight();`, bei 15 `mbot.showText("Querlinie!");`, sonst `mbot.forward(5);`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Jetzt in Java: Startsignal - -Vor einem Rennen zeigt der mBot2 nacheinander drei Texte und fährt dann los. Eine For-Schleife zählt die Runden 1, 2 und 3. Die Fallauswahl sucht zu jeder Runde den passenden Text: - -```java -for (int runde = 1; runde <= 3; runde++) { - switch (runde) { - case 1 -> { mbot.showText("Auf die Plätze"); } - ... - } - mbot.pause(1000); -} -``` - -::: übung vb-startsignal -titel: Startsignal -code: java -auftrag: Schreibe in Java: Eine Schleife zählt `runde` von 1 bis 3. In Runde 1 zeigt der Bildschirm **Auf die Plätze**, in Runde 2 **Fertig**, sonst **Los!**. Nach jedem Text wartet der mBot2 eine Sekunde. Nach der Schleife fährt er 50 cm vor. -befehle: showText "Auf die Plätze", pause 1000, showText "Fertig", pause 1000, showText "Los!", pause 1000 -fahrt: forward 50 -braucht: fallauswahl -tipp: Die Schleife heißt `for (int runde = 1; runde <= 3; runde++) { … }`. Darin steht `switch (runde) { … }` und danach `mbot.pause(1000);`. -tipp: `case 1 -> { mbot.showText("Auf die Plätze"); }`, `case 2 -> { … }` und `default -> { mbot.showText("Los!"); }`. Nach der Schleife `mbot.forward(50);`. -lösung: vb-startsignal-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Fallauswahl“](?visustruct&lektion=10-fallauswahl). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-09-fallauswahl-mbot2 -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Linienmuster“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-09-fallauswahl-mbot2-start.visustruct -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Querlinie!" -messwerte: getLine=4 -> forward 5 -tipp: Lies die Fehlermeldung genau. Ist der Befehl richtig geschrieben? Nach `mbot.` schlägt dir der Editor alle Befehle vor. -tipp: Richtig heißt es `mbot.turnRight()`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Spiel: Welcher Fall läuft? - -::: spiel frage -titel: Spiel: Welcher Fall läuft? -intro: Lies die Fallauswahl und sag vorher, was der mBot2 tut. Sechs Runden. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Grün. Was tut er? -- Er dreht sich nach rechts. -- [x] Er fährt 20 cm vor. -- Er fährt 10 cm vor. -> Der Fall `"grün"` passt, also läuft genau dieser. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Gelb. Was tut er? -- Gar nichts. -- Er piept. -- [x] Er fährt 10 cm vor. -> Für Gelb gibt es keinen Fall, also läuft `default`. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Blau. Was tut er? -- [x] Er piept. -- Er piept und fährt dann 10 cm vor. -- Er dreht sich nach rechts. -> Es läuft nur der passende Fall. `default` kommt nur dran, wenn kein anderer Fall passt. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Querlinie!"); } - default -> { mbot.stop(); } - } -frage: Alle vier Fühler sehen Schwarz. Was tut der mBot2? -- Er fährt 10 cm vor. -- [x] Er zeigt **Querlinie!** -- Er hält an. -> Vier Fühler auf Schwarz ergeben 15. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Querlinie!"); } - default -> { mbot.stop(); } - } -frage: Unter dem mBot2 ist alles weiß. Was tut er? -- Er fährt 10 cm vor. -- [x] Er dreht sich nach rechts. -- Er hält an. -> Kein Fühler sieht Schwarz, also liefert `mbot.getLine()` 0. - -runde: - for (int runde = 1; runde <= 3; runde++) { - switch (runde) { - case 1 -> { mbot.showText("Auf die Plätze"); } - case 2 -> { mbot.showText("Fertig"); } - default -> { mbot.showText("Los!"); } - } - } -frage: Was zeigt der Bildschirm in Runde 3? -- Fertig -- [x] Los! -- Auf die Plätze -> Für 3 gibt es keinen eigenen Fall, also läuft `default`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Fallauswahl mit Texten bauen. → Viele Farben, viele Fälle -- Ich kann eine Fallauswahl mit Zahlen in Java schreiben. → Jetzt in Java: Linienmuster -- Ich kann je nach Zahl einen anderen Text auf dem Bildschirm zeigen. → Jetzt in Java: Startsignal -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf Gelb. Welcher Fall der Farbweiche läuft? -→ Viele Farben, viele Fälle -- [ ] der Fall `"rot"` -- [ ] gar keiner -- [x] `default` -> Für Gelb gibt es keinen eigenen Fall. Dann läuft der Fall für alles andere. -? Wie schreibst du den Fall für die Zahl 6? -→ Jetzt in Java: Linienmuster -- [x] `case 6 -> { … }` -- [ ] `case "6" -> { … }` -- [ ] `if 6 -> { … }` -> Zahlen stehen ohne Anführungszeichen, Texte wie `"rot"` mit. -::: diff --git a/web/lessons/de/visubot/10-tasten-joystick.md b/web/lessons/de/visubot/10-tasten-joystick.md deleted file mode 100644 index a0780669..00000000 --- a/web/lessons/de/visubot/10-tasten-joystick.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -titel: Tasten und Joystick -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 wartet auf dich und lässt sich fernsteuern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Sensoren für Tasten und Joystick abfragen -- auf eine Taste warten -- den mBot2 mit dem Joystick steuern -::: - -## Drücken als Sensor - -::: mbot2 -fokus: joystick, buttons -ansicht: oben -::: - -Auf dem CyberPi sitzen zwei [Tasten]{teil=buttons} (A und B) und ein [Joystick]{teil=joystick}. Für dein Programm sind das Sensoren: Sie liefern **wahr**, solange du drückst. - -| Sensor | Text | -|---|---| -| Taste A / B gedrückt? | `mbot.isButtonA()`, `mbot.isButtonB()` | -| Joystick nach oben / unten | `mbot.isJoystickUp()`, `mbot.isJoystickDown()` | -| Joystick nach links / rechts | `mbot.isJoystickLeft()`, `mbot.isJoystickRight()` | - -> Ohne echten mBot2 klappst du in der [Simulation](#zeige-simulation) unter der Welt **🎛 Sensoren ausprobieren** auf. Dort gibt es die Knöpfe A, B und die Pfeile für den Joystick. Ein Klick hält sie eine Sekunde lang gedrückt. - -## Warten auf Taste A im Struktogramm - -Ein Roboter soll nicht sofort losfahren, wenn das Programm startet. Besser: Er wartet, bis du A drückst. - -Warten heißt: eine Schleife, die so lange läuft, wie A **nicht** gedrückt ist. Damit sie nicht ganz leer ist, wartet der mBot2 darin kurz mit **kurz warten** (`mbot.pause(100)`, also 100 Millisekunden). - -::: übung vb-start-a -titel: Start mit Taste A -auftrag: Der mBot2 wartet, bis Taste A gedrückt ist. Dann fährt er 30 cm vor. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: Eine While-Schleife mit der Bedingung `!mbot.isButtonA()`. Darin steht **kurz warten**. -tipp: Nach der Schleife steht `mbot.forward(30)`. -lösung: vb-start-a-loesung.visustruct -::: - -> Beim Prüfen ist Taste A erst eine Weile nicht gedrückt und dann gedrückt. Fährt dein mBot2 zu früh los, fällt das auf. - -## Jetzt in Java: Fernsteuerung - -Mit dem Joystick lenkst du den mBot2. Das Programm fragt fünfmal nacheinander den Joystick ab: nach oben heißt vorfahren, nach unten zurückfahren. - -::: übung vb-java-joystick -titel: Fernsteuerung -code: java -auftrag: Schreibe in Java eine For-Schleife, die fünfmal läuft. Darin: Ist der Joystick oben, fährt der mBot2 vor. Sonst: Ist er unten, fährt er zurück. -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -braucht: forSchleife, verzweigung -tipp: `for (int i = 0; i < 5; i++) { … }` und darin `if (mbot.isJoystickUp()) { … } else { … }`. -tipp: Im `else` steht noch eine Verzweigung: `if (mbot.isJoystickDown()) { mbot.backward(); }`. -lösung: vb-java-joystick-loesung.visustruct -::: - -::: übung vb-fehler-10-tasten-joystick -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Fernsteuerung“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-10-tasten-joystick-start.visustruct -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.backward()`. -lösung: vb-java-joystick-loesung.visustruct -::: - -## Spiel: Was tut der mBot2? - -::: spiel frage -titel: Spiel: Was tut der mBot2? -intro: Du drückst eine Taste oder den Joystick. Was tut der mBot2? Sechs Runden. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Niemand drückt eine Taste. Was tut der mBot2? -- Er fährt 30 cm vor. -- [x] Er wartet weiter. -- Er piept. -> Solange A nicht gedrückt ist, bleibt er in der Warteschleife. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du drückst Taste B. Was tut der mBot2? -- Er fährt 30 cm vor. -- [x] Er wartet weiter. -- Er fährt 30 cm zurück. -> Die Schleife fragt nur nach Taste A. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du drückst Taste A. Was tut der mBot2? -- [x] Er fährt 30 cm vor. -- Er wartet weiter. -- Er wartet 100 ms und hört dann auf. -> Jetzt ist `!mbot.isButtonA()` falsch, die Schleife endet, und er fährt los. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: Du drückst den Joystick nach unten. Was tut der mBot2? -- Er fährt vor. -- [x] Er fährt zurück. -- Gar nichts. -> Die erste Bedingung ist falsch, die zweite wahr. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: Du drückst den Joystick nach links. Was tut der mBot2? -- Er dreht sich nach links. -- Er fährt zurück. -- [x] Gar nichts. -> Für links gibt es keinen Zweig. Beide Bedingungen sind falsch. - -runde: -frage: Du hältst Taste A gedrückt. Was liefert `mbot.isButtonA()`? -- [x] true -- false -- 1 -> Tasten liefern wahr oder falsch, und gedrückt heißt wahr. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Sensoren für Tasten und Joystick abfragen. → Drücken als Sensor -- Ich kann auf eine Taste warten. → Warten auf Taste A im Struktogramm -- Ich kann den mBot2 mit dem Joystick steuern. → Jetzt in Java: Fernsteuerung -::: - -## Wissenstest - -::: quiz -? Was liefert `mbot.isButtonB()`, wenn niemand drückt? -→ Drücken als Sensor -- [ ] 0 -- [x] false -- [ ] B -> Tasten liefern wahr oder falsch. -? Warum steht in der Warteschleife **kurz warten**? -→ Warten auf Taste A im Struktogramm -- [x] Damit die Schleife nicht leer ist und der CyberPi nicht unnötig schnell im Kreis rennt. -- [ ] Damit die Taste länger leuchtet. -- [ ] Ohne Pause fährt der mBot2 rückwärts. -> Die Schleife fragt alle 100 Millisekunden nach, das reicht völlig. -::: diff --git a/web/lessons/de/visubot/10-unterbrechen-mit-break.md b/web/lessons/de/visubot/10-unterbrechen-mit-break.md deleted file mode 100644 index 556fadf2..00000000 --- a/web/lessons/de/visubot/10-unterbrechen-mit-break.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -titel: Notbremse mit break -bereich: Sensoren und Entscheidungen -kurz: Eine Endlosschleife mit Aussprung: Der mBot2 fährt immer weiter, bis die Notbremse greift. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Endlosschleife mit einem Aussprung beenden -- erklären, warum der Aussprung in einer Verzweigung steht -- in Java eine Schleife mit `while (true)` und `break` schreiben -::: - -## Endlosschleife mit Aussprung - -Viele Roboter-Programme laufen „für immer“: Der mBot2 fährt, misst, fährt, misst … Dafür gibt es die **Endlosschleife**. Sie hat keine Bedingung und hört von selbst nie auf. - -Damit der mBot2 trotzdem anhalten kann, brauchst du eine **Notbremse**: den **Aussprung**. Er verlässt sofort die Schleife, in der er steht. Das Programm macht mit dem ersten Kasten **nach** der Schleife weiter. Du erkennst ihn an der Spitze am linken Rand. In den Kasten schreibst du einen kurzen Kommentar, zum Beispiel `Schleife verlassen`. - -Ein Aussprung steht fast immer in einer **Verzweigung**. Sonst würde die Schleife schon beim ersten Durchgang enden. - -::: übung vb-notbremse -titel: Notbremse an der Wand -auftrag: Baue eine Endlosschleife. Darin prüft eine Verzweigung: Ist der Abstand kleiner als 15 cm, kommt ein Aussprung. Danach (unter der Verzweigung, noch in der Schleife) fährt der mBot2 vor. Nach der Schleife spielt er einen Ton. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -braucht: endlosschleife, aussprung -tipp: Zieh die **Endlosschleife** ins Struktogramm. In die Schleife kommt zuerst eine Verzweigung mit `mbot.getDistance() < 15`, im true-Zweig der **Aussprung**, der false-Zweig bleibt leer. -tipp: Unter die Verzweigung (in der Schleife) kommt **fahre vor**, unter die Schleife `mbot.playTone(440, 300)`. -lösung: vb-notbremse-loesung.visustruct -::: - -## Jetzt in Java: Stopp mit Taste A - -In Java ist die Endlosschleife `while (true)`. Der Aussprung heißt `break`: - -```java -while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); -} -``` - -::: übung vb-java-taste-stopp -titel: Stopp mit Taste A -code: java -auftrag: Schreibe in Java: Der mBot2 fährt in einer Endlosschleife immer weiter vor. Wird Taste A gedrückt, verlässt er die Schleife mit `break`. Danach zeigt der Bildschirm **Gestoppt**. -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -braucht: endlosschleife, aussprung -tipp: `while (true) { … }` mit `if (mbot.isButtonA()) { break; }` und danach `mbot.forward();`. -tipp: Nach der Schleife `mbot.showText("Gestoppt");`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Aufruf und Aussprung“](?visustruct&lektion=18-aufruf-und-aussprung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-10-unterbrechen-mit-break -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Stopp mit Taste A“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-10-unterbrechen-mit-break-start.visustruct -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es `mbot.showText("Gestoppt")`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -## Spiel: Notbremse - -::: spiel frage -titel: Spiel: Notbremse -intro: Wann greift der Aussprung, und was passiert danach? Jede Fahrt ist 10 cm lang. Fünf Runden. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Die Wand ist am Anfang 40 cm entfernt. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- immer weiter -> Bei 40, 30 und 20 fährt er. Bei 10 greift die Notbremse. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Was passiert direkt nach `break`? -- Die Schleife beginnt von vorn. -- Er fährt noch einmal vor. -- [x] Er piept. -> `break` verlässt die Schleife. Weiter geht es mit dem ersten Befehl danach. - -runde: - while (true) { - mbot.forward(); - break; - } -frage: Wie oft fährt der mBot2 vor? -- [x] 1-mal -- gar nicht -- immer weiter -> Der Aussprung steht nicht in einer Verzweigung. Die Schleife endet schon im ersten Durchgang. - -runde: - while (true) { - mbot.forward(); - } -frage: Wann hört diese Schleife auf? -- nach 10 Fahrten -- an der Wand -- [x] nie -> Ohne `break` ist `while (true)` wirklich endlos. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Du drückst Taste A, nachdem er zweimal vorgefahren ist. Wie oft ist er vorgefahren? -- [x] 2-mal -- 3-mal -- gar nicht -> Beim nächsten Durchgang merkt er die Taste und springt aus der Schleife. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (true) { - if (mbot.getDistance() > 15) { - break; - } - mbot.forward(); - } -frage: Soll vorfahren und an der Wand mit der Notbremse anhalten. -fehler: 2 -besser: if (mbot.getDistance() < 15) { -> Die Notbremse soll greifen, wenn die Wand **nah** ist. - -runde: - while (true) { - if (mbot.isButtonB()) { - break; - } - mbot.forward(); - } - mbot.showText("Gestoppt"); -frage: Soll vorfahren, bis Taste A gedrückt wird, und dann „Gestoppt“ zeigen. -fehler: 2 -besser: if (mbot.isButtonA()) { -> Gebremst werden soll mit Taste A, nicht mit B. - -runde: - while (true) { - if (mbot.isButtonA()) { - brake; - } - mbot.forward(); - } -frage: Soll vorfahren, bis Taste A gedrückt wird. -fehler: 3 -besser: break; -> Der Aussprung heißt `break`, nicht `brake`. - -runde: - while (false) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Soll in einer Endlosschleife fahren, bis Taste A gedrückt wird. -fehler: 1 -besser: while (true) { -> Die Endlosschleife heißt `while (true)`. Mit `false` läuft sie kein einziges Mal. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Endlosschleife mit einem Aussprung beenden. → Endlosschleife mit Aussprung -- Ich kann erklären, warum der Aussprung in einer Verzweigung steht. → Endlosschleife mit Aussprung -- Ich kann in Java eine Schleife mit `while (true)` und `break` schreiben. → Jetzt in Java: Stopp mit Taste A -::: - -## Wissenstest - -::: quiz -? Wo macht das Programm nach einem Aussprung weiter? -→ Endlosschleife mit Aussprung -- [ ] am Anfang der Schleife -- [x] beim ersten Kasten nach der Schleife -- [ ] ganz am Anfang des Programms -> Der Aussprung verlässt die Schleife sofort. -? Warum steht der Aussprung in einer Verzweigung? -→ Endlosschleife mit Aussprung -- [x] Sonst endet die Schleife schon im ersten Durchgang. -- [ ] Sonst fährt der mBot2 rückwärts. -- [ ] Das ist nur schöner. -> Nur wenn die Bedingung zutrifft, soll die Notbremse greifen. -? Wie heißt die Endlosschleife in Java? -→ Jetzt in Java: Stopp mit Taste A -- [ ] `while (false)` -- [x] `while (true)` -- [ ] `for (;;) break;` -> `true` ist immer wahr, die Schleife hört nur mit `break` auf. -::: diff --git a/web/lessons/de/visubot/11-licht.md b/web/lessons/de/visubot/11-licht.md deleted file mode 100644 index 66881c98..00000000 --- a/web/lessons/de/visubot/11-licht.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -titel: Der Lichtsensor -bereich: Sensoren und Entscheidungen -kurz: Hell oder dunkel? Der mBot2 wird zum Nachtlicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Helligkeit abfragen -- mit einer Verzweigung die LEDs schalten -- mehrere Befehle in einen Zweig schreiben -::: - -## Wie hell ist es? - -::: mbot2 -fokus: lightSensor -ansicht: oben -::: - -Der [Lichtsensor]{teil=lightSensor} sitzt auf dem CyberPi. `mbot.getBrightness()` liefert eine Zahl von **0** (stockdunkel) bis **100** (sehr hell). - -| Sensor | Text | Wert | -|---|---|---| -| Helligkeit | `mbot.getBrightness()` | 0 bis 100 | - -> In der Simulation stellst du die Helligkeit mit dem Regler unter **🎛 Sensoren ausprobieren** ein. Mit einem echten mBot2 hältst du einfach die Hand über den CyberPi. - -## Nachtlicht im Struktogramm - -::: übung vb-nachtlicht -titel: Nachtlicht -auftrag: Ist die Helligkeit kleiner als 30, leuchten die LEDs weiß (`mbot.setLed(255, 255, 255)`). Sonst gehen die LEDs aus (`mbot.ledOff()`). -messwerte: getBrightness=10 -> ledOn -messwerte: getBrightness=80 -> ledOff -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und den Sensor **Helligkeit** darauf. Schreib `< 30` dahinter. -tipp: Im true-Zweig steht **LEDs an** mit `mbot.setLed(255, 255, 255)`, im false-Zweig **LEDs aus**. -lösung: vb-nachtlicht-loesung.visustruct -::: - -::: angeberwissen -Straßenlaternen machen es genauso: Ein Lichtsensor merkt, wann es dämmert, und schaltet sie ein. Darum gehen sie im Winter früher an als im Sommer, ohne dass jemand eine Uhr umstellt. -::: - -## Jetzt in Java: Nachtlicht mit Ton - -::: übung vb-java-nachtlicht-ton -titel: Nachtlicht mit Ton -code: java -auftrag: Schreibe das Nachtlicht in Java, aber mit zwei Änderungen: Im Dunkeln leuchten die LEDs gelb (`mbot.setLed(255, 200, 0)`) **und** der mBot2 spielt einen Ton. Im Hellen gehen die LEDs aus, ohne Ton. -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -braucht: verzweigung -tipp: `if (mbot.getBrightness() < 30) { … } else { … }` -tipp: Im `if`-Teil stehen zwei Zeilen: `mbot.setLed(255, 200, 0);` und `mbot.playTone(440, 300);`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -::: übung vb-fehler-11-licht -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Nachtlicht mit Ton“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-11-licht-start.visustruct -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es `mbot.setLed(255, 200, 0)`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -## Spiel: Licht an oder aus? - -::: spiel frage -titel: Spiel: Licht an oder aus? -intro: Wie hell ist es, und was tun die LEDs? Sechs Runden. -antworten: LEDs an | LEDs aus - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Es ist fast dunkel, die Helligkeit ist 10. -richtig: LEDs an -> 10 ist kleiner als 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist 45. -richtig: LEDs aus -> 45 ist nicht kleiner als 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist genau 30. -richtig: LEDs aus -> 30 ist nicht kleiner als 30. Für „bis einschließlich 30“ bräuchtest du `<=`. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist 29. -richtig: LEDs an -> 29 ist gerade noch kleiner als 30. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: Die Helligkeit ist 50. -richtig: LEDs an -> Achtung, hier ist es umgekehrt: 50 ist nicht größer als 70, also läuft der false-Zweig. Die LEDs leuchten gelb. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: Die Sonne scheint, die Helligkeit ist 100. -richtig: LEDs aus -> 100 ist größer als 70, also läuft der true-Zweig. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Helligkeit abfragen. → Wie hell ist es? -- Ich kann mit einer Verzweigung die LEDs schalten. → Nachtlicht im Struktogramm -- Ich kann mehrere Befehle in einen Zweig schreiben. → Jetzt in Java: Nachtlicht mit Ton -::: - -## Wissenstest - -::: quiz -? `mbot.getBrightness()` liefert 45. Was tut das Nachtlicht? -→ Nachtlicht im Struktogramm -- [ ] Die LEDs leuchten. -- [x] Die LEDs gehen aus. -> 45 ist nicht kleiner als 30. -? In welchem Bereich liegen die Werte des Lichtsensors? -→ Wie hell ist es? -- [ ] 0 bis 255 -- [x] 0 bis 100 -- [ ] -100 bis 100 -::: diff --git a/web/lessons/de/visubot/11-merker-boolean.md b/web/lessons/de/visubot/11-merker-boolean.md deleted file mode 100644 index e8c53ec6..00000000 --- a/web/lessons/de/visubot/11-merker-boolean.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -titel: Ein Merker mit boolean -bereich: Sensoren und Entscheidungen -kurz: Mit einer Variablen vom Typ boolean festhalten, ob etwas passiert ist. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable vom Typ `boolean` anlegen -- mit einem Merker festhalten, ob etwas passiert ist -- einen Merker nach der Schleife in einer Bedingung abfragen -::: - -## Wahr oder falsch - -Eine Variable vom Typ `boolean` kennt nur zwei Werte: `true` (wahr) und `false` (falsch). Du kennst das schon von den Sensoren: `mbot.isButtonA()` liefert `true` oder `false`. Auch ein Vergleich wie `mbot.getDistance() < 10` ist am Ende `true` oder `false`. - -| Anlegen | Bedeutung | -|---|---| -| `boolean dunkel = false` | Es war noch nicht dunkel. | -| `dunkel = true` | Jetzt war es dunkel. | -| `if (dunkel)` | Ist `dunkel` wahr? | - -Bei `if (dunkel)` brauchst du kein `== true`. Die Variable ist ja selbst schon wahr oder falsch. - -## Der Merker - -Der mBot2 soll drei Sekunden lang Wache halten. Er misst immer wieder die Helligkeit. Danach soll er sagen, ob es **irgendwann** dunkel war. - -Dafür nimmst du einen **Merker**: eine `boolean`-Variable, die mit `false` anfängt. Wird es dunkel, setzt du sie auf `true`. Danach setzt sie niemand mehr zurück. Nach der Schleife weißt du also: War es auch nur ein einziges Mal dunkel? - -::: übung vb-wurde-es-dunkel -titel: Wurde es dunkel? -auftrag: Baue ein Struktogramm: Lege `boolean dunkel = false` an. Prüfe in einer For-Schleife 30-mal: Ist die Helligkeit kleiner als 20, wird `dunkel` wahr. Warte in jedem Durchgang 100 ms. Zeige danach **Es war dunkel** oder **Immer hell**. -messwerte: getBrightness=80/10/80 -> none, screen "Es war dunkel" -messwerte: getBrightness=80 -> none, screen "Immer hell" -braucht: forSchleife, verzweigung -tipp: In der Schleife steht eine Verzweigung mit `mbot.getBrightness() < 20`. Im Ja-Zweig steht `dunkel = true`, der Nein-Zweig bleibt leer. Danach kommt `mbot.pause(100)`. -tipp: Nach der Schleife kommt eine zweite Verzweigung mit der Bedingung `dunkel`. Ja: `mbot.showText("Es war dunkel")`, Nein: `mbot.showText("Immer hell")`. -lösung: vb-wurde-es-dunkel-loesung.visustruct -::: - -> Beim Prüfen wird es in der Mitte der Wache kurz dunkel und dann wieder hell. Zeigt dein mBot2 trotzdem **Es war dunkel**? - -## Jetzt in Java: Wurde geklatscht? - -In Java sieht der Merker so aus: - -```java -boolean laut = false; -for (int i = 0; i < 30; i++) { - if (mbot.getLoudness() > 60) { - laut = true; - } - mbot.pause(100); -} -``` - -`true` und `false` schreibst du in Java immer klein. - -::: übung vb-java-klatsch-merker -titel: Wurde geklatscht? -code: java -auftrag: Schreibe in Java: Der mBot2 hört drei Sekunden lang zu (30-mal mit 100 ms Pause). Ist die Lautstärke einmal größer als 60, merkt er sich das in `boolean laut`. Danach zeigt er **Geklatscht!** oder **Alles still**. -messwerte: getLoudness=0/90/0 -> none, screen "Geklatscht!" -messwerte: getLoudness=0 -> none, screen "Alles still" -braucht: forSchleife, verzweigung -tipp: Nimm den Code oben und schreibe danach `if (laut) { … } else { … }`. -tipp: In die Zweige kommen `mbot.showText("Geklatscht!");` und `mbot.showText("Alles still");`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -::: übung vb-fehler-11-merker-boolean -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wurde geklatscht?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-11-merker-boolean-start.visustruct -messwerte: getLoudness=0/90/0 -> none, screen "Geklatscht!" -messwerte: getLoudness=0 -> none, screen "Alles still" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie schreibt man wahr und falsch in Java? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `laut = true`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -## Spiel: true oder false? - -::: spiel frage -titel: Spiel: true oder false? -intro: Ludwig fragt, was in der Bedingung herauskommt. Sechs Runden. -frage: Ist es true oder false? -antworten: true | false - -runde: -frage: `mbot.getDistance() < 10`, der Abstand ist 25 cm. -richtig: false -> 25 ist nicht kleiner als 10. - -runde: -frage: `mbot.isButtonA()`, und du drückst gerade Taste A. -richtig: true -> Der Sensor meldet: gedrückt. - -runde: -frage: `boolean dunkel = false`. In der Schleife wird es nie dunkel. Was ist `dunkel` danach? -richtig: false -> Niemand hat den Merker auf `true` gesetzt. - -runde: -frage: `boolean dunkel = false`. In der Schleife wird es einmal dunkel, danach wieder hell. Was ist `dunkel` danach? -richtig: true -> Ein Merker bleibt `true`, wenn er einmal gesetzt wurde. - -runde: -frage: `mbot.getBrightness() > 50`, die Helligkeit ist 50. -richtig: false -> 50 ist nicht größer als 50. - -runde: -frage: `!mbot.isButtonA()`, und du drückst Taste A nicht. -richtig: true -> Taste A ist nicht gedrückt, also `false`. Das `!` dreht es um: `true`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable vom Typ `boolean` anlegen. → Wahr oder falsch -- Ich kann mit einem Merker festhalten, ob etwas passiert ist. → Der Merker -- Ich kann einen Merker nach der Schleife in einer Bedingung abfragen. → Jetzt in Java: Wurde geklatscht? -::: - -## Wissenstest - -::: quiz -? Welche Werte kann eine Variable vom Typ `boolean` haben? -→ Wahr oder falsch -- [ ] Jede ganze Zahl -- [x] Nur `true` und `false` -- [ ] Nur 0 und 1 -? Womit fängt ein Merker an? -→ Der Merker -- [x] Mit `false`, weil noch nichts passiert ist -- [ ] Mit `true` -- [ ] Mit dem ersten Messwert -> Erst wenn das Ereignis passiert, wird er `true`. -? Wie fragst du in Java ab, ob der Merker `laut` wahr ist? -→ Jetzt in Java: Wurde geklatscht? -- [x] `if (laut)` -- [ ] `if (laut = true)` -- [ ] `if laut` -> `laut` ist selbst schon wahr oder falsch. Ein einzelnes `=` wäre eine Zuweisung. -::: diff --git a/web/lessons/de/visubot/12-lautstaerke.md b/web/lessons/de/visubot/12-lautstaerke.md deleted file mode 100644 index ad8b47ce..00000000 --- a/web/lessons/de/visubot/12-lautstaerke.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -titel: Das Mikrofon -bereich: Sensoren und Entscheidungen -kurz: Klatschen zum Losfahren und eine Lärmampel für das Klassenzimmer. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Lautstärke abfragen -- warten, bis etwas passiert -- eine Lärmampel in Java schreiben -::: - -## Wie laut ist es? - -::: mbot2 -fokus: microphone -ansicht: oben -::: - -Das [Mikrofon]{teil=microphone} des CyberPi misst die Lautstärke. `mbot.getLoudness()` liefert **0** (still) bis **100** (sehr laut). Ein Klatschen in der Nähe ergibt ungefähr 60 bis 90. - -| Sensor | Text | Wert | -|---|---|---| -| Lautstärke | `mbot.getLoudness()` | 0 bis 100 | - -## Klatschen zum Losfahren im Struktogramm - -Wie beim Warten auf Taste A: Eine Schleife wartet, solange es leise ist. Erst dann fährt der mBot2 los. - -::: übung vb-klatschen -titel: Klatschen zum Losfahren -auftrag: Der mBot2 wartet, solange die Lautstärke kleiner als 50 ist. Danach fährt er 30 cm vor. -messwerte: getLoudness=0/80 -> forward 30 -messwerte: getLoudness=90 -> forward 30 -braucht: whileSchleife -tipp: Eine While-Schleife mit der Bedingung `mbot.getLoudness() < 50`, darin **kurz warten**. -tipp: Nach der Schleife: `mbot.forward(30)`. -lösung: vb-klatschen-loesung.visustruct -::: - -> In der Simulation schiebst du den Regler **Lautstärke** nach rechts, während das Programm wartet. - -## Jetzt in Java: Lärmampel - -::: übung vb-java-laermampel -titel: Lärmampel -code: java -auftrag: Schreibe in Java: Ist die Lautstärke größer als 60, leuchten die LEDs rot. Sonst sind sie aus. -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -braucht: verzweigung -tipp: `if (mbot.getLoudness() > 60) { … } else { … }` -tipp: Rot ist `mbot.setLed(255, 0, 0);`, aus ist `mbot.ledOff();`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -::: angeberwissen -Lautstärke misst man eigentlich in **Dezibel** (dB). Flüstern hat etwa 30 dB, ein Klassenzimmer 60 bis 70 dB, ein Presslufthammer 100 dB. Der CyberPi rechnet das in eine einfache Zahl von 0 bis 100 um. -::: - -::: übung vb-fehler-12-lautstaerke -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Lärmampel“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-12-lautstaerke-start.visustruct -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.ledOff()`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -## Spiel: Laut oder leise? - -::: spiel frage -titel: Spiel: Laut oder leise? -intro: Wie laut ist es, und was tut der mBot2? Sechs Runden. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: In der Klasse ist es ruhig, die Lautstärke ist 30. -- [x] Er wartet weiter. -- Er fährt 30 cm vor. -> 30 ist kleiner als 50, die Warteschleife läuft weiter. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du klatschst, die Lautstärke ist 70. -- Er wartet weiter. -- [x] Er fährt 30 cm vor. -> 70 ist nicht kleiner als 50. Die Schleife endet, und er fährt los. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: Die Lautstärke ist genau 50. -- Er wartet weiter. -- [x] Er fährt 30 cm vor. -> 50 ist nicht kleiner als 50. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Die Lautstärke ist 65. -- [x] Die LEDs leuchten rot. -- Die LEDs sind aus. -> 65 ist größer als 60: zu laut. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Die Lautstärke ist genau 60. -- Die LEDs leuchten rot. -- [x] Die LEDs sind aus. -> 60 ist nicht größer als 60. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Jemand flüstert, die Lautstärke ist 10. -- Die LEDs leuchten rot. -- [x] Die LEDs sind aus. -> Flüstern ist leise. Die Lärmampel bleibt aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Lautstärke abfragen. → Wie laut ist es? -- Ich kann warten, bis etwas passiert. → Klatschen zum Losfahren im Struktogramm -- Ich kann eine Lärmampel in Java schreiben. → Jetzt in Java: Lärmampel -::: - -## Wissenstest - -::: quiz -? Die Klatsch-Schleife lautet `while (mbot.getLoudness() < 50)`. Es ist 30 laut. Was passiert? -→ Klatschen zum Losfahren im Struktogramm -- [x] Der mBot2 wartet weiter. -- [ ] Der mBot2 fährt los. -> 30 ist kleiner als 50, die Bedingung ist wahr, die Schleife läuft weiter. -::: diff --git a/web/lessons/de/visubot/13-neigung.md b/web/lessons/de/visubot/13-neigung.md deleted file mode 100644 index 31914e86..00000000 --- a/web/lessons/de/visubot/13-neigung.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -titel: Der Lagesensor -bereich: Sensoren und Entscheidungen -kurz: Neigung messen und Schütteln erkennen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was der Lagesensor misst -- zwei Bedingungen mit „oder“ verbinden -- mehrere Bedingungen mit „und“ verbinden -::: - -## Schief oder gerade? - -::: mbot2 -fokus: cyberpi -drehen: ja -::: - -Im [CyberPi]{teil=cyberpi} steckt ein **Lagesensor** (Gyroskop). Er misst, wie schief der mBot2 steht, in Grad: - -| Sensor | Text | Wert | -|---|---|---| -| Neigung vorn/hinten | `mbot.getPitch()` | Nase nach unten: positiv, nach oben: negativ (−90 bis 90) | -| Neigung seitlich | `mbot.getRoll()` | zur einen Seite positiv, zur anderen negativ | -| Geschüttelt? | `mbot.isShaken()` | `true` oder `false` | - -Steht der mBot2 flach auf dem Tisch, sind beide Neigungen ungefähr **0**. - -> In der Simulation gibt es dafür zwei Regler unter **🎛 Sensoren ausprobieren** und den Knopf **schütteln**. Mit einem echten mBot2 kippst du ihn vorsichtig in der Hand. - -## Schräglage-Warnung im Struktogramm - -Fährt der mBot2 eine zu steile Rampe hoch oder runter, soll er warnen. Zu steil heißt: mehr als 20° nach vorn **oder** mehr als 20° nach hinten. - -::: übung vb-schraeglage -titel: Schräglage-Warnung -auftrag: Ist die Neigung vorn/hinten größer als 20 **oder** kleiner als −20, spielt der mBot2 einen Ton. Sonst tut er nichts. -messwerte: getPitch=30 -> tone -messwerte: getPitch=-30 -> tone -messwerte: getPitch=5 -> noTone -braucht: verzweigung -tipp: „oder“ heißt in einer Bedingung `||`. Zieh den Sensor **Neigung vorn/hinten** zweimal auf die Verzweigung. -tipp: Die Bedingung heißt `mbot.getPitch() > 20 || mbot.getPitch() < -20`, im true-Zweig steht **Ton spielen**. -lösung: vb-schraeglage-loesung.visustruct -::: - -## Jetzt in Java: Wasserwaage - -Eine Wasserwaage zeigt, ob etwas genau gerade ist. Der mBot2 leuchtet grün, wenn beide Neigungen zwischen −5 und 5 liegen. - -::: übung vb-java-wasserwaage -titel: Wasserwaage -code: java -auftrag: Schreibe in Java: Liegen `mbot.getPitch()` und `mbot.getRoll()` beide zwischen −5 und 5, leuchten die LEDs grün. Sonst sind sie aus. -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -braucht: verzweigung -tipp: „und“ heißt `&&`. Du brauchst vier Vergleiche: `mbot.getPitch() > -5 && mbot.getPitch() < 5 && …` -tipp: Grün ist `mbot.setLed(0, 255, 0);`, im `else` steht `mbot.ledOff();`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -::: übung vb-fehler-13-neigung -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wasserwaage“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-13-neigung-start.visustruct -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.setLed(0, 255, 0)`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -## Spiel: Zu schief? - -::: spiel frage -titel: Spiel: Zu schief? -intro: Ludwig kippt den mBot2. Was tut er? Sechs Runden. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Die Nase zeigt 30° nach unten, `mbot.getPitch()` liefert 30. -- [x] Er piept. -- Er piept nicht. -> 30 ist größer als 20. Ein wahrer Teil reicht bei `||`. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Die Nase zeigt nach oben, `mbot.getPitch()` liefert −25. -- [x] Er piept. -- Er piept nicht. -> −25 ist kleiner als −20, also ist der zweite Teil wahr. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Eine flache Rampe, `mbot.getPitch()` liefert 10. -- Er piept. -- [x] Er piept nicht. -> 10 liegt zwischen −20 und 20. Beide Teile sind falsch. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: `mbot.getPitch()` liefert genau 20. -- Er piept. -- [x] Er piept nicht. -> 20 ist nicht größer als 20. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: `mbot.getPitch()` liefert 0, `mbot.getRoll()` liefert 3. -- [x] Die LEDs leuchten grün. -- Die LEDs sind aus. -> Alle vier Teile sind wahr. Der mBot2 liegt fast gerade. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: `mbot.getPitch()` liefert 2, `mbot.getRoll()` liefert −8. -- Die LEDs leuchten grün. -- [x] Die LEDs sind aus. -> −8 ist nicht größer als −5. Bei `&&` macht ein falscher Teil alles falsch. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- ↓ 1 7 JOYSTICK: kleiner Hebel auf dem CyberPi -- → 4 1 FALLAUSWAHL: switch: für jeden Wert ein eigener Fall -- → 2 3 SENSOR: Bauteil, das etwas misst -- → 6 1 VERZWEIGUNG: je nach Bedingung das eine oder das andere -- ↓ 6 9 ULTRASCHALL: damit misst der mBot2 den Abstand: ein sehr hoher Ton und sein Echo -- → 8 3 BREAK: Notbremse: verlässt sofort die Schleife -- ↓ 8 3 BEDINGUNG: ist wahr oder falsch, z. B. mbot.getDistance() < 20 -- ↓ 1 11 HELLIGKEIT: mbot.getBrightness() misst sie -lösung: 4 7, 6 6, 12 3, 2 6, 2 7, 8 4 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was der Lagesensor misst. → Schief oder gerade? -- Ich kann zwei Bedingungen mit „oder“ verbinden. → Schräglage-Warnung im Struktogramm -- Ich kann mehrere Bedingungen mit „und“ verbinden. → Jetzt in Java: Wasserwaage -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht flach auf dem Tisch. Was liefert `mbot.getPitch()` ungefähr? -→ Schief oder gerade? -- [x] 0 -- [ ] 90 -- [ ] 100 -? Was macht `mbot.isShaken()`? -→ Schief oder gerade? -- [ ] Es schüttelt den mBot2. -- [x] Es meldet, ob jemand den mBot2 schüttelt. -> Sensoren tun nichts, sie messen nur. -::: diff --git a/web/lessons/de/visubot/14-leds.md b/web/lessons/de/visubot/14-leds.md deleted file mode 100644 index 4a7426cd..00000000 --- a/web/lessons/de/visubot/14-leds.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -titel: Die LEDs -bereich: Licht, Ton und Bildschirm -kurz: Farben mischen, eine Ampel bauen und blinken lassen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Farbe aus Rot, Grün und Blau mischen -- Farben mit Pausen nacheinander zeigen -- die LEDs in Java blinken lassen -::: - -## Rot, Grün und Blau mischen - -::: mbot2 -fokus: leds -ansicht: hinten -::: - -Die fünf [LEDs]{teil=leds} sind ein **Aktor**: Sie tun etwas, sie leuchten. Jede LED mischt ihre Farbe aus Rot, Grün und Blau, jeweils von 0 (aus) bis 255 (ganz hell). - -| Befehl | Kasten | Was passiert | -|---|---|---| -| `mbot.setLed(255, 0, 0)` | **LEDs an** | alle LEDs rot | -| `mbot.ledOff()` | **LEDs aus** | alle LEDs aus | -| `mbot.pause(1000)` | **kurz warten** | 1000 Millisekunden (1 Sekunde) warten | - -| Farbe | Rot | Grün | Blau | -|---|---|---|---| -| Rot | 255 | 0 | 0 | -| Gelb | 255 | 200 | 0 | -| Grün | 0 | 255 | 0 | -| Blau | 0 | 0 | 255 | -| Weiß | 255 | 255 | 255 | -| Lila | 160 | 0 | 255 | - -> Ohne **kurz warten** schaltet der mBot2 die Farben so schnell um, dass du nur die letzte siehst. In der Simulation siehst du die Farbe an den LEDs des 3D-Modells. - -## Ampel im Struktogramm - -::: übung vb-ampel -titel: Ampel -auftrag: Die LEDs zeigen nacheinander Rot, Gelb und Grün wie eine Ampel. Zwischen den Farben wartet der mBot2 jeweils 1000 Millisekunden. Gelb ist `255, 200, 0`. -befehle: setLed 255 0 0, pause 1000, setLed 255 200 0, pause 1000, setLed 0 255 0 -tipp: Zieh dreimal **LEDs an** und zweimal **kurz warten** ins Struktogramm, abwechselnd. -tipp: Ändere die Zahlen: `mbot.setLed(255, 0, 0)`, `mbot.pause(1000)`, `mbot.setLed(255, 200, 0)`, `mbot.pause(1000)`, `mbot.setLed(0, 255, 0)`. -lösung: vb-ampel-loesung.visustruct -::: - -## Jetzt in Java: Blaulicht - -Blinken heißt: an, warten, aus, warten, und das immer wieder. Das „immer wieder“ ist eine Schleife. - -::: übung vb-java-blinken -titel: Blaulicht -code: java -auftrag: Schreibe in Java eine For-Schleife, die dreimal läuft. Darin gehen die LEDs blau an, der mBot2 wartet 500 Millisekunden, die LEDs gehen aus, und er wartet wieder 500 Millisekunden. -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: `for (int i = 0; i < 3; i++) { … }` -tipp: In der Schleife: `mbot.setLed(0, 0, 255);`, `mbot.pause(500);`, `mbot.ledOff();`, `mbot.pause(500);`. -lösung: vb-java-blinken-loesung.visustruct -::: - -::: übung vb-fehler-14-leds -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Blaulicht“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-14-leds-start.visustruct -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.pause(500)`. -lösung: vb-java-blinken-loesung.visustruct -::: - -## Spiel: Welche Farbe? - -::: spiel frage -titel: Spiel: Welche Farbe? -intro: Rot, Grün und Blau gemischt: Welche Farbe zeigen die LEDs? Sieben Runden. - -runde: - mbot.setLed(255, 0, 0); -frage: Welche Farbe leuchtet? -- [x] Rot -- Grün -- Blau -> Nur der erste Wert, Rot, ist an. - -runde: - mbot.setLed(0, 0, 255); -frage: Welche Farbe leuchtet? -- Rot -- Grün -- [x] Blau -> Die Reihenfolge ist immer Rot, Grün, Blau. Nur Blau ist an. - -runde: - mbot.setLed(255, 255, 255); -frage: Welche Farbe leuchtet? -- Gelb -- [x] Weiß -- aus -> Alle drei Farben ganz hell ergeben Weiß. - -runde: - mbot.setLed(255, 200, 0); -frage: Welche Farbe leuchtet? -- [x] Gelb -- Lila -- Weiß -> Rot und Grün zusammen ergeben Gelb. - -runde: - mbot.setLed(160, 0, 255); -frage: Welche Farbe leuchtet? -- Gelb -- [x] Lila -- Grün -> Rot und Blau zusammen ergeben Lila. - -runde: - mbot.setLed(0, 0, 0); -frage: Welche Farbe leuchtet? -- Weiß -- Schwarz -- [x] keine, die LEDs sind aus -> Alle drei Werte sind 0. Eine LED kann nicht schwarz leuchten. - -runde: - mbot.setLed(255, 0, 0); - mbot.setLed(0, 255, 0); - mbot.pause(1000); -frage: Welche Farbe siehst du eine Sekunde lang? -- Rot -- [x] Grün -- erst Rot, dann Grün -> Ohne **kurz warten** zwischen den Farben ist Rot sofort wieder weg. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.setLed(255, 0, 0); - mbot.pause(1000); - mbot.setLed(255, 200, 0); - mbot.pause(1000); - mbot.setLed(0, 0, 255); -frage: Soll wie eine Ampel Rot, Gelb und Grün zeigen. -fehler: 5 -besser: mbot.setLed(0, 255, 0); -> Grün ist `0, 255, 0`. `0, 0, 255` ist Blau. - -runde: - for (int i = 0; i < 3; i++) { - mbot.setLed(0, 0, 255); - mbot.pause(500); - mbot.ledOff; - mbot.pause(500); - } -frage: Soll dreimal blau blinken. -fehler: 4 -besser: mbot.ledOff(); -> Auch ein Befehl ohne Zahl braucht seine Klammern. - -runde: - mbot.setLed(0, 0, 300); - mbot.pause(1000); - mbot.ledOff(); -frage: Soll eine Sekunde ganz hell blau leuchten. -fehler: 1 -besser: mbot.setLed(0, 0, 255); -> Jede Farbe geht nur von 0 bis 255. - -runde: - mbot.setLed(255, 255, 255); - mbot.pause(1000); - mbot.setLed(0, 0, 0, 0); -frage: Soll eine Sekunde weiß leuchten und dann ausgehen. -fehler: 3 -besser: mbot.ledOff(); -> `setLed` hat genau drei Zahlen: Rot, Grün, Blau. Zum Ausschalten gibt es `mbot.ledOff()`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Farbe aus Rot, Grün und Blau mischen. → Rot, Grün und Blau mischen -- Ich kann Farben mit Pausen nacheinander zeigen. → Ampel im Struktogramm -- Ich kann die LEDs in Java blinken lassen. → Jetzt in Java: Blaulicht -::: - -## Wissenstest - -::: quiz -? Welche Farbe ergibt `mbot.setLed(0, 255, 0)`? -→ Rot, Grün und Blau mischen -- [ ] Rot -- [x] Grün -- [ ] Blau -> Die Zahlen sind Rot, Grün, Blau. Nur Grün ist an. -? Warum braucht das Blaulicht **kurz warten**? -→ Jetzt in Java: Blaulicht -- [x] Sonst wechselt es so schnell, dass man kein Blinken sieht. -- [ ] Sonst gehen die LEDs kaputt. -- [ ] Weil `ledOff()` sonst nicht geht. -::: diff --git a/web/lessons/de/visubot/15-einzelne-leds.md b/web/lessons/de/visubot/15-einzelne-leds.md deleted file mode 100644 index 77e0ea03..00000000 --- a/web/lessons/de/visubot/15-einzelne-leds.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -titel: Einzelne LEDs -bereich: Licht, Ton und Bildschirm -kurz: Jede der fünf LEDs bekommt ihre eigene Farbe, bis zum Lauflicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die LEDs über ihre Nummern 0 bis 4 ansprechen -- einzelne LEDs in verschiedenen Farben leuchten lassen -- mit einer Schleife ein Lauflicht bauen -::: - -## Jede LED hat eine Nummer - -::: mbot2 -fokus: leds -ansicht: hinten -::: - -Bisher haben alle fünf [LEDs]{teil=leds} dieselbe Farbe gezeigt. Du kannst aber auch jede LED einzeln ansprechen. Dafür hat jede LED eine **Nummer**. Wie bei Arrays fängt das Zählen bei **0** an: Die fünf LEDs heißen 0, 1, 2, 3 und 4. - -| Befehl | Kasten | Was passiert | -|---|---|---| -| `mbot.led(0).setColor("grün")` | **LED-Farbe setzen** | LED 0 leuchtet grün | -| `mbot.led(3).setColor("aus")` | **LED-Farbe setzen** | LED 3 geht aus | - -Die Farbe schreibst du als Namen in Anführungszeichen: - -| Farbname | Farbname | Farbname | -|---|---|---| -| `"rot"` | `"orange"` | `"gelb"` | -| `"grün"` | `"türkis"` | `"blau"` | -| `"lila"` | `"pink"` | `"weiß"` | - -Mit `"aus"` schaltest du eine LED aus. - -> Die Nummer darf auch eine Variable sein: `mbot.led(i).setColor("rot")`. So kann eine Schleife eine LED nach der anderen einschalten. - -## Drei Lichter im Struktogramm - -::: übung vb-drei-lichter -titel: Drei Lichter -auftrag: Schalte LED 0 rot, LED 2 gelb und LED 4 grün. Die LEDs 1 und 3 bleiben aus. -befehle: ledColor 0 "rot", ledColor 2 "gelb", ledColor 4 "grün" -tipp: Zieh dreimal **LED-Farbe setzen** ins Struktogramm. -tipp: Ändere Nummer und Farbe: `mbot.led(0).setColor("rot")`, `mbot.led(2).setColor("gelb")`, `mbot.led(4).setColor("grün")`. -lösung: vb-drei-lichter-loesung.visustruct -::: - -## Jetzt in Java: Lauflicht - -Bei einem Lauflicht wandert ein Licht von einer LED zur nächsten. Die Schleifenvariable `i` ist dabei die Nummer der LED. - -::: übung vb-java-lauflicht -titel: Lauflicht -code: java -auftrag: Schreibe in Java eine For-Schleife, in der `i` von 0 bis 4 läuft. Darin geht LED `i` blau an, der mBot2 wartet 200 Millisekunden, und LED `i` geht wieder aus. -befehle: ledColor 0 "blau", pause 200, ledColor 0 "aus", ledColor 1 "blau", pause 200, ledColor 1 "aus", ledColor 2 "blau", pause 200, ledColor 2 "aus", ledColor 3 "blau", pause 200, ledColor 3 "aus", ledColor 4 "blau", pause 200, ledColor 4 "aus" -braucht: forSchleife -tipp: `for (int i = 0; i < 5; i++) { … }` -tipp: In der Schleife: `mbot.led(i).setColor("blau");`, `mbot.pause(200);`, `mbot.led(i).setColor("aus");`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -::: übung vb-fehler-15-einzelne-leds -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Lauflicht“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-15-einzelne-leds-start.visustruct -befehle: ledColor 0 "blau", pause 200, ledColor 0 "aus", ledColor 1 "blau", pause 200, ledColor 1 "aus", ledColor 2 "blau", pause 200, ledColor 2 "aus", ledColor 3 "blau", pause 200, ledColor 3 "aus", ledColor 4 "blau", pause 200, ledColor 4 "aus" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.led(i)`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -## Spiel: Welche LED leuchtet? - -::: spiel frage -titel: Spiel: Welche LED leuchtet? -intro: Fünf LEDs mit den Nummern 0 bis 4. Welche leuchten? Sechs Runden. - -runde: - mbot.led(0).setColor("rot"); -frage: Welche LED leuchtet rot? -- [x] die erste -- die zweite -- alle -> Die Nummern fangen bei 0 an. LED 0 ist die erste. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blau"); - } -frage: Welche LEDs leuchten blau? -- LED 0 bis 3 -- [x] alle fünf -- nur LED 4 -> `i` läuft von 0 bis 4, das sind alle fünf. - -runde: - for (int i = 0; i < 5; i = i + 2) { - mbot.led(i).setColor("grün"); - } -frage: Welche LEDs leuchten grün? -- LED 1 und 3 -- [x] LED 0, 2 und 4 -- LED 2 und 4 -> `i` springt in Zweierschritten: 0, 2, 4. - -runde: - mbot.led(2).setColor("gelb"); - mbot.led(2).setColor("aus"); -frage: Welche LED leuchtet am Ende? -- LED 2 gelb -- [x] keine -- LED 3 gelb -> LED 2 geht an und gleich wieder aus. - -runde: - for (int i = 4; i >= 0; i--) { - mbot.led(i).setColor("rot"); - mbot.pause(200); - mbot.led(i).setColor("aus"); - } -frage: Bei welcher LED beginnt dieses Lauflicht? -- LED 0 -- [x] LED 4 -- LED 5 -> `i` beginnt bei 4 und zählt rückwärts. Das Licht läuft andersherum. - -runde: -frage: Ludwig schreibt `mbot.led(5).setColor("rot");`. Welche LED leuchtet? -- die fünfte -- die sechste -- [x] keine, eine LED 5 gibt es nicht -> Die fünf LEDs haben die Nummern 0 bis 4. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.led(1).setColor("rot"); - mbot.led(2).setColor("gelb"); - mbot.led(4).setColor("grün"); -frage: Soll LED 0 rot, LED 2 gelb und LED 4 grün schalten. -fehler: 1 -besser: mbot.led(0).setColor("rot"); -> Die erste LED hat die Nummer 0. - -runde: - for (int i = 1; i <= 5; i++) { - mbot.led(i).setColor("blau"); - } -frage: Soll alle fünf LEDs blau schalten. -fehler: 1 -besser: for (int i = 0; i < 5; i++) { -> Die LEDs haben die Nummern 0 bis 4. Eine LED 5 gibt es nicht. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blau"); - mbot.pause(200); - mbot.led(0).setColor("aus"); - } -frage: Lauflicht: Jede LED geht kurz blau an und wieder aus. -fehler: 4 -besser: mbot.led(i).setColor("aus"); -> Ausgehen soll dieselbe LED `i`, nicht immer LED 0. - -runde: - mbot.led(3).setColor(rot); -frage: Soll LED 3 rot schalten. -fehler: 1 -besser: mbot.led(3).setColor("rot"); -> Der Farbname ist ein Text und braucht Anführungszeichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die LEDs über ihre Nummern 0 bis 4 ansprechen. → Jede LED hat eine Nummer -- Ich kann einzelne LEDs in verschiedenen Farben leuchten lassen. → Drei Lichter im Struktogramm -- Ich kann mit einer Schleife ein Lauflicht bauen. → Jetzt in Java: Lauflicht -::: - -## Wissenstest - -::: quiz -? Welche Nummer hat die erste LED? -→ Jede LED hat eine Nummer -- [x] 0 -- [ ] 1 -- [ ] 5 -> Wie bei Arrays zählt man ab 0. Die fünf LEDs haben die Nummern 0 bis 4. -? Was macht `mbot.led(4).setColor("aus")`? -→ Drei Lichter im Struktogramm -- [ ] Alle LEDs gehen aus. -- [x] Nur LED 4 geht aus. -- [ ] Vier LEDs gehen aus. -::: diff --git a/web/lessons/de/visubot/16-lautsprecher.md b/web/lessons/de/visubot/16-lautsprecher.md deleted file mode 100644 index 2e3a7716..00000000 --- a/web/lessons/de/visubot/16-lautsprecher.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -titel: Der Lautsprecher -bereich: Licht, Ton und Bildschirm -kurz: Töne, eine Tonleiter und eine Sirene. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was die beiden Zahlen bei `playTone` bedeuten -- eine Tonfolge bauen -- in Java mit der Zählvariable rechnen -- den mBot2 ein Gefühl zeigen lassen -::: - -## Wie hoch, wie lang? - -::: mbot2 -fokus: cyberpi -ansicht: oben -::: - -Im [CyberPi]{teil=cyberpi} steckt ein kleiner Lautsprecher. `mbot.playTone(440, 300)` spielt einen Ton: - -- Die erste Zahl ist die **Tonhöhe** in Hertz (Schwingungen pro Sekunde). Je größer, desto höher. -- Die zweite Zahl ist die **Länge** in Millisekunden. - -| Ton | c | d | e | f | g | a | h | c' | -|---|---|---|---|---|---|---|---|---| -| Hertz | 262 | 294 | 330 | 349 | 392 | 440 | 494 | 523 | - -> In der Simulation hörst du die Töne über die Lautsprecher deines Computers. Dreh ruhig etwas leiser. - -## Tonleiter im Struktogramm - -::: übung vb-tonleiter -titel: Tonleiter -auftrag: Der mBot2 spielt die Töne c, d, e, f, g nacheinander, jeden 300 Millisekunden lang. Die Hertz-Zahlen stehen in der Tabelle. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -tipp: Zieh fünfmal **Ton spielen** ins Struktogramm. -tipp: Die Kästen heißen `mbot.playTone(262, 300)`, `mbot.playTone(294, 300)` und so weiter. -lösung: vb-tonleiter-loesung.visustruct -::: - -::: angeberwissen -Der Ton a mit 440 Hertz heißt **Kammerton**. Nach ihm stimmen Orchester ihre Instrumente. Eine Oktave höher hat genau doppelt so viele Schwingungen: 880 Hertz. -::: - -## Jetzt in Java: immer höher - -In einer For-Schleife kannst du mit der Zählvariable rechnen. So wird jeder Ton höher als der letzte: - -```java -mbot.playTone(i * 200, 200); -``` - -::: übung vb-java-hoeher -titel: Immer höher -code: java -auftrag: Schreibe in Java eine For-Schleife, in der `i` von 1 bis 5 zählt. In jedem Durchgang spielt der mBot2 einen Ton mit `i * 200` Hertz, 200 Millisekunden lang. -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -braucht: forSchleife -tipp: `for (int i = 1; i <= 5; i++) { … }` -tipp: In der Schleife steht nur `mbot.playTone(i * 200, 200);`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Gefühle zeigen - -Der mBot2 kann auch fröhlich, traurig oder müde sein. Mit **Gefühl zeigen** (`mbot.emotion("smile")`) spielt er eine kleine Vorführung: Die LEDs leuchten, er macht ein Geräusch und wackelt ein bisschen. - -| Text | Gefühl | -|---|---| -| `mbot.emotion("smile")` | lacht | -| `mbot.emotion("hello")` | grüßt | -| `mbot.emotion("sad")` | ist traurig | -| `mbot.emotion("angry")` | ist wütend | -| `mbot.emotion("dizzy")` | ist schwindelig | -| `mbot.emotion("zzz")` | schläft | - -> Probier es mit dem echten mBot2 aus. Im Simulator siehst du davon nichts, er zeigt nur den Befehl an. Weitere Gefühle stehen unter **Nachschlagen**. - -::: übung vb-fehler-16-lautsprecher -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Immer höher“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-16-lautsprecher-start.visustruct -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es `mbot.playTone(i * 200, 200)`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Spiel: Von tief nach hoch - -::: spiel reihenfolge -titel: Spiel: Von tief nach hoch -intro: Tippe die Antworten in der richtigen Reihenfolge an. Ein falscher Tipp beendet die Runde. Fünf Runden. - -runde: -frage: Tippe die Töne von tief nach hoch an. -- `mbot.playTone(262, 300)` -- `mbot.playTone(330, 300)` -- `mbot.playTone(440, 300)` -- `mbot.playTone(880, 300)` -> Je mehr Hertz, desto höher der Ton. - -runde: -frage: Tippe die Töne von kurz nach lang an. -- `mbot.playTone(440, 100)` -- `mbot.playTone(440, 300)` -- `mbot.playTone(440, 1000)` -- `mbot.playTone(440, 2000)` -> Die zweite Zahl ist die Länge in Millisekunden. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.playTone(i * 200, 200); - } -frage: Tippe die Töne in der Reihenfolge an, in der sie klingen. -- 600 Hertz -- 400 Hertz -- 200 Hertz -> `i` zählt rückwärts: 3, 2, 1. Die Töne werden immer tiefer. - -runde: -frage: Tippe die Töne von tief nach hoch an. -- c mit 262 Hertz -- der Kammerton a -- a eine Oktave höher -> Der Kammerton a hat 440 Hertz, eine Oktave höher sind es doppelt so viele: 880 Hertz. - -runde: -frage: Tippe die Zeiten von kurz nach lang an. -- 300 Millisekunden -- 1 Sekunde -- 1500 Millisekunden -- 2 Sekunden -> 1 Sekunde sind 1000 Millisekunden, 2 Sekunden sind 2000. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was die beiden Zahlen bei `playTone` bedeuten. → Wie hoch, wie lang? -- Ich kann eine Tonfolge bauen. → Tonleiter im Struktogramm -- Ich kann in Java mit der Zählvariable rechnen. → Jetzt in Java: immer höher -- Ich kann den mBot2 ein Gefühl zeigen lassen. → Gefühle zeigen -::: - -## Wissenstest - -::: quiz -? Welcher Ton ist höher? -→ Wie hoch, wie lang? -- [x] `mbot.playTone(880, 300)` -- [ ] `mbot.playTone(440, 300)` -> Mehr Hertz heißt höher. -? Wie lange klingt `mbot.playTone(440, 1000)`? -→ Wie hoch, wie lang? -- [ ] 440 Sekunden -- [x] 1 Sekunde -- [ ] 1000 Sekunden -> 1000 Millisekunden sind eine Sekunde. -::: diff --git a/web/lessons/de/visubot/17-arrays-melodie.md b/web/lessons/de/visubot/17-arrays-melodie.md deleted file mode 100644 index 1c789deb..00000000 --- a/web/lessons/de/visubot/17-arrays-melodie.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -titel: Arrays und Melodien -bereich: Licht, Ton und Bildschirm -kurz: Viele Werte in einem Array speichern und mit einer Schleife abarbeiten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Array anlegen und mit einer Schleife abspielen -- ein Array mit Texten in Java benutzen -- Messwerte in einem Array sammeln und den Mittelwert ausrechnen -::: - -## Eine Melodie als Array - -Eine Melodie ist eine Reihe von Tönen. Statt fünf Variablen `ton1`, `ton2`, … nimmst du ein **Array**. Ein Array ist eine Reihe von Fächern unter **einem** Namen: - -```java -int[] toene = {262, 294, 330, 349, 392}; -``` - -- `int[]` heißt: ein Array aus ganzen Zahlen. -- Die Fächer sind von **0** an nummeriert. `toene[0]` ist 262, `toene[4]` ist 392. -- `toene.length` sagt, wie viele Fächer es gibt, hier 5. - -Eine For-Schleife mit `i` von 0 bis `toene.length - 1` kommt so an jedes Fach. - -::: übung vb-melodie -titel: Melodie -auftrag: Lege ein Array `toene` mit den Tonhöhen 262, 294, 330, 349 und 392 an. Eine For-Schleife spielt sie nacheinander, jeden 300 Millisekunden lang. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -braucht: forSchleife -tipp: `int[] toene = {262, 294, 330, 349, 392}` legt das Array an. -tipp: Die Schleife läuft von `i = 0` bis `i < toene.length`. Darin steht `mbot.playTone(toene[i], 300)`. -lösung: vb-melodie-loesung.visustruct -::: - -> Willst du eine andere Melodie, änderst du nur das Array. Die Schleife bleibt gleich. - -## Jetzt in Java: Regenbogen - -Die fünf LEDs haben die Nummern 0 bis 4, genau wie die Fächer eines Arrays mit fünf Werten. Ein Array kann auch Texte enthalten: `String[] farben = {"rot", "gelb"};`. LED `i` bekommt dann die Farbe aus Fach `i`: `mbot.led(i).setColor(farben[i])`. - -::: übung vb-farben -titel: Regenbogen -code: java -auftrag: Schreibe in Java: Lege ein Array `farben` mit den Texten `"rot"`, `"gelb"`, `"grün"`, `"blau"` und `"lila"` an. Eine For-Schleife gibt jeder LED `i` die Farbe aus `farben[i]`. -befehle: ledColor 0 "rot", ledColor 1 "gelb", ledColor 2 "grün", ledColor 3 "blau", ledColor 4 "lila" -braucht: forSchleife -tipp: `String[] farben = {"rot", "gelb", "grün", "blau", "lila"};` legt das Array an. -tipp: `for (int i = 0; i < farben.length; i++) { mbot.led(i).setColor(farben[i]); }` -lösung: vb-farben-loesung.visustruct -::: - -## Jetzt in Java: Messreihe - -Sensoren messen nie ganz genau. Halte die Hand vor den Ultraschallsensor: Die Zahl springt immer ein bisschen hin und her. Profis messen deshalb mehrmals und rechnen den **Mittelwert** aus. - -Die Messwerte kommen in ein Array. `new int[5]` legt ein Array mit fünf leeren Fächern an: - -```java -int[] werte = new int[5]; -for (int i = 0; i < werte.length; i++) { - werte[i] = mbot.getDistance(); - mbot.pause(100); -} -``` - -Danach zählst du alle Werte zusammen. Dafür brauchst du eine Variable, die mit 0 anfängt und in jedem Durchgang wächst: - -```java -int summe = 0; -for (int i = 0; i < werte.length; i++) { - summe = summe + werte[i]; -} -``` - -Der Mittelwert ist die Summe geteilt durch die Anzahl. Schreib `5.0` statt `5`, dann rechnet Java mit Kommazahlen: `double mittelwert = summe / 5.0;` - -::: übung vb-messreihe -titel: Messreihe -code: java -auftrag: Miss den Abstand fünfmal in ein Array `int[] werte = new int[5]`, mit 100 ms Pause dazwischen. Zähle danach alle Werte zusammen, rechne den Mittelwert aus und zeige zum Beispiel **Mittelwert: 40.0** auf dem Bildschirm. -messwerte: getDistance=40 -> screen "Mittelwert: 40.0" -messwerte: getDistance=25 -> screen "Mittelwert: 25.0" -braucht: forSchleife -tipp: Zwei For-Schleifen: Die erste füllt das Array mit `werte[i] = mbot.getDistance();`, die zweite rechnet `summe = summe + werte[i];`. -tipp: Danach `double mittelwert = summe / 5.0;` und `mbot.showText("Mittelwert: " + mittelwert);`. -lösung: vb-messreihe-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Arrays“](?visustruct&lektion=15-arrays). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-17-arrays-melodie -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Messreihe“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-17-arrays-melodie-start.visustruct -messwerte: getDistance=40 -> screen "Mittelwert: 40.0" -messwerte: getDistance=25 -> screen "Mittelwert: 25.0" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `summe = summe + werte[i]`. -lösung: vb-messreihe-loesung.visustruct -::: - -## Spiel: Welches Fach? - -::: spiel frage -titel: Spiel: Welches Fach? -intro: Fächer zählen ab 0. Was steht wo im Array? Sechs Runden. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist `toene[2]`? -- 294 -- [x] 330 -- 349 -> Fach 0 ist 262, Fach 1 ist 294, Fach 2 ist 330. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist `toene.length`? -- 4 -- [x] 5 -- 392 -> `length` zählt die Fächer. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist `toene[toene.length - 1]`? -- 349 -- [x] 392 -- Fehler -> `toene.length - 1` ist 4, das letzte Fach. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was passiert bei `mbot.playTone(toene[5], 300)`? -- Er spielt 392 Hertz. -- Er spielt gar nichts und macht weiter. -- [x] Fehler: Fach 5 gibt es nicht. -> Die Fächer haben die Nummern 0 bis 4. Java meldet `ArrayIndexOutOfBoundsException`. - -runde: - String[] farben = {"rot", "gelb", "grün", "blau", "lila"}; - for (int i = 0; i < farben.length; i++) { - mbot.led(i).setColor(farben[i]); - } -frage: Welche Farbe hat LED 3? -- grün -- [x] blau -- lila -> LED 3 bekommt `farben[3]`, das vierte Wort. - -runde: - int[] werte = {20, 22, 18, 20, 20}; - int summe = 0; - for (int i = 0; i < werte.length; i++) { - summe = summe + werte[i]; - } - double mittelwert = summe / 5.0; -frage: Welchen Wert hat `mittelwert`? -- 18.0 -- [x] 20.0 -- 100.0 -> Die Summe ist 100, und 100 / 5.0 ist 20.0. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - int[] toene = {262, 294, 330, 349, 392}; - for (int i = 0; i <= toene.length; i++) { - mbot.playTone(toene[i], 300); - } -frage: Soll alle fünf Töne spielen. -fehler: 2 -besser: for (int i = 0; i < toene.length; i++) { -> Mit `<=` will die Schleife auch `toene[5]` spielen. Das Fach gibt es nicht. - -runde: - int[] toene = {262, 294, 330, 349, 392}; - for (int i = 0; i < toene.length; i++) { - mbot.playTone(toene, 300); - } -frage: Soll alle fünf Töne spielen. -fehler: 3 -besser: mbot.playTone(toene[i], 300); -> Gespielt wird ein Fach, `toene[i]`, nicht das ganze Array. - -runde: - String[] farben = {"rot", "gelb", "grün", "blau", "lila"}; - for (int i = 0; i < farben.length(); i++) { - mbot.led(i).setColor(farben[i]); - } -frage: Soll jeder LED eine Farbe aus dem Array geben. -fehler: 2 -besser: for (int i = 0; i < farben.length; i++) { -> Bei Arrays steht `length` ohne Klammern. Klammern braucht nur `length()` bei Strings. - -runde: - int[] werte = new int[5]; - int summe = 0; - for (int i = 0; i < werte.length; i++) { - summe = werte[i]; - } -frage: Soll alle Messwerte zusammenzählen. -fehler: 4 -besser: summe = summe + werte[i]; -> So steht am Ende nur der letzte Wert in `summe`. Der neue Wert muss dazukommen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Array anlegen und mit einer Schleife abspielen. → Eine Melodie als Array -- Ich kann ein Array mit Texten in Java benutzen. → Jetzt in Java: Regenbogen -- Ich kann Messwerte in einem Array sammeln und den Mittelwert ausrechnen. → Jetzt in Java: Messreihe -::: - -## Wissenstest - -::: quiz -? Was ist `toene[1]`, wenn `int[] toene = {262, 294, 330};`? -→ Eine Melodie als Array -- [ ] 262 -- [x] 294 -- [ ] 1 -> Die Fächer zählen ab 0. Fach 1 ist also das zweite. -? Wie viele Fächer hat `{262, 294, 330}`, also was ist `toene.length`? -→ Eine Melodie als Array -- [ ] 2 -- [x] 3 -- [ ] 330 -> `length` zählt die Fächer, hier drei. Das letzte Fach hat deshalb die Nummer 2. -::: diff --git a/web/lessons/de/visubot/18-bildschirm.md b/web/lessons/de/visubot/18-bildschirm.md deleted file mode 100644 index 35f92e65..00000000 --- a/web/lessons/de/visubot/18-bildschirm.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -titel: Der Bildschirm -bereich: Licht, Ton und Bildschirm -kurz: Text, Zahlen und Messwerte auf dem CyberPi anzeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Text auf dem Bildschirm anzeigen -- mehrere Texte nacheinander zeigen -- Zahlen und Messwerte anzeigen -- einen Countdown in Java schreiben -::: - -## Text anzeigen - -::: mbot2 -fokus: screen -ansicht: hinten -::: - -Der farbige [Bildschirm]{teil=screen} des CyberPi ist auch ein Aktor. Mit **Text anzeigen** schreibst du etwas darauf: - -```java -mbot.showText("Hallo!") -``` - -Der Text steht in Anführungszeichen, wie bei `System.out.println`. Ein neuer Text ersetzt den alten. - -> In der Simulation erscheint der Text auf dem Bildschirm des 3D-Modells und unter der Welt hinter 📺. - -## Begrüßung im Struktogramm - -::: übung vb-begruessung -titel: Begrüßung -auftrag: Der mBot2 zeigt zuerst „Hallo!“, wartet 1000 Millisekunden und zeigt dann „Ich bin VisuBot“. -befehle: showText "Hallo!", pause 1000, showText "Ich bin VisuBot" -tipp: Zieh zweimal **Text anzeigen** und einmal **kurz warten** ins Struktogramm. -tipp: `mbot.showText("Hallo!")`, `mbot.pause(1000)`, `mbot.showText("Ich bin VisuBot")`. -lösung: vb-begruessung-loesung.visustruct -::: - -## Zahlen und Messwerte - -Statt Text kannst du auch Zahlen und Variablen anzeigen, und mit `+` setzt du alles zusammen: - -```java -mbot.showText(i) -mbot.showText("Abstand: " + mbot.getDistance()) -``` - -So zeigt der mBot2 dir, was seine Sensoren gerade messen. - -## Jetzt in Java: Countdown - -::: übung vb-java-countdown -titel: Countdown -code: java -auftrag: Schreibe in Java einen Countdown: Eine For-Schleife zählt `i` von 3 rückwärts bis 1. In jedem Durchgang zeigt der mBot2 `i` an und wartet 1000 Millisekunden. Danach zeigt er „Los!“. -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Los!" -braucht: forSchleife -tipp: Rückwärts zählen: `for (int i = 3; i >= 1; i--) { … }` -tipp: In der Schleife: `mbot.showText(i);` und `mbot.pause(1000);`. Nach der Schleife `mbot.showText("Los!");`. -lösung: vb-java-countdown-loesung.visustruct -::: - -::: übung vb-fehler-18-bildschirm -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Countdown“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-18-bildschirm-start.visustruct -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Los!" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.showText(i)`. -lösung: vb-java-countdown-loesung.visustruct -::: - -## Spiel: Was steht auf dem Bildschirm? - -::: spiel frage -titel: Spiel: Was steht auf dem Bildschirm? -intro: Lies den Code und sag vorher, was der Bildschirm am Ende zeigt. Sechs Runden. - -runde: - mbot.showText("Hallo!"); - mbot.pause(1000); - mbot.showText("Ich bin VisuBot"); -frage: Was steht am Ende auf dem Bildschirm? -- Hallo! Ich bin VisuBot -- [x] Ich bin VisuBot -- Hallo! -> Ein neuer Text ersetzt den alten. - -runde: - mbot.showText("Abstand: " + 25); -frage: Was steht auf dem Bildschirm? -- [x] Abstand: 25 -- "Abstand: " + 25 -- Abstand: -> Das `+` hängt die Zahl an den Text. - -runde: - int i = 3; - mbot.showText("i"); -frage: Was steht auf dem Bildschirm? -- 3 -- [x] i -- "i" -> In Anführungszeichen ist `i` ein Text, keine Variable. Die Anführungszeichen selbst erscheinen nicht. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.showText(i); - mbot.pause(1000); - } - mbot.showText("Start!"); -frage: Welche Zahl steht direkt vor „Start!“ da? -- 0 -- [x] 1 -- 3 -> Der Countdown zeigt 3, 2, 1. Bei 0 ist `i >= 1` falsch. - -runde: - mbot.showText("Summe: " + 2 + 3); -frage: Was steht auf dem Bildschirm? -- Summe: 5 -- [x] Summe: 23 -- Summe: 2 + 3 -> Java rechnet von links nach rechts: Erst kommt die 2 an den Text, dann die 3. - -runde: - mbot.showText("Summe: " + (2 + 3)); -frage: Was steht auf dem Bildschirm? -- [x] Summe: 5 -- Summe: 23 -- Summe: (2 + 3) -> Die Klammern rechnet Java zuerst aus. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.showText("Hallo!"); - mbot.pause(1); - mbot.showText("Ich bin VisuBot"); -frage: Soll „Hallo!“ zeigen, eine Sekunde warten und dann „Ich bin VisuBot“ zeigen. -fehler: 2 -besser: mbot.pause(1000); -> `pause` zählt in Millisekunden. Eine Sekunde sind 1000. - -runde: - for (int i = 3; i >= 1; i++) { - mbot.showText(i); - mbot.pause(1000); - } -frage: Countdown: soll 3, 2, 1 zeigen. -fehler: 1 -besser: for (int i = 3; i >= 1; i--) { -> Rückwärts zählen heißt `i--`. Mit `i++` hört die Schleife nie auf. - -runde: - mbot.showText(Hallo!); -frage: Soll „Hallo!“ zeigen. -fehler: 1 -besser: mbot.showText("Hallo!"); -> Text steht in Anführungszeichen. - -runde: - mbot.showText("Abstand: " - mbot.getDistance()); -frage: Soll zum Beispiel „Abstand: 25“ zeigen. -fehler: 1 -besser: mbot.showText("Abstand: " + mbot.getDistance()); -> Text und Zahl hängst du mit `+` aneinander. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Text auf dem Bildschirm anzeigen. → Text anzeigen -- Ich kann mehrere Texte nacheinander zeigen. → Begrüßung im Struktogramm -- Ich kann Zahlen und Messwerte anzeigen. → Zahlen und Messwerte -- Ich kann einen Countdown in Java schreiben. → Jetzt in Java: Countdown -::: - -## Wissenstest - -::: quiz -? Was zeigt `mbot.showText("Abstand: " + 25)`? -→ Zahlen und Messwerte -- [x] Abstand: 25 -- [ ] "Abstand: " + 25 -- [ ] 25 -> Das `+` hängt die Zahl an den Text. -? Was passiert mit dem alten Text, wenn du einen neuen anzeigst? -→ Text anzeigen -- [x] Er wird ersetzt. -- [ ] Der neue kommt darunter. -::: diff --git a/web/lessons/de/visubot/19-zeichenketten.md b/web/lessons/de/visubot/19-zeichenketten.md deleted file mode 100644 index 191afef3..00000000 --- a/web/lessons/de/visubot/19-zeichenketten.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -titel: Zeichenketten -bereich: Licht, Ton und Bildschirm -kurz: Mit Texten rechnen, Buchstaben herausholen und auf dem Bildschirm zeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit `length()` und `substring(…)` arbeiten -- Texte und Zahlen mit `+` verbinden -- mit `charAt(…)` einzelne Zeichen holen -::: - -## Buchstabe für Buchstabe - -Ein Text heißt in Java **Zeichenkette** oder `String`. Er besteht aus einzelnen Zeichen, die wie bei einem Array ab **0** nummeriert sind: - -| Zeichen | V | i | s | u | B | o | t | -|---|---|---|---|---|---|---|---| -| Nummer | 0 | 1 | 2 | 3 | 4 | 5 | 6 | - -- `wort.length()` liefert die Anzahl der Zeichen, hier 7. -- `wort.substring(0, 3)` liefert die ersten 3 Zeichen: `"Vis"`. - -Der mBot2 soll seinen Namen auf dem [Bildschirm]{teil=screen} aufbauen: erst **V**, dann **Vi**, dann **Vis** und so weiter bis **VisuBot**. - -::: übung vb-buchstaben -titel: Name aufbauen -auftrag: Lege `String wort = "VisuBot"` an. Eine For-Schleife zählt `i` von 1 bis `wort.length()`. Darin zeigt der mBot2 `wort.substring(0, i)` und wartet 300 Millisekunden. -befehle: showText "V", pause 300, showText "Vi", pause 300, showText "Vis", pause 300, showText "Visu", pause 300, showText "VisuB", pause 300, showText "VisuBo", pause 300, showText "VisuBot", pause 300 -braucht: forSchleife -tipp: Die Schleife beginnt bei `int i = 1` und läuft, solange `i <= wort.length()`. -tipp: In der Schleife: `mbot.showText(wort.substring(0, i))` und `mbot.pause(300)`. -lösung: vb-buchstaben-loesung.visustruct -::: - -## Jetzt in Java: Text und Länge - -Mit `+` hängst du Texte und Zahlen aneinander. So zeigt der mBot2 einen Text und dahinter seine Länge. - -::: übung vb-java-textlaenge -titel: Wie lang ist der Text? -code: java -auftrag: Schreibe in Java: Lege `String text = "Hallo Klasse"` an. Der mBot2 zeigt den Text und in Klammern seine Länge, also **Hallo Klasse (12)**. -befehle: showText "Hallo Klasse (12)" -tipp: Die Länge liefert `text.length()`. Auch das Leerzeichen zählt mit. -tipp: `mbot.showText(text + " (" + text.length() + ")");` -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Jetzt in Java: Laufschrift - -`wort.charAt(i)` liefert das Zeichen mit der Nummer `i`. Zeigst du die Zeichen nacheinander mit einer Pause, läuft das Wort Buchstabe für Buchstabe über den Bildschirm: - -```java -String wort = "Ludwig"; -for (int i = 0; i < wort.length(); i++) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); -} -``` - -| Durchgang | `i` | Bildschirm | -|---|---|---| -| 1 | 0 | L | -| 2 | 1 | u | -| 3 | 2 | d | - -::: übung vb-laufschrift -titel: Ludwig rückwärts -code: java -auftrag: Lege `String wort = "Ludwig"` an. Zeige das Wort **rückwärts** Buchstabe für Buchstabe auf dem Bildschirm, also g, i, w, d, u, L, mit 500 ms Pause nach jedem Buchstaben. -befehle: showText "g", pause 500, showText "i", pause 500, showText "w", pause 500, showText "d", pause 500, showText "u", pause 500, showText "L", pause 500 -braucht: forSchleife -tipp: Das letzte Zeichen hat die Nummer `wort.length() - 1`. Die Schleife zählt rückwärts: `for (int i = wort.length() - 1; i >= 0; i--)`. -tipp: In der Schleife: `mbot.showText(wort.charAt(i));` und `mbot.pause(500);`. -lösung: vb-laufschrift-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Zeichenketten“](?visustruct&lektion=17-zeichenketten). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-19-zeichenketten -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wie lang ist der Text?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-19-zeichenketten-start.visustruct -befehle: showText "Hallo Klasse (12)" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.showText(text +`. -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 zeigen soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Fünf Runden, je 30 Sekunden. -zeit: 30 - -runde: - String wort = "VisuBot"; - for (int i = 1; i <= wort.length(); i++) { - mbot.showText(wort.substring(1, i)); - mbot.pause(300); - } -frage: Soll V, Vi, Vis und so weiter bis VisuBot zeigen. -fehler: 3 -besser: mbot.showText(wort.substring(0, i)); -> Das erste Zeichen hat die Nummer 0. Mit `substring(1, i)` fehlt das V. - -runde: - String text = "Hallo Klasse"; - mbot.showText(text + " (" + text.length + ")"); -frage: Soll **Hallo Klasse (12)** zeigen. -fehler: 2 -besser: mbot.showText(text + " (" + text.length() + ")"); -> Bei einem String ist `length()` eine Methode und braucht Klammern. Nur bei Arrays steht `length` ohne Klammern. - -runde: - String wort = "Ludwig"; - for (int i = 0; i <= wort.length(); i++) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); - } -frage: Soll L, u, d, w, i, g zeigen. -fehler: 2 -besser: for (int i = 0; i < wort.length(); i++) { -> Das letzte Zeichen hat die Nummer 5. Bei `i = 6` gibt es kein Zeichen mehr. - -runde: - String wort = "Ludwig"; - for (int i = wort.length(); i >= 0; i--) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); - } -frage: Soll das Wort rückwärts zeigen: g, i, w, d, u, L. -fehler: 2 -besser: for (int i = wort.length() - 1; i >= 0; i--) { -> Rückwärts beginnt man beim letzten Zeichen, und das hat die Nummer `wort.length() - 1`. - -runde: - String name = 'Ludwig'; - mbot.showText("Hallo " + name); -frage: Soll **Hallo Ludwig** zeigen. -fehler: 1 -besser: String name = "Ludwig"; -> Texte stehen in doppelten Anführungszeichen. Einfache gibt es nur für ein einzelnes Zeichen (`char`). -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 6 1 LAUFSCHRIFT: ein Wort Buchstabe für Buchstabe über den Bildschirm -- ↓ 4 1 BILDSCHIRM: zeigt mit mbot.showText einen Text -- ↓ 6 7 HERTZ: Einheit der Tonhöhe: Schwingungen pro Sekunde -- ↓ 4 11 AKTOR: Bauteil, das etwas tut, z. B. leuchten -- ↓ 4 3 LAUTSPRECHER: spielt mit mbot.playTone einen Ton -- ↓ 4 9 ZEICHENKETTE: anderes Wort für String -- ↓ 1 5 MILLISEKUNDE: ein Tausendstel einer Sekunde -- → 15 1 ARRAY: Reihe von Fächern unter einem Namen -lösung: 6 2, 8 5, 9 7, 7 11, 10 3 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit `length()` und `substring(…)` arbeiten. → Buchstabe für Buchstabe -- Ich kann Texte und Zahlen mit `+` verbinden. → Jetzt in Java: Text und Länge -- Ich kann mit `charAt(…)` einzelne Zeichen holen. → Jetzt in Java: Laufschrift -::: - -## Wissenstest - -::: quiz -? Was liefert `"VisuBot".substring(0, 4)`? -→ Buchstabe für Buchstabe -- [ ] `"VisuB"` -- [x] `"Visu"` -- [ ] `"uBot"` -> `substring(0, 4)` nimmt die Zeichen 0, 1, 2 und 3, also vier Stück. -? Was liefert `"Ludwig".charAt(0)`? -→ Jetzt in Java: Laufschrift -- [x] `L` -- [ ] `u` -- [ ] `g` -> Die Zeichen werden ab 0 gezählt. Zeichen 0 ist das erste. -::: diff --git a/web/lessons/de/visubot/20-motoren.md b/web/lessons/de/visubot/20-motoren.md deleted file mode 100644 index 2f090e00..00000000 --- a/web/lessons/de/visubot/20-motoren.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -titel: Motoren und Geschwindigkeit -bereich: Motoren und Methoden -kurz: Gas geben, lenken und einer Linie folgen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit Räder einzeln Gas geben und anhalten -- fahren, bis ein Sensor etwas meldet -- einen Linienfolger in Java schreiben -::: - -## Zwei Motoren, zwei Räder - -::: mbot2 -fokus: motors, wheels -ansicht: unten -::: - -Der mBot2 hat zwei [Motoren]{teil=motors}, jeder treibt ein [Rad]{teil=wheels} an. Bisher hast du **fahre vor** und **drehe rechts** benutzt. Diese Befehle fahren eine feste Strecke und warten, bis der mBot2 angekommen ist. - -Mit **Räder einzeln** (`mbot.drive(links, rechts)`) gibst du stattdessen Gas: Du sagst, wie stark sich jedes Rad dreht, in Prozent von −100 bis 100. Der mBot2 fährt dann **weiter**, bis du ihm etwas anderes sagst. Mit **anhalten** (`mbot.stop()`) bleibt er stehen. - -| Befehl | Was passiert? | -|---|---| -| `mbot.drive(40, 40)` | beide Räder gleich schnell: geradeaus | -| `mbot.drive(-40, -40)` | rückwärts | -| `mbot.drive(10, 40)` | rechts schneller: Kurve nach links | -| `mbot.drive(40, -40)` | Räder gegeneinander: dreht auf der Stelle nach rechts | -| `mbot.stop()` | anhalten | - -> Im Simulator fährt `mbot.drive(100, 100)` 20 cm pro Sekunde. Probier es in der [Simulation](#zeige-simulation) aus. - -## Gas geben bis zur Wand im Struktogramm - -In Lektion 5 ist der mBot2 in 10-cm-Schritten bis zur Wand gefahren. Mit **Räder einzeln** geht das ohne Ruckeln: einmal Gas geben, dann warten, bis die Wand nah ist, dann anhalten. - -::: übung vb-gas-bis-wand -titel: Gas geben bis zur Wand -auftrag: Der mBot2 gibt mit 50 % Gas. Solange die Wand mehr als 15 cm entfernt ist, wartet er kurz. Danach hält er an. Es gibt drei Welten. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Ganz oben steht `mbot.drive(50, 50)`. Darunter eine While-Schleife mit der Bedingung `mbot.getDistance() > 15`. -tipp: In der Schleife steht nur **kurz warten**. Der mBot2 fährt dabei weiter! Nach der Schleife steht **anhalten**. -lösung: vb-gas-bis-wand-loesung.visustruct -::: - -## Der Linienfolger - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Ein Klassiker: Der mBot2 folgt einer schwarzen Linie auf dem Boden. Dafür schaut er auf die beiden mittleren Fühler des [Liniensensors]{teil=lineSensor}: - -| Sensor | Text | -|---|---| -| linker mittlerer Fühler sieht Schwarz | `mbot.isLineLeft()` | -| rechter mittlerer Fühler sieht Schwarz | `mbot.isLineRight()` | - -Die Idee: Sehen **beide** die Linie, fährt er geradeaus. Sieht nur der **linke** sie, ist die Linie nach links gewandert: Er lenkt nach links, also dreht sich das rechte Rad schneller. Sieht nur der **rechte** sie, lenkt er nach rechts. - -## Jetzt in Java: Linienfolger - -::: übung vb-java-linienfolger -titel: Linienfolger -code: java -auftrag: Schreibe in Java einen Linienfolger. Solange die Wand mehr als 15 cm entfernt ist, prüft der mBot2 seine beiden Fühler und lenkt. Danach hält er an. In der ersten Welt biegt die Linie nach rechts ab, in der zweiten nach links. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: `while (mbot.getDistance() > 15) { … }` und danach `mbot.stop();`. -tipp: In der Schleife: `if (mbot.isLineLeft() && mbot.isLineRight()) { mbot.drive(40, 40); } else if (mbot.isLineLeft()) { mbot.drive(10, 40); } else if (mbot.isLineRight()) { mbot.drive(40, 10); }` -lösung: vb-java-linienfolger-loesung.visustruct -::: - -> Am echten mBot2 klebst du die Linie mit schwarzem Isolierband auf einen hellen Boden. Fahr anfangs langsam, sonst schießt er in der Kurve über die Linie hinaus. - -::: übung vb-fehler-20-motoren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Linienfolger“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-20-motoren-start.visustruct -welt: linie-1, linie-2 -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es `mbot.stop()`. -lösung: vb-java-linienfolger-loesung.visustruct -::: - -## Spiel: Wohin fährt er? - -::: spiel frage -titel: Spiel: Wohin fährt er? -intro: Links und rechts Gas in Prozent: Wohin fährt der mBot2? Sieben Runden. -antworten: geradeaus | rückwärts | Kurve nach links | Kurve nach rechts | dreht sich auf der Stelle - -runde: - mbot.drive(40, 40); -frage: Wohin fährt er? -richtig: geradeaus -> Beide Räder drehen sich gleich schnell. - -runde: - mbot.drive(-40, -40); -frage: Wohin fährt er? -richtig: rückwärts -> Minus heißt rückwärts, und beide Räder sind gleich. - -runde: - mbot.drive(10, 40); -frage: Wohin fährt er? -richtig: Kurve nach links -> Das rechte Rad ist schneller, also lenkt er nach links. - -runde: - mbot.drive(40, 10); -frage: Wohin fährt er? -richtig: Kurve nach rechts -> Das linke Rad ist schneller, also lenkt er nach rechts. - -runde: - mbot.drive(40, -40); -frage: Wohin fährt er? -richtig: dreht sich auf der Stelle -> Ein Rad vor, eins zurück: Er dreht sich nach rechts, ohne vom Fleck zu kommen. - -runde: - mbot.drive(-40, 40); -frage: Wohin fährt er? -richtig: dreht sich auf der Stelle -> Diesmal dreht er sich nach links. - -runde: - if (mbot.isLineLeft() && !mbot.isLineRight()) { - ... - } -frage: Linienfolger: Nur der linke Fühler sieht die Linie. Was gehört in die Lücke? -- `mbot.drive(40, 10);` -- [x] `mbot.drive(10, 40);` -- `mbot.stop();` -> Die Linie ist nach links gewandert. Er lenkt nach links, also dreht sich das rechte Rad schneller. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit Räder einzeln Gas geben und anhalten. → Zwei Motoren, zwei Räder -- Ich kann fahren, bis ein Sensor etwas meldet. → Gas geben bis zur Wand im Struktogramm -- Ich kann einen Linienfolger in Java schreiben. → Jetzt in Java: Linienfolger -::: - -## Wissenstest - -::: quiz -? Was macht `mbot.drive(30, 30)`? -→ Zwei Motoren, zwei Räder -- [ ] Er fährt 30 cm und hält an. -- [x] Er fährt geradeaus und hört nicht auf, bis ein anderer Fahrbefehl kommt. -- [ ] Er dreht sich um 30°. -> **Räder einzeln** gibt nur Gas. Anhalten musst du selbst. -? Der mBot2 soll nach rechts lenken. Welcher Befehl passt? -→ Zwei Motoren, zwei Räder -- [ ] `mbot.drive(10, 40)` -- [x] `mbot.drive(40, 10)` -- [ ] `mbot.drive(-40, -40)` -> Dreht sich das linke Rad schneller, fährt er eine Rechtskurve. -::: diff --git a/web/lessons/de/visubot/21-stoppuhr-akku.md b/web/lessons/de/visubot/21-stoppuhr-akku.md deleted file mode 100644 index 409fa0e6..00000000 --- a/web/lessons/de/visubot/21-stoppuhr-akku.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -titel: Stoppuhr und Akku -bereich: Motoren und Methoden -kurz: Zeit messen und den Akku im Blick behalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Stoppuhr auf 0 setzen und ablesen -- eine bestimmte Zeit lang fahren -- den Akkustand anzeigen -::: - -## Die Stoppuhr - -::: mbot2 -fokus: cyberpi -ansicht: oben -::: - -Im [CyberPi]{teil=cyberpi} läuft eine Stoppuhr. Sie zählt die Sekunden, seit das Programm läuft. Mit **Stoppuhr auf 0** (`mbot.resetTimer()`) fängt sie wieder bei 0 an. - -| Sensor oder Befehl | Text | Wert | -|---|---|---| -| Stoppuhr auf 0 | `mbot.resetTimer()` | | -| Stoppuhr | `mbot.getTimer()` | Sekunden mit Nachkommastelle, z. B. `1.5` | - -So fährst du eine bestimmte Zeit lang: Stoppuhr auf 0, Gas geben, warten, solange die Stoppuhr noch nicht weit genug ist, anhalten. - -## Zwei Sekunden fahren im Struktogramm - -::: übung vb-zwei-sekunden -titel: Zwei Sekunden -auftrag: Der mBot2 setzt die Stoppuhr auf 0 und gibt mit 50 % Gas. Solange die Stoppuhr unter 2 Sekunden ist, wartet er kurz. Danach hält er an. Mit 50 % schafft er 10 cm pro Sekunde, er muss also 20 cm weiter stehen bleiben. -welt: zeit-1 -braucht: whileSchleife -tipp: Oben stehen **Stoppuhr auf 0** und `mbot.drive(50, 50)`. Darunter eine While-Schleife mit `mbot.getTimer() < 2`. -tipp: In der Schleife steht **kurz warten**, nach der Schleife **anhalten**. -lösung: vb-zwei-sekunden-loesung.visustruct -::: - -## Der Akku - -::: mbot2 -fokus: shield -ansicht: oben -::: - -Unter dem durchsichtigen [Shield]{teil=shield} siehst du den Akku. Wie voll er ist, liefert `mbot.getBattery()` in Prozent: `100` ist ganz voll. Ist er fast leer, fahren die Motoren schwächer. Dann wird es Zeit zum Laden. - -::: angeberwissen -Auch dein Handy misst seinen Akku nicht direkt in Prozent. Es misst die Spannung: Ein voller Lithium-Akku hat etwas über 4 Volt, ein leerer gut 3. Daraus rechnet es die Prozente aus, genau wie der CyberPi. -::: - -## Jetzt in Java: Akku-Anzeige - -::: übung vb-java-akku -titel: Akku-Anzeige -code: java -auftrag: Schreibe in Java: Der mBot2 zeigt auf dem Bildschirm zum Beispiel **Akku: 80 %**. Ist der Akku unter 20 %, leuchten zusätzlich die LEDs rot. -messwerte: getBattery=80 -> none, ledOff, screen "Akku: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Akku: 15 %" -braucht: verzweigung -tipp: `mbot.showText("Akku: " + mbot.getBattery() + " %");` setzt den Text aus drei Teilen zusammen. -tipp: Danach: `if (mbot.getBattery() < 20) { mbot.setLed(255, 0, 0); }` -lösung: vb-java-akku-loesung.visustruct -::: - -::: übung vb-fehler-21-stoppuhr-akku -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Akku-Anzeige“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-21-stoppuhr-akku-start.visustruct -messwerte: getBattery=80 -> none, ledOff, screen "Akku: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Akku: 15 %" -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es `mbot.setLed(255, 0, 0)`. -lösung: vb-java-akku-loesung.visustruct -::: - -## Spiel: Stoppuhr und Akku - -::: spiel frage -titel: Spiel: Stoppuhr und Akku -intro: Lies den Code und sag vorher, was passiert. Fünf Runden. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: Wie lange fährt der mBot2? -- 0,1 Sekunden -- [x] 2 Sekunden -- bis zur Wand -> Er wartet, bis die Stoppuhr 2 Sekunden zeigt, dann hält er an. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: Mit 50 % schafft er 10 cm pro Sekunde. Wie weit fährt er? -- 10 cm -- [x] 20 cm -- 50 cm -> 2 Sekunden mal 10 cm pro Sekunde sind 20 cm. - -runde: - mbot.pause(3000); - mbot.resetTimer(); - mbot.pause(1000); - mbot.showText(mbot.getTimer()); -frage: Welche Zahl zeigt der Bildschirm ungefähr? -- 0 -- [x] 1 -- 4 -> Die Stoppuhr fängt nach `mbot.resetTimer()` wieder bei 0 an. Danach vergeht nur noch 1 Sekunde. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Akku: " + mbot.getBattery() + " %"); -frage: Der Akku hat noch 15 %. Was passiert? -- Nur der Bildschirm zeigt **Akku: 15 %**. -- Nur die LEDs leuchten rot. -- [x] Der Bildschirm zeigt **Akku: 15 %**, und die LEDs leuchten rot. -> 15 ist kleiner als 20. Die Anzeige steht unter der Verzweigung und kommt immer. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Akku: " + mbot.getBattery() + " %"); -frage: Der Akku hat 80 %. Was passiert? -- [x] Nur der Bildschirm zeigt **Akku: 80 %**. -- Nur die LEDs leuchten rot. -- Gar nichts. -> 80 ist nicht kleiner als 20, die LEDs bleiben aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Stoppuhr auf 0 setzen und ablesen. → Die Stoppuhr -- Ich kann eine bestimmte Zeit lang fahren. → Zwei Sekunden fahren im Struktogramm -- Ich kann den Akkustand anzeigen. → Jetzt in Java: Akku-Anzeige -::: - -## Wissenstest - -::: quiz -? Was liefert `mbot.getTimer()` direkt nach `mbot.resetTimer()`? -→ Die Stoppuhr -- [x] 0 -- [ ] 100 -- [ ] Die Uhrzeit -> Die Stoppuhr fängt wieder bei 0 an. -? Warum wartet der mBot2 in der Schleife nur **kurz** und hält nicht an? -→ Zwei Sekunden fahren im Struktogramm -- [ ] Weil die Schleife sonst nicht startet. -- [x] Weil er weiterfahren soll, bis die Zeit um ist. -- [ ] Weil die Stoppuhr sonst stehen bleibt. -> Gas geben gilt weiter, auch während er wartet. -::: diff --git a/web/lessons/de/visubot/21-zustaende.md b/web/lessons/de/visubot/21-zustaende.md deleted file mode 100644 index 2cfa993f..00000000 --- a/web/lessons/de/visubot/21-zustaende.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -titel: Zustände: Was mache ich gerade? -bereich: Motoren und Methoden -kurz: Mit einer Zustandsvariablen und einer Fallauswahl eine Robo-Ampel weiterschalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Zustand ist -- mit einer Fallauswahl je nach Zustand handeln und in den nächsten Zustand wechseln -- Zustände in Java mit Namen und `.equals` unterscheiden -::: - -## Was ist ein Zustand? - -Eine Ampel ist immer in genau einem **Zustand**: rot, grün oder gelb. In jedem Zustand weiß sie zwei Dinge: Was zeige ich? Und was kommt danach? - -Unsere Robo-Ampel hat drei Zustände. Jeder bekommt eine Nummer: - -| Zustand | LEDs | danach | -|---|---|---| -| 0 | rot | 1 | -| 1 | grün | 2 | -| 2 | gelb | 0 | - -Den aktuellen Zustand speicherst du in einer Variablen: `int zustand = 0`. So eine Variable heißt **Zustandsvariable**. Ein Programm, das so von Zustand zu Zustand springt, heißt **Zustandsautomat**. - -> Eine echte Ampel in Deutschland hat vier Zustände: rot, rot-gelb, grün, gelb. Unsere Robo-Ampel lässt rot-gelb weg. - -::: angeberwissen -Auch ein Saugroboter ist ein Zustandsautomat: saugen, ausweichen, zur Ladestation fahren. Ich habe nur zwei Zustände: fressen und schlafen. -::: - -## Die Robo-Ampel im Struktogramm - -Die Fallauswahl passt perfekt: Für jeden Zustand gibt es einen Fall. In jedem Fall stehen zwei Kästen: Was tue ich? Und welcher Zustand kommt als Nächstes? - -::: übung vb-ampel-zustand -titel: Robo-Ampel -auftrag: Baue ein Struktogramm: Lege `int zustand = 0` an. Eine For-Schleife läuft sechsmal. In ihr steht eine Fallauswahl über `zustand`. Fall 0: LEDs rot, `zustand = 1`. Fall 1: LEDs grün, `zustand = 2`. Sonst: LEDs gelb, `zustand = 0`. Nach der Fallauswahl wartet der mBot2 eine Sekunde. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, fallauswahl -tipp: Die Fallauswahl findest du bei den Elementen. Schreib `zustand` hinein. Die Fälle heißen 0, 1 und sonst (default). -tipp: Die Farben sind `mbot.setLed(255, 0, 0)` für rot, `mbot.setLed(0, 255, 0)` für grün und `mbot.setLed(255, 200, 0)` für gelb. Unter der Fallauswahl, noch in der Schleife, steht `mbot.pause(1000)`. -lösung: vb-ampel-zustand-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Fallauswahl“](?visustruct&lektion=10-fallauswahl). Sie öffnet sich in einem neuen Fenster. - -## Jetzt in Java: Zustände mit Namen - -Nummern sind schnell vergessen. Was war noch mal Zustand 2? Lesbarer sind Namen. Dann ist der Zustand ein Text: - -```java -String zustand = "rot"; -... -if (zustand.equals("rot")) { - mbot.setLed(255, 0, 0); - zustand = "grün"; -} else if (zustand.equals("grün")) { - ... -``` - -Texte vergleichst du in Java mit `.equals("…")`, nicht mit `==`. - -::: übung vb-java-ampel-text -titel: Ampel mit Namen -code: java -auftrag: Schreibe die Robo-Ampel in Java mit `String zustand = "rot";`. Prüfe mit `if` und `else if`, in welchem Zustand die Ampel ist: rot, dann grün, dann gelb, dann wieder rot. Die Schleife läuft sechsmal, nach jedem Durchgang 1 Sekunde Pause. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, verzweigung -tipp: In der Schleife: `if (zustand.equals("rot")) { … } else if (zustand.equals("grün")) { … } else { … }` und danach `mbot.pause(1000);`. -tipp: Im Fall rot stehen `mbot.setLed(255, 0, 0);` und `zustand = "grün";`. Grün schaltet auf `"gelb"`, gelb wieder auf `"rot"`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -::: übung vb-fehler-21-zustaende -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Ampel mit Namen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-21-zustaende-start.visustruct -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es `zustand = "grün"`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -## Zustände beim Fahren - -Mit Zuständen kann der mBot2 auch fahren. Im Zustand **fahren** fährt er vor. Sieht er eine Wand, wechselt er in den Zustand **drehen**. Dort dreht er sich einmal und wechselt zurück zu **fahren**. - -::: diagramm vb-l21z-ausweichen.visustruct -Fahren und Ausweichen mit zwei Zuständen. Öffne das Diagramm im Editor und probiere es in einer Welt aus. -::: - -## Spiel: Welcher Zustand kommt jetzt? - -::: spiel frage -titel: Spiel: Welcher Zustand kommt jetzt? -intro: Die Robo-Ampel schaltet: 0 rot → 1 grün → 2 gelb → 0 rot. Welcher Zustand kommt als Nächstes? Fünf Runden. -frage: Welcher Zustand kommt danach? -antworten: 0 | 1 | 2 - -runde: -frage: Die Ampel ist gerade in Zustand **0** (rot). -richtig: 1 -> Nach rot kommt grün. - -runde: -frage: Die Ampel ist gerade in Zustand **2** (gelb). -richtig: 0 -> Nach gelb geht es wieder von vorn los. - -runde: -frage: Die Ampel ist gerade in Zustand **1** (grün). -richtig: 2 -> Nach grün kommt gelb. - -runde: -frage: Die Ampel startet in Zustand 0 und schaltet **zweimal** weiter. -richtig: 2 -> 0 → 1 → 2. - -runde: -frage: Die Ampel startet in Zustand 0 und schaltet **dreimal** weiter. -richtig: 0 -> 0 → 1 → 2 → 0. Nach drei Schritten ist sie wieder am Anfang. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Zustand ist. → Was ist ein Zustand? -- Ich kann mit einer Fallauswahl je nach Zustand handeln und in den nächsten Zustand wechseln. → Die Robo-Ampel im Struktogramm -- Ich kann Zustände in Java mit Namen und `.equals` unterscheiden. → Jetzt in Java: Zustände mit Namen -::: - -## Wissenstest - -::: quiz -? Was speichert eine Zustandsvariable? -→ Was ist ein Zustand? -- [ ] Wie oft die Schleife gelaufen ist -- [x] Was das Programm gerade macht, zum Beispiel rot oder grün -- [ ] Den größten Messwert -? Was steht in jedem Fall der Fallauswahl? -→ Die Robo-Ampel im Struktogramm -- [x] Was zu tun ist und welcher Zustand als Nächstes kommt -- [ ] Nur eine Pause -- [ ] Eine neue Schleife -? Wie prüfst du in Java, ob `zustand` den Text `"rot"` enthält? -→ Jetzt in Java: Zustände mit Namen -- [ ] `zustand == rot` -- [x] `zustand.equals("rot")` -- [ ] `zustand = "rot"` -> Texte vergleichst du mit `.equals`. Ein einzelnes `=` würde `"rot"` zuweisen. -::: diff --git a/web/lessons/de/visubot/22-eigene-methoden.md b/web/lessons/de/visubot/22-eigene-methoden.md deleted file mode 100644 index 650a590f..00000000 --- a/web/lessons/de/visubot/22-eigene-methoden.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -titel: Eigene Methoden -bereich: Motoren und Methoden -kurz: Eigene Befehle mit Parametern und Rückgabewert schreiben und aufrufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Unterprogramm mit Parameter bauen und aufrufen -- Methoden in Java erkennen -- eine überladene Methode erkennen und aufrufen -- eine Methode mit Rückgabewert schreiben -::: - -## Ein Unterprogramm für das Quadrat - -Ein Quadrat fahren kann der mBot2 schon. Jetzt soll er zwei verschieden große Quadrate fahren. Statt die Schleife zweimal zu bauen, schreibst du ein **Unterprogramm**. In Java heißt es **Methode**. Es ist ein eigener Befehl, den du so oft aufrufen kannst, wie du willst. - -Die Kopfzeile `void quadrat(int seite)` sagt: - -- `void`: Die Methode liefert kein Ergebnis zurück, sie **tut** nur etwas. -- `quadrat`: So heißt dein neuer Befehl. -- `int seite`: Beim Aufruf gibst du eine Zahl mit, den **Parameter**. - -::: übung vb-quadrat-methode -titel: Zwei Quadrate -auftrag: Baue ein Unterprogramm `void quadrat(int seite)`, das ein Quadrat mit der Seitenlänge `seite` fährt. Das Hauptprogramm ruft es zweimal auf: erst mit 30, dann mit 50. -fahrt: 4 x (forward 30, turnRight), 4 x (forward 50, turnRight) -braucht: aufruf -tipp: Lege mit **+** einen neuen Reiter an und gib ihm den Titel `void quadrat(int seite)`. Darin steht eine For-Schleife, die viermal `mbot.forward(seite)` und `mbot.turnRight()` ausführt. -tipp: Im Hauptprogramm stehen zwei Aufruf-Kästen: `quadrat(30)` und `quadrat(50)`. -lösung: vb-quadrat-methode-loesung.visustruct -unterprogramm: vb-quadrat-unterprogramm-loesung.visustruct -::: - -## Methoden in Java - -In Java steht jede Methode in einer Klasse. Das Programm beginnt immer in der Methode `main`. Von dort wird `quadrat` aufgerufen: - -```java -public class Quadrate { - public static void main(String[] args) { - quadrat(30); - quadrat(50); - } - - static void quadrat(int seite) { - for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); - } - } -} -``` - -Im Bereich [Code](#zeige-codelive) siehst du jede Methode in ihrem eigenen Reiter, so wie im Struktogramm. - -::: quiz -? Was bedeutet `void` in `void quadrat(int seite)`? -→ Methoden in Java -- [ ] Die Methode hat keinen Parameter. -- [x] Die Methode liefert kein Ergebnis zurück. -- [ ] Die Methode darf nur einmal aufgerufen werden. -> `void` heißt „leer“: Es kommt kein Wert zurück. Der Parameter steht in den Klammern. -? Mit welchem Wert für `seite` läuft `quadrat(50)`? -→ Ein Unterprogramm für das Quadrat -- [ ] 30 -- [x] 50 -- [ ] mit keinem -> Der Wert in den Klammern des Aufrufs wird zum Parameter `seite`. -::: - -## Gleicher Name, andere Parameter: Überladung - -Du kennst `mbot.forward()` und `mbot.forward(30)`. Das sind **zwei verschiedene Methoden mit demselben Namen**. Java erkennt am Aufruf, welche gemeint ist: an der **Anzahl** und am **Typ** der Parameter. Das nennt man **Überladung** (englisch *overloading*). - -Es gibt noch eine dritte `forward`-Methode. Sie hat zwei Parameter: eine Zahl und eine **Einheit** als Text. - -```java -mbot.forward(); // 10 cm (Standardwert) -mbot.forward(30); // 30 cm -mbot.forward(30, "cm"); // 30 cm, Einheit ausdrücklich -mbot.forward(2, "Umdrehungen"); // die Räder drehen sich zweimal -``` - -Eine Radumdrehung ist so weit wie der Umfang des Rads: π · 6,5 cm ≈ 20,4 cm. Zwei Umdrehungen sind also etwa 40,8 cm. Für `mbot.backward` gibt es dieselben drei Varianten. - -So sehen die drei Kopfzeilen aus. Der Methodenname ist gleich, die Parameterlisten sind verschieden: - -```java -void forward() -void forward(int cm) -void forward(int wert, String einheit) -``` - -Überladung kennst du schon: `System.out.println` gibt es für `int`, für `double`, für `String` und noch mehr. Deshalb kannst du jeden dieser Werte einfach in die Klammern schreiben. - -::: angeberwissen -Stimmen die 20,4 cm bei **deinem** mBot2 wirklich? Profis glauben keiner Zahl, die sie nicht nachgemessen haben. Miss den Raddurchmesser und lass `mbot.forward(5, "Umdrehungen")` fahren: Soll sind 102,1 cm. So testet man Softwaresysteme: Jeder **Testfall** hat ein erwartetes Ergebnis (**Soll**) und ein gemessenes (**Ist**). Man testet mehrmals und schreibt alles in ein **Protokoll**. Hier ist dein [Messprotokoll zum Ausdrucken (PDF)](https://visubot.visucode.de/messprotokoll-radumdrehung.pdf). Gib es deiner Lehrkraft, dann sieht sie, ob alle Roboter gleich weit fahren. -::: - -::: übung vb-ueberladung-umdrehung -titel: Quadrat mit Umdrehungen -auftrag: Fahre ein Quadrat, bei dem jede Seite genau **eine Radumdrehung** lang ist. Nimm dafür die überladene Methode `mbot.forward(1, "Umdrehungen")`. -fahrt: 4 x (forward 20.42, turnRight) -braucht: forSchleife -tipp: Eine For-Schleife mit `int i = 0`, `i < 4`, `i++`. -tipp: Im Inhalt stehen `mbot.forward(1, "Umdrehungen")` und `mbot.turnRight()`. -lösung: vb-ueberladung-umdrehung-loesung.visustruct -::: - -::: übung vb-java-rechteck-umdrehungen -titel: Rechteck mit Umdrehungen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt ein Rechteck. Die langen Seiten sind **zwei** Radumdrehungen lang, die kurzen **eine**. Benutze `mbot.forward(2, "Umdrehungen")` und `mbot.forward(1, "Umdrehungen")`. -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -braucht: forSchleife -tipp: Eine For-Schleife, die zweimal läuft: `for (int i = 0; i < 2; i++) { … }`. -tipp: In der Schleife: `mbot.forward(2, "Umdrehungen");`, `mbot.turnRight();`, `mbot.forward(1, "Umdrehungen");`, `mbot.turnRight();` -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -::: quiz -? Welche Methode ruft `mbot.forward(3, "Umdrehungen")` auf? -→ Gleicher Name, andere Parameter: Überladung -- [ ] `forward()` -- [ ] `forward(int cm)` -- [x] `forward(int wert, String einheit)` -> Java wählt die Variante, deren Parameter passen: zwei Parameter, eine Zahl und ein Text. -::: - -## Methode mit Rückgabe - -Eine Methode kann auch etwas **ausrechnen** und das Ergebnis zurückgeben. Statt `void` steht dann der Datentyp des Ergebnisses in der Kopfzeile, und `return` gibt den Wert zurück: - -```java -static int zentimeter(int umdrehungen) { - return umdrehungen * 20; -} -``` - -Eine Radumdrehung sind rund 20 cm. Der Aufruf `int strecke = zentimeter(3);` legt also 60 in `strecke`. - -::: übung vb-zentimeter -titel: Umdrehungen in Zentimeter -auftrag: Baue ein Unterprogramm `int zentimeter(int umdrehungen)`, das `umdrehungen * 20` zurückgibt. Das Hauptprogramm ruft es mit 3 auf, fährt die Strecke und zeigt **Strecke: 60 cm** auf dem Bildschirm. -fahrt: forward 60 -befehle: showText "Strecke: 60 cm" -braucht: aufruf -tipp: Neuer Reiter mit dem Titel `int zentimeter(int umdrehungen)`. Darin reicht eine Anweisung: `return umdrehungen * 20`. -tipp: Im Hauptprogramm: Aufruf `int strecke = zentimeter(3)`, danach `mbot.forward(strecke)` und `mbot.showText("Strecke: " + strecke + " cm")`. -lösung: vb-zentimeter-loesung.visustruct -unterprogramm: vb-zentimeter-unterprogramm-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Aufruf und Aussprung“](?visustruct&lektion=18-aufruf-und-aussprung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-22-eigene-methoden -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Rechteck mit Umdrehungen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-22-eigene-methoden-start.visustruct -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es `mbot.forward(1, "Umdrehungen")`. -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -## Spiel: Welche Methode? - -::: spiel frage -titel: Spiel: Welche Methode? -intro: Gleicher Name, andere Parameter: Welche Methode ruft Java auf, und was kommt heraus? Sechs Runden. - -runde: - mbot.forward(30, "cm"); -frage: Welche `forward`-Methode läuft? -- `forward()` -- `forward(int cm)` -- [x] `forward(int wert, String einheit)` -> Zwei Parameter, eine Zahl und ein Text: Das passt nur zur dritten. - -runde: - mbot.forward(); -frage: Wie weit fährt der mBot2? -- [x] 10 cm -- gar nicht, die Zahl fehlt -- eine Radumdrehung -> Ohne Parameter fährt `forward` 10 cm. Das ist die Variante ohne Parameter. - -runde: - mbot.forward(2, "Umdrehungen"); -frage: Wie weit fährt der mBot2 ungefähr? -- 2 cm -- 20 cm -- [x] 41 cm -> Eine Radumdrehung sind etwa 20,4 cm, zwei also etwa 40,8 cm. - -runde: - static int zentimeter(int umdrehungen) { - return umdrehungen * 20; - } -frage: Was liefert `zentimeter(4)`? -- 4 -- 24 -- [x] 80 -> Der Parameter `umdrehungen` ist 4, und `return` gibt 4 · 20 zurück. - -runde: - public static void main(String[] args) { - quadrat(30); - quadrat(50); - } -frage: Wie viele Quadrate fährt der mBot2, und wie groß ist das zweite? -- eins mit 80 cm -- [x] zwei, das zweite mit 50 cm Seitenlänge -- zwei, beide mit 30 cm Seitenlänge -> Jeder Aufruf fährt ein Quadrat. Der Wert in den Klammern wird zu `seite`. - -runde: -frage: Welche Kopfzeile passt zu einer Methode, die eine Zahl zurückgibt? -- `void zentimeter(int umdrehungen)` -- [x] `int zentimeter(int umdrehungen)` -- `zentimeter(int umdrehungen)` -> Vor dem Namen steht der Datentyp des Ergebnisses. `void` heißt: kein Ergebnis. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - static void quadrat(int seite) { - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } - } -frage: Soll ein Quadrat mit der Seitenlänge `seite` fahren. -fehler: 3 -besser: mbot.forward(seite); -> Mit `30` ist jedes Quadrat gleich groß, egal was man übergibt. - -runde: - static void zentimeter(int umdrehungen) { - return umdrehungen * 20; - } -frage: Soll die Strecke in cm zurückgeben. -fehler: 1 -besser: static int zentimeter(int umdrehungen) { -> Wer etwas zurückgibt, braucht den Datentyp des Ergebnisses statt `void`. - -runde: - static int zentimeter(int umdrehungen) { - int cm = umdrehungen * 20; - } -frage: Soll die Strecke in cm zurückgeben. -fehler: 2 -besser: return umdrehungen * 20; -> Die Methode rechnet, gibt aber nichts zurück. Es fehlt `return`. - -runde: - mbot.forward(2, Umdrehungen); -frage: Soll zwei Radumdrehungen weit fahren. -fehler: 1 -besser: mbot.forward(2, "Umdrehungen"); -> Die Einheit ist ein Text und braucht Anführungszeichen. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 5 3 PARAMETER: Wert, den du beim Aufruf in die Klammern schreibst -- ↓ 1 3 STOPPUHR: mbot.getTimer() liest sie ab -- ↓ 5 5 RÜCKGABE: mit return liefert eine Methode ein Ergebnis zurück -- ↓ 3 8 ÜBERLADUNG: gleicher Methodenname, andere Parameter -- → 2 1 METHODE: eigener Befehl mit Namen, z. B. quadrat(30) -- → 10 5 AKKU: mbot.getBattery() sagt, wie voll er ist -- ↓ 2 1 MOTOR: jedes Rad hat einen eigenen -- ↓ 1 10 LINIENFOLGER: fährt einer schwarzen Linie nach -lösung: 5 7, 5 10, 4 1, 7 3, 3 3, 9 8, 5 8 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Unterprogramm mit Parameter bauen und aufrufen. → Ein Unterprogramm für das Quadrat -- Ich kann Methoden in Java erkennen. → Methoden in Java -- Ich kann eine überladene Methode erkennen und aufrufen. → Gleicher Name, andere Parameter: Überladung -- Ich kann eine Methode mit Rückgabewert schreiben. → Methode mit Rückgabe -::: - -## Wissenstest - -::: quiz -? Das Unterprogramm `void quadrat(int seite)` soll ein Quadrat mit 30 cm Seitenlänge fahren. Wie sieht der Aufruf aus? -→ Ein Unterprogramm für das Quadrat -- [ ] `quadrat()` -- [x] `quadrat(30)` -- [ ] `int seite = quadrat(30)` -> Der Wert in den Klammern wird zum Parameter `seite`. Weil `quadrat` nichts zurückgibt, gibt es keine Zuweisung. -? In welcher Methode beginnt ein Java-Programm? -→ Methoden in Java -- [ ] in der ersten Methode der Klasse -- [x] in der Methode `main` -- [ ] in der Methode `quadrat` -> Das Programm beginnt immer in `main`. Von dort werden die anderen Methoden aufgerufen. -? Woran unterscheidet Java die überladenen Methoden `forward()`, `forward(int cm)` und `forward(int wert, String einheit)`? -→ Gleicher Name, andere Parameter: Überladung -- [ ] am Namen -- [x] an der Anzahl und am Typ der Parameter -- [ ] an der Reihenfolge im Programm -> Der Name ist bei allen gleich. Java wählt die Variante, deren Parameter zum Aufruf passen. -? `static int zentimeter(int umdrehungen) { return umdrehungen * 20; }`: Was liegt nach `int strecke = zentimeter(2);` in `strecke`? -→ Methode mit Rückgabe -- [ ] 2 -- [ ] 20 -- [x] 40 -> `return` gibt 2 · 20 = 40 zurück, und dieser Wert landet in `strecke`. -? Was steht in der Kopfzeile einer Methode, die eine ganze Zahl zurückgibt, statt `void`? -→ Methode mit Rückgabe -- [ ] `return` -- [x] `int` -- [ ] `static` -> Statt `void` steht der Datentyp des Ergebnisses in der Kopfzeile. -::: diff --git a/web/lessons/de/visubot/23-wie-geht-es-weiter.md b/web/lessons/de/visubot/23-wie-geht-es-weiter.md deleted file mode 100644 index fbeb461b..00000000 --- a/web/lessons/de/visubot/23-wie-geht-es-weiter.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -titel: Wie geht es weiter? -bereich: Ausblick -kurz: Zurückblicken, was du mit dem mBot2 gelernt hast, und sehen, was in VisuStruct noch auf dich wartet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- sagen, welche Grundlagen der Programmierung ich mit dem mBot2 gelernt habe -- einen Fehler in einem Roboterprogramm allein finden und reparieren -- die Themen nennen, die in VisuStruct noch auf mich warten -::: - -## Das kannst du jetzt - -Glückwunsch, du hast alle Roboter-Lektionen geschafft! Mit dem mBot2 hast du die **Grundlagen der strukturierten Programmierung** gelernt: - -- ein **Struktogramm** lesen und bauen und es mit dem **Schreibtischtest** prüfen, einen Plan als **PAP** und als **Pseudocode** aufschreiben, -- **Sequenz**, **Verzweigung** und **Fallauswahl**, Bedingungen mit **und**, **oder** und **nicht**, -- die Schleifen **for**, **while** und **do-while**, auch ineinander verschachtelt, und den Aussprung mit **break**, -- **Variablen**, **Datentypen** und **Konstanten** mit `final`, Zählen, Summieren und den größten Wert finden, -- **Arrays** und **Zeichenketten**, -- eigene **Methoden** mit Parametern und Rückgabewert. - -Das alles gilt nicht nur für Roboter. Jedes Java-Programm ist aus genau diesen Bausteinen gebaut, auch ein Spiel oder eine App. - -::: angeberwissen -Määäh, weißt du, was du mit dem mBot2 eigentlich gebaut hast? Ein **eingebettetes System**: einen kleinen Computer, der in einem Gerät steckt und mit Sensoren seine Umwelt misst. Solche Systeme stecken in Waschmaschinen, Autos und Herzschrittmachern. In einem modernen Auto sind es über hundert. Und programmiert werden sie mit genau den Bausteinen, die du jetzt kennst. -::: - -## Noch einmal: Warum läuft das nicht? - -Zum Schluss eine letzte Fehlersuche. Diesmal ist es ein Fehler, den auch Profis oft machen. - -::: übung vb-fehler-23-wie-geht-es-weiter -titel: Warum läuft das nicht? -code: java -auftrag: Das Programm soll bis kurz vor die Wand fahren, aber es läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-23-wie-geht-es-weiter-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Lies die Fehlermeldung genau. Vergleiche den Sensor mit den anderen Befehlen: Was steht hinter `mbot.forward`? -tipp: Hinter einem Befehl stehen immer runde Klammern, auch wenn nichts darin steht. Richtig heißt es `mbot.getDistance() > 15`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Was noch fehlt - -Einige Themen der Programmierung haben mit dem Roboter wenig zu tun. Du findest sie in **VisuStruct**, der Welt ohne Roboter. Weil du die Grundlagen schon kennst, kommst du dort schnell voran. Die Links öffnen sich in einem neuen Fenster. - -- **Eingabe mit der Tastatur:** Werte beim Ausführen abfragen, in [„Eingabe und Ausgabe“](?visustruct&lektion=06-eingabe-und-ausgabe). -- **Tabellen im Speicher:** Arrays mit Zeilen und Spalten, zum Beispiel für einen Kinosaal, in [„Zweidimensionale Arrays“](?visustruct&lektion=16-zweidimensionale-arrays). -- **Suchen und Sortieren:** einen Wert schnell finden und eine Liste der Größe nach ordnen, in [„Suchen und Sortieren“](?visustruct&lektion=19-suchen-und-sortieren). -- **Andere Sprachen:** dasselbe Programm in Python und JavaScript, in [„Python und JavaScript“](?visustruct&lektion=21-python-und-javascript). -- **Abstürze abfangen:** was passiert, wenn jemand „abc“ statt einer Zahl eintippt, in [„Wenn das Programm abstürzt: try und catch“](?visustruct&lektion=22-try-und-catch). -- **Dateien:** Text, Tabellen und Daten lesen und speichern, ab [„Text einlesen und speichern“](?visustruct&lektion=23-text-einlesen-und-speichern) über [„Tabellen als CSV“](?visustruct&lektion=25-tabellen-als-csv) bis [„Daten als JSON“](?visustruct&lektion=26-json). -- **Fenster mit Knöpfen:** eigene Programme mit Fenster, Textfeld und Knopf, ab [„Dein erstes Fenster“](?visustruct&lektion=28-erstes-fenster) bis [„Model View Controller“](?visustruct&lektion=40-model-view-controller). - -> Den mBot2 brauchst du dafür nicht. Du kannst aber jederzeit zurückkommen und ihm neue Kunststücke beibringen. - -## Spiel: Welche Welt? - -::: spiel frage -titel: Spiel: Welche Welt? -intro: Ludwig hat Ideen für neue Programme. Lernst du das mit dem mBot2 in VisuBot oder ohne Roboter in VisuStruct? Acht Runden. -antworten: VisuBot | VisuStruct - -runde: -frage: „Fahre bis zur Wand und piepe dann.“ -richtig: VisuBot -> Abstandssensor, Motoren und Lautsprecher: Das ist ein Fall für den mBot2. - -runde: -frage: „Ein Fenster mit einem Knopf, der bis 10 zählt.“ -richtig: VisuStruct -> Fenster mit Knöpfen baust du mit Swing in VisuStruct. - -runde: -frage: „Spiele eine Melodie, die in einem Array steht.“ -richtig: VisuBot -> Arrays und Melodien hast du mit dem Lautsprecher des mBot2 gelernt. - -runde: -frage: „Lies die Noten der Klasse aus einer CSV-Datei.“ -richtig: VisuStruct -> Dateien lesen und speichern lernst du in VisuStruct. - -runde: -frage: „Tippt jemand ‚abc‘ statt einer Zahl ein, soll das Programm nicht abstürzen.“ -richtig: VisuStruct -> Abstürze fängst du mit try und catch ab. Das steht in VisuStruct. - -runde: -frage: „Folge mit dem Liniensensor einer schwarzen Linie.“ -richtig: VisuBot -> Der Liniensensor sitzt unter dem mBot2. - -runde: -frage: „Sortiere zehn Zahlen der Größe nach.“ -richtig: VisuStruct -> Suchen und Sortieren steht in VisuStruct. Den größten Wert finden kannst du aber schon. - -runde: -frage: „Speichere die Bestenliste in einer Datei.“ -richtig: VisuStruct -> Dateien schreiben lernst du in VisuStruct in „Text einlesen und speichern“. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann sagen, welche Grundlagen der Programmierung ich mit dem mBot2 gelernt habe. → Das kannst du jetzt -- Ich kann einen Fehler in einem Roboterprogramm allein finden und reparieren. → Noch einmal: Warum läuft das nicht? -- Ich kann die Themen nennen, die in VisuStruct noch auf mich warten. → Was noch fehlt -::: - -## Wissenstest - -::: quiz -? Gelten die Bausteine aus den Roboter-Lektionen auch für Programme ohne Roboter? -→ Das kannst du jetzt -- [x] Ja, jedes Java-Programm ist aus Sequenz, Verzweigung, Schleife, Variablen und Methoden gebaut -- [ ] Nein, ohne Roboter braucht man ganz andere Befehle -- [ ] Nur die Schleifen -> Nur die `mbot.`-Befehle gehören zum Roboter. Alles andere ist ganz normales Java. - -? Warum läuft `mbot.getDistance > 15` nicht? -→ Noch einmal: Warum läuft das nicht? -- [ ] Weil 15 zu klein ist -- [x] Weil hinter dem Befehl die runden Klammern fehlen -- [ ] Weil man `<` schreiben muss -> Ohne Klammern sucht Java eine Variable `getDistance` und findet keine. - -? Wo lernst du, wie ein Programm Abstürze mit try und catch abfängt? -→ Was noch fehlt -- [ ] In VisuBot -- [x] In VisuStruct -> Dort steht die Lektion „Wenn das Programm abstürzt: try und catch“. - -? Welche Themen findest du in VisuStruct? Wähle die passende Liste. -→ Was noch fehlt -- [x] Sortieren, Dateien und Fenster mit Knöpfen -- [ ] Liniensensor, LEDs und Motoren -- [ ] Nur die for-Schleife -> Liniensensor, LEDs und Motoren gehören zum mBot2, die for-Schleife kennst du schon. -::: diff --git a/web/lessons/de/visubot/aufgaben/alarmanlage.md b/web/lessons/de/visubot/aufgaben/alarmanlage.md deleted file mode 100644 index b5219824..00000000 --- a/web/lessons/de/visubot/aufgaben/alarmanlage.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Die Alarmanlage -nach: 16 -stufe: 2 ---- - -Der mBot2 bewacht dein Zimmer. Kommt jemand näher als 20 cm **oder** wird es laut (Lautstärke über 60), schlägt er Alarm: Die LEDs leuchten rot und er piept. Sonst sind die LEDs aus. Mit Taste B schaltest du die Alarmanlage ab. - -::: übung vb-alarmanlage -titel: Die Alarmanlage -auftrag: Solange Taste B nicht gedrückt ist, prüft der mBot2 immer wieder: Ist der Abstand unter 20 cm oder die Lautstärke über 60, leuchten die LEDs rot und er spielt einen Ton. Sonst gehen die LEDs aus. Danach wartet er kurz. -messwerte: getDistance=10, isButtonB=false/true -> none, tone -messwerte: getLoudness=80, isButtonB=false/true -> none, tone -messwerte: getDistance=100, getLoudness=0, isButtonB=false/true -> none, noTone, ledOff -braucht: whileSchleife, verzweigung -tipp: Außen steht eine While-Schleife mit `!mbot.isButtonB()`. Darin eine Verzweigung und danach **kurz warten**. -tipp: Die Bedingung der Verzweigung: `mbot.getDistance() < 20 || mbot.getLoudness() > 60`. Im true-Zweig `mbot.setLed(255, 0, 0)` und `mbot.playTone(880, 200)`, im false-Zweig `mbot.ledOff()`. -lösung: vb-alarmanlage-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/ausgang.md b/web/lessons/de/visubot/aufgaben/ausgang.md deleted file mode 100644 index 60a7c61d..00000000 --- a/web/lessons/de/visubot/aufgaben/ausgang.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Labyrinth mit Ausgängen -nach: 5 -stufe: 3 ---- - -Der mBot2 steht unten links und schaut nach rechts. Über ihm liegen Wandreihen. Jede hat genau eine Lücke, und jede Lücke liegt weiter rechts als die darunter. Ganz oben ist das Ziel. - -Der mBot2 fährt nach rechts und schaut in jedem Feld nach oben. Ist dort eine Lücke, fährt er hindurch in den nächsten Gang. - -::: übung vb-ausgang -titel: Labyrinth mit Ausgängen -auftrag: Programmiere den mBot2 so, dass er in beiden Welten durch alle Lücken bis zum Ziel fährt, ohne anzustoßen. Ein Feld ist 30 cm groß. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: In jedem Durchlauf dreht sich der mBot2 zuerst nach links und misst. Ist oben frei, sind es mehr als 20 cm. -tipp: Durch die Lücke sind es zwei Felder bis in den nächsten Gang. Danach soll er wieder nach rechts schauen. Ist oben eine Wand, dreht er sich zurück und fährt ein Feld weiter. -lösung: vb-ausgang-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/binaer-zaehlen.md b/web/lessons/de/visubot/aufgaben/binaer-zaehlen.md deleted file mode 100644 index 1812269a..00000000 --- a/web/lessons/de/visubot/aufgaben/binaer-zaehlen.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -titel: Binär zählen -nach: 15 -stufe: 3 ---- - -Computer rechnen mit nur zwei Ziffern: 0 und 1. Das heißt **binär**. Mit drei LEDs kannst du die Zahlen 0 bis 7 binär zeigen: LED 0 zählt 1, LED 1 zählt 2 und LED 2 zählt 4. Die Zahl 5 ist 4 + 1, also leuchten LED 2 und LED 0. - -| Zahl | LED 2 (4) | LED 1 (2) | LED 0 (1) | -|---|---|---|---| -| 0 | aus | aus | aus | -| 1 | aus | aus | an | -| 2 | aus | an | aus | -| 3 | aus | an | an | -| 5 | an | aus | an | - -::: übung vb-binaer-zaehlen -titel: Binär zählen -auftrag: Eine For-Schleife zählt `zahl` von 0 bis 7. Für jede Zahl leuchtet jede der LEDs 0, 1 und 2 grün oder ist aus, genau wie in der Tabelle. Danach wartet der mBot2 1000 Millisekunden. Schalte die LEDs immer in der Reihenfolge 0, 1, 2. -befehle: ledColor 0 "aus", ledColor 1 "aus", ledColor 2 "aus", pause 1000, ledColor 0 "grün", ledColor 1 "aus", ledColor 2 "aus", pause 1000, ledColor 0 "aus", ledColor 1 "grün", ledColor 2 "aus", pause 1000, ledColor 0 "grün", ledColor 1 "grün", ledColor 2 "aus", pause 1000, ledColor 0 "aus", ledColor 1 "aus", ledColor 2 "grün", pause 1000, ledColor 0 "grün", ledColor 1 "aus", ledColor 2 "grün", pause 1000, ledColor 0 "aus", ledColor 1 "grün", ledColor 2 "grün", pause 1000, ledColor 0 "grün", ledColor 1 "grün", ledColor 2 "grün", pause 1000 -braucht: forSchleife, verzweigung -tipp: Ob LED 0 an ist, verrät der Rest beim Teilen durch 2: `zahl % 2 == 1`. Für LED 1 teilst du erst durch 2: `zahl / 2 % 2 == 1`. Für LED 2 durch 4: `zahl / 4 % 2 == 1`. -tipp: In der Schleife stehen drei Verzweigungen und danach **kurz warten**. Im true-Zweig zum Beispiel `mbot.led(0).setColor("grün")`, im false-Zweig `mbot.led(0).setColor("aus")`. -lösung: vb-binaer-zaehlen-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/dreieck.md b/web/lessons/de/visubot/aufgaben/dreieck.md deleted file mode 100644 index d75c8c94..00000000 --- a/web/lessons/de/visubot/aufgaben/dreieck.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -titel: Das Dreieck -nach: 2 -stufe: 1 ---- - -Beim Quadrat dreht sich der mBot2 viermal um 90°. Zusammen sind das 360°, eine ganze Runde. Bei einem Dreieck dreht er sich auch einmal ganz herum, aber nur in drei Ecken. - -In die Klammer von **drehe rechts** kannst du schreiben, wie weit er sich drehen soll: `mbot.turnRight(45)` dreht nur 45°. - -::: übung vb-dreieck -titel: Das Dreieck -auftrag: Lass den mBot2 ein Dreieck mit drei gleich langen Seiten fahren. Jede Seite ist 30 cm lang. Benutze eine Schleife. -fahrt: 3 x (forward 30, turnRight 120) -braucht: forSchleife -tipp: Wie viel Grad muss eine Ecke sein, damit drei Ecken zusammen eine ganze Runde (360°) ergeben? -tipp: In der Schleife fährt der mBot2 eine Seite und dreht sich dann um 120°. -lösung: vb-dreieck-loesung.visustruct -::: - -::: angeberwissen -Die Idee, mit einem fahrenden Roboter Figuren zu zeichnen, ist über 50 Jahre alt: Ende der 1960er-Jahre entstand die Sprache **Logo**. Kinder steuerten damit eine „Schildkröte“, die mit einem Stift über das Papier fuhr. -::: diff --git a/web/lessons/de/visubot/aufgaben/einparken.md b/web/lessons/de/visubot/aufgaben/einparken.md deleted file mode 100644 index 4132bf34..00000000 --- a/web/lessons/de/visubot/aufgaben/einparken.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -titel: Rückwärts einparken -nach: 16 -stufe: 2 ---- - -Rechts von der Straße parken Autos (die Klötze). Irgendwo ist eine Lücke, und genau davor klebt eine blaue Markierung auf der Straße. Der mBot2 sucht die Lücke und parkt rückwärts ein, wie ein Auto. - -Hinten hat er keinen Sensor. Er schaut deshalb nach vorn auf die Wand gegenüber: Je weiter er rückwärts in die Lücke fährt, desto größer wird der Abstand dorthin. - -::: übung vb-einparken -titel: Rückwärts einparken -auftrag: Der mBot2 fährt in 10-cm-Schritten vor, bis er auf Blau steht. Dann dreht er sich nach links, sodass sein Heck zur Lücke zeigt. Er fährt rückwärts, solange der Abstand zur Wand vor ihm unter 45 cm ist. Zum Schluss leuchten die LEDs grün und er zeigt **Geparkt!**. Es gibt zwei Straßen mit verschiedenen Lücken. -welt: parken-1 -> ledOn, screen "Geparkt!" -welt: parken-2 -> ledOn, screen "Geparkt!" -braucht: whileSchleife -tipp: Zwei While-Schleifen hintereinander: erst `!mbot.getColor().equals("blau")` mit `mbot.forward()` darin, dann `mbot.getDistance() < 45` mit `mbot.backward()` darin. Dazwischen steht `mbot.turnLeft()`. -tipp: Am Ende: `mbot.setLed(0, 255, 0)` und `mbot.showText("Geparkt!")`. -lösung: vb-einparken-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/hindernis.md b/web/lessons/de/visubot/aufgaben/hindernis.md deleted file mode 100644 index 3942b474..00000000 --- a/web/lessons/de/visubot/aufgaben/hindernis.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -titel: Hindernis ausweichen -nach: 16 -stufe: 2 ---- - -Auf dem Weg zum roten Feld stehen Klötze im Weg. Der mBot2 fährt in 10-cm-Schritten vor. Taucht ein Klotz auf (näher als 20 cm), warnt er mit Licht und Ton und fährt außen herum: nach rechts drehen, 30 cm vor, nach links drehen. Dann geht es weiter geradeaus. - -::: übung vb-hindernis -titel: Hindernis ausweichen -auftrag: Der mBot2 fährt, solange er nicht auf Rot steht. Ist ein Klotz näher als 20 cm, leuchten die LEDs orange (`mbot.setLed(255, 128, 0)`), er piept und weicht aus. Danach gehen die LEDs aus. Sonst fährt er 10 cm vor. Es gibt zwei Welten. -welt: hindernis-1 -> tone -welt: hindernis-2 -> tone -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit `!mbot.getColor().equals("rot")`, darin eine Verzweigung mit `mbot.getDistance() < 20`. -tipp: Im true-Zweig: `mbot.setLed(255, 128, 0)`, `mbot.playTone(880, 200)`, `mbot.turnRight()`, `mbot.forward(30)`, `mbot.turnLeft()`, `mbot.ledOff()`. Im false-Zweig: `mbot.forward()`. -lösung: vb-hindernis-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/knight-rider.md b/web/lessons/de/visubot/aufgaben/knight-rider.md deleted file mode 100644 index ad95a901..00000000 --- a/web/lessons/de/visubot/aufgaben/knight-rider.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: Knight Rider -nach: 15 -stufe: 2 ---- - -In der Fernsehserie „Knight Rider“ hat das sprechende Auto KITT vorn ein rotes Licht, das immer hin und her läuft. Das baust du mit den fünf LEDs nach. - -::: übung vb-knight-rider -titel: Knight Rider -auftrag: Ein rotes Licht läuft von LED 0 bis LED 4 und wieder zurück bis LED 1. Jede LED leuchtet 100 Millisekunden und geht dann aus. Benutze zwei For-Schleifen: eine zählt hoch, eine zählt runter. -befehle: ledColor 0 "rot", pause 100, ledColor 0 "aus", ledColor 1 "rot", pause 100, ledColor 1 "aus", ledColor 2 "rot", pause 100, ledColor 2 "aus", ledColor 3 "rot", pause 100, ledColor 3 "aus", ledColor 4 "rot", pause 100, ledColor 4 "aus", ledColor 3 "rot", pause 100, ledColor 3 "aus", ledColor 2 "rot", pause 100, ledColor 2 "aus", ledColor 1 "rot", pause 100, ledColor 1 "aus" -braucht: forSchleife -tipp: Die erste Schleife zählt `i` von 0 bis 4 hoch. Die zweite startet bei 3 und zählt mit `i--` runter, solange `i > 0`. -tipp: In beiden Schleifen: `mbot.led(i).setColor("rot")`, `mbot.pause(100)`, `mbot.led(i).setColor("aus")`. -lösung: vb-knight-rider-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/labyrinth.md b/web/lessons/de/visubot/aufgaben/labyrinth.md deleted file mode 100644 index 0272a546..00000000 --- a/web/lessons/de/visubot/aufgaben/labyrinth.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -titel: Das Labyrinth -nach: 5 -stufe: 2 ---- - -Der mBot2 steht in einem Labyrinth. Das Ziel ist ein schwarzes Feld mit grüner Fahne. Die Gänge sind 30 cm breit. - -Eine einfache Regel bringt ihn hindurch: **Ist vorn frei, fahr vor. Sonst dreh dich nach rechts. Ist dort auch eine Wand, dreh dich ganz um.** - -Woran merkt der mBot2, dass er im Ziel ist? Der Liniensensor unten sieht das schwarze Feld: `mbot.getLine()` ist dann nicht mehr `0`. - -::: übung vb-labyrinth -titel: Das Labyrinth -auftrag: Programmiere den mBot2 so, dass er in beiden Labyrinthen ins Ziel findet, ohne anzustoßen. -welt: labyrinth-1, labyrinth-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit der Bedingung `mbot.getLine() == 0`. Sie läuft, bis der mBot2 auf dem Ziel steht. -tipp: In der Schleife eine Verzweigung `mbot.getDistance() > 15`: Ja → `mbot.forward()`. Nein → `mbot.turnRight()`, danach noch eine Verzweigung `mbot.getDistance() < 15` mit `mbot.turnRight(180)`. -lösung: vb-labyrinth-loesung.visustruct -::: - -::: angeberwissen -Mit der Regel „Hand immer an der Wand“ findet man aus jedem Labyrinth, dessen Wände alle zusammenhängen. Schon die Griechen erzählten von Theseus, der sich im Labyrinth des Minotaurus mit einem Wollfaden half. -::: diff --git a/web/lessons/de/visubot/aufgaben/linienfolger-stopp.md b/web/lessons/de/visubot/aufgaben/linienfolger-stopp.md deleted file mode 100644 index 22e48d87..00000000 --- a/web/lessons/de/visubot/aufgaben/linienfolger-stopp.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Linienfolger mit Stopp -nach: 20 -stufe: 3 ---- - -Der mBot2 folgt einer schwarzen Linie und hält am roten Feld an ihrem Ende an. - -Dafür gibt er mit **Räder einzeln** Gas: `mbot.drive(40, 40)` lässt beide Räder gleich schnell drehen, er fährt geradeaus weiter, bis ein anderer Fahrbefehl kommt. `mbot.drive(10, 40)` macht eine Linkskurve, `mbot.drive(40, 10)` eine Rechtskurve. `mbot.stop()` hält ihn an. - -Die beiden mittleren Fühler des Liniensensors fragst du mit `mbot.isLineLeft()` und `mbot.isLineRight()` ab. Sehen beide die Linie, fährt er geradeaus. Sieht nur der linke sie, lenkt er nach links, sieht nur der rechte sie, nach rechts. - -::: übung vb-linienfolger-stopp -titel: Linienfolger mit Stopp -auftrag: Solange der mBot2 nicht auf Rot steht, folgt er der Linie. Danach hält er an. In der zweiten Welt schlängelt sich die Linie. -welt: linie-rot-1, linie-rot-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit `!mbot.getColor().equals("rot")`, danach **anhalten**. -tipp: In der Schleife: Sind `mbot.isLineLeft() && mbot.isLineRight()`, dann `mbot.drive(40, 40)`. Sonst, wenn `mbot.isLineLeft()`, dann `mbot.drive(10, 40)`. Sonst, wenn `mbot.isLineRight()`, dann `mbot.drive(40, 10)`. -lösung: vb-linienfolger-stopp-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/morsen.md b/web/lessons/de/visubot/aufgaben/morsen.md deleted file mode 100644 index b138ca96..00000000 --- a/web/lessons/de/visubot/aufgaben/morsen.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: Morsen -nach: 15 -stufe: 2 ---- - -Früher haben Schiffe mit Licht Nachrichten geschickt, im **Morsecode**: Jeder Buchstabe besteht aus kurzen und langen Signalen. Der bekannteste Notruf ist **SOS**: S ist dreimal kurz, O ist dreimal lang. - -::: übung vb-morsen -titel: SOS morsen -auftrag: LED 2 morst SOS in Weiß. Kurz heißt: 200 Millisekunden an, dann 200 aus. Lang heißt: 600 Millisekunden an, dann 200 aus. Zwischen den Buchstaben wartet der mBot2 zusätzlich 400 Millisekunden. Benutze für jeden Buchstaben eine Schleife. -befehle: 3 x (ledColor 2 "weiß", pause 200, ledColor 2 "aus", pause 200), pause 400, 3 x (ledColor 2 "weiß", pause 600, ledColor 2 "aus", pause 200), pause 400, 3 x (ledColor 2 "weiß", pause 200, ledColor 2 "aus", pause 200) -braucht: forSchleife -tipp: Drei For-Schleifen hintereinander, jede läuft dreimal. Dazwischen steht jeweils **kurz warten** mit 400. -tipp: In der ersten und dritten Schleife: `mbot.led(2).setColor("weiß")`, `mbot.pause(200)`, `mbot.led(2).setColor("aus")`, `mbot.pause(200)`. In der mittleren wartet er nach dem Einschalten 600. -lösung: vb-morsen-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/nachtwaechter.md b/web/lessons/de/visubot/aufgaben/nachtwaechter.md deleted file mode 100644 index 4c03cdf2..00000000 --- a/web/lessons/de/visubot/aufgaben/nachtwaechter.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -titel: Der Nachtwächter -nach: 18 -stufe: 1 ---- - -Der mBot2 ist ein Nachtwächter. Solange es dunkel ist (Helligkeit unter 20), läuft er Streife: 30 cm vor und dann umdrehen. Sobald es hell wird, ist seine Schicht vorbei: Er zeigt **Zzz** auf dem Bildschirm und schläft. - -::: übung vb-nachtwaechter -titel: Der Nachtwächter -auftrag: Solange die Helligkeit unter 20 ist, fährt der mBot2 30 cm vor und dreht sich um 180°. Danach zeigt er **Zzz** auf dem Bildschirm. -messwerte: getBrightness=5/80 -> drives, screen "Zzz" -messwerte: getBrightness=80 -> none, screen "Zzz" -braucht: whileSchleife -tipp: Eine While-Schleife mit `mbot.getBrightness() < 20`. Darin `mbot.forward(30)` und `mbot.turnRight(180)`. -tipp: Nach der Schleife steht `mbot.showText("Zzz")`. -lösung: vb-nachtwaechter-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/slalom.md b/web/lessons/de/visubot/aufgaben/slalom.md deleted file mode 100644 index b26b07ec..00000000 --- a/web/lessons/de/visubot/aufgaben/slalom.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Vor dem mBot2 stehen vier Pylonen in einer Reihe. Er fährt Slalom: am ersten Pylon oben vorbei, am zweiten unten, dann wieder oben und unten. Hinter dem letzten Pylon ist das Ziel. - -In dieser Welt ist ein Feld 30 cm groß, so breit wie der mBot2. Ein Schritt von Feld zu Feld ist also `mbot.forward(30)`. - -::: übung vb-slalom -titel: Slalom -auftrag: Fahre den Slalom bis ins Ziel, ohne einen Pylon zu berühren. Benutze eine Schleife. -welt: slalom -braucht: forSchleife -tipp: Überlege zuerst, wie der mBot2 oben um einen einzigen Pylon herumfährt: Er steht danach zwei Felder weiter in derselben Reihe und schaut wieder nach rechts. -tipp: Unten herum geht genauso, nur mit links und rechts vertauscht. „Oben herum, dann unten herum“ wiederholt sich zweimal. -lösung: vb-slalom-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/spirale.md b/web/lessons/de/visubot/aufgaben/spirale.md deleted file mode 100644 index d09c7ca4..00000000 --- a/web/lessons/de/visubot/aufgaben/spirale.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Die Spirale -nach: 2 -stufe: 2 ---- - -Der mBot2 soll eine eckige Spirale fahren: Die erste Seite ist 10 cm lang, die zweite 20 cm, die dritte 30 cm und so weiter. Nach jeder Seite dreht er sich nach rechts. - -Die Zählvariable einer For-Schleife kannst du auch in der Schleife benutzen, zum Beispiel in der Klammer von **fahre vor**. Dort darf auch eine Rechnung stehen, etwa `mbot.forward(i * 5)`. - -::: übung vb-spirale -titel: Die Spirale -auftrag: Lass den mBot2 eine Spirale mit 8 Seiten fahren: 10 cm, 20 cm, 30 cm … bis 80 cm. Nach jeder Seite dreht er sich nach rechts. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight, forward 50, turnRight, forward 60, turnRight, forward 70, turnRight, forward 80, turnRight -braucht: forSchleife -tipp: Lass die Zählvariable von 1 bis 8 laufen. Wie lang ist dann die Seite in jedem Durchlauf? -tipp: In der Klammer von **fahre vor** steht eine Rechnung mit der Zählvariable. -lösung: vb-spirale-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/treppe.md b/web/lessons/de/visubot/aufgaben/treppe.md deleted file mode 100644 index 6d48ee46..00000000 --- a/web/lessons/de/visubot/aufgaben/treppe.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: Die Treppe -nach: 2 -stufe: 1 ---- - -Der mBot2 steigt eine Treppe hinauf, von oben gesehen: Jede Stufe ist 20 cm tief und 20 cm breit. - -::: übung vb-treppe -titel: Die Treppe -auftrag: Lass den mBot2 drei Stufen fahren. Eine Stufe: 20 cm vor, nach links drehen, 20 cm vor, nach rechts drehen. Benutze eine Schleife. -fahrt: 3 x (forward 20, turnLeft, forward 20, turnRight) -braucht: forSchleife -tipp: Schreib zuerst eine Stufe auf. Dann packst du sie in eine For-Schleife, die dreimal läuft. -tipp: In der Schleife: `mbot.forward(20)`, `mbot.turnLeft()`, `mbot.forward(20)`, `mbot.turnRight()`. -lösung: vb-treppe-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/tunnel.md b/web/lessons/de/visubot/aufgaben/tunnel.md deleted file mode 100644 index 5871205d..00000000 --- a/web/lessons/de/visubot/aufgaben/tunnel.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Der Tunnelsucher -nach: 5 -stufe: 3 ---- - -Der mBot2 soll im ersten **Tunnelfeld** anhalten: Das ist ein Feld mit einer Wand links **und** einer Wand rechts. Felder mit nur einer Wand zählen nicht. In der Simulation ist das richtige Feld hellgrün, der Liniensensor sieht es aber nicht. - -Der Ultraschallsensor schaut nur nach vorn. Um nach links zu schauen, dreht sich der mBot2 nach links, misst und dreht sich wieder zurück. - -Den Messwert merkst du dir in einer **Variable**, zum Beispiel in einer Anweisung `links = mbot.getDistance()`. Am Anfang legst du sie an: `int links = 99`. - -::: übung vb-tunnel -titel: Der Tunnelsucher -auftrag: Programmiere den mBot2 so, dass er in beiden Welten im ersten Tunnelfeld anhält. Ein Feld ist 30 cm groß. -welt: tunnel-1, tunnel-2 -braucht: doUntilSchleife -tipp: In jedem Durchlauf fährt der mBot2 ein Feld vor und schaut sich dann nach links und nach rechts um. Merk dir beide Abstände in Variablen. Am Ende schaut er wieder nach vorn. -tipp: Hier passt eine Schleife, die erst fährt und danach prüft. Sie läuft weiter, solange links **oder** rechts mindestens 20 cm frei ist: `links >= 20 || rechts >= 20`. -lösung: vb-tunnel-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/aufgaben/wald.md b/web/lessons/de/visubot/aufgaben/wald.md deleted file mode 100644 index 33ae6531..00000000 --- a/web/lessons/de/visubot/aufgaben/wald.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Kleeblatt im Wald -nach: 5 -stufe: 2 ---- - -Das Ziel liegt geradeaus. Auf dem Weg stehen Bäume, aber nie zwei direkt hintereinander. Steht ein Baum vor dem mBot2, fährt er außen herum und dann weiter geradeaus. - -Ein Feld ist 30 cm groß. Steht ein Baum im nächsten Feld, misst der Ultraschallsensor weniger als 20 cm. - -::: übung vb-wald -titel: Kleeblatt im Wald -auftrag: Der mBot2 soll in beiden Welten bis zum Ziel fahren und dabei jeden Baum umfahren, ohne anzustoßen. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: Wie bei „Fahre bis zum Ziel“ läuft außen eine Schleife, bis der Liniensensor das Ziel sieht. In jedem Durchlauf gibt es zwei Möglichkeiten: Baum vorn oder frei. -tipp: Um einen Baum herum: nach links drehen, ein Feld vor, nach rechts drehen, zwei Felder vor, nach rechts drehen, ein Feld vor, nach links drehen. -lösung: vb-wald-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/bloecke/01-losfahren.md b/web/lessons/de/visubot/bloecke/01-losfahren.md deleted file mode 100644 index 652a8d37..00000000 --- a/web/lessons/de/visubot/bloecke/01-losfahren.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -titel: Losfahren -bereich: Erste Schritte -kurz: Den mBot2 mit Blöcken fahren und drehen lassen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 mit Blöcken fahren lassen -- die Zahl in einem Block ändern -- den mBot2 drehen lassen -::: - -Du baust dein Programm aus **Blöcken**, wie aus Bausteinen. Probier ruhig alles aus. Kaputt machen kannst du nichts. - -## Dein erster Block - -Links unter **Elemente** findest du die Gruppe **🚗 Fahren und Drehen**. Zieh den Block **fahre vor** nach rechts und lass ihn direkt unter **🏁 Wenn das Programm startet** los. - -Im Block steht jetzt `mbot.forward( )`. Das ist Englisch: *forward* heißt vorwärts. Ganz rechts unter **mBot2 Live** siehst du sofort, wohin dein mBot2 fährt. - -::: übung vbb-erster-block -titel: Der erste Block -auftrag: Zieh einen Block **fahre vor** unter **🏁 Wenn das Programm startet**. Klick dann auf **Simulation starten** und schau zu. -fahrt: forward -tipp: Die Blöcke liegen links unter **Elemente**, in der Gruppe **🚗 Fahren und Drehen**. -tipp: Lass den Block los, wenn er genau unter dem Block mit der Zielflagge 🏁 hängt. -lösung: vbb-erster-block-loesung.visustruct -::: - -## Weiter fahren - -Ist das weiße Feld im Block leer, fährt der mBot2 nur 10 cm. Das ist eine Kästchenbreite im Gitter. Klick auf das weiße Feld und tipp eine Zahl ein, zum Beispiel **50**. Jetzt fährt er 50 cm. - -::: übung vbb-weiter -titel: 50 cm -auftrag: Lass den mBot2 genau 50 cm vorwärts fahren. Benutze dafür nur **einen** Block. -fahrt: forward 50 -tipp: Klick in das weiße Feld im Block **fahre vor**. -tipp: Tipp **50** ein und drück Enter. Im Block steht dann `mbot.forward(50)`. -lösung: vbb-weiter-loesung.visustruct -::: - -Probier aus: Was passiert bei **200**? Und was bei **fahre zurück**? - -## Um die Ecke - -Mit **drehe rechts** dreht sich der mBot2 auf der Stelle. Ist das Feld leer, dreht er sich um 90 Grad, also eine Ecke wie bei einem Blatt Papier. Die Blöcke werden von oben nach unten ausgeführt, einer nach dem anderen. - -::: übung vbb-ecke -titel: Um die Ecke -auftrag: Der mBot2 fährt 50 cm vor, dreht sich nach rechts und fährt dann noch 30 cm. -fahrt: forward 50, turnRight 90, forward 30 -tipp: Du brauchst drei Blöcke: **fahre vor**, **drehe rechts** und noch einmal **fahre vor**. -tipp: Im ersten Block steht 50, im letzten 30. Das Feld von **drehe rechts** bleibt leer. -lösung: vbb-ecke-loesung.visustruct -::: - -Jetzt du: Tipp bei **drehe rechts** mal **180** ein. Wohin schaut der mBot2 danach? - -::: übung vbb-hin-und-zurueck -titel: Hin und zurück -auftrag: Der mBot2 fährt 50 cm vor, dreht sich um und fährt zurück zum Start. -fahrt: forward 50, turnRight 180, forward 50 -tipp: Umdrehen heißt: einmal halb herum, das sind 180 Grad. -tipp: **fahre vor** mit 50, **drehe rechts** mit 180, **fahre vor** mit 50. -lösung: vbb-hin-und-zurueck-loesung.visustruct -::: - -::: ludwig -Zwei Blöcke **drehe rechts** mit leerem Feld hintereinander gehen auch. 90 und 90 sind zusammen 180. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 2,0 -> 60 cm sind zwei Felder. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,1 -> Ein Feld nach vorn, nach rechts drehen, ein Feld weiter. - -runde: - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,2 -> Erst dreht er sich nach links, dann fährt er zwei Felder. - -runde: - mbot.forward(30); - mbot.turnRight(180); - mbot.forward(60); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Ein Feld vor, umdrehen, und dann zwei Felder zurück. Er landet unter dem Start. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann einen Block ins Programm ziehen und starten. → Dein erster Block -- Ich kann die Zahl in einem Block ändern. → Weiter fahren -- Ich kann den mBot2 um die Ecke fahren lassen. → Um die Ecke -::: diff --git a/web/lessons/de/visubot/bloecke/02-immer-wieder.md b/web/lessons/de/visubot/bloecke/02-immer-wieder.md deleted file mode 100644 index 7b15b26a..00000000 --- a/web/lessons/de/visubot/bloecke/02-immer-wieder.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -titel: Immer wieder -bereich: Erste Schritte -kurz: Ein Quadrat fahren und mit „wiederhole“ Blöcke sparen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 ein Quadrat fahren lassen -- mit dem Block „wiederhole“ Blöcke sparen -- eigene Formen ausprobieren -::: - -## Ein Quadrat - -Ein Quadrat hat vier gleich lange Seiten und vier Ecken. Für jede Seite fährt der mBot2 ein Stück vor und dreht sich dann nach rechts. - -::: übung vbb-quadrat -titel: Das Quadrat -auftrag: Lass den mBot2 ein Quadrat mit 50 cm langen Seiten fahren. Am Ende steht er wieder am Start. -fahrt: 4 x (forward 50, turnRight 90) -tipp: Eine Seite sind zwei Blöcke: **fahre vor** mit 50 und **drehe rechts**. -tipp: Diese zwei Blöcke brauchst du viermal untereinander, zusammen also acht Blöcke. -lösung: vbb-quadrat-loesung.visustruct -::: - -Acht Blöcke, und immer wieder dieselben zwei. Das geht auch kürzer! - -## Wiederhole - -In der Gruppe **Steuerung** liegt der Block **wiederhole 10 mal**. Er sieht aus wie ein Maul. Alles, was du hineinlegst, macht der mBot2 so oft, wie die Zahl sagt. So eine Wiederholung heißt beim Programmieren **Schleife**. - -::: übung vbb-quadrat-wiederhole -titel: Quadrat mit wiederhole -auftrag: Baue das Quadrat noch einmal, aber nur mit **wiederhole** und zwei Blöcken darin. -fahrt: 4 x (forward 50, turnRight 90) -braucht: forSchleife -tipp: Zieh **wiederhole 10 mal** unter 🏁. Leg **fahre vor** und **drehe rechts** in das Maul. -tipp: Klick auf die 10 und tipp **4** ein, denn ein Quadrat hat vier Seiten. -lösung: vbb-quadrat-wiederhole-loesung.visustruct -::: - -Aus acht Blöcken sind drei geworden. Starte die Simulation und schau zu, wie der mBot2 viermal dasselbe macht. - -## Neue Formen - -Jetzt bist du dran. Ändere nur die Zahlen in deinem Programm und schau, was passiert. - -::: übung vbb-dreieck -titel: Das Dreieck -auftrag: Lass den mBot2 ein Dreieck mit 50 cm langen Seiten fahren. -fahrt: 3 x (forward 50, turnRight 120) -braucht: forSchleife -tipp: Ein Dreieck hat drei Seiten. Also **wiederhole 3 mal**. -tipp: Bei drei Ecken muss er sich jedes Mal um 120 Grad drehen. Tipp **120** in **drehe rechts**. -lösung: vbb-dreieck-loesung.visustruct -::: - -Noch mehr zum Ausprobieren: - -- **wiederhole 5 mal** und **drehe rechts** mit **72** ergibt ein Fünfeck. -- **wiederhole 6 mal** mit **60** ergibt ein Sechseck. -- Was passiert mit **wiederhole 36 mal**, **fahre vor** mit 10 und **drehe rechts** mit 10? - -::: ludwig -Fällt dir etwas auf? Die Zahl beim Wiederholen mal die Zahl beim Drehen ergibt immer 360. Einmal ganz herum! -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - for (int i = 0; i < 2; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..^.. -ziel: 2,0 -> Zweimal ein Feld vor, das sind zwei Felder. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: ..... | ..... | ..^.. -ziel: 2,2 -> Das ist ein Quadrat. Am Ende steht er wieder am Start. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..... | ..^.. -ziel: 3,0 -> Erst dreimal vor, dann nach rechts drehen und noch ein Feld. Der letzte Block steht nicht mehr im Maul von wiederhole. - -runde: - for (int i = 0; i < 2; i++) { - mbot.forward(30); - mbot.turnLeft(); - } -karte: ..... | ..... | ..^.. -ziel: 1,1 -> Vor, links drehen, vor, links drehen. Er steht links neben dem Feld über dem Start. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 ein Quadrat fahren lassen. → Ein Quadrat -- Ich kann mit „wiederhole“ Blöcke sparen. → Wiederhole -- Ich kann neue Formen ausprobieren. → Neue Formen -::: diff --git a/web/lessons/de/visubot/bloecke/03-der-mbot2-misst.md b/web/lessons/de/visubot/bloecke/03-der-mbot2-misst.md deleted file mode 100644 index c4e65451..00000000 --- a/web/lessons/de/visubot/bloecke/03-der-mbot2-misst.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -titel: Der mBot2 misst -bereich: Sensoren und Entscheidungen -kurz: Den Abstand zur Wand messen und rechtzeitig anhalten. ---- - -## Das schaffst du heute - -::: kompetenzen -- den Abstand messen und auf dem Bildschirm zeigen -- fahren lassen, solange vorn frei ist -::: - -## Die Augen vorn - -Vorn am mBot2 sitzen zwei runde „Augen“. Das ist der **Ultraschallsensor**. Er schickt ein Geräusch los, das du nicht hören kannst, und wartet auf das Echo. So weiß er, wie weit die nächste Wand weg ist, in Zentimetern. - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Links unter **Elemente** findest du weiter unten die **mBot2-Sensoren**. Ein Sensor ist kein eigener Block. Er ist ein runder Baustein, den du in ein weißes Feld legst. - -::: übung vbb-abstand-zeigen -titel: Abstand anzeigen -auftrag: Der mBot2 soll den Abstand zur Wand auf seinem Bildschirm zeigen. Nimm dafür den Block **Text anzeigen** und leg den Sensor **Abstand** in sein weißes Feld. -messwerte: getDistance=35 -> screen "35" -messwerte: getDistance=12 -> screen "12" -tipp: **Text anzeigen** liegt in der Gruppe **🖥️ Bildschirm**, **Abstand** in der Gruppe **📏 Abstand**. -tipp: Zieh **Abstand** genau auf das weiße Feld mit „Hallo“. Dann steht im Block `mbot.showText(mbot.getDistance())`. -lösung: vbb-abstand-zeigen-loesung.visustruct -::: - -## Fahren, solange frei ist - -Der mBot2 soll auf eine Wand zufahren und kurz davor anhalten. Wie weit die Wand weg ist, weiß er vorher nicht. Er muss also immer wieder messen. - -Dafür gibt es die **While-Schleife**. Im Block steht oben **solange**. Sie wiederholt ihren Inhalt, solange die Frage oben drin mit **ja** beantwortet wird. So baust du die Frage „Ist mehr als 15 cm frei?“: - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **größer als** auf die Frage hinter **solange**. -3. Leg den Sensor **Abstand** in das linke Feld und tipp rechts **15** ein. -4. Leg **fahre vor** in die Schleife. - -::: übung vbb-bis-wand -titel: Bis zur Wand -auftrag: Der mBot2 fährt vor, solange mehr als 15 cm frei sind. Dein Programm muss in drei Welten klappen, die Wand steht jedes Mal woanders. Über der Simulation kannst du die Welt wechseln. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Die Frage der Schleife heißt `mbot.getDistance() > 15`. -tipp: In der Schleife liegt nur **fahre vor** mit leerem Feld. So fährt er immer 10 cm und misst dann neu. -lösung: vbb-bis-wand-loesung.visustruct -::: - -Probier aus: Was passiert, wenn du statt 15 eine **5** eintippst? Und was bei **100**? - -::: ludwig -Mit fünfmal **fahre vor** klappt es nur in einer Welt. Mit dem Sensor findet der mBot2 die Wand überall. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..#.. | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, solange mehr als 15 cm frei sind. Direkt vor der Wand bleibt er stehen. - -runde: - while (mbot.getDistance() > 40) { - mbot.forward(30); - } -karte: ..#.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> Zwei Felder vor der Wand sind schon weniger als 40 cm frei. Dort hält er an. - -runde: - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..... | ..^.# | ..... -ziel: 3,1 -> Erst dreht er sich zur Wand, dann fährt er bis kurz davor. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnLeft(); - mbot.forward(60); -karte: ..#.. | ..... | ..... | ..^.. -ziel: 0,1 -> Bis vor die Wand, dann nach links drehen und zwei Felder weiter. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den Abstand auf dem Bildschirm zeigen. → Die Augen vorn -- Ich kann den mBot2 fahren lassen, solange vorn frei ist. → Fahren, solange frei ist -::: diff --git a/web/lessons/de/visubot/bloecke/04-entscheidungen.md b/web/lessons/de/visubot/bloecke/04-entscheidungen.md deleted file mode 100644 index 0484bcd5..00000000 --- a/web/lessons/de/visubot/bloecke/04-entscheidungen.md +++ /dev/null @@ -1,109 +0,0 @@ ---- -titel: Entscheidungen -bereich: Sensoren und Entscheidungen -kurz: Mit „wenn“ und „sonst“ entscheidet der mBot2 selbst, was er tut. ---- - -## Das schaffst du heute - -::: kompetenzen -- mit **wenn** und **sonst** eine Entscheidung bauen -- den mBot2 auf eine Wand reagieren lassen -- den mBot2 auf eine Farbe reagieren lassen -::: - -## Wenn … sonst … - -Du entscheidest jeden Tag: **Wenn** es regnet, nimmst du einen Schirm mit, **sonst** nicht. Der mBot2 kann das auch. - -Zieh eine **Verzweigung** unter 🏁. Oben steht **wenn** und dahinter eine Frage. Darunter kommt alles, was passiert, wenn die Antwort **ja** ist. Bei **sonst** steht, was bei **nein** passiert. Der mBot2 macht immer nur einen der beiden Teile. In Java heißen die beiden übrigens `if` und `else`. - -::: übung vbb-wand-vorn -titel: Wand vorn? -auftrag: Wenn die Wand näher als 20 cm ist, dreht sich der mBot2 nach rechts. Sonst fährt er vor. Dein Programm wird in zwei Welten geprüft, einmal mit Wand direkt vor ihm und einmal mit viel Platz. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Zieh **kleiner als** aus **Operatoren** auf die Frage bei **wenn**. Leg **Abstand** in das linke Feld und tipp rechts **20** ein. -tipp: Unter **wenn** liegt **drehe rechts**, unter **sonst** liegt **fahre vor**. -lösung: vbb-wand-vorn-loesung.visustruct -::: - -## Rot heißt Stopp - -Unten am mBot2 sitzt ein Farbsensor. Der Baustein **Farbe ist rot?** liefert schon ja oder nein. Du kannst ihn darum direkt auf die Frage bei **wenn** ziehen, ohne **kleiner als**. - -::: übung vbb-ampel -titel: Rote Ampel -auftrag: Steht der mBot2 auf Rot, hält er an. Sonst fährt er ein Stück vor. -messwerte: getColor=rot -> none -messwerte: getColor=grün -> forward -braucht: verzweigung -tipp: **Farbe ist rot?** liegt in der Gruppe **🎨 Farben** bei den Sensoren. -tipp: Unter **wenn** liegt **anhalten**, unter **sonst** liegt **fahre vor**. -lösung: vbb-ampel-loesung.visustruct -::: - -Probier aus: Klick im Block auf „rot“ und tipp **grün** ein. Was macht der mBot2 jetzt? - -::: ludwig -Ich entscheide auch so: Wenn Löwenzahn da ist, fresse ich. Sonst suche ich weiter. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Überleg, ob der mBot2 den wenn-Teil oder den sonst-Teil nimmt, und tipp auf das Feld, auf dem er am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } -karte: ..#.. | ..^.. | ..... -ziel: 2,1 -> Die Wand ist direkt vor ihm. Er nimmt den wenn-Teil, dreht sich nur und bleibt auf seinem Feld. - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } -karte: ..#.. | ..... | ..^.. -ziel: 2,1 -> Die Wand ist noch weit weg. Er nimmt den sonst-Teil und fährt ein Feld vor. - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } - mbot.forward(30); -karte: ..#.. | ..^.. | ..... -ziel: 3,1 -> Die Wand ist nah, also dreht er sich nach rechts. Der Block unter der Verzweigung läuft immer, also fährt er danach ein Feld nach rechts. - -runde: - if (mbot.getDistance() > 50) { - mbot.forward(60); - } else { - mbot.turnLeft(); - mbot.forward(30); - } -karte: ..#.. | ..... | ..^.. -ziel: 1,2 -> Bis zur Wand sind es weniger als 50 cm. Also nimmt er den sonst-Teil: links drehen und ein Feld fahren. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann mit wenn und sonst eine Entscheidung bauen. → Wenn … sonst … -- Ich kann den mBot2 auf eine Wand reagieren lassen. → Wenn … sonst … -- Ich kann den mBot2 auf eine Farbe reagieren lassen. → Rot heißt Stopp -::: diff --git a/web/lessons/de/visubot/bloecke/05-lichter-und-toene.md b/web/lessons/de/visubot/bloecke/05-lichter-und-toene.md deleted file mode 100644 index 1a1a142b..00000000 --- a/web/lessons/de/visubot/bloecke/05-lichter-und-toene.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -titel: Lichter und Töne -bereich: Licht und Ton -kurz: LEDs in allen Farben leuchten lassen, blinken und Töne spielen. ---- - -## Das schaffst du heute - -::: kompetenzen -- die LEDs in einer Farbe leuchten lassen -- die LEDs blinken lassen -- Töne spielen -::: - -## Licht an - -Hinten auf dem mBot2 sitzt eine Leiste mit fünf bunten **LEDs**. Das sind kleine Lampen. - -::: mbot2 -fokus: leds -ansicht: oben -::: - -Zieh aus der Gruppe **💡 Licht** den Block **LEDs an** unter 🏁. Im Block stehen drei Zahlen: wie viel **Rot**, wie viel **Grün** und wie viel **Blau** die LEDs mischen. Jede Zahl geht von **0** (gar nicht) bis **255** (ganz hell). `mbot.setLed(255, 0, 0)` ist also ganz rot. - -::: übung vbb-licht-gruen -titel: Grünes Licht -auftrag: Lass die LEDs grün leuchten. -befehle: setLed 0 255 0 -tipp: Rot und Blau brauchst du nicht, sie bekommen eine 0. -tipp: Im Block steht dann `mbot.setLed(0, 255, 0)`. -lösung: vbb-licht-gruen-loesung.visustruct -::: - -Probier aus: Welche Farbe gibt `255, 255, 0`? Und `255, 0, 255`? Findest du Orange? - -## Blinken - -Ein Blinklicht geht an, wartet, geht aus und wartet wieder. Und das immer wieder. Den Block **kurz warten** findest du in der Gruppe **⏱️ Warten und Stoppuhr**. Die Zahl darin sind **Millisekunden**: 1000 Millisekunden sind eine Sekunde, 500 also eine halbe. - -::: übung vbb-blinken -titel: Blinklicht -auftrag: Die LEDs blinken dreimal rot: eine halbe Sekunde an, eine halbe Sekunde aus. -befehle: 3 x (setLed 255 0 0, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: Du brauchst **wiederhole 3 mal** und darin vier Blöcke: **LEDs an**, **kurz warten**, **LEDs aus**, **kurz warten**. -tipp: In beiden Blöcken **kurz warten** steht **500**. -lösung: vbb-blinken-loesung.visustruct -::: - -::: ludwig -Ohne **kurz warten** geht das Licht so schnell an und aus, dass du gar nichts siehst. Der mBot2 ist viel schneller als deine Augen! -::: - -## Töne - -Mit **Ton spielen** aus der Gruppe **🔊 Ton** piept der mBot2. Die erste Zahl sagt, wie **hoch** der Ton ist: 200 ist tief, 1000 ist hoch. Die zweite Zahl sagt, wie **lange** er klingt, wieder in Millisekunden. - -::: übung vbb-sirene -titel: Sirene -auftrag: Baue eine Sirene: dreimal hintereinander ein hoher Ton mit 800 und ein tiefer mit 600, jeder 300 Millisekunden lang. -befehle: 3 x (playTone 800 300, playTone 600 300) -braucht: forSchleife -tipp: Leg zwei Blöcke **Ton spielen** in **wiederhole 3 mal**. -tipp: Im ersten steht `mbot.playTone(800, 300)`, im zweiten `mbot.playTone(600, 300)`. -lösung: vbb-sirene-loesung.visustruct -::: - -Jetzt du: Bau eine **Lichtshow**. Lass die LEDs abwechselnd in verschiedenen Farben leuchten und spiel bei jeder Farbe einen anderen Ton. Je mehr Farben, desto besser! - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Fünf Runden. - -runde: - mbot.setLed(0, 0, 255); -frage: Welche Farbe leuchtet? -- rot -- grün -- [x] blau -> Rot 0, Grün 0, Blau 255: nur Blau leuchtet. - -runde: - mbot.setLed(255, 255, 0); -frage: Welche Farbe leuchtet? -- [x] gelb -- lila -- weiß -> Rot und Grün zusammen ergeben bei Licht Gelb. - -runde: - for (int i = 0; i < 4; i++) { - mbot.playTone(440, 300); - } -frage: Wie oft piept der mBot2? -- 1-mal -- 3-mal -- [x] 4-mal -> Der Block **Ton spielen** liegt in **wiederhole 4 mal**. - -runde: - for (int i = 0; i < 2; i++) { - mbot.setLed(255, 0, 0); - mbot.pause(500); - mbot.ledOff(); - mbot.pause(500); - } -frage: Wie lange dauert das Programm? -- eine Sekunde -- [x] zwei Sekunden -- vier Sekunden -> Jede Runde dauert 500 + 500 Millisekunden, also eine Sekunde. Zwei Runden sind zwei Sekunden. - -runde: - mbot.playTone(1000, 200); - mbot.playTone(200, 200); -frage: Wie klingt das? -- erst tief, dann hoch -- [x] erst hoch, dann tief -- zweimal gleich -> 1000 ist ein hoher Ton, 200 ein tiefer. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann die LEDs in einer Farbe leuchten lassen. → Licht an -- Ich kann die LEDs blinken lassen. → Blinken -- Ich kann Töne spielen. → Töne -::: diff --git a/web/lessons/de/visubot/bloecke/06-immer-wieder-pruefen.md b/web/lessons/de/visubot/bloecke/06-immer-wieder-pruefen.md deleted file mode 100644 index e8289b47..00000000 --- a/web/lessons/de/visubot/bloecke/06-immer-wieder-pruefen.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -titel: Immer wieder prüfen -bereich: Sensoren und Entscheidungen -kurz: Auf Taste A warten, immer wieder prüfen und Hindernissen ausweichen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 auf Taste A warten lassen -- eine Schleife mit „wiederhole immer“ bauen und wieder verlassen -- den mBot2 Hindernissen ausweichen lassen -::: - -## Start mit Taste A - -Oben auf dem mBot2 sitzt die Taste **A**, das ist die [Taste mit dem Quadrat]{teil=buttons}. Der mBot2 soll erst losfahren, wenn du sie drückst. Bis dahin wartet er. - -Warten heißt: **solange** die Taste **nicht** gedrückt ist, kurz warten. So baust du das: - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **nicht** auf die Frage hinter **solange**. -3. Zieh den Sensor **Taste A gedrückt?** in den Baustein **nicht**. -4. Leg **kurz warten** in die Schleife und **fahre vor** darunter. - -::: übung vbb-start-a -titel: Start mit Taste A -auftrag: Der mBot2 wartet, bis Taste A gedrückt ist. Dann fährt er 30 cm vor. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: Im Kopf der Schleife steht **solange nicht** und dann `mbot.isButtonA()`. -tipp: In der Schleife liegt **kurz warten**, unter der Schleife **fahre vor** mit 30. -lösung: vbb-start-a-loesung.visustruct -::: - -> Beim Prüfen drückt VisuBot die Taste für dich: erst eine Weile nicht, dann schon. Fährt dein mBot2 zu früh los, fällt das auf. - -## Wiederhole immer - -Manche Programme sollen nie aufhören: Der mBot2 fährt, schaut, fährt, schaut … Dafür gibt es die **Endlosschleife**. Im Block steht **wiederhole immer**. - -Damit du ihn trotzdem anhalten kannst, baust du eine **Notbremse** ein: **Wenn** Taste A gedrückt ist, **verlasse die Schleife**. Den Block dafür findest du unter **Elemente**, er heißt **Aussprung**. Danach macht das Programm unter der Schleife weiter. - -::: übung vbb-notbremse -titel: Notbremse -auftrag: Der mBot2 fährt immer weiter vor. Wird Taste A gedrückt, verlässt er die Schleife und zeigt **Gestoppt** auf dem Bildschirm. -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -braucht: endlosschleife, aussprung -tipp: In **wiederhole immer** liegt zuerst eine Verzweigung mit **Taste A gedrückt?**. Unter **wenn** kommt der **Aussprung**, **sonst** bleibt leer. -tipp: Unter der Verzweigung, noch in der Schleife, liegt **fahre vor**. Unter der Schleife liegt **Text anzeigen** mit „Gestoppt“. -lösung: vbb-notbremse-loesung.visustruct -::: - -## Ausweichen - -Jetzt wird es spannend! Der mBot2 soll durch einen Raum fahren, ohne irgendwo anzustoßen. Baue dafür: - -- **wiederhole immer** -- darin: **wenn** der Abstand kleiner als 20 ist, **drehe rechts**, **sonst** **fahre vor** - -Starte die Simulation und schau zu, wie er sich seinen Weg sucht. Mit **Simulation beenden** hältst du ihn an. - -Probier aus: - -- Was passiert, wenn du statt **drehe rechts** mal **drehe links** nimmst? -- Was passiert bei **drehe rechts** mit **45**? -- Kannst du ihn bei jeder Drehung piepen lassen? - -::: ludwig -So einen Roboter gibt es wirklich: Viele Staubsauger-Roboter fahren genau so durch die Wohnung. Wand vorn? Drehen! Frei? Weiter! -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Vier Runden. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(50); -frage: Niemand drückt eine Taste. Was tut der mBot2? -- Er fährt 50 cm. -- [x] Er wartet und wartet. -- Er fährt immer weiter. -> Solange Taste A nicht gedrückt ist, wartet er in der Schleife. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(50); -frage: Du drückst Taste A. Was tut der mBot2? -- [x] Er fährt 50 cm und hört dann auf. -- Er fährt immer weiter. -- Er wartet weiter. -> Jetzt ist die Schleife zu Ende, und der Block darunter läuft einmal. - -runde: - while (true) { - mbot.playTone(440, 300); - } -frage: Wie oft piept der mBot2? -- einmal -- zehnmal -- [x] immer weiter, ohne Ende -> **wiederhole immer** hört nie von selbst auf. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } - mbot.setLed(255, 0, 0); -frage: Wann leuchten die LEDs rot? -- sofort -- [x] wenn jemand Taste A drückt -- nie -> Erst die Notbremse verlässt die Schleife. Dann geht es unter der Schleife weiter. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 auf Taste A warten lassen. → Start mit Taste A -- Ich kann eine Schleife mit „wiederhole immer“ bauen und wieder verlassen. → Wiederhole immer -- Ich kann den mBot2 Hindernissen ausweichen lassen. → Ausweichen -::: diff --git a/web/lessons/de/visubot/bloecke/07-der-linie-folgen.md b/web/lessons/de/visubot/bloecke/07-der-linie-folgen.md deleted file mode 100644 index 367d33ed..00000000 --- a/web/lessons/de/visubot/bloecke/07-der-linie-folgen.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -titel: Der Linie folgen -bereich: Sensoren und Entscheidungen -kurz: Mit dem Liniensensor am Rand umkehren und einer schwarzen Linie folgen. ---- - -## Das schaffst du heute - -::: kompetenzen -- mit dem Liniensensor eine schwarze Linie erkennen -- den mBot2 am Rand umkehren lassen -- den mBot2 einer Linie folgen lassen -::: - -## Augen nach unten - -Unter dem mBot2 sitzt vorn der **Liniensensor**. Er leuchtet auf den Boden und sieht, ob es dort hell oder dunkel ist. - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der Sensor **Linie** aus der Gruppe **➖ Linie** liefert **0**, wenn unter dem mBot2 alles hell ist. Sieht er etwas Schwarzes, ist die Zahl größer als 0. - -## Am Rand umkehren - -Stell dir einen Tisch vor, um den schwarzes Klebeband klebt. Der mBot2 soll vorfahren, bis er das Band sieht, und dann umkehren. So fällt er nicht herunter. - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **gleich** auf die Frage hinter **solange**. -3. Leg den Sensor **Linie** in das linke Feld und tipp rechts **0** ein. -4. Leg **fahre vor** in die Schleife und **drehe rechts** mit **180** darunter. - -::: übung vbb-rand -titel: Am Rand umkehren -auftrag: Der mBot2 fährt vor, solange er keine Linie sieht. Dann dreht er sich um. Es gibt zwei Welten, der Rand ist mal näher und mal weiter weg. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Die Frage der Schleife heißt `mbot.getLine() == 0`. -tipp: **drehe rechts** mit 180 liegt unter der Schleife, nicht darin. -lösung: vbb-rand-loesung.visustruct -::: - -## Der Linie folgen - -Jetzt kommt ein Klassiker: Der mBot2 fährt eine schwarze Linie entlang, auch durch Kurven. Dafür brauchst du zwei neue Dinge: - -- **Linie links?** aus der Gruppe **➖ Linie** sagt ja, wenn der linke Fühler Schwarz sieht. -- **Räder einzeln** aus der Gruppe **⚙️ Räder einzeln** lässt jedes Rad anders schnell drehen. Die erste Zahl ist das linke Rad, die zweite das rechte. Dreht das rechte schneller, fährt er eine Linkskurve. - -Die Idee ist ganz einfach: **Wenn** links Schwarz ist, lenkt er nach links. **Sonst** lenkt er nach rechts. So wackelt er an der Linie entlang. - -::: übung vbb-linienfolger -titel: Linienfolger -auftrag: Solange die Wand mehr als 15 cm weg ist, folgt der mBot2 der Linie. Danach hält er an. In der einen Welt biegt die Linie nach rechts ab, in der anderen nach links. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit `mbot.getDistance() > 15`. Darin eine Verzweigung mit **Linie links?**. Unter der Schleife liegt **anhalten**. -tipp: Unter **wenn** liegt **Räder einzeln** mit `10, 40`, unter **sonst** **Räder einzeln** mit `40, 10`. -lösung: vbb-linienfolger-loesung.visustruct -::: - -Probier aus: Was passiert mit `20, 60` und `60, 20`? Wird er schneller oder fliegt er aus der Kurve? - -::: ludwig -Am echten mBot2 klebst du die Linie mit schwarzem Isolierband auf einen hellen Boden. Fang langsam an, sonst schießt er in der Kurve über die Linie hinaus. -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Vier Runden. - -runde: - mbot.drive(40, 40); -frage: Wie fährt der mBot2? -- [x] geradeaus -- Linkskurve -- Rechtskurve -> Beide Räder drehen gleich schnell. - -runde: - mbot.drive(10, 40); -frage: Wie fährt der mBot2? -- geradeaus -- [x] Linkskurve -- Rechtskurve -> Das rechte Rad ist schneller, also schiebt es ihn nach links. - -runde: - if (mbot.isLineLeft()) { - mbot.drive(10, 40); - } else { - mbot.drive(40, 10); - } -frage: Nur der rechte Fühler sieht die Linie. Wie fährt er? -- Linkskurve -- [x] Rechtskurve -- Er hält an. -> Links ist kein Schwarz, also nimmt er den sonst-Teil: das linke Rad ist schneller. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Der mBot2 steht schon auf der schwarzen Linie. Was tut er? -- Er fährt erst ein Stück vor. -- [x] Er dreht sich sofort um. -- Er fährt immer weiter. -> Die Frage der Schleife ist gleich am Anfang nein, also läuft sie gar nicht. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann mit dem Liniensensor eine Linie erkennen. → Augen nach unten -- Ich kann den mBot2 am Rand umkehren lassen. → Am Rand umkehren -- Ich kann den mBot2 einer Linie folgen lassen. → Der Linie folgen -::: diff --git a/web/lessons/de/visubot/bloecke/08-parcours.md b/web/lessons/de/visubot/bloecke/08-parcours.md deleted file mode 100644 index 69130411..00000000 --- a/web/lessons/de/visubot/bloecke/08-parcours.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -titel: Parcours -bereich: Sensoren und Entscheidungen -kurz: Alles zusammen: der Linie bis Rot folgen, Klötzen ausweichen und einen eigenen Parcours bauen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 einer Linie bis zum roten Feld folgen lassen -- den mBot2 vor einem Klotz warnen und ausweichen lassen -- einen eigenen Parcours planen und Stück für Stück bauen -::: - -## Station 1: Bis zum roten Feld - -Ein Parcours besteht aus Stationen. Bei der ersten folgt der mBot2 einer schwarzen Linie. Am Ende der Linie liegt ein rotes Feld, dort soll er anhalten. - -Du kennst schon alles dafür. Den Linienfolger hast du in der letzten Lektion gebaut. Neu ist nur die Frage hinter **solange**: Er fährt, solange er **nicht** auf Rot steht. - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh **nicht** aus **Operatoren** auf die Frage und leg **Farbe ist rot?** hinein. -3. Leg in die Schleife eine **Verzweigung** mit **Linie links?**. -4. Unter **wenn** kommt **Räder einzeln** mit `10, 40`, unter **sonst** mit `40, 10`. -5. Unter die Schleife legst du **anhalten**. - -::: übung vbb-linie-bis-rot -titel: Bis zum roten Feld -auftrag: Der mBot2 folgt der Linie, solange er nicht auf Rot steht. Dann hält er an. In der zweiten Welt schlängelt sich die Linie. -welt: linie-rot-1, linie-rot-2 -braucht: whileSchleife, verzweigung -tipp: Die Frage der Schleife heißt `!mbot.getColor().equals("rot")`. Im Block steht dort **nicht Farbe ist rot?**. -tipp: In der Schleife liegt dieselbe Verzweigung wie beim Linienfolger aus Lektion 7. -lösung: vbb-linie-bis-rot-loesung.visustruct -::: - -## Station 2: Achtung, Klotz! - -Bei der zweiten Station stehen Klötze im Weg. Der mBot2 fährt in kleinen Schritten vor. Ist ein Klotz näher als 20 cm, warnt er mit oranger Lampe und Piepton und fährt außen herum. Danach geht die Lampe aus. - -Außen herum heißt: **drehe rechts**, **fahre vor** mit 30, **drehe links**. Dann steht er neben dem Klotz und schaut wieder nach vorn. - -::: übung vbb-klotz -titel: Achtung, Klotz! -auftrag: Solange der mBot2 nicht auf Rot steht: Ist ein Klotz näher als 20 cm, leuchtet er orange, piept und weicht aus. Sonst fährt er ein Stück vor. Es gibt zwei Welten, in der zweiten stehen zwei Klötze. -welt: hindernis-1 -> tone -welt: hindernis-2 -> tone -braucht: whileSchleife, verzweigung -tipp: Außen dieselbe Schleife wie bei Station 1. Darin eine Verzweigung mit **Abstand** **kleiner als** 20. Unter **sonst** liegt **fahre vor**. -tipp: Unter **wenn**: **LEDs an** mit `255, 128, 0`, **Ton spielen** mit `880, 200`, **drehe rechts**, **fahre vor** mit 30, **drehe links**, **LEDs aus**. -lösung: vbb-klotz-loesung.visustruct -::: - -## Dein eigener Parcours - -Jetzt bist du dran! Bau mit Klebeband und Kartons einen Parcours auf dem Boden. Plane erst, dann programmier Station für Station: - -- **Start:** Der mBot2 wartet, bis du Taste A drückst. -- **Linie:** Er folgt einer schwarzen Linie bis zu einem roten Blatt Papier. -- **Klotz:** Danach weicht er einem Karton aus. -- **Ziel:** Am Ende blinkt er grün und spielt eine Melodie. - -Probier jede Station erst allein aus. Klappt sie, hängst du die nächste unten an. So findest du Fehler viel schneller. - -::: ludwig -Profis machen das genauso: Sie testen jedes kleine Stück, bevor sie alles zusammenstecken. Bei Robotik-Wettbewerben gewinnt oft nicht der schnellste Roboter, sondern der, der zuverlässig ins Ziel kommt. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Drei Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: #.... | ..... | ^...# -ziel: 4,1 -> Er fährt bis vor die Wand, dreht sich nach rechts und fährt wieder, bis der Rand kommt. - -runde: - for (int i = 0; i < 3; i++) { - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } - } -karte: ..#.. | ..... | ..^.. -ziel: 3,1 -> Erst ist frei, er fährt ein Feld. Dann ist die Wand direkt vor ihm, er dreht sich. Beim dritten Mal ist wieder frei, er fährt nach rechts. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnLeft(); - mbot.forward(30); - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..... | ..#.. | ..... | ..^.. -ziel: 1,0 -> Er fährt bis vor den Klotz, weicht nach links aus und fährt dann geradeaus weiter bis zum Rand. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 einer Linie bis zum roten Feld folgen lassen. → Station 1: Bis zum roten Feld -- Ich kann den mBot2 vor einem Klotz warnen und ausweichen lassen. → Station 2: Achtung, Klotz! -- Ich kann einen eigenen Parcours Station für Station bauen. → Dein eigener Parcours -::: diff --git a/web/lessons/de/visubot/bloecke/aufgaben/block-ausgang.md b/web/lessons/de/visubot/bloecke/aufgaben/block-ausgang.md deleted file mode 100644 index 30bfa68d..00000000 --- a/web/lessons/de/visubot/bloecke/aufgaben/block-ausgang.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Ausgang finden -nach: 8 -stufe: 3 ---- - -Der mBot2 steht unten links und schaut nach rechts. Über ihm liegen Wände. Jede Wand hat genau eine Lücke, und jede Lücke liegt weiter rechts als die darunter. Ganz oben ist das schwarze Ziel. - -Der mBot2 fährt nach rechts und schaut in jedem Feld nach oben. Ist dort eine Lücke, fährt er hindurch in den nächsten Gang. - -::: übung vbb-ausgang -titel: Ausgang finden -auftrag: Der mBot2 fährt in beiden Welten durch alle Lücken bis zum Ziel, ohne anzustoßen. Ein Feld ist 30 cm groß. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit **Linie** **gleich** 0. Darin dreht sich der mBot2 zuerst nach links und schaut: Ist oben frei, misst er mehr als 20 cm. -tipp: **Wenn** frei: 60 vor (durch die Lücke in den nächsten Gang), dann rechts drehen. **Sonst**: rechts drehen und 30 vor. -lösung: vbb-ausgang-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/bloecke/aufgaben/block-disco.md b/web/lessons/de/visubot/bloecke/aufgaben/block-disco.md deleted file mode 100644 index 63142e47..00000000 --- a/web/lessons/de/visubot/bloecke/aufgaben/block-disco.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Disco -nach: 5 -stufe: 1 ---- - -Der mBot2 macht Party! Seine LEDs wechseln die Farbe: rot, grün, blau, immer wieder. - -::: übung vbb-disco -titel: Disco -auftrag: Die LEDs leuchten nacheinander rot, grün und blau, jede Farbe eine halbe Sekunde lang. Das Ganze dreimal. Danach gehen die LEDs aus. -befehle: 3 x (setLed 255 0 0, pause 500, setLed 0 255 0, pause 500, setLed 0 0 255, pause 500), ledOff -braucht: forSchleife -tipp: In **wiederhole 3 mal** liegen drei Blöcke **LEDs an**, nach jedem ein **kurz warten** mit 500. -tipp: Rot ist `255, 0, 0`, grün `0, 255, 0`, blau `0, 0, 255`. **LEDs aus** liegt unter der Schleife. -lösung: vbb-disco-loesung.visustruct -::: - -Probier aus: Mach eine eigene Farbe, zum Beispiel lila mit `255, 0, 255`. diff --git a/web/lessons/de/visubot/bloecke/aufgaben/block-slalom.md b/web/lessons/de/visubot/bloecke/aufgaben/block-slalom.md deleted file mode 100644 index 65d8c2d7..00000000 --- a/web/lessons/de/visubot/bloecke/aufgaben/block-slalom.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Vor dem mBot2 stehen vier Pylonen in einer Reihe. Er fährt Slalom: am ersten Pylon oben vorbei, am zweiten unten, dann wieder oben und unten. Hinter dem letzten Pylon ist das Ziel. - -Ein Feld ist 30 cm groß, so breit wie der mBot2. Ein Schritt von Feld zu Feld ist also **fahre vor** mit 30. - -::: übung vbb-slalom -titel: Slalom -auftrag: Fahre den Slalom bis ins Ziel, ohne einen Pylon zu berühren. Benutze **wiederhole**. -welt: slalom -braucht: forSchleife -tipp: Fahr erst nur oben um den ersten Pylon herum: links drehen, ein Feld vor, rechts drehen, zwei Felder vor, rechts drehen, ein Feld vor, links drehen. -tipp: Unten herum geht genauso, nur mit links und rechts vertauscht. „Oben herum, dann unten herum“ kommt in **wiederhole 2 mal**. -lösung: vbb-slalom-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/bloecke/aufgaben/block-wald.md b/web/lessons/de/visubot/bloecke/aufgaben/block-wald.md deleted file mode 100644 index abb44459..00000000 --- a/web/lessons/de/visubot/bloecke/aufgaben/block-wald.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Kleeblatt im Wald -nach: 7 -stufe: 3 ---- - -Ganz hinten im Wald liegt ein Kleeblatt auf einem schwarzen Feld. Auf dem Weg dorthin stehen Bäume, aber nie zwei direkt hintereinander. Steht ein Baum vor dem mBot2, fährt er außen herum und dann weiter geradeaus. - -Ein Feld ist 30 cm groß. Steht ein Baum im nächsten Feld, misst der Abstandssensor weniger als 20 cm. - -::: übung vbb-wald -titel: Kleeblatt im Wald -auftrag: Der mBot2 fährt in beiden Welten bis zum schwarzen Feld und umfährt dabei jeden Baum, ohne anzustoßen. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit **Linie** **gleich** 0, so wie beim Rand in Lektion 7. Darin eine Verzweigung: **wenn** **Abstand** **kleiner als** 20, **sonst** **fahre vor** mit 30. -tipp: Um einen Baum herum: links drehen, 30 vor, rechts drehen, 60 vor, rechts drehen, 30 vor, links drehen. -lösung: vbb-wald-loesung.visustruct -::: diff --git a/web/lessons/de/visubot/nachschlagen.md b/web/lessons/de/visubot/nachschlagen.md deleted file mode 100644 index ee264907..00000000 --- a/web/lessons/de/visubot/nachschlagen.md +++ /dev/null @@ -1,168 +0,0 @@ -## 🤖 mBot2-Befehle - -Ein Befehl sagt dem mBot2, was er **tun** soll. Jeder Befehl beginnt mit `mbot.` und endet mit Klammern. In die Klammern schreibst du oft eine Zahl. - -### 🚗 Fahren und Drehen {.bot-fahren} - -Der mBot2 fährt geradeaus oder dreht sich auf der Stelle. - -``` -mbot.forward() fährt 10 cm nach vorn -mbot.forward(30) fährt 30 cm nach vorn -mbot.forward(2, "Umdrehungen") die Räder drehen sich 2-mal (≈ 41 cm) -mbot.backward(1, "Umdrehungen") eine Radumdrehung zurück (≈ 20 cm) -mbot.backward(20) fährt 20 cm zurück -mbot.turnRight() dreht sich nach rechts (90°) -mbot.turnLeft() dreht sich nach links (90°) -mbot.turnLeft(45) dreht sich nur ein bisschen nach links (45°) -mbot.turnRight(180) dreht sich ganz um -``` - -> `forward` und `backward` sind **überladen**: gleicher Name, verschiedene Parameter. Als Einheit gehen `"cm"` und `"Umdrehungen"`. Eine Radumdrehung sind etwa 20,4 cm. - -> Ohne Zahl fährt er 10 cm oder dreht sich 90°. 90° ist ein rechter Winkel, wie die Ecke von einem Blatt Papier. - -> Für ein Vieleck dreht er sich insgesamt einmal ganz herum: Dreieck 3 × 120°, Quadrat 4 × 90°, Sechseck 6 × 60°. - -### ⚙️ Räder einzeln {.bot-fahren} - -Hier gibst du jedem Rad selbst Gas. Die Zahlen sind Prozent: 100 ist volles Gas, 0 ist Stillstand, eine Zahl mit Minus fährt rückwärts. Der mBot2 fährt so lange weiter, bis ein anderer Fahrbefehl kommt. - -``` -mbot.drive(40, 40) beide Räder gleich: geradeaus -mbot.drive(10, 40) links langsamer: Kurve nach links -mbot.drive(40, -40) ein Rad vor, eins zurück: dreht sich -mbot.stop() hält an -``` - -### 💡 Licht {.bot-licht} - -Der mBot2 hat fünf LEDs mit den Nummern 0 bis 4. - -``` -mbot.setLed(255, 0, 0) alle LEDs leuchten rot -mbot.ledOff() alle LEDs aus -mbot.led(0).setColor("grün") nur LED 0 leuchtet grün -mbot.led(0).setColor("aus") nur LED 0 aus -``` - -Bei `setLed` mischst du Rot, Grün und Blau, jedes von 0 (aus) bis 255 (ganz hell). - -### 🔊 Ton {.bot-ton} - -``` -mbot.playTone(440, 300) spielt einen Ton -``` - -Die erste Zahl ist die Tonhöhe. Je größer, desto höher. Die zweite Zahl ist die Länge in Millisekunden (1000 Millisekunden sind 1 Sekunde). - -### 🖥️ Bildschirm {.bot-anzeige} - -``` -mbot.showText("Hallo") zeigt Hallo auf dem Bildschirm -``` - -Mit `+` hängst du Text und Zahlen aneinander: `mbot.showText("Abstand: " + mbot.getDistance())`. - -### 😊 Gefühle {.bot-anzeige} - -Der mBot2 zeigt ein Gefühl mit Licht, Ton und Wackeln. Das siehst du nur am echten mBot2. - -``` -mbot.emotion("smile") lacht -mbot.emotion("sad") ist traurig -mbot.emotion("wow") staunt -``` - -Weitere Gefühle: hello, yeah, proud, yummy, naughty, uh_oh, hurt, angry, startle, shiver, dizzy, sleepy, zzz, wake_up, blink, yes, no, look_up, look_down, look_left, look_right, look_around - -### ⏱️ Warten und Stoppuhr {.bot-warten} - -``` -mbot.pause(1000) wartet 1 Sekunde -mbot.resetTimer() stellt die Stoppuhr auf 0 -``` - -Die Zeit der Stoppuhr liest du mit dem Sensor `mbot.getTimer()` (unten bei **Stoppuhr und Akku**). - -## 📡 mBot2-Sensoren - -Ein Sensor **misst** etwas und gibt dir einen Wert zurück. Den Wert benutzt du in einer Bedingung, zum Beispiel in einer Schleife oder Verzweigung. Manche Sensoren liefern eine **Zahl**, manche **wahr oder falsch**, einer einen **Text**. - -### 📏 Abstand {.bot-sensor} - -Der Ultraschallsensor vorn misst, wie weit die nächste Wand weg ist, in Zentimetern. - -``` -mbot.getDistance() Abstand in cm, z. B. 25 -mbot.getDistance() > 15 ist vorn mehr als 15 cm frei? -int abstand = mbot.getDistance() Abstand in einer Variablen merken -``` - -### ➖ Linie {.bot-sensor} - -Unter dem mBot2 sitzen vier Fühler. Sie sehen, ob der Boden schwarz ist. - -``` -mbot.getLine() Zahl: 0 heißt, kein Fühler sieht Schwarz -mbot.getLine() == 0 kein Schwarz, weiterfahren -mbot.getLine() != 0 ein Fühler sieht Schwarz -mbot.isLineLeft() sieht der linke mittlere Fühler Schwarz? -mbot.isLineRight() sieht der rechte mittlere Fühler Schwarz? -``` - -### 🎨 Farben {.bot-sensor} - -Derselbe Sensor unten erkennt auch Farben. `mbot.getColor()` gibt dir den Namen der Farbe als Text: `"rot"`, `"grün"`, `"blau"`, `"gelb"`, `"weiß"` oder `"schwarz"`. - -``` -mbot.getColor().equals("rot") steht er auf Rot? -!mbot.getColor().equals("rot") steht er nicht auf Rot? -``` - -> Texte vergleichst du mit `.equals(…)`, nicht mit `==`. Das `!` davor heißt **nicht**. - -### 🕹️ Tasten und Joystick {.bot-sensor} - -Auf dem CyberPi gibt es die Tasten A und B und einen kleinen Joystick. Jeder Sensor sagt **wahr**, solange gedrückt wird. - -``` -mbot.isButtonA() wird Taste A gedrückt? -mbot.isButtonB() wird Taste B gedrückt? -mbot.isJoystickUp() Joystick nach oben gedrückt? -mbot.isJoystickDown() Joystick nach unten gedrückt? -mbot.isJoystickLeft() Joystick nach links gedrückt? -mbot.isJoystickRight() Joystick nach rechts gedrückt? -``` - -### 🔆 Licht und Lautstärke {.bot-sensor} - -``` -mbot.getBrightness() wie hell? 0 ist dunkel, 100 ist hell -mbot.getLoudness() wie laut? 0 ist still, 100 ist laut -``` - -### 📐 Neigung und Schütteln {.bot-sensor} - -``` -mbot.getPitch() nach vorn oder hinten gekippt? (in Grad) -mbot.getRoll() zur Seite gekippt? (in Grad) -mbot.isShaken() wird er geschüttelt? -``` - -> Liegt der mBot2 flach, sind beide Neigungen 0. In der Simulation stellst du diese Werte unter **🎛 Sensoren ausprobieren** ein. - -### 🔋 Stoppuhr und Akku {.bot-sensor} - -``` -mbot.getTimer() Sekunden seit dem Start oder seit resetTimer() -mbot.getBattery() wie voll ist der Akku? (in Prozent) -``` - -### Echter mBot2 - -Mit **🔌 mBot2** neben „Simulation starten“ verbindest du den echten Roboter mit dem USB-Kabel. Das geht nur in Chrome oder Edge. Dann fährt er jeden Schritt der Simulation mit, und die Sensoren messen echte Werte. Ein zweiter Klick trennt ihn wieder. - -### Ohne Kabel fahren - -Mit **⬆ Übertragen** speicherst du dein Programm fest auf dem mBot2. Danach Kabel ab, mBot2 an den Start und **Taste A** drücken: Er fährt dein Programm allein. Es bleibt gespeichert, bis du ein neues überträgst. Eingaben mit der Tastatur gehen am mBot2 nicht. diff --git a/web/lessons/diagramme/aufgabe-alter-haupt-loesung.visustruct b/web/lessons/diagramme/aufgabe-alter-haupt-loesung.visustruct deleted file mode 100644 index 9d9471d9..00000000 --- a/web/lessons/diagramme/aufgabe-alter-haupt-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;34; - - - 112;114;117;101;102;101;65;108;116;101;114;40;41; - - - 111;117;116;112;117;116;58;32;34;65;117;102;32;87;105;101;100;101;114;115;101;104;101;110;34; - - diff --git a/web/lessons/diagramme/aufgabe-alter-unterprogramm-loesung.visustruct b/web/lessons/diagramme/aufgabe-alter-unterprogramm-loesung.visustruct deleted file mode 100644 index e55b871e..00000000 --- a/web/lessons/diagramme/aufgabe-alter-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;118;111;108;108;106;228;104;114;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;100;101;114;106;228;104;114;105;103;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-ballwurf-loesung.visustruct b/web/lessons/diagramme/aufgabe-ballwurf-loesung.visustruct deleted file mode 100644 index 56a88d26..00000000 --- a/web/lessons/diagramme/aufgabe-ballwurf-loesung.visustruct +++ /dev/null @@ -1,81 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;98;97;108;108;119;117;114;102;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 100;111;117;98;108;101;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;98;101;115;116;101;87;101;105;116;101;32;61;32;48; - - - 83;116;114;105;110;103;32;98;101;115;116;101;114;78;97;109;101;32;61;32;34;34; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;119;101;105;116;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 97;110;122;97;104;108;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;119;101;105;116;101; - - - 119;101;105;116;101;32;62;32;98;101;115;116;101;87;101;105;116;101; - - - 98;101;115;116;101;87;101;105;116;101;32;61;32;119;101;105;116;101; - - - 98;101;115;116;101;114;78;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;115;99;104;110;105;116;116;32;61;32;115;117;109;109;101;32;47;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;84;101;105;108;110;101;104;109;101;114;58;32;34;32;43;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;83;99;104;110;105;116;116;58;32;34;32;43;32;115;99;104;110;105;116;116;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;105;101;103;101;114;46;116;120;116;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;83;105;101;103;101;114;58;32;34;32;43;32;98;101;115;116;101;114;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;101;87;101;105;116;101;32;43;32;34;32;109;41;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/aufgabe-ballwurf-start.visustruct b/web/lessons/diagramme/aufgabe-ballwurf-start.visustruct deleted file mode 100644 index 3b6a805c..00000000 --- a/web/lessons/diagramme/aufgabe-ballwurf-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;98;97;108;108;119;117;114;102;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/aufgabe-bestenliste-loesung.visustruct b/web/lessons/diagramme/aufgabe-bestenliste-loesung.visustruct deleted file mode 100644 index 37326674..00000000 --- a/web/lessons/diagramme/aufgabe-bestenliste-loesung.visustruct +++ /dev/null @@ -1,65 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;80;117;110;107;116;101;63;34; - - - 112;117;110;107;116;101;91;105;93;32;61;32;122;97;104;108; - - - - - 105;110;116;32;100;117;114;99;104;103;97;110;103;32;61;32;49; - 100;117;114;99;104;103;97;110;103;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 100;117;114;99;104;103;97;110;103;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104;32;45;32;100;117;114;99;104;103;97;110;103; - 105;43;43; - - - 112;117;110;107;116;101;91;105;93;32;60;32;112;117;110;107;116;101;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;112;117;110;107;116;101;91;105;93; - - - 112;117;110;107;116;101;91;105;93;32;61;32;112;117;110;107;116;101;91;105;32;43;32;49;93; - - - 112;117;110;107;116;101;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;112;108;97;116;122;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;112;108;97;116;122;32;43;32;34;46;32;34;32;43;32;112;117;110;107;116;101;91;105;93; - - - - diff --git a/web/lessons/diagramme/aufgabe-binaer-loesung.visustruct b/web/lessons/diagramme/aufgabe-binaer-loesung.visustruct deleted file mode 100644 index 02784eef..00000000 --- a/web/lessons/diagramme/aufgabe-binaer-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;71;97;110;122;101;32;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;32;98;105;110;97;101;114;32;61;32;34;34; - - - 122;97;104;108;32;62;32;48; - - - 105;110;116;32;114;101;115;116;32;61;32;122;97;104;108;32;37;32;50; - - - 98;105;110;97;101;114;32;61;32;114;101;115;116;32;43;32;98;105;110;97;101;114; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;101;114; - - diff --git a/web/lessons/diagramme/aufgabe-caesar-array-loesung.visustruct b/web/lessons/diagramme/aufgabe-caesar-array-loesung.visustruct deleted file mode 100644 index 7917ec47..00000000 --- a/web/lessons/diagramme/aufgabe-caesar-array-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;101;108;99;104;101;115;32;87;111;114;116;32;40;110;117;114;32;71;114;111;223;98;117;99;104;115;116;97;98;101;110;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;103;101;104;101;105;109;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;112;104;97;98;101;116;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;112;104;97;98;101;116;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 103;101;104;101;105;109;32;61;32;103;101;104;101;105;109;32;43;32;97;108;112;104;97;98;101;116;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;103;101;104;101;105;109; - - diff --git a/web/lessons/diagramme/aufgabe-caesar-array-start.visustruct b/web/lessons/diagramme/aufgabe-caesar-array-start.visustruct deleted file mode 100644 index 4de2d6ca..00000000 --- a/web/lessons/diagramme/aufgabe-caesar-array-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - diff --git a/web/lessons/diagramme/aufgabe-caesar-ascii-loesung.visustruct b/web/lessons/diagramme/aufgabe-caesar-ascii-loesung.visustruct deleted file mode 100644 index 62bfacda..00000000 --- a/web/lessons/diagramme/aufgabe-caesar-ascii-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;101;108;99;104;101;115;32;87;111;114;116;32;40;110;117;114;32;71;114;111;223;98;117;99;104;115;116;97;98;101;110;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;103;101;104;101;105;109;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 103;101;104;101;105;109;32;61;32;103;101;104;101;105;109;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;103;101;104;101;105;109; - - diff --git a/web/lessons/diagramme/aufgabe-caesar-entschluesseln-loesung.visustruct b/web/lessons/diagramme/aufgabe-caesar-entschluesseln-loesung.visustruct deleted file mode 100644 index 4233ee2c..00000000 --- a/web/lessons/diagramme/aufgabe-caesar-entschluesseln-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;103;101;104;101;105;109;32;34;87;101;108;99;104;101;114;32;71;101;104;101;105;109;116;101;120;116;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;107;108;97;114;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;103;101;104;101;105;109;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;103;101;104;101;105;109;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 107;108;97;114;116;101;120;116;32;61;32;107;108;97;114;116;101;120;116;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;107;108;97;114;116;101;120;116; - - diff --git a/web/lessons/diagramme/aufgabe-collatz-loesung.visustruct b/web/lessons/diagramme/aufgabe-collatz-loesung.visustruct deleted file mode 100644 index ba7fd99d..00000000 --- a/web/lessons/diagramme/aufgabe-collatz-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 122;97;104;108;32;33;61;32;49; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 115;99;104;114;105;116;116;101;43;43; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101; - - diff --git a/web/lessons/diagramme/aufgabe-durchschnitt-loesung.visustruct b/web/lessons/diagramme/aufgabe-durchschnitt-loesung.visustruct deleted file mode 100644 index f11069f1..00000000 --- a/web/lessons/diagramme/aufgabe-durchschnitt-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;78;111;116;101;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;97;110;122;97;104;108; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;32;101;105;110;103;101;98;101;110;34; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;110;111;116;101; - - - - - 100;111;117;98;108;101;32;115;99;104;110;105;116;116;32;61;32;115;117;109;109;101;32;42;32;49;46;48;32;47;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;115;99;104;110;105;116;116; - - diff --git a/web/lessons/diagramme/aufgabe-fibonacci-loesung.visustruct b/web/lessons/diagramme/aufgabe-fibonacci-loesung.visustruct deleted file mode 100644 index 9dcb78e7..00000000 --- a/web/lessons/diagramme/aufgabe-fibonacci-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;90;97;104;108;101;110;63;34; - - - 105;110;116;32;122;97;104;108;49;32;61;32;49; - - - 105;110;116;32;122;97;104;108;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;97;110;122;97;104;108; - 105;43;43; - - - 105;110;116;32;122;97;104;108;51;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108;51; - - - 122;97;104;108;49;32;61;32;122;97;104;108;50; - - - 122;97;104;108;50;32;61;32;122;97;104;108;51; - - - - diff --git a/web/lessons/diagramme/aufgabe-gerade-loesung.visustruct b/web/lessons/diagramme/aufgabe-gerade-loesung.visustruct deleted file mode 100644 index c42d90f3..00000000 --- a/web/lessons/diagramme/aufgabe-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;105;115;116;32;103;101;114;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;105;115;116;32;117;110;103;101;114;97;100;101;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-mehrwertsteuer-loesung.visustruct b/web/lessons/diagramme/aufgabe-mehrwertsteuer-loesung.visustruct deleted file mode 100644 index 6295e6a9..00000000 --- a/web/lessons/diagramme/aufgabe-mehrwertsteuer-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;77;87;83;84;32;61;32;48;46;49;57; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;110;101;116;116;111;112;114;101;105;115;32;34;78;101;116;116;111;112;114;101;105;115;63;34; - - - 100;111;117;98;108;101;32;115;116;101;117;101;114;32;61;32;110;101;116;116;111;112;114;101;105;115;32;42;32;77;87;83;84; - - - 100;111;117;98;108;101;32;98;114;117;116;116;111;112;114;101;105;115;32;61;32;110;101;116;116;111;112;114;101;105;115;32;43;32;115;116;101;117;101;114; - - - 111;117;116;112;117;116;58;32;115;116;101;117;101;114; - - - 111;117;116;112;117;116;58;32;98;114;117;116;116;111;112;114;101;105;115; - - diff --git a/web/lessons/diagramme/aufgabe-pin-java-loesung.visustruct b/web/lessons/diagramme/aufgabe-pin-java-loesung.visustruct deleted file mode 100644 index 894de023..00000000 --- a/web/lessons/diagramme/aufgabe-pin-java-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;118;101;114;115;117;99;104;101;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;105;110;103;97;98;101;32;34;80;73;78;32;101;105;110;103;101;98;101;110;34; - - - 112;105;110;32;61;32;101;105;110;103;97;98;101; - - - 118;101;114;115;117;99;104;101;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;114;105;99;104;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;75;97;114;116;101;32;103;101;115;112;101;114;114;116;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-pin-loesung.visustruct b/web/lessons/diagramme/aufgabe-pin-loesung.visustruct deleted file mode 100644 index 638ecac4..00000000 --- a/web/lessons/diagramme/aufgabe-pin-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;118;101;114;115;117;99;104;101;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;80;73;78;32;101;105;110;103;101;98;101;110;34; - - - 118;101;114;115;117;99;104;101;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;114;105;99;104;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;75;97;114;116;101;32;103;101;115;112;101;114;114;116;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-rechner-loesung.visustruct b/web/lessons/diagramme/aufgabe-rechner-loesung.visustruct deleted file mode 100644 index 08edeb78..00000000 --- a/web/lessons/diagramme/aufgabe-rechner-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;49;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;50;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;122;101;105;99;104;101;110;32;34;82;101;99;104;101;110;122;101;105;99;104;101;110;32;40;43;44;32;45;32;111;100;101;114;32;42;41;34; - - - 122;101;105;99;104;101;110; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;45;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;42;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 111;117;116;112;117;116;58;32;34;85;110;98;101;107;97;110;110;116;101;115;32;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-schiffe-loesung.visustruct b/web/lessons/diagramme/aufgabe-schiffe-loesung.visustruct deleted file mode 100644 index deb1fd61..00000000 --- a/web/lessons/diagramme/aufgabe-schiffe-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;101;101;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;101;101;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;115;112;97;108;116;101;110;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;109;101;101;114;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 105;110;116;32;115;112;97;108;116;101;32;61;32;48; - 115;112;97;108;116;101;32;60;32;109;101;101;114;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 115;112;97;108;116;101;43;43; - - - 109;101;101;114;91;114;101;105;104;101;93;91;115;112;97;108;116;101;93; - - - 111;117;116;112;117;116;58;32;115;112;97;108;116;101;110;91;115;112;97;108;116;101;93;32;43;32;40;114;101;105;104;101;32;43;32;49;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/aufgabe-schiffe-start.visustruct b/web/lessons/diagramme/aufgabe-schiffe-start.visustruct deleted file mode 100644 index db6d3c2d..00000000 --- a/web/lessons/diagramme/aufgabe-schiffe-start.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;101;101;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;101;101;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;115;112;97;108;116;101;110;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - diff --git a/web/lessons/diagramme/aufgabe-speisekarte-loesung.visustruct b/web/lessons/diagramme/aufgabe-speisekarte-loesung.visustruct deleted file mode 100644 index 87a4310f..00000000 --- a/web/lessons/diagramme/aufgabe-speisekarte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;109;101;114;32;34;87;101;108;99;104;101;115;32;71;101;114;105;99;104;116;63;32;40;49;44;32;50;32;111;100;101;114;32;51;41;34; - - - 110;117;109;109;101;114; - - - 111;117;116;112;117;116;58;32;34;71;114;105;101;99;104;105;115;99;104;101;114;32;83;97;108;97;116;34; - - - - - 111;117;116;112;117;116;58;32;34;82;101;105;115;45;66;111;119;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;97;102;101;108;45;87;114;97;112;34; - - - - - 111;117;116;112;117;116;58;32;34;66;105;116;116;101;32;101;105;110;101;32;103;252;108;116;105;103;101;32;78;117;109;109;101;114;32;119;228;104;108;101;110;34; - - - - diff --git a/web/lessons/diagramme/aufgabe-summe-loesung.visustruct b/web/lessons/diagramme/aufgabe-summe-loesung.visustruct deleted file mode 100644 index 9a6eb376..00000000 --- a/web/lessons/diagramme/aufgabe-summe-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;49;32;61;32;52; - - - 105;110;116;32;122;97;104;108;50;32;61;32;51; - - - 105;110;116;32;115;117;109;109;101;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/aufgabe-tafeldienst-loesung.visustruct b/web/lessons/diagramme/aufgabe-tafeldienst-loesung.visustruct deleted file mode 100644 index ef844b15..00000000 --- a/web/lessons/diagramme/aufgabe-tafeldienst-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;110;91;105;93;32;34;78;97;109;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;109;101;114;32;34;76;111;115;110;117;109;109;101;114;32;40;49;32;98;105;115;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;84;97;102;101;108;100;105;101;110;115;116;58;32;34;32;43;32;110;97;109;101;110;91;110;117;109;109;101;114;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/aufgabe-verdoppeln-loesung.visustruct b/web/lessons/diagramme/aufgabe-verdoppeln-loesung.visustruct deleted file mode 100644 index be4e5a84..00000000 --- a/web/lessons/diagramme/aufgabe-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;86;101;114;100;111;112;112;101;108;116;101;32;90;97;104;108;58;32;34;32;43;32;122;97;104;108; - - diff --git a/web/lessons/diagramme/aufgabe-verleih-java-loesung.visustruct b/web/lessons/diagramme/aufgabe-verleih-java-loesung.visustruct deleted file mode 100644 index 65852aa0..00000000 --- a/web/lessons/diagramme/aufgabe-verleih-java-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;61;32;69;45;66;105;107;101;44;32;50;32;61;32;69;45;83;99;111;111;116;101;114;44;32;48;32;61;32;69;110;100;101;34; - - - 119;97;104;108;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;84;115;99;104;252;115;115;34; - - - -1; - - - - - 119;97;104;108;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;69;45;66;105;107;101;32;103;101;98;117;99;104;116;34; - - - - - 119;97;104;108;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;69;45;83;99;111;111;116;101;114;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;85;110;103;252;108;116;105;103;101;32;65;117;115;119;97;104;108;34; - - - - - - - - - - diff --git a/web/lessons/diagramme/aufgabe-verleih-loesung.visustruct b/web/lessons/diagramme/aufgabe-verleih-loesung.visustruct deleted file mode 100644 index 2af6fe0b..00000000 --- a/web/lessons/diagramme/aufgabe-verleih-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;61;32;69;45;66;105;107;101;44;32;50;32;61;32;69;45;83;99;111;111;116;101;114;44;32;48;32;61;32;69;110;100;101;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;34;84;115;99;104;252;115;115;34; - - - 77;101;110;252;32;118;101;114;108;97;115;115;101;110; - - - - - 111;117;116;112;117;116;58;32;34;69;45;66;105;107;101;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;69;45;83;99;111;111;116;101;114;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;85;110;103;252;108;116;105;103;101;32;65;117;115;119;97;104;108;34; - - - - - - diff --git a/web/lessons/diagramme/aufgabe-vokabeln-loesung.visustruct b/web/lessons/diagramme/aufgabe-vokabeln-loesung.visustruct deleted file mode 100644 index fa79ca01..00000000 --- a/web/lessons/diagramme/aufgabe-vokabeln-loesung.visustruct +++ /dev/null @@ -1,60 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;107;97;98;101;108;110;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;114;105;99;104;116;105;103;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;87;97;115;32;104;101;105;223;116;32;34;32;43;32;116;101;105;108;101;91;48;93;32;43;32;34;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;110;116;119;111;114;116; - - - 97;110;122;97;104;108;43;43; - - - 97;110;116;119;111;114;116;46;101;113;117;97;108;115;40;116;101;105;108;101;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;82;105;99;104;116;105;103;33;34; - - - 114;105;99;104;116;105;103;43;43; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;115;99;104;44;32;114;105;99;104;116;105;103;32;105;115;116;32;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;46;34; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;114;105;99;104;116;105;103;32;43;32;34;32;118;111;110;32;34;32;43;32;97;110;122;97;104;108;32;43;32;34;32;114;105;99;104;116;105;103;34; - - diff --git a/web/lessons/diagramme/aufgabe-vokabeln-start.visustruct b/web/lessons/diagramme/aufgabe-vokabeln-start.visustruct deleted file mode 100644 index 8b730fcc..00000000 --- a/web/lessons/diagramme/aufgabe-vokabeln-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;107;97;98;101;108;110;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/aufgabe-volljaehrig-loesung.visustruct b/web/lessons/diagramme/aufgabe-volljaehrig-loesung.visustruct deleted file mode 100644 index 8a0aed43..00000000 --- a/web/lessons/diagramme/aufgabe-volljaehrig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;118;111;108;108;106;228;104;114;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;100;101;114;106;228;104;114;105;103;34; - - - - diff --git a/web/lessons/diagramme/dt-buffered.visustruct b/web/lessons/diagramme/dt-buffered.visustruct deleted file mode 100644 index d5e5a97c..00000000 --- a/web/lessons/diagramme/dt-buffered.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/dt-csv.visustruct b/web/lessons/diagramme/dt-csv.visustruct deleted file mode 100644 index 8516e71d..00000000 --- a/web/lessons/diagramme/dt-csv.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - 105;110;116;32;109;97;116;104;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;104;97;116;32;105;110;32;77;97;116;104;101;32;101;105;110;101;32;34;32;43;32;109;97;116;104;101; - - diff --git a/web/lessons/diagramme/dt-json.visustruct b/web/lessons/diagramme/dt-json.visustruct deleted file mode 100644 index 857bd493..00000000 --- a/web/lessons/diagramme/dt-json.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;123;92;34;110;97;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;97;114;116;92;34;58;32;92;34;90;105;101;103;101;92;34;44;32;92;34;97;108;116;101;114;92;34;58;32;54;125;44;34; - - - 105;110;116;32;115;116;97;114;116;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;101;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;122;101;105;108;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;101;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101; - - diff --git a/web/lessons/diagramme/dt-lesen.visustruct b/web/lessons/diagramme/dt-lesen.visustruct deleted file mode 100644 index d4c59b35..00000000 --- a/web/lessons/diagramme/dt-lesen.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/aufgabe-alter-haupt-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-alter-haupt-loesung.visustruct deleted file mode 100644 index 31e46f70..00000000 --- a/web/lessons/diagramme/en/aufgabe-alter-haupt-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;34; - - - 99;104;101;99;107;65;103;101;40;41; - - - 111;117;116;112;117;116;58;32;34;71;111;111;100;98;121;101;34; - - diff --git a/web/lessons/diagramme/en/aufgabe-alter-unterprogramm-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-alter-unterprogramm-loesung.visustruct deleted file mode 100644 index 7c2f1f90..00000000 --- a/web/lessons/diagramme/en/aufgabe-alter-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;97;100;117;108;116;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;111;114;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-ballwurf-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-ballwurf-loesung.visustruct deleted file mode 100644 index c81a418e..00000000 --- a/web/lessons/diagramme/en/aufgabe-ballwurf-loesung.visustruct +++ /dev/null @@ -1,81 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;104;114;111;119;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 100;111;117;98;108;101;32;115;117;109;32;61;32;48; - - - 105;110;116;32;98;101;115;116;68;105;115;116;97;110;99;101;32;61;32;48; - - - 83;116;114;105;110;103;32;98;101;115;116;78;97;109;101;32;61;32;34;34; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 99;111;117;110;116;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;100;105;115;116;97;110;99;101; - - - 100;105;115;116;97;110;99;101;32;62;32;98;101;115;116;68;105;115;116;97;110;99;101; - - - 98;101;115;116;68;105;115;116;97;110;99;101;32;61;32;100;105;115;116;97;110;99;101; - - - 98;101;115;116;78;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;99;105;112;97;110;116;115;58;32;34;32;43;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;119;105;110;110;101;114;46;116;120;116;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;87;105;110;110;101;114;58;32;34;32;43;32;98;101;115;116;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;68;105;115;116;97;110;99;101;32;43;32;34;32;109;41;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/aufgabe-ballwurf-start.visustruct b/web/lessons/diagramme/en/aufgabe-ballwurf-start.visustruct deleted file mode 100644 index 77de888b..00000000 --- a/web/lessons/diagramme/en/aufgabe-ballwurf-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;104;114;111;119;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/aufgabe-bestenliste-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-bestenliste-loesung.visustruct deleted file mode 100644 index cdf49686..00000000 --- a/web/lessons/diagramme/en/aufgabe-bestenliste-loesung.visustruct +++ /dev/null @@ -1,65 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;80;111;105;110;116;115;63;34; - - - 112;111;105;110;116;115;91;105;93;32;61;32;110;117;109;98;101;114; - - - - - 105;110;116;32;114;117;110;32;61;32;49; - 114;117;110;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 114;117;110;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104;32;45;32;114;117;110; - 105;43;43; - - - 112;111;105;110;116;115;91;105;93;32;60;32;112;111;105;110;116;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;112;111;105;110;116;115;91;105;93; - - - 112;111;105;110;116;115;91;105;93;32;61;32;112;111;105;110;116;115;91;105;32;43;32;49;93; - - - 112;111;105;110;116;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;112;108;97;99;101;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;112;108;97;99;101;32;43;32;34;46;32;34;32;43;32;112;111;105;110;116;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-binaer-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-binaer-loesung.visustruct deleted file mode 100644 index 42958038..00000000 --- a/web/lessons/diagramme/en/aufgabe-binaer-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;119;104;111;108;101;32;110;117;109;98;101;114;34; - - - 83;116;114;105;110;103;32;98;105;110;97;114;121;32;61;32;34;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;110;117;109;98;101;114;32;37;32;50; - - - 98;105;110;97;114;121;32;61;32;114;101;109;97;105;110;100;101;114;32;43;32;98;105;110;97;114;121; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;114;121; - - diff --git a/web/lessons/diagramme/en/aufgabe-caesar-array-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-caesar-array-loesung.visustruct deleted file mode 100644 index 94eae79f..00000000 --- a/web/lessons/diagramme/en/aufgabe-caesar-array-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;87;104;105;99;104;32;119;111;114;100;32;40;99;97;112;105;116;97;108;32;108;101;116;116;101;114;115;32;111;110;108;121;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;99;114;101;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;112;104;97;98;101;116;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;112;104;97;98;101;116;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 115;101;99;114;101;116;32;61;32;115;101;99;114;101;116;32;43;32;97;108;112;104;97;98;101;116;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;115;101;99;114;101;116; - - diff --git a/web/lessons/diagramme/en/aufgabe-caesar-array-start.visustruct b/web/lessons/diagramme/en/aufgabe-caesar-array-start.visustruct deleted file mode 100644 index 4de2d6ca..00000000 --- a/web/lessons/diagramme/en/aufgabe-caesar-array-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - diff --git a/web/lessons/diagramme/en/aufgabe-caesar-ascii-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-caesar-ascii-loesung.visustruct deleted file mode 100644 index 636d4cea..00000000 --- a/web/lessons/diagramme/en/aufgabe-caesar-ascii-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;87;104;105;99;104;32;119;111;114;100;32;40;99;97;112;105;116;97;108;32;108;101;116;116;101;114;115;32;111;110;108;121;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;99;114;101;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 115;101;99;114;101;116;32;61;32;115;101;99;114;101;116;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;115;101;99;114;101;116; - - diff --git a/web/lessons/diagramme/en/aufgabe-caesar-entschluesseln-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-caesar-entschluesseln-loesung.visustruct deleted file mode 100644 index a351d25c..00000000 --- a/web/lessons/diagramme/en/aufgabe-caesar-entschluesseln-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;99;114;101;116;32;34;87;104;105;99;104;32;115;101;99;114;101;116;32;116;101;120;116;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;112;108;97;105;110;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;99;114;101;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;115;101;99;114;101;116;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 112;108;97;105;110;32;61;32;112;108;97;105;110;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;112;108;97;105;110; - - diff --git a/web/lessons/diagramme/en/aufgabe-collatz-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-collatz-loesung.visustruct deleted file mode 100644 index 2979ede0..00000000 --- a/web/lessons/diagramme/en/aufgabe-collatz-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;115;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 110;117;109;98;101;114;32;33;61;32;49; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 115;116;101;112;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115; - - diff --git a/web/lessons/diagramme/en/aufgabe-durchschnitt-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-durchschnitt-loesung.visustruct deleted file mode 100644 index b0eed491..00000000 --- a/web/lessons/diagramme/en/aufgabe-durchschnitt-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;103;114;97;100;101;115;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;111;117;110;116; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;69;110;116;101;114;32;97;32;103;114;97;100;101;34; - - - 115;117;109;32;61;32;115;117;109;32;43;32;103;114;97;100;101; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;42;32;49;46;48;32;47;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;97;118;101;114;97;103;101; - - diff --git a/web/lessons/diagramme/en/aufgabe-fibonacci-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-fibonacci-loesung.visustruct deleted file mode 100644 index b17fca56..00000000 --- a/web/lessons/diagramme/en/aufgabe-fibonacci-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;110;117;109;98;101;114;115;63;34; - - - 105;110;116;32;110;117;109;98;101;114;49;32;61;32;49; - - - 105;110;116;32;110;117;109;98;101;114;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;99;111;117;110;116; - 105;43;43; - - - 105;110;116;32;110;117;109;98;101;114;51;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;51; - - - 110;117;109;98;101;114;49;32;61;32;110;117;109;98;101;114;50; - - - 110;117;109;98;101;114;50;32;61;32;110;117;109;98;101;114;51; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-gerade-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-gerade-loesung.visustruct deleted file mode 100644 index 63aaa2ca..00000000 --- a/web/lessons/diagramme/en/aufgabe-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;105;115;32;101;118;101;110;34; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;105;115;32;111;100;100;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-mehrwertsteuer-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-mehrwertsteuer-loesung.visustruct deleted file mode 100644 index 9c302d7d..00000000 --- a/web/lessons/diagramme/en/aufgabe-mehrwertsteuer-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;86;65;84;32;61;32;48;46;49;57; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;110;101;116;80;114;105;99;101;32;34;78;101;116;32;112;114;105;99;101;63;34; - - - 100;111;117;98;108;101;32;116;97;120;32;61;32;110;101;116;80;114;105;99;101;32;42;32;86;65;84; - - - 100;111;117;98;108;101;32;103;114;111;115;115;80;114;105;99;101;32;61;32;110;101;116;80;114;105;99;101;32;43;32;116;97;120; - - - 111;117;116;112;117;116;58;32;116;97;120; - - - 111;117;116;112;117;116;58;32;103;114;111;115;115;80;114;105;99;101; - - diff --git a/web/lessons/diagramme/en/aufgabe-pin-java-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-pin-java-loesung.visustruct deleted file mode 100644 index dc2d28f7..00000000 --- a/web/lessons/diagramme/en/aufgabe-pin-java-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;97;116;116;101;109;112;116;115;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;97;116;116;101;109;112;116;115;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;110;116;101;114;101;100;32;34;69;110;116;101;114;32;80;73;78;34; - - - 112;105;110;32;61;32;101;110;116;101;114;101;100; - - - 97;116;116;101;109;112;116;115;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;99;116;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;100;32;98;108;111;99;107;101;100;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-pin-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-pin-loesung.visustruct deleted file mode 100644 index 05ef36ec..00000000 --- a/web/lessons/diagramme/en/aufgabe-pin-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;97;116;116;101;109;112;116;115;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;97;116;116;101;109;112;116;115;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;69;110;116;101;114;32;80;73;78;34; - - - 97;116;116;101;109;112;116;115;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;99;116;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;100;32;98;108;111;99;107;101;100;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-rechner-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-rechner-loesung.visustruct deleted file mode 100644 index c6f6dec5..00000000 --- a/web/lessons/diagramme/en/aufgabe-rechner-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;49;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;50;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;116;111;114;32;34;79;112;101;114;97;116;111;114;32;40;43;44;32;45;32;111;114;32;42;41;34; - - - 111;112;101;114;97;116;111;114; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;45;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;42;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 111;117;116;112;117;116;58;32;34;85;110;107;110;111;119;110;32;111;112;101;114;97;116;111;114;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-schiffe-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-schiffe-loesung.visustruct deleted file mode 100644 index a1a40b7f..00000000 --- a/web/lessons/diagramme/en/aufgabe-schiffe-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 115;101;97;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 115;101;97;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;109;110;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 105;110;116;32;99;111;108;117;109;110;32;61;32;48; - 99;111;108;117;109;110;32;60;32;115;101;97;91;114;111;119;93;46;108;101;110;103;116;104; - 99;111;108;117;109;110;43;43; - - - 115;101;97;91;114;111;119;93;91;99;111;108;117;109;110;93; - - - 111;117;116;112;117;116;58;32;99;111;108;117;109;110;115;91;99;111;108;117;109;110;93;32;43;32;40;114;111;119;32;43;32;49;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/en/aufgabe-schiffe-start.visustruct b/web/lessons/diagramme/en/aufgabe-schiffe-start.visustruct deleted file mode 100644 index d7fedb33..00000000 --- a/web/lessons/diagramme/en/aufgabe-schiffe-start.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 115;101;97;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 115;101;97;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;109;110;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - diff --git a/web/lessons/diagramme/en/aufgabe-speisekarte-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-speisekarte-loesung.visustruct deleted file mode 100644 index cb639931..00000000 --- a/web/lessons/diagramme/en/aufgabe-speisekarte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;112;105;99;107;32;34;87;104;105;99;104;32;100;105;115;104;63;32;40;49;44;32;50;32;111;114;32;51;41;34; - - - 112;105;99;107; - - - 111;117;116;112;117;116;58;32;34;71;114;101;101;107;32;115;97;108;97;100;34; - - - - - 111;117;116;112;117;116;58;32;34;82;105;99;101;32;98;111;119;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;97;102;101;108;32;119;114;97;112;34; - - - - - 111;117;116;112;117;116;58;32;34;80;108;101;97;115;101;32;99;104;111;111;115;101;32;97;32;118;97;108;105;100;32;110;117;109;98;101;114;34; - - - - diff --git a/web/lessons/diagramme/en/aufgabe-summe-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-summe-loesung.visustruct deleted file mode 100644 index 6096ca91..00000000 --- a/web/lessons/diagramme/en/aufgabe-summe-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;49;32;61;32;52; - - - 105;110;116;32;110;117;109;98;101;114;50;32;61;32;51; - - - 105;110;116;32;115;117;109;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/aufgabe-tafeldienst-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-tafeldienst-loesung.visustruct deleted file mode 100644 index 396c9c11..00000000 --- a/web/lessons/diagramme/en/aufgabe-tafeldienst-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;97;109;101;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;112;105;99;107;32;34;68;114;97;119;32;110;117;109;98;101;114;32;40;49;32;116;111;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;66;111;97;114;100;32;100;117;116;121;58;32;34;32;43;32;110;97;109;101;115;91;112;105;99;107;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/en/aufgabe-verdoppeln-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-verdoppeln-loesung.visustruct deleted file mode 100644 index c07822b9..00000000 --- a/web/lessons/diagramme/en/aufgabe-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;68;111;117;98;108;101;100;32;110;117;109;98;101;114;58;32;34;32;43;32;110;117;109;98;101;114; - - diff --git a/web/lessons/diagramme/en/aufgabe-verleih-java-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-verleih-java-loesung.visustruct deleted file mode 100644 index 01fc108c..00000000 --- a/web/lessons/diagramme/en/aufgabe-verleih-java-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;61;32;101;45;98;105;107;101;44;32;50;32;61;32;101;45;115;99;111;111;116;101;114;44;32;48;32;61;32;113;117;105;116;34; - - - 99;104;111;105;99;101;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;66;121;101;34; - - - -1; - - - - - 99;104;111;105;99;101;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;69;45;98;105;107;101;32;98;111;111;107;101;100;34; - - - - - 99;104;111;105;99;101;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;69;45;115;99;111;111;116;101;114;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;73;110;118;97;108;105;100;32;99;104;111;105;99;101;34; - - - - - - - - - - diff --git a/web/lessons/diagramme/en/aufgabe-verleih-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-verleih-loesung.visustruct deleted file mode 100644 index fd944af2..00000000 --- a/web/lessons/diagramme/en/aufgabe-verleih-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;61;32;101;45;98;105;107;101;44;32;50;32;61;32;101;45;115;99;111;111;116;101;114;44;32;48;32;61;32;113;117;105;116;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;34;66;121;101;34; - - - 76;101;97;118;101;32;116;104;101;32;109;101;110;117; - - - - - 111;117;116;112;117;116;58;32;34;69;45;98;105;107;101;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;69;45;115;99;111;111;116;101;114;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;73;110;118;97;108;105;100;32;99;104;111;105;99;101;34; - - - - - - diff --git a/web/lessons/diagramme/en/aufgabe-vokabeln-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-vokabeln-loesung.visustruct deleted file mode 100644 index 6bd86b9d..00000000 --- a/web/lessons/diagramme/en/aufgabe-vokabeln-loesung.visustruct +++ /dev/null @@ -1,60 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;119;111;114;100;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;114;105;103;104;116;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;87;104;97;116;32;105;115;32;34;32;43;32;112;97;114;116;115;91;48;93;32;43;32;34;32;105;110;32;71;101;114;109;97;110;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;110;115;119;101;114; - - - 99;111;117;110;116;43;43; - - - 97;110;115;119;101;114;46;101;113;117;97;108;115;40;112;97;114;116;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;33;34; - - - 114;105;103;104;116;43;43; - - - - - 111;117;116;112;117;116;58;32;34;87;114;111;110;103;44;32;105;116;32;105;115;32;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;46;34; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;114;105;103;104;116;32;43;32;34;32;111;102;32;34;32;43;32;99;111;117;110;116;32;43;32;34;32;114;105;103;104;116;34; - - diff --git a/web/lessons/diagramme/en/aufgabe-vokabeln-start.visustruct b/web/lessons/diagramme/en/aufgabe-vokabeln-start.visustruct deleted file mode 100644 index e39c9c06..00000000 --- a/web/lessons/diagramme/en/aufgabe-vokabeln-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;119;111;114;100;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/aufgabe-volljaehrig-loesung.visustruct b/web/lessons/diagramme/en/aufgabe-volljaehrig-loesung.visustruct deleted file mode 100644 index 7456f320..00000000 --- a/web/lessons/diagramme/en/aufgabe-volljaehrig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;97;100;117;108;116;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;111;114;34; - - - - diff --git a/web/lessons/diagramme/en/dt-buffered.visustruct b/web/lessons/diagramme/en/dt-buffered.visustruct deleted file mode 100644 index f429b106..00000000 --- a/web/lessons/diagramme/en/dt-buffered.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/dt-csv.visustruct b/web/lessons/diagramme/en/dt-csv.visustruct deleted file mode 100644 index 7307e33a..00000000 --- a/web/lessons/diagramme/en/dt-csv.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - 105;110;116;32;109;97;116;104;115;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;103;111;116;32;97;32;34;32;43;32;109;97;116;104;115;32;43;32;34;32;105;110;32;109;97;116;104;115;34; - - diff --git a/web/lessons/diagramme/en/dt-json.visustruct b/web/lessons/diagramme/en/dt-json.visustruct deleted file mode 100644 index 9ce6fa1f..00000000 --- a/web/lessons/diagramme/en/dt-json.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;123;92;34;110;97;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;115;112;101;99;105;101;115;92;34;58;32;92;34;103;111;97;116;92;34;44;32;92;34;97;103;101;92;34;58;32;54;125;44;34; - - - 105;110;116;32;115;116;97;114;116;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;108;105;110;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101; - - diff --git a/web/lessons/diagramme/en/dt-lesen.visustruct b/web/lessons/diagramme/en/dt-lesen.visustruct deleted file mode 100644 index 77040d10..00000000 --- a/web/lessons/diagramme/en/dt-lesen.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/kino-einmaleins-loesung.visustruct b/web/lessons/diagramme/en/kino-einmaleins-loesung.visustruct deleted file mode 100644 index 5a4b5479..00000000 --- a/web/lessons/diagramme/en/kino-einmaleins-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;116;97;98;108;101;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;108;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;108;101;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;32;40;49;32;116;111;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;32;40;49;32;116;111;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;108;101;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/en/kino-frei-loesung.visustruct b/web/lessons/diagramme/en/kino-frei-loesung.visustruct deleted file mode 100644 index fb714da3..00000000 --- a/web/lessons/diagramme/en/kino-frei-loesung.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;114;101;101;32;61;32;48; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 105;110;116;32;115;101;97;116;32;61;32;48; - 115;101;97;116;32;60;32;115;101;97;116;115;91;114;111;119;93;46;108;101;110;103;116;104; - 115;101;97;116;43;43; - - - 33;115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 102;114;101;101;32;61;32;102;114;101;101;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;58;32;34;32;43;32;102;114;101;101; - - diff --git a/web/lessons/diagramme/en/kino-frei-start.visustruct b/web/lessons/diagramme/en/kino-frei-start.visustruct deleted file mode 100644 index 8cb1dbd7..00000000 --- a/web/lessons/diagramme/en/kino-frei-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - diff --git a/web/lessons/diagramme/en/kino-karte-loesung.visustruct b/web/lessons/diagramme/en/kino-karte-loesung.visustruct deleted file mode 100644 index c233b2fa..00000000 --- a/web/lessons/diagramme/en/kino-karte-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;82;111;119;34; - - - 105;110;112;117;116;58;32;105;110;116;32;115;101;97;116;32;34;83;101;97;116;34; - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 111;117;116;112;117;116;58;32;34;65;108;114;101;97;100;121;32;115;111;108;100;34; - - - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;83;111;108;100;58;32;114;111;119;32;34;32;43;32;114;111;119;32;43;32;34;44;32;115;101;97;116;32;34;32;43;32;115;101;97;116; - - - - diff --git a/web/lessons/diagramme/en/kino-karte-start.visustruct b/web/lessons/diagramme/en/kino-karte-start.visustruct deleted file mode 100644 index ca7633b6..00000000 --- a/web/lessons/diagramme/en/kino-karte-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;82;111;119;34; - - - 105;110;112;117;116;58;32;105;110;116;32;115;101;97;116;32;34;83;101;97;116;34; - - diff --git a/web/lessons/diagramme/en/kino-saalplan.visustruct b/web/lessons/diagramme/en/kino-saalplan.visustruct deleted file mode 100644 index 04b08949..00000000 --- a/web/lessons/diagramme/en/kino-saalplan.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;34; - - - 105;110;116;32;115;101;97;116;32;61;32;48; - 115;101;97;116;32;60;32;115;101;97;116;115;91;114;111;119;93;46;108;101;110;103;116;104; - 115;101;97;116;43;43; - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;88;34; - - - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - diff --git a/web/lessons/diagramme/en/kino-verkaufen.visustruct b/web/lessons/diagramme/en/kino-verkaufen.visustruct deleted file mode 100644 index db41d7e6..00000000 --- a/web/lessons/diagramme/en/kino-verkaufen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;82;111;119;115;58;32;34;32;43;32;115;101;97;116;115;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;83;101;97;116;115;32;112;101;114;32;114;111;119;58;32;34;32;43;32;115;101;97;116;115;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;82;111;119;32;49;44;32;115;101;97;116;32;50;32;115;111;108;100;63;32;34;32;43;32;115;101;97;116;115;91;49;93;91;50;93; - - diff --git a/web/lessons/diagramme/en/l04-gross-klein-loesung.visustruct b/web/lessons/diagramme/en/l04-gross-klein-loesung.visustruct deleted file mode 100644 index 30fa15a4..00000000 --- a/web/lessons/diagramme/en/l04-gross-klein-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 105;110;116;32;78;117;109;98;101;114;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;78;117;109;98;101;114; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;58;32;34;32;43;32;114;101;115;117;108;116; - - diff --git a/web/lessons/diagramme/en/l04-gross-klein-start.visustruct b/web/lessons/diagramme/en/l04-gross-klein-start.visustruct deleted file mode 100644 index 8dca0580..00000000 --- a/web/lessons/diagramme/en/l04-gross-klein-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 105;110;116;32;78;117;109;98;101;114;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;78;85;77;66;69;82; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;58;32;34;32;43;32;114;101;115;117;108;116; - - diff --git a/web/lessons/diagramme/en/l04-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l04-java-fehler-loesung.visustruct deleted file mode 100644 index aec94c4f..00000000 --- a/web/lessons/diagramme/en/l04-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/en/l04-java-fehler-start.visustruct b/web/lessons/diagramme/en/l04-java-fehler-start.visustruct deleted file mode 100644 index f85f7d6a..00000000 --- a/web/lessons/diagramme/en/l04-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/en/l04-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l04-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 7c5a77ad..00000000 --- a/web/lessons/diagramme/en/l04-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - diff --git a/web/lessons/diagramme/en/l04-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l04-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 452fedd5..00000000 --- a/web/lessons/diagramme/en/l04-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;65;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34;41; - - diff --git a/web/lessons/diagramme/en/l04-java-steckbrief-loesung.visustruct b/web/lessons/diagramme/en/l04-java-steckbrief-loesung.visustruct deleted file mode 100644 index 60dc368e..00000000 --- a/web/lessons/diagramme/en/l04-java-steckbrief-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 100;111;117;98;108;101;32;104;101;105;103;104;116;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/en/l04-rechnen.visustruct b/web/lessons/diagramme/en/l04-rechnen.visustruct deleted file mode 100644 index c12f157a..00000000 --- a/web/lessons/diagramme/en/l04-rechnen.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - diff --git a/web/lessons/diagramme/en/l04-reihenfolge-loesung.visustruct b/web/lessons/diagramme/en/l04-reihenfolge-loesung.visustruct deleted file mode 100644 index 6ed4eb6a..00000000 --- a/web/lessons/diagramme/en/l04-reihenfolge-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;51; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;52; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - diff --git a/web/lessons/diagramme/en/l04-reihenfolge-start.visustruct b/web/lessons/diagramme/en/l04-reihenfolge-start.visustruct deleted file mode 100644 index 9ccd6139..00000000 --- a/web/lessons/diagramme/en/l04-reihenfolge-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;52; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;51; - - diff --git a/web/lessons/diagramme/en/l04-tauschen-loesung.visustruct b/web/lessons/diagramme/en/l04-tauschen-loesung.visustruct deleted file mode 100644 index 7ed3afd9..00000000 --- a/web/lessons/diagramme/en/l04-tauschen-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;101;102;116;32;61;32;34;106;117;105;99;101;34; - - - 83;116;114;105;110;103;32;114;105;103;104;116;32;61;32;34;119;97;116;101;114;34; - - - 83;116;114;105;110;103;32;116;101;109;112;32;61;32;108;101;102;116; - - - 108;101;102;116;32;61;32;114;105;103;104;116; - - - 114;105;103;104;116;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;108;101;102;116; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;58;32;34;32;43;32;114;105;103;104;116; - - diff --git a/web/lessons/diagramme/en/l04-tauschen-start.visustruct b/web/lessons/diagramme/en/l04-tauschen-start.visustruct deleted file mode 100644 index 9804141c..00000000 --- a/web/lessons/diagramme/en/l04-tauschen-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;101;102;116;32;61;32;34;106;117;105;99;101;34; - - - 83;116;114;105;110;103;32;114;105;103;104;116;32;61;32;34;119;97;116;101;114;34; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;108;101;102;116; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;58;32;34;32;43;32;114;105;103;104;116; - - diff --git a/web/lessons/diagramme/en/l04-tauschen.visustruct b/web/lessons/diagramme/en/l04-tauschen.visustruct deleted file mode 100644 index 43bbf624..00000000 --- a/web/lessons/diagramme/en/l04-tauschen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/en/l05-datentypen.visustruct b/web/lessons/diagramme/en/l05-datentypen.visustruct deleted file mode 100644 index ebc2eff8..00000000 --- a/web/lessons/diagramme/en/l05-datentypen.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;97;103;101;32;61;32;49;52; - - - 100;111;117;98;108;101;32;104;101;105;103;104;116;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;77;105;97;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/en/l05-division.visustruct b/web/lessons/diagramme/en/l05-division.visustruct deleted file mode 100644 index 2ee5bda6..00000000 --- a/web/lessons/diagramme/en/l05-division.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;49;51;53; - - - 105;110;116;32;104;111;117;114;115;32;61;32;109;105;110;117;116;101;115;32;47;32;54;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;109;105;110;117;116;101;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;104;111;117;114;115;32;43;32;34;32;104;111;117;114;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;109;105;110;117;116;101;115;34; - - diff --git a/web/lessons/diagramme/en/l05-einkauf-loesung.visustruct b/web/lessons/diagramme/en/l05-einkauf-loesung.visustruct deleted file mode 100644 index 1e9f4026..00000000 --- a/web/lessons/diagramme/en/l05-einkauf-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;105;99;101;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;116;111;116;97;108;80;114;105;99;101;32;61;32;99;111;117;110;116;32;42;32;112;114;105;99;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108;80;114;105;99;101;32;43;32;34;32;101;117;114;111;115;34; - - diff --git a/web/lessons/diagramme/en/l05-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l05-java-fehler-loesung.visustruct deleted file mode 100644 index 93bfc1fc..00000000 --- a/web/lessons/diagramme/en/l05-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;99;114;101;100;105;116;32;61;32;50;48; - - - 105;110;116;32;112;114;105;99;101;32;61;32;55; - - - 99;114;101;100;105;116;32;61;32;99;114;101;100;105;116;32;45;32;112;114;105;99;101; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;99;114;101;100;105;116; - - diff --git a/web/lessons/diagramme/en/l05-java-fehler-start.visustruct b/web/lessons/diagramme/en/l05-java-fehler-start.visustruct deleted file mode 100644 index 7302db48..00000000 --- a/web/lessons/diagramme/en/l05-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;99;114;101;100;105;116;32;61;32;50;48; - - - 105;110;116;32;112;114;105;99;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;99;114;101;100;105;116; - - - 99;114;101;100;105;116;32;61;32;99;114;101;100;105;116;32;45;32;112;114;105;99;101; - - diff --git a/web/lessons/diagramme/en/l05-java-kino-loesung.visustruct b/web/lessons/diagramme/en/l05-java-kino-loesung.visustruct deleted file mode 100644 index 7c17f7c9..00000000 --- a/web/lessons/diagramme/en/l05-java-kino-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;114;105;99;101;32;61;32;57; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;105;99;101;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/l05-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l05-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 10d41a69..00000000 --- a/web/lessons/diagramme/en/l05-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;108;105;99;101;115;32;61;32;56; - - - 105;110;116;32;112;101;111;112;108;101;32;61;32;52; - - - 105;110;116;32;112;101;114;80;101;114;115;111;110;32;61;32;115;108;105;99;101;115;32;47;32;112;101;111;112;108;101; - - - 111;117;116;112;117;116;58;32;34;69;118;101;114;121;111;110;101;32;103;101;116;115;32;34;32;43;32;112;101;114;80;101;114;115;111;110;32;43;32;34;32;115;108;105;99;101;115;46;34; - - diff --git a/web/lessons/diagramme/en/l05-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l05-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 85a73dc7..00000000 --- a/web/lessons/diagramme/en/l05-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;108;105;99;101;115;32;61;32;56; - - - 105;110;116;32;112;101;111;112;108;101;32;61;32;52; - - - 105;110;116;32;112;101;114;80;101;114;115;111;110;32;61;32;115;108;105;99;101;115;32;47;32;112;101;111;112;108;101; - - - 111;117;116;112;117;116;58;32;34;69;118;101;114;121;111;110;101;32;103;101;116;115;32;34;32;43;32;112;101;114;112;101;114;115;111;110;32;43;32;34;32;115;108;105;99;101;115;46;34; - - diff --git a/web/lessons/diagramme/en/l05-rechteck-loesung.visustruct b/web/lessons/diagramme/en/l05-rechteck-loesung.visustruct deleted file mode 100644 index 0a7e5133..00000000 --- a/web/lessons/diagramme/en/l05-rechteck-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;55; - - - 105;110;116;32;119;105;100;116;104;32;61;32;52; - - - 105;110;116;32;97;114;101;97;32;61;32;108;101;110;103;116;104;32;42;32;119;105;100;116;104; - - - 105;110;116;32;112;101;114;105;109;101;116;101;114;32;61;32;50;32;42;32;40;108;101;110;103;116;104;32;43;32;119;105;100;116;104;41; - - - 111;117;116;112;117;116;58;32;34;65;114;101;97;58;32;34;32;43;32;97;114;101;97; - - - 111;117;116;112;117;116;58;32;34;80;101;114;105;109;101;116;101;114;58;32;34;32;43;32;112;101;114;105;109;101;116;101;114; - - diff --git a/web/lessons/diagramme/en/l05-sekunden-loesung.visustruct b/web/lessons/diagramme/en/l05-sekunden-loesung.visustruct deleted file mode 100644 index 938d0a94..00000000 --- a/web/lessons/diagramme/en/l05-sekunden-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;117;116;101;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;115;101;99;111;110;100;115;34; - - diff --git a/web/lessons/diagramme/en/l05-sekunden-start.visustruct b/web/lessons/diagramme/en/l05-sekunden-start.visustruct deleted file mode 100644 index 0bd403b8..00000000 --- a/web/lessons/diagramme/en/l05-sekunden-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;117;116;101;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;115;101;99;111;110;100;115;34; - - diff --git a/web/lessons/diagramme/en/l05-sequenz.visustruct b/web/lessons/diagramme/en/l05-sequenz.visustruct deleted file mode 100644 index b315fd06..00000000 --- a/web/lessons/diagramme/en/l05-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;53; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51; - - - 105;110;116;32;97;114;101;97;32;61;32;108;101;110;103;116;104;32;42;32;119;105;100;116;104; - - - 111;117;116;112;117;116;58;32;97;114;101;97; - - diff --git a/web/lessons/diagramme/en/l06-begruessung-loesung.visustruct b/web/lessons/diagramme/en/l06-begruessung-loesung.visustruct deleted file mode 100644 index d9b1e9cf..00000000 --- a/web/lessons/diagramme/en/l06-begruessung-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;104;97;116;32;105;115;32;121;111;117;114;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;34;32;43;32;110;97;109;101;32;43;32;34;33;34; - - diff --git a/web/lessons/diagramme/en/l06-eva.visustruct b/web/lessons/diagramme/en/l06-eva.visustruct deleted file mode 100644 index f74ce1e2..00000000 --- a/web/lessons/diagramme/en/l06-eva.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l06-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l06-java-fehler-loesung.visustruct deleted file mode 100644 index 61159adb..00000000 --- a/web/lessons/diagramme/en/l06-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;40;97;32;43;32;98;41; - - diff --git a/web/lessons/diagramme/en/l06-java-fehler-start.visustruct b/web/lessons/diagramme/en/l06-java-fehler-start.visustruct deleted file mode 100644 index 53a94d8c..00000000 --- a/web/lessons/diagramme/en/l06-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;97;32;43;32;98; - - diff --git a/web/lessons/diagramme/en/l06-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l06-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index b5f1eb55..00000000 --- a/web/lessons/diagramme/en/l06-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;101;120;116;32;121;101;97;114;32;121;111;117;32;119;105;108;108;32;98;101;32;34;32;43;32;40;97;103;101;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/en/l06-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l06-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 6d6dbef3..00000000 --- a/web/lessons/diagramme/en/l06-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;101;120;116;32;121;101;97;114;32;121;111;117;32;119;105;108;108;32;98;101;32;34;32;43;32;40;65;103;101;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/en/l06-java-tage-loesung.visustruct b/web/lessons/diagramme/en/l06-java-tage-loesung.visustruct deleted file mode 100644 index 9871e49a..00000000 --- a/web/lessons/diagramme/en/l06-java-tage-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;121;101;97;114;115;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 105;110;116;32;100;97;121;115;32;61;32;121;101;97;114;115;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;105;115;32;97;98;111;117;116;32;34;32;43;32;100;97;121;115;32;43;32;34;32;100;97;121;115;46;34; - - diff --git a/web/lessons/diagramme/en/l06-mittelwert-loesung.visustruct b/web/lessons/diagramme/en/l06-mittelwert-loesung.visustruct deleted file mode 100644 index 012401fa..00000000 --- a/web/lessons/diagramme/en/l06-mittelwert-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 100;111;117;98;108;101;32;109;101;97;110;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;101;97;110;58;32;34;32;43;32;109;101;97;110; - - diff --git a/web/lessons/diagramme/en/l06-rechner-loesung.visustruct b/web/lessons/diagramme/en/l06-rechner-loesung.visustruct deleted file mode 100644 index 96bd5f54..00000000 --- a/web/lessons/diagramme/en/l06-rechner-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;99;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;99;116;58;32;34;32;43;32;112;114;111;100;117;99;116; - - diff --git a/web/lessons/diagramme/en/l07-alter-pruefen.visustruct b/web/lessons/diagramme/en/l07-alter-pruefen.visustruct deleted file mode 100644 index edc4d5ad..00000000 --- a/web/lessons/diagramme/en/l07-alter-pruefen.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;97;114;101;32;97;110;32;97;100;117;108;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;97;114;101;32;110;111;116;32;97;110;32;97;100;117;108;116;32;121;101;116;46;34; - - - - diff --git a/web/lessons/diagramme/en/l07-betrag-loesung.visustruct b/web/lessons/diagramme/en/l07-betrag-loesung.visustruct deleted file mode 100644 index a45f87ba..00000000 --- a/web/lessons/diagramme/en/l07-betrag-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;60;32;48; - - - 110;117;109;98;101;114;32;61;32;48;32;45;32;110;117;109;98;101;114; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - diff --git a/web/lessons/diagramme/en/l07-gerade-loesung.visustruct b/web/lessons/diagramme/en/l07-gerade-loesung.visustruct deleted file mode 100644 index 212aad9a..00000000 --- a/web/lessons/diagramme/en/l07-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;101;118;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;111;100;100;34; - - - - diff --git a/web/lessons/diagramme/en/l07-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l07-java-fehler-loesung.visustruct deleted file mode 100644 index c0ed691d..00000000 --- a/web/lessons/diagramme/en/l07-java-fehler-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;53; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - diff --git a/web/lessons/diagramme/en/l07-java-fehler-start.visustruct b/web/lessons/diagramme/en/l07-java-fehler-start.visustruct deleted file mode 100644 index 86a4c589..00000000 --- a/web/lessons/diagramme/en/l07-java-fehler-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;53; - - - 112;111;105;110;116;115;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - diff --git a/web/lessons/diagramme/en/l07-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l07-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index d24a1154..00000000 --- a/web/lessons/diagramme/en/l07-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - diff --git a/web/lessons/diagramme/en/l07-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l07-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 83da3485..00000000 --- a/web/lessons/diagramme/en/l07-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - diff --git a/web/lessons/diagramme/en/l07-java-pizza-loesung.visustruct b/web/lessons/diagramme/en/l07-java-pizza-loesung.visustruct deleted file mode 100644 index c652b5a2..00000000 --- a/web/lessons/diagramme/en/l07-java-pizza-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;105;99;101;32;61;32;56; - - - 105;110;116;32;99;104;101;101;115;101;32;61;32;50; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;40;112;114;105;99;101;32;43;32;99;104;101;101;115;101;41;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/l07-java-pizza-start.visustruct b/web/lessons/diagramme/en/l07-java-pizza-start.visustruct deleted file mode 100644 index ecdbe774..00000000 --- a/web/lessons/diagramme/en/l07-java-pizza-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;105;99;101;32;61;32;56; - - - 105;110;116;32;99;104;101;101;115;101;32;61;32;50; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;105;99;101;32;43;32;99;104;101;101;115;101;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/l07-maximum-loesung.visustruct b/web/lessons/diagramme/en/l07-maximum-loesung.visustruct deleted file mode 100644 index 94c35760..00000000 --- a/web/lessons/diagramme/en/l07-maximum-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - diff --git a/web/lessons/diagramme/en/l07-rabatt.visustruct b/web/lessons/diagramme/en/l07-rabatt.visustruct deleted file mode 100644 index 61f79e1d..00000000 --- a/web/lessons/diagramme/en/l07-rabatt.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;105;99;101;32;34;80;114;105;99;101;32;105;110;32;101;117;114;111;115;63;34; - - - 112;114;105;99;101;32;62;32;53;48; - - - 112;114;105;99;101;32;61;32;112;114;105;99;101;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;84;111;32;112;97;121;58;32;34;32;43;32;112;114;105;99;101;32;43;32;34;32;101;117;114;111;115;34; - - diff --git a/web/lessons/diagramme/en/l08-gueltig-loesung.visustruct b/web/lessons/diagramme/en/l08-gueltig-loesung.visustruct deleted file mode 100644 index cb8fe90c..00000000 --- a/web/lessons/diagramme/en/l08-gueltig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;71;114;97;100;101;63;34; - - - 103;114;97;100;101;32;62;61;32;49;32;38;38;32;103;114;97;100;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;118;97;108;105;100;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;118;97;108;105;100;34; - - - - diff --git a/web/lessons/diagramme/en/l08-java-bestanden-loesung.visustruct b/web/lessons/diagramme/en/l08-java-bestanden-loesung.visustruct deleted file mode 100644 index 642975e7..00000000 --- a/web/lessons/diagramme/en/l08-java-bestanden-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;112;111;105;110;116;115;32;34;72;111;119;32;109;97;110;121;32;112;111;105;110;116;115;63;34; - - - 112;111;105;110;116;115;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;112;97;115;115;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;110;111;116;32;112;97;115;115;101;100;34; - - - - diff --git a/web/lessons/diagramme/en/l08-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l08-java-fehler-loesung.visustruct deleted file mode 100644 index c706fc3d..00000000 --- a/web/lessons/diagramme/en/l08-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - diff --git a/web/lessons/diagramme/en/l08-java-fehler-start.visustruct b/web/lessons/diagramme/en/l08-java-fehler-start.visustruct deleted file mode 100644 index dcdb17ca..00000000 --- a/web/lessons/diagramme/en/l08-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - diff --git a/web/lessons/diagramme/en/l08-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l08-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index c9c94139..00000000 --- a/web/lessons/diagramme/en/l08-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;97;103;101;32;61;32;49;53; - - - 97;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;101;110;116;114;121;34; - - - - diff --git a/web/lessons/diagramme/en/l08-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l08-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 93d3ebc2..00000000 --- a/web/lessons/diagramme/en/l08-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;97;103;101;32;61;32;49;53; - - - 65;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;101;110;116;114;121;34; - - - - diff --git a/web/lessons/diagramme/en/l08-jugendticket.visustruct b/web/lessons/diagramme/en/l08-jugendticket.visustruct deleted file mode 100644 index f89ebbcc..00000000 --- a/web/lessons/diagramme/en/l08-jugendticket.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;50;32;38;38;32;97;103;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;89;111;117;116;104;32;116;105;99;107;101;116;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;121;111;117;116;104;32;116;105;99;107;101;116;34; - - - - diff --git a/web/lessons/diagramme/en/l08-note.visustruct b/web/lessons/diagramme/en/l08-note.visustruct deleted file mode 100644 index 05ea3c05..00000000 --- a/web/lessons/diagramme/en/l08-note.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;71;114;97;100;101;63;34; - - - 103;114;97;100;101;32;60;32;49;32;124;124;32;103;114;97;100;101;32;62;32;54; - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;103;114;97;100;101;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;71;114;97;100;101;32;34;32;43;32;103;114;97;100;101;32;43;32;34;32;105;115;32;118;97;108;105;100;46;34; - - - - diff --git a/web/lessons/diagramme/en/l08-schaltjahr-loesung.visustruct b/web/lessons/diagramme/en/l08-schaltjahr-loesung.visustruct deleted file mode 100644 index 654a6aac..00000000 --- a/web/lessons/diagramme/en/l08-schaltjahr-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;121;101;97;114;32;34;89;101;97;114;63;34; - - - 40;121;101;97;114;32;37;32;52;32;61;61;32;48;32;38;38;32;121;101;97;114;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;121;101;97;114;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;76;101;97;112;32;121;101;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;32;108;101;97;112;32;121;101;97;114;34; - - - - diff --git a/web/lessons/diagramme/en/l08-wochenende-loesung.visustruct b/web/lessons/diagramme/en/l08-wochenende-loesung.visustruct deleted file mode 100644 index 475e2355..00000000 --- a/web/lessons/diagramme/en/l08-wochenende-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;68;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;100;97;121;34; - - - - diff --git a/web/lessons/diagramme/en/l09-ampel.visustruct b/web/lessons/diagramme/en/l09-ampel.visustruct deleted file mode 100644 index 1fdeb21e..00000000 --- a/web/lessons/diagramme/en/l09-ampel.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;99;111;108;111;114;32;34;87;104;97;116;32;99;111;108;111;114;32;105;115;32;116;104;101;32;116;114;97;102;102;105;99;32;108;105;103;104;116;63;34; - - - 99;111;108;111;114; - - - 111;117;116;112;117;116;58;32;34;83;116;111;112;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;105;116;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;108;107;34; - - - - - 111;117;116;112;117;116;58;32;34;73;32;100;111;32;110;111;116;32;107;110;111;119;32;116;104;105;115;32;99;111;108;111;114;46;34; - - - - diff --git a/web/lessons/diagramme/en/l09-automat.visustruct b/web/lessons/diagramme/en/l09-automat.visustruct deleted file mode 100644 index d7d2b9bc..00000000 --- a/web/lessons/diagramme/en/l09-automat.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;119;97;116;101;114;44;32;50;32;106;117;105;99;101;44;32;51;32;99;111;99;111;97;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;119;97;116;101;114;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;106;117;105;99;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;99;111;99;111;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;98;117;116;116;111;110;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - diff --git a/web/lessons/diagramme/en/l09-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l09-java-fehler-loesung.visustruct deleted file mode 100644 index 241a6703..00000000 --- a/web/lessons/diagramme/en/l09-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;87;104;105;99;104;32;100;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - diff --git a/web/lessons/diagramme/en/l09-java-fehler-start.visustruct b/web/lessons/diagramme/en/l09-java-fehler-start.visustruct deleted file mode 100644 index 8bec3a7d..00000000 --- a/web/lessons/diagramme/en/l09-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;87;104;105;99;104;32;100;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;38;38;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - diff --git a/web/lessons/diagramme/en/l09-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l09-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 4b1a2f88..00000000 --- a/web/lessons/diagramme/en/l09-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;100;97;121;32;61;32;55; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - diff --git a/web/lessons/diagramme/en/l09-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l09-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 83a4aaab..00000000 --- a/web/lessons/diagramme/en/l09-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;100;97;121;32;61;32;55; - - - 100;97;121;32;61;61;32;54;32;124;124;32;68;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - diff --git a/web/lessons/diagramme/en/l09-java-schule-loesung.visustruct b/web/lessons/diagramme/en/l09-java-schule-loesung.visustruct deleted file mode 100644 index 4ea562a6..00000000 --- a/web/lessons/diagramme/en/l09-java-schule-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;117;114;32;34;87;104;97;116;32;116;105;109;101;32;105;115;32;105;116;32;40;106;117;115;116;32;116;104;101;32;104;111;117;114;41;63;34; - - - 104;111;117;114;32;62;61;32;56;32;38;38;32;104;111;117;114;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;83;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;116;105;109;101;34; - - - - diff --git a/web/lessons/diagramme/en/l09-note-loesung.visustruct b/web/lessons/diagramme/en/l09-note-loesung.visustruct deleted file mode 100644 index 99a442a5..00000000 --- a/web/lessons/diagramme/en/l09-note-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;87;104;105;99;104;32;103;114;97;100;101;63;34; - - - 103;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;115;97;116;105;115;102;97;99;116;111;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;115;117;102;102;105;99;105;101;110;116;34; - - - - - 111;117;116;112;117;116;58;32;34;112;111;111;114;34; - - - - - 111;117;116;112;117;116;58;32;34;102;97;105;108;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;32;118;97;108;105;100;32;103;114;97;100;101;34; - - - - diff --git a/web/lessons/diagramme/en/l09-rechner-loesung.visustruct b/web/lessons/diagramme/en/l09-rechner-loesung.visustruct deleted file mode 100644 index 796dff90..00000000 --- a/web/lessons/diagramme/en/l09-rechner-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;116;111;114;32;34;79;112;101;114;97;116;111;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;48; - - - 111;112;101;114;97;116;111;114; - - - 114;101;115;117;108;116;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 114;101;115;117;108;116;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 114;101;115;117;108;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 111;117;116;112;117;116;58;32;34;85;110;107;110;111;119;110;32;111;112;101;114;97;116;111;114;34; - - - - diff --git a/web/lessons/diagramme/en/l09-verschachtelt.visustruct b/web/lessons/diagramme/en/l09-verschachtelt.visustruct deleted file mode 100644 index ae78ad18..00000000 --- a/web/lessons/diagramme/en/l09-verschachtelt.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;119;97;116;101;114;44;32;50;32;106;117;105;99;101;44;32;51;32;99;111;99;111;97;34; - - - 99;104;111;105;99;101;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;119;97;116;101;114;46;34; - - - - - 99;104;111;105;99;101;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;106;117;105;99;101;46;34; - - - - - 99;104;111;105;99;101;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;99;111;99;111;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;98;117;116;116;111;110;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - - - - - diff --git a/web/lessons/diagramme/en/l1-grundbausteine.visustruct b/web/lessons/diagramme/en/l1-grundbausteine.visustruct deleted file mode 100644 index c53c4f4a..00000000 --- a/web/lessons/diagramme/en/l1-grundbausteine.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 80;117;116;32;97;32;99;117;112;32;111;110;32;116;104;101;32;116;97;98;108;101; - - - 83;117;103;97;114;32;119;97;110;116;101;100;63; - - - 65;100;100;32;115;117;103;97;114; - - - - - 248; - - - - - 84;101;97;32;115;116;105;108;108;32;116;111;111;32;104;111;116; - - - 87;97;105;116;32;49;32;109;105;110;117;116;101; - - - - - 68;114;105;110;107;32;116;104;101;32;116;101;97; - - diff --git a/web/lessons/diagramme/en/l1-sequenz.visustruct b/web/lessons/diagramme/en/l1-sequenz.visustruct deleted file mode 100644 index cbd0c58e..00000000 --- a/web/lessons/diagramme/en/l1-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 71;101;116;32;117;112; - - - 66;114;117;115;104;32;116;101;101;116;104; - - - 71;101;116;32;100;114;101;115;115;101;100; - - - 72;97;118;101;32;98;114;101;97;107;102;97;115;116; - - diff --git a/web/lessons/diagramme/en/l1-tee.visustruct b/web/lessons/diagramme/en/l1-tee.visustruct deleted file mode 100644 index 55b442e6..00000000 --- a/web/lessons/diagramme/en/l1-tee.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 70;105;108;108;32;116;104;101;32;107;101;116;116;108;101;32;119;105;116;104;32;119;97;116;101;114; - - - 83;119;105;116;99;104;32;111;110;32;116;104;101;32;107;101;116;116;108;101; - - - 80;117;116;32;97;32;116;101;97;32;98;97;103;32;105;110;32;116;104;101;32;99;117;112; - - - 80;111;117;114;32;104;111;116;32;119;97;116;101;114;32;105;110;116;111;32;116;104;101;32;99;117;112; - - - 87;97;105;116;32;51;32;109;105;110;117;116;101;115; - - - 84;97;107;101;32;111;117;116;32;116;104;101;32;116;101;97;32;98;97;103; - - diff --git a/web/lessons/diagramme/en/l1-verzweigung.visustruct b/web/lessons/diagramme/en/l1-verzweigung.visustruct deleted file mode 100644 index fa580c28..00000000 --- a/web/lessons/diagramme/en/l1-verzweigung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 73;115;32;105;116;32;114;97;105;110;105;110;103;63; - - - 80;97;99;107;32;97;110;32;117;109;98;114;101;108;108;97; - - - - - 80;97;99;107;32;115;117;110;103;108;97;115;115;101;115; - - - - - 76;101;97;118;101; - - diff --git a/web/lessons/diagramme/en/l1-wiederholung.visustruct b/web/lessons/diagramme/en/l1-wiederholung.visustruct deleted file mode 100644 index 1ab21c7a..00000000 --- a/web/lessons/diagramme/en/l1-wiederholung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 80;108;97;116;101;32;110;111;116;32;101;109;112;116;121; - - - 84;97;107;101;32;97;32;98;105;116;101; - - - - - 67;108;101;97;114;32;116;104;101;32;112;108;97;116;101; - - diff --git a/web/lessons/diagramme/en/l10-bis-n-loesung.visustruct b/web/lessons/diagramme/en/l10-bis-n-loesung.visustruct deleted file mode 100644 index 4a72f1f5..00000000 --- a/web/lessons/diagramme/en/l10-bis-n-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;67;111;117;110;116;32;117;112;32;116;111;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/en/l10-countdown.visustruct b/web/lessons/diagramme/en/l10-countdown.visustruct deleted file mode 100644 index 7ecd64b3..00000000 --- a/web/lessons/diagramme/en/l10-countdown.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/en/l10-endlos.visustruct b/web/lessons/diagramme/en/l10-endlos.visustruct deleted file mode 100644 index 54f4322b..00000000 --- a/web/lessons/diagramme/en/l10-endlos.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/en/l10-fakultaet.visustruct b/web/lessons/diagramme/en/l10-fakultaet.visustruct deleted file mode 100644 index f08f6787..00000000 --- a/web/lessons/diagramme/en/l10-fakultaet.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;99;116;111;114;105;97;108;32;111;102;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;102;97;99;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;99;116;111;114;105;97;108;32;61;32;102;97;99;116;111;114;105;97;108;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;99;116;111;114;105;97;108; - - diff --git a/web/lessons/diagramme/en/l10-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l10-java-fehler-loesung.visustruct deleted file mode 100644 index ed31dcc2..00000000 --- a/web/lessons/diagramme/en/l10-java-fehler-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;116;105;109;101;115;63;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;110;32;111;112;116;105;111;110;34; - - - - diff --git a/web/lessons/diagramme/en/l10-java-fehler-start.visustruct b/web/lessons/diagramme/en/l10-java-fehler-start.visustruct deleted file mode 100644 index 3af2e7be..00000000 --- a/web/lessons/diagramme/en/l10-java-fehler-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;116;105;109;101;115;63;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;110;32;111;112;116;105;111;110;34; - - - - diff --git a/web/lessons/diagramme/en/l10-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l10-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index e4ac0ac4..00000000 --- a/web/lessons/diagramme/en/l10-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;103;114;97;100;101;32;61;32;50; - - - 103;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;101;112;32;112;114;97;99;116;105;115;105;110;103;34; - - - - diff --git a/web/lessons/diagramme/en/l10-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l10-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 4e9df662..00000000 --- a/web/lessons/diagramme/en/l10-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;103;114;97;100;101;32;61;32;50; - - - 71;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;101;112;32;112;114;97;99;116;105;115;105;110;103;34; - - - - diff --git a/web/lessons/diagramme/en/l10-java-wuerfel-loesung.visustruct b/web/lessons/diagramme/en/l10-java-wuerfel-loesung.visustruct deleted file mode 100644 index a93c6b77..00000000 --- a/web/lessons/diagramme/en/l10-java-wuerfel-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114; - - - 111;117;116;112;117;116;58;32;34;111;110;101;34; - - - - - 111;117;116;112;117;116;58;32;34;116;119;111;34; - - - - - 111;117;116;112;117;116;58;32;34;116;104;114;101;101;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;107;110;111;119;110;34; - - - - diff --git a/web/lessons/diagramme/en/l10-verdoppeln-loesung.visustruct b/web/lessons/diagramme/en/l10-verdoppeln-loesung.visustruct deleted file mode 100644 index 6e4cd880..00000000 --- a/web/lessons/diagramme/en/l10-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 110;117;109;98;101;114;32;60;32;49;48;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;116;101;112;115; - - diff --git a/web/lessons/diagramme/en/l11-eingabe-pruefen.visustruct b/web/lessons/diagramme/en/l11-eingabe-pruefen.visustruct deleted file mode 100644 index a5bb5f3c..00000000 --- a/web/lessons/diagramme/en/l11-eingabe-pruefen.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - - - 110;117;109;98;101;114;32;60;32;49;32;124;124;32;110;117;109;98;101;114;32;62;32;49;48; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;32;102;114;111;109;32;49;32;116;111;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;97;110;107;32;121;111;117;33;34; - - diff --git a/web/lessons/diagramme/en/l11-einmal.visustruct b/web/lessons/diagramme/en/l11-einmal.visustruct deleted file mode 100644 index a28efef4..00000000 --- a/web/lessons/diagramme/en/l11-einmal.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/en/l11-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l11-java-fehler-loesung.visustruct deleted file mode 100644 index d1a4bd25..00000000 --- a/web/lessons/diagramme/en/l11-java-fehler-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;111;97;108;32;34;83;97;118;105;110;103;115;32;103;111;97;108;63;34; - - - 105;110;116;32;109;111;110;101;121;32;61;32;48; - - - 105;110;116;32;119;101;101;107;115;32;61;32;48; - - - 109;111;110;101;121;32;60;32;103;111;97;108; - - - 109;111;110;101;121;32;61;32;109;111;110;101;121;32;43;32;53; - - - 119;101;101;107;115;32;61;32;119;101;101;107;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;115;58;32;34;32;43;32;119;101;101;107;115; - - diff --git a/web/lessons/diagramme/en/l11-java-fehler-start.visustruct b/web/lessons/diagramme/en/l11-java-fehler-start.visustruct deleted file mode 100644 index db908019..00000000 --- a/web/lessons/diagramme/en/l11-java-fehler-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;111;97;108;32;34;83;97;118;105;110;103;115;32;103;111;97;108;63;34; - - - 105;110;116;32;109;111;110;101;121;32;61;32;48; - - - 105;110;116;32;119;101;101;107;115;32;61;32;48; - - - 109;111;110;101;121;32;60;61;32;103;111;97;108; - - - 109;111;110;101;121;32;61;32;109;111;110;101;121;32;43;32;53; - - - 119;101;101;107;115;32;61;32;119;101;101;107;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;115;58;32;34;32;43;32;119;101;101;107;115; - - diff --git a/web/lessons/diagramme/en/l11-java-gerade-loesung.visustruct b/web/lessons/diagramme/en/l11-java-gerade-loesung.visustruct deleted file mode 100644 index ce913b19..00000000 --- a/web/lessons/diagramme/en/l11-java-gerade-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;101;114;101;63;34; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;50; - - - - diff --git a/web/lessons/diagramme/en/l11-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l11-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 36b01ec6..00000000 --- a/web/lessons/diagramme/en/l11-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - diff --git a/web/lessons/diagramme/en/l11-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l11-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 4e2756c2..00000000 --- a/web/lessons/diagramme/en/l11-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;78;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - diff --git a/web/lessons/diagramme/en/l11-note-loesung.visustruct b/web/lessons/diagramme/en/l11-note-loesung.visustruct deleted file mode 100644 index 264996bd..00000000 --- a/web/lessons/diagramme/en/l11-note-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;103;114;97;100;101;32;61;32;48; - - - 103;114;97;100;101;32;60;32;49;32;124;124;32;103;114;97;100;101;32;62;32;54; - - - 105;110;112;117;116;58;32;103;114;97;100;101;32;34;71;114;97;100;101;32;102;114;111;109;32;49;32;116;111;32;54;34; - - - - - 111;117;116;112;117;116;58;32;34;89;111;117;114;32;103;114;97;100;101;58;32;34;32;43;32;103;114;97;100;101; - - diff --git a/web/lessons/diagramme/en/l11-summe-loesung.visustruct b/web/lessons/diagramme/en/l11-summe-loesung.visustruct deleted file mode 100644 index 83b77da4..00000000 --- a/web/lessons/diagramme/en/l11-summe-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - - - 110;117;109;98;101;114;32;33;61;32;48; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;32;40;48;32;115;116;111;112;115;41;34; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l12-countdown-loesung.visustruct b/web/lessons/diagramme/en/l12-countdown-loesung.visustruct deleted file mode 100644 index 8fb911b8..00000000 --- a/web/lessons/diagramme/en/l12-countdown-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;83;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - diff --git a/web/lessons/diagramme/en/l12-einmaleins-loesung.visustruct b/web/lessons/diagramme/en/l12-einmaleins-loesung.visustruct deleted file mode 100644 index b42072e7..00000000 --- a/web/lessons/diagramme/en/l12-einmaleins-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;99;116;32;61;32;110;117;109;98;101;114;32;42;32;105; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;99;116; - - - - diff --git a/web/lessons/diagramme/en/l12-eins-bis-fuenf.visustruct b/web/lessons/diagramme/en/l12-eins-bis-fuenf.visustruct deleted file mode 100644 index 155894b1..00000000 --- a/web/lessons/diagramme/en/l12-eins-bis-fuenf.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/en/l12-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l12-java-fehler-loesung.visustruct deleted file mode 100644 index d33f5ffa..00000000 --- a/web/lessons/diagramme/en/l12-java-fehler-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - diff --git a/web/lessons/diagramme/en/l12-java-fehler-start.visustruct b/web/lessons/diagramme/en/l12-java-fehler-start.visustruct deleted file mode 100644 index 0fdbe953..00000000 --- a/web/lessons/diagramme/en/l12-java-fehler-start.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;62;32;49; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - diff --git a/web/lessons/diagramme/en/l12-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l12-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 572b6780..00000000 --- a/web/lessons/diagramme/en/l12-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;32;61;32;50;48; - - - 110;117;109;98;101;114;32;60;32;108;105;109;105;116; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/en/l12-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l12-java-laeuft-nicht-start.visustruct deleted file mode 100644 index d232f1da..00000000 --- a/web/lessons/diagramme/en/l12-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;32;61;32;50;48; - - - 110;117;109;98;101;114;32;60;32;76;105;109;105;116; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/en/l12-java-verdoppeln-loesung.visustruct b/web/lessons/diagramme/en/l12-java-verdoppeln-loesung.visustruct deleted file mode 100644 index dbad0a20..00000000 --- a/web/lessons/diagramme/en/l12-java-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;60;32;49;48;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - diff --git a/web/lessons/diagramme/en/l12-summe.visustruct b/web/lessons/diagramme/en/l12-summe.visustruct deleted file mode 100644 index aaf24ec0..00000000 --- a/web/lessons/diagramme/en/l12-summe.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l13-dreieck.visustruct b/web/lessons/diagramme/en/l13-dreieck.visustruct deleted file mode 100644 index 1d6f21e0..00000000 --- a/web/lessons/diagramme/en/l13-dreieck.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;72;111;119;32;109;97;110;121;32;108;105;110;101;115;63;34; - - - 105;110;116;32;108;105;110;101;32;61;32;49; - 108;105;110;101;32;60;61;32;110; - 108;105;110;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;116;97;114;32;61;32;49; - 115;116;97;114;32;60;61;32;108;105;110;101; - 115;116;97;114;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - diff --git a/web/lessons/diagramme/en/l13-gerade-eingaben-loesung.visustruct b/web/lessons/diagramme/en/l13-gerade-eingaben-loesung.visustruct deleted file mode 100644 index ab694474..00000000 --- a/web/lessons/diagramme/en/l13-gerade-eingaben-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l13-gerade-zaehlen.visustruct b/web/lessons/diagramme/en/l13-gerade-zaehlen.visustruct deleted file mode 100644 index 9ff2a3b7..00000000 --- a/web/lessons/diagramme/en/l13-gerade-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l13-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l13-java-fehler-loesung.visustruct deleted file mode 100644 index 6e5e6717..00000000 --- a/web/lessons/diagramme/en/l13-java-fehler-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;111;119;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/en/l13-java-fehler-start.visustruct b/web/lessons/diagramme/en/l13-java-fehler-start.visustruct deleted file mode 100644 index 78e44f69..00000000 --- a/web/lessons/diagramme/en/l13-java-fehler-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;111;119;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/en/l13-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l13-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 228ddea0..00000000 --- a/web/lessons/diagramme/en/l13-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;104;101;105;103;104;116;32;61;32;48; - - - 105;110;116;32;115;116;101;112;32;61;32;49; - 115;116;101;112;32;60;61;32;53; - 115;116;101;112;43;43; - - - 104;101;105;103;104;116;32;61;32;104;101;105;103;104;116;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/en/l13-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l13-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 2a399b98..00000000 --- a/web/lessons/diagramme/en/l13-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;32;61;32;49; - 115;116;101;112;32;60;61;32;53; - 115;116;101;112;43;43; - - - 104;101;105;103;104;116;32;61;32;104;101;105;103;104;116;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/en/l13-java-quadrate-loesung.visustruct b/web/lessons/diagramme/en/l13-java-quadrate-loesung.visustruct deleted file mode 100644 index 5bbbb82a..00000000 --- a/web/lessons/diagramme/en/l13-java-quadrate-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/en/l13-rechteck-loesung.visustruct b/web/lessons/diagramme/en/l13-rechteck-loesung.visustruct deleted file mode 100644 index d93cdb1d..00000000 --- a/web/lessons/diagramme/en/l13-rechteck-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;119;105;100;116;104;32;34;87;105;100;116;104;34; - - - 105;110;112;117;116;58;32;105;110;116;32;104;101;105;103;104;116;32;34;72;101;105;103;104;116;34; - - - 105;110;116;32;108;105;110;101;32;61;32;49; - 108;105;110;101;32;60;61;32;104;101;105;103;104;116; - 108;105;110;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;99;111;108;117;109;110;32;61;32;49; - 99;111;108;117;109;110;32;60;61;32;119;105;100;116;104; - 99;111;108;117;109;110;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - diff --git a/web/lessons/diagramme/en/l14-array-durchlaufen.visustruct b/web/lessons/diagramme/en/l14-array-durchlaufen.visustruct deleted file mode 100644 index 4efa7d30..00000000 --- a/web/lessons/diagramme/en/l14-array-durchlaufen.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/l14-erster-letzter-loesung.visustruct b/web/lessons/diagramme/en/l14-erster-letzter-loesung.visustruct deleted file mode 100644 index 281631b7..00000000 --- a/web/lessons/diagramme/en/l14-erster-letzter-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;91;48;93; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;91;118;97;108;117;101;115;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/en/l14-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l14-java-fehler-loesung.visustruct deleted file mode 100644 index fe123b96..00000000 --- a/web/lessons/diagramme/en/l14-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l14-java-fehler-start.visustruct b/web/lessons/diagramme/en/l14-java-fehler-start.visustruct deleted file mode 100644 index a05e8472..00000000 --- a/web/lessons/diagramme/en/l14-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - - - diff --git a/web/lessons/diagramme/en/l14-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l14-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 39f22d7f..00000000 --- a/web/lessons/diagramme/en/l14-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/l14-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l14-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 549a55f6..00000000 --- a/web/lessons/diagramme/en/l14-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/l14-java-treppe-loesung.visustruct b/web/lessons/diagramme/en/l14-java-treppe-loesung.visustruct deleted file mode 100644 index 918f79b4..00000000 --- a/web/lessons/diagramme/en/l14-java-treppe-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;72;111;119;32;109;97;110;121;32;115;116;101;112;115;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - diff --git a/web/lessons/diagramme/en/l14-namen-eingeben.visustruct b/web/lessons/diagramme/en/l14-namen-eingeben.visustruct deleted file mode 100644 index 3d06c9e9..00000000 --- a/web/lessons/diagramme/en/l14-namen-eingeben.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;97;109;101;34; - - - - - 111;117;116;112;117;116;58;32;34;70;105;114;115;116;32;110;97;109;101;58;32;34;32;43;32;110;97;109;101;115;91;48;93; - - diff --git a/web/lessons/diagramme/en/l14-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/en/l14-rueckwaerts-loesung.visustruct deleted file mode 100644 index cce8dc60..00000000 --- a/web/lessons/diagramme/en/l14-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - - - 105;110;116;32;105;32;61;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/l14-summe-max-loesung.visustruct b/web/lessons/diagramme/en/l14-summe-max-loesung.visustruct deleted file mode 100644 index 30956b18..00000000 --- a/web/lessons/diagramme/en/l14-summe-max-loesung.visustruct +++ /dev/null @@ -1,51 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;110;117;109;98;101;114;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114;115;91;105;93; - - - 110;117;109;98;101;114;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;110;117;109;98;101;114;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/en/l14-summe-maximum.visustruct b/web/lessons/diagramme/en/l14-summe-maximum.visustruct deleted file mode 100644 index ed53ee55..00000000 --- a/web/lessons/diagramme/en/l14-summe-maximum.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;111;105;110;116;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - 112;111;105;110;116;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;111;105;110;116;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/en/l15-bis-null-loesung.visustruct b/web/lessons/diagramme/en/l15-bis-null-loesung.visustruct deleted file mode 100644 index f24b61cf..00000000 --- a/web/lessons/diagramme/en/l15-bis-null-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - 110;117;109;98;101;114;32;33;61;32;48; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l15-endlos-aussprung.visustruct b/web/lessons/diagramme/en/l15-endlos-aussprung.visustruct deleted file mode 100644 index df565e07..00000000 --- a/web/lessons/diagramme/en/l15-endlos-aussprung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - 110;117;109;98;101;114;32;61;61;32;48; - - - 76;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l15-flaeche-loesung.visustruct b/web/lessons/diagramme/en/l15-flaeche-loesung.visustruct deleted file mode 100644 index 88b730bd..00000000 --- a/web/lessons/diagramme/en/l15-flaeche-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;108;101;110;103;116;104;32;34;76;101;110;103;116;104;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;105;100;116;104;32;34;87;105;100;116;104;63;34; - - - 105;110;116;32;102;32;61;32;97;114;101;97;40;108;101;110;103;116;104;44;32;119;105;100;116;104;41; - - - 111;117;116;112;117;116;58;32;34;65;114;101;97;58;32;34;32;43;32;102; - - diff --git a/web/lessons/diagramme/en/l15-flaeche-unterprogramm-loesung.visustruct b/web/lessons/diagramme/en/l15-flaeche-unterprogramm-loesung.visustruct deleted file mode 100644 index 8b67013e..00000000 --- a/web/lessons/diagramme/en/l15-flaeche-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 114;101;116;117;114;110;32;97;32;42;32;98; - - diff --git a/web/lessons/diagramme/en/l15-hauptprogramm.visustruct b/web/lessons/diagramme/en/l15-hauptprogramm.visustruct deleted file mode 100644 index 879ab87a..00000000 --- a/web/lessons/diagramme/en/l15-hauptprogramm.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 115;104;111;119;71;114;101;101;116;105;110;103;40;41; - - - 97;115;107;81;117;101;115;116;105;111;110;115;40;41; - - - 115;104;111;119;82;101;115;117;108;116;40;41; - - diff --git a/web/lessons/diagramme/en/l15-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l15-java-fehler-loesung.visustruct deleted file mode 100644 index c91c9cbe..00000000 --- a/web/lessons/diagramme/en/l15-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;105;103;104;101;115;116;32;116;101;109;112;101;114;97;116;117;114;101;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/en/l15-java-fehler-start.visustruct b/web/lessons/diagramme/en/l15-java-fehler-start.visustruct deleted file mode 100644 index 262b5c76..00000000 --- a/web/lessons/diagramme/en/l15-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;105;103;104;101;115;116;32;116;101;109;112;101;114;97;116;117;114;101;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/en/l15-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l15-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index b3d70e69..00000000 --- a/web/lessons/diagramme/en/l15-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l15-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l15-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 03818a5c..00000000 --- a/web/lessons/diagramme/en/l15-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;76;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l15-java-noten-loesung.visustruct b/web/lessons/diagramme/en/l15-java-noten-loesung.visustruct deleted file mode 100644 index d9470357..00000000 --- a/web/lessons/diagramme/en/l15-java-noten-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;103;114;97;100;101;115;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;103;114;97;100;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;103;114;97;100;101;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l15-quadrat-haupt.visustruct b/web/lessons/diagramme/en/l15-quadrat-haupt.visustruct deleted file mode 100644 index 4f1308dd..00000000 --- a/web/lessons/diagramme/en/l15-quadrat-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;113;32;61;32;115;113;117;97;114;101;40;110;117;109;98;101;114;41; - - - 111;117;116;112;117;116;58;32;34;84;104;101;32;115;113;117;97;114;101;32;105;115;32;34;32;43;32;113; - - diff --git a/web/lessons/diagramme/en/l15-quadrat.visustruct b/web/lessons/diagramme/en/l15-quadrat.visustruct deleted file mode 100644 index 093d2a63..00000000 --- a/web/lessons/diagramme/en/l15-quadrat.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;114;101;115;117;108;116; - - diff --git a/web/lessons/diagramme/en/l15-titel-loesung.visustruct b/web/lessons/diagramme/en/l15-titel-loesung.visustruct deleted file mode 100644 index 121b951e..00000000 --- a/web/lessons/diagramme/en/l15-titel-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 115;104;111;119;84;105;116;108;101;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;104;97;116;32;105;115;32;121;111;117;114;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;34;32;43;32;110;97;109;101; - - diff --git a/web/lessons/diagramme/en/l15-unterprogramm.visustruct b/web/lessons/diagramme/en/l15-unterprogramm.visustruct deleted file mode 100644 index 1c38f9f3..00000000 --- a/web/lessons/diagramme/en/l15-unterprogramm.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;32;116;111;32;116;104;101;32;113;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;103;101;116;32;116;104;114;101;101;32;113;117;101;115;116;105;111;110;115;46;34; - - diff --git a/web/lessons/diagramme/en/l15-zeige-summe-haupt.visustruct b/web/lessons/diagramme/en/l15-zeige-summe-haupt.visustruct deleted file mode 100644 index 6ccf3351..00000000 --- a/web/lessons/diagramme/en/l15-zeige-summe-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 115;104;111;119;83;117;109;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 115;104;111;119;83;117;109;40;120;44;32;120;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/en/l15-zeige-summe.visustruct b/web/lessons/diagramme/en/l15-zeige-summe.visustruct deleted file mode 100644 index cb3267d2..00000000 --- a/web/lessons/diagramme/en/l15-zeige-summe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l16-countdown-loesung.visustruct b/web/lessons/diagramme/en/l16-countdown-loesung.visustruct deleted file mode 100644 index 6454fc18..00000000 --- a/web/lessons/diagramme/en/l16-countdown-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;32;118;97;108;117;101;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - diff --git a/web/lessons/diagramme/en/l16-countdown-start.visustruct b/web/lessons/diagramme/en/l16-countdown-start.visustruct deleted file mode 100644 index 8803f9d5..00000000 --- a/web/lessons/diagramme/en/l16-countdown-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;32;118;97;108;117;101;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;34; - - diff --git a/web/lessons/diagramme/en/l16-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l16-java-fehler-loesung.visustruct deleted file mode 100644 index f4d1b3bb..00000000 --- a/web/lessons/diagramme/en/l16-java-fehler-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;112;111;105;110;116;115;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/l16-java-fehler-start.visustruct b/web/lessons/diagramme/en/l16-java-fehler-start.visustruct deleted file mode 100644 index 2edead6a..00000000 --- a/web/lessons/diagramme/en/l16-java-fehler-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;112;111;105;110;116;115;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/l16-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l16-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index e5ae3c20..00000000 --- a/web/lessons/diagramme/en/l16-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;44;32;114;111;117;110;100;32;49;58;32;34;32;43;32;112;111;105;110;116;115;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/en/l16-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l16-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 71267d5a..00000000 --- a/web/lessons/diagramme/en/l16-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;44;32;114;111;117;110;100;32;49;58;32;34;32;43;32;80;111;105;110;116;115;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/en/l16-java-punkte-loesung.visustruct b/web/lessons/diagramme/en/l16-java-punkte-loesung.visustruct deleted file mode 100644 index 3fe97f87..00000000 --- a/web/lessons/diagramme/en/l16-java-punkte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l16-schreibtisch.visustruct b/web/lessons/diagramme/en/l16-schreibtisch.visustruct deleted file mode 100644 index 4f13f95b..00000000 --- a/web/lessons/diagramme/en/l16-schreibtisch.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l16-summe-loesung.visustruct b/web/lessons/diagramme/en/l16-summe-loesung.visustruct deleted file mode 100644 index 180eadae..00000000 --- a/web/lessons/diagramme/en/l16-summe-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l16-summe-start.visustruct b/web/lessons/diagramme/en/l16-summe-start.visustruct deleted file mode 100644 index 8ffa0ab9..00000000 --- a/web/lessons/diagramme/en/l16-summe-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l17-beispiel.visustruct b/web/lessons/diagramme/en/l17-beispiel.visustruct deleted file mode 100644 index 6e13646e..00000000 --- a/web/lessons/diagramme/en/l17-beispiel.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;116;105;109;101;115;32;34;32;43;32;110;117;109;98;101;114;32;43;32;34;32;61;32;34;32;43;32;114;101;115;117;108;116; - - - - - 110;117;109;98;101;114;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;119;97;115;32;104;97;114;100;33;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;119;97;115;32;101;97;115;121;46;34; - - - - diff --git a/web/lessons/diagramme/en/l17-fakultaet-loesung.visustruct b/web/lessons/diagramme/en/l17-fakultaet-loesung.visustruct deleted file mode 100644 index 277fc32b..00000000 --- a/web/lessons/diagramme/en/l17-fakultaet-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 105;110;116;32;102;97;99;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;99;116;111;114;105;97;108;32;61;32;102;97;99;116;111;114;105;97;108;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;99;116;111;114;105;97;108; - - diff --git a/web/lessons/diagramme/en/l17-java-brief-loesung.visustruct b/web/lessons/diagramme/en/l17-java-brief-loesung.visustruct deleted file mode 100644 index a501b4f4..00000000 --- a/web/lessons/diagramme/en/l17-java-brief-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;101;116;116;101;114;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;101;116;116;101;114;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;108;101;116;116;101;114;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;76;105;110;101;115;58;32;34;32;43;32;108;105;110;101;115;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;101;115;91;105;93;46;99;111;110;116;97;105;110;115;40;34;102;111;120;34;41; - - - 111;117;116;112;117;116;58;32;34;70;111;120;32;105;110;32;108;105;110;101;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/l17-java-brief-start.visustruct b/web/lessons/diagramme/en/l17-java-brief-start.visustruct deleted file mode 100644 index 428bff3c..00000000 --- a/web/lessons/diagramme/en/l17-java-brief-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;101;116;116;101;114;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;101;116;116;101;114;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/en/l17-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l17-java-fehler-loesung.visustruct deleted file mode 100644 index 63a44d42..00000000 --- a/web/lessons/diagramme/en/l17-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;32;34;70;105;114;115;116;32;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;32;34;76;97;115;116;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/en/l17-java-fehler-start.visustruct b/web/lessons/diagramme/en/l17-java-fehler-start.visustruct deleted file mode 100644 index 13a7f753..00000000 --- a/web/lessons/diagramme/en/l17-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;32;34;70;105;114;115;116;32;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;32;34;76;97;115;116;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/en/l17-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l17-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index dadd5b88..00000000 --- a/web/lessons/diagramme/en/l17-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;104;97;115;32;34;32;43;32;110;97;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;116;101;114;115;34; - - diff --git a/web/lessons/diagramme/en/l17-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l17-java-laeuft-nicht-start.visustruct deleted file mode 100644 index f5a73be8..00000000 --- a/web/lessons/diagramme/en/l17-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;104;97;115;32;34;32;43;32;110;97;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;116;101;114;115;34; - - diff --git a/web/lessons/diagramme/en/l17-java-passwort-loesung.visustruct b/web/lessons/diagramme/en/l17-java-passwort-loesung.visustruct deleted file mode 100644 index a812c6cd..00000000 --- a/web/lessons/diagramme/en/l17-java-passwort-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;105;110;112;117;116;32;34;80;97;115;115;119;111;114;100;63;34; - - - 105;110;112;117;116;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;33;34; - - - - - 111;117;116;112;117;116;58;32;34;87;114;111;110;103;32;112;97;115;115;119;111;114;100;34; - - - - diff --git a/web/lessons/diagramme/en/l17-ticket-loesung.visustruct b/web/lessons/diagramme/en/l17-ticket-loesung.visustruct deleted file mode 100644 index 2b5ead64..00000000 --- a/web/lessons/diagramme/en/l17-ticket-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 97;103;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;67;104;105;108;100;32;116;105;99;107;101;116;58;32;52;32;101;117;114;111;115;34; - - - - - 111;117;116;112;117;116;58;32;34;65;100;117;108;116;32;116;105;99;107;101;116;58;32;56;32;101;117;114;111;115;34; - - - - - - diff --git a/web/lessons/diagramme/en/l18-beispiel.visustruct b/web/lessons/diagramme/en/l18-beispiel.visustruct deleted file mode 100644 index 269f6139..00000000 --- a/web/lessons/diagramme/en/l18-beispiel.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;118;97;108;117;101;63;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;69;110;100;34; - - diff --git a/web/lessons/diagramme/en/l18-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l18-java-fehler-loesung.visustruct deleted file mode 100644 index 6907b0c1..00000000 --- a/web/lessons/diagramme/en/l18-java-fehler-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;116;114;105;101;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;103;117;101;115;115;32;34;89;111;117;114;32;103;117;101;115;115;63;34; - - - 116;114;105;101;115;32;61;32;116;114;105;101;115;32;43;32;49; - - - 103;117;101;115;115;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;114;105;101;115;58;32;34;32;43;32;116;114;105;101;115; - - diff --git a/web/lessons/diagramme/en/l18-java-fehler-start.visustruct b/web/lessons/diagramme/en/l18-java-fehler-start.visustruct deleted file mode 100644 index 2b41a1b3..00000000 --- a/web/lessons/diagramme/en/l18-java-fehler-start.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;116;114;105;101;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;103;117;101;115;115;32;34;89;111;117;114;32;103;117;101;115;115;63;34; - - - 103;117;101;115;115;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 116;114;105;101;115;32;61;32;116;114;105;101;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;84;114;105;101;115;58;32;34;32;43;32;116;114;105;101;115; - - diff --git a/web/lessons/diagramme/en/l18-java-kasse-loesung.visustruct b/web/lessons/diagramme/en/l18-java-kasse-loesung.visustruct deleted file mode 100644 index 95c939ad..00000000 --- a/web/lessons/diagramme/en/l18-java-kasse-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;105;99;101;32;34;80;114;105;99;101;63;32;40;48;32;61;32;101;110;100;41;34; - - - 112;114;105;99;101;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;114;105;99;101; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;73;116;101;109;115;58;32;34;32;43;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;115;117;109; - - diff --git a/web/lessons/diagramme/en/l18-js-loesung.visustruct b/web/lessons/diagramme/en/l18-js-loesung.visustruct deleted file mode 100644 index 6c39c541..00000000 --- a/web/lessons/diagramme/en/l18-js-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;109;97;121;32;118;111;116;101;46;34; - - - - - 105;110;116;32;121;101;97;114;115;32;61;32;49;54;32;45;32;97;103;101; - - - 111;117;116;112;117;116;58;32;34;87;97;105;116;32;34;32;43;32;121;101;97;114;115;32;43;32;34;32;109;111;114;101;32;121;101;97;114;115;46;34; - - - - diff --git a/web/lessons/diagramme/en/l18-python-loesung.visustruct b/web/lessons/diagramme/en/l18-python-loesung.visustruct deleted file mode 100644 index 86574e55..00000000 --- a/web/lessons/diagramme/en/l18-python-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;108;105;109;105;116;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;108;105;109;105;116; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/en/l19-java-fehler-loesung.visustruct b/web/lessons/diagramme/en/l19-java-fehler-loesung.visustruct deleted file mode 100644 index a4251365..00000000 --- a/web/lessons/diagramme/en/l19-java-fehler-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/en/l19-java-fehler-start.visustruct b/web/lessons/diagramme/en/l19-java-fehler-start.visustruct deleted file mode 100644 index f76b8c49..00000000 --- a/web/lessons/diagramme/en/l19-java-fehler-start.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/en/l19-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/en/l19-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 091030c3..00000000 --- a/web/lessons/diagramme/en/l19-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;119;97;110;116;101;100;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/en/l19-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/en/l19-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 82c8278c..00000000 --- a/web/lessons/diagramme/en/l19-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;119;97;110;116;101;100;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;87;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/en/l19-java-zaehlen-loesung.visustruct b/web/lessons/diagramme/en/l19-java-zaehlen-loesung.visustruct deleted file mode 100644 index eace5f66..00000000 --- a/web/lessons/diagramme/en/l19-java-zaehlen-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l2-ausgabe.visustruct b/web/lessons/diagramme/en/l2-ausgabe.visustruct deleted file mode 100644 index 45cdd7f5..00000000 --- a/web/lessons/diagramme/en/l2-ausgabe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;71;111;111;100;32;109;111;114;110;105;110;103;34; - - - 111;117;116;112;117;116;58;32;34;84;111;100;97;121;32;119;101;32;108;101;97;114;110;32;115;116;114;117;99;116;117;114;101;32;99;104;97;114;116;115;46;34; - - diff --git a/web/lessons/diagramme/en/l2-hallo-loesung.visustruct b/web/lessons/diagramme/en/l2-hallo-loesung.visustruct deleted file mode 100644 index 2b9af12a..00000000 --- a/web/lessons/diagramme/en/l2-hallo-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;87;111;114;108;100;34; - - diff --git a/web/lessons/diagramme/en/l2-zwei-zeilen-loesung.visustruct b/web/lessons/diagramme/en/l2-zwei-zeilen-loesung.visustruct deleted file mode 100644 index 596bcf57..00000000 --- a/web/lessons/diagramme/en/l2-zwei-zeilen-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;34; - - - 111;117;116;112;117;116;58;32;34;73;32;97;109;32;108;101;97;114;110;105;110;103;32;115;116;114;117;99;116;117;114;101;32;99;104;97;114;116;115;34; - - diff --git a/web/lessons/diagramme/en/l23-java-ergaenzen-loesung.visustruct b/web/lessons/diagramme/en/l23-java-ergaenzen-loesung.visustruct deleted file mode 100644 index 2e952151..00000000 --- a/web/lessons/diagramme/en/l23-java-ergaenzen-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;104;111;112;112;105;110;103;45;110;101;119;46;116;120;116;34;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;108;105;110;101;41; - - - 99;111;117;110;116;43;43; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;67;97;107;101;34;41; - - - 99;111;117;110;116;43;43; - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;111;117;110;116;32;43;32;34;32;108;105;110;101;115;32;115;97;118;101;100;34; - - diff --git a/web/lessons/diagramme/en/l23-java-ergaenzen-start.visustruct b/web/lessons/diagramme/en/l23-java-ergaenzen-start.visustruct deleted file mode 100644 index c3833f76..00000000 --- a/web/lessons/diagramme/en/l23-java-ergaenzen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;104;111;112;112;105;110;103;45;110;101;119;46;116;120;116;34;41; - - diff --git a/web/lessons/diagramme/en/l23-java-nummern-loesung.visustruct b/web/lessons/diagramme/en/l23-java-nummern-loesung.visustruct deleted file mode 100644 index 67eedb87..00000000 --- a/web/lessons/diagramme/en/l23-java-nummern-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;46;32;34;32;43;32;108;105;110;101; - - - 110;117;109;98;101;114;43;43; - - - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/l23-java-nummern-start.visustruct b/web/lessons/diagramme/en/l23-java-nummern-start.visustruct deleted file mode 100644 index c8802a36..00000000 --- a/web/lessons/diagramme/en/l23-java-nummern-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/en/l24-java-fuchs-loesung.visustruct b/web/lessons/diagramme/en/l24-java-fuchs-loesung.visustruct deleted file mode 100644 index 301ee130..00000000 --- a/web/lessons/diagramme/en/l24-java-fuchs-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 108;105;110;101;46;99;111;110;116;97;105;110;115;40;34;70;111;120;34;41; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;70;111;120;58;32;34;32;43;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l24-java-fuchs-start.visustruct b/web/lessons/diagramme/en/l24-java-fuchs-start.visustruct deleted file mode 100644 index 0e414b70..00000000 --- a/web/lessons/diagramme/en/l24-java-fuchs-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/l24-java-laengster-loesung.visustruct b/web/lessons/diagramme/en/l24-java-laengster-loesung.visustruct deleted file mode 100644 index 6b82902c..00000000 --- a/web/lessons/diagramme/en/l24-java-laengster-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;108;111;110;103;101;115;116;32;61;32;108;105;110;101;115;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;101;115;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;108;111;110;103;101;115;116;46;108;101;110;103;116;104;40;41; - - - 108;111;110;103;101;115;116;32;61;32;108;105;110;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;76;111;110;103;101;115;116;32;101;110;116;114;121;58;32;34;32;43;32;108;111;110;103;101;115;116; - - diff --git a/web/lessons/diagramme/en/l24-java-laengster-start.visustruct b/web/lessons/diagramme/en/l24-java-laengster-start.visustruct deleted file mode 100644 index 2ca812d9..00000000 --- a/web/lessons/diagramme/en/l24-java-laengster-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - diff --git a/web/lessons/diagramme/en/l25-java-beste-loesung.visustruct b/web/lessons/diagramme/en/l25-java-beste-loesung.visustruct deleted file mode 100644 index 12b27e5e..00000000 --- a/web/lessons/diagramme/en/l25-java-beste-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;98;101;115;116;78;97;109;101;32;61;32;34;34; - - - 105;110;116;32;98;101;115;116;71;114;97;100;101;32;61;32;55; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;103;114;97;100;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;51;93;41; - - - 103;114;97;100;101;32;60;32;98;101;115;116;71;114;97;100;101; - - - 98;101;115;116;71;114;97;100;101;32;61;32;103;114;97;100;101; - - - 98;101;115;116;78;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;66;101;115;116;32;103;114;97;100;101;32;105;110;32;69;110;103;108;105;115;104;58;32;34;32;43;32;98;101;115;116;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;71;114;97;100;101;32;43;32;34;41;34; - - diff --git a/web/lessons/diagramme/en/l25-java-beste-start.visustruct b/web/lessons/diagramme/en/l25-java-beste-start.visustruct deleted file mode 100644 index 7a42ab68..00000000 --- a/web/lessons/diagramme/en/l25-java-beste-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/l25-java-schnitt-loesung.visustruct b/web/lessons/diagramme/en/l25-java-schnitt-loesung.visustruct deleted file mode 100644 index 68e2b79a..00000000 --- a/web/lessons/diagramme/en/l25-java-schnitt-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;117;109;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 115;117;109;32;61;32;115;117;109;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 99;111;117;110;116;43;43; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;97;116;104;115;32;97;118;101;114;97;103;101;58;32;34;32;43;32;115;117;109;32;47;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/l25-java-schnitt-start.visustruct b/web/lessons/diagramme/en/l25-java-schnitt-start.visustruct deleted file mode 100644 index 423b5c40..00000000 --- a/web/lessons/diagramme/en/l25-java-schnitt-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/l26-java-namen-loesung.visustruct b/web/lessons/diagramme/en/l26-java-namen-loesung.visustruct deleted file mode 100644 index 6eed226b..00000000 --- a/web/lessons/diagramme/en/l26-java-namen-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 108;105;110;101;46;99;111;110;116;97;105;110;115;40;34;92;34;110;97;109;101;92;34;34;41; - - - 105;110;116;32;115;116;97;114;116;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 111;117;116;112;117;116;58;32;108;105;110;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;41; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/l26-java-namen-start.visustruct b/web/lessons/diagramme/en/l26-java-namen-start.visustruct deleted file mode 100644 index 018f8bf5..00000000 --- a/web/lessons/diagramme/en/l26-java-namen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/en/l26-java-schreiben-loesung.visustruct b/web/lessons/diagramme/en/l26-java-schreiben-loesung.visustruct deleted file mode 100644 index 75af86d7..00000000 --- a/web/lessons/diagramme/en/l26-java-schreiben-loesung.visustruct +++ /dev/null @@ -1,50 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;111;120;34;125; - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;105;110;116;115;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;110;116;114;121;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;110;97;109;101;115;91;105;93;32;43;32;34;92;34;44;32;92;34;112;111;105;110;116;115;92;34;58;32;34;32;43;32;112;111;105;110;116;115;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;121;32;61;32;101;110;116;114;121;32;43;32;34;44;34; - - - - - 248; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;101;110;116;114;121;41; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;115;46;108;101;110;103;116;104;32;43;32;34;32;101;110;116;114;105;101;115;32;115;97;118;101;100;34; - - diff --git a/web/lessons/diagramme/en/l26-java-schreiben-start.visustruct b/web/lessons/diagramme/en/l26-java-schreiben-start.visustruct deleted file mode 100644 index 7607ec6f..00000000 --- a/web/lessons/diagramme/en/l26-java-schreiben-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;111;120;34;125; - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;105;110;116;115;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/l27-java-json-loesung.visustruct b/web/lessons/diagramme/en/l27-java-json-loesung.visustruct deleted file mode 100644 index 443fb708..00000000 --- a/web/lessons/diagramme/en/l27-java-json-loesung.visustruct +++ /dev/null @@ -1,53 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;108;115;45;110;101;119;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;115;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;110;116;114;121;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;115;91;48;93;32;43;32;34;92;34;44;32;92;34;115;112;101;99;105;101;115;92;34;58;32;92;34;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;92;34;44;32;92;34;97;103;101;92;34;58;32;34;32;43;32;112;97;114;116;115;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;121;32;61;32;101;110;116;114;121;32;43;32;34;44;34; - - - - - 248; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;101;110;116;114;121;41; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;108;105;110;101;115;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;97;110;105;109;97;108;115;32;115;97;118;101;100;34; - - diff --git a/web/lessons/diagramme/en/l27-java-json-start.visustruct b/web/lessons/diagramme/en/l27-java-json-start.visustruct deleted file mode 100644 index dbe66c3b..00000000 --- a/web/lessons/diagramme/en/l27-java-json-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;108;115;45;110;101;119;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/l27-java-saetze-loesung.visustruct b/web/lessons/diagramme/en/l27-java-saetze-loesung.visustruct deleted file mode 100644 index a1778889..00000000 --- a/web/lessons/diagramme/en/l27-java-saetze-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;101;110;116;101;110;99;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;112;97;114;116;115;91;48;93;32;43;32;34;32;40;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;41;32;105;115;32;34;32;43;32;112;97;114;116;115;91;50;93;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34;41; - - - 99;111;117;110;116;43;43; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;111;117;110;116;32;43;32;34;32;115;101;110;116;101;110;99;101;115;32;115;97;118;101;100;34; - - diff --git a/web/lessons/diagramme/en/l27-java-saetze-start.visustruct b/web/lessons/diagramme/en/l27-java-saetze-start.visustruct deleted file mode 100644 index dc1b3285..00000000 --- a/web/lessons/diagramme/en/l27-java-saetze-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;101;110;116;101;110;99;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/en/sim-kino-loesung.visustruct b/web/lessons/diagramme/en/sim-kino-loesung.visustruct deleted file mode 100644 index 2c4e777a..00000000 --- a/web/lessons/diagramme/en/sim-kino-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;116;105;99;107;101;116;115;63;34; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;99;111;117;110;116;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/en/sim-kino-start.visustruct b/web/lessons/diagramme/en/sim-kino-start.visustruct deleted file mode 100644 index 283546b9..00000000 --- a/web/lessons/diagramme/en/sim-kino-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;116;105;99;107;101;116;115;63;34; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;99;111;117;110;116;32;42;32;56;46;53; - - diff --git a/web/lessons/diagramme/en/sim-tausch-loesung.visustruct b/web/lessons/diagramme/en/sim-tausch-loesung.visustruct deleted file mode 100644 index 5045661f..00000000 --- a/web/lessons/diagramme/en/sim-tausch-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/en/sim-tausch-start.visustruct b/web/lessons/diagramme/en/sim-tausch-start.visustruct deleted file mode 100644 index 36ce1dea..00000000 --- a/web/lessons/diagramme/en/sim-tausch-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/en/sim-tausch.visustruct b/web/lessons/diagramme/en/sim-tausch.visustruct deleted file mode 100644 index 29061b19..00000000 --- a/web/lessons/diagramme/en/sim-tausch.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/en/ss-absteigend-loesung.visustruct b/web/lessons/diagramme/en/ss-absteigend-loesung.visustruct deleted file mode 100644 index 46bd866c..00000000 --- a/web/lessons/diagramme/en/ss-absteigend-loesung.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;100;115;91;105;93;32;34;69;110;116;101;114;32;97;32;99;97;114;100;34; - - - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;60;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/ss-absteigend-start.visustruct b/web/lessons/diagramme/en/ss-absteigend-start.visustruct deleted file mode 100644 index 9774c469..00000000 --- a/web/lessons/diagramme/en/ss-absteigend-start.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;100;115;91;105;93;32;34;69;110;116;101;114;32;97;32;99;97;114;100;34; - - - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;62;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/ss-binaere-suche.visustruct b/web/lessons/diagramme/en/ss-binaere-suche.visustruct deleted file mode 100644 index 099c6c14..00000000 --- a/web/lessons/diagramme/en/ss-binaere-suche.visustruct +++ /dev/null @@ -1,74 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;32;97;114;101;32;121;111;117;32;108;111;111;107;105;110;103;32;102;111;114;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;105;115;111;110;115;32;61;32;48; - - - 105;110;116;32;108;101;102;116;32;61;32;48; - - - 105;110;116;32;114;105;103;104;116;32;61;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;105;100;100;108;101;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;108;101;102;116;32;60;61;32;114;105;103;104;116; - - - 109;105;100;100;108;101;32;61;32;40;108;101;102;116;32;43;32;114;105;103;104;116;41;32;47;32;50; - - - 99;111;109;112;97;114;105;115;111;110;115;32;61;32;99;111;109;112;97;114;105;115;111;110;115;32;43;32;49; - - - 99;97;114;100;115;91;109;105;100;100;108;101;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;109;105;100;100;108;101; - - - - - 99;97;114;100;115;91;109;105;100;100;108;101;93;32;60;32;116;97;114;103;101;116; - - - 108;101;102;116;32;61;32;109;105;100;100;108;101;32;43;32;49; - - - - - 114;105;103;104;116;32;61;32;109;105;100;100;108;101;32;45;32;49; - - - - - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;70;111;117;110;100;32;97;116;32;112;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;105;115;111;110;115;58;32;34;32;43;32;99;111;109;112;97;114;105;115;111;110;115; - - diff --git a/web/lessons/diagramme/en/ss-bubblesort.visustruct b/web/lessons/diagramme/en/ss-bubblesort.visustruct deleted file mode 100644 index 33b474ee..00000000 --- a/web/lessons/diagramme/en/ss-bubblesort.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;62;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/ss-karte-suchen-loesung.visustruct b/web/lessons/diagramme/en/ss-karte-suchen-loesung.visustruct deleted file mode 100644 index 1e8d853c..00000000 --- a/web/lessons/diagramme/en/ss-karte-suchen-loesung.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - diff --git a/web/lessons/diagramme/en/ss-lineare-suche.visustruct b/web/lessons/diagramme/en/ss-lineare-suche.visustruct deleted file mode 100644 index 303acf52..00000000 --- a/web/lessons/diagramme/en/ss-lineare-suche.visustruct +++ /dev/null @@ -1,58 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;32;97;114;101;32;121;111;117;32;108;111;111;107;105;110;103;32;102;111;114;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;105;115;111;110;115;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;111;109;112;97;114;105;115;111;110;115;32;61;32;99;111;109;112;97;114;105;115;111;110;115;32;43;32;49; - - - 99;97;114;100;115;91;105;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;70;111;117;110;100;32;97;116;32;112;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;105;115;111;110;115;58;32;34;32;43;32;99;111;109;112;97;114;105;115;111;110;115; - - diff --git a/web/lessons/diagramme/en/uebergabe-bonus-loesung.visustruct b/web/lessons/diagramme/en/uebergabe-bonus-loesung.visustruct deleted file mode 100644 index d6f52036..00000000 --- a/web/lessons/diagramme/en/uebergabe-bonus-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;105;110;116;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;105;110;116;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/uebergabe-bonus-start.visustruct b/web/lessons/diagramme/en/uebergabe-bonus-start.visustruct deleted file mode 100644 index d6f52036..00000000 --- a/web/lessons/diagramme/en/uebergabe-bonus-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;105;110;116;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;105;110;116;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/uebergabe-bonus-unterprogramm-loesung.visustruct b/web/lessons/diagramme/en/uebergabe-bonus-unterprogramm-loesung.visustruct deleted file mode 100644 index 905cf79e..00000000 --- a/web/lessons/diagramme/en/uebergabe-bonus-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 112;111;105;110;116;115;91;105;93;32;61;32;112;111;105;110;116;115;91;105;93;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/en/uebergabe-haupt.visustruct b/web/lessons/diagramme/en/uebergabe-haupt.visustruct deleted file mode 100644 index bff4adbe..00000000 --- a/web/lessons/diagramme/en/uebergabe-haupt.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;53; - - - 100;111;117;98;108;101;73;116;40;110;117;109;98;101;114;41; - - - 111;117;116;112;117;116;58;32;34;73;110;32;116;104;101;32;109;97;105;110;32;112;114;111;103;114;97;109;58;32;110;117;109;98;101;114;32;61;32;34;32;43;32;110;117;109;98;101;114; - - - 98;111;111;108;101;97;110;91;93;32;114;111;119;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;66;101;102;111;114;101;58;32;34;32;43;32;114;111;119;91;48;93;32;43;32;34;44;32;34;32;43;32;114;111;119;91;49;93; - - - 99;108;101;97;114;40;114;111;119;41; - - - 111;117;116;112;117;116;58;32;34;65;102;116;101;114;58;32;34;32;43;32;114;111;119;91;48;93;32;43;32;34;44;32;34;32;43;32;114;111;119;91;49;93; - - diff --git a/web/lessons/diagramme/en/uebergabe-leeren.visustruct b/web/lessons/diagramme/en/uebergabe-leeren.visustruct deleted file mode 100644 index 1cbf7323..00000000 --- a/web/lessons/diagramme/en/uebergabe-leeren.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;101;97;116;115;91;105;93;32;61;32;102;97;108;115;101; - - - - diff --git a/web/lessons/diagramme/en/uebergabe-verdopple.visustruct b/web/lessons/diagramme/en/uebergabe-verdopple.visustruct deleted file mode 100644 index 61f5ad5a..00000000 --- a/web/lessons/diagramme/en/uebergabe-verdopple.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;73;110;32;116;104;101;32;115;117;98;112;114;111;103;114;97;109;58;32;120;32;61;32;34;32;43;32;120; - - diff --git a/web/lessons/diagramme/en/vb-abstand-anzeigen-loesung.visustruct b/web/lessons/diagramme/en/vb-abstand-anzeigen-loesung.visustruct deleted file mode 100644 index 62a97ee4..00000000 --- a/web/lessons/diagramme/en/vb-abstand-anzeigen-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-abstand-halten-loesung.visustruct b/web/lessons/diagramme/en/vb-abstand-halten-loesung.visustruct deleted file mode 100644 index 4f13f463..00000000 --- a/web/lessons/diagramme/en/vb-abstand-halten-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/vb-alarmanlage-loesung.visustruct b/web/lessons/diagramme/en/vb-alarmanlage-loesung.visustruct deleted file mode 100644 index 7b9743b1..00000000 --- a/web/lessons/diagramme/en/vb-alarmanlage-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-ampel-loesung.visustruct b/web/lessons/diagramme/en/vb-ampel-loesung.visustruct deleted file mode 100644 index 90df2995..00000000 --- a/web/lessons/diagramme/en/vb-ampel-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - diff --git a/web/lessons/diagramme/en/vb-ampel-zustand-loesung.visustruct b/web/lessons/diagramme/en/vb-ampel-zustand-loesung.visustruct deleted file mode 100644 index 3512436d..00000000 --- a/web/lessons/diagramme/en/vb-ampel-zustand-loesung.visustruct +++ /dev/null @@ -1,43 +0,0 @@ - - - - 105;110;116;32;115;116;97;116;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;49; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;50; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;48; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-ausgang-loesung.visustruct b/web/lessons/diagramme/en/vb-ausgang-loesung.visustruct deleted file mode 100644 index 1cd50df8..00000000 --- a/web/lessons/diagramme/en/vb-ausgang-loesung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-begruessung-loesung.visustruct b/web/lessons/diagramme/en/vb-begruessung-loesung.visustruct deleted file mode 100644 index 910b16f6..00000000 --- a/web/lessons/diagramme/en/vb-begruessung-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;72;101;108;108;111;33;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;32;97;109;32;86;105;115;117;66;111;116;34;41; - - diff --git a/web/lessons/diagramme/en/vb-binaer-zaehlen-loesung.visustruct b/web/lessons/diagramme/en/vb-binaer-zaehlen-loesung.visustruct deleted file mode 100644 index 19d58d84..00000000 --- a/web/lessons/diagramme/en/vb-binaer-zaehlen-loesung.visustruct +++ /dev/null @@ -1,52 +0,0 @@ - - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - 110;117;109;98;101;114;32;60;32;56; - 110;117;109;98;101;114;43;43; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 110;117;109;98;101;114;32;47;32;50;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 110;117;109;98;101;114;32;47;32;52;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-bis-rot-loesung.visustruct b/web/lessons/diagramme/en/vb-bis-rot-loesung.visustruct deleted file mode 100644 index 6d8842c9..00000000 --- a/web/lessons/diagramme/en/vb-bis-rot-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-bis-wand-loesung.visustruct b/web/lessons/diagramme/en/vb-bis-wand-loesung.visustruct deleted file mode 100644 index 7ccb357f..00000000 --- a/web/lessons/diagramme/en/vb-bis-wand-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-bis-ziel-loesung.visustruct b/web/lessons/diagramme/en/vb-bis-ziel-loesung.visustruct deleted file mode 100644 index ba017755..00000000 --- a/web/lessons/diagramme/en/vb-bis-ziel-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-buchstaben-loesung.visustruct b/web/lessons/diagramme/en/vb-buchstaben-loesung.visustruct deleted file mode 100644 index c5eb152e..00000000 --- a/web/lessons/diagramme/en/vb-buchstaben-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;86;105;115;117;66;111;116;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;48;44;32;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;51;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-do-while-loesung.visustruct b/web/lessons/diagramme/en/vb-do-while-loesung.visustruct deleted file mode 100644 index 8e0323b6..00000000 --- a/web/lessons/diagramme/en/vb-do-while-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-drei-lichter-loesung.visustruct b/web/lessons/diagramme/en/vb-drei-lichter-loesung.visustruct deleted file mode 100644 index 157f1b2c..00000000 --- a/web/lessons/diagramme/en/vb-drei-lichter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;121;101;108;108;111;119;34;41; - - - 109;98;111;116;46;108;101;100;40;52;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - diff --git a/web/lessons/diagramme/en/vb-dreieck-loesung.visustruct b/web/lessons/diagramme/en/vb-dreieck-loesung.visustruct deleted file mode 100644 index deeed0c6..00000000 --- a/web/lessons/diagramme/en/vb-dreieck-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-einparken-loesung.visustruct b/web/lessons/diagramme/en/vb-einparken-loesung.visustruct deleted file mode 100644 index c6b81b56..00000000 --- a/web/lessons/diagramme/en/vb-einparken-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;52;53; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;80;97;114;107;101;100;33;34;41; - - diff --git a/web/lessons/diagramme/en/vb-erster-plan-loesung.visustruct b/web/lessons/diagramme/en/vb-erster-plan-loesung.visustruct deleted file mode 100644 index 582403ed..00000000 --- a/web/lessons/diagramme/en/vb-erster-plan-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/en/vb-farben-loesung.visustruct b/web/lessons/diagramme/en/vb-farben-loesung.visustruct deleted file mode 100644 index 538a2ee9..00000000 --- a/web/lessons/diagramme/en/vb-farben-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;99;111;108;111;117;114;115;32;61;32;123;34;114;101;100;34;44;32;34;121;101;108;108;111;119;34;44;32;34;103;114;101;101;110;34;44;32;34;98;108;117;101;34;44;32;34;112;117;114;112;108;101;34;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;111;108;111;117;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;99;111;108;111;117;114;115;91;105;93;41; - - - - diff --git a/web/lessons/diagramme/en/vb-farbweiche-loesung.visustruct b/web/lessons/diagramme/en/vb-farbweiche-loesung.visustruct deleted file mode 100644 index 150365b3..00000000 --- a/web/lessons/diagramme/en/vb-farbweiche-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-04-sensoren-start.visustruct b/web/lessons/diagramme/en/vb-fehler-04-sensoren-start.visustruct deleted file mode 100644 index 0d30993a..00000000 --- a/web/lessons/diagramme/en/vb-fehler-04-sensoren-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;114;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-05-bis-zur-wand-start.visustruct b/web/lessons/diagramme/en/vb-fehler-05-bis-zur-wand-start.visustruct deleted file mode 100644 index ae5f42e4..00000000 --- a/web/lessons/diagramme/en/vb-fehler-05-bis-zur-wand-start.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;70;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct b/web/lessons/diagramme/en/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct deleted file mode 100644 index 623b3d9f..00000000 --- a/web/lessons/diagramme/en/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;83;116;101;112;115;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-05-zaehlen-und-summieren-start.visustruct b/web/lessons/diagramme/en/vb-fehler-05-zaehlen-und-summieren-start.visustruct deleted file mode 100644 index 5469aca6..00000000 --- a/web/lessons/diagramme/en/vb-fehler-05-zaehlen-und-summieren-start.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;49;48; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 116;111;116;97;108;32;61;32;84;111;116;97;108;32;43;32;115;105;100;101; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;116;111;116;97;108;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-06-abstand-start.visustruct b/web/lessons/diagramme/en/vb-fehler-06-abstand-start.visustruct deleted file mode 100644 index 1c84e7f1..00000000 --- a/web/lessons/diagramme/en/vb-fehler-06-abstand-start.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-06-konstanten-final-start.visustruct b/web/lessons/diagramme/en/vb-fehler-06-konstanten-final-start.visustruct deleted file mode 100644 index e8778244..00000000 --- a/web/lessons/diagramme/en/vb-fehler-06-konstanten-final-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;83;73;68;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-06-nah-mittel-weit-start.visustruct b/web/lessons/diagramme/en/vb-fehler-06-nah-mittel-weit-start.visustruct deleted file mode 100644 index da7d550f..00000000 --- a/web/lessons/diagramme/en/vb-fehler-06-nah-mittel-weit-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;78;105;103;104;116;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;117;115;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;97;121;34;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-07-linie-start.visustruct b/web/lessons/diagramme/en/vb-fehler-07-linie-start.visustruct deleted file mode 100644 index d1f2f64e..00000000 --- a/web/lessons/diagramme/en/vb-fehler-07-linie-start.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;83;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;116;101;112;115; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-07-und-oder-start.visustruct b/web/lessons/diagramme/en/vb-fehler-07-und-oder-start.visustruct deleted file mode 100644 index 95f7f458..00000000 --- a/web/lessons/diagramme/en/vb-fehler-07-und-oder-start.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;116;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-08-farben-start.visustruct b/web/lessons/diagramme/en/vb-fehler-08-farben-start.visustruct deleted file mode 100644 index 841ab1fa..00000000 --- a/web/lessons/diagramme/en/vb-fehler-08-farben-start.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;103;114;101;101;110;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-08-groesster-wert-start.visustruct b/web/lessons/diagramme/en/vb-fehler-08-groesster-wert-start.visustruct deleted file mode 100644 index d795bd53..00000000 --- a/web/lessons/diagramme/en/vb-fehler-08-groesster-wert-start.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;100;105;114;101;99;116;105;111;110;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - 100;105;114;101;99;116;105;111;110;32;61;32;73; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;114;101;99;116;105;111;110;58;32;34;32;43;32;100;105;114;101;99;116;105;111;110;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-09-fallauswahl-mbot2-start.visustruct b/web/lessons/diagramme/en/vb-fehler-09-fallauswahl-mbot2-start.visustruct deleted file mode 100644 index 7e04b84f..00000000 --- a/web/lessons/diagramme/en/vb-fehler-09-fallauswahl-mbot2-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;116;104;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;114;111;115;115;32;108;105;110;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-10-tasten-joystick-start.visustruct b/web/lessons/diagramme/en/vb-fehler-10-tasten-joystick-start.visustruct deleted file mode 100644 index 6529b132..00000000 --- a/web/lessons/diagramme/en/vb-fehler-10-tasten-joystick-start.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;66;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-10-unterbrechen-mit-break-start.visustruct b/web/lessons/diagramme/en/vb-fehler-10-unterbrechen-mit-break-start.visustruct deleted file mode 100644 index badfbbd5..00000000 --- a/web/lessons/diagramme/en/vb-fehler-10-unterbrechen-mit-break-start.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;83;116;111;112;112;101;100;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-11-licht-start.visustruct b/web/lessons/diagramme/en/vb-fehler-11-licht-start.visustruct deleted file mode 100644 index b9a15503..00000000 --- a/web/lessons/diagramme/en/vb-fehler-11-licht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-11-merker-boolean-start.visustruct b/web/lessons/diagramme/en/vb-fehler-11-merker-boolean-start.visustruct deleted file mode 100644 index f1b27d79..00000000 --- a/web/lessons/diagramme/en/vb-fehler-11-merker-boolean-start.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;108;111;117;100;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;111;117;100;32;61;32;84;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;111;117;100; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;108;97;112;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;32;113;117;105;101;116;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-12-lautstaerke-start.visustruct b/web/lessons/diagramme/en/vb-fehler-12-lautstaerke-start.visustruct deleted file mode 100644 index acc3ba5d..00000000 --- a/web/lessons/diagramme/en/vb-fehler-12-lautstaerke-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;111;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-13-neigung-start.visustruct b/web/lessons/diagramme/en/vb-fehler-13-neigung-start.visustruct deleted file mode 100644 index 23bd3839..00000000 --- a/web/lessons/diagramme/en/vb-fehler-13-neigung-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;108;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-14-leds-start.visustruct b/web/lessons/diagramme/en/vb-fehler-14-leds-start.visustruct deleted file mode 100644 index 6806f5d0..00000000 --- a/web/lessons/diagramme/en/vb-fehler-14-leds-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;80;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-15-einzelne-leds-start.visustruct b/web/lessons/diagramme/en/vb-fehler-15-einzelne-leds-start.visustruct deleted file mode 100644 index ae5fd96d..00000000 --- a/web/lessons/diagramme/en/vb-fehler-15-einzelne-leds-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;73;41;46;115;101;116;67;111;108;111;114;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-16-lautsprecher-start.visustruct b/web/lessons/diagramme/en/vb-fehler-16-lautsprecher-start.visustruct deleted file mode 100644 index 48b53da8..00000000 --- a/web/lessons/diagramme/en/vb-fehler-16-lautsprecher-start.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-17-arrays-melodie-start.visustruct b/web/lessons/diagramme/en/vb-fehler-17-arrays-melodie-start.visustruct deleted file mode 100644 index 0d59e016..00000000 --- a/web/lessons/diagramme/en/vb-fehler-17-arrays-melodie-start.visustruct +++ /dev/null @@ -1,38 +0,0 @@ - - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 118;97;108;117;101;115;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;86;97;108;117;101;115;91;105;93; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-18-bildschirm-start.visustruct b/web/lessons/diagramme/en/vb-fehler-18-bildschirm-start.visustruct deleted file mode 100644 index 08edc257..00000000 --- a/web/lessons/diagramme/en/vb-fehler-18-bildschirm-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;73;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-19-zeichenketten-start.visustruct b/web/lessons/diagramme/en/vb-fehler-19-zeichenketten-start.visustruct deleted file mode 100644 index 771732b2..00000000 --- a/web/lessons/diagramme/en/vb-fehler-19-zeichenketten-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;101;108;108;111;32;99;108;97;115;115;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;84;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-20-motoren-start.visustruct b/web/lessons/diagramme/en/vb-fehler-20-motoren-start.visustruct deleted file mode 100644 index 629c5ddd..00000000 --- a/web/lessons/diagramme/en/vb-fehler-20-motoren-start.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;83;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/en/vb-fehler-21-stoppuhr-akku-start.visustruct b/web/lessons/diagramme/en/vb-fehler-21-stoppuhr-akku-start.visustruct deleted file mode 100644 index 75fb4d99..00000000 --- a/web/lessons/diagramme/en/vb-fehler-21-stoppuhr-akku-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;66;97;116;116;101;114;121;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-21-zustaende-start.visustruct b/web/lessons/diagramme/en/vb-fehler-21-zustaende-start.visustruct deleted file mode 100644 index 77445908..00000000 --- a/web/lessons/diagramme/en/vb-fehler-21-zustaende-start.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;114;101;100;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;103;114;101;101;110; - - - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;121;101;108;108;111;119;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;114;101;100;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-22-eigene-methoden-start.visustruct b/web/lessons/diagramme/en/vb-fehler-22-eigene-methoden-start.visustruct deleted file mode 100644 index 4188b4dd..00000000 --- a/web/lessons/diagramme/en/vb-fehler-22-eigene-methoden-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;114;111;116;97;116;105;111;110;115;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-fehler-23-wie-geht-es-weiter-start.visustruct b/web/lessons/diagramme/en/vb-fehler-23-wie-geht-es-weiter-start.visustruct deleted file mode 100644 index ef1117ee..00000000 --- a/web/lessons/diagramme/en/vb-fehler-23-wie-geht-es-weiter-start.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-gas-bis-wand-loesung.visustruct b/web/lessons/diagramme/en/vb-gas-bis-wand-loesung.visustruct deleted file mode 100644 index a65735d2..00000000 --- a/web/lessons/diagramme/en/vb-gas-bis-wand-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/en/vb-gerade-loesung.visustruct b/web/lessons/diagramme/en/vb-gerade-loesung.visustruct deleted file mode 100644 index 84b341ea..00000000 --- a/web/lessons/diagramme/en/vb-gerade-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/en/vb-hindernis-loesung.visustruct b/web/lessons/diagramme/en/vb-hindernis-loesung.visustruct deleted file mode 100644 index bf15d030..00000000 --- a/web/lessons/diagramme/en/vb-hindernis-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-java-akku-loesung.visustruct b/web/lessons/diagramme/en/vb-java-akku-loesung.visustruct deleted file mode 100644 index d32e7083..00000000 --- a/web/lessons/diagramme/en/vb-java-akku-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;66;97;116;116;101;114;121;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/en/vb-java-ampel-text-loesung.visustruct b/web/lessons/diagramme/en/vb-java-ampel-text-loesung.visustruct deleted file mode 100644 index 6eb4839c..00000000 --- a/web/lessons/diagramme/en/vb-java-ampel-text-loesung.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;114;101;100;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;103;114;101;101;110;34; - - - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;121;101;108;108;111;119;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;114;101;100;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-blau-gruen-loesung.visustruct b/web/lessons/diagramme/en/vb-java-blau-gruen-loesung.visustruct deleted file mode 100644 index b9e896a9..00000000 --- a/web/lessons/diagramme/en/vb-java-blau-gruen-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/en/vb-java-blinken-loesung.visustruct b/web/lessons/diagramme/en/vb-java-blinken-loesung.visustruct deleted file mode 100644 index 967e1494..00000000 --- a/web/lessons/diagramme/en/vb-java-blinken-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-countdown-loesung.visustruct b/web/lessons/diagramme/en/vb-java-countdown-loesung.visustruct deleted file mode 100644 index 5ddc447c..00000000 --- a/web/lessons/diagramme/en/vb-java-countdown-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;105;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - diff --git a/web/lessons/diagramme/en/vb-java-hoeher-loesung.visustruct b/web/lessons/diagramme/en/vb-java-hoeher-loesung.visustruct deleted file mode 100644 index d455d6ec..00000000 --- a/web/lessons/diagramme/en/vb-java-hoeher-loesung.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;44;32;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-joystick-loesung.visustruct b/web/lessons/diagramme/en/vb-java-joystick-loesung.visustruct deleted file mode 100644 index 979fa644..00000000 --- a/web/lessons/diagramme/en/vb-java-joystick-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/en/vb-java-klatsch-merker-loesung.visustruct b/web/lessons/diagramme/en/vb-java-klatsch-merker-loesung.visustruct deleted file mode 100644 index b609f3f5..00000000 --- a/web/lessons/diagramme/en/vb-java-klatsch-merker-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;108;111;117;100;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;111;117;100;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;111;117;100; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;108;97;112;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;32;113;117;105;101;116;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-konstante-seite-loesung.visustruct b/web/lessons/diagramme/en/vb-java-konstante-seite-loesung.visustruct deleted file mode 100644 index bccd6cdc..00000000 --- a/web/lessons/diagramme/en/vb-java-konstante-seite-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;83;73;68;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;83;73;68;69;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-laermampel-loesung.visustruct b/web/lessons/diagramme/en/vb-java-laermampel-loesung.visustruct deleted file mode 100644 index 6825f78b..00000000 --- a/web/lessons/diagramme/en/vb-java-laermampel-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-lauflicht-loesung.visustruct b/web/lessons/diagramme/en/vb-java-lauflicht-loesung.visustruct deleted file mode 100644 index 98246fc5..00000000 --- a/web/lessons/diagramme/en/vb-java-lauflicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-linienfolger-loesung.visustruct b/web/lessons/diagramme/en/vb-java-linienfolger-loesung.visustruct deleted file mode 100644 index 7458d58e..00000000 --- a/web/lessons/diagramme/en/vb-java-linienfolger-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/en/vb-java-nachtlicht-ton-loesung.visustruct b/web/lessons/diagramme/en/vb-java-nachtlicht-ton-loesung.visustruct deleted file mode 100644 index 8a99c683..00000000 --- a/web/lessons/diagramme/en/vb-java-nachtlicht-ton-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-rand-loesung.visustruct b/web/lessons/diagramme/en/vb-java-rand-loesung.visustruct deleted file mode 100644 index 3ee464e1..00000000 --- a/web/lessons/diagramme/en/vb-java-rand-loesung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;116;101;112;115; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-rechteck-loesung.visustruct b/web/lessons/diagramme/en/vb-java-rechteck-loesung.visustruct deleted file mode 100644 index 72754fe0..00000000 --- a/web/lessons/diagramme/en/vb-java-rechteck-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;54;48; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;101;110;103;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;119;105;100;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-rechteck-umdrehungen-loesung.visustruct b/web/lessons/diagramme/en/vb-java-rechteck-umdrehungen-loesung.visustruct deleted file mode 100644 index 8a8e8928..00000000 --- a/web/lessons/diagramme/en/vb-java-rechteck-umdrehungen-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-java-richtung-loesung.visustruct b/web/lessons/diagramme/en/vb-java-richtung-loesung.visustruct deleted file mode 100644 index 567aab66..00000000 --- a/web/lessons/diagramme/en/vb-java-richtung-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;100;105;114;101;99;116;105;111;110;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - 100;105;114;101;99;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;114;101;99;116;105;111;110;58;32;34;32;43;32;100;105;114;101;99;116;105;111;110;41; - - diff --git a/web/lessons/diagramme/en/vb-java-schritte-zaehlen-loesung.visustruct b/web/lessons/diagramme/en/vb-java-schritte-zaehlen-loesung.visustruct deleted file mode 100644 index e8a25d4d..00000000 --- a/web/lessons/diagramme/en/vb-java-schritte-zaehlen-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115;41; - - diff --git a/web/lessons/diagramme/en/vb-java-sicher-fahren-loesung.visustruct b/web/lessons/diagramme/en/vb-java-sicher-fahren-loesung.visustruct deleted file mode 100644 index 3c5b79f3..00000000 --- a/web/lessons/diagramme/en/vb-java-sicher-fahren-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-java-spirale-strecke-loesung.visustruct b/web/lessons/diagramme/en/vb-java-spirale-strecke-loesung.visustruct deleted file mode 100644 index 680ade9b..00000000 --- a/web/lessons/diagramme/en/vb-java-spirale-strecke-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;49;48; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;115;105;100;101; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;116;111;116;97;108;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-java-tageszeit-loesung.visustruct b/web/lessons/diagramme/en/vb-java-tageszeit-loesung.visustruct deleted file mode 100644 index 0cbbcf06..00000000 --- a/web/lessons/diagramme/en/vb-java-tageszeit-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;78;105;103;104;116;34;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;117;115;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;97;121;34;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-java-taste-stopp-loesung.visustruct b/web/lessons/diagramme/en/vb-java-taste-stopp-loesung.visustruct deleted file mode 100644 index fd1d4c96..00000000 --- a/web/lessons/diagramme/en/vb-java-taste-stopp-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;112;101;100;34;41; - - diff --git a/web/lessons/diagramme/en/vb-java-textlaenge-loesung.visustruct b/web/lessons/diagramme/en/vb-java-textlaenge-loesung.visustruct deleted file mode 100644 index 8316ea11..00000000 --- a/web/lessons/diagramme/en/vb-java-textlaenge-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;101;108;108;111;32;99;108;97;115;115;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;116;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - diff --git a/web/lessons/diagramme/en/vb-java-wand-piep-loesung.visustruct b/web/lessons/diagramme/en/vb-java-wand-piep-loesung.visustruct deleted file mode 100644 index 7e824d34..00000000 --- a/web/lessons/diagramme/en/vb-java-wand-piep-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-java-wasserwaage-loesung.visustruct b/web/lessons/diagramme/en/vb-java-wasserwaage-loesung.visustruct deleted file mode 100644 index 657b6703..00000000 --- a/web/lessons/diagramme/en/vb-java-wasserwaage-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-klatschen-loesung.visustruct b/web/lessons/diagramme/en/vb-klatschen-loesung.visustruct deleted file mode 100644 index e0a9bbd1..00000000 --- a/web/lessons/diagramme/en/vb-klatschen-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;60;32;53;48; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/en/vb-knight-rider-loesung.visustruct b/web/lessons/diagramme/en/vb-knight-rider-loesung.visustruct deleted file mode 100644 index 11023bfe..00000000 --- a/web/lessons/diagramme/en/vb-knight-rider-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;32;48; - 105;45;45; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-konstante-wand-loesung.visustruct b/web/lessons/diagramme/en/vb-konstante-wand-loesung.visustruct deleted file mode 100644 index a3fd1d85..00000000 --- a/web/lessons/diagramme/en/vb-konstante-wand-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;68;73;83;84;65;78;67;69;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-l00-bausteine-loesung.visustruct b/web/lessons/diagramme/en/vb-l00-bausteine-loesung.visustruct deleted file mode 100644 index 124f0ff6..00000000 --- a/web/lessons/diagramme/en/vb-l00-bausteine-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-l00-sequenz-loesung.visustruct b/web/lessons/diagramme/en/vb-l00-sequenz-loesung.visustruct deleted file mode 100644 index d6c8354c..00000000 --- a/web/lessons/diagramme/en/vb-l00-sequenz-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/en/vb-l03-wachsend-loesung.visustruct b/web/lessons/diagramme/en/vb-l03-wachsend-loesung.visustruct deleted file mode 100644 index 9f4154f1..00000000 --- a/web/lessons/diagramme/en/vb-l03-wachsend-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - diff --git a/web/lessons/diagramme/en/vb-l06k-mit.visustruct b/web/lessons/diagramme/en/vb-l06k-mit.visustruct deleted file mode 100644 index 36bfc964..00000000 --- a/web/lessons/diagramme/en/vb-l06k-mit.visustruct +++ /dev/null @@ -1,25 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;68;73;83;84;65;78;67;69;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-l06k-ohne.visustruct b/web/lessons/diagramme/en/vb-l06k-ohne.visustruct deleted file mode 100644 index 5bd24605..00000000 --- a/web/lessons/diagramme/en/vb-l06k-ohne.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-l06n-einparkhilfe.visustruct b/web/lessons/diagramme/en/vb-l06n-einparkhilfe.visustruct deleted file mode 100644 index 5a30edf7..00000000 --- a/web/lessons/diagramme/en/vb-l06n-einparkhilfe.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;34;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;108;111;119;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;114;101;101;34;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-l21z-ausweichen.visustruct b/web/lessons/diagramme/en/vb-l21z-ausweichen.visustruct deleted file mode 100644 index 6e91129f..00000000 --- a/web/lessons/diagramme/en/vb-l21z-ausweichen.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;100;114;105;118;101;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50;48; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;100;114;105;118;101;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 115;116;97;116;101;32;61;32;34;116;117;114;110;34; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;116;97;116;101;32;61;32;34;100;114;105;118;101;34; - - - - - - diff --git a/web/lessons/diagramme/en/vb-labyrinth-loesung.visustruct b/web/lessons/diagramme/en/vb-labyrinth-loesung.visustruct deleted file mode 100644 index ba2defc6..00000000 --- a/web/lessons/diagramme/en/vb-labyrinth-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/en/vb-laufschrift-loesung.visustruct b/web/lessons/diagramme/en/vb-laufschrift-loesung.visustruct deleted file mode 100644 index bb8c77d1..00000000 --- a/web/lessons/diagramme/en/vb-laufschrift-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;32;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;100;46;99;104;97;114;65;116;40;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-linienfolger-stopp-loesung.visustruct b/web/lessons/diagramme/en/vb-linienfolger-stopp-loesung.visustruct deleted file mode 100644 index 8e5c7464..00000000 --- a/web/lessons/diagramme/en/vb-linienfolger-stopp-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/en/vb-linienmuster-loesung.visustruct b/web/lessons/diagramme/en/vb-linienmuster-loesung.visustruct deleted file mode 100644 index c25e550b..00000000 --- a/web/lessons/diagramme/en/vb-linienmuster-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;114;111;115;115;32;108;105;110;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - diff --git a/web/lessons/diagramme/en/vb-meister-platz-loesung.visustruct b/web/lessons/diagramme/en/vb-meister-platz-loesung.visustruct deleted file mode 100644 index 353b2534..00000000 --- a/web/lessons/diagramme/en/vb-meister-platz-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;97;114;116;104;101;115;116;58;32;34;32;43;32;109;97;120;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-melodie-loesung.visustruct b/web/lessons/diagramme/en/vb-melodie-loesung.visustruct deleted file mode 100644 index fe45889f..00000000 --- a/web/lessons/diagramme/en/vb-melodie-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;91;93;32;116;111;110;101;115;32;61;32;123;50;54;50;44;32;50;57;52;44;32;51;51;48;44;32;51;52;57;44;32;51;57;50;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;111;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;116;111;110;101;115;91;105;93;44;32;51;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-messreihe-loesung.visustruct b/web/lessons/diagramme/en/vb-messreihe-loesung.visustruct deleted file mode 100644 index 2698ac48..00000000 --- a/web/lessons/diagramme/en/vb-messreihe-loesung.visustruct +++ /dev/null @@ -1,38 +0,0 @@ - - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 118;97;108;117;101;115;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;118;97;108;117;101;115;91;105;93; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;41; - - diff --git a/web/lessons/diagramme/en/vb-morsen-loesung.visustruct b/web/lessons/diagramme/en/vb-morsen-loesung.visustruct deleted file mode 100644 index baac3335..00000000 --- a/web/lessons/diagramme/en/vb-morsen-loesung.visustruct +++ /dev/null @@ -1,66 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;54;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/en/vb-nachtlicht-loesung.visustruct b/web/lessons/diagramme/en/vb-nachtlicht-loesung.visustruct deleted file mode 100644 index a949bf3e..00000000 --- a/web/lessons/diagramme/en/vb-nachtlicht-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;53;53;44;32;50;53;53;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-nachtwaechter-loesung.visustruct b/web/lessons/diagramme/en/vb-nachtwaechter-loesung.visustruct deleted file mode 100644 index cc286ed3..00000000 --- a/web/lessons/diagramme/en/vb-nachtwaechter-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;90;122;122;34;41; - - diff --git a/web/lessons/diagramme/en/vb-notbremse-loesung.visustruct b/web/lessons/diagramme/en/vb-notbremse-loesung.visustruct deleted file mode 100644 index 990cf19a..00000000 --- a/web/lessons/diagramme/en/vb-notbremse-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-oder-loesung.visustruct b/web/lessons/diagramme/en/vb-oder-loesung.visustruct deleted file mode 100644 index 85324cdf..00000000 --- a/web/lessons/diagramme/en/vb-oder-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41;32;124;124;32;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/en/vb-quadrat-loesung.visustruct b/web/lessons/diagramme/en/vb-quadrat-loesung.visustruct deleted file mode 100644 index bec34a5a..00000000 --- a/web/lessons/diagramme/en/vb-quadrat-loesung.visustruct +++ /dev/null @@ -1,75 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - diff --git a/web/lessons/diagramme/en/vb-quadrat-methode-loesung.visustruct b/web/lessons/diagramme/en/vb-quadrat-methode-loesung.visustruct deleted file mode 100644 index c8f98385..00000000 --- a/web/lessons/diagramme/en/vb-quadrat-methode-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 115;113;117;97;114;101;40;51;48;41; - - - 115;113;117;97;114;101;40;53;48;41; - - diff --git a/web/lessons/diagramme/en/vb-quadrat-schleife-loesung.visustruct b/web/lessons/diagramme/en/vb-quadrat-schleife-loesung.visustruct deleted file mode 100644 index b3d6af97..00000000 --- a/web/lessons/diagramme/en/vb-quadrat-schleife-loesung.visustruct +++ /dev/null @@ -1,55 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - diff --git a/web/lessons/diagramme/en/vb-quadrat-unterprogramm-loesung.visustruct b/web/lessons/diagramme/en/vb-quadrat-unterprogramm-loesung.visustruct deleted file mode 100644 index ccc90b31..00000000 --- a/web/lessons/diagramme/en/vb-quadrat-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-quadrat-verschachtelt-loesung.visustruct b/web/lessons/diagramme/en/vb-quadrat-verschachtelt-loesung.visustruct deleted file mode 100644 index e0f4e19a..00000000 --- a/web/lessons/diagramme/en/vb-quadrat-verschachtelt-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;53; - 106;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-rand-loesung.visustruct b/web/lessons/diagramme/en/vb-rand-loesung.visustruct deleted file mode 100644 index c1c0ac7e..00000000 --- a/web/lessons/diagramme/en/vb-rand-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - diff --git a/web/lessons/diagramme/en/vb-schraeglage-loesung.visustruct b/web/lessons/diagramme/en/vb-schraeglage-loesung.visustruct deleted file mode 100644 index 0ef75ac0..00000000 --- a/web/lessons/diagramme/en/vb-schraeglage-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;45;50;48; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;51;48;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/en/vb-schritte-welt-loesung.visustruct b/web/lessons/diagramme/en/vb-schritte-welt-loesung.visustruct deleted file mode 100644 index 63fa86cd..00000000 --- a/web/lessons/diagramme/en/vb-schritte-welt-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115;41; - - diff --git a/web/lessons/diagramme/en/vb-slalom-loesung.visustruct b/web/lessons/diagramme/en/vb-slalom-loesung.visustruct deleted file mode 100644 index 885ff4a4..00000000 --- a/web/lessons/diagramme/en/vb-slalom-loesung.visustruct +++ /dev/null @@ -1,52 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-spirale-falsch-loesung.visustruct b/web/lessons/diagramme/en/vb-spirale-falsch-loesung.visustruct deleted file mode 100644 index 9f4154f1..00000000 --- a/web/lessons/diagramme/en/vb-spirale-falsch-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - diff --git a/web/lessons/diagramme/en/vb-spirale-falsch-start.visustruct b/web/lessons/diagramme/en/vb-spirale-falsch-start.visustruct deleted file mode 100644 index b1826439..00000000 --- a/web/lessons/diagramme/en/vb-spirale-falsch-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-spirale-loesung.visustruct b/web/lessons/diagramme/en/vb-spirale-loesung.visustruct deleted file mode 100644 index 7f972ce7..00000000 --- a/web/lessons/diagramme/en/vb-spirale-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;56; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;105;32;42;32;49;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-start-a-loesung.visustruct b/web/lessons/diagramme/en/vb-start-a-loesung.visustruct deleted file mode 100644 index 1f22c3d1..00000000 --- a/web/lessons/diagramme/en/vb-start-a-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/en/vb-startsignal-loesung.visustruct b/web/lessons/diagramme/en/vb-startsignal-loesung.visustruct deleted file mode 100644 index 8dbf8eaf..00000000 --- a/web/lessons/diagramme/en/vb-startsignal-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;61;32;51; - 114;117;110;100;101;43;43; - - - 114;117;110;100;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;79;110;32;121;111;117;114;32;109;97;114;107;115;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;116;32;115;101;116;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - diff --git a/web/lessons/diagramme/en/vb-strecke-anzeigen-loesung.visustruct b/web/lessons/diagramme/en/vb-strecke-anzeigen-loesung.visustruct deleted file mode 100644 index 39aa8985..00000000 --- a/web/lessons/diagramme/en/vb-strecke-anzeigen-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;54;48; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;101;110;103;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;119;105;100;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;50;32;42;32;108;101;110;103;116;104;32;43;32;50;32;42;32;119;105;100;116;104; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-tempo-abstand-loesung.visustruct b/web/lessons/diagramme/en/vb-tempo-abstand-loesung.visustruct deleted file mode 100644 index 5e76eef7..00000000 --- a/web/lessons/diagramme/en/vb-tempo-abstand-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-tonleiter-loesung.visustruct b/web/lessons/diagramme/en/vb-tonleiter-loesung.visustruct deleted file mode 100644 index ab95a780..00000000 --- a/web/lessons/diagramme/en/vb-tonleiter-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;54;50;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;57;52;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;51;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;52;57;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;57;50;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/en/vb-treppe-loesung.visustruct b/web/lessons/diagramme/en/vb-treppe-loesung.visustruct deleted file mode 100644 index 40845b7e..00000000 --- a/web/lessons/diagramme/en/vb-treppe-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-tunnel-loesung.visustruct b/web/lessons/diagramme/en/vb-tunnel-loesung.visustruct deleted file mode 100644 index 7e6d8fe8..00000000 --- a/web/lessons/diagramme/en/vb-tunnel-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;32;108;101;102;116;32;61;32;57;57; - - - 105;110;116;32;114;105;103;104;116;32;61;32;57;57; - - - 108;101;102;116;32;62;61;32;50;48;32;124;124;32;114;105;103;104;116;32;62;61;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 108;101;102;116;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 114;105;103;104;116;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-ueberladung-umdrehung-loesung.visustruct b/web/lessons/diagramme/en/vb-ueberladung-umdrehung-loesung.visustruct deleted file mode 100644 index 1ca9ed45..00000000 --- a/web/lessons/diagramme/en/vb-ueberladung-umdrehung-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-und-loesung.visustruct b/web/lessons/diagramme/en/vb-und-loesung.visustruct deleted file mode 100644 index 975a49dc..00000000 --- a/web/lessons/diagramme/en/vb-und-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/en/vb-variable-seite-loesung.visustruct b/web/lessons/diagramme/en/vb-variable-seite-loesung.visustruct deleted file mode 100644 index cc5a5268..00000000 --- a/web/lessons/diagramme/en/vb-variable-seite-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;115;105;100;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-vier-quadrate-loesung.visustruct b/web/lessons/diagramme/en/vb-vier-quadrate-loesung.visustruct deleted file mode 100644 index f0dafc40..00000000 --- a/web/lessons/diagramme/en/vb-vier-quadrate-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;52; - 106;43;43; - - - 105;110;116;32;107;32;61;32;48; - 107;32;60;32;53; - 107;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-wald-loesung.visustruct b/web/lessons/diagramme/en/vb-wald-loesung.visustruct deleted file mode 100644 index 0de1caa5..00000000 --- a/web/lessons/diagramme/en/vb-wald-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/en/vb-wand-vorn-loesung.visustruct b/web/lessons/diagramme/en/vb-wand-vorn-loesung.visustruct deleted file mode 100644 index 07622693..00000000 --- a/web/lessons/diagramme/en/vb-wand-vorn-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/en/vb-wurde-es-dunkel-loesung.visustruct b/web/lessons/diagramme/en/vb-wurde-es-dunkel-loesung.visustruct deleted file mode 100644 index 68ae8a5c..00000000 --- a/web/lessons/diagramme/en/vb-wurde-es-dunkel-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;100;97;114;107;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 100;97;114;107;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 100;97;114;107; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;116;32;119;97;115;32;100;97;114;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;119;97;121;115;32;98;114;105;103;104;116;34;41; - - - - diff --git a/web/lessons/diagramme/en/vb-zentimeter-loesung.visustruct b/web/lessons/diagramme/en/vb-zentimeter-loesung.visustruct deleted file mode 100644 index 56306e9b..00000000 --- a/web/lessons/diagramme/en/vb-zentimeter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;99;101;110;116;105;109;101;116;114;101;115;40;51;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;100;105;115;116;97;110;99;101;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/en/vb-zentimeter-unterprogramm-loesung.visustruct b/web/lessons/diagramme/en/vb-zentimeter-unterprogramm-loesung.visustruct deleted file mode 100644 index 983acbf4..00000000 --- a/web/lessons/diagramme/en/vb-zentimeter-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 114;101;116;117;114;110;32;114;111;116;97;116;105;111;110;115;32;42;32;50;48; - - diff --git a/web/lessons/diagramme/en/vb-zwei-sekunden-loesung.visustruct b/web/lessons/diagramme/en/vb-zwei-sekunden-loesung.visustruct deleted file mode 100644 index 68d640e4..00000000 --- a/web/lessons/diagramme/en/vb-zwei-sekunden-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 109;98;111;116;46;114;101;115;101;116;84;105;109;101;114;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;84;105;109;101;114;40;41;32;60;32;50; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/en/zk-initialen-loesung.visustruct b/web/lessons/diagramme/en/zk-initialen-loesung.visustruct deleted file mode 100644 index b29508f7..00000000 --- a/web/lessons/diagramme/en/zk-initialen-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;78;97;109;101;32;34;70;105;114;115;116;32;110;97;109;101;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;78;97;109;101;32;34;76;97;115;116;32;110;97;109;101;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;78;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;78;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/en/zk-mehr.visustruct b/web/lessons/diagramme/en/zk-mehr.visustruct deleted file mode 100644 index de73dfda..00000000 --- a/web/lessons/diagramme/en/zk-mehr.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;66;105;108;108;121;103;111;97;116;34; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;110;100;101;120;79;102;40;34;108;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;108;97;115;116;73;110;100;101;120;79;102;40;34;108;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;66;73;76;76;89;71;79;65;84;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;114;101;112;108;97;99;101;40;34;66;105;108;108;121;34;44;32;34;78;97;110;110;121;34;41; - - - 111;117;116;112;117;116;58;32;34;66;97;97;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;97;103;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;97;103;101;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;97;103;101;41;32;43;32;34;32;121;101;97;114;115;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - diff --git a/web/lessons/diagramme/en/zk-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/en/zk-rueckwaerts-loesung.visustruct deleted file mode 100644 index 19cb5586..00000000 --- a/web/lessons/diagramme/en/zk-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;69;110;116;101;114;32;97;32;119;111;114;100;34; - - - 83;116;114;105;110;103;32;98;97;99;107;119;97;114;100;115;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 98;97;99;107;119;97;114;100;115;32;61;32;98;97;99;107;119;97;114;100;115;32;43;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;98;97;99;107;119;97;114;100;115; - - diff --git a/web/lessons/diagramme/en/zk-split.visustruct b/web/lessons/diagramme/en/zk-split.visustruct deleted file mode 100644 index 72e0d231..00000000 --- a/web/lessons/diagramme/en/zk-split.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;76;117;100;119;105;103;59;98;105;108;108;121;32;103;111;97;116;59;55;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;114;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;112;97;114;116;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/en/zk-woerter-loesung.visustruct b/web/lessons/diagramme/en/zk-woerter-loesung.visustruct deleted file mode 100644 index 7a3ec793..00000000 --- a/web/lessons/diagramme/en/zk-woerter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;110;116;101;110;99;101;32;34;69;110;116;101;114;32;97;32;115;101;110;116;101;110;99;101;34; - - - 83;116;114;105;110;103;91;93;32;119;111;114;100;115;32;61;32;115;101;110;116;101;110;99;101;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;87;111;114;100;115;58;32;34;32;43;32;119;111;114;100;115;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/en/zk-zaehlen.visustruct b/web/lessons/diagramme/en/zk-zaehlen.visustruct deleted file mode 100644 index 353fc38e..00000000 --- a/web/lessons/diagramme/en/zk-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;69;110;116;101;114;32;97;32;119;111;114;100;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 119;111;114;100;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;101;39; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;78;117;109;98;101;114;32;111;102;32;101;58;32;34;32;43;32;99;111;117;110;116; - - diff --git a/web/lessons/diagramme/en/zk-zeichen.visustruct b/web/lessons/diagramme/en/zk-zeichen.visustruct deleted file mode 100644 index 2801bbde..00000000 --- a/web/lessons/diagramme/en/zk-zeichen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;66;105;108;108;121;103;111;97;116;34; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;48;44;32;53;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;53;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;110;100;101;120;79;102;40;34;103;111;97;116;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - diff --git a/web/lessons/diagramme/kino-einmaleins-loesung.visustruct b/web/lessons/diagramme/kino-einmaleins-loesung.visustruct deleted file mode 100644 index f7549528..00000000 --- a/web/lessons/diagramme/kino-einmaleins-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;116;97;98;101;108;108;101;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;101;108;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;101;108;108;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;101;108;108;101;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;32;40;49;32;98;105;115;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;32;40;49;32;98;105;115;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;101;108;108;101;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/kino-frei-loesung.visustruct b/web/lessons/diagramme/kino-frei-loesung.visustruct deleted file mode 100644 index da83706e..00000000 --- a/web/lessons/diagramme/kino-frei-loesung.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;114;101;105;32;61;32;48; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;115;105;116;122;101;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 105;110;116;32;112;108;97;116;122;32;61;32;48; - 112;108;97;116;122;32;60;32;115;105;116;122;101;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 112;108;97;116;122;43;43; - - - 33;115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 102;114;101;105;32;61;32;102;114;101;105;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;105;58;32;34;32;43;32;102;114;101;105; - - diff --git a/web/lessons/diagramme/kino-frei-start.visustruct b/web/lessons/diagramme/kino-frei-start.visustruct deleted file mode 100644 index 566b4520..00000000 --- a/web/lessons/diagramme/kino-frei-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;50;93;91;51;93;32;61;32;116;114;117;101; - - diff --git a/web/lessons/diagramme/kino-karte-loesung.visustruct b/web/lessons/diagramme/kino-karte-loesung.visustruct deleted file mode 100644 index d8d27e81..00000000 --- a/web/lessons/diagramme/kino-karte-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;82;101;105;104;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;112;108;97;116;122;32;34;80;108;97;116;122;34; - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 111;117;116;112;117;116;58;32;34;83;99;104;111;110;32;118;101;114;107;97;117;102;116;34; - - - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;86;101;114;107;97;117;102;116;58;32;82;101;105;104;101;32;34;32;43;32;114;101;105;104;101;32;43;32;34;44;32;80;108;97;116;122;32;34;32;43;32;112;108;97;116;122; - - - - diff --git a/web/lessons/diagramme/kino-karte-start.visustruct b/web/lessons/diagramme/kino-karte-start.visustruct deleted file mode 100644 index f9a257e6..00000000 --- a/web/lessons/diagramme/kino-karte-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;82;101;105;104;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;112;108;97;116;122;32;34;80;108;97;116;122;34; - - diff --git a/web/lessons/diagramme/kino-saalplan.visustruct b/web/lessons/diagramme/kino-saalplan.visustruct deleted file mode 100644 index 6898beaa..00000000 --- a/web/lessons/diagramme/kino-saalplan.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;115;105;116;122;101;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;34; - - - 105;110;116;32;112;108;97;116;122;32;61;32;48; - 112;108;97;116;122;32;60;32;115;105;116;122;101;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 112;108;97;116;122;43;43; - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;88;34; - - - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - diff --git a/web/lessons/diagramme/kino-verkaufen.visustruct b/web/lessons/diagramme/kino-verkaufen.visustruct deleted file mode 100644 index cf1d33d5..00000000 --- a/web/lessons/diagramme/kino-verkaufen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;82;101;105;104;101;110;58;32;34;32;43;32;115;105;116;122;101;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;80;108;228;116;122;101;32;112;114;111;32;82;101;105;104;101;58;32;34;32;43;32;115;105;116;122;101;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;82;101;105;104;101;32;49;44;32;80;108;97;116;122;32;50;32;118;101;114;107;97;117;102;116;63;32;34;32;43;32;115;105;116;122;101;91;49;93;91;50;93; - - diff --git a/web/lessons/diagramme/l04-gross-klein-loesung.visustruct b/web/lessons/diagramme/l04-gross-klein-loesung.visustruct deleted file mode 100644 index e2f906ab..00000000 --- a/web/lessons/diagramme/l04-gross-klein-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 105;110;116;32;90;97;104;108;32;61;32;54; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;90;97;104;108; - - - 111;117;116;112;117;116;58;32;34;69;114;103;101;98;110;105;115;58;32;34;32;43;32;101;114;103;101;98;110;105;115; - - diff --git a/web/lessons/diagramme/l04-gross-klein-start.visustruct b/web/lessons/diagramme/l04-gross-klein-start.visustruct deleted file mode 100644 index 6888d8ea..00000000 --- a/web/lessons/diagramme/l04-gross-klein-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 105;110;116;32;90;97;104;108;32;61;32;54; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;90;65;72;76; - - - 111;117;116;112;117;116;58;32;34;69;114;103;101;98;110;105;115;58;32;34;32;43;32;101;114;103;101;98;110;105;115; - - diff --git a/web/lessons/diagramme/l04-java-fehler-loesung.visustruct b/web/lessons/diagramme/l04-java-fehler-loesung.visustruct deleted file mode 100644 index d3380a78..00000000 --- a/web/lessons/diagramme/l04-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/l04-java-fehler-start.visustruct b/web/lessons/diagramme/l04-java-fehler-start.visustruct deleted file mode 100644 index fdd86a60..00000000 --- a/web/lessons/diagramme/l04-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/l04-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l04-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 28528731..00000000 --- a/web/lessons/diagramme/l04-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - diff --git a/web/lessons/diagramme/l04-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l04-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 0b0c5f6a..00000000 --- a/web/lessons/diagramme/l04-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;65;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34;41; - - diff --git a/web/lessons/diagramme/l04-java-steckbrief-loesung.visustruct b/web/lessons/diagramme/l04-java-steckbrief-loesung.visustruct deleted file mode 100644 index fa64abcf..00000000 --- a/web/lessons/diagramme/l04-java-steckbrief-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 100;111;117;98;108;101;32;103;114;111;101;115;115;101;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - - 111;117;116;112;117;116;58;32;34;71;114;246;223;101;58;32;34;32;43;32;103;114;111;101;115;115;101;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/l04-rechnen.visustruct b/web/lessons/diagramme/l04-rechnen.visustruct deleted file mode 100644 index cd0d36a8..00000000 --- a/web/lessons/diagramme/l04-rechnen.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - diff --git a/web/lessons/diagramme/l04-reihenfolge-loesung.visustruct b/web/lessons/diagramme/l04-reihenfolge-loesung.visustruct deleted file mode 100644 index 21a9fac3..00000000 --- a/web/lessons/diagramme/l04-reihenfolge-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;51; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;52; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - diff --git a/web/lessons/diagramme/l04-reihenfolge-start.visustruct b/web/lessons/diagramme/l04-reihenfolge-start.visustruct deleted file mode 100644 index 1c43ff05..00000000 --- a/web/lessons/diagramme/l04-reihenfolge-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;52; - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;51; - - diff --git a/web/lessons/diagramme/l04-tauschen-loesung.visustruct b/web/lessons/diagramme/l04-tauschen-loesung.visustruct deleted file mode 100644 index 4376ee6e..00000000 --- a/web/lessons/diagramme/l04-tauschen-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;107;115;32;61;32;34;83;97;102;116;34; - - - 83;116;114;105;110;103;32;114;101;99;104;116;115;32;61;32;34;87;97;115;115;101;114;34; - - - 83;116;114;105;110;103;32;104;105;108;102;32;61;32;108;105;110;107;115; - - - 108;105;110;107;115;32;61;32;114;101;99;104;116;115; - - - 114;101;99;104;116;115;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;76;105;110;107;115;58;32;34;32;43;32;108;105;110;107;115; - - - 111;117;116;112;117;116;58;32;34;82;101;99;104;116;115;58;32;34;32;43;32;114;101;99;104;116;115; - - diff --git a/web/lessons/diagramme/l04-tauschen-start.visustruct b/web/lessons/diagramme/l04-tauschen-start.visustruct deleted file mode 100644 index 675b73f6..00000000 --- a/web/lessons/diagramme/l04-tauschen-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;107;115;32;61;32;34;83;97;102;116;34; - - - 83;116;114;105;110;103;32;114;101;99;104;116;115;32;61;32;34;87;97;115;115;101;114;34; - - - 111;117;116;112;117;116;58;32;34;76;105;110;107;115;58;32;34;32;43;32;108;105;110;107;115; - - - 111;117;116;112;117;116;58;32;34;82;101;99;104;116;115;58;32;34;32;43;32;114;101;99;104;116;115; - - diff --git a/web/lessons/diagramme/l04-tauschen.visustruct b/web/lessons/diagramme/l04-tauschen.visustruct deleted file mode 100644 index 990611d9..00000000 --- a/web/lessons/diagramme/l04-tauschen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/l05-datentypen.visustruct b/web/lessons/diagramme/l05-datentypen.visustruct deleted file mode 100644 index c7309f40..00000000 --- a/web/lessons/diagramme/l05-datentypen.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;52; - - - 100;111;117;98;108;101;32;103;114;111;101;115;115;101;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;77;105;97;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - - 111;117;116;112;117;116;58;32;34;71;114;246;223;101;58;32;34;32;43;32;103;114;111;101;115;115;101;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/l05-division.visustruct b/web/lessons/diagramme/l05-division.visustruct deleted file mode 100644 index 116cec6f..00000000 --- a/web/lessons/diagramme/l05-division.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;49;51;53; - - - 105;110;116;32;115;116;117;110;100;101;110;32;61;32;109;105;110;117;116;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;109;105;110;117;116;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;115;116;117;110;100;101;110;32;43;32;34;32;83;116;117;110;100;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;77;105;110;117;116;101;110;34; - - diff --git a/web/lessons/diagramme/l05-einkauf-loesung.visustruct b/web/lessons/diagramme/l05-einkauf-loesung.visustruct deleted file mode 100644 index 35de6502..00000000 --- a/web/lessons/diagramme/l05-einkauf-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;101;105;115;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;80;114;101;105;115;32;61;32;97;110;122;97;104;108;32;42;32;112;114;101;105;115; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116;80;114;101;105;115;32;43;32;34;32;69;117;114;111;34; - - diff --git a/web/lessons/diagramme/l05-java-fehler-loesung.visustruct b/web/lessons/diagramme/l05-java-fehler-loesung.visustruct deleted file mode 100644 index bbf789a5..00000000 --- a/web/lessons/diagramme/l05-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;103;117;116;104;97;98;101;110;32;61;32;50;48; - - - 105;110;116;32;112;114;101;105;115;32;61;32;55; - - - 103;117;116;104;97;98;101;110;32;61;32;103;117;116;104;97;98;101;110;32;45;32;112;114;101;105;115; - - - 111;117;116;112;117;116;58;32;34;220;98;114;105;103;58;32;34;32;43;32;103;117;116;104;97;98;101;110; - - diff --git a/web/lessons/diagramme/l05-java-fehler-start.visustruct b/web/lessons/diagramme/l05-java-fehler-start.visustruct deleted file mode 100644 index 84a28ed4..00000000 --- a/web/lessons/diagramme/l05-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;103;117;116;104;97;98;101;110;32;61;32;50;48; - - - 105;110;116;32;112;114;101;105;115;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;220;98;114;105;103;58;32;34;32;43;32;103;117;116;104;97;98;101;110; - - - 103;117;116;104;97;98;101;110;32;61;32;103;117;116;104;97;98;101;110;32;45;32;112;114;101;105;115; - - diff --git a/web/lessons/diagramme/l05-java-kino-loesung.visustruct b/web/lessons/diagramme/l05-java-kino-loesung.visustruct deleted file mode 100644 index 7be38767..00000000 --- a/web/lessons/diagramme/l05-java-kino-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;114;101;105;115;32;61;32;57; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;112;114;101;105;115;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - diff --git a/web/lessons/diagramme/l05-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l05-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 1e053fec..00000000 --- a/web/lessons/diagramme/l05-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;116;117;101;99;107;101;32;61;32;56; - - - 105;110;116;32;112;101;114;115;111;110;101;110;32;61;32;52; - - - 105;110;116;32;112;114;111;80;101;114;115;111;110;32;61;32;115;116;117;101;99;107;101;32;47;32;112;101;114;115;111;110;101;110; - - - 111;117;116;112;117;116;58;32;34;74;101;100;101;114;32;98;101;107;111;109;109;116;32;34;32;43;32;112;114;111;80;101;114;115;111;110;32;43;32;34;32;83;116;252;99;107;101;46;34; - - diff --git a/web/lessons/diagramme/l05-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l05-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 4a751c92..00000000 --- a/web/lessons/diagramme/l05-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;116;117;101;99;107;101;32;61;32;56; - - - 105;110;116;32;112;101;114;115;111;110;101;110;32;61;32;52; - - - 105;110;116;32;112;114;111;80;101;114;115;111;110;32;61;32;115;116;117;101;99;107;101;32;47;32;112;101;114;115;111;110;101;110; - - - 111;117;116;112;117;116;58;32;34;74;101;100;101;114;32;98;101;107;111;109;109;116;32;34;32;43;32;112;114;111;112;101;114;115;111;110;32;43;32;34;32;83;116;252;99;107;101;46;34; - - diff --git a/web/lessons/diagramme/l05-rechteck-loesung.visustruct b/web/lessons/diagramme/l05-rechteck-loesung.visustruct deleted file mode 100644 index 4f32d414..00000000 --- a/web/lessons/diagramme/l05-rechteck-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;55; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;52; - - - 105;110;116;32;102;108;97;101;99;104;101;32;61;32;108;97;101;110;103;101;32;42;32;98;114;101;105;116;101; - - - 105;110;116;32;117;109;102;97;110;103;32;61;32;50;32;42;32;40;108;97;101;110;103;101;32;43;32;98;114;101;105;116;101;41; - - - 111;117;116;112;117;116;58;32;34;70;108;228;99;104;101;58;32;34;32;43;32;102;108;97;101;99;104;101; - - - 111;117;116;112;117;116;58;32;34;85;109;102;97;110;103;58;32;34;32;43;32;117;109;102;97;110;103; - - diff --git a/web/lessons/diagramme/l05-sekunden-loesung.visustruct b/web/lessons/diagramme/l05-sekunden-loesung.visustruct deleted file mode 100644 index 92d4d865..00000000 --- a/web/lessons/diagramme/l05-sekunden-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;77;105;110;117;116;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;83;101;107;117;110;100;101;110;34; - - diff --git a/web/lessons/diagramme/l05-sekunden-start.visustruct b/web/lessons/diagramme/l05-sekunden-start.visustruct deleted file mode 100644 index 0899268f..00000000 --- a/web/lessons/diagramme/l05-sekunden-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;77;105;110;117;116;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;83;101;107;117;110;100;101;110;34; - - diff --git a/web/lessons/diagramme/l05-sequenz.visustruct b/web/lessons/diagramme/l05-sequenz.visustruct deleted file mode 100644 index 415fd4d4..00000000 --- a/web/lessons/diagramme/l05-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;53; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51; - - - 105;110;116;32;102;108;97;101;99;104;101;32;61;32;108;97;101;110;103;101;32;42;32;98;114;101;105;116;101; - - - 111;117;116;112;117;116;58;32;102;108;97;101;99;104;101; - - diff --git a/web/lessons/diagramme/l06-begruessung-loesung.visustruct b/web/lessons/diagramme/l06-begruessung-loesung.visustruct deleted file mode 100644 index 5ec8cc19..00000000 --- a/web/lessons/diagramme/l06-begruessung-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;105;101;32;104;101;105;223;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;34;32;43;32;110;97;109;101;32;43;32;34;33;34; - - diff --git a/web/lessons/diagramme/l06-eva.visustruct b/web/lessons/diagramme/l06-eva.visustruct deleted file mode 100644 index c1a79f58..00000000 --- a/web/lessons/diagramme/l06-eva.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l06-java-fehler-loesung.visustruct b/web/lessons/diagramme/l06-java-fehler-loesung.visustruct deleted file mode 100644 index 3a0494e4..00000000 --- a/web/lessons/diagramme/l06-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;40;97;32;43;32;98;41; - - diff --git a/web/lessons/diagramme/l06-java-fehler-start.visustruct b/web/lessons/diagramme/l06-java-fehler-start.visustruct deleted file mode 100644 index 6b2a18bd..00000000 --- a/web/lessons/diagramme/l06-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;97;32;43;32;98; - - diff --git a/web/lessons/diagramme/l06-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l06-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 060902e2..00000000 --- a/web/lessons/diagramme/l06-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;228;99;104;115;116;101;115;32;74;97;104;114;32;98;105;115;116;32;100;117;32;34;32;43;32;40;97;108;116;101;114;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/l06-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l06-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 03658422..00000000 --- a/web/lessons/diagramme/l06-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;228;99;104;115;116;101;115;32;74;97;104;114;32;98;105;115;116;32;100;117;32;34;32;43;32;40;65;108;116;101;114;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/l06-java-tage-loesung.visustruct b/web/lessons/diagramme/l06-java-tage-loesung.visustruct deleted file mode 100644 index 566b02ae..00000000 --- a/web/lessons/diagramme/l06-java-tage-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;106;97;104;114;101;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 105;110;116;32;116;97;103;101;32;61;32;106;97;104;114;101;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;115;105;110;100;32;101;116;119;97;32;34;32;43;32;116;97;103;101;32;43;32;34;32;84;97;103;101;46;34; - - diff --git a/web/lessons/diagramme/l06-mittelwert-loesung.visustruct b/web/lessons/diagramme/l06-mittelwert-loesung.visustruct deleted file mode 100644 index b08f004a..00000000 --- a/web/lessons/diagramme/l06-mittelwert-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108; - - diff --git a/web/lessons/diagramme/l06-rechner-loesung.visustruct b/web/lessons/diagramme/l06-rechner-loesung.visustruct deleted file mode 100644 index 9900f0cb..00000000 --- a/web/lessons/diagramme/l06-rechner-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;107;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;107;116;58;32;34;32;43;32;112;114;111;100;117;107;116; - - diff --git a/web/lessons/diagramme/l07-alter-pruefen.visustruct b/web/lessons/diagramme/l07-alter-pruefen.visustruct deleted file mode 100644 index 1465a7a5..00000000 --- a/web/lessons/diagramme/l07-alter-pruefen.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;105;115;116;32;118;111;108;108;106;228;104;114;105;103;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;105;115;116;32;110;111;99;104;32;110;105;99;104;116;32;118;111;108;108;106;228;104;114;105;103;46;34; - - - - diff --git a/web/lessons/diagramme/l07-betrag-loesung.visustruct b/web/lessons/diagramme/l07-betrag-loesung.visustruct deleted file mode 100644 index e50936bc..00000000 --- a/web/lessons/diagramme/l07-betrag-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;63;34; - - - 122;97;104;108;32;60;32;48; - - - 122;97;104;108;32;61;32;48;32;45;32;122;97;104;108; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - diff --git a/web/lessons/diagramme/l07-gerade-loesung.visustruct b/web/lessons/diagramme/l07-gerade-loesung.visustruct deleted file mode 100644 index c872e462..00000000 --- a/web/lessons/diagramme/l07-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;63;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;103;101;114;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;101;114;97;100;101;34; - - - - diff --git a/web/lessons/diagramme/l07-java-fehler-loesung.visustruct b/web/lessons/diagramme/l07-java-fehler-loesung.visustruct deleted file mode 100644 index aad15b91..00000000 --- a/web/lessons/diagramme/l07-java-fehler-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;53; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - diff --git a/web/lessons/diagramme/l07-java-fehler-start.visustruct b/web/lessons/diagramme/l07-java-fehler-start.visustruct deleted file mode 100644 index 7c8b52da..00000000 --- a/web/lessons/diagramme/l07-java-fehler-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;53; - - - 112;117;110;107;116;101;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - diff --git a/web/lessons/diagramme/l07-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l07-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 0ec83a9a..00000000 --- a/web/lessons/diagramme/l07-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - diff --git a/web/lessons/diagramme/l07-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l07-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 436bf3d4..00000000 --- a/web/lessons/diagramme/l07-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - diff --git a/web/lessons/diagramme/l07-java-pizza-loesung.visustruct b/web/lessons/diagramme/l07-java-pizza-loesung.visustruct deleted file mode 100644 index dd5c9bbd..00000000 --- a/web/lessons/diagramme/l07-java-pizza-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;101;105;115;32;61;32;56; - - - 105;110;116;32;107;97;101;115;101;32;61;32;50; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;40;112;114;101;105;115;32;43;32;107;97;101;115;101;41;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - diff --git a/web/lessons/diagramme/l07-java-pizza-start.visustruct b/web/lessons/diagramme/l07-java-pizza-start.visustruct deleted file mode 100644 index 71f8f387..00000000 --- a/web/lessons/diagramme/l07-java-pizza-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;101;105;115;32;61;32;56; - - - 105;110;116;32;107;97;101;115;101;32;61;32;50; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;112;114;101;105;115;32;43;32;107;97;101;115;101;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - diff --git a/web/lessons/diagramme/l07-maximum-loesung.visustruct b/web/lessons/diagramme/l07-maximum-loesung.visustruct deleted file mode 100644 index 93a51295..00000000 --- a/web/lessons/diagramme/l07-maximum-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - diff --git a/web/lessons/diagramme/l07-rabatt.visustruct b/web/lessons/diagramme/l07-rabatt.visustruct deleted file mode 100644 index 361f9cce..00000000 --- a/web/lessons/diagramme/l07-rabatt.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;105;115;32;34;80;114;101;105;115;32;105;110;32;69;117;114;111;63;34; - - - 112;114;101;105;115;32;62;32;53;48; - - - 112;114;101;105;115;32;61;32;112;114;101;105;115;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;90;117;32;122;97;104;108;101;110;58;32;34;32;43;32;112;114;101;105;115;32;43;32;34;32;69;117;114;111;34; - - diff --git a/web/lessons/diagramme/l08-gueltig-loesung.visustruct b/web/lessons/diagramme/l08-gueltig-loesung.visustruct deleted file mode 100644 index bf26ecc6..00000000 --- a/web/lessons/diagramme/l08-gueltig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;63;34; - - - 110;111;116;101;32;62;61;32;49;32;38;38;32;110;111;116;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;103;252;108;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;252;108;116;105;103;34; - - - - diff --git a/web/lessons/diagramme/l08-java-bestanden-loesung.visustruct b/web/lessons/diagramme/l08-java-bestanden-loesung.visustruct deleted file mode 100644 index cacba37f..00000000 --- a/web/lessons/diagramme/l08-java-bestanden-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;112;117;110;107;116;101;32;34;87;105;101;32;118;105;101;108;101;32;80;117;110;107;116;101;63;34; - - - 112;117;110;107;116;101;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;98;101;115;116;97;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;110;105;99;104;116;32;98;101;115;116;97;110;100;101;110;34; - - - - diff --git a/web/lessons/diagramme/l08-java-fehler-loesung.visustruct b/web/lessons/diagramme/l08-java-fehler-loesung.visustruct deleted file mode 100644 index 261b7255..00000000 --- a/web/lessons/diagramme/l08-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;69;117;114;111;34; - - - - diff --git a/web/lessons/diagramme/l08-java-fehler-start.visustruct b/web/lessons/diagramme/l08-java-fehler-start.visustruct deleted file mode 100644 index b2687615..00000000 --- a/web/lessons/diagramme/l08-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;69;117;114;111;34; - - - - diff --git a/web/lessons/diagramme/l08-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l08-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 698dac33..00000000 --- a/web/lessons/diagramme/l08-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;53; - - - 97;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;108;97;115;115;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;69;105;110;108;97;115;115;34; - - - - diff --git a/web/lessons/diagramme/l08-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l08-java-laeuft-nicht-start.visustruct deleted file mode 100644 index f94df1e8..00000000 --- a/web/lessons/diagramme/l08-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;53; - - - 65;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;108;97;115;115;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;69;105;110;108;97;115;115;34; - - - - diff --git a/web/lessons/diagramme/l08-jugendticket.visustruct b/web/lessons/diagramme/l08-jugendticket.visustruct deleted file mode 100644 index fb1ab7da..00000000 --- a/web/lessons/diagramme/l08-jugendticket.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;50;32;38;38;32;97;108;116;101;114;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;74;117;103;101;110;100;116;105;99;107;101;116;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;74;117;103;101;110;100;116;105;99;107;101;116;34; - - - - diff --git a/web/lessons/diagramme/l08-note.visustruct b/web/lessons/diagramme/l08-note.visustruct deleted file mode 100644 index c7525b9b..00000000 --- a/web/lessons/diagramme/l08-note.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;63;34; - - - 110;111;116;101;32;60;32;49;32;124;124;32;110;111;116;101;32;62;32;54; - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;78;111;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;101;32;34;32;43;32;110;111;116;101;32;43;32;34;32;105;115;116;32;103;252;108;116;105;103;46;34; - - - - diff --git a/web/lessons/diagramme/l08-schaltjahr-loesung.visustruct b/web/lessons/diagramme/l08-schaltjahr-loesung.visustruct deleted file mode 100644 index 67f92162..00000000 --- a/web/lessons/diagramme/l08-schaltjahr-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;106;97;104;114;32;34;74;97;104;114;63;34; - - - 40;106;97;104;114;32;37;32;52;32;61;61;32;48;32;38;38;32;106;97;104;114;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;106;97;104;114;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;83;99;104;97;108;116;106;97;104;114;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;105;110;32;83;99;104;97;108;116;106;97;104;114;34; - - - - diff --git a/web/lessons/diagramme/l08-wochenende-loesung.visustruct b/web/lessons/diagramme/l08-wochenende-loesung.visustruct deleted file mode 100644 index 86550145..00000000 --- a/web/lessons/diagramme/l08-wochenende-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;101;110;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;87;101;114;107;116;97;103;34; - - - - diff --git a/web/lessons/diagramme/l09-ampel.visustruct b/web/lessons/diagramme/l09-ampel.visustruct deleted file mode 100644 index ee5cd3f7..00000000 --- a/web/lessons/diagramme/l09-ampel.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;97;114;98;101;32;34;87;101;108;99;104;101;32;70;97;114;98;101;32;104;97;116;32;100;105;101;32;65;109;112;101;108;63;34; - - - 102;97;114;98;101; - - - 111;117;116;112;117;116;58;32;34;83;116;101;104;101;110;32;98;108;101;105;98;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;114;116;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;71;101;104;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;70;97;114;98;101;32;107;101;110;110;101;32;105;99;104;32;110;105;99;104;116;46;34; - - - - diff --git a/web/lessons/diagramme/l09-automat.visustruct b/web/lessons/diagramme/l09-automat.visustruct deleted file mode 100644 index ce611c37..00000000 --- a/web/lessons/diagramme/l09-automat.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;87;97;115;115;101;114;44;32;50;32;83;97;102;116;44;32;51;32;75;97;107;97;111;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;87;97;115;115;101;114;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;83;97;102;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;75;97;107;97;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;84;97;115;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - diff --git a/web/lessons/diagramme/l09-java-fehler-loesung.visustruct b/web/lessons/diagramme/l09-java-fehler-loesung.visustruct deleted file mode 100644 index 2a159522..00000000 --- a/web/lessons/diagramme/l09-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;87;101;108;99;104;101;114;32;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - diff --git a/web/lessons/diagramme/l09-java-fehler-start.visustruct b/web/lessons/diagramme/l09-java-fehler-start.visustruct deleted file mode 100644 index 607d5414..00000000 --- a/web/lessons/diagramme/l09-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;87;101;108;99;104;101;114;32;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;38;38;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - diff --git a/web/lessons/diagramme/l09-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l09-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 9ed82396..00000000 --- a/web/lessons/diagramme/l09-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;116;97;103;32;61;32;55; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - diff --git a/web/lessons/diagramme/l09-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l09-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 986f59b3..00000000 --- a/web/lessons/diagramme/l09-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;116;97;103;32;61;32;55; - - - 116;97;103;32;61;61;32;54;32;124;124;32;84;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - diff --git a/web/lessons/diagramme/l09-java-schule-loesung.visustruct b/web/lessons/diagramme/l09-java-schule-loesung.visustruct deleted file mode 100644 index e1fd1be2..00000000 --- a/web/lessons/diagramme/l09-java-schule-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;117;110;100;101;32;34;87;105;101;32;115;112;228;116;32;105;115;116;32;101;115;32;40;110;117;114;32;100;105;101;32;83;116;117;110;100;101;41;63;34; - - - 115;116;117;110;100;101;32;62;61;32;56;32;38;38;32;115;116;117;110;100;101;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;105;122;101;105;116;34; - - - - diff --git a/web/lessons/diagramme/l09-note-loesung.visustruct b/web/lessons/diagramme/l09-note-loesung.visustruct deleted file mode 100644 index 0dcdd181..00000000 --- a/web/lessons/diagramme/l09-note-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;87;101;108;99;104;101;32;78;111;116;101;63;34; - - - 110;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;98;101;102;114;105;101;100;105;103;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;97;117;115;114;101;105;99;104;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;109;97;110;103;101;108;104;97;102;116;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;101;110;252;103;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;101;32;103;252;108;116;105;103;101;32;78;111;116;101;34; - - - - diff --git a/web/lessons/diagramme/l09-rechner-loesung.visustruct b/web/lessons/diagramme/l09-rechner-loesung.visustruct deleted file mode 100644 index 3e16b2c8..00000000 --- a/web/lessons/diagramme/l09-rechner-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;122;101;105;99;104;101;110;32;34;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;48; - - - 122;101;105;99;104;101;110; - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 111;117;116;112;117;116;58;32;34;85;110;98;101;107;97;110;110;116;101;115;32;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - - diff --git a/web/lessons/diagramme/l09-verschachtelt.visustruct b/web/lessons/diagramme/l09-verschachtelt.visustruct deleted file mode 100644 index 5c0c9593..00000000 --- a/web/lessons/diagramme/l09-verschachtelt.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;87;97;115;115;101;114;44;32;50;32;83;97;102;116;44;32;51;32;75;97;107;97;111;34; - - - 119;97;104;108;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;87;97;115;115;101;114;46;34; - - - - - 119;97;104;108;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;83;97;102;116;46;34; - - - - - 119;97;104;108;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;75;97;107;97;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;84;97;115;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - - - - - diff --git a/web/lessons/diagramme/l1-grundbausteine.visustruct b/web/lessons/diagramme/l1-grundbausteine.visustruct deleted file mode 100644 index 15b1d94b..00000000 --- a/web/lessons/diagramme/l1-grundbausteine.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 84;97;115;115;101;32;97;117;102;32;100;101;110;32;84;105;115;99;104;32;115;116;101;108;108;101;110; - - - 90;117;99;107;101;114;32;103;101;119;252;110;115;99;104;116;63; - - - 90;117;99;107;101;114;32;104;105;110;101;105;110;103;101;98;101;110; - - - - - 248; - - - - - 84;101;101;32;110;111;99;104;32;122;117;32;104;101;105;223; - - - 49;32;77;105;110;117;116;101;32;119;97;114;116;101;110; - - - - - 84;101;101;32;116;114;105;110;107;101;110; - - diff --git a/web/lessons/diagramme/l1-sequenz.visustruct b/web/lessons/diagramme/l1-sequenz.visustruct deleted file mode 100644 index 9f84605a..00000000 --- a/web/lessons/diagramme/l1-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 65;117;102;115;116;101;104;101;110; - - - 90;228;104;110;101;32;112;117;116;122;101;110; - - - 65;110;122;105;101;104;101;110; - - - 70;114;252;104;115;116;252;99;107;101;110; - - diff --git a/web/lessons/diagramme/l1-tee.visustruct b/web/lessons/diagramme/l1-tee.visustruct deleted file mode 100644 index 1a69335a..00000000 --- a/web/lessons/diagramme/l1-tee.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 87;97;115;115;101;114;32;105;110;32;100;101;110;32;87;97;115;115;101;114;107;111;99;104;101;114;32;102;252;108;108;101;110; - - - 87;97;115;115;101;114;107;111;99;104;101;114;32;101;105;110;115;99;104;97;108;116;101;110; - - - 84;101;101;98;101;117;116;101;108;32;105;110;32;100;105;101;32;84;97;115;115;101;32;108;101;103;101;110; - - - 72;101;105;223;101;115;32;87;97;115;115;101;114;32;105;110;32;100;105;101;32;84;97;115;115;101;32;103;105;101;223;101;110; - - - 51;32;77;105;110;117;116;101;110;32;119;97;114;116;101;110; - - - 84;101;101;98;101;117;116;101;108;32;104;101;114;97;117;115;110;101;104;109;101;110; - - diff --git a/web/lessons/diagramme/l1-verzweigung.visustruct b/web/lessons/diagramme/l1-verzweigung.visustruct deleted file mode 100644 index 0cbc066c..00000000 --- a/web/lessons/diagramme/l1-verzweigung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 82;101;103;110;101;116;32;101;115;63; - - - 82;101;103;101;110;115;99;104;105;114;109;32;101;105;110;112;97;99;107;101;110; - - - - - 83;111;110;110;101;110;98;114;105;108;108;101;32;101;105;110;112;97;99;107;101;110; - - - - - 76;111;115;103;101;104;101;110; - - diff --git a/web/lessons/diagramme/l1-wiederholung.visustruct b/web/lessons/diagramme/l1-wiederholung.visustruct deleted file mode 100644 index 95569e48..00000000 --- a/web/lessons/diagramme/l1-wiederholung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 84;101;108;108;101;114;32;110;105;99;104;116;32;108;101;101;114; - - - 69;105;110;101;110;32;66;105;115;115;101;110;32;101;115;115;101;110; - - - - - 84;101;108;108;101;114;32;97;98;114;228;117;109;101;110; - - diff --git a/web/lessons/diagramme/l10-bis-n-loesung.visustruct b/web/lessons/diagramme/l10-bis-n-loesung.visustruct deleted file mode 100644 index 85431a06..00000000 --- a/web/lessons/diagramme/l10-bis-n-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;32;122;228;104;108;101;110;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/l10-countdown.visustruct b/web/lessons/diagramme/l10-countdown.visustruct deleted file mode 100644 index baed185c..00000000 --- a/web/lessons/diagramme/l10-countdown.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/l10-endlos.visustruct b/web/lessons/diagramme/l10-endlos.visustruct deleted file mode 100644 index 7bf0dfe5..00000000 --- a/web/lessons/diagramme/l10-endlos.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/l10-fakultaet.visustruct b/web/lessons/diagramme/l10-fakultaet.visustruct deleted file mode 100644 index 158e98dc..00000000 --- a/web/lessons/diagramme/l10-fakultaet.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;107;117;108;116;228;116;32;118;111;110;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;102;97;107;117;108;116;97;101;116;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;107;117;108;116;97;101;116;32;61;32;102;97;107;117;108;116;97;101;116;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;107;117;108;116;97;101;116; - - diff --git a/web/lessons/diagramme/l10-java-fehler-loesung.visustruct b/web/lessons/diagramme/l10-java-fehler-loesung.visustruct deleted file mode 100644 index 52d83b56..00000000 --- a/web/lessons/diagramme/l10-java-fehler-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;109;97;108;63;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;71;105;98;116;32;101;115;32;110;105;99;104;116;34; - - - - diff --git a/web/lessons/diagramme/l10-java-fehler-start.visustruct b/web/lessons/diagramme/l10-java-fehler-start.visustruct deleted file mode 100644 index d92715a6..00000000 --- a/web/lessons/diagramme/l10-java-fehler-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;109;97;108;63;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;71;105;98;116;32;101;115;32;110;105;99;104;116;34; - - - - diff --git a/web/lessons/diagramme/l10-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l10-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index a7c9ac48..00000000 --- a/web/lessons/diagramme/l10-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;111;116;101;32;61;32;50; - - - 110;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;119;101;105;116;101;114;32;252;98;101;110;34; - - - - diff --git a/web/lessons/diagramme/l10-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l10-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 37aadd29..00000000 --- a/web/lessons/diagramme/l10-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;111;116;101;32;61;32;50; - - - 78;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;119;101;105;116;101;114;32;252;98;101;110;34; - - - - diff --git a/web/lessons/diagramme/l10-java-wuerfel-loesung.visustruct b/web/lessons/diagramme/l10-java-wuerfel-loesung.visustruct deleted file mode 100644 index 88351962..00000000 --- a/web/lessons/diagramme/l10-java-wuerfel-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;90;97;104;108;63;34; - - - 122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;101;105;110;115;34; - - - - - 111;117;116;112;117;116;58;32;34;122;119;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;100;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;110;110;101;32;105;99;104;32;110;105;99;104;116;34; - - - - diff --git a/web/lessons/diagramme/l10-verdoppeln-loesung.visustruct b/web/lessons/diagramme/l10-verdoppeln-loesung.visustruct deleted file mode 100644 index 320cd62b..00000000 --- a/web/lessons/diagramme/l10-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;34; - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 122;97;104;108;32;60;32;49;48;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;99;104;114;105;116;116;101; - - diff --git a/web/lessons/diagramme/l11-eingabe-pruefen.visustruct b/web/lessons/diagramme/l11-eingabe-pruefen.visustruct deleted file mode 100644 index b3d1e5d6..00000000 --- a/web/lessons/diagramme/l11-eingabe-pruefen.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;48; - - - 122;97;104;108;32;60;32;49;32;124;124;32;122;97;104;108;32;62;32;49;48; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;118;111;110;32;49;32;98;105;115;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;68;97;110;107;101;33;34; - - diff --git a/web/lessons/diagramme/l11-einmal.visustruct b/web/lessons/diagramme/l11-einmal.visustruct deleted file mode 100644 index 8757f666..00000000 --- a/web/lessons/diagramme/l11-einmal.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/l11-java-fehler-loesung.visustruct b/web/lessons/diagramme/l11-java-fehler-loesung.visustruct deleted file mode 100644 index 1510d0be..00000000 --- a/web/lessons/diagramme/l11-java-fehler-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;105;101;108;32;34;83;112;97;114;122;105;101;108;63;34; - - - 105;110;116;32;103;101;108;100;32;61;32;48; - - - 105;110;116;32;119;111;99;104;101;110;32;61;32;48; - - - 103;101;108;100;32;60;32;122;105;101;108; - - - 103;101;108;100;32;61;32;103;101;108;100;32;43;32;53; - - - 119;111;99;104;101;110;32;61;32;119;111;99;104;101;110;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;58;32;34;32;43;32;119;111;99;104;101;110; - - diff --git a/web/lessons/diagramme/l11-java-fehler-start.visustruct b/web/lessons/diagramme/l11-java-fehler-start.visustruct deleted file mode 100644 index 51ce7be1..00000000 --- a/web/lessons/diagramme/l11-java-fehler-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;105;101;108;32;34;83;112;97;114;122;105;101;108;63;34; - - - 105;110;116;32;103;101;108;100;32;61;32;48; - - - 105;110;116;32;119;111;99;104;101;110;32;61;32;48; - - - 103;101;108;100;32;60;61;32;122;105;101;108; - - - 103;101;108;100;32;61;32;103;101;108;100;32;43;32;53; - - - 119;111;99;104;101;110;32;61;32;119;111;99;104;101;110;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;58;32;34;32;43;32;119;111;99;104;101;110; - - diff --git a/web/lessons/diagramme/l11-java-gerade-loesung.visustruct b/web/lessons/diagramme/l11-java-gerade-loesung.visustruct deleted file mode 100644 index d59ba6e5..00000000 --- a/web/lessons/diagramme/l11-java-gerade-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;50; - - - - diff --git a/web/lessons/diagramme/l11-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l11-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index baed185c..00000000 --- a/web/lessons/diagramme/l11-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/l11-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l11-java-laeuft-nicht-start.visustruct deleted file mode 100644 index f6132fea..00000000 --- a/web/lessons/diagramme/l11-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;90;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/l11-note-loesung.visustruct b/web/lessons/diagramme/l11-note-loesung.visustruct deleted file mode 100644 index dbfae923..00000000 --- a/web/lessons/diagramme/l11-note-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;111;116;101;32;61;32;48; - - - 110;111;116;101;32;60;32;49;32;124;124;32;110;111;116;101;32;62;32;54; - - - 105;110;112;117;116;58;32;110;111;116;101;32;34;78;111;116;101;32;118;111;110;32;49;32;98;105;115;32;54;34; - - - - - 111;117;116;112;117;116;58;32;34;68;101;105;110;101;32;78;111;116;101;58;32;34;32;43;32;110;111;116;101; - - diff --git a/web/lessons/diagramme/l11-summe-loesung.visustruct b/web/lessons/diagramme/l11-summe-loesung.visustruct deleted file mode 100644 index 93764ea4..00000000 --- a/web/lessons/diagramme/l11-summe-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;122;97;104;108;32;61;32;48; - - - 122;97;104;108;32;33;61;32;48; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;40;48;32;98;101;101;110;100;101;116;41;34; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l12-countdown-loesung.visustruct b/web/lessons/diagramme/l12-countdown-loesung.visustruct deleted file mode 100644 index e1c2d476..00000000 --- a/web/lessons/diagramme/l12-countdown-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;83;116;97;114;116;122;97;104;108;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;33;34; - - diff --git a/web/lessons/diagramme/l12-einmaleins-loesung.visustruct b/web/lessons/diagramme/l12-einmaleins-loesung.visustruct deleted file mode 100644 index 0f4f4f10..00000000 --- a/web/lessons/diagramme/l12-einmaleins-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;107;116;32;61;32;122;97;104;108;32;42;32;105; - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;107;116; - - - - diff --git a/web/lessons/diagramme/l12-eins-bis-fuenf.visustruct b/web/lessons/diagramme/l12-eins-bis-fuenf.visustruct deleted file mode 100644 index f1fb1e1a..00000000 --- a/web/lessons/diagramme/l12-eins-bis-fuenf.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/l12-java-fehler-loesung.visustruct b/web/lessons/diagramme/l12-java-fehler-loesung.visustruct deleted file mode 100644 index 00157fb0..00000000 --- a/web/lessons/diagramme/l12-java-fehler-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - diff --git a/web/lessons/diagramme/l12-java-fehler-start.visustruct b/web/lessons/diagramme/l12-java-fehler-start.visustruct deleted file mode 100644 index 9e7ffef5..00000000 --- a/web/lessons/diagramme/l12-java-fehler-start.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;49; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - diff --git a/web/lessons/diagramme/l12-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l12-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 415e2068..00000000 --- a/web/lessons/diagramme/l12-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;49; - - - 105;110;116;32;103;114;101;110;122;101;32;61;32;50;48; - - - 122;97;104;108;32;60;32;103;114;101;110;122;101; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/l12-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l12-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 2ce71b70..00000000 --- a/web/lessons/diagramme/l12-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;49; - - - 105;110;116;32;103;114;101;110;122;101;32;61;32;50;48; - - - 122;97;104;108;32;60;32;71;114;101;110;122;101; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/l12-java-verdoppeln-loesung.visustruct b/web/lessons/diagramme/l12-java-verdoppeln-loesung.visustruct deleted file mode 100644 index f94a1b68..00000000 --- a/web/lessons/diagramme/l12-java-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;60;32;49;48;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - diff --git a/web/lessons/diagramme/l12-summe.visustruct b/web/lessons/diagramme/l12-summe.visustruct deleted file mode 100644 index f1174faa..00000000 --- a/web/lessons/diagramme/l12-summe.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l13-dreieck.visustruct b/web/lessons/diagramme/l13-dreieck.visustruct deleted file mode 100644 index 78052335..00000000 --- a/web/lessons/diagramme/l13-dreieck.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;87;105;101;32;118;105;101;108;101;32;90;101;105;108;101;110;63;34; - - - 105;110;116;32;122;101;105;108;101;32;61;32;49; - 122;101;105;108;101;32;60;61;32;110; - 122;101;105;108;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;116;101;114;110;32;61;32;49; - 115;116;101;114;110;32;60;61;32;122;101;105;108;101; - 115;116;101;114;110;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - diff --git a/web/lessons/diagramme/l13-gerade-eingaben-loesung.visustruct b/web/lessons/diagramme/l13-gerade-eingaben-loesung.visustruct deleted file mode 100644 index 4d308c89..00000000 --- a/web/lessons/diagramme/l13-gerade-eingaben-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l13-gerade-zaehlen.visustruct b/web/lessons/diagramme/l13-gerade-zaehlen.visustruct deleted file mode 100644 index c096a48b..00000000 --- a/web/lessons/diagramme/l13-gerade-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l13-java-fehler-loesung.visustruct b/web/lessons/diagramme/l13-java-fehler-loesung.visustruct deleted file mode 100644 index 7e087fc5..00000000 --- a/web/lessons/diagramme/l13-java-fehler-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;101;105;104;101;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/l13-java-fehler-start.visustruct b/web/lessons/diagramme/l13-java-fehler-start.visustruct deleted file mode 100644 index 9b64cf71..00000000 --- a/web/lessons/diagramme/l13-java-fehler-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;101;105;104;101;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/l13-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l13-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index b9832516..00000000 --- a/web/lessons/diagramme/l13-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;104;111;101;104;101;32;61;32;48; - - - 105;110;116;32;115;116;117;102;101;32;61;32;49; - 115;116;117;102;101;32;60;61;32;53; - 115;116;117;102;101;43;43; - - - 104;111;101;104;101;32;61;32;104;111;101;104;101;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;246;104;101;58;32;34;32;43;32;104;111;101;104;101;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/l13-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l13-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 8fb62110..00000000 --- a/web/lessons/diagramme/l13-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;115;116;117;102;101;32;61;32;49; - 115;116;117;102;101;32;60;61;32;53; - 115;116;117;102;101;43;43; - - - 104;111;101;104;101;32;61;32;104;111;101;104;101;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;246;104;101;58;32;34;32;43;32;104;111;101;104;101;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/l13-java-quadrate-loesung.visustruct b/web/lessons/diagramme/l13-java-quadrate-loesung.visustruct deleted file mode 100644 index f02d9d44..00000000 --- a/web/lessons/diagramme/l13-java-quadrate-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/l13-rechteck-loesung.visustruct b/web/lessons/diagramme/l13-rechteck-loesung.visustruct deleted file mode 100644 index f0249c0f..00000000 --- a/web/lessons/diagramme/l13-rechteck-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;98;114;101;105;116;101;32;34;66;114;101;105;116;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;101;104;101;32;34;72;246;104;101;34; - - - 105;110;116;32;122;101;105;108;101;32;61;32;49; - 122;101;105;108;101;32;60;61;32;104;111;101;104;101; - 122;101;105;108;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;112;97;108;116;101;32;61;32;49; - 115;112;97;108;116;101;32;60;61;32;98;114;101;105;116;101; - 115;112;97;108;116;101;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - diff --git a/web/lessons/diagramme/l14-array-durchlaufen.visustruct b/web/lessons/diagramme/l14-array-durchlaufen.visustruct deleted file mode 100644 index 427a092d..00000000 --- a/web/lessons/diagramme/l14-array-durchlaufen.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;122;97;104;108;101;110;91;105;93; - - - - diff --git a/web/lessons/diagramme/l14-erster-letzter-loesung.visustruct b/web/lessons/diagramme/l14-erster-letzter-loesung.visustruct deleted file mode 100644 index 980c1bcb..00000000 --- a/web/lessons/diagramme/l14-erster-letzter-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;91;48;93; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;91;119;101;114;116;101;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/l14-java-fehler-loesung.visustruct b/web/lessons/diagramme/l14-java-fehler-loesung.visustruct deleted file mode 100644 index cc8989c0..00000000 --- a/web/lessons/diagramme/l14-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l14-java-fehler-start.visustruct b/web/lessons/diagramme/l14-java-fehler-start.visustruct deleted file mode 100644 index acf58235..00000000 --- a/web/lessons/diagramme/l14-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - - - diff --git a/web/lessons/diagramme/l14-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l14-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 74d801ff..00000000 --- a/web/lessons/diagramme/l14-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/l14-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l14-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 3bab3941..00000000 --- a/web/lessons/diagramme/l14-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/l14-java-treppe-loesung.visustruct b/web/lessons/diagramme/l14-java-treppe-loesung.visustruct deleted file mode 100644 index bd136c79..00000000 --- a/web/lessons/diagramme/l14-java-treppe-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;87;105;101;32;118;105;101;108;101;32;83;116;117;102;101;110;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - diff --git a/web/lessons/diagramme/l14-namen-eingeben.visustruct b/web/lessons/diagramme/l14-namen-eingeben.visustruct deleted file mode 100644 index 0c7709ad..00000000 --- a/web/lessons/diagramme/l14-namen-eingeben.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;110;91;105;93;32;34;78;97;109;101;32;101;105;110;103;101;98;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;69;114;115;116;101;114;32;78;97;109;101;58;32;34;32;43;32;110;97;109;101;110;91;48;93; - - diff --git a/web/lessons/diagramme/l14-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/l14-rueckwaerts-loesung.visustruct deleted file mode 100644 index 0e2991ed..00000000 --- a/web/lessons/diagramme/l14-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;122;97;104;108;101;110;91;105;93;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;105;32;61;32;122;97;104;108;101;110;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;122;97;104;108;101;110;91;105;93; - - - - diff --git a/web/lessons/diagramme/l14-summe-max-loesung.visustruct b/web/lessons/diagramme/l14-summe-max-loesung.visustruct deleted file mode 100644 index 1244c8e6..00000000 --- a/web/lessons/diagramme/l14-summe-max-loesung.visustruct +++ /dev/null @@ -1,51 +0,0 @@ - - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;122;97;104;108;101;110;91;105;93;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;122;97;104;108;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108;101;110;91;105;93; - - - 122;97;104;108;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;122;97;104;108;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/l14-summe-maximum.visustruct b/web/lessons/diagramme/l14-summe-maximum.visustruct deleted file mode 100644 index 40c44c84..00000000 --- a/web/lessons/diagramme/l14-summe-maximum.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;117;110;107;116;101;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - 112;117;110;107;116;101;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;117;110;107;116;101;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/l15-bis-null-loesung.visustruct b/web/lessons/diagramme/l15-bis-null-loesung.visustruct deleted file mode 100644 index 018c60ae..00000000 --- a/web/lessons/diagramme/l15-bis-null-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - 122;97;104;108;32;33;61;32;48; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l15-endlos-aussprung.visustruct b/web/lessons/diagramme/l15-endlos-aussprung.visustruct deleted file mode 100644 index 5fe0b250..00000000 --- a/web/lessons/diagramme/l15-endlos-aussprung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - 122;97;104;108;32;61;61;32;48; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l15-flaeche-loesung.visustruct b/web/lessons/diagramme/l15-flaeche-loesung.visustruct deleted file mode 100644 index 04f013e6..00000000 --- a/web/lessons/diagramme/l15-flaeche-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;101;110;103;101;32;34;76;228;110;103;101;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;114;101;105;116;101;32;34;66;114;101;105;116;101;63;34; - - - 105;110;116;32;102;32;61;32;102;108;97;101;99;104;101;40;108;97;101;110;103;101;44;32;98;114;101;105;116;101;41; - - - 111;117;116;112;117;116;58;32;34;70;108;228;99;104;101;58;32;34;32;43;32;102; - - diff --git a/web/lessons/diagramme/l15-flaeche-unterprogramm-loesung.visustruct b/web/lessons/diagramme/l15-flaeche-unterprogramm-loesung.visustruct deleted file mode 100644 index a05b965b..00000000 --- a/web/lessons/diagramme/l15-flaeche-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 114;101;116;117;114;110;32;97;32;42;32;98; - - diff --git a/web/lessons/diagramme/l15-hauptprogramm.visustruct b/web/lessons/diagramme/l15-hauptprogramm.visustruct deleted file mode 100644 index 33679bef..00000000 --- a/web/lessons/diagramme/l15-hauptprogramm.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 122;101;105;103;101;66;101;103;114;117;101;115;115;117;110;103;40;41; - - - 115;116;101;108;108;101;70;114;97;103;101;110;40;41; - - - 122;101;105;103;101;69;114;103;101;98;110;105;115;40;41; - - diff --git a/web/lessons/diagramme/l15-java-fehler-loesung.visustruct b/web/lessons/diagramme/l15-java-fehler-loesung.visustruct deleted file mode 100644 index bb4e237a..00000000 --- a/web/lessons/diagramme/l15-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;110;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;246;99;104;115;116;101;32;84;101;109;112;101;114;97;116;117;114;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/l15-java-fehler-start.visustruct b/web/lessons/diagramme/l15-java-fehler-start.visustruct deleted file mode 100644 index 6992a54b..00000000 --- a/web/lessons/diagramme/l15-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;110;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;246;99;104;115;116;101;32;84;101;109;112;101;114;97;116;117;114;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/l15-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l15-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index f27992de..00000000 --- a/web/lessons/diagramme/l15-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l15-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l15-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 18654a09..00000000 --- a/web/lessons/diagramme/l15-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;76;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l15-java-noten-loesung.visustruct b/web/lessons/diagramme/l15-java-noten-loesung.visustruct deleted file mode 100644 index a19f8aed..00000000 --- a/web/lessons/diagramme/l15-java-noten-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;110;111;116;101;110;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;110;111;116;101;110;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l15-quadrat-haupt.visustruct b/web/lessons/diagramme/l15-quadrat-haupt.visustruct deleted file mode 100644 index 0d79bc87..00000000 --- a/web/lessons/diagramme/l15-quadrat-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;90;97;104;108;63;34; - - - 105;110;116;32;113;32;61;32;113;117;97;100;114;97;116;40;122;97;104;108;41; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;81;117;97;100;114;97;116;32;105;115;116;32;34;32;43;32;113; - - diff --git a/web/lessons/diagramme/l15-quadrat.visustruct b/web/lessons/diagramme/l15-quadrat.visustruct deleted file mode 100644 index f60e1982..00000000 --- a/web/lessons/diagramme/l15-quadrat.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;101;114;103;101;98;110;105;115; - - diff --git a/web/lessons/diagramme/l15-titel-loesung.visustruct b/web/lessons/diagramme/l15-titel-loesung.visustruct deleted file mode 100644 index aca606ab..00000000 --- a/web/lessons/diagramme/l15-titel-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 122;101;105;103;101;84;105;116;101;108;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;105;101;32;104;101;105;223;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;34;32;43;32;110;97;109;101; - - diff --git a/web/lessons/diagramme/l15-unterprogramm.visustruct b/web/lessons/diagramme/l15-unterprogramm.visustruct deleted file mode 100644 index 79262ab3..00000000 --- a/web/lessons/diagramme/l15-unterprogramm.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;32;122;117;109;32;81;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;101;107;111;109;109;115;116;32;100;114;101;105;32;70;114;97;103;101;110;46;34; - - diff --git a/web/lessons/diagramme/l15-zeige-summe-haupt.visustruct b/web/lessons/diagramme/l15-zeige-summe-haupt.visustruct deleted file mode 100644 index 77b3ed07..00000000 --- a/web/lessons/diagramme/l15-zeige-summe-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 122;101;105;103;101;83;117;109;109;101;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 122;101;105;103;101;83;117;109;109;101;40;120;44;32;120;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/l15-zeige-summe.visustruct b/web/lessons/diagramme/l15-zeige-summe.visustruct deleted file mode 100644 index a29894d7..00000000 --- a/web/lessons/diagramme/l15-zeige-summe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-countdown-loesung.visustruct b/web/lessons/diagramme/l16-countdown-loesung.visustruct deleted file mode 100644 index 66eeaf1f..00000000 --- a/web/lessons/diagramme/l16-countdown-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;119;101;114;116;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;33;34; - - diff --git a/web/lessons/diagramme/l16-countdown-start.visustruct b/web/lessons/diagramme/l16-countdown-start.visustruct deleted file mode 100644 index b137abdb..00000000 --- a/web/lessons/diagramme/l16-countdown-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;119;101;114;116;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;34; - - diff --git a/web/lessons/diagramme/l16-java-fehler-loesung.visustruct b/web/lessons/diagramme/l16-java-fehler-loesung.visustruct deleted file mode 100644 index 4df77770..00000000 --- a/web/lessons/diagramme/l16-java-fehler-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-java-fehler-start.visustruct b/web/lessons/diagramme/l16-java-fehler-start.visustruct deleted file mode 100644 index aacc051d..00000000 --- a/web/lessons/diagramme/l16-java-fehler-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l16-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 01d3fff6..00000000 --- a/web/lessons/diagramme/l16-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;44;32;82;117;110;100;101;32;49;58;32;34;32;43;32;112;117;110;107;116;101;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/l16-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l16-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 1cb5a556..00000000 --- a/web/lessons/diagramme/l16-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;44;32;82;117;110;100;101;32;49;58;32;34;32;43;32;80;117;110;107;116;101;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/l16-java-punkte-loesung.visustruct b/web/lessons/diagramme/l16-java-punkte-loesung.visustruct deleted file mode 100644 index 233400c3..00000000 --- a/web/lessons/diagramme/l16-java-punkte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-schreibtisch.visustruct b/web/lessons/diagramme/l16-schreibtisch.visustruct deleted file mode 100644 index 75ee4239..00000000 --- a/web/lessons/diagramme/l16-schreibtisch.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-summe-loesung.visustruct b/web/lessons/diagramme/l16-summe-loesung.visustruct deleted file mode 100644 index 9df43c16..00000000 --- a/web/lessons/diagramme/l16-summe-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l16-summe-start.visustruct b/web/lessons/diagramme/l16-summe-start.visustruct deleted file mode 100644 index d698bdd3..00000000 --- a/web/lessons/diagramme/l16-summe-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l17-beispiel.visustruct b/web/lessons/diagramme/l17-beispiel.visustruct deleted file mode 100644 index 5cb8029d..00000000 --- a/web/lessons/diagramme/l17-beispiel.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;109;97;108;32;34;32;43;32;122;97;104;108;32;43;32;34;32;61;32;34;32;43;32;101;114;103;101;98;110;105;115; - - - - - 122;97;104;108;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;119;97;114;32;115;99;104;119;101;114;33;34; - - - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;119;97;114;32;108;101;105;99;104;116;46;34; - - - - diff --git a/web/lessons/diagramme/l17-fakultaet-loesung.visustruct b/web/lessons/diagramme/l17-fakultaet-loesung.visustruct deleted file mode 100644 index 2aabf78a..00000000 --- a/web/lessons/diagramme/l17-fakultaet-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;102;97;107;117;108;116;97;101;116;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;107;117;108;116;97;101;116;32;61;32;102;97;107;117;108;116;97;101;116;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;107;117;108;116;97;101;116; - - diff --git a/web/lessons/diagramme/l17-java-brief-loesung.visustruct b/web/lessons/diagramme/l17-java-brief-loesung.visustruct deleted file mode 100644 index 8d1e922b..00000000 --- a/web/lessons/diagramme/l17-java-brief-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 83;116;114;105;110;103;32;98;114;105;101;102;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;98;114;105;101;102;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;98;114;105;101;102;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;90;101;105;108;101;110;58;32;34;32;43;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 122;101;105;108;101;110;91;105;93;46;99;111;110;116;97;105;110;115;40;34;70;117;99;104;115;34;41; - - - 111;117;116;112;117;116;58;32;34;70;117;99;104;115;32;105;110;32;90;101;105;108;101;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/l17-java-brief-start.visustruct b/web/lessons/diagramme/l17-java-brief-start.visustruct deleted file mode 100644 index 7e7dc36c..00000000 --- a/web/lessons/diagramme/l17-java-brief-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;116;114;105;110;103;32;98;114;105;101;102;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;98;114;105;101;102;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/l17-java-fehler-loesung.visustruct b/web/lessons/diagramme/l17-java-fehler-loesung.visustruct deleted file mode 100644 index 45333fd6..00000000 --- a/web/lessons/diagramme/l17-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/l17-java-fehler-start.visustruct b/web/lessons/diagramme/l17-java-fehler-start.visustruct deleted file mode 100644 index 6288efa3..00000000 --- a/web/lessons/diagramme/l17-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/l17-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l17-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 5081c927..00000000 --- a/web/lessons/diagramme/l17-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;104;97;116;32;34;32;43;32;110;97;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;66;117;99;104;115;116;97;98;101;110;34; - - diff --git a/web/lessons/diagramme/l17-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l17-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 00940be2..00000000 --- a/web/lessons/diagramme/l17-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;104;97;116;32;34;32;43;32;110;97;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;66;117;99;104;115;116;97;98;101;110;34; - - diff --git a/web/lessons/diagramme/l17-java-passwort-loesung.visustruct b/web/lessons/diagramme/l17-java-passwort-loesung.visustruct deleted file mode 100644 index 5a2eaabc..00000000 --- a/web/lessons/diagramme/l17-java-passwort-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;101;105;110;103;97;98;101;32;34;80;97;115;115;119;111;114;116;63;34; - - - 101;105;110;103;97;98;101;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;33;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;115;99;104;101;115;32;80;97;115;115;119;111;114;116;34; - - - - diff --git a/web/lessons/diagramme/l17-ticket-loesung.visustruct b/web/lessons/diagramme/l17-ticket-loesung.visustruct deleted file mode 100644 index 1199fcd2..00000000 --- a/web/lessons/diagramme/l17-ticket-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 97;108;116;101;114;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;75;105;110;100;101;114;116;105;99;107;101;116;58;32;52;32;69;117;114;111;34; - - - - - 111;117;116;112;117;116;58;32;34;69;114;119;97;99;104;115;101;110;101;110;116;105;99;107;101;116;58;32;56;32;69;117;114;111;34; - - - - - - diff --git a/web/lessons/diagramme/l18-beispiel.visustruct b/web/lessons/diagramme/l18-beispiel.visustruct deleted file mode 100644 index f72ef250..00000000 --- a/web/lessons/diagramme/l18-beispiel.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;119;101;114;116;63;34; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;69;110;100;101;34; - - diff --git a/web/lessons/diagramme/l18-java-fehler-loesung.visustruct b/web/lessons/diagramme/l18-java-fehler-loesung.visustruct deleted file mode 100644 index 420f688a..00000000 --- a/web/lessons/diagramme/l18-java-fehler-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;116;105;112;112;32;34;68;101;105;110;32;84;105;112;112;63;34; - - - 118;101;114;115;117;99;104;101;32;61;32;118;101;114;115;117;99;104;101;32;43;32;49; - - - 116;105;112;112;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;115;117;99;104;101;58;32;34;32;43;32;118;101;114;115;117;99;104;101; - - diff --git a/web/lessons/diagramme/l18-java-fehler-start.visustruct b/web/lessons/diagramme/l18-java-fehler-start.visustruct deleted file mode 100644 index 2a299b4b..00000000 --- a/web/lessons/diagramme/l18-java-fehler-start.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;116;105;112;112;32;34;68;101;105;110;32;84;105;112;112;63;34; - - - 116;105;112;112;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 118;101;114;115;117;99;104;101;32;61;32;118;101;114;115;117;99;104;101;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;115;117;99;104;101;58;32;34;32;43;32;118;101;114;115;117;99;104;101; - - diff --git a/web/lessons/diagramme/l18-java-kasse-loesung.visustruct b/web/lessons/diagramme/l18-java-kasse-loesung.visustruct deleted file mode 100644 index cd891002..00000000 --- a/web/lessons/diagramme/l18-java-kasse-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;101;105;115;32;34;80;114;101;105;115;63;32;40;48;32;61;32;69;110;100;101;41;34; - - - 112;114;101;105;115;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;114;101;105;115; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;65;114;116;105;107;101;108;58;32;34;32;43;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;115;117;109;109;101; - - diff --git a/web/lessons/diagramme/l18-js-loesung.visustruct b/web/lessons/diagramme/l18-js-loesung.visustruct deleted file mode 100644 index 607195f0..00000000 --- a/web/lessons/diagramme/l18-js-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;68;117;32;100;97;114;102;115;116;32;119;228;104;108;101;110;46;34; - - - - - 105;110;116;32;106;97;104;114;101;32;61;32;49;54;32;45;32;97;108;116;101;114; - - - 111;117;116;112;117;116;58;32;34;78;111;99;104;32;34;32;43;32;106;97;104;114;101;32;43;32;34;32;74;97;104;114;101;32;119;97;114;116;101;110;46;34; - - - - diff --git a/web/lessons/diagramme/l18-python-loesung.visustruct b/web/lessons/diagramme/l18-python-loesung.visustruct deleted file mode 100644 index dd5f0cca..00000000 --- a/web/lessons/diagramme/l18-python-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;101;110;122;101;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;103;114;101;110;122;101; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/l19-java-fehler-loesung.visustruct b/web/lessons/diagramme/l19-java-fehler-loesung.visustruct deleted file mode 100644 index 6da69702..00000000 --- a/web/lessons/diagramme/l19-java-fehler-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/l19-java-fehler-start.visustruct b/web/lessons/diagramme/l19-java-fehler-start.visustruct deleted file mode 100644 index a7f08974..00000000 --- a/web/lessons/diagramme/l19-java-fehler-start.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/l19-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/l19-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index c3ad8012..00000000 --- a/web/lessons/diagramme/l19-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;103;101;115;117;99;104;116;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/l19-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/l19-java-laeuft-nicht-start.visustruct deleted file mode 100644 index e0b9623b..00000000 --- a/web/lessons/diagramme/l19-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;103;101;115;117;99;104;116;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;71;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - diff --git a/web/lessons/diagramme/l19-java-zaehlen-loesung.visustruct b/web/lessons/diagramme/l19-java-zaehlen-loesung.visustruct deleted file mode 100644 index edd45620..00000000 --- a/web/lessons/diagramme/l19-java-zaehlen-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l2-ausgabe.visustruct b/web/lessons/diagramme/l2-ausgabe.visustruct deleted file mode 100644 index e3b67b58..00000000 --- a/web/lessons/diagramme/l2-ausgabe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;71;117;116;101;110;32;77;111;114;103;101;110;34; - - - 111;117;116;112;117;116;58;32;34;72;101;117;116;101;32;108;101;114;110;101;110;32;119;105;114;32;83;116;114;117;107;116;111;103;114;97;109;109;101;46;34; - - diff --git a/web/lessons/diagramme/l2-hallo-loesung.visustruct b/web/lessons/diagramme/l2-hallo-loesung.visustruct deleted file mode 100644 index 57737002..00000000 --- a/web/lessons/diagramme/l2-hallo-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;87;101;108;116;34; - - diff --git a/web/lessons/diagramme/l2-zwei-zeilen-loesung.visustruct b/web/lessons/diagramme/l2-zwei-zeilen-loesung.visustruct deleted file mode 100644 index 76211f5b..00000000 --- a/web/lessons/diagramme/l2-zwei-zeilen-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;34; - - - 111;117;116;112;117;116;58;32;34;73;99;104;32;108;101;114;110;101;32;83;116;114;117;107;116;111;103;114;97;109;109;101;34; - - diff --git a/web/lessons/diagramme/l20-countdown-loesung.visustruct b/web/lessons/diagramme/l20-countdown-loesung.visustruct deleted file mode 100644 index 1c79f478..00000000 --- a/web/lessons/diagramme/l20-countdown-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - diff --git a/web/lessons/diagramme/l23-java-ergaenzen-loesung.visustruct b/web/lessons/diagramme/l23-java-ergaenzen-loesung.visustruct deleted file mode 100644 index fb42bf3c..00000000 --- a/web/lessons/diagramme/l23-java-ergaenzen-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;101;105;110;107;97;117;102;45;110;101;117;46;116;120;116;34;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;122;101;105;108;101;41; - - - 97;110;122;97;104;108;43;43; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;75;117;99;104;101;110;34;41; - - - 97;110;122;97;104;108;43;43; - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108;32;43;32;34;32;90;101;105;108;101;110;32;103;101;115;112;101;105;99;104;101;114;116;34; - - diff --git a/web/lessons/diagramme/l23-java-ergaenzen-start.visustruct b/web/lessons/diagramme/l23-java-ergaenzen-start.visustruct deleted file mode 100644 index 91054e69..00000000 --- a/web/lessons/diagramme/l23-java-ergaenzen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;101;105;110;107;97;117;102;45;110;101;117;46;116;120;116;34;41; - - diff --git a/web/lessons/diagramme/l23-java-nummern-loesung.visustruct b/web/lessons/diagramme/l23-java-nummern-loesung.visustruct deleted file mode 100644 index 8271df50..00000000 --- a/web/lessons/diagramme/l23-java-nummern-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;109;101;114;32;61;32;49; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;109;101;114;32;43;32;34;46;32;34;32;43;32;122;101;105;108;101; - - - 110;117;109;109;101;114;43;43; - - - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/l23-java-nummern-start.visustruct b/web/lessons/diagramme/l23-java-nummern-start.visustruct deleted file mode 100644 index 0781b837..00000000 --- a/web/lessons/diagramme/l23-java-nummern-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/l24-java-fuchs-loesung.visustruct b/web/lessons/diagramme/l24-java-fuchs-loesung.visustruct deleted file mode 100644 index 6fd1cd06..00000000 --- a/web/lessons/diagramme/l24-java-fuchs-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 122;101;105;108;101;46;99;111;110;116;97;105;110;115;40;34;70;117;99;104;115;34;41; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;70;117;99;104;115;58;32;34;32;43;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l24-java-fuchs-start.visustruct b/web/lessons/diagramme/l24-java-fuchs-start.visustruct deleted file mode 100644 index de3bd722..00000000 --- a/web/lessons/diagramme/l24-java-fuchs-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/l24-java-laengster-loesung.visustruct b/web/lessons/diagramme/l24-java-laengster-loesung.visustruct deleted file mode 100644 index 65d97f21..00000000 --- a/web/lessons/diagramme/l24-java-laengster-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;108;97;101;110;103;115;116;101;114;32;61;32;122;101;105;108;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 122;101;105;108;101;110;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;108;97;101;110;103;115;116;101;114;46;108;101;110;103;116;104;40;41; - - - 108;97;101;110;103;115;116;101;114;32;61;32;122;101;105;108;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;76;228;110;103;115;116;101;114;32;69;105;110;116;114;97;103;58;32;34;32;43;32;108;97;101;110;103;115;116;101;114; - - diff --git a/web/lessons/diagramme/l24-java-laengster-start.visustruct b/web/lessons/diagramme/l24-java-laengster-start.visustruct deleted file mode 100644 index 6f4e03c7..00000000 --- a/web/lessons/diagramme/l24-java-laengster-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - diff --git a/web/lessons/diagramme/l25-java-beste-loesung.visustruct b/web/lessons/diagramme/l25-java-beste-loesung.visustruct deleted file mode 100644 index 69d1fe2d..00000000 --- a/web/lessons/diagramme/l25-java-beste-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;98;101;115;116;101;114;78;97;109;101;32;61;32;34;34; - - - 105;110;116;32;98;101;115;116;101;78;111;116;101;32;61;32;55; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;110;111;116;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;51;93;41; - - - 110;111;116;101;32;60;32;98;101;115;116;101;78;111;116;101; - - - 98;101;115;116;101;78;111;116;101;32;61;32;110;111;116;101; - - - 98;101;115;116;101;114;78;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;66;101;115;116;101;32;78;111;116;101;32;105;110;32;69;110;103;108;105;115;99;104;58;32;34;32;43;32;98;101;115;116;101;114;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;101;78;111;116;101;32;43;32;34;41;34; - - diff --git a/web/lessons/diagramme/l25-java-beste-start.visustruct b/web/lessons/diagramme/l25-java-beste-start.visustruct deleted file mode 100644 index 8f27c0f9..00000000 --- a/web/lessons/diagramme/l25-java-beste-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/l25-java-schnitt-loesung.visustruct b/web/lessons/diagramme/l25-java-schnitt-loesung.visustruct deleted file mode 100644 index 68bfb6b5..00000000 --- a/web/lessons/diagramme/l25-java-schnitt-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 97;110;122;97;104;108;43;43; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;83;99;104;110;105;116;116;32;77;97;116;104;101;58;32;34;32;43;32;115;117;109;109;101;32;47;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/l25-java-schnitt-start.visustruct b/web/lessons/diagramme/l25-java-schnitt-start.visustruct deleted file mode 100644 index 428dd67d..00000000 --- a/web/lessons/diagramme/l25-java-schnitt-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/l26-java-namen-loesung.visustruct b/web/lessons/diagramme/l26-java-namen-loesung.visustruct deleted file mode 100644 index abf1a2c7..00000000 --- a/web/lessons/diagramme/l26-java-namen-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 122;101;105;108;101;46;99;111;110;116;97;105;110;115;40;34;92;34;110;97;109;101;92;34;34;41; - - - 105;110;116;32;115;116;97;114;116;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;101;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;101;41; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/l26-java-namen-start.visustruct b/web/lessons/diagramme/l26-java-namen-start.visustruct deleted file mode 100644 index e5e3e85d..00000000 --- a/web/lessons/diagramme/l26-java-namen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/l26-java-schreiben-loesung.visustruct b/web/lessons/diagramme/l26-java-schreiben-loesung.visustruct deleted file mode 100644 index 2b799fab..00000000 --- a/web/lessons/diagramme/l26-java-schreiben-loesung.visustruct +++ /dev/null @@ -1,50 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;117;99;104;115;34;125; - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;117;110;107;116;101;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;105;110;116;114;97;103;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;110;97;109;101;110;91;105;93;32;43;32;34;92;34;44;32;92;34;112;117;110;107;116;101;92;34;58;32;34;32;43;32;112;117;110;107;116;101;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 101;105;110;116;114;97;103;32;61;32;101;105;110;116;114;97;103;32;43;32;34;44;34; - - - - - 248; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;101;105;110;116;114;97;103;41; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;110;46;108;101;110;103;116;104;32;43;32;34;32;69;105;110;116;114;228;103;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - diff --git a/web/lessons/diagramme/l26-java-schreiben-start.visustruct b/web/lessons/diagramme/l26-java-schreiben-start.visustruct deleted file mode 100644 index 369984a1..00000000 --- a/web/lessons/diagramme/l26-java-schreiben-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;117;99;104;115;34;125; - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;117;110;107;116;101;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/l27-java-json-loesung.visustruct b/web/lessons/diagramme/l27-java-json-loesung.visustruct deleted file mode 100644 index 714b7b27..00000000 --- a/web/lessons/diagramme/l27-java-json-loesung.visustruct +++ /dev/null @@ -1,53 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;116;105;101;114;101;45;110;101;117;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;110;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;105;110;116;114;97;103;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;116;101;105;108;101;91;48;93;32;43;32;34;92;34;44;32;92;34;97;114;116;92;34;58;32;92;34;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;92;34;44;32;92;34;97;108;116;101;114;92;34;58;32;34;32;43;32;116;101;105;108;101;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 101;105;110;116;114;97;103;32;61;32;101;105;110;116;114;97;103;32;43;32;34;44;34; - - - - - 248; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;101;105;110;116;114;97;103;41; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;122;101;105;108;101;110;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;84;105;101;114;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - diff --git a/web/lessons/diagramme/l27-java-json-start.visustruct b/web/lessons/diagramme/l27-java-json-start.visustruct deleted file mode 100644 index b0e4e82f..00000000 --- a/web/lessons/diagramme/l27-java-json-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;116;105;101;114;101;45;110;101;117;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/l27-java-saetze-loesung.visustruct b/web/lessons/diagramme/l27-java-saetze-loesung.visustruct deleted file mode 100644 index e551cbc1..00000000 --- a/web/lessons/diagramme/l27-java-saetze-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;97;101;116;122;101;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;116;101;105;108;101;91;48;93;32;43;32;34;32;40;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;41;32;105;115;116;32;34;32;43;32;116;101;105;108;101;91;50;93;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34;41; - - - 97;110;122;97;104;108;43;43; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108;32;43;32;34;32;83;228;116;122;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - diff --git a/web/lessons/diagramme/l27-java-saetze-start.visustruct b/web/lessons/diagramme/l27-java-saetze-start.visustruct deleted file mode 100644 index 6472f015..00000000 --- a/web/lessons/diagramme/l27-java-saetze-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;97;101;116;122;101;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/aufgabe-alter-haupt-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-alter-haupt-loesung.visustruct deleted file mode 100644 index 3a22dad0..00000000 --- a/web/lessons/diagramme/pt/aufgabe-alter-haupt-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;34; - - - 118;101;114;105;102;105;99;97;114;73;100;97;100;101;40;41; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - diff --git a/web/lessons/diagramme/pt/aufgabe-alter-unterprogramm-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-alter-unterprogramm-loesung.visustruct deleted file mode 100644 index a92d0fe8..00000000 --- a/web/lessons/diagramme/pt/aufgabe-alter-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;109;97;105;111;114;32;100;101;32;105;100;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;109;101;110;111;114;32;100;101;32;105;100;97;100;101;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-ballwurf-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-ballwurf-loesung.visustruct deleted file mode 100644 index 2c73ab7d..00000000 --- a/web/lessons/diagramme/pt/aufgabe-ballwurf-loesung.visustruct +++ /dev/null @@ -1,81 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;97;110;99;97;109;101;110;116;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;110;116;97;103;101;109;32;61;32;48; - - - 100;111;117;98;108;101;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;61;32;48; - - - 83;116;114;105;110;103;32;109;101;108;104;111;114;78;111;109;101;32;61;32;34;34; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;100;105;115;116;97;110;99;105;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 99;111;110;116;97;103;101;109;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;100;105;115;116;97;110;99;105;97; - - - 100;105;115;116;97;110;99;105;97;32;62;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97; - - - 109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;61;32;100;105;115;116;97;110;99;105;97; - - - 109;101;108;104;111;114;78;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;115;111;109;97;32;47;32;99;111;110;116;97;103;101;109; - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;99;105;112;97;110;116;101;115;58;32;34;32;43;32;99;111;110;116;97;103;101;109; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;58;32;34;32;43;32;109;101;100;105;97;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;118;101;110;99;101;100;111;114;46;116;120;116;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;86;101;110;99;101;100;111;114;58;32;34;32;43;32;109;101;108;104;111;114;78;111;109;101;32;43;32;34;32;40;34;32;43;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;43;32;34;32;109;41;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/aufgabe-ballwurf-start.visustruct b/web/lessons/diagramme/pt/aufgabe-ballwurf-start.visustruct deleted file mode 100644 index b089d7a2..00000000 --- a/web/lessons/diagramme/pt/aufgabe-ballwurf-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;97;110;99;97;109;101;110;116;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/aufgabe-bestenliste-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-bestenliste-loesung.visustruct deleted file mode 100644 index 3e727f4c..00000000 --- a/web/lessons/diagramme/pt/aufgabe-bestenliste-loesung.visustruct +++ /dev/null @@ -1,65 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;80;111;110;116;111;115;63;34; - - - 112;111;110;116;111;115;91;105;93;32;61;32;110;117;109;101;114;111; - - - - - 105;110;116;32;112;97;115;115;97;103;101;109;32;61;32;49; - 112;97;115;115;97;103;101;109;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 112;97;115;115;97;103;101;109;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104;32;45;32;112;97;115;115;97;103;101;109; - 105;43;43; - - - 112;111;110;116;111;115;91;105;93;32;60;32;112;111;110;116;111;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;112;111;110;116;111;115;91;105;93; - - - 112;111;110;116;111;115;91;105;93;32;61;32;112;111;110;116;111;115;91;105;32;43;32;49;93; - - - 112;111;110;116;111;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;108;117;103;97;114;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;108;117;103;97;114;32;43;32;34;46;32;34;32;43;32;112;111;110;116;111;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-binaer-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-binaer-loesung.visustruct deleted file mode 100644 index 8565916a..00000000 --- a/web/lessons/diagramme/pt/aufgabe-binaer-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;105;110;116;101;105;114;111;34; - - - 83;116;114;105;110;103;32;98;105;110;97;114;105;111;32;61;32;34;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;110;117;109;101;114;111;32;37;32;50; - - - 98;105;110;97;114;105;111;32;61;32;114;101;115;116;111;32;43;32;98;105;110;97;114;105;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;114;105;111; - - diff --git a/web/lessons/diagramme/pt/aufgabe-caesar-array-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-caesar-array-loesung.visustruct deleted file mode 100644 index 99c6a896..00000000 --- a/web/lessons/diagramme/pt/aufgabe-caesar-array-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;102;97;98;101;116;111;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;81;117;101;32;112;97;108;97;118;114;97;32;40;115;243;32;109;97;105;250;115;99;117;108;97;115;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;102;97;98;101;116;111;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;102;97;98;101;116;111;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 115;101;103;114;101;100;111;32;61;32;115;101;103;114;101;100;111;32;43;32;97;108;102;97;98;101;116;111;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;115;101;103;114;101;100;111; - - diff --git a/web/lessons/diagramme/pt/aufgabe-caesar-array-start.visustruct b/web/lessons/diagramme/pt/aufgabe-caesar-array-start.visustruct deleted file mode 100644 index cba5fadf..00000000 --- a/web/lessons/diagramme/pt/aufgabe-caesar-array-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 99;104;97;114;91;93;32;97;108;102;97;98;101;116;111;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - diff --git a/web/lessons/diagramme/pt/aufgabe-caesar-ascii-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-caesar-ascii-loesung.visustruct deleted file mode 100644 index 20bd7ef5..00000000 --- a/web/lessons/diagramme/pt/aufgabe-caesar-ascii-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;81;117;101;32;112;97;108;97;118;114;97;32;40;115;243;32;109;97;105;250;115;99;117;108;97;115;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;105;103;111;32;61;32;40;105;110;116;41;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;105;103;111;32;61;32;40;99;111;100;105;103;111;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 115;101;103;114;101;100;111;32;61;32;115;101;103;114;101;100;111;32;43;32;40;99;104;97;114;41;32;99;111;100;105;103;111; - - - - - 111;117;116;112;117;116;58;32;115;101;103;114;101;100;111; - - diff --git a/web/lessons/diagramme/pt/aufgabe-caesar-entschluesseln-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-caesar-entschluesseln-loesung.visustruct deleted file mode 100644 index 8cb285f3..00000000 --- a/web/lessons/diagramme/pt/aufgabe-caesar-entschluesseln-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;34;81;117;97;108;32;233;32;111;32;116;101;120;116;111;32;99;105;102;114;97;100;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;103;114;101;100;111;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;105;103;111;32;61;32;40;105;110;116;41;32;115;101;103;114;101;100;111;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;105;103;111;32;61;32;40;99;111;100;105;103;111;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;40;99;104;97;114;41;32;99;111;100;105;103;111; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - diff --git a/web/lessons/diagramme/pt/aufgabe-collatz-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-collatz-loesung.visustruct deleted file mode 100644 index 89d47a06..00000000 --- a/web/lessons/diagramme/pt/aufgabe-collatz-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;112;97;115;115;111;115;32;61;32;48; - - - 110;117;109;101;114;111;32;33;61;32;49; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 112;97;115;115;111;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;80;97;115;115;111;115;58;32;34;32;43;32;112;97;115;115;111;115; - - diff --git a/web/lessons/diagramme/pt/aufgabe-durchschnitt-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-durchschnitt-loesung.visustruct deleted file mode 100644 index 494e08e8..00000000 --- a/web/lessons/diagramme/pt/aufgabe-durchschnitt-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;97;115;32;110;111;116;97;115;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;113;117;97;110;116;105;100;97;100;101; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;110;111;116;97;34; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;111;116;97; - - - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;115;111;109;97;32;42;32;49;46;48;32;47;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;109;101;100;105;97; - - diff --git a/web/lessons/diagramme/pt/aufgabe-fibonacci-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-fibonacci-loesung.visustruct deleted file mode 100644 index bb998bde..00000000 --- a/web/lessons/diagramme/pt/aufgabe-fibonacci-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;110;250;109;101;114;111;115;63;34; - - - 105;110;116;32;110;117;109;101;114;111;49;32;61;32;49; - - - 105;110;116;32;110;117;109;101;114;111;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;113;117;97;110;116;105;100;97;100;101; - 105;43;43; - - - 105;110;116;32;110;117;109;101;114;111;51;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;51; - - - 110;117;109;101;114;111;49;32;61;32;110;117;109;101;114;111;50; - - - 110;117;109;101;114;111;50;32;61;32;110;117;109;101;114;111;51; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-gerade-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-gerade-loesung.visustruct deleted file mode 100644 index d55cbaf9..00000000 --- a/web/lessons/diagramme/pt/aufgabe-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;233;32;112;97;114;34; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;233;32;237;109;112;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-mehrwertsteuer-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-mehrwertsteuer-loesung.visustruct deleted file mode 100644 index ec2a5ffc..00000000 --- a/web/lessons/diagramme/pt/aufgabe-mehrwertsteuer-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;73;86;65;32;61;32;48;46;50;51; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;99;111;83;101;109;73;118;97;32;34;80;114;101;231;111;32;115;101;109;32;73;86;65;63;34; - - - 100;111;117;98;108;101;32;105;109;112;111;115;116;111;32;61;32;112;114;101;99;111;83;101;109;73;118;97;32;42;32;73;86;65; - - - 100;111;117;98;108;101;32;112;114;101;99;111;67;111;109;73;118;97;32;61;32;112;114;101;99;111;83;101;109;73;118;97;32;43;32;105;109;112;111;115;116;111; - - - 111;117;116;112;117;116;58;32;105;109;112;111;115;116;111; - - - 111;117;116;112;117;116;58;32;112;114;101;99;111;67;111;109;73;118;97; - - diff --git a/web/lessons/diagramme/pt/aufgabe-pin-java-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-pin-java-loesung.visustruct deleted file mode 100644 index 6e65f4ec..00000000 --- a/web/lessons/diagramme/pt/aufgabe-pin-java-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;116;101;110;116;97;116;105;118;97;115;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;110;116;114;97;100;97;32;34;73;110;116;114;111;100;117;122;32;111;32;80;73;78;34; - - - 112;105;110;32;61;32;101;110;116;114;97;100;97; - - - 116;101;110;116;97;116;105;118;97;115;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;116;227;111;32;98;108;111;113;117;101;97;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-pin-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-pin-loesung.visustruct deleted file mode 100644 index 91c73954..00000000 --- a/web/lessons/diagramme/pt/aufgabe-pin-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;116;101;110;116;97;116;105;118;97;115;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;73;110;116;114;111;100;117;122;32;111;32;80;73;78;34; - - - 116;101;110;116;97;116;105;118;97;115;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;116;227;111;32;98;108;111;113;117;101;97;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-rechner-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-rechner-loesung.visustruct deleted file mode 100644 index 1e718be1..00000000 --- a/web/lessons/diagramme/pt/aufgabe-rechner-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;49;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;50;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;100;111;114;32;34;79;112;101;114;97;100;111;114;32;40;43;44;32;45;32;111;117;32;42;41;34; - - - 111;112;101;114;97;100;111;114; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;45;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;42;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 111;117;116;112;117;116;58;32;34;79;112;101;114;97;100;111;114;32;100;101;115;99;111;110;104;101;99;105;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-schiffe-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-schiffe-loesung.visustruct deleted file mode 100644 index 72a9a1c8..00000000 --- a/web/lessons/diagramme/pt/aufgabe-schiffe-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;97;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;97;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;97;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;110;97;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;109;97;114;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 105;110;116;32;99;111;108;117;110;97;32;61;32;48; - 99;111;108;117;110;97;32;60;32;109;97;114;91;102;105;108;97;93;46;108;101;110;103;116;104; - 99;111;108;117;110;97;43;43; - - - 109;97;114;91;102;105;108;97;93;91;99;111;108;117;110;97;93; - - - 111;117;116;112;117;116;58;32;99;111;108;117;110;97;115;91;99;111;108;117;110;97;93;32;43;32;40;102;105;108;97;32;43;32;49;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-schiffe-start.visustruct b/web/lessons/diagramme/pt/aufgabe-schiffe-start.visustruct deleted file mode 100644 index 57468b14..00000000 --- a/web/lessons/diagramme/pt/aufgabe-schiffe-start.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;97;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;97;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;97;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;110;97;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - diff --git a/web/lessons/diagramme/pt/aufgabe-speisekarte-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-speisekarte-loesung.visustruct deleted file mode 100644 index 4ff18c2b..00000000 --- a/web/lessons/diagramme/pt/aufgabe-speisekarte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;97;116;111;32;34;81;117;101;32;112;114;97;116;111;63;32;40;49;44;32;50;32;111;117;32;51;41;34; - - - 112;114;97;116;111; - - - 111;117;116;112;117;116;58;32;34;83;97;108;97;100;97;32;103;114;101;103;97;34; - - - - - 111;117;116;112;117;116;58;32;34;66;97;99;97;108;104;97;117;32;224;32;66;114;225;115;34; - - - - - 111;117;116;112;117;116;58;32;34;87;114;97;112;32;100;101;32;102;97;108;97;102;101;108;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;101;32;117;109;32;110;250;109;101;114;111;32;118;225;108;105;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-summe-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-summe-loesung.visustruct deleted file mode 100644 index 24676f08..00000000 --- a/web/lessons/diagramme/pt/aufgabe-summe-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;49;32;61;32;52; - - - 105;110;116;32;110;117;109;101;114;111;50;32;61;32;51; - - - 105;110;116;32;115;111;109;97;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/aufgabe-tafeldienst-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-tafeldienst-loesung.visustruct deleted file mode 100644 index 07781f6a..00000000 --- a/web/lessons/diagramme/pt/aufgabe-tafeldienst-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;111;109;101;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;111;109;101;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;115;111;114;116;101;97;100;111;32;34;78;250;109;101;114;111;32;115;111;114;116;101;97;100;111;32;40;49;32;97;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;76;105;109;112;97;32;111;32;113;117;97;100;114;111;58;32;34;32;43;32;110;111;109;101;115;91;115;111;114;116;101;97;100;111;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/pt/aufgabe-verdoppeln-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-verdoppeln-loesung.visustruct deleted file mode 100644 index 7a6c95a4..00000000 --- a/web/lessons/diagramme/pt/aufgabe-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;78;250;109;101;114;111;32;100;117;112;108;105;99;97;100;111;58;32;34;32;43;32;110;117;109;101;114;111; - - diff --git a/web/lessons/diagramme/pt/aufgabe-verleih-java-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-verleih-java-loesung.visustruct deleted file mode 100644 index b2fffdcb..00000000 --- a/web/lessons/diagramme/pt/aufgabe-verleih-java-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;61;32;98;105;99;105;99;108;101;116;97;32;101;108;233;116;114;105;99;97;44;32;50;32;61;32;116;114;111;116;105;110;101;116;101;32;101;108;233;116;114;105;99;97;44;32;48;32;61;32;115;97;105;114;34; - - - 101;115;99;111;108;104;97;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - - -1; - - - - - 101;115;99;111;108;104;97;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;66;105;99;105;99;108;101;116;97;32;114;101;115;101;114;118;97;100;97;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;84;114;111;116;105;110;101;116;101;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;97;32;105;110;118;225;108;105;100;97;34; - - - - - - - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-verleih-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-verleih-loesung.visustruct deleted file mode 100644 index c24b29ce..00000000 --- a/web/lessons/diagramme/pt/aufgabe-verleih-loesung.visustruct +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;61;32;98;105;99;105;99;108;101;116;97;32;101;108;233;116;114;105;99;97;44;32;50;32;61;32;116;114;111;116;105;110;101;116;101;32;101;108;233;116;114;105;99;97;44;32;48;32;61;32;115;97;105;114;34; - - - 101;115;99;111;108;104;97; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - - 83;97;105;114;32;100;111;32;109;101;110;117; - - - - - 111;117;116;112;117;116;58;32;34;66;105;99;105;99;108;101;116;97;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;84;114;111;116;105;110;101;116;101;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;97;32;105;110;118;225;108;105;100;97;34; - - - - - - diff --git a/web/lessons/diagramme/pt/aufgabe-vokabeln-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-vokabeln-loesung.visustruct deleted file mode 100644 index 8428b1f0..00000000 --- a/web/lessons/diagramme/pt/aufgabe-vokabeln-loesung.visustruct +++ /dev/null @@ -1,60 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;99;97;98;117;108;97;114;105;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;101;114;116;97;115;32;61;32;48; - - - 105;110;116;32;99;111;110;116;97;103;101;109;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;67;111;109;111;32;115;101;32;100;105;122;32;34;32;43;32;112;97;114;116;101;115;91;48;93;32;43;32;34;32;101;109;32;105;110;103;108;234;115;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;114;101;115;112;111;115;116;97; - - - 99;111;110;116;97;103;101;109;43;43; - - - 114;101;115;112;111;115;116;97;46;101;113;117;97;108;115;40;112;97;114;116;101;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;67;101;114;116;111;33;34; - - - 99;101;114;116;97;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;69;114;114;97;100;111;44;32;233;32;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;46;34; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;101;114;116;97;115;32;43;32;34;32;100;101;32;34;32;43;32;99;111;110;116;97;103;101;109;32;43;32;34;32;99;101;114;116;97;115;34; - - diff --git a/web/lessons/diagramme/pt/aufgabe-vokabeln-start.visustruct b/web/lessons/diagramme/pt/aufgabe-vokabeln-start.visustruct deleted file mode 100644 index 878d7ebb..00000000 --- a/web/lessons/diagramme/pt/aufgabe-vokabeln-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;99;97;98;117;108;97;114;105;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/aufgabe-volljaehrig-loesung.visustruct b/web/lessons/diagramme/pt/aufgabe-volljaehrig-loesung.visustruct deleted file mode 100644 index c6df7fd9..00000000 --- a/web/lessons/diagramme/pt/aufgabe-volljaehrig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;109;97;105;111;114;32;100;101;32;105;100;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;109;101;110;111;114;32;100;101;32;105;100;97;100;101;34; - - - - diff --git a/web/lessons/diagramme/pt/dt-buffered.visustruct b/web/lessons/diagramme/pt/dt-buffered.visustruct deleted file mode 100644 index 27a64807..00000000 --- a/web/lessons/diagramme/pt/dt-buffered.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/dt-csv.visustruct b/web/lessons/diagramme/pt/dt-csv.visustruct deleted file mode 100644 index 600f54e2..00000000 --- a/web/lessons/diagramme/pt/dt-csv.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - 105;110;116;32;109;97;116;101;109;97;116;105;99;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;109;97;116;101;109;97;116;105;99;97;32;43;32;34;32;97;32;77;97;116;101;109;225;116;105;99;97;34; - - diff --git a/web/lessons/diagramme/pt/dt-json.visustruct b/web/lessons/diagramme/pt/dt-json.visustruct deleted file mode 100644 index 7b3c846a..00000000 --- a/web/lessons/diagramme/pt/dt-json.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;123;92;34;110;111;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;101;115;112;101;99;105;101;92;34;58;32;92;34;99;97;98;114;97;92;34;44;32;92;34;105;100;97;100;101;92;34;58;32;54;125;44;34; - - - 105;110;116;32;105;110;105;99;105;111;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;110;111;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;102;105;109;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;105;110;105;99;105;111;41; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;108;105;110;104;97;46;115;117;98;115;116;114;105;110;103;40;105;110;105;99;105;111;44;32;102;105;109;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101; - - diff --git a/web/lessons/diagramme/pt/dt-lesen.visustruct b/web/lessons/diagramme/pt/dt-lesen.visustruct deleted file mode 100644 index 2b271a1b..00000000 --- a/web/lessons/diagramme/pt/dt-lesen.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/kino-einmaleins-loesung.visustruct b/web/lessons/diagramme/pt/kino-einmaleins-loesung.visustruct deleted file mode 100644 index 46fa2078..00000000 --- a/web/lessons/diagramme/pt/kino-einmaleins-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;116;97;98;101;108;97;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;101;108;97;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;101;108;97;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;101;108;97;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;32;40;49;32;97;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;32;40;49;32;97;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;101;108;97;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - diff --git a/web/lessons/diagramme/pt/kino-frei-loesung.visustruct b/web/lessons/diagramme/pt/kino-frei-loesung.visustruct deleted file mode 100644 index b41a0636..00000000 --- a/web/lessons/diagramme/pt/kino-frei-loesung.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;108;105;118;114;101;115;32;61;32;48; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 105;110;116;32;108;117;103;97;114;32;61;32;48; - 108;117;103;97;114;32;60;32;108;117;103;97;114;101;115;91;102;105;108;97;93;46;108;101;110;103;116;104; - 108;117;103;97;114;43;43; - - - 33;108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 108;105;118;114;101;115;32;61;32;108;105;118;114;101;115;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;76;105;118;114;101;115;58;32;34;32;43;32;108;105;118;114;101;115; - - diff --git a/web/lessons/diagramme/pt/kino-frei-start.visustruct b/web/lessons/diagramme/pt/kino-frei-start.visustruct deleted file mode 100644 index 3f5fcdb7..00000000 --- a/web/lessons/diagramme/pt/kino-frei-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - diff --git a/web/lessons/diagramme/pt/kino-karte-loesung.visustruct b/web/lessons/diagramme/pt/kino-karte-loesung.visustruct deleted file mode 100644 index fe0340b2..00000000 --- a/web/lessons/diagramme/pt/kino-karte-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;102;105;108;97;32;34;70;105;108;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;117;103;97;114;32;34;76;117;103;97;114;34; - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 111;117;116;112;117;116;58;32;34;74;225;32;118;101;110;100;105;100;111;34; - - - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;86;101;110;100;105;100;111;58;32;102;105;108;97;32;34;32;43;32;102;105;108;97;32;43;32;34;44;32;108;117;103;97;114;32;34;32;43;32;108;117;103;97;114; - - - - diff --git a/web/lessons/diagramme/pt/kino-karte-start.visustruct b/web/lessons/diagramme/pt/kino-karte-start.visustruct deleted file mode 100644 index eeefcbdc..00000000 --- a/web/lessons/diagramme/pt/kino-karte-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;102;105;108;97;32;34;70;105;108;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;117;103;97;114;32;34;76;117;103;97;114;34; - - diff --git a/web/lessons/diagramme/pt/kino-saalplan.visustruct b/web/lessons/diagramme/pt/kino-saalplan.visustruct deleted file mode 100644 index f725ccea..00000000 --- a/web/lessons/diagramme/pt/kino-saalplan.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;34; - - - 105;110;116;32;108;117;103;97;114;32;61;32;48; - 108;117;103;97;114;32;60;32;108;117;103;97;114;101;115;91;102;105;108;97;93;46;108;101;110;103;116;104; - 108;117;103;97;114;43;43; - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;88;34; - - - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - diff --git a/web/lessons/diagramme/pt/kino-verkaufen.visustruct b/web/lessons/diagramme/pt/kino-verkaufen.visustruct deleted file mode 100644 index 12d70197..00000000 --- a/web/lessons/diagramme/pt/kino-verkaufen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;70;105;108;97;115;58;32;34;32;43;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;76;117;103;97;114;101;115;32;112;111;114;32;102;105;108;97;58;32;34;32;43;32;108;117;103;97;114;101;115;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;70;105;108;97;32;49;44;32;108;117;103;97;114;32;50;32;118;101;110;100;105;100;111;63;32;34;32;43;32;108;117;103;97;114;101;115;91;49;93;91;50;93; - - diff --git a/web/lessons/diagramme/pt/l04-gross-klein-loesung.visustruct b/web/lessons/diagramme/pt/l04-gross-klein-loesung.visustruct deleted file mode 100644 index 94febc4d..00000000 --- a/web/lessons/diagramme/pt/l04-gross-klein-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 105;110;116;32;78;117;109;101;114;111;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;78;117;109;101;114;111; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;97;100;111;58;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - diff --git a/web/lessons/diagramme/pt/l04-gross-klein-start.visustruct b/web/lessons/diagramme/pt/l04-gross-klein-start.visustruct deleted file mode 100644 index 070d436d..00000000 --- a/web/lessons/diagramme/pt/l04-gross-klein-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 105;110;116;32;78;117;109;101;114;111;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;78;85;77;69;82;79; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;97;100;111;58;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - diff --git a/web/lessons/diagramme/pt/l04-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l04-java-fehler-loesung.visustruct deleted file mode 100644 index 358965fd..00000000 --- a/web/lessons/diagramme/pt/l04-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/pt/l04-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l04-java-fehler-start.visustruct deleted file mode 100644 index 68c8d7f3..00000000 --- a/web/lessons/diagramme/pt/l04-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;34; - - diff --git a/web/lessons/diagramme/pt/l04-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l04-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 486512b0..00000000 --- a/web/lessons/diagramme/pt/l04-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l04-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l04-java-laeuft-nicht-start.visustruct deleted file mode 100644 index f836ebf2..00000000 --- a/web/lessons/diagramme/pt/l04-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;73;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34;41; - - diff --git a/web/lessons/diagramme/pt/l04-java-steckbrief-loesung.visustruct b/web/lessons/diagramme/pt/l04-java-steckbrief-loesung.visustruct deleted file mode 100644 index 37b12afd..00000000 --- a/web/lessons/diagramme/pt/l04-java-steckbrief-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 100;111;117;98;108;101;32;97;108;116;117;114;97;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/pt/l04-rechnen.visustruct b/web/lessons/diagramme/pt/l04-rechnen.visustruct deleted file mode 100644 index 009f1905..00000000 --- a/web/lessons/diagramme/pt/l04-rechnen.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - diff --git a/web/lessons/diagramme/pt/l04-reihenfolge-loesung.visustruct b/web/lessons/diagramme/pt/l04-reihenfolge-loesung.visustruct deleted file mode 100644 index 623b29c5..00000000 --- a/web/lessons/diagramme/pt/l04-reihenfolge-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;51; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;52; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - diff --git a/web/lessons/diagramme/pt/l04-reihenfolge-start.visustruct b/web/lessons/diagramme/pt/l04-reihenfolge-start.visustruct deleted file mode 100644 index 1dbafec0..00000000 --- a/web/lessons/diagramme/pt/l04-reihenfolge-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;52; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;51; - - diff --git a/web/lessons/diagramme/pt/l04-tauschen-loesung.visustruct b/web/lessons/diagramme/pt/l04-tauschen-loesung.visustruct deleted file mode 100644 index 6b244a71..00000000 --- a/web/lessons/diagramme/pt/l04-tauschen-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;101;115;113;117;101;114;100;97;32;61;32;34;115;117;109;111;34; - - - 83;116;114;105;110;103;32;100;105;114;101;105;116;97;32;61;32;34;225;103;117;97;34; - - - 83;116;114;105;110;103;32;97;117;120;32;61;32;101;115;113;117;101;114;100;97; - - - 101;115;113;117;101;114;100;97;32;61;32;100;105;114;101;105;116;97; - - - 100;105;114;101;105;116;97;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;69;115;113;117;101;114;100;97;58;32;34;32;43;32;101;115;113;117;101;114;100;97; - - - 111;117;116;112;117;116;58;32;34;68;105;114;101;105;116;97;58;32;34;32;43;32;100;105;114;101;105;116;97; - - diff --git a/web/lessons/diagramme/pt/l04-tauschen-start.visustruct b/web/lessons/diagramme/pt/l04-tauschen-start.visustruct deleted file mode 100644 index af12d118..00000000 --- a/web/lessons/diagramme/pt/l04-tauschen-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 83;116;114;105;110;103;32;101;115;113;117;101;114;100;97;32;61;32;34;115;117;109;111;34; - - - 83;116;114;105;110;103;32;100;105;114;101;105;116;97;32;61;32;34;225;103;117;97;34; - - - 111;117;116;112;117;116;58;32;34;69;115;113;117;101;114;100;97;58;32;34;32;43;32;101;115;113;117;101;114;100;97; - - - 111;117;116;112;117;116;58;32;34;68;105;114;101;105;116;97;58;32;34;32;43;32;100;105;114;101;105;116;97; - - diff --git a/web/lessons/diagramme/pt/l04-tauschen.visustruct b/web/lessons/diagramme/pt/l04-tauschen.visustruct deleted file mode 100644 index 4be6aae1..00000000 --- a/web/lessons/diagramme/pt/l04-tauschen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/pt/l05-datentypen.visustruct b/web/lessons/diagramme/pt/l05-datentypen.visustruct deleted file mode 100644 index 599ee696..00000000 --- a/web/lessons/diagramme/pt/l05-datentypen.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;52; - - - 100;111;117;98;108;101;32;97;108;116;117;114;97;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;73;110;234;115;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;109;34; - - diff --git a/web/lessons/diagramme/pt/l05-division.visustruct b/web/lessons/diagramme/pt/l05-division.visustruct deleted file mode 100644 index 01533e16..00000000 --- a/web/lessons/diagramme/pt/l05-division.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;49;51;53; - - - 105;110;116;32;104;111;114;97;115;32;61;32;109;105;110;117;116;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;109;105;110;117;116;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;104;111;114;97;115;32;43;32;34;32;104;111;114;97;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;109;105;110;117;116;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l05-einkauf-loesung.visustruct b/web/lessons/diagramme/pt/l05-einkauf-loesung.visustruct deleted file mode 100644 index 85deedd6..00000000 --- a/web/lessons/diagramme/pt/l05-einkauf-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;101;99;111;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;112;114;101;99;111;84;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;112;114;101;99;111; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;112;114;101;99;111;84;111;116;97;108;32;43;32;34;32;101;117;114;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l05-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l05-java-fehler-loesung.visustruct deleted file mode 100644 index 29a4d575..00000000 --- a/web/lessons/diagramme/pt/l05-java-fehler-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;97;108;100;111;32;61;32;50;48; - - - 105;110;116;32;112;114;101;99;111;32;61;32;55; - - - 115;97;108;100;111;32;61;32;115;97;108;100;111;32;45;32;112;114;101;99;111; - - - 111;117;116;112;117;116;58;32;34;82;101;115;116;97;58;32;34;32;43;32;115;97;108;100;111; - - diff --git a/web/lessons/diagramme/pt/l05-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l05-java-fehler-start.visustruct deleted file mode 100644 index 9c7b9e8d..00000000 --- a/web/lessons/diagramme/pt/l05-java-fehler-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;97;108;100;111;32;61;32;50;48; - - - 105;110;116;32;112;114;101;99;111;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;82;101;115;116;97;58;32;34;32;43;32;115;97;108;100;111; - - - 115;97;108;100;111;32;61;32;115;97;108;100;111;32;45;32;112;114;101;99;111; - - diff --git a/web/lessons/diagramme/pt/l05-java-kino-loesung.visustruct b/web/lessons/diagramme/pt/l05-java-kino-loesung.visustruct deleted file mode 100644 index 1a4fd66f..00000000 --- a/web/lessons/diagramme/pt/l05-java-kino-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;114;101;99;111;32;61;32;57; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;101;99;111;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/pt/l05-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l05-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 3dcbf271..00000000 --- a/web/lessons/diagramme/pt/l05-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;102;97;116;105;97;115;32;61;32;56; - - - 105;110;116;32;112;101;115;115;111;97;115;32;61;32;52; - - - 105;110;116;32;112;111;114;80;101;115;115;111;97;32;61;32;102;97;116;105;97;115;32;47;32;112;101;115;115;111;97;115; - - - 111;117;116;112;117;116;58;32;34;67;97;100;97;32;117;109;32;114;101;99;101;98;101;32;34;32;43;32;112;111;114;80;101;115;115;111;97;32;43;32;34;32;102;97;116;105;97;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l05-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l05-java-laeuft-nicht-start.visustruct deleted file mode 100644 index c43a4807..00000000 --- a/web/lessons/diagramme/pt/l05-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;102;97;116;105;97;115;32;61;32;56; - - - 105;110;116;32;112;101;115;115;111;97;115;32;61;32;52; - - - 105;110;116;32;112;111;114;80;101;115;115;111;97;32;61;32;102;97;116;105;97;115;32;47;32;112;101;115;115;111;97;115; - - - 111;117;116;112;117;116;58;32;34;67;97;100;97;32;117;109;32;114;101;99;101;98;101;32;34;32;43;32;112;111;114;112;101;115;115;111;97;32;43;32;34;32;102;97;116;105;97;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l05-rechteck-loesung.visustruct b/web/lessons/diagramme/pt/l05-rechteck-loesung.visustruct deleted file mode 100644 index a3934ea2..00000000 --- a/web/lessons/diagramme/pt/l05-rechteck-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;61;32;55; - - - 105;110;116;32;108;97;114;103;117;114;97;32;61;32;52; - - - 105;110;116;32;97;114;101;97;32;61;32;99;111;109;112;114;105;109;101;110;116;111;32;42;32;108;97;114;103;117;114;97; - - - 105;110;116;32;112;101;114;105;109;101;116;114;111;32;61;32;50;32;42;32;40;99;111;109;112;114;105;109;101;110;116;111;32;43;32;108;97;114;103;117;114;97;41; - - - 111;117;116;112;117;116;58;32;34;193;114;101;97;58;32;34;32;43;32;97;114;101;97; - - - 111;117;116;112;117;116;58;32;34;80;101;114;237;109;101;116;114;111;58;32;34;32;43;32;112;101;114;105;109;101;116;114;111; - - diff --git a/web/lessons/diagramme/pt/l05-sekunden-loesung.visustruct b/web/lessons/diagramme/pt/l05-sekunden-loesung.visustruct deleted file mode 100644 index 62eb9940..00000000 --- a/web/lessons/diagramme/pt/l05-sekunden-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;117;116;111;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;101;103;117;110;100;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l05-sekunden-start.visustruct b/web/lessons/diagramme/pt/l05-sekunden-start.visustruct deleted file mode 100644 index f5803d1d..00000000 --- a/web/lessons/diagramme/pt/l05-sekunden-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;117;116;111;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;101;103;117;110;100;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l05-sequenz.visustruct b/web/lessons/diagramme/pt/l05-sequenz.visustruct deleted file mode 100644 index 380d07db..00000000 --- a/web/lessons/diagramme/pt/l05-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;61;32;53; - - - 105;110;116;32;108;97;114;103;117;114;97;32;61;32;51; - - - 105;110;116;32;97;114;101;97;32;61;32;99;111;109;112;114;105;109;101;110;116;111;32;42;32;108;97;114;103;117;114;97; - - - 111;117;116;112;117;116;58;32;97;114;101;97; - - diff --git a/web/lessons/diagramme/pt/l06-begruessung-loesung.visustruct b/web/lessons/diagramme/pt/l06-begruessung-loesung.visustruct deleted file mode 100644 index dd754a20..00000000 --- a/web/lessons/diagramme/pt/l06-begruessung-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;67;111;109;111;32;116;101;32;99;104;97;109;97;115;63;34; - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;34;32;43;32;110;111;109;101;32;43;32;34;33;34; - - diff --git a/web/lessons/diagramme/pt/l06-eva.visustruct b/web/lessons/diagramme/pt/l06-eva.visustruct deleted file mode 100644 index a695c357..00000000 --- a/web/lessons/diagramme/pt/l06-eva.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l06-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l06-java-fehler-loesung.visustruct deleted file mode 100644 index 8ecbd2e7..00000000 --- a/web/lessons/diagramme/pt/l06-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;40;97;32;43;32;98;41; - - diff --git a/web/lessons/diagramme/pt/l06-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l06-java-fehler-start.visustruct deleted file mode 100644 index 4e4723bc..00000000 --- a/web/lessons/diagramme/pt/l06-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;97;32;43;32;98; - - diff --git a/web/lessons/diagramme/pt/l06-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l06-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 90dc1170..00000000 --- a/web/lessons/diagramme/pt/l06-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;243;120;105;109;111;32;97;110;111;32;116;101;110;115;32;34;32;43;32;40;105;100;97;100;101;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/pt/l06-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l06-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 9b16b8f4..00000000 --- a/web/lessons/diagramme/pt/l06-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;243;120;105;109;111;32;97;110;111;32;116;101;110;115;32;34;32;43;32;40;73;100;97;100;101;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/pt/l06-java-tage-loesung.visustruct b/web/lessons/diagramme/pt/l06-java-tage-loesung.visustruct deleted file mode 100644 index a3a2bd8b..00000000 --- a/web/lessons/diagramme/pt/l06-java-tage-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;111;115;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;110;116;32;100;105;97;115;32;61;32;97;110;111;115;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;83;227;111;32;99;101;114;99;97;32;100;101;32;34;32;43;32;100;105;97;115;32;43;32;34;32;100;105;97;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l06-mittelwert-loesung.visustruct b/web/lessons/diagramme/pt/l06-mittelwert-loesung.visustruct deleted file mode 100644 index 55e47560..00000000 --- a/web/lessons/diagramme/pt/l06-mittelwert-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;58;32;34;32;43;32;109;101;100;105;97; - - diff --git a/web/lessons/diagramme/pt/l06-rechner-loesung.visustruct b/web/lessons/diagramme/pt/l06-rechner-loesung.visustruct deleted file mode 100644 index ff67d0d6..00000000 --- a/web/lessons/diagramme/pt/l06-rechner-loesung.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;116;111;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;116;111;58;32;34;32;43;32;112;114;111;100;117;116;111; - - diff --git a/web/lessons/diagramme/pt/l07-alter-pruefen.visustruct b/web/lessons/diagramme/pt/l07-alter-pruefen.visustruct deleted file mode 100644 index 16636f6a..00000000 --- a/web/lessons/diagramme/pt/l07-alter-pruefen.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;201;115;32;109;97;105;111;114;32;100;101;32;105;100;97;100;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;105;110;100;97;32;110;227;111;32;233;115;32;109;97;105;111;114;32;100;101;32;105;100;97;100;101;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l07-betrag-loesung.visustruct b/web/lessons/diagramme/pt/l07-betrag-loesung.visustruct deleted file mode 100644 index 014d3bd4..00000000 --- a/web/lessons/diagramme/pt/l07-betrag-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111;32;60;32;48; - - - 110;117;109;101;114;111;32;61;32;48;32;45;32;110;117;109;101;114;111; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - diff --git a/web/lessons/diagramme/pt/l07-gerade-loesung.visustruct b/web/lessons/diagramme/pt/l07-gerade-loesung.visustruct deleted file mode 100644 index 62a33245..00000000 --- a/web/lessons/diagramme/pt/l07-gerade-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;112;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;237;109;112;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/l07-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l07-java-fehler-loesung.visustruct deleted file mode 100644 index 70c44da2..00000000 --- a/web/lessons/diagramme/pt/l07-java-fehler-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;53; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - diff --git a/web/lessons/diagramme/pt/l07-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l07-java-fehler-start.visustruct deleted file mode 100644 index d6df005e..00000000 --- a/web/lessons/diagramme/pt/l07-java-fehler-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;53; - - - 112;111;110;116;111;115;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - diff --git a/web/lessons/diagramme/pt/l07-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l07-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 817d1131..00000000 --- a/web/lessons/diagramme/pt/l07-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - diff --git a/web/lessons/diagramme/pt/l07-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l07-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 666440a0..00000000 --- a/web/lessons/diagramme/pt/l07-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - diff --git a/web/lessons/diagramme/pt/l07-java-pizza-loesung.visustruct b/web/lessons/diagramme/pt/l07-java-pizza-loesung.visustruct deleted file mode 100644 index d720f965..00000000 --- a/web/lessons/diagramme/pt/l07-java-pizza-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;101;99;111;32;61;32;56; - - - 105;110;116;32;113;117;101;105;106;111;32;61;32;50; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;40;112;114;101;99;111;32;43;32;113;117;101;105;106;111;41;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/pt/l07-java-pizza-start.visustruct b/web/lessons/diagramme/pt/l07-java-pizza-start.visustruct deleted file mode 100644 index 76b1479e..00000000 --- a/web/lessons/diagramme/pt/l07-java-pizza-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 105;110;116;32;112;114;101;99;111;32;61;32;56; - - - 105;110;116;32;113;117;101;105;106;111;32;61;32;50; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;101;99;111;32;43;32;113;117;101;105;106;111;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/pt/l07-maximum-loesung.visustruct b/web/lessons/diagramme/pt/l07-maximum-loesung.visustruct deleted file mode 100644 index f810fd6d..00000000 --- a/web/lessons/diagramme/pt/l07-maximum-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - diff --git a/web/lessons/diagramme/pt/l07-rabatt.visustruct b/web/lessons/diagramme/pt/l07-rabatt.visustruct deleted file mode 100644 index e27b79bd..00000000 --- a/web/lessons/diagramme/pt/l07-rabatt.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;99;111;32;34;80;114;101;231;111;32;101;109;32;101;117;114;111;115;63;34; - - - 112;114;101;99;111;32;62;32;53;48; - - - 112;114;101;99;111;32;61;32;112;114;101;99;111;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;65;32;112;97;103;97;114;58;32;34;32;43;32;112;114;101;99;111;32;43;32;34;32;101;117;114;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l08-gueltig-loesung.visustruct b/web/lessons/diagramme/pt/l08-gueltig-loesung.visustruct deleted file mode 100644 index 50359443..00000000 --- a/web/lessons/diagramme/pt/l08-gueltig-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;78;111;116;97;63;34; - - - 110;111;116;97;32;62;61;32;49;32;38;38;32;110;111;116;97;32;60;61;32;53; - - - 111;117;116;112;117;116;58;32;34;118;225;108;105;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;118;225;108;105;100;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-java-bestanden-loesung.visustruct b/web/lessons/diagramme/pt/l08-java-bestanden-loesung.visustruct deleted file mode 100644 index b2ad09c2..00000000 --- a/web/lessons/diagramme/pt/l08-java-bestanden-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;112;111;110;116;111;115;32;34;81;117;97;110;116;111;115;32;112;111;110;116;111;115;63;34; - - - 112;111;110;116;111;115;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;97;112;114;111;118;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;114;101;112;114;111;118;97;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l08-java-fehler-loesung.visustruct deleted file mode 100644 index 6cfd1f42..00000000 --- a/web/lessons/diagramme/pt/l08-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;101;32;105;100;97;100;101;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l08-java-fehler-start.visustruct deleted file mode 100644 index 2032550c..00000000 --- a/web/lessons/diagramme/pt/l08-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;101;32;105;100;97;100;101;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l08-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index e67f03cf..00000000 --- a/web/lessons/diagramme/pt/l08-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;53; - - - 105;100;97;100;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;32;101;110;116;114;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;112;111;100;101;32;101;110;116;114;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l08-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 81e5beaa..00000000 --- a/web/lessons/diagramme/pt/l08-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;53; - - - 73;100;97;100;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;32;101;110;116;114;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;112;111;100;101;32;101;110;116;114;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-jugendticket.visustruct b/web/lessons/diagramme/pt/l08-jugendticket.visustruct deleted file mode 100644 index cbca48c0..00000000 --- a/web/lessons/diagramme/pt/l08-jugendticket.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;50;32;38;38;32;105;100;97;100;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;106;111;118;101;109;34; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;32;98;105;108;104;101;116;101;32;106;111;118;101;109;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-note.visustruct b/web/lessons/diagramme/pt/l08-note.visustruct deleted file mode 100644 index f29ec9a0..00000000 --- a/web/lessons/diagramme/pt/l08-note.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;78;111;116;97;63;34; - - - 110;111;116;97;32;60;32;49;32;124;124;32;110;111;116;97;32;62;32;53; - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;110;111;116;97;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;32;110;111;116;97;32;34;32;43;32;110;111;116;97;32;43;32;34;32;233;32;118;225;108;105;100;97;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-schaltjahr-loesung.visustruct b/web/lessons/diagramme/pt/l08-schaltjahr-loesung.visustruct deleted file mode 100644 index abfb9f9a..00000000 --- a/web/lessons/diagramme/pt/l08-schaltjahr-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;111;32;34;65;110;111;63;34; - - - 40;97;110;111;32;37;32;52;32;61;61;32;48;32;38;38;32;97;110;111;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;97;110;111;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;65;110;111;32;98;105;115;115;101;120;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;233;32;97;110;111;32;98;105;115;115;101;120;116;111;34; - - - - diff --git a/web/lessons/diagramme/pt/l08-wochenende-loesung.visustruct b/web/lessons/diagramme/pt/l08-wochenende-loesung.visustruct deleted file mode 100644 index 8f51bddc..00000000 --- a/web/lessons/diagramme/pt/l08-wochenende-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;68;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;70;105;109;32;100;101;32;115;101;109;97;110;97;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;97;32;250;116;105;108;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-ampel.visustruct b/web/lessons/diagramme/pt/l09-ampel.visustruct deleted file mode 100644 index 404bd545..00000000 --- a/web/lessons/diagramme/pt/l09-ampel.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;99;111;114;32;34;68;101;32;113;117;101;32;99;111;114;32;101;115;116;225;32;111;32;115;101;109;225;102;111;114;111;63;34; - - - 99;111;114; - - - 111;117;116;112;117;116;58;32;34;80;97;114;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;112;101;114;97;34; - - - - - 111;117;116;112;117;116;58;32;34;65;118;97;110;231;97;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;99;111;110;104;101;231;111;32;101;115;116;97;32;99;111;114;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-automat.visustruct b/web/lessons/diagramme/pt/l09-automat.visustruct deleted file mode 100644 index d516cbce..00000000 --- a/web/lessons/diagramme/pt/l09-automat.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;225;103;117;97;44;32;50;32;115;117;109;111;44;32;51;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;34; - - - 101;115;99;111;108;104;97; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;97;32;116;117;97;32;225;103;117;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;115;117;109;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;101;32;98;111;116;227;111;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l09-java-fehler-loesung.visustruct deleted file mode 100644 index 55e1be7e..00000000 --- a/web/lessons/diagramme/pt/l09-java-fehler-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;81;117;101;32;100;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l09-java-fehler-start.visustruct deleted file mode 100644 index 7af9c76e..00000000 --- a/web/lessons/diagramme/pt/l09-java-fehler-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;81;117;101;32;100;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;38;38;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l09-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 8b4ae283..00000000 --- a/web/lessons/diagramme/pt/l09-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;100;105;97;32;61;32;55; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l09-java-laeuft-nicht-start.visustruct deleted file mode 100644 index a374f5c3..00000000 --- a/web/lessons/diagramme/pt/l09-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;100;105;97;32;61;32;55; - - - 100;105;97;32;61;61;32;54;32;124;124;32;68;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-java-schule-loesung.visustruct b/web/lessons/diagramme/pt/l09-java-schule-loesung.visustruct deleted file mode 100644 index 92ecaa0c..00000000 --- a/web/lessons/diagramme/pt/l09-java-schule-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;114;97;32;34;81;117;101;32;104;111;114;97;115;32;115;227;111;32;40;115;243;32;97;32;104;111;114;97;41;63;34; - - - 104;111;114;97;32;62;61;32;56;32;38;38;32;104;111;114;97;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;111;32;108;105;118;114;101;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-note-loesung.visustruct b/web/lessons/diagramme/pt/l09-note-loesung.visustruct deleted file mode 100644 index e0a05f0a..00000000 --- a/web/lessons/diagramme/pt/l09-note-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;81;117;101;32;110;111;116;97;63;34; - - - 110;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;105;110;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;97;32;105;110;118;225;108;105;100;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-rechner-loesung.visustruct b/web/lessons/diagramme/pt/l09-rechner-loesung.visustruct deleted file mode 100644 index 1cfedbf0..00000000 --- a/web/lessons/diagramme/pt/l09-rechner-loesung.visustruct +++ /dev/null @@ -1,47 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;100;111;114;32;34;79;112;101;114;97;100;111;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;48; - - - 111;112;101;114;97;100;111;114; - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 111;117;116;112;117;116;58;32;34;79;112;101;114;97;100;111;114;32;100;101;115;99;111;110;104;101;99;105;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/l09-verschachtelt.visustruct b/web/lessons/diagramme/pt/l09-verschachtelt.visustruct deleted file mode 100644 index ef157db2..00000000 --- a/web/lessons/diagramme/pt/l09-verschachtelt.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;225;103;117;97;44;32;50;32;115;117;109;111;44;32;51;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;34; - - - 101;115;99;111;108;104;97;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;97;32;116;117;97;32;225;103;117;97;46;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;115;117;109;111;46;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;101;32;98;111;116;227;111;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - - - - - diff --git a/web/lessons/diagramme/pt/l1-grundbausteine.visustruct b/web/lessons/diagramme/pt/l1-grundbausteine.visustruct deleted file mode 100644 index 23018719..00000000 --- a/web/lessons/diagramme/pt/l1-grundbausteine.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 80;244;114;32;117;109;97;32;99;104;225;118;101;110;97;32;110;97;32;109;101;115;97; - - - 81;117;101;114;32;97;231;250;99;97;114;63; - - - 74;117;110;116;97;114;32;97;231;250;99;97;114; - - - - - 248; - - - - - 67;104;225;32;97;105;110;100;97;32;100;101;109;97;115;105;97;100;111;32;113;117;101;110;116;101; - - - 69;115;112;101;114;97;114;32;49;32;109;105;110;117;116;111; - - - - - 66;101;98;101;114;32;111;32;99;104;225; - - diff --git a/web/lessons/diagramme/pt/l1-sequenz.visustruct b/web/lessons/diagramme/pt/l1-sequenz.visustruct deleted file mode 100644 index efca5ad9..00000000 --- a/web/lessons/diagramme/pt/l1-sequenz.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 76;101;118;97;110;116;97;114; - - - 76;97;118;97;114;32;111;115;32;100;101;110;116;101;115; - - - 86;101;115;116;105;114; - - - 84;111;109;97;114;32;111;32;112;101;113;117;101;110;111;45;97;108;109;111;231;111; - - diff --git a/web/lessons/diagramme/pt/l1-tee.visustruct b/web/lessons/diagramme/pt/l1-tee.visustruct deleted file mode 100644 index 11dfc44b..00000000 --- a/web/lessons/diagramme/pt/l1-tee.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 69;110;99;104;101;114;32;97;32;99;104;97;108;101;105;114;97;32;99;111;109;32;225;103;117;97; - - - 76;105;103;97;114;32;97;32;99;104;97;108;101;105;114;97; - - - 80;244;114;32;117;109;97;32;115;97;113;117;101;116;97;32;100;101;32;99;104;225;32;110;97;32;99;104;225;118;101;110;97; - - - 68;101;105;116;97;114;32;225;103;117;97;32;113;117;101;110;116;101;32;110;97;32;99;104;225;118;101;110;97; - - - 69;115;112;101;114;97;114;32;51;32;109;105;110;117;116;111;115; - - - 84;105;114;97;114;32;97;32;115;97;113;117;101;116;97;32;100;101;32;99;104;225; - - diff --git a/web/lessons/diagramme/pt/l1-verzweigung.visustruct b/web/lessons/diagramme/pt/l1-verzweigung.visustruct deleted file mode 100644 index eb6d5fcc..00000000 --- a/web/lessons/diagramme/pt/l1-verzweigung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 69;115;116;225;32;97;32;99;104;111;118;101;114;63; - - - 76;101;118;97;114;32;103;117;97;114;100;97;45;99;104;117;118;97; - - - - - 76;101;118;97;114;32;243;99;117;108;111;115;32;100;101;32;115;111;108; - - - - - 83;97;105;114; - - diff --git a/web/lessons/diagramme/pt/l1-wiederholung.visustruct b/web/lessons/diagramme/pt/l1-wiederholung.visustruct deleted file mode 100644 index 0bca7c36..00000000 --- a/web/lessons/diagramme/pt/l1-wiederholung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 80;114;97;116;111;32;110;227;111;32;118;97;122;105;111; - - - 68;97;114;32;117;109;97;32;103;97;114;102;97;100;97; - - - - - 76;101;118;97;110;116;97;114;32;111;32;112;114;97;116;111; - - diff --git a/web/lessons/diagramme/pt/l10-bis-n-loesung.visustruct b/web/lessons/diagramme/pt/l10-bis-n-loesung.visustruct deleted file mode 100644 index 4967e8b1..00000000 --- a/web/lessons/diagramme/pt/l10-bis-n-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;67;111;110;116;97;114;32;97;116;233;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/pt/l10-countdown.visustruct b/web/lessons/diagramme/pt/l10-countdown.visustruct deleted file mode 100644 index 6940e6b8..00000000 --- a/web/lessons/diagramme/pt/l10-countdown.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - diff --git a/web/lessons/diagramme/pt/l10-endlos.visustruct b/web/lessons/diagramme/pt/l10-endlos.visustruct deleted file mode 100644 index aeeb24d8..00000000 --- a/web/lessons/diagramme/pt/l10-endlos.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - diff --git a/web/lessons/diagramme/pt/l10-fakultaet.visustruct b/web/lessons/diagramme/pt/l10-fakultaet.visustruct deleted file mode 100644 index 27730b0c..00000000 --- a/web/lessons/diagramme/pt/l10-fakultaet.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;116;111;114;105;97;108;32;100;101;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;102;97;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;116;111;114;105;97;108;32;61;32;102;97;116;111;114;105;97;108;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;116;111;114;105;97;108; - - diff --git a/web/lessons/diagramme/pt/l10-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l10-java-fehler-loesung.visustruct deleted file mode 100644 index c664b769..00000000 --- a/web/lessons/diagramme/pt/l10-java-fehler-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;111;112;99;97;111;32;34;49;32;109;97;105;115;44;32;50;32;109;101;110;111;115;44;32;51;32;118;101;122;101;115;63;34; - - - 111;112;99;97;111; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;120;105;115;116;101;34; - - - - diff --git a/web/lessons/diagramme/pt/l10-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l10-java-fehler-start.visustruct deleted file mode 100644 index 9c434d97..00000000 --- a/web/lessons/diagramme/pt/l10-java-fehler-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;111;112;99;97;111;32;34;49;32;109;97;105;115;44;32;50;32;109;101;110;111;115;44;32;51;32;118;101;122;101;115;63;34; - - - 111;112;99;97;111; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;120;105;115;116;101;34; - - - - diff --git a/web/lessons/diagramme/pt/l10-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l10-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index ae44f8ff..00000000 --- a/web/lessons/diagramme/pt/l10-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;111;116;97;32;61;32;50; - - - 110;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;99;111;110;116;105;110;117;97;32;97;32;112;114;97;116;105;99;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/l10-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l10-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 70376ff5..00000000 --- a/web/lessons/diagramme/pt/l10-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;111;116;97;32;61;32;50; - - - 78;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;99;111;110;116;105;110;117;97;32;97;32;112;114;97;116;105;99;97;114;34; - - - - diff --git a/web/lessons/diagramme/pt/l10-java-wuerfel-loesung.visustruct b/web/lessons/diagramme/pt/l10-java-wuerfel-loesung.visustruct deleted file mode 100644 index 45271a75..00000000 --- a/web/lessons/diagramme/pt/l10-java-wuerfel-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;110;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111; - - - 111;117;116;112;117;116;58;32;34;117;109;34; - - - - - 111;117;116;112;117;116;58;32;34;100;111;105;115;34; - - - - - 111;117;116;112;117;116;58;32;34;116;114;234;115;34; - - - - - 111;117;116;112;117;116;58;32;34;110;227;111;32;99;111;110;104;101;231;111;34; - - - - diff --git a/web/lessons/diagramme/pt/l10-verdoppeln-loesung.visustruct b/web/lessons/diagramme/pt/l10-verdoppeln-loesung.visustruct deleted file mode 100644 index a8f63a40..00000000 --- a/web/lessons/diagramme/pt/l10-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;112;97;115;115;111;115;32;61;32;48; - - - 110;117;109;101;114;111;32;60;32;49;48;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 112;97;115;115;111;115;32;61;32;112;97;115;115;111;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;112;97;115;115;111;115; - - diff --git a/web/lessons/diagramme/pt/l11-eingabe-pruefen.visustruct b/web/lessons/diagramme/pt/l11-eingabe-pruefen.visustruct deleted file mode 100644 index 776d30c5..00000000 --- a/web/lessons/diagramme/pt/l11-eingabe-pruefen.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;48; - - - 110;117;109;101;114;111;32;60;32;49;32;124;124;32;110;117;109;101;114;111;32;62;32;49;48; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;100;101;32;49;32;97;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;79;98;114;105;103;97;100;111;33;34; - - diff --git a/web/lessons/diagramme/pt/l11-einmal.visustruct b/web/lessons/diagramme/pt/l11-einmal.visustruct deleted file mode 100644 index 6b74149f..00000000 --- a/web/lessons/diagramme/pt/l11-einmal.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/pt/l11-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l11-java-fehler-loesung.visustruct deleted file mode 100644 index 99c1af17..00000000 --- a/web/lessons/diagramme/pt/l11-java-fehler-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;111;98;106;101;116;105;118;111;32;34;79;98;106;101;116;105;118;111;63;34; - - - 105;110;116;32;100;105;110;104;101;105;114;111;32;61;32;48; - - - 105;110;116;32;115;101;109;97;110;97;115;32;61;32;48; - - - 100;105;110;104;101;105;114;111;32;60;32;111;98;106;101;116;105;118;111; - - - 100;105;110;104;101;105;114;111;32;61;32;100;105;110;104;101;105;114;111;32;43;32;53; - - - 115;101;109;97;110;97;115;32;61;32;115;101;109;97;110;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;97;110;97;115;58;32;34;32;43;32;115;101;109;97;110;97;115; - - diff --git a/web/lessons/diagramme/pt/l11-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l11-java-fehler-start.visustruct deleted file mode 100644 index 2470765d..00000000 --- a/web/lessons/diagramme/pt/l11-java-fehler-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;111;98;106;101;116;105;118;111;32;34;79;98;106;101;116;105;118;111;63;34; - - - 105;110;116;32;100;105;110;104;101;105;114;111;32;61;32;48; - - - 105;110;116;32;115;101;109;97;110;97;115;32;61;32;48; - - - 100;105;110;104;101;105;114;111;32;60;61;32;111;98;106;101;116;105;118;111; - - - 100;105;110;104;101;105;114;111;32;61;32;100;105;110;104;101;105;114;111;32;43;32;53; - - - 115;101;109;97;110;97;115;32;61;32;115;101;109;97;110;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;97;110;97;115;58;32;34;32;43;32;115;101;109;97;110;97;115; - - diff --git a/web/lessons/diagramme/pt/l11-java-gerade-loesung.visustruct b/web/lessons/diagramme/pt/l11-java-gerade-loesung.visustruct deleted file mode 100644 index d6d6f9cd..00000000 --- a/web/lessons/diagramme/pt/l11-java-gerade-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;111;110;100;101;63;34; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;50; - - - - diff --git a/web/lessons/diagramme/pt/l11-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l11-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 6940e6b8..00000000 --- a/web/lessons/diagramme/pt/l11-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - diff --git a/web/lessons/diagramme/pt/l11-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l11-java-laeuft-nicht-start.visustruct deleted file mode 100644 index b68d74fe..00000000 --- a/web/lessons/diagramme/pt/l11-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;78;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - diff --git a/web/lessons/diagramme/pt/l11-note-loesung.visustruct b/web/lessons/diagramme/pt/l11-note-loesung.visustruct deleted file mode 100644 index bd138ce6..00000000 --- a/web/lessons/diagramme/pt/l11-note-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;116;32;110;111;116;97;32;61;32;48; - - - 110;111;116;97;32;60;32;49;32;124;124;32;110;111;116;97;32;62;32;53; - - - 105;110;112;117;116;58;32;110;111;116;97;32;34;78;111;116;97;32;100;101;32;49;32;97;32;53;34; - - - - - 111;117;116;112;117;116;58;32;34;65;32;116;117;97;32;110;111;116;97;58;32;34;32;43;32;110;111;116;97; - - diff --git a/web/lessons/diagramme/pt/l11-summe-loesung.visustruct b/web/lessons/diagramme/pt/l11-summe-loesung.visustruct deleted file mode 100644 index 6500954c..00000000 --- a/web/lessons/diagramme/pt/l11-summe-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;48; - - - 110;117;109;101;114;111;32;33;61;32;48; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;40;48;32;116;101;114;109;105;110;97;41;34; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l12-countdown-loesung.visustruct b/web/lessons/diagramme/pt/l12-countdown-loesung.visustruct deleted file mode 100644 index dad90eb6..00000000 --- a/web/lessons/diagramme/pt/l12-countdown-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;74;225;33;34; - - diff --git a/web/lessons/diagramme/pt/l12-einmaleins-loesung.visustruct b/web/lessons/diagramme/pt/l12-einmaleins-loesung.visustruct deleted file mode 100644 index 5e2c1d8d..00000000 --- a/web/lessons/diagramme/pt/l12-einmaleins-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;116;111;32;61;32;110;117;109;101;114;111;32;42;32;105; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;116;111; - - - - diff --git a/web/lessons/diagramme/pt/l12-eins-bis-fuenf.visustruct b/web/lessons/diagramme/pt/l12-eins-bis-fuenf.visustruct deleted file mode 100644 index 42e13a57..00000000 --- a/web/lessons/diagramme/pt/l12-eins-bis-fuenf.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/pt/l12-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l12-java-fehler-loesung.visustruct deleted file mode 100644 index 09c07721..00000000 --- a/web/lessons/diagramme/pt/l12-java-fehler-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - diff --git a/web/lessons/diagramme/pt/l12-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l12-java-fehler-start.visustruct deleted file mode 100644 index ff756bd3..00000000 --- a/web/lessons/diagramme/pt/l12-java-fehler-start.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;49; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - diff --git a/web/lessons/diagramme/pt/l12-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l12-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index cb93db74..00000000 --- a/web/lessons/diagramme/pt/l12-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;101;32;61;32;50;48; - - - 110;117;109;101;114;111;32;60;32;108;105;109;105;116;101; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/pt/l12-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l12-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 999008ed..00000000 --- a/web/lessons/diagramme/pt/l12-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;101;32;61;32;50;48; - - - 110;117;109;101;114;111;32;60;32;76;105;109;105;116;101; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - - diff --git a/web/lessons/diagramme/pt/l12-java-verdoppeln-loesung.visustruct b/web/lessons/diagramme/pt/l12-java-verdoppeln-loesung.visustruct deleted file mode 100644 index daef2150..00000000 --- a/web/lessons/diagramme/pt/l12-java-verdoppeln-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;60;32;49;48;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - diff --git a/web/lessons/diagramme/pt/l12-summe.visustruct b/web/lessons/diagramme/pt/l12-summe.visustruct deleted file mode 100644 index a84881b4..00000000 --- a/web/lessons/diagramme/pt/l12-summe.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l13-dreieck.visustruct b/web/lessons/diagramme/pt/l13-dreieck.visustruct deleted file mode 100644 index 9ecddb9c..00000000 --- a/web/lessons/diagramme/pt/l13-dreieck.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;81;117;97;110;116;97;115;32;108;105;110;104;97;115;63;34; - - - 105;110;116;32;108;105;110;104;97;32;61;32;49; - 108;105;110;104;97;32;60;61;32;110; - 108;105;110;104;97;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;101;115;116;114;101;108;97;32;61;32;49; - 101;115;116;114;101;108;97;32;60;61;32;108;105;110;104;97; - 101;115;116;114;101;108;97;43;43; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - - - diff --git a/web/lessons/diagramme/pt/l13-gerade-eingaben-loesung.visustruct b/web/lessons/diagramme/pt/l13-gerade-eingaben-loesung.visustruct deleted file mode 100644 index 95b8c1f2..00000000 --- a/web/lessons/diagramme/pt/l13-gerade-eingaben-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l13-gerade-zaehlen.visustruct b/web/lessons/diagramme/pt/l13-gerade-zaehlen.visustruct deleted file mode 100644 index 89219f45..00000000 --- a/web/lessons/diagramme/pt/l13-gerade-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l13-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l13-java-fehler-loesung.visustruct deleted file mode 100644 index 0fb53e25..00000000 --- a/web/lessons/diagramme/pt/l13-java-fehler-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;98;117;97;100;97;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;116;97;98;117;97;100;97;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/pt/l13-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l13-java-fehler-start.visustruct deleted file mode 100644 index faecf13b..00000000 --- a/web/lessons/diagramme/pt/l13-java-fehler-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;98;117;97;100;97;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;116;97;98;117;97;100;97;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/pt/l13-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l13-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 4405cc20..00000000 --- a/web/lessons/diagramme/pt/l13-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;97;108;116;117;114;97;32;61;32;48; - - - 105;110;116;32;100;101;103;114;97;117;32;61;32;49; - 100;101;103;114;97;117;32;60;61;32;53; - 100;101;103;114;97;117;43;43; - - - 97;108;116;117;114;97;32;61;32;97;108;116;117;114;97;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/pt/l13-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l13-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 4704c0ef..00000000 --- a/web/lessons/diagramme/pt/l13-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;100;101;103;114;97;117;32;61;32;49; - 100;101;103;114;97;117;32;60;61;32;53; - 100;101;103;114;97;117;43;43; - - - 97;108;116;117;114;97;32;61;32;97;108;116;117;114;97;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;99;109;34; - - diff --git a/web/lessons/diagramme/pt/l13-java-quadrate-loesung.visustruct b/web/lessons/diagramme/pt/l13-java-quadrate-loesung.visustruct deleted file mode 100644 index 15b96e15..00000000 --- a/web/lessons/diagramme/pt/l13-java-quadrate-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - diff --git a/web/lessons/diagramme/pt/l13-rechteck-loesung.visustruct b/web/lessons/diagramme/pt/l13-rechteck-loesung.visustruct deleted file mode 100644 index a07dee28..00000000 --- a/web/lessons/diagramme/pt/l13-rechteck-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;114;103;117;114;97;32;34;76;97;114;103;117;114;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;117;114;97;32;34;65;108;116;117;114;97;34; - - - 105;110;116;32;108;105;110;104;97;32;61;32;49; - 108;105;110;104;97;32;60;61;32;97;108;116;117;114;97; - 108;105;110;104;97;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;99;111;108;117;110;97;32;61;32;49; - 99;111;108;117;110;97;32;60;61;32;108;97;114;103;117;114;97; - 99;111;108;117;110;97;43;43; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - - - diff --git a/web/lessons/diagramme/pt/l14-array-durchlaufen.visustruct b/web/lessons/diagramme/pt/l14-array-durchlaufen.visustruct deleted file mode 100644 index 1d5a3397..00000000 --- a/web/lessons/diagramme/pt/l14-array-durchlaufen.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/l14-erster-letzter-loesung.visustruct b/web/lessons/diagramme/pt/l14-erster-letzter-loesung.visustruct deleted file mode 100644 index eb88c1fe..00000000 --- a/web/lessons/diagramme/pt/l14-erster-letzter-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;116;91;93;32;118;97;108;111;114;101;115;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;91;48;93; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;91;118;97;108;111;114;101;115;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/pt/l14-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l14-java-fehler-loesung.visustruct deleted file mode 100644 index 7d784caf..00000000 --- a/web/lessons/diagramme/pt/l14-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l14-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l14-java-fehler-start.visustruct deleted file mode 100644 index 537cd1a1..00000000 --- a/web/lessons/diagramme/pt/l14-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - - - diff --git a/web/lessons/diagramme/pt/l14-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l14-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index c0e3899f..00000000 --- a/web/lessons/diagramme/pt/l14-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/pt/l14-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l14-java-laeuft-nicht-start.visustruct deleted file mode 100644 index c3a6d24f..00000000 --- a/web/lessons/diagramme/pt/l14-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/pt/l14-java-treppe-loesung.visustruct b/web/lessons/diagramme/pt/l14-java-treppe-loesung.visustruct deleted file mode 100644 index a6b51157..00000000 --- a/web/lessons/diagramme/pt/l14-java-treppe-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;81;117;97;110;116;111;115;32;100;101;103;114;97;117;115;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - diff --git a/web/lessons/diagramme/pt/l14-namen-eingeben.visustruct b/web/lessons/diagramme/pt/l14-namen-eingeben.visustruct deleted file mode 100644 index 989d5d79..00000000 --- a/web/lessons/diagramme/pt/l14-namen-eingeben.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;111;109;101;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;111;109;101;34; - - - - - 111;117;116;112;117;116;58;32;34;80;114;105;109;101;105;114;111;32;110;111;109;101;58;32;34;32;43;32;110;111;109;101;115;91;48;93; - - diff --git a/web/lessons/diagramme/pt/l14-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/pt/l14-rueckwaerts-loesung.visustruct deleted file mode 100644 index e825ebe1..00000000 --- a/web/lessons/diagramme/pt/l14-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - - - 105;110;116;32;105;32;61;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/l14-summe-max-loesung.visustruct b/web/lessons/diagramme/pt/l14-summe-max-loesung.visustruct deleted file mode 100644 index 6def81d3..00000000 --- a/web/lessons/diagramme/pt/l14-summe-max-loesung.visustruct +++ /dev/null @@ -1,51 +0,0 @@ - - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;110;117;109;101;114;111;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111;115;91;105;93; - - - 110;117;109;101;114;111;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;110;117;109;101;114;111;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;77;225;120;105;109;111;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/pt/l14-summe-maximum.visustruct b/web/lessons/diagramme/pt/l14-summe-maximum.visustruct deleted file mode 100644 index a5d0df14..00000000 --- a/web/lessons/diagramme/pt/l14-summe-maximum.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;111;110;116;111;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - 112;111;110;116;111;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;111;110;116;111;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;77;225;120;105;109;111;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/pt/l15-bis-null-loesung.visustruct b/web/lessons/diagramme/pt/l15-bis-null-loesung.visustruct deleted file mode 100644 index 9c9551e2..00000000 --- a/web/lessons/diagramme/pt/l15-bis-null-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - 110;117;109;101;114;111;32;33;61;32;48; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l15-endlos-aussprung.visustruct b/web/lessons/diagramme/pt/l15-endlos-aussprung.visustruct deleted file mode 100644 index 042883f8..00000000 --- a/web/lessons/diagramme/pt/l15-endlos-aussprung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - 110;117;109;101;114;111;32;61;61;32;48; - - - 83;97;105;114;32;100;111;32;99;105;99;108;111; - - - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l15-flaeche-loesung.visustruct b/web/lessons/diagramme/pt/l15-flaeche-loesung.visustruct deleted file mode 100644 index 08e4e5a9..00000000 --- a/web/lessons/diagramme/pt/l15-flaeche-loesung.visustruct +++ /dev/null @@ -1,15 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;34;67;111;109;112;114;105;109;101;110;116;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;114;103;117;114;97;32;34;76;97;114;103;117;114;97;63;34; - - - 105;110;116;32;102;32;61;32;97;114;101;97;40;99;111;109;112;114;105;109;101;110;116;111;44;32;108;97;114;103;117;114;97;41; - - - 111;117;116;112;117;116;58;32;34;193;114;101;97;58;32;34;32;43;32;102; - - diff --git a/web/lessons/diagramme/pt/l15-hauptprogramm.visustruct b/web/lessons/diagramme/pt/l15-hauptprogramm.visustruct deleted file mode 100644 index 97eb67fd..00000000 --- a/web/lessons/diagramme/pt/l15-hauptprogramm.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;111;115;116;114;97;114;66;111;97;115;86;105;110;100;97;115;40;41; - - - 102;97;122;101;114;80;101;114;103;117;110;116;97;115;40;41; - - - 109;111;115;116;114;97;114;82;101;115;117;108;116;97;100;111;40;41; - - diff --git a/web/lessons/diagramme/pt/l15-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l15-java-fehler-loesung.visustruct deleted file mode 100644 index cb14d2d2..00000000 --- a/web/lessons/diagramme/pt/l15-java-fehler-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;97;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;97;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;101;114;97;116;117;114;97;32;109;97;105;115;32;97;108;116;97;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/pt/l15-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l15-java-fehler-start.visustruct deleted file mode 100644 index 46805c96..00000000 --- a/web/lessons/diagramme/pt/l15-java-fehler-start.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;97;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;97;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;101;114;97;116;117;114;97;32;109;97;105;115;32;97;108;116;97;58;32;34;32;43;32;109;97;120; - - diff --git a/web/lessons/diagramme/pt/l15-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l15-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 6509da2a..00000000 --- a/web/lessons/diagramme/pt/l15-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l15-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l15-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 8d70b22e..00000000 --- a/web/lessons/diagramme/pt/l15-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;76;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l15-java-noten-loesung.visustruct b/web/lessons/diagramme/pt/l15-java-noten-loesung.visustruct deleted file mode 100644 index c0d6db16..00000000 --- a/web/lessons/diagramme/pt/l15-java-noten-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;32;110;111;116;97;115;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;111;116;97;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l15-quadrat-haupt.visustruct b/web/lessons/diagramme/pt/l15-quadrat-haupt.visustruct deleted file mode 100644 index 10561fa4..00000000 --- a/web/lessons/diagramme/pt/l15-quadrat-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;32;61;32;113;117;97;100;114;97;100;111;40;110;117;109;101;114;111;41; - - - 111;117;116;112;117;116;58;32;34;79;32;113;117;97;100;114;97;100;111;32;233;32;34;32;43;32;113; - - diff --git a/web/lessons/diagramme/pt/l15-quadrat.visustruct b/web/lessons/diagramme/pt/l15-quadrat.visustruct deleted file mode 100644 index 66780122..00000000 --- a/web/lessons/diagramme/pt/l15-quadrat.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;114;101;115;117;108;116;97;100;111; - - diff --git a/web/lessons/diagramme/pt/l15-titel-loesung.visustruct b/web/lessons/diagramme/pt/l15-titel-loesung.visustruct deleted file mode 100644 index 86d1d7bd..00000000 --- a/web/lessons/diagramme/pt/l15-titel-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;111;115;116;114;97;114;84;105;116;117;108;111;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;67;111;109;111;32;116;101;32;99;104;97;109;97;115;63;34; - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;34;32;43;32;110;111;109;101; - - diff --git a/web/lessons/diagramme/pt/l15-unterprogramm.visustruct b/web/lessons/diagramme/pt/l15-unterprogramm.visustruct deleted file mode 100644 index 33e486a2..00000000 --- a/web/lessons/diagramme/pt/l15-unterprogramm.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;32;97;111;32;113;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;86;97;105;115;32;116;101;114;32;116;114;234;115;32;112;101;114;103;117;110;116;97;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l15-zeige-summe-haupt.visustruct b/web/lessons/diagramme/pt/l15-zeige-summe-haupt.visustruct deleted file mode 100644 index c0e2892f..00000000 --- a/web/lessons/diagramme/pt/l15-zeige-summe-haupt.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;111;115;116;114;97;114;83;111;109;97;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 109;111;115;116;114;97;114;83;111;109;97;40;120;44;32;120;32;43;32;49;41; - - diff --git a/web/lessons/diagramme/pt/l15-zeige-summe.visustruct b/web/lessons/diagramme/pt/l15-zeige-summe.visustruct deleted file mode 100644 index daafa9a8..00000000 --- a/web/lessons/diagramme/pt/l15-zeige-summe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-countdown-loesung.visustruct b/web/lessons/diagramme/pt/l16-countdown-loesung.visustruct deleted file mode 100644 index 5fdb9321..00000000 --- a/web/lessons/diagramme/pt/l16-countdown-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;110;105;99;105;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;105;110;105;99;105;111; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;74;225;33;34; - - diff --git a/web/lessons/diagramme/pt/l16-countdown-start.visustruct b/web/lessons/diagramme/pt/l16-countdown-start.visustruct deleted file mode 100644 index a63d2edd..00000000 --- a/web/lessons/diagramme/pt/l16-countdown-start.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;110;105;99;105;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;105;110;105;99;105;111; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;74;225;34; - - diff --git a/web/lessons/diagramme/pt/l16-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l16-java-fehler-loesung.visustruct deleted file mode 100644 index 1b12d5da..00000000 --- a/web/lessons/diagramme/pt/l16-java-fehler-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l16-java-fehler-start.visustruct deleted file mode 100644 index 2144f0e0..00000000 --- a/web/lessons/diagramme/pt/l16-java-fehler-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l16-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 6482c7ed..00000000 --- a/web/lessons/diagramme/pt/l16-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;44;32;114;111;110;100;97;32;49;58;32;34;32;43;32;112;111;110;116;111;115;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/pt/l16-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l16-java-laeuft-nicht-start.visustruct deleted file mode 100644 index c871b3e2..00000000 --- a/web/lessons/diagramme/pt/l16-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;44;32;114;111;110;100;97;32;49;58;32;34;32;43;32;80;111;110;116;111;115;91;49;93;91;48;93; - - diff --git a/web/lessons/diagramme/pt/l16-java-punkte-loesung.visustruct b/web/lessons/diagramme/pt/l16-java-punkte-loesung.visustruct deleted file mode 100644 index a6c2cbd6..00000000 --- a/web/lessons/diagramme/pt/l16-java-punkte-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-schreibtisch.visustruct b/web/lessons/diagramme/pt/l16-schreibtisch.visustruct deleted file mode 100644 index 5c73af64..00000000 --- a/web/lessons/diagramme/pt/l16-schreibtisch.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-summe-loesung.visustruct b/web/lessons/diagramme/pt/l16-summe-loesung.visustruct deleted file mode 100644 index 1a76018e..00000000 --- a/web/lessons/diagramme/pt/l16-summe-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l16-summe-start.visustruct b/web/lessons/diagramme/pt/l16-summe-start.visustruct deleted file mode 100644 index a62097a0..00000000 --- a/web/lessons/diagramme/pt/l16-summe-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l17-beispiel.visustruct b/web/lessons/diagramme/pt/l17-beispiel.visustruct deleted file mode 100644 index b97bc3ed..00000000 --- a/web/lessons/diagramme/pt/l17-beispiel.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;118;101;122;101;115;32;34;32;43;32;110;117;109;101;114;111;32;43;32;34;32;61;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - - - - 110;117;109;101;114;111;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;102;111;105;32;100;105;102;237;99;105;108;33;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;102;111;105;32;102;225;99;105;108;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l17-fakultaet-loesung.visustruct b/web/lessons/diagramme/pt/l17-fakultaet-loesung.visustruct deleted file mode 100644 index b5bf4434..00000000 --- a/web/lessons/diagramme/pt/l17-fakultaet-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 105;110;116;32;102;97;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;116;111;114;105;97;108;32;61;32;102;97;116;111;114;105;97;108;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;116;111;114;105;97;108; - - diff --git a/web/lessons/diagramme/pt/l17-java-brief-loesung.visustruct b/web/lessons/diagramme/pt/l17-java-brief-loesung.visustruct deleted file mode 100644 index 917ec89e..00000000 --- a/web/lessons/diagramme/pt/l17-java-brief-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 83;116;114;105;110;103;32;99;97;114;116;97;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;99;97;114;116;97;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;99;97;114;116;97;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;76;105;110;104;97;115;58;32;34;32;43;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;104;97;115;91;105;93;46;99;111;110;116;97;105;110;115;40;34;114;97;112;111;115;97;34;41; - - - 111;117;116;112;117;116;58;32;34;82;97;112;111;115;97;32;110;97;32;108;105;110;104;97;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/pt/l17-java-brief-start.visustruct b/web/lessons/diagramme/pt/l17-java-brief-start.visustruct deleted file mode 100644 index 659154a6..00000000 --- a/web/lessons/diagramme/pt/l17-java-brief-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;116;114;105;110;103;32;99;97;114;116;97;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;99;97;114;116;97;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/pt/l17-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l17-java-fehler-loesung.visustruct deleted file mode 100644 index e1efa3af..00000000 --- a/web/lessons/diagramme/pt/l17-java-fehler-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;63;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/pt/l17-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l17-java-fehler-start.visustruct deleted file mode 100644 index 2238e0f6..00000000 --- a/web/lessons/diagramme/pt/l17-java-fehler-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;63;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/pt/l17-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l17-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 606502e3..00000000 --- a/web/lessons/diagramme/pt/l17-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;116;101;109;32;34;32;43;32;110;111;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;114;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l17-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l17-java-laeuft-nicht-start.visustruct deleted file mode 100644 index 70fdf064..00000000 --- a/web/lessons/diagramme/pt/l17-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;116;101;109;32;34;32;43;32;110;111;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;114;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l17-java-passwort-loesung.visustruct b/web/lessons/diagramme/pt/l17-java-passwort-loesung.visustruct deleted file mode 100644 index 70dbb1c2..00000000 --- a/web/lessons/diagramme/pt/l17-java-passwort-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;34;80;97;108;97;118;114;97;45;112;97;115;115;101;63;34; - - - 101;110;116;114;97;100;97;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;33;34; - - - - - 111;117;116;112;117;116;58;32;34;80;97;108;97;118;114;97;45;112;97;115;115;101;32;101;114;114;97;100;97;34; - - - - diff --git a/web/lessons/diagramme/pt/l17-ticket-loesung.visustruct b/web/lessons/diagramme/pt/l17-ticket-loesung.visustruct deleted file mode 100644 index 1dcf2f37..00000000 --- a/web/lessons/diagramme/pt/l17-ticket-loesung.visustruct +++ /dev/null @@ -1,29 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 105;100;97;100;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;100;101;32;99;114;105;97;110;231;97;58;32;52;32;101;117;114;111;115;34; - - - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;100;101;32;97;100;117;108;116;111;58;32;56;32;101;117;114;111;115;34; - - - - - - diff --git a/web/lessons/diagramme/pt/l18-beispiel.visustruct b/web/lessons/diagramme/pt/l18-beispiel.visustruct deleted file mode 100644 index dc416e8c..00000000 --- a/web/lessons/diagramme/pt/l18-beispiel.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;70;105;109;34; - - diff --git a/web/lessons/diagramme/pt/l18-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l18-java-fehler-loesung.visustruct deleted file mode 100644 index 0f0179c2..00000000 --- a/web/lessons/diagramme/pt/l18-java-fehler-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;97;108;112;105;116;101;32;34;79;32;116;101;117;32;112;97;108;112;105;116;101;63;34; - - - 116;101;110;116;97;116;105;118;97;115;32;61;32;116;101;110;116;97;116;105;118;97;115;32;43;32;49; - - - 112;97;108;112;105;116;101;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;110;116;97;116;105;118;97;115;58;32;34;32;43;32;116;101;110;116;97;116;105;118;97;115; - - diff --git a/web/lessons/diagramme/pt/l18-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l18-java-fehler-start.visustruct deleted file mode 100644 index 79911b46..00000000 --- a/web/lessons/diagramme/pt/l18-java-fehler-start.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;97;108;112;105;116;101;32;34;79;32;116;101;117;32;112;97;108;112;105;116;101;63;34; - - - 112;97;108;112;105;116;101;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 116;101;110;116;97;116;105;118;97;115;32;61;32;116;101;110;116;97;116;105;118;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;84;101;110;116;97;116;105;118;97;115;58;32;34;32;43;32;116;101;110;116;97;116;105;118;97;115; - - diff --git a/web/lessons/diagramme/pt/l18-java-kasse-loesung.visustruct b/web/lessons/diagramme/pt/l18-java-kasse-loesung.visustruct deleted file mode 100644 index a2f4509b..00000000 --- a/web/lessons/diagramme/pt/l18-java-kasse-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;97;114;116;105;103;111;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;101;99;111;32;34;80;114;101;231;111;63;32;40;48;32;61;32;102;105;109;41;34; - - - 112;114;101;99;111;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;114;101;99;111; - - - 97;114;116;105;103;111;115;32;61;32;97;114;116;105;103;111;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;65;114;116;105;103;111;115;58;32;34;32;43;32;97;114;116;105;103;111;115; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;115;111;109;97; - - diff --git a/web/lessons/diagramme/pt/l18-js-loesung.visustruct b/web/lessons/diagramme/pt/l18-js-loesung.visustruct deleted file mode 100644 index 4217b90b..00000000 --- a/web/lessons/diagramme/pt/l18-js-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;115;32;118;111;116;97;114;46;34; - - - - - 105;110;116;32;97;110;111;115;32;61;32;49;56;32;45;32;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;69;115;112;101;114;97;32;109;97;105;115;32;34;32;43;32;97;110;111;115;32;43;32;34;32;97;110;111;115;46;34; - - - - diff --git a/web/lessons/diagramme/pt/l18-python-loesung.visustruct b/web/lessons/diagramme/pt/l18-python-loesung.visustruct deleted file mode 100644 index 9d8b4c5e..00000000 --- a/web/lessons/diagramme/pt/l18-python-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;108;105;109;105;116;101;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;108;105;109;105;116;101; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - diff --git a/web/lessons/diagramme/pt/l19-java-fehler-loesung.visustruct b/web/lessons/diagramme/pt/l19-java-fehler-loesung.visustruct deleted file mode 100644 index c05bcaaa..00000000 --- a/web/lessons/diagramme/pt/l19-java-fehler-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - diff --git a/web/lessons/diagramme/pt/l19-java-fehler-start.visustruct b/web/lessons/diagramme/pt/l19-java-fehler-start.visustruct deleted file mode 100644 index 444f29de..00000000 --- a/web/lessons/diagramme/pt/l19-java-fehler-start.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - diff --git a/web/lessons/diagramme/pt/l19-java-laeuft-nicht-loesung.visustruct b/web/lessons/diagramme/pt/l19-java-laeuft-nicht-loesung.visustruct deleted file mode 100644 index 9203c8e1..00000000 --- a/web/lessons/diagramme/pt/l19-java-laeuft-nicht-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;112;114;111;99;117;114;97;100;97;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - diff --git a/web/lessons/diagramme/pt/l19-java-laeuft-nicht-start.visustruct b/web/lessons/diagramme/pt/l19-java-laeuft-nicht-start.visustruct deleted file mode 100644 index bd4d1442..00000000 --- a/web/lessons/diagramme/pt/l19-java-laeuft-nicht-start.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;112;114;111;99;117;114;97;100;97;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;80;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - diff --git a/web/lessons/diagramme/pt/l19-java-zaehlen-loesung.visustruct b/web/lessons/diagramme/pt/l19-java-zaehlen-loesung.visustruct deleted file mode 100644 index 4ca71157..00000000 --- a/web/lessons/diagramme/pt/l19-java-zaehlen-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l2-ausgabe.visustruct b/web/lessons/diagramme/pt/l2-ausgabe.visustruct deleted file mode 100644 index 047d0205..00000000 --- a/web/lessons/diagramme/pt/l2-ausgabe.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;66;111;109;32;100;105;97;34; - - - 111;117;116;112;117;116;58;32;34;72;111;106;101;32;97;112;114;101;110;100;101;109;111;115;32;101;115;116;114;117;116;111;103;114;97;109;97;115;46;34; - - diff --git a/web/lessons/diagramme/pt/l2-hallo-loesung.visustruct b/web/lessons/diagramme/pt/l2-hallo-loesung.visustruct deleted file mode 100644 index c6a771f6..00000000 --- a/web/lessons/diagramme/pt/l2-hallo-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;77;117;110;100;111;34; - - diff --git a/web/lessons/diagramme/pt/l2-zwei-zeilen-loesung.visustruct b/web/lessons/diagramme/pt/l2-zwei-zeilen-loesung.visustruct deleted file mode 100644 index f9cc8582..00000000 --- a/web/lessons/diagramme/pt/l2-zwei-zeilen-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 111;117;116;112;117;116;58;32;34;79;108;225;34; - - - 111;117;116;112;117;116;58;32;34;69;115;116;111;117;32;97;32;97;112;114;101;110;100;101;114;32;101;115;116;114;117;116;111;103;114;97;109;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l23-java-ergaenzen-loesung.visustruct b/web/lessons/diagramme/pt/l23-java-ergaenzen-loesung.visustruct deleted file mode 100644 index 91c05d85..00000000 --- a/web/lessons/diagramme/pt/l23-java-ergaenzen-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;99;111;109;112;114;97;115;45;110;111;118;97;46;116;120;116;34;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;108;105;110;104;97;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;66;111;108;111;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101;32;43;32;34;32;108;105;110;104;97;115;32;103;117;97;114;100;97;100;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l23-java-ergaenzen-start.visustruct b/web/lessons/diagramme/pt/l23-java-ergaenzen-start.visustruct deleted file mode 100644 index d28b8702..00000000 --- a/web/lessons/diagramme/pt/l23-java-ergaenzen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;99;111;109;112;114;97;115;45;110;111;118;97;46;116;120;116;34;41; - - diff --git a/web/lessons/diagramme/pt/l23-java-nummern-loesung.visustruct b/web/lessons/diagramme/pt/l23-java-nummern-loesung.visustruct deleted file mode 100644 index 830311d6..00000000 --- a/web/lessons/diagramme/pt/l23-java-nummern-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;46;32;34;32;43;32;108;105;110;104;97; - - - 110;117;109;101;114;111;43;43; - - - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l23-java-nummern-start.visustruct b/web/lessons/diagramme/pt/l23-java-nummern-start.visustruct deleted file mode 100644 index e707e408..00000000 --- a/web/lessons/diagramme/pt/l23-java-nummern-start.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - diff --git a/web/lessons/diagramme/pt/l24-java-fuchs-loesung.visustruct b/web/lessons/diagramme/pt/l24-java-fuchs-loesung.visustruct deleted file mode 100644 index e7f198e1..00000000 --- a/web/lessons/diagramme/pt/l24-java-fuchs-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 108;105;110;104;97;46;99;111;110;116;97;105;110;115;40;34;82;97;112;111;115;97;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;82;97;112;111;115;97;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l24-java-fuchs-start.visustruct b/web/lessons/diagramme/pt/l24-java-fuchs-start.visustruct deleted file mode 100644 index db31f1f2..00000000 --- a/web/lessons/diagramme/pt/l24-java-fuchs-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l24-java-laengster-loesung.visustruct b/web/lessons/diagramme/pt/l24-java-laengster-loesung.visustruct deleted file mode 100644 index e710d151..00000000 --- a/web/lessons/diagramme/pt/l24-java-laengster-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;109;97;105;115;76;111;110;103;97;32;61;32;108;105;110;104;97;115;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;104;97;115;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;109;97;105;115;76;111;110;103;97;46;108;101;110;103;116;104;40;41; - - - 109;97;105;115;76;111;110;103;97;32;61;32;108;105;110;104;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;109;97;105;115;32;108;111;110;103;97;58;32;34;32;43;32;109;97;105;115;76;111;110;103;97; - - diff --git a/web/lessons/diagramme/pt/l24-java-laengster-start.visustruct b/web/lessons/diagramme/pt/l24-java-laengster-start.visustruct deleted file mode 100644 index 09ce6c3c..00000000 --- a/web/lessons/diagramme/pt/l24-java-laengster-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - diff --git a/web/lessons/diagramme/pt/l25-java-beste-loesung.visustruct b/web/lessons/diagramme/pt/l25-java-beste-loesung.visustruct deleted file mode 100644 index 63be9499..00000000 --- a/web/lessons/diagramme/pt/l25-java-beste-loesung.visustruct +++ /dev/null @@ -1,54 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;109;101;108;104;111;114;78;111;109;101;32;61;32;34;34; - - - 105;110;116;32;109;101;108;104;111;114;78;111;116;97;32;61;32;55; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;110;111;116;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;51;93;41; - - - 110;111;116;97;32;60;32;109;101;108;104;111;114;78;111;116;97; - - - 109;101;108;104;111;114;78;111;116;97;32;61;32;110;111;116;97; - - - 109;101;108;104;111;114;78;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;101;108;104;111;114;32;110;111;116;97;32;97;32;73;110;103;108;234;115;58;32;34;32;43;32;109;101;108;104;111;114;78;111;109;101;32;43;32;34;32;40;34;32;43;32;109;101;108;104;111;114;78;111;116;97;32;43;32;34;41;34; - - diff --git a/web/lessons/diagramme/pt/l25-java-beste-start.visustruct b/web/lessons/diagramme/pt/l25-java-beste-start.visustruct deleted file mode 100644 index a7648532..00000000 --- a/web/lessons/diagramme/pt/l25-java-beste-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l25-java-schnitt-loesung.visustruct b/web/lessons/diagramme/pt/l25-java-schnitt-loesung.visustruct deleted file mode 100644 index ad056f90..00000000 --- a/web/lessons/diagramme/pt/l25-java-schnitt-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;32;100;101;32;77;97;116;101;109;225;116;105;99;97;58;32;34;32;43;32;115;111;109;97;32;47;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/l25-java-schnitt-start.visustruct b/web/lessons/diagramme/pt/l25-java-schnitt-start.visustruct deleted file mode 100644 index 05d2cf0e..00000000 --- a/web/lessons/diagramme/pt/l25-java-schnitt-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l26-java-namen-loesung.visustruct b/web/lessons/diagramme/pt/l26-java-namen-loesung.visustruct deleted file mode 100644 index be24ee8d..00000000 --- a/web/lessons/diagramme/pt/l26-java-namen-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 108;105;110;104;97;46;99;111;110;116;97;105;110;115;40;34;92;34;110;111;109;101;92;34;34;41; - - - 105;110;116;32;105;110;105;99;105;111;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;110;111;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;102;105;109;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;105;110;105;99;105;111;41; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97;46;115;117;98;115;116;114;105;110;103;40;105;110;105;99;105;111;44;32;102;105;109;41; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l26-java-namen-start.visustruct b/web/lessons/diagramme/pt/l26-java-namen-start.visustruct deleted file mode 100644 index 0d79a5d2..00000000 --- a/web/lessons/diagramme/pt/l26-java-namen-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l26-java-schreiben-loesung.visustruct b/web/lessons/diagramme/pt/l26-java-schreiben-loesung.visustruct deleted file mode 100644 index fe0810ae..00000000 --- a/web/lessons/diagramme/pt/l26-java-schreiben-loesung.visustruct +++ /dev/null @@ -1,50 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;82;97;112;111;115;97;34;125; - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;110;116;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;61;32;34;32;32;123;92;34;110;111;109;101;92;34;58;32;92;34;34;32;43;32;110;111;109;101;115;91;105;93;32;43;32;34;92;34;44;32;92;34;112;111;110;116;111;115;92;34;58;32;34;32;43;32;112;111;110;116;111;115;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;97;100;97;32;61;32;101;110;116;114;97;100;97;32;43;32;34;44;34; - - - - - 248; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;101;110;116;114;97;100;97;41; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101;115;46;108;101;110;103;116;104;32;43;32;34;32;101;110;116;114;97;100;97;115;32;103;117;97;114;100;97;100;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l26-java-schreiben-start.visustruct b/web/lessons/diagramme/pt/l26-java-schreiben-start.visustruct deleted file mode 100644 index 3d85129b..00000000 --- a/web/lessons/diagramme/pt/l26-java-schreiben-start.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;82;97;112;111;115;97;34;125; - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;110;116;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l27-java-json-loesung.visustruct b/web/lessons/diagramme/pt/l27-java-json-loesung.visustruct deleted file mode 100644 index 2ae1ee3d..00000000 --- a/web/lessons/diagramme/pt/l27-java-json-loesung.visustruct +++ /dev/null @@ -1,53 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;105;115;45;110;111;118;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;115;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;61;32;34;32;32;123;92;34;110;111;109;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;101;115;91;48;93;32;43;32;34;92;34;44;32;92;34;101;115;112;101;99;105;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;92;34;44;32;92;34;105;100;97;100;101;92;34;58;32;34;32;43;32;112;97;114;116;101;115;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;97;100;97;32;61;32;101;110;116;114;97;100;97;32;43;32;34;44;34; - - - - - 248; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;101;110;116;114;97;100;97;41; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;108;105;110;104;97;115;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;97;110;105;109;97;105;115;32;103;117;97;114;100;97;100;111;115;34; - - diff --git a/web/lessons/diagramme/pt/l27-java-json-start.visustruct b/web/lessons/diagramme/pt/l27-java-json-start.visustruct deleted file mode 100644 index f071efef..00000000 --- a/web/lessons/diagramme/pt/l27-java-json-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;105;115;45;110;111;118;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/l27-java-saetze-loesung.visustruct b/web/lessons/diagramme/pt/l27-java-saetze-loesung.visustruct deleted file mode 100644 index d6736a5a..00000000 --- a/web/lessons/diagramme/pt/l27-java-saetze-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;102;114;97;115;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;112;97;114;116;101;115;91;48;93;32;43;32;34;32;40;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;41;32;116;101;109;32;34;32;43;32;112;97;114;116;101;115;91;50;93;32;43;32;34;32;97;110;111;115;46;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101;32;43;32;34;32;102;114;97;115;101;115;32;103;117;97;114;100;97;100;97;115;34; - - diff --git a/web/lessons/diagramme/pt/l27-java-saetze-start.visustruct b/web/lessons/diagramme/pt/l27-java-saetze-start.visustruct deleted file mode 100644 index 8f71f062..00000000 --- a/web/lessons/diagramme/pt/l27-java-saetze-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;102;114;97;115;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - diff --git a/web/lessons/diagramme/pt/sim-kino-loesung.visustruct b/web/lessons/diagramme/pt/sim-kino-loesung.visustruct deleted file mode 100644 index c271c3d1..00000000 --- a/web/lessons/diagramme/pt/sim-kino-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;98;105;108;104;101;116;101;115;63;34; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - diff --git a/web/lessons/diagramme/pt/sim-kino-start.visustruct b/web/lessons/diagramme/pt/sim-kino-start.visustruct deleted file mode 100644 index afd9c58f..00000000 --- a/web/lessons/diagramme/pt/sim-kino-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;98;105;108;104;101;116;101;115;63;34; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;56;46;53; - - diff --git a/web/lessons/diagramme/pt/sim-tausch-loesung.visustruct b/web/lessons/diagramme/pt/sim-tausch-loesung.visustruct deleted file mode 100644 index f51751e9..00000000 --- a/web/lessons/diagramme/pt/sim-tausch-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/pt/sim-tausch-start.visustruct b/web/lessons/diagramme/pt/sim-tausch-start.visustruct deleted file mode 100644 index df460a35..00000000 --- a/web/lessons/diagramme/pt/sim-tausch-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/pt/sim-tausch.visustruct b/web/lessons/diagramme/pt/sim-tausch.visustruct deleted file mode 100644 index 2fefd778..00000000 --- a/web/lessons/diagramme/pt/sim-tausch.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/pt/ss-absteigend-loesung.visustruct b/web/lessons/diagramme/pt/ss-absteigend-loesung.visustruct deleted file mode 100644 index 47cc9481..00000000 --- a/web/lessons/diagramme/pt/ss-absteigend-loesung.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;116;97;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;99;97;114;116;97;34; - - - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;60;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/ss-absteigend-start.visustruct b/web/lessons/diagramme/pt/ss-absteigend-start.visustruct deleted file mode 100644 index 2356a9f7..00000000 --- a/web/lessons/diagramme/pt/ss-absteigend-start.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;116;97;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;99;97;114;116;97;34; - - - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;62;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/ss-binaere-suche.visustruct b/web/lessons/diagramme/pt/ss-binaere-suche.visustruct deleted file mode 100644 index 33b16cb5..00000000 --- a/web/lessons/diagramme/pt/ss-binaere-suche.visustruct +++ /dev/null @@ -1,74 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;32;112;114;111;99;117;114;97;115;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;97;99;111;101;115;32;61;32;48; - - - 105;110;116;32;101;115;113;117;101;114;100;97;32;61;32;48; - - - 105;110;116;32;100;105;114;101;105;116;97;32;61;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;101;105;111;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;101;115;113;117;101;114;100;97;32;60;61;32;100;105;114;101;105;116;97; - - - 109;101;105;111;32;61;32;40;101;115;113;117;101;114;100;97;32;43;32;100;105;114;101;105;116;97;41;32;47;32;50; - - - 99;111;109;112;97;114;97;99;111;101;115;32;61;32;99;111;109;112;97;114;97;99;111;101;115;32;43;32;49; - - - 99;97;114;116;97;115;91;109;101;105;111;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;109;101;105;111; - - - - - 99;97;114;116;97;115;91;109;101;105;111;93;32;60;32;112;114;111;99;117;114;97;100;97; - - - 101;115;113;117;101;114;100;97;32;61;32;109;101;105;111;32;43;32;49; - - - - - 100;105;114;101;105;116;97;32;61;32;109;101;105;111;32;45;32;49; - - - - - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;69;110;99;111;110;116;114;97;100;97;32;110;97;32;112;111;115;105;231;227;111;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;97;231;245;101;115;58;32;34;32;43;32;99;111;109;112;97;114;97;99;111;101;115; - - diff --git a/web/lessons/diagramme/pt/ss-bubblesort.visustruct b/web/lessons/diagramme/pt/ss-bubblesort.visustruct deleted file mode 100644 index 1180b076..00000000 --- a/web/lessons/diagramme/pt/ss-bubblesort.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;62;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/ss-karte-suchen-loesung.visustruct b/web/lessons/diagramme/pt/ss-karte-suchen-loesung.visustruct deleted file mode 100644 index 729671f4..00000000 --- a/web/lessons/diagramme/pt/ss-karte-suchen-loesung.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - diff --git a/web/lessons/diagramme/pt/ss-lineare-suche.visustruct b/web/lessons/diagramme/pt/ss-lineare-suche.visustruct deleted file mode 100644 index 1c25999b..00000000 --- a/web/lessons/diagramme/pt/ss-lineare-suche.visustruct +++ /dev/null @@ -1,58 +0,0 @@ - - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;32;112;114;111;99;117;114;97;115;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;97;99;111;101;115;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;111;109;112;97;114;97;99;111;101;115;32;61;32;99;111;109;112;97;114;97;99;111;101;115;32;43;32;49; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;69;110;99;111;110;116;114;97;100;97;32;110;97;32;112;111;115;105;231;227;111;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;97;231;245;101;115;58;32;34;32;43;32;99;111;109;112;97;114;97;99;111;101;115; - - diff --git a/web/lessons/diagramme/pt/uebergabe-bonus-loesung.visustruct b/web/lessons/diagramme/pt/uebergabe-bonus-loesung.visustruct deleted file mode 100644 index 6b82b34a..00000000 --- a/web/lessons/diagramme/pt/uebergabe-bonus-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;110;116;111;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;110;116;111;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/uebergabe-bonus-start.visustruct b/web/lessons/diagramme/pt/uebergabe-bonus-start.visustruct deleted file mode 100644 index 6b82b34a..00000000 --- a/web/lessons/diagramme/pt/uebergabe-bonus-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;110;116;111;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;110;116;111;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/uebergabe-bonus-unterprogramm-loesung.visustruct b/web/lessons/diagramme/pt/uebergabe-bonus-unterprogramm-loesung.visustruct deleted file mode 100644 index eb9ddb8b..00000000 --- a/web/lessons/diagramme/pt/uebergabe-bonus-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 112;111;110;116;111;115;91;105;93;32;61;32;112;111;110;116;111;115;91;105;93;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/pt/uebergabe-haupt.visustruct b/web/lessons/diagramme/pt/uebergabe-haupt.visustruct deleted file mode 100644 index ead09f46..00000000 --- a/web/lessons/diagramme/pt/uebergabe-haupt.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;53; - - - 100;117;112;108;105;99;97;114;40;110;117;109;101;114;111;41; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;111;103;114;97;109;97;32;112;114;105;110;99;105;112;97;108;58;32;110;117;109;101;114;111;32;61;32;34;32;43;32;110;117;109;101;114;111; - - - 98;111;111;108;101;97;110;91;93;32;102;105;108;97;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;65;110;116;101;115;58;32;34;32;43;32;102;105;108;97;91;48;93;32;43;32;34;44;32;34;32;43;32;102;105;108;97;91;49;93; - - - 108;105;109;112;97;114;40;102;105;108;97;41; - - - 111;117;116;112;117;116;58;32;34;68;101;112;111;105;115;58;32;34;32;43;32;102;105;108;97;91;48;93;32;43;32;34;44;32;34;32;43;32;102;105;108;97;91;49;93; - - diff --git a/web/lessons/diagramme/pt/uebergabe-leeren.visustruct b/web/lessons/diagramme/pt/uebergabe-leeren.visustruct deleted file mode 100644 index f1e5d98f..00000000 --- a/web/lessons/diagramme/pt/uebergabe-leeren.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;117;103;97;114;101;115;91;105;93;32;61;32;102;97;108;115;101; - - - - diff --git a/web/lessons/diagramme/pt/uebergabe-verdopple.visustruct b/web/lessons/diagramme/pt/uebergabe-verdopple.visustruct deleted file mode 100644 index 6b4b81c4..00000000 --- a/web/lessons/diagramme/pt/uebergabe-verdopple.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;78;111;32;115;117;98;112;114;111;103;114;97;109;97;58;32;120;32;61;32;34;32;43;32;120; - - diff --git a/web/lessons/diagramme/pt/zk-initialen-loesung.visustruct b/web/lessons/diagramme/pt/zk-initialen-loesung.visustruct deleted file mode 100644 index b6724323..00000000 --- a/web/lessons/diagramme/pt/zk-initialen-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/pt/zk-mehr.visustruct b/web/lessons/diagramme/pt/zk-mehr.visustruct deleted file mode 100644 index d3269eed..00000000 --- a/web/lessons/diagramme/pt/zk-mehr.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;61;32;34;80;97;115;115;97;116;101;109;112;111;34; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;110;100;101;120;79;102;40;34;97;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;108;97;115;116;73;110;100;101;120;79;102;40;34;97;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;80;65;83;83;65;84;69;77;80;79;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;114;101;112;108;97;99;101;40;34;116;101;109;112;111;34;44;32;34;112;111;114;116;101;34;41; - - - 111;117;116;112;117;116;58;32;34;77;233;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;105;100;97;100;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;105;100;97;100;101;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;105;100;97;100;101;41;32;43;32;34;32;97;110;111;115;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - diff --git a/web/lessons/diagramme/pt/zk-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/pt/zk-rueckwaerts-loesung.visustruct deleted file mode 100644 index 596c3389..00000000 --- a/web/lessons/diagramme/pt/zk-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;112;97;108;97;118;114;97;34; - - - 83;116;114;105;110;103;32;105;110;118;101;114;116;105;100;97;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 105;110;118;101;114;116;105;100;97;32;61;32;105;110;118;101;114;116;105;100;97;32;43;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;105;110;118;101;114;116;105;100;97; - - diff --git a/web/lessons/diagramme/pt/zk-split.visustruct b/web/lessons/diagramme/pt/zk-split.visustruct deleted file mode 100644 index bbfd79fd..00000000 --- a/web/lessons/diagramme/pt/zk-split.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;76;117;100;119;105;103;59;98;111;100;101;59;55;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;114;116;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;112;97;114;116;101;115;91;105;93; - - - - diff --git a/web/lessons/diagramme/pt/zk-woerter-loesung.visustruct b/web/lessons/diagramme/pt/zk-woerter-loesung.visustruct deleted file mode 100644 index 54901704..00000000 --- a/web/lessons/diagramme/pt/zk-woerter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;114;97;115;101;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;102;114;97;115;101;34; - - - 83;116;114;105;110;103;91;93;32;112;97;108;97;118;114;97;115;32;61;32;102;114;97;115;101;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;80;97;108;97;118;114;97;115;58;32;34;32;43;32;112;97;108;97;118;114;97;115;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/pt/zk-zaehlen.visustruct b/web/lessons/diagramme/pt/zk-zaehlen.visustruct deleted file mode 100644 index 1ff7ff80..00000000 --- a/web/lessons/diagramme/pt/zk-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;112;97;108;97;118;114;97;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;97;39; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;78;250;109;101;114;111;32;100;101;32;97;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - diff --git a/web/lessons/diagramme/pt/zk-zeichen.visustruct b/web/lessons/diagramme/pt/zk-zeichen.visustruct deleted file mode 100644 index 9b2b37ce..00000000 --- a/web/lessons/diagramme/pt/zk-zeichen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;61;32;34;80;97;115;115;97;116;101;109;112;111;34; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;115;117;98;115;116;114;105;110;103;40;48;44;32;53;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;115;117;98;115;116;114;105;110;103;40;53;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;110;100;101;120;79;102;40;34;116;101;109;112;111;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - diff --git a/web/lessons/diagramme/sim-kino-loesung.visustruct b/web/lessons/diagramme/sim-kino-loesung.visustruct deleted file mode 100644 index f8e2ebce..00000000 --- a/web/lessons/diagramme/sim-kino-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;75;97;114;116;101;110;63;34; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;32;61;32;97;110;122;97;104;108;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - diff --git a/web/lessons/diagramme/sim-kino-start.visustruct b/web/lessons/diagramme/sim-kino-start.visustruct deleted file mode 100644 index 5b619568..00000000 --- a/web/lessons/diagramme/sim-kino-start.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;75;97;114;116;101;110;63;34; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;32;61;32;97;110;122;97;104;108;32;42;32;56;46;53; - - diff --git a/web/lessons/diagramme/sim-tausch-loesung.visustruct b/web/lessons/diagramme/sim-tausch-loesung.visustruct deleted file mode 100644 index e09714c2..00000000 --- a/web/lessons/diagramme/sim-tausch-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/sim-tausch-start.visustruct b/web/lessons/diagramme/sim-tausch-start.visustruct deleted file mode 100644 index dc6a1728..00000000 --- a/web/lessons/diagramme/sim-tausch-start.visustruct +++ /dev/null @@ -1,21 +0,0 @@ - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - diff --git a/web/lessons/diagramme/sim-tausch.visustruct b/web/lessons/diagramme/sim-tausch.visustruct deleted file mode 100644 index 25f7665e..00000000 --- a/web/lessons/diagramme/sim-tausch.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - diff --git a/web/lessons/diagramme/ss-absteigend-loesung.visustruct b/web/lessons/diagramme/ss-absteigend-loesung.visustruct deleted file mode 100644 index 480da713..00000000 --- a/web/lessons/diagramme/ss-absteigend-loesung.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;107;97;114;116;101;110;91;105;93;32;34;75;97;114;116;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;60;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - diff --git a/web/lessons/diagramme/ss-absteigend-start.visustruct b/web/lessons/diagramme/ss-absteigend-start.visustruct deleted file mode 100644 index 837c83a8..00000000 --- a/web/lessons/diagramme/ss-absteigend-start.visustruct +++ /dev/null @@ -1,59 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;107;97;114;116;101;110;91;105;93;32;34;75;97;114;116;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;62;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - diff --git a/web/lessons/diagramme/ss-binaere-suche.visustruct b/web/lessons/diagramme/ss-binaere-suche.visustruct deleted file mode 100644 index a40c3658..00000000 --- a/web/lessons/diagramme/ss-binaere-suche.visustruct +++ /dev/null @@ -1,74 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;32;115;117;99;104;115;116;32;100;117;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;118;101;114;103;108;101;105;99;104;101;32;61;32;48; - - - 105;110;116;32;108;105;110;107;115;32;61;32;48; - - - 105;110;116;32;114;101;99;104;116;115;32;61;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;105;116;116;101;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;108;105;110;107;115;32;60;61;32;114;101;99;104;116;115; - - - 109;105;116;116;101;32;61;32;40;108;105;110;107;115;32;43;32;114;101;99;104;116;115;41;32;47;32;50; - - - 118;101;114;103;108;101;105;99;104;101;32;61;32;118;101;114;103;108;101;105;99;104;101;32;43;32;49; - - - 107;97;114;116;101;110;91;109;105;116;116;101;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;109;105;116;116;101; - - - - - 107;97;114;116;101;110;91;109;105;116;116;101;93;32;60;32;103;101;115;117;99;104;116; - - - 108;105;110;107;115;32;61;32;109;105;116;116;101;32;43;32;49; - - - - - 114;101;99;104;116;115;32;61;32;109;105;116;116;101;32;45;32;49; - - - - - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;71;101;102;117;110;100;101;110;32;97;110;32;80;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;103;108;101;105;99;104;101;58;32;34;32;43;32;118;101;114;103;108;101;105;99;104;101; - - diff --git a/web/lessons/diagramme/ss-bubblesort.visustruct b/web/lessons/diagramme/ss-bubblesort.visustruct deleted file mode 100644 index 90cdc3ba..00000000 --- a/web/lessons/diagramme/ss-bubblesort.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;62;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - diff --git a/web/lessons/diagramme/ss-karte-suchen-loesung.visustruct b/web/lessons/diagramme/ss-karte-suchen-loesung.visustruct deleted file mode 100644 index 025e8169..00000000 --- a/web/lessons/diagramme/ss-karte-suchen-loesung.visustruct +++ /dev/null @@ -1,49 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - diff --git a/web/lessons/diagramme/ss-lineare-suche.visustruct b/web/lessons/diagramme/ss-lineare-suche.visustruct deleted file mode 100644 index d1046f38..00000000 --- a/web/lessons/diagramme/ss-lineare-suche.visustruct +++ /dev/null @@ -1,58 +0,0 @@ - - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;32;115;117;99;104;115;116;32;100;117;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;118;101;114;103;108;101;105;99;104;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 118;101;114;103;108;101;105;99;104;101;32;61;32;118;101;114;103;108;101;105;99;104;101;32;43;32;49; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;71;101;102;117;110;100;101;110;32;97;110;32;80;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;103;108;101;105;99;104;101;58;32;34;32;43;32;118;101;114;103;108;101;105;99;104;101; - - diff --git a/web/lessons/diagramme/uebergabe-bonus-loesung.visustruct b/web/lessons/diagramme/uebergabe-bonus-loesung.visustruct deleted file mode 100644 index a920297f..00000000 --- a/web/lessons/diagramme/uebergabe-bonus-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;117;110;107;116;101;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;117;110;107;116;101;91;105;93; - - - - diff --git a/web/lessons/diagramme/uebergabe-bonus-start.visustruct b/web/lessons/diagramme/uebergabe-bonus-start.visustruct deleted file mode 100644 index a920297f..00000000 --- a/web/lessons/diagramme/uebergabe-bonus-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;117;110;107;116;101;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;117;110;107;116;101;91;105;93; - - - - diff --git a/web/lessons/diagramme/uebergabe-bonus-unterprogramm-loesung.visustruct b/web/lessons/diagramme/uebergabe-bonus-unterprogramm-loesung.visustruct deleted file mode 100644 index 8a89de99..00000000 --- a/web/lessons/diagramme/uebergabe-bonus-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 112;117;110;107;116;101;91;105;93;32;61;32;112;117;110;107;116;101;91;105;93;32;43;32;49; - - - - diff --git a/web/lessons/diagramme/uebergabe-haupt.visustruct b/web/lessons/diagramme/uebergabe-haupt.visustruct deleted file mode 100644 index 4ee3ce0f..00000000 --- a/web/lessons/diagramme/uebergabe-haupt.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;53; - - - 118;101;114;100;111;112;112;108;101;40;122;97;104;108;41; - - - 111;117;116;112;117;116;58;32;34;73;109;32;72;97;117;112;116;112;114;111;103;114;97;109;109;58;32;122;97;104;108;32;61;32;34;32;43;32;122;97;104;108; - - - 98;111;111;108;101;97;110;91;93;32;114;101;105;104;101;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;86;111;114;104;101;114;58;32;34;32;43;32;114;101;105;104;101;91;48;93;32;43;32;34;44;32;34;32;43;32;114;101;105;104;101;91;49;93; - - - 108;101;101;114;101;110;40;114;101;105;104;101;41; - - - 111;117;116;112;117;116;58;32;34;78;97;99;104;104;101;114;58;32;34;32;43;32;114;101;105;104;101;91;48;93;32;43;32;34;44;32;34;32;43;32;114;101;105;104;101;91;49;93; - - diff --git a/web/lessons/diagramme/uebergabe-leeren.visustruct b/web/lessons/diagramme/uebergabe-leeren.visustruct deleted file mode 100644 index 512dad1a..00000000 --- a/web/lessons/diagramme/uebergabe-leeren.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;108;97;101;116;122;101;46;108;101;110;103;116;104; - 105;43;43; - - - 112;108;97;101;116;122;101;91;105;93;32;61;32;102;97;108;115;101; - - - - diff --git a/web/lessons/diagramme/uebergabe-verdopple.visustruct b/web/lessons/diagramme/uebergabe-verdopple.visustruct deleted file mode 100644 index d46d3925..00000000 --- a/web/lessons/diagramme/uebergabe-verdopple.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;73;109;32;85;110;116;101;114;112;114;111;103;114;97;109;109;58;32;120;32;61;32;34;32;43;32;120; - - diff --git a/web/lessons/diagramme/vb-abstand-anzeigen-loesung.visustruct b/web/lessons/diagramme/vb-abstand-anzeigen-loesung.visustruct deleted file mode 100644 index 649275b4..00000000 --- a/web/lessons/diagramme/vb-abstand-anzeigen-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;98;115;116;97;110;100;58;32;34;32;43;32;97;98;115;116;97;110;100;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-abstand-halten-loesung.visustruct b/web/lessons/diagramme/vb-abstand-halten-loesung.visustruct deleted file mode 100644 index 44439995..00000000 --- a/web/lessons/diagramme/vb-abstand-halten-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/vb-alarmanlage-loesung.visustruct b/web/lessons/diagramme/vb-alarmanlage-loesung.visustruct deleted file mode 100644 index 17f707f6..00000000 --- a/web/lessons/diagramme/vb-alarmanlage-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-ampel-loesung.visustruct b/web/lessons/diagramme/vb-ampel-loesung.visustruct deleted file mode 100644 index 3829666b..00000000 --- a/web/lessons/diagramme/vb-ampel-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - diff --git a/web/lessons/diagramme/vb-ampel-zustand-loesung.visustruct b/web/lessons/diagramme/vb-ampel-zustand-loesung.visustruct deleted file mode 100644 index e8c16ca2..00000000 --- a/web/lessons/diagramme/vb-ampel-zustand-loesung.visustruct +++ /dev/null @@ -1,43 +0,0 @@ - - - - 105;110;116;32;122;117;115;116;97;110;100;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;49; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;50; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;48; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-ausgang-loesung.visustruct b/web/lessons/diagramme/vb-ausgang-loesung.visustruct deleted file mode 100644 index f8d71eef..00000000 --- a/web/lessons/diagramme/vb-ausgang-loesung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/vb-begruessung-loesung.visustruct b/web/lessons/diagramme/vb-begruessung-loesung.visustruct deleted file mode 100644 index e8753e35..00000000 --- a/web/lessons/diagramme/vb-begruessung-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;72;97;108;108;111;33;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;99;104;32;98;105;110;32;86;105;115;117;66;111;116;34;41; - - diff --git a/web/lessons/diagramme/vb-binaer-zaehlen-loesung.visustruct b/web/lessons/diagramme/vb-binaer-zaehlen-loesung.visustruct deleted file mode 100644 index 5e14345b..00000000 --- a/web/lessons/diagramme/vb-binaer-zaehlen-loesung.visustruct +++ /dev/null @@ -1,52 +0,0 @@ - - - - 105;110;116;32;122;97;104;108;32;61;32;48; - 122;97;104;108;32;60;32;56; - 122;97;104;108;43;43; - - - 122;97;104;108;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 122;97;104;108;32;47;32;50;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 122;97;104;108;32;47;32;52;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-bis-rot-loesung.visustruct b/web/lessons/diagramme/vb-bis-rot-loesung.visustruct deleted file mode 100644 index d34d1bf7..00000000 --- a/web/lessons/diagramme/vb-bis-rot-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-bis-wand-loesung.visustruct b/web/lessons/diagramme/vb-bis-wand-loesung.visustruct deleted file mode 100644 index 61006955..00000000 --- a/web/lessons/diagramme/vb-bis-wand-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-bis-ziel-loesung.visustruct b/web/lessons/diagramme/vb-bis-ziel-loesung.visustruct deleted file mode 100644 index c4406947..00000000 --- a/web/lessons/diagramme/vb-bis-ziel-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-buchstaben-loesung.visustruct b/web/lessons/diagramme/vb-buchstaben-loesung.visustruct deleted file mode 100644 index 7e525c62..00000000 --- a/web/lessons/diagramme/vb-buchstaben-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;86;105;115;117;66;111;116;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;48;44;32;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;51;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-do-while-loesung.visustruct b/web/lessons/diagramme/vb-do-while-loesung.visustruct deleted file mode 100644 index f2700981..00000000 --- a/web/lessons/diagramme/vb-do-while-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-drei-lichter-loesung.visustruct b/web/lessons/diagramme/vb-drei-lichter-loesung.visustruct deleted file mode 100644 index 0fc5644e..00000000 --- a/web/lessons/diagramme/vb-drei-lichter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;101;108;98;34;41; - - - 109;98;111;116;46;108;101;100;40;52;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - diff --git a/web/lessons/diagramme/vb-dreieck-loesung.visustruct b/web/lessons/diagramme/vb-dreieck-loesung.visustruct deleted file mode 100644 index a9274899..00000000 --- a/web/lessons/diagramme/vb-dreieck-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - diff --git a/web/lessons/diagramme/vb-einparken-loesung.visustruct b/web/lessons/diagramme/vb-einparken-loesung.visustruct deleted file mode 100644 index c7d76ec8..00000000 --- a/web/lessons/diagramme/vb-einparken-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;52;53; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;112;97;114;107;116;33;34;41; - - diff --git a/web/lessons/diagramme/vb-erster-plan-loesung.visustruct b/web/lessons/diagramme/vb-erster-plan-loesung.visustruct deleted file mode 100644 index 8b9629d1..00000000 --- a/web/lessons/diagramme/vb-erster-plan-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/vb-farben-loesung.visustruct b/web/lessons/diagramme/vb-farben-loesung.visustruct deleted file mode 100644 index 8f0297c6..00000000 --- a/web/lessons/diagramme/vb-farben-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 83;116;114;105;110;103;91;93;32;102;97;114;98;101;110;32;61;32;123;34;114;111;116;34;44;32;34;103;101;108;98;34;44;32;34;103;114;252;110;34;44;32;34;98;108;97;117;34;44;32;34;108;105;108;97;34;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;102;97;114;98;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;102;97;114;98;101;110;91;105;93;41; - - - - diff --git a/web/lessons/diagramme/vb-farbweiche-loesung.visustruct b/web/lessons/diagramme/vb-farbweiche-loesung.visustruct deleted file mode 100644 index fced2c4e..00000000 --- a/web/lessons/diagramme/vb-farbweiche-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-04-sensoren-start.visustruct b/web/lessons/diagramme/vb-fehler-04-sensoren-start.visustruct deleted file mode 100644 index a9c2f881..00000000 --- a/web/lessons/diagramme/vb-fehler-04-sensoren-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;114;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-05-bis-zur-wand-start.visustruct b/web/lessons/diagramme/vb-fehler-05-bis-zur-wand-start.visustruct deleted file mode 100644 index 7067b49d..00000000 --- a/web/lessons/diagramme/vb-fehler-05-bis-zur-wand-start.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;70;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct b/web/lessons/diagramme/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct deleted file mode 100644 index d1c948e7..00000000 --- a/web/lessons/diagramme/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;83;99;104;114;105;116;116;101;41; - - diff --git a/web/lessons/diagramme/vb-fehler-05-zaehlen-und-summieren-start.visustruct b/web/lessons/diagramme/vb-fehler-05-zaehlen-und-summieren-start.visustruct deleted file mode 100644 index ada1a5f9..00000000 --- a/web/lessons/diagramme/vb-fehler-05-zaehlen-und-summieren-start.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;49;48; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 103;101;115;97;109;116;32;61;32;71;101;115;97;109;116;32;43;32;115;101;105;116;101; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;103;101;115;97;109;116;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-fehler-06-abstand-start.visustruct b/web/lessons/diagramme/vb-fehler-06-abstand-start.visustruct deleted file mode 100644 index bf9d17e6..00000000 --- a/web/lessons/diagramme/vb-fehler-06-abstand-start.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;41; - - diff --git a/web/lessons/diagramme/vb-fehler-06-konstanten-final-start.visustruct b/web/lessons/diagramme/vb-fehler-06-konstanten-final-start.visustruct deleted file mode 100644 index 5a1b924b..00000000 --- a/web/lessons/diagramme/vb-fehler-06-konstanten-final-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;83;69;73;84;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-06-nah-mittel-weit-start.visustruct b/web/lessons/diagramme/vb-fehler-06-nah-mittel-weit-start.visustruct deleted file mode 100644 index 5a614024..00000000 --- a/web/lessons/diagramme/vb-fehler-06-nah-mittel-weit-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;78;97;99;104;116;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;228;109;109;101;114;117;110;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;84;97;103;34;41; - - - - - - diff --git a/web/lessons/diagramme/vb-fehler-07-linie-start.visustruct b/web/lessons/diagramme/vb-fehler-07-linie-start.visustruct deleted file mode 100644 index 31bc2281..00000000 --- a/web/lessons/diagramme/vb-fehler-07-linie-start.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;83;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;99;104;114;105;116;116;101; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-07-und-oder-start.visustruct b/web/lessons/diagramme/vb-fehler-07-und-oder-start.visustruct deleted file mode 100644 index 3e2a4c3d..00000000 --- a/web/lessons/diagramme/vb-fehler-07-und-oder-start.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;116;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-fehler-08-farben-start.visustruct b/web/lessons/diagramme/vb-fehler-08-farben-start.visustruct deleted file mode 100644 index 76621b0f..00000000 --- a/web/lessons/diagramme/vb-fehler-08-farben-start.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;103;114;252;110;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/vb-fehler-08-groesster-wert-start.visustruct b/web/lessons/diagramme/vb-fehler-08-groesster-wert-start.visustruct deleted file mode 100644 index b8b7ab73..00000000 --- a/web/lessons/diagramme/vb-fehler-08-groesster-wert-start.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;114;105;99;104;116;117;110;103;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - 114;105;99;104;116;117;110;103;32;61;32;73; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;82;105;99;104;116;117;110;103;58;32;34;32;43;32;114;105;99;104;116;117;110;103;41; - - diff --git a/web/lessons/diagramme/vb-fehler-09-fallauswahl-mbot2-start.visustruct b/web/lessons/diagramme/vb-fehler-09-fallauswahl-mbot2-start.visustruct deleted file mode 100644 index cb289a8e..00000000 --- a/web/lessons/diagramme/vb-fehler-09-fallauswahl-mbot2-start.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;116;104;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;81;117;101;114;108;105;110;105;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-10-tasten-joystick-start.visustruct b/web/lessons/diagramme/vb-fehler-10-tasten-joystick-start.visustruct deleted file mode 100644 index d23f531a..00000000 --- a/web/lessons/diagramme/vb-fehler-10-tasten-joystick-start.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;66;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/vb-fehler-10-unterbrechen-mit-break-start.visustruct b/web/lessons/diagramme/vb-fehler-10-unterbrechen-mit-break-start.visustruct deleted file mode 100644 index 60311a88..00000000 --- a/web/lessons/diagramme/vb-fehler-10-unterbrechen-mit-break-start.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;71;101;115;116;111;112;112;116;41; - - diff --git a/web/lessons/diagramme/vb-fehler-11-licht-start.visustruct b/web/lessons/diagramme/vb-fehler-11-licht-start.visustruct deleted file mode 100644 index b6402b72..00000000 --- a/web/lessons/diagramme/vb-fehler-11-licht-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-11-merker-boolean-start.visustruct b/web/lessons/diagramme/vb-fehler-11-merker-boolean-start.visustruct deleted file mode 100644 index 54781a09..00000000 --- a/web/lessons/diagramme/vb-fehler-11-merker-boolean-start.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;108;97;117;116;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;97;117;116;32;61;32;84;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;97;117;116; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;107;108;97;116;115;99;104;116;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;101;115;32;115;116;105;108;108;34;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-12-lautstaerke-start.visustruct b/web/lessons/diagramme/vb-fehler-12-lautstaerke-start.visustruct deleted file mode 100644 index 6d30b64d..00000000 --- a/web/lessons/diagramme/vb-fehler-12-lautstaerke-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;111;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-13-neigung-start.visustruct b/web/lessons/diagramme/vb-fehler-13-neigung-start.visustruct deleted file mode 100644 index 2ee2a480..00000000 --- a/web/lessons/diagramme/vb-fehler-13-neigung-start.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;108;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-14-leds-start.visustruct b/web/lessons/diagramme/vb-fehler-14-leds-start.visustruct deleted file mode 100644 index 47876f0f..00000000 --- a/web/lessons/diagramme/vb-fehler-14-leds-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;80;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-15-einzelne-leds-start.visustruct b/web/lessons/diagramme/vb-fehler-15-einzelne-leds-start.visustruct deleted file mode 100644 index b0bb8ff1..00000000 --- a/web/lessons/diagramme/vb-fehler-15-einzelne-leds-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;73;41;46;115;101;116;67;111;108;111;114;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-16-lautsprecher-start.visustruct b/web/lessons/diagramme/vb-fehler-16-lautsprecher-start.visustruct deleted file mode 100644 index 1769d482..00000000 --- a/web/lessons/diagramme/vb-fehler-16-lautsprecher-start.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-17-arrays-melodie-start.visustruct b/web/lessons/diagramme/vb-fehler-17-arrays-melodie-start.visustruct deleted file mode 100644 index d576d3f6..00000000 --- a/web/lessons/diagramme/vb-fehler-17-arrays-melodie-start.visustruct +++ /dev/null @@ -1,38 +0,0 @@ - - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 119;101;114;116;101;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;87;101;114;116;101;91;105;93; - - - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;119;101;114;116;32;61;32;115;117;109;109;101;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108;119;101;114;116;41; - - diff --git a/web/lessons/diagramme/vb-fehler-18-bildschirm-start.visustruct b/web/lessons/diagramme/vb-fehler-18-bildschirm-start.visustruct deleted file mode 100644 index e183bcee..00000000 --- a/web/lessons/diagramme/vb-fehler-18-bildschirm-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;73;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - diff --git a/web/lessons/diagramme/vb-fehler-19-zeichenketten-start.visustruct b/web/lessons/diagramme/vb-fehler-19-zeichenketten-start.visustruct deleted file mode 100644 index 471458ae..00000000 --- a/web/lessons/diagramme/vb-fehler-19-zeichenketten-start.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;97;108;108;111;32;75;108;97;115;115;101;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;84;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - diff --git a/web/lessons/diagramme/vb-fehler-20-motoren-start.visustruct b/web/lessons/diagramme/vb-fehler-20-motoren-start.visustruct deleted file mode 100644 index 7bb29edd..00000000 --- a/web/lessons/diagramme/vb-fehler-20-motoren-start.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;83;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vb-fehler-21-stoppuhr-akku-start.visustruct b/web/lessons/diagramme/vb-fehler-21-stoppuhr-akku-start.visustruct deleted file mode 100644 index 91981c2d..00000000 --- a/web/lessons/diagramme/vb-fehler-21-stoppuhr-akku-start.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;107;107;117;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/vb-fehler-21-zustaende-start.visustruct b/web/lessons/diagramme/vb-fehler-21-zustaende-start.visustruct deleted file mode 100644 index 086ea035..00000000 --- a/web/lessons/diagramme/vb-fehler-21-zustaende-start.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;103;114;252;110; - - - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;101;108;98;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-22-eigene-methoden-start.visustruct b/web/lessons/diagramme/vb-fehler-22-eigene-methoden-start.visustruct deleted file mode 100644 index a297bf57..00000000 --- a/web/lessons/diagramme/vb-fehler-22-eigene-methoden-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;85;109;100;114;101;104;117;110;103;101;110;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-fehler-23-wie-geht-es-weiter-start.visustruct b/web/lessons/diagramme/vb-fehler-23-wie-geht-es-weiter-start.visustruct deleted file mode 100644 index 4a79ee1a..00000000 --- a/web/lessons/diagramme/vb-fehler-23-wie-geht-es-weiter-start.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-gas-bis-wand-loesung.visustruct b/web/lessons/diagramme/vb-gas-bis-wand-loesung.visustruct deleted file mode 100644 index 9bde42e9..00000000 --- a/web/lessons/diagramme/vb-gas-bis-wand-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vb-gerade-loesung.visustruct b/web/lessons/diagramme/vb-gerade-loesung.visustruct deleted file mode 100644 index f4a8d0dc..00000000 --- a/web/lessons/diagramme/vb-gerade-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/vb-hindernis-loesung.visustruct b/web/lessons/diagramme/vb-hindernis-loesung.visustruct deleted file mode 100644 index a55408ef..00000000 --- a/web/lessons/diagramme/vb-hindernis-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - diff --git a/web/lessons/diagramme/vb-java-akku-loesung.visustruct b/web/lessons/diagramme/vb-java-akku-loesung.visustruct deleted file mode 100644 index 27293e85..00000000 --- a/web/lessons/diagramme/vb-java-akku-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;107;107;117;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/vb-java-ampel-text-loesung.visustruct b/web/lessons/diagramme/vb-java-ampel-text-loesung.visustruct deleted file mode 100644 index 97995a57..00000000 --- a/web/lessons/diagramme/vb-java-ampel-text-loesung.visustruct +++ /dev/null @@ -1,48 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;114;252;110;34; - - - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;101;108;98;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-java-blau-gruen-loesung.visustruct b/web/lessons/diagramme/vb-java-blau-gruen-loesung.visustruct deleted file mode 100644 index a02ee1e4..00000000 --- a/web/lessons/diagramme/vb-java-blau-gruen-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - diff --git a/web/lessons/diagramme/vb-java-blinken-loesung.visustruct b/web/lessons/diagramme/vb-java-blinken-loesung.visustruct deleted file mode 100644 index dabcffb1..00000000 --- a/web/lessons/diagramme/vb-java-blinken-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-java-countdown-loesung.visustruct b/web/lessons/diagramme/vb-java-countdown-loesung.visustruct deleted file mode 100644 index 1cb55130..00000000 --- a/web/lessons/diagramme/vb-java-countdown-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;105;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - diff --git a/web/lessons/diagramme/vb-java-hoeher-loesung.visustruct b/web/lessons/diagramme/vb-java-hoeher-loesung.visustruct deleted file mode 100644 index fb887f9b..00000000 --- a/web/lessons/diagramme/vb-java-hoeher-loesung.visustruct +++ /dev/null @@ -1,13 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;44;32;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-java-joystick-loesung.visustruct b/web/lessons/diagramme/vb-java-joystick-loesung.visustruct deleted file mode 100644 index c6f2fdfb..00000000 --- a/web/lessons/diagramme/vb-java-joystick-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/vb-java-klatsch-merker-loesung.visustruct b/web/lessons/diagramme/vb-java-klatsch-merker-loesung.visustruct deleted file mode 100644 index c378e8fa..00000000 --- a/web/lessons/diagramme/vb-java-klatsch-merker-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;108;97;117;116;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;97;117;116;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;97;117;116; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;107;108;97;116;115;99;104;116;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;101;115;32;115;116;105;108;108;34;41; - - - - diff --git a/web/lessons/diagramme/vb-java-konstante-seite-loesung.visustruct b/web/lessons/diagramme/vb-java-konstante-seite-loesung.visustruct deleted file mode 100644 index 4709b9a9..00000000 --- a/web/lessons/diagramme/vb-java-konstante-seite-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;83;69;73;84;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;83;69;73;84;69;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-laermampel-loesung.visustruct b/web/lessons/diagramme/vb-java-laermampel-loesung.visustruct deleted file mode 100644 index e53abe39..00000000 --- a/web/lessons/diagramme/vb-java-laermampel-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-lauflicht-loesung.visustruct b/web/lessons/diagramme/vb-java-lauflicht-loesung.visustruct deleted file mode 100644 index c0d1c576..00000000 --- a/web/lessons/diagramme/vb-java-lauflicht-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - diff --git a/web/lessons/diagramme/vb-java-linienfolger-loesung.visustruct b/web/lessons/diagramme/vb-java-linienfolger-loesung.visustruct deleted file mode 100644 index 88c13463..00000000 --- a/web/lessons/diagramme/vb-java-linienfolger-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vb-java-nachtlicht-ton-loesung.visustruct b/web/lessons/diagramme/vb-java-nachtlicht-ton-loesung.visustruct deleted file mode 100644 index 1af2e87b..00000000 --- a/web/lessons/diagramme/vb-java-nachtlicht-ton-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-rand-loesung.visustruct b/web/lessons/diagramme/vb-java-rand-loesung.visustruct deleted file mode 100644 index cd17afc4..00000000 --- a/web/lessons/diagramme/vb-java-rand-loesung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;99;104;114;105;116;116;101; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-rechteck-loesung.visustruct b/web/lessons/diagramme/vb-java-rechteck-loesung.visustruct deleted file mode 100644 index cc3578d7..00000000 --- a/web/lessons/diagramme/vb-java-rechteck-loesung.visustruct +++ /dev/null @@ -1,28 +0,0 @@ - - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;54;48; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;97;101;110;103;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;98;114;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-rechteck-umdrehungen-loesung.visustruct b/web/lessons/diagramme/vb-java-rechteck-umdrehungen-loesung.visustruct deleted file mode 100644 index 67b9f72f..00000000 --- a/web/lessons/diagramme/vb-java-rechteck-umdrehungen-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-java-richtung-loesung.visustruct b/web/lessons/diagramme/vb-java-richtung-loesung.visustruct deleted file mode 100644 index 9eb32a98..00000000 --- a/web/lessons/diagramme/vb-java-richtung-loesung.visustruct +++ /dev/null @@ -1,41 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;114;105;99;104;116;117;110;103;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - 114;105;99;104;116;117;110;103;32;61;32;105; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;82;105;99;104;116;117;110;103;58;32;34;32;43;32;114;105;99;104;116;117;110;103;41; - - diff --git a/web/lessons/diagramme/vb-java-schritte-zaehlen-loesung.visustruct b/web/lessons/diagramme/vb-java-schritte-zaehlen-loesung.visustruct deleted file mode 100644 index dba178f6..00000000 --- a/web/lessons/diagramme/vb-java-schritte-zaehlen-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101;41; - - diff --git a/web/lessons/diagramme/vb-java-sicher-fahren-loesung.visustruct b/web/lessons/diagramme/vb-java-sicher-fahren-loesung.visustruct deleted file mode 100644 index 0aea9179..00000000 --- a/web/lessons/diagramme/vb-java-sicher-fahren-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-java-spirale-strecke-loesung.visustruct b/web/lessons/diagramme/vb-java-spirale-strecke-loesung.visustruct deleted file mode 100644 index 9f6a7df9..00000000 --- a/web/lessons/diagramme/vb-java-spirale-strecke-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;49;48; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 103;101;115;97;109;116;32;61;32;103;101;115;97;109;116;32;43;32;115;101;105;116;101; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;103;101;115;97;109;116;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-java-tageszeit-loesung.visustruct b/web/lessons/diagramme/vb-java-tageszeit-loesung.visustruct deleted file mode 100644 index 766d96d2..00000000 --- a/web/lessons/diagramme/vb-java-tageszeit-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;78;97;99;104;116;34;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;228;109;109;101;114;117;110;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;84;97;103;34;41; - - - - - - diff --git a/web/lessons/diagramme/vb-java-taste-stopp-loesung.visustruct b/web/lessons/diagramme/vb-java-taste-stopp-loesung.visustruct deleted file mode 100644 index 30a7a776..00000000 --- a/web/lessons/diagramme/vb-java-taste-stopp-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;115;116;111;112;112;116;34;41; - - diff --git a/web/lessons/diagramme/vb-java-textlaenge-loesung.visustruct b/web/lessons/diagramme/vb-java-textlaenge-loesung.visustruct deleted file mode 100644 index 61824d1c..00000000 --- a/web/lessons/diagramme/vb-java-textlaenge-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;97;108;108;111;32;75;108;97;115;115;101;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;116;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - diff --git a/web/lessons/diagramme/vb-java-wand-piep-loesung.visustruct b/web/lessons/diagramme/vb-java-wand-piep-loesung.visustruct deleted file mode 100644 index 13a3d130..00000000 --- a/web/lessons/diagramme/vb-java-wand-piep-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-java-wasserwaage-loesung.visustruct b/web/lessons/diagramme/vb-java-wasserwaage-loesung.visustruct deleted file mode 100644 index 82c30a84..00000000 --- a/web/lessons/diagramme/vb-java-wasserwaage-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-klatschen-loesung.visustruct b/web/lessons/diagramme/vb-klatschen-loesung.visustruct deleted file mode 100644 index c61e7e6e..00000000 --- a/web/lessons/diagramme/vb-klatschen-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;60;32;53;48; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/vb-knight-rider-loesung.visustruct b/web/lessons/diagramme/vb-knight-rider-loesung.visustruct deleted file mode 100644 index b4ea5574..00000000 --- a/web/lessons/diagramme/vb-knight-rider-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;32;48; - 105;45;45; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - diff --git a/web/lessons/diagramme/vb-konstante-wand-loesung.visustruct b/web/lessons/diagramme/vb-konstante-wand-loesung.visustruct deleted file mode 100644 index cab9e6a1..00000000 --- a/web/lessons/diagramme/vb-konstante-wand-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;65;66;83;84;65;78;68;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-l00-bausteine-loesung.visustruct b/web/lessons/diagramme/vb-l00-bausteine-loesung.visustruct deleted file mode 100644 index f8e6edf9..00000000 --- a/web/lessons/diagramme/vb-l00-bausteine-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - diff --git a/web/lessons/diagramme/vb-l00-sequenz-loesung.visustruct b/web/lessons/diagramme/vb-l00-sequenz-loesung.visustruct deleted file mode 100644 index 28be0d65..00000000 --- a/web/lessons/diagramme/vb-l00-sequenz-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/vb-l03-wachsend-loesung.visustruct b/web/lessons/diagramme/vb-l03-wachsend-loesung.visustruct deleted file mode 100644 index 13ec1e5f..00000000 --- a/web/lessons/diagramme/vb-l03-wachsend-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - diff --git a/web/lessons/diagramme/vb-l06k-mit.visustruct b/web/lessons/diagramme/vb-l06k-mit.visustruct deleted file mode 100644 index 46fc7980..00000000 --- a/web/lessons/diagramme/vb-l06k-mit.visustruct +++ /dev/null @@ -1,25 +0,0 @@ - - - - 99;111;110;115;116;32;105;110;116;32;65;66;83;84;65;78;68;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-l06k-ohne.visustruct b/web/lessons/diagramme/vb-l06k-ohne.visustruct deleted file mode 100644 index 35c22f4c..00000000 --- a/web/lessons/diagramme/vb-l06k-ohne.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-l06n-einparkhilfe.visustruct b/web/lessons/diagramme/vb-l06n-einparkhilfe.visustruct deleted file mode 100644 index f41c348f..00000000 --- a/web/lessons/diagramme/vb-l06n-einparkhilfe.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;112;34;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;97;110;103;115;97;109;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;114;101;105;34;41; - - - - - - diff --git a/web/lessons/diagramme/vb-l21z-ausweichen.visustruct b/web/lessons/diagramme/vb-l21z-ausweichen.visustruct deleted file mode 100644 index eee0f567..00000000 --- a/web/lessons/diagramme/vb-l21z-ausweichen.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;102;97;104;114;101;110;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50;48; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;102;97;104;114;101;110;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 122;117;115;116;97;110;100;32;61;32;34;100;114;101;104;101;110;34; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 122;117;115;116;97;110;100;32;61;32;34;102;97;104;114;101;110;34; - - - - - - diff --git a/web/lessons/diagramme/vb-labyrinth-loesung.visustruct b/web/lessons/diagramme/vb-labyrinth-loesung.visustruct deleted file mode 100644 index 1ce4696e..00000000 --- a/web/lessons/diagramme/vb-labyrinth-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 248; - - - - - - - - diff --git a/web/lessons/diagramme/vb-laufschrift-loesung.visustruct b/web/lessons/diagramme/vb-laufschrift-loesung.visustruct deleted file mode 100644 index 40558b46..00000000 --- a/web/lessons/diagramme/vb-laufschrift-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;32;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;116;46;99;104;97;114;65;116;40;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-linienfolger-stopp-loesung.visustruct b/web/lessons/diagramme/vb-linienfolger-stopp-loesung.visustruct deleted file mode 100644 index 2364830d..00000000 --- a/web/lessons/diagramme/vb-linienfolger-stopp-loesung.visustruct +++ /dev/null @@ -1,44 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vb-linienmuster-loesung.visustruct b/web/lessons/diagramme/vb-linienmuster-loesung.visustruct deleted file mode 100644 index 33a0442b..00000000 --- a/web/lessons/diagramme/vb-linienmuster-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;81;117;101;114;108;105;110;105;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - diff --git a/web/lessons/diagramme/vb-meister-platz-loesung.visustruct b/web/lessons/diagramme/vb-meister-platz-loesung.visustruct deleted file mode 100644 index 59d5aac8..00000000 --- a/web/lessons/diagramme/vb-meister-platz-loesung.visustruct +++ /dev/null @@ -1,35 +0,0 @@ - - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;109;32;119;101;105;116;101;115;116;101;110;58;32;34;32;43;32;109;97;120;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-melodie-loesung.visustruct b/web/lessons/diagramme/vb-melodie-loesung.visustruct deleted file mode 100644 index 8e93cb1f..00000000 --- a/web/lessons/diagramme/vb-melodie-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;91;93;32;116;111;101;110;101;32;61;32;123;50;54;50;44;32;50;57;52;44;32;51;51;48;44;32;51;52;57;44;32;51;57;50;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;111;101;110;101;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;116;111;101;110;101;91;105;93;44;32;51;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-messreihe-loesung.visustruct b/web/lessons/diagramme/vb-messreihe-loesung.visustruct deleted file mode 100644 index 3a8edb7d..00000000 --- a/web/lessons/diagramme/vb-messreihe-loesung.visustruct +++ /dev/null @@ -1,38 +0,0 @@ - - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 119;101;114;116;101;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;119;101;114;116;101;91;105;93; - - - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;119;101;114;116;32;61;32;115;117;109;109;101;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108;119;101;114;116;41; - - diff --git a/web/lessons/diagramme/vb-morsen-loesung.visustruct b/web/lessons/diagramme/vb-morsen-loesung.visustruct deleted file mode 100644 index f571cbeb..00000000 --- a/web/lessons/diagramme/vb-morsen-loesung.visustruct +++ /dev/null @@ -1,66 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;54;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - diff --git a/web/lessons/diagramme/vb-nachtlicht-loesung.visustruct b/web/lessons/diagramme/vb-nachtlicht-loesung.visustruct deleted file mode 100644 index 0fbf4b85..00000000 --- a/web/lessons/diagramme/vb-nachtlicht-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;53;53;44;32;50;53;53;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - diff --git a/web/lessons/diagramme/vb-nachtwaechter-loesung.visustruct b/web/lessons/diagramme/vb-nachtwaechter-loesung.visustruct deleted file mode 100644 index 281f3984..00000000 --- a/web/lessons/diagramme/vb-nachtwaechter-loesung.visustruct +++ /dev/null @@ -1,17 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;90;122;122;34;41; - - diff --git a/web/lessons/diagramme/vb-notbremse-loesung.visustruct b/web/lessons/diagramme/vb-notbremse-loesung.visustruct deleted file mode 100644 index f719d9a6..00000000 --- a/web/lessons/diagramme/vb-notbremse-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-oder-loesung.visustruct b/web/lessons/diagramme/vb-oder-loesung.visustruct deleted file mode 100644 index 3522ff31..00000000 --- a/web/lessons/diagramme/vb-oder-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41;32;124;124;32;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/vb-quadrat-loesung.visustruct b/web/lessons/diagramme/vb-quadrat-loesung.visustruct deleted file mode 100644 index 0c141aac..00000000 --- a/web/lessons/diagramme/vb-quadrat-loesung.visustruct +++ /dev/null @@ -1,75 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - diff --git a/web/lessons/diagramme/vb-quadrat-methode-loesung.visustruct b/web/lessons/diagramme/vb-quadrat-methode-loesung.visustruct deleted file mode 100644 index 66f40d9d..00000000 --- a/web/lessons/diagramme/vb-quadrat-methode-loesung.visustruct +++ /dev/null @@ -1,9 +0,0 @@ - - - - 113;117;97;100;114;97;116;40;51;48;41; - - - 113;117;97;100;114;97;116;40;53;48;41; - - diff --git a/web/lessons/diagramme/vb-quadrat-schleife-loesung.visustruct b/web/lessons/diagramme/vb-quadrat-schleife-loesung.visustruct deleted file mode 100644 index 994ab632..00000000 --- a/web/lessons/diagramme/vb-quadrat-schleife-loesung.visustruct +++ /dev/null @@ -1,55 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - diff --git a/web/lessons/diagramme/vb-quadrat-unterprogramm-loesung.visustruct b/web/lessons/diagramme/vb-quadrat-unterprogramm-loesung.visustruct deleted file mode 100644 index 77a676ba..00000000 --- a/web/lessons/diagramme/vb-quadrat-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-quadrat-verschachtelt-loesung.visustruct b/web/lessons/diagramme/vb-quadrat-verschachtelt-loesung.visustruct deleted file mode 100644 index dcff5999..00000000 --- a/web/lessons/diagramme/vb-quadrat-verschachtelt-loesung.visustruct +++ /dev/null @@ -1,23 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;53; - 106;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-rand-loesung.visustruct b/web/lessons/diagramme/vb-rand-loesung.visustruct deleted file mode 100644 index 776c42d4..00000000 --- a/web/lessons/diagramme/vb-rand-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - diff --git a/web/lessons/diagramme/vb-schraeglage-loesung.visustruct b/web/lessons/diagramme/vb-schraeglage-loesung.visustruct deleted file mode 100644 index 30ebd3a7..00000000 --- a/web/lessons/diagramme/vb-schraeglage-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;45;50;48; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;51;48;48;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/vb-schritte-welt-loesung.visustruct b/web/lessons/diagramme/vb-schritte-welt-loesung.visustruct deleted file mode 100644 index a30a0ede..00000000 --- a/web/lessons/diagramme/vb-schritte-welt-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101;41; - - diff --git a/web/lessons/diagramme/vb-slalom-loesung.visustruct b/web/lessons/diagramme/vb-slalom-loesung.visustruct deleted file mode 100644 index 885ff4a4..00000000 --- a/web/lessons/diagramme/vb-slalom-loesung.visustruct +++ /dev/null @@ -1,52 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-spirale-falsch-loesung.visustruct b/web/lessons/diagramme/vb-spirale-falsch-loesung.visustruct deleted file mode 100644 index 13ec1e5f..00000000 --- a/web/lessons/diagramme/vb-spirale-falsch-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - diff --git a/web/lessons/diagramme/vb-spirale-falsch-start.visustruct b/web/lessons/diagramme/vb-spirale-falsch-start.visustruct deleted file mode 100644 index 21998266..00000000 --- a/web/lessons/diagramme/vb-spirale-falsch-start.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-spirale-loesung.visustruct b/web/lessons/diagramme/vb-spirale-loesung.visustruct deleted file mode 100644 index 04f5926b..00000000 --- a/web/lessons/diagramme/vb-spirale-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;56; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;105;32;42;32;49;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-start-a-loesung.visustruct b/web/lessons/diagramme/vb-start-a-loesung.visustruct deleted file mode 100644 index 7e4f2167..00000000 --- a/web/lessons/diagramme/vb-start-a-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/vb-startsignal-loesung.visustruct b/web/lessons/diagramme/vb-startsignal-loesung.visustruct deleted file mode 100644 index 418c547d..00000000 --- a/web/lessons/diagramme/vb-startsignal-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;61;32;51; - 114;117;110;100;101;43;43; - - - 114;117;110;100;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;117;102;32;100;105;101;32;80;108;228;116;122;101;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;101;114;116;105;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - diff --git a/web/lessons/diagramme/vb-strecke-anzeigen-loesung.visustruct b/web/lessons/diagramme/vb-strecke-anzeigen-loesung.visustruct deleted file mode 100644 index d86bfed5..00000000 --- a/web/lessons/diagramme/vb-strecke-anzeigen-loesung.visustruct +++ /dev/null @@ -1,34 +0,0 @@ - - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;54;48; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;97;101;110;103;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;98;114;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 105;110;116;32;115;116;114;101;99;107;101;32;61;32;50;32;42;32;108;97;101;110;103;101;32;43;32;50;32;42;32;98;114;101;105;116;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;115;116;114;101;99;107;101;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-tempo-abstand-loesung.visustruct b/web/lessons/diagramme/vb-tempo-abstand-loesung.visustruct deleted file mode 100644 index 63518a55..00000000 --- a/web/lessons/diagramme/vb-tempo-abstand-loesung.visustruct +++ /dev/null @@ -1,26 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - - diff --git a/web/lessons/diagramme/vb-tonleiter-loesung.visustruct b/web/lessons/diagramme/vb-tonleiter-loesung.visustruct deleted file mode 100644 index 6e401309..00000000 --- a/web/lessons/diagramme/vb-tonleiter-loesung.visustruct +++ /dev/null @@ -1,18 +0,0 @@ - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;54;50;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;57;52;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;51;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;52;57;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;57;50;44;32;51;48;48;41; - - diff --git a/web/lessons/diagramme/vb-treppe-loesung.visustruct b/web/lessons/diagramme/vb-treppe-loesung.visustruct deleted file mode 100644 index d59d8d3f..00000000 --- a/web/lessons/diagramme/vb-treppe-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-tunnel-loesung.visustruct b/web/lessons/diagramme/vb-tunnel-loesung.visustruct deleted file mode 100644 index f831c2f5..00000000 --- a/web/lessons/diagramme/vb-tunnel-loesung.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;116;32;108;105;110;107;115;32;61;32;57;57; - - - 105;110;116;32;114;101;99;104;116;115;32;61;32;57;57; - - - 108;105;110;107;115;32;62;61;32;50;48;32;124;124;32;114;101;99;104;116;115;32;62;61;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 108;105;110;107;115;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 114;101;99;104;116;115;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-ueberladung-umdrehung-loesung.visustruct b/web/lessons/diagramme/vb-ueberladung-umdrehung-loesung.visustruct deleted file mode 100644 index e53e0ec0..00000000 --- a/web/lessons/diagramme/vb-ueberladung-umdrehung-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-und-loesung.visustruct b/web/lessons/diagramme/vb-und-loesung.visustruct deleted file mode 100644 index 09977d66..00000000 --- a/web/lessons/diagramme/vb-und-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - diff --git a/web/lessons/diagramme/vb-variable-seite-loesung.visustruct b/web/lessons/diagramme/vb-variable-seite-loesung.visustruct deleted file mode 100644 index dfc419ae..00000000 --- a/web/lessons/diagramme/vb-variable-seite-loesung.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 105;110;116;32;115;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-vier-quadrate-loesung.visustruct b/web/lessons/diagramme/vb-vier-quadrate-loesung.visustruct deleted file mode 100644 index c1ec5583..00000000 --- a/web/lessons/diagramme/vb-vier-quadrate-loesung.visustruct +++ /dev/null @@ -1,33 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;52; - 106;43;43; - - - 105;110;116;32;107;32;61;32;48; - 107;32;60;32;53; - 107;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vb-wald-loesung.visustruct b/web/lessons/diagramme/vb-wald-loesung.visustruct deleted file mode 100644 index da9a53f0..00000000 --- a/web/lessons/diagramme/vb-wald-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/vb-wand-vorn-loesung.visustruct b/web/lessons/diagramme/vb-wand-vorn-loesung.visustruct deleted file mode 100644 index 259f2533..00000000 --- a/web/lessons/diagramme/vb-wand-vorn-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vb-wurde-es-dunkel-loesung.visustruct b/web/lessons/diagramme/vb-wurde-es-dunkel-loesung.visustruct deleted file mode 100644 index 910f2711..00000000 --- a/web/lessons/diagramme/vb-wurde-es-dunkel-loesung.visustruct +++ /dev/null @@ -1,42 +0,0 @@ - - - - 98;111;111;108;101;97;110;32;100;117;110;107;101;108;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 100;117;110;107;101;108;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 100;117;110;107;101;108; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;69;115;32;119;97;114;32;100;117;110;107;101;108;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;109;109;101;114;32;104;101;108;108;34;41; - - - - diff --git a/web/lessons/diagramme/vb-zentimeter-loesung.visustruct b/web/lessons/diagramme/vb-zentimeter-loesung.visustruct deleted file mode 100644 index 24f26469..00000000 --- a/web/lessons/diagramme/vb-zentimeter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;116;32;115;116;114;101;99;107;101;32;61;32;122;101;110;116;105;109;101;116;101;114;40;51;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;116;114;101;99;107;101;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;115;116;114;101;99;107;101;32;43;32;34;32;99;109;34;41; - - diff --git a/web/lessons/diagramme/vb-zentimeter-unterprogramm-loesung.visustruct b/web/lessons/diagramme/vb-zentimeter-unterprogramm-loesung.visustruct deleted file mode 100644 index e2931b4b..00000000 --- a/web/lessons/diagramme/vb-zentimeter-unterprogramm-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 114;101;116;117;114;110;32;117;109;100;114;101;104;117;110;103;101;110;32;42;32;50;48; - - diff --git a/web/lessons/diagramme/vb-zwei-sekunden-loesung.visustruct b/web/lessons/diagramme/vb-zwei-sekunden-loesung.visustruct deleted file mode 100644 index cc9ce11d..00000000 --- a/web/lessons/diagramme/vb-zwei-sekunden-loesung.visustruct +++ /dev/null @@ -1,20 +0,0 @@ - - - - 109;98;111;116;46;114;101;115;101;116;84;105;109;101;114;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;84;105;109;101;114;40;41;32;60;32;50; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vbb-abstand-zeigen-loesung.visustruct b/web/lessons/diagramme/vbb-abstand-zeigen-loesung.visustruct deleted file mode 100644 index f003bf79..00000000 --- a/web/lessons/diagramme/vbb-abstand-zeigen-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;41; - - diff --git a/web/lessons/diagramme/vbb-ampel-loesung.visustruct b/web/lessons/diagramme/vbb-ampel-loesung.visustruct deleted file mode 100644 index 083e58e2..00000000 --- a/web/lessons/diagramme/vbb-ampel-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;116;111;112;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vbb-ausgang-loesung.visustruct b/web/lessons/diagramme/vbb-ausgang-loesung.visustruct deleted file mode 100644 index 7bfd3115..00000000 --- a/web/lessons/diagramme/vbb-ausgang-loesung.visustruct +++ /dev/null @@ -1,30 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/vbb-bis-wand-loesung.visustruct b/web/lessons/diagramme/vbb-bis-wand-loesung.visustruct deleted file mode 100644 index 1d0a17e3..00000000 --- a/web/lessons/diagramme/vbb-bis-wand-loesung.visustruct +++ /dev/null @@ -1,11 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vbb-blinken-loesung.visustruct b/web/lessons/diagramme/vbb-blinken-loesung.visustruct deleted file mode 100644 index 777aa971..00000000 --- a/web/lessons/diagramme/vbb-blinken-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - diff --git a/web/lessons/diagramme/vbb-disco-loesung.visustruct b/web/lessons/diagramme/vbb-disco-loesung.visustruct deleted file mode 100644 index 00343e1e..00000000 --- a/web/lessons/diagramme/vbb-disco-loesung.visustruct +++ /dev/null @@ -1,31 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - diff --git a/web/lessons/diagramme/vbb-dreieck-loesung.visustruct b/web/lessons/diagramme/vbb-dreieck-loesung.visustruct deleted file mode 100644 index d51a7ac6..00000000 --- a/web/lessons/diagramme/vbb-dreieck-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - diff --git a/web/lessons/diagramme/vbb-ecke-loesung.visustruct b/web/lessons/diagramme/vbb-ecke-loesung.visustruct deleted file mode 100644 index 58855c8b..00000000 --- a/web/lessons/diagramme/vbb-ecke-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/vbb-erster-block-loesung.visustruct b/web/lessons/diagramme/vbb-erster-block-loesung.visustruct deleted file mode 100644 index c80687be..00000000 --- a/web/lessons/diagramme/vbb-erster-block-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - diff --git a/web/lessons/diagramme/vbb-hin-und-zurueck-loesung.visustruct b/web/lessons/diagramme/vbb-hin-und-zurueck-loesung.visustruct deleted file mode 100644 index 92e16818..00000000 --- a/web/lessons/diagramme/vbb-hin-und-zurueck-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - diff --git a/web/lessons/diagramme/vbb-klotz-loesung.visustruct b/web/lessons/diagramme/vbb-klotz-loesung.visustruct deleted file mode 100644 index 77449c19..00000000 --- a/web/lessons/diagramme/vbb-klotz-loesung.visustruct +++ /dev/null @@ -1,36 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - diff --git a/web/lessons/diagramme/vbb-licht-gruen-loesung.visustruct b/web/lessons/diagramme/vbb-licht-gruen-loesung.visustruct deleted file mode 100644 index f94e4eea..00000000 --- a/web/lessons/diagramme/vbb-licht-gruen-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - diff --git a/web/lessons/diagramme/vbb-linie-bis-rot-loesung.visustruct b/web/lessons/diagramme/vbb-linie-bis-rot-loesung.visustruct deleted file mode 100644 index 460b38e9..00000000 --- a/web/lessons/diagramme/vbb-linie-bis-rot-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vbb-linienfolger-loesung.visustruct b/web/lessons/diagramme/vbb-linienfolger-loesung.visustruct deleted file mode 100644 index 0b3e617a..00000000 --- a/web/lessons/diagramme/vbb-linienfolger-loesung.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - diff --git a/web/lessons/diagramme/vbb-notbremse-loesung.visustruct b/web/lessons/diagramme/vbb-notbremse-loesung.visustruct deleted file mode 100644 index 42dbd492..00000000 --- a/web/lessons/diagramme/vbb-notbremse-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - -1; - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - -1; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;115;116;111;112;112;116;34;41; - - diff --git a/web/lessons/diagramme/vbb-quadrat-loesung.visustruct b/web/lessons/diagramme/vbb-quadrat-loesung.visustruct deleted file mode 100644 index 8eb8fcf9..00000000 --- a/web/lessons/diagramme/vbb-quadrat-loesung.visustruct +++ /dev/null @@ -1,27 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - diff --git a/web/lessons/diagramme/vbb-quadrat-wiederhole-loesung.visustruct b/web/lessons/diagramme/vbb-quadrat-wiederhole-loesung.visustruct deleted file mode 100644 index 6e483de3..00000000 --- a/web/lessons/diagramme/vbb-quadrat-wiederhole-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vbb-rand-loesung.visustruct b/web/lessons/diagramme/vbb-rand-loesung.visustruct deleted file mode 100644 index cad8cce9..00000000 --- a/web/lessons/diagramme/vbb-rand-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - diff --git a/web/lessons/diagramme/vbb-sirene-loesung.visustruct b/web/lessons/diagramme/vbb-sirene-loesung.visustruct deleted file mode 100644 index f3628547..00000000 --- a/web/lessons/diagramme/vbb-sirene-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;48;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;54;48;48;44;32;51;48;48;41; - - - - diff --git a/web/lessons/diagramme/vbb-slalom-loesung.visustruct b/web/lessons/diagramme/vbb-slalom-loesung.visustruct deleted file mode 100644 index 4a6ca877..00000000 --- a/web/lessons/diagramme/vbb-slalom-loesung.visustruct +++ /dev/null @@ -1,52 +0,0 @@ - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - diff --git a/web/lessons/diagramme/vbb-start-a-loesung.visustruct b/web/lessons/diagramme/vbb-start-a-loesung.visustruct deleted file mode 100644 index 555cc6f9..00000000 --- a/web/lessons/diagramme/vbb-start-a-loesung.visustruct +++ /dev/null @@ -1,14 +0,0 @@ - - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - diff --git a/web/lessons/diagramme/vbb-wald-loesung.visustruct b/web/lessons/diagramme/vbb-wald-loesung.visustruct deleted file mode 100644 index 4d06b675..00000000 --- a/web/lessons/diagramme/vbb-wald-loesung.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - diff --git a/web/lessons/diagramme/vbb-wand-vorn-loesung.visustruct b/web/lessons/diagramme/vbb-wand-vorn-loesung.visustruct deleted file mode 100644 index 22d28eab..00000000 --- a/web/lessons/diagramme/vbb-wand-vorn-loesung.visustruct +++ /dev/null @@ -1,16 +0,0 @@ - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - diff --git a/web/lessons/diagramme/vbb-weiter-loesung.visustruct b/web/lessons/diagramme/vbb-weiter-loesung.visustruct deleted file mode 100644 index e92baa3f..00000000 --- a/web/lessons/diagramme/vbb-weiter-loesung.visustruct +++ /dev/null @@ -1,6 +0,0 @@ - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - diff --git a/web/lessons/diagramme/zk-initialen-loesung.visustruct b/web/lessons/diagramme/zk-initialen-loesung.visustruct deleted file mode 100644 index eea530b2..00000000 --- a/web/lessons/diagramme/zk-initialen-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - diff --git a/web/lessons/diagramme/zk-mehr.visustruct b/web/lessons/diagramme/zk-mehr.visustruct deleted file mode 100644 index 43785a3b..00000000 --- a/web/lessons/diagramme/zk-mehr.visustruct +++ /dev/null @@ -1,39 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;90;105;101;103;101;110;98;111;99;107;34; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;110;100;101;120;79;102;40;34;101;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;108;97;115;116;73;110;100;101;120;79;102;40;34;101;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;90;73;69;71;69;78;66;79;67;75;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;114;101;112;108;97;99;101;40;34;98;111;99;107;34;44;32;34;107;228;115;101;34;41; - - - 111;117;116;112;117;116;58;32;34;77;228;104;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;97;108;116;101;114;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;97;108;116;101;114;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;97;108;116;101;114;41;32;43;32;34;32;74;97;104;114;101;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - diff --git a/web/lessons/diagramme/zk-rueckwaerts-loesung.visustruct b/web/lessons/diagramme/zk-rueckwaerts-loesung.visustruct deleted file mode 100644 index 6d84212f..00000000 --- a/web/lessons/diagramme/zk-rueckwaerts-loesung.visustruct +++ /dev/null @@ -1,22 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;111;114;116;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;32;114;117;101;99;107;119;97;101;114;116;115;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 114;117;101;99;107;119;97;101;114;116;115;32;61;32;114;117;101;99;107;119;97;101;114;116;115;32;43;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;114;117;101;99;107;119;97;101;114;116;115; - - diff --git a/web/lessons/diagramme/zk-split.visustruct b/web/lessons/diagramme/zk-split.visustruct deleted file mode 100644 index 11b53fd4..00000000 --- a/web/lessons/diagramme/zk-split.visustruct +++ /dev/null @@ -1,19 +0,0 @@ - - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;76;117;100;119;105;103;59;71;101;105;223;98;111;99;107;59;55;34; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;105;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;116;101;105;108;101;91;105;93; - - - - diff --git a/web/lessons/diagramme/zk-woerter-loesung.visustruct b/web/lessons/diagramme/zk-woerter-loesung.visustruct deleted file mode 100644 index ac607d14..00000000 --- a/web/lessons/diagramme/zk-woerter-loesung.visustruct +++ /dev/null @@ -1,12 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;97;116;122;32;34;83;97;116;122;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;91;93;32;119;111;101;114;116;101;114;32;61;32;115;97;116;122;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;87;246;114;116;101;114;58;32;34;32;43;32;119;111;101;114;116;101;114;46;108;101;110;103;116;104; - - diff --git a/web/lessons/diagramme/zk-zaehlen.visustruct b/web/lessons/diagramme/zk-zaehlen.visustruct deleted file mode 100644 index 9a440e5b..00000000 --- a/web/lessons/diagramme/zk-zaehlen.visustruct +++ /dev/null @@ -1,32 +0,0 @@ - - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;111;114;116;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 119;111;114;116;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;101;39; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;32;101;58;32;34;32;43;32;97;110;122;97;104;108; - - diff --git a/web/lessons/diagramme/zk-zeichen.visustruct b/web/lessons/diagramme/zk-zeichen.visustruct deleted file mode 100644 index a8e82425..00000000 --- a/web/lessons/diagramme/zk-zeichen.visustruct +++ /dev/null @@ -1,24 +0,0 @@ - - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;71;101;105;223;98;111;99;107;34; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;48;44;32;52;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;52;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;110;100;101;120;79;102;40;34;98;111;99;107;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - diff --git a/web/lessons/en/01-was-ist-ein-struktogramm.md b/web/lessons/en/01-was-ist-ein-struktogramm.md deleted file mode 100644 index 25a7198f..00000000 --- a/web/lessons/en/01-was-ist-ein-struktogramm.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -titel: What is a structure chart? -bereich: First steps -kurz: Show and read algorithms as a series of clear steps. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an algorithm is -- read a structure chart -- recognise the three basic building blocks: sequence, branch and loop -::: - -## An algorithm is an exact set of instructions - -An **algorithm** describes step by step how to solve a task. Every step must be so clear that someone can carry it out without asking, a person just as well as a computer. - -You know algorithms from everyday life: a recipe, directions to a place or the instructions for building a wardrobe. - -::: diagramm l1-tee.visustruct -Making tea as a structure chart: every box is one step. -::: - -::: angeberwissen -Baaah, who wrote the first program? A woman, back in 1843, long before the first computer! **Ada Lovelace** described step by step how Charles Babbage's *Analytical Engine* should calculate the so-called Bernoulli numbers. That was a real algorithm for a machine. The instructions were meant to be stored on punched cards (more on this in lesson 3). However, the machine was never built. - -Ada had another brilliant idea: a machine like this can do more than calculate. It could also work with letters, symbols or even music. That is exactly what computers do today. A programming language called **Ada** is named after her. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), mathematician (picture: watercolour by Alfred Edward Chalon, around 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), public domain). -::: - -## How to read a structure chart - -You read a structure chart **from top to bottom**. Every box is one step. Only when a step is finished does the next one start. - -This way of drawing was invented by **Isaac Nassi and Ben Shneiderman** (1973). That is why it is also called a Nassi-Shneiderman diagram. In Germany it is standardised as DIN 66261. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -Left: Isaac “Ike” Nassi (photo: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/), cropped), right: Ben Shneiderman (photo: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> A structure chart always has exactly one start (at the top) and one end (at the bottom). There are no arrows and no jumps all over the place. Programs that jump all over the place are called **spaghetti code**, because their flow gets tangled like noodles on a plate. More on this in the next lesson. - -::: angeberwissen -Baaah, listen up! In 1972 Nassi and Shneiderman were still PhD students at Stony Brook University near New York. Shneiderman was about 25 and Nassi about 23. So basically two students who wrote computer science history on the side. -::: - -## Building block 1: the sequence - -Every algorithm can be built from just **three basic building blocks**: **sequence**, [**branch**]{.blau} and [**loop**]{.rosa}. The colours help you match them: [statements]{.gelb} are yellow, branches blue, loops pink. The sequence has no colour of its own, it is simply the row of statements one below the other. You see the same colours in the editor and later in the code. You can switch them off in the settings under “Colored blocks”. In this step and the next two you get to know them one by one. - -The **sequence** is the simplest building block: several statements run **one after another**, each exactly once. - -This is what it looks like: - -- Every statement is a **simple rectangle**. -- The rectangles sit **on top of each other** without a gap. -- They run from top to bottom, one statement after the other. - -::: diagramm l1-sequenz.visustruct -Four statements as a sequence. First you get up, then you brush your teeth, and so on. -::: - -> The tea example above is a sequence, too. - -::: angeberwissen -15 minutes! That is how long Shneiderman needed for the first sketch with sequence, branch and loop. I need longer than that to choose between hay and clover. -::: - -## Building block 2: the branch - -Often the next step depends on a **condition**. A condition is a question you can only answer with **yes or no**, for example *Is it raining?* In VisuStruct yes is **true** and no is **false**. - -For decisions like this there is the **branch**. This is what it looks like: - -- At the top, the condition sits in a **triangle**. -- Below it, the box splits into **two columns**: on the left the path for **true**, on the right the path for **false**. -- **Only one** of the two paths ever runs. After that, both paths continue together below the branch. - -::: diagramm l1-verzweigung.visustruct -If it is raining, the left path runs, otherwise the right one. After that, both cases continue with “Leave”. -::: - -> If nothing should happen in one case, that path stays empty. The sign **ø** shows this. - -::: angeberwissen -Spelling alert! It is Shneiderman, with no c. Anyone who writes “Schneiderman” gets a gentle nudge from my horns. He himself points out that people keep sneaking a c into his name. -::: - -## Building block 3: the loop - -If something should happen **several times**, you do not write it down several times. Instead you use a **loop**, also called repetition. - -This is what it looks like: - -- At the top is the condition, for example *Plate not empty*. -- The steps that are repeated are **indented** below it. At the top and on the left, the box forms an **upside-down L** around them. -- Before every pass the condition is checked. As long as it is **true**, the steps inside the upside-down L run once more. If it is **false**, the program continues below the loop. - -::: diagramm l1-wiederholung.visustruct -As long as the plate is not empty, you take a bite. After that, the plate is cleared. -::: - -> There are more kinds of loops. You will meet them in later lessons. - -## All three together - -The building blocks become really useful when you combine them. This diagram contains all three: a sequence from top to bottom, a [branch]{.blau} and a [loop]{.rosa}. The yellow boxes are simple [statements]{.gelb}. The statements inside a branch or a loop are yellow too. Strictly speaking, the whole diagram is a sequence, because the branch and the loop also stand in the row from top to bottom. - -::: diagramm l1-grundbausteine.visustruct -You recognise the branch by its triangle and the loop by its upside-down L. Everything together sits in a sequence. -::: - -The boxes can also be **nested** inside each other: a loop or a path can contain sequences, branches and loops again. - -::: angeberwissen -Comfort for anyone who has messed up a test: the famous journal *Communications of the ACM* rejected the structure chart article. According to Nassi, the reviewer even said they should collect every copy and burn it. Ouch! It then appeared in 1973 in a smaller journal and today it is quoted thousands of times. Ha! -::: - -## Structure first, then code - -When you program, two things happen at once: you have to think about the steps and pay attention to how the programming language is written. A structure chart lets you keep the two apart. - -1. First you think about the **logic**: which steps, which conditions, which repetitions? -2. Then you translate the finished structure into **Java, Python or JavaScript**. VisuStruct can even generate the code for you. - -With the **simulation** you can also run your structure chart step by step and check whether it really does what it should. - -## Game: Which building block? - -::: spiel frage -titel: Game: Which building block? -intro: Ludwig describes things from his everyday life. Which of the three building blocks is hidden in each? Eight rounds. -antworten: sequence | branch | loop - -runde: -frage: "Toothpaste on the brush, brush your teeth, rinse your mouth." -richtig: sequence -> Three steps, each exactly once and always in this order. - -runde: -frage: "If it rains, take the umbrella, otherwise the sunglasses." -richtig: branch -> A condition decides which of the two ways is taken. - -runde: -frage: "Stir until the dough is smooth." -richtig: loop -> Stirring is repeated as long as the dough isn't smooth yet. - -runde: -frage: "Run ten laps around the sports field." -richtig: loop -> The same lap is run ten times. - -runde: -frage: "Shoes on, jacket on, door open, off you go." -richtig: sequence -> One step after the other, nothing is skipped or repeated. - -runde: -frage: "If the light is green, you cross the street." -richtig: branch -> Even without "otherwise" it is a branch. The otherwise branch is simply empty. - -runde: -frage: "Roll the dice until you get a six." -richtig: loop -> You don't know beforehand how often. You roll until the condition is met. - -runde: -frage: "If someone says 'Come in', you go in. Otherwise you wait at the door." -richtig: branch -> The answer decides between two ways: go in or wait. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an algorithm is. → An algorithm is an exact set of instructions -- I can read a structure chart from top to bottom. → How to read a structure chart -- I can name the three basic building blocks: sequence, branch and loop. → Building block 1: the sequence -- I can recognise every building block in a structure chart by its shape. → All three together -::: - -## Quiz - -::: quiz -? In which direction do you read a structure chart? -→ How to read a structure chart -- [ ] From left to right -- [x] From top to bottom -- [ ] From bottom to top -> Every box is one step, and the steps follow each other from top to bottom. - -? Which building block is NOT one of the three basic building blocks? -→ Building block 1: the sequence -- [ ] Sequence -- [ ] Branch -- [x] Jump -- [ ] Loop -> There are no jumps in a structure chart. Sequence, branch and loop are enough for every algorithm. - -? How do you recognise a branch in a structure chart? -→ All three together -- [ ] By an upside-down L -- [x] By a triangle with the condition and two columns below it -- [ ] By several rectangles on top of each other -> The upside-down L belongs to the loop, and rectangles on top of each other are a sequence. - -? When does the inside of a loop run once more? -→ All three together -- [x] As long as the condition is true -- [ ] Only if the condition is false -- [ ] Always exactly twice -> The condition is checked before every pass. If it is false, the program continues below the loop. - -? What is an algorithm? -→ An algorithm is an exact set of instructions -- [x] Clear step-by-step instructions for solving a task -- [ ] A particular program in Java -- [ ] A picture that explains a program -> An algorithm does not depend on a programming language. A structure chart is one way to write it down. -::: diff --git a/web/lessons/en/01-weitere-darstellungen.md b/web/lessons/en/01-weitere-darstellungen.md deleted file mode 100644 index 94816fce..00000000 --- a/web/lessons/en/01-weitere-darstellungen.md +++ /dev/null @@ -1,239 +0,0 @@ ---- -titel: Flowcharts and pseudocode -bereich: First steps -kurz: Show algorithms as a flowchart and as pseudocode, and compare them with the structure chart. ---- - -## Skills I will gain now - -::: kompetenzen -- read a flowchart and build one myself -- explain why jumps in a flowchart lead to mistakes -- read a simple algorithm as pseudocode -::: - -## Other ways to draw it: the flowchart - -There are two more ways to show an algorithm: the **flowchart** and **pseudocode**. You will meet both in class. - -Here you see our structure chart for drinking tea from the last lesson once more. Next we look at the flowchart, using the same example. - -::: diagramm l1-grundbausteine.visustruct -The tea example as a structure chart: first the cup, then the branch for the sugar, then the loop. -::: - -### The same algorithm as a flowchart - -::: grafik l01-pap.svg -The tea example as a flowchart. The arrow from “Wait 1 minute” back up forms the loop. -::: - -### The symbols - -A flowchart is made of a few symbols joined by **arrows**. It is standardised as DIN 66001 (ISO 5807). - -- **Oval:** start or end. Every flowchart has exactly one start and one end. -- **Rectangle:** a statement, for example *Put a cup on the table*. -- **Diamond:** a decision. The diamond holds a question, and two arrows lead out of it: **yes** and **no**. -- **Parallelogram:** an input or output. The tea example does not have one. -- **Arrow:** shows which step comes next. - -A loop has no symbol of its own in a flowchart. You recognise it by an arrow that leads **back up**. - -### The advantages - -- **Very easy to read**, even for people who have never seen a flowchart. You simply follow the arrows with your finger. -- Flowcharts are not only used in programming but wherever a process is explained: in user manuals, for troubleshooting (“Is the lamp on? No: is it plugged in?”) or in companies for work processes. - -## Exercise: Baking a cake as a flowchart - -Now you build a flowchart yourself. Ludwig bakes a cake like this: - -He puts the ingredients in the bowl and stirs the batter. If the batter is not smooth yet, he keeps stirring. Once it is smooth, he pours the batter into the tin and bakes the cake for 30 minutes. Then he checks with a wooden skewer: if batter still sticks to it, he bakes for 5 more minutes and checks again. Once the skewer comes out clean, he takes the cake out and calls: “The cake is ready!” - -::: pap -titel: Exercise: Baking a cake as a flowchart -auftrag: The places in the chart are empty. Put each element in its place. The shape of the place helps you. -start: Start -anweisung: Put ingredients in the bowl -anweisung: Stir the batter -frage: Batter smooth? | nein -> Stir the batter -anweisung: Pour batter into the tin -anweisung: Bake for 30 minutes -frage: Skewer clean? | nein: Bake 5 more minutes -anweisung: Take the cake out -ausgabe: “The cake is ready!” -ende: End -erklärung: Two loops, two arrows back up: when stirring, the arrow leads back to “Stir the batter”; when baking, it leads from “Bake 5 more minutes” back to the question. The output sits in the parallelogram. -legende: ja -::: - -**Draw your own flowcharts:** With **VisuFlow** you can easily draw your own flowchart. The symbols are ready in a bar: tapping one adds the next symbol and connects it with an arrow right away. To get started you can load the tea example. You save your flowchart as a file and export it as an image or PDF, for example for your notebook or a presentation. [Open VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Drawbacks of the flowchart: jumps - -An arrow in a flowchart may lead from any symbol to any other. That sounds handy, but it is dangerous. - -In the old days, programs were written exactly like that. Every line had a **number**, and the command **GOTO** jumped to a line number. Every GOTO is an arrow from the flowchart. Our tea example would look like this: - -``` -10 Put a cup on the table -20 IF no sugar wanted, GOTO 40 -30 Add sugar -40 IF tea not too hot, GOTO 70 -50 Wait 1 minute -60 GOTO 40 -70 Drink the tea -``` - -### One more line, and the jumps go wrong - -Now you improve the algorithm: after the sugar, the tea bag should come out. You insert the new line 40, and all lines below move down one number. But the jumps still point to the old numbers: - -``` -10 Put a cup on the table -20 IF no sugar wanted, GOTO 40 -30 Add sugar -40 Take out the tea bag -50 IF tea not too hot, GOTO 70 -60 Wait 1 minute -70 GOTO 40 -80 Drink the tea -``` - -Play it through: line 70 no longer sends you to the check in line 50 but to the tea bag. And when the tea is cool enough, line 50 jumps to line 70 instead of *Drink the tea*. So you keep taking out the tea bag and never drink. With every change you have to check every GOTO. In a big program with hundreds of jumps, something easily goes wrong. - -### All over the place: spaghetti code - -With arrows you can also jump out of the middle of a loop, or from outside into the middle of a loop. Such jumps quickly make the chart confusing. With many jumps the arrows cross until the chart looks like a plate of spaghetti. That is why it is called **spaghetti code**. You can hardly follow the flow any more, and mistakes hide well. - -A structure chart has no arrows. If you insert a box, everything below simply moves down with it, and no jump can point to the wrong place. For leaving a loop early there is a separate, tidy box, the break. You will meet it in lesson 18. - -::: angeberwissen -In 1968 the computer scientist Edsger Dijkstra wrote a famous letter: “Go To Statement Considered Harmful”. Since then, programs are built only from sequence, branch and loop. In Java, `goto` is still a reserved word today, but you are not allowed to use it. Baah, I would rather eat spaghetti than program it. -::: - -## Pseudocode and comparison - -**Pseudocode** is text that looks like a program but is written in plain language. Fixed words such as IF and WHILE show the building blocks, and the **indentation** shows what belongs to them. There are no jumps here either. This is our tea example: - -``` -Put a cup on the table -IF sugar wanted THEN - Add sugar -END IF -WHILE tea still too hot - Wait 1 minute -END WHILE -Drink the tea -``` - -Pseudocode has **no fixed notation**. Some people write WHEN instead of IF, others use an arrow for assignment (`number ← 5`) or leave out the END lines. All of that is fine. What matters is that anyone can understand it without an explanation. So pseudocode is not another programming language you have to learn. - -::: angeberwissen -Does pseudocode look familiar? The programming language Python looks very similar, and that is on purpose: its inventor Guido van Rossum wanted programs to read almost like plain text. Python also uses indentation to show what belongs together. But careful: Python is a real programming language with strict rules. One space too many and the computer complains. Pseudocode is more relaxed, just like me at breakfast. -::: - -### Compared - -- **Structure chart:** boxes inside boxes, the loop is an upside-down L. You see the structure at a glance, and wild jumps are impossible. -- **Flowchart:** symbols with arrows, the loop is an arrow back up. Very easy to picture, but big charts quickly get confusing, and jumps all over the place are possible. -- **Pseudocode:** text with indentation, the loop sits between WHILE and END WHILE. Quick to write and already close to program code. - -> All three describe the same algorithm. Once you can read one form, you learn the others quickly. - -::: angeberwissen -Nassi and Shneiderman were out to get the arrows! Their 1973 article was called “Flowchart Techniques for Structured Programming”. With arrows you can jump all over the place until the chart looks like a plate of spaghetti. In a structure chart that is simply impossible. Baah, that is how I like it: tidy, like my stable. -::: - -## Game: Which way of drawing? - -::: spiel frage -titel: Game: Which way of drawing? -intro: Ludwig describes a way of drawing an algorithm without saying its name. Is it the structure chart, the flowchart or pseudocode? Eight rounds. -antworten: structure chart | flowchart | pseudocode - -runde: -frage: “When something is repeated, one of my arrows points back up.” -richtig: flowchart -> A flowchart has no special symbol for a loop. An arrow leads back up. - -runde: -frage: “My loop looks like an upside-down L.” -richtig: structure chart -> The box wraps around the repeated steps at the top and on the left. - -runde: -frage: “I am made of words like IF and WHILE, and the indentation shows what belongs together.” -richtig: pseudocode -> Pseudocode is text in plain language that is laid out like a program. - -runde: -frage: “My branch is a diamond with the exits yes and no.” -richtig: flowchart -> In a structure chart, the condition sits in a triangle instead. - -runde: -frage: “My arrows can get tangled all over the place like spaghetti.” -richtig: flowchart -> Because arrows may lead anywhere, spaghetti code is possible in a flowchart. - -runde: -frage: “I have no fixed way of writing. You may write WHEN instead of IF.” -richtig: pseudocode -> All that matters is that anyone can understand it without an explanation. - -runde: -frage: “The condition sits at the top in a triangle, and below it I split into two columns.” -richtig: structure chart -> This is how a branch looks in a structure chart: true on the left, false on the right. - -runde: -frage: “If you insert a box, everything below simply moves down with it. No jump can go wrong.” -richtig: structure chart -> A structure chart has no arrows and no jumps, so nothing can point to the wrong place. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the symbols of a flowchart and read a simple flowchart. → Other ways to draw it: the flowchart -- I can build a simple algorithm as a flowchart myself. → Exercise: Baking a cake as a flowchart -- I can explain why jumps with GOTO lead to mistakes and spaghetti code. → Drawbacks of the flowchart: jumps -- I can read a simple algorithm as pseudocode. → Pseudocode and comparison -::: - -## Quiz - -::: quiz -? How do you recognise a loop in a flowchart? -→ Other ways to draw it: the flowchart -- [ ] By an upside-down L -- [x] By an arrow that leads back up -- [ ] By the word WHILE -> The upside-down L belongs to the structure chart, and WHILE to pseudocode. - -? Match each flowchart symbol to its meaning. -→ Other ways to draw it: the flowchart -= Oval -> Start or end -= Rectangle -> Statement -= Diamond -> Decision -= Parallelogram -> Input or output -> The diamond has two exits: yes and no. - -? You insert a new line into a program with GOTO. What can happen? -→ Drawbacks of the flowchart: jumps -- [x] The jumps point to the wrong lines because the numbers have moved -- [ ] Nothing, the jumps adjust by themselves -- [ ] The program automatically gets faster -> Every GOTO points to a fixed line number. If the lines move down, the program jumps to the wrong place. - -? In pseudocode, what shows which lines belong to a loop? -→ Pseudocode and comparison -- [ ] The colour -- [x] The indentation and the END WHILE -- [ ] An arrow -> Everything between WHILE and END WHILE is indented and is repeated. -::: diff --git a/web/lessons/en/02-der-editor.md b/web/lessons/en/02-der-editor.md deleted file mode 100644 index 2c3abebd..00000000 --- a/web/lessons/en/02-der-editor.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -titel: The editor -bereich: First steps -kurz: Insert elements, type text, move and delete. ---- - -## Skills I will gain now - -::: kompetenzen -- insert an element from the Elements area and replace a placeholder -- change the text of a box and switch between Statement, Input and Output -- move, copy and delete boxes and undo steps -::: - -## The screen - -VisuStruct has two editors: the [structure chart editor](#zeige-editor), which this lesson is about, and on the right the [Code](#zeige-codelive) area for Java code (from lesson 4). If the structure chart editor is collapsed, this lesson opens it for you. The blue words show you the areas, even when they are collapsed: move the mouse over one, and the area opens next to the learning area and lights up. Click it (or tap it), and it stays open. - -The structure chart editor has several areas. Move the mouse over a blue word or tap it, and that area lights up in the editor; collapsed areas such as Elements open. - -- **At the very top, the [toolbar](#zeige-leiste):** New, Open and Save. On the far right, the gear for [Settings](#zeige-einstellungen): text size and language. -- **Below it, the [tools](#zeige-werkzeuge)** above the elements and the structure chart: Undo, Cut, Copy, Paste, Delete, Export, Code and Simulation. The buttons only show icons. Hold the mouse over one and its name appears. -- **On the left, the [Elements](#zeige-elemente):** Here are all the building blocks you can drag into the structure chart. Some people also call this area the palette. -- **In the middle, the [structure chart](#zeige-struktogramm):** A new diagram contains only an empty placeholder, marked with **ø**. -- **At the bottom, the [Object Inspector](#zeige-objektinspektor):** Here you edit the text of the selected element. If it is too small or too big, drag the line with the grip above it up or down. - -> You can collapse this learning area at any time with the [sidebar icon](#zeige-lernen) at the top of this area. Then only the slim “🎓 Learn” strip stays on the left; click it to open the area again. It stays open next to the editor so you can try out right away what you read. In the same way there is the [📁 Files](#zeige-dateien) column on the left for your saved programs (lesson 3) and the [Code](#zeige-codelive) area on the right for Java (from lesson 4). - -## Insert an element - -Drag an element from the **Elements** area onto the structure chart. While you drag, a blue line shows where it will land: - -- **Onto the placeholder ø:** The element replaces the placeholder. -- **Onto the top half of a box:** The element goes in front of it. -- **Onto the bottom half of a box:** The element goes after it. - -::: grafik l02-einfuegen.svg -Where you drop it decides where the new element lands. -::: - -It also works without dragging: **click** an element in the Elements area (or tap it). Then it is inserted after the selected box. If the placeholder **ø** is selected, it replaces it. - -With the keyboard, press **Tab** to jump to an element and press **Enter**. The new element also goes after the selected box. - -::: angeberwissen -Grabbing boxes and dragging them around, just like you are doing now, has a fancy name: **direct manipulation**. Ben Shneiderman coined the term in 1982. Yes, the structure chart guy. That man worked harder than I do when I graze. -::: - -## Type text - -Click a box. It gets a blue outline, and its text appears in the Object Inspector below the diagram. Whatever you type there shows up in the diagram straight away. - -It is quicker to **double-click** the box, or **double-tap** it on a tablet. Then you type the text right in the structure chart. **Enter** takes it, **Esc** cancels. With the keyboard, select the box and press **Enter**. - -The box from the Elements area is called a **Statement**. At the top of the Object Inspector you choose what sort of statement it should be: - -- **Statement:** calculate or set something, e.g. `int number = 5`. You will learn how in lesson 4. -- **Input:** ask for a value while the chart runs. That comes in lesson 6. -- **Output:** show something. In the diagram it then has `output:` in front, e.g. `output: "Hello"`. - -To start with, you only need Output. Texts that should be shown exactly as they are go in **quotation marks**. - -::: diagramm l2-ausgabe.visustruct -Two outputs one after the other. Open the diagram in the editor and start the [simulation](#zeige-simulation) in the tools to see the output. You will learn more about it in lesson 7. -::: - -## Move, copy, delete - -- **Move:** Drag an existing box to a new place with the mouse. -- **Copy and paste:** Select a box, then press Ctrl+C and Ctrl+V (on a Mac ⌘C and ⌘V). The copy goes after the selected box. Without a keyboard, for example on a tablet, tap **Copy** and **Paste** in the [tools](#zeige-werkzeuge). -- **Delete:** Select a box and press Del, or click the [trash can](#zeige-loeschen) „Delete Selection“ in the tools. -- **Undo:** Ctrl+Z undoes the last step, Ctrl+Shift+Z brings it back (on a Mac ⌘Z and ⌘⇧Z). Without a keyboard, tap [Undo](#zeige-rueckgaengig) in the tools. - -## Exercise: Hello World - -::: übung l2-hallo -titel: Hello World -auftrag: Build a structure chart that outputs **Hello World**. -Drag a **Statement** from the Elements onto the placeholder **ø** and click the box. In the Object Inspector below the structure chart there are three buttons at the top: Statement, Input and Output. Click **Output**. Then type `"Hello World"` in the text field below, with quotation marks. -test: - -> Hello World -braucht: anweisung -tipp: You only need one single box. The text that should appear goes into the text field in the Object Inspector. -tipp: Watch out for the quotation marks. Without them, VisuStruct looks for a variable called Hello. -lösung: l2-hallo-loesung.visustruct -::: - -Click **Check** when you are done. VisuStruct runs your structure chart and compares the output. - -## Exercise: Two lines - -::: übung l2-zwei-zeilen -titel: Two lines -auftrag: First output **Hello** and then **I am learning structure charts**, each on its own line. -test: - -> Hello | I am learning structure charts -tipp: Two lines means two outputs. The boxes sit on top of each other and run from top to bottom. -tipp: Every output is a box of its own. Drag the second Statement onto the bottom half of the first one. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Game: Editor pro - -::: spiel frage -titel: Game: Editor pro -intro: Ludwig tests whether you have the editor under control. Seven rounds. - -runde: -frage: You drag a statement onto the placeholder **ø**. What happens? -- [x] The statement replaces the placeholder. -- The statement goes after the placeholder. -- Nothing, you can't drop anything on ø. -> The placeholder only keeps the place free. The new element takes it. - -runde: -frage: You drag an element onto the **top** half of a box. Where does it end up? -- [x] in front of it -- after it -- It replaces the box. -> Top half means in front, bottom half means after. The blue line shows you while dragging. - -runde: -frage: You drag an element onto the **bottom** half of a box. Where does it end up? -- in front of it -- [x] after it -- at the very end of the structure chart -> Bottom half means: directly after this box. - -runde: -frage: You deleted a box by accident. What brings it back? -- Ctrl+C / ⌘C -- [x] Ctrl+Z / ⌘Z -- Ctrl+V / ⌘V -> Ctrl+Z / ⌘Z undoes the last step. Ctrl+C / ⌘C copies, Ctrl+V / ⌘V pastes. - -runde: -frage: When it runs, exactly **Hello** should appear. What is in the box? -- [x] output: "Hello" -- output: Hello -- "Hello" -> Text that should be shown exactly like that goes in quotation marks. The editor puts `output:` in front when you choose the kind Output. - -runde: -frage: Where do you change the text of the selected box? -- in the Elements -- [x] in the Object Inspector -- in the toolbar at the very top -> The Object Inspector below the diagram shows the text of the selected box. - -runde: -frage: You click an element in the Elements area without dragging. Where does it end up? -- [x] after the selected box -- at the very beginning -- at the very end -> Without dragging (click, or Tab and Enter) the new element goes after the box that is selected right now. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can insert an element from the Elements area and replace a placeholder. → Insert an element -- I can change the text of a box and switch between Statement, Input and Output. → Type text -- I can move, copy and delete boxes and undo steps. → Move, copy, delete -::: - -## Quiz - -::: quiz -? What does the sign ø in a box mean? -→ Insert an element -- [ ] The box contains a mistake. -- [x] There is still an empty placeholder here. -- [ ] The program ends here. -> A placeholder is replaced when you drag an element onto it. - -? You drag a Statement onto the bottom half of a box. Where does it land? -→ Insert an element -- [ ] In front of it -- [x] After it -- [ ] The box is replaced. - -? How do you output the text Good morning exactly as it is? -→ Type text -- [ ] output: Good morning -- [x] output: "Good morning" -- [ ] input: "Good morning" -> Without quotation marks, Good morning is understood as the name of a variable. -::: diff --git a/web/lessons/en/03-speichern-oeffnen-exportieren.md b/web/lessons/en/03-speichern-oeffnen-exportieren.md deleted file mode 100644 index a4710e76..00000000 --- a/web/lessons/en/03-speichern-oeffnen-exportieren.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -titel: Save, open, export -bereich: First steps -kurz: Keep your files safe, use your workspace, open several diagrams and share them as an image or PDF. ---- - -## Skills I will gain now - -::: kompetenzen -- give a diagram a title -- save a diagram as a .visustruct file and open it again -- export a diagram as an image (PNG) or PDF -- open a second diagram in a new tab -- create my workspace `VisuStruct-Files` -- save a program in my workspace -::: - -## The .visustruct file - -Structure charts are saved as **.visustruct** files. All versions of VisuStruct use the same format: in the browser, on Mac, iPad and iPhone, and the Java app on the computer. So you can start in the browser at school and carry on in the app at home. - -::: angeberwissen -When Nassi was a student, there were no files to save. There were punched cards. Nassi says a Saturday night date looked like this: his future wife sat with him in the computer centre and punched his cards. Romance, computer science style! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -This is what a punched card looked like. One card held one line of a program, here `PRINT *, 'HELLO WORLD'` in the Fortran language. Each column is one character, and the holes in it are its code. A whole program was a stack of these cards. If you dropped it, you had to sort everything again. -::: - -::: angeberwissen -A computer pioneer from my home region in Germany: **Konrad Zuse**. On 12 May 1941 he presented the **Z3** in Berlin, the world's first working program-controlled digital computer. It already calculated in the **binary system**, using only 0 and 1, with about 2,000 relays (electric switches). Its program was not on punched cards but punched as holes into used cinema film. Zuse then invented **Plankalkül**, one of the very first programming languages. Later he lived in **Hünfeld** near Fulda until his death in 1995. The Konrad Zuse Museum there shows replicas of the Z3. My tip for a school trip! The town of Hünfeld's website tells you when it is open. -::: - -::: grafik l03-zuse-lochstreifen.svg -Roughly what Zuse's program strip looked like: old cinema film with punched holes. Each column is one instruction with 8 positions. A hole means 1, no hole means 0. The machine read the strip one instruction after another. -::: - -## Save and open - -- **[Save](#zeige-speichern)** (Ctrl+S, on a Mac ⌘S): The diagram is saved as a file. If the **Files** column is set up (see below), it goes there. Otherwise the browser asks where to put it or puts the file in your downloads folder. -- **[Open](#zeige-oeffnen)** (Ctrl+O, on a Mac ⌘O): Load a saved file again. You can also simply drag a file into the browser window. - -> The browser remembers your exercises automatically. But that only works in this browser on this device. So always save important work as a file as well. - -## The Files column - -To the left of the learning area you find the [📁 Files](#zeige-dateien) column. There you keep all your programs in one place, your **workspace**. - -**Your turn:** Set up your workspace right now, before you read on. From today on you save all your programs there. - -1. Create a workspace: open the column and click **Choose folder**. In the window, go to your **Documents**, use **New Folder** to create a folder called `VisuStruct-Files` there and select it. Allow VisuStruct to use the folder. This works in Chrome and Edge. On the iPad and in other browsers, click **Save in the browser** instead. -2. Save as usual. In your workspace, VisuStruct automatically creates a folder with today's date, for example `2026-10-23`, and saves the program in it. The file name comes from the title. - -This is what your workspace looks like then: - -```text -VisuStruct-Files ← workspace -└── 2026-10-23 ← day - └── Factorial.visustruct -``` - -Clicking a program in the column opens it again. That way you quickly find every lesson later. - -Your **learning progress** goes into the workspace too: the file `learnProgress.json` lists the lessons and exercises you have finished. If you open the workspace in another browser, your progress is back. - -> Why a real folder? Your learning progress and your programs are stored in it as files. You can take the folder with you, for example on a USB stick or in your cloud folder such as OneDrive. That way you carry on at home on another computer exactly where you stopped. If you only save in the browser, everything stays in this one browser on this device. If someone deletes the browser data, your learning progress is gone too. - -## Several diagrams in tabs - -Above the structure chart you see the [tabs](#zeige-reiter). With **+** or [New](#zeige-neu) you open another, empty diagram. A dot • in front of the name shows that there are unsaved changes. - -You type the [title](#zeige-titel) of a diagram into the field above the structure chart. It also appears when you export and is suggested as the file name. - -## Share: image, PDF or file - -With [Export](#zeige-exportieren) in the tools (icon with an arrow pointing up) you can share your structure chart: - -- **Image (PNG):** to put into a worksheet or a presentation -- **Vector graphic (SVG):** stays sharp at any size -- **PDF (A4):** one page to hand in or print -- **VisuStruct file (.visustruct):** to pass on to someone who should open and keep editing your structure chart in VisuStruct, for example by email or through the learning platform -- **Print** (Ctrl+P, on a Mac ⌘P): straight to the printer - -Under the [gear](#zeige-textgroesse) at the top right, Text size sets the font size. Larger text also shows when you export an image. - -## Try it out - -Open one of your exercises from lesson 2 and try the following: - -1. Give the diagram a name in the [title](#zeige-titel) field above the structure chart, for example `Hello World`. -2. [Save](#zeige-speichern) it in your workspace. Look in the [📁 Files](#zeige-dateien) column: is it in today's folder? -3. Open a second tab with **+** and click your program in the Files column there. It opens again. -4. [Export](#zeige-exportieren) it as an **image (PNG)** and as a **PDF (A4)**. Open both files and compare them. - -Did everything work? Then you know where your programs are and how to share them. - -## Game: How do you pass it on? - -::: spiel frage -titel: Game: How do you pass it on? -intro: Ludwig wants to pass on his structure chart. Which format fits best? Six rounds. -frage: Which format fits? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: You want to keep working on your structure chart tomorrow. -richtig: .visustruct -> Only the .visustruct file can be opened and edited in VisuStruct again. - -runde: -frage: The structure chart should go into a presentation as a picture. -richtig: PNG -> A PNG image can be put into any presentation and any worksheet. - -runde: -frage: Your teacher wants to print your work as one A4 page. -richtig: PDF -> The PDF is exactly one A4 page to hand in or print. - -runde: -frage: The structure chart goes on a poster and should stay sharp even when huge. -richtig: SVG -> A vector graphic stays sharp at any size. A PNG gets pixelated when you enlarge it. - -runde: -frage: Your friend should change the structure chart in her VisuStruct app on the iPad. -richtig: .visustruct -> All VisuStruct versions open the same .visustruct file: in the browser, on Mac, iPad and iPhone. - -runde: -frage: You want to send a picture into a chat quickly. -richtig: PNG -> Anyone can look at a PNG straight away, without VisuStruct. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can give a diagram a title. → Try it out -- I can save a diagram as a .visustruct file and open it again. → Save and open -- I can export a diagram as an image (PNG) or PDF. → Share: image, PDF or file -- I can open a second diagram in a new tab. → Several diagrams in tabs -- I can create my workspace `VisuStruct-Files`. → The Files column -- I can save a program in my workspace. → The Files column -::: - -## Quiz - -::: quiz -? Which file can you open again in the Mac app and in the Java app? -→ Save and open -- [x] The .visustruct file -- [ ] The exported PNG image -- [ ] The exported PDF -> Images and PDFs are only for looking at. You can only keep editing the .visustruct file. - -? The browser remembers your exercises. Why should you still save? -→ Save and open -- [ ] Because otherwise the exercises are not checked -- [x] Because the browser only keeps the data on this device and it can get lost -- [ ] Because you cannot export without saving - -? Today you save a program in your workspace. Where does it end up? -→ The Files column -- [ ] In the downloads folder -- [x] In the workspace, inside a folder with today's date -- [ ] Directly in the workspace without a subfolder -> VisuStruct automatically creates a date folder for each day. That keeps the programs sorted by lesson. -::: diff --git a/web/lessons/en/04-variablen-und-datentypen.md b/web/lessons/en/04-variablen-und-datentypen.md deleted file mode 100644 index 0b920edb..00000000 --- a/web/lessons/en/04-variablen-und-datentypen.md +++ /dev/null @@ -1,407 +0,0 @@ ---- -titel: Variables and data types -bereich: Basics -kurz: Store values, get to know the data types int, double, String and boolean, and calculate. ---- - -## Skills I will gain now - -::: kompetenzen -- create a variable with a data type, a name and a value -- explain that Java is case-sensitive (`number` is not `Number`) -- tell the data types int, double, String and boolean apart -- explain that = is an assignment and not a comparison -- calculate with + - * / % and use whole-number division -- create and output variables in Java -::: - -## What is a variable? - -A **variable** is like a labelled box. It has a **name**, a **data type** and a **value**. - -``` -int age = 14 -``` - -Here the variable is called `age`, its data type is `int` (whole number), and its value is 14. You can use the value again later through its name. - -::: grafik l04-variable.svg -The variable as a box: the name on the label, the data type as a sticker, the value inside. -::: - -> Variable names start with a **lowercase letter** and contain no spaces. Good names say what is inside: `price` is better than `p`. - -::: angeberwissen -In September 1999 the Mars Climate Orbiter space probe was lost. One team calculated the thrust in American units (pounds), the other expected metric units (newtons). The data only contained the number, not the unit. The probe flew too low into the Martian atmosphere and burned up, a loss of about 125 million dollars. A meaningful name like `impulseInNewtonSeconds` would have said what is inside. By the way, I always label my hay box `hayInKilos`. -::: - -If a name is made of several words, you write them together and start every further word with a capital letter: `priceWithTax`, `numberOfStudents`. This way of writing is called **camelCase**. - -::: angeberwissen -camelCase got its name because the capital letters in the middle stick out of the word like the humps of a camel: `priceWithTax` has two humps. Me, I'm a billy goat with zero humps but two horns. Sadly, nobody has invented GoatCase yet. -::: - -::: grafik l04-camelcase.svg -The capitals W and T stick out like the camel's two humps. -::: - -## Upper and lower case - -Java tells **upper and lower case** apart. The English word for this is **case-sensitive**. For Java, `number`, `Number` and `NUMBER` are three different names, so three different boxes: - -``` -int number = 4 -int Number = 6 -``` - -There are two variables here. If you later write `NUMBER`, Java finds no variable with that name. The program doesn’t run and you get an error message. - -> This also applies to Java words: `String` and `System` start with a capital, `int` and `double` are small. In your own programs, never create two variables that differ only in upper and lower case. It confuses you and everyone who reads your code. - -## Exercise: Three names - -::: übung l04-gross-klein -titel: Three names -auftrag: Ludwig created two variables: `number` with the value 4 and `Number` with the value 6. The structure chart should multiply them and output **Result: 24**. But it doesn’t run. - Click “Open in editor”, then Check, and read the error message. Fix the statement and click Check again. -start: l04-gross-klein-start.visustruct -test: - -> Result: 24 -braucht: anweisung -tipp: How many different names are in the structure chart? `number`, `Number` and `NUMBER` are three different names for the computer. -tipp: `NUMBER` doesn’t exist. For 24 you need 4 times 6, so `number` times `Number`: `int result = number * Number`. `number * number` gives 16. -lösung: l04-gross-klein-loesung.visustruct -::: - -## The four data types - -The data type says what kind of value may be stored in the variable. - -- **int:** whole numbers, e.g. `int count = 3` or `int temperature = -5` -- **double:** decimal numbers, e.g. `double price = 2.5`. You write a **point**, never a comma. -- **String:** text, e.g. `String name = "Mia"`. Text goes in quotation marks. -- **boolean:** truth values, only `true` or `false` - -There are more data types. You will soon need one of them: `char` for a **single character**, e.g. `char grade = 'C'`. A `char` goes in **single** quotation marks. You will get to know it better in lesson 17. - -::: diagramm l05-datentypen.visustruct -Three variables with different data types. Open the diagram and start the [simulation](#zeige-simulation). -::: - -You mostly meet truth values in **conditions**: `age >= 18` is either `true` or `false`. That is what lesson 8 is about. A variable of type `boolean` simply gets `true` or `false`: - -``` -boolean adult = false -``` - -::: angeberwissen -On 4 June 1996 the Ariane 5 rocket exploded just 37 seconds after launch. The cause was a single line: a decimal number for the speed was put into a whole-number variable that was too small for such large values. So the data type is no small matter, it can cost half a billion euros. -::: - -## Assignment with = - -The sign `=` is called an **assignment**. It does not mean “equals” as in maths. It means: work out the right-hand side and store the result in the variable on the left. - -``` -int points = 10 -points = points + 5 -``` - -In maths the second line would be wrong, because no number equals itself plus 5. In programming you read it like this: take the old value 10, add 5 and store 15 in `points`. You only write the data type the first time, when the variable is created. - -::: grafik l04-zuweisung.svg -First the right-hand side is calculated, then the result lands in the variable on the left. -::: - -## Calculating - -You can calculate with numbers: - -- `+` plus, `-` minus, `*` times, `/` divided by -- `%` remainder of a division (called “modulo”), e.g. `17 % 5` is 2 - -You know **division with remainder** from primary school, before you learned about decimals: 17 ÷ 5 = 3 remainder 2. 5 fits into 17 three times, that makes 15. To get to 17, 2 are still missing: that is the remainder. The computer calculates with whole numbers in exactly this way: `/` gives the result without the remainder, `%` gives only the remainder. - -| Primary school | with `/` | with `%` | -|---|---|---| -| 17 ÷ 5 = 3 remainder 2 | `17 / 5` gives 3 | `17 % 5` gives 2 | -| 20 ÷ 5 = 4 remainder 0 | `20 / 5` gives 4 | `20 % 5` gives 0 | -| 3 ÷ 5 = 0 remainder 3 | `3 / 5` gives 0 | `3 % 5` gives 3 | - -If the remainder is 0, the division works out exactly. Later you use this to check whether a number is even: for even numbers, `number % 2` is 0. - -**Multiplication and division come before addition and subtraction**, and brackets are worked out first: `2 * (3 + 4)` is 14. - -### Output text and result together - -A result on its own says little. With `+` you join a text and the value of a variable: - -``` -int age = 14 -output: "Age: " + age -``` - -This shows **Age: 14**. The text in quotation marks appears exactly as written; for `age` the computer puts in the value. The space after the colon belongs to the text, otherwise you get `Age:14`. - -## Exercise: Rectangle - -::: übung l05-rechteck -titel: Work out a rectangle -auftrag: A rectangle is 7 long and 4 wide. Create the variables `length` and `width`. Calculate the area and the perimeter in variables of their own and output both, like this: -`Area: 28` and on the next line `Perimeter: 22`. -test: - -> Area: 28 | Perimeter: 22 -braucht: anweisung -tipp: First think about which variables you need: two for the measurements and two for the results. They are all whole numbers. -tipp: The area is length times width, the perimeter is 2 times (length plus width). For the output you write e.g. `output: "Area: " + area`. The `+` joins the text and the value of the variable together. -lösung: l05-rechteck-loesung.visustruct -::: - -## Whole-number division - -If you divide two whole numbers and store the result in an `int` variable, everything after the decimal point is dropped. It is **not rounded**. - -``` -int x = 7 / 2 gives 3 -int r = 7 % 2 gives 1 (the remainder) -double d = 7.0 / 2 gives 3.5 -``` - -With `/` and `%` together you can convert things nicely, for example minutes into hours and minutes: - -::: diagramm l05-division.visustruct -135 minutes are 2 hours (135 / 60) with 15 minutes left over (135 % 60). -::: - -## Exercise: Convert seconds - -::: übung l05-sekunden -titel: Convert seconds -auftrag: 200 seconds should be output as minutes and seconds. The first and the last statement are already there. Insert statements between them that calculate `minutes` and `remainder`. -start: l05-sekunden-start.visustruct -test: - -> 3 minutes and 20 seconds -braucht: anweisung -tipp: How many whole minutes fit into 200 seconds, and how many seconds are left over? Work it out in your head first. -tipp: A minute has 60 seconds. Use `/` for the whole minutes and `%` for the remainder. Both variables have the type `int`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Exercise: Shopping - -::: übung l05-einkauf -titel: Shopping -auftrag: You buy 3 exercise books at 2.5 euros each. Create `count` as an `int` and `price` as a `double`, calculate the total price in a variable `totalPrice` and output `Total: 7.5 euros`. -test: - -> Total: 7.5 euros -braucht: anweisung -tipp: The count times the price gives the total price. For each variable, think about whether it is a whole number or a decimal number. -tipp: The total price is a decimal number, so `totalPrice` needs the type `double`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Now in Java - -A structure chart is a plan. In **Java** it becomes a program that the computer runs. Your shopping from the exercise looks like this: - -```java editor -int count = 3; -double price = 2.5; -double totalPrice = count * price; -System.out.println("Total: " + totalPrice + " euros"); -``` - -- First the data type, then the name, then the value, just like in the box. -- Every statement ends with a **semicolon** `;`. -- Output in Java is written `System.out.println(…)`. - -How the computer understands your Java code and why it runs the same on Windows, macOS and Linux is explained in the lesson “Structure chart and code”. - -::: angeberwissen -Java was first called **Oak**, after the tree outside James Gosling’s office window. From 1991 his team at Sun Microsystems wanted to program small devices such as TV set-top boxes. But another company already had the name Oak. So in 1995 it became **Java**, like the coffee from the island of Java. -::: - -## Text in Java - -Variables do not only store numbers. For text there is the data type `String`: - -```java editor -String name = "Mia"; -int age = 14; -double height = 1.62; -System.out.println(name + " is " + age + " years old."); -System.out.println("Height: " + height + " m"); -``` - -- Text goes in quotation marks. -- You write `String` with a capital letter, `int`, `double` and `boolean` in lowercase. Java tells them apart. -- With `+` you join text and numbers together. - -You type Java in the [Code](#zeige-codelive) area. The structure chart builds itself as you type. “Open in editor” brings the example into the Code area. Click “Run” there, then change the name, age or height and try again. - -::: übung l04-java-steckbrief -titel: Profile in Java -bereich: beide -code: java -auftrag: Write a profile of Ludwig the billy goat. Create three variables: the name **Ludwig**, the age **7** and the height **1.2**. Then output these two lines: - **Ludwig is 7 years old.** and **Height: 1.2 m** - “Open in editor” also opens the Code area on the right. Type your code there; the structure chart builds itself. Then click Check. -test: - -> Ludwig is 7 years old. | Height: 1.2 m -braucht: anweisung -tipp: You need a `String`, an `int` and a `double`. Every line ends with a semicolon. -tipp: `String name = "Ludwig";`, `int age = 7;` and `double height = 1.2;`. You join the output with `+`: `System.out.println(name + " is " + age + " years old.");` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Fill in the gaps: data types - -::: lückentext -titel: Profile in Java -auftrag: Fill in the missing words. On the outside is the frame that every Java program in its own file needs. The variables go inside it. -```java -[[public]] class Profile { - public static [[void]] main(String[] args) { - [[int]] age = 14; - [[double]] height = 1.62; - [[String]] name = "Mia"; - [[boolean]] hasPet = true; - System.out.[[println]](name + " is " + age + " years old."); - } -} -``` -erklärung: Whole numbers are `int`, decimal numbers `double`, texts `String` (with a capital S) and truth values `boolean`. `public class` and `public static void main` make up the frame. In the Code area you may leave it out, more on this in the lesson “Structure chart and code”. -::: - -## Find the bug: seconds - -::: übung l04-java-fehler -titel: Fix the seconds -code: java -auftrag: The program should convert 200 seconds into minutes and seconds and output **3 min 20 s**. But it outputs **20 min 3 s**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: `/` divides without a remainder, `%` gives the remainder. Which one do you need for the minutes, which one for the seconds left over? -tipp: The two operators are swapped: `int minutes = seconds / 60;` and `int rest = seconds % 60;` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l04-java-laeuft-nicht -titel: Fix the profile -code: java -auftrag: The program should output **Ludwig is 7 years old.** But it doesn’t run, because two words are spelled wrong. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig is 7 years old. -braucht: anweisung -tipp: Java is case-sensitive. Compare every word in the output carefully with what was created above. -tipp: `System` is written with a capital S. The variable is called `age` with a small a: `System.out.println(name + " is " + age + " years old.");` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which data type? - -::: spiel frage -titel: Game: Which data type? -intro: Ludwig has collected values. Which data type fits each value? Eight rounds. -antworten: int | double | String | boolean - -runde: -frage: Which data type fits `17`? -richtig: int -> A whole number without a decimal point is an `int`. - -runde: -frage: Which data type fits `3.5`? -richtig: double -> Numbers with a decimal point are `double`. - -runde: -frage: Which data type fits `"Hello"`? -richtig: String -> Text in quotation marks is a `String`. - -runde: -frage: Which data type fits `true`? -richtig: boolean -> `true` and `false` are the two truth values, data type `boolean`. - -runde: -frage: Which data type fits `"17"`? -richtig: String -> Careful, quotation marks! Here 17 is a text you can't calculate with. - -runde: -frage: Which data type fits `-4`? -richtig: int -> Negative whole numbers are `int` as well. - -runde: -frage: Which data type fits `0.0`? -richtig: double -> The point makes it a decimal number, even if there is only a 0 behind it. - -runde: -frage: Which data type fits `"false"`? -richtig: String -> In quotation marks, false is just a text too. The truth value has no quotation marks. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a variable with a data type, a name and a value. → What is a variable? -- I can explain that Java is case-sensitive (`number` is not `Number`). → Upper and lower case -- I can tell the data types int, double, String and boolean apart. → The four data types -- I can explain that = is an assignment and not a comparison. → Assignment with = -- I can calculate with + - * / % and use whole-number division. → Calculating -- I can create and output variables in Java. → Now in Java -::: - -## Quiz - -::: quiz -? Which data type fits the value 3.75? -→ The four data types -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Decimal numbers have the type double. In VisuStruct you write them with a point. - -? How many variables do `int number = 1`, `int Number = 2` and `int NUMBER = 3` create? -→ Upper and lower case -- [ ] one -- [ ] two -- [x] three -> Java is case-sensitive. The three names are different, so there are three variables. - -? What value does x have after `int x = 17 / 5`? -→ Calculating -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> With whole numbers, everything after the decimal point is dropped. It is not rounded. - -? What does `17 % 5` give? -→ Calculating -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 divided by 5 is 3, remainder 2. The operator % gives the remainder. - -? At the start, `int points = 10`. What is in points after `points = points * 2`? -→ Assignment with = -- [ ] 10 -- [ ] 12 -- [x] 20 -> The right-hand side is calculated with the old value 10, and the result 20 is stored in points. - -? Which line correctly creates a decimal number in Java? -→ Now in Java -- [ ] double price = 2,5; -- [x] double price = 2.5; -- [ ] Double price = 2.5 -> Decimal numbers use a point, and the line ends with a semicolon. -::: diff --git a/web/lessons/en/05-sequenz.md b/web/lessons/en/05-sequenz.md deleted file mode 100644 index fa7f9d02..00000000 --- a/web/lessons/en/05-sequenz.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -titel: Sequence -bereich: Basics -kurz: Run steps one after another, watch the order and swap values. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that a sequence runs from top to bottom -- explain why the order of the steps matters -- swap two values with a temporary variable -- write a sequence in Java -::: - -## Step by step - -A **sequence** is a series of steps that run exactly once and in exactly this order. In the structure chart the boxes simply sit on top of each other. - -Each box only starts when the box above it is finished. Nothing is skipped and nothing is repeated. - -::: diagramm l05-sequenz.visustruct -A sequence of four boxes: first `length` and `width` get their values, then the area is calculated, and finally it is output. Each box runs exactly once, from top to bottom. Output: 15. -::: - -> The sequence is the simplest of the three basic building blocks. Almost every structure chart is mostly made of sequences. - -## Order matters - -When you make tea, you notice straight away if the order is wrong. With calculations it is just the same, only it is harder to spot. - -::: diagramm l04-rechnen.visustruct -First double, then add 1: the result is 7. If you swap the two middle boxes, you get 8. -::: - -The statement `number = number * 2` means: take the value of `number`, multiply it by 2 and store the result in `number` again. So what is in `number` depends on what happened before. - -::: angeberwissen -Structure charts are not just school stuff. According to Shneiderman, companies like IBM and Siemens planned their programs with them too. So you are learning pro stuff here. Baaa-mazing! -::: - -## Exercise: Put in order - -::: übung l04-reihenfolge -titel: Put in order -auftrag: The start diagram has the right boxes, but in the wrong order. Move them so that **20** is output at the end. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Read each box and ask yourself: which variable does it need, and where does that variable get its value? -tipp: A variable has to be created before you calculate with it. The output comes right at the end. Check in your head: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Swap two values - -Imagine two glasses: one with juice, one with water. If you want to swap what is in them, you need a third, empty glass. - -It is exactly the same with variables. If you simply write `a = b` and then `b = a`, the old value of `a` is already lost. In the end both variables have the same value. - -The solution is a **temporary variable** that keeps the old value for a short time: - -::: grafik l05-tauschen.svg -Three steps with an empty glass: first the juice into temp, then the water to the left, then the juice from temp to the right. -::: - -::: diagramm l04-tauschen.visustruct -First a is kept in temp. Then a gets the value of b, and b gets the kept value from temp. -::: - -## Exercise: Swap glasses - -::: übung l04-glaeser-tauschen -titel: Swap glasses -auftrag: The left glass holds juice, the right one water. Swap the contents of the variables `left` and `right` before they are output. -Do not change the first two boxes. Insert new statements between them instead. -start: l04-tauschen-start.visustruct -test: - -> Left: water | Right: juice -braucht: anweisung -tipp: How do you swap two full glasses in real life? You need a third, empty glass. -tipp: Create a temporary variable with `String temp = left`. After that you need two more assignments. -lösung: l04-tauschen-loesung.visustruct -::: - -## Now in Java - -As in lesson 4, you can also write a sequence directly in Java. The swap from above looks like this: - -```java editor -int a = 5; -int b = 9; -int temp = a; -a = b; -b = temp; -System.out.println("a = " + a); -System.out.println("b = " + b); -``` - -- Every box becomes one line. The lines run from top to bottom, just like the boxes. -- Every statement ends with a **semicolon** `;`. -- An output is called `System.out.println(…)` in Java. Mind the capital S in `System`: Java tells upper and lower case apart. - -You type Java in the [Code](#zeige-codelive) area to the right of the structure chart. While you type, VisuStruct builds the structure chart along with it. So you see straight away which box belongs to which line. - -::: übung l05-java-kino -titel: Cinema tickets in Java -bereich: beide -code: java -auftrag: A cinema ticket costs 9 euros. Write a Java program that calculates the price of 3 tickets and outputs **Total: 27**. - “Open in editor” also opens the Code area on the right. Type your code there; the structure chart builds itself. Then click Check. -test: - -> Total: 27 -braucht: anweisung -tipp: You need three variables: the price, the number of tickets and the result. Every line ends with a semicolon. -tipp: `int price = 9;` and `int count = 3;` create the variables. `int total = price * count;` calculates, and `System.out.println("Total: " + total);` outputs the result. -lösung: l05-java-kino-loesung.visustruct -::: - -## Fill in the gaps: swapping glasses - -::: lückentext -titel: Swap two values -auftrag: The values of `a` and `b` should be swapped. Fill in the missing words. -```java -int a = 3; -int b = 8; -[[int]] temp = [[a]]; -a = [[b]]; -b = [[temp]]; -System.out.println("a = " + a + ", b = " + b); -``` -erklärung: First you save `a` in the helper variable, then `a` gets the value of `b`, and finally `b` gets the saved value. Output: **a = 8, b = 3** -::: - -## Find the bug: credit - -::: übung l05-java-fehler -titel: Fix the credit -code: java -auftrag: The canteen card holds 20 euros. A meal costs 7 euros. The program should output the credit **after** paying, so **Left: 13**. But it outputs **Left: 20**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l05-java-fehler-start.visustruct -test: - -> Left: 13 -braucht: anweisung -tipp: All lines are right, only their order is not. When is the credit output, and when is the meal paid? -tipp: Swap the last two lines: first `credit = credit - price;`, then the output. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l05-java-laeuft-nicht -titel: Share a pizza -code: java -auftrag: A pizza has 8 slices and 4 friends share it. The program should output **Everyone gets 2 slices.** But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Everyone gets 2 slices. -braucht: anweisung -tipp: Compare the name in the output letter by letter with the name created in the third line. -tipp: The variable is called `perPerson` with a capital P (camelCase). Write it exactly like that in the output. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Sort the boxes - -::: spiel reihenfolge -titel: Game: Sort the boxes -intro: Ludwig has mixed up the boxes. Put them in the right order. Four rounds. - -runde: -frage: At the end, **7** should be output. -- `int number = 3` -- `number = number * 2` -- `number = number + 1` -- `output: number` -> First double, then add 1: 3 · 2 + 1 = 7. The other way round you'd get (3 + 1) · 2 = 8. - -runde: -frage: At the end, **8** should be output. -- `int number = 3` -- `number = number + 1` -- `number = number * 2` -- `output: number` -> The same boxes, a different order, a different result: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a and b should swap their values. -- `int temp = a` -- `a = b` -- `b = temp` -- `output: a + " " + b` -> First a is saved in temp. Otherwise the old value is gone as soon as a gets the value of b. - -runde: -frage: Ludwig makes tea. -- Fill the kettle with water -- Switch the kettle on -- Wait until the water boils -- Pour the water over the tea bag -- Let the tea brew -> Every step needs the one before. Just like in a structure chart. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that a sequence runs from top to bottom. → Step by step -- I can explain why the order of the steps matters. → Order matters -- I can swap two values with a temporary variable. → Swap two values -- I can write a sequence in Java. → Now in Java -::: - -## Quiz - -::: quiz -? How often does each box of a sequence run? -→ Step by step -- [ ] Until a condition is false -- [x] Exactly once -- [ ] Not at all if it is empty -> In a sequence nothing is repeated and nothing is skipped. Loops are there for repetition. - -? At the start a = 5 and b = 9. Then `a = b` runs and after that `b = a`. What is in the variables at the end? -→ Swap two values -- [ ] a = 9 and b = 5 -- [x] a = 9 and b = 9 -- [ ] a = 5 and b = 5 -> After `a = b` the old value 5 is lost. `b = a` then only copies the 9 back. - -? Why do you need a temporary variable when swapping? -→ Swap two values -- [x] So that a value is not overwritten before it is needed -- [ ] So that the structure chart gets longer -- [ ] Because otherwise you cannot output variables - -? What does the sequence `int x = 4`, `x = x + 1`, `x = x * 3`, `output: x` output? -→ Order matters -- [ ] 13 -- [x] 15 -- [ ] 4 -> First x becomes 5, then 15. - -? What does every statement end with in Java? -→ Now in Java -- [ ] With a full stop . -- [x] With a semicolon ; -- [ ] With a colon : -> If the semicolon is missing, VisuStruct shows a hint below the code. -::: diff --git a/web/lessons/en/06-eingabe-und-ausgabe.md b/web/lessons/en/06-eingabe-und-ausgabe.md deleted file mode 100644 index 50056621..00000000 --- a/web/lessons/en/06-eingabe-und-ausgabe.md +++ /dev/null @@ -1,432 +0,0 @@ ---- -titel: Input and output -bereich: Basics -kurz: Ask for values, show results and use the IPO model. ---- - -## Skills I will gain now - -::: kompetenzen -- explain the IPO model -- write an input with a data type, a name and a question -- join text and variables in an output with + -- work out a result first and store it in a variable, then output that variable, e.g. `int sum = a + b`, then `output: sum` -- pick out input, process and output from a task -- read numbers in Java with the scanner -- write comments with `//` and `/* */` and use them to structure and explain my code -::: - -## The IPO model - -Many programs work in three parts: - -1. **Input:** The program asks for values, e.g. two numbers. -2. **Process:** It calculates with the values. -3. **Output:** It shows the result. - -The first letters give **IPO**. So far the values were fixed in the structure chart. With an input, a different value can be used every time the chart runs. - -::: diagramm l06-eva.visustruct -Input at the top, process in the middle, output at the bottom. Start the simulation and try different numbers. -::: - -## Input with input: - -An input asks for a value while the chart runs and stores it in a variable. You write the **data type**, the **name** and a **question** in quotation marks: - -``` -input: int age "How old are you?" -input: double price "Price in euros?" -input: String name "What is your name?" -``` - -In the editor you choose the kind **Input** at the top of a Statement. The word `input:` is then already in front, and you only type `int age "How old are you?"`. - -> The data type decides what may be typed in. With `int`, the simulation reports an error if someone types 2.5 or Hello. - -## Output with output: - -An output shows text and values. Text goes in quotation marks, variables do not. With `+` you **join** the parts together: - -``` -output: "Hello " + name + "!" -output: "You are " + age + " years old." -``` - -Watch out for the spaces inside the quotation marks. Without them everything sticks together, and you see `HelloMia!` instead of `Hello Mia!`. - -## Exercise: Greeting - -::: übung l06-begruessung -titel: Greeting -auftrag: Ask for a name and greet the person. When Ada is typed in, **Hello Ada!** should be output. -test: Ada -> Hello Ada! -test: Ben -> Hello Ben! -braucht: anweisung -tipp: First comes an input for the name, then an output that uses the name. -tipp: The name is text, so the input needs the type `String`. In the output you join `"Hello "`, the variable and `"!"` with `+`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Careful with + in an output - -In an output, `+` **always** joins the parts together, even with numbers. If `a` is 3 and `b` is 4, then - -``` -output: a + b -``` - -does not show 7 but **34**. So calculate in a variable first and then output the variable: - -``` -int sum = a + b -output: "Sum: " + sum -``` - -That also fits the IPO model: you calculate in the process part, not in the output. - -## From the task to the structure chart - -Before you drag the first box, read the task carefully and take it apart. This is called **problem analysis**. Three questions help you: - -1. **What is given?** These values become the **input**. Also think about the data type. -2. **What are you looking for?** That becomes the **output**. -3. **How do you get from what is given to what you are looking for?** That is the **process**, often a formula. - -An example: *A cinema ticket costs 9 euros. The program asks how many tickets someone buys and shows the total price.* - -- Given: the number of tickets → `input: int count "How many tickets?"` -- Looking for: the total price → `output: "Total price: " + total` -- Way: count times 9 → `int total = count * 9` - -Now you only have to put the three parts on top of each other in the order I, P, O. - -> Look out for signal words in the task: *asks*, *reads in* or *enters* point to an input. *Shows*, *outputs* or *reports* point to an output. - -## Exercise: Calculator - -::: übung l06-rechner -titel: Sum and product -auftrag: Ask for two whole numbers. Then output their sum and their product, each on its own line. -With 3 and 4 it looks like this: `Sum: 7` and `Product: 12`. -test: 3, 4 -> Sum: 7 | Product: 12 -test: 10, -2 -> Sum: 8 | Product: -20 -test: 0, 5 -> Sum: 5 | Product: 0 -braucht: anweisung -tipp: Plan the order: first read in both numbers, then calculate, then output. -tipp: You need two inputs, two statements for calculating and two outputs. Calculate the sum in a variable of its own, otherwise `+` joins the numbers together. -lösung: l06-rechner-loesung.visustruct -::: - -## Exercise: Mean - -::: übung l06-mittelwert -titel: Mean -auftrag: Ask for two numbers, which may also be decimal numbers. Output their mean, so for 2 and 3 that is `Mean: 2.5`. -test: 2, 3 -> Mean: 2.5 -test: 4, 6 -> Mean: 5.0 -test: 1.5, 2 -> Mean: 1.75 -braucht: anweisung -tipp: You get the mean of two numbers by adding them and halving the result. -tipp: For decimal numbers you use the type `double`. The mean is the sum divided by 2. Remember the brackets: `(x + y) / 2`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Now in Java - -This is the IPO example from the start in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("First number?"); -int a = scanner.nextInt(); -System.out.println("Second number?"); -int b = scanner.nextInt(); -int sum = a + b; -System.out.println("Sum: " + sum); -``` - -- For input, Java needs a **Scanner**. You write the first two lines once at the very top, then you can read as often as you like. -- `scanner.nextInt()` reads a whole number, `scanner.nextDouble()` a decimal number and `scanner.next()` a word. -- The question to the user is a normal output right before it. VisuStruct turns both lines together into one input box. - -::: angeberwissen -Java was originally meant to control small devices, and it still does: many SIM cards in phones and many bank cards run a mini version called **Java Card**. Quite possibly you have Java in your pocket right now. -::: - -::: übung l06-java-tage -titel: Age in days -code: java -auftrag: Write a Java program that asks for the age in years and outputs roughly how many days that is. Use 365 days per year. For 2 years it shows **That is about 730 days.** -test: 2 -> That is about 730 days. -test: 10 -> That is about 3650 days. -test: 1 -> That is about 365 days. -braucht: anweisung -tipp: First the two scanner lines, then the question and the input, then the calculation, then the output. -tipp: `int years = scanner.nextInt();` reads. `int days = years * 365;` calculates, and `System.out.println("That is about " + days + " days.");` outputs. -lösung: l06-java-tage-loesung.visustruct -::: - -## Comments in code - -You may write notes in your code. They are called **comments**. Java skips them when it runs, so the program and the structure chart stay the same. There are two kinds: - -- `//` turns the rest of the line into a comment. This is a **single-line comment**. -- `/*` starts a **multi-line comment** and `*/` ends it. Everything in between belongs to the comment, however many lines. Java does not need the stars at the start of the lines in between, they just look tidy. - -In finished programs you sometimes also see `/** … */` with two stars at the start. That is a **Javadoc comment**, from which Java can create a description of the program. For you, `//` and `/* */` are enough. - -This is the IPO example with comments: - -```java editor -/* - * Task: The program asks for two numbers - * and outputs their sum. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Input -System.out.println("First number?"); -int a = scanner.nextInt(); -System.out.println("Second number?"); -int b = scanner.nextInt(); - -// Process -int sum = a + b; // add the two numbers - -// Output -System.out.println("Sum: " + sum); // for 3 and 4 I expect: Sum: 7 - -// Next time: allow decimal numbers too -``` - -What comments are good for: - -- **The task:** the multi-line comment at the top says what the program should do. -- **Structure:** `// Input`, `// Process` and `// Output` divide the code by the IPO model. -- **Explanation:** a comment after a line says what your algorithm does there. -- **Expectation:** write down which output you expect. When you test, you compare. -- **Matching brackets:** later you will meet blocks in curly brackets `{ }`. Then, for example, `} // end for` shows which bracket closes what. -- **Keeping your thoughts:** what do you need to remember when you carry on next time? - -Click **Open in editor**, change a comment or add a new one. The structure chart stays the same, and VisuStruct keeps your comments. - -> Don't put a comment after every line. Explain what your algorithm does and why, not every single character. - -::: angeberwissen -Both kinds are much older than Java: `/* … */` comes from the language PL/I from 1964, `//` from BCPL from 1967. Java inherited them via C and C++. -::: - -## Fill in the gaps: comments - -::: lückentext -titel: Add the comments -auftrag: Add the characters so that a multi-line comment with the task is at the top and the other two notes are single-line comments. -```java -[[/*]] - * Task: The program converts euros to cents. - [[*/]] -int euros = 3; -[[//]] Process -int cents = euros * 100; -[[//]] Output, expected: 300 -System.out.println(cents); -``` -erklärung: `/*` opens the multi-line comment and `*/` closes it. `//` only lasts until the end of the line, so every note needs its own `//`. -::: - -## Fill in the gaps: input - -::: lückentext -titel: Read a name and an age -auftrag: Fill in the missing words so that the program reads a name and an age. -```java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("What is your name?"); -[[String]] name = scanner.[[next]](); -System.out.println("How old are you?"); -int age = scanner.[[nextInt]](); -System.out.println("Hello " + name + ", next year you will be " + [[(age + 1)]] + "."); -``` -erklärung: `scanner.next()` reads a word, `scanner.nextInt()` a whole number. The brackets around `age + 1` matter: without them Java first appends `age` and then `1` to the text, and 14 would become **141**. -::: - -## Find the bug: sum - -::: übung l06-java-fehler -titel: Fix the sum -code: java -auftrag: The program reads two numbers and should output their sum. For 3 and 4, however, it shows **Sum: 34** instead of **Sum: 7**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Sum: 7 -test: 10, 5 -> Sum: 15 -test: 0, 9 -> Sum: 9 -braucht: anweisung -tipp: Read the output from left to right: what is `"Sum: " + a`? And what happens when `+ b` comes after it? -tipp: Add brackets so that Java calculates first: `System.out.println("Sum: " + (a + b));` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l06-java-laeuft-nicht -titel: Age next year -code: java -auftrag: The program asks for your age and should say how old you will be next year. For 14 that is **Next year you will be 15**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> Next year you will be 15 -test: 9 -> Next year you will be 10 -braucht: anweisung -tipp: The input ends up in a variable. What exactly is it called, and what is it called in the output? -tipp: The variable is called `age` with a small a: `System.out.println("Next year you will be " + (age + 1));` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Game: What is output? - -::: spiel frage -titel: Game: What is output? -intro: Ludwig has built inputs and outputs. Predict what happens. Seven rounds. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: What is output? -- 7 -- [x] 34 -- a + b -> In an output, `+` always joins the parts together, even numbers. - -runde: - int a = 3 - int b = 4 - int sum = a + b - output: "Sum: " + sum -frage: What is output? -- [x] Sum: 7 -- Sum: 34 -- Sum: sum -> The calculating happens before, in a variable. The output only joins text and result. - -runde: - input: String name "What is your name?" - output: "Hello" + name + "!" -frage: You type Mia. What is output? -- Hello Mia! -- [x] HelloMia! -- Hello name! -> The text `"Hello"` has no space at the end. That's why everything sticks together. - -runde: - input: int age "How old are you?" -frage: You type 2.5. What happens? -- age is 2. -- age is 2.5. -- [x] The simulation reports an error. -> The data type `int` only allows whole numbers. - -runde: - input: double price "Price in euros?" -frage: Which part of IPO does this box belong to? -- [x] Input -- Process -- Output -> `input:` asks for a value. That is the input. - -runde: - double mean = (a + b) / 2.0 -frage: Which part of IPO does this box belong to? -- Input -- [x] Process -- Output -> Here something is calculated. That is the process. - -runde: - output: "Mean: " + mean -frage: Which part of IPO does this box belong to? -- Input -- Process -- [x] Output -> `output:` shows the result. That is the output. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain the IPO model. → The IPO model -- I can write an input with a data type, a name and a question. → Input with input: -- I can join text and variables in an output with +. → Careful with + in an output -- I can work out a result first and store it in a variable, then output that variable, e.g. `int sum = a + b`, then `output: sum`. → Careful with + in an output -- I can pick out input, process and output from a task. → From the task to the structure chart -- I can read numbers in Java with the scanner. → Now in Java -- I can write comments with `//` and `/* */` and use them to structure and explain my code. → Comments in code -::: - -## Quiz - -::: quiz -? What does IPO stand for? -→ The IPO model -- [x] Input, process, output -- [ ] Input, program, order -- [ ] Insert, paste, open -> Many programs first ask for values, process them and then show the result. - -? The variables a and b have the values 3 and 4. What does `output: a + b` show? -→ Careful with + in an output -- [ ] 7 -- [x] 34 -- [ ] a + b -> In an output, + joins the parts together. Calculate the sum in a variable first. - -? Which input fits if a price such as 4.99 should be typed in? -→ Input with input: -- [ ] input: int price "Price?" -- [x] input: double price "Price?" -- [ ] input: String "Price?" -> 4.99 is a decimal number, so double. Also, every input needs a variable name. - -? What does `output: "Age: " + age` show if age has the value 12? -→ Careful with + in an output -- [x] Age: 12 -- [ ] "Age: " + 12 -- [ ] Age: age - -? Task: *The program asks for the radius of a circle and shows its circumference.* Match each part of the text to where it belongs in the IPO model. -→ From the task to the structure chart -= asks for the radius -> Input -= circumference = 2 * 3.14 * radius -> Process -= shows its circumference -> Output -> The radius is given, the circumference is what you are looking for. The formula connects the two. - -? What does `scanner.nextInt()` read? -→ Now in Java -- [x] A whole number -- [ ] A decimal number -- [ ] A whole sentence -> For decimal numbers there is `scanner.nextDouble()`, for a word `scanner.next()`. - -? What does Java do with a comment? -→ Comments in code -- [ ] It outputs it as text. -- [x] It skips it. -- [ ] It reports an error. -> Comments are notes for people. The program runs exactly the same with or without them. - -? Where does a comment that starts with `/*` end? -→ Comments in code -- [x] At `*/` -- [ ] At the end of the line -- [ ] At the next empty line -> That is why a comment with `/*` can span several lines. A comment with `//`, on the other hand, ends at the end of the line. -::: diff --git a/web/lessons/en/07-simulation-und-schreibtischtest.md b/web/lessons/en/07-simulation-und-schreibtischtest.md deleted file mode 100644 index 6ef19a48..00000000 --- a/web/lessons/en/07-simulation-und-schreibtischtest.md +++ /dev/null @@ -1,364 +0,0 @@ ---- -titel: Simulation and desk check -bereich: Basics -kurz: Run a structure chart step by step, work it out by hand and find mistakes. ---- - -## Skills I will gain now - -::: kompetenzen -- start the simulation, pause it, run single steps and stop it -- read the variables, the output and the last step during the simulation -- do a desk check by hand as a table -- tell syntax errors, runtime errors and logic errors apart -- find a mistake by comparing the values with what I expect -- follow Java code with Step and breakpoints -::: - -## Start the simulation - -Before branches and loops come along, you get to know your most important tool. From now on you can use the simulation to check every exercise yourself. - -With the [▶︎ Start simulation](#zeige-simulation) button on the right of the tools, VisuStruct runs your structure chart. While the simulation is running, you cannot edit the diagram. The same button is then called **■ End simulation**; it takes you back to editing. - -On the right, the **Controls** appear: - -- **Play** runs the diagram automatically, step by step. You set how long it waits between two steps under **Pause per step**. -- **Pause** stops for a moment. Then you carry on with Play or Step. -- **Step** runs exactly one step. That way you can watch calmly. -- **Stop** cancels and resets everything to the start. - -Open the following diagram in the editor and go through it with **Step**: - -::: diagramm sim-tausch.visustruct -The values of a and b are swapped. The variable temp keeps one value meanwhile. -::: - -## What the simulation shows - -- **Highlight:** One box in the diagram is coloured. With **Last step** it is the box that has just run. With **Next step** it is the box that comes next. -- **Input:** If the diagram asks for a value, you type it in here and confirm with **OK**. -- **Last Step:** Here you see what has just happened, e.g. `a = 5 -> 5` after the box `a = b`. On the left, the variables have already been replaced by their values; to the right of the arrow is the result. -- **Variables:** All variables with their current values. -- **Output:** Everything that was output with `output:`. - -If the [Code](#zeige-codelive) area is open on the right, the [console](#zeige-konsole) runs along there as well. It is cleared at the start and then shows output and input just as they would appear in the real program. - -At the end, **Simulation finished.** appears. If something goes wrong, the simulation stops on the faulty box and shows a message, e.g. **Unknown variable: total**. - -## The desk check - -In a **desk check** you play the computer yourself. You go through the structure chart with pen and paper and write down the values of the variables after every step. It is best to make a table: one column for the step, one for each variable and one for the output. - -::: grafik l07-schreibtischtest-en.svg -A sheet of paper and a pen are all you need: after each step you write the new values in a new row. -::: - -For the diagram above, the table looks like this: - -| Step | a | b | temp | Output | -|---|---|---|---|---| -| `int a = 3` | 3 | – | – | | -| `int b = 5` | 3 | 5 | – | | -| `int temp = a` | 3 | 5 | 3 | | -| `a = b` | 5 | 5 | 3 | | -| `b = temp` | 5 | 3 | 3 | | -| `output: "a = " + a` | 5 | 3 | 3 | a = 5 | -| `output: "b = " + b` | 5 | 3 | 3 | b = 3 | - -Then compare your table with the simulation. Do the values under **Variables** match your row after every step? - -> A desk check is especially useful in tests. There you have no computer, but you can still show what your structure chart does. - -::: angeberwissen -“Computer” used to be a job: people who did calculations with pencil, paper and a calculating machine. One of the best was the mathematician Katherine Johnson at NASA. She calculated flight paths for space capsules, and later also for the Apollo 11 flight to the Moon. - -In 1962 John Glenn was to become the first American to orbit the Earth. A new IBM computer had calculated the flight path. Glenn did not fully trust the machine and said: “Get the girl to check the numbers. If she says they’re good, I’m ready to go.” Katherine Johnson checked everything by hand, a real desk check. Her numbers were right, and Glenn flew. - -Katherine Johnson was Black, and at that time laws in the US state of Virginia separated Black and white people. In the film “Hidden Figures” (2016) she has to run across the whole site every time she needs the toilet. The film exaggerates this: separate toilets really existed, but Katherine Johnson said she simply ignored the sign. - -Her colleague Dorothy Vaughan saw early on that the IBM computers would take over the calculating work. She taught herself and her whole team the programming language FORTRAN. That is how human computers became programmers. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), mathematician at NASA (photo: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), public domain). -::: - -## Three kinds of errors - -Errors are part of programming, even for professionals. There are three kinds, and you find each of them in a different way: - -- **Syntax error**: The text breaks the rules of the language, e.g. a `;` is missing at the end. The program does not start at all. -- **Runtime error**: The program starts but stops at one point, e.g. when it divides by 0. -- **Logic error**: The program runs to the end, but the result is wrong, e.g. because brackets are missing: `a + b / 2` instead of `(a + b) / 2`. - -This is how VisuStruct shows you the errors: - -- **Syntax error:** They happen less often in a structure chart, because the boxes give you the shape. In the [Code](#zeige-codelive) area the spot is underlined in red, the line number turns red, and you read e.g. **Line 2: Still missing here: ;**. -- **Runtime error:** The simulation stops on the box and reports e.g. **Division by zero.** In the Code area the line number turns red, and the [console](#zeige-konsole) names the line: **Line 3: Division by zero.** -- **Logic error:** Nobody reports anything here. The computer does not know what you actually wanted. Only you know the right result, so the desk check helps here. - -> A red line number shows an error. The round red dot in front of the line number is something else: a breakpoint. You will meet it below in “Now in Java”. - -## Find mistakes - -If a structure chart outputs something wrong, that is a logic error. Then do this: - -1. **Write down what you expect:** What should come out for a particular input? -2. **Run it step by step:** Start the simulation with **Step** and watch the variables. -3. **Find the spot:** Where does a value first differ from what you expected? That is exactly where the mistake is. - -Common mistakes in a sequence are: - -- The **order** is wrong: a variable is used before it has a value. -- A value is **overwritten** before it is needed. -- **Brackets** are missing: `a + b / 2` is not the same as `(a + b) / 2`. -- Two whole numbers are divided: `7 / 2` gives `3`, because with `int` everything after the decimal point is dropped. - -> Later, branches and loops come along. Then the simulation also shows you which path is taken and how often a loop runs. - -::: angeberwissen -On 9 September 1947 Grace Hopper’s team found a real moth stuck between two contacts in the Harvard Mark II computer. They taped it into the logbook and wrote: “First actual case of bug being found.” Today the moth is in a museum in Washington. - -Grace Hopper was one of the first programmers in the world. In 1952 she built one of the first **compilers**: a program that translates code into machine code. She believed people should be able to give computers instructions in almost normal English. This led to the programming language COBOL, which many banks still use today. She even became an admiral in the US Navy. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), computer scientist and admiral (photo: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), public domain). -::: - -## Exercise: Fix the swap - -::: übung sim-tausch -titel: Fix the swap -auftrag: The structure chart should read in two numbers and output them swapped. So with 3 and 5, first **5** and then **3** should appear. But it outputs 5 twice. - Do a desk check or use **Step** and fix the mistake. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Go through the diagram with **Step** and write down the values of a and b after every step. Where does the 3 disappear? -tipp: Look at a after the step `a = b`. Where did the old value of a go? Keep it in a temporary variable first, as in the example above. -lösung: sim-tausch-loesung.visustruct -::: - -## Exercise: Fix the cinema tickets - -::: übung sim-kino -titel: Fix the cinema tickets -auftrag: A cinema ticket costs 8.50 euros. The structure chart should ask for the number of tickets and output the total price, so for 2 tickets `Total: 17.0`. - Start the simulation. It stops with an error message. Find out why and fix the structure chart. -start: sim-kino-start.visustruct -test: 2 -> Total: 17.0 -test: 3 -> Total: 25.5 -test: 0 -> Total: 0.0 -braucht: anweisung -tipp: Read the error message of the simulation carefully. Which variable does it not know yet? -tipp: The simulation runs the boxes from top to bottom. Does total already have a value when the output comes? You can move boxes with the mouse. -lösung: sim-kino-loesung.visustruct -::: - -> If a simulation keeps running forever, click **Stop**. When you **Check** an exercise, VisuStruct stops by itself after a very large number of steps. - -## Now in Java - -You can run Java code step by step too. In the [Code](#zeige-codelive) area the tool for this is called the **debugger**: - -- Click a line number on the left. A red dot appears: that is a **breakpoint**. [▶︎ Run](#zeige-ausfuehren) pauses before this line. -- The [Step](#zeige-einzelschritt) button runs exactly one line, like **Step** in the simulation. -- Below the code you see the current value of every variable. That is your desk check, except that the computer writes it down for you. - -::: übung l07-java-pizza -titel: Fix the pizza order -code: java -auftrag: A pizza costs 8 euros, extra cheese 2 euros. For 3 pizzas with extra cheese the program should output **Total: 30**. But it outputs 14. - Go through the Java code with Step and watch `total`. Find the mistake and fix it in the code. -start: l07-java-pizza-start.visustruct -test: - -> Total: 30 -braucht: anweisung -tipp: What value is in `total` after the fourth line? Work out in your head what Java calculates first. -tipp: As in maths, multiplication comes before addition: `price + cheese * count` is `8 + 6`. Add brackets: `(price + cheese) * count`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Fill in the gaps: desk check - -::: lückentext -titel: What is in the variables? -auftrag: Do a desk check in your head. After each line, write down the value the variable has afterwards, and in the last line what is output. -```java -int a = 5; -int b = 2; -a = a + b; // a is now [[7]] -b = a * 2; // b is now [[14]] -a = b - a; // a is now [[7]] -System.out.println(a + b); // output: [[21]] -``` -erklärung: Each line calculates with the values that are in the variables right now: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, output 7 + 14 = 21. You can check it with Step in the Code area. -::: - -## Find the bug: score - -::: übung l07-java-fehler -titel: Fix the score -code: java -auftrag: In a game Ludwig first gets 10 points, then 5 points, then he loses 3 points. At the end **Points: 12** should appear. But **Points: 3** appears. - Go through the code with Step and watch `points`. Fix the wrong line and click Check. -start: l07-java-fehler-start.visustruct -test: - -> Points: 12 -braucht: anweisung -tipp: After which line does `points` suddenly have the value 3? What should actually happen in that line? -tipp: Ludwig loses 3 points, so 3 is subtracted from the old value: `points = points - 3;` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l07-java-laeuft-nicht -titel: Fix the bonus -code: java -auftrag: Ludwig has 10 points and gets 5 bonus points. At the end **Points: 15** should appear. But the code doesn’t run. Also step through it with Single step: in which line does it stop? - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Points: 15 -braucht: anweisung -tipp: Java runs the code from top to bottom. Does `bonus` already exist when it is used in the calculation? -tipp: Create `bonus` before the calculation: first `int bonus = 5;`, then `points = points + bonus;` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Do the desk check in your head and tap the wrong line before time runs out. The programs are in Java, but every line is a box like in the structure chart. Six rounds, 30 seconds each. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int temp = a; - a = b; - b = a; -frage: Should swap the values of a and b. -fehler: 5 -besser: b = temp; -> a already holds the value of b. The old value of a is in temp. - -runde: - int a = 4; - int b = 6; - double mean = a + b / 2; - System.out.println(mean); -frage: Should output the mean 5.0. -fehler: 3 -besser: double mean = (a + b) / 2.0; -> Without brackets the division comes first: 4 + 6 / 2 = 7. - -runde: - int tickets = 3; - double price = 8.5; - double total = tickets + price; - System.out.println("Total: " + total); -frage: Should output the price for 3 cinema tickets at €8.50 each. -fehler: 3 -besser: double total = tickets * price; -> For the total price you multiply, you don't add. - -runde: - int minutes = 135; - int hours = minutes / 60; - int rest = minutes / 60; - System.out.println(hours + " h " + rest + " min"); -frage: Should output 2 h 15 min. -fehler: 3 -besser: int rest = minutes % 60; -> `%` gives the remainder of a division. With `/` rest would hold a 2 as well. - -runde: - String name = "Ada"; - System.out.println("Hello " + "name" + "!"); -frage: Should output "Hello Ada!". -fehler: 2 -besser: System.out.println("Hello " + name + "!"); -> In quotation marks, name is just a text. The variable has no quotation marks. - -runde: - double price = 100; - price = price * 0.1; - System.out.println(price); -frage: Should raise the price by 10 %, so output 110.0. -fehler: 2 -besser: price = price + price * 0.1; -> Times 0.1 only gives the 10 %, so 10.0. That has to be added to the old price. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can start the simulation, pause it, run single steps and stop it. → Start the simulation -- I can read the variables, the output and the last step during the simulation. → What the simulation shows -- I can do a desk check by hand as a table. → The desk check -- I can tell syntax errors, runtime errors and logic errors apart. → Three kinds of errors -- I can find a mistake by comparing the values with what I expect. → Find mistakes -- I can follow Java code with Step and breakpoints. → Now in Java -::: - -## Quiz - -::: quiz -? Which button runs exactly one step? -→ Start the simulation -- [ ] Play -- [x] Step -- [ ] Stop -> Play keeps going automatically, Step runs only one step. - -? What do you write down after every step in a desk check? -→ The desk check -- [ ] Only the output at the end -- [x] The current values of all variables -- [ ] The generated Java code -> That way you see exactly where a value is different from what you expected. - -? First `int a = 3`, then `int b = 5`, then `a = b`. What values do a and b have afterwards? -→ The desk check -- [ ] a = 5 and b = 3 -- [x] a = 5 and b = 5 -- [ ] a = 3 and b = 5 -> The old value of a has been overwritten. That is why you need a temporary variable when swapping. - -? The Last Step field shows `price = 4 * 2.5 -> 10.0`. What does that mean? -→ What the simulation shows -- [ ] The variable price now has the value 4. -- [x] 4 * 2.5 was worked out and the result 10.0 was stored in price. -- [ ] The simulation has found a mistake. - -? The simulation reports **Unknown variable: sum**. What is a likely reason? -→ Find mistakes -- [x] sum is used before it has been given a value. -- [ ] sum is too big. -- [ ] The structure chart has too many boxes. - -? What does a red dot in front of a line number in the Code area do? -→ Now in Java -- [ ] It marks an error. -- [x] The program pauses before this line. -- [ ] The line is skipped. -> That is a breakpoint. ▶︎ or Step carries on. -? Match each error to its kind. -→ Three kinds of errors -= The semicolon at the end of the line is missing. -> Syntax error -= The program divides by 0 and stops. -> Runtime error -= The average is too big because brackets are missing. -> Logic error - -? The program runs to the end without any message but prints 12 instead of 21. What kind of error is that? -→ Three kinds of errors -- [ ] Syntax error -- [ ] Runtime error -- [x] Logic error -> There is no message, but the result is still wrong. You only find an error like this with your expectation and a desk check. -::: diff --git a/web/lessons/en/08-verzweigung.md b/web/lessons/en/08-verzweigung.md deleted file mode 100644 index 2e92d4b7..00000000 --- a/web/lessons/en/08-verzweigung.md +++ /dev/null @@ -1,368 +0,0 @@ ---- -titel: Branch -bereich: Basics -kurz: Use a condition to decide which of two paths runs. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that exactly one path of a branch runs -- use the comparison operators == != < <= > >= -- tell == and = apart -- build a branch with an empty path -- write a branch with if and else in Java -::: - -## If, then, else - -So far every box always ran. But often a program should **decide**: if a condition is met, do one thing, otherwise do the other. - -::: diagramm l07-alter-pruefen.visustruct -Depending on the age typed in, one message or the other appears. Try 17 and 18 in the simulation. -::: - -That is what the **branch** is for. The condition sits in the triangle at the top. Below it, the structure chart splits into two paths: - -- **true** (left): runs if the condition is true -- **false** (right): runs if the condition is false - -Always **exactly one** of the two paths runs. After that, the program continues below the branch. - -## Insert a branch - -Drag the **Branch** from the Elements area into the structure chart. Click the triangle and type the condition in the Object Inspector, e.g. `age >= 18`. The word `if` is already in front. - -Each path starts with a placeholder ø. Drag statements into it. A path may also hold several boxes on top of each other. - -## Comparison operators - -A condition is usually a **comparison**. Its result is always `true` or `false`. - -- `==` equal, e.g. `number == 0` -- `!=` not equal, e.g. `answer != 42` -- `<` less than, `<=` less than or equal -- `>` greater than, `>=` greater than or equal - -> Do not mix up `==` and `=`. A single `=` is an assignment and stores a value. To compare, you need two equals signs. - -With `%` you check whether a number can be divided by another: `number % 2 == 0` is true if `number` is even. - -## Exercise: Even or odd - -::: übung l07-gerade -titel: Even or odd -auftrag: Ask for a whole number. Output **even** if it is even, otherwise **odd**. -test: 4 -> even -test: 7 -> odd -test: 0 -> even -braucht: verzweigung -tipp: There are two possibilities here, so you need a branch. How do you recognise an even number? -tipp: A number is even if there is no remainder when you divide it by 2. The condition is `number % 2 == 0`. -lösung: l07-gerade-loesung.visustruct -::: - -## Exercise: The larger number - -::: übung l07-maximum -titel: The larger number -auftrag: Ask for two whole numbers and output only the larger one. If both are the same, output that number. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: A branch compares the two numbers. Each path holds exactly one output. -tipp: Compare with `a > b`. In the true path you output `a`, in the false path `b`. -lösung: l07-maximum-loesung.visustruct -::: - -## An empty path - -Sometimes something should only happen if the condition is true, and otherwise nothing at all. Then the false path stays **empty**. In the structure chart it shows the placeholder **ø**. - -::: diagramm l07-rabatt.visustruct -From 50 euros on, you get 5 euros off. Otherwise nothing happens; the false path is empty. The output below runs in both cases. -::: - -::: angeberwissen -A single forgotten branch caused a stir across half the internet in 2014: the security hole **Heartbleed** in OpenSSL, a program that many websites use for encryption. A computer could tell the server: “Send me back the word *hat*, it has 500 letters.” The server did not check whether 500 matched the real length of 3 and simply sent back 500 characters from its memory. Sometimes they contained other people's passwords. All that was missing was one if: if the stated length is greater than the real one, then send nothing back. By the way, this had nothing to do with brackets. Baah, check your inputs before you believe them. -::: - -## Exercise: Absolute value - -::: übung l07-betrag -titel: Absolute value of a number -auftrag: Ask for a whole number. If it is negative, make it positive. Then output the number. So -5 becomes 5, and 7 stays 7. -Use a branch with an empty path and only **one** output below the branch. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Only negative numbers need to be changed. What happens to a number if you multiply it by -1? -tipp: The condition is `number < 0`. In the true path you flip the sign, e.g. with `number = 0 - number`. The false path stays empty. -lösung: l07-betrag-loesung.visustruct -::: - -## Now in Java - -This is the age check from the start of this lesson in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("How old are you?"); -int age = scanner.nextInt(); -if (age >= 18) { - System.out.println("You are an adult."); -} else { - System.out.println("You are not an adult yet."); -} -``` - -- The condition comes after `if` in **round brackets** `( )`. -- The true branch is in **curly brackets** `{ }`, the false branch follows after `else` in its own curly brackets. There is no semicolon after a curly bracket. -- If the false branch is empty, simply leave out `else { }`. -- Your program needs the first two lines once as soon as it reads input. After that, `scanner.nextInt()` reads a whole number. The question before it is a normal output. - -::: angeberwissen -Java has a mascot too: **Duke**, a little black-and-white fellow with a big red nose. He was drawn by the Java team in 1992 and has been waving from conferences and T-shirts ever since. Ludwig thinks a billy goat would have been a better fit. -::: - -::: übung l08-java-bestanden -titel: Passed in Java -code: java -auftrag: Write a Java program that asks for the number of points. From 50 points on it outputs **passed**, otherwise **not passed**. - Type the code in the Code area and then click Check. -test: 50 -> passed -test: 49 -> not passed -test: 100 -> passed -test: 0 -> not passed -braucht: verzweigung -tipp: Start with the two lines for the scanner. Then ask for the points, read them and compare them with `if`. -tipp: The condition is `points >= 50`. One branch has `System.out.println("passed");`, and after `else` comes `System.out.println("not passed");`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Fill in the gaps: even or odd - -::: lückentext -titel: Even or odd -auftrag: Fill in the missing words and symbols. The program should output whether a number is even. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which number?"); -int number = scanner.nextInt(); -[[if]] (number [[%]] 2 [[==]] 0) [[{]] - System.out.println("even"); -[[}]] [[else]] { - System.out.println("odd"); -} -``` -erklärung: `number % 2` is the remainder when dividing by 2. If it is 0, the number is even. To compare you need `==`, a single `=` is an assignment. Both branches are in curly brackets. -::: - -::: angeberwissen -If a branch holds only a single statement, Java even lets you leave out the curly brackets. We never do that, and here is why: - -```java -if (age >= 18) - System.out.println("You are an adult."); - System.out.println("You may watch the film."); -``` - -Without brackets only the first line belongs to the if. The second one always runs, even for twelve-year-olds. The indentation is misleading, because Java ignores spaces. - -Exactly this kind of mistake was hidden in iPhones and Macs in 2014, known as **“goto fail”**. In the language C, the line `goto fail;` accidentally appeared twice under an if without brackets. The second one always ran and skipped the check of the security certificates. On an open Wi-Fi network, an attacker could have pretended to be your bank, for example. Baah, brackets cost two characters, a security hole costs much more. -::: - -## Find the bug: admission - -::: übung l08-java-fehler -titel: Fix the admission -code: java -auftrag: At the zoo, children **under** 6 get in for free, everyone else pays 4 euros. But the program also lets six-year-olds in for free. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l08-java-fehler-start.visustruct -test: 5 -> Free entry -test: 6 -> 4 euros -test: 10 -> 4 euros -test: 0 -> Free entry -braucht: verzweigung -tipp: Try the program with 6. Which comparison operator still lets 6 through? -tipp: “Under 6” means `age < 6`. `<=` means “less than or equal to”. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l08-java-laeuft-nicht -titel: Rated 16 -code: java -auftrag: The film is rated 16. Ludwig is 15, so the program should output **No entry**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> No entry -braucht: verzweigung -tipp: Look closely at the condition in the brackets after `if`. Does this variable exist? -tipp: The variable is called `age` with a small a: `if (age >= 16) {` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which way? - -::: spiel frage -titel: Game: Which way? -intro: Ludwig has written branches in Java. Work out the condition in your head and predict what is output. Seven rounds. - -runde: - int x = 5; - if (x > 5) { - System.out.println("big"); - } else { - System.out.println("small"); - } -frage: What is output? -- big -- [x] small -- nothing -> 5 > 5 is false, because 5 is not greater than 5. So the false branch. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("big"); - } else { - System.out.println("small"); - } -frage: What is output? -- [x] big -- small -- big and small -> `>=` means "greater than or equal". 5 >= 5 is true. - -runde: - int number = 7; - if (number % 2 == 0) { - System.out.println("even"); - } else { - System.out.println("odd"); - } -frage: What is output? -- even -- [x] odd -- 1 -> 7 % 2 is the remainder 1, so not 0. The number is odd. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: What is output? -- -4 -- [x] 4 -- nothing -> -4 < 0 is true, so the sign is turned around. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: What is output? -- -4 -- [x] 4 -- nothing -> 4 < 0 is false. The false branch is empty, the program simply carries on below. - -runde: - int grade = 3; - if (grade != 3) { - System.out.println("different"); - } else { - System.out.println("three"); - } -frage: What is output? -- different -- [x] three -- nothing -> `!=` means "not equal". 3 != 3 is false. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: What is output? -- 8 -- [x] 12 -- 20 -> The program outputs the bigger of the two numbers. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that exactly one path of a branch runs. → If, then, else -- I can use the comparison operators == != < <= > >=. → Comparison operators -- I can tell == and = apart. → Comparison operators -- I can build a branch with an empty path. → An empty path -- I can write a branch with if and else in Java. → Now in Java -::: - -## Quiz - -::: quiz -? Which path runs if the condition is false? -→ If, then, else -- [ ] The true path -- [x] The false path -- [ ] Both paths -- [ ] Neither, the program ends -> With false, the right path runs. After that, the program continues below the branch. - -? Which condition checks whether age is exactly 18? -→ Comparison operators -- [ ] age = 18 -- [x] age == 18 -- [ ] age >= 18 -> A single = is an assignment. To compare, you need ==. - -? What does ø in a path mean? -→ An empty path -- [x] Nothing happens in this case. -- [ ] The program ends here. -- [ ] The condition is false. - -? The condition is `points >= 50`. For which value is it true? -→ Comparison operators -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= means greater than or equal. So 50 is included. - -? In textbooks the branch often has another name. Which one? -→ If, then, else -- [ ] Multiple selection -- [x] Conditional statement -- [ ] Iteration -> The statements in a path only run under a condition. That is why it is also called a conditional statement. - -? Where does the condition of a branch go in Java? -→ Now in Java -- [x] In round brackets right after if -- [ ] In curly brackets after else -- [ ] After a semicolon -> `if (age >= 18) { … }`: the round brackets belong to the condition, the curly ones to the branch. -::: diff --git a/web/lessons/en/09-bedingungen-verknuepfen.md b/web/lessons/en/09-bedingungen-verknuepfen.md deleted file mode 100644 index 07be3712..00000000 --- a/web/lessons/en/09-bedingungen-verknuepfen.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -titel: Combining conditions -bereich: Basics -kurz: Join several conditions with and, or and not, and check ranges of values. ---- - -## Skills I will gain now - -::: kompetenzen -- explain when a combination with && is true -- explain when a combination with || is true -- turn a condition round with ! -- check whether a value is in a range such as 1 to 6 -- read the truth table for &&, || and ! -- store the result of a condition in a boolean variable -- evaluate combined conditions confidently -- combine conditions in Java with && and || -::: - -## And: && - -Sometimes a single comparison is not enough. For example, you only get a youth ticket if you are **at least 12 and younger than 18**. - -With `&&` (and) you join two conditions. The result is only `true` if **both** conditions are true. - -::: diagramm l08-jugendticket.visustruct -Both comparisons must be right. Try 11, 12, 17 and 18 in the simulation. -::: - -## Or: || - -With `||` (or) the result is `true` if **at least one** of the conditions is true. Both may be true as well. - -``` -day == 6 || day == 7 -``` - -This condition is true if `day` has the value 6 or the value 7, so on Saturday or Sunday. - -> On many keyboards you type the vertical bar `|` with Shift and the backslash key. On a German keyboard it is AltGr and `<`, on a Mac Option and 7. - -## Exercise: Weekend - -::: übung l08-wochenende -titel: Weekend -auftrag: Ask for a day of the week as a number (1 is Monday, 7 is Sunday). Output **Weekend** if it is Saturday or Sunday, otherwise **Weekday**. -test: 6 -> Weekend -test: 7 -> Weekend -test: 1 -> Weekday -test: 5 -> Weekday -braucht: verzweigung -tipp: It is the weekend if the day is 6 **or** 7. For that you need two comparisons in one condition. -tipp: Saturday is 6, Sunday is 7. Join the two comparisons with `||`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Check ranges of values - -In German schools, grades go from 1 (best) to 6. You check whether a grade is **in the range** with two comparisons and `&&`: - -``` -grade >= 1 && grade <= 6 -``` - -::: grafik l08-zahlenstrahl.svg -The green range from 1 to 6 is valid; both ends are included. -::: - -The other way round, a grade is **outside** the range if it is too small **or** too big: - -::: diagramm l08-note.visustruct -There is no grade smaller than 1 or bigger than 6. -::: - -> The way you write it in maths, `1 <= grade <= 6`, does not work. You have to split it into two comparisons and join them with `&&`. - -## Exercise: Valid grade - -::: übung l08-note-gueltig -titel: Valid grade -auftrag: Ask for a grade as a whole number. Output **valid** if it is between 1 and 6 (1 and 6 are included), otherwise **invalid**. -test: 1 -> valid -test: 6 -> valid -test: 3 -> valid -test: 0 -> invalid -test: 7 -> invalid -braucht: verzweigung -tipp: A valid grade has to meet two things at once: not too small **and** not too big. -tipp: The grade must be greater than or equal to 1 and at the same time less than or equal to 6. -lösung: l08-gueltig-loesung.visustruct -::: - -## Not and brackets - -With `!` (not) you turn a condition round: `true` becomes `false` and the other way round. - -``` -!(grade >= 1 && grade <= 6) -``` - -This is true if the grade is **not** in the range, and it means the same as `grade < 1 || grade > 6`. - -If `&&` and `||` appear together, `&&` is worked out first, just like multiplication before addition. It is better to use **brackets**; then it is clear what belongs together: - -``` -(age >= 12 && age < 18) || student == 1 -``` - -## The truth table - -A **truth table** shows at a glance what `&&`, `||` and `!` produce. On the left are all four possibilities for two conditions `a` and `b`, on the right the result: - -::: grafik l09-wahrheitstabelle.svg -Green is true, red is false. With && there is only one green row, with || only one red one. -::: - -Rules of thumb: - -- `a && b` is only true if **both** are true. -- `a || b` is only false if **both** are false. -- `!a` flips the value. - -::: angeberwissen -The data type `boolean` is named after **George Boole**. As early as 1847 this Englishman showed that you can calculate with true and false just like with numbers, almost a hundred years before the first computer existed. Today his logic is in every chip. -::: - -## Truth values in variables - -The result of a condition is a **truth value** of type `boolean`. You can store it in a variable just like a number and use it later: - -``` -boolean weekend = day == 6 || day == 7 -``` - -The branch then only says `weekend`. That reads almost like a sentence: "If weekend, then …". Don't write `weekend == true`, because `weekend` already is true or false. - -With `!weekend` you ask for the opposite, a weekday. - -::: ludwig -Long conditions become clearer when you put parts of them into boolean variables with meaningful names, for example `boolean teenager = age >= 12 && age < 18`. -::: - -## Game: True or false? - -Now it's your turn. Work out each condition in your head. After every answer you see how VisuStruct evaluates it step by step. - -::: spiel -age: 8..20 -day: 1..7 -grade: 0..7 -student: boolean -weekend: boolean -? age >= 18 -? day == 6 -? grade != 3 -? age >= 12 && age < 18 -? day == 6 || day == 7 -? student && weekend -? !student -? grade >= 1 && grade <= 6 -? !(age < 18) -? student || age >= 18 -? (age >= 12 && age < 18) || student -? !(grade >= 1 && grade <= 6) -? !weekend && day != 3 -? age >= 12 && age < 18 && !student -::: - -## Exercise: Leap year - -::: übung l08-schaltjahr -titel: Leap year -auftrag: A year is a leap year if it can be divided by 4 but not by 100. Years that can be divided by 400 are leap years anyway. -Ask for a year and output **Leap year** or **Not a leap year**. -test: 2024 -> Leap year -test: 2023 -> Not a leap year -test: 1900 -> Not a leap year -test: 2000 -> Leap year -braucht: verzweigung -tipp: Check the rule with examples first: 2024 is a leap year, 1900 is not, 2000 is. “Can be divided” means the remainder is 0. -tipp: Divisible by 4 means `year % 4 == 0`, not divisible by 100 means `year % 100 != 0`. Join these two with `&&`, put brackets around them and add the case for 400 with `||`. -tipp: Almost the solution: one single branch with the condition `(year % 4 == 0 && year % 100 != 0) || year % 400 == 0`. The true branch has `output: "Leap year"`, the false branch `output: "Not a leap year"`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Now in Java - -The youth ticket from the start looks like this in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("How old are you?"); -int age = scanner.nextInt(); -if (age >= 12 && age < 18) { - System.out.println("Youth ticket"); -} else { - System.out.println("No youth ticket"); -} -``` - -The symbols `&&`, `||` and `!` are exactly the ones you already use in the structure chart. They come from Java. As always, the whole condition is in the round brackets after `if`. - -::: übung l09-java-schule -titel: School or free time -code: java -auftrag: Write a Java program that asks for the hour (0 to 23). From 8 up to and including 15 it outputs **School**, otherwise **Free time**. -test: 8 -> School -test: 15 -> School -test: 16 -> Free time -test: 7 -> Free time -braucht: verzweigung -tipp: The hour has to meet two conditions at the same time. Which operator means “and”? -tipp: `if (hour >= 8 && hour <= 15) { … } else { … }` -lösung: l09-java-schule-loesung.visustruct -::: - -## Fill in the gaps: conditions - -::: lückentext -titel: Grade and weekend -auftrag: Fill in the missing operators and words. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which grade?"); -int grade = scanner.nextInt(); -if (grade >= 1 [[&&]] grade [[<=]] 6) { - System.out.println("valid grade"); -} else { - System.out.println("invalid grade"); -} -System.out.println("Which day (1 to 7)?"); -int day = scanner.nextInt(); -[[boolean]] weekend = day == 6 [[||]] day == 7; -if ([[!]]weekend) { - System.out.println("school day"); -} -``` -erklärung: A grade is valid when **both** conditions are true, so `&&`. It is the weekend when **one** of the two is true, so `||`. The result fits into a `boolean` variable, and `!` turns it around. -::: - -## Find the bug: no school - -::: übung l09-java-fehler -titel: Fix the day off -code: java -auftrag: The program should output **no school** for day 6 (Saturday) and day 7 (Sunday), otherwise **school**. But it always outputs **school**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l09-java-fehler-start.visustruct -test: 6 -> no school -test: 7 -> no school -test: 3 -> school -test: 1 -> school -braucht: verzweigung -tipp: Can `day` be 6 **and** 7 at the same time? -tipp: It is enough if one of the two conditions is true: `day == 6 || day == 7`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l09-java-laeuft-nicht -titel: Fix the weekend -code: java -auftrag: Day 6 is Saturday, day 7 is Sunday. Today is day 7, so **no school** should appear. But the program doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> no school -braucht: verzweigung -tipp: The condition has two parts. Compare the variable name in both parts. -tipp: In the second part the variable is also called `day` with a small d: `if (day == 6 || day == 7) {` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain when a combination with && is true. → And: && -- I can explain when a combination with || is true. → Or: || -- I can turn a condition round with !. → Not and brackets -- I can check whether a value is in a range such as 1 to 6. → Check ranges of values -- I can read the truth table for &&, || and !. → The truth table -- I can store the result of a condition in a boolean variable. → Truth values in variables -- I can evaluate combined conditions confidently (at least 8 of 10 right in the game). → Game: True or false? -- I can combine conditions in Java with && and ||. → Now in Java -::: - -## Quiz - -::: quiz -? When is `a && b` true? -→ And: && -- [ ] If a or b is true -- [x] Only if a and b are both true -- [ ] If exactly one of them is true -> With && both parts must be right. If one is false, the whole thing is false. - -? The variable day has the value 6. What does `day == 6 || day == 7` give? -→ Or: || -- [x] true -- [ ] false -> With || it is enough if one side is true. - -? Which condition checks whether number is between 10 and 20 (both included)? -→ Check ranges of values -- [ ] 10 <= number <= 20 -- [ ] number >= 10 || number <= 20 -- [x] number >= 10 && number <= 20 -> With || every number would be allowed, because every number is greater than 10 or less than 20. - -? What does `!(5 > 3)` give? -→ Not and brackets -- [ ] true -- [x] false -> 5 > 3 is true, and ! turns that round. - -? How do you write “age is at least 12 and less than 18” in Java? -→ Now in Java -- [ ] if (12 <= age < 18) -- [x] if (age >= 12 && age < 18) -- [ ] if (age >= 12 || age < 18) -> Each comparison stands on its own, joined with `&&`. -::: diff --git a/web/lessons/en/10-fallauswahl.md b/web/lessons/en/10-fallauswahl.md deleted file mode 100644 index 7ce8219e..00000000 --- a/web/lessons/en/10-fallauswahl.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -titel: Switch -bereich: Basics -kurz: Run one of many cases, depending on the value of a variable. ---- - -## Skills I will gain now - -::: kompetenzen -- insert a switch, add cases and change case names -- explain when the case default runs -- decide whether a switch or a branch fits better -- write a case selection in Java with switch -::: - -## Many possibilities, one variable - -A drinks machine has three buttons: 1 for water, 2 for juice, 3 for cocoa. With branches you need three boxes nested inside each other. That quickly gets wide and confusing. - -::: diagramm l09-verschachtelt.visustruct -The machine with three nested branches. Each branch checks the same variable choice again. -::: - -Whenever you compare **one variable** with **several fixed values**, there is an easier way: the **switch**. - -## How a switch is built - -At the top of the box is the variable that decides. Below it there is a column of its own for every case. Above each column is the value it belongs to. - -::: diagramm l09-automat.visustruct -The same machine as a switch. If choice has the value 2, only column 2 runs. -::: - -- The simulation looks for the case whose name matches the value exactly. -- If no case matches, **default** runs. This case is always on the far right. -- After that, the program continues below the switch. Only **one** case ever runs. - -> A new switch has the cases 1, 2 and default. Click a case to change its **Case name** in the Object Inspector. With **Add Case** you get a new column in front of default. - -## Cases with text - -The cases do not have to be numbers. You can also choose by the content of a `String` variable. Then you write the text in quotation marks as the case name, for example `"red"`. Just like in code, this shows that it is a text. - -::: diagramm l09-ampel.visustruct -A traffic light: depending on the colour typed in, a different message appears. In the simulation, also try a colour that does not exist. -::: - -Upper and lower case matter: `Red` does not match the case `"red"`. - -## Exercise: Grade in words - -::: übung l09-note -titel: Grade in words -auftrag: Ask for a grade as a whole number and output it in words: 1 very good, 2 good, 3 satisfactory, 4 sufficient, 5 poor, 6 fail. - For any other number, **Not a valid grade** should appear. -test: 1 -> very good -test: 4 -> sufficient -test: 6 -> fail -test: 9 -> Not a valid grade -braucht: fallauswahl -tipp: For many fixed values, a switch is clearer than lots of branches. Every grade gets a case of its own. -tipp: Keep using “Add Case” until you have the cases 1 to 6 and default. At the top of the switch you write `grade`. -lösung: l09-note-loesung.visustruct -::: - -## Exercise: Calculator - -::: übung l09-rechner -titel: Calculator -auftrag: Ask, one after another, for a whole number `a`, an operator as a `String` and a second whole number `b`. - For `+`, `-` and `*` you output the result. For any other sign, **Unknown operator** appears. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Unknown operator -braucht: fallauswahl -tipp: First read in all three values. After that, a switch on the operator decides what is calculated. -tipp: Before the switch, create `int result = 0`. In each case you calculate, for example, `result = a + b` and then output `result`. The case names are `"+"`, `"-"` and `"*"` in quotation marks, because the operator is a `String`. -tipp: The outline from top to bottom: three inputs (`int a`, `String operator`, `int b`), then `int result = 0`, then a switch over `operator`. Case `"+"` holds `result = a + b` and `output: result`, cases `"-"` and `"*"` the same. The default case holds `output: "Unknown operator"`. -lösung: l09-rechner-loesung.visustruct -::: - -## Switch or branch? - -The switch only checks whether a value is **exactly equal** to a case name. So: - -- **Switch:** one variable, several fixed values. Examples: choosing from a menu, day of the week as a number, grades. -- **Branch:** ranges and combined conditions. Examples: `age >= 18`, `temperature < 0 || temperature > 30`. - -So for “age 18 or older” you still need the branch. You cannot create a case of its own for every possible age. - -## Now in Java - -The drinks machine looks like this in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 water, 2 juice, 3 cocoa"); -int choice = scanner.nextInt(); -switch (choice) { - case 1 -> { - System.out.println("Here is your water."); - } - case 2 -> { - System.out.println("Here is your juice."); - } - case 3 -> { - System.out.println("Here is your cocoa."); - } - default -> { - System.out.println("This button does not exist."); - } -} -``` - -- After `switch`, the variable being compared is in round brackets. -- Every case starts with `case`, the value and an arrow `->`. After it, curly brackets hold what should happen. -- `default` is the otherwise case. It runs when no value matches. - -::: angeberwissen -The arrow `case 1 ->` has only existed in Java since 2020 (Java 14). Before that you wrote `case 1:` and had to end every case with `break;`. If you forgot it, the program simply ran on into the next case. So the arrow has saved a lot of programmers a lot of trouble. -::: - -::: übung l10-java-wuerfel -titel: Dice in words -code: java -auftrag: Write a Java program with `switch` that reads a number. For 1 it outputs **one**, for 2 **two**, for 3 **three** and otherwise **unknown**. -test: 1 -> one -test: 2 -> two -test: 3 -> three -test: 7 -> unknown -braucht: fallauswahl -tipp: You need three `case` branches and one `default` branch. -tipp: `switch (number) { case 1 -> { System.out.println("one"); } … default -> { System.out.println("unknown"); } }` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Fill in the gaps: switch - -::: lückentext -titel: Months in words -auftrag: Fill in the missing words and symbols of the case selection. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which month (1 to 3)?"); -int month = scanner.nextInt(); -[[switch]] (month) { - [[case]] 1 -> { - System.out.println("January"); - } - case 2 [[->]] { - System.out.println("February"); - } - [[case]] 3 -> { - System.out.println("March"); - } - [[default]] -> { - System.out.println("Unknown"); - } -} -``` -erklärung: After `switch` the variable is in round brackets. Each case starts with `case`, the value and the arrow `->`. `default` runs when no case matches. -::: - -## Find the bug: calculator - -::: übung l10-java-fehler -titel: Fix the calculator -code: java -auftrag: The calculator reads two numbers and a choice: 1 plus, 2 minus, 3 times. For 7, 3 and choice 2 the result should be **4**, but it calculates **-4**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Not an option -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Plus and times are right. Take a close look at `case 2`. -tipp: With minus the order matters: `a - b` instead of `b - a`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l10-java-laeuft-nicht -titel: Fix the grade -code: java -auftrag: Ludwig got grade 2. The program should output **good**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> good -braucht: fallauswahl -tipp: Which variable does the case selection check? Is it written the same way above? -tipp: The brackets after `switch` need `grade` with a small g: `switch (grade) {` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which case? - -::: spiel frage -titel: Game: Which case? -intro: Ludwig presses buttons on the drinks machine and other case selections. What happens? Six rounds. - -runde: - int choice = 2; - switch (choice) { - case 1 -> { System.out.println("water"); } - case 2 -> { System.out.println("juice"); } - case 3 -> { System.out.println("cocoa"); } - default -> { System.out.println("There is no such button."); } - } -frage: What is output? -- water -- [x] juice -- There is no such button. -> choice is 2, so exactly `case 2` runs. - -runde: - int choice = 4; - switch (choice) { - case 1 -> { System.out.println("water"); } - case 2 -> { System.out.println("juice"); } - case 3 -> { System.out.println("cocoa"); } - default -> { System.out.println("There is no such button."); } - } -frage: What is output? -- cocoa -- nothing -- [x] There is no such button. -> No case fits 4, so `default` runs. - -runde: - String day = "monday"; - switch (day) { - case "Monday" -> { System.out.println("maths"); } - case "Tuesday" -> { System.out.println("sports"); } - default -> { System.out.println("free"); } - } -frage: What is output? -- maths -- sports -- [x] free -> With texts every letter counts: "monday" with a small m is not "Monday". - -runde: - int grade = 0; - switch (grade) { - case 1 -> { System.out.println("very good"); } - case 2 -> { System.out.println("good"); } - case 3 -> { System.out.println("satisfactory"); } - } -frage: What is output? -- very good -- [x] nothing -- satisfactory -> No case fits, and there is no `default`. Then simply nothing happens. - -runde: -frage: You want to check whether `age >= 18`. What fits better? -- [x] a branch -- a case selection -> A case selection only compares with fixed values. For "greater than or equal" you need a branch. - -runde: -frage: A dice shows 1 to 6, and for every number a different word should appear. What fits better? -- a branch -- [x] a case selection -> One variable, six fixed values: that's exactly what the case selection is for. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can insert a switch, add cases and change case names. → How a switch is built -- I can explain when the case default runs. → How a switch is built -- I can decide whether a switch or a branch fits better. → Switch or branch? -- I can write a case selection in Java with switch. → Now in Java -::: - -## Quiz - -::: quiz -? The variable day has the value 5. There are the cases 1, 2, 3 and default. What runs? -→ How a switch is built -- [ ] Nothing at all -- [ ] The case 3 -- [x] The case default -> No case name matches 5, so default applies. - -? How many cases run each time the switch runs? -→ How a switch is built -- [x] Exactly one -- [ ] All cases from the matching one on -- [ ] All cases -> After the matching case, the program continues straight below the switch. - -? What is a switch NOT suitable for? -→ Switch or branch? -- [ ] Choosing menu item 1, 2 or 3 -- [x] Checking whether a temperature is above 30 degrees -- [ ] Handling a colour such as red, yellow or green -> “Above 30” is a range. For that you need a branch with a comparison. - -? At the traffic light you type `Red`, and the case is called `"red"`. What happens? -→ How a switch is built -- [ ] The case `"red"` runs. -- [x] The case default runs. -- [ ] The simulation reports an error. -> Upper and lower case matter. Red and red are different texts. - -? Under which name do you also find the switch in textbooks? -→ How a switch is built -- [ ] Conditional statement -- [ ] Sequence -- [x] Multiple selection -> One of many cases is selected. That is why it is also called multiple selection. - -? What is the otherwise case of a switch called in Java? -→ Now in Java -- [ ] else -- [x] default -- [ ] case 0 -::: diff --git a/web/lessons/en/11-while-schleife.md b/web/lessons/en/11-while-schleife.md deleted file mode 100644 index 6e494266..00000000 --- a/web/lessons/en/11-while-schleife.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -titel: While loop -bereich: Basics -kurz: Repeat steps as long as a condition is true. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the while loop checks its condition before every pass -- use a counter variable with a start value, a condition and a change -- notice when a loop never ends and fix the mistake -- follow a loop pass by pass in a desk check -- write a while loop in Java -::: - -## Repeat as long as something holds - -Often a program should do something several times. Instead of writing the same box five times, you use a **loop**. - -The **while loop** repeats its inside **as long as** its condition is true. The condition is at the top, in the head of the box. The inside is indented below it, framed by a bracket on the left. - -::: diagramm l10-countdown.visustruct -A countdown: as long as number is greater than 0, number is output and gets smaller by 1. After that, Start! appears. -::: - -## The condition comes first - -The while loop is a **pre-test loop**. Before **every** pass, the condition is checked: - -1. Check the condition. -2. If it is true, run the inside once and start again at 1. -3. If it is false, the program continues below the loop. - -::: grafik l10-kopfgesteuert.svg -After every pass it goes back to the condition. Only when it is false does the program continue below the loop. -::: - -In the countdown the condition is checked four times: with 3, 2 and 1 it is true, with 0 it is false. - -> If the condition is already false the first time it is checked, the inside runs **not even once**. Start the countdown with `int number = 0` and only Start! appears. - -## A counter variable - -Many loops count along. For that you need three things: - -- **A start value before the loop**, e.g. `int i = 1` -- **A condition in the head**, e.g. `i <= 5` -- **A change inside**, e.g. `i = i + 1` (short: `i++`) - -If one of these parts is missing, the loop counts wrongly or not at all. - -::: angeberwissen -Counting loops are ancient! In their 1973 article, Nassi and Shneiderman showed their idea using the **factorial**, among other things: 5! = 1 · 2 · 3 · 4 · 5 = 120. Here is my goat version of it. Try it with **Step** in the simulation and watch `factorial` grow. Baaah, that goes fast! -::: - -::: diagramm l10-fakultaet.visustruct -The factorial with a counter variable `i`, rebuilt from an example in the 1973 article. -::: - -## Exercise: Count to n - -::: übung l10-bis-n -titel: Count to n -auftrag: Ask for a whole number `n` and output all numbers from 1 to `n`, each on its own line. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: You need a counter variable that starts at 1 and grows by 1 in every pass. -tipp: Before the loop, create `int i = 1`. The condition is `i <= n`. Inside, you output `i` and then increase `i` by 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Watch out: endless loop - -If nothing inside the loop changes the condition, it stays true forever. The loop never stops. This is called an **endless loop**. - -::: diagramm l10-endlos.visustruct -Here number = number - 1 is missing. The loop outputs 3 again and again and never reaches Start! -::: - -Ask yourself for every loop: **What inside makes sure that the condition becomes false at some point?** When an exercise is checked, VisuStruct stops after a very large number of steps and reports a possible endless loop. - -## Exercise: Double up to 100 - -::: übung l10-verdoppeln -titel: Double up to 100 -auftrag: Ask for a whole number. Keep doubling it until it is at least 100. - At the end, output **how often** you doubled it. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: The loop runs as long as the number is still too small. Count along how often it has run. -tipp: You need a second variable `int steps = 0`. The condition is `number < 100`. Inside, you double the number with `number = number * 2` and increase `steps` by 1. If the number is already 100, the loop does not run at all. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Loops in a desk check - -With a loop, the **desk check** is especially valuable. You see exactly how often the loop runs and with which value it stops. Every check of the condition gets a row of its own. - -::: diagramm l16-schreibtisch.visustruct -A while loop adds up the numbers 1, 2 and 3. -::: - -| Step | sum | i | Output | -|---|---|---|---| -| `int sum = 0` | 0 | – | | -| `int i = 1` | 0 | 1 | | -| `i <= 3` → true | 0 | 1 | | -| `sum = sum + i` | 1 | 1 | | -| `i = i + 1` | 1 | 2 | | -| `i <= 3` → true | 1 | 2 | | -| `sum = sum + i` | 3 | 2 | | -| `i = i + 1` | 3 | 3 | | -| `i <= 3` → true | 3 | 3 | | -| `sum = sum + i` | 6 | 3 | | -| `i = i + 1` | 6 | 4 | | -| `i <= 3` → false | 6 | 4 | | -| `output: sum` | 6 | 4 | 6 | - -Common mistakes in loops are: - -- The loop runs **once too often or once too few**, e.g. `<` instead of `<=`. -- A variable is **not changed** in the loop, or changed in the wrong direction. Then the loop never ends. -- A variable gets its start value **inside** the loop instead of before it and is reset in every pass. - -## Exercise: Fix the countdown - -::: übung l16-countdown -titel: Fix the countdown -auftrag: The countdown should count down from the start value and output **Go!** at the end. So with 3: 3, 2, 1, Go! - The start diagram has two mistakes. Find and fix both. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Go! -test: 1 -> 1 | Go! -test: 0 -> Go! -tipp: There are two mistakes. One is about counting, the other about the output at the end. -tipp: Start the simulation with start value 3 and watch i under Variables. Does i get smaller? Also compare the last output carefully with the task. -lösung: l16-countdown-loesung.visustruct -::: - -## Now in Java - -The countdown from the start of this lesson looks like this in Java: - -```java editor -int number = 3; -while (number > 0) { - System.out.println(number); - number = number - 1; -} -System.out.println("Start!"); -``` - -- After `while` the condition is in round brackets, as with `if`. -- Everything that is repeated is in curly brackets. That is the body of the loop. -- The body is indented. As in the structure chart, you can see what belongs to the loop. - -::: übung l11-java-gerade -titel: Even numbers in Java -code: java -auftrag: Write a Java program that reads a whole number `n` and uses a while loop to output all even numbers from 2 up to at most `n`. For 7 that is 2, 4 and 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: You need a counter variable that starts at 2. The loop runs as long as it is not greater than `n`. -tipp: `int number = 2;` before the loop, then `while (number <= n) { … }`. In the body you output `number` and increase it with `number = number + 2;`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Fill in the gaps: while - -::: lückentext -titel: Sum from 1 to 10 -auftrag: The program should add up the numbers from 1 to 10. Fill in the missing words and symbols. -```java -int sum = 0; -int number = 1; -[[while]] (number [[<=]] 10) [[{]] - sum = sum + [[number]]; - [[number]] = number + 1; -} -System.out.println("Sum: " + sum); -``` -erklärung: The loop runs as long as `number` is at most 10. In each pass `number` is added to the sum, then `number` goes up by 1. Without that line it would be an endless loop. Output: **Sum: 55** -::: - -## Find the bug: saving - -::: übung l11-java-fehler -titel: Fix the savings plan -code: java -auftrag: Ludwig saves 5 euros every week. The program asks for the savings goal and should output after how many weeks he has reached it. For 50 euros that is 10 weeks, but the program says **Weeks: 11**. - Go through the code with Step and watch `money` and `weeks`. Fix the bug and click Check. -start: l11-java-fehler-start.visustruct -test: 50 -> Weeks: 10 -test: 12 -> Weeks: 3 -test: 5 -> Weeks: 1 -test: 1 -> Weeks: 1 -braucht: whileSchleife -tipp: How much money does Ludwig have when the loop runs for the last time? Does he really need to save for another week? -tipp: He only saves as long as the goal has **not been reached** yet: `while (money < goal)`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l11-java-laeuft-nicht -titel: Fix the countdown -code: java -auftrag: The countdown should output **3**, **2**, **1** and then **Go!** But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Go! -braucht: whileSchleife -tipp: The bug is inside the loop. Which variable should be output, and what is it really called? -tipp: Output `number` with a small n: `System.out.println(number);` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Game: How often? - -::: spiel frage -titel: Game: How often? -intro: Ludwig has written while loops. Do the desk check in your head. Six rounds. - -runde: - int number = 3; - while (number > 0) { - System.out.println(number); - number = number - 1; - } - System.out.println("Go!"); -frage: What is output? -- [x] 3\n2\n1\nGo! -- 3\n2\n1\n0\nGo! -- Go! -> When number is 0, `number > 0` is false, so the 0 is not output any more. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: What is output? -- [x] nothing -- 10 -- 10 to 4 -> The condition is checked first. 10 < 5 is false, the body doesn't run even once. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: What happens? -- 1, 2 and 3 are output. -- [x] The loop never stops. -- Nothing is output. -> i never gets bigger in the body. The condition always stays true: an endless loop. - -runde: - int number = 1; - while (number < 100) { - number = number * 2; - } - System.out.println(number); -frage: What is output? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Only at 128 is `number < 100` false. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: What is output? -- 4 -- 5 -- [x] 6 -> i runs 0, 2, 4, 6. At 6, `i < 5` is false. The 5 is skipped. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: How often does the body run? -- 3 times -- [x] 4 times -- 10 times -> The body runs at i = 1, 4, 7 and 10. After that i is 13. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the while loop checks its condition before every pass. → The condition comes first -- I can use a counter variable with a start value, a condition and a change. → A counter variable -- I can notice when a loop never ends and fix the mistake. → Watch out: endless loop -- I can follow a loop pass by pass in a desk check. → Loops in a desk check -- I can write a while loop in Java. → Now in Java -::: - -## Quiz - -::: quiz -? What does pre-test loop mean? -→ The condition comes first -- [x] The condition is checked before every pass. -- [ ] The condition is checked after every pass. -- [ ] The loop always runs exactly once. -> The head with the condition is at the top and is worked out first. - -? `int i = 5`, then a while loop with the condition `i < 3`. How often does the inside run? -→ The condition comes first -- [x] Not at all -- [ ] Once -- [ ] Three times -> The very first time it is checked, 5 < 3 is false. - -? Why does a loop with the condition `number > 0` never end if the inside only contains `output: number`? -→ Watch out: endless loop -- [ ] Because output makes the number bigger -- [x] Because number never changes and the condition stays true -- [ ] Because the condition is written wrongly -> Something has to happen inside that makes the condition false at some point. - -? What else are loops called in textbooks? -→ The condition comes first -- [x] Iteration or repetition -- [ ] Branches -- [ ] Subprograms -> A loop repeats statements. That is why it is also called iteration or repetition. - -? A loop should count from 1 to 5, but it only goes up to 4. What is the likely mistake? -→ A counter variable -- [x] The condition says i < 5 instead of i <= 5. -- [ ] The loop is a while loop. -- [ ] The variable i has the wrong name. - -? Which line starts a while loop correctly in Java? -→ Now in Java -- [ ] while number > 0 { -- [x] while (number > 0) { -- [ ] while (number > 0); -> The condition is in round brackets, then `{` opens the body. -::: diff --git a/web/lessons/en/12-do-while-schleife.md b/web/lessons/en/12-do-while-schleife.md deleted file mode 100644 index fc12d7aa..00000000 --- a/web/lessons/en/12-do-while-schleife.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -titel: Do-while loop -bereich: Basics -kurz: Run first, check afterwards: loops with at least one pass. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the do-while loop only checks its condition after the inside -- explain why the inside runs at least once -- repeat an input until it is valid -- write a do-while loop in Java -::: - -## Do first, check afterwards - -The **do-while loop** is a **post-test loop**. The condition is at the bottom, in the foot of the box. This is how it runs: - -1. Run the inside once. -2. Check the condition. -3. If it is true, start again at 1. If it is false, the program continues below the loop. - -::: grafik l11-vergleich.svg -On the left, the while loop checks first; on the right, the do-while loop runs first. -::: - -At the top of the box is `do`, and the condition is in the foot. When you click the loop, the word `while` is in front of the text field in the Object Inspector: the condition means “repeat as long as …”. - -> Just like with the while loop, the condition says **when the loop repeats**. As long as it is true, the loop keeps running. - -::: angeberwissen -Python has no do-while loop at all. If you want “do first, check later” there, you write `while True:` and leave the loop with `break`. Java, C and JavaScript, on the other hand, have had `do … while` for decades. -::: - -## At least one pass - -Because the condition is only checked at the end, the inside **always runs at least once**. That is the most important difference from the while loop. - -::: diagramm l11-einmal.visustruct -i is 10 and so it is not less than 5. Still, 10 is output once, because the check only comes afterwards. -::: - -With a while loop and the same condition `i < 5`, nothing at all would be output here. - -## Typical use: repeat an input - -The do-while loop fits whenever something has to happen first before you can check it. The best example is an input: only after someone has typed something do you know whether it is valid. - -::: diagramm l11-eingabe-pruefen.visustruct -The question comes again until a number from 1 to 10 has been typed in. In the simulation, first try 0 and then 12. -::: - -Here the condition describes the **invalid** input: `number < 1 || number > 10`. As long as the input is invalid, the question is asked again. - -## Exercise: Valid grade - -::: übung l11-note -titel: Valid grade -auftrag: Ask for a grade. If it is not between 1 and 6, ask again. If it is valid, output it, for example **Your grade: 3**. -test: 3 -> Your grade: 3 -test: 0, 7, 2 -> Your grade: 2 -test: 9, 6 -> Your grade: 6 -test: -1, 1 -> Your grade: 1 -braucht: doUntilSchleife -tipp: The grade is asked for at least once. After that, the condition decides whether to ask again. -tipp: Before the loop, create `int grade = 0`. The input `input: grade "Grade from 1 to 6"` goes inside the loop. The condition is `grade < 1 || grade > 6`. -lösung: l11-note-loesung.visustruct -::: - -## Exercise: Sum until 0 - -::: übung l11-summe -titel: Sum until 0 -auftrag: Keep asking for numbers until a 0 is typed in. Then output the sum of all the numbers typed in. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: You need a variable for the sum that starts at 0 before the loop. In every pass the new number is added. -tipp: Before the loop, create `int sum = 0` and `int number = 0`. In the loop you ask for `number` and calculate `sum = sum + number`. Adding the 0 does no harm. The loop keeps running as long as `number != 0`. -tipp: The outline: `int sum = 0` and `int number = 0`, then a do-while loop with the condition `number != 0`. Inside it: the input of `number` and `sum = sum + number`. Below the loop: `output: sum`. -lösung: l11-summe-loesung.visustruct -::: - -## Now in Java - -The loop that still runs once looks like this in Java: - -```java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -``` - -- The body is between `do {` and `}`, the condition only comes afterwards. -- Here there is a **semicolon** after `while (…)`. That is different from the while loop. - -::: übung l12-java-verdoppeln -titel: Double at least once -code: java -auftrag: Write a Java program that reads a start number. A do-while loop doubles it and outputs it as long as it is less than 100. A number of 100 or more is still doubled at least once: 200 becomes 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: In the body you double the number and output it. The condition comes at the end. -tipp: `do { number = number * 2; System.out.println(number); } while (number < 100);` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Fill in the gaps: do-while - -::: lückentext -titel: Tripling -auftrag: The number is tripled and output as long as it is less than 100. Fill in the missing words and symbols. -```java -int number = 1; -[[do]] { - number = number * 3; - System.out.println(number); -} [[while]] (number [[<]] 100)[[;]] -``` -erklärung: The body is between `do {` and `}`, the condition only comes afterwards. In a do-while loop there is a semicolon after `while (…)`. Output: 3, 9, 27, 81 and 243. -::: - -## Find the bug: halving - -::: übung l12-java-fehler -titel: Fix the halving -code: java -auftrag: The program reads a start number and keeps dividing it by 2 (whole-number division) with a do-while loop. Every result is output until **0** is reached. For 10 that is 5, 2, 1 and 0. But the 0 is missing. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: What value does `number` have after the 1 has been output? Does the loop run once more then? -tipp: The loop should keep going as long as the number is greater than 0: `while (number > 0);` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l12-java-laeuft-nicht -titel: Fix the doubling -code: java -auftrag: Starting at 1, the program should keep doubling as long as the number is less than 20: **1**, **2**, **4**, **8**, **16**. It outputs only the 1 and then stops. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: The first round still runs. The bug appears when the condition at the bottom is checked for the first time. -tipp: The condition needs `limit` with a small l: `} while (number < limit);` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Game: At least once? - -::: spiel frage -titel: Game: At least once? -intro: While or do-while? Ludwig shows you loops. Predict what happens. Six rounds. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: What is output? -- nothing -- [x] 10 -- 10 to 4 -> First do, then check: the body runs once, after that 11 < 5 is false. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: What is output? -- [x] nothing -- 10 -- 11 -> In a while loop the condition comes first. 10 < 5 is false, so nothing runs at all. - -runde: - int number = 200; - do { - number = number * 2; - } while (number < 100); - System.out.println(number); -frage: What is output? -- 200 -- [x] 400 -- 800 -> It is doubled once no matter what. After that 400 < 100 is false. - -runde: - int number = 30; - do { - number = number * 2; - System.out.println(number); - } while (number < 100); -frage: What is output? -- 60 -- [x] 60\n120 -- 60\n120\n240 -> After 60, 60 < 100 is true, so it goes on. After 120 it stops. - -runde: -frage: Which line ends a do-while loop in Java correctly? -- [x] `} while (i < 5);` -- `} while (i < 5)` -- `while (i < 5) {` -> After the `while (…)` of a do-while loop there is a semicolon. - -runde: -frage: What is a do-while loop typically used for? -- [x] repeating an input until it is valid -- counting from 1 to 10 -- swapping two values -> The question is asked at least once. Only after that do you know whether the input was valid. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the do-while loop only checks its condition after the inside. → Do first, check afterwards -- I can explain why the inside runs at least once. → At least one pass -- I can repeat an input until it is valid. → Typical use: repeat an input -- I can write a do-while loop in Java. → Now in Java -::: - -## Quiz - -::: quiz -? What does post-test loop mean? -→ Do first, check afterwards -- [ ] The condition is checked before every pass. -- [x] The condition is checked after every pass. -- [ ] The loop has no condition. -> The condition is in the foot, at the bottom, and is worked out after the inside. - -? The condition of a do-while loop is false from the start. How often does the inside run? -→ At least one pass -- [ ] Not at all -- [x] Exactly once -- [ ] Forever -> The inside runs first. Only then does the loop notice that it should stop. - -? You want to ask for a number from 1 to 10. Which condition goes in the foot? -→ Typical use: repeat an input -- [ ] `number >= 1 && number <= 10` -- [x] `number < 1 || number > 10` -- [ ] `number == 10` -> The loop repeats as long as the input is invalid, that is, less than 1 or greater than 10. - -? When is a do-while loop better than a while loop? -→ At least one pass -- [x] When the inside has to run at least once, e.g. for an input -- [ ] When the loop might not need to run at all -- [ ] When you want to count exactly to 10 - -? What comes at the end of a do-while loop in Java? -→ Now in Java -- [ ] } while (condition) { -- [x] } while (condition); -- [ ] } until (condition); -> A semicolon follows the condition. -::: diff --git a/web/lessons/en/13-for-schleife.md b/web/lessons/en/13-for-schleife.md deleted file mode 100644 index 720f8e2d..00000000 --- a/web/lessons/en/13-for-schleife.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -titel: For loop -bereich: Basics -kurz: Count a fixed number of passes with Init, Condition and Step. ---- - -## Skills I will gain now - -::: kompetenzen -- name the three parts Init, Condition and Step of the for loop -- explain in which order the three parts run -- count forwards, backwards and in bigger steps with a for loop -- write a for loop in Java and explain its three fields -::: - -## Counting with a fixed plan - -In the while loop you already counted along: a start value before the loop, a condition in the head, a change inside. The **for loop** puts these three parts together in the head. That way you see at a glance how it counts. - -::: grafik l12-while-zu-for.svg animiert -The while loop becomes a for loop: init, condition and step move together into the head. -::: - -When you click a for loop, it has three fields on the right: - -- **Init:** `int i = 1` creates the counter variable. -- **Condition:** `i <= 5` says how long to count. -- **Step:** `i++` increases `i` by 1 after every pass. - -::: diagramm l12-eins-bis-fuenf.visustruct -A for loop that outputs the numbers from 1 to 5. -::: - -::: angeberwissen -Why is the counting variable almost always called `i`? In maths `i` has long stood for an index. And in FORTRAN, one of the first programming languages from 1957, all variables starting with I to N were automatically whole numbers. Programmers have not broken the habit to this day. -::: - -## In which order? - -The three parts run at different times: - -1. **Init** runs exactly once, right at the start. -2. **Condition** is checked before every pass. If it is false, the loop is finished. -3. After the inside comes the **Step**, then the condition again. - -::: grafik l12-for-ablauf.svg -Init only runs once. After that, condition, inside and step repeat. -::: - -So the for loop is a pre-test loop, like the while loop. Go through the example in the simulation with **Step** and watch how `i` changes. - -> Use the for loop when you know beforehand how often something should happen. If the end depends on something that only happens during the loop, for example an input, the while or do-while loop is better. - -## Example: sum from 1 to n - -A for loop is good for adding up numbers. Before the loop there is `int sum = 0`. In every pass the current number `i` is added. - -::: diagramm l12-summe.visustruct -With n = 4 the loop calculates 0 + 1 + 2 + 3 + 4. At the end, 10 is output. -::: - -## Other steps - -The step does not always have to be `i++`: - -- `i--` counts backwards, for example with Init `int i = 10` and Condition `i >= 1`. -- `i = i + 2` jumps in steps of two, for example 0, 2, 4, 6. - -Make sure that Init, Condition and Step fit together. With Init `int i = 1`, Condition `i >= 1` and Step `i++`, the condition never becomes false. - -::: übung l12-countdown -titel: Countdown -auftrag: Ask for a whole number `n`. Use a for loop to count backwards from `n` to 1 and output each number. Then output **Go!**. -test: 3 -> 3 | 2 | 1 | Go! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Go! -test: 1 -> 1 | Go! -braucht: forSchleife -tipp: This time the for loop counts backwards. Think about where it starts, when it stops and how it counts. -tipp: Init is `int i = n`, the condition `i >= 1` and the step `i--`. The output Go! goes below the loop, not inside it. -lösung: l12-countdown-loesung.visustruct -::: - -## Exercise: Times table - -::: übung l12-einmaleins -titel: Times table -auftrag: Ask for a number and output its times table from 1 to 10. Each line looks like this: **3 x 1 = 3**, **3 x 2 = 6** and so on. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: The loop counts from 1 to 10. In every pass you output one line of the table. -tipp: Inside, first calculate `int product = number * i`. Then output `number + " x " + i + " = " + product`. Remember the spaces inside the quotation marks. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Exercise: Fix the sum - -::: übung l16-summe -titel: Fix the sum -auftrag: The structure chart should add up all numbers from 1 to n. So with n = 4 the result should be 1 + 2 + 3 + 4 = **10**. But it outputs 6. - Do a desk check or use **Step** and fix the mistake. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Go through the loop with n = 4 in your head or with **Step**. Which numbers are really added? -tipp: Look at the last value of i the loop runs with. With n = 4, i = 4 must be added too. -lösung: l16-summe-loesung.visustruct -::: - -## Now in Java - -The sum from 1 to n looks like this in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Up to which number?"); -int n = scanner.nextInt(); -int sum = 0; -for (int i = 1; i <= n; i++) { - sum = sum + i; -} -System.out.println(sum); -``` - -The round brackets after `for` hold the three fields of the for loop, separated by semicolons: - -| Field | In Java | Meaning | -|---|---|---| -| Init | `int i = 1` | `i` starts at 1. | -| Condition | `i <= n` | The loop runs as long as this is true. | -| Step | `i++` | After every pass `i` gets 1 bigger. | - -`i++` is short for `i = i + 1`. To count backwards, use `i--`. - -::: übung l13-java-quadrate -titel: Square numbers in Java -code: java -auftrag: Write a Java program that reads a whole number `n` and uses a for loop to output the square numbers from 1 to `n`, each on its own line. For 3 that is 1, 4 and 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: The loop counts `i` from 1 to `n`. The square of `i` is `i * i`. -tipp: `for (int i = 1; i <= n; i++) { … }` and in the body `System.out.println(i * i);`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Fill in the gaps: for - -::: lückentext -titel: Counting up and down -auftrag: The first loop adds up the numbers from 1 to 5, the second counts down from 10 to 0. Fill in the missing words and symbols. -```java -int sum = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - sum = sum + i; -} -System.out.println("Sum: " + sum); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -``` -erklärung: The round brackets hold the start value, the condition and the step, separated by semicolons. `i++` counts up, `i--` counts down. When counting down to 0 the condition is `i >= 0`. -::: - -## Find the bug: times table - -::: übung l13-java-fehler -titel: Fix the times table -code: java -auftrag: The program asks for a times table and should output it from 1 times to 10 times, for 7 that is 7, 14, … up to 70. But the 70 is missing. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: How often does the loop run? Count which values `i` takes. -tipp: `i` should also take the value 10: `i <= 10` instead of `i < 10`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l13-java-laeuft-nicht -titel: Fix the stairs -code: java -auftrag: Ludwig climbs 5 steps, each 20 cm high. The program should output **Height: 100 cm**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Height: 100 cm -braucht: forSchleife -tipp: `height` is used in a calculation, but where is the variable created? And at what height does Ludwig start? -tipp: Create `height` before the loop: `int height = 0;` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program hides **exactly one mistake**, and the bomb is ticking. Read what the program should do and tap the wrong line before time runs out. If the parts of a line have a border, tap exactly the wrong part, for example just `i++`. Eight rounds, 25 seconds each. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Should output 1, 2 and 3. -fehler: 2 -besser: System.out.println(i); -> The semicolon at the end of the statement is missing. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Should output 1 to 5. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> The three fields of the for loop are separated by semicolons, not commas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Should count backwards from 10 to 1. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> With `i++`, `i` keeps growing and the condition `i >= 1` stays true: an endless loop. To count backwards, use `i--`. - -runde: - int sum = 0; - for (int i = 1; i <= 4; i++) { - sum = i; - } - System.out.println(sum); -frage: Should output the sum 1 + 2 + 3 + 4 = 10. -fehler: 3 -stelle: i -besser: sum = sum + i; -> `sum = i` overwrites the sum in every pass. In the end it only holds 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Baaa"); - } -frage: Should output Baaa exactly five times. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> From 0 up to and including 5 there are six passes. If you start at 0, use `<`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Should output 1, 2 and 3. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> Java tells upper and lower case apart: `System` is written with a capital S. - -runde: - int number = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(number + " x " + i + " = " + number * number); - } -frage: Should output the 7 times table: 7 x 1 = 7, 7 x 2 = 14 and so on. -fehler: 3 -stelle: number * number -besser: System.out.println(number + " x " + i + " = " + number * i); -> `number * number` is always 49. It has to be `number * i`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Should output the even numbers 2, 4, 6, 8 and 10. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> `i + 2` only calculates and stores nothing. For `i` to really grow, you need `i = i + 2`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the three parts Init, Condition and Step of the for loop. → Counting with a fixed plan -- I can explain in which order the three parts run. → In which order? -- I can count forwards, backwards and in bigger steps with a for loop. → Other steps -- I can write a for loop in Java and explain its three fields. → Now in Java -::: - -## Quiz - -::: quiz -? How often does the Init of a for loop run? -→ In which order? -- [x] Exactly once -- [ ] Before every pass -- [ ] After every pass -> Init creates the counter variable once at the start. - -? Init `int i = 0`, Condition `i < 3`, Step `i++`. Which values does i have inside the loop? -→ Counting with a fixed plan -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> With i = 3 the condition is false, so the inside does not run any more. - -? Init `int i = 2`, Condition `i <= 10`, Step `i = i + 2`. How often does the inside run? -→ Other steps -- [ ] 4 times -- [x] 5 times -- [ ] 10 times -> i takes the values 2, 4, 6, 8 and 10. - -? When does a for loop fit best? -→ Counting with a fixed plan -- [x] When you know beforehand how often to repeat -- [ ] When you keep asking until an input is valid -- [ ] When only a single value is checked - -? What does `i++` mean in a for loop? -→ Counting with a fixed plan -- [ ] i is doubled. -- [x] i gets 1 bigger. -- [ ] i is output. -> `i++` is short for `i = i + 1`. -::: diff --git a/web/lessons/en/14-verschachteln.md b/web/lessons/en/14-verschachteln.md deleted file mode 100644 index f9641fb1..00000000 --- a/web/lessons/en/14-verschachteln.md +++ /dev/null @@ -1,305 +0,0 @@ ---- -titel: Nesting -bereich: Basics -kurz: Combine branches inside loops and loops inside loops. ---- - -## Skills I will gain now - -::: kompetenzen -- put a branch inside a loop -- put a loop inside another loop -- explain that the inner loop runs all the way through in every pass of the outer one -- write nested loops and branches in Java -::: - -## Building blocks inside each other - -You now know all the important building blocks: sequence, branch, switch and three kinds of loops. They only get really powerful together. - -Every path and every loop body is a sequence itself. You may put any element there, including more branches and loops. This is called **nesting**. - -> To do this, simply drag an element onto the placeholder ø inside a loop or a path. - -## A branch inside a loop - -The loop goes through all the numbers, and the branch decides for each single number whether something happens. That way you can count, for example, how many even numbers there are from 1 to n. - -::: diagramm l13-gerade-zaehlen.visustruct -In every pass the branch checks with i % 2 == 0 whether i is even. Only then is count increased by 1. The false path stays empty. -::: - -With n = 7 these are the numbers 2, 4 and 6, so 3 is output. - -## Exercise: Count even inputs - -::: übung l13-gerade-eingaben -titel: Count even inputs -auftrag: Ask for exactly **five** whole numbers, one after another. Then output how many of them were even. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Doing the same thing five times means: loop. In every pass a number is read in and checked. -tipp: A for loop from 1 to 5. Inside, first comes the input `input: int number "Enter a number"`, below it a branch with `number % 2 == 0`. In the true path you increase `count` by 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## A loop inside a loop - -If one loop sits inside another, the **inner loop runs all the way through in every pass of the outer one**. With this you can build patterns of rows and columns. - -::: diagramm l13-dreieck.visustruct -The outer loop counts the lines. The inner one adds as many stars to the text as the line number says. With n = 3 you get a triangle of 1, 2 and 3 stars. -::: - -With `String text = ""` every line starts empty. `text = text + "*"` adds a star. Only when the line is finished is it output. - -Experienced programmers give the counter variables the short names `i` and `j`: `i` outside, `j` inside. Then it looks like this, here with a small times table: - -::: grafik l13-verschachtelt-tabelle.svg animiert -The outer loop counts the lines with `i`, the inner one counts the numbers in a line with `j`. On every new line, `j` starts again at 1. -::: - -## Exercise: Rectangle - -::: übung l13-rechteck -titel: Rectangle -auftrag: Ask for a width and a height. Output a rectangle made of `#`: as many lines as the height, and in each line as many `#` as the width. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: You need two loops inside each other: one for the lines and inside it one for the characters of a line. -tipp: Build it like the triangle. The difference: the inner loop always counts up to `width`, not up to the line number. The outer loop counts up to `height`. -tipp: The outline: inputs `width` and `height`. Outer for loop `int line = 1`, `line <= height`, `line++`. Inside it first `String text = ""`, then the inner for loop `int column = 1`, `column <= width`, `column++` with `text = text + "#"`, and below the inner loop `output: text`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Keep track - -With nested diagrams, the drawing helps you: whatever is further inside is indented and surrounded by its loop or path. - -- Read from the outside in: what repeats? What is decided in every pass? -- Give counter variables meaningful names such as `line` and `column`. Two loops must not change the same counter variable. -- Go through an example in the simulation with **Step** and watch the variables. - -::: angeberwissen -This is exactly what the structure chart was invented for. Shneiderman asked himself: if we are supposed to do without GOTO, why not do without the arrows in the flowchart as well? The result: no arrow spaghetti, and every block stays nicely in its pen. -::: - -## Now in Java - -Counting the even numbers looks like this in Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Up to which number?"); -int n = scanner.nextInt(); -int count = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - count++; - } // end if -} // end for -System.out.println(count); -``` - -What is nested in the structure chart is nested in curly brackets in Java. Every level is indented four spaces further. If your code gets messy, the **Indent** button at the top of the Code area helps. - -With many closing brackets `}` it is easy to lose track: which bracket belongs to which block? A comment after it tells you, for example `} // end if` and `} // end for`. This also helps when you carry on later. - -`count++` is short for `count = count + 1`. - -::: übung l14-java-treppe -titel: Stairs of stars -code: java -auftrag: Write a Java program that reads the number of steps and outputs stairs made of stars. For 3 steps that is `*`, `**` and `***`, each step on its own line. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: An outer loop counts the steps. In each step an inner loop builds a text of stars. -tipp: In the outer loop: `String line = "";`, then `for (int j = 1; j <= i; j++) { line = line + "*"; }` and after that `System.out.println(line);`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Fill in the gaps: nesting - -::: lückentext -titel: Rectangle of stars -auftrag: The program outputs a rectangle of 3 lines with 4 stars each. Fill in the missing words and symbols. -```java -for (int line = 1; line <= 3; line++) { - String text = [[""]]; - [[for]] (int column = 1; column [[<=]] 4; [[column++ | column += 1 | column = column + 1]]) { - text = text [[+]] "*"; - } - System.out.[[println]](text); -[[}]] -``` -erklärung: The inner loop builds one line of 4 stars, the outer loop repeats that three times. Each line starts with an empty text `""`. The last bracket closes the outer loop. -::: - -## Find the bug: divisible by 3 - -::: übung l14-java-fehler -titel: Fix the counting -code: java -auftrag: The program should count how many numbers from 1 to `n` are divisible by 3 and output the result **once**. For 10 that is just **Count: 3**. But it outputs far too many lines. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l14-java-fehler-start.visustruct -test: 10 -> Count: 3 -test: 3 -> Count: 1 -test: 2 -> Count: 0 -braucht: forSchleife, verzweigung -tipp: Look at the curly brackets: is the output inside the loop or after it? -tipp: Move the line `System.out.println("Count: " + count);` after the closing bracket of the loop. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l14-java-laeuft-nicht -titel: Fix the even numbers -code: java -auftrag: The program should output the even numbers from 1 to 10: **2**, **4**, **6**, **8**, **10**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: The bug is right inside, in the output. What is the loop’s counter variable called? -tipp: The counter variable is `i` with a small i: `System.out.println(i);` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Inside and outside - -::: spiel frage -titel: Game: Inside and outside -intro: Ludwig has put building blocks inside each other. Read from the outside in and predict what happens. Six rounds. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: How many stars are output? -- 3 -- 5 -- [x] 6 -> The inner loop runs completely in each of the 3 runs of the outer loop: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Which line is output fourth? -- 1 4 -- [x] 2 1 -- 2 2 -> First j runs through for i = 1: 1 1, 1 2, 1 3. Then j starts again at 1, now with i = 2. - -runde: - int count = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - count++; - } - } - System.out.println(count); -frage: What is output? -- [x] 3 -- 10 -- 4 -> The branch only counts 3, 6 and 9. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("even"); - } else { - System.out.println(i); - } - } -frage: What is output? -- 1\n2\n3\n4 -- [x] 1\neven\n3\neven -- even\neven -> For every number the branch decides again: odd numbers themselves, even ones as a word. - -runde: - for (int i = 1; i <= 3; i++) { - String line = ""; - for (int j = 1; j <= i; j++) { - line = line + "*"; - } - System.out.println(line); - } -frage: What is output? -- ***\n***\n*** -- [x] *\n**\n*** -- ***\n**\n* -> The inner loop runs up to i. That's why every line gets one star longer. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: What happens? -- 1 to 3 is output three times. -- 1 to 3 is output once. -- [x] Java reports an error. -> Two loops inside each other need two different counter variables, for example `row` and `column`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can put a branch inside a loop. → A branch inside a loop -- I can put a loop inside another loop. → A loop inside a loop -- I can explain that the inner loop runs all the way through in every pass of the outer one. → A loop inside a loop -- I can write nested loops and branches in Java. → Now in Java -::: - -## Quiz - -::: quiz -? The outer loop runs 3 times, the inner one 4 times. How often does the inside of the inner loop run in total? -→ A loop inside a loop -- [ ] 7 times -- [x] 12 times -- [ ] 4 times -> In each of the 3 outer passes, the inner loop runs 4 times, so 3 · 4 = 12. - -? How can you tell whether a number is even? -→ A branch inside a loop -- [ ] `number / 2 == 0` -- [x] `number % 2 == 0` -- [ ] `number == 2` -> % gives the remainder of a division. For even numbers, the remainder when dividing by 2 is zero. - -? There is a branch inside a for loop. How often is the condition of the branch checked? -→ A branch inside a loop -- [ ] Once -- [x] Once per pass of the loop -- [ ] Only if it is true -> The branch is part of the inside and runs in every pass. - -? How can you tell in Java what belongs to a loop? -→ Now in Java -- [ ] By the colour -- [x] By the curly brackets and the indentation -- [ ] By the semicolons -::: diff --git a/web/lessons/en/15-arrays.md b/web/lessons/en/15-arrays.md deleted file mode 100644 index f2a73902..00000000 --- a/web/lessons/en/15-arrays.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -titel: Arrays -bereich: Basics -kurz: Store many values under one name, reach them with the index and go through them with a loop. ---- - -## Skills I will gain now - -::: kompetenzen -- create an array with values or with `new int[5]` -- explain that the index starts at 0 and that the last index is `length - 1` -- go through and fill an array with a for loop -- calculate the sum and the maximum of an array -- create an array in Java and go through it with a loop -::: - -## Many values under one name - -Imagine you want to store the points from five tests. With five variables `points1` to `points5` that quickly gets confusing. An **array** is a row of slots that all have the same name and the same data type. - -You create an array with a statement. After the data type you write square brackets `[]`: - -``` -int[] numbers = {2, 4, 6, 8, 10} -String[] names = {"Ada", "Ben", "Cem"} -``` - -The values in the curly brackets go straight into the slots. You can also create an empty array with a fixed number of slots: - -``` -int[] points = new int[5] -String[] names = new String[3] -``` - -With `new int[5]` all five slots hold zeros at first, with `new String[3]` empty texts. - -## The index starts at 0 - -Every slot has a number, the **index**. Counting starts at **0**. So with `int[] numbers = {2, 4, 6, 8, 10}`: - -- `numbers[0]` is 2 -- `numbers[1]` is 4 -- `numbers[4]` is 10 - -::: grafik l14-array.svg -Five slots with the indices 0 to 4. The index is above the slot, the value inside it. -::: - -**Why start at 0?** In computer science the index is not “which slot” but the **distance from the start**. The first slot is right at the start, so 0 steps away. The second is 1 step away, the third 2 steps. You know this from a lift: the ground floor is **0**, and the first floor is one level above it. - -This gives you a rule worth remembering: with 5 slots the **last index is 4**, always one less than the number of slots. - -::: angeberwissen -Counting from 0 is pretty old. The programming language C from 1972 already works like this: the computer knows the address of the first slot, and for `numbers[i]` it simply calculates “start plus i slots further”. With i = 0 it stays at the start, which saves a calculation. Java, Python and JavaScript took this over. By the way, I count my horns from 0 as well: horn 0 on the left, horn 1 on the right. -::: - -With the index you read a value or write a new one into it: - -``` -output: numbers[0] -numbers[1] = 99 -``` - -> An array with 5 slots has the indices 0 to 4. `numbers[5]` does not exist. The simulation then reports an invalid expression. - -## The length of an array - -`numbers.length` tells you how many slots an array has. With five values it is 5. So the **last** index is always `numbers.length - 1`: - -``` -output: numbers.length -output: numbers[numbers.length - 1] -``` - -The second line outputs the last value, however long the array is. VisuStruct also understands `numbers.length()`. - -::: übung l14-erster-letzter -titel: First and last value -auftrag: Create the array `int[] values = {4, 8, 15, 16, 23, 42}`. Then output on three lines: the first value, the last value and the number of values. - For the last value, use the length and do not simply write the index 5. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Counting starts at 0, so the first slot has the index 0. And what is the index of the last slot? -tipp: The first value is `values[0]`. The last one is `values[values.length - 1]`. The number of values is `values.length`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Go through an array with a loop - -An array shows its strength together with a **for loop**. The counter variable `i` runs from 0 to the last index and is used as the index: - -``` -int i = 0 -i < numbers.length -i++ -``` - -In the first pass `i` is 0, in the last one 4. Because the condition is `i < numbers.length`, the loop fits any length. - -::: diagramm l14-array-durchlaufen.visustruct -The for loop outputs all five values one after another. Under Variables in the simulation, watch i count from 0 to 4. -::: - -This is also how you fill an empty array with inputs. As the target of the input you write the slot `names[i]`: - -::: diagramm l14-namen-eingeben.visustruct -Three names are asked for and written into the slots 0, 1 and 2. -::: - -::: übung l14-rueckwaerts -titel: Print backwards -auftrag: Create an array for 4 whole numbers and read in the 4 numbers with a loop. Then output them in **reverse** order, each on its own line. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: You need two loops: one for reading in from front to back and one for outputting from back to front. -tipp: You create the array with `int[] numbers = new int[4]`. The input is `input: numbers[i] "Enter a number"`. For the second loop you start at `int i = numbers.length - 1`, check `i >= 0` and count down with `i--`. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr** from Vienna was a famous Hollywood film star in the 1940s. In her spare time she liked to invent things. In 1942, together with the composer George Antheil, she received a patent for a radio link that an enemy should not be able to jam: sender and receiver jump together through a list of **88 frequencies**, as many as a piano has keys. The list was punched into a paper strip like the roll of a self-playing piano. Both go through the same list in the same order, just like your loop goes through an array with `i`. Anyone who listens to or jams only one frequency gets almost nothing. - -This method, **frequency hopping**, is used today in Bluetooth, for example. She did not invent it all by herself; there were earlier ideas. But her patent is seen as an important forerunner, and in 1997 she received an award for it. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), actress and inventor, 1944 (publicity photo: [unknown](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), public domain). -::: - -## Sum and maximum - -Two tasks come up with arrays again and again: - -- **Sum:** You start with `sum = 0` and add every value in the loop. -- **Maximum:** First you remember the value in slot 0 as the largest so far. In the loop you check every value. If it is larger, it becomes the new maximum. - -::: diagramm l14-summe-maximum.visustruct -Sum and maximum in one single pass. The maximum starts with points[0] and not with 0, so that it is also right for negative numbers. -::: - -::: übung l14-summe-max -titel: Sum and maximum -auftrag: Read 5 whole numbers into an array. Then output **Sum: …** and on the next line **Maximum: …**. -test: 3, 9, 2, 9, 1 -> Sum: 24 | Maximum: 9 -test: 10, 20, 30, 40, 50 -> Sum: 150 | Maximum: 50 -test: -5, -2, -9, -1, -3 -> Sum: -20 | Maximum: -1 -braucht: forSchleife, verzweigung -tipp: Go through the array with a loop. Add every value to the sum and remember the largest value seen so far. -tipp: Calculate the sum and the maximum in variables of their own and only output them at the end, e.g. with `output: "Sum: " + sum`. If your maximum starts at 0, the third test fails. -tipp: The outline: create the array and read it in with a loop. Then `int sum = 0` and `int max = numbers[0]`. A second loop calculates `sum = sum + numbers[i]`, and a branch `numbers[i] > max` sets `max = numbers[i]` in its true branch. Finally the two outputs. -lösung: l14-summe-max-loesung.visustruct -::: - -## Now in Java - -Going through an array looks like this in Java: - -```java editor -int[] numbers = {2, 4, 6, 8, 10}; -for (int i = 0; i < numbers.length; i++) { - System.out.println(numbers[i]); -} -``` - -- `int[]` means “array of whole numbers”. The values are in curly brackets. -- You create an empty array with 5 slots with `new int[5]`. -- `numbers[i]` is the slot with index `i`, `numbers.length` is the length. - -If you output the whole array, that is `System.out.println(numbers);`, Java does not show the numbers. You only see a text like `[I@1b6d3586`. This is the **reference**, the place where the array is stored in memory. `Arrays.toString(numbers)` shows the content: `[2, 4, 6, 8, 10]`. For this you write `import java.util.Arrays;` at the very top. - -::: übung l15-java-noten -titel: Sum of grades in Java -code: java -auftrag: Create the array `grades` in Java with the values 2, 3, 1 and 4. Add up all grades with a for loop and output **Sum: 10**. -test: - -> Sum: 10 -braucht: forSchleife -tipp: Create `int sum = 0;` before the loop. The loop runs from 0 to `grades.length - 1`. -tipp: `int[] grades = {2, 3, 1, 4};`, then `for (int i = 0; i < grades.length; i++) { sum = sum + grades[i]; }` and finally `System.out.println("Sum: " + sum);`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Fill in the gaps: arrays - -::: lückentext -titel: Finding the maximum -auftrag: The program looks for the largest value in the array. Fill in the missing words and symbols. -```java -[[int]][] values = {3, 7, 1, 9, 4}; -int max = values[0]; -[[for]] (int i = 1; i < values.[[length]]; i++) { - if (values[i] [[>]] max) { - max = values[i]; - } -} -System.out.println("Maximum: " + max); -``` -erklärung: `int[]` is an array of whole numbers, `values.length` is its length. `max` starts with the first value, and the loop compares all the others with it. Output: **Maximum: 9** -::: - -## Find the bug: temperatures - -::: übung l15-java-fehler -titel: Fix the highest temperature -code: java -auftrag: In January the temperatures -5, -2, -8 and -3 degrees were measured. The program should output the highest, so **Highest temperature: -2**. But it claims 0 degrees, although it was never that warm. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l15-java-fehler-start.visustruct -test: - -> Highest temperature: -2 -braucht: forSchleife, verzweigung -tipp: What value does `max` start with? Is any of the temperatures greater than this start value? -tipp: `max` has to start with a value from the array: `int max = temperatures[0];` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l15-java-laeuft-nicht -titel: Add up the points -code: java -auftrag: The array `points` holds 4, 7, 2 and 5. The program should add them all up and output **Sum: 18**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Sum: 18 -braucht: forSchleife -tipp: The Code area underlines the wrong word in red. How do you write the length of an array? -tipp: `length` is written all in small letters: `i < points.length` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake** to do with arrays, and the bomb is ticking. Tap the wrong line before time runs out. If the parts of a line have a border, tap exactly the wrong part. Six rounds, 30 seconds each. -zeit: 30 - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - for (int i = 0; i <= numbers.length; i++) { - System.out.println(numbers[i]); - } -frage: Should output all five values. -fehler: 2 -stelle: i <= numbers.length -besser: for (int i = 0; i < numbers.length; i++) { -> With `<=`, i runs up to 5. There is no slot `numbers[5]`. - -runde: - String[] names = {"Ada", "Ben", "Cem"}; - System.out.println(names[1]); -frage: Should output the first name, Ada. -fehler: 2 -stelle: names[1] -besser: System.out.println(names[0]); -> Counting starts at 0. `names[1]` is already Ben. - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - System.out.println(numbers[numbers.length]); -frage: Should output the last value, 10. -fehler: 2 -stelle: numbers[numbers.length] -besser: System.out.println(numbers[numbers.length - 1]); -> The last index is always one less than the length. - -runde: - int[] points = {3, 9, 2}; - int sum = 0; - for (int i = 0; i < points.length; i++) { - sum = points[i]; - } - System.out.println(sum); -frage: Should output the sum 14. -fehler: 4 -stelle: points[i] -besser: sum = sum + points[i]; -> Like this every value overwrites the one before, and at the end sum only holds 2. - -runde: - int[] points = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < points.length; i++) { - if (points[i] > max) { - max = points[i]; - } - } - System.out.println(max); -frage: Should output the maximum -2. -fehler: 2 -stelle: 0 -besser: int max = points[0]; -> None of the numbers is greater than 0, so max would stay 0. That's why the maximum starts with the first slot. - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - for (int i = numbers.length; i >= 0; i--) { - System.out.println(numbers[i]); - } -frage: Should output the values backwards, from 10 to 2. -fehler: 2 -stelle: int i = numbers.length -besser: for (int i = numbers.length - 1; i >= 0; i--) { -> Backwards you start at the last index, and that is `numbers.length - 1`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create an array with values or with `new int[5]`. → Many values under one name -- I can explain that the index starts at 0 and that the last index is `length - 1`. → The index starts at 0 -- I can go through and fill an array with a for loop. → Go through an array with a loop -- I can calculate the sum and the maximum of an array. → Sum and maximum -- I can create an array in Java and go through it with a loop. → Now in Java -::: - -## Quiz - -::: quiz -? What does `output: numbers[2]` output with `int[] numbers = {5, 10, 15, 20}`? -→ The index starts at 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Counting starts at 0: numbers[0] is 5, numbers[1] is 10, numbers[2] is 15. - -? An array has 8 slots. Which index is the last one? -→ The index starts at 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> The last index is always length - 1. - -? Which condition fits a for loop that goes through all values of `numbers` and starts at `int i = 0`? -→ Go through an array with a loop -- [x] i < numbers.length -- [ ] i <= numbers.length -- [ ] i > numbers.length -> With <= the loop would also read numbers[length], and that slot does not exist. - -? What should the maximum start with before the loop begins? -→ Sum and maximum -- [ ] Always with 0 -- [x] With the first value of the array -- [ ] With the length of the array -> If it starts with 0, then with only negative numbers the result would be 0, although 0 is not in the array at all. - -? What does `output: numbers.length` output with `int[] numbers = {5, 10, 15, 20}`? -→ Many values under one name -- [ ] 3 -- [x] 4 -- [ ] 20 -> length is the number of slots, not the last index and not the last value. - -? Which line creates an array with three numbers in Java? -→ Now in Java -- [x] int[] numbers = {1, 2, 3}; -- [ ] int numbers = [1, 2, 3]; -- [ ] array numbers = (1, 2, 3); -::: diff --git a/web/lessons/en/16-zweidimensionale-arrays.md b/web/lessons/en/16-zweidimensionale-arrays.md deleted file mode 100644 index 11264a18..00000000 --- a/web/lessons/en/16-zweidimensionale-arrays.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -titel: Two-dimensional arrays -bereich: Basics -kurz: Store values in rows and columns, like the seats in a cinema, and go through them with two nested loops. ---- - -## Skills I will gain now - -::: kompetenzen -- create a 2D array and explain what the two numbers mean -- read and write a slot with a row and a seat -- tell `seats.length` and `seats[0].length` apart -- explain in which order two nested loops visit the slots -- create and go through a two-dimensional array in Java -::: - -## A cinema as a table - -A cinema has several **rows**, and every row has several **seats**. For every seat we want to remember whether its ticket has already been sold. That is a yes-or-no question, so the data type `boolean` fits: `true` means sold, `false` means free. - -A normal array is one single row of slots. For the cinema we need a **table** with rows and columns. That is a **two-dimensional array**, 2D array for short. You write two pairs of square brackets for it: - -``` -boolean[][] seats = new boolean[3][5] -``` - -That gives 3 rows with 5 seats each, so 15 slots. At the start every slot holds `false`: all seats are free. - -::: grafik kino-saal.svg -The first index is the row, the second the seat. Both are counted from 0. -::: - -## Reach a slot - -For a slot you now need **two** indices: first the row, then the seat. - -``` -seats[1][2] = true -``` - -Now seat 2 in row 1 is sold. Reading works the same way: `seats[1][2]` is now `true`. - -You can also ask for the size: - -- `seats.length` is the number of **rows**, here 3. -- `seats[0].length` is the number of **seats** in row 0, here 5. - -::: diagramm kino-verkaufen.visustruct -Three tickets are sold. After that, the diagram outputs the size of the cinema and whether row 1, seat 2 is sold. Watch under Variables in the simulation: seats is shown row by row. -::: - -> Just like with a normal array, there is no slot `seats[3][0]`, because the rows are called 0, 1 and 2. The simulation then reports an invalid expression. - -## Two loops inside each other - -To look at **all** seats, you need two for loops inside each other: - -- The **outer loop** counts the row: `row` runs from 0 to 2. -- The **inner loop** counts the seat: `seat` runs from 0 to 4. - -::: grafik kino-schleifen.svg -Blue is the outer loop, orange the inner one. The numbers show the order in which the seats come up. -::: - -For every row, the inner loop runs **all the way** through once. Only when `seat` has visited all five seats does the outer loop move `row` on by one. Then `seat` starts at 0 again. That way all 3 · 5 = 15 seats come up exactly once. - -The conditions are `row < seats.length` and `seat < seats[row].length`. That way the loops fit a cinema of any size. - -::: diagramm kino-saalplan.visustruct -The seating plan: for every row, a line of X (sold) and O (free) is put together and output. Go through it with Step and watch how seat keeps starting again from the beginning while row only slowly counts on. -::: - -::: angeberwissen -Every phone photo is a 2D array! A photo with 4000 · 3000 pixels has 3000 rows with 4000 slots each, so 12 million pixels. Every slot holds the colour of one dot. When you make a photo brighter, the phone runs over all the pixels with two nested loops. My profile picture, by the way, has only one slot: brown. -::: - -## Exercise: Count free seats - -::: übung kino-frei -titel: Count free seats -auftrag: In the start diagram three tickets have already been sold. Use two nested loops to count how many seats are still **free**, and output e.g. `Free: 12`. -start: kino-frei-start.visustruct -test: - -> Free: 12 -braucht: forSchleife, verzweigung -tipp: Just like for the seating plan, you go over all seats with two loops. Instead of outputting something, you count. -tipp: Before the loops, create `int free = 0`. A seat is free if `seats[row][seat]` is false. The condition for that is `!seats[row][seat]`. -lösung: kino-frei-loesung.visustruct -::: - -## Exercise: Sell a ticket - -::: übung kino-karte -titel: Sell a ticket -auftrag: The start diagram asks for a row and a seat. If the seat is already sold, output `Already sold`. Otherwise mark it as sold and output e.g. `Sold: row 0, seat 4`. -start: kino-karte-start.visustruct -test: 1, 2 -> Already sold -test: 0, 4 -> Sold: row 0, seat 4 -test: 2, 0 -> Sold: row 2, seat 0 -braucht: verzweigung -tipp: First check whether the seat is sold. Depending on the answer, something different happens. -tipp: You do not need a loop here. A branch with the condition `seats[row][seat]` is enough. In the false path you set the slot to `true`. -lösung: kino-karte-loesung.visustruct -::: - -## Exercise: Times table grid - -::: übung kino-einmaleins -titel: Times table grid -auftrag: Create `int[][] table = new int[10][10]` and fill it with the times tables up to 10 using two loops. Then ask for two numbers from 1 to 10 and read the result from the table, e.g. `3 · 4 = 12`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: First two nested loops fill the table; after that you read only one single slot. -tipp: The indices run from 0 to 9, but the numbers from 1 to 10. That is why the slot `table[i][j]` holds the value `(i + 1) * (j + 1)`, and for 3 · 4 you read `table[a - 1][b - 1]`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Now in Java - -The cinema looks like this in Java: - -```java editor -boolean[][] seats = new boolean[3][5]; -seats[0][2] = true; -seats[1][1] = true; -seats[1][2] = true; -System.out.println("Rows: " + seats.length); -System.out.println("Seats per row: " + seats[0].length); -System.out.println("Row 1, seat 2 sold? " + seats[1][2]); -``` - -- Two pairs of square brackets `[][]` stand for a table: first the row, then the seat. -- `seats.length` is the number of rows, `seats[0].length` the number of seats in one row. -- With fixed values you write every row in its own curly brackets: `int[][] t = {{1, 2}, {3, 4}};` - -::: übung l16-java-punkte -titel: Add up the points -code: java -auftrag: Three players have played two rounds each. Create the table `points` in Java with the rows {3, 5}, {4, 1} and {2, 2}. Add up all points with two nested for loops and output **Sum: 17**. -test: - -> Sum: 17 -braucht: forSchleife -tipp: The outer loop goes through the rows, the inner one through the seats of a row. -tipp: `int[][] points = {{3, 5}, {4, 1}, {2, 2}};` and in the inner loop `sum = sum + points[i][j];`. The inner loop runs up to `points[i].length`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Fill in the gaps: tables - -::: lückentext -titel: Adding up all fields -auftrag: The program adds up all numbers in the table. Fill in the missing words and symbols. -```java -[[int]][][] grid = {{1, 2, 3}, {4, 5, 6}}; -int sum = 0; -for (int r = 0; r < grid.[[length]]; r++) { - for (int c = 0; c < grid[r].[[length]]; c++) { - sum = sum [[+]] grid[r][c]; - } -} -System.out.println("Sum: " + sum); -``` -erklärung: `int[][]` is a table. `grid.length` is the number of rows (2), `grid[r].length` the number of fields in row `r` (3). Output: **Sum: 21** -::: - -## Find the bug: points - -::: übung l16-java-fehler -titel: Fix player 2 -code: java -auftrag: In the table `points` each row stands for one player. The program should add up the points of player 2 (row 1), so **Player 2: 11**. Instead it reports an error. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l16-java-fehler-start.visustruct -test: - -> Player 2: 11 -braucht: forSchleife -tipp: In `points[a][b]`, `a` is the row and `b` the place in the row. Which number stands for the row of player 2? -tipp: The indices are swapped: `points[1][j]` instead of `points[j][1]`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l16-java-laeuft-nicht -titel: Read the score -code: java -auftrag: In the table `points` each row is a player and each column a round. The program should output player 2’s points in round 1, so **Player 2, round 1: 4**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Player 2, round 1: 4 -braucht: anweisung -tipp: The cells `[1][0]` are right. Look at the name of the table. -tipp: The table is called `points` with a small p: `points[1][0]` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which seat? - -::: spiel frage -titel: Game: Which seat? -intro: Ludwig is selling cinema tickets. First the row, then the seat, both from 0. Seven rounds. - -runde: - boolean[][] seats = new boolean[3][5]; - System.out.println(seats.length); -frage: What is output? -- [x] 3 -- 5 -- 15 -> `seats.length` is the number of rows. - -runde: - boolean[][] seats = new boolean[3][5]; - System.out.println(seats[0].length); -frage: What is output? -- 3 -- [x] 5 -- 15 -> `seats[0]` is row 0. Its length is the number of seats in this row. - -runde: - int[][] table = new int[4][6]; -frage: How many slots does the table have? -- 10 -- [x] 24 -- 35 -> 4 rows with 6 seats each: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: What is output? -- 2 -- [x] 3 -- 4 -> Row 1 is `{3, 4}`, and seat 0 in it is the 3. - -runde: - boolean[][] seats = new boolean[3][5]; - seats[3][0] = true; -frage: What happens? -- The first seat in row 3 is sold. -- The first seat in the last row is sold. -- [x] There is an error, because the rows are called 0, 1 and 2. -> Just like with a normal array, the last index is one less than the length. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int sum = 0; - for (int row = 0; row < t.length; row++) { - for (int seat = 0; seat < t[row].length; seat++) { - sum = sum + t[row][seat]; - } - } - System.out.println(sum); -frage: What is output? -- 6 -- 15 -- [x] 21 -> The two loops visit all six slots: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Two loops go through the cinema `new boolean[3][5]`. Which slot comes right after `seats[0][4]`? -- `seats[0][5]` -- [x] `seats[1][0]` -- `seats[1][4]` -> Row 0 is done. The outer loop counts on, and the seat starts again at 0. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a 2D array and explain what the two numbers mean. → A cinema as a table -- I can read and write a slot with a row and a seat. → Reach a slot -- I can tell `seats.length` and `seats[0].length` apart. → Reach a slot -- I can explain in which order two nested loops visit the slots. → Two loops inside each other -- I can create and go through a two-dimensional array in Java. → Now in Java -::: - -## Quiz - -::: quiz -? How many slots does `boolean[][] seats = new boolean[4][6]` have? -→ A cinema as a table -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 rows with 6 seats each: 4 · 6 = 24. - -? What is in all slots after `new boolean[3][5]`? -→ A cinema as a table -- [ ] true -- [x] false -- [ ] Nothing, the slots are empty -> A new boolean array is filled with false. In the cinema that means: all seats are free. - -? Two nested loops run over 3 rows with 5 seats. How often does the inside of the **inner** loop run? -→ Two loops inside each other -- [ ] 3 times -- [ ] 8 times -- [x] 15 times -> For each of the 3 rows the inner loop runs 5 times: 3 · 5 = 15. - -? Match what each expression means in the cinema. -→ Reach a slot -= seats.length -> Number of rows -= seats[0].length -> Number of seats in row 0 -= seats[2][4] -> Row 2, seat 4 -= seats[1][0] = true -> Seat 0 in row 1 is sold - -? What does `seats.length` give for `new boolean[3][5]`? -→ Reach a slot -- [x] 3, the number of rows -- [ ] 5, the number of seats -- [ ] 15, all seats together -::: diff --git a/web/lessons/en/17-zeichenketten.md b/web/lessons/en/17-zeichenketten.md deleted file mode 100644 index f856513e..00000000 --- a/web/lessons/en/17-zeichenketten.md +++ /dev/null @@ -1,526 +0,0 @@ ---- -titel: Strings -bereich: Basics -kurz: Work with text: read single characters, cut out parts, search, compare and split. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the characters of a String are also counted from 0 -- work with `length()` and `charAt(i)` -- tell a `char` from a String and use the character code with `(int)` and `(char)` -- cut a part out of a text with `substring` -- use `indexOf`, `contains`, `toUpperCase` and `equals` -- go through a text character by character with a loop -- split a text into a String array with `split` -- read a text in Java and compare it with `equals` -- use more methods such as `lastIndexOf`, `replace`, `compareTo` and `Integer.parseInt` -- split a text from a file into lines with `split("\n")` -- look up a method in the Java API and read its description -::: - -## Text is a row of characters - -You already know `String`: `String name = "Ludwig"`. In computer science such a text is called a **string**, because the characters hang one after another like beads on a string. Every character has a place with an index, just like in an array. Here, too, **counting starts at 0**: the first character is 0 steps away from the start. - -::: grafik zk-zeichen.svg -The nine characters of “Billygoat” with their indices 0 to 8. -::: - -A String comes with **methods**. You write the name of the variable, a dot and the method: - -- `word.length()` gives the number of characters, here 9. -- `word.charAt(0)` gives the character with the index 0, here `'B'`. - -> With Strings you write `length()` **with** brackets, with arrays `length` **without**. Sadly, that is just how Java is. - -Just like in an array, the last character has the index `length() - 1`. - -## A single character: char - -Java has its own data type for a single character: **char**, short for *character*. A `char` goes in **single** quotation marks, a String in double ones: - -- `'C'` is a `char`: exactly one character. -- `"C"` is a String: a text that is only one character long. -- A `char` always holds exactly one character. There is no empty `char` like `''`, but there is an empty String `""`. - -`charAt` gives you such a character from a text: - -``` -char first = word.charAt(0) -char last = word.charAt(word.length() - 1) -``` - -You compare two `char` values with `==`: `word.charAt(0) == 'B'` is `true`. - -Behind every character there is a number, its **character code**. Which number belongs to which character is listed in the ASCII table: `'A'` has the code 65, `'B'` 66, `'C'` 67 and so on up to `'Z'` with 90. With `(int)` you get the code of a character, with `(char)` a code turns back into a character: - -``` -int code = (int) 'C' -code = code + 1 -char next = (char) code -``` - -After that `code` is 68 and `next` is `'D'`. This is how you shift a letter in the alphabet, and that is exactly what you will need later for the Caesar cipher. - -## Cut out parts - -With `substring` you cut a part out of the text: - -- `word.substring(0, 5)` gives the characters from index 0 **up to just before** index 5, so `"Billy"`. -- `word.substring(5)` gives everything from index 5 to the end, so `"goat"`. - -The end is **not** included. Handy: `substring(0, 5)` gives exactly 5 characters. - -::: diagramm zk-zeichen.visustruct -All the methods from this lesson in one example. Go through it in the simulation and compare every output with the picture. -::: - -## Search and compare - -More methods you often need: - -| Method | Meaning | Example with `"Billygoat"` | -|---|---|---| -| `indexOf("goat")` | Where does the text start? | 5 | -| `indexOf("x")` | not found | -1 | -| `contains("lly")` | Does the text appear? | true | -| `toUpperCase()` | all capitals | BILLYGOAT | -| `toLowerCase()` | all small letters | billygoat | -| `equals("Billygoat")` | same text? | true | - -> In Java you compare two Strings with `equals`, not with `==`. Two single characters (`char`), on the other hand, you compare with `==`, e.g. `word.charAt(i) == 'e'`. - -::: angeberwissen -Try `"Billygoat".equals("billygoat")`. The answer is false! For a computer, B and b are completely different characters. Inside, every character is stored as a number: B is 66 and b is 98. This numbering is called ASCII and dates from 1963. Baah, so for a computer Ludwig and ludwig are two different goats. I am the one with the capital L. -::: - -## Character by character - -With a for loop you go through a text character by character. The index `i` runs from 0 to `word.length() - 1`, just like with an array: - -::: diagramm zk-zaehlen.visustruct -Counts how often the letter e appears. In the simulation, try “cheese” and watch i and count. -::: - -## Split with split - -Often several pieces of information sit in one single line, separated by a character, e.g. `"Ludwig;billy goat;7"`. With `split` you cut the line apart at this character. The result is a **String array**: - -``` -String[] parts = line.split(";") -``` - -After that, `parts[0]` is `"Ludwig"`, `parts[1]` is `"billy goat"` and `parts[2]` is `"7"`. With `parts.length` you find out how many parts there are. - -::: diagramm zk-split.visustruct -The line is split at the semicolon, and every part is output with its index. -::: - -> CSV files, which you know from spreadsheets, are built the same way: one line per record, the values separated by a comma or semicolon. - -## Exercise: Initials - -::: übung zk-initialen -titel: Initials -auftrag: Ask for a first name and a last name and output the initials. So for Ludwig Billygoat that is `L. B.` -test: Ludwig, Billygoat -> L. B. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: The initial is the first letter of a name. Which method gives the character at a particular place? -tipp: `firstName.charAt(0)` gives the first letter. The output is then `firstName.charAt(0) + ". " + lastName.charAt(0) + "."` -lösung: zk-initialen-loesung.visustruct -::: - -## Exercise: Backwards - -::: übung zk-rueckwaerts -titel: Backwards -auftrag: Ask for a word and output it backwards. Ludwig becomes `giwduL`. -test: Ludwig -> giwduL -test: Anna -> annA -test: a -> a -braucht: forSchleife -tipp: Go through the word character by character, but from back to front, and build a new text as you go. -tipp: Create `String backwards = ""`. The loop starts at the last index `word.length() - 1` and counts down to 0 with `i--`. In every pass you add `word.charAt(i)` at the end. -tipp: The outline: input `word`, then `String backwards = ""`. For loop with start `int i = word.length() - 1`, condition `i >= 0` and step `i--`. Inside it `backwards = backwards + word.charAt(i)`. Below the loop `output: backwards`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Exercise: Count words - -::: übung zk-woerter -titel: Count words -auftrag: Ask for a sentence and output how many words it has, e.g. `Words: 4` for “Ludwig is a goat”. The words are separated by exactly one space. -test: Ludwig is a goat -> Words: 4 -test: Hello -> Words: 1 -test: I like goats -> Words: 3 -braucht: anweisung -tipp: There is always a space between the words. Which method splits a text at a character? -tipp: Split the sentence into a String array with `sentence.split(" ")`. Its length is the number of words. -tipp: The whole program has three boxes: the input `sentence`, then `String[] words = sentence.split(" ")` and finally `output: "Words: " + words.length`. -lösung: zk-woerter-loesung.visustruct -::: - -## Now in Java - -The methods in this lesson already are Java. A whole program with a text looks like this: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("What is your name?"); -String name = scanner.next(); -System.out.println("Hello " + name.toUpperCase() + "!"); -System.out.println("Your name has " + name.length() + " letters."); -``` - -- `scanner.next()` reads one word, that is text up to the next space. For numbers you keep using `scanner.nextInt()`. -- Text goes in **double** quotes `"Ludwig"`, a single character in **single** quotes `'L'`. -- `length()`, `toUpperCase()` and the other methods always have round brackets after them, even when nothing is inside. -- You compare two texts with `equals`. Every letter counts: `"Ludwig".equals("ludwig")` is false. - -::: übung l17-java-passwort -titel: Check the password -code: java -auftrag: Write a Java program that asks for the password. If it is **Ludwig**, it outputs **Welcome!**, otherwise **Wrong password**. - Mind upper and lower case: `ludwig` is wrong. -test: Ludwig -> Welcome! -test: ludwig -> Wrong password -test: Geißbock -> Wrong password -braucht: verzweigung -tipp: Read the password with `scanner.next()` into a String variable. Then compare it in a selection. -tipp: The condition is `input.equals("Ludwig")`. In Java, `==` does not reliably compare texts. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Fill in the gaps: texts - -::: lückentext -titel: Examining a word -auftrag: Fill in the missing methods and words. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("A word?"); -[[String]] word = scanner.next(); -System.out.println("Length: " + word.[[length]]()); -System.out.println("Capitals: " + word.[[toUpperCase]]()); -System.out.println("First letter: " + word.[[charAt]](0)); -if (word.[[equals]]("Ludwig")) { - System.out.println("Hello Ludwig!"); -} -``` -erklärung: `length()` counts the characters, `toUpperCase()` makes everything upper case, `charAt(0)` gives the first character. You compare texts with `equals`, not with `==`. -::: - -## Find the bug: initials - -::: übung l17-java-fehler -titel: Fix the initials -code: java -auftrag: The program reads a first name and a last name and should output the initials. For Ludwig Geißbock that is **L. G.** But it outputs **u. e.** - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: At which index does Java start counting? -tipp: The first character has the index 0: `first.charAt(0)` and `last.charAt(0)`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l17-java-laeuft-nicht -titel: Count the letters -code: java -auftrag: The program should output the first letter and the length of **Ludwig**, so **L has 6 letters**. But it doesn’t run, because two methods are spelled wrong. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L has 6 letters -braucht: anweisung -tipp: Method names are camelCase too: they start small, and every further word starts with a capital. -tipp: Correct are `name.charAt(0)` with a capital A and `name.length()` all small. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Even more methods - -A String can do much more. You will often need these methods too: - -| Method | Meaning | Example | Result | -|---|---|---|---| -| `lastIndexOf("l")` | Where is the last l? | `"Billygoat".lastIndexOf("l")` | 3 | -| `isEmpty()` | Is the text empty? | `"".isEmpty()` | true | -| `equalsIgnoreCase(…)` | same text, whether upper or lower case? | `"Billygoat".equalsIgnoreCase("BILLYGOAT")` | true | -| `replace(old, new)` | replaces every occurrence | `"Billygoat".replace("Billy", "Nanny")` | Nannygoat | -| `repeat(n)` | the text n times in a row | `"Baa".repeat(3)` | BaaBaaBaa | -| `concat(…)` | adds text to the end, like `+` | `"Billy".concat("goat")` | Billygoat | -| `compareTo(…)` | Which comes first in the alphabet? | `"Anna".compareTo("Ben")` | -1 | - -`compareTo` returns a number: **negative** if the text comes earlier in the alphabet, **0** if both are the same, and **positive** if it comes later. You can use it to sort names. - -Two methods do not belong to a text but to a class. That is why the class name comes first: - -- `Integer.parseInt("7")` turns the text "7" into the number 7. You can calculate with it. -- `String.valueOf(7)` turns the number 7 into the text "7". - -::: diagramm zk-mehr.visustruct -All the new methods in one example. Go through it in the simulation and compare every output with the table. -::: - -## Text from a file - -Programs often get their text from a **file**. Princess Zora has written Ludwig a letter. It is ready as the file `letter.txt`: - -::: datei letter.txt -Dear Ludwig, -the fox has locked me in his tower. -The door has a lock with a secret word. -You can only find the word with Java. -Please help me! -Yours, Princess Zora -::: - -These lines read the whole file into a String: - -``` -import java.nio.file.*; - -String letter = Files.readString(Path.of("letter.txt")); -``` - -You will learn exactly how reading works later, in a lesson of its own about files. For now this is enough: afterwards the whole letter is in `letter`, with all its lines. - -At the end of every line there is an invisible character for the **line break**. In Java you write it as `"\n"`. So `letter.split("\n")` splits the letter into its lines. - -::: übung l17-java-brief -titel: Where is the fox? -code: java -auftrag: The start code already reads the letter. Split it into lines and output **Lines:** and the number of lines. Then find the line that contains **fox** and output **Fox in line** and its number. The first line has the number 1. -start: l17-java-brief-start.visustruct -test: - -> Lines: 6 | Fox in line 2 -braucht: forSchleife, verzweigung -tipp: `String[] lines = letter.split("\n");` splits the letter. Then a for loop goes through all the lines. -tipp: In the loop you check `lines[i].contains("fox")`. The index starts at 0, so you output `(i + 1)`. You need the brackets so that Java calculates instead of adding 1 to the text. -lösung: l17-java-brief-loesung.visustruct -::: - -## The Java API - -Nobody knows all methods by heart, not even professionals. They look them up in the **Java API**. API stands for Application Programming Interface. It means the description of everything Java already brings along. - -You can find all methods of String here: [String in the Java API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Scroll down to the table **Method Summary**. Each row describes one method: - -| Column | Example | Meaning | -|---|---|---| -| Modifier and Type | `int` | What the method returns | -| Method | `lastIndexOf(String str)` | Name and parameters with their data type | -| Description | Returns the index within this string of the last occurrence … | What the method does | - -::: ludwig -The page is very long. Press Ctrl+F (on a Mac ⌘+F) and search for the name of the method to get there at once. -::: - -Look up these methods. They are not in this lesson: - -::: quiz -? Look up `strip()`. What does the method do? -- [ ] It removes all spaces in the text. -- [x] It removes the spaces at the beginning and the end. -- [ ] It cuts off the last character. -> Like `trim()`. But `strip()` also knows special spaces from other writing systems. - -? Look up `isBlank()`. What does `" ".isBlank()` return? -- [x] true -- [ ] false -- [ ] 3 -> isBlank checks whether the text is empty or only contains spaces. `" ".isEmpty()`, on the other hand, would be false. - -? Look up `toCharArray()`. Which data type does the method return? -- [ ] String -- [ ] String[] -- [x] char[] -> An array with all characters of the text, one element for each character. - -? Look up `indexOf` with two parameters. What does `"Billygoat".indexOf("l", 3)` return? -- [ ] 2 -- [x] 3 -- [ ] -1 -> The search only starts at index 3. The first l from there is at index 3. - -? Look up `startsWith`. Which data type does the method return? -- [x] boolean -- [ ] int -- [ ] String -> The column “Modifier and Type” says boolean: the answer is true or false. -::: - -## Game: Rescue the princess - -Now you help Ludwig. The fox has locked Princess Zora in his tower, and the gate only opens with a secret word. - -::: spiel prinzessin -titel: Game: Rescue the princess -intro: On the way to the tower Ludwig comes to **six stations**. Each one has a piece of Java code, and a number comes out. `alphabet.charAt(number)` turns the number into a letter that goes into the **crossword**. Solve the crossword, and the yellow squares spell the secret word for the gate. - -station: At the moat - String name = " Zora "; - int number = name.trim().length(); -frage: Which number is in `number`? -zahl: 4 -feld: 5 1 -tipp: `trim()` cuts off the spaces at the front and the back. Then count the characters. -> After `trim()` only `"Zora"` is left, and that is 4 characters. - -station: In the fox forest - String place = "Fox lair"; - int number = place.length(); -frage: Which number is in `number`? -zahl: 8 -feld: 4 4 -tipp: `length()` counts every character, the space too. -> F-o-x, the space and l-a-i-r make 8 characters. - -station: At the goat rock - String text = "Ludwig the goat"; - int number = text.indexOf(" "); -frage: Which number is in `number`? -zahl: 6 -feld: 4 6 -tipp: `indexOf` returns the position of the first space. The L has index 0. -> L u d w i g has the indexes 0 to 5, so the first space is at index 6. - -station: On the tower stairs - String word = "Princess"; - int number = word.indexOf("s", 7); -frage: Which number is in `number`? Look up `indexOf(String str, int fromIndex)` in the Java API. -zahl: 7 -feld: 6 6 -tipp: The search starts at index 7, and index 7 itself counts. Write the indexes under the letters. -> P r i n c e s s has the indexes 0 to 7. Searching from index 7, the first “s” is right at 7. - -station: In front of the fox den - int number = "Zora".compareTo("Zoe"); -frage: Which number is in `number`? -zahl: 13 -feld: 1 9 -tipp: `compareTo` looks for the first position where the texts differ and subtracts the character codes. `'r'` is 114, `'e'` is 101. -> “Zo” is the same, then `'r'` meets `'e'`: 114 − 101 = 13. - -station: At the tower gate - int number = Integer.parseInt("1" + "1") / 5; -frage: Which number is in `number`? -zahl: 2 -feld: 6 5 -tipp: First the texts are joined, then they become a number. With `int`, division drops the remainder. -> `"1" + "1"` is `"11"`, `parseInt` makes 11 of it, and 11 / 5 is 2 for whole numbers. - -kreuzwort: You already have the blue letters. Fill the other squares with Java terms. Click a clue to jump to its word. -- → 1 3 BOOLEAN: Data type for true or false -- → 4 1 STRING: Data type for text -- → 6 5 CHAR: Data type for a single character -- ↓ 1 6 LENGTH: Method that counts the characters of a string -- ↓ 2 1 ELSE: The “otherwise” part of a branch -- ↓ 3 3 BREAK: Jumps straight out of a loop -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: Ludwig stands in front of the gate. The lock compares your word with `equals`. Enter the secret word. - String word = "___"; - if (word.equals(secretWord)) { - gate.open(); - } -ende: Zora is free, and the fox has run off. The secret word was `BREAK`: with `break` you get out of any loop, and Zora gets out of the tower. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the characters of a String are also counted from 0. → Text is a row of characters -- I can work with `length()` and `charAt(i)`. → Text is a row of characters -- I can tell a `char` from a String and use the character code with `(int)` and `(char)`. → A single character: char -- I can cut a part out of a text with `substring`. → Cut out parts -- I can use `indexOf`, `contains`, `toUpperCase` and `equals`. → Search and compare -- I can go through a text character by character with a loop. → Character by character -- I can split a text into a String array with `split`. → Split with split -- I can read a text in Java and compare it with `equals`. → Now in Java -- I can use more methods such as `lastIndexOf`, `replace`, `compareTo` and `Integer.parseInt`. → Even more methods -- I can split a text from a file into lines with `split("\n")`. → Text from a file -- I can look up a method in the Java API and read its description. → The Java API -::: - -## Quiz - -::: quiz -? What does `"Ludwig".charAt(1)` give? -→ Text is a row of characters -- [ ] L -- [x] u -- [ ] d -> Counting starts at 0: L has the index 0, u the index 1. - -? What is `'C'`? -→ A single character: char -- [x] a `char`, that is, exactly one character -- [ ] a String -- [ ] a variable called C -> Single quotation marks stand for a single character, a `char`. `"C"` in double ones would be a String. - -? `'A'` has the character code 65. What does `(char) 67` give? -→ A single character: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 is A, 66 is B, 67 is C. With `(char)` the code turns back into a character. - -? What does `"Structure".substring(0, 6)` give? -→ Cut out parts -- [x] Struct -- [ ] Structu -- [ ] tructu -> The characters with the indices 0 to 5. The end 6 is not included. - -? A word has 7 characters. Which index does the last character have? -→ Text is a row of characters -- [ ] 7 -- [x] 6 -- [ ] 8 -> Just like with an array: always one less than the length. - -? How do you compare two Strings for the same content in Java? -→ Search and compare -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Match each method to what it gives for `"Billygoat"`. -→ Search and compare -= length() -> 9 -= charAt(0) -> B -= indexOf("goat") -> 5 -= substring(5) -> goat - -? Which condition checks in Java whether `input` contains the text Ludwig? -→ Now in Java -- [ ] input = "Ludwig" -- [ ] input == 'Ludwig' -- [x] input.equals("Ludwig") -> You compare texts with equals. Single quotes are only for a single character. - -? What does `Integer.parseInt("12") + 1` return? -→ Even more methods -- [ ] "121" -- [x] 13 -- [ ] an error message -> parseInt turns the text into a number. After that Java calculates as usual. -::: diff --git a/web/lessons/en/18-aufruf-und-aussprung.md b/web/lessons/en/18-aufruf-und-aussprung.md deleted file mode 100644 index 9b977298..00000000 --- a/web/lessons/en/18-aufruf-und-aussprung.md +++ /dev/null @@ -1,482 +0,0 @@ ---- -titel: Call and break -bereich: Basics -kurz: Split big tasks into subprograms and leave loops early. ---- - -## Skills I will gain now - -::: kompetenzen -- split a big task into smaller problems -- insert a call and build a subprogram in a tab of its own -- pass values to a subprogram with parameters -- hand back a return value with return and store it when calling -- explain why a subprogram cannot change a number but can change an array -- explain what a Break does and why it sits in a branch -- end a loop with a Break or through its condition -- recognise a method in Java and leave a loop with `break` -::: - -## Split big tasks - -Long structure charts quickly get confusing. That is why you split a big task into **smaller problems**. Every smaller problem gets a name of its own and a structure chart of its own, a **subprogram**. - -A quiz, for example, has three parts: greet, ask questions, show the result. The main program only names these three parts: - -::: diagramm l15-hauptprogramm.visustruct -The main program is made of just three calls. What happens in each part is in a structure chart of its own. -::: - -That way you see at a glance how the program is built. The details are somewhere else. - -## The call - -A **call** is a box with double vertical lines on the left and right. It means: run the subprogram with this name here, then carry on. - -- You drag **Call** from the Elements area and type the name into it, e.g. `showGreeting()`. -- The round brackets at the end show that it is a subprogram. That is how you write it in Java, Python and JavaScript, too. -- You build the subprogram itself as a structure chart of its own in a new tab. As the title you type the same name. - -::: diagramm l15-unterprogramm.visustruct -The subprogram showGreeting() as a structure chart of its own. -::: - -> **In the simulation:** When the simulation reaches a call, it looks among the open tabs for the structure chart with this title and runs it. Then it carries on in the main program. While the subprogram is running, the call box stays highlighted. A subprogram like this one, with only a name as its title, shares its variables with the main program. You will soon learn how a subprogram gets variables of its own. If there is no matching tab, the simulation skips the call and shows a note. - -::: angeberwissen -Why does your teacher always say “method” in Java and never “function” or “procedure”? Because in Java every subprogram has to belong to a class, and the subprograms of a class are called methods. The difference still exists, though: if a method hands back a result, it is really a function. If it has `void` in front, it hands back nothing and is really a procedure. Java just keeps it simple and calls both methods. So do I: my method is called Munching. -::: - -## Exercise: Add a call - -::: übung l15-titel -titel: Greeting with a call -auftrag: Imagine the subprogram `showTitle()` already exists. Build the main program: first it calls `showTitle()`. Then it asks for a name and outputs **Hello** and the name. - So when Ada is typed in, **Hello Ada** appears. -test: Ada -> Hello Ada -test: Ben -> Hello Ben -braucht: aufruf -tipp: The main program has three parts: the call, the input and the output. -tipp: Drag a Call from the Elements area right to the top and type `showTitle()` into it. Below it come the input `input: String name "What is your name?"` and the output. -lösung: l15-titel-loesung.visustruct -::: - -## Pass values: parameters - -Often a subprogram should work with values that are only known when it is called. Then it gets a **header line** as its title: - -``` -void showSum(int a, int b) -``` - -- `void` means: the subprogram does not hand back a result. -- `showSum` is the name. -- The brackets hold the **parameters**: variables with a data type that get their values when the subprogram is called. You separate several of them with commas. - -::: diagramm l15-zeige-summe.visustruct -The subprogram with the header line as its title. It calculates with a and b as if they were normal variables. -::: - -In the call box you write the values in the brackets, e.g. `showSum(3, 4)`. The first value goes into `a`, the second into `b`. Instead of numbers you can also use variables and calculations such as `showSum(x, x + 1)`. - -::: diagramm l15-zeige-summe-haupt.visustruct -The main program calls showSum twice with different values. Open both diagrams and start the simulation here. -::: - -> **Local variables:** The parameters and all variables created in the subprogram belong only to the subprogram. It gets **copies** of the values; more on that below under call by value. During the call, the simulation only shows the variables of the subprogram. - -## Hand back a result: return - -A subprogram can also hand back a result, the **return value**. Then the header line has the data type of the result instead of `void`: - -``` -int square(int x) -``` - -The statement `return result` hands back the value and ends the subprogram straight away. When you call it, you store the result in a variable: after `int q = square(5)`, `q` holds the value 25. - -::: diagramm l15-quadrat.visustruct -The subprogram square calculates and hands back the result with return. -::: - -::: diagramm l15-quadrat-haupt.visustruct -The main program stores the return value in q. Open both diagrams and try the simulation. -::: - -> A subprogram with a return value has to end with `return`. If the return is missing, the simulation reports an error. - -In the **code view**, the header line becomes a Java method (`public static int square(int x)`), a `def` in Python and a `function` in JavaScript. If the subprograms are open in other tabs, they appear in the code together with the main program. - -## Exercise: Calculate the area - -::: übung l15-flaeche -titel: Calculate the area -auftrag: Build two diagrams. The subprogram `int area(int a, int b)` hands back the area of a rectangle, that is a times b. The main program asks for the length and the width, calls `area` and outputs **Area:** and the result. - With 3 and 4, **Area: 12** appears. -test: 3, 4 -> Area: 12 -test: 5, 5 -> Area: 25 -test: 7, 0 -> Area: 0 -braucht: aufruf -tipp: You build two diagrams: a subprogram that only calculates and hands back the result, and a main program for input and output. -tipp: Open a new tab with **+** and give it the title `int area(int a, int b)`. One statement is enough inside: `return a * b`. In the main program you type `int f = area(length, width)` into the call box. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Copy or original: call by value and call by reference - -What happens if a subprogram changes its parameter? That depends on **what** is passed. - -**Call by value:** With `int`, `double`, `boolean`, `char` and `String`, the subprogram gets a **copy** of the value. If it changes its parameter, the caller's variable stays as it was. - -**Call by reference:** With an **array**, the subprogram does not get a copy but a reference to the same array. If it changes a slot, the slot is changed for the caller too. The header line for this looks like this: - -``` -void clear(boolean[] seats) -``` - -::: grafik uebergabe.svg -At the top, doubleIt gets a copy of number. At the bottom, row and seats point to the same array. -::: - -Try out both. Open the three diagrams and start the simulation in the main program: - -::: diagramm uebergabe-verdopple.visustruct -doubleIt only changes its copy x. -::: - -::: diagramm uebergabe-leeren.visustruct -clear sets all seats to false, for example when a showing is cancelled. -::: - -::: diagramm uebergabe-haupt.visustruct -number is still 5 after the call. The seats in row, on the other hand, are free after the call. -::: - -> If you want to get a number back from a subprogram, use `return`, e.g. `number = doubleIt(number)` with the header line `int doubleIt(int x)`. An array, on the other hand, can be changed by the subprogram directly. - -::: angeberwissen -Strictly speaking, Java only knows call by value. But with an array, the whole array is not copied, only its **address** in memory. With the copied address, the subprogram finds the same array. It is as if I gave you a copy of my locker key: the key is copied, but the hay in the locker only exists once. -::: - -## Exercise: Bonus points - -::: übung uebergabe-bonus -titel: Bonus points -auftrag: The main program calls `bonus(points)` and then outputs all the points. Build the subprogram `void bonus(int[] points)` that increases every score in the array by 1. Instead of 3, 5 and 8 you then see **4**, **6** and **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: The main program is already finished. You only build the subprogram in a new tab. -tipp: Open a new tab with **+** and the title `void bonus(int[] points)`. Inside, a for loop runs over all slots and calculates `points[i] = points[i] + 1`. You do not need a return, because the array is changed directly. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## The break - -Sometimes a loop should end **early**, in the middle of its body. That is what the **Break** is for. It leaves the loop it is in straight away. The program carries on with the first box after the loop. In Java, Python and JavaScript this is called `break`. - -You recognise the Break by the point on its left edge. You can write a short comment into the box, e.g. `Leave the loop`. It appears in the code as a comment after `break`. - -A Break almost always sits in a **branch**: the loop is only left if a condition is met. - -A typical case is the **endless loop with a Break**. The loop itself has no condition. It runs until the Break is reached: - -::: diagramm l15-endlos-aussprung.visustruct -The loop keeps asking for a number. With 0 it jumps out, otherwise it adds the number to the sum. -::: - -Without a Break, an endless loop would really never end. - -## Break in the simulation - -The simulation runs the Break like `break`: it leaves the innermost loop and carries on after it. If a Break is outside every loop, the subprogram ends; in the main program, the whole program ends. `return` does the same. - -Often you can do without a Break altogether: you write the stop condition straight into the loop. That is usually even clearer. - -## Exercise: Sum until 0 - -::: übung l15-bis-null -titel: Sum until 0 -auftrag: Rebuild the program “Sum until 0” **without** a Break. It keeps asking for whole numbers until someone types 0. Then it outputs **Sum:** and the sum of all the numbers typed in. -test: 3, 4, 0 -> Sum: 7 -test: 0 -> Sum: 0 -test: 10, -2, 5, 0 -> Sum: 13 -tipp: Without a Break, the condition of the loop itself has to check whether 0 was typed in. -tipp: Ask for the first number before the loop. The while loop runs with the condition `number != 0`. In the body you add the number to the sum and then ask for the next number. For the second input you only write `input: number "…"`, because `number` already exists. -lösung: l15-bis-null-loesung.visustruct -::: - -## Now in Java - -In Java a subprogram is called a **method**. The square from above looks like this: - -```java -public static int square(int x) { - int result = x * x; - return result; -} -``` - -The main program calls the method by its name and passes the value in round brackets: - -```java -int q = square(number); -System.out.println("The square is " + q); -``` - -- In the header, what the method returns (`int`) comes before the name. If it returns nothing, it says `void`. -- The parameters go in the round brackets with their data type: `(int x)`. You separate several with commas. -- In the Code area you write every method in its own tab, just as every subprogram has its own structure chart. -- In Java the exit is called `break`. With `while (true)` you build a loop that only ends through `break`. - -::: übung l18-java-kasse -titel: Till with break -code: java -auftrag: Write a till in Java. It keeps asking for a price in whole euros. At 0 it leaves the loop with `break`. Then it outputs how many items there were and what they cost altogether. - For 3, 4 and then 0 you see **Items: 2** and **Total: 7**. -test: 3, 4, 0 -> Items: 2 | Total: 7 -test: 0 -> Items: 0 | Total: 0 -test: 10, 2, 5, 0 -> Items: 3 | Total: 17 -braucht: endlosschleife, aussprung -tipp: You need two variables that start at 0 before the loop: one for the sum and one for the count. -tipp: Inside `while (true) { … }` you read the price. Right after that you check `if (price == 0) { break; }`. Only after that do you add the price to the sum and increase the count by 1. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Fill in the gaps: methods and break - -::: lückentext -titel: Method and exit -auftrag: Fill in the missing words. At the top is the method that calculates a square. Below is the main program that calls it. In the Code area both are in tabs of their own. -```java -public static [[int]] square(int x) { - [[return]] x * x; -} -``` -```java -int number = 1; -while ([[true]]) { - int q = [[square]](number); - if (q > 50) { - [[break]]; - } - System.out.println(q); - number = number + 1; -} -``` -erklärung: `int` before the method name means: the method returns a whole number, with `return`. If a method returns nothing, it says `void` there. `while (true)` runs endlessly until `break` leaves the loop. Output: 1, 4, 9, 16, 25, 36 and 49. -::: - -## Find the bug: guessing - -::: übung l18-java-fehler -titel: Fix the try counter -code: java -auftrag: In this guessing game the secret number is 7. The program keeps asking for a guess until 7 comes, then it outputs the number of tries. For 3, 5 and 7 that was 3 tries, but the program says **Tries: 2**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Tries: 3 -test: 7 -> Tries: 1 -test: 1, 2, 3, 7 -> Tries: 4 -braucht: endlosschleife, aussprung -tipp: What happens with the right guess? Is it still counted before `break` leaves the loop? -tipp: Count first, then check: `tries = tries + 1;` belongs before the `if` with the `break`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Game: What comes back? - -::: spiel frage -titel: Game: What comes back? -intro: Ludwig calls methods and jumps out of loops. Predict what is output. Seven rounds. - -runde: - public static int square(int x) { - return x * x; - } - - int q = square(5); - System.out.println(q); -frage: What is output? -- 5 -- 10 -- [x] 25 -> The call puts 5 into x, and return gives back 5 · 5. - -runde: - public static void showSum(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - showSum(x, x + 1); -frage: What is output? -- [x] 5 -- 23 -- 3 -> The first value 2 goes into a, the second value 3 into b. In Java, `a + b` calculates with numbers as usual. - -runde: -frage: What does a method return when `void` stands in front of its name? -- a whole number -- [x] nothing -- always 0 -> `void` means: no return value. With `int` there, a whole number comes back. - -runde: - public static void doubleIt(int x) { - x = x * 2; - } - - int number = 5; - doubleIt(number); - System.out.println(number); -frage: What is output? -- [x] 5 -- 10 -- 0 -> Call by value: doubleIt gets a copy. number stays 5. - -runde: - public static void clear(boolean[] seats) { - for (int i = 0; i < seats.length; i++) { - seats[i] = false; - } - } - - boolean[] row = {true, true}; - clear(row); - System.out.println(row[0]); -frage: What is output? -- true -- [x] false -> With an array, row and seats point to the same array. The caller sees the change too. - -runde: - int sum = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - sum = sum + i; - } - System.out.println(sum); -frage: What is output? -- [x] 6 -- 10 -- 55 -> At i = 4, `break` jumps out of the loop straight away. Only 1 + 2 + 3 have been added. - -runde: - int number = 1; - while (true) { - number = number * 3; - if (number > 20) { - break; - } - } - System.out.println(number); -frage: What is output? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Only 27 is greater than 20, then the endless loop ends with break. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can split a big task into smaller problems. → Split big tasks -- I can insert a call and build a subprogram in a tab of its own. → The call -- I can pass values to a subprogram with parameters. → Pass values: parameters -- I can hand back a return value with return and store it when calling. → Hand back a result: return -- I can explain why a subprogram cannot change a number but can change an array. → Copy or original: call by value and call by reference -- I can explain what a Break does and why it sits in a branch. → The break -- I can end a loop with a Break or through its condition. → Break in the simulation -- I can recognise a method in Java and leave a loop with `break`. → Now in Java -::: - -## Quiz - -::: quiz -? What does a call box with the text `showResult()` mean? -→ The call -- [ ] The program ends here. -- [x] The subprogram showResult() runs here, and then the program carries on. -- [ ] The word showResult() is output here. - -? Where does the program carry on after a Break? -→ The break -- [ ] At the start of the loop -- [x] At the first box after the loop -- [ ] Right at the start of the program -> The Break leaves the loop it is in. In Java, Python and JavaScript it is called break. - -? Why does a Break usually sit in a branch? -→ The break -- [x] So that the loop is only left if a condition is met -- [ ] Because otherwise a Break cannot be saved -- [ ] Because a branch is a loop itself -> Without a branch, the loop would be left in the very first pass. - -? Where does the simulation find the subprogram for a call `showResult()`? -→ The call -- [ ] It has to be in the same structure chart. -- [x] In an open tab with the title showResult() -- [ ] The simulation cannot run subprograms. -> Build every subprogram in a tab of its own and give it the name from the call as its title. - -? The subprogram has the header line `void showSum(int a, int b)`. Which call fits? -→ Pass values: parameters -- [ ] showSum() -- [x] showSum(3, 4) -- [ ] int s = showSum(3, 4) -> Two parameters need two values. And `void` means nothing comes back that you could store. - -? After `int q = square(5)`, q holds 25. What is at the end of the subprogram square? -→ Hand back a result: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> With return the subprogram hands the value back to the call. An output would only show it. - -? `void doubleIt(int x)` calculates `x = x * 2`. The main program has `int number = 5` and then the call `doubleIt(number)`. What value does number have afterwards? -→ Copy or original: call by value and call by reference -- [x] 5 -- [ ] 10 -- [ ] 0 -> Call by value: doubleIt only changed a copy of number. - -? `void clear(boolean[] seats)` sets all slots to false. What is true for the array row in the main program after `clear(row)`? -→ Copy or original: call by value and call by reference -- [ ] It is unchanged, because the subprogram gets a copy. -- [x] All slots are false, because seats and row are the same array. -- [ ] The array is deleted. -> Call by reference: an array is passed as a reference. The caller sees changes to the slots too. - -? What is a subprogram called in Java? -→ Now in Java -- [ ] Class -- [x] Method -- [ ] Loop -> In Java a subprogram is a method, for example the `main` method. Depending on its kind, it is also called a procedure or a function. - -? Many building blocks have a second name in textbooks. Match each term from VisuStruct to its other name. -→ Now in Java -= Branch -> Conditional statement -= Switch -> Multiple selection -= Loop -> Iteration -= Subprogram -> Method -> Both names mean the same thing. If you know one of them, you understand the other as well. - -? What is written in the header of a Java method that returns nothing? -→ Hand back a result: return -- [ ] int -- [x] void -- [ ] return -> void means “empty”: the method does something but returns no value. -::: diff --git a/web/lessons/en/19-suchen-und-sortieren.md b/web/lessons/en/19-suchen-und-sortieren.md deleted file mode 100644 index 40a8b852..00000000 --- a/web/lessons/en/19-suchen-und-sortieren.md +++ /dev/null @@ -1,352 +0,0 @@ ---- -titel: Searching and sorting -bereich: Basics -kurz: Use playing cards to understand how linear search, binary search and bubble sort work, and why some methods are much faster. ---- - -## Skills I will gain now - -::: kompetenzen -- explain how linear search goes through an array -- build a linear search as a structure chart -- explain why binary search only works with sorted values -- explain why binary search is much faster with many values -- show one round of bubble sort with playing cards -- search an array in Java and count matches -::: - -## Look for a card - -A pile of playing cards lies face down in a row in front of you. You are looking for the 9. What do you do? You turn over one card after another until you find the 9 or have turned over all the cards. - -::: grafik ss-lineare-suche.svg -The cards are mixed up. Every card you turn over is one comparison: is this the 9? -::: - -This is exactly how **linear search** works: it goes through the array from index 0 to the end and compares every slot with the value it is looking for. It always works, even if the values are mixed up. - -In the array our cards are numbers. We leave out jack, queen and king. - -::: diagramm ss-lineare-suche.visustruct -The loop runs until the card is found or there is no slot left. The variable comparisons counts how often a comparison was made. In the simulation, look for the 2, the 27 and the 4. -::: - -> If the card is not there, `position` stays at **-1**. There is no index -1, so here -1 means “not found”. - -## Exercise: Find a card - -::: übung ss-karte-suchen -titel: Find a card -auftrag: Create the array `int[] cards = {8, 4, 11, 4, 6}` and ask for a card. Output the position of the **first** matching card, e.g. `Position: 1` for the 4. If the card is not there, output `Not found`. -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 8 -> Position: 0 -test: 9 -> Not found -braucht: whileSchleife, verzweigung -tipp: Go through the array from front to back and compare every card with the one you are looking for. -tipp: Rebuild the example above. You do not need the variable comparisons here. The loop has to stop as soon as `position` is no longer -1, otherwise for the 4 you find the second one instead of the first. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Binary search: sorted is faster - -Now the cards lie in front of you **sorted**, from small to large. You are looking for the 27. You no longer turn over the first card at the front, but the **middle** one: - -- If it is the one you are looking for, you are done. -- If it is smaller, the card you are looking for can only be on the right. The whole left half drops out. -- If it is larger, the right half drops out. - -You do the same again with the rest. With every comparison the pile is halved. - -::: grafik ss-binaere-suche.svg -Three comparisons instead of fourteen: with every middle, half of the remaining cards drop out. -::: - -In the structure chart, two variables remember the range that is still being searched: `left` and `right`. The middle is `(left + right) / 2`. Because this is whole-number division, the result is always a whole index. - -::: diagramm ss-binaere-suche.visustruct -The same array as for the linear search. In the simulation, look for the 27 again and compare the output Comparisons with the linear search. -::: - -> Binary search **only works with sorted values**. With mixed-up cards, after looking at the middle you do not know which half to keep searching in. - -::: angeberwissen -I think of a number from 1 to 100. You may guess, and I only say “higher” or “lower”. With binary search you need at most **7** guesses. For a number up to a million it is at most 20, for a billion 30. Every doubling costs only one single extra guess. This is how databases search millions of entries in a fraction of a second. I search linearly, by the way: I eat the meadow from left to right. -::: - -## How fast is it? - -How good a method is gets measured by the number of comparisons it needs **in the worst case**. For linear search, that is the case where the card is right at the back or missing. - -::: grafik ss-vergleiche.svg -With 100 cards, linear search needs up to 100 comparisons, binary search at most 7. -::: - -| Number of cards | Linear search | Binary search | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1,000 | 1,000 | 10 | -| 1,000,000 | 1,000,000 | 20 | - -Linear search grows at the same rate as the number of cards. With binary search, twice as many cards add only **one** comparison. - -## Sorting with bubble sort - -For binary search the values must be sorted. One of the simplest ways of sorting is **bubble sort**. You go through the row from left to right and always compare **two neighbours**. If the larger card is on the left, you swap the two. - -::: grafik ss-bubblesort.svg -After one round, the largest card is on the far right. -::: - -After the first round the largest card is on the far right, like an air bubble rising to the top. That is where the name comes from. Then you repeat the round for the remaining cards. With 5 cards you need 4 rounds. - -The structure chart has two for loops inside each other. The outer one counts the rounds, the inner one compares the neighbours `cards[i]` and `cards[i + 1]`. You already know how to swap: it needs a **temporary variable**. - -::: diagramm ss-bubblesort.visustruct -The inner loop gets shorter with every round, because the largest cards on the right are already in the right place. Go through it in the simulation with Step and watch the array under Variables. -::: - -Bubble sort is easy to understand, but slow. With n cards it needs up to n · (n − 1) / 2 comparisons: with 10 cards 45, with 100 cards already 4,950 and with 1,000 cards almost half a million. You may get to know faster methods such as quicksort later. - -## Exercise: Sort descending - -::: übung ss-absteigend -titel: Sort descending -auftrag: The start diagram reads in 5 cards and sorts them with bubble sort **ascending**, that is from small to large. Change it so that the cards are output **descending**, the largest first. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Look at the condition in the branch. It decides when two neighbours are swapped. -tipp: You only need to change one single character. When should two neighbours be swapped if the larger card is supposed to go to the left? -tipp: In the branch, change `cards[i] > cards[i + 1]` to `cards[i] < cards[i + 1]`. Then two neighbours swap when the bigger card is on the right, and the big card moves to the left. -lösung: ss-absteigend-loesung.visustruct -::: - -## Now in Java - -The linear search from the exercise looks like this in Java: - -```java editor -int[] cards = {8, 4, 11, 4, 6}; -int wanted = 4; -int position = -1; -int i = 0; -while (position == -1 && i < cards.length) { - if (cards[i] == wanted) { - position = i; - } - i = i + 1; -} -System.out.println("Position: " + position); -``` - -- `&&` means “and”: the loop only runs while nothing has been found **and** there are still cards left. -- If the false branch is empty, you simply leave out `else`. -- The swap in bubble sort needs a helper variable in Java too: `int temp = cards[i];` - -::: übung l19-java-zaehlen -titel: Count cards -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. Write a Java program that asks for a card and counts how often it appears. It outputs **Count:** and the number. - For 4 you see **Count: 2**. -test: 4 -> Count: 2 -test: 8 -> Count: 1 -test: 9 -> Count: 0 -braucht: forSchleife, verzweigung -tipp: Unlike the search, you do not stop at the first match. A for loop goes through all cards. -tipp: `for (int i = 0; i < cards.length; i++)` and inside it `if (cards[i] == wanted) { count = count + 1; }`. `count` starts at 0 before the loop. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Fill in the gaps: bubble sort - -::: lückentext -titel: Sorting cards -auftrag: The program sorts the cards in ascending order with bubble sort. Fill in the missing words and symbols. -```java -int[] cards = {5, 2, 8, 1}; -for (int round = 0; round < cards.[[length]] - 1; round++) { - for (int i = 0; i < cards.length - 1; i++) { - if (cards[i] [[>]] cards[i + 1]) { - [[int]] temp = cards[i]; - cards[i] = cards[i + 1]; - cards[i + 1] = [[temp]]; - } - } -} -for (int i = 0; i < cards.length; i++) { - System.out.println(cards[i]); -} -``` -erklärung: If a card is larger than its right neighbour, the two are swapped, with a helper variable as when swapping glasses. After `length - 1` rounds everything is sorted: 1, 2, 5, 8. -::: - -## Find the bug: search - -::: übung l19-java-fehler -titel: Fix the linear search -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. The program should output the **first** position of the card you are looking for, so for 4 **Position: 1**. But it outputs **Position: 3**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l19-java-fehler-start.visustruct -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 9 -> Position: -1 -test: 8 -> Position: 0 -braucht: whileSchleife, verzweigung -tipp: The loop always runs up to the last card. What happens when the 4 comes a second time? -tipp: The loop should stop as soon as something has been found: `while (position == -1 && i < cards.length)`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l19-java-laeuft-nicht -titel: Find a card -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. The program searches for the 11 and should output **Position: 2**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Position: 2 -braucht: forSchleife, verzweigung -tipp: Compare the names in the condition with the names created above. -tipp: The card you are looking for is in `wanted` with a small w: `if (cards[i] == wanted) {` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Search pro - -::: spiel frage -titel: Game: Search pro -intro: Ludwig searches and sorts cards. How fast is it, and what comes out? Seven rounds. - -runde: - int[] cards = {8, 4, 11, 4, 6}; - int wanted = 4; - int position = -1; - int i = 0; - while (position == -1 && i < cards.length) { - if (cards[i] == wanted) { - position = i; - } - i = i + 1; - } - System.out.println("Position: " + position); -frage: What is output? -- [x] Position: 1 -- Position: 3 -- Position: 2 -> The search stops at the first 4. It lies in slot 1, counting from 0. - -runde: - int[] cards = {8, 4, 11, 4, 6}; - int wanted = 7; -frage: With the same linear search: what is in position afterwards? -- 0 -- 5 -- [x] -1 -> The 7 isn't there. position stays -1, which means "not found". - -runde: -frage: For which cards does binary search work? -- for all of them -- [x] only for sorted ones -- only for fewer than 100 -> Only if the cards are sorted do you know, after looking at the middle, in which half to keep searching. - -runde: -frage: How many comparisons does binary search need at most for 100 cards? -- 50 -- [x] 7 -- 100 -> Every comparison halves the pile. Halving 7 times is enough for up to 128 cards, so for 100 as well. - -runde: -frage: Instead of 1,000 cards there are now 2,000. How many more comparisons does binary search need at most? -- [x] one -- twice as many -- 1,000 -> The first comparison halves 2,000 to 1,000. After that it goes on as before. - -runde: - int[] cards = {5, 3, 8, 1}; -frage: One pass of bubble sort compares two neighbours at a time, from left to right. What is the order of the cards afterwards? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 and 3 swap, 5 and 8 stay, 8 and 1 swap. The biggest card is on the far right. - -runde: -frage: Bubble sort swaps `cards[i]` and `cards[i + 1]`. What does it need for that? -- [x] a temporary variable -- a second array -- a binary search -> Just like swapping two variables: `int temp = cards[i];`, otherwise a value gets lost. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain how linear search goes through an array. → Look for a card -- I can build a linear search as a structure chart. → Exercise: Find a card -- I can explain why binary search only works with sorted values. → Binary search: sorted is faster -- I can explain why binary search is much faster with many values. → How fast is it? -- I can show one round of bubble sort with playing cards. → Sorting with bubble sort -- I can search an array in Java and count matches. → Now in Java -::: - -## Quiz - -::: quiz -? What does binary search need? -→ Binary search: sorted is faster -- [ ] The array must have exactly 100 slots. -- [x] The values must be sorted. -- [ ] The value you are looking for must be there. -> Only with sorted values do you know, after looking at the middle, which half to keep searching in. - -? A sorted array has 1,000 values. How many comparisons does binary search need at most? -→ How fast is it? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1,000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: after about 10 halvings, one value is left. - -? What does bubble sort compare in every step? -→ Sorting with bubble sort -- [ ] The first and the last card -- [x] Two neighbouring cards -- [ ] Every card with the middle -> If the larger card is on the left, the neighbours are swapped. - -? Where is the largest card after the first round of bubble sort? -→ Sorting with bubble sort -- [ ] On the far left -- [ ] In the middle -- [x] On the far right -> It is swapped on with every comparison and rises to the right like a bubble. - -? What does `position = -1` mean in linear search? -→ Look for a card -- [x] The card has not been found (yet). -- [ ] The card is right at the back. -- [ ] The array is empty. - -? Match each method to the right description. -→ Look for a card -= Linear search -> look at one card after another -= Binary search -> look at the middle and drop one half -= Bubble sort -> compare neighbours and swap them - -? Why do you use a for loop for counting and not a search that stops at the first match? -→ Now in Java -- [ ] A for loop is always faster. -- [x] You have to look at every card, otherwise later matches are missing. -- [ ] A while loop cannot go through arrays. -> The card can appear several times. That is why you go through all cards to the end. -::: diff --git a/web/lessons/en/20-java.md b/web/lessons/en/20-java.md deleted file mode 100644 index d6c18740..00000000 --- a/web/lessons/en/20-java.md +++ /dev/null @@ -1,472 +0,0 @@ ---- -titel: Structure chart and code -bereich: From structure chart to code -kurz: All elements in Java at a glance, generate code, write Java in the Code area, run it and find mistakes. ---- - -## Skills I will gain now - -::: kompetenzen -- open the code view and choose Java as the language -- recognise output, input, branches and loops in Java -- explain the difference between Snippet and Class -- explain how Java code runs on every operating system with the compiler, bytecode and the JVM -- tell the JDK and the JRE apart -- explain why a C++ program has to be compiled again for every operating system but a Java program does not -- rebuild a short Java program as a structure chart -- type Java in the Code area and read the structure chart that is built -- run a program with ▶︎ and use input and output in the console -- find a syntax error or a runtime error by its red line number -- set breakpoints and follow a program step by step -::: - -## The code view - -In lessons 4 to 19 you have already written every element in Java. Here you see everything at a glance and learn how VisuStruct turns a whole structure chart into a Java program. - -A structure chart describes a process. For a computer to run it, you need a program in a programming language. VisuStruct writes this code for you. - -Click [Code](#zeige-code) in the tools (icon with angle brackets). On the right, the **Code generator** appears: - -- At the top you choose the language: **Java**, **Python** or **JavaScript**. -- For Java you also choose under **Java output** between **Snippet** (only the statements) and **Class** (a complete Java file). -- **Diagram text as comments** writes the text of every box as a comment above the code. -- **Copy** puts the code on the clipboard, **Save Java…** saves a `.java` file. - -The code changes straight away whenever you change something in the structure chart. - -::: angeberwissen -Later on, Nassi worked on the programming language Ada and was in charge of software at Apple. So if you invent structure charts, you do not end up in a goat shed, you end up at Apple. -::: - -::: diagramm l17-beispiel.visustruct -A small program with an input, a for loop and a branch. Open it and look at the Java code for it. -::: - -## This is the example in Java - -For the diagram above, VisuStruct generates this code as a **Snippet**: - -``` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which times table?"); -int number = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int result = number * i; - System.out.println(i + " times " + number + " = " + result); -} -if (number > 10) { - System.out.println("That was hard!"); -} else { - System.out.println("That was easy."); -} -``` - -In Java every statement ends with a **semicolon** `;`. Whatever is inside a box goes in **curly brackets** `{ }` in Java. The indentation shows you what belongs together. - -## The elements in Java - -| Element | Java | Lesson | -|---|---|---| -| Statement | `int sum = 0;` | 4, 5 | -| Output | `System.out.println(sum);` | 6 | -| Input | `int number = scanner.nextInt();` | 6 | -| Branch | `if (condition) { … } else { … }` | 8, 9 | -| Switch | `switch (value) { case 1 -> { … } default -> { … } }` | 10 | -| While loop | `while (condition) { … }` | 11 | -| Do-while loop | `do { … } while (condition);` | 12 | -| For loop | `for (int i = 0; i < 5; i++) { … }` | 13 | -| Endless loop | `while (true) { … }` | 11, 18 | -| Break | `break;` | 18 | -| Call | `calculate();` | 18 | -| Array | `int[] numbers = new int[5];` | 15, 16 | -| Text (String) | `String name = scanner.next();` | 17 | - -For input, `import java.util.Scanner;` and `Scanner scanner = new Scanner(System.in);` are written once at the very top. Before every input VisuStruct outputs the question with `System.out.println(…)`. - -If the false path of a branch is empty, VisuStruct still generates an `else` part that contains only the comment `// empty element`. If you switch off **Emit empty else branch in code**, the empty `else` part disappears. - -## Snippet or Class - -You can paste a **Snippet** into the `main` method of an existing Java program. With **Class** you get a complete file that you can compile and run straight away: - -``` -import java.util.Scanner; - -public class TimesTable { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Which times table?"); - int number = scanner.nextInt(); - ... - } // end main -} // end class -``` - -The comments `// end main` and `// end class` after the last two brackets are not needed, but very helpful: you see at once which bracket closes what, and you don't forget one. - -The name of the class comes from the **title** of the diagram. The file must have exactly the same name, so here `TimesTable.java`. **Save Java…** takes care of that for you. - -> The generated code is a good start, but check it. If you first create a variable and later read it in again with an input, VisuStruct still writes `int pin = scanner.nextInt();`. Java then reports that `pin` already exists. Simply delete the `int` in that line. - -## How Java runs on every computer - -Your file `TimesTable.java` is **source code**: text for people. The processor in your computer cannot understand it directly. That is why Java runs in two steps: - -1. The **compiler** `javac` translates the source code into **bytecode**. The result is the file `TimesTable.class`. -2. The **JVM** (Java Virtual Machine) reads the bytecode and, while running it, translates it into **machine code**: instructions for the processor in your computer. - -::: grafik l20-jvm.svg -Compile once: the same bytecode runs on every JVM. -::: - -There is only one bytecode. But there is a JVM for Windows, macOS, Linux and many other systems. That is why the same `.class` file runs on every computer that has a JVM installed. This is called **platform independent**. The slogan for it is **“Write once, run anywhere”**. - -::: angeberwissen -One of the first compilers was built in 1952 by **Grace Hopper**, whom you already know from lesson 7. Back then many people told her that computers could only do arithmetic and could not translate programs. She did it anyway. -::: - -Compare this with **C++**: there the compiler translates the source code directly into machine code for exactly one processor and one operating system. The finished program only runs there. For Windows, macOS and Linux you have to compile it again each time. - -::: grafik l20-cpp.svg -C++ is compiled to machine code separately for every system. -::: - -| | C++ | Java | -|---|---|---| -| The compiler creates | machine code | bytecode | -| How often do you compile? | again for every system | once | -| What does the computer need to run it? | nothing else | a JVM | - -Does that make Java slower? Hardly. Parts of a program that run often, loops for example, are translated completely into machine code once by the JVM’s **JIT compiler** (“just in time”). After that they run almost as fast as C++. - -> Think of the JVM as a small computer made of software, almost like an operating system of its own just for Java programs. Strictly speaking it is not an operating system, though, but a **virtual machine**: a program that itself runs on Windows, macOS or Linux and pretends to be a computer inside it. - -When you install Java you meet two abbreviations: - -| Abbreviation | What is inside? | What for? | -|---|---|---| -| **JRE** (Java Runtime Environment) | the JVM and ready-made building blocks such as `Scanner` | running Java programs | -| **JDK** (Java Development Kit) | the JRE plus the compiler `javac` and other tools | writing and compiling Java programs | - -So to program you need the JDK. Today you usually only download the JDK, the JRE is inside it. For VisuStruct in the browser you don't need to install any of this. - -::: angeberwissen -With a JDK you can do all of this yourself in the console, that is in the terminal or the command prompt: - -``` -java -version -javac TimesTable.java -java TimesTable -``` - -- `java -version` shows which Java version is installed. If you get an error message, Java is still missing. -- `javac TimesTable.java` starts the compiler. Afterwards there are two files in the folder: `TimesTable.java` with your source code and the new `TimesTable.class` with the bytecode. -- `java TimesTable` starts the JVM with your bytecode. Here you write the class name without `.class`. - -If `java -version` works but `javac -version` doesn't, only a JRE is installed. Then you are missing the JDK. -::: - -::: angeberwissen -Every compiled Java file (`.class`) starts with the same four bytes, written in hexadecimal: `CA FE BA BE`. That is how the computer recognises it as Java. James Gosling came up with this “cafe babe”, to go with the coffee in the name. -::: - -## The Java language mode - -Under the [gear](#zeige-einstellungen) at the top right you can switch on **Code keywords**. Then the building blocks in the Elements area have the same names as the commands in Java: `if`, `switch`, `for`, `while`, `do`, `while(true)`, `break` and `call`. The rest of the interface stays in your language. - -This helps you match the boxes to the Java commands. Your diagram itself stays unchanged. - -## Write Java directly - -So far you drew the structure chart first and then looked at the code. In the [Code](#zeige-codelive) area it also works the other way round. Click the slim **Code** strip on the right. Next to the structure chart a column appears where you type Java yourself. - -- **Structure chart to code:** When you change something in the structure chart, the Java code changes right away. -- **Code to structure chart:** When you type Java, VisuStruct builds the structure chart as you type. If something is still missing, for example a closing bracket `}`, a hint appears below the code. -- **Suggestions:** While you type, VisuStruct suggests templates, for example for `if`, `for` or `while`. Press Enter to accept a template. Simply replace the grey placeholders with your own text. -- **Indent:** If your code has got messy, click the **Indent** button at the top of the Code area. Every level then sits four spaces deeper, as in Eclipse or IntelliJ. If you have selected lines, VisuStruct only indents those. - -So you can try both ways. Drawing the structure chart first is good for planning a process. Writing code directly is faster when you already know what you want. The structure chart then helps you check your code. - -## Run the program - -At the top of the Code area, [▶︎ Run](#zeige-ausfuehren) starts your program and **■ Stop** ends it, for example in an endless loop. What your program outputs appears below in the [console](#zeige-konsole). If it asks for input, type it there and press Enter. - -You can drag the border between code and console up or down with the mouse. - -If something goes wrong, the line number turns red. With a syntax error the spot is also underlined in red, and below it you read e.g. **Line 2: Still missing here: ;**. Then ▶︎ does not even start the program. If the program stops while it runs, it is a runtime error, and the console names the line, e.g. **Line 3: Division by zero.** You know the three kinds of errors from the lesson “Simulation and desk check”. - -If VisuStruct knows exactly what is missing, there is a 💡 at the end of the line. Move the mouse onto it to see the suggestion, e.g. **Suggestion: insert import java.util.Scanner;**. A click applies it. Still read what was inserted, so that you know it yourself next time. - -> The Code area understands the Java commands that also exist as boxes in the structure chart. It does not know your own classes and objects yet. Windows with Swing work anyway: turn on **Real Java (with Swing)** under the gear (lesson “Your first window”). - -::: angeberwissen -Professionals call a program where you write code, run it and look for mistakes an **IDE** (Integrated Development Environment). With the Code area, VisuStruct is a small IDE with a built-in structure chart. -::: - -## Find mistakes with the debugger - -Sometimes a program runs but calculates something wrong. That is a logic error. Then the **debugger** helps: it runs your program slowly while you watch. - -- **Breakpoint:** Click a line number on the left. A red dot appears. [▶︎ Run](#zeige-ausfuehren) pauses before this line. Click again to remove the dot. -- **Yellow line:** This line runs next. The matching box is selected in the structure chart. -- **Variables:** Below the code you see the current value of every variable. Whatever changed since the last pause lights up yellow for a moment. -- **Step:** The [Step](#zeige-einzelschritt) button next to ▶︎ runs exactly one line and pauses again. Without a breakpoint it starts the program and pauses before the first line. -- **Continue:** ▶︎ runs to the next breakpoint or to the end. **■ Stop** ends it. - -Try it with this program. Set a breakpoint on line 3 and press ▶︎ several times. How do `i` and `summe` grow? - -``` -int summe = 0; -for (int i = 1; i <= 4; i++) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -``` - -::: ludwig -This is a desk check, except the computer takes the notes for you. Before each step, think about which value the variable should have next. When the table doesn't match your guess, you have found the place with the mistake. -::: - -## Exercise: Code first, then the structure chart - -This time you don't draw. Open the Code area and type in this Java code: - -``` -System.out.println("Start number?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -``` - -::: übung l20-countdown -titel: Countdown -bereich: beide -auftrag: Type the Java code above into the Code area. Run it with ▶︎ and enter a start number in the console. - Then look at the structure chart that was built and click Check. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Open the Code area with the slim strip on the far right and just start typing. -tipp: Watch out for the semicolon at the end of every statement and the curly brackets of the while loop. A hint below the code tells you when something is still missing. -lösung: l20-countdown-loesung.visustruct -::: - -## Exercise: Java as a structure chart - -Now the other way round: here is a piece of Java code. Rebuild it as a structure chart. - -``` -System.out.println("Enter a number"); -int n = scanner.nextInt(); -int factorial = 1; -for (int i = 1; i <= n; i++) { - factorial = factorial * i; -} -System.out.println(factorial); -``` - -::: übung l17-fakultaet -titel: Factorial from Java -bereich: beide -auftrag: Rebuild the Java code above as a structure chart. The program reads in a number n and outputs the product 1 · 2 · … · n. - At the end, check in the code view whether your Java code looks the same. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Read the Java code line by line and think about which box fits each line. -tipp: The first two Java lines together are one single input: `input: int n "Enter a number"`. The for loop has the three fields `int i = 1`, `i <= n` and `i++`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Exercise: else if - -In Java, several cases are often written as `else if`: - -``` -if (age < 6) { - System.out.println("Free entry"); -} else if (age < 18) { - System.out.println("Child ticket: 4 euros"); -} else { - System.out.println("Adult ticket: 8 euros"); -} -``` - -There is no `else if` in a structure chart. There, a second branch sits in the false path of the first one. If the false path holds only this one branch, VisuStruct turns it back into `else if` when it generates the code, and into `elif` in Python. - -::: übung l17-ticket -titel: Ticket price -bereich: beide -auftrag: Ask for the age as a whole number and rebuild the Java code above as a structure chart. -test: 5 -> Free entry -test: 6 -> Child ticket: 4 euros -test: 17 -> Child ticket: 4 euros -test: 18 -> Adult ticket: 8 euros -braucht: verzweigung -tipp: In the Java code there is another `if` inside an `else`. In the structure chart that is a branch inside a path. -tipp: Drag a second branch with the condition `age < 18` into the false path of the first branch. -lösung: l17-ticket-loesung.visustruct -::: - -## Game: Which box? - -::: spiel frage -titel: Game: Which box? -intro: Ludwig shows you Java. Which box from the structure chart is behind it? Eight rounds. -frage: Which box is this? - -runde: - do { - number = number * 2; - } while (number < 100); -- While loop -- [x] Do-while loop -- For loop -> The condition is at the bottom. So the body runs at least once. - -runde: - switch (choice) { - case 1 -> { System.out.println("water"); } - default -> { System.out.println("There is no such button."); } - } -- Branch -- [x] Switch -- Call -> One variable is compared with fixed values: that is the switch. - -runde: - break; -- Call -- Endless loop -- [x] Break -> `break` leaves the loop it stands in straight away. - -runde: - calculate(); -- [x] Call -- Output -- Statement -> A name with round brackets: a subprogram is called here. - -runde: - int number = scanner.nextInt(); -- Output -- [x] Input -- Call -> The scanner reads in a value. That is an input. - -runde: - while (true) { - System.out.println("Hello"); - } -- While loop with a condition -- [x] Endless loop -- Do-while loop -> `while (true)` has no real condition. Only a break could end it. - -runde: -frage: You want a complete Java file that you can compile and run straight away. What do you choose in the code view? -- Snippet -- [x] Class -> A snippet is only the statements for the `main` method. Class creates the whole file. - -runde: -frage: In the debugger, a line is yellow. What does that mean? -- It has a mistake. -- [x] It runs next. -- There is a breakpoint there. -> Yellow is the next line. A breakpoint is a red dot at the line number. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can open the code view and choose Java as the language. → The code view -- I can recognise output, input, branches and loops in Java. → The elements in Java -- I can explain the difference between Snippet and Class. → Snippet or Class -- I can explain how Java code runs on every operating system with the compiler, bytecode and the JVM. → How Java runs on every computer -- I can tell the JDK and the JRE apart. → How Java runs on every computer -- I can explain why a C++ program has to be compiled again for every operating system but a Java program does not. → How Java runs on every computer -- I can rebuild a short Java program as a structure chart. → Exercise: Java as a structure chart -- I can type Java in the Code area and read the structure chart that is built. → Write Java directly -- I can run a program with ▶︎ and use input and output in the console. → Run the program -- I can find a syntax error or a runtime error by its red line number. → Run the program -- I can set breakpoints and follow a program step by step. → Find mistakes with the debugger -::: - -## Quiz - -::: quiz -? What does `output: "Hello"` look like in Java? -→ The elements in Java -- [ ] print("Hello") -- [x] System.out.println("Hello"); -- [ ] console.log("Hello"); -> print is Python, console.log is JavaScript. - -? Where does the class name come from with Class? -→ Snippet or Class -- [ ] It is always called Main. -- [x] From the title of the diagram -- [ ] From the first box -> Without a title, the class is called Main. - -? Which Java code belongs to a do-while loop? -→ The elements in Java -- [ ] while (condition) { … } -- [x] do { … } while (condition); -- [ ] for (;;) { … } -> With do-while the condition is at the end, so the body runs at least once. - -? What does a Break become in Java code? -→ The elements in Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? What happens at a red dot before the line number? -→ Find mistakes with the debugger -- [ ] The line is skipped. -- [x] The program pauses before this line. -- [ ] The line contains a mistake. -> That is a breakpoint. Continue with ▶︎ or Step. - -? Why does the same `.class` file run on Windows, macOS and Linux? -→ How Java runs on every computer -- [ ] The compiler creates a separate file for every operating system. -- [x] On every operating system a JVM runs the same bytecode. -- [ ] Java programs don't need an operating system. -> The bytecode stays the same. Only the JVM exists separately for each system. - -? What do you need to write and compile Java programs? -→ How Java runs on every computer -- [ ] only the JRE -- [x] the JDK -- [ ] only the JVM -> The JRE is enough to run programs. The compiler `javac` only comes with the JDK. - -? You want to use your program on Windows and on a Mac. What is true? -→ How Java runs on every computer -- [ ] You have to compile Java again for every system, but not C++. -- [x] You have to compile C++ again for every system, but not Java. -- [ ] You only compile both once. -> C++ becomes machine code for one system directly. Java becomes bytecode, and the JVM runs it on every system. - -? Line number 3 in the Code area is red. What does that mean? -→ Run the program -- [ ] There is a breakpoint in line 3. -- [x] There is an error in line 3. -- [ ] Line 3 runs next. -> A breakpoint is a round red dot, and the next line is yellow. A red line number shows a syntax error or a runtime error. -::: diff --git a/web/lessons/en/21-python-und-javascript.md b/web/lessons/en/21-python-und-javascript.md deleted file mode 100644 index f40cb8ec..00000000 --- a/web/lessons/en/21-python-und-javascript.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -titel: Python and JavaScript -bereich: From structure chart to code -kurz: Generate Python and JavaScript code from the structure chart and run JavaScript straight in the browser. ---- - -## Skills I will gain now - -::: kompetenzen -- choose and compare Python and JavaScript in the code view -- explain that Python forms blocks by indentation and needs no data types -- explain what VisuStruct adjusts when translating to Python -- run JavaScript with Test in browser -::: - -## The same diagram, three languages - -In the code view ([Code](#zeige-code) in the tools) you choose the language at the top. The structure chart stays the same; only the way it is written changes. That way you can compare the languages easily. - -::: diagramm l18-beispiel.visustruct -A number is made smaller by 3 again and again until it is no longer greater than 0. -::: - -## Python - -For the diagram above, VisuStruct generates this Python code: - -``` -number = int(input("Start value?")) -while number > 0: - print(number) - number = number - 3 -print("End") -``` - -What is different in Python compared with Java: - -- There are **no semicolons** and **no curly brackets**. Python recognises what belongs to a loop or a branch by the **indentation** alone. After `while`, `if` and `else` there is a colon. -- Variables get **no data type**: `int number = 5` becomes `number = 5`. -- Output is called `print(…)`, input `input(…)`. For a number, `int(…)` goes around it. -- In Python, arrays are called **lists**: `int[] numbers = {2, 4, 6}` becomes `numbers = [2, 4, 6]`, and `numbers.length` becomes `len(numbers)`. -- A simple for loop such as `int i = 0`, `i < 5`, `i++` becomes `for i in range(5):`. - -## What to watch out for in Python - -In some places Python is stricter than Java. That is why VisuStruct translates the Java way of writing things when it exports: - -- **Text and number with +:** `"Sum: " + sum` becomes `"Sum: " + str(sum)`. Without `str(…)`, Python stops with an error. -- **There is no i++:** it becomes `i += 1`. -- **&&, || and !** are called `and`, `or` and `not` in Python, and `true` and `false` become `True` and `False`. -- **Whole-number division:** In Python, `7 / 2` always gives 3.5. If the structure chart divides two `int` values, VisuStruct writes `7 // 2`, which gives 3, just like in Java. -- **For loops:** Counting loops become `for … in range(…)`, also with `i <= n`, counting backwards or steps such as `i = i + 2`. If a loop does not fit that pattern, it becomes a `while` loop. -- **There is no do-while:** it becomes `while True:` with `if not (…): break` at the end. - -If a diagram is marked as **pseudocode**, only a note appears above the code. Then the text is a sketch and not a runnable program. - -## JavaScript - -The same diagram in JavaScript: - -``` -"use strict"; - -let number = parseInt((prompt("Start value?") ?? "0"), 10); -while (number > 0) { - console.log(number); - number = number - 3; -} -console.log("End"); -``` - -JavaScript looks similar to Java: curly brackets, semicolons, `while (…)` and `if (…)`. The differences: - -- Variables are created with `let`, without a data type. Constants with `const`. -- Output is called `console.log(…)`, input `prompt(…)`. `parseInt(…)` turns the input into a whole number. -- Arrays go in square brackets: `let numbers = [2, 4, 6];`. -- `"use strict";` at the very top makes JavaScript complain about typos in variable names. -- JavaScript has only one number type. If the structure chart divides two `int` values, VisuStruct writes `Math.trunc(a / b)`. Then the digits after the decimal point are dropped, just like in Java. - -::: angeberwissen -JavaScript has almost nothing to do with Java. Brendan Eich built it in 1995 in just ten days for the Netscape browser. First it was called Mocha, then LiveScript. Because everyone was excited about Java at the time, it was renamed JavaScript shortly before launch, for advertising reasons. People have been mixing the two up ever since. Programmers say: Java is to JavaScript as car is to carpet. -::: - -## Run JavaScript in the browser - -JavaScript is the language of browsers. That is why VisuStruct can **run the code straight away**: - -::: angeberwissen -Why are links in the browser blue, anyway? In 1985 Ben Shneiderman's team tested which colour is easy to see but does not get in the way when you read. Light blue won. Red stood out more, but people remembered the text less well afterwards. -::: - -1. Choose **JavaScript** in the code view. -2. Click **Test in browser**. -3. Whatever `console.log` outputs appears under **Output**. If the program asks for a value with `prompt`, type it in and click **OK**. -4. **Stop** cancels the program. - -VisuStruct stops an endless loop `while (true)` by itself after 100,000 passes and reports **Execution stopped: possible infinite loop.** - -> In JavaScript code, a **Call** needs a subprogram with that name, otherwise JavaScript reports an error. Unlike the simulation, the browser does not look in the other tabs. - -## Exercise: Python as a structure chart - -This Python program outputs all even numbers from 0 up to a limit: - -``` -limit = int(input("Up to which number?")) -for i in range(0, limit + 1, 2): - print(i) -``` - -`range(0, limit + 1, 2)` means: start at 0, stop before `limit + 1`, count in steps of two. - -::: übung l18-python -titel: Even numbers -bereich: beide -auftrag: Rebuild the Python program above as a structure chart with a for loop. With the limit 7 it outputs 0, 2, 4 and 6. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: `range(0, limit + 1, 2)` counts from 0 up to the limit in steps of two. That is a for loop. -tipp: The three fields of the for loop are `int i = 0`, `i <= limit` and `i = i + 2`. -lösung: l18-python-loesung.visustruct -::: - -## Exercise: JavaScript as a structure chart - -``` -let age = parseInt((prompt("How old are you?") ?? "0"), 10); -if (age >= 16) { - console.log("You may vote."); -} else { - let years = 16 - age; - console.log("Wait " + years + " more years."); -} -``` - -::: übung l18-js -titel: Voting from 16 -bereich: beide -auftrag: Rebuild the JavaScript code above as a structure chart. Then test in the code view with **JavaScript** and **Test in browser** whether your code outputs the same as the simulation. -test: 12 -> Wait 4 more years. -test: 16 -> You may vote. -test: 13 -> Wait 3 more years. -test: 40 -> You may vote. -braucht: verzweigung -tipp: Read the code line by line. `if` and `else` become a branch. -tipp: `prompt` and `parseInt` together are one input: `input: int age "How old are you?"`. The false path holds two boxes: first `int years = 16 - age`, then the output. -lösung: l18-js-loesung.visustruct -::: - -## Game: Which language? - -::: spiel frage -titel: Game: Which language? -intro: Ludwig has mixed up lines from three languages. Can you tell where each comes from? Eight rounds. -frage: Which language is this written in? -antworten: Java | Python | JavaScript - -runde: - print(number) -richtig: Python -> In Python the output is called `print(…)`, without a semicolon. - -runde: - console.log(number); -richtig: JavaScript -> `console.log` is the output in JavaScript. - -runde: - System.out.println(number); -richtig: Java -> The long output with `System.out` only exists in Java. - -runde: - let numbers = [2, 4, 6]; -richtig: JavaScript -> `let` and square brackets: an array in JavaScript. - -runde: - for i in range(5): -richtig: Python -> A colon instead of curly brackets, and `range`: typical Python. - -runde: - int[] numbers = {2, 4, 6}; -richtig: Java -> Only Java writes the data type `int[]` in front. - -runde: - number = int(input("Start value?")) -richtig: Python -> No data type in front of the variable, no semicolon, and `int(…)` around the input. - -runde: - let number = parseInt(prompt("Start value?"), 10); -richtig: JavaScript -> `prompt` asks for a value in the browser, `parseInt` turns it into a number. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can choose and compare Python and JavaScript in the code view. → The same diagram, three languages -- I can explain that Python forms blocks by indentation and needs no data types. → Python -- I can explain what VisuStruct adjusts when translating to Python. → What to watch out for in Python -- I can run JavaScript with Test in browser. → Run JavaScript in the browser -::: - -## Quiz - -::: quiz -? How does Python recognise which lines belong to a loop? -→ Python -- [ ] By the curly brackets -- [x] By the indentation -- [ ] By the semicolon at the end of the line - -? What is `output: sum` in JavaScript? -→ The same diagram, three languages -- [ ] print(sum) -- [ ] System.out.println(sum); -- [x] console.log(sum); - -? Which language can VisuStruct run straight in the browser? -→ Run JavaScript in the browser -- [ ] Java -- [ ] Python -- [x] JavaScript -> Every browser can run JavaScript. For Java and Python you need a program of its own. - -? What happens in Python with `print("Sum: " + sum)` if sum is the number 7? -→ What to watch out for in Python -- [ ] Sum: 7 is output. -- [x] Python reports an error, because text and a number cannot be joined with +. -- [ ] Sum: is output. -> str(sum) turns the number into text. -::: diff --git a/web/lessons/en/22-try-und-catch.md b/web/lessons/en/22-try-und-catch.md deleted file mode 100644 index 5c1aec01..00000000 --- a/web/lessons/en/22-try-und-catch.md +++ /dev/null @@ -1,293 +0,0 @@ ---- -titel: When the program crashes: try and catch -bereich: Catching errors -kurz: Use real Java to read error messages and catch crashes with try and catch. ---- - -## Skills I will gain now - -::: kompetenzen -- switch on real Java in the Code area -- read the exception and the line from an error message -- catch an exception with try and catch -- turn text into a number with Integer.parseInt and catch the NumberFormatException -- write a program that divides safely, even by 0 -::: - -## Crashed! - -This program should print three grades. Type it into the [Code](#zeige-codelive) area and click [▶︎ Run](#zeige-ausfuehren): - -```java -int[] grades = {2, 3, 1}; -System.out.println("First grade: " + grades[0]); -System.out.println("Fourth grade: " + grades[3]); -System.out.println("Done!"); -``` - -The first grade appears, then it stops. VisuStruct reports “Invalid expression: grades[3]”, and “Done!” never shows up. The array only has the slots 0, 1 and 2; there is no slot 3. - -::: ludwig -Has this ever happened to you? One small mistake, and the whole program is gone. It happened to me so often that I cried in front of the screen. But there is a solution, and it is called **try** and **catch**. -::: - -::: angeberwissen -In 1947 Grace Hopper's team found a moth in the Mark II computer at Harvard University that had caused a fault. They taped it into the logbook and wrote: “First actual case of bug being found”. Since then, program errors have been called **bugs**. -::: - -## Switching on real Java - -The Code area normally calculates with the same simulation as the structure chart. It simply stops when there is an error. To see what real Java does, switch on real Java: - -1. Click the [gear](#zeige-einstellungen) at the top right and choose Java as the **Programming language**. -2. In the Java section, tick **Real Java (with Swing)**. - -The first time you run a program, VisuStruct downloads about 40 MB once. After that your code is compiled and run just like in a real Java development environment. - -> With real Java, input from the console (`Scanner`) does not work yet. That is why our examples work without input. - -Now write the program as a whole class, so that the line numbers match, and run it: - -```java -public class Grades { - public static void main(String[] args) { - int[] grades = {2, 3, 1}; - System.out.println("First grade: " + grades[0]); - System.out.println("Fourth grade: " + grades[3]); - System.out.println("Done!"); - } -} -``` - -The [console](#zeige-konsole) now shows: - -``` -First grade: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Grades.main(Grades.java:5) -``` - -## Reading the error message - -A message like this looks scary at first. But it tells you exactly what is going on: - -| Part | Meaning | -|---|---| -| `ArrayIndexOutOfBoundsException` | The kind of error: the index is outside the array. | -| `3` | The wrong index. | -| `Grades.java:5` | The line where it happened: line 5. | - -Java calls a runtime error like this an **exception**. We say: Java *throws* an exception. If nobody catches it, the program stops. That is why “Done!” does not appear. - -::: ludwig -Always read error messages from the top: first the name of the exception, then the line number. In the Code area the line numbers are on the left next to the code. -::: - -## try and catch - -With **try** and **catch** you catch the exception. Change the line with `grades[3]` like this: - -```java -try { - System.out.println("Fourth grade: " + grades[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("This grade does not exist."); -} -System.out.println("Done!"); -``` - -- `try` means “give it a go”. Java runs the block in curly brackets as usual. -- If a line in it throws an exception, Java jumps straight into the `catch` block. The rest of the `try` block is skipped. -- The brackets after `catch` say which exception is caught, plus a name for it, usually `e`. -- After that the program carries on as normal. Now “Done!” appears too. - -If everything in the `try` block goes well, the `catch` block is simply skipped. - -::: ludwig -try and catch are like the net under the trapeze at the circus: as long as everything works, you don't notice it. If someone falls, the net catches them and the show goes on. -::: - -> The structure chart has no box for try and catch. So VisuStruct shows the whole block as one statement. You can only run it with real Java. - -::: angeberwissen -In July 1969, Apollo 11 landed on the Moon. Shortly before touchdown, the onboard computer suddenly reported the errors **1202** and **1201**: it had more work than it could handle. Still, it did not crash. **Margaret Hamilton**'s team had built the software so that it recognised such errors, put aside the unimportant jobs and kept only the most important ones running. The landing succeeded. - -Margaret Hamilton led the group at MIT that wrote the software for the Apollo spacecraft. She always planned for things going wrong, just like you do with `try` and `catch`. So that software would be taken as seriously as hardware, she called her work **software engineering**. Today a whole field of computer science has that name. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (born 1936) in 1969, next to the printed programs her team wrote for Apollo (photo: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), public domain). -::: - -## Turning text into a number - -Often a number is text at first, for example later in a text field in a window. `Integer.parseInt` turns text into a whole number: - -```java -String input = "twelve"; -try { - int number = Integer.parseInt(input); - System.out.println("Double: " + number * 2); -} catch (NumberFormatException e) { - System.out.println("That is not a number: " + input); -} -``` - -Java cannot convert “twelve” and throws a `NumberFormatException`. The program prints “That is not a number: twelve”. Change the first line to `String input = "12";` and run it again: now “Double: 24” appears. - -## Exceptions are objects - -This is not in the curriculum, but it is useful to know. - -::: angeberwissen -Every exception is an object of its own class, for example `ArithmeticException` for dividing by 0. `e.getMessage()` gives you its message, such as `/ by zero`. - -A `try` may be followed by several `catch` blocks, one for each kind of error. `catch (Exception e)` catches all exceptions, but then tells you less about what happened. And a `finally { … }` block at the end always runs, whether there was an error or not. -::: - -## Game: Does the net catch it? - -::: spiel frage -titel: Game: Does the net catch it? -intro: Ludwig is swinging on the trapeze. Do try and catch save him, or does the program crash? Seven rounds. - -runde: - int[] grades = {2, 3, 1}; - System.out.println(grades[3]); - System.out.println("Done!"); -frage: What happens with real Java? -- 1 and Done! appear. -- Only Done! appears. -- [x] The program crashes with an ArrayIndexOutOfBoundsException. -> There is no slot 3. Nobody catches the exception, so Done! never arrives. - -runde: - int[] grades = {2, 3, 1}; - try { - System.out.println(grades[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("This grade does not exist."); - } - System.out.println("Done!"); -frage: What is output? -- Done! -- [x] This grade does not exist.\nDone! -- This grade does not exist. -> The catch block catches the exception, and after that the program carries on as normal. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: What is output? -- A\nB\nC -- [x] A\nC -- C -> At 10 / 0, Java jumps straight into the catch block. The rest of the try block, B, is skipped. - -runde: - String input = "12"; - try { - int number = Integer.parseInt(input); - System.out.println("Double: " + number * 2); - } catch (NumberFormatException e) { - System.out.println("That is not a number: " + input); - } -frage: What is output? -- [x] Double: 24 -- That is not a number: 12 -- Double: 1212 -> "12" can be converted. If everything goes well in the try block, catch is skipped. - -runde: -frage: Which exception does `Integer.parseInt("2.5")` throw? -- ArithmeticException -- [x] NumberFormatException -- none, the result is 2 -> parseInt only converts whole numbers. With a point it isn't a valid format. - -runde: -frage: Which exception does `12 / 0` throw with `int`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Dividing by 0 doesn't work with whole numbers. The message is `/ by zero`. - -runde: - try { - int pieces = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Error!"); - } - System.out.println("Done!"); -frage: What happens? -- Error! and Done! appear. -- Only Done! appears. -- [x] The program crashes. -> catch only catches the exception named in the brackets. An ArithmeticException falls through the net. -::: - -## Task: dividing safely - -This program crashes: - -```java -int cake = 12; -int people = 0; -System.out.println("Everyone gets " + cake / people + " pieces."); -System.out.println("Enjoy!"); -``` - -1. Run it with real Java and read the error message. Which exception is it? -2. Catch it with try and catch. Instead of the crash, “You can't share without people.” should appear, and “Enjoy!” should still be printed. -3. Set `people` to 4 and check that “Everyone gets 3 pieces.” appears. - -::: ludwig -The exception is called `ArithmeticException`. Only put the line that can crash into the `try` block. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can switch on real Java in the Code area. → Switching on real Java -- I can read the exception and the line from an error message. → Reading the error message -- I can catch an exception with try and catch. → try and catch -- I can turn text into a number with Integer.parseInt and catch the NumberFormatException. → Turning text into a number -- I can write a program that divides safely, even by 0. → Task: dividing safely -::: - -## Quiz - -::: quiz -? What happens if an exception is not caught? -→ Reading the error message -- [ ] Java skips the line and carries on. -- [x] The program stops. -- [ ] The computer restarts. -> That is why “Done!” does not appear in the first example. - -? What happens in the try block after the line that throws an exception? -→ try and catch -- [ ] The remaining lines run anyway. -- [x] The rest is skipped and Java jumps into the catch block. -- [ ] Java starts the try block again. - -? Which exception does Integer.parseInt("twelve") throw? -→ Turning text into a number -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> “twelve” is not in a number format that Java understands. - -? Match each exception to its cause. -→ Reading the error message -= ArrayIndexOutOfBoundsException -> index outside the array -= ArithmeticException -> dividing by 0 -= NumberFormatException -> text is not a number -::: diff --git a/web/lessons/en/23-text-einlesen-und-speichern.md b/web/lessons/en/23-text-einlesen-und-speichern.md deleted file mode 100644 index 5086c763..00000000 --- a/web/lessons/en/23-text-einlesen-und-speichern.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -titel: Reading and saving text -bereich: Files -kurz: Read a text file line by line with Scanner and save a file with PrintWriter. ---- - -## Skills I will gain now - -::: kompetenzen -- explain why programs save data in files -- read a text file line by line with Scanner, hasNextLine and nextLine -- explain why Java requires try and catch or throws for files -- write a file with PrintWriter and finish it with close -- read a file, add to it and save it again with numbers -::: - -## Why files? - -Everything stored in variables is gone as soon as the program ends. If a program should remember something, for example a game score, a list or grades, it saves the data in a **file**. The next time it starts, it reads the file again. - -Zora is free, and Ludwig is planning a party. His shopping list is ready in the file `shopping.txt`: - -::: datei shopping.txt -Hay -Apples -Carrots -Dandelions -::: - -A file ending in `.txt` contains only text, line by line. You can open it with any editor, for example Notepad on Windows or TextEdit on a Mac. - -## Reading a file with Scanner - -You already know the `Scanner`: `new Scanner(System.in)` reads from the keyboard. If you give it a file instead of `System.in`, it reads from the file: - -```java -import java.io.*; -import java.util.Scanner; - -Scanner file = new Scanner(new File("shopping.txt")); -while (file.hasNextLine()) { - String line = file.nextLine(); - System.out.println(line); -} -file.close(); -``` - -| Method | Meaning | -|---|---| -| `new File("shopping.txt")` | the file with this name | -| `hasNextLine()` | Is there another line? `true` or `false` | -| `nextLine()` | returns the next line and moves on by one line | -| `close()` | closes the file when you are done | - -The loop runs as long as there is another line. In each pass, `nextLine()` fetches the next line. `File` belongs to the package `java.io`, so you need `import java.io.*;`. - -::: diagramm dt-lesen.visustruct -This is what reading looks like in a structure chart. Try it in the simulation. -::: - -::: ludwig -Imagine the Scanner as a finger pointing at a line. `nextLine()` reads the line under the finger and moves the finger down one line. Once the finger is below the last line, `hasNextLine()` returns `false`. -::: - -## What if the file is missing? - -If the file does not exist, Java cannot read it and throws a `FileNotFoundException`. Java insists that your program is prepared for this. Either you catch it with try and catch, as in lesson 22: - -```java -try { - Scanner file = new Scanner(new File("shopping.txt")); - // ... read ... - file.close(); -} catch (FileNotFoundException e) { - System.out.println("The file shopping.txt is missing!"); -} -``` - -Or you write `throws Exception` after `main`. It means: "This method may throw an exception, I won't deal with it." Then the program stops if the file is missing. - -```java -public static void main(String[] args) throws Exception { -``` - -> VisuStruct adds `throws Exception` after `main` automatically as soon as your program works with files. In the simulation, VisuStruct reports a missing file with "There is no file called …". - -## Exercise: Numbering the shopping list - -::: übung l23-java-nummern -titel: Numbering the shopping list -code: java -auftrag: Read `shopping.txt` line by line and print each line with its number: **1. Hay**, **2. Apples** and so on. -start: l23-java-nummern-start.visustruct -test: - -> 1. Hay | 2. Apples | 3. Carrots | 4. Dandelions -braucht: whileSchleife -tipp: Create `int number = 1;` before the loop and increase the number at the end of each pass. -tipp: The output is `System.out.println(number + ". " + line);` -lösung: l23-java-nummern-loesung.visustruct -::: - -## Writing to a file with PrintWriter - -For writing there is the `PrintWriter`. Its methods have the same names as those of `System.out`: - -```java -PrintWriter out = new PrintWriter("greeting.txt"); -out.println("Hello Zora!"); -out.println("See you tomorrow, Ludwig"); -out.close(); -``` - -Afterwards `greeting.txt` contains: - -``` -Hello Zora! -See you tomorrow, Ludwig -``` - -- `println` writes the text and then starts a new line, `print` writes without a new line. -- If the file already exists, it is **overwritten**. The old content is gone. -- After the run, VisuStruct shows below the output what was saved in the file. - -::: ludwig -Don't forget `close()`! The PrintWriter first collects the text in a buffer and only writes it to the file when it is closed. Without `close()` the file stays empty. I once lost a whole story that way. -::: - -## Exercise: Adding to the shopping list - -::: übung l23-java-ergaenzen -titel: Adding to the shopping list -code: java -auftrag: Ludwig still needs cake. Read `shopping.txt` and write each line to the new file `shopping-new.txt`. Then add **Cake** as the last line. At the end, print how many lines were saved: **5 lines saved**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 lines saved -datei: shopping-new.txt -> Hay | Apples | Carrots | Dandelions | Cake -braucht: whileSchleife -tipp: In the loop, read a line with `file.nextLine()` and write it straight out again with `out.println(line)`. -tipp: After the loop comes `out.println("Cake");`. Close both files with `close()`, otherwise `shopping-new.txt` stays empty. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Game: What is in the file? - -::: spiel frage -titel: Game: What is in the file? -intro: Ludwig writes files with the PrintWriter. Read the code and predict what is in the file afterwards. Six rounds. -frage: What is in the file afterwards? - -runde: - PrintWriter out = new PrintWriter("greeting.txt"); - out.println("Hello"); - out.println("Zora"); - out.close(); -- [x] Hello\nZora -- HelloZora -- Zora -> `println` starts a new line after each text. - -runde: - PrintWriter out = new PrintWriter("word.txt"); - out.print("He"); - out.print("llo"); - out.println("!"); - out.close(); -- He\nllo\n! -- [x] Hello! -- He llo ! -> `print` carries on without a new line, so the parts are joined directly. - -runde: - PrintWriter out = new PrintWriter("secret.txt"); - out.println("The treasure is in the tower"); -- The treasure is in the tower -- [x] Nothing, the file stays empty. -- Java reports an error. -> Without `close()` the text never reaches the file. It stays in the buffer. - -runde: - // Before, note.txt contains: Old - PrintWriter out = new PrintWriter("note.txt"); - out.println("New"); - out.close(); -- Old\nNew -- [x] New -- New\nOld -> A new PrintWriter overwrites the file. The old content is gone. - -runde: - PrintWriter out = new PrintWriter("numbers.txt"); - for (int i = 1; i <= 3; i++) { - out.println(i * 2); - } - out.close(); -- 1\n2\n3 -- 246 -- [x] 2\n4\n6 -> The loop writes 2, 4 and 6, each number on its own line. - -runde: - // shopping.txt contains: Hay, Apples (one per line) - Scanner file = new Scanner(new File("shopping.txt")); - PrintWriter out = new PrintWriter("big.txt"); - while (file.hasNextLine()) { - out.println(file.nextLine().toUpperCase()); - } - file.close(); - out.close(); -- Hay\nApples -- [x] HAY\nAPPLES -- HAYAPPLES -> Each line is read, turned into capital letters and written on its own line. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain why programs save data in files. → Why files? -- I can read a text file line by line with Scanner, hasNextLine and nextLine. → Reading a file with Scanner -- I can explain why Java requires try and catch or throws for files. → What if the file is missing? -- I can write a file with PrintWriter and finish it with close(). → Writing to a file with PrintWriter -- I can read a file, add to it and save it again with numbers. → Exercise: Adding to the shopping list -::: - -## Quiz - -::: quiz -? What does hasNextLine() return? -→ Reading a file with Scanner -- [ ] the next line -- [x] true if there is another line, otherwise false -- [ ] the number of lines -> With hasNextLine() the loop checks whether to carry on. - -? What happens if you call new PrintWriter("list.txt") for a file that already exists? -→ Writing to a file with PrintWriter -- [ ] The new text is added at the end. -- [x] The file is overwritten. -- [ ] Java reports an error. - -? Why does a file stay empty even though println was called? -→ Writing to a file with PrintWriter -- [ ] Because println only writes to the console. -- [x] Because close() is missing. -- [ ] Because the file name ends in .txt. -> Only close() writes the buffer into the file. - -? Match each method to its job. -→ Reading a file with Scanner -= nextLine() -> reads the next line -= println(text) -> writes a line -= close() -> closes the file -::: diff --git a/web/lessons/en/24-zeile-fuer-zeile.md b/web/lessons/en/24-zeile-fuer-zeile.md deleted file mode 100644 index 12a69712..00000000 --- a/web/lessons/en/24-zeile-fuer-zeile.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -titel: Line by line with BufferedReader -bereich: Files -kurz: Read with BufferedReader and readLine until null comes, and read a whole file at once with Files. ---- - -## Skills I will gain now - -::: kompetenzen -- read a file with BufferedReader and readLine -- spot the end of a file, because readLine returns null there -- count how often a word appears in a file -- read a whole file with Files.readString and split it into lines with split -- find the longest entry in a file -::: - -## The cake is gone! - -The morning after the party, Ludwig's cake has disappeared. Luckily the castle keeps a logbook. Every line starts with the day and the time: - -::: datei logbook.txt -Mo 08:15 Ludwig feeds the goats -Mo 12:30 Zora reads in the tower -Mo 18:00 Fox sneaks past the gate -Tu 09:00 Ludwig bakes a cake -Tu 14:45 Hare brings carrots -Tu 16:20 Fox asks for the recipe -Tu 23:55 The cake is gone -We 07:00 Zora finds crumbs at Fox's den -::: - -You could already read it with the Scanner from lesson 23. For files, though, professionals often use the **BufferedReader**. - -## Reading with BufferedReader - -```java -import java.io.*; - -BufferedReader reader = new BufferedReader(new FileReader("logbook.txt")); -String line = reader.readLine(); -while (line != null) { - System.out.println(line); - line = reader.readLine(); -} -reader.close(); -``` - -- `new FileReader("logbook.txt")` opens the file, and `new BufferedReader(…)` puts a buffer around it. It fetches the file in large chunks and is therefore faster for long files. -- `readLine()` returns the next line. When there are no more lines, it returns **null**. -- That is why the loop runs as long as `line != null`. At the end of each pass it reads the next line. - -::: diagramm dt-buffered.visustruct -First read a line, then check, then read the next one at the end of the pass. -::: - -**null** means "nothing at all". It is not the empty text `""`, but no text. A variable holding `null` has no methods: `line.length()` then stops with a `NullPointerException`. So always check `line != null` first. - -| | Scanner | BufferedReader | -|---|---|---| -| Create | `new Scanner(new File(…))` | `new BufferedReader(new FileReader(…))` | -| Is there another line? | `hasNextLine()` | `readLine()` returns `null` | -| Next line | `nextLine()` | `readLine()` | -| Strength | can also read numbers (`nextInt()`) | fast, even with large files | - -::: ludwig -The loop looks different from the Scanner one: the first line is read **before** the loop, every further one **at the end** of the pass. If you forget the read at the end, the program reads the same line again and again and never stops. -::: - -::: angeberwissen -Where a line ends is not the same everywhere: Windows writes two invisible characters, “carriage return” and “line feed”, Linux and macOS only the line feed. The names come from the typewriter, where the carriage moved back and the paper moved up one line. `readLine()` copes with both. -::: - -## Exercise: How often does the fox appear? - -::: übung l24-java-fuchs -titel: How often does the fox appear? -code: java -auftrag: Read the logbook with the BufferedReader and count the lines that contain **Fox**. At the end, print **Fox:** and the count. -start: l24-java-fuchs-start.visustruct -test: - -> Fox: 3 -braucht: whileSchleife, verzweigung -tipp: Create `int count = 0;` before the loop. In the loop, check `line.contains("Fox")`. -tipp: The `line = reader.readLine();` belongs at the end of the loop, after the if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Closing automatically - -So that `close()` isn't forgotten even after an exception, there is a short form of try: whatever is created in the round brackets after `try` is closed by Java on its own at the end. - -```java -try (BufferedReader reader = new BufferedReader(new FileReader("logbook.txt"))) { - String line = reader.readLine(); - while (line != null) { - System.out.println(line); - line = reader.readLine(); - } -} catch (IOException e) { - System.out.println("The logbook is missing."); -} -``` - -This is called **try-with-resources**. `IOException` catches all errors while reading and writing, including a missing file. - -> Like try and catch, try-with-resources only runs with real Java (lesson 22). In the simulation, you write `close()` yourself. - -## The shortcut: Files - -Since Java 11 it is even shorter. `Files.readString` reads the whole file at once, as you know from lesson 17. With `split("\n")` you split the text into an array of lines: - -```java -import java.nio.file.*; - -String text = Files.readString(Path.of("logbook.txt")); -String[] lines = text.split("\n"); -for (int i = 0; i < lines.length; i++) { - System.out.println(lines[i]); -} -``` - -Now all lines are in the array. You can look at them in any order, for example backwards or only every second one. - -| Way | Good for | -|---|---| -| `Scanner` | small files, reading numbers and words | -| `BufferedReader` | large files, line by line | -| `Files.readString` | small files that fit into the program as a whole | - -::: ludwig -A file with a million lines is better read line by line with the BufferedReader. With `Files.readString` the whole file would have to go into memory at once. -::: - -## Exercise: The longest entry - -::: übung l24-java-laengster -titel: The longest entry -code: java -auftrag: In the start code, the lines of the logbook are already in the array `lines`. Find the longest line and print it like this: **Longest entry:** followed by the line. -start: l24-java-laengster-start.visustruct -test: - -> Longest entry: We 07:00 Zora finds crumbs at Fox's den -braucht: forSchleife, verzweigung -tipp: Remember the longest line so far in `String longest = lines[0];` and go through the other lines with a for loop. -tipp: If `lines[i].length() > longest.length()`, then `lines[i]` becomes the new longest. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Game: File detective - -::: spiel frage -titel: Game: File detective -intro: Who took the cake? Each program searches the **logbook** from the top. Predict what it prints and collect the clues. Five rounds. -frage: What does the program print? - -runde: - String line = reader.readLine(); - line = reader.readLine(); - System.out.println(line.substring(9)); -- Ludwig feeds the goats -- [x] Zora reads in the tower -- Mo 12:30 Zora reads in the tower -> The second `readLine()` returns the second line. The text after the time starts at index 9. - -runde: - int n = 0; - String line = reader.readLine(); - while (line != null) { - if (line.startsWith("Tu")) n++; - line = reader.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Four entries are from Tuesday. - -runde: - String line = reader.readLine(); - while (line != null) { - if (line.contains("cake")) { - System.out.println(line.substring(3, 8)); - } - line = reader.readLine(); - } -- [x] 09:00\n23:55 -- 09:00 -- Tu\nTu -> Two lines contain "cake". `substring(3, 8)` cuts out the time. - -runde: - String line = reader.readLine(); - String last = ""; - while (line != null) { - if (line.contains("Fox")) last = line; - line = reader.readLine(); - } - System.out.println(last); -- Tu 16:20 Fox asks for the recipe -- [x] We 07:00 Zora finds crumbs at Fox's den -- Mo 18:00 Fox sneaks past the gate -> `last` is overwritten at every match. At the end it holds the last match. "Fox's den" contains "Fox" too. - -runde: - String line = reader.readLine(); - while (line != null) { - if (line.contains("crumbs")) { - int at = line.indexOf("at "); - System.out.println(line.substring(at + 3)); - } - line = reader.readLine(); - } -- [x] Fox's den -- at Fox's den -- crumbs -> The crumbs are at Fox's den. The fox asked for the recipe, and at 23:55 the cake was gone. Case solved! -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can read a file with BufferedReader and readLine. → Reading with BufferedReader -- I can spot the end of a file, because readLine returns null there (line != null). → Reading with BufferedReader -- I can count how often a word appears in a file. → Exercise: How often does the fox appear? -- I can read a whole file with Files.readString and split it into lines with split. → The shortcut: Files -- I can find the longest entry in a file. → Exercise: The longest entry -::: - -## Quiz - -::: quiz -? What does readLine() return when the file has ended? -→ Reading with BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null means: there is no more text. - -? Why is there another line = reader.readLine() at the end of the loop? -→ Reading with BufferedReader -- [ ] So that the file is closed. -- [x] Otherwise line never changes and the loop never stops. -- [ ] So that the first line is printed twice. - -? What does try-with-resources do? -→ Reading with BufferedReader -- [ ] It reads the file faster. -- [x] It closes the file on its own at the end. -- [ ] It prevents every exception. - -? Match each way to what it is good for. -→ The shortcut: Files -= BufferedReader -> large files, line by line -= Files.readString -> small files all at once -= Scanner -> reading numbers and words -::: diff --git a/web/lessons/en/25-tabellen-als-csv.md b/web/lessons/en/25-tabellen-als-csv.md deleted file mode 100644 index 1757c55c..00000000 --- a/web/lessons/en/25-tabellen-als-csv.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -titel: Tables as CSV -bereich: Files -kurz: Read a table as a CSV file, split each line with split and calculate with the numbers. ---- - -## Skills I will gain now - -::: kompetenzen -- explain how a CSV file is built: header, records, separator -- split a line with split and make a number out of it with Integer.parseInt -- skip the header before calculating -- calculate an average from a column of a CSV file -- find the best value in a column of a CSV file -- name one strength and one weakness of CSV -::: - -## A table in a text file - -Mrs Goat, the teacher at the castle school, has saved her class's grades as a table. As in Germany, 1 is the best grade and 6 the worst: - -| Name | Maths | German | English | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Fox | 4 | 3 | 5 | -| Hare | 2 | 1 | 3 | - -In the file, the table looks like this: - -::: datei class.csv -Name;Maths;German;English -Ludwig;2;3;1 -Zora;1;2;2 -Fox;4;3;5 -Hare;2;1;3 -::: - -This is a **CSV file**. CSV stands for "Comma-Separated Values". Each line is a row of the table, and the columns are separated by a separator character. In English-speaking countries this is usually a comma, in Germany and many other countries a **semicolon** `;`, because the comma is already used in numbers like `2,5`. We use the semicolon. - -- The first line is the **header**. It says what is in the columns. -- Every further line is a **record**, here one pupil. -- Excel, LibreOffice and Numbers open CSV files directly as a table. - -## Splitting a line with split - -You know `split(";")` from lesson 17. It splits a text at every semicolon and returns an array: - -```java -String line = "Zora;1;2;2"; -String[] parts = line.split(";"); -// parts[0] is "Zora", parts[1] is "1", parts[2] is "2", parts[3] is "2" -``` - -Afterwards the grades are still **text**. You can only calculate with them once you have turned them into a number with `Integer.parseInt`: - -```java -int maths = Integer.parseInt(parts[1]); -``` - -::: diagramm dt-csv.visustruct -Split a CSV line and read the maths grade as a number. -::: - -## Reading the whole file - -For the whole file, you combine the BufferedReader from lesson 24 with `split`. The header contains no grades, so you read it before the loop and skip it: - -```java -BufferedReader reader = new BufferedReader(new FileReader("class.csv")); -String line = reader.readLine(); // header -line = reader.readLine(); // first record -while (line != null) { - String[] parts = line.split(";"); - System.out.println(parts[0] + ": Maths " + parts[1]); - line = reader.readLine(); -} -reader.close(); -``` - -::: ludwig -Without the second `readLine()` before the loop, the header ends up in the loop. Then `Integer.parseInt("Maths")` tries to make a number out of the word, and Java stops with a `NumberFormatException`. -::: - -## Exercise: Maths average - -::: übung l25-java-schnitt -titel: Maths average -code: java -auftrag: Calculate the average maths grade. Skip the header, add up the maths grades (column 1) and divide by the number of records. Print **Maths average:** and the average. -start: l25-java-schnitt-start.visustruct -test: - -> Maths average: 2.25 -braucht: whileSchleife -tipp: Create `double sum = 0;` and `int count = 0;` before the loop. With `double`, nothing is cut off when dividing. -tipp: In the loop: `String[] parts = line.split(";");`, then `sum = sum + Integer.parseInt(parts[1]);` and `count++;`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Exercise: Best grade in English - -::: übung l25-java-beste -titel: Best grade in English -code: java -auftrag: Who has the best grade in English (column 3)? Print **Best grade in English:**, the name and the grade in brackets. -start: l25-java-beste-start.visustruct -test: - -> Best grade in English: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Remember the best grade so far in `int bestGrade = 7;` and the name in `String bestName = "";`. Every real grade is better than 7. -tipp: If `grade < bestGrade`, remember the new grade and `parts[0]` as the name. -lösung: l25-java-beste-loesung.visustruct -::: - -## Strengths and weaknesses of CSV - -| Good | Tricky | -|---|---| -| Tables take up little space. | If the separator appears in the text, for example `Hare;very good; hard-working`, the line falls apart into too many parts. | -| Excel and other programs open the file directly. | Everything is a flat table. If an animal has several favourite foods, that doesn't fit well into one cell. | -| Every program can read it with `split`. | The file doesn't say whether `1` is a number or a text. | - -::: ludwig -Excel saves CSV files with semicolons in Germany and with commas in England and the USA. That is why someone opening a file from another country sometimes sees everything in one single column. -::: - -## Game: Broken line - -::: spiel bombe -titel: Game: Broken line -intro: In every CSV file **exactly one line is broken**, and the bomb is ticking. Compare each line with the header and tap the broken line before time runs out. Five rounds, 20 seconds each. -zeit: 20 -daten: csv - -runde: - Name;Maths;German;English - Ludwig;2;3;1 - Zora;1 - Fox;4;3;5 -frage: Every line needs four columns. -fehler: 3 -besser: Zora;1;2;2 -> Two grades are missing for Zora. Then there is no `parts[3]`, and Java stops with an `ArrayIndexOutOfBoundsException`. - -runde: - Name;Grade;Comment - Ludwig;2;works well with others - Hare;1;very good; hard-working - Zora;1;reads a lot -frage: Every line needs three columns. -fehler: 3 -besser: Hare;1;very good, hard-working -> The semicolon in the comment splits off a fourth column. The separator must not appear in the text. - -runde: - Name;Maths;German;English - Ludwig;2;3;1 - Zora;1;2;2 - Fox,4,3,5 -frage: The separator is a semicolon. -fehler: 4 -besser: Fox;4;3;5 -> Here there are commas instead of semicolons. `split(";")` finds no semicolon and returns the whole line as a single part. - -runde: - Name;Maths;German;English - Hare;2;1;3 - Fox;four;3;5 - Ludwig;2;3;1 -frage: Grades are numbers. -fehler: 3 -besser: Fox;4;3;5 -> `Integer.parseInt("four")` cannot make a number out of the word and throws a `NumberFormatException`. - -runde: - Name;Maths;German;English - Zora;1;2;2 - Hare;2;1;3 - Ludwig;2;;1 -frage: No field may be empty. -fehler: 4 -besser: Ludwig;2;3;1 -> There is nothing between the two semicolons. `Integer.parseInt("")` throws a `NumberFormatException`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain how a CSV file is built: header, records, separator. → A table in a text file -- I can split a line with split and make a number out of it with Integer.parseInt. → Splitting a line with split -- I can skip the header before calculating. → Reading the whole file -- I can calculate an average from a column of a CSV file. → Exercise: Maths average -- I can find the best value in a column of a CSV file. → Exercise: Best grade in English -- I can name one strength and one weakness of CSV. → Strengths and weaknesses of CSV -::: - -## Quiz - -::: quiz -? What does "Hare;2;1;3".split(";")[2] return? -→ Splitting a line with split -- [ ] "2" -- [x] "1" -- [ ] 1 -> Counting starts at 0. And the result is a text, not a number. - -? Why does the program call readLine() twice before the loop? -→ Reading the whole file -- [ ] So that the file is read faster. -- [x] The first line is the header without grades. It is skipped. -- [ ] Because readLine always needs two lines. - -? What happens with Integer.parseInt("Maths")? -→ Splitting a line with split -- [ ] It returns 0. -- [x] Java throws a NumberFormatException. -- [ ] It returns the length of the word. - -? Match. -→ Strengths and weaknesses of CSV -= header -> says what is in the columns -= record -> a line with values -= separator -> separates the columns, often ; -::: diff --git a/web/lessons/en/26-json.md b/web/lessons/en/26-json.md deleted file mode 100644 index 145782ef..00000000 --- a/web/lessons/en/26-json.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -titel: Data as JSON -bereich: Files -kurz: Understand JSON files, get values out with indexOf and substring, and write JSON yourself. ---- - -## Skills I will gain now - -::: kompetenzen -- recognise objects, keys, values and lists in JSON -- get a value out of a JSON line with indexOf and substring -- output all values of one key from a JSON file -- build and save a JSON entry with + and \" -- save data as a JSON file -- name one strength and one weakness of JSON -::: - -## Data with names - -The castle zoo stores its animals in a file `zoo.json`: - -::: datei zoo.json -{ - "zoo": "Castle Zoo", - "animals": [ - {"name": "Ludwig", "species": "billy goat", "age": 7}, - {"name": "Zora", "species": "goat", "age": 6}, - {"name": "Fox", "species": "red fox", "age": 4}, - {"name": "Hare", "species": "brown hare", "age": 2} - ] -} -::: - -This is **JSON**, the "JavaScript Object Notation". Almost every web app and every game sends and stores data like this. Unlike in a CSV file, each value has its **name** in front of it, the **key**. - -| Character | Meaning | Example | -|---|---|---| -| `{ … }` | an **object**: pairs of key and value | `{"name": "Zora", "age": 6}` | -| `"key": value` | a pair, separated by a colon | `"age": 6` | -| `[ … ]` | a **list** of values, like an array | `"animals": [ … ]` | -| `,` | separates the pairs and the entries of a list | | -| `"…"` | a text. Numbers and `true`/`false` have no quotation marks. | `"goat"`, `6` | - -Objects and lists can be **nested**: the whole zoo object contains the list `animals`, and each animal is an object again. As with arrays, the list counts from 0: `animals[1].name` is `"Zora"`. - -::: angeberwissen -Douglas Crockford made JSON popular in the early 2000s. In the licence of his JSON program he wrote: “The Software shall be used for Good, not Evil.” So the company IBM asked whether it could use it anyway. He replied that IBM and its customers were allowed to use it for evil. -::: - -## Getting a value out - -Professionals read JSON with a library, for example **Gson**. It is not part of Java and has to be added first. For our zoo file, the string methods from lesson 17 are enough, because each animal is on its own line. - -In Java you write a quotation mark inside a text as `\"`. So the text `"\"name\": \""` is `"name": "`, exactly what comes before each name. - -```java -String line = "{\"name\": \"Zora\", \"species\": \"goat\", \"age\": 6},"; -int start = line.indexOf("\"name\": \"") + 9; -int end = line.indexOf("\"", start); -String name = line.substring(start, end); -``` - -- `indexOf("\"name\": \"")` finds the start of `"name": "`. That is 9 characters, so the name begins 9 characters further on. -- `indexOf("\"", start)` searches for the next quotation mark **from** `start`. That is where the name ends. -- `substring(start, end)` cuts out the name: `Zora`. - -::: diagramm dt-json.visustruct -Getting the name out of a JSON line. -::: - -## Exercise: All the names in the zoo - -::: übung l26-java-namen -titel: All the names in the zoo -code: java -auftrag: Read `zoo.json` line by line. In every line that contains `"name"`, print only the name. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Fox | Hare -braucht: whileSchleife, verzweigung -tipp: Check with `line.contains("\"name\"")` whether there is a name in the line. Skip the other lines. -tipp: Inside the if: `int start = line.indexOf("\"name\": \"") + 9;`, then `int end = line.indexOf("\"", start);` and `System.out.println(line.substring(start, end));`. -lösung: l26-java-namen-loesung.visustruct -::: - -## Writing JSON - -To write, you put the text together with `+` and write it to the file with the PrintWriter from lesson 23. Texts in JSON need quotation marks, so `\"` in the Java text, numbers don't: - -```java -String name = "Zora"; -int points = 15; -String entry = "{\"name\": \"" + name + "\", \"points\": " + points + "}"; -// entry is {"name": "Zora", "points": 15} -``` - -In a list there is a comma between the entries, but **none** after the last one. You check that with an if. - -::: ludwig -A single forgotten quotation mark or one comma too many, and no program can read the file any more. JSON is strict. That is why professionals also use a library for writing. -::: - -## Exercise: Saving points as JSON - -::: übung l26-java-schreiben -titel: Saving points as JSON -code: java -auftrag: Save the points from the quiz tournament as a list in `points.json`. Each line contains an object with **name** and **points**, and there is a comma between the objects. At the end, print **3 entries saved**. -start: l26-java-schreiben-start.visustruct -test: - -> 3 entries saved -datei: points.json -> [ | {"name": "Ludwig", "points": 12}, | {"name": "Zora", "points": 15}, | {"name": "Fox", "points": 9} | ] -braucht: forSchleife, verzweigung -tipp: Go through the arrays with `for (int i = 0; i < names.length; i++)`. Build the entry with `" {\"name\": \"" + names[i] + "\", \"points\": " + points[i] + "}"`. -tipp: If `i < names.length - 1`, add `","`. After the loop, write `"]"` and close the file. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Strengths and weaknesses of JSON - -| Good | Tricky | -|---|---| -| Every value has a name. You can see at once what `6` means. | The file is longer than a CSV file, because the keys appear on every line. | -| Data can be nested, for example a list inside an object. | Java can't read JSON on its own. You need a library such as Gson. | -| JSON is the standard on the web. Almost every programming language knows it. | One character too many or too few makes the whole file unreadable. | - -## Game: Which key? - -::: spiel frage -titel: Game: Which key? -intro: Ludwig is looking for values in JSON files. Read the file and say which value is meant. Five rounds. - -runde: - {"name": "Zora", "species": "goat", "age": 6} -frage: What is the value of **age**? -- "age" -- [x] 6 -- "goat" -> The key comes before the colon, the value after it. Numbers have no quotation marks. - -runde: - { - "animals": [ - {"name": "Ludwig", "age": 7}, - {"name": "Zora", "age": 6}, - {"name": "Fox", "age": 4} - ] - } -frage: What is **animals[1].name**? -- Ludwig -- [x] Zora -- Fox -> The list counts from 0, like an array. `animals[1]` is the second animal. - -runde: - { - "zoo": "Castle Zoo", - "boss": {"name": "Ludwig", "species": "billy goat"} - } -frage: How do you get to **billy goat**? -- [x] boss.species -- species -- zoo.boss.species -> `species` is inside the object `boss`. So the way goes through `boss`. - -runde: - { - "name": "Hare", - "favouriteFood": ["carrots", "clover", "dandelions"] - } -frage: What is **favouriteFood[2]**? -- clover -- carrots -- [x] dandelions -> Index 0 is carrots, 1 is clover, 2 is dandelions. Something like this hardly fits into one cell in CSV. - -runde: - {"name": "Fox", "age": 4, "hungry": true} -frage: Which key has a truth value? -- name -- age -- [x] hungry -> `true` and `false` have no quotation marks, like numbers. In Java this would be a `boolean`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can recognise objects, keys, values and lists in JSON. → Data with names -- I can get a value out of a JSON line with indexOf and substring. → Getting a value out -- I can output all values of one key from a JSON file. → Exercise: All the names in the zoo -- I can build and save a JSON entry with + and \". → Writing JSON -- I can save data as a JSON file. → Exercise: Saving points as JSON -- I can name one strength and one weakness of JSON. → Strengths and weaknesses of JSON -::: - -## Quiz - -::: quiz -? What comes before each value in JSON? -→ Data with names -- [ ] its line number -- [x] its key, for example "name" -- [ ] a semicolon -> Key and value are separated by a colon. - -? How do you write a quotation mark inside a Java text? -→ Writing JSON -- [ ] "" -- [x] \" -- [ ] '"' - -? In {"animals": [{"name": "Ludwig"}, {"name": "Zora"}]}, what is the value of animals[0].name? -→ Data with names -- [x] Ludwig -- [ ] Zora -- [ ] animals -> The list counts from 0. - -? Match. -→ Strengths and weaknesses of JSON -= { } -> an object with keys and values -= [ ] -> a list -= : -> separates key and value -::: diff --git a/web/lessons/en/27-formate-im-vergleich.md b/web/lessons/en/27-formate-im-vergleich.md deleted file mode 100644 index f5bc1b51..00000000 --- a/web/lessons/en/27-formate-im-vergleich.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -titel: Comparing formats -bereich: Files -kurz: Compare the same data as txt, CSV and JSON, choose the right format and convert a file into another format. ---- - -## Skills I will gain now - -::: kompetenzen -- recognise the same data as txt, CSV and JSON -- compare strengths and weaknesses of txt, CSV and JSON -- convert a CSV file into sentences -- convert a CSV file into JSON -::: - -## The same animals three times - -Ludwig has saved the castle's animals in three files. They all contain the same, just written down differently. - -As text, the way you would write it to a person: - -::: datei animals.txt -Ludwig (billy goat) is 7 years old. -Zora (goat) is 6 years old. -Fox (red fox) is 4 years old. -::: - -As a table in CSV: - -::: datei animals.csv -Name;Species;Age -Ludwig;billy goat;7 -Zora;goat;6 -Fox;red fox;4 -::: - -As JSON: - -::: datei animals.json -[ - {"name": "Ludwig", "species": "billy goat", "age": 7}, - {"name": "Zora", "species": "goat", "age": 6}, - {"name": "Fox", "species": "red fox", "age": 4} -] -::: - -## What can each format do? - -| | txt | CSV | JSON | -|---|---|---|---| -| Easy for people to read | yes | so-so | so-so | -| Fixed order for programs | no | yes, columns | yes, keys | -| What does a value mean? | it's in the sentence | it's in the header | it's right in front of it | -| Nesting (a list inside an entry) | no | hard | yes | -| Opens in Excel | no | yes | no | -| Reading in Java | Scanner, BufferedReader | plus `split` | string methods or a library | -| Size | small | smallest | largest | - -In short: - -- **txt** for texts that people read: letters, notes, a logbook. -- **CSV** for tables: grades, measurements, a high-score list. Every line has the same columns. -- **JSON** for data with structure: game saves, settings, everything that goes back and forth between programs and on the web. - -::: ludwig -When your weather app fetches the temperature, the answer almost always comes as JSON. And when a teacher exports the grades from the school software, they often get a CSV file. -::: - -## Exercise: From CSV to sentences - -::: übung l27-java-saetze -titel: From CSV to sentences -code: java -auftrag: Convert `animals.csv` into text. For each animal, write a sentence like **Ludwig (billy goat) is 7 years old.** into the file `sentences.txt`. At the end, print **3 sentences saved**. -start: l27-java-saetze-start.visustruct -test: - -> 3 sentences saved -datei: sentences.txt -> Ludwig (billy goat) is 7 years old. | Zora (goat) is 6 years old. | Fox (red fox) is 4 years old. -braucht: whileSchleife -tipp: The start code already skips the header. Split each line with `String[] parts = line.split(";");`. -tipp: The sentence is `parts[0] + " (" + parts[1] + ") is " + parts[2] + " years old."`. Count along with `count++` and read the next line at the end of the loop. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Exercise: From CSV to JSON - -::: übung l27-java-json -titel: From CSV to JSON -code: java -auftrag: Convert `animals.csv` into JSON and save it in `animals-new.json`, exactly like `animals.json` above. At the end, print **3 animals saved**. -start: l27-java-json-start.visustruct -test: - -> 3 animals saved -datei: animals-new.json -> [ | {"name": "Ludwig", "species": "billy goat", "age": 7}, | {"name": "Zora", "species": "goat", "age": 6}, | {"name": "Fox", "species": "red fox", "age": 4} | ] -braucht: forSchleife, verzweigung -tipp: `lines[0]` is the header. So start the for loop at `i = 1` and split `lines[i]` with `split(";")`. -tipp: Build the entry with `\"` as in lesson 26. The age is a number and has no quotation marks. Only add a comma if `i < lines.length - 1`. -lösung: l27-java-json-loesung.visustruct -::: - -## Game: Which format fits? - -::: spiel frage -titel: Game: Which format fits? -intro: Ludwig wants to save data and doesn't know which format to use. Help him. Six rounds. -antworten: txt | CSV | JSON - -runde: -frage: The grades of the whole class should be opened in Excel. -richtig: CSV -> Grades are a table, and Excel opens CSV files directly. - -runde: -frage: Ludwig writes a letter to Zora. -richtig: txt -> A letter is text for people. It needs no columns and no keys. - -runde: -frage: A game save with a name, a level and a list of all the keys collected. -richtig: JSON -> A list inside an entry can be nested in JSON. In CSV it hardly fits into one cell. - -runde: -frage: A measuring device saves the time and temperature every minute, for a whole year. -richtig: CSV -> Many identical lines with the same columns: CSV is the smallest for this. JSON would repeat the keys on every line. - -runde: -frage: A weather app fetches the forecast from a server on the internet. -richtig: JSON -> JSON is the standard on the web. Almost every app gets its data this way. - -runde: -frage: The castle logbook, where every entry is a short sentence. -richtig: txt -> Every line is a sentence for people. It is read line by line, as in lesson 24. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can recognise the same data as txt, CSV and JSON. → The same animals three times -- I can compare strengths and weaknesses of txt, CSV and JSON. → What can each format do? -- I can convert a CSV file into sentences. → Exercise: From CSV to sentences -- I can convert a CSV file into JSON. → Exercise: From CSV to JSON -::: - -## Quiz - -::: quiz -? Which format does Excel open directly as a table? -→ What can each format do? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Why is a JSON file usually larger than a CSV file with the same data? -→ What can each format do? -- [ ] Because JSON stores more numbers. -- [x] Because the keys are repeated in every entry. -- [ ] Because JSON uses no commas. - -? In which format does every value say what it means? -→ What can each format do? -- [ ] txt -- [ ] CSV -- [x] JSON -> In JSON the key comes right before the value, for example "age": 7. - -? Match each purpose with a format. -→ What can each format do? -= a letter to Zora -> txt -= the class grade list -> CSV -= a game save with lists -> JSON -::: diff --git a/web/lessons/en/28-erstes-fenster.md b/web/lessons/en/28-erstes-fenster.md deleted file mode 100644 index 2ff87904..00000000 --- a/web/lessons/en/28-erstes-fenster.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -titel: Your first window -bereich: Windows with Swing -kurz: Open your own window with Java and Swing, explained line by line. ---- - -## Skills I will gain now - -::: kompetenzen -- switch on “Real Java” in the Code area -- open a window with a title and a size -- explain what each line of the window program does -- explain why setVisible(true) comes last -::: - -## Programs with windows - -So far your programs have written text to the console. Most programs you know, however, have **windows** with buttons and text fields. This is called a graphical user interface. - -Java comes with a toolbox for this: **Swing**. In VisuStruct you switch it on like this: - -1. Open the [Code](#zeige-codelive) area on the right. -2. Click the [gear](#zeige-einstellungen) at the top right and choose Java as the **Programming language**. -3. Below that, in the Java section, tick **Real Java (with Swing)**. - -The first time you click [▶︎ Run](#zeige-ausfuehren), VisuStruct downloads about 40 MB once, which takes a moment. After that your code runs as real Java, and your windows appear in the Code area in the **Java windows** box above the console. - -> With “Real Java”, input from the console (`Scanner`) does not work yet. For programs with input, untick the box again. - -::: angeberwissen -Swing has been around since 1998. Before that, Java only had the older AWT with a class called `Frame`. So that nobody mixes up the new building blocks, almost all names in Swing start with **J**: `JFrame`, `JButton`, `JLabel`. The J tells you straight away that it is Swing. -::: - -## The whole program - -This is the smallest program that opens a window. Type it into the Code area and click [▶︎ Run](#zeige-ausfuehren): - -```java -import javax.swing.*; - -public class MyWindow { - public static void main(String[] args) { - JFrame window = new JFrame("My first window"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); - } -} -``` - -An empty grey window with the title “My first window” appears. That's all, but it is your very own window! - -You already know the frame with `public class` and `main` from the Java lesson. The first line and the four lines inside `main` are new. Let's look at them one by one. - -::: ludwig -Look at the structure chart next to the code: every line in `main` is a box, one below the other. So at the start, a window program is just a sequence. -::: - -## Line by line - -| Line | What it does | -|---|---| -| `import javax.swing.*;` | Brings the Swing toolbox into the program. Without this line Java does not know `JFrame`. | -| `JFrame window = new JFrame("My first window");` | Builds a new window. The text in brackets is the title at the top of the window frame. After that you talk to it through the variable `window`. | -| `window.setSize(300, 200);` | The window becomes 300 pixels wide and 200 pixels high. | -| `window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);` | When someone closes the window, the program ends too. | -| `window.setVisible(true);` | Only now does the window become visible. | - -The dot in `window.setSize(…)` means: “Window, do this!” Before the dot is who should do something, after it is what they should do. - -> Without `setDefaultCloseOperation`, Java only hides the window when it is closed. The program then keeps running invisibly. - -::: quiz -? What does the dot in `window.setSize(300, 200)` mean? -- [x] “Window, do this!”: before the dot is who does something, after it what they do. -- [ ] It separates the two numbers. -- [ ] It ends the line. -> Before the dot is the object `window`, after it its method `setSize`. -::: - -## Why setVisible comes last - -`setVisible(true)` shows the window exactly as it is at that moment. So you set everything up first: title, size, and later buttons too. Only at the very end do you make the window visible. - -Things you put into the window after `setVisible(true)` often do not appear. Java does not redraw the window on its own. Sometimes they only show up when you resize the window. Mistakes like that are hard to find. - -::: ludwig -Set up first, then show. Like in the theatre: the curtain only goes up once the stage is ready. -::: - -Try it: delete the line with `setVisible(true)` and run the program again. The program runs, but you do not see a window. - -## Classes and objects - -This is not in the curriculum, but it helps you understand Swing. - -::: angeberwissen -Java is really an **object-oriented** language. `JFrame` is a class, a blueprint for windows. With `new JFrame(…)` Java builds a real window from this blueprint, an **object**. With two `new` you could build two windows, both from the same blueprint. - -`setSize` and `setVisible` are methods that every window object has. That is why you write `window.setSize(…)`: the object `window` runs its method. Your program itself is a class too, namely `MyWindow`. -::: - -## Game: What happens? - -::: spiel frage -titel: Game: What happens? -intro: Ludwig has been tinkering with the window program. Read the lines and predict what happens. Six rounds. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: What do you see after running it? -- a window with the title "Hello" -- [x] no window at all -- an error message -> `setVisible(true)` is missing. The window is built, but it stays invisible. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(200, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); -frage: What does the window look like? -- wider than high -- [x] higher than wide -- square -> The first number is the width, the second the height: 200 wide, 300 high. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(300, 200); - window.setVisible(true); -frage: You close the window. What happens to the program? -- It ends. -- [x] It keeps running invisibly. -- The window opens again right away. -> Without `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)` Java only hides the window. - -runde: - JFrame window = new JFrame(); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); -frage: What is written at the top of the window frame? -- window -- JFrame -- [x] nothing, the title is empty -> Without text in the brackets the window has no title. The variable name `window` never shows up in the window. - -runde: - public class Test { - public static void main(String[] args) { - JFrame window = new JFrame("Test"); - window.setVisible(true); - } - } -frage: What does javac report? -- [x] cannot find symbol: class JFrame -- nothing, the window appears -- missing return statement -> The line `import javax.swing.*;` is missing. Without it Java doesn't know `JFrame`. - -runde: - JFrame a = new JFrame("One"); - JFrame b = new JFrame("Two"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: What do you see? -- [x] one window with the title "One" -- two windows -- one window with the title "Two" -> Two windows from the same blueprint, but only `a` is made visible. -::: - -## Task: your own window - -Change the program so that - -- the title says “Hello” and your name, -- the window is 400 pixels wide and 300 pixels high, -- the program ends when you close the window. - -Run it and check all three points. - -::: ludwig -Don't feel like typing so much? With “Real Java” you may type just the four lines from `main` into the Code area. VisuStruct then wraps them in a class itself and adds the `import` lines. In a real Java development environment, though, you need the whole program. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can switch on “Real Java” in the Code area. → Programs with windows -- I can open a window with a title and a size. → The whole program -- I can explain what each line of the window program does. → Line by line -- I can explain why setVisible(true) comes last. → Why setVisible comes last -::: - -## Quiz - -::: quiz -? Which line makes the window visible? -→ Why setVisible comes last -- [ ] window.setSize(300, 200); -- [x] window.setVisible(true); -- [ ] new JFrame("Hello"); -> Without setVisible(true) the window stays invisible. - -? Where is the best place for window.setVisible(true)? -→ Why setVisible comes last -- [ ] At the very start of main -- [x] At the end, once the window is set up -- [ ] It does not matter. -> Things added after setVisible(true) often do not appear. - -? What does setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) do? -→ Line by line -- [ ] The window closes immediately. -- [x] When you close the window, the program ends. -- [ ] The window can no longer be closed. - -? How do you recognise building blocks from Swing? -→ Line by line -- [x] Their name starts with J, e.g. JFrame. -- [ ] They are in curly brackets. -- [ ] They end in Window. -::: diff --git a/web/lessons/en/29-knopf-im-fenster.md b/web/lessons/en/29-knopf-im-fenster.md deleted file mode 100644 index 873942a2..00000000 --- a/web/lessons/en/29-knopf-im-fenster.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -titel: A button in the window -bereich: Windows with Swing -kurz: Put a button into the window and arrange it neatly with a layout. ---- - -## Skills I will gain now - -::: kompetenzen -- build a button and put it into the window with add -- explain why the button goes in before setVisible -- use FlowLayout so that the button has its normal size -- build a window with a button you can press -::: - -## Building a button - -An empty window is boring. Now a button goes in. In Swing a button is called **JButton**. - -You need two new lines for it: - -```java -JButton button = new JButton("Click me"); -window.add(button); -``` - -- `new JButton("Click me")` builds a button. The text in brackets will be written on the button. -- `window.add(button)` puts the button into the window. Without `add` the button exists, but it is not placed anywhere and you cannot see it. - -::: quiz -? What is written on the button after `new JButton("Start")`? -- [ ] button -- [x] Start -- [ ] JButton -> The text in brackets is the label. `button` is only the name of the variable. -::: - -## The whole program - -Both lines go **before** `setVisible(true)`, because the window should be set up completely first and then shown: - -```java -import javax.swing.*; - -public class ButtonWindow { - public static void main(String[] args) { - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton button = new JButton("Click me"); - window.add(button); - - window.setVisible(true); - } -} -``` - -Run the program. Surprise: the button is huge and fills the whole window! - -::: ludwig -The order is the same as with your first window: set up, build the button, put the button in and show everything at the very end. The empty lines in the code help you see these parts. -::: - -## The giant button - -How a window arranges its parts is decided by its **layout**. Without further instructions, a `JFrame` puts everything in the middle and makes it as big as the whole window. - -With a different layout the button gets its normal size. The **FlowLayout** places everything side by side like words in a line, only as big as needed: - -```java -import javax.swing.*; -import java.awt.*; - -public class ButtonWindow { - public static void main(String[] args) { - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new FlowLayout()); - - JButton button = new JButton("Click me"); - window.add(button); - - window.setVisible(true); - } -} -``` - -`FlowLayout` belongs to the older AWT toolbox. That is why there is a second line at the top: `import java.awt.*;`. - -::: angeberwissen -With FlowLayout the building blocks flow like text: if a button no longer fits into the line, it moves to the next one. Make the window narrow and watch the buttons wrap. That is where the name comes from. -::: - -## Clicking works, but nothing happens - -Click the button. It turns a little darker while you press it. So Swing shows that it was pressed. Apart from that, nothing happens. - -That is not a mistake: you have not told the program **what** should happen on a click yet. You will learn that in the next lesson. - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every window program contains **exactly one mistake**, and the bomb is ticking. Read what the program should do and tap the wrong line before time runs out. Six rounds, 25 seconds each. -zeit: 25 - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - JButton button = new JButton(Click me); - window.add(button); - window.setVisible(true); -frage: Should show a button "Click me". -fehler: 3 -besser: JButton button = new JButton("Click me"); -> The caption is a text and needs quotation marks. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - JButton button = new JButton("OK"); - window.add(window); - window.setVisible(true); -frage: Should put the button "OK" into the window. -fehler: 4 -besser: window.add(button); -> The button goes into the window, not the window itself. - -runde: - import java.swing.*; - import java.awt.*; -frage: Should bring Swing and the FlowLayout into the program. -fehler: 1 -besser: import javax.swing.*; -> Swing lives in `javax.swing`, with an x. The FlowLayout comes from `java.awt`, without an x. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout); - window.add(new JButton("OK")); - window.setVisible(true); -frage: Should show the button at its normal size. -fehler: 3 -besser: window.setLayout(new FlowLayout()); -> After `new` and the name there are always round brackets, even if nothing is inside. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout()); - JButton button = new Jbutton("OK"); - window.add(button); - window.setVisible(true); -frage: Should show a button "OK". -fehler: 4 -besser: JButton button = new JButton("OK"); -> Java tells capital and small letters apart: `JButton` with a capital B. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout()); - window.add(new JButton("OK")); - window.setVisible(false); -frage: Should show the window with the button. -fehler: 5 -besser: window.setVisible(true); -> With `false` the window stays invisible. -::: - -## Task: click me - -Build a window with the title “Button test”, 300 by 200 pixels, with a button “Click me” in normal size. It should be possible to press the button, even though nothing happens yet. - -Extra: put a second button “Me too” next to it. In which order are the buttons? Swap the two `add` lines and try again. - -**You are done when …** - -- the window title says “Button test”, -- the window is 300 pixels wide and 200 pixels high, -- the “Click me” button is only as big as its text and does not fill the whole window, -- the button visibly presses down when you click it. - -::: ludwig -Every button needs its own variable, for example `button` and `button2`, and its own `add`. FlowLayout places the buttons in the order of the `add` lines. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a button and put it into the window with add. → Building a button -- I can explain why the button goes in before setVisible(true). → The whole program -- I can use FlowLayout so that the button has its normal size. → The giant button -- I can build a window with a button you can press. → Task: click me -::: - -## Quiz - -::: quiz -? What does window.add(button) do? -→ Building a button -- [ ] It builds a new button. -- [x] It puts the button into the window. -- [ ] It makes the button clickable. -> The button is built with new JButton(…). - -? Why does the button fill the whole window without FlowLayout? -→ The giant button -- [ ] Because the text is too long -- [x] Because without its own layout the window makes everything as big as the window -- [ ] Because setSize is missing - -? Which import line do you need for FlowLayout? -→ The giant button -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> FlowLayout comes from the older AWT. - -? What happens when you click the button in this lesson? -→ Task: click me -- [ ] The program ends. -- [x] The button turns darker for a moment, nothing else happens. -- [ ] The button says “I was clicked”. -> What should happen on a click is what you learn in the next lesson. -::: diff --git a/web/lessons/en/30-der-knopf-reagiert.md b/web/lessons/en/30-der-knopf-reagiert.md deleted file mode 100644 index 77e236b5..00000000 --- a/web/lessons/en/30-der-knopf-reagiert.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -titel: The button reacts -bereich: Windows with Swing -kurz: Use an ActionListener to decide what happens on a click. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an event is -- use addActionListener to decide what happens on a click -- run several statements on a click -- build a button that changes its text after the click -::: - -## A click is an event - -In the last lesson you could press the button, but nothing happened. In a window program it is the person who decides when something happens: they click, type or close a window. Each such action is called an **event**. - -So your program has to wait for the click and notice it. For this you register a listener with the button, an **ActionListener**. On every click the button calls its listener, and the listener runs your code. - -::: ludwig -Until now your program ran from top to bottom, just like the structure chart. The code in the listener only runs later, whenever someone clicks. Nobody knows in advance how often that will be. -::: - -## The line with the arrow - -This is how you register the listener: - -```java -button.addActionListener(e -> button.setText("I was clicked")); -``` - -Read from the outside in: - -- `button.addActionListener( … )` registers a listener with the button. -- `e` is the event. It contains, for example, when the click happened. You do not need it here, but the name has to be there. -- Read `->` as “then do”. -- `button.setText("I was clicked")` is what happens on a click: the button gets a new label. - -The line comes after `new JButton(…)`, because the button has to exist already. And like everything else it comes before `setVisible(true)`. - -::: quiz -? What does the `e` in `e -> button.setText(…)` stand for? -- [x] The event, that is the click -- [ ] “End” -- [ ] An error -> You do not need `e` here, but the name has to be there. -::: - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; - -public class ClickMe { - public static void main(String[] args) { - JFrame window = new JFrame("Click me"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new FlowLayout()); - - JButton button = new JButton("Click me"); - button.addActionListener(e -> button.setText("I was clicked")); - window.add(button); - - window.setVisible(true); - } -} -``` - -Run it and click the button. Now it says “I was clicked”. - -> If the new text is longer than the old one, FlowLayout makes the button wider on its own. - -## Several statements on a click - -If more than one thing should happen on a click, put curly brackets after the arrow. Inside, every statement has its semicolon, just like in a loop body: - -```java -button.addActionListener(e -> { - button.setText("I was clicked"); - window.setTitle("Thank you!"); - System.out.println("Click!"); -}); -``` - -`System.out.println` writes to the [console](#zeige-konsole) as usual. Click several times: every click adds a new line “Click!”. That shows you that the code in the listener really runs on every click. - -::: ludwig -Watch the ending: first `}` closes the block, then `)` closes the bracket of `addActionListener`, then comes the semicolon. So `});`. If any of it is missing, a hint appears below the code. -::: - -## Game: Who is listening? - -::: spiel zuhoerer -titel: Game: Who is listening? -intro: Ludwig shows you code and the window that goes with it. Below it you see which buttons are clicked, one after the other. What happens? After your answer Ludwig plays the clicks: you see which listener hears the event and which lines then run. -frage: What does the label say after the clicks? - -runde: - JFrame window = new JFrame("Greeting"); - JLabel text = new JLabel("Start"); - JButton button = new JButton("Hello"); - button.addActionListener(e -> text.setText("Hello world")); -klicks: Hello -- Start -- [x] Hello world -- Hello -> The button has a listener. On a click it runs `text.setText("Hello world")`, and the label shows “Hello world”. - -runde: - JFrame window = new JFrame("Fruit"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apple")); - b.addActionListener(e -> text.setText("Pear")); -klicks: A, B -- Apple -- [x] Pear -- Apple Pear -> Each button has its own listener. First A writes “Apple”, then B overwrites it with “Pear”. `setText` replaces the old text, it doesn't add anything. - -runde: - JFrame window = new JFrame("Fruit"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apple")); -klicks: B -- Apple -- Pear -- [x] ? -> Nobody signed up at button B: there is no `b.addActionListener`. The click is an event, but nobody is listening. So the question mark stays. - -runde: - JFrame window = new JFrame("Colours"); - JLabel text = new JLabel("none"); - JButton red = new JButton("Red"); - JButton blue = new JButton("Blue"); - red.addActionListener(e -> text.setText("Blue")); - blue.addActionListener(e -> text.setText("Red")); -klicks: Red -- Red -- [x] Blue -- none -> Careful: what happens is decided by the code in the listener, not by the caption of the button. The listener on the button “Red” writes “Blue”. - -runde: - JFrame window = new JFrame("Button"); - JLabel text = new JLabel("Please click"); - JButton button = new JButton("Click me"); - button.addActionListener(e -> { - button.setText("Thanks!"); - text.setText("Clicked"); - }); -klicks: Click me -frage: What does the button say after the click? -- Click me -- [x] Thanks! -- Clicked -> The block after the arrow has two statements. Both run on the click, one after the other: the button gets “Thanks!”, the label “Clicked”. - -runde: - JFrame window = new JFrame("Console"); - JButton button = new JButton("Go"); - button.addActionListener(e -> System.out.println("Click!")); -klicks: Go, Go, Go -frage: How many lines “Click!” are in the console afterwards? -- 1 -- [x] 3 -- 0 -> The listener runs on **every** click, not only on the first one. Three clicks, three lines. -::: - -## ActionListener is an interface - -This is not in the curriculum. If you want to know what is behind the arrow, read on. - -::: angeberwissen -`ActionListener` is an **interface**, a kind of contract. It says: whoever wants to be an ActionListener must have a method `actionPerformed(ActionEvent e)`. On every click the button calls exactly this method. -::: - -You already know the line with the arrow from step 3: - -```java -button.addActionListener(e -> button.setText("I was clicked")); -``` - -Without the arrow you write the listener as a class without a name that fulfils the contract (an anonymous class): - -```java -button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - button.setText("I was clicked"); - } -}); -``` - -For this you also need `import java.awt.event.*;` at the top. Both ways do exactly the same. The arrow `e -> …` is the short form (a lambda expression, since Java 8): `e` is the parameter of `actionPerformed`, and after the arrow comes its body. The short form works because the interface has only one method. So Java knows which method is meant. - -## Game: Contract fulfilled? - -::: spiel vertrag -titel: Game: Contract fulfilled? -intro: The contract `ActionListener` asks for exactly one method: `public void actionPerformed(ActionEvent e)`. Ludwig shows you code. Does it fulfil the contract, so that you can sign it up at a button? Ludwig checked every answer with the real Java compiler. -frage: Does this fulfil the contract ActionListener? -antworten: fulfilled | not fulfilled - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: fulfilled -> Everything is there: `implements ActionListener` signs the contract, and the method is called exactly `actionPerformed`, is `public`, returns nothing (`void`) and gets an `ActionEvent`. - -runde: - class Greeting implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> Look closely: `actionPerfomed` instead of `actionPerformed`, an r is missing. For Java that is a completely different method. The compiler reports that `actionPerformed` is missing. - -runde: - class Greeting implements ActionListener { - public void actionPerformed() { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> The parameter is missing. The contract asks for `actionPerformed(ActionEvent e)`, because on a click the button hands over the event. - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent event) { - System.out.println("Hello"); - } - } -richtig: fulfilled -> You can choose the name of the parameter freely. Only the type `ActionEvent` matters. So instead of `e` it may also be called `event`. - -runde: - class Greeting implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> The `public` is missing. All methods of an interface are public, because the button has to be able to call them from outside. Without `public` the compiler reports an error. - -runde: - class Greeting implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: not fulfilled -> The contract says `void`: the method returns nothing. With `int` as the return type it no longer fits. - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - sayHello(); - } - void sayHello() { - System.out.println("Hello"); - } - } -richtig: fulfilled -> Extra methods are allowed. The contract only asks that `actionPerformed` is there, not that nothing else is there. - -runde: - class Greeting { - public void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } - button.addActionListener(new Greeting()); -richtig: not fulfilled -> The method is right, but the contract isn't signed: `implements ActionListener` is missing. At a button Java only accepts objects that officially fulfil the contract. - -runde: - button.addActionListener(e -> System.out.println("Hello")); -richtig: fulfilled -> The arrow is the short form. Because the contract has only one method, Java knows: `e` is the parameter of `actionPerformed`, and after the arrow comes its body. - -runde: - button.addActionListener((a, b) -> System.out.println("Hello")); -richtig: not fulfilled -> Here the arrow has two parameters. But `actionPerformed` gets exactly one, the event. So this short form doesn't fit the contract. -::: - -## Task: I was clicked - -Now you finish the task: build a window with a button “Click me” in normal size. After the click the button says “I was clicked”. - -Extra: build two buttons “Red” and “Blue”. Clicking a button writes its name into the window title, for example “You chose Red”. - -**You are done when …** - -- the button says “Click me” before the click, -- it says “I was clicked” after the click, -- the button does not fill the whole window. - -::: ludwig -Every button gets its own listener with its own `addActionListener`. You change the title with `window.setTitle(…)`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an event is. → A click is an event -- I can use addActionListener to decide what happens on a click. → The line with the arrow -- I can run several statements on a click. → Several statements on a click -- I can build a button that changes its text after the click. → Task: I was clicked -::: - -## Quiz - -::: quiz -? What is an ActionListener? -→ A click is an event -- [ ] A button with text -- [x] A listener that runs your code on every click -- [ ] A layout for windows -> You register it with the button using addActionListener. - -? When does button.setText("I was clicked") in the listener run? -→ The line with the arrow -- [ ] Right when the program starts -- [x] Every time someone clicks the button -- [ ] Only on the first click -> The code in the listener only runs when the event happens. - -? How does a listener with curly brackets end? -→ The line with the arrow -- [ ] };) -- [x] }); -- [ ] );} -> First the block closes, then the bracket of addActionListener, then the semicolon. - -? Match each part to its job. -→ The line with the arrow -= JButton -> a button -= addActionListener -> register a listener -= setText -> change the label -= setVisible(true) -> show the window -::: diff --git a/web/lessons/en/31-zahl-verdoppeln.md b/web/lessons/en/31-zahl-verdoppeln.md deleted file mode 100644 index 4ab802c8..00000000 --- a/web/lessons/en/31-zahl-verdoppeln.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Doubling a number in a window -bereich: Windows with Swing -kurz: Arrange a label, a text field and a button with a GridLayout and calculate with the text in the text field. ---- - -## Skills I will gain now - -::: kompetenzen -- put a JLabel and a JTextField into the window -- explain why only the last building block shows without a layout -- arrange everything below each other with GridLayout -- calculate with getText, Integer.parseInt and setText -- write a program that doesn't crash with “abc” -::: - -## The goal - -We build a small calculator with a window: at the top there is a label, below it a text field and a button **Calculate**. If you type 2 and click, the text field shows 4. Click again and it becomes 8, then 16 and so on. - -For this you need two new building blocks: - -- **JLabel**: a label, simply text in the window. -- **JTextField**: a text field you can type into. - -As in the last lessons, tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Three building blocks in the window - -We put all three building blocks into the window, just like the button in lesson 29: - -```java -import javax.swing.*; - -public class Doubling { - public static void main(String[] args) { - JFrame window = new JFrame("Double the number"); - window.setSize(250, 150); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel label = new JLabel("Enter a number:"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate"); - window.add(label); - window.add(field); - window.add(button); - - window.setVisible(true); - } -} -``` - -Run the program. Oops: all you can see is the giant button. Where are the label and the text field? - -## Why only the button shows - -::: ludwig -Remember the giant button? Without a layout of its own, a `JFrame` has only **one** place: the middle. Every `add` puts the new building block exactly there, as big as the whole window. So the three building blocks land on top of each other in the same place, and the window only shows the last one you put in: the button. You can't use the program like this. -::: - -The solution is a layout again. This time we use the **GridLayout**. It divides the window into cells of equal size, like a grid: - -```java -window.setLayout(new GridLayout(0, 1)); -``` - -The two numbers are rows and columns. `0` rows means “as many as needed”, `1` column means: everything below each other. For `GridLayout` you also need `import java.awt.*;` at the top. - -| Layout | How it arranges things | -|---|---| -| no layout | everything in the middle, on top of each other | -| `FlowLayout` | side by side like words, with a line break when needed | -| `GridLayout(0, 1)` | below each other, every row the same height | - -## The window with GridLayout - -```java -import javax.swing.*; -import java.awt.*; - -public class Doubling { - public static void main(String[] args) { - JFrame window = new JFrame("Double the number"); - window.setSize(250, 150); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel label = new JLabel("Enter a number:"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate"); - window.add(label); - window.add(field); - window.add(button); - - window.setVisible(true); - } -} -``` - -Now all three are below each other. You can type into the text field and press the button. But the program doesn't calculate anything yet. - -::: angeberwissen -With `new GridLayout(2, 3)` you get 2 rows with 3 columns each, so 6 cells of the same size. That is how you build the keys of a calculator, for example. The building blocks fill the cells in the order of the `add` lines, row by row from left to right. -::: - -## Calculating with the text in the text field - -On a click, the program should take the number from the text field, double it and write it back. Three methods do this: - -- `field.getText()` gives you the text in the text field, for example `"2"`. -- `Integer.parseInt(…)` turns it into the number 2. You know this from lesson 22. -- `field.setText(…)` writes a new text into the text field. - -Together with the ActionListener from lesson 30 it looks like this: - -```java -button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); -}); -``` - -`setText` needs a text. `"" + number * 2` appends the result to an empty text and so turns the number into a text. - -## Not a number? No crash! - -Type “abc” into the text field and click Calculate. The window stays, but nothing happens, and the [console](#zeige-konsole) shows a `NumberFormatException`. From lesson 22 you know what to do: - -```java -button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } -}); -``` - -Instead of an error in the console, you now see a hint in the window. - -::: ludwig -The arrow already has an `e` for the event. That is why the exception in `catch` needs a different name, here `error`. Java doesn't allow two variables with the same name. -::: - -::: quiz -? Why is the exception in `catch` called `error` here and not `e`? -- [ ] `catch` always needs the name `error`. -- [x] `e` is already the event of the arrow, and Java does not allow two variables with the same name. -- [ ] `e` is only allowed for numbers. -> You can choose the name freely, just not twice. -::: - -## Game: What is in the window afterwards? - -::: spiel frage -titel: Game: What is in the window afterwards? -intro: Ludwig types something into the text field and clicks Calculate. Read the listener and predict what the window shows afterwards. Six rounds. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - }); -frage: The text field says 21. What does it say after the click? -- 2121 -- [x] 42 -- 21 -> `parseInt` turns the text into the number 21. Double that is 42. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - }); -frage: The text field says 21. You click twice. What does it say afterwards? -- 42 -- [x] 84 -- 4242 -> Every click doubles whatever is in the field right now: 21, 42, 84. - -runde: - button.addActionListener(e -> { - field.setText(field.getText() + field.getText()); - }); -frage: The text field says 21. What does it say after the click? -- 42 -- [x] 2121 -- 21 -> Without `parseInt` it stays a text, and `+` joins texts together. - -runde: - button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } - }); -frage: The text field says abc. What happens on the click? -- [x] The label shows "Please enter a whole number!" -- The text field says abcabc. -- The window crashes. -> `parseInt` throws a NumberFormatException, and `catch` catches it. - -runde: - button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } - }); -frage: The text field says 2.5. What happens on the click? -- The text field says 5.0. -- The text field says 5. -- [x] The label shows "Please enter a whole number!" -> `Integer.parseInt` only knows whole numbers. For it, 2.5 is not a number. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number + number); - }); -frage: The text field says 5. What does it say after the click? -- 10 -- [x] 55 -- 5 -> `"" + number` is already a text, and the second `+` adds the 5 once more. With `"" + (number + number)` Java calculates first. -::: - -## Task: doubling a number - -Put the whole program together: - -- a window with the title “Double the number”, 250 pixels wide and 150 pixels high, -- below each other a label, a text field and a button “Calculate”, -- every click doubles the number in the text field: 2, 4, 8, 16, … -- with “abc” a hint appears in the label instead of a crash. - -Extra: a second button “Halve” divides the number by 2. What happens with 5? - -**You are done when …** - -- the title is “Double the number” and label, text field and button are stacked, -- 2 becomes 4, 8 and 16 after three clicks, -- with “abc” a hint appears in the label and the program keeps running. - -::: ludwig -The “Halve” button goes into the GridLayout as the fourth row and gets its own ActionListener. With `int`, Java cuts off the decimal places when dividing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can put a JLabel and a JTextField into the window. → Three building blocks in the window -- I can explain why only the last building block shows without a layout. → Why only the button shows -- I can arrange everything below each other with GridLayout(0, 1). → The window with GridLayout -- I can calculate with getText, Integer.parseInt and setText. → Calculating with the text in the text field -- I can write a program that doesn't crash with “abc”. → Not a number? No crash! -::: - -## Quiz - -::: quiz -? Why does only the button show without a layout? -→ Why only the button shows -- [ ] Because the label and the text field are missing -- [x] Because all building blocks lie on top of each other in the middle and the last one hides the others -- [ ] Because the window is too small - -? What does new GridLayout(0, 1) mean? -→ The window with GridLayout -- [ ] No row and one column, so the window is empty -- [x] As many rows as needed in one column, so everything below each other -- [ ] One row, everything side by side - -? What does field.getText() give you? -→ Calculating with the text in the text field -- [ ] Always a number -- [x] The text in the text field -- [ ] The title of the window -> For a number you still need Integer.parseInt. - -? Match each building block to its job. -→ Three building blocks in the window -= JLabel -> label -= JTextField -> text field for typing -= JButton -> button -= GridLayout -> arrangement in a grid -::: diff --git a/web/lessons/en/32-klickzaehler.md b/web/lessons/en/32-klickzaehler.md deleted file mode 100644 index 7b04576e..00000000 --- a/web/lessons/en/32-klickzaehler.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Click counter with reset -bereich: Windows with Swing -kurz: Two buttons change the same number: create a variable for the whole class. ---- - -## Skills I will gain now - -::: kompetenzen -- explain why a listener may not change a variable from main -- create a static variable in the class -- let two buttons change the same number -- build a counter with −1 that never goes below 0 -::: - -## The goal - -A window counts clicks: the number is at the top, two buttons below it. **+1** counts up by one, **Reset** sets the number back to 0. - -What's new this time: two buttons work with the **same** number. For that you need a variable that both listeners know. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## The first attempt - -The obvious idea is to create the number in `main` as usual and count it up in the listener: - -```java -int count = 0; -button.addActionListener(e -> { - count++; - number.setText("" + count); -}); -``` - -If you run this with real Java, the compiler reports an error: - -``` -local variables referenced from a lambda expression must be final or effectively final -``` - -::: ludwig -The listener only runs later, on every click, long after `main` has finished. That is why it may read the variables from `main`, but not change them. Java insists: a variable from `main` that appears in the arrow must stay the same. For a number that changes on every click, we need a different place. -::: - -## A variable for the whole class - -The solution: the variable does **not** go into `main`, but directly into the class, above `main`. Then it belongs to the whole class, and every listener may change it: - -```java -public class Counter { - static int count = 0; - - public static void main(String[] args) { - // the window as usual - } -} -``` - -- `static` means: the variable belongs to the class itself. Because `main` is `static` too, it is needed here. -- A variable like this is called a **field** of the class. It lives as long as the program, not just as long as `main`. - -::: quiz -? What do you call a variable that sits directly in the class and not in `main`? -- [ ] A parameter -- [x] A field -- [ ] A listener -> A field lives as long as the program, not just as long as `main`. -::: - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; - -public class Counter { - static int count = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel number = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); - reset.addActionListener(e -> { - count = 0; - number.setText("0"); - }); - - window.add(number); - window.add(plus); - window.add(reset); - window.setVisible(true); - } -} -``` - -`SwingConstants.CENTER` puts the text of the label in the middle. Click +1 a few times and then Reset. - -::: angeberwissen -With `number.setFont(new Font("SansSerif", Font.BOLD, 48));` the number becomes big and bold. `Font` comes from `java.awt`, which you have already imported. -::: - -## Game: Who is counting? - -::: spiel zuhoerer -titel: Game: Who is counting? -intro: Now the listeners calculate with the variable of the class. Ludwig shows you code, window and the clicks. Which number is shown at the end? Afterwards Ludwig plays the clicks. -frage: What does the label say after the clicks? - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> The variable is in the class and keeps its value between the clicks. Every click counts up by one: 1, 2, 3. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); - reset.addActionListener(e -> { - count = 0; - number.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> After two clicks it shows 2. Reset sets the variable to 0, and the last click counts up to 1 again. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> count++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> Afterwards the variable is 2, but nobody writes it into the label. Without `number.setText(…)` the window keeps showing 0. Calculating and showing are two different things. - -runde: - static int count = 0; - JLabel number = new JLabel("Clicks: 0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("Clicks: " + count + 1); - }); -klicks: +1 -- Clicks: 2 -- Clicks: 1 -- [x] Clicks: 11 -> The bracket trap: Java works from left to right. `"Clicks: " + count` gives the text “Clicks: 1”, and `+ 1` adds another 1 to the end. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton one = new JButton("+1"); - JButton ten = new JButton("+10"); - one.addActionListener(e -> { - count++; - number.setText("" + count); - }); - ten.addActionListener(e -> { - count += 10; - number.setText("" + count); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Both listeners change the same variable: 10, then 11, then 21. `count += 10` is the short form of `count = count + 10`. -::: - -## Task: click counter - -Rebuild the click counter and extend it: - -- a button **−1** that counts down by one, -- the number should never go below 0. If it is 0, −1 does nothing. - -**You are done when …** - -- **+1** counts up by one and **−1** counts down by one, -- at 0 a click on **−1** changes nothing, -- **Reset** sets the number back to 0. - -::: ludwig -For “never below 0” the listener of −1 needs an `if`: only if `count > 0` does it count down. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain why a listener may not change a variable from main. → The first attempt -- I can create a static variable in the class. → A variable for the whole class -- I can let two buttons change the same number. → The whole program -- I can build a counter with −1 that never goes below 0. → Task: click counter -::: - -## Quiz - -::: quiz -? Why may the listener not change the variable count from main? -→ The first attempt -- [ ] Because numbers are not allowed in Swing -- [x] Because the listener runs later and Java doesn't let the arrow change variables from main -- [ ] Because count gets too big - -? Where does static int count = 0; go? -→ A variable for the whole class -- [ ] In main, at the very top -- [x] In the class, outside main -- [ ] In the listener - -? What does SwingConstants.CENTER do in new JLabel("0", SwingConstants.CENTER)? -→ The whole program -- [x] It puts the text in the middle of the label. -- [ ] It puts the window in the middle of the screen. -- [ ] It makes the font bigger. -::: diff --git a/web/lessons/en/33-ampel.md b/web/lessons/en/33-ampel.md deleted file mode 100644 index 39ac7b4a..00000000 --- a/web/lessons/en/33-ampel.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -titel: A traffic light with colours -bereich: Windows with Swing -kurz: Set background colours and switch a traffic light on with a state variable. ---- - -## Skills I will gain now - -::: kompetenzen -- colour a label with setOpaque and setBackground -- remember a state with a static variable -- use BorderLayout to put something big in the middle and something narrow at the bottom -- explain what FlowLayout, GridLayout and BorderLayout are good for -- program a traffic light with four phases -::: - -## The goal - -We build a traffic light: a large area lights up red. Each click on **Next** switches it on: red, green, yellow and red again. - -New this time are **colours** and a **state**: the program has to remember which colour is currently showing. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Colours in Swing - -Colours live in the class `Color` from `java.awt`. Ready-made colours are, for example, `Color.RED`, `Color.GREEN`, `Color.YELLOW`, `Color.ORANGE` and `Color.WHITE`. - -This is how you colour a label: - -```java -JLabel light = new JLabel("", SwingConstants.CENTER); -light.setOpaque(true); -light.setBackground(Color.RED); -``` - -- `setBackground(…)` sets the background colour. -- A `JLabel` also needs `setOpaque(true)`. Without this line the background is transparent and the colour stays invisible. - -::: ludwig -`opaque` means “not see-through”. A label is normally transparent, like writing on a pane of glass. Only with `setOpaque(true)` does it get its own area that can be painted. -::: - -::: quiz -? Which line paints the background of the label `light` green? -- [ ] `light.setText(Color.GREEN);` -- [x] `light.setBackground(Color.GREEN);` -- [ ] `light.setColor("green");` -> `setBackground` sets the background colour. To see it, a `JLabel` first needs `setOpaque(true)`. -::: - -## Remembering the colour - -So that the traffic light knows what comes next, it remembers its state in a number: 0 means red, 1 means green, 2 means yellow. As with the click counter from lesson 32, the variable is in the class so that the listener may change it: - -```java -static int phase = 0; -``` - -On every click the phase moves on by one. After yellow (2) comes red (0) again: - -```java -next.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - light.setBackground(Color.RED); - } else if (phase == 1) { - light.setBackground(Color.GREEN); - } else { - light.setBackground(Color.YELLOW); - } -}); -``` - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; - -public class TrafficLight { - static int phase = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Traffic light"); - window.setSize(200, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new BorderLayout()); - - JLabel light = new JLabel("", SwingConstants.CENTER); - light.setOpaque(true); - light.setBackground(Color.RED); - JButton next = new JButton("Next"); - - next.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - light.setBackground(Color.RED); - } else if (phase == 1) { - light.setBackground(Color.GREEN); - } else { - light.setBackground(Color.YELLOW); - } - }); - - window.add(light, BorderLayout.CENTER); - window.add(next, BorderLayout.SOUTH); - window.setVisible(true); - } -} -``` - -This uses a new layout, the **BorderLayout**. It has five places: `NORTH` (top), `SOUTH` (bottom), `WEST` (left), `EAST` (right) and `CENTER` (middle). The middle gets all the space that is left. So the light becomes big, and the button stays narrow at the bottom. - -::: angeberwissen -BorderLayout is the layout a `JFrame` uses when you don't give it one. An `add` without a place lands in `CENTER`. That is why all the building blocks lay on top of each other in lesson 31. -::: - -::: angeberwissen -Professionals switch on in one line: `phase = (phase + 1) % 3;`. The remainder operator `%` gives the remainder of a division. 3 % 3 is 0, so after 2 it starts at 0 again. -::: - -## Which layout for what? - -You now know three layouts. Each one arranges the building blocks differently: - -| Layout | How it arranges | Good for | -|---|---|---| -| `FlowLayout` (lesson 29) | next to each other like words in a line, each block as big as needed | a few buttons in a row | -| `GridLayout` (lesson 31) | a grid of cells of the same size, filled row by row | forms, keypads like on a calculator | -| `BorderLayout` (this lesson) | five places: top, bottom, left, right and the middle, which gets the rest | something big in the middle, such as a picture or a display, with buttons at the edge | - -Without `setLayout` a window uses the BorderLayout. That is why everything then lands in the middle. - -::: ludwig -Ask yourself when planning: should all parts be the same size? Then GridLayout. Should one part be big and the rest narrow at the edge? Then BorderLayout. Should the parts simply keep their normal size? Then FlowLayout. -::: - -## Game: Where does the button go? - -::: spiel layout -titel: Game: Where does the button go? -intro: You now know three layouts and the window with no layout at all. Ludwig shows you code with the buttons A, B, C … What does the window look like? Tap the right picture. -frage: What does the window look like? - -runde: - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Without a layout of its own, every `add` lands in the centre, as big as the window. The three lie on top of each other, and you only see the last one: C. - -runde: - window.setLayout(new FlowLayout()); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> FlowLayout puts the buttons next to each other at their normal size, like words in a line, and centres them. - -runde: - window.setLayout(new GridLayout(0, 1)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> One column, as many rows as needed: everything below each other, every row the same height and as wide as the window. - -runde: - window.setLayout(new GridLayout(0, 2)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); - window.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Two columns. The cells are filled row by row, always left first, then right: A B, then C D. - -runde: - window.setLayout(new GridLayout(2, 3)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); - window.add(new JButton("D")); - window.add(new JButton("E")); - window.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Rows come first, then columns: 2 rows with 3 columns each. - -runde: - window.setLayout(new GridLayout(0, 2)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Two columns, three buttons: the first row is full, in the second C is on the left, and the cell on the right stays empty. - -runde: - window.setLayout(new BorderLayout()); - window.add(new JButton("A"), BorderLayout.NORTH); - window.add(new JButton("B"), BorderLayout.CENTER); - window.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH is at the top, SOUTH at the bottom. The centre gets all the remaining space, which is why B is big. - -runde: - window.setLayout(new BorderLayout()); - window.add(new JButton("A"), BorderLayout.WEST); - window.add(new JButton("B")); - window.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST is on the left, EAST on the right. B has no place given, so it lands in CENTER. -::: - -## Task: traffic light - -Rebuild the traffic light and make it more realistic. In the UK and Germany, red and amber light up together briefly after red. So add a fourth phase: - -- 0: red -- 1: red and amber (use `Color.ORANGE`) -- 2: green -- 3: amber (yellow) - -Extra: in each phase, also write with `light.setText(…)` what the light means, for example “Stop” or “Go”. - -**You are done when …** - -- every click shows the next phase: red, red-yellow (orange), green, yellow, -- red comes again after yellow, -- for the extra, every phase shows its matching text. - -::: ludwig -With four phases, 0 comes after 3. So change the number in `if (phase > 2)`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can colour a label with setOpaque and setBackground. → Colours in Swing -- I can remember a state with a static variable. → Remembering the colour -- I can use BorderLayout to put something big in the middle and something narrow at the bottom. → The whole program -- I can explain what FlowLayout, GridLayout and BorderLayout are good for. → Which layout for what? -- I can program a traffic light with four phases. → Task: traffic light -::: - -## Quiz - -::: quiz -? Why does the colour stay invisible without setOpaque(true)? -→ Colours in Swing -- [ ] Because Color.RED doesn't exist -- [x] Because a label is transparent otherwise -- [ ] Because the window is too small - -? Which place in the BorderLayout gets the space that is left? -→ The whole program -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? phase is 2 and the listener runs phase++ and then if (phase > 2) { phase = 0; }. What is phase afterwards? -→ Remembering the colour -- [x] 0 -- [ ] 2 -- [ ] 3 -> After phase++ it is 3, which is greater than 2, so it becomes 0. -::: diff --git a/web/lessons/en/34-notenrechner.md b/web/lessons/en/34-notenrechner.md deleted file mode 100644 index 5e015fd1..00000000 --- a/web/lessons/en/34-notenrechner.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -titel: Grade calculator in a window -bereich: Windows with Swing -kurz: Enter points, work out the grade with else if and catch wrong input. ---- - -## Skills I will gain now - -::: kompetenzen -- turn points into a grade with else if -- show the grade in the window -- catch and report “abc” and points outside 0 to 100 -- test a program at the limits, for example with 90 and 89 -::: - -## The goal - -A window turns points into a grade: you type the points of a test (0 to 100), click **Calculate grade**, and the grade appears at the bottom. - -This time there is no new Swing building block. What's new is that you combine everything so far: text field, button, `else if` from lesson 20 and try/catch from lesson 22. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## The grading scale - -We use this grading scale: - -| Points | Grade | -|---|---| -| 90 to 100 | A | -| 80 to 89 | B | -| 70 to 79 | C | -| 60 to 69 | D | -| 0 to 59 | F | - -With `else if` you check from top to bottom. The first condition that is true wins: - -```java -String grade; -if (points >= 90) { - grade = "A"; -} else if (points >= 80) { - grade = "B"; -} else if (points >= 70) { - grade = "C"; -} else if (points >= 60) { - grade = "D"; -} else { - grade = "F"; -} -``` - -::: ludwig -With 85 points, `points >= 90` is false, but `points >= 80` is true. So grade B, and the remaining conditions are not checked at all. That is why the lower limit is enough each time. -::: - -You can try this part without a window in the [Code](#zeige-codelive) area. Put `int points = 85;` in front of it and `System.out.println("Grade: " + grade);` after it. The structure chart shows you the nested branches. - -::: quiz -? Which grade does the key give for 29 points? -- [ ] 4 -- [ ] 5 -- [x] 6 -> All conditions are false, even `points >= 30`. Then the `else` branch applies. -::: - -## The window - -```java -import javax.swing.*; -import java.awt.*; - -public class GradeCalculator { - public static void main(String[] args) { - JFrame window = new JFrame("Grade calculator"); - window.setSize(260, 180); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel question = new JLabel("Points (0 to 100):"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate grade"); - JLabel result = new JLabel("Grade: ?"); - - button.addActionListener(e -> { - int points = Integer.parseInt(field.getText()); - String grade; - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else if (points >= 60) { - grade = "D"; - } else { - grade = "F"; - } - result.setText("Grade: " + grade); - }); - - window.add(question); - window.add(field); - window.add(button); - window.add(result); - window.setVisible(true); - } -} -``` - -Try 100, 85, 60 and 12. - -## Catching wrong input - -Two things can still go wrong: - -1. Someone types “abc”. Then `Integer.parseInt` throws a `NumberFormatException`. -2. Someone types 150 or −5. That is a number, but not a valid score. - -You catch the first case with try/catch, the second with an `if` before working out the grade: - -```java -button.addActionListener(e -> { - try { - int points = Integer.parseInt(field.getText()); - if (points < 0 || points > 100) { - result.setText("Please enter 0 to 100."); - } else { - // the grade calculation from above goes here - } - } catch (NumberFormatException error) { - result.setText("That is not a number."); - } -}); -``` - -::: ludwig -`||` means “or” (lesson 9). The condition is true if the points are too small **or** too big. -::: - -## Game: Which grade? - -::: spiel frage -titel: Game: Which grade? -intro: Ludwig has built grade calculators, but not all of them are right. Work out which grade comes out. Six rounds. - -runde: - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else { - grade = "D"; - } -frage: points is 80. Which grade comes out? -- A -- [x] B -- C -> 80 >= 90 is false, 80 >= 80 is true. The limit still belongs to B. - -runde: - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else if (points >= 60) { - grade = "D"; - } else { - grade = "F"; - } -frage: points is 69. Which grade comes out? -- C -- [x] D -- F -> 69 >= 70 is only just false, 69 >= 60 is true. So grade D. - -runde: - if (points >= 60) { - grade = "D"; - } else if (points >= 90) { - grade = "A"; - } else { - grade = "F"; - } -frage: points is 95. Which grade comes out? -- A -- [x] D -- F -> The first true condition wins. 95 >= 60 is already true, so A is never reached. That's why you check from the high points downwards. - -runde: - grade = "F"; - if (points >= 60) { - grade = "D"; - } - if (points >= 70) { - grade = "C"; - } -frage: points is 85. What is in grade afterwards? -- F -- D -- [x] C -> Without `else` every `if` is checked. Both are true, and the last one sets grade C. - -runde: - // grade has already been worked out - if (points < 0 || points > 100) { - result.setText("Please enter 0 to 100."); - } else { - result.setText("Grade: " + grade); - } -frage: The text field says 100. What does the window show? -- [x] Grade: A -- Please enter 0 to 100. -- That is not a number. -> 100 > 100 is false. So 100 points are valid and give grade A. - -runde: - // grade has already been worked out - if (points < 0 && points > 100) { - result.setText("Please enter 0 to 100."); - } else { - result.setText("Grade: " + grade); - } -frage: The text field says 150. What does the window show? -- Please enter 0 to 100. -- [x] a grade, although 150 is not valid -- That is not a number. -> With `&&` the points would have to be below 0 and above 100 at the same time. That never happens, so the program never warns. The right one is `||`. -::: - -## Task: grade calculator - -Put the grade calculator together, with both checks from the last step. Test it with 90, 89, 0, 100, 101 and “ten”. - -Extra: also write a word for the grade, for example “Grade: B (good)”. The words are excellent, good, satisfactory, poor and fail. - -**You are done when …** - -- 92 and 100 give a 1, 91 a 2 and 0 a 6, -- 101 shows the hint to enter 0 to 100, -- “ten” shows a hint and the program keeps running. - -::: ludwig -Always test the limits: 90 must be an A, 89 a B. Most mistakes hide at the limits, for example `>` instead of `>=`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can turn points into a grade with else if. → The grading scale -- I can show the grade in the window. → The window -- I can catch and report “abc” and points outside 0 to 100. → Catching wrong input -- I can test a program at the limits, for example with 90 and 89. → Task: grade calculator -::: - -## Quiz - -::: quiz -? Which grade does the scale give for 70 points? -→ The grading scale -- [ ] B -- [x] C -- [ ] D -> 70 is the lower limit for a C. - -? Why is the lower limit enough in each condition? -→ The grading scale -- [x] Because else if has already sorted out the higher cases -- [ ] Because Java doesn't know upper limits -- [ ] Because the points are always 100 - -? Which condition detects invalid points? -→ Catching wrong input -- [ ] points < 0 && points > 100 -- [x] points < 0 || points > 100 -- [ ] points >= 0 || points <= 100 -> A number can't be smaller than 0 and bigger than 100 at the same time, so you need or. -::: diff --git a/web/lessons/en/35-taschenrechner.md b/web/lessons/en/35-taschenrechner.md deleted file mode 100644 index 221721dd..00000000 --- a/web/lessons/en/35-taschenrechner.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -titel: A calculator -bereich: Windows with Swing -kurz: Two columns with GridLayout, four arithmetic buttons and catching division by 0. ---- - -## Skills I will gain now - -::: kompetenzen -- build two columns with GridLayout -- explain why "" + (a + b) needs brackets -- program all four arithmetic buttons -- build a calculator that doesn't crash with 0 or “abc” -::: - -## The goal - -We build a small calculator: two text fields for the numbers, four buttons **+**, **-**, **\*** and **/** and a line for the result. - -New this time are **two columns** in the GridLayout and **several buttons** that all calculate with the same text fields. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Two columns - -So far `GridLayout(0, 1)` put everything below each other. `GridLayout(0, 2)` gives you two columns. The building blocks fill the cells in the order of the `add` lines, always left first, then right: - -| left | right | -|---|---| -| First number: | text field | -| Second number: | text field | -| + | - | -| \* | / | -| Result: | ? | - -You won't need the labels on the left later. So you can build them directly inside `add`, without a variable: - -```java -window.add(new JLabel("First number:")); -window.add(field1); -``` - -## The window with the first button - -```java -import javax.swing.*; -import java.awt.*; - -public class Calculator { - public static void main(String[] args) { - JFrame window = new JFrame("Calculator"); - window.setSize(280, 220); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 2)); - - JTextField field1 = new JTextField(); - JTextField field2 = new JTextField(); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - JButton times = new JButton("*"); - JButton divide = new JButton("/"); - JLabel result = new JLabel("?"); - - plus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a + b)); - }); - - window.add(new JLabel("First number:")); - window.add(field1); - window.add(new JLabel("Second number:")); - window.add(field2); - window.add(plus); - window.add(minus); - window.add(times); - window.add(divide); - window.add(new JLabel("Result:")); - window.add(result); - window.setVisible(true); - } -} -``` - -Type 3 and 4 and click +. The result shows 7. - -::: ludwig -Watch the brackets in `"" + (a + b)`. Without them Java works from left to right: `"" + a` gives the text “3”, and `"3" + b` appends the 4. You get “34” instead of 7. The bracket makes sure the calculation happens first. -::: - -## The other three buttons - -The buttons -, \* and / work the same way. Only the operator in the last line changes: - -```java -minus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a - b)); -}); -``` - -/ has two special features: - -- With `int`, Java cuts off the decimal places when dividing. 7 / 2 gives 3. -- If the second number is 0, Java throws an `ArithmeticException`, as in the task “Dividing safely” in lesson 22. - -## Catching division by 0 - -```java -divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } -}); -``` - -::: angeberwissen -With `double` and `Double.parseDouble(…)` you get decimal places: 7 / 2 gives 3.5. In return, Java throws no exception when dividing decimal numbers by 0. The result is then `Infinity`. -::: - -::: quiz -? What does the calculator show for 8 divided by 0? -- [ ] 0 -- [ ] Infinity -- [x] Not by 0! -> With `int`, `a / b` throws an `ArithmeticException`. The `catch` catches it and shows the message. -::: - -## A method of your own - -This is not in the curriculum, but it makes the code shorter. - -::: angeberwissen -Every listener reads the same two fields. You can move this repetition into a method of your own, a subprogram like in lesson 18. It goes in the class next to `main`: - -`static int number(JTextField field) { return Integer.parseInt(field.getText()); }` - -The listener then just says `result.setText("" + (number(field1) + number(field2)));`. -::: - -## Game: What does the calculator show? - -::: spiel frage -titel: Game: What does the calculator show? -intro: Ludwig has been tinkering with the calculator. Read the listener and predict what the result shows. Six rounds. - -runde: - plus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + a + b); - }); -frage: You type 3 and 4 and click +. What does the result show? -- 7 -- [x] 34 -- 12 -> Without brackets Java works from left to right: `"" + 3` is the text "3", and `+ 4` adds the 4 to the end. - -runde: - times.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + a * b); - }); -frage: You type 3 and 4 and click *. What does the result show? -- 34 -- [x] 12 -- 7 -> Multiplication comes before addition here too: Java first works out 3 * 4, then adds the 12 to the text. Plus doesn't get this priority, which is why it needs the brackets. - -runde: - divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a / b)); - }); -frage: You type 7 and 2 and click /. What does the result show? -- 3.5 -- [x] 3 -- 4 -> With `int` Java cuts off the decimal places. It doesn't round. - -runde: - divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } - }); -frage: You type 8 and 0 and click /. What does the result show? -- 0 -- [x] Not by 0! -- Infinity -> With `int`, dividing by 0 throws an ArithmeticException, and `catch` writes the hint. - -runde: - divide.addActionListener(e -> { - double a = Double.parseDouble(field1.getText()); - double b = Double.parseDouble(field2.getText()); - result.setText("" + (a / b)); - }); -frage: You type 7 and 2 and click /. What does the result show? -- 3 -- [x] 3.5 -- 3.0 -> With `double` the decimal places are kept. - -runde: - divide.addActionListener(e -> { - double a = Double.parseDouble(field1.getText()); - double b = Double.parseDouble(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } - }); -frage: You type 8 and 0 and click /. What does the result show? -- Not by 0! -- [x] Infinity -- 0.0 -> With `double` there is no exception. The result is `Infinity`, and the `catch` never gets its turn. -::: - -## Task: calculator - -Finish the calculator: - -- all four buttons calculate, -- / reports “Not by 0!” instead of crashing, -- if a field contains “abc” or nothing, “Please enter two numbers” appears. - -Extra: a fifth button **C** empties both fields and sets the result back to “?”. - -**You are done when …** - -- 7 and 2 give 9, 5, 14 and 3 for +, −, * and /, -- 7 / 0 shows the “not by 0” message, -- “abc” or an empty field shows the “enter two numbers” message and nothing crashes. - -::: ludwig -For “abc” every listener needs a try/catch with `NumberFormatException`. A `try` may also be followed by two `catch` blocks, one for `NumberFormatException` and one for `ArithmeticException`. By the way, an empty field gives the text `""`, and that isn't a number either. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build two columns with GridLayout(0, 2). → Two columns -- I can explain why "" + (a + b) needs brackets. → The window with the first button -- I can program all four arithmetic buttons. → The other three buttons -- I can build a calculator that doesn't crash with 0 or “abc”. → Task: calculator -::: - -## Quiz - -::: quiz -? a is 3 and b is 4. What does "" + a + b give? -→ The window with the first button -- [ ] 7 -- [x] 34 -- [ ] An error -> Java works from left to right: first the text "3" is created, then the 4 is appended. - -? What does 7 / 2 give with int? -→ The other three buttons -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? In which order does GridLayout(0, 2) fill the cells? -→ Two columns -- [x] Row by row, left first, then right -- [ ] Column by column, starting top to bottom on the far left -- [ ] Randomly -::: diff --git a/web/lessons/en/36-zahlenraten.md b/web/lessons/en/36-zahlenraten.md deleted file mode 100644 index 9e3a5942..00000000 --- a/web/lessons/en/36-zahlenraten.md +++ /dev/null @@ -1,242 +0,0 @@ ---- -titel: Guess the number -bereich: Windows with Swing -kurz: A game with a random number, a counter for the guesses and a button that switches itself off at the end. ---- - -## Skills I will gain now - -::: kompetenzen -- create a random whole number from 1 to 100 with Math.random -- decide which variables belong in the class -- switch a button off with setEnabled -- build a guessing game with a “New game” button -::: - -## The goal - -Time for a game: the computer thinks of a number from 1 to 100. You guess, and the window tells you “too small” or “too big”. Once you have found it, it tells you how many guesses you needed. - -New this time are **random numbers** and a button that **switches itself off** at the end. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## A random number - -`Math.random()` gives a random decimal number from 0 to just under 1, for example 0.4173. Three steps turn it into a whole number from 1 to 100: - -```java -int secret = (int) (Math.random() * 100) + 1; -``` - -| Step | Example | -|---|---| -| `Math.random()` | 0.4173 | -| `* 100` | 41.73 | -| `(int)` cuts off the decimal places | 41 | -| `+ 1` | 42 | - -Without `+ 1` you would get 0 to 99. - -Try it in the [Code](#zeige-codelive) area: `int secret = (int) (Math.random() * 100) + 1;` and `System.out.println(secret);`. Every run gives a different number. - -::: angeberwissen -Computers can't actually roll dice. `Math.random()` uses a formula to calculate a long sequence of numbers that only **look** random. They are called pseudo-random numbers. For games that is perfectly fine. -::: - -## What the game remembers - -The game has to remember two things across all clicks: the secret number and the number of guesses. As with the click counter from lesson 32, both go in the class: - -```java -static int secret = (int) (Math.random() * 100) + 1; -static int guesses = 0; -``` - -::: quiz -? Why are `secret` and `guesses` in the class and not in `main`? -- [ ] Because `Math.random()` only works in the class -- [x] Because they must last over all clicks and the listener has to change `guesses` -- [ ] Because `main` cannot store numbers -> Like in the click counter: the listener must not change variables from `main`. -::: - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; - -public class GuessTheNumber { - static int secret = (int) (Math.random() * 100) + 1; - static int guesses = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Guess the number"); - window.setSize(320, 180); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel hint = new JLabel("I'm thinking of a number from 1 to 100."); - JTextField field = new JTextField(); - JButton button = new JButton("Guess"); - - button.addActionListener(e -> { - int guess = Integer.parseInt(field.getText()); - guesses++; - if (guess < secret) { - hint.setText(guess + " is too small."); - } else if (guess > secret) { - hint.setText(guess + " is too big."); - } else { - hint.setText("Correct! You needed " + guesses + " guesses."); - button.setEnabled(false); - } - field.setText(""); - }); - - window.add(hint); - window.add(field); - window.add(button); - window.setVisible(true); - } -} -``` - -- `field.setText("")` empties the text field after each guess, so you can type the next one straight away. -- `button.setEnabled(false)` switches the button off. It turns grey and can't be clicked any more. The game is over. - -::: ludwig -`setEnabled(true)` switches a button back on. That way you can, for example, only allow a button once something has been entered. -::: - -## Winning in 7 guesses - -::: angeberwissen -If you guess cleverly, you never need more than 7 guesses. Always guess the middle of the range that is left: first 50, then 25 or 75 and so on. Each guess halves the range: 100, 50, 25, 13, 7, 4, 2, 1. That is the **binary search** from lesson 19. Because 2 to the power of 7 = 128 is bigger than 100, 7 guesses are enough. -::: - -## Task: guess the number - -Rebuild the game and improve it: - -- An input like “abc” doesn't count as a guess and shows the hint “Please enter a number”. -- A second button **New game** thinks of a new number, sets the guesses to 0, switches the Guess button back on and resets the hint. - -Extra: after 7 wrong guesses the game is lost. The window then reveals the secret number. - -**You are done when …** - -- every wrong guess shows “too small” or “too big”, -- “abc” shows the hint to enter a number and the number of guesses stays the same, -- **New game** sets the guesses to 0, turns the guess button back on and resets the hint. - -::: ludwig -For “doesn't count as a guess”, `guesses++` must come **after** `Integer.parseInt`, both in the `try` block. If `parseInt` throws an exception, the line with `guesses++` is skipped. -::: - -## Game: Too small or too big? - -::: spiel frage -titel: Game: Too small or too big? -intro: Ludwig is playing guess the number. Read the code and predict what happens. Seven rounds. - -runde: - // secret is 42 - if (guess < secret) { - hint.setText(guess + " is too small."); - } else if (guess > secret) { - hint.setText(guess + " is too big."); - } else { - hint.setText("Correct!"); - } -frage: Ludwig types 50. What does the hint say? -- 50 is too small. -- [x] 50 is too big. -- Correct! -> 50 is greater than 42. - -runde: -frage: The number is between 1 and 100. 50 was too big, 25 was too small. Which number does Ludwig cleverly guess next? -- 26 -- [x] 37 -- 49 -> 26 to 49 are left. The middle of that is 37 (or 38). That way every guess halves the range. - -runde: - int roll = (int) (Math.random() * 6) + 1; -frage: Which numbers can `roll` have? -- 0 to 6 -- [x] 1 to 6 -- 1 to 7 -> `Math.random() * 6` is from 0 to just under 6. `(int)` turns that into 0 to 5, and `+ 1` into 1 to 6. A dice! - -runde: - int number = (int) (Math.random() * 10); -frage: Which numbers can `number` have? -- [x] 0 to 9 -- 1 to 10 -- 0 to 10 -> Without `+ 1` it starts at 0, and 10 is never reached. - -runde: - // secret is 42 - guesses++; - if (guess == secret) { - hint.setText("Correct! You needed " + guesses + " guesses."); - } -frage: Ludwig types 50, then 25, then 42. What does the hint say at the end? -- Correct! You needed 2 guesses. -- [x] Correct! You needed 3 guesses. -- Correct! You needed 42 guesses. -> `guesses++` counts up with every click, including the hit. - -runde: - button.setEnabled(false); -frage: Ludwig has found the number and clicks **Guess** once more. What happens? -- The game starts again. -- It counts one more guess. -- [x] Nothing, the button is grey and can't be clicked. -> `setEnabled(false)` switches the button off. - -runde: -frage: The number is now between 1 and 1000. How many guesses do you need at most with binary search? -- 7 -- [x] 10 -- 500 -> 2 to the power of 10 = 1024 is greater than 1000. Ten halvings are enough. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a random whole number from 1 to 100 with Math.random. → A random number -- I can decide which variables belong in the class. → What the game remembers -- I can switch a button off with setEnabled. → The whole program -- I can build a guessing game with a “New game” button. → Task: guess the number -::: - -## Quiz - -::: quiz -? Which numbers does (int) (Math.random() * 100) + 1 give? -→ A random number -- [ ] 0 to 100 -- [x] 1 to 100 -- [ ] 1 to 101 - -? What does button.setEnabled(false) do? -→ The whole program -- [ ] It removes the button from the window. -- [x] The button turns grey and can't be clicked any more. -- [ ] It changes the text on the button. - -? With binary search, how many guesses do you need at most for 1 to 100? -→ Task: guess the number -- [ ] 50 -- [ ] 10 -- [x] 7 -> Each guess halves the range, and 2 to the power of 7 = 128 is bigger than 100. -::: diff --git a/web/lessons/en/37-viele-knoepfe-ein-zuhoerer.md b/web/lessons/en/37-viele-knoepfe-ein-zuhoerer.md deleted file mode 100644 index cd9f2ca8..00000000 --- a/web/lessons/en/37-viele-knoepfe-ein-zuhoerer.md +++ /dev/null @@ -1,318 +0,0 @@ ---- -titel: Many buttons, one listener -bereich: Windows with Swing -kurz: One listener for many buttons, and finding out with getActionCommand and getSource which button was clicked. ---- - -## Skills I will gain now - -::: kompetenzen -- store a listener in a variable of type ActionListener and sign it up at several buttons -- explain what getActionCommand and getSource return -- put buttons onto a JPanel in a loop -- build a code lock that only opens with the right code -::: - -## The goal - -Since the business with the cake, Ludwig wants to lock his stable. He builds a **code lock**: nine digit keys, a zero, **C** to clear and **OK** to check. Only someone who types 1234 gets "Gate open!". - -That's twelve buttons. Until now every button had its own listener, as with the calculator in lesson 35. Today you learn how **one** listener is enough for all buttons. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Nine almost identical listeners - -With one listener per key it would look like this: - -```java -one.addActionListener(e -> display.setText(display.getText() + "1")); -two.addActionListener(e -> display.setText(display.getText() + "2")); -three.addActionListener(e -> display.setText(display.getText() + "3")); -// ... and so on up to 9 -``` - -The lines only differ in the digit. If you want to change a small detail, you have to do it nine times and are bound to forget one line. - -## A listener as a variable - -A listener can also be stored in a **variable**. Its data type is `ActionListener`, the contract from lesson 30. Then you sign up the same listener at several buttons: - -```java -ActionListener digit = e -> display.setText(display.getText() + e.getActionCommand()); - -one.addActionListener(digit); -two.addActionListener(digit); -three.addActionListener(digit); -``` - -`ActionListener` is in the package `java.awt.event`. So you need `import java.awt.event.*;` at the top. - -Now the code exists only once. But how does the listener know which digit to add? - -## Who clicked? - -The `e` before the arrow is the **event**, an object of type `ActionEvent`. The button passes it along with every click, and it contains what happened: - -| Call | Returns | Example | -|---|---|---| -| `e.getActionCommand()` | the caption of the clicked button, as text | `"7"` | -| `e.getSource()` | the clicked button itself | the button `ok` | - -- `getActionCommand()` is handy when the caption already says what to do, as with the digits. -- You compare `getSource()` with a button using `==`: `if (e.getSource() == ok)`. Here `==` is right, because it is about exactly this one object, not about a text. - -::: ludwig -You compare texts with `equals` and buttons with `==`. If you want to decide by the caption, you write `e.getActionCommand().equals("OK")`. That works too, but breaks as soon as someone changes the caption to "Open". `e.getSource() == ok` stays correct. -::: - -## Buttons in a loop - -With a shared listener you can even build the digit keys in a loop. So that they line up neatly in a grid, they go onto a **JPanel**. A JPanel is an area in the window with its own layout: - -```java -JPanel keys = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton digit = new JButton("" + i); - digit.addActionListener(key); - keys.add(digit); -} -``` - -- `"" + i` turns the number `i` into the text for the caption. -- Each pass creates a new button. All of them get the same listener `key`. -- `window.add(keys, BorderLayout.CENTER)` puts the whole area with all the keys into the window. - -::: quiz -? How many buttons and how many listeners does this loop create? -- [ ] 9 buttons and 9 listeners -- [x] 9 buttons and 1 listener -- [ ] 1 button and 9 listeners -> Each pass adds a new button. All of them get the same listener `key`. -::: - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class CodeLock { - public static void main(String[] args) { - JFrame window = new JFrame("Code lock"); - window.setSize(240, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new BorderLayout()); - - JLabel display = new JLabel("", SwingConstants.CENTER); - JPanel keys = new JPanel(new GridLayout(0, 3)); - JButton clear = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener key = e -> { - if (e.getSource() == clear) { - display.setText(""); - } else if (e.getSource() == ok) { - if (display.getText().equals("1234")) { - display.setText("Gate open!"); - } else { - display.setText("Wrong"); - } - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton digit = new JButton("" + i); - digit.addActionListener(key); - keys.add(digit); - } - JButton zeroKey = new JButton("0"); - zeroKey.addActionListener(key); - clear.addActionListener(key); - ok.addActionListener(key); - keys.add(clear); - keys.add(zeroKey); - keys.add(ok); - - window.add(display, BorderLayout.NORTH); - window.add(keys, BorderLayout.CENTER); - window.setVisible(true); - } -} -``` - -- `clear` and `ok` are created **before** the listener, because the listener compares with them. -- The listener first checks the two special buttons. All the others are digits and add their caption. - -::: angeberwissen -The keypad on a phone has the 1 at the top left, the one on a calculator and a computer keyboard has the 7 at the top left. Why? When push-button phones came along, there were tests with people who had to dial with them. With the 1 at the top they were faster, and calculating machines had their layout long before that. Ludwig's lock does it like the phone. -::: - -## Game: Who clicked? - -::: spiel zuhoerer -titel: Game: Who clicked? -intro: In every round **one** listener listens at several buttons. Ludwig shows you code, the window and the clicks. What does the display say at the end? Afterwards Ludwig plays the clicks. -frage: What does the display say after the clicks? - -runde: - JFrame window = new JFrame("Letters"); - JLabel display = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> display.setText(display.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> One listener, two buttons. Each time, `e.getActionCommand()` returns the caption of the clicked button, and it is added at the end. - -runde: - JFrame window = new JFrame("Counter"); - static int number = 0; - JLabel display = new JLabel("0"); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == plus) { - number++; - } else { - number--; - } - display.setText("" + number); - }; - plus.addActionListener(z); - minus.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> `e.getSource()` tells you which button it was. Plus twice, minus once: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame window = new JFrame("Letters"); - JLabel display = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> display.setText(display.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> The listener is only signed up at A and B. Nobody is listening when C is clicked. - -runde: - JFrame window = new JFrame("Keys"); - JLabel display = new JLabel(""); - JButton one = new JButton("1"); - JButton two = new JButton("2"); - JButton del = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - display.setText(""); - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - one.addActionListener(z); - two.addActionListener(z); - del.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> After 1 and 2 the display says "12". C clears everything, and the last 1 stands alone. - -runde: - JFrame window = new JFrame("Lock"); - JLabel display = new JLabel(""); - JButton four = new JButton("4"); - JButton two = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (display.getText().equals("42")) { - display.setText("Gate open!"); - } else { - display.setText("Wrong"); - } - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - four.addActionListener(z); - two.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Gate open! -- [x] Wrong -- 24OK -> "24" was typed, but the secret is "42". The order of the clicks matters. -::: - -## Task: Code lock - -Build the code lock and try it out. - -Then make it better: - -- After three wrong attempts, **OK** switches itself off, like the button in Guess the number in lesson 36. Count the attempts in a `static int` variable in the class. -- Extra: the display shows `*` instead of the digits. Remember the real input in a `static String input = "";` and only add a star to the display. - -**You are done when …** - -- the right code opens the lock and a wrong one doesn’t, -- after three wrong tries **OK** is switched off, -- for the extra, the display only shows stars but the code is still checked. - -::: ludwig -In the branch for **OK**, count `attempts++` for every wrong code and then check `if (attempts >= 3)`. Then `ok.setEnabled(false)` switches the button off, like in the guessing game. For the stars: `input = input + e.getActionCommand();` and `display.setText(display.getText() + "*");`. Then you compare `input`, not the text of the display. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can store a listener in a variable of type ActionListener and sign it up at several buttons. → A listener as a variable -- I can explain what getActionCommand and getSource return. → Who clicked? -- I can put buttons onto a JPanel in a loop. → Buttons in a loop -- I can build a code lock that only opens with the right code. → Task: Code lock -::: - -## Quiz - -::: quiz -? What does e.getActionCommand() return for a click on the button new JButton("7")? -→ Who clicked? -- [ ] the number 7 -- [x] the text "7" -- [ ] the button itself - -? How do you check whether the button ok was clicked? -→ Who clicked? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource returns the button itself, and you compare it with ==. - -? Why is a listener stored in a variable? -→ A listener as a variable -- [ ] So that it runs faster. -- [x] So that you can sign it up at several buttons and write the code only once. -- [ ] Because lambdas aren't allowed otherwise. - -? Match. -→ Who clicked? -= ActionListener -> data type of a listener -= ActionEvent -> the event e with the information about the click -= JPanel -> an area with its own layout -::: diff --git a/web/lessons/en/38-innere-klassen.md b/web/lessons/en/38-innere-klassen.md deleted file mode 100644 index 30aa2cc2..00000000 --- a/web/lessons/en/38-innere-klassen.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Inner classes -bereich: Windows with Swing -kurz: Write a listener as a named inner class, as an anonymous class and as a lambda, and know who can see what. ---- - -## Skills I will gain now - -::: kompetenzen -- write a listener as a named inner class -- write a listener as an anonymous class -- explain what is left out in a lambda -- explain which way of writing sees which variables -- build a counter with three buttons written in three ways -::: - -## The goal - -You already know the click counter from lesson 32. Today you write its listener in **three ways**, all of which do exactly the same: - -1. as a **named inner class**, the way it was mostly done before Java 8, -2. as an **anonymous class**, which you know from lesson 30 (“ActionListener is an interface”), -3. as a **lambda** with the arrow, as before. - -In other people's code, in books and on the internet you will meet all three. After this lesson you will recognise them. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Reminder: the contract - -A listener is anything that fulfils the contract `ActionListener`. It contains exactly one method: - -``` -public void actionPerformed(ActionEvent e) -``` - -The button calls it with every click. The three ways of writing are three ways of providing this one method. - -## 1. The named inner class - -An **inner class** is a class inside another class. It has a name, here `Plus`, and fulfils the contract with `implements ActionListener`: - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Counter { - static int count = 0; - static JLabel number = new JLabel("0", SwingConstants.CENTER); - - static class Plus implements ActionListener { - public void actionPerformed(ActionEvent e) { - count++; - number.setText("" + count); - } - } - - public static void main(String[] args) { - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - JButton again = new JButton("Also +1"); - plus.addActionListener(new Plus()); - again.addActionListener(new Plus()); - - window.add(number); - window.add(plus); - window.add(again); - window.setVisible(true); - } -} -``` - -- `new Plus()` creates an object of the class `Plus`. That is the listener the button gets. -- Because the class has a name, you can use it for several buttons, here for `plus` and `again`. -- The class is **outside** `main`. So it only sees what is in the class `Counter`: `count` and `number`. That's why `number` is a variable of the class this time and not of `main`. -- You need `static class` because `main` is static. Without `static`, javac reports "non-static variable this cannot be referenced from a static context". - -## 2. The anonymous class - -If you only need the listener in one place, you can write the class right where the object is needed. Then it gets no name: - -```java -plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - count++; - number.setText("" + count); - } -}); -``` - -- `new ActionListener() { … }` means: "a new object that fulfils the contract ActionListener, like this: …". -- At the end there is `});`: the curly bracket closes the class, the round one closes the call to `addActionListener`. -- The anonymous class is **inside** `main`. So it also sees the variables of `main`, as long as they don't change any more. - -::: quiz -? What does `});` close at the end of the anonymous class? -- [x] `}` closes the class, `)` closes the call of `addActionListener`. -- [ ] `}` closes `main`, `)` closes the class. -- [ ] Both close the method `actionPerformed`. -> The method `actionPerformed` already ends one line earlier with its own `}`. -::: - -## 3. The lambda - -The lambda is the short form of the anonymous class, since Java 8: - -```java -plus.addActionListener(e -> { - count++; - number.setText("" + count); -}); -``` - -Everything Java knows anyway is left out: `new ActionListener()`, the name `actionPerformed`, `public void` and the type `ActionEvent`. What remains is the parameter `e`, the arrow and the body. This only works because the contract has exactly **one** method. - -## The three compared - -| | named inner class | anonymous class | lambda | -|---|---|---|---| -| has a name | yes, e.g. `Plus` | no | no | -| stands | in the class, outside `main` | right at the button | right at the button | -| for several buttons | yes, `new Plus()` | no | yes, in a variable (lesson 37) | -| sees variables of `main` | no | yes, if they don't change | yes, if they don't change | -| length | longest | medium | shortest | - -::: ludwig -If a variable of `main` should change in the listener, like `count++`, it has to go into the class, whichever way you write the listener. That was the problem from lesson 32. -::: - -::: angeberwissen -After compiling, look into the folder with the `.class` files. javac creates one file for every class: `Counter.class`, `Counter$Plus.class` for the inner class and `Counter$1.class` for the anonymous class. As it has no name, it gets a number. A lambda gets no file of its own, Java only builds it when the program starts. -::: - -## Game: Which way of writing? - -::: spiel frage -titel: Game: Which way of writing? -intro: Ludwig shows you listeners from other people's programs. Which way of writing is it? Six rounds. -frage: Which way of writing is this? -antworten: named inner class | anonymous class | lambda - -runde: - button.addActionListener(e -> text.setText("Hello")); -richtig: lambda -> The arrow `->` gives the lambda away. - -runde: - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - text.setText("Hello"); - } - }); -richtig: anonymous class -> `new ActionListener() {` followed by a whole method: a class without a name is being written here. - -runde: - static class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - text.setText("Hello"); - } - } -richtig: named inner class -> The class is called `Greeting` and fulfils the contract with `implements ActionListener`. - -runde: - button.addActionListener(new Greeting()); -richtig: named inner class -> `new Greeting()` creates an object of the class `Greeting`. The class itself is somewhere else and has a name. - -runde: - ActionListener key = e -> { - display.setText(display.getText() + e.getActionCommand()); - }; -richtig: lambda -> A lambda in a variable, as with the code lock in lesson 37. The arrow gives it away. - -runde: - ActionListener key = new ActionListener() { - public void actionPerformed(ActionEvent e) { - display.setText(display.getText() + e.getActionCommand()); - } - }; -richtig: anonymous class -> An anonymous class can be stored in a variable too. Then it can be signed up at several buttons, just like the lambda. -::: - -## Task: One counter, three ways - -Build the click counter with three buttons: - -- **+1** gets the named inner class `Plus`. -- **-1** gets an anonymous class. -- **Reset** gets a lambda. - -Check that all three buttons count as expected. Then remove the `static` from the class `Plus` and read javac's error message. - -**You are done when …** - -- **+1** counts up, **-1** counts down and **Reset** sets 0, -- the code contains exactly one named inner class, one anonymous class and one lambda, -- you have read javac’s error message without `static` on `Plus` and can explain what it means. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can write a listener as a named inner class. → 1. The named inner class -- I can write a listener as an anonymous class. → 2. The anonymous class -- I can explain what is left out in a lambda. → 3. The lambda -- I can explain which way of writing sees which variables. → The three compared -- I can build a counter with three buttons written in three ways. → Task: One counter, three ways -::: - -## Quiz - -::: quiz -? Why does the short form with the arrow work for ActionListener? -→ 3. The lambda -- [ ] Because ActionListener is a class. -- [x] Because the contract has exactly one method. -- [ ] Because the button demands it. - -? Which way of writing can you use with new Plus() for several buttons without storing it in a variable? -→ 1. The named inner class -- [x] the named inner class -- [ ] the anonymous class -- [ ] the lambda - -? Why is there static in front of class Plus? -→ 1. The named inner class -- [ ] So that the class is faster. -- [x] Because main is static and couldn't create an object of Plus otherwise. -- [ ] Because every class has to be static. - -? Match. -→ The three compared -= class Plus implements ActionListener -> named inner class -= new ActionListener() { … } -> anonymous class -= the arrow after e -> lambda -::: diff --git a/web/lessons/en/39-zwei-reiter.md b/web/lessons/en/39-zwei-reiter.md deleted file mode 100644 index 03bc438a..00000000 --- a/web/lessons/en/39-zwei-reiter.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Two tabs -bereich: Windows with Swing -kurz: Build a window with two tabs, bring data from one tab to the other and save the list as a text file. ---- - -## Skills I will gain now - -::: kompetenzen -- build a window with two tabs -- add lines to a JTextArea with append -- explain why a listener can use building blocks on both tabs -- save a file inside a listener with try and catch -- build a shopping list that counts how many things are on it -::: - -## The goal - -Ludwig's shopping list from lesson 23 gets a window. It has two **tabs**, like the tabs in a browser: - -- In the tab **Input** you type what you need and click **Add**. -- The tab **List** shows everything you have entered. A button **Save** writes the list into a text file. - -New are the tabs (`JTabbedPane`), a text area with several lines (`JTextArea`) and saving from inside a listener. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Tabs with JTabbedPane - -Each tab shows its own area. The areas are JPanels, like the keypad of the code lock in lesson 37: - -```java -JTabbedPane tabs = new JTabbedPane(); -JPanel input = new JPanel(new GridLayout(0, 1)); -JPanel list = new JPanel(new BorderLayout()); - -tabs.addTab("Input", input); -tabs.addTab("List", list); -window.add(tabs); -``` - -| Call | Meaning | -|---|---| -| `addTab("Input", input)` | a new tab with the caption "Input", it shows the JPanel `input` | -| `setSelectedIndex(1)` | switches to the second tab, counting starts at 0 | -| `setTitleAt(1, "List (3)")` | changes the caption of the second tab | - -At the start, the tab that was added first is open. - -## A text area with several lines - -A `JTextField` has only one line. For the list you need a `JTextArea`: - -```java -JTextArea area = new JTextArea(); -area.setEditable(false); -area.append("Hay\n"); -``` - -- `append` adds text at the end. The `"\n"` then starts a new line, as when splitting with `split("\n")` in lesson 24. -- `setEditable(false)` stops people from typing into the list itself. -- `getText()` returns the whole text with all lines. - -::: quiz -? What does `area.append("Hay\n")` do? -- [ ] It replaces the whole text with “Hay”. -- [x] It adds “Hay” at the end and then starts a new line. -- [ ] It writes “Hay\n” into the list exactly like that. -> `append` adds at the end, `"\n"` is the line break. -::: - -## From one tab to the other - -The button **Add** is on the tab Input, the text area on the tab List. Still, the listener can use both. All building blocks are variables in `main`, and the lambda sees all of them, no matter on which tab they are shown later: - -```java -add.addActionListener(e -> { - area.append(field.getText() + "\n"); - info.setText(field.getText() + " is on the list."); - field.setText(""); -}); -``` - -::: ludwig -A building block can only lie on **one** area. If you add the same button first to `input` and then to `list`, Swing takes it away from `input`. It only shows up on the tab List. -::: - -## Saving inside the listener - -To save, you use the `PrintWriter` from lesson 23. Inside the listener there is a catch, though: the way out with `throws Exception` from lesson 23 doesn't exist here. The lambda is a method of its own, `actionPerformed`, and according to the contract it must not pass on a `FileNotFoundException`. That's why saving goes into try and catch: - -```java -save.addActionListener(e -> { - try { - PrintWriter out = new PrintWriter("shoppinglist.txt"); - out.print(area.getText()); - out.close(); - info.setText("Saved in shoppinglist.txt"); - tabs.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Saving didn't work."); - } -}); -``` - -- The exception is called `ex` here, because the name `e` is already taken by the event. -- After saving, the window jumps back to the tab Input with `setSelectedIndex(0)`, where the message is. -- VisuStruct shows the saved file below the console as soon as you have clicked Save. - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class ShoppingList { - public static void main(String[] args) { - JFrame window = new JFrame("Shopping list"); - window.setSize(300, 250); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane tabs = new JTabbedPane(); - - JPanel input = new JPanel(new GridLayout(0, 1)); - JTextField field = new JTextField(); - JButton add = new JButton("Add"); - JLabel info = new JLabel("Nothing on the list yet."); - input.add(new JLabel("What do you need?")); - input.add(field); - input.add(add); - input.add(info); - - JPanel list = new JPanel(new BorderLayout()); - JTextArea area = new JTextArea(); - area.setEditable(false); - JButton save = new JButton("Save"); - list.add(area, BorderLayout.CENTER); - list.add(save, BorderLayout.SOUTH); - - tabs.addTab("Input", input); - tabs.addTab("List", list); - - add.addActionListener(e -> { - area.append(field.getText() + "\n"); - info.setText(field.getText() + " is on the list."); - field.setText(""); - }); - - save.addActionListener(e -> { - try { - PrintWriter out = new PrintWriter("shoppinglist.txt"); - out.print(area.getText()); - out.close(); - info.setText("Saved in shoppinglist.txt"); - tabs.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Saving didn't work."); - } - }); - - window.add(tabs); - window.setVisible(true); - } -} -``` - -Type Hay, click Add, then Apples and Add again. Switch to the tab List and click Save. - -## Game: Where does it end up? - -::: spiel frage -titel: Game: Where does it end up? -intro: Ludwig builds windows with tabs. Read the code and predict what you see. Six rounds. - -runde: - tabs.addTab("Input", input); - tabs.addTab("List", list); -frage: Which tab is open at the start? -- [x] Input -- List -- none -> At the start, the tab that was added first is open. - -runde: - tabs.addTab("List", list); - tabs.addTab("Input", input); -frage: Which tab is open at the start? -- Input -- [x] List -- both -> This time List comes first. The order of `addTab` decides the order of the tabs. - -runde: - tabs.addTab("Input", input); - tabs.addTab("List", list); - tabs.addTab("Help", help); - tabs.setSelectedIndex(1); -frage: Which tab is open? -- Input -- [x] List -- Help -> Counting starts at 0: 0 is Input, 1 is List, 2 is Help. - -runde: - add.addActionListener(e -> { - area.append(field.getText() + "\n"); - field.setText(""); - }); - // You type Hay, click Add, type Apples, click Add. -frage: What is on the tab List afterwards? -- Apples -- HayApples -- [x] Hay\nApples -> `append` adds at the end, and each `"\n"` starts a new line. - -runde: - JButton button = new JButton("OK"); - input.add(button); - list.add(button); -frage: Where does the button OK show up? -- on both tabs -- only on the tab Input -- [x] only on the tab List -> A building block can only lie on one area. With the second `add`, Swing takes it away from `input`. - -runde: - save.addActionListener(e -> { - area.setText(""); - tabs.setTitleAt(1, "List (empty)"); - tabs.setSelectedIndex(0); - }); - // The button save is on the tab List. -frage: What do you see after the click? -- the empty tab List -- [x] the tab Input, and the second tab is called "List (empty)" -- the tab Input, the second tab is still called "List" -> The listener empties the area, renames the tab and switches to tab 0. All of it happens with one click. -::: - -## Task: Shopping list with tabs - -Build the program and try it out. - -Then make it better: - -- Empty inputs don't go onto the list. Check `field.getText().isEmpty()` first (lesson 17). -- The tab List shows in its title how many things are on the list: "List (3)". Count along in a `static int` variable and use `setTitleAt`. -- Extra: a third tab **Help** with a JLabel that explains how the program works. - -**You are done when …** - -- an entry you type appears in the list tab after the click, -- an empty field adds nothing to the list, -- after three entries the tab title shows the count, e.g. “List (3)”. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a window with two tabs. → Tabs with JTabbedPane -- I can add lines to a JTextArea with append. → A text area with several lines -- I can explain why a listener can use building blocks on both tabs. → From one tab to the other -- I can save a file inside a listener with try and catch. → Saving inside the listener -- I can build a shopping list that counts how many things are on it. → Task: Shopping list with tabs -::: - -## Quiz - -::: quiz -? Which tab is open after tabs.setSelectedIndex(0)? -→ Tabs with JTabbedPane -- [x] the first one -- [ ] the second one -- [ ] none - -? Why is saving inside the listener in try and catch? -→ Saving inside the listener -- [ ] Because PrintWriter only works inside try. -- [x] Because actionPerformed must not pass on a FileNotFoundException. -- [ ] Because main has no throws. - -? What is the difference between JTextField and JTextArea? -→ A text area with several lines -- [ ] There is none. -- [x] JTextArea can show several lines. -- [ ] You can't write into a JTextField. - -? Match. -→ Tabs with JTabbedPane -= JTabbedPane -> an area with tabs -= JPanel -> the area of a tab -= JTextArea -> a text area with several lines -::: diff --git a/web/lessons/en/40-model-view-controller.md b/web/lessons/en/40-model-view-controller.md deleted file mode 100644 index 6d577637..00000000 --- a/web/lessons/en/40-model-view-controller.md +++ /dev/null @@ -1,430 +0,0 @@ ---- -titel: Model View Controller -bereich: Windows with Swing -kurz: Split the click counter into model, view and controller and understand who talks to whom. ---- - -## Skills I will gain now - -::: kompetenzen -- say what model, view and controller each do -- write a model without Swing and test it without a window -- explain what new Counter() builds and why there is no static -- name the four messages after a click -- add a second view without changing the model -- build a counter with −1, Reset and limits in the model -::: - -## The goal - -Big programs with windows almost always share the same structure. It is called **Model View Controller**, **MVC** for short: - -- The **model** holds the data and knows its rules. -- The **view** only shows the data. -- The **controller** reacts to clicks and passes the word on. - -You learn MVC with the click counter from lesson 32. It is split into three parts, step by step. In the end it shows the same count twice: as a number and as a bar. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Everything in one class - -This is what the click counter looked like so far, everything sits in `main` and in one variable of the class: - -```java -static int count = 0; - -public static void main(String[] args) { - // ... build the window ... - JLabel number = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); -} -``` - -That works. But imagine the count should also show up as a bar, later in a second window and in a file as well. Then every listener has to think of every display. Counting and showing are tied up in a knot, and with every display the knot gets bigger. - -MVC unties the knot: every job gets its own class. - -## The model - -The model is the counter itself, without any Swing: - -```java -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} -``` - -- This time there is no `static` in front of `value` and the methods. That makes them belong to an **object**: `new Counter()` builds a counter with its own `value`, just as `new JButton("+1")` builds a button with its own caption. -- `model.increase()` calls the method of this one counter, just like `button.setText(…)`. -- Whoever wants to know the value asks with `getValue()`. It can only be changed through `increase()`. - -As the model needs no window, you can try it out without a window at all: - -```java -public class CounterTest { - public static void main(String[] args) { - Counter model = new Counter(); - model.increase(); - model.increase(); - System.out.println(model.getValue()); - } -} - -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} -``` - -The output is 2. Several classes may stand in one file. Only one of them is `public`, and that one contains `main`. - -## The view - -The view shows the value and does nothing else. It doesn't calculate, and it doesn't know where the number comes from: - -```java -class NumberView { - JLabel number = new JLabel("0", SwingConstants.CENTER); - - void update(int value) { - number.setText("" + value); - } -} -``` - -- The view is handed the value with `update(int value)` and writes it into its JLabel. -- You build the window and the button in `main`, as always. `window.add(view.number)` puts the view's JLabel into the window. - -## The controller - -The controller is the listener at the button. It connects model and view: - -```java -Counter model = new Counter(); -NumberView view = new NumberView(); - -plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); -}); -``` - -- First it changes the model, then it fetches the new value and hands it to the view. -- The problem from lesson 32 is gone: the variable `model` never changes, it always points to the same counter. Only the `value` inside it changes. That's why the lambda may use `model`, without any `static`. - -## Who talks to whom? - -With every click four messages run, always in the same order: - -::: grafik mvc-klick.svg -The arrows light up one after the other. The model counts up at the second arrow, the display only at the fourth. -::: - -1. The button reports the click to the controller (`actionPerformed`). -2. The controller tells the model: `increase()`. -3. The controller asks the model for the new value: `getValue()`. -4. The controller hands the value to the view: `update(value)`. - -The model talks to nobody. It waits until it is asked. - -::: ludwig -Remember: the model knows nothing about windows. In `Counter` there isn't a single `JLabel` and no `import javax.swing`. If you need Swing there, the code belongs in the view. -::: - -::: quiz -? Who does the model talk to on its own? -- [ ] The view -- [ ] The button -- [x] Nobody -> The model waits until the controller asks it. -::: - -## The pay-off: a second view - -Now the count should also show up as a bar. For that a second view joins in. A `JProgressBar` is a bar that fills up, here from 0 to 10: - -```java -class BarView { - JProgressBar bar = new JProgressBar(0, 10); - - void update(int value) { - bar.setValue(value); - } -} -``` - -The controller gets one more line. The model stays exactly as it was: - -```java -plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); -}); -``` - -That is exactly the pay-off of MVC: you can add or swap displays without touching the model. - -## The whole program - -```java -import javax.swing.*; -import java.awt.*; - -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} - -class NumberView { - JLabel number = new JLabel("0", SwingConstants.CENTER); - - void update(int value) { - number.setText("" + value); - } -} - -class BarView { - JProgressBar bar = new JProgressBar(0, 10); - - void update(int value) { - bar.setValue(value); - } -} - -public class ClickCounter { - public static void main(String[] args) { - Counter model = new Counter(); - NumberView view = new NumberView(); - BarView bar = new BarView(); - - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); - }); - - window.add(view.number); - window.add(bar.bar); - window.add(plus); - window.setVisible(true); - } -} -``` - -Click +1 a few times and watch the number and the bar grow together. - -::: angeberwissen -MVC was invented in 1979 by the Norwegian Trygve Reenskaug, at the research centre Xerox PARC in California. Back then there were already computers with windows and a mouse there. In the same year Steve Jobs visited the lab and was so excited that Apple went on to build computers with windows and a mouse. -::: - -## Game: Where does the code belong? - -::: spiel frage -titel: Game: Where does the code belong? -intro: Ludwig has thrown code from an MVC program onto a pile. Sort every piece. Eight rounds. -frage: Where does this code belong? -antworten: Model | View | Controller - -runde: - int value = 0; -richtig: Model -> The count is the data. Data lives in the model. - -runde: - number.setText("" + value); -richtig: View -> Showing something on the screen is the view's job. - -runde: - plus.addActionListener(e -> model.increase()); -richtig: Controller -> Reacting to a click and telling the model: that's what the controller does. - -runde: - void increase() { - value++; - } -richtig: Model -> The model itself knows how to change its data. - -runde: - bar.setValue(value); -richtig: View -> A bar that fills up only shows something. So: view. - -runde: - void increase() { - if (value < 10) { - value++; - } - } -richtig: Model -> "At most 10" is a rule for the data. Rules belong in the model, then they hold for every button and every view. - -runde: - view.update(model.getValue()); -richtig: Controller -> Fetching the value from the model and handing it to the view: that's the controller in the middle. - -runde: - JLabel number = new JLabel("0", SwingConstants.CENTER); -richtig: View -> A JLabel is a building block of the screen. There is no Swing in the model. -::: - -## Game: Whose turn is it? - -::: spiel reihenfolge -titel: Game: Whose turn is it? -intro: After a click, model, view and controller talk to each other, but it only works in one order. Put the steps in the right order. Five rounds. - -runde: -frage: You click +1 in the click counter. What happens, one after the other? -- You click +1. -- The controller gets `actionPerformed`. -- The controller calls `model.increase()`. -- The model counts `value` up. -- The controller fetches `model.getValue()`. -- The view shows the new number. -> Click, controller, change the model, fetch the value, show it. Those are the four arrows from the picture, with counting up in the middle. - -runde: - plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); - }); -frage: What happens after a click on +1, one after the other? -- The click on +1 -- `model.increase()` -- The number shows the new value. -- The bar grows. -> The listener works through its lines from top to bottom: first the model, then the number, then the bar. - -runde: -frage: Put the controller in the right order so that the view shows the new number. -- `plus.addActionListener(e -> {` -- `model.increase();` -- `view.update(model.getValue());` -- `});` -> First change, then show. The other way round, the view still shows the old value. - -runde: - reset.addActionListener(e -> { - model.reset(); - view.update(model.getValue()); - }); -frage: The counter is at 5. You click Reset. What happens, one after the other? -- You click Reset. -- The model sets `value` to 0. -- `model.getValue()` returns 0. -- The view shows 0. -> Reset also runs through the controller into the model. The view only learns about the new value at the very end. - -runde: -frage: A test without a window should print 1. In which order do the lines stand in main? -- `Counter model = new Counter();` -- `model.increase();` -- `System.out.println(model.getValue());` -> First build the object, then work with it, and look at the result last. The model can be tested without any window. -::: - -## Task: A counter with limits - -Build the whole program and try it out. - -Then make it better: - -- A button **−1** counts down. The model gets a method `decrease()` for it, the controller a second listener. -- A button **Reset** sets the counter back to 0 with `reset()`. -- The model itself takes care of the limits: never below 0 and never above 10, because then the bar is full. -- Extra: a third view `ConsoleView` whose `update` prints "New value: 3" with `System.out.println`. A view doesn't have to be a window. - -**You are done when …** - -- **+1**, **−1** and **Reset** count correctly, -- the counter never goes below 0 or above 10, however often you click, -- number and bar always show the same value, -- the model class contains no `JButton`, `JLabel` or other Swing. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can say what model, view and controller each do. → The goal -- I can write a model without Swing and test it without a window. → The model -- I can explain what new Counter() builds and why there is no static. → The model -- I can name the four messages after a click. → Who talks to whom? -- I can add a second view without changing the model. → The pay-off: a second view -- I can build a counter with −1, Reset and limits in the model. → Task: A counter with limits -::: - -## Quiz - -::: quiz -? Where does the rule "The counter never goes above 10" belong? -→ The model -- [x] in the model -- [ ] in the view -- [ ] in the controller -> Rules about the data belong in the model. Then they hold no matter which button is pressed. - -? What does the view do? -→ The goal -- [ ] It calculates the new value. -- [x] It shows the value it is given. -- [ ] It listens for clicks. - -? Why may the lambda use model, even though model is a variable of main? -→ Who talks to whom? -- [ ] Because Counter is public. -- [x] Because model always points to the same counter; only the value inside it changes. -- [ ] Because lambdas may change any variable. - -? What do you have to change in the model when a second view joins in? -→ The pay-off: a second view -- [x] nothing -- [ ] a second method increase -- [ ] a second JLabel - -? Match. -→ The goal -= Counter with value and increase() -> model -= NumberView with update(int value) -> view -= the listener at the button -> controller -::: diff --git a/web/lessons/en/aufgaben/alter-unterprogramm.md b/web/lessons/en/aufgaben/alter-unterprogramm.md deleted file mode 100644 index fc8b888f..00000000 --- a/web/lessons/en/aufgaben/alter-unterprogramm.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Age in a subprogram -nach: 18 -stufe: 2 ---- - -The age check from “Adult?” now becomes a subprogram of its own. The main program only calls it. - -::: übung aufgabe-alter-unterprogramm -titel: Age in a subprogram -auftrag: Build two diagrams. The main program outputs **Welcome**, then calls `checkAge()` and finally outputs **Goodbye**. - The subprogram `checkAge` is in a tab of its own: it asks for the age and outputs **adult** or **minor**. -test: 20 -> Welcome | adult | Goodbye -test: 18 -> Welcome | adult | Goodbye -test: 12 -> Welcome | minor | Goodbye -braucht: aufruf -tipp: The main program has three boxes. The subprogram does the real work. -tipp: Open the exercise in the editor and build the main program there with the call `checkAge()`. Then open a new tab with **+** and give it the title `checkAge`. Build the input and the branch in it. Check finds the subprogram by the title of the tab. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/ballwurf.md b/web/lessons/en/aufgaben/ballwurf.md deleted file mode 100644 index 868b5cd5..00000000 --- a/web/lessons/en/aufgaben/ballwurf.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -titel: Ball throw results -nach: 25 -stufe: 2 ---- - -At sports day everyone wrote their ball throw distance into a table: - -::: datei throw.csv -Name;Distance -Ludwig;28 -Zora;42 -Fox;35 -Hare;21 -::: - -::: übung aufgabe-ballwurf -titel: Ball throw results -code: java -auftrag: Read the file and skip the header line. Output how many took part (**Participants: 4**) and how far they threw on average (**Average: 31.5 m**). - Save the longest throw in the file **winner.txt**, for example **Winner: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Participants: 4 | Average: 31.5 m -datei: winner.txt -> Winner: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: You need four variables before the loop: the count, the sum as a `double`, the best distance and the best name. -tipp: In the loop: `String[] parts = line.split(";");` and `int distance = Integer.parseInt(parts[1]);`. If `distance > bestDistance`, remember the distance and the name. To save: `PrintWriter out = new PrintWriter("winner.txt");`, `out.println(…)` and `out.close();`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/bestenliste.md b/web/lessons/en/aufgaben/bestenliste.md deleted file mode 100644 index 68ec396a..00000000 --- a/web/lessons/en/aufgaben/bestenliste.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -titel: Leaderboard -nach: 19 -stufe: 2 ---- - -Five goats have run the obstacle race. Now the leaderboard goes on the stable door: most points at the top, fewest at the bottom. - -::: übung aufgabe-bestenliste -titel: Leaderboard -auftrag: Read 5 scores into an array. Sort the array with bubble sort in **descending** order, so the biggest number comes first. Then output the list with places, for example **1. 9**, on the next line **2. 9** and so on. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Bubble sort from lesson 19 sorts in ascending order. For descending order you only turn round the comparison of the neighbours. -tipp: Swap when `points[i] < points[i + 1]`, using a temporary variable. For the output, first work out `int place = i + 1` and then output `place + ". " + points[i]`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-bestenliste-java -titel: Leaderboard in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Read each number into a variable first and then put it into the array: `int number = scanner.nextInt();` and `points[i] = number;`. -tipp: The swap needs a temporary variable in Java too: `int temp = points[i]; points[i] = points[i + 1]; points[i + 1] = temp;`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/binaerzahl.md b/web/lessons/en/aufgaben/binaerzahl.md deleted file mode 100644 index 69c5b325..00000000 --- a/web/lessons/en/aufgaben/binaerzahl.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -titel: Binary number -nach: 11 -stufe: 3 ---- - -Computers calculate with only two digits: 0 and 1. This is how you convert a number into the **binary system**: - -1. Divide the number by 2 and remember the remainder (0 or 1). -2. Carry on with the result without the remainder. -3. Repeat this until the number is 0. - -The remainders read **from bottom to top** give the binary number. For 6 the remainders are 0, 1, 1, so the binary number is 110. - -::: übung aufgabe-binaer -titel: Binary number -auftrag: Ask for a whole number greater than 0 and output it as a binary number. For 6 the output is **110**, for 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Keep dividing the number by 2 and remember every remainder. The remainders read backwards give the binary number. -tipp: Collect the digits in a text: `String binary = ""`. In the loop you calculate `int remainder = number % 2` and add the remainder at the **front**: `binary = remainder + binary`. After that, `number = number / 2`. Because number is a whole number, the remainder is dropped. -lösung: aufgabe-binaer-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-binaer-java -titel: Binary number in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Start with `String binary = "";`. The loop runs with `while (number > 0)`. -tipp: In the body: `int remainder = number % 2;`, then `binary = remainder + binary;` and `number = number / 2;`. Because `binary` is a String, `+` puts the remainder at the front. -lösung: aufgabe-binaer-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/caesar.md b/web/lessons/en/aufgaben/caesar.md deleted file mode 100644 index b147e7e2..00000000 --- a/web/lessons/en/aufgaben/caesar.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -titel: Caesar cipher -nach: 17 -stufe: 3 ---- - -More than 2000 years ago, the Roman general Julius Caesar sent secret messages to his troops. His trick was simple: he replaced every letter with the letter **three places further on** in the alphabet. A becomes D, B becomes E and so on. The readable message is called the **plain text**, the encrypted one the **secret text**. The number of places you shift by is the **key**, often just called `k`. - -::: grafik caesar-verschiebung.svg -Encryption with the key 3: each letter at the top becomes the letter below it. At the end you start again from the beginning, so X, Y and Z become A, B and C. -::: - -This turns `HALLO` into `KDOOR`. Anyone who knows the key simply shifts three places back and can read the message again. - -### The cipher wheel - -To avoid counting every time, people built a **cipher wheel**: two discs with the alphabet, a small one on top of a large one, fixed in the middle so it can turn. The outer ring shows the plain text, the inner ring the secret text. Turn the inner disc by three letters, and below every outer letter you find its secret letter. Wheels like this have existed since the 15th century. In the American Civil War (1861–1865), soldiers carried small brass wheels. - -::: grafik caesar-scheibe.svg -A cipher wheel modelled on historical brass wheels. The blue window shows: with the key 3, D is below A. -::: - -::: angeberwissen -Caesar himself didn’t even have a wheel! We know this from the Roman historian Suetonius: in his letters, Caesar shifted every letter by three places and simply counted it out in his head. The wheel was only invented by Leon Battista Alberti around 1467, about 1500 years later. By the way, I count in my head too. I don’t have fingers, only hooves. -::: - -Try it yourself. Turn the inner wheel, type a text and watch the secret text appear. **Decrypt** goes the other way. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -You can easily make a real wheel yourself: two circles of cardboard, one a bit smaller, each with 26 equal fields for the alphabet, and a paper fastener in the middle. Your secret agent tool is ready! -::: - -::: quiz -? What does the letter Y become with the key 3? -- [ ] A -- [x] B -- [ ] C -> After Y comes Z, then you start again with A and B. Three places further on is therefore B. - -? A secret text was encrypted with the key 5. How do you decrypt it? -- [ ] Shift it 5 places further on again -- [x] Shift it 5 places back -- [ ] Read the letters backwards -> Decrypting is the opposite of encrypting. By the way, 5 places back is the same as 21 places forward, because 5 + 21 = 26. -::: - -### The program: there is more than one way - -How do you teach the computer to shift a letter by `k` places? There is no *single* right solution. We look at two ways. Both go through the text character by character with a for loop, just like in the lesson on strings. The difference is how they shift a single letter. - -In both ways the remainder operator `%` helps. It makes sure that after Z you continue with A: positions 0 to 25 stay as they are, 26 becomes 0, 27 becomes 1 and so on. - -### Way 1: the alphabet in an array - -You put the alphabet into an array `char[] alphabet = {'A', 'B', …, 'Z'}`. For every letter of the word you search the array for its position `pos`, that is its index. The secret letter is then in the same array at position `(pos + k) % 26`. - -An example with the key 3: X is at position 23. Then `(23 + 3) % 26 = 0`, and position 0 holds A. - -::: übung aufgabe-caesar-array -titel: Caesar with an array -auftrag: The start diagram already contains the array `alphabet`. Ask for a word in capital letters and for the key `k`. Then output the encrypted word. For HALLO and 3 that is `KDOOR`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Create an empty text `String secret = ""`. Go through the word character by character with a for loop. In every pass you append one secret letter to `secret`. -tipp: To find the position of a letter you need a second, inner loop over the array: if `alphabet[j] == letter`, remember `pos = j`. -tipp: After the inner loop, append `alphabet[(pos + k) % 26]`: `secret = secret + alphabet[(pos + k) % 26]`. After the outer loop, output `secret`. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Way 2: calculating with the ASCII table - -For the computer, every character is really a number. Which number belongs to which character is listed in the **ASCII table**. There A has the code 65, B the code 66 and so on up to Z with 90. In Java you turn a character into its code with `(int)` and a code back into a character with `(char)`: - -``` -int code = (int) 'A' -char letter = (char) 68 -``` - -After that, `code` is 65 and `letter` is `'D'`. Changing a value from one data type into another like this is called a **type cast**. - -Now you can calculate with the letter. So that the jump from Z back to A works, first subtract 65. Then A has position 0 and Z position 25, exactly as in way 1. Shift with `% 26` and add the 65 back at the end: - -::: grafik caesar-ascii.svg -At the top the capital letters with their ASCII codes. Below, the calculation for X with the key 3: the result is A. -::: - -::: übung aufgabe-caesar-ascii -titel: Caesar with ASCII -code: java -auftrag: Write a Java program that reads a word in capital letters and the key `k` and outputs the encrypted word. This time without an array: calculate with `(int)` and `(char)`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Go through the word with a for loop. You get the code of the character at position `i` with `int code = (int) word.charAt(i);`. -tipp: Calculate `code = (code - 65 + k) % 26 + 65;` and append the character: `secret = secret + (char) code;`. Before the loop write `String secret = "";`, after the loop `System.out.println(secret);`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Which way is better? - -**Way 1: array.** The idea is to search for the position in the array. -- Advantage: easy to understand, and your own alphabets work too, e.g. with Ä, Ö and Ü. -- Disadvantage: every letter has to be searched for. - -**Way 2: ASCII.** The idea is to calculate with the character code. -- Advantage: short and without searching, therefore faster. -- Disadvantage: you need to know the ASCII codes. - -Both programs give the same result. That is often how programming works: there are several correct solutions, and you choose the one that fits your task best. - -::: übung aufgabe-caesar-entschluesseln -titel: Decrypt Caesar -code: java -auftrag: Now turn the tables. Read a secret text in capital letters and the key `k` and output the plain text. For KDOOR and 3 that is `HALLO`. You decide which way to use. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Decrypting means shifting `k` places back instead of forward. So `+ k` becomes `- k`. -tipp: Careful with A: `0 - 3` gives -3, and in Java `%` does not turn that into a positive number. So add another 26: `code = (code - 65 - k + 26) % 26 + 65;` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Unfortunately, Caesar is not secure. There are only 25 keys, and you can try them all in a few minutes. It is even quicker with a trick: in English, E is the most common letter. If H is the most common letter in the secret text, the key was probably 3. On the internet there is also ROT13, which is Caesar with the key 13. Applying ROT13 twice gives you the plain text again, because 13 + 13 = 26. -::: diff --git a/web/lessons/en/aufgaben/collatz.md b/web/lessons/en/aufgaben/collatz.md deleted file mode 100644 index 0bf3000e..00000000 --- a/web/lessons/en/aufgaben/collatz.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -titel: Collatz sequence -nach: 11 -stufe: 2 ---- - -The Collatz sequence is a famous puzzle in mathematics. You start with a number and apply the same rule again and again: - -- If the number is **even**, you divide it by 2. -- If it is **odd**, you multiply it by 3 and add 1. - -So far, for every start number anyone has tried, the sequence ends at 1 at some point. Whether this is true for all numbers, nobody knows to this day. - -::: übung aufgabe-collatz -titel: Collatz sequence -auftrag: Ask for a start number. Apply the rule until the number is 1, and output the new number after every step. Count the steps as you go and output **Steps:** and the count at the end. - With the start number 6 the sequence is 3, 10, 5, 16, 8, 4, 2, 1. That is 8 steps. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 7 -test: 1 -> Steps: 0 -braucht: whileSchleife, verzweigung -tipp: You do not know how many steps there will be. That is why a while loop fits better than a for loop. -tipp: The while loop runs as long as `number != 1`. You check whether a number is even with `number % 2 == 0`. The output and `steps++` come after the branch, but still inside the loop. -lösung: aufgabe-collatz-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-collatz-java -titel: Collatz sequence in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 7 -test: 1 -> Steps: 0 -braucht: whileSchleife, verzweigung -tipp: A `while` loop runs as long as `number != 1`. Inside it, an `if` with `number % 2 == 0` decides. -tipp: In the body: halve it or `number = number * 3 + 1;`, then `System.out.println(number);` and `steps++;`. After the loop comes `"Steps: " + steps`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -This task could make you rich! The mathematician Paul Erdős offered 500 dollars for a proof. In 2021 the Japanese company Bakuage added quite a bit more: 120 million yen, about a million dollars at the time, for a proof or a counterexample. Nobody has collected the money yet. By the way, the puzzle was invented by the German mathematician Lothar Collatz in 1937. I am going to start calculating right now; that much money buys a lot of hay. -::: diff --git a/web/lessons/en/aufgaben/einheitenrechner.md b/web/lessons/en/aufgaben/einheitenrechner.md deleted file mode 100644 index 50aac46e..00000000 --- a/web/lessons/en/aufgaben/einheitenrechner.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -titel: Unit converter -nach: 35 -stufe: 2 ---- - -Build a window that converts lengths. At the top you type a length in metres, below it there are three buttons: - -- **in cm** shows the length in centimetres, 1.5 becomes "150.0 cm", -- **in mm** shows it in millimetres, 1.5 becomes "1500.0 mm", -- **in km** shows it in kilometres, 1500 becomes "1.5 km". - -The result is shown in a JLabel below the buttons. If the field doesn't contain a number, it says "Please enter a number", and the program doesn't crash. - -You can start like this: - -```java -import javax.swing.*; -import java.awt.*; - -public class Units { - public static void main(String[] args) { - JFrame window = new JFrame("Unit converter"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JTextField metres = new JTextField(); - JButton inCm = new JButton("in cm"); - JLabel result = new JLabel("?", SwingConstants.CENTER); - - inCm.addActionListener(e -> { - double m = Double.parseDouble(metres.getText()); - result.setText((m * 100) + " cm"); - }); - - window.add(metres); - window.add(inCm); - window.add(result); - window.setVisible(true); - } -} -``` - -**You are done when …** - -- 1.5 shows "150.0 cm" after **in cm** and "1500.0 mm" after **in mm**, -- 1500 shows "1.5 km" after **in km**, -- "abc" in the field shows "Please enter a number" and the window stays open. - -Extra: a fourth button **in feet** converts to the imperial unit. One foot is 0.3048 m. - -::: ludwig -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). For "abc" in the field, every listener needs a try/catch with `NumberFormatException`, just like the calculator. -::: diff --git a/web/lessons/en/aufgaben/fibonacci.md b/web/lessons/en/aufgaben/fibonacci.md deleted file mode 100644 index ba2b4c4f..00000000 --- a/web/lessons/en/aufgaben/fibonacci.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -titel: Fibonacci numbers -nach: 13 -stufe: 2 ---- - -With the Fibonacci numbers, every number is the sum of the two numbers before it. The sequence starts with 1 and 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -You find these numbers surprisingly often in nature, for example in the way sunflower seeds are arranged. - -::: ludwig -Do you remember **swapping** from lesson 5? There you needed a helper variable, because `a = b` overwrites the old value of `a`. It is the same here: before `number2` gets the new number, its old value must be saved in `number1`. So watch the **order**: first `number1 = number2`, then `number2 = number3`. You will need this moving on and swapping again later, for example when sorting in lesson 19. -::: - -::: angeberwissen -In 1202 Leonardo of Pisa, known as Fibonacci, set a puzzle about rabbits in his arithmetic book: from its second month on, every pair of rabbits has a new pair every month. How many pairs are there after a year? The answer is this sequence. Sadly, there were no goats in the book. -::: - -::: übung aufgabe-fibonacci -titel: Fibonacci numbers -auftrag: Ask how many Fibonacci numbers should be output. Then output exactly that many, each on its own line. You can assume that at least 2 is typed in. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Every new number is the sum of the two before it. So you always have to remember the last two numbers. -tipp: Keep the last two numbers in `number1` and `number2` and output both before the loop. The loop starts at `int i = 2`. Inside it you calculate `int number3 = number1 + number2`, output it and then move on: `number1 = number2` and `number2 = number3`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-fibonacci-java -titel: Fibonacci numbers in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Output the first two numbers before the loop. The `for` loop then starts at `int i = 2`. -tipp: In the body: `int number3 = number1 + number2;`, output it, then `number1 = number2;` and `number2 = number3;`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Why must `number1 = number2` come before `number2 = number3` in the loop? -- [ ] Because otherwise Java does not allow the assignments -- [x] Because otherwise the old value of number2 is overwritten before number1 gets it -- [ ] The order does not matter -> The other way round, number1 and number2 would have the same value afterwards. Just like swapping: first save, then overwrite. -::: diff --git a/web/lessons/en/aufgaben/gerade-ungerade.md b/web/lessons/en/aufgaben/gerade-ungerade.md deleted file mode 100644 index 7955d3e7..00000000 --- a/web/lessons/en/aufgaben/gerade-ungerade.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Even or odd -nach: 8 -stufe: 1 ---- - -A number is even if there is no remainder when you divide it by 2. You calculate the remainder with `%`: `8 % 2` is 0, `7 % 2` is 1. - -::: übung aufgabe-gerade -titel: Even or odd -auftrag: Ask for a whole number. Output whether it is even or odd. For 4 the output is **4 is even**, for 7 **7 is odd**. -test: 4 -> 4 is even -test: 7 -> 7 is odd -test: 0 -> 0 is even -test: 15 -> 15 is odd -braucht: verzweigung -tipp: A branch decides between even and odd. Even means: can be divided by 2 without a remainder. -tipp: The condition is `number % 2 == 0`. In the output you join the number and the text with `+`, e.g. `number + " is even"`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-gerade-java -titel: Even or odd in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 4 -> 4 is even -test: 7 -> 7 is odd -test: 0 -> 0 is even -test: 15 -> 15 is odd -braucht: verzweigung -tipp: Read a number with `int number = scanner.nextInt();`. Ask with `System.out.println(…)` before that. -tipp: `if (number % 2 == 0) { … } else { … }`. The output is `System.out.println(number + " is even");`. -lösung: aufgabe-gerade-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/mehrwertsteuer.md b/web/lessons/en/aufgaben/mehrwertsteuer.md deleted file mode 100644 index a615a34b..00000000 --- a/web/lessons/en/aufgaben/mehrwertsteuer.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -titel: Sales tax -nach: 6 -stufe: 1 ---- - -In Germany, 19 % **VAT** (value added tax, a kind of sales tax) is added to almost everything you buy. The shop calculates with the net price (without tax) and adds the tax on top. That gives the gross price you pay at the till. - -An example: you buy a folding phone that opens up into a tablet. The shop's price tag says 1000 euros net. On top of that come 19 %, so 190 euros. At the till you pay 1190 euros. The shop is not allowed to keep the 190 euros: it passes them on to the tax office. - -::: grafik aufgabe-mehrwertsteuer.svg -Net price plus VAT gives the gross price. The VAT goes to the tax office. -::: - -::: angeberwissen -Baah, the shop bought the phone itself, of course: from the manufacturer for 800 euros plus 152 euros VAT. These 152 euros are called **input tax**, and the shop may subtract them from the 190 euros. So it only transfers 38 euros to the tax office. In the end only one person pays the whole tax: the customer at the till. That is you. I pay no tax at all, by the way. I just eat grass. -::: - -The tax rate never changes while you calculate. That is why you create it as a **constant**: a variable whose value stays the same once it is set. You write `const` in front of it, for example `const double VAT = 0.19`. Names of constants are written in capital letters so you recognise them straight away. In Java code this becomes `final double VAT = 0.19;`. - -If the tax rate ever changes, you only have to change this one line. - -::: übung aufgabe-mehrwertsteuer -titel: Sales tax -auftrag: Create the constant `VAT` with the value 0.19. Then ask for the net price. First output the tax and then the gross price, only the number each time. - For a net price of 100 the outputs are **19** and **119**. -test: 100 -> 19 | 119 -test: 50 -> 9.5 | 59.5 -test: 0 -> 0 | 0 -tipp: The tax is 19 percent of the net price. The gross price is what you pay in the end. -tipp: The tax is `netPrice * VAT`. You get the gross price by adding the tax to the net price. Both values are decimal numbers, so `double`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Why do you create the tax rate as a constant? -- [ ] Because constants are calculated faster -- [x] Because its value should not change in the program -- [ ] Because decimal numbers can only be stored as constants -> A constant keeps its value. If the tax rate ever changes, you only change the one line with `const`. -::: diff --git a/web/lessons/en/aufgaben/notendurchschnitt.md b/web/lessons/en/aufgaben/notendurchschnitt.md deleted file mode 100644 index aae55cc0..00000000 --- a/web/lessons/en/aufgaben/notendurchschnitt.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Grade average -nach: 13 -stufe: 2 ---- - -How did you do lately? The average is the sum of all grades divided by how many there are. - -::: übung aufgabe-durchschnitt -titel: Grade average -auftrag: First ask how many grades will be typed in. Then read in exactly that many grades and output the average, only the number. - For the grades 1 and 2 the average is **1.5**. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: You need the sum of all grades and how many there are. The average is the sum divided by the count. -tipp: The for loop runs from `int i = 0` while `i < count`. Inside it you read in a grade and add it to the sum. You store the average as a decimal number: `double average = sum * 1.0 / count`. The `* 1.0` makes sure that Java does not do whole-number division either. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-durchschnitt-java -titel: Grade average in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: `for (int i = 0; i < count; i++)` reads exactly as many grades as were given before. -tipp: Divide with `double average = sum * 1.0 / count;`. Without `* 1.0`, Java divides two `int` values as whole numbers, and 1.5 becomes 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/pin-pruefen.md b/web/lessons/en/aufgaben/pin-pruefen.md deleted file mode 100644 index 5c73caa5..00000000 --- a/web/lessons/en/aufgaben/pin-pruefen.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -titel: Check the PIN -nach: 12 -stufe: 2 ---- - -At a cash machine you have three tries for your PIN. If you type a wrong PIN three times, the card is blocked. - -::: angeberwissen -John Shepherd-Barron, who set up the first cash machine in London in 1967, actually wanted six-digit PINs. But his wife said she could remember four digits at most. That is why your PIN has four digits today. -::: - -::: übung aufgabe-pin -titel: Check the PIN -auftrag: The right PIN is 1234. Keep asking for the PIN until it is right or three tries have been used. Then output **PIN correct** or **Card blocked**. -test: 1234 -> PIN correct -test: 1111, 1234 -> PIN correct -test: 1, 2, 1234 -> PIN correct -test: 1, 2, 3 -> Card blocked -braucht: doUntilSchleife, verzweigung -tipp: The loop ends in two cases: when the PIN is right or when three tries have been used. Count the tries. -tipp: The PIN is asked for at least once, so a do-while loop fits. Create `int pin = 0` and `int attempts = 0` before it. The loop repeats as long as `pin != 1234 && attempts < 3`. After the loop, a branch decides what is output. -lösung: aufgabe-pin-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-pin-java -titel: Check the PIN in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 1234 -> PIN correct -test: 1111, 1234 -> PIN correct -test: 1, 2, 1234 -> PIN correct -test: 1, 2, 3 -> Card blocked -braucht: doUntilSchleife, verzweigung -tipp: `do { … } while (…);` asks at least once. There is a semicolon after `while (…)`. -tipp: Create `int pin = 0;` and `int attempts = 0;` first. In the loop: `int entered = scanner.nextInt();`, `pin = entered;` and `attempts++;`. The condition is `pin != 1234 && attempts < 3`. -lösung: aufgabe-pin-java-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/schiffe-versenken.md b/web/lessons/en/aufgaben/schiffe-versenken.md deleted file mode 100644 index 6dd3a355..00000000 --- a/web/lessons/en/aufgaben/schiffe-versenken.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Battleship -nach: 16 -stufe: 2 ---- - -In the game Battleship the sea is a grid. The columns are called A to E, the rows 1 to 5. In the start diagram the sea is the 2D array `sea`: `true` means that part of a ship is on this square. - -::: grafik schiffe-versenken.svg -Grey means `true`: there is a ship here. The small grey numbers are the indices in the array; they start at 0. In the game, on the other hand, the squares are called A to E and 1 to 5. -::: - -::: übung aufgabe-schiffe -titel: Battleship -auftrag: Output all squares that have a ship on them, each square on its own line, e.g. **B1**. Go through the sea row by row with two nested loops. - For the start diagram, **B1**, **C1**, **D1**, **E3**, **E4** and **A5** appear. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Go through the sea with two nested loops, just like the cinema. Only output the squares that hold `true`. -tipp: The outer loop counts `row`, the inner one `column`, both from 0. If there is a ship on `sea[row][column]`, you output the letter from `columns[column]` and the row number: `output: columns[column] + (row + 1)`. You need the `+ 1` because the game starts counting at 1. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-schiffe-java -titel: Battleship in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Two `for` loops inside each other: the row outside, the column inside, both up to `.length`. -tipp: `if (sea[row][column]) { System.out.println(columns[column] + (row + 1)); }`. The brackets around `row + 1` make Java calculate first. -lösung: aufgabe-schiffe-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/speisekarte.md b/web/lessons/en/aufgaben/speisekarte.md deleted file mode 100644 index 6ef39a05..00000000 --- a/web/lessons/en/aufgaben/speisekarte.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Menu -nach: 10 -stufe: 1 ---- - -In the school canteen you order by number. The program tells you which dish belongs to the number. - -::: übung aufgabe-speisekarte -titel: Menu -auftrag: Ask for a number. For 1 output **Greek salad**, for 2 **Rice bowl** and for 3 **Falafel wrap**. For any other number output **Please choose a valid number**. -test: 1 -> Greek salad -test: 2 -> Rice bowl -test: 3 -> Falafel wrap -test: 7 -> Please choose a valid number -braucht: fallauswahl -tipp: A switch fits several fixed numbers. What happens with all the other numbers? -tipp: Read in the number with `input: int pick "Which dish?"`. The switch checks `pick`, and the cases are called `1`, `2` and `3`. All other numbers end up in the case `default`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-speisekarte-java -titel: Menu in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 1 -> Greek salad -test: 2 -> Rice bowl -test: 3 -> Falafel wrap -test: 7 -> Please choose a valid number -braucht: fallauswahl -tipp: In Java this is a switch: `switch (pick) { … }`. -tipp: Every case looks like this: `case 1 -> { System.out.println("…"); }`. All other numbers go to `default -> { … }`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/summe-bilden.md b/web/lessons/en/aufgaben/summe-bilden.md deleted file mode 100644 index 31ec656d..00000000 --- a/web/lessons/en/aufgaben/summe-bilden.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Add up -nach: 2 -stufe: 1 ---- - -Set two numbers, add them and output the result. - -::: übung aufgabe-summe -titel: Add up -auftrag: Set `number1` to the value 4 and `number2` to the value 3. Calculate the sum in a third variable and output only the sum. -test: - -> 7 -tipp: You need three variables: two for the numbers and one for the result. -tipp: You set a variable with a statement such as `int number1 = 4`. For the output you write the name of the variable without quotation marks. -lösung: aufgabe-summe-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/tafeldienst.md b/web/lessons/en/aufgaben/tafeldienst.md deleted file mode 100644 index 2aab5429..00000000 --- a/web/lessons/en/aufgaben/tafeldienst.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Draw for board duty -nach: 15 -stufe: 2 ---- - -Who is on board duty this week, cleaning the board? Five names go into an array, then a number is drawn. - -::: übung aufgabe-tafeldienst -titel: Draw for board duty -auftrag: Read five names into a String array. Then ask for a draw number from 1 to 5 and output **Board duty:** and the matching name. - Draw number 1 belongs to the first name, 5 to the last one. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Board duty: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Board duty: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Board duty: Pia -braucht: forSchleife -tipp: First a loop reads in the five names. After that you do not need a loop any more, just one look into a slot. -tipp: You create the array with `String[] names = new String[5]`. Careful: the draw number starts at 1, the index at 0. So the name is in `names[pick - 1]`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-tafeldienst-java -titel: Draw for board duty in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Board duty: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Board duty: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Board duty: Pia -braucht: forSchleife -tipp: Create an array for five names with `String[] names = new String[5];`. -tipp: In the loop: `names[i] = scanner.next();`. Ticket number 1 belongs to slot 0, so you output `names[pick - 1]`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> With `int pick = (int) (Math.random() * 5) + 1` you can also let chance draw the number. But for the automatic check we need a fixed input. diff --git a/web/lessons/en/aufgaben/taschenrechner.md b/web/lessons/en/aufgaben/taschenrechner.md deleted file mode 100644 index a5606940..00000000 --- a/web/lessons/en/aufgaben/taschenrechner.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Calculator -nach: 10 -stufe: 2 ---- - -A small calculator: type in two numbers and an operator, and the result appears. - -::: übung aufgabe-rechner -titel: Calculator -auftrag: Ask for two whole numbers and then for an operator as text: `+`, `-` or `*`. Output the result. For any other sign output **Unknown operator**. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unknown operator -braucht: fallauswahl -tipp: Read in all three values. A switch on the operator then decides what is calculated. -tipp: You read in the operator with `input: String operator "Operator"`. The switch checks `operator`, the cases are called `"+"`, `"-"` and `"*"`, and the last case is `default`. In every case, first calculate in a variable and then output it, otherwise `+` only joins the numbers together. -lösung: aufgabe-rechner-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-rechner-java -titel: Calculator in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unknown operator -braucht: fallauswahl -tipp: The operator is a text. Read it with `String operator = scanner.next();`. -tipp: `switch (operator) { case "+" -> { … } … default -> { … } }`. With texts, the cases are in quotation marks. -lösung: aufgabe-rechner-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/verleih-menue.md b/web/lessons/en/aufgaben/verleih-menue.md deleted file mode 100644 index b8676fc9..00000000 --- a/web/lessons/en/aufgaben/verleih-menue.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Rental menu -nach: 18 -stufe: 2 ---- - -At the rental machine you can book several vehicles one after another. The menu keeps coming back until you choose **0** to quit. - -::: übung aufgabe-verleih -titel: Rental menu -auftrag: Keep asking for a choice: 1 = e-bike, 2 = e-scooter, 0 = quit. For 1 output **E-bike booked**, for 2 **E-scooter booked**, and for any other number **Invalid choice**. - For 0 the program outputs **Bye** and leaves the loop with a Break. -test: 0 -> Bye -test: 1, 2, 0 -> E-bike booked | E-scooter booked | Bye -test: 2, 5, 1, 0 -> E-scooter booked | Invalid choice | E-bike booked | Bye -braucht: endlosschleife, aussprung -tipp: The menu runs again and again until someone chooses 0. An endless loop with a Break is good for that. -tipp: Use an endless loop. Inside it, first comes the input `input: int choice "1 = e-bike, 2 = e-scooter, 0 = quit"`, then a switch on `choice` with the cases `0`, `1`, `2` and `default`. The case `0` holds the output and, below it, the Break. -lösung: aufgabe-verleih-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-verleih-java -titel: Rental menu in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 0 -> Bye -test: 1, 2, 0 -> E-bike booked | E-scooter booked | Bye -test: 2, 5, 1, 0 -> E-scooter booked | Invalid choice | E-bike booked | Bye -braucht: endlosschleife, aussprung -tipp: `while (true) { … }` runs forever until a `break;` comes. -tipp: In Java, use `if`/`else if` here instead of `switch`: a `break;` inside a `switch` only leaves the `switch`, not the loop. `if (choice == 0) { System.out.println("Bye"); break; } else if (choice == 1) { … }`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/vokabeltrainer.md b/web/lessons/en/aufgaben/vokabeltrainer.md deleted file mode 100644 index a5ae4cc6..00000000 --- a/web/lessons/en/aufgaben/vokabeltrainer.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Vocabulary trainer -nach: 27 -stufe: 3 ---- - -Ludwig is learning German. His words are in a CSV file, English on the left, German on the right: - -::: datei words.csv -English;German -goat;Ziege -hay;Heu -meadow;Wiese -::: - -Because only the file knows the words, you can simply add new lines later without changing the program. - -::: angeberwissen -Sebastian Leitner made the flashcard box with several compartments popular in 1972: cards you know move one compartment further and come up less often, wrong ones go back to the first compartment. Many vocabulary apps still work on this principle today. -::: - -::: übung aufgabe-vokabeln -titel: Vocabulary trainer -code: java -auftrag: Ask for every word in the file: output **What is goat in German?** and read the answer with `scanner.next()`. If it is right, output **Right!**, otherwise for example **Wrong, it is Ziege.** - At the end, show how many were right, for example **2 of 3 right**. -start: aufgabe-vokabeln-start.visustruct -test: Ziege, Heu, Wiese -> What is goat in German? | Right! | What is hay in German? | Right! | What is meadow in German? | Right! | 3 of 3 right -test: Kuh, Heu, Wiese -> What is goat in German? | Wrong, it is Ziege. | What is hay in German? | Right! | What is meadow in German? | Right! | 2 of 3 right -test: Ziege, hay, wiese -> What is goat in German? | Right! | What is hay in German? | Wrong, it is Heu. | What is meadow in German? | Wrong, it is Wiese. | 1 of 3 right -braucht: whileSchleife, verzweigung -tipp: Skip the header line. In the loop, split every line with `split(";")`: `parts[0]` is the English word, `parts[1]` the German one. -tipp: Compare texts with `answer.equals(parts[1])`, not with `==`. Count along with `int right = 0;` and `int count = 0;` before the loop. -lösung: aufgabe-vokabeln-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/volljaehrig.md b/web/lessons/en/aufgaben/volljaehrig.md deleted file mode 100644 index be9ae873..00000000 --- a/web/lessons/en/aufgaben/volljaehrig.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Adult? -nach: 8 -stufe: 1 ---- - -At the cinema, for a driving licence or for voting: it often matters whether someone is already 18. - -::: übung aufgabe-volljaehrig -titel: Adult? -auftrag: Ask for the age. If the person is 18 or older, output **adult**, otherwise **minor**. -test: 18 -> adult -test: 35 -> adult -test: 17 -> minor -test: 6 -> minor -braucht: verzweigung -tipp: There are two possible outputs, so you need a branch. -tipp: Read in the age with `input: int age "How old are you?"`. The condition of the branch is `age >= 18`. Make sure that 18 already counts as adult. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-volljaehrig-java -titel: Adult? in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 18 -> adult -test: 35 -> adult -test: 17 -> minor -test: 6 -> minor -braucht: verzweigung -tipp: Read the age with `int age = scanner.nextInt();`. -tipp: `if (age >= 18) { … } else { … }`, with a `System.out.println(…)` in each branch. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: diff --git a/web/lessons/en/aufgaben/wuerfel-mvc.md b/web/lessons/en/aufgaben/wuerfel-mvc.md deleted file mode 100644 index 78d521c6..00000000 --- a/web/lessons/en/aufgaben/wuerfel-mvc.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -titel: Rolling dice with MVC -nach: 40 -stufe: 3 ---- - -Build a dice game with Model View Controller. A button **Roll** rolls the dice, and two views show the result. - -The model is already finished. It rolls and counts how often it was rolled and how many sixes came up: - -```java -class Dice { - int pips = 1; - int rolls = 0; - int sixes = 0; - - void roll() { - pips = (int) (Math.random() * 6) + 1; - rolls++; - if (pips == 6) { - sixes++; - } - } -} -``` - -Your task: - -1. Test the model without a window first: roll ten times in `main` and then output `rolls` and `sixes`. -2. Write the view `PipsView`. It shows the number rolled in a big JLabel, for example "4". -3. Write the view `StatsView`. It shows for example "Sixes: 2 of 10 rolls". -4. The controller is the listener at the button: it lets the model roll and hands the new values to both views. - -**You are done when …** - -- after ten rolls in the console `rolls` is 10 and `sixes` is between 0 and 10, -- every click on **Roll** shows a new number from 1 to 6 in the `PipsView`, -- the `StatsView` counts one more roll after every click and counts the sixes correctly, -- there is no `JLabel` in the model and no `Math.random` in the views. - -Extra: a button **New game** resets everything. The method for that belongs in the model. - -::: ludwig -Remember the rule from lesson 40: there isn't a single `JLabel` in the model. If you change the font size when rolling, that belongs in the view. -::: diff --git a/web/lessons/en/aufgaben/zahl-verdoppeln.md b/web/lessons/en/aufgaben/zahl-verdoppeln.md deleted file mode 100644 index e5dc9e5b..00000000 --- a/web/lessons/en/aufgaben/zahl-verdoppeln.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Double a number -nach: 2 -stufe: 1 ---- - -A variable can change its value. Here a number is doubled. - -::: übung aufgabe-verdoppeln -titel: Double a number -auftrag: Set the variable `number` to the value 4. Double it in a second statement without creating a new variable. Then output **Doubled number: 8**, where the 8 comes from the variable. -test: - -> Doubled number: 8 -tipp: Doubling means: times 2. You store the result in the same variable again. -tipp: `number = number * 2` calculates with the old value and stores the result in `number` again. You join text and a variable with `+`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: diff --git a/web/lessons/en/begriffe.md b/web/lessons/en/begriffe.md deleted file mode 100644 index 5f3cfa84..00000000 --- a/web/lessons/en/begriffe.md +++ /dev/null @@ -1,266 +0,0 @@ -Technical terms for the learning area. When a term appears in a step, it is marked there once and shows its explanation -on hover or tap. If it is in bold in that step, it is being explained right there and is not marked. -`also:` lists other forms that are recognised too. Upper and lower case matter. - -### Algorithm -also: algorithm, algorithms, Algorithms -Exact step-by-step instructions for solving a task. Every step is clear. - -### Structure chart -also: structure chart, structure charts, Structure charts, Nassi-Shneiderman diagram, Nassi-Shneiderman diagrams -An algorithm drawn as a picture made of boxes. You read it from top to bottom. It is also called a Nassi-Shneiderman diagram. - -### Flowchart -also: flowchart, flowcharts, Flowcharts -An algorithm drawn as symbols joined by arrows (DIN 66001 / ISO 5807). A diamond is a decision. - -### Pseudocode -also: pseudocode -An algorithm written as text in plain language with fixed words such as IF and WHILE. The indentation shows what belongs together. - -### Statement -also: statement, statements, Statements -A single step in the structure chart, for example a calculation, an input or an output. - -### Sequence -also: sequence, sequences, Sequences -Steps that run one after another from top to bottom, each exactly once. - -### Branch -also: branch, branches, Branches, conditional statement, conditional statements -A block that uses a condition to decide which of two paths runs. It is also called a conditional statement. - -### Path -also: path, paths -One of the two ways through a branch: left for true, right for false. - -### Loop -also: loop, loops, Loops, iteration, repetition -A block that repeats steps as long as a condition holds. - -### Loop body -also: loop body -The inside of a loop, that is, the steps that are repeated. - -### Pass -also: passes -Running the inside of a loop once. - -### Condition -also: condition, conditions, Conditions -A question that can only be answered with yes (true) or no (false), for example age >= 18. - -### Placeholder -also: placeholder, placeholders -An empty box with the sign ø. If you drag an element onto it, the element replaces it. - -### Object Inspector -The area below the diagram. Here you edit the text and settings of the box you just clicked. - -### Simulation -also: simulation -Runs your structure chart in the editor, step by step if you like. That way you see what happens. - -### Variable -also: variable, variables, Variables -A labelled box for a value. It has a name, a data type and a value. - -### camelCase -also: CamelCase -A way of writing names made of several words: written together, every further word starts with a capital, e.g. `priceWithTax`. - -### case-sensitive -Java tells upper and lower case apart: `number`, `Number` and `NUMBER` are three different names. `system` instead of `System` is an error. - -### Constant -also: constant, constants, Constants -A variable whose value stays the same once it is set. You write `const` in front of it, e.g. `const double VAT = 0.19`. - -### Data type -also: data type, data types, Data types -Says what kind of value fits into a variable: int (whole number), double (decimal number), String (text), boolean (true or false) or char (a single character). - -### Assignment -also: assignment, assignments, Assignments -The sign =. It works out the right-hand side and stores the result in the variable on the left. - -### Temporary variable -also: temporary variable, temporary variables, helper variable -An extra variable that keeps a value for a short time, for example when swapping. - -### IPO model -also: input-process-output -Input, process, output: first ask for values, then calculate, then show the result. - -### Problem analysis -also: problem analysis -You read a task carefully and work out what is given, what you are looking for and how to get from one to the other. - -### Comparison operator -also: comparison operator, comparison operators, Comparison operators -A sign that compares two values, for example == or <. The result is true or false. - -### Switch -also: switch, multiple selection -A block that runs exactly one of several cases, depending on the value of a variable. It is also called multiple selection. - -### Pre-test loop -also: pre-test loop, pre-test loops, head-controlled -The loop checks its condition before every pass. If the condition is false right at the start, the loop does not run even once. - -### Post-test loop -also: post-test loop, post-test loops, foot-controlled -The loop checks its condition only after every pass. So it always runs at least once. - -### Counter variable -also: counter variable, counter variables, counter -A variable that counts along in a loop, usually i. - -### Endless loop -also: endless loop, endless loops, infinite loop, infinite loops -A loop whose condition never becomes false. It never stops by itself. - -### Flag -also: flag, flags -A boolean variable that starts with false and is set to true as soon as something has happened. - -### State machine -also: state machine, state machines, state variable, state variables -A program that is always in exactly one state and changes to the next state by fixed rules, e.g. a traffic light. - -### Array -also: array, arrays, Arrays -A row of slots with one shared name and the same data type. - -### Index -also: index, indices, indexes -The number of a slot in an array. Counting starts at 0. - -### char -The data type for a single character. It goes in single quotation marks, e.g. 'C'. "C" with double quotation marks, on the other hand, is a String. - -### Character code -also: character code, character codes, ASCII code, ASCII table -The number behind a character. In the ASCII table A has the code 65, B 66 and so on. In Java (int) turns a character into its code and (char) turns a code into the character. - -### String -also: strings, Strings -A text, that is, a row of characters. In Java the data type is called String. The characters are counted from 0. - -### 2D array -also: 2D arrays, two-dimensional array, two-dimensional arrays -An array with rows and columns, like a table. A slot needs two indices, e.g. `seats[1][2]`. - -### Subprogram -also: subprogram, subprograms, Subprograms, method, methods, Methods, procedure, procedures, function, functions, Functions -A structure chart of its own for part of a task. It runs when it is called. In Java it is called a method; depending on its kind also a procedure or a function. - -### Parameter -also: parameter, parameters, Parameters -A variable in the header line of a subprogram. When it is called, it gets the value from the parentheses. - -### Overloading -also: overloaded, overload -Several methods with the same name but different parameters, e.g. `mbot.forward(30)` and `mbot.forward(2, "rotations")`. When calling, Java picks the right one by the number and type of the parameters. - -### Spaghetti code -also: spaghetti code -A program that jumps around so wildly that you can hardly follow what happens. Structure charts prevent that. - -### Call by value -also: call by value -A subprogram gets a copy of the value. If it changes its parameter, the caller's variable stays the same. - -### Call by reference -also: call by reference -A subprogram gets a reference to the same data, e.g. to an array. If it changes a slot, the caller sees the change too. - -### Return value -also: return value, return values, Return values -The result that a subprogram hands back to the call with return. - -### Break -A block that leaves the loop it is in straight away. Then the program continues below the loop. - -### Syntax error -also: syntax error, syntax errors, Syntax errors -A mistake in how the code is written, for example a missing `;`. The program does not start at all. - -### Runtime error -also: runtime error, runtime errors, Runtime errors -An error that only happens while the program runs, for example dividing by 0. The program stops at that point. - -### Logic error -also: logic error, logic errors, Logic errors -The program runs without any message but gives a wrong result. You find it with a desk check. - -### Desk check -also: desk check, desk checks, Desk checks -You run a structure chart on paper and write down the values of the variables after every step. - -### Linear search -also: linear search -A way of searching that compares all values one after another with the value you are looking for. It also works with unsorted values. - -### Binary search -also: binary search -A way of searching sorted values. It looks at the middle and keeps searching only in the matching half. - -### Bubble sort -also: bubble sort, Bubblesort -A simple way of sorting. It always compares two neighbours and swaps them if they are the wrong way round. - -### Event -also: event, events, Events -Something a person does in a window program, for example clicking a button. The program reacts to it with a listener (ActionListener). - -### Interface -also: Interfaces -A kind of contract in Java: it states which methods a class must have. `ActionListener` is an interface with the method `actionPerformed`. - -### Exception -also: exception, exceptions, Exceptions -An error that only happens while the program runs, for example an index outside the array. If no `catch` catches it, the program stops. - -### Scanner -A ready-made Java building block that reads input from the keyboard, for example a number with `scanner.nextInt()`. It needs `import java.util.Scanner;` at the very top. - -### comment -auch: comments, Comment, Comments -A note in the code for people. Java skips it when it runs. `//` lasts until the end of the line, `/* … */` can span several lines. - -### import -A line at the very top of Java code that fetches a ready-made building block from a package, for example `import java.util.Scanner;`. Without it, Java doesn't know the name. - -### source code -auch: Source code -The program text that people write and read, for example in a file `TimesTable.java`. - -### compiler -auch: compilers, Compiler -A program that translates source code. In Java it is called `javac` and turns a `.java` file into bytecode in a `.class` file. - -### bytecode -auch: Bytecode -The translated form of a Java program in a `.class` file. It is read by the JVM, not by people. - -### JVM -auch: Java Virtual Machine, virtual machine -The Java Virtual Machine runs bytecode. It is a program that works like a small computer made of software on Windows, macOS or Linux. That is why Java is platform independent. - -### JRE -Java Runtime Environment: the JVM and ready-made building blocks. You can run Java programs with it, but not compile them. - -### JDK -Java Development Kit: the JRE plus the compiler `javac` and other tools. You need it to program. - -### platform independent -A program runs without changes on different operating systems. In Java the JVM makes this possible: “Write once, run anywhere”. - -### machine code -auch: Machine code -Instructions as numbers that the processor runs directly. Every kind of processor has its own machine code. - -### JIT compiler -auch: JIT -A part of the JVM that translates often-used bytecode into machine code while the program runs (“just in time”). This makes Java faster. diff --git a/web/lessons/en/nachschlagen.md b/web/lessons/en/nachschlagen.md deleted file mode 100644 index 8711c1a9..00000000 --- a/web/lessons/en/nachschlagen.md +++ /dev/null @@ -1,169 +0,0 @@ -## Basics - -### Statement - -A single step. You use it to create variables or to calculate. - -``` -int number = 5 -double price = 2.5 -String name = "Ada" -number = number + 1 -``` - -### Data types - -The data type comes before the name of the variable. - -| Data type | Meaning | Example | -|---|---|---| -| `int` | whole number | `int count = 3` | -| `double` | decimal number (with a point) | `double price = 2.5` | -| `String` | text in double quotation marks | `String name = "Ada"` | -| `boolean` | truth value | `boolean done = false` | -| `char` | a single character in single quotation marks | `char grade = 'C'` | - -`'C'` is a `char`, `"C"` is a String. You get a character from a text with `charAt`: `char first = name.charAt(0)`. Every character has a character code: `(int) 'A'` is 65, `(char) 66` is `'B'`. - -### Input - -Asks for a value while the chart runs and stores it in a variable. The text in quotation marks is the question. - -``` -input: int age "How old are you?" -input: double price "Enter a price" -input: String name "What is your name?" -``` - -### Output - -Shows a text or a value. Texts go in quotation marks; you join text and variables with `+`. - -``` -output: "Hello" -output: sum -output: "You are " + age + " years old." -``` - -> In an output, `+` always joins the parts together: with 3 and 4, `output: a + b` shows 34. So calculate in a variable first, e.g. `int sum = a + b`, and then output `sum`. - -## Branches - -### Branch - -Checks a condition. If it is true, the left path (true) runs, otherwise the right one (false). - -``` -age >= 18 -number % 2 == 0 -grade < 1 || grade > 6 -``` - -### Switch - -Picks one of several cases, depending on the value of a variable. If no case fits, `default` applies. You add cases on the right with **Add Case**. - -## Loops - -### While loop - -Repeats its inside **as long as** the condition is true. The condition is checked before every pass. - -``` -number != 1 -i < 10 -``` - -### Do-while loop - -Runs its inside **at least once** and checks the condition only afterwards. - -### For loop - -Counts a variable from a start value up to a limit. The three fields are Init, Condition and Step. - -``` -int i = 0 -i < 5 -i++ -``` - -### Endless loop - -Repeats its inside without a condition. A **Break** inside it lets you leave it. - -### Break - -Leaves the loop around it (`break`). - -## Subprograms - -### Call - -Stands for a subprogram, e.g. `calculateSum()`. With parameters and a return value, the subprogram has a header line as its title: - -``` -int square(int x) header line of the subprogram -return x * x hands back the result -int q = square(5) call, q gets 25 -showSum(3, 4) call of a void subprogram -``` - -## Calculating and comparing - -### Operators and comparisons - -- `+ - * /` the four basic operations -- `%` remainder of a division (`7 % 2` is 1) -- `==` equal, `!=` not equal -- `<` `<=` `>` `>=` less than, less than or equal, greater than, greater than or equal -- `&&` and, `||` or - -## Arrays - -### Arrays - -Several values under one name. Counting starts at 0. - -``` -int[] numbers = {3, 7, 1} -output: numbers[0] -int[] empty = new int[5] -``` - -A **2D array** is a table with rows and columns: - -``` -boolean[][] seats = new boolean[3][5] -seats[1][4] = true -output: seats.length + " rows, " + seats[0].length + " seats" -``` - -## Java - -### From code to program - -The compiler `javac` translates `File.java` into bytecode (`File.class`). The JVM translates the bytecode into machine code while it runs, the same way on Windows, macOS and Linux. C++, on the other hand, becomes machine code directly and has to be compiled again for every system. - -| | contains | for | -|---|---|---| -| JRE | JVM and libraries | running | -| JDK | JRE, compiler `javac`, tools | programming | - -### Comments - -Notes in the code. Java skips them, the structure chart stays the same. - -```java -/* - * Task: add the numbers from 1 to 10 - * (multi-line comment) - */ -// Process (single-line comment) -int sum = 0; -for (int i = 1; i <= 10; i++) { - sum = sum + i; // comment after the code -} // end for -``` - -Good for: the task, dividing the code into input, process and output, an explanation, the end of a block after `}`, the expected output and notes for next time. diff --git a/web/lessons/en/visubot/00-struktogramm-und-editor.md b/web/lessons/en/visubot/00-struktogramm-und-editor.md deleted file mode 100644 index 74554afb..00000000 --- a/web/lessons/en/visubot/00-struktogramm-und-editor.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -titel: Structure chart and editor -bereich: Getting started -kurz: What a structure chart is, how to use the editor and how to save your programs. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an algorithm and a structure chart are -- recognise the three building blocks sequence, branch and repetition -- insert, move and delete elements in the editor -- save a structure chart and open it again -::: - -## A plan for the robot - -A robot does not think for itself. It does exactly what is in its program, no more and no less. So before you program it, you need an exact plan. Such an exact step-by-step instruction is called an **algorithm**. - -A **structure chart** draws the plan as boxes. You read it from top to bottom. Each box is one step. It was invented in 1973 by Isaac Nassi and Ben Shneiderman, which is why it is also called a **Nassi-Shneiderman diagram**. - -::: diagramm vb-l00-sequenz-loesung.visustruct -Drive forward, turn right, drive forward: three boxes, from top to bottom. -::: - -::: angeberwissen -Ada Lovelace wrote the first program for a machine back in 1843, for a calculating machine that was never finished. Even then she guessed that such machines would one day do more than calculate, for example make music. Your mBot2 can do both. -::: - -## Three building blocks - -Every program, including one for a robot, is made of only three building blocks: - -| Building block | What it does | Example with the mBot2 | -|---|---|---| -| **Sequence** | steps one after the other | drive forward, turn, drive forward | -| **Branch** | one thing or the other, depending on a condition | Is the wall close? Then turn, otherwise drive forward. | -| **Repetition** | steps several times | do the same thing four times | - -In a structure chart you see the building blocks straight away: the branch has a triangle and two columns: **true** on the left, **false** on the right. The repetition (loop) wraps around its boxes like a tilted L. - -::: diagramm vb-l00-bausteine-loesung.visustruct -All three building blocks: first a box, then a loop, inside it a branch. -::: - -## The editor - -The [structure chart editor](#zeige-editor) has several areas. Move the mouse over a blue word or tap it, and the area lights up: - -- **On the left the [Elements](#zeige-elemente):** all building blocks to drag in. The group **mBot2 commands** has the commands for the mBot2 such as **drive forward** and **turn right**. -- **In the middle the [structure chart](#zeige-struktogramm):** a new diagram only contains an empty placeholder, shown as **ø**. -- **Below it the [Object Inspector](#zeige-objektinspektor):** here you change the text of the selected box, for example `mbot.forward(30)` instead of `mbot.forward()`. You can drag the line with the grip above it up or down. -- **Above it the [tools](#zeige-werkzeuge):** undo, copy, delete, export and, on the right, the **simulation**. - -This is how you build: - -- **Insert:** drag an element from the Elements onto the structure chart. On the placeholder **ø** it replaces it. On the top half of a box it goes before it, on the bottom half after it. A blue line shows you where it lands. -- **Change text:** double-click a box, or double-tap it on a tablet. Then you type right in the structure chart, **Enter** takes it. -- **Move:** drag a box to another place. -- **Delete:** click the box and press Delete, or click the [trash can](#zeige-loeschen). -- **Undo:** Ctrl+Z, on a Mac ⌘Z. - -> 🧩 **Prefer blocks like in Scratch?** Click [Blocks](#zeige-bloecke) in the tools. Then you see the same program as blocks that snap together. You type numbers right into the white fields and drag sensors in. **Structure chart** takes you back. It stays the same program, so you can switch at any time. - -::: übung vb-erster-plan -titel: Round the corner -auftrag: Build a structure chart: the mBot2 drives forward, turns right, drives forward, turns left and drives forward once more. Then start the simulation and watch. -fahrt: forward, turnRight, forward, turnLeft, forward -braucht: anweisung -tipp: Drag **drive forward** onto the placeholder ø. Drag each next command onto the bottom half of the last box. -tipp: From top to bottom: **drive forward**, **turn right**, **drive forward**, **turn left**, **drive forward**. -lösung: vb-erster-plan-loesung.visustruct -::: - -> When you are done, click **Check** in the exercise. VisuStruct then runs your structure chart and checks whether the mBot2 drives exactly as required. Later it often checks your program in several worlds, for example once with a wall and once without. If it doesn't work, the **hints** help you. - -## Saving and opening - -- **[Save](#zeige-speichern)** (Ctrl+S, on a Mac ⌘S): the structure chart is saved as a file ending in `.visustruct`. The [title](#zeige-titel) above the structure chart suggests the file name. -- **[Open](#zeige-oeffnen)** (Ctrl+O, on a Mac ⌘O): load a saved file again. - -In the column [📁 Files](#zeige-dateien) you set up a folder once, your **workspace**. All your programs and your learning progress go there. You can take the folder with you and carry on at home. - -> 📘 **More without the robot:** VisuStruct has the lessons ["What is a structure chart?"](?visustruct&lektion=01-was-ist-ein-struktogramm), ["The editor"](?visustruct&lektion=02-der-editor) and ["Save, open, export"](?visustruct&lektion=03-speichern-oeffnen-exportieren). They open in a new window. - -## Game: Which building block? - -::: spiel frage -titel: Game: Which building block? -intro: Ludwig describes what his mBot2 should do. Is it a sequence, a branch or a loop? Six rounds. -antworten: sequence | branch | loop - -runde: -frage: The mBot2 drives forward, turns right and drives forward again. -richtig: sequence -> Three steps one after another, each exactly once. - -runde: -frage: If the wall is close, it turns. Otherwise it drives forward. -richtig: branch -> Depending on the condition, one thing or the other happens. - -runde: -frage: It drives forward 10 cm four times. -richtig: loop -> The same step happens several times. - -runde: -frage: As long as no line comes, it keeps driving. -richtig: loop -> "As long as" means: again and again until the condition is no longer true. - -runde: -frage: If it is standing on red, it beeps. -richtig: branch -> It only beeps if the condition is true. - -runde: -frage: It shows "Hello" on the screen and then plays a tone. -richtig: sequence -> Two steps one after another, with no condition and no repetition. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an algorithm and a structure chart are. → A plan for the robot -- I can recognise the three building blocks sequence, branch and repetition. → Three building blocks -- I can insert, move and delete elements in the editor. → The editor -- I can save a structure chart and open it again. → Saving and opening -::: - -## Quiz - -::: quiz -? What is an algorithm? -→ A plan for the robot -- [ ] a part of the mBot2 -- [x] an exact step-by-step instruction -- [ ] a programming language -> An algorithm says exactly what happens in which order. The structure chart draws it. -? Which building block decides depending on a condition? -→ Three building blocks -- [ ] the sequence -- [x] the branch -- [ ] the repetition -> The branch has a triangle and two columns for true and false. -? You drag **drive forward** onto the bottom half of a box. Where does it land? -→ The editor -- [ ] before it -- [x] after it -- [ ] it replaces the box -> Top half means before, bottom half means after. -? Which ending does a saved structure chart file have? -→ Saving and opening -- [ ] `.java` -- [x] `.visustruct` -- [ ] `.png` -> `.png` is a picture to pass on. You can only keep editing the `.visustruct` file. -::: diff --git a/web/lessons/en/visubot/01-der-mbot2.md b/web/lessons/en/visubot/01-der-mbot2.md deleted file mode 100644 index 21bf1a7e..00000000 --- a/web/lessons/en/visubot/01-der-mbot2.md +++ /dev/null @@ -1,416 +0,0 @@ ---- -titel: The mBot2 -bereich: Getting started -kurz: Get to know the robot you are about to program. ---- - -## Skills I will gain now - -::: kompetenzen -- name the parts of the mBot2 -- explain how the mBot2 measures distances -- explain how the mBot2 detects a line -- explain what sensors and actuators are -- control the mBot2 with elements from the group mBot2 commands -- let the mBot2 drive in the simulator -- connect the real mBot2 with USB-C and disconnect it again -- check that all sensors and actuators work -::: - -## Hello mBot2 - -::: mbot2 -focus: -spin: yes -::: - -The **mBot2** is a small robot made by Makeblock. In this lesson you take a tour through all its parts: - -- the [CyberPi]{part=cyberpi}, the small computer at the back on top -- the see-through [shield with battery]{part=shield} in the middle -- two [motors]{part=motors} with [wheels]{part=wheels} -- the [ultrasonic sensor]{part=ultrasonic} at the front, which looks like two eyes -- the [line sensor]{part=lineSensor} under the nose -- the turquoise [chassis]{part=chassis} that holds everything together - -In VisuBot you program it with structure charts. You can try every program in the simulator first, even without a real robot. - -> Move the mouse over a word underlined in yellow and the part lights up in the model. Tap the word and the camera flies there. You can rotate the model with the mouse or your finger and zoom with the mouse wheel. - -## The CyberPi - -::: mbot2 -focus: cyberpi, screen, joystick, buttons, leds, lightSensor, microphone -view: back -::: - -The [CyberPi]{part=cyberpi,screen,joystick,buttons,leds,lightSensor,microphone} is the brain of the mBot2: a **microcontroller**, a small computer without a keyboard. It runs your program. It lies crosswise at the back on top of the robot and can be taken off. - -A lot is already built in: - -- a colour [screen]{part=screen} that shows text and pictures -- a [joystick]{part=joystick} you can push in four directions -- two [buttons]{part=buttons}, one with a triangle and one with a square, and the Home button on the side next to the USB port -- five colourful [LEDs]{part=leds} as a light bar along the back -- a [light sensor]{part=lightSensor} and a [microphone]{part=microphone} - -There is also a speaker and a motion sensor that you cannot see from outside. The motion sensor notices when the CyberPi is tilted or turned. - -> The model was built from photos. Look at your own mBot2 and compare. - -## Screen, LEDs and speaker - -::: mbot2 -focus: screen, leds -view: back -::: - -With the [screen]{part=screen} and the five [LEDs]{part=leds} the mBot2 shows you what it is doing. Each LED can glow in almost any colour because it mixes red, green and blue. That is why they are called **RGB LEDs**. - -Later you make the LEDs glow red with `mbot.setLed(255, 0, 0)`. The three numbers are red, green and blue, each from 0 to 255. - -The [CyberPi]{part=cyberpi} also has a small **speaker**. With `mbot.playTone(440, 300)` it plays a tone: 440 vibrations per second (hertz), 300 milliseconds long. - -## Measuring light and sound - -::: mbot2 -focus: lightSensor, microphone -view: top -::: - -The [light sensor]{part=lightSensor} measures how bright it is. The [microphone]{part=microphone} measures how loud it is. For example, the mBot2 can start driving when you clap your hands. - -## Buttons and joystick - -::: mbot2 -focus: joystick, buttons -view: top -::: - -The [buttons]{part=buttons} A and B and the [joystick]{part=joystick} are sensors too: they notice when you press them. Your program can, for example, wait until you press A and only then start driving. With the joystick you steer the mBot2 like with a remote control. - -## The motion sensor - -::: mbot2 -focus: cyberpi -spin: yes -::: - -Inside the [CyberPi]{part=cyberpi} there is a sensor you cannot see: the **motion sensor** (a gyroscope with an accelerometer). It notices whether the mBot2 is tilted forward, backward or to the side, how far it has turned and whether someone shakes it. Your phone has such a sensor too: that is why the picture turns when you hold it sideways. - -## Power: the shield with battery - -::: mbot2 -focus: shield, powerSwitch -view: side -::: - -The big see-through case is the [mBot2 shield]{part=shield}. Through the lid you see the grey **battery** that powers the robot. At the back of the shield the cables of the motors (M1 and M2) and of the ultrasonic sensor are plugged in. - -On the left side is the [power switch]{part=powerSwitch} labelled ON/OFF. Next to it are two sockets S1 and S2 for small servo motors. Switch the mBot2 off when you do not need it, so the battery lasts longer. You charge it with the USB cable on the CyberPi. - -## Motors and wheels - -::: mbot2 -focus: motors, wheels -view: side -::: - -Two [motors]{part=motors} drive the two [wheels]{part=wheels}, each its own. You see them from below: silver motors in light blue gearboxes. - -- If both wheels turn forward at the same speed, the mBot2 drives **straight**. -- If one wheel turns forward and the other backward, it turns **on the spot**. - -The motors are **encoder motors**: they count how far the wheel has turned. That is how the mBot2 knows when it has driven exactly 10 cm or turned exactly 90°. - -## The ultrasonic sensor - -::: mbot2 -focus: ultrasonic -view: front -::: - -At the front sits the black [ultrasonic sensor]{part=ultrasonic}. It looks like two eyes but is more like an ear: one circle sends out a very high sound we cannot hear. The other waits for the echo. The longer the echo takes, the farther away the wall is. - -That is how the mBot2 measures distances in centimetres. The rings around the circles can also glow blue. - -::: angeberwissen -Bats do exactly what the ultrasonic sensor does: they call so high that we cannot hear it and listen for the echo. Sound travels about 343 metres per second. If a wall is one metre in front of the mBot2, the echo is back after just under 6 thousandths of a second. -::: - -## Under the robot - -::: mbot2 -focus: lineSensor, caster -view: bottom -::: - -Now you look from below. Right at the front the black board of the [line sensor]{part=lineSensor} sticks out. It has **four** small probes side by side. Each one shines light on the floor and measures how much comes back. A black line reflects little, a bright floor a lot. That is how the mBot2 knows whether it is on a line. - -The sensor is called a **quad RGB sensor** because it can do even more: it also recognises **colours**, for example a red or green field on the floor. - -The [ball caster]{part=caster} behind it is the third leg. It has no motor and simply rolls along in any direction. - -## The chassis - -::: mbot2 -focus: chassis -::: - -The [chassis]{part=chassis} is the turquoise aluminium frame. It holds all the parts together. At the front it has two round "ears", at the back a big **M** for Makeblock. The many holes are there on purpose: you can screw more parts onto them. - -## Sensors and actuators - -A robot has two kinds of parts: - -- **Sensors** measure something: the distance, a line, a colour, the brightness, the loudness, the tilt or whether a button is pressed. -- **Actuators** do something: the motors turn the wheels, the LEDs light up, the screen shows something, the speaker beeps. - -The word comes from the Latin *agere*, “to act”: actuators act, sensors (from *sentire*, “to feel”) feel. - -Your program reads the sensors and controls the actuators. You already know this as input, processing and output. - -## Find the parts - -Your turn: click the part you are looking for in the model. If you are right, the next question appears. - -::: mbot2quiz -- ultrasonic: What does the mBot2 use to measure how far away the wall is? -- screen: Where does text from your program appear? -- wheels: What moves the mBot2 forward? -- lineSensor: What detects a black line on the floor? (Hint: rotate the model.) -- powerSwitch: Where do you switch the mBot2 on and off? -- leds: What glows red with `mbot.setLed(255, 0, 0)`? -done: Great, you know your way around the mBot2! -::: - -## Commands for the mBot2 - -In VisuBot the [Elements](#zeige-elemente) have their own group **mBot2 commands**. Each box in it is a command for the robot: - -| Element | Text in the box | What happens | -|---|---|---| -| move forward | `mbot.forward()` | 10 cm forward | -| move back | `mbot.backward()` | 10 cm back | -| turn right | `mbot.turnRight()` | turn 90° to the right | -| turn left | `mbot.turnLeft()` | turn 90° to the left | -| LEDs on | `mbot.setLed(255, 0, 0)` | the LEDs light up red | - -You may also put a number in the brackets: `mbot.forward(30)` drives 30 cm, `mbot.turnRight(45)` turns only 45°. - -> The commands are real Java. `mbot` is the robot, and after the dot comes what it should do. - -## First drive - -After building, start the [simulation](#zeige-simulation). The VisuBot world with the mBot2 appears on the right. The grid on the floor has squares of 10 cm, exactly one **move forward**. - -::: übung vb-gerade -titel: Straight ahead -auftrag: Drag **move forward** into the structure chart five times. Then start the simulation and watch the mBot2 drive 50 cm straight ahead. -fahrt: 5 x (forward) -tipp: You find the group mBot2 commands below the normal elements. -tipp: Five boxes `mbot.forward()` one below the other. Each drives 10 cm. -lösung: vb-gerade-loesung.visustruct -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Then it drives the route for you. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - mbot.forward(60); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,0 -> Two fields forward, turn right, one more field. - -runde: - mbot.forward(30); - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,1 -> One field forward, then it faces left and drives two fields. - -runde: - mbot.turnRight(); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Turning right twice means it faces backwards. Then it drives one field down. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: .... | .... | .^.. -ziel: 2,2 -> Up, to the right, and after the second turn it faces down. -::: - -## Connecting the real mBot2 - -::: mbot2 -focus: cyberpi, powerSwitch, joystick, buttons -view: side -::: - -Everything you build in the simulator can also be driven by the real mBot2. This is how you connect it: - -1. Plug a **USB-C cable** into your computer (for example a MacBook) and the other end into the USB-C port on the right of the [CyberPi]{part=cyberpi}, next to the Home button. -2. Push the [ON/OFF slide switch]{part=powerSwitch} on the left of the shield to ON. The CyberPi's screen lights up. -3. A menu appears on the screen with **restart** highlighted (on a German CyberPi: „neu starten“). Press the [joystick]{part=joystick} down like a button to confirm it. -4. After the restart it says “Press B to start”. Press **button B**, that is the [button with the triangle]{part=buttons}. -5. Open VisuStruct in **Chrome** or **Edge**. Safari and the iPad cannot talk to USB devices. -6. As soon as the CyberPi has started up, click **🔌 mBot2** at the top (or “Connect mBot2” in the gear menu). -7. Chrome shows a list of ports. Choose the entry **USB Serial** (for example “USB Serial (cu.usbserial-110)”). It is often at the very bottom, so you may have to scroll down. Then click **Connect**. -8. The CyberPi restarts briefly (⏳). Then “Hallo VisuBot!” appears on its screen and the button shows 🟢. - -Now the mBot2 drives along with every step of the simulation. Put it on the floor and give it room! - -> Finished? Click **🟢 mBot2** once more. It stops, shows “Bis bald!” and releases the port. Only then pull out the cable. - -> Want to drive with no cable at all? You learn that in lesson 2 under **Driving without a cable**. - -## Testing the mBot2 - -When the mBot2 is connected, **🔧 Test the mBot2** appears below the world. Open it: - -- With the buttons you try each actuator once: LEDs, tone, text on the screen and the wheels. Lift the mBot2 up for this so it does not drive off the table. -- Below are all sensors with their values, and they update live. Hold your hand in front of the ultrasonic sensor, press button A, tilt it, clap your hands or put it on a red sheet of paper. - -If a value does not change although you do something, something is wrong. Then tell your teacher before you start programming. - -## Game: Sensor or actuator? - -::: spiel frage -titel: Game: sensor or actuator? -intro: Does the part measure something, or does it do something? Ten rounds. -antworten: Sensor | Actuator - -runde: -frage: The ultrasonic sensor measures how far away the wall is. -richtig: Sensor -> It measures something: the distance. - -runde: -frage: The motors turn the wheels. -richtig: Actuator -> They do something: they move the mBot2. - -runde: -frage: The LEDs glow red. -richtig: Actuator -> Glowing is doing something, not measuring. - -runde: -frage: The line sensor recognises the black tape. -richtig: Sensor -> It measures how much light comes back from the floor. - -runde: -frage: The speaker beeps. -richtig: Actuator -> It outputs a sound. - -runde: -frage: The microphone hears you clap. -richtig: Sensor -> It measures the loudness. - -runde: -frage: The screen shows “Hello VisuBot!”. -richtig: Actuator -> Showing something is an output. - -runde: -frage: Button A notices that you press it. -richtig: Sensor -> Buttons are sensors: they report whether they are pressed. - -runde: -frage: The motion sensor notices that the mBot2 is tilted. -richtig: Sensor -> It measures the tilt. - -runde: -frage: The rings on the ultrasonic sensor glow blue. -richtig: Actuator -> Tricky! The part is a sensor, but the glowing rings are an output. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the parts of the mBot2. → Hello mBot2 -- I can explain how the mBot2 measures distances. → The ultrasonic sensor -- I can explain how the mBot2 detects a line. → Under the robot -- I can explain what sensors and actuators are. → Sensors and actuators -- I can control the mBot2 with elements from the group mBot2 commands. → Commands for the mBot2 -- I can let the mBot2 drive in the simulator. → First drive -- I can connect the real mBot2 with USB-C and disconnect it again. → Connecting the real mBot2 -- I can check that all sensors and actuators work. → Testing the mBot2 -::: - -## Quiz - -::: quiz -? Match each part to its job. -→ Hello mBot2 -= Ultrasonic sensor -> measures the distance -= Motors -> move the wheels -= CyberPi -> runs the program -= Line sensor -> detects a dark line -? Which parts are actuators? -→ Sensors and actuators -- [x] Motors -- [ ] Ultrasonic sensor -- [x] LEDs -- [ ] Line sensor -> Actuators do something, sensors measure something. -? How does the ultrasonic sensor measure the distance? -→ The ultrasonic sensor -- [ ] It takes a photo of the wall. -- [x] It sends out a very high sound and waits for the echo. -- [ ] It feels the wall with its blue rings. -> The longer the echo takes, the further away the wall is. -? How does the line sensor detect a black line? -→ Under the robot -- [x] Little light comes back from the black line. -- [ ] The line is warmer than the floor. -- [ ] It hears the wheels rolling over the line. -> Each feeler shines light on the floor. A light floor reflects a lot of light, a black line only a little. -? What does `mbot.turnRight()` do? -→ Commands for the mBot2 -- [ ] The mBot2 drives 10 cm to the right. -- [x] The mBot2 turns 90° to the right. -- [ ] The right LED switches on. -> With no number in the brackets it turns 90°. `mbot.turnRight(45)` turns only 45°. -? The structure chart has five boxes `mbot.forward()` one below the other. How far does the mBot2 drive in the simulation? -→ First drive -- [ ] 5 cm -- [x] 50 cm -- [ ] 10 cm -> Each `mbot.forward()` drives 10 cm, which is one square of the grid. -? You have finished with the real mBot2. What do you do before you pull out the cable? -→ Connecting the real mBot2 -- [x] Click **🟢 mBot2** once more and wait until “Bis bald!” appears -- [ ] Push the slide switch to OFF -- [ ] Nothing, the cable can come out right away -> Only after “Bis bald!” is the port free. -? You hold your hand in front of the ultrasonic sensor, but the value under **🔧 Test the mBot2** does not change. What do you do? -→ Testing the mBot2 -- [ ] Start programming anyway -- [x] Tell your teacher before you start programming -- [ ] Change the value in the simulation by hand -> If a value does not change although you do something, something is wrong with the robot. -::: diff --git a/web/lessons/en/visubot/02-das-quadrat.md b/web/lessons/en/visubot/02-das-quadrat.md deleted file mode 100644 index d11d4912..00000000 --- a/web/lessons/en/visubot/02-das-quadrat.md +++ /dev/null @@ -1,365 +0,0 @@ ---- -titel: The square -bereich: Getting started -kurz: From a long structure chart to a triple nested loop. ---- - -## Skills I will gain now - -::: kompetenzen -- build a sequence without a loop first -- spot repetitions and replace them with a for loop -- nest two loops -- nest three loops -- upload my program to the mBot2 and start it with button A without a cable -::: - -## Driving a square - -The mBot2 should drive a square with sides of 50 cm. You already know one side: **move forward** five times. Then it turns right, and the next side begins. - -::: übung vb-quadrat -titel: The square -auftrag: Build a structure chart that makes the mBot2 drive a square: **move forward** five times, then **turn right**. And all of that four times. -fahrt: 4 x (5 x (forward), turnRight) -tipp: One side is five times move forward and once turn right. -tipp: That makes 24 boxes: four times five `mbot.forward()`, each followed by `mbot.turnRight()`. -lösung: vb-quadrat-loesung.visustruct -::: - -Quite a lot of boxes, right? If you miscount, the mBot2 no longer drives a square. - -## Always the same - -Look closely at your structure chart: five times **move forward** appears four times. When something repeats, you use a **loop**. We know exactly how often to drive, five times. The for loop fits that. - -Drag a for loop into the structure chart and give it these three parts: - -- Start: `int i = 0` -- Condition: `i < 5` -- Step: `i++` - -The body of the loop gets a single **move forward**. The loop counts `i` from 0 to 4, that is five runs. - -`i` is a **counter**. The computer uses it to remember which run it is in. Such a named store is called a **variable**; you will get to know it properly in lesson 3. The three parts mean: - -| Part | Meaning | -|---|---| -| `int i = 0` | The counter `i` is a whole number (`int`) and starts at 0. | -| `i < 5` | As long as `i` is less than 5, the loop runs once more. | -| `i++` | After every run `i` gets 1 bigger. | - -This is how the loop counts: - -| Run | 1 | 2 | 3 | 4 | 5 | after that | -|---|---|---|---|---|---|---| -| `i` | 0 | 1 | 2 | 3 | 4 | 5 | -| `i < 5`? | true | true | true | true | true | false, done | - -::: übung vb-quadrat-schleife -titel: Sides with a loop -auftrag: In your square, replace each row of five **move forward** with a for loop that drives five times. The mBot2 then drives exactly the same square. -fahrt: 4 x (5 x (forward), turnRight) -braucht: forSchleife -tipp: Start `int i = 0`, condition `i < 5`, step `i++`. The body only contains `mbot.forward()`. -tipp: Four times one below the other: the for loop and then `mbot.turnRight()`. -lösung: vb-quadrat-schleife-loesung.visustruct -::: - -## A loop in a loop - -Now the same pair appears four times: a for loop and **turn right**. That is a repetition too! Put both into a second for loop that runs four times. A loop inside a loop is called a **nested loop**. - -Each loop needs its own counter variable, otherwise the two get in each other's way. The usual way: the outer loop counts with `i`, the inner one with `j`. So rename your inner loop to `j`. - -::: übung vb-quadrat-verschachtelt -titel: Nested square -auftrag: Build the square with two nested for loops. The outer one runs four times, once for each side. The inner one runs five times, once for each step. -fahrt: 4 x (5 x (forward), turnRight) -schleifentiefe: 2 -tipp: Outside: start `int i = 0`, condition `i < 4`, step `i++`. Inside: `int j = 0`, `j < 5`, `j++`. -tipp: The outer loop contains the inner loop with `mbot.forward()` and below it `mbot.turnRight()`. -lösung: vb-quadrat-verschachtelt-loesung.visustruct -::: - -24 boxes have become four. Step through the program in the simulation with **Step** and watch the variables. - -::: quiz -? How often does the mBot2 run `mbot.forward()` in the nested square? -- [ ] 9 times -- [x] 20 times -- [ ] 5 times -> The inner loop runs five times, in each of the four runs of the outer loop: 4 · 5 = 20. -::: - -## Four squares - -Now it gets really nice: the mBot2 should drive four squares. After each square it turns right once. That makes a big window with a cross. - -You could copy your square four times and add a **turn right** after each square. Go ahead and try it! Notice something? Again the same thing appears four times. - -::: ludwig -Whenever you copy boxes, ask yourself: does something repeat here? Then a loop fits. -::: - -::: übung vb-vier-quadrate -titel: Four squares -auftrag: Let the mBot2 drive four squares. After each square it turns right once. Use three nested loops. -fahrt: 4 x (4 x (5 x (forward), turnRight), turnRight) -schleifentiefe: 3 -tipp: Put a third for loop that runs four times around your nested square. -tipp: Now `i` counts on the very outside (`int i = 0`, `i < 4`, `i++`). The loops inside are then called `j` and `k`. Inside the outer loop goes your square and then `mbot.turnRight()`. -lösung: vb-vier-quadrate-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lessons ["For loop"](?visustruct&lektion=13-for-schleife) and ["Nesting"](?visustruct&lektion=14-verschachteln). They open in a new window. - -::: quiz -? How often does the mBot2 drive 10 cm forward for the four squares? -→ Four squares -- [ ] 20 times -- [ ] 60 times -- [x] 80 times -> 4 squares · 4 sides · 5 steps = 80. Without loops that would be 80 boxes. -::: - -## Driving without a cable - -::: mbot2 -fokus: cyberpi, buttons -ansicht: oben -::: - -So far the mBot2 hangs on the cable while it drives. But you can also **upload your program to the mBot2**. Then it remembers it and drives all by itself, for example through a maze on the floor. - -1. Connect the mBot2 with the USB cable and switch it on, as in lesson 1. This only works in **Chrome** or **Edge**. -2. Open your program, for example the four squares. -3. Click **⬆ Upload** at the top and then **Upload now**. Choose **USB Serial** again. -4. Wait until it says “Done!”. The mBot2 restarts and shows **A = Start**. -5. Unplug the cable and put the mBot2 at the start. -6. Press **button A**, that is the [button with the square]{teil=buttons}. Now your program starts. - -The program stays saved, even when you switch the mBot2 off and on again. Only the next upload replaces it. - -> Afraid it drives off before you are ready? Don't worry: it always waits for button A. If you press A again while it drives, nothing happens. When it has finished, A starts the program again from the beginning. - -## Game: How often does it drive? - -::: spiel frage -titel: Game: How often does it drive? -intro: Count along without starting the simulation. How often does the mBot2 drive or turn? Six rounds. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(); - mbot.turnRight(); - } -frage: How often does the mBot2 drive forward? -- 3 times -- [x] 4 times -- 5 times -> `i` counts 0, 1, 2 and 3. That is four runs. - -runde: - for (int i = 1; i <= 4; i++) { - mbot.forward(); - } -frage: How often does the mBot2 drive forward? -- 3 times -- [x] 4 times -- 5 times -> This time `i` counts from 1 to 4 because of `<=`. That is four runs too. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: How often does the mBot2 drive forward? -- 8 times -- [x] 15 times -- 5 times -> The inner loop runs five times in each of the three outer runs: 3 · 5 = 15. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: How often does the mBot2 turn right? -- [x] 3 times -- 15 times -- 5 times -> `mbot.turnRight()` is in the outer loop, not in the inner one. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - } - mbot.turnRight(); -frage: Does the mBot2 drive a square? -- Yes, with 50 cm sides. -- [x] No, it drives 200 cm straight ahead and only turns at the end. -- Yes, but only half of one. -> `mbot.turnRight()` is below the loop. It only runs when the loop has finished. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - mbot.forward(30); - mbot.turnRight(); - } - mbot.turnRight(); - } -frage: How many squares does the mBot2 drive? -- [x] 2 -- 4 -- 8 -> The inner loop drives one square. The outer loop repeats that twice. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should drive and tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(50); - mbot.turnRight(); - } -frage: Should drive a square with 50 cm sides. -fehler: 1 -besser: for (int i = 0; i < 4; i++) { -> A square has four sides. With `i < 3` it only drives three. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(180); - } -frage: Should drive a square with 50 cm sides. -fehler: 3 -besser: mbot.turnRight(); -> With 180° it turns round and just drives back and forth. A square needs 90°. - -runde: - for (int i = 0; i < 4; i++) { - for (int i = 0; i < 5; i++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Should drive a square made of four times five steps. -fehler: 2 -besser: for (int j = 0; j < 5; j++) { -> The inner loop needs its own counter `j`. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnLeft(45); - } -frage: Should drive a square with 30 cm sides. -fehler: 3 -besser: mbot.turnLeft(); -> A square needs right angles, so 90°. Without a number it turns 90°. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Then it drives the route for you. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..... | ..^.. -ziel: 2,0 -> One field forward, three times. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 1,2 -> A whole square: it arrives exactly where it started. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 2,2 -> Only three sides: the fourth is missing, so it ends up to the right of the start. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - mbot.forward(30); - } - mbot.turnRight(); - } -karte: .... | .... | .... | ^... -ziel: 2,1 -> The inner loop drives two fields, then it turns. All that twice: two fields up, two to the right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a sequence without a loop first. → Driving a square -- I can spot repetitions and replace them with a for loop. → Always the same -- I can nest two loops. → A loop in a loop -- I can nest three loops. → Four squares -- I can upload my program to the mBot2 and start it with button A without a cable. → Driving without a cable -::: - -## Quiz - -::: quiz -? Still without a loop, the mBot2 should drive one side of 50 cm and then turn. Which boxes do you need? -→ Driving a square -- [ ] one `mbot.forward()` and one `mbot.turnRight()` -- [x] five `mbot.forward()` and one `mbot.turnRight()` -- [ ] five `mbot.turnRight()` -> Each `mbot.forward()` drives 10 cm, five of them are 50 cm. -? A for loop has the start `int i = 0`, the condition `i < 5` and the step `i++`. How often does it run? -→ Always the same -- [ ] 4 times -- [x] 5 times -- [ ] 6 times -> `i` counts 0, 1, 2, 3, 4. That is five runs. -? Why is the counter of the inner loop called `j` and not `i` as well? -→ A loop in a loop -- [ ] Each letter may only appear once in a program. -- [x] Each loop needs its own counter, otherwise the two get in each other's way. -- [ ] `j` counts faster than `i`. -> If both counted with `i`, the inner loop would mix up the outer one. -? How often does the mBot2 run `mbot.turnRight()` for the four squares? -→ Four squares -- [ ] 4 times -- [ ] 16 times -- [x] 20 times -> In each square it turns four times, that is 4 · 4 = 16. After each square there is one more turn: 16 + 4 = 20. -? Your program has been transferred and the cable is out. How do you start it? -→ Driving without a cable -- [ ] with button B -- [x] with button A -- [ ] with **⬆ Transfer** -> After the transfer the mBot2 shows **A = Start** and waits for button A. -::: diff --git a/web/lessons/en/visubot/02-quadrat-als-pap.md b/web/lessons/en/visubot/02-quadrat-als-pap.md deleted file mode 100644 index 52ca3801..00000000 --- a/web/lessons/en/visubot/02-quadrat-als-pap.md +++ /dev/null @@ -1,182 +0,0 @@ ---- -titel: Flowcharts and pseudocode -bereich: Getting started -kurz: Write the plan for the square as a flowchart and as pseudocode and compare them with the structure chart. ---- - -## Skills I will gain now - -::: kompetenzen -- read a flowchart for the mBot2 and build one myself -- read a plan written as pseudocode -- compare structure chart, flowchart and pseudocode -::: - -## Three ways to write down a plan - -You know the plan for the square as a **structure chart**: a loop that runs four times, with driving one side and turning right inside it. - -There are two more ways to write down the same plan. You will meet both often, in class and on the internet: - -- the **flowchart**, -- **pseudocode**, a plan made of text in plain language. - -You can't control the mBot2 with them directly. But with all three you can write down a plan before you build it as a structure chart or in Java. - -## The flowchart - -A flowchart is made of a few symbols connected by **arrows**: - -- **Oval:** start or end. -- **Rectangle:** a statement, for example *drive forward 50 cm*. -- **Diamond:** a question with two exits: **yes** and **no**. -- **Parallelogram:** an input or output, for example a text on the screen. -- **Arrow:** shows which step comes next. - -A loop has no symbol of its own in a flowchart. You recognise it by an arrow that leads **back up**. As long as the answer in the diamond is **no**, it goes back and the steps run again. - -> Careful: in a flowchart an arrow may point anywhere, even all over the place. In big charts this quickly becomes confusing. This is called **spaghetti code**. It can't happen in a structure chart, because there are no arrows. - -## Exercise: The square as a flowchart - -The mBot2 drives one side, turns right and counts the side. After four sides it shows "Square done!" on the screen. - -::: pap -titel: Exercise: The square as a flowchart -auftrag: The places in the chart are empty. Put each element in its place. The shape of the place helps you. -start: Start -anweisung: Drive forward 50 cm -anweisung: Turn right -anweisung: Count the side -frage: 4 sides driven? | nein -> Drive forward 50 cm -ausgabe: Show “Square done!” -ende: End -erklärung: As long as four sides have not been driven yet, the arrow leads from the diamond back to “Drive forward 50 cm”. That is the loop. Showing a text on the screen is an output, so it sits in the parallelogram. -legende: ja -::: - -**Draw your own flowcharts:** With **VisuFlow** you can easily draw your own flowchart, for example for your robot's next drive. The symbols are ready in a bar: tapping one adds the next symbol and connects it with an arrow right away. You save your flowchart as a file and export it as an image or PDF, for example for your notebook or a presentation. [Open VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Pseudocode - -**Pseudocode** is text that is laid out like a program but written in plain language. Fixed words like REPEAT, IF and WHILE show the building blocks, and the **indentation** shows what belongs to them. The square looks like this: - -``` -REPEAT 4 times - drive forward 50 cm - turn right -END REPEAT -show "Square done!" -``` - -And this is the same plan in Java: - -```java -for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(); -} -mbot.showText("Square done!"); -``` - -Pseudocode has **no fixed way of writing**. Some people write FOR i FROM 1 TO 4, others REPEAT 4 times. All that matters is that anyone can understand it without an explanation. You can't control the computer with it, for that you need Java. - -### Comparison - -- **Structure chart:** boxes inside each other, the loop is an upside-down L. You see the structure at a glance. -- **Flowchart:** symbols with arrows, the loop is an arrow back up. Very clear, but big charts get confusing. -- **Pseudocode:** text with indentation, the loop sits between REPEAT and END REPEAT. Quick to write and already close to Java. - -::: angeberwissen -Baah, who invented flowcharts? A woman and her husband: **Lillian and Frank Gilbreth** presented their "process charts" in 1921, long before the first computer. They used them to draw work steps in factories so they could make them simpler. Lillian Gilbreth had a doctorate in psychology and in 1965 became the first woman elected to the American National Academy of Engineering. Programmers later took over the idea for their plans. -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Flowcharts and pseudocode"](?visustruct&lektion=01-weitere-darstellungen) with baking a cake as a flowchart and the GOTO command. It opens in a new window. - -## Game: Which way of drawing? - -::: spiel frage -titel: Game: Which way of drawing? -intro: Ludwig describes a plan for the mBot2 without saying how it is written down. Is it the structure chart, the flowchart or pseudocode? Six rounds. -antworten: structure chart | flowchart | pseudocode - -runde: -frage: "An arrow leads from the diamond back to 'Drive forward 50 cm'." -richtig: flowchart -> You recognise a loop in a flowchart by the arrow back up. - -runde: -frage: "The loop looks like an upside-down L, with drive forward and turn right inside." -richtig: structure chart -> The upside-down L is the loop in a structure chart. - -runde: -frage: "REPEAT 4 times, with two indented lines below." -richtig: pseudocode -> Fixed words and indentation: that is pseudocode. - -runde: -frage: "'Square done!' sits in a slanted box." -richtig: flowchart -> The slanted box is the parallelogram for input and output. - -runde: -frage: "There are no arrows. If you insert a box, everything below simply moves down." -richtig: structure chart -> Without arrows, no jump in a structure chart can point to the wrong place. - -runde: -frage: "Some write FOR i FROM 1 TO 4, others REPEAT 4 times. Both are right." -richtig: pseudocode -> Pseudocode has no fixed way of writing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the elements of a flowchart. → The flowchart -- I can build the square as a flowchart myself. → Exercise: The square as a flowchart -- I can read a plan written as pseudocode. → Pseudocode -- I can compare structure chart, flowchart and pseudocode. → Three ways to write down a plan -::: - -## Quiz - -::: quiz -? How do you recognise a loop in a flowchart? -→ The flowchart -- [ ] By an upside-down L -- [x] By an arrow that leads back up -- [ ] By the word REPEAT -> The upside-down L belongs to the structure chart, REPEAT to pseudocode. - -? Match each flowchart symbol to its meaning. -→ The flowchart -= Oval -> Start or end -= Rectangle -> Statement -= Diamond -> Question with yes and no -= Parallelogram -> Input or output -> "Drive forward 50 cm" is a statement, showing "Square done!" is an output. - -? In the square flowchart, the diamond says "4 sides driven?". Where does the arrow lead for **no**? -→ Exercise: The square as a flowchart -- [x] Back to "Drive forward 50 cm" -- [ ] To the end -- [ ] To showing "Square done!" -> With no, the square isn't finished yet, so the mBot2 drives the next side. - -? In pseudocode, what shows which lines belong to the loop? -→ Pseudocode -- [ ] An arrow -- [x] The indentation and the END REPEAT -- [ ] The colour -> Everything between REPEAT and END REPEAT is indented and repeated. - -? Can the mBot2 run pseudocode directly? -→ Three ways to write down a plan -- [ ] Yes, like Java -- [x] No, pseudocode is only a plan for people -> The mBot2 can run the structure chart or Java. Pseudocode helps with planning. -::: diff --git a/web/lessons/en/visubot/03-variablen-und-ausgabe.md b/web/lessons/en/visubot/03-variablen-und-ausgabe.md deleted file mode 100644 index f6fa78f8..00000000 --- a/web/lessons/en/visubot/03-variablen-und-ausgabe.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -titel: Variables and output -bereich: Getting started -kurz: Store values in variables, drive the mBot2 with them and show results on the screen. ---- - -## Skills I will gain now - -::: kompetenzen -- create and use a variable -- read a short Java program and type it in the Code area -- tell the data types `int`, `double`, `String` and `boolean` apart -- drive a rectangle in Java with two variables -- show a result on the screen with `mbot.showText` -::: - -## A variable for the side - -With `mbot.forward(30)` the mBot2 drives 30 cm in one go. If you drive a square with it and no loop, the 30 appears four times in your program. To make the square bigger, you have to change it in four places. It is better to store the side length **once** and then use it everywhere. - -That is what **variables** are for. A variable is a box with a name that holds a value: - -```java -int side = 30; -mbot.forward(side); -``` - -- `int` is the **data type**: a whole number. -- `side` is the **name**. You may choose it yourself. -- `30` is the **value**. The `=` puts it into the box. - -Wherever `side` appears afterwards, the computer uses its value. So `mbot.forward(side)` drives 30 cm. - -::: übung vb-variable-seite -titel: Square with a variable -auftrag: In one statement, create the variable `int side = 30`. After that the mBot2 drives a square: four times `side` centimetres forward and turn right. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: The first statement is `int side = 30`. Below it comes a for loop that runs four times. -tipp: Inside the loop are `mbot.forward(side)` and `mbot.turnRight()`. -lösung: vb-variable-seite-loesung.visustruct -::: - -> Now change only the 30 to 60 and start the simulation. The whole square becomes twice as big. - -## Which data types are there? - -Every variable has a data type. It decides which values fit inside: - -| Data type | What fits inside? | Example | -|---|---|---| -| `int` | whole numbers | `int side = 30;` | -| `double` | decimal numbers, written with a point | `double wheelCircumference = 20.4;` | -| `String` | text in quotation marks | `String name = "Ludwig";` | -| `boolean` | true or false | `boolean wallNear = mbot.getDistance() < 20;` | - -One wheel rotation takes the mBot2 about 20.4 cm, which is a decimal number. `wallNear` holds `true` if the wall is closer than 20 cm. You will meet the distance sensor in Lesson 4. - -You can also calculate with variables: `int distance = 4 * side;` works out the whole distance around the square. - -## This is what Java looks like - -So far you have dragged boxes. From now on you will also write some programs in **Java**. You already know the boxes, because their text is real Java already. To the right of the structure chart is the [Code](#zeige-codelive) area. It shows your structure chart as Java code, and whatever you type there turns into the structure chart straight away. - -The square with a variable looks like this in Java: - -```java -int seite = 30; -for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); -} -``` - -- Every box is one line. Every statement ends with a **semicolon** `;`. -- The for loop is called `for`. Its three parts start, condition and step stand in round brackets, separated by `;`. -- Whatever the loop repeats stands in **curly brackets** `{ }`. Indent it so you can see at once what belongs to the loop. There is no semicolon after `{` and `}`. -- Upper and lower case matter: `mbot.turnRight()` works, `mbot.turnright()` doesn't. - -> How to solve a Java exercise: click **Open in editor**. The Code area opens on the right. Type your program there, watch the simulation and finally click **Check**. - -## Now in Java: rectangle - -A rectangle has two different sides. So you need two variables. - -::: übung vb-java-rechteck -titel: Rectangle with variables -code: java -auftrag: Write in Java: create `int length = 60` and `int width = 30`. The mBot2 drives a rectangle: length, turn right, width, turn right, and all that twice. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -braucht: forSchleife -tipp: Two variables at the top: `int length = 60;` and `int width = 30;`. Below them a for loop that runs twice. -tipp: Inside the loop: `mbot.forward(length);`, `mbot.turnRight();`, `mbot.forward(width);`, `mbot.turnRight();` -lösung: vb-java-rechteck-loesung.visustruct -::: - -## Result on the screen - -The mBot2 can do more than drive, it can also show you something. With `mbot.showText(…)` a text appears on the CyberPi's [screen]{teil=screen}. With `+` you join text and values: - -```java -int side = 30; -int distance = 4 * side; -mbot.showText("Distance: " + distance + " cm"); -``` - -The screen then shows **Distance: 120 cm**. You will learn more about the screen in Lesson 18. - -::: übung vb-strecke-anzeigen -titel: How far does it drive? -code: java -auftrag: Create `int length = 60` and `int width = 30` and drive the rectangle as before. Then work out in a variable `distance` how far the mBot2 drove all the way round, and show **Distance: 180 cm** on the screen. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -befehle: showText "Distance: 180 cm" -braucht: forSchleife -tipp: All the way round is twice the length and twice the width: `int distance = 2 * length + 2 * width;` -tipp: After the loop: `mbot.showText("Distance: " + distance + " cm");` -lösung: vb-strecke-anzeigen-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Input and output"](?visustruct&lektion=06-eingabe-und-ausgabe). It opens in a new window. There you print results to the console with `System.out.println`. - -## Game: Which data type? - -::: spiel frage -titel: Game: Which data type? -intro: Ludwig has wiped out the data types. Which one fits the gap? Seven rounds. -antworten: int | double | String | boolean - -runde: - ___ side = 30; -frage: Which data type fits? -richtig: int -> 30 is a whole number. - -runde: - ___ wheelCircumference = 20.4; -frage: Which data type fits? -richtig: double -> 20.4 is a decimal number, written with a point in Java. - -runde: - ___ name = "Ludwig"; -frage: Which data type fits? -richtig: String -> Text in quotation marks is a `String`. - -runde: - ___ wallNear = mbot.getDistance() < 20; -frage: Which data type fits? -richtig: boolean -> A comparison is true or false, so `boolean`. - -runde: - ___ distance = mbot.getDistance(); -frage: Which data type fits? -richtig: int -> The distance sensor gives whole centimetres, for example 38. - -runde: - ___ greeting = "Hello " + "class"; -frage: Which data type fits? -richtig: String -> Two texts joined with `+` give a text again. - -runde: - ___ average = 120 / 5.0; -frage: Which data type fits? -richtig: double -> With `5.0`, Java calculates with decimal numbers. The result is 24.0. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Watch the values in the variables. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - int side = 60; - mbot.forward(side); - mbot.turnRight(); - mbot.forward(side); -karte: ... | ... | ^.. -ziel: 2,0 -> `side` is 60, so two fields up and two to the right. - -runde: - int a = 30; - int b = a * 3; - mbot.forward(b); -karte: ... | ... | ... | ^.. -ziel: 0,0 -> `b` is 3 · 30 = 90 cm, so three fields. - -runde: - int distance = 30 * 2; - mbot.backward(distance); -karte: ..^.. | ..... | ..... -ziel: 2,2 -> `backward` drives in reverse: it faces up but drives 60 cm down. - -runde: - int side = 30; - for (int i = 0; i < 2; i++) { - mbot.forward(side); - mbot.turnRight(); - side = side + 30; - } -karte: .... | .... | .... | ^... -ziel: 2,2 -> First 30 cm up, then `side` grows to 60: two fields to the right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create and use a variable. → A variable for the side -- I can read a short Java program and type it in the Code area. → This is what Java looks like -- I can tell the data types `int`, `double`, `String` and `boolean` apart. → Which data types are there? -- I can drive a rectangle in Java with two variables. → Now in Java: rectangle -- I can show a result on the screen with `mbot.showText`. → Result on the screen -::: - -## Quiz - -::: quiz -? What does `mbot.forward(side)` drive if `int side = 40;` comes first? -→ A variable for the side -- [ ] 4 cm -- [x] 40 cm -- [ ] nothing, `side` is not a command -> Wherever `side` appears, Java uses the value 40. -? Which data type fits `"Ludwig"`? -→ Which data types are there? -- [ ] `int` -- [ ] `double` -- [x] `String` -> Text in quotation marks is a `String`. -? What does `mbot.showText("Side: " + 30);` show on the screen? -→ Result on the screen -- [x] Side: 30 -- [ ] "Side: " + 30 -- [ ] 30 -> The `+` joins the text and the number. -::: diff --git a/web/lessons/en/visubot/03-zeile-fuer-zeile-testen.md b/web/lessons/en/visubot/03-zeile-fuer-zeile-testen.md deleted file mode 100644 index 10ff30fd..00000000 --- a/web/lessons/en/visubot/03-zeile-fuer-zeile-testen.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -titel: Testing step by step -bereich: Getting started -kurz: Use the simulation and a desk check to find out what your program really does. ---- - -## Skills I will gain now - -::: kompetenzen -- control the simulation with Play, Step and Stop -- watch the variables in the simulation -- do a desk check with a table -- find and fix a mistake with the simulation -::: - -## Controlling the simulation - -With the [▶︎ Start simulation](#zeige-simulation) button on the right of the tools, VisuStruct runs your structogram. In the VisuBot world the mBot2 drives along. On the right, the **Controls** appear: - -- **Play** runs the program automatically, step by step. You set how fast under **Pause per step**. -- **Pause** stops for a moment. Then you carry on with Play or Step. -- **Step** runs exactly one box. That way you can watch calmly. -- **Stop** cancels and puts the mBot2 back at the start. - -During the simulation one box is highlighted. **Last step** shows what has just happened, **Variables** shows all variables with their values. - -::: diagramm vb-l03-wachsend-loesung.visustruct -Each side gets 10 cm longer. Open the diagram in the editor and go through it with **Step**. Watch `side` and `i`. -::: - -## The desk check - -In a **desk check** you play the computer yourself. You go through the structogram with pen and paper and write down the values of the variables after every step. For the diagram above the table looks like this: - -| Step | `i` | `side` | The mBot2 … | -|---|---|---|---| -| `int side = 20` | – | 20 | | -| `mbot.forward(side)` | 0 | 20 | drives 20 cm | -| `mbot.turnRight()` | 0 | 20 | turns right | -| `side = side + 10` | 0 | 30 | | -| `mbot.forward(side)` | 1 | 30 | drives 30 cm | -| … | … | … | … | - -Then compare your table with the simulation. Do the values under **Variables** match after every step? - -::: angeberwissen -"Computer" used to be a job: people who calculated with pen and paper. One of the best was Katherine Johnson at NASA. In 1962 the astronaut John Glenn only wanted to fly once she had checked the new IBM computer's orbit by hand: "If she says they're good, I'm ready to go." -::: - -## Finding mistakes with the simulation - -Ludwig wanted to build the spiral from above: 20 cm, 30 cm, 40 cm. But his mBot2 drives 30, 40 and 50 cm. The program runs without an error message, it just does the wrong thing. This kind of mistake is called a **logic error**. You find it best with **Step** and the variables. - -::: übung vb-spirale-falsch -titel: Fix the spiral -auftrag: "Open in editor" loads Ludwig's structogram. Go through it with **Step** and watch `side`. Find out why the first side is 30 cm long and fix it. The mBot2 should drive 20, 30 and 40 cm and turn right after each side. -start: vb-spirale-falsch-start.visustruct -fahrt: forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -braucht: forSchleife -tipp: Look at the value of `side` when `mbot.forward(side)` comes up. Is the order inside the loop right? -tipp: `side = side + 10` must come **after** `mbot.forward(side)` and `mbot.turnRight()`. Drag the box to the end of the loop. -lösung: vb-spirale-falsch-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Simulation and desk check"](?visustruct&lektion=07-simulation-und-schreibtischtest). It opens in a new window. - -## Game: Be the computer - -::: spiel frage -titel: Game: Be the computer -intro: Do the desk check in your head. What does the mBot2 do, and what values do the variables have? Five rounds. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side + 10; - } -frage: What value does `side` have after the loop? -- 40 -- [x] 50 -- 30 -> 20, then 30, 40 and after the third run 50. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side + 10; - } -frage: How far does the mBot2 drive the third time? -- [x] 40 cm -- 50 cm -- 30 cm -> It drives 20, 30 and 40 cm. The 50 only ends up in `side` afterwards. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - side = side + 10; - mbot.forward(side); - } -frage: How far does the mBot2 drive the first time? -- 20 cm -- [x] 30 cm -- 10 cm -> `side` grows before driving. That was exactly Ludwig's logic error. - -runde: - int distance = 0; - for (int i = 0; i < 4; i++) { - mbot.forward(25); - distance = distance + 25; - } - mbot.showText("Distance: " + distance); -frage: What does the screen show at the end? -- Distance: 25 -- [x] Distance: 100 -- Distance: 0 -> 25 is added four times: 25, 50, 75, 100. - -runde: - int side = 10; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side * 2; - } -frage: How far does the mBot2 drive in total? -- 60 cm -- [x] 70 cm -- 80 cm -> It drives 10, 20 and 40 cm. Together that is 70 cm. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 6 1 SIMULATION: runs your program on the screen, even without a robot -- ↓ 1 10 SEQUENCE: steps that come one after another -- ↓ 3 4 STRUCTOGRAM: another name for a Nassi-Shneiderman diagram -- ↓ 3 6 DATATYPE: decides which values fit into a variable, e.g. int -- ↓ 1 8 ALGORITHM: exact step-by-step instructions -- → 9 3 LOOP: repeats steps, looks like a tilted L in the structure chart -- → 12 3 VARIABLE: a little box with a name that holds a value -- ↓ 4 1 DESKCHECK: you play the computer with pen and paper -lösung: 11 4, 9 4, 12 8, 6 9, 7 6 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can control the simulation with Play, Step and Stop. → Controlling the simulation -- I can watch the variables in the simulation. → Controlling the simulation -- I can do a desk check with a table. → The desk check -- I can find and fix a mistake with the simulation. → Finding mistakes with the simulation -::: - -## Quiz - -::: quiz -? Which button runs exactly one box? -→ Controlling the simulation -- [ ] Play -- [x] Step -- [ ] Stop -> With Step you go box by box. Play carries on by itself. -? How far does the mBot2 drive in the second pass of "Growing sides"? -→ The desk check -- [ ] 20 cm -- [x] 30 cm -- [ ] 40 cm -> After the first pass `side` has grown by 10, so it is 30. -? The program runs without a message, but the mBot2 drives the wrong way. What kind of mistake is it? -→ Finding mistakes with the simulation -- [ ] a typo -- [x] a logic error -- [ ] a fault in the robot -> The program does exactly what it says, just not what was meant. -::: diff --git a/web/lessons/en/visubot/04-sensoren.md b/web/lessons/en/visubot/04-sensoren.md deleted file mode 100644 index bb67cd31..00000000 --- a/web/lessons/en/visubot/04-sensoren.md +++ /dev/null @@ -1,179 +0,0 @@ ---- -titel: Reading sensors -bereich: Sensors and decisions -kurz: The mBot2 measures something, and your program decides. ---- - -## Skills I will gain now - -::: kompetenzen -- explain the difference between a command and a sensor -- build a sensor into a condition -::: - -## Commands and sensors - -So far you have only given the mBot2 **commands**: move forward, turn right. It carried them out without paying attention to its surroundings. - -Now the **sensors** join in. Among the [elements](#zeige-elemente) there is a group **mBot2 sensors** for them: - -| Sensor | Text | What you get | -|---|---|---| -| Distance | `mbot.getDistance()` | distance to the next wall in cm, e.g. `38` | -| Line | `mbot.getLine()` | `0` if everything under the line sensor is bright, otherwise a number greater than 0 | - -The mBot2 has even more sensors: colours, buttons and joystick, light, loudness and tilt. You get to know each of them in lessons 6 to 13, the motors in lesson 20 and the stopwatch and battery in lesson 21. - -A sensor is **not a box of its own**. It gives you a number, and with that number you build a **condition**: - -```java -mbot.getDistance() < 20 -``` - -This means: "Is the wall closer than 20 cm?" The answer is true or false. - -> How to build it: first drag a **branch** into the structure chart. Then drag the sensor **Distance** onto the branch. The condition now says `mbot.getDistance()`. You add the comparison `< 20` yourself. - -## Wall ahead? - -The mBot2 should decide by itself: if there is a wall ahead, it turns right. If the way is free, it drives forward a bit. - -This exercise has **two worlds**: in one the wall is right in front of it, in the other there is plenty of space. Your program must do the right thing in both. - -::: übung vb-wand-vorn -titel: Wall ahead? -auftrag: Build a branch with the condition `mbot.getDistance() < 20`. If **true**, the mBot2 turns right; if **false**, it moves forward. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Drag a branch into the structure chart and then the sensor **Distance** onto the branch. Write `< 20` after it. -tipp: The true branch contains `mbot.turnRight()`, the false branch `mbot.forward()`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -In the [Code](#zeige-codelive) area you can also see your structure chart as Java. A branch is called **if** there, and the false branch **else**. The condition goes in round brackets, each branch in curly brackets: - -```java -if (mbot.getDistance() < 20) { - mbot.turnRight(); -} else { - mbot.forward(); -} -``` - -> You don't have to type `if` yourself yet. There is more about it in the lesson on the ultrasonic sensor. - -## What does the sensor give you? - -A sensor gives you a **value**, just like a variable. When the mBot2 drives on, the value changes: the distance gets smaller as the wall comes closer. Only a **comparison** turns the value into a question with the answer `true` or `false`. - -| Condition | Question | Answer at a distance of 38 cm | -|---|---|---| -| `mbot.getDistance() < 20` | Is the wall closer than 20 cm? | `false` | -| `mbot.getDistance() > 30` | Is there more than 30 cm of space? | `true` | -| `mbot.getLine() == 0` | Is everything bright under the mBot2? | depends on the floor | - -You can compare with `<` (less than), `>` (greater than), `<=` (less than or equal), `>=` (greater than or equal), `==` (equal) and `!=` (not equal). - -> For "equal" you write **two** equals signs: `==`. A single `=` puts a value into a variable, which is something completely different. - -> 📘 **More without the robot:** VisuStruct has the lesson ["Branch"](?visustruct&lektion=08-verzweigung). It opens in a new window. - -::: übung vb-fehler-04-sensoren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Wall ahead?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-04-sensoren-start.visustruct -welt: wand-nah -> turnRight -welt: wand-frei -> forward -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.turnRight()`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -## Game: Command or sensor? - -::: spiel frage -titel: Game: Command or sensor? -intro: Does the line do something, or does it give a value? Eight rounds. -antworten: command | sensor - -runde: - mbot.forward(30) -frage: Command or sensor? -richtig: command -> It drives 30 cm. That is doing something. - -runde: - mbot.getDistance() -frage: Command or sensor? -richtig: sensor -> It gives the distance in cm, for example 38. - -runde: - mbot.turnRight() -frage: Command or sensor? -richtig: command -> Turning is doing something. - -runde: - mbot.getLine() -frage: Command or sensor? -richtig: sensor -> It gives 0 or a number greater than 0. - -runde: - mbot.playTone(440, 300) -frage: Command or sensor? -richtig: command -> A tone is an output. - -runde: - mbot.isButtonA() -frage: Command or sensor? -richtig: sensor -> It gives true or false: is button A pressed? - -runde: - mbot.showText("Hello") -frage: Command or sensor? -richtig: command -> Showing something is doing something. - -runde: - mbot.getBrightness() -frage: Command or sensor? -richtig: sensor -> It measures how bright it is, from 0 to 100. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain the difference between a command and a sensor. → Commands and sensors -- I can build a sensor into a condition. → Wall ahead? -::: - -## Quiz - -::: quiz -? The wall is 12 cm away. Is `mbot.getDistance() < 20` true or false? -→ Wall ahead? -- [x] true -- [ ] false -> 12 is less than 20, so the condition is true. -? The mBot2 is standing on a bright floor. What does `mbot.getLine()` give you? -→ Commands and sensors -- [x] 0 -- [ ] 15 -- [ ] the distance in cm -> 0 means: none of the four probes sees anything dark. -? Why is the sensor not a box of its own in the structure chart? -→ Wall ahead? -- [x] It does nothing, it only gives a value. You need that value in a condition. -- [ ] Because the mBot2 would get too slow otherwise. -- [ ] Because sensors only work in Java. -> Commands are statements. Sensors are values, just like a variable. -::: diff --git a/web/lessons/en/visubot/05-bis-zur-wand.md b/web/lessons/en/visubot/05-bis-zur-wand.md deleted file mode 100644 index 97bde0cc..00000000 --- a/web/lessons/en/visubot/05-bis-zur-wand.md +++ /dev/null @@ -1,207 +0,0 @@ ---- -titel: Up to the wall -bereich: Sensors and decisions -kurz: The mBot2 measures the distance and stops in time. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the distance with `mbot.getDistance()` -- decide when to use a while loop instead of a for loop -- find a goal with the line sensor -::: - -## How far away is the wall? - -So far you told the mBot2 exactly how far to drive. Now it should notice by itself when to stop. - -That is what the ultrasonic sensor is for. `mbot.getDistance()` gives the distance to the next wall in centimetres, for example `38`. You can use it like a number: - -```java -mbot.getDistance() > 15 -``` - -This is a **condition**: it is true as long as the wall is more than 15 cm away. - -> Start the [simulation](#zeige-simulation): below the world you always see the current distance. When the mBot2 gets closer to the wall, the number gets smaller. - -## Three worlds, one program - -This exercise has **three worlds**. In each one the wall is somewhere else, and the mBot2 should stop at the goal with the green flag every time, without bumping into it. - -A fixed number of **move forward** only works in one world. You need a loop that keeps driving as long as the condition is true: the **while loop**. - -::: übung vb-bis-wand -titel: Up to the wall -auftrag: The mBot2 should drive forward until the wall is at most 15 cm away. Your program must work in all three worlds. Above the simulation you choose which world you see. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Distance** onto the loop. Then only the comparison is missing. -tipp: A while loop with the condition `mbot.getDistance() > 15`, with `mbot.forward()` inside. -lösung: vb-bis-wand-loesung.visustruct -::: - -::: ludwig -A loop that checks a condition before it drives is like me at the feeding trough: first I check whether there is anything left, and only then do I keep eating. -::: - -## To the goal - -Now it is the second sensor's turn. The goal is a black square with a green flag. When the mBot2 drives onto it, its line sensor sees it: `mbot.getLine()` is then no longer `0`. - -::: übung vb-bis-ziel -titel: Drive to the goal -auftrag: The mBot2 should keep driving forward until it is on the goal. Again there are two worlds. -welt: ziel-kurz, ziel-lang -braucht: whileSchleife -tipp: The loop should run as long as the line sensor sees **nothing**. Drag the sensor **Line** onto the while loop. -tipp: The condition is `mbot.getLine() == 0`. Inside the loop is `mbot.forward()`. -lösung: vb-bis-ziel-loesung.visustruct -::: - -## What does the loop check? - -> 📘 **More without the robot:** VisuStruct has the lesson ["While loop"](?visustruct&lektion=11-while-schleife). It opens in a new window. - -::: übung vb-fehler-05-bis-zur-wand -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Up to the wall", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-bis-zur-wand-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.forward()`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Game: How often does it drive? - -::: spiel frage -titel: Game: How often does it drive? -intro: Each drive is 10 cm long. Work out how often the mBot2 drives forward before the loop stops. Six rounds. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 40 cm away. How often does it drive forward? -- 2 times -- [x] 3 times -- 4 times -> 40, 30 and 20 are greater than 15: three drives. At 10 the loop stops. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 15 cm away. How often does it drive forward? -- [x] not at all -- once -- 2 times -> 15 is not greater than 15. The condition is false right at the start. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 50 cm away. Where does it stop? -- 15 cm from the wall -- [x] 10 cm from the wall -- 0 cm, it bumps into it -> 50, 40, 30, 20: four drives. Then the wall is 10 cm away and the loop stops. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } -frage: The mBot2 is already standing on the black goal. How often does it drive forward? -- [x] not at all -- once -- forever -> On black, `mbot.getLine()` is not 0. The condition is false straight away. - -runde: -frage: The mBot2 should drive forward exactly five times. Which loop fits best? -- [x] a for loop -- a while loop with `mbot.getDistance()` -- no loop at all -> The number is known in advance. That is what the for loop is for. - -runde: -frage: The mBot2 should drive until it almost touches the wall. Which loop fits? -- a for loop that runs ten times -- [x] a while loop with `mbot.getDistance()` -- no loop at all -> You don't know in advance how far away the wall is. The while loop checks on the way. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Grey is a wall. Tap the field where the mBot2 is at the end. Each field is 30 cm, and the sensor is at the front of the mBot2. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> It drives as long as more than 15 cm ahead are free. It stops right in front of the wall. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,1 -> However far away the wall is, the while loop stops it right in front of it. - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(60); -karte: ##### | ..... | ..... | ..... | ^.... -ziel: 2,2 -> With more than 45 cm it stops one field earlier, turns and drives two fields to the right. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,0 -> It drives until its line sensor sees black. Then it is standing on the black field. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the distance with `mbot.getDistance()`. → How far away is the wall? -- I can decide when to use a while loop instead of a for loop. → Three worlds, one program -- I can find a goal with the line sensor. → To the goal -::: - -## Quiz - -::: quiz -? The mBot2 is 40 cm in front of the wall. How often does it drive forward with `while (mbot.getDistance() > 15) mbot.forward();` if each move is 10 cm? -→ How far away is the wall? -- [ ] not at all -- [ ] once -- [x] until the wall is at most 15 cm away -- [ ] always exactly five times -> The while loop checks the condition before every move. When the wall is close enough, it stops. -? Why is a for loop with a fixed count not enough here? -→ Three worlds, one program -- [x] Because the wall is at a different distance in every world. -- [ ] Because a for loop must not contain statements. -- [ ] Because the mBot2 does not understand for loops. -> The for loop knows beforehand how often it runs. The while loop decides on the way. -::: diff --git a/web/lessons/en/visubot/05-erst-fahren-dann-pruefen.md b/web/lessons/en/visubot/05-erst-fahren-dann-pruefen.md deleted file mode 100644 index 04b18a7e..00000000 --- a/web/lessons/en/visubot/05-erst-fahren-dann-pruefen.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -titel: Drive first, then check -bereich: Sensors and decisions -kurz: The do-while loop always drives at least once and only checks afterwards. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the do-while loop checks its condition only after the body -- compare a while loop and a do-while loop -- write a do-while loop in Java -::: - -## Drive first, then check - -The while loop from Lesson 5 checks **first** and then drives. If the wall is already right in front of the mBot2, it does not drive off at all. It is a **pre-test loop**: the condition is at the top, in the head. - -The **do-while loop** is a **post-test loop**. The condition is at the bottom, in the foot of the box: - -1. Run the body once. -2. Check the condition. -3. If it is true, start again at 1. If it is false, carry on below the loop. - -That is why the body **always runs at least once**. When you click the loop, the word `while` is in front of the text field in the Object Inspector: the condition means "repeat as long as …". - -| Distance at the start | While loop | Do-while loop | -|---|---|---| -| 50 cm | drives until 15 cm | drives until 15 cm | -| 10 cm | does **not drive at all** | drives 10 cm **once** | - -::: übung vb-do-while -titel: At least one step -auftrag: Build a structogram with a do-while loop: the mBot2 drives forward as long as the distance is greater than 15 cm, but **at least once**. Then it plays a tone. -messwerte: getDistance=10 -> forward, tone -welt: wand-kurz -> tone -braucht: doUntilSchleife -tipp: Drag the **do-while loop** from the Elements into the structogram. **Drive forward** goes into the box, the condition `mbot.getDistance() > 15` into the foot. -tipp: Below the loop (not inside it) goes `mbot.playTone(440, 300)`. -lösung: vb-do-while-loesung.visustruct -::: - -> When checking, the wall is once only 10 cm in front of the mBot2. A while loop would not drive at all then. - -## Now in Java: counting steps - -In Java, `do` is at the top and `while (…)` with a semicolon at the bottom: - -```java -do { - mbot.forward(); -} while (mbot.getDistance() > 15); -``` - -::: übung vb-java-schritte-zaehlen -titel: Count the steps -code: java -auftrag: Write in Java: create `int steps = 0`. In a do-while loop the mBot2 drives forward and counts `steps` up by one, as long as the distance is greater than 15 cm. Then the screen shows for example **Steps: 3**. -welt: wand-kurz -> screen "Steps: 3" -messwerte: getDistance=10 -> forward, screen "Steps: 1" -braucht: doUntilSchleife -tipp: `do { mbot.forward(); steps = steps + 1; } while (mbot.getDistance() > 15);` -tipp: After the loop `mbot.showText("Steps: " + steps);`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Do-while loop"](?visustruct&lektion=12-do-while-schleife). It opens in a new window. - -::: übung vb-fehler-05-erst-fahren-dann-pruefen -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Count the steps", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct -welt: wand-kurz -> screen "Steps: 3" -messwerte: getDistance=10 -> forward, screen "Steps: 1" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `mbot.showText("Steps: " + steps)`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -## Game: Head or foot? - -::: spiel frage -titel: Game: Head or foot? -intro: While or do-while: how often does the mBot2 drive forward? Each drive is 10 cm long. Six rounds. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall is 10 cm in front of the mBot2. How often does it drive forward? -- [x] not at all -- once -- 2 times -> The while loop checks first. 10 is not greater than 15. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: The wall is 10 cm in front of the mBot2. How often does it drive forward? -- not at all -- [x] once -- 2 times -> The do-while loop drives first and checks afterwards. It always drives once. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: The wall is 40 cm in front of the mBot2. How often does it drive forward? -- 2 times -- [x] 3 times -- 4 times -> After the first drive it is 30 cm, then 20, then 10. At 10 it stops. Here both loops drive the same number of times. - -runde: - int steps = 0; - do { - mbot.forward(); - steps = steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + steps); -frage: The wall is 10 cm in front of the mBot2. What does the screen show? -- Steps: 0 -- [x] Steps: 1 -- Steps: 2 -> The body runs once, so `steps` counts up once. - -runde: -frage: Which loop is foot-controlled? -- the while loop -- [x] the do-while loop -- the for loop -> Its condition is at the bottom, in the foot. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15) -frage: Why doesn't this code run? -- `do` must be written with a capital letter. -- [x] The semicolon after `while (…)` is missing. -- The condition must be at the top. -> The do-while loop ends with `} while (condition);`. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() < 15); -frage: Should drive forward at least once, and keep going as long as the wall is more than 15 cm away. -fehler: 3 -besser: } while (mbot.getDistance() > 15); -> "More than 15 cm" means `> 15`. - -runde: - do - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Should drive forward at least once, and keep going as long as the wall is more than 15 cm away. -fehler: 1 -besser: do { -> The opening curly bracket after `do` is missing. - -runde: - int steps = 0; - do { - mbot.forward(); - steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + steps); -frage: Should count the steps and show them at the end. -fehler: 4 -besser: steps = steps + 1; -> `steps + 1` only calculates, it doesn't store anything. The result has to go back into `steps` with `=`. - -runde: - int steps = 0; - do { - mbot.forward(); - steps = steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + Steps); -frage: Should count the steps and show them at the end. -fehler: 6 -besser: mbot.showText("Steps: " + steps); -> Java tells capital and small letters apart. There is no `Steps`. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Grey is a wall. While or do-while: tap the field where the mBot2 is at the end. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,2 -> The wall is already close. The while loop checks first and doesn't set off at all. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 45); -karte: ##### | ..... | ..^.. -ziel: 2,1 -> The do-while loop drives once first and checks afterwards. One field further on it stops. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); - mbot.turnLeft(); - mbot.forward(30); -karte: ##### | ..... | ..^.. -ziel: 1,1 -> One field forward, then the wall is right in front. Then left and one more field. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> If the wall is far away, while and do-while drive the same distance: right up to the wall. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the do-while loop checks its condition only after the body. → Drive first, then check -- I can compare a while loop and a do-while loop. → Drive first, then check -- I can write a do-while loop in Java. → Now in Java: counting steps -::: - -## Quiz - -::: quiz -? Where is the condition of a do-while loop? -→ Drive first, then check -- [ ] at the top, in the head -- [x] at the bottom, in the foot -- [ ] nowhere -> Post-test means: first the body, then the condition. -? The wall is 10 cm in front of the mBot2. How often does `do { mbot.forward(); } while (mbot.getDistance() > 15);` drive? -→ Drive first, then check -- [ ] not at all -- [x] once -- [ ] until it bumps -> The body always runs at least once. After that the condition is false. -? What comes after `while (…)` at the end of a do-while loop in Java? -→ Now in Java: counting steps -- [x] a semicolon `;` -- [ ] a curly bracket `{` -- [ ] nothing -> `} while (condition);` ends with a semicolon. -::: diff --git a/web/lessons/en/visubot/05-zaehlen-und-summieren.md b/web/lessons/en/visubot/05-zaehlen-und-summieren.md deleted file mode 100644 index ca2ef274..00000000 --- a/web/lessons/en/visubot/05-zaehlen-und-summieren.md +++ /dev/null @@ -1,154 +0,0 @@ ---- -titel: Counting and adding up -bereich: Sensors and decisions -kurz: Count along with a variable and add values together. ---- - -## Skills I will gain now - -::: kompetenzen -- create a counter variable and count it up in a loop -- build a sum in a loop -- show the result on the screen after the loop -::: - -## Counting along - -How many steps does the mBot2 drive to the wall? You don't know in advance, the world is different every time. So the mBot2 should **count along**. You need three boxes for that: - -1. **Before** the loop: `int steps = 0`. At the start nothing has been counted yet. -2. **Inside** the loop: `steps = steps + 1`. One more after every step. -3. **After** the loop: show the result, for example with `mbot.showText("Steps: " + steps)`. - -Read `steps = steps + 1` from right to left: take the old value of `steps`, add 1 and store the result in `steps` again. A variable like this is called a **counter variable**, or counter for short. - -> Careful: if `int steps = 0` is **inside** the loop, the counter starts again at 0 in every pass. Then the result is always 1. - -::: angeberwissen -Your phone counts your steps exactly like this: with every step it calculates `steps = steps + 1`. I prefer counting my blades of grass. I'm at 4 million. -::: - -::: übung vb-schritte-welt -titel: Steps to the wall -auftrag: Build a structure chart: the mBot2 drives forward as long as the distance is greater than 15. It counts its steps along the way. Then it shows, for example, **Steps: 3** on the screen. There are three worlds. -welt: wand-kurz -> screen "Steps: 3" -welt: wand-mittel -> screen "Steps: 7" -welt: wand-lang -> screen "Steps: 12" -braucht: whileSchleife -tipp: The first box is `int steps = 0`. Then comes the while loop with `mbot.getDistance() > 15`. -tipp: Inside the loop are **drive forward** and `steps = steps + 1`. After the loop is `mbot.showText("Steps: " + steps)`. -lösung: vb-schritte-welt-loesung.visustruct -::: - -## Adding up - -A counter always grows by 1. A **sum** grows by different values. The mBot2 drives a spiral: first 10 cm, then 20, then 30, then 40. How far has it driven in total? - -| Pass | `side` | `total` afterwards | -|---|---|---| -| before | 10 | 0 | -| 1 | 10 | 0 + 10 = 10 | -| 2 | 20 | 10 + 20 = 30 | -| 3 | 30 | 30 + 30 = 60 | -| 4 | 40 | 60 + 40 = 100 | - -The pattern is the same as for counting. Only instead of 1 you now add the value that comes in: `total = total + side`. - -## Now in Java: spiral with distance - -::: übung vb-java-spirale-strecke -titel: Spiral with distance -code: java -auftrag: Write in Java: create `int side = 10` and `int total = 0`. Four times: drive `side` cm forward, turn right, add `side` to `total`, make `side` 10 bigger. Then show **Distance: 100 cm** on the screen. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Distance: 100 cm" -braucht: forSchleife -tipp: The for loop has four lines: `mbot.forward(side);`, `mbot.turnRight();`, `total = total + side;` and `side = side + 10;`. -tipp: After the loop comes `mbot.showText("Distance: " + total + " cm");`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -::: übung vb-fehler-05-zaehlen-und-summieren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Spiral with distance", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-zaehlen-und-summieren-start.visustruct -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Distance: 100 cm" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `total = total + side`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -## Game: what is in the variable at the end? - -::: spiel frage -titel: Game: what is in the variable at the end? -intro: Ludwig has written small programs. Work them out in your head. Five rounds. - -runde: -frage: `int z = 0`, then three times `z = z + 1`. What is `z`? -- 1 -- [x] 3 -- 0 -> Each pass adds 1: 1, 2, 3. - -runde: -frage: `int sum = 0`, then `sum = sum + 5` and `sum = sum + 7`. What is `sum`? -- 7 -- [x] 12 -- 5 -> 0 + 5 = 5, then 5 + 7 = 12. - -runde: -frage: A loop runs four times. Inside it are `int z = 0` and `z = z + 1`. What is `z` at the end? -- 4 -- [x] 1 -- 0 -> The counter is set back to 0 in every pass. It belongs before the loop. - -runde: -frage: `int total = 0`. The mBot2 drives 20, 20 and 30 cm and adds each one. What is `total`? -- 30 -- 20 -- [x] 70 -> 20 + 20 + 30 = 70. - -runde: -frage: `int steps = 10`, then twice `steps = steps + 1`. What is `steps`? -- 2 -- [x] 12 -- 11 -> Here the counter starts at 10, not at 0. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a counter variable and count it up in a loop. → Counting along -- I can build a sum in a loop. → Adding up -- I can show the result on the screen after the loop. → Now in Java: spiral with distance -::: - -## Quiz - -::: quiz -? Where do you create the counter variable `int steps = 0`? -→ Counting along -- [x] Before the loop -- [ ] Inside the loop -- [ ] After the loop -> Inside the loop it would become 0 again in every pass. -? What does `steps = steps + 1` do? -→ Counting along -- [ ] It compares steps with steps + 1. -- [x] It takes the old value, adds 1 and stores the result. -- [ ] It sets steps to 1. -? How do you add the side length `side` to the sum `total`? -→ Adding up -- [ ] `total = side` -- [ ] `side = total + side` -- [x] `total = total + side` -::: diff --git a/web/lessons/en/visubot/06-abstand.md b/web/lessons/en/visubot/06-abstand.md deleted file mode 100644 index 8b8f2d63..00000000 --- a/web/lessons/en/visubot/06-abstand.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -titel: The ultrasonic sensor -bereich: Sensors and decisions -kurz: Measure the distance, keep your distance, and do it all in Java. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what `mbot.getDistance()` measures -- put one branch inside another -- write a while loop with the distance in Java -::: - -## How does the mBot2 measure distance? - -::: mbot2 -focus: ultrasonic -view: front -::: - -The [ultrasonic sensor]{part=ultrasonic} sends out a very high sound and waits for the echo. The longer the echo takes, the further away the wall is. `mbot.getDistance()` gives you the distance in **centimetres**, from about 3 to 300. - -| Sensor | Text | Value | -|---|---|---| -| Distance | `mbot.getDistance()` | whole number in cm, e.g. `38` | - -> In the [simulation](#zeige-simulation) the current distance is always shown below the world. With a real mBot2 you can hold your hand in front of the sensor and watch the number change. - -## Keep your distance in the structure chart - -The mBot2 should behave like a dog on a lead: if your hand is too close (under 10 cm), it backs away. If it is too far away (over 20 cm), it comes closer. In between, it stays where it is. - -For this you need two branches, one inside the other. The outer one checks “too close?”, the inner one in the false branch checks “too far?”. - -::: übung vb-abstand-halten -titel: Keep your distance -auftrag: Build a structure chart: if the distance is less than 10, the mBot2 drives back. Otherwise: if the distance is greater than 20, it drives forward. Otherwise it does nothing. -messwerte: getDistance=5 -> backward -messwerte: getDistance=35 -> forward -messwerte: getDistance=15 -> none -braucht: verzweigung -tipp: Drag a branch into the structure chart and the sensor **Distance** onto it. Write `< 10` after it. The true branch gets **move back**. -tipp: The false branch gets a second branch with `mbot.getDistance() > 20` and **move forward** in its true branch. Its false branch stays empty. -lösung: vb-abstand-halten-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance to 5, 35 and 15 cm one after the other and looks at what your mBot2 does. - -## Now in Java - -In the [Code](#zeige-codelive) area you already see your structure chart as Java code. A branch is called `if` there, the false branch is `else`: - -```java -if (mbot.getDistance() < 10) { - mbot.backward(); -} else { - ... -} -``` - -You type a sensor like a command: `mbot.` and then `getDistance()`. After the dot the editor suggests all commands and sensors. - -::: übung vb-java-wand-piep -titel: To the wall and beep -code: java -auftrag: Write in Java: the mBot2 drives forward as long as the wall is more than 15 cm away. Then it plays a tone. There are three worlds. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: A while loop in Java is `while (condition) { … }`. The condition is `mbot.getDistance() > 15`. -tipp: Inside the loop is `mbot.forward();`, after the loop `mbot.playTone(440, 300);`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -::: übung vb-fehler-06-abstand -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To the wall and beep", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-abstand-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be `mbot.playTone(440, 300)`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should do and tap the wrong line before time runs out. Six rounds, 30 seconds each. -zeit: 30 - -runde: - if (mbot.getDistance() < 10) { - mbot.forward(); - } - if (mbot.getDistance() > 20) { - mbot.forward(); - } -frage: Under 10 cm it should drive back, over 20 cm forward. -fehler: 2 -besser: mbot.backward(); -> If the hand is too close, it has to go back, not forward. - -runde: - while (mbot.getDistance() < 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive forward as long as the wall is more than 15 cm away, then beep. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> "More than 15 cm away" means `> 15`. - -runde: - while (mbot.getDistance > 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive to the wall and then beep. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> A sensor is a method. Without brackets, Java can't find it. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } els { - mbot.forward(); - } -frage: Under 10 cm it should drive back, otherwise forward. -fehler: 3 -besser: } else { -> The false branch is called `else`. - -runde: - while (mbot.getDistance() > 15) { - mbot.turnRight(); - } - mbot.playTone(440, 300); -frage: Should drive to the wall and then beep. -fehler: 2 -besser: mbot.forward(); -> It should drive forward, not turn. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } else { - if (mbot.getDistance() < 20) { - mbot.forward(); - } - } -frage: Under 10 cm back, over 20 cm forward, in between stand still. -fehler: 4 -besser: if (mbot.getDistance() > 20) { -> It should only drive forward if the hand is more than 20 cm away. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what `mbot.getDistance()` measures. → How does the mBot2 measure distance? -- I can put one branch inside another. → Keep your distance in the structure chart -- I can write a while loop with the distance in Java. → Now in Java -::: - -## Quiz - -::: quiz -? `mbot.getDistance()` gives `8`. What does the program from “Keep your distance” do? -→ Keep your distance in the structure chart -- [x] It drives back. -- [ ] It drives forward. -- [ ] It stays where it is. -> 8 is less than 10, so the first condition is already true. -? What is the false branch of a branch called in Java? -→ Now in Java -- [ ] `then` -- [x] `else` -- [ ] `otherwise` -> `if` is the start, `else` the false branch. -::: diff --git a/web/lessons/en/visubot/06-konstanten-final.md b/web/lessons/en/visubot/06-konstanten-final.md deleted file mode 100644 index 91468c84..00000000 --- a/web/lessons/en/visubot/06-konstanten-final.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -titel: Fixed values with final -bereich: Sensors and decisions -kurz: Create a constant so that an important number appears in only one place. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what a constant is -- create a constant in the structure chart and in Java -- use a constant in a condition and in a command -::: - -## One number in many places - -Ludwig has written a program for the hallway: the mBot2 drives up to 20 cm from the wall, turns around and drives up to 20 cm from the other wall. - -::: diagramm vb-l06k-ohne.visustruct -There and back. The number 20 appears in two places. -::: - -Now the mBot2 should stop at 30 cm instead. Ludwig has to find and change every 20. If he forgets one, the mBot2 stops once at 30 and once at 20 cm. In long programs this happens quickly. - -::: angeberwissen -Professionals call a number that just sits in the program a **magic number**. Magic, because nobody remembers what it means. The only magic I like is magic grass. -::: - -## Creating a constant - -A **constant** is better. It is a variable whose value stays the same once it is set. You create it once at the very top and after that you only use its name. - -| | Structure chart | Java | -|---|---|---| -| create | `const int DISTANCE = 20` | `final int DISTANCE = 20;` | -| use | `mbot.getDistance() > DISTANCE` | `mbot.getDistance() > DISTANCE` | - -In the structure chart you write `const` in front, in Java `final`. Both mean: this value does not change any more. Names of constants are written in **capital letters**, so you can spot them right away. - -::: diagramm vb-l06k-mit.visustruct -The same program with the constant DISTANCE. For 30 cm you only change the first line. -::: - -::: übung vb-konstante-wand -titel: Wall with a constant -auftrag: Create the constant `DISTANCE` with the value 20. The mBot2 drives forward as long as the distance is greater than `DISTANCE`. Then it plays a tone. There are three worlds. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: The first box is a statement with `const int DISTANCE = 20`. Below it comes a while loop. -tipp: The loop condition is `mbot.getDistance() > DISTANCE`. Inside the loop is **drive forward**, after the loop **play tone**. -lösung: vb-konstante-wand-loesung.visustruct -::: - -## Now in Java: square with a constant - -A constant also fits into a command. With `final int SIDE = 30;`, `mbot.forward(SIDE);` drives exactly 30 cm. If the square should get bigger, you only change that one line. - -::: übung vb-java-konstante-seite -titel: Square with a constant -code: java -auftrag: Write in Java: create the constant `SIDE` with the value 30. Then use a for loop to drive a square with side length `SIDE`. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: The first line is `final int SIDE = 30;`. Then comes `for (int i = 0; i < 4; i++) { … }`. -tipp: Inside the loop are `mbot.forward(SIDE);` and `mbot.turnRight();`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -::: übung vb-fehler-06-konstanten-final -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Square with a constant", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-konstanten-final-start.visustruct -fahrt: 4 x (forward 30, turnRight) -tipp: The error message names an unknown variable. What is the constant called where it is created? Constants are written in capital letters. -tipp: It should be `mbot.forward(SIDE)`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -## Game: constant or variable? - -::: spiel frage -titel: Game: constant or variable? -intro: Ludwig is sorting values. If the value stays the same in the whole program, it is a constant. If it changes, it is a variable. Six rounds. -frage: Constant or variable? -antworten: Constant | Variable - -runde: -frage: The number of corners of a square -richtig: Constant -> A square always has four corners. - -runde: -frage: The distance the ultrasonic sensor is measuring right now -richtig: Variable -> When the mBot2 drives, the distance changes all the time. - -runde: -frage: The minimum distance at which the mBot2 should always stop -richtig: Constant -> You set it once, for example `final int DISTANCE = 20;`. - -runde: -frage: The number of steps the mBot2 has already driven -richtig: Variable -> It grows by 1 after every step. - -runde: -frage: The pitch of the warning tone, always 440 Hz -richtig: Constant -> The warning tone always sounds the same. - -runde: -frage: The battery level in percent -richtig: Variable -> The battery slowly runs down while driving. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what a constant is. → Creating a constant -- I can create a constant in the structure chart and in Java. → Creating a constant -- I can use a constant in a condition and in a command. → Now in Java: square with a constant -::: - -## Quiz - -::: quiz -? What is a constant? -→ Creating a constant -- [ ] A variable that changes in every pass -- [x] A variable whose value stays the same once it is set -- [ ] A sensor that always measures the same value -> You set a constant once. After that you only use its name. -? How do you create the constant `DISTANCE` with the value 20 in Java? -→ Creating a constant -- [ ] `const DISTANCE = 20;` -- [x] `final int DISTANCE = 20;` -- [ ] `int final = 20;` -> In Java, `final` comes before the data type. In the structure chart you write `const`. -? Why is a constant better than the number 20 in many places? -→ Creating a constant -- [ ] Because the program drives faster -- [x] Because you only have to change the value in one place -- [ ] Because the sensor measures more precisely -::: diff --git a/web/lessons/en/visubot/06-nah-mittel-weit.md b/web/lessons/en/visubot/06-nah-mittel-weit.md deleted file mode 100644 index 3a8a8ae6..00000000 --- a/web/lessons/en/visubot/06-nah-mittel-weit.md +++ /dev/null @@ -1,189 +0,0 @@ ---- -titel: Near, medium or far? -bereich: Sensors and decisions -kurz: Build a branch with three cases and write it in Java with else if. ---- - -## Skills I will gain now - -::: kompetenzen -- build a branch with three cases -- read and write `else if` in Java -- check the conditions in the right order -::: - -## Three cases instead of two - -Many cars have a **parking aid**. If the wall is far away, everything is free. When it gets closer, it says: slow. When it is very close: stop! - -A branch has only two branches, yes and no. For three cases you put a second branch into the no branch of the first one: - -1. Is the distance less than 10? Then **Stop**. -2. Otherwise: is the distance less than 30? Then **Slow**. -3. Otherwise: **Free**. - -::: diagramm vb-l06n-einparkhilfe.visustruct -The parking aid. The second question is only asked if the answer to the first one was no. -::: - -Try it in the [simulation](#zeige-simulation): open the diagram in the editor and set the distance to 5, 20 and 50 cm. - -> 📘 **More without the robot:** VisuStruct has the lesson ["Branch"](?visustruct&lektion=08-verzweigung). It opens in a new window. - -## Speed by distance in the structure chart - -::: übung vb-tempo-abstand -titel: Speed by distance -auftrag: Build a structure chart with three cases: if the distance is less than 10, the mBot2 drives 5 cm backward. Otherwise: if the distance is less than 30, it drives 5 cm forward. Otherwise it drives 20 cm forward. -messwerte: getDistance=5 -> backward 5 -messwerte: getDistance=20 -> forward 5 -messwerte: getDistance=50 -> forward 20 -braucht: verzweigung -tipp: Build it like the parking aid: a branch with `mbot.getDistance() < 10` and in its no branch a second one with `mbot.getDistance() < 30`. -tipp: The commands are `mbot.backward(5)`, `mbot.forward(5)` and `mbot.forward(20)`. The last one goes into the no branch of the second branch. -lösung: vb-tempo-abstand-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance to 5, 20 and 50 cm one after the other. - -## Now in Java: else if - -Look at the [Code](#zeige-codelive) area. The branch inside the no branch looks like this: - -```java -if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} else { - if (mbot.getDistance() < 30) { - mbot.showText("Slow"); - } else { - mbot.showText("Free"); - } -} -``` - -In Java you may write this more briefly. The `if` moves right behind the `else`: - -```java -if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} else if (mbot.getDistance() < 30) { - mbot.showText("Slow"); -} else { - mbot.showText("Free"); -} -``` - -Both mean exactly the same. With `else if` it is easier to read, though. You still always write the curly brackets `{ }`. - -::: übung vb-java-tageszeit -titel: Day or night? -code: java -auftrag: Write in Java with `else if`: if the brightness is less than 20, the mBot2 shows **Night**. Otherwise: if it is less than 60, it shows **Dusk**. Otherwise it shows **Day**. -messwerte: getBrightness=10 -> none, screen "Night" -messwerte: getBrightness=40 -> none, screen "Dusk" -messwerte: getBrightness=80 -> none, screen "Day" -braucht: verzweigung -tipp: You read the brightness with `mbot.getBrightness()`. The first condition is `mbot.getBrightness() < 20`. -tipp: `if (…) { mbot.showText("Night"); } else if (mbot.getBrightness() < 60) { mbot.showText("Dusk"); } else { mbot.showText("Day"); }` -lösung: vb-java-tageszeit-loesung.visustruct -::: - -::: übung vb-fehler-06-nah-mittel-weit -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Day or night?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-nah-mittel-weit-start.visustruct -messwerte: getBrightness=10 -> none, screen "Night" -messwerte: getBrightness=40 -> none, screen "Dusk" -messwerte: getBrightness=80 -> none, screen "Day" -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be `mbot.showText("Night")`. -lösung: vb-java-tageszeit-loesung.visustruct -::: - -## The order matters - -Ludwig rebuilt the parking aid and swapped the questions: - -```java -if (mbot.getDistance() < 30) { - mbot.showText("Slow"); -} else if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} -``` - -At 5 cm his mBot2 shows **Slow** instead of **Stop**. That is because 5 is also less than 30. The first condition is already true, so the second one is never checked. **Stop** never appears. - -> Remember: with `<`, check the smallest value first, then the next bigger one. - -::: spiel frage -titel: Game: what does the parking aid show? -intro: Ludwig's parking aid from above: under 10 cm **Stop**, under 30 cm **Slow**, otherwise **Free**. What does it show? Six rounds. -frage: What does the parking aid show? -antworten: Stop | Slow | Free - -runde: -frage: The distance is **5 cm**. -richtig: Stop -> 5 is less than 10. The very first question is true. - -runde: -frage: The distance is **25 cm**. -richtig: Slow -> 25 is not less than 10, but it is less than 30. - -runde: -frage: The distance is **80 cm**. -richtig: Free -> Both questions are false, so the last no branch is left. - -runde: -frage: The distance is exactly **10 cm**. -richtig: Slow -> 10 is not less than 10. The first question is false, the second true. - -runde: -frage: The distance is exactly **30 cm**. -richtig: Free -> 30 is not less than 30. Both questions are false. - -runde: -frage: The distance is **9 cm**. -richtig: Stop -> 9 is less than 10. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a branch with three cases. → Three cases instead of two -- I can read and write `else if` in Java. → Now in Java: else if -- I can check the conditions in the right order. → The order matters -::: - -## Quiz - -::: quiz -? How many branches do you need in the structure chart for three cases? -→ Three cases instead of two -- [ ] One -- [x] Two, the second in the no branch of the first -- [ ] Three side by side -> Each branch splits into two. The second one splits the no branch again. -? What does `else if` mean in Java? -→ Now in Java: else if -- [x] Otherwise, check another condition -- [ ] Repeat the condition -- [ ] Check both conditions at the same time -> `else if` is the short way of writing an `if` inside the `else` branch. -? Ludwig checks `< 30` first and then `< 10`. What happens at 5 cm? -→ The order matters -- [ ] Stop appears. -- [x] Slow appears. -- [ ] Nothing appears. -> 5 is also less than 30. The second condition is no longer checked. -::: diff --git a/web/lessons/en/visubot/07-linie.md b/web/lessons/en/visubot/07-linie.md deleted file mode 100644 index 1518879f..00000000 --- a/web/lessons/en/visubot/07-linie.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -titel: The line sensor -bereich: Sensors and decisions -kurz: Detect dark lines on the floor and don't fall off the table. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the number from `mbot.getLine()` means -- drive up to a line -- count steps with a variable in Java -::: - -## Four probes under the nose - -::: mbot2 -focus: lineSensor -view: bottom -::: - -The [line sensor]{part=lineSensor} has four probes side by side. Each one shines light on the floor and measures how much comes back. Black swallows the light, white reflects it. - -| Sensor | Text | Value | -|---|---|---| -| Line | `mbot.getLine()` | `0` = all bright, greater than `0` = at least one probe sees black | - -To start with, this is all you need: **`mbot.getLine() == 0` means “no line”**, `mbot.getLine() != 0` means “line found”. - -::: angeberwissen -`mbot.getLine()` is really a binary number with four digits, one for each probe: `1111` (that is 15) means “all four see black”, `0110` (6) “only the middle two”. With that an mBot2 can even follow a line. -::: - -## Turn round at the edge in the structure chart - -Imagine a table with black tape along the edge. The mBot2 should drive forward until it sees the tape and then turn round so it doesn't fall off. - -::: übung vb-rand -titel: Turn round at the edge -auftrag: The mBot2 drives forward as long as `mbot.getLine() == 0`. Then it turns 180°. There are two worlds with different distances. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Line** onto it. Write `== 0` after it. **move forward** goes inside the loop. -tipp: After the loop (below it, not inside) is `mbot.turnRight(180)`. -lösung: vb-rand-loesung.visustruct -::: - -## Now in Java: there and back - -Now the mBot2 should drive exactly back to the start after turning round. To do that it counts with a **variable** how often it drove forward. - -```java -int steps = 0; -while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; -} -``` - -::: übung vb-java-rand -titel: To the line and back -code: java -auftrag: Write in Java: the mBot2 drives to the line and counts its steps. Then it turns 180° and uses a for loop to drive the same number of steps back. -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -braucht: whileSchleife, forSchleife -tipp: After the while loop: `mbot.turnRight(180);` and then `for (int i = 0; i < steps; i++) { … }`. -tipp: The for loop only contains `mbot.forward();`. -lösung: vb-java-rand-loesung.visustruct -::: - -::: übung vb-fehler-07-linie -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To the line and back", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-07-linie-start.visustruct -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `steps = steps + 1`. -lösung: vb-java-rand-loesung.visustruct -::: - -## Game: Line or no line? - -::: spiel frage -titel: Game: Line or no line? -intro: Where is the mBot2 standing, and is the condition true or false? Six rounds. -antworten: true | false - -runde: - mbot.getLine() == 0 -frage: The mBot2 is standing on white paper. Is the condition true? -richtig: true -> No sensor sees black, so `mbot.getLine()` gives 0. - -runde: - mbot.getLine() == 0 -frage: The two middle sensors are on the black tape. Is the condition true? -richtig: false -> Two sensors see black, so `mbot.getLine()` is greater than 0. - -runde: - mbot.getLine() != 0 -frage: Only the outer left sensor sees black. Is the condition true? -richtig: true -> One sensor is enough, then the value is no longer 0. - -runde: - mbot.getLine() != 0 -frage: The mBot2 is standing on a white floor. Is the condition true? -richtig: false -> On white the value is 0, and `0 != 0` is false. - -runde: - mbot.getLine() == 15 -frage: A cross line: all four sensors see black. Is the condition true? -richtig: true -> All four sensors give the binary number `1111`, which is 15. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; - } -frage: The line is 30 cm away, each drive is 10 cm long. Is `steps == 3` afterwards? -richtig: true -> After three drives it is on the line, and the loop stops. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getLine() != 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Should drive to the black edge and then turn round. -fehler: 1 -besser: while (mbot.getLine() == 0) { -> It should drive as long as there is **no** line, so `== 0`. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(); -frage: Should drive to the black edge and then turn round. -fehler: 4 -besser: mbot.turnRight(180); -> Turning round means 180°. Without a number it only turns 90°. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps - 1; - } -frage: Should drive to the line and count the steps. -fehler: 4 -besser: steps = steps + 1; -> Counting means adding one, not taking one away. - -runde: - int steps = "0"; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; - } -frage: Should drive to the line and count the steps. -fehler: 1 -besser: int steps = 0; -> In quotation marks, the 0 is a text. Only a number fits into an `int`. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Black is tape. Tap the field where the mBot2 is at the end. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ..=.. | ..... | ..... | ..^.. -ziel: 2,1 -> It drives until it is standing on the black field. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } - mbot.turnRight(180); - mbot.forward(60); -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> To the edge, turn round and two fields back. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(30); - steps = steps + 1; - } - mbot.turnRight(180); - for (int i = 0; i < steps; i++) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..^.. -ziel: 2,3 -> It counts three steps to the line and drives exactly three back: at the start again. - -runde: - mbot.turnRight(); - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ^...= | ..... -ziel: 4,1 -> First turn right, then on to the black field on the far right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the number from `mbot.getLine()` means. → Four probes under the nose -- I can drive up to a line. → Turn round at the edge in the structure chart -- I can count steps with a variable in Java. → Now in Java: there and back -::: - -## Quiz - -::: quiz -? The mBot2 is on white paper. What does `mbot.getLine()` give? -→ Four probes under the nose -- [x] 0 -- [ ] 15 -- [ ] 1 -> No probe sees black, so 0. -? Which condition means “line found”? -→ Turn round at the edge in the structure chart -- [ ] `mbot.getLine() == 0` -- [x] `mbot.getLine() != 0` -- [ ] `mbot.getLine() < 0` -> `!=` means “not equal”. -::: diff --git a/web/lessons/en/visubot/07-und-oder.md b/web/lessons/en/visubot/07-und-oder.md deleted file mode 100644 index 64925a18..00000000 --- a/web/lessons/en/visubot/07-und-oder.md +++ /dev/null @@ -1,257 +0,0 @@ ---- -titel: And, or, not -bereich: Sensors and decisions -kurz: Check two sensors in one condition: and, or, not. ---- - -## Skills I will gain now - -::: kompetenzen -- combine two conditions with `&&` (and) -- combine two conditions with `||` (or) -- turn a condition around with `!` (not) -- write a combined condition in a Java loop -::: - -## Wall and line: and - -The mBot2 should only drive forward if the way ahead is clear **and** there is no line under it. Two sensors, one condition. In Java, **and** is written `&&`: - -```java -mbot.getDistance() > 15 && mbot.getLine() == 0 -``` - -The whole condition is only true if **both** parts are true: - -| clear ahead? | no line? | clear `&&` no line | -|---|---|---| -| true | true | **true** | -| true | false | false | -| false | true | false | -| false | false | false | - -A table like this is called a **truth table**. - -::: übung vb-und -titel: Clear and no line -auftrag: Build a structogram with a branch: if the distance is greater than 15 **and** `mbot.getLine() == 0`, the mBot2 drives forward. Otherwise it does nothing. -messwerte: getDistance=50, getLine=0 -> forward -messwerte: getDistance=10, getLine=0 -> none -messwerte: getDistance=50, getLine=6 -> none -braucht: verzweigung -tipp: Drag the **distance** sensor onto the branch and write `> 15` after it. Then type ` && ` and the second part `mbot.getLine() == 0`. -tipp: The true branch contains **drive forward**, the false branch stays empty. -lösung: vb-und-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance and the line to different values. Only if both fit may the mBot2 drive. - -## Button A or button B: or - -Now the mBot2 should beep when you press button A **or** button B. It is enough if one of them is pressed. In Java, **or** is written `||`. - -| Button A? | Button B? | A or B | -|---|---|---| -| true | true | **true** | -| true | false | **true** | -| false | true | **true** | -| false | false | false | - -::: übung vb-oder -titel: Button A or B -auftrag: Build a structogram: if button A **or** button B is pressed, the mBot2 plays a tone. Otherwise it does nothing. -messwerte: isButtonA=true, isButtonB=false -> tone -messwerte: isButtonA=false, isButtonB=true -> tone -messwerte: isButtonA=false, isButtonB=false -> noTone -braucht: verzweigung -tipp: The condition of the branch is `mbot.isButtonA() || mbot.isButtonB()`. -tipp: The true branch contains `mbot.playTone(440, 300)`, the false branch stays empty. -lösung: vb-oder-loesung.visustruct -::: - -## Not: the opposite - -Sometimes you want the opposite of a condition. That is what `!` is for, read as **not**. `mbot.isButtonA()` is true when button A is pressed. `!mbot.isButtonA()` is true when it is **not** pressed. - -```java -while (!mbot.isButtonA()) { - mbot.pause(100); -} -mbot.forward(30); -``` - -The mBot2 waits as long as button A is not pressed. Then it drives off. - -## Now in Java: drive safely - -Now everything comes together. The mBot2 drives as long as the way is clear **and** no line comes. Whatever comes first, the wall or the edge: it stops and beeps. - -::: übung vb-java-sicher-fahren -titel: Drive safely -code: java -auftrag: Write in Java: the mBot2 drives forward as long as the distance is greater than 15 **and** `mbot.getLine() == 0`. Then it plays a tone. In two worlds a wall is waiting, in two worlds a black edge. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -braucht: whileSchleife -tipp: `while (mbot.getDistance() > 15 && mbot.getLine() == 0) { … }` -tipp: Inside the loop is `mbot.forward();`, after the loop `mbot.playTone(440, 300);`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Combining conditions"](?visustruct&lektion=09-bedingungen-verknuepfen). It opens in a new window. - -::: übung vb-fehler-07-und-oder -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Drive safely", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-07-und-oder-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.playTone(440, 300)`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -## Game: True or false? - -::: spiel frage -titel: Game: True or false? -intro: Ludwig puts the mBot2 into a situation. Is the condition true or false? Eight rounds. -antworten: true | false - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: The wall is 50 cm away, there is no line underneath. -richtig: true -> Both parts are true, so the whole thing is true. - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: The wall is 10 cm away, there is no line underneath. -richtig: false -> With `&&`, both parts must be true. The distance part isn't. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Only button B is pressed. -richtig: true -> With `||`, one true part is enough. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: No button is pressed. -richtig: false -> Both parts are false, so `||` is false too. - -runde: - !mbot.isButtonA() -frage: Button A is pressed. -richtig: false -> `mbot.isButtonA()` is true, and the `!` turns it into false. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: The wall is 10 cm away, and it is standing on a line. -richtig: false -> No part is true, so `||` is false too. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: The wall is 50 cm away, and it is standing on a line. -richtig: true -> The first part is true, and that is enough for `||`. - -runde: - !mbot.isButtonA() && mbot.getDistance() > 15 -frage: Nobody is pressing a button, the wall is 30 cm away. -richtig: true -> A isn't pressed, so `!mbot.isButtonA()` is true. The distance part is true as well. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getDistance() > 15 || mbot.getLine() == 0) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive as long as the way ahead is clear **and** no line comes. -fehler: 1 -besser: while (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> With `||` it keeps going if only one part is true, for example over the edge. - -runde: - if (mbot.isButtonA() && mbot.isButtonB()) { - mbot.playTone(440, 300); - } -frage: Should beep when button A **or** button B is pressed. -fehler: 1 -besser: if (mbot.isButtonA() || mbot.isButtonB()) { -> "Or" is `||` in Java. With `&&` it only beeps when both are pressed. - -runde: - while (mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Should wait until button A is pressed and then set off. -fehler: 1 -besser: while (!mbot.isButtonA()) { -> Waiting means: loop as long as A is **not** pressed. The `!` is missing. - -runde: - if (mbot.getDistance() > 15 & mbot.getLine() == 0) { - mbot.forward(); - } -frage: Should drive forward if the way ahead is clear and there is no line. -fehler: 1 -besser: if (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> "And" is written with two characters: `&&`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can combine two conditions with `&&` (and). → Wall and line: and -- I can combine two conditions with `||` (or). → Button A or button B: or -- I can turn a condition around with `!` (not). → Not: the opposite -- I can write a combined condition in a Java loop. → Now in Java: drive safely -::: - -## Quiz - -::: quiz -? The distance is 50 and `mbot.getLine()` gives 6. Does the mBot2 drive in "Clear and no line"? -→ Wall and line: and -- [ ] Yes, the way ahead is clear. -- [x] No, because with `&&` both parts must be true. -- [ ] Only half as far. -> It is standing on a line. So one part is false, and so is the whole condition. -? Only button B is pressed. Is `mbot.isButtonA() || mbot.isButtonB()` true? -→ Button A or button B: or -- [x] Yes -- [ ] No -> With `||` it is enough if one part is true. -? Button A is pressed. What is `!mbot.isButtonA()`? -→ Not: the opposite -- [ ] true -- [x] false -> `!` turns the value around: true becomes false. -? Which condition stops the mBot2 at the wall **and** at the edge? -→ Now in Java: drive safely -- [x] `while (mbot.getDistance() > 15 && mbot.getLine() == 0)` -- [ ] `while (mbot.getDistance() > 15 || mbot.getLine() == 0)` -- [ ] `while (mbot.getDistance() > 15)` -> With `||` it would keep driving as long as just one part is true, for example over the edge. -::: diff --git a/web/lessons/en/visubot/08-farben.md b/web/lessons/en/visubot/08-farben.md deleted file mode 100644 index e975de14..00000000 --- a/web/lessons/en/visubot/08-farben.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -titel: Recognising colours -bereich: Sensors and decisions -kurz: The quad RGB sensor tells you which colour the mBot2 is standing on. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for a colour with `mbot.getColor().equals(…)` -- turn a condition round with `!` -- put a branch inside a loop -::: - -## A sensor that sees colours - -::: mbot2 -focus: lineSensor -view: bottom -::: - -The [line sensor]{part=lineSensor} is really called a **quad RGB sensor**. “Quad” means four (four probes), “RGB” stands for red, green and blue. It shines these three colours on the floor and measures which colour comes back. That is how it recognises coloured fields. - -`mbot.getColor()` gives the name of the colour under the mBot2, as **text**: - -| Floor | `mbot.getColor()` | -|---|---| -| red field | `"red"` | -| green field | `"green"` | -| blue field | `"blue"` | -| yellow field | `"yellow"` | -| black field or line | `"black"` | -| light floor | `"white"` | - -You ask whether it is a certain colour with `.equals(…)`: `mbot.getColor().equals("red")` is **true** when the mBot2 stands on red, otherwise **false**. So it is already a complete condition. - -> In Java you always compare texts with `.equals(…)`, **not** with `==`. The `==` looks right but often doesn't work with texts. - -> With a `!` in front you turn a condition round: `!mbot.getColor().equals("red")` means “**not** red”. - -## Drive to red in the structure chart - -::: übung vb-bis-rot -titel: Drive to red -auftrag: The mBot2 drives forward as long as it is **not** on a red field. In the second world there is a green field first, which should not bother it. -welt: farbe-1 -> forward 20 -welt: farbe-2 -> forward 50 -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Colour is red?** onto it. Write a `!` in front of `mbot.getColor().equals("red")`. -tipp: The condition is `!mbot.getColor().equals("red")`, inside the loop is **move forward**. -lösung: vb-bis-rot-loesung.visustruct -::: - -## Now in Java: beep on green - -Now you combine a loop with a branch: the mBot2 drives to blue. If it passes over a green field on the way, it beeps. - -::: übung vb-java-blau-gruen -titel: To blue, beep on green -code: java -auftrag: Write in Java: the mBot2 drives forward as long as it is not on blue. After each step it checks: if it is on green, it plays a tone. In the second world there is no green field, so it must not beep there. -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -braucht: whileSchleife, verzweigung -tipp: Outside is `while (!mbot.getColor().equals("blue")) { … }`. Inside, first `mbot.forward();`. -tipp: After that, still inside the loop: `if (mbot.getColor().equals("green")) { mbot.playTone(880, 200); }`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -::: übung vb-fehler-08-farben -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To blue, beep on green", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-08-farben-start.visustruct -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be `equals("green")`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should do and tap the wrong line before time runs out. Five rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getColor().equals("red")) { - mbot.forward(); - } -frage: Should drive forward until it is standing on red. -fehler: 1 -besser: while (!mbot.getColor().equals("red")) { -> It should drive as long as it is **not** on red. The `!` is missing. - -runde: - if (mbot.getColor() == "green") { - mbot.playTone(440, 300); - } -frage: Should beep when it is standing on green. -fehler: 1 -besser: if (mbot.getColor().equals("green")) { -> You compare texts with `.equals(…)`, not with `==`. - -runde: - while (!mbot.getColor().equals("blue")) { - mbot.forward(); - if (mbot.getColor().equals("green")) { - mbot.turnRight(); - } - } -frage: Should drive to blue and beep on green. -fehler: 4 -besser: mbot.playTone(440, 300); -> On green it should beep, not turn. - -runde: - if (mbot.getColor().equals(red)) { - mbot.turnRight(); - } -frage: Should turn right on red. -fehler: 1 -besser: if (mbot.getColor().equals("red")) { -> Without quotation marks, Java looks for a variable called `red`. The colour name is a text. - -runde: - while (!mbot.getColor().equals("red")) { - mbot.forward(); - } - mbot.playTone(440, 300) -frage: Should drive to red and then beep. -fehler: 4 -besser: mbot.playTone(440, 300); -> The semicolon at the end of the statement is missing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for a colour with `mbot.getColor().equals(…)`. → A sensor that sees colours -- I can turn a condition round with `!`. → Drive to red in the structure chart -- I can put a branch inside a loop. → Now in Java: beep on green -::: - -## Quiz - -::: quiz -? The mBot2 is on a red field. What is `!mbot.getColor().equals("red")`? -→ Drive to red in the structure chart -- [ ] true -- [x] false -> `mbot.getColor().equals("red")` is true, the `!` turns it into false. So the loop stops. -? How do you compare the colour with `"green"`? -→ A sensor that sees colours -- [x] `mbot.getColor().equals("green")` -- [ ] `mbot.getColor() == "green"` -- [ ] `mbot.getColor() = "green"` -> `mbot.getColor()` gives a text, and texts are compared with `.equals(…)`. A single `=` is an assignment, not a comparison at all. -::: diff --git a/web/lessons/en/visubot/08-groesster-wert.md b/web/lessons/en/visubot/08-groesster-wert.md deleted file mode 100644 index 11506f2e..00000000 --- a/web/lessons/en/visubot/08-groesster-wert.md +++ /dev/null @@ -1,162 +0,0 @@ ---- -titel: The biggest value -bereich: Sensors and decisions -kurz: Measure all around and remember the biggest value: where is the most space? ---- - -## Skills I will gain now - -::: kompetenzen -- remember the biggest value so far in a variable -- compare every new reading with the biggest so far in a loop -- find out with the mBot2 in which direction there is the most space -::: - -## Remembering the record - -In the long jump there is a board showing the **record**. After every jump the referee checks: is the new jump longer than the record? Then it goes on the board. Otherwise the old record stays. - -A program finds the biggest value in exactly the same way. The board is a variable `max`: - -1. **Before** the loop: `int max = 0`. Nothing is on the board yet. -2. **Inside** the loop: is the new value bigger than `max`? Then `max = value`. -3. **After** the loop, `max` holds the biggest value. - -The mBot2 turns four times and measures the distance each time. This is how it looks in a desk check: - -| Direction | measured | bigger than `max`? | `max` afterwards | -|---|---|---|---| -| before | | | 0 | -| ahead | 16 | yes | 16 | -| right | 36 | yes | 36 | -| behind | 6 | no | 36 | -| left | 26 | no | 36 | - -> Important: the branch only has a yes branch. If the value is not bigger, nothing happens. The old record stays. - -## Where is the most space? - -::: übung vb-meister-platz -titel: Where is the most space? -auftrag: Build a structure chart: create `int max = 0`. The mBot2 measures the distance four times and turns right after each one. If a distance is bigger than `max`, it remembers it in `max`. At the end it shows, for example, **Farthest: 36 cm**. There are two worlds. -welt: platz-1 -> screen "Farthest: 36 cm" -welt: platz-2 -> screen "Farthest: 46 cm" -braucht: forSchleife, verzweigung -tipp: The for loop runs four times. It contains three boxes: `int distance = mbot.getDistance()`, a branch and **turn right**. -tipp: The branch checks `distance > max`. The yes branch has `max = distance`, the no branch stays empty. After the loop: `mbot.showText("Farthest: " + max + " cm")`. -lösung: vb-meister-platz-loesung.visustruct -::: - -::: angeberwissen -Many robot vacuums have a small tower with a laser on top. It spins and measures the distances all around. That way the robot draws a map of the home. Nobody measures anything at my place. I just eat my way through. -::: - -## Now in Java: remembering the direction - -Now the mBot2 knows **how much** space there is at most. But **where**? For that it also remembers the direction in a second variable. The counter variable `i` tells you which measurement it was: 0 is ahead, 1 is right, 2 is behind and 3 is left. - -```java -if (distance > max) { - max = distance; - direction = i; -} -``` - -Both lines are in the yes branch. The direction only changes when there is a new record. - -::: übung vb-java-richtung -titel: Remembering the direction -code: java -auftrag: Write "Where is the most space?" in Java. Also remember in `int direction = 0` the number of the measurement with the biggest distance. At the end show, for example, **Direction: 1**. -welt: platz-1 -> screen "Direction: 1" -welt: platz-2 -> screen "Direction: 0" -braucht: forSchleife, verzweigung -tipp: Before the loop are `int max = 0;` and `int direction = 0;`. Inside the loop: `int distance = mbot.getDistance();`, the branch and `mbot.turnRight();`. -tipp: The yes branch has `max = distance;` and `direction = i;`. After the loop: `mbot.showText("Direction: " + direction);` -lösung: vb-java-richtung-loesung.visustruct -::: - -::: übung vb-fehler-08-groesster-wert -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Remembering the direction", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-08-groesster-wert-start.visustruct -welt: platz-1 -> screen "Direction: 1" -welt: platz-2 -> screen "Direction: 0" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `direction = i`. -lösung: vb-java-richtung-loesung.visustruct -::: - -## Game: new record? - -::: spiel frage -titel: Game: new record? -intro: The mBot2 measures several distances one after the other. What is in `max` at the end? Five rounds. - -runde: -frage: Measured: 20, 50, 30 -- 30 -- [x] 50 -- 100 -> 50 is the biggest value. The 30 after it is not a new record. - -runde: -frage: Measured: 10, 20, 30, 40 -- 10 -- [x] 40 -- 100 -> Every value is a new record. At the end the last one is there. - -runde: -frage: Measured: 90, 10, 20, 30 -- 30 -- [x] 90 -- 10 -> The record comes right at the start. After that no value is bigger. - -runde: -frage: Measured: 15, 15, 15 -- 45 -- 0 -- [x] 15 -> 15 is not bigger than 15. `max` stays 15. - -runde: -frage: Measured: 25, 60, 60, 5. In which direction (`i`) is the record? -- 0 -- [x] 1 -- 2 -> At `i = 1` the 60 comes. The second 60 is not **bigger**, so `direction` stays at 1. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can remember the biggest value so far in a variable. → Remembering the record -- I can compare every new reading with the biggest so far in a loop. → Where is the most space? -- I can find out with the mBot2 in which direction there is the most space. → Now in Java: remembering the direction -::: - -## Quiz - -::: quiz -? What does `max` start with before the loop? -→ Remembering the record -- [x] With 0, because nothing has been measured yet -- [ ] With the biggest value -- [ ] With 100 -> Every distance is bigger than 0, so the very first reading becomes the record. -? What is in the no branch of the branch `distance > max`? -→ Remembering the record -- [ ] `max = 0` -- [ ] `max = distance` -- [x] Nothing, the old record stays. -? When does `direction` change? -→ Now in Java: remembering the direction -- [ ] In every pass -- [x] Only when there is a new record -- [ ] Only in the last round -::: diff --git a/web/lessons/en/visubot/09-fallauswahl-mbot2.md b/web/lessons/en/visubot/09-fallauswahl-mbot2.md deleted file mode 100644 index 68a002f4..00000000 --- a/web/lessons/en/visubot/09-fallauswahl-mbot2.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -titel: Case selection with colours -bereich: Sensors and decisions -kurz: Do something different for each colour or line, with one case selection instead of many branches. ---- - -## Skills I will gain now - -::: kompetenzen -- build a case selection with texts -- write a case selection with numbers in Java -- show a different text on the screen depending on a number -::: - -## Many colours, many cases - -The mBot2 should react differently to each colour: turn right on red, drive forward a bit on green, beep on blue. With branches you would need three inside each other. A **case selection** is clearer, in Java `switch`: - -```java -switch (mbot.getColor()) { - case "red" -> { - mbot.turnRight(); - } - case "green" -> { - mbot.forward(20); - } - default -> { - mbot.forward(10); - } -} -``` - -- The value being checked goes in the brackets at the top, here the colour. -- Each `case` is one **case**. If the value matches, exactly this case runs. -- `default` is the case for **everything else**. - -In the structogram the case selection is a box with several columns, one for each case. - -::: übung vb-farbweiche -titel: Colour switch -auftrag: Build a case selection on `mbot.getColor()`. For `"red"` the mBot2 turns right. For `"green"` it drives forward 20 cm. For `"blue"` it plays a tone of 880 hertz for 200 milliseconds. In all other cases it drives forward 10 cm. -messwerte: getColor=red -> turnRight -messwerte: getColor=green -> forward 20 -messwerte: getColor=blue -> none, tone -messwerte: getColor=white -> forward 10 -braucht: fallauswahl -tipp: Drag a case selection into the structogram. At the top goes `mbot.getColor()`, the cases are `"red"`, `"green"` and `"blue"`, plus the case for everything else. -tipp: For red `mbot.turnRight()`, for green `mbot.forward(20)`, for blue `mbot.playTone(880, 200)`, otherwise `mbot.forward(10)`. -lösung: vb-farbweiche-loesung.visustruct -::: - -## Now in Java: line patterns - -A number can also be the value of a case selection. `mbot.getLine()` from lesson 7 gives for example: - -| Value | Meaning | -|---|---| -| `0` | no line | -| `6` | the two middle sensors see the line: it is right on it | -| `15` | all four see black: a cross line | - -For numbers you write the case without quotation marks: `case 6 -> { … }`. - -::: übung vb-linienmuster -titel: Line patterns -code: java -auftrag: Write a case selection on `mbot.getLine()` in Java. For 6 the mBot2 drives forward 10 cm. For 0 it turns right. For 15 it shows **Cross line!** on its screen. In all other cases it drives forward 5 cm. -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Cross line!" -messwerte: getLine=4 -> forward 5 -braucht: fallauswahl -tipp: `switch (mbot.getLine()) { case 6 -> { … } case 0 -> { … } case 15 -> { … } default -> { … } }` -tipp: For 6 `mbot.forward(10);`, for 0 `mbot.turnRight();`, for 15 `mbot.showText("Cross line!");`, otherwise `mbot.forward(5);`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Now in Java: start signal - -Before a race the mBot2 shows three texts one after the other and then drives off. A for loop counts the rounds 1, 2 and 3. The case selection finds the right text for each round: - -```java -for (int round = 1; round <= 3; round++) { - switch (round) { - case 1 -> { mbot.showText("On your marks"); } - ... - } - mbot.pause(1000); -} -``` - -::: übung vb-startsignal -titel: Start signal -code: java -auftrag: Write in Java: a loop counts `round` from 1 to 3. In round 1 the screen shows **On your marks**, in round 2 **Get set**, otherwise **Go!**. After each text the mBot2 waits one second. After the loop it drives 50 cm forward. -befehle: showText "On your marks", pause 1000, showText "Get set", pause 1000, showText "Go!", pause 1000 -fahrt: forward 50 -braucht: fallauswahl -tipp: The loop is `for (int round = 1; round <= 3; round++) { … }`. Inside it is `switch (round) { … }` and after that `mbot.pause(1000);`. -tipp: `case 1 -> { mbot.showText("On your marks"); }`, `case 2 -> { … }` and `default -> { mbot.showText("Go!"); }`. After the loop `mbot.forward(50);`. -lösung: vb-startsignal-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Switch"](?visustruct&lektion=10-fallauswahl). It opens in a new window. - -::: übung vb-fehler-09-fallauswahl-mbot2 -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Line patterns", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-09-fallauswahl-mbot2-start.visustruct -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Cross line!" -messwerte: getLine=4 -> forward 5 -tipp: Read the error message carefully. Is the command spelled correctly? After `mbot.` the editor suggests all commands. -tipp: It should be `mbot.turnRight()`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Game: Which case runs? - -::: spiel frage -titel: Game: Which case runs? -intro: Read the switch and predict what the mBot2 does. Six rounds. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on green. What does it do? -- It turns right. -- [x] It drives forward 20 cm. -- It drives forward 10 cm. -> The case `"green"` matches, so exactly that one runs. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on yellow. What does it do? -- Nothing at all. -- It beeps. -- [x] It drives forward 10 cm. -> There is no case for yellow, so `default` runs. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on blue. What does it do? -- [x] It beeps. -- It beeps and then drives forward 10 cm. -- It turns right. -> Only the matching case runs. `default` only runs when no other case matches. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Cross line!"); } - default -> { mbot.stop(); } - } -frage: All four sensors see black. What does the mBot2 do? -- It drives forward 10 cm. -- [x] It shows **Cross line!** -- It stops. -> Four sensors on black give 15. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Cross line!"); } - default -> { mbot.stop(); } - } -frage: Everything under the mBot2 is white. What does it do? -- It drives forward 10 cm. -- [x] It turns right. -- It stops. -> No sensor sees black, so `mbot.getLine()` gives 0. - -runde: - for (int round = 1; round <= 3; round++) { - switch (round) { - case 1 -> { mbot.showText("On your marks"); } - case 2 -> { mbot.showText("Get set"); } - default -> { mbot.showText("Go!"); } - } - } -frage: What does the screen show in round 3? -- Get set -- [x] Go! -- On your marks -> There is no case of its own for 3, so `default` runs. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a case selection with texts. → Many colours, many cases -- I can write a case selection with numbers in Java. → Now in Java: line patterns -- I can show a different text on the screen depending on a number. → Now in Java: start signal -::: - -## Quiz - -::: quiz -? The mBot2 is on yellow. Which case of the colour switch runs? -→ Many colours, many cases -- [ ] the case `"red"` -- [ ] none at all -- [x] `default` -> There is no case of its own for yellow. So the case for everything else runs. -? How do you write the case for the number 6? -→ Now in Java: line patterns -- [x] `case 6 -> { … }` -- [ ] `case "6" -> { … }` -- [ ] `if 6 -> { … }` -> Numbers have no quotation marks, texts like `"red"` do. -::: diff --git a/web/lessons/en/visubot/10-tasten-joystick.md b/web/lessons/en/visubot/10-tasten-joystick.md deleted file mode 100644 index 18ec5955..00000000 --- a/web/lessons/en/visubot/10-tasten-joystick.md +++ /dev/null @@ -1,177 +0,0 @@ ---- -titel: Buttons and joystick -bereich: Sensors and decisions -kurz: The mBot2 waits for you and can be remote controlled. ---- - -## Skills I will gain now - -::: kompetenzen -- ask the sensors for buttons and joystick -- wait for a button -- steer the mBot2 with the joystick -::: - -## Pressing as a sensor - -::: mbot2 -focus: joystick, buttons -view: top -::: - -On the CyberPi there are two [buttons]{part=buttons} (A and B) and a [joystick]{part=joystick}. For your program these are sensors: they give **true** as long as you press them. - -| Sensor | Text | -|---|---| -| Button A / B pressed? | `mbot.isButtonA()`, `mbot.isButtonB()` | -| Joystick up / down | `mbot.isJoystickUp()`, `mbot.isJoystickDown()` | -| Joystick left / right | `mbot.isJoystickLeft()`, `mbot.isJoystickRight()` | - -> Without a real mBot2, open **🎛 Try the sensors** below the world in the [simulation](#zeige-simulation). There you find the buttons A, B and the arrows for the joystick. A click keeps them pressed for one second. - -## Waiting for button A in the structure chart - -A robot should not drive off as soon as the program starts. Better: it waits until you press A. - -Waiting means: a loop that runs as long as A is **not** pressed. So that it is not completely empty, the mBot2 waits briefly inside with **wait a moment** (`mbot.pause(100)`, that is 100 milliseconds). - -::: übung vb-start-a -titel: Start with button A -auftrag: The mBot2 waits until button A is pressed. Then it drives forward 30 cm. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: A while loop with the condition `!mbot.isButtonA()`. Inside is **wait a moment**. -tipp: After the loop is `mbot.forward(30)`. -lösung: vb-start-a-loesung.visustruct -::: - -> When checking, button A is not pressed for a while and then pressed. If your mBot2 drives off too early, it shows. - -## Now in Java: remote control - -With the joystick you steer the mBot2. The program asks the joystick five times in a row: up means drive forward, down means drive back. - -::: übung vb-java-joystick -titel: Remote control -code: java -auftrag: Write a for loop in Java that runs five times. Inside: if the joystick is up, the mBot2 drives forward. Otherwise: if it is down, it drives back. -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -braucht: forSchleife, verzweigung -tipp: `for (int i = 0; i < 5; i++) { … }` and inside `if (mbot.isJoystickUp()) { … } else { … }`. -tipp: The `else` contains another branch: `if (mbot.isJoystickDown()) { mbot.backward(); }`. -lösung: vb-java-joystick-loesung.visustruct -::: - -::: übung vb-fehler-10-tasten-joystick -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Remote control", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-10-tasten-joystick-start.visustruct -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.backward()`. -lösung: vb-java-joystick-loesung.visustruct -::: - -## Game: What does the mBot2 do? - -::: spiel frage -titel: Game: What does the mBot2 do? -intro: You press a button or the joystick. What does the mBot2 do? Six rounds. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Nobody presses a button. What does the mBot2 do? -- It drives forward 30 cm. -- [x] It keeps waiting. -- It beeps. -> As long as A isn't pressed, it stays in the waiting loop. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: You press button B. What does the mBot2 do? -- It drives forward 30 cm. -- [x] It keeps waiting. -- It drives back 30 cm. -> The loop only asks about button A. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: You press button A. What does the mBot2 do? -- [x] It drives forward 30 cm. -- It keeps waiting. -- It waits 100 ms and then stops. -> Now `!mbot.isButtonA()` is false, the loop ends, and it sets off. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: You push the joystick down. What does the mBot2 do? -- It drives forward. -- [x] It drives back. -- Nothing at all. -> The first condition is false, the second is true. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: You push the joystick to the left. What does the mBot2 do? -- It turns left. -- It drives back. -- [x] Nothing at all. -> There is no branch for left. Both conditions are false. - -runde: -frage: You hold button A down. What does `mbot.isButtonA()` give? -- [x] true -- false -- 1 -> Buttons give true or false, and pressed means true. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask the sensors for buttons and joystick. → Pressing as a sensor -- I can wait for a button. → Waiting for button A in the structure chart -- I can steer the mBot2 with the joystick. → Now in Java: remote control -::: - -## Quiz - -::: quiz -? What does `mbot.isButtonB()` give when nobody presses it? -→ Pressing as a sensor -- [ ] 0 -- [x] false -- [ ] B -> Buttons give true or false. -? Why is there **wait a moment** in the waiting loop? -→ Waiting for button A in the structure chart -- [x] So the loop is not empty and the CyberPi doesn't race round in circles for nothing. -- [ ] So the button glows longer. -- [ ] Without a pause the mBot2 drives backwards. -> The loop checks every 100 milliseconds, that is plenty. -::: diff --git a/web/lessons/en/visubot/10-unterbrechen-mit-break.md b/web/lessons/en/visubot/10-unterbrechen-mit-break.md deleted file mode 100644 index 7b445db0..00000000 --- a/web/lessons/en/visubot/10-unterbrechen-mit-break.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -titel: Emergency brake with break -bereich: Sensors and decisions -kurz: An endless loop with a Break: the mBot2 keeps driving until the emergency brake kicks in. ---- - -## Skills I will gain now - -::: kompetenzen -- end an endless loop with a Break -- explain why the Break sits inside a branch -- write a loop with `while (true)` and `break` in Java -::: - -## Endless loop with a Break - -Many robot programs run "forever": the mBot2 drives, measures, drives, measures … That is what the **endless loop** is for. It has no condition and never stops by itself. - -So that the mBot2 can still stop, you need an **emergency brake**: the **Break**. It leaves the loop it is in straight away. The program carries on with the first box **after** the loop. You recognise it by the point on its left edge. In the box you write a short comment, for example `leave the loop`. - -A Break almost always sits inside a **branch**. Otherwise the loop would end in the very first pass. - -::: übung vb-notbremse -titel: Emergency brake at the wall -auftrag: Build an endless loop. Inside it a branch checks: if the distance is less than 15 cm, there is a Break. After that (below the branch, still inside the loop) the mBot2 drives forward. After the loop it plays a tone. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -braucht: endlosschleife, aussprung -tipp: Drag the **endless loop** into the structogram. First a branch with `mbot.getDistance() < 15` goes into the loop, the **Break** into its true branch, the false branch stays empty. -tipp: Below the branch (inside the loop) goes **drive forward**, below the loop `mbot.playTone(440, 300)`. -lösung: vb-notbremse-loesung.visustruct -::: - -## Now in Java: stop with button A - -In Java the endless loop is `while (true)`. The Break is called `break`: - -```java -while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); -} -``` - -::: übung vb-java-taste-stopp -titel: Stop with button A -code: java -auftrag: Write in Java: the mBot2 keeps driving forward in an endless loop. When button A is pressed, it leaves the loop with `break`. Then the screen shows **Stopped**. -messwerte: isButtonA=true -> none, screen "Stopped" -messwerte: isButtonA=false/true -> drives, screen "Stopped" -braucht: endlosschleife, aussprung -tipp: `while (true) { … }` with `if (mbot.isButtonA()) { break; }` and then `mbot.forward();`. -tipp: After the loop `mbot.showText("Stopped");`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Call and break"](?visustruct&lektion=18-aufruf-und-aussprung). It opens in a new window. - -::: übung vb-fehler-10-unterbrechen-mit-break -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Stop with button A", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-10-unterbrechen-mit-break-start.visustruct -messwerte: isButtonA=true -> none, screen "Stopped" -messwerte: isButtonA=false/true -> drives, screen "Stopped" -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be `mbot.showText("Stopped")`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -## Game: Emergency brake - -::: spiel frage -titel: Game: Emergency brake -intro: When does the break kick in, and what happens afterwards? Each drive is 10 cm long. Five rounds. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: The wall starts 40 cm away. How often does it drive forward? -- 2 times -- [x] 3 times -- forever -> At 40, 30 and 20 it drives. At 10 the emergency brake kicks in. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: What happens straight after `break`? -- The loop starts again. -- It drives forward once more. -- [x] It beeps. -> `break` leaves the loop. The program carries on with the first command after it. - -runde: - while (true) { - mbot.forward(); - break; - } -frage: How often does the mBot2 drive forward? -- [x] once -- not at all -- forever -> The break isn't inside a branch. The loop ends in its first run. - -runde: - while (true) { - mbot.forward(); - } -frage: When does this loop stop? -- after 10 drives -- at the wall -- [x] never -> Without `break`, `while (true)` really is endless. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: You press button A after it has driven forward twice. How often has it driven forward? -- [x] 2 times -- 3 times -- not at all -> In the next run it notices the button and jumps out of the loop. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (true) { - if (mbot.getDistance() > 15) { - break; - } - mbot.forward(); - } -frage: Should drive forward and stop at the wall with the emergency brake. -fehler: 2 -besser: if (mbot.getDistance() < 15) { -> The emergency brake should kick in when the wall is **close**. - -runde: - while (true) { - if (mbot.isButtonB()) { - break; - } - mbot.forward(); - } - mbot.showText("Stopped"); -frage: Should drive forward until button A is pressed, then show "Stopped". -fehler: 2 -besser: if (mbot.isButtonA()) { -> The brake should be button A, not B. - -runde: - while (true) { - if (mbot.isButtonA()) { - brake; - } - mbot.forward(); - } -frage: Should drive forward until button A is pressed. -fehler: 3 -besser: break; -> The exit is called `break`, not `brake`. - -runde: - while (false) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Should drive in an endless loop until button A is pressed. -fehler: 1 -besser: while (true) { -> The endless loop is `while (true)`. With `false` it doesn't run even once. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can end an endless loop with a Break. → Endless loop with a Break -- I can explain why the Break sits inside a branch. → Endless loop with a Break -- I can write a loop with `while (true)` and `break` in Java. → Now in Java: stop with button A -::: - -## Quiz - -::: quiz -? Where does the program carry on after a Break? -→ Endless loop with a Break -- [ ] at the start of the loop -- [x] at the first box after the loop -- [ ] at the very start of the program -> The Break leaves the loop straight away. -? Why does the Break sit inside a branch? -→ Endless loop with a Break -- [x] Otherwise the loop ends in the first pass. -- [ ] Otherwise the mBot2 drives backwards. -- [ ] It just looks nicer. -> The emergency brake should only kick in when the condition is true. -? What is the endless loop called in Java? -→ Now in Java: stop with button A -- [ ] `while (false)` -- [x] `while (true)` -- [ ] `for (;;) break;` -> `true` is always true, the loop only stops with `break`. -::: diff --git a/web/lessons/en/visubot/11-licht.md b/web/lessons/en/visubot/11-licht.md deleted file mode 100644 index fa3409ab..00000000 --- a/web/lessons/en/visubot/11-licht.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -titel: The light sensor -bereich: Sensors and decisions -kurz: Bright or dark? The mBot2 becomes a night light. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the brightness -- switch the LEDs with a branch -- write several commands in one branch -::: - -## How bright is it? - -::: mbot2 -focus: lightSensor -view: top -::: - -The [light sensor]{part=lightSensor} is on the CyberPi. `mbot.getBrightness()` gives a number from **0** (pitch dark) to **100** (very bright). - -| Sensor | Text | Value | -|---|---|---| -| Brightness | `mbot.getBrightness()` | 0 to 100 | - -> In the simulation you set the brightness with the slider under **🎛 Try the sensors**. With a real mBot2 you simply hold your hand over the CyberPi. - -## Night light in the structure chart - -::: übung vb-nachtlicht -titel: Night light -auftrag: If the brightness is less than 30, the LEDs glow white (`mbot.setLed(255, 255, 255)`). Otherwise the LEDs go off (`mbot.ledOff()`). -messwerte: getBrightness=10 -> ledOn -messwerte: getBrightness=80 -> ledOff -braucht: verzweigung -tipp: Drag a branch into the structure chart and the sensor **Brightness** onto it. Write `< 30` after it. -tipp: The true branch gets **LEDs on** with `mbot.setLed(255, 255, 255)`, the false branch **LEDs off**. -lösung: vb-nachtlicht-loesung.visustruct -::: - -::: angeberwissen -Street lights do the same: a light sensor notices when it gets dark and switches them on. That is why they come on earlier in winter than in summer, without anyone changing a clock. -::: - -## Now in Java: night light with sound - -::: übung vb-java-nachtlicht-ton -titel: Night light with sound -code: java -auftrag: Write the night light in Java, but with two changes: in the dark the LEDs glow yellow (`mbot.setLed(255, 200, 0)`) **and** the mBot2 plays a tone. In the light the LEDs go off, without a tone. -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -braucht: verzweigung -tipp: `if (mbot.getBrightness() < 30) { … } else { … }` -tipp: The `if` part has two lines: `mbot.setLed(255, 200, 0);` and `mbot.playTone(440, 300);`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -::: übung vb-fehler-11-licht -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Night light with sound", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-11-licht-start.visustruct -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be `mbot.setLed(255, 200, 0)`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -## Game: Lights on or off? - -::: spiel frage -titel: Game: Lights on or off? -intro: How bright is it, and what do the LEDs do? Six rounds. -antworten: LEDs on | LEDs off - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: It is almost dark, the brightness is 10. -richtig: LEDs on -> 10 is less than 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is 45. -richtig: LEDs off -> 45 is not less than 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is exactly 30. -richtig: LEDs off -> 30 is not less than 30. For "up to and including 30" you would need `<=`. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is 29. -richtig: LEDs on -> 29 is just less than 30. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: The brightness is 50. -richtig: LEDs on -> Careful, it's the other way round here: 50 is not greater than 70, so the false branch runs. The LEDs light up yellow. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: The sun is shining, the brightness is 100. -richtig: LEDs off -> 100 is greater than 70, so the true branch runs. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the brightness. → How bright is it? -- I can switch the LEDs with a branch. → Night light in the structure chart -- I can write several commands in one branch. → Now in Java: night light with sound -::: - -## Quiz - -::: quiz -? `mbot.getBrightness()` gives 45. What does the night light do? -→ Night light in the structure chart -- [ ] The LEDs glow. -- [x] The LEDs go off. -> 45 is not less than 30. -? What range do the light sensor's values have? -→ How bright is it? -- [ ] 0 to 255 -- [x] 0 to 100 -- [ ] -100 to 100 -::: diff --git a/web/lessons/en/visubot/11-merker-boolean.md b/web/lessons/en/visubot/11-merker-boolean.md deleted file mode 100644 index c6fb016f..00000000 --- a/web/lessons/en/visubot/11-merker-boolean.md +++ /dev/null @@ -1,155 +0,0 @@ ---- -titel: A flag with boolean -bereich: Sensors and decisions -kurz: Use a variable of type boolean to remember whether something happened. ---- - -## Skills I will gain now - -::: kompetenzen -- create a variable of type `boolean` -- use a flag to remember whether something happened -- check a flag in a condition after the loop -::: - -## True or false - -A variable of type `boolean` knows only two values: `true` and `false`. You already know this from the sensors: `mbot.isButtonA()` gives you `true` or `false`. A comparison like `mbot.getDistance() < 10` is also `true` or `false` in the end. - -| Code | Meaning | -|---|---| -| `boolean dark = false` | It has not been dark yet. | -| `dark = true` | Now it has been dark. | -| `if (dark)` | Is `dark` true? | - -With `if (dark)` you don't need `== true`. The variable itself is already true or false. - -## The flag - -The mBot2 should keep watch for three seconds. It measures the brightness again and again. Afterwards it should say whether it was dark **at any point**. - -For this you use a **flag**: a `boolean` variable that starts with `false`. When it gets dark, you set it to `true`. After that nobody sets it back. So after the loop you know: was it dark even just once? - -::: übung vb-wurde-es-dunkel -titel: Did it get dark? -auftrag: Build a structure chart: create `boolean dark = false`. In a for loop, check 30 times: if the brightness is less than 20, `dark` becomes true. Wait 100 ms in every pass. Then show **It was dark** or **Always bright**. -messwerte: getBrightness=80/10/80 -> none, screen "It was dark" -messwerte: getBrightness=80 -> none, screen "Always bright" -braucht: forSchleife, verzweigung -tipp: Inside the loop is a branch with `mbot.getBrightness() < 20`. The yes branch has `dark = true`, the no branch stays empty. Then comes `mbot.pause(100)`. -tipp: After the loop comes a second branch with the condition `dark`. Yes: `mbot.showText("It was dark")`, no: `mbot.showText("Always bright")`. -lösung: vb-wurde-es-dunkel-loesung.visustruct -::: - -> When checking, it gets dark for a moment in the middle of the watch and then bright again. Does your mBot2 still show **It was dark**? - -## Now in Java: was there a clap? - -In Java the flag looks like this: - -```java -boolean loud = false; -for (int i = 0; i < 30; i++) { - if (mbot.getLoudness() > 60) { - loud = true; - } - mbot.pause(100); -} -``` - -In Java you always write `true` and `false` in lower case. - -::: übung vb-java-klatsch-merker -titel: Was there a clap? -code: java -auftrag: Write in Java: the mBot2 listens for three seconds (30 times with a 100 ms pause). If the loudness is ever greater than 60, it remembers this in `boolean loud`. Then it shows **Clap!** or **All quiet**. -messwerte: getLoudness=0/90/0 -> none, screen "Clap!" -messwerte: getLoudness=0 -> none, screen "All quiet" -braucht: forSchleife, verzweigung -tipp: Take the code above and then write `if (loud) { … } else { … }`. -tipp: The branches get `mbot.showText("Clap!");` and `mbot.showText("All quiet");`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -::: übung vb-fehler-11-merker-boolean -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Was there a clap?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-11-merker-boolean-start.visustruct -messwerte: getLoudness=0/90/0 -> none, screen "Clap!" -messwerte: getLoudness=0 -> none, screen "All quiet" -tipp: The error message names an unknown variable. How do you write true and false in Java? Java is case-sensitive. -tipp: It should be `loud = true`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -## Game: true or false? - -::: spiel frage -titel: Game: true or false? -intro: Ludwig asks what the condition comes out as. Six rounds. -frage: Is it true or false? -antworten: true | false - -runde: -frage: `mbot.getDistance() < 10`, the distance is 25 cm. -richtig: false -> 25 is not less than 10. - -runde: -frage: `mbot.isButtonA()`, and you are pressing button A right now. -richtig: true -> The sensor reports: pressed. - -runde: -frage: `boolean dark = false`. It never gets dark in the loop. What is `dark` afterwards? -richtig: false -> Nobody set the flag to `true`. - -runde: -frage: `boolean dark = false`. It gets dark once in the loop, then bright again. What is `dark` afterwards? -richtig: true -> A flag stays `true` once it has been set. - -runde: -frage: `mbot.getBrightness() > 50`, the brightness is 50. -richtig: false -> 50 is not greater than 50. - -runde: -frage: `!mbot.isButtonA()`, and you are not pressing button A. -richtig: true -> Button A is not pressed, so `false`. The `!` turns it around: `true`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a variable of type `boolean`. → True or false -- I can use a flag to remember whether something happened. → The flag -- I can check a flag in a condition after the loop. → Now in Java: was there a clap? -::: - -## Quiz - -::: quiz -? Which values can a variable of type `boolean` have? -→ True or false -- [ ] Any whole number -- [x] Only `true` and `false` -- [ ] Only 0 and 1 -? What does a flag start with? -→ The flag -- [x] With `false`, because nothing has happened yet -- [ ] With `true` -- [ ] With the first reading -> Only when the event happens does it become `true`. -? How do you check in Java whether the flag `loud` is true? -→ Now in Java: was there a clap? -- [x] `if (loud)` -- [ ] `if (loud = true)` -- [ ] `if loud` -> `loud` itself is already true or false. A single `=` would be an assignment. -::: diff --git a/web/lessons/en/visubot/12-lautstaerke.md b/web/lessons/en/visubot/12-lautstaerke.md deleted file mode 100644 index cd679d70..00000000 --- a/web/lessons/en/visubot/12-lautstaerke.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -titel: The microphone -bereich: Sensors and decisions -kurz: Clap to start, and a noise light for the classroom. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the loudness -- wait until something happens -- write a noise light in Java -::: - -## How loud is it? - -::: mbot2 -focus: microphone -view: top -::: - -The CyberPi's [microphone]{part=microphone} measures the loudness. `mbot.getLoudness()` gives **0** (silent) to **100** (very loud). A clap nearby gives about 60 to 90. - -| Sensor | Text | Value | -|---|---|---| -| Loudness | `mbot.getLoudness()` | 0 to 100 | - -## Clap to start in the structure chart - -Just like waiting for button A: a loop waits as long as it is quiet. Only then does the mBot2 drive off. - -::: übung vb-klatschen -titel: Clap to start -auftrag: The mBot2 waits as long as the loudness is less than 50. Then it drives forward 30 cm. -messwerte: getLoudness=0/80 -> forward 30 -messwerte: getLoudness=90 -> forward 30 -braucht: whileSchleife -tipp: A while loop with the condition `mbot.getLoudness() < 50`, with **wait a moment** inside. -tipp: After the loop: `mbot.forward(30)`. -lösung: vb-klatschen-loesung.visustruct -::: - -> In the simulation you push the **Loudness** slider to the right while the program is waiting. - -## Now in Java: noise light - -::: übung vb-java-laermampel -titel: Noise light -code: java -auftrag: Write in Java: if the loudness is greater than 60, the LEDs glow red. Otherwise they are off. -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -braucht: verzweigung -tipp: `if (mbot.getLoudness() > 60) { … } else { … }` -tipp: Red is `mbot.setLed(255, 0, 0);`, off is `mbot.ledOff();`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -::: angeberwissen -Loudness is really measured in **decibels** (dB). Whispering is about 30 dB, a classroom 60 to 70 dB, a pneumatic drill 100 dB. The CyberPi turns that into a simple number from 0 to 100. -::: - -::: übung vb-fehler-12-lautstaerke -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Noise light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-12-lautstaerke-start.visustruct -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.ledOff()`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -## Game: Loud or quiet? - -::: spiel frage -titel: Game: Loud or quiet? -intro: How loud is it, and what does the mBot2 do? Six rounds. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: The class is quiet, the loudness is 30. -- [x] It keeps waiting. -- It drives forward 30 cm. -> 30 is less than 50, the waiting loop keeps running. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: You clap, the loudness is 70. -- It keeps waiting. -- [x] It drives forward 30 cm. -> 70 is not less than 50. The loop ends and it sets off. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: The loudness is exactly 50. -- It keeps waiting. -- [x] It drives forward 30 cm. -> 50 is not less than 50. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: The loudness is 65. -- [x] The LEDs light up red. -- The LEDs are off. -> 65 is greater than 60: too loud. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: The loudness is exactly 60. -- The LEDs light up red. -- [x] The LEDs are off. -> 60 is not greater than 60. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Someone whispers, the loudness is 10. -- The LEDs light up red. -- [x] The LEDs are off. -> Whispering is quiet. The noise light stays off. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the loudness. → How loud is it? -- I can wait until something happens. → Clap to start in the structure chart -- I can write a noise light in Java. → Now in Java: noise light -::: - -## Quiz - -::: quiz -? The clap loop is `while (mbot.getLoudness() < 50)`. The loudness is 30. What happens? -→ Clap to start in the structure chart -- [x] The mBot2 keeps waiting. -- [ ] The mBot2 drives off. -> 30 is less than 50, the condition is true, the loop keeps running. -::: diff --git a/web/lessons/en/visubot/13-neigung.md b/web/lessons/en/visubot/13-neigung.md deleted file mode 100644 index a64c00a6..00000000 --- a/web/lessons/en/visubot/13-neigung.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -titel: The motion sensor -bereich: Sensors and decisions -kurz: Measure tilt and detect shaking. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the motion sensor measures -- join two conditions with “or” -- join several conditions with “and” -::: - -## Tilted or level? - -::: mbot2 -focus: cyberpi -spin: yes -::: - -Inside the [CyberPi]{part=cyberpi} there is a **motion sensor** (gyroscope). It measures how tilted the mBot2 is, in degrees: - -| Sensor | Text | Value | -|---|---|---| -| Tilt forward/back | `mbot.getPitch()` | nose down: positive, nose up: negative (−90 to 90) | -| Tilt sideways | `mbot.getRoll()` | positive to one side, negative to the other | -| Shaken? | `mbot.isShaken()` | `true` or `false` | - -When the mBot2 is lying flat on the table, both tilts are about **0**. - -> In the simulation there are two sliders for this under **🎛 Try the sensors** and the button **shake**. With a real mBot2 you tilt it carefully in your hand. - -## Tilt warning in the structure chart - -If the mBot2 drives up or down a ramp that is too steep, it should warn you. Too steep means: more than 20° forward **or** more than 20° back. - -::: übung vb-schraeglage -titel: Tilt warning -auftrag: If the forward/back tilt is greater than 20 **or** less than −20, the mBot2 plays a tone. Otherwise it does nothing. -messwerte: getPitch=30 -> tone -messwerte: getPitch=-30 -> tone -messwerte: getPitch=5 -> noTone -braucht: verzweigung -tipp: “or” in a condition is `||`. Drag the sensor **Tilt forward/back** onto the branch twice. -tipp: The condition is `mbot.getPitch() > 20 || mbot.getPitch() < -20`, the true branch gets **play a tone**. -lösung: vb-schraeglage-loesung.visustruct -::: - -## Now in Java: spirit level - -A spirit level shows whether something is exactly level. The mBot2 glows green when both tilts are between −5 and 5. - -::: übung vb-java-wasserwaage -titel: Spirit level -code: java -auftrag: Write in Java: if `mbot.getPitch()` and `mbot.getRoll()` are both between −5 and 5, the LEDs glow green. Otherwise they are off. -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -braucht: verzweigung -tipp: “and” is `&&`. You need four comparisons: `mbot.getPitch() > -5 && mbot.getPitch() < 5 && …` -tipp: Green is `mbot.setLed(0, 255, 0);`, the `else` has `mbot.ledOff();`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -::: übung vb-fehler-13-neigung -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Spirit level", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-13-neigung-start.visustruct -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.setLed(0, 255, 0)`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -## Game: Too steep? - -::: spiel frage -titel: Game: Too steep? -intro: Ludwig tilts the mBot2. What does it do? Six rounds. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: The nose points 30° down, `mbot.getPitch()` gives 30. -- [x] It beeps. -- It doesn't beep. -> 30 is greater than 20. One true part is enough for `||`. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: The nose points up, `mbot.getPitch()` gives −25. -- [x] It beeps. -- It doesn't beep. -> −25 is less than −20, so the second part is true. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: A gentle ramp, `mbot.getPitch()` gives 10. -- It beeps. -- [x] It doesn't beep. -> 10 is between −20 and 20. Both parts are false. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: `mbot.getPitch()` gives exactly 20. -- It beeps. -- [x] It doesn't beep. -> 20 is not greater than 20. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: `mbot.getPitch()` gives 0, `mbot.getRoll()` gives 3. -- [x] The LEDs light up green. -- The LEDs are off. -> All four parts are true. The mBot2 is almost level. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: `mbot.getPitch()` gives 2, `mbot.getRoll()` gives −8. -- The LEDs light up green. -- [x] The LEDs are off. -> −8 is not greater than −5. With `&&`, one false part makes everything false. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 6 2 CONDITION: is true or false, e.g. mbot.getDistance() < 20 -- ↓ 4 4 SENSOR: a part that measures something -- ↓ 3 10 BRANCH: does one thing or the other, depending on a condition -- ↓ 4 6 BRIGHTNESS: mbot.getBrightness() measures it -- → 11 4 BREAK: emergency brake: leaves the loop straight away -- → 13 1 ULTRASOUND: how the mBot2 measures distance: a very high sound and its echo -- ↓ 1 8 JOYSTICK: small lever on the CyberPi -- ↓ 2 2 SWITCH: one case of its own for each value -lösung: 7 4, 11 6, 10 6, 4 8, 6 9, 9 4 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the motion sensor measures. → Tilted or level? -- I can join two conditions with “or”. → Tilt warning in the structure chart -- I can join several conditions with “and”. → Now in Java: spirit level -::: - -## Quiz - -::: quiz -? The mBot2 is lying flat on the table. What does `mbot.getPitch()` give, roughly? -→ Tilted or level? -- [x] 0 -- [ ] 90 -- [ ] 100 -? What does `mbot.isShaken()` do? -→ Tilted or level? -- [ ] It shakes the mBot2. -- [x] It reports whether someone is shaking the mBot2. -> Sensors don't do anything, they only measure. -::: diff --git a/web/lessons/en/visubot/14-leds.md b/web/lessons/en/visubot/14-leds.md deleted file mode 100644 index 15121599..00000000 --- a/web/lessons/en/visubot/14-leds.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -titel: The LEDs -bereich: Light, sound and screen -kurz: Mix colours, build a traffic light and make it blink. ---- - -## Skills I will gain now - -::: kompetenzen -- mix a colour from red, green and blue -- show colours one after the other with pauses -- make the LEDs blink in Java -::: - -## Mixing red, green and blue - -::: mbot2 -focus: leds -view: back -::: - -The five [LEDs]{part=leds} are an **actuator**: they do something, they light up. Each LED mixes its colour from red, green and blue, each from 0 (off) to 255 (fully bright). - -| Command | Box | What happens | -|---|---|---| -| `mbot.setLed(255, 0, 0)` | **LEDs on** | all LEDs red | -| `mbot.ledOff()` | **LEDs off** | all LEDs off | -| `mbot.pause(1000)` | **wait a moment** | wait 1000 milliseconds (1 second) | - -| Colour | Red | Green | Blue | -|---|---|---|---| -| Red | 255 | 0 | 0 | -| Yellow | 255 | 200 | 0 | -| Green | 0 | 255 | 0 | -| Blue | 0 | 0 | 255 | -| White | 255 | 255 | 255 | -| Purple | 160 | 0 | 255 | - -> Without **wait a moment** the mBot2 switches the colours so fast that you only see the last one. In the simulation you see the colour on the LEDs of the 3D model. - -## Traffic light in the structure chart - -::: übung vb-ampel -titel: Traffic light -auftrag: The LEDs show red, yellow and green one after the other like a traffic light. Between the colours the mBot2 waits 1000 milliseconds each time. Yellow is `255, 200, 0`. -befehle: setLed 255 0 0, pause 1000, setLed 255 200 0, pause 1000, setLed 0 255 0 -tipp: Drag **LEDs on** three times and **wait a moment** twice into the structure chart, taking turns. -tipp: Change the numbers: `mbot.setLed(255, 0, 0)`, `mbot.pause(1000)`, `mbot.setLed(255, 200, 0)`, `mbot.pause(1000)`, `mbot.setLed(0, 255, 0)`. -lösung: vb-ampel-loesung.visustruct -::: - -## Now in Java: blue light - -Blinking means: on, wait, off, wait, again and again. “Again and again” is a loop. - -::: übung vb-java-blinken -titel: Blue light -code: java -auftrag: Write a for loop in Java that runs three times. Inside, the LEDs go blue, the mBot2 waits 500 milliseconds, the LEDs go off, and it waits 500 milliseconds again. -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: `for (int i = 0; i < 3; i++) { … }` -tipp: Inside the loop: `mbot.setLed(0, 0, 255);`, `mbot.pause(500);`, `mbot.ledOff();`, `mbot.pause(500);`. -lösung: vb-java-blinken-loesung.visustruct -::: - -::: übung vb-fehler-14-leds -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Blue light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-14-leds-start.visustruct -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.pause(500)`. -lösung: vb-java-blinken-loesung.visustruct -::: - -## Game: Which colour? - -::: spiel frage -titel: Game: Which colour? -intro: Red, green and blue mixed together: which colour do the LEDs show? Seven rounds. - -runde: - mbot.setLed(255, 0, 0); -frage: Which colour lights up? -- [x] red -- green -- blue -> Only the first value, red, is on. - -runde: - mbot.setLed(0, 0, 255); -frage: Which colour lights up? -- red -- green -- [x] blue -> The order is always red, green, blue. Only blue is on. - -runde: - mbot.setLed(255, 255, 255); -frage: Which colour lights up? -- yellow -- [x] white -- off -> All three colours at full brightness make white. - -runde: - mbot.setLed(255, 200, 0); -frage: Which colour lights up? -- [x] yellow -- purple -- white -> Red and green together make yellow. - -runde: - mbot.setLed(160, 0, 255); -frage: Which colour lights up? -- yellow -- [x] purple -- green -> Red and blue together make purple. - -runde: - mbot.setLed(0, 0, 0); -frage: Which colour lights up? -- white -- black -- [x] none, the LEDs are off -> All three values are 0. An LED can't shine black. - -runde: - mbot.setLed(255, 0, 0); - mbot.setLed(0, 255, 0); - mbot.pause(1000); -frage: Which colour do you see for one second? -- red -- [x] green -- first red, then green -> Without **wait a moment** between the colours, red is gone immediately. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.setLed(255, 0, 0); - mbot.pause(1000); - mbot.setLed(255, 200, 0); - mbot.pause(1000); - mbot.setLed(0, 0, 255); -frage: Should show red, yellow and green like a traffic light. -fehler: 5 -besser: mbot.setLed(0, 255, 0); -> Green is `0, 255, 0`. `0, 0, 255` is blue. - -runde: - for (int i = 0; i < 3; i++) { - mbot.setLed(0, 0, 255); - mbot.pause(500); - mbot.ledOff; - mbot.pause(500); - } -frage: Should flash blue three times. -fehler: 4 -besser: mbot.ledOff(); -> A command without a number still needs its brackets. - -runde: - mbot.setLed(0, 0, 300); - mbot.pause(1000); - mbot.ledOff(); -frage: Should shine bright blue for one second. -fehler: 1 -besser: mbot.setLed(0, 0, 255); -> Each colour only goes from 0 to 255. - -runde: - mbot.setLed(255, 255, 255); - mbot.pause(1000); - mbot.setLed(0, 0, 0, 0); -frage: Should shine white for one second and then go off. -fehler: 3 -besser: mbot.ledOff(); -> `setLed` has exactly three numbers: red, green, blue. To switch off there is `mbot.ledOff()`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can mix a colour from red, green and blue. → Mixing red, green and blue -- I can show colours one after the other with pauses. → Traffic light in the structure chart -- I can make the LEDs blink in Java. → Now in Java: blue light -::: - -## Quiz - -::: quiz -? Which colour does `mbot.setLed(0, 255, 0)` give? -→ Mixing red, green and blue -- [ ] Red -- [x] Green -- [ ] Blue -> The numbers are red, green, blue. Only green is on. -? Why does the blue light need **wait a moment**? -→ Now in Java: blue light -- [x] Otherwise it changes so fast that you can't see any blinking. -- [ ] Otherwise the LEDs break. -- [ ] Because `ledOff()` doesn't work otherwise. -::: diff --git a/web/lessons/en/visubot/15-einzelne-leds.md b/web/lessons/en/visubot/15-einzelne-leds.md deleted file mode 100644 index bd217f24..00000000 --- a/web/lessons/en/visubot/15-einzelne-leds.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -titel: Single LEDs -bereich: Light, sound and screen -kurz: Each of the five LEDs gets its own colour, all the way to a running light. ---- - -## Skills I will gain now - -::: kompetenzen -- address the LEDs by their numbers 0 to 4 -- make single LEDs light up in different colours -- build a running light with a loop -::: - -## Every LED has a number - -::: mbot2 -focus: leds -view: back -::: - -So far all five [LEDs]{part=leds} showed the same colour. But you can also address each LED on its own. For that, every LED has a **number**. As with arrays, counting starts at **0**: the five LEDs are called 0, 1, 2, 3 and 4. - -| Command | Box | What happens | -|---|---|---| -| `mbot.led(0).setColor("green")` | **set LED colour** | LED 0 lights up green | -| `mbot.led(3).setColor("off")` | **set LED colour** | LED 3 goes off | - -You write the colour as a name in quotes: - -| Colour name | Colour name | Colour name | -|---|---|---| -| `"red"` | `"orange"` | `"yellow"` | -| `"green"` | `"cyan"` | `"blue"` | -| `"purple"` | `"pink"` | `"white"` | - -With `"off"` you switch an LED off. - -> The number can also be a variable: `mbot.led(i).setColor("red")`. That way a loop can switch on one LED after the other. - -## Three lights in the structogram - -::: übung vb-drei-lichter -titel: Three lights -auftrag: Switch LED 0 red, LED 2 yellow and LED 4 green. LEDs 1 and 3 stay off. -befehle: ledColor 0 "red", ledColor 2 "yellow", ledColor 4 "green" -tipp: Drag **set LED colour** into the structogram three times. -tipp: Change number and colour: `mbot.led(0).setColor("red")`, `mbot.led(2).setColor("yellow")`, `mbot.led(4).setColor("green")`. -lösung: vb-drei-lichter-loesung.visustruct -::: - -## Now in Java: running light - -In a running light, a light moves from one LED to the next. The loop variable `i` is the number of the LED. - -::: übung vb-java-lauflicht -titel: Running light -code: java -auftrag: Write a for loop in Java in which `i` runs from 0 to 4. Inside, LED `i` turns blue, the mBot2 waits 200 milliseconds, and LED `i` goes off again. -befehle: ledColor 0 "blue", pause 200, ledColor 0 "off", ledColor 1 "blue", pause 200, ledColor 1 "off", ledColor 2 "blue", pause 200, ledColor 2 "off", ledColor 3 "blue", pause 200, ledColor 3 "off", ledColor 4 "blue", pause 200, ledColor 4 "off" -braucht: forSchleife -tipp: `for (int i = 0; i < 5; i++) { … }` -tipp: Inside the loop: `mbot.led(i).setColor("blue");`, `mbot.pause(200);`, `mbot.led(i).setColor("off");`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -::: übung vb-fehler-15-einzelne-leds -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Running light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-15-einzelne-leds-start.visustruct -befehle: ledColor 0 "blue", pause 200, ledColor 0 "off", ledColor 1 "blue", pause 200, ledColor 1 "off", ledColor 2 "blue", pause 200, ledColor 2 "off", ledColor 3 "blue", pause 200, ledColor 3 "off", ledColor 4 "blue", pause 200, ledColor 4 "off" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `mbot.led(i)`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -## Game: Which LED is on? - -::: spiel frage -titel: Game: Which LED is on? -intro: Five LEDs numbered 0 to 4. Which ones light up? Six rounds. - -runde: - mbot.led(0).setColor("red"); -frage: Which LED lights up red? -- [x] the first one -- the second one -- all of them -> The numbers start at 0. LED 0 is the first one. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blue"); - } -frage: Which LEDs light up blue? -- LEDs 0 to 3 -- [x] all five -- only LED 4 -> `i` runs from 0 to 4, that is all five. - -runde: - for (int i = 0; i < 5; i = i + 2) { - mbot.led(i).setColor("green"); - } -frage: Which LEDs light up green? -- LEDs 1 and 3 -- [x] LEDs 0, 2 and 4 -- LEDs 2 and 4 -> `i` jumps in steps of two: 0, 2, 4. - -runde: - mbot.led(2).setColor("yellow"); - mbot.led(2).setColor("off"); -frage: Which LED is on at the end? -- LED 2 yellow -- [x] none -- LED 3 yellow -> LED 2 goes on and straight off again. - -runde: - for (int i = 4; i >= 0; i--) { - mbot.led(i).setColor("red"); - mbot.pause(200); - mbot.led(i).setColor("off"); - } -frage: At which LED does this running light start? -- LED 0 -- [x] LED 4 -- LED 5 -> `i` starts at 4 and counts down. The light runs the other way round. - -runde: -frage: Ludwig writes `mbot.led(5).setColor("red");`. Which LED lights up? -- the fifth one -- the sixth one -- [x] none, there is no LED 5 -> The five LEDs are numbered 0 to 4. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.led(1).setColor("red"); - mbot.led(2).setColor("yellow"); - mbot.led(4).setColor("green"); -frage: Should switch LED 0 red, LED 2 yellow and LED 4 green. -fehler: 1 -besser: mbot.led(0).setColor("red"); -> The first LED has the number 0. - -runde: - for (int i = 1; i <= 5; i++) { - mbot.led(i).setColor("blue"); - } -frage: Should switch all five LEDs blue. -fehler: 1 -besser: for (int i = 0; i < 5; i++) { -> The LEDs are numbered 0 to 4. There is no LED 5. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blue"); - mbot.pause(200); - mbot.led(0).setColor("off"); - } -frage: Running light: each LED goes blue for a moment and then off again. -fehler: 4 -besser: mbot.led(i).setColor("off"); -> The same LED `i` should go off, not always LED 0. - -runde: - mbot.led(3).setColor(red); -frage: Should switch LED 3 red. -fehler: 1 -besser: mbot.led(3).setColor("red"); -> The colour name is a text and needs quotation marks. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can address the LEDs by their numbers 0 to 4. → Every LED has a number -- I can make single LEDs light up in different colours. → Three lights in the structogram -- I can build a running light with a loop. → Now in Java: running light -::: - -## Quiz - -::: quiz -? Which number does the first LED have? -→ Every LED has a number -- [x] 0 -- [ ] 1 -- [ ] 5 -> As with arrays, you count from 0. The five LEDs have the numbers 0 to 4. -? What does `mbot.led(4).setColor("off")` do? -→ Three lights in the structogram -- [ ] All LEDs go off. -- [x] Only LED 4 goes off. -- [ ] Four LEDs go off. -::: diff --git a/web/lessons/en/visubot/16-lautsprecher.md b/web/lessons/en/visubot/16-lautsprecher.md deleted file mode 100644 index fda4d446..00000000 --- a/web/lessons/en/visubot/16-lautsprecher.md +++ /dev/null @@ -1,167 +0,0 @@ ---- -titel: The speaker -bereich: Light, sound and screen -kurz: Tones, a scale and a siren. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the two numbers in `playTone` mean -- build a sequence of tones -- calculate with the counting variable in Java -- make the mBot2 show a feeling -::: - -## How high, how long? - -::: mbot2 -focus: cyberpi -view: top -::: - -The [CyberPi]{part=cyberpi} has a small speaker. `mbot.playTone(440, 300)` plays a tone: - -- The first number is the **pitch** in hertz (vibrations per second). The bigger, the higher. -- The second number is the **length** in milliseconds. - -| Note | C | D | E | F | G | A | B | C' | -|---|---|---|---|---|---|---|---|---| -| Hertz | 262 | 294 | 330 | 349 | 392 | 440 | 494 | 523 | - -> In the simulation you hear the tones through your computer's speakers. Feel free to turn it down a bit. - -## Scale in the structure chart - -::: übung vb-tonleiter -titel: Scale -auftrag: The mBot2 plays the notes C, D, E, F, G one after the other, each 300 milliseconds long. The hertz numbers are in the table. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -tipp: Drag **play a tone** into the structure chart five times. -tipp: The boxes are `mbot.playTone(262, 300)`, `mbot.playTone(294, 300)` and so on. -lösung: vb-tonleiter-loesung.visustruct -::: - -::: angeberwissen -The note A at 440 hertz is called **concert pitch**. Orchestras tune their instruments to it. One octave higher has exactly twice as many vibrations: 880 hertz. -::: - -## Now in Java: higher and higher - -In a for loop you can calculate with the counting variable. That way each tone is higher than the last: - -```java -mbot.playTone(i * 200, 200); -``` - -::: übung vb-java-hoeher -titel: Higher and higher -code: java -auftrag: Write a for loop in Java in which `i` counts from 1 to 5. In each round the mBot2 plays a tone with `i * 200` hertz, 200 milliseconds long. -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -braucht: forSchleife -tipp: `for (int i = 1; i <= 5; i++) { … }` -tipp: The loop only contains `mbot.playTone(i * 200, 200);`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Showing feelings - -The mBot2 can also be happy, sad or sleepy. With **show a feeling** (`mbot.emotion("smile")`) it plays a little show: the LEDs light up, it makes a sound and wiggles a bit. - -| Text | Feeling | -|---|---| -| `mbot.emotion("smile")` | laughs | -| `mbot.emotion("hello")` | says hello | -| `mbot.emotion("sad")` | is sad | -| `mbot.emotion("angry")` | is angry | -| `mbot.emotion("dizzy")` | is dizzy | -| `mbot.emotion("zzz")` | sleeps | - -> Try it with the real mBot2. The simulator only shows the command. More feelings are listed under **Reference**. - -::: übung vb-fehler-16-lautsprecher -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Higher and higher", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-16-lautsprecher-start.visustruct -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be `mbot.playTone(i * 200, 200)`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Game: From low to high - -::: spiel reihenfolge -titel: Game: From low to high -intro: Tap the answers in the right order. One wrong tap ends the round. Five rounds. - -runde: -frage: Tap the tones from low to high. -- `mbot.playTone(262, 300)` -- `mbot.playTone(330, 300)` -- `mbot.playTone(440, 300)` -- `mbot.playTone(880, 300)` -> The more hertz, the higher the tone. - -runde: -frage: Tap the tones from short to long. -- `mbot.playTone(440, 100)` -- `mbot.playTone(440, 300)` -- `mbot.playTone(440, 1000)` -- `mbot.playTone(440, 2000)` -> The second number is the length in milliseconds. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.playTone(i * 200, 200); - } -frage: Tap the tones in the order in which they sound. -- 600 hertz -- 400 hertz -- 200 hertz -> `i` counts down: 3, 2, 1. The tones get lower and lower. - -runde: -frage: Tap the tones from low to high. -- C at 262 hertz -- concert pitch A -- A one octave higher -> Concert pitch A has 440 hertz; one octave higher has twice as many: 880 hertz. - -runde: -frage: Tap the times from short to long. -- 300 milliseconds -- 1 second -- 1500 milliseconds -- 2 seconds -> 1 second is 1000 milliseconds, 2 seconds are 2000. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the two numbers in `playTone` mean. → How high, how long? -- I can build a sequence of tones. → Scale in the structure chart -- I can calculate with the counting variable in Java. → Now in Java: higher and higher -- I can make the mBot2 show a feeling. → Showing feelings -::: - -## Quiz - -::: quiz -? Which tone is higher? -→ How high, how long? -- [x] `mbot.playTone(880, 300)` -- [ ] `mbot.playTone(440, 300)` -> More hertz means higher. -? How long does `mbot.playTone(440, 1000)` sound? -→ How high, how long? -- [ ] 440 seconds -- [x] 1 second -- [ ] 1000 seconds -> 1000 milliseconds are one second. -::: diff --git a/web/lessons/en/visubot/17-arrays-melodie.md b/web/lessons/en/visubot/17-arrays-melodie.md deleted file mode 100644 index 818fe6d2..00000000 --- a/web/lessons/en/visubot/17-arrays-melodie.md +++ /dev/null @@ -1,244 +0,0 @@ ---- -titel: Arrays and melodies -bereich: Light, sound and screen -kurz: Store many values in one array and work through them with a loop. ---- - -## Skills I will gain now - -::: kompetenzen -- create an array and play it with a loop -- use an array of texts in Java -- collect measurements in an array and work out the average -::: - -## A melody as an array - -A melody is a row of tones. Instead of five variables `tone1`, `tone2`, … you use an **array**. An array is a row of slots under **one** name: - -```java -int[] tones = {262, 294, 330, 349, 392}; -``` - -- `int[]` means: an array of whole numbers. -- The slots are numbered from **0**. `tones[0]` is 262, `tones[4]` is 392. -- `tones.length` tells you how many slots there are, here 5. - -A for loop with `i` from 0 to `tones.length - 1` reaches every slot. - -::: übung vb-melodie -titel: Melody -auftrag: Create an array `tones` with the pitches 262, 294, 330, 349 and 392. A for loop plays them one after the other, each for 300 milliseconds. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -braucht: forSchleife -tipp: `int[] tones = {262, 294, 330, 349, 392}` creates the array. -tipp: The loop runs from `i = 0` while `i < tones.length`. Inside is `mbot.playTone(tones[i], 300)`. -lösung: vb-melodie-loesung.visustruct -::: - -> If you want a different melody, you only change the array. The loop stays the same. - -## Now in Java: rainbow - -The five LEDs have the numbers 0 to 4, just like the slots of an array with five values. An array can hold texts too: `String[] colours = {"red", "yellow"};`. LED `i` then gets the colour from slot `i`: `mbot.led(i).setColor(colours[i])`. - -::: übung vb-farben -titel: Rainbow -code: java -auftrag: Write in Java: create an array `colours` with the texts `"red"`, `"yellow"`, `"green"`, `"blue"` and `"purple"`. A for loop gives every LED `i` the colour from `colours[i]`. -befehle: ledColor 0 "red", ledColor 1 "yellow", ledColor 2 "green", ledColor 3 "blue", ledColor 4 "purple" -braucht: forSchleife -tipp: `String[] colours = {"red", "yellow", "green", "blue", "purple"};` creates the array. -tipp: `for (int i = 0; i < colours.length; i++) { mbot.led(i).setColor(colours[i]); }` -lösung: vb-farben-loesung.visustruct -::: - -## Now in Java: measurement series - -Sensors never measure exactly. Hold your hand in front of the ultrasonic sensor: the number always jumps around a little. That is why professionals measure several times and work out the **average**. - -The measurements go into an array. `new int[5]` creates an array with five empty slots: - -```java -int[] values = new int[5]; -for (int i = 0; i < values.length; i++) { - values[i] = mbot.getDistance(); - mbot.pause(100); -} -``` - -Then you add up all the values. For that you need a variable that starts at 0 and grows in every pass: - -```java -int sum = 0; -for (int i = 0; i < values.length; i++) { - sum = sum + values[i]; -} -``` - -The average is the sum divided by the number of values. Write `5.0` instead of `5`, then Java calculates with decimal numbers: `double average = sum / 5.0;` - -::: übung vb-messreihe -titel: Measurement series -code: java -auftrag: Measure the distance five times into an array `int[] values = new int[5]`, with a 100 ms pause in between. Then add up all values, work out the average and show for example **Average: 40.0** on the screen. -messwerte: getDistance=40 -> screen "Average: 40.0" -messwerte: getDistance=25 -> screen "Average: 25.0" -braucht: forSchleife -tipp: Two for loops: the first fills the array with `values[i] = mbot.getDistance();`, the second calculates `sum = sum + values[i];`. -tipp: Then `double average = sum / 5.0;` and `mbot.showText("Average: " + average);`. -lösung: vb-messreihe-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Arrays"](?visustruct&lektion=15-arrays). It opens in a new window. - -::: übung vb-fehler-17-arrays-melodie -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Measurement series", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-17-arrays-melodie-start.visustruct -messwerte: getDistance=40 -> screen "Average: 40.0" -messwerte: getDistance=25 -> screen "Average: 25.0" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `sum = sum + values[i]`. -lösung: vb-messreihe-loesung.visustruct -::: - -## Game: Which slot? - -::: spiel frage -titel: Game: Which slot? -intro: Slots are counted from 0. What is where in the array? Six rounds. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is `tones[2]`? -- 294 -- [x] 330 -- 349 -> Slot 0 is 262, slot 1 is 294, slot 2 is 330. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is `tones.length`? -- 4 -- [x] 5 -- 392 -> `length` counts the slots. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is `tones[tones.length - 1]`? -- 349 -- [x] 392 -- an error -> `tones.length - 1` is 4, the last slot. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What happens with `mbot.playTone(tones[5], 300)`? -- It plays 392 hertz. -- It plays nothing and carries on. -- [x] An error: there is no slot 5. -> The slots are numbered 0 to 4. Java reports an `ArrayIndexOutOfBoundsException`. - -runde: - String[] colours = {"red", "yellow", "green", "blue", "purple"}; - for (int i = 0; i < colours.length; i++) { - mbot.led(i).setColor(colours[i]); - } -frage: Which colour does LED 3 have? -- green -- [x] blue -- purple -> LED 3 gets `colours[3]`, the fourth word. - -runde: - int[] values = {20, 22, 18, 20, 20}; - int sum = 0; - for (int i = 0; i < values.length; i++) { - sum = sum + values[i]; - } - double average = sum / 5.0; -frage: What value does `average` have? -- 18.0 -- [x] 20.0 -- 100.0 -> The sum is 100, and 100 / 5.0 is 20.0. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - int[] tones = {262, 294, 330, 349, 392}; - for (int i = 0; i <= tones.length; i++) { - mbot.playTone(tones[i], 300); - } -frage: Should play all five tones. -fehler: 2 -besser: for (int i = 0; i < tones.length; i++) { -> With `<=` the loop also tries to play `tones[5]`. That slot doesn't exist. - -runde: - int[] tones = {262, 294, 330, 349, 392}; - for (int i = 0; i < tones.length; i++) { - mbot.playTone(tones, 300); - } -frage: Should play all five tones. -fehler: 3 -besser: mbot.playTone(tones[i], 300); -> One slot is played, `tones[i]`, not the whole array. - -runde: - String[] colours = {"red", "yellow", "green", "blue", "purple"}; - for (int i = 0; i < colours.length(); i++) { - mbot.led(i).setColor(colours[i]); - } -frage: Should give each LED a colour from the array. -fehler: 2 -besser: for (int i = 0; i < colours.length; i++) { -> Arrays have `length` without brackets. Only `length()` for Strings needs brackets. - -runde: - int[] values = new int[5]; - int sum = 0; - for (int i = 0; i < values.length; i++) { - sum = values[i]; - } -frage: Should add up all the measurements. -fehler: 4 -besser: sum = sum + values[i]; -> Like this, only the last value ends up in `sum`. The new value has to be added on. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create an array and play it with a loop. → A melody as an array -- I can use an array of texts in Java. → Now in Java: rainbow -- I can collect measurements in an array and work out the average. → Now in Java: measurement series -::: - -## Quiz - -::: quiz -? What is `tones[1]` if `int[] tones = {262, 294, 330};`? -→ A melody as an array -- [ ] 262 -- [x] 294 -- [ ] 1 -> The slots count from 0. So slot 1 is the second one. -? How many slots does `{262, 294, 330}` have, so what is `tones.length`? -→ A melody as an array -- [ ] 2 -- [x] 3 -- [ ] 330 -> `length` counts the slots, here three. That is why the last slot has the number 2. -::: diff --git a/web/lessons/en/visubot/18-bildschirm.md b/web/lessons/en/visubot/18-bildschirm.md deleted file mode 100644 index e2aae904..00000000 --- a/web/lessons/en/visubot/18-bildschirm.md +++ /dev/null @@ -1,206 +0,0 @@ ---- -titel: The screen -bereich: Light, sound and screen -kurz: Show text, numbers and sensor values on the CyberPi. ---- - -## Skills I will gain now - -::: kompetenzen -- show text on the screen -- show several texts one after the other -- show numbers and sensor values -- write a countdown in Java -::: - -## Showing text - -::: mbot2 -focus: screen -view: back -::: - -The CyberPi's colour [screen]{part=screen} is an actuator too. With **show text** you write something on it: - -```java -mbot.showText("Hello!") -``` - -The text is in quotation marks, as with `System.out.println`. A new text replaces the old one. - -> In the simulation the text appears on the screen of the 3D model and below the world after 📺. - -## Greeting in the structure chart - -::: übung vb-begruessung -titel: Greeting -auftrag: The mBot2 first shows “Hello!”, waits 1000 milliseconds and then shows “I am VisuBot”. -befehle: showText "Hello!", pause 1000, showText "I am VisuBot" -tipp: Drag **show text** twice and **wait a moment** once into the structure chart. -tipp: `mbot.showText("Hello!")`, `mbot.pause(1000)`, `mbot.showText("I am VisuBot")`. -lösung: vb-begruessung-loesung.visustruct -::: - -## Numbers and sensor values - -Instead of text you can also show numbers and variables, and with `+` you join everything together: - -```java -mbot.showText(i) -mbot.showText("Distance: " + mbot.getDistance()) -``` - -That way the mBot2 shows you what its sensors are measuring right now. - -## Now in Java: countdown - -::: übung vb-java-countdown -titel: Countdown -code: java -auftrag: Write a countdown in Java: a for loop counts `i` backwards from 3 to 1. In each round the mBot2 shows `i` and waits 1000 milliseconds. After that it shows “Go!”. -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Go!" -braucht: forSchleife -tipp: Counting backwards: `for (int i = 3; i >= 1; i--) { … }` -tipp: Inside the loop: `mbot.showText(i);` and `mbot.pause(1000);`. After the loop `mbot.showText("Go!");`. -lösung: vb-java-countdown-loesung.visustruct -::: - -::: übung vb-fehler-18-bildschirm -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Countdown", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-18-bildschirm-start.visustruct -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Go!" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `mbot.showText(i)`. -lösung: vb-java-countdown-loesung.visustruct -::: - -## Game: What's on the screen? - -::: spiel frage -titel: Game: What's on the screen? -intro: Read the code and predict what the screen shows at the end. Six rounds. - -runde: - mbot.showText("Hello!"); - mbot.pause(1000); - mbot.showText("I am VisuBot"); -frage: What is on the screen at the end? -- Hello! I am VisuBot -- [x] I am VisuBot -- Hello! -> A new text replaces the old one. - -runde: - mbot.showText("Distance: " + 25); -frage: What is on the screen? -- [x] Distance: 25 -- "Distance: " + 25 -- Distance: -> The `+` attaches the number to the text. - -runde: - int i = 3; - mbot.showText("i"); -frage: What is on the screen? -- 3 -- [x] i -- "i" -> In quotation marks, `i` is a text, not a variable. The quotation marks themselves don't appear. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.showText(i); - mbot.pause(1000); - } - mbot.showText("Start!"); -frage: Which number is shown just before "Start!"? -- 0 -- [x] 1 -- 3 -> The countdown shows 3, 2, 1. At 0, `i >= 1` is false. - -runde: - mbot.showText("Sum: " + 2 + 3); -frage: What is on the screen? -- Sum: 5 -- [x] Sum: 23 -- Sum: 2 + 3 -> Java works from left to right: first the 2 is attached to the text, then the 3. - -runde: - mbot.showText("Sum: " + (2 + 3)); -frage: What is on the screen? -- [x] Sum: 5 -- Sum: 23 -- Sum: (2 + 3) -> Java works out the brackets first. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.showText("Hello!"); - mbot.pause(1); - mbot.showText("I am VisuBot"); -frage: Should show "Hello!", wait one second and then show "I am VisuBot". -fehler: 2 -besser: mbot.pause(1000); -> `pause` counts in milliseconds. One second is 1000. - -runde: - for (int i = 3; i >= 1; i++) { - mbot.showText(i); - mbot.pause(1000); - } -frage: Countdown: should show 3, 2, 1. -fehler: 1 -besser: for (int i = 3; i >= 1; i--) { -> Counting down means `i--`. With `i++` the loop never stops. - -runde: - mbot.showText(Hello!); -frage: Should show "Hello!". -fehler: 1 -besser: mbot.showText("Hello!"); -> Text goes in quotation marks. - -runde: - mbot.showText("Distance: " - mbot.getDistance()); -frage: Should show for example "Distance: 25". -fehler: 1 -besser: mbot.showText("Distance: " + mbot.getDistance()); -> You join text and number with `+`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can show text on the screen. → Showing text -- I can show several texts one after the other. → Greeting in the structure chart -- I can show numbers and sensor values. → Numbers and sensor values -- I can write a countdown in Java. → Now in Java: countdown -::: - -## Quiz - -::: quiz -? What does `mbot.showText("Distance: " + 25)` show? -→ Numbers and sensor values -- [x] Distance: 25 -- [ ] "Distance: " + 25 -- [ ] 25 -> The `+` adds the number to the text. -? What happens to the old text when you show a new one? -→ Showing text -- [x] It is replaced. -- [ ] The new one appears below it. -::: diff --git a/web/lessons/en/visubot/19-zeichenketten.md b/web/lessons/en/visubot/19-zeichenketten.md deleted file mode 100644 index a19f283b..00000000 --- a/web/lessons/en/visubot/19-zeichenketten.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -titel: Strings -bereich: Light, sound and screen -kurz: Work with texts, pick out letters and show them on the screen. ---- - -## Skills I will gain now - -::: kompetenzen -- work with `length()` and `substring(…)` -- join texts and numbers with `+` -- get single characters with `charAt(…)` -::: - -## Letter by letter - -In Java a text is called a **string** or `String`. It consists of single characters, numbered from **0** just like an array: - -| Character | V | i | s | u | B | o | t | -|---|---|---|---|---|---|---|---| -| Number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | - -- `word.length()` gives the number of characters, here 7. -- `word.substring(0, 3)` gives the first 3 characters: `"Vis"`. - -The mBot2 should build its name on the [screen]{part=screen}: first **V**, then **Vi**, then **Vis** and so on up to **VisuBot**. - -::: übung vb-buchstaben -titel: Build the name -auftrag: Create `String word = "VisuBot"`. A for loop counts `i` from 1 to `word.length()`. Inside, the mBot2 shows `word.substring(0, i)` and waits 300 milliseconds. -befehle: showText "V", pause 300, showText "Vi", pause 300, showText "Vis", pause 300, showText "Visu", pause 300, showText "VisuB", pause 300, showText "VisuBo", pause 300, showText "VisuBot", pause 300 -braucht: forSchleife -tipp: The loop starts at `int i = 1` and runs as long as `i <= word.length()`. -tipp: Inside the loop: `mbot.showText(word.substring(0, i))` and `mbot.pause(300)`. -lösung: vb-buchstaben-loesung.visustruct -::: - -## Now in Java: text and length - -With `+` you join texts and numbers. This way the mBot2 shows a text followed by its length. - -::: übung vb-java-textlaenge -titel: How long is the text? -code: java -auftrag: Write in Java: create `String text = "Hello class"`. The mBot2 shows the text and its length in brackets, so **Hello class (11)**. -befehle: showText "Hello class (11)" -tipp: `text.length()` gives the length. The space counts too. -tipp: `mbot.showText(text + " (" + text.length() + ")");` -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Now in Java: scrolling text - -`word.charAt(i)` gives the character with the number `i`. If you show the characters one after the other with a pause, the word runs across the screen letter by letter: - -```java -String word = "Ludwig"; -for (int i = 0; i < word.length(); i++) { - mbot.showText(word.charAt(i)); - mbot.pause(500); -} -``` - -| Pass | `i` | Screen | -|---|---|---| -| 1 | 0 | L | -| 2 | 1 | u | -| 3 | 2 | d | - -::: übung vb-laufschrift -titel: Ludwig backwards -code: java -auftrag: Create `String word = "Ludwig"`. Show the word **backwards** letter by letter on the screen, so g, i, w, d, u, L, with a 500 ms pause after each letter. -befehle: showText "g", pause 500, showText "i", pause 500, showText "w", pause 500, showText "d", pause 500, showText "u", pause 500, showText "L", pause 500 -braucht: forSchleife -tipp: The last character has the number `word.length() - 1`. The loop counts backwards: `for (int i = word.length() - 1; i >= 0; i--)`. -tipp: Inside the loop: `mbot.showText(word.charAt(i));` and `mbot.pause(500);`. -lösung: vb-laufschrift-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Strings"](?visustruct&lektion=17-zeichenketten). It opens in a new window. - -::: übung vb-fehler-19-zeichenketten -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "How long is the text?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-19-zeichenketten-start.visustruct -befehle: showText "Hello class (11)" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be `mbot.showText(text +`. -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should show and tap the wrong line before time runs out. Five rounds, 30 seconds each. -zeit: 30 - -runde: - String word = "VisuBot"; - for (int i = 1; i <= word.length(); i++) { - mbot.showText(word.substring(1, i)); - mbot.pause(300); - } -frage: Should show V, Vi, Vis and so on up to VisuBot. -fehler: 3 -besser: mbot.showText(word.substring(0, i)); -> The first character has the number 0. With `substring(1, i)` the V is missing. - -runde: - String text = "Hello class"; - mbot.showText(text + " (" + text.length + ")"); -frage: Should show **Hello class (11)**. -fehler: 2 -besser: mbot.showText(text + " (" + text.length() + ")"); -> For a String, `length()` is a method and needs brackets. Only arrays have `length` without brackets. - -runde: - String word = "Ludwig"; - for (int i = 0; i <= word.length(); i++) { - mbot.showText(word.charAt(i)); - mbot.pause(500); - } -frage: Should show L, u, d, w, i, g. -fehler: 2 -besser: for (int i = 0; i < word.length(); i++) { -> The last character has the number 5. At `i = 6` there is no character left. - -runde: - String word = "Ludwig"; - for (int i = word.length(); i >= 0; i--) { - mbot.showText(word.charAt(i)); - mbot.pause(500); - } -frage: Should show the word backwards: g, i, w, d, u, L. -fehler: 2 -besser: for (int i = word.length() - 1; i >= 0; i--) { -> Backwards you start at the last character, and that has the number `word.length() - 1`. - -runde: - String name = 'Ludwig'; - mbot.showText("Hello " + name); -frage: Should show **Hello Ludwig**. -fehler: 1 -besser: String name = "Ludwig"; -> Texts go in double quotation marks. Single ones are only for a single character (`char`). -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 8 6 LED: the mBot2 has five of these little lights -- ↓ 2 7 MILLISECOND: a thousandth of a second -- → 11 2 SCREEN: mbot.showText shows a text on it -- ↓ 4 4 ACTUATOR: a part that does something, e.g. light up -- → 4 1 ARRAY: a row of slots under one name -- → 9 1 HERTZ: unit of pitch: vibrations per second -- ↓ 1 1 SPEAKER: mbot.playTone plays a tone through it -- → 1 1 STRING: a text in Java -lösung: 7 7, 10 7, 7 4, 11 7, 12 7 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can work with `length()` and `substring(…)`. → Letter by letter -- I can join texts and numbers with `+`. → Now in Java: text and length -- I can get single characters with `charAt(…)`. → Now in Java: scrolling text -::: - -## Quiz - -::: quiz -? What does `"VisuBot".substring(0, 4)` give? -→ Letter by letter -- [ ] `"VisuB"` -- [x] `"Visu"` -- [ ] `"uBot"` -> `substring(0, 4)` takes the characters 0, 1, 2 and 3, so four of them. -? What does `"Ludwig".charAt(0)` give? -→ Now in Java: scrolling text -- [x] `L` -- [ ] `u` -- [ ] `g` -> Characters are counted from 0. Character 0 is the first one. -::: diff --git a/web/lessons/en/visubot/20-motoren.md b/web/lessons/en/visubot/20-motoren.md deleted file mode 100644 index 892e2feb..00000000 --- a/web/lessons/en/visubot/20-motoren.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -titel: Motors and speed -bereich: Motors and methods -kurz: Step on the gas, steer and follow a line. ---- - -## Skills I will gain now - -::: kompetenzen -- step on the gas with wheels separately and stop -- drive until a sensor reports something -- write a line follower in Java -::: - -## Two motors, two wheels - -::: mbot2 -focus: motors, wheels -view: bottom -::: - -The mBot2 has two [motors]{part=motors}, each drives one [wheel]{part=wheels}. So far you have used **move forward** and **turn right**. These commands drive a fixed distance and wait until the mBot2 has arrived. - -With **wheels separately** (`mbot.drive(left, right)`) you step on the gas instead: you say how strongly each wheel turns, in percent from −100 to 100. The mBot2 then **keeps going** until you tell it something else. With **stop** (`mbot.stop()`) it stops. - -| Command | What happens? | -|---|---| -| `mbot.drive(40, 40)` | both wheels equally fast: straight ahead | -| `mbot.drive(-40, -40)` | backwards | -| `mbot.drive(10, 40)` | right faster: curve to the left | -| `mbot.drive(40, -40)` | wheels against each other: turns right on the spot | -| `mbot.stop()` | stop | - -> In the simulator `mbot.drive(100, 100)` drives 20 cm per second. Try it in the [simulation](#zeige-simulation). - -## Full speed to the wall in the structure chart - -In lesson 5 the mBot2 drove to the wall in steps of 10 cm. With **wheels separately** it works without jerking: step on the gas once, wait until the wall is close, then stop. - -::: übung vb-gas-bis-wand -titel: Full speed to the wall -auftrag: The mBot2 steps on the gas with 50 %. As long as the wall is more than 15 cm away, it waits a moment. Then it stops. There are three worlds. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: At the top is `mbot.drive(50, 50)`. Below it a while loop with the condition `mbot.getDistance() > 15`. -tipp: Inside the loop is only **wait a moment**. The mBot2 keeps driving meanwhile! After the loop is **stop**. -lösung: vb-gas-bis-wand-loesung.visustruct -::: - -## The line follower - -::: mbot2 -focus: lineSensor -view: bottom -::: - -A classic: the mBot2 follows a black line on the floor. For this it looks at the two middle probes of the [line sensor]{part=lineSensor}: - -| Sensor | Text | -|---|---| -| left middle probe sees black | `mbot.isLineLeft()` | -| right middle probe sees black | `mbot.isLineRight()` | - -The idea: if **both** see the line, it drives straight ahead. If only the **left** one sees it, the line has moved to the left: it steers left, so the right wheel turns faster. If only the **right** one sees it, it steers right. - -## Now in Java: line follower - -::: übung vb-java-linienfolger -titel: Line follower -code: java -auftrag: Write a line follower in Java. As long as the wall is more than 15 cm away, the mBot2 checks its two probes and steers. Then it stops. In the first world the line bends to the right, in the second to the left. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: `while (mbot.getDistance() > 15) { … }` and after it `mbot.stop();`. -tipp: Inside the loop: `if (mbot.isLineLeft() && mbot.isLineRight()) { mbot.drive(40, 40); } else if (mbot.isLineLeft()) { mbot.drive(10, 40); } else if (mbot.isLineRight()) { mbot.drive(40, 10); }` -lösung: vb-java-linienfolger-loesung.visustruct -::: - -> For the real mBot2, stick the line with black insulating tape on a light floor. Drive slowly at first, otherwise it shoots past the line in the curve. - -::: übung vb-fehler-20-motoren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Line follower", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-20-motoren-start.visustruct -welt: linie-1, linie-2 -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be `mbot.stop()`. -lösung: vb-java-linienfolger-loesung.visustruct -::: - -## Game: Where does it go? - -::: spiel frage -titel: Game: Where does it go? -intro: Power for left and right in percent: where does the mBot2 go? Seven rounds. -antworten: straight ahead | backwards | curve to the left | curve to the right | turns on the spot - -runde: - mbot.drive(40, 40); -frage: Where does it go? -richtig: straight ahead -> Both wheels turn at the same speed. - -runde: - mbot.drive(-40, -40); -frage: Where does it go? -richtig: backwards -> Minus means backwards, and both wheels are the same. - -runde: - mbot.drive(10, 40); -frage: Where does it go? -richtig: curve to the left -> The right wheel is faster, so it steers to the left. - -runde: - mbot.drive(40, 10); -frage: Where does it go? -richtig: curve to the right -> The left wheel is faster, so it steers to the right. - -runde: - mbot.drive(40, -40); -frage: Where does it go? -richtig: turns on the spot -> One wheel forward, one back: it turns right without moving from the spot. - -runde: - mbot.drive(-40, 40); -frage: Where does it go? -richtig: turns on the spot -> This time it turns to the left. - -runde: - if (mbot.isLineLeft() && !mbot.isLineRight()) { - ... - } -frage: Line follower: only the left sensor sees the line. What goes in the gap? -- `mbot.drive(40, 10);` -- [x] `mbot.drive(10, 40);` -- `mbot.stop();` -> The line has moved to the left. It steers to the left, so the right wheel turns faster. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can step on the gas with wheels separately and stop. → Two motors, two wheels -- I can drive until a sensor reports something. → Full speed to the wall in the structure chart -- I can write a line follower in Java. → Now in Java: line follower -::: - -## Quiz - -::: quiz -? What does `mbot.drive(30, 30)` do? -→ Two motors, two wheels -- [ ] It drives 30 cm and stops. -- [x] It drives straight ahead and does not stop until another driving command comes. -- [ ] It turns by 30°. -> **wheels separately** only steps on the gas. You have to stop it yourself. -? The mBot2 should steer to the right. Which command fits? -→ Two motors, two wheels -- [ ] `mbot.drive(10, 40)` -- [x] `mbot.drive(40, 10)` -- [ ] `mbot.drive(-40, -40)` -> If the left wheel turns faster, it drives a right curve. -::: diff --git a/web/lessons/en/visubot/21-stoppuhr-akku.md b/web/lessons/en/visubot/21-stoppuhr-akku.md deleted file mode 100644 index 15408a29..00000000 --- a/web/lessons/en/visubot/21-stoppuhr-akku.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -titel: Stopwatch and battery -bereich: Motors and methods -kurz: Measure time and keep an eye on the battery. ---- - -## Skills I will gain now - -::: kompetenzen -- reset the stopwatch and read it -- drive for a certain time -- show the battery level -::: - -## The stopwatch - -::: mbot2 -focus: cyberpi -view: top -::: - -A stopwatch runs inside the [CyberPi]{part=cyberpi}. It counts the seconds since the program started. With **reset stopwatch** (`mbot.resetTimer()`) it starts again at 0. - -| Sensor or command | Text | Value | -|---|---|---| -| reset stopwatch | `mbot.resetTimer()` | | -| stopwatch | `mbot.getTimer()` | seconds with a decimal place, e.g. `1.5` | - -This is how you drive for a certain time: reset the stopwatch, step on the gas, wait as long as the stopwatch is not far enough yet, stop. - -## Driving two seconds in the structure chart - -::: übung vb-zwei-sekunden -titel: Two seconds -auftrag: The mBot2 resets the stopwatch and steps on the gas with 50 %. As long as the stopwatch is below 2 seconds, it waits a moment. Then it stops. With 50 % it manages 10 cm per second, so it must stop 20 cm further on. -welt: zeit-1 -braucht: whileSchleife -tipp: At the top are **reset stopwatch** and `mbot.drive(50, 50)`. Below it a while loop with `mbot.getTimer() < 2`. -tipp: Inside the loop is **wait a moment**, after the loop **stop**. -lösung: vb-zwei-sekunden-loesung.visustruct -::: - -## The battery - -::: mbot2 -focus: shield -view: top -::: - -Under the transparent [shield]{part=shield} you see the battery. How full it is comes from `mbot.getBattery()` in percent: `100` is completely full. When it is almost empty, the motors get weaker. Then it is time to charge. - -::: angeberwissen -Your phone does not measure its battery directly in percent either. It measures the voltage: a full lithium battery has a little over 4 volts, an empty one just over 3. From that it calculates the percentage, just like the CyberPi. -::: - -## Now in Java: battery display - -::: übung vb-java-akku -titel: Battery display -code: java -auftrag: Write in Java: the mBot2 shows for example **Battery: 80 %** on the screen. If the battery is below 20 %, the LEDs also light up red. -messwerte: getBattery=80 -> none, ledOff, screen "Battery: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Battery: 15 %" -braucht: verzweigung -tipp: `mbot.showText("Battery: " + mbot.getBattery() + " %");` puts the text together from three parts. -tipp: After it: `if (mbot.getBattery() < 20) { mbot.setLed(255, 0, 0); }` -lösung: vb-java-akku-loesung.visustruct -::: - -::: übung vb-fehler-21-stoppuhr-akku -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Battery display", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-21-stoppuhr-akku-start.visustruct -messwerte: getBattery=80 -> none, ledOff, screen "Battery: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Battery: 15 %" -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be `mbot.setLed(255, 0, 0)`. -lösung: vb-java-akku-loesung.visustruct -::: - -## Game: Stopwatch and battery - -::: spiel frage -titel: Game: Stopwatch and battery -intro: Read the code and predict what happens. Five rounds. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: How long does the mBot2 drive? -- 0.1 seconds -- [x] 2 seconds -- until it reaches the wall -> It waits until the stopwatch shows 2 seconds, then it stops. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: At 50 % it manages 10 cm per second. How far does it drive? -- 10 cm -- [x] 20 cm -- 50 cm -> 2 seconds times 10 cm per second is 20 cm. - -runde: - mbot.pause(3000); - mbot.resetTimer(); - mbot.pause(1000); - mbot.showText(mbot.getTimer()); -frage: Roughly which number does the screen show? -- 0 -- [x] 1 -- 4 -> After `mbot.resetTimer()` the stopwatch starts again at 0. After that only 1 second passes. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Battery: " + mbot.getBattery() + " %"); -frage: The battery has 15 % left. What happens? -- Only the screen shows **Battery: 15 %**. -- Only the LEDs light up red. -- [x] The screen shows **Battery: 15 %**, and the LEDs light up red. -> 15 is less than 20. The display is below the branch and always comes. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Battery: " + mbot.getBattery() + " %"); -frage: The battery has 80 %. What happens? -- [x] Only the screen shows **Battery: 80 %**. -- Only the LEDs light up red. -- Nothing at all. -> 80 is not less than 20, so the LEDs stay off. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can reset the stopwatch and read it. → The stopwatch -- I can drive for a certain time. → Driving two seconds in the structure chart -- I can show the battery level. → Now in Java: battery display -::: - -## Quiz - -::: quiz -? What does `mbot.getTimer()` give right after `mbot.resetTimer()`? -→ The stopwatch -- [x] 0 -- [ ] 100 -- [ ] The time of day -> The stopwatch starts again at 0. -? Why does the mBot2 only wait **a moment** in the loop instead of stopping? -→ Driving two seconds in the structure chart -- [ ] Because otherwise the loop does not start. -- [x] Because it should keep driving until the time is up. -- [ ] Because otherwise the stopwatch stops. -> Stepping on the gas still applies while it waits. -::: diff --git a/web/lessons/en/visubot/21-zustaende.md b/web/lessons/en/visubot/21-zustaende.md deleted file mode 100644 index c93c9cd7..00000000 --- a/web/lessons/en/visubot/21-zustaende.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -titel: States: what am I doing right now? -bereich: Motors and methods -kurz: Switch a robo traffic light on with a state variable and a switch. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what a state is -- act depending on the state with a switch and change to the next state -- tell states apart in Java with names and `.equals` -::: - -## What is a state? - -A traffic light is always in exactly one **state**: red, green or yellow. In each state it knows two things: what do I show? And what comes next? - -Our robo traffic light has three states. Each gets a number: - -| State | LEDs | next | -|---|---|---| -| 0 | red | 1 | -| 1 | green | 2 | -| 2 | yellow | 0 | - -You store the current state in a variable: `int state = 0`. A variable like this is called a **state variable**. A program that jumps from state to state like this is called a **state machine**. - -> A real traffic light in Germany has four states: red, red-yellow, green, yellow. Our robo traffic light leaves out red-yellow. - -::: angeberwissen -A robot vacuum is a state machine too: vacuuming, avoiding, driving back to the charging station. I only have two states: eating and sleeping. -::: - -## The robo traffic light in the structure chart - -The switch fits perfectly: there is one case for each state. Each case has two boxes: what do I do? And which state comes next? - -::: übung vb-ampel-zustand -titel: Robo traffic light -auftrag: Build a structure chart: create `int state = 0`. A for loop runs six times. Inside it is a switch on `state`. Case 0: LEDs red, `state = 1`. Case 1: LEDs green, `state = 2`. Otherwise: LEDs yellow, `state = 0`. After the switch the mBot2 waits one second. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, fallauswahl -tipp: You will find the switch among the Elements. Write `state` into it. The cases are called 0, 1 and default. -tipp: The colours are `mbot.setLed(255, 0, 0)` for red, `mbot.setLed(0, 255, 0)` for green and `mbot.setLed(255, 200, 0)` for yellow. Below the switch, still inside the loop, is `mbot.pause(1000)`. -lösung: vb-ampel-zustand-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Switch"](?visustruct&lektion=10-fallauswahl). It opens in a new window. - -## Now in Java: states with names - -Numbers are easy to forget. What was state 2 again? Names are easier to read. Then the state is a text: - -```java -String state = "red"; -... -if (state.equals("red")) { - mbot.setLed(255, 0, 0); - state = "green"; -} else if (state.equals("green")) { - ... -``` - -In Java you compare texts with `.equals("…")`, not with `==`. - -::: übung vb-java-ampel-text -titel: Traffic light with names -code: java -auftrag: Write the robo traffic light in Java with `String state = "red";`. Use `if` and `else if` to check which state the light is in: red, then green, then yellow, then red again. The loop runs six times, with a 1 second pause after each pass. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, verzweigung -tipp: Inside the loop: `if (state.equals("red")) { … } else if (state.equals("green")) { … } else { … }` and then `mbot.pause(1000);`. -tipp: In the red case are `mbot.setLed(255, 0, 0);` and `state = "green";`. Green switches to `"yellow"`, yellow back to `"red"`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -::: übung vb-fehler-21-zustaende -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Traffic light with names", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-21-zustaende-start.visustruct -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be `state = "green"`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -## States while driving - -The mBot2 can also drive with states. In the state **drive** it drives forward. When it sees a wall, it changes to the state **turn**. There it turns once and changes back to **drive**. - -::: diagramm vb-l21z-ausweichen.visustruct -Driving and avoiding with two states. Open the diagram in the editor and try it out in a world. -::: - -## Game: which state comes now? - -::: spiel frage -titel: Game: which state comes now? -intro: The robo traffic light switches: 0 red → 1 green → 2 yellow → 0 red. Which state comes next? Five rounds. -frage: Which state comes next? -antworten: 0 | 1 | 2 - -runde: -frage: The light is in state **0** (red) right now. -richtig: 1 -> After red comes green. - -runde: -frage: The light is in state **2** (yellow) right now. -richtig: 0 -> After yellow it starts all over again. - -runde: -frage: The light is in state **1** (green) right now. -richtig: 2 -> After green comes yellow. - -runde: -frage: The light starts in state 0 and switches on **twice**. -richtig: 2 -> 0 → 1 → 2. - -runde: -frage: The light starts in state 0 and switches on **three times**. -richtig: 0 -> 0 → 1 → 2 → 0. After three steps it is back at the start. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what a state is. → What is a state? -- I can act depending on the state with a switch and change to the next state. → The robo traffic light in the structure chart -- I can tell states apart in Java with names and `.equals`. → Now in Java: states with names -::: - -## Quiz - -::: quiz -? What does a state variable store? -→ What is a state? -- [ ] How often the loop has run -- [x] What the program is doing right now, for example red or green -- [ ] The biggest reading -? What is in each case of the switch? -→ The robo traffic light in the structure chart -- [x] What to do and which state comes next -- [ ] Only a pause -- [ ] A new loop -? How do you check in Java whether `state` contains the text `"red"`? -→ Now in Java: states with names -- [ ] `state == red` -- [x] `state.equals("red")` -- [ ] `state = "red"` -> You compare texts with `.equals`. A single `=` would assign `"red"`. -::: diff --git a/web/lessons/en/visubot/22-eigene-methoden.md b/web/lessons/en/visubot/22-eigene-methoden.md deleted file mode 100644 index 2980f731..00000000 --- a/web/lessons/en/visubot/22-eigene-methoden.md +++ /dev/null @@ -1,334 +0,0 @@ ---- -titel: Your own methods -bereich: Motors and methods -kurz: Write and call your own commands with parameters and return values. ---- - -## Skills I will gain now - -::: kompetenzen -- build and call a subprogram with a parameter -- recognise methods in Java -- recognise and call an overloaded method -- write a method with a return value -::: - -## A subprogram for the square - -The mBot2 can already drive a square. Now it should drive two squares of different sizes. Instead of building the loop twice, you write a **subprogram**. In Java it is called a **method**. It is a command of your own that you can call as often as you like. - -The header `void square(int side)` says: - -- `void`: the method gives no result back, it only **does** something. -- `square`: that is the name of your new command. -- `int side`: when you call it, you pass a number, the **parameter**. - -::: übung vb-quadrat-methode -titel: Two squares -auftrag: Build a subprogram `void square(int side)` that drives a square with side length `side`. The main program calls it twice: first with 30, then with 50. -fahrt: 4 x (forward 30, turnRight), 4 x (forward 50, turnRight) -braucht: aufruf -tipp: Create a new tab with **+** and give it the title `void square(int side)`. Inside is a for loop that runs `mbot.forward(side)` and `mbot.turnRight()` four times. -tipp: The main program contains two call boxes: `square(30)` and `square(50)`. -lösung: vb-quadrat-methode-loesung.visustruct -unterprogramm: vb-quadrat-unterprogramm-loesung.visustruct -::: - -## Methods in Java - -In Java every method lives in a class. The program always starts in the method `main`. From there `square` is called: - -```java -public class Squares { - public static void main(String[] args) { - square(30); - square(50); - } - - static void square(int side) { - for (int i = 0; i < 4; i++) { - mbot.forward(side); - mbot.turnRight(); - } - } -} -``` - -In [Code](#zeige-codelive) you see each method in its own tab, just like in the structogram. - -::: quiz -? What does `void` mean in `void square(int side)`? -→ Methods in Java -- [ ] The method has no parameter. -- [x] The method gives no result back. -- [ ] The method may only be called once. -> `void` means "empty": no value comes back. The parameter is in the brackets. -? With which value for `side` does `square(50)` run? -→ A subprogram for the square -- [ ] 30 -- [x] 50 -- [ ] with none -> The value in the brackets of the call becomes the parameter `side`. -::: - -## Same name, different parameters: overloading - -You know `mbot.forward()` and `mbot.forward(30)`. These are **two different methods with the same name**. Java can tell from the call which one is meant: by the **number** and the **type** of the parameters. This is called **overloading**. - -There is a third `forward` method. It has two parameters: a number and a **unit** as text. - -```java -mbot.forward(); // 10 cm (default) -mbot.forward(30); // 30 cm -mbot.forward(30, "cm"); // 30 cm, unit written out -mbot.forward(2, "rotations"); // the wheels turn twice -``` - -One wheel rotation is as far as the wheel's circumference: π · 6.5 cm ≈ 20.4 cm. So two rotations are about 40.8 cm. `mbot.backward` has the same three versions. - -These are the three headers. The method name is the same, the parameter lists are different: - -```java -void forward() -void forward(int cm) -void forward(int value, String unit) -``` - -You already know overloading: `System.out.println` exists for `int`, for `double`, for `String` and more. That is why you can simply put any of these values in the brackets. - -::: angeberwissen -Are the 20.4 cm really true for **your** mBot2? Professionals don't trust a number they haven't measured. Measure the wheel diameter and run `mbot.forward(5, "rotations")`: the expected distance is 102.1 cm. This is how software systems are tested: every **test case** has an expected result and an actual (measured) result. You test several times and write everything into a **test record**. Here is your [test record to print (PDF)](https://visubot.visucode.de/test-record-wheel-rotation.pdf). Give it to your teacher, so they can see whether all robots drive the same distance. -::: - -::: übung vb-ueberladung-umdrehung -titel: Square with rotations -auftrag: Drive a square in which every side is exactly **one wheel rotation** long. Use the overloaded method `mbot.forward(1, "rotations")`. -fahrt: 4 x (forward 20.42, turnRight) -braucht: forSchleife -tipp: A for loop with `int i = 0`, `i < 4`, `i++`. -tipp: Inside are `mbot.forward(1, "rotations")` and `mbot.turnRight()`. -lösung: vb-ueberladung-umdrehung-loesung.visustruct -::: - -::: übung vb-java-rechteck-umdrehungen -titel: Rectangle with rotations -code: java -auftrag: Write in Java: the mBot2 drives a rectangle. The long sides are **two** wheel rotations long, the short ones **one**. Use `mbot.forward(2, "rotations")` and `mbot.forward(1, "rotations")`. -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -braucht: forSchleife -tipp: A for loop that runs twice: `for (int i = 0; i < 2; i++) { … }`. -tipp: Inside the loop: `mbot.forward(2, "rotations");`, `mbot.turnRight();`, `mbot.forward(1, "rotations");`, `mbot.turnRight();` -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -::: quiz -? Which method does `mbot.forward(3, "rotations")` call? -→ Same name, different parameters: overloading -- [ ] `forward()` -- [ ] `forward(int cm)` -- [x] `forward(int value, String unit)` -> Java picks the version whose parameters fit: two parameters, a number and a text. -::: - -## A method with a return value - -A method can also **work something out** and give the result back. Instead of `void`, the header then has the data type of the result, and `return` gives the value back: - -```java -static int centimetres(int rotations) { - return rotations * 20; -} -``` - -One wheel rotation is about 20 cm. So the call `int distance = centimetres(3);` puts 60 into `distance`. - -::: übung vb-zentimeter -titel: Rotations into centimetres -auftrag: Build a subprogram `int centimetres(int rotations)` that returns `rotations * 20`. The main program calls it with 3, drives the distance and shows **Distance: 60 cm** on the screen. -fahrt: forward 60 -befehle: showText "Distance: 60 cm" -braucht: aufruf -tipp: A new tab with the title `int centimetres(int rotations)`. One statement is enough inside: `return rotations * 20`. -tipp: In the main program: call `int distance = centimetres(3)`, then `mbot.forward(distance)` and `mbot.showText("Distance: " + distance + " cm")`. -lösung: vb-zentimeter-loesung.visustruct -unterprogramm: vb-zentimeter-unterprogramm-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Call and break"](?visustruct&lektion=18-aufruf-und-aussprung). It opens in a new window. - -::: übung vb-fehler-22-eigene-methoden -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Rectangle with rotations", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-22-eigene-methoden-start.visustruct -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be `mbot.forward(1, "rotations")`. -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -## Game: Which method? - -::: spiel frage -titel: Game: Which method? -intro: Same name, different parameters: which method does Java call, and what comes out? Six rounds. - -runde: - mbot.forward(30, "cm"); -frage: Which `forward` method runs? -- `forward()` -- `forward(int cm)` -- [x] `forward(int value, String unit)` -> Two parameters, a number and a text: that only fits the third one. - -runde: - mbot.forward(); -frage: How far does the mBot2 drive? -- [x] 10 cm -- not at all, the number is missing -- one wheel rotation -> Without a parameter, `forward` drives 10 cm. That is the version with no parameters. - -runde: - mbot.forward(2, "rotations"); -frage: Roughly how far does the mBot2 drive? -- 2 cm -- 20 cm -- [x] 41 cm -> One wheel rotation is about 20.4 cm, so two are about 40.8 cm. - -runde: - static int centimetres(int rotations) { - return rotations * 20; - } -frage: What does `centimetres(4)` give? -- 4 -- 24 -- [x] 80 -> The parameter `rotations` is 4, and `return` gives back 4 · 20. - -runde: - public static void main(String[] args) { - square(30); - square(50); - } -frage: How many squares does the mBot2 drive, and how big is the second one? -- one with 80 cm -- [x] two, the second with 50 cm sides -- two, both with 30 cm sides -> Each call drives one square. The value in the brackets becomes `side`. - -runde: -frage: Which header fits a method that gives back a number? -- `void centimetres(int rotations)` -- [x] `int centimetres(int rotations)` -- `centimetres(int rotations)` -> The data type of the result comes before the name. `void` means: no result. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - static void square(int side) { - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } - } -frage: Should drive a square with sides of length `side`. -fehler: 3 -besser: mbot.forward(side); -> With `30` every square is the same size, whatever you pass in. - -runde: - static void centimetres(int rotations) { - return rotations * 20; - } -frage: Should give back the distance in cm. -fehler: 1 -besser: static int centimetres(int rotations) { -> A method that gives something back needs the data type of the result instead of `void`. - -runde: - static int centimetres(int rotations) { - int cm = rotations * 20; - } -frage: Should give back the distance in cm. -fehler: 2 -besser: return rotations * 20; -> The method calculates but gives nothing back. `return` is missing. - -runde: - mbot.forward(2, rotations); -frage: Should drive two wheel rotations. -fehler: 1 -besser: mbot.forward(2, "rotations"); -> The unit is a text and needs quotation marks. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 7 1 PARAMETER: value you write in the brackets when you call a method -- ↓ 5 7 METHOD: your own command with a name, e.g. square(30) -- ↓ 1 4 OVERLOADING: same method name, different parameters -- ↓ 2 9 BATTERY: mbot.getBattery() tells you how full it is -- → 2 4 VOID: in the header: the method gives nothing back -- ↓ 2 2 STOPWATCH: mbot.getTimer() reads it -- → 5 7 MOTOR: each wheel has its own -- ↓ 1 11 RETURN: gives a result back from a method -lösung: 7 5, 6 9, 5 9, 10 2, 5 8, 8 4 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build and call a subprogram with a parameter. → A subprogram for the square -- I can recognise methods in Java. → Methods in Java -- I can recognise and call an overloaded method. → Same name, different parameters: overloading -- I can write a method with a return value. → A method with a return value -::: - -## Quiz - -::: quiz -? The subprogram `void quadrat(int seite)` should drive a square with sides of 30 cm. What does the call look like? -→ A subprogram for the square -- [ ] `quadrat()` -- [x] `quadrat(30)` -- [ ] `int seite = quadrat(30)` -> The value in the brackets becomes the parameter `seite`. Because `quadrat` returns nothing, there is no assignment. -? In which method does a Java program start? -→ Methods in Java -- [ ] in the first method of the class -- [x] in the method `main` -- [ ] in the method `quadrat` -> The program always starts in `main`. The other methods are called from there. -? How does Java tell the overloaded methods `forward()`, `forward(int cm)` and `forward(int wert, String einheit)` apart? -→ Same name, different parameters: overloading -- [ ] by their name -- [x] by the number and type of the parameters -- [ ] by their order in the program -> The name is the same for all of them. Java chooses the variant whose parameters fit the call. -? `static int zentimeter(int umdrehungen) { return umdrehungen * 20; }`: What is in `strecke` after `int strecke = zentimeter(2);`? -→ A method with a return value -- [ ] 2 -- [ ] 20 -- [x] 40 -> `return` gives back 2 · 20 = 40, and this value ends up in `strecke`. -? A method returns a whole number. What is written in its header instead of `void`? -→ A method with a return value -- [ ] `return` -- [x] `int` -- [ ] `static` -> Instead of `void`, the header names the data type of the result. -::: diff --git a/web/lessons/en/visubot/23-wie-geht-es-weiter.md b/web/lessons/en/visubot/23-wie-geht-es-weiter.md deleted file mode 100644 index 66681ce8..00000000 --- a/web/lessons/en/visubot/23-wie-geht-es-weiter.md +++ /dev/null @@ -1,148 +0,0 @@ ---- -titel: What's next? -bereich: Outlook -kurz: Look back at what you learned with the mBot2 and see what is still waiting for you in VisuStruct. ---- - -## Skills I will gain now - -::: kompetenzen -- say which programming basics I learned with the mBot2 -- find and fix a mistake in a robot program on my own -- name the topics that are still waiting for me in VisuStruct -::: - -## What you can do now - -Congratulations, you have finished all the robot lessons! With the mBot2 you learned the **basics of structured programming**: - -- reading and building a **structure chart** and checking it with a **desk check**, writing a plan as a **flowchart** and as **pseudocode**, -- **sequence**, **branch** and **switch**, conditions with **and**, **or** and **not**, -- the loops **for**, **while** and **do-while**, also nested, and leaving a loop with **break**, -- **variables**, **data types** and **constants** with `final`, counting, adding up and finding the largest value, -- **arrays** and **strings**, -- your own **methods** with parameters and a return value. - -All of this is not just for robots. Every Java program is built from exactly these building blocks, even a game or an app. - -::: angeberwissen -Baah, do you know what you actually built with the mBot2? An **embedded system**: a small computer inside a device that measures its surroundings with sensors. Such systems are in washing machines, cars and pacemakers. A modern car has more than a hundred of them. And they are programmed with exactly the building blocks you know now. -::: - -## Once more: Why doesn't it run? - -One last bug hunt to finish. This time it is a mistake that even professionals often make. - -::: übung vb-fehler-23-wie-geht-es-weiter -titel: Why doesn't it run? -code: java -auftrag: The program should drive up to just before the wall, but it doesn't run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-23-wie-geht-es-weiter-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Read the error message carefully. Compare the sensor with the other commands: what comes after `mbot.forward`? -tipp: A command is always followed by round brackets, even if there is nothing inside them. It should be `mbot.getDistance() > 15`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## What is still missing - -Some programming topics have little to do with the robot. You will find them in **VisuStruct**, the world without the robot. Because you already know the basics, you will make quick progress there. The links open in a new window. - -- **Keyboard input:** asking for values while the program runs, in ["Input and output"](?visustruct&lektion=06-eingabe-und-ausgabe). -- **Tables in memory:** arrays with rows and columns, for example for a cinema, in ["Two-dimensional arrays"](?visustruct&lektion=16-zweidimensionale-arrays). -- **Searching and sorting:** finding a value quickly and putting a list in order, in ["Searching and sorting"](?visustruct&lektion=19-suchen-und-sortieren). -- **Other languages:** the same program in Python and JavaScript, in ["Python and JavaScript"](?visustruct&lektion=21-python-und-javascript). -- **Catching crashes:** what happens when someone types "abc" instead of a number, in ["When the program crashes: try and catch"](?visustruct&lektion=22-try-und-catch). -- **Files:** reading and saving text, tables and data, from ["Reading and saving text"](?visustruct&lektion=23-text-einlesen-und-speichern) via ["Tables as CSV"](?visustruct&lektion=25-tabellen-als-csv) to ["Data as JSON"](?visustruct&lektion=26-json). -- **Windows with buttons:** your own programs with a window, a text field and a button, from ["Your first window"](?visustruct&lektion=28-erstes-fenster) to ["Model View Controller"](?visustruct&lektion=40-model-view-controller). - -> You don't need the mBot2 for this. But you can come back at any time and teach it new tricks. - -## Game: Which world? - -::: spiel frage -titel: Game: Which world? -intro: Ludwig has ideas for new programs. Do you learn this with the mBot2 in VisuBot or without the robot in VisuStruct? Eight rounds. -antworten: VisuBot | VisuStruct - -runde: -frage: "Drive up to the wall and then beep." -richtig: VisuBot -> Distance sensor, motors and speaker: that is a job for the mBot2. - -runde: -frage: "A window with a button that counts to 10." -richtig: VisuStruct -> You build windows with buttons with Swing in VisuStruct. - -runde: -frage: "Play a melody that is stored in an array." -richtig: VisuBot -> You learned arrays and melodies with the mBot2's speaker. - -runde: -frage: "Read the class's marks from a CSV file." -richtig: VisuStruct -> You learn to read and save files in VisuStruct. - -runde: -frage: "If someone types 'abc' instead of a number, the program should not crash." -richtig: VisuStruct -> You catch crashes with try and catch. That is in VisuStruct. - -runde: -frage: "Follow a black line with the line sensor." -richtig: VisuBot -> The line sensor sits under the mBot2. - -runde: -frage: "Sort ten numbers by size." -richtig: VisuStruct -> Searching and sorting is in VisuStruct. But you can already find the largest value. - -runde: -frage: "Save the high score list in a file." -richtig: VisuStruct -> You learn to write files in VisuStruct in "Reading and saving text". -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can say which programming basics I learned with the mBot2. → What you can do now -- I can find and fix a mistake in a robot program on my own. → Once more: Why doesn't it run? -- I can name the topics that are still waiting for me in VisuStruct. → What is still missing -::: - -## Quiz - -::: quiz -? Do the building blocks from the robot lessons also work for programs without a robot? -→ What you can do now -- [x] Yes, every Java program is built from sequence, branch, loop, variables and methods -- [ ] No, without a robot you need completely different commands -- [ ] Only the loops -> Only the `mbot.` commands belong to the robot. Everything else is ordinary Java. - -? Why doesn't `mbot.getDistance > 15` run? -→ Once more: Why doesn't it run? -- [ ] Because 15 is too small -- [x] Because the round brackets after the command are missing -- [ ] Because you have to write `<` -> Without brackets, Java looks for a variable `getDistance` and finds none. - -? Where do you learn how a program catches crashes with try and catch? -→ What is still missing -- [ ] In VisuBot -- [x] In VisuStruct -> That is where the lesson "When the program crashes: try and catch" is. - -? Which topics do you find in VisuStruct? Choose the right list. -→ What is still missing -- [x] Sorting, files and windows with buttons -- [ ] Line sensor, LEDs and motors -- [ ] Only the for loop -> Line sensor, LEDs and motors belong to the mBot2, and you already know the for loop. -::: diff --git a/web/lessons/en/visubot/aufgaben/alarmanlage.md b/web/lessons/en/visubot/aufgaben/alarmanlage.md deleted file mode 100644 index f66bc472..00000000 --- a/web/lessons/en/visubot/aufgaben/alarmanlage.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: The alarm system -nach: 16 -stufe: 2 ---- - -The mBot2 guards your room. If someone comes closer than 20 cm **or** it gets loud (loudness above 60), it raises the alarm: the LEDs light up red and it beeps. Otherwise the LEDs are off. With button B you switch the alarm system off. - -::: übung vb-alarmanlage -titel: The alarm system -auftrag: As long as button B is not pressed, the mBot2 checks again and again: if the distance is below 20 cm or the loudness above 60, the LEDs light up red and it plays a tone. Otherwise the LEDs go off. Then it waits a moment. -messwerte: getDistance=10, isButtonB=false/true -> none, tone -messwerte: getLoudness=80, isButtonB=false/true -> none, tone -messwerte: getDistance=100, getLoudness=0, isButtonB=false/true -> none, noTone, ledOff -braucht: whileSchleife, verzweigung -tipp: On the outside is a while loop with `!mbot.isButtonB()`. Inside a branch and after it **wait a moment**. -tipp: The condition of the branch: `mbot.getDistance() < 20 || mbot.getLoudness() > 60`. In the true branch `mbot.setLed(255, 0, 0)` and `mbot.playTone(880, 200)`, in the false branch `mbot.ledOff()`. -lösung: vb-alarmanlage-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/ausgang.md b/web/lessons/en/visubot/aufgaben/ausgang.md deleted file mode 100644 index 040e0726..00000000 --- a/web/lessons/en/visubot/aufgaben/ausgang.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Maze with exits -nach: 5 -stufe: 3 ---- - -The mBot2 stands at the bottom left and faces right. Above it are rows of walls. Each has exactly one gap, and each gap lies further right than the one below. The goal is at the very top. - -The mBot2 drives to the right and looks up in every square. If there is a gap, it drives through into the next corridor. - -::: übung vb-ausgang -titel: Maze with exits -auftrag: Program the mBot2 so that in both worlds it drives through all gaps to the goal without bumping into anything. One square is 30 cm. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: In each pass the mBot2 first turns left and measures. If the way up is free, it is more than 20 cm. -tipp: Through the gap it is two squares into the next corridor. Afterwards it should face right again. If there is a wall above, it turns back and drives one square further. -lösung: vb-ausgang-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/binaer-zaehlen.md b/web/lessons/en/visubot/aufgaben/binaer-zaehlen.md deleted file mode 100644 index 17abc29c..00000000 --- a/web/lessons/en/visubot/aufgaben/binaer-zaehlen.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -titel: Counting in binary -nach: 15 -stufe: 3 ---- - -Computers calculate with only two digits: 0 and 1. That is called **binary**. With three LEDs you can show the numbers 0 to 7 in binary: LED 0 counts 1, LED 1 counts 2 and LED 2 counts 4. The number 5 is 4 + 1, so LED 2 and LED 0 light up. - -| Number | LED 2 (4) | LED 1 (2) | LED 0 (1) | -|---|---|---|---| -| 0 | off | off | off | -| 1 | off | off | on | -| 2 | off | on | off | -| 3 | off | on | on | -| 5 | on | off | on | - -::: übung vb-binaer-zaehlen -titel: Counting in binary -auftrag: A for loop counts `number` from 0 to 7. For each number, each of the LEDs 0, 1 and 2 lights up green or is off, exactly as in the table. Then the mBot2 waits 1000 milliseconds. Always switch the LEDs in the order 0, 1, 2. -befehle: ledColor 0 "off", ledColor 1 "off", ledColor 2 "off", pause 1000, ledColor 0 "green", ledColor 1 "off", ledColor 2 "off", pause 1000, ledColor 0 "off", ledColor 1 "green", ledColor 2 "off", pause 1000, ledColor 0 "green", ledColor 1 "green", ledColor 2 "off", pause 1000, ledColor 0 "off", ledColor 1 "off", ledColor 2 "green", pause 1000, ledColor 0 "green", ledColor 1 "off", ledColor 2 "green", pause 1000, ledColor 0 "off", ledColor 1 "green", ledColor 2 "green", pause 1000, ledColor 0 "green", ledColor 1 "green", ledColor 2 "green", pause 1000 -braucht: forSchleife, verzweigung -tipp: Whether LED 0 is on is given by the remainder when dividing by 2: `number % 2 == 1`. For LED 1 you first divide by 2: `number / 2 % 2 == 1`. For LED 2 by 4: `number / 4 % 2 == 1`. -tipp: The loop contains three branches and then **wait a moment**. In the true branch, for example, `mbot.led(0).setColor("green")`, in the false branch `mbot.led(0).setColor("off")`. -lösung: vb-binaer-zaehlen-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/dreieck.md b/web/lessons/en/visubot/aufgaben/dreieck.md deleted file mode 100644 index 023af1f2..00000000 --- a/web/lessons/en/visubot/aufgaben/dreieck.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -titel: The triangle -nach: 2 -stufe: 1 ---- - -For the square the mBot2 turns four times by 90°. Together that is 360°, one full turn. For a triangle it also turns all the way around once, but in only three corners. - -Inside the brackets of **turn right** you can write how far it should turn: `mbot.turnRight(45)` turns only 45°. - -::: übung vb-dreieck -titel: The triangle -auftrag: Let the mBot2 drive a triangle with three sides of equal length. Each side is 30 cm long. Use a loop. -fahrt: 3 x (forward 30, turnRight 120) -braucht: forSchleife -tipp: How many degrees must one corner be so that three corners add up to a full turn (360°)? -tipp: In the loop the mBot2 drives one side and then turns by 120°. -lösung: vb-dreieck-loesung.visustruct -::: - -::: angeberwissen -The idea of drawing shapes with a driving robot is more than 50 years old: the language **Logo** was created in the late 1960s. Children used it to steer a "turtle" that rolled across paper with a pen. -::: diff --git a/web/lessons/en/visubot/aufgaben/einparken.md b/web/lessons/en/visubot/aufgaben/einparken.md deleted file mode 100644 index b56d719a..00000000 --- a/web/lessons/en/visubot/aufgaben/einparken.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -titel: Reverse parking -nach: 16 -stufe: 2 ---- - -To the right of the road there are parked cars (the blocks). Somewhere there is a gap, and right in front of it a blue marking is stuck on the road. The mBot2 looks for the gap and reverses into it, like a car. - -It has no sensor at the back. So it looks forward at the wall opposite: the further it reverses into the gap, the bigger the distance to that wall gets. - -::: übung vb-einparken -titel: Reverse parking -auftrag: The mBot2 drives forward in steps of 10 cm until it is on blue. Then it turns left, so its rear points to the gap. It reverses as long as the distance to the wall in front of it is below 45 cm. Finally the LEDs light up green and it shows **Parked!**. There are two roads with different gaps. -welt: parken-1 -> ledOn, screen "Parked!" -welt: parken-2 -> ledOn, screen "Parked!" -braucht: whileSchleife -tipp: Two while loops one after the other: first `!mbot.getColor().equals("blue")` with `mbot.forward()` inside, then `mbot.getDistance() < 45` with `mbot.backward()` inside. In between is `mbot.turnLeft()`. -tipp: At the end: `mbot.setLed(0, 255, 0)` and `mbot.showText("Parked!")`. -lösung: vb-einparken-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/hindernis.md b/web/lessons/en/visubot/aufgaben/hindernis.md deleted file mode 100644 index b04b37a2..00000000 --- a/web/lessons/en/visubot/aufgaben/hindernis.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -titel: Avoid obstacles -nach: 16 -stufe: 2 ---- - -On the way to the red field there are blocks in the way. The mBot2 drives forward in steps of 10 cm. If a block shows up (closer than 20 cm), it warns with light and sound and drives around it: turn right, 30 cm forward, turn left. Then it carries on straight ahead. - -::: übung vb-hindernis -titel: Avoid obstacles -auftrag: The mBot2 drives as long as it is not on red. If a block is closer than 20 cm, the LEDs light up orange (`mbot.setLed(255, 128, 0)`), it beeps and drives around it. Then the LEDs go off. Otherwise it drives forward 10 cm. There are two worlds. -welt: hindernis-1 -> tone -welt: hindernis-2 -> tone -braucht: whileSchleife, verzweigung -tipp: On the outside a while loop with `!mbot.getColor().equals("red")`, inside a branch with `mbot.getDistance() < 20`. -tipp: In the true branch: `mbot.setLed(255, 128, 0)`, `mbot.playTone(880, 200)`, `mbot.turnRight()`, `mbot.forward(30)`, `mbot.turnLeft()`, `mbot.ledOff()`. In the false branch: `mbot.forward()`. -lösung: vb-hindernis-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/knight-rider.md b/web/lessons/en/visubot/aufgaben/knight-rider.md deleted file mode 100644 index 4de63316..00000000 --- a/web/lessons/en/visubot/aufgaben/knight-rider.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: Knight Rider -nach: 15 -stufe: 2 ---- - -In the TV series “Knight Rider”, the talking car KITT has a red light at the front that keeps running back and forth. You rebuild it with the five LEDs. - -::: übung vb-knight-rider -titel: Knight Rider -auftrag: A red light runs from LED 0 to LED 4 and back to LED 1. Each LED lights up for 100 milliseconds and then goes off. Use two for loops: one counts up, one counts down. -befehle: ledColor 0 "red", pause 100, ledColor 0 "off", ledColor 1 "red", pause 100, ledColor 1 "off", ledColor 2 "red", pause 100, ledColor 2 "off", ledColor 3 "red", pause 100, ledColor 3 "off", ledColor 4 "red", pause 100, ledColor 4 "off", ledColor 3 "red", pause 100, ledColor 3 "off", ledColor 2 "red", pause 100, ledColor 2 "off", ledColor 1 "red", pause 100, ledColor 1 "off" -braucht: forSchleife -tipp: The first loop counts `i` up from 0 to 4. The second starts at 3 and counts down with `i--` as long as `i > 0`. -tipp: In both loops: `mbot.led(i).setColor("red")`, `mbot.pause(100)`, `mbot.led(i).setColor("off")`. -lösung: vb-knight-rider-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/labyrinth.md b/web/lessons/en/visubot/aufgaben/labyrinth.md deleted file mode 100644 index f2c6f0ae..00000000 --- a/web/lessons/en/visubot/aufgaben/labyrinth.md +++ /dev/null @@ -1,25 +0,0 @@ ---- -titel: The maze -nach: 5 -stufe: 2 ---- - -The mBot2 is standing in a maze. The goal is a black square with a green flag. The corridors are 30 cm wide. - -A simple rule gets it through: **If the way ahead is free, drive forward. Otherwise turn right. If there is a wall there too, turn all the way around.** - -How does the mBot2 notice that it has reached the goal? The line sensor underneath sees the black square: `mbot.getLine()` is then no longer `0`. - -::: übung vb-labyrinth -titel: The maze -auftrag: Program the mBot2 so that it finds the goal in both mazes without bumping into a wall. -welt: labyrinth-1, labyrinth-2 -braucht: whileSchleife, verzweigung -tipp: On the outside a while loop with the condition `mbot.getLine() == 0`. It runs until the mBot2 is on the goal. -tipp: Inside the loop a branch `mbot.getDistance() > 15`: yes → `mbot.forward()`. No → `mbot.turnRight()`, then another branch `mbot.getDistance() < 15` with `mbot.turnRight(180)`. -lösung: vb-labyrinth-loesung.visustruct -::: - -::: angeberwissen -With the rule "always keep one hand on the wall" you get out of any maze whose walls are all connected. The ancient Greeks already told of Theseus, who found his way through the Minotaur's labyrinth with a ball of thread. -::: diff --git a/web/lessons/en/visubot/aufgaben/linienfolger-stopp.md b/web/lessons/en/visubot/aufgaben/linienfolger-stopp.md deleted file mode 100644 index 7448d39f..00000000 --- a/web/lessons/en/visubot/aufgaben/linienfolger-stopp.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Line follower with stop -nach: 20 -stufe: 3 ---- - -The mBot2 follows a black line and stops at the red field at its end. - -For this it steps on the gas with **wheels separately**: `mbot.drive(40, 40)` turns both wheels equally fast, it keeps driving straight ahead until another driving command comes. `mbot.drive(10, 40)` makes a left curve, `mbot.drive(40, 10)` a right curve. `mbot.stop()` stops it. - -You ask the two middle probes of the line sensor with `mbot.isLineLeft()` and `mbot.isLineRight()`. If both see the line, it drives straight ahead. If only the left one sees it, it steers left, if only the right one sees it, right. - -::: übung vb-linienfolger-stopp -titel: Line follower with stop -auftrag: As long as the mBot2 is not on red, it follows the line. Then it stops. In the second world the line winds about. -welt: linie-rot-1, linie-rot-2 -braucht: whileSchleife, verzweigung -tipp: On the outside a while loop with `!mbot.getColor().equals("red")`, after it **stop**. -tipp: Inside the loop: if `mbot.isLineLeft() && mbot.isLineRight()`, then `mbot.drive(40, 40)`. Otherwise, if `mbot.isLineLeft()`, then `mbot.drive(10, 40)`. Otherwise, if `mbot.isLineRight()`, then `mbot.drive(40, 10)`. -lösung: vb-linienfolger-stopp-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/morsen.md b/web/lessons/en/visubot/aufgaben/morsen.md deleted file mode 100644 index b2cb503b..00000000 --- a/web/lessons/en/visubot/aufgaben/morsen.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: Morse code -nach: 15 -stufe: 2 ---- - -Ships used to send messages with light, in **Morse code**: every letter is made of short and long signals. The best-known distress call is **SOS**: S is three short, O is three long. - -::: übung vb-morsen -titel: Morse SOS -auftrag: LED 2 sends SOS in white. Short means: on for 200 milliseconds, then off for 200. Long means: on for 600 milliseconds, then off for 200. Between the letters the mBot2 waits another 400 milliseconds. Use a loop for each letter. -befehle: 3 x (ledColor 2 "white", pause 200, ledColor 2 "off", pause 200), pause 400, 3 x (ledColor 2 "white", pause 600, ledColor 2 "off", pause 200), pause 400, 3 x (ledColor 2 "white", pause 200, ledColor 2 "off", pause 200) -braucht: forSchleife -tipp: Three for loops one after the other, each runs three times. Between them there is **wait a moment** with 400. -tipp: In the first and third loop: `mbot.led(2).setColor("white")`, `mbot.pause(200)`, `mbot.led(2).setColor("off")`, `mbot.pause(200)`. In the middle one it waits 600 after switching on. -lösung: vb-morsen-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/nachtwaechter.md b/web/lessons/en/visubot/aufgaben/nachtwaechter.md deleted file mode 100644 index 163444f0..00000000 --- a/web/lessons/en/visubot/aufgaben/nachtwaechter.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -titel: The night watchman -nach: 18 -stufe: 1 ---- - -The mBot2 is a night watchman. As long as it is dark (brightness below 20), it goes on patrol: 30 cm forward and then turn around. As soon as it gets light, its shift is over: it shows **Zzz** on its screen and sleeps. - -::: übung vb-nachtwaechter -titel: The night watchman -auftrag: As long as the brightness is below 20, the mBot2 drives forward 30 cm and turns by 180°. Then it shows **Zzz** on its screen. -messwerte: getBrightness=5/80 -> drives, screen "Zzz" -messwerte: getBrightness=80 -> none, screen "Zzz" -braucht: whileSchleife -tipp: A while loop with `mbot.getBrightness() < 20`. Inside `mbot.forward(30)` and `mbot.turnRight(180)`. -tipp: After the loop is `mbot.showText("Zzz")`. -lösung: vb-nachtwaechter-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/slalom.md b/web/lessons/en/visubot/aufgaben/slalom.md deleted file mode 100644 index 4a57c373..00000000 --- a/web/lessons/en/visubot/aufgaben/slalom.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Four cones stand in a row in front of the mBot2. It drives a slalom: past the first cone on the top side, past the second on the bottom side, then top and bottom again. Behind the last cone is the goal. - -In this world one square is 30 cm, as wide as the mBot2. One step from square to square is therefore `mbot.forward(30)`. - -::: übung vb-slalom -titel: Slalom -auftrag: Drive the slalom to the goal without touching a cone. Use a loop. -welt: slalom -braucht: forSchleife -tipp: First think about how the mBot2 drives around a single cone on the top side: afterwards it is two squares further along in the same row and faces right again. -tipp: Around the bottom works the same way, only with left and right swapped. "Top, then bottom" repeats twice. -lösung: vb-slalom-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/spirale.md b/web/lessons/en/visubot/aufgaben/spirale.md deleted file mode 100644 index 3e12caf4..00000000 --- a/web/lessons/en/visubot/aufgaben/spirale.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: The spiral -nach: 2 -stufe: 2 ---- - -The mBot2 should drive a square spiral: the first side is 10 cm long, the second 20 cm, the third 30 cm and so on. After each side it turns right. - -You can also use the counter variable of a for loop inside the loop, for example in the brackets of **drive forward**. A calculation is allowed there too, such as `mbot.forward(i * 5)`. - -::: übung vb-spirale -titel: The spiral -auftrag: Let the mBot2 drive a spiral with 8 sides: 10 cm, 20 cm, 30 cm … up to 80 cm. After each side it turns right. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight, forward 50, turnRight, forward 60, turnRight, forward 70, turnRight, forward 80, turnRight -braucht: forSchleife -tipp: Let the counter variable run from 1 to 8. How long is the side in each pass then? -tipp: The brackets of **drive forward** contain a calculation with the counter variable. -lösung: vb-spirale-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/treppe.md b/web/lessons/en/visubot/aufgaben/treppe.md deleted file mode 100644 index 8d267f08..00000000 --- a/web/lessons/en/visubot/aufgaben/treppe.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -titel: The staircase -nach: 2 -stufe: 1 ---- - -The mBot2 climbs a staircase, seen from above: each step is 20 cm deep and 20 cm wide. - -::: übung vb-treppe -titel: The staircase -auftrag: Let the mBot2 drive three steps. One step: 20 cm forward, turn left, 20 cm forward, turn right. Use a loop. -fahrt: 3 x (forward 20, turnLeft, forward 20, turnRight) -braucht: forSchleife -tipp: Write down one step first. Then put it into a for loop that runs three times. -tipp: Inside the loop: `mbot.forward(20)`, `mbot.turnLeft()`, `mbot.forward(20)`, `mbot.turnRight()`. -lösung: vb-treppe-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/tunnel.md b/web/lessons/en/visubot/aufgaben/tunnel.md deleted file mode 100644 index 07b10c85..00000000 --- a/web/lessons/en/visubot/aufgaben/tunnel.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: The tunnel finder -nach: 5 -stufe: 3 ---- - -The mBot2 should stop in the first **tunnel square**: a square with a wall on the left **and** a wall on the right. Squares with only one wall don't count. In the simulation the right square is light green, but the line sensor can't see it. - -The ultrasonic sensor only looks forward. To look left, the mBot2 turns left, measures and turns back again. - -You remember the measured value in a **variable**, for example with a statement `left = mbot.getDistance()`. At the start you create it: `int left = 99`. - -::: übung vb-tunnel -titel: The tunnel finder -auftrag: Program the mBot2 so that it stops in the first tunnel square in both worlds. One square is 30 cm. -welt: tunnel-1, tunnel-2 -braucht: doUntilSchleife -tipp: In each pass the mBot2 drives one square forward and then looks left and right. Remember both distances in variables. At the end it faces forward again. -tipp: A loop that drives first and checks afterwards fits here. It keeps going as long as at least 20 cm are free on the left **or** on the right: `left >= 20 || right >= 20`. -lösung: vb-tunnel-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/aufgaben/wald.md b/web/lessons/en/visubot/aufgaben/wald.md deleted file mode 100644 index 723c87c2..00000000 --- a/web/lessons/en/visubot/aufgaben/wald.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -titel: Clover in the forest -nach: 5 -stufe: 2 ---- - -The goal is straight ahead. There are trees on the way, but never two directly behind each other. If a tree stands in front of the mBot2, it drives around it and then continues straight ahead. - -One square is 30 cm. If there is a tree in the next square, the ultrasonic sensor measures less than 20 cm. - -::: übung vb-wald -titel: Clover in the forest -auftrag: In both worlds the mBot2 should drive to the goal and go around every tree without bumping into it. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: As in "Drive to the goal", a loop on the outside runs until the line sensor sees the goal. In each pass there are two possibilities: tree ahead or free. -tipp: Around a tree: turn left, one square forward, turn right, two squares forward, turn right, one square forward, turn left. -lösung: vb-wald-loesung.visustruct -::: diff --git a/web/lessons/en/visubot/nachschlagen.md b/web/lessons/en/visubot/nachschlagen.md deleted file mode 100644 index 30265fe0..00000000 --- a/web/lessons/en/visubot/nachschlagen.md +++ /dev/null @@ -1,168 +0,0 @@ -## 🤖 mBot2 commands - -A command tells the mBot2 what to **do**. Every command starts with `mbot.` and ends with brackets. Often you write a number in the brackets. - -### 🚗 Driving and turning {.bot-fahren} - -The mBot2 drives straight or turns on the spot. - -``` -mbot.forward() drives 10 cm forward -mbot.forward(30) drives 30 cm forward -mbot.forward(2, "rotations") the wheels turn twice (≈ 41 cm) -mbot.backward(1, "rotations") one wheel rotation back (≈ 20 cm) -mbot.backward(20) drives 20 cm back -mbot.turnRight() turns to the right (90°) -mbot.turnLeft() turns to the left (90°) -mbot.turnLeft(45) turns just a little to the left (45°) -mbot.turnRight(180) turns all the way round -``` - -> `forward` and `backward` are **overloaded**: same name, different parameters. Units are `"cm"` and `"rotations"`. One wheel rotation is about 20.4 cm. - -> Without a number it drives 10 cm or turns 90°. 90° is a right angle, like the corner of a sheet of paper. - -> For a polygon it turns all the way round once in total: triangle 3 × 120°, square 4 × 90°, hexagon 6 × 60°. - -### ⚙️ Wheels separately {.bot-fahren} - -Here you give each wheel gas yourself. The numbers are percent: 100 is full gas, 0 is standing still, a number with a minus drives backwards. The mBot2 keeps going until another driving command comes. - -``` -mbot.drive(40, 40) both wheels the same: straight ahead -mbot.drive(10, 40) left slower: curve to the left -mbot.drive(40, -40) one wheel forward, one back: it turns -mbot.stop() stops -``` - -### 💡 Light {.bot-licht} - -The mBot2 has five LEDs with the numbers 0 to 4. - -``` -mbot.setLed(255, 0, 0) all LEDs light up red -mbot.ledOff() all LEDs off -mbot.led(0).setColor("green") only LED 0 lights up green -mbot.led(0).setColor("off") only LED 0 off -``` - -With `setLed` you mix red, green and blue, each from 0 (off) to 255 (very bright). - -### 🔊 Sound {.bot-ton} - -``` -mbot.playTone(440, 300) plays a tone -``` - -The first number is the pitch. The bigger, the higher. The second number is the length in milliseconds (1000 milliseconds are 1 second). - -### 🖥️ Screen {.bot-anzeige} - -``` -mbot.showText("Hello") shows Hello on the screen -``` - -With `+` you join text and numbers: `mbot.showText("Distance: " + mbot.getDistance())`. - -### 😊 Emotions {.bot-anzeige} - -The mBot2 shows a feeling with light, sound and wiggling. You only see it on the real mBot2. - -``` -mbot.emotion("smile") laughs -mbot.emotion("sad") is sad -mbot.emotion("wow") is amazed -``` - -More feelings: hello, yeah, proud, yummy, naughty, uh_oh, hurt, angry, startle, shiver, dizzy, sleepy, zzz, wake_up, blink, yes, no, look_up, look_down, look_left, look_right, look_around - -### ⏱️ Waiting and stopwatch {.bot-warten} - -``` -mbot.pause(1000) waits 1 second -mbot.resetTimer() sets the stopwatch to 0 -``` - -You read the stopwatch time with the sensor `mbot.getTimer()` (below under **Stopwatch and battery**). - -## 📡 mBot2 sensors - -A sensor **measures** something and gives you a value back. You use the value in a condition, for example in a loop or a branch. Some sensors give a **number**, some **true or false**, one gives a **text**. - -### 📏 Distance {.bot-sensor} - -The ultrasonic sensor at the front measures how far away the next wall is, in centimetres. - -``` -mbot.getDistance() distance in cm, e.g. 25 -mbot.getDistance() > 15 is there more than 15 cm free ahead? -int distance = mbot.getDistance() remember the distance in a variable -``` - -### ➖ Line {.bot-sensor} - -Under the mBot2 there are four probes. They see whether the floor is black. - -``` -mbot.getLine() number: 0 means no probe sees black -mbot.getLine() == 0 no black, keep driving -mbot.getLine() != 0 a probe sees black -mbot.isLineLeft() does the left middle probe see black? -mbot.isLineRight() does the right middle probe see black? -``` - -### 🎨 Colours {.bot-sensor} - -The same sensor underneath also recognises colours. `mbot.getColor()` gives you the name of the colour as text: `"red"`, `"green"`, `"blue"`, `"yellow"`, `"white"` or `"black"`. - -``` -mbot.getColor().equals("red") is it standing on red? -!mbot.getColor().equals("red") is it not standing on red? -``` - -> You compare texts with `.equals(…)`, not with `==`. The `!` in front means **not**. - -### 🕹️ Buttons and joystick {.bot-sensor} - -On the CyberPi there are the buttons A and B and a small joystick. Each sensor says **true** while it is being pressed. - -``` -mbot.isButtonA() is button A pressed? -mbot.isButtonB() is button B pressed? -mbot.isJoystickUp() joystick pushed up? -mbot.isJoystickDown() joystick pushed down? -mbot.isJoystickLeft() joystick pushed left? -mbot.isJoystickRight() joystick pushed right? -``` - -### 🔆 Light and loudness {.bot-sensor} - -``` -mbot.getBrightness() how bright? 0 is dark, 100 is bright -mbot.getLoudness() how loud? 0 is silent, 100 is loud -``` - -### 📐 Tilt and shaking {.bot-sensor} - -``` -mbot.getPitch() tipped forward or back? (in degrees) -mbot.getRoll() tipped to the side? (in degrees) -mbot.isShaken() is it being shaken? -``` - -> When the mBot2 lies flat, both tilts are 0. In the simulation you set these values under **🎛 Try the sensors**. - -### 🔋 Stopwatch and battery {.bot-sensor} - -``` -mbot.getTimer() seconds since the start or since resetTimer() -mbot.getBattery() how full is the battery? (in percent) -``` - -### Real mBot2 - -With **🔌 mBot2** next to "Start simulation" you connect the real robot with the USB cable. This only works in Chrome or Edge. Then it drives every step of the simulation along, and the sensors measure real values. A second click disconnects it again. - -### Driving without a cable - -With **⬆ Upload** you save your program on the mBot2. Then unplug the cable, put the mBot2 at the start and press **button A**: it drives your program on its own. It stays saved until you upload a new one. Keyboard inputs do not work on the mBot2. diff --git a/web/lessons/pt/01-was-ist-ein-struktogramm.md b/web/lessons/pt/01-was-ist-ein-struktogramm.md deleted file mode 100644 index 97894452..00000000 --- a/web/lessons/pt/01-was-ist-ein-struktogramm.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -titel: O que é um estrutograma? -bereich: Primeiros passos -kurz: Mostrar e ler algoritmos como uma série de passos claros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o que é um algoritmo -- ler um estrutograma -- reconhecer os três blocos básicos: sequência, decisão e ciclo -::: - -## Um algoritmo é uma instrução exata - -Um **algoritmo** descreve passo a passo como resolver uma tarefa. Cada passo tem de ser tão claro que qualquer um o consiga executar sem perguntar, tanto uma pessoa como um computador. - -Conheces algoritmos do dia a dia: uma receita, o caminho para um sítio ou as instruções para montar um roupeiro. - -::: diagramm l1-tee.visustruct -Fazer chá como estrutograma: cada bloco é um passo. -::: - -::: angeberwissen -Méééé, quem escreveu o primeiro programa? Uma mulher, já em 1843, muito antes do primeiro computador! **Ada Lovelace** descreveu passo a passo como a *Analytical Engine* (Máquina Analítica) de Charles Babbage devia calcular os chamados números de Bernoulli. Era um verdadeiro algoritmo para uma máquina. As instruções deviam ficar guardadas em cartões perfurados (mais sobre isto na lição 3). Mas a máquina nunca chegou a ser construída. - -A Ada teve ainda outra ideia genial: uma máquina destas consegue fazer mais do que contas. Também podia trabalhar com letras, símbolos ou até música. É exatamente isso que os computadores fazem hoje. Em sua honra, há uma linguagem de programação chamada **Ada**. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), matemática (imagem: aguarela de Alfred Edward Chalon, por volta de 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), domínio público). -::: - -## Como se lê um estrutograma - -Um estrutograma lê-se **de cima para baixo**. Cada bloco é um passo. Só quando um passo termina é que começa o seguinte. - -Esta forma de desenhar foi inventada por **Isaac Nassi e Ben Shneiderman** (1973). Por isso também se chama diagrama de Nassi-Shneiderman. Na Alemanha está normalizado como DIN 66261. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -À esquerda: Isaac “Ike” Nassi (foto: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/), recortada), à direita: Ben Shneiderman (foto: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> Um estrutograma tem sempre exatamente um início (em cima) e um fim (em baixo). Não há setas nem saltos para todo o lado. Programas que saltam para todo o lado chamam-se **código esparguete**, porque o seu fluxo se enrola como massa no prato. Mais sobre isto na próxima lição. - -::: angeberwissen -Méééé, ouve bem! Em 1972, Nassi e Shneiderman ainda eram doutorandos na Universidade de Stony Brook, perto de Nova Iorque. Shneiderman tinha uns 25 anos e Nassi uns 23. Ou seja, dois estudantes que escreveram história da informática nas horas vagas. -::: - -## Bloco básico 1: a sequência - -Qualquer algoritmo pode ser construído com apenas **três blocos básicos**: **sequência**, [**decisão**]{.blau} e [**ciclo**]{.rosa}. As cores ajudam-te a associá-los: as [instruções]{.gelb} são amarelas, as decisões azuis, os ciclos cor-de-rosa. A sequência não tem cor própria, é simplesmente a fila de instruções umas por baixo das outras. Vês as mesmas cores no editor e mais tarde no código. Podes desligá-las nas definições em “Blocos coloridos”. Neste passo e nos dois seguintes vais conhecê-los um a um. - -A **sequência** é o bloco mais simples: várias instruções são executadas **uma a seguir à outra**, cada uma exatamente uma vez. - -É assim que se parece: - -- Cada instrução é um **retângulo simples**. -- Os retângulos ficam **uns por cima dos outros**, sem espaço entre eles. -- São executados de cima para baixo, uma instrução depois da outra. - -::: diagramm l1-sequenz.visustruct -Quatro instruções em sequência. Primeiro levantas-te, depois lavas os dentes, e assim por diante. -::: - -> O exemplo do chá lá em cima também é uma sequência. - -::: angeberwissen -15 minutos! Foi o tempo de que Shneiderman precisou para o primeiro esboço com sequência, decisão e ciclo. Eu preciso de mais tempo do que isso para escolher entre feno e trevo. -::: - -## Bloco básico 2: a decisão - -Muitas vezes o passo seguinte depende de uma **condição**. Uma condição é uma pergunta a que só se pode responder com **sim ou não**, por exemplo *Está a chover?* No VisuStruct, sim é **true** e não é **false**. - -Para decisões destas existe a **decisão**. É assim que se parece: - -- Em cima, a condição fica dentro de um **triângulo**. -- Por baixo, o bloco divide-se em **duas colunas**: à esquerda o ramo para **true**, à direita o ramo para **false**. -- Só é executado **um** dos dois ramos. Depois, os dois ramos continuam juntos por baixo da decisão. - -::: diagramm l1-verzweigung.visustruct -Se estiver a chover, é executado o ramo da esquerda, senão o da direita. Depois, os dois casos continuam com “Sair”. -::: - -> Se num dos casos não deve acontecer nada, esse ramo fica vazio. O sinal **ø** indica isso. - -::: angeberwissen -Alerta de ortografia! Escreve-se Shneiderman, sem c. Quem escrever “Schneiderman” leva um empurrãozinho dos meus chifres. Ele próprio conta que as pessoas estão sempre a meter um c no nome dele. -::: - -## Bloco básico 3: o ciclo - -Se uma coisa deve acontecer **várias vezes**, não a escreves várias vezes. Em vez disso usas um **ciclo**, também chamado repetição. - -É assim que se parece: - -- Em cima está a condição, por exemplo *Prato não vazio*. -- Os passos que se repetem ficam **recuados** por baixo dela. Em cima e à esquerda, o bloco forma um **L invertido** à volta deles. -- Antes de cada iteração, a condição é verificada. Enquanto for **true**, os passos dentro do L invertido são executados mais uma vez. Se for **false**, o programa continua por baixo do ciclo. - -::: diagramm l1-wiederholung.visustruct -Enquanto o prato não estiver vazio, dás uma garfada. Depois, o prato é levantado. -::: - -> Há mais tipos de ciclos. Vais conhecê-los nas próximas lições. - -## Os três juntos - -Os blocos básicos tornam-se realmente úteis quando os combinas. Este diagrama contém os três: uma sequência de cima para baixo, uma [decisão]{.blau} e um [ciclo]{.rosa}. As caixas amarelas são [instruções]{.gelb} simples. As instruções dentro de uma decisão ou de um ciclo também são amarelas. Em rigor, o diagrama todo é uma sequência, porque a decisão e o ciclo também estão na fila de cima para baixo. - -::: diagramm l1-grundbausteine.visustruct -Reconheces a decisão pelo triângulo e o ciclo pelo L invertido. Tudo junto forma uma sequência. -::: - -Os blocos também podem ser **encaixados** uns dentro dos outros: um ciclo ou um ramo pode voltar a conter sequências, decisões e ciclos. - -::: angeberwissen -Consolo para quem correu mal um teste: a famosa revista *Communications of the ACM* rejeitou o artigo sobre estrutogramas. Segundo Nassi, o revisor até disse que deviam juntar todas as cópias e queimá-las. Ai! O artigo saiu em 1973 numa revista mais pequena e hoje é citado milhares de vezes. Ha! -::: - -## Primeiro a estrutura, depois o código - -Quando programas, acontecem duas coisas ao mesmo tempo: tens de pensar nos passos e prestar atenção à forma como a linguagem de programação se escreve. Um estrutograma permite-te separar as duas coisas. - -1. Primeiro pensas na **lógica**: que passos, que condições, que repetições? -2. Depois traduzes a estrutura pronta para **Java, Python ou JavaScript**. O VisuStruct até consegue gerar o código por ti. - -Com a **simulação** também podes executar o teu estrutograma passo a passo e verificar se ele faz mesmo o que deve. - -## Jogo: Que bloco básico? - -::: spiel frage -titel: Jogo: Que bloco básico? -intro: O Ludwig descreve coisas do seu dia a dia. Que bloco básico se esconde em cada uma? Oito rondas. -antworten: sequência | decisão | ciclo - -runde: -frage: "Pasta na escova, lavar os dentes, bochechar." -richtig: sequência -> Três passos, cada um exatamente uma vez e sempre por esta ordem. - -runde: -frage: "Se chover, levo o guarda-chuva, senão os óculos de sol." -richtig: decisão -> Uma condição decide qual dos dois caminhos é seguido. - -runde: -frage: "Mexer até a massa ficar lisa." -richtig: ciclo -> Mexe-se repetidamente enquanto a massa ainda não estiver lisa. - -runde: -frage: "Correr dez voltas ao campo de desporto." -richtig: ciclo -> A mesma volta é corrida dez vezes. - -runde: -frage: "Calçar os sapatos, vestir o casaco, abrir a porta, e lá vamos." -richtig: sequência -> Um passo a seguir ao outro, nada é saltado nem repetido. - -runde: -frage: "Se o semáforo estiver verde, atravessas a rua." -richtig: decisão -> Mesmo sem "senão" é uma decisão. O ramo senão fica simplesmente vazio. - -runde: -frage: "Lançar o dado até sair um seis." -richtig: ciclo -> Não se sabe antes quantas vezes. Lança-se até a condição se cumprir. - -runde: -frage: "Se alguém disser 'Entra', entras. Senão, esperas à porta." -richtig: decisão -> A resposta decide entre dois caminhos: entrar ou esperar. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o que é um algoritmo. → Um algoritmo é uma instrução exata -- Consigo ler um estrutograma de cima para baixo. → Como se lê um estrutograma -- Consigo nomear os três blocos básicos: sequência, decisão e ciclo. → Bloco básico 1: a sequência -- Consigo reconhecer cada bloco básico num estrutograma pela sua forma. → Os três juntos -::: - -## Teste de conhecimentos - -::: quiz -? Em que direção se lê um estrutograma? -→ Como se lê um estrutograma -- [ ] Da esquerda para a direita -- [x] De cima para baixo -- [ ] De baixo para cima -> Cada bloco é um passo, e os passos seguem-se de cima para baixo. - -? Qual destes NÃO é um dos três blocos básicos? -→ Bloco básico 1: a sequência -- [ ] Sequência -- [ ] Decisão -- [x] Salto -- [ ] Ciclo -> Num estrutograma não há saltos. Sequência, decisão e ciclo chegam para qualquer algoritmo. - -? Como reconheces uma decisão num estrutograma? -→ Os três juntos -- [ ] Por um L invertido -- [x] Por um triângulo com a condição e duas colunas por baixo -- [ ] Por vários retângulos uns por cima dos outros -> O L invertido pertence ao ciclo, e retângulos uns por cima dos outros são uma sequência. - -? Quando é que o interior de um ciclo é executado mais uma vez? -→ Os três juntos -- [x] Enquanto a condição for verdadeira -- [ ] Só se a condição for falsa -- [ ] Sempre exatamente duas vezes -> A condição é verificada antes de cada iteração. Se for falsa, o programa continua por baixo do ciclo. - -? O que é um algoritmo? -→ Um algoritmo é uma instrução exata -- [x] Instruções claras, passo a passo, para resolver uma tarefa -- [ ] Um determinado programa em Java -- [ ] Uma imagem que explica um programa -> Um algoritmo não depende de nenhuma linguagem de programação. Um estrutograma é uma forma de o escrever. -::: diff --git a/web/lessons/pt/01-weitere-darstellungen.md b/web/lessons/pt/01-weitere-darstellungen.md deleted file mode 100644 index b9f4ffd1..00000000 --- a/web/lessons/pt/01-weitere-darstellungen.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Fluxograma e pseudocódigo -bereich: Primeiros passos -kurz: Representar algoritmos como fluxograma e como pseudocódigo e compará-los com o estrutograma. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ler um fluxograma (em inglês *flowchart*) e construir um sozinho -- explicar porque é que os saltos num fluxograma levam a erros -- ler um algoritmo simples em pseudocódigo -::: - -## Outras formas de desenhar: o fluxograma - -Há mais duas formas de representar um algoritmo: o **fluxograma** e o **pseudocódigo**. Vais conhecer as duas nas aulas. Em inglês, o fluxograma chama-se **flowchart**. Esta palavra aparece muitas vezes, por exemplo em programas e na internet. - -Aqui vês outra vez o nosso estrutograma para beber chá da última lição. A seguir vamos ver o fluxograma, com o mesmo exemplo. - -::: diagramm l1-grundbausteine.visustruct -O exemplo do chá como estrutograma: primeiro a chávena, depois a decisão para o açúcar e depois o ciclo. -::: - -### O mesmo algoritmo como fluxograma - -::: grafik l01-pap.svg -O exemplo do chá como fluxograma. A seta de “Esperar 1 minuto” para cima forma o ciclo. -::: - -### Os símbolos - -Um fluxograma é feito de poucos símbolos ligados por **setas**. Está normalizado como DIN 66001 (ISO 5807). - -- **Oval:** início ou fim. Cada fluxograma tem exatamente um início e um fim. -- **Retângulo:** uma instrução, por exemplo *Pôr uma chávena na mesa*. -- **Losango:** uma decisão. No losango está uma pergunta, e dele saem duas setas: **sim** e **não**. -- **Paralelogramo:** uma entrada ou saída. No exemplo do chá não aparece. -- **Seta:** mostra qual é o passo seguinte. - -Um ciclo não tem símbolo próprio no fluxograma. Reconheces um ciclo por uma seta que volta **para cima**. - -### As vantagens - -- **Muito fácil de ler**, mesmo para quem nunca viu um fluxograma. Basta seguir as setas com o dedo. -- Os fluxogramas não se usam só na programação, mas em todo o lado onde se explica um processo: em manuais de instruções, na procura de avarias (“A lâmpada acende? Não: a ficha está ligada?”) ou nas empresas para os processos de trabalho. - -## Exercício: Fazer um bolo como fluxograma - -Agora és tu que constróis um fluxograma. O Ludwig faz um bolo assim: - -Põe os ingredientes na tigela e mexe a massa. Se a massa ainda não estiver lisa, continua a mexer. Quando está lisa, deita a massa na forma e coze o bolo durante 30 minutos. Depois testa com um palito: se ainda tiver massa agarrada, coze mais 5 minutos e testa outra vez. Quando o palito sai limpo, tira o bolo e diz: “O bolo está pronto!” - -::: pap -titel: Exercício: Fazer um bolo como fluxograma -auftrag: Os lugares no fluxograma estão vazios. Põe cada elemento no seu lugar. A forma do lugar ajuda-te. -start: Início -anweisung: Pôr os ingredientes na tigela -anweisung: Mexer a massa -frage: Massa lisa? | nein -> Mexer a massa -anweisung: Deitar a massa na forma -anweisung: Cozer 30 minutos -frage: Palito limpo? | nein: Cozer mais 5 minutos -anweisung: Tirar o bolo do forno -ausgabe: “O bolo está pronto!” -ende: Fim -erklärung: Dois ciclos, duas setas para cima: ao mexer, a seta volta a “Mexer a massa”; ao cozer, vai de “Cozer mais 5 minutos” de volta à pergunta. A saída fica no paralelogramo. -legende: ja -::: - -**Desenhar os teus próprios fluxogramas:** Com o **VisuFlow** desenhas o teu fluxograma com toda a facilidade. Os símbolos estão prontos numa barra: tocar num acrescenta o símbolo seguinte e liga-o logo com uma seta. Para começar, podes carregar o exemplo do chá. Guardas o teu fluxograma como ficheiro e exportas como imagem ou PDF, por exemplo para o teu caderno ou uma apresentação. [Abrir o VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Desvantagens do fluxograma: saltos - -Uma seta num fluxograma pode ir de qualquer símbolo para qualquer outro. Parece prático, mas é perigoso. - -Antigamente, os programas escreviam-se exatamente assim. Cada linha tinha um **número**, e o comando **GOTO**, em português “ir para”, saltava para um número de linha. Cada IR PARA é uma seta do fluxograma. O nosso exemplo do chá ficava assim: - -``` -10 Pôr uma chávena na mesa -20 SE não quer açúcar, IR PARA 40 -30 Juntar açúcar -40 SE chá não demasiado quente, IR PARA 70 -50 Esperar 1 minuto -60 IR PARA 40 -70 Beber o chá -``` - -### Mais uma linha, e os saltos ficam errados - -Agora melhoras o algoritmo: depois do açúcar, o saquinho de chá deve sair. Acrescentas a nova linha 40, e todas as linhas abaixo avançam um número. Mas os saltos continuam a apontar para os números antigos: - -``` -10 Pôr uma chávena na mesa -20 SE não quer açúcar, IR PARA 40 -30 Juntar açúcar -40 Tirar o saquinho de chá -50 SE chá não demasiado quente, IR PARA 70 -60 Esperar 1 minuto -70 IR PARA 40 -80 Beber o chá -``` - -Experimenta: a linha 70 já não te manda para a verificação da linha 50, mas para o saquinho. E quando o chá já está frio o suficiente, a linha 50 salta para a linha 70 em vez de *Beber o chá*. Ou seja, tiras o saquinho vezes sem conta e nunca bebes. A cada alteração tens de verificar todos os IR PARA. Num programa grande, com centenas de saltos, é fácil algo correr mal. - -### Para todo o lado: código esparguete - -Com setas também se pode saltar do meio de um ciclo para fora, ou de fora para o meio de um ciclo. Estes saltos tornam o fluxograma confuso muito depressa. Com muitos saltos, as setas cruzam-se até o fluxograma parecer um prato de esparguete. Por isso chama-se **código esparguete**. Mal se consegue seguir o fluxo, e os erros escondem-se bem. - -Um estrutograma não tem setas. Se acrescentares um bloco, tudo o que está abaixo desce com ele, e nenhum salto pode apontar para o sítio errado. Para sair de um ciclo mais cedo existe lá um bloco próprio e arrumado, a interrupção. Vais conhecê-la na lição 18. - -::: angeberwissen -Em 1968, o informático Edsger Dijkstra escreveu uma carta famosa: “Go To Statement Considered Harmful”, ou seja, “o GOTO é considerado prejudicial”. Desde então, os programas são construídos só com sequência, decisão e ciclo. Em Java, `goto` ainda hoje é uma palavra reservada, mas não a podes usar. Mééé, prefiro comer esparguete a programá-lo. -::: - -## Pseudocódigo e comparação - -**Pseudocódigo** é texto que parece um programa, mas está escrito em linguagem corrente. Palavras fixas como SE e ENQUANTO mostram os blocos, e a **indentação** mostra o que pertence a eles. Aqui também não há saltos. Este é o nosso exemplo do chá: - -``` -Pôr uma chávena na mesa -SE quer açúcar ENTÃO - Juntar açúcar -FIM SE -ENQUANTO chá ainda demasiado quente - Esperar 1 minuto -FIM ENQUANTO -Beber o chá -``` - -O pseudocódigo **não tem uma forma fixa**. Há quem escreva CASO em vez de SE, quem use uma seta para a atribuição (`numero ← 5`) ou quem não escreva as linhas FIM. Tudo isso é permitido. O importante é que qualquer pessoa o perceba sem explicação. Por isso, o pseudocódigo não é mais uma linguagem de programação que tens de aprender. - -::: angeberwissen -O pseudocódigo parece-te familiar? A linguagem de programação Python é muito parecida, e isso é de propósito: o seu criador, Guido van Rossum, queria que os programas se lessem quase como texto normal. O Python também usa a indentação para mostrar o que pertence junto. Mas atenção: o Python é uma linguagem de programação a sério, com regras fixas. Um espaço a mais e o computador reclama. O pseudocódigo é mais descontraído, tal como eu ao pequeno-almoço. -::: - -### Comparação - -- **Estrutograma:** blocos dentro de blocos, o ciclo é um L invertido. Vês a estrutura num relance, e saltos malucos são impossíveis. -- **Fluxograma:** símbolos com setas, o ciclo é uma seta que volta para cima. Muito fácil de imaginar, mas os fluxogramas grandes ficam depressa confusos, e são possíveis saltos para todo o lado. -- **Pseudocódigo:** texto com indentação, o ciclo fica entre ENQUANTO e FIM ENQUANTO. Rápido de escrever e já perto do código de um programa. - -> Os três descrevem o mesmo algoritmo. Quando sabes ler uma das formas, aprendes as outras depressa. - -::: angeberwissen -Nassi e Shneiderman estavam em guerra com as setas! O artigo deles de 1973 chamava-se “Flowchart Techniques for Structured Programming”. Com setas pode-se saltar para todo o lado até o fluxograma parecer um prato de esparguete. Num estrutograma isso é simplesmente impossível. Mééé, é assim que eu gosto: arrumado, como o meu estábulo. -::: - -## Jogo: Que representação? - -::: spiel frage -titel: Jogo: Que representação? -intro: O Ludwig descreve uma forma de representar um algoritmo sem dizer o nome. É o estrutograma, o fluxograma ou o pseudocódigo? Oito rondas. -antworten: estrutograma | fluxograma | pseudocódigo - -runde: -frage: “Quando algo se repete, uma das minhas setas aponta de volta para cima.” -richtig: fluxograma -> No fluxograma, o ciclo não tem símbolo próprio. Uma seta volta para cima. - -runde: -frage: “O meu ciclo parece um L invertido.” -richtig: estrutograma -> A caixa envolve os passos repetidos em cima e à esquerda. - -runde: -frage: “Sou feito de palavras como SE e ENQUANTO, e a indentação mostra o que pertence a quê.” -richtig: pseudocódigo -> O pseudocódigo é texto em linguagem normal, organizado como um programa. - -runde: -frage: “A minha decisão é um losango com as saídas sim e não.” -richtig: fluxograma -> No estrutograma, a condição fica num triângulo. - -runde: -frage: “As minhas setas podem enrolar-se para todo o lado como esparguete.” -richtig: fluxograma -> Como as setas podem ir para qualquer lado, no fluxograma é possível haver código esparguete. - -runde: -frage: “Não tenho uma forma fixa de escrever. Também podes escrever CASO em vez de SE.” -richtig: pseudocódigo -> O importante é que qualquer pessoa o perceba sem explicação. - -runde: -frage: “A condição fica em cima num triângulo, e por baixo divido-me em duas colunas.” -richtig: estrutograma -> É assim que a decisão aparece no estrutograma: true à esquerda, false à direita. - -runde: -frage: “Se inserires uma caixa, tudo o que está por baixo desce também. Nenhum salto pode falhar.” -richtig: estrutograma -> O estrutograma não tem setas nem saltos, por isso nada pode apontar para o sítio errado. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo nomear os símbolos de um fluxograma e ler um fluxograma simples. → Outras formas de desenhar: o fluxograma -- Consigo construir sozinho um algoritmo simples como fluxograma. → Exercício: Fazer um bolo como fluxograma -- Consigo explicar porque é que os saltos com GOTO levam a erros e a código esparguete. → Desvantagens do fluxograma: saltos -- Consigo ler um algoritmo simples em pseudocódigo. → Pseudocódigo e comparação -::: - -## Teste de conhecimentos - -::: quiz -? Como reconheces um ciclo num fluxograma? -→ Outras formas de desenhar: o fluxograma -- [ ] Por um L invertido -- [x] Por uma seta que volta para cima -- [ ] Pela palavra ENQUANTO -> O L invertido pertence ao estrutograma, e ENQUANTO ao pseudocódigo. - -? Associa cada símbolo do fluxograma ao seu significado. -→ Outras formas de desenhar: o fluxograma -= Oval -> Início ou fim -= Retângulo -> Instrução -= Losango -> Decisão -= Paralelogramo -> Entrada ou saída -> O losango tem duas saídas: sim e não. - -? Como se diz fluxograma em inglês? -→ Outras formas de desenhar: o fluxograma -- [x] Flowchart -- [ ] Structure chart -- [ ] Pseudocode -> Structure chart é o estrutograma. - -? Acrescentas uma nova linha a um programa com GOTO. O que pode acontecer? -→ Desvantagens do fluxograma: saltos -- [x] Os saltos apontam para as linhas erradas, porque os números mudaram -- [ ] Nada, os saltos ajustam-se sozinhos -- [ ] O programa fica automaticamente mais rápido -> Cada IR PARA aponta para um número de linha fixo. Se as linhas descem, o programa salta para o sítio errado. - -? No pseudocódigo, o que mostra que linhas pertencem a um ciclo? -→ Pseudocódigo e comparação -- [ ] A cor -- [x] A indentação e o FIM ENQUANTO -- [ ] Uma seta -> Tudo o que está entre ENQUANTO e FIM ENQUANTO está recuado e é repetido. -::: diff --git a/web/lessons/pt/02-der-editor.md b/web/lessons/pt/02-der-editor.md deleted file mode 100644 index c3106415..00000000 --- a/web/lessons/pt/02-der-editor.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -titel: O editor -bereich: Primeiros passos -kurz: Inserir elementos, escrever texto, mover e eliminar. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- inserir um elemento da área Elementos e substituir um marcador -- alterar o texto de um bloco e mudar entre Instrução, Entrada e Saída -- mover, copiar e eliminar blocos e anular passos -::: - -## O ecrã - -O VisuStruct tem dois editores: o [editor de estrutogramas](#zeige-editor), de que trata esta lição, e à direita a área [Código](#zeige-codelive) para código Java (a partir da lição 4). Se o editor de estrutogramas estiver recolhido, esta lição abre-o sozinha. As palavras azuis mostram-te as áreas, mesmo que estejam recolhidas: passa o rato por cima de uma, e a área abre-se ao lado da área de aprendizagem e acende-se. Se clicares nela (ou tocares nela), fica aberta. - -O editor de estrutogramas tem várias áreas. Passa o rato por cima de uma palavra azul ou toca nela, e essa área acende-se no editor; as áreas recolhidas, como os Elementos, abrem-se. - -- **Mesmo em cima, a [barra de ferramentas](#zeige-leiste):** Novo, Abrir e Guardar. À direita, a roda dentada das [Definições](#zeige-einstellungen): tamanho do texto e idioma. -- **Por baixo, as [ferramentas](#zeige-werkzeuge)** por cima dos elementos e do estrutograma: Anular, Cortar, Copiar, Colar, Eliminar, Exportar, Código e Simulação. Os botões só mostram ícones. Se puseres o rato por cima de um, aparece o nome. -- **À esquerda, os [Elementos](#zeige-elemente):** Aqui estão todos os blocos que podes arrastar para o estrutograma. Há quem chame a esta área a paleta. -- **Ao meio, o [estrutograma](#zeige-struktogramm):** Um diagrama novo contém apenas um marcador vazio, assinalado com **ø**. -- **Em baixo, o [Inspetor de objetos](#zeige-objektinspektor):** Aqui editas o texto do elemento selecionado. Se for pequeno ou grande demais, arrasta a linha com a pega por cima dele para cima ou para baixo. - -> Podes recolher esta área de aprendizagem a qualquer momento com o [ícone da barra lateral](#zeige-lernen) no topo desta área. Fica só a barra estreita “🎓 Aprender” à esquerda; um clique abre-a de novo. Ela fica aberta ao lado do editor, para poderes experimentar logo o que lês. Da mesma forma, há à esquerda a coluna [📁 Ficheiros](#zeige-dateien) para os teus programas guardados (lição 3) e à direita a área [Código](#zeige-codelive) para Java (a partir da lição 4). - -## Inserir um elemento - -Arrasta um elemento da área **Elementos** para o estrutograma. Enquanto arrastas, uma linha azul mostra onde ele vai ficar: - -- **Sobre o marcador ø:** O elemento substitui o marcador. -- **Sobre a metade de cima de um bloco:** O elemento fica antes dele. -- **Sobre a metade de baixo de um bloco:** O elemento fica depois dele. - -::: grafik l02-einfuegen.svg -O sítio onde largas decide onde fica o novo elemento. -::: - -Também funciona sem arrastar: **clica** num elemento da área Elementos (ou toca nele). Ele é então inserido depois do bloco selecionado. Se o marcador **ø** estiver selecionado, substitui-o. - -Com o teclado, usa a tecla **Tab** para saltar para um elemento e prime **Enter**. O novo elemento também fica depois do bloco selecionado. - -::: angeberwissen -Agarrar blocos e arrastá-los de um lado para o outro, tal como estás a fazer agora, tem um nome pomposo: **manipulação direta**. Foi Ben Shneiderman quem inventou o termo em 1982. Sim, o senhor dos estrutogramas. Aquele homem trabalhava mais do que eu quando estou a pastar. -::: - -## Escrever texto - -Clica num bloco. Ele fica com um contorno azul, e o seu texto aparece no Inspetor de objetos por baixo do diagrama. O que escreveres lá aparece logo no diagrama. - -Mais rápido é fazer **duplo clique** no bloco, ou **toque duplo** no tablet. Assim escreves o texto diretamente no estrutograma. **Enter** aceita, **Esc** cancela. Com o teclado, seleciona o bloco e carrega em **Enter**. - -O bloco da área Elementos chama-se **Instrução**. Em cima, no Inspetor de objetos, escolhes de que tipo deve ser: - -- **Instrução:** calcular ou definir alguma coisa, p. ex. `int numero = 5`. Vais aprender como na lição 4. -- **Entrada:** pedir um valor enquanto o estrutograma é executado. Isso vem na lição 6. -- **Saída:** mostrar alguma coisa. No diagrama fica com `output:` à frente, p. ex. `output: "Olá"`. - -Para começar, só precisas de Saída. Os textos que devem aparecer exatamente como estão ficam entre **aspas**. - -::: diagramm l2-ausgabe.visustruct -Duas saídas, uma a seguir à outra. Abre o diagrama no editor e inicia a [simulação](#zeige-simulation) nas ferramentas para veres a saída. Vais conhecê-la melhor na lição 7. -::: - -## Mover, copiar, eliminar - -- **Mover:** Arrasta um bloco existente com o rato para um novo sítio. -- **Copiar e colar:** Seleciona um bloco e prime Ctrl+C e Ctrl+V (no Mac ⌘C e ⌘V). A cópia fica depois do bloco selecionado. Sem teclado, por exemplo no tablet, toca em **Copiar** e **Colar** nas [ferramentas](#zeige-werkzeuge). -- **Eliminar:** Seleciona um bloco e prime Del, ou clica no [caixote do lixo](#zeige-loeschen) „Eliminar seleção“ nas ferramentas. -- **Anular:** Ctrl+Z anula o último passo, Ctrl+Shift+Z refá-lo (no Mac ⌘Z e ⌘⇧Z). Sem teclado, toca em [Anular](#zeige-rueckgaengig) nas ferramentas. - -## Exercício: Olá Mundo - -::: übung l2-hallo -titel: Olá Mundo -auftrag: Constrói um estrutograma que mostre **Olá Mundo**. -Arrasta uma **Instrução** dos Elementos para o marcador **ø** e clica no bloco. No Inspetor de objetos, por baixo do estrutograma, há três botões em cima: Instrução, Entrada e Saída. Clica em **Saída**. Depois escreve `"Olá Mundo"` no campo de texto por baixo, com aspas. -test: - -> Olá Mundo -braucht: anweisung -tipp: Só precisas de um único bloco. O texto que deve aparecer vai para o campo de texto do Inspetor de objetos. -tipp: Atenção às aspas. Sem elas, o VisuStruct procura uma variável chamada Olá. -lösung: l2-hallo-loesung.visustruct -::: - -Clica em **Verificar** quando terminares. O VisuStruct executa o teu estrutograma e compara a saída. - -## Exercício: Duas linhas - -::: übung l2-zwei-zeilen -titel: Duas linhas -auftrag: Mostra primeiro **Olá** e depois **Estou a aprender estrutogramas**, cada um na sua linha. -test: - -> Olá | Estou a aprender estrutogramas -tipp: Duas linhas significa duas saídas. Os blocos ficam uns por cima dos outros e são executados de cima para baixo. -tipp: Cada saída é um bloco próprio. Arrasta a segunda Instrução para a metade de baixo da primeira. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Jogo: Profissional do editor - -::: spiel frage -titel: Jogo: Profissional do editor -intro: O Ludwig testa se dominas o editor. Sete rondas. - -runde: -frage: Arrastas uma instrução para o marcador **ø**. O que acontece? -- [x] A instrução substitui o marcador. -- A instrução fica depois do marcador. -- Nada, não se pode largar nada sobre ø. -> O marcador só guarda o lugar. O novo elemento ocupa-o. - -runde: -frage: Arrastas um elemento para a metade **de cima** de um bloco. Onde fica? -- [x] antes dele -- depois dele -- Substitui o bloco. -> Metade de cima quer dizer antes, metade de baixo quer dizer depois. A linha azul mostra-te enquanto arrastas. - -runde: -frage: Arrastas um elemento para a metade **de baixo** de um bloco. Onde fica? -- antes dele -- [x] depois dele -- no fim do estrutograma -> Metade de baixo quer dizer: logo a seguir a este bloco. - -runde: -frage: Apagaste um bloco sem querer. O que o traz de volta? -- Ctrl+C / ⌘C -- [x] Ctrl+Z / ⌘Z -- Ctrl+V / ⌘V -> Ctrl+Z / ⌘Z anula o último passo. Ctrl+C / ⌘C copia, Ctrl+V / ⌘V cola. - -runde: -frage: Ao executar deve aparecer exatamente **Olá**. O que está no bloco? -- [x] output: "Olá" -- output: Olá -- "Olá" -> Um texto que deve aparecer tal e qual fica entre aspas. O editor põe `output:` à frente quando escolhes o tipo Saída. - -runde: -frage: Onde alteras o texto do bloco selecionado? -- na área Elementos -- [x] no Inspetor de objetos -- na barra de ferramentas lá em cima -> O Inspetor de objetos por baixo do diagrama mostra o texto do bloco selecionado. - -runde: -frage: Clicas num elemento da área Elementos, sem arrastar. Onde fica? -- [x] depois do bloco selecionado -- no início -- no fim -> Sem arrastar (clique, ou Tab e Enter), o novo elemento fica depois do bloco que está selecionado nesse momento. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo inserir um elemento da área Elementos e substituir um marcador. → Inserir um elemento -- Consigo alterar o texto de um bloco e mudar entre Instrução, Entrada e Saída. → Escrever texto -- Consigo mover, copiar e eliminar blocos e anular passos. → Mover, copiar, eliminar -::: - -## Teste de conhecimentos - -::: quiz -? O que significa o sinal ø num bloco? -→ Inserir um elemento -- [ ] O bloco contém um erro. -- [x] Aqui ainda há um marcador vazio. -- [ ] O programa termina aqui. -> Um marcador é substituído quando arrastas um elemento para cima dele. - -? Arrastas uma Instrução para a metade de baixo de um bloco. Onde é que ela fica? -→ Inserir um elemento -- [ ] Antes dele -- [x] Depois dele -- [ ] O bloco é substituído. - -? Como mostras o texto Bom dia exatamente como está? -→ Escrever texto -- [ ] output: Bom dia -- [x] output: "Bom dia" -- [ ] input: "Bom dia" -> Sem aspas, Bom dia é entendido como o nome de uma variável. -::: diff --git a/web/lessons/pt/03-speichern-oeffnen-exportieren.md b/web/lessons/pt/03-speichern-oeffnen-exportieren.md deleted file mode 100644 index c3fcd62c..00000000 --- a/web/lessons/pt/03-speichern-oeffnen-exportieren.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -titel: Guardar, abrir, exportar -bereich: Primeiros passos -kurz: Guardar os teus ficheiros em segurança, usar o teu espaço de trabalho, abrir vários diagramas e partilhá-los como imagem ou PDF. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dar um título a um diagrama -- guardar um diagrama como ficheiro .visustruct e voltar a abri-lo -- exportar um diagrama como imagem (PNG) ou PDF -- abrir um segundo diagrama num novo separador -- criar o meu espaço de trabalho `VisuStruct-Ficheiros` -- guardar um programa no meu espaço de trabalho -::: - -## O ficheiro .visustruct - -Os estrutogramas são guardados como ficheiros **.visustruct**. Todas as versões do VisuStruct usam o mesmo formato: no navegador, no Mac, iPad e iPhone, e a app Java no computador. Assim podes começar no navegador na escola e continuar na app em casa. - -::: angeberwissen -Quando Nassi era estudante, não havia ficheiros para guardar. Havia cartões perfurados. Nassi conta que um encontro ao sábado à noite era assim: a futura mulher dele sentava-se com ele no centro de computação e perfurava-lhe os cartões. Romance à moda da informática! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -Era assim um cartão perfurado. Cada cartão tinha uma linha de programa, aqui `PRINT *, 'OLA MUNDO'` na linguagem Fortran. Cada coluna é um carácter, e os furos são o seu código. Um programa inteiro era uma pilha destes cartões. Se caísse ao chão, era preciso voltar a ordenar tudo. -::: - -::: angeberwissen -Um pioneiro da informática da minha região na Alemanha: **Konrad Zuse**. A 12 de maio de 1941 apresentou em Berlim o **Z3**, o primeiro computador digital programável a funcionar no mundo. Já calculava no **sistema binário**, só com 0 e 1, com cerca de 2000 relés (interruptores elétricos). O programa não estava em cartões perfurados, mas sim em furos numa película de cinema usada. Depois, Zuse inventou o **Plankalkül**, uma das primeiras linguagens de programação. Mais tarde viveu em **Hünfeld**, perto de Fulda, até morrer em 1995. O Museu Konrad Zuse de lá mostra réplicas do Z3. A minha dica para uma visita de estudo! O horário está no site da cidade de Hünfeld. -::: - -::: grafik l03-zuse-lochstreifen.svg -Mais ou menos assim era a fita de programa de Zuse: uma película de cinema velha com furos. Cada coluna é uma instrução com 8 posições. Um furo significa 1, sem furo significa 0. A máquina lia a fita instrução a instrução. -::: - -## Guardar e abrir - -- **[Guardar](#zeige-speichern)** (Ctrl+S, no Mac ⌘S): O diagrama é guardado como ficheiro. Se a coluna **Ficheiros** estiver configurada (ver abaixo), fica lá. Caso contrário, o navegador pergunta onde o guardar ou põe o ficheiro na pasta de transferências. -- **[Abrir](#zeige-oeffnen)** (Ctrl+O, no Mac ⌘O): Voltar a carregar um ficheiro guardado. Também podes simplesmente arrastar um ficheiro para a janela do navegador. - -> O navegador memoriza os teus exercícios automaticamente. Mas isso só funciona neste navegador, neste dispositivo. Por isso, guarda sempre também os trabalhos importantes como ficheiro. - -## A coluna Ficheiros - -À esquerda da área de aprendizagem encontras a coluna [📁 Ficheiros](#zeige-dateien). Aí reúnes todos os teus programas num só sítio, o teu **espaço de trabalho**. - -**A tua vez:** Configura já o teu espaço de trabalho, antes de continuares a ler. A partir de hoje guardas lá todos os teus programas. - -1. Cria um espaço de trabalho: abre a coluna e clica em **Escolher pasta**. Na janela, vai aos teus **Documentos**, cria aí com **Nova pasta** uma pasta chamada `VisuStruct-Ficheiros` e seleciona-a. Permite que o VisuStruct use a pasta. Isto funciona no Chrome e no Edge. No iPad e noutros navegadores, clica antes em **Guardar no navegador**. -2. Guarda como de costume. No teu espaço de trabalho, o VisuStruct cria automaticamente uma pasta com a data de hoje, por exemplo `2026-10-23`, e guarda lá o programa. O nome do ficheiro vem do título. - -O teu espaço de trabalho fica assim: - -```text -VisuStruct-Ficheiros ← espaço de trabalho -└── 2026-10-23 ← dia - └── Fatorial.visustruct -``` - -Um clique num programa da coluna volta a abri-lo. Assim encontras depressa cada aula mais tarde. - -Também o teu **progresso** fica no espaço de trabalho: o ficheiro `learnProgress.json` guarda as lições e os exercícios que já concluíste. Se abrires o espaço de trabalho noutro navegador, o teu progresso volta a estar lá. - -> Porquê uma pasta real? O teu progresso e os teus programas ficam lá guardados como ficheiros. Podes levar a pasta contigo, por exemplo numa pen USB ou na tua pasta na nuvem, como o OneDrive. Assim continuas em casa, noutro computador, exatamente onde paraste. Se guardares só no navegador, tudo fica apenas neste navegador, neste dispositivo. Se alguém apagar os dados do navegador, o teu progresso também desaparece. - -## Vários diagramas em separadores - -Por cima do estrutograma vês os [separadores](#zeige-reiter). Com **+** ou [Novo](#zeige-neu) abres mais um diagrama vazio. Um ponto • antes do nome mostra que há alterações não guardadas. - -O [título](#zeige-titel) de um diagrama escreve-se no campo por cima do estrutograma. Também aparece quando exportas e é sugerido como nome do ficheiro. - -## Partilhar: imagem, PDF ou ficheiro - -Com [Exportar](#zeige-exportieren) nas ferramentas (ícone com uma seta para cima) podes partilhar o teu estrutograma: - -- **Imagem (PNG):** para pôr numa ficha de trabalho ou numa apresentação -- **Gráfico vetorial (SVG):** fica nítido em qualquer tamanho -- **PDF (A4):** uma página para entregar ou imprimir -- **Ficheiro VisuStruct (.visustruct):** para passar a alguém que deva abrir e continuar a editar o teu estrutograma no VisuStruct, por exemplo por e-mail ou pela plataforma de aprendizagem -- **Imprimir** (Ctrl+P, no Mac ⌘P): diretamente para a impressora - -Na [roda dentada](#zeige-textgroesse) em cima à direita, em Tamanho do texto, defines o tamanho da letra. O texto maior também se nota quando exportas uma imagem. - -## Experimenta - -Abre um dos teus exercícios da lição 2 e experimenta o seguinte: - -1. Dá um nome ao diagrama no campo [título](#zeige-titel) por cima do estrutograma, por exemplo `Olá Mundo`. -2. [Guarda-o](#zeige-speichern) no teu espaço de trabalho. Vê na coluna [📁 Ficheiros](#zeige-dateien): está na pasta de hoje? -3. Abre um segundo separador com **+** e clica lá no teu programa na coluna Ficheiros. Volta a abrir-se. -4. [Exporta-o](#zeige-exportieren) como **imagem (PNG)** e como **PDF (A4)**. Abre os dois ficheiros e compara-os. - -Correu tudo bem? Então sabes onde estão os teus programas e como os partilhar. - -## Jogo: Como o partilhas? - -::: spiel frage -titel: Jogo: Como o partilhas? -intro: O Ludwig quer partilhar o seu estrutograma. Que formato serve melhor? Seis rondas. -frage: Que formato serve? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: Amanhã queres continuar a trabalhar no teu estrutograma. -richtig: .visustruct -> Só o ficheiro .visustruct pode voltar a ser aberto e editado no VisuStruct. - -runde: -frage: O estrutograma deve entrar numa apresentação como imagem. -richtig: PNG -> Uma imagem PNG pode ser posta em qualquer apresentação e em qualquer ficha de trabalho. - -runde: -frage: O teu professor quer imprimir o teu trabalho numa página A4. -richtig: PDF -> O PDF é exatamente uma página A4 para entregar ou imprimir. - -runde: -frage: O estrutograma vai para um cartaz e deve ficar nítido mesmo em tamanho enorme. -richtig: SVG -> Um gráfico vetorial fica nítido em qualquer tamanho. Um PNG fica com pixéis quando o aumentas. - -runde: -frage: A tua amiga deve alterar o estrutograma na app VisuStruct do iPad dela. -richtig: .visustruct -> Todas as versões do VisuStruct abrem o mesmo ficheiro .visustruct: no browser, no Mac, no iPad e no iPhone. - -runde: -frage: Queres enviar depressa uma imagem para um chat. -richtig: PNG -> Um PNG pode ser visto logo por qualquer pessoa, sem VisuStruct. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dar um título a um diagrama. → Experimenta -- Consigo guardar um diagrama como ficheiro .visustruct e voltar a abri-lo. → Guardar e abrir -- Consigo exportar um diagrama como imagem (PNG) ou PDF. → Partilhar: imagem, PDF ou ficheiro -- Consigo abrir um segundo diagrama num novo separador. → Vários diagramas em separadores -- Consigo criar o meu espaço de trabalho `VisuStruct-Ficheiros`. → A coluna Ficheiros -- Consigo guardar um programa no meu espaço de trabalho. → A coluna Ficheiros -::: - -## Teste de conhecimentos - -::: quiz -? Que ficheiro podes voltar a abrir na app para Mac e na app Java? -→ Guardar e abrir -- [x] O ficheiro .visustruct -- [ ] A imagem PNG exportada -- [ ] O PDF exportado -> As imagens e os PDF servem só para ver. Só o ficheiro .visustruct pode continuar a ser editado. - -? O navegador memoriza os teus exercícios. Porque deves guardar mesmo assim? -→ Guardar e abrir -- [ ] Porque senão os exercícios não são verificados -- [x] Porque o navegador só guarda os dados neste dispositivo e eles podem perder-se -- [ ] Porque não se pode exportar sem guardar - -? Hoje guardas um programa no teu espaço de trabalho. Onde fica? -→ A coluna Ficheiros -- [ ] Na pasta de transferências -- [x] No espaço de trabalho, dentro de uma pasta com a data de hoje -- [ ] Diretamente no espaço de trabalho, sem subpasta -> O VisuStruct cria automaticamente uma pasta com a data de cada dia. Assim os programas ficam ordenados por aula. -::: diff --git a/web/lessons/pt/04-variablen-und-datentypen.md b/web/lessons/pt/04-variablen-und-datentypen.md deleted file mode 100644 index 209f097d..00000000 --- a/web/lessons/pt/04-variablen-und-datentypen.md +++ /dev/null @@ -1,407 +0,0 @@ ---- -titel: Variáveis e tipos de dados -bereich: Fundamentos -kurz: Guardar valores, conhecer os tipos de dados int, double, String e boolean, e fazer contas. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar uma variável com tipo de dados, nome e valor -- explicar que o Java distingue maiúsculas de minúsculas (`numero` não é `Numero`) -- distinguir os tipos de dados int, double, String e boolean -- explicar que = é uma atribuição e não uma comparação -- fazer contas com + - * / % e usar a divisão inteira -- criar e mostrar variáveis em Java -::: - -## O que é uma variável? - -Uma **variável** é como uma caixa com etiqueta. Tem um **nome**, um **tipo de dados** e um **valor**. - -``` -int idade = 14 -``` - -Aqui a variável chama-se `idade`, o seu tipo de dados é `int` (número inteiro) e o seu valor é 14. Mais tarde podes voltar a usar o valor através do nome. - -::: grafik l04-variable.svg -A variável como caixa: o nome na etiqueta, o tipo de dados como autocolante, o valor lá dentro. -::: - -> Os nomes de variáveis começam por uma **letra minúscula** e não têm espaços. Bons nomes dizem o que está lá dentro: `preco` é melhor do que `p`. Evita acentos e cedilhas nos nomes: escreve `preco` e não `preço`. - -::: angeberwissen -Em setembro de 1999 perdeu-se a sonda Mars Climate Orbiter. Uma equipa calculava a força de impulso em unidades americanas (libras), a outra esperava unidades métricas (newtons). Nos dados só estava o número, não a unidade. A sonda voou demasiado baixo na atmosfera de Marte e ardeu, um prejuízo de cerca de 125 milhões de dólares. Um nome que diz o que guarda, como `impulsoEmNewtonSegundos`, teria evitado a confusão. Já agora, eu escrevo sempre `fenoEmQuilos` na minha caixa de feno. -::: - -Se um nome tiver várias palavras, escreves-as juntas e começas cada palavra seguinte com maiúscula: `precoComIva`, `numeroDeAlunos`. Esta forma de escrever chama-se **camelCase**. - -::: angeberwissen -O camelCase tem este nome porque as maiúsculas no meio saem da palavra como as bossas de um camelo: `precoComIva` tem duas bossas. Eu, como bode, tenho zero bossas, mas dois chifres. Infelizmente ainda ninguém inventou a escrita BodeCase. -::: - -::: grafik l04-camelcase.svg -As maiúsculas C e I sobressaem como as duas bossas do camelo. -::: - -## Maiúsculas e minúsculas - -O Java distingue **maiúsculas de minúsculas**. Em inglês diz-se **case-sensitive**. Para o Java, `numero`, `Numero` e `NUMERO` são três nomes diferentes, ou seja, três caixas diferentes: - -``` -int numero = 4 -int Numero = 6 -``` - -Aqui há duas variáveis. Se mais tarde escreveres `NUMERO`, o Java não encontra nenhuma variável com esse nome. O programa não corre e aparece uma mensagem de erro. - -> Isto também vale para as palavras do Java: `String` e `System` escrevem-se com maiúscula, `int` e `double` com minúscula. Nos teus programas, nunca cries duas variáveis que só se distinguem por maiúsculas e minúsculas. Isso confunde-te a ti e a quem lê o teu código. - -## Exercício: Três nomes - -::: übung l04-gross-klein -titel: Três nomes -auftrag: O Ludwig criou duas variáveis: `numero` com o valor 4 e `Numero` com o valor 6. O estrutograma deve multiplicá-las e mostrar **Resultado: 24**. Mas não corre. - Carrega em “Abrir no editor”, depois em Verificar, e lê a mensagem de erro. Corrige a instrução e carrega outra vez em Verificar. -start: l04-gross-klein-start.visustruct -test: - -> Resultado: 24 -braucht: anweisung -tipp: Quantos nomes diferentes há no estrutograma? `numero`, `Numero` e `NUMERO` são três nomes diferentes para o computador. -tipp: `NUMERO` não existe. Para 24 precisas de 4 vezes 6, ou seja, `numero` vezes `Numero`: `int resultado = numero * Numero`. Com `numero * numero` dá 16. -lösung: l04-gross-klein-loesung.visustruct -::: - -## Os quatro tipos de dados - -O tipo de dados diz que tipo de valor pode ser guardado na variável. - -- **int:** números inteiros, p. ex. `int quantidade = 3` ou `int temperatura = -5` -- **double:** números decimais, p. ex. `double preco = 2.5`. Escreves um **ponto**, nunca uma vírgula. -- **String:** texto, p. ex. `String nome = "Inês"`. O texto fica entre aspas. -- **boolean:** valores lógicos, só `true` ou `false` - -Há mais tipos de dados. Um deles vais precisar em breve: `char` para um **único caractere**, p. ex. `char nota = 'C'`. Um `char` fica entre aspas **simples**. Vais conhecê-lo melhor na lição 17. - -::: diagramm l05-datentypen.visustruct -Três variáveis com tipos de dados diferentes. Abre o diagrama e inicia a [simulação](#zeige-simulation). -::: - -Os valores lógicos aparecem sobretudo em **condições**: `idade >= 18` é `true` ou `false`. É disso que trata a lição 8. Uma variável do tipo `boolean` recebe simplesmente `true` ou `false`: - -``` -boolean adulto = false -``` - -::: angeberwissen -A 4 de junho de 1996, o foguetão Ariane 5 explodiu apenas 37 segundos depois da descolagem. A culpa foi de uma única linha: um número decimal para a velocidade foi metido numa variável inteira demasiado pequena para valores tão grandes. O tipo de dados não é um pormenor, pode custar meio milhar de milhões de euros. -::: - -## Atribuição com = - -O sinal `=` chama-se **atribuição**. Não quer dizer “é igual a” como na matemática. Quer dizer: calcula o lado direito e guarda o resultado na variável da esquerda. - -``` -int pontos = 10 -pontos = pontos + 5 -``` - -Na matemática, a segunda linha estaria errada, porque nenhum número é igual a si próprio mais 5. Na programação lê-se assim: pega no valor antigo 10, soma 5 e guarda 15 em `pontos`. O tipo de dados só se escreve da primeira vez, quando a variável é criada. - -::: grafik l04-zuweisung.svg -Primeiro calcula-se o lado direito, depois o resultado vai para a variável da esquerda. -::: - -## Fazer contas - -Podes fazer contas com números: - -- `+` mais, `-` menos, `*` vezes, `/` a dividir por -- `%` resto de uma divisão (diz-se “módulo”), p. ex. `17 % 5` dá 2 - -A **divisão com resto** conheces da escola primária, quando ainda não sabias números decimais: 17 : 5 = 3 e sobram 2. O 5 cabe três vezes no 17, o que dá 15. Até 17 faltam ainda 2: esse é o resto. O computador calcula com números inteiros exatamente assim: `/` dá o resultado sem o resto, `%` dá só o resto. - -| Escola primária | com `/` | com `%` | -|---|---|---| -| 17 : 5 = 3, resto 2 | `17 / 5` dá 3 | `17 % 5` dá 2 | -| 20 : 5 = 4, resto 0 | `20 / 5` dá 4 | `20 % 5` dá 0 | -| 3 : 5 = 0, resto 3 | `3 / 5` dá 0 | `3 % 5` dá 3 | - -Se o resto for 0, a divisão é exata. Mais tarde usas isto para ver se um número é par: nos números pares, `numero % 2` dá 0. - -**A multiplicação e a divisão vêm antes da adição e da subtração**, e os parênteses calculam-se primeiro: `2 * (3 + 4)` dá 14. - -### Mostrar texto e resultado juntos - -Um resultado sozinho diz pouco. Com `+` juntas um texto e o valor de uma variável: - -``` -int idade = 14 -output: "Idade: " + idade -``` - -Isto mostra **Idade: 14**. O texto entre aspas aparece tal e qual; em vez de `idade` o computador põe o valor. O espaço depois dos dois pontos faz parte do texto, senão aparece `Idade:14`. - -## Exercício: Retângulo - -::: übung l05-rechteck -titel: Calcular um retângulo -auftrag: Um retângulo tem 7 de comprimento e 4 de largura. Cria as variáveis `comprimento` e `largura`. Calcula a área e o perímetro em variáveis próprias e mostra os dois assim: -`Área: 28` e na linha seguinte `Perímetro: 22`. -test: - -> Área: 28 | Perímetro: 22 -braucht: anweisung -tipp: Pensa primeiro de que variáveis precisas: duas para as medidas e duas para os resultados. São todas números inteiros. -tipp: A área é o comprimento vezes a largura, o perímetro é 2 vezes (comprimento mais largura). Para a saída escreves p. ex. `output: "Área: " + area`. O `+` junta o texto e o valor da variável. -lösung: l05-rechteck-loesung.visustruct -::: - -## Divisão inteira - -Se dividires dois números inteiros e guardares o resultado numa variável `int`, tudo o que vem depois da vírgula desaparece. **Não é arredondado.** - -``` -int x = 7 / 2 dá 3 -int r = 7 % 2 dá 1 (o resto) -double d = 7.0 / 2 dá 3.5 -``` - -Com `/` e `%` juntos podes fazer boas conversões, por exemplo de minutos para horas e minutos: - -::: diagramm l05-division.visustruct -135 minutos são 2 horas (135 / 60) e sobram 15 minutos (135 % 60). -::: - -## Exercício: Converter segundos - -::: übung l05-sekunden -titel: Converter segundos -auftrag: 200 segundos devem ser mostrados em minutos e segundos. A primeira e a última instrução já lá estão. Insere entre elas instruções que calculem `minutos` e `resto`. -start: l05-sekunden-start.visustruct -test: - -> 3 minutos e 20 segundos -braucht: anweisung -tipp: Quantos minutos inteiros cabem em 200 segundos, e quantos segundos sobram? Calcula primeiro de cabeça. -tipp: Um minuto tem 60 segundos. Usa `/` para os minutos inteiros e `%` para o resto. As duas variáveis são do tipo `int`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Exercício: Compras - -::: übung l05-einkauf -titel: Compras -auftrag: Compras 3 cadernos a 2.5 euros cada. Cria `quantidade` como `int` e `preco` como `double`, calcula o preço total numa variável `precoTotal` e mostra `Total: 7.5 euros`. -test: - -> Total: 7.5 euros -braucht: anweisung -tipp: A quantidade vezes o preço dá o preço total. Para cada variável, pensa se é um número inteiro ou um número decimal. -tipp: O preço total é um número decimal, por isso `precoTotal` precisa do tipo `double`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Agora em Java - -Um estrutograma é um plano. Em **Java** torna-se um programa que o computador executa. As tuas compras do exercício ficam assim: - -```java editor -int quantidade = 3; -double preco = 2.5; -double precoTotal = quantidade * preco; -System.out.println("Total: " + precoTotal + " euros"); -``` - -- Primeiro o tipo de dados, depois o nome, depois o valor, tal como no bloco. -- Cada instrução termina com um **ponto e vírgula** `;`. -- Em Java, uma saída escreve-se `System.out.println(…)`. - -Como é que o computador entende o teu código Java e porque é que ele corre igual no Windows, no macOS e no Linux, vais ver na lição “Estrutograma e código”. - -::: angeberwissen -O Java chamava-se primeiro **Oak** (carvalho), por causa da árvore em frente à janela do escritório de James Gosling. A partir de 1991, a sua equipa na Sun Microsystems queria programar pequenos aparelhos, como boxes de televisão. Mas o nome Oak já pertencia a outra empresa. Assim, em 1995 passou a chamar-se **Java**, como o café da ilha de Java. -::: - -## Texto em Java - -As variáveis não guardam só números. Para texto existe o tipo de dados `String`: - -```java editor -String nome = "Inês"; -int idade = 14; -double altura = 1.62; -System.out.println(nome + " tem " + idade + " anos."); -System.out.println("Altura: " + altura + " m"); -``` - -- O texto fica entre aspas. -- `String` escreve-se com maiúscula, `int`, `double` e `boolean` com minúscula. O Java distingue-os. -- Com `+` juntas texto e números. - -Escreves Java na área [Código](#zeige-codelive). O estrutograma constrói-se sozinho enquanto escreves. “Abrir no editor” leva o exemplo para a área Código. Clica aí em “Executar”, depois muda o nome, a idade ou a altura e experimenta outra vez. - -::: übung l04-java-steckbrief -titel: Ficha em Java -bereich: beide -code: java -auftrag: Escreve a ficha do Ludwig, o bode. Cria três variáveis: o nome **Ludwig**, a idade **7** e a altura **1.2**. Depois mostra estas duas linhas: - **Ludwig tem 7 anos.** e **Altura: 1.2 m** - “Abrir no editor” abre também a área Código à direita. Escreve lá o teu código; o estrutograma constrói-se sozinho. Depois clica em Verificar. -test: - -> Ludwig tem 7 anos. | Altura: 1.2 m -braucht: anweisung -tipp: Precisas de uma `String`, um `int` e um `double`. Cada linha termina com ponto e vírgula. -tipp: `String nome = "Ludwig";`, `int idade = 7;` e `double altura = 1.2;`. Juntas a saída com `+`: `System.out.println(nome + " tem " + idade + " anos.");` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Preenche os espaços: tipos de dados - -::: lückentext -titel: Ficha em Java -auftrag: Preenche as palavras que faltam. Por fora está a moldura de que qualquer programa Java precisa num ficheiro próprio. As variáveis ficam lá dentro. -```java -[[public]] class Ficha { - public static [[void]] main(String[] args) { - [[int]] idade = 14; - [[double]] altura = 1.62; - [[String]] nome = "Mia"; - [[boolean]] temAnimal = true; - System.out.[[println]](nome + " tem " + idade + " anos."); - } -} -``` -erklärung: Números inteiros são `int`, números decimais `double`, textos `String` (com S maiúsculo) e valores lógicos `boolean`. `public class` e `public static void main` formam a moldura. Na área Código podes deixá-la de fora, há mais sobre isto na lição “Estrutograma e código”. -::: - -## Encontra o erro: segundos - -::: übung l04-java-fehler -titel: Corrigir os segundos -code: java -auftrag: O programa deve converter 200 segundos em minutos e segundos e mostrar **3 min 20 s**. Mas mostra **20 min 3 s**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: `/` divide sem resto, `%` dá o resto. Qual dos dois precisas para os minutos e qual para os segundos que sobram? -tipp: Os dois operadores estão trocados: `int minutos = segundos / 60;` e `int resto = segundos % 60;` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l04-java-laeuft-nicht -titel: Corrigir a ficha -code: java -auftrag: O programa deve mostrar **Ludwig tem 7 anos.** Mas não corre, porque há duas palavras mal escritas. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig tem 7 anos. -braucht: anweisung -tipp: O Java distingue maiúsculas de minúsculas. Compara cada palavra da saída com o que foi criado em cima. -tipp: `System` escreve-se com S maiúsculo. A variável chama-se `idade` com i minúsculo: `System.out.println(nome + " tem " + idade + " anos.");` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que tipo de dados? - -::: spiel frage -titel: Jogo: Que tipo de dados? -intro: O Ludwig juntou valores. Que tipo de dados serve para cada valor? Oito rondas. -antworten: int | double | String | boolean - -runde: -frage: Que tipo de dados serve para `17`? -richtig: int -> Um número inteiro sem ponto decimal é um `int`. - -runde: -frage: Que tipo de dados serve para `3.5`? -richtig: double -> Números com ponto decimal são `double`. - -runde: -frage: Que tipo de dados serve para `"Olá"`? -richtig: String -> Texto entre aspas é uma `String`. - -runde: -frage: Que tipo de dados serve para `true`? -richtig: boolean -> `true` e `false` são os dois valores lógicos, tipo de dados `boolean`. - -runde: -frage: Que tipo de dados serve para `"17"`? -richtig: String -> Atenção às aspas! Aqui 17 é um texto com o qual não se pode calcular. - -runde: -frage: Que tipo de dados serve para `-4`? -richtig: int -> Os números inteiros negativos também são `int`. - -runde: -frage: Que tipo de dados serve para `0.0`? -richtig: double -> O ponto torna-o um número decimal, mesmo que a seguir só esteja um 0. - -runde: -frage: Que tipo de dados serve para `"false"`? -richtig: String -> Entre aspas, false também é só um texto. O valor lógico não tem aspas. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar uma variável com tipo de dados, nome e valor. → O que é uma variável? -- Consigo explicar que o Java distingue maiúsculas de minúsculas (`numero` não é `Numero`). → Maiúsculas e minúsculas -- Consigo distinguir os tipos de dados int, double, String e boolean. → Os quatro tipos de dados -- Consigo explicar que = é uma atribuição e não uma comparação. → Atribuição com = -- Consigo fazer contas com + - * / % e usar a divisão inteira. → Fazer contas -- Consigo criar e mostrar variáveis em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que tipo de dados serve para o valor 3.75? -→ Os quatro tipos de dados -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Os números decimais são do tipo double. No VisuStruct escrevem-se com ponto. - -? Quantas variáveis criam `int numero = 1`, `int Numero = 2` e `int NUMERO = 3`? -→ Maiúsculas e minúsculas -- [ ] uma -- [ ] duas -- [x] três -> O Java distingue maiúsculas de minúsculas. Os três nomes são diferentes, por isso há três variáveis. - -? Que valor tem x depois de `int x = 17 / 5`? -→ Fazer contas -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> Com números inteiros, tudo o que vem depois da vírgula desaparece. Não é arredondado. - -? Quanto dá `17 % 5`? -→ Fazer contas -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 a dividir por 5 dá 3, resto 2. O operador % dá o resto. - -? No início, `int pontos = 10`. O que fica em pontos depois de `pontos = pontos * 2`? -→ Atribuição com = -- [ ] 10 -- [ ] 12 -- [x] 20 -> O lado direito é calculado com o valor antigo 10, e o resultado 20 é guardado em pontos. - -? Que linha cria corretamente um número decimal em Java? -→ Agora em Java -- [ ] double preco = 2,5; -- [x] double preco = 2.5; -- [ ] Double preco = 2.5 -> Os números decimais escrevem-se com ponto, e no fim há um ponto e vírgula. -::: diff --git a/web/lessons/pt/05-sequenz.md b/web/lessons/pt/05-sequenz.md deleted file mode 100644 index 486f7bcc..00000000 --- a/web/lessons/pt/05-sequenz.md +++ /dev/null @@ -1,253 +0,0 @@ ---- -titel: Sequência -bereich: Fundamentos -kurz: Executar passos um a seguir ao outro, prestar atenção à ordem e trocar valores. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que uma sequência é executada de cima para baixo -- explicar porque é que a ordem dos passos conta -- trocar dois valores com uma variável auxiliar -- escrever uma sequência em Java -::: - -## Passo a passo - -Uma **sequência** é uma série de passos que são executados exatamente uma vez e exatamente por esta ordem. No estrutograma, os blocos ficam simplesmente uns por cima dos outros. - -Cada bloco só começa quando o bloco de cima terminou. Nada é saltado e nada é repetido. - -::: diagramm l05-sequenz.visustruct -Uma sequência de quatro blocos: primeiro `comprimento` e `largura` recebem os seus valores, depois a área é calculada e, no fim, é apresentada. Cada bloco é executado exatamente uma vez, de cima para baixo. Saída: 15. -::: - -> A sequência é o mais simples dos três blocos básicos. Quase todos os estrutogramas são feitos sobretudo de sequências. - -## A ordem conta - -Quando fazes chá, notas logo se a ordem estiver errada. Com contas é igual, só que é mais difícil de ver. - -::: diagramm l04-rechnen.visustruct -Primeiro duplicar, depois somar 1: o resultado é 7. Se trocares os dois blocos do meio, dá 8. -::: - -A instrução `numero = numero * 2` quer dizer: pega no valor de `numero`, multiplica-o por 2 e volta a guardar o resultado em `numero`. Por isso, o que está em `numero` depende do que aconteceu antes. - -::: angeberwissen -Os estrutogramas não são só coisa da escola. Segundo Shneiderman, empresas como a IBM e a Siemens também planeavam os seus programas com eles. Ou seja, aqui estás a aprender coisas de profissionais. Mééé-ravilha! -::: - -## Exercício: Pôr por ordem - -::: übung l04-reihenfolge -titel: Pôr por ordem -auftrag: O diagrama inicial tem os blocos certos, mas pela ordem errada. Move-os de modo que no fim seja mostrado **20**. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Lê cada bloco e pergunta-te: de que variável precisa, e onde é que essa variável recebe o seu valor? -tipp: Uma variável tem de ser criada antes de fazeres contas com ela. A saída vem mesmo no fim. Confirma de cabeça: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Trocar dois valores - -Imagina dois copos: um com sumo, outro com água. Se quiseres trocar o que está lá dentro, precisas de um terceiro copo, vazio. - -Com variáveis é exatamente igual. Se escreveres simplesmente `a = b` e depois `b = a`, o valor antigo de `a` já se perdeu. No fim, as duas variáveis têm o mesmo valor. - -A solução é uma **variável auxiliar** que guarda o valor antigo durante pouco tempo: - -::: grafik l05-tauschen.svg -Três passos com um copo vazio: primeiro o sumo para aux, depois a água para a esquerda, depois o sumo de aux para a direita. -::: - -::: diagramm l04-tauschen.visustruct -Primeiro guarda-se a em aux. Depois a recebe o valor de b, e b recebe o valor guardado em aux. -::: - -## Exercício: Trocar copos - -::: übung l04-glaeser-tauschen -titel: Trocar copos -auftrag: O copo da esquerda tem sumo, o da direita tem água. Troca o conteúdo das variáveis `esquerda` e `direita` antes de serem mostradas. -Não alteres os dois primeiros blocos. Insere novas instruções entre eles. -start: l04-tauschen-start.visustruct -test: - -> Esquerda: água | Direita: sumo -braucht: anweisung -tipp: Como trocas dois copos cheios na vida real? Precisas de um terceiro copo, vazio. -tipp: Cria uma variável auxiliar com `String aux = esquerda`. Depois disso precisas de mais duas atribuições. -lösung: l04-tauschen-loesung.visustruct -::: - -## Agora em Java - -Tal como na lição 4, também podes escrever uma sequência diretamente em Java. A troca de cima fica assim: - -```java editor -int a = 5; -int b = 9; -int aux = a; -a = b; -b = aux; -System.out.println("a = " + a); -System.out.println("b = " + b); -``` - -- Cada bloco passa a ser uma linha. As linhas correm de cima para baixo, tal como os blocos. -- No fim de cada instrução há um **ponto e vírgula** `;`. -- Uma saída chama-se `System.out.println(…)` em Java. Atenção ao S maiúsculo de `System`: o Java distingue maiúsculas de minúsculas. - -Escreves Java na área [Código](#zeige-codelive), à direita do estrutograma. Enquanto escreves, o VisuStruct constrói o estrutograma ao mesmo tempo. Assim vês logo que bloco pertence a que linha. - -::: übung l05-java-kino -titel: Bilhetes de cinema em Java -bereich: beide -code: java -auftrag: Um bilhete de cinema custa 9 euros. Escreve um programa em Java que calcula o preço de 3 bilhetes e mostra **Total: 27**. - “Abrir no editor” abre também a área Código à direita. Escreve lá o teu código; o estrutograma constrói-se sozinho. Depois clica em Verificar. -test: - -> Total: 27 -braucht: anweisung -tipp: Precisas de três variáveis: o preço, a quantidade e o resultado. Cada linha termina com ponto e vírgula. -tipp: `int preco = 9;` e `int quantidade = 3;` criam as variáveis. `int total = preco * quantidade;` calcula, e `System.out.println("Total: " + total);` mostra o resultado. -lösung: l05-java-kino-loesung.visustruct -::: - -## Preenche os espaços: trocar os copos - -::: lückentext -titel: Trocar dois valores -auftrag: Os valores de `a` e `b` devem ser trocados. Preenche as palavras que faltam. -```java -int a = 3; -int b = 8; -[[int]] aux = [[a]]; -a = [[b]]; -b = [[aux]]; -System.out.println("a = " + a + ", b = " + b); -``` -erklärung: Primeiro guardas `a` na variável auxiliar, depois `a` recebe o valor de `b` e, por fim, `b` recebe o valor guardado. Saída: **a = 8, b = 3** -::: - -## Encontra o erro: saldo - -::: übung l05-java-fehler -titel: Corrigir o saldo -code: java -auftrag: O cartão da cantina tem 20 euros. Uma refeição custa 7 euros. O programa deve mostrar o saldo **depois** de pagar, ou seja, **Resta: 13**. Mas mostra **Resta: 20**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l05-java-fehler-start.visustruct -test: - -> Resta: 13 -braucht: anweisung -tipp: Todas as linhas estão certas, só a ordem não. Quando é mostrado o saldo e quando se paga? -tipp: Troca as duas últimas linhas: primeiro `saldo = saldo - preco;`, depois a saída. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l05-java-laeuft-nicht -titel: Dividir uma piza -code: java -auftrag: Uma piza tem 8 fatias e 4 amigos dividem-na. O programa deve mostrar **Cada um recebe 2 fatias.** Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Cada um recebe 2 fatias. -braucht: anweisung -tipp: Compara o nome na saída, letra a letra, com o nome criado na terceira linha. -tipp: A variável chama-se `porPessoa` com P maiúsculo (camelCase). Escreve-a exatamente assim na saída. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Ordenar os blocos - -::: spiel reihenfolge -titel: Jogo: Ordenar os blocos -intro: O Ludwig baralhou os blocos. Põe-nos pela ordem certa. Quatro rondas. - -runde: -frage: No fim deve aparecer **7**. -- `int numero = 3` -- `numero = numero * 2` -- `numero = numero + 1` -- `output: numero` -> Primeiro duplicar, depois somar 1: 3 · 2 + 1 = 7. Ao contrário dava (3 + 1) · 2 = 8. - -runde: -frage: No fim deve aparecer **8**. -- `int numero = 3` -- `numero = numero + 1` -- `numero = numero * 2` -- `output: numero` -> Os mesmos blocos, outra ordem, outro resultado: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a e b devem trocar os seus valores. -- `int aux = a` -- `a = b` -- `b = aux` -- `output: a + " " + b` -> Primeiro guarda-se a em aux. Senão o valor antigo perde-se assim que a recebe o valor de b. - -runde: -frage: O Ludwig faz chá. -- Encher a chaleira com água -- Ligar a chaleira -- Esperar até a água ferver -- Deitar a água sobre o saquinho de chá -- Deixar o chá em infusão -> Cada passo precisa do anterior. Tal como num estrutograma. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que uma sequência é executada de cima para baixo. → Passo a passo -- Consigo explicar porque é que a ordem dos passos conta. → A ordem conta -- Consigo trocar dois valores com uma variável auxiliar. → Trocar dois valores -- Consigo escrever uma sequência em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas vezes é executado cada bloco de uma sequência? -→ Passo a passo -- [ ] Até uma condição ser falsa -- [x] Exatamente uma vez -- [ ] Nenhuma, se estiver vazio -> Numa sequência nada é repetido e nada é saltado. Para repetir existem os ciclos. - -? No início a = 5 e b = 9. Depois é executado `a = b` e a seguir `b = a`. O que está nas variáveis no fim? -→ Trocar dois valores -- [ ] a = 9 e b = 5 -- [x] a = 9 e b = 9 -- [ ] a = 5 e b = 5 -> Depois de `a = b`, o valor antigo 5 perdeu-se. `b = a` só copia o 9 de volta. - -? Porque precisas de uma variável auxiliar para trocar valores? -→ Trocar dois valores -- [x] Para um valor não ser escrito por cima antes de ser preciso -- [ ] Para o estrutograma ficar mais comprido -- [ ] Porque senão não se podem mostrar variáveis - -? O que mostra a sequência `int x = 4`, `x = x + 1`, `x = x * 3`, `output: x`? -→ A ordem conta -- [ ] 13 -- [x] 15 -- [ ] 4 -> Primeiro x passa a 5, depois a 15. - -? Com que termina cada instrução em Java? -→ Agora em Java -- [ ] Com um ponto . -- [x] Com um ponto e vírgula ; -- [ ] Com dois pontos : -> Se faltar o ponto e vírgula, o VisuStruct mostra uma indicação por baixo do código. -::: diff --git a/web/lessons/pt/06-eingabe-und-ausgabe.md b/web/lessons/pt/06-eingabe-und-ausgabe.md deleted file mode 100644 index ec59d96c..00000000 --- a/web/lessons/pt/06-eingabe-und-ausgabe.md +++ /dev/null @@ -1,432 +0,0 @@ ---- -titel: Entrada e saída -bereich: Fundamentos -kurz: Pedir valores, mostrar resultados e usar o modelo EPS. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o modelo EPS -- escrever uma entrada com tipo de dados, nome e pergunta -- juntar texto e variáveis numa saída com + -- calcular primeiro um resultado e guardá-lo numa variável, depois mostrar essa variável, p. ex. `int soma = a + b`, depois `output: soma` -- identificar a entrada, o processamento e a saída numa tarefa -- ler números em Java com o scanner -- escrever comentários com `//` e `/* */` e usá-los para organizar e explicar o meu código -::: - -## O modelo EPS - -Muitos programas funcionam em três partes: - -1. **Entrada:** O programa pede valores, p. ex. dois números. -2. **Processamento:** Faz contas com os valores. -3. **Saída:** Mostra o resultado. - -As primeiras letras dão **EPS**. Até agora, os valores estavam fixos no estrutograma. Com uma entrada, pode ser usado um valor diferente de cada vez que o estrutograma é executado. - -::: diagramm l06-eva.visustruct -Entrada em cima, processamento ao meio, saída em baixo. Inicia a simulação e experimenta números diferentes. -::: - -## Entrada com input: - -Uma entrada pede um valor enquanto o estrutograma é executado e guarda-o numa variável. Escreves o **tipo de dados**, o **nome** e uma **pergunta** entre aspas: - -``` -input: int idade "Quantos anos tens?" -input: double preco "Preço em euros?" -input: String nome "Como te chamas?" -``` - -No editor escolhes o tipo **Entrada** em cima, numa Instrução. A palavra `input:` já fica à frente, e só escreves `int idade "Quantos anos tens?"`. - -> O tipo de dados decide o que pode ser introduzido. Com `int`, a simulação dá erro se alguém escrever 2.5 ou Olá. - -## Saída com output: - -Uma saída mostra texto e valores. O texto fica entre aspas, as variáveis não. Com `+` **juntas** as partes: - -``` -output: "Olá " + nome + "!" -output: "Tens " + idade + " anos." -``` - -Atenção aos espaços dentro das aspas. Sem eles fica tudo colado, e vês `OláInês!` em vez de `Olá Inês!`. - -## Exercício: Cumprimento - -::: übung l06-begruessung -titel: Cumprimento -auftrag: Pede um nome e cumprimenta a pessoa. Quando se escreve Ada, deve ser mostrado **Olá Ada!**. -test: Ada -> Olá Ada! -test: Rui -> Olá Rui! -braucht: anweisung -tipp: Primeiro vem uma entrada para o nome, depois uma saída que usa o nome. -tipp: O nome é texto, por isso a entrada precisa do tipo `String`. Na saída juntas `"Olá "`, a variável e `"!"` com `+`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Cuidado com o + numa saída - -Numa saída, o `+` junta **sempre** as partes, mesmo com números. Se `a` for 3 e `b` for 4, então - -``` -output: a + b -``` - -não mostra 7, mas **34**. Por isso calcula primeiro numa variável e depois mostra a variável: - -``` -int soma = a + b -output: "Soma: " + soma -``` - -Isso também encaixa no modelo EPS: calculas na parte do processamento, não na saída. - -## Da tarefa ao estrutograma - -Antes de arrastares o primeiro bloco, lê a tarefa com atenção e desmonta-a. A isto chama-se **análise do problema**. Três perguntas ajudam-te: - -1. **O que é dado?** Esses valores tornam-se a **entrada**. Pensa também no tipo de dados. -2. **O que procuras?** Isso torna-se a **saída**. -3. **Como chegas do que é dado ao que procuras?** Isso é o **processamento**, muitas vezes uma fórmula. - -Um exemplo: *Um bilhete de cinema custa 9 euros. O programa pergunta quantos bilhetes alguém compra e mostra o preço total.* - -- Dado: o número de bilhetes → `input: int quantidade "Quantos bilhetes?"` -- Procurado: o preço total → `output: "Preço total: " + total` -- Caminho: quantidade vezes 9 → `int total = quantidade * 9` - -Agora só tens de pôr as três partes umas por cima das outras, pela ordem E, P, S. - -> Procura palavras-sinal na tarefa: *pergunta*, *lê* ou *introduz* apontam para uma entrada. *Mostra*, *escreve* ou *indica* apontam para uma saída. - -## Exercício: Calculadora - -::: übung l06-rechner -titel: Soma e produto -auftrag: Pede dois números inteiros. Depois mostra a soma e o produto deles, cada um na sua linha. -Com 3 e 4 fica assim: `Soma: 7` e `Produto: 12`. -test: 3, 4 -> Soma: 7 | Produto: 12 -test: 10, -2 -> Soma: 8 | Produto: -20 -test: 0, 5 -> Soma: 5 | Produto: 0 -braucht: anweisung -tipp: Planeia a ordem: primeiro ler os dois números, depois calcular, depois mostrar. -tipp: Precisas de duas entradas, duas instruções para calcular e duas saídas. Calcula a soma numa variável própria, senão o `+` junta os números. -lösung: l06-rechner-loesung.visustruct -::: - -## Exercício: Média - -::: übung l06-mittelwert -titel: Média -auftrag: Pede dois números, que também podem ser números decimais. Mostra a média deles, ou seja, para 2 e 3 é `Média: 2.5`. -test: 2, 3 -> Média: 2.5 -test: 4, 6 -> Média: 5.0 -test: 1.5, 2 -> Média: 1.75 -braucht: anweisung -tipp: A média de dois números obtém-se somando-os e dividindo o resultado ao meio. -tipp: Para números decimais usas o tipo `double`. A média é a soma a dividir por 2. Não te esqueças dos parênteses: `(x + y) / 2`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Agora em Java - -O exemplo EPS do início fica assim em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Primeiro número?"); -int a = scanner.nextInt(); -System.out.println("Segundo número?"); -int b = scanner.nextInt(); -int soma = a + b; -System.out.println("Soma: " + soma); -``` - -- Para entradas, o Java precisa de um **Scanner**. Escreves as duas primeiras linhas uma vez lá em cima e depois podes ler quantas vezes quiseres. -- `scanner.nextInt()` lê um número inteiro, `scanner.nextDouble()` um número decimal e `scanner.next()` uma palavra. -- A pergunta ao utilizador é uma saída normal logo antes. O VisuStruct junta as duas linhas num bloco de entrada. - -::: angeberwissen -O Java foi pensado para controlar pequenos aparelhos, e ainda o faz: em muitos cartões SIM de telemóvel e cartões bancários corre uma mini versão chamada **Java Card**. É bem possível que tenhas Java no bolso neste momento. -::: - -::: übung l06-java-tage -titel: Idade em dias -code: java -auftrag: Escreve um programa em Java que pergunta a idade em anos e mostra quantos dias são, mais ou menos. Conta 365 dias por ano. Para 2 anos aparece **São cerca de 730 dias.** -test: 2 -> São cerca de 730 dias. -test: 10 -> São cerca de 3650 dias. -test: 1 -> São cerca de 365 dias. -braucht: anweisung -tipp: Primeiro as duas linhas do scanner, depois a pergunta e a entrada, depois o cálculo, depois a saída. -tipp: `int anos = scanner.nextInt();` lê. `int dias = anos * 365;` calcula, e `System.out.println("São cerca de " + dias + " dias.");` mostra. -lösung: l06-java-tage-loesung.visustruct -::: - -## Comentários no código - -Podes escrever notas no teu código. Chamam-se **comentários** (em inglês *comments*). O Java salta-os ao executar, por isso o programa e o estrutograma ficam iguais. Há dois tipos: - -- `//` transforma o resto da linha num comentário. É um **comentário de uma linha**. -- `/*` começa um **comentário de várias linhas** e `*/` termina-o. Tudo o que está entre os dois faz parte do comentário, seja quantas linhas forem. O Java não precisa dos asteriscos no início das linhas do meio, só ficam arrumados. - -Em programas acabados às vezes também vês `/** … */` com dois asteriscos no início. É um **comentário Javadoc**, a partir do qual o Java consegue criar uma descrição do programa. Para ti, chegam `//` e `/* */`. - -O exemplo EPS com comentários fica assim: - -```java editor -/* - * Tarefa: O programa pede dois números - * e mostra a sua soma. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Entrada -System.out.println("Primeiro número?"); -int a = scanner.nextInt(); -System.out.println("Segundo número?"); -int b = scanner.nextInt(); - -// Processamento -int soma = a + b; // somar os dois números - -// Saída -System.out.println("Soma: " + soma); // com 3 e 4 espero: Soma: 7 - -// Da próxima vez: permitir também números decimais -``` - -Para que servem os comentários: - -- **A tarefa:** o comentário de várias linhas lá em cima diz o que o programa deve fazer. -- **Organização:** `// Entrada`, `// Processamento` e `// Saída` dividem o código segundo o modelo EPS. -- **Explicação:** um comentário depois de uma linha diz o que o teu algoritmo faz ali. -- **Expectativa:** escreve que saída esperas. Ao testar, comparas. -- **Associar chavetas:** mais tarde vais ter blocos entre chavetas `{ }`. Aí, p. ex. `} // fim do for` mostra que chaveta fecha o quê. -- **Guardar ideias:** de que te tens de lembrar quando continuares da próxima vez? - -Clica em **Abrir no editor**, muda um comentário ou acrescenta um novo. O estrutograma fica igual e o VisuStruct guarda os teus comentários. - -> Não escrevas um comentário depois de cada linha. Explica o que o teu algoritmo faz e porquê, não cada carácter. - -::: angeberwissen -Os dois tipos são muito mais antigos do que o Java: `/* … */` vem da linguagem PL/I, de 1964, e `//` da BCPL, de 1967. O Java herdou-os através do C e do C++. -::: - -## Preenche os espaços: comentários - -::: lückentext -titel: Completar os comentários -auftrag: Completa os caracteres para que lá em cima fique um comentário de várias linhas com a tarefa e as outras duas notas sejam comentários de uma linha. -```java -[[/*]] - * Tarefa: O programa converte euros em cêntimos. - [[*/]] -int euros = 3; -[[//]] Processamento -int centimos = euros * 100; -[[//]] Saída, esperado: 300 -System.out.println(centimos); -``` -erklärung: `/*` abre o comentário de várias linhas e `*/` fecha-o. `//` só vale até ao fim da linha, por isso cada nota precisa do seu próprio `//`. -::: - -## Preenche os espaços: entrada - -::: lückentext -titel: Ler um nome e uma idade -auftrag: Preenche as palavras que faltam para que o programa leia um nome e uma idade. -```java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("Como te chamas?"); -[[String]] nome = scanner.[[next]](); -System.out.println("Que idade tens?"); -int idade = scanner.[[nextInt]](); -System.out.println("Olá " + nome + ", para o ano vais ter " + [[(idade + 1)]] + " anos."); -``` -erklärung: `scanner.next()` lê uma palavra, `scanner.nextInt()` um número inteiro. Os parênteses à volta de `idade + 1` são importantes: sem eles, o Java junta primeiro `idade` e depois `1` ao texto, e 14 passaria a **141**. -::: - -## Encontra o erro: soma - -::: übung l06-java-fehler -titel: Corrigir a soma -code: java -auftrag: O programa lê dois números e deve mostrar a soma. Mas com 3 e 4 aparece **Soma: 34** em vez de **Soma: 7**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Soma: 7 -test: 10, 5 -> Soma: 15 -test: 0, 9 -> Soma: 9 -braucht: anweisung -tipp: Lê a saída da esquerda para a direita: o que dá `"Soma: " + a`? E o que acontece quando ainda vem `+ b` a seguir? -tipp: Põe parênteses para o Java calcular primeiro: `System.out.println("Soma: " + (a + b));` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l06-java-laeuft-nicht -titel: Idade no próximo ano -code: java -auftrag: O programa pergunta a tua idade e deve dizer quantos anos terás no próximo ano. Com 14, **No próximo ano tens 15**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> No próximo ano tens 15 -test: 9 -> No próximo ano tens 10 -braucht: anweisung -tipp: A entrada fica guardada numa variável. Como se chama exatamente, e como se chama na saída? -tipp: A variável chama-se `idade` com i minúsculo: `System.out.println("No próximo ano tens " + (idade + 1));` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: O que aparece? - -::: spiel frage -titel: Jogo: O que aparece? -intro: O Ludwig construiu entradas e saídas. Diz antes o que acontece. Sete rondas. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: O que aparece? -- 7 -- [x] 34 -- a + b -> Numa saída, o `+` junta sempre as partes, mesmo números. - -runde: - int a = 3 - int b = 4 - int soma = a + b - output: "Soma: " + soma -frage: O que aparece? -- [x] Soma: 7 -- Soma: 34 -- Soma: soma -> O cálculo acontece antes, numa variável. A saída só junta o texto e o resultado. - -runde: - input: String nome "Como te chamas?" - output: "Olá" + nome + "!" -frage: Escreves Mia. O que aparece? -- Olá Mia! -- [x] OláMia! -- Olá nome! -> O texto `"Olá"` não tem espaço no fim. Por isso fica tudo colado. - -runde: - input: int idade "Quantos anos tens?" -frage: Escreves 2.5. O que acontece? -- idade fica 2. -- idade fica 2.5. -- [x] A simulação mostra um erro. -> O tipo de dados `int` só aceita números inteiros. - -runde: - input: double preco "Preço em euros?" -frage: A que parte do modelo EPS pertence este bloco? -- [x] Entrada -- Processamento -- Saída -> `input:` pede um valor. Isso é a entrada. - -runde: - double media = (a + b) / 2.0 -frage: A que parte do modelo EPS pertence este bloco? -- Entrada -- [x] Processamento -- Saída -> Aqui calcula-se alguma coisa. Isso é o processamento. - -runde: - output: "Média: " + media -frage: A que parte do modelo EPS pertence este bloco? -- Entrada -- Processamento -- [x] Saída -> `output:` mostra o resultado. Isso é a saída. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o modelo EPS. → O modelo EPS -- Consigo escrever uma entrada com tipo de dados, nome e pergunta. → Entrada com input: -- Consigo juntar texto e variáveis numa saída com +. → Cuidado com o + numa saída -- Consigo calcular primeiro um resultado e guardá-lo numa variável, depois mostrar essa variável, p. ex. `int soma = a + b`, depois `output: soma`. → Cuidado com o + numa saída -- Consigo identificar a entrada, o processamento e a saída numa tarefa. → Da tarefa ao estrutograma -- Consigo ler números em Java com o scanner. → Agora em Java -- Consigo escrever comentários com `//` e `/* */` e usá-los para organizar e explicar o meu código. → Comentários no código -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer EPS? -→ O modelo EPS -- [x] Entrada, processamento, saída -- [ ] Entrada, programa, sequência -- [ ] Editar, partilhar, sair -> Muitos programas pedem primeiro valores, processam-nos e depois mostram o resultado. - -? As variáveis a e b têm os valores 3 e 4. O que mostra `output: a + b`? -→ Cuidado com o + numa saída -- [ ] 7 -- [x] 34 -- [ ] a + b -> Numa saída, o + junta as partes. Calcula primeiro a soma numa variável. - -? Que entrada serve se for introduzido um preço como 4.99? -→ Entrada com input: -- [ ] input: int preco "Preço?" -- [x] input: double preco "Preço?" -- [ ] input: String "Preço?" -> 4.99 é um número decimal, portanto double. Além disso, cada entrada precisa de um nome de variável. - -? O que mostra `output: "Idade: " + idade` se idade tiver o valor 12? -→ Cuidado com o + numa saída -- [x] Idade: 12 -- [ ] "Idade: " + 12 -- [ ] Idade: idade - -? Tarefa: *O programa pede o raio de um círculo e mostra o seu perímetro.* Associa cada parte do texto ao seu lugar no modelo EPS. -→ Da tarefa ao estrutograma -= pede o raio -> Entrada -= perímetro = 2 * 3.14 * raio -> Processamento -= mostra o seu perímetro -> Saída -> O raio é dado, o perímetro é o que procuras. A fórmula liga os dois. - -? O que lê `scanner.nextInt()`? -→ Agora em Java -- [x] Um número inteiro -- [ ] Um número decimal -- [ ] Uma frase inteira -> Para números decimais há `scanner.nextDouble()`, para uma palavra `scanner.next()`. - -? O que faz o Java com um comentário? -→ Comentários no código -- [ ] Mostra-o como texto. -- [x] Salta-o. -- [ ] Indica um erro. -> Os comentários são notas para pessoas. O programa corre exatamente igual com ou sem eles. - -? Onde termina um comentário que começa com `/*`? -→ Comentários no código -- [x] Em `*/` -- [ ] No fim da linha -- [ ] Na próxima linha vazia -> Por isso um comentário com `/*` pode ocupar várias linhas. Já um comentário com `//` termina no fim da linha. -::: diff --git a/web/lessons/pt/07-simulation-und-schreibtischtest.md b/web/lessons/pt/07-simulation-und-schreibtischtest.md deleted file mode 100644 index 187fd9a6..00000000 --- a/web/lessons/pt/07-simulation-und-schreibtischtest.md +++ /dev/null @@ -1,364 +0,0 @@ ---- -titel: Simulação e teste de secretária -bereich: Fundamentos -kurz: Executar um estrutograma passo a passo, segui-lo à mão e encontrar erros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- iniciar a simulação, fazer pausa, executar passos individuais e pará-la -- ler as variáveis, a saída e o último passo durante a simulação -- fazer um teste de secretária à mão, em forma de tabela -- distinguir erros de sintaxe, erros de execução e erros de lógica -- encontrar um erro comparando os valores com o que espero -- acompanhar código Java com Passo a passo e pontos de paragem -::: - -## Iniciar a simulação - -Antes de chegarem as decisões e os ciclos, vais conhecer a tua ferramenta mais importante. A partir de agora podes usar a simulação para verificar sozinho cada exercício. - -Com o botão [▶︎ Iniciar simulação](#zeige-simulation) à direita nas ferramentas, o VisuStruct executa o teu estrutograma. Enquanto a simulação está a correr, não podes editar o diagrama. O mesmo botão chama-se então **■ Terminar simulação** e leva-te de volta à edição. - -À direita aparecem os **Controlos**: - -- **Reproduzir** executa o diagrama automaticamente, passo a passo. Quanto tempo espera entre dois passos defines em **Pausa por passo**. -- **Pausa** para por um momento. Depois continuas com Reproduzir ou Passo. -- **Passo** executa exatamente um passo. Assim podes observar com calma. -- **Parar** cancela e põe tudo de volta ao início. - -Abre o diagrama seguinte no editor e percorre-o com **Passo**: - -::: diagramm sim-tausch.visustruct -Os valores de a e b são trocados. A variável aux guarda um dos valores entretanto. -::: - -## O que a simulação mostra - -- **Destaque:** Um bloco do diagrama fica colorido. Com **Último passo** é o bloco que acabou de ser executado. Com **Próximo passo** é o bloco que vem a seguir. -- **Entrada:** Se o diagrama pedir um valor, escreves aqui e confirmas com **OK**. -- **Último passo:** Aqui vês o que acabou de acontecer, p. ex. `a = 5 -> 5` depois do bloco `a = b`. À esquerda, as variáveis já foram substituídas pelos seus valores; à direita da seta está o resultado. -- **Variáveis:** Todas as variáveis com os seus valores atuais. -- **Saída:** Tudo o que foi mostrado com `output:`. - -Se a área [Código](#zeige-codelive) estiver aberta à direita, a [consola](#zeige-konsole) também acompanha lá. É limpa no início e depois mostra as saídas e entradas tal como apareceriam no programa real. - -No fim aparece **Simulação terminada.** Se algo correr mal, a simulação para no bloco com erro e mostra uma mensagem, p. ex. **Variável desconhecida: total**. - -## O teste de secretária - -Num **teste de secretária** és tu o computador. Percorres o estrutograma com papel e caneta e anotas os valores das variáveis depois de cada passo. O melhor é fazer uma tabela: uma coluna para o passo, uma para cada variável e uma para a saída. - -::: grafik l07-schreibtischtest-pt.svg -Basta uma folha de papel e uma caneta: depois de cada passo escreves os novos valores numa nova linha. -::: - -Para o diagrama de cima, a tabela fica assim: - -| Passo | a | b | aux | Saída | -|---|---|---|---|---| -| `int a = 3` | 3 | – | – | | -| `int b = 5` | 3 | 5 | – | | -| `int aux = a` | 3 | 5 | 3 | | -| `a = b` | 5 | 5 | 3 | | -| `b = aux` | 5 | 3 | 3 | | -| `output: "a = " + a` | 5 | 3 | 3 | a = 5 | -| `output: "b = " + b` | 5 | 3 | 3 | b = 3 | - -Depois compara a tua tabela com a simulação. Os valores em **Variáveis** coincidem com a tua linha depois de cada passo? - -> Um teste de secretária é especialmente útil nos testes escritos. Aí não tens computador, mas mesmo assim podes mostrar o que o teu estrutograma faz. - -::: angeberwissen -Antigamente, “computador” era uma profissão: pessoas que faziam contas com lápis, papel e máquina de calcular. Uma das melhores foi a matemática Katherine Johnson, na NASA. Ela calculava trajetórias de cápsulas espaciais e, mais tarde, também do voo da Apollo 11 até à Lua. - -Em 1962, John Glenn ia ser o primeiro americano a dar a volta à Terra em órbita. A trajetória tinha sido calculada por um novo computador da IBM. Glenn não confiava totalmente na máquina e disse: “Chamem a rapariga para verificar os números. Se ela disser que estão certos, eu vou.” Katherine Johnson refez todas as contas à mão, um verdadeiro teste de secretária. Os números estavam certos, e Glenn voou. - -Katherine Johnson era negra e, nessa altura, no estado norte-americano da Virgínia, havia leis que separavam pessoas negras e brancas. No filme “Elementos Secretos” (2016), ela tem de atravessar o recinto todo a correr sempre que precisa de ir à casa de banho. O filme exagera: as casas de banho separadas existiam mesmo, mas Katherine Johnson contou que simplesmente ignorava a placa. - -A sua colega Dorothy Vaughan percebeu cedo que os computadores da IBM iam ficar com o trabalho de cálculo. Aprendeu sozinha a linguagem de programação FORTRAN e ensinou-a a toda a sua equipa. Foi assim que os computadores humanos passaram a ser programadoras. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), matemática na NASA (foto: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), domínio público). -::: - -## Três tipos de erros - -Os erros fazem parte da programação, até para profissionais. Há três tipos, e cada um encontra-se de outra maneira: - -- **Erro de sintaxe** (syntax error): O texto não cumpre as regras da linguagem, p. ex. falta um `;` no fim. O programa nem sequer arranca. -- **Erro de execução** (runtime error): O programa arranca, mas para num certo ponto, p. ex. ao dividir por 0. -- **Erro de lógica** (logic error): O programa corre até ao fim, mas o resultado está errado, p. ex. porque faltam parênteses: `a + b / 2` em vez de `(a + b) / 2`. - -É assim que o VisuStruct te mostra os erros: - -- **Erro de sintaxe:** No estrutograma acontecem menos, porque os blocos dão a forma. Na área [Código](#zeige-codelive) o sítio fica sublinhado a vermelho, o número da linha fica vermelho e lês, por exemplo, **Linha 2: Ainda falta aqui: ;**. -- **Erro de execução:** A simulação para no bloco e indica, por exemplo, **Divisão por zero.** Na área Código o número da linha fica vermelho e a [consola](#zeige-konsole) diz a linha: **Linha 3: Divisão por zero.** -- **Erro de lógica:** Aqui ninguém avisa nada. O computador não sabe o que querias. Só tu conheces o resultado certo, por isso aqui ajuda o teste de secretária. - -> Um número de linha vermelho mostra um erro. O ponto vermelho redondo antes do número da linha é outra coisa: um ponto de paragem. Vais conhecê-lo mais abaixo em “Agora em Java”. - -## Encontrar erros - -Se um estrutograma mostrar algo errado, é um erro de lógica. Então faz assim: - -1. **Escreve o que esperas:** O que deve sair para uma determinada entrada? -2. **Executa passo a passo:** Inicia a simulação com **Passo** e observa as variáveis. -3. **Encontra o sítio:** Onde é que um valor fica pela primeira vez diferente do que esperavas? É exatamente aí que está o erro. - -Erros frequentes numa sequência são: - -- A **ordem** está errada: uma variável é usada antes de ter um valor. -- Um valor é **escrito por cima** antes de ser preciso. -- Faltam **parênteses**: `a + b / 2` não é o mesmo que `(a + b) / 2`. -- São divididos dois números inteiros: `7 / 2` dá `3`, porque com `int` tudo o que vem depois da vírgula desaparece. - -> Mais tarde chegam as decisões e os ciclos. Aí a simulação também te mostra que ramo é seguido e quantas vezes um ciclo é executado. - -::: angeberwissen -A 9 de setembro de 1947, a equipa de Grace Hopper encontrou uma traça verdadeira presa entre dois contactos do computador Harvard Mark II. Colaram-na no diário de bordo e escreveram: “Primeiro caso real de um bug encontrado.” Hoje a traça está num museu em Washington. - -Grace Hopper foi uma das primeiras programadoras do mundo. Em 1952 construiu um dos primeiros **compiladores**: um programa que traduz código para código máquina. Achava que devia ser possível dar ordens aos computadores em inglês quase normal. Daí nasceu a linguagem de programação COBOL, que muitos bancos ainda hoje usam. Na Marinha dos EUA chegou mesmo a almirante. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), informática e almirante (foto: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), domínio público). -::: - -## Exercício: Corrigir a troca - -::: übung sim-tausch -titel: Corrigir a troca -auftrag: O estrutograma deve ler dois números e mostrá-los trocados. Ou seja, com 3 e 5 deve aparecer primeiro **5** e depois **3**. Mas mostra 5 duas vezes. - Faz um teste de secretária ou usa **Passo** e corrige o erro. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Percorre o diagrama com **Passo** e anota os valores de a e b depois de cada passo. Onde é que o 3 desaparece? -tipp: Olha para a depois do passo `a = b`. Para onde foi o valor antigo de a? Guarda-o primeiro numa variável auxiliar, como no exemplo de cima. -lösung: sim-tausch-loesung.visustruct -::: - -## Exercício: Corrigir os bilhetes de cinema - -::: übung sim-kino -titel: Corrigir os bilhetes de cinema -auftrag: Um bilhete de cinema custa 8.50 euros. O estrutograma deve pedir o número de bilhetes e mostrar o preço total, ou seja, para 2 bilhetes `Total: 17.0`. - Inicia a simulação. Ela para com uma mensagem de erro. Descobre porquê e corrige o estrutograma. -start: sim-kino-start.visustruct -test: 2 -> Total: 17.0 -test: 3 -> Total: 25.5 -test: 0 -> Total: 0.0 -braucht: anweisung -tipp: Lê com atenção a mensagem de erro da simulação. Que variável é que ela ainda não conhece? -tipp: A simulação executa os blocos de cima para baixo. O total já tem um valor quando chega a saída? Podes mover os blocos com o rato. -lösung: sim-kino-loesung.visustruct -::: - -> Se uma simulação nunca mais acabar, clica em **Parar**. Quando **Verificas** um exercício, o VisuStruct para sozinho depois de um número muito grande de passos. - -## Agora em Java - -Também podes executar código Java passo a passo. Na área [Código](#zeige-codelive), a ferramenta para isso chama-se **depurador** (debugger): - -- Clica num número de linha à esquerda. Aparece um ponto vermelho: é um **ponto de paragem**. [▶︎ Executar](#zeige-ausfuehren) para antes dessa linha. -- O botão [Passo a passo](#zeige-einzelschritt) executa exatamente uma linha, como **Passo** na simulação. -- Por baixo do código vês o valor atual de cada variável. É o teu teste de secretária, só que é o computador que escreve. - -::: übung l07-java-pizza -titel: Corrigir a encomenda de pizzas -code: java -auftrag: Uma pizza custa 8 euros, o queijo extra 2 euros. Para 3 pizzas com queijo extra, o programa deve mostrar **Total: 30**. Mas mostra 14. - Percorre o código Java com Passo a passo e observa `total`. Encontra o erro e corrige-o no código. -start: l07-java-pizza-start.visustruct -test: - -> Total: 30 -braucht: anweisung -tipp: Que valor está em `total` depois da quarta linha? Faz as contas de cabeça: o que é que o Java calcula primeiro? -tipp: Como na matemática, a multiplicação vem antes da soma: `preco + queijo * quantidade` é `8 + 6`. Põe parênteses: `(preco + queijo) * quantidade`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Preenche os espaços: teste de secretária - -::: lückentext -titel: O que está nas variáveis? -auftrag: Faz um teste de secretária de cabeça. Escreve depois de cada linha o valor que a variável tem a seguir e, na última linha, o que é mostrado. -```java -int a = 5; -int b = 2; -a = a + b; // a vale agora [[7]] -b = a * 2; // b vale agora [[14]] -a = b - a; // a vale agora [[7]] -System.out.println(a + b); // saída: [[21]] -``` -erklärung: Cada linha calcula com os valores que estão nesse momento nas variáveis: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, saída 7 + 14 = 21. Podes confirmar com Passo a passo na área Código. -::: - -## Encontra o erro: pontuação - -::: übung l07-java-fehler -titel: Corrigir a pontuação -code: java -auftrag: Num jogo, o Ludwig ganha primeiro 10 pontos, depois 5 pontos e depois perde 3 pontos. No fim deve aparecer **Pontos: 12**. Mas aparece **Pontos: 3**. - Percorre o código com Passo a passo e observa `pontos`. Corrige a linha errada e carrega em Verificar. -start: l07-java-fehler-start.visustruct -test: - -> Pontos: 12 -braucht: anweisung -tipp: Depois de que linha é que `pontos` passa de repente a valer 3? O que devia acontecer nessa linha? -tipp: O Ludwig perde 3 pontos, por isso tira-se 3 ao valor antigo: `pontos = pontos - 3;` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l07-java-laeuft-nicht -titel: Corrigir o bónus -code: java -auftrag: O Ludwig tem 10 pontos e recebe 5 pontos de bónus. No fim deve aparecer **Pontos: 15**. Mas o código não corre. Percorre-o também com o passo a passo: em que linha para? - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Pontos: 15 -braucht: anweisung -tipp: O Java executa o código de cima para baixo. A variável `bonus` já existe quando é usada no cálculo? -tipp: Cria `bonus` antes do cálculo: primeiro `int bonus = 5;`, depois `pontos = pontos + bonus;` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa tem **exatamente um erro**, e a bomba está a fazer tique-taque. Faz o teste de secretária de cabeça e toca na linha errada antes de o tempo acabar. Os programas estão em Java, mas cada linha é um bloco como no estrutograma. Seis rondas, 30 segundos cada. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int aux = a; - a = b; - b = a; -frage: Deve trocar os valores de a e b. -fehler: 5 -besser: b = aux; -> a já tem o valor de b. O valor antigo de a está em aux. - -runde: - int a = 4; - int b = 6; - double media = a + b / 2; - System.out.println(media); -frage: Deve mostrar a média 5.0. -fehler: 3 -besser: double media = (a + b) / 2.0; -> Sem parênteses, a divisão vem primeiro: 4 + 6 / 2 = 7. - -runde: - int bilhetes = 3; - double preco = 8.5; - double total = bilhetes + preco; - System.out.println("Total: " + total); -frage: Deve mostrar o preço de 3 bilhetes de cinema a 8,50 € cada. -fehler: 3 -besser: double total = bilhetes * preco; -> Para o preço total multiplica-se, não se soma. - -runde: - int minutos = 135; - int horas = minutos / 60; - int resto = minutos / 60; - System.out.println(horas + " h " + resto + " min"); -frage: Deve mostrar 2 h 15 min. -fehler: 3 -besser: int resto = minutos % 60; -> `%` dá o resto de uma divisão. Com `/`, resto também ficava com 2. - -runde: - String nome = "Ada"; - System.out.println("Olá " + "nome" + "!"); -frage: Deve mostrar "Olá Ada!". -fehler: 2 -besser: System.out.println("Olá " + nome + "!"); -> Entre aspas, nome é só um texto. A variável não tem aspas. - -runde: - double preco = 100; - preco = preco * 0.1; - System.out.println(preco); -frage: Deve aumentar o preço em 10 %, ou seja, mostrar 110.0. -fehler: 2 -besser: preco = preco + preco * 0.1; -> Vezes 0.1 dá só os 10 %, ou seja, 10.0. Isso ainda tem de ser somado ao preço antigo. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo iniciar a simulação, fazer pausa, executar passos individuais e pará-la. → Iniciar a simulação -- Consigo ler as variáveis, a saída e o último passo durante a simulação. → O que a simulação mostra -- Consigo fazer um teste de secretária à mão, em forma de tabela. → O teste de secretária -- Consigo distinguir erros de sintaxe, erros de execução e erros de lógica. → Três tipos de erros -- Consigo encontrar um erro comparando os valores com o que espero. → Encontrar erros -- Consigo acompanhar código Java com Passo a passo e pontos de paragem. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que botão executa exatamente um passo? -→ Iniciar a simulação -- [ ] Reproduzir -- [x] Passo -- [ ] Parar -> Reproduzir continua automaticamente, Passo executa só um passo. - -? O que anotas depois de cada passo num teste de secretária? -→ O teste de secretária -- [ ] Só a saída no fim -- [x] Os valores atuais de todas as variáveis -- [ ] O código Java gerado -> Assim vês exatamente onde um valor é diferente do que esperavas. - -? Primeiro `int a = 3`, depois `int b = 5`, depois `a = b`. Que valores têm a e b a seguir? -→ O teste de secretária -- [ ] a = 5 e b = 3 -- [x] a = 5 e b = 5 -- [ ] a = 3 e b = 5 -> O valor antigo de a foi escrito por cima. É por isso que precisas de uma variável auxiliar para trocar valores. - -? O campo Último passo mostra `preco = 4 * 2.5 -> 10.0`. O que quer isso dizer? -→ O que a simulação mostra -- [ ] A variável preco tem agora o valor 4. -- [x] Foi calculado 4 * 2.5 e o resultado 10.0 foi guardado em preco. -- [ ] A simulação encontrou um erro. - -? A simulação indica **Variável desconhecida: soma**. Qual é uma razão provável? -→ Encontrar erros -- [x] soma é usada antes de lhe ter sido dado um valor. -- [ ] soma é demasiado grande. -- [ ] O estrutograma tem demasiados blocos. - -? O que faz um ponto vermelho antes de um número de linha na área Código? -→ Agora em Java -- [ ] Marca um erro. -- [x] O programa para antes dessa linha. -- [ ] A linha é saltada. -> É um ponto de paragem. Continuas com ▶︎ ou com Passo a passo. -? Associa cada erro ao seu tipo. -→ Três tipos de erros -= Falta o ponto e vírgula no fim da linha. -> Erro de sintaxe -= O programa divide por 0 e para. -> Erro de execução -= A média dá demasiado alta porque faltam parênteses. -> Erro de lógica - -? O programa corre até ao fim sem nenhuma mensagem, mas mostra 12 em vez de 21. Que tipo de erro é? -→ Três tipos de erros -- [ ] Erro de sintaxe -- [ ] Erro de execução -- [x] Erro de lógica -> Não há mensagem, mas o resultado está errado. Um erro destes só se encontra com o que esperas e com o teste de secretária. -::: diff --git a/web/lessons/pt/08-verzweigung.md b/web/lessons/pt/08-verzweigung.md deleted file mode 100644 index 26ffe253..00000000 --- a/web/lessons/pt/08-verzweigung.md +++ /dev/null @@ -1,368 +0,0 @@ ---- -titel: Decisão -bereich: Fundamentos -kurz: Usar uma condição para decidir qual de dois ramos é executado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que é executado exatamente um ramo de uma decisão -- usar os operadores de comparação == != < <= > >= -- distinguir == de = -- construir uma decisão com um ramo vazio -- escrever uma decisão com if e else em Java -::: - -## Se, então, senão - -Até agora, todos os blocos eram sempre executados. Mas muitas vezes um programa deve **decidir**: se uma condição se verificar, faz uma coisa, senão faz a outra. - -::: diagramm l07-alter-pruefen.visustruct -Consoante a idade introduzida, aparece uma mensagem ou a outra. Experimenta 17 e 18 na simulação. -::: - -É para isso que serve a **decisão**. A condição fica no triângulo em cima. Por baixo, o estrutograma divide-se em dois ramos: - -- **true** (esquerda): é executado se a condição for verdadeira -- **false** (direita): é executado se a condição for falsa - -É sempre executado **exatamente um** dos dois ramos. Depois, o programa continua por baixo da decisão. - -## Inserir uma decisão - -Arrasta a **Decisão** da área Elementos para o estrutograma. Clica no triângulo e escreve a condição no Inspetor de objetos, p. ex. `idade >= 18`. A palavra `if` já está à frente. - -Cada ramo começa com um marcador ø. Arrasta instruções para lá. Um ramo também pode ter vários blocos uns por cima dos outros. - -## Operadores de comparação - -Uma condição é normalmente uma **comparação**. O seu resultado é sempre `true` ou `false`. - -- `==` igual, p. ex. `numero == 0` -- `!=` diferente, p. ex. `resposta != 42` -- `<` menor, `<=` menor ou igual -- `>` maior, `>=` maior ou igual - -> Não confundas `==` com `=`. Um só `=` é uma atribuição e guarda um valor. Para comparar precisas de dois sinais de igual. - -Com `%` verificas se um número é divisível por outro: `numero % 2 == 0` é verdadeiro se `numero` for par. - -## Exercício: Par ou ímpar - -::: übung l07-gerade -titel: Par ou ímpar -auftrag: Pede um número inteiro. Mostra **par** se for par, senão **ímpar**. -test: 4 -> par -test: 7 -> ímpar -test: 0 -> par -braucht: verzweigung -tipp: Aqui há duas possibilidades, por isso precisas de uma decisão. Como reconheces um número par? -tipp: Um número é par se não sobrar resto quando o divides por 2. A condição é `numero % 2 == 0`. -lösung: l07-gerade-loesung.visustruct -::: - -## Exercício: O número maior - -::: übung l07-maximum -titel: O número maior -auftrag: Pede dois números inteiros e mostra só o maior. Se os dois forem iguais, mostra esse número. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: Uma decisão compara os dois números. Cada ramo tem exatamente uma saída. -tipp: Compara com `a > b`. No ramo true mostras `a`, no ramo false `b`. -lösung: l07-maximum-loesung.visustruct -::: - -## Um ramo vazio - -Às vezes uma coisa só deve acontecer se a condição for verdadeira e, caso contrário, não acontece nada. Então o ramo false fica **vazio**. No estrutograma mostra o marcador **ø**. - -::: diagramm l07-rabatt.visustruct -A partir de 50 euros tens 5 euros de desconto. Caso contrário não acontece nada; o ramo false está vazio. A saída em baixo é executada nos dois casos. -::: - -::: angeberwissen -Uma única decisão esquecida deixou meia Internet em alvoroço em 2014: a falha de segurança **Heartbleed** no OpenSSL, um programa que muitos sites usam para a encriptação. Um computador podia dizer ao servidor: “Devolve-me a palavra *pão*, tem 500 letras.” O servidor não verificava se 500 correspondia ao comprimento real de 3 e devolvia simplesmente 500 caracteres da sua memória. Às vezes lá dentro estavam palavras-passe de outras pessoas. Só faltava um if: se o comprimento indicado for maior do que o real, então não devolver nada. Já agora, isto não tinha nada a ver com chavetas. Mééé, verifica as tuas entradas antes de acreditares nelas. -::: - -## Exercício: Valor absoluto - -::: übung l07-betrag -titel: Valor absoluto de um número -auftrag: Pede um número inteiro. Se for negativo, torna-o positivo. Depois mostra o número. Assim, -5 passa a 5, e 7 fica 7. -Usa uma decisão com um ramo vazio e só **uma** saída por baixo da decisão. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Só os números negativos têm de ser alterados. O que acontece a um número se o multiplicares por -1? -tipp: A condição é `numero < 0`. No ramo true trocas o sinal, p. ex. com `numero = 0 - numero`. O ramo false fica vazio. -lösung: l07-betrag-loesung.visustruct -::: - -## Agora em Java - -Esta é a verificação da idade do início desta lição em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Quantos anos tens?"); -int idade = scanner.nextInt(); -if (idade >= 18) { - System.out.println("És maior de idade."); -} else { - System.out.println("Ainda não és maior de idade."); -} -``` - -- A condição fica depois de `if`, entre **parênteses curvos** `( )`. -- O ramo true fica entre **chavetas** `{ }`, o ramo false vem depois de `else`, nas suas próprias chavetas. Depois de uma chaveta não há ponto e vírgula. -- Se o ramo false estiver vazio, deixas simplesmente de fora `else { }`. -- O teu programa precisa das duas primeiras linhas uma vez, assim que lê alguma coisa. Depois, `scanner.nextInt()` lê um número inteiro. A pergunta antes disso é uma saída normal. - -::: angeberwissen -O Java também tem uma mascote: o **Duke**, um bonequinho preto e branco com um grande nariz vermelho. Foi desenhado pela equipa do Java em 1992 e desde então acena em conferências e t-shirts. O Ludwig acha que um bode teria ficado melhor. -::: - -::: übung l08-java-bestanden -titel: Aprovado em Java -code: java -auftrag: Escreve um programa em Java que pergunta pela pontuação. A partir de 50 pontos mostra **aprovado**, senão **reprovado**. - Escreve o código na área Código e depois clica em Verificar. -test: 50 -> aprovado -test: 49 -> reprovado -test: 100 -> aprovado -test: 0 -> reprovado -braucht: verzweigung -tipp: Começa pelas duas linhas do scanner. Depois perguntas pelos pontos, lês o valor e comparas com `if`. -tipp: A condição é `pontos >= 50`. Num ramo fica `System.out.println("aprovado");` e depois de `else` vem `System.out.println("reprovado");`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Preenche os espaços: par ou ímpar - -::: lückentext -titel: Par ou ímpar -auftrag: Preenche as palavras e os sinais que faltam. O programa deve mostrar se um número é par. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que número?"); -int numero = scanner.nextInt(); -[[if]] (numero [[%]] 2 [[==]] 0) [[{]] - System.out.println("par"); -[[}]] [[else]] { - System.out.println("ímpar"); -} -``` -erklärung: `numero % 2` é o resto da divisão por 2. Se for 0, o número é par. Para comparar precisas de `==`, um `=` sozinho é uma atribuição. Os dois ramos ficam entre chavetas. -::: - -::: angeberwissen -Se um ramo tiver só uma instrução, o Java até deixa tirar as chavetas. Nós nunca fazemos isso, e é por esta razão: - -```java -if (idade >= 18) - System.out.println("És maior de idade."); - System.out.println("Podes ver o filme."); -``` - -Sem chavetas, só a primeira linha pertence ao if. A segunda é sempre executada, até para quem tem doze anos. A indentação engana, porque o Java não liga aos espaços. - -Foi exatamente um erro destes que estava escondido em iPhones e Macs em 2014, conhecido como **“goto fail”**. Na linguagem C, a linha `goto fail;` aparecia por engano duas vezes debaixo de um if sem chavetas. A segunda era sempre executada e saltava a verificação dos certificados de segurança. Numa rede Wi-Fi aberta, um atacante podia, por exemplo, fazer-se passar pelo teu banco. Mééé, as chavetas custam dois caracteres, uma falha de segurança custa muito mais. -::: - -## Encontra o erro: entrada - -::: übung l08-java-fehler -titel: Corrigir a entrada -code: java -auftrag: No jardim zoológico, as crianças com **menos** de 6 anos entram de graça, todos os outros pagam 4 euros. Mas o programa também deixa entrar de graça as crianças de seis anos. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l08-java-fehler-start.visustruct -test: 5 -> Entrada gratuita -test: 6 -> 4 euros -test: 10 -> 4 euros -test: 0 -> Entrada gratuita -braucht: verzweigung -tipp: Experimenta o programa com 6. Que operador de comparação ainda deixa passar o 6? -tipp: “Menos de 6” é `idade < 6`. `<=` significa “menor ou igual”. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l08-java-laeuft-nicht -titel: Filme para maiores de 16 -code: java -auftrag: O filme é para maiores de 16. O Ludwig tem 15, por isso o programa deve mostrar **Não pode entrar**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> Não pode entrar -braucht: verzweigung -tipp: Olha com atenção para a condição entre parênteses depois de `if`. Esta variável existe? -tipp: A variável chama-se `idade` com i minúsculo: `if (idade >= 16) {` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que caminho? - -::: spiel frage -titel: Jogo: Que caminho? -intro: O Ludwig escreveu decisões em Java. Avalia a condição de cabeça e diz o que aparece. Sete rondas. - -runde: - int x = 5; - if (x > 5) { - System.out.println("grande"); - } else { - System.out.println("pequeno"); - } -frage: O que aparece? -- grande -- [x] pequeno -- nada -> 5 > 5 é falso, porque 5 não é maior do que 5. Portanto, o ramo falso. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("grande"); - } else { - System.out.println("pequeno"); - } -frage: O que aparece? -- [x] grande -- pequeno -- grande e pequeno -> `>=` quer dizer "maior ou igual". 5 >= 5 é verdadeiro. - -runde: - int numero = 7; - if (numero % 2 == 0) { - System.out.println("par"); - } else { - System.out.println("ímpar"); - } -frage: O que aparece? -- par -- [x] ímpar -- 1 -> 7 % 2 dá resto 1, portanto não 0. O número é ímpar. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: O que aparece? -- -4 -- [x] 4 -- nada -> -4 < 0 é verdadeiro, por isso o sinal é invertido. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: O que aparece? -- -4 -- [x] 4 -- nada -> 4 < 0 é falso. O ramo falso está vazio, o programa continua simplesmente por baixo. - -runde: - int nota = 3; - if (nota != 3) { - System.out.println("diferente"); - } else { - System.out.println("três"); - } -frage: O que aparece? -- diferente -- [x] três -- nada -> `!=` quer dizer "diferente de". 3 != 3 é falso. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: O que aparece? -- 8 -- [x] 12 -- 20 -> O programa mostra o maior dos dois números. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que é executado exatamente um ramo de uma decisão. → Se, então, senão -- Consigo usar os operadores de comparação == != < <= > >=. → Operadores de comparação -- Consigo distinguir == de =. → Operadores de comparação -- Consigo construir uma decisão com um ramo vazio. → Um ramo vazio -- Consigo escrever uma decisão com if e else em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que ramo é executado se a condição for falsa? -→ Se, então, senão -- [ ] O ramo true -- [x] O ramo false -- [ ] Os dois ramos -- [ ] Nenhum, o programa termina -> Com false, é executado o ramo da direita. Depois, o programa continua por baixo da decisão. - -? Que condição verifica se idade é exatamente 18? -→ Operadores de comparação -- [ ] idade = 18 -- [x] idade == 18 -- [ ] idade >= 18 -> Um só = é uma atribuição. Para comparar precisas de ==. - -? O que significa ø num ramo? -→ Um ramo vazio -- [x] Neste caso não acontece nada. -- [ ] O programa termina aqui. -- [ ] A condição é falsa. - -? A condição é `pontos >= 50`. Para que valor é verdadeira? -→ Operadores de comparação -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= quer dizer maior ou igual. Portanto o 50 está incluído. - -? Nos manuais, a decisão tem muitas vezes outro nome. Qual? -→ Se, então, senão -- [ ] Seleção múltipla -- [x] Instrução condicional -- [ ] Iteração -> As instruções de um ramo só são executadas sob uma condição. Por isso também se chama instrução condicional. - -? Onde fica em Java a condição de uma decisão? -→ Agora em Java -- [x] Entre parênteses curvos logo a seguir a if -- [ ] Entre chavetas depois de else -- [ ] Depois de um ponto e vírgula -> `if (idade >= 18) { … }`: os parênteses curvos pertencem à condição, as chavetas ao ramo. -::: diff --git a/web/lessons/pt/09-bedingungen-verknuepfen.md b/web/lessons/pt/09-bedingungen-verknuepfen.md deleted file mode 100644 index e18e0a47..00000000 --- a/web/lessons/pt/09-bedingungen-verknuepfen.md +++ /dev/null @@ -1,328 +0,0 @@ ---- -titel: Combinar condições -bereich: Fundamentos -kurz: Juntar várias condições com e, ou e não, e verificar intervalos de valores. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar quando é que uma combinação com && é verdadeira -- explicar quando é que uma combinação com || é verdadeira -- inverter uma condição com ! -- verificar se um valor está num intervalo como de 1 a 5 -- ler a tabela de verdade de &&, || e ! -- guardar o resultado de uma condição numa variável boolean -- avaliar condições compostas com segurança -- ligar condições em Java com && e || -::: - -## E: && - -Às vezes uma só comparação não chega. Por exemplo, só tens direito a um bilhete jovem se tiveres **pelo menos 12 anos e menos de 18**. - -Com `&&` (e) juntas duas condições. O resultado só é `true` se **as duas** condições forem verdadeiras. - -::: diagramm l08-jugendticket.visustruct -As duas comparações têm de estar certas. Experimenta 11, 12, 17 e 18 na simulação. -::: - -## Ou: || - -Com `||` (ou), o resultado é `true` se **pelo menos uma** das condições for verdadeira. Também podem ser as duas. - -``` -dia == 6 || dia == 7 -``` - -Esta condição é verdadeira se `dia` tiver o valor 6 ou o valor 7, ou seja, ao sábado ou ao domingo. - -> Num teclado português, a barra vertical `|` escreve-se com Shift e a tecla à esquerda do 1. - -## Exercício: Fim de semana - -::: übung l08-wochenende -titel: Fim de semana -auftrag: Pede um dia da semana como número (1 é segunda-feira, 7 é domingo). Mostra **Fim de semana** se for sábado ou domingo, senão **Dia útil**. -test: 6 -> Fim de semana -test: 7 -> Fim de semana -test: 1 -> Dia útil -test: 5 -> Dia útil -braucht: verzweigung -tipp: É fim de semana se o dia for 6 **ou** 7. Para isso precisas de duas comparações numa só condição. -tipp: Sábado é 6, domingo é 7. Junta as duas comparações com `||`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Verificar intervalos de valores - -No ensino básico em Portugal, as notas vão de 1 a 5 (5 é a melhor). Verificas se uma nota está **dentro do intervalo** com duas comparações e `&&`: - -``` -nota >= 1 && nota <= 5 -``` - -::: grafik l08-zahlenstrahl.svg -O intervalo verde de 1 a 5 é válido; as duas pontas estão incluídas. -::: - -Ao contrário, uma nota está **fora** do intervalo se for demasiado pequena **ou** demasiado grande: - -::: diagramm l08-note.visustruct -Não há notas menores do que 1 nem maiores do que 5. -::: - -> A forma como se escreve na matemática, `1 <= nota <= 5`, não funciona. Tens de a dividir em duas comparações e juntá-las com `&&`. - -## Exercício: Nota válida - -::: übung l08-note-gueltig -titel: Nota válida -auftrag: Pede uma nota como número inteiro. Mostra **válida** se estiver entre 1 e 5 (1 e 5 incluídos), senão **inválida**. -test: 1 -> válida -test: 5 -> válida -test: 3 -> válida -test: 0 -> inválida -test: 6 -> inválida -braucht: verzweigung -tipp: Uma nota válida tem de cumprir duas coisas ao mesmo tempo: não ser demasiado pequena **e** não ser demasiado grande. -tipp: A nota tem de ser maior ou igual a 1 e, ao mesmo tempo, menor ou igual a 5. -lösung: l08-gueltig-loesung.visustruct -::: - -## Não e parênteses - -Com `!` (não) inverte-se uma condição: `true` passa a `false` e vice-versa. - -``` -!(nota >= 1 && nota <= 5) -``` - -Isto é verdadeiro se a nota **não** estiver no intervalo, e quer dizer o mesmo que `nota < 1 || nota > 5`. - -Se `&&` e `||` aparecerem juntos, o `&&` é calculado primeiro, tal como a multiplicação antes da adição. É melhor usar **parênteses**; assim fica claro o que pertence ao quê: - -``` -(idade >= 12 && idade < 18) || estudante == 1 -``` - -## A tabela de verdade - -Uma **tabela de verdade** mostra de relance o que resulta de `&&`, `||` e `!`. À esquerda estão as quatro possibilidades para duas condições `a` e `b`, à direita o resultado: - -::: grafik l09-wahrheitstabelle.svg -Verde é true, vermelho é false. Com && só há uma linha verde, com || só uma vermelha. -::: - -Para fixar: - -- `a && b` só é verdadeiro se **ambas** forem verdadeiras. -- `a || b` só é falso se **ambas** forem falsas. -- `!a` inverte o valor. - -::: angeberwissen -O tipo de dados `boolean` tem o nome de **George Boole**. Este inglês mostrou já em 1847 que se pode calcular com verdadeiro e falso como com números, quase cem anos antes de existir o primeiro computador. Hoje a sua lógica está em todos os chips. -::: - -## Valores lógicos em variáveis - -O resultado de uma condição é um **valor lógico** do tipo `boolean`. Podes guardá-lo numa variável, tal como um número, e usá-lo mais tarde: - -``` -boolean fimDeSemana = dia == 6 || dia == 7 -``` - -Na decisão fica então só `fimDeSemana`. Lê-se quase como uma frase: "Se for fim de semana, então …". Não escrevas `fimDeSemana == true`, porque `fimDeSemana` já é true ou false. - -Com `!fimDeSemana` perguntas pelo contrário, ou seja, um dia útil. - -::: ludwig -Condições longas ficam mais claras se puseres partes delas em variáveis boolean com nomes claros, por exemplo `boolean jovem = idade >= 12 && idade < 18`. -::: - -## Jogo: Verdadeiro ou falso? - -Agora é a tua vez. Calcula cada condição de cabeça. Depois de cada resposta vês como o VisuStruct a avalia passo a passo. - -::: spiel -idade: 8..20 -dia: 1..7 -nota: 0..6 -estudante: boolean -fimDeSemana: boolean -? idade >= 18 -? dia == 6 -? nota != 3 -? idade >= 12 && idade < 18 -? dia == 6 || dia == 7 -? estudante && fimDeSemana -? !estudante -? nota >= 1 && nota <= 5 -? !(idade < 18) -? estudante || idade >= 18 -? (idade >= 12 && idade < 18) || estudante -? !(nota >= 1 && nota <= 5) -? !fimDeSemana && dia != 3 -? idade >= 12 && idade < 18 && !estudante -::: - -## Exercício: Ano bissexto - -::: übung l08-schaltjahr -titel: Ano bissexto -auftrag: Um ano é bissexto se for divisível por 4, mas não por 100. Os anos divisíveis por 400 são bissextos de qualquer forma. -Pede um ano e mostra **Ano bissexto** ou **Não é ano bissexto**. -test: 2024 -> Ano bissexto -test: 2023 -> Não é ano bissexto -test: 1900 -> Não é ano bissexto -test: 2000 -> Ano bissexto -braucht: verzweigung -tipp: Confirma primeiro a regra com exemplos: 2024 é bissexto, 1900 não é, 2000 é. “Divisível” quer dizer que o resto é 0. -tipp: Divisível por 4 é `ano % 4 == 0`, não divisível por 100 é `ano % 100 != 0`. Junta estas duas com `&&`, põe parênteses à volta e acrescenta o caso do 400 com `||`. -tipp: Quase a solução: uma única decisão com a condição `(ano % 4 == 0 && ano % 100 != 0) || ano % 400 == 0`. No ramo true fica `output: "Ano bissexto"`, no ramo false `output: "Não é ano bissexto"`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Agora em Java - -O bilhete jovem do início fica assim em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Quantos anos tens?"); -int idade = scanner.nextInt(); -if (idade >= 12 && idade < 18) { - System.out.println("Bilhete jovem"); -} else { - System.out.println("Sem bilhete jovem"); -} -``` - -Os sinais `&&`, `||` e `!` são exatamente os que já usas no estrutograma. Vêm do Java. Como sempre, a condição inteira fica entre os parênteses curvos depois de `if`. - -::: übung l09-java-schule -titel: Escola ou tempo livre -code: java -auftrag: Escreve um programa em Java que pergunta a hora (0 a 23). Das 8 até às 15, inclusive, mostra **Escola**, senão **Tempo livre**. -test: 8 -> Escola -test: 15 -> Escola -test: 16 -> Tempo livre -test: 7 -> Tempo livre -braucht: verzweigung -tipp: A hora tem de cumprir duas condições ao mesmo tempo. Que operador quer dizer “e”? -tipp: `if (hora >= 8 && hora <= 15) { … } else { … }` -lösung: l09-java-schule-loesung.visustruct -::: - -## Preenche os espaços: condições - -::: lückentext -titel: Nota e fim de semana -auftrag: Preenche os operadores e as palavras que faltam. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que nota?"); -int nota = scanner.nextInt(); -if (nota >= 1 [[&&]] nota [[<=]] 5) { - System.out.println("nota válida"); -} else { - System.out.println("nota inválida"); -} -System.out.println("Que dia (1 a 7)?"); -int dia = scanner.nextInt(); -[[boolean]] fimDeSemana = dia == 6 [[||]] dia == 7; -if ([[!]]fimDeSemana) { - System.out.println("dia de escola"); -} -``` -erklärung: Uma nota é válida quando **as duas** condições são verdadeiras, por isso `&&`. É fim de semana quando **uma** das duas é verdadeira, por isso `||`. O resultado cabe numa variável `boolean`, e `!` inverte-o. -::: - -## Encontra o erro: sem escola - -::: übung l09-java-fehler -titel: Corrigir o dia sem escola -code: java -auftrag: O programa deve mostrar **sem escola** para o dia 6 (sábado) e o dia 7 (domingo), e **escola** nos outros dias. Mas mostra sempre **escola**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l09-java-fehler-start.visustruct -test: 6 -> sem escola -test: 7 -> sem escola -test: 3 -> escola -test: 1 -> escola -braucht: verzweigung -tipp: `dia` pode ser 6 **e** 7 ao mesmo tempo? -tipp: Basta que uma das duas condições seja verdadeira: `dia == 6 || dia == 7`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l09-java-laeuft-nicht -titel: Corrigir o fim de semana -code: java -auftrag: O dia 6 é sábado, o dia 7 é domingo. Hoje é o dia 7, por isso deve aparecer **sem escola**. Mas o programa não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> sem escola -braucht: verzweigung -tipp: A condição tem duas partes. Compara o nome da variável nas duas partes. -tipp: Também na segunda parte a variável chama-se `dia` com d minúsculo: `if (dia == 6 || dia == 7) {` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar quando é que uma combinação com && é verdadeira. → E: && -- Consigo explicar quando é que uma combinação com || é verdadeira. → Ou: || -- Consigo inverter uma condição com !. → Não e parênteses -- Consigo verificar se um valor está num intervalo como de 1 a 5. → Verificar intervalos de valores -- Consigo ler a tabela de verdade de &&, || e !. → A tabela de verdade -- Consigo guardar o resultado de uma condição numa variável boolean. → Valores lógicos em variáveis -- Consigo avaliar condições compostas com segurança (pelo menos 8 de 10 certas no jogo). → Jogo: Verdadeiro ou falso? -- Consigo ligar condições em Java com && e ||. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quando é que `a && b` é verdadeiro? -→ E: && -- [ ] Se a ou b for verdadeiro -- [x] Só se a e b forem ambos verdadeiros -- [ ] Se exatamente um deles for verdadeiro -> Com && as duas partes têm de estar certas. Se uma for falsa, o todo é falso. - -? A variável dia tem o valor 6. Quanto dá `dia == 6 || dia == 7`? -→ Ou: || -- [x] true -- [ ] false -> Com || basta que um dos lados seja verdadeiro. - -? Que condição verifica se numero está entre 10 e 20 (ambos incluídos)? -→ Verificar intervalos de valores -- [ ] 10 <= numero <= 20 -- [ ] numero >= 10 || numero <= 20 -- [x] numero >= 10 && numero <= 20 -> Com || todos os números seriam permitidos, porque qualquer número é maior do que 10 ou menor do que 20. - -? Quanto dá `!(5 > 3)`? -→ Não e parênteses -- [ ] true -- [x] false -> 5 > 3 é verdadeiro, e o ! inverte isso. - -? Como escreves em Java “idade é pelo menos 12 e menor do que 18”? -→ Agora em Java -- [ ] if (12 <= idade < 18) -- [x] if (idade >= 12 && idade < 18) -- [ ] if (idade >= 12 || idade < 18) -> Cada comparação fica sozinha, ligada com `&&`. -::: diff --git a/web/lessons/pt/10-fallauswahl.md b/web/lessons/pt/10-fallauswahl.md deleted file mode 100644 index 7a16b1dd..00000000 --- a/web/lessons/pt/10-fallauswahl.md +++ /dev/null @@ -1,330 +0,0 @@ ---- -titel: Seleção -bereich: Fundamentos -kurz: Executar um de vários casos, consoante o valor de uma variável. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- inserir uma seleção, acrescentar casos e alterar os nomes dos casos -- explicar quando é executado o caso default -- decidir se serve melhor uma seleção ou uma decisão -- escrever uma seleção múltipla em Java com switch -::: - -## Muitas possibilidades, uma variável - -Uma máquina de bebidas tem três botões: 1 para água, 2 para sumo, 3 para chocolate quente. Com decisões precisas de três blocos encaixados uns nos outros. Isso fica depressa largo e confuso. - -::: diagramm l09-verschachtelt.visustruct -A máquina com três decisões encaixadas. Cada decisão volta a verificar a mesma variável escolha. -::: - -Sempre que comparas **uma variável** com **vários valores fixos**, há uma forma mais simples: a **seleção**. - -## Como se constrói uma seleção - -Em cima, no bloco, está a variável que decide. Por baixo há uma coluna própria para cada caso. Por cima de cada coluna está o valor a que ela pertence. - -::: diagramm l09-automat.visustruct -A mesma máquina como seleção. Se escolha tiver o valor 2, só é executada a coluna 2. -::: - -- A simulação procura o caso cujo nome corresponde exatamente ao valor. -- Se nenhum caso corresponder, é executado o **default**. Este caso fica sempre na ponta da direita. -- Depois, o programa continua por baixo da seleção. Só é executado **um** caso. - -> Uma seleção nova tem os casos 1, 2 e default. Clica num caso para alterar o seu **Nome do caso** no Inspetor de objetos. Com **Adicionar caso** ganhas uma nova coluna antes do default. - -## Casos com texto - -Os casos não têm de ser números. Também podes escolher pelo conteúdo de uma variável `String`. Então escreves o texto entre aspas como nome do caso, por exemplo `"vermelho"`. Tal como no código, isto mostra que é um texto. - -::: diagramm l09-ampel.visustruct -Um semáforo: consoante a cor introduzida, aparece uma mensagem diferente. Na simulação, experimenta também uma cor que não existe. -::: - -Maiúsculas e minúsculas contam: `Vermelho` não corresponde ao caso `"vermelho"`. - -## Exercício: Nota por extenso - -::: übung l09-note -titel: Nota por extenso -auftrag: Pede uma nota como número inteiro e mostra-a por extenso: 1 muito insuficiente, 2 insuficiente, 3 suficiente, 4 bom, 5 muito bom. - Para qualquer outro número deve aparecer **Nota inválida**. -test: 5 -> muito bom -test: 3 -> suficiente -test: 1 -> muito insuficiente -test: 9 -> Nota inválida -braucht: fallauswahl -tipp: Para muitos valores fixos, uma seleção é mais clara do que muitas decisões. Cada nota tem um caso próprio. -tipp: Usa “Adicionar caso” até teres os casos 1 a 5 e default. Em cima, na seleção, escreves `nota`. -lösung: l09-note-loesung.visustruct -::: - -## Exercício: Calculadora - -::: übung l09-rechner -titel: Calculadora -auftrag: Pede, um a seguir ao outro, um número inteiro `a`, um operador como `String` e um segundo número inteiro `b`. - Para `+`, `-` e `*` mostras o resultado. Para qualquer outro sinal aparece **Operador desconhecido**. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Operador desconhecido -braucht: fallauswahl -tipp: Primeiro lê os três valores. Depois, uma seleção sobre o operador decide o que é calculado. -tipp: Antes da seleção, cria `int resultado = 0`. Em cada caso calculas, por exemplo, `resultado = a + b` e depois mostras `resultado`. Os nomes dos casos são `"+"`, `"-"` e `"*"` entre aspas, porque o operador é uma `String`. -tipp: O esqueleto de cima para baixo: três entradas (`int a`, `String operador`, `int b`), depois `int resultado = 0`, depois uma seleção múltipla sobre `operador`. No caso `"+"` ficam `resultado = a + b` e `output: resultado`, em `"-"` e `"*"` igual. No caso senão fica `output: "Operador desconhecido"`. -lösung: l09-rechner-loesung.visustruct -::: - -## Seleção ou decisão? - -A seleção só verifica se um valor é **exatamente igual** a um nome de caso. Portanto: - -- **Seleção:** uma variável, vários valores fixos. Exemplos: escolher num menu, dia da semana como número, notas. -- **Decisão:** intervalos e condições combinadas. Exemplos: `idade >= 18`, `temperatura < 0 || temperatura > 30`. - -Por isso, para “idade 18 ou mais” continuas a precisar da decisão. Não podes criar um caso próprio para cada idade possível. - -## Agora em Java - -A máquina de bebidas fica assim em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 água, 2 sumo, 3 chocolate quente"); -int escolha = scanner.nextInt(); -switch (escolha) { - case 1 -> { - System.out.println("Aqui está a tua água."); - } - case 2 -> { - System.out.println("Aqui está o teu sumo."); - } - case 3 -> { - System.out.println("Aqui está o teu chocolate quente."); - } - default -> { - System.out.println("Este botão não existe."); - } -} -``` - -- Depois de `switch`, a variável que é comparada fica entre parênteses curvos. -- Cada caso começa com `case`, o valor e uma seta `->`. Depois, entre chavetas, fica o que deve acontecer. -- `default` é o caso “senão”. Corre quando nenhum valor serve. - -::: angeberwissen -A seta `case 1 ->` só existe em Java desde 2020 (Java 14). Antes escrevia-se `case 1:` e era preciso terminar cada caso com `break;`. Quem se esquecia, continuava simplesmente para o caso seguinte. A seta poupou muitos problemas a muitos programadores. -::: - -::: übung l10-java-wuerfel -titel: Dado por extenso -code: java -auftrag: Escreve um programa em Java com `switch` que lê um número. Para 1 mostra **um**, para 2 **dois**, para 3 **três** e senão **não conheço**. -test: 1 -> um -test: 2 -> dois -test: 3 -> três -test: 7 -> não conheço -braucht: fallauswahl -tipp: Precisas de três casos `case` e um caso `default`. -tipp: `switch (numero) { case 1 -> { System.out.println("um"); } … default -> { System.out.println("não conheço"); } }` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Preenche os espaços: switch - -::: lückentext -titel: Meses por extenso -auftrag: Preenche as palavras e os sinais que faltam na seleção múltipla. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que mês (1 a 3)?"); -int mes = scanner.nextInt(); -[[switch]] (mes) { - [[case]] 1 -> { - System.out.println("janeiro"); - } - case 2 [[->]] { - System.out.println("fevereiro"); - } - [[case]] 3 -> { - System.out.println("março"); - } - [[default]] -> { - System.out.println("Não conheço"); - } -} -``` -erklärung: Depois de `switch` vem a variável entre parênteses. Cada caso começa com `case`, o valor e a seta `->`. `default` é executado quando nenhum caso serve. -::: - -## Encontra o erro: calculadora - -::: übung l10-java-fehler -titel: Corrigir a calculadora -code: java -auftrag: A calculadora lê dois números e uma opção: 1 mais, 2 menos, 3 vezes. Com 7, 3 e a opção 2 devia dar **4**, mas dá **-4**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Não existe -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Mais e vezes estão certos. Olha com atenção para o `case 2`. -tipp: No menos, a ordem conta: `a - b` em vez de `b - a`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l10-java-laeuft-nicht -titel: Corrigir a nota -code: java -auftrag: O Ludwig teve nota 2. O programa deve mostrar **bom**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> bom -braucht: fallauswahl -tipp: Que variável verifica a seleção múltipla? Está escrita da mesma forma em cima? -tipp: Entre parênteses depois de `switch` tem de estar `nota` com n minúsculo: `switch (nota) {` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que caso? - -::: spiel frage -titel: Jogo: Que caso? -intro: O Ludwig carrega em botões da máquina de bebidas e de outras seleções. O que acontece? Seis rondas. - -runde: - int escolha = 2; - switch (escolha) { - case 1 -> { System.out.println("água"); } - case 2 -> { System.out.println("sumo"); } - case 3 -> { System.out.println("chocolate quente"); } - default -> { System.out.println("Esse botão não existe."); } - } -frage: O que aparece? -- água -- [x] sumo -- Esse botão não existe. -> escolha é 2, por isso corre exatamente o `case 2`. - -runde: - int escolha = 4; - switch (escolha) { - case 1 -> { System.out.println("água"); } - case 2 -> { System.out.println("sumo"); } - case 3 -> { System.out.println("chocolate quente"); } - default -> { System.out.println("Esse botão não existe."); } - } -frage: O que aparece? -- chocolate quente -- nada -- [x] Esse botão não existe. -> Nenhum caso serve para 4, por isso corre o `default`. - -runde: - String dia = "segunda"; - switch (dia) { - case "Segunda" -> { System.out.println("Matemática"); } - case "Terça" -> { System.out.println("Educação Física"); } - default -> { System.out.println("livre"); } - } -frage: O que aparece? -- Matemática -- Educação Física -- [x] livre -> Nos textos conta cada letra: "segunda" com s minúsculo não é "Segunda". - -runde: - int nota = 0; - switch (nota) { - case 1 -> { System.out.println("muito bom"); } - case 2 -> { System.out.println("bom"); } - case 3 -> { System.out.println("suficiente"); } - } -frage: O que aparece? -- muito bom -- [x] nada -- suficiente -> Nenhum caso serve e não há `default`. Então simplesmente não acontece nada. - -runde: -frage: Queres verificar se `idade >= 18`. O que serve melhor? -- [x] uma decisão -- uma seleção -> Uma seleção só compara com valores fixos. Para "maior ou igual" precisas de uma decisão. - -runde: -frage: Um dado mostra de 1 a 6, e para cada número deve aparecer uma palavra diferente. O que serve melhor? -- uma decisão -- [x] uma seleção -> Uma variável, seis valores fixos: é exatamente para isso que serve a seleção. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo inserir uma seleção, acrescentar casos e alterar os nomes dos casos. → Como se constrói uma seleção -- Consigo explicar quando é executado o caso default. → Como se constrói uma seleção -- Consigo decidir se serve melhor uma seleção ou uma decisão. → Seleção ou decisão? -- Consigo escrever uma seleção múltipla em Java com switch. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? A variável dia tem o valor 5. Existem os casos 1, 2, 3 e default. O que é executado? -→ Como se constrói uma seleção -- [ ] Nada -- [ ] O caso 3 -- [x] O caso default -> Nenhum nome de caso corresponde a 5, por isso aplica-se o default. - -? Quantos casos são executados de cada vez que a seleção é executada? -→ Como se constrói uma seleção -- [x] Exatamente um -- [ ] Todos os casos a partir do que corresponde -- [ ] Todos os casos -> Depois do caso que corresponde, o programa continua logo por baixo da seleção. - -? Para que é que uma seleção NÃO serve? -→ Seleção ou decisão? -- [ ] Escolher a opção 1, 2 ou 3 de um menu -- [x] Verificar se uma temperatura está acima dos 30 graus -- [ ] Tratar uma cor como vermelho, amarelo ou verde -> “Acima dos 30” é um intervalo. Para isso precisas de uma decisão com uma comparação. - -? No semáforo escreves `Vermelho`, e o caso chama-se `"vermelho"`. O que acontece? -→ Como se constrói uma seleção -- [ ] É executado o caso `"vermelho"`. -- [x] É executado o caso default. -- [ ] A simulação indica um erro. -> Maiúsculas e minúsculas contam. Vermelho e vermelho são textos diferentes. - -? Com que nome também encontras a seleção nos manuais? -→ Como se constrói uma seleção -- [ ] Instrução condicional -- [ ] Sequência -- [x] Seleção múltipla -> É escolhido um de muitos casos. Por isso também se chama seleção múltipla. - -? Como se chama em Java o caso “senão” de um switch? -→ Agora em Java -- [ ] else -- [x] default -- [ ] case 0 -::: diff --git a/web/lessons/pt/11-while-schleife.md b/web/lessons/pt/11-while-schleife.md deleted file mode 100644 index e97e1b64..00000000 --- a/web/lessons/pt/11-while-schleife.md +++ /dev/null @@ -1,359 +0,0 @@ ---- -titel: Ciclo while -bereich: Fundamentos -kurz: Repetir passos enquanto uma condição for verdadeira. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que o ciclo while verifica a sua condição antes de cada iteração -- usar uma variável contadora com valor inicial, condição e alteração -- notar quando um ciclo nunca acaba e corrigir o erro -- seguir um ciclo iteração a iteração num teste de secretária -- escrever um ciclo while em Java -::: - -## Repetir enquanto algo se verificar - -Muitas vezes um programa deve fazer uma coisa várias vezes. Em vez de escreveres o mesmo bloco cinco vezes, usas um **ciclo**. - -O **ciclo while** repete o seu interior **enquanto** a sua condição for verdadeira. A condição fica em cima, na cabeça do bloco. O interior fica recuado por baixo, enquadrado por um parêntese à esquerda. - -::: diagramm l10-countdown.visustruct -Uma contagem decrescente: enquanto numero for maior do que 0, numero é mostrado e diminui 1. Depois aparece Partida! -::: - -## Primeiro a condição - -O ciclo while é um **ciclo pré-teste**. Antes de **cada** iteração, a condição é verificada: - -1. Verificar a condição. -2. Se for verdadeira, executar o interior uma vez e recomeçar no 1. -3. Se for falsa, o programa continua por baixo do ciclo. - -::: grafik l10-kopfgesteuert.svg -Depois de cada iteração volta-se à condição. Só quando ela é falsa é que o programa continua por baixo do ciclo. -::: - -Na contagem decrescente, a condição é verificada quatro vezes: com 3, 2 e 1 é verdadeira, com 0 é falsa. - -> Se a condição já for falsa da primeira vez que é verificada, o interior **não é executado nem uma vez**. Começa a contagem com `int numero = 0` e só aparece Partida! - -## Uma variável contadora - -Muitos ciclos vão contando. Para isso precisas de três coisas: - -- **Um valor inicial antes do ciclo**, p. ex. `int i = 1` -- **Uma condição na cabeça**, p. ex. `i <= 5` -- **Uma alteração no interior**, p. ex. `i = i + 1` (abreviado: `i++`) - -Se faltar uma destas partes, o ciclo conta mal ou nem sequer conta. - -::: angeberwissen -Os ciclos de contagem são antiquíssimos! No artigo de 1973, Nassi e Shneiderman mostraram a sua ideia, entre outras coisas, com o **fatorial**: 5! = 1 · 2 · 3 · 4 · 5 = 120. Aqui está a minha versão caprina. Experimenta-a com **Passo** na simulação e vê o `fatorial` a crescer. Mééé, isto vai depressa! -::: - -::: diagramm l10-fakultaet.visustruct -O fatorial com uma variável contadora `i`, reconstruído a partir de um exemplo do artigo de 1973. -::: - -## Exercício: Contar até n - -::: übung l10-bis-n -titel: Contar até n -auftrag: Pede um número inteiro `n` e mostra todos os números de 1 a `n`, cada um na sua linha. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: Precisas de uma variável contadora que começa em 1 e aumenta 1 em cada iteração. -tipp: Antes do ciclo, cria `int i = 1`. A condição é `i <= n`. No interior mostras `i` e depois aumentas `i` em 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Atenção: ciclo infinito - -Se nada no interior do ciclo alterar a condição, ela fica verdadeira para sempre. O ciclo nunca para. A isto chama-se **ciclo infinito**. - -::: diagramm l10-endlos.visustruct -Aqui falta numero = numero - 1. O ciclo mostra 3 uma e outra vez e nunca chega a Partida! -::: - -Pergunta-te em cada ciclo: **O que é que no interior garante que a condição acaba por ficar falsa?** Quando um exercício é verificado, o VisuStruct para depois de um número muito grande de passos e indica um possível ciclo infinito. - -## Exercício: Duplicar até 100 - -::: übung l10-verdoppeln -titel: Duplicar até 100 -auftrag: Pede um número inteiro. Duplica-o até ele ser pelo menos 100. - No fim, mostra **quantas vezes** o duplicaste. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: O ciclo é executado enquanto o número ainda for demasiado pequeno. Vai contando quantas vezes foi executado. -tipp: Precisas de uma segunda variável `int passos = 0`. A condição é `numero < 100`. No interior duplicas o número com `numero = numero * 2` e aumentas `passos` em 1. Se o número já for 100, o ciclo nem chega a ser executado. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Ciclos num teste de secretária - -Com um ciclo, o **teste de secretária** é especialmente valioso. Vês exatamente quantas vezes o ciclo é executado e com que valor para. Cada verificação da condição tem uma linha própria. - -::: diagramm l16-schreibtisch.visustruct -Um ciclo while soma os números 1, 2 e 3. -::: - -| Passo | soma | i | Saída | -|---|---|---|---| -| `int soma = 0` | 0 | – | | -| `int i = 1` | 0 | 1 | | -| `i <= 3` → true | 0 | 1 | | -| `soma = soma + i` | 1 | 1 | | -| `i = i + 1` | 1 | 2 | | -| `i <= 3` → true | 1 | 2 | | -| `soma = soma + i` | 3 | 2 | | -| `i = i + 1` | 3 | 3 | | -| `i <= 3` → true | 3 | 3 | | -| `soma = soma + i` | 6 | 3 | | -| `i = i + 1` | 6 | 4 | | -| `i <= 3` → false | 6 | 4 | | -| `output: soma` | 6 | 4 | 6 | - -Erros frequentes em ciclos são: - -- O ciclo é executado **uma vez a mais ou uma vez a menos**, p. ex. `<` em vez de `<=`. -- Uma variável **não é alterada** no ciclo, ou é alterada no sentido errado. Então o ciclo nunca acaba. -- Uma variável recebe o seu valor inicial **dentro** do ciclo em vez de antes dele e é reposta em cada iteração. - -## Exercício: Corrigir a contagem decrescente - -::: übung l16-countdown -titel: Corrigir a contagem decrescente -auftrag: A contagem decrescente deve contar para trás a partir do valor inicial e mostrar **Já!** no fim. Ou seja, com 3: 3, 2, 1, Já! - O diagrama inicial tem dois erros. Encontra e corrige os dois. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Já! -test: 1 -> 1 | Já! -test: 0 -> Já! -tipp: Há dois erros. Um tem a ver com a contagem, o outro com a saída no fim. -tipp: Inicia a simulação com o valor inicial 3 e observa i em Variáveis. O i diminui? Compara também com atenção a última saída com a tarefa. -lösung: l16-countdown-loesung.visustruct -::: - -## Agora em Java - -A contagem decrescente do início desta lição fica assim em Java: - -```java editor -int numero = 3; -while (numero > 0) { - System.out.println(numero); - numero = numero - 1; -} -System.out.println("Partida!"); -``` - -- Depois de `while`, a condição fica entre parênteses curvos, como em `if`. -- Tudo o que se repete fica entre chavetas. É o corpo do ciclo. -- O corpo está recuado. Assim vês, como no estrutograma, o que pertence ao ciclo. - -::: übung l11-java-gerade -titel: Números pares em Java -code: java -auftrag: Escreve um programa em Java que lê um número inteiro `n` e, com um ciclo while, mostra todos os números pares de 2 até no máximo `n`. Para 7, são 2, 4 e 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: Precisas de uma variável contadora que começa em 2. O ciclo corre enquanto ela não for maior do que `n`. -tipp: `int numero = 2;` antes do ciclo, depois `while (numero <= n) { … }`. No corpo mostras `numero` e aumentas com `numero = numero + 2;`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Preenche os espaços: while - -::: lückentext -titel: Soma de 1 a 10 -auftrag: O programa deve somar os números de 1 a 10. Preenche as palavras e os sinais que faltam. -```java -int soma = 0; -int numero = 1; -[[while]] (numero [[<=]] 10) [[{]] - soma = soma + [[numero]]; - [[numero]] = numero + 1; -} -System.out.println("Soma: " + soma); -``` -erklärung: O ciclo repete-se enquanto `numero` for no máximo 10. Em cada volta, `numero` é somado à soma e depois `numero` aumenta 1. Sem essa linha seria um ciclo infinito. Saída: **Soma: 55** -::: - -## Encontra o erro: poupar - -::: übung l11-java-fehler -titel: Corrigir o plano de poupança -code: java -auftrag: O Ludwig poupa 5 euros por semana. O programa pergunta o objetivo e deve mostrar ao fim de quantas semanas ele o atinge. Para 50 euros são 10 semanas, mas o programa diz **Semanas: 11**. - Percorre o código com Passo a passo e observa `dinheiro` e `semanas`. Corrige o erro e carrega em Verificar. -start: l11-java-fehler-start.visustruct -test: 50 -> Semanas: 10 -test: 12 -> Semanas: 3 -test: 5 -> Semanas: 1 -test: 1 -> Semanas: 1 -braucht: whileSchleife -tipp: Quanto dinheiro tem o Ludwig quando o ciclo se repete pela última vez? Precisa mesmo de poupar mais uma semana? -tipp: Só se poupa enquanto o objetivo **ainda não foi atingido**: `while (dinheiro < objetivo)`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l11-java-laeuft-nicht -titel: Corrigir a contagem -code: java -auftrag: A contagem decrescente deve mostrar **3**, **2**, **1** e depois **Partida!** Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Partida! -braucht: whileSchleife -tipp: O erro está dentro do ciclo. Que variável deve ser mostrada e como se chama de facto? -tipp: Mostra `numero` com n minúsculo: `System.out.println(numero);` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Quantas vezes? - -::: spiel frage -titel: Jogo: Quantas vezes? -intro: O Ludwig escreveu ciclos while. Faz o teste de secretária de cabeça. Seis rondas. - -runde: - int numero = 3; - while (numero > 0) { - System.out.println(numero); - numero = numero - 1; - } - System.out.println("Já!"); -frage: O que aparece? -- [x] 3\n2\n1\nJá! -- 3\n2\n1\n0\nJá! -- Já! -> Quando numero é 0, `numero > 0` é falso, por isso o 0 já não aparece. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: O que aparece? -- [x] nada -- 10 -- de 10 a 4 -> A condição é verificada primeiro. 10 < 5 é falso, o corpo não corre nem uma vez. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: O que acontece? -- Aparecem 1, 2 e 3. -- [x] O ciclo nunca para. -- Não aparece nada. -> i nunca aumenta no corpo. A condição fica sempre verdadeira: um ciclo infinito. - -runde: - int numero = 1; - while (numero < 100) { - numero = numero * 2; - } - System.out.println(numero); -frage: O que aparece? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Só com 128 é que `numero < 100` é falso. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: O que aparece? -- 4 -- 5 -- [x] 6 -> i passa por 0, 2, 4, 6. Com 6, `i < 5` é falso. O 5 é saltado. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: Quantas vezes corre o corpo? -- 3 vezes -- [x] 4 vezes -- 10 vezes -> O corpo corre com i = 1, 4, 7 e 10. Depois i fica 13. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que o ciclo while verifica a sua condição antes de cada iteração. → Primeiro a condição -- Consigo usar uma variável contadora com valor inicial, condição e alteração. → Uma variável contadora -- Consigo notar quando um ciclo nunca acaba e corrigir o erro. → Atenção: ciclo infinito -- Consigo seguir um ciclo iteração a iteração num teste de secretária. → Ciclos num teste de secretária -- Consigo escrever um ciclo while em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer ciclo pré-teste? -→ Primeiro a condição -- [x] A condição é verificada antes de cada iteração. -- [ ] A condição é verificada depois de cada iteração. -- [ ] O ciclo é sempre executado exatamente uma vez. -> A cabeça com a condição está em cima e é avaliada primeiro. - -? `int i = 5`, depois um ciclo while com a condição `i < 3`. Quantas vezes é executado o interior? -→ Primeiro a condição -- [x] Nenhuma -- [ ] Uma vez -- [ ] Três vezes -> Logo da primeira vez que é verificado, 5 < 3 é falso. - -? Porque é que um ciclo com a condição `numero > 0` nunca acaba se o interior só tiver `output: numero`? -→ Atenção: ciclo infinito -- [ ] Porque a saída aumenta o número -- [x] Porque numero nunca muda e a condição fica sempre verdadeira -- [ ] Porque a condição está mal escrita -> No interior tem de acontecer algo que acabe por tornar a condição falsa. - -? Que outros nomes têm os ciclos nos manuais? -→ Primeiro a condição -- [x] Iteração ou repetição -- [ ] Decisões -- [ ] Subprogramas -> Um ciclo repete instruções. Por isso também se chama iteração ou estrutura de repetição. - -? Um ciclo deve contar de 1 a 5, mas só vai até 4. Qual é o erro provável? -→ Uma variável contadora -- [x] A condição diz i < 5 em vez de i <= 5. -- [ ] O ciclo é um ciclo while. -- [ ] A variável i tem o nome errado. - -? Que linha começa corretamente um ciclo while em Java? -→ Agora em Java -- [ ] while numero > 0 { -- [x] while (numero > 0) { -- [ ] while (numero > 0); -> A condição fica entre parênteses curvos, depois `{` abre o corpo. -::: diff --git a/web/lessons/pt/12-do-while-schleife.md b/web/lessons/pt/12-do-while-schleife.md deleted file mode 100644 index acb2226f..00000000 --- a/web/lessons/pt/12-do-while-schleife.md +++ /dev/null @@ -1,279 +0,0 @@ ---- -titel: Ciclo do-while -bereich: Fundamentos -kurz: Primeiro executar, depois verificar: ciclos com pelo menos uma iteração. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que o ciclo do-while só verifica a sua condição depois do interior -- explicar porque é que o interior é executado pelo menos uma vez -- repetir uma entrada até ela ser válida -- escrever um ciclo do-while em Java -::: - -## Primeiro fazer, depois verificar - -O **ciclo do-while** é um **ciclo pós-teste**. A condição fica em baixo, no pé do bloco. É executado assim: - -1. Executar o interior uma vez. -2. Verificar a condição. -3. Se for verdadeira, recomeçar no 1. Se for falsa, o programa continua por baixo do ciclo. - -::: grafik l11-vergleich.svg -À esquerda, o ciclo while verifica primeiro; à direita, o ciclo do-while executa primeiro. -::: - -Em cima, no bloco, está `do`, e a condição fica no pé. Quando clicas no ciclo, a palavra `while` está à frente do campo de texto no Inspetor de objetos: a condição quer dizer “repete enquanto …”. - -> Tal como no ciclo while, a condição diz **quando é que o ciclo se repete**. Enquanto for verdadeira, o ciclo continua. - -::: angeberwissen -O Python não tem ciclo do-while. Quem lá quer “primeiro fazer, depois verificar” escreve `while True:` e sai do ciclo com `break`. Java, C e JavaScript, pelo contrário, têm `do … while` há décadas. -::: - -## Pelo menos uma iteração - -Como a condição só é verificada no fim, o interior **é sempre executado pelo menos uma vez**. Essa é a diferença mais importante em relação ao ciclo while. - -::: diagramm l11-einmal.visustruct -i vale 10 e, portanto, não é menor do que 5. Mesmo assim, 10 é mostrado uma vez, porque a verificação só vem depois. -::: - -Com um ciclo while e a mesma condição `i < 5`, aqui não seria mostrado nada. - -## Uso típico: repetir uma entrada - -O ciclo do-while serve sempre que alguma coisa tem de acontecer primeiro para a poderes verificar. O melhor exemplo é uma entrada: só depois de alguém ter escrito alguma coisa é que sabes se é válida. - -::: diagramm l11-eingabe-pruefen.visustruct -A pergunta repete-se até ser introduzido um número de 1 a 10. Na simulação, experimenta primeiro 0 e depois 12. -::: - -Aqui a condição descreve a entrada **inválida**: `numero < 1 || numero > 10`. Enquanto a entrada for inválida, a pergunta é feita outra vez. - -## Exercício: Nota válida - -::: übung l11-note -titel: Nota válida -auftrag: Pede uma nota. Se não estiver entre 1 e 5, pergunta outra vez. Se for válida, mostra-a, por exemplo **A tua nota: 3**. -test: 3 -> A tua nota: 3 -test: 0, 6, 2 -> A tua nota: 2 -test: 9, 5 -> A tua nota: 5 -test: -1, 1 -> A tua nota: 1 -braucht: doUntilSchleife -tipp: A nota é pedida pelo menos uma vez. Depois, a condição decide se se pergunta outra vez. -tipp: Antes do ciclo, cria `int nota = 0`. A entrada `input: nota "Nota de 1 a 5"` fica dentro do ciclo. A condição é `nota < 1 || nota > 5`. -lösung: l11-note-loesung.visustruct -::: - -## Exercício: Somar até 0 - -::: übung l11-summe -titel: Somar até 0 -auftrag: Pede números até ser introduzido um 0. Depois mostra a soma de todos os números introduzidos. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: Precisas de uma variável para a soma que começa em 0 antes do ciclo. Em cada iteração, o novo número é somado. -tipp: Antes do ciclo, cria `int soma = 0` e `int numero = 0`. No ciclo pedes `numero` e calculas `soma = soma + numero`. Somar o 0 não faz mal. O ciclo continua enquanto `numero != 0`. -tipp: O esqueleto: `int soma = 0` e `int numero = 0`, depois um ciclo do-while com a condição `numero != 0`. Dentro: a entrada de `numero` e `soma = soma + numero`. Por baixo do ciclo: `output: soma`. -lösung: l11-summe-loesung.visustruct -::: - -## Agora em Java - -O ciclo que corre mesmo assim uma vez fica assim em Java: - -```java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -``` - -- O corpo fica entre `do {` e `}`, a condição só vem depois. -- Aqui, depois de `while (…)`, há um **ponto e vírgula**. É diferente do ciclo while. - -::: übung l12-java-verdoppeln -titel: Duplicar pelo menos uma vez -code: java -auftrag: Escreve um programa em Java que lê um número inicial. Um ciclo do-while duplica-o e mostra-o enquanto for menor do que 100. Um número a partir de 100 também é duplicado pelo menos uma vez: 200 passa a 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: No corpo duplicas o número e mostras. A condição fica no fim. -tipp: `do { numero = numero * 2; System.out.println(numero); } while (numero < 100);` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Preenche os espaços: do-while - -::: lückentext -titel: Triplicar -auftrag: O número é triplicado e mostrado enquanto for menor do que 100. Preenche as palavras e os sinais que faltam. -```java -int numero = 1; -[[do]] { - numero = numero * 3; - System.out.println(numero); -} [[while]] (numero [[<]] 100)[[;]] -``` -erklärung: O corpo fica entre `do {` e `}`, a condição só vem depois. No ciclo do-while há um ponto e vírgula depois de `while (…)`. Saída: 3, 9, 27, 81 e 243. -::: - -## Encontra o erro: dividir ao meio - -::: übung l12-java-fehler -titel: Corrigir a divisão ao meio -code: java -auftrag: O programa lê um número inicial e, com um ciclo do-while, divide-o repetidamente por 2 (divisão inteira). Cada resultado é mostrado até chegar a **0**. Para 10 são 5, 2, 1 e 0. Mas falta o 0. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: Que valor tem `numero` depois de o 1 ser mostrado? O ciclo ainda se repete mais uma vez? -tipp: O ciclo deve continuar enquanto o número for maior do que 0: `while (numero > 0);` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l12-java-laeuft-nicht -titel: Corrigir a duplicação -code: java -auftrag: A partir de 1, o programa deve duplicar enquanto o número for menor do que 20: **1**, **2**, **4**, **8**, **16**. Só mostra o 1 e depois para. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: A primeira volta ainda corre. O erro aparece quando a condição em baixo é verificada pela primeira vez. -tipp: Na condição tem de estar `limite` com l minúsculo: `} while (numero < limite);` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Pelo menos uma vez? - -::: spiel frage -titel: Jogo: Pelo menos uma vez? -intro: While ou do-while? O Ludwig mostra-te ciclos. Diz antes o que acontece. Seis rondas. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: O que aparece? -- nada -- [x] 10 -- de 10 a 4 -> Primeiro fazer, depois verificar: o corpo corre uma vez, depois 11 < 5 é falso. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: O que aparece? -- [x] nada -- 10 -- 11 -> No ciclo while a condição vem primeiro. 10 < 5 é falso, por isso não corre nada. - -runde: - int numero = 200; - do { - numero = numero * 2; - } while (numero < 100); - System.out.println(numero); -frage: O que aparece? -- 200 -- [x] 400 -- 800 -> Duplica-se uma vez de certeza. Depois 400 < 100 é falso. - -runde: - int numero = 30; - do { - numero = numero * 2; - System.out.println(numero); - } while (numero < 100); -frage: O que aparece? -- 60 -- [x] 60\n120 -- 60\n120\n240 -> Depois de 60, 60 < 100 é verdadeiro, continua. Depois de 120 acaba. - -runde: -frage: Que linha termina corretamente um ciclo do-while em Java? -- [x] `} while (i < 5);` -- `} while (i < 5)` -- `while (i < 5) {` -> Depois do `while (…)` do ciclo do-while há um ponto e vírgula. - -runde: -frage: Para que se usa tipicamente um ciclo do-while? -- [x] repetir uma entrada até ser válida -- contar de 1 a 10 -- trocar dois valores -> A pergunta é feita pelo menos uma vez. Só depois se sabe se a entrada era válida. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que o ciclo do-while só verifica a sua condição depois do interior. → Primeiro fazer, depois verificar -- Consigo explicar porque é que o interior é executado pelo menos uma vez. → Pelo menos uma iteração -- Consigo repetir uma entrada até ela ser válida. → Uso típico: repetir uma entrada -- Consigo escrever um ciclo do-while em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer ciclo pós-teste? -→ Primeiro fazer, depois verificar -- [ ] A condição é verificada antes de cada iteração. -- [x] A condição é verificada depois de cada iteração. -- [ ] O ciclo não tem condição. -> A condição fica no pé, em baixo, e é avaliada depois do interior. - -? A condição de um ciclo do-while é falsa desde o início. Quantas vezes é executado o interior? -→ Pelo menos uma iteração -- [ ] Nenhuma -- [x] Exatamente uma vez -- [ ] Para sempre -> O interior é executado primeiro. Só depois o ciclo nota que deve parar. - -? Queres pedir um número de 1 a 10. Que condição fica no pé? -→ Uso típico: repetir uma entrada -- [ ] `numero >= 1 && numero <= 10` -- [x] `numero < 1 || numero > 10` -- [ ] `numero == 10` -> O ciclo repete-se enquanto a entrada for inválida, ou seja, menor do que 1 ou maior do que 10. - -? Quando é que um ciclo do-while é melhor do que um ciclo while? -→ Pelo menos uma iteração -- [x] Quando o interior tem de ser executado pelo menos uma vez, p. ex. numa entrada -- [ ] Quando o ciclo talvez nem precise de ser executado -- [ ] Quando queres contar exatamente até 10 - -? O que fica no fim de um ciclo do-while em Java? -→ Agora em Java -- [ ] } while (condicao) { -- [x] } while (condicao); -- [ ] } until (condicao); -> Depois da condição vem um ponto e vírgula. -::: diff --git a/web/lessons/pt/13-for-schleife.md b/web/lessons/pt/13-for-schleife.md deleted file mode 100644 index a7b6c5ae..00000000 --- a/web/lessons/pt/13-for-schleife.md +++ /dev/null @@ -1,337 +0,0 @@ ---- -titel: Ciclo for -bereich: Fundamentos -kurz: Contar um número fixo de iterações com Início, Condição e Passo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- nomear as três partes Início, Condição e Passo do ciclo for -- explicar por que ordem são executadas as três partes -- contar para a frente, para trás e em passos maiores com um ciclo for -- escrever um ciclo for em Java e explicar os seus três campos -::: - -## Contar com um plano fixo - -No ciclo while já foste contando: um valor inicial antes do ciclo, uma condição na cabeça, uma alteração no interior. O **ciclo for** junta estas três partes na cabeça. Assim vês num relance como ele conta. - -::: grafik l12-while-zu-for.svg animiert -O ciclo while torna-se um ciclo for: o início, a condição e o passo juntam-se na cabeça. -::: - -Quando clicas num ciclo for, ele tem três campos à direita: - -- **Início:** `int i = 1` cria a variável contadora. -- **Condição:** `i <= 5` diz até quando se conta. -- **Passo:** `i++` aumenta `i` em 1 depois de cada iteração. - -::: diagramm l12-eins-bis-fuenf.visustruct -Um ciclo for que mostra os números de 1 a 5. -::: - -::: angeberwissen -Porque é que a variável de contagem se chama quase sempre `i`? Na matemática, `i` representa há muito tempo um índice. E em FORTRAN, uma das primeiras linguagens de programação, de 1957, todas as variáveis começadas por I a N eram automaticamente números inteiros. Os programadores ainda não perderam o hábito. -::: - -## Por que ordem? - -As três partes são executadas em momentos diferentes: - -1. O **Início** é executado exatamente uma vez, logo no começo. -2. A **Condição** é verificada antes de cada iteração. Se for falsa, o ciclo termina. -3. Depois do interior vem o **Passo**, depois outra vez a condição. - -::: grafik l12-for-ablauf.svg -O Início só é executado uma vez. Depois, a condição, o interior e o passo repetem-se. -::: - -Portanto, o ciclo for é um ciclo pré-teste, como o ciclo while. Percorre o exemplo na simulação com **Passo** e observa como `i` muda. - -> Usa o ciclo for quando sabes de antemão quantas vezes uma coisa deve acontecer. Se o fim depender de algo que só acontece durante o ciclo, por exemplo uma entrada, é melhor o ciclo while ou do-while. - -## Exemplo: soma de 1 a n - -Um ciclo for é bom para somar números. Antes do ciclo está `int soma = 0`. Em cada iteração soma-se o número atual `i`. - -::: diagramm l12-summe.visustruct -Com n = 4, o ciclo calcula 0 + 1 + 2 + 3 + 4. No fim é mostrado 10. -::: - -## Outros passos - -O passo nem sempre tem de ser `i++`: - -- `i--` conta para trás, por exemplo com Início `int i = 10` e Condição `i >= 1`. -- `i = i + 2` salta de dois em dois, por exemplo 0, 2, 4, 6. - -Garante que o Início, a Condição e o Passo combinam. Com Início `int i = 1`, Condição `i >= 1` e Passo `i++`, a condição nunca fica falsa. - -::: übung l12-countdown -titel: Contagem decrescente -auftrag: Pede um número inteiro `n`. Usa um ciclo for para contar para trás de `n` até 1 e mostra cada número. Depois mostra **Já!**. -test: 3 -> 3 | 2 | 1 | Já! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Já! -test: 1 -> 1 | Já! -braucht: forSchleife -tipp: Desta vez o ciclo for conta para trás. Pensa onde começa, quando para e como conta. -tipp: O Início é `int i = n`, a condição `i >= 1` e o passo `i--`. A saída Já! fica por baixo do ciclo, não dentro dele. -lösung: l12-countdown-loesung.visustruct -::: - -## Exercício: Tabuada - -::: übung l12-einmaleins -titel: Tabuada -auftrag: Pede um número e mostra a sua tabuada de 1 a 10. Cada linha fica assim: **3 x 1 = 3**, **3 x 2 = 6** e assim por diante. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: O ciclo conta de 1 a 10. Em cada iteração mostras uma linha da tabuada. -tipp: No interior, calcula primeiro `int produto = numero * i`. Depois mostra `numero + " x " + i + " = " + produto`. Não te esqueças dos espaços dentro das aspas. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Exercício: Corrigir a soma - -::: übung l16-summe -titel: Corrigir a soma -auftrag: O estrutograma deve somar todos os números de 1 a n. Ou seja, com n = 4 o resultado deve ser 1 + 2 + 3 + 4 = **10**. Mas mostra 6. - Faz um teste de secretária ou usa **Passo** e corrige o erro. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Percorre o ciclo com n = 4 de cabeça ou com **Passo**. Que números são realmente somados? -tipp: Olha para o último valor de i com que o ciclo é executado. Com n = 4, também tem de ser somado i = 4. -lösung: l16-summe-loesung.visustruct -::: - -## Agora em Java - -A soma de 1 até n fica assim em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Até que número?"); -int n = scanner.nextInt(); -int soma = 0; -for (int i = 1; i <= n; i++) { - soma = soma + i; -} -System.out.println(soma); -``` - -Nos parênteses curvos depois de `for` estão os três campos do ciclo for, separados por ponto e vírgula: - -| Campo | Em Java | Significado | -|---|---|---| -| Início | `int i = 1` | `i` começa em 1. | -| Condição | `i <= n` | O ciclo corre enquanto isto for verdade. | -| Passo | `i++` | Depois de cada iteração, `i` aumenta 1. | - -`i++` é a forma curta de `i = i + 1`. Para contar para trás usas `i--`. - -::: übung l13-java-quadrate -titel: Quadrados em Java -code: java -auftrag: Escreve um programa em Java que lê um número inteiro `n` e, com um ciclo for, mostra os quadrados de 1 até `n`, cada um na sua linha. Para 3, são 1, 4 e 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: O ciclo conta `i` de 1 até `n`. O quadrado de `i` é `i * i`. -tipp: `for (int i = 1; i <= n; i++) { … }` e no corpo `System.out.println(i * i);`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Preenche os espaços: for - -::: lückentext -titel: Contar para cima e para baixo -auftrag: O primeiro ciclo soma os números de 1 a 5, o segundo conta de 10 até 0 para trás. Preenche as palavras e os sinais que faltam. -```java -int soma = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - soma = soma + i; -} -System.out.println("Soma: " + soma); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -``` -erklärung: Entre os parênteses ficam o valor inicial, a condição e o passo, separados por ponto e vírgula. `i++` conta para cima, `i--` conta para baixo. Ao contar para trás até 0, a condição é `i >= 0`. -::: - -## Encontra o erro: tabuada - -::: übung l13-java-fehler -titel: Corrigir a tabuada -code: java -auftrag: O programa pergunta que tabuada queres e deve mostrá-la de 1 vez até 10 vezes, para o 7 são 7, 14, … até 70. Mas falta o 70. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: Quantas vezes se repete o ciclo? Conta os valores que `i` toma. -tipp: `i` também deve tomar o valor 10: `i <= 10` em vez de `i < 10`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l13-java-laeuft-nicht -titel: Corrigir a escada -code: java -auftrag: O Ludwig sobe 5 degraus, cada um com 20 cm. O programa deve mostrar **Altura: 100 cm**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Altura: 100 cm -braucht: forSchleife -tipp: A variável `altura` é usada num cálculo, mas onde é criada? E com que altura começa o Ludwig? -tipp: Cria `altura` antes do ciclo: `int altura = 0;` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa esconde **exatamente um erro**, e a bomba está a fazer tiquetaque. Lê o que o programa deve fazer e toca na linha errada antes de o tempo acabar. Se as partes de uma linha tiverem moldura, toca exatamente na parte errada, por exemplo só em `i++`. Oito rondas, 25 segundos cada. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Deve mostrar 1, 2 e 3. -fehler: 2 -besser: System.out.println(i); -> Falta o ponto e vírgula no fim da instrução. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Deve mostrar de 1 a 5. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> Os três campos do ciclo for separam-se com ponto e vírgula, não com vírgulas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Deve contar para trás de 10 até 1. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> Com `i++`, `i` fica sempre maior e a condição `i >= 1` continua verdadeira: um ciclo infinito. Para contar para trás usas `i--`. - -runde: - int soma = 0; - for (int i = 1; i <= 4; i++) { - soma = i; - } - System.out.println(soma); -frage: Deve mostrar a soma 1 + 2 + 3 + 4 = 10. -fehler: 3 -stelle: i -besser: soma = soma + i; -> `soma = i` substitui a soma em cada iteração. No fim fica lá só 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Mééé"); - } -frage: Deve mostrar Mééé exatamente cinco vezes. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> De 0 até 5, inclusive, são seis iterações. Quem começa em 0 usa `<`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Deve mostrar 1, 2 e 3. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> O Java distingue maiúsculas de minúsculas: `System` escreve-se com S maiúsculo. - -runde: - int numero = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(numero + " x " + i + " = " + numero * numero); - } -frage: Deve mostrar a tabuada do 7: 7 x 1 = 7, 7 x 2 = 14 e assim por diante. -fehler: 3 -stelle: numero * numero -besser: System.out.println(numero + " x " + i + " = " + numero * i); -> `numero * numero` dá sempre 49. Tem de ser `numero * i`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Deve mostrar os números pares 2, 4, 6, 8 e 10. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> `i + 2` só calcula e não guarda nada. Para `i` crescer mesmo, é preciso `i = i + 2`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo nomear as três partes Início, Condição e Passo do ciclo for. → Contar com um plano fixo -- Consigo explicar por que ordem são executadas as três partes. → Por que ordem? -- Consigo contar para a frente, para trás e em passos maiores com um ciclo for. → Outros passos -- Consigo escrever um ciclo for em Java e explicar os seus três campos. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas vezes é executado o Início de um ciclo for? -→ Por que ordem? -- [x] Exatamente uma vez -- [ ] Antes de cada iteração -- [ ] Depois de cada iteração -> O Início cria a variável contadora uma vez, no começo. - -? Início `int i = 0`, Condição `i < 3`, Passo `i++`. Que valores tem i dentro do ciclo? -→ Contar com um plano fixo -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> Com i = 3 a condição é falsa, por isso o interior já não é executado. - -? Início `int i = 2`, Condição `i <= 10`, Passo `i = i + 2`. Quantas vezes é executado o interior? -→ Outros passos -- [ ] 4 vezes -- [x] 5 vezes -- [ ] 10 vezes -> i toma os valores 2, 4, 6, 8 e 10. - -? Quando é que um ciclo for serve melhor? -→ Contar com um plano fixo -- [x] Quando sabes de antemão quantas vezes repetir -- [ ] Quando perguntas até uma entrada ser válida -- [ ] Quando só é verificado um único valor - -? O que significa `i++` num ciclo for? -→ Contar com um plano fixo -- [ ] i é duplicado. -- [x] i aumenta 1. -- [ ] i é mostrado. -> `i++` é a forma curta de `i = i + 1`. -::: diff --git a/web/lessons/pt/14-verschachteln.md b/web/lessons/pt/14-verschachteln.md deleted file mode 100644 index 709f069f..00000000 --- a/web/lessons/pt/14-verschachteln.md +++ /dev/null @@ -1,305 +0,0 @@ ---- -titel: Encaixar -bereich: Fundamentos -kurz: Combinar decisões dentro de ciclos e ciclos dentro de ciclos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pôr uma decisão dentro de um ciclo -- pôr um ciclo dentro de outro ciclo -- explicar que o ciclo interior é executado do princípio ao fim em cada iteração do exterior -- escrever ciclos e decisões encaixados em Java -::: - -## Blocos uns dentro dos outros - -Já conheces todos os blocos importantes: sequência, decisão, seleção e três tipos de ciclos. Só juntos é que eles se tornam realmente poderosos. - -Cada ramo e cada corpo do ciclo é, ele próprio, uma sequência. Podes pôr lá qualquer elemento, incluindo mais decisões e ciclos. A isto chama-se **encaixar**. - -> Para isso, basta arrastares um elemento para o marcador ø dentro de um ciclo ou de um ramo. - -## Uma decisão dentro de um ciclo - -O ciclo percorre todos os números, e a decisão decide para cada número se acontece alguma coisa. Assim podes contar, por exemplo, quantos números pares há de 1 a n. - -::: diagramm l13-gerade-zaehlen.visustruct -Em cada iteração, a decisão verifica com i % 2 == 0 se i é par. Só nesse caso quantidade aumenta 1. O ramo false fica vazio. -::: - -Com n = 7 são os números 2, 4 e 6, portanto é mostrado 3. - -## Exercício: Contar entradas pares - -::: übung l13-gerade-eingaben -titel: Contar entradas pares -auftrag: Pede exatamente **cinco** números inteiros, um a seguir ao outro. Depois mostra quantos deles eram pares. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Fazer a mesma coisa cinco vezes significa: ciclo. Em cada iteração é lido e verificado um número. -tipp: Um ciclo for de 1 a 5. No interior vem primeiro a entrada `input: int numero "Introduz um número"`, por baixo uma decisão com `numero % 2 == 0`. No ramo true aumentas `quantidade` em 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## Um ciclo dentro de um ciclo - -Se um ciclo estiver dentro de outro, o **ciclo interior é executado do princípio ao fim em cada iteração do exterior**. Com isto podes construir padrões de linhas e colunas. - -::: diagramm l13-dreieck.visustruct -O ciclo exterior conta as linhas. O interior acrescenta ao texto tantas estrelas quantas o número da linha indica. Com n = 3 obténs um triângulo de 1, 2 e 3 estrelas. -::: - -Com `String texto = ""` cada linha começa vazia. `texto = texto + "*"` acrescenta uma estrela. Só quando a linha está pronta é que é mostrada. - -Programadores experientes dão às variáveis contadoras os nomes curtos `i` e `j`: `i` por fora, `j` por dentro. Fica assim, aqui com uma pequena tabuada: - -::: grafik l13-verschachtelt-tabelle.svg animiert -O ciclo exterior conta as linhas com `i`, o interior conta os números de uma linha com `j`. Em cada nova linha, `j` começa outra vez em 1. -::: - -## Exercício: Retângulo - -::: übung l13-rechteck -titel: Retângulo -auftrag: Pede uma largura e uma altura. Mostra um retângulo feito de `#`: tantas linhas como a altura, e em cada linha tantos `#` como a largura. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: Precisas de dois ciclos, um dentro do outro: um para as linhas e, lá dentro, um para os caracteres de uma linha. -tipp: Constrói-o como o triângulo. A diferença: o ciclo interior conta sempre até `largura`, não até ao número da linha. O ciclo exterior conta até `altura`. -tipp: O esqueleto: entradas `largura` e `altura`. Ciclo for exterior `int linha = 1`, `linha <= altura`, `linha++`. Dentro, primeiro `String texto = ""`, depois o ciclo for interior `int coluna = 1`, `coluna <= largura`, `coluna++` com `texto = texto + "#"`, e por baixo do ciclo interior `output: texto`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Não perder o fio - -Nos diagramas encaixados, o desenho ajuda-te: o que está mais para dentro fica recuado e rodeado pelo seu ciclo ou ramo. - -- Lê de fora para dentro: o que se repete? O que é decidido em cada iteração? -- Dá nomes com significado às variáveis contadoras, como `linha` e `coluna`. Dois ciclos não podem alterar a mesma variável contadora. -- Percorre um exemplo na simulação com **Passo** e observa as variáveis. - -::: angeberwissen -Foi exatamente para isto que o estrutograma foi inventado. Shneiderman perguntou-se: se devemos passar sem GOTO, porque não passar também sem as setas do fluxograma? O resultado: nada de esparguete de setas, e cada bloco fica bem arrumado no seu cercado. -::: - -## Agora em Java - -Contar os números pares fica assim em Java: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Até que número?"); -int n = scanner.nextInt(); -int quantidade = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - quantidade++; - } // fim do if -} // fim do for -System.out.println(quantidade); -``` - -O que no estrutograma está encaixado, em Java está encaixado entre chavetas. Cada nível fica quatro espaços mais para dentro. Se o teu código ficar desarrumado, ajuda o botão **Indentar** em cima na área Código. - -Com muitas chavetas de fecho `}` é fácil perder a noção: que chaveta pertence a que bloco? Um comentário a seguir diz-te, p. ex. `} // fim do if` e `} // fim do for`. Isto também ajuda quando continuares mais tarde. - -`quantidade++` é a forma curta de `quantidade = quantidade + 1`. - -::: übung l14-java-treppe -titel: Escada de estrelas -code: java -auftrag: Escreve um programa em Java que lê o número de degraus e mostra uma escada de asteriscos. Para 3 degraus são `*`, `**` e `***`, cada degrau na sua linha. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: Um ciclo exterior conta os degraus. Em cada degrau, um ciclo interior junta um texto de asteriscos. -tipp: No ciclo exterior: `String linha = "";`, depois `for (int j = 1; j <= i; j++) { linha = linha + "*"; }` e a seguir `System.out.println(linha);`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Preenche os espaços: encaixar - -::: lückentext -titel: Retângulo de estrelas -auftrag: O programa mostra um retângulo de 3 linhas com 4 estrelas cada. Preenche as palavras e os sinais que faltam. -```java -for (int linha = 1; linha <= 3; linha++) { - String texto = [[""]]; - [[for]] (int coluna = 1; coluna [[<=]] 4; [[coluna++ | coluna += 1 | coluna = coluna + 1]]) { - texto = texto [[+]] "*"; - } - System.out.[[println]](texto); -[[}]] -``` -erklärung: O ciclo de dentro constrói uma linha de 4 estrelas, o de fora repete isso três vezes. Cada linha começa com um texto vazio `""`. A última chaveta fecha o ciclo de fora. -::: - -## Encontra o erro: divisíveis por 3 - -::: übung l14-java-fehler -titel: Corrigir a contagem -code: java -auftrag: O programa deve contar quantos números de 1 a `n` são divisíveis por 3 e mostrar o resultado **uma vez**. Para 10 é só **Quantidade: 3**. Mas mostra linhas a mais. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l14-java-fehler-start.visustruct -test: 10 -> Quantidade: 3 -test: 3 -> Quantidade: 1 -test: 2 -> Quantidade: 0 -braucht: forSchleife, verzweigung -tipp: Repara nas chavetas: a saída está dentro do ciclo ou depois dele? -tipp: Passa a linha `System.out.println("Quantidade: " + quantidade);` para depois da chaveta que fecha o ciclo. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l14-java-laeuft-nicht -titel: Corrigir os números pares -code: java -auftrag: O programa deve mostrar os números pares de 1 a 10: **2**, **4**, **6**, **8**, **10**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: O erro está lá dentro, na saída. Como se chama a variável contadora do ciclo? -tipp: A variável contadora é `i` com i minúsculo: `System.out.println(i);` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Dentro e fora - -::: spiel frage -titel: Jogo: Dentro e fora -intro: O Ludwig encaixou blocos uns nos outros. Lê de fora para dentro e diz antes o que acontece. Seis rondas. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: Quantas estrelas aparecem? -- 3 -- 5 -- [x] 6 -> O ciclo interior corre por completo em cada uma das 3 voltas do ciclo exterior: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Que linha aparece em quarto lugar? -- 1 4 -- [x] 2 1 -- 2 2 -> Primeiro j percorre tudo com i = 1: 1 1, 1 2, 1 3. Depois j recomeça em 1, agora com i = 2. - -runde: - int contagem = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - contagem++; - } - } - System.out.println(contagem); -frage: O que aparece? -- [x] 3 -- 10 -- 4 -> A decisão só conta o 3, o 6 e o 9. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("par"); - } else { - System.out.println(i); - } - } -frage: O que aparece? -- 1\n2\n3\n4 -- [x] 1\npar\n3\npar -- par\npar -> Em cada número a decisão volta a decidir: os ímpares aparecem como número, os pares como palavra. - -runde: - for (int i = 1; i <= 3; i++) { - String linha = ""; - for (int j = 1; j <= i; j++) { - linha = linha + "*"; - } - System.out.println(linha); - } -frage: O que aparece? -- ***\n***\n*** -- [x] *\n**\n*** -- ***\n**\n* -> O ciclo interior vai até i. Por isso cada linha fica com mais uma estrela. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: O que acontece? -- Aparece 1 a 3 três vezes. -- Aparece 1 a 3 uma vez. -- [x] O Java indica um erro. -> Dois ciclos um dentro do outro precisam de duas variáveis de contagem diferentes, por exemplo `linha` e `coluna`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pôr uma decisão dentro de um ciclo. → Uma decisão dentro de um ciclo -- Consigo pôr um ciclo dentro de outro ciclo. → Um ciclo dentro de um ciclo -- Consigo explicar que o ciclo interior é executado do princípio ao fim em cada iteração do exterior. → Um ciclo dentro de um ciclo -- Consigo escrever ciclos e decisões encaixados em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O ciclo exterior é executado 3 vezes, o interior 4 vezes. Quantas vezes é executado ao todo o interior do ciclo interior? -→ Um ciclo dentro de um ciclo -- [ ] 7 vezes -- [x] 12 vezes -- [ ] 4 vezes -> Em cada uma das 3 iterações exteriores, o ciclo interior é executado 4 vezes, portanto 3 · 4 = 12. - -? Como sabes se um número é par? -→ Uma decisão dentro de um ciclo -- [ ] `numero / 2 == 0` -- [x] `numero % 2 == 0` -- [ ] `numero == 2` -> O % dá o resto de uma divisão. Nos números pares, o resto da divisão por 2 é zero. - -? Há uma decisão dentro de um ciclo for. Quantas vezes é verificada a condição da decisão? -→ Uma decisão dentro de um ciclo -- [ ] Uma vez -- [x] Uma vez por iteração do ciclo -- [ ] Só se for verdadeira -> A decisão faz parte do interior e é executada em cada iteração. - -? Como reconheces em Java o que pertence a um ciclo? -→ Agora em Java -- [ ] Pela cor -- [x] Pelas chavetas e pela indentação -- [ ] Pelos pontos e vírgulas -::: diff --git a/web/lessons/pt/15-arrays.md b/web/lessons/pt/15-arrays.md deleted file mode 100644 index f46218dd..00000000 --- a/web/lessons/pt/15-arrays.md +++ /dev/null @@ -1,362 +0,0 @@ ---- -titel: Arrays -bereich: Fundamentos -kurz: Guardar muitos valores com um só nome, chegar a eles com o índice e percorrê-los com um ciclo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar um array com valores ou com `new int[5]` -- explicar que o índice começa em 0 e que o último índice é `length - 1` -- percorrer e encher um array com um ciclo for -- calcular a soma e o máximo de um array -- criar um array em Java e percorrê-lo com um ciclo -::: - -## Muitos valores com um só nome - -Imagina que queres guardar os pontos de cinco testes. Com cinco variáveis `pontos1` a `pontos5` isso fica depressa confuso. Um **array** é uma fila de posições que têm todas o mesmo nome e o mesmo tipo de dados. - -Crias um array com uma instrução. Depois do tipo de dados escreves parênteses retos `[]`: - -``` -int[] numeros = {2, 4, 6, 8, 10} -String[] nomes = {"Ada", "Rui", "Inês"} -``` - -Os valores entre chavetas vão logo para as posições. Também podes criar um array vazio com um número fixo de posições: - -``` -int[] pontos = new int[5] -String[] nomes = new String[3] -``` - -Com `new int[5]`, as cinco posições têm zeros no início; com `new String[3]`, textos vazios. - -## O índice começa em 0 - -Cada posição tem um número, o **índice**. Começa-se a contar em **0**. Portanto, com `int[] numeros = {2, 4, 6, 8, 10}`: - -- `numeros[0]` é 2 -- `numeros[1]` é 4 -- `numeros[4]` é 10 - -::: grafik l14-array.svg -Cinco posições com os índices 0 a 4. O índice está por cima da posição, o valor lá dentro. -::: - -**Porquê começar em 0?** Na informática, o índice não é “que posição”, mas sim a **distância ao início**. A primeira posição está logo no início, portanto a 0 passos. A segunda está a 1 passo, a terceira a 2 passos. Conheces isto do elevador: o rés-do-chão é o **0**, e o primeiro andar fica um nível acima. - -Daqui sai uma regra que vale a pena decorar: com 5 posições, o **último índice é 4**, sempre um a menos do que o número de posições. - -::: angeberwissen -Contar a partir de 0 já é bem antigo. A linguagem de programação C, de 1972, já funciona assim: o computador sabe o endereço da primeira posição e, para `numeros[i]`, calcula simplesmente “início mais i posições”. Com i = 0 fica no início, o que poupa uma conta. Java, Python e JavaScript copiaram isto. Já agora, eu também conto os meus chifres a partir de 0: chifre 0 à esquerda, chifre 1 à direita. -::: - -Com o índice lês um valor ou escreves lá um novo: - -``` -output: numeros[0] -numeros[1] = 99 -``` - -> Um array com 5 posições tem os índices 0 a 4. `numeros[5]` não existe. A simulação indica então uma expressão inválida. - -## O comprimento de um array - -`numeros.length` diz-te quantas posições tem um array. Com cinco valores é 5. Por isso, o **último** índice é sempre `numeros.length - 1`: - -``` -output: numeros.length -output: numeros[numeros.length - 1] -``` - -A segunda linha mostra o último valor, seja qual for o comprimento do array. O VisuStruct também percebe `numeros.length()`. - -::: übung l14-erster-letzter -titel: Primeiro e último valor -auftrag: Cria o array `int[] valores = {4, 8, 15, 16, 23, 42}`. Depois mostra em três linhas: o primeiro valor, o último valor e o número de valores. - Para o último valor, usa o comprimento e não escrevas simplesmente o índice 5. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Começa-se a contar em 0, por isso a primeira posição tem o índice 0. E qual é o índice da última posição? -tipp: O primeiro valor é `valores[0]`. O último é `valores[valores.length - 1]`. O número de valores é `valores.length`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Percorrer um array com um ciclo - -Um array mostra a sua força junto com um **ciclo for**. A variável contadora `i` vai de 0 até ao último índice e é usada como índice: - -``` -int i = 0 -i < numeros.length -i++ -``` - -Na primeira iteração `i` é 0, na última é 4. Como a condição é `i < numeros.length`, o ciclo serve para qualquer comprimento. - -::: diagramm l14-array-durchlaufen.visustruct -O ciclo for mostra os cinco valores um a seguir ao outro. Em Variáveis, na simulação, vê o i a contar de 0 a 4. -::: - -É também assim que enches um array vazio com entradas. Como destino da entrada escreves a posição `nomes[i]`: - -::: diagramm l14-namen-eingeben.visustruct -São pedidos três nomes, que são escritos nas posições 0, 1 e 2. -::: - -::: übung l14-rueckwaerts -titel: Mostrar ao contrário -auftrag: Cria um array para 4 números inteiros e lê os 4 números com um ciclo. Depois mostra-os pela ordem **inversa**, cada um na sua linha. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: Precisas de dois ciclos: um para ler da frente para trás e outro para mostrar de trás para a frente. -tipp: Crias o array com `int[] numeros = new int[4]`. A entrada é `input: numeros[i] "Introduz um número"`. No segundo ciclo começas em `int i = numeros.length - 1`, verificas `i >= 0` e contas para trás com `i--`. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr**, nascida em Viena, era uma estrela de cinema famosa de Hollywood nos anos 40. Nos tempos livres gostava de inventar. Em 1942, juntamente com o compositor George Antheil, recebeu uma patente para uma ligação de rádio que o inimigo não conseguisse perturbar: o emissor e o recetor saltam juntos por uma lista de **88 frequências**, tantas como as teclas de um piano. A lista estava perfurada numa fita de papel, como o rolo de um piano automático. Os dois percorrem a mesma lista pela mesma ordem, tal como o teu ciclo percorre um array com `i`. Quem escuta ou perturba só uma frequência quase não apanha nada. - -Este método, o **salto de frequência** (em inglês *frequency hopping*), é usado hoje, por exemplo, no Bluetooth. Ela não o inventou sozinha, já havia ideias anteriores. Mas a sua patente é vista como uma precursora importante, e em 1997 recebeu um prémio por isso. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), atriz e inventora, 1944 (foto promocional: [desconhecido](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), domínio público). -::: - -## Soma e máximo - -Há duas tarefas que aparecem sempre com arrays: - -- **Soma:** Começas com `soma = 0` e somas cada valor no ciclo. -- **Máximo:** Primeiro guardas o valor da posição 0 como o maior até agora. No ciclo verificas cada valor. Se for maior, passa a ser o novo máximo. - -::: diagramm l14-summe-maximum.visustruct -Soma e máximo numa única passagem. O máximo começa com pontos[0] e não com 0, para também estar certo com números negativos. -::: - -::: übung l14-summe-max -titel: Soma e máximo -auftrag: Lê 5 números inteiros para um array. Depois mostra **Soma: …** e na linha seguinte **Máximo: …**. -test: 3, 9, 2, 9, 1 -> Soma: 24 | Máximo: 9 -test: 10, 20, 30, 40, 50 -> Soma: 150 | Máximo: 50 -test: -5, -2, -9, -1, -3 -> Soma: -20 | Máximo: -1 -braucht: forSchleife, verzweigung -tipp: Percorre o array com um ciclo. Soma cada valor à soma e guarda o maior valor visto até agora. -tipp: Calcula a soma e o máximo em variáveis próprias e só os mostra no fim, p. ex. com `output: "Soma: " + soma`. Se o teu máximo começar em 0, o terceiro teste falha. -tipp: O esqueleto: criar o array e lê-lo com um ciclo. Depois `int soma = 0` e `int max = numeros[0]`. Um segundo ciclo calcula `soma = soma + numeros[i]`, e uma decisão `numeros[i] > max` põe `max = numeros[i]` no ramo true. No fim, as duas saídas. -lösung: l14-summe-max-loesung.visustruct -::: - -## Agora em Java - -Percorrer um array fica assim em Java: - -```java editor -int[] numeros = {2, 4, 6, 8, 10}; -for (int i = 0; i < numeros.length; i++) { - System.out.println(numeros[i]); -} -``` - -- `int[]` quer dizer “array de números inteiros”. Os valores ficam entre chavetas. -- Um array vazio com 5 posições cria-se com `new int[5]`. -- `numeros[i]` é a posição com o índice `i`, `numeros.length` é o comprimento. - -Se mostrares o array inteiro, ou seja `System.out.println(numeros);`, o Java não mostra os números. Só vês um texto como `[I@1b6d3586`. É a **referência**, o sítio onde o array está guardado na memória. O conteúdo só aparece com `Arrays.toString(numeros)`, que dá `[2, 4, 6, 8, 10]`. Para isso escreves `import java.util.Arrays;` logo no início. - -::: übung l15-java-noten -titel: Soma das notas em Java -code: java -auftrag: Cria em Java o array `notas` com os valores 2, 3, 1 e 4. Soma todas as notas com um ciclo for e mostra **Soma: 10**. -test: - -> Soma: 10 -braucht: forSchleife -tipp: Cria `int soma = 0;` antes do ciclo. O ciclo vai de 0 até `notas.length - 1`. -tipp: `int[] notas = {2, 3, 1, 4};`, depois `for (int i = 0; i < notas.length; i++) { soma = soma + notas[i]; }` e no fim `System.out.println("Soma: " + soma);`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Preenche os espaços: arrays - -::: lückentext -titel: Procurar o máximo -auftrag: O programa procura o maior valor do array. Preenche as palavras e os sinais que faltam. -```java -[[int]][] valores = {3, 7, 1, 9, 4}; -int max = valores[0]; -[[for]] (int i = 1; i < valores.[[length]]; i++) { - if (valores[i] [[>]] max) { - max = valores[i]; - } -} -System.out.println("Máximo: " + max); -``` -erklärung: `int[]` é um array de números inteiros, `valores.length` é o seu comprimento. `max` começa com o primeiro valor e o ciclo compara todos os outros com ele. Saída: **Máximo: 9** -::: - -## Encontra o erro: temperaturas - -::: übung l15-java-fehler -titel: Corrigir a temperatura mais alta -code: java -auftrag: Em janeiro mediram-se as temperaturas -5, -2, -8 e -3 graus. O programa deve mostrar a mais alta, ou seja, **Temperatura mais alta: -2**. Mas diz 0 graus, embora nunca tenha estado tão quente. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l15-java-fehler-start.visustruct -test: - -> Temperatura mais alta: -2 -braucht: forSchleife, verzweigung -tipp: Com que valor começa `max`? Alguma das temperaturas é maior do que esse valor inicial? -tipp: `max` tem de começar com um valor do array: `int max = temperaturas[0];` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l15-java-laeuft-nicht -titel: Somar os pontos -code: java -auftrag: O array `pontos` contém 4, 7, 2 e 5. O programa deve somá-los todos e mostrar **Soma: 18**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Soma: 18 -braucht: forSchleife -tipp: A área Código sublinha a palavra errada a vermelho. Como se escreve o comprimento de um array? -tipp: `length` escreve-se todo em minúsculas: `i < pontos.length` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa tem **exatamente um erro** com arrays, e a bomba está a fazer tique-taque. Toca na linha errada antes de o tempo acabar. Se as partes de uma linha tiverem moldura, toca exatamente na parte errada. Seis rondas, 30 segundos cada. -zeit: 30 - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - for (int i = 0; i <= numeros.length; i++) { - System.out.println(numeros[i]); - } -frage: Deve mostrar os cinco valores. -fehler: 2 -stelle: i <= numeros.length -besser: for (int i = 0; i < numeros.length; i++) { -> Com `<=`, i vai até 5. A posição `numeros[5]` não existe. - -runde: - String[] nomes = {"Ada", "Ben", "Cem"}; - System.out.println(nomes[1]); -frage: Deve mostrar o primeiro nome, ou seja, Ada. -fehler: 2 -stelle: nomes[1] -besser: System.out.println(nomes[0]); -> Conta-se a partir de 0. `nomes[1]` já é o Ben. - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - System.out.println(numeros[numeros.length]); -frage: Deve mostrar o último valor, ou seja, 10. -fehler: 2 -stelle: numeros[numeros.length] -besser: System.out.println(numeros[numeros.length - 1]); -> O último índice é sempre um a menos do que o comprimento. - -runde: - int[] pontos = {3, 9, 2}; - int soma = 0; - for (int i = 0; i < pontos.length; i++) { - soma = pontos[i]; - } - System.out.println(soma); -frage: Deve mostrar a soma 14. -fehler: 4 -stelle: pontos[i] -besser: soma = soma + pontos[i]; -> Assim cada valor apaga o anterior, e no fim soma só tem 2. - -runde: - int[] pontos = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < pontos.length; i++) { - if (pontos[i] > max) { - max = pontos[i]; - } - } - System.out.println(max); -frage: Deve mostrar o máximo -2. -fehler: 2 -stelle: 0 -besser: int max = pontos[0]; -> Nenhum dos números é maior do que 0, por isso max ficaria em 0. É por isso que o máximo começa com a primeira posição. - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - for (int i = numeros.length; i >= 0; i--) { - System.out.println(numeros[i]); - } -frage: Deve mostrar os valores de trás para a frente, de 10 a 2. -fehler: 2 -stelle: int i = numeros.length -besser: for (int i = numeros.length - 1; i >= 0; i--) { -> De trás para a frente começa-se no último índice, que é `numeros.length - 1`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar um array com valores ou com `new int[5]`. → Muitos valores com um só nome -- Consigo explicar que o índice começa em 0 e que o último índice é `length - 1`. → O índice começa em 0 -- Consigo percorrer e encher um array com um ciclo for. → Percorrer um array com um ciclo -- Consigo calcular a soma e o máximo de um array. → Soma e máximo -- Consigo criar um array em Java e percorrê-lo com um ciclo. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que mostra `output: numeros[2]` com `int[] numeros = {5, 10, 15, 20}`? -→ O índice começa em 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Começa-se a contar em 0: numeros[0] é 5, numeros[1] é 10, numeros[2] é 15. - -? Um array tem 8 posições. Qual é o último índice? -→ O índice começa em 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> O último índice é sempre length - 1. - -? Que condição serve para um ciclo for que percorre todos os valores de `numeros` e começa em `int i = 0`? -→ Percorrer um array com um ciclo -- [x] i < numeros.length -- [ ] i <= numeros.length -- [ ] i > numeros.length -> Com <= o ciclo também leria numeros[length], e essa posição não existe. - -? Com que valor deve começar o máximo antes de o ciclo começar? -→ Soma e máximo -- [ ] Sempre com 0 -- [x] Com o primeiro valor do array -- [ ] Com o comprimento do array -> Se começar com 0, então só com números negativos o resultado seria 0, embora o 0 nem esteja no array. - -? O que mostra `output: numeros.length` com `int[] numeros = {5, 10, 15, 20}`? -→ Muitos valores com um só nome -- [ ] 3 -- [x] 4 -- [ ] 20 -> length é o número de posições, não o último índice nem o último valor. - -? Que linha cria em Java um array com três números? -→ Agora em Java -- [x] int[] numeros = {1, 2, 3}; -- [ ] int numeros = [1, 2, 3]; -- [ ] array numeros = (1, 2, 3); -::: diff --git a/web/lessons/pt/16-zweidimensionale-arrays.md b/web/lessons/pt/16-zweidimensionale-arrays.md deleted file mode 100644 index ba7a73a2..00000000 --- a/web/lessons/pt/16-zweidimensionale-arrays.md +++ /dev/null @@ -1,317 +0,0 @@ ---- -titel: Arrays bidimensionais -bereich: Fundamentos -kurz: Guardar valores em linhas e colunas, como os lugares de um cinema, e percorrê-los com dois ciclos encaixados. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar um array 2D e explicar o que significam os dois números -- ler e escrever uma posição com fila e lugar -- distinguir `lugares.length` de `lugares[0].length` -- explicar por que ordem dois ciclos encaixados visitam as posições -- criar e percorrer um array bidimensional em Java -::: - -## Um cinema como tabela - -Um cinema tem várias **filas**, e cada fila tem vários **lugares**. Para cada lugar queremos saber se o bilhete já foi vendido. É uma pergunta de sim ou não, por isso serve o tipo de dados `boolean`: `true` quer dizer vendido, `false` quer dizer livre. - -Um array normal é uma só fila de posições. Para o cinema precisamos de uma **tabela** com linhas e colunas. Isso é um **array bidimensional**, abreviado array 2D. Para isso escreves dois pares de parênteses retos: - -``` -boolean[][] lugares = new boolean[3][5] -``` - -Isto dá 3 filas com 5 lugares cada, ou seja, 15 posições. No início, todas as posições têm `false`: todos os lugares estão livres. - -::: grafik kino-saal.svg -O primeiro índice é a fila, o segundo é o lugar. Os dois contam-se a partir de 0. -::: - -## Chegar a uma posição - -Para uma posição precisas agora de **dois** índices: primeiro a fila, depois o lugar. - -``` -lugares[1][2] = true -``` - -Agora o lugar 2 da fila 1 está vendido. Ler funciona da mesma maneira: `lugares[1][2]` é agora `true`. - -Também podes perguntar o tamanho: - -- `lugares.length` é o número de **filas**, aqui 3. -- `lugares[0].length` é o número de **lugares** da fila 0, aqui 5. - -::: diagramm kino-verkaufen.visustruct -São vendidos três bilhetes. Depois, o diagrama mostra o tamanho do cinema e se a fila 1, lugar 2, está vendido. Repara em Variáveis, na simulação: lugares é mostrado fila a fila. -::: - -> Tal como num array normal, não existe a posição `lugares[3][0]`, porque as filas se chamam 0, 1 e 2. A simulação indica então uma expressão inválida. - -## Dois ciclos um dentro do outro - -Para olhar para **todos** os lugares, precisas de dois ciclos for, um dentro do outro: - -- O **ciclo exterior** conta a fila: `fila` vai de 0 a 2. -- O **ciclo interior** conta o lugar: `lugar` vai de 0 a 4. - -::: grafik kino-schleifen.svg -Azul é o ciclo exterior, laranja o interior. Os números mostram a ordem pela qual os lugares aparecem. -::: - -Para cada fila, o ciclo interior é executado **do princípio ao fim** uma vez. Só quando `lugar` passou pelos cinco lugares é que o ciclo exterior avança `fila` em um. Depois, `lugar` recomeça em 0. Assim, todos os 3 · 5 = 15 lugares aparecem exatamente uma vez. - -As condições são `fila < lugares.length` e `lugar < lugares[fila].length`. Assim os ciclos servem para um cinema de qualquer tamanho. - -::: diagramm kino-saalplan.visustruct -A planta da sala: para cada fila é montada e mostrada uma linha de X (vendido) e O (livre). Percorre-a com Passo e vê como lugar recomeça sempre do início, enquanto fila só vai contando devagar. -::: - -::: angeberwissen -Cada foto do telemóvel é um array 2D! Uma foto com 4000 · 3000 píxeis tem 3000 linhas com 4000 posições cada, ou seja, 12 milhões de píxeis. Cada posição guarda a cor de um ponto. Quando pões uma foto mais clara, o telemóvel percorre todos os píxeis com dois ciclos encaixados. A minha foto de perfil, já agora, só tem uma posição: castanho. -::: - -## Exercício: Contar lugares livres - -::: übung kino-frei -titel: Contar lugares livres -auftrag: No diagrama inicial já foram vendidos três bilhetes. Usa dois ciclos encaixados para contar quantos lugares ainda estão **livres** e mostra p. ex. `Livres: 12`. -start: kino-frei-start.visustruct -test: - -> Livres: 12 -braucht: forSchleife, verzweigung -tipp: Tal como na planta da sala, percorres todos os lugares com dois ciclos. Em vez de mostrares alguma coisa, contas. -tipp: Antes dos ciclos, cria `int livres = 0`. Um lugar está livre se `lugares[fila][lugar]` for false. A condição para isso é `!lugares[fila][lugar]`. -lösung: kino-frei-loesung.visustruct -::: - -## Exercício: Vender um bilhete - -::: übung kino-karte -titel: Vender um bilhete -auftrag: O diagrama inicial pede uma fila e um lugar. Se o lugar já estiver vendido, mostra `Já vendido`. Caso contrário, marca-o como vendido e mostra p. ex. `Vendido: fila 0, lugar 4`. -start: kino-karte-start.visustruct -test: 1, 2 -> Já vendido -test: 0, 4 -> Vendido: fila 0, lugar 4 -test: 2, 0 -> Vendido: fila 2, lugar 0 -braucht: verzweigung -tipp: Verifica primeiro se o lugar está vendido. Consoante a resposta, acontece uma coisa diferente. -tipp: Aqui não precisas de ciclo. Basta uma decisão com a condição `lugares[fila][lugar]`. No ramo false pões a posição a `true`. -lösung: kino-karte-loesung.visustruct -::: - -## Exercício: Tabela da tabuada - -::: übung kino-einmaleins -titel: Tabela da tabuada -auftrag: Cria `int[][] tabela = new int[10][10]` e enche-a com a tabuada até 10 usando dois ciclos. Depois pede dois números de 1 a 10 e lê o resultado da tabela, p. ex. `3 · 4 = 12`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: Primeiro, dois ciclos encaixados enchem a tabela; depois lês só uma única posição. -tipp: Os índices vão de 0 a 9, mas os números de 1 a 10. Por isso, a posição `tabela[i][j]` tem o valor `(i + 1) * (j + 1)`, e para 3 · 4 lês `tabela[a - 1][b - 1]`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Agora em Java - -A sala de cinema fica assim em Java: - -```java editor -boolean[][] lugares = new boolean[3][5]; -lugares[0][2] = true; -lugares[1][1] = true; -lugares[1][2] = true; -System.out.println("Filas: " + lugares.length); -System.out.println("Lugares por fila: " + lugares[0].length); -System.out.println("Fila 1, lugar 2 vendido? " + lugares[1][2]); -``` - -- Dois pares de parênteses retos `[][]` representam uma tabela: primeiro a fila, depois o lugar. -- `lugares.length` é o número de filas, `lugares[0].length` o número de lugares numa fila. -- Com valores fixos, escreves cada fila nas suas próprias chavetas: `int[][] t = {{1, 2}, {3, 4}};` - -::: übung l16-java-punkte -titel: Somar os pontos -code: java -auftrag: Três jogadores jogaram duas rondas cada um. Cria em Java a tabela `pontos` com as filas {3, 5}, {4, 1} e {2, 2}. Soma todos os pontos com dois ciclos for encaixados e mostra **Soma: 17**. -test: - -> Soma: 17 -braucht: forSchleife -tipp: O ciclo exterior percorre as filas, o interior os lugares de uma fila. -tipp: `int[][] pontos = {{3, 5}, {4, 1}, {2, 2}};` e no ciclo interior `soma = soma + pontos[i][j];`. O ciclo interior vai até `pontos[i].length`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Preenche os espaços: tabelas - -::: lückentext -titel: Somar todos os campos -auftrag: O programa soma todos os números da tabela. Preenche as palavras e os sinais que faltam. -```java -[[int]][][] campo = {{1, 2, 3}, {4, 5, 6}}; -int soma = 0; -for (int l = 0; l < campo.[[length]]; l++) { - for (int c = 0; c < campo[l].[[length]]; c++) { - soma = soma [[+]] campo[l][c]; - } -} -System.out.println("Soma: " + soma); -``` -erklärung: `int[][]` é uma tabela. `campo.length` é o número de linhas (2), `campo[l].length` o número de campos na linha `l` (3). Saída: **Soma: 21** -::: - -## Encontra o erro: pontos - -::: übung l16-java-fehler -titel: Corrigir o jogador 2 -code: java -auftrag: Na tabela `pontos`, cada linha corresponde a um jogador. O programa deve somar os pontos do jogador 2 (linha 1), ou seja, **Jogador 2: 11**. Em vez disso, dá um erro. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l16-java-fehler-start.visustruct -test: - -> Jogador 2: 11 -braucht: forSchleife -tipp: Em `pontos[a][b]`, `a` é a linha e `b` o lugar na linha. Que número corresponde à linha do jogador 2? -tipp: Os índices estão trocados: `pontos[1][j]` em vez de `pontos[j][1]`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l16-java-laeuft-nicht -titel: Ler a pontuação -code: java -auftrag: Na tabela `pontos` cada linha é um jogador e cada coluna uma ronda. O programa deve mostrar os pontos do jogador 2 na ronda 1, ou seja, **Jogador 2, ronda 1: 4**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Jogador 2, ronda 1: 4 -braucht: anweisung -tipp: As posições `[1][0]` estão certas. Olha para o nome da tabela. -tipp: A tabela chama-se `pontos` com p minúsculo: `pontos[1][0]` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que lugar? - -::: spiel frage -titel: Jogo: Que lugar? -intro: O Ludwig vende bilhetes de cinema. Primeiro a fila, depois o lugar, ambos a partir de 0. Sete rondas. - -runde: - boolean[][] lugares = new boolean[3][5]; - System.out.println(lugares.length); -frage: O que aparece? -- [x] 3 -- 5 -- 15 -> `lugares.length` é o número de filas. - -runde: - boolean[][] lugares = new boolean[3][5]; - System.out.println(lugares[0].length); -frage: O que aparece? -- 3 -- [x] 5 -- 15 -> `lugares[0]` é a fila 0. O seu comprimento é o número de lugares nessa fila. - -runde: - int[][] tabela = new int[4][6]; -frage: Quantas posições tem a tabela? -- 10 -- [x] 24 -- 35 -> 4 filas com 6 lugares cada: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: O que aparece? -- 2 -- [x] 3 -- 4 -> A fila 1 é `{3, 4}`, e o lugar 0 dela é o 3. - -runde: - boolean[][] lugares = new boolean[3][5]; - lugares[3][0] = true; -frage: O que acontece? -- O primeiro lugar da fila 3 fica vendido. -- O primeiro lugar da última fila fica vendido. -- [x] Há um erro, porque as filas são 0, 1 e 2. -> Tal como num array normal, o último índice é um a menos do que o comprimento. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int soma = 0; - for (int fila = 0; fila < t.length; fila++) { - for (int lugar = 0; lugar < t[fila].length; lugar++) { - soma = soma + t[fila][lugar]; - } - } - System.out.println(soma); -frage: O que aparece? -- 6 -- 15 -- [x] 21 -> Os dois ciclos passam pelas seis posições: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Dois ciclos percorrem a sala `new boolean[3][5]`. Que posição vem logo a seguir a `lugares[0][4]`? -- `lugares[0][5]` -- [x] `lugares[1][0]` -- `lugares[1][4]` -> A fila 0 terminou. O ciclo exterior avança, e o lugar recomeça em 0. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar um array 2D e explicar o que significam os dois números. → Um cinema como tabela -- Consigo ler e escrever uma posição com fila e lugar. → Chegar a uma posição -- Consigo distinguir `lugares.length` de `lugares[0].length`. → Chegar a uma posição -- Consigo explicar por que ordem dois ciclos encaixados visitam as posições. → Dois ciclos um dentro do outro -- Consigo criar e percorrer um array bidimensional em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas posições tem `boolean[][] lugares = new boolean[4][6]`? -→ Um cinema como tabela -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 filas com 6 lugares cada: 4 · 6 = 24. - -? O que está em todas as posições depois de `new boolean[3][5]`? -→ Um cinema como tabela -- [ ] true -- [x] false -- [ ] Nada, as posições estão vazias -> Um array boolean novo é enchido com false. No cinema isso quer dizer: todos os lugares estão livres. - -? Dois ciclos encaixados percorrem 3 filas com 5 lugares. Quantas vezes é executado o interior do ciclo **interior**? -→ Dois ciclos um dentro do outro -- [ ] 3 vezes -- [ ] 8 vezes -- [x] 15 vezes -> Para cada uma das 3 filas, o ciclo interior é executado 5 vezes: 3 · 5 = 15. - -? Associa o que cada expressão significa no cinema. -→ Chegar a uma posição -= lugares.length -> Número de filas -= lugares[0].length -> Número de lugares da fila 0 -= lugares[2][4] -> Fila 2, lugar 4 -= lugares[1][0] = true -> O lugar 0 da fila 1 está vendido - -? O que dá `lugares.length` para `new boolean[3][5]`? -→ Chegar a uma posição -- [x] 3, o número de filas -- [ ] 5, o número de lugares -- [ ] 15, todos os lugares juntos -::: diff --git a/web/lessons/pt/17-zeichenketten.md b/web/lessons/pt/17-zeichenketten.md deleted file mode 100644 index b0f13c5d..00000000 --- a/web/lessons/pt/17-zeichenketten.md +++ /dev/null @@ -1,528 +0,0 @@ ---- -titel: Strings -bereich: Fundamentos -kurz: Trabalhar com texto: ler caracteres, recortar partes, pesquisar, comparar e dividir. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que os caracteres de uma String também se contam a partir de 0 -- trabalhar com `length()` e `charAt(i)` -- distinguir um `char` de uma String e usar o código de caractere com `(int)` e `(char)` -- recortar uma parte de um texto com `substring` -- usar `indexOf`, `contains`, `toUpperCase` e `equals` -- percorrer um texto caractere a caractere com um ciclo -- dividir um texto num array de Strings com `split` -- ler um texto em Java e compará-lo com `equals` -- usar mais métodos, como `lastIndexOf`, `replace`, `compareTo` e `Integer.parseInt` -- dividir o texto de um ficheiro em linhas com `split("\n")` -- consultar um método na API do Java e ler a sua descrição -::: - -## Um texto é uma fila de caracteres - -Já conheces `String`: `String nome = "Ludwig"`. Na informática, um texto destes chama-se **string** (em inglês, “fio”), porque os caracteres ficam pendurados uns a seguir aos outros como contas num fio. Também se diz **cadeia de caracteres**. Cada caractere tem um lugar com um índice, tal como num array. Também aqui **se começa a contar em 0**: o primeiro caractere está a 0 passos do início. - -::: grafik zk-zeichen.svg -Os dez caracteres de “Passatempo” com os seus índices de 0 a 9. -::: - -Uma String traz consigo **métodos**. Escreves o nome da variável, um ponto e o método: - -- `palavra.length()` dá o número de caracteres, aqui 10. -- `palavra.charAt(0)` dá o caractere com o índice 0, aqui `'P'`. - -> Nas Strings escreves `length()` **com** parênteses, nos arrays `length` **sem**. Infelizmente, o Java é mesmo assim. - -Tal como num array, o último caractere tem o índice `length() - 1`. - -## Um único caractere: char - -Para um único caractere, o Java tem um tipo de dados próprio: **char**, abreviatura do inglês *character* (caractere). Um `char` fica entre aspas **simples**, uma String entre aspas duplas: - -- `'C'` é um `char`: exatamente um caractere. -- `"C"` é uma String: um texto que só tem um caractere. -- Um `char` tem sempre exatamente um caractere. Não existe um `char` vazio como `''`, mas uma String vazia `""` existe. - -`charAt` dá-te um caractere destes a partir de um texto: - -``` -char primeiro = palavra.charAt(0) -char ultimo = palavra.charAt(palavra.length() - 1) -``` - -Dois `char` comparas com `==`: `palavra.charAt(0) == 'P'` é `true`. - -Por trás de cada caractere há um número, o seu **código de caractere**. Que número pertence a que caractere está na tabela ASCII: `'A'` tem o código 65, `'B'` 66, `'C'` 67 e assim por diante até `'Z'` com 90. Com `(int)` obténs o código de um caractere, com `(char)` um código volta a ser um caractere: - -``` -int codigo = (int) 'C' -codigo = codigo + 1 -char seguinte = (char) codigo -``` - -Depois disto, `codigo` vale 68 e `seguinte` é `'D'`. Assim deslocas uma letra no alfabeto, e é exatamente isso que vais precisar mais tarde na cifra de César. - -## Recortar partes - -Com `substring` recortas uma parte do texto: - -- `palavra.substring(0, 5)` dá os caracteres desde o índice 0 **até mesmo antes** do índice 5, ou seja, `"Passa"`. -- `palavra.substring(5)` dá tudo desde o índice 5 até ao fim, ou seja, `"tempo"`. - -O fim **não** está incluído. Dá jeito: `substring(0, 5)` dá exatamente 5 caracteres. - -::: diagramm zk-zeichen.visustruct -Todos os métodos desta lição num exemplo. Percorre-o na simulação e compara cada saída com a imagem. -::: - -## Pesquisar e comparar - -Mais métodos de que precisas muitas vezes: - -| Método | Significado | Exemplo com `"Passatempo"` | -|---|---|---| -| `indexOf("tempo")` | Onde começa o texto? | 5 | -| `indexOf("x")` | não encontrado | -1 | -| `contains("sat")` | O texto aparece? | true | -| `toUpperCase()` | tudo em maiúsculas | PASSATEMPO | -| `toLowerCase()` | tudo em minúsculas | passatempo | -| `equals("Passatempo")` | o mesmo texto? | true | - -> Em Java comparas duas Strings com `equals`, não com `==`. Já dois caracteres (`char`) comparas com `==`, p. ex. `palavra.charAt(i) == 'a'`. - -::: angeberwissen -Experimenta `"Ludwig".equals("ludwig")`. A resposta é false! Para um computador, L e l são caracteres completamente diferentes. Lá dentro, cada caractere é guardado como um número: L é 76 e l é 108. Esta numeração chama-se ASCII e é de 1963. Mééé, portanto, para um computador, Ludwig e ludwig são dois bodes diferentes. Eu sou o do L maiúsculo. -::: - -## Caractere a caractere - -Com um ciclo for percorres um texto caractere a caractere. O índice `i` vai de 0 a `palavra.length() - 1`, tal como num array: - -::: diagramm zk-zaehlen.visustruct -Conta quantas vezes aparece a letra a. Na simulação, experimenta “banana” e observa i e quantidade. -::: - -## Dividir com split - -Muitas vezes há várias informações numa só linha, separadas por um caractere, p. ex. `"Ludwig;bode;7"`. Com `split` cortas a linha nesse caractere. O resultado é um **array de Strings**: - -``` -String[] partes = linha.split(";") -``` - -Depois disso, `partes[0]` é `"Ludwig"`, `partes[1]` é `"bode"` e `partes[2]` é `"7"`. Com `partes.length` descobres quantas partes há. - -::: diagramm zk-split.visustruct -A linha é dividida no ponto e vírgula, e cada parte é mostrada com o seu índice. -::: - -> Os ficheiros CSV, que conheces das folhas de cálculo, são feitos da mesma maneira: uma linha por registo, com os valores separados por vírgula ou ponto e vírgula. - -## Exercício: Iniciais - -::: übung zk-initialen -titel: Iniciais -auftrag: Pede um nome próprio e um apelido e mostra as iniciais. Ou seja, para Ludwig Bode fica `L. B.` -test: Ludwig, Bode -> L. B. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: A inicial é a primeira letra de um nome. Que método dá o caractere numa determinada posição? -tipp: `nome.charAt(0)` dá a primeira letra. A saída fica então `nome.charAt(0) + ". " + apelido.charAt(0) + "."` -lösung: zk-initialen-loesung.visustruct -::: - -## Exercício: Ao contrário - -::: übung zk-rueckwaerts -titel: Ao contrário -auftrag: Pede uma palavra e mostra-a ao contrário. Ludwig passa a `giwduL`. -test: Ludwig -> giwduL -test: Ana -> anA -test: a -> a -braucht: forSchleife -tipp: Percorre a palavra caractere a caractere, mas de trás para a frente, e vai construindo um novo texto. -tipp: Cria `String invertida = ""`. O ciclo começa no último índice `palavra.length() - 1` e conta para trás até 0 com `i--`. Em cada iteração acrescentas `palavra.charAt(i)` no fim. -tipp: O esqueleto: entrada `palavra`, depois `String invertida = ""`. Ciclo for com início `int i = palavra.length() - 1`, condição `i >= 0` e passo `i--`. Dentro, `invertida = invertida + palavra.charAt(i)`. Por baixo do ciclo, `output: invertida`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Exercício: Contar palavras - -::: übung zk-woerter -titel: Contar palavras -auftrag: Pede uma frase e mostra quantas palavras tem, p. ex. `Palavras: 5` para “O Ludwig é um bode”. As palavras estão separadas por exatamente um espaço. -test: O Ludwig é um bode -> Palavras: 5 -test: Olá -> Palavras: 1 -test: Eu gosto de cabras -> Palavras: 4 -braucht: anweisung -tipp: Entre as palavras há sempre um espaço. Que método divide um texto num caractere? -tipp: Divide a frase num array de Strings com `frase.split(" ")`. O seu comprimento é o número de palavras. -tipp: O programa todo tem três blocos: a entrada `frase`, depois `String[] palavras = frase.split(" ")` e no fim `output: "Palavras: " + palavras.length`. -lösung: zk-woerter-loesung.visustruct -::: - -## Agora em Java - -Os métodos desta lição já são Java. Um programa completo com um texto fica assim: - -```java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Como te chamas?"); -String nome = scanner.next(); -System.out.println("Olá " + nome.toUpperCase() + "!"); -System.out.println("O teu nome tem " + nome.length() + " letras."); -``` - -- `scanner.next()` lê uma palavra, ou seja, texto até ao próximo espaço. Para números continuas a usar `scanner.nextInt()`. -- O texto fica entre aspas **duplas** `"Ludwig"`, um único carácter entre aspas **simples** `'L'`. -- Depois de `length()`, `toUpperCase()` e dos outros métodos há sempre parênteses curvos, mesmo quando não há nada lá dentro. -- Comparas dois textos com `equals`. Cada letra conta: `"Ludwig".equals("ludwig")` é false. - -::: übung l17-java-passwort -titel: Verificar a palavra-passe -code: java -auftrag: Escreve em Java um programa que pergunta pela palavra-passe. Se for **Ludwig**, mostra **Bem-vindo!**, senão **Palavra-passe errada**. - Atenção às maiúsculas e minúsculas: `ludwig` está errado. -test: Ludwig -> Bem-vindo! -test: ludwig -> Palavra-passe errada -test: Geißbock -> Palavra-passe errada -braucht: verzweigung -tipp: Lê a palavra-passe com `scanner.next()` para uma variável String. Depois comparas numa decisão. -tipp: A condição é `entrada.equals("Ludwig")`. Em Java, `==` não compara textos de forma fiável. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Preenche os espaços: textos - -::: lückentext -titel: Analisar uma palavra -auftrag: Preenche os métodos e as palavras que faltam. -```java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Uma palavra?"); -[[String]] palavra = scanner.next(); -System.out.println("Comprimento: " + palavra.[[length]]()); -System.out.println("Maiúsculas: " + palavra.[[toUpperCase]]()); -System.out.println("Primeira letra: " + palavra.[[charAt]](0)); -if (palavra.[[equals]]("Ludwig")) { - System.out.println("Olá Ludwig!"); -} -``` -erklärung: `length()` conta os carateres, `toUpperCase()` põe tudo em maiúsculas, `charAt(0)` dá o primeiro caráter. Textos comparas com `equals`, não com `==`. -::: - -## Encontra o erro: iniciais - -::: übung l17-java-fehler -titel: Corrigir as iniciais -code: java -auftrag: O programa lê o nome próprio e o apelido e deve mostrar as iniciais. Para Ludwig Geißbock é **L. G.** Mas mostra **u. e.** - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: Em que índice começa o Java a contar? -tipp: O primeiro caráter tem o índice 0: `nome.charAt(0)` e `apelido.charAt(0)`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l17-java-laeuft-nicht -titel: Contar as letras -code: java -auftrag: O programa deve mostrar a primeira letra e o comprimento de **Ludwig**, ou seja, **L tem 6 letras**. Mas não corre, porque há dois métodos mal escritos. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L tem 6 letras -braucht: anweisung -tipp: Os nomes dos métodos também são camelCase: começam com minúscula e cada palavra seguinte começa com maiúscula. -tipp: O correto é `nome.charAt(0)` com A maiúsculo e `nome.length()` todo em minúsculas. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Ainda mais métodos - -Uma String sabe fazer muito mais. Também vais precisar muitas vezes destes métodos: - -| Método | Significado | Exemplo | Resultado | -|---|---|---|---| -| `lastIndexOf("a")` | Onde está o último a? | `"Passatempo".lastIndexOf("a")` | 4 | -| `isEmpty()` | O texto está vazio? | `"".isEmpty()` | true | -| `equalsIgnoreCase(…)` | o mesmo texto, sem contar maiúsculas e minúsculas? | `"Passatempo".equalsIgnoreCase("PASSATEMPO")` | true | -| `replace(antigo, novo)` | substitui todas as ocorrências | `"Passatempo".replace("tempo", "porte")` | Passaporte | -| `repeat(n)` | o texto n vezes seguidas | `"Mé".repeat(3)` | MéMéMé | -| `concat(…)` | junta texto no fim, como `+` | `"Passa".concat("tempo")` | Passatempo | -| `compareTo(…)` | Qual vem primeiro no alfabeto? | `"Anna".compareTo("Ben")` | -1 | - -`compareTo` devolve um número: **negativo** se o texto vem antes no alfabeto, **0** se os dois são iguais e **positivo** se vem depois. Assim podes ordenar nomes. - -Dois métodos não pertencem a um texto, mas a uma classe. Por isso, à frente fica o nome da classe: - -- `Integer.parseInt("7")` transforma o texto "7" no número 7. Com ele já podes fazer contas. -- `String.valueOf(7)` transforma o número 7 no texto "7". - -::: diagramm zk-mehr.visustruct -Todos os métodos novos num só exemplo. Percorre-o na simulação e compara cada saída com a tabela. -::: - -## Texto de um ficheiro - -Os programas recebem muitas vezes o texto de um **ficheiro**. A Princesa Zora escreveu uma carta ao Ludwig. Ela está pronta no ficheiro `carta.txt`: - -::: datei carta.txt -Querido Ludwig, -a raposa fechou-me na sua torre. -A porta tem um cadeado com uma palavra secreta. -Só encontras a palavra com Java. -Ajuda-me, por favor! -A tua Princesa Zora -::: - -Estas linhas leem o ficheiro inteiro para uma String: - -``` -import java.nio.file.*; - -String carta = Files.readString(Path.of("carta.txt")); -``` - -Como a leitura funciona ao certo, vais aprender mais tarde, numa lição só sobre ficheiros. Por agora basta: depois disto, a carta inteira está em `carta`, com todas as linhas. - -No fim de cada linha há um carácter invisível para a **mudança de linha**. Em Java escreve-se `"\n"`. Por isso, `carta.split("\n")` divide a carta nas suas linhas. - -::: übung l17-java-brief -titel: Onde está a raposa? -code: java -auftrag: O código inicial já lê a carta. Divide-a em linhas e mostra **Linhas:** e o número de linhas. Depois procura a linha onde está **raposa** e mostra **Raposa na linha** e o seu número. A primeira linha tem o número 1. -start: l17-java-brief-start.visustruct -test: - -> Linhas: 6 | Raposa na linha 2 -braucht: forSchleife, verzweigung -tipp: `String[] linhas = carta.split("\n");` divide a carta. Depois, um ciclo for percorre todas as linhas. -tipp: No ciclo verificas `linhas[i].contains("raposa")`. O índice começa em 0, por isso mostras `(i + 1)`. Os parênteses são precisos para o Java fazer a conta em vez de juntar 1 ao texto. -lösung: l17-java-brief-loesung.visustruct -::: - -## A API do Java - -Ninguém sabe todos os métodos de cor, nem os profissionais. Eles consultam a **API do Java**. API significa Application Programming Interface. É a descrição de tudo o que o Java já traz consigo. - -Todos os métodos de String estão aqui: [String na API do Java](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Desce até à tabela **Method Summary**. Cada linha descreve um método: - -| Coluna | Exemplo | Significado | -|---|---|---| -| Modifier and Type | `int` | O que o método devolve | -| Method | `lastIndexOf(String str)` | Nome e parâmetros com o seu tipo de dados | -| Description | Returns the index within this string of the last occurrence … | O que o método faz | - -A API está em inglês. O teu browser pode traduzir a página. Os nomes dos métodos ficam em inglês, porque é assim que os escreves em Java. - -::: ludwig -A página é muito comprida. Com Ctrl+F (no Mac ⌘+F) procuras o nome do método e chegas lá logo. -::: - -Consulta estes métodos. Não estão nesta lição: - -::: quiz -? Consulta `strip()`. O que faz o método? -- [ ] Remove todos os espaços do texto. -- [x] Remove os espaços no início e no fim. -- [ ] Corta o último carácter. -> Como `trim()`. Mas `strip()` também conhece espaços especiais de outras escritas. - -? Consulta `isBlank()`. O que devolve `" ".isBlank()`? -- [x] true -- [ ] false -- [ ] 3 -> isBlank verifica se o texto está vazio ou só tem espaços. Já `" ".isEmpty()` seria false. - -? Consulta `toCharArray()`. Que tipo de dados devolve o método? -- [ ] String -- [ ] String[] -- [x] char[] -> Um array com todos os caracteres do texto, uma posição para cada carácter. - -? Consulta `indexOf` com dois parâmetros. O que devolve `"Passatempo".indexOf("a", 2)`? -- [ ] 1 -- [x] 4 -- [ ] -1 -> A pesquisa só começa no índice 2. O primeiro a a partir daí está no índice 4. - -? Consulta `startsWith`. Que tipo de dados devolve o método? -- [x] boolean -- [ ] int -- [ ] String -> Na coluna “Modifier and Type” está boolean: a resposta é true ou false. -::: - -## Jogo: Salvar a princesa - -Agora ajudas o Ludwig. A raposa fechou a princesa Zora na sua torre, e o portão só abre com uma palavra secreta. - -::: spiel prinzessin -titel: Jogo: Salvar a princesa -intro: No caminho para a torre, o Ludwig passa por **seis estações**. Em cada uma há um pedaço de código Java, e sai um número. Com `alphabet.charAt(numero)` o número transforma-se numa letra, que vai para as **palavras cruzadas**. Resolve-as, e os quadrados amarelos formam a palavra secreta para o portão. - -station: No fosso - String nome = " Zora "; - int numero = nome.trim().length(); -frage: Que número fica em `numero`? -zahl: 4 -feld: 5 1 -tipp: `trim()` corta os espaços no início e no fim. Depois conta os caracteres. -> Depois do `trim()` fica só `"Zora"`, que tem 4 caracteres. - -station: Na floresta da raposa - String lugar = "A raposa"; - int numero = lugar.length(); -frage: Que número fica em `numero`? -zahl: 8 -feld: 4 4 -tipp: `length()` conta todos os caracteres, também o espaço. -> O A, o espaço e r-a-p-o-s-a dão 8 caracteres. - -station: Na rocha das cabras - String texto = "Ludwig o bode"; - int numero = texto.indexOf(" "); -frage: Que número fica em `numero`? -zahl: 6 -feld: 4 6 -tipp: `indexOf` devolve a posição do primeiro espaço. O L tem o índice 0. -> L u d w i g tem os índices 0 a 5, por isso o primeiro espaço está no índice 6. - -station: Na escada da torre - String palavra = "Princesa"; - int numero = palavra.indexOf("a", 5); -frage: Que número fica em `numero`? Procura `indexOf(String str, int fromIndex)` na API do Java. -zahl: 7 -feld: 6 6 -tipp: A pesquisa começa no índice 5. Escreve os índices por baixo das letras. -> P r i n c e s a tem os índices 0 a 7. A partir do índice 5, o primeiro “a” está no 7. - -station: Diante da toca da raposa - int numero = "Zora".compareTo("Zoe"); -frage: Que número fica em `numero`? -zahl: 13 -feld: 1 9 -tipp: `compareTo` procura a primeira posição em que os textos são diferentes e subtrai os códigos dos caracteres. `'r'` é 114, `'e'` é 101. -> “Zo” é igual, depois vem `'r'` contra `'e'`: 114 − 101 = 13. - -station: No portão da torre - int numero = Integer.parseInt("1" + "1") / 5; -frage: Que número fica em `numero`? -zahl: 2 -feld: 6 5 -tipp: Primeiro os textos são juntados, depois passam a número. Com `int`, a divisão deita fora o resto. -> `"1" + "1"` é `"11"`, o `parseInt` faz disso 11, e 11 / 5 dá 2 com números inteiros. - -kreuzwort: As letras azuis já as tens. Preenche os outros quadrados com termos do Java. Clica numa pista para saltar para a palavra. -- → 1 3 BOOLEAN: Tipo de dados para verdadeiro ou falso -- → 4 1 STRING: Tipo de dados para texto -- → 6 5 CHAR: Tipo de dados para um único carácter -- ↓ 1 6 LENGTH: Método que conta os caracteres de uma string -- ↓ 2 1 ELSE: A parte “senão” de uma decisão -- ↓ 3 3 BREAK: Sai imediatamente de um ciclo -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: O Ludwig está diante do portão. A fechadura compara a tua palavra com `equals`. Escreve a palavra secreta. - String palavra = "___"; - if (palavra.equals(palavraSecreta)) { - portao.abrir(); - } -ende: A Zora está livre, e a raposa fugiu a correr. A palavra secreta era `BREAK`: com `break` sai-se de qualquer ciclo, e a Zora sai da torre. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que os caracteres de uma String também se contam a partir de 0. → Um texto é uma fila de caracteres -- Consigo trabalhar com `length()` e `charAt(i)`. → Um texto é uma fila de caracteres -- Consigo distinguir um `char` de uma String e usar o código de caractere com `(int)` e `(char)`. → Um único caractere: char -- Consigo recortar uma parte de um texto com `substring`. → Recortar partes -- Consigo usar `indexOf`, `contains`, `toUpperCase` e `equals`. → Pesquisar e comparar -- Consigo percorrer um texto caractere a caractere com um ciclo. → Caractere a caractere -- Consigo dividir um texto num array de Strings com `split`. → Dividir com split -- Consigo ler um texto em Java e compará-lo com `equals`. → Agora em Java -- Consigo usar mais métodos, como `lastIndexOf`, `replace`, `compareTo` e `Integer.parseInt`. → Ainda mais métodos -- Consigo dividir o texto de um ficheiro em linhas com `split("\n")`. → Texto de um ficheiro -- Consigo consultar um método na API do Java e ler a sua descrição. → A API do Java -::: - -## Teste de conhecimentos - -::: quiz -? Quanto dá `"Ludwig".charAt(1)`? -→ Um texto é uma fila de caracteres -- [ ] L -- [x] u -- [ ] d -> Começa-se a contar em 0: L tem o índice 0, u o índice 1. - -? O que é `'C'`? -→ Um único caractere: char -- [x] um `char`, ou seja, exatamente um caractere -- [ ] uma String -- [ ] uma variável chamada C -> Aspas simples indicam um único caractere, um `char`. `"C"` entre aspas duplas seria uma String. - -? `'A'` tem o código de caractere 65. Quanto dá `(char) 67`? -→ Um único caractere: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 é A, 66 é B, 67 é C. Com `(char)` o código volta a ser um caractere. - -? Quanto dá `"Estrutura".substring(0, 6)`? -→ Recortar partes -- [x] Estrut -- [ ] Estrutu -- [ ] strutu -> Os caracteres com os índices 0 a 5. O fim 6 não está incluído. - -? Uma palavra tem 7 caracteres. Que índice tem o último caractere? -→ Um texto é uma fila de caracteres -- [ ] 7 -- [x] 6 -- [ ] 8 -> Tal como num array: sempre um a menos do que o comprimento. - -? Como comparas duas Strings quanto ao conteúdo em Java? -→ Pesquisar e comparar -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Associa cada método ao que dá para `"Passatempo"`. -→ Pesquisar e comparar -= length() -> 10 -= charAt(0) -> P -= indexOf("tempo") -> 5 -= substring(5) -> tempo - -? Que condição verifica em Java se `entrada` contém o texto Ludwig? -→ Agora em Java -- [ ] entrada = "Ludwig" -- [ ] entrada == 'Ludwig' -- [x] entrada.equals("Ludwig") -> Comparas textos com equals. As aspas simples são só para um único carácter. - -? O que devolve `Integer.parseInt("12") + 1`? -→ Ainda mais métodos -- [ ] "121" -- [x] 13 -- [ ] uma mensagem de erro -> parseInt transforma o texto num número. Depois o Java faz a conta normalmente. -::: diff --git a/web/lessons/pt/18-aufruf-und-aussprung.md b/web/lessons/pt/18-aufruf-und-aussprung.md deleted file mode 100644 index aa65552e..00000000 --- a/web/lessons/pt/18-aufruf-und-aussprung.md +++ /dev/null @@ -1,482 +0,0 @@ ---- -titel: Chamada e interrupção -bereich: Fundamentos -kurz: Dividir tarefas grandes em subprogramas e sair mais cedo dos ciclos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dividir uma tarefa grande em problemas mais pequenos -- inserir uma chamada e construir um subprograma num separador próprio -- passar valores a um subprograma com parâmetros -- devolver um valor de retorno com return e guardá-lo ao chamar -- explicar porque é que um subprograma não consegue alterar um número, mas consegue alterar um array -- explicar o que faz uma Interrupção e porque está numa decisão -- terminar um ciclo com uma Interrupção ou através da sua condição -- reconhecer um método em Java e sair de um ciclo com `break` -::: - -## Dividir tarefas grandes - -Os estrutogramas compridos ficam depressa confusos. Por isso divides uma tarefa grande em **problemas mais pequenos**. Cada problema mais pequeno tem um nome próprio e um estrutograma próprio, um **subprograma**. - -Um quiz, por exemplo, tem três partes: dar as boas-vindas, fazer as perguntas, mostrar o resultado. O programa principal só nomeia estas três partes: - -::: diagramm l15-hauptprogramm.visustruct -O programa principal é feito só de três chamadas. O que acontece em cada parte está num estrutograma próprio. -::: - -Assim vês num relance como o programa está construído. Os pormenores estão noutro sítio. - -## A chamada - -Uma **chamada** é um bloco com linhas verticais duplas à esquerda e à direita. Quer dizer: executa aqui o subprograma com este nome e depois continua. - -- Arrastas a **Chamada** da área Elementos e escreves lá o nome, p. ex. `mostrarBoasVindas()`. -- Os parênteses curvos no fim mostram que é um subprograma. É assim que também se escreve em Java, Python e JavaScript. -- O subprograma em si constróis como estrutograma próprio num novo separador. Como título escreves o mesmo nome. - -::: diagramm l15-unterprogramm.visustruct -O subprograma mostrarBoasVindas() como estrutograma próprio. -::: - -> **Na simulação:** Quando a simulação chega a uma chamada, procura entre os separadores abertos o estrutograma com este título e executa-o. Depois continua no programa principal. Enquanto o subprograma é executado, o bloco da chamada fica destacado. Um subprograma como este, só com um nome como título, partilha as suas variáveis com o programa principal. Daqui a pouco vais aprender como um subprograma tem variáveis próprias. Se não houver nenhum separador que corresponda, a simulação salta a chamada e mostra um aviso. - -::: angeberwissen -Porque é que o teu professor diz sempre “método” em Java e nunca “função” ou “procedimento”? Porque em Java cada subprograma tem de pertencer a uma classe, e os subprogramas de uma classe chamam-se métodos. Mas a diferença continua a existir: se um método devolve um resultado, na verdade é uma função. Se tiver `void` à frente, não devolve nada e na verdade é um procedimento. O Java simplifica e chama métodos aos dois. Eu também: o meu método chama-se Ruminar. -::: - -## Exercício: Acrescentar uma chamada - -::: übung l15-titel -titel: Cumprimento com chamada -auftrag: Imagina que o subprograma `mostrarTitulo()` já existe. Constrói o programa principal: primeiro chama `mostrarTitulo()`. Depois pede um nome e mostra **Olá** e o nome. - Ou seja, quando se escreve Ada, aparece **Olá Ada**. -test: Ada -> Olá Ada -test: Rui -> Olá Rui -braucht: aufruf -tipp: O programa principal tem três partes: a chamada, a entrada e a saída. -tipp: Arrasta uma Chamada da área Elementos lá para cima e escreve `mostrarTitulo()`. Por baixo vêm a entrada `input: String nome "Como te chamas?"` e a saída. -lösung: l15-titel-loesung.visustruct -::: - -## Passar valores: parâmetros - -Muitas vezes um subprograma deve trabalhar com valores que só se conhecem quando é chamado. Nesse caso tem uma **linha de cabeçalho** como título: - -``` -void mostrarSoma(int a, int b) -``` - -- `void` quer dizer: o subprograma não devolve nenhum resultado. -- `mostrarSoma` é o nome. -- Entre os parênteses estão os **parâmetros**: variáveis com tipo de dados que recebem os seus valores quando o subprograma é chamado. Vários separam-se com vírgulas. - -::: diagramm l15-zeige-summe.visustruct -O subprograma com a linha de cabeçalho como título. Faz contas com a e b como se fossem variáveis normais. -::: - -No bloco da chamada escreves os valores entre os parênteses, p. ex. `mostrarSoma(3, 4)`. O primeiro valor vai para `a`, o segundo para `b`. Em vez de números também podes usar variáveis e contas, como `mostrarSoma(x, x + 1)`. - -::: diagramm l15-zeige-summe-haupt.visustruct -O programa principal chama mostrarSoma duas vezes com valores diferentes. Abre os dois diagramas e inicia aqui a simulação. -::: - -> **Variáveis locais:** Os parâmetros e todas as variáveis criadas no subprograma pertencem só ao subprograma. Ele recebe **cópias** dos valores; mais sobre isso em baixo, em passagem por valor. Durante a chamada, a simulação só mostra as variáveis do subprograma. - -## Devolver um resultado: return - -Um subprograma também pode devolver um resultado, o **valor de retorno**. Nesse caso, a linha de cabeçalho tem o tipo de dados do resultado em vez de `void`: - -``` -int quadrado(int x) -``` - -A instrução `return resultado` devolve o valor e termina logo o subprograma. Quando o chamas, guardas o resultado numa variável: depois de `int q = quadrado(5)`, `q` tem o valor 25. - -::: diagramm l15-quadrat.visustruct -O subprograma quadrado calcula e devolve o resultado com return. -::: - -::: diagramm l15-quadrat-haupt.visustruct -O programa principal guarda o valor de retorno em q. Abre os dois diagramas e experimenta a simulação. -::: - -> Um subprograma com valor de retorno tem de terminar com `return`. Se faltar o return, a simulação indica um erro. - -Na **vista de código**, a linha de cabeçalho passa a ser um método Java (`public static int quadrado(int x)`), um `def` em Python e uma `function` em JavaScript. Se os subprogramas estiverem abertos noutros separadores, aparecem no código juntamente com o programa principal. - -## Exercício: Calcular a área - -::: übung l15-flaeche -titel: Calcular a área -auftrag: Constrói dois diagramas. O subprograma `int area(int a, int b)` devolve a área de um retângulo, ou seja, a vezes b. O programa principal pede o comprimento e a largura, chama `area` e mostra **Área:** e o resultado. - Com 3 e 4 aparece **Área: 12**. -test: 3, 4 -> Área: 12 -test: 5, 5 -> Área: 25 -test: 7, 0 -> Área: 0 -braucht: aufruf -tipp: Constróis dois diagramas: um subprograma que só calcula e devolve o resultado, e um programa principal para a entrada e a saída. -tipp: Abre um novo separador com **+** e dá-lhe o título `int area(int a, int b)`. Lá dentro basta uma instrução: `return a * b`. No programa principal escreves `int f = area(comprimento, largura)` no bloco da chamada. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Cópia ou original: passagem por valor e passagem por referência - -O que acontece se um subprograma alterar o seu parâmetro? Depende do **que** é passado. - -**Passagem por valor:** Com `int`, `double`, `boolean`, `char` e `String`, o subprograma recebe uma **cópia** do valor. Se alterar o seu parâmetro, a variável de quem chamou fica como estava. - -**Passagem por referência:** Com um **array**, o subprograma não recebe uma cópia, mas uma referência para o mesmo array. Se alterar uma posição, a posição também fica alterada para quem chamou. A linha de cabeçalho para isso fica assim: - -``` -void limpar(boolean[] lugares) -``` - -::: grafik uebergabe.svg -Em cima, duplicar recebe uma cópia de numero. Em baixo, fila e lugares apontam para o mesmo array. -::: - -Experimenta as duas. Abre os três diagramas e inicia a simulação no programa principal: - -::: diagramm uebergabe-verdopple.visustruct -duplicar só altera a sua cópia x. -::: - -::: diagramm uebergabe-leeren.visustruct -limpar põe todos os lugares a false, por exemplo quando uma sessão é cancelada. -::: - -::: diagramm uebergabe-haupt.visustruct -numero continua a ser 5 depois da chamada. Já os lugares em fila ficam livres depois da chamada. -::: - -> Se quiseres receber um número de volta de um subprograma, usa `return`, p. ex. `numero = duplicar(numero)` com a linha de cabeçalho `int duplicar(int x)`. Um array, pelo contrário, pode ser alterado diretamente pelo subprograma. - -::: angeberwissen -Em rigor, o Java só conhece a passagem por valor. Mas, com um array, não é copiado o array inteiro, só o seu **endereço** na memória. Com o endereço copiado, o subprograma encontra o mesmo array. É como se eu te desse uma cópia da chave do meu cacifo: a chave é copiada, mas o feno no cacifo só existe uma vez. -::: - -## Exercício: Pontos de bónus - -::: übung uebergabe-bonus -titel: Pontos de bónus -auftrag: O programa principal chama `bonus(pontos)` e depois mostra todos os pontos. Constrói o subprograma `void bonus(int[] pontos)`, que aumenta em 1 cada pontuação do array. Em vez de 3, 5 e 8 vês então **4**, **6** e **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: O programa principal já está pronto. Só constróis o subprograma num novo separador. -tipp: Abre um novo separador com **+** e o título `void bonus(int[] pontos)`. Lá dentro, um ciclo for percorre todas as posições e calcula `pontos[i] = pontos[i] + 1`. Não precisas de return, porque o array é alterado diretamente. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## A interrupção - -Às vezes um ciclo deve terminar **mais cedo**, a meio do seu corpo. É para isso que serve a **Interrupção**. Ela sai imediatamente do ciclo em que está. O programa continua no primeiro bloco depois do ciclo. Em Java, Python e JavaScript chama-se `break`. - -Reconheces a Interrupção pela ponta no seu lado esquerdo. Podes escrever um pequeno comentário no bloco, p. ex. `Sair do ciclo`. Ele aparece no código como comentário depois de `break`. - -Uma Interrupção está quase sempre numa **decisão**: só se sai do ciclo se uma condição se verificar. - -Um caso típico é o **ciclo infinito com Interrupção**. O ciclo em si não tem condição. É executado até se chegar à Interrupção: - -::: diagramm l15-endlos-aussprung.visustruct -O ciclo pede sempre um número. Com 0 salta para fora, senão soma o número à soma. -::: - -Sem Interrupção, um ciclo infinito nunca acabaria mesmo. - -## Interrupção na simulação - -A simulação executa a Interrupção como `break`: sai do ciclo mais interior e continua depois dele. Se uma Interrupção estiver fora de qualquer ciclo, o subprograma termina; no programa principal termina o programa todo. O `return` faz o mesmo. - -Muitas vezes podes passar completamente sem Interrupção: escreves a condição de paragem diretamente no ciclo. Normalmente até fica mais claro. - -## Exercício: Somar até 0 - -::: übung l15-bis-null -titel: Somar até 0 -auftrag: Reconstrói o programa “Somar até 0” **sem** Interrupção. Ele pede números inteiros até alguém escrever 0. Depois mostra **Soma:** e a soma de todos os números introduzidos. -test: 3, 4, 0 -> Soma: 7 -test: 0 -> Soma: 0 -test: 10, -2, 5, 0 -> Soma: 13 -tipp: Sem Interrupção, é a própria condição do ciclo que tem de verificar se foi escrito 0. -tipp: Pede o primeiro número antes do ciclo. O ciclo while é executado com a condição `numero != 0`. No corpo somas o número à soma e depois pedes o número seguinte. Na segunda entrada só escreves `input: numero "…"`, porque `numero` já existe. -lösung: l15-bis-null-loesung.visustruct -::: - -## Agora em Java - -Em Java, um subprograma chama-se **método**. O quadrado de cima fica assim: - -```java -public static int quadrado(int x) { - int resultado = x * x; - return resultado; -} -``` - -O programa principal chama o método pelo nome e passa-lhe o valor entre parênteses curvos: - -```java -int q = quadrado(numero); -System.out.println("O quadrado é " + q); -``` - -- No cabeçalho, antes do nome, fica o que o método devolve (`int`). Se não devolver nada, fica `void`. -- Os parâmetros ficam entre parênteses curvos, com o tipo de dados: `(int x)`. Separas vários com vírgulas. -- Na área Código escreves cada método no seu próprio separador, tal como cada subprograma tem o seu próprio estrutograma. -- Em Java, a saída chama-se `break`. Com `while (true)` constróis um ciclo que só termina com `break`. - -::: übung l18-java-kasse -titel: Caixa com break -code: java -auftrag: Escreve uma caixa registadora em Java. Pergunta repetidamente por um preço em euros inteiros. Com 0 sai do ciclo com `break`. Depois mostra quantos artigos foram e quanto custam ao todo. - Com 3, 4 e depois 0 aparece **Artigos: 2** e **Total: 7**. -test: 3, 4, 0 -> Artigos: 2 | Total: 7 -test: 0 -> Artigos: 0 | Total: 0 -test: 10, 2, 5, 0 -> Artigos: 3 | Total: 17 -braucht: endlosschleife, aussprung -tipp: Precisas de duas variáveis que começam em 0 antes do ciclo: uma para a soma e outra para a quantidade de artigos. -tipp: Dentro de `while (true) { … }` lês o preço. Logo a seguir verificas `if (preco == 0) { break; }`. Só depois somas o preço e aumentas os artigos em 1. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Preenche os espaços: métodos e break - -::: lückentext -titel: Método e saída -auftrag: Preenche as palavras que faltam. Em cima está o método que calcula um quadrado. Por baixo está o programa principal, que o chama. Na área Código, cada um fica no seu separador. -```java -public static [[int]] quadrado(int x) { - [[return]] x * x; -} -``` -```java -int numero = 1; -while ([[true]]) { - int q = [[quadrado]](numero); - if (q > 50) { - [[break]]; - } - System.out.println(q); - numero = numero + 1; -} -``` -erklärung: `int` antes do nome do método quer dizer que o método devolve um número inteiro, com `return`. Se um método não devolver nada, aparece `void`. `while (true)` repete-se sem fim até `break` sair do ciclo. Saída: 1, 4, 9, 16, 25, 36 e 49. -::: - -## Encontra o erro: adivinhar - -::: übung l18-java-fehler -titel: Corrigir a contagem de tentativas -code: java -auftrag: Neste jogo de adivinhar, o número secreto é 7. O programa pede um palpite até aparecer o 7 e depois mostra o número de tentativas. Com 3, 5 e 7 foram 3 tentativas, mas o programa diz **Tentativas: 2**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Tentativas: 3 -test: 7 -> Tentativas: 1 -test: 1, 2, 3, 7 -> Tentativas: 4 -braucht: endlosschleife, aussprung -tipp: O que acontece com o palpite certo? Ainda é contado antes de `break` sair do ciclo? -tipp: Primeiro conta, depois verifica: `tentativas = tentativas + 1;` fica antes do `if` com o `break`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Jogo: O que é devolvido? - -::: spiel frage -titel: Jogo: O que é devolvido? -intro: O Ludwig chama métodos e sai de ciclos a meio. Diz antes o que aparece. Sete rondas. - -runde: - public static int quadrado(int x) { - return x * x; - } - - int q = quadrado(5); - System.out.println(q); -frage: O que aparece? -- 5 -- 10 -- [x] 25 -> A chamada põe 5 em x, e o return devolve 5 · 5. - -runde: - public static void mostrarSoma(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - mostrarSoma(x, x + 1); -frage: O que aparece? -- [x] 5 -- 23 -- 3 -> O primeiro valor 2 vai para a, o segundo valor 3 para b. Em Java, `a + b` calcula com números normalmente. - -runde: -frage: O que devolve um método que tem `void` antes do nome? -- um número inteiro -- [x] nada -- sempre 0 -> `void` quer dizer: sem valor de retorno. Com `int` nesse lugar, é devolvido um número inteiro. - -runde: - public static void duplicar(int x) { - x = x * 2; - } - - int numero = 5; - duplicar(numero); - System.out.println(numero); -frage: O que aparece? -- [x] 5 -- 10 -- 0 -> Passagem por valor (call by value): duplicar recebe uma cópia. numero continua a ser 5. - -runde: - public static void limpar(boolean[] lugares) { - for (int i = 0; i < lugares.length; i++) { - lugares[i] = false; - } - } - - boolean[] fila = {true, true}; - limpar(fila); - System.out.println(fila[0]); -frage: O que aparece? -- true -- [x] false -> Num array, fila e lugares apontam para o mesmo array. Quem chama também vê a alteração. - -runde: - int soma = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - soma = soma + i; - } - System.out.println(soma); -frage: O que aparece? -- [x] 6 -- 10 -- 55 -> Com i = 4, o `break` sai logo do ciclo. Só foram somados 1 + 2 + 3. - -runde: - int numero = 1; - while (true) { - numero = numero * 3; - if (numero > 20) { - break; - } - } - System.out.println(numero); -frage: O que aparece? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Só 27 é maior do que 20, e então o ciclo infinito termina com break. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dividir uma tarefa grande em problemas mais pequenos. → Dividir tarefas grandes -- Consigo inserir uma chamada e construir um subprograma num separador próprio. → A chamada -- Consigo passar valores a um subprograma com parâmetros. → Passar valores: parâmetros -- Consigo devolver um valor de retorno com return e guardá-lo ao chamar. → Devolver um resultado: return -- Consigo explicar porque é que um subprograma não consegue alterar um número, mas consegue alterar um array. → Cópia ou original: passagem por valor e passagem por referência -- Consigo explicar o que faz uma Interrupção e porque está numa decisão. → A interrupção -- Consigo terminar um ciclo com uma Interrupção ou através da sua condição. → Interrupção na simulação -- Consigo reconhecer um método em Java e sair de um ciclo com `break`. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que significa um bloco de chamada com o texto `mostrarResultado()`? -→ A chamada -- [ ] O programa termina aqui. -- [x] O subprograma mostrarResultado() é executado aqui, e depois o programa continua. -- [ ] A palavra mostrarResultado() é mostrada aqui. - -? Onde continua o programa depois de uma Interrupção? -→ A interrupção -- [ ] No início do ciclo -- [x] No primeiro bloco depois do ciclo -- [ ] Logo no início do programa -> A Interrupção sai do ciclo em que está. Em Java, Python e JavaScript chama-se break. - -? Porque é que uma Interrupção costuma estar numa decisão? -→ A interrupção -- [x] Para só se sair do ciclo se uma condição se verificar -- [ ] Porque senão uma Interrupção não pode ser guardada -- [ ] Porque uma decisão é ela própria um ciclo -> Sem decisão, sair-se-ia do ciclo logo na primeira iteração. - -? Onde encontra a simulação o subprograma de uma chamada `mostrarResultado()`? -→ A chamada -- [ ] Tem de estar no mesmo estrutograma. -- [x] Num separador aberto com o título mostrarResultado() -- [ ] A simulação não consegue executar subprogramas. -> Constrói cada subprograma num separador próprio e dá-lhe como título o nome da chamada. - -? O subprograma tem a linha de cabeçalho `void mostrarSoma(int a, int b)`. Que chamada serve? -→ Passar valores: parâmetros -- [ ] mostrarSoma() -- [x] mostrarSoma(3, 4) -- [ ] int s = mostrarSoma(3, 4) -> Dois parâmetros precisam de dois valores. E `void` quer dizer que não volta nada que pudesses guardar. - -? Depois de `int q = quadrado(5)`, q tem 25. O que está no fim do subprograma quadrado? -→ Devolver um resultado: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> Com return, o subprograma devolve o valor à chamada. Uma saída só o mostraria. - -? `void duplicar(int x)` calcula `x = x * 2`. O programa principal tem `int numero = 5` e depois a chamada `duplicar(numero)`. Que valor tem numero a seguir? -→ Cópia ou original: passagem por valor e passagem por referência -- [x] 5 -- [ ] 10 -- [ ] 0 -> Passagem por valor: duplicar só alterou uma cópia de numero. - -? `void limpar(boolean[] lugares)` põe todas as posições a false. O que é verdade para o array fila no programa principal depois de `limpar(fila)`? -→ Cópia ou original: passagem por valor e passagem por referência -- [ ] Não mudou, porque o subprograma recebe uma cópia. -- [x] Todas as posições são false, porque lugares e fila são o mesmo array. -- [ ] O array é apagado. -> Passagem por referência: um array é passado como referência. Quem chamou também vê as alterações nas posições. - -? Como se chama um subprograma em Java? -→ Agora em Java -- [ ] Classe -- [x] Método -- [ ] Ciclo -> Em Java, um subprograma é um método, por exemplo o método `main`. Consoante o tipo, também se chama procedimento ou função. - -? Muitos blocos têm um segundo nome nos manuais. Associa cada termo do VisuStruct ao seu outro nome. -→ Agora em Java -= Decisão -> Instrução condicional -= Seleção -> Seleção múltipla -= Ciclo -> Iteração -= Subprograma -> Método -> Os dois nomes querem dizer o mesmo. Se conheces um deles, também percebes o outro. - -? O que fica no cabeçalho de um método Java que não devolve nada? -→ Devolver um resultado: return -- [ ] int -- [x] void -- [ ] return -> void significa “vazio”: o método faz alguma coisa, mas não devolve nenhum valor. -::: diff --git a/web/lessons/pt/19-suchen-und-sortieren.md b/web/lessons/pt/19-suchen-und-sortieren.md deleted file mode 100644 index d99d1d8c..00000000 --- a/web/lessons/pt/19-suchen-und-sortieren.md +++ /dev/null @@ -1,352 +0,0 @@ ---- -titel: Pesquisar e ordenar -bereich: Fundamentos -kurz: Perceber com cartas de jogar como funcionam a pesquisa linear, a pesquisa binária e o bubble sort, e porque é que alguns métodos são muito mais rápidos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar como a pesquisa linear percorre um array -- construir uma pesquisa linear como estrutograma -- explicar porque é que a pesquisa binária só funciona com valores ordenados -- explicar porque é que a pesquisa binária é muito mais rápida com muitos valores -- mostrar uma ronda de bubble sort com cartas de jogar -- percorrer um array em Java e contar resultados -::: - -## Procurar uma carta - -À tua frente está uma fila de cartas de jogar viradas para baixo. Procuras o 9. O que fazes? Viras uma carta a seguir à outra até encontrares o 9 ou teres virado todas as cartas. - -::: grafik ss-lineare-suche.svg -As cartas estão baralhadas. Cada carta que viras é uma comparação: é esta o 9? -::: - -É exatamente assim que funciona a **pesquisa linear**: percorre o array do índice 0 até ao fim e compara cada posição com o valor procurado. Funciona sempre, mesmo que os valores estejam baralhados. - -No array, as nossas cartas são números. Deixamos de fora o valete, a dama e o rei. - -::: diagramm ss-lineare-suche.visustruct -O ciclo é executado até a carta ser encontrada ou já não haver posições. A variável comparacoes conta quantas vezes se comparou. Na simulação, procura o 2, o 27 e o 4. -::: - -> Se a carta não estiver lá, `posicao` fica em **-1**. Não existe o índice -1, por isso aqui -1 quer dizer “não encontrado”. - -## Exercício: Encontrar uma carta - -::: übung ss-karte-suchen -titel: Encontrar uma carta -auftrag: Cria o array `int[] cartas = {8, 4, 11, 4, 6}` e pede uma carta. Mostra a posição da **primeira** carta que corresponde, p. ex. `Posição: 1` para o 4. Se a carta não estiver lá, mostra `Não encontrado`. -test: 4 -> Posição: 1 -test: 6 -> Posição: 4 -test: 8 -> Posição: 0 -test: 9 -> Não encontrado -braucht: whileSchleife, verzweigung -tipp: Percorre o array da frente para trás e compara cada carta com a que procuras. -tipp: Reconstrói o exemplo de cima. Aqui não precisas da variável comparacoes. O ciclo tem de parar assim que `posicao` deixar de ser -1, senão, para o 4, encontras o segundo em vez do primeiro. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Pesquisa binária: ordenado é mais rápido - -Agora as cartas estão à tua frente **ordenadas**, da mais pequena para a maior. Procuras o 27. Já não viras a primeira carta da frente, mas a do **meio**: - -- Se for a que procuras, terminaste. -- Se for mais pequena, a carta procurada só pode estar à direita. A metade esquerda inteira fica de fora. -- Se for maior, fica de fora a metade direita. - -Fazes o mesmo outra vez com o resto. Com cada comparação, o monte fica reduzido a metade. - -::: grafik ss-binaere-suche.svg -Três comparações em vez de catorze: com cada meio, metade das cartas que restam fica de fora. -::: - -No estrutograma, duas variáveis guardam a parte em que ainda se procura: `esquerda` e `direita`. O meio é `(esquerda + direita) / 2`. Como é uma divisão inteira, o resultado é sempre um índice inteiro. - -::: diagramm ss-binaere-suche.visustruct -O mesmo array da pesquisa linear. Na simulação, procura outra vez o 27 e compara a saída Comparações com a pesquisa linear. -::: - -> A pesquisa binária **só funciona com valores ordenados**. Com cartas baralhadas, depois de olhares para o meio não sabes em que metade continuar a procurar. - -::: angeberwissen -Penso num número de 1 a 100. Podes adivinhar, e eu só digo “maior” ou “menor”. Com a pesquisa binária precisas no máximo de **7** tentativas. Para um número até um milhão são no máximo 20, para mil milhões 30. Cada duplicação custa só mais uma tentativa. É assim que as bases de dados pesquisam milhões de registos numa fração de segundo. Eu, já agora, pesquiso de forma linear: como o prado da esquerda para a direita. -::: - -## Quão rápido é? - -A qualidade de um método mede-se pelo número de comparações de que precisa **no pior caso**. Na pesquisa linear, é o caso em que a carta está mesmo lá atrás ou não existe. - -::: grafik ss-vergleiche.svg -Com 100 cartas, a pesquisa linear precisa de até 100 comparações, a pesquisa binária no máximo de 7. -::: - -| Número de cartas | Pesquisa linear | Pesquisa binária | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1000 | 1000 | 10 | -| 1 000 000 | 1 000 000 | 20 | - -A pesquisa linear cresce ao mesmo ritmo que o número de cartas. Na pesquisa binária, o dobro das cartas acrescenta só **uma** comparação. - -## Ordenar com bubble sort - -Para a pesquisa binária, os valores têm de estar ordenados. Uma das formas mais simples de ordenar é o **bubble sort**. Percorres a fila da esquerda para a direita e comparas sempre **dois vizinhos**. Se a carta maior estiver à esquerda, trocas as duas. - -::: grafik ss-bubblesort.svg -Depois de uma ronda, a carta maior está na ponta da direita. -::: - -Depois da primeira ronda, a carta maior está na ponta da direita, como uma bolha de ar que sobe. Daí vem o nome (*bubble* quer dizer bolha). Depois repetes a ronda para as cartas que restam. Com 5 cartas precisas de 4 rondas. - -O estrutograma tem dois ciclos for, um dentro do outro. O exterior conta as rondas, o interior compara os vizinhos `cartas[i]` e `cartas[i + 1]`. Já sabes como trocar: precisas de uma **variável auxiliar**. - -::: diagramm ss-bubblesort.visustruct -O ciclo interior fica mais curto em cada ronda, porque as cartas maiores à direita já estão no sítio certo. Percorre-o na simulação com Passo e observa o array em Variáveis. -::: - -O bubble sort é fácil de perceber, mas lento. Com n cartas precisa de até n · (n − 1) / 2 comparações: com 10 cartas 45, com 100 cartas já 4950 e com 1000 cartas quase meio milhão. Mais tarde talvez conheças métodos mais rápidos, como o quicksort. - -## Exercício: Ordenar por ordem decrescente - -::: übung ss-absteigend -titel: Ordenar por ordem decrescente -auftrag: O diagrama inicial lê 5 cartas e ordena-as com bubble sort por ordem **crescente**, ou seja, da mais pequena para a maior. Altera-o de modo que as cartas sejam mostradas por ordem **decrescente**, a maior primeiro. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Olha para a condição na decisão. É ela que decide quando dois vizinhos são trocados. -tipp: Só precisas de alterar um único caractere. Quando devem ser trocados dois vizinhos, se a carta maior deve ir para a esquerda? -tipp: Na decisão, troca `cartas[i] > cartas[i + 1]` por `cartas[i] < cartas[i + 1]`. Assim os vizinhos trocam quando a carta maior está à direita, e a carta grande vai para a esquerda. -lösung: ss-absteigend-loesung.visustruct -::: - -## Agora em Java - -A pesquisa linear do exercício fica assim em Java: - -```java editor -int[] cartas = {8, 4, 11, 4, 6}; -int procurada = 4; -int posicao = -1; -int i = 0; -while (posicao == -1 && i < cartas.length) { - if (cartas[i] == procurada) { - posicao = i; - } - i = i + 1; -} -System.out.println("Posição: " + posicao); -``` - -- `&&` significa “e”: o ciclo só corre enquanto não se encontrou nada **e** ainda há cartas. -- Se o ramo false estiver vazio, deixas simplesmente de fora `else`. -- A troca no bubble sort também precisa de uma variável auxiliar em Java: `int aux = cartas[i];` - -::: übung l19-java-zaehlen -titel: Contar cartas -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. Escreve em Java um programa que pergunta por uma carta e conta quantas vezes aparece. Mostra **Quantidade:** e o número. - Com 4 aparece **Quantidade: 2**. -test: 4 -> Quantidade: 2 -test: 8 -> Quantidade: 1 -test: 9 -> Quantidade: 0 -braucht: forSchleife, verzweigung -tipp: Ao contrário da pesquisa, não paras no primeiro resultado. Um ciclo for percorre todas as cartas. -tipp: `for (int i = 0; i < cartas.length; i++)` e lá dentro `if (cartas[i] == procurada) { quantidade = quantidade + 1; }`. `quantidade` começa em 0 antes do ciclo. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Preenche os espaços: bubble sort - -::: lückentext -titel: Ordenar cartas -auftrag: O programa ordena as cartas por ordem crescente com o bubble sort. Preenche as palavras e os sinais que faltam. -```java -int[] cartas = {5, 2, 8, 1}; -for (int ronda = 0; ronda < cartas.[[length]] - 1; ronda++) { - for (int i = 0; i < cartas.length - 1; i++) { - if (cartas[i] [[>]] cartas[i + 1]) { - [[int]] aux = cartas[i]; - cartas[i] = cartas[i + 1]; - cartas[i + 1] = [[aux]]; - } - } -} -for (int i = 0; i < cartas.length; i++) { - System.out.println(cartas[i]); -} -``` -erklärung: Se uma carta for maior do que a vizinha da direita, as duas trocam de lugar, com uma variável auxiliar como ao trocar os copos. Depois de `length - 1` rondas está tudo ordenado: 1, 2, 5, 8. -::: - -## Encontra o erro: pesquisa - -::: übung l19-java-fehler -titel: Corrigir a pesquisa linear -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. O programa deve mostrar a **primeira** posição da carta procurada, para o 4 é **Posição: 1**. Mas mostra **Posição: 3**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l19-java-fehler-start.visustruct -test: 4 -> Posição: 1 -test: 6 -> Posição: 4 -test: 9 -> Posição: -1 -test: 8 -> Posição: 0 -braucht: whileSchleife, verzweigung -tipp: O ciclo vai sempre até à última carta. O que acontece quando o 4 aparece uma segunda vez? -tipp: O ciclo deve parar assim que encontrar algo: `while (posicao == -1 && i < cartas.length)`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l19-java-laeuft-nicht -titel: Procurar uma carta -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. O programa procura o 11 e deve mostrar **Posição: 2**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Posição: 2 -braucht: forSchleife, verzweigung -tipp: Compara os nomes na condição com os nomes criados em cima. -tipp: A carta procurada está em `procurada` com p minúsculo: `if (cartas[i] == procurada) {` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Profissional da pesquisa - -::: spiel frage -titel: Jogo: Profissional da pesquisa -intro: O Ludwig procura e ordena cartas. Quão rápido é, e o que sai? Sete rondas. - -runde: - int[] cartas = {8, 4, 11, 4, 6}; - int procurada = 4; - int posicao = -1; - int i = 0; - while (posicao == -1 && i < cartas.length) { - if (cartas[i] == procurada) { - posicao = i; - } - i = i + 1; - } - System.out.println("Posição: " + posicao); -frage: O que aparece? -- [x] Posição: 1 -- Posição: 3 -- Posição: 2 -> A pesquisa para no primeiro 4. Está na posição 1, contando a partir de 0. - -runde: - int[] cartas = {8, 4, 11, 4, 6}; - int procurada = 7; -frage: Com a mesma pesquisa linear: o que fica em posicao? -- 0 -- 5 -- [x] -1 -> O 7 não está lá. posicao fica em -1, que quer dizer "não encontrado". - -runde: -frage: Com que cartas funciona a pesquisa binária? -- com todas -- [x] só com cartas ordenadas -- só com menos de 100 -> Só com as cartas ordenadas é que, depois de olhares para o meio, sabes em que metade continuar a procurar. - -runde: -frage: De quantas comparações precisa no máximo a pesquisa binária com 100 cartas? -- 50 -- [x] 7 -- 100 -> Cada comparação corta o monte a meio. Cortar 7 vezes chega para até 128 cartas, portanto também para 100. - -runde: -frage: Em vez de 1000 cartas agora são 2000. Quantas comparações a mais precisa no máximo a pesquisa binária? -- [x] uma -- o dobro -- 1000 -> A primeira comparação reduz 2000 a 1000. Depois continua como antes. - -runde: - int[] cartas = {5, 3, 8, 1}; -frage: Uma passagem do bubble sort compara sempre dois vizinhos, da esquerda para a direita. Como ficam as cartas depois? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 e 3 trocam, 5 e 8 ficam, 8 e 1 trocam. A carta maior fica na ponta da direita. - -runde: -frage: O bubble sort troca `cartas[i]` e `cartas[i + 1]`. De que precisa para isso? -- [x] de uma variável auxiliar -- de um segundo array -- de uma pesquisa binária -> Tal como ao trocar duas variáveis: `int aux = cartas[i];`, senão perde-se um valor. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar como a pesquisa linear percorre um array. → Procurar uma carta -- Consigo construir uma pesquisa linear como estrutograma. → Exercício: Encontrar uma carta -- Consigo explicar porque é que a pesquisa binária só funciona com valores ordenados. → Pesquisa binária: ordenado é mais rápido -- Consigo explicar porque é que a pesquisa binária é muito mais rápida com muitos valores. → Quão rápido é? -- Consigo mostrar uma ronda de bubble sort com cartas de jogar. → Ordenar com bubble sort -- Consigo percorrer um array em Java e contar resultados. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? De que precisa a pesquisa binária? -→ Pesquisa binária: ordenado é mais rápido -- [ ] O array tem de ter exatamente 100 posições. -- [x] Os valores têm de estar ordenados. -- [ ] O valor procurado tem de existir. -> Só com valores ordenados é que sabes, depois de olhares para o meio, em que metade continuar a procurar. - -? Um array ordenado tem 1000 valores. De quantas comparações precisa a pesquisa binária no máximo? -→ Quão rápido é? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: depois de cerca de 10 divisões a meio, resta um valor. - -? O que compara o bubble sort em cada passo? -→ Ordenar com bubble sort -- [ ] A primeira e a última carta -- [x] Duas cartas vizinhas -- [ ] Cada carta com a do meio -> Se a carta maior estiver à esquerda, os vizinhos são trocados. - -? Onde está a carta maior depois da primeira ronda do bubble sort? -→ Ordenar com bubble sort -- [ ] Na ponta da esquerda -- [ ] No meio -- [x] Na ponta da direita -> Vai sendo trocada em cada comparação e sobe para a direita como uma bolha. - -? O que quer dizer `posicao = -1` na pesquisa linear? -→ Procurar uma carta -- [x] A carta (ainda) não foi encontrada. -- [ ] A carta está mesmo lá atrás. -- [ ] O array está vazio. - -? Associa cada método à descrição certa. -→ Procurar uma carta -= Pesquisa linear -> olhar para uma carta a seguir à outra -= Pesquisa binária -> olhar para o meio e pôr de lado uma metade -= Bubble sort -> comparar vizinhos e trocá-los - -? Porque usas um ciclo for para contar e não uma pesquisa que para no primeiro resultado? -→ Agora em Java -- [ ] Um ciclo for é sempre mais rápido. -- [x] Tens de ver todas as cartas, senão faltam resultados mais à frente. -- [ ] Um ciclo while não consegue percorrer arrays. -> A carta pode aparecer várias vezes. Por isso percorres todas as cartas até ao fim. -::: diff --git a/web/lessons/pt/20-java.md b/web/lessons/pt/20-java.md deleted file mode 100644 index 53d3fa83..00000000 --- a/web/lessons/pt/20-java.md +++ /dev/null @@ -1,472 +0,0 @@ ---- -titel: Estrutograma e código -bereich: Do estrutograma ao código -kurz: Todos os elementos em Java num relance, gerar código, escrever Java na área Código, executá-lo e procurar erros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- abrir a vista de código e escolher Java como linguagem -- reconhecer em Java a saída, a entrada, as decisões e os ciclos -- explicar a diferença entre Excerto e Classe -- explicar como o código Java corre em qualquer sistema operativo com o compilador, o bytecode e a JVM -- distinguir o JDK do JRE -- explicar porque é que um programa em C++ tem de ser compilado de novo para cada sistema operativo e um programa Java não -- reconstruir um programa Java curto como estrutograma -- escrever Java na área Código e ler o estrutograma que é construído -- executar um programa com ▶︎ e usar entradas e saídas na consola -- encontrar um erro de sintaxe ou de execução pelo número de linha vermelho -- pôr pontos de paragem e seguir um programa passo a passo -::: - -## A vista de código - -Nas lições 4 a 19 já escreveste cada elemento em Java. Aqui vês tudo num relance e aprendes como o VisuStruct transforma um estrutograma inteiro num programa Java. - -Um estrutograma descreve um processo. Para um computador o executar, precisas de um programa numa linguagem de programação. O VisuStruct escreve esse código por ti. - -Clica em [Código](#zeige-code) nas ferramentas (ícone com parênteses angulares). À direita aparece o **Gerador de código**: - -- Em cima escolhes a linguagem: **Java**, **Python** ou **JavaScript**. -- Para Java escolhes ainda em **Saída Java** entre **Excerto** (só as instruções) e **Classe** (um ficheiro Java completo). -- **Texto do diagrama como comentários** escreve o texto de cada bloco como comentário por cima do código. -- **Copiar** põe o código na área de transferência, **Guardar Java…** guarda um ficheiro `.java`. - -O código muda logo sempre que alteras alguma coisa no estrutograma. - -::: angeberwissen -Mais tarde, Nassi trabalhou na linguagem de programação Ada e foi responsável pelo software na Apple. Portanto, quem inventa estrutogramas não acaba num curral de cabras, acaba na Apple. -::: - -::: diagramm l17-beispiel.visustruct -Um pequeno programa com uma entrada, um ciclo for e uma decisão. Abre-o e olha para o código Java correspondente. -::: - -## O exemplo em Java - -Para o diagrama de cima, o VisuStruct gera este código como **Excerto**: - -``` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que tabuada?"); -int numero = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int resultado = numero * i; - System.out.println(i + " vezes " + numero + " = " + resultado); -} -if (numero > 10) { - System.out.println("Esta foi difícil!"); -} else { - System.out.println("Esta foi fácil."); -} -``` - -Em Java, cada instrução termina com um **ponto e vírgula** `;`. O que está dentro de um bloco fica entre **chavetas** `{ }` em Java. A indentação mostra-te o que pertence ao quê. - -## Os elementos em Java - -| Elemento | Java | Lição | -|---|---|---| -| Instrução | `int soma = 0;` | 4, 5 | -| Saída | `System.out.println(soma);` | 6 | -| Entrada | `int numero = scanner.nextInt();` | 6 | -| Decisão | `if (condição) { … } else { … }` | 8, 9 | -| Seleção | `switch (valor) { case 1 -> { … } default -> { … } }` | 10 | -| Ciclo while | `while (condição) { … }` | 11 | -| Ciclo do-while | `do { … } while (condição);` | 12 | -| Ciclo for | `for (int i = 0; i < 5; i++) { … }` | 13 | -| Ciclo infinito | `while (true) { … }` | 11, 18 | -| Interrupção | `break;` | 18 | -| Chamada | `calcular();` | 18 | -| Array | `int[] numeros = new int[5];` | 15, 16 | -| Texto (String) | `String nome = scanner.next();` | 17 | - -Para entradas, `import java.util.Scanner;` e `Scanner scanner = new Scanner(System.in);` são escritos uma vez lá em cima. Antes de cada entrada, o VisuStruct mostra a pergunta com `System.out.println(…)`. - -Se o ramo false de uma decisão estiver vazio, o VisuStruct gera mesmo assim uma parte `else` que só contém o comentário `// empty element`. Se desligares **Gerar ramo else vazio no código**, a parte `else` vazia desaparece. - -## Excerto ou Classe - -Podes colar um **Excerto** no método `main` de um programa Java que já exista. Com **Classe** obténs um ficheiro completo que podes compilar e executar logo: - -``` -import java.util.Scanner; - -public class Tabuada { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Que tabuada?"); - int numero = scanner.nextInt(); - ... - } // fim do main -} // fim da classe -``` - -Os comentários `// fim do main` e `// fim da classe` depois das duas últimas chavetas não são necessários, mas ajudam muito: vês logo que chaveta fecha o quê e não te esqueces de nenhuma. - -O nome da classe vem do **título** do diagrama. O ficheiro tem de ter exatamente o mesmo nome, aqui `Tabuada.java`. O **Guardar Java…** trata disso por ti. - -> O código gerado é um bom começo, mas confirma-o. Se primeiro criares uma variável e mais tarde a voltares a ler com uma entrada, o VisuStruct escreve mesmo assim `int pin = scanner.nextInt();`. O Java indica então que `pin` já existe. Basta apagares o `int` nessa linha. - -## Como o Java corre em qualquer computador - -O teu ficheiro `Tabuada.java` é **código-fonte**: texto para pessoas. O processador do teu computador não o entende diretamente. Por isso, o Java corre em dois passos: - -1. O **compilador** `javac` traduz o código-fonte em **bytecode**. O resultado é o ficheiro `Tabuada.class`. -2. A **JVM** (Java Virtual Machine, máquina virtual Java) lê o bytecode e, ao executá-lo, traduz-o em **código máquina**: instruções para o processador do teu computador. - -::: grafik l20-jvm.svg -Compilar uma vez: o mesmo bytecode corre em qualquer JVM. -::: - -O bytecode existe só uma vez. Já a JVM existe para Windows, macOS, Linux e muitos outros sistemas. Por isso, o mesmo ficheiro `.class` corre em qualquer computador que tenha uma JVM instalada. A isto chama-se **independente da plataforma**. O lema é **“Write once, run anywhere”**: escrever uma vez, executar em qualquer lado. - -::: angeberwissen -Um dos primeiros compiladores foi construído em 1952 por **Grace Hopper**, que já conheces da lição 7. Na altura, muitos diziam-lhe que os computadores só sabiam fazer contas e não traduzir programas. Ela fê-lo na mesma. -::: - -Compara com o **C++**: aí o compilador traduz o código-fonte diretamente em código máquina para um só processador e um só sistema operativo. O programa pronto só corre aí. Para Windows, macOS e Linux tens de o compilar de novo de cada vez. - -::: grafik l20-cpp.svg -O C++ é compilado para código máquina em separado para cada sistema. -::: - -| | C++ | Java | -|---|---|---| -| O compilador gera | código máquina | bytecode | -| Quantas vezes compilar? | de novo para cada sistema | uma vez | -| De que precisa o computador para o executar? | de mais nada | de uma JVM | - -Isto torna o Java mais lento? Quase nada. As partes do programa que correm muitas vezes, por exemplo ciclos, são traduzidas de uma vez para código máquina pelo **compilador JIT** da JVM (“just in time”, mesmo a tempo). Depois correm quase tão depressa como em C++. - -> Imagina a JVM como um pequeno computador feito de software, quase como um sistema operativo só para programas Java. Mas, rigorosamente, não é um sistema operativo, é uma **máquina virtual**: um programa que corre ele próprio no Windows, no macOS ou no Linux e que, lá dentro, faz de conta que é um computador. - -Ao instalar o Java, encontras duas siglas: - -| Sigla | O que tem lá dentro? | Para quê? | -|---|---|---| -| **JRE** (Java Runtime Environment) | a JVM e blocos prontos como o `Scanner` | executar programas Java | -| **JDK** (Java Development Kit) | o JRE e ainda o compilador `javac` e outras ferramentas | escrever e compilar programas Java | - -Para programar precisas, portanto, do JDK. Hoje em dia normalmente só se descarrega o JDK, o JRE vem lá dentro. Para usar o VisuStruct no browser não precisas de instalar nada disto. - -::: angeberwissen -Com um JDK podes fazer tudo isto tu próprio na consola, ou seja, no terminal ou na linha de comandos: - -``` -java -version -javac Tabuada.java -java Tabuada -``` - -- `java -version` mostra que versão do Java está instalada. Se aparecer uma mensagem de erro, ainda falta o Java. -- `javac Tabuada.java` inicia o compilador. Depois tens dois ficheiros na pasta: o `Tabuada.java` com o teu código-fonte e o novo `Tabuada.class` com o bytecode. -- `java Tabuada` inicia a JVM com o teu bytecode. Aqui escreves o nome da classe sem `.class`. - -Se `java -version` funcionar mas `javac -version` não, só tens um JRE instalado. Nesse caso falta-te o JDK. -::: - -::: angeberwissen -Todos os ficheiros Java compilados (`.class`) começam com os mesmos quatro bytes, escritos em hexadecimal: `CA FE BA BE`. É assim que o computador reconhece que é Java. Foi James Gosling que inventou este “cafe babe”, a condizer com o café do nome. -::: - -## O modo de linguagem Java - -Na [roda dentada](#zeige-einstellungen) em cima à direita podes ativar as **Palavras-chave do código**. Então os blocos da área Elementos têm os mesmos nomes que os comandos em Java: `if`, `switch`, `for`, `while`, `do`, `while(true)`, `break` e `call`. O resto da interface fica no teu idioma. - -Isto ajuda-te a associar os blocos aos comandos Java. O teu diagrama em si fica igual. - -## Escrever Java diretamente - -Até agora desenhaste primeiro o estrutograma e depois viste o código. Na área [Código](#zeige-codelive) também funciona ao contrário. Clica na barra estreita **Código** à direita. Ao lado do estrutograma aparece uma coluna onde escreves tu mesmo o Java. - -- **Do estrutograma para o código:** Quando alteras alguma coisa no estrutograma, o código Java muda logo. -- **Do código para o estrutograma:** Quando escreves Java, o VisuStruct constrói o estrutograma enquanto escreves. Se ainda faltar alguma coisa, por exemplo uma chaveta de fecho `}`, aparece uma indicação por baixo do código. -- **Sugestões:** Enquanto escreves, o VisuStruct sugere modelos, por exemplo para `if`, `for` ou `while`. Com Enter aceitas um modelo. Substitui simplesmente os marcadores cinzentos pelo teu texto. -- **Indentar:** Se o teu código ficou desarrumado, clica no botão **Indentar** em cima na área Código. Cada nível fica então quatro espaços mais para dentro, como no Eclipse ou no IntelliJ. Se tiveres linhas selecionadas, o VisuStruct só indenta essas. - -Assim podes experimentar os dois caminhos. Desenhar primeiro o estrutograma é bom para planear um processo. Escrever o código diretamente é mais rápido quando já sabes o que queres. O estrutograma ajuda-te depois a verificar o teu código. - -## Executar o programa - -Em cima, na área Código, [▶︎ Executar](#zeige-ausfuehren) inicia o teu programa e **■ Parar** termina-o, por exemplo num ciclo infinito. O que o teu programa mostra aparece em baixo na [consola](#zeige-konsole). Se pedir uma entrada, escreve-a lá e carrega em Enter. - -Podes arrastar com o rato a fronteira entre o código e a consola para cima ou para baixo. - -Se algo correr mal, o número da linha fica vermelho. Num erro de sintaxe, o sítio também fica sublinhado a vermelho e por baixo lês, por exemplo, **Linha 2: Ainda falta aqui: ;**. Aí o ▶︎ nem sequer arranca o programa. Se o programa parar enquanto corre, é um erro de execução e a consola diz a linha, por exemplo **Linha 3: Divisão por zero.** Os três tipos de erros já os conheces da lição “Simulação e teste de secretária”. - -Se o VisuStruct souber exatamente o que falta, aparece um 💡 no fim da linha. Passa o rato por cima para veres a sugestão, por exemplo **Sugestão: inserir import java.util.Scanner;**. Um clique aplica-a. Mesmo assim, lê o que foi inserido, para saberes fazê-lo sozinho da próxima vez. - -> A área Código entende os comandos Java que também existem como blocos no estrutograma. Ainda não conhece classes próprias nem objetos. Janelas com Swing funcionam na mesma: liga o **Java verdadeiro (com Swing)** na roda dentada (lição “A tua primeira janela”). - -::: angeberwissen -Os profissionais chamam **IDE** (Integrated Development Environment) a um programa onde se escreve código, se executa e se procuram erros. Com a área Código, o VisuStruct é um pequeno IDE com estrutograma incluído. -::: - -## Encontrar erros com o depurador - -Às vezes um programa corre, mas calcula mal. É um erro de lógica. Aí ajuda o **depurador** (debugger): executa o teu programa devagar e tu vês o que acontece. - -- **Ponto de paragem:** Clica num número de linha à esquerda. Aparece um ponto vermelho. [▶︎ Executar](#zeige-ausfuehren) para antes dessa linha. Outro clique tira o ponto. -- **Linha amarela:** É a próxima a ser executada. No estrutograma, o bloco correspondente fica selecionado. -- **Variáveis:** Por baixo do código vês o valor atual de cada variável. O que mudou desde a última paragem acende-se a amarelo por um momento. -- **Passo a passo:** O botão [Passo a passo](#zeige-einzelschritt) ao lado de ▶︎ executa exatamente uma linha e para outra vez. Sem ponto de paragem, inicia o programa e para antes da primeira linha. -- **Continuar:** ▶︎ corre até ao próximo ponto de paragem ou até ao fim. **■ Parar** termina. - -Experimenta com este programa. Põe um ponto de paragem na linha 3 e carrega várias vezes em ▶︎. Como crescem `i` e `soma`? - -``` -int soma = 0; -for (int i = 1; i <= 4; i++) { - soma = soma + i; -} -System.out.println("Soma: " + soma); -``` - -::: ludwig -É um teste de secretária, só que o computador toma notas por ti. Antes de cada passo, pensa no valor que a variável devia ter a seguir. Quando a tabela não bate com o que esperavas, encontraste o sítio do erro. -::: - -## Exercício: Primeiro o código, depois o estrutograma - -Desta vez não desenhas. Abre a área Código e escreve este código Java: - -``` -System.out.println("Número inicial?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -``` - -::: übung l20-countdown -titel: Contagem decrescente -bereich: beide -auftrag: Escreve o código Java acima na área Código. Executa-o com ▶︎ e introduz um número inicial na consola. - Depois observa o estrutograma que foi construído e clica em Verificar. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Abre a área Código com a barra estreita mais à direita e começa simplesmente a escrever. -tipp: Atenção ao ponto e vírgula no fim de cada instrução e às chavetas do ciclo while. Por baixo do código aparece uma indicação quando ainda falta alguma coisa. -lösung: l20-countdown-loesung.visustruct -::: - -## Exercício: Java como estrutograma - -Agora ao contrário: aqui está um pedaço de código Java. Reconstrói-o como estrutograma. - -``` -System.out.println("Introduz um número"); -int n = scanner.nextInt(); -int fatorial = 1; -for (int i = 1; i <= n; i++) { - fatorial = fatorial * i; -} -System.out.println(fatorial); -``` - -::: übung l17-fakultaet -titel: Fatorial a partir de Java -bereich: beide -auftrag: Reconstrói o código Java de cima como estrutograma. O programa lê um número n e mostra o produto 1 · 2 · … · n. - No fim, confirma na vista de código se o teu código Java fica igual. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Lê o código Java linha a linha e pensa que bloco serve para cada linha. -tipp: As duas primeiras linhas Java juntas são uma única entrada: `input: int n "Introduz um número"`. O ciclo for tem os três campos `int i = 1`, `i <= n` e `i++`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Exercício: else if - -Em Java, vários casos escrevem-se muitas vezes com `else if`: - -``` -if (idade < 6) { - System.out.println("Entrada gratuita"); -} else if (idade < 18) { - System.out.println("Bilhete de criança: 4 euros"); -} else { - System.out.println("Bilhete de adulto: 8 euros"); -} -``` - -Num estrutograma não existe `else if`. Aí, uma segunda decisão fica no ramo false da primeira. Se o ramo false tiver só essa decisão, o VisuStruct volta a transformá-la em `else if` quando gera o código, e em `elif` em Python. - -::: übung l17-ticket -titel: Preço do bilhete -bereich: beide -auftrag: Pede a idade como número inteiro e reconstrói o código Java de cima como estrutograma. -test: 5 -> Entrada gratuita -test: 6 -> Bilhete de criança: 4 euros -test: 17 -> Bilhete de criança: 4 euros -test: 18 -> Bilhete de adulto: 8 euros -braucht: verzweigung -tipp: No código Java há outro `if` dentro de um `else`. No estrutograma isso é uma decisão dentro de um ramo. -tipp: Arrasta uma segunda decisão com a condição `idade < 18` para o ramo false da primeira decisão. -lösung: l17-ticket-loesung.visustruct -::: - -## Jogo: Que bloco é? - -::: spiel frage -titel: Jogo: Que bloco é? -intro: O Ludwig mostra-te Java. Que bloco do estrutograma está por trás? Oito rondas. -frage: Que bloco é este? - -runde: - do { - numero = numero * 2; - } while (numero < 100); -- Ciclo while -- [x] Ciclo do-while -- Ciclo for -> A condição está em baixo. Por isso o corpo corre pelo menos uma vez. - -runde: - switch (escolha) { - case 1 -> { System.out.println("água"); } - default -> { System.out.println("Esse botão não existe."); } - } -- Decisão -- [x] Seleção -- Chamada -> Uma variável é comparada com valores fixos: isso é a seleção. - -runde: - break; -- Chamada -- Ciclo infinito -- [x] Interrupção -> O `break` sai logo do ciclo onde está. - -runde: - calcular(); -- [x] Chamada -- Saída -- Instrução -> Um nome com parênteses: aqui é chamado um subprograma. - -runde: - int numero = scanner.nextInt(); -- Saída -- [x] Entrada -- Chamada -> O scanner lê um valor. Isso é uma entrada. - -runde: - while (true) { - System.out.println("Olá"); - } -- Ciclo while com condição -- [x] Ciclo infinito -- Ciclo do-while -> `while (true)` não tem uma condição verdadeira. Só um break o poderia terminar. - -runde: -frage: Queres um ficheiro Java completo que possas compilar e executar logo. O que escolhes na vista de código? -- Excerto -- [x] Classe -> Um excerto são só as instruções para o método `main`. Classe cria o ficheiro inteiro. - -runde: -frage: No depurador, uma linha está amarela. O que quer dizer? -- Tem um erro. -- [x] É a próxima a ser executada. -- Tem um ponto de paragem. -> Amarela é a próxima linha. Um ponto de paragem é um ponto vermelho no número da linha. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo abrir a vista de código e escolher Java como linguagem. → A vista de código -- Consigo reconhecer em Java a saída, a entrada, as decisões e os ciclos. → Os elementos em Java -- Consigo explicar a diferença entre Excerto e Classe. → Excerto ou Classe -- Consigo explicar como o código Java corre em qualquer sistema operativo com o compilador, o bytecode e a JVM. → Como o Java corre em qualquer computador -- Consigo distinguir o JDK do JRE. → Como o Java corre em qualquer computador -- Consigo explicar porque é que um programa em C++ tem de ser compilado de novo para cada sistema operativo e um programa Java não. → Como o Java corre em qualquer computador -- Consigo reconstruir um programa Java curto como estrutograma. → Exercício: Java como estrutograma -- Consigo escrever Java na área Código e ler o estrutograma que é construído. → Escrever Java diretamente -- Consigo executar um programa com ▶︎ e usar entradas e saídas na consola. → Executar o programa -- Consigo encontrar um erro de sintaxe ou de execução pelo número de linha vermelho. → Executar o programa -- Consigo pôr pontos de paragem e seguir um programa passo a passo. → Encontrar erros com o depurador -::: - -## Teste de conhecimentos - -::: quiz -? Como fica `output: "Olá"` em Java? -→ Os elementos em Java -- [ ] print("Olá") -- [x] System.out.println("Olá"); -- [ ] console.log("Olá"); -> print é Python, console.log é JavaScript. - -? De onde vem o nome da classe em Classe? -→ Excerto ou Classe -- [ ] Chama-se sempre Main. -- [x] Do título do diagrama -- [ ] Do primeiro bloco -> Sem título, a classe chama-se Main. - -? Que código Java pertence a um ciclo do-while? -→ Os elementos em Java -- [ ] while (condição) { … } -- [x] do { … } while (condição); -- [ ] for (;;) { … } -> No do-while a condição está no fim, por isso o corpo é executado pelo menos uma vez. - -? Em que se transforma uma Interrupção no código Java? -→ Os elementos em Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? O que acontece num ponto vermelho antes do número da linha? -→ Encontrar erros com o depurador -- [ ] A linha é ignorada. -- [x] O programa para antes dessa linha. -- [ ] A linha tem um erro. -> É um ponto de paragem. Continua-se com ▶︎ ou com Passo a passo. - -? Porque é que o mesmo ficheiro `.class` corre no Windows, no macOS e no Linux? -→ Como o Java corre em qualquer computador -- [ ] O compilador cria um ficheiro diferente para cada sistema operativo. -- [x] Em cada sistema operativo, uma JVM executa o mesmo bytecode. -- [ ] Os programas Java não precisam de sistema operativo. -> O bytecode fica igual. Só a JVM existe em separado para cada sistema. - -? De que precisas para escrever e compilar programas Java? -→ Como o Java corre em qualquer computador -- [ ] só do JRE -- [x] do JDK -- [ ] só da JVM -> O JRE chega para executar. O compilador `javac` só vem com o JDK. - -? Queres usar o teu programa no Windows e no Mac. O que é verdade? -→ Como o Java corre em qualquer computador -- [ ] O Java tem de ser compilado de novo para cada sistema, o C++ não. -- [x] O C++ tem de ser compilado de novo para cada sistema, o Java não. -- [ ] Os dois só se compilam uma vez. -> O C++ passa diretamente a código máquina para um sistema. O Java passa a bytecode, e é a JVM que o executa em cada sistema. - -? O número de linha 3 na área Código está vermelho. O que quer isso dizer? -→ Executar o programa -- [ ] Há um ponto de paragem na linha 3. -- [x] Há um erro na linha 3. -- [ ] A linha 3 é a próxima a ser executada. -> Um ponto de paragem é um ponto vermelho redondo, e a próxima linha está a amarelo. Um número de linha vermelho mostra um erro de sintaxe ou de execução. -::: diff --git a/web/lessons/pt/21-python-und-javascript.md b/web/lessons/pt/21-python-und-javascript.md deleted file mode 100644 index f07e8c4f..00000000 --- a/web/lessons/pt/21-python-und-javascript.md +++ /dev/null @@ -1,240 +0,0 @@ ---- -titel: Python e JavaScript -bereich: Do estrutograma ao código -kurz: Gerar código Python e JavaScript a partir do estrutograma e executar JavaScript diretamente no navegador. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- escolher e comparar Python e JavaScript na vista de código -- explicar que o Python forma blocos pela indentação e não precisa de tipos de dados -- explicar o que o VisuStruct ajusta ao traduzir para Python -- executar JavaScript com Testar no navegador -::: - -## O mesmo diagrama, três linguagens - -Na vista de código ([Código](#zeige-code) nas ferramentas) escolhes a linguagem em cima. O estrutograma fica igual; só muda a forma de escrever. Assim podes comparar as linguagens facilmente. - -::: diagramm l18-beispiel.visustruct -Um número é diminuído em 3 uma e outra vez até deixar de ser maior do que 0. -::: - -## Python - -Para o diagrama de cima, o VisuStruct gera este código Python: - -``` -numero = int(input("Valor inicial?")) -while numero > 0: - print(numero) - numero = numero - 3 -print("Fim") -``` - -O que é diferente em Python em relação ao Java: - -- **Não há ponto e vírgula** nem **chavetas**. O Python reconhece o que pertence a um ciclo ou a uma decisão só pela **indentação**. Depois de `while`, `if` e `else` vêm dois pontos. -- As variáveis **não têm tipo de dados**: `int numero = 5` passa a `numero = 5`. -- A saída chama-se `print(…)`, a entrada `input(…)`. Para um número, fica `int(…)` à volta. -- Em Python, os arrays chamam-se **listas**: `int[] numeros = {2, 4, 6}` passa a `numeros = [2, 4, 6]`, e `numeros.length` passa a `len(numeros)`. -- Um ciclo for simples como `int i = 0`, `i < 5`, `i++` passa a `for i in range(5):`. - -## Cuidados com o Python - -Em alguns sítios, o Python é mais rigoroso do que o Java. Por isso, o VisuStruct traduz a forma de escrever do Java quando exporta: - -- **Texto e número com +:** `"Soma: " + soma` passa a `"Soma: " + str(soma)`. Sem `str(…)`, o Python para com um erro. -- **Não existe i++:** passa a `i += 1`. -- **&&, || e !** chamam-se `and`, `or` e `not` em Python, e `true` e `false` passam a `True` e `False`. -- **Divisão inteira:** Em Python, `7 / 2` dá sempre 3.5. Se o estrutograma dividir dois valores `int`, o VisuStruct escreve `7 // 2`, que dá 3, tal como em Java. -- **Ciclos for:** Os ciclos de contagem passam a `for … in range(…)`, também com `i <= n`, a contar para trás ou com passos como `i = i + 2`. Se um ciclo não encaixar nesse padrão, passa a ciclo `while`. -- **Não existe do-while:** passa a `while True:` com `if not (…): break` no fim. - -Se um diagrama estiver marcado como **pseudocódigo**, só aparece um aviso por cima do código. Nesse caso o texto é um esboço e não um programa executável. - -## JavaScript - -O mesmo diagrama em JavaScript: - -``` -"use strict"; - -let numero = parseInt((prompt("Valor inicial?") ?? "0"), 10); -while (numero > 0) { - console.log(numero); - numero = numero - 3; -} -console.log("Fim"); -``` - -O JavaScript parece-se com o Java: chavetas, ponto e vírgula, `while (…)` e `if (…)`. As diferenças: - -- As variáveis criam-se com `let`, sem tipo de dados. As constantes com `const`. -- A saída chama-se `console.log(…)`, a entrada `prompt(…)`. O `parseInt(…)` transforma a entrada num número inteiro. -- Os arrays ficam entre parênteses retos: `let numeros = [2, 4, 6];`. -- `"use strict";` mesmo lá em cima faz o JavaScript queixar-se de erros de escrita nos nomes das variáveis. -- O JavaScript só tem um tipo de número. Se o estrutograma dividir dois valores `int`, o VisuStruct escreve `Math.trunc(a / b)`. Assim desaparecem os algarismos depois da vírgula, tal como em Java. - -::: angeberwissen -O JavaScript não tem quase nada a ver com o Java. Brendan Eich criou-o em 1995 em apenas dez dias para o navegador Netscape. Primeiro chamava-se Mocha, depois LiveScript. Como toda a gente andava entusiasmada com o Java, pouco antes do lançamento mudaram-lhe o nome para JavaScript, por razões de publicidade. Desde então, as duas linguagens são constantemente confundidas. Os programadores dizem que o Java e o JavaScript têm tanto em comum como um carro e uma carpete. -::: - -## Executar JavaScript no navegador - -O JavaScript é a linguagem dos navegadores. Por isso o VisuStruct consegue **executar o código logo**: - -::: angeberwissen -Já agora, porque é que as ligações no navegador são azuis? Em 1985, a equipa de Ben Shneiderman testou que cor se vê bem mas não atrapalha a leitura. Ganhou o azul-claro. O vermelho destacava-se mais, mas as pessoas lembravam-se pior do texto a seguir. -::: - -1. Escolhe **JavaScript** na vista de código. -2. Clica em **Testar no navegador**. -3. O que o `console.log` mostra aparece em **Saída**. Se o programa pedir um valor com `prompt`, escreve-o e clica em **OK**. -4. **Parar** cancela o programa. - -O VisuStruct para sozinho um ciclo infinito `while (true)` depois de 100 000 iterações e indica **Execução interrompida: possível ciclo infinito.** - -> No código JavaScript, uma **Chamada** precisa de um subprograma com esse nome, senão o JavaScript indica um erro. Ao contrário da simulação, o navegador não procura nos outros separadores. - -## Exercício: Python como estrutograma - -Este programa Python mostra todos os números pares de 0 até um limite: - -``` -limite = int(input("Até que número?")) -for i in range(0, limite + 1, 2): - print(i) -``` - -`range(0, limite + 1, 2)` quer dizer: começar em 0, parar antes de `limite + 1`, contar de dois em dois. - -::: übung l18-python -titel: Números pares -bereich: beide -auftrag: Reconstrói o programa Python de cima como estrutograma com um ciclo for. Com o limite 7, mostra 0, 2, 4 e 6. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: `range(0, limite + 1, 2)` conta de 0 até ao limite de dois em dois. Isso é um ciclo for. -tipp: Os três campos do ciclo for são `int i = 0`, `i <= limite` e `i = i + 2`. -lösung: l18-python-loesung.visustruct -::: - -## Exercício: JavaScript como estrutograma - -``` -let idade = parseInt((prompt("Quantos anos tens?") ?? "0"), 10); -if (idade >= 18) { - console.log("Podes votar."); -} else { - let anos = 18 - idade; - console.log("Espera mais " + anos + " anos."); -} -``` - -::: übung l18-js -titel: Votar a partir dos 18 -bereich: beide -auftrag: Reconstrói o código JavaScript de cima como estrutograma. Depois testa na vista de código, com **JavaScript** e **Testar no navegador**, se o teu código mostra o mesmo que a simulação. -test: 12 -> Espera mais 6 anos. -test: 18 -> Podes votar. -test: 15 -> Espera mais 3 anos. -test: 40 -> Podes votar. -braucht: verzweigung -tipp: Lê o código linha a linha. `if` e `else` passam a uma decisão. -tipp: `prompt` e `parseInt` juntos são uma entrada: `input: int idade "Quantos anos tens?"`. O ramo false tem dois blocos: primeiro `int anos = 18 - idade`, depois a saída. -lösung: l18-js-loesung.visustruct -::: - -## Jogo: Que linguagem? - -::: spiel frage -titel: Jogo: Que linguagem? -intro: O Ludwig misturou linhas de três linguagens. Reconheces de onde vem cada uma? Oito rondas. -frage: Em que linguagem está isto escrito? -antworten: Java | Python | JavaScript - -runde: - print(numero) -richtig: Python -> Em Python, a saída chama-se `print(…)`, sem ponto e vírgula. - -runde: - console.log(numero); -richtig: JavaScript -> `console.log` é a saída em JavaScript. - -runde: - System.out.println(numero); -richtig: Java -> A saída comprida com `System.out` só existe em Java. - -runde: - let numeros = [2, 4, 6]; -richtig: JavaScript -> `let` e parênteses retos: um array em JavaScript. - -runde: - for i in range(5): -richtig: Python -> Dois pontos em vez de chavetas, e `range`: típico de Python. - -runde: - int[] numeros = {2, 4, 6}; -richtig: Java -> Só o Java escreve o tipo de dados `int[]` à frente. - -runde: - numero = int(input("Valor inicial?")) -richtig: Python -> Sem tipo de dados antes da variável, sem ponto e vírgula, e `int(…)` à volta da entrada. - -runde: - let numero = parseInt(prompt("Valor inicial?"), 10); -richtig: JavaScript -> `prompt` pede um valor no browser, `parseInt` transforma-o num número. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo escolher e comparar Python e JavaScript na vista de código. → O mesmo diagrama, três linguagens -- Consigo explicar que o Python forma blocos pela indentação e não precisa de tipos de dados. → Python -- Consigo explicar o que o VisuStruct ajusta ao traduzir para Python. → Cuidados com o Python -- Consigo executar JavaScript com Testar no navegador. → Executar JavaScript no navegador -::: - -## Teste de conhecimentos - -::: quiz -? Como reconhece o Python que linhas pertencem a um ciclo? -→ Python -- [ ] Pelas chavetas -- [x] Pela indentação -- [ ] Pelo ponto e vírgula no fim da linha - -? Como fica `output: soma` em JavaScript? -→ O mesmo diagrama, três linguagens -- [ ] print(soma) -- [ ] System.out.println(soma); -- [x] console.log(soma); - -? Que linguagem consegue o VisuStruct executar diretamente no navegador? -→ Executar JavaScript no navegador -- [ ] Java -- [ ] Python -- [x] JavaScript -> Qualquer navegador consegue executar JavaScript. Para Java e Python precisas de um programa próprio. - -? O que acontece em Python com `print("Soma: " + soma)` se soma for o número 7? -→ Cuidados com o Python -- [ ] É mostrado Soma: 7. -- [x] O Python indica um erro, porque texto e número não se podem juntar com +. -- [ ] É mostrado Soma:. -> str(soma) transforma o número em texto. -::: diff --git a/web/lessons/pt/22-try-und-catch.md b/web/lessons/pt/22-try-und-catch.md deleted file mode 100644 index 89d8352e..00000000 --- a/web/lessons/pt/22-try-und-catch.md +++ /dev/null @@ -1,293 +0,0 @@ ---- -titel: Quando o programa falha: try e catch -bereich: Apanhar erros -kurz: Ler mensagens de erro com Java verdadeiro e apanhar falhas com try e catch. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ligar o Java verdadeiro na área Código -- ler numa mensagem de erro a exceção e a linha -- apanhar uma exceção com try e catch -- transformar texto num número com Integer.parseInt e apanhar a NumberFormatException -- escrever um programa que divide em segurança, mesmo por 0 -::: - -## Falhou! - -Este programa deve escrever três notas. Escreve-o na área [Código](#zeige-codelive) e clica em [▶︎ Executar](#zeige-ausfuehren): - -```java -int[] notas = {2, 3, 1}; -System.out.println("Primeira nota: " + notas[0]); -System.out.println("Quarta nota: " + notas[3]); -System.out.println("Pronto!"); -``` - -A primeira nota aparece e depois acabou. O VisuStruct indica “Expressão inválida: notas[3]”, e “Pronto!” nunca chega a aparecer. O array só tem as posições 0, 1 e 2; a posição 3 não existe. - -::: ludwig -Já te aconteceu? Um pequeno erro e o programa inteiro vai-se. A mim aconteceu-me tantas vezes que cheguei a chorar em frente ao ecrã. Mas há uma solução, e chama-se **try** e **catch**. -::: - -::: angeberwissen -Em 1947, a equipa de Grace Hopper encontrou no computador Mark II da Universidade de Harvard uma traça que tinha causado uma avaria. Colaram-na no diário de bordo e escreveram: “First actual case of bug being found”. Desde então, os erros de programa chamam-se **bugs** (inglês para insetos). -::: - -## Ligar o Java verdadeiro - -Normalmente a área Código calcula com a mesma simulação que o estrutograma. Quando há um erro, ela simplesmente para. Para veres o que faz o Java verdadeiro, liga o Java verdadeiro: - -1. Clica na [roda dentada](#zeige-einstellungen) em cima à direita e escolhe Java como **Linguagem de programação**. -2. Na secção Java, marca **Java verdadeiro (com Swing)**. - -Na primeira execução, o VisuStruct descarrega uma única vez cerca de 40 MB. Depois disso, o teu código é compilado e executado como num ambiente de desenvolvimento Java a sério. - -> Com o Java verdadeiro, as entradas pela consola (`Scanner`) ainda não funcionam. Por isso os nossos exemplos não precisam de entradas. - -Escreve agora o programa como uma classe completa, para que os números das linhas batam certo, e executa-o: - -```java -public class Notas { - public static void main(String[] args) { - int[] notas = {2, 3, 1}; - System.out.println("Primeira nota: " + notas[0]); - System.out.println("Quarta nota: " + notas[3]); - System.out.println("Pronto!"); - } -} -``` - -Na [consola](#zeige-konsole) aparece agora: - -``` -Primeira nota: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Notas.main(Notas.java:5) -``` - -## Ler a mensagem de erro - -Uma mensagem destas assusta à primeira vista. Mas diz-te exatamente o que se passa: - -| Parte | Significado | -|---|---| -| `ArrayIndexOutOfBoundsException` | O tipo de erro: o índice está fora do array. | -| `3` | O índice errado. | -| `Notas.java:5` | A linha onde aconteceu: a linha 5. | - -O Java chama a um erro de execução destes uma **exceção** (em inglês *exception*). Diz-se: o Java *lança* uma exceção. Se ninguém a apanhar, o programa para. É por isso que “Pronto!” não aparece. - -::: ludwig -Lê as mensagens de erro sempre de cima: primeiro o nome da exceção, depois o número da linha. Na área Código, os números das linhas estão à esquerda do código. -::: - -## try e catch - -Com **try** e **catch** apanhas a exceção. Altera a linha com `notas[3]` assim: - -```java -try { - System.out.println("Quarta nota: " + notas[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Esta nota não existe."); -} -System.out.println("Pronto!"); -``` - -- `try` quer dizer “tenta”. O Java executa o bloco entre chavetas normalmente. -- Se uma linha lá dentro lançar uma exceção, o Java salta logo para o bloco `catch`. O resto do bloco `try` é saltado. -- Entre parênteses, depois de `catch`, está a exceção que é apanhada e um nome para ela, normalmente `e`. -- Depois, o programa continua normalmente. Agora também aparece “Pronto!”. - -Se tudo correr bem no bloco `try`, o bloco `catch` é simplesmente saltado. - -::: ludwig -try e catch são como a rede por baixo do trapézio no circo: enquanto tudo corre bem, nem dás por ela. Se alguém cair, a rede apanha-o e o espetáculo continua. -::: - -> No estrutograma não há nenhuma caixa para try e catch. Por isso o VisuStruct mostra o bloco inteiro como uma instrução. Só o podes executar com o Java verdadeiro. - -::: angeberwissen -Em julho de 1969, a Apollo 11 aterrou na Lua. Pouco antes da aterragem, o computador de bordo mostrou de repente os erros **1202** e **1201**: tinha mais trabalho do que conseguia fazer. Mesmo assim, não falhou. A equipa de **Margaret Hamilton** tinha construído o software de forma a reconhecer esses erros, deixar de lado as tarefas pouco importantes e continuar só com as mais importantes. A aterragem correu bem. - -Margaret Hamilton dirigia no MIT o grupo que escreveu o software das naves Apollo. Ela contava sempre com a possibilidade de algo correr mal, tal como tu fazes com `try` e `catch`. Para que o software fosse levado tão a sério como o hardware, chamou ao seu trabalho **software engineering** (engenharia de software). Hoje, uma área inteira da informática tem esse nome. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (nascida em 1936) em 1969, ao lado dos programas impressos que a sua equipa escreveu para a Apollo (foto: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), domínio público). -::: - -## Transformar texto num número - -Muitas vezes um número começa por ser texto, por exemplo mais tarde num campo de texto numa janela. `Integer.parseInt` transforma texto num número inteiro: - -```java -String entrada = "doze"; -try { - int numero = Integer.parseInt(entrada); - System.out.println("Dobro: " + numero * 2); -} catch (NumberFormatException e) { - System.out.println("Isto não é um número: " + entrada); -} -``` - -O Java não consegue converter “doze” e lança uma `NumberFormatException`. O programa escreve “Isto não é um número: doze”. Muda a primeira linha para `String entrada = "12";` e executa outra vez: agora aparece “Dobro: 24”. - -## As exceções são objetos - -Isto não está no programa da disciplina, mas é útil saber. - -::: angeberwissen -Cada exceção é um objeto de uma classe própria, por exemplo `ArithmeticException` para a divisão por 0. `e.getMessage()` dá-te a sua mensagem, por exemplo `/ by zero`. - -Depois de um `try` podem vir vários blocos `catch`, um para cada tipo de erro. `catch (Exception e)` apanha todas as exceções, mas diz-te menos sobre o que aconteceu. E um bloco `finally { … }` no fim corre sempre, quer tenha havido erro quer não. -::: - -## Jogo: A rede apanha? - -::: spiel frage -titel: Jogo: A rede apanha? -intro: O Ludwig anda no trapézio. O try e o catch apanham-no, ou o programa vai abaixo? Sete rondas. - -runde: - int[] notas = {2, 3, 1}; - System.out.println(notas[3]); - System.out.println("Pronto!"); -frage: O que acontece com Java a sério? -- Aparece 1 e Pronto! -- Só aparece Pronto! -- [x] O programa vai abaixo com uma ArrayIndexOutOfBoundsException. -> A posição 3 não existe. Ninguém apanha a exceção, por isso Pronto! nunca chega a aparecer. - -runde: - int[] notas = {2, 3, 1}; - try { - System.out.println(notas[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Esta nota não existe."); - } - System.out.println("Pronto!"); -frage: O que aparece? -- Pronto! -- [x] Esta nota não existe.\nPronto! -- Esta nota não existe. -> O bloco catch apanha a exceção, e depois o programa continua normalmente. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: O que aparece? -- A\nB\nC -- [x] A\nC -- C -> Em 10 / 0, o Java salta logo para o bloco catch. O resto do bloco try, o B, é saltado. - -runde: - String entrada = "12"; - try { - int numero = Integer.parseInt(entrada); - System.out.println("Dobro: " + numero * 2); - } catch (NumberFormatException e) { - System.out.println("Isto não é um número: " + entrada); - } -frage: O que aparece? -- [x] Dobro: 24 -- Isto não é um número: 12 -- Dobro: 1212 -> "12" pode ser convertido. Se tudo correr bem no bloco try, o catch é saltado. - -runde: -frage: Que exceção lança `Integer.parseInt("2.5")`? -- ArithmeticException -- [x] NumberFormatException -- nenhuma, o resultado é 2 -> parseInt só converte números inteiros. Com um ponto, o formato não é válido. - -runde: -frage: Que exceção lança `12 / 0` com `int`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Dividir por 0 não funciona com números inteiros. A mensagem é `/ by zero`. - -runde: - try { - int fatias = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Erro!"); - } - System.out.println("Pronto!"); -frage: O que acontece? -- Aparece Erro! e Pronto! -- Só aparece Pronto! -- [x] O programa vai abaixo. -> O catch só apanha a exceção que está entre parênteses. Uma ArithmeticException cai pela rede. -::: - -## Tarefa: dividir em segurança - -Este programa falha: - -```java -int bolo = 12; -int pessoas = 0; -System.out.println("Cada um recebe " + bolo / pessoas + " fatias."); -System.out.println("Bom apetite!"); -``` - -1. Executa-o com o Java verdadeiro e lê a mensagem de erro. Que exceção é? -2. Apanha-a com try e catch. Em vez da falha deve aparecer “Sem pessoas não se pode dividir.”, e “Bom apetite!” deve aparecer na mesma. -3. Põe `pessoas` a 4 e verifica que aparece “Cada um recebe 3 fatias.”. - -::: ludwig -A exceção chama-se `ArithmeticException`. Põe no bloco `try` só a linha que pode falhar. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ligar o Java verdadeiro na área Código. → Ligar o Java verdadeiro -- Consigo ler numa mensagem de erro a exceção e a linha. → Ler a mensagem de erro -- Consigo apanhar uma exceção com try e catch. → try e catch -- Consigo transformar texto num número com Integer.parseInt e apanhar a NumberFormatException. → Transformar texto num número -- Consigo escrever um programa que divide em segurança, mesmo por 0. → Tarefa: dividir em segurança -::: - -## Teste de conhecimentos - -::: quiz -? O que acontece quando uma exceção não é apanhada? -→ Ler a mensagem de erro -- [ ] O Java salta a linha e continua. -- [x] O programa para. -- [ ] O computador reinicia. -> É por isso que no primeiro exemplo “Pronto!” não aparece. - -? O que acontece no bloco try depois da linha que lança uma exceção? -→ try e catch -- [ ] As restantes linhas correm na mesma. -- [x] O resto é saltado e o Java salta para o bloco catch. -- [ ] O Java recomeça o bloco try. - -? Que exceção lança Integer.parseInt("doze")? -→ Transformar texto num número -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> “doze” não tem um formato de número que o Java entenda. - -? Associa cada exceção à sua causa. -→ Ler a mensagem de erro -= ArrayIndexOutOfBoundsException -> índice fora do array -= ArithmeticException -> divisão por 0 -= NumberFormatException -> o texto não é um número -::: diff --git a/web/lessons/pt/23-text-einlesen-und-speichern.md b/web/lessons/pt/23-text-einlesen-und-speichern.md deleted file mode 100644 index 8733244d..00000000 --- a/web/lessons/pt/23-text-einlesen-und-speichern.md +++ /dev/null @@ -1,254 +0,0 @@ ---- -titel: Ler e guardar texto -bereich: Ficheiros -kurz: Ler um ficheiro de texto linha a linha com Scanner e guardar um ficheiro com PrintWriter. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar porque é que os programas guardam dados em ficheiros -- ler um ficheiro de texto linha a linha com Scanner, hasNextLine e nextLine -- explicar porque é que o Java exige try e catch ou throws com ficheiros -- escrever um ficheiro com PrintWriter e terminá-lo com close -- ler um ficheiro, completá-lo e voltar a guardá-lo numerado -::: - -## Porquê ficheiros? - -Tudo o que está em variáveis desaparece assim que o programa termina. Se um programa deve lembrar-se de alguma coisa, por exemplo uma pontuação, uma lista ou notas, guarda os dados num **ficheiro**. Na próxima vez que arranca, volta a ler o ficheiro. - -A Zora está livre, e o Ludwig está a preparar uma festa. A lista de compras dele está pronta no ficheiro `compras.txt`: - -::: datei compras.txt -Feno -Maçãs -Cenouras -Dentes-de-leão -::: - -Um ficheiro com a extensão `.txt` contém só texto, linha a linha. Podes abri-lo com qualquer editor, por exemplo o Bloco de Notas no Windows ou o TextEdit no Mac. - -## Ler um ficheiro com Scanner - -Já conheces o `Scanner`: `new Scanner(System.in)` lê do teclado. Se lhe deres um ficheiro em vez de `System.in`, ele lê do ficheiro: - -```java -import java.io.*; -import java.util.Scanner; - -Scanner ficheiro = new Scanner(new File("compras.txt")); -while (ficheiro.hasNextLine()) { - String linha = ficheiro.nextLine(); - System.out.println(linha); -} -ficheiro.close(); -``` - -| Método | Significado | -|---|---| -| `new File("compras.txt")` | o ficheiro com este nome | -| `hasNextLine()` | Ainda há mais uma linha? `true` ou `false` | -| `nextLine()` | devolve a linha seguinte e avança uma linha | -| `close()` | fecha o ficheiro quando terminas | - -O ciclo repete-se enquanto houver mais uma linha. Em cada volta, `nextLine()` vai buscar a linha seguinte. `File` pertence ao pacote `java.io`, por isso precisas de `import java.io.*;`. - -::: diagramm dt-lesen.visustruct -É assim que a leitura fica num estrutograma. Experimenta na simulação. -::: - -::: ludwig -Imagina o Scanner como um dedo a apontar para uma linha. `nextLine()` lê a linha debaixo do dedo e desce o dedo uma linha. Quando o dedo já está abaixo da última linha, `hasNextLine()` devolve `false`. -::: - -## E se o ficheiro não existir? - -Se o ficheiro não existir, o Java não o consegue ler e lança uma `FileNotFoundException`. O Java exige que o teu programa conte com isso. Ou apanhas a exceção com try e catch, como na lição 22: - -```java -try { - Scanner ficheiro = new Scanner(new File("compras.txt")); - // ... ler ... - ficheiro.close(); -} catch (FileNotFoundException e) { - System.out.println("Falta o ficheiro compras.txt!"); -} -``` - -Ou escreves `throws Exception` a seguir a `main`. Isso quer dizer: "Este método pode lançar uma exceção, eu não trato dela." Nesse caso, o programa para se o ficheiro não existir. - -```java -public static void main(String[] args) throws Exception { -``` - -> O VisuStruct escreve `throws Exception` a seguir a `main` automaticamente assim que o teu programa trabalha com ficheiros. Na simulação, o VisuStruct indica um ficheiro em falta com "Não existe nenhum ficheiro chamado …". - -## Exercício: Numerar a lista de compras - -::: übung l23-java-nummern -titel: Numerar a lista de compras -code: java -auftrag: Lê `compras.txt` linha a linha e mostra cada linha com o seu número: **1. Feno**, **2. Maçãs** e assim por diante. -start: l23-java-nummern-start.visustruct -test: - -> 1. Feno | 2. Maçãs | 3. Cenouras | 4. Dentes-de-leão -braucht: whileSchleife -tipp: Antes do ciclo, cria `int numero = 1;` e aumenta o número no fim de cada volta. -tipp: A saída é `System.out.println(numero + ". " + linha);` -lösung: l23-java-nummern-loesung.visustruct -::: - -## Escrever num ficheiro com PrintWriter - -Para escrever existe o `PrintWriter`. Os seus métodos têm os mesmos nomes que os de `System.out`: - -```java -PrintWriter saida = new PrintWriter("saudacao.txt"); -saida.println("Olá Zora!"); -saida.println("Até amanhã, Ludwig"); -saida.close(); -``` - -Depois disto, `saudacao.txt` contém: - -``` -Olá Zora! -Até amanhã, Ludwig -``` - -- `println` escreve o texto e começa depois uma nova linha, `print` escreve sem nova linha. -- Se o ficheiro já existir, é **substituído**. O conteúdo antigo desaparece. -- Depois da execução, o VisuStruct mostra por baixo da saída o que foi guardado no ficheiro. - -::: ludwig -Não te esqueças do `close()`! O PrintWriter junta primeiro o texto numa memória intermédia e só o escreve no ficheiro quando é fechado. Sem `close()` o ficheiro fica vazio. Já perdi assim uma história inteira. -::: - -## Exercício: Completar a lista de compras - -::: übung l23-java-ergaenzen -titel: Completar a lista de compras -code: java -auftrag: O Ludwig ainda precisa de bolo. Lê `compras.txt` e escreve cada linha no novo ficheiro `compras-nova.txt`. Depois acrescenta **Bolo** como última linha. No fim, mostra quantas linhas foram guardadas: **5 linhas guardadas**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 linhas guardadas -datei: compras-nova.txt -> Feno | Maçãs | Cenouras | Dentes-de-leão | Bolo -braucht: whileSchleife -tipp: No ciclo, lês uma linha com `ficheiro.nextLine()` e escreve-la logo a seguir com `saida.println(linha)`. -tipp: Depois do ciclo vem `saida.println("Bolo");`. Fecha os dois ficheiros com `close()`, senão `compras-nova.txt` fica vazio. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Jogo: O que fica no ficheiro? - -::: spiel frage -titel: Jogo: O que fica no ficheiro? -intro: O Ludwig escreve ficheiros com o PrintWriter. Lê o código e prevê o que fica no ficheiro. Seis rondas. -frage: O que fica depois no ficheiro? - -runde: - PrintWriter saida = new PrintWriter("saudacao.txt"); - saida.println("Olá"); - saida.println("Zora"); - saida.close(); -- [x] Olá\nZora -- OláZora -- Zora -> `println` começa uma nova linha depois de cada texto. - -runde: - PrintWriter saida = new PrintWriter("palavra.txt"); - saida.print("Bo"); - saida.print("la"); - saida.println("!"); - saida.close(); -- Bo\nla\n! -- [x] Bola! -- Bo la ! -> `print` continua a escrever sem nova linha, por isso as partes ficam coladas. - -runde: - PrintWriter saida = new PrintWriter("segredo.txt"); - saida.println("O tesouro está na torre"); -- O tesouro está na torre -- [x] Nada, o ficheiro fica vazio. -- O Java indica um erro. -> Sem `close()` o texto nunca chega ao ficheiro. Fica na memória intermédia. - -runde: - // Antes, nota.txt contém: Antigo - PrintWriter saida = new PrintWriter("nota.txt"); - saida.println("Novo"); - saida.close(); -- Antigo\nNovo -- [x] Novo -- Novo\nAntigo -> Um novo PrintWriter substitui o ficheiro. O conteúdo antigo desaparece. - -runde: - PrintWriter saida = new PrintWriter("numeros.txt"); - for (int i = 1; i <= 3; i++) { - saida.println(i * 2); - } - saida.close(); -- 1\n2\n3 -- 246 -- [x] 2\n4\n6 -> O ciclo escreve 2, 4 e 6, cada número na sua linha. - -runde: - // compras.txt contém: Feno, Maçãs (uma por linha) - Scanner ficheiro = new Scanner(new File("compras.txt")); - PrintWriter saida = new PrintWriter("grande.txt"); - while (ficheiro.hasNextLine()) { - saida.println(ficheiro.nextLine().toUpperCase()); - } - ficheiro.close(); - saida.close(); -- Feno\nMaçãs -- [x] FENO\nMAÇÃS -- FENOMAÇÃS -> Cada linha é lida, passada para maiúsculas e escrita na sua própria linha. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar porque é que os programas guardam dados em ficheiros. → Porquê ficheiros? -- Consigo ler um ficheiro de texto linha a linha com Scanner, hasNextLine e nextLine. → Ler um ficheiro com Scanner -- Consigo explicar porque é que o Java exige try e catch ou throws com ficheiros. → E se o ficheiro não existir? -- Consigo escrever um ficheiro com PrintWriter e terminá-lo com close(). → Escrever num ficheiro com PrintWriter -- Consigo ler um ficheiro, completá-lo e voltar a guardá-lo numerado. → Exercício: Completar a lista de compras -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve hasNextLine()? -→ Ler um ficheiro com Scanner -- [ ] a linha seguinte -- [x] true se ainda houver uma linha, senão false -- [ ] o número de linhas -> Com hasNextLine() o ciclo verifica se continua. - -? O que acontece se chamares new PrintWriter("lista.txt") para um ficheiro que já existe? -→ Escrever num ficheiro com PrintWriter -- [ ] O texto novo é acrescentado no fim. -- [x] O ficheiro é substituído. -- [ ] O Java indica um erro. - -? Porque é que um ficheiro fica vazio, apesar de println ter sido chamado? -→ Escrever num ficheiro com PrintWriter -- [ ] Porque println só escreve na consola. -- [x] Porque falta o close(). -- [ ] Porque o nome do ficheiro termina em .txt. -> Só o close() escreve a memória intermédia no ficheiro. - -? Liga cada método à sua tarefa. -→ Ler um ficheiro com Scanner -= nextLine() -> lê a linha seguinte -= println(texto) -> escreve uma linha -= close() -> fecha o ficheiro -::: diff --git a/web/lessons/pt/24-zeile-fuer-zeile.md b/web/lessons/pt/24-zeile-fuer-zeile.md deleted file mode 100644 index 976eae23..00000000 --- a/web/lessons/pt/24-zeile-fuer-zeile.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -titel: Linha a linha com BufferedReader -bereich: Ficheiros -kurz: Ler com BufferedReader e readLine até chegar null, e ler um ficheiro inteiro de uma vez com Files. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ler um ficheiro com BufferedReader e readLine -- reconhecer o fim de um ficheiro, porque readLine aí devolve null -- contar quantas vezes uma palavra aparece num ficheiro -- ler um ficheiro inteiro com Files.readString e dividi-lo em linhas com split -- encontrar a entrada mais longa de um ficheiro -::: - -## O bolo desapareceu! - -Na manhã a seguir à festa, o bolo do Ludwig desapareceu. Felizmente, o castelo tem um diário. Cada linha começa com o dia e a hora: - -::: datei diario.txt -Seg 08:15 Ludwig alimenta as cabras -Seg 12:30 Zora lê na torre -Seg 18:00 Raposa passa às escondidas pelo portão -Ter 09:00 Ludwig faz um bolo -Ter 14:45 Lebre traz cenouras -Ter 16:20 Raposa pergunta pela receita -Ter 23:55 O bolo desapareceu -Qua 07:00 Zora encontra migalhas junto à toca da Raposa -::: - -Já o conseguias ler com o Scanner da lição 23. Mas, para ficheiros, os profissionais usam muitas vezes o **BufferedReader**. - -## Ler com BufferedReader - -```java -import java.io.*; - -BufferedReader leitor = new BufferedReader(new FileReader("diario.txt")); -String linha = leitor.readLine(); -while (linha != null) { - System.out.println(linha); - linha = leitor.readLine(); -} -leitor.close(); -``` - -- `new FileReader("diario.txt")` abre o ficheiro, e `new BufferedReader(…)` põe-lhe uma memória intermédia à volta. Vai buscar o ficheiro em pedaços grandes e por isso é mais rápido com ficheiros longos. -- `readLine()` devolve a linha seguinte. Quando já não há linhas, devolve **null**. -- Por isso, o ciclo repete-se enquanto `linha != null`. No fim de cada volta lê a linha seguinte. - -::: diagramm dt-buffered.visustruct -Primeiro ler uma linha, depois verificar, e no fim da volta ler a seguinte. -::: - -**null** quer dizer "nada". Não é o texto vazio `""`, mas nenhum texto. Uma variável com `null` não tem métodos: `linha.length()` para então com uma `NullPointerException`. Por isso, verifica sempre primeiro `linha != null`. - -| | Scanner | BufferedReader | -|---|---|---| -| Criar | `new Scanner(new File(…))` | `new BufferedReader(new FileReader(…))` | -| Ainda há mais uma linha? | `hasNextLine()` | `readLine()` devolve `null` | -| Linha seguinte | `nextLine()` | `readLine()` | -| Ponto forte | também lê números (`nextInt()`) | rápido, mesmo com ficheiros grandes | - -::: ludwig -O ciclo é diferente do ciclo com o Scanner: a primeira linha é lida **antes** do ciclo, e cada uma das seguintes **no fim** da volta. Se te esqueceres da leitura no fim, o programa lê sempre a mesma linha e nunca para. -::: - -::: angeberwissen -O fim de uma linha não é igual em todo o lado: o Windows escreve dois carateres invisíveis, “retorno do carro” e “avanço de linha”, o Linux e o macOS só o avanço de linha. Os nomes vêm da máquina de escrever, em que o carro voltava atrás e o papel subia uma linha. O `readLine()` funciona com os dois. -::: - -## Exercício: Quantas vezes aparece a Raposa? - -::: übung l24-java-fuchs -titel: Quantas vezes aparece a Raposa? -code: java -auftrag: Lê o diário com o BufferedReader e conta as linhas onde aparece **Raposa**. No fim, mostra **Raposa:** e o número. -start: l24-java-fuchs-start.visustruct -test: - -> Raposa: 3 -braucht: whileSchleife, verzweigung -tipp: Antes do ciclo, cria `int quantidade = 0;`. No ciclo verificas `linha.contains("Raposa")`. -tipp: O `linha = leitor.readLine();` fica no fim do ciclo, depois do if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Fechar automaticamente - -Para que o `close()` não seja esquecido, mesmo depois de uma exceção, existe uma forma curta do try: o que é criado entre os parênteses a seguir a `try` é fechado pelo Java sozinho no fim. - -```java -try (BufferedReader leitor = new BufferedReader(new FileReader("diario.txt"))) { - String linha = leitor.readLine(); - while (linha != null) { - System.out.println(linha); - linha = leitor.readLine(); - } -} catch (IOException e) { - System.out.println("Falta o diário."); -} -``` - -Isto chama-se **try-with-resources**. `IOException` apanha todos os erros ao ler e escrever, incluindo um ficheiro em falta. - -> Tal como try e catch, o try-with-resources só funciona com Java verdadeiro (lição 22). Na simulação, escreves tu o `close()`. - -## O atalho: Files - -Desde o Java 11 é ainda mais curto. `Files.readString` lê o ficheiro inteiro de uma vez, como já conheces da lição 17. Com `split("\n")` divides o texto num array de linhas: - -```java -import java.nio.file.*; - -String texto = Files.readString(Path.of("diario.txt")); -String[] linhas = texto.split("\n"); -for (int i = 0; i < linhas.length; i++) { - System.out.println(linhas[i]); -} -``` - -Agora todas as linhas estão no array. Podes vê-las pela ordem que quiseres, por exemplo de trás para a frente ou só uma sim, uma não. - -| Forma | Boa para | -|---|---| -| `Scanner` | ficheiros pequenos, ler números e palavras | -| `BufferedReader` | ficheiros grandes, linha a linha | -| `Files.readString` | ficheiros pequenos que cabem inteiros no programa | - -::: ludwig -Um ficheiro com um milhão de linhas é melhor lido linha a linha com o BufferedReader. Com `Files.readString`, o ficheiro inteiro teria de ir de uma vez para a memória. -::: - -## Exercício: A entrada mais longa - -::: übung l24-java-laengster -titel: A entrada mais longa -code: java -auftrag: No código inicial, as linhas do diário já estão no array `linhas`. Encontra a linha mais longa e mostra-a assim: **Entrada mais longa:** seguido da linha. -start: l24-java-laengster-start.visustruct -test: - -> Entrada mais longa: Qua 07:00 Zora encontra migalhas junto à toca da Raposa -braucht: forSchleife, verzweigung -tipp: Guarda a linha mais longa até agora em `String maisLonga = linhas[0];` e percorre as outras linhas com um ciclo for. -tipp: Se `linhas[i].length() > maisLonga.length()`, então `linhas[i]` passa a ser a mais longa. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Jogo: Detetive de ficheiros - -::: spiel frage -titel: Jogo: Detetive de ficheiros -intro: Quem levou o bolo? Cada programa procura no **diário** a partir do início. Prevê o que mostra e junta as pistas. Cinco rondas. -frage: O que mostra o programa? - -runde: - String linha = leitor.readLine(); - linha = leitor.readLine(); - System.out.println(linha.substring(10)); -- Ludwig alimenta as cabras -- [x] Zora lê na torre -- Seg 12:30 Zora lê na torre -> O segundo `readLine()` devolve a segunda linha. O texto depois da hora começa no índice 10. - -runde: - int n = 0; - String linha = leitor.readLine(); - while (linha != null) { - if (linha.startsWith("Ter")) n++; - linha = leitor.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Quatro entradas são de terça-feira. - -runde: - String linha = leitor.readLine(); - while (linha != null) { - if (linha.contains("bolo")) { - System.out.println(linha.substring(4, 9)); - } - linha = leitor.readLine(); - } -- [x] 09:00\n23:55 -- 09:00 -- Ter\nTer -> Há duas linhas com "bolo". `substring(4, 9)` recorta a hora. - -runde: - String linha = leitor.readLine(); - String ultima = ""; - while (linha != null) { - if (linha.contains("Raposa")) ultima = linha; - linha = leitor.readLine(); - } - System.out.println(ultima); -- Ter 16:20 Raposa pergunta pela receita -- [x] Qua 07:00 Zora encontra migalhas junto à toca da Raposa -- Seg 18:00 Raposa passa às escondidas pelo portão -> `ultima` é substituída a cada resultado. No fim fica lá o último. "toca da Raposa" também contém "Raposa". - -runde: - String linha = leitor.readLine(); - while (linha != null) { - if (linha.contains("migalhas")) { - int junto = linha.indexOf("junto à "); - System.out.println(linha.substring(junto + 8)); - } - linha = leitor.readLine(); - } -- [x] toca da Raposa -- junto à toca da Raposa -- migalhas -> As migalhas estão junto à toca da Raposa. A Raposa perguntou pela receita, e às 23:55 o bolo tinha desaparecido. Caso resolvido! -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ler um ficheiro com BufferedReader e readLine. → Ler com BufferedReader -- Consigo reconhecer o fim de um ficheiro, porque readLine aí devolve null (linha != null). → Ler com BufferedReader -- Consigo contar quantas vezes uma palavra aparece num ficheiro. → Exercício: Quantas vezes aparece a Raposa? -- Consigo ler um ficheiro inteiro com Files.readString e dividi-lo em linhas com split. → O atalho: Files -- Consigo encontrar a entrada mais longa de um ficheiro. → Exercício: A entrada mais longa -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve readLine() quando o ficheiro chegou ao fim? -→ Ler com BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null quer dizer: já não há mais texto. - -? Porque é que no fim do ciclo há outra vez linha = leitor.readLine()? -→ Ler com BufferedReader -- [ ] Para fechar o ficheiro. -- [x] Senão linha fica sempre igual e o ciclo nunca para. -- [ ] Para mostrar a primeira linha duas vezes. - -? O que faz o try-with-resources? -→ Ler com BufferedReader -- [ ] Lê o ficheiro mais depressa. -- [x] Fecha o ficheiro sozinho no fim. -- [ ] Impede qualquer exceção. - -? Liga cada forma àquilo para que é boa. -→ O atalho: Files -= BufferedReader -> ficheiros grandes, linha a linha -= Files.readString -> ficheiros pequenos de uma vez -= Scanner -> ler números e palavras -::: diff --git a/web/lessons/pt/25-tabellen-als-csv.md b/web/lessons/pt/25-tabellen-als-csv.md deleted file mode 100644 index 40a6bb69..00000000 --- a/web/lessons/pt/25-tabellen-als-csv.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -titel: Tabelas em CSV -bereich: Ficheiros -kurz: Ler uma tabela num ficheiro CSV, dividir cada linha com split e fazer contas com os números. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar como é feito um ficheiro CSV: cabeçalho, registos, separador -- dividir uma linha com split e fazer dela um número com Integer.parseInt -- saltar o cabeçalho antes de fazer contas -- calcular uma média a partir de uma coluna de um ficheiro CSV -- encontrar o melhor valor numa coluna de um ficheiro CSV -- nomear um ponto forte e um ponto fraco do CSV -::: - -## Uma tabela num ficheiro de texto - -A professora Cabra, da escola do castelo, guardou as notas da turma numa tabela. Como na Alemanha, 1 é a melhor nota e 6 a pior: - -| Nome | Matemática | Alemão | Inglês | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Raposa | 4 | 3 | 5 | -| Lebre | 2 | 1 | 3 | - -No ficheiro, a tabela fica assim: - -::: datei turma.csv -Nome;Matemática;Alemão;Inglês -Ludwig;2;3;1 -Zora;1;2;2 -Raposa;4;3;5 -Lebre;2;1;3 -::: - -Isto é um **ficheiro CSV**. CSV quer dizer "Comma-Separated Values", ou seja, "valores separados por vírgulas". Cada linha é uma linha da tabela, e as colunas estão separadas por um carácter separador. Em Portugal e na Alemanha usa-se normalmente o **ponto e vírgula** `;`, porque a vírgula já aparece em números como `2,5`. - -- A primeira linha é o **cabeçalho**. Diz o que está em cada coluna. -- Cada uma das linhas seguintes é um **registo**, aqui um aluno ou uma aluna. -- O Excel, o LibreOffice e o Numbers abrem ficheiros CSV diretamente como tabela. - -## Dividir uma linha com split - -Já conheces o `split(";")` da lição 17. Divide um texto em cada ponto e vírgula e devolve um array: - -```java -String linha = "Zora;1;2;2"; -String[] partes = linha.split(";"); -// partes[0] é "Zora", partes[1] é "1", partes[2] é "2", partes[3] é "2" -``` - -Depois disto, as notas ainda são **texto**. Só podes fazer contas com elas depois de as transformares num número com `Integer.parseInt`: - -```java -int matematica = Integer.parseInt(partes[1]); -``` - -::: diagramm dt-csv.visustruct -Dividir uma linha CSV e ler a nota de Matemática como número. -::: - -## Ler o ficheiro inteiro - -Para o ficheiro inteiro, combinas o BufferedReader da lição 24 com `split`. O cabeçalho não tem notas, por isso lês essa linha antes do ciclo e saltas por cima dela: - -```java -BufferedReader leitor = new BufferedReader(new FileReader("turma.csv")); -String linha = leitor.readLine(); // cabeçalho -linha = leitor.readLine(); // primeiro registo -while (linha != null) { - String[] partes = linha.split(";"); - System.out.println(partes[0] + ": Matemática " + partes[1]); - linha = leitor.readLine(); -} -leitor.close(); -``` - -::: ludwig -Sem o segundo `readLine()` antes do ciclo, o cabeçalho vai parar ao ciclo. Aí, `Integer.parseInt("Matemática")` tenta fazer um número a partir da palavra, e o Java para com uma `NumberFormatException`. -::: - -## Exercício: Média de Matemática - -::: übung l25-java-schnitt -titel: Média de Matemática -code: java -auftrag: Calcula a média das notas de Matemática. Salta o cabeçalho, soma as notas de Matemática (coluna 1) e divide pelo número de registos. Mostra **Média de Matemática:** e a média. -start: l25-java-schnitt-start.visustruct -test: - -> Média de Matemática: 2.25 -braucht: whileSchleife -tipp: Antes do ciclo, cria `double soma = 0;` e `int quantidade = 0;`. Com `double`, nada é cortado na divisão. -tipp: No ciclo: `String[] partes = linha.split(";");`, depois `soma = soma + Integer.parseInt(partes[1]);` e `quantidade++;`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Exercício: Melhor nota a Inglês - -::: übung l25-java-beste -titel: Melhor nota a Inglês -code: java -auftrag: Quem tem a melhor nota a Inglês (coluna 3)? Mostra **Melhor nota a Inglês:**, o nome e, entre parênteses, a nota. -start: l25-java-beste-start.visustruct -test: - -> Melhor nota a Inglês: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Guarda a melhor nota até agora em `int melhorNota = 7;` e o nome em `String melhorNome = "";`. Qualquer nota verdadeira é melhor do que 7. -tipp: Se `nota < melhorNota`, guardas a nova nota e `partes[0]` como nome. -lösung: l25-java-beste-loesung.visustruct -::: - -## Pontos fortes e fracos do CSV - -| Bom | Difícil | -|---|---| -| As tabelas ocupam pouco espaço. | Se o separador aparecer no texto, por exemplo `Lebre;muito bem; esforçada`, a linha parte-se em partes a mais. | -| O Excel e outros programas abrem o ficheiro diretamente. | Tudo é uma tabela plana. Se um animal tiver várias comidas preferidas, isso não cabe bem numa célula. | -| Qualquer programa o consegue ler com `split`. | O ficheiro não diz se `1` é um número ou um texto. | - -::: ludwig -O Excel guarda ficheiros CSV com ponto e vírgula em Portugal e na Alemanha, mas com vírgula em Inglaterra e nos EUA. Por isso, quem abre um ficheiro de outro país às vezes vê tudo numa única coluna. -::: - -## Jogo: Linha estragada - -::: spiel bombe -titel: Jogo: Linha estragada -intro: Em cada ficheiro CSV há **exatamente uma linha estragada**, e a bomba está a contar. Compara cada linha com o cabeçalho e toca na linha estragada antes que o tempo acabe. Cinco rondas, 20 segundos cada. -zeit: 20 -daten: csv - -runde: - Nome;Matemática;Alemão;Inglês - Ludwig;2;3;1 - Zora;1 - Raposa;4;3;5 -frage: Cada linha precisa de quatro colunas. -fehler: 3 -besser: Zora;1;2;2 -> Faltam duas notas à Zora. Então `partes[3]` não existe, e o Java para com uma `ArrayIndexOutOfBoundsException`. - -runde: - Nome;Nota;Comentário - Ludwig;2;trabalha bem em equipa - Lebre;1;muito bem; esforçada - Zora;1;lê muito -frage: Cada linha precisa de três colunas. -fehler: 3 -besser: Lebre;1;muito bem, esforçada -> O ponto e vírgula no comentário separa uma quarta coluna. O separador não pode aparecer no texto. - -runde: - Nome;Matemática;Alemão;Inglês - Ludwig;2;3;1 - Zora;1;2;2 - Raposa,4,3,5 -frage: O separador é o ponto e vírgula. -fehler: 4 -besser: Raposa;4;3;5 -> Aqui há vírgulas em vez de pontos e vírgulas. `split(";")` não encontra nenhum ponto e vírgula e devolve a linha inteira como uma única parte. - -runde: - Nome;Matemática;Alemão;Inglês - Lebre;2;1;3 - Raposa;quatro;3;5 - Ludwig;2;3;1 -frage: As notas são números. -fehler: 3 -besser: Raposa;4;3;5 -> `Integer.parseInt("quatro")` não consegue fazer um número a partir da palavra e lança uma `NumberFormatException`. - -runde: - Nome;Matemática;Alemão;Inglês - Zora;1;2;2 - Lebre;2;1;3 - Ludwig;2;;1 -frage: Nenhum campo pode estar vazio. -fehler: 4 -besser: Ludwig;2;3;1 -> Entre os dois pontos e vírgulas não há nada. `Integer.parseInt("")` lança uma `NumberFormatException`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar como é feito um ficheiro CSV: cabeçalho, registos, separador. → Uma tabela num ficheiro de texto -- Consigo dividir uma linha com split e fazer dela um número com Integer.parseInt. → Dividir uma linha com split -- Consigo saltar o cabeçalho antes de fazer contas. → Ler o ficheiro inteiro -- Consigo calcular uma média a partir de uma coluna de um ficheiro CSV. → Exercício: Média de Matemática -- Consigo encontrar o melhor valor numa coluna de um ficheiro CSV. → Exercício: Melhor nota a Inglês -- Consigo nomear um ponto forte e um ponto fraco do CSV. → Pontos fortes e fracos do CSV -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve "Lebre;2;1;3".split(";")[2]? -→ Dividir uma linha com split -- [ ] "2" -- [x] "1" -- [ ] 1 -> A contagem começa em 0. E o resultado é um texto, não um número. - -? Porque é que o programa chama readLine() duas vezes antes do ciclo? -→ Ler o ficheiro inteiro -- [ ] Para ler o ficheiro mais depressa. -- [x] A primeira linha é o cabeçalho, sem notas. É saltada. -- [ ] Porque readLine precisa sempre de duas linhas. - -? O que acontece com Integer.parseInt("Matemática")? -→ Dividir uma linha com split -- [ ] Devolve 0. -- [x] O Java lança uma NumberFormatException. -- [ ] Devolve o comprimento da palavra. - -? Liga. -→ Pontos fortes e fracos do CSV -= cabeçalho -> diz o que está nas colunas -= registo -> uma linha com valores -= separador -> separa as colunas, muitas vezes ; -::: diff --git a/web/lessons/pt/26-json.md b/web/lessons/pt/26-json.md deleted file mode 100644 index 8517db4f..00000000 --- a/web/lessons/pt/26-json.md +++ /dev/null @@ -1,225 +0,0 @@ ---- -titel: Dados em JSON -bereich: Ficheiros -kurz: Perceber ficheiros JSON, tirar valores com indexOf e substring e escrever JSON tu mesmo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- reconhecer objetos, chaves, valores e listas em JSON -- tirar um valor de uma linha JSON com indexOf e substring -- mostrar todos os valores de uma chave de um ficheiro JSON -- construir e guardar uma entrada JSON com + e \" -- guardar dados num ficheiro JSON -- nomear um ponto forte e um ponto fraco do JSON -::: - -## Dados com nomes - -O zoo do castelo guarda os seus animais num ficheiro `zoo.json`: - -::: datei zoo.json -{ - "zoo": "Zoo do Castelo", - "animais": [ - {"nome": "Ludwig", "especie": "bode", "idade": 7}, - {"nome": "Zora", "especie": "cabra", "idade": 6}, - {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4}, - {"nome": "Lebre", "especie": "lebre", "idade": 2} - ] -} -::: - -Isto é **JSON**, a "JavaScript Object Notation". Quase todas as aplicações web e todos os jogos enviam e guardam dados assim. Ao contrário de um ficheiro CSV, antes de cada valor está o seu **nome**, a **chave**. - -| Carácter | Significado | Exemplo | -|---|---|---| -| `{ … }` | um **objeto**: pares de chave e valor | `{"nome": "Zora", "idade": 6}` | -| `"chave": valor` | um par, separado por dois pontos | `"idade": 6` | -| `[ … ]` | uma **lista** de valores, como um array | `"animais": [ … ]` | -| `,` | separa os pares e as entradas de uma lista | | -| `"…"` | um texto. Números e `true`/`false` ficam sem aspas. | `"cabra"`, `6` | - -Objetos e listas podem ser **encaixados** uns nos outros: o objeto do zoo contém a lista `animais`, e cada animal é outra vez um objeto. Tal como nos arrays, a lista conta a partir de 0: `animais[1].nome` é `"Zora"`. - -::: angeberwissen -Foi Douglas Crockford quem tornou o JSON conhecido no início dos anos 2000. Na licença do seu programa de JSON escreveu: “O software deve ser usado para o Bem, não para o Mal.” Por isso, a empresa IBM perguntou se o podia usar na mesma. Ele respondeu que a IBM e os seus clientes também o podiam usar para o Mal. -::: - -## Tirar um valor - -Os profissionais leem JSON com uma biblioteca, por exemplo o **Gson**. Ela não faz parte do Java e tem de ser adicionada primeiro. Para o nosso ficheiro do zoo chegam os métodos de String da lição 17, porque cada animal está na sua própria linha. - -Em Java, escreves umas aspas dentro de um texto como `\"`. Assim, o texto `"\"nome\": \""` é `"nome": "`, exatamente o que está antes de cada nome. - -```java -String linha = "{\"nome\": \"Zora\", \"especie\": \"cabra\", \"idade\": 6},"; -int inicio = linha.indexOf("\"nome\": \"") + 9; -int fim = linha.indexOf("\"", inicio); -String nome = linha.substring(inicio, fim); -``` - -- `indexOf("\"nome\": \"")` encontra o início de `"nome": "`. São 9 caracteres, por isso o nome começa 9 caracteres mais à frente. -- `indexOf("\"", inicio)` procura as aspas seguintes **a partir de** `inicio`. É aí que o nome acaba. -- `substring(inicio, fim)` recorta o nome: `Zora`. - -::: diagramm dt-json.visustruct -Tirar o nome de uma linha JSON. -::: - -## Exercício: Todos os nomes do zoo - -::: übung l26-java-namen -titel: Todos os nomes do zoo -code: java -auftrag: Lê `zoo.json` linha a linha. Em cada linha que contém `"nome"`, mostra só o nome. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Raposa | Lebre -braucht: whileSchleife, verzweigung -tipp: Verifica com `linha.contains("\"nome\"")` se há um nome na linha. As outras linhas são saltadas. -tipp: Dentro do if: `int inicio = linha.indexOf("\"nome\": \"") + 9;`, depois `int fim = linha.indexOf("\"", inicio);` e `System.out.println(linha.substring(inicio, fim));`. -lösung: l26-java-namen-loesung.visustruct -::: - -## Escrever JSON - -Para escrever, juntas o texto com `+` e escreves-o no ficheiro com o PrintWriter da lição 23. Os textos em JSON precisam de aspas, portanto `\"` no texto Java, os números não: - -```java -String nome = "Zora"; -int pontos = 15; -String entrada = "{\"nome\": \"" + nome + "\", \"pontos\": " + pontos + "}"; -// entrada é {"nome": "Zora", "pontos": 15} -``` - -Numa lista há uma vírgula entre as entradas, mas **nenhuma** depois da última. Isso verificas com um if. - -::: ludwig -Umas aspas esquecidas ou uma vírgula a mais, e nenhum programa consegue ler o ficheiro. O JSON é rigoroso. Por isso, os profissionais também usam uma biblioteca para escrever. -::: - -## Exercício: Guardar pontos em JSON - -::: übung l26-java-schreiben -titel: Guardar pontos em JSON -code: java -auftrag: Guarda os pontos do torneio de perguntas como lista em `pontos.json`. Cada linha contém um objeto com **nome** e **pontos**, e entre os objetos há uma vírgula. No fim, mostra **3 entradas guardadas**. -start: l26-java-schreiben-start.visustruct -test: - -> 3 entradas guardadas -datei: pontos.json -> [ | {"nome": "Ludwig", "pontos": 12}, | {"nome": "Zora", "pontos": 15}, | {"nome": "Raposa", "pontos": 9} | ] -braucht: forSchleife, verzweigung -tipp: Percorre os arrays com `for (int i = 0; i < nomes.length; i++)`. Constrói a entrada com `" {\"nome\": \"" + nomes[i] + "\", \"pontos\": " + pontos[i] + "}"`. -tipp: Se `i < nomes.length - 1`, acrescentas ainda `","`. Depois do ciclo, escreve `"]"` e fecha o ficheiro. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Pontos fortes e fracos do JSON - -| Bom | Difícil | -|---|---| -| Cada valor tem um nome. Vê-se logo o que `6` quer dizer. | O ficheiro é mais longo do que um CSV, porque as chaves aparecem em todas as linhas. | -| Os dados podem ser encaixados, por exemplo uma lista dentro de um objeto. | O Java não lê JSON sozinho. É precisa uma biblioteca como o Gson. | -| O JSON é o padrão na web. Quase todas as linguagens de programação o conhecem. | Um carácter a mais ou a menos torna o ficheiro inteiro ilegível. | - -## Jogo: Qual é a chave? - -::: spiel frage -titel: Jogo: Qual é a chave? -intro: O Ludwig procura valores em ficheiros JSON. Lê o ficheiro e diz qual é o valor pedido. Cinco rondas. - -runde: - {"nome": "Zora", "especie": "cabra", "idade": 6} -frage: Qual é o valor de **idade**? -- "idade" -- [x] 6 -- "cabra" -> Antes dos dois pontos está a chave, depois está o valor. Os números ficam sem aspas. - -runde: - { - "animais": [ - {"nome": "Ludwig", "idade": 7}, - {"nome": "Zora", "idade": 6}, - {"nome": "Raposa", "idade": 4} - ] - } -frage: O que é **animais[1].nome**? -- Ludwig -- [x] Zora -- Raposa -> A lista conta a partir de 0, como um array. `animais[1]` é o segundo animal. - -runde: - { - "zoo": "Zoo do Castelo", - "chefe": {"nome": "Ludwig", "especie": "bode"} - } -frage: Como chegas a **bode**? -- [x] chefe.especie -- especie -- zoo.chefe.especie -> `especie` está dentro do objeto `chefe`. Por isso o caminho passa por `chefe`. - -runde: - { - "nome": "Lebre", - "comidaPreferida": ["cenouras", "trevo", "dentes-de-leão"] - } -frage: O que é **comidaPreferida[2]**? -- trevo -- cenouras -- [x] dentes-de-leão -> O índice 0 é cenouras, o 1 é trevo e o 2 é dentes-de-leão. Algo assim dificilmente cabe numa célula em CSV. - -runde: - {"nome": "Raposa", "idade": 4, "comFome": true} -frage: Que chave tem um valor lógico? -- nome -- idade -- [x] comFome -> `true` e `false` ficam sem aspas, como os números. Em Java isto seria um `boolean`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo reconhecer objetos, chaves, valores e listas em JSON. → Dados com nomes -- Consigo tirar um valor de uma linha JSON com indexOf e substring. → Tirar um valor -- Consigo mostrar todos os valores de uma chave de um ficheiro JSON. → Exercício: Todos os nomes do zoo -- Consigo construir e guardar uma entrada JSON com + e \". → Escrever JSON -- Consigo guardar dados num ficheiro JSON. → Exercício: Guardar pontos em JSON -- Consigo nomear um ponto forte e um ponto fraco do JSON. → Pontos fortes e fracos do JSON -::: - -## Teste de conhecimentos - -::: quiz -? O que está em JSON antes de cada valor? -→ Dados com nomes -- [ ] o número da linha -- [x] a sua chave, por exemplo "nome" -- [ ] um ponto e vírgula -> A chave e o valor estão separados por dois pontos. - -? Como escreves umas aspas dentro de um texto Java? -→ Escrever JSON -- [ ] "" -- [x] \" -- [ ] '"' - -? Em {"animais": [{"nome": "Ludwig"}, {"nome": "Zora"}]}, qual é o valor de animais[0].nome? -→ Dados com nomes -- [x] Ludwig -- [ ] Zora -- [ ] animais -> A lista conta a partir de 0. - -? Liga. -→ Pontos fortes e fracos do JSON -= { } -> um objeto com chaves e valores -= [ ] -> uma lista -= : -> separa a chave do valor -::: diff --git a/web/lessons/pt/27-formate-im-vergleich.md b/web/lessons/pt/27-formate-im-vergleich.md deleted file mode 100644 index b8ae2026..00000000 --- a/web/lessons/pt/27-formate-im-vergleich.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -titel: Comparar formatos -bereich: Ficheiros -kurz: Comparar os mesmos dados em txt, CSV e JSON, escolher o formato certo e converter um ficheiro noutro formato. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- reconhecer os mesmos dados em txt, CSV e JSON -- comparar pontos fortes e fracos de txt, CSV e JSON -- converter um ficheiro CSV em frases -- converter um ficheiro CSV em JSON -::: - -## Os mesmos animais três vezes - -O Ludwig guardou os animais do castelo em três ficheiros. Todos dizem o mesmo, só que escrito de outra maneira. - -Como texto, tal como se escreveria a uma pessoa: - -::: datei animais.txt -Ludwig (bode) tem 7 anos. -Zora (cabra) tem 6 anos. -Raposa (raposa-vermelha) tem 4 anos. -::: - -Como tabela em CSV: - -::: datei animais.csv -Nome;Espécie;Idade -Ludwig;bode;7 -Zora;cabra;6 -Raposa;raposa-vermelha;4 -::: - -Como JSON: - -::: datei animais.json -[ - {"nome": "Ludwig", "especie": "bode", "idade": 7}, - {"nome": "Zora", "especie": "cabra", "idade": 6}, - {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4} -] -::: - -## O que sabe fazer cada formato? - -| | txt | CSV | JSON | -|---|---|---|---| -| Fácil de ler para pessoas | sim | mais ou menos | mais ou menos | -| Ordem fixa para programas | não | sim, colunas | sim, chaves | -| O que quer dizer um valor? | está na frase | está no cabeçalho | está mesmo antes dele | -| Encaixar (uma lista dentro de uma entrada) | não | difícil | sim | -| Abrir no Excel | não | sim | não | -| Ler em Java | Scanner, BufferedReader | mais `split` | métodos de String ou uma biblioteca | -| Tamanho | pequeno | o mais pequeno | o maior | - -Em resumo: - -- **txt** para textos que pessoas leem: cartas, notas, um diário. -- **CSV** para tabelas: notas, medições, uma lista de recordes. Todas as linhas têm as mesmas colunas. -- **JSON** para dados com estrutura: jogos guardados, definições, tudo o que circula entre programas e na web. - -::: ludwig -Quando a tua aplicação de meteorologia vai buscar a temperatura, a resposta chega quase sempre em JSON. E quando um professor exporta as notas do programa da escola, recebe muitas vezes um ficheiro CSV. -::: - -## Exercício: De CSV para frases - -::: übung l27-java-saetze -titel: De CSV para frases -code: java -auftrag: Converte `animais.csv` em texto. Para cada animal, escreve uma frase como **Ludwig (bode) tem 7 anos.** no ficheiro `frases.txt`. No fim, mostra **3 frases guardadas**. -start: l27-java-saetze-start.visustruct -test: - -> 3 frases guardadas -datei: frases.txt -> Ludwig (bode) tem 7 anos. | Zora (cabra) tem 6 anos. | Raposa (raposa-vermelha) tem 4 anos. -braucht: whileSchleife -tipp: O código inicial já salta o cabeçalho. Divide cada linha com `String[] partes = linha.split(";");`. -tipp: A frase é `partes[0] + " (" + partes[1] + ") tem " + partes[2] + " anos."`. Conta com `quantidade++` e lê a linha seguinte no fim do ciclo. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Exercício: De CSV para JSON - -::: übung l27-java-json -titel: De CSV para JSON -code: java -auftrag: Converte `animais.csv` em JSON e guarda-o em `animais-novos.json`, exatamente como `animais.json` acima. No fim, mostra **3 animais guardados**. -start: l27-java-json-start.visustruct -test: - -> 3 animais guardados -datei: animais-novos.json -> [ | {"nome": "Ludwig", "especie": "bode", "idade": 7}, | {"nome": "Zora", "especie": "cabra", "idade": 6}, | {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4} | ] -braucht: forSchleife, verzweigung -tipp: `linhas[0]` é o cabeçalho. Por isso, começa o ciclo for em `i = 1` e divide `linhas[i]` com `split(";")`. -tipp: Constrói a entrada com `\"`, como na lição 26. A idade é um número e fica sem aspas. Só acrescentas uma vírgula se `i < linhas.length - 1`. -lösung: l27-java-json-loesung.visustruct -::: - -## Jogo: Que formato serve? - -::: spiel frage -titel: Jogo: Que formato serve? -intro: O Ludwig quer guardar dados e não sabe que formato usar. Ajuda-o. Seis rondas. -antworten: txt | CSV | JSON - -runde: -frage: As notas da turma inteira devem ser abertas no Excel. -richtig: CSV -> As notas são uma tabela, e o Excel abre ficheiros CSV diretamente. - -runde: -frage: O Ludwig escreve uma carta à Zora. -richtig: txt -> Uma carta é texto para pessoas. Não precisa de colunas nem de chaves. - -runde: -frage: Um jogo guardado com nome, nível e uma lista de todas as chaves apanhadas. -richtig: JSON -> Em JSON, uma lista pode ficar encaixada numa entrada. Em CSV dificilmente cabe numa célula. - -runde: -frage: Um aparelho de medição guarda a hora e a temperatura a cada minuto, durante um ano inteiro. -richtig: CSV -> Muitas linhas iguais com as mesmas colunas: para isto, o CSV é o mais pequeno. O JSON repetiria as chaves em todas as linhas. - -runde: -frage: Uma aplicação de meteorologia vai buscar a previsão a um servidor na internet. -richtig: JSON -> O JSON é o padrão na web. Quase todas as aplicações recebem os dados assim. - -runde: -frage: O diário do castelo, em que cada entrada é uma frase curta. -richtig: txt -> Cada linha é uma frase para pessoas. É lida linha a linha, como na lição 24. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo reconhecer os mesmos dados em txt, CSV e JSON. → Os mesmos animais três vezes -- Consigo comparar pontos fortes e fracos de txt, CSV e JSON. → O que sabe fazer cada formato? -- Consigo converter um ficheiro CSV em frases. → Exercício: De CSV para frases -- Consigo converter um ficheiro CSV em JSON. → Exercício: De CSV para JSON -::: - -## Teste de conhecimentos - -::: quiz -? Que formato o Excel abre diretamente como tabela? -→ O que sabe fazer cada formato? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Porque é que um ficheiro JSON é normalmente maior do que um CSV com os mesmos dados? -→ O que sabe fazer cada formato? -- [ ] Porque o JSON guarda mais números. -- [x] Porque as chaves se repetem em cada entrada. -- [ ] Porque o JSON não usa vírgulas. - -? Em que formato cada valor diz o que significa? -→ O que sabe fazer cada formato? -- [ ] txt -- [ ] CSV -- [x] JSON -> Em JSON a chave está mesmo antes do valor, por exemplo "idade": 7. - -? Liga cada objetivo a um formato. -→ O que sabe fazer cada formato? -= uma carta à Zora -> txt -= a pauta de notas da turma -> CSV -= um jogo guardado com listas -> JSON -::: diff --git a/web/lessons/pt/28-erstes-fenster.md b/web/lessons/pt/28-erstes-fenster.md deleted file mode 100644 index 61575dbd..00000000 --- a/web/lessons/pt/28-erstes-fenster.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -titel: A tua primeira janela -bereich: Janelas com Swing -kurz: Abrir uma janela tua com Java e Swing, explicado linha a linha. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ligar o “Java verdadeiro” na área Código -- abrir uma janela com título e tamanho -- explicar o que faz cada linha do programa com janela -- explicar porque é que setVisible(true) fica no fim -::: - -## Programas com janelas - -Até agora os teus programas escreviam texto na consola. Mas a maioria dos programas que conheces tem **janelas** com botões e campos de texto. A isto chama-se uma interface gráfica. - -O Java traz uma caixa de ferramentas para isso: o **Swing**. No VisuStruct ligas o Swing assim: - -1. Abre à direita a área [Código](#zeige-codelive). -2. Clica na [roda dentada](#zeige-einstellungen) em cima à direita e escolhe Java como **Linguagem de programação**. -3. Por baixo, na secção Java, marca **Java verdadeiro (com Swing)**. - -Na primeira vez que clicas em [▶︎ Executar](#zeige-ausfuehren), o VisuStruct descarrega uma única vez cerca de 40 MB, o que demora um pouco. Depois disso o teu código corre como Java verdadeiro, e as tuas janelas aparecem na área Código na área **Janelas Java**, por cima da consola. - -> Com o “Java verdadeiro”, as entradas pela consola (`Scanner`) ainda não funcionam. Para programas com entradas, desliga outra vez a opção. - -::: angeberwissen -O Swing existe desde 1998. Antes disso, o Java só tinha o AWT, mais antigo, com uma classe `Frame`. Para ninguém confundir os blocos novos, quase todos os nomes no Swing começam por **J**: `JFrame`, `JButton`, `JLabel`. Pelo J reconheces logo o Swing. -::: - -## O programa completo - -Este é o programa mais pequeno que abre uma janela. Escreve-o na área Código e clica em [▶︎ Executar](#zeige-ausfuehren): - -```java -import javax.swing.*; - -public class MinhaJanela { - public static void main(String[] args) { - JFrame janela = new JFrame("A minha primeira janela"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); - } -} -``` - -Aparece uma janela cinzenta vazia com o título “A minha primeira janela”. Mais nada, mas é a tua própria janela! - -A moldura com `public class` e `main` já conheces da lição Java. Novas são a primeira linha e as quatro linhas dentro de `main`. Vamos vê-las uma a uma. - -::: ludwig -Olha para o estrutograma ao lado do código: cada linha em `main` é um bloco, todos uns por baixo dos outros. No início, um programa com janelas é então só uma sequência. -::: - -## Linha a linha - -| Linha | O que faz | -|---|---| -| `import javax.swing.*;` | Traz a caixa de ferramentas Swing para o programa. Sem esta linha o Java não conhece `JFrame`. | -| `JFrame janela = new JFrame("A minha primeira janela");` | Constrói uma janela nova. O texto entre parênteses é o título em cima, na moldura da janela. Depois falas com ela através da variável `janela`. | -| `janela.setSize(300, 200);` | A janela fica com 300 píxeis de largura e 200 píxeis de altura. | -| `janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);` | Quando alguém fecha a janela, o programa também termina. | -| `janela.setVisible(true);` | Só agora a janela fica visível. | - -O ponto em `janela.setSize(…)` quer dizer: “Janela, faz isto!” Antes do ponto está quem deve fazer alguma coisa, depois do ponto o que deve fazer. - -> Sem `setDefaultCloseOperation`, o Java só esconde a janela quando ela é fechada. O programa continua a correr sem se ver. - -::: quiz -? O que quer dizer o ponto em `janela.setSize(300, 200)`? -- [x] “Janela, faz isto!”: antes do ponto está quem faz, depois o que faz. -- [ ] Separa os dois números. -- [ ] Termina a linha. -> Antes do ponto está o objeto `janela`, depois o seu método `setSize`. -::: - -## Porque é que setVisible fica no fim - -`setVisible(true)` mostra a janela tal como ela está nesse momento. Por isso preparas tudo primeiro: título, tamanho e, mais tarde, também botões. Só mesmo no fim tornas a janela visível. - -O que só pões na janela depois de `setVisible(true)` muitas vezes não aparece. É que o Java não volta a desenhar a janela sozinho. Às vezes só aparece quando se muda o tamanho da janela. Erros destes são difíceis de encontrar. - -::: ludwig -Primeiro preparar, depois mostrar. Como no teatro: a cortina só sobe quando o palco está pronto. -::: - -Experimenta: apaga a linha com `setVisible(true)` e executa o programa outra vez. O programa corre, mas não vês nenhuma janela. - -## Classes e objetos - -Isto não está no programa da disciplina, mas ajuda-te a perceber o Swing. - -::: angeberwissen -O Java é, na verdade, uma linguagem **orientada a objetos**. `JFrame` é uma classe, ou seja, uma planta para janelas. Com `new JFrame(…)` o Java constrói, segundo essa planta, uma janela a sério: um **objeto**. Com dois `new` podias construir duas janelas, ambas com a mesma planta. - -`setSize` e `setVisible` são métodos que qualquer objeto janela sabe fazer. Por isso escreves `janela.setSize(…)`: o objeto `janela` executa o seu método. O teu próprio programa também é uma classe, neste caso `MinhaJanela`. -::: - -## Jogo: O que acontece? - -::: spiel frage -titel: Jogo: O que acontece? -intro: O Ludwig andou a mexer no programa da janela. Lê as linhas e prevê o que acontece. Seis rondas. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: O que vês depois de executar? -- uma janela com o título "Olá" -- [x] nenhuma janela -- uma mensagem de erro -> Falta `setVisible(true)`. A janela está construída, mas fica invisível. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(200, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); -frage: Como é a janela? -- mais larga do que alta -- [x] mais alta do que larga -- quadrada -> O primeiro número é a largura, o segundo a altura: 200 de largura, 300 de altura. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(300, 200); - janela.setVisible(true); -frage: Fechas a janela. O que acontece ao programa? -- Termina. -- [x] Continua a correr, invisível. -- A janela volta logo a abrir. -> Sem `setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)`, o Java só esconde a janela. - -runde: - JFrame janela = new JFrame(); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); -frage: O que aparece no topo da moldura da janela? -- janela -- JFrame -- [x] nada, o título está vazio -> Sem texto entre os parênteses, a janela não tem título. O nome da variável `janela` nunca aparece na janela. - -runde: - public class Teste { - public static void main(String[] args) { - JFrame janela = new JFrame("Teste"); - janela.setVisible(true); - } - } -frage: O que diz o javac? -- [x] cannot find symbol: class JFrame -- nada, a janela aparece -- missing return statement -> Falta a linha `import javax.swing.*;`. Sem ela, o Java não conhece `JFrame`. - -runde: - JFrame a = new JFrame("Uma"); - JFrame b = new JFrame("Duas"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: O que vês? -- [x] uma janela com o título "Uma" -- duas janelas -- uma janela com o título "Duas" -> Duas janelas feitas com a mesma planta, mas só a `a` fica visível. -::: - -## Tarefa: a tua própria janela - -Altera o programa de forma que - -- o título diga “Olá” e o teu nome, -- a janela tenha 400 píxeis de largura e 300 píxeis de altura, -- o programa termine quando fechas a janela. - -Executa-o e verifica os três pontos. - -::: ludwig -Não te apetece escrever tanto? Com o “Java verdadeiro” podes escrever na área Código só as quatro linhas de `main`. O VisuStruct põe-nas sozinho dentro de uma classe e acrescenta as linhas `import`. Num ambiente de desenvolvimento Java a sério precisas, no entanto, do programa completo. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ligar o “Java verdadeiro” na área Código. → Programas com janelas -- Consigo abrir uma janela com título e tamanho. → O programa completo -- Consigo explicar o que faz cada linha do programa com janela. → Linha a linha -- Consigo explicar porque é que setVisible(true) fica no fim. → Porque é que setVisible fica no fim -::: - -## Teste de conhecimentos - -::: quiz -? Que linha torna a janela visível? -→ Porque é que setVisible fica no fim -- [ ] janela.setSize(300, 200); -- [x] janela.setVisible(true); -- [ ] new JFrame("Olá"); -> Sem setVisible(true) a janela fica invisível. - -? Onde fica melhor janela.setVisible(true)? -→ Porque é que setVisible fica no fim -- [ ] Mesmo no início de main -- [x] No fim, quando a janela já está preparada -- [ ] Tanto faz. -> O que vem depois de setVisible(true) muitas vezes não aparece. - -? O que faz setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)? -→ Linha a linha -- [ ] A janela fecha-se logo. -- [x] Quando se fecha a janela, o programa termina. -- [ ] A janela já não se pode fechar. - -? Como reconheces os blocos do Swing? -→ Linha a linha -- [x] O nome começa por J, p. ex. JFrame. -- [ ] Estão entre chavetas. -- [ ] Terminam em Window. -::: diff --git a/web/lessons/pt/29-knopf-im-fenster.md b/web/lessons/pt/29-knopf-im-fenster.md deleted file mode 100644 index 3c884c23..00000000 --- a/web/lessons/pt/29-knopf-im-fenster.md +++ /dev/null @@ -1,231 +0,0 @@ ---- -titel: Um botão na janela -bereich: Janelas com Swing -kurz: Pôr um botão na janela e arrumá-lo bem com um layout. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir um botão e pô-lo na janela com add -- explicar porque é que o botão entra antes de setVisible -- usar o FlowLayout para o botão ter o tamanho normal -- construir uma janela com um botão em que se pode carregar -::: - -## Construir um botão - -Uma janela vazia é aborrecida. Agora vai lá para dentro um botão. No Swing, um botão chama-se **JButton** (button é inglês para botão). - -Precisas de duas linhas novas: - -```java -JButton botao = new JButton("Clica-me"); -janela.add(botao); -``` - -- `new JButton("Clica-me")` constrói um botão. O texto entre parênteses fica escrito no botão. -- `janela.add(botao)` põe o botão na janela. Sem `add` o botão existe, mas não está em lado nenhum e não o vês. - -::: quiz -? O que está escrito no botão depois de `new JButton("Iniciar")`? -- [ ] botao -- [x] Iniciar -- [ ] JButton -> O texto entre parênteses é a legenda. `botao` é só o nome da variável. -::: - -## O programa completo - -As duas linhas ficam **antes** de `setVisible(true)`, porque a janela deve primeiro ficar pronta e só depois ser mostrada: - -```java -import javax.swing.*; - -public class JanelaBotao { - public static void main(String[] args) { - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton botao = new JButton("Clica-me"); - janela.add(botao); - - janela.setVisible(true); - } -} -``` - -Executa o programa. Surpresa: o botão é enorme e enche a janela toda! - -::: ludwig -A ordem é a mesma da primeira janela: preparar, construir o botão, pô-lo na janela e só no fim mostrar. As linhas em branco no código ajudam-te a ver estas partes. -::: - -## O botão gigante - -A forma como uma janela arruma as suas partes é decidida pelo seu **layout**. Sem mais indicações, um `JFrame` põe tudo no meio e torna-o tão grande como a janela inteira. - -Com outro layout o botão fica com o tamanho normal. O **FlowLayout** põe tudo lado a lado, como palavras numa linha, só do tamanho necessário: - -```java -import javax.swing.*; -import java.awt.*; - -public class JanelaBotao { - public static void main(String[] args) { - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new FlowLayout()); - - JButton botao = new JButton("Clica-me"); - janela.add(botao); - - janela.setVisible(true); - } -} -``` - -O `FlowLayout` pertence à caixa de ferramentas AWT, mais antiga. Por isso há uma segunda linha em cima: `import java.awt.*;`. - -::: angeberwissen -No FlowLayout os blocos correm como texto: se um botão já não cabe na linha, passa para a seguinte. Torna a janela estreita e vê como os botões mudam de linha. Flow é inglês para fluxo. -::: - -## Clicar funciona, mas nada acontece - -Clica no botão. Ele fica um pouco mais escuro enquanto o carregas. O Swing mostra assim que foi carregado. Fora isso, não acontece nada. - -Isto não é um erro: ainda não disseste ao programa **o que** deve acontecer com um clique. Isso aprendes na próxima lição. - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Em cada programa de janela há **exatamente um erro**, e a bomba está a contar. Lê o que o programa deve fazer e toca na linha errada antes de o tempo acabar. Seis rondas, 25 segundos cada. -zeit: 25 - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - JButton botao = new JButton(Clica-me); - janela.add(botao); - janela.setVisible(true); -frage: Deve mostrar um botão "Clica-me". -fehler: 3 -besser: JButton botao = new JButton("Clica-me"); -> A legenda é um texto e precisa de aspas. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - JButton botao = new JButton("OK"); - janela.add(janela); - janela.setVisible(true); -frage: Deve pôr o botão "OK" na janela. -fehler: 4 -besser: janela.add(botao); -> Na janela entra o botão, não a própria janela. - -runde: - import java.swing.*; - import java.awt.*; -frage: Deve trazer o Swing e o FlowLayout para o programa. -fehler: 1 -besser: import javax.swing.*; -> O Swing está em `javax.swing`, com x. O FlowLayout vem de `java.awt`, sem x. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout); - janela.add(new JButton("OK")); - janela.setVisible(true); -frage: Deve mostrar o botão no tamanho normal. -fehler: 3 -besser: janela.setLayout(new FlowLayout()); -> Depois de `new` e do nome vêm sempre parênteses, mesmo que não haja nada lá dentro. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout()); - JButton botao = new Jbutton("OK"); - janela.add(botao); - janela.setVisible(true); -frage: Deve mostrar um botão "OK". -fehler: 4 -besser: JButton botao = new JButton("OK"); -> O Java distingue maiúsculas de minúsculas: `JButton` com B maiúsculo. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout()); - janela.add(new JButton("OK")); - janela.setVisible(false); -frage: Deve mostrar a janela com o botão. -fehler: 5 -besser: janela.setVisible(true); -> Com `false`, a janela fica invisível. -::: - -## Tarefa: clica-me - -Constrói uma janela com o título “Teste do botão”, com 300 por 200 píxeis e um botão “Clica-me” de tamanho normal. Deve ser possível carregar no botão, mesmo que ainda não aconteça nada. - -Extra: põe um segundo botão “Eu também” ao lado. Por que ordem aparecem os botões? Troca as duas linhas `add` e experimenta outra vez. - -**Terminaste quando …** - -- o título da janela diz “Teste do botão”, -- a janela tem 300 píxeis de largura e 200 de altura, -- o botão “Clica-me” só é do tamanho do texto e não enche a janela toda, -- o botão afunda visivelmente quando clicas nele. - -::: ludwig -Cada botão precisa da sua própria variável, por exemplo `botao` e `botao2`, e do seu próprio `add`. O FlowLayout põe os botões pela ordem das linhas `add`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir um botão e pô-lo na janela com add. → Construir um botão -- Consigo explicar porque é que o botão entra antes de setVisible(true). → O programa completo -- Consigo usar o FlowLayout para o botão ter o tamanho normal. → O botão gigante -- Consigo construir uma janela com um botão em que se pode carregar. → Tarefa: clica-me -::: - -## Teste de conhecimentos - -::: quiz -? O que faz janela.add(botao)? -→ Construir um botão -- [ ] Constrói um botão novo. -- [x] Põe o botão na janela. -- [ ] Torna o botão clicável. -> O botão é construído com new JButton(…). - -? Porque é que, sem FlowLayout, o botão enche a janela toda? -→ O botão gigante -- [ ] Porque o texto é demasiado comprido -- [x] Porque sem layout próprio a janela torna tudo tão grande como ela -- [ ] Porque falta o setSize - -? Que linha import precisas para o FlowLayout? -→ O botão gigante -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> O FlowLayout vem do AWT, mais antigo. - -? O que acontece nesta lição quando clicas no botão? -→ Tarefa: clica-me -- [ ] O programa termina. -- [x] O botão fica mais escuro por um momento, e mais nada. -- [ ] No botão aparece “Fui clicado”. -> O que deve acontecer com um clique aprendes na próxima lição. -::: diff --git a/web/lessons/pt/30-der-knopf-reagiert.md b/web/lessons/pt/30-der-knopf-reagiert.md deleted file mode 100644 index 954cacaf..00000000 --- a/web/lessons/pt/30-der-knopf-reagiert.md +++ /dev/null @@ -1,356 +0,0 @@ ---- -titel: O botão reage -bereich: Janelas com Swing -kurz: Com um ActionListener decides o que acontece com um clique. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o que é um evento -- usar addActionListener para decidir o que acontece com um clique -- executar várias instruções com um clique -- construir um botão que muda o seu texto depois do clique -::: - -## Um clique é um evento - -Na lição anterior podias carregar no botão, mas nada acontecia. Num programa com janelas é a pessoa que decide quando acontece alguma coisa: clica, escreve ou fecha uma janela. Cada ação destas chama-se **evento**. - -Por isso o teu programa tem de esperar pelo clique e dar por ele. Para isso registas no botão um ouvinte, um **ActionListener** (inglês para “ouvinte de ações”). A cada clique o botão chama o seu ouvinte, e o ouvinte executa o teu código. - -::: ludwig -Até agora o teu programa corria de cima para baixo, como no estrutograma. O código do ouvinte, pelo contrário, só corre mais tarde, sempre que alguém clica. Quantas vezes isso acontece, ninguém sabe de antemão. -::: - -## A linha com a seta - -É assim que registas o ouvinte: - -```java -botao.addActionListener(e -> botao.setText("Fui clicado")); -``` - -Lido de fora para dentro: - -- `botao.addActionListener( … )` regista um ouvinte no botão. -- `e` é o evento. Contém, por exemplo, quando foi o clique. Aqui não precisas dele, mas o nome tem de estar lá. -- `->` lê-se “então faz”. -- `botao.setText("Fui clicado")` é o que acontece com o clique: o botão recebe um texto novo. - -A linha vem depois de `new JButton(…)`, porque o botão já tem de existir. E, como tudo o resto, vem antes de `setVisible(true)`. - -::: quiz -? O que representa o `e` em `e -> botao.setText(…)`? -- [x] O evento, ou seja, o clique -- [ ] “Fim” -- [ ] Um erro -> Aqui não precisas de `e`, mas o nome tem de estar lá. -::: - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; - -public class ClicaMe { - public static void main(String[] args) { - JFrame janela = new JFrame("Clica-me"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new FlowLayout()); - - JButton botao = new JButton("Clica-me"); - botao.addActionListener(e -> botao.setText("Fui clicado")); - janela.add(botao); - - janela.setVisible(true); - } -} -``` - -Executa-o e clica no botão. Agora diz “Fui clicado”. - -> Se o texto novo for mais comprido do que o antigo, o FlowLayout alarga o botão sozinho. - -## Várias instruções com um clique - -Se com um clique deve acontecer mais do que uma coisa, pões chavetas depois da seta. Lá dentro, cada instrução tem o seu ponto e vírgula, como no corpo de um ciclo: - -```java -botao.addActionListener(e -> { - botao.setText("Fui clicado"); - janela.setTitle("Obrigado!"); - System.out.println("Clique!"); -}); -``` - -`System.out.println` escreve na [consola](#zeige-konsole), como de costume. Clica várias vezes: a cada clique aparece uma nova linha “Clique!”. Assim vês que o código do ouvinte corre mesmo a cada clique. - -::: ludwig -Atenção ao fim: primeiro `}` fecha o bloco, depois `)` fecha o parêntese de `addActionListener`, e só então vem o ponto e vírgula. Ou seja, `});`. Se faltar alguma coisa, aparece uma indicação por baixo do código. -::: - -## Jogo: Quem está a ouvir? - -::: spiel zuhoerer -titel: Jogo: Quem está a ouvir? -intro: O Ludwig mostra-te código e a janela correspondente. Por baixo vês que botões são clicados, um a seguir ao outro. O que acontece? Depois da tua resposta, o Ludwig reproduz os cliques: vês que ouvinte ouve o evento e que linhas correm a seguir. -frage: O que diz a etiqueta depois dos cliques? - -runde: - JFrame janela = new JFrame("Saudação"); - JLabel texto = new JLabel("Início"); - JButton botao = new JButton("Olá"); - botao.addActionListener(e -> texto.setText("Olá mundo")); -klicks: Olá -- Início -- [x] Olá mundo -- Olá -> O botão tem um ouvinte. Com o clique executa `texto.setText("Olá mundo")`, e a etiqueta mostra “Olá mundo”. - -runde: - JFrame janela = new JFrame("Fruta"); - JLabel texto = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> texto.setText("Maçã")); - b.addActionListener(e -> texto.setText("Pera")); -klicks: A, B -- Maçã -- [x] Pera -- Maçã Pera -> Cada botão tem o seu próprio ouvinte. Primeiro o A escreve “Maçã”, depois o B substitui-o por “Pera”. O `setText` troca o texto antigo, não acrescenta nada. - -runde: - JFrame janela = new JFrame("Fruta"); - JLabel texto = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> texto.setText("Maçã")); -klicks: B -- Maçã -- Pera -- [x] ? -> Ninguém se inscreveu no botão B: não há `b.addActionListener`. O clique é um evento, mas ninguém está a ouvir. Por isso o ponto de interrogação fica. - -runde: - JFrame janela = new JFrame("Cores"); - JLabel texto = new JLabel("nenhuma"); - JButton vermelho = new JButton("Vermelho"); - JButton azul = new JButton("Azul"); - vermelho.addActionListener(e -> texto.setText("Azul")); - azul.addActionListener(e -> texto.setText("Vermelho")); -klicks: Vermelho -- Vermelho -- [x] Azul -- nenhuma -> Atenção: o que acontece é decidido pelo código no ouvinte, não pela legenda do botão. O ouvinte do botão “Vermelho” escreve “Azul”. - -runde: - JFrame janela = new JFrame("Botão"); - JLabel texto = new JLabel("Clica, por favor"); - JButton botao = new JButton("Clica-me"); - botao.addActionListener(e -> { - botao.setText("Obrigado!"); - texto.setText("Clicado"); - }); -klicks: Clica-me -frage: O que diz o botão depois do clique? -- Clica-me -- [x] Obrigado! -- Clicado -> O bloco depois da seta tem duas instruções. As duas correm com o clique, uma a seguir à outra: o botão fica com “Obrigado!”, a etiqueta com “Clicado”. - -runde: - JFrame janela = new JFrame("Consola"); - JButton botao = new JButton("Vai"); - botao.addActionListener(e -> System.out.println("Clique!")); -klicks: Vai, Vai, Vai -frage: Quantas linhas “Clique!” há depois na consola? -- 1 -- [x] 3 -- 0 -> O ouvinte corre a **cada** clique, não só no primeiro. Três cliques, três linhas. -::: - -## ActionListener é uma interface - -Isto não está no programa da disciplina. Se quiseres saber o que está por trás da seta, continua a ler. - -::: angeberwissen -`ActionListener` é uma **interface**, uma espécie de contrato. Diz: quem quiser ser um ActionListener tem de ter um método `actionPerformed(ActionEvent e)`. A cada clique o botão chama exatamente esse método. -::: - -A linha com a seta já conheces do passo 3: - -```java -botao.addActionListener(e -> botao.setText("Fui clicado")); -``` - -Sem a seta escreve-se o ouvinte como uma classe sem nome que cumpre o contrato (uma classe anónima): - -```java -botao.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - botao.setText("Fui clicado"); - } -}); -``` - -Para isso precisas ainda de `import java.awt.event.*;` em cima. As duas formas fazem exatamente o mesmo. A seta `e -> …` é a forma curta (uma expressão lambda, desde o Java 8): `e` é o parâmetro de `actionPerformed`, e depois da seta vem o seu corpo. A forma curta funciona porque a interface tem um único método. Assim o Java sabe de que método se trata. - -## Jogo: Contrato cumprido? - -::: spiel vertrag -titel: Jogo: Contrato cumprido? -intro: O contrato `ActionListener` exige exatamente um método: `public void actionPerformed(ActionEvent e)`. O Ludwig mostra-te código. Cumpre o contrato, de forma a poder ser inscrito num botão? O Ludwig verificou cada resposta com o compilador de Java verdadeiro. -frage: Isto cumpre o contrato ActionListener? -antworten: cumprido | não cumprido - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: cumprido -> Está tudo: `implements ActionListener` assina o contrato, e o método chama-se exatamente `actionPerformed`, é `public`, não devolve nada (`void`) e recebe um `ActionEvent`. - -runde: - class Saudacao implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Olha bem: `actionPerfomed` em vez de `actionPerformed`, falta um r. Para o Java é um método completamente diferente. O compilador avisa que falta `actionPerformed`. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed() { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Falta o parâmetro. O contrato exige `actionPerformed(ActionEvent e)`, porque com o clique o botão entrega o evento. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent evento) { - System.out.println("Olá"); - } - } -richtig: cumprido -> O nome do parâmetro escolhe-se livremente. Só conta o tipo `ActionEvent`. Em vez de `e` também se pode chamar `evento`. - -runde: - class Saudacao implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Falta o `public`. Todos os métodos de uma interface são públicos, porque o botão tem de os poder chamar de fora. Sem `public` o compilador indica um erro. - -runde: - class Saudacao implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: não cumprido -> O contrato diz `void`: o método não devolve nada. Com `int` como tipo de retorno já não serve. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - dizOla(); - } - void dizOla() { - System.out.println("Olá"); - } - } -richtig: cumprido -> Métodos a mais são permitidos. O contrato só exige que o `actionPerformed` exista, não que não haja mais nada. - -runde: - class Saudacao { - public void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } - botao.addActionListener(new Saudacao()); -richtig: não cumprido -> O método está certo, mas o contrato não foi assinado: falta `implements ActionListener`. Num botão, o Java só aceita objetos que cumprem oficialmente o contrato. - -runde: - botao.addActionListener(e -> System.out.println("Olá")); -richtig: cumprido -> A seta é a forma curta. Como o contrato só tem um método, o Java sabe: `e` é o parâmetro de `actionPerformed`, e depois da seta vem o seu corpo. - -runde: - botao.addActionListener((a, b) -> System.out.println("Olá")); -richtig: não cumprido -> Aqui a seta tem dois parâmetros. Mas o `actionPerformed` recebe exatamente um, o evento. Por isso esta forma curta não serve para o contrato. -::: - -## Tarefa: fui clicado - -Agora terminas a tarefa: constrói uma janela com um botão “Clica-me” de tamanho normal. Depois do clique, o botão diz “Fui clicado”. - -Extra: constrói dois botões “Vermelho” e “Azul”. Um clique num botão escreve o seu nome no título da janela, por exemplo “Escolheste Vermelho”. - -**Terminaste quando …** - -- o botão diz “Clica-me” antes do clique, -- depois do clique diz “Fui clicado”, -- o botão não enche a janela toda. - -::: ludwig -Cada botão recebe o seu próprio ouvinte, com o seu próprio `addActionListener`. O título mudas com `janela.setTitle(…)`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o que é um evento. → Um clique é um evento -- Consigo usar addActionListener para decidir o que acontece com um clique. → A linha com a seta -- Consigo executar várias instruções com um clique. → Várias instruções com um clique -- Consigo construir um botão que muda o seu texto depois do clique. → Tarefa: fui clicado -::: - -## Teste de conhecimentos - -::: quiz -? O que é um ActionListener? -→ Um clique é um evento -- [ ] Um botão com texto -- [x] Um ouvinte que executa o teu código a cada clique -- [ ] Um layout para janelas -> Registas o ouvinte no botão com addActionListener. - -? Quando corre botao.setText("Fui clicado") dentro do ouvinte? -→ A linha com a seta -- [ ] Logo no início do programa -- [x] Sempre que alguém clica no botão -- [ ] Só no primeiro clique -> O código do ouvinte só corre quando o evento acontece. - -? Como termina um ouvinte com chavetas? -→ A linha com a seta -- [ ] };) -- [x] }); -- [ ] );} -> Primeiro fecha o bloco, depois o parêntese de addActionListener, depois o ponto e vírgula. - -? Associa cada parte à sua função. -→ A linha com a seta -= JButton -> um botão -= addActionListener -> registar um ouvinte -= setText -> mudar o texto -= setVisible(true) -> mostrar a janela -::: diff --git a/web/lessons/pt/31-zahl-verdoppeln.md b/web/lessons/pt/31-zahl-verdoppeln.md deleted file mode 100644 index 9df772da..00000000 --- a/web/lessons/pt/31-zahl-verdoppeln.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Duplicar um número na janela -bereich: Janelas com Swing -kurz: Arrumar uma etiqueta, um campo de texto e um botão com um GridLayout e calcular com o texto do campo de texto. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pôr um JLabel e um JTextField na janela -- explicar porque é que sem layout só se vê a última peça -- arrumar tudo umas por baixo das outras com GridLayout -- calcular com getText, Integer.parseInt e setText -- escrever um programa que não falha com “abc” -::: - -## O objetivo - -Vamos construir uma pequena calculadora com janela: em cima está uma etiqueta, por baixo um campo de texto e um botão **Calcular**. Se escreveres 2 e clicares, o campo de texto mostra 4. Clicas outra vez e passa a 8, depois 16 e assim por diante. - -Para isso precisas de duas peças novas: - -- **JLabel**: uma etiqueta, ou seja, texto simples na janela. -- **JTextField**: um campo de texto onde se pode escrever. - -Como nas últimas lições, marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Três peças na janela - -Pomos as três peças na janela, tal como o botão na lição 29: - -```java -import javax.swing.*; - -public class Duplicar { - public static void main(String[] args) { - JFrame janela = new JFrame("Duplicar número"); - janela.setSize(250, 150); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel etiqueta = new JLabel("Escreve um número:"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular"); - janela.add(etiqueta); - janela.add(campo); - janela.add(botao); - - janela.setVisible(true); - } -} -``` - -Executa o programa. Ups: só se vê o botão gigante. Onde estão a etiqueta e o campo de texto? - -## Porque só se vê o botão - -::: ludwig -Lembras-te do botão gigante? Sem um layout próprio, um `JFrame` só tem **um** lugar: o meio. Cada `add` põe a peça nova exatamente aí, do tamanho da janela inteira. As três peças ficam por isso umas por cima das outras no mesmo lugar, e a janela só mostra a última que lá puseste: o botão. Assim não consegues usar o programa. -::: - -A solução é outra vez um layout. Desta vez usamos o **GridLayout**. Divide a janela em células do mesmo tamanho, como uma grelha: - -```java -janela.setLayout(new GridLayout(0, 1)); -``` - -Os dois números são linhas e colunas. `0` linhas quer dizer “tantas quantas forem precisas”, `1` coluna quer dizer: tudo umas por baixo das outras. Para o `GridLayout` precisas ainda de `import java.awt.*;` lá em cima. - -| Layout | Como arruma | -|---|---| -| sem layout | tudo no meio, umas por cima das outras | -| `FlowLayout` | lado a lado como palavras, com mudança de linha quando é preciso | -| `GridLayout(0, 1)` | umas por baixo das outras, todas as linhas da mesma altura | - -## A janela com GridLayout - -```java -import javax.swing.*; -import java.awt.*; - -public class Duplicar { - public static void main(String[] args) { - JFrame janela = new JFrame("Duplicar número"); - janela.setSize(250, 150); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel etiqueta = new JLabel("Escreve um número:"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular"); - janela.add(etiqueta); - janela.add(campo); - janela.add(botao); - - janela.setVisible(true); - } -} -``` - -Agora as três estão umas por baixo das outras. Podes escrever no campo de texto e carregar no botão. Mas o programa ainda não calcula nada. - -::: angeberwissen -Com `new GridLayout(2, 3)` obténs 2 linhas com 3 colunas cada, ou seja, 6 células do mesmo tamanho. É assim que se constroem, por exemplo, as teclas de uma calculadora. As peças preenchem as células pela ordem das linhas `add`, linha a linha da esquerda para a direita. -::: - -## Calcular com o texto do campo de texto - -Com um clique, o programa deve ir buscar o número ao campo de texto, duplicá-lo e voltar a escrevê-lo lá. Isso faz-se com três métodos: - -- `campo.getText()` dá-te o texto do campo de texto, por exemplo `"2"`. -- `Integer.parseInt(…)` transforma-o no número 2. Já conheces isto da lição 22. -- `campo.setText(…)` escreve um texto novo no campo de texto. - -Juntamente com o ActionListener da lição 30 fica assim: - -```java -botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); -}); -``` - -`setText` precisa de um texto. `"" + numero * 2` junta o resultado a um texto vazio e assim transforma o número num texto. - -## Não é um número? Sem falhas! - -Escreve “abc” no campo de texto e clica em Calcular. A janela fica, mas não acontece nada, e na [consola](#zeige-konsole) aparece uma `NumberFormatException`. Da lição 22 já sabes o que fazer: - -```java -botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } -}); -``` - -Em vez de um erro na consola, vês agora um aviso na janela. - -::: ludwig -A seta já tem um `e` para o evento. Por isso a exceção no `catch` precisa de outro nome, aqui `erro`. O Java não permite duas variáveis com o mesmo nome. -::: - -::: quiz -? Porque é que a exceção no `catch` se chama aqui `erro` e não `e`? -- [ ] O `catch` exige sempre o nome `erro`. -- [x] `e` já é o evento da seta, e o Java não permite duas variáveis com o mesmo nome. -- [ ] `e` só é permitido para números. -> Podes escolher o nome à vontade, só não pode repetir-se. -::: - -## Jogo: O que fica na janela? - -::: spiel frage -titel: Jogo: O que fica na janela? -intro: O Ludwig escreve algo no campo de texto e clica em Calcular. Lê o ouvinte e prevê o que a janela mostra depois. Seis rondas. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - }); -frage: No campo de texto está 21. O que fica lá depois do clique? -- 2121 -- [x] 42 -- 21 -> `parseInt` transforma o texto no número 21. O dobro é 42. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - }); -frage: No campo de texto está 21. Clicas duas vezes. O que fica lá depois? -- 42 -- [x] 84 -- 4242 -> Cada clique duplica o que está no campo nesse momento: 21, 42, 84. - -runde: - botao.addActionListener(e -> { - campo.setText(campo.getText() + campo.getText()); - }); -frage: No campo de texto está 21. O que fica lá depois do clique? -- 42 -- [x] 2121 -- 21 -> Sem `parseInt` continua a ser texto, e `+` junta textos. - -runde: - botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } - }); -frage: No campo de texto está abc. O que acontece com o clique? -- [x] A etiqueta mostra "Escreve um número inteiro!" -- No campo de texto fica abcabc. -- A janela vai abaixo. -> `parseInt` lança uma NumberFormatException, e o `catch` apanha-a. - -runde: - botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } - }); -frage: No campo de texto está 2.5. O que acontece com o clique? -- No campo de texto fica 5.0. -- No campo de texto fica 5. -- [x] A etiqueta mostra "Escreve um número inteiro!" -> `Integer.parseInt` só conhece números inteiros. Para ele, 2.5 não é um número. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero + numero); - }); -frage: No campo de texto está 5. O que fica lá depois do clique? -- 10 -- [x] 55 -- 5 -> `"" + numero` já é um texto, e o segundo `+` junta outra vez o 5. Com `"" + (numero + numero)` o Java calcula primeiro. -::: - -## Tarefa: duplicar um número - -Junta o programa todo: - -- uma janela com o título “Duplicar número”, com 250 píxeis de largura e 150 píxeis de altura, -- umas por baixo das outras, uma etiqueta, um campo de texto e um botão “Calcular”, -- cada clique duplica o número no campo de texto: 2, 4, 8, 16, … -- com “abc” aparece um aviso na etiqueta em vez de uma falha. - -Extra: um segundo botão “Dividir por 2” divide o número por 2. O que acontece com 5? - -**Terminaste quando …** - -- o título é “Duplicar número” e etiqueta, campo de texto e botão estão uns por baixo dos outros, -- o 2 passa a 4, 8 e 16 com três cliques, -- com “abc” aparece um aviso na etiqueta e o programa continua a funcionar. - -::: ludwig -O botão “Dividir por 2” entra no GridLayout como quarta linha e recebe o seu próprio ActionListener. Com `int`, o Java corta as casas decimais ao dividir. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pôr um JLabel e um JTextField na janela. → Três peças na janela -- Consigo explicar porque é que sem layout só se vê a última peça. → Porque só se vê o botão -- Consigo arrumar tudo umas por baixo das outras com GridLayout(0, 1). → A janela com GridLayout -- Consigo calcular com getText, Integer.parseInt e setText. → Calcular com o texto do campo de texto -- Consigo escrever um programa que não falha com “abc”. → Não é um número? Sem falhas! -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que sem layout só se vê o botão? -→ Porque só se vê o botão -- [ ] Porque faltam a etiqueta e o campo de texto -- [x] Porque todas as peças estão no meio umas por cima das outras e a última tapa as outras -- [ ] Porque a janela é demasiado pequena - -? O que significa new GridLayout(0, 1)? -→ A janela com GridLayout -- [ ] Nenhuma linha e uma coluna, por isso a janela fica vazia -- [x] Tantas linhas quantas forem precisas numa coluna, ou seja, tudo umas por baixo das outras -- [ ] Uma linha, tudo lado a lado - -? O que te dá campo.getText()? -→ Calcular com o texto do campo de texto -- [ ] Sempre um número -- [x] O texto do campo de texto -- [ ] O título da janela -> Para um número precisas ainda de Integer.parseInt. - -? Associa cada peça à sua função. -→ Três peças na janela -= JLabel -> etiqueta -= JTextField -> campo de texto para escrever -= JButton -> botão -= GridLayout -> arrumação em grelha -::: diff --git a/web/lessons/pt/32-klickzaehler.md b/web/lessons/pt/32-klickzaehler.md deleted file mode 100644 index 6d7be2ba..00000000 --- a/web/lessons/pt/32-klickzaehler.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Contador de cliques com reset -bereich: Janelas com Swing -kurz: Dois botões alteram o mesmo número: criar uma variável para a classe toda. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar porque é que um ouvinte não pode alterar uma variável do main -- criar uma variável static na classe -- fazer com que dois botões alterem o mesmo número -- construir um contador com −1 que nunca fica abaixo de 0 -::: - -## O objetivo - -Uma janela conta cliques: em cima está o número, por baixo dois botões. **+1** soma um, **Reset** volta a pôr o número a 0. - -A novidade desta vez: dois botões trabalham com o **mesmo** número. Para isso precisas de uma variável que os dois ouvintes conheçam. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## A primeira tentativa - -A ideia mais óbvia é criar o número no `main`, como de costume, e somá-lo no ouvinte: - -```java -int contagem = 0; -botao.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); -}); -``` - -Se executares isto com o Java verdadeiro, o compilador indica um erro: - -``` -local variables referenced from a lambda expression must be final or effectively final -``` - -::: ludwig -O ouvinte só corre mais tarde, a cada clique, muito depois de o `main` ter terminado. Por isso pode ler as variáveis do `main`, mas não alterá-las. O Java insiste: uma variável do `main` que aparece na seta tem de ficar igual. Para um número que muda a cada clique, precisamos de outro lugar. -::: - -## Uma variável para a classe toda - -A solução: a variável **não** vai para o `main`, mas diretamente para a classe, por cima do `main`. Assim pertence à classe toda, e qualquer ouvinte a pode alterar: - -```java -public class Contador { - static int contagem = 0; - - public static void main(String[] args) { - // aqui a janela, como de costume - } -} -``` - -- `static` quer dizer: a variável pertence à própria classe. Como o `main` também é `static`, aqui é preciso. -- Uma variável destas chama-se **atributo** da classe. Vive tanto tempo como o programa, e não só enquanto o `main` corre. - -::: quiz -? Como se chama uma variável que está diretamente na classe e não no `main`? -- [ ] Parâmetro -- [x] Atributo -- [ ] Ouvinte -> Um atributo vive tanto tempo como o programa, e não só enquanto o `main` corre. -::: - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; - -public class Contador { - static int contagem = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel numero = new JLabel("0", SwingConstants.CENTER); - JButton mais = new JButton("+1"); - JButton reset = new JButton("Reset"); - - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - reset.addActionListener(e -> { - contagem = 0; - numero.setText("0"); - }); - - janela.add(numero); - janela.add(mais); - janela.add(reset); - janela.setVisible(true); - } -} -``` - -`SwingConstants.CENTER` põe o texto da etiqueta no meio. Clica algumas vezes em +1 e depois em Reset. - -::: angeberwissen -Com `numero.setFont(new Font("SansSerif", Font.BOLD, 48));` o número fica grande e a negrito. `Font` vem de `java.awt`, que já importaste. -::: - -## Jogo: Quem está a contar? - -::: spiel zuhoerer -titel: Jogo: Quem está a contar? -intro: Agora os ouvintes fazem contas com a variável da classe. O Ludwig mostra-te o código, a janela e os cliques. Que número aparece no fim? A seguir o Ludwig reproduz os cliques. -frage: O que diz a etiqueta depois dos cliques? - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> A variável está na classe e guarda o valor entre os cliques. Cada clique soma um: 1, 2, 3. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - JButton reset = new JButton("Reset"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - reset.addActionListener(e -> { - contagem = 0; - numero.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> Depois de dois cliques aparece 2. O Reset põe a variável a 0, e o último clique volta a contar até 1. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> contagem++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> A variável fica a 2, mas ninguém a escreve na etiqueta. Sem `numero.setText(…)` a janela continua a mostrar 0. Calcular e mostrar são duas coisas diferentes. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("Cliques: 0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("Cliques: " + contagem + 1); - }); -klicks: +1 -- Cliques: 2 -- Cliques: 1 -- [x] Cliques: 11 -> A armadilha dos parênteses: o Java calcula da esquerda para a direita. `"Cliques: " + contagem` dá o texto “Cliques: 1”, e `+ 1` junta mais um 1 no fim. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton um = new JButton("+1"); - JButton dez = new JButton("+10"); - um.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - dez.addActionListener(e -> { - contagem += 10; - numero.setText("" + contagem); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Os dois ouvintes alteram a mesma variável: 10, depois 11, depois 21. `contagem += 10` é a forma curta de `contagem = contagem + 10`. -::: - -## Tarefa: contador de cliques - -Reconstrói o contador de cliques e acrescenta: - -- um botão **−1** que subtrai um, -- o número nunca deve ficar abaixo de 0. Se estiver em 0, o −1 não faz nada. - -**Terminaste quando …** - -- **+1** conta mais um e **−1** conta menos um, -- no 0 um clique em **−1** não muda nada, -- **Reset** volta a pôr o número em 0. - -::: ludwig -Para “nunca abaixo de 0”, o ouvinte do −1 precisa de um `if`: só se `contagem > 0` é que subtrai. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar porque é que um ouvinte não pode alterar uma variável do main. → A primeira tentativa -- Consigo criar uma variável static na classe. → Uma variável para a classe toda -- Consigo fazer com que dois botões alterem o mesmo número. → O programa completo -- Consigo construir um contador com −1 que nunca fica abaixo de 0. → Tarefa: contador de cliques -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que o ouvinte não pode alterar a variável contagem do main? -→ A primeira tentativa -- [ ] Porque no Swing não são permitidos números -- [x] Porque o ouvinte só corre mais tarde e o Java não deixa a seta alterar variáveis do main -- [ ] Porque a contagem fica demasiado grande - -? Onde fica static int contagem = 0;? -→ Uma variável para a classe toda -- [ ] No main, bem em cima -- [x] Na classe, fora do main -- [ ] No ouvinte - -? O que faz SwingConstants.CENTER em new JLabel("0", SwingConstants.CENTER)? -→ O programa completo -- [x] Põe o texto no meio da etiqueta. -- [ ] Põe a janela no meio do ecrã. -- [ ] Aumenta a letra. -::: diff --git a/web/lessons/pt/33-ampel.md b/web/lessons/pt/33-ampel.md deleted file mode 100644 index 0549eab8..00000000 --- a/web/lessons/pt/33-ampel.md +++ /dev/null @@ -1,290 +0,0 @@ ---- -titel: Um semáforo com cores -bereich: Janelas com Swing -kurz: Definir cores de fundo e mudar um semáforo com uma variável de estado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pintar uma etiqueta com setOpaque e setBackground -- guardar um estado numa variável static -- usar o BorderLayout para pôr algo grande no meio e algo estreito em baixo -- explicar para que servem o FlowLayout, o GridLayout e o BorderLayout -- programar um semáforo com quatro fases -::: - -## O objetivo - -Vamos construir um semáforo: uma área grande acende a vermelho. A cada clique em **Seguinte** muda: vermelho, verde, amarelo e outra vez vermelho. - -As novidades desta vez são as **cores** e um **estado**: o programa tem de se lembrar de que cor está acesa. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Cores no Swing - -As cores estão na classe `Color` de `java.awt`. Cores já prontas são, por exemplo, `Color.RED`, `Color.GREEN`, `Color.YELLOW`, `Color.ORANGE` e `Color.WHITE`. - -É assim que pintas uma etiqueta: - -```java -JLabel luz = new JLabel("", SwingConstants.CENTER); -luz.setOpaque(true); -luz.setBackground(Color.RED); -``` - -- `setBackground(…)` define a cor de fundo. -- Um `JLabel` precisa ainda de `setOpaque(true)`. Sem esta linha o fundo é transparente e a cor fica invisível. - -::: ludwig -`opaque` é inglês para “opaco”, ou seja, não transparente. Normalmente uma etiqueta é transparente, como letras escritas num vidro. Só com `setOpaque(true)` ganha uma área própria que se pode pintar. -::: - -::: quiz -? Que linha pinta de verde o fundo da etiqueta `luz`? -- [ ] `luz.setText(Color.GREEN);` -- [x] `luz.setBackground(Color.GREEN);` -- [ ] `luz.setColor("verde");` -> A cor de fundo define-se com `setBackground`. Para se ver, um `JLabel` precisa antes de `setOpaque(true)`. -::: - -## Lembrar a cor - -Para o semáforo saber o que vem a seguir, guarda o seu estado num número: 0 é vermelho, 1 é verde, 2 é amarelo. Tal como no contador de cliques da lição 32, a variável fica na classe, para o ouvinte a poder alterar: - -```java -static int fase = 0; -``` - -A cada clique a fase avança um. Depois do amarelo (2) vem outra vez o vermelho (0): - -```java -seguinte.addActionListener(e -> { - fase++; - if (fase > 2) { - fase = 0; - } - if (fase == 0) { - luz.setBackground(Color.RED); - } else if (fase == 1) { - luz.setBackground(Color.GREEN); - } else { - luz.setBackground(Color.YELLOW); - } -}); -``` - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; - -public class Semaforo { - static int fase = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Semáforo"); - janela.setSize(200, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new BorderLayout()); - - JLabel luz = new JLabel("", SwingConstants.CENTER); - luz.setOpaque(true); - luz.setBackground(Color.RED); - JButton seguinte = new JButton("Seguinte"); - - seguinte.addActionListener(e -> { - fase++; - if (fase > 2) { - fase = 0; - } - if (fase == 0) { - luz.setBackground(Color.RED); - } else if (fase == 1) { - luz.setBackground(Color.GREEN); - } else { - luz.setBackground(Color.YELLOW); - } - }); - - janela.add(luz, BorderLayout.CENTER); - janela.add(seguinte, BorderLayout.SOUTH); - janela.setVisible(true); - } -} -``` - -Aqui entra um layout novo, o **BorderLayout**. Tem cinco lugares: `NORTH` (em cima), `SOUTH` (em baixo), `WEST` (à esquerda), `EAST` (à direita) e `CENTER` (no meio). O meio fica com todo o espaço que sobra. Assim a luz fica grande e o botão fica estreito em baixo. - -::: angeberwissen -O BorderLayout é o layout que um `JFrame` usa quando não lhe dás nenhum. Um `add` sem indicação de lugar vai para o `CENTER`. É por isso que na lição 31 as peças ficaram todas umas por cima das outras. -::: - -::: angeberwissen -Os profissionais avançam a fase numa linha: `fase = (fase + 1) % 3;`. O operador de resto `%` dá o resto da divisão. 3 % 3 é 0, por isso depois do 2 recomeça no 0. -::: - -## Que layout para quê? - -Já conheces três layouts. Cada um arruma os blocos de outra maneira: - -| Layout | Como arruma | Bom para | -|---|---|---| -| `FlowLayout` (lição 29) | lado a lado, como palavras numa linha, cada bloco do tamanho necessário | alguns botões numa fila | -| `GridLayout` (lição 31) | uma grelha de células do mesmo tamanho, preenchida linha a linha | formulários, teclados como o da calculadora | -| `BorderLayout` (esta lição) | cinco lugares: em cima, em baixo, à esquerda, à direita e o meio, que fica com o resto | algo grande no meio, por exemplo uma imagem ou um mostrador, com botões na margem | - -Sem `setLayout`, uma janela usa o BorderLayout. É por isso que, nesse caso, tudo vai para o meio. - -::: ludwig -Ao planear, pergunta-te: todas as partes devem ter o mesmo tamanho? Então GridLayout. Uma parte deve ser grande e o resto estreito na margem? Então BorderLayout. As partes devem simplesmente manter o tamanho normal? Então FlowLayout. -::: - -## Jogo: Onde fica o botão? - -::: spiel layout -titel: Jogo: Onde fica o botão? -intro: Já conheces três layouts e a janela sem layout nenhum. O Ludwig mostra-te código com os botões A, B, C … Como fica a janela? Toca na imagem certa. -frage: Como fica a janela? - -runde: - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Sem layout próprio, cada `add` vai para o meio, do tamanho da janela. Os três ficam uns por cima dos outros e só se vê o último: C. - -runde: - janela.setLayout(new FlowLayout()); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> O FlowLayout põe os botões no tamanho normal lado a lado, como palavras numa linha, e centra-os. - -runde: - janela.setLayout(new GridLayout(0, 1)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> Uma coluna e tantas linhas quantas forem precisas: tudo uns por baixo dos outros, cada linha com a mesma altura e tão larga como a janela. - -runde: - janela.setLayout(new GridLayout(0, 2)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); - janela.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Duas colunas. As células são preenchidas linha a linha, sempre primeiro à esquerda e depois à direita: A B, depois C D. - -runde: - janela.setLayout(new GridLayout(2, 3)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); - janela.add(new JButton("D")); - janela.add(new JButton("E")); - janela.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Primeiro vêm as linhas, depois as colunas: 2 linhas com 3 colunas cada. - -runde: - janela.setLayout(new GridLayout(0, 2)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Duas colunas, três botões: a primeira linha fica cheia, na segunda o C fica à esquerda e a célula da direita fica vazia. - -runde: - janela.setLayout(new BorderLayout()); - janela.add(new JButton("A"), BorderLayout.NORTH); - janela.add(new JButton("B"), BorderLayout.CENTER); - janela.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH é em cima, SOUTH em baixo. O meio fica com todo o espaço que sobra, por isso o B é grande. - -runde: - janela.setLayout(new BorderLayout()); - janela.add(new JButton("A"), BorderLayout.WEST); - janela.add(new JButton("B")); - janela.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST é à esquerda, EAST à direita. O B não tem lugar indicado e por isso vai para o CENTER. -::: - -## Tarefa: semáforo - -Reconstrói o semáforo e torna-o mais realista. Em alguns países, como a Alemanha, depois do vermelho acendem durante pouco tempo o vermelho e o amarelo juntos. Acrescenta por isso uma quarta fase: - -- 0: vermelho -- 1: vermelho e amarelo (usa `Color.ORANGE`) -- 2: verde -- 3: amarelo - -Extra: em cada fase, escreve também com `luz.setText(…)` o que o semáforo quer dizer, por exemplo “Parar” ou “Avançar”. - -**Terminaste quando …** - -- cada clique mostra a fase seguinte: vermelho, vermelho-amarelo (laranja), verde, amarelo, -- depois do amarelo volta o vermelho, -- no extra, cada fase mostra o texto certo. - -::: ludwig -Com quatro fases, depois do 3 vem o 0. Ajusta por isso o número em `if (fase > 2)`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pintar uma etiqueta com setOpaque e setBackground. → Cores no Swing -- Consigo guardar um estado numa variável static. → Lembrar a cor -- Consigo usar o BorderLayout para pôr algo grande no meio e algo estreito em baixo. → O programa completo -- Consigo explicar para que servem o FlowLayout, o GridLayout e o BorderLayout. → Que layout para quê? -- Consigo programar um semáforo com quatro fases. → Tarefa: semáforo -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que a cor fica invisível sem setOpaque(true)? -→ Cores no Swing -- [ ] Porque Color.RED não existe -- [x] Porque, sem isso, uma etiqueta é transparente -- [ ] Porque a janela é demasiado pequena - -? Que lugar do BorderLayout fica com o espaço que sobra? -→ O programa completo -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? fase é 2 e o ouvinte executa fase++ e depois if (fase > 2) { fase = 0; }. Que valor tem fase a seguir? -→ Lembrar a cor -- [x] 0 -- [ ] 2 -- [ ] 3 -> Depois de fase++ é 3, que é maior do que 2, por isso passa a 0. -::: diff --git a/web/lessons/pt/34-notenrechner.md b/web/lessons/pt/34-notenrechner.md deleted file mode 100644 index 0d29db31..00000000 --- a/web/lessons/pt/34-notenrechner.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -titel: Calculadora de notas na janela -bereich: Janelas com Swing -kurz: Escrever a percentagem, determinar o nível com else if e apanhar entradas erradas. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- transformar uma percentagem num nível com else if -- mostrar o nível na janela -- apanhar e avisar “abc” e valores fora de 0 a 100 -- testar um programa nos limites, por exemplo com 90 e 89 -::: - -## O objetivo - -Uma janela transforma a percentagem de um teste num nível: escreves a percentagem (0 a 100), clicas em **Calcular nível** e em baixo aparece o nível. - -Desta vez não há nenhuma peça nova do Swing. A novidade é que combinas tudo o que já sabes: campo de texto, botão, `else if` da lição 20 e try/catch da lição 22. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## A escala de níveis - -Usamos a escala de níveis do ensino básico: - -| Percentagem | Nível | -|---|---| -| 90 a 100 | 5 | -| 70 a 89 | 4 | -| 50 a 69 | 3 | -| 20 a 49 | 2 | -| 0 a 19 | 1 | - -Com `else if` verificas de cima para baixo. Ganha a primeira condição verdadeira: - -```java -int nivel; -if (percentagem >= 90) { - nivel = 5; -} else if (percentagem >= 70) { - nivel = 4; -} else if (percentagem >= 50) { - nivel = 3; -} else if (percentagem >= 20) { - nivel = 2; -} else { - nivel = 1; -} -``` - -::: ludwig -Com 75 %, `percentagem >= 90` é falso, mas `percentagem >= 70` é verdadeiro. Portanto nível 4, e as restantes condições já nem são verificadas. É por isso que basta sempre o limite de baixo. -::: - -Podes experimentar esta parte sem janela na área [Código](#zeige-codelive). Escreve `int percentagem = 75;` antes e `System.out.println("Nível: " + nivel);` depois. O estrutograma mostra-te as decisões encaixadas. - -::: quiz -? Que nota dá a tabela com 29 pontos? -- [ ] 4 -- [ ] 5 -- [x] 6 -> Todas as condições são falsas, também `pontos >= 30`. Então vale o ramo `else`. -::: - -## A janela - -```java -import javax.swing.*; -import java.awt.*; - -public class CalculadoraNiveis { - public static void main(String[] args) { - JFrame janela = new JFrame("Calculadora de níveis"); - janela.setSize(260, 180); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel pergunta = new JLabel("Percentagem (0 a 100):"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular nível"); - JLabel resultado = new JLabel("Nível: ?"); - - botao.addActionListener(e -> { - int percentagem = Integer.parseInt(campo.getText()); - int nivel; - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 20) { - nivel = 2; - } else { - nivel = 1; - } - resultado.setText("Nível: " + nivel); - }); - - janela.add(pergunta); - janela.add(campo); - janela.add(botao); - janela.add(resultado); - janela.setVisible(true); - } -} -``` - -Experimenta 100, 75, 50 e 12. - -## Apanhar entradas erradas - -Ainda há duas coisas que podem correr mal: - -1. Alguém escreve “abc”. Então `Integer.parseInt` lança uma `NumberFormatException`. -2. Alguém escreve 150 ou −5. É um número, mas não é uma percentagem válida. - -O primeiro caso apanhas com try/catch, o segundo com um `if` antes de calcular o nível: - -```java -botao.addActionListener(e -> { - try { - int percentagem = Integer.parseInt(campo.getText()); - if (percentagem < 0 || percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - // aqui o cálculo do nível de cima - } - } catch (NumberFormatException erro) { - resultado.setText("Isso não é um número."); - } -}); -``` - -::: ludwig -`||` quer dizer “ou” (lição 9). A condição é verdadeira se a percentagem for demasiado pequena **ou** demasiado grande. -::: - -## Jogo: Que nível? - -::: spiel frage -titel: Jogo: Que nível? -intro: O Ludwig construiu calculadoras de níveis, mas nem todas estão certas. Calcula que nível sai. Seis rondas. - -runde: - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else { - nivel = 2; - } -frage: percentagem é 70. Que nível sai? -- 5 -- [x] 4 -- 3 -> 70 >= 90 é falso, 70 >= 70 é verdadeiro. O limite ainda pertence ao 4. - -runde: - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 20) { - nivel = 2; - } else { - nivel = 1; - } -frage: percentagem é 49. Que nível sai? -- 3 -- [x] 2 -- 1 -> 49 >= 50 é falso por pouco, 49 >= 20 é verdadeiro. Portanto nível 2. - -runde: - if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 90) { - nivel = 5; - } else { - nivel = 1; - } -frage: percentagem é 95. Que nível sai? -- 5 -- [x] 3 -- 1 -> Ganha a primeira condição verdadeira. 95 >= 50 já é verdadeiro, o 5 nunca é alcançado. Por isso verifica-se das percentagens altas para baixo. - -runde: - nivel = 1; - if (percentagem >= 20) { - nivel = 2; - } - if (percentagem >= 50) { - nivel = 3; - } -frage: percentagem é 80. Que valor tem nivel depois? -- 1 -- 2 -- [x] 3 -> Sem `else`, todos os `if` são verificados. Os dois são verdadeiros, e o último dá o nível 3. - -runde: - // nivel já está calculado - if (percentagem < 0 || percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - resultado.setText("Nível: " + nivel); - } -frage: No campo de texto está 100. O que mostra a janela? -- [x] Nível: 5 -- Escreve um valor de 0 a 100. -- Isso não é um número. -> 100 > 100 é falso. Portanto 100 é válido e dá o nível 5. - -runde: - // nivel já está calculado - if (percentagem < 0 && percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - resultado.setText("Nível: " + nivel); - } -frage: No campo de texto está 150. O que mostra a janela? -- Escreve um valor de 0 a 100. -- [x] um nível, embora 150 não seja válido -- Isso não é um número. -> Com `&&`, a percentagem teria de ser ao mesmo tempo menor que 0 e maior que 100. Isso nunca acontece, por isso o programa nunca avisa. O certo é `||`. -::: - -## Tarefa: calculadora de níveis - -Junta a calculadora de níveis, com as duas verificações do último passo. Testa com 90, 89, 0, 100, 101 e “dez”. - -Extra: escreve também a menção do nível, por exemplo “Nível: 4 (Bom)”. As menções são Muito bom, Bom, Suficiente, Insuficiente e Fraco. - -**Terminaste quando …** - -- 92 e 100 dão 1, 91 dá 2 e 0 dá 6, -- 101 mostra o aviso para escrever 0 a 100, -- um texto como “dez” mostra um aviso e o programa continua a funcionar. - -::: ludwig -Testa sempre os limites: 90 tem de dar 5, 89 tem de dar 4. É nos limites que se escondem a maioria dos erros, por exemplo `>` em vez de `>=`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo transformar uma percentagem num nível com else if. → A escala de níveis -- Consigo mostrar o nível na janela. → A janela -- Consigo apanhar e avisar “abc” e valores fora de 0 a 100. → Apanhar entradas erradas -- Consigo testar um programa nos limites, por exemplo com 90 e 89. → Tarefa: calculadora de níveis -::: - -## Teste de conhecimentos - -::: quiz -? Que nível dá a escala com 70 %? -→ A escala de níveis -- [ ] 3 -- [x] 4 -- [ ] 5 -> 70 é o limite de baixo do nível 4. - -? Porque é que em cada condição basta o limite de baixo? -→ A escala de níveis -- [x] Porque o else if já separou antes os casos mais altos -- [ ] Porque o Java não conhece limites de cima -- [ ] Porque a percentagem é sempre 100 - -? Que condição deteta percentagens inválidas? -→ Apanhar entradas erradas -- [ ] percentagem < 0 && percentagem > 100 -- [x] percentagem < 0 || percentagem > 100 -- [ ] percentagem >= 0 || percentagem <= 100 -> Um número não pode ser ao mesmo tempo menor do que 0 e maior do que 100, por isso é preciso o ou. -::: diff --git a/web/lessons/pt/35-taschenrechner.md b/web/lessons/pt/35-taschenrechner.md deleted file mode 100644 index d98fc585..00000000 --- a/web/lessons/pt/35-taschenrechner.md +++ /dev/null @@ -1,285 +0,0 @@ ---- -titel: Uma calculadora -bereich: Janelas com Swing -kurz: Duas colunas com GridLayout, quatro botões de cálculo e apanhar a divisão por 0. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir duas colunas com GridLayout -- explicar porque é que "" + (a + b) precisa de parênteses -- programar os quatro botões de cálculo -- construir uma calculadora que não falha com 0 nem com “abc” -::: - -## O objetivo - -Vamos construir uma pequena calculadora: dois campos de texto para os números, quatro botões **+**, **-**, **\*** e **/** e uma linha para o resultado. - -As novidades desta vez são **duas colunas** no GridLayout e **vários botões** que calculam todos com os mesmos campos de texto. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Duas colunas - -Até agora, com `GridLayout(0, 1)`, ficava tudo umas por baixo das outras. Com `GridLayout(0, 2)` há duas colunas. As peças preenchem as células pela ordem das linhas `add`, sempre primeiro à esquerda e depois à direita: - -| esquerda | direita | -|---|---| -| Primeiro número: | campo de texto | -| Segundo número: | campo de texto | -| + | - | -| \* | / | -| Resultado: | ? | - -As etiquetas da esquerda não vais precisar delas mais tarde. Por isso podes construí-las logo dentro do `add`, sem variável: - -```java -janela.add(new JLabel("Primeiro número:")); -janela.add(campo1); -``` - -## A janela com o primeiro botão - -```java -import javax.swing.*; -import java.awt.*; - -public class Calculadora { - public static void main(String[] args) { - JFrame janela = new JFrame("Calculadora"); - janela.setSize(280, 220); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 2)); - - JTextField campo1 = new JTextField(); - JTextField campo2 = new JTextField(); - JButton mais = new JButton("+"); - JButton menos = new JButton("-"); - JButton vezes = new JButton("*"); - JButton dividir = new JButton("/"); - JLabel resultado = new JLabel("?"); - - mais.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a + b)); - }); - - janela.add(new JLabel("Primeiro número:")); - janela.add(campo1); - janela.add(new JLabel("Segundo número:")); - janela.add(campo2); - janela.add(mais); - janela.add(menos); - janela.add(vezes); - janela.add(dividir); - janela.add(new JLabel("Resultado:")); - janela.add(resultado); - janela.setVisible(true); - } -} -``` - -Escreve 3 e 4 e clica em +. Em baixo aparece 7. - -::: ludwig -Atenção aos parênteses em `"" + (a + b)`. Sem eles, o Java calcula da esquerda para a direita: `"" + a` dá o texto “3”, e `"3" + b` junta o 4 no fim. Sai “34” em vez de 7. O parêntese garante que primeiro se faz a conta. -::: - -## Os outros três botões - -Os botões -, \* e / funcionam da mesma maneira. Só muda o sinal na última linha: - -```java -menos.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a - b)); -}); -``` - -O / tem duas particularidades: - -- Com `int`, o Java corta as casas decimais ao dividir. 7 / 2 dá 3. -- Se o segundo número for 0, o Java lança uma `ArithmeticException`, como na tarefa “Dividir em segurança” da lição 22. - -## Apanhar a divisão por 0 - -```java -dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } -}); -``` - -::: angeberwissen -Com `double` e `Double.parseDouble(…)` há casas decimais: 7 / 2 dá 3.5. Em contrapartida, com números decimais o Java não lança nenhuma exceção ao dividir por 0. O resultado é então `Infinity`, ou seja, “infinito”. -::: - -::: quiz -? O que mostra a calculadora com 8 a dividir por 0? -- [ ] 0 -- [ ] Infinity -- [x] Não se divide por 0! -> Com `int`, `a / b` lança uma `ArithmeticException`. O `catch` apanha-a e mostra o aviso. -::: - -## Um método próprio - -Isto não está no programa da disciplina, mas torna o código mais curto. - -::: angeberwissen -Cada ouvinte lê os mesmos dois campos. Podes passar esta repetição para um método próprio, um subprograma como na lição 18. Fica na classe, ao lado do `main`: - -`static int numero(JTextField campo) { return Integer.parseInt(campo.getText()); }` - -No ouvinte basta então escrever `resultado.setText("" + (numero(campo1) + numero(campo2)));`. -::: - -## Jogo: O que mostra a calculadora? - -::: spiel frage -titel: Jogo: O que mostra a calculadora? -intro: O Ludwig andou a mexer na calculadora. Lê o ouvinte e prevê o que aparece no resultado. Seis rondas. - -runde: - mais.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + a + b); - }); -frage: Escreves 3 e 4 e clicas em +. O que aparece no resultado? -- 7 -- [x] 34 -- 12 -> Sem parênteses, o Java trabalha da esquerda para a direita: `"" + 3` é o texto "3", e `+ 4` junta o 4 no fim. - -runde: - vezes.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + a * b); - }); -frage: Escreves 3 e 4 e clicas em *. O que aparece no resultado? -- 34 -- [x] 12 -- 7 -> A multiplicação vem antes da soma também aqui: o Java calcula primeiro 3 * 4 e depois junta o 12 ao texto. O mais não tem esta prioridade, por isso precisa dos parênteses. - -runde: - dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a / b)); - }); -frage: Escreves 7 e 2 e clicas em /. O que aparece no resultado? -- 3.5 -- [x] 3 -- 4 -> Com `int`, o Java corta as casas decimais. Não arredonda. - -runde: - dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } - }); -frage: Escreves 8 e 0 e clicas em /. O que aparece no resultado? -- 0 -- [x] Não se divide por 0! -- Infinity -> Com `int`, dividir por 0 lança uma ArithmeticException, e o `catch` escreve o aviso. - -runde: - dividir.addActionListener(e -> { - double a = Double.parseDouble(campo1.getText()); - double b = Double.parseDouble(campo2.getText()); - resultado.setText("" + (a / b)); - }); -frage: Escreves 7 e 2 e clicas em /. O que aparece no resultado? -- 3 -- [x] 3.5 -- 3.0 -> Com `double`, as casas decimais ficam. - -runde: - dividir.addActionListener(e -> { - double a = Double.parseDouble(campo1.getText()); - double b = Double.parseDouble(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } - }); -frage: Escreves 8 e 0 e clicas em /. O que aparece no resultado? -- Não se divide por 0! -- [x] Infinity -- 0.0 -> Com `double` não há exceção. O resultado é `Infinity`, ou seja "infinito", e o `catch` nunca chega a entrar. -::: - -## Tarefa: calculadora - -Acaba a calculadora: - -- os quatro botões calculam, -- o / avisa “Não se divide por 0!” em vez de falhar, -- se um campo tiver “abc” ou estiver vazio, aparece “Escreve dois números”. - -Extra: um quinto botão **C** esvazia os dois campos e volta a pôr o resultado em “?”. - -**Terminaste quando …** - -- 7 e 2 dão 9, 5, 14 e 3 em +, −, * e /, -- 7 / 0 mostra a mensagem de não dividir por 0, -- “abc” ou um campo vazio mostra a mensagem para escrever dois números e nada falha. - -::: ludwig -Para o “abc” cada ouvinte precisa de um try/catch com `NumberFormatException`. Depois de um `try` também podem vir dois `catch`, um para `NumberFormatException` e outro para `ArithmeticException`. Já agora: um campo vazio dá o texto `""`, e isso também não é um número. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir duas colunas com GridLayout(0, 2). → Duas colunas -- Consigo explicar porque é que "" + (a + b) precisa de parênteses. → A janela com o primeiro botão -- Consigo programar os quatro botões de cálculo. → Os outros três botões -- Consigo construir uma calculadora que não falha com 0 nem com “abc”. → Tarefa: calculadora -::: - -## Teste de conhecimentos - -::: quiz -? a é 3 e b é 4. Quanto dá "" + a + b? -→ A janela com o primeiro botão -- [ ] 7 -- [x] 34 -- [ ] Um erro -> O Java calcula da esquerda para a direita: primeiro surge o texto "3", depois junta-se o 4. - -? Quanto dá 7 / 2 com int? -→ Os outros três botões -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? Por que ordem preenche o GridLayout(0, 2) as células? -→ Duas colunas -- [x] Linha a linha, primeiro à esquerda e depois à direita -- [ ] Coluna a coluna, começando de cima para baixo na coluna da esquerda -- [ ] Ao acaso -::: diff --git a/web/lessons/pt/36-zahlenraten.md b/web/lessons/pt/36-zahlenraten.md deleted file mode 100644 index 51330f45..00000000 --- a/web/lessons/pt/36-zahlenraten.md +++ /dev/null @@ -1,242 +0,0 @@ ---- -titel: Adivinha o número -bereich: Janelas com Swing -kurz: Um jogo com número aleatório, contagem de tentativas e um botão que se desliga no fim. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar com Math.random um número inteiro aleatório de 1 a 100 -- decidir que variáveis pertencem à classe -- desligar um botão com setEnabled -- construir um jogo de adivinhar com um botão “Novo jogo” -::: - -## O objetivo - -Agora um jogo: o computador pensa num número de 1 a 100. Tu adivinhas, e a janela diz-te “demasiado pequeno” ou “demasiado grande”. Quando o encontrares, diz-te quantas tentativas precisaste. - -As novidades desta vez são os **números aleatórios** e um botão que se **desliga** no fim. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Um número aleatório - -`Math.random()` dá um número decimal aleatório de 0 até quase 1, por exemplo 0.4173. Em três passos transforma-se num número inteiro de 1 a 100: - -```java -int secreto = (int) (Math.random() * 100) + 1; -``` - -| Passo | Exemplo | -|---|---| -| `Math.random()` | 0.4173 | -| `* 100` | 41.73 | -| `(int)` corta as casas decimais | 41 | -| `+ 1` | 42 | - -Sem o `+ 1` sairia de 0 a 99. - -Experimenta na área [Código](#zeige-codelive): `int secreto = (int) (Math.random() * 100) + 1;` e `System.out.println(secreto);`. Cada execução dá um número diferente. - -::: angeberwissen -Na verdade, os computadores não sabem lançar dados. O `Math.random()` calcula com uma fórmula uma longa sequência de números que só **parecem** aleatórios. Chamam-se números pseudoaleatórios. Para jogos chega perfeitamente. -::: - -## O que o jogo guarda - -O jogo tem de se lembrar de duas coisas ao longo de todos os cliques: o número secreto e o número de tentativas. Tal como no contador de cliques da lição 32, as duas ficam na classe: - -```java -static int secreto = (int) (Math.random() * 100) + 1; -static int tentativas = 0; -``` - -::: quiz -? Porque é que `secreto` e `tentativas` estão na classe e não no `main`? -- [ ] Porque `Math.random()` só funciona na classe -- [x] Porque têm de durar ao longo de todos os cliques e o ouvinte tem de alterar `tentativas` -- [ ] Porque o `main` não consegue guardar números -> Como no contador de cliques: o ouvinte não pode alterar variáveis do `main`. -::: - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; - -public class AdivinhaNumero { - static int secreto = (int) (Math.random() * 100) + 1; - static int tentativas = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Adivinha o número"); - janela.setSize(320, 180); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel dica = new JLabel("Estou a pensar num número de 1 a 100."); - JTextField campo = new JTextField(); - JButton botao = new JButton("Adivinhar"); - - botao.addActionListener(e -> { - int palpite = Integer.parseInt(campo.getText()); - tentativas++; - if (palpite < secreto) { - dica.setText(palpite + " é demasiado pequeno."); - } else if (palpite > secreto) { - dica.setText(palpite + " é demasiado grande."); - } else { - dica.setText("Certo! Precisaste de " + tentativas + " tentativas."); - botao.setEnabled(false); - } - campo.setText(""); - }); - - janela.add(dica); - janela.add(campo); - janela.add(botao); - janela.setVisible(true); - } -} -``` - -- `campo.setText("")` esvazia o campo de texto depois de cada palpite, para escreveres logo o seguinte. -- `botao.setEnabled(false)` desliga o botão. Fica cinzento e já não se pode clicar. O jogo acabou. - -::: ludwig -Com `setEnabled(true)` voltas a ligar um botão. Assim podes, por exemplo, só ativar um botão depois de alguém ter escrito alguma coisa. -::: - -## Ganhar com 7 tentativas - -::: angeberwissen -Quem adivinha com jeito nunca precisa de mais de 7 tentativas. Escolhe sempre o meio do intervalo que ainda resta: primeiro 50, depois 25 ou 75 e assim por diante. Cada palpite corta o intervalo a meio: 100, 50, 25, 13, 7, 4, 2, 1. É a **pesquisa binária** da lição 19. Como 2 elevado a 7 = 128 é maior do que 100, chegam 7 palpites. -::: - -## Tarefa: adivinha o número - -Reconstrói o jogo e melhora-o: - -- Uma entrada como “abc” não conta como tentativa e mostra a dica “Escreve um número”. -- Um segundo botão **Novo jogo** pensa num número novo, põe as tentativas a 0, volta a ligar o botão Adivinhar e repõe a dica. - -Extra: depois de 7 tentativas falhadas o jogo está perdido. A janela revela então o número secreto. - -**Terminaste quando …** - -- cada palpite errado mostra “demasiado pequeno” ou “demasiado grande”, -- “abc” mostra o aviso para escrever um número e as tentativas não mudam, -- **Novo jogo** põe as tentativas a 0, volta a ligar o botão de adivinhar e repõe o aviso. - -::: ludwig -Para “não conta como tentativa”, o `tentativas++` tem de vir **depois** de `Integer.parseInt`, os dois dentro do bloco `try`. Se o `parseInt` lançar uma exceção, a linha com `tentativas++` é saltada. -::: - -## Jogo: Demasiado pequeno ou demasiado grande? - -::: spiel frage -titel: Jogo: Demasiado pequeno ou demasiado grande? -intro: O Ludwig está a jogar ao adivinha o número. Lê o código e prevê o que acontece. Sete rondas. - -runde: - // secreto é 42 - if (palpite < secreto) { - dica.setText(palpite + " é demasiado pequeno."); - } else if (palpite > secreto) { - dica.setText(palpite + " é demasiado grande."); - } else { - dica.setText("Certo!"); - } -frage: O Ludwig escreve 50. O que diz a dica? -- 50 é demasiado pequeno. -- [x] 50 é demasiado grande. -- Certo! -> 50 é maior do que 42. - -runde: -frage: O número está entre 1 e 100. 50 era demasiado grande, 25 era demasiado pequeno. Que número é que o Ludwig, esperto, tenta a seguir? -- 26 -- [x] 37 -- 49 -> Sobram 26 a 49. O meio é 37 (ou 38). Assim, cada palpite corta o intervalo a meio. - -runde: - int dado = (int) (Math.random() * 6) + 1; -frage: Que números pode ter `dado`? -- 0 a 6 -- [x] 1 a 6 -- 1 a 7 -> `Math.random() * 6` vai de 0 até um pouco menos de 6. `(int)` faz disso 0 a 5, e `+ 1` faz 1 a 6. Um dado! - -runde: - int numero = (int) (Math.random() * 10); -frage: Que números pode ter `numero`? -- [x] 0 a 9 -- 1 a 10 -- 0 a 10 -> Sem `+ 1` começa em 0, e o 10 nunca é alcançado. - -runde: - // secreto é 42 - tentativas++; - if (palpite == secreto) { - dica.setText("Certo! Precisaste de " + tentativas + " tentativas."); - } -frage: O Ludwig escreve 50, depois 25, depois 42. O que diz a dica no fim? -- Certo! Precisaste de 2 tentativas. -- [x] Certo! Precisaste de 3 tentativas. -- Certo! Precisaste de 42 tentativas. -> `tentativas++` conta a cada clique, também no acerto. - -runde: - botao.setEnabled(false); -frage: O Ludwig encontrou o número e clica mais uma vez em **Adivinhar**. O que acontece? -- O jogo recomeça. -- Conta mais uma tentativa. -- [x] Nada, o botão fica cinzento e não se pode clicar. -> `setEnabled(false)` desliga o botão. - -runde: -frage: Agora o número está entre 1 e 1000. Quantos palpites são precisos, no máximo, com a pesquisa binária? -- 7 -- [x] 10 -- 500 -> 2 elevado a 10 = 1024 é maior do que 1000. Dez divisões a meio chegam. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar com Math.random um número inteiro aleatório de 1 a 100. → Um número aleatório -- Consigo decidir que variáveis pertencem à classe. → O que o jogo guarda -- Consigo desligar um botão com setEnabled. → O programa completo -- Consigo construir um jogo de adivinhar com um botão “Novo jogo”. → Tarefa: adivinha o número -::: - -## Teste de conhecimentos - -::: quiz -? Que números dá (int) (Math.random() * 100) + 1? -→ Um número aleatório -- [ ] 0 a 100 -- [x] 1 a 100 -- [ ] 1 a 101 - -? O que faz botao.setEnabled(false)? -→ O programa completo -- [ ] Tira o botão da janela. -- [x] O botão fica cinzento e já não se pode clicar. -- [ ] Muda o texto do botão. - -? Com a pesquisa binária, quantas tentativas precisas no máximo para 1 a 100? -→ Tarefa: adivinha o número -- [ ] 50 -- [ ] 10 -- [x] 7 -> Cada palpite corta o intervalo a meio, e 2 elevado a 7 = 128 é maior do que 100. -::: diff --git a/web/lessons/pt/37-viele-knoepfe-ein-zuhoerer.md b/web/lessons/pt/37-viele-knoepfe-ein-zuhoerer.md deleted file mode 100644 index b6c3a01b..00000000 --- a/web/lessons/pt/37-viele-knoepfe-ein-zuhoerer.md +++ /dev/null @@ -1,318 +0,0 @@ ---- -titel: Muitos botões, um ouvinte -bereich: Janelas com Swing -kurz: Um ouvinte para muitos botões e descobrir com getActionCommand e getSource qual foi o botão clicado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- guardar um ouvinte numa variável do tipo ActionListener e registá-lo em vários botões -- explicar o que devolvem getActionCommand e getSource -- pôr botões num JPanel com um ciclo -- construir um cadeado que só abre com o código certo -::: - -## O objetivo - -Desde a história do bolo, o Ludwig quer trancar o seu estábulo. Ele constrói um **cadeado de código**: nove teclas com algarismos, um zero, **C** para apagar e **OK** para verificar. Só quem escrever 1234 recebe "Portão aberto!". - -São doze botões. Até agora, cada botão tinha o seu próprio ouvinte, como na calculadora da lição 35. Hoje aprendes como **um** ouvinte chega para todos os botões. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Nove ouvintes quase iguais - -Com um ouvinte por tecla ficaria assim: - -```java -um.addActionListener(e -> visor.setText(visor.getText() + "1")); -dois.addActionListener(e -> visor.setText(visor.getText() + "2")); -tres.addActionListener(e -> visor.setText(visor.getText() + "3")); -// ... e assim por diante até 9 -``` - -As linhas só se distinguem pelo algarismo. Quem quiser mudar um pormenor tem de o fazer nove vezes e de certeza que se esquece de uma linha. - -## Um ouvinte numa variável - -Um ouvinte também pode ficar numa **variável**. O seu tipo de dados é `ActionListener`, o contrato da lição 30. Depois registas o mesmo ouvinte em vários botões: - -```java -ActionListener algarismo = e -> visor.setText(visor.getText() + e.getActionCommand()); - -um.addActionListener(algarismo); -dois.addActionListener(algarismo); -tres.addActionListener(algarismo); -``` - -`ActionListener` está no pacote `java.awt.event`. Por isso precisas de `import java.awt.event.*;` lá em cima. - -Agora o código só existe uma vez. Mas como sabe o ouvinte que algarismo deve acrescentar? - -## Quem clicou? - -O `e` antes da seta é o **evento**, um objeto do tipo `ActionEvent`. O botão entrega-o a cada clique, e lá dentro está o que aconteceu: - -| Chamada | Devolve | Exemplo | -|---|---|---| -| `e.getActionCommand()` | a legenda do botão clicado, como texto | `"7"` | -| `e.getSource()` | o próprio botão clicado | o botão `ok` | - -- `getActionCommand()` dá jeito quando a legenda já diz o que fazer, como nos algarismos. -- `getSource()` compara-se com um botão usando `==`: `if (e.getSource() == ok)`. Aqui `==` está certo, porque se trata exatamente deste objeto, não de um texto. - -::: ludwig -Textos comparas com `equals`, botões com `==`. Se quiseres decidir pela legenda, escreves `e.getActionCommand().equals("OK")`. Também funciona, mas deixa de funcionar assim que alguém mudar a legenda para "Abrir". `e.getSource() == ok` continua certo. -::: - -## Botões num ciclo - -Com um ouvinte partilhado, podes até criar as teclas dos algarismos num ciclo. Para ficarem bem arrumadas numa grelha, vão para um **JPanel**. Um JPanel é uma área na janela com o seu próprio layout: - -```java -JPanel teclas = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton algarismo = new JButton("" + i); - algarismo.addActionListener(tecla); - teclas.add(algarismo); -} -``` - -- `"" + i` transforma o número `i` no texto da legenda. -- Em cada volta nasce um botão novo. Todos recebem o mesmo ouvinte `tecla`. -- `janela.add(teclas, BorderLayout.CENTER)` põe a área inteira, com todas as teclas, na janela. - -::: quiz -? Quantos botões e quantos ouvintes cria este ciclo? -- [ ] 9 botões e 9 ouvintes -- [x] 9 botões e 1 ouvinte -- [ ] 1 botão e 9 ouvintes -> Em cada passagem nasce um botão novo. Todos recebem o mesmo ouvinte `tecla`. -::: - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Cadeado { - public static void main(String[] args) { - JFrame janela = new JFrame("Cadeado de código"); - janela.setSize(240, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new BorderLayout()); - - JLabel visor = new JLabel("", SwingConstants.CENTER); - JPanel teclas = new JPanel(new GridLayout(0, 3)); - JButton apagar = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener tecla = e -> { - if (e.getSource() == apagar) { - visor.setText(""); - } else if (e.getSource() == ok) { - if (visor.getText().equals("1234")) { - visor.setText("Portão aberto!"); - } else { - visor.setText("Errado"); - } - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton algarismo = new JButton("" + i); - algarismo.addActionListener(tecla); - teclas.add(algarismo); - } - JButton zero = new JButton("0"); - zero.addActionListener(tecla); - apagar.addActionListener(tecla); - ok.addActionListener(tecla); - teclas.add(apagar); - teclas.add(zero); - teclas.add(ok); - - janela.add(visor, BorderLayout.NORTH); - janela.add(teclas, BorderLayout.CENTER); - janela.setVisible(true); - } -} -``` - -- `apagar` e `ok` são criados **antes** do ouvinte, porque o ouvinte compara com eles. -- O ouvinte verifica primeiro os dois botões especiais. Todos os outros são algarismos e acrescentam a sua legenda. - -::: angeberwissen -O teclado do telefone tem o 1 em cima à esquerda, o da calculadora e do teclado do computador tem o 7 em cima à esquerda. Porquê? Quando chegaram os telefones de teclas, fizeram-se testes com pessoas que tinham de marcar números com eles. Com o 1 em cima eram mais rápidas, e as máquinas de calcular já tinham a sua disposição há muito tempo. O cadeado do Ludwig faz como o telefone. -::: - -## Jogo: Quem clicou? - -::: spiel zuhoerer -titel: Jogo: Quem clicou? -intro: Em cada ronda, **um** ouvinte está registado em vários botões. O Ludwig mostra-te o código, a janela e os cliques. O que fica no visor no fim? Depois o Ludwig reproduz os cliques. -frage: O que fica no visor depois dos cliques? - -runde: - JFrame janela = new JFrame("Letras"); - JLabel visor = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> visor.setText(visor.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> Um ouvinte, dois botões. `e.getActionCommand()` devolve de cada vez a legenda do botão clicado, e ela é acrescentada no fim. - -runde: - JFrame janela = new JFrame("Contador"); - static int numero = 0; - JLabel visor = new JLabel("0"); - JButton mais = new JButton("+"); - JButton menos = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == mais) { - numero++; - } else { - numero--; - } - visor.setText("" + numero); - }; - mais.addActionListener(z); - menos.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> `e.getSource()` diz qual foi o botão. Duas vezes mais, uma vez menos: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame janela = new JFrame("Letras"); - JLabel visor = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> visor.setText(visor.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> O ouvinte só está registado em A e B. Quando se clica em C, ninguém está a ouvir. - -runde: - JFrame janela = new JFrame("Teclas"); - JLabel visor = new JLabel(""); - JButton um = new JButton("1"); - JButton dois = new JButton("2"); - JButton apagar = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - visor.setText(""); - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - um.addActionListener(z); - dois.addActionListener(z); - apagar.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> Depois de 1 e 2 o visor mostra "12". C apaga tudo, e o último 1 fica sozinho. - -runde: - JFrame janela = new JFrame("Cadeado"); - JLabel visor = new JLabel(""); - JButton quatro = new JButton("4"); - JButton dois = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (visor.getText().equals("42")) { - visor.setText("Portão aberto!"); - } else { - visor.setText("Errado"); - } - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - quatro.addActionListener(z); - dois.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Portão aberto! -- [x] Errado -- 24OK -> Foi escrito "24", mas o segredo é "42". A ordem dos cliques conta. -::: - -## Tarefa: Cadeado de código - -Constrói o cadeado de código e experimenta-o. - -Depois melhora-o: - -- Depois de três tentativas erradas, o **OK** desliga-se, como o botão do Adivinha o número na lição 36. Conta as tentativas numa variável `static int` na classe. -- Extra: o visor mostra `*` em vez dos algarismos. Guarda a entrada verdadeira numa `static String entrada = "";` e acrescenta ao visor só uma estrela. - -**Terminaste quando …** - -- o código certo abre o cadeado e um errado não, -- depois de três tentativas erradas o **OK** fica desligado, -- no extra, o visor só mostra estrelas mas o código continua a ser verificado. - -::: ludwig -No ramo do **OK**, conta `tentativas++` a cada código errado e depois verifica `if (tentativas >= 3)`. Aí `ok.setEnabled(false)` desliga o botão, como no Adivinha o número. Para as estrelas: `entrada = entrada + e.getActionCommand();` e `visor.setText(visor.getText() + "*");`. Depois comparas `entrada`, não o texto do visor. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo guardar um ouvinte numa variável do tipo ActionListener e registá-lo em vários botões. → Um ouvinte numa variável -- Consigo explicar o que devolvem getActionCommand e getSource. → Quem clicou? -- Consigo pôr botões num JPanel com um ciclo. → Botões num ciclo -- Consigo construir um cadeado que só abre com o código certo. → Tarefa: Cadeado de código -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve e.getActionCommand() num clique no botão new JButton("7")? -→ Quem clicou? -- [ ] o número 7 -- [x] o texto "7" -- [ ] o próprio botão - -? Como verificas se foi clicado o botão ok? -→ Quem clicou? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource devolve o próprio botão, e esse comparas com ==. - -? Porque é que um ouvinte fica numa variável? -→ Um ouvinte numa variável -- [ ] Para correr mais depressa. -- [x] Para o poder registar em vários botões e escrever o código só uma vez. -- [ ] Porque senão os lambdas não são permitidos. - -? Liga. -→ Quem clicou? -= ActionListener -> tipo de dados de um ouvinte -= ActionEvent -> o evento e com a informação sobre o clique -= JPanel -> uma área com o seu próprio layout -::: diff --git a/web/lessons/pt/38-innere-klassen.md b/web/lessons/pt/38-innere-klassen.md deleted file mode 100644 index 2679783d..00000000 --- a/web/lessons/pt/38-innere-klassen.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -titel: Classes internas -bereich: Janelas com Swing -kurz: Escrever um ouvinte como classe interna com nome, como classe anónima e como lambda, e saber quem vê o quê. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- escrever um ouvinte como classe interna com nome -- escrever um ouvinte como classe anónima -- explicar o que fica de fora no lambda -- explicar que maneira de escrever vê que variáveis -- construir um contador com três botões escritos de três maneiras -::: - -## O objetivo - -O contador de cliques da lição 32 já conheces. Hoje escreves o seu ouvinte de **três maneiras**, que fazem todas exatamente o mesmo: - -1. como **classe interna com nome**, como se fazia quase sempre antes do Java 8, -2. como **classe anónima**, que conheces da lição 30 (“ActionListener é uma interface”), -3. como **lambda** com a seta, como até agora. - -Em código de outras pessoas, em livros e na internet vais encontrar as três. Depois desta lição vais reconhecê-las. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Lembrete: o contrato - -Um ouvinte é tudo o que cumpre o contrato `ActionListener`. Nele está exatamente um método: - -``` -public void actionPerformed(ActionEvent e) -``` - -O botão chama-o a cada clique. As três maneiras de escrever são três caminhos para fornecer este único método. - -## 1. A classe interna com nome - -Uma **classe interna** fica dentro de outra classe. Tem um nome, aqui `Mais`, e cumpre o contrato com `implements ActionListener`: - -```java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Contador { - static int contagem = 0; - static JLabel numero = new JLabel("0", SwingConstants.CENTER); - - static class Mais implements ActionListener { - public void actionPerformed(ActionEvent e) { - contagem++; - numero.setText("" + contagem); - } - } - - public static void main(String[] args) { - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JButton mais = new JButton("+1"); - JButton outro = new JButton("Também +1"); - mais.addActionListener(new Mais()); - outro.addActionListener(new Mais()); - - janela.add(numero); - janela.add(mais); - janela.add(outro); - janela.setVisible(true); - } -} -``` - -- `new Mais()` cria um objeto da classe `Mais`. É esse o ouvinte que o botão recebe. -- Como a classe tem nome, podes usá-la em vários botões, aqui em `mais` e `outro`. -- A classe está **fora** de `main`. Por isso só vê o que está na classe `Contador`: `contagem` e `numero`. É por isso que `numero` desta vez é uma variável da classe e não de `main`. -- Precisas de `static class` porque `main` é static. Sem `static`, o javac indica "non-static variable this cannot be referenced from a static context". - -## 2. A classe anónima - -Se só precisas do ouvinte num sítio, podes escrever a classe mesmo onde o objeto é preciso. Assim ela não recebe nome: - -```java -mais.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - contagem++; - numero.setText("" + contagem); - } -}); -``` - -- `new ActionListener() { … }` quer dizer: "um objeto novo que cumpre o contrato ActionListener, e é assim: …". -- No fim está `});`: a chaveta fecha a classe, o parêntese fecha a chamada de `addActionListener`. -- A classe anónima está **dentro** de `main`. Por isso também vê as variáveis de `main`, desde que já não mudem. - -::: quiz -? O que fecha `});` no fim da classe anónima? -- [x] `}` fecha a classe, `)` fecha a chamada de `addActionListener`. -- [ ] `}` fecha o `main`, `)` fecha a classe. -- [ ] Os dois fecham o método `actionPerformed`. -> O método `actionPerformed` já termina uma linha antes com a sua própria `}`. -::: - -## 3. O lambda - -O lambda é a forma curta da classe anónima, desde o Java 8: - -```java -mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); -}); -``` - -Fica de fora tudo o que o Java já sabe: `new ActionListener()`, o nome `actionPerformed`, `public void` e o tipo `ActionEvent`. Sobram o parâmetro `e`, a seta e o corpo. Isto só funciona porque o contrato tem exatamente **um** método. - -## As três comparadas - -| | classe interna com nome | classe anónima | lambda | -|---|---|---|---| -| tem nome | sim, p. ex. `Mais` | não | não | -| fica | na classe, fora de `main` | mesmo junto ao botão | mesmo junto ao botão | -| para vários botões | sim, `new Mais()` | não | sim, numa variável (lição 37) | -| vê variáveis de `main` | não | sim, se não mudarem | sim, se não mudarem | -| comprimento | a mais longa | média | a mais curta | - -::: ludwig -Se uma variável de `main` tiver de mudar no ouvinte, como `contagem++`, tem de ir para a classe, seja qual for a maneira de escrever. Esse era o problema da lição 32. -::: - -::: angeberwissen -Depois de compilar, espreita a pasta com os ficheiros `.class`. O javac cria um ficheiro para cada classe: `Contador.class`, `Contador$Mais.class` para a classe interna e `Contador$1.class` para a classe anónima. Como não tem nome, recebe um número. Um lambda não tem ficheiro próprio, o Java só o constrói quando o programa arranca. -::: - -## Jogo: Que maneira de escrever? - -::: spiel frage -titel: Jogo: Que maneira de escrever? -intro: O Ludwig mostra-te ouvintes de programas de outras pessoas. Que maneira de escrever é esta? Seis rondas. -frage: Que maneira de escrever é esta? -antworten: classe interna com nome | classe anónima | lambda - -runde: - botao.addActionListener(e -> texto.setText("Olá")); -richtig: lambda -> A seta `->` denuncia o lambda. - -runde: - botao.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - texto.setText("Olá"); - } - }); -richtig: classe anónima -> `new ActionListener() {` e a seguir um método inteiro: aqui está a ser escrita uma classe sem nome. - -runde: - static class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - texto.setText("Olá"); - } - } -richtig: classe interna com nome -> A classe chama-se `Saudacao` e cumpre o contrato com `implements ActionListener`. - -runde: - botao.addActionListener(new Saudacao()); -richtig: classe interna com nome -> `new Saudacao()` cria um objeto da classe `Saudacao`. A própria classe está noutro sítio e tem nome. - -runde: - ActionListener tecla = e -> { - visor.setText(visor.getText() + e.getActionCommand()); - }; -richtig: lambda -> Um lambda numa variável, como no cadeado da lição 37. A seta denuncia-o. - -runde: - ActionListener tecla = new ActionListener() { - public void actionPerformed(ActionEvent e) { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; -richtig: classe anónima -> Uma classe anónima também pode ficar numa variável. Assim pode ser registada em vários botões, tal como o lambda. -::: - -## Tarefa: Um contador, três maneiras - -Constrói o contador de cliques com três botões: - -- **+1** recebe a classe interna com nome `Mais`. -- **-1** recebe uma classe anónima. -- **Reset** recebe um lambda. - -Experimenta se os três botões contam como esperado. Depois tira o `static` da classe `Mais` e lê a mensagem de erro do javac. - -**Terminaste quando …** - -- **+1** conta para cima, **-1** para baixo e **Reset** põe 0, -- o código tem exatamente uma classe interna com nome, uma classe anónima e uma lambda, -- leste a mensagem de erro do javac sem `static` em `Plus` e consegues explicar o que significa. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo escrever um ouvinte como classe interna com nome. → 1. A classe interna com nome -- Consigo escrever um ouvinte como classe anónima. → 2. A classe anónima -- Consigo explicar o que fica de fora no lambda. → 3. O lambda -- Consigo explicar que maneira de escrever vê que variáveis. → As três comparadas -- Consigo construir um contador com três botões escritos de três maneiras. → Tarefa: Um contador, três maneiras -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que a forma curta com a seta funciona com ActionListener? -→ 3. O lambda -- [ ] Porque ActionListener é uma classe. -- [x] Porque o contrato tem exatamente um método. -- [ ] Porque o botão o exige. - -? Que maneira de escrever podes usar com new Mais() em vários botões sem a guardar numa variável? -→ 1. A classe interna com nome -- [x] a classe interna com nome -- [ ] a classe anónima -- [ ] o lambda - -? Porque é que está static antes de class Mais? -→ 1. A classe interna com nome -- [ ] Para a classe ser mais rápida. -- [x] Porque main é static e senão não consegue criar um objeto de Mais. -- [ ] Porque todas as classes têm de ser static. - -? Liga. -→ As três comparadas -= class Mais implements ActionListener -> classe interna com nome -= new ActionListener() { … } -> classe anónima -= a seta depois de e -> lambda -::: diff --git a/web/lessons/pt/39-zwei-reiter.md b/web/lessons/pt/39-zwei-reiter.md deleted file mode 100644 index c8df5456..00000000 --- a/web/lessons/pt/39-zwei-reiter.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -titel: Dois separadores -bereich: Janelas com Swing -kurz: Construir uma janela com dois separadores, levar dados de um separador para o outro e guardar a lista num ficheiro de texto. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir uma janela com dois separadores -- acrescentar linhas a uma JTextArea com append -- explicar porque é que um ouvinte pode usar peças dos dois separadores -- guardar um ficheiro dentro de um ouvinte com try e catch -- construir uma lista de compras que conta quantas coisas tem -::: - -## O objetivo - -A lista de compras do Ludwig da lição 23 ganha uma janela. Tem dois **separadores**, como os do browser: - -- No separador **Entrada** escreves o que precisas e clicas em **Adicionar**. -- No separador **Lista** está tudo o que escreveste. Um botão **Guardar** escreve a lista num ficheiro de texto. - -Novos são os separadores (`JTabbedPane`), uma área de texto com várias linhas (`JTextArea`) e guardar a partir de um ouvinte. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Separadores com JTabbedPane - -Cada separador mostra a sua própria área. As áreas são JPanels, como o teclado do cadeado na lição 37: - -```java -JTabbedPane separadores = new JTabbedPane(); -JPanel entrada = new JPanel(new GridLayout(0, 1)); -JPanel lista = new JPanel(new BorderLayout()); - -separadores.addTab("Entrada", entrada); -separadores.addTab("Lista", lista); -janela.add(separadores); -``` - -| Chamada | Significado | -|---|---| -| `addTab("Entrada", entrada)` | um separador novo com a legenda "Entrada", mostra o JPanel `entrada` | -| `setSelectedIndex(1)` | muda para o segundo separador, conta-se a partir de 0 | -| `setTitleAt(1, "Lista (3)")` | muda a legenda do segundo separador | - -No início está aberto o separador que foi adicionado primeiro. - -## Uma área de texto com várias linhas - -Um `JTextField` só tem uma linha. Para a lista precisas de uma `JTextArea`: - -```java -JTextArea area = new JTextArea(); -area.setEditable(false); -area.append("Feno\n"); -``` - -- `append` acrescenta texto no fim. O `"\n"` começa depois uma nova linha, como ao dividir com `split("\n")` na lição 24. -- `setEditable(false)` impede que se escreva diretamente na lista. -- `getText()` devolve o texto inteiro com todas as linhas. - -::: quiz -? O que faz `area.append("Feno\n")`? -- [ ] Substitui o texto todo por “Feno”. -- [x] Acrescenta “Feno” no fim e começa depois uma linha nova. -- [ ] Escreve “Feno\n” tal e qual na lista. -> `append` acrescenta no fim, `"\n"` é a mudança de linha. -::: - -## De um separador para o outro - -O botão **Adicionar** está no separador Entrada, a área de texto no separador Lista. Mesmo assim, o ouvinte pode usar os dois. Todas as peças são variáveis em `main`, e o lambda vê-as todas, seja qual for o separador em que aparecem depois: - -```java -adicionar.addActionListener(e -> { - area.append(campo.getText() + "\n"); - info.setText(campo.getText() + " está na lista."); - campo.setText(""); -}); -``` - -::: ludwig -Uma peça só pode estar numa **única** área. Se juntares o mesmo botão primeiro a `entrada` e depois a `lista`, o Swing tira-o de `entrada`. Só aparece no separador Lista. -::: - -## Guardar dentro do ouvinte - -Para guardar usas o `PrintWriter` da lição 23. Dentro do ouvinte há um senão: a saída com `throws Exception` da lição 23 aqui não existe. O lambda é um método próprio, `actionPerformed`, e segundo o contrato não pode passar adiante uma `FileNotFoundException`. Por isso, guardar fica dentro de try e catch: - -```java -guardar.addActionListener(e -> { - try { - PrintWriter saida = new PrintWriter("listacompras.txt"); - saida.print(area.getText()); - saida.close(); - info.setText("Guardado em listacompras.txt"); - separadores.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Não foi possível guardar."); - } -}); -``` - -- A exceção chama-se aqui `ex`, porque o nome `e` já está ocupado pelo evento. -- Depois de guardar, a janela volta com `setSelectedIndex(0)` ao separador Entrada, onde está a mensagem. -- O VisuStruct mostra o ficheiro guardado por baixo da consola, assim que clicares em Guardar. - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class ListaCompras { - public static void main(String[] args) { - JFrame janela = new JFrame("Lista de compras"); - janela.setSize(300, 250); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane separadores = new JTabbedPane(); - - JPanel entrada = new JPanel(new GridLayout(0, 1)); - JTextField campo = new JTextField(); - JButton adicionar = new JButton("Adicionar"); - JLabel info = new JLabel("Ainda não há nada na lista."); - entrada.add(new JLabel("Do que precisas?")); - entrada.add(campo); - entrada.add(adicionar); - entrada.add(info); - - JPanel lista = new JPanel(new BorderLayout()); - JTextArea area = new JTextArea(); - area.setEditable(false); - JButton guardar = new JButton("Guardar"); - lista.add(area, BorderLayout.CENTER); - lista.add(guardar, BorderLayout.SOUTH); - - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); - - adicionar.addActionListener(e -> { - area.append(campo.getText() + "\n"); - info.setText(campo.getText() + " está na lista."); - campo.setText(""); - }); - - guardar.addActionListener(e -> { - try { - PrintWriter saida = new PrintWriter("listacompras.txt"); - saida.print(area.getText()); - saida.close(); - info.setText("Guardado em listacompras.txt"); - separadores.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Não foi possível guardar."); - } - }); - - janela.add(separadores); - janela.setVisible(true); - } -} -``` - -Escreve Feno, clica em Adicionar, depois Maçãs e outra vez Adicionar. Muda para o separador Lista e clica em Guardar. - -## Jogo: Onde vai parar? - -::: spiel frage -titel: Jogo: Onde vai parar? -intro: O Ludwig constrói janelas com separadores. Lê o código e prevê o que vês. Seis rondas. - -runde: - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); -frage: Que separador está aberto no início? -- [x] Entrada -- Lista -- nenhum -> No início está aberto o separador que foi adicionado primeiro. - -runde: - separadores.addTab("Lista", lista); - separadores.addTab("Entrada", entrada); -frage: Que separador está aberto no início? -- Entrada -- [x] Lista -- os dois -> Desta vez a Lista vem primeiro. A ordem dos `addTab` decide a ordem dos separadores. - -runde: - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); - separadores.addTab("Ajuda", ajuda); - separadores.setSelectedIndex(1); -frage: Que separador está aberto? -- Entrada -- [x] Lista -- Ajuda -> Conta-se a partir de 0: 0 é Entrada, 1 é Lista, 2 é Ajuda. - -runde: - adicionar.addActionListener(e -> { - area.append(campo.getText() + "\n"); - campo.setText(""); - }); - // Escreves Feno, clicas em Adicionar, escreves Maçãs, clicas em Adicionar. -frage: O que está depois no separador Lista? -- Maçãs -- FenoMaçãs -- [x] Feno\nMaçãs -> `append` acrescenta no fim, e cada `"\n"` começa uma nova linha. - -runde: - JButton botao = new JButton("OK"); - entrada.add(botao); - lista.add(botao); -frage: Onde aparece o botão OK? -- nos dois separadores -- só no separador Entrada -- [x] só no separador Lista -> Uma peça só pode estar numa área. No segundo `add`, o Swing tira-a de `entrada`. - -runde: - guardar.addActionListener(e -> { - area.setText(""); - separadores.setTitleAt(1, "Lista (vazia)"); - separadores.setSelectedIndex(0); - }); - // O botão guardar está no separador Lista. -frage: O que vês depois do clique? -- o separador Lista vazio -- [x] o separador Entrada, e o segundo separador chama-se "Lista (vazia)" -- o separador Entrada, e o segundo separador continua a chamar-se "Lista" -> O ouvinte esvazia a área, muda o nome do separador e muda para o separador 0. Tudo acontece com um clique. -::: - -## Tarefa: Lista de compras com separadores - -Constrói o programa e experimenta-o. - -Depois melhora-o: - -- Entradas vazias não vão para a lista. Verifica antes `campo.getText().isEmpty()` (lição 17). -- O separador Lista mostra no título quantas coisas estão na lista: "Lista (3)". Conta numa variável `static int` e usa `setTitleAt`. -- Extra: um terceiro separador **Ajuda** com um JLabel que explica como funciona o programa. - -**Terminaste quando …** - -- um item escrito aparece no separador da lista depois do clique, -- um campo vazio não acrescenta nada à lista, -- depois de três itens o título do separador mostra o número, p. ex. “Lista (3)”. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir uma janela com dois separadores. → Separadores com JTabbedPane -- Consigo acrescentar linhas a uma JTextArea com append. → Uma área de texto com várias linhas -- Consigo explicar porque é que um ouvinte pode usar peças dos dois separadores. → De um separador para o outro -- Consigo guardar um ficheiro dentro de um ouvinte com try e catch. → Guardar dentro do ouvinte -- Consigo construir uma lista de compras que conta quantas coisas tem. → Tarefa: Lista de compras com separadores -::: - -## Teste de conhecimentos - -::: quiz -? Que separador está aberto depois de separadores.setSelectedIndex(0)? -→ Separadores com JTabbedPane -- [x] o primeiro -- [ ] o segundo -- [ ] nenhum - -? Porque é que guardar dentro do ouvinte fica em try e catch? -→ Guardar dentro do ouvinte -- [ ] Porque o PrintWriter só funciona dentro de try. -- [x] Porque actionPerformed não pode passar adiante uma FileNotFoundException. -- [ ] Porque o main não tem throws. - -? Qual é a diferença entre JTextField e JTextArea? -→ Uma área de texto com várias linhas -- [ ] Não há nenhuma. -- [x] A JTextArea pode mostrar várias linhas. -- [ ] Num JTextField não se pode escrever. - -? Liga. -→ Separadores com JTabbedPane -= JTabbedPane -> uma área com separadores -= JPanel -> a área de um separador -= JTextArea -> uma área de texto com várias linhas -::: diff --git a/web/lessons/pt/40-model-view-controller.md b/web/lessons/pt/40-model-view-controller.md deleted file mode 100644 index a724bb5c..00000000 --- a/web/lessons/pt/40-model-view-controller.md +++ /dev/null @@ -1,430 +0,0 @@ ---- -titel: Model View Controller -bereich: Janelas com Swing -kurz: Dividir o contador de cliques em model, view e controller e perceber quem fala com quem. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dizer o que fazem o model, a view e o controller -- escrever um model sem Swing e testá-lo sem janela -- explicar o que constrói new Contador() e porque é que ali não há static -- nomear as quatro mensagens depois de um clique -- acrescentar uma segunda view sem mudar o model -- construir um contador com −1, Reset e limites no model -::: - -## O objetivo - -Os programas grandes com janelas têm quase sempre a mesma estrutura. Chama-se **Model View Controller**, ou **MVC**, em português modelo, vista e controlador: - -- O **model** guarda os dados e conhece as suas regras. -- A **view** só mostra os dados. -- O **controller** reage aos cliques e dá o recado. - -Aprendes MVC com o contador de cliques da lição 32. Ele é dividido em três partes, passo a passo. No fim mostra a mesma contagem duas vezes: como número e como barra. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Tudo numa classe - -Até agora o contador de cliques era assim, tudo está em `main` e numa variável da classe: - -```java -static int contagem = 0; - -public static void main(String[] args) { - // ... construir a janela ... - JLabel numero = new JLabel("0", SwingConstants.CENTER); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); -} -``` - -Funciona. Mas imagina que a contagem também deve aparecer como barra, mais tarde numa segunda janela e num ficheiro. Então cada ouvinte tem de se lembrar de todos os visores. Contar e mostrar ficam atados num nó, e com cada visor o nó fica maior. - -O MVC desata o nó: cada tarefa recebe a sua própria classe. - -## O model - -O model é o próprio contador, sem nada de Swing: - -```java -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} -``` - -- Desta vez não há `static` antes de `valor` e dos métodos. Assim pertencem a um **objeto**: `new Contador()` constrói um contador com o seu próprio `valor`, tal como `new JButton("+1")` constrói um botão com a sua própria legenda. -- `model.aumentar()` chama o método deste contador, tal como `botao.setText(…)`. -- Quem quer saber o valor pergunta com `getValor()`. Mudá-lo só é possível através de `aumentar()`. - -Como o model não precisa de janela, podes experimentá-lo sem janela nenhuma: - -```java -public class ContadorTeste { - public static void main(String[] args) { - Contador model = new Contador(); - model.aumentar(); - model.aumentar(); - System.out.println(model.getValor()); - } -} - -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} -``` - -A saída é 2. Num ficheiro podem estar várias classes. Só uma delas é `public`, e essa tem o `main`. - -## A view - -A view mostra o valor e mais nada. Não faz contas e não sabe de onde vem o número: - -```java -class VistaNumero { - JLabel numero = new JLabel("0", SwingConstants.CENTER); - - void atualizar(int valor) { - numero.setText("" + valor); - } -} -``` - -- A view recebe o valor com `atualizar(int valor)` e escreve-o no seu JLabel. -- A janela e o botão constróis em `main`, como sempre. Com `janela.add(view.numero)` o JLabel da view entra na janela. - -## O controller - -O controller é o ouvinte do botão. Liga o model à view: - -```java -Contador model = new Contador(); -VistaNumero view = new VistaNumero(); - -mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); -}); -``` - -- Primeiro muda o model, depois vai buscar o novo valor e entrega-o à view. -- O problema da lição 32 desapareceu: a variável `model` nunca muda, aponta sempre para o mesmo contador. Só o `valor` lá dentro muda. Por isso o lambda pode usar `model`, sem nenhum `static`. - -## Quem fala com quem? - -A cada clique correm quatro mensagens, sempre pela mesma ordem: - -::: grafik mvc-klick.svg -As setas acendem-se uma após a outra. O model conta na segunda seta, o visor só na quarta. -::: - -1. O botão comunica o clique ao controller (`actionPerformed`). -2. O controller diz ao model: `aumentar()`. -3. O controller pergunta ao model o novo valor: `getValor()`. -4. O controller entrega o valor à view: `atualizar(valor)`. - -O model não fala com ninguém. Espera até lhe perguntarem. - -::: ludwig -Lembra-te: o model não sabe nada de janelas. Em `Contador` não há nenhum `JLabel` nem nenhum `import javax.swing`. Se precisares de Swing ali, o código pertence à view. -::: - -::: quiz -? Com quem fala o model por iniciativa própria? -- [ ] Com a view -- [ ] Com o botão -- [x] Com ninguém -> O model espera até o controller lhe perguntar. -::: - -## A vantagem: uma segunda view - -Agora a contagem também deve aparecer como barra. Para isso junta-se uma segunda view. Uma `JProgressBar` é uma barra que se enche, aqui de 0 a 10: - -```java -class VistaBarra { - JProgressBar barra = new JProgressBar(0, 10); - - void atualizar(int valor) { - barra.setValue(valor); - } -} -``` - -O controller ganha mais uma linha. O model fica exatamente como estava: - -```java -mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); -}); -``` - -É exatamente esta a vantagem do MVC: podes acrescentar ou trocar visores sem mexer no model. - -## O programa completo - -```java -import javax.swing.*; -import java.awt.*; - -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} - -class VistaNumero { - JLabel numero = new JLabel("0", SwingConstants.CENTER); - - void atualizar(int valor) { - numero.setText("" + valor); - } -} - -class VistaBarra { - JProgressBar barra = new JProgressBar(0, 10); - - void atualizar(int valor) { - barra.setValue(valor); - } -} - -public class ContadorCliques { - public static void main(String[] args) { - Contador model = new Contador(); - VistaNumero view = new VistaNumero(); - VistaBarra barra = new VistaBarra(); - - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); - }); - - janela.add(view.numero); - janela.add(barra.barra); - janela.add(mais); - janela.setVisible(true); - } -} -``` - -Clica algumas vezes em +1 e vê o número e a barra a crescer juntos. - -::: angeberwissen -O MVC foi inventado em 1979 pelo norueguês Trygve Reenskaug, no centro de investigação Xerox PARC, na Califórnia. Já nessa altura havia lá computadores com janelas e rato. No mesmo ano, Steve Jobs visitou o laboratório e ficou tão entusiasmado que a Apple passou a construir computadores com janelas e rato. -::: - -## Jogo: Onde pertence o código? - -::: spiel frage -titel: Jogo: Onde pertence o código? -intro: O Ludwig atirou código de um programa MVC para um monte. Arruma cada peça. Oito rondas. -frage: Onde pertence este código? -antworten: Model | View | Controller - -runde: - int valor = 0; -richtig: Model -> A contagem são os dados. Os dados moram no model. - -runde: - numero.setText("" + valor); -richtig: View -> Mostrar algo no ecrã é a tarefa da view. - -runde: - mais.addActionListener(e -> model.aumentar()); -richtig: Controller -> Reagir a um clique e avisar o model: é isso que o controller faz. - -runde: - void aumentar() { - valor++; - } -richtig: Model -> O próprio model sabe como mudar os seus dados. - -runde: - barra.setValue(valor); -richtig: View -> Uma barra que se enche só mostra. Portanto: view. - -runde: - void aumentar() { - if (valor < 10) { - valor++; - } - } -richtig: Model -> "No máximo 10" é uma regra para os dados. As regras pertencem ao model, assim valem para todos os botões e todas as views. - -runde: - view.atualizar(model.getValor()); -richtig: Controller -> Ir buscar o valor ao model e entregá-lo à view: é o controller no meio. - -runde: - JLabel numero = new JLabel("0", SwingConstants.CENTER); -richtig: View -> Um JLabel é uma peça do ecrã. No model não há Swing. -::: - -## Jogo: Quem é o próximo? - -::: spiel reihenfolge -titel: Jogo: Quem é o próximo? -intro: Depois de um clique, o model, a view e o controller falam uns com os outros, mas só funciona por uma ordem. Põe os passos pela ordem certa. Cinco rondas. - -runde: -frage: Clicas em +1 no contador de cliques. O que acontece, um passo após o outro? -- Clicas em +1. -- O controller recebe `actionPerformed`. -- O controller chama `model.aumentar()`. -- O model aumenta o `valor`. -- O controller vai buscar `model.getValor()`. -- A view mostra o novo número. -> Clique, controller, mudar o model, ir buscar o valor, mostrar. São as quatro setas da imagem, com a contagem no meio. - -runde: - mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); - }); -frage: O que acontece depois de um clique em +1, um passo após o outro? -- O clique em +1 -- `model.aumentar()` -- O número mostra o novo valor. -- A barra cresce. -> O ouvinte trata as suas linhas de cima para baixo: primeiro o model, depois o número, depois a barra. - -runde: -frage: Põe o controller pela ordem certa, para que a view mostre o novo número. -- `mais.addActionListener(e -> {` -- `model.aumentar();` -- `view.atualizar(model.getValor());` -- `});` -> Primeiro mudar, depois mostrar. Ao contrário, a view ainda mostra o valor antigo. - -runde: - reset.addActionListener(e -> { - model.reiniciar(); - view.atualizar(model.getValor()); - }); -frage: O contador está em 5. Clicas em Reset. O que acontece, um passo após o outro? -- Clicas em Reset. -- O model põe o `valor` a 0. -- `model.getValor()` devolve 0. -- A view mostra 0. -> Também o Reset passa pelo controller até ao model. A view só sabe do novo valor no fim. - -runde: -frage: Um teste sem janela deve escrever 1. Por que ordem estão as linhas no main? -- `Contador model = new Contador();` -- `model.aumentar();` -- `System.out.println(model.getValor());` -> Primeiro construir o objeto, depois trabalhar com ele e no fim ver o resultado. O model pode ser testado sem janela nenhuma. -::: - -## Tarefa: Um contador com limites - -Constrói o programa completo e experimenta-o. - -Depois melhora-o: - -- Um botão **−1** conta para baixo. O model recebe para isso um método `diminuir()`, o controller um segundo ouvinte. -- Um botão **Reset** põe o contador a 0 com `reiniciar()`. -- O próprio model trata dos limites: nunca abaixo de 0 e nunca acima de 10, porque aí a barra está cheia. -- Extra: uma terceira view `VistaConsola`, cujo `atualizar` escreve "Novo valor: 3" com `System.out.println`. Uma view não tem de ser uma janela. - -**Terminaste quando …** - -- **+1**, **−1** e **Reset** contam bem, -- o contador nunca fica abaixo de 0 nem acima de 10, por mais que cliques, -- o número e a barra mostram sempre o mesmo valor, -- a classe do modelo não tem `JButton`, `JLabel` nem outro Swing. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dizer o que fazem o model, a view e o controller. → O objetivo -- Consigo escrever um model sem Swing e testá-lo sem janela. → O model -- Consigo explicar o que constrói new Contador() e porque é que ali não há static. → O model -- Consigo nomear as quatro mensagens depois de um clique. → Quem fala com quem? -- Consigo acrescentar uma segunda view sem mudar o model. → A vantagem: uma segunda view -- Consigo construir um contador com −1, Reset e limites no model. → Tarefa: Um contador com limites -::: - -## Teste de conhecimentos - -::: quiz -? Onde fica a regra "O contador nunca passa de 10"? -→ O model -- [x] no model -- [ ] na view -- [ ] no controller -> As regras sobre os dados pertencem ao model. Assim valem seja qual for o botão. - -? O que faz a view? -→ O objetivo -- [ ] Calcula o novo valor. -- [x] Mostra o valor que recebe. -- [ ] Ouve os cliques. - -? Porque é que o lambda pode usar model, se model é uma variável do main? -→ Quem fala com quem? -- [ ] Porque Contador é public. -- [x] Porque model aponta sempre para o mesmo contador; só o valor lá dentro muda. -- [ ] Porque os lambdas podem mudar qualquer variável. - -? O que tens de mudar no model quando se junta uma segunda view? -→ A vantagem: uma segunda view -- [x] nada -- [ ] um segundo método aumentar -- [ ] um segundo JLabel - -? Liga. -→ O objetivo -= Contador com valor e aumentar() -> model -= VistaNumero com atualizar(int valor) -> view -= o ouvinte do botão -> controller -::: diff --git a/web/lessons/pt/aufgaben/alter-unterprogramm.md b/web/lessons/pt/aufgaben/alter-unterprogramm.md deleted file mode 100644 index b2c6ba46..00000000 --- a/web/lessons/pt/aufgaben/alter-unterprogramm.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -titel: Idade num subprograma -nach: 18 -stufe: 2 ---- - -A verificação da idade de “Maior de idade?” passa agora a ser um subprograma próprio. O programa principal só o chama. - -::: übung aufgabe-alter-unterprogramm -titel: Idade num subprograma -auftrag: Constrói dois diagramas. O programa principal mostra **Bem-vindo**, depois chama `verificarIdade()` e no fim mostra **Adeus**. - O subprograma `verificarIdade` fica num separador próprio: pede a idade e mostra **maior de idade** ou **menor de idade**. -test: 20 -> Bem-vindo | maior de idade | Adeus -test: 18 -> Bem-vindo | maior de idade | Adeus -test: 12 -> Bem-vindo | menor de idade | Adeus -braucht: aufruf -tipp: O programa principal tem três blocos. O subprograma faz o trabalho a sério. -tipp: Abre o exercício no editor e constrói lá o programa principal com a chamada `verificarIdade()`. Depois abre um novo separador com **+** e dá-lhe o título `verificarIdade`. Constrói nele a entrada e a decisão. O Verificar encontra o subprograma pelo título do separador. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/ballwurf.md b/web/lessons/pt/aufgaben/ballwurf.md deleted file mode 100644 index fcc9f56b..00000000 --- a/web/lessons/pt/aufgaben/ballwurf.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -titel: Avaliar o lançamento da bola -nach: 25 -stufe: 2 ---- - -No dia do desporto, todos escreveram numa tabela a distância a que lançaram a bola: - -::: datei lancamento.csv -Nome;Distancia -Ludwig;28 -Zora;42 -Raposa;35 -Lebre;21 -::: - -::: übung aufgabe-ballwurf -titel: Avaliar o lançamento da bola -code: java -auftrag: Lê o ficheiro e salta a linha de cabeçalho. Mostra quantos participaram (**Participantes: 4**) e a que distância lançaram em média (**Média: 31.5 m**). - Guarda o lançamento mais longo no ficheiro **vencedor.txt**, por exemplo **Vencedor: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Participantes: 4 | Média: 31.5 m -datei: vencedor.txt -> Vencedor: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: Precisas de quatro variáveis antes do ciclo: a contagem, a soma como `double`, a melhor distância e o melhor nome. -tipp: No ciclo: `String[] partes = linha.split(";");` e `int distancia = Integer.parseInt(partes[1]);`. Se `distancia > melhorDistancia`, guarda a distância e o nome. Para guardar: `PrintWriter saida = new PrintWriter("vencedor.txt");`, `saida.println(…)` e `saida.close();`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/bestenliste.md b/web/lessons/pt/aufgaben/bestenliste.md deleted file mode 100644 index 775c7679..00000000 --- a/web/lessons/pt/aufgaben/bestenliste.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -titel: Classificação -nach: 19 -stufe: 2 ---- - -Cinco cabras fizeram a corrida de obstáculos. Agora a classificação vai para a porta do estábulo: mais pontos em cima, menos pontos em baixo. - -::: übung aufgabe-bestenliste -titel: Classificação -auftrag: Lê 5 pontuações para um array. Ordena o array com o bubble sort por ordem **decrescente**, ou seja, o maior número primeiro. Depois mostra a lista com o lugar, por exemplo **1. 9**, na linha seguinte **2. 9** e assim por diante. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: O bubble sort da lição 19 ordena por ordem crescente. Para decrescente basta inverter a comparação dos vizinhos. -tipp: Troca-se quando `pontos[i] < pontos[i + 1]`, com uma variável auxiliar. Para a saída, calcula primeiro `int lugar = i + 1` e depois mostra `lugar + ". " + pontos[i]`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-bestenliste-java -titel: Classificação em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Lê cada número primeiro para uma variável e depois põe-no no array: `int numero = scanner.nextInt();` e `pontos[i] = numero;`. -tipp: A troca também precisa de uma variável auxiliar em Java: `int aux = pontos[i]; pontos[i] = pontos[i + 1]; pontos[i + 1] = aux;`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/binaerzahl.md b/web/lessons/pt/aufgaben/binaerzahl.md deleted file mode 100644 index ef81854c..00000000 --- a/web/lessons/pt/aufgaben/binaerzahl.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -titel: Número binário -nach: 11 -stufe: 3 ---- - -Os computadores fazem contas só com dois algarismos: 0 e 1. É assim que se converte um número para o **sistema binário**: - -1. Divide o número por 2 e guarda o resto (0 ou 1). -2. Continua com o resultado sem o resto. -3. Repete isto até o número ser 0. - -Os restos lidos **de baixo para cima** dão o número binário. Para 6, os restos são 0, 1, 1, portanto o número binário é 110. - -::: übung aufgabe-binaer -titel: Número binário -auftrag: Pede um número inteiro maior do que 0 e mostra-o como número binário. Para 6 a saída é **110**, para 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Vai dividindo o número por 2 e guarda cada resto. Os restos lidos ao contrário dão o número binário. -tipp: Junta os algarismos num texto: `String binario = ""`. No ciclo calculas `int resto = numero % 2` e acrescentas o resto à **frente**: `binario = resto + binario`. Depois, `numero = numero / 2`. Como numero é um número inteiro, o resto desaparece. -lösung: aufgabe-binaer-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-binaer-java -titel: Número binário em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Começa com `String binario = "";`. O ciclo corre com `while (numero > 0)`. -tipp: No corpo: `int resto = numero % 2;`, depois `binario = resto + binario;` e `numero = numero / 2;`. Como `binario` é uma String, o `+` põe o resto à frente. -lösung: aufgabe-binaer-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/caesar.md b/web/lessons/pt/aufgaben/caesar.md deleted file mode 100644 index 406e78e6..00000000 --- a/web/lessons/pt/aufgaben/caesar.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -titel: Cifra de César -nach: 17 -stufe: 3 ---- - -Há mais de 2000 anos, o general romano Júlio César enviava mensagens secretas às suas tropas. O truque era simples: substituía cada letra pela letra que está **três posições à frente** no alfabeto. O A passa a D, o B passa a E e assim por diante. A mensagem legível chama-se **texto simples**, a cifrada **texto cifrado**. O número de posições que se avança é a **chave**, muitas vezes chamada apenas `k`. - -::: grafik caesar-verschiebung.svg -Cifrar com a chave 3: cada letra de cima passa a ser a letra de baixo. No fim recomeça-se do início, por isso X, Y e Z passam a A, B e C. -::: - -Assim, `HALLO` passa a `KDOOR`. Quem conhece a chave recua simplesmente três posições e consegue voltar a ler a mensagem. - -### O disco de cifra - -Para não ter de contar sempre, construiu-se um **disco de cifra**: dois discos com o alfabeto, um pequeno sobre um grande, presos no meio para poderem rodar. Por fora está o texto simples, por dentro o texto cifrado. Rodando o disco interior três letras, por baixo de cada letra exterior aparece a sua letra cifrada. Discos assim existem desde o século XV. Na Guerra Civil Americana (1861–1865), os soldados levavam pequenos discos de latão. - -::: grafik caesar-scheibe.svg -Um disco de cifra inspirado nos discos históricos de latão. A janela azul mostra: com a chave 3, por baixo do A está o D. -::: - -::: angeberwissen -O próprio César nem sequer tinha um disco! Sabemo-lo pelo historiador romano Suetónio: nas suas cartas, César avançava cada letra três posições e contava isso simplesmente de cabeça. O disco só foi inventado por Leon Battista Alberti por volta de 1467, cerca de 1500 anos mais tarde. Aliás, eu também conto de cabeça. Não tenho dedos, só cascos. -::: - -Experimenta tu. Roda o disco interior, escreve um texto e vê como surge o texto cifrado. Com **Decifrar** faz-se o caminho inverso. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -Um disco verdadeiro é muito fácil de fazer: dois círculos de cartão, um um pouco mais pequeno, cada um com 26 campos iguais para o alfabeto, e um colchete de latão no meio. Está pronta a tua ferramenta de agente secreto! -::: - -::: quiz -? Em que letra se transforma o Y com a chave 3? -- [ ] A -- [x] B -- [ ] C -> A seguir ao Y vem o Z, depois recomeça-se com A e B. Três posições à frente é, portanto, o B. - -? Um texto cifrado foi cifrado com a chave 5. Como o decifras? -- [ ] Avançar mais 5 posições -- [x] Recuar 5 posições -- [ ] Ler as letras de trás para a frente -> Decifrar é o contrário de cifrar. Recuar 5 posições é, aliás, o mesmo que avançar 21, porque 5 + 21 = 26. -::: - -### O programa: há mais do que um caminho - -Como ensinas o computador a avançar uma letra `k` posições? Não existe *a* única solução certa. Vamos ver dois caminhos. Ambos percorrem o texto carácter a carácter com um ciclo for, tal como na lição sobre cadeias de caracteres. A diferença está na forma como avançam uma única letra. - -Nos dois caminhos ajuda o operador de resto `%`. Ele garante que depois do Z se continua no A: as posições 0 a 25 ficam como estão, 26 passa a 0, 27 passa a 1 e assim por diante. - -### Caminho 1: o alfabeto num array - -Colocas o alfabeto num array `char[] alfabeto = {'A', 'B', …, 'Z'}`. Para cada letra da palavra procuras no array a sua posição `pos`, ou seja, o seu índice. A letra cifrada está então no mesmo array na posição `(pos + k) % 26`. - -Um exemplo com a chave 3: o X está na posição 23. Então `(23 + 3) % 26 = 0`, e na posição 0 está o A. - -::: übung aufgabe-caesar-array -titel: César com array -auftrag: O diagrama inicial já contém o array `alfabeto`. Pede uma palavra em maiúsculas e a chave `k`. Depois mostra a palavra cifrada. Para HALLO e 3, isso dá `KDOOR`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Cria um texto vazio `String segredo = ""`. Percorre a palavra carácter a carácter com um ciclo for. Em cada passagem acrescentas uma letra cifrada ao fim de `segredo`. -tipp: Para encontrar a posição de uma letra precisas de um segundo ciclo, interior, sobre o array: se `alfabeto[j] == letra`, guardas `pos = j`. -tipp: Depois do ciclo interior acrescentas `alfabeto[(pos + k) % 26]`: `segredo = segredo + alfabeto[(pos + k) % 26]`. Depois do ciclo exterior mostras `segredo`. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Caminho 2: calcular com a tabela ASCII - -Para o computador, cada carácter é na verdade um número. Que número pertence a que carácter está na **tabela ASCII**. Aí o A tem o código 65, o B o código 66 e assim por diante até ao Z com 90. Em Java transformas um carácter no seu código com `(int)` e um código de volta num carácter com `(char)`: - -``` -int codigo = (int) 'A' -char letra = (char) 68 -``` - -Depois disso, `codigo` vale 65 e `letra` vale `'D'`. Uma conversão destas de um tipo de dados para outro chama-se **conversão de tipo** (em inglês *cast*). - -Agora já podes fazer contas com a letra. Para que o salto do Z de volta ao A funcione, subtrais primeiro 65. Assim o A fica na posição 0 e o Z na posição 25, tal como no caminho 1. Avanças com `% 26` e no fim voltas a somar os 65: - -::: grafik caesar-ascii.svg -Em cima, as letras maiúsculas com os seus códigos ASCII. Em baixo, o cálculo para o X com a chave 3: o resultado é A. -::: - -::: übung aufgabe-caesar-ascii -titel: César com ASCII -code: java -auftrag: Escreve um programa em Java que lê uma palavra em maiúsculas e a chave `k` e mostra a palavra cifrada. Desta vez sem array: calcula com `(int)` e `(char)`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Percorre a palavra com um ciclo for. O código do carácter na posição `i` obténs com `int codigo = (int) palavra.charAt(i);`. -tipp: Calcula `codigo = (codigo - 65 + k) % 26 + 65;` e acrescenta o carácter: `segredo = segredo + (char) codigo;`. Antes do ciclo escreves `String segredo = "";`, depois do ciclo `System.out.println(segredo);`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Qual é o melhor caminho? - -**Caminho 1: array.** A ideia é procurar a posição no array. -- Vantagem: fácil de perceber, e também funciona com alfabetos próprios, p. ex. com Ç. -- Desvantagem: é preciso procurar cada letra. - -**Caminho 2: ASCII.** A ideia é calcular com o código do carácter. -- Vantagem: curto e sem procura, por isso mais rápido. -- Desvantagem: é preciso conhecer os códigos ASCII. - -Os dois programas dão o mesmo resultado. Na programação é muitas vezes assim: há várias soluções certas, e escolhes a que melhor se adapta à tua tarefa. - -::: übung aufgabe-caesar-entschluesseln -titel: Decifrar César -code: java -auftrag: Agora inverte o jogo. Lê um texto cifrado em maiúsculas e a chave `k` e mostra o texto simples. Para KDOOR e 3, isso dá `HALLO`. Escolhes tu o caminho. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Decifrar significa recuar `k` posições em vez de avançar. Assim, o `+ k` passa a `- k`. -tipp: Cuidado com o A: `0 - 3` dá -3, e em Java o `%` não transforma isso num número positivo. Por isso soma ainda 26: `codigo = (codigo - 65 - k + 26) % 26 + 65;` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Infelizmente, a cifra de César não é segura. Há apenas 25 chaves, e é possível experimentá-las todas em poucos minutos. Com um truque é ainda mais rápido: em português, as letras mais frequentes são o A e o E. Se no texto cifrado o H aparecer muitas vezes, a chave foi provavelmente 3. Na internet existe ainda o ROT13, que é a cifra de César com a chave 13. Aplicar ROT13 duas vezes devolve o texto simples, porque 13 + 13 = 26. -::: diff --git a/web/lessons/pt/aufgaben/collatz.md b/web/lessons/pt/aufgaben/collatz.md deleted file mode 100644 index 36359bb0..00000000 --- a/web/lessons/pt/aufgaben/collatz.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -titel: Sequência de Collatz -nach: 11 -stufe: 2 ---- - -A sequência de Collatz é um enigma famoso da matemática. Começas com um número e aplicas sempre a mesma regra: - -- Se o número for **par**, divides por 2. -- Se for **ímpar**, multiplicas por 3 e somas 1. - -Até agora, para todos os números iniciais que alguém experimentou, a sequência acaba por chegar a 1. Se isto é verdade para todos os números, ninguém sabe até hoje. - -::: übung aufgabe-collatz -titel: Sequência de Collatz -auftrag: Pede um número inicial. Aplica a regra até o número ser 1 e mostra o novo número depois de cada passo. Vai contando os passos e mostra no fim **Passos:** e o número de passos. - Com o número inicial 6, a sequência é 3, 10, 5, 16, 8, 4, 2, 1. São 8 passos. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 7 -test: 1 -> Passos: 0 -braucht: whileSchleife, verzweigung -tipp: Não sabes quantos passos vão ser. Por isso um ciclo while serve melhor do que um ciclo for. -tipp: O ciclo while é executado enquanto `numero != 1`. Verificas se um número é par com `numero % 2 == 0`. A saída e `passos++` vêm depois da decisão, mas ainda dentro do ciclo. -lösung: aufgabe-collatz-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-collatz-java -titel: Sequência de Collatz em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 7 -test: 1 -> Passos: 0 -braucht: whileSchleife, verzweigung -tipp: Um ciclo `while` corre enquanto `numero != 1`. Lá dentro, um `if` com `numero % 2 == 0` decide. -tipp: No corpo: dividir por 2 ou `numero = numero * 3 + 1;`, depois `System.out.println(numero);` e `passos++;`. Depois do ciclo vem `"Passos: " + passos`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -Esta tarefa pode tornar-te rico! O matemático Paul Erdős ofereceu 500 dólares por uma demonstração. Em 2021, a empresa japonesa Bakuage juntou bastante mais: 120 milhões de ienes, na altura cerca de um milhão de dólares, por uma demonstração ou um contraexemplo. Ainda ninguém foi buscar o dinheiro. Já agora, o enigma foi inventado pelo matemático alemão Lothar Collatz em 1937. Vou já começar a fazer contas; com tanto dinheiro compra-se muito feno. -::: diff --git a/web/lessons/pt/aufgaben/einheitenrechner.md b/web/lessons/pt/aufgaben/einheitenrechner.md deleted file mode 100644 index a1d0c800..00000000 --- a/web/lessons/pt/aufgaben/einheitenrechner.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -titel: Conversor de unidades -nach: 35 -stufe: 2 ---- - -Constrói uma janela que converte comprimentos. Em cima escreves um comprimento em metros, por baixo há três botões: - -- **em cm** mostra o comprimento em centímetros, 1.5 passa a "150.0 cm", -- **em mm** mostra-o em milímetros, 1.5 passa a "1500.0 mm", -- **em km** mostra-o em quilómetros, 1500 passa a "1.5 km". - -O resultado aparece num JLabel por baixo dos botões. Se no campo não estiver um número, aparece "Escreve um número", e o programa não vai abaixo. - -Podes começar assim: - -```java -import javax.swing.*; -import java.awt.*; - -public class Unidades { - public static void main(String[] args) { - JFrame janela = new JFrame("Conversor de unidades"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JTextField metros = new JTextField(); - JButton emCm = new JButton("em cm"); - JLabel resultado = new JLabel("?", SwingConstants.CENTER); - - emCm.addActionListener(e -> { - double m = Double.parseDouble(metros.getText()); - resultado.setText((m * 100) + " cm"); - }); - - janela.add(metros); - janela.add(emCm); - janela.add(resultado); - janela.setVisible(true); - } -} -``` - -**Terminaste quando …** - -- 1.5 mostra "150.0 cm" depois de **em cm** e "1500.0 mm" depois de **em mm**, -- 1500 mostra "1.5 km" depois de **em km**, -- "abc" no campo mostra "Escreve um número" e a janela continua aberta. - -Extra: um quarto botão **em pés** converte para a unidade inglesa. Um pé são 0.3048 m. - -::: ludwig -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). Para "abc" no campo, cada ouvinte precisa de um try/catch com `NumberFormatException`, tal como na calculadora. -::: diff --git a/web/lessons/pt/aufgaben/fibonacci.md b/web/lessons/pt/aufgaben/fibonacci.md deleted file mode 100644 index 2e7df238..00000000 --- a/web/lessons/pt/aufgaben/fibonacci.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -titel: Números de Fibonacci -nach: 13 -stufe: 2 ---- - -Nos números de Fibonacci, cada número é a soma dos dois anteriores. A sequência começa com 1 e 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -Encontras estes números surpreendentemente vezes na natureza, por exemplo na forma como as sementes do girassol estão dispostas. - -::: ludwig -Lembras-te da **troca** da lição 5? Aí precisavas de uma variável auxiliar, porque `a = b` substitui o valor antigo de `a`. Aqui é igual: antes de `numero2` receber o número novo, o seu valor antigo tem de estar guardado em `numero1`. Atenção à **ordem**: primeiro `numero1 = numero2`, depois `numero2 = numero3`. Vais precisar outra vez de avançar e trocar valores mais tarde, por exemplo ao ordenar na lição 19. -::: - -::: angeberwissen -Em 1202, Leonardo de Pisa, conhecido como Fibonacci, pôs no seu livro de cálculo um problema sobre coelhos: a partir do segundo mês, cada casal de coelhos tem um novo casal todos os meses. Quantos casais há ao fim de um ano? A resposta é esta sequência. Infelizmente, o livro não tinha cabras. -::: - -::: übung aufgabe-fibonacci -titel: Números de Fibonacci -auftrag: Pergunta quantos números de Fibonacci devem ser mostrados. Depois mostra exatamente esses, cada um na sua linha. Podes partir do princípio de que é introduzido pelo menos 2. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Cada número novo é a soma dos dois anteriores. Por isso tens sempre de guardar os dois últimos números. -tipp: Guarda os dois últimos números em `numero1` e `numero2` e mostra os dois antes do ciclo. O ciclo começa em `int i = 2`. Lá dentro calculas `int numero3 = numero1 + numero2`, mostras e depois avanças: `numero1 = numero2` e `numero2 = numero3`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-fibonacci-java -titel: Números de Fibonacci em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Os dois primeiros números mostras antes do ciclo. O ciclo `for` começa então em `int i = 2`. -tipp: No corpo: `int numero3 = numero1 + numero2;`, mostrar, depois `numero1 = numero2;` e `numero2 = numero3;`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Porque é que no ciclo `numero1 = numero2` tem de vir antes de `numero2 = numero3`? -- [ ] Porque senão o Java não permite as atribuições -- [x] Porque senão o valor antigo de numero2 é substituído antes de numero1 o receber -- [ ] A ordem não importa -> Ao contrário, numero1 e numero2 ficariam depois com o mesmo valor. Tal como na troca: primeiro guardar, depois substituir. -::: diff --git a/web/lessons/pt/aufgaben/gerade-ungerade.md b/web/lessons/pt/aufgaben/gerade-ungerade.md deleted file mode 100644 index 7496f1d4..00000000 --- a/web/lessons/pt/aufgaben/gerade-ungerade.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Par ou ímpar -nach: 8 -stufe: 1 ---- - -Um número é par se não sobrar resto quando o divides por 2. O resto calcula-se com `%`: `8 % 2` dá 0, `7 % 2` dá 1. - -::: übung aufgabe-gerade -titel: Par ou ímpar -auftrag: Pede um número inteiro. Mostra se é par ou ímpar. Para 4 a saída é **4 é par**, para 7 **7 é ímpar**. -test: 4 -> 4 é par -test: 7 -> 7 é ímpar -test: 0 -> 0 é par -test: 15 -> 15 é ímpar -braucht: verzweigung -tipp: Uma decisão escolhe entre par e ímpar. Par quer dizer: divisível por 2 sem resto. -tipp: A condição é `numero % 2 == 0`. Na saída juntas o número e o texto com `+`, p. ex. `numero + " é par"`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-gerade-java -titel: Par ou ímpar em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 4 -> 4 é par -test: 7 -> 7 é ímpar -test: 0 -> 0 é par -test: 15 -> 15 é ímpar -braucht: verzweigung -tipp: Lês um número com `int numero = scanner.nextInt();`. Antes disso perguntas com `System.out.println(…)`. -tipp: `if (numero % 2 == 0) { … } else { … }`. A saída é `System.out.println(numero + " é par");`. -lösung: aufgabe-gerade-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/mehrwertsteuer.md b/web/lessons/pt/aufgaben/mehrwertsteuer.md deleted file mode 100644 index 01a0266e..00000000 --- a/web/lessons/pt/aufgaben/mehrwertsteuer.md +++ /dev/null @@ -1,41 +0,0 @@ ---- -titel: IVA -nach: 6 -stufe: 1 ---- - -Em Portugal continental, a quase tudo o que compras é somado 23 % de **IVA** (imposto sobre o valor acrescentado). A loja calcula com o preço sem IVA e soma o imposto por cima. Isso dá o preço com IVA que pagas na caixa. - -Um exemplo: compras um telemóvel dobrável que se abre e fica um tablet. Na etiqueta da loja estão 1000 euros sem IVA. Por cima vêm 23 %, ou seja, 230 euros. Na caixa pagas 1230 euros. A loja não pode ficar com os 230 euros: entrega-os às Finanças. - -::: grafik aufgabe-mehrwertsteuer.svg -Preço sem IVA mais IVA dá o preço com IVA. O IVA vai para as Finanças. -::: - -::: angeberwissen -Mééé, claro que a própria loja comprou o telemóvel: ao fabricante, por 800 euros mais 184 euros de IVA. Estes 184 euros chamam-se **IVA dedutível**, e a loja pode descontá-los dos 230 euros. Por isso só entrega 46 euros às Finanças. No fim, só uma pessoa paga o imposto todo: o cliente na caixa. Ou seja, tu. Eu, já agora, não pago imposto nenhum. Só como erva. -::: - -A taxa do imposto nunca muda enquanto fazes as contas. Por isso crias-a como **constante**: uma variável cujo valor não muda depois de definido. Escreves `const` à frente, por exemplo `const double IVA = 0.23`. Os nomes das constantes escrevem-se em maiúsculas para as reconheceres logo. No código Java isto passa a `final double IVA = 0.23;`. - -Se a taxa alguma vez mudar, só tens de alterar esta linha. - -::: übung aufgabe-mehrwertsteuer -titel: IVA -auftrag: Cria a constante `IVA` com o valor 0.23. Depois pede o preço sem IVA. Mostra primeiro o imposto e depois o preço com IVA, de cada vez só o número. - Para um preço sem IVA de 100, as saídas são **23** e **123**. -test: 100 -> 23 | 123 -test: 50 -> 11.5 | 61.5 -test: 0 -> 0 | 0 -tipp: O imposto é 23 por cento do preço sem IVA. O preço com IVA é o que pagas no fim. -tipp: O imposto é `precoSemIva * IVA`. O preço com IVA obténs somando o imposto ao preço sem IVA. Os dois valores são números decimais, portanto `double`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Porque crias a taxa do imposto como constante? -- [ ] Porque as constantes são calculadas mais depressa -- [x] Porque o seu valor não deve mudar no programa -- [ ] Porque os números decimais só podem ser guardados como constantes -> Uma constante mantém o seu valor. Se a taxa alguma vez mudar, só alteras a linha com `const`. -::: diff --git a/web/lessons/pt/aufgaben/notendurchschnitt.md b/web/lessons/pt/aufgaben/notendurchschnitt.md deleted file mode 100644 index ebb59cf5..00000000 --- a/web/lessons/pt/aufgaben/notendurchschnitt.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Média das notas -nach: 13 -stufe: 2 ---- - -Como te correu ultimamente? A média é a soma de todas as notas a dividir pelo número de notas. - -::: übung aufgabe-durchschnitt -titel: Média das notas -auftrag: Pergunta primeiro quantas notas vão ser introduzidas. Depois lê exatamente essas notas e mostra a média, só o número. - Para as notas 4 e 5, a média é **4.5**. -test: 2, 4, 5 -> 4.5 -test: 3, 3, 4, 5 -> 4 -test: 4, 3, 3, 4, 5 -> 3.75 -braucht: forSchleife -tipp: Precisas da soma de todas as notas e de quantas são. A média é a soma a dividir pela quantidade. -tipp: O ciclo for vai de `int i = 0` enquanto `i < quantidade`. Lá dentro lês uma nota e somas à soma. Guardas a média como número decimal: `double media = soma * 1.0 / quantidade`. O `* 1.0` garante que também o Java não faz uma divisão inteira. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-durchschnitt-java -titel: Média das notas em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 2, 4, 5 -> 4.5 -test: 3, 3, 4, 5 -> 4 -test: 4, 3, 3, 4, 5 -> 3.75 -braucht: forSchleife -tipp: `for (int i = 0; i < quantidade; i++)` lê exatamente as notas que foram indicadas antes. -tipp: Divide com `double media = soma * 1.0 / quantidade;`. Sem `* 1.0`, o Java divide dois `int` como inteiros, e 1.5 passa a 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/pin-pruefen.md b/web/lessons/pt/aufgaben/pin-pruefen.md deleted file mode 100644 index 3828e50b..00000000 --- a/web/lessons/pt/aufgaben/pin-pruefen.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -titel: Verificar o PIN -nach: 12 -stufe: 2 ---- - -Numa caixa multibanco tens três tentativas para o teu PIN. Se escreveres um PIN errado três vezes, o cartão fica bloqueado. - -::: angeberwissen -John Shepherd-Barron, que instalou o primeiro multibanco em Londres em 1967, queria PINs de seis algarismos. Mas a mulher dele disse que só conseguia decorar quatro. É por isso que o teu PIN tem hoje quatro algarismos. -::: - -::: übung aufgabe-pin -titel: Verificar o PIN -auftrag: O PIN certo é 1234. Pede o PIN até estar certo ou até se terem gastado três tentativas. Depois mostra **PIN correto** ou **Cartão bloqueado**. -test: 1234 -> PIN correto -test: 1111, 1234 -> PIN correto -test: 1, 2, 1234 -> PIN correto -test: 1, 2, 3 -> Cartão bloqueado -braucht: doUntilSchleife, verzweigung -tipp: O ciclo termina em dois casos: quando o PIN está certo ou quando se gastaram três tentativas. Conta as tentativas. -tipp: O PIN é pedido pelo menos uma vez, por isso serve um ciclo do-while. Antes dele, cria `int pin = 0` e `int tentativas = 0`. O ciclo repete-se enquanto `pin != 1234 && tentativas < 3`. Depois do ciclo, uma decisão decide o que é mostrado. -lösung: aufgabe-pin-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-pin-java -titel: Verificar o PIN em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 1234 -> PIN correto -test: 1111, 1234 -> PIN correto -test: 1, 2, 1234 -> PIN correto -test: 1, 2, 3 -> Cartão bloqueado -braucht: doUntilSchleife, verzweigung -tipp: `do { … } while (…);` pergunta pelo menos uma vez. Depois do `while (…)` há um ponto e vírgula. -tipp: Cria antes `int pin = 0;` e `int tentativas = 0;`. No ciclo: `int entrada = scanner.nextInt();`, `pin = entrada;` e `tentativas++;`. A condição é `pin != 1234 && tentativas < 3`. -lösung: aufgabe-pin-java-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/schiffe-versenken.md b/web/lessons/pt/aufgaben/schiffe-versenken.md deleted file mode 100644 index 149f903c..00000000 --- a/web/lessons/pt/aufgaben/schiffe-versenken.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Batalha naval -nach: 16 -stufe: 2 ---- - -No jogo da batalha naval, o mar é uma grelha. As colunas chamam-se A a E, as filas 1 a 5. No diagrama inicial, o mar é o array 2D `mar`: `true` quer dizer que há parte de um navio nesse quadrado. - -::: grafik schiffe-versenken.svg -Cinzento quer dizer `true`: aqui há um navio. Os pequenos números cinzentos são os índices no array; começam em 0. No jogo, pelo contrário, os quadrados chamam-se A a E e 1 a 5. -::: - -::: übung aufgabe-schiffe -titel: Batalha naval -auftrag: Mostra todos os quadrados que têm um navio, cada quadrado na sua linha, p. ex. **B1**. Percorre o mar fila a fila com dois ciclos encaixados. - Para o diagrama inicial aparecem **B1**, **C1**, **D1**, **E3**, **E4** e **A5**. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Percorre o mar com dois ciclos encaixados, tal como no cinema. Só mostras os quadrados que têm `true`. -tipp: O ciclo exterior conta `fila`, o interior `coluna`, os dois a partir de 0. Se houver um navio em `mar[fila][coluna]`, mostras a letra de `colunas[coluna]` e o número da fila: `output: colunas[coluna] + (fila + 1)`. Precisas do `+ 1` porque o jogo começa a contar em 1. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-schiffe-java -titel: Batalha naval em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Dois ciclos `for` um dentro do outro: fora a fila, dentro a coluna, ambos até `.length`. -tipp: `if (mar[fila][coluna]) { System.out.println(colunas[coluna] + (fila + 1)); }`. Os parênteses à volta de `fila + 1` fazem o Java calcular primeiro. -lösung: aufgabe-schiffe-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/speisekarte.md b/web/lessons/pt/aufgaben/speisekarte.md deleted file mode 100644 index 9618a126..00000000 --- a/web/lessons/pt/aufgaben/speisekarte.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Ementa -nach: 10 -stufe: 1 ---- - -Na cantina da escola pedes por número. O programa diz-te que prato corresponde ao número. - -::: übung aufgabe-speisekarte -titel: Ementa -auftrag: Pede um número. Para 1 mostra **Salada grega**, para 2 **Bacalhau à Brás** e para 3 **Wrap de falafel**. Para qualquer outro número mostra **Escolhe um número válido**. -test: 1 -> Salada grega -test: 2 -> Bacalhau à Brás -test: 3 -> Wrap de falafel -test: 7 -> Escolhe um número válido -braucht: fallauswahl -tipp: Para vários números fixos serve uma seleção. O que acontece com todos os outros números? -tipp: Lê o número com `input: int prato "Que prato?"`. A seleção verifica `prato`, e os casos chamam-se `1`, `2` e `3`. Todos os outros números vão parar ao caso `default`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-speisekarte-java -titel: Ementa em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 1 -> Salada grega -test: 2 -> Bacalhau à Brás -test: 3 -> Wrap de falafel -test: 7 -> Escolhe um número válido -braucht: fallauswahl -tipp: Em Java isto é uma seleção com `switch (prato) { … }`. -tipp: Cada caso é assim: `case 1 -> { System.out.println("…"); }`. Para todos os outros números há `default -> { … }`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/summe-bilden.md b/web/lessons/pt/aufgaben/summe-bilden.md deleted file mode 100644 index 69f65def..00000000 --- a/web/lessons/pt/aufgaben/summe-bilden.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Somar -nach: 2 -stufe: 1 ---- - -Define dois números, soma-os e mostra o resultado. - -::: übung aufgabe-summe -titel: Somar -auftrag: Dá a `numero1` o valor 4 e a `numero2` o valor 3. Calcula a soma numa terceira variável e mostra só a soma. -test: - -> 7 -tipp: Precisas de três variáveis: duas para os números e uma para o resultado. -tipp: Defines uma variável com uma instrução como `int numero1 = 4`. Na saída escreves o nome da variável sem aspas. -lösung: aufgabe-summe-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/tafeldienst.md b/web/lessons/pt/aufgaben/tafeldienst.md deleted file mode 100644 index 28505391..00000000 --- a/web/lessons/pt/aufgaben/tafeldienst.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -titel: Sortear quem limpa o quadro -nach: 15 -stufe: 2 ---- - -Quem é que limpa o quadro esta semana? Cinco nomes vão para um array, depois é sorteado um número. - -::: übung aufgabe-tafeldienst -titel: Sortear quem limpa o quadro -auftrag: Lê cinco nomes para um array de Strings. Depois pede um número sorteado de 1 a 5 e mostra **Limpa o quadro:** e o nome correspondente. - O número 1 corresponde ao primeiro nome, o 5 ao último. -test: Ada, Rui, Inês, Duarte, Eva, 3 -> Limpa o quadro: Inês -test: Ada, Rui, Inês, Duarte, Eva, 1 -> Limpa o quadro: Ada -test: Lara, Mário, Nuno, Olga, Pedro, 5 -> Limpa o quadro: Pedro -braucht: forSchleife -tipp: Primeiro, um ciclo lê os cinco nomes. Depois já não precisas de ciclo, só de olhar para uma posição. -tipp: Crias o array com `String[] nomes = new String[5]`. Atenção: o número sorteado começa em 1, o índice em 0. Por isso o nome está em `nomes[sorteado - 1]`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-tafeldienst-java -titel: Sortear quem limpa o quadro em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: Ada, Rui, Inês, Duarte, Eva, 3 -> Limpa o quadro: Inês -test: Ada, Rui, Inês, Duarte, Eva, 1 -> Limpa o quadro: Ada -test: Lara, Mário, Nuno, Olga, Pedro, 5 -> Limpa o quadro: Pedro -braucht: forSchleife -tipp: Um array para cinco nomes crias com `String[] nomes = new String[5];`. -tipp: No ciclo: `nomes[i] = scanner.next();`. O número 1 corresponde à posição 0, por isso mostras `nomes[sorteado - 1]`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> Com `int sorteado = (int) (Math.random() * 5) + 1` também podes deixar o acaso sortear o número. Mas para a verificação automática precisamos de uma entrada fixa. diff --git a/web/lessons/pt/aufgaben/taschenrechner.md b/web/lessons/pt/aufgaben/taschenrechner.md deleted file mode 100644 index efd1854d..00000000 --- a/web/lessons/pt/aufgaben/taschenrechner.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Calculadora -nach: 10 -stufe: 2 ---- - -Uma pequena calculadora: escreves dois números e um operador, e aparece o resultado. - -::: übung aufgabe-rechner -titel: Calculadora -auftrag: Pede dois números inteiros e depois um operador como texto: `+`, `-` ou `*`. Mostra o resultado. Para qualquer outro sinal mostra **Operador desconhecido**. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Operador desconhecido -braucht: fallauswahl -tipp: Lê os três valores. Depois, uma seleção sobre o operador decide o que é calculado. -tipp: Lês o operador com `input: String operador "Operador"`. A seleção verifica `operador`, os casos chamam-se `"+"`, `"-"` e `"*"`, e o último caso é `default`. Em cada caso, calcula primeiro numa variável e depois mostra-a, senão o `+` só junta os números. -lösung: aufgabe-rechner-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-rechner-java -titel: Calculadora em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Operador desconhecido -braucht: fallauswahl -tipp: O operador é um texto. Lês com `String operador = scanner.next();`. -tipp: `switch (operador) { case "+" -> { … } … default -> { … } }`. Com textos, os casos ficam entre aspas. -lösung: aufgabe-rechner-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/verleih-menue.md b/web/lessons/pt/aufgaben/verleih-menue.md deleted file mode 100644 index b712ef28..00000000 --- a/web/lessons/pt/aufgaben/verleih-menue.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Menu de aluguer -nach: 18 -stufe: 2 ---- - -Na máquina de aluguer podes reservar vários veículos, um a seguir ao outro. O menu volta sempre até escolheres **0** para sair. - -::: übung aufgabe-verleih -titel: Menu de aluguer -auftrag: Pede sempre uma escolha: 1 = bicicleta elétrica, 2 = trotinete elétrica, 0 = sair. Para 1 mostra **Bicicleta reservada**, para 2 **Trotinete reservada** e para qualquer outro número **Escolha inválida**. - Para 0, o programa mostra **Adeus** e sai do ciclo com uma Interrupção. -test: 0 -> Adeus -test: 1, 2, 0 -> Bicicleta reservada | Trotinete reservada | Adeus -test: 2, 5, 1, 0 -> Trotinete reservada | Escolha inválida | Bicicleta reservada | Adeus -braucht: endlosschleife, aussprung -tipp: O menu é executado uma e outra vez até alguém escolher 0. Para isso serve bem um ciclo infinito com uma Interrupção. -tipp: Usa um ciclo infinito. Lá dentro vem primeiro a entrada `input: int escolha "1 = bicicleta elétrica, 2 = trotinete elétrica, 0 = sair"`, depois uma seleção sobre `escolha` com os casos `0`, `1`, `2` e `default`. O caso `0` tem a saída e, por baixo, a Interrupção. -lösung: aufgabe-verleih-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-verleih-java -titel: Menu de aluguer em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 0 -> Adeus -test: 1, 2, 0 -> Bicicleta reservada | Trotinete reservada | Adeus -test: 2, 5, 1, 0 -> Trotinete reservada | Escolha inválida | Bicicleta reservada | Adeus -braucht: endlosschleife, aussprung -tipp: `while (true) { … }` corre sem fim até aparecer um `break;`. -tipp: Em Java usa aqui `if`/`else if` em vez de `switch`: um `break;` dentro de um `switch` só sai do `switch`, não do ciclo. `if (escolha == 0) { System.out.println("Adeus"); break; } else if (escolha == 1) { … }`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/vokabeltrainer.md b/web/lessons/pt/aufgaben/vokabeltrainer.md deleted file mode 100644 index 122eac67..00000000 --- a/web/lessons/pt/aufgaben/vokabeltrainer.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -titel: Treino de vocabulário -nach: 27 -stufe: 3 ---- - -O Ludwig está a aprender inglês. As palavras dele estão num ficheiro CSV, à esquerda em português, à direita em inglês: - -::: datei vocabulario.csv -Português;Inglês -cabra;goat -feno;hay -prado;meadow -::: - -Como só o ficheiro conhece as palavras, mais tarde podes simplesmente acrescentar linhas novas sem alterar o programa. - -::: angeberwissen -Foi Sebastian Leitner quem, em 1972, tornou conhecida a caixa de fichas com várias divisões: as fichas que sabes passam para a divisão seguinte e aparecem menos vezes, as erradas voltam para a primeira. Muitas apps de vocabulário ainda funcionam assim. -::: - -::: übung aufgabe-vokabeln -titel: Treino de vocabulário -code: java -auftrag: Pergunta cada palavra do ficheiro: mostra **Como se diz cabra em inglês?** e lê a resposta com `scanner.next()`. Se estiver certa, mostra **Certo!**, senão por exemplo **Errado, é goat.** - No fim aparece quantas estavam certas, por exemplo **2 de 3 certas**. -start: aufgabe-vokabeln-start.visustruct -test: goat, hay, meadow -> Como se diz cabra em inglês? | Certo! | Como se diz feno em inglês? | Certo! | Como se diz prado em inglês? | Certo! | 3 de 3 certas -test: cow, hay, meadow -> Como se diz cabra em inglês? | Errado, é goat. | Como se diz feno em inglês? | Certo! | Como se diz prado em inglês? | Certo! | 2 de 3 certas -test: goat, feno, prado -> Como se diz cabra em inglês? | Certo! | Como se diz feno em inglês? | Errado, é hay. | Como se diz prado em inglês? | Errado, é meadow. | 1 de 3 certas -braucht: whileSchleife, verzweigung -tipp: Salta a linha de cabeçalho. No ciclo, divide cada linha com `split(";")`: `partes[0]` é a palavra em português, `partes[1]` a palavra em inglês. -tipp: Os textos comparam-se com `resposta.equals(partes[1])`, não com `==`. Conta com `int certas = 0;` e `int contagem = 0;` antes do ciclo. -lösung: aufgabe-vokabeln-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/volljaehrig.md b/web/lessons/pt/aufgaben/volljaehrig.md deleted file mode 100644 index 00457c9d..00000000 --- a/web/lessons/pt/aufgaben/volljaehrig.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -titel: Maior de idade? -nach: 8 -stufe: 1 ---- - -No cinema, na carta de condução ou para votar: muitas vezes interessa saber se alguém já tem 18 anos. - -::: übung aufgabe-volljaehrig -titel: Maior de idade? -auftrag: Pede a idade. Se a pessoa tiver 18 anos ou mais, mostra **maior de idade**, senão **menor de idade**. -test: 18 -> maior de idade -test: 35 -> maior de idade -test: 17 -> menor de idade -test: 6 -> menor de idade -braucht: verzweigung -tipp: Há duas saídas possíveis, por isso precisas de uma decisão. -tipp: Lê a idade com `input: int idade "Quantos anos tens?"`. A condição da decisão é `idade >= 18`. Garante que 18 já conta como maior de idade. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-volljaehrig-java -titel: Maior de idade? em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 18 -> maior de idade -test: 35 -> maior de idade -test: 17 -> menor de idade -test: 6 -> menor de idade -braucht: verzweigung -tipp: Lê a idade com `int idade = scanner.nextInt();`. -tipp: `if (idade >= 18) { … } else { … }`, com um `System.out.println(…)` em cada ramo. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: diff --git a/web/lessons/pt/aufgaben/wuerfel-mvc.md b/web/lessons/pt/aufgaben/wuerfel-mvc.md deleted file mode 100644 index 61e8a89d..00000000 --- a/web/lessons/pt/aufgaben/wuerfel-mvc.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -titel: Lançar o dado com MVC -nach: 40 -stufe: 3 ---- - -Constrói um jogo de dados com Model View Controller. Um botão **Lançar** lança o dado, e duas views mostram o resultado. - -O model já está pronto. Lança o dado e conta quantas vezes foi lançado e quantos seis saíram: - -```java -class Dado { - int pontos = 1; - int lancamentos = 0; - int seis = 0; - - void lancar() { - pontos = (int) (Math.random() * 6) + 1; - lancamentos++; - if (pontos == 6) { - seis++; - } - } -} -``` - -A tua tarefa: - -1. Testa primeiro o model sem janela: lança dez vezes no `main` e depois mostra `lancamentos` e `seis`. -2. Escreve a view `VistaPontos`. Mostra o número que saiu num JLabel grande, por exemplo "4". -3. Escreve a view `VistaEstatistica`. Mostra por exemplo "Seis: 2 em 10 lançamentos". -4. O controller é o ouvinte do botão: manda o model lançar e passa os valores novos às duas views. - -**Terminaste quando …** - -- na consola, depois de dez lançamentos, `lancamentos` é 10 e `seis` está entre 0 e 10, -- cada clique em **Lançar** mostra um número novo de 1 a 6 na `VistaPontos`, -- a `VistaEstatistica` conta mais um lançamento depois de cada clique e conta bem os seis, -- no model não há nenhum `JLabel` e nas views não há nenhum `Math.random`. - -Extra: um botão **Novo jogo** põe tudo no início. O método para isso pertence ao model. - -::: ludwig -Lembra-te da regra da lição 40: no model não há um único `JLabel`. Se mudares o tamanho da letra ao lançar, isso pertence à view. -::: diff --git a/web/lessons/pt/aufgaben/zahl-verdoppeln.md b/web/lessons/pt/aufgaben/zahl-verdoppeln.md deleted file mode 100644 index 401ee7ad..00000000 --- a/web/lessons/pt/aufgaben/zahl-verdoppeln.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -titel: Duplicar um número -nach: 2 -stufe: 1 ---- - -Uma variável pode mudar de valor. Aqui um número é duplicado. - -::: übung aufgabe-verdoppeln -titel: Duplicar um número -auftrag: Dá à variável `numero` o valor 4. Duplica-a numa segunda instrução, sem criar uma variável nova. Depois mostra **Número duplicado: 8**, em que o 8 vem da variável. -test: - -> Número duplicado: 8 -tipp: Duplicar quer dizer: vezes 2. Guardas o resultado outra vez na mesma variável. -tipp: `numero = numero * 2` calcula com o valor antigo e volta a guardar o resultado em `numero`. Juntas texto e uma variável com `+`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: diff --git a/web/lessons/pt/begriffe.md b/web/lessons/pt/begriffe.md deleted file mode 100644 index 58d6b4ac..00000000 --- a/web/lessons/pt/begriffe.md +++ /dev/null @@ -1,258 +0,0 @@ -Termos técnicos da área de aprendizagem. Quando um termo aparece num passo, fica marcado uma vez e mostra a sua -explicação ao passar o rato ou ao tocar. Se estiver a negrito nesse passo, é explicado ali mesmo e não é marcado. -`auch:` indica outras formas que também são reconhecidas. Maiúsculas e minúsculas contam. - -### Algoritmo -auch: algoritmo, algoritmos, Algoritmos -Instruções exatas, passo a passo, para resolver uma tarefa. Cada passo é claro. - -### Estrutograma -auch: estrutograma, estrutogramas, Estrutogramas, diagrama de Nassi-Shneiderman, diagramas de Nassi-Shneiderman, diagrama estruturado -Um algoritmo desenhado como imagem feita de blocos. Lê-se de cima para baixo. Também se chama diagrama de Nassi-Shneiderman. - -### Fluxograma -auch: fluxograma, fluxogramas, Fluxogramas -Um algoritmo desenhado com símbolos ligados por setas (DIN 66001 / ISO 5807). Um losango é uma decisão. - -### Pseudocódigo -auch: pseudocódigo -Um algoritmo escrito como texto em linguagem corrente com palavras fixas como SE e ENQUANTO. A indentação mostra o que pertence ao quê. - -### Instrução -auch: instrução, instruções, Instruções -Um único passo no estrutograma, por exemplo um cálculo, uma entrada ou uma saída. - -### Sequência -auch: sequência, sequências, Sequências -Passos que são executados uns a seguir aos outros, de cima para baixo, cada um exatamente uma vez. - -### Decisão -auch: decisão, decisões, Decisões, instrução condicional, instruções condicionais, estrutura de decisão -Um bloco que usa uma condição para decidir qual de dois ramos é executado. Também se chama instrução condicional. - -### Ramo -auch: ramo, ramos -Um dos dois caminhos de uma decisão: à esquerda para verdadeiro (true), à direita para falso (false). - -### Ciclo -auch: ciclo, ciclos, Ciclos, repetição, estrutura de repetição -Um bloco que repete passos enquanto uma condição se verificar. - -### Corpo do ciclo -auch: corpo do ciclo -O interior de um ciclo, ou seja, os passos que são repetidos. - -### Iteração -auch: iteração, iterações -Executar uma vez o interior de um ciclo. - -### Condição -auch: condição, condições, Condições -Uma pergunta que só pode ter como resposta sim (true) ou não (false), por exemplo idade >= 18. - -### Marcador -auch: marcador, marcadores -Um bloco vazio com o sinal ø. Se arrastares um elemento para cima dele, o elemento substitui-o. - -### Inspetor de objetos -A área por baixo do diagrama. Aqui editas o texto e as definições do bloco em que acabaste de clicar. - -### Simulação -auch: simulação -Executa o teu estrutograma no editor, se quiseres passo a passo. Assim vês o que acontece. - -### Variável -auch: variável, variáveis, Variáveis -Uma caixa com etiqueta para um valor. Tem um nome, um tipo de dados e um valor. - -### camelCase -auch: CamelCase -Forma de escrever nomes com várias palavras: escritas juntas, cada palavra seguinte começa com maiúscula, p. ex. `precoComIva`. - -### case-sensitive -O Java distingue maiúsculas de minúsculas: `numero`, `Numero` e `NUMERO` são três nomes diferentes. `system` em vez de `System` é um erro. - -### Constante -auch: constante, constantes, Constantes -Uma variável cujo valor não muda depois de definido. Escreve-se `const` à frente, p. ex. `const double IVA = 0.23`. - -### Tipo de dados -auch: tipo de dados, tipos de dados, Tipos de dados -Diz que tipo de valor cabe numa variável: int (número inteiro), double (número decimal), String (texto), boolean (verdadeiro ou falso) ou char (um único caractere). - -### Atribuição -auch: atribuição, atribuições, Atribuições -O sinal =. Calcula o lado direito e guarda o resultado na variável à esquerda. - -### Variável auxiliar -auch: variável auxiliar, variáveis auxiliares -Uma variável extra que guarda um valor durante pouco tempo, por exemplo ao trocar valores. - -### Modelo EPS -auch: entrada-processamento-saída -Entrada, processamento, saída: primeiro pedir os valores, depois calcular, depois mostrar o resultado. - -### Análise do problema -auch: análise do problema -Lês uma tarefa com atenção e descobres o que é dado, o que procuras e como chegar de um ao outro. - -### Operador de comparação -auch: operador de comparação, operadores de comparação, Operadores de comparação -Um sinal que compara dois valores, por exemplo == ou <. O resultado é true ou false. - -### Seleção -auch: seleção, seleção múltipla, escolha múltipla -Um bloco que executa exatamente um de vários casos, consoante o valor de uma variável. Também se chama seleção múltipla. - -### Ciclo pré-teste -auch: ciclo pré-teste, ciclos pré-teste, controlado no início -O ciclo verifica a sua condição antes de cada iteração. Se a condição for falsa logo no início, o ciclo não é executado nem uma vez. - -### Ciclo pós-teste -auch: ciclo pós-teste, ciclos pós-teste, controlado no fim -O ciclo só verifica a sua condição depois de cada iteração. Por isso é sempre executado pelo menos uma vez. - -### Variável contadora -auch: variável contadora, variáveis contadoras, contador -Uma variável que vai contando num ciclo, normalmente i. - -### Ciclo infinito -auch: ciclo infinito, ciclos infinitos, ciclo sem fim -Um ciclo cuja condição nunca se torna falsa. Nunca para sozinho. - -### Array -auch: array, arrays, Arrays, vetor, vetores -Uma fila de posições com um nome comum e o mesmo tipo de dados. - -### Índice -auch: índice, índices -O número de uma posição num array. Começa-se a contar em 0. - -### char -O tipo de dados para um único caractere. Fica entre aspas simples, p. ex. 'C'. Já "C" com aspas duplas é uma String. - -### Código de caractere -auch: código de caractere, códigos de caractere, código ASCII, tabela ASCII -O número que está por trás de um caractere. Na tabela ASCII, o A tem o código 65, o B 66 e assim por diante. Em Java, (int) transforma um caractere no seu código e (char) transforma um código no caractere. - -### String -auch: strings, Strings, cadeia de caracteres, cadeias de caracteres -Um texto, ou seja, uma fila de caracteres. Em Java o tipo de dados chama-se String. Os caracteres contam-se a partir de 0. - -### Array 2D -auch: arrays 2D, array bidimensional, arrays bidimensionais, matriz, matrizes -Um array com linhas e colunas, como uma tabela. Uma posição precisa de dois índices, p. ex. `lugares[1][2]`. - -### Subprograma -auch: subprograma, subprogramas, Subprogramas, método, métodos, Métodos, procedimento, procedimentos, função, funções, Funções -Um estrutograma próprio para uma parte da tarefa. É executado quando é chamado. Em Java chama-se método; consoante o tipo, também procedimento ou função. - -### Parâmetro -auch: parâmetro, parâmetros, Parâmetros -Uma variável na linha de cabeçalho de um subprograma. Quando é chamado, recebe o valor que está entre parênteses. - -### Código esparguete -auch: código esparguete -Um programa que salta de um lado para o outro de forma tão confusa que mal se consegue seguir o que acontece. Os estrutogramas evitam isso. - -### Passagem por valor -auch: passagem por valor -Um subprograma recebe uma cópia do valor. Se alterar o seu parâmetro, a variável de quem chamou fica igual. - -### Passagem por referência -auch: passagem por referência -Um subprograma recebe uma referência para os mesmos dados, p. ex. para um array. Se alterar uma posição, quem chamou também vê a alteração. - -### Valor de retorno -auch: valor de retorno, valores de retorno, Valores de retorno -O resultado que um subprograma devolve à chamada com return. - -### Interrupção -Um bloco que sai imediatamente do ciclo em que está. Depois o programa continua por baixo do ciclo. - -### Erro de sintaxe -auch: erro de sintaxe, erros de sintaxe, Erros de sintaxe -Um erro na forma como o código está escrito, por exemplo um `;` em falta. O programa nem sequer arranca. Em inglês: syntax error. - -### Erro de execução -auch: erro de execução, erros de execução, Erros de execução -Um erro que só acontece enquanto o programa corre, por exemplo dividir por 0. O programa para nesse ponto. Em inglês: runtime error. - -### Erro de lógica -auch: erro de lógica, erros de lógica, Erros de lógica -O programa corre sem nenhuma mensagem, mas dá um resultado errado. Encontra-se com o teste de secretária. Em inglês: logic error. - -### Teste de secretária -auch: teste de secretária, testes de secretária, Testes de secretária, teste de mesa -Executas um estrutograma no papel e anotas os valores das variáveis depois de cada passo. - -### Pesquisa linear -auch: pesquisa linear -Uma forma de pesquisar que compara todos os valores, um a seguir ao outro, com o valor procurado. Também funciona com valores desordenados. - -### Pesquisa binária -auch: pesquisa binária -Uma forma de pesquisar em valores ordenados. Olha para o meio e continua a pesquisar só na metade certa. - -### Bubble sort -auch: bubble sort, Bubblesort, ordenação por bolha -Uma forma simples de ordenar. Compara sempre dois vizinhos e troca-os se estiverem na ordem errada. - -### Evento -auch: evento, eventos, Eventos -Algo que uma pessoa faz num programa com janelas, por exemplo clicar num botão. O programa reage com um ouvinte (ActionListener). - -### Interface -auch: Interfaces -Uma espécie de contrato em Java: diz que métodos uma classe tem de ter. `ActionListener` é uma interface com o método `actionPerformed`. - -### Exceção -auch: exceção, exceções, Exceções, Exception, Exceptions -Um erro que só acontece durante a execução, por exemplo um índice fora do array. Se nenhum `catch` a apanhar, o programa termina. - -### Atributo -auch: atributo, atributos, Atributos -Uma variável que está diretamente na classe e não dentro de um método. Com `static` pertence à própria classe e vive tanto tempo como o programa, por exemplo um contador de cliques que vários ouvintes alteram. - -### Scanner -Um bloco pronto do Java que lê entradas do teclado, por exemplo um número com `scanner.nextInt()`. Precisa de `import java.util.Scanner;` mesmo no início. - -### Comentário -auch: Comentários, comentário, comentários -Uma nota no código para pessoas. O Java salta-a ao executar. `//` vale até ao fim da linha, `/* … */` pode ocupar várias linhas. - -### import -Uma linha mesmo no início do código Java que vai buscar um bloco pronto a um pacote, por exemplo `import java.util.Scanner;`. Sem ela, o Java não conhece o nome. - -### código-fonte -auch: Código-fonte -O texto do programa que as pessoas escrevem e leem, por exemplo num ficheiro `Tabuada.java`. - -### compilador -auch: Compilador, compiladores -Um programa que traduz código-fonte. Em Java chama-se `javac` e transforma um ficheiro `.java` em bytecode num ficheiro `.class`. - -### bytecode -auch: Bytecode -A forma traduzida de um programa Java num ficheiro `.class`. Quem o lê é a JVM, não as pessoas. - -### JVM -auch: Java Virtual Machine, máquina virtual -A Java Virtual Machine executa bytecode. É um programa que funciona como um pequeno computador feito de software no Windows, no macOS ou no Linux. Por isso o Java é independente da plataforma. - -### JRE -Java Runtime Environment: a JVM e blocos prontos. Com ele podes executar programas Java, mas não compilá-los. - -### JDK -Java Development Kit: o JRE e ainda o compilador `javac` e outras ferramentas. É preciso para programar. - -### independente da plataforma -Um programa corre sem alterações em sistemas operativos diferentes. Em Java é a JVM que o permite: “Write once, run anywhere”. - -### código máquina -auch: Código máquina -Instruções em forma de números que o processador executa diretamente. Cada tipo de processador tem o seu próprio código máquina. - -### compilador JIT -auch: JIT -Uma parte da JVM que, durante a execução (“just in time”), traduz o bytecode muito usado em código máquina. Assim o Java fica mais rápido. diff --git a/web/lessons/pt/nachschlagen.md b/web/lessons/pt/nachschlagen.md deleted file mode 100644 index ea6ef9c4..00000000 --- a/web/lessons/pt/nachschlagen.md +++ /dev/null @@ -1,169 +0,0 @@ -## Elementos básicos - -### Instrução - -Um único passo. Serve para criar variáveis ou para calcular. - -``` -int numero = 5 -double preco = 2.5 -String nome = "Ada" -numero = numero + 1 -``` - -### Tipos de dados - -O tipo de dados vem antes do nome da variável. - -| Tipo de dados | Significado | Exemplo | -|---|---|---| -| `int` | número inteiro | `int quantidade = 3` | -| `double` | número decimal (com ponto) | `double preco = 2.5` | -| `String` | texto entre aspas duplas | `String nome = "Ada"` | -| `boolean` | valor lógico | `boolean pronto = false` | -| `char` | um único caractere entre aspas simples | `char nota = 'C'` | - -`'C'` é um `char`, `"C"` é uma String. Um caractere de um texto obténs com `charAt`: `char primeiro = nome.charAt(0)`. Cada caractere tem um código de caractere: `(int) 'A'` é 65, `(char) 66` é `'B'`. - -### Entrada - -Pede um valor enquanto o estrutograma é executado e guarda-o numa variável. O texto entre aspas é a pergunta. - -``` -input: int idade "Quantos anos tens?" -input: double preco "Introduz um preço" -input: String nome "Como te chamas?" -``` - -### Saída - -Mostra um texto ou um valor. Os textos ficam entre aspas; juntas texto e variáveis com `+`. - -``` -output: "Olá" -output: soma -output: "Tens " + idade + " anos." -``` - -> Numa saída, `+` junta sempre as partes: com 3 e 4, `output: a + b` mostra 34. Por isso calcula primeiro numa variável, p. ex. `int soma = a + b`, e depois mostra `soma`. - -## Decisões - -### Decisão - -Verifica uma condição. Se for verdadeira, é executado o ramo da esquerda (true), senão o da direita (false). - -``` -idade >= 18 -numero % 2 == 0 -nota < 1 || nota > 5 -``` - -### Seleção - -Escolhe um de vários casos, consoante o valor de uma variável. Se nenhum caso servir, aplica-se `default`. Acrescentas casos à direita com **Adicionar caso**. - -## Ciclos - -### Ciclo while - -Repete o seu interior **enquanto** a condição for verdadeira. A condição é verificada antes de cada iteração. - -``` -numero != 1 -i < 10 -``` - -### Ciclo do-while - -Executa o seu interior **pelo menos uma vez** e só depois verifica a condição. - -### Ciclo for - -Conta uma variável desde um valor inicial até um limite. Os três campos são Início, Condição e Passo. - -``` -int i = 0 -i < 5 -i++ -``` - -### Ciclo infinito - -Repete o seu interior sem condição. Uma **Interrupção** lá dentro permite sair dele. - -### Interrupção - -Sai do ciclo que a rodeia (`break`). - -## Subprogramas - -### Chamada - -Representa um subprograma, p. ex. `calcularSoma()`. Com parâmetros e valor de retorno, o subprograma tem uma linha de cabeçalho como título: - -``` -int quadrado(int x) linha de cabeçalho do subprograma -return x * x devolve o resultado -int q = quadrado(5) chamada, q fica com 25 -mostrarSoma(3, 4) chamada de um subprograma void -``` - -## Cálculos e comparações - -### Operadores e comparações - -- `+ - * /` as quatro operações básicas -- `%` resto de uma divisão (`7 % 2` dá 1) -- `==` igual, `!=` diferente -- `<` `<=` `>` `>=` menor, menor ou igual, maior, maior ou igual -- `&&` e, `||` ou - -## Arrays - -### Arrays - -Vários valores com um só nome. Começa-se a contar em 0. - -``` -int[] numeros = {3, 7, 1} -output: numeros[0] -int[] vazio = new int[5] -``` - -Um **array 2D** é uma tabela com linhas e colunas: - -``` -boolean[][] lugares = new boolean[3][5] -lugares[1][4] = true -output: lugares.length + " filas, " + lugares[0].length + " lugares" -``` - -## Java - -### Do código ao programa - -O compilador `javac` traduz `Ficheiro.java` em bytecode (`Ficheiro.class`). A JVM traduz o bytecode em código máquina ao executá-lo, da mesma forma no Windows, no macOS e no Linux. Já o C++ passa diretamente a código máquina e tem de ser compilado de novo para cada sistema. - -| | contém | para | -|---|---|---| -| JRE | JVM e bibliotecas | executar | -| JDK | JRE, compilador `javac`, ferramentas | programar | - -### Comentários - -Notas no código. O Java salta-as, o estrutograma fica igual. - -```java -/* - * Tarefa: somar os números de 1 a 10 - * (comentário de várias linhas) - */ -// Processamento (comentário de uma linha) -int soma = 0; -for (int i = 1; i <= 10; i++) { - soma = soma + i; // comentário depois do código -} // fim do for -``` - -Serve para: a tarefa, dividir o código em entrada, processamento e saída, uma explicação, o fim de um bloco depois de `}`, a saída esperada e notas para a próxima vez. diff --git a/web/lessons/welten/ausgang-1.txt b/web/lessons/welten/ausgang-1.txt deleted file mode 100644 index d7e86cea..00000000 --- a/web/lessons/welten/ausgang-1.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Labyrinth mit Ausgängen (nach Kara): jede Wandreihe hat genau eine Lücke, immer weiter rechts. Ein Zeichen = 30 cm. -raster: 3 -.......Z -#####.## -........ -##.##### ->....... diff --git a/web/lessons/welten/ausgang-2.txt b/web/lessons/welten/ausgang-2.txt deleted file mode 100644 index 81674d6a..00000000 --- a/web/lessons/welten/ausgang-2.txt +++ /dev/null @@ -1,9 +0,0 @@ -// Labyrinth mit Ausgängen, drei Reihen. Ein Zeichen = 30 cm. -raster: 3 -........Z -######.## -......... -####.#### -......... -.######## ->........ diff --git a/web/lessons/welten/ecke.txt b/web/lessons/welten/ecke.txt deleted file mode 100644 index 3ee64e8b..00000000 --- a/web/lessons/welten/ecke.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Um die Ecke (Sequenz im Kurs, Lektion 5): ein Zeichen = 30 cm. -raster: 3 -Z.... -###.. -..... -..### -^.... diff --git a/web/lessons/welten/farbe-1.txt b/web/lessons/welten/farbe-1.txt deleted file mode 100644 index 55a28f9c..00000000 --- a/web/lessons/welten/farbe-1.txt +++ /dev/null @@ -1,6 +0,0 @@ -// Fahre bis Rot: kurzer Weg -..... -..r.. -..... -..... -..^.. diff --git a/web/lessons/welten/farbe-2.txt b/web/lessons/welten/farbe-2.txt deleted file mode 100644 index 0139ed72..00000000 --- a/web/lessons/welten/farbe-2.txt +++ /dev/null @@ -1,9 +0,0 @@ -// Fahre bis Rot: erst kommt ein grünes Feld -..... -..r.. -..... -..... -..g.. -..... -..... -..^.. diff --git a/web/lessons/welten/farbe-3.txt b/web/lessons/welten/farbe-3.txt deleted file mode 100644 index c5bdee8f..00000000 --- a/web/lessons/welten/farbe-3.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Bis Blau, bei Grün piepen -..... -..b.. -..... -..g.. -..... -..^.. diff --git a/web/lessons/welten/farbe-4.txt b/web/lessons/welten/farbe-4.txt deleted file mode 100644 index fc805b95..00000000 --- a/web/lessons/welten/farbe-4.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Bis Blau, ohne grünes Feld -..... -..b.. -..... -..... -..... -..^.. diff --git a/web/lessons/welten/hindernis-1.txt b/web/lessons/welten/hindernis-1.txt deleted file mode 100644 index f9f4b093..00000000 --- a/web/lessons/welten/hindernis-1.txt +++ /dev/null @@ -1,11 +0,0 @@ -// Hindernis ausweichen: ein Klotz im Weg, das rote Feld liegt dahinter. -......... -.....r... -.....o... -.....o... -......... -..#...... -......... -......... -......... -..^...... diff --git a/web/lessons/welten/hindernis-2.txt b/web/lessons/welten/hindernis-2.txt deleted file mode 100644 index 5c7bbbb5..00000000 --- a/web/lessons/welten/hindernis-2.txt +++ /dev/null @@ -1,15 +0,0 @@ -// Hindernis ausweichen: zwei Klötze im Weg. -......... -.......r. -.......o. -.......o. -......... -....#.... -......... -......... -......... -.#....... -......... -......... -......... -.^....... diff --git a/web/lessons/welten/labyrinth-1.txt b/web/lessons/welten/labyrinth-1.txt deleted file mode 100644 index 63bd01c3..00000000 --- a/web/lessons/welten/labyrinth-1.txt +++ /dev/null @@ -1,12 +0,0 @@ -// Labyrinth: Gänge 30 cm breit, Wände 10 cm dick. Ziel oben links. -........... -.Z......... -........... -########... -........... -........... -........... -...######## -........... -.........<. -........... diff --git a/web/lessons/welten/labyrinth-2.txt b/web/lessons/welten/labyrinth-2.txt deleted file mode 100644 index 287cf47e..00000000 --- a/web/lessons/welten/labyrinth-2.txt +++ /dev/null @@ -1,12 +0,0 @@ -// Labyrinth: Gänge 30 cm breit, Wände 10 cm dick. Ziel oben rechts. -........... -.........Z. -........... -...######## -........... -........... -........... -########... -........... -.>......... -........... diff --git a/web/lessons/welten/linie-1.txt b/web/lessons/welten/linie-1.txt deleted file mode 100644 index 6dcfb72e..00000000 --- a/web/lessons/welten/linie-1.txt +++ /dev/null @@ -1,15 +0,0 @@ -// Linienfolger: Die Linie biegt nach rechts ab und endet vor der Wand. -######### -......... -......... -....o.... -....o.... -......... -......... -......... -......... -......... -......... -......... -..^...... -linie: 2,12 2,8 4,5 4,0.6 diff --git a/web/lessons/welten/linie-2.txt b/web/lessons/welten/linie-2.txt deleted file mode 100644 index ec530bc3..00000000 --- a/web/lessons/welten/linie-2.txt +++ /dev/null @@ -1,15 +0,0 @@ -// Linienfolger: Die Linie biegt nach links ab und endet vor der Wand. -######### -......... -......... -....o.... -....o.... -......... -......... -......... -......... -......... -......... -......... -......^.. -linie: 6,12 6,8 4,5 4,0.6 diff --git a/web/lessons/welten/linie-rot-1.txt b/web/lessons/welten/linie-rot-1.txt deleted file mode 100644 index f639632a..00000000 --- a/web/lessons/welten/linie-rot-1.txt +++ /dev/null @@ -1,14 +0,0 @@ -// Linienfolger mit Stopp: Am Ende der Linie liegt ein rotes Feld. -......... -.....r... -.....o... -.....o... -......... -......... -......... -......... -......... -......... -......... -..^...... -linie: 2,11 2,8 5,4 5,1.5 diff --git a/web/lessons/welten/linie-rot-2.txt b/web/lessons/welten/linie-rot-2.txt deleted file mode 100644 index 69a9c33e..00000000 --- a/web/lessons/welten/linie-rot-2.txt +++ /dev/null @@ -1,16 +0,0 @@ -// Linienfolger mit Stopp: Die Linie schlängelt sich zum roten Feld. -........... -...r....... -...o....... -...o....... -........... -........... -........... -........... -........... -........... -........... -........... -........... -.......^... -linie: 7,13 7,10 5,8 5,7 3,5 3,1.5 diff --git a/web/lessons/welten/parken-1.txt b/web/lessons/welten/parken-1.txt deleted file mode 100644 index ddd3a23a..00000000 --- a/web/lessons/welten/parken-1.txt +++ /dev/null @@ -1,13 +0,0 @@ -// Einparken: rechts parken Autos (Klötze), die blaue Markierung zeigt die Lücke. -#....#### -#....#### -#....#### -#..b....# -#.....o.# -#.......# -#....#### -#....#### -#....#### -#....#### -#....#### -#..^.#### diff --git a/web/lessons/welten/parken-2.txt b/web/lessons/welten/parken-2.txt deleted file mode 100644 index e7d8f74b..00000000 --- a/web/lessons/welten/parken-2.txt +++ /dev/null @@ -1,13 +0,0 @@ -// Einparken: Die Lücke liegt weiter vorn. -#....#### -#..b....# -#.....o.# -#.......# -#....#### -#....#### -#....#### -#....#### -#....#### -#....#### -#....#### -#..^.#### diff --git a/web/lessons/welten/platz-1.txt b/web/lessons/welten/platz-1.txt deleted file mode 100644 index bc337091..00000000 --- a/web/lessons/welten/platz-1.txt +++ /dev/null @@ -1,8 +0,0 @@ -// Wo ist am meisten Platz? Rechts ist es am weitesten. -........... -.....#..... -........... -........... -.#...^....# -........... -.....#..... diff --git a/web/lessons/welten/platz-2.txt b/web/lessons/welten/platz-2.txt deleted file mode 100644 index 588885aa..00000000 --- a/web/lessons/welten/platz-2.txt +++ /dev/null @@ -1,11 +0,0 @@ -// Wo ist am meisten Platz? Vorn ist es am weitesten. -.....#..... -........... -........... -........... -........... -........... -....#^.#... -........... -........... -.....#..... diff --git a/web/lessons/welten/rand-1.txt b/web/lessons/welten/rand-1.txt deleted file mode 100644 index 25efff9e..00000000 --- a/web/lessons/welten/rand-1.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Bis zur Linie: der schwarze Rand ist nah -..... -===== -..... -..... -..^.. -..... diff --git a/web/lessons/welten/rand-2.txt b/web/lessons/welten/rand-2.txt deleted file mode 100644 index 6454c26c..00000000 --- a/web/lessons/welten/rand-2.txt +++ /dev/null @@ -1,10 +0,0 @@ -// Bis zur Linie: der schwarze Rand ist weiter weg -..... -===== -..... -..... -..... -..... -..... -..^.. -..... diff --git a/web/lessons/welten/slalom.txt b/web/lessons/welten/slalom.txt deleted file mode 100644 index 50bc6686..00000000 --- a/web/lessons/welten/slalom.txt +++ /dev/null @@ -1,5 +0,0 @@ -// Slalom: ein Zeichen = 30 cm. Vier Pylonen, abwechselnd oben und unten herum, Ziel am Ende. -raster: 3 -......... ->#.#.#.#Z -......... diff --git a/web/lessons/welten/tunnel-1.txt b/web/lessons/welten/tunnel-1.txt deleted file mode 100644 index 1cbd0efc..00000000 --- a/web/lessons/welten/tunnel-1.txt +++ /dev/null @@ -1,5 +0,0 @@ -// Tunnelsucher (nach Kara): Halt im ersten Feld mit Wand links UND rechts (hellgrün). Ein Zeichen = 30 cm. -raster: 3 -..#..###. ->....o... -....##### diff --git a/web/lessons/welten/tunnel-2.txt b/web/lessons/welten/tunnel-2.txt deleted file mode 100644 index 230bfeef..00000000 --- a/web/lessons/welten/tunnel-2.txt +++ /dev/null @@ -1,8 +0,0 @@ -// Tunnelsucher, nach oben. Ein Zeichen = 30 cm. -raster: 3 -.#.#. -.#o#. -...#. -.#... -..... -..^.. diff --git a/web/lessons/welten/wald-1.txt b/web/lessons/welten/wald-1.txt deleted file mode 100644 index ece463e9..00000000 --- a/web/lessons/welten/wald-1.txt +++ /dev/null @@ -1,5 +0,0 @@ -// Kleeblatt im Wald (nach Kara): Bäume liegen auf dem Weg zum Ziel, nie zwei nebeneinander. Ein Zeichen = 30 cm. -raster: 3 -.......... ->..#.#..#Z -.......... diff --git a/web/lessons/welten/wald-2.txt b/web/lessons/welten/wald-2.txt deleted file mode 100644 index f7e759f7..00000000 --- a/web/lessons/welten/wald-2.txt +++ /dev/null @@ -1,9 +0,0 @@ -// Kleeblatt im Wald, nach oben. Ein Zeichen = 30 cm. -raster: 3 -.Z. -.#. -... -.#. -... -... -.^. diff --git a/web/lessons/welten/wand-frei.txt b/web/lessons/welten/wand-frei.txt deleted file mode 100644 index 5376c80e..00000000 --- a/web/lessons/welten/wand-frei.txt +++ /dev/null @@ -1,10 +0,0 @@ -// Wand vorn? Vor dem mBot2 ist viel Platz. -##### -..... -..... -..... -..... -..... -..... -..^.. -..... diff --git a/web/lessons/welten/wand-kurz.txt b/web/lessons/welten/wand-kurz.txt deleted file mode 100644 index e7490ee1..00000000 --- a/web/lessons/welten/wand-kurz.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Bis zur Wand: kurzer Weg -##### -..... -..Z.. -..... -..... -..^.. diff --git a/web/lessons/welten/wand-lang.txt b/web/lessons/welten/wand-lang.txt deleted file mode 100644 index 12d7c716..00000000 --- a/web/lessons/welten/wand-lang.txt +++ /dev/null @@ -1,16 +0,0 @@ -// Bis zur Wand: langer Weg -##### -..... -..Z.. -..... -..... -..... -..... -..... -..... -..... -..... -..... -..... -..... -..^.. diff --git a/web/lessons/welten/wand-mittel.txt b/web/lessons/welten/wand-mittel.txt deleted file mode 100644 index d1db0b8b..00000000 --- a/web/lessons/welten/wand-mittel.txt +++ /dev/null @@ -1,11 +0,0 @@ -// Bis zur Wand: mittlerer Weg -##### -..... -..Z.. -..... -..... -..... -..... -..... -..... -..^.. diff --git a/web/lessons/welten/wand-nah.txt b/web/lessons/welten/wand-nah.txt deleted file mode 100644 index 098a3e33..00000000 --- a/web/lessons/welten/wand-nah.txt +++ /dev/null @@ -1,5 +0,0 @@ -// Wand vorn? Die Wand steht direkt vor dem mBot2. -##### -..... -..^.. -..... diff --git a/web/lessons/welten/zeit-1.txt b/web/lessons/welten/zeit-1.txt deleted file mode 100644 index 12660f8a..00000000 --- a/web/lessons/welten/zeit-1.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Zwei Sekunden mit halber Kraft: Wo bleibt er stehen? -..... -..... -..... -..o.. -..... -..^.. diff --git a/web/lessons/welten/ziel-kurz.txt b/web/lessons/welten/ziel-kurz.txt deleted file mode 100644 index 4d828bc3..00000000 --- a/web/lessons/welten/ziel-kurz.txt +++ /dev/null @@ -1,7 +0,0 @@ -// Fahre bis zum Ziel: kurzer Weg, keine Wand -..... -..Z.. -..... -..... -..^.. -..... diff --git a/web/lessons/welten/ziel-lang.txt b/web/lessons/welten/ziel-lang.txt deleted file mode 100644 index d7efa89f..00000000 --- a/web/lessons/welten/ziel-lang.txt +++ /dev/null @@ -1,13 +0,0 @@ -// Fahre bis zum Ziel: langer Weg, keine Wand -..... -..Z.. -..... -..... -..... -..... -..... -..... -..... -..... -..^.. -..... diff --git a/web/mbot2-labor/index.html b/web/mbot2-labor/index.html deleted file mode 100644 index 800372a9..00000000 --- a/web/mbot2-labor/index.html +++ /dev/null @@ -1,316 +0,0 @@ - - - - - -mBot2-Labor - - - - -
-

mBot2-Labor

-

Verbindungstest: mBot2 per USB-Kabel (USB-C rechts am CyberPi) anschließen und einschalten: Der Schiebeschalter ON/OFF sitzt links am durchsichtigen Shield, von hinten gesehen. Wenn der Bildschirm des CyberPi leuchtet, ist er an. mBlock schließen, dann „Verbinden“. Nach dem Verbinden startet der CyberPi einmal neu (ca. 3 Sekunden).

- -
- - - nicht verbunden -
- -
-

👉 Jetzt testen (Runde 22: einzelne LEDs und Farbsensor)

-
    -
  1. Oben „Verbinden“
  2. -
  3. -
  4. Welche LED leuchtet rot, ganz links oder ganz rechts (von hinten gesehen)?
  5. -
  6. Leuchtet jetzt zusätzlich die mittlere grün?
  7. -
  8. Leuchtet die am anderen Ende blau?
  9. -
  10. Läuft ein rotes Licht hin und her?
  11. -
  12. mBot2 mit dem Farbsensor (unten vorn) auf ein rotes Blatt oder Klebeband stellen
  13. -
  14. Was steht im Protokoll?
  15. -
  16. -
  17. -
  18. Dann auf weißen Boden und auf schwarz stellen und F1 jeweils noch einmal drücken
  19. -
  20. Berichten (mit Protokoll)
  21. -
-
- -
-

1. Antwortet der CyberPi?

- - - - - - -
- -
-

2. Fahren (mBot2 hochheben oder Platz lassen!)

- - - - - - - -
- -
-

3. Sensoren lesen

- - - - - - - - - - - - - - - - -
- -
-

Bildschirm erkunden (Logo?)

- - - - - - -

Runde 2: Zeichenwerkzeuge

- - - - - - - -

Runde 3: Zeichnen ausprobieren

-

Erst verbinden, dann „Helfer anlegen“, dann ohne zu trennen die anderen Knöpfe.

- - - - - - - - - - - - - - - - - - - - -
- -
-

4. Eigene Python-Zeile

-
- - - -
-
- -
-

Protokoll

-

-  
-
- - - diff --git a/web/package-lock.json b/web/package-lock.json deleted file mode 100644 index c3788ebc..00000000 --- a/web/package-lock.json +++ /dev/null @@ -1,2390 +0,0 @@ -{ - "name": "visustruct-web", - "version": "0.15.29", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "visustruct-web", - "version": "0.15.29", - "dependencies": { - "@codemirror/autocomplete": "^6.20.4", - "@codemirror/commands": "^6.11.1", - "@codemirror/lang-java": "^6.0.2", - "@codemirror/language": "^6.13.1", - "@codemirror/state": "^6.7.6", - "@codemirror/view": "^6.43.15", - "@lezer/highlight": "^1.2.5", - "fflate": "^0.8.2", - "three": "^0.181.2", - "visublocks": "github:code4teaching/visublocks#ceb3723d5f7e39ef89fc2294968c52a60cbdec02" - }, - "devDependencies": { - "@types/node": "^22.20.5", - "@types/three": "^0.181.0", - "jsdom": "^26.1.0", - "typescript": "^5.9.3", - "vite": "^6.4.4", - "vite-plugin-singlefile": "^2.3.4", - "vitest": "^3.2.7" - } - }, - "node_modules/@asamuzakjp/css-color": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", - "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@csstools/css-calc": "^2.1.3", - "@csstools/css-color-parser": "^3.0.9", - "@csstools/css-parser-algorithms": "^3.0.4", - "@csstools/css-tokenizer": "^3.0.3", - "lru-cache": "^10.4.3" - } - }, - "node_modules/@codemirror/autocomplete": { - "version": "6.20.4", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.4.tgz", - "integrity": "sha512-yF8RhOf2SXlg00+qtupzLhrglcas61boqaONiNcbPIebHCpJocpqTBA6dyrtUrQO47hqyTdeob/3XnYilT7TaQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.17.0", - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@codemirror/commands": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.11.1.tgz", - "integrity": "sha512-O/4hG3SC1YwcmQ0d2UVNDs+AsaNWd1iHVxbTeEBuqH+6bExAiPK3iS/BvpY6rZGURALv4ZD3sIgcCmRvw3ehBg==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.7.0", - "@codemirror/view": "^6.27.0", - "@lezer/common": "^1.1.0" - } - }, - "node_modules/@codemirror/lang-java": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", - "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", - "license": "MIT", - "dependencies": { - "@codemirror/language": "^6.0.0", - "@lezer/java": "^1.0.0" - } - }, - "node_modules/@codemirror/language": { - "version": "6.13.1", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.13.1.tgz", - "integrity": "sha512-zfvU2r3TyylQLMj/te+5CnQ0rSqZi8B1oTCBbQkagcLW4Xc6imab6qN+pEJhsuNOLaJqiRyBXp9UhWA1DhVvDw==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.0.0", - "@codemirror/streamparser": "^6.0.0", - "@codemirror/view": "^6.23.0", - "@lezer/common": "^1.5.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0", - "style-mod": "^4.0.0" - } - }, - "node_modules/@codemirror/state": { - "version": "6.7.6", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.7.6.tgz", - "integrity": "sha512-kAz+AncRtKuIknedxT1bq4XwXv4UowhbkHU1myPrtVb/jZtImWuV5BXzv5vK6i3kYACsdiZiQKFQQ5Mq7elW8w==", - "license": "MIT", - "dependencies": { - "@marijn/find-cluster-break": "^1.0.0" - } - }, - "node_modules/@codemirror/streamparser": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@codemirror/streamparser/-/streamparser-6.0.0.tgz", - "integrity": "sha512-JTFPF6xrfjt0mnfGPgzcLiMwYmvIyNaoizp8BYzv8FE8KrT6GBGDZ190rtt+O7Wke3gG/MFHyiY9YGNeimof4w==", - "license": "MIT" - }, - "node_modules/@codemirror/view": { - "version": "6.43.15", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.43.15.tgz", - "integrity": "sha512-HHsfpqFeHyXLs983pSRe38Xajs0vFWZQ2BuX0m7Ki8w3jZ5iD8cuR3lBz2/5/X53Dhf32SBHKh6rtSTB6tKeGw==", - "license": "MIT", - "dependencies": { - "@codemirror/state": "^6.7.0", - "crelt": "^1.0.6", - "style-mod": "^4.1.0", - "w3c-keyname": "^2.2.4" - } - }, - "node_modules/@csstools/color-helpers": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", - "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT-0", - "engines": { - "node": ">=18" - } - }, - "node_modules/@csstools/css-calc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", - "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-color-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", - "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "dependencies": { - "@csstools/color-helpers": "^5.1.0", - "@csstools/css-calc": "^2.1.4" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-parser-algorithms": "^3.0.5", - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-parser-algorithms": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", - "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@csstools/css-tokenizer": "^3.0.4" - } - }, - "node_modules/@csstools/css-tokenizer": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", - "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/csstools" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/csstools" - } - ], - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/@dimforge/rapier3d-compat": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz", - "integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", - "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@lezer/common": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.4.tgz", - "integrity": "sha512-xt3Q4z64xM+xhg4M7NXqw1N//1O74bUFo5kz6fwMeC4qwxSuRdwkYSmjcqCEEKYkNw5e67f2hcNqBoNERAEU0w==", - "license": "MIT" - }, - "node_modules/@lezer/highlight": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.5.tgz", - "integrity": "sha512-O1GMVKgtf5YspFaRzpmqkVgMtIk2HG9uMAbH6wdAIdNh3TVzCsch/+cRi+6l2//UGDKe6NB9BW3cvuR5ezMQ0w==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.3.0" - } - }, - "node_modules/@lezer/java": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.5.tgz", - "integrity": "sha512-JppTd1iKHpeNlLYw/Zl8JX7zSY5HqAzK4zZ05lpn8l8Sk7Fo5KAKiDiMiezKoT5lfeC2xZf8HeKp1tSctj4G6Q==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.2.0", - "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" - } - }, - "node_modules/@lezer/lr": { - "version": "1.4.11", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.11.tgz", - "integrity": "sha512-UwgZcl1R66H2hDzBrA7kWyYmX6s8p5TQ+e/t7CuvgCtBEPxbEPaHYHu+pJBvTvDotEJpuQkJjbXuHiw5Cixxgw==", - "license": "MIT", - "dependencies": { - "@lezer/common": "^1.0.0" - } - }, - "node_modules/@marijn/find-cluster-break": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.4.tgz", - "integrity": "sha512-Wy0V7+SGUjnF9/TkiM1hKVDPj7jKXduPNboMVtHTA8dySMURWqfg/JZ9E2Sq8JgSJmkl7k7Qe9FLeMSrSraWmQ==", - "license": "MIT" - }, - "node_modules/@napi-rs/lzma-linux-x64-gnu": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", - "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^22.20 || ^24.12 || >=25" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.64.10.tgz", - "integrity": "sha512-oJ9ZfVIZjSjts0ZIYA48+5T0vurE5fQfX/Liad2U52Y6EN99x/OCdjGGuKMDP6jSk2i2lY6jwwA3X0drEc4PBg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.64.10.tgz", - "integrity": "sha512-LylBINgYYwgHNz8OMCy5+Y0w/LEgRYE0PcAIZfEuTuLNMf0S40GSLX6SFwqZsBLplS7RIHHy4zOAbl8Y5cQEvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.64.10.tgz", - "integrity": "sha512-vbuT3VT1GBI2fAOoUJUrsHVoOg9nbrFXTM7e3DSnb9PYf4n6jZCN8v4Vo8O6x4u5B67+/c803b8BtCZe2nIeqA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.64.10.tgz", - "integrity": "sha512-qYUp/qYvM3T4cF+VWalPl+przBUESILxi+qZ1m1MVRHpg06vS+jHaxXubjCaRpXjLJPDFdXzOZ0FABDghHOhNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.64.10.tgz", - "integrity": "sha512-v9NRQG+JztbP4DP/0yuhSyDcEJk047lYbqeNnqIgbXITcVDjGDS7OpkW5nadSY3tJ7MY8W1qIMMqP4Q3Q8BDjQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.64.10.tgz", - "integrity": "sha512-CHc9LBHdxhn6/NXfb9IegXCWoz5VlvcDOx9QrzZ9q+XYhFlfzwymSWJjOlZcEMMtC1zgMV7qxDe6U18AI1SG0A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.64.10.tgz", - "integrity": "sha512-ble2WR7kWfzZFJUd3ODMGCJn1XDszptfRC0QvWc0h9/EA6MxnrQ1mvbjPZifEdgHyfHhDrwWeKeJONDITcrHSg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.64.10.tgz", - "integrity": "sha512-DMCYegihxBMKvLpODdk4OsvIgxtHTi+Oddf1Pm3d6wMVNjcBiG2pqyXjMPy55HgG1gCdEFr9VfhB1CmvuKn1lw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.64.10.tgz", - "integrity": "sha512-HQ6HuVH9TmhvPWIas7MA/KeAROgdwqH3REpUU6hZq47NW7m1b1QCrHgM+M3+N/B8DfMt88x1/z79ppRc9RflCQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.64.10.tgz", - "integrity": "sha512-avBhxba1DdctX0kxKkN3JWdTSg72QsxSn6FdfhZ9MkL3TTYDOXqxY1GqbC7VNttlgviK7tCe2No5btfRkFgeIg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.64.10.tgz", - "integrity": "sha512-UtQMex7fiXq0W/GqcWRpy6XKqUbXgQeGKB+Bd5rJngmHzpI+3kjwpWFLAhKg02onFsIMSh+Zx0zp9JIMQ7/05A==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.64.10.tgz", - "integrity": "sha512-0Aum2pUsCSuOopi6oka+nR1DTj/nfvc1xjL979lvh6kXUjNa30TCX7sKFjyMsrX7bK3bNVs0FsJwQcE2HCWX1Q==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.64.10.tgz", - "integrity": "sha512-4dgsouKal3qfMFORgtNigLPt7K3HXiO6jTr7+nFJv1GWu1OkZx0kvQIXbK4sjrIGnaLBk0s80eQCQ9hPIelLrw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.64.10.tgz", - "integrity": "sha512-d7NE89try3jisYzBD7J9M5iwfgcrXYC4dwHEjQ3HIWBftvwch1U/cy3iZ26SthtUOyClKiu7iqYiFjQEwiOlqQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.64.10.tgz", - "integrity": "sha512-dqB5ChSBp91JWXxpCKAS5ZVAE38esKQvdfyqANm0lF5JPiQrXnYwgG3oVBQmuNRcQ1WHkA3j5qYLsrl2CtfhCA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.64.10.tgz", - "integrity": "sha512-7GMf3vbQQslorWLmfsbi5fdt9RVnglk205XpOMFvW6UY5jE5oJc6JLieQJPHtsYlZ2EO0PtC06nY9Hz/IG8guw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.64.10.tgz", - "integrity": "sha512-tuKbkB23DKc5LG+irPSUnZZztRHqC3CNmyxeWloueBteHB+i+1HkRCMzAE41lseLDlarybeLhqfibFitKOxKMg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.64.10.tgz", - "integrity": "sha512-ntHtQOMw7oIshHRF1swV3vyBw+beQt9PeZ04O3D32EeDZUMoHMJuZKp4/fDllH/+xogeK0GUCONVfjjKusqKLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.64.10.tgz", - "integrity": "sha512-Dlh8q8r145aYgh+N3gNIG1dz35Fz/FCxe5hs/43orDsffjP8S1P4Xz5I4FpSJsDS4LpQw1QmswZkQlUbkCldjQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.64.10.tgz", - "integrity": "sha512-VBJhHWz/55u45CWFv9OmfSRDgJDVENESgFxSAn13gIKJpTuFJbP4/7ymMZhDfeKFx1zGn1XBsLnNB7Ryy71qww==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.64.10.tgz", - "integrity": "sha512-Vjcazg+EEzUfIz+LcaxS6YtbQHo+sv0sCD+5VbwhY504bCKNW4OOreVgKq61pHT2esRQ/+AXYVPStDwxSvAOdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.64.10.tgz", - "integrity": "sha512-jJAaP4El+HWxYkM0jeJghASaFgfnluY0FRHhyHXxU6STs/nBhKMIxIMDrJEFg2ftKz6orX9WAo4diVan5NugNA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.64.10.tgz", - "integrity": "sha512-bXXdRUfRx9PaHCToKt8R/7wjDV6Tbk/hmSHK0uQBKmD3rj4jkuYndct8uGX77ZuZsePrVkQbWUgPR2jJhCTMpQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.64.10.tgz", - "integrity": "sha512-57Idx+VkEOQeG6Ys6sZOBFT7FfbeyxGv9A3k1m+Lf/z2SwqT40rNWbAINzjPHSMoCCBvY7Fnb9RGZWQin/HHAw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.64.10.tgz", - "integrity": "sha512-vrvuasIpm/xf+BpZbzgirku0WnrQZMAc+H2Ai26jzqBxYEmQQE4DdJb7h8erkJMfRRipx0ma2Cty8jJwUm9ocQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@tweenjs/tween.js": { - "version": "23.1.3", - "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", - "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/chai": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", - "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/deep-eql": "*", - "assertion-error": "^2.0.1" - } - }, - "node_modules/@types/deep-eql": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", - "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "22.20.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", - "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/stats.js": { - "version": "0.17.4", - "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", - "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/three": { - "version": "0.181.0", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.181.0.tgz", - "integrity": "sha512-MLF1ks8yRM2k71D7RprFpDb9DOX0p22DbdPqT/uAkc6AtQXjxWCVDjCy23G9t1o8HcQPk7woD2NIyiaWcWPYmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@dimforge/rapier3d-compat": "~0.12.0", - "@tweenjs/tween.js": "~23.1.3", - "@types/stats.js": "*", - "@types/webxr": "*", - "@webgpu/types": "*", - "fflate": "~0.8.2", - "meshoptimizer": "~0.22.0" - } - }, - "node_modules/@types/webxr": { - "version": "0.5.24", - "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", - "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitest/expect": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", - "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", - "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/spy": "3.2.7", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", - "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", - "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "3.2.7", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", - "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.7", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", - "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", - "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "3.2.7", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@webgpu/types": { - "version": "0.1.74", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.74.tgz", - "integrity": "sha512-lgiI4hbuLcI9unnm2cL/tvCaQU45dp0xcLWJh5uB/9MBGvIA4F8XIk7nSiBeg+K4xWGYwgZKn80LmERI5CxoTA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, - "node_modules/crelt": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", - "integrity": "sha512-aK6BbWfhf4U/wCcLHKPJl/xa6VkVstRaPywWtMKGwuOLc/wZTyQYuoxgvZnNsBvv7Kg3YTBQYYBCggcviQczuA==", - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", - "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@asamuzakjp/css-color": "^3.2.0", - "rrweb-cssom": "^0.8.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/data-urls": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", - "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", - "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", - "dev": true, - "license": "MIT" - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/expect-type": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", - "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", - "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^3.1.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsdom": { - "version": "26.1.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", - "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssstyle": "^4.2.1", - "data-urls": "^5.0.0", - "decimal.js": "^10.5.0", - "html-encoding-sniffer": "^4.0.0", - "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.6", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.16", - "parse5": "^7.2.1", - "rrweb-cssom": "^0.8.0", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^5.1.1", - "w3c-xmlserializer": "^5.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^3.1.1", - "whatwg-mimetype": "^4.0.0", - "whatwg-url": "^14.1.1", - "ws": "^8.18.0", - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "canvas": "^3.0.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/meshoptimizer": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz", - "integrity": "sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", - "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/nwsapi": { - "version": "2.2.28", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.28.tgz", - "integrity": "sha512-IlVB7OS7qrOsVYlpnFIkETjMwT9jwvmocJmmM+GZU/PAB3uGi9Ezd7vcWhWBUnSc0ya4ppmQITOyP1ez9gg8cg==", - "dev": true, - "license": "MIT" - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", - "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.20", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/rollup": { - "version": "4.64.10", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.64.10.tgz", - "integrity": "sha512-8N52YH1B5JzBP+B1rkcBERG7rfRTGAM0RmUpyd76yyRfTljuG452GCNbVm6MPzs1+gXBha6Rw5/oHRFohOpfDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.9" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@napi-rs/lzma-linux-x64-gnu": "1.5.1", - "@rollup/rollup-android-arm-eabi": "4.64.10", - "@rollup/rollup-android-arm64": "4.64.10", - "@rollup/rollup-darwin-arm64": "4.64.10", - "@rollup/rollup-darwin-x64": "4.64.10", - "@rollup/rollup-freebsd-arm64": "4.64.10", - "@rollup/rollup-freebsd-x64": "4.64.10", - "@rollup/rollup-linux-arm-gnueabihf": "4.64.10", - "@rollup/rollup-linux-arm-musleabihf": "4.64.10", - "@rollup/rollup-linux-arm64-gnu": "4.64.10", - "@rollup/rollup-linux-arm64-musl": "4.64.10", - "@rollup/rollup-linux-loong64-gnu": "4.64.10", - "@rollup/rollup-linux-loong64-musl": "4.64.10", - "@rollup/rollup-linux-ppc64-gnu": "4.64.10", - "@rollup/rollup-linux-ppc64-musl": "4.64.10", - "@rollup/rollup-linux-riscv64-gnu": "4.64.10", - "@rollup/rollup-linux-riscv64-musl": "4.64.10", - "@rollup/rollup-linux-s390x-gnu": "4.64.10", - "@rollup/rollup-linux-x64-gnu": "4.64.10", - "@rollup/rollup-linux-x64-musl": "4.64.10", - "@rollup/rollup-openbsd-x64": "4.64.10", - "@rollup/rollup-openharmony-arm64": "4.64.10", - "@rollup/rollup-win32-arm64-msvc": "4.64.10", - "@rollup/rollup-win32-ia32-msvc": "4.64.10", - "@rollup/rollup-win32-x64-gnu": "4.64.10", - "@rollup/rollup-win32-x64-msvc": "4.64.10", - "fsevents": "~2.3.2" - } - }, - "node_modules/rrweb-cssom": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", - "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, - "node_modules/source-map-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", - "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, - "node_modules/std-env": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", - "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", - "dev": true, - "license": "MIT" - }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/style-mod": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.4.tgz", - "integrity": "sha512-XXWIQt633/EpAFx8aZDOTjBzrCaGmhvEQlQo6MVPfa2OzO2cWo+4hV9h+6UkHYlXGfy+ODXKUdP7Pthmcu5ATw==", - "license": "MIT" - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/three": { - "version": "0.181.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.181.2.tgz", - "integrity": "sha512-k/CjiZ80bYss6Qs7/ex1TBlPD11whT9oKfT8oTGiHa34W4JRd1NiH/Tr1DbHWQ2/vMUypxksLnF2CfmlmM5XFQ==", - "license": "MIT" - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", - "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tldts": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", - "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tldts-core": "^6.1.86" - }, - "bin": { - "tldts": "bin/cli.js" - } - }, - "node_modules/tldts-core": { - "version": "6.1.86", - "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", - "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tough-cookie": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", - "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tldts": "^6.1.32" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/tr46": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", - "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/visublocks": { - "version": "0.1.0", - "resolved": "git+ssh://git@github.com/code4teaching/visublocks.git#ceb3723d5f7e39ef89fc2294968c52a60cbdec02", - "integrity": "sha512-DnDBUaFDt5Fzf0RtGCdJiPp9pTFw2h86GDo6N/63toqsmiY6UgruCLZuwwubzPgmVS8HnnQ3FHkWlzpiIBu8Ng==", - "license": "MIT" - }, - "node_modules/vite": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.4.tgz", - "integrity": "sha512-xGjU/SyjeNyb0E4KxXBvYz/1C+3JXuujvf2hXNOtnlcE+58o5ajd9N2/n2VhkiJvpCP2qQasnUpx5PXNeI+p1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vite-plugin-singlefile": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.4.tgz", - "integrity": "sha512-1FAhlTCL6KJDpSUudrVU+gspapvS+tmKCg70vwNaAU6tyXn3E/C6+NnwQIze06byhBRv7VggZ78K0h+J/Rsdrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^4.0.7" - }, - "engines": { - "node": ">18.0.0" - }, - "peerDependencies": { - "rollup": "^4.59.0", - "vite": "^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/vitest": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", - "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.7", - "@vitest/mocker": "3.2.7", - "@vitest/pretty-format": "^3.2.7", - "@vitest/runner": "3.2.7", - "@vitest/snapshot": "3.2.7", - "@vitest/spy": "3.2.7", - "@vitest/utils": "3.2.7", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.7", - "@vitest/ui": "3.2.7", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/w3c-keyname": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", - "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "license": "MIT" - }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ws": { - "version": "8.22.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", - "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", - "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - } - } -} diff --git a/web/package.json b/web/package.json deleted file mode 100644 index 9cdad157..00000000 --- a/web/package.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "name": "visustruct-web", - "private": true, - "version": "0.15.29", - "type": "module", - "scripts": { - "dev": "vite", - "build": "tsc --noEmit && vite build", - "build:single": "tsc --noEmit && vite build --mode single", - "test": "vitest run", - "build:site": "tsc --noEmit && vite build --outDir ../website/app --emptyOutDir" - }, - "devDependencies": { - "@types/node": "^22.20.5", - "@types/three": "^0.181.0", - "jsdom": "^26.1.0", - "typescript": "^5.9.3", - "vite": "^6.4.4", - "vite-plugin-singlefile": "^2.3.4", - "vitest": "^3.2.7" - }, - "dependencies": { - "@codemirror/autocomplete": "^6.20.4", - "@codemirror/commands": "^6.11.1", - "@codemirror/lang-java": "^6.0.2", - "@codemirror/language": "^6.13.1", - "@codemirror/state": "^6.7.6", - "@codemirror/view": "^6.43.15", - "@lezer/highlight": "^1.2.5", - "fflate": "^0.8.2", - "three": "^0.181.2", - "visublocks": "github:code4teaching/visublocks#ceb3723d5f7e39ef89fc2294968c52a60cbdec02" - } -} diff --git a/web/prog.png b/web/prog.png deleted file mode 100644 index d1cce063..00000000 Binary files a/web/prog.png and /dev/null differ diff --git a/web/settings.png b/web/settings.png deleted file mode 100644 index 2b24f02a..00000000 Binary files a/web/settings.png and /dev/null differ diff --git a/web/shots/e2e-1.png b/web/shots/e2e-1.png deleted file mode 100644 index ec3c2db9..00000000 Binary files a/web/shots/e2e-1.png and /dev/null differ diff --git a/web/shots/e2e-2.png b/web/shots/e2e-2.png deleted file mode 100644 index fddcf06d..00000000 Binary files a/web/shots/e2e-2.png and /dev/null differ diff --git a/web/shots/e2e-3.png b/web/shots/e2e-3.png deleted file mode 100644 index 1e5bf02f..00000000 Binary files a/web/shots/e2e-3.png and /dev/null differ diff --git a/web/shots/e2e-4.png b/web/shots/e2e-4.png deleted file mode 100644 index 26264075..00000000 Binary files a/web/shots/e2e-4.png and /dev/null differ diff --git a/web/src/core/classModel.ts b/web/src/core/classModel.ts deleted file mode 100644 index 5043dcc3..00000000 --- a/web/src/core/classModel.ts +++ /dev/null @@ -1,690 +0,0 @@ -/** - * VisuObject: Klassen aus Java-Code für das Klassendiagramm lesen und das Diagramm wieder in Java schreiben. - * Kein vollständiger Java-Parser: nur der Aufbau (Klassen, Attribute, Methoden-Köpfe), Methodenrümpfe werden übersprungen. - * Jeder Teil kennt seine Stelle im Code, damit eine Änderung im Diagramm nur diese Stelle neu schreibt. - */ - -export type Visibility = '+' | '-' | '#' | '~' - -export interface UmlParam { - name: string - type: string -} - -export interface UmlMember { - kind: 'field' | 'method' | 'constructor' - name: string - /** Typ des Attributs bzw. Ergebnistyp der Methode (leer beim Konstruktor). */ - type: string - params: UmlParam[] - visibility: Visibility - isStatic: boolean - isAbstract: boolean - /** Ganze Deklaration im Code, mit Rumpf bzw. `;`. */ - from: number - to: number - /** Kopf: bei Attributen bis zum Namen (ohne `= …;`), bei Methoden bis zur `)`. */ - headerFrom: number - headerTo: number - /** Teil einer Zeile wie `int a, b;`: dann wird beim Ändern nur der Name ersetzt. */ - shared: boolean - nameFrom: number - nameTo: number -} - -export interface UmlClass { - name: string - kind: 'class' | 'interface' - isAbstract: boolean - superclass?: string - interfaces: string[] - members: UmlMember[] - from: number - to: number - /** Kopf bis vor `{`. */ - headerFrom: number - headerTo: number - nameFrom: number - nameTo: number - /** Stelle der schließenden `}`. */ - bodyEnd: number -} - -export interface UmlRelation { - kind: 'extends' | 'implements' | 'association' - from: string - to: string - /** Bei Assoziationen: Name des Attributs und Multiplizität am Ziel. */ - role?: string - multiplicity?: string -} - -interface Token { - text: string - from: number - to: number - /** Bezeichner oder Schlüsselwort. */ - word: boolean -} - -/** Zerlegt den Code in Wörter und Zeichen; Kommentare, Zeichenketten und Zeichen fallen weg (als ein Token "…"). */ -export function tokenize(source: string): Token[] { - const tokens: Token[] = [] - let i = 0 - while (i < source.length) { - const c = source[i] - if (/\s/.test(c)) { - i++ - } else if (source.startsWith('//', i)) { - const end = source.indexOf('\n', i) - i = end < 0 ? source.length : end - } else if (source.startsWith('/*', i)) { - const end = source.indexOf('*/', i + 2) - i = end < 0 ? source.length : end + 2 - } else if (c === '"' || c === "'") { - const start = i++ - while (i < source.length && source[i] !== c && source[i] !== '\n') i += source[i] === '\\' ? 2 : 1 - i++ - tokens.push({ text: '"…"', from: start, to: Math.min(i, source.length), word: false }) - } else if (/[\p{L}_$]/u.test(c)) { - const start = i - while (i < source.length && /[\p{L}\p{N}_$]/u.test(source[i])) i++ - tokens.push({ text: source.slice(start, i), from: start, to: i, word: true }) - } else if (/\d/.test(c)) { - const start = i - while (i < source.length && /[\w.]/.test(source[i])) i++ - tokens.push({ text: source.slice(start, i), from: start, to: i, word: false }) - } else { - tokens.push({ text: c, from: i, to: i + 1, word: false }) - i++ - } - } - return tokens -} - -const modifierWords = new Set(['public', 'private', 'protected', 'static', 'final', 'abstract', 'synchronized', 'native', 'transient', 'volatile', 'default', 'strictfp', 'sealed']) - -/** Index hinter der passenden schließenden Klammer (`{`/`}`, `(`/`)`, `<`/`>`). */ -function skipBalanced(tokens: Token[], i: number, open: string, close: string): number { - let depth = 0 - for (; i < tokens.length; i++) { - if (tokens[i].text === open) depth++ - else if (tokens[i].text === close && --depth === 0) return i + 1 - } - return tokens.length -} - -/** Typ-Text aus Tokens, ohne Leerzeichen außer nach Kommas: `ArrayList`, `Map`, `int[]`. */ -function typeText(tokens: Token[]): string { - return tokens.map((t) => (t.text === ',' ? ', ' : t.text)).join('') -} - -/** Teilt an Kommas, die nicht in `<>`, `()` oder `[]` stehen. */ -function splitTopLevel(tokens: Token[]): Token[][] { - const parts: Token[][] = [[]] - let depth = 0 - for (const t of tokens) { - if (t.text === '<' || t.text === '(' || t.text === '[' || t.text === '{') depth++ - else if (t.text === '>' || t.text === ')' || t.text === ']' || t.text === '}') depth-- - if (t.text === ',' && depth === 0) parts.push([]) - else parts[parts.length - 1].push(t) - } - return parts.filter((p) => p.length > 0) -} - -/** Lässt Annotationen wie `@Override` oder `@SuppressWarnings("x")` weg. */ -function withoutAnnotations(tokens: Token[]): Token[] { - const out: Token[] = [] - for (let i = 0; i < tokens.length; i++) { - if (tokens[i].text === '@' && tokens[i + 1]?.word && tokens[i + 1].text !== 'interface') { - i++ - if (tokens[i + 1]?.text === '(') i = skipBalanced(tokens, i + 1, '(', ')') - 1 - continue - } - out.push(tokens[i]) - } - return out -} - -function visibilityOf(mods: Set, fallback: Visibility): Visibility { - if (mods.has('public')) return '+' - if (mods.has('private')) return '-' - if (mods.has('protected')) return '#' - return fallback -} - -function parseParams(tokens: Token[]): UmlParam[] { - return splitTopLevel(tokens).map((part) => { - const p = withoutAnnotations(part).filter((t) => t.text !== 'final') - const name = p[p.length - 1] - // `String... namen` bleibt als Typ `String...` - return { name: name?.word ? name.text : '', type: typeText(p.slice(0, -1)) } - }) -} - -/** Liest eine Deklaration im Klassenrumpf (ohne `;` bzw. Rumpf). */ -function parseMember(decl: Token[], className: string, inInterface: boolean, end: number): UmlMember[] { - const tokens = withoutAnnotations(decl) - const mods = new Set() - let i = 0 - while (i < tokens.length && modifierWords.has(tokens[i].text)) mods.add(tokens[i++].text) - // Generische Methode `public void f()`: Typparameter überspringen. - if (tokens[i]?.text === '<') i = skipBalanced(tokens, i, '<', '>') - const rest = tokens.slice(i) - if (rest.length === 0 || decl.length === 0) return [] - const headerFrom = decl[0].from - const paren = rest.findIndex((t) => t.text === '(') - const equals = rest.findIndex((t) => t.text === '=') - const isMethod = paren > 0 && (equals < 0 || paren < equals) - const fallback: Visibility = inInterface ? '+' : '~' - if (isMethod) { - const nameToken = rest[paren - 1] - if (!nameToken.word) return [] - const close = skipBalanced(rest, paren, '(', ')') - const isConstructor = paren === 1 && nameToken.text === className - return [ - { - kind: isConstructor ? 'constructor' : 'method', - name: nameToken.text, - type: isConstructor ? '' : typeText(rest.slice(0, paren - 1)), - params: parseParams(rest.slice(paren + 1, close - 1)), - visibility: visibilityOf(mods, fallback), - isStatic: mods.has('static'), - isAbstract: mods.has('abstract') || (inInterface && !mods.has('default') && !mods.has('static')), - from: headerFrom, - to: end, - headerFrom, - headerTo: rest[close - 1]?.to ?? end, - shared: false, - nameFrom: nameToken.from, - nameTo: nameToken.to, - }, - ] - } - // Attribut(e): Typ, dann ein oder mehrere Namen, jeweils evtl. mit `= …`. - const declarators = splitTopLevel(rest) - const first = declarators[0] - const firstEq = first.findIndex((t) => t.text === '=') - const firstHead = firstEq < 0 ? first : first.slice(0, firstEq) - // Name ist das letzte Wort vor `=` (`int[] zahlen`, `int zahlen[]`). - let nameIndex = firstHead.length - 1 - while (nameIndex > 0 && !firstHead[nameIndex].word) nameIndex-- - if (nameIndex < 1) return [] - const baseType = typeText(firstHead.slice(0, nameIndex)) - const shared = declarators.length > 1 - const members: UmlMember[] = [] - declarators.forEach((d, k) => { - const eq = d.findIndex((t) => t.text === '=') - const head = eq < 0 ? d : d.slice(0, eq) - const nameTok = k === 0 ? firstHead[nameIndex] : head.find((t) => t.word) - if (!nameTok) return - const brackets = head.filter((t) => t.from > nameTok.from && t.text === '[').length - members.push({ - kind: 'field', - name: nameTok.text, - type: baseType + '[]'.repeat(brackets), - params: [], - visibility: visibilityOf(mods, fallback), - isStatic: mods.has('static'), - isAbstract: false, - from: headerFrom, - to: end, - headerFrom, - headerTo: k === 0 ? (head[head.length - 1]?.to ?? nameTok.to) : nameTok.to, - shared, - nameFrom: nameTok.from, - nameTo: nameTok.to, - }) - }) - return members -} - -function parseClassBody(tokens: Token[], start: number, cls: UmlClass): number { - let decl: Token[] = [] - let i = start - while (i < tokens.length) { - const t = tokens[i] - if (t.text === '}') { - cls.bodyEnd = t.from - return i + 1 - } - if (t.text === ';') { - cls.members.push(...parseMember(decl, cls.name, cls.kind === 'interface', t.to)) - decl = [] - i++ - continue - } - if (t.text === '{') { - const hasEquals = decl.some((d) => d.text === '=') - const close = skipBalanced(tokens, i, '{', '}') - if (hasEquals) { - // `int[] a = {1, 2};` oder ein Lambda: weiter bis zum `;`. - decl.push(...tokens.slice(i, close)) - i = close - continue - } - const words = decl.map((d) => d.text) - if (words.includes('class') || words.includes('interface') || words.includes('enum')) { - // Innere Klasse: überspringen (vorerst nicht im Diagramm). - } else if (decl.length > 0) { - cls.members.push(...parseMember(decl, cls.name, cls.kind === 'interface', tokens[close - 1]?.to ?? t.to)) - } - decl = [] - i = close - continue - } - decl.push(t) - i++ - } - cls.bodyEnd = tokens.length > 0 ? tokens[tokens.length - 1].to : 0 - return i -} - -/** Alle Klassen und Interfaces auf oberster Ebene, in der Reihenfolge im Code. */ -export function parseClasses(source: string): UmlClass[] { - const tokens = tokenize(source) - const classes: UmlClass[] = [] - let decl: Token[] = [] - let i = 0 - while (i < tokens.length) { - const t = tokens[i] - if (t.text === ';') { - decl = [] - i++ - continue - } - if (t.text !== '{') { - decl.push(t) - i++ - continue - } - const head = withoutAnnotations(decl) - const k = head.findIndex((d) => d.text === 'class' || d.text === 'interface') - const nameToken = k >= 0 ? head[k + 1] : undefined - if (!nameToken?.word) { - i = skipBalanced(tokens, i, '{', '}') - decl = [] - continue - } - // Kopf beginnt beim ersten Modifizierer (nicht bei `import …`, das schon mit `;` endete). - let s = k - while (s > 0 && (modifierWords.has(head[s - 1].text) || head[s - 1].text === '@')) s-- - const cls: UmlClass = { - name: nameToken.text, - kind: head[k].text === 'interface' ? 'interface' : 'class', - isAbstract: head.slice(s, k).some((d) => d.text === 'abstract'), - interfaces: [], - members: [], - from: decl[decl.indexOf(head[s])]?.from ?? head[s].from, - to: 0, - headerFrom: decl[decl.indexOf(head[s])]?.from ?? head[s].from, - headerTo: head[head.length - 1].to, - nameFrom: nameToken.from, - nameTo: nameToken.to, - bodyEnd: 0, - } - // `extends A` (Interfaces: `extends A, B`) und `implements A, B`, Typparameter weglassen. - let j = k + 2 - if (head[j]?.text === '<') j = skipBalanced(head, j, '<', '>') - let clause = '' - for (; j < head.length; j++) { - const w = head[j] - if (w.text === 'extends' || w.text === 'implements') clause = w.text - else if (w.text === '<') j = skipBalanced(head, j, '<', '>') - 1 - else if (w.word && clause === 'extends' && cls.kind === 'class') cls.superclass ??= w.text - else if (w.word && clause) cls.interfaces.push(w.text) - } - i = parseClassBody(tokens, i + 1, cls) - cls.to = tokens[i - 1]?.to ?? source.length - classes.push(cls) - decl = [] - } - return classes -} - -/** Elementtyp von Behältern: `Hund[]`, `ArrayList`, `List` → `Hund` mit `0..*`. */ -export function associationTarget(type: string): { name: string; many: boolean } { - const array = /^([\p{L}_$][\p{L}\p{N}_$]*)(\[\])+$/u.exec(type) - if (array) return { name: array[1], many: true } - const generic = /^(?:ArrayList|LinkedList|List|Set|HashSet|TreeSet|Collection|Queue|Deque|ArrayDeque|Stack|Vector)<([\p{L}_$][\p{L}\p{N}_$]*)>$/u.exec(type) - if (generic) return { name: generic[1], many: true } - return { name: type, many: false } -} - -/** Pfeile im Klassendiagramm: Vererbung, Realisierung und Assoziationen aus Attributen. */ -export function relations(classes: UmlClass[]): UmlRelation[] { - const names = new Set(classes.map((c) => c.name)) - const out: UmlRelation[] = [] - for (const c of classes) { - if (c.superclass && names.has(c.superclass)) out.push({ kind: 'extends', from: c.name, to: c.superclass }) - for (const i of c.interfaces) if (names.has(i)) out.push({ kind: c.kind === 'interface' ? 'extends' : 'implements', from: c.name, to: i }) - for (const m of c.members) { - if (m.kind !== 'field') continue - const target = associationTarget(m.type) - if (names.has(target.name)) out.push({ kind: 'association', from: c.name, to: target.name, role: m.name, multiplicity: target.many ? '0..*' : '1' }) - } - } - return out -} - -// MARK: - UML-Schreibweise - -/** Zeile im Klassendiagramm: `- name: String`, `+ bellen(laut: int): void`, `+ Hund(name: String)`. */ -export function umlText(m: UmlMember): string { - const params = m.params.map((p) => `${p.name}: ${p.type}`).join(', ') - if (m.kind === 'field') return `${m.visibility} ${m.name}: ${m.type}` - if (m.kind === 'constructor') return `${m.visibility} ${m.name}(${params})` - return `${m.visibility} ${m.name}(${params}): ${m.type}` -} - -export interface UmlLine { - kind: 'field' | 'method' - visibility?: Visibility - name: string - type?: string - params: UmlParam[] - isStatic: boolean -} - -const identifier = /^[\p{L}_$][\p{L}\p{N}_$]*$/u - -/** - * Getippte Zeile in UML-Schreibweise lesen. Erlaubt auch Java-Stil (`String name`, `void bellen()`), - * damit Anfänger nicht scheitern. `static` davor oder unterstrichen geht nicht zu tippen, also: Wort `static`. - */ -export function parseUmlLine(text: string): UmlLine | undefined { - let s = text.trim().replace(/;$/, '').trim() - if (!s) return undefined - let visibility: Visibility | undefined - if (/^[+\-#~]/.test(s)) { - visibility = s[0] as Visibility - s = s.slice(1).trim() - } - let isStatic = false - const words = s.split(/\s+/) - while (['static', 'public', 'private', 'protected'].includes(words[0])) { - const w = words.shift()! - if (w === 'static') isStatic = true - else visibility ??= w === 'public' ? '+' : w === 'private' ? '-' : '#' - } - s = words.join(' ') - const paren = s.indexOf('(') - if (paren >= 0) { - const close = s.lastIndexOf(')') - if (close < paren) return undefined - let name = s.slice(0, paren).trim() - let type = s.slice(close + 1).trim().replace(/^:\s*/, '') || undefined - // Java-Stil `void bellen()` - const javaStyle = /^(.+)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(name) - if (javaStyle) { - type ??= javaStyle[1].replace(/\s+/g, '') - name = javaStyle[2] - } - if (!identifier.test(name)) return undefined - const params = s - .slice(paren + 1, close) - .split(',') - .map((p) => p.trim()) - .filter(Boolean) - .map((p) => { - const uml = /^([\p{L}_$][\p{L}\p{N}_$]*)\s*:\s*(.+)$/u.exec(p) - if (uml) return { name: uml[1], type: uml[2].replace(/\s+/g, '') } - const java = /^(.+)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(p) - return java ? { name: java[2], type: java[1].replace(/\s+/g, '') } : { name: p, type: 'int' } - }) - return { kind: 'method', visibility, name, type: type?.replace(/\s+/g, ''), params, isStatic } - } - const uml = /^([\p{L}_$][\p{L}\p{N}_$]*)\s*(?::\s*(.+))?$/u.exec(s) - if (uml) return { kind: 'field', visibility, name: uml[1], type: uml[2]?.replace(/\s+/g, ''), params: [], isStatic } - const java = /^(.+?)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(s) - if (java) return { kind: 'field', visibility, name: java[2], type: java[1].replace(/\s+/g, ''), params: [], isStatic } - return undefined -} - -const javaVisibility: Record = { '+': 'public ', '-': 'private ', '#': 'protected ', '~': '' } - -/** Standardwert für `return` im neuen Methodenrumpf, damit der Code gleich übersetzt. */ -function defaultValue(type: string): string { - if (/^(int|long|short|byte)$/.test(type)) return '0' - if (/^(double|float)$/.test(type)) return '0.0' - if (type === 'boolean') return 'false' - if (type === 'char') return "' '" - return 'null' -} - -function javaHeader(line: UmlLine, cls: UmlClass, abstract: boolean): string { - const inInterface = cls.kind === 'interface' - const vis = inInterface ? '' : javaVisibility[line.visibility ?? (line.kind === 'field' ? '-' : '+')] - const stat = line.isStatic ? 'static ' : '' - if (line.kind === 'field') return `${vis}${stat}${line.type ?? 'int'} ${line.name}` - const params = line.params.map((p) => `${p.type} ${p.name}`).join(', ') - if (line.name === cls.name) return `${vis}${line.name}(${params})` - return `${vis}${stat}${abstract && !inInterface ? 'abstract ' : ''}${line.type ?? 'void'} ${line.name}(${params})` -} - -// MARK: - Diagramm → Code - -const lineStart = (source: string, pos: number) => source.lastIndexOf('\n', pos - 1) + 1 - -/** Bereich auf ganze Zeilen erweitern, wenn davor und danach nur Leerraum steht. */ -function wholeLines(source: string, from: number, to: number): [number, number] { - const start = lineStart(source, from) - const before = source.slice(start, from) - let end = to - while (end < source.length && (source[end] === ' ' || source[end] === '\t')) end++ - if (/^\s*$/.test(before) && (end === source.length || source[end] === '\n')) return [start, Math.min(end + 1, source.length)] - return [from, to] -} - -/** Einrückung der Mitglieder einer Klasse (wie das erste Mitglied, sonst vier Leerzeichen). */ -function memberIndent(source: string, cls: UmlClass): string { - const first = cls.members[0] - if (first) { - const start = lineStart(source, first.from) - const ws = /^[ \t]*/.exec(source.slice(start))![0] - if (start + ws.length === first.from) return ws - } - const classIndent = /^[ \t]*/.exec(source.slice(lineStart(source, cls.from)))![0] - return classIndent + ' ' -} - -const splice = (source: string, from: number, to: number, text: string) => source.slice(0, from) + text + source.slice(to) - -export function addClass(source: string, name: string, kind: 'class' | 'interface' = 'class'): string { - const text = `public ${kind} ${name} {\n}\n` - if (!source.trim()) return text - return source.replace(/\s*$/, '') + '\n\n' + text -} - -export function deleteClass(source: string, cls: UmlClass): string { - const [from, to] = wholeLines(source, cls.from, cls.to) - // Leere Zeile davor mitnehmen, sonst bleiben zwei stehen. - const prev = source.slice(0, from).match(/\n\s*\n$/) - return splice(source, prev ? from - prev[0].length + 1 : from, to, '') -} - -/** Klasse umbenennen: Name im Kopf und alle Konstruktoren. */ -export function renameClass(source: string, cls: UmlClass, name: string): string { - const spots = [cls, ...cls.members.filter((m) => m.kind === 'constructor')].map((x) => [x.nameFrom, x.nameTo] as const) - for (const [from, to] of spots.sort((a, b) => b[0] - a[0])) source = splice(source, from, to, name) - return source -} - -/** Oberklasse setzen oder entfernen (nur bei Klassen); `implements` bleibt. */ -export function setSuperclass(source: string, cls: UmlClass, superclass: string | undefined): string { - const head = source.slice(cls.headerFrom, cls.headerTo) - const nameEnd = cls.nameTo - cls.headerFrom - const before = head.slice(0, nameEnd) - let after = head.slice(nameEnd).replace(/\s*\bextends\s+[\p{L}_$][\p{L}\p{N}_$<>,\s]*?(?=\s*\bimplements\b|\s*$)/u, '') - if (superclass) after = ` extends ${superclass}` + after - return splice(source, cls.headerFrom, cls.headerTo, before + after) -} - -/** Interface hinzufügen: `implements` bei Klassen, `extends` bei Interfaces. */ -export function addInterface(source: string, cls: UmlClass, name: string): string { - if (cls.interfaces.includes(name)) return source - const head = source.slice(cls.headerFrom, cls.headerTo) - const word = cls.kind === 'interface' ? 'extends' : 'implements' - const list = new RegExp(`\\b${word}\\s+[\\p{L}_$][\\p{L}\\p{N}_$<>,\\s]*?(?=\\s*$)`, 'u') - const changed = list.test(head) ? head.replace(list, (m) => `${m.replace(/\s+$/, '')}, ${name}`) : `${head.replace(/\s+$/, '')} ${word} ${name}${/\s$/.test(head) ? ' ' : ''}` - return splice(source, cls.headerFrom, cls.headerTo, changed) -} - -/** Interface aus `implements` (bzw. `extends` beim Interface) streichen; leere Liste fällt weg. */ -export function removeInterface(source: string, cls: UmlClass, name: string): string { - if (!cls.interfaces.includes(name)) return source - const head = source.slice(cls.headerFrom, cls.headerTo) - const word = cls.kind === 'interface' ? 'extends' : 'implements' - const list = new RegExp(`\\s*\\b${word}\\s+([\\p{L}_$][\\p{L}\\p{N}_$<>,\\s]*?)(?=\\s*$)`, 'u') - const changed = head.replace(list, (_m, names: string) => { - const rest = names.split(',').map((n) => n.trim()).filter((n) => n && n !== name) - return rest.length ? ` ${word} ${rest.join(', ')}` : '' - }) - return splice(source, cls.headerFrom, cls.headerTo, changed) -} - -/** Typ eines Attributs ändern, mit neuem Startwert (oder keinem). Modifizierer und Name bleiben. */ -export function setFieldType(source: string, member: UmlMember, type: string, init?: string): string { - if (member.kind !== 'field' || member.shared) return source - const head = source.slice(member.headerFrom, member.headerTo) - const typeAt = head.lastIndexOf(member.type, member.nameFrom - member.headerFrom) - if (typeAt < 0) return source - const newHead = head.slice(0, typeAt) + type + head.slice(typeAt + member.type.length) - return splice(source, member.headerFrom, member.to, `${newHead}${init ? ` = ${init}` : ''};`) -} - -/** Abstrakt an- oder ausschalten (nur bei Klassen). */ -export function setAbstract(source: string, cls: UmlClass, abstract: boolean): string { - const head = source.slice(cls.headerFrom, cls.headerTo) - const changed = abstract ? head.replace(/\bclass\b/, 'abstract class') : head.replace(/\babstract\s+/, '') - return splice(source, cls.headerFrom, cls.headerTo, changed) -} - -/** Modifier im Kopf der Klasse lesen (das Modell kennt nur `abstract`). */ -export function classModifiers(source: string, cls: UmlClass): { isPublic: boolean; isFinal: boolean } { - const head = source.slice(cls.headerFrom, cls.nameFrom) - return { isPublic: /\bpublic\b/.test(head), isFinal: /\bfinal\b/.test(head) } -} - -/** `public` setzen oder weglassen (dann nur im Package sichtbar). */ -export function setPublic(source: string, cls: UmlClass, on: boolean): string { - const head = source.slice(cls.headerFrom, cls.headerTo) - const has = /\bpublic\s+/.test(head.slice(0, cls.nameFrom - cls.headerFrom)) - const changed = on ? (has ? head : `public ${head}`) : head.replace(/\bpublic\s+/, '') - return splice(source, cls.headerFrom, cls.headerTo, changed) -} - -/** `final` setzen (nimmt `abstract` weg, beides zusammen geht in Java nicht) oder entfernen. */ -export function setFinal(source: string, cls: UmlClass, on: boolean): string { - const head = source.slice(cls.headerFrom, cls.headerTo) - const changed = on ? (/\bfinal\b/.test(head) ? head : head.replace(/\babstract\s+/, '').replace(/\bclass\b/, 'final class')) : head.replace(/\bfinal\s+/, '') - return splice(source, cls.headerFrom, cls.headerTo, changed) -} - -/** Neues Attribut (hinter dem letzten Attribut) oder neue Methode (vor der schließenden Klammer). */ -export function addMember(source: string, cls: UmlClass, text: string): string { - const line = parseUmlLine(text) - if (!line) return source - const indent = memberIndent(source, cls) - const header = javaHeader(line, cls, cls.kind === 'interface') - if (line.kind === 'field') { - const lastField = [...cls.members].reverse().find((m) => m.kind === 'field') - const at = lastField ? wholeLines(source, lastField.from, lastField.to)[1] : afterBrace(source, cls) - const prefix = source[at - 1] === '\n' ? '' : '\n' - return splice(source, at, at, `${prefix}${indent}${header};\n`) - } - let decl: string - if (cls.kind === 'interface') decl = `${indent}${header};\n` - else { - const type = line.type ?? 'void' - const isConstructor = line.name === cls.name - const body = !isConstructor && type !== 'void' ? `${indent} return ${defaultValue(type)};\n` : `${indent} // Dein Code\n` - decl = `${indent}${header} {\n${body}${indent}}\n` - } - const closeLine = lineStart(source, cls.bodyEnd) - if (!/^\s*$/.test(source.slice(closeLine, cls.bodyEnd))) return splice(source, cls.bodyEnd, cls.bodyEnd, '\n' + decl) - // Leerzeile vor der neuen Methode, außer direkt hinter `{` oder wenn schon eine da ist. - const before = source.slice(0, closeLine) - const blank = /\n[ \t]*\n$/.test(before) || /\{[ \t]*\n$/.test(before) ? '' : '\n' - return splice(source, closeLine, closeLine, blank + decl) -} - -/** Stelle hinter der `{` der Klasse (nächste Zeile, wenn die Klammer am Zeilenende steht). */ -function afterBrace(source: string, cls: UmlClass): number { - const brace = source.indexOf('{', cls.headerTo) + 1 - const rest = /^[ \t]*\n/.exec(source.slice(brace)) - return rest ? brace + rest[0].length : brace -} - -/** Mitglied neu schreiben; Rumpf und `= …` bleiben. Wechsel Attribut ↔ Methode: löschen und neu anlegen. */ -export function replaceMember(source: string, cls: UmlClass, member: UmlMember, text: string): string { - const line = parseUmlLine(text) - if (!line) return source - const wasField = member.kind === 'field' - if (wasField !== (line.kind === 'field')) { - const without = deleteMember(source, member) - const again = parseClasses(without).find((c) => c.name === cls.name) - return again ? addMember(without, again, text) : without - } - if (member.shared) return splice(source, member.nameFrom, member.nameTo, line.name) - line.visibility ??= member.visibility - if (line.kind === 'field') line.type ??= member.type - else if (member.kind !== 'constructor') line.type ??= member.type - const header = javaHeader(line, cls, member.isAbstract) - // Bei Attributen nur Kopf ohne `= …`; Modifizierer wie `final` behalten. - const old = source.slice(member.headerFrom, member.headerTo) - const keep = /\bfinal\s+/.test(old) && !/\bfinal\b/.test(header) ? header.replace(/^((?:public|private|protected)\s+)?/, '$1final ') : header - return splice(source, member.headerFrom, member.headerTo, keep) -} - -export function deleteMember(source: string, member: UmlMember): string { - if (member.shared) return source - const [from, to] = wholeLines(source, member.from, member.to) - return splice(source, from, to, '') -} - -// MARK: - Beispiel - -/** Start-Code wie im Spickzettel Java OOP & UML: Tier, Hund, Besitzer. */ -export const exampleJava = `public class Tier { - private String name; - private int alter; - - public Tier(String name, int alter) { - this.name = name; - this.alter = alter; - } - - public String getName() { - return name; - } - - public void lautGeben() { - System.out.println("..."); - } -} - -public class Hund extends Tier { - private String rasse; - - public Hund(String name, int alter, String rasse) { - super(name, alter); - this.rasse = rasse; - } - - public void lautGeben() { - System.out.println("Wuff"); - } -} - -public class Besitzer { - private String name; - private ArrayList hunde; - - public void gassiGehen() { - for (Hund h : hunde) { - h.lautGeben(); - } - } -} -` diff --git a/web/src/core/codeGenerator.ts b/web/src/core/codeGenerator.ts deleted file mode 100644 index a529c828..00000000 --- a/web/src/core/codeGenerator.ts +++ /dev/null @@ -1,1842 +0,0 @@ -/** - * Port von `VisuStructCodeGenerator.swift` und `CodeGeneration/*.swift`: - * Quellcode (Java, Python, JavaScript) aus dem Struktogramm. - */ -import { ElementType, type VSDocument, type VSElement } from './model' -import { parseSubprogramHeader, type SubprogramHeader } from './simulation' - -export type CodeTarget = 'java' | 'python' | 'javascript' - -export interface CodeOptions { - baseIndent: number - indentPerLevel: number - /** Diagramm-Texte zusätzlich als Kommentar (`/* … *\/` bzw. `""" … """`). */ - includeDiagramTextAsComments: boolean - /** `else` auch ausgeben, wenn der Nein-Zweig nur leer (ø) ist. */ - includeEmptyElseBranch: boolean - /** Kommentar über dem Code, wenn das Diagramm als Pseudocode markiert ist. */ - pseudocodeNote: string - /** Interner Heuristik-Kontext für JavaScript und Python (Ganzzahldivision). */ - jsIntVariables?: Set - /** Elementtyp deklarierter Arrays (`int[] zahlen` → `int`), für Eingaben in `zahlen[i]`. */ - arrayElementTypes?: Map - /** Text-Variablen: In Python ohne `str(…)` verketten. */ - stringVariables?: Set - /** Python ohne `match` (MicroPython auf dem mBot2 kennt es nicht): Fallauswahl als `if`/`elif`/`else`. */ - pythonWithoutMatch?: boolean - /** Nur für die Ansicht „Code live“: graue Platzhalter statt `true`/„empty element“, Zeilen je Element. */ - live?: LiveContext -} - -export const defaultCodeOptions: CodeOptions = { - baseIndent: 0, - indentPerLevel: 4, - includeDiagramTextAsComments: false, - includeEmptyElseBranch: true, - pseudocodeNote: 'Pseudocode – not a runnable program', -} - -/** - * `subprograms`: Unterprogramme mit Kopfzeile aus anderen Reitern. Python und JavaScript stellen ihre - * Definitionen vor das Hauptprogramm; in Java gehören sie als Methoden in die Klasse (`generateJavaClassFile`). - */ -export function generateCode( - document: VSDocument, - target: CodeTarget, - options: Partial = {}, - subprograms: VSDocument[] = [], -): string { - const opts = documentOptions(document, target, options) - let out = '' - // Schul-tauglich: Strict-Modus immer am Anfang des Snippets. - if (target === 'javascript') out += '"use strict";\n\n' - const all = [document, ...(target === 'java' ? [] : subprograms)] - if (target === 'python' && all.some(documentContainsJavaMathRandom)) out += 'import random\n\n' - if (target !== 'java') out += fileHelperClasses(all, target) - if (target === 'java' && documentContainsJavaInput(document)) { - // Snippet-Modus: Import als Kommentar (damit es auch beim Einfügen in `main` nicht „falsch“ steht). - out += '// import java.util.Scanner;\n' - out += 'Scanner scanner = new Scanner(System.in);\n\n' - } - // Nach Import/Scanner, damit die Java-Klasse den Vorspann weiterhin erkennt. - if (document.pseudocode) out += (target === 'python' ? '# ' : '// ') + opts.pseudocodeNote + '\n\n' - if (target !== 'java') { - for (const sub of subprograms) { - if (sub === document) continue - const header = parseSubprogramHeader(sub.caption) - if (header) out += subprogramDefinition(sub, header, target, options, 0) + '\n' - } - } - const header = parseSubprogramHeader(document.caption) - const sink = { out: '' } - if (header) sink.out = subprogramDefinition(document, header, target, options, opts.baseIndent) - else for (const el of document.elements) emit(el, target, opts, opts.baseIndent, sink) - out += sink.out - if (out.trim() === '') { - const empty = commentPrefix(target, true) - return target === 'javascript' ? '"use strict";\n\n' + empty : empty - } - return postProcessLengthSyntax(out.replace(/^[\n\r]+|[\n\r]+$/g, ''), target) -} - -function documentOptions(document: VSDocument, target: CodeTarget, options: Partial): CodeOptions { - const opts: CodeOptions = { - ...defaultCodeOptions, - ...options, - jsIntVariables: new Set(), - arrayElementTypes: inferArrayElementTypes(document), - stringVariables: inferStringVariables(document), - } - if (target !== 'java') opts.jsIntVariables = inferIntegerVariables(document) - return opts -} - -const javaTypeName = (type: SubprogramHeader['returnType']) => - type === 'string' ? 'String' : type === undefined ? 'void' : type - -function javaMethodHeader(header: SubprogramHeader): string { - const params = header.params.map((p) => `${javaTypeName(p.type)}${'[]'.repeat(p.dims ?? 0)} ${p.name}`).join(', ') - return `public static ${javaTypeName(header.returnType)} ${header.name}(${params}) {` -} - -/** Kopfzeile wie `int quadrat(int x)` als Java-Methode, Python-`def` oder JavaScript-`function`. */ -function subprogramDefinition( - document: VSDocument, - header: SubprogramHeader, - target: CodeTarget, - options: Partial, - indent: number, -): string { - const opts = documentOptions(document, target, options) - for (const p of header.params) { - if (p.dims) continue - if (p.type === 'int') opts.jsIntVariables!.add(p.name) - if (p.type === 'string') opts.stringVariables!.add(p.name) - } - const names = header.params.map((p) => p.name).join(', ') - const sink = { out: '' } - for (const el of document.elements) emit(el, target, opts, indent + opts.indentPerLevel, sink) - if (sink.out.trim() === '') sink.out = sp(indent + opts.indentPerLevel) + (target === 'python' ? 'pass\n' : '\n') - switch (target) { - case 'java': - return `${sp(indent)}${javaMethodHeader(header)}\n${sink.out}${sp(indent)}}\n` - case 'python': - return `${sp(indent)}def ${header.name}(${names}):\n${sink.out}` - case 'javascript': - return `${sp(indent)}function ${header.name}(${names}) {\n${sink.out}${sp(indent)}}\n` - } -} - -// MARK: - Hilfen - -type Sink = { out: string } - -const sp = (n: number) => (n > 0 ? ' '.repeat(n) : '') - -/** Wie Swift `regexGroups`: alle Gruppen, fehlende als "". */ -function groups(re: RegExp, s: string): string[] | undefined { - const m = re.exec(s) - return m ? Array.from(m, (g) => g ?? '') : undefined -} - -function commentPrefix(target: CodeTarget, forEmpty: boolean): string { - if (!forEmpty) return '' - return target === 'python' ? '# empty element' : '// empty element' -} - -const isDiagramEmptyPlaceholderText = (s: string) => s.trim() === 'ø' - -function emptyElementCommentLine(indent: number, target: CodeTarget): string { - return sp(indent) + (target === 'python' ? '# empty element\n' : '// empty element\n') -} - -function isElementVisuallyEmptyPlaceholderOnly(el: VSElement): boolean { - switch (el.type) { - case ElementType.leerElement: - return true - case ElementType.anweisung: - case ElementType.aufruf: - return el.textLines.map((l) => l.trim()).every((l) => l === '' || isDiagramEmptyPlaceholderText(l)) - default: - return false - } -} - -function isIfElseFalseBranchVisuallyEmpty(elements: VSElement[]): boolean { - return elements.every(isElementVisuallyEmptyPlaceholderOnly) -} - -function blockComment(lines: string[], target: CodeTarget, indent: number): string { - const trimmed = lines.map((l) => l.replace(/^[ \t]+|[ \t]+$/g, '')).filter((l) => l !== '') - if (trimmed.length === 0) return '' - if (target === 'python') { - return sp(indent) + '"""\n' + trimmed.map((t) => sp(indent) + t + '\n').join('') + sp(indent) + '"""\n' - } - if (trimmed.length === 1) return sp(indent) + '/* ' + trimmed[0] + ' */\n' - return sp(indent) + '/*\n' + trimmed.map((t) => sp(indent) + ' * ' + t + '\n').join('') + sp(indent) + ' */\n' -} - -const isCStyleSemicolonTarget = (t: CodeTarget) => t === 'java' || t === 'javascript' - -/** Semikolon-Heuristik für Java/JavaScript: ergänzt `;`, wenn sinnvoll und noch nicht vorhanden. */ -export function formatCStyleStatementLine(rawLine: string): string { - const line = rawLine.trim() - if (line === '') return '' - if (line.startsWith('//') || line.startsWith('/*') || line.startsWith('*') || line.startsWith('#')) return line - if (line.startsWith('{') || line.startsWith('}')) return line - if (line.endsWith('{')) return line - if (line.endsWith(';')) return line - const lower = line.toLowerCase() - for (const kw of ['if ', 'else', 'for ', 'while ', 'do ', 'switch ', 'case ', 'default']) { - if (lower.startsWith(kw)) return line - } - return line + ';' -} - -/** Bedingung/Ausdruck für Klammern: mehrzeilig mit Leerzeichen zusammenfassen. */ -function conditionText(lines: string[]): string { - const parts = lines.map((l) => l.trim()).filter((l) => l !== '') - return stripStructureKeywordPrefix(parts.length === 0 ? 'true' : parts.join(' ')) -} - -function stripStructureKeywordPrefix(text: string): string { - const trimmed = text.trim() - for (const keyword of ['if', 'while', 'until', 'switch']) { - const stripped = stripKeywordPrefix(keyword, trimmed) - if (stripped !== undefined) return stripped === '' ? 'true' : stripped - } - return trimmed -} - -function stripKeywordPrefix(keyword: string, text: string): string | undefined { - if (!text.toLowerCase().startsWith(keyword)) return undefined - const after = text.slice(keyword.length) - if (!(after === '' || /^\s/.test(after) || after[0] === '(')) return undefined - let content = after.trim() - if (content.startsWith('(') && content.endsWith(')') && content.length >= 2) content = content.slice(1, -1).trim() - return content -} - -function stripTrailingSemicolon(s: string): string { - let x = s.trim() - while (x.endsWith(';')) x = x.slice(0, -1).trim() - return x -} - -// MARK: - Ein-/Ausgabe - -/** Diagramm-Präfixe für Ausgabe: `output:` bzw. älteres `print:`. */ -function expressionAfterOutputDiagramPrefix(raw: string): string | undefined { - const t = raw.trim() - const lower = t.toLowerCase() - let expr: string - if (lower.startsWith('output:')) expr = t.slice(7).trim() - else if (lower.startsWith('print:')) expr = t.slice(6).trim() - else return undefined - return expr === '' ? undefined : expr -} - -interface InputLineParse { - type: string - name: string - /** Optionaler Eingabe-Hinweis in `"…"`. */ - customPrompt?: string -} - -const isIndexedTarget = (p: InputLineParse) => p.name.includes('[') && p.name.includes(']') - -/** `input:`-Rest: `int pin`, `int pin "Hinweis"`, Kurzform `pin "Hinweis"` (implizit `int`), bisher `zahl` / `int zahl`. */ -function parseInputLineAfterColon(rest: string): InputLineParse | undefined { - const trimmed = rest.trim() - if (trimmed === '') return undefined - const typed = groups( - /^\s*(integer|int|long|float|double|string|boolean|char|bool)\s+([a-zA-Z_]\w*(?:\s*\[[^\]]+\])?)(?:\s+"([^"]*)")?\s*$/i, - trimmed, - ) - if (typed) { - const name = typed[2].replaceAll(' ', '') - if (name === '') return undefined - const c = (typed[3] ?? '').trim() - return { type: normalizeInputTypeKeyword(typed[1]), name, customPrompt: c === '' ? undefined : c } - } - const quoted = groups(/^\s*([a-zA-Z_]\w*(?:\s*\[[^\]]+\])?)\s+"([^"]*)"\s*$/i, trimmed) - if (quoted) { - const target = quoted[1].replaceAll(' ', '') - const notName = ['int', 'integer', 'long', 'float', 'double', 'string', 'boolean', 'char', 'bool', 'void'] - if (notName.includes(target.toLowerCase())) return undefined - return { type: target.includes('[') ? 'String' : 'int', name: target, customPrompt: quoted[2].trim() } - } - const pair = parseInputTypeAndName(trimmed) - if (pair) return { type: normalizeInputTypeKeyword(pair[0]), name: pair[1] } - return undefined -} - -function normalizeInputTypeKeyword(raw: string): string { - switch (raw.trim().toLowerCase()) { - case 'integer': - case 'int': - return 'int' - case 'long': return 'long' - case 'float': return 'float' - case 'double': return 'double' - case 'string': return 'String' - case 'boolean': - case 'bool': - return 'boolean' - case 'char': return 'char' - default: return raw - } -} - -/** Nach `input:`: `int zahl` wie bisher, oder Kurzform `zahl` → Typ `int`. */ -function parseInputTypeAndName(rest: string): [string, string] | undefined { - const parts = rest.split(/[ \t]+/).filter(Boolean) - if (parts.length === 0) return undefined - if (parts.length >= 2) return [parts[0], parts[1]] - const incomplete = ['int', 'double', 'string', 'boolean', 'char', 'long', 'float', 'short', 'byte', 'void', 'bool', 'integer'] - if (incomplete.includes(parts[0].toLowerCase())) return undefined - return ['int', parts[0]] -} - -/** Ohne `output:`/`print:`-Präfix: Zeile beginnt mit `"…"` → Ausgabe. */ -function emitBareLeadingStringAsPrintIfNeeded( - raw: string, - target: CodeTarget, - indent: number, - intVariables: Set, - stringVariables: Set, -): string | undefined { - const t = stripTrailingSemicolon(raw).trim() - if (!t.startsWith('"')) return undefined - switch (target) { - case 'java': return sp(indent) + 'System.out.println(' + t + ');\n' - case 'javascript': return sp(indent) + 'console.log(' + t + ');\n' - case 'python': return sp(indent) + 'print(' + pythonConcatenation(t, intVariables, stringVariables) + ')\n' - } -} - -/** Zeichenkette für `"…"`-Literale (Java, JavaScript und Python escapen gleich). */ -function escapeForDoubleQuotedLiteral(s: string): string { - return s.replaceAll('\\', '\\\\').replaceAll('"', '\\"').replaceAll('\n', '\\n').replaceAll('\r', '\\r') -} - -function inputParseFrom(raw: string, arrayTypes: Map): InputLineParse | undefined { - const t = raw.trim() - if (!t.toLowerCase().startsWith('input:')) return undefined - const rest = t.slice(6).trim() - if (rest === '') return undefined - const p = parseInputLineAfterColon(rest) - // Kurzform `input: zahlen[i]`: Typ aus der Array-Deklaration statt immer `String`. - if (p && isIndexedTarget(p) && !/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s/i.test(rest)) { - const declared = arrayTypes.get(p.name.slice(0, p.name.indexOf('[')).trim()) - if (declared) return { ...p, type: declared } - } - return p -} - -function inferStringVariables(doc: VSDocument): Set { - const out = new Set() - walkElements(doc.elements, (e) => { - for (const raw of e.textLines) { - const t = raw.trim() - if (t.toLowerCase().startsWith('input:')) { - const p = parseInputLineAfterColon(t.slice(6).trim()) - if (p?.type === 'String' && !isIndexedTarget(p)) out.add(p.name) - continue - } - const g = groups(/^\s*(?:const\s+)?string\s+([a-zA-Z_]\w*)\s*=/i, t) - if (g) out.add(g[1]) - // String-Arrays als `name[]`, damit `spalten[i] + 1` in Python Text bleibt. - const a = groups(/^\s*string\s*\[\s*\]\s*([a-zA-Z_]\w*)\s*=/i, t) - if (a) out.add(a[1] + '[]') - } - }) - return out -} - -function inferArrayElementTypes(doc: VSDocument): Map { - const out = new Map() - walkElements(doc.elements, (e) => { - for (const raw of e.textLines) { - const g = - groups(/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s*\[\s*\]\s*([a-zA-Z_]\w*)/i, raw) ?? - groups(/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s+([a-zA-Z_]\w*)\s*\[\s*\]/i, raw) - if (g) out.set(g[2], normalizeInputTypeKeyword(g[1])) - } - }) - return out -} - -function emitJavaInputLineIfNeeded(raw: string, indent: number, arrayTypes: Map): string | undefined { - const p = inputParseFrom(raw, arrayTypes) - if (!p) return undefined - const promptText = p.customPrompt ? p.customPrompt : p.name + ': ' - const promptOut = sp(indent) + 'System.out.println("' + escapeForDoubleQuotedLiteral(promptText) + '");\n' - const decl = (type: string) => (isIndexedTarget(p) ? p.name : `${type} ${p.name}`) - switch (p.type) { - case 'int': return promptOut + sp(indent) + `${decl('int')} = scanner.nextInt();\n` - case 'double': return promptOut + sp(indent) + `${decl('double')} = scanner.nextDouble();\n` - // `next()` statt `nextLine()` (vermeidet die Newline-Falle nach `nextInt()`). - case 'String': return promptOut + sp(indent) + `${decl('String')} = scanner.next();\n` - case 'boolean': return promptOut + sp(indent) + `${decl('boolean')} = scanner.nextBoolean();\n` - default: return undefined - } -} - -function emitJavaScriptInputLineIfNeeded(raw: string, indent: number, arrayTypes: Map): string | undefined { - const p = inputParseFrom(raw, arrayTypes) - if (!p) return undefined - const useCustom = !!p.customPrompt - // `prompt` trägt den eigenen Hinweis; sonst zusätzlich `console.log`. - const hint = useCustom ? '' : sp(indent) + 'console.log("' + escapeForDoubleQuotedLiteral(p.name + ': ') + '");\n' - const promptExpr = (fallback: string) => { - const label = useCustom ? p.customPrompt! : p.name + ': ' - return `(prompt("${escapeForDoubleQuotedLiteral(label)}") ?? "${fallback}")` - } - const target = isIndexedTarget(p) ? p.name : `let ${p.name}` - switch (p.type) { - case 'int': return hint + sp(indent) + `${target} = parseInt(${promptExpr('0')}, 10);\n` - case 'double': return hint + sp(indent) + `${target} = Number(${promptExpr('0')});\n` - case 'String': return hint + sp(indent) + `${target} = ${promptExpr('')};\n` - case 'boolean': return hint + sp(indent) + `${target} = ${promptExpr('')}.trim().toLowerCase() === "true";\n` - default: return undefined - } -} - -function emitPythonInputLineIfNeeded(raw: string, indent: number, arrayTypes: Map): string | undefined { - const p = inputParseFrom(raw, arrayTypes) - if (!p) return undefined - const useCustom = !!p.customPrompt - const hint = useCustom ? '' : sp(indent) + 'print("' + escapeForDoubleQuotedLiteral(p.name + ': ') + '")\n' - const input = `input("${escapeForDoubleQuotedLiteral(useCustom ? p.customPrompt! : p.name + ': ')}")` - switch (p.type) { - case 'int': return hint + sp(indent) + `${p.name} = int(${input})\n` - case 'double': return hint + sp(indent) + `${p.name} = float(${input})\n` - case 'String': return hint + sp(indent) + `${p.name} = ${input}\n` - case 'boolean': return hint + sp(indent) + `${p.name} = ${input}.strip().lower() in ("true", "wahr")\n` - default: return undefined - } -} - -// MARK: - Dateien - -const fileName = String.raw`("(?:[^"\\]|\\.)*"|[A-Za-z_]\w*)` -const fileReaders: [RegExp, string][] = [ - [new RegExp(String.raw`^new\s+Scanner\s*\(\s*new\s+File\s*\(\s*${fileName}\s*\)\s*\)$`), 'Scanner'], - [new RegExp(String.raw`^new\s+BufferedReader\s*\(\s*new\s+FileReader\s*\(\s*${fileName}\s*\)\s*\)$`), 'BufferedReader'], - [new RegExp(String.raw`^new\s+PrintWriter\s*\(\s*(?:new\s+(?:File|FileWriter)\s*\(\s*)?${fileName}\s*\)?\s*\)$`), 'PrintWriter'], -] - -/** `new Scanner(new File("a.txt"))` → `Scanner`, `"a.txt"`; ebenso `BufferedReader` und `PrintWriter`. */ -export function fileConstructor(rhs: string): { kind: 'Scanner' | 'BufferedReader' | 'PrintWriter'; name: string } | undefined { - const t = rhs.trim() - for (const [re, kind] of fileReaders) { - const m = re.exec(t) - if (m) return { kind: kind as 'Scanner', name: m[1] } - } - return undefined -} - -/** - * Python und JavaScript kennen `Scanner`, `BufferedReader` und `PrintWriter` nicht; der erzeugte Code bekommt kleine - * Klassen mit denselben Methodennamen (siehe `fileHelperClasses`). Hier: Anlegen, `println`/`print` und `Files.writeString`. - */ -function emitFileLineIfNeeded( - raw: string, - target: CodeTarget, - indent: number, - intVariables: Set, - stringVariables: Set, -): string | undefined { - if (target === 'java') return undefined - const t = stripTrailingSemicolon(raw).trim() - const decl = /^(?:(?:Scanner|BufferedReader|PrintWriter)\s+)?([A-Za-z_]\w*)\s*=\s*(new\s[\s\S]+)$/.exec(t) - const made = decl && fileConstructor(decl[2]) - if (decl && made) { - const declared = /^(?:Scanner|BufferedReader|PrintWriter)\s/.test(t) - return target === 'python' - ? sp(indent) + `${decl[1]} = ${made.kind}(${made.name})\n` - : sp(indent) + `${declared ? 'let ' : ''}${decl[1]} = new ${made.kind}(${made.name});\n` - } - const write = /^([A-Za-z_]\w*)\.(println|print)\s*\(([\s\S]*)\)$/.exec(t) - if (write && write[1] !== 'System') { - const expr = write[3].trim() - const value = target === 'python' && expr !== '' ? pythonConcatenation(expr, intVariables, stringVariables) : expr - return sp(indent) + `${write[1]}.${write[2]}(${value})${target === 'javascript' ? ';' : ''}\n` - } - const files = /^Files\.writeString\s*\(\s*(?:Path\.of|Paths\.get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*,\s*([\s\S]+)\)$/.exec(t) - if (files) { - const text = files[2].trim() - return target === 'python' - ? sp(indent) + `open(${files[1]}, "w", encoding="utf-8").write(${pythonConcatenation(text, intVariables, stringVariables)})\n` - : sp(indent) + `require("fs").writeFileSync(${files[1]}, ${text});\n` - } - return undefined -} - -/** Welche Datei-Klassen ein Diagramm anlegt. */ -function usedFileClasses(docs: VSDocument[]): Set<'Scanner' | 'BufferedReader' | 'PrintWriter'> { - const used = new Set<'Scanner' | 'BufferedReader' | 'PrintWriter'>() - for (const doc of docs) { - walkElements(doc.elements, (e) => { - for (const l of e.textLines) { - const m = /=\s*(new\s.+?);?\s*$/.exec(l) - const made = m && fileConstructor(m[1]) - if (made) used.add(made.kind) - } - }) - } - return used -} - -const pythonFileHelpers: Record = { - Scanner: `class Scanner: - def __init__(self, name): - self.lines = open(name, encoding="utf-8").read().splitlines() - def hasNextLine(self): - return len(self.lines) > 0 - def nextLine(self): - return self.lines.pop(0) - def close(self): - pass -`, - BufferedReader: `class BufferedReader: - def __init__(self, name): - self.lines = open(name, encoding="utf-8").read().splitlines() - def readLine(self): - return self.lines.pop(0) if self.lines else None - def close(self): - pass -`, - PrintWriter: `class PrintWriter: - def __init__(self, name): - self.file = open(name, "w", encoding="utf-8") - def println(self, text=""): - self.file.write(str(text) + "\\n") - def print(self, text): - self.file.write(str(text)) - def close(self): - self.file.close() -`, -} - -const javaScriptFileHelpers: Record = { - Scanner: `class Scanner { - constructor(name) { this.lines = require("fs").readFileSync(name, "utf8").split(/\\r?\\n/); if (this.lines.at(-1) === "") this.lines.pop(); } - hasNextLine() { return this.lines.length > 0; } - nextLine() { return this.lines.shift(); } - close() {} -} -`, - BufferedReader: `class BufferedReader { - constructor(name) { this.lines = require("fs").readFileSync(name, "utf8").split(/\\r?\\n/); if (this.lines.at(-1) === "") this.lines.pop(); } - readLine() { return this.lines.length > 0 ? this.lines.shift() : null; } - close() {} -} -`, - PrintWriter: `class PrintWriter { - constructor(name) { this.name = name; this.text = ""; } - println(text = "") { this.text += text + "\\n"; } - print(text) { this.text += text; } - close() { require("fs").writeFileSync(this.name, this.text); } -} -`, -} - -/** Kleine Klassen für Python und JavaScript, die wie Javas `Scanner`, `BufferedReader` und `PrintWriter` heißen. */ -function fileHelperClasses(docs: VSDocument[], target: 'python' | 'javascript'): string { - const used = usedFileClasses(docs) - if (used.size === 0) return '' - const helpers = target === 'python' ? pythonFileHelpers : javaScriptFileHelpers - return [...used].map((k) => helpers[k]).join('\n') + '\n' -} - -function emitPrintLineIfNeeded( - raw: string, - target: CodeTarget, - indent: number, - intVariables: Set, - stringVariables: Set, -): string | undefined { - // `System.out.print(…)`: Ausgabe ohne neue Zeile. - const print = /^System\s*\.\s*out\s*\.\s*print\s*\(([\s\S]*)\)$/.exec(stripTrailingSemicolon(raw).trim()) - if (print) { - const value = print[1].trim() - switch (target) { - case 'java': return sp(indent) + 'System.out.print(' + value + ');\n' - case 'javascript': return sp(indent) + 'process.stdout.write(String(' + value + '));\n' - case 'python': return sp(indent) + 'print(' + (value === '' ? '' : pythonConcatenation(value, intVariables, stringVariables) + ', ') + 'end="")\n' - } - } - const expr = expressionAfterOutputDiagramPrefix(raw) - if (expr === undefined) { - // Leere Ausgabe (`output:` ohne Text): eine Leerzeile. - if (!/^\s*(?:output|print):\s*$/i.test(raw)) return undefined - return sp(indent) + { java: 'System.out.println();', javascript: 'console.log();', python: 'print()' }[target] + '\n' - } - switch (target) { - case 'java': return sp(indent) + 'System.out.println(' + expr + ');\n' - case 'javascript': return sp(indent) + 'console.log(' + expr + ');\n' - case 'python': return sp(indent) + 'print(' + pythonConcatenation(expr, intVariables, stringVariables) + ')\n' - } -} - -// MARK: - Java-ähnliche Zeilen - -/** Kopf einer C-`for`-Schleife: mehrere Felder mit `; `. Eine Zeile bleibt unverändert. */ -function cStyleForLoopHeaderText(lines: string[]): string { - const parts = lines.map(stripTrailingSemicolon).filter((p) => p !== '') - if (parts.length === 0) return ';;' - return parts.join('; ') -} - -function javaScriptForLoopHeaderText(lines: string[]): string { - const header = cStyleForLoopHeaderText(lines) - const parts = header.split(';') - const translated = javaScriptForInitializerFromJavaTypedDeclaration(parts[0]) - if (translated !== undefined) parts[0] = translated - return parts.join(';') -} - -function javaScriptForInitializerFromJavaTypedDeclaration(raw: string): string | undefined { - const g = groups(/^(\s*)(?:integer|int|long|float|double|string|boolean|char|bool)\s+(\w+)(?:\s*=\s*(.*))?\s*$/i, raw) - if (!g) return undefined - const name = g[2].trim() - if (name === '') return undefined - const rhs = (g[3] ?? '').trim() - if (raw.includes('=') && rhs !== '') return g[1] + `let ${name} = ${rhs}` - return g[1] + `let ${name}` -} - -/** Felder einer `for`-Schleife: drei Zeilen oder eine Zeile `int i = 0; i < n; i++` (auch mit `for (…)`). */ -function forLoopFields(lines: string[]): [string, string, string] | undefined { - const raw = lines.map((l) => stripTrailingSemicolon(l).trim()).filter((l) => l !== '') - let parts = raw - if (raw.length === 1) { - let one = raw[0] - const kw = stripKeywordPrefix('for', one) - if (kw !== undefined) one = kw - parts = one.split(';').map((p) => p.trim()) - } - if (parts.length !== 3 || parts.some((p) => p === '')) return undefined - return [parts[0], parts[1], parts[2]] -} - -interface PythonForLoop { - /** `for i in range(…):` – oder `undefined`, wenn nur eine `while`-Schleife passt. */ - rangeLine?: string - variable: string - init: string - condition: string - increment: string -} - -/** Schrittweite aus `i++`, `i--`, `i += 2`, `i -= 2`, `i = i + 2`, `i = i - 2`. */ -function forLoopStepSize(s: string, name: string): number | undefined { - let g = groups(/^\s*(\w+)\s*(\+\+|--)\s*$/, s) ?? groups(/^\s*(\+\+|--)\s*(\w+)\s*$/, s) - if (g) { - const [v, op] = g[1] === '++' || g[1] === '--' ? [g[2], g[1]] : [g[1], g[2]] - return v === name ? (op === '++' ? 1 : -1) : undefined - } - g = groups(/^\s*(\w+)\s*(\+|-)=\s*(\d+)\s*$/, s) - if (g) return g[1] === name ? (g[2] === '+' ? 1 : -1) * Number(g[3]) : undefined - g = groups(/^\s*(\w+)\s*=\s*(\w+)\s*(\+|-)\s*(\d+)\s*$/, s) - if (g) return g[1] === name && g[2] === name ? (g[3] === '+' ? 1 : -1) * Number(g[4]) : undefined - return undefined -} - -/** `n` → `n + 1`, `10` → `11`. */ -function shiftedBound(bound: string, by: number): string { - if (/^-?\d+$/.test(bound)) return String(Number(bound) + by) - const simple = /^[\w.()[\]]+$/.test(bound) ? bound : `(${bound})` - return by > 0 ? `${simple} + ${by}` : `${simple} - ${-by}` -} - -/** Zählschleife für Python: `range(…)`, wenn Kopf und Schritt passen; sonst Bausteine für `while`. */ -function pythonForLoop(lines: string[], intVariables: Set): PythonForLoop | undefined { - const fields = forLoopFields(lines) - if (!fields) return undefined - const [initS, condS, stepS] = fields - const init = groups(/^\s*(?:integer|int|long|short|byte|double|float)?\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i, initS) - if (!init) return undefined - const name = init[1] - const loop: PythonForLoop = { variable: name, init: `${name} = ${pythonExpression(init[2], intVariables)}`, condition: condS, increment: stepS } - const cond = groups(/^\s*(\w+)\s*(<=|<|>=|>)\s*(.+)$/, condS) - const step = forLoopStepSize(stepS, name) - if (!cond || cond[1] !== name || step === undefined || step === 0) return loop - const start = pythonExpression(init[2], intVariables) - let stop = pythonExpression(cond[3], intVariables) - const op = cond[2] - if (step > 0 && op === '<=') stop = shiftedBound(stop, 1) - else if (step < 0 && op === '>=') stop = shiftedBound(stop, -1) - else if (!(step > 0 && op === '<') && !(step < 0 && op === '>')) return loop - const args = step === 1 ? (start === '0' ? [stop] : [start, stop]) : [start, stop, String(step)] - loop.rangeLine = `for ${name} in range(${args.join(', ')}):` - return loop -} - -interface ConstTypedLineParse { - rawType: string - name: string - rhs?: string -} - -/** `const int PIN` bzw. `const double PI = 3.14`. */ -function parseConstTypedLine(rawLine: string): ConstTypedLineParse | undefined { - const line = stripTrailingSemicolon(rawLine).trim() - if (line === '') return undefined - const g = groups(/^\s*const\s+(integer|int|long|float|double|string|boolean|char|bool)\s+(\w+)(?:\s*=\s*(.*))?$/i, line) - if (!g) return undefined - const name = g[2].trim() - if (name === '') return undefined - if (!line.includes('=')) return { rawType: g[1].trim(), name } - const rhs = (g[3] ?? '').trim() - if (rhs === '') return undefined - return { rawType: g[1].trim(), name, rhs } -} - -function emitConstTypedLineIfNeeded( - raw: string, - target: CodeTarget, - indent: number, - intVariables: Set, -): string | undefined { - const p = parseConstTypedLine(raw) - if (!p) return undefined - const jt = normalizeInputTypeKeyword(p.rawType) - const r = p.rhs?.trim() - switch (target) { - case 'java': - return sp(indent) + formatCStyleStatementLine(r ? `final ${jt} ${p.name} = ${r}` : `final ${jt} ${p.name}`) + '\n' - case 'javascript': - if (r) return sp(indent) + formatCStyleStatementLine(`const ${p.name} = ${r}`) + '\n' - // `const` verlangt in JS einen Initialwert. - return sp(indent) + `let ${p.name}; // from diagram: const ${jt} ${p.name}\n` - case 'python': - if (r) return sp(indent) + `${p.name} = ${pythonRhsFromJavaRhs(r, intVariables)} # const ${jt}\n` - return sp(indent) + `${p.name} = None # const ${jt}, uninitialized\n` - } -} - -/** Eine Zeile `Typ name = ausdruck` (Schulsyntax). */ -function parseJavaStyleSimpleAssignment(rawLine: string): { name: string; rhs: string; isArray: boolean } | undefined { - const line = stripTrailingSemicolon(rawLine).trim() - if (line === '') return undefined - if (parseGridDeclaration(line)) return undefined - // `integer` vor `int`, damit nicht nur der Präfix `int` von `integer` passt. - const g = groups( - /^\s*(?:integer|int|long|float|double|string|boolean|char|bool)(?:\s*\[\s*\]\s+(\w+)|\s+(\w+)(\s*\[\s*\])?)\s*=\s*(.*)$/i, - line, - ) - if (!g) return undefined - const name = g[1] === '' ? g[2] : g[1] - const rhs = g[4].trim() - if (rhs === '') return undefined - return { name, rhs, isArray: g[1] !== '' || g[3] !== '' } -} - -function javaIntCastExpression(rhs: string): string | undefined { - const g = groups(/^\s*\(\s*int(?:eger)?\s*\)\s*(.+?)\s*$/i, rhs) - const e = g?.[1].trim() - // `(int) wort.charAt(i)` liefert den Zeichencode, das übernimmt `convertCharCasts`. - if (!e || isCharOperand(e)) return undefined - return e.startsWith('(') && matchingClose(e, 0) === e.length - 1 ? e.slice(1, -1).trim() : e -} - -/** Index der schließenden Klammer zu `s[open]` (rund oder eckig), sonst -1. */ -function matchingClose(s: string, open: number): number { - let depth = 0 - for (let i = open; i < s.length; i++) { - if (s[i] === '(' || s[i] === '[') depth++ - else if ((s[i] === ')' || s[i] === ']') && --depth === 0) return i - } - return -1 -} - -/** Ein einzelnes Zeichen: `'A'` oder `wort.charAt(i)`. */ -const isCharOperand = (e: string) => /^'.'$/u.test(e) || /\.charAt\(.*\)$/.test(e) - -/** Ende des Operanden ab `start`: `(…)`, `'A'`, eine Zahl oder ein Name mit `.methode(…)` und `[…]`; sonst -1. */ -function operandEnd(s: string, start: number): number { - if (s[start] === '(') { - const close = matchingClose(s, start) - return close < 0 ? -1 : close + 1 - } - if (s[start] === "'") return s[start + 2] === "'" ? start + 3 : -1 - const head = /^(?:[A-Za-z_]\w*|\d+)/.exec(s.slice(start)) - if (!head) return -1 - let i = start + head[0].length - for (;;) { - const member = /^\.[A-Za-z_]\w*/.exec(s.slice(i)) - if (member) i += member[0].length - else if (s[i] === '(' || s[i] === '[') { - const close = matchingClose(s, i) - if (close < 0) return -1 - i = close + 1 - } else return i - } -} - -/** - * Umwandlungen zwischen Zeichen und Zeichencode wie in Java: `(int) wort.charAt(i)` → Python `ord(…)`, - * JavaScript `….charCodeAt(0)`; `(char) code` → Python `chr(code)`, JavaScript `String.fromCharCode(code)`. - */ -function convertCharCasts(s: string, target: 'python' | 'javascript'): string { - return mapOutsideStringLiterals(s, (code) => { - const re = /\(\s*(int|char)\s*\)\s*/g - let out = '' - let last = 0 - for (let m = re.exec(code); m; m = re.exec(code)) { - const start = m.index + m[0].length - const end = operandEnd(code, start) - if (end < 0) continue - const operand = code.slice(start, end) - if (m[1] === 'int' && !isCharOperand(operand)) continue - const inner = convertCharCasts(operand.startsWith('(') ? operand.slice(1, -1).trim() : operand, target) - const replacement = m[1] === 'int' - ? (target === 'python' ? `ord(${inner})` : `${inner}.charCodeAt(0)`) - : (target === 'python' ? `chr(${inner})` : `String.fromCharCode(${inner})`) - out += code.slice(last, m.index) + replacement - last = end - re.lastIndex = end - } - return out + code.slice(last) - }) -} - -function pythonRhsFromJavaRhs(rhs: string, intVariables: Set = new Set()): string { - const t = rhs.trim() - if (t === 'null') return 'None' - const cast = javaIntCastExpression(t) - if (cast !== undefined) return `int(${pythonRhsFromJavaRhs(cast, intVariables)})` - return pythonExpression(t, intVariables) -} - -/** Teile außerhalb von `"…"`-Literalen umwandeln, Literale unverändert lassen. */ -function mapOutsideStringLiterals(s: string, f: (code: string) => string): string { - let out = '' - let code = '' - let i = 0 - while (i < s.length) { - if (s[i] !== '"') { - code += s[i++] - continue - } - out += f(code) - code = '' - let j = i + 1 - while (j < s.length && s[j] !== '"') j += s[j] === '\\' ? 2 : 1 - out += s.slice(i, j + 1) - i = j + 1 - } - return out + f(code) -} - -/** - * Java-Schreibweise in einem Ausdruck nach Python: `&&` → `and`, `||` → `or`, `!` → `not`, `true` → `True`, - * `a.equals(b)` → `a == b`, `Math.random()` → `random.random()`, `/` zwischen ganzen Zahlen → `//`. - */ -function pythonExpression(expression: string, intVariables: Set): string { - const withEquals = expression.replace(/\.equals\(\s*("(?:[^"\\]|\\.)*"|[^()"]*?)\s*\)/g, ' == $1') - const isInt = (operand: string) => /^\d+$/.test(operand) || intVariables.has(operand) - return mapOutsideStringLiterals(withEquals, (code) => - code - .replace(/\bMath\.random\s*\(\s*\)/gi, 'random.random()') - .replace(/===/g, '==') - .replace(/!==/g, '!=') - .replace(/\s*&&\s*/g, ' and ') - .replace(/\s*\|\|\s*/g, ' or ') - .replace(/!(?!=)\s*/g, 'not ') - .replace(/\btrue\b/g, 'True') - .replace(/\bfalse\b/g, 'False') - .replace(/\bnull\b/g, 'None') - .replace(/(? - isInt(a) && isInt(b) ? `${a} // ${b}` : m, - ), - ) -} - -/** Teile einer Verkettung mit `+` außerhalb von Klammern und Strings. */ -function splitTopLevelPlus(expression: string): string[] { - const parts: string[] = [] - let depth = 0 - let current = '' - let inString = false - for (let i = 0; i < expression.length; i++) { - const c = expression[i] - if (inString) { - current += c - if (c === '\\' && i + 1 < expression.length) current += expression[++i] - else if (c === '"') inString = false - continue - } - if (c === '"') inString = true - else if (c === '(' || c === '[') depth++ - else if (c === ')' || c === ']') depth-- - else if (c === '+' && depth === 0) { - parts.push(current.trim()) - current = '' - continue - } - current += c - } - parts.push(current.trim()) - return parts -} - -const isDoubleQuotedLiteral = (s: string) => /^"(?:[^"\\]|\\.)*"$/.test(s) - -/** `"Summe: " + summe` → `"Summe: " + str(summe)`: Python verkettet Text nicht mit Zahlen. Sonst nur übersetzt. */ -function pythonConcatenation(expression: string, intVariables: Set, stringVariables: Set): string { - const parts = splitTopLevelPlus(expression) - const isText = (p: string) => - isDoubleQuotedLiteral(p) || stringVariables.has(p) || stringVariables.has(p.replace(/^([A-Za-z_]\w*)\[[^[\]]*\]$/u, '$1[]')) - if (parts.length < 2 || !parts.some(isText) || parts.some((p) => p === '')) { - return pythonExpression(expression, intVariables) - } - return parts.map((p) => (isText(p) ? p : `str(${pythonExpression(p, intVariables)})`)).join(' + ') -} - -/** Python: `i++` → `i += 1`, `x /= 2` → `x //= 2` (ganze Zahl), sonst der Ausdruck übersetzt. */ -function pythonStatementLine(raw: string, intVariables: Set, stringVariables: Set = new Set()): string { - const t = stripTrailingSemicolon(raw).trim() - const inc = groups(/^(\w+(?:\[[^\]]*\])?)\s*(\+\+|--)$/, t) ?? groups(/^(\+\+|--)\s*(\w+(?:\[[^\]]*\])?)$/, t) - if (inc) { - const [target, op] = inc[1] === '++' || inc[1] === '--' ? [inc[2], inc[1]] : [inc[1], inc[2]] - return `${pythonExpression(target, intVariables)} ${op === '++' ? '+=' : '-='} 1` - } - const div = groups(/^(\w+)\s*\/=\s*(.+)$/, t) - if (div && intVariables.has(div[1])) return `${div[1]} //= ${pythonExpression(div[2], intVariables)}` - const assign = groups(/^([\w[\]. ]+?)\s*(=|\+=|-=|\*=|\/=|%=)\s*(.+)$/, t) - if (assign && !assign[3].startsWith('=')) { - const [, target, op, rhs] = assign - let value: string - if (op === '+=' && stringVariables.has(target.trim())) { - const parts = splitTopLevelPlus(rhs) - value = pythonConcatenation(`"" + ${rhs}`, intVariables, stringVariables).replace(/^"" \+ /, '') - if (parts.length === 1 && !isDoubleQuotedLiteral(rhs.trim()) && !stringVariables.has(rhs.trim())) { - value = `str(${pythonExpression(rhs, intVariables)})` - } - } else if (op === '=' && javaIntCastExpression(rhs) === undefined && rhs.trim() !== 'null') { - value = pythonConcatenation(rhs, intVariables, stringVariables) - } else { - value = op === '=' ? pythonRhsFromJavaRhs(rhs, intVariables) : pythonExpression(rhs, intVariables) - } - return `${target} ${op} ${value}` - } - return pythonExpression(t, intVariables) -} - -const normalizeJavaScriptMathRandomCall = (s: string) => s.replace(/\bMath\.random\b(?!\s*\()/gi, 'Math.random()') - -function arrayLiteralFromJavaInitializer(rhs: string): string { - const t = rhs.trim() - return t.startsWith('{') && t.endsWith('}') ? '[' + t.slice(1, -1) + ']' : rhs -} - -function javaNewArraySizeExpression(rhs: string): string | undefined { - const g = groups(/^\s*new\s+(?:byte|short|int|long|float|double|boolean|char|string)\s*\[\s*(.+)\s*\]\s*$/i, rhs) - const size = g?.[1].trim() - return size ? size : undefined -} - -interface GridDeclaration { - elementType: string - name: string - rhs: string -} - -/** `boolean[][] sitze = …` (2D-Array). */ -function parseGridDeclaration(line: string): GridDeclaration | undefined { - const g = groups(/^\s*(int|double|string|boolean)\s*\[\s*\]\s*\[\s*\]\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i, stripTrailingSemicolon(line).trim()) - return g ? { elementType: g[1].toLowerCase(), name: g[2], rhs: g[3].trim() } : undefined -} - -/** `new boolean[3][4]` → `['3', '4']`. */ -function gridSizeExpressions(rhs: string): [string, string] | undefined { - const g = groups(/^\s*new\s+(?:int|double|string|boolean)\s*\[\s*(.+?)\s*\]\s*\[\s*(.+?)\s*\]\s*$/i, rhs) - return g ? [g[1], g[2]] : undefined -} - -const nestedArrayLiteral = (rhs: string) => rhs.replaceAll('{', '[').replaceAll('}', ']') - -function gridDefault(elementType: string, target: 'python' | 'javascript'): string { - if (elementType === 'boolean') return target === 'python' ? 'False' : 'false' - if (elementType === 'string') return '""' - if (elementType === 'double') return '0.0' - return '0' -} - -function gridRhs(d: GridDeclaration, target: 'python' | 'javascript', intVariables: Set): string { - const size = gridSizeExpressions(d.rhs) - if (target === 'javascript') { - return size - ? `Array.from({ length: ${size[0]} }, () => new Array(${size[1]}).fill(${gridDefault(d.elementType, target)}))` - : nestedArrayLiteral(d.rhs) - } - return size - ? `[[${gridDefault(d.elementType, target)}] * ${pythonExpression(size[1], intVariables)} for _ in range(${pythonExpression(size[0], intVariables)})]` - : pythonExpression(nestedArrayLiteral(d.rhs), intVariables) -} - -function javaScriptRhsFromJavaRhs(rhs: string, isArray: boolean): string { - const t = rhs.trim() - if (isArray) { - const size = javaNewArraySizeExpression(t) - return size !== undefined ? `new Array(${size})` : arrayLiteralFromJavaInitializer(t) - } - const cast = javaIntCastExpression(t) - if (cast !== undefined) return `Math.floor(${javaScriptRhsFromJavaRhs(cast, false)})` - return normalizeJavaScriptMathRandomCall(t) -} - -function pythonRhsFromJavaRhsArray(rhs: string, isArray: boolean, intVariables: Set): string { - const t = rhs.trim() - if (isArray) { - const size = javaNewArraySizeExpression(t) - return size !== undefined ? `[None] * ${size}` : pythonExpression(arrayLiteralFromJavaInitializer(t), intVariables) - } - return pythonRhsFromJavaRhs(t, intVariables) -} - -/** JavaScript: `int x = 5` → `let x = 5;`. Python: `x = 5`. Java: allgemeiner Pfad. */ -function emitTranslatedJavaLikeAssignmentIfNeeded( - raw: string, - target: CodeTarget, - indent: number, - intVariables: Set, - stringVariables: Set, -): string | undefined { - if (target === 'java') return undefined - const grid = parseGridDeclaration(raw) - if (grid) { - const value = gridRhs(grid, target, intVariables) - return target === 'javascript' ? sp(indent) + `let ${grid.name} = ${value};\n` : sp(indent) + `${grid.name} = ${value}\n` - } - const p = parseJavaStyleSimpleAssignment(raw) - if (!p) return undefined - if (target === 'javascript') { - return sp(indent) + formatCStyleStatementLine(`let ${p.name} = ${javaScriptRhsFromJavaRhs(p.rhs, p.isArray)}`) + '\n' - } - const plain = !p.isArray && javaIntCastExpression(p.rhs) === undefined && p.rhs.trim() !== 'null' - const value = plain - ? pythonConcatenation(p.rhs, intVariables, stringVariables) - : pythonRhsFromJavaRhsArray(p.rhs, p.isArray, intVariables) - return sp(indent) + `${p.name} = ${value}\n` -} - -function walkElements(elements: VSElement[], visit: (e: VSElement) => void) { - for (const e of elements) { - visit(e) - if (e.loopBody) walkElements(e.loopBody, visit) - for (const c of e.cases ?? []) walkElements(c.elements, visit) - } -} - -function inferIntegerVariables(doc: VSDocument): Set { - const isIntegerType = (t: string) => ['int', 'integer', 'long', 'short', 'byte'].includes(t.trim().toLowerCase()) - const out = new Set() - walkElements(doc.elements, (e) => { - for (const raw of e.textLines) { - const t = raw.trim() - if (t === '') continue - if (t.toLowerCase().startsWith('input:')) { - const p = parseInputLineAfterColon(t.slice(6).trim()) - // Array-Ziele ignorieren (z. B. a[i]). - if (p && isIntegerType(p.type) && !p.name.includes('[')) out.add(p.name) - continue - } - const c = parseConstTypedLine(t) - if (c && isIntegerType(c.rawType)) { - out.add(c.name) - continue - } - const g = groups(/^\s*(integer|int|long|short|byte)\s+([a-zA-Z_]\w*)\s*=/i, t) - if (g) out.add(g[2].trim()) - } - }) - return out -} - -/** Wie in Java: `/` zwischen ganzen Zahlen schneidet in JavaScript mit `Math.trunc` ab, auch bei `x /= y`. */ -function javaScriptIntegerDivision(code: string, intVariables: Set): string { - const isInt = (operand: string) => /^\d+$/.test(operand) || intVariables.has(operand) - // Ganze Ketten wie `x * x / 2` von links nach rechts auswerten: In Java ist das `(x * x) / 2`, nicht `x * (x / 2)`. - const operand = String.raw`(?:[a-zA-Z_]\w*|\d+(?:\.\d+)?)` - const chain = new RegExp(String.raw`(? { - const tokens = m.split(/\s*([*/%])\s*/) - if (!tokens.some((t) => t === '/')) return m - let result = tokens[0] - let resultIsInt = isInt(tokens[0]) - for (let i = 1; i < tokens.length; i += 2) { - const op = tokens[i] - const next = tokens[i + 1] - if (op === '/' && resultIsInt && isInt(next)) result = `Math.trunc(${result} / ${next})` - else result = `${result} ${op} ${next}` - resultIsInt = resultIsInt && isInt(next) - } - return result - } - return mapOutsideStringLiterals(code, (part) => - part - .replace(chain, foldChain) - .replace(/(? - isInt(a) && isInt(b) ? `${a} = Math.trunc(${a} / ${b})` : m, - ), - ) -} - -function documentContainsJavaInput(doc: VSDocument): boolean { - let found = false - walkElements(doc.elements, (e) => { - if (e.type === ElementType.anweisung && e.textLines.some((l) => l.trim().toLowerCase().startsWith('input:'))) found = true - }) - return found -} - -const arraysImport = 'import java.util.Arrays;' - -/** - * Imports für Dateien: `Files`/`Path` aus `java.nio.file`, `File`, `PrintWriter`, `FileReader` und `BufferedReader` aus - * `java.io`, dazu `java.util.Scanner` für einen Scanner auf einer Datei und `java.util.Arrays` für `Arrays.toString(…)`. - */ -export function fileImports(doc: VSDocument): string[] { - let nio = false - let io = false - let scanner = false - let arrays = false - walkElements(doc.elements, (e) => { - for (const l of e.textLines) { - if (/\bArrays\s*\./.test(l)) arrays = true - if (/\bFiles\s*\./.test(l)) nio = true - if (/\bnew\s+(?:File|PrintWriter|FileReader|FileWriter|BufferedReader)\s*\(/.test(l)) io = true - if (/\bnew\s+Scanner\s*\(\s*new\s+File\b/.test(l)) scanner = true - } - }) - return [...(io ? ['import java.io.*;'] : []), ...(nio ? ['import java.nio.file.*;'] : []), ...(scanner ? ['import java.util.Scanner;'] : []), ...(arrays ? [arraysImport] : [])] -} - -/** Arbeitet das Programm mit Dateien? Dann kann eine IOException kommen, und Java verlangt `throws` an der Methode. */ -export function documentReadsFiles(doc: VSDocument): boolean { - return fileImports(doc).some((i) => i !== arraysImport) -} - -function documentContainsJavaMathRandom(doc: VSDocument): boolean { - let found = false - walkElements(doc.elements, (e) => { - if (e.textLines.some((l) => /\bMath\.random\s*\(/i.test(l))) found = true - }) - return found -} - -function caseLabelToken(name: string, target: CodeTarget): string { - const t = name.trim() - if (t === '') return target === 'python' ? '""' : '0' - if (target === 'java' || /^\p{N}+$/u.test(t) || isDoubleQuotedLiteral(t)) return t - return '"' + t.replaceAll('"', '\\"') + '"' -} - -function forcedCaseComment(name: string, target: CodeTarget): string { - const t = name.trim() - if (t === '') return '' - switch (target) { - case 'java': return '/* ' + t + ' */' - case 'python': return ' # ' + t - case 'javascript': return ' /* ' + t + ' */' - } -} - -// MARK: - Ausgabe der Elemente - -function emitInstructionLike(textLines: string[], target: CodeTarget, options: CodeOptions, indent: number, sink: Sink) { - const body = textLines.map((l) => l.trim()) - if (body.every((l) => l === '')) { - // Neue, noch leere Anweisung: eine leere Anweisung (Python: `pass`). - if (options.live) sink.out += sp(indent) + (target === 'python' ? 'pass' : ';') + '\n' - return - } - if (options.includeDiagramTextAsComments) { - const forDiagram = body.filter((l) => l !== '' && !isDiagramEmptyPlaceholderText(l)) - if (forDiagram.length > 0) sink.out += blockComment(forDiagram, target, indent) - } - for (const raw of body) { - if (raw === '') continue - if (isDiagramEmptyPlaceholderText(raw)) { - sink.out += options.live ? livePlaceholderLine(indent, options.live) : emptyElementCommentLine(indent, target) - continue - } - const ints = options.jsIntVariables ?? new Set() - const arrays = options.arrayElementTypes ?? new Map() - const texts = options.stringVariables ?? new Set() - const special = - (target === 'java' ? emitJavaInputLineIfNeeded(raw, indent, arrays) : undefined) ?? - (target === 'javascript' ? emitJavaScriptInputLineIfNeeded(raw, indent, arrays) : undefined) ?? - (target === 'python' ? emitPythonInputLineIfNeeded(raw, indent, arrays) : undefined) ?? - emitFileLineIfNeeded(raw, target, indent, ints, texts) ?? - emitPrintLineIfNeeded(raw, target, indent, ints, texts) ?? - emitConstTypedLineIfNeeded(raw, target, indent, ints) ?? - emitTranslatedJavaLikeAssignmentIfNeeded(raw, target, indent, ints, texts) ?? - emitBareLeadingStringAsPrintIfNeeded(raw, target, indent, ints, texts) - if (special !== undefined) { - sink.out += target === 'javascript' ? javaScriptIntegerDivision(special, ints) : special - continue - } - let line = raw - if (isCStyleSemicolonTarget(target)) { - line = formatCStyleStatementLine(target === 'javascript' ? javaScriptIntegerDivision(raw, ints) : raw) - } else if (target === 'python') { - line = pythonStatementLine(raw, ints, texts) - } - if (line === '') continue - sink.out += sp(indent) + line + '\n' - } -} - -function emitConditionHeader( - prefix: string, - suffix: string, - textLines: string[], - target: CodeTarget, - options: CodeOptions, - indent: number, - sink: Sink, - livePlaceholder = options.live?.labels.condition, -) { - if (options.includeDiagramTextAsComments) sink.out += blockComment(textLines, target, indent) - if (livePlaceholder !== undefined && isBlank(textLines)) { - sink.out += sp(indent) + prefix + livePlaceholderText(livePlaceholder) + suffix - return - } - const condition = conditionText(textLines) - const ints = options.jsIntVariables ?? new Set() - const text = - target === 'python' - ? pythonExpression(condition, ints) - : target === 'javascript' - ? javaScriptIntegerDivision(condition, ints) - : condition - sink.out += sp(indent) + prefix + text + suffix -} - -/** Leere Blöcke: Python braucht `pass`. */ -function emitBlockBody(elements: VSElement[], target: CodeTarget, options: CodeOptions, indent: number, sink: Sink) { - const before = sink.out.length - for (const el of elements) emit(el, target, options, indent, sink) - if (options.live && sink.out.length === before) sink.out += livePlaceholderLine(indent, options.live) - if (target !== 'python') return - // Nur Kommentare (z. B. „# empty element“) zählen in Python nicht als Anweisung. - const emitted = sink.out.slice(before).split('\n').map((l) => l.trim()) - if (emitted.every((l) => l === '' || l.startsWith('#'))) sink.out += sp(indent) + 'pass\n' -} - -/** Steht im false-Zweig nur eine weitere Verzweigung, wird daraus `else if` (Python: `elif`) statt eines verschachtelten Blocks. */ -function elseIfBranch(elements: VSElement[], options: CodeOptions): VSElement | undefined { - // Mit Diagrammtext als Kommentar stünde der Kommentar zwischen `}` und `else if`: dann verschachtelt lassen. - if (options.includeDiagramTextAsComments || elements.length !== 1) return undefined - const only = elements[0] - return only.type === ElementType.verzweigung && (only.cases?.length ?? 0) >= 2 ? only : undefined -} - -function emitIfChain(element: VSElement, target: CodeTarget, options: CodeOptions, indent: number, sink: Sink, isElseIf: boolean) { - const cases = element.cases - if (!cases || cases.length < 2) return - const py = target === 'python' - const inner = indent + options.indentPerLevel - const omitElse = !options.includeEmptyElseBranch && isIfElseFalseBranchVisuallyEmpty(cases[1].elements) - const next = omitElse ? undefined : elseIfBranch(cases[1].elements, options) - if (py) { - emitConditionHeader(isElseIf ? 'elif ' : 'if ', ':\n', element.textLines, target, options, indent, sink) - emitBlockBody(cases[0].elements, target, options, inner, sink) - if (next) emitIfChain(next, target, options, indent, sink, true) - else if (!omitElse) { - sink.out += sp(indent) + 'else:\n' - emitBlockBody(cases[1].elements, target, options, inner, sink) - } - return - } - emitConditionHeader(isElseIf ? '} else if (' : 'if (', ') {\n', element.textLines, target, options, indent, sink) - emitBlockBody(cases[0].elements, target, options, inner, sink) - if (next) { - const start = sink.out.length - emitIfChain(next, target, options, indent, sink, true) - recordLiveSpan(options, next, sink, start) - } else if (omitElse) sink.out += sp(indent) + '}\n' - else { - sink.out += sp(indent) + '} else {\n' - emitBlockBody(cases[1].elements, target, options, inner, sink) - sink.out += sp(indent) + '}\n' - } -} - -function emit(element: VSElement, target: CodeTarget, options: CodeOptions, indent: number, sink: Sink) { - const start = sink.out.length - emitElement(element, target, options, indent, sink) - recordLiveSpan(options, element, sink, start) -} - -function emitElement(element: VSElement, target: CodeTarget, options: CodeOptions, indent: number, sink: Sink) { - const step = options.indentPerLevel - const py = target === 'python' - const body = (els: VSElement[], i: number) => emitBlockBody(els, target, options, i, sink) - const header = (prefix: string, suffix: string) => - emitConditionHeader(prefix, suffix, element.textLines, target, options, indent, sink) - const commentIfWanted = (i = indent) => { - if (options.includeDiagramTextAsComments) sink.out += blockComment(element.textLines, target, i) - } - - switch (element.type) { - case ElementType.leerElement: - sink.out += options.live ? livePlaceholderLine(indent, options.live) : emptyElementCommentLine(indent, target) - return - - case ElementType.anweisung: - case ElementType.aufruf: - emitInstructionLike(element.textLines, target, options, indent, sink) - return - - case ElementType.aussprung: { - const hint = conditionText(element.textLines) - if (element.textLines.some((l) => l.trim() !== '')) commentIfWanted() - if (py) sink.out += sp(indent) + (hint === '' ? 'break\n' : `break # ${hint}\n`) - else sink.out += sp(indent) + (hint === '' ? 'break;\n' : `break; // ${hint}\n`) - return - } - - case ElementType.verzweigung: - emitIfChain(element, target, options, indent, sink, false) - return - - case ElementType.fallauswahl: { - const cases = element.cases - if (!cases || cases.length === 0) return - if (py && options.pythonWithoutMatch) { - const choice = `wahl${indent / step + 1}` - emitConditionHeader(choice + ' = ', '\n', element.textLines, target, options, indent, sink) - cases.forEach((c, i) => { - const isLast = i === cases.length - 1 - if (isLast && i === 0) sink.out += sp(indent) + 'if True:' + forcedCaseComment(c.name, target) + '\n' - else if (isLast) sink.out += sp(indent) + 'else:' + forcedCaseComment(c.name, target) + '\n' - else sink.out += sp(indent) + (i === 0 ? 'if ' : 'elif ') + choice + ' == ' + caseLabelToken(c.name, target) + ':\n' - body(c.elements, indent + step) - }) - return - } - emitConditionHeader( - py ? 'match ' : 'switch (', - py ? ':\n' : ') {\n', - element.textLines, - target, - options, - indent, - sink, - options.live?.labels.selector, - ) - cases.forEach((c, i) => { - const isLast = i === cases.length - 1 - if (target === 'java') { - const label = isLast - ? 'default -> { ' + forcedCaseComment(c.name, target) + '\n' - : 'case ' + caseLabelToken(c.name, target) + ' -> {\n' - sink.out += sp(indent + step) + label - body(c.elements, indent + step * 2) - sink.out += sp(indent + step) + '}\n' - } else if (py) { - const line = isLast ? 'case _:' + forcedCaseComment(c.name, target) : 'case ' + caseLabelToken(c.name, target) + ':' - sink.out += sp(indent + step) + line + '\n' - body(c.elements, indent + step * 2) - } else { - const line = isLast ? 'default:' + forcedCaseComment(c.name, target) : 'case ' + caseLabelToken(c.name, target) + ':' - sink.out += sp(indent + step) + line + '\n' - body(c.elements, indent + step * 2) - // Immer `break;`: kein Fallthrough. - sink.out += sp(indent + step * 2) + 'break;\n' - } - }) - if (!py) sink.out += sp(indent) + '}\n' - return - } - - case ElementType.forSchleife: - if (target === 'java') { - commentIfWanted() - const head = options.live ? liveForLoopHeaderText(element.textLines, options.live.labels) : cStyleForLoopHeaderText(element.textLines) - sink.out += sp(indent) + 'for (' + head + ') {\n' - } else if (py) { - const ints = options.jsIntVariables ?? new Set() - const loop = pythonForLoop(element.textLines, ints) - if (loop?.rangeLine !== undefined) { - commentIfWanted() - sink.out += sp(indent) + loop.rangeLine + '\n' - } else if (loop) { - // Kein passendes `range`: Startwert, `while` mit Bedingung, Schritt am Ende des Rumpfs. - commentIfWanted() - sink.out += sp(indent) + loop.init + '\n' - sink.out += sp(indent) + 'while ' + pythonExpression(loop.condition, ints) + ':\n' - for (const el of element.loopBody ?? []) emit(el, target, options, indent + step, sink) - sink.out += sp(indent + step) + pythonStatementLine(loop.increment, ints) + '\n' - return - } else if (options.live) { - sink.out += sp(indent) + livePythonForLoopHeaderText(element.textLines, options.live.labels, ints) + '\n' - } else { - header('for ', ':\n') - } - } else { - commentIfWanted() - const head = options.live - ? liveJavaScriptForLoopHeaderText(element.textLines, options.live.labels) - : javaScriptForLoopHeaderText(element.textLines) - sink.out += sp(indent) + 'for (' + head + ') {\n' - } - body(element.loopBody ?? [], indent + step) - if (!py) sink.out += sp(indent) + '}\n' - return - - case ElementType.whileSchleife: - header(py ? 'while ' : 'while (', py ? ':\n' : ') {\n') - body(element.loopBody ?? [], indent + step) - if (!py) sink.out += sp(indent) + '}\n' - return - - case ElementType.doUntilSchleife: { - const cond = - options.live && isBlank(element.textLines) - ? livePlaceholderText(options.live.labels.condition) - : conditionText(element.textLines) - if (py) { - sink.out += sp(indent) + 'while True:\n' - const bodyStart = indent + step - body(element.loopBody ?? [], bodyStart) - commentIfWanted(bodyStart) - const expression = cond.includes(livePlaceholderStart) ? cond : pythonExpression(cond, options.jsIntVariables ?? new Set()) - sink.out += sp(bodyStart) + 'if not (' + expression + '):\n' - sink.out += sp(bodyStart + step) + 'break\n' - } else { - sink.out += sp(indent) + 'do {\n' - body(element.loopBody ?? [], indent + step) - commentIfWanted() - sink.out += sp(indent) + '} while (' + cond + ');\n' - } - return - } - - case ElementType.endlosschleife: { - const extra = conditionText(element.textLines) - const plain = extra === '' || extra === 'true' - if (!plain) commentIfWanted() - if (py) sink.out += sp(indent) + (plain ? 'while True:\n' : `while True: # ${extra}\n`) - else sink.out += sp(indent) + (plain ? 'while (true) {\n' : `while (true) /* ${extra} */ {\n`) - body(element.loopBody ?? [], indent + step) - if (!py) sink.out += sp(indent) + '}\n' - return - } - } -} - -// MARK: - Ansicht „Code live“ - -/** Platzhalter stehen zwischen diesen Zeichen; die Ansicht zeigt sie grau und ohne die Zeichen. */ -export const livePlaceholderStart = '\uE000' -export const livePlaceholderEnd = '\uE001' - -/** Platzhalter-Wörter in der Sprache der Oberfläche, z. B. „Bedingung“. */ -export interface LiveLabels { - condition: string - selector: string - code: string - start: string - step: string - /** Obergrenze von `range(…)` in Python. */ - end: string -} - -/** Zeilen eines Elements im erzeugten Code (ab 0, `to` einschließlich). */ -export interface LiveSpan { - path: number[] - from: number - to: number -} - -export interface LiveCode { - /** Code mit Platzhalter-Markierungen (`livePlaceholderStart` … `livePlaceholderEnd`). */ - text: string - spans: LiveSpan[] -} - -interface LiveContext { - target: LiveTarget - labels: LiveLabels - paths: Map - spans: LiveSpan[] -} - -const livePlaceholderText = (label: string) => livePlaceholderStart + label + livePlaceholderEnd -/** Leerer Block: `// Dein Code`; Python braucht dort eine Anweisung, also `pass # Dein Code`. */ -const livePlaceholderLine = (indent: number, live: LiveContext) => - sp(indent) + livePlaceholderText(live.target === 'python' ? `pass # ${live.labels.code}` : `// ${live.labels.code}`) + '\n' -const isBlank = (lines: string[]) => lines.every((l) => l.trim() === '') - -/** `for (Startwert; Bedingung; Schritt)`: leere Felder werden zu Platzhaltern. */ -function liveForLoopHeaderText(lines: string[], labels: LiveLabels): string { - if (isBlank(lines) || lines.length === 3) { - const placeholders = [labels.start, labels.condition, labels.step] - return placeholders - .map((label, i) => { - const field = stripTrailingSemicolon(lines[i] ?? '') - return field === '' ? livePlaceholderText(label) : field - }) - .join('; ') - } - return cStyleForLoopHeaderText(lines) -} - -/** Wie `javaScriptForLoopHeaderText`: `int i = 0` wird zu `let i = 0`, Platzhalter bleiben. */ -function liveJavaScriptForLoopHeaderText(lines: string[], labels: LiveLabels): string { - const parts = liveForLoopHeaderText(lines, labels).split(';') - const translated = parts[0].includes(livePlaceholderStart) ? undefined : javaScriptForInitializerFromJavaTypedDeclaration(parts[0]) - if (translated !== undefined) parts[0] = translated - return parts.join(';') -} - -/** - * Unvollständige Zählschleife in Python: `for i in range(Start, Ende):`, fehlende Teile als Platzhalter. - * (Vollständige Köpfe übersetzt `pythonForLoop`.) - */ -function livePythonForLoopHeaderText(lines: string[], labels: LiveLabels, intVariables: Set): string { - const [initS = '', condS = '', stepS = ''] = lines.length === 3 ? lines.map((l) => stripTrailingSemicolon(l)) : [] - const init = groups(/^\s*(?:integer|int|long|short|byte|double|float)?\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i, initS) - const name = init?.[1] ?? groups(/^\s*([a-zA-Z_]\w*)\s*(?:<=|<|>=|>)/, condS)?.[1] - const start = init ? pythonExpression(init[2], intVariables) : livePlaceholderText(labels.start) - const cond = groups(/^\s*(\w+)\s*(<=|<)\s*(.+)$/, condS) - let stop = livePlaceholderText(labels.end) - if (cond && (name === undefined || cond[1] === name)) { - stop = pythonExpression(cond[3], intVariables) - if (cond[2] === '<=') stop = shiftedBound(stop, 1) - } - const step = name === undefined ? undefined : forLoopStepSize(stepS, name) - const args = [start, stop, ...(step !== undefined && step !== 1 ? [String(step)] : [])] - return `for ${name ?? livePlaceholderText(labels.selector)} in range(${args.join(', ')}):` -} - -/** Pfade wie in `editing.ts`: `[i, Zweig, j, …]`, Schleifenrumpf = Zweig 0. */ -function elementPaths(elements: VSElement[], prefix: number[] = [], out = new Map()) { - elements.forEach((e, i) => { - const path = [...prefix, i] - if (!out.has(e)) out.set(e, path) - if (e.loopBody) elementPaths(e.loopBody, [...path, 0], out) - e.cases?.forEach((c, k) => elementPaths(c.elements, [...path, k], out)) - }) - return out -} - -function recordLiveSpan(options: CodeOptions, element: VSElement, sink: Sink, start: number) { - const live = options.live - const path = live?.paths.get(element) - if (!live || !path || sink.out.length === start) return - const lineAt = (offset: number) => sink.out.slice(0, offset).split('\n').length - 1 - live.spans.push({ path, from: lineAt(start), to: lineAt(sink.out.length - 1) }) -} - -/** Zielsprachen der Ansicht „Code live“. */ -export type LiveTarget = 'java' | 'javascript' | 'python' - -/** - * Code für die Ansicht „Code live“: wie das Code-Snippet, aber für Anfänger lesbar, solange noch etwas fehlt - * (leere Anweisung `;`, `if (Bedingung)`, `// Dein Code` in leeren Blöcken), und mit den Zeilen jedes Elements. - */ -export function generateLiveCode(document: VSDocument, target: LiveTarget, labels: LiveLabels): LiveCode { - const live: LiveContext = { target, labels, paths: elementPaths(document.elements), spans: [] } - const opts = documentOptions(document, target, { includeEmptyElseBranch: true, live }) - const header = parseSubprogramHeader(document.caption) - let prelude = '' - if (target === 'java') { - const files = fileImports(document) - for (const line of files) prelude += line + '\n' - if (documentContainsJavaInput(document)) { - if (!files.includes('import java.util.Scanner;')) prelude += 'import java.util.Scanner;\n' - prelude += 'Scanner scanner = new Scanner(System.in);\n\n' - } else if (files.length) prelude += '\n' - if (header) prelude += javaMethodHeader(header) + '\n' - } else { - if (header) { - for (const p of header.params) { - if (p.dims) continue - if (p.type === 'int') opts.jsIntVariables!.add(p.name) - if (p.type === 'string') opts.stringVariables!.add(p.name) - } - } - const names = header?.params.map((p) => p.name).join(', ') - if (target === 'javascript') { - prelude += '"use strict";\n\n' - prelude += fileHelperClasses([document], 'javascript') - if (header) prelude += `function ${header.name}(${names}) {\n` - } else { - if (documentContainsJavaMathRandom(document)) prelude += 'import random\n\n' - prelude += fileHelperClasses([document], 'python') - if (header) prelude += `def ${header.name}(${names}):\n` - } - } - const sink = { out: '' } - emitBlockBody(document.elements, target, opts, header ? opts.indentPerLevel : 0, sink) - if (header && target !== 'python') sink.out += '}\n' - const offset = prelude.split('\n').length - 1 - return { - text: postProcessLengthSyntax((prelude + sink.out).replace(/\n+$/, ''), target), - spans: live.spans.map((s) => ({ ...s, from: s.from + offset, to: s.to + offset })), - } -} - -export const generateLiveJava = (document: VSDocument, labels: LiveLabels) => generateLiveCode(document, 'java', labels) - -// MARK: - Nachbearbeitung: `.length` / `.length()` zielsprachentauglich (Heuristik) - -function postProcessLengthSyntax(s: string, target: CodeTarget): string { - switch (target) { - case 'javascript': - return javaScriptStringMethods(convertCharCasts(s, 'javascript')).replace(/\.length\(\)/g, '.length').replace(/\.contains\(/g, '.includes(') - case 'python': - return pythonStringMethodsDeep(convertCharCasts(s, 'python')).replace(/\b([a-zA-Z_]\w*(?:\[[^[\]]*\])?)\.length(?:\(\)|\b)/g, 'len($1)') - case 'java': - return replaceIdentifierDotLengthWithParensInJava(s) - } -} - -/** Java-String-Methoden nach Python: `w.charAt(i)` → `w[i]`, `w.substring(a, b)` → `w[a:b]`, `w.contains(x)` → `(x in w)` usw. */ -function pythonStringMethodsDeep(s: string): string { - // Ketten wie `w.trim().toLowerCase()` brauchen mehrere Durchgänge. - for (let i = 0; i < 5; i++) { - const next = javaStaticCalls(pythonStringMethods(s), 'python') - if (next === s) break - s = next - } - return s -} - -const pythonRenamed: Record = { - indexOf: 'find', - lastIndexOf: 'rfind', - toUpperCase: 'upper', - toLowerCase: 'lower', - trim: 'strip', - startsWith: 'startswith', - endsWith: 'endswith', -} - -function pythonStringMethods(s: string): string { - return rewriteMethodCalls(s, ['charAt', 'substring', 'contains', 'isEmpty', 'equalsIgnoreCase', 'compareTo', 'repeat', 'concat', ...Object.keys(pythonRenamed)], (target, method, args) => { - switch (method) { - case 'charAt': - return `${target}[${args}]` - case 'substring': { - const parts = splitTopLevelComma(args) - return parts.length === 2 ? `${target}[${parts[0].trim()}:${parts[1].trim()}]` : `${target}[${args.trim()}:]` - } - case 'contains': - return `(${args.trim()} in ${target})` - case 'isEmpty': - return `(len(${target}) == 0)` - case 'equalsIgnoreCase': - return `(${target}.lower() == ${args.trim()}.lower())` - case 'compareTo': - return `((${target} > ${args.trim()}) - (${target} < ${args.trim()}))` - case 'repeat': - return `(${target} * ${args.trim()})` - case 'concat': - return `(${target} + ${args.trim()})` - default: - return `${target}.${pythonRenamed[method]}(${args})` - } - }) -} - -/** Java-String-Methoden, die es in JavaScript nicht gibt: `w.isEmpty()`, `w.equalsIgnoreCase(x)`, `w.compareTo(x)`; `replace` ersetzt wie in Java alle Stellen. */ -function javaScriptStringMethods(s: string): string { - for (let i = 0; i < 5; i++) { - const next = javaStaticCalls( - rewriteMethodCalls(s, ['isEmpty', 'equalsIgnoreCase', 'compareTo', 'replace'], (target, method, args) => { - switch (method) { - case 'isEmpty': - return `(${target}.length === 0)` - case 'equalsIgnoreCase': - return `(${target}.toLowerCase() === ${args.trim()}.toLowerCase())` - case 'compareTo': - return `${target}.localeCompare(${args.trim()})` - default: - return `${target}.replaceAll(${args})` - } - }), - 'javascript', - ) - if (next === s) break - s = next - } - return s -} - -/** `Integer.parseInt(x)`, `Double.parseDouble(x)`, `String.valueOf(x)` und `Files.readString(Path.of(x))` in Python und JavaScript. */ -function javaStaticCalls(s: string, target: 'python' | 'javascript'): string { - const file = /\bFiles\.readString\(\s*(?:Path\.of|Paths\.get)\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*\)/g - if (target === 'python') { - return s - .replace(file, 'open($1, encoding="utf-8").read()') - .replace(/\bInteger\.parseInt\(/g, 'int(') - .replace(/\bDouble\.parseDouble\(/g, 'float(') - .replace(/\bString\.valueOf\(/g, 'str(') - } - return s - .replace(file, 'require("fs").readFileSync($1, "utf8")') - .replace(/\bInteger\.parseInt\(/g, 'parseInt(') - .replace(/\bDouble\.parseDouble\(/g, 'parseFloat(') - .replace(/\bString\.valueOf\(/g, 'String(') -} - -/** Teilt Argumente am Komma außerhalb von Klammern. */ -function splitTopLevelComma(args: string): string[] { - let depth = 0 - const comma = [...args].findIndex((c) => (c === '(' ? (depth += 1, false) : c === ')' ? (depth -= 1, false) : c === ',' && depth === 0)) - return comma < 0 ? [args] : [args.slice(0, comma), args.slice(comma + 1)] -} - -/** Ersetzt Aufrufe `ziel.methode(…)` der genannten Methoden; das Ziel ist ein Name, ein Fach, eine Kette oder ein String. */ -function rewriteMethodCalls(s: string, methods: string[], replace: (target: string, method: string, args: string) => string): string { - const re = new RegExp(`([A-Za-z_]\\w*(?:\\[[^[\\]]*\\]|\\.\\w+\\(\\))*|"(?:[^"\\\\]|\\\\.)*")\\.(${methods.join('|')})\\(`, 'g') - let out = '' - let last = 0 - for (let m = re.exec(s); m; m = re.exec(s)) { - const open = m.index + m[0].length - 1 - let depth = 0 - let close = -1 - for (let i = open; i < s.length; i++) { - if (s[i] === '(') depth += 1 - else if (s[i] === ')' && --depth === 0) { - close = i - break - } - } - if (close < 0) break - const args = rewriteMethodCalls(s.slice(open + 1, close), methods, replace) - out += s.slice(last, m.index) + replace(m[1], m[2], args) - last = close + 1 - re.lastIndex = close + 1 - } - return out + s.slice(last) -} - -function javaArrayDeclarationNames(s: string): Set { - const type = '(?:byte|short|int|long|float|double|boolean|char|String)' - const patterns = [ - new RegExp(`\\b(?:final\\s+)?${type}\\s*\\[\\s*\\](?:\\s*\\[\\s*\\])?\\s+([a-zA-Z_]\\w*)\\b`, 'g'), - new RegExp(`\\b(?:final\\s+)?${type}\\s+([a-zA-Z_]\\w*)\\s*\\[\\s*\\]`, 'g'), - ] - const names = new Set() - for (const re of patterns) for (const m of s.matchAll(re)) names.add(m[1].toLowerCase()) - return names -} - -/** `String`: `s.length()`; Array: `a.length` — nur wenn `a` im erzeugten Code als Array deklariert ist. */ -function replaceIdentifierDotLengthWithParensInJava(s: string): string { - const arrays = javaArrayDeclarationNames(s) - return s.replace(/\b([a-zA-Z_]\w*)\.length(?!\()/g, (m, id: string) => (arrays.has(id.toLowerCase()) ? m : `${id}.length()`)) -} - -// MARK: - Java: vollständige Klasse (.java) - -const javaReserved = new Set( - ( - 'abstract assert boolean break byte case catch char class const continue default do double else enum extends final ' + - 'finally float for goto if implements import instanceof int interface long native new package private protected ' + - 'public return short static strictfp super switch synchronized this throw throws transient try void volatile while ' + - 'true false null var record sealed permits yield module open opens requires exports to uses provides with transitive' - ).split(' '), -) - -/** Gültiger Java-Klassenname aus freiem Text (z. B. Diagrammtitel). */ -export function sanitizedJavaClassName(raw: string): string { - const trimmed = raw.trim() - if (trimmed === '') return 'Main' - let result = '' - Array.from(trimmed).forEach((ch, idx) => { - if (idx === 0) { - if (/\p{L}/u.test(ch) || ch === '_' || ch === '$') result += ch - else if (/\p{Nd}/u.test(ch)) result += '_' + ch - else result += '_' - } else if (/[\p{L}\p{M}\p{N}]/u.test(ch) || ch === '_' || ch === '$') { - result += ch - } else { - result += '_' - } - }) - result = result.replace(/_{2,}/g, '_').replace(/^_+|_+$/g, '') - if (result === '') return 'Main' - // Das Trimmen oben entfernt auch den `_` vor einer führenden Ziffer. - if (/^\p{Nd}/u.test(result)) result = '_' + result - if (javaReserved.has(result.toLowerCase())) result = '_' + result - return result -} - -/** Vollständige `.java`-Datei: `public class` mit `main`, erzeugtes Snippet eingerückt im Rumpf. */ -export function generateJavaClassFile( - document: VSDocument, - className: string, - options: Partial = {}, - subprograms: VSDocument[] = [], -): string { - const opts = { ...defaultCodeOptions, ...options } - const name = sanitizedJavaClassName(className) - const methodDocs = [document, ...subprograms] - .filter((d, i, all) => all.indexOf(d) === i) - .map((d) => ({ d, header: parseSubprogramHeader(d.caption) })) - .filter((m): m is { d: VSDocument; header: SubprogramHeader } => m.header !== undefined) - const methods = methodDocs.map((m) => subprogramDefinition(m.d, m.header, 'java', options, opts.indentPerLevel)) - const ownHeader = parseSubprogramHeader(document.caption) - // Mit Methoden braucht jede denselben Scanner, deshalb steht er dann als Feld in der Klasse. - const methodInput = methodDocs.some((m) => documentContainsJavaInput(m.d)) - const snippet = generateCode(document, 'java', opts) - const innerBase = opts.indentPerLevel * 2 - const hasInput = documentContainsJavaInput(document) - const bodyNoPrelude = hasInput ? stripJavaSnippetScannerPrelude(snippet) : snippet - const prefix = sp(innerBase) - const indentedBody = - bodyNoPrelude.trim() === '' - ? '' - : bodyNoPrelude - .split('\n') - .map((line) => (line.trim() === '' ? prefix : prefix + line)) - .join('\n') - - let out = '' - const scannerField = methods.length > 0 && (hasInput || methodInput) - // `Files.readString` kann eine IOException werfen; Java verlangt dann `throws` an der Methode. - const imports = [...new Set([document, ...methodDocs.map((m) => m.d)].flatMap(fileImports))] - const files = imports.some((i) => i !== arraysImport) - if ((hasInput || methodInput) && !imports.includes('import java.util.Scanner;')) imports.push('import java.util.Scanner;') - for (const line of imports) out += line + '\n' - if (imports.length) out += '\n' - out += `public class ${name} {\n` - if (scannerField) out += sp(opts.indentPerLevel) + 'static Scanner scanner = new Scanner(System.in);\n\n' - // Ein Unterprogramm allein ist nur eine Methode, ohne `main`. - if (!ownHeader) { - out += sp(opts.indentPerLevel) + `public static void main(String[] args)${files ? ' throws Exception' : ''} {\n` - if (hasInput && !scannerField) { - out += sp(innerBase) + 'Scanner scanner = new Scanner(System.in);\n' - if (indentedBody !== '') out += '\n' - } - out += indentedBody - if (indentedBody !== '' && !indentedBody.endsWith('\n')) out += '\n' - out += sp(opts.indentPerLevel) + '}\n' - } - for (const [i, m] of methods.entries()) out += (i > 0 || !ownHeader ? '\n' : '') + m - out += '}\n' - return out -} - -/** Entfernt die im Snippet-Modus vorangestellte Scanner-Zeile (und den Kommentar-„Import“). */ -function stripJavaSnippetScannerPrelude(snippet: string): string { - const lines = snippet.split('\n') - let i = 0 - if (lines[i] === '// import java.util.Scanner;') i++ - while (i < lines.length && lines[i].trim() === '') i++ - if (lines[i] === 'Scanner scanner = new Scanner(System.in);') i++ - while (i < lines.length && lines[i].trim() === '') i++ - return i >= lines.length ? '' : lines.slice(i).join('\n') -} diff --git a/web/src/core/diagramText.ts b/web/src/core/diagramText.ts deleted file mode 100644 index 4c66f76b..00000000 --- a/web/src/core/diagramText.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { ElementType, type VSElement } from './model' - -/** Texte, wie sie im Diagramm angezeigt werden (`VisuStructDiagramText.swift`). `undefined` = kein Text → Platzhalter. */ -export function displayLines(element: VSElement): string[] | undefined { - const raw = element.textLines - if (raw.every((l) => l.trim() === '')) return undefined - switch (element.type) { - case ElementType.forSchleife: - return raw.length <= 1 ? raw : [forLoopSingleLine(raw)] - case ElementType.verzweigung: - return withLeadingKeyword('if', raw) - case ElementType.whileSchleife: - return withLeadingKeyword('while', raw) - case ElementType.doUntilSchleife: - return withLeadingKeyword('while', normalizedDoUntilConditionLines(raw)) - case ElementType.fallauswahl: - return withLeadingKeyword('switch', raw) - default: - return raw - } -} - -/** Entfernt ein führendes `until`, damit die Anzeige einheitlich `while …` nutzt. */ -export function normalizedDoUntilConditionLines(raw: string[]): string[] { - if (raw.length === 0) return raw - const t = raw[0].trim() - const lower = t.toLowerCase() - const o = [...raw] - if (lower === 'until') o[0] = '' - else if (lower.startsWith('until ')) o[0] = t.slice(6).trim() - else if (lower.startsWith('until(')) o[0] = t.slice(5) - else return raw - return o -} - -/** `while i < 3` → `{ keyword: 'while', rest: 'i < 3' }` (Schlüsselwort fett). */ -export function keywordSplit(line: string): { keyword: string; rest: string } | undefined { - const keywords = new Set(['if', 'for', 'while', 'until', 'switch']) - const colonKeywords = ['input:', 'output:', 'print:'] - const trimmed = line.trim() - const lower = trimmed.toLowerCase() - for (const prefix of colonKeywords) { - if (lower === prefix) return { keyword: trimmed, rest: '' } - if (lower.startsWith(prefix + ' ')) { - return { keyword: trimmed.slice(0, prefix.length), rest: trimmed.slice(prefix.length).trim() } - } - } - const firstSpace = trimmed.indexOf(' ') - if (firstSpace < 0) return undefined - const head = trimmed.slice(0, firstSpace) - if (!keywords.has(head.toLowerCase())) return undefined - return { keyword: head, rest: trimmed.slice(firstSpace + 1).trim() } -} - -function withLeadingKeyword(keyword: string, lines: string[]): string[] { - if (lines.length === 0) return lines - const trimmed = lines[0].trim() - const lower = trimmed.toLowerCase() - if (lower === keyword || lower.startsWith(keyword + ' ')) return lines - const out = [...lines] - out[0] = keyword + ' ' + trimmed - return out -} - -/** Zählschleife einzeilig: `for (init; cond; step)` → `for init; cond; step`. */ -export function forLoopSingleLine(textLines: string[]): string { - let parts = [...textLines] - while (parts.length < 3) parts.push('') - parts = parts.slice(0, 3) - const strip = (s: string) => { - let x = s.trim() - while (x.endsWith(';')) x = x.slice(0, -1).trim() - return x - } - const joined = parts.map(strip).join('; ').trim() - return joined === '' ? 'for' : 'for ' + joined -} - -/** Platzhaltertexte für leere Elemente (deutsche Werte aus `de.lproj/Localizable.strings`). */ -export const stubTexts: Record = { - [ElementType.anweisung]: 'Anweisung', - [ElementType.verzweigung]: 'Bedingung', - [ElementType.fallauswahl]: 'Fallauswahl', - [ElementType.forSchleife]: 'for 0 < i < anzahl', - [ElementType.whileSchleife]: 'while …', - [ElementType.doUntilSchleife]: 'while …', - [ElementType.endlosschleife]: 'endless', - [ElementType.aussprung]: 'break', - [ElementType.aufruf]: 'Aufruf()', - [ElementType.leerElement]: 'ø', -} - -/** Wie `DiagramCanvasView.displayTextLines`. */ -export function setCanvasStubTexts(texts: Partial>) { - Object.assign(stubTexts, texts) -} - -export function canvasTextLines(element: VSElement): string[] { - return displayLines(element) ?? [stubTexts[element.type]] -} diff --git a/web/src/core/editing.ts b/web/src/core/editing.ts deleted file mode 100644 index 16de25ca..00000000 --- a/web/src/core/editing.ts +++ /dev/null @@ -1,241 +0,0 @@ -import { ElementType, isLoop, isMultiway, makeElement, type VSDocument, type VSElement } from './model' - -/** - * Port der benötigten Teile von `VisuStructDocumentEditing.swift`. - * Pfade wie dort: Wurzelindex, dann je Schleife `0, i` bzw. je Verzweigung/Fallauswahl `fall, i`. - * Alle Funktionen sind rein (das Dokument wird nicht verändert). - */ -export class EditError extends Error {} - -const clone = (v: T): T => structuredClone(v) - -export function placeholderElement(): VSElement { - return makeElement(ElementType.anweisung, { textLines: ['ø'] }) -} - -export function newEmptyDocument(): VSDocument { - return { caption: '', elements: [placeholderElement()] } -} - -export function isPlaceholderElement(el: VSElement): boolean { - if (el.type === ElementType.leerElement) return true - if (el.type === ElementType.anweisung) { - const t = el.textLines - return t.length === 0 || (t.length === 1 && t[0] === 'ø') - } - return false -} - -export function isPlaceholderSlot(doc: VSDocument, path: number[]): boolean { - const el = tryElementAt(doc, path) - return el !== undefined && isPlaceholderElement(el) -} - -/** Die Liste, in der das Element am Pfad steht, plus sein Index darin. */ -function locate(elements: VSElement[], path: number[]): { list: VSElement[]; index: number } { - if (path.length === 0) throw new EditError('Invalid element path.') - const [i, ...rest] = path - if (i < 0 || i >= elements.length) throw new EditError('Invalid element path.') - if (rest.length === 0) return { list: elements, index: i } - const [head, ...tail] = rest - if (tail.length === 0) throw new EditError('Invalid element path.') - return locate(childList(elements[i], head), tail) -} - -/** Kindliste eines Containers: Schleifenrumpf (`head` = 0) bzw. Fall `head`. */ -function childList(el: VSElement, head: number): VSElement[] { - if (isLoop(el.type)) { - if (head !== 0 || !el.loopBody) throw new EditError('Invalid element path.') - return el.loopBody - } - if (isMultiway(el.type)) { - if (!el.cases || head < 0 || head >= el.cases.length) throw new EditError('Invalid element path.') - return el.cases[head].elements - } - throw new EditError('Invalid element path.') -} - -export function elementAt(doc: VSDocument, path: number[]): VSElement { - const { list, index } = locate(doc.elements, path) - return list[index] -} - -export function tryElementAt(doc: VSDocument, path: number[]): VSElement | undefined { - try { - return elementAt(doc, path) - } catch { - return undefined - } -} - -export function replaceAtPath(doc: VSDocument, path: number[], element: VSElement): VSDocument { - const d = clone(doc) - const { list, index } = locate(d.elements, path) - list[index] = clone(element) - return d -} - -export function setTextLines(doc: VSDocument, path: number[], lines: string[]): VSDocument { - const el = clone(elementAt(doc, path)) - el.textLines = lines.length === 0 ? [''] : lines - return replaceAtPath(doc, path, el) -} - -export function canDeleteElement(doc: VSDocument, path: number[]): boolean { - if (path.length === 0) return false - if (path.length === 1 && doc.elements.length === 1) return !isPlaceholderSlot(doc, path) - return tryElementAt(doc, path) !== undefined -} - -export function deleteAtPath(doc: VSDocument, path: number[]): VSDocument { - const d = clone(doc) - const { list, index } = locate(d.elements, path) - if (path.length === 1 && list.length === 1) { - // Letztes Wurzelelement: durch Platzhalter ersetzen; der Platzhalter selbst bleibt. - if (isPlaceholderElement(list[0])) throw new EditError('The last root element cannot be deleted.') - list[0] = placeholderElement() - return d - } - list.splice(index, 1) - // Leere Schleifenrümpfe/Fälle bekommen wieder einen Platzhalter (wie Swift). - if (path.length > 1 && list.length === 0) list.push(placeholderElement()) - return d -} - -/** Einfügen in die Kindliste unter `parentPath` (`[]` = Wurzel, sonst Container-Pfad + Rumpf/Fall-Index). */ -export function insertAt(doc: VSDocument, parentPath: number[], index: number, element: VSElement): VSDocument { - const d = clone(doc) - let list: VSElement[] - if (parentPath.length === 0) { - list = d.elements - } else { - const container = parentPath.slice(0, -1) - const head = parentPath[parentPath.length - 1] - const { list: l, index: i } = locate(d.elements, container) - list = childList(l[i], head) - } - list.splice(Math.min(Math.max(0, index), list.length), 0, clone(element)) - return d -} - -export function insertBeforeSibling(doc: VSDocument, element: VSElement, before: number[]): VSDocument { - if (before.length === 0) throw new EditError('Invalid element path.') - return insertAt(doc, before.slice(0, -1), before[before.length - 1], element) -} - -export function insertAfterSibling(doc: VSDocument, element: VSElement, after: number[]): VSDocument { - if (after.length === 0) throw new EditError('Invalid element path.') - return insertAt(doc, after.slice(0, -1), after[after.length - 1] + 1, element) -} - -function isStrictAncestor(ancestor: number[], path: number[]): boolean { - return ancestor.length < path.length && ancestor.every((v, i) => path[i] === v) -} - -/** Passt einen Pfad an, nachdem `removedPath` aus seiner Geschwisterliste entfernt wurde. */ -export function adjustPathAfterRemoving(path: number[], removedPath: number[]): number[] { - if (removedPath.length === 0) return path - const removedIndex = removedPath[removedPath.length - 1] - const parent = removedPath.slice(0, -1) - if (path.length <= parent.length || !parent.every((v, i) => path[i] === v)) return path - const pos = parent.length - if (path[pos] <= removedIndex) return path - const adjusted = [...path] - adjusted[pos] -= 1 - return adjusted -} - -const samePath = (a: number[], b: number[]) => a.length === b.length && a.every((v, i) => v === b[i]) - -export function moveBeforeSibling(doc: VSDocument, from: number[], before: number[]): VSDocument { - if (samePath(from, before)) return doc - if (isStrictAncestor(from, before)) throw new EditError('Invalid element path.') - const moving = elementAt(doc, from) - const d = deleteAtPath(doc, from) - return insertBeforeSibling(d, moving, adjustPathAfterRemoving(before, from)) -} - -export function moveAfterSibling(doc: VSDocument, from: number[], after: number[]): VSDocument { - if (samePath(from, after)) return doc - if (isStrictAncestor(from, after)) throw new EditError('Invalid element path.') - const moving = elementAt(doc, from) - const d = deleteAtPath(doc, from) - return insertAfterSibling(d, moving, adjustPathAfterRemoving(after, from)) -} - -export function moveReplacingPlaceholder(doc: VSDocument, from: number[], placeholder: number[]): VSDocument { - if (!isPlaceholderSlot(doc, placeholder)) throw new EditError('Invalid element path.') - if (samePath(from, placeholder)) return doc - if (isStrictAncestor(from, placeholder)) throw new EditError('Invalid element path.') - const moving = elementAt(doc, from) - const d = deleteAtPath(doc, from) - return replaceAtPath(d, adjustPathAfterRemoving(placeholder, from), moving) -} - -// MARK: - Fallauswahl (VisuStructDocumentEditing.swift, Editing/VisuStructDocumentEditing+Multiway.swift) - -/** - * Neuer Fall für eine Fallauswahl (nicht Verzweigung): ein Platzhalter „ø“, Name automatisch (`3` vor `default` usw.). - * Liefert das Dokument und den Pfad zum neuen Platzhalter. - */ -export function appendCaseToFallauswahl(doc: VSDocument, fallauswahlPath: number[]): { doc: VSDocument; path: number[] } { - const parent = clone(elementAt(doc, fallauswahlPath)) - const cases = parent.cases - if (parent.type !== ElementType.fallauswahl || !cases || cases.length === 0) throw new EditError('Invalid element path.') - const lastIsDefault = cases[cases.length - 1].name.toLowerCase() === 'default' - const insertIndex = lastIsDefault ? cases.length - 1 : cases.length - const name = String(lastIsDefault ? cases.length : cases.length + 1) - cases.splice(insertIndex, 0, { name, elements: [placeholderElement()] }) - return { doc: replaceAtPath(doc, fallauswahlPath, parent), path: [...fallauswahlPath, insertIndex, 0] } -} - -/** Entfernt einen Fall der Fallauswahl. Es bleiben mindestens zwei Fälle übrig. */ -export function removeCaseFromFallauswahl( - doc: VSDocument, - fallauswahlPath: number[], - caseIndex: number, -): { doc: VSDocument; path: number[] } { - const parent = clone(elementAt(doc, fallauswahlPath)) - const cases = parent.cases - if (parent.type !== ElementType.fallauswahl || !cases) throw new EditError('Invalid element path.') - if (cases.length <= 2) throw new EditError('A switch needs at least two cases.') - if (caseIndex < 0 || caseIndex >= cases.length) throw new EditError('Invalid element path.') - cases.splice(caseIndex, 1) - const newCaseIndex = Math.min(caseIndex, cases.length - 1) - return { doc: replaceAtPath(doc, fallauswahlPath, parent), path: [...fallauswahlPath, newCaseIndex, 0] } -} - -/** Beschriftung eines Falls der Fallauswahl (z. B. `1`, `ok`, `default`). */ -export function setFallauswahlCaseName(doc: VSDocument, fallauswahlPath: number[], caseIndex: number, name: string): VSDocument { - const parent = clone(elementAt(doc, fallauswahlPath)) - const cases = parent.cases - if (parent.type !== ElementType.fallauswahl || !cases) throw new EditError('Invalid element path.') - if (caseIndex < 0 || caseIndex >= cases.length) throw new EditError('Invalid element path.') - const trimmed = name.trim() - if (trimmed === '') throw new EditError('Invalid element path.') - cases[caseIndex].name = trimmed - return replaceAtPath(doc, fallauswahlPath, parent) -} - -/** Innerste Fallauswahl, die `path` enthält (oder selbst ist). */ -export function multiwayAncestorPath(path: number[] | undefined, doc: VSDocument): number[] | undefined { - if (!path || path.length === 0) return undefined - for (let len = path.length; len >= 1; len--) { - const prefix = path.slice(0, len) - if (tryElementAt(doc, prefix)?.type === ElementType.fallauswahl) return prefix - } - return undefined -} - -/** Spaltenindex der Fallauswahl, wenn die Auswahl in einer Spalte liegt (`[…, fall, spalte, …]`). */ -export function caseIndexInMultiway(selectedPath: number[] | undefined, multiwayPath: number[]): number | undefined { - if (!selectedPath || selectedPath.length <= multiwayPath.length) return undefined - return selectedPath[multiwayPath.length] -} - -/** Auswahl nach Undo/Redo oder Löschen: leeres Dokument → keine; ungültige Auswahl → erstes Element. */ -export function sanitizedSelection(path: number[] | undefined, doc: VSDocument): number[] | undefined { - if (doc.elements.length === 0) return undefined - if (!path || !tryElementAt(doc, path)) return [0] - return path -} diff --git a/web/src/core/exerciseCheck.ts b/web/src/core/exerciseCheck.ts deleted file mode 100644 index 50af1f05..00000000 --- a/web/src/core/exerciseCheck.ts +++ /dev/null @@ -1,202 +0,0 @@ -import { ElementType, type VSDocument, type VSElement } from './model' -import { actionSignature, driveSignature, type MBotSensorValue } from './mbot' -import { ROBOT_STEP_MS, RobotWorld, type RobotWorldSetup } from './robotWorld' -import { SimulationEngine, type SimNotice, type SubprogramResolver } from './simulation' - -/** Automatische Prüfung einer Übung: Diagramm mit festen Eingaben simulieren und die Ausgabe vergleichen. */ - -export interface ExerciseTest { - inputs: string[] - expected: string[] - /** Dateien, die das Programm speichern soll, mit ihren Zeilen. */ - files?: { name: string; lines: string[] }[] - /** VisuBot: erwartete Fahrt (`driveSignature`), z. B. `['forward 50', 'turnRight 90']`. */ - drive?: string[] - /** VisuBot: Welt (Name einer Weltkarte in `lessons/welten`); der Roboter darf nicht anstoßen und muss ins Ziel, falls sie eins hat. */ - world?: string - /** - * VisuBot: Messwerte der Sensoren, z. B. `{ getLoudness: [0, 80] }`. Mehrere Werte kommen nacheinander, - * jeder für `SENSOR_PHASE_STEPS` Schritte, der letzte bleibt (so lässt sich „warte, bis es laut ist“ prüfen). - */ - sensors?: Record - /** VisuBot: Sollen die LEDs am Ende leuchten (`true`) oder aus sein (`false`)? */ - led?: boolean - /** VisuBot: Soll ein Ton erklingen? */ - tone?: boolean - /** VisuBot: Fährt er überhaupt (egal wie weit)? */ - drives?: boolean - /** VisuBot: Text, der am Ende auf dem Bildschirm steht (`mbot.showText`). */ - screen?: string - /** VisuBot: genau diese Befehle für LEDs, Ton, Pausen und Bildschirm (`actionSignature`, ohne Fahren). */ - actions?: string[] -} - -/** So viele Schritte gilt ein Messwert aus `ExerciseTest.sensors`, bevor der nächste kommt. */ -export const SENSOR_PHASE_STEPS = 40 - -export interface RunResult { - outputs: string[] - /** Gespeicherte Dateien (Name → Inhalt). */ - files?: Map - /** VisuBot: tatsächliche Fahrt (`driveSignature`). */ - drive?: string[] - /** VisuBot: Steht der Roboter am Ende im Ziel, wie oft ist er angestoßen? */ - atGoal?: boolean - bumps?: number - /** VisuBot: Leuchten die LEDs am Ende, kam ein Ton? */ - led?: boolean - tone?: boolean - screen?: string - /** VisuBot: Befehle ohne Fahren (`actionSignature`). */ - actions?: string[] - /** Fehler der Simulation (z. B. unbekannte Variable). */ - error?: SimNotice - /** Das Diagramm wollte mehr Eingaben, als der Test hat. */ - missingInput?: boolean - /** Eingaben blieben übrig. */ - unusedInputs?: number - /** Abbruch nach zu vielen Schritten (vermutlich Endlosschleife). */ - tooManySteps?: boolean -} - -export const maxCheckSteps = 100_000 - -/** Fahrbefehle; alle anderen (LEDs, Ton, Pause, Bildschirm) prüft `ExerciseTest.actions`. */ -const DRIVING = new Set(['forward', 'backward', 'turnLeft', 'turnRight', 'stop', 'drive']) - -export function runWithInputs( - doc: VSDocument, - inputs: string[], - subprograms?: SubprogramResolver, - world?: RobotWorldSetup, - sensors?: ExerciseTest['sensors'], -): RunResult { - const engine = new SimulationEngine(doc, subprograms) - const queue = [...inputs] - // Sensoren wie im Simulator: die Welt fährt mit, damit `mbot.getDistance()` passende Werte liefert. - const robot = new RobotWorld(world) - let applied = 0 - for (let i = 0; i < maxCheckSteps; i++) { - const { state } = engine - for (; applied < state.robotActions.length; applied++) robot.apply(state.robotActions[applied]) - // jeder Schritt dauert etwas; mit `mbot.drive` fährt der Roboter dabei weiter - if (i > 0) robot.tick(ROBOT_STEP_MS) - engine.robotSensors = { ...robot.sensors(), ...sensorsAt(sensors, i) } - const done = () => ({ - outputs: [...state.outputLines], - files: new Map(state.files), - drive: state.robotActions.length ? driveSignature(state.robotActions) : undefined, - ...(world ? { atGoal: robot.atGoal(), bumps: robot.bumps } : {}), - led: robot.leds.some((c) => c !== undefined), - tone: state.robotActions.some((a) => a.method === 'playTone'), - screen: robot.screen, - actions: actionSignature(state.robotActions.filter((a) => !DRIVING.has(a.method))), - }) - if (state.inputRequest) { - const next = queue.shift() - if (next === undefined) return { ...done(), missingInput: true } - engine.provideInput(next) - if (state.inputErrorNotice) return { ...done(), error: state.inputErrorNotice } - continue - } - if (state.isHalted) return { ...done(), error: state.notice } - if (!engine.canStep) { - return { ...done(), unusedInputs: queue.length || undefined } - } - engine.step() - } - return { outputs: [...engine.state.outputLines], files: new Map(engine.state.files), drive: driveSignature(engine.state.robotActions), tooManySteps: true } -} - -/** Messwerte im Schritt `step`: je Sensor der Wert seiner Phase, ab der letzten Phase bleibt er. */ -function sensorsAt(sensors: ExerciseTest['sensors'], step: number): Record { - const phase = Math.floor(step / SENSOR_PHASE_STEPS) - return Object.fromEntries(Object.entries(sensors ?? {}).map(([name, values]) => [name, values[Math.min(phase, values.length - 1)]])) -} - -/** Zeilen einer gespeicherten Datei; der Zeilenumbruch am Ende beginnt keine neue Zeile. Fehlt die Datei: keine Zeilen. */ -export function fileLines(content: string | undefined): string[] { - return content === undefined || content === '' ? [] : content.replace(/\n$/, '').split('\n') -} - -/** Gleiche Ausgabe: Zeile für Zeile, Leerraum am Rand egal, `1.0` gleich `1`. */ -export function sameOutput(actual: string[], expected: string[]): boolean { - if (actual.length !== expected.length) return false - return actual.every((line, i) => normalize(line) === normalize(expected[i])) -} - -/** Stimmt die Ausgabe bis auf fehlende Zeilenumbrüche, z. B. zweimal `print` statt `println`? */ -export function onlyLineBreaksMissing(actual: string[], expected: string[]): boolean { - const joined = (lines: string[]) => lines.join('').replace(/\s+/g, '') - return actual.length > 0 && actual.length < expected.length && joined(actual) === joined(expected) -} - -function normalize(line: string): string { - const t = line.trim().replace(/\s+/g, ' ') - return /^-?\d+\.0+$/.test(t) ? t.replace(/\.0+$/, '') : t -} - -export interface TestOutcome { - test: ExerciseTest - result: RunResult - passed: boolean -} - -/** `subprograms` findet Unterprogramme für Aufrufe, z. B. in anderen Reitern; `worlds` die Weltkarten der VisuBot-Übungen. */ -export function checkExercise( - doc: VSDocument, - tests: ExerciseTest[], - subprograms?: SubprogramResolver, - worlds?: (name: string) => RobotWorldSetup | undefined, -): TestOutcome[] { - return tests.map((test) => { - const world = test.world ? worlds?.(test.world) : undefined - if (test.world && !world) throw new Error(`Weltkarte „${test.world}“ fehlt.`) - const result = runWithInputs(doc, test.inputs, subprograms, world, test.sensors) - const filesRight = (test.files ?? []).every((f) => sameOutput(fileLines(result.files?.get(f.name)), f.lines)) - const driveRight = !test.drive || sameOutput(result.drive ?? [], test.drive) - // Hat die Welt ein Ziel, muss der Roboter hin; anstoßen darf er nie. - const worldRight = !test.world || (result.bumps === 0 && (!(world?.goals?.length || world?.targets?.length) || result.atGoal === true)) - const robotRight = - (test.led === undefined || result.led === test.led) && - (test.tone === undefined || result.tone === test.tone) && - (test.screen === undefined || result.screen === test.screen) && - (test.drives === undefined || (result.drive?.length ?? 0) > 0 === test.drives) && - (!test.actions || sameOutput(result.actions ?? [], test.actions)) - const passed = - !result.error && - !result.missingInput && - !result.tooManySteps && - sameOutput(result.outputs, test.expected) && - filesRight && - driveRight && - worldRight && - robotRight - return { test, result, passed } - }) -} - -/** Welche Elementarten im Diagramm vorkommen (für „braucht: verzweigung“). */ -export function usedElementTypes(doc: VSDocument): Set { - const found = new Set() - const visit = (els: VSElement[]) => { - for (const e of els) { - found.add(e.type) - if (e.loopBody) visit(e.loopBody) - for (const c of e.cases ?? []) visit(c.elements) - } - } - visit(doc.elements) - return found -} - -/** Wie tief Schleifen höchstens ineinander stecken (für „schleifentiefe: 2“). */ -export function loopDepth(elements: VSElement[]): number { - const loops = [ElementType.forSchleife, ElementType.whileSchleife, ElementType.doUntilSchleife, ElementType.endlosschleife] - let best = 0 - for (const e of elements) { - const inner = Math.max(loopDepth(e.loopBody ?? []), ...(e.cases ?? []).map((c) => loopDepth(c.elements))) - best = Math.max(best, inner + (loops.includes(e.type) ? 1 : 0)) - } - return best -} diff --git a/web/src/core/flowModel.ts b/web/src/core/flowModel.ts deleted file mode 100644 index 07a0d8af..00000000 --- a/web/src/core/flowModel.ts +++ /dev/null @@ -1,242 +0,0 @@ -/** - * VisuFlow: Programmablaufplan (PAP, englisch flowchart) nach DIN 66001 frei zeichnen. - * Nur Zeichnen: keine Simulation, kein Code. Ein Plan besteht aus Symbolen (Mittelpunkt x/y) - * und Pfeilen vom Ausgang eines Symbols zum Eingang (oben) eines anderen. - */ - -export type FlowShape = 'start' | 'ende' | 'anweisung' | 'ein' | 'verzweigung' | 'unterprogramm' - -/** Ausgang eines Symbols: `out` unten, bei der Raute `ja` und `nein`. */ -export type FlowPort = 'out' | 'ja' | 'nein' - -export interface FlowNode { - id: string - shape: FlowShape - x: number - y: number - text: string -} - -export interface FlowEdge { - id: string - from: string - port: FlowPort - to: string -} - -export interface FlowDoc { - title: string - nodes: FlowNode[] - edges: FlowEdge[] - /** Raute: `true` = „ja“ geht nach rechts, „nein“ nach unten (Standard umgekehrt). */ - yesRight?: string[] -} - -export const FLOW_SHAPES: FlowShape[] = ['start', 'anweisung', 'ein', 'verzweigung', 'unterprogramm', 'ende'] - -export const FLOW_GRID = 20 - -/** Füllfarben wie im Lernbaustein „PAP bauen“ (papView.ts). */ -export const FLOW_FILL: Record = { - start: '#d1f2d9', - ende: '#d1f2d9', - anweisung: '#e5f0ff', - ein: '#f2e6ff', - verzweigung: '#fff4d6', - unterprogramm: '#e5f0ff', -} - -export type Measure = (text: string) => number - -export interface FlowBox { - x: number - y: number - w: number - h: number - lines: string[] -} - -export const FLOW_LINE_HEIGHT = 16 -const MAX_TEXT = 170 - -/** Text auf Zeilen umbrechen (an Leerzeichen, harte Umbrüche bleiben). */ -export function flowLines(text: string, max: number, measure: Measure): string[] { - const out: string[] = [] - for (const para of text.split('\n')) { - let line = '' - for (const word of para.split(/\s+/).filter(Boolean)) { - const next = line ? `${line} ${word}` : word - if (line && measure(next) > max) { - out.push(line) - line = word - } else line = next - } - out.push(line) - } - return out.length ? out : [''] -} - -const snap = (v: number) => Math.round(v / FLOW_GRID) * FLOW_GRID - -/** Größe eines Symbols aus seinem Text; Breite und Höhe auf das Raster gerundet. */ -export function flowBox(node: FlowNode, measure: Measure): FlowBox { - const lines = flowLines(node.text, MAX_TEXT, measure) - const textW = Math.max(0, ...lines.map(measure)) - const textH = lines.length * FLOW_LINE_HEIGHT - let w: number - let h: number - if (node.shape === 'verzweigung') { - // Raute: Text passt in das innere Rechteck, wenn Breite und Höhe doppelt so groß sind. - w = Math.max(140, textW * 1.7 + 30) - h = Math.max(80, textH * 1.8 + 24) - } else { - const pad = node.shape === 'ein' ? 44 : node.shape === 'unterprogramm' ? 40 : node.shape === 'anweisung' ? 24 : 36 - w = Math.max(node.shape === 'start' || node.shape === 'ende' ? 100 : 120, textW + pad) - h = Math.max(40, textH + 18) - } - return { x: node.x, y: node.y, w: Math.ceil(w / FLOW_GRID) * FLOW_GRID, h: Math.ceil(h / FLOW_GRID) * FLOW_GRID, lines } -} - -/** Welche Ausgänge ein Symbol hat. */ -export function flowPorts(shape: FlowShape): FlowPort[] { - if (shape === 'ende') return [] - if (shape === 'verzweigung') return ['ja', 'nein'] - return ['out'] -} - -export const hasInput = (shape: FlowShape) => shape !== 'start' - -type Side = 'bottom' | 'right' - -/** Seite eines Ausgangs. Raute: Standard „ja“ unten, „nein“ rechts; umschaltbar. */ -export function portSide(doc: FlowDoc, node: FlowNode, port: FlowPort): Side { - if (port === 'out') return 'bottom' - const yesRight = doc.yesRight?.includes(node.id) ?? false - return (port === 'ja') === yesRight ? 'right' : 'bottom' -} - -export function portPoint(box: FlowBox, side: Side): [number, number] { - return side === 'bottom' ? [box.x, box.y + box.h / 2] : [box.x + box.w / 2, box.y] -} - -export const inputPoint = (box: FlowBox): [number, number] => [box.x, box.y - box.h / 2] - -/** - * Rechtwinkliger Weg eines Pfeils vom Ausgang zum Eingang oben. Geht es nach oben zurück (Schleife), - * läuft der Pfeil rechts an beiden Symbolen vorbei. - */ -export function routeEdge(from: FlowBox, side: Side, to: FlowBox, lane = 0): [number, number][] { - const [sx, sy] = portPoint(from, side) - const [tx, ty] = inputPoint(to) - const gap = FLOW_GRID - const outside = Math.max(from.x + from.w / 2, to.x + to.w / 2) + gap * (1.5 + lane) - if (side === 'bottom') { - if (ty >= sy + gap) { - if (sx === tx) return [[sx, sy], [tx, ty]] - const my = sy + gap - return [[sx, sy], [sx, my], [tx, my], [tx, ty]] - } - return [[sx, sy], [sx, sy + gap], [outside, sy + gap], [outside, ty - gap], [tx, ty - gap], [tx, ty]] - } - // Ausgang rechts; liegt das Ziel rechts daneben auf gleicher Höhe, endet der Pfeil links an seiner Seite. - const left = to.x - to.w / 2 - if (left >= sx + gap && Math.abs(to.y - sy) < to.h / 2) return [[sx, sy], [left, sy]] - if (ty >= sy + gap) { - if (tx >= sx + gap) return [[sx, sy], [tx, sy], [tx, ty]] - const rx = sx + gap - return [[sx, sy], [rx, sy], [rx, ty - gap], [tx, ty - gap], [tx, ty]] - } - const rx = Math.max(sx + gap, outside) - return [[sx, sy], [rx, sy], [rx, ty - gap], [tx, ty - gap], [tx, ty]] -} - -// MARK: - Bearbeiten (immer neue Objekte, damit Rückgängig einfach den alten Stand behält) - -export function emptyFlow(title = ''): FlowDoc { - return { title, nodes: [], edges: [] } -} - -export function nextId(doc: FlowDoc, prefix: 'n' | 'e'): string { - const used = new Set([...doc.nodes.map((n) => n.id), ...doc.edges.map((e) => e.id)]) - let i = 1 - while (used.has(`${prefix}${i}`)) i++ - return `${prefix}${i}` -} - -export function addNode(doc: FlowDoc, shape: FlowShape, x: number, y: number, text: string): { doc: FlowDoc; id: string } { - const id = nextId(doc, 'n') - return { doc: { ...doc, nodes: [...doc.nodes, { id, shape, x: snap(x), y: snap(y), text }] }, id } -} - -export function moveNode(doc: FlowDoc, id: string, x: number, y: number): FlowDoc { - return { ...doc, nodes: doc.nodes.map((n) => (n.id === id ? { ...n, x: Math.max(FLOW_GRID * 4, snap(x)), y: Math.max(FLOW_GRID * 2, snap(y)) } : n)) } -} - -export function setText(doc: FlowDoc, id: string, text: string): FlowDoc { - return { ...doc, nodes: doc.nodes.map((n) => (n.id === id ? { ...n, text } : n)) } -} - -/** Pfeil ziehen: Ein Ausgang hat höchstens einen Pfeil, ein neuer ersetzt den alten. */ -export function connect(doc: FlowDoc, from: string, port: FlowPort, to: string): FlowDoc { - const a = doc.nodes.find((n) => n.id === from) - const b = doc.nodes.find((n) => n.id === to) - if (!a || !b || from === to || !flowPorts(a.shape).includes(port) || !hasInput(b.shape)) return doc - const edges = doc.edges.filter((e) => !(e.from === from && e.port === port)) - return { ...doc, edges: [...edges, { id: nextId(doc, 'e'), from, port, to }] } -} - -export function freePort(doc: FlowDoc, node: FlowNode): FlowPort | undefined { - return flowPorts(node.shape).find((p) => !doc.edges.some((e) => e.from === node.id && e.port === p)) -} - -export function removeNode(doc: FlowDoc, id: string): FlowDoc { - return { - ...doc, - nodes: doc.nodes.filter((n) => n.id !== id), - edges: doc.edges.filter((e) => e.from !== id && e.to !== id), - yesRight: doc.yesRight?.filter((n) => n !== id), - } -} - -export function removeEdge(doc: FlowDoc, id: string): FlowDoc { - return { ...doc, edges: doc.edges.filter((e) => e.id !== id) } -} - -/** Raute: „ja“ und „nein“ tauschen die Seiten (die Pfeile bleiben an ihrem Ausgang). */ -export function swapYesNo(doc: FlowDoc, id: string): FlowDoc { - const set = new Set(doc.yesRight ?? []) - if (set.has(id)) set.delete(id) - else set.add(id) - return { ...doc, yesRight: [...set] } -} - -// MARK: - Datei (.visuflow, JSON) - -export function flowToJSON(doc: FlowDoc): string { - return JSON.stringify({ format: 'visuflow', version: 1, ...doc }, null, 2) -} - -export function flowFromJSON(text: string): FlowDoc | undefined { - let raw: unknown - try { - raw = JSON.parse(text) - } catch { - return undefined - } - if (!raw || typeof raw !== 'object') return undefined - const r = raw as Record - if (!Array.isArray(r.nodes) || !Array.isArray(r.edges)) return undefined - const nodes: FlowNode[] = [] - for (const n of r.nodes as Record[]) { - if (!n || typeof n.id !== 'string' || !FLOW_SHAPES.includes(n.shape as FlowShape)) continue - if (!Number.isFinite(n.x) || !Number.isFinite(n.y) || nodes.some((m) => m.id === n.id)) continue - nodes.push({ id: n.id, shape: n.shape as FlowShape, x: n.x as number, y: n.y as number, text: typeof n.text === 'string' ? n.text : '' }) - } - let doc: FlowDoc = { title: typeof r.title === 'string' ? r.title : '', nodes, edges: [] } - for (const e of r.edges as Record[]) { - if (!e || typeof e.from !== 'string' || typeof e.to !== 'string' || typeof e.port !== 'string') continue - doc = connect(doc, e.from, e.port as FlowPort, e.to) - } - if (Array.isArray(r.yesRight)) doc.yesRight = (r.yesRight as unknown[]).filter((id): id is string => typeof id === 'string' && nodes.some((n) => n.id === id)) - return doc -} diff --git a/web/src/core/javaFormat.ts b/web/src/core/javaFormat.ts deleted file mode 100644 index d7ee4d28..00000000 --- a/web/src/core/javaFormat.ts +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Java-Code formatieren wie „Formatieren“ in Eclipse, aber vorsichtig: Es ändert nur Leerraum zwischen den - * Wörtern und Zeichen, nie den Code selbst. Jede Block-Klammer `{`/`}` bekommt ihre eigene Zeile (`} else {` - * bleibt zusammen), jede Anweisung eine eigene Zeile, um Operatoren wie `>=` und `=` steht ein Leerzeichen. - * Danach rückt `indentJava` ein. Zeilen werden nur getrennt, nie zusammengezogen. - */ -import { indentJava } from './javaIndent' - -export interface FormatEdit { - from: number - to: number - insert: string -} - -type Kind = 'word' | 'num' | 'str' | 'comment' | 'op' | 'other' - -interface Token { - kind: Kind - text: string - from: number - to: number - /** Zeile (ab 1), in der das Zeichen beginnt. */ - line: number -} - -// Längste zuerst. `>>` und `>>>` fehlen absichtlich: Sie schließen oft zwei Generics wie `List>`. -const operators = ['>>>=', '<<=', '>>=', '...', '->', '::', '++', '--', '&&', '||', '==', '!=', '<=', '>=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<'] -const alwaysBinary = new Set(['=', '==', '!=', '<=', '>=', '&&', '||', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', '>>>=', '->', '&', '|', '^', '<<']) -const arithmetic = new Set(['+', '-', '*', '/', '%']) -/** Wörter, nach denen ein `-` oder `+` zur Zahl gehört (`return -1`). */ -const notValue = new Set(['return', 'case', 'throw', 'new', 'else', 'do', 'yield', 'assert', 'instanceof']) -const primitive = new Set(['byte', 'short', 'int', 'long', 'float', 'double', 'char', 'boolean']) -const control = new Set(['if', 'for', 'while', 'switch', 'catch', 'synchronized', 'try']) -const joinAfterBrace = new Set(['else', 'catch', 'finally']) - -function tokenize(text: string): Token[] { - const tokens: Token[] = [] - let line = 1 - let i = 0 - const push = (kind: Kind, end: number) => { - tokens.push({ kind, text: text.slice(i, end), from: i, to: end, line }) - for (let k = i; k < end; k++) if (text[k] === '\n') line++ - i = end - } - while (i < text.length) { - const c = text[i] - const rest = text.slice(i, i + 4) - if (c === '\n') { - line++ - i++ - } else if (/\s/.test(c)) i++ - else if (rest.startsWith('//')) { - const end = text.indexOf('\n', i) - push('comment', end < 0 ? text.length : end) - } else if (rest.startsWith('/*')) { - const end = text.indexOf('*/', i + 2) - push('comment', end < 0 ? text.length : end + 2) - } else if (rest.startsWith('"""')) { - const end = text.indexOf('"""', i + 3) - push('str', end < 0 ? text.length : end + 3) - } else if (c === '"' || c === "'") { - let k = i + 1 - while (k < text.length && text[k] !== c && text[k] !== '\n') k += text[k] === '\\' ? 2 : 1 - push('str', Math.min(text.length, k + 1)) - } else if (/[\d]/.test(c) || (c === '.' && /\d/.test(text[i + 1] ?? ''))) { - const m = /^(?:0[xXbB][\da-fA-F_]+|[\d_]*\.?[\d_]*(?:[eE][+-]?\d+)?)[a-zA-Z]*/.exec(text.slice(i))! - push('num', i + Math.max(1, m[0].length)) - } else if (/[\p{L}_$]/u.test(c)) { - const m = /^[\p{L}\p{N}_$]+/u.exec(text.slice(i))! - push('word', i + m[0].length) - } else { - const op = operators.find((o) => text.startsWith(o, i)) - push(op ? 'op' : 'other', i + (op?.length ?? 1)) - } - } - return tokens -} - -/** Endet hier ein Wert, sodass ein folgendes `-` rechnet statt ein Vorzeichen zu sein? */ -function endsValue(tokens: Token[], i: number): boolean { - const t = tokens[i] - if (!t) return false - if (t.kind === 'num' || t.kind === 'str') return true - if (t.kind === 'word') return !notValue.has(t.text) - if (t.text === ']') return true - // `(int) -x` ist eine Typumwandlung mit Vorzeichen. - if (t.text === ')') return !(primitive.has(tokens[i - 1]?.text ?? '') && tokens[i - 2]?.text === '(') - return false -} - -const upper = (t: Token | undefined) => t?.kind === 'word' && /^\p{Lu}/u.test(t.text) - -/** Änderungen am Leerraum, nur in den Zeilen `fromLine`…`toLine` (ab 1). */ -export function formatJavaEdits(text: string, fromLine = 1, toLine = Number.MAX_SAFE_INTEGER): FormatEdit[] { - const tokens = tokenize(text) - const n = tokens.length - const binary: boolean[] = new Array(n).fill(false) - const blockBrace: boolean[] = new Array(n).fill(false) - const match: number[] = new Array(n).fill(-1) - const afterDo: boolean[] = new Array(n).fill(false) - const statementEnd: boolean[] = new Array(n).fill(false) - - // Klammern zuordnen: Block-Klammern gegen Array-Initialisierer, Klammertiefe je Block für `for (…;…;…)`. - const frames: { at: number; block: boolean; paren: number }[] = [{ at: -1, block: true, paren: 0 }] - let generic = 0 - let ternary = 0 - tokens.forEach((t, i) => { - const prev = tokens[i - 1] - const top = frames.at(-1)! - if (t.kind === 'comment') return - if (t.text === '(') top.paren++ - else if (t.text === ')') top.paren = Math.max(0, top.paren - 1) - else if (t.text === '{') { - const p = prev?.text ?? '' - const init = ['=', ',', ']', '('].includes(p) || (p === '{' && !blockBrace[i - 1]) - blockBrace[i] = !init - afterDo[i] = p === 'do' - frames.push({ at: i, block: !init, paren: 0 }) - } else if (t.text === '}') { - if (frames.length > 1) { - const open = frames.pop()! - blockBrace[i] = open.block - match[i] = open.at - match[open.at] = i - afterDo[i] = afterDo[open.at] - } else blockBrace[i] = true - } else if (t.text === ';') { - statementEnd[i] = top.block && top.paren === 0 - ternary = 0 - } else if (t.text === '?' && prev?.text !== '<' && ![')', ';', ',', undefined].includes(tokens[i + 1]?.text)) { - // Ein Fragezeichen am Ende wie in `if (Alter über 18?)` ist Pseudocode, kein `a ? b : c`. - binary[i] = true - ternary++ - } else if (t.text === ':') { - // `a ? b : c` und `for (int x : zahlen)`, aber nicht `case 1:`. - binary[i] = ternary > 0 || top.paren > 0 - if (ternary > 0) ternary-- - } else if (t.text === '<') { - const next = tokens[i + 1] - if ((upper(prev) || prev?.text === '.') && (upper(next) || next?.text === '>' || next?.text === '?')) generic++ - else binary[i] = !(prev?.text === '<' || next?.text === '<') - } else if (t.text === '>') { - if (generic > 0) generic-- - else binary[i] = !(prev?.text === '>' || tokens[i + 1]?.text === '>') - } else if (alwaysBinary.has(t.text)) binary[i] = true - else if (arithmetic.has(t.text)) binary[i] = endsValue(tokens, i - 1) - }) - - const edits: FormatEdit[] = [] - for (let i = 1; i < n; i++) { - const a = tokens[i - 1] - const b = tokens[i] - const gap = text.slice(a.to, b.from) - if (gap.includes('\n') || a.line < fromLine || a.line > toLine) continue - if (a.kind === 'comment') continue - let want: string | undefined - if (b.kind === 'comment') want = undefined - else if (blockBrace[i - 1] && a.text === '{' && match[i - 1] !== i) want = '\n' - else if (blockBrace[i] && b.text === '}' && match[i] !== i - 1) want = '\n' - else if (blockBrace[i - 1] && a.text === '}') { - if (joinAfterBrace.has(b.text) || (b.text === 'while' && afterDo[i - 1])) want = ' ' - else if (![')', ';', ',', '.'].includes(b.text)) want = '\n' - } else if (statementEnd[i - 1]) want = '\n' - else if (b.text === ',' || b.text === ';') want = '' - else if (a.text === ',') want = ' ' - else if (a.text === ';') want = b.text === ')' ? '' : ' ' - else if (a.text === '(' || b.text === ')') want = '' - else if (b.text === '(' && control.has(a.text)) want = ' ' - else if (b.text === '{' && blockBrace[i]) want = ' ' - else if (binary[i - 1] || binary[i]) want = ' ' - if (want !== undefined && want !== gap) edits.push({ from: a.to, to: b.from, insert: want }) - } - return edits -} - -/** Code formatieren und einrücken; nur die Zeilen `fromLine`…`toLine` (ab 1) werden angefasst. */ -export function formatJava(text: string, fromLine = 1, toLine = Number.MAX_SAFE_INTEGER): string { - const edits = formatJavaEdits(text, fromLine, toLine) - let out = '' - let pos = 0 - for (const e of edits) { - out += text.slice(pos, e.from) + e.insert - pos = e.to - } - out += text.slice(pos) - // Die angefassten Zeilen nach dem Trennen: Neue Zeilenumbrüche verschieben das Ende nach unten. - const lines = text.split('\n').length - const added = edits.reduce((sum, e) => sum + (e.insert === '\n' ? 1 : 0), 0) - const to = toLine >= lines ? Number.MAX_SAFE_INTEGER : toLine + added - return indentJava(out, fromLine, to) -} diff --git a/web/src/core/javaIndent.ts b/web/src/core/javaIndent.ts deleted file mode 100644 index 04434c7d..00000000 --- a/web/src/core/javaIndent.ts +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Java-Code einrücken wie Eclipse oder IntelliJ: vier Leerzeichen je Ebene, `case` eine Ebene tiefer als - * `switch`, die Anweisungen eines klassischen `case …:` noch eine tiefer. Zählt nur Klammern, damit auch - * halbfertiger Code und Pseudocode wie `if (Alter über 18?)` sauber eingerückt werden. - */ - -const unit = ' ' - -interface Open { - /** Zeile, in der die Klammer aufgeht: Mehrere Klammern in einer Zeile ergeben nur eine Ebene. */ - line: number - char: string - /** `{` eines klassischen `switch`, in dem gerade ein `case …:` offen ist. */ - inCase?: boolean -} - -/** Klammern einer Zeile ohne Zeichenketten und Kommentare; `comment` sagt, ob ein Blockkommentar offen bleibt. */ -function brackets(text: string, inComment: boolean): { chars: string; comment: boolean } { - let chars = '' - let comment = inComment - for (let i = 0; i < text.length; i++) { - const c = text[i] - if (comment) { - if (c === '*' && text[i + 1] === '/') { - comment = false - i++ - } - } else if (c === '/' && text[i + 1] === '/') break - else if (c === '/' && text[i + 1] === '*') { - comment = true - i++ - } else if (c === '"' || c === "'") { - for (i++; i < text.length && text[i] !== c; i++) if (text[i] === '\\') i++ - } else if ('{}()[]'.includes(c)) chars += c - } - return { chars, comment } -} - -/** Ebene: wie viele verschiedene Zeilen noch offene Klammern haben, plus ein offenes `case`. */ -function level(stack: Open[]): number { - let n = 0 - let last = -1 - for (const open of stack) { - if (open.line !== last) n++ - last = open.line - if (open.inCase) n++ - } - return n -} - -const caseLabel = /^(case\b|default\b)/ -const classicCase = /^(case\b.*|default\s*):\s*(\/\/.*)?$/ - -/** Zeilen `fromLine`…`toLine` (ab 1) neu einrücken; die übrigen bleiben, wie sie sind. */ -export function indentJava(text: string, fromLine = 1, toLine = Number.MAX_SAFE_INTEGER): string { - const stack: Open[] = [] - let comment = false - return text - .split('\n') - .map((line, i) => { - const trimmed = line.trim() - const inComment = comment - const { chars, comment: stillComment } = brackets(line, comment) - comment = stillComment - // Schließende Klammern am Zeilenanfang gehören schon zur äußeren Ebene. - const lead = inComment ? 0 : (/^[}\])\s]*/.exec(trimmed)![0].match(/[}\])]/g)?.length ?? 0) - const outer = stack.slice(0, Math.max(0, stack.length - lead)) - if (caseLabel.test(trimmed) && outer.at(-1)?.inCase) outer.at(-1)!.inCase = false - const depth = level(outer) - for (const c of chars) { - if ('{[('.includes(c)) stack.push({ line: i, char: c }) - else stack.pop() - } - const top = stack.at(-1) - if (top?.line === i && top.char === '{' && /^switch\b/.test(trimmed)) top.char = 'switch' - if (top?.char === 'switch' && classicCase.test(trimmed)) top.inCase = true - const n = i + 1 - // Zeilen in einem Blockkommentar bleiben, wie sie sind. - if (n < fromLine || n > toLine || (inComment && !trimmed.startsWith('*'))) return line - if (trimmed === '') return '' - return unit.repeat(depth) + (inComment ? ' ' : '') + trimmed - }) - .join('\n') -} diff --git a/web/src/core/javaMembers.ts b/web/src/core/javaMembers.ts deleted file mode 100644 index 76ef5d7e..00000000 --- a/web/src/core/javaMembers.ts +++ /dev/null @@ -1,507 +0,0 @@ -/** - * Vorschläge nach dem Punkt: `eingabe.` → nextInt(), nextLine() … - * Der Typ kommt aus der Deklaration im eigenen Code (`Scanner eingabe = …`), der Variablenname ist beliebig. - * Nur die Klassen und Methoden, die in den Lektionen vorkommen, mit kurzer Beschreibung für Anfänger. - */ - -export type MemberLanguage = 'de' | 'en' | 'pt' - -export interface JavaMember { - name: string - /** Methode (mit Klammern) oder Feld wie `length` bei Arrays. */ - kind: 'method' | 'field' - /** Methode erwartet Werte in den Klammern. */ - params: boolean - /** Typ des Ergebnisses, für Ketten wie `eingabe.nextLine().` */ - returns: string - description: string -} - -/** [Name, Klammerinhalt (leer = keine Werte, null = Feld), Ergebnistyp, de, en, pt] */ -type Row = [string, string | null, string, string, string, string] - -const scanner: Row[] = [ - ['nextInt', '', 'int', 'Ganze Zahl einlesen', 'Read a whole number', 'Ler um número inteiro'], - ['nextDouble', '', 'double', 'Kommazahl einlesen', 'Read a decimal number', 'Ler um número decimal'], - ['nextLine', '', 'String', 'Ganze Zeile als Text einlesen', 'Read a whole line as text', 'Ler uma linha inteira como texto'], - ['next', '', 'String', 'Ein Wort einlesen (bis zum Leerzeichen)', 'Read one word (up to a space)', 'Ler uma palavra (até ao espaço)'], - ['nextBoolean', '', 'boolean', 'true oder false einlesen', 'Read true or false', 'Ler true ou false'], - ['nextLong', '', 'long', 'Große ganze Zahl einlesen', 'Read a large whole number', 'Ler um número inteiro grande'], - ['hasNextInt', '', 'boolean', 'Kommt als Nächstes eine ganze Zahl?', 'Is the next input a whole number?', 'A próxima entrada é um número inteiro?'], - ['hasNextDouble', '', 'boolean', 'Kommt als Nächstes eine Kommazahl?', 'Is the next input a decimal number?', 'A próxima entrada é um número decimal?'], - ['hasNextLine', '', 'boolean', 'Gibt es noch eine Zeile?', 'Is there another line?', 'Existe mais uma linha?'], - ['hasNext', '', 'boolean', 'Gibt es noch ein Wort?', 'Is there another word?', 'Existe mais uma palavra?'], - ['close', '', 'void', 'Eingabe schließen', 'Close the input', 'Fechar a entrada'], -] - -const string: Row[] = [ - ['length', '', 'int', 'Anzahl der Zeichen', 'Number of characters', 'Número de caracteres'], - ['charAt', 'index', 'char', 'Zeichen an Stelle index (ab 0)', 'Character at position index (from 0)', 'Carácter na posição index (a partir de 0)'], - ['equals', 'text', 'boolean', 'Gleicher Text? (statt ==)', 'Same text? (instead of ==)', 'Mesmo texto? (em vez de ==)'], - ['equalsIgnoreCase', 'text', 'boolean', 'Gleicher Text, groß/klein egal?', 'Same text, ignoring case?', 'Mesmo texto, ignorando maiúsculas?'], - ['compareTo', 'text', 'int', 'Alphabetisch vergleichen (<0, 0, >0)', 'Compare alphabetically (<0, 0, >0)', 'Comparar alfabeticamente (<0, 0, >0)'], - ['substring', 'von, bis', 'String', 'Teiltext von Stelle von bis vor bis', 'Part of the text from start up to before end', 'Parte do texto do início até antes do fim'], - ['indexOf', 'text', 'int', 'Erste Stelle des Textes (−1 = fehlt)', 'First position of the text (−1 = missing)', 'Primeira posição do texto (−1 = não existe)'], - ['lastIndexOf', 'text', 'int', 'Letzte Stelle des Textes (−1 = fehlt)', 'Last position of the text (−1 = missing)', 'Última posição do texto (−1 = não existe)'], - ['contains', 'text', 'boolean', 'Kommt der Text darin vor?', 'Does it contain the text?', 'Contém o texto?'], - ['startsWith', 'text', 'boolean', 'Fängt damit an?', 'Starts with it?', 'Começa com isto?'], - ['endsWith', 'text', 'boolean', 'Hört damit auf?', 'Ends with it?', 'Termina com isto?'], - ['toUpperCase', '', 'String', 'In GROSSBUCHSTABEN', 'In UPPER CASE', 'Em MAIÚSCULAS'], - ['toLowerCase', '', 'String', 'In kleinbuchstaben', 'In lower case', 'Em minúsculas'], - ['trim', '', 'String', 'Leerzeichen vorn und hinten weg', 'Remove spaces at both ends', 'Remover espaços no início e no fim'], - ['replace', 'alt, neu', 'String', 'Text ersetzen', 'Replace text', 'Substituir texto'], - ['split', 'trenner', 'String[]', 'In Teile zerlegen, z. B. split(";")', 'Split into parts, e.g. split(";")', 'Dividir em partes, p. ex. split(";")'], - ['isEmpty', '', 'boolean', 'Leerer Text?', 'Empty text?', 'Texto vazio?'], - ['toCharArray', '', 'char[]', 'Als Array von Zeichen', 'As an array of characters', 'Como array de caracteres'], -] - -const printStream: Row[] = [ - ['println', 'wert', 'void', 'Ausgeben, danach neue Zeile', 'Print, then a new line', 'Escrever e mudar de linha'], - ['print', 'wert', 'void', 'Ausgeben ohne neue Zeile', 'Print without a new line', 'Escrever sem mudar de linha'], - ['printf', 'format, werte', 'PrintStream', 'Formatiert ausgeben, z. B. "%.2f"', 'Print formatted, e.g. "%.2f"', 'Escrever formatado, p. ex. "%.2f"'], -] - -const printWriter: Row[] = [...printStream, ['close', '', 'void', 'Datei speichern und schließen', 'Save and close the file', 'Guardar e fechar o ficheiro']] - -const bufferedReader: Row[] = [ - ['readLine', '', 'String', 'Nächste Zeile lesen (null = Ende)', 'Read the next line (null = end)', 'Ler a próxima linha (null = fim)'], - ['close', '', 'void', 'Datei schließen', 'Close the file', 'Fechar o ficheiro'], -] - -const file: Row[] = [ - ['exists', '', 'boolean', 'Gibt es die Datei?', 'Does the file exist?', 'O ficheiro existe?'], - ['getName', '', 'String', 'Name der Datei', 'Name of the file', 'Nome do ficheiro'], - ['length', '', 'long', 'Größe in Bytes', 'Size in bytes', 'Tamanho em bytes'], -] - -const exception: Row[] = [ - ['getMessage', '', 'String', 'Fehlermeldung als Text', 'Error message as text', 'Mensagem de erro como texto'], - ['printStackTrace', '', 'void', 'Fehler mit Zeilen ausgeben', 'Print the error with its lines', 'Mostrar o erro com as linhas'], -] - -const actionEvent: Row[] = [ - ['getSource', '', 'Object', 'Welcher Knopf wurde geklickt?', 'Which button was clicked?', 'Que botão foi clicado?'], - ['getActionCommand', '', 'String', 'Beschriftung des Knopfs', 'Label of the button', 'Texto do botão'], -] - -// Gemeinsam für alle Swing-Bausteine (JFrame, JButton, JLabel, JTextField, JPanel). -const component: Row[] = [ - ['setBackground', 'farbe', 'void', 'Hintergrundfarbe, z. B. Color.RED', 'Background colour, e.g. Color.RED', 'Cor de fundo, p. ex. Color.RED'], - ['setForeground', 'farbe', 'void', 'Schriftfarbe', 'Text colour', 'Cor do texto'], - ['setFont', 'schrift', 'void', 'Schrift, z. B. new Font("Arial", Font.BOLD, 24)', 'Font, e.g. new Font("Arial", Font.BOLD, 24)', 'Letra, p. ex. new Font("Arial", Font.BOLD, 24)'], - ['setVisible', 'true', 'void', 'Anzeigen (true) oder verstecken (false)', 'Show (true) or hide (false)', 'Mostrar (true) ou esconder (false)'], - ['setEnabled', 'true', 'void', 'Anklickbar (true) oder grau (false)', 'Clickable (true) or greyed out (false)', 'Clicável (true) ou cinzento (false)'], - ['setSize', 'breite, höhe', 'void', 'Größe in Pixeln', 'Size in pixels', 'Tamanho em píxeis'], -] - -const container: Row[] = [ - ['add', 'baustein', 'Component', 'Baustein hinzufügen', 'Add a component', 'Adicionar um componente'], - ['setLayout', 'layout', 'void', 'Anordnung, z. B. new FlowLayout()', 'Layout, e.g. new FlowLayout()', 'Disposição, p. ex. new FlowLayout()'], - ['remove', 'baustein', 'void', 'Baustein entfernen', 'Remove a component', 'Remover um componente'], -] - -const jframe: Row[] = [ - ['setTitle', 'text', 'void', 'Titel oben im Fenster', 'Title at the top of the window', 'Título no topo da janela'], - ['setDefaultCloseOperation', 'JFrame.EXIT_ON_CLOSE', 'void', 'Was beim Schließen passiert', 'What happens when closing', 'O que acontece ao fechar'], - ['setLocationRelativeTo', 'null', 'void', 'Mit null: Fenster in die Mitte', 'With null: centre the window', 'Com null: janela no centro'], - ['setResizable', 'false', 'void', 'Größe änderbar?', 'Can the size change?', 'Pode mudar de tamanho?'], - ['pack', '', 'void', 'Größe passend zum Inhalt', 'Size to fit the content', 'Tamanho ajustado ao conteúdo'], - ['getContentPane', '', 'JPanel', 'Inhaltsfläche des Fensters', 'Content area of the window', 'Área de conteúdo da janela'], - ['dispose', '', 'void', 'Fenster schließen', 'Close the window', 'Fechar a janela'], -] - -const textHolder: Row[] = [ - ['setText', 'text', 'void', 'Text setzen', 'Set the text', 'Definir o texto'], - ['getText', '', 'String', 'Text holen', 'Get the text', 'Obter o texto'], -] - -const jbutton: Row[] = [ - ['addActionListener', 'e -> { }', 'void', 'Was beim Klick passiert', 'What happens on click', 'O que acontece ao clicar'], - ['setOpaque', 'true', 'void', 'Hintergrund sichtbar (für Farben)', 'Show the background (for colours)', 'Mostrar o fundo (para cores)'], - ['setBorderPainted', 'false', 'void', 'Rahmen zeichnen?', 'Draw the border?', 'Desenhar a borda?'], -] - -const jlabel: Row[] = [ - ['setOpaque', 'true', 'void', 'Hintergrund sichtbar (für Farben)', 'Show the background (for colours)', 'Mostrar o fundo (para cores)'], - ['setHorizontalAlignment', 'JLabel.CENTER', 'void', 'Text links, mittig oder rechts', 'Text left, centre or right', 'Texto à esquerda, centro ou direita'], -] - -const jtextfield: Row[] = [ - ['addActionListener', 'e -> { }', 'void', 'Was bei Enter passiert', 'What happens on Enter', 'O que acontece ao carregar em Enter'], - ['setEditable', 'false', 'void', 'Darf man hineinschreiben?', 'Can you type into it?', 'Pode-se escrever?'], - ['setColumns', 'anzahl', 'void', 'Breite in Zeichen', 'Width in characters', 'Largura em caracteres'], - ['requestFocus', '', 'void', 'Cursor hineinsetzen', 'Put the cursor into it', 'Pôr o cursor no campo'], -] - -const array: Row[] = [['length', null, 'int', 'Anzahl der Elemente (ohne Klammern)', 'Number of elements (no brackets)', 'Número de elementos (sem parênteses)']] - -/** `E` = Typ der Elemente, z. B. `Spieler` bei `ArrayList`. */ -const arrayList: Row[] = [ - ['add', 'wert', 'boolean', 'Element hinten anhängen', 'Append an element at the end', 'Acrescentar um elemento no fim'], - ['get', 'index', 'E', 'Element an Stelle index holen (ab 0)', 'Get the element at index (from 0)', 'Obter o elemento na posição index (a partir de 0)'], - ['size', '', 'int', 'Anzahl der Elemente', 'Number of elements', 'Número de elementos'], - ['set', 'index, wert', 'E', 'Element an Stelle index ersetzen', 'Replace the element at index', 'Substituir o elemento na posição index'], - ['remove', 'index', 'E', 'Element an Stelle index entfernen', 'Remove the element at index', 'Remover o elemento na posição index'], - ['contains', 'wert', 'boolean', 'Ist das Element in der Liste?', 'Is the element in the list?', 'O elemento está na lista?'], - ['indexOf', 'wert', 'int', 'Stelle des Elements (-1 = nicht da)', 'Position of the element (-1 = not found)', 'Posição do elemento (-1 = não existe)'], - ['isEmpty', '', 'boolean', 'Ist die Liste leer?', 'Is the list empty?', 'A lista está vazia?'], - ['clear', '', 'void', 'Alle Elemente entfernen', 'Remove all elements', 'Remover todos os elementos'], -] - -/** Methoden an Objekten; Basisklassen über `extendsOf`. */ -const instance: Record = { - Scanner: scanner, - String: string, - PrintStream: printStream, - PrintWriter: printWriter, - BufferedReader: bufferedReader, - FileReader: [['close', '', 'void', 'Datei schließen', 'Close the file', 'Fechar o ficheiro']], - File: file, - Exception: exception, - ActionEvent: actionEvent, - Component: component, - Container: container, - JFrame: jframe, - JPanel: [], - JButton: jbutton, - JLabel: jlabel, - JTextField: jtextfield, - TextHolder: textHolder, - ArrayList: arrayList, - List: arrayList, - LinkedList: arrayList, - // `mbot.led(0).` → setColor - LED: [['setColor', 'farbe', 'void', 'Farbe der LED, z. B. "grün" oder "aus"', 'Colour of the LED, e.g. "green" or "off"', 'Cor do LED, p. ex. "green" ou "off"']], -} - -const extendsOf: Record = { - JFrame: ['Container', 'Component'], - JPanel: ['Container', 'Component'], - JButton: ['TextHolder', 'Component'], - JLabel: ['TextHolder', 'Component'], - JTextField: ['TextHolder', 'Component'], - PrintWriter: [], -} - -/** Klassenname mit Punkt: `Math.`, `System.`, `Integer.` … */ -const statics: Record = { - // mBot2 (siehe mbot.ts): fester Name wie `System.out`, ohne eigenes Objekt anzulegen. - mbot: [ - ['forward', 'cm', 'void', 'Geradeaus vorwärts fahren', 'Drive forward', 'Andar para a frente'], - ['forward', 'wert, "Umdrehungen"', 'void', 'Überladen: mit Einheit "cm" oder "Umdrehungen" (1 Umdrehung ≈ 20,4 cm)', 'Overloaded: with unit "cm" or "rotations" (1 rotation ≈ 20.4 cm)', 'Sobrecarregado: com unidade "cm" ou "rotações" (1 rotação ≈ 20,4 cm)'], - ['backward', 'cm', 'void', 'Rückwärts fahren', 'Drive backward', 'Andar para trás'], - ['backward', 'wert, "Umdrehungen"', 'void', 'Überladen: rückwärts mit Einheit "cm" oder "Umdrehungen"', 'Overloaded: backward with unit "cm" or "rotations"', 'Sobrecarregado: para trás com unidade "cm" ou "rotações"'], - ['turnLeft', 'grad', 'void', 'Auf der Stelle nach links drehen', 'Turn left on the spot', 'Rodar para a esquerda'], - ['turnRight', 'grad', 'void', 'Auf der Stelle nach rechts drehen', 'Turn right on the spot', 'Rodar para a direita'], - ['stop', '', 'void', 'Motoren anhalten', 'Stop the motors', 'Parar os motores'], - ['drive', 'links, rechts', 'void', 'Räder einzeln in Prozent (−100–100), fährt weiter bis stop()', 'Wheels separately in percent (−100–100), keeps going until stop()', 'Rodas separadas em percentagem (−100–100), continua até stop()'], - ['resetTimer', '', 'void', 'Stoppuhr auf 0 setzen', 'Reset the stopwatch to 0', 'Pôr o cronómetro a 0'], - ['pause', 'ms', 'void', 'Millisekunden warten', 'Wait milliseconds', 'Esperar milissegundos'], - ['setLed', 'rot, gruen, blau', 'void', 'LEDs in einer Farbe (je 0–255)', 'LEDs in a colour (0–255 each)', 'LEDs numa cor (0–255 cada)'], - ['ledOff', '', 'void', 'LEDs aus', 'LEDs off', 'Desligar LEDs'], - ['led', 'nr', 'LED', 'Eine LED (0–4), dann .setColor("grün") oder .setColor("aus")', 'One LED (0–4), then .setColor("green") or .setColor("off")', 'Um LED (0–4), depois .setColor("green") ou .setColor("off")'], - ['showText', '"Hallo"', 'void', 'Text auf dem Bildschirm zeigen', 'Show text on the screen', 'Mostrar texto no ecrã'], - ['emotion', '"smile"', 'void', 'Gefühl zeigen: LEDs, Ton, Bewegung (smile, hello, sad …)', 'Show a feeling: LEDs, sound, motion (smile, hello, sad …)', 'Mostrar um sentimento: LEDs, som, movimento (smile, hello, sad …)'], - ['playTone', 'hz, ms', 'void', 'Ton spielen, z. B. playTone(440, 500)', 'Play a tone, e.g. playTone(440, 500)', 'Tocar um som, p. ex. playTone(440, 500)'], - ['getDistance', '', 'int', 'Abstand vorn in cm (Ultraschall)', 'Distance ahead in cm (ultrasonic)', 'Distância à frente em cm (ultrassom)'], - ['getLine', '', 'int', 'Liniensensor: 0 = keine Linie, 15 = alle vier auf der Linie', 'Line sensor: 0 = no line, 15 = all four on the line', 'Sensor de linha: 0 = sem linha, 15 = os quatro na linha'], - ['isLineLeft', '', 'boolean', 'Linie unter dem linken mittleren Fühler', 'Line under the left middle probe', 'Linha sob a sonda central esquerda'], - ['isLineRight', '', 'boolean', 'Linie unter dem rechten mittleren Fühler', 'Line under the right middle probe', 'Linha sob a sonda central direita'], - ['getBrightness', '', 'int', 'Helligkeit 0–100', 'Brightness 0–100', 'Luminosidade 0–100'], - ['getLoudness', '', 'int', 'Lautstärke 0–100', 'Loudness 0–100', 'Volume 0–100'], - ['getColor', '', 'String', 'Farbe unter dem mBot2, z. B. "rot"', 'Colour under the mBot2, e.g. "red"', 'Cor por baixo do mBot2, p. ex. "red"'], - ['isButtonA', '', 'boolean', 'Taste A ist gedrückt', 'Button A is pressed', 'O botão A está premido'], - ['isButtonB', '', 'boolean', 'Taste B ist gedrückt', 'Button B is pressed', 'O botão B está premido'], - ['isJoystickUp', '', 'boolean', 'Joystick nach oben gedrückt', 'Joystick pushed up', 'Joystick para cima'], - ['isJoystickDown', '', 'boolean', 'Joystick nach unten gedrückt', 'Joystick pushed down', 'Joystick para baixo'], - ['isJoystickLeft', '', 'boolean', 'Joystick nach links gedrückt', 'Joystick pushed left', 'Joystick para a esquerda'], - ['isJoystickRight', '', 'boolean', 'Joystick nach rechts gedrückt', 'Joystick pushed right', 'Joystick para a direita'], - ['getPitch', '', 'int', 'Neigung nach vorn (+) oder hinten (−) in Grad', 'Tilt forward (+) or back (−) in degrees', 'Inclinação para a frente (+) ou trás (−) em graus'], - ['getRoll', '', 'int', 'Neigung zur Seite in Grad', 'Tilt sideways in degrees', 'Inclinação lateral em graus'], - ['isShaken', '', 'boolean', 'Der mBot2 wird geschüttelt', 'The mBot2 is being shaken', 'O mBot2 está a ser abanado'], - ['getTimer', '', 'double', 'Stoppuhr in Sekunden', 'Stopwatch in seconds', 'Cronómetro em segundos'], - ['getBattery', '', 'int', 'Akku in Prozent', 'Battery in percent', 'Bateria em percentagem'], - ], - System: [ - ['out', null, 'PrintStream', 'Ausgabe in die Konsole', 'Output to the console', 'Saída para a consola'], - ['in', null, 'InputStream', 'Eingabe von der Tastatur (für Scanner)', 'Keyboard input (for Scanner)', 'Entrada do teclado (para Scanner)'], - ['err', null, 'PrintStream', 'Fehlerausgabe', 'Error output', 'Saída de erros'], - ['currentTimeMillis', '', 'long', 'Uhrzeit in Millisekunden', 'Time in milliseconds', 'Hora em milissegundos'], - ['exit', '0', 'void', 'Programm beenden', 'End the program', 'Terminar o programa'], - ], - Math: [ - ['random', '', 'double', 'Zufallszahl von 0 bis unter 1', 'Random number from 0 to below 1', 'Número aleatório de 0 até menos de 1'], - ['abs', 'zahl', 'double', 'Betrag (ohne Minus)', 'Absolute value (no minus)', 'Valor absoluto (sem menos)'], - ['sqrt', 'zahl', 'double', 'Wurzel', 'Square root', 'Raiz quadrada'], - ['pow', 'basis, exponent', 'double', 'Hoch: pow(2, 3) = 8', 'Power: pow(2, 3) = 8', 'Potência: pow(2, 3) = 8'], - ['max', 'a, b', 'double', 'Größere von zwei Zahlen', 'Larger of two numbers', 'Maior de dois números'], - ['min', 'a, b', 'double', 'Kleinere von zwei Zahlen', 'Smaller of two numbers', 'Menor de dois números'], - ['round', 'zahl', 'long', 'Runden', 'Round', 'Arredondar'], - ['floor', 'zahl', 'double', 'Abrunden', 'Round down', 'Arredondar para baixo'], - ['ceil', 'zahl', 'double', 'Aufrunden', 'Round up', 'Arredondar para cima'], - ['PI', null, 'double', 'Kreiszahl π = 3.14159…', 'Pi π = 3.14159…', 'Pi π = 3,14159…'], - ], - Integer: [ - ['parseInt', 'text', 'int', 'Text in ganze Zahl umwandeln', 'Turn text into a whole number', 'Converter texto em número inteiro'], - ['toString', 'zahl', 'String', 'Zahl in Text umwandeln', 'Turn a number into text', 'Converter número em texto'], - ['MAX_VALUE', null, 'int', 'Größte int-Zahl', 'Largest int value', 'Maior valor int'], - ['MIN_VALUE', null, 'int', 'Kleinste int-Zahl', 'Smallest int value', 'Menor valor int'], - ], - Double: [ - ['parseDouble', 'text', 'double', 'Text in Kommazahl umwandeln', 'Turn text into a decimal number', 'Converter texto em número decimal'], - ['toString', 'zahl', 'String', 'Zahl in Text umwandeln', 'Turn a number into text', 'Converter número em texto'], - ], - String: [ - ['valueOf', 'wert', 'String', 'Wert in Text umwandeln', 'Turn a value into text', 'Converter valor em texto'], - ['format', 'format, werte', 'String', 'Formatierter Text, z. B. "%.2f"', 'Formatted text, e.g. "%.2f"', 'Texto formatado, p. ex. "%.2f"'], - ['join', 'trenner, teile', 'String', 'Teile mit Trenner verbinden', 'Join parts with a separator', 'Juntar partes com um separador'], - ], - Character: [ - ['isDigit', 'zeichen', 'boolean', 'Ist es eine Ziffer?', 'Is it a digit?', 'É um algarismo?'], - ['isLetter', 'zeichen', 'boolean', 'Ist es ein Buchstabe?', 'Is it a letter?', 'É uma letra?'], - ['isUpperCase', 'zeichen', 'boolean', 'Ist es ein Großbuchstabe?', 'Is it upper case?', 'É maiúscula?'], - ['toUpperCase', 'zeichen', 'char', 'In Großbuchstaben', 'To upper case', 'Para maiúscula'], - ['toLowerCase', 'zeichen', 'char', 'In Kleinbuchstaben', 'To lower case', 'Para minúscula'], - ], - Files: [ - ['readString', 'Path.of("datei.txt")', 'String', 'Ganze Datei als Text lesen', 'Read the whole file as text', 'Ler o ficheiro inteiro como texto'], - ['writeString', 'pfad, text', 'Path', 'Text in Datei schreiben', 'Write text to a file', 'Escrever texto num ficheiro'], - ['readAllLines', 'pfad', 'List', 'Alle Zeilen lesen', 'Read all lines', 'Ler todas as linhas'], - ['exists', 'pfad', 'boolean', 'Gibt es die Datei?', 'Does the file exist?', 'O ficheiro existe?'], - ], - Path: [['of', '"datei.txt"', 'Path', 'Pfad zu einer Datei', 'Path to a file', 'Caminho para um ficheiro']], - JFrame: [['EXIT_ON_CLOSE', null, 'int', 'Programm endet beim Schließen', 'Program ends when closed', 'O programa termina ao fechar']], - JLabel: [ - ['CENTER', null, 'int', 'Mittig', 'Centred', 'Ao centro'], - ['LEFT', null, 'int', 'Links', 'Left', 'À esquerda'], - ['RIGHT', null, 'int', 'Rechts', 'Right', 'À direita'], - ], - Font: [ - ['BOLD', null, 'int', 'Fett', 'Bold', 'Negrito'], - ['PLAIN', null, 'int', 'Normal', 'Plain', 'Normal'], - ['ITALIC', null, 'int', 'Kursiv', 'Italic', 'Itálico'], - ], - Color: ( - [ - ['RED', 'Rot', 'Red', 'Vermelho'], - ['GREEN', 'Grün', 'Green', 'Verde'], - ['BLUE', 'Blau', 'Blue', 'Azul'], - ['YELLOW', 'Gelb', 'Yellow', 'Amarelo'], - ['ORANGE', 'Orange', 'Orange', 'Laranja'], - ['WHITE', 'Weiß', 'White', 'Branco'], - ['BLACK', 'Schwarz', 'Black', 'Preto'], - ['GRAY', 'Grau', 'Grey', 'Cinzento'], - ['LIGHT_GRAY', 'Hellgrau', 'Light grey', 'Cinzento-claro'], - ['PINK', 'Rosa', 'Pink', 'Rosa'], - ['CYAN', 'Türkis', 'Cyan', 'Ciano'], - ['MAGENTA', 'Magenta', 'Magenta', 'Magenta'], - ] as const - ).map(([name, de, en, pt]): Row => [name, null, 'Color', de, en, pt]), -} - -function toMember(row: Row, lang: MemberLanguage): JavaMember { - const [name, params, returns, de, en, pt] = row - return { name, kind: params === null ? 'field' : 'method', params: !!params, returns, description: lang === 'de' ? de : lang === 'pt' ? pt : en } -} - -/** `ArrayList` → `ArrayList`. */ -const baseType = (type: string) => type.replace(/<.*>$/, '') -/** `ArrayList` → `Spieler`. */ -const elementType = (type: string) => /<\s*([^<>,]+?)\s*>$/.exec(type)?.[1] - -function rowsOf(type: string): Row[] | undefined { - if (/\[\]$/.test(type)) return array - type = baseType(type) - if (/Exception$|^Throwable$/.test(type)) return instance.Exception - const own = instance[type] - if (!own) return undefined - const seen = new Set() - const rows: Row[] = [] - for (const t of [type, ...(extendsOf[type] ?? [])]) { - for (const row of instance[t] ?? []) { - if (seen.has(row[0])) continue - seen.add(row[0]) - rows.push(row) - } - } - return rows -} - -/** Methoden eines Typs, z. B. `membersOf('Scanner', 'de')`. */ -export function membersOf(type: string, lang: MemberLanguage = 'de'): JavaMember[] { - return (rowsOf(type) ?? []).map((r) => toMember(r, lang)) -} - -const declarationTypes = '[A-Z]\\w*|int|long|double|float|boolean|char|byte|short|var' - -/** Typ einer Variablen aus ihrer Deklaration: `Scanner eingabe`, `String[] namen`, `for (String s : namen)`, `catch (Exception e)`. */ -export function declaredType(code: string, name: string): string | undefined { - const re = new RegExp(`(?:^|[^\\w.])(${declarationTypes})\\s*(?:<([^<>;]*)>)?((?:\\s*\\[\\s*\\])*)\\s+${name}\\s*(?:\\[\\s*\\])*\\s*(=\\s*([^;]*))?(?=[=;,):]|$)`, 'gm') - let type: string | undefined - for (const m of code.matchAll(re)) { - let t = m[1] - if (t === 'var') { - const init = m[5] ?? '' - t = /^new\s+([A-Z]\w*)/.exec(init)?.[1] ?? (/^"/.test(init) ? 'String' : '') - if (!t) continue - } - // Schlüsselwörter, die wie ein Typ aussehen könnten, nicht mitnehmen. - if (/^(return|new|else|case|throw)$/.test(t)) continue - const generic = m[2]?.replace(/\s+/g, '') - type = t + (generic ? `<${generic}>` : '') + (m[3] ?? '').replace(/\s+/g, '') - } - return type -} - -/** Ein Glied der Kette vor dem Punkt: `eingabe`, `namen[i]`, `nextLine()`. */ -interface Link { - name: string - call: boolean - indices: number -} - -/** Zerlegt `a.b(x).c[0]` in Glieder; `undefined`, wenn es kein einfacher Ausdruck ist. */ -function parseChain(text: string): { head: string; links: Link[] } | undefined { - let i = 0 - let head: string - const literal = /^"(?:[^"\\]|\\.)*"/.exec(text) - if (literal) { - head = '"' - i = literal[0].length - } else { - const id = /^[A-Za-z_]\w*/.exec(text) - if (!id) return undefined - head = id[0] - i = id[0].length - } - const links: Link[] = [{ name: head, call: false, indices: 0 }] - const skipGroup = (open: string, close: string) => { - let depth = 0 - for (; i < text.length; i++) { - if (text[i] === '"') { - const s = /^"(?:[^"\\]|\\.)*"/.exec(text.slice(i)) - if (!s) return false - i += s[0].length - 1 - } else if (text[i] === open) depth++ - else if (text[i] === close && --depth === 0) { - i++ - return true - } - } - return false - } - while (i < text.length) { - const last = links[links.length - 1] - if (text[i] === '(' && !last.call && last.indices === 0) { - if (!skipGroup('(', ')')) return undefined - last.call = true - } else if (text[i] === '[') { - if (!skipGroup('[', ']')) return undefined - last.indices++ - } else if (text[i] === '.') { - const id = /^[A-Za-z_]\w*/.exec(text.slice(i + 1)) - if (!id) return undefined - links.push({ name: id[0], call: false, indices: 0 }) - i += 1 + id[0].length - } else return undefined - } - return { head, links } -} - -/** Anfang des Ausdrucks vor dem Punkt an `end` (rückwärts über Namen, Punkte, Klammern und Texte). */ -function chainStart(line: string, end: number): number { - let i = end - while (i > 0) { - const c = line[i - 1] - if (/[\w.]/.test(c)) i-- - else if (c === ')' || c === ']') { - const open = c === ')' ? '(' : '[' - let depth = 0 - let j = i - 1 - for (; j >= 0; j--) { - if (line[j] === c) depth++ - else if (line[j] === open && --depth === 0) break - } - if (j < 0) return i - i = j - } else if (c === '"') { - const j = line.lastIndexOf('"', i - 2) - if (j < 0) return i - i = j - } else break - } - return i -} - -const withoutIndex = (type: string, n: number) => type.slice(0, Math.max(0, type.length - 2 * n)) - -/** - * Eigene Klassen aus dem Projekt (VisuObject): `s1.` → Methoden und Attribute von `Spieler`, - * `Spieler.` → seine static-Mitglieder, `this.` → die Klasse, in der man schreibt. - * Was von der Stelle aus nicht sichtbar ist (private, Package), lässt der Aufrufer schon weg. - */ -export interface OwnTypes { - members(type: string): JavaMember[] | undefined - statics(type: string): JavaMember[] | undefined - /** Klasse, in der der Cursor steht (für `this.`). */ - thisType?: string -} - -function lookup(type: string, isStatic: boolean, own: OwnTypes | undefined, lang: MemberLanguage): JavaMember[] | undefined { - const mine = isStatic ? own?.statics(baseType(type)) : own?.members(baseType(type)) - if (mine) return mine - const rows = isStatic ? statics[type] : rowsOf(type) - return rows?.map((r) => toMember(r, lang)) -} - -function memberType(type: string, name: string, isStatic: boolean, own: OwnTypes | undefined): string | undefined { - const returns = lookup(type, isStatic, own, 'en')?.find((m) => m.name === name)?.returns - return returns === 'E' ? (elementType(type) ?? 'Object') : returns -} - -/** Typ des Ausdrucks vor dem Punkt, z. B. `eingabe` → Scanner, `eingabe.nextLine()` → String. */ -export function receiverType(code: string, receiver: string, own?: OwnTypes): { type: string; isStatic: boolean } | undefined { - const chain = parseChain(receiver) - if (!chain) return undefined - const [first, ...rest] = chain.links - let type: string | undefined - let isStatic = false - if (chain.head === '"') type = 'String' - else if (first.name === 'this' && own?.thisType && !first.call && !first.indices) type = own.thisType - else { - type = declaredType(code, first.name) - if (!type && (statics[first.name] || own?.statics(first.name)) && !first.call) { - type = first.name - isStatic = true - } - if (!type) return undefined - if (first.indices) type = withoutIndex(type, first.indices) - } - for (const link of rest) { - const next = memberType(type, link.name, isStatic, own) - if (!next) return undefined - type = withoutIndex(next, link.indices) - isStatic = false - } - return { type, isStatic } -} - -/** - * Vorschläge an der Stelle `pos`, wenn direkt davor `ausdruck.` (plus angefangener Name) steht. - * `from` ist der Anfang des angefangenen Namens hinter dem Punkt. - */ -export function memberCompletionsAt(code: string, pos: number, lang: MemberLanguage = 'de', own?: OwnTypes): { from: number; members: JavaMember[] } | undefined { - const lineStart = code.lastIndexOf('\n', pos - 1) + 1 - const before = code.slice(lineStart, pos) - const m = /\.\s*(\w*)$/.exec(before) - if (!m) return undefined - const dot = m.index - const receiver = before.slice(chainStart(before, dot), dot) - if (!receiver || /^\d/.test(receiver)) return undefined - const found = receiverType(code, receiver, own) - if (!found) return undefined - const members = lookup(found.type, found.isStatic, own, lang) - if (!members) return undefined - const element = elementType(found.type) ?? 'Object' - return { from: pos - m[1].length, members: members.map((x) => (x.returns === 'E' ? { ...x, returns: element } : x)) } -} diff --git a/web/src/core/javaParser.ts b/web/src/core/javaParser.ts deleted file mode 100644 index c366fbce..00000000 --- a/web/src/core/javaParser.ts +++ /dev/null @@ -1,895 +0,0 @@ -import type { LiveLabels, LiveSpan } from './codeGenerator' -import { placeholderElement } from './editing' -import { ElementType, makeElement, type VSCase, type VSElement } from './model' -import { parseSubprogramHeader } from './simulation' -import { similarType } from './typeTypos' - -/** - * Gegenrichtung zu „Code live“: Java-Code, den man tippt, wird zum Struktogramm. - * Der Leser ist absichtlich nachsichtig, denn beim Tippen ist Code fast immer unfertig: - * Offene Blöcke gelten als geschlossen, fehlende Teile werden zu Platzhaltern, und eine Zeile, - * die nicht passt, wird eine Anweisung mit dem getippten Text. Was fehlt, steht in `problems`. - */ - -/** Hinweis zu einer Stelle im Code (Zeichenpositionen, `to` ausschließlich). `key` ist ein Text aus `i18n`. */ -export interface JavaProblem { - from: number - to: number - key: JavaProblemKey - /** Setzt `%@` im Text, z. B. das fehlende Zeichen. */ - arg?: string -} - -export type JavaProblemKey = - | 'web_parse_missing' - | 'web_parse_extra' - | 'web_parse_missing_semicolon' - | 'web_parse_unclosed_string' - | 'web_parse_else_without_if' - | 'web_parse_case_outside_switch' - | 'web_parse_missing_do_while' - | 'web_parse_more_methods' - | 'web_parse_missing_import' - | 'web_parse_missing_scanner' - | 'web_parse_unknown_type' - | 'web_parse_wrong_case' - -export interface ParsedJava { - /** Kopfzeile eines Unterprogramms, z. B. `int quadrat(int x)`; `undefined` = Hauptprogramm. */ - caption?: string - /** Name aus `public class Name`, falls der Code eine Klasse ist. */ - className?: string - elements: VSElement[] - /** Zeilen jedes Elements im getippten Code (ab 0), Pfade wie in `editing.ts`. */ - spans: LiveSpan[] - /** Platzhalter-Wörter wie „Bedingung“ (Zeichenpositionen): grau, beim Tippen ersetzt. */ - placeholders: { from: number; to: number }[] - problems: JavaProblem[] -} - -export interface JavaParseOptions { - /** Namen der Unterprogramme aus den anderen Reitern: ihr Aufruf wird ein Aufruf-Kasten. */ - subprograms?: Set -} - -// MARK: - Zerlegen in Wörter - -type TokKind = 'id' | 'num' | 'str' | 'op' | 'comment' - -interface Tok { - kind: TokKind - text: string - start: number - end: number - line: number -} - -const multiOps = ['>>>=', '<<=', '>>=', '>>>', '->', '::', '++', '--', '==', '!=', '<=', '>=', '&&', '||', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<'] - -function tokenize(src: string, problems: JavaProblem[]): Tok[] { - const out: Tok[] = [] - let i = 0 - let line = 0 - const push = (kind: TokKind, start: number, end: number, startLine: number) => - out.push({ kind, text: src.slice(start, end), start, end, line: startLine }) - while (i < src.length) { - const c = src[i] - if (c === '\n') { - line++ - i++ - continue - } - if (/\s/.test(c)) { - i++ - continue - } - const start = i - const startLine = line - if (c === '/' && src[i + 1] === '/') { - while (i < src.length && src[i] !== '\n') i++ - push('comment', start, i, startLine) - continue - } - if (c === '/' && src[i + 1] === '*') { - const close = src.indexOf('*/', i + 2) - i = close < 0 ? src.length : close + 2 - for (let k = start; k < i; k++) if (src[k] === '\n') line++ - push('comment', start, i, startLine) - continue - } - if (c === '"' || c === "'") { - i++ - while (i < src.length && src[i] !== c && src[i] !== '\n') i += src[i] === '\\' ? 2 : 1 - if (src[i] === c) i++ - else problems.push({ from: start, to: Math.min(i, src.length), key: 'web_parse_unclosed_string', arg: c }) - push('str', start, Math.min(i, src.length), startLine) - continue - } - const num = /^\d[\d_]*(?:\.\d+)?(?:[eE][+-]?\d+)?[lLfFdD]?/.exec(src.slice(i, i + 64)) - if (num) { - i += num[0].length - push('num', start, i, startLine) - continue - } - const id = /^[\p{L}_$][\p{L}\p{N}_$]*/u.exec(src.slice(i, i + 256)) - if (id) { - i += id[0].length - push('id', start, i, startLine) - continue - } - const op = multiOps.find((o) => src.startsWith(o, i)) ?? c - i += op.length - push('op', start, i, startLine) - } - return out -} - -// MARK: - Lesen - -const statementKeywords = new Set(['if', 'else', 'for', 'while', 'do', 'switch', 'case', 'default', 'break', 'return', 'try']) -const modifiers = new Set(['public', 'private', 'protected', 'static', 'final', 'abstract']) -const declTypes = '(?:int|long|short|byte|float|double|boolean|char|String)' - -type SwitchCase = VSCase & { isDefault?: boolean } - -/** Ein Element mit seinen Zeilen im Code, solange die Pfade noch nicht feststehen. */ -interface Built { - element: VSElement - from: number - to: number - /** Ausgabe eines reinen Textes: kann mit der folgenden Eingabe zu einem Kasten werden. */ - prompt?: string -} - -class Parser { - private toks: Tok[] - private comments: Tok[] - private i = 0 - readonly problems: JavaProblem[] = [] - readonly placeholders: { from: number; to: number }[] = [] - private lines = new Map() - - constructor( - private src: string, - private labels: LiveLabels, - private opts: JavaParseOptions, - ) { - const all = tokenize(src, this.problems) - this.toks = all.filter((t) => t.kind !== 'comment') - this.comments = all.filter((t) => t.kind === 'comment') - } - - // Hilfen - - private get tok(): Tok | undefined { - return this.toks[this.i] - } - private is(text: string, t = this.tok) { - return t !== undefined && t.text === text && t.kind !== 'str' - } - private next(): Tok { - return this.toks[this.i++] - } - private eat(text: string): Tok | undefined { - return this.is(text) ? this.next() : undefined - } - private prev(): Tok | undefined { - return this.toks[this.i - 1] - } - private problem(key: JavaProblemKey, t: { start: number; end: number } | undefined, arg?: string) { - const at = t ?? { start: this.src.length, end: this.src.length } - this.problems.push({ from: at.start, to: Math.max(at.end, at.start + 1), key, arg }) - } - /** Fehlendes Zeichen direkt hinter einem Wort melden. */ - private missingAfter(t: Tok | undefined, what: string) { - const at = t ? t.end : this.src.length - this.problems.push({ from: Math.max(0, at - 1), to: at, key: 'web_parse_missing', arg: what }) - } - - /** Text zwischen zwei Positionen ohne Kommentare, Zeilenumbrüche zu Leerzeichen. */ - private text(from: number, to: number): string { - let s = '' - let k = from - for (const c of this.comments) { - if (c.end <= from || c.start >= to) continue - s += this.src.slice(k, Math.max(k, c.start)) - k = Math.max(k, c.end) - } - s += this.src.slice(k, Math.max(k, to)) - return s.replace(/\s*\n\s*/g, ' ').trim() - } - - /** Ein Platzhalter-Wort (z. B. „Bedingung“) gilt als leer und wird grau gezeigt. */ - private slot(from: number, to: number, label: string): string { - const t = this.text(from, to) - if (t === label) { - const at = this.src.indexOf(label, from) - if (at >= 0) this.placeholders.push({ from: at, to: at + label.length }) - return '' - } - return t - } - - private record(element: VSElement, from: number, to: number): Built { - this.lines.set(element, [from, to]) - return { element, from, to } - } - - private lineOf(t: Tok | undefined) { - return t?.line ?? this.toks[this.toks.length - 1]?.line ?? 0 - } - - // Programm - - className?: string - /** Getippte Imports ohne Leerzeichen, z. B. `importjava.util.Scanner;`. */ - private imports: string[] = [] - - /** - * Wie javac: `Scanner` ohne `import java.util.Scanner;` (oder `java.util.*`) ist ein Fehler, ebenso `Files` und `Path` ohne - * `java.nio.file`, `File`, `PrintWriter`, `FileReader`, `FileWriter`, `BufferedReader` ohne `java.io` und `Arrays` ohne - * `java.util.Arrays`. - */ - checkImports() { - const io = ['File', 'PrintWriter', 'FileReader', 'FileWriter', 'BufferedReader', 'IOException', 'FileNotFoundException'].map((n) => [n, 'java.io']) - for (const [name, pkg] of [['Scanner', 'java.util'], ['Arrays', 'java.util'], ['Files', 'java.nio.file'], ['Path', 'java.nio.file'], ['Paths', 'java.nio.file'], ...io]) { - const known = this.imports.some((i) => i === `import${pkg}.${name};` || i === `import${pkg}.*;`) - // `Files.readString(…)`: nur als Klasse vor einem Punkt, damit eine Variable `path` o. Ä. nicht zählt. - const use = this.toks.find((t, k) => t.kind === 'id' && t.text === name && (name !== 'Arrays' && pkg !== 'java.nio.file' || this.toks[k + 1]?.text === '.')) - if (!known && use) { - this.problem('web_parse_missing_import', use, pkg === 'java.util' ? `import ${pkg}.${name};` : `import ${pkg}.*;`) - return - } - } - } - - /** - * Wie javac: `eingabe.next()` ohne angelegte Variable `eingabe` ist ein Fehler. Angelegt heißt: vor dem Namen steht ein Typ - * (`Scanner eingabe`, `Iterator it`, `Scanner[] s`), auch als Parameter oder Attribut. - */ - checkScanners() { - const reads = new Set(['next', 'nextInt', 'nextDouble', 'nextLine', 'nextBoolean', 'nextLong', 'nextFloat', 'hasNext', 'hasNextInt', 'hasNextLine', 'hasNextDouble']) - const declared = (name: string) => - this.toks.some((t, k) => { - const before = this.toks[k - 1] - return t.kind === 'id' && t.text === name && !!before && ((before.kind === 'id' && !statementKeywords.has(before.text) && before.text !== 'new') || before.text === '>' || before.text === ']') - }) - for (let k = 0; k < this.toks.length - 3; k++) { - const [v, dot, m, open] = this.toks.slice(k, k + 4) - if (v.kind !== 'id' || dot.text !== '.' || !reads.has(m.text) || open.text !== '(' || this.toks[k - 1]?.text === '.') continue - if (declared(v.text)) continue - this.problem('web_parse_missing_scanner', v, `Scanner ${v.text} = new Scanner(System.in);`) - return - } - } - - /** - * Wie javac: `booelan fertig;` oder `Int x = 3;` ist kein Datentyp. Gemeldet wird nur ein Wort am Anfang einer Anweisung, - * auf das ein Name und `=`, `;` oder `,` folgen, und nur wenn es einem Grundtyp sehr ähnlich ist; eigene Klassen - * (`Auto meinAuto;`) bleiben so unberührt. - */ - checkTypes() { - const starts = new Set([';', '{', '}', '(', ',']) - const classes = new Set(this.toks.filter((_, k) => this.toks[k - 1]?.text === 'class').map((t) => t.text)) - for (let k = 0; k < this.toks.length - 2; k++) { - const [type, name, after] = this.toks.slice(k, k + 3) - const before = this.toks[k - 1] - if (type.kind !== 'id' || name.kind !== 'id' || !['=', ';', ','].includes(after.text)) continue - if (before && !starts.has(before.text) && !modifiers.has(before.text)) continue - if (classes.has(type.text)) continue - const suggestion = similarType(type.text) - if (!suggestion) continue - this.problem('web_parse_unknown_type', type, suggestion) - return - } - } - - /** - * Wie javac: Java unterscheidet Groß- und Kleinschreibung. `system.out.println`, `System.Out.println`, `math.sqrt` oder - * `System.out.Println` sind Fehler, ebenso `text.Length()` oder `name.charat(0)`; gemeldet wird das erste falsch - * geschriebene Wort mit der richtigen Schreibweise. - */ - checkCase() { - const classes = ['System', 'Math', 'Integer', 'Double', 'String'] - // Eine eigene Variable `string` oder `math` ist erlaubt: angelegt heißt, vor dem Namen steht ein Typ. - const declared = (name: string) => this.toks.some((t, k) => t.text === name && this.toks[k - 1]?.kind === 'id' && !statementKeywords.has(this.toks[k - 1].text)) - const prints = ['println', 'print', 'printf'] - const members = ['length', 'charAt', 'substring', 'indexOf', 'equals', 'equalsIgnoreCase', 'compareTo', 'contains', 'startsWith', 'endsWith', - 'toUpperCase', 'toLowerCase', 'trim', 'isEmpty', 'split', 'replace', 'sqrt', 'pow', 'abs', 'max', 'min', 'random', 'round', - 'parseInt', 'parseDouble', 'nextInt', 'nextDouble', 'nextLine', 'next', 'hasNext', 'hasNextInt', 'hasNextLine'] - const fix = (word: string, list: string[]) => list.find((w) => w !== word && w.toLowerCase() === word.toLowerCase()) - for (let k = 0; k < this.toks.length; k++) { - const t = this.toks[k] - if (t.kind !== 'id') continue - const before = this.toks[k - 1]?.text - const after = this.toks[k + 1]?.text - let right: string | undefined - if (after === '.' && before !== '.' && !declared(t.text)) right = fix(t.text, classes) - else if (before === '.' && after === '.' && this.toks[k - 2]?.text.toLowerCase() === 'system') right = fix(t.text, ['out', 'err']) - else if (before === '.' && after === '(' && ['out', 'err'].includes(this.toks[k - 2]?.text.toLowerCase() ?? '')) right = fix(t.text, prints) - else if (before === '.') right = fix(t.text, members) - if (!right) continue - this.problem('web_parse_wrong_case', t, right) - return - } - } - - parseProgram(): { caption?: string; elements: VSElement[] } { - const elements: Built[] = [] - let caption: string | undefined - let sawMethod = false - while (this.tok) { - if (this.is('import') || this.is('package')) { - const start = this.i - this.skipPast(';') - this.imports.push(this.toks.slice(start, this.i).map((t) => t.text).join('')) - continue - } - const cls = this.classStart() - if (cls !== undefined) { - this.className ??= cls.name - this.i = cls.index - const inner = this.parseClassBody() - elements.push(...inner.elements) - caption ??= inner.caption - continue - } - const method = this.methodStart() - if (method) { - if (sawMethod) this.problem('web_parse_more_methods', this.tok) - const body = this.parseMethod(method) - if (!sawMethod) { - caption = method.caption - elements.push(...body) - } - sawMethod = true - continue - } - if (this.is('}')) { - this.problem('web_parse_extra', this.next(), '}') - continue - } - elements.push(...this.parseStatement()) - } - return { caption, elements: this.finishList(elements) } - } - - private skipPast(text: string) { - while (this.tok && !this.is(text)) this.next() - this.eat(text) - } - - /** `public class Name {`: Name und Index hinter `{`. */ - private classStart(): { name: string; index: number } | undefined { - let k = this.i - while (this.toks[k] && modifiers.has(this.toks[k].text)) k++ - if (this.toks[k]?.text !== 'class' || this.toks[k + 1]?.kind !== 'id') return undefined - const name = this.toks[k + 1].text - k += 2 - while (this.toks[k] && this.toks[k].text !== '{') k++ - return { name, index: k + 1 } - } - - private parseClassBody(): { caption?: string; elements: Built[] } { - let main: Built[] | undefined - let other: { caption?: string; body: Built[] } | undefined - let extra = false - // Klasse ohne `main`: Anweisungen stehen direkt in der Klasse. - const loose: Built[] = [] - while (this.tok && !this.is('}')) { - const method = this.methodStart() - if (!method) { - if (this.is('{')) loose.push(...this.parseBlock().elements) - else loose.push(...this.parseStatement()) - continue - } - const start = this.tok - const body = this.parseMethod(method) - if (method.name === 'main') main ??= body - else if (!other) other = { caption: method.caption, body } - else extra = true - if ((main && other) || extra) this.problem('web_parse_more_methods', start) - } - this.eat('}') - if (main) return { elements: main } - if (loose.length > 0 || !other) return { elements: loose } - return { caption: other.caption, elements: other.body } - } - - /** `[public static] int quadrat(int x) {`: Name und Kopfzeile, Index steht danach auf `{`. */ - private methodStart(): { name: string; caption?: string } | undefined { - let k = this.i - let mods = 0 - while (this.toks[k] && modifiers.has(this.toks[k].text)) { - k++ - mods++ - } - const type = this.toks[k] - if (type?.kind !== 'id' || statementKeywords.has(type.text) || type.text === 'new') return undefined - k++ - while (this.toks[k]?.text === '[' && this.toks[k + 1]?.text === ']') k += 2 - const name = this.toks[k] - if (name?.kind !== 'id' || this.toks[k + 1]?.text !== '(') return undefined - // Ohne `public`/`static` nur, wenn danach wirklich ein Rumpf `{` folgt (sonst ist es eine Anweisung). - let depth = 0 - let close = k + 1 - for (; this.toks[close]; close++) { - const t = this.toks[close].text - if (t === '(') depth++ - else if (t === ')' && --depth === 0) break - else if (t === ';' || t === '{' || t === '}') break - } - // `throws IOException, Exception` nach der Parameterliste gehört zur Kopfzeile. - let bodyAt = close + 1 - if (this.toks[close]?.text === ')' && this.toks[bodyAt]?.text === 'throws') { - bodyAt++ - while (this.toks[bodyAt]?.kind === 'id' && (this.toks[bodyAt + 1]?.text === ',' || this.toks[bodyAt + 1]?.text === '{' || this.toks[bodyAt + 1]?.text === '.')) { - bodyAt += this.toks[bodyAt + 1].text === '{' ? 1 : 2 - } - } - const after = this.toks[bodyAt] - const hasBody = this.toks[close]?.text === ')' && after?.text === '{' - if (!hasBody && !(mods > 0 && (after === undefined || this.toks[close] === undefined))) return undefined - const paramsEnd = this.toks[close]?.text === ')' ? this.toks[close].start : this.src.length - const params = this.text(this.toks[k + 1].end, paramsEnd) - const typeText = this.text(type.start, this.toks[k - 1].end).replace(/\s+/g, '') - const header = `${typeText} ${name.text}(${params.replace(/\s*,\s*/g, ', ')})` - this.i = Math.min(hasBody ? bodyAt : close + 1, this.toks.length) - return { name: name.text, caption: name.text === 'main' || !parseSubprogramHeader(header) ? undefined : header } - } - - private parseMethod(_method: { name: string }): Built[] { - if (!this.is('{')) { - this.missingAfter(this.prev(), '{') - return [] - } - return this.parseBlock().elements - } - - // Blöcke - - /** `{ … }`; fehlt die schließende Klammer, endet der Block am Ende des Codes. */ - private parseBlock(): { elements: Built[]; end: number } { - const open = this.next() - const elements: Built[] = [] - while (this.tok && !this.is('}')) { - if (this.is('case') || this.is('default')) break - elements.push(...this.parseStatement()) - } - const close = this.eat('}') - if (!close) this.problem('web_parse_missing', open, '}') - if (elements.length === 0) { - // Leerer Block mit `// Dein Code`: Der Platzhalter steht in dieser Zeile. - const until = close?.start ?? this.src.length - const c = this.comments.find((c) => c.start > open.end && c.start < until && this.isCodePlaceholder(c)) - if (c) elements.push(this.record(placeholderElement(), c.line, c.line)) - } - return { elements, end: this.lineOf(close ?? this.prev()) } - } - - /** Rumpf nach `if (…)`, `for (…)` usw.: Block oder eine einzelne Anweisung. */ - private parseBody(): { elements: VSElement[]; end: number } { - if (this.is('{')) { - const b = this.parseBlock() - return { elements: this.finishList(b.elements), end: b.end } - } - if (!this.tok || this.is('}')) { - this.missingAfter(this.prev(), '{') - return { elements: [placeholderElement()], end: this.lineOf(this.prev()) } - } - const one = this.parseStatement() - return { elements: this.finishList(one), end: one.length ? one[one.length - 1].to : this.lineOf(this.prev()) } - } - - /** Ausgabe + Eingabe zusammenfassen; leere Liste wird zum Platzhalter `ø`. */ - private finishList(list: Built[]): VSElement[] { - const out: Built[] = [] - for (const b of list) { - const last = out[out.length - 1] - const input = b.element.type === ElementType.anweisung ? /^input: (\S+(?: \S+)?)$/.exec(b.element.textLines[0]) : null - if (input && last?.prompt !== undefined) { - const name = input[1].split(' ').pop()! - if (last.prompt !== `${name}: `) b.element.textLines = [`${b.element.textLines[0]} "${last.prompt}"`] - out.pop() - this.lines.delete(last.element) - out.push(this.record(b.element, last.from, b.to)) - continue - } - out.push(b) - } - if (out.length === 0) return [placeholderElement()] - return out.map((b) => b.element) - } - - // Anweisungen - - private parseStatement(): Built[] { - const t = this.tok! - switch (t.kind === 'id' ? t.text : '') { - case 'if': - return [this.parseIf()] - case 'while': - return [this.parseWhile()] - case 'do': - return [this.parseDo()] - case 'for': - return [this.parseFor()] - case 'switch': - return [this.parseSwitch()] - case 'try': - return this.parseTry() - case 'break': { - this.next() - const semi = this.eat(';') - if (!semi) this.missingAfter(t, ';') - const hint = this.trailingComment(semi ?? t) - return [this.record(makeElement(ElementType.aussprung, { textLines: [hint] }), t.line, t.line)] - } - case 'else': - this.problem('web_parse_else_without_if', this.next()) - return [] - case 'case': - case 'default': - this.problem('web_parse_case_outside_switch', this.next()) - while (this.tok && !this.is(':') && !this.is('->') && !this.is(';')) this.next() - this.next() - return [] - } - if (this.is('{')) { - const b = this.parseBlock() - return b.elements - } - if (this.is(';')) { - this.next() - return [this.record(makeElement(ElementType.anweisung), t.line, t.line)] - } - return this.parseSimple() - } - - /** - * `try { … } catch (…) { … } finally { … }`: Im Struktogramm gibt es dafür keinen Kasten, deshalb wird alles - * zusammen ein Anweisungskasten (wie ein Lambda mit Rumpf). Fehlt eine `}`, wird sie gemeldet. - */ - private parseTry(): Built[] { - const first = this.next() - let last = first - /** Klammerpaar ab dem aktuellen Wort überspringen; `false`, wenn die schließende Klammer fehlt. */ - const skipPair = (open: string, close: string): boolean => { - const start = this.next() - last = start - let depth = 1 - while (this.tok) { - const t = this.next() - last = t - if (t.kind === 'str') continue - if (t.text === open) depth++ - else if (t.text === close && --depth === 0) return true - } - this.missingAfter(last, close) - return false - } - let complete = true - if (this.is('(')) complete = skipPair('(', ')') - if (complete && this.is('{')) complete = skipPair('{', '}') - else if (complete) this.missingAfter(last, '{') - while (complete && (this.is('catch') || this.is('finally'))) { - const isCatch = this.next().text === 'catch' - last = this.prev()! - if (isCatch) { - if (this.is('(')) complete = skipPair('(', ')') - else this.missingAfter(last, '(') - } - if (complete && this.is('{')) complete = skipPair('{', '}') - else if (complete) this.missingAfter(last, '{') - } - const raw = this.text(first.start, last.end).replace(/\s+/g, ' ') - return [this.record(makeElement(ElementType.anweisung, { textLines: [raw] }), first.line, last.line)] - } - - /** Kommentar direkt hinter einem Wort in derselben Zeile, z. B. `break; // fertig`. */ - private trailingComment(after: Tok): string { - const c = this.comments.find((c) => c.start >= after.end && c.line === after.line) - const nextTok = this.toks.find((x) => x.start >= after.end) - if (!c || (nextTok && nextTok.start < c.start)) return '' - return c.text.replace(/^\/\/|^\/\*|\*\/$/g, '').trim() - } - - /** Bedingung in `( … )`. Fehlt `(` oder `)`, endet sie vor `{` bzw. am Zeilenende. */ - private parenthesized(keyword: Tok, label: string): { text: string; close?: Tok } { - const open = this.eat('(') - if (!open) this.missingAfter(keyword, '(') - const startTok = this.tok - let depth = 0 - let lastTok: Tok | undefined - while (this.tok) { - const t = this.tok - if (!open && t.line !== keyword.line) break - if (t.text === '{' || t.text === '}' || t.text === ';') break - if (t.text === '(') depth++ - if (t.text === ')') { - if (depth === 0) break - depth-- - } - lastTok = this.next() - } - const close = open ? this.eat(')') : undefined - if (open && !close) this.missingAfter(lastTok ?? open, ')') - const from = open ? open.end : (startTok?.start ?? keyword.end) - const to = close ? close.start : (lastTok?.end ?? from) - return { text: this.slot(from, to, label), close } - } - - private parseIf(): Built { - const kw = this.next() - const cond = this.parenthesized(kw, this.labels.condition) - const yes = this.parseBody() - let no: VSElement[] = [placeholderElement()] - let end = yes.end - if (this.is('else')) { - this.next() - if (this.is('if')) { - const nested = this.parseIf() - no = [nested.element] - end = nested.to - } else { - const b = this.parseBody() - no = b.elements - end = b.end - } - } - const element = makeElement(ElementType.verzweigung, { - textLines: [cond.text], - cases: [ - { name: 'true', elements: yes.elements }, - { name: 'false', elements: no }, - ], - }) - return this.record(element, kw.line, end) - } - - private parseWhile(): Built { - const kw = this.next() - const cond = this.parenthesized(kw, this.labels.condition) - const body = this.parseBody() - const endless = cond.text === 'true' - const element = makeElement(endless ? ElementType.endlosschleife : ElementType.whileSchleife, { - textLines: [endless ? '' : cond.text], - loopBody: body.elements, - }) - return this.record(element, kw.line, body.end) - } - - private parseDo(): Built { - const kw = this.next() - const body = this.parseBody() - let cond = '' - let end = body.end - const w = this.eat('while') - if (w) { - cond = this.parenthesized(w, this.labels.condition).text - const semi = this.eat(';') - if (!semi) this.missingAfter(this.prev(), ';') - end = this.lineOf(this.prev()) - } else { - this.problem('web_parse_missing_do_while', kw) - } - return this.record(makeElement(ElementType.doUntilSchleife, { textLines: [cond], loopBody: body.elements }), kw.line, end) - } - - private parseFor(): Built { - const kw = this.next() - const open = this.eat('(') - if (!open) this.missingAfter(kw, '(') - const fields: string[] = [] - const labels = [this.labels.start, this.labels.condition, this.labels.step] - let from = open?.end ?? this.tok?.start ?? kw.end - let depth = 0 - let close: Tok | undefined - let last: Tok | undefined - while (this.tok) { - const t = this.tok - if (t.text === '{' || t.text === '}') break - if (!open && t.line !== kw.line) break - if (t.text === '(') depth++ - if (t.text === ')') { - if (depth === 0) { - close = this.next() - break - } - depth-- - } - if (t.text === ';' && depth === 0) { - fields.push(this.slot(from, t.start, labels[fields.length] ?? '')) - this.next() - from = t.end - continue - } - last = this.next() - } - if (open && !close) this.missingAfter(last ?? this.prev() ?? open, ')') - const tail = close ? close.start : (last?.end ?? this.prev()?.end ?? from) - fields.push(this.slot(from, Math.max(from, tail), labels[fields.length] ?? '')) - // Kopf `int x : liste` oder unvollständig: wie im Objektinspektor drei Felder. - let textLines = fields - if (fields.length === 1 && fields[0] !== '' && !fields[0].includes(':')) textLines = [fields[0], '', ''] - while (textLines.length < 3 && textLines.length > 1) textLines.push('') - const body = this.parseBody() - return this.record(makeElement(ElementType.forSchleife, { textLines, loopBody: body.elements }), kw.line, body.end) - } - - private parseSwitch(): Built { - const kw = this.next() - const sel = this.parenthesized(kw, this.labels.selector) - const cases: SwitchCase[] = [] - let end = this.lineOf(this.prev()) - const open = this.eat('{') - if (!open) this.missingAfter(this.prev(), '{') - else { - while (this.tok && !this.is('}')) { - if (!this.is('case') && !this.is('default')) { - // Anweisung vor dem ersten `case`: einem Fall ohne Namen zuordnen wäre verwirrend, also melden. - this.problem('web_parse_case_outside_switch', this.tok) - this.parseStatement() - continue - } - cases.push(this.parseCase()) - } - const close = this.eat('}') - if (!close) this.problem('web_parse_missing', open, '}') - end = this.lineOf(close ?? this.prev()) - } - if (!cases.some((c) => c.isDefault)) cases.push({ name: 'default', elements: [placeholderElement()], isDefault: true }) - // `default` steht immer als letzter Fall. - const ordered = [...cases.filter((c) => !c.isDefault), ...cases.filter((c) => c.isDefault)].map(({ name, elements }) => ({ name, elements })) - return this.record(makeElement(ElementType.fallauswahl, { textLines: [sel.text], cases: ordered }), kw.line, end) - } - - private parseCase(): SwitchCase { - const kw = this.next() - const isDefault = kw.text === 'default' - const labelStart = this.tok?.start ?? kw.end - let labelEnd = labelStart - while (this.tok && !this.is('->') && !this.is(':') && !this.is('{') && !this.is('}')) labelEnd = this.next().end - const arrow = this.eat('->') ?? this.eat(':') - if (!arrow) this.missingAfter(this.prev(), '->') - let name = isDefault ? 'default' : this.text(labelStart, labelEnd) - let elements: Built[] = [] - if (arrow?.text === '->') { - if (this.is('{')) { - // `default -> { /* Name */`: der Name des letzten Falls steht als Kommentar dahinter. - if (isDefault) name = this.trailingComment(this.tok!) || name - elements = this.parseBlock().elements - } else if (this.tok && !this.is('}')) elements = this.parseStatement() - } else { - if (isDefault && arrow) name = this.trailingComment(arrow) || name - while (this.tok && !this.is('case') && !this.is('default') && !this.is('}')) elements.push(...this.parseStatement()) - // Klassisches `break;` am Ende eines Falls gehört zur Fallauswahl selbst. - const last = elements[elements.length - 1] - if (last?.element.type === ElementType.aussprung && last.element.textLines.every((l) => l === '')) elements.pop() - } - return { name, elements: this.finishList(elements), isDefault } - } - - /** Eine einfache Anweisung bis `;`. Fehlt das Semikolon, endet sie am Zeilenende. */ - private parseSimple(): Built[] { - const first = this.next() - let last = first - // Offene Klammern dieser Anweisung. Steht die innerste `{` in der Anweisung (Lambda `e -> { … }` oder - // anonyme Klasse `new ActionListener() { … }`), gehören `;` und neue Zeilen darin noch dazu. - const open: string[] = first.text === '(' || first.text === '[' || first.text === '{' ? [first.text] : [] - const inBraces = () => open.length > 0 && open[open.length - 1] === '{' && first.text !== '{' - let semi: Tok | undefined - while (this.tok) { - const t = this.tok - if (t.text === ';' && t.kind === 'op' && !inBraces()) { - semi = this.next() - break - } - if (t.line !== last.line && !inBraces()) { - if (open.length === 0 || (t.kind === 'id' && statementKeywords.has(t.text)) || t.text === '}') break - } - if (t.text === '}' && open.length === 0) break - if (t.text === '(' || t.text === '[' || t.text === '{') open.push(t.text) - if (t.text === ')' || t.text === ']' || t.text === '}') open.pop() - last = this.next() - } - if (!semi) this.missingAfter(last, ';') - this.checkBrackets(first, last) - const raw = this.text(first.start, last.end) - const built = this.simpleElement(raw) - if (!built) return [] - const b = this.record(built.element, first.line, last.line) - b.prompt = built.prompt - return [b] - } - - /** Runde und eckige Klammern innerhalb einer Anweisung. */ - private checkBrackets(first: Tok, last: Tok) { - const stack: Tok[] = [] - const pairs: Record = { ')': '(', ']': '[', '}': '{' } - const closing: Record = { '(': ')', '[': ']', '{': '}' } - for (let k = this.toks.indexOf(first); k >= 0 && k < this.toks.length && this.toks[k].start <= last.start; k++) { - const t = this.toks[k] - if (t.kind !== 'op') continue - if (closing[t.text]) stack.push(t) - else if (pairs[t.text]) { - if (stack.length && stack[stack.length - 1].text === pairs[t.text]) stack.pop() - else this.problem('web_parse_extra', t, t.text) - } - } - if (stack.length) this.missingAfter(last, closing[stack[stack.length - 1].text]) - } - - /** Variablen mit einem Scanner auf einer Datei (`new Scanner(new File(…))`). */ - private fileScanners = new Set() - - /** Anweisungstext → Kasten. `undefined` = gehört nicht ins Struktogramm (z. B. `Scanner scanner = …`). */ - private simpleElement(raw: string): { element: VSElement; prompt?: string } | undefined { - const fileScanner = /^(?:Scanner\s+)?(\w+)\s*=\s*new\s+Scanner\s*\(\s*new\s+File\b/.exec(raw) - if (fileScanner) this.fileScanners.add(fileScanner[1]) - if (/^(?:(?:private|public|static|final)\s+)*Scanner\s+\w+\s*=\s*new\s+Scanner\s*\(\s*System\.in\s*\)$/.test(raw)) return undefined - const anweisung = (text: string) => makeElement(ElementType.anweisung, { textLines: [text] }) - // Auch unfertig (`System.out.println(` beim Tippen) und leer schon eine Ausgabe. - const out = /^System\.out\.println\s*\(([\s\S]*?)\)?$/.exec(raw) - if (out) { - const expr = out[1].trim() - if (expr === '') return { element: anweisung('output: ') } - const literal = /^"((?:[^"\\]|\\.)*)"$/.exec(expr) - const prompt = literal ? unescapeJava(literal[1]) : undefined - return { element: anweisung(`output: ${expr}`), prompt: prompt !== undefined && !prompt.includes('"') ? prompt : undefined } - } - const input = new RegExp(`^(?:(${declTypes})\\s+)?([A-Za-z_]\\w*(?:\\s*\\[[^\\]]+\\])*)\\s*=\\s*\\w+\\.(nextInt|nextDouble|nextLine|next|nextBoolean)\\s*\\(\\s*\\)$`).exec(raw) - // `zeile = datei.nextLine()` mit einem Scanner auf einer Datei liest nicht von der Tastatur. - if (input && !this.fileScanners.has(raw.replace(/^[\s\S]*=\s*(\w+)\.\w+\s*\(\s*\)$/, '$1'))) { - const kind = { nextInt: 'int', nextDouble: 'double', nextLine: 'String', next: 'String', nextBoolean: 'boolean' }[input[3]]! - const name = input[2].replace(/\s+/g, '') - // Ohne Typ nur bei Array-Zellen, sonst würde der erzeugte Code die Variable ein zweites Mal anlegen. - if ((input[1] === kind || (!input[1] && name.includes('['))) && kind) return { element: anweisung(`input: ${kind} ${name}`) } - } - const final = new RegExp(`^final\\s+(${declTypes}\\s+[\\s\\S]*)$`).exec(raw) - if (final) return { element: anweisung(`const ${final[1]}`) } - const call = /^(?:(?:\w+\s+)?\w+\s*=\s*)?([A-Za-z_]\w*)\s*\(/.exec(raw) - if (call && this.opts.subprograms?.has(call[1])) return { element: makeElement(ElementType.aufruf, { textLines: [raw] }) } - return { element: anweisung(raw) } - } - - // Ergebnis - - /** Pfade wie in `editing.ts` und die Zeilen dazu. */ - spansFor(elements: VSElement[], prefix: number[] = [], out: LiveSpan[] = []): LiveSpan[] { - elements.forEach((e, i) => { - const path = [...prefix, i] - const lines = this.lines.get(e) - if (lines) out.push({ path, from: lines[0], to: lines[1] }) - if (e.loopBody) this.spansFor(e.loopBody, [...path, 0], out) - e.cases?.forEach((c, k) => this.spansFor(c.elements, [...path, k], out)) - }) - return out - } - - private isCodePlaceholder(c: Tok) { - return c.text.startsWith('//') && c.text.slice(2).trim() === this.labels.code - } - - /** `// Dein Code` in leeren Blöcken ist ein Platzhalter. */ - markPlaceholderComments() { - for (const c of this.comments) if (this.isCodePlaceholder(c)) this.placeholders.push({ from: c.start, to: c.end }) - } -} - -function unescapeJava(s: string): string { - return s.replace(/\\(.)/g, (_, c: string) => ({ n: '\n', r: '\r', t: '\t' })[c] ?? c) -} - -export function parseLiveJava(source: string, labels: LiveLabels, opts: JavaParseOptions = {}): ParsedJava { - const p = new Parser(source, labels, opts) - const { caption, elements } = p.parseProgram() - p.markPlaceholderComments() - p.checkImports() - p.checkScanners() - p.checkTypes() - p.checkCase() - const spans = p.spansFor(elements) - const problems = p.problems.sort((a, b) => a.from - b.from) - return { caption, className: p.className, elements, spans, placeholders: p.placeholders.sort((a, b) => a.from - b.from), problems } -} diff --git a/web/src/core/javaProject.ts b/web/src/core/javaProject.ts deleted file mode 100644 index 4173f28a..00000000 --- a/web/src/core/javaProject.ts +++ /dev/null @@ -1,375 +0,0 @@ -/** - * VisuObject: ein Java-Projekt aus mehreren Dateien, wie im Package Explorer von Eclipse. - * Jede Datei liegt in einem Package (Ordner); die Zeile `package …;` im Code folgt dem Ordner. - * Das Klassendiagramm zeigt die Klassen aller Dateien. - */ - -import { parseClasses, type UmlClass } from './classModel' -import type { UmlInfo } from './umlRelations' - -export interface ProjectFile { - /** - * Pfad unter `src/`, z. B. `tiere/Hund.java`; ohne Ordner im Standard-Package. - * In einem anderen Source Folder steht er davor mit Doppelpunkt: `test:tiere/HundTest.java`. - */ - path: string - text: string -} - -export interface JavaProject { - name: string - files: ProjectFile[] - /** Auch leere Packages, z. B. gleich nach „+ Package“ (in anderen Source Folders als `test:tiere`). */ - packages: string[] - /** Source Folders außer `src`, z. B. `test`, wie in Eclipse; auch leere. */ - sourceFolders?: string[] - /** Was nur UML kennt (Aggregation, Name der Beziehung …), siehe `umlRelations.ts`. */ - uml?: UmlInfo - /** Notizen im Klassendiagramm (Kasten mit Eselsohr), evtl. gestrichelt an Klassen geheftet. */ - notes?: UmlNote[] - /** Selbst gezeichnetes Objektdiagramm: Objekte mit Werten; Referenzen sind Werte mit dem Namen eines Objekts. */ - objects?: UmlObject[] -} - -/** Objekt im Objektdiagramm, z. B. `s1 : Spieler` mit `punkte = 3` und `team = t1`. */ -export interface UmlObject { - id: string - name: string - cls: string - x: number - y: number - /** Attributname → Wert wie getippt; bei Listen `[s1, s2]`. */ - values: Record -} - -export interface UmlNote { - id: string - text: string - x: number - y: number - /** Klassen, an die die Notiz mit gestrichelter Linie geheftet ist. */ - attach?: string[] -} - -/** Klasse mit Datei und Package, für Diagramm und Explorer. */ -export interface ProjectClass extends UmlClass { - path: string - /** Java-Package ohne Source Folder (wie in `package …;`). */ - pkg: string - /** Source Folder, meist `src`. */ - root: string -} - -export const defaultRoot = 'src' - -/** `test:tiere/X.java` → `test` und `tiere/X.java`; ohne Doppelpunkt `src`. Genauso für Package-Schlüssel. */ -export function splitRoot(key: string): { root: string; rest: string } { - const i = key.indexOf(':') - return i < 0 ? { root: defaultRoot, rest: key } : { root: key.slice(0, i), rest: key.slice(i + 1) } -} -const withRoot = (root: string, rest: string) => (root === defaultRoot ? rest : `${root}:${rest}`) - -/** Schlüssel des Packages im Explorer: `tiere`, in anderen Source Folders `test:tiere`. */ -export const packageKey = (root: string, pkg: string) => withRoot(root, pkg) -/** Java-Package ohne Source Folder. */ -export const barePackage = (key: string) => splitRoot(key).rest -export const rootOf = (key: string) => splitRoot(key).root - -export const packageOf = (path: string) => { - const { root, rest } = splitRoot(path) - return withRoot(root, rest.includes('/') ? rest.slice(0, rest.lastIndexOf('/')).replace(/\//g, '.') : '') -} -export const fileNameOf = (path: string) => { - const rest = splitRoot(path).rest - return rest.slice(rest.lastIndexOf('/') + 1) -} -export const pathFor = (pkg: string, file: string) => { - const { root, rest } = splitRoot(pkg) - return withRoot(root, rest ? `${rest.replace(/\./g, '/')}/${file}` : file) -} -/** Pfad wie auf der Festplatte, z. B. `test/tiere/HundTest.java`. */ -export const diskPath = (path: string) => { - const { root, rest } = splitRoot(path) - return `${root}/${rest}` -} - -const folderName = /^[\p{L}\p{N}_$-][\p{L}\p{N}_$.-]*$/u -export const isFolderName = (name: string) => folderName.test(name) && !name.includes('..') - -/** Alle Source Folders: `src` zuerst, dann die übrigen (auch leere und solche, die nur aus Dateien bekannt sind). */ -export function sourceFoldersOf(project: JavaProject): string[] { - const all = new Set([defaultRoot, ...(project.sourceFolders ?? [])]) - for (const f of project.files) all.add(rootOf(f.path)) - for (const p of project.packages) all.add(rootOf(p)) - return [...all].sort((a, b) => (a === defaultRoot ? -1 : b === defaultRoot ? 1 : a.localeCompare(b))) -} - -const packageName = /^[a-z_$][\p{L}\p{N}_$]*(\.[a-z_$][\p{L}\p{N}_$]*)*$/u -export const isPackageName = (name: string) => packageName.test(name) - -/** - * Alle Packages als Schlüssel: aus den Dateien und die leeren, sortiert nach Source Folder; - * das Standard-Package nur, wenn Dateien darin liegen. Mit `root` nur die aus diesem Source Folder. - */ -export function packagesOf(project: JavaProject, root?: string): string[] { - const all = new Set(project.packages) - for (const f of project.files) all.add(packageOf(f.path)) - const roots = sourceFoldersOf(project) - return [...all] - .filter((p) => barePackage(p) !== '' || project.files.some((f) => packageOf(f.path) === p)) - .filter((p) => root === undefined || rootOf(p) === root) - .sort((a, b) => { - const ra = roots.indexOf(rootOf(a)) - roots.indexOf(rootOf(b)) - if (ra) return ra - const [x, y] = [barePackage(a), barePackage(b)] - return x === '' ? 1 : y === '' ? -1 : x.localeCompare(y) - }) -} - -export function filesIn(project: JavaProject, pkg: string): ProjectFile[] { - return project.files.filter((f) => packageOf(f.path) === pkg).sort((a, b) => a.path.localeCompare(b.path)) -} - -/** `package …;` am Anfang setzen, ändern oder (Standard-Package) entfernen. */ -export function withPackageLine(text: string, key: string): string { - const pkg = barePackage(key) - const line = /^(\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*)package\s+[\w.]+\s*;[ \t]*\n?/ - const match = line.exec(text) - if (match) { - if (!pkg) return match[1] + text.slice(match[0].length).replace(/^\n/, '') - return `${match[1]}package ${pkg};\n` + text.slice(match[0].length) - } - if (!pkg) return text - return `package ${pkg};\n\n${text.replace(/^\n+/, '')}` -} - -/** Package aus der Zeile `package …;` lesen (für geöffnete Dateien). */ -export function declaredPackage(text: string): string { - return /^\s*(?:(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)\s*)*package\s+([\w.]+)\s*;/.exec(text)?.[1] ?? '' -} - -export function projectClasses(project: JavaProject): ProjectClass[] { - return project.files.flatMap((f) => parseClasses(f.text).map((c) => ({ ...c, path: f.path, pkg: barePackage(packageOf(f.path)), root: rootOf(f.path) }))) -} - -export function updateFile(project: JavaProject, path: string, text: string): JavaProject { - return { ...project, files: project.files.map((f) => (f.path === path ? { ...f, text } : f)) } -} - -export function addFile(project: JavaProject, path: string, text: string): JavaProject { - return { ...project, files: [...project.files.filter((f) => f.path !== path), { path, text }] } -} - -export function removeFile(project: JavaProject, path: string): JavaProject { - // Das Package bleibt sichtbar, auch wenn es jetzt leer ist. - const pkg = packageOf(path) - return { ...project, files: project.files.filter((f) => f.path !== path), packages: pkg && !project.packages.includes(pkg) ? [...project.packages, pkg] : project.packages } -} - -/** Datei umbenennen oder in ein anderes Package schieben; die `package`-Zeile passt sich an. */ -export function moveFile(project: JavaProject, path: string, to: string): JavaProject { - const pkg = packageOf(to) - return { - ...project, - files: project.files.map((f) => (f.path === path ? { path: to, text: packageOf(path) === pkg ? f.text : withPackageLine(f.text, pkg) } : f)), - } -} - -export function addPackage(project: JavaProject, pkg: string): JavaProject { - return project.packages.includes(pkg) ? project : { ...project, packages: [...project.packages, pkg] } -} - -/** Package umbenennen (auch Unter-Packages wie `tiere.haus`). */ -export function renamePackage(project: JavaProject, from: string, to: string): JavaProject { - const rename = (p: string) => (p === from ? to : p.startsWith(from + '.') ? to + p.slice(from.length) : p) - return { - ...project, - packages: [...new Set(project.packages.map(rename))], - files: project.files.map((f) => { - const pkg = packageOf(f.path) - const next = rename(pkg) - return next === pkg ? f : { path: pathFor(next, fileNameOf(f.path)), text: withPackageLine(f.text, next) } - }), - } -} - -/** Neuer, leerer Source Folder wie `test`. */ -export function addSourceFolder(project: JavaProject, name: string): JavaProject { - return sourceFoldersOf(project).includes(name) ? project : { ...project, sourceFolders: [...(project.sourceFolders ?? []), name] } -} - -/** Source Folder umbenennen; Dateien und Packages ziehen mit (`src` bleibt). */ -export function renameSourceFolder(project: JavaProject, from: string, to: string): JavaProject { - const move = (key: string) => (rootOf(key) === from ? withRoot(to, barePackage(key)) : key) - return { - ...project, - sourceFolders: (project.sourceFolders ?? []).map((r) => (r === from ? to : r)).filter((r) => r !== defaultRoot), - packages: project.packages.map(move), - files: project.files.map((f) => (rootOf(f.path) === from ? { ...f, path: withRoot(to, splitRoot(f.path).rest) } : f)), - } -} - -/** Source Folder samt Packages und Dateien löschen (`src` nicht). */ -export function removeSourceFolder(project: JavaProject, root: string): JavaProject { - if (root === defaultRoot) return project - return { - ...project, - sourceFolders: (project.sourceFolders ?? []).filter((r) => r !== root), - packages: project.packages.filter((p) => rootOf(p) !== root), - files: project.files.filter((f) => rootOf(f.path) !== root), - } -} - -/** Package samt Dateien löschen. */ -export function removePackage(project: JavaProject, pkg: string): JavaProject { - return { ...project, packages: project.packages.filter((p) => p !== pkg), files: project.files.filter((f) => packageOf(f.path) !== pkg) } -} - -/** Neue Datei für eine Klasse: `package …;` und ein leerer Rumpf. */ -export function classFileText(pkg: string, name: string, kind: 'class' | 'interface'): string { - return withPackageLine(`public ${kind} ${name} {\n}\n`, pkg) -} - -/** Eine einzelne Datei (z. B. aus Stufe 1 oder ein geöffnetes `.java`) als Projekt. */ -export function projectFromFiles(name: string, files: { name: string; text: string }[]): JavaProject { - const out: ProjectFile[] = [] - for (const f of files) { - const text = f.text.replace(/\r\n/g, '\n') - const pkg = declaredPackage(text) - const publicClass = /\bpublic\s+(?:abstract\s+|final\s+)*(?:class|interface|enum)\s+(\w+)/.exec(text)?.[1] - const fileName = /\.java$/i.test(f.name) ? fileNameOf(f.name) : `${publicClass ?? parseClasses(text)[0]?.name ?? 'Main'}.java` - out.push({ path: pathFor(pkg, fileName), text }) - } - return { name, files: out, packages: [] } -} - -/** Speichern im Browser und als `.visuobject`-Datei. */ -export function projectToJSON(project: JavaProject): string { - return JSON.stringify({ format: 'visuobject', version: 1, ...project }) -} - -export function projectFromJSON(json: string): JavaProject | undefined { - try { - const data = JSON.parse(json) as Partial - if (!Array.isArray(data.files)) return undefined - const files = data.files.filter((f): f is ProjectFile => typeof f?.path === 'string' && typeof f?.text === 'string') - const project: JavaProject = { name: typeof data.name === 'string' && data.name ? data.name : 'Projekt', files, packages: Array.isArray(data.packages) ? data.packages.filter((p) => typeof p === 'string') : [] } - const roots = Array.isArray(data.sourceFolders) ? data.sourceFolders.filter((r): r is string => typeof r === 'string' && isFolderName(r) && r !== defaultRoot) : [] - if (roots.length) project.sourceFolders = roots - const uml = umlFromJSON(data.uml) - if (uml) project.uml = uml - const notes = notesFromJSON(data.notes) - if (notes) project.notes = notes - const objects = objectsFromJSON(data.objects) - if (objects) project.objects = objects - return project - } catch { - return undefined - } -} - -/** Notizen aus einer Datei prüfen: nur Einträge mit Text und Lage. */ -export function notesFromJSON(data: unknown): UmlNote[] | undefined { - if (!Array.isArray(data)) return undefined - const out: UmlNote[] = [] - for (const n of data as Partial[]) { - if (!n || typeof n.id !== 'string' || typeof n.text !== 'string' || typeof n.x !== 'number' || typeof n.y !== 'number') continue - const attach = Array.isArray(n.attach) ? n.attach.filter((a): a is string => typeof a === 'string') : [] - out.push({ id: n.id, text: n.text, x: n.x, y: n.y, ...(attach.length ? { attach } : {}) }) - } - return out.length ? out : undefined -} - -/** Objekte aus einer Datei prüfen. */ -export function objectsFromJSON(data: unknown): UmlObject[] | undefined { - if (!Array.isArray(data)) return undefined - const out: UmlObject[] = [] - for (const o of data as Partial[]) { - if (!o || typeof o.id !== 'string' || typeof o.name !== 'string' || typeof o.cls !== 'string' || typeof o.x !== 'number' || typeof o.y !== 'number') continue - const values: Record = {} - if (o.values && typeof o.values === 'object') for (const [k, v] of Object.entries(o.values)) if (typeof v === 'string') values[k] = v - out.push({ id: o.id, name: o.name, cls: o.cls, x: o.x, y: o.y, values }) - } - return out.length ? out : undefined -} - -/** Nur Einträge mit Objekt als Wert übernehmen (Datei kann von außen kommen). */ -export function umlFromJSON(data: unknown): UmlInfo | undefined { - if (!data || typeof data !== 'object' || Array.isArray(data)) return undefined - const out: UmlInfo = {} - for (const [key, value] of Object.entries(data as Record)) if (value && typeof value === 'object' && !Array.isArray(value)) out[key] = value as UmlInfo[string] - return Object.keys(out).length ? out : undefined -} - -// MARK: - Beispiel - -/** Beispiel wie im Spickzettel Java OOP & UML: Tier und Hund im Package `tiere`, Besitzer in `personen`. */ -export function exampleProject(): JavaProject { - return { - name: 'Zoo', - packages: [], - uml: { 'Besitzer.hunde': { kind: 'aggregation', name: 'führt aus', reading: 'forward', sourceMultiplicity: '1' } }, - files: [ - { - path: 'tiere/Tier.java', - text: `package tiere; - -public class Tier { - private String name; - private int alter; - - public Tier(String name, int alter) { - this.name = name; - this.alter = alter; - } - - public String getName() { - return name; - } - - public void lautGeben() { - System.out.println("..."); - } -} -`, - }, - { - path: 'tiere/Hund.java', - text: `package tiere; - -public class Hund extends Tier { - private String rasse; - - public Hund(String name, int alter, String rasse) { - super(name, alter); - this.rasse = rasse; - } - - public void lautGeben() { - System.out.println("Wuff"); - } -} -`, - }, - { - path: 'personen/Besitzer.java', - text: `package personen; - -import java.util.ArrayList; -import tiere.Hund; - -public class Besitzer { - private String name; - private ArrayList hunde = new ArrayList<>(); - - public void gassiGehen() { - for (Hund h : hunde) { - h.lautGeben(); - } - } -} -`, - }, - ], - } -} diff --git a/web/src/core/javaStringCoding.ts b/web/src/core/javaStringCoding.ts deleted file mode 100644 index 2a41a4ff..00000000 --- a/web/src/core/javaStringCoding.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** Java `XMLLeser.encodeS/decodeS`: Unicode-Codepoints als `;`-getrennte Zahlen. */ -export function encodeJavaString(s: string): string { - if (s === '') return '-1;' - let out = '' - for (const ch of s) out += ch.codePointAt(0)! + ';' - return out -} - -export function decodeJavaString(coded: string): string { - const parts = coded.split(';').filter((p) => p !== '') - if (parts.length === 1 && parts[0] === '-1') return '' - let out = '' - for (const p of parts) { - if (!/^[+-]?\d+$/.test(p)) continue - const i = Number(p) - // Java: -1 => leere Zeile - if (i === -1) continue - // Wie Swift `UnicodeScalar(i)`: keine Surrogate, nichts außerhalb des Unicode-Bereichs. - if (i < 0 || i > 0x10ffff || (i >= 0xd800 && i <= 0xdfff)) continue - out += String.fromCodePoint(i) - } - return out -} diff --git a/web/src/core/layout.ts b/web/src/core/layout.ts deleted file mode 100644 index 8764cbdc..00000000 --- a/web/src/core/layout.ts +++ /dev/null @@ -1,591 +0,0 @@ -import { ElementType, type VSDocument, type VSElement } from './model' -import { approximateMeasure, type MeasureText } from './textMeasurement' - -/** - * Port von `VisuStructLayout.swift` / `VisuStructLayoutEngineMetrics.swift` (Näherung an das Java-Layout). - * Die Struktur folgt bewusst dem Swift-Code, damit Änderungen dort leicht nachgezogen werden können. - */ - -export interface Rect { - x: number - y: number - width: number - height: number -} - -export interface LayoutNode { - /** Stabiler Pfad im Baum (für Auswahl und Bearbeitung). */ - idPath: number[] - element: VSElement - frame: Rect - /** Höhe des Kopfbands (Verzweigung/Fallauswahl), bzw. Fuß bei Do-Until. */ - headerBandHeight: number - topBandHeight: number - loopBodyLeadingInset: number - caseColumnFrames: Rect[] - dividerX?: number - dividerYInset?: number - /** Verzweigung/Fallauswahl: Kinder pro Spalte. */ - columns?: LayoutNode[][] - children: LayoutNode[] -} - -export interface Layout { - canvasSize: { width: number; height: number } - elements: LayoutNode[] -} - -export interface Metrics { - fontSize: number - lineHeight: number - charWidth: number - baseHeaderExtra: number - minSidePadding: number - cellTextInsetY: number - doUntilDoLabelTopInset: number - doUntilFooterTopInset: number - doUntilFooterBottomInset: number -} - -const defaultMetrics: Metrics = { - fontSize: 15, - lineHeight: 20, - charWidth: 7, - baseHeaderExtra: 20, - minSidePadding: 42, - cellTextInsetY: 15, - doUntilDoLabelTopInset: 2, - doUntilFooterTopInset: 6, - doUntilFooterBottomInset: 8, -} - -/** Gleiche Typometrie wie in der iOS-`DiagramCanvasView`. */ -export function diagramDisplayMetrics(isRegularWidth: boolean): Metrics { - const dF = 2 - const dLH = 5 - if (isRegularWidth) { - const fontSize = 12 + dF - return { - ...defaultMetrics, - fontSize, - lineHeight: 16 + dLH, - charWidth: 5.5 * (fontSize / 12), - baseHeaderExtra: 16 + dLH, - minSidePadding: 40, - } - } - const b = defaultMetrics - const fontSize = b.fontSize + dF - return { - ...defaultMetrics, - fontSize, - lineHeight: b.lineHeight + dLH, - charWidth: b.charWidth * (fontSize / b.fontSize), - baseHeaderExtra: b.baseHeaderExtra + 2, - minSidePadding: b.minSidePadding + 8, - } -} - -export function scaleMetrics(m: Metrics, factor: number): Metrics { - if (factor <= 0 || Math.abs(factor - 1) <= 1e-6) return m - const out = { ...m } - for (const k of Object.keys(out) as (keyof Metrics)[]) out[k] = m[k] * factor - return out -} - -const isLoopType = (t: ElementType) => - t === ElementType.forSchleife || - t === ElementType.whileSchleife || - t === ElementType.doUntilSchleife || - t === ElementType.endlosschleife - -const isMultiType = (t: ElementType) => t === ElementType.fallauswahl || t === ElementType.verzweigung - -function newNode(idPath: number[], element: VSElement, frame: Rect, headerBandHeight = 0): LayoutNode { - return { - idPath, - element, - frame, - headerBandHeight, - topBandHeight: 0, - loopBodyLeadingInset: 0, - caseColumnFrames: [], - children: [], - } -} - -/** Flache Kopie mit eigenem `frame` (Swift-Structs sind Werttypen). */ -function copy(n: LayoutNode): LayoutNode { - return { ...n, frame: { ...n.frame } } -} - -export class LayoutEngine { - constructor( - public metrics: Metrics = defaultMetrics, - private measure: MeasureText = approximateMeasure, - ) {} - - layout(document: VSDocument, inset: { left: number; top: number } = { left: 28, top: 28 }): Layout { - const rawNodes = this.layoutList(document.elements, inset.left, inset.top, []) - const nodes = rawNodes.map((n) => this.stretchLoopBodiesVertically(n)) - let maxX = 0 - let maxY = 0 - const walk = (n: LayoutNode) => { - maxX = Math.max(maxX, n.frame.x + n.frame.width) - maxY = Math.max(maxY, n.frame.y + n.frame.height) - n.children.forEach(walk) - } - nodes.forEach(walk) - return { canvasSize: { width: maxX + inset.left, height: maxY + inset.top }, elements: nodes } - } - - private lineWidth(text: string): number { - return Math.ceil(this.measure(text, this.metrics.fontSize, 'medium')) - } - - private lineWidthBold(text: string): number { - return Math.ceil(this.measure(text, this.metrics.fontSize, 'bold')) - } - - private layoutList(elements: VSElement[], x: number, y: number, idBase: number[]): LayoutNode[] { - let out: LayoutNode[] = [] - let curY = y - elements.forEach((e, i) => { - const node = this.layoutElement(e, x, curY, [...idBase, i]) - out.push(node) - curY += node.frame.height - }) - // Alle Nodes auf gleiche Breite wie die breiteste (Java: `breiteDerUnterelementeSetzen`). - const maxW = Math.max(0, ...out.map((n) => n.frame.width)) - if (maxW > 0) out = out.map((n) => this.stretchNodeTreeToWidth(n, maxW)) - return out - } - - private stretchMultiwayNodeToWidth(node: LayoutNode, newWidth: number): LayoutNode { - const n = copy(node) - if (n.caseColumnFrames.length === 0) return n - // Alte Breite aus den Spalten, nicht aus `frame.width`: Aufrufer setzen `frame.width` oft schon - // vorher auf die neue Breite. Sonst wäre scale = 1 und die Spalten blieben schmaler als der Kopf. - const oldW = n.caseColumnFrames.reduce((s, c) => s + c.width, 0) - if (!(oldW > 0)) return n - const scale = newWidth / oldW - n.frame.width = newWidth - let xCursor = n.frame.x - const newFrames: Rect[] = n.caseColumnFrames.map((col) => { - const nw = col.width * scale - const r = { x: xCursor, y: col.y, width: nw, height: col.height } - xCursor += nw - return r - }) - n.caseColumnFrames = newFrames - n.dividerX = newFrames.length > 0 ? newFrames[newFrames.length - 1].x : n.frame.x + newWidth - if (n.columns && n.columns.length === newFrames.length) { - n.columns = n.columns.map((colNodes, idx) => { - const col = newFrames[idx] - return colNodes.map((cn) => { - const c = copy(cn) - c.frame.x = col.x - c.frame.width = col.width - return this.stretchDescendantsWidths(c, col.width) - }) - }) - n.children = n.columns.flat() - } - return n - } - - private stretchLoopChildren(n: LayoutNode, newWidth: number): void { - const lead = this.effectiveLoopBodyLeading(n) - const innerW = Math.max(0, newWidth - lead) - n.children = n.children.map((child) => { - const c = copy(child) - c.frame.x = n.frame.x + lead - c.frame.width = innerW - return this.stretchDescendantsWidths(c, innerW) - }) - } - - private stretchNodeTreeToWidth(node: LayoutNode, newWidth: number): LayoutNode { - const n = copy(node) - const oldW = n.frame.width - if (!(oldW > 0)) return n - if (isMultiType(n.element.type) && n.caseColumnFrames.length > 0) { - return this.stretchMultiwayNodeToWidth(n, newWidth) - } - n.frame.width = newWidth - if (isLoopType(n.element.type)) { - this.stretchLoopChildren(n, newWidth) - } else { - n.children = n.children.map((c) => this.stretchDescendantsWidths(c, newWidth)) - } - return n - } - - private stretchDescendantsWidths(node: LayoutNode, parentWidth: number): LayoutNode { - let n = copy(node) - if (isMultiType(n.element.type)) { - n = this.stretchNodeTreeToWidth(n, parentWidth) - } else if (isLoopType(n.element.type)) { - n.frame.width = parentWidth - this.stretchLoopChildren(n, parentWidth) - } else { - n.frame.width = parentWidth - n.children = n.children.map((c) => this.stretchDescendantsWidths(c, parentWidth)) - } - return n - } - - /** Letztes Rumpf-Element von Schleifen bis zur unteren Kante strecken (verhindert weiße Streifen). */ - private stretchLoopBodiesVertically(node: LayoutNode): LayoutNode { - const n = copy(node) - if (n.columns && n.columns.length > 0) { - n.columns = n.columns.map((col) => col.map((c) => this.stretchLoopBodiesVertically(c))) - n.children = n.columns.flat() - return n - } - n.children = n.children.map((c) => this.stretchLoopBodiesVertically(c)) - - const canAbsorb = (t: ElementType) => - t === ElementType.anweisung || - t === ElementType.leerElement || - t === ElementType.aufruf || - t === ElementType.aussprung || - isLoopType(t) - - let limitY: number | undefined - switch (n.element.type) { - case ElementType.forSchleife: - case ElementType.whileSchleife: - case ElementType.endlosschleife: { - const bottomStrip = n.element.type === ElementType.endlosschleife ? 40 : 0 - limitY = n.frame.y + n.frame.height - bottomStrip - break - } - case ElementType.doUntilSchleife: - limitY = n.frame.y + n.frame.height - n.headerBandHeight - break - } - if (limitY !== undefined && n.children.length > 0) { - const li = n.children.length - 1 - let last = copy(n.children[li]) - const gap = limitY - (last.frame.y + last.frame.height) - if (gap > 0.5 && canAbsorb(last.element.type)) { - last.frame.height += gap - last = this.stretchLoopBodiesVertically(last) - n.children = [...n.children.slice(0, li), last] - } - } - return n - } - - private headerHeight(e: VSElement): number { - const m = this.metrics - const lines = Math.max(1, e.textLines.length) - if (e.type === ElementType.doUntilSchleife) { - return m.doUntilFooterTopInset + lines * m.lineHeight + m.doUntilFooterBottomInset + e.zoomY - } - let baseExtra: number - switch (e.type) { - case ElementType.fallauswahl: - baseExtra = 40 - break - case ElementType.verzweigung: - baseExtra = 20 - break - case ElementType.forSchleife: - case ElementType.whileSchleife: - case ElementType.endlosschleife: - baseExtra = m.baseHeaderExtra + 4 - break - default: - baseExtra = 14 - } - return baseExtra + lines * m.lineHeight + e.zoomY - } - - private doUntilBodyLeadingInset(): number { - return Math.max(20, this.lineWidthBold('do') + 12) - } - - private effectiveLoopBodyLeading(node: LayoutNode): number { - if (node.element.type === ElementType.doUntilSchleife) { - return node.loopBodyLeadingInset > 0 ? node.loopBodyLeadingInset : this.doUntilBodyLeadingInset() - } - return 20 - } - - private horizontalPaddingInsideCell(t: ElementType): number { - if (isMultiType(t)) return Math.max(100, this.metrics.minSidePadding * 2.45) - return this.metrics.minSidePadding - } - - private minWidth(e: VSElement): number { - const measured = e.textLines.length === 0 ? 0 : Math.max(...e.textLines.map((l) => this.lineWidth(l))) - let w = measured + e.zoomX + this.horizontalPaddingInsideCell(e.type) - if (e.type === ElementType.aufruf) { - const callMargins = Math.max(68, this.metrics.minSidePadding * 1.7) - const breathingRoom = Math.max(8, this.metrics.minSidePadding * 0.2) - w = Math.max(w, measured + e.zoomX + callMargins + breathingRoom) - } - const floor = e.type === ElementType.leerElement ? 76 : e.type === ElementType.aufruf ? 104 : 72 - return Math.max(floor, w) - } - - private layoutElement(e: VSElement, x: number, y: number, idPath: number[]): LayoutNode { - if (isMultiType(e.type)) return this.layoutMultiway(e, x, y, idPath) - if (isLoopType(e.type)) return this.layoutLoop(e, x, y, idPath) - const h = this.headerHeight(e) - return newNode(idPath, e, { x, y, width: this.minWidth(e), height: h }, h) - } - - private layoutLoop(e: VSElement, x: number, y: number, idPath: number[]): LayoutNode { - const sumH = (ns: LayoutNode[]) => ns.reduce((s, n) => s + n.frame.height, 0) - const maxW = (ns: LayoutNode[]) => Math.max(0, ...ns.map((n) => n.frame.width)) - const body = e.loopBody ?? [] - - if (e.type === ElementType.doUntilSchleife) { - const bodyLeading = this.doUntilBodyLeadingInset() - const doBand = e.zoomY - const footerH = this.headerHeight(e) - const children = this.layoutList(body, x + bodyLeading, y + doBand, [...idPath, 0]) - const w = Math.max(this.minWidth(e), maxW(children) + bodyLeading) - const h = sumH(children) + footerH + doBand - const innerW = Math.max(0, w - bodyLeading) - const node = newNode(idPath, e, { x, y, width: w, height: h }, footerH) - node.topBandHeight = doBand - node.loopBodyLeadingInset = bodyLeading - node.children = children.map((c) => { - const nn = copy(c) - nn.frame.x = x + bodyLeading - nn.frame.width = innerW - return this.stretchDescendantsWidths(nn, innerW) - }) - return node - } - - const leftInset = 20 - const header = this.headerHeight(e) - const bottom = e.type === ElementType.endlosschleife ? 40 : 0 - const children = this.layoutList(body, x + leftInset, y + header, [...idPath, 0]) - const w = Math.max(this.minWidth(e), maxW(children) + leftInset) - const h = header + bottom + sumH(children) - const innerW = Math.max(0, w - leftInset) - const node = newNode(idPath, e, { x, y, width: w, height: h }, header) - node.children = children.map((c) => { - const nn = copy(c) - nn.frame.x = x + leftInset - nn.frame.width = innerW - return this.stretchDescendantsWidths(nn, innerW) - }) - return node - } - - private layoutMultiway(e: VSElement, x: number, y: number, idPath: number[]): LayoutNode { - const header = this.headerHeight(e) - const cases = e.cases ?? [] - const colCount = Math.max(1, cases.length) - const colWidthGuess = Math.max(this.minWidth(e) / colCount, 132) - - let columnFrames: Rect[] = [] - let columnChildren: LayoutNode[][] = [] - let totalW = 0 - let maxColH = 0 - - cases.forEach((c, i) => { - const colX = x + totalW - const listNodes = this.layoutList(c.elements, colX, y + header, [...idPath, i]) - const contentW = Math.max(0, ...listNodes.map((n) => n.frame.width)) - let colW: number - if (e.type === ElementType.verzweigung && colCount === 2) { - const branchLabel = i === 0 ? 'true' : 'false' - colW = Math.max(contentW, branchLabel.length * this.metrics.charWidth + 24) - } else { - colW = Math.max(colWidthGuess, contentW) - } - const colH = listNodes.reduce((s, n) => s + n.frame.height, 0) - maxColH = Math.max(maxColH, colH) - columnFrames.push({ x: colX, y: y + header, width: colW, height: colH }) - columnChildren.push( - listNodes.map((n) => { - const nn = copy(n) - nn.frame.x = colX - nn.frame.width = colW - return this.stretchDescendantsWidths(nn, colW) - }), - ) - totalW += colW - }) - - // Zweiarmige Verzweigung: beide Spalten gleich breit. Nicht, wenn eine Spalte selbst eine - // Verzweigung oder Fallauswahl enthält: Sonst verdoppelt sich die Breite mit jeder Ebene - // (verschachtelte else-Ketten werden unlesbar breit). - if ( - e.type === ElementType.verzweigung && - colCount === 2 && - columnFrames.length === 2 && - !cases.some((c) => containsMultiway(c.elements)) - ) { - const target = Math.max(columnFrames[0].width, columnFrames[1].width) - const xLeft = x - const xRight = x + target - const deltaRight = xRight - columnFrames[1].x - columnFrames[0] = { ...columnFrames[0], x: xLeft, width: target } - columnFrames[1] = { ...columnFrames[1], x: xRight, width: target } - totalW = target * 2 - columnChildren[0] = columnChildren[0].map((cn) => { - const c = copy(cn) - c.frame.x = xLeft - c.frame.width = target - return this.stretchDescendantsWidths(c, target) - }) - columnChildren[1] = columnChildren[1].map((cn) => { - const c = shiftTreeX(cn, deltaRight) - c.frame.width = target - return this.stretchDescendantsWidths(c, target) - }) - } - - if (columnChildren.length > 0) { - const harmonized = harmonizeRowHeightsAcrossColumns(columnChildren, y + header) - columnChildren = harmonized.columns.map((col) => col.map((c) => this.stretchLoopBodiesVertically(c))) - for (let i = 0; i < Math.min(columnFrames.length, harmonized.columnHeights.length); i++) { - columnFrames[i] = { ...columnFrames[i], height: harmonized.columnHeights[i] } - } - maxColH = harmonized.columnHeights.length > 0 ? Math.max(...harmonized.columnHeights) : maxColH - } - - let w = Math.max(totalW, this.minWidth(e)) - if (totalW > 0.5 && w > totalW + 0.5) { - const scale = w / totalW - let xCursor = x - const newFrames: Rect[] = [] - const newCols: LayoutNode[][] = [] - columnFrames.forEach((col, i) => { - const nw = col.width * scale - newCols.push( - columnChildren[i].map((cn) => { - const c = copy(cn) - c.frame.x = xCursor - c.frame.width = nw - return this.stretchDescendantsWidths(c, nw) - }), - ) - newFrames.push({ x: xCursor, y: col.y, width: nw, height: col.height }) - xCursor += nw - }) - columnFrames = newFrames - columnChildren = newCols - totalW = xCursor - x - w = totalW - } - - const lastW = columnFrames.length > 0 ? columnFrames[columnFrames.length - 1].width : totalW / colCount - const node = newNode(idPath, e, { x, y, width: w, height: header + maxColH }, header) - node.caseColumnFrames = columnFrames - node.dividerX = columnFrames.length > 0 ? columnFrames[columnFrames.length - 1].x : x + totalW - lastW - node.dividerYInset = e.type === ElementType.fallauswahl ? -20 : 0 - node.columns = columnChildren - node.children = columnChildren.flat() - return node - } -} - -/** Steckt irgendwo (auch in Schleifen) eine Verzweigung oder Fallauswahl? */ -function containsMultiway(elements: VSElement[]): boolean { - return elements.some( - (el) => - isMultiType(el.type) || - containsMultiway(el.loopBody ?? []) || - (el.cases ?? []).some((c) => containsMultiway(c.elements)), - ) -} - -function shiftDescendantsY(node: LayoutNode, delta: number): LayoutNode { - if (Math.abs(delta) <= 0.0001) return node - const n = copy(node) - const shift = (c: LayoutNode) => { - const cc = copy(c) - cc.frame.y += delta - return shiftDescendantsY(cc, delta) - } - if (n.columns) { - n.columns = n.columns.map((col) => col.map(shift)) - n.children = n.columns.flat() - } else { - n.children = n.children.map(shift) - } - return n -} - -function shiftTreeX(node: LayoutNode, delta: number): LayoutNode { - if (Math.abs(delta) <= 0.0001) return node - const n = copy(node) - n.frame.x += delta - if (n.columns) { - n.columns = n.columns.map((col) => col.map((c) => shiftTreeX(c, delta))) - n.children = n.columns.flat() - } else { - n.children = n.children.map((c) => shiftTreeX(c, delta)) - } - return n -} - -/** Zeilenhöhen spaltenübergreifend synchronisieren, damit horizontale Trennlinien nicht „stufig“ werden. */ -function harmonizeRowHeightsAcrossColumns( - columns: LayoutNode[][], - startY: number, -): { columns: LayoutNode[][]; columnHeights: number[] } { - if (columns.length < 2) { - return { columns, columnHeights: columns.map((c) => c.reduce((s, n) => s + n.frame.height, 0)) } - } - const eps = 0.5 - const lines: number[] = [startY] - for (const col of columns) for (const n of col) lines.push(n.frame.y + n.frame.height) - const maxBottom = Math.max(...lines) - lines.push(maxBottom) - lines.sort((a, b) => a - b) - - let merged: number[] = [] - for (const v of lines) { - if (merged.length > 0 && Math.abs(v - merged[merged.length - 1]) <= eps) continue - merged.push(v) - } - if (merged.length === 0 || Math.abs(merged[0] - startY) > eps) merged.unshift(startY) - if (merged.length === 0) merged = [startY, maxBottom] - else if (merged[merged.length - 1] < maxBottom - 1e-6) merged.push(maxBottom) - else merged[merged.length - 1] = maxBottom - - const nextLineIndex = (y: number, minIndex: number) => { - let i = Math.max(0, minIndex) - while (i < merged.length && merged[i] < y - eps) i++ - return Math.min(i, merged.length - 1) - } - - const outCols: LayoutNode[][] = [] - const colHeights: number[] = [] - for (const col of columns) { - if (col.length === 0) { - outCols.push([]) - colHeights.push(0) - continue - } - const newCol: LayoutNode[] = [] - let topLineIdx = 0 - col.forEach((orig, idx) => { - let n = copy(orig) - const desiredTop = merged[topLineIdx] - const origBottom = orig.frame.y + orig.frame.height - const bottomLineIdx = idx === col.length - 1 ? merged.length - 1 : nextLineIndex(origBottom, topLineIdx + 1) - const desiredBottom = merged[bottomLineIdx] - const deltaY = desiredTop - n.frame.y - n.frame.y = desiredTop - n.frame.height = Math.max(0, desiredBottom - desiredTop) - n = shiftDescendantsY(n, deltaY) - newCol.push(n) - topLineIdx = bottomLineIdx - }) - outCols.push(newCol) - colHeights.push(merged[merged.length - 1] - startY) - } - return { columns: outCols, columnHeights: colHeights } -} diff --git a/web/src/core/lineEditorText.ts b/web/src/core/lineEditorText.ts deleted file mode 100644 index 615d4d0c..00000000 --- a/web/src/core/lineEditorText.ts +++ /dev/null @@ -1,95 +0,0 @@ -/** Port von `Editing/VisuStructLineEditorText.swift`: Textumformungen für den Zeileneditor (ohne UI). */ - -export type StatementIOMode = 'statement' | 'input' | 'output' - -const isWs = (c: string | undefined) => c !== undefined && /\s/.test(c) - -export function ioMode(text: string): StatementIOMode { - const t = text.trim().toLowerCase() - if (t.startsWith('input:')) return 'input' - if (t.startsWith('output:') || t.startsWith('print:')) return 'output' - return 'statement' -} - -/** Entfernt `input:` / `output:` / `print:` und umgebende Leerzeichen; ohne Präfix bleibt der Text unverändert. */ -export function strippingIOPrefix(s: string): string { - const trimmed = s.trim() - const lower = trimmed.toLowerCase() - if (lower.startsWith('input:')) return trimmed.slice(6).trim() - if (lower.startsWith('output:')) return trimmed.slice(7).trim() - if (lower.startsWith('print:')) return trimmed.slice(6).trim() - return s -} - -function prefixing(p: string, content: string): string { - const c = content.trim() - return c === '' ? `${p} ` : `${p} ${c}` -} - -/** Text nach Wechsel der Art: Präfix tauschen, Inhalt behalten. */ -export function applyingIOMode(mode: StatementIOMode, text: string): string { - const stripped = strippingIOPrefix(text) - switch (mode) { - case 'statement': return stripped - case 'input': return prefixing('input:', stripped) - case 'output': return prefixing('output:', stripped) - } -} - -const dropOneLeadingWhitespace = (s: string) => (isWs(s[0]) ? s.slice(1) : s) - -function strippingIOPrefixPreservingEditingWhitespace(s: string): string { - const lead = s.replace(/^\s+/, '') - const lower = lead.toLowerCase() - if (lower.startsWith('input:')) return dropOneLeadingWhitespace(lead.slice(6)) - if (lower.startsWith('output:')) return dropOneLeadingWhitespace(lead.slice(7)) - if (lower.startsWith('print:')) return dropOneLeadingWhitespace(lead.slice(6)) - return s -} - -function strippingKeywordPrefixPreservingEditingWhitespace(keyword: string, s: string): string { - const lead = s.replace(/^\s+/, '') - if (!lead.toLowerCase().startsWith(keyword.toLowerCase())) return s - const after = lead.slice(keyword.length) - if (!(after === '' || isWs(after[0]) || after[0] === '(')) return s - const content = dropOneLeadingWhitespace(after) - if (content.startsWith('(') && content.endsWith(')') && content.length >= 2) return content.slice(1, -1) - return content -} - -/** Wie `strippingIOPrefix` bzw. Schlüsselwort-Entfernung, aber Leerzeichen beim Tippen bleiben erhalten. */ -export function strippingPrefixForEditing(p: string, s: string): string { - if (p === 'input:' || p === 'output:') return strippingIOPrefixPreservingEditingWhitespace(s) - const stripped = strippingKeywordPrefixPreservingEditingWhitespace(p, s) - if (p === 'while' && stripped === s) { - const legacy = strippingKeywordPrefixPreservingEditingWhitespace('until', s) - if (legacy !== s) return legacy - } - return stripped -} - -export function prefixingForEditing(p: string, content: string): string { - return content === '' ? `${p} ` : `${p} ${content}` -} - -/** Typografische Anführungszeichen (z. B. von der Tastatur) werden zu geraden Quotes. */ -export function normalizingQuotes(s: string): string { - return s.replace(/[“”„«»]/g, '"').replace(/[’‘‚´]/g, "'") -} - -/** Genau drei Felder (Start, Bedingung, Schritt) der Zählschleife. */ -export function paddedForLoopParts(lines: string[]): string[] { - const a = lines.length === 0 ? [''] : lines.slice(0, 3) - while (a.length < 3) a.push('') - return a -} - -/** Ein Feld der Zählschleife: gerade Quotes, ohne abschließende `;`. */ -export function normalizedForLoopPart(s: string): string { - let x = normalizingQuotes(s) - for (;;) { - const t = x.trim() - if (!t.endsWith(';')) return x - x = t.slice(0, -1).trim() - } -} diff --git a/web/src/core/liveSource.ts b/web/src/core/liveSource.ts deleted file mode 100644 index b13e1513..00000000 --- a/web/src/core/liveSource.ts +++ /dev/null @@ -1,186 +0,0 @@ -import { documentReadsFiles, generateLiveCode, livePlaceholderEnd, livePlaceholderStart, sanitizedJavaClassName, type LiveCode, type LiveLabels } from './codeGenerator' -import { parseLiveJava, type JavaParseOptions, type ParsedJava } from './javaParser' -import type { VSDocument, VSElement } from './model' -import { parseSubprogramHeader } from './simulation' - -/** - * Getippter Java-Code und Struktogramm gehören zusammen: Ändert man danach das Struktogramm, - * werden nur die Zeilen der geänderten Elemente neu geschrieben. Eigene Kommentare, Leerzeilen - * und Einrückungen im übrigen Code bleiben so, wie man sie getippt hat. - */ - -/** Form des Java-Codes in „Code live“ (Zahnrad-Menü): Snippet, mit `main`, in einer Klasse oder beides. */ -export interface JavaForm { - /** `public class { … }` */ - asClass: boolean - /** `public static void main(String[] args) { … }` */ - withMain: boolean -} - -export const snippetForm: JavaForm = { asClass: false, withMain: false } - -const isImport = (line: string) => /^import\s+[\w.]+(?:\.\*)?\s*;$/.test(line.trim()) -const indented = (lines: string[]) => lines.map((l) => (l.trim() === '' ? '' : ' ' + l)) - -/** Erzeugter Java-Code der Ansicht „Code live“ als reiner Text (Platzhalter ohne Markierung) mit den Zeilen jedes Elements. */ -export function liveJava(doc: VSDocument, labels: LiveLabels, form: JavaForm = snippetForm): LiveCode { - const live = generateLiveCode(doc, 'java', labels) - let lines = live.text.replaceAll(livePlaceholderStart, '').replaceAll(livePlaceholderEnd, '').split('\n') - let spans = live.spans - const shift = (by: number) => (spans = spans.map((s) => ({ ...s, from: s.from + by, to: s.to + by }))) - const isMethod = parseSubprogramHeader(doc.caption) !== undefined - // Um `main` oder eine Klasse herum gehören die Imports ganz nach oben. - const imports: string[] = [] - if ((form.withMain && !isMethod) || form.asClass) { - for (let at = lines.findIndex(isImport); at >= 0; at = lines.findIndex(isImport)) { - imports.push(lines[at].trim()) - lines.splice(at, 1) - spans = spans.map((s) => ({ ...s, from: s.from > at ? s.from - 1 : s.from, to: s.to > at ? s.to - 1 : s.to })) - } - // Die Leerzeile hinter den Imports wandert mit nach oben. - if (imports.length && lines[0]?.trim() === '') { - lines.splice(0, 1) - spans = spans.map((s) => ({ ...s, from: s.from > 0 ? s.from - 1 : s.from, to: s.to > 0 ? s.to - 1 : s.to })) - } - if (imports.length) imports.push('') - } - // Ein Unterprogramm ist schon eine Methode: kein `main` drumherum. - if (form.withMain && !isMethod) { - // `Files.readString` kann eine IOException werfen; Java verlangt dann `throws`. - lines = [`public static void main(String[] args)${documentReadsFiles(doc) ? ' throws Exception' : ''} {`, ...indented(lines), '}'] - shift(1) - } - if (form.asClass) { - lines = [`public class ${sanitizedJavaClassName(isMethod ? 'Main' : doc.caption)} {`, ...indented(lines), '}'] - shift(1) - } - if (imports.length) { - lines = [...imports, ...lines] - shift(imports.length) - } - return { text: lines.join('\n'), spans } -} - -export function plainLiveJava(doc: VSDocument, labels: LiveLabels, form: JavaForm = snippetForm): string { - return liveJava(doc, labels, form).text -} - -/** Zum Vergleichen: ohne Leerraum, Kommentar am Zeilenende weg (ganze Kommentarzeilen bleiben). */ -function normalized(line: string): string { - const code = splitTrailingComment(line).code - return (code.trim() === '' ? line : code).replace(/\s+/g, '') -} - -/** `x++; // zählen` → Code und Kommentar (Zeichenketten werden übersprungen). */ -function splitTrailingComment(line: string): { code: string; comment: string } { - let quote = '' - for (let i = 0; i < line.length; i++) { - const c = line[i] - if (quote) { - if (c === '\\') i++ - else if (c === quote) quote = '' - } else if (c === '"' || c === "'") quote = c - else if (c === '/' && line[i + 1] === '/') return { code: line.slice(0, i).replace(/\s+$/, ''), comment: line.slice(i) } - } - return { code: line, comment: '' } -} - -type Op = { kind: 'keep'; a: number; b: number } | { kind: 'del'; a: number } | { kind: 'ins'; b: number } - -/** Zeilenweiser Vergleich (längste gemeinsame Teilfolge). */ -function diff(a: string[], b: string[], eq: (x: string, y: string) => boolean): Op[] { - const n = a.length - const m = b.length - const lcs: number[][] = Array.from({ length: n + 1 }, () => new Array(m + 1).fill(0)) - for (let i = n - 1; i >= 0; i--) { - for (let j = m - 1; j >= 0; j--) lcs[i][j] = eq(a[i], b[j]) ? lcs[i + 1][j + 1] + 1 : Math.max(lcs[i + 1][j], lcs[i][j + 1]) - } - const ops: Op[] = [] - let i = 0 - let j = 0 - while (i < n || j < m) { - if (i < n && j < m && eq(a[i], b[j])) ops.push({ kind: 'keep', a: i++, b: j++ }) - // Bei Gleichstand erst löschen, dann einfügen: So landet die neue Zeile an der Stelle der alten. - else if (i < n && (j === m || lcs[i + 1][j] >= lcs[i][j + 1])) ops.push({ kind: 'del', a: i++ }) - else ops.push({ kind: 'ins', b: j++ }) - } - return ops -} - -/** - * `userText` gehört zu `oldDoc`; das Struktogramm wurde zu `newDoc` geändert. - * Ergebnis: `userText` mit den Änderungen. Lässt sich das nicht sicher übertragen, der neu erzeugte Code. - */ -export function mergeLiveJava( - userText: string, - oldDoc: VSDocument, - newDoc: VSDocument, - labels: LiveLabels, - opts: JavaParseOptions = {}, - form: JavaForm = snippetForm, -): string { - const generatedNew = plainLiveJava(newDoc, labels, form) - const generatedOld = plainLiveJava(oldDoc, labels, form) - if (userText === generatedOld) return generatedNew - const oldLines = generatedOld.split('\n') - const newLines = generatedNew.split('\n') - const user = userText.split('\n') - // Welche Zeile des getippten Codes entspricht welcher erzeugten Zeile? - const where = new Array(oldLines.length).fill(-1) - for (const op of diff(oldLines, user, (x, y) => normalized(x) !== '' && normalized(x) === normalized(y))) { - if (op.kind === 'keep') where[op.a] = op.b - } - const out: string[] = [] - let u = 0 - let comment = '' - const copyUntil = (end: number) => { - while (u < end) out.push(user[u++]) - } - for (const op of diff(oldLines, newLines, (x, y) => x === y)) { - if (op.kind === 'keep') { - if (where[op.a] >= 0) { - copyUntil(where[op.a] + 1) - comment = '' - } - } else if (op.kind === 'del') { - const at = where[op.a] - if (at < 0) continue - copyUntil(at) - comment ||= splitTrailingComment(user[at]).comment - u = at + 1 - } else { - const line = newLines[op.b] - out.push(comment && line.trim() !== '' ? `${line} ${comment}` : line) - if (line.trim() !== '') comment = '' - } - } - copyUntil(user.length) - const merged = out.join('\n') - // Nur übernehmen, wenn der Code wirklich das neue Struktogramm ergibt. - const parsed = parseLiveJava(merged, labels, opts) - return plainLiveJava(documentFromJava(newDoc, parsed), labels, form) === generatedNew ? merged : generatedNew -} - -/** - * Neues Dokument aus getipptem Code. Titel, Schrift und die Farben/Größen der Kästen bleiben, - * soweit an derselben Stelle noch dieselbe Art Element steht. - */ -export function documentFromJava(prev: VSDocument, parsed: ParsedJava): VSDocument { - let caption = parsed.caption ?? (parseSubprogramHeader(prev.caption) ? '' : prev.caption) - // `public class Rechner` wird zum Titel; aus „Mein Programm“ wird zwar `Mein_Programm`, der Titel bleibt dann aber. - if (!parsed.caption && parsed.className && sanitizedJavaClassName(caption) !== parsed.className) caption = parsed.className - return { ...prev, caption, elements: carryLooks(prev.elements, parsed.elements) } -} - -function carryLooks(old: VSElement[], next: VSElement[]): VSElement[] { - return next.map((e, i) => { - const o = old[i] - if (!o || o.type !== e.type) return e - const out: VSElement = { ...e, zoomX: o.zoomX, zoomY: o.zoomY } - if (o.textColor) out.textColor = o.textColor - if (o.backgroundColor) out.backgroundColor = o.backgroundColor - if (e.loopBody) out.loopBody = carryLooks(o.loopBody ?? [], e.loopBody) - if (e.cases) out.cases = e.cases.map((c, k) => ({ ...c, elements: carryLooks(o.cases?.[k]?.elements ?? [], c.elements) })) - return out - }) -} diff --git a/web/src/core/mbot.ts b/web/src/core/mbot.ts deleted file mode 100644 index 8ab579c5..00000000 --- a/web/src/core/mbot.ts +++ /dev/null @@ -1,415 +0,0 @@ -import { showLabel } from './mbotLink' - -/** - * mBot2 (Makeblock, mit CyberPi) im Struktogramm. Befehle stehen als Anweisung `mbot.forward(10)`, - * Sensoren als Ausdruck `mbot.getDistance()`. Die Simulation merkt sich jeden Befehl als `MBotAction`; - * mit angeschlossenem Roboter schickt die Oberfläche den Python-Text an den CyberPi. - */ - -export interface MBotAction { - method: string - args: number[] - /** Zeile für die Makeblock-Firmware; `undefined`, wenn der Browser selbst wartet (`pause`). */ - python?: string - /** Wartezeit im Browser in Millisekunden (nur `pause`). */ - waitMs?: number - /** Kurz für die Konsole, z. B. „10 cm vorwärts“. */ - label: string - /** Text für den Bildschirm (`showText`). */ - text?: string -} - -interface CommandSpec { - args: number - /** Werte, wenn die Klammern leer bleiben: `mbot.forward()` fährt einen Schritt (10 cm), `turnRight()` dreht 90°. */ - defaults?: number[] - python?: (a: number[], text: string) => string - label: (a: number[], text: string) => string - /** Das letzte Argument ist ein Text, z. B. `mbot.showText("Hallo")`; `args` zählt nur die Zahlen davor. */ - text?: boolean - /** Erlaubte Texte (sonst ist der Befehl ungültig). */ - choices?: readonly string[] - /** Sind die Zahlen erlaubt? (z. B. LED-Nummer 1–5) */ - valid?: (a: number[]) => boolean -} - -/** Der CyberPi hat fünf LEDs; in Java zählen sie wie Arrays ab 0 (`mbot.led(0)` bis `mbot.led(4)`), die Firmware ab 1. */ -export const MBOT_LED_COUNT = 5 -const ledNumber = (nr: number) => Number.isInteger(nr) && nr >= 0 && nr < MBOT_LED_COUNT - -/** Farben für `mbot.led(i).setColor("grün")`, deutsch und englisch; „aus“ schaltet die LED aus. */ -export const MBOT_LED_COLORS: Record = { - rot: [255, 0, 0], red: [255, 0, 0], - orange: [255, 100, 0], - gelb: [255, 200, 0], yellow: [255, 200, 0], - grün: [0, 255, 0], green: [0, 255, 0], - türkis: [0, 200, 200], cyan: [0, 200, 200], - blau: [0, 0, 255], blue: [0, 0, 255], - lila: [160, 0, 255], purple: [160, 0, 255], - pink: [255, 0, 120], - weiß: [255, 255, 255], white: [255, 255, 255], - aus: [0, 0, 0], off: [0, 0, 0], -} - -/** - * Vorführungen der Firmware (`cyberpi.emotion.*`): LEDs, Ton und kleine Bewegungen, am echten mBot2 getestet mit `smile`. - * `sprint` fehlt absichtlich, weil der Roboter dabei losfährt. - */ -export const MBOT_EMOTIONS = ['smile', 'hello', 'yeah', 'proud', 'wow', 'yummy', 'naughty', 'uh_oh', 'hurt', 'sad', 'angry', 'startle', 'shiver', 'dizzy', 'sleepy', 'zzz', 'wake_up', 'blink', 'yes', 'no', 'look_up', 'look_down', 'look_left', 'look_right', 'look_around'] as const - -/** Ein Schritt von `mbot.forward()` ohne Zahl. */ -export const MBOT_STEP_CM = 10 - -const n = (v: number) => String(Math.round(v * 100) / 100) - -/** - * Raddurchmesser des mBot2 in cm (Reifen außen, wie im 3D-Modell). Eine Umdrehung ist π · 6,5 ≈ 20,4 cm. - * Am echten Roboter rechnet die Firmware selbst in cm; bei `mbot2.straight` geben wir die umgerechnete Strecke. - */ -export const MBOT_WHEEL_DIAMETER_CM = 6.5 -export const MBOT_WHEEL_CIRCUMFERENCE_CM = Math.PI * MBOT_WHEEL_DIAMETER_CM - -/** - * Einheiten für die Überladung `mbot.forward(2, "Umdrehungen")` bzw. `mbot.backward(30, "cm")`: - * Faktor in cm und die Wörter für die Konsole. - */ -const DRIVE_UNITS: Record = { - cm: { cm: 1, one: 'cm', many: 'cm' }, - umdrehungen: { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'Umdrehung', many: 'Umdrehungen' }, - umdrehung: { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'Umdrehung', many: 'Umdrehungen' }, - rotations: { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'rotation', many: 'rotations' }, - rotation: { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'rotation', many: 'rotations' }, - 'rotações': { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'rotação', many: 'rotações' }, - 'rotação': { cm: MBOT_WHEEL_CIRCUMFERENCE_CM, one: 'rotação', many: 'rotações' }, -} - -/** Erlaubte Einheiten (für Fehlermeldungen). */ -export const MBOT_DRIVE_UNITS = ['cm', 'Umdrehungen', 'rotations'] as const - -/** - * `mbot.forward(2, "Umdrehungen")`: Fahrt mit Einheit. Die Aktion speichert wie `forward(41)` die Strecke in cm, - * damit Simulation, Übungsprüfung und echter Roboter nichts Neues lernen müssen; nur die Konsole nennt die Umdrehungen. - * `undefined`, wenn die Einheit unbekannt ist. - */ -export function makeMBotDriveAction(method: 'forward' | 'backward', value: number, unit: string): MBotAction | undefined { - const u = DRIVE_UNITS[unit.trim().toLowerCase()] - if (!u) return undefined - const cm = value * u.cm - const action = makeMBotAction(method, [cm]) - if (u.cm === 1) return action - const word = value === 1 ? u.one : u.many - return { ...action, label: `${n(value)} ${word} ${method === 'forward' ? 'vorwärts' : 'rückwärts'} (${n(cm)} cm)` } -} - -/** Befehle (Anweisungen). Drehen: positiver Winkel bei `mbot2.turn` dreht nach rechts. */ -export const MBOT_COMMANDS: Record = { - forward: { args: 1, defaults: [MBOT_STEP_CM], python: ([cm]) => `mbot2.straight(${n(cm)})`, label: ([cm]) => `${n(cm)} cm vorwärts` }, - backward: { args: 1, defaults: [MBOT_STEP_CM], python: ([cm]) => `mbot2.straight(${n(-cm)})`, label: ([cm]) => `${n(cm)} cm rückwärts` }, - turnLeft: { args: 1, defaults: [90], python: ([deg]) => `mbot2.turn(${n(-deg)})`, label: ([deg]) => `${n(deg)}° nach links drehen` }, - turnRight: { args: 1, defaults: [90], python: ([deg]) => `mbot2.turn(${n(deg)})`, label: ([deg]) => `${n(deg)}° nach rechts drehen` }, - stop: { args: 0, python: () => `mbot2.EM_stop("ALL")`, label: () => 'anhalten' }, - /** - * Räder einzeln, Leistung in Prozent (−100 … 100); er fährt weiter, bis ein anderer Fahrbefehl oder `stop()` kommt. - * Der rechte Motor ist am mBot2 gespiegelt eingebaut, deshalb das Minus. - */ - drive: { args: 2, python: ([l, r]) => `mbot2.drive_power(${n(l)}, ${n(-r)})`, label: ([l, r]) => `Räder links ${n(l)} %, rechts ${n(r)} %` }, - setLed: { args: 3, python: ([r, g, b]) => `cyberpi.led.on(${n(r)}, ${n(g)}, ${n(b)})`, label: ([r, g, b]) => `LEDs (${n(r)}, ${n(g)}, ${n(b)})` }, - ledOff: { args: 0, python: () => 'cyberpi.led.off()', label: () => 'LEDs aus' }, - /** `mbot.led(i).setColor("grün")`: eine LED (0–4); aus ist `on(0, 0, 0)`. */ - ledColor: { - args: 1, - text: true, - choices: Object.keys(MBOT_LED_COLORS), - valid: ([nr]) => ledNumber(nr), - python: ([nr], color) => `cyberpi.led.on(${MBOT_LED_COLORS[color].join(', ')}, id=${nr + 1})`, - label: ([nr], color) => `LED ${nr}: ${color}`, - }, - playTone: { args: 2, python: ([hz, ms]) => `cyberpi.audio.play_tone(${n(hz)}, ${n(ms / 1000)})`, label: ([hz, ms]) => `Ton ${n(hz)} Hz, ${n(ms)} ms` }, - pause: { args: 1, label: ([ms]) => `${n(ms)} ms warten` }, - resetTimer: { args: 0, python: () => 'cyberpi.timer.reset()', label: () => 'Stoppuhr auf 0' }, - emotion: { args: 0, text: true, choices: MBOT_EMOTIONS, python: (_, name) => `cyberpi.emotion.${name}()`, label: (_, name) => `Gefühl: ${name}` }, - showText: { args: 0, text: true, python: (_, text) => showLabel(text), label: (_, text) => `Bildschirm: ${text}` }, -} - -/** Messwert eines Sensors: Zahl, wahr/falsch oder Text (Farbname bei `getColor()`). */ -export type MBotSensorValue = number | boolean | string - -interface SensorSpec { - python: string - /** Wert in der Simulation, wenn weder Welt noch Regler ihn liefern */ - simulated: MBotSensorValue -} - -/** Farben, die der Quad-RGB-Sensor im Simulator erkennt (Felder `r g b y` in einer Weltkarte). */ -export const MBOT_COLORS = ['red', 'green', 'blue', 'yellow'] as const -export type MBotColor = (typeof MBOT_COLORS)[number] - -/** Farbnamen von `mbot.getColor()`: wie bei `setColor`, Deutsch oder Englisch je nach Sprache der Lektionen. */ -const COLOR_NAMES: Record = { - red: { de: 'rot', en: 'red' }, - green: { de: 'grün', en: 'green' }, - blue: { de: 'blau', en: 'blue' }, - yellow: { de: 'gelb', en: 'yellow' }, - cyan: { de: 'türkis', en: 'cyan' }, - purple: { de: 'lila', en: 'purple' }, - white: { de: 'weiß', en: 'white' }, - black: { de: 'schwarz', en: 'black' }, -} -let colorLanguage: 'de' | 'en' = 'en' - -/** Sprache der Farbnamen, die `mbot.getColor()` liefert (Deutsch nur bei deutschen Lektionen). */ -export function setMBotColorLanguage(language: string) { - colorLanguage = language === 'de' ? 'de' : 'en' -} - -export const mbotColorLanguage = () => colorLanguage - -/** `red`, `rot` oder eine Antwort des echten Sensors → Farbname in der aktuellen Sprache. */ -export function mbotColorName(color: string): string { - const key = color.trim().toLowerCase() - const entry = COLOR_NAMES[key] ?? Object.values(COLOR_NAMES).find((e) => e.de === key) - return entry ? entry[colorLanguage] : key -} -const press = (key: string): SensorSpec => ({ python: `cyberpi.controller.is_press('${key}')`, simulated: false }) - -/** - * Sensoren (Ausdrücke ohne Argumente): Zahlen oder Wahrheitswerte. - * Der Python-Text liest den Wert am echten mBot2 (Modus 1, Antwort `{"ret":…}`). - */ -export const MBOT_SENSORS: Record = { - getDistance: { python: 'mbuild.ultrasonic2.get(1)', simulated: 100 }, - getLine: { python: "mbuild.quad_rgb_sensor.get_line_sta('all', 1)", simulated: 0 }, - // die beiden mittleren Fühler (L1, R1) des Liniensensors - isLineLeft: { python: "mbuild.quad_rgb_sensor.is_line('L1', 1)", simulated: false }, - isLineRight: { python: "mbuild.quad_rgb_sensor.is_line('R1', 1)", simulated: false }, - /** Farbe unter dem mBot2 als Text: `mbot.getColor().equals("rot")` */ - getColor: { python: "mbuild.quad_rgb_sensor.get_color_sta('L1', 1)", simulated: 'white' }, - isButtonA: press('a'), - isButtonB: press('b'), - isJoystickUp: press('up'), - isJoystickDown: press('down'), - isJoystickLeft: press('left'), - isJoystickRight: press('right'), - getBrightness: { python: 'cyberpi.get_bri()', simulated: 50 }, - getLoudness: { python: "cyberpi.get_loudness('maximum')", simulated: 0 }, - getPitch: { python: 'cyberpi.get_pitch()', simulated: 0 }, - getRoll: { python: 'cyberpi.get_roll()', simulated: 0 }, - isShaken: { python: 'cyberpi.is_shake()', simulated: false }, - getBattery: { python: 'cyberpi.get_battery()', simulated: 100 }, - /** Sekunden seit dem Start bzw. seit `resetTimer()` */ - getTimer: { python: 'cyberpi.timer.get()', simulated: 0 }, -} - -/** Messwerte des Roboters nach Namen (`getDistance` → 38, `isButtonA` → true). */ -export type MBotSensorValues = Partial> - -/** Welche Sensoren kommen in diesem Text vor (z. B. im ganzen Diagramm)? */ -export function usedMBotSensors(text: string): string[] { - const found = new Set() - for (const m of text.matchAll(/\bmbot\s*\.\s*([A-Za-z_]\w*)\s*\(\s*\)/gu)) if (m[1] in MBOT_SENSORS) found.add(m[1]) - return [...found] -} - -/** `mbot.forward(10)` → Name und Argumenttexte; `undefined`, wenn es kein mBot-Befehl ist. */ -export function parseMBotCommand(text: string): { method: string; args: string[] } | undefined { - // `mbot.led(i).setColor("grün")` wird zu `ledColor(i, "grün")` - const led = /^mbot\s*\.\s*led\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)\s*\.\s*setColor\s*\(([\s\S]*)\)\s*;?$/u.exec(text.trim()) - if (led) return { method: 'ledColor', args: [led[1].trim(), led[2].trim()] } - const m = /^mbot\s*\.\s*([A-Za-z_]\w*)\s*\(([\s\S]*)\)\s*;?$/u.exec(text.trim()) - if (!m) return undefined - const inner = m[2].trim() - return { method: m[1], args: inner === '' ? [] : splitArgs(inner) } -} - -/** Argumente prüfen und leere Klammern mit den Standardwerten füllen; `undefined` bei falscher Anzahl. */ -export function mbotArguments(method: string, args: number[]): number[] | undefined { - const spec = MBOT_COMMANDS[method] - if (!spec) return undefined - const values = args.length === 0 && spec.defaults ? spec.defaults : args - if (values.length !== spec.args) return undefined - return !spec.valid || spec.valid(values) ? values : undefined -} - -export function makeMBotAction(method: string, args: number[], text = ''): MBotAction { - const spec = MBOT_COMMANDS[method] - return { - method, - args, - python: spec.python?.(args, text), - waitMs: method === 'pause' ? Math.max(0, args[0]) : undefined, - label: spec.label(args, text), - ...(spec.text ? { text } : {}), - } -} - -/** - * Alle Befehle als Liste wie `setLed 255 0 0`, `pause 500`, `showText "Hallo"` (zum Prüfen von LEDs, Tönen und Bildschirm). - * Anders als `driveSignature` wird nichts zusammengefasst. - */ -export function actionSignature(actions: { method: string; args: number[]; text?: string }[]): string[] { - return actions.map((a) => [a.method, ...a.args.map(n), ...(a.text !== undefined ? [JSON.stringify(a.text)] : [])].join(' ')) -} - -/** Erwartete Befehle einer Übung, z. B. `befehle: 3 x (setLed 255 0 0, pause 500, ledOff, pause 500)`; Texte in Anführungszeichen. */ -export function parseActionSpec(spec: string): string[] { - let i = 0 - const src = spec.trim() - const list = (): string[] => { - const items: string[] = [] - for (;;) { - while (src[i] === ' ' || src[i] === ',') i++ - if (i >= src.length || src[i] === ')') return items - const rep = /^(\d+)\s*[x×]\s*\(/u.exec(src.slice(i)) - if (rep) { - i += rep[0].length - const inner = list() - if (src[i] !== ')') throw new Error(`„)“ fehlt in „${spec}“`) - i++ - for (let k = 0; k < Number(rep[1]); k++) items.push(...inner) - continue - } - const step = /^([A-Za-z]+)((?:\s+-?\d+(?:\.\d+)?)*)(?:\s+("(?:[^"\\]|\\.)*"))?/u.exec(src.slice(i)) - if (!step) throw new Error(`Unbekannt in „${spec}“: ${src.slice(i)}`) - i += step[0].length - const spec_ = MBOT_COMMANDS[step[1]] - if (!spec_) throw new Error(`Unbekannter Befehl in „${spec}“: ${step[1]}`) - const nums = step[2].trim() === '' ? [] : step[2].trim().split(/\s+/).map(Number) - const args = mbotArguments(step[1], nums) - if (!args) throw new Error(`Falsche Anzahl Zahlen in „${spec}“: ${step[1]}`) - items.push(actionSignature([{ method: step[1], args, text: spec_.text ? JSON.parse(step[3] ?? '""') : undefined }])[0]) - } - } - const items = list() - if (i < src.length) throw new Error(`Zu viele „)“ in „${spec}“`) - return items -} - -/** Ersetzt `mbot.getDistance()` usw. durch Zahlen, damit der Rechner sie wie Literale liest. */ -export function substituteMBotSensors(expression: string, read: (name: string) => MBotSensorValue): string { - if (!expression.includes('mbot')) return expression - return expression.replace(/\bmbot\s*\.\s*([A-Za-z_]\w*)\s*\(\s*\)/gu, (all, name: string) => { - if (!(name in MBOT_SENSORS)) return all - const value = read(name) - return typeof value === 'string' ? JSON.stringify(name === 'getColor' ? mbotColorName(value) : value) : String(value) - }) -} - -/** Argumente an Kommas auf oberster Ebene trennen (nicht in Klammern oder Texten). */ -function splitArgs(text: string): string[] { - const parts: string[] = [] - let depth = 0 - let quote = false - let start = 0 - for (let i = 0; i < text.length; i++) { - const c = text[i] - if (quote) { - if (c === '\\') i++ - else if (c === '"') quote = false - } else if (c === '"') quote = true - else if (c === '(' || c === '[') depth++ - else if (c === ')' || c === ']') depth-- - else if (c === ',' && depth === 0) { - parts.push(text.slice(start, i).trim()) - start = i + 1 - } - } - parts.push(text.slice(start).trim()) - return parts -} - -/** - * Gefahrene Strecke als Liste wie `forward 50`, `turnRight 90`: aufeinanderfolgende Fahrten bzw. Drehungen - * zusammengefasst, LEDs und Töne zählen nicht. So ist `5 × forward()` dasselbe wie `forward(50)`. - */ -export function driveSignature(actions: { method: string; args: number[] }[]): string[] { - const out: { kind: 'drive' | 'turn'; value: number }[] = [] - for (const a of actions) { - const value = { forward: a.args[0], backward: -a.args[0], turnRight: a.args[0], turnLeft: -a.args[0] }[a.method] - if (value === undefined) continue - const kind = a.method === 'forward' || a.method === 'backward' ? 'drive' : 'turn' - const last = out[out.length - 1] - if (last && last.kind === kind) last.value += value - else out.push({ kind, value }) - } - return out.flatMap(({ kind, value }) => { - const v = kind === 'turn' ? ((((value + 180) % 360) + 360) % 360) - 180 : value - if (Math.abs(v) < 1e-6) return [] - const r = Math.round(Math.abs(v) * 100) / 100 - if (kind === 'drive') return [`${v > 0 ? 'forward' : 'backward'} ${r}`] - return [v === -180 ? 'turnRight 180' : `${v > 0 ? 'turnRight' : 'turnLeft'} ${r}`] - }) -} - -/** - * Erwartete Fahrt einer Übung, z. B. `fahrt: 4 x (forward 50, turnRight 90)`. Teile durch Kommas getrennt, - * `n x (…)` wiederholt; ohne Zahl gelten die Werte von `mbot.forward()` bzw. `turnRight()`. - */ -export function parseDriveSpec(spec: string): { method: string; args: number[] }[] { - let i = 0 - const src = spec.trim() - const list = (): { method: string; args: number[] }[] => { - const items: { method: string; args: number[] }[] = [] - for (;;) { - while (src[i] === ' ' || src[i] === ',') i++ - if (i >= src.length || src[i] === ')') return items - const rep = /^(\d+)\s*[x×]\s*\(/u.exec(src.slice(i)) - if (rep) { - i += rep[0].length - const inner = list() - if (src[i] !== ')') throw new Error(`„)“ fehlt in „${spec}“`) - i++ - for (let k = 0; k < Number(rep[1]); k++) items.push(...inner) - continue - } - const step = /^([A-Za-z]+)(?:\s+(-?\d+(?:\.\d+)?))?/u.exec(src.slice(i)) - if (!step) throw new Error(`Unbekannt in „${spec}“: ${src.slice(i)}`) - i += step[0].length - const args = mbotArguments(step[1], step[2] === undefined ? [] : [Number(step[2])]) - if (!args) throw new Error(`Unbekannter Befehl in „${spec}“: ${step[1]}`) - items.push({ method: step[1], args }) - } - } - const items = list() - if (i < src.length) throw new Error(`Zu viele „)“ in „${spec}“`) - return items -} - -/** Fahrt kurz zum Anzeigen: Wiederholungen als `4 x (forward 50, turnRight 90)`, auch verschachtelt. */ -export function compressDrive(drive: string[]): string { - const n = drive.length - for (let unit = 1; unit <= n / 2; unit++) { - if (n % unit !== 0) continue - const times = n / unit - if (drive.every((s, i) => s === drive[i % unit])) { - const inner = compressDrive(drive.slice(0, unit)) - return `${times} x ${unit === 1 ? inner : `(${inner})`}` - } - } - return drive.join(', ') -} - -/** - * Teile des mBot2 im 3D-Modell, z. B. für `fokus:` im Lektionsblock `::: mbot2` und `[Wort]{teil=cyberpi}` im Text. - * Die ersten acht gehören zum CyberPi; `cyberpi` allein ist sein Gehäuse. - */ -export const MBOT_PARTS = [ - 'cyberpi', - 'screen', - 'joystick', - 'buttons', - 'leds', - 'lightSensor', - 'microphone', - 'shield', - 'powerSwitch', - 'motors', - 'wheels', - 'ultrasonic', - 'lineSensor', - 'caster', - 'chassis', -] as const - -export type MBotPart = (typeof MBOT_PARTS)[number] - -export const isMBotPart = (name: string): name is MBotPart => (MBOT_PARTS as readonly string[]).includes(name) diff --git a/web/src/core/mbotLink.ts b/web/src/core/mbotLink.ts deleted file mode 100644 index 8350d941..00000000 --- a/web/src/core/mbotLink.ts +++ /dev/null @@ -1,379 +0,0 @@ -/** - * Verbindung zum echten mBot2 über USB (Web Serial, nur Chrome/Edge). Die Makeblock-Firmware des CyberPi - * führt Python-Zeilen aus, die in einen Rahmen verpackt sind: - * `F3, Kopfsumme, Länge (2 Byte), 28, Modus, Nummer (2 Byte), Textlänge (2 Byte), Text, Summe, F4`. - * Modus 4 führt nur aus (Fahrbefehle antworten erst, wenn sie fertig sind), Modus 1 liefert den Wert zurück. - * Die Antwort trägt dieselbe Nummer und enthält `{"ret":…}`. Getestet mit Holgers mBot2 (Oktober 2026). - */ - -import type { MBotSensorValue } from './mbot' - -const HEAD = 0xf3 -const FOOT = 0xf4 -const SCRIPT = 0x28 -export const RUN = 4 -export const ASK = 1 - -/** Rahmen um einen Inhalt legen. */ -export function frame(body: number[]): Uint8Array { - const len = body.length - const head = (HEAD + (len & 0xff) + (len >> 8)) & 0xff - const sum = body.reduce((a, b) => a + b, 0) & 0xff - return Uint8Array.from([HEAD, head, len & 0xff, len >> 8, ...body, sum, FOOT]) -} - -/** Python-Zeile als Rahmen mit Modus und Nummer. */ -export function pythonFrame(code: string, mode: number, idx: number): Uint8Array { - const text = Array.from(new TextEncoder().encode(code)) - return frame([SCRIPT, mode, idx & 0xff, (idx >> 8) & 0xff, text.length & 0xff, text.length >> 8, ...text]) -} - -export interface ScriptAnswer { - idx: number - /** Wert aus `{"ret":…}`: Zahl, Wahrheitswert, Text oder `null` (Python `None`). */ - value: MBotSensorValue | null -} - -/** - * Sucht vollständige Rahmen in `rx` und gibt die Antworten auf Python-Zeilen zurück; verbrauchte Bytes werden entfernt. - * Textausgaben des CyberPi außerhalb von Rahmen (z. B. nach dem Neustart) werden übersprungen. - */ -export function readAnswers(rx: number[]): ScriptAnswer[] { - return readFrames(rx).flatMap((body) => scriptAnswer(body) ?? []) -} - -/** Inhalt einer Antwort auf eine Python-Zeile, sonst `undefined`. */ -function scriptAnswer(body: number[]): ScriptAnswer | undefined { - if (body[0] !== SCRIPT || body.length < 6) return undefined - const text = new TextDecoder().decode(Uint8Array.from(body.slice(6))) - const m = /"ret"\s*:\s*([^}]*)\}/.exec(text) - return m ? { idx: body[2] | (body[3] << 8), value: pythonValue(m[1].trim()) } : undefined -} - -/** Alle vollständigen Rahmen aus `rx` (nur der Inhalt); verbrauchte Bytes werden entfernt. */ -export function readFrames(rx: number[]): number[][] { - const bodies: number[][] = [] - for (;;) { - const start = rx.indexOf(HEAD) - if (start < 0) { - rx.length = 0 - break - } - rx.splice(0, start) - if (rx.length < 4) break - const len = rx[2] | (rx[3] << 8) - // Kein Rahmen, nur ein zufälliges F3 im Text: weitersuchen - if (rx[1] !== ((HEAD + rx[2] + rx[3]) & 0xff) || len > 4096) { - rx.splice(0, 1) - continue - } - if (rx.length < len + 6) break - bodies.push(rx.slice(4, 4 + len)) - rx.splice(0, len + 6) - } - return bodies -} - -function pythonValue(text: string): MBotSensorValue | null { - if (text === 'True') return true - if (text === 'False') return false - // Text, z. B. ein Farbname: "red" oder 'red' - const quoted = /^["'](.*)["']$/.exec(text) - if (quoted) return quoted[1] - const n = Number(text) - return text !== '' && Number.isFinite(n) ? n : null -} - -/** - * Programm fest auf dem CyberPi speichern, wie „Hochladen“ in mBlock (Ablauf aus github.com/weare2plus3/cyberpi-uploader, MIT): - * Upload-Modus (`0D 00 00`), REPL aus, Dateikopf mit Größe, XOR-Prüfsumme und Pfad, dann Blöcke zu 80 Byte. - * Die Datei landet in einem Zwischenplatz, den die Firmware beim nächsten Neustart übernimmt. - */ -export const UPLOAD_PATH = '/flash/_xx_main.py' -const UPLOAD_MODE = [0x0d, 0x00, 0x00] -const FILE_SERVICE = 0x5e -const BLOCK = 80 -export const UPLOAD_PREPARE = ['try:\n import config\nexcept:\n pass', 'try:\n config.write_config("repl_enable", False)\nexcept:\n pass'] - -const le = (value: number, bytes: number) => Array.from({ length: bytes }, (_, i) => (value >>> (8 * i)) & 0xff) - -/** XOR der 32-Bit-Wörter (little endian, mit Nullen aufgefüllt), wie mBlock. */ -export function fileChecksum(data: Uint8Array): number[] { - let x = 0 - for (let i = 0; i < data.length; i += 4) x = (x ^ ((data[i] ?? 0) | ((data[i + 1] ?? 0) << 8) | ((data[i + 2] ?? 0) << 16) | ((data[i + 3] ?? 0) << 24))) >>> 0 - return le(x, 4) -} - -const fileFrame = (cmd: number, payload: number[]) => frame([0x01, 0x00, FILE_SERVICE, cmd, ...le(payload.length, 2), ...payload]) - -/** Python-Datei in Rahmen: Kopf, dann Blöcke zu 80 Byte und ein letzter Block mit 4 Byte (wie mBlock; mit Zeilenumbrüchen aufgefüllt). */ -export function uploadFrames(source: string, path = UPLOAD_PATH): Uint8Array[] { - const raw = new TextEncoder().encode(source) - const pad = (4 - (raw.length % BLOCK) + BLOCK) % BLOCK - const data = new Uint8Array(raw.length + pad).fill(0x0a) - data.set(raw) - const frames = [fileFrame(0x01, [0x00, ...le(data.length, 4), ...fileChecksum(data), ...Array.from(new TextEncoder().encode(path))])] - const end = data.length - 4 - for (let off = 0; off < end; off += BLOCK) frames.push(fileFrame(0x02, [...le(off, 4), ...data.subarray(off, Math.min(off + BLOCK, end))])) - frames.push(fileFrame(0x02, [...le(end, 4), ...data.subarray(end)])) - return frames -} - -const isModeEcho = (body: number[]) => body.length === 3 && body[0] === 0x0d -const isFileAck = (body: number[]) => body.length === 7 || body.some((b, i) => b === FILE_SERVICE && body[i + 1] === 0xf0) - -// Web Serial ist nicht in den TypeScript-Typen; nur das Nötigste. -interface SerialPortLike { - open(options: { baudRate: number }): Promise - close(): Promise - readable: ReadableStream | null - writable: WritableStream | null -} -interface SerialLike { - requestPort(): Promise -} - -const serial = (): SerialLike | undefined => (typeof navigator === 'undefined' ? undefined : (navigator as unknown as { serial?: SerialLike }).serial) - -/** Kann dieser Browser einen mBot2 über USB ansprechen? */ -export const canConnectMBot = () => serial() !== undefined - -/** Mehrere Python-Anweisungen als eine Zeile (der CyberPi nimmt nur eine). JSON-Strings sind auch gültige Python-Strings. */ -export const pyLines = (...lines: string[]) => `exec(${JSON.stringify(lines.join('; '))})` - -/** - * Bildschirm leeren und Text in der Mitte zeigen. Ohne Pinselfarbe: `display.set_brush` wirkt am CyberPi - * unzuverlässig (Texte kamen rot, lila, grün oder blau statt in der gewählten Farbe, auch mit Pausen). - * Die Schrift hat dann die Farbe, die der CyberPi gerade hat (einfarbig, nicht immer weiß; Holger: in Ordnung). - */ -export const showLabel = (text: string, size = 24) => pyLines('cyberpi.display.clear()', `cyberpi.display.show_label(${JSON.stringify(text)}, ${size}, 'center')`) - -/** - * Logo nach dem Verbinden (und nach „mBot2 testen“ → Text): „VisuBot“ groß, einfarbig. - * Grafik (screen.set_pixel, Sprite) zeigt über USB nichts, Farben gehen nicht zuverlässig. Am echten mBot2 getestet (Oktober 2026). - */ -export const HELLO = showLabel('VisuBot', 32) - -export class MBotLink { - private port?: SerialPortLike - private writer?: WritableStreamDefaultWriter - private reader?: ReadableStreamDefaultReader - private loop?: Promise - private rx: number[] = [] - private idx = 1 - private frameWaiters: { match: (body: number[]) => boolean; resolve: () => void }[] = [] - private waiting = new Map void; reject: (e: Error) => void }>() - private queue: Promise = Promise.resolve() - private listeners = new Set<() => void>() - error?: string - /** Port ist offen, der CyberPi startet noch neu. */ - connecting = false - - /** `fn` bei Verbinden, Trennen und Fehlern rufen; gibt eine Abmeldung zurück. */ - listen(fn: () => void): () => void { - this.listeners.add(fn) - return () => this.listeners.delete(fn) - } - - private changed() { - for (const fn of this.listeners) fn() - } - - get connected() { - return this.writer !== undefined && !this.connecting - } - - /** Fragt nach dem USB-Anschluss, öffnet ihn und wartet den Neustart des CyberPi ab. */ - async connect() { - const s = serial() - if (!s) throw new Error('Web Serial') - const port = await s.requestPort() - await port.open({ baudRate: 115200 }) - this.port = port - this.writer = port.writable!.getWriter() - this.error = undefined - this.connecting = true - this.changed() - this.loop = this.readLoop() - // Seite schließen oder neu laden: Anschluss freigeben - window.addEventListener('pagehide', () => void this.disconnect(), { once: true }) - // Das Öffnen setzt den CyberPi zurück - await new Promise((r) => setTimeout(r, 3000)) - if (!this.writer) return - // Textausgaben vom Neustart verwerfen - this.rx.length = 0 - this.connecting = false - this.changed() - await this.run(HELLO) - } - - /** - * Sauber trennen wie beim Auswerfen: Motoren anhalten, LEDs aus, Abschied auf dem Bildschirm, - * dann Leser und Schreiber freigeben und den Anschluss schließen. So lässt er sich ohne Neustart wieder verbinden. - */ - async disconnect(error?: string) { - if (!this.port) { - this.error = error - return this.changed() - } - if (!error && this.writer && !this.connecting) { - for (const code of ["mbot2.EM_stop('ALL')", 'cyberpi.led.off()', showLabel('Bis bald!')]) - await this.send(code, RUN, 2000).catch(() => undefined) - } - const port = this.port - const writer = this.writer - const reader = this.reader - const loop = this.loop - this.port = this.writer = this.reader = this.loop = undefined - this.error = error - this.connecting = false - for (const w of this.waiting.values()) w.reject(new Error(error ?? 'getrennt')) - this.waiting.clear() - try { - await reader?.cancel() - await loop - await writer?.close() - } catch { - /* Leitung schon weg */ - } - try { - writer?.releaseLock() - await port.close() - } catch (e) { - console.warn('[mBot2] Schließen', (e as Error).message) - } - this.changed() - } - - /** Python-Zeile ausführen; kehrt zurück, wenn der Roboter fertig ist (Fahren dauert). */ - run(code: string): Promise { - return this.send(code, RUN, 30000).then(() => undefined) - } - - /** Wert eines Python-Ausdrucks, z. B. eines Sensors. */ - ask(code: string): Promise { - return this.send(code, ASK, 3000) - } - - /** - * Programm auf den CyberPi übertragen (verbindet vorher, wenn nötig). Danach startet der CyberPi neu und - * das Programm bleibt gespeichert, auch ohne Kabel. `progress` bekommt erledigte und alle Schritte. - */ - async upload(source: string, progress: (done: number, total: number) => void = () => {}) { - if (!this.port) await this.connect() - if (!this.writer) throw new Error(this.error ?? 'nicht verbunden') - const files = uploadFrames(source) - const total = 6 + files.length + 3 - let done = 0 - const step = () => progress(++done, total) - const prepare = async () => { - await this.sendFrame(frame(UPLOAD_MODE), isModeEcho) - step() - for (const code of UPLOAD_PREPARE) { - await this.send(code, RUN, 3000) - step() - } - } - await prepare() - await prepare() - for (const f of files) { - await this.sendFrame(f, isFileAck) - step() - } - done = total - 3 - await prepare() - await this.restart() - } - - /** Rahmen schicken und auf die passende Antwort warten (bis zu drei Versuche). */ - private sendFrame(data: Uint8Array, match: (body: number[]) => boolean): Promise { - const next = this.queue.then(async () => { - for (let attempt = 0; attempt < 3; attempt++) { - if (!this.writer) throw new Error('nicht verbunden') - let resolve = () => {} - const answered = new Promise((r) => (resolve = r)) - const waiter = { match, resolve } - this.frameWaiters.push(waiter) - await this.writer.write(data) - const ok = await Promise.race([answered.then(() => true), new Promise((r) => setTimeout(() => r(false), 3000))]) - this.frameWaiters = this.frameWaiters.filter((w) => w !== waiter) - if (ok) return - } - throw new Error('keine Antwort') - }) - this.queue = next.catch(() => undefined) - return next - } - - /** Anschluss schließen und wieder öffnen: das setzt den CyberPi zurück, er übernimmt das neue Programm. */ - private async restart() { - const port = this.port! - await this.disconnect('restart') - this.error = undefined - await new Promise((r) => setTimeout(r, 300)) - await port.open({ baudRate: 115200 }) - await new Promise((r) => setTimeout(r, 500)) - await port.close() - this.changed() - } - - private send(code: string, mode: number, timeoutMs: number): Promise { - // Immer nur eine Zeile unterwegs: der CyberPi arbeitet sie nacheinander ab. - const next = this.queue.then( - () => - new Promise((resolve, reject) => { - if (!this.writer) return reject(new Error('nicht verbunden')) - const idx = this.idx - this.idx = (this.idx % 0xffff) + 1 - const timer = setTimeout(() => { - this.waiting.delete(idx) - reject(new Error('keine Antwort')) - }, timeoutMs) - this.waiting.set(idx, { - resolve: (v) => (clearTimeout(timer), resolve(v)), - reject: (e) => (clearTimeout(timer), reject(e)), - }) - this.writer.write(pythonFrame(code, mode, idx)).catch((e: Error) => { - this.waiting.delete(idx) - reject(e) - }) - }), - ) - this.queue = next.catch(() => undefined) - return next - } - - private async readLoop() { - while (this.port?.readable) { - const reader = this.port.readable.getReader() - this.reader = reader - try { - for (;;) { - const { value, done } = await reader.read() - if (done) break - this.rx.push(...value) - for (const body of readFrames(this.rx)) { - const f = this.frameWaiters.findIndex((w) => w.match(body)) - if (f >= 0) this.frameWaiters.splice(f, 1)[0].resolve() - const a = scriptAnswer(body) - if (!a) continue - const w = this.waiting.get(a.idx) - this.waiting.delete(a.idx) - w?.resolve(a.value) - } - } - } catch (e) { - // Störungen auf der Leitung (z. B. beim Neustart des CyberPi) sind harmlos: mit neuem Leser weiter - const harmless = ['BufferOverrunError', 'FramingError', 'BreakError', 'ParityError'].includes((e as Error).name) - console.warn('[mBot2] Lesefehler', (e as Error).name, (e as Error).message) - if (!harmless && this.port) void this.disconnect((e as Error).message) - } finally { - reader.releaseLock() - } - if (!this.writer) break - } - } -} - -/** Eine Verbindung für die ganze Seite (bleibt beim Neuaufbau der Oberfläche erhalten). */ -export const mbotLink = new MBotLink() diff --git a/web/src/core/mbotProgram.ts b/web/src/core/mbotProgram.ts deleted file mode 100644 index 6fc4ab23..00000000 --- a/web/src/core/mbotProgram.ts +++ /dev/null @@ -1,274 +0,0 @@ -import { generateCode } from './codeGenerator' -import { MBOT_LED_COLORS, MBOT_SENSORS, MBOT_WHEEL_DIAMETER_CM } from './mbot' -import type { VSDocument } from './model' -import { parseSubprogramHeader } from './simulation' - -/** - * VisuBot: das fertige Programm als MicroPython-Datei für den CyberPi, damit der mBot2 es ohne Kabel fährt. - * Der Code kommt aus dem Python-Generator; `mbot.forward()` usw. bleiben stehen, weil die Datei vorn eine - * kleine Klasse `MBot` mit denselben Namen wie in Java mitbringt. Nach dem Einschalten zeigt der Bildschirm - * „A = Start“, das Programm läuft nach einem Druck auf Taste A. - */ - -/** Firmware-Farbnamen (`get_color_sta`) → Name in der Sprache der Lektionen. */ -const COLOR_WORDS: Record = { red: 'rot', green: 'grün', blue: 'blau', yellow: 'gelb', cyan: 'türkis', purple: 'lila', white: 'weiß', black: 'schwarz' } - -const sensorMethods = () => - Object.entries(MBOT_SENSORS) - .map(([name, spec]) => { - if (name === 'getColor') return ` def ${name}(self):\n farbe = ${spec.python}\n return FARBNAMEN.get(farbe, farbe)\n` - const value = typeof spec.simulated === 'boolean' ? `bool(${spec.python})` : spec.python - return ` def ${name}(self):\n return ${value}\n` - }) - .join('\n') - -function prelude(language: string): string { - const colors = Object.entries(MBOT_LED_COLORS) - .map(([name, rgb]) => ` ${JSON.stringify(name)}: (${rgb.join(', ')}),\n`) - .join('') - const words = Object.entries(COLOR_WORDS) - .map(([en, de]) => ` ${JSON.stringify(en)}: ${JSON.stringify(language === 'de' ? de : en)},\n`) - .join('') - return `import event, time, math, cyberpi, mbot2, mbuild - -FARBEN = { -${colors}} - -FARBNAMEN = { -${words}} - - -class Led: - def __init__(self, nr): - self.nr = nr - - def setColor(self, farbe): - r, g, b = FARBEN.get(farbe, (0, 0, 0)) - cyberpi.led.on(r, g, b, id=self.nr + 1) - - -def strecke(wert, einheit): - # Überladung forward(2, "Umdrehungen"): eine Radumdrehung ist pi * ${MBOT_WHEEL_DIAMETER_CM} cm - if einheit.lower() in ("umdrehung", "umdrehungen", "rotation", "rotations", "rotação", "rotações"): - return wert * math.pi * ${MBOT_WHEEL_DIAMETER_CM} - return wert - - -class MBot: - def forward(self, wert=10, einheit="cm"): - mbot2.straight(strecke(wert, einheit)) - - def backward(self, wert=10, einheit="cm"): - mbot2.straight(-strecke(wert, einheit)) - - def turnLeft(self, grad=90): - mbot2.turn(-grad) - - def turnRight(self, grad=90): - mbot2.turn(grad) - - def stop(self): - mbot2.EM_stop("ALL") - - def drive(self, links, rechts): - mbot2.drive_power(links, -rechts) - - def setLed(self, r, g, b): - cyberpi.led.on(r, g, b) - - def ledOff(self): - cyberpi.led.off() - - def led(self, nr): - return Led(nr) - - def playTone(self, hz, ms): - cyberpi.audio.play_tone(hz, ms / 1000) - - def pause(self, ms): - time.sleep(ms / 1000) - - def resetTimer(self): - cyberpi.timer.reset() - - def emotion(self, name): - getattr(cyberpi.emotion, name)() - - def showText(self, text): - cyberpi.display.clear() - cyberpi.display.show_label(str(text), 24, "center") - -${sensorMethods()} - -mbot = MBot() -` -} - -const startPart = (label: string) => ` - -laeuft = False - - -@event.start -def beim_einschalten(): - mbot.showText(${JSON.stringify(label)}) - - -@event.is_press("a") -def taste_a(): - global laeuft - if laeuft: - return - laeuft = True - cyberpi.display.clear() - try: - programm() - finally: - mbot.stop() - laeuft = False -` - -export type MBotProgram = { source: string } | { error: 'input' | 'files' | 'empty' } - -/** - * `doc`: Hauptprogramm, `subprograms`: Unterprogramme aus anderen Reitern; `language`: Sprache der Farbnamen - * (wie `mbot.getColor()` in der Simulation). Eingaben und Dateien gehen am Roboter nicht. - */ -export function mbotProgram(doc: VSDocument, subprograms: VSDocument[], language: string, startLabel = 'A = Start'): MBotProgram { - const options = { pythonWithoutMatch: true } - const main = javaConcatenation(generateCode(doc, 'python', options)) - const defs = subprograms.filter((s) => s !== doc && parseSubprogramHeader(s.caption)).map((s) => javaConcatenation(generateCode(s, 'python', options))) - const all = [main, ...defs].join('\n') - if (/\binput\(/u.test(all)) return { error: 'input' } - if (/\bopen\(|\bos\.path\b/u.test(all)) return { error: 'files' } - const imports = new Set() - const strip = (code: string) => - code - .split('\n') - .filter((line) => { - if (/^import \w+$/u.test(line)) imports.add(line) - return !/^import \w+$/u.test(line) - }) - .join('\n') - .trim() - const body = strip(main) - if (body === '' || /^#[^\n]*$/u.test(body)) return { error: 'empty' } - const indented = body - .split('\n') - .map((line) => (line.trim() === '' ? '' : ' ' + line)) - .join('\n') - const source = - `# VisuStruct VisuBot\n${prelude(language)}${[...imports].join('\n')}\n\n` + - defs.map((d) => strip(d) + '\n\n\n').join('') + - `def programm():\n${indented}\n` + - startPart(startLabel) - return { source } -} - -/** - * Java verkettet Text mit `+` auch mit Zahlen (`"Akku: " + mbot.getBattery()`), Python nicht. In jeder Kette - * mit einem Text in Anführungszeichen wird alles andere zu `str(…)`; Zahlen vor dem ersten Text werden wie in - * Java erst addiert (`a + b + "x"` → `str(a + b) + "x"`). - */ -export function javaConcatenation(code: string): string { - return code - .split('\n') - .map((line) => { - const m = /^(\s*)(.*)$/u.exec(line)! - return m[2].startsWith('#') ? line : m[1] + rewrite(m[2]) - }) - .join('\n') -} - -const separator = /^(==|!=|<=|>=|\+=|-=|\*=|\/=|=|<|>|,|:)/u -const keyword = /^(and|or|not|in|if|elif|while|return|for|else)\b/u - -/** Text an Trennern (Vergleich, Zuweisung, Komma, Schlüsselwort) auf oberster Ebene zerlegen, jedes Stück umschreiben. */ -function rewrite(text: string): string { - let out = '' - let piece = '' - let depth = 0 - for (let i = 0; i < text.length; i++) { - const c = text[i] - if (c === '"' || c === "'") { - const end = stringEnd(text, i) - piece += text.slice(i, end) - i = end - 1 - continue - } - if (c === '(' || c === '[' || c === '{') depth++ - if (c === ')' || c === ']' || c === '}') depth-- - if (depth === 0) { - const sep = separator.exec(text.slice(i)) ?? (/\w/u.test(text[i - 1] ?? '') ? null : keyword.exec(text.slice(i))) - if (sep) { - out += rewritePiece(piece) + sep[0] - piece = '' - i += sep[0].length - 1 - continue - } - } - piece += c - } - return out + rewritePiece(piece) -} - -function stringEnd(text: string, start: number): number { - const quote = text[start] - for (let i = start + 1; i < text.length; i++) { - if (text[i] === '\\') i++ - else if (text[i] === quote) return i + 1 - } - return text.length -} - -/** Ein Stück ohne Trenner: erst die Klammern innen, dann die `+`-Kette. */ -function rewritePiece(piece: string): string { - let inner = '' - const operands: string[] = [] - let depth = 0 - let open = 0 - for (let i = 0; i < piece.length; i++) { - const c = piece[i] - if (c === '"' || c === "'") { - const end = stringEnd(piece, i) - if (depth === 0) inner += piece.slice(i, end) - i = end - 1 - continue - } - if (c === '(' || c === '[' || c === '{') { - if (depth === 0) open = i - depth++ - if (depth === 1) { - inner += c - continue - } - } - if (c === ')' || c === ']' || c === '}') { - depth-- - if (depth === 0) { - inner += rewrite(piece.slice(open + 1, i)) + c - continue - } - } - if (depth > 0) continue - // binäres Plus: davor steht ein Wert, kein Operator - if (c === '+' && inner.trim() !== '' && !/[-+*/%(,=<>]\s*$/u.test(inner)) { - operands.push(inner) - inner = '' - continue - } - inner += c - } - if (depth > 0) inner += piece.slice(open) - operands.push(inner) - if (operands.length < 2) return operands[0] - const lead = /^\s*/u.exec(operands[0])![0] - const trail = /\s*$/u.exec(operands[operands.length - 1])![0] - const parts = operands.map((o) => o.trim()) - const isText = (p: string) => p.startsWith('"') || p.startsWith("'") || p.startsWith('str(') - const first = parts.findIndex(isText) - if (first < 0) return piece - const before = first > 0 ? [`str(${parts.slice(0, first).join(' + ')})`] : [] - const after = parts.slice(first).map((p) => (isText(p) ? p : `str(${p})`)) - return lead + [...before, ...after].join(' + ') + trail -} diff --git a/web/src/core/memberEdit.ts b/web/src/core/memberEdit.ts deleted file mode 100644 index 0b899213..00000000 --- a/web/src/core/memberEdit.ts +++ /dev/null @@ -1,189 +0,0 @@ -/** - * VisuObject: Klassen, Attribute und Methoden aus dem Objektinspektor ändern. - * Geschrieben wird nur die betroffene Deklaration; Methodenrümpfe, Kommentare und Startwerte bleiben. - * Dazu die Erzeuger aus dem Eclipse-Menü „Source“: Konstruktor aus Attributen, Getter/Setter, toString(). - */ - -import type { UmlClass, UmlMember, UmlParam, Visibility } from './classModel' - -const splice = (source: string, from: number, to: number, text: string) => source.slice(0, from) + text + source.slice(to) -const javaVisibility: Record = { '+': 'public ', '-': 'private ', '#': 'protected ', '~': '' } -const lineStart = (source: string, pos: number) => source.lastIndexOf('\n', pos - 1) + 1 - -/** Einrückung der Mitglieder (wie die erste Deklaration), sonst vier Leerzeichen. */ -function indentOf(source: string, cls: UmlClass): string { - const first = cls.members[0] - if (first) { - const start = lineStart(source, first.from) - const ws = /^[ \t]*/.exec(source.slice(start))![0] - if (start + ws.length === first.from) return ws - } - return ' ' -} - -/** Annotationen vor einer Deklaration (`@Override` …) bleiben beim Umschreiben stehen. */ -function annotations(source: string, from: number, to: number): string { - return /^(\s*@[\w.]+(\([^)]*\))?\s*)*/.exec(source.slice(from, to))![0] -} - -// MARK: - Attribute - -export interface FieldInfo { - visibility: Visibility - name: string - type: string - isStatic: boolean - isFinal: boolean - /** Startwert ohne `=`, leer = keiner. */ - init: string -} - -export function fieldInfo(source: string, m: UmlMember): FieldInfo { - const head = source.slice(m.headerFrom, m.nameFrom) - const tail = source.slice(m.nameTo, m.to) - const init = m.shared ? '' : (/^\s*=\s*([\s\S]*?)\s*;?\s*$/.exec(tail)?.[1] ?? '') - return { visibility: m.visibility, name: m.name, type: m.type, isStatic: m.isStatic, isFinal: /\bfinal\b/.test(head), init } -} - -export function fieldDeclaration(f: FieldInfo): string { - return `${javaVisibility[f.visibility]}${f.isStatic ? 'static ' : ''}${f.isFinal ? 'final ' : ''}${f.type} ${f.name}${f.init.trim() ? ` = ${f.init.trim()}` : ''};` -} - -/** Attribut neu schreiben. Bei `int a, b;` geht nur der Name. */ -export function setField(source: string, m: UmlMember, change: Partial): string { - if (m.shared) return change.name && change.name !== m.name ? splice(source, m.nameFrom, m.nameTo, change.name) : source - const next = { ...fieldInfo(source, m), ...change } - return splice(source, m.from, m.to, annotations(source, m.from, m.to) + fieldDeclaration(next)) -} - -// MARK: - Methoden - -export interface MethodInfo { - visibility: Visibility - name: string - /** Ergebnistyp, leer beim Konstruktor. */ - type: string - params: UmlParam[] - isStatic: boolean - isAbstract: boolean -} - -export function methodInfo(m: UmlMember): MethodInfo { - return { visibility: m.visibility, name: m.name, type: m.type, params: m.params, isStatic: m.isStatic, isAbstract: m.isAbstract } -} - -/** `name: Typ, n: int` oder Java-Stil `Typ name, int n` → Parameter. */ -export function parseParams(text: string): UmlParam[] | undefined { - const out: UmlParam[] = [] - for (const part of text.split(',').map((p) => p.trim()).filter(Boolean)) { - const uml = /^([\p{L}_$][\p{L}\p{N}_$]*)\s*:\s*(.+)$/u.exec(part) - const java = /^(.+?)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(part) - if (uml) out.push({ name: uml[1], type: uml[2].replace(/\s+/g, '') }) - else if (java) out.push({ name: java[2], type: java[1].replace(/\s+/g, '') }) - else return undefined - } - return out -} - -export const paramsText = (params: UmlParam[]) => params.map((p) => `${p.name}: ${p.type}`).join(', ') - -/** - * Kopf einer Methode neu schreiben; der Rumpf bleibt. `abstract` an: Rumpf wird `;`, - * `abstract` aus: aus `;` wird ein leerer Rumpf. In Interfaces ohne Sichtbarkeit und ohne `abstract`. - */ -export function setMethod(source: string, cls: UmlClass, m: UmlMember, change: Partial): string { - const next = { ...methodInfo(m), ...change } - const inInterface = cls.kind === 'interface' - const isConstructor = m.kind === 'constructor' - const vis = inInterface ? '' : javaVisibility[next.visibility] - const mods = `${vis}${next.isStatic && !isConstructor ? 'static ' : ''}${next.isAbstract && !inInterface && !isConstructor ? 'abstract ' : ''}` - const params = next.params.map((p) => `${p.type} ${p.name}`).join(', ') - const head = isConstructor ? `${mods}${m.name}(${params})` : `${mods}${next.type || 'void'} ${next.name}(${params})` - const keep = annotations(source, m.headerFrom, m.headerTo) - let out = splice(source, m.headerFrom, m.headerTo, keep + head) - const shift = keep.length + head.length - (m.headerTo - m.headerFrom) - const restFrom = m.headerTo + shift - const restTo = m.to + shift - const rest = out.slice(restFrom, restTo) - const wantsBody = !(next.isAbstract || (inInterface && !next.isStatic)) - if (!wantsBody && /^\s*(throws[^{;]*)?\{/.test(rest)) out = splice(out, restFrom, restTo, ';') - else if (wantsBody && /^\s*;/.test(rest)) { - const indent = indentOf(source, cls) - const ret = next.type && next.type !== 'void' && !isConstructor ? `${indent} return ${next.type === 'boolean' ? 'false' : /^(int|long|short|byte|double|float|char)$/.test(next.type) ? '0' : 'null'};\n` : '' - out = splice(out, restFrom, restTo, ` {\n${ret}${indent}}`) - } - return out -} - -// MARK: - Klasse - -/** Javadoc direkt über der Klasse (ohne `/** … *\/`), leer = keiner. */ -export function classDoc(source: string, cls: UmlClass): string { - const before = source.slice(0, cls.headerFrom) - const m = /\/\*\*([\s\S]*?)\*\/\s*$/.exec(before) - if (!m) return '' - return m[1] - .split('\n') - .map((l) => l.replace(/^\s*\*\s?/, '')) - .join('\n') - .trim() -} - -export function setClassDoc(source: string, cls: UmlClass, text: string): string { - const before = source.slice(0, cls.headerFrom) - const m = /\/\*\*[\s\S]*?\*\/\s*$/.exec(before) - const indent = /[ \t]*$/.exec(before)![0] - const doc = text.trim() ? `/**\n${text.trim().split('\n').map((l) => ` * ${l}`.trimEnd()).join('\n')}\n */\n${indent}` : '' - if (m) return splice(source, m.index, cls.headerFrom, doc) - return splice(source, cls.headerFrom, cls.headerFrom, doc) -} - -// MARK: - Source-Menü wie in Eclipse - -/** Vor der schließenden Klammer der Klasse einfügen, mit Leerzeile davor. */ -function insertAtEnd(source: string, cls: UmlClass, block: string): string { - const indent = indentOf(source, cls) - const lines = block - .split('\n') - .map((l) => (l ? indent + l : l)) - .join('\n') - const at = lineStart(source, cls.bodyEnd) - const onOwnLine = /^\s*$/.test(source.slice(at, cls.bodyEnd)) - const pos = onOwnLine ? at : cls.bodyEnd - const prefix = onOwnLine ? '' : '\n' - const blank = cls.members.length ? '\n' : '' - return splice(source, pos, pos, `${prefix}${blank}${lines}\n`) -} - -const instanceFields = (cls: UmlClass) => cls.members.filter((m) => m.kind === 'field' && !m.isStatic) -const capital = (s: string) => s.charAt(0).toUpperCase() + s.slice(1) - -/** Konstruktor mit allen Attributen (ohne static), wie „Generate Constructor using Fields“. */ -export function addFieldConstructor(source: string, cls: UmlClass): string { - const fields = instanceFields(cls) - const params = fields.map((f) => `${f.type} ${f.name}`).join(', ') - const body = fields.map((f) => ` this.${f.name} = ${f.name};`).join('\n') - return insertAtEnd(source, cls, `public ${cls.name}(${params}) {\n${body}${body ? '\n' : ''}}`) -} - -/** Getter und Setter für alle Attribute ohne; `final`-Attribute bekommen keinen Setter. */ -export function addGettersSetters(source: string, cls: UmlClass): string { - const methods = new Set(cls.members.filter((m) => m.kind === 'method').map((m) => m.name)) - const blocks: string[] = [] - for (const f of instanceFields(cls)) { - const isFinal = /\bfinal\b/.test(source.slice(f.headerFrom, f.nameFrom)) - const getter = `${f.type === 'boolean' ? 'is' : 'get'}${capital(f.name)}` - const setter = `set${capital(f.name)}` - if (!methods.has(getter)) blocks.push(`public ${f.type} ${getter}() {\n return ${f.name};\n}`) - if (!isFinal && !methods.has(setter)) blocks.push(`public void ${setter}(${f.type} ${f.name}) {\n this.${f.name} = ${f.name};\n}`) - } - return blocks.length ? insertAtEnd(source, cls, blocks.join('\n\n')) : source -} - -/** `toString()` wie „Generate toString()“ in Eclipse: `Hund [name=…, rasse=…]`. */ -export function addToString(source: string, cls: UmlClass): string { - if (cls.members.some((m) => m.kind === 'method' && m.name === 'toString' && m.params.length === 0)) return source - const parts = instanceFields(cls).map((f, i) => `"${i ? ', ' : ''}${f.name}=" + ${f.name}`) - const expr = parts.length ? `"${cls.name} [" + ${parts.join(' + ')} + "]"` : `"${cls.name} []"` - return insertAtEnd(source, cls, `@Override\npublic String toString() {\n return ${expr};\n}`) -} diff --git a/web/src/core/miniJava.ts b/web/src/core/miniJava.ts deleted file mode 100644 index 83e23e49..00000000 --- a/web/src/core/miniJava.ts +++ /dev/null @@ -1,1902 +0,0 @@ -/** - * VisuObject-Debugger: führt das Java aus `miniJavaParser` selbst aus, Anweisung für Anweisung. - * Jede Anweisung meldet sich vorher mit einem `step`-Ereignis (Generator), so kann der Debugger - * anhalten, schrittweise weitergehen und dabei Variablen und Objekte zeigen. Echtes Java läuft - * weiter über ▶ Start (CheerpJ); hier zählt, dass Schülerprogramme der FOS 12 gleich ablaufen. - */ - -import { JavaSyntaxError, misspelledType, missingImport, parseJava, type ClassDecl, type Expr, type MethodDecl, type Pos, type Stmt, type TypeRef } from './miniJavaParser' - -// MARK: - Werte - -export class JDouble { - constructor(readonly v: number) {} -} -export class JChar { - constructor(readonly code: number) {} -} -export class JObject { - fields = new Map() - /** Nachricht und Aufrufstapel bei Exceptions, Name und Nummer bei enum-Konstanten. */ - native: { message?: Val; trace?: string[]; enumName?: string; ordinal?: number } = {} - constructor( - readonly cls: RtClass, - readonly id: number, - ) {} -} -export class JArray { - constructor( - readonly elem: TypeRef, - public items: Val[], - readonly id: number, - ) {} -} -export class JList { - items: Val[] = [] - constructor(readonly id: number) {} -} -export class JMap { - entries = new Map() - constructor(readonly id: number) {} -} -export class JNative { - buf = '' - constructor( - readonly kind: 'out' | 'err' | 'in' | 'Scanner' | 'Random' | 'StringBuilder', - readonly id: number, - ) {} -} -export class ClassRef { - constructor(readonly name: string) {} -} - -export type Val = number | boolean | string | null | undefined | JDouble | JChar | JObject | JArray | JList | JMap | JNative | ClassRef - -export interface RtClass { - name: string - decl?: ClassDecl - sup?: RtClass - interfaces: string[] - statics: Map - staticTypes: Map - builtin?: boolean - /** Voller Name für die Ausgabe, z. B. `java.lang.ArithmeticException`. */ - javaName?: string -} - -interface Variable { - v: Val - type: TypeRef -} - -interface Frame { - cls: RtClass - method?: MethodDecl - self?: JObject - scopes: Map[] - pos: Pos - /** Titel im Aufrufstapel, z. B. `Spieler.punkte()` oder `new Spieler()`. */ - title: string -} - -type Done = undefined | { k: 'break' } | { k: 'continue' } | { k: 'return'; v: Val } -const BREAK = { k: 'break' } as const -const CONTINUE = { k: 'continue' } as const - -export type Ev = { k: 'step'; pos: Pos; depth: number } | { k: 'input' } | { k: 'sleep'; ms: number } -type G = Generator - -/** Fehler, die echtes Java schon beim Übersetzen meldet oder die der Debugger nicht kann. */ -export class MiniJavaError extends Error { - constructor( - readonly code: 'syntax' | 'symbol' | 'unsupported' | 'nomain' | 'type' | 'import' | 'typename', - readonly detail: string, - readonly pos?: Pos, - ) { - super(`${code}: ${detail}`) - } -} - -/** Eine geworfene Java-Exception. */ -export class JavaThrow extends Error { - constructor(readonly obj: JObject) { - super(obj.cls.name) - } -} - -const T = (name: string, dims = 0): TypeRef => ({ name, dims, args: [] }) -const intTypes = new Set(['int', 'long', 'short', 'byte', 'Integer', 'Long', 'Short', 'Byte']) -const doubleTypes = new Set(['double', 'float', 'Double', 'Float']) -const listTypes = new Set(['ArrayList', 'List', 'LinkedList', 'Collection', 'Iterable']) -const mapTypes = new Set(['HashMap', 'Map', 'TreeMap', 'LinkedHashMap']) - -const builtinExceptions: [string, string, string][] = [ - ['Throwable', 'Object', 'java.lang'], - ['Exception', 'Throwable', 'java.lang'], - ['Error', 'Throwable', 'java.lang'], - ['StackOverflowError', 'Error', 'java.lang'], - ['RuntimeException', 'Exception', 'java.lang'], - ['IOException', 'Exception', 'java.io'], - ['InterruptedException', 'Exception', 'java.lang'], - ['ArithmeticException', 'RuntimeException', 'java.lang'], - ['NullPointerException', 'RuntimeException', 'java.lang'], - ['ClassCastException', 'RuntimeException', 'java.lang'], - ['IllegalArgumentException', 'RuntimeException', 'java.lang'], - ['NumberFormatException', 'IllegalArgumentException', 'java.lang'], - ['IllegalStateException', 'RuntimeException', 'java.lang'], - ['UnsupportedOperationException', 'RuntimeException', 'java.lang'], - ['IndexOutOfBoundsException', 'RuntimeException', 'java.lang'], - ['ArrayIndexOutOfBoundsException', 'IndexOutOfBoundsException', 'java.lang'], - ['StringIndexOutOfBoundsException', 'IndexOutOfBoundsException', 'java.lang'], - ['NegativeArraySizeException', 'RuntimeException', 'java.lang'], - ['NoSuchElementException', 'RuntimeException', 'java.util'], - ['InputMismatchException', 'NoSuchElementException', 'java.util'], - ['ConcurrentModificationException', 'RuntimeException', 'java.util'], -] -const staticBuiltins = new Set(['Math', 'System', 'Integer', 'Double', 'String', 'Character', 'Boolean', 'Long', 'Float', 'Arrays', 'Collections', 'Thread', 'Objects', 'List']) - -/** Kommazahl wie Java `Double.toString`: `5.0`, `0.1`, `1.0E7`. */ -export function formatDouble(v: number): string { - if (Number.isNaN(v)) return 'NaN' - if (!Number.isFinite(v)) return v > 0 ? 'Infinity' : '-Infinity' - if (v === 0) return Object.is(v, -0) ? '-0.0' : '0.0' - const a = Math.abs(v) - if (a >= 1e-3 && a < 1e7) { - const s = String(v) - return s.includes('.') || s.includes('e') ? s : `${s}.0` - } - const [m, ex] = v.toExponential().split('e') - return `${m.includes('.') ? m : `${m}.0`}E${Number(ex)}` -} - -const javaHash = (id: number) => ((0x1b6d3586 + id * 0x9e3779b) >>> 0).toString(16) - -function stringHash(s: string) { - let h = 0 - for (let i = 0; i < s.length; i++) h = (Math.imul(31, h) + s.charCodeAt(i)) | 0 - return h -} - -const num = (v: Val): number => (typeof v === 'number' ? v : v instanceof JDouble ? v.v : v instanceof JChar ? v.code : NaN) -const isNum = (v: Val) => typeof v === 'number' || v instanceof JDouble || v instanceof JChar -const inInt = (x: number) => x >= -2147483648 && x <= 2147483647 - -/** Wert passend zum Typ der Variablen (int → double, char ↔ int, Cast). */ -export function castTo(v: Val, type: TypeRef | undefined): Val { - if (!type || type.dims || v === null || v === undefined) return v - const n = type.name - if (doubleTypes.has(n)) return isNum(v) ? new JDouble(num(v)) : v - if (intTypes.has(n)) { - if (v instanceof JChar) return v.code - if (v instanceof JDouble) { - if (Number.isNaN(v.v)) return 0 - const t = Math.trunc(v.v) - return n === 'int' || n === 'Integer' ? Math.max(-2147483648, Math.min(2147483647, t)) : t - } - if (typeof v === 'number' && (n === 'byte' || n === 'Byte')) return (v << 24) >> 24 - if (typeof v === 'number' && (n === 'short' || n === 'Short')) return (v << 16) >> 16 - return v - } - if (n === 'char' || n === 'Character') return typeof v === 'number' || v instanceof JDouble ? new JChar(Math.trunc(num(v)) & 0xffff) : v - return v -} - -export function defaultValue(type: TypeRef): Val { - if (type.dims) return null - if (type.name === 'int' || type.name === 'long' || type.name === 'short' || type.name === 'byte') return 0 - if (type.name === 'double' || type.name === 'float') return new JDouble(0) - if (type.name === 'boolean') return false - if (type.name === 'char') return new JChar(0) - return null -} - -const typeText = (t: TypeRef): string => `${t.name}${t.args.length ? `<${t.args.map(typeText).join(', ')}>` : ''}${'[]'.repeat(t.dims)}` - -// MARK: - Ansichten für den Debugger - -export interface VarView { - name: string - type: string - value: string - /** Objekt, auf das die Variable zeigt (für das Objektdiagramm). */ - ref?: number -} -export interface FrameView { - title: string - pos: Pos - vars: VarView[] -} -export interface HeapObject { - id: number - name: string - cls: string - fields: { name: string; type: string; value: string }[] -} - -export type RunState = - | { k: 'paused'; pos: Pos } - | { k: 'running' } - | { k: 'input' } - | { k: 'sleep'; ms: number } - | { k: 'done' } - | { k: 'exception'; text: string; pos?: Pos } - | { k: 'error'; error: MiniJavaError } - -export interface SeqEvent { - k: 'call' | 'create' | 'return' - /** Teilnehmer: `o12` (Objekt) oder `cMain` (Klasse, statisch); bei `return` leer. */ - from: string - to: string - label: string - /** Zeile des Aufrufs. */ - pos: Pos - /** Rückkehr aus einem Konstruktor: nicht als Pfeil zeichnen. */ - hidden?: boolean -} -export interface SeqParticipant { - key: string - cls: string - label: string - isStatic: boolean -} - -export type StepMode = 'run' | 'into' | 'over' | 'out' - -export interface MiniJavaIO { - out(text: string, err?: boolean): void -} - -// MARK: - Interpreter - -export class MiniJava { - readonly classes = new Map() - private frames: Frame[] = [] - private nextId = 1 - private quiet = 0 - private gen?: G - private send: string | null | undefined - private mode: StepMode = 'run' - private startDepth = 0 - private prevKey = '' - private pauseRequested = false - /** Objekt-Nummer → Name im Objektdiagramm (bleibt beim Weiterschalten gleich). */ - private names = new Map() - private tempNames = new Map() - /** Mitschnitt für das Sequenzdiagramm: Aufrufe eigener Methoden, `new`, Rückgaben. */ - private seq: SeqEvent[] = [] - private seqParticipants = new Map() - private seqLimit = 3000 - private stdin = new JNative('in', 0) - finished = false - - constructor( - files: { path: string; source: string }[], - private io: MiniJavaIO, - ) { - const object: RtClass = { name: 'Object', interfaces: [], statics: new Map(), staticTypes: new Map(), builtin: true, javaName: 'java.lang.Object' } - this.classes.set('Object', object) - for (const [name, sup, pkg] of builtinExceptions) - this.classes.set(name, { name, sup: this.classes.get(sup), interfaces: [], statics: new Map(), staticTypes: new Map(), builtin: true, javaName: `${pkg}.${name}` }) - const decls: ClassDecl[] = [] - for (const f of files) { - try { - decls.push(...parseJava(f.source, f.path)) - } catch (err) { - if (err instanceof JavaSyntaxError) throw new MiniJavaError('syntax', err.message, err.pos) - throw err - } - } - for (const d of decls) this.classes.set(d.name, { name: d.name, decl: d, interfaces: d.interfaces, statics: new Map(), staticTypes: new Map() }) - const own = new Set(decls.map((d) => d.name)) - for (const f of files) { - const missing = missingImport(f.source, f.path, own) - if (missing) throw new MiniJavaError('import', missing.line, missing.pos) - const typo = misspelledType(f.source, f.path, own) - if (typo) throw new MiniJavaError('typename', typo.type, typo.pos) - } - for (const d of decls) { - const c = this.classes.get(d.name)! - const sup = d.superclass ?? (d.kind === 'class' ? 'Object' : undefined) - if (sup) { - c.sup = this.classes.get(sup) - if (!c.sup) throw new MiniJavaError('symbol', sup, d) - } - } - } - - // MARK: Steuerung - - /** Programm vorbereiten: `main` der Klasse `mainClass` (oder der ersten mit main). */ - start(mainClass?: string) { - const candidates = [...this.classes.values()].filter((c) => c.decl?.methods.some(isMain)) - const cls = candidates.find((c) => c.name === mainClass) ?? candidates[0] - if (!cls) throw new MiniJavaError('nomain', '') - this.gen = this.program(cls) - this.finished = false - } - - /** Wie es weitergehen soll: bis zum Haltepunkt, in die nächste Anweisung, darüber, heraus. */ - go(mode: StepMode) { - this.mode = mode - this.startDepth = this.frames.length - this.pauseRequested = false - } - - pause() { - this.pauseRequested = true - } - - /** Antwort auf `input` (eine Zeile aus der Konsole; `null` = Eingabe beendet). */ - input(line: string | null) { - this.send = line - } - - /** Höchstens `budget` Anweisungen ausführen. Haltepunkte als `Datei:Zeile` (Zeile ab 0). */ - run(breakpoints: Set, budget = 5000): RunState { - if (!this.gen || this.finished) return { k: 'done' } - for (let n = 0; n < budget; n++) { - let r: IteratorResult - try { - r = this.gen.next(this.send) - } catch (err) { - this.finished = true - if (err instanceof ProgramExit) return { k: 'done' } - if (err instanceof JavaThrow) return { k: 'exception', text: this.uncaught(err.obj), pos: this.frames.at(-1)?.pos } - if (err instanceof MiniJavaError) return { k: 'error', error: err } - if (err instanceof RangeError) return { k: 'exception', text: 'Exception in thread "main" java.lang.StackOverflowError' } - throw err - } - this.send = undefined - if (r.done) { - this.finished = true - return { k: 'done' } - } - const ev = r.value - if (ev.k === 'input') return { k: 'input' } - if (ev.k === 'sleep') return { k: 'sleep', ms: ev.ms } - const key = `${ev.pos.file}:${ev.pos.line}` - const fresh = key !== this.prevKey - this.prevKey = key - const hit = - this.pauseRequested || this.mode === 'into' || (this.mode === 'over' && ev.depth <= this.startDepth) || (this.mode === 'out' && ev.depth < this.startDepth) || (fresh && breakpoints.has(key)) - if (hit) { - this.pauseRequested = false - return { k: 'paused', pos: ev.pos } - } - } - return { k: 'running' } - } - - private uncaught(obj: JObject): string { - const head = `Exception in thread "main" ${this.throwableText(obj)}` - return [head, ...(obj.native.trace ?? []).map((t) => `\tat ${t}`)].join('\n') - } - - // MARK: Ansichten - - /** Aufrufstapel, oberste Methode zuerst, mit ihren Variablen. */ - stack(): FrameView[] { - this.nameObjects() - return [...this.frames].reverse().map((f) => { - const vars: VarView[] = [] - if (f.self) vars.push({ name: 'this', type: f.self.cls.name, value: this.show(f.self), ref: f.self.id }) - for (const scope of f.scopes) for (const [name, v] of scope) vars.push({ name, type: typeText(v.type), value: this.show(v.v), ...(v.v instanceof JObject ? { ref: v.v.id } : {}) }) - return { title: f.title, pos: f.pos, vars } - }) - } - - /** Alle erreichbaren Objekte eigener Klassen (für das Objektdiagramm). */ - heap(): HeapObject[] { - return this.nameObjects().map((o) => ({ - id: o.id, - name: (this.names.get(o.id) ?? this.tempNames.get(o.id))!, - cls: o.cls.name, - fields: [...o.fields].map(([name, v]) => ({ name, type: typeText(this.fieldType(o.cls, name) ?? T('Object')), value: this.show(v) })), - })) - } - - private fieldType(cls: RtClass | undefined, name: string): TypeRef | undefined { - for (let c = cls; c; c = c.sup) { - const f = c.decl?.fields.find((x) => x.name === name && !x.isStatic) - if (f) return f.type - } - return undefined - } - - /** Objekte erreichbar von statischen Attributen und Variablen; Namen nach der Variablen (`s1`), dem Attribut (`team`) oder der Klasse. */ - private nameObjects(): JObject[] { - const found: JObject[] = [] - const seen = new Set() - const taken = new Set(this.names.values()) - this.tempNames.clear() - // Name nach einer Variablen bleibt; nach einem Attribut oder der Klasse (in einer Liste, `this` im Konstruktor) nur vorläufig. - const give = (o: JObject, base: string, keep: boolean) => { - if (this.names.has(o.id)) return - const real = /^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(base) - let name = real ? base : o.cls.name.charAt(0).toLowerCase() + o.cls.name.slice(1) - if (taken.has(name)) { - let n = 2 - while (taken.has(`${name}${n}`)) n++ - name = `${name}${n}` - } - taken.add(name) - ;(real && keep ? this.names : this.tempNames).set(o.id, name) - } - const queue: [Val, string, boolean?][] = [] - const visit = (v: Val, base: string, keep: boolean) => { - if (v instanceof JObject) { - if (v.cls.builtin || v.native.enumName !== undefined || seen.has(v.id)) return - seen.add(v.id) - give(v, base, keep) - found.push(v) - for (const [f, x] of v.fields) queue.push([x, f]) - } else if (v instanceof JArray || v instanceof JList) { - for (const x of v.items) if (x instanceof JObject) queue.push([x, '']) - } else if (v instanceof JMap) for (const [k, x] of v.entries.values()) queue.push([k, ''], [x, '']) - } - for (const f of this.frames) { - if (f.self) queue.push([f.self, '']) - for (const scope of f.scopes) for (const [name, v] of scope) queue.push([v.v, name, true]) - } - for (const c of this.classes.values()) for (const [name, v] of c.statics) queue.push([v, name, true]) - // Erst die direkt benannten Objekte (Variablen), dann was an Attributen hängt. - for (let i = 0; i < queue.length; i++) { - const [v, base, keep] = queue[i] - // Ohne Namen erst später besuchen: Vielleicht zeigt noch eine Variable darauf. - if (base === '' && v instanceof JObject && !this.names.has(v.id) && queue.slice(i + 1).some(([x, b]) => x === v && b !== '')) continue - visit(v, base, !!keep) - } - return found.sort((a, b) => a.id - b.id) - } - - /** Wert für Anzeige und Objektdiagramm: Objekte mit ihrem Namen, Texte in Anführungszeichen. */ - show(v: Val): string { - if (v === undefined) return '?' - if (v === null) return 'null' - if (typeof v === 'string') return JSON.stringify(v) - if (typeof v === 'number' || typeof v === 'boolean') return String(v) - if (v instanceof JDouble) return formatDouble(v.v) - if (v instanceof JChar) return v.code ? `'${String.fromCharCode(v.code)}'` : "''" - if (v instanceof JObject) { - if (v.native.enumName !== undefined) return v.native.enumName - if (v.cls.builtin) return v.cls.name - return this.names.get(v.id) ?? this.tempNames.get(v.id) ?? `${v.cls.name}@${javaHash(v.id)}` - } - if (v instanceof JArray || v instanceof JList) { - const items = v.items.slice(0, 30).map((x) => this.show(x)) - return `[${items.join(', ')}${v.items.length > 30 ? ', …' : ''}]` - } - if (v instanceof JMap) return `{${[...v.entries.values()].map(([k, x]) => `${this.show(k)}=${this.show(x)}`).join(', ')}}` - if (v instanceof JNative) return v.kind === 'StringBuilder' ? JSON.stringify(v.buf) : v.kind - return v.name - } - - // MARK: Sequenzdiagramm - - private seqKey(of: JObject | RtClass) { - return of instanceof JObject ? `o${of.id}` : `c${of.name}` - } - - private record(k: 'call' | 'create', to: JObject | RtClass, label: () => string): boolean { - if (this.quiet || this.seq.length >= this.seqLimit) return false - const caller = this.frames.at(-1) - const from = caller ? this.seqKey(caller.self ?? caller.cls) : '' - const key = this.seqKey(to) - if (!this.seqParticipants.has(key)) this.seqParticipants.set(key, to instanceof JObject ? { key, cls: to.cls.name, id: to.id } : { key, cls: to.name }) - this.nameObjects() - this.seq.push({ k, from, to: key, label: label(), pos: caller?.pos ?? { file: '', line: 0 } }) - return true - } - - private recordReturn(label: string, exception: boolean, hidden = false) { - if (this.seq.length >= this.seqLimit + 1000) return - this.seq.push({ k: 'return', from: '', to: '', label: exception ? '⚡' : label, pos: this.frames.at(-1)?.pos ?? { file: '', line: 0 }, ...(hidden && !exception ? { hidden } : {}) }) - } - - /** Mitschnitt mit Namen wie im Objektdiagramm: `s1 : Spieler`, ohne Namen `:Spieler`, statisch der Klassenname. */ - sequence(): { participants: SeqParticipant[]; events: SeqEvent[]; truncated: boolean } { - this.nameObjects() - const participants = [...this.seqParticipants.values()].map((p) => ({ - key: p.key, - cls: p.cls, - label: p.id === undefined ? p.cls : `${this.names.get(p.id) ?? this.tempNames.get(p.id) ?? ''} : ${p.cls}`, - isStatic: p.id === undefined, - })) - return { participants, events: [...this.seq], truncated: this.seq.length >= this.seqLimit } - } - - // MARK: Ablauf - - private get frame(): Frame { - return this.frames[this.frames.length - 1] - } - - private *step(at: Pos): G { - const pos = { file: at.file, line: at.line } - if (this.frames.length) this.frame.pos = pos - if (!this.quiet) yield { k: 'step', pos, depth: this.frames.length } - } - - private *program(cls: RtClass): G { - this.quiet++ - try { - for (const c of this.classes.values()) yield* this.initStatics(c) - } finally { - this.quiet-- - } - const main = cls.decl!.methods.find(isMain)! - this.seqParticipants.set(`c${cls.name}`, { key: `c${cls.name}`, cls: cls.name }) - yield* this.invoke(main, cls, undefined, [new JArray(T('String'), [], this.id())]) - } - - private id() { - return this.nextId++ - } - - private *initStatics(c: RtClass): G { - const d = c.decl - if (!d) return - if (d.kind === 'enum') - d.enumConstants.forEach((name, ordinal) => { - const o = new JObject(c, this.id()) - o.native = { enumName: name, ordinal } - c.statics.set(name, o) - c.staticTypes.set(name, T(c.name)) - }) - for (const f of d.fields) - if (f.isStatic) { - c.statics.set(f.name, defaultValue(f.type)) - c.staticTypes.set(f.name, f.type) - } - const frame: Frame = { cls: c, scopes: [new Map()], pos: d, title: `${c.name} (static)` } - this.frames.push(frame) - try { - for (const f of d.fields) if (f.isStatic && f.init) c.statics.set(f.name, castTo(yield* this.initValue(f.init, f.type), f.type)) - } finally { - this.frames.pop() - } - } - - private *initValue(e: Expr, type: TypeRef): G { - return e.k === 'arrayInit' ? yield* this.arrayFrom(e, type) : yield* this.eval(e) - } - - private *arrayFrom(e: Expr & { k: 'arrayInit' }, type: TypeRef): G { - const elem = { ...type, dims: type.dims - 1 } - const items: Val[] = [] - for (const x of e.items) items.push(castTo(x.k === 'arrayInit' ? yield* this.arrayFrom(x, elem) : yield* this.eval(x), elem)) - return new JArray(elem, items, this.id()) - } - - // MARK: Methoden und Konstruktoren - - private *invoke(m: MethodDecl, owner: RtClass, self: JObject | undefined, args: Val[]): G { - if (!m.body) throw new MiniJavaError('unsupported', `abstract ${m.name}`, m) - if (this.frames.length > 900) throw new JavaThrow(this.exception('StackOverflowError')) - const scope = new Map() - m.params.forEach((p, i) => scope.set(p.name, { v: castTo(args[i], p.type), type: p.type })) - const title = m.isConstructor ? `new ${owner.name}()` : `${owner.name}.${m.name}()` - const recorded = this.frames.length > 0 && this.record('call', self ? self : owner, () => `${m.name}(${args.map((a) => this.show(a)).join(', ')})`) - this.frames.push({ cls: owner, method: m, self, scopes: [scope], pos: m, title }) - let done = false - try { - const r = yield* this.execBody(m.body.body) - let v: Val = undefined - if (r?.k === 'return') v = castTo(r.v, m.returns) - else yield* this.step({ file: m.file, line: m.endLine }) - done = true - this.frames.pop() - if (recorded) this.recordReturn(m.returns && m.returns.name !== 'void' ? this.show(v) : '', false) - return v - } finally { - if (!done) { - this.frames.pop() - if (recorded) this.recordReturn('', true) - } - } - } - - private *execBody(body: Stmt[]): G { - for (const s of body) { - const r = yield* this.exec(s) - if (r) return r - } - return undefined - } - - private *construct(cls: RtClass, args: Val[], pos: Pos): G { - if (cls.decl?.isAbstract) throw new MiniJavaError('type', `abstract ${cls.name}`, pos) - const obj = new JObject(cls, this.id()) - const chain: RtClass[] = [] - for (let c: RtClass | undefined = cls; c; c = c.sup) chain.unshift(c) - for (const c of chain) for (const f of c.decl?.fields ?? []) if (!f.isStatic) obj.fields.set(f.name, defaultValue(f.type)) - const recorded = this.record('create', obj, () => `new ${cls.name}(${args.map((a) => this.show(a)).join(', ')})`) - let done = false - try { - yield* this.runCtor(cls, obj, args, pos) - done = true - } finally { - if (recorded) this.recordReturn('', !done, true) - } - return obj - } - - private *runCtor(cls: RtClass, obj: JObject, args: Val[], pos: Pos): G { - if (cls.builtin) { - if (args.length) obj.native.message = typeof args[0] === 'string' || args[0] === null ? args[0] : yield* this.str(args[0]) - obj.native.trace = this.trace() - return - } - const decl = cls.decl! - const ctors = decl.methods.filter((m) => m.isConstructor) - if (!ctors.length) { - if (args.length) throw new MiniJavaError('symbol', `${cls.name}(${args.length})`, pos) - if (cls.sup) yield* this.runCtor(cls.sup, obj, [], pos) - this.frames.push({ cls, self: obj, scopes: [new Map()], pos: decl, title: `new ${cls.name}()` }) - try { - yield* this.fieldInits(cls, obj) - } finally { - this.frames.pop() - } - return - } - const m = this.pick(ctors, args) - if (!m) throw new MiniJavaError('symbol', `${cls.name}(${args.length})`, pos) - const scope = new Map() - m.params.forEach((p, i) => scope.set(p.name, { v: castTo(args[i], p.type), type: p.type })) - this.frames.push({ cls, method: m, self: obj, scopes: [scope], pos: m, title: `new ${cls.name}()` }) - try { - let body = m.body!.body - const first = body[0] - if (first?.k === 'ctorCall') { - yield* this.step(first) - const a: Val[] = [] - for (const x of first.args) a.push(yield* this.eval(x)) - body = body.slice(1) - if (first.isSuper) { - if (cls.sup) yield* this.runCtor(cls.sup, obj, a, first) - yield* this.fieldInits(cls, obj) - } else yield* this.runCtor(cls, obj, a, first) - } else { - if (cls.sup) yield* this.runCtor(cls.sup, obj, [], m) - yield* this.fieldInits(cls, obj) - } - const r = yield* this.execBody(body) - if (r?.k !== 'return') yield* this.step({ file: m.file, line: m.endLine }) - } finally { - this.frames.pop() - } - } - - private *fieldInits(cls: RtClass, obj: JObject): G { - for (const f of cls.decl?.fields ?? []) - if (!f.isStatic && f.init) { - yield* this.step(f) - obj.fields.set(f.name, castTo(yield* this.initValue(f.init, f.type), f.type)) - } - } - - /** Passende Methode zu den Argumenten (Überladen: genauester Treffer). */ - private pick(methods: MethodDecl[], args: Val[]): MethodDecl | undefined { - let best: MethodDecl | undefined - let bestScore = -1 - for (const m of methods) { - if (m.params.length !== args.length) continue - let score = 0 - for (let i = 0; i < args.length && score >= 0; i++) { - const s = this.fits(args[i], m.params[i].type) - score = s < 0 ? -1 : score + s - } - if (score > bestScore) { - best = m - bestScore = score - } - } - return best - } - - /** Passt der Wert zum Parametertyp? 2 = genau, 1 = umwandelbar, -1 = nein. */ - private fits(v: Val, t: TypeRef): number { - const n = t.name - if (t.dims) return v === null ? 1 : v instanceof JArray ? 2 : -1 - if (intTypes.has(n)) return typeof v === 'number' ? 2 : v instanceof JChar ? 1 : -1 - if (doubleTypes.has(n)) return v instanceof JDouble ? 2 : isNum(v) ? 1 : -1 - if (n === 'char' || n === 'Character') return v instanceof JChar ? 2 : -1 - if (n === 'boolean' || n === 'Boolean') return typeof v === 'boolean' ? 2 : -1 - if (n === 'String') return typeof v === 'string' ? 2 : v === null ? 1 : -1 - if (n === 'Object') return 0 - if (listTypes.has(n)) return v instanceof JList ? 2 : v === null ? 1 : -1 - if (mapTypes.has(n)) return v instanceof JMap ? 2 : v === null ? 1 : -1 - const cls = this.classes.get(n) - if (cls) return v === null ? 1 : v instanceof JObject && this.isA(v.cls, n) ? (v.cls.name === n ? 2 : 1) : -1 - return 0 - } - - private isA(cls: RtClass | undefined, name: string): boolean { - const seen = new Set() - const walk = (c: RtClass | undefined): boolean => { - if (!c || seen.has(c.name)) return false - if (c.name === name) return true - seen.add(c.name) - return walk(c.sup) || c.interfaces.some((i) => walk(this.classes.get(i))) - } - return name === 'Object' || walk(cls) - } - - /** Methode suchen: Klasse, dann Oberklassen, dann Interfaces (default-Methoden). */ - private findMethod(cls: RtClass | undefined, name: string, args: Val[], wantBody = true): { m: MethodDecl; owner: RtClass } | undefined { - const seen = new Set() - const ifaces: RtClass[] = [] - for (let c = cls; c; c = c.sup) { - const m = this.pick(c.decl?.methods.filter((x) => x.name === name && !x.isConstructor && (!wantBody || x.body)) ?? [], args) - if (m) return { m, owner: c } - for (const i of c.interfaces) ifaces.push(this.classes.get(i)!) - } - while (ifaces.length) { - const c = ifaces.shift() - if (!c || seen.has(c.name)) continue - seen.add(c.name) - const m = this.pick(c.decl?.methods.filter((x) => x.name === name && (!wantBody || x.body)) ?? [], args) - if (m) return { m, owner: c } - for (const i of c.interfaces) ifaces.push(this.classes.get(i)!) - } - return undefined - } - - private trace(): string[] { - return [...this.frames].reverse().map((f) => { - const name = f.method ? (f.method.isConstructor ? '' : f.method.name) : '' - return `${f.cls.name}.${name}(${f.pos.file.split('/').pop()}:${f.pos.line + 1})` - }) - } - - private exception(name: string, message?: string): JObject { - const o = new JObject(this.classes.get(name)!, this.id()) - o.native = { message: message ?? null, trace: this.trace() } - return o - } - - private fail(name: string, message?: string): never { - throw new JavaThrow(this.exception(name, message)) - } - - private throwableText(o: JObject): string { - const msg = o.native.message - const name = o.cls.javaName ?? o.cls.name - return msg === null || msg === undefined ? name : `${name}: ${msg}` - } - - // MARK: Anweisungen - - private *exec(s: Stmt): G { - switch (s.k) { - case 'block': { - this.frame.scopes.push(new Map()) - try { - return yield* this.execBody(s.body) - } finally { - this.frame.scopes.pop() - } - } - case 'empty': - return undefined - case 'local': - yield* this.step(s) - yield* this.declare(s) - return undefined - case 'expr': - yield* this.step(s) - yield* this.eval(s.expr) - return undefined - case 'if': - yield* this.step(s) - if (yield* this.test(s.test)) return yield* this.exec(s.then) - return s.else ? yield* this.exec(s.else) : undefined - case 'while': - for (;;) { - yield* this.step(s) - if (!(yield* this.test(s.test))) return undefined - const r = yield* this.exec(s.body) - if (r?.k === 'break') return undefined - if (r?.k === 'return') return r - } - case 'do': - for (;;) { - const r = yield* this.exec(s.body) - if (r?.k === 'break') return undefined - if (r?.k === 'return') return r - yield* this.step(s.test) - if (!(yield* this.test(s.test))) return undefined - } - case 'for': { - this.frame.scopes.push(new Map()) - try { - yield* this.step(s) - for (const init of s.init) { - if (init.k === 'local') yield* this.declare(init) - else if (init.k === 'expr') yield* this.eval(init.expr) - } - for (;;) { - if (s.test && !(yield* this.test(s.test))) return undefined - const r = yield* this.exec(s.body) - if (r?.k === 'break') return undefined - if (r?.k === 'return') return r - yield* this.step(s) - for (const u of s.update) yield* this.eval(u) - } - } finally { - this.frame.scopes.pop() - } - } - case 'foreach': { - yield* this.step(s) - const iter = yield* this.eval(s.iter) - if (iter === null) this.fail('NullPointerException') - const items = iter instanceof JArray || iter instanceof JList ? [...iter.items] : iter instanceof JMap ? [...iter.entries.values()].map((e) => e[0]) : undefined - if (!items) throw new MiniJavaError('type', 'for-each', s) - for (let i = 0; i < items.length; i++) { - if (i > 0) yield* this.step(s) - if (iter instanceof JList && iter.items.length !== items.length) this.fail('ConcurrentModificationException') - this.frame.scopes.push(new Map([[s.name, { v: castTo(items[i], s.type), type: s.type }]])) - try { - const r = yield* this.exec(s.body) - if (r?.k === 'break') return undefined - if (r?.k === 'return') return r - } finally { - this.frame.scopes.pop() - } - } - return undefined - } - case 'switch': { - yield* this.step(s) - const v = yield* this.eval(s.expr) - let start = -1 - for (let i = 0; i < s.cases.length && start < 0; i++) { - const labels = s.cases[i].labels - if (labels === 'default') continue - for (const l of labels) { - const lv = v instanceof JObject && v.native.enumName !== undefined && l.k === 'name' ? v.cls.statics.get(l.name) : yield* this.eval(l) - if (this.same(v, lv)) start = i - } - } - if (start < 0) start = s.cases.findIndex((c) => c.labels === 'default') - if (start < 0) return undefined - this.frame.scopes.push(new Map()) - try { - for (let i = start; i < s.cases.length; i++) { - const r = yield* this.execBody(s.cases[i].body) - if (r?.k === 'break') return undefined - if (r) return r - if (s.cases[i].arrow) return undefined - } - } finally { - this.frame.scopes.pop() - } - return undefined - } - case 'break': - yield* this.step(s) - return BREAK - case 'continue': - yield* this.step(s) - return CONTINUE - case 'return': - yield* this.step(s) - return { k: 'return', v: s.value ? yield* this.eval(s.value) : undefined } - case 'throw': { - yield* this.step(s) - const v = yield* this.eval(s.value) - if (!(v instanceof JObject)) this.fail('NullPointerException') - throw new JavaThrow(v) - } - case 'try': { - let result: Done - try { - try { - result = yield* this.exec(s.body) - } catch (err) { - if (!(err instanceof JavaThrow)) throw err - const c = s.catches.find((x) => x.types.some((t) => this.isA(err.obj.cls, t.name))) - if (!c) throw err - this.frame.scopes.push(new Map([[c.name, { v: err.obj, type: c.types[0] }]])) - try { - result = yield* this.exec(c.body) - } finally { - this.frame.scopes.pop() - } - } - } finally { - if (s.finally) { - const r = yield* this.exec(s.finally) - if (r) return r - } - } - return result - } - case 'ctorCall': - throw new MiniJavaError('syntax', 'ctorCall', s) - } - } - - private *declare(s: Stmt & { k: 'local' }): G { - for (const v of s.vars) { - const type = v.dims ? { ...s.type, dims: s.type.dims + v.dims } : s.type - const value = v.init ? castTo(yield* this.initValue(v.init, type), type) : undefined - this.frame.scopes[this.frame.scopes.length - 1].set(v.name, { v: value, type }) - } - } - - private *test(e: Expr): G { - const v = yield* this.eval(e) - if (typeof v !== 'boolean') throw new MiniJavaError('type', 'boolean', e) - return v - } - - // MARK: Ausdrücke - - private lookupVar(name: string): Variable | undefined { - const scopes = this.frame.scopes - for (let i = scopes.length - 1; i >= 0; i--) { - const v = scopes[i].get(name) - if (v) return v - } - return undefined - } - - private staticOwner(cls: RtClass | undefined, name: string): RtClass | undefined { - const seen = new Set() - const walk = (c: RtClass | undefined): RtClass | undefined => { - if (!c || seen.has(c.name)) return undefined - seen.add(c.name) - if (c.statics.has(name)) return c - return walk(c.sup) ?? c.interfaces.map((i) => walk(this.classes.get(i))).find(Boolean) - } - return walk(cls) - } - - /** Ort zum Lesen und Schreiben: Variable, Attribut, statisches Attribut oder Array-Element. */ - private *place(e: Expr): G<{ get(): Val; set(v: Val): void; type?: TypeRef }> { - if (e.k === 'name') { - const local = this.lookupVar(e.name) - if (local) return { get: () => local.v, set: (v) => (local.v = v), type: local.type } - const self = this.frame.self - if (self?.fields.has(e.name)) return { get: () => self.fields.get(e.name), set: (v) => self.fields.set(e.name, v), type: this.fieldType(self.cls, e.name) } - const owner = this.staticOwner(this.frame.cls, e.name) - if (owner) return { get: () => owner.statics.get(e.name), set: (v) => owner.statics.set(e.name, v), type: owner.staticTypes.get(e.name) } - if (this.classes.has(e.name) || staticBuiltins.has(e.name)) return { get: () => new ClassRef(e.name), set: () => {} } - throw new MiniJavaError('symbol', e.name, e) - } - if (e.k === 'field') { - const target = yield* this.eval(e.target) - if (target instanceof JObject && target.fields.has(e.name)) return { get: () => target.fields.get(e.name), set: (v) => target.fields.set(e.name, v), type: this.fieldType(target.cls, e.name) } - const owner = target instanceof ClassRef ? this.staticOwner(this.classes.get(target.name), e.name) : target instanceof JObject ? this.staticOwner(target.cls, e.name) : undefined - if (owner) return { get: () => owner.statics.get(e.name), set: (v) => owner.statics.set(e.name, v), type: owner.staticTypes.get(e.name) } - const v = this.builtinField(target, e.name, e) - return { - get: () => v, - set: () => { - throw new MiniJavaError('type', e.name, e) - }, - } - } - if (e.k === 'index') { - const arr = yield* this.eval(e.target) - const i = num(yield* this.eval(e.index)) - if (arr === null) this.fail('NullPointerException') - if (!(arr instanceof JArray)) throw new MiniJavaError('type', '[]', e) - if (i < 0 || i >= arr.items.length) this.fail('ArrayIndexOutOfBoundsException', `Index ${i} out of bounds for length ${arr.items.length}`) - return { get: () => arr.items[i], set: (v) => (arr.items[i] = v), type: arr.elem } - } - const v = yield* this.eval(e) - return { - get: () => v, - set: () => { - throw new MiniJavaError('syntax', 'assignTarget', e) - }, - } - } - - private builtinField(target: Val, name: string, pos: Pos): Val { - if (target === null) this.fail('NullPointerException', `Cannot read field "${name}" because value is null`) - if (target instanceof JArray && name === 'length') return target.items.length - if (target instanceof ClassRef) { - const key = `${target.name}.${name}` - switch (key) { - case 'System.out': - return new JNative('out', 0) - case 'System.err': - return new JNative('err', 0) - case 'System.in': - return this.stdin - case 'Math.PI': - return new JDouble(Math.PI) - case 'Math.E': - return new JDouble(Math.E) - case 'Integer.MAX_VALUE': - return 2147483647 - case 'Integer.MIN_VALUE': - return -2147483648 - case 'Long.MAX_VALUE': - return Number.MAX_SAFE_INTEGER - case 'Double.MAX_VALUE': - return new JDouble(Number.MAX_VALUE) - case 'Double.MIN_VALUE': - return new JDouble(Number.MIN_VALUE) - } - } - throw new MiniJavaError('symbol', name, pos) - } - - private *eval(e: Expr): G { - switch (e.k) { - case 'lit': { - const { t, v } = e.value - return t === 'double' ? new JDouble(v as number) : t === 'char' ? new JChar(v as number) : (v as Val) - } - case 'name': { - const local = this.lookupVar(e.name) - if (local) { - if (local.v === undefined) throw new MiniJavaError('type', `uninit:${e.name}`, e) - return local.v - } - return (yield* this.place(e)).get() - } - case 'this': - if (!this.frame.self) throw new MiniJavaError('type', 'static:this', e) - return this.frame.self - case 'super': - if (!this.frame.self) throw new MiniJavaError('type', 'static:super', e) - return this.frame.self - case 'field': - case 'index': - return (yield* this.place(e)).get() - case 'assign': { - const p = yield* this.place(e.target) - let v: Val - if (e.op === '=') v = e.value.k === 'arrayInit' && p.type ? yield* this.arrayFrom(e.value, p.type) : yield* this.eval(e.value) - else { - const old = p.get() - v = yield* this.binary(e.op.slice(0, -1), old, yield* this.eval(e.value), e) - } - v = castTo(v, p.type) - p.set(v) - return v - } - case 'incdec': { - const p = yield* this.place(e.target) - const old = p.get() - if (!isNum(old)) this.fail('NullPointerException') - const d = e.op === '++' ? 1 : -1 - const nv = old instanceof JDouble ? new JDouble(old.v + d) : old instanceof JChar ? new JChar((old.code + d) & 0xffff) : inInt(num(old)) ? (num(old) + d) | 0 : num(old) + d - p.set(castTo(nv, p.type)) - return e.prefix ? nv : old - } - case 'unary': { - const v = yield* this.eval(e.expr) - if (e.op === '!') return !v - if (e.op === '-') return v instanceof JDouble ? new JDouble(-v.v) : inInt(num(v)) ? -num(v) | 0 : -num(v) - if (e.op === '~') return ~num(v) - return v instanceof JChar ? v.code : v - } - case 'binary': { - if (e.op === '&&') return (yield* this.test(e.left)) && (yield* this.test(e.right)) - if (e.op === '||') return (yield* this.test(e.left)) || (yield* this.test(e.right)) - const a = yield* this.eval(e.left) - const b = yield* this.eval(e.right) - return yield* this.binary(e.op, a, b, e) - } - case 'cond': - return (yield* this.test(e.test)) ? yield* this.eval(e.then) : yield* this.eval(e.else) - case 'cast': { - const v = yield* this.eval(e.expr) - const n = e.type.name - if (v instanceof JObject && this.classes.has(n) && !this.isA(v.cls, n)) this.fail('ClassCastException', `class ${v.cls.name} cannot be cast to class ${n}`) - return castTo(v, e.type) - } - case 'instanceof': { - const v = yield* this.eval(e.expr) - if (v === null) return false - const n = e.type.name - if (v instanceof JObject) return this.isA(v.cls, n) - if (typeof v === 'string') return n === 'String' || n === 'Object' || n === 'CharSequence' - if (v instanceof JList) return listTypes.has(n) || n === 'Object' - return n === 'Object' - } - case 'new': - return yield* this.create(e) - case 'newArray': { - if (e.init && e.init.k === 'arrayInit') return yield* this.arrayFrom(e.init, e.type) - const sizes: number[] = [] - for (const s of e.sizes) sizes.push(num(yield* this.eval(s))) - const make = (level: number, type: TypeRef): JArray => { - const n = sizes[level] - if (n < 0) this.fail('NegativeArraySizeException', String(n)) - const elem = { ...type, dims: type.dims - 1 } - const items = Array.from({ length: n }, () => (level + 1 < sizes.length ? make(level + 1, elem) : defaultValue(elem))) - return new JArray(elem, items, this.id()) - } - return make(0, e.type) - } - case 'arrayInit': - return yield* this.arrayFrom(e, T('Object', 1)) - case 'call': - return yield* this.call(e) - } - } - - private *binary(op: string, a: Val, b: Val, pos: Pos): G { - if (op === '+' && (typeof a === 'string' || typeof b === 'string')) return (yield* this.str(a)) + (yield* this.str(b)) - if (op === '==') return this.same(a, b) - if (op === '!=') return !this.same(a, b) - if (typeof a === 'boolean' && typeof b === 'boolean') { - if (op === '&') return a && b - if (op === '|') return a || b - if (op === '^') return a !== b - } - if (!isNum(a) || !isNum(b)) { - if (a === null || b === null) this.fail('NullPointerException') - throw new MiniJavaError('type', op, pos) - } - const x = num(a) - const y = num(b) - const dbl = a instanceof JDouble || b instanceof JDouble - const ints = inInt(x) && inInt(y) - switch (op) { - case '+': - return dbl ? new JDouble(x + y) : ints ? (x + y) | 0 : x + y - case '-': - return dbl ? new JDouble(x - y) : ints ? (x - y) | 0 : x - y - case '*': - return dbl ? new JDouble(x * y) : ints ? Math.imul(x, y) : x * y - case '/': - if (dbl) return new JDouble(x / y) - if (y === 0) this.fail('ArithmeticException', '/ by zero') - return ints ? Math.trunc(x / y) | 0 : Math.trunc(x / y) - case '%': - if (dbl) return new JDouble(x % y) - if (y === 0) this.fail('ArithmeticException', '/ by zero') - return x % y - case '<': - return x < y - case '>': - return x > y - case '<=': - return x <= y - case '>=': - return x >= y - case '&': - return x & y - case '|': - return x | y - case '^': - return x ^ y - case '<<': - return x << y - case '>>': - return x >> y - case '>>>': - return x >>> y - } - throw new MiniJavaError('unsupported', op, pos) - } - - private same(a: Val, b: Val): boolean { - if (isNum(a) && isNum(b)) return num(a) === num(b) - return a === b - } - - /** Text eines Werts wie `String.valueOf` (ruft eigenes `toString()` auf). */ - private *str(v: Val): G { - if (v === null || v === undefined) return 'null' - if (typeof v === 'string') return v - if (typeof v === 'number' || typeof v === 'boolean') return String(v) - if (v instanceof JDouble) return formatDouble(v.v) - if (v instanceof JChar) return String.fromCharCode(v.code) - if (v instanceof JObject) { - const own = this.findMethod(v.cls, 'toString', []) - if (own) return yield* this.str(yield* this.invoke(own.m, own.owner, v, [])) - if (v.native.enumName !== undefined) return v.native.enumName - if (v.cls.builtin) return this.throwableText(v) - return `${v.cls.name}@${javaHash(v.id)}` - } - if (v instanceof JList) { - const parts: string[] = [] - for (const x of v.items) parts.push(x === v ? '(this Collection)' : yield* this.str(x)) - return `[${parts.join(', ')}]` - } - if (v instanceof JMap) { - const parts: string[] = [] - for (const [k, x] of v.entries.values()) parts.push(`${yield* this.str(k)}=${yield* this.str(x)}`) - return `{${parts.join(', ')}}` - } - if (v instanceof JArray) { - const code = v.elem.dims ? '[L' : (({ int: 'I', double: 'D', char: 'C', boolean: 'Z', long: 'J' } as Record)[v.elem.name] ?? `L${v.elem.name};`) - return `[${code}@${javaHash(v.id)}` - } - if (v instanceof JNative) return v.kind === 'StringBuilder' ? v.buf : `java.util.${v.kind}@${javaHash(v.id)}` - return `class ${v.name}` - } - - private *equalsVal(a: Val, b: Val): G { - if (a instanceof JObject && !a.cls.builtin) { - const own = this.findMethod(a.cls, 'equals', [b]) - if (own) return (yield* this.invoke(own.m, own.owner, a, [b])) === true - } - if (typeof a === 'string' || a === null) return a === b - return this.same(a, b) && a instanceof JDouble === b instanceof JDouble - } - - private *create(e: Expr & { k: 'new' }): G { - const args: Val[] = [] - for (const a of e.args) args.push(yield* this.eval(a)) - const n = e.type.name - const cls = this.classes.get(n) - if (cls) return yield* this.construct(cls, args, e) - if (n === 'ArrayList' || n === 'LinkedList') { - const l = new JList(this.id()) - if (args[0] instanceof JList) l.items = [...args[0].items] - return l - } - if (n === 'HashMap' || n === 'TreeMap' || n === 'LinkedHashMap') return new JMap(this.id()) - if (n === 'Scanner') return new JNative('Scanner', this.id()) - if (n === 'Random') return new JNative('Random', this.id()) - if (n === 'StringBuilder') { - const sb = new JNative('StringBuilder', this.id()) - if (typeof args[0] === 'string') sb.buf = args[0] - return sb - } - if (n === 'String') return args.length ? yield* this.str(args[0] instanceof JArray ? args[0].items.map((c) => (c instanceof JChar ? String.fromCharCode(c.code) : '')).join('') : args[0]) : '' - if (n === 'Object') return new JObject(this.classes.get('Object')!, this.id()) - throw new MiniJavaError('unsupported', `new ${n}`, e) - } - - // MARK: Aufrufe - - private *call(e: Expr & { k: 'call' }): G { - const args = () => this.evalArgs(e.args) - if (!e.target) { - const a = yield* args() - const self = this.frame.self - const found = this.findMethod(self?.cls ?? this.frame.cls, e.name, a) ?? (self ? this.findMethod(this.frame.cls, e.name, a) : undefined) - if (!found) throw new MiniJavaError('symbol', `${e.name}()`, e) - if (!found.m.isStatic && !self) throw new MiniJavaError('type', `static:${e.name}`, e) - return yield* this.invoke(found.m, found.owner, found.m.isStatic ? undefined : self, a) - } - if (e.target.k === 'super') { - const a = yield* args() - const found = this.findMethod(this.frame.cls.sup, e.name, a) - if (found) return yield* this.invoke(found.m, found.owner, this.frame.self, a) - return yield* this.objectMethod(this.frame.self!, e.name, a, e, true) - } - const target = yield* this.eval(e.target) - const a = yield* args() - if (target === null || target === undefined) this.fail('NullPointerException', `Cannot invoke "${e.name}()" because value is null`) - if (typeof target === 'string') return yield* this.stringMethod(target, e.name, a, e) - if (target instanceof JObject) { - const found = this.findMethod(target.cls, e.name, a) - if (found) return yield* this.invoke(found.m, found.owner, found.m.isStatic ? undefined : target, a) - return yield* this.objectMethod(target, e.name, a, e) - } - if (target instanceof JList) return yield* this.listMethod(target, e.name, a, e) - if (target instanceof JMap) return yield* this.mapMethod(target, e.name, a, e) - if (target instanceof JNative) return yield* this.nativeMethod(target, e.name, a, e) - if (target instanceof ClassRef) { - const cls = this.classes.get(target.name) - if (cls && !cls.builtin) { - if (cls.decl?.kind === 'enum' && e.name === 'values') - return new JArray( - T(cls.name), - cls.decl.enumConstants.map((c) => cls.statics.get(c)), - this.id(), - ) - if (cls.decl?.kind === 'enum' && e.name === 'valueOf') { - const v = cls.statics.get(String(a[0])) - if (!(v instanceof JObject)) this.fail('IllegalArgumentException', `No enum constant ${cls.name}.${a[0]}`) - return v - } - const found = this.findMethod(cls, e.name, a) - if (!found) throw new MiniJavaError('symbol', `${target.name}.${e.name}()`, e) - return yield* this.invoke(found.m, found.owner, undefined, a) - } - return yield* this.staticBuiltin(target.name, e.name, a, e) - } - if (target instanceof JArray) { - if (e.name === 'clone') return new JArray(target.elem, [...target.items], this.id()) - if (e.name === 'length') throw new MiniJavaError('symbol', 'length()', e) - } - if (isNum(target) || typeof target === 'boolean') { - if (e.name === 'equals') return yield* this.equalsVal(target, a[0]) - if (e.name === 'toString') return yield* this.str(target) - if (e.name === 'compareTo') return Math.sign(num(target) - num(a[0])) - if (e.name === 'intValue') return Math.trunc(num(target)) - if (e.name === 'doubleValue') return new JDouble(num(target)) - } - throw new MiniJavaError('symbol', `${e.name}()`, e) - } - - private *evalArgs(list: Expr[]): G { - const out: Val[] = [] - for (const a of list) out.push(yield* this.eval(a)) - return out - } - - private *objectMethod(o: JObject, name: string, a: Val[], pos: Pos, viaSuper = false): G { - switch (name) { - case 'toString': - if (viaSuper) return o.cls.builtin || this.isA(o.cls, 'Throwable') ? this.throwableText(o) : `${o.cls.name}@${javaHash(o.id)}` - return yield* this.str(o) - case 'equals': - return o === a[0] - case 'hashCode': - return parseInt(javaHash(o.id), 16) | 0 - case 'getClass': - return new ClassRef(o.cls.name) - case 'getMessage': - case 'getLocalizedMessage': - return o.native.message ?? null - case 'printStackTrace': - this.io.out(`${this.throwableText(o)}\n${(o.native.trace ?? []).map((t) => `\tat ${t}\n`).join('')}`, true) - return undefined - case 'name': - if (o.native.enumName !== undefined) return o.native.enumName - break - case 'ordinal': - if (o.native.ordinal !== undefined) return o.native.ordinal - break - case 'compareTo': - if (o.native.ordinal !== undefined && a[0] instanceof JObject) return o.native.ordinal - (a[0].native.ordinal ?? 0) - break - } - throw new MiniJavaError('symbol', `${o.cls.name}.${name}()`, pos) - } - - private index(i: Val, size: number, pos: Pos, inclusive = false): number { - const n = num(i) - if (!(n >= 0 && (inclusive ? n <= size : n < size))) this.fail('IndexOutOfBoundsException', `Index ${n} out of bounds for length ${size}`) - void pos - return n - } - - private *stringMethod(s: string, name: string, a: Val[], pos: Pos): G { - const sIdx = (i: Val, inclusive = false) => { - const n = num(i) - if (!(n >= 0 && (inclusive ? n <= s.length : n < s.length))) this.fail('StringIndexOutOfBoundsException', `index ${n}, length ${s.length}`) - return n - } - const arg = typeof a[0] === 'string' ? a[0] : a[0] instanceof JChar ? String.fromCharCode(a[0].code) : yield* this.str(a[0]) - switch (name) { - case 'length': - return s.length - case 'charAt': - return new JChar(s.charCodeAt(sIdx(a[0]))) - case 'equals': - return s === a[0] - case 'equalsIgnoreCase': - return typeof a[0] === 'string' && s.toLowerCase() === a[0].toLowerCase() - case 'compareTo': - case 'compareToIgnoreCase': { - const x = name === 'compareTo' ? s : s.toLowerCase() - const y = name === 'compareTo' ? arg : arg.toLowerCase() - for (let i = 0; i < Math.min(x.length, y.length); i++) if (x[i] !== y[i]) return x.charCodeAt(i) - y.charCodeAt(i) - return x.length - y.length - } - case 'substring': { - const from = sIdx(a[0], true) - const to = a.length > 1 ? sIdx(a[1], true) : s.length - if (to < from) this.fail('StringIndexOutOfBoundsException', `begin ${from}, end ${to}, length ${s.length}`) - return s.substring(from, to) - } - case 'indexOf': - return s.indexOf(arg, a.length > 1 ? num(a[1]) : 0) - case 'lastIndexOf': - return s.lastIndexOf(arg) - case 'contains': - return s.includes(arg) - case 'startsWith': - return s.startsWith(arg) - case 'endsWith': - return s.endsWith(arg) - case 'isEmpty': - return s.length === 0 - case 'isBlank': - return s.trim().length === 0 - case 'toUpperCase': - return s.toUpperCase() - case 'toLowerCase': - return s.toLowerCase() - case 'trim': - case 'strip': - return s.trim() - case 'concat': - return s + arg - case 'replace': { - const to = typeof a[1] === 'string' ? a[1] : a[1] instanceof JChar ? String.fromCharCode(a[1].code) : yield* this.str(a[1]) - return s.split(arg).join(to) - } - case 'replaceAll': - return s.replace(new RegExp(arg, 'g'), String(a[1])) - case 'matches': - return new RegExp(`^(?:${arg})$`).test(s) - case 'split': - return new JArray( - T('String'), - (s === '' ? [''] : s.split(new RegExp(arg))).filter((x, i, all) => x !== '' || i < all.length - 1 || all.length === 1), - this.id(), - ) - case 'toCharArray': - return new JArray( - T('char'), - [...s].map((c) => new JChar(c.charCodeAt(0))), - this.id(), - ) - case 'repeat': - return s.repeat(num(a[0])) - case 'hashCode': - return stringHash(s) - case 'toString': - case 'intern': - return s - case 'chars': - break - } - throw new MiniJavaError('unsupported', `String.${name}()`, pos) - } - - private *listMethod(l: JList, name: string, a: Val[], pos: Pos): G { - switch (name) { - case 'add': - if (a.length === 2) l.items.splice(this.index(a[0], l.items.length, pos, true), 0, a[1]) - else l.items.push(a[0]) - return a.length === 2 ? undefined : true - case 'addAll': - if (a[0] instanceof JList) l.items.push(...a[0].items) - return true - case 'get': - return l.items[this.index(a[0], l.items.length, pos)] - case 'set': { - const i = this.index(a[0], l.items.length, pos) - const old = l.items[i] - l.items[i] = a[1] - return old - } - case 'size': - return l.items.length - case 'isEmpty': - return l.items.length === 0 - case 'clear': - l.items = [] - return undefined - case 'remove': { - if (typeof a[0] === 'number') return l.items.splice(this.index(a[0], l.items.length, pos), 1)[0] - for (let i = 0; i < l.items.length; i++) - if (yield* this.equalsVal(a[0], l.items[i])) { - l.items.splice(i, 1) - return true - } - return false - } - case 'contains': - case 'indexOf': - for (let i = 0; i < l.items.length; i++) if (yield* this.equalsVal(a[0], l.items[i])) return name === 'contains' ? true : i - return name === 'contains' ? false : -1 - case 'getFirst': - case 'getLast': - case 'removeFirst': - case 'removeLast': { - if (!l.items.length) this.fail('NoSuchElementException') - const first = name.endsWith('First') - const v = first ? l.items[0] : l.items[l.items.length - 1] - if (name.startsWith('remove')) l.items.splice(first ? 0 : l.items.length - 1, 1) - return v - } - case 'addFirst': - l.items.unshift(a[0]) - return undefined - case 'addLast': - l.items.push(a[0]) - return undefined - case 'toString': - return yield* this.str(l) - case 'sort': - l.items = yield* this.sorted(l.items) - return undefined - } - throw new MiniJavaError('unsupported', `ArrayList.${name}()`, pos) - } - - private key(v: Val): string { - if (typeof v === 'string') return `s${v}` - if (isNum(v)) return `n${num(v)}` - if (v instanceof JObject && v.native.enumName !== undefined) return `e${v.cls.name}.${v.native.enumName}` - if (v instanceof JObject || v instanceof JList || v instanceof JArray) return `o${v.id}` - return String(v) - } - - private *mapMethod(m: JMap, name: string, a: Val[], pos: Pos): G { - const k = this.key(a[0]) - switch (name) { - case 'put': { - const old = m.entries.get(k)?.[1] ?? null - m.entries.set(k, [a[0], a[1]]) - return old - } - case 'get': - return m.entries.get(k)?.[1] ?? null - case 'getOrDefault': - return m.entries.has(k) ? m.entries.get(k)![1] : a[1] - case 'containsKey': - return m.entries.has(k) - case 'containsValue': - for (const [, v] of m.entries.values()) if (yield* this.equalsVal(a[0], v)) return true - return false - case 'remove': { - const old = m.entries.get(k)?.[1] ?? null - m.entries.delete(k) - return old - } - case 'size': - return m.entries.size - case 'isEmpty': - return m.entries.size === 0 - case 'clear': - m.entries.clear() - return undefined - case 'keySet': - case 'values': { - const l = new JList(this.id()) - l.items = [...m.entries.values()].map((e) => (name === 'keySet' ? e[0] : e[1])) - return l - } - case 'toString': - return yield* this.str(m) - } - throw new MiniJavaError('unsupported', `HashMap.${name}()`, pos) - } - - private *nativeMethod(n: JNative, name: string, a: Val[], pos: Pos): G { - if (n.kind === 'out' || n.kind === 'err') { - const err = n.kind === 'err' - if (name === 'println') this.io.out(`${a.length ? yield* this.str(a[0]) : ''}\n`, err) - else if (name === 'print') this.io.out(yield* this.str(a[0]), err) - else if (name === 'printf' || name === 'format') this.io.out(yield* this.format(String(a[0]), a.slice(1)), err) - else if (name === 'flush') return undefined - else throw new MiniJavaError('unsupported', `System.out.${name}()`, pos) - return undefined - } - if (n.kind === 'Scanner') return yield* this.scan(name, pos) - if (n.kind === 'Random') { - if (name === 'nextInt') { - if (!a.length) return (Math.random() * 4294967296 - 2147483648) | 0 - const lo = a.length > 1 ? num(a[0]) : 0 - const hi = a.length > 1 ? num(a[1]) : num(a[0]) - if (hi <= lo) this.fail('IllegalArgumentException', 'bound must be positive') - return lo + Math.floor(Math.random() * (hi - lo)) - } - if (name === 'nextDouble') return new JDouble(Math.random()) - if (name === 'nextBoolean') return Math.random() < 0.5 - } - if (n.kind === 'StringBuilder') { - switch (name) { - case 'append': - n.buf += yield* this.str(a[0]) - return n - case 'toString': - return n.buf - case 'length': - return n.buf.length - case 'reverse': - n.buf = [...n.buf].reverse().join('') - return n - case 'charAt': - return new JChar(n.buf.charCodeAt(num(a[0]))) - case 'insert': - n.buf = n.buf.slice(0, num(a[0])) + (yield* this.str(a[1])) + n.buf.slice(num(a[0])) - return n - case 'setLength': - n.buf = n.buf.slice(0, num(a[0])) - return undefined - case 'deleteCharAt': - n.buf = n.buf.slice(0, num(a[0])) + n.buf.slice(num(a[0]) + 1) - return n - } - } - if (name === 'close') return undefined - throw new MiniJavaError('unsupported', `${n.kind}.${name}()`, pos) - } - - /** Scanner wie in Java: Wörter aus dem Puffer, `nextLine` liest bis zum Zeilenende (auch das leere Rest-Ende nach `nextInt`). */ - private *scan(name: string, pos: Pos): G { - const sc = this.stdin - const more = function* (): G { - const line = yield { k: 'input' } - if (line === null || line === undefined) return false - sc.buf += `${line}\n` - return true - } - const token = function* (self: MiniJava, consume: boolean): G { - for (;;) { - const m = /^\s*(\S+)/.exec(sc.buf) - if (m && (m[0].length < sc.buf.length || sc.buf.endsWith('\n'))) { - if (consume) sc.buf = sc.buf.slice(m[0].length) - return m[1] - } - if (!(yield* more())) return undefined - void self - } - } - switch (name) { - case 'nextLine': { - if (!sc.buf.includes('\n') && !(yield* more())) this.fail('NoSuchElementException', 'No line found') - const i = sc.buf.indexOf('\n') - const line = sc.buf.slice(0, i) - sc.buf = sc.buf.slice(i + 1) - return line - } - case 'next': - case 'nextInt': - case 'nextLong': - case 'nextDouble': - case 'nextBoolean': { - const t = yield* token(this, false) - if (t === undefined) this.fail('NoSuchElementException') - let v: Val - if (name === 'next') v = t - else if (name === 'nextInt' || name === 'nextLong') v = /^[+-]?\d+$/.test(t) ? parseInt(t, 10) : undefined - else if (name === 'nextDouble') v = /^[+-]?(\d+([.,]\d*)?|[.,]\d+)$/.test(t) ? new JDouble(parseFloat(t.replace(',', '.'))) : undefined - else v = /^(true|false)$/i.test(t) ? t.toLowerCase() === 'true' : undefined - if (v === undefined) this.fail('InputMismatchException', `For input string: "${t}"`) - yield* token(this, true) - return v - } - case 'hasNext': - case 'hasNextInt': - case 'hasNextDouble': - case 'hasNextLine': { - if (name === 'hasNextLine') return sc.buf.includes('\n') || (yield* more()) - const t = yield* token(this, false) - if (t === undefined) return false - if (name === 'hasNextInt') return /^[+-]?\d+$/.test(t) - if (name === 'hasNextDouble') return /^[+-]?(\d+([.,]\d*)?|[.,]\d+)$/.test(t) - return true - } - case 'close': - return undefined - } - throw new MiniJavaError('unsupported', `Scanner.${name}()`, pos) - } - - /** `printf` / `String.format`: %d %s %f %.2f %c %b %n %x %e %% mit Breite und `-`/`0`. */ - private *format(fmt: string, args: Val[]): G { - let i = 0 - let out = '' - const re = /%([-#+ 0,(]*)(\d+)?(?:\.(\d+))?([a-zA-Z%])/g - let last = 0 - for (let m = re.exec(fmt); m; m = re.exec(fmt)) { - out += fmt.slice(last, m.index) - last = re.lastIndex - const [, flags, width, prec, conv] = m - let s: string - if (conv === 'n') s = '\n' - else if (conv === '%') s = '%' - else { - if (i >= args.length) this.fail('IllegalArgumentException', `Format specifier '${m[0]}'`) - const v = args[i++] - switch (conv.toLowerCase()) { - case 'd': - if (v instanceof JDouble) this.fail('IllegalArgumentException', `d != java.lang.Double`) - s = String(Math.trunc(num(v))) - if (flags.includes(',')) s = s.replace(/\B(?=(\d{3})+(?!\d))/g, '.') - break - case 'f': - s = num(v).toFixed(prec ? Number(prec) : 6) - if (flags.includes(',')) { - const [int, frac] = s.split('.') - s = int.replace(/\B(?=(\d{3})+(?!\d))/g, '.') + (frac ? `,${frac}` : '') - } else s = s.replace('.', ',') - break - case 'e': - s = num(v) - .toExponential(prec ? Number(prec) : 6) - .replace('.', ',') - break - case 'x': - s = (num(v) >>> 0).toString(16) - break - case 'c': - s = v instanceof JChar ? String.fromCharCode(v.code) : String.fromCharCode(num(v)) - break - case 'b': - s = String(v !== null && v !== false) - break - default: - s = yield* this.str(v) - if (prec) s = s.slice(0, Number(prec)) - } - if (flags.includes('+') && /^\d/.test(s) && /[df]/i.test(conv)) s = `+${s}` - if (conv === conv.toUpperCase() && conv !== '%') s = s.toUpperCase() - } - if (width && s.length < Number(width)) { - const pad = Number(width) - s.length - s = flags.includes('-') ? s + ' '.repeat(pad) : flags.includes('0') ? s.replace(/^([+-]?)/, `$1${'0'.repeat(pad)}`) : ' '.repeat(pad) + s - } - out += s - } - return out + fmt.slice(last) - } - - private *sorted(items: Val[]): G { - const out = [...items] - for (let i = 1; i < out.length; i++) for (let j = i; j > 0 && (yield* this.compare(out[j - 1], out[j])) > 0; j--) [out[j - 1], out[j]] = [out[j], out[j - 1]] - return out - } - - private *compare(a: Val, b: Val): G { - if (isNum(a) && isNum(b)) return num(a) - num(b) - if (typeof a === 'string' && typeof b === 'string') return a < b ? -1 : a > b ? 1 : 0 - if (a instanceof JObject) { - const own = this.findMethod(a.cls, 'compareTo', [b]) - if (own) return num(yield* this.invoke(own.m, own.owner, a, [b])) - if (a.native.ordinal !== undefined && b instanceof JObject) return a.native.ordinal - (b.native.ordinal ?? 0) - } - this.fail('ClassCastException', 'not Comparable') - } - - private *staticBuiltin(cls: string, name: string, a: Val[], pos: Pos): G { - const x = num(a[0]) - const dbl = (v: number) => new JDouble(v) - switch (`${cls}.${name}`) { - case 'Math.abs': - return a[0] instanceof JDouble ? dbl(Math.abs(x)) : Math.abs(x) - case 'Math.max': - case 'Math.min': { - const r = name === 'max' ? Math.max(x, num(a[1])) : Math.min(x, num(a[1])) - return a[0] instanceof JDouble || a[1] instanceof JDouble ? dbl(r) : r - } - case 'Math.pow': - return dbl(Math.pow(x, num(a[1]))) - case 'Math.sqrt': - return dbl(Math.sqrt(x)) - case 'Math.cbrt': - return dbl(Math.cbrt(x)) - case 'Math.random': - return dbl(Math.random()) - case 'Math.round': - return Math.floor(x + 0.5) - case 'Math.floor': - return dbl(Math.floor(x)) - case 'Math.ceil': - return dbl(Math.ceil(x)) - case 'Math.sin': - case 'Math.cos': - case 'Math.tan': - case 'Math.log': - case 'Math.log10': - case 'Math.exp': - return dbl((Math as unknown as Record number>)[name](x)) - case 'Math.toRadians': - return dbl((x * Math.PI) / 180) - case 'Math.toDegrees': - return dbl((x * 180) / Math.PI) - case 'Math.floorMod': - return ((x % num(a[1])) + num(a[1])) % num(a[1]) - case 'Integer.parseInt': - case 'Integer.valueOf': - case 'Long.parseLong': { - if (typeof a[0] === 'number') return a[0] - const s = String(a[0]) - if (!/^[+-]?\d+$/.test(s)) this.fail('NumberFormatException', `For input string: "${s}"`) - return parseInt(s, 10) - } - case 'Double.parseDouble': - case 'Double.valueOf': { - if (isNum(a[0])) return dbl(x) - const s = String(a[0]).trim() - if (!/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?[dDfF]?$/.test(s)) this.fail('NumberFormatException', `For input string: "${s}"`) - return dbl(parseFloat(s)) - } - case 'Boolean.parseBoolean': - return String(a[0]).toLowerCase() === 'true' - case 'Integer.toString': - case 'Double.toString': - case 'String.valueOf': - case 'Objects.toString': - if (a[0] instanceof JArray && a[0].elem.name === 'char') return a[0].items.map((c) => String.fromCharCode(num(c))).join('') - return yield* this.str(a[0]) - case 'Integer.compare': - case 'Double.compare': - return Math.sign(x - num(a[1])) - case 'Integer.toBinaryString': - return (x >>> 0).toString(2) - case 'Integer.toHexString': - return (x >>> 0).toString(16) - case 'Integer.sum': - return (x + num(a[1])) | 0 - case 'Objects.equals': - return a[0] === null ? a[1] === null : yield* this.equalsVal(a[0], a[1]) - case 'String.format': - return yield* this.format(String(a[0]), a.slice(1)) - case 'String.join': { - const parts: string[] = [] - const items = a[1] instanceof JList || a[1] instanceof JArray ? a[1].items : a.slice(1) - for (const p of items) parts.push(yield* this.str(p)) - return parts.join(String(a[0])) - } - case 'Character.isDigit': - return /\p{Nd}/u.test(String.fromCharCode(x)) - case 'Character.isLetter': - return /\p{L}/u.test(String.fromCharCode(x)) - case 'Character.isLetterOrDigit': - return /[\p{L}\p{Nd}]/u.test(String.fromCharCode(x)) - case 'Character.isUpperCase': - return /\p{Lu}/u.test(String.fromCharCode(x)) - case 'Character.isLowerCase': - return /\p{Ll}/u.test(String.fromCharCode(x)) - case 'Character.isWhitespace': - return /\s/.test(String.fromCharCode(x)) - case 'Character.toUpperCase': - return new JChar(String.fromCharCode(x).toUpperCase().charCodeAt(0)) - case 'Character.toLowerCase': - return new JChar(String.fromCharCode(x).toLowerCase().charCodeAt(0)) - case 'Character.getNumericValue': - return parseInt(String.fromCharCode(x), 36) - case 'Arrays.toString': { - if (a[0] === null) return 'null' - const arr = a[0] as JArray - const parts: string[] = [] - for (const v of arr.items) parts.push(yield* this.str(v)) - return `[${parts.join(', ')}]` - } - case 'Arrays.sort': - case 'Collections.sort': { - const arr = a[0] as JArray | JList - arr.items = yield* this.sorted(arr.items) - return undefined - } - case 'Arrays.fill': - ;(a[0] as JArray).items.fill(castTo(a[1], (a[0] as JArray).elem)) - return undefined - case 'Arrays.asList': - case 'List.of': { - const l = new JList(this.id()) - l.items = a.length === 1 && a[0] instanceof JArray ? [...a[0].items] : a - return l - } - case 'Collections.reverse': - ;(a[0] as JList).items.reverse() - return undefined - case 'Collections.shuffle': - ;(a[0] as JList).items.sort(() => Math.random() - 0.5) - return undefined - case 'Collections.max': - case 'Collections.min': { - const items = (a[0] as JList).items - if (!items.length) this.fail('NoSuchElementException') - const s = yield* this.sorted(items) - return name === 'max' ? s[s.length - 1] : s[0] - } - case 'Thread.sleep': - yield { k: 'sleep', ms: x } - return undefined - case 'System.currentTimeMillis': - return Date.now() - case 'System.nanoTime': - return Math.round(performance.now() * 1e6) - case 'System.exit': - throw new ProgramExit() - } - throw new MiniJavaError('unsupported', `${cls}.${name}()`, pos) - } -} - -/** `System.exit` beendet das Programm still. */ -class ProgramExit extends Error {} - -function isMain(m: MethodDecl) { - return m.name === 'main' && m.isStatic && m.params.length === 1 && m.params[0].type.name === 'String' && m.params[0].type.dims === 1 -} - -/** `System.exit`: der Lauf endet wie ein normales Ende. */ -export function isProgramExit(err: unknown) { - return err instanceof ProgramExit -} diff --git a/web/src/core/miniJavaParser.ts b/web/src/core/miniJavaParser.ts deleted file mode 100644 index f7d61bd3..00000000 --- a/web/src/core/miniJavaParser.ts +++ /dev/null @@ -1,798 +0,0 @@ -/** - * VisuObject-Debugger: Parser für das Java, das in der FOS 12 vorkommt (Klassen, Objekte, Vererbung, - * Arrays, ArrayList, Schleifen, Exceptions). Kein vollständiges Java: Generics werden gelesen und - * übergangen, Lambdas, innere Klassen und Annotationen außer `@Override` gibt es nicht. - * Jeder Knoten kennt seine Datei und Zeile (ab 0), damit der Debugger die Zeile gelb markieren kann. - */ - -import { similarType } from './typeTypos' - -export interface Pos { - file: string - line: number -} - -export type TypeRef = { name: string; dims: number; args: TypeRef[] } - -export type Expr = ( - | { k: 'lit'; value: { t: 'int' | 'double' | 'char' | 'string' | 'bool' | 'null'; v: number | string | boolean | null } } - | { k: 'name'; name: string } - | { k: 'this' } - | { k: 'super' } - | { k: 'field'; target: Expr; name: string } - | { k: 'call'; target?: Expr; name: string; args: Expr[]; isSuper?: boolean } - | { k: 'new'; type: TypeRef; args: Expr[] } - | { k: 'newArray'; type: TypeRef; sizes: Expr[]; init?: Expr } - | { k: 'arrayInit'; items: Expr[] } - | { k: 'index'; target: Expr; index: Expr } - | { k: 'unary'; op: string; expr: Expr } - | { k: 'incdec'; op: '++' | '--'; prefix: boolean; target: Expr } - | { k: 'binary'; op: string; left: Expr; right: Expr } - | { k: 'assign'; op: string; target: Expr; value: Expr } - | { k: 'cond'; test: Expr; then: Expr; else: Expr } - | { k: 'cast'; type: TypeRef; expr: Expr } - | { k: 'instanceof'; expr: Expr; type: TypeRef } -) & - Pos - -export type Stmt = ( - | { k: 'block'; body: Stmt[] } - | { k: 'local'; type: TypeRef; vars: { name: string; dims: number; init?: Expr }[] } - | { k: 'expr'; expr: Expr } - | { k: 'if'; test: Expr; then: Stmt; else?: Stmt } - | { k: 'while'; test: Expr; body: Stmt } - | { k: 'do'; body: Stmt; test: Expr } - | { k: 'for'; init: Stmt[]; test?: Expr; update: Expr[]; body: Stmt } - | { k: 'foreach'; type: TypeRef; name: string; iter: Expr; body: Stmt } - | { k: 'switch'; expr: Expr; cases: { labels: Expr[] | 'default'; body: Stmt[]; arrow: boolean }[] } - | { k: 'break' } - | { k: 'continue' } - | { k: 'return'; value?: Expr } - | { k: 'throw'; value: Expr } - | { k: 'try'; body: Stmt; catches: { types: TypeRef[]; name: string; body: Stmt }[]; finally?: Stmt } - | { k: 'ctorCall'; isSuper: boolean; args: Expr[] } - | { k: 'empty' } -) & - Pos - -export interface FieldDecl extends Pos { - name: string - type: TypeRef - isStatic: boolean - init?: Expr -} - -export interface MethodDecl extends Pos { - name: string - /** Leer beim Konstruktor. */ - returns?: TypeRef - params: { name: string; type: TypeRef }[] - isStatic: boolean - isAbstract: boolean - isConstructor: boolean - body?: Stmt & { k: 'block' } - /** Zeile der schließenden Klammer (für den letzten Schritt einer Methode). */ - endLine: number -} - -export interface ClassDecl extends Pos { - name: string - kind: 'class' | 'interface' | 'enum' - superclass?: string - interfaces: string[] - isAbstract: boolean - fields: FieldDecl[] - methods: MethodDecl[] - enumConstants: string[] -} - -export class JavaSyntaxError extends Error { - constructor( - message: string, - readonly pos: Pos, - ) { - super(message) - } -} - -// MARK: - Zerlegen in Wörter - -interface Tok { - t: 'id' | 'num' | 'str' | 'char' | 'op' | 'eof' - v: string - line: number -} - -const ops = ['>>>=', '<<=', '>>=', '>>>', '...', '->', '::', '++', '--', '&&', '||', '==', '!=', '<=', '>=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<'] - -function tokenize(src: string, file: string): Tok[] { - const out: Tok[] = [] - let i = 0 - let line = 0 - while (i < src.length) { - const c = src[i] - if (c === '\n') { - line++ - i++ - } else if (/\s/.test(c)) i++ - else if (src.startsWith('//', i)) { - while (i < src.length && src[i] !== '\n') i++ - } else if (src.startsWith('/*', i)) { - const end = src.indexOf('*/', i + 2) - const stop = end < 0 ? src.length : end + 2 - for (let j = i; j < stop; j++) if (src[j] === '\n') line++ - i = stop - } else if (/[\p{L}_$]/u.test(c)) { - let j = i + 1 - while (j < src.length && /[\p{L}\p{N}_$]/u.test(src[j])) j++ - out.push({ t: 'id', v: src.slice(i, j), line }) - i = j - } else if (/[0-9]/.test(c) || (c === '.' && /[0-9]/.test(src[i + 1] ?? ''))) { - const m = /^(0[xX][0-9a-fA-F_]+[lL]?|[0-9][0-9_]*(\.[0-9_]*)?([eE][+-]?[0-9]+)?[fFdDlL]?|\.[0-9]+([eE][+-]?[0-9]+)?[fFdD]?)/.exec(src.slice(i))! - out.push({ t: 'num', v: m[0], line }) - i += m[0].length - } else if (c === '"' || c === "'") { - let j = i + 1 - let text = '' - while (j < src.length && src[j] !== c) { - if (src[j] === '\n') throw new JavaSyntaxError(c === '"' ? 'string' : 'char', { file, line }) - if (src[j] === '\\') { - const n = src[j + 1] - text += n === 'n' ? '\n' : n === 't' ? '\t' : n === 'r' ? '\r' : n === '0' ? '\0' : n === 'u' ? String.fromCharCode(parseInt(src.slice(j + 2, j + 6), 16)) : n - j += n === 'u' ? 6 : 2 - } else text += src[j++] - } - if (j >= src.length) throw new JavaSyntaxError(c === '"' ? 'string' : 'char', { file, line }) - out.push({ t: c === '"' ? 'str' : 'char', v: text, line }) - i = j + 1 - } else { - const op = ops.find((o) => src.startsWith(o, i)) ?? c - out.push({ t: 'op', v: op, line }) - i += op.length - } - } - out.push({ t: 'eof', v: '', line }) - return out -} - -// MARK: - Parser - -const modifiers = new Set(['public', 'private', 'protected', 'static', 'final', 'abstract', 'synchronized', 'transient', 'volatile', 'native', 'strictfp', 'default']) -const primitives = new Set(['int', 'long', 'short', 'byte', 'double', 'float', 'boolean', 'char', 'void', 'var']) - -class Parser { - private i = 0 - constructor( - private toks: Tok[], - private file: string, - ) {} - - private get tok() { - return this.toks[this.i] - } - private peek(n = 1) { - return this.toks[Math.min(this.i + n, this.toks.length - 1)] - } - private pos(tok = this.tok): Pos { - return { file: this.file, line: tok.line } - } - private is(v: string, tok = this.tok) { - return (tok.t === 'op' || tok.t === 'id') && tok.v === v - } - private eat(v: string) { - if (this.is(v)) { - this.i++ - return true - } - return false - } - private expect(v: string) { - if (!this.eat(v)) this.fail(`expected:${v}`) - } - private fail(what: string): never { - throw new JavaSyntaxError(what, this.pos()) - } - private ident(): string { - if (this.tok.t !== 'id') this.fail('identifier') - return this.toks[this.i++].v - } - - unit(): ClassDecl[] { - const out: ClassDecl[] = [] - if (this.eat('package')) this.skipTo(';') - while (this.is('import')) this.skipTo(';') - while (this.tok.t !== 'eof') { - if (this.eat(';')) continue - out.push(this.classDecl()) - } - return out - } - - private skipTo(v: string) { - while (this.tok.t !== 'eof' && !this.is(v)) this.i++ - this.expect(v) - } - - private mods(): Set { - const out = new Set() - for (;;) { - if (this.tok.t === 'op' && this.tok.v === '@') { - this.i++ - this.ident() - if (this.is('(')) this.skipParens() - } else if (this.tok.t === 'id' && modifiers.has(this.tok.v) && !(this.tok.v === 'default' && this.is(':', this.peek()))) out.add(this.toks[this.i++].v) - else return out - } - } - - private skipParens() { - let depth = 0 - do { - if (this.is('(')) depth++ - else if (this.is(')')) depth-- - this.i++ - } while (depth > 0 && this.tok.t !== 'eof') - } - - private classDecl(): ClassDecl { - const mods = this.mods() - const pos = this.pos() - const kind = this.eat('class') ? 'class' : this.eat('interface') ? 'interface' : this.eat('enum') ? 'enum' : this.fail('class') - const name = this.ident() - if (this.is('<')) this.typeArgs() - const decl: ClassDecl = { ...pos, name, kind, interfaces: [], isAbstract: mods.has('abstract') || kind === 'interface', fields: [], methods: [], enumConstants: [] } - if (this.eat('extends')) { - const list = [this.type().name] - while (this.eat(',')) list.push(this.type().name) - if (kind === 'interface') decl.interfaces = list - else decl.superclass = list[0] - } - if (this.eat('implements')) { - decl.interfaces.push(this.type().name) - while (this.eat(',')) decl.interfaces.push(this.type().name) - } - this.expect('{') - if (kind === 'enum') { - while (this.tok.t === 'id') { - decl.enumConstants.push(this.ident()) - if (this.is('(')) this.skipParens() - if (!this.eat(',')) break - } - this.eat(';') - } - while (!this.eat('}')) { - if (this.tok.t === 'eof') this.fail('expected:}') - if (this.eat(';')) continue - this.member(decl) - } - return decl - } - - private member(cls: ClassDecl) { - const start = this.tok - const mods = this.mods() - if (this.is('class') || this.is('interface') || this.is('enum')) this.fail('innerClass') - if (this.is('{')) { - // Initialisierungsblock: wie ein Konstruktor-Vorspann nicht unterstützt. - this.fail('initializer') - } - if (this.is('<')) this.typeArgs() - const pos = this.pos(start) - // Konstruktor: Name der Klasse direkt vor `(`. - if (this.tok.t === 'id' && this.tok.v === cls.name && this.is('(', this.peek())) { - this.i++ - const params = this.params() - if (this.eat('throws')) this.skipThrows() - const body = this.block() - cls.methods.push({ ...pos, name: cls.name, params, isStatic: false, isAbstract: false, isConstructor: true, body, endLine: this.toks[this.i - 1].line }) - return - } - const type = this.type() - const name = this.ident() - if (this.is('(')) { - const params = this.params() - while (this.eat('[')) this.expect(']') - if (this.eat('throws')) this.skipThrows() - const isAbstract = mods.has('abstract') || (cls.kind === 'interface' && !this.is('{')) - const body = this.is('{') ? this.block() : (this.expect(';'), undefined) - cls.methods.push({ ...pos, name, returns: type, params, isStatic: mods.has('static'), isAbstract, isConstructor: false, body, endLine: this.toks[this.i - 1].line }) - return - } - // Attribute, auch `int a, b = 2;` - let first = true - let varName = name - for (;;) { - if (!first) varName = this.ident() - first = false - let dims = 0 - while (this.eat('[')) { - this.expect(']') - dims++ - } - const t = dims ? { ...type, dims: type.dims + dims } : type - const init = this.eat('=') ? (this.is('{') ? this.arrayInit() : this.expr()) : undefined - cls.fields.push({ ...pos, name: varName, type: t, isStatic: mods.has('static') || cls.kind === 'interface', init }) - if (!this.eat(',')) break - } - this.expect(';') - } - - private skipThrows() { - this.type() - while (this.eat(',')) this.type() - } - - private params() { - this.expect('(') - const out: { name: string; type: TypeRef }[] = [] - if (!this.eat(')')) { - do { - this.mods() - let type = this.type() - if (this.eat('...')) type = { ...type, dims: type.dims + 1 } - const name = this.ident() - while (this.eat('[')) { - this.expect(']') - type = { ...type, dims: type.dims + 1 } - } - out.push({ name, type }) - } while (this.eat(',')) - this.expect(')') - } - return out - } - - private typeArgs(): TypeRef[] { - this.expect('<') - const out: TypeRef[] = [] - if (this.eat('>')) return out - do { - if (this.eat('?')) { - if (this.eat('extends') || this.eat('super')) out.push(this.type()) - else out.push({ name: 'Object', dims: 0, args: [] }) - } else out.push(this.type()) - } while (this.eat(',')) - // `>>` am Ende verschachtelter Generics: das zweite `>` bleibt stehen. - if (this.is('>>')) this.toks[this.i] = { ...this.tok, v: '>' } - else if (this.is('>>>')) this.toks[this.i] = { ...this.tok, v: '>>' } - else this.expect('>') - return out - } - - type(): TypeRef { - let name = this.ident() - let args: TypeRef[] = [] - if (this.is('<')) args = this.typeArgs() - while (this.is('.') && this.peek().t === 'id') { - this.i++ - name = this.ident() - if (this.is('<')) args = this.typeArgs() - } - let dims = 0 - while (this.is('[') && this.is(']', this.peek())) { - this.i += 2 - dims++ - } - return { name, dims, args } - } - - /** Steht hier eine Deklaration `Typ name`? (ohne etwas zu verbrauchen) */ - private looksLikeDecl(): boolean { - const save = this.i - try { - this.mods() - if (this.tok.t !== 'id') return false - const first = this.tok.v - if (['return', 'new', 'throw', 'this', 'super', 'true', 'false', 'null'].includes(first)) return false - this.type() - return this.tok.t === 'id' && (this.is('=', this.peek()) || this.is(';', this.peek()) || this.is(',', this.peek()) || this.is(':', this.peek()) || this.is('[', this.peek())) - } catch { - return false - } finally { - this.i = save - } - } - - block(): Stmt & { k: 'block' } { - const pos = this.pos() - this.expect('{') - const body: Stmt[] = [] - while (!this.eat('}')) { - if (this.tok.t === 'eof') this.fail('expected:}') - body.push(this.stmt()) - } - return { ...pos, k: 'block', body } - } - - private localDecl(): Stmt { - const pos = this.pos() - this.mods() - const type = this.type() - const vars: { name: string; dims: number; init?: Expr }[] = [] - do { - const name = this.ident() - let dims = 0 - while (this.eat('[')) { - this.expect(']') - dims++ - } - const init = this.eat('=') ? (this.is('{') ? this.arrayInit() : this.expr()) : undefined - vars.push({ name, dims, init }) - } while (this.eat(',')) - return { ...pos, k: 'local', type, vars } - } - - stmt(): Stmt { - const pos = this.pos() - const v = this.tok.t === 'id' || this.tok.t === 'op' ? this.tok.v : '' - if (v === '{') return this.block() - if (this.eat(';')) return { ...pos, k: 'empty' } - if (this.eat('if')) { - this.expect('(') - const test = this.expr() - this.expect(')') - const then = this.stmt() - return { ...pos, k: 'if', test, then, else: this.eat('else') ? this.stmt() : undefined } - } - if (this.eat('while')) { - this.expect('(') - const test = this.expr() - this.expect(')') - return { ...pos, k: 'while', test, body: this.stmt() } - } - if (this.eat('do')) { - const body = this.stmt() - this.expect('while') - this.expect('(') - const test = this.expr() - this.expect(')') - this.expect(';') - return { ...pos, k: 'do', body, test } - } - if (this.eat('for')) { - this.expect('(') - // for (Typ x : liste) - const save = this.i - try { - this.mods() - const type = this.type() - const name = this.ident() - if (this.eat(':')) { - const iter = this.expr() - this.expect(')') - return { ...pos, k: 'foreach', type, name, iter, body: this.stmt() } - } - } catch { - /* keine for-each-Schleife */ - } - this.i = save - const init: Stmt[] = [] - if (!this.is(';')) { - if (this.looksLikeDecl()) init.push(this.localDecl()) - else - do { - const p = this.pos() - init.push({ ...p, k: 'expr', expr: this.expr() }) - } while (this.eat(',')) - } - this.expect(';') - const test = this.is(';') ? undefined : this.expr() - this.expect(';') - const update: Expr[] = [] - if (!this.is(')')) - do update.push(this.expr()) - while (this.eat(',')) - this.expect(')') - return { ...pos, k: 'for', init, test, update, body: this.stmt() } - } - if (this.eat('switch')) { - this.expect('(') - const expr = this.expr() - this.expect(')') - this.expect('{') - const cases: { labels: Expr[] | 'default'; body: Stmt[]; arrow: boolean }[] = [] - while (!this.eat('}')) { - let labels: Expr[] | 'default' - if (this.eat('default')) labels = 'default' - else { - this.expect('case') - labels = [this.expr()] - while (this.eat(',')) labels.push(this.expr()) - } - if (this.eat('->')) { - const body = this.is('{') ? [this.block()] : [this.stmt()] - cases.push({ labels, body, arrow: true }) - continue - } - this.expect(':') - const body: Stmt[] = [] - while (!this.is('case') && !this.is('default') && !this.is('}')) body.push(this.stmt()) - cases.push({ labels, body, arrow: false }) - } - return { ...pos, k: 'switch', expr, cases } - } - if (this.eat('break')) { - if (this.tok.t === 'id') this.i++ - this.expect(';') - return { ...pos, k: 'break' } - } - if (this.eat('continue')) { - if (this.tok.t === 'id') this.i++ - this.expect(';') - return { ...pos, k: 'continue' } - } - if (this.eat('return')) { - const value = this.is(';') ? undefined : this.expr() - this.expect(';') - return { ...pos, k: 'return', value } - } - if (this.eat('throw')) { - const value = this.expr() - this.expect(';') - return { ...pos, k: 'throw', value } - } - if (this.eat('try')) { - if (this.is('(')) this.fail('tryResources') - const body = this.block() - const catches: { types: TypeRef[]; name: string; body: Stmt }[] = [] - while (this.eat('catch')) { - this.expect('(') - this.mods() - const types = [this.type()] - while (this.eat('|')) types.push(this.type()) - const name = this.ident() - this.expect(')') - catches.push({ types, name, body: this.block() }) - } - const fin = this.eat('finally') ? this.block() : undefined - return { ...pos, k: 'try', body, catches, finally: fin } - } - if ((v === 'this' || v === 'super') && this.is('(', this.peek())) { - this.i++ - const args = this.args() - this.expect(';') - return { ...pos, k: 'ctorCall', isSuper: v === 'super', args } - } - if (v === 'class' || v === 'interface') this.fail('innerClass') - if (this.looksLikeDecl()) { - const decl = this.localDecl() - this.expect(';') - return decl - } - const expr = this.expr() - this.expect(';') - return { ...pos, k: 'expr', expr } - } - - private args(): Expr[] { - this.expect('(') - const out: Expr[] = [] - if (!this.eat(')')) { - do out.push(this.expr()) - while (this.eat(',')) - this.expect(')') - } - return out - } - - private arrayInit(): Expr { - const pos = this.pos() - this.expect('{') - const items: Expr[] = [] - while (!this.eat('}')) { - items.push(this.is('{') ? this.arrayInit() : this.expr()) - if (!this.eat(',')) { - this.expect('}') - break - } - } - return { ...pos, k: 'arrayInit', items } - } - - expr(): Expr { - return this.assign() - } - - private assign(): Expr { - const left = this.cond() - const op = this.tok.v - if (this.tok.t === 'op' && ['=', '+=', '-=', '*=', '/=', '%=', '&=', '|=', '^=', '<<=', '>>=', '>>>='].includes(op)) { - const pos = this.pos() - this.i++ - if (left.k !== 'name' && left.k !== 'field' && left.k !== 'index') this.fail('assignTarget') - return { ...pos, k: 'assign', op, target: left, value: this.assign() } - } - return left - } - - private cond(): Expr { - const test = this.binary(0) - if (this.is('?')) { - const pos = this.pos() - this.i++ - const then = this.assign() - this.expect(':') - return { ...pos, k: 'cond', test, then, else: this.assign() } - } - return test - } - - private static levels: string[][] = [['||'], ['&&'], ['|'], ['^'], ['&'], ['==', '!='], ['<', '>', '<=', '>=', 'instanceof'], ['<<', '>>', '>>>'], ['+', '-'], ['*', '/', '%']] - - private binary(level: number): Expr { - if (level >= Parser.levels.length) return this.unary() - let left = this.binary(level + 1) - for (;;) { - const op = this.tok.v - if (!(this.tok.t === 'op' || op === 'instanceof') || !Parser.levels[level].includes(op)) return left - const pos = this.pos() - this.i++ - if (op === 'instanceof') { - left = { ...pos, k: 'instanceof', expr: left, type: this.type() } - if (this.tok.t === 'id') this.i++ // Pattern `instanceof Hund h` wird nicht gebunden - continue - } - left = { ...pos, k: 'binary', op, left, right: this.binary(level + 1) } - } - } - - private unary(): Expr { - const pos = this.pos() - if (this.is('++') || this.is('--')) { - const op = this.toks[this.i++].v as '++' | '--' - return { ...pos, k: 'incdec', op, prefix: true, target: this.unary() } - } - if (this.is('!') || this.is('-') || this.is('+') || this.is('~')) { - const op = this.toks[this.i++].v - return { ...pos, k: 'unary', op, expr: this.unary() } - } - // Cast: `(int) x`, `(Hund) tier` - if (this.is('(') && this.peek().t === 'id') { - const save = this.i - this.i++ - try { - const type = this.type() - if (this.eat(')')) { - const primitive = primitives.has(type.name) - const next = this.tok - const castable = primitive - ? !(next.t === 'op' && ['+', '-', '*', '/', ')', ';', ',', '.', '==', '<', '>'].includes(next.v) && !['(', '-', '+', '!', '~'].includes(next.v)) || - next.t !== 'op' || - ['(', '-', '!', '~'].includes(next.v) - : next.t !== 'op' || next.v === '(' - if (castable && (primitive || /^[A-Z]/.test(type.name))) return { ...pos, k: 'cast', type, expr: this.unary() } - } - } catch { - /* kein Cast */ - } - this.i = save - } - return this.postfix(this.primary()) - } - - private postfix(e: Expr): Expr { - for (;;) { - const pos = this.pos() - if (this.eat('.')) { - if (this.eat('new')) this.fail('innerClass') - const name = this.ident() - if (this.is('(')) e = { ...pos, k: 'call', target: e, name, args: this.args(), isSuper: e.k === 'super' } - else e = { ...pos, k: 'field', target: e, name } - } else if (this.is('[')) { - this.i++ - const index = this.expr() - this.expect(']') - e = { ...pos, k: 'index', target: e, index } - } else if (this.is('++') || this.is('--')) { - const op = this.toks[this.i++].v as '++' | '--' - e = { ...pos, k: 'incdec', op, prefix: false, target: e } - } else if (this.is('::') || this.is('->')) this.fail('lambda') - else return e - } - } - - private primary(): Expr { - const tok = this.tok - const pos = this.pos() - if (tok.t === 'num') { - this.i++ - const text = tok.v.replace(/_/g, '') - if (/^0[xX]/.test(text)) return { ...pos, k: 'lit', value: { t: 'int', v: parseInt(text.replace(/[lL]$/, ''), 16) } } - if (/[.eEfFdD]/.test(text) && !/^0[xX]/.test(text)) return { ...pos, k: 'lit', value: { t: 'double', v: parseFloat(text.replace(/[fFdD]$/, '')) } } - return { ...pos, k: 'lit', value: { t: 'int', v: parseInt(text.replace(/[lL]$/, ''), 10) } } - } - if (tok.t === 'str') { - this.i++ - return { ...pos, k: 'lit', value: { t: 'string', v: tok.v } } - } - if (tok.t === 'char') { - this.i++ - return { ...pos, k: 'lit', value: { t: 'char', v: tok.v.charCodeAt(0) || 0 } } - } - if (this.eat('(')) { - const e = this.expr() - this.expect(')') - return e - } - if (tok.t !== 'id') this.fail('expression') - if (this.eat('true')) return { ...pos, k: 'lit', value: { t: 'bool', v: true } } - if (this.eat('false')) return { ...pos, k: 'lit', value: { t: 'bool', v: false } } - if (this.eat('null')) return { ...pos, k: 'lit', value: { t: 'null', v: null } } - if (this.eat('this')) return { ...pos, k: 'this' } - if (this.eat('super')) return { ...pos, k: 'super' } - if (this.eat('new')) { - const name = this.ident() - let args: TypeRef[] = [] - if (this.is('<')) args = this.typeArgs() - const type: TypeRef = { name, dims: 0, args } - if (this.is('[')) { - const sizes: Expr[] = [] - let dims = 0 - while (this.eat('[')) { - if (this.eat(']')) { - dims++ - continue - } - sizes.push(this.expr()) - this.expect(']') - dims++ - } - const init = this.is('{') ? this.arrayInit() : undefined - return { ...pos, k: 'newArray', type: { ...type, dims }, sizes, init } - } - const a = this.args() - if (this.is('{')) this.fail('anonymousClass') - return { ...pos, k: 'new', type, args: a } - } - const name = this.ident() - if (this.is('(')) return { ...pos, k: 'call', name, args: this.args() } - return { ...pos, k: 'name', name } - } -} - -const javaUtil = ['Scanner', 'Random', 'ArrayList', 'List', 'LinkedList', 'HashMap', 'Map', 'TreeMap', 'LinkedHashMap', 'Collection', 'Collections', 'Arrays', 'Iterator', 'Objects'] - -/** - * Wie javac: `Scanner`, `ArrayList` usw. ohne `import java.util.Scanner;` (oder `java.util.*`) sind unbekannt. - * `own`: Klassen aus dem Projekt, die so heißen dürfen. Ergebnis: die fehlende Zeile und wo die Klasse zuerst vorkommt. - */ -export function missingImport(source: string, file: string, own: Set): { line: string; pos: Pos } | undefined { - const toks = tokenize(source, file) - const imports: string[] = [] - for (let k = 0; k < toks.length; k++) { - if (toks[k].v !== 'import' || toks[k].t !== 'id') continue - let end = k + 1 - while (end < toks.length && toks[end].v !== ';' && toks[end].t !== 'eof') end++ - imports.push(toks.slice(k + 1, end).map((t) => t.v).join('')) - k = end - } - for (let k = 0; k < toks.length; k++) { - const t = toks[k] - if (t.t !== 'id' || !javaUtil.includes(t.v) || own.has(t.v) || toks[k - 1]?.v === '.' || toks[k - 1]?.v === 'import') continue - if (imports.includes(`java.util.${t.v}`) || imports.includes('java.util.*')) continue - return { line: `import java.util.${t.v};`, pos: { file, line: t.line } } - } - return undefined -} - -/** - * Wie javac: `booelan fertig;` oder `Int x = 3;` ist kein Datentyp. Nur ein Wort am Anfang einer Anweisung, auf das ein Name - * und `=`, `;` oder `,` folgen, und nur wenn es einem Grundtyp sehr ähnlich ist. Ergebnis: die richtige Schreibweise und die Stelle. - */ -export function misspelledType(source: string, file: string, own: Set): { type: string; pos: Pos } | undefined { - const toks = tokenize(source, file) - const starts = new Set([';', '{', '}', '(', ',', 'public', 'private', 'protected', 'static', 'final']) - for (let k = 0; k < toks.length - 2; k++) { - const [type, name, after] = toks.slice(k, k + 3) - if (type.t !== 'id' || name.t !== 'id' || !['=', ';', ','].includes(after.v) || own.has(type.v)) continue - if (k > 0 && !starts.has(toks[k - 1].v)) continue - const suggestion = similarType(type.v) - if (suggestion) return { type: suggestion, pos: { file, line: type.line } } - } - return undefined -} - -/** Eine Datei lesen; Fehler als `JavaSyntaxError` mit Zeile. */ -export function parseJava(source: string, file: string): ClassDecl[] { - return new Parser(tokenize(source, file), file).unit() -} diff --git a/web/src/core/model.ts b/web/src/core/model.ts deleted file mode 100644 index f10a0a87..00000000 --- a/web/src/core/model.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** Abbild von `VisuStructElement.swift` / Java-Typnummern in `Struktogramm`. */ -export enum ElementType { - anweisung = 0, - verzweigung = 1, - fallauswahl = 2, - forSchleife = 3, - whileSchleife = 4, - doUntilSchleife = 5, - endlosschleife = 6, - aussprung = 7, - aufruf = 8, - leerElement = 9, -} - -export interface RGBA { - r: number - g: number - b: number - a: number -} - -/** Ein `` inkl. Unterstrukturen (Schleifenrumpf bzw. Fälle). */ -export interface VSElement { - type: ElementType - /** Java `xVergroesserung`/`yVergroesserung` (`zx`/`zy`). */ - zoomX: number - zoomY: number - textColor?: RGBA - backgroundColor?: RGBA - /** ``-Kinder, jede Zeile einzeln. */ - textLines: string[] - /** Nur bei Schleifen gesetzt. */ - loopBody?: VSElement[] - /** Nur bei Verzweigung/Fallauswahl gesetzt. Letzter Fall ist i. d. R. „default“. */ - cases?: VSCase[] -} - -export interface VSCase { - name: string - elements: VSElement[] -} - -/** Root-Tag ``. */ -export interface VSDocument { - fontFamily?: string - fontSize?: number - fontStyle?: number - caption: string - /** Als Pseudocode markiert: Der erzeugte Code ist nur eine Skizze, kein lauffähiges Programm. */ - pseudocode?: boolean - elements: VSElement[] -} - -export function isLoop(t: ElementType): boolean { - return ( - t === ElementType.forSchleife || - t === ElementType.whileSchleife || - t === ElementType.doUntilSchleife || - t === ElementType.endlosschleife - ) -} - -export function isMultiway(t: ElementType): boolean { - return t === ElementType.verzweigung || t === ElementType.fallauswahl -} - -export function makeElement(type: ElementType, init: Partial = {}): VSElement { - return { type, zoomX: 0, zoomY: 0, textLines: [''], ...init } -} diff --git a/web/src/core/objectModel.ts b/web/src/core/objectModel.ts deleted file mode 100644 index c712ec46..00000000 --- a/web/src/core/objectModel.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * VisuObject: Objektdiagramm. Ein Objekt hat die Attribute seiner Klasse (samt Oberklassen, ohne static); - * ein Referenzattribut zeigt mit dem Namen eines anderen Objekts auf dieses (`team = t1`, `spieler = [s1, s2]`). - */ - -import { associationTarget, type UmlClass } from './classModel' - -export interface ObjectField { - name: string - type: string - /** Klasse, auf die ein Referenzattribut zeigt. */ - target?: string - /** Liste oder Array von Referenzen. */ - many: boolean -} - -/** Attribute eines Objekts der Klasse `cls`, Oberklassen zuerst (wie in Java im Speicher). */ -export function objectFields(classes: UmlClass[], cls: string): ObjectField[] { - const byName = new Map(classes.map((c) => [c.name, c])) - const chain: UmlClass[] = [] - const seen = new Set() - for (let c = byName.get(cls); c && !seen.has(c.name); c = c.superclass ? byName.get(c.superclass) : undefined) { - seen.add(c.name) - chain.unshift(c) - } - const out: ObjectField[] = [] - for (const c of chain) - for (const m of c.members) { - if (m.kind !== 'field' || m.isStatic || out.some((f) => f.name === m.name)) continue - const t = associationTarget(m.type) - out.push({ name: m.name, type: m.type, many: t.many, ...(byName.has(t.name) ? { target: t.name } : {}) }) - } - return out -} - -/** Startwert wie in Java, wenn ein Objekt neu entsteht. */ -export function javaDefault(type: string): string { - if (/^(int|long|short|byte)$/.test(type)) return '0' - if (/^(double|float)$/.test(type)) return '0.0' - if (type === 'boolean') return 'false' - if (type === 'char') return "''" - return 'null' -} - -/** Namen der Objekte, auf die ein Wert zeigt: `t1` oder `[s1, s2]`; `null` und leer = keine. */ -export function refNames(value: string | undefined): string[] { - const v = (value ?? '').trim() - if (!v || v === 'null') return [] - const inner = /^\[(.*)\]$/s.exec(v)?.[1] ?? v - return inner - .split(',') - .map((s) => s.trim()) - .filter((s) => /^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(s) && s !== 'null') -} - -/** Neuer Wert nach dem Verbinden: Einzelreferenz ersetzt, Liste hängt an (ohne doppelt). */ -export function linkedValue(field: ObjectField, value: string | undefined, target: string): string { - if (!field.many) return target - const names = refNames(value) - return `[${(names.includes(target) ? names : [...names, target]).join(', ')}]` -} - -/** Ist `cls` die Klasse `target` oder erbt davon (bzw. implementiert es)? */ -export function isA(classes: UmlClass[], cls: string, target: string): boolean { - const byName = new Map(classes.map((c) => [c.name, c])) - const seen = new Set() - const walk = (name: string | undefined): boolean => { - if (!name || seen.has(name)) return false - if (name === target) return true - seen.add(name) - const c = byName.get(name) - return !!c && (walk(c.superclass) || c.interfaces.some(walk)) - } - return walk(cls) -} - -/** Freier Name wie in Java üblich: `spieler1`, `spieler2` … */ -export function freeObjectName(cls: string, taken: string[]): string { - const base = cls.charAt(0).toLowerCase() + cls.slice(1) - for (let n = 1; ; n++) if (!taken.includes(`${base}${n}`)) return `${base}${n}` -} diff --git a/web/src/core/ownTypes.ts b/web/src/core/ownTypes.ts deleted file mode 100644 index 4677a424..00000000 --- a/web/src/core/ownTypes.ts +++ /dev/null @@ -1,82 +0,0 @@ -/** - * VisuObject: Vorschläge nach dem Punkt für die eigenen Klassen des Projekts. - * `Spieler s1 = new Spieler(); s1.` → Methoden und Attribute von Spieler (samt Oberklassen), - * aber nur, was von der Stelle aus sichtbar ist: public überall, Package nur im selben Package, - * protected im selben Package oder in Unterklassen, private nur in der Klasse selbst. - */ - -import type { UmlMember } from './classModel' -import { membersOf, type JavaMember, type MemberLanguage, type OwnTypes } from './javaMembers' - -export interface TypeInfo { - name: string - pkg: string - superclass?: string - interfaces: string[] - members: UmlMember[] -} - -export interface Place { - /** Klasse, in der der Cursor steht. */ - cls?: string - pkg: string -} - -function toJavaMember(m: UmlMember): JavaMember { - return { - name: m.name, - kind: m.kind === 'field' ? 'field' : 'method', - params: m.params.length > 0, - returns: m.type, - description: m.kind === 'field' ? '' : m.params.map((p) => `${p.name}: ${p.type}`).join(', '), - } -} - -export function ownTypes(classes: TypeInfo[], here: Place, lang: MemberLanguage = 'de'): OwnTypes { - const byName = new Map(classes.map((c) => [c.name, c])) - const isSubclass = (sub: string | undefined, sup: string) => { - const seen = new Set() - for (let c = sub ? byName.get(sub) : undefined; c && !seen.has(c.name); c = c.superclass ? byName.get(c.superclass) : undefined) { - if (c.name === sup) return true - seen.add(c.name) - } - return false - } - /** `inherited`: Mitglied einer Oberklasse; private wird nicht vererbt (`h.name` geht auch in Tier nicht, wenn h ein Hund ist). */ - const visible = (m: UmlMember, owner: TypeInfo, inherited: boolean) => { - if (m.visibility === '+') return true - if (m.visibility === '-') return !inherited && here.cls === owner.name - if (owner.pkg === here.pkg) return true - return m.visibility === '#' && isSubclass(here.cls, owner.name) - } - /** Eigene Klasse, dann Oberklassen und Interfaces; eine Oberklasse wie JFrame aus der Liste der Standardklassen. */ - const collect = (type: string, isStatic: boolean): JavaMember[] | undefined => { - const start = byName.get(type) - if (!start) return undefined - const out: JavaMember[] = [] - const seen = new Set() - const keys = new Set() - const visit = (name: string, inherited: boolean) => { - if (seen.has(name)) return - seen.add(name) - const c = byName.get(name) - if (!c) { - if (!isStatic) for (const m of membersOf(name, lang)) if (!keys.has(m.name)) (keys.add(m.name), out.push(m)) - return - } - for (const m of c.members) { - if (m.kind === 'constructor' || m.isStatic !== isStatic || !visible(m, c, inherited)) continue - // Überschriebene Methoden nur einmal (die der Unterklasse). - const key = `${m.kind}:${m.name}:${m.params.map((p) => p.type).join(',')}` - if (keys.has(key)) continue - keys.add(key) - out.push(toJavaMember(m)) - } - if (c.superclass) visit(c.superclass, true) - for (const i of c.interfaces) visit(i, true) - } - visit(type, false) - return out - } - return { members: (type) => collect(type, false), statics: (type) => collect(type, true), thisType: here.cls } -} diff --git a/web/src/core/palette.ts b/web/src/core/palette.ts deleted file mode 100644 index 89fe8d68..00000000 --- a/web/src/core/palette.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { placeholderElement } from './editing' -import { ElementType, makeElement, type VSElement } from './model' - -/** Wie `ElementPalette.swift`: neue Elemente aus der Palette. */ -export type PaletteItem = - | 'anweisung' - | 'verzweigung' - | 'whileSchleife' - | 'doUntilSchleife' - | 'forZaehlSchleife' - | 'fallauswahl' - | 'endlosschleife' - | 'aussprung' - | 'aufruf' - -/** Beschriftungen aus `de.lproj/Localizable.strings` (`element_*`). */ -export const paletteItems: { item: PaletteItem; label: string }[] = [ - { item: 'anweisung', label: 'Anweisung' }, - { item: 'verzweigung', label: 'Verzweigung' }, - { item: 'fallauswahl', label: 'Fallauswahl' }, - { item: 'forZaehlSchleife', label: 'For-Schleife' }, - { item: 'whileSchleife', label: 'While-Schleife' }, - { item: 'doUntilSchleife', label: 'Do-while-Schleife' }, - { item: 'endlosschleife', label: 'Endlosschleife' }, - { item: 'aufruf', label: 'Aufruf' }, - { item: 'aussprung', label: 'Aussprung' }, -] - -export function paletteElement(item: PaletteItem): VSElement { - const stub = placeholderElement - switch (item) { - case 'anweisung': - return makeElement(ElementType.anweisung) - case 'verzweigung': - return makeElement(ElementType.verzweigung, { - cases: [ - { name: 'true', elements: [stub()] }, - { name: 'false', elements: [stub()] }, - ], - }) - case 'fallauswahl': - return makeElement(ElementType.fallauswahl, { - cases: [ - { name: '1', elements: [stub()] }, - { name: '2', elements: [stub()] }, - { name: 'default', elements: [stub()] }, - ], - }) - case 'forZaehlSchleife': - return makeElement(ElementType.forSchleife, { textLines: ['', '', ''], loopBody: [stub()] }) - case 'whileSchleife': - return makeElement(ElementType.whileSchleife, { loopBody: [stub()] }) - case 'doUntilSchleife': - return makeElement(ElementType.doUntilSchleife, { loopBody: [stub()] }) - case 'endlosschleife': - return makeElement(ElementType.endlosschleife, { loopBody: [stub()] }) - case 'aussprung': - return makeElement(ElementType.aussprung) - case 'aufruf': - return makeElement(ElementType.aufruf) - } -} diff --git a/web/src/core/rename.ts b/web/src/core/rename.ts deleted file mode 100644 index c9b0adc7..00000000 --- a/web/src/core/rename.ts +++ /dev/null @@ -1,172 +0,0 @@ -import type { VSDocument, VSElement } from './model' - -/** - * Umbenennen (Refactoring): eine Variable an allen Stellen umbenennen, im Java-Code und im Struktogramm. - * Genau dieselbe Schreibweise (`zahl` ≠ `Zahl`), nur ganze Wörter; Text in "…"/'…' und Kommentare bleiben. - */ - -export const javaKeywords = new Set( - ( - 'abstract assert boolean break byte case catch char class const continue default do double else enum extends final ' + - 'finally float for goto if implements import instanceof int interface long native new package private protected public ' + - 'return short static strictfp super switch synchronized this throw throws transient try void volatile while ' + - 'true false null var record yield' - ).split(' '), -) - -const wordChar = /[\p{L}\p{N}_$]/u -const wordStart = /[\p{L}_$]/u - -export interface Word { - from: number - to: number - name: string -} - -/** Alle Wörter im Code außerhalb von Zeichenketten, Zeichen und Kommentaren. */ -export function codeWords(text: string): Word[] { - const words: Word[] = [] - let i = 0 - while (i < text.length) { - const c = text[i] - if (c === '/' && text[i + 1] === '/') { - while (i < text.length && text[i] !== '\n') i++ - } else if (c === '/' && text[i + 1] === '*') { - const end = text.indexOf('*/', i + 2) - i = end < 0 ? text.length : end + 2 - } else if (c === '"' || c === "'") { - i++ - while (i < text.length && text[i] !== c && text[i] !== '\n') i += text[i] === '\\' ? 2 : 1 - i++ - } else if (wordStart.test(c)) { - const from = i - while (i < text.length && wordChar.test(text[i])) i++ - words.push({ from, to: i, name: text.slice(from, i) }) - } else if (/\d/.test(c)) { - // Zahlen wie 1e5 oder 10L gehören nicht zu einem Namen. - while (i < text.length && wordChar.test(text[i])) i++ - } else i++ - } - return words -} - -/** Stellen, an denen die Variable `name` vorkommt (nicht hinter einem Punkt: `feld.length` ist keine Variable `length`). */ -export function nameRanges(text: string, name: string): Word[] { - return codeWords(text).filter((w) => w.name === name && !afterDot(text, w.from)) -} - -function afterDot(text: string, from: number): boolean { - let i = from - 1 - while (i >= 0 && (text[i] === ' ' || text[i] === '\t')) i-- - return text[i] === '.' -} - -/** Wort unter dem Cursor (auch direkt dahinter), wenn es ein umbenennbarer Name ist. */ -export function nameAt(text: string, pos: number): Word | undefined { - const w = codeWords(text).find((w) => pos >= w.from && pos <= w.to) - if (!w || javaKeywords.has(w.name) || afterDot(text, w.from)) return undefined - return w -} - -/** Text mit umbenannter Variable und Anzahl der geänderten Stellen. */ -export function renameInText(text: string, from: string, to: string): { text: string; count: number } { - const ranges = nameRanges(text, from) - let out = '' - let last = 0 - for (const r of ranges) { - out += text.slice(last, r.from) + to - last = r.to - } - return { text: out + text.slice(last), count: ranges.length } -} - -/** Alle Texte des Struktogramms (Kopfzeile, Elemente, Fallnamen). */ -function documentTexts(doc: VSDocument): string[] { - const texts = [doc.caption] - const walk = (elements: VSElement[]) => { - for (const el of elements) { - texts.push(el.textLines.join('\n')) - if (el.loopBody) walk(el.loopBody) - for (const c of el.cases ?? []) { - texts.push(c.name) - walk(c.elements) - } - } - } - walk(doc.elements) - return texts -} - -export function renameInDocument(doc: VSDocument, from: string, to: string): { doc: VSDocument; count: number } { - let count = 0 - const rename = (s: string) => { - const r = renameInText(s, from, to) - count += r.count - return r.text - } - const walk = (elements: VSElement[]): VSElement[] => - elements.map((el) => ({ - ...el, - textLines: rename(el.textLines.join('\n')).split('\n'), - ...(el.loopBody ? { loopBody: walk(el.loopBody) } : {}), - ...(el.cases ? { cases: el.cases.map((c) => ({ name: rename(c.name), elements: walk(c.elements) })) } : {}), - })) - const next = { ...doc, caption: rename(doc.caption), elements: walk(doc.elements) } - return { doc: next, count } -} - -/** - * Namen, die wie Variablen aussehen: keine Schlüsselwörter, keine Methoden `name(`, keine Klassen - * (`Scanner sc`, `Math.max`) und nichts hinter einem Punkt. Reihenfolge wie im Text. - */ -export function variableNames(texts: string[]): string[] { - const names = new Set() - const skip = new Set() - const declared = new Set() - for (const text of texts) { - const words = codeWords(text).filter((w) => !afterDot(text, w.from)) - words.forEach((w, i) => { - // „ø“ = leere Anweisung im Struktogramm, kein Name. - if (javaKeywords.has(w.name) || w.name === 'ø') return - const rest = text.slice(w.to) - const next = words[i + 1] - const between = next ? text.slice(w.to, next.from) : '' - // `Scanner sc`, `String[] namen`: das erste Wort ist ein Typ, das zweite die Variable. - const isType = next !== undefined && !javaKeywords.has(next.name) && /^(?:\s*\[\s*\])*[ \t]+$/.test(between) - if (isType && /^\s*(?:[=;,):]|$)/.test(text.slice(next.to))) declared.add(next.name) - const isClass = /^\p{Lu}/u.test(w.name) && /^\s*\./.test(rest) - if (/^\s*\(/.test(rest) || isClass || isType) skip.add(w.name) - else names.add(w.name) - }) - } - return [...names].filter((n) => !skip.has(n) || declared.has(n)) -} - -/** Alle Wörter im Struktogramm, auch Methoden und Klassen: Der neue Name soll keinen davon treffen. */ -export function documentWords(doc: VSDocument): string[] { - return documentTexts(doc).flatMap((t) => codeWords(t).map((w) => w.name)) -} - -export function countInDocument(doc: VSDocument, name: string): number { - return documentTexts(doc).reduce((n, t) => n + nameRanges(t, name).length, 0) -} - -export function documentVariableNames(doc: VSDocument): string[] { - return variableNames(documentTexts(doc)) -} - -export function elementVariableNames(el: VSElement): string[] { - return variableNames([el.textLines.join('\n')]) -} - -export type RenameCheck = { ok: true; warning?: 'exists' } | { ok: false; reason: 'empty' | 'same' | 'invalid' | 'keyword' } - -/** Neuen Namen prüfen; `existing` = Namen, die schon vorkommen. */ -export function checkNewName(from: string, to: string, existing: Iterable): RenameCheck { - if (to === '') return { ok: false, reason: 'empty' } - if (to === from) return { ok: false, reason: 'same' } - if (!/^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(to)) return { ok: false, reason: 'invalid' } - if (javaKeywords.has(to)) return { ok: false, reason: 'keyword' } - for (const n of existing) if (n === to) return { ok: true, warning: 'exists' } - return { ok: true } -} diff --git a/web/src/core/robotPreview.ts b/web/src/core/robotPreview.ts deleted file mode 100644 index a3d87292..00000000 --- a/web/src/core/robotPreview.ts +++ /dev/null @@ -1,68 +0,0 @@ -import type { VSDocument } from './model' -import type { MBotSensorValues } from './mbot' -import { ROBOT_STEP_MS, RobotWorld, type Point, type Pose, type RobotWorldSetup } from './robotWorld' -import { SimulationEngine, type SimNotice, type SubprogramResolver } from './simulation' - -/** Ein Bild der Fahrt: wo der mBot2 steht, seine Spur, LEDs und Bildschirm, dazu ein Ton in diesem Schritt. */ -export interface PreviewFrame { - pose: Pose - trail: Point[] - leds: RobotWorld['leds'] - screen: string | undefined - tone?: [number, number] -} - -export interface PreviewResult { - frames: PreviewFrame[] - /** Warum die Vorschau aufhört: Ende, Eingabe nötig, Fehler oder zu viele Schritte (Endlosschleife?). */ - stop: 'end' | 'input' | 'error' | 'limit' - notice?: SimNotice - bumps: number - /** Strecke der Spur in cm. */ - distance: number -} - -export const maxPreviewSteps = 5000 - -/** - * mBot2-Vorschau beim Bearbeiten: das Programm im Hintergrund ganz durchrechnen, wie in der Simulation - * (die Welt liefert die Sensorwerte), und jedes Bild der Fahrt merken. - */ -export function previewRun(doc: VSDocument, subprograms: SubprogramResolver | undefined, setup: RobotWorldSetup, controls: MBotSensorValues = {}): PreviewResult { - const engine = new SimulationEngine(doc, subprograms) - const robot = new RobotWorld(setup) - const frame = (tone?: [number, number]): PreviewFrame => ({ - pose: { ...robot.pose }, - trail: robot.trail.map((p) => ({ ...p })), - leds: [...robot.leds], - screen: robot.screen, - tone, - }) - const frames: PreviewFrame[] = [frame()] - let applied = 0 - const result = (stop: PreviewResult['stop'], notice?: SimNotice): PreviewResult => ({ - frames, - stop, - notice, - bumps: robot.bumps, - distance: Math.round(robot.trail.slice(1).reduce((sum, p, i) => sum + Math.hypot(p.x - robot.trail[i].x, p.y - robot.trail[i].y), 0)), - }) - for (let i = 0; i < maxPreviewSteps; i++) { - const { state } = engine - if (state.inputRequest) return result('input') - if (state.isHalted) return result('error', state.notice) - if (!engine.canStep) return result('end') - engine.robotSensors = { ...controls, ...robot.sensors() } - engine.step() - // jeder neue Befehl ist ein Bild; mit `mbot.drive` fährt er auch ohne Befehl weiter - for (; applied < state.robotActions.length; applied++) { - const action = state.robotActions[applied] - robot.apply(action) - frames.push(frame(action.method === 'playTone' ? [action.args[0], action.args[1]] : undefined)) - } - const moving = robot.wheels.some((p) => p !== 0) - robot.tick(ROBOT_STEP_MS) - if (moving) frames.push(frame()) - } - return result('limit') -} diff --git a/web/src/core/robotWorld.ts b/web/src/core/robotWorld.ts deleted file mode 100644 index d90471a4..00000000 --- a/web/src/core/robotWorld.ts +++ /dev/null @@ -1,352 +0,0 @@ -import { MBOT_LED_COLORS, MBOT_LED_COUNT, type MBotAction, type MBotColor, type MBotSensorValue } from './mbot' - -/** - * Die Welt des VisuBot-Simulators von oben: Boden in Zentimetern, x nach rechts, y nach vorn (weg vom Betrachter). - * Blickrichtung in Grad, 0 = nach vorn, positiv im Uhrzeigersinn (wie `mbot2.turn`). - * Abstand und Liniensensor rechnen hier, damit Programme ohne echten Roboter dieselben Werte sehen. - */ - -export interface Point { - x: number - y: number -} - -export interface Pose extends Point { - heading: number -} - -export interface Wall { - from: Point - to: Point -} - -export interface RobotWorldSetup { - /** Breite und Tiefe des Bodens in cm; die Mitte ist (0, 0). */ - width: number - depth: number - start: Pose - walls: Wall[] - /** Schwarze Klebeband-Linien (je ein Linienzug). */ - lines: Point[][] - /** Wandklötze von 10 × 10 cm (Mitte) aus einer Weltkarte, wie die Bäume bei Kara. */ - blocks?: Point[] - /** Schwarze Felder von 10 × 10 cm (Mitte); der Liniensensor sieht sie. */ - marks?: Point[] - /** Zielfelder von 10 × 10 cm (Mitte); sie sind ebenfalls schwarz. */ - goals?: Point[] - /** Haltefelder von 10 × 10 cm (Mitte): Ziel, das der Liniensensor nicht sieht; die Radachse muss darauf stehen. */ - targets?: Point[] - /** Farbige Felder von 10 × 10 cm (Mitte); der Farbsensor erkennt sie, der Liniensensor nicht. */ - colors?: { at: Point; color: MBotColor }[] -} - -/** Größe eines Feldes in einer Weltkarte (cm). */ -export const CELL = 10 - -/** - * Weltkarte als Text, ein Zeichen = 10 cm, oben ist hinten (weg vom Betrachter): - * `#` Wand, `=` schwarzes Feld, `Z` Ziel (auch schwarz), `o` Haltefeld (unsichtbares Ziel), - * `r g b y` rotes, grünes, blaues, gelbes Feld, - * `^ > v <` Start mit Blickrichtung, sonst freier Boden. - * Zeilen mit `//` am Anfang sind Kommentare. Um die Karte kommt automatisch der Rahmen. - * Eine Zeile `linie: 2,8 2,2 7,2` legt eine schwarze Klebeband-Linie durch die Mitten dieser Felder (Spalte,Zeile ab 0, Kommazahlen erlaubt). - * Eine Zeile `raster: 3` vorweg macht aus jedem Zeichen 3 × 3 Felder (30 cm wie ein Kara-Feld, so breit wie der mBot2); - * der Start liegt dann in der Mitte seines großen Feldes. - */ -export function parseWorldMap(text: string): RobotWorldSetup { - let rows = text.replace(/\r/g, '').split('\n').filter((l) => !l.startsWith('//')) - const lineSpecs = rows.filter((l) => /^linie:/.test(l)).map((l) => l.slice(6).trim()) - rows = rows.filter((l) => !/^linie:/.test(l)) - const rasterLine = rows.findIndex((l) => /^raster:\s*\d+\s*$/.test(l)) - if (rasterLine >= 0) { - const n = Number(rows[rasterLine].split(':')[1]) - rows.splice(rasterLine, 1) - rows = rows.flatMap((row) => - Array.from({ length: n }, (_, i) => - Array.from(row) - .map((ch) => Array.from({ length: n }, (_, j) => ('^>v<'.includes(ch) ? (i === (n >> 1) && j === (n >> 1) ? ch : '.') : ch)).join('')) - .join(''), - ), - ) - } - while (rows.length && rows[rows.length - 1].trim() === '') rows.pop() - while (rows.length && rows[0].trim() === '') rows.shift() - const cols = Math.max(...rows.map((r) => r.length)) - const width = cols * CELL - const depth = rows.length * CELL - const center = (c: number, r: number): Point => ({ x: -width / 2 + (c + 0.5) * CELL, y: depth / 2 - (r + 0.5) * CELL }) - const setup: RobotWorldSetup = { width, depth, start: { x: 0, y: 0, heading: 0 }, walls: [], lines: [], blocks: [], marks: [], goals: [], targets: [], colors: [] } - let starts = 0 - rows.forEach((row, r) => { - Array.from(row).forEach((ch, c) => { - const p = center(c, r) - if (ch === '#') setup.blocks!.push(p) - else if (ch === '=') setup.marks!.push(p) - else if (ch === 'Z') setup.goals!.push(p) - else if (ch === 'o') setup.targets!.push(p) - else if (ch in FIELD_COLORS) setup.colors!.push({ at: p, color: FIELD_COLORS[ch] }) - else if ('^>v<'.includes(ch)) { - starts++ - setup.start = { ...p, heading: { '^': 0, '>': 90, v: 180, '<': 270 }[ch]! } - } - }) - }) - for (const spec of lineSpecs) { - setup.lines.push( - spec.split(/\s+/).map((pair) => { - const [c, r] = pair.split(',').map(Number) - if (!Number.isFinite(c) || !Number.isFinite(r)) throw new Error(`Linie: „${pair}“ ist kein Feld (Spalte,Zeile).`) - return center(c, r) - }), - ) - } - if (starts !== 1) throw new Error(`Die Weltkarte braucht genau einen Start (^ > v <), gefunden: ${starts}`) - return setup -} - -const FIELD_COLORS: Record = { r: 'red', g: 'green', b: 'blue', y: 'yellow' } - -const inCell = (p: Point, cell: Point) => Math.abs(p.x - cell.x) <= CELL / 2 && Math.abs(p.y - cell.y) <= CELL / 2 - -/** Abstand von der Radachse bis vorn (Sensoren, Ohren): so weit vorher hält der mBot2 vor einer Wand. Die Lage (x, y) ist die Mitte der Radachse. */ -export const ROBOT_RADIUS = 10 -/** Ultraschall sitzt vorn, so weit vor der Mitte. */ -const SONAR_OFFSET = 9 -/** Ultraschall misst höchstens so weit (mbuild ultrasonic2: 300 cm). */ -export const SONAR_MAX = 300 -/** Liniensensor: Abstand vor der Mitte und seitliche Lage der vier Fühler (L2, L1, R1, R2). */ -const LINE_OFFSET = 8 -const LINE_PROBES = [-3.6, -1.2, 1.2, 3.6] -export const LINE_WIDTH = 2.5 -/** `mbot.drive(100, 100)` fährt so viele cm pro Sekunde (im Simulator). */ -export const MAX_SPEED = 20 -/** Abstand der beiden Räder in cm (zum Drehen mit `mbot.drive`). */ -const WHEEL_BASE = 12 -/** So viel Zeit vergeht im Simulator bei jedem Schritt des Programms (ms). */ -export const ROBOT_STEP_MS = 100 - -export const emptyWorld: RobotWorldSetup = { width: 200, depth: 200, start: { x: 0, y: 0, heading: 0 }, walls: [], lines: [] } - -const rad = (deg: number) => (deg * Math.PI) / 180 -const forwardOf = (heading: number): Point => ({ x: Math.sin(rad(heading)), y: Math.cos(rad(heading)) }) - -export type RGB = [number, number, number] -const ledsOff = (): (RGB | undefined)[] => Array.from({ length: MBOT_LED_COUNT }, () => undefined) -/** Farbe einer LED; (0, 0, 0) ist aus. */ -const lit = ([r, g, b]: number[]): RGB | undefined => (r > 0 || g > 0 || b > 0 ? [r, g, b] : undefined) - -export class RobotWorld { - pose: Pose - /** Gefahrene Strecke (Stift unten), beginnt an der Startposition. */ - trail: Point[] - /** Wie oft der Roboter an eine Wand oder den Rand gestoßen ist. */ - bumps = 0 - /** Farben der fünf LEDs (Nummer 1 bis 5); `undefined` = aus. */ - leds: (RGB | undefined)[] = ledsOff() - /** Text auf dem Bildschirm des CyberPi (`mbot.showText`). */ - screen: string | undefined - /** Leistung der Räder in Prozent (`mbot.drive`); der Roboter fährt damit in `tick` weiter. */ - wheels: [number, number] = [0, 0] - /** Zeit seit dem Start in ms und Stand beim letzten `resetTimer()` */ - time = 0 - private timerStart = 0 - /** Steht er gerade an einer Wand an (zählt `bumps` nur einmal pro Anstoßen)? */ - private blocked = false - - constructor(readonly setup: RobotWorldSetup = emptyWorld) { - this.pose = { ...setup.start } - this.trail = [{ x: setup.start.x, y: setup.start.y }] - } - - reset() { - this.pose = { ...this.setup.start } - this.trail = [{ x: this.pose.x, y: this.pose.y }] - this.bumps = 0 - this.leds = ledsOff() - this.screen = undefined - this.wheels = [0, 0] - this.time = 0 - this.timerStart = 0 - this.blocked = false - } - - /** Alle Wände einschließlich Rand des Bodens. */ - get allWalls(): Wall[] { - const w = this.setup.width / 2 - const d = this.setup.depth / 2 - const c = [{ x: -w, y: -d }, { x: w, y: -d }, { x: w, y: d }, { x: -w, y: d }] - return [...this.setup.walls, ...c.map((from, i) => ({ from, to: c[(i + 1) % 4] })), ...this.blockWalls] - } - - /** Kanten der Wandklötze (einmal berechnet). */ - private get blockWalls(): Wall[] { - this.blockWallCache ??= (this.setup.blocks ?? []).flatMap((b) => { - const h = CELL / 2 - const c = [{ x: b.x - h, y: b.y - h }, { x: b.x + h, y: b.y - h }, { x: b.x + h, y: b.y + h }, { x: b.x - h, y: b.y + h }] - return c.map((from, i) => ({ from, to: c[(i + 1) % 4] })) - }) - return this.blockWallCache - } - - private blockWallCache?: Wall[] - - /** Ist der Roboter im Ziel? Ja, wenn seine Mitte oder sein Liniensensor über einem Zielfeld ist oder seine Mitte auf einem Haltefeld. */ - atGoal(): boolean { - const dir = forwardOf(this.pose.heading) - const probe = { x: this.pose.x + dir.x * LINE_OFFSET, y: this.pose.y + dir.y * LINE_OFFSET } - return (this.setup.goals ?? []).some((g) => inCell(this.pose, g) || inCell(probe, g)) || (this.setup.targets ?? []).some((t) => inCell(this.pose, t)) - } - - apply(action: MBotAction) { - switch (action.method) { - case 'forward': this.wheels = [0, 0]; return this.drive(action.args[0]) - case 'backward': this.wheels = [0, 0]; return this.drive(-action.args[0]) - case 'turnRight': this.wheels = [0, 0]; return this.turn(action.args[0]) - case 'turnLeft': this.wheels = [0, 0]; return this.turn(-action.args[0]) - case 'stop': this.wheels = [0, 0]; return - case 'drive': this.wheels = [clampPower(action.args[0]), clampPower(action.args[1])]; return - case 'pause': return this.tick(action.args[0]) - case 'resetTimer': this.timerStart = this.time; return - case 'setLed': this.leds = ledsOff().map(() => lit(action.args)); return - case 'ledOff': this.leds = ledsOff(); return - case 'ledColor': this.leds[action.args[0]] = lit(MBOT_LED_COLORS[action.text ?? ''] ?? [0, 0, 0]); return - case 'showText': this.screen = action.text; return - } - } - - /** - * Zeit vergeht: Mit `mbot.drive` gestartete Räder bewegen den Roboter weiter (Kurve, wenn sie verschieden schnell sind). - * Vor einer Wand bleibt er stehen und zählt einmal als angestoßen. - */ - tick(ms: number) { - if (ms <= 0) return - this.time += ms - const [l, r] = this.wheels.map((p) => (p / 100) * MAX_SPEED) - if (l === 0 && r === 0) return - const parts = Math.ceil(ms / 10) - const dt = ms / 1000 / parts - let moved = false - for (let i = 0; i < parts; i++) { - const v = ((l + r) / 2) * dt - // rechts schneller → Linkskurve, also kleinerer Kurs - this.pose = { ...this.pose, heading: normalizeHeading(this.pose.heading - (((r - l) / WHEEL_BASE) * dt * 180) / Math.PI) } - if (Math.abs(v) < 1e-9) continue - const dir = forwardOf(this.pose.heading) - const sign = Math.sign(v) - const free = this.freeDistance(this.pose, { x: dir.x * sign, y: dir.y * sign }) - ROBOT_RADIUS - const step = Math.min(Math.abs(v), Math.max(0, free)) - if (step < Math.abs(v) - 1e-9) { - if (!this.blocked) this.bumps += 1 - this.blocked = true - } else this.blocked = false - this.pose = { ...this.pose, x: this.pose.x + dir.x * sign * step, y: this.pose.y + dir.y * sign * step } - moved = true - } - if (moved) this.trail.push({ x: this.pose.x, y: this.pose.y }) - } - - /** Stoppuhr in Sekunden (eine Nachkommastelle), wie `cyberpi.timer.get()` */ - get timer(): number { - return Math.round((this.time - this.timerStart) / 100) / 10 - } - - turn(degrees: number) { - this.pose = { ...this.pose, heading: normalizeHeading(this.pose.heading + degrees) } - } - - /** Fährt `cm` weit (negativ = rückwärts) und hält vor einer Wand an. */ - drive(cm: number) { - const dir = forwardOf(this.pose.heading) - const sign = Math.sign(cm) - const dirMoving = { x: dir.x * sign, y: dir.y * sign } - const free = this.freeDistance(this.pose, dirMoving) - const distance = Math.min(Math.abs(cm), Math.max(0, free - ROBOT_RADIUS)) - if (distance < Math.abs(cm) - 1e-9) this.bumps += 1 - this.pose = { ...this.pose, x: this.pose.x + dirMoving.x * distance, y: this.pose.y + dirMoving.y * distance } - this.trail.push({ x: this.pose.x, y: this.pose.y }) - } - - /** Abstand des Ultraschallsensors bis zur nächsten Wand in cm (wie der echte Sensor ganzzahlig). */ - distance(): number { - const dir = forwardOf(this.pose.heading) - const origin = { x: this.pose.x + dir.x * SONAR_OFFSET, y: this.pose.y + dir.y * SONAR_OFFSET } - return Math.min(SONAR_MAX, Math.round(this.freeDistance(origin, dir))) - } - - /** Liniensensor wie `get_line_sta`: Bit 3 = ganz links … Bit 0 = ganz rechts; 0 = keine Linie, 15 = alle vier. */ - lineState(): number { - const dir = forwardOf(this.pose.heading) - const right = { x: dir.y, y: -dir.x } - let bits = 0 - LINE_PROBES.forEach((side, i) => { - const p = { - x: this.pose.x + dir.x * LINE_OFFSET + right.x * side, - y: this.pose.y + dir.y * LINE_OFFSET + right.y * side, - } - const dark = [...(this.setup.marks ?? []), ...(this.setup.goals ?? [])].some((cell) => inCell(p, cell)) - if (dark || this.setup.lines.some((line) => distanceToPolyline(p, line) <= LINE_WIDTH / 2)) bits |= 1 << (3 - i) - }) - return bits - } - - /** Farbe unter dem Liniensensor (Mitte vorn); `undefined` auf hellem oder schwarzem Boden. */ - colorUnder(): MBotColor | undefined { - const dir = forwardOf(this.pose.heading) - const probe = { x: this.pose.x + dir.x * LINE_OFFSET, y: this.pose.y + dir.y * LINE_OFFSET } - return (this.setup.colors ?? []).find((c) => inCell(probe, c.at))?.color - } - - sensors(): Record { - const line = this.lineState() - return { - getDistance: this.distance(), - getLine: line, - // auf schwarzem Feld oder Klebeband schwarz, sonst heller Boden - getColor: this.colorUnder() ?? (line & 0b0110 ? 'black' : 'white'), - isLineLeft: (line & 0b0100) !== 0, - isLineRight: (line & 0b0010) !== 0, - getTimer: this.timer, - } - } - - /** Strecke von `origin` in Richtung `dir`, bis ein Strahl eine Wand trifft. */ - private freeDistance(origin: Point, dir: Point): number { - let best = Infinity - for (const wall of this.allWalls) { - const t = rayHitsSegment(origin, dir, wall.from, wall.to) - if (t !== undefined && t < best) best = t - } - return best - } -} - -const clampPower = (p: number) => Math.max(-100, Math.min(100, p)) - -export function normalizeHeading(deg: number): number { - const h = ((deg % 360) + 360) % 360 - return Math.abs(h - 360) < 1e-9 ? 0 : h -} - -function rayHitsSegment(o: Point, d: Point, a: Point, b: Point): number | undefined { - const e = { x: b.x - a.x, y: b.y - a.y } - const denom = d.x * e.y - d.y * e.x - if (Math.abs(denom) < 1e-12) return undefined - const ao = { x: a.x - o.x, y: a.y - o.y } - const t = (ao.x * e.y - ao.y * e.x) / denom - const u = (ao.x * d.y - ao.y * d.x) / denom - return t >= 0 && u >= 0 && u <= 1 ? t : undefined -} - -function distanceToPolyline(p: Point, line: Point[]): number { - let best = Infinity - for (let i = 0; i + 1 < line.length; i++) best = Math.min(best, distanceToSegment(p, line[i], line[i + 1])) - return best -} - -function distanceToSegment(p: Point, a: Point, b: Point): number { - const dx = b.x - a.x - const dy = b.y - a.y - const len2 = dx * dx + dy * dy - const t = len2 === 0 ? 0 : Math.max(0, Math.min(1, ((p.x - a.x) * dx + (p.y - a.y) * dy) / len2)) - return Math.hypot(p.x - (a.x + t * dx), p.y - (a.y + t * dy)) -} diff --git a/web/src/core/sections.ts b/web/src/core/sections.ts deleted file mode 100644 index 485cded5..00000000 --- a/web/src/core/sections.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * VisuObject: Abschnitts-Kommentare in Klassen wie im Unterricht: - * `// Attribute`, `// Referenzattribute`, `// Konstruktor`, `// Methoden`. - * Referenzattribute haben eine eigene Klasse des Projekts als Typ (auch `ArrayList` oder `Spieler[]`). - * Neue Mitglieder aus dem Klassendiagramm landen im passenden Abschnitt; die Reihenfolge innerhalb bleibt. - */ - -import { associationTarget, parseClasses, type UmlClass, type UmlMember } from './classModel' - -export type Section = 'fields' | 'refs' | 'ctors' | 'methods' -export type SectionLabels = Record - -const order: Section[] = ['fields', 'refs', 'ctors', 'methods'] - -/** Alle Sprachen: So erkennt VisuObject die Kommentare auch nach einem Sprachwechsel wieder. */ -const known: SectionLabels[] = [ - { fields: 'Attribute', refs: 'Referenzattribute', ctors: 'Konstruktor', methods: 'Methoden' }, - { fields: 'Attributes', refs: 'Reference attributes', ctors: 'Constructor', methods: 'Methods' }, - { fields: 'Atributos', refs: 'Atributos de referência', ctors: 'Construtor', methods: 'Métodos' }, -] - -const norm = (s: string) => s.trim().toLowerCase().replace(/[:.]$/, '') -const knownWords = new Set(known.flatMap((k) => Object.values(k)).flatMap((w) => [norm(w), norm(w) + 'en', norm(w) + 's', norm(w) + 'es'])) -/** `// Attribute`, `// Konstruktoren:` … */ -const isSectionLine = (line: string) => { - const m = /^\s*\/\/\s*(.+?)\s*$/.exec(line) - return !!m && knownWords.has(norm(m[1])) -} - -export function sectionOf(m: UmlMember, classNames: Set): Section { - if (m.kind === 'constructor') return 'ctors' - if (m.kind === 'method') return 'methods' - return classNames.has(associationTarget(m.type).name) ? 'refs' : 'fields' -} - -const lineStart = (s: string, pos: number) => s.lastIndexOf('\n', pos - 1) + 1 -const lineEnd = (s: string, pos: number) => { - const i = s.indexOf('\n', pos) - return i < 0 ? s.length : i + 1 -} - -/** Hat die Klasse schon Abschnitts-Kommentare? */ -export function hasSections(source: string, cls: UmlClass): boolean { - const open = source.indexOf('{', cls.headerTo) - return source - .slice(open + 1, cls.bodyEnd) - .split('\n') - .some(isSectionLine) -} - -/** - * Klassenrumpf neu ordnen: die vier Kommentare und darunter die Mitglieder ihres Abschnitts. - * Kommentare und Annotationen direkt über einem Mitglied wandern mit. Anderes im Rumpf - * (z. B. eine innere Klasse) kommt unverändert ans Ende. Interfaces bleiben, wie sie sind. - */ -export function withSections(source: string, cls: UmlClass, classNames: Set, labels: SectionLabels): string { - if (cls.kind !== 'class') return source - const open = source.indexOf('{', cls.headerTo) - if (open < 0 || open > cls.bodyEnd) return source - // Innere Klassen: lieber nichts verschieben. - if (parseClasses(source).some((c) => c.from > open && c.from < cls.bodyEnd)) return source - const bodyFrom = lineEnd(source, open) - const bodyTo = lineStart(source, cls.bodyEnd) - if (bodyFrom > bodyTo || !/^\s*$/.test(source.slice(open + 1, bodyFrom)) || !/^\s*$/.test(source.slice(bodyTo, cls.bodyEnd))) return source - const indent = /^[ \t]*/.exec(source.slice(lineStart(source, cls.from)))![0] + ' ' - const groups = new Map(order.map((s) => [s, []])) - const taken: [number, number][] = [] - const seen = new Set() - for (const m of cls.members) { - if (seen.has(m.from)) continue - seen.add(m.from) - let from = lineStart(source, m.from) - if (!/^\s*$/.test(source.slice(from, m.from))) return source - const to = lineEnd(source, m.to) - if (!/^\s*$/.test(source.slice(m.to, to))) return source - // Javadoc, Kommentare und Annotationen darüber gehören dazu (aber keine Abschnitts-Kommentare). - for (;;) { - if (from <= bodyFrom) break - const prev = lineStart(source, from - 1) - const line = source.slice(prev, from) - if (isSectionLine(line) || !/^\s*(\/\/|\/\*|\*|@)/.test(line)) break - from = prev - } - taken.push([from, to]) - groups.get(sectionOf(m, classNames))!.push(source.slice(from, to).replace(/\s+$/, '')) - } - // Was übrig bleibt (ohne Leerzeilen und alte Abschnitts-Kommentare). - taken.sort((a, b) => a[0] - b[0]) - let rest = '' - let at = bodyFrom - for (const [f, t] of [...taken, [bodyTo, bodyTo] as [number, number]]) { - if (f > at) rest += source.slice(at, f) - at = Math.max(at, t) - } - const extra = rest - .split('\n') - .filter((l) => !isSectionLine(l)) - .join('\n') - .trim() - const blocks = order.map((s) => { - const items = groups.get(s)! - const sep = s === 'fields' || s === 'refs' ? '\n' : '\n\n' - return `${indent}// ${labels[s]}` + (items.length ? '\n' + items.join(sep) : '') - }) - if (extra) blocks.push(indent + extra) - return source.slice(0, bodyFrom) + blocks.join('\n\n') + '\n' + source.slice(bodyTo) -} - -/** Nur einsortieren, wenn die Klasse schon Abschnitte hat. */ -export function keepSections(source: string, className: string, classNames: Set, labels: SectionLabels): string { - const cls = parseClasses(source).find((c) => c.name === className) - return cls && hasSections(source, cls) ? withSections(source, cls, classNames, labels) : source -} diff --git a/web/src/core/simulation.ts b/web/src/core/simulation.ts deleted file mode 100644 index 6652b609..00000000 --- a/web/src/core/simulation.ts +++ /dev/null @@ -1,2368 +0,0 @@ -/** - * Port von `VisuStructSimulationEngine.swift`: führt ein Struktogramm Schritt für Schritt aus. - * Verzweigungen und Schleifen ersetzen sich beim Ausführen durch die Schritte ihres Rumpfs. - */ -import { ElementType, type VSCase, type VSDocument, type VSElement } from './model' -import { MBOT_COMMANDS, MBOT_DRIVE_UNITS, MBOT_SENSORS, makeMBotAction, makeMBotDriveAction, mbotArguments, parseMBotCommand, substituteMBotSensors, type MBotAction, type MBotSensorValues } from './mbot' - -export type SimValue = - | { kind: 'int'; value: number } - | { kind: 'double'; value: number } - | { kind: 'string'; value: string } - | { kind: 'boolean'; value: boolean } - | { kind: 'intArray'; value: number[] } - | { kind: 'doubleArray'; value: number[] } - | { kind: 'stringArray'; value: string[] } - | { kind: 'booleanArray'; value: boolean[] } - /** Zweidimensionales Array (`boolean[][] sitze`): Zeilen, darin die Werte. */ - | { kind: 'grid'; elementType: SimValueType; value: (number | string | boolean)[][] } - /** `new Scanner(new File(…))` oder `new BufferedReader(new FileReader(…))`: liest Zeile für Zeile, `next` ist die nächste. */ - | { kind: 'reader'; className: 'Scanner' | 'BufferedReader'; file: string; lines: string[]; next: number } - /** `new PrintWriter(…)`: sammelt Text, der erst mit `close()` in der Datei landet (wie in Java). */ - | { kind: 'writer'; file: string; text: string } - /** `null`, z. B. von `readLine()` am Ende der Datei. */ - | { kind: 'null' } - -export type SimValueType = 'int' | 'double' | 'string' | 'boolean' - -export type StepKind = - | 'statement' - | 'branch' - | 'switchSelection' - | 'forLoopStart' - | 'forLoopCondition' - | 'forLoopIncrement' - | 'whileLoop' - | 'doUntilLoop' - | 'doUntilCondition' - | 'infiniteLoop' - | 'jump' - | 'call' - | 'callEnd' - -export interface SimStep { - path: number[] - text: string - kind: StepKind - cases?: VSCase[] - loopBody?: VSElement[] - forParts?: string[] - /** Aufrufebene: 0 ist das Hauptprogramm, jeder Aufruf bekommt eine neue Nummer. */ - frame?: number - /** Im Unterprogramm: Pfad des Aufruf-Kastens im Hauptprogramm, der markiert bleibt. */ - callPath?: number[] -} - -/** Sucht zum Text eines Aufruf-Kastens das passende Diagramm (z. B. einen offenen Reiter). */ -export type SubprogramResolver = (name: string) => VSDocument | undefined - -/** Ab dieser Tiefe bricht die Simulation ab (vermutlich ein Aufruf, der sich selbst aufruft). */ -export const maxCallDepth = 100 - -export interface InputRequest { - variableName: string - valueType: SimValueType - prompt: string - /** Wie in Java hinter `System.out.print("Note: ")`: Die Eingabe steht in derselben Zeile wie diese Ausgabe. */ - continuesLine?: boolean - /** Pseudocode: Die Frage gehört zu einer Bedingung, die Antwort (true/false) entscheidet den Weg. */ - decision?: boolean -} - -/** Meldungen der Simulation, unabhängig von der Sprache. */ -export type SimNotice = - | { kind: 'finished' } - | { kind: 'infiniteLoopWithoutBody' } - | { kind: 'unsupportedStatement'; text: string } - | { kind: 'invalidExpression'; text: string } - /** `similar`: gleicher Name in anderer Groß- und Kleinschreibung (`Zahl` statt `zahl`). */ - | { kind: 'unknownVariable'; text: string; similar?: string } - | { kind: 'divisionByZero' } - | { kind: 'wholeNumberExpected' } - | { kind: 'numberExpected' } - | { kind: 'booleanExpected' } - | { kind: 'subprogramNotFound'; text: string } - | { kind: 'callTooDeep' } - /** Die Werte in den Klammern passen nicht zur Kopfzeile des Unterprogramms. */ - | { kind: 'callArguments'; text: string } - /** Ein Unterprogramm mit Rückgabetyp endet ohne `return`. */ - | { kind: 'missingReturn'; text: string } - /** `Files.readString(Path.of("…"))` mit einem Namen, den es nicht gibt. */ - | { kind: 'fileNotFound'; text: string } - /** `nextLine()`, obwohl die Datei zu Ende ist (in Java eine NoSuchElementException). */ - | { kind: 'endOfFile'; text: string } - /** Eine Methode auf `null` (in Java eine NullPointerException). */ - | { kind: 'nullValue'; text: string } - | { kind: 'other'; text: string } - -export interface SimState { - stepIndex: number - variables: Map - outputLines: string[] - /** Die letzte Ausgabe war `System.out.print(…)`: Die nächste Ausgabe schreibt in derselben Zeile weiter. */ - outputLineOpen?: boolean - inputRequest?: InputRequest - /** Fehler zur letzten Eingabe (z. B. keine Zahl). */ - inputErrorNotice?: SimNotice - lastTrace?: string - /** Hinweis nach dem letzten Schritt (Ende, Fehler). */ - notice?: SimNotice - /** Nach einem Fehler bleibt die Simulation auf dem fehlerhaften Schritt stehen. */ - isHalted: boolean - /** Dateien, die das Programm geschrieben hat (Name → Inhalt). */ - files: Map - /** Befehle an den mBot2 in der Reihenfolge der Schritte (`mbot.forward(10)` usw.). */ - robotActions: MBotAction[] -} - -export function noticeEnglish(n: SimNotice): string { - switch (n.kind) { - case 'finished': return 'Simulation finished.' - case 'infiniteLoopWithoutBody': return 'Infinite loop has no body.' - case 'unsupportedStatement': return `Unsupported statement: ${n.text}` - case 'invalidExpression': return `Invalid expression: ${n.text}` - case 'unknownVariable': return n.similar ? `Unknown variable: ${n.text}. Java is case-sensitive: did you mean ${n.similar}?` : `Unknown variable: ${n.text}` - case 'divisionByZero': return 'Division by zero.' - case 'wholeNumberExpected': return 'Please enter a whole number.' - case 'numberExpected': return 'Please enter a number.' - case 'booleanExpected': return 'Please enter true or false.' - case 'subprogramNotFound': return `No open diagram is called ${n.text}. The call was skipped.` - case 'callTooDeep': return 'Too many nested calls. Does a subprogram call itself?' - case 'callArguments': return `The call does not match the header of ${n.text}. Check the values in the parentheses.` - case 'missingReturn': return `${n.text} ended without returning a value with return.` - case 'fileNotFound': return `There is no file called ${n.text}.` - case 'endOfFile': return `The file ${n.text} has no more lines.` - case 'nullValue': return `${n.text} is null.` - case 'other': return n.text - } -} - -export function noticeGerman(n: SimNotice): string { - switch (n.kind) { - case 'finished': return 'Simulation beendet.' - case 'infiniteLoopWithoutBody': return 'Die Endlosschleife hat keinen Rumpf.' - case 'unsupportedStatement': return `Nicht unterstützte Anweisung: ${n.text}` - case 'invalidExpression': return `Ungültiger Ausdruck: ${n.text}` - case 'unknownVariable': return n.similar ? `Unbekannte Variable: ${n.text}. Java unterscheidet Groß- und Kleinschreibung: Meintest du ${n.similar}?` : `Unbekannte Variable: ${n.text}` - case 'divisionByZero': return 'Division durch null.' - case 'wholeNumberExpected': return 'Bitte eine ganze Zahl eingeben.' - case 'numberExpected': return 'Bitte eine Zahl eingeben.' - case 'booleanExpected': return 'Bitte true oder false eingeben.' - case 'subprogramNotFound': return `Kein offenes Diagramm heißt ${n.text}. Der Aufruf wurde übersprungen.` - case 'callTooDeep': return 'Zu viele verschachtelte Aufrufe. Ruft sich ein Unterprogramm selbst auf?' - case 'callArguments': return `Der Aufruf passt nicht zur Kopfzeile von ${n.text}. Prüfe die Werte in den Klammern.` - case 'missingReturn': return `${n.text} hat keinen Wert mit return zurückgegeben.` - case 'fileNotFound': return `Es gibt keine Datei ${n.text}.` - case 'endOfFile': return `Die Datei ${n.text} hat keine Zeile mehr.` - case 'nullValue': return `${n.text} ist null.` - case 'other': return n.text - } -} - -class SimulationError extends Error { - constructor(readonly notice: SimNotice) { - super(noticeEnglish(notice)) - } -} - -const unsupported = (text: string) => new SimulationError({ kind: 'unsupportedStatement', text }) -const invalid = (text: string) => new SimulationError({ kind: 'invalidExpression', text }) -/** Unbekannte Variable; gibt es sie in anderer Groß- und Kleinschreibung, nennt der Hinweis sie. */ -const unknownVar = (text: string, variables?: Map) => { - const similar = variables && [...variables.keys()].find((k) => k !== text && k.toLowerCase() === text.toLowerCase()) - return new SimulationError(similar ? { kind: 'unknownVariable', text, similar } : { kind: 'unknownVariable', text }) -} -const divByZero = () => new SimulationError({ kind: 'divisionByZero' }) - -/** Textdateien, die Java mit `Files.readString(Path.of("name"))` liest, z. B. aus einer Lektion. */ -const textFiles = new Map() - -export function registerTextFile(name: string, content: string) { - textFiles.set(name, content) -} - -export const textFileEntries = (): [string, string][] => [...textFiles] - -function toNotice(error: unknown): SimNotice { - if (error instanceof SimulationError) return error.notice - return { kind: 'other', text: error instanceof Error ? error.message : String(error) } -} - -// MARK: - Werte - -/** `%.12g` wie in C. */ -function formatG12(value: number): string { - if (value === 0) return '0' - if (!Number.isFinite(value)) return Number.isNaN(value) ? 'nan' : value > 0 ? 'inf' : '-inf' - const exp = Math.floor(Math.log10(Math.abs(Number(value.toPrecision(12))))) - if (exp < -4 || exp >= 12) { - const [mantissa, e] = value.toExponential(11).split('e') - const m = mantissa.includes('.') ? mantissa.replace(/0+$/, '').replace(/\.$/, '') : mantissa - const n = Number(e) - return `${m}e${n < 0 ? '-' : '+'}${String(Math.abs(n)).padStart(2, '0')}` - } - const fixed = value.toFixed(Math.max(0, 11 - exp)) - return fixed.includes('.') ? fixed.replace(/0+$/, '').replace(/\.$/, '') : fixed -} - -function formatDouble(value: number): string { - if (Number.isFinite(value) && Math.round(value) === value) return value.toFixed(1) - return formatG12(value) -} - -export function describeValue(v: SimValue): string { - switch (v.kind) { - case 'int': return String(v.value) - case 'double': return formatDouble(v.value) - case 'string': return v.value - case 'boolean': return String(v.value) - case 'intArray': return '[' + v.value.map(String).join(', ') + ']' - case 'doubleArray': return '[' + v.value.map(formatDouble).join(', ') + ']' - case 'stringArray': return '[' + v.value.join(', ') + ']' - case 'booleanArray': return '[' + v.value.map(String).join(', ') + ']' - case 'grid': return '[' + describeRows(v).join(', ') + ']' - case 'reader': return `${v.className}(${v.file})` - case 'writer': return `PrintWriter(${v.file})` - case 'null': return 'null' - } -} - -/** Ein 2D-Array Zeile für Zeile, z. B. für die Anzeige unter Variablen. */ -export function describeRows(v: SimValue & { kind: 'grid' }): string[] { - return v.value.map((row) => '[' + row.map((x) => describeValue(elementValue(v.elementType, x))).join(', ') + ']') -} - -function elementValue(type: SimValueType, raw: number | string | boolean): SimValue { - switch (type) { - case 'int': return { kind: 'int', value: raw as number } - case 'double': return { kind: 'double', value: raw as number } - case 'string': return { kind: 'string', value: raw as string } - case 'boolean': return { kind: 'boolean', value: raw as boolean } - } -} - -function elementDefault(type: SimValueType): number | string | boolean { - return type === 'string' ? '' : type === 'boolean' ? false : 0 -} - -/** - * Was `System.out.println(zahlen)` in Java ausgibt: nicht den Inhalt, sondern Typ und Kennung der Referenz, - * z. B. `[I@1b6d3586`. Den Inhalt zeigt erst `Arrays.toString(zahlen)`. Die Kennung bleibt für dasselbe Array gleich. - */ -const arrayIds = new WeakMap() -let nextArrayId = 0 - -export function javaReference(v: SimValue): string { - const code = (t: SimValueType) => ({ int: 'I', double: 'D', boolean: 'Z', string: 'Ljava.lang.String;' })[t] - const prefix = - v.kind === 'grid' ? '[[' + code(v.elementType) - : v.kind === 'intArray' ? '[I' - : v.kind === 'doubleArray' ? '[D' - : v.kind === 'booleanArray' ? '[Z' - : v.kind === 'stringArray' ? '[Ljava.lang.String;' - : undefined - if (!prefix || !('value' in v)) return describeValue(v) - const key = v.value as object - let id = arrayIds.get(key) - if (!id) { - id = ((Math.imul(++nextArrayId, 0x9e3779b1) >>> 0) & 0x7fffffff).toString(16) - arrayIds.set(key, id) - } - return prefix + '@' + id -} - -/** Ein Fach wurde geändert und das Array dabei kopiert: Es ist in Java aber dasselbe Array, also dieselbe Kennung. */ -function keepArrayId(from: object, to: object): void { - const id = arrayIds.get(from) - if (id) arrayIds.set(to, id) -} - -function isArrayValue(v: SimValue): boolean { - return v.kind === 'intArray' || v.kind === 'doubleArray' || v.kind === 'stringArray' || v.kind === 'booleanArray' || v.kind === 'grid' -} - -function asDouble(v: SimValue): number { - return v.kind === 'int' || v.kind === 'double' ? v.value : NaN -} - -const int = (value: number): SimValue => ({ kind: 'int', value }) -const dbl = (value: number): SimValue => ({ kind: 'double', value }) - -// MARK: - Zeichenklassen - -const isWhitespace = (c: string | undefined) => c !== undefined && /\s/u.test(c) -const isLetter = (c: string) => /\p{L}/u.test(c) -const isNumberChar = (c: string) => /\p{N}/u.test(c) - -function isIdentifier(text: string): boolean { - const chars = Array.from(text) - if (chars.length === 0 || !(isLetter(chars[0]) || chars[0] === '_')) return false - return chars.every((c) => isLetter(c) || isNumberChar(c) || c === '_') -} - -function valueTypeKeyword(text: string): SimValueType | undefined { - switch (text.toLowerCase()) { - case 'int': return 'int' - case 'double': return 'double' - case 'string': return 'string' - // Ein `char` ist in VisuStruct ein String mit genau einem Zeichen. - case 'char': return 'string' - case 'boolean': case 'bool': return 'boolean' - default: return undefined - } -} - -function valueTypeOf(v: SimValue): SimValueType { - return v.kind === 'int' ? 'int' : v.kind === 'double' ? 'double' : v.kind === 'boolean' ? 'boolean' : 'string' -} - -function unquotedString(text: string): string | undefined { - const chars = Array.from(text) - // Zeichenliteral wie in Java: 'a' - if (chars.length === 3 && chars[0] === "'" && chars[2] === "'") return chars[1] - if (chars.length < 2 || chars[0] !== '"' || chars[chars.length - 1] !== '"') return undefined - const inner = chars.slice(1, -1).join('') - // `"a\"" + b + "\"c"` beginnt und endet auch mit `"`, ist aber kein einzelnes Literal. - if (!/^(?:[^"\\]|\\.)*$/u.test(inner)) return undefined - return inner.replaceAll('\\"', '"').replaceAll('\\n', '\n') -} - -function normalizeNumericExpression(expression: string): string { - return expression.replaceAll('(int)', '').replaceAll('(double)', '').trim() -} - -/** Für jedes Zeichen: liegt es außerhalb von Klammern und String-Literalen? */ -function topLevelMask(chars: string[]): boolean[] { - const mask = new Array(chars.length).fill(false) - let depth = 0 - let inString = false - let escaped = false - chars.forEach((c, i) => { - if (inString) { - if (escaped) escaped = false - else if (c === '\\') escaped = true - else if (c === '"') inString = false - return - } - switch (c) { - case '"': inString = true; break - case '(': case '[': depth += 1; break - case ')': case ']': depth -= 1; break - default: mask[i] = depth === 0 - } - }) - return mask -} - -function splitTopLevel(expression: string, separator: string): string[] { - const chars = Array.from(expression) - const sep = Array.from(separator) - const mask = topLevelMask(chars) - const parts: string[] = [] - let current: string[] = [] - let i = 0 - while (i < chars.length) { - const end = i + sep.length - if (end <= chars.length && sep.every((s, k) => chars[i + k] === s) && mask.slice(i, end).every(Boolean)) { - parts.push(current.join('').trim()) - current = [] - i = end - continue - } - current.push(chars[i]) - i += 1 - } - parts.push(current.join('').trim()) - return parts -} - -/** Erster Vergleichsoperator außerhalb von Klammern und Strings. */ -function splitComparison(expression: string): { lhs: string; op: string; rhs: string } | undefined { - const chars = Array.from(expression) - const mask = topLevelMask(chars) - for (let i = 0; i < chars.length; i++) { - if (!mask[i]) continue - let op: string | undefined - if (i + 1 < chars.length && mask[i + 1]) { - const pair = chars[i] + chars[i + 1] - if (['>=', '<=', '==', '!='].includes(pair)) op = pair - } - if (op === undefined && (chars[i] === '<' || chars[i] === '>')) op = chars[i] - if (op === undefined) continue - const lhs = chars.slice(0, i).join('').trim() - const rhs = chars.slice(i + op.length).join('').trim() - return { lhs, op, rhs } - } - return undefined -} - -/** Index der `)`, die die `(` bei `openIndex` schließt. */ -function matchingCloseParenthesis(chars: string[], openIndex: number): number | undefined { - let depth = 0 - let inString = false - let escaped = false - for (let i = openIndex; i < chars.length; i++) { - const c = chars[i] - if (inString) { - if (escaped) escaped = false - else if (c === '\\') escaped = true - else if (c === '"') inString = false - continue - } - if (c === '"') inString = true - else if (c === '(') depth += 1 - else if (c === ')') { - depth -= 1 - if (depth === 0) return i - } - } - return undefined -} - -/** `(a || b)` → `a || b`; `(a) && (b)` bleibt unverändert. */ -function innerOfOuterParentheses(text: string): string | undefined { - const chars = Array.from(text) - if (chars[0] !== '(' || chars[chars.length - 1] !== ')' || matchingCloseParenthesis(chars, 0) !== chars.length - 1) { - return undefined - } - return chars.slice(1, -1).join('').trim() -} - -/** Java-Stil `name.equals("Anna")`. */ -function parseEqualsCall(text: string): { lhs: string; rhs: string } | undefined { - const chars = Array.from(text) - const marker = Array.from('.equals(') - if (chars[chars.length - 1] !== ')') return undefined - const mask = topLevelMask(chars) - for (let i = 0; i < chars.length; i++) { - if (!mask[i] || i + marker.length > chars.length) continue - if (!marker.every((m, k) => chars[i + k] === m)) continue - const openIndex = i + marker.length - 1 - if (matchingCloseParenthesis(chars, openIndex) !== chars.length - 1) continue - return { - lhs: chars.slice(0, i).join('').trim(), - rhs: chars.slice(openIndex + 1, -1).join('').trim(), - } - } - return undefined -} - -function stripConditionKeyword(text: string): string { - const trimmed = text.trim() - for (const keyword of ['if', 'while', 'until', 'switch']) { - if (!trimmed.startsWith(keyword)) continue - const after = trimmed.slice(keyword.length) - if (!(after === '' || isWhitespace(after[0]) || after[0] === '(')) continue - let stripped = after.trim() - const inner = innerOfOuterParentheses(stripped) - if (inner !== undefined) stripped = inner - return stripped === '' ? 'true' : stripped - } - return trimmed === '' ? 'true' : trimmed -} - -interface ArrayElementAccess { - arrayName: string - /** Ein Index bei `a[i]`, zwei bei `a[i][j]`. */ - indexExpressions: string[] -} - -/** `name[…]` oder `name[…][…]`, und zwar der ganze Text (nicht etwa `a[i] > a[i + 1]`). */ -function parseArrayElementAccess(text: string): ArrayElementAccess | undefined { - const trimmed = text.trim() - const open = trimmed.indexOf('[') - if (!trimmed.endsWith(']') || open < 0) return undefined - const name = trimmed.slice(0, open).trim() - if (!isIdentifier(name)) return undefined - const indexExpressions: string[] = [] - let i = open - while (i < trimmed.length) { - if (trimmed[i] !== '[') return undefined - let depth = 0 - let close = -1 - for (let k = i; k < trimmed.length; k++) { - if (trimmed[k] === '[') depth += 1 - else if (trimmed[k] === ']' && --depth === 0) { - close = k - break - } - } - if (close < 0) return undefined - indexExpressions.push(trimmed.slice(i + 1, close)) - i = close + 1 - while (i < trimmed.length && isWhitespace(trimmed[i])) i += 1 - } - if (indexExpressions.length > 2) return undefined - return { arrayName: name, indexExpressions } -} - -/** `ziel.methode(arg, arg)` als Ganzes; das Ziel ist ein Name, ein Arrayfach oder ein String-Literal. */ -function parseMethodCall(text: string): { target: string; method: string; args: string[] } | undefined { - const trimmed = text.trim() - if (!trimmed.endsWith(')')) return undefined - const m = /^([A-Za-z_]\w*(?:\s*\[[^\[\]]*\])*|"(?:[^"\\]|\\.)*")\s*\.\s*([A-Za-z_]\w*)\s*\(/u.exec(trimmed) - if (!m) return undefined - const chars = Array.from(trimmed) - const open = Array.from(m[0]).length - 1 - if (matchingCloseParenthesis(chars, open) !== chars.length - 1) return undefined - const inner = chars.slice(open + 1, -1).join('').trim() - return { target: m[1], method: m[2], args: inner === '' ? [] : splitTopLevel(inner, ',') } -} - -function isAssignableTarget(text: string): boolean { - return isIdentifier(text) || parseArrayElementAccess(text) !== undefined -} - -function splitByPlusOutsideQuotes(expression: string): string[] { - const parts: string[] = [] - let current = '' - let inString = false - let escaped = false - let depth = 0 - for (const c of expression) { - if (escaped) { - current += c - escaped = false - continue - } - if (c === '\\') { - current += c - escaped = true - continue - } - if (c === '"') { - inString = !inString - current += c - continue - } - if (!inString && c === '(') depth += 1 - if (!inString && c === ')') depth -= 1 - if (c === '+' && !inString && depth === 0) { - parts.push(current) - current = '' - continue - } - current += c - } - if (inString) throw invalid(expression) - parts.push(current) - return parts -} - -/** Teilt an Kommas, die nicht in geschweiften Klammern stehen: `{1, 2}, {3, 4}` → zwei Teile. */ -function splitTopLevelBraces(text: string): string[] { - const parts: string[] = [] - let depth = 0 - let current = '' - for (const c of text) { - if (c === '{') depth += 1 - if (c === '}') depth -= 1 - if (c === ',' && depth === 0) { - parts.push(current) - current = '' - continue - } - current += c - } - if (current.trim() !== '') parts.push(current) - return parts -} - -// MARK: - Zahlenausdrücke - -type Token = - | { t: 'number'; literal: string; value: number } - | { t: 'identifier'; name: string } - | { t: '+' | '-' | '*' | '/' | '%' | '.' | '(' | ')' | '[' | ']' } - -function tokenize(expression: string): Token[] { - const chars = Array.from(expression) - const tokens: Token[] = [] - let i = 0 - while (i < chars.length) { - const c = chars[i] - if (isWhitespace(c)) { - i += 1 - continue - } - if (isNumberChar(c) || (c === '.' && i + 1 < chars.length && isNumberChar(chars[i + 1]))) { - const start = i - let dots = 0 - while (i < chars.length) { - if (chars[i] === '.') { - dots += 1 - if (dots > 1) throw invalid(expression) - } else if (!isNumberChar(chars[i])) { - break - } - i += 1 - } - const literal = chars.slice(start, i).join('') - if (literal === '.' || !/^[0-9]*\.?[0-9]*$/.test(literal)) throw invalid(expression) - tokens.push({ t: 'number', literal, value: Number(literal) }) - continue - } - if (isLetter(c) || c === '_') { - const start = i - while (i < chars.length && (isLetter(chars[i]) || isNumberChar(chars[i]) || chars[i] === '_')) i += 1 - tokens.push({ t: 'identifier', name: chars.slice(start, i).join('') }) - continue - } - // Zeichenliteral wie in Java: in einer Rechnung zählt sein Zeichencode, also 'A' = 65 - if (c === "'" && chars[i + 2] === "'" && chars[i + 1] !== undefined) { - const code = chars[i + 1].codePointAt(0)! - tokens.push({ t: 'number', literal: String(code), value: code }) - i += 3 - continue - } - if ('+-*/%.()[]'.includes(c)) { - tokens.push({ t: c as '+' }) - i += 1 - continue - } - throw invalid(expression) - } - return tokens -} - -type Num = { v: number; int: boolean } - -/** Zeichencode eines `char` (String mit genau einem Zeichen) wie in Java, wenn damit gerechnet wird; sonst `undefined`. */ -function charCode(text: string | undefined): number | undefined { - const chars = text === undefined ? [] : Array.from(text) - return chars.length === 1 ? chars[0].codePointAt(0) : undefined -} - -class NumericParser { - private index = 0 - constructor(private tokens: Token[], private variables: Map) {} - - parseInt(): number { - const value = this.expression().v - if (this.index !== this.tokens.length) throw invalid('') - if (!Number.isFinite(value)) throw invalid('') - return Math.trunc(value) - } - - parseDouble(): number { - return this.parseNumber().v - } - - /** Wert samt Angabe, ob er wie in Java ganzzahlig ist (nur `int`-Werte und Literale ohne Punkt). */ - parseNumber(): Num { - const value = this.expression() - if (this.index !== this.tokens.length) throw invalid('') - return value - } - - private get current(): Token | undefined { - return this.tokens[this.index] - } - - /** Wie `current`, aber ohne Typverengung durch TypeScript nach vorherigen Prüfungen. */ - private kind(): Token['t'] | undefined { - return this.tokens[this.index]?.t - } - - private isIdent(name: string): boolean { - const c = this.current - return c?.t === 'identifier' && c.name === name - } - - private expression(): Num { - let value = this.term() - for (let tok = this.current; tok && (tok.t === '+' || tok.t === '-'); tok = this.current) { - this.index += 1 - const rhs = this.term() - value = { v: tok.t === '+' ? value.v + rhs.v : value.v - rhs.v, int: value.int && rhs.int } - } - return value - } - - private term(): Num { - let value = this.factor() - for (let tok = this.current; tok && (tok.t === '*' || tok.t === '/' || tok.t === '%'); tok = this.current) { - this.index += 1 - const rhs = this.factor() - const bothInt = value.int && rhs.int - if (tok.t === '*') { - value = { v: value.v * rhs.v, int: bothInt } - } else { - if (rhs.v === 0) throw divByZero() - // Wie in Java: int / int schneidet Richtung null ab (7 / 2 = 3, -7 / 2 = -3) - const v = tok.t === '/' ? (bothInt ? Math.trunc(value.v / rhs.v) : value.v / rhs.v) : value.v % rhs.v - value = { v, int: bothInt } - } - } - return value - } - - private factor(): Num { - const token = this.current - if (!token) throw invalid('') - this.index += 1 - switch (token.t) { - case 'number': - return { v: token.value, int: !token.literal.includes('.') } - case 'identifier': { - const name = token.name - if (name === 'Math' && this.kind() === '.') { - this.index += 1 - if (!this.isIdent('random')) throw invalid(name) - this.index += 1 - if (this.kind() !== '(') throw invalid('Math.random') - this.index += 1 - if (this.kind() !== ')') throw invalid('Math.random') - this.index += 1 - return { v: Math.random(), int: false } - } - const value = this.variables.get(name) - if (!value) throw unknownVar(name, this.variables) - if (this.kind() === '.') { - this.index += 1 - if (!this.isIdent('length')) throw invalid(name) - this.index += 1 - if (this.kind() === '(') { - this.index += 1 - if (this.kind() !== ')') throw invalid(`${name}.length`) - this.index += 1 - } - if (isArrayValue(value)) return { v: ((value as { value: unknown[] }).value).length, int: true } - throw invalid(name) - } - if (this.kind() === '[') { - this.index += 1 - const elementIndex = Math.trunc(this.expression().v) - if (this.kind() !== ']') throw invalid(name) - this.index += 1 - if (value.kind === 'grid') { - if (!(elementIndex >= 0 && elementIndex < value.value.length)) throw invalid(`${name}[${elementIndex}]`) - const row = value.value[elementIndex] - if (this.kind() === '.') { - // `sitze[0].length`: Anzahl der Spalten in dieser Zeile - this.index += 1 - if (!this.isIdent('length')) throw invalid(name) - this.index += 1 - if (this.kind() === '(') { - this.index += 1 - if (this.kind() !== ')') throw invalid(`${name}.length`) - this.index += 1 - } - return { v: row.length, int: true } - } - if (this.kind() !== '[') throw invalid(name) - this.index += 1 - const column = Math.trunc(this.expression().v) - if (this.kind() !== ']') throw invalid(name) - this.index += 1 - if (!(column >= 0 && column < row.length)) throw invalid(`${name}[${elementIndex}][${column}]`) - if (value.elementType !== 'int' && value.elementType !== 'double') throw invalid(name) - return { v: row[column] as number, int: value.elementType === 'int' } - } - if (value.kind === 'intArray' || value.kind === 'doubleArray') { - if (!(elementIndex >= 0 && elementIndex < value.value.length)) throw invalid(`${name}[${elementIndex}]`) - return { v: value.value[elementIndex], int: value.kind === 'intArray' } - } - if (value.kind === 'stringArray') { - const code = charCode(value.value[elementIndex]) - if (code !== undefined) return { v: code, int: true } - } - throw invalid(name) - } - if (value.kind === 'string') { - const code = charCode(value.value) - if (code !== undefined) return { v: code, int: true } - } - const numeric = asDouble(value) - if (!Number.isFinite(numeric)) throw invalid(name) - return { v: numeric, int: value.kind === 'int' } - } - case '-': { - const inner = this.factor() - return { v: -inner.v, int: inner.int } - } - case '(': { - const value = this.expression() - if (this.kind() !== ')') throw invalid('') - this.index += 1 - return value - } - default: - throw invalid('') - } - } -} - -// MARK: - Schritte - -/** Vergleich oder Verknüpfung außerhalb von Texten (`a < b`, `!fertig`, `x == 3 && y`): ergibt true oder false. */ -function looksBoolean(expression: string): boolean { - const code = expression.replace(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/gu, '""') - return /==|!=|<|>|&&|\|\||^\s*!/u.test(code) -} - -function stripTrailingSemicolon(text: string): string { - let result = text.trim() - while (result.endsWith(';')) result = result.slice(0, -1).trim() - return result -} - -function paddedForParts(parts: string[]): string[] { - const result = parts.slice(0, 3) - while (result.length < 3) result.push('') - return result -} - -function normalizedForParts(rawParts: string[]): string[] { - const stripped = rawParts.map((p) => stripTrailingSemicolon(p).trim()) - if (stripped.length === 1 && stripped[0].includes(';')) { - return paddedForParts(stripped[0].split(';').map((p) => p.trim())) - } - return paddedForParts(stripped) -} - -type StepContext = Pick - -/** Aufrufebene eines Schritts, damit Rümpfe und Zweige im selben Unterprogramm bleiben. */ -const contextOf = (step: SimStep): StepContext => (step.frame ? { frame: step.frame, callPath: step.callPath } : {}) - -function buildSteps(elements: VSElement[], pathPrefix: number[] = [], context: StepContext = {}): SimStep[] { - const steps: SimStep[] = [] - const push = (step: SimStep) => steps.push(context.frame ? { ...step, ...context } : step) - elements.forEach((element, index) => { - const path = [...pathPrefix, index] - const text = element.textLines.join(' ').trim() - switch (element.type) { - case ElementType.anweisung: - // Leere Anweisung und Platzhalter „ø“ werden übersprungen - if (text !== '' && text !== 'ø') push({ path, text, kind: 'statement' }) - break - case ElementType.verzweigung: - push({ path, text: text || 'true', kind: 'branch', cases: element.cases }) - break - case ElementType.fallauswahl: - push({ path, text: text || '0', kind: 'switchSelection', cases: element.cases }) - break - case ElementType.forSchleife: - push({ - path, - text, - kind: 'forLoopStart', - loopBody: element.loopBody, - forParts: normalizedForParts(element.textLines), - }) - break - case ElementType.whileSchleife: - push({ path, text: text || 'true', kind: 'whileLoop', loopBody: element.loopBody }) - break - case ElementType.doUntilSchleife: - push({ path, text: text || 'true', kind: 'doUntilLoop', loopBody: element.loopBody }) - break - case ElementType.endlosschleife: - push({ path, text: text || 'true', kind: 'infiniteLoop', loopBody: element.loopBody }) - break - case ElementType.aussprung: - push({ path, text, kind: 'jump' }) - break - case ElementType.aufruf: - if (text !== '' && text !== 'ø') push({ path, text, kind: 'call' }) - break - } - }) - return steps -} - -function parseInputValue(rawValue: string, valueType: SimValueType): SimValue { - const trimmed = rawValue.trim().replaceAll(',', '.') - switch (valueType) { - case 'int': - if (!/^[+-]?[0-9]+$/.test(trimmed)) throw new SimulationError({ kind: 'wholeNumberExpected' }) - return int(Number(trimmed)) - case 'double': - if (!/^[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?$/.test(trimmed)) { - throw new SimulationError({ kind: 'numberExpected' }) - } - return dbl(Number(trimmed)) - case 'string': - return { kind: 'string', value: rawValue } - case 'boolean': - switch (trimmed.toLowerCase()) { - case 'true': case 'wahr': return { kind: 'boolean', value: true } - case 'false': case 'falsch': return { kind: 'boolean', value: false } - default: throw new SimulationError({ kind: 'booleanExpected' }) - } - } -} - -function freshState(): SimState { - return { stepIndex: 0, variables: new Map(), outputLines: [], isHalted: false, files: new Map(), robotActions: [] } -} - -type SwitchSelector = { kind: 'int'; value: number } | { kind: 'text'; value: string } - -function matchingCaseIndex(selector: SwitchSelector, cases: VSCase[]): number | undefined { - for (let i = 0; i < cases.length; i++) { - const label = cases[i].name.trim() - if (label.toLowerCase() === 'default') continue - if (selector.kind === 'int') { - if (/^[+-]?[0-9]+$/.test(label) && Number(label) === selector.value) return i - } else if (unquotedString(label) === selector.value || label === selector.value) { - return i - } - } - return undefined -} - -function isLoopContinuation(kind: StepKind): boolean { - return ( - kind === 'forLoopIncrement' || - kind === 'forLoopCondition' || - kind === 'whileLoop' || - kind === 'doUntilCondition' || - kind === 'infiniteLoop' - ) -} - -function sameStep(a: SimStep, b: SimStep): boolean { - return (a.frame ?? 0) === (b.frame ?? 0) && a.path.length === b.path.length && a.path.every((v, i) => v === b.path[i]) -} - -/** Kopfzeile eines Unterprogramms im Titel, z. B. `int quadrat(int x)` oder `void zeigeSumme(int a, int b)`. */ -export interface SubprogramHeader { - /** Fehlt bei `void`. */ - returnType?: SimValueType - name: string - /** `dims` steht bei Array-Parametern wie `int[] zahlen` (1) oder `boolean[][] sitze` (2). */ - params: { type: SimValueType; name: string; dims?: 1 | 2 }[] -} - -export function parseSubprogramHeader(text: string): SubprogramHeader | undefined { - const m = /^(void|int|double|String|boolean)\s+([A-Za-z_]\w*)\s*\((.*)\)$/u.exec(stripTrailingSemicolon(text)) - if (!m) return undefined - const params: SubprogramHeader['params'] = [] - if (m[3].trim() !== '') { - for (const part of m[3].split(',')) { - const p = /^(int|double|String|boolean|char)\s*((?:\[\s*\]){0,2})\s+([A-Za-z_]\w*)$/u.exec(part.trim()) - if (!p) return undefined - const dims = (p[2].match(/\[/gu) ?? []).length - params.push({ type: valueTypeKeyword(p[1])!, name: p[3], ...(dims > 0 ? { dims: dims as 1 | 2 } : {}) }) - } - } - return { returnType: valueTypeKeyword(m[1]), name: m[2], params } -} - -/** Text eines Aufruf-Kastens, z. B. `zeigeTitel()`, `zeigeSumme(3, 4)` oder `int y = quadrat(5)`. */ -interface CallText { - target?: string - targetType?: SimValueType - name: string - args: string[] -} - -function parseCallText(text: string): CallText | undefined { - const m = /^(?:(?:(int|double|String|boolean)\s+)?([A-Za-z_]\w*)\s*=\s*)?([A-Za-z_]\w*)\s*\((.*)\)$/u.exec( - stripTrailingSemicolon(text), - ) - if (!m) return undefined - return { - targetType: m[1] ? valueTypeKeyword(m[1]) : undefined, - target: m[2], - name: m[3], - args: m[4].trim() === '' ? [] : splitTopLevel(m[4], ','), - } -} - -/** `zeigeTitel()`, `zeigeTitel;`, `int y = quadrat(5)` und die Kopfzeile `int quadrat(int x)` liefern den Namen. */ -export function subprogramName(text: string): string { - const t = stripTrailingSemicolon(text) - return parseSubprogramHeader(t)?.name ?? parseCallText(t)?.name ?? t.replace(/\(\s*\)$/u, '').trim() -} - -/** Ein laufendes Unterprogramm mit Kopfzeile: eigene Variablen, die des Aufrufers warten in `saved`. */ -interface CallFrame { - saved: Map - header: SubprogramHeader - call?: CallText - returnValue?: SimValue - /** Array-Parameter: Änderungen gehen am Ende an diese Variablen des Aufrufers zurück (Referenzübergabe). */ - references: { param: string; callerName: string }[] -} - -// MARK: - Engine - -export class SimulationEngine { - steps: SimStep[] - state: SimState = freshState() - private nextFrame = 1 - private callDepth = 0 - private frames = new Map() - /** Beim Aufbereiten der Anzeige (`lastTrace`) lesen `nextLine()`/`readLine()` nur nach, ohne weiterzugehen. */ - private peeking = false - /** Messwerte des echten mBot2; fehlt ein Wert, gilt der simulierte aus `MBOT_SENSORS`. */ - robotSensors: MBotSensorValues = {} - /** Pseudocode: Kästen werden nur der Reihe nach gezeigt, Bedingungen beantwortet man selbst. */ - private pseudocode: boolean - /** Pseudocode: Antwort auf die gerade gestellte Frage (Bedingung: true/false, Fallauswahl: Name des Falls). */ - private decided?: boolean | string - - constructor( - document: VSDocument, - private resolveSubprogram: SubprogramResolver = () => undefined, - ) { - this.steps = buildSteps(document.elements) - this.pseudocode = !!document.pseudocode - } - - /** Im Unterprogramm bleibt der Aufruf-Kasten im Hauptprogramm markiert. */ - get currentStepPath(): number[] | undefined { - const step = this.steps[this.state.stepIndex] - return step?.callPath ?? step?.path - } - - get canStep(): boolean { - return !this.state.isHalted && !this.state.inputRequest && this.state.stepIndex < this.steps.length - } - - /** Englischer Text von `notice` (Tests). */ - get message(): string | undefined { - return this.state.notice && noticeEnglish(this.state.notice) - } - - reset(document?: VSDocument): void { - if (document) { - this.steps = buildSteps(document.elements) - this.pseudocode = !!document.pseudocode - } - this.decided = undefined - this.state = freshState() - this.afterOutput = false - this.nextFrame = 1 - this.callDepth = 0 - this.frames.clear() - } - - step(): void { - const state = this.state - if (!this.canStep) { - if (!state.inputRequest && !state.isHalted) state.notice = { kind: 'finished' } - return - } - const step = this.steps[state.stepIndex] - try { - switch (step.kind) { - case 'statement': - // Pseudocode ist kein Programm: Der Kasten wird nur gezeigt, nicht ausgeführt. - if (this.pseudocode) { - state.lastTrace = step.text - break - } - if (/^return\b/u.test(step.text)) return this.executeReturn(step) - this.executeStatement(step.text) - break - case 'branch': return this.executeBranch(step) - case 'switchSelection': return this.executeSwitchSelection(step) - case 'forLoopStart': return this.executeForLoopStart(step) - case 'forLoopCondition': return this.executeForLoopCondition(step) - case 'forLoopIncrement': return this.executeForLoopIncrement(step) - case 'whileLoop': return this.executeWhileLoop(step) - case 'doUntilLoop': return this.enterDoUntilLoop(step) - case 'doUntilCondition': return this.executeDoUntilCondition(step) - case 'infiniteLoop': return this.enterInfiniteLoop(step) - case 'jump': return this.executeJump(step) - case 'call': return this.executeCall(step) - case 'callEnd': return this.executeCallEnd(step) - } - if (state.inputRequest) { - state.notice = undefined - return - } - state.stepIndex += 1 - state.notice = this.finishedIfAtEnd() - } catch (error) { - state.notice = toNotice(error) - state.isHalted = true - } - } - - provideInput(rawValue: string): void { - const request = this.state.inputRequest - if (!request) return - if (request.decision) return this.provideDecision(rawValue) - try { - const value = parseInputValue(rawValue, request.valueType) - this.assignValue(value, request.variableName) - this.state.lastTrace = `${request.variableName} = input -> ${describeValue(value)}` - } catch (error) { - this.state.inputErrorNotice = toNotice(error) - return - } - this.state.inputRequest = undefined - this.state.inputErrorNotice = undefined - this.state.stepIndex += 1 - this.state.notice = this.finishedIfAtEnd() - } - - /** Pseudocode: Antwort auf die Frage zur Bedingung, dann gleich den Weg nehmen (die Bedingung ist der Schritt). */ - private provideDecision(rawValue: string): void { - if (this.state.inputRequest?.valueType === 'string') { - this.state.inputRequest = undefined - this.decided = rawValue.trim() - return this.step() - } - const answer = rawValue.trim().toLowerCase() - const yes = ['true', 'wahr', 'ja', 'yes', 'sim', 'verdadeiro'].includes(answer) - const no = ['false', 'falsch', 'nein', 'no', 'não', 'nao', 'falso'].includes(answer) - if (!yes && !no) { - this.state.inputErrorNotice = { kind: 'booleanExpected' } - return - } - this.state.inputRequest = undefined - this.state.inputErrorNotice = undefined - this.decided = yes - this.step() - } - - /** - * Wert einer Bedingung. Bei Pseudocode (z. B. „Regnet es?“) fragt die Simulation: `undefined`, bis die Antwort da ist. - */ - private condition(condition: string): boolean | undefined { - if (!this.pseudocode) return this.evaluateBool(condition) - const answer = this.decided - if (typeof answer === 'boolean') { - this.decided = undefined - return answer - } - this.state.inputRequest = { variableName: '', valueType: 'boolean', prompt: `${condition} (true/false)`, decision: true } - this.state.inputErrorNotice = undefined - this.state.notice = undefined - return undefined - } - - private finishedIfAtEnd(): SimNotice | undefined { - return this.state.stepIndex >= this.steps.length ? { kind: 'finished' } : undefined - } - - /** Ersetzt den aktuellen Schritt durch `replacement`. */ - private splice(replacement: SimStep[]): void { - this.steps.splice(this.state.stepIndex, 1, ...replacement) - } - - private noticeAfterEntering(bodySteps: SimStep[]): SimNotice | undefined { - return bodySteps.length > 0 ? undefined : this.finishedIfAtEnd() - } - - private executeBranch(step: SimStep): void { - const cases = step.cases - if (!cases || cases.length < 2) throw unsupported(step.text) - const condition = stripConditionKeyword(step.text) - const result = this.condition(condition) - if (result === undefined) return - const selected = result ? 0 : 1 - const branchSteps = buildSteps(cases[selected].elements, [...step.path, selected], contextOf(step)) - this.splice(branchSteps) - this.state.lastTrace = `${condition} -> ${result}` - this.state.notice = this.noticeAfterEntering(branchSteps) - } - - private executeSwitchSelection(step: SimStep): void { - const cases = step.cases - if (!cases || cases.length === 0) throw unsupported(step.text) - const selector = stripConditionKeyword(step.text) - let selected: number - if (this.pseudocode) { - // Pseudocode: Man wählt den Fall selbst; unbekannte Antwort = letzter Fall (sonst). - const answer = this.decided - if (typeof answer !== 'string') { - const names = cases.map((c) => c.name).join(' / ') - this.state.inputRequest = { variableName: '', valueType: 'string', prompt: `${selector} (${names})`, decision: true } - this.state.notice = undefined - return - } - this.decided = undefined - const index = cases.findIndex((c) => c.name.trim().toLowerCase() === answer.toLowerCase()) - selected = index >= 0 ? index : cases.length - 1 - } else selected = matchingCaseIndex(this.evaluateSwitchSelector(selector), cases) ?? cases.length - 1 - const caseSteps = buildSteps(cases[selected].elements, [...step.path, selected], contextOf(step)) - this.splice(caseSteps) - this.state.lastTrace = `${selector} -> ${cases[selected].name}` - this.state.notice = this.noticeAfterEntering(caseSteps) - } - - private executeForLoopStart(step: SimStep): void { - // Pseudocode wie „für jeden Gast“: eine Frage wie bei der kopfgesteuerten Schleife. - if (this.pseudocode) return this.splice([{ ...step, kind: 'whileLoop' }]) - const parts = normalizedForParts(step.forParts ?? [step.text]) - if (parts[0] !== '') this.executeStatement(parts[0]) - this.splice([{ ...contextOf(step), path: step.path, text: parts[1], kind: 'forLoopCondition', loopBody: step.loopBody, forParts: parts }]) - } - - private executeForLoopCondition(step: SimStep): void { - const parts = normalizedForParts(step.forParts ?? [step.text]) - const condition = parts[1] === '' ? 'true' : parts[1] - const shouldEnter = this.condition(condition) - if (shouldEnter === undefined) return - this.state.lastTrace = `${condition} -> ${shouldEnter}` - if (shouldEnter) { - const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step)) - this.splice([ - ...bodySteps, - { ...contextOf(step), path: step.path, text: parts[2], kind: 'forLoopIncrement', loopBody: step.loopBody, forParts: parts }, - { ...contextOf(step), path: step.path, text: parts[1], kind: 'forLoopCondition', loopBody: step.loopBody, forParts: parts }, - ]) - } else { - this.splice([]) - this.state.notice = this.finishedIfAtEnd() - } - } - - private executeForLoopIncrement(step: SimStep): void { - const increment = step.text.trim() - if (increment !== '') this.executeIncrement(increment) - this.splice([]) - } - - private executeWhileLoop(step: SimStep): void { - const condition = stripConditionKeyword(step.text) - const shouldEnter = this.condition(condition) - if (shouldEnter === undefined) return - this.state.lastTrace = `${condition} -> ${shouldEnter}` - if (shouldEnter) { - const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step)) - this.splice([...bodySteps, { ...contextOf(step), path: step.path, text: step.text, kind: 'whileLoop', loopBody: step.loopBody }]) - this.state.notice = this.noticeAfterEntering(bodySteps) - } else { - this.splice([]) - this.state.notice = this.finishedIfAtEnd() - } - } - - private enterDoUntilLoop(step: SimStep): void { - const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step)) - this.splice([...bodySteps, { ...contextOf(step), path: step.path, text: step.text, kind: 'doUntilCondition', loopBody: step.loopBody }]) - this.state.notice = this.noticeAfterEntering(bodySteps) - } - - private executeDoUntilCondition(step: SimStep): void { - const condition = stripConditionKeyword(step.text) - const shouldRepeat = this.condition(condition) - if (shouldRepeat === undefined) return - this.state.lastTrace = `${condition} -> ${shouldRepeat}` - if (shouldRepeat) { - const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step)) - this.splice([...bodySteps, { ...contextOf(step), path: step.path, text: step.text, kind: 'doUntilCondition', loopBody: step.loopBody }]) - } else { - this.splice([]) - this.state.notice = this.finishedIfAtEnd() - } - } - - private enterInfiniteLoop(step: SimStep): void { - const bodySteps = buildSteps(step.loopBody ?? [], [...step.path, 0], contextOf(step)) - this.splice([...bodySteps, { ...contextOf(step), path: step.path, text: step.text, kind: 'infiniteLoop', loopBody: step.loopBody }]) - this.state.lastTrace = 'true -> true' - this.state.notice = bodySteps.length > 0 ? undefined : { kind: 'infiniteLoopWithoutBody' } - } - - // MARK: Aussprung und Aufruf - - /** - * Wie `break`: verlässt die innerste Schleife, in der der Aussprung steht. Außerhalb jeder Schleife endet - * das Unterprogramm bzw. im Hauptprogramm das ganze Programm. - */ - private executeJump(step: SimStep): void { - const frame = step.frame ?? 0 - const isPrefix = (p: number[]) => p.length < step.path.length && p.every((v, i) => v === step.path[i]) - let end = this.steps.length - let outcome: 'break' | 'return' | 'end' = 'end' - for (let i = this.state.stepIndex + 1; i < this.steps.length; i++) { - const s = this.steps[i] - if ((s.frame ?? 0) !== frame) continue - if (s.kind === 'callEnd') { - end = i + 1 - outcome = 'return' - break - } - if (isLoopContinuation(s.kind) && isPrefix(s.path)) { - end = i + 1 - // Bei der For-Schleife folgt auf das Hochzählen noch die Bedingung - while (end < this.steps.length && sameStep(this.steps[end], s)) end += 1 - outcome = 'break' - break - } - } - const callEnd = this.steps[end - 1] - if (outcome === 'return') this.callDepth -= 1 - this.steps.splice(this.state.stepIndex, end - this.state.stepIndex) - this.state.lastTrace = `${step.text || 'break'} -> ${outcome}` - if (outcome === 'return') this.finishFrame(callEnd) - this.state.notice = this.finishedIfAtEnd() - } - - /** Führt das Diagramm mit dem Namen aus dem Aufruf-Kasten aus und macht danach hier weiter. */ - private executeCall(step: SimStep): void { - const name = step.text - const document = this.resolveSubprogram(subprogramName(name)) - if (!document) { - this.splice([]) - this.state.lastTrace = `${name} -> ?` - this.state.notice = { kind: 'subprogramNotFound', text: name } - if (this.state.stepIndex >= this.steps.length) this.state.notice = { kind: 'finished' } - return - } - const call = parseCallText(name) - const header = parseSubprogramHeader(document.caption) - const argumentsMismatch = header - ? (call?.args.length ?? 0) !== header.params.length || (call?.target !== undefined && !header.returnType) - : call !== undefined && (call.args.length > 0 || call.target !== undefined) - if (argumentsMismatch) throw new SimulationError({ kind: 'callArguments', text: header?.name ?? subprogramName(name) }) - if (this.callDepth >= maxCallDepth) throw new SimulationError({ kind: 'callTooDeep' }) - // Die Werte werden noch mit den Variablen des Aufrufers ausgerechnet (Wertübergabe). - // Arrays werden dagegen als Referenz übergeben: Das Unterprogramm arbeitet mit demselben Array. - const locals = new Map() - const references: CallFrame['references'] = [] - header?.params.forEach((p, i) => { - const arg = call!.args[i].trim() - if (!p.dims) { - locals.set(p.name, this.evaluateExpression(arg, p.type)) - return - } - const value = this.state.variables.get(arg) - if (!value) { - if (/^[A-Za-z_]\w*$/u.test(arg)) throw unknownVar(arg, this.state.variables) - throw invalid(arg) - } - const matches = - p.dims === 1 ? value.kind === `${p.type}Array` : value.kind === 'grid' && value.elementType === p.type - if (!matches) throw invalid(arg) - locals.set(p.name, value) - references.push({ param: p.name, callerName: arg }) - }) - this.callDepth += 1 - const context: StepContext = { frame: this.nextFrame++, callPath: step.callPath ?? step.path } - if (header) { - this.frames.set(context.frame!, { saved: this.state.variables, header, call, references }) - this.state.variables = locals - } - const bodySteps = buildSteps(document.elements, [], context) - this.splice([...bodySteps, { ...context, path: step.path, text: name, kind: 'callEnd' }]) - const passed = header?.params - .map((p, i) => `${p.name} = ${p.dims ? call!.args[i].trim() : describeValue(locals.get(p.name)!)}`) - .join(', ') - this.state.lastTrace = passed ? `${name} -> ${passed}` : `${name} -> call` - } - - private executeCallEnd(step: SimStep): void { - this.callDepth -= 1 - this.splice([]) - this.state.lastTrace = `${step.text} -> return` - this.finishFrame(step) - this.state.notice = this.finishedIfAtEnd() - } - - /** `return` beendet das Unterprogramm sofort, im Hauptprogramm das ganze Programm. */ - private executeReturn(step: SimStep): void { - const frameNumber = step.frame ?? 0 - const expression = stripTrailingSemicolon(step.text).slice('return'.length).trim() - const frame = this.frames.get(frameNumber) - if (frame && expression !== '') { - if (!frame.header.returnType) throw invalid(step.text) - frame.returnValue = this.evaluateExpression(expression, frame.header.returnType) - } - const trace = frame?.returnValue ? `${step.text} -> ${describeValue(frame.returnValue)}` : `${step.text} -> return` - if (frameNumber === 0) { - this.steps.splice(this.state.stepIndex) - this.state.lastTrace = trace - this.state.notice = { kind: 'finished' } - return - } - let end = this.state.stepIndex + 1 - while (end < this.steps.length && !(this.steps[end].kind === 'callEnd' && this.steps[end].frame === frameNumber)) end++ - const callEnd = this.steps[end] - this.steps.splice(this.state.stepIndex, end - this.state.stepIndex + 1) - this.callDepth -= 1 - this.state.lastTrace = trace - if (callEnd) this.finishFrame(callEnd) - this.state.notice = this.finishedIfAtEnd() - } - - /** Zurück beim Aufrufer: seine Variablen wiederherstellen und den Rückgabewert zuweisen. */ - private finishFrame(callEnd: SimStep): void { - const frame = this.frames.get(callEnd.frame ?? 0) - if (!frame) return - this.frames.delete(callEnd.frame ?? 0) - const restored = frame.references.length > 0 ? new Map(frame.saved) : frame.saved - for (const ref of frame.references) { - const value = this.state.variables.get(ref.param) - if (value) restored.set(ref.callerName, value) - } - this.state.variables = restored - const result = frame.returnValue - if (frame.header.returnType && !result) throw new SimulationError({ kind: 'missingReturn', text: frame.header.name }) - const target = frame.call?.target - if (!target || !result) return - let type = frame.call?.targetType - if (!type) { - try { - type = this.inferTargetValueType(target) - } catch { - type = frame.header.returnType! - } - } - const value = - type === 'double' && result.kind === 'int' ? dbl(result.value) - : type === 'string' ? { kind: 'string' as const, value: describeValue(result) } - : result - if (valueTypeOf(value) !== type) throw invalid(callEnd.text) - this.assignValue(value, target) - this.state.lastTrace = `${target} = ${callEnd.text.slice(callEnd.text.indexOf('=') + 1).trim()} -> ${describeValue(value)}` - } - - // MARK: Anweisungen - - /** Die letzte Anweisung war eine Ausgabe (für die Frage einer folgenden Eingabe). */ - private afterOutput = false - - private executeStatement(rawText: string): void { - const state = this.state - const text = rawText.trim() - const lower = text.toLowerCase() - const afterOutput = this.afterOutput - this.afterOutput = false - if (lower.startsWith('input:')) { - state.inputRequest = this.parseInputRequest(text, afterOutput) - // Mit Enter endet die Zeile. - state.outputLineOpen = false - state.inputErrorNotice = undefined - return - } - - // `System.out.print(…)` schreibt ohne neue Zeile, `System.out.println(…)` wie `output:`. - const javaOut = /^System\s*\.\s*out\s*\.\s*(println|print)\s*\(([\s\S]*)\)\s*;?$/u.exec(text) - if (lower.startsWith('output:') || lower.startsWith('print:') || javaOut) { - const expression = javaOut ? javaOut[2] : text.slice(lower.startsWith('output:') ? 'output:'.length : 'print:'.length) - const rendered = this.peek(() => this.renderOutputExpression(expression)) - // `println()` ohne Text: nur eine neue Zeile. - const output = expression.trim() === '' ? '' : this.evaluateOutputExpression(expression) - // `\n` im Text beginnt eine neue Zeile; `print("…\n")` endet wie `println("…")`. - const print = javaOut?.[1] === 'print' - const endsLine = print && output.endsWith('\n') - const [first, ...rest] = (endsLine ? output.slice(0, -1) : output).split('\n') - if (state.outputLineOpen && state.outputLines.length) state.outputLines[state.outputLines.length - 1] += first - else state.outputLines.push(first) - state.outputLines.push(...rest) - state.outputLineOpen = print && !endsLine - this.afterOutput = true - state.lastTrace = `${rendered} -> ${output}` - return - } - - const robot = parseMBotCommand(text) - if (robot) { - this.executeMBotCommand(robot.method, robot.args, text) - return - } - - if (this.executeFileStatement(text)) return - - if (text.endsWith('++') || text.endsWith('--') || text.includes('+=') || text.includes('-=')) { - this.executeIncrement(text) - return - } - - // `boolean fertig;` oder `int a, b;`: Variable ohne Startwert, sie bekommt den Standardwert wie ein Java-Attribut. - const declared = /^(?:final\s+)?([A-Za-z]+)\s+([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)$/u.exec(stripTrailingSemicolon(text).trim()) - const declaredType = declared ? valueTypeKeyword(declared[1]) : undefined - if (declared && declaredType) { - const names = declared[2].split(',').map((n) => n.trim()) - for (const name of names) state.variables.set(name, elementValue(declaredType, elementDefault(declaredType))) - state.lastTrace = names.map((n) => `${n} = ${describeValue(state.variables.get(n)!)}`).join(', ') - return - } - - const equalsIndex = text.indexOf('=') - if (equalsIndex < 0) throw unsupported(text) - const left = text.slice(0, equalsIndex).trim() - const expression = text.slice(equalsIndex + 1) - const array = this.parseArrayDeclaration(left, expression) - if (array) { - state.variables.set(array.name, array.value) - state.lastTrace = `${array.name} = new ${array.typeName}[${array.count}]` - return - } - - const target = this.parseAssignmentTarget(left) - let rendered: string - try { - rendered = this.peek(() => this.renderNumericExpression(expression)) - } catch { - rendered = expression.trim() - } - let targetType = target.valueType - if (!targetType) { - try { - targetType = this.inferTargetValueType(target.variableName) - } catch { - targetType = 'int' - } - } - const value = this.evaluateExpression(expression, targetType) - this.assignValue(value, target.variableName) - state.lastTrace = `${target.variableName} = ${rendered} -> ${describeValue(value)}` - } - - /** `mbot.forward(10)`: Befehl merken; die VisuBot-Welt (und später der echte Roboter) fährt damit. */ - private executeMBotCommand(method: string, args: string[], text: string): void { - // `mbot.showText("Abstand: " + mbot.getDistance())`: Text wie bei System.out.println - if (MBOT_COMMANDS[method]?.text) { - const spec = MBOT_COMMANDS[method] - if (args.length !== spec.args + 1) throw invalid(text) - const numbers = mbotArguments(method, args.slice(0, -1).map((a) => this.evaluateDouble(a))) - if (!numbers) throw invalid(text) - const action = makeMBotAction(method, numbers, this.evaluateOutputExpression(args[args.length - 1])) - const choices = spec.choices - if (choices && !choices.includes(action.text ?? '')) throw invalid(`${text} (${choices.join(', ')})`) - this.state.robotActions.push(action) - this.state.lastTrace = `${text} -> ${action.label}` - return - } - // Überladung `mbot.forward(2, "Umdrehungen")`: gleicher Name, zweiter Parameter ist die Einheit - if ((method === 'forward' || method === 'backward') && args.length === 2) { - const unit = this.evaluateOutputExpression(args[1]) - const action = makeMBotDriveAction(method, this.evaluateDouble(args[0]), unit) - if (!action) throw invalid(`${text} (${MBOT_DRIVE_UNITS.map((u) => `"${u}"`).join(', ')})`) - this.state.robotActions.push(action) - this.state.lastTrace = `${text} -> ${action.label}` - return - } - const values = mbotArguments(method, args.map((a) => this.evaluateDouble(a))) - if (!values) throw invalid(text) - const action = makeMBotAction(method, values) - this.state.robotActions.push(action) - this.state.lastTrace = `${text} -> ${action.label}` - } - - /** - * Dateien: `Scanner datei = new Scanner(new File("a.txt"))`, `new BufferedReader(new FileReader(…))`, - * `new PrintWriter("a.txt")`, `aus.println(…)`, `aus.print(…)`, `x.close()` und `Files.writeString(Path.of(…), text)`. - */ - private executeFileStatement(text: string): boolean { - const state = this.state - const statement = stripTrailingSemicolon(text).trim() - const made = /^(?:(Scanner|BufferedReader|PrintWriter)\s+)?([A-Za-z_]\w*)\s*=\s*new\s+(Scanner|BufferedReader|PrintWriter)\s*\(([\s\S]*)\)$/u.exec(statement) - if (made) { - const [, declared, name, className, inner] = made - if (declared && declared !== className) throw invalid(statement) - const source = - className === 'Scanner' ? /^new\s+File\s*\(([\s\S]+)\)$/u.exec(inner.trim()) - : className === 'BufferedReader' ? /^new\s+FileReader\s*\(([\s\S]+)\)$/u.exec(inner.trim()) - : /^(?:new\s+(?:File|FileWriter)\s*\(([\s\S]+)\)|([\s\S]+))$/u.exec(inner.trim()) - if (!source) throw unsupported(statement) - const file = this.fileNameValue(source[1] ?? source[2]) - if (className === 'PrintWriter') { - state.variables.set(name, { kind: 'writer', file, text: '' }) - } else { - const content = this.fileContent(file) - const lines = content.split(/\r?\n/u) - if (lines[lines.length - 1] === '') lines.pop() - state.variables.set(name, { kind: 'reader', className: className as 'Scanner', file, lines, next: 0 }) - } - state.lastTrace = `${name} = new ${className}(${file})` - return true - } - const files = /^Files\s*\.\s*writeString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(([^()]+)\)\s*,([\s\S]+)\)$/u.exec(statement) - if (files) { - const file = this.fileNameValue(files[1]) - const content = this.evaluateOutputExpression(files[2]) - state.files.set(file, content) - state.lastTrace = `${file} ← ${JSON.stringify(content)}` - return true - } - const call = /^([A-Za-z_]\w*)\s*\.\s*(println|print|close)\s*\(([\s\S]*)\)$/u.exec(statement) - const object = call && state.variables.get(call[1]) - if (!call || !object) return false - if (object.kind === 'null') throw new SimulationError({ kind: 'nullValue', text: call[1] }) - if (object.kind === 'reader' && call[2] === 'close') { - state.lastTrace = `${call[1]}.close()` - return true - } - if (object.kind !== 'writer') return false - if (call[2] === 'close') { - state.files.set(object.file, object.text) - state.lastTrace = `${object.file} ← ${JSON.stringify(object.text)}` - return true - } - const value = call[3].trim() === '' ? '' : this.evaluateOutputExpression(call[3]) - const added = call[2] === 'println' ? value + '\n' : value - state.variables.set(call[1], { ...object, text: object.text + added }) - state.lastTrace = `${call[1]}.${call[2]}(${JSON.stringify(value)})` - return true - } - - /** Name einer Datei: `"a.txt"` oder eine String-Variable. */ - private fileNameValue(text: string): string { - const trimmed = text.trim() - const literal = unquotedString(trimmed) - if (literal !== undefined) return literal - const ref = this.evaluateReference(trimmed) - if (ref?.kind === 'string') return ref.value - throw invalid(trimmed) - } - - /** Inhalt einer Datei: zuerst, was das Programm geschrieben hat, sonst die Datei der Lektion. */ - private fileContent(name: string): string { - const content = this.state.files.get(name) ?? textFiles.get(name) - if (content === undefined) throw new SimulationError({ kind: 'fileNotFound', text: name }) - return content - } - - /** `datei.hasNextLine()`, `datei.nextLine()` und `leser.readLine()`. */ - private readerMethod(name: string, reader: SimValue & { kind: 'reader' }, call: { method: string; args: string[] }, text: string): SimValue | undefined { - if (call.args.length) throw invalid(text) - const left = reader.next < reader.lines.length - if (call.method === 'hasNextLine' && reader.className === 'Scanner') return { kind: 'boolean', value: left } - const reads = (call.method === 'nextLine' && reader.className === 'Scanner') || (call.method === 'readLine' && reader.className === 'BufferedReader') - if (!reads) return undefined - let value: SimValue - if (left) value = { kind: 'string', value: reader.lines[reader.next] } - else if (reader.className === 'BufferedReader') value = { kind: 'null' } - else throw new SimulationError({ kind: 'endOfFile', text: reader.file }) - if (!this.peeking) this.state.variables.set(name, { ...reader, next: Math.min(reader.next + 1, reader.lines.length) }) - return value - } - - private peek(render: () => T): T { - this.peeking = true - try { - return render() - } finally { - this.peeking = false - } - } - - private executeIncrement(text: string): void { - const vars = this.state.variables - for (const [suffix, sign] of [['++', 1], ['--', -1]] as const) { - if (!text.endsWith(suffix)) continue - const name = text.slice(0, -2).trim() - const old = vars.get(name) - if (!isIdentifier(name) || !old) throw unsupported(text) - const op = sign > 0 ? '+' : '-' - if (old.kind === 'int') { - vars.set(name, int(old.value + sign)) - this.state.lastTrace = `${name} = ${old.value} ${op} 1 -> ${old.value + sign}` - return - } - if (old.kind === 'double') { - const result = old.value + sign - vars.set(name, dbl(result)) - this.state.lastTrace = `${name} = ${describeValue(old)} ${op} 1 -> ${describeValue(dbl(result))}` - return - } - throw unsupported(text) - } - - for (const [operator, sign] of [['+=', 1], ['-=', -1]] as const) { - const at = text.indexOf(operator) - if (at < 0) continue - const name = text.slice(0, at).trim() - const rhs = text.slice(at + 2) - const old = vars.get(name) - if (!isIdentifier(name) || !old) throw unsupported(text) - const op = sign > 0 ? '+' : '-' - if (old.kind === 'int') { - const amount = this.evaluateInt(rhs) - const result = old.value + sign * amount - vars.set(name, int(result)) - this.state.lastTrace = `${name} = ${old.value} ${op} ${amount} -> ${result}` - return - } - if (old.kind === 'double') { - const amount = this.evaluateDouble(rhs) - const result = old.value + sign * amount - vars.set(name, dbl(result)) - this.state.lastTrace = `${name} = ${describeValue(old)} ${op} ${describeValue(dbl(amount))} -> ${describeValue(dbl(result))}` - return - } - throw unsupported(text) - } - - this.executeStatement(text) - } - - private parseAssignmentTarget(text: string): { variableName: string; valueType?: SimValueType } { - // Ein Arrayfach darf Leerzeichen im Index haben: `karten[i + 1] = hilf`. - if (parseArrayElementAccess(text)) return { variableName: text.trim() } - const parts = text.split(/\s+/u).filter(Boolean) - let name: string - let valueType: SimValueType | undefined - if (parts.length === 1) { - name = parts[0] - } else if (parts.length === 2 && valueTypeKeyword(parts[0])) { - name = parts[1] - valueType = valueTypeKeyword(parts[0]) - } else if (parts.length === 3 && parts[0] === 'const' && valueTypeKeyword(parts[1])) { - name = parts[2] - valueType = valueTypeKeyword(parts[1]) - } else { - throw unsupported(text) - } - if (!isAssignableTarget(name)) throw unsupported(text) - return { variableName: name, valueType } - } - - /** - * Ohne eigene Frage steht `name:` vor der Eingabe. Kommt direkt davor eine Ausgabe, fragt diese schon (wie in Java, - * z. B. `System.out.println("Note für Fach " + fach[i])`): Dann steht nichts davor, nach `System.out.print(…)` steht - * die Eingabe in derselben Zeile. - */ - private parseInputRequest(text: string, afterOutput = false): InputRequest { - const afterColon = text.slice('input:'.length).trim() - // Wie Swift `split(maxSplits: 2)`: zwei Wörter, dann der Rest. - const match = /^(\S+)(?:\s+(\S+))?(?:\s+([\s\S]*))?$/u.exec(afterColon) - if (!match) throw unsupported(text) - const parts = [match[1], match[2], match[3]].filter((p): p is string => p !== undefined) - let target: string - let valueType: SimValueType - let promptRaw: string - const explicitType = valueTypeKeyword(parts[0]) - if (parts.length >= 2 && explicitType) { - target = parts[1] - valueType = explicitType - promptRaw = (parts[2] ?? '').trim() - } else { - target = parts[0] - valueType = this.inferTargetValueType(target) - promptRaw = (parts[1] ?? '').trim() - } - if (!isAssignableTarget(target)) throw unsupported(text) - const prompt = unquotedString(promptRaw) ?? promptRaw - if (prompt === '' && afterOutput) { - return this.state.outputLineOpen ? { variableName: target, valueType, prompt: '', continuesLine: true } : { variableName: target, valueType, prompt: '' } - } - return { variableName: target, valueType, prompt: prompt === '' ? `${target}:` : prompt } - } - - private parseArrayDeclaration( - left: string, - expression: string, - ): { name: string; typeName: string; count: number | string; value: SimValue } | undefined { - const leftParts = left.split(/\s+/u).filter(Boolean) - if (leftParts.length !== 2 || !leftParts[0].endsWith('[]')) return undefined - const twoDimensional = leftParts[0].endsWith('[][]') - const typeName = leftParts[0].slice(0, twoDimensional ? -4 : -2) - const valueType = valueTypeKeyword(typeName) - const name = leftParts[1] - if (!valueType || !isIdentifier(name)) throw unsupported(left) - if (twoDimensional) return this.parseGridDeclaration(name, typeName, valueType, expression) - - const trimmed = expression.trim() - if (trimmed.startsWith('{') && trimmed.endsWith('}')) { - const items = trimmed.slice(1, -1).split(',').map((s) => s.trim()) - let value: SimValue - switch (valueType) { - case 'int': - value = { kind: 'intArray', value: items.map((i) => this.evaluateInt(i)) } - break - case 'double': - value = { kind: 'doubleArray', value: items.map((i) => this.evaluateDouble(i)) } - break - case 'string': - value = { kind: 'stringArray', value: items.map((i) => unquotedString(i) ?? this.evaluateOutputExpression(i)) } - break - case 'boolean': - value = { kind: 'booleanArray', value: items.map((i) => this.evaluateBool(i)) } - break - } - return { name, typeName, count: items.length, value } - } - - const prefix = `new ${typeName}[` - if (!trimmed.startsWith(prefix)) { - // z. B. `String[] woerter = satz.split(" ")` - const value = this.evaluateStringMethod(trimmed) - if (value?.kind === 'stringArray' && valueType === 'string') return { name, typeName, count: value.value.length, value } - } - if (!trimmed.startsWith(prefix) || !trimmed.endsWith(']')) throw unsupported(expression) - const sizeExpression = trimmed.slice(prefix.length, -1) - const count = this.evaluateInt(sizeExpression) - if (count < 0) throw invalid(sizeExpression) - const value: SimValue = - valueType === 'int' - ? { kind: 'intArray', value: new Array(count).fill(0) } - : valueType === 'double' - ? { kind: 'doubleArray', value: new Array(count).fill(0) } - : valueType === 'boolean' - ? { kind: 'booleanArray', value: new Array(count).fill(false) } - : { kind: 'stringArray', value: new Array(count).fill('') } - return { name, typeName, count, value } - } - - /** `int[][] a = new int[3][4]` oder `int[][] a = {{1, 2}, {3, 4}}`. */ - private parseGridDeclaration( - name: string, - typeName: string, - elementType: SimValueType, - expression: string, - ): { name: string; typeName: string; count: number | string; value: SimValue } { - const trimmed = expression.trim() - const element = (text: string): number | string | boolean => { - switch (elementType) { - case 'int': return this.evaluateInt(text) - case 'double': return this.evaluateDouble(text) - case 'boolean': return this.evaluateBool(text) - case 'string': return unquotedString(text.trim()) ?? this.evaluateOutputExpression(text) - } - } - if (trimmed.startsWith('{') && trimmed.endsWith('}')) { - const rows = splitTopLevelBraces(trimmed.slice(1, -1)).map((row) => { - const r = row.trim() - if (!r.startsWith('{') || !r.endsWith('}')) throw invalid(r) - return r.slice(1, -1).trim() === '' ? [] : r.slice(1, -1).split(',').map((x) => element(x.trim())) - }) - return { name, typeName, count: `${rows.length}][${rows[0]?.length ?? 0}`, value: { kind: 'grid', elementType, value: rows } } - } - const g = new RegExp(String.raw`^new\s+${typeName}\s*\[(.+)\]\s*\[(.+)\]$`, 'u').exec(trimmed) - if (!g) throw unsupported(expression) - const rows = this.evaluateInt(g[1]) - const columns = this.evaluateInt(g[2]) - if (rows < 0) throw invalid(g[1]) - if (columns < 0) throw invalid(g[2]) - const value: SimValue = { - kind: 'grid', - elementType, - value: Array.from({ length: rows }, () => new Array(columns).fill(elementDefault(elementType))), - } - return { name, typeName, count: `${rows}][${columns}`, value } - } - - private arrayIndex(expression: string): number { - const index = this.evaluateInt(expression) - if (index < 0) throw invalid(expression) - return index - } - - private evaluateReference(text: string): SimValue | undefined { - const trimmed = text.trim() - const direct = this.state.variables.get(trimmed) - if (direct) return direct - const call = this.evaluateStringMethod(trimmed) - if (call) return call - // `(char) code` wie in Java: aus dem Zeichencode wird wieder ein Zeichen - const charCast = /^\(\s*char\s*\)\s*(.+)$/su.exec(trimmed) - if (charCast) { - const code = this.evaluateInt(charCast[1]) - if (!(code >= 0 && code <= 0x10ffff)) throw invalid(trimmed) - return { kind: 'string', value: String.fromCodePoint(code) } - } - const access = parseArrayElementAccess(trimmed) - if (!access) return undefined - return this.valueInArray(access.arrayName, access.indexExpressions.map((e) => this.arrayIndex(e))) - } - - /** - * String-Methoden wie in Java: `wort.length()`, `wort.charAt(i)`, `wort.substring(a, b)`, `wort.indexOf("x")`, - * `wort.toUpperCase()`, `wort.toLowerCase()`, `wort.trim()`, `wort.contains("x")`, `wort.startsWith("x")`, - * `wort.endsWith("x")`, `wort.replace("a", "b")`, `satz.split(" ")`, `wort.lastIndexOf("x")`, `wort.isEmpty()`, - * `wort.equalsIgnoreCase("x")`, `wort.compareTo("x")`, `wort.repeat(3)`, `wort.concat("x")`, dazu - * `Integer.parseInt("12")`, `Double.parseDouble("1.5")`, `String.valueOf(12)` und - * `Files.readString(Path.of("brief.txt"))`. Sonst `undefined`. - */ - private evaluateStringMethod(text: string): SimValue | undefined { - const call = parseMethodCall(text) - if (!call) return undefined - const staticValue = this.evaluateStaticCall(text, call) - if (staticValue) return staticValue - const object = this.state.variables.get(call.target.trim()) - if (object?.kind === 'reader') return this.readerMethod(call.target.trim(), object, call, text) - if (object?.kind === 'null') throw new SimulationError({ kind: 'nullValue', text: call.target.trim() }) - const target = unquotedString(call.target) ?? this.stringValueOf(call.target) - if (target === undefined) return undefined - const args = call.args - const str = (i: number) => { - const a = args[i] - if (a === undefined) throw invalid(text) - const literal = unquotedString(a.trim()) - if (literal !== undefined) return literal - const ref = this.evaluateReference(a) - if (ref?.kind === 'string') return ref.value - throw invalid(a) - } - const num = (i: number) => { - if (args[i] === undefined) throw invalid(text) - return this.evaluateInt(args[i]) - } - const count = (n: number) => { - if (args.length !== n) throw invalid(text) - } - const chars = Array.from(target) - const index = (i: number, max: number) => { - if (i < 0 || i > max) throw invalid(text) - return i - } - switch (call.method) { - case 'length': - count(0) - return int(chars.length) - case 'charAt': - count(1) - return { kind: 'string', value: chars[index(num(0), chars.length - 1)] } - case 'substring': { - if (args.length !== 1 && args.length !== 2) throw invalid(text) - const begin = index(num(0), chars.length) - const end = args.length === 2 ? index(num(1), chars.length) : chars.length - if (end < begin) throw invalid(text) - return { kind: 'string', value: chars.slice(begin, end).join('') } - } - case 'indexOf': - case 'lastIndexOf': { - if (args.length !== 1 && args.length !== 2) throw invalid(text) - // Die Stelle zählt wie in Java in Zeichen, nicht in UTF-16-Einheiten. - const from = args.length === 2 ? Array.from(target).slice(0, Math.max(0, num(1))).join('').length : undefined - const found = call.method === 'indexOf' ? target.indexOf(str(0), from) : target.lastIndexOf(str(0), from) - return int(found < 0 ? -1 : Array.from(target.slice(0, found)).length) - } - case 'isEmpty': - count(0) - return { kind: 'boolean', value: chars.length === 0 } - case 'equalsIgnoreCase': - count(1) - return { kind: 'boolean', value: target.toLowerCase() === str(0).toLowerCase() } - case 'compareTo': { - // Wie Java: Unterschied der ersten verschiedenen Zeichen, sonst der Längen. - count(1) - const other = Array.from(str(0)) - for (let i = 0; i < Math.min(chars.length, other.length); i++) { - if (chars[i] !== other[i]) return int(chars[i].codePointAt(0)! - other[i].codePointAt(0)!) - } - return int(chars.length - other.length) - } - case 'repeat': { - count(1) - const times = num(0) - if (times < 0) throw invalid(text) - return { kind: 'string', value: target.repeat(times) } - } - case 'concat': - count(1) - return { kind: 'string', value: target + str(0) } - case 'toUpperCase': - count(0) - return { kind: 'string', value: target.toUpperCase() } - case 'toLowerCase': - count(0) - return { kind: 'string', value: target.toLowerCase() } - case 'trim': - count(0) - return { kind: 'string', value: target.trim() } - case 'contains': - count(1) - return { kind: 'boolean', value: target.includes(str(0)) } - case 'startsWith': - count(1) - return { kind: 'boolean', value: target.startsWith(str(0)) } - case 'endsWith': - count(1) - return { kind: 'boolean', value: target.endsWith(str(0)) } - case 'replace': - count(2) - return { kind: 'string', value: target.replaceAll(str(0), str(1)) } - case 'split': { - count(1) - const separator = str(0) - // Wie Java: leere Teile am Ende fallen weg. - const parts = separator === '' ? chars : target.split(separator) - while (parts.length > 0 && parts[parts.length - 1] === '') parts.pop() - return { kind: 'stringArray', value: parts } - } - default: - return undefined - } - } - - /** - * `Integer.parseInt(…)`, `Double.parseDouble(…)`, `String.valueOf(…)`, `Files.readString(Path.of(…))`, - * `Arrays.toString(…)` und `Arrays.deepToString(…)`. - */ - private evaluateStaticCall(text: string, call: { target: string; method: string; args: string[] }): SimValue | undefined { - const one = () => { - if (call.args.length !== 1) throw invalid(text) - return call.args[0] - } - const stringArg = (a: string) => { - const literal = unquotedString(a.trim()) - if (literal !== undefined) return literal - const ref = this.evaluateReference(a) - if (ref?.kind === 'string') return ref.value - throw invalid(a) - } - switch (`${call.target}.${call.method}`) { - case 'Integer.parseInt': { - const digits = stringArg(one()).trim() - if (!/^[+-]?\d+$/u.test(digits)) throw new SimulationError({ kind: 'wholeNumberExpected' }) - return int(Number(digits)) - } - case 'Double.parseDouble': { - const value = Number(stringArg(one()).trim()) - if (!Number.isFinite(value)) throw new SimulationError({ kind: 'numberExpected' }) - return dbl(value) - } - case 'String.valueOf': - return { kind: 'string', value: this.evaluateOutputExpression(one()) } - case 'Arrays.toString': - case 'Arrays.deepToString': { - const array = this.state.variables.get(one().trim()) - if (!array || !isArrayValue(array)) throw invalid(text) - // `Arrays.toString` zeigt bei einem 2D-Array nur die Referenzen der Zeilen, `deepToString` alles. - if (array.kind === 'grid' && call.method === 'toString') { - const rows = array.value.map((row) => javaReference({ kind: `${array.elementType}Array`, value: row } as SimValue)) - return { kind: 'string', value: '[' + rows.join(', ') + ']' } - } - return { kind: 'string', value: describeValue(array) } - } - case 'Files.readString': { - const path = /^(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)$/u.exec(one().trim()) - if (!path) throw invalid(text) - const name = unquotedString(path[1])! - const content = textFiles.get(name) - if (content === undefined) throw new SimulationError({ kind: 'fileNotFound', text: name }) - return { kind: 'string', value: content } - } - default: - return undefined - } - } - - /** Wert einer String-Variablen oder eines String-Fachs; sonst `undefined`. */ - private stringValueOf(text: string): string | undefined { - const trimmed = text.trim() - const direct = this.state.variables.get(trimmed) - if (direct) return direct.kind === 'string' ? direct.value : undefined - const access = parseArrayElementAccess(trimmed) - if (!access) return undefined - const value = this.valueInArray(access.arrayName, access.indexExpressions.map((e) => this.arrayIndex(e))) - return value.kind === 'string' ? value.value : undefined - } - - /** Setzt in einem Zahlenausdruck Aufrufe wie `wort.length()` oder `wort.indexOf("a")` als Zahl ein. */ - private substituteStringCalls(expression: string): string { - if (!expression.includes('(')) return expression - let result = '' - let rest = expression - const re = /([A-Za-z_]\w*(?:\[[^\[\]]*\])*|"(?:[^"\\]|\\.)*")\s*\.\s*(length|indexOf|lastIndexOf|compareTo|charAt|parseInt|parseDouble)\s*\(/u - for (let m = re.exec(rest); m; m = re.exec(rest)) { - const open = m.index + m[0].length - 1 - const close = matchingCloseParenthesis(Array.from(rest), Array.from(rest.slice(0, open)).length) - if (close === undefined) break - const chars = Array.from(rest) - const callText = chars.slice(Array.from(rest.slice(0, m.index)).length, close + 1).join('') - const value = this.evaluateStringMethod(callText) - const before = chars.slice(0, Array.from(rest.slice(0, m.index)).length).join('') - // Mit `wort.charAt(i)` wird wie in Java über den Zeichencode gerechnet. - const code = value?.kind === 'string' ? charCode(value.value) : undefined - if (value?.kind === 'int') { - result += before + String(value.value) - } else if (value?.kind === 'double') { - // `Double.parseDouble("7")` bleibt eine Kommazahl, damit `/ 2` nicht ganzzahlig teilt. - result += before + (Number.isInteger(value.value) ? value.value.toFixed(1) : String(value.value)) - } else if (code !== undefined) { - result += before + String(code) - } else { - result += before + callText - } - rest = chars.slice(close + 1).join('') - } - return result + rest - } - - private valueInArray(name: string, indices: number[]): SimValue { - const value = this.state.variables.get(name) - if (!value) throw unknownVar(name, this.state.variables) - const at = `${name}[${indices.join('][')}]` - if (value.kind === 'grid') { - if (indices.length !== 2) throw invalid(at) - const row = value.value[indices[0]] - if (!row || indices[1] >= row.length) throw invalid(at) - return elementValue(value.elementType, row[indices[1]]) - } - if (indices.length !== 1) throw invalid(at) - const index = indices[0] - if (!isArrayValue(value)) throw invalid(name) - if (index >= ((value as { value: unknown[] }).value).length) throw invalid(at) - switch (value.kind) { - case 'intArray': return int(value.value[index]) - case 'doubleArray': return dbl(value.value[index]) - case 'stringArray': return { kind: 'string', value: value.value[index] } - case 'booleanArray': return { kind: 'boolean', value: value.value[index] } - default: throw invalid(name) - } - } - - private inferTargetValueType(target: string): SimValueType { - const trimmed = target.trim() - const direct = this.state.variables.get(trimmed) - if (direct) return valueTypeOf(direct) - const access = parseArrayElementAccess(trimmed) - if (access) { - const value = this.state.variables.get(access.arrayName) - if (!value) throw unknownVar(access.arrayName, this.state.variables) - if (value.kind === 'intArray') return 'int' - if (value.kind === 'doubleArray') return 'double' - if (value.kind === 'stringArray') return 'string' - if (value.kind === 'booleanArray') return 'boolean' - if (value.kind === 'grid') return value.elementType - throw invalid(access.arrayName) - } - throw unsupported(target) - } - - private assignValue(value: SimValue, target: string): void { - const trimmed = target.trim() - const access = parseArrayElementAccess(trimmed) - if (!access) { - this.state.variables.set(trimmed, value) - return - } - const indices = access.indexExpressions.map((e) => this.arrayIndex(e)) - const old = this.state.variables.get(access.arrayName) - if (!old) throw unknownVar(access.arrayName, this.state.variables) - if (old.kind === 'grid') { - if (indices.length !== 2 || indices[0] >= old.value.length || indices[1] >= old.value[indices[0]].length) throw invalid(trimmed) - const raw = old.elementType === 'double' && value.kind === 'int' ? value.value : value.kind === old.elementType ? value.value : undefined - if (raw === undefined || isArrayValue(value)) throw invalid(trimmed) - const rows = old.value.map((row) => [...row]) - rows[indices[0]][indices[1]] = raw as number | string | boolean - keepArrayId(old.value, rows) - this.state.variables.set(access.arrayName, { ...old, value: rows }) - return - } - if (indices.length !== 1) throw invalid(trimmed) - const index = indices[0] - let updated: SimValue - if (old.kind === 'intArray' && value.kind === 'int') { - updated = { kind: 'intArray', value: [...old.value] } - } else if (old.kind === 'doubleArray' && (value.kind === 'double' || value.kind === 'int')) { - updated = { kind: 'doubleArray', value: [...old.value] } - } else if (old.kind === 'stringArray' && value.kind === 'string') { - updated = { kind: 'stringArray', value: [...old.value] } - } else if (old.kind === 'booleanArray' && value.kind === 'boolean') { - updated = { kind: 'booleanArray', value: [...old.value] } - } else { - throw invalid(trimmed) - } - if (index >= (updated.value as unknown[]).length) throw invalid(trimmed) - ;(updated.value as unknown[])[index] = value.value - keepArrayId(old.value, updated.value) - this.state.variables.set(access.arrayName, updated) - } - - // MARK: Ausdrücke - - /** Wie Java von links nach rechts: Zahlen werden addiert, bis ein Text kommt; ab dann wird alles angehängt. */ - private evaluateOutputExpression(expression: string): string { - // `System.out.println(x + 1 > 3)`: ohne Text ist die ganze Klammer eine Bedingung und ergibt true oder false. - const whole = this.withSensors(expression).trim() - if (looksBoolean(whole) && splitByPlusOutsideQuotes(whole).every((p) => unquotedString(p.trim()) === undefined) && !this.evaluateReference(whole)) { - return String(this.evaluateBool(whole)) - } - let sum: SimValue | undefined - let text: string | undefined - // `mbot.getColor()` wird zu einem Text wie "rot" - for (const part of splitByPlusOutsideQuotes(this.withSensors(expression))) { - const trimmed = part.trim() - const literal = unquotedString(trimmed) - const value: SimValue = - literal !== undefined ? { kind: 'string', value: literal } - : (this.evaluateReference(trimmed) ?? (looksBoolean(trimmed) ? { kind: 'boolean', value: this.evaluateBool(trimmed) } : this.evaluateNumeric(trimmed))) - const isNumber = value.kind === 'int' || value.kind === 'double' - if (text === undefined && isNumber) { - if (!sum) sum = value - else sum = sum.kind === 'int' && value.kind === 'int' ? int(sum.value + value.value) : dbl((sum as { value: number }).value + (value as { value: number }).value) - continue - } - // Ein ganzes Array gibt Java nur als Referenz aus, z. B. `[I@1b6d3586`. - text = (text ?? (sum ? describeValue(sum) : '')) + javaReference(value) - sum = undefined - } - return text ?? (sum ? describeValue(sum) : '') - } - - private renderOutputExpression(expression: string): string { - return splitByPlusOutsideQuotes(expression) - .map((part) => { - const trimmed = part.trim() - if (unquotedString(trimmed) !== undefined) return trimmed - const ref = this.evaluateReference(trimmed) - if (ref) return describeValue(ref) - try { - return this.renderNumericExpression(trimmed) - } catch { - return trimmed - } - }) - .join(' + ') - } - - private evaluateExpression(expression: string, valueType: SimValueType): SimValue { - switch (valueType) { - case 'int': - return int(this.evaluateInt(expression)) - case 'double': - return dbl(this.evaluateDouble(expression)) - case 'string': { - const trimmed = expression.trim() - const literal = unquotedString(trimmed) - if (literal !== undefined) return { kind: 'string', value: literal } - if (trimmed === 'null') return { kind: 'null' } - const ref = this.evaluateReference(trimmed) - if (ref?.kind === 'null') return ref - if (ref) return { kind: 'string', value: describeValue(ref) } - return { kind: 'string', value: this.evaluateOutputExpression(expression) } - } - case 'boolean': - return { kind: 'boolean', value: this.evaluateBool(expression) } - } - } - - private evaluateNumeric(expression: string): SimValue { - const { v: value, int: isInt } = this.parser(expression).parseNumber() - return isInt || (Number.isFinite(value) && Math.round(value) === value) ? int(value) : dbl(value) - } - - private parser(expression: string): NumericParser { - const sensors = this.withSensors(expression) - return new NumericParser(tokenize(normalizeNumericExpression(this.substituteStringCalls(sensors))), this.state.variables) - } - - /** mBot2-Sensoren durch ihre Werte ersetzen (echter Roboter, Welt oder Regler, sonst der Standardwert). */ - private withSensors(expression: string): string { - return substituteMBotSensors(expression, (name) => this.robotSensors[name] ?? MBOT_SENSORS[name].simulated) - } - - private evaluateInt(expression: string): number { - return this.parser(expression).parseInt() - } - - private evaluateDouble(expression: string): number { - return this.parser(expression).parseDouble() - } - - /** Bedingungen wie in Java: `||` vor `&&` vor `!`, Klammern, Vergleiche von Zahlen und Strings, `a.equals(b)`. */ - private evaluateBool(expression: string): boolean { - // `mbot.isButtonA()` wird zu `true`/`false`, `mbot.getDistance()` zu einer Zahl - const trimmed = this.withSensors(expression).trim() - if (trimmed === 'true') return true - if (trimmed === 'false') return false - const ref = this.evaluateReference(trimmed) - if (ref?.kind === 'boolean') return ref.value - - const orParts = splitTopLevel(trimmed, '||') - if (orParts.length > 1) return orParts.some((p) => this.evaluateBool(p)) - - const andParts = splitTopLevel(trimmed, '&&') - if (andParts.length > 1) return andParts.every((p) => this.evaluateBool(p)) - - if (trimmed.startsWith('!') && !trimmed.startsWith('!=')) return !this.evaluateBool(trimmed.slice(1)) - - const inner = innerOfOuterParentheses(trimmed) - if (inner !== undefined) return this.evaluateBool(inner) - - const call = parseEqualsCall(trimmed) - if (call) return this.evaluateComparison(call.lhs, '==', call.rhs) - - const comparison = splitComparison(trimmed) - if (comparison) return this.evaluateComparison(comparison.lhs, comparison.op, comparison.rhs) - - if (this.stringOperand(trimmed) !== undefined) throw invalid(trimmed) - return this.evaluateDouble(trimmed) !== 0 - } - - private evaluateComparison(lhs: string, op: string, rhs: string): boolean { - if ((lhs.trim() === 'null' || rhs.trim() === 'null') && (op === '==' || op === '!=')) { - const isNull = (side: string) => side.trim() === 'null' || this.evaluateReference(side)?.kind === 'null' - const same = isNull(lhs) === isNull(rhs) - return op === '==' ? same : !same - } - const lb = this.booleanOperand(lhs) - const rb = this.booleanOperand(rhs) - if ((lb !== undefined || rb !== undefined) && (op === '==' || op === '!=')) { - const left = lb ?? this.evaluateBool(lhs) - const right = rb ?? this.evaluateBool(rhs) - return op === '==' ? left === right : left !== right - } - const ls = this.stringOperand(lhs) - const rs = this.stringOperand(rhs) - let left: string | number - let right: string | number - if (ls !== undefined || rs !== undefined) { - if (ls === undefined || rs === undefined) throw invalid(`${lhs} ${op} ${rhs}`) - left = ls - right = rs - } else { - left = this.evaluateDouble(lhs) - right = this.evaluateDouble(rhs) - } - switch (op) { - case '==': return left === right - case '!=': return left !== right - case '<': return left < right - case '<=': return left <= right - case '>': return left > right - case '>=': return left >= right - default: throw invalid(`${lhs} ${op} ${rhs}`) - } - } - - /** `true`, `false` oder eine boolean-Variable; sonst `undefined`. */ - private booleanOperand(text: string): boolean | undefined { - const trimmed = text.trim() - if (trimmed === 'true') return true - if (trimmed === 'false') return false - const ref = this.evaluateReference(trimmed) - return ref?.kind === 'boolean' ? ref.value : undefined - } - - /** String-Literal oder String-Variable (auch Element eines String-Arrays); sonst `undefined`. */ - private stringOperand(text: string): string | undefined { - const trimmed = text.trim() - const literal = unquotedString(trimmed) - if (literal !== undefined) return literal - const ref = this.evaluateReference(trimmed) - return ref?.kind === 'string' ? ref.value : undefined - } - - private evaluateSwitchSelector(selector: string): SwitchSelector { - const trimmed = this.withSensors(selector).trim() - const literal = unquotedString(trimmed) - if (literal !== undefined) return { kind: 'text', value: literal } - const variable = this.state.variables.get(trimmed) - if (variable) { - switch (variable.kind) { - case 'int': return { kind: 'int', value: variable.value } - case 'double': return { kind: 'int', value: Math.trunc(variable.value) } - case 'string': return { kind: 'text', value: variable.value } - default: throw invalid(trimmed) - } - } - if (/^[+-]?[0-9]+$/.test(trimmed)) return { kind: 'int', value: Number(trimmed) } - return { kind: 'int', value: this.evaluateInt(trimmed) } - } - - private renderNumericExpression(expression: string): string { - return tokenize(normalizeNumericExpression(expression)) - .map((token) => { - if (token.t === 'number') return token.literal - if (token.t === 'identifier') { - const value = this.state.variables.get(token.name) - if (!value) throw unknownVar(token.name, this.state.variables) - return describeValue(value) - } - return token.t - }) - .join(' ') - .replaceAll('( ', '(') - .replaceAll(' )', ')') - .replaceAll(' . ', '.') - .replaceAll('[ ', '[') - .replaceAll(' ]', ']') - } -} diff --git a/web/src/core/textMeasurement.ts b/web/src/core/textMeasurement.ts deleted file mode 100644 index 212da9e4..00000000 --- a/web/src/core/textMeasurement.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** Breitenmessung für Layout und Zeichnen — gleiche Systemschrift wie `DiagramCanvasView` (`.medium` / `.bold`). */ -export type FontWeight = 'medium' | 'bold' -export type MeasureText = (text: string, fontSize: number, weight: FontWeight) => number - -export const diagramFontFamily = 'system-ui, -apple-system, "Segoe UI", Roboto, sans-serif' - -export function fontCSS(fontSize: number, weight: FontWeight): string { - return `${weight === 'bold' ? 700 : 500} ${fontSize}px ${diagramFontFamily}` -} - -/** Fallback ohne Browser (Tests): wie der `#else`-Zweig in `VisuStructLayoutTextMeasurement.swift`. */ -export const approximateMeasure: MeasureText = (text, fontSize, weight) => - [...text].length * fontSize * (weight === 'bold' ? 0.68 : 0.62) - -/** - * Unsichtbares SVG-Textelement: misst mit genau der Schrift, mit der das Diagramm gezeichnet wird. - * Safari löst `system-ui` im Canvas anders auf als im SVG; Canvas-Breiten passten dort nicht zum Bild. - */ -function probeText(): SVGTextElement | undefined { - if (typeof document === 'undefined' || !document.body) return undefined - const ns = 'http://www.w3.org/2000/svg' - const svg = document.createElementNS(ns, 'svg') - svg.setAttribute('aria-hidden', 'true') - svg.setAttribute('width', '0') - svg.setAttribute('height', '0') - svg.style.cssText = 'position:absolute;left:-9999px;top:0;visibility:hidden;overflow:hidden' - const text = document.createElementNS(ns, 'text') - text.setAttribute('font-family', diagramFontFamily) - text.style.whiteSpace = 'pre' - svg.append(text) - document.body.append(svg) - if (typeof text.getComputedTextLength !== 'function' || typeof text.getBBox !== 'function') { - svg.remove() - return undefined - } - text.textContent = 'Mg' - text.setAttribute('font-size', '20') - if (!(text.getComputedTextLength() > 0)) { - svg.remove() - return undefined - } - return text -} - -let probe: SVGTextElement | null | undefined - -function sharedProbe(): SVGTextElement | undefined { - if (probe === undefined) probe = probeText() ?? null - return probe ?? undefined -} - -function setProbeFont(text: SVGTextElement, fontSize: number, weight: FontWeight) { - text.setAttribute('font-size', String(fontSize)) - text.setAttribute('font-weight', weight === 'bold' ? '700' : '500') -} - -/** Ober- und Unterlänge der Diagrammschrift (für Text, der oben oder unten an einer Linie sitzt). */ -export function fontExtents(fontSize: number, weight: FontWeight): { ascent: number; descent: number } { - const key = `${fontSize}|${weight}` - let e = extentsCache.get(key) - if (!e) { - const text = sharedProbe() - e = { ascent: fontSize * 0.95, descent: fontSize * 0.24 } - if (text) { - setProbeFont(text, fontSize, weight) - text.setAttribute('y', '0') - text.textContent = 'Mg' - const box = text.getBBox() - if (box.height > 0) e = { ascent: -box.y, descent: box.height + box.y } - } - extentsCache.set(key, e) - } - return e -} -const extentsCache = new Map() - -export function createCanvasMeasure(): MeasureText { - const svgText = sharedProbe() - if (svgText) { - const cache = new Map() - return (text, fontSize, weight) => { - const key = `${fontSize}|${weight}|${text}` - let w = cache.get(key) - if (w === undefined) { - setProbeFont(svgText, fontSize, weight) - svgText.textContent = text - w = text === '' ? 0 : svgText.getComputedTextLength() - if (cache.size > 2000) cache.clear() - cache.set(key, w) - } - return w - } - } - const ctx = typeof document !== 'undefined' ? document.createElement('canvas').getContext('2d') : null - if (!ctx) return approximateMeasure - const cache = new Map() - return (text, fontSize, weight) => { - const key = `${fontSize}|${weight}|${text}` - let w = cache.get(key) - if (w === undefined) { - ctx.font = fontCSS(fontSize, weight) - w = ctx.measureText(text).width - if (cache.size > 2000) cache.clear() - cache.set(key, w) - } - return w - } -} diff --git a/web/src/core/typeTypos.ts b/web/src/core/typeTypos.ts deleted file mode 100644 index 21f5fd1e..00000000 --- a/web/src/core/typeTypos.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** Vertippte Datentypen erkennen, für den Code-Editor (`javaParser`) und den VisuObject-Debugger (`miniJava`). */ - -const primitiveTypes = ['boolean', 'int', 'double', 'String', 'char', 'long', 'float', 'short', 'byte'] -/** Gültige Java-Typen, die einem Grundtyp ähneln (`Boolean`, `Integer`) und deshalb nie gemeldet werden. */ -const validTypes = new Set([...primitiveTypes, 'Boolean', 'Integer', 'Double', 'Character', 'Long', 'Float', 'Short', 'Byte', 'var', 'void']) - -/** Vertippter Grundtyp (`booelan`, `Int`, `string`) → richtige Schreibweise; sonst `undefined`. */ -export function similarType(word: string): string | undefined { - if (validTypes.has(word)) return undefined - const lower = word.toLowerCase() - for (const type of primitiveTypes) { - // Kurze Typen nur mit einem Fehler, sonst wäre eine Klasse `Kind` schon ein vertipptes `int`. - if (typoDistance(lower, type.toLowerCase()) <= (type.length >= 6 ? 2 : 1)) return type - } - return undefined -} - -/** Tippfehler-Abstand: Buchstabe fehlt, zu viel, falsch oder zwei vertauscht (Damerau-Levenshtein). */ -function typoDistance(a: string, b: string): number { - const d = Array.from({ length: a.length + 1 }, (_, i) => Array.from({ length: b.length + 1 }, (_, j) => (i === 0 ? j : j === 0 ? i : 0))) - for (let i = 1; i <= a.length; i++) { - for (let j = 1; j <= b.length; j++) { - const cost = a[i - 1] === b[j - 1] ? 0 : 1 - d[i][j] = Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost) - if (i > 1 && j > 1 && a[i - 1] === b[j - 2] && a[i - 2] === b[j - 1]) d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1) - } - } - return d[a.length][b.length] -} diff --git a/web/src/core/umlRelations.ts b/web/src/core/umlRelations.ts deleted file mode 100644 index baaef5c3..00000000 --- a/web/src/core/umlRelations.ts +++ /dev/null @@ -1,221 +0,0 @@ -/** - * VisuObject: Beziehungen im Klassendiagramm. - * Was Java kennt, steht im Code: ein Attribut vom Typ der anderen Klasse ist die Assoziation, - * sein Name die Rolle, eine Liste oder ein Array heißt „viele“. Was nur UML kennt - * (Aggregation, Komposition, Name mit Leserichtung, genaue Multiplizitäten, Rolle ohne Attribut) - * steht in `uml` am Projekt, Schlüssel `Klasse.attribut`. - */ - -import { associationTarget, relations, type UmlClass, type UmlMember } from './classModel' - -export type AssocKind = 'association' | 'aggregation' | 'composition' -/** Navigierbarkeit eines Endes: Pfeilspitze, Kreuz × oder ohne Kennzeichnung (offen). */ -export type EndNav = 'navigable' | 'non-navigable' | 'unspecified' - -/** Zusatz aus UML zu einem Attribut. */ -export interface AssocInfo { - kind?: AssocKind - name?: string - /** Leserichtung des Namens: von der Klasse mit dem Attribut zum Typ (Zugrichtung) oder zurück. */ - reading?: 'forward' | 'backward' - /** Ende beim Typ des Attributs; ohne Angabe navigierbar. */ - targetEnd?: EndNav - /** Ende bei der Klasse mit dem Attribut, wenn es dort kein Gegen-Attribut gibt; ohne Angabe offen. */ - sourceEnd?: EndNav - /** Alte Fassung: `false` hieß „ohne Pfeil“. */ - navigable?: boolean - multiplicity?: string - sourceMultiplicity?: string - /** Rolle am anderen Ende, wenn es dort kein Attribut gibt. */ - sourceRole?: string -} - -export type UmlInfo = Record - -/** Eine Assoziation im Diagramm: Attribut in `from`, evtl. das Gegenstück in `to` (beide Richtungen). */ -export interface Assoc { - key: string - from: string - to: string - kind: AssocKind - name?: string - reading?: 'forward' | 'backward' - /** Ende an `to`. */ - navTo: EndNav - /** Ende an `from`: navigierbar, wenn `to` ein Attribut vom Typ `from` hat. */ - navFrom: EndNav - role: string - multiplicity: string - sourceRole?: string - sourceMultiplicity?: string - field: UmlMember - back?: { key: string; field: UmlMember } -} - -export interface Generalization { - kind: 'extends' | 'implements' - from: string - to: string -} - -/** Abhängigkeit: Eine Methode erzeugt (`new X()`) oder benutzt eine Klasse, ohne sie in einem Attribut zu merken. */ -export interface Dependency { - from: string - to: string - kind: 'create' | 'use' - /** Methoden bzw. Konstruktoren, in denen das vorkommt. */ - methods: string[] -} - -/** Kommentare und Texte in Anführungszeichen ausblenden, damit `new Hund` im Kommentar nicht zählt. */ -const codeOnly = (text: string) => text.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'/g, (m) => ' '.repeat(m.length)) - -/** - * Abhängigkeiten aus den Methoden: gestrichelter Pfeil «create» bei `new X(…)`, sonst «use», wenn der Typ vorkommt. - * Keine, wo schon eine Assoziation (Attribut) oder Vererbung von `from` zu `to` besteht. - */ -export function dependencies(classes: UmlClass[], textOf: (cls: UmlClass) => string | undefined): Dependency[] { - const names = classes.map((c) => c.name) - const out = new Map() - for (const cls of classes) { - const text = textOf(cls) - if (!text) continue - const code = codeOnly(text) - const known = new Set([cls.superclass, ...cls.interfaces, ...cls.members.filter((m) => m.kind === 'field' && !m.isStatic).map((m) => associationTarget(m.type).name)]) - for (const m of cls.members) { - if (m.kind === 'field') continue - const body = code.slice(m.from, m.to) - for (const other of names) { - if (other === cls.name || known.has(other)) continue - const word = new RegExp(`\\b${other}\\b`) - if (!word.test(body)) continue - const kind = new RegExp(`\\bnew\\s+${other}\\s*[(<\\[{]`).test(body) ? 'create' : 'use' - const key = `${cls.name}>${other}` - const dep = out.get(key) ?? { from: cls.name, to: other, kind, methods: [] } - if (kind === 'create') dep.kind = 'create' - const label = m.kind === 'constructor' ? `${m.name}()` : `${m.name}()` - if (!dep.methods.includes(label)) dep.methods.push(label) - out.set(key, dep) - } - } - } - return [...out.values()] -} - -export const multiplicities = ['1', '0..1', '0..*', '1..*', '*'] - -/** Obergrenze größer als 1: Dann gehört eine Liste in den Code. */ -export function isMany(multiplicity: string): boolean { - const upper = multiplicity.trim().split('..').pop()!.trim() - return upper === '*' || upper === 'n' || (/^\d+$/.test(upper) && Number(upper) > 1) -} - -export const relationKey = (cls: string, field: string) => `${cls}.${field}` - -export function generalizations(classes: UmlClass[]): Generalization[] { - return relations(classes).filter((r): r is Generalization => r.kind !== 'association') -} - -/** Assoziationen aus den Attributen; zwei Attribute, die aufeinander zeigen, werden eine Linie mit zwei Pfeilen. */ -export function associations(classes: UmlClass[], uml: UmlInfo = {}): Assoc[] { - const names = new Set(classes.map((c) => c.name)) - const raw: { cls: UmlClass; field: UmlMember; target: string; many: boolean }[] = [] - for (const cls of classes) - for (const field of cls.members) { - if (field.kind !== 'field' || field.isStatic) continue - const target = associationTarget(field.type) - if (names.has(target.name) && target.name !== cls.name) raw.push({ cls, field, target: target.name, many: target.many }) - } - const used = new Set<(typeof raw)[number]>() - const out: Assoc[] = [] - const multiplicity = (stored: string | undefined, many: boolean) => (stored && isMany(stored) === many ? stored : many ? '0..*' : '1') - // Was die ganze Beziehung betrifft (nicht nur ein Ende), entscheidet, welches Attribut vorne steht. - const hasInfo = (x: (typeof raw)[number]) => { - const i = uml[relationKey(x.cls.name, x.field.name)] - return !!i && (i.kind !== undefined || i.name !== undefined || i.reading !== undefined || i.navigable !== undefined || i.targetEnd !== undefined || i.sourceEnd !== undefined) - } - for (const first of raw) { - if (used.has(first)) continue - used.add(first) - // Gegenstück nur, wenn es in beiden Richtungen genau ein Attribut gibt. - const backs = raw.filter((b) => !used.has(b) && b.cls.name === first.target && b.target === first.cls.name) - let back = backs.length === 1 && raw.filter((x) => x.cls.name === first.cls.name && x.target === first.target).length === 1 ? backs[0] : undefined - if (back) used.add(back) - // Vorne steht das Attribut mit UML-Zusatz (z. B. die Raute am Ganzen), sonst das zuerst gefundene. - let r = first - if (back && !hasInfo(first) && hasInfo(back)) [r, back] = [back, first] - const key = relationKey(r.cls.name, r.field.name) - const info = uml[key] ?? {} - const backInfo = back ? (uml[relationKey(back.cls.name, back.field.name)] ?? {}) : {} - out.push({ - key, - from: r.cls.name, - to: r.target, - kind: info.kind ?? 'association', - name: info.name, - // Ein Name liest sich in Zugrichtung, solange niemand umdreht. - reading: info.reading ?? (info.name ? 'forward' : undefined), - navTo: info.targetEnd ?? (info.navigable === false ? 'unspecified' : 'navigable'), - navFrom: back ? 'navigable' : (info.sourceEnd ?? 'unspecified'), - role: r.field.name, - multiplicity: multiplicity(info.multiplicity, r.many), - sourceRole: back ? back.field.name : info.sourceRole, - sourceMultiplicity: back ? multiplicity(backInfo.multiplicity, back.many) : info.sourceMultiplicity, - field: r.field, - back: back && { key: relationKey(back.cls.name, back.field.name), field: back.field }, - }) - } - return out -} - -/** Neues Attribut zu einer Klasse: Name klein wie die Klasse, bei vielen in der Mehrzahl mit -s, nicht doppelt. */ -export function fieldNameFor(cls: UmlClass, target: string, many: boolean): string { - const base = target.charAt(0).toLowerCase() + target.slice(1) + (many ? 'Liste' : '') - let name = base - for (let n = 2; cls.members.some((m) => m.name === name); n++) name = base + n - return name -} - -/** Java-Typ zur Multiplizität: eine Liste bei vielen, sonst die Klasse selbst. */ -export function fieldTypeFor(target: string, many: boolean): { type: string; init?: string } { - return many ? { type: `ArrayList<${target}>`, init: 'new ArrayList<>()' } : { type: target } -} - -/** `import a.b.C;` einfügen, wenn er fehlt (hinter `package` bzw. den anderen Imports). */ -export function ensureImport(text: string, qualified: string): string { - if (!qualified.includes('.')) return text - const escaped = qualified.replace(/\./g, '\\.') - const pkg = qualified.slice(0, qualified.lastIndexOf('.')) - if (new RegExp(`^\\s*import\\s+(${escaped}|${pkg.replace(/\./g, '\\.')}\\.\\*)\\s*;`, 'm').test(text)) return text - const imports = [...text.matchAll(/^[ \t]*import\s+[\w.*]+\s*;[ \t]*\n/gm)] - if (imports.length) { - const last = imports[imports.length - 1] - const at = last.index! + last[0].length - return text.slice(0, at) + `import ${qualified};\n` + text.slice(at) - } - const pkgLine = /^\s*package\s+[\w.]+\s*;[ \t]*\n/.exec(text) - if (pkgLine) return text.slice(0, pkgLine[0].length) + `\nimport ${qualified};\n` + text.slice(pkgLine[0].length).replace(/^(?!\n)/, '\n') - return `import ${qualified};\n\n${text}` -} - -/** Zusatz neu schreiben; leere Felder fallen weg, ein leerer Eintrag verschwindet. */ -export function withInfo(uml: UmlInfo, key: string, change: Partial): UmlInfo { - const next: AssocInfo = { ...uml[key], ...change } - for (const k of Object.keys(next) as (keyof AssocInfo)[]) if (next[k] === undefined || next[k] === '') delete next[k] - if (next.kind === 'association') delete next.kind - delete next.navigable - if (next.targetEnd === 'navigable') delete next.targetEnd - if (next.sourceEnd === 'unspecified') delete next.sourceEnd - const out = { ...uml } - if (Object.keys(next).length) out[key] = next - else delete out[key] - return out -} - -/** Schlüssel umbenennen, z. B. wenn die Rolle (das Attribut) einen neuen Namen bekommt. */ -export function renameInfo(uml: UmlInfo, from: string, to: string): UmlInfo { - if (!uml[from] || from === to) return uml - const out = { ...uml, [to]: uml[from] } - delete out[from] - return out -} diff --git a/web/src/core/workspace.ts b/web/src/core/workspace.ts deleted file mode 100644 index 45da6aae..00000000 --- a/web/src/core/workspace.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** Workspace in der Spalte „Dateien“: Klasse → Tagesordner → Programme. Reine Hilfsfunktionen, ohne Dateizugriff. */ - -/** Ordnername für den Tag, z. B. `2026-10-23` (Ortszeit, nicht UTC). */ -export function todayFolder(date = new Date()): string { - const pad = (n: number) => String(n).padStart(2, '0') - return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}` -} - -/** Tagesordner werden neueste zuerst gezeigt, alles andere alphabetisch. */ -export const isDayFolder = (name: string) => /^\d{4}-\d{2}-\d{2}$/.test(name) - -export function compareEntries(a: { kind: string; name: string }, b: { kind: string; name: string }): number { - if (a.kind !== b.kind) return a.kind === 'directory' ? -1 : 1 - if (isDayFolder(a.name) && isDayFolder(b.name)) return b.name.localeCompare(a.name) - return a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' }) -} - -/** Dateiname aus dem Titel: ohne Zeichen, die Windows oder macOS nicht erlauben. */ -export function programBaseName(title: string, fallback: string): string { - const cleaned = title - .replace(/\.(visustruct|strk|java)$/i, '') - .replace(/[\\/:*?"<>|\u0000-\u001f]/g, '-') - .replace(/\s+/g, ' ') - .trim() - .replace(/^\.+/, '') - return cleaned || fallback -} - -/** Freier Name im Ordner: `Rechner`, sonst `Rechner 2`, `Rechner 3` … (für .visustruct und .java gemeinsam). */ -export function uniqueBaseName(base: string, taken: Iterable): string { - const names = new Set([...taken].map((n) => n.toLowerCase())) - const free = (b: string) => !names.has(`${b}.visustruct`.toLowerCase()) && !names.has(`${b}.java`.toLowerCase()) - if (free(base)) return base - for (let i = 2; ; i++) if (free(`${base} ${i}`)) return `${base} ${i}` -} - -/** Ordnername, den man anlegen darf. */ -export function validFolderName(name: string): string | undefined { - const cleaned = name.replace(/[\\/:*?"<>|\u0000-\u001f]/g, '').trim() - return cleaned && cleaned !== '.' && cleaned !== '..' ? cleaned : undefined -} diff --git a/web/src/core/xmlCodec.ts b/web/src/core/xmlCodec.ts deleted file mode 100644 index b126154e..00000000 --- a/web/src/core/xmlCodec.ts +++ /dev/null @@ -1,145 +0,0 @@ -import { decodeJavaString, encodeJavaString } from './javaStringCoding' -import { ElementType, type RGBA, type VSCase, type VSDocument, type VSElement } from './model' - -/** XML <-> Modell, kompatibel zu `VisuStructXMLCodec.swift` und Java `XMLLeser`/`Struktogramm.xmlErstellen`. */ -export class VisuStructXMLError extends Error {} - -export function decodeDocument(xml: string): VSDocument { - const dom = new DOMParser().parseFromString(xml, 'application/xml') - if (dom.getElementsByTagName('parsererror').length > 0) { - throw new VisuStructXMLError('Could not read XML.') - } - const root = dom.documentElement - if (!root || root.tagName !== 'struktogramm') { - throw new VisuStructXMLError('Invalid XML: root element is not .') - } - const doc: VSDocument = { - caption: decodeAttr(root, 'caption') ?? '', - elements: decodeList(root), - } - const fontFamily = decodeAttr(root, 'fontfamily') - if (fontFamily !== undefined) doc.fontFamily = fontFamily - const fontSize = intAttr(root, 'fontsize') - if (fontSize !== undefined) doc.fontSize = fontSize - const fontStyle = intAttr(root, 'fontstyle') - if (fontStyle !== undefined) doc.fontStyle = fontStyle - if (root.getAttribute('pseudocode') === 'true') doc.pseudocode = true - return doc -} - -export function encodeDocument(doc: VSDocument): string { - const attr = (k: string, v: string | undefined) => (v === undefined ? '' : ` ${k}="${escapeAttr(v)}"`) - let out = '\n' - out += '${escapeText(encodeJavaString(line))}\n` - } - if (e.loopBody) { - out += `${indent} \n` - for (const child of e.loopBody) out += encodeElement(child, indent + ' ') - out += `${indent} \n` - } - if (e.cases) { - for (const c of e.cases) { - out += `${indent} \n` - for (const child of c.elements) out += encodeElement(child, indent + ' ') - out += `${indent} \n` - } - } - out += `${indent}\n` - return out -} - -function escapeAttr(s: string): string { - return s.replace(/&/g, '&').replace(/"/g, '"').replace(//g, '>') -} - -function escapeText(s: string): string { - return s.replace(/&/g, '&').replace(//g, '>') -} - -/** Java speichert `Color.getRGB()` als dezimalen, vorzeichenbehafteten 32-bit-ARGB-Int. */ -function javaColorIntString(c: RGBA): string { - return String(((c.a << 24) | (c.r << 16) | (c.g << 8) | c.b) | 0) -} - -// MARK: - Decoding - -function childElements(parent: Element, tag: string): Element[] { - return Array.from(parent.children).filter((c) => c.tagName === tag) -} - -function decodeList(parent: Element): VSElement[] { - const out: VSElement[] = [] - for (const node of childElements(parent, 'strelem')) { - const e = decodeElement(node) - if (e) out.push(e) - } - return out -} - -function decodeElement(node: Element): VSElement | undefined { - const typ = intAttr(node, 'typ') - if (typ === undefined || ElementType[typ] === undefined) return undefined - - const e: VSElement = { - type: typ as ElementType, - zoomX: intAttr(node, 'zx') ?? 0, - zoomY: intAttr(node, 'zy') ?? 0, - textLines: childElements(node, 'text').map((t) => decodeJavaString((t.textContent ?? '').trim())), - } - const textColor = parseJavaARGB(node.getAttribute('textcolor')) - if (textColor) e.textColor = textColor - const bgColor = parseJavaARGB(node.getAttribute('bgcolor')) - if (bgColor) e.backgroundColor = bgColor - if (e.textLines.length === 0) e.textLines = [''] - - const loop = childElements(node, 'schleifeninhalt')[0] - if (loop) { - const body = decodeList(loop) - if (body.length > 0) e.loopBody = body - } - const cases: VSCase[] = childElements(node, 'fall').map((f) => ({ - name: decodeJavaString(f.getAttribute('fallname') ?? ''), - elements: decodeList(f), - })) - if (cases.length > 0) e.cases = cases - return e -} - -function decodeAttr(node: Element, name: string): string | undefined { - const v = node.getAttribute(name) - return v === null ? undefined : decodeJavaString(v) -} - -function intAttr(node: Element, name: string): number | undefined { - const v = node.getAttribute(name) - if (v === null || !/^[+-]?\d+$/.test(v)) return undefined - return Number(v) -} - -function parseJavaARGB(s: string | null): RGBA | undefined { - if (s === null || !/^[+-]?\d+$/.test(s)) return undefined - const i = Number(s) - if (i < -0x80000000 || i > 0x7fffffff) return undefined - const u = i >>> 0 - return { a: (u >>> 24) & 0xff, r: (u >>> 16) & 0xff, g: (u >>> 8) & 0xff, b: u & 0xff } -} diff --git a/web/src/i18n/ar.strings b/web/src/i18n/ar.strings deleted file mode 100644 index 438489ba..00000000 --- a/web/src/i18n/ar.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* نصوص موجودة في نسخة الويب فقط. أما بقية النصوص فتأتي من ملف Localizable.strings الخاص بالتطبيق. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "مخطط VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "تصدير…"; -"web_export_title" = "حفظ كصورة أو PDF، أو طباعة"; -"web_export_png" = "صورة (PNG)"; -"web_export_svg" = "رسم متجهي (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "ملف VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "فشل التصدير: %@"; -"web_print_hint" = "استخدم «تصدير… → PDF» لحفظ المخطط ثم طباعته من هناك."; -"web_delete" = "حذف"; -"web_language_title" = "لغة الواجهة"; -"web_simulation_start" = "بدء المحاكاة"; -"web_simulation_end" = "إنهاء المحاكاة"; -"web_simulation_start_title" = "تشغيل المخطط خطوة بخطوة"; -"web_simulation_end_title" = "إنهاء المحاكاة ومتابعة التحرير"; -"web_palette_hint" = "اسحب إلى المخطط"; -"web_tabs_label" = "المخططات"; -"web_status_modified" = "معدَّل"; -"web_inspector_title" = "الخصائص"; -"web_inspector_resize" = "تغيير ارتفاع مفتش الكائنات: اسحب الخط لأعلى أو لأسفل (نقر مزدوج: الافتراضي)"; -"web_inspector_help_drop" = "اسحب العناصر من اللوحة إلى كتلة: أعلى = قبل، أسفل = بعد، على «ø» = استبدال. يمكن نقل العناصر الموجودة بالفأرة."; -"web_inspector_help_inline" = "انقر نقرًا مزدوجًا أو المس مرتين على عنصر لتعديل نصه مباشرة في المخطط (Enter للتطبيق، Esc للإلغاء). وكذلك أسماء الحالات."; -"web_inspector_help_files" = "يمكنك أيضًا إفلات الملفات في النافذة."; -"web_case_name" = "اسم الحالة"; -"web_case_name_placeholder" = "مثلًا 1، ok، default"; -"web_copied" = "تم نسخ العنصر."; -"web_discard_message_fmt" = "ستُفقد التغييرات على «%@»."; -"web_err_unreadable_fmt" = "«%@» ليس ملف VisuStruct قابلًا للقراءة (%@)."; -"web_saved_fmt" = "تم حفظ «%@»."; -"web_saved_txt_fmt" = "تم الحفظ باسم «%@». احذف الامتداد .txt لاستخدامه."; -"web_drop_impossible" = "لا يمكن الإفلات هنا"; -"web_sim_delay" = "التوقف لكل خطوة"; -"web_sim_delay_value_fmt" = "%@ ث"; -"web_codegen_language" = "اللغة"; -"web_codegen_comments" = "نص المخطط كتعليقات"; -"web_codegen_empty_else" = "إخراج فرع else فارغ في الشفرة"; -"web_copied_check" = "تم النسخ ✓"; -"web_copy_manual" = "تم التحديد، اضغط Ctrl+C للنسخ"; -"web_run_stopped_loop" = "توقف التنفيذ: حلقة لا نهائية محتملة."; -"web_run_cancelled" = "تم إلغاء التنفيذ."; -"web_run_error" = "خطأ أثناء التشغيل."; -"web_run_dropped_fmt" = "… تم إخفاء %d من الأسطر السابقة"; -"web_sim_call_arguments_fmt" = "الاستدعاء لا يطابق ترويسة %@. تحقق من القيم بين القوسين."; -"web_sim_missing_return_fmt" = "انتهى %@ دون إرجاع قيمة باستخدام return."; -"web_sim_file_not_found_fmt" = "لا يوجد ملف باسم %@."; -"web_sim_end_of_file_fmt" = "لا يوجد سطر آخر في الملف %@."; -"web_sim_null_value_fmt" = "القيمة في %@ هي null."; -"web_sim_unknown_variable_case_fmt" = "متغير غير معروف: %1$@. تميّز Java بين الأحرف الكبيرة والصغيرة: هل تقصد %2$@؟"; -"web_sim_written_file_fmt" = "%@ (محفوظ)"; -"web_site_home" = "الصفحة الرئيسية"; -"web_site_imprint" = "بيانات الناشر"; -"web_site_privacy" = "الخصوصية"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "البرمجة"; -"web_live_hide" = "إخفاء البرمجة"; -"web_scale_canvas" = "حجم النص: المخطط"; -"web_scale_code" = "حجم النص: البرمجة"; -"web_live_indent" = "إزاحة"; -"web_live_indent_title" = "إزاحة الشيفرة: أربع مسافات لكل مستوى (الأسطر المحددة، وإلا فالكل)"; -"web_live_show" = "إظهار البرمجة"; -"web_live_resize" = "تغيير عرض منطقة الشيفرة (نقر مزدوج: إعادة تعيين)"; -"web_live_hint" = "تنمو الشيفرة مع مخططك. الرمادي = عنصر نائب: اكتب في المفتش ليظهر نصك هنا. انقر على سطر لتحديد عنصره."; -"web_live_ph_selector" = "المتغير"; -"web_live_ph_code" = "شيفرتك"; -"web_live_ph_end" = "النهاية"; -"web_live_language" = "لغة البرمجة"; -"web_live_java_class" = "كصنف"; -"web_live_java_class_hint" = "اسم الصنف = عنوان المخطط"; -"web_live_java_main" = "مع الطريقة main"; -"web_settings_display" = "العرض"; -"web_live_swing" = "جافا حقيقية (مع Swing)"; -"web_live_swing_hint" = "جافا حقيقية مع نوافذ (CheerpJ). تُنزّل نحو 40 ميغابايت مرة واحدة عند أول تشغيل."; -"web_live_swing_windows" = "نوافذ جافا"; -"web_live_swing_hide" = "إغلاق نوافذ جافا"; -"web_live_swing_loading" = "جارٍ تحميل جافا (نحو 40 ميغابايت في المرة الأولى) …"; -"web_live_swing_compiling" = "جارٍ الترجمة …"; -"web_live_swing_running" = "البرنامج قيد التشغيل."; -"web_live_swing_compile_failed" = "خطأ في الترجمة، انظر أعلاه."; -"web_live_swing_failed" = "تعذّر تحميل جافا. تحقّق من الاتصال بالإنترنت."; -"web_live_hint_edit" = "اكتب هنا شيفرة Java وسيُبنى المخطط تلقائيًا. أو اعمل في المخطط وستنمو الشيفرة معه. الرمادي = عنصر نائب، اكتب فوقه مباشرة."; -"web_live_edit_label" = "شيفرة Java"; -"web_parse_line_fmt" = "السطر %d: %@"; -"web_parse_missing" = "ما زال ينقص هنا: %@"; -"web_parse_extra" = "زائد: %@"; -"web_parse_unclosed_string" = "ينقص %@ الإغلاق هنا."; -"web_parse_else_without_if" = "لا يوجد «if» لهذا «else»."; -"web_parse_case_outside_switch" = "«case» و«default» مكانهما داخل «switch»."; -"web_parse_missing_do_while" = "بعد «do { … }» ينقص «while (الشرط);»."; -"web_parse_more_methods" = "حاليًا تُحتسب طريقة واحدة فقط هنا. أنشئ البرامج الفرعية الأخرى في علامات تبويب منفصلة."; -"web_parse_missing_import" = "ينقص في الأعلى %@ بدون هذا السطر لا تعرف Java الصنف Scanner."; -"web_parse_missing_scanner" = "ينقص قبل ذلك %@ بدون هذا السطر لا يوجد Scanner."; -"web_parse_unknown_type" = "لا تعرف Java نوع البيانات هذا. هل تقصد %@؟"; -"web_quickfix_insert_fmt" = "اقتراح: إدراج %@"; -"web_quickfix_replace_fmt" = "اقتراح: %1$@ بدلاً من %2$@"; -"web_parse_wrong_case" = "تميّز Java بين الأحرف الكبيرة والصغيرة. الصحيح هو %@."; -"web_editor_title" = "المخطط"; -"web_editor_show" = "إظهار المخطط"; -"web_editor_hide" = "إخفاء المخطط"; -"web_keywords_title" = "كلمات الشيفرة المفتاحية"; -"web_keywords_hint" = "if و while و for بدلًا من أسماء العناصر"; -"web_palette_compact" = "العناصر مضغوطة دائمًا"; -"web_palette_compact_hint" = "رمز مع اسم صغير تحته، وإلا فقط عندما يكون العمود ضيقًا"; -"web_about_version_fmt" = "الإصدار %@"; -"web_about_software" = "البرمجيات المستخدمة"; -"web_about_software_text" = "يستخدم محرر الشيفرة CodeMirror و Lezer (ترخيص MIT). مع دعم Swing يحمّل VisuStruct برنامج CheerpJ من Leaning Technologies (ترخيص Community) و javac من OpenJDK."; -"web_about_licenses" = "نصوص التراخيص"; -"web_live_run" = "تشغيل"; -"web_live_run_title" = "تشغيل البرنامج: تظهر المخرجات والمدخلات بالأسفل"; -"web_live_run_finished" = "انتهى البرنامج."; -"web_live_run_stopped" = "تم الإيقاف."; -"web_console" = "وحدة التحكم"; -"web_console_hide" = "إخفاء وحدة التحكم"; -"web_console_resize" = "تغيير ارتفاع وحدة التحكم"; -"web_diagram_tools" = "أدوات المخطط"; -"web_files_title" = "الملفات"; -"web_files_show" = "إظهار الملفات"; -"web_files_hide" = "إخفاء الملفات"; -"web_files_resize" = "تغيير عرض الملفات"; -"web_files_new_folder" = "مجلد جديد"; -"web_files_switch" = "اختيار مجلد آخر"; -"web_files_delete" = "حذف"; -"web_files_in_browser" = "في تخزين هذا المتصفح"; -"web_files_save_hint_fmt" = "الحفظ يضع البرنامج في %@."; -"web_files_empty" = "لم يُحفظ شيء بعد. عند الحفظ ينشئ VisuStruct هنا مجلداً بتاريخ اليوم."; -"web_files_reconnect_hint" = "بعد إعادة التحميل يسأل المتصفح مرة أخرى إن كان يمكن لـ VisuStruct استخدام مجلدك."; -"web_files_reconnect_fmt" = "فتح المجلد «%@»"; -"web_files_connect_hint" = "اختر مجلداً كمساحة عمل. ينشئ VisuStruct فيه مجلداً لكل يوم ويحفظ المخطط والشيفرة."; -"web_files_connect" = "اختيار مجلد"; -"web_files_browser_hint" = "لا يستطيع هذا المتصفح فتح مجلدات الجهاز (مثلاً على iPad). تبقى ملفاتك في تخزين هذا المتصفح."; -"web_files_browser" = "الحفظ في المتصفح"; -"web_files_delete_confirm_fmt" = "هل تريد حذف «%@» فعلاً؟"; -"web_files_folder_prompt" = "اسم المجلد الجديد:"; -"web_files_default_name" = "برنامج"; -"web_files_saved_fmt" = "تم الحفظ: %@"; -"web_files_create" = "إنشاء"; -"web_learn_ludwig_tip" = "نصيحة لودفيغ"; -"web_game_title" = "لعبة: صحيح أم خطأ؟"; -"web_game_intro" = "يعرض لك لودفيغ قيماً وشرطاً. هل الشرط صحيح (true) أم خطأ (false)؟ عشر جولات تزداد صعوبة."; -"web_game_start" = "ابدأ اللعبة"; -"web_game_round_fmt" = "الجولة %@ من %@"; -"web_game_score_fmt" = "النقاط: %@"; -"web_game_true" = "صحيح (true)"; -"web_game_false" = "خطأ (false)"; -"web_game_right" = "صحيح!"; -"web_game_wrong" = "ليس تماماً. هكذا يُحسب:"; -"web_game_next" = "التالي"; -"web_game_finish" = "النتيجة"; -"web_game_done_fmt" = "%@ من %@ صحيحة."; -"web_game_perfect" = "كلها صحيحة – لودفيغ معجب جداً!"; -"web_game_good" = "رائع! راجع أخطاءك وحاول مرة أخرى."; -"web_game_practice" = "التمرين يصنع الإتقان. اقرأ جدول الحقيقة مرة أخرى والعب جولة أخرى."; -"web_game_again" = "العب مرة أخرى"; -"web_game_wrong_short" = "للأسف خطأ."; -"web_game_practice_short" = "التمرين يصنع الإتقان. اقرأ الشروح مرة أخرى والعب جولة أخرى."; -"web_game_clicks" = "النقرات:"; -"web_game_nobody" = "لا أحد يستمع – لا يحدث شيء."; -"web_game_event_fmt" = "نقرة على «%@»: المستمع يسمعها وينفّذ شيفرته."; -"web_game_try" = "جرّب الآن أزرار النافذة بنفسك."; -"web_game_time_fmt" = "متبقٍ %@ ث"; -"web_game_defused" = "✂️ تم تعطيلها! وجدت الخطأ."; -"web_game_boom_time" = "💥 بوم! انتهى الوقت."; -"web_game_boom_wrong" = "💥 بوم! كان ذلك السطر الخطأ."; -"web_game_boom_part" = "💥 بوم! السطر صحيح، لكن الخطأ في جزء آخر منه."; -"web_game_boom_wrong_part" = "💥 بوم! الخطأ في سطر آخر."; -"web_game_bomb_parts_hint" = "اضغط على الجزء الخاطئ بالضبط."; -"web_game_fix" = "السطر الصحيح:"; -"web_game_order_hint" = "اضغط على الخطوات بالترتيب الصحيح."; -"web_debug_step" = "خطوة"; -"web_debug_step_title" = "خطوة: تنفيذ تعليمة واحدة ثم التوقف مجدداً"; -"web_debug_continue" = "متابعة حتى نقطة التوقف التالية"; -"web_debug_paused_fmt" = "متوقف قبل السطر %d."; -"web_debug_gutter_title" = "انقر على رقم السطر لوضع نقطة توقف أو إزالتها"; -"web_swap_to_code" = "البرمجة كبيرة"; -"web_swap_to_editor" = "المخطط كبير"; -"web_editor_resize" = "تغيير عرض المخطط"; -"web_palette_resize" = "تغيير عرض العناصر"; -"web_palette_hide" = "إخفاء العناصر"; -"web_palette_show" = "إظهار العناصر"; -"web_code_scopes" = "كتل الشيفرة ملوّنة"; -"web_code_scopes_hint" = "الصنف والدالة والحلقة والتفرّع في مربعات خاصة"; -"web_pseudocode" = "شبه كود"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "إعادة تسمية المتغير …"; -"web_rename_title" = "إعادة تسمية المتغير"; -"web_rename_old" = "الاسم"; -"web_rename_new" = "الاسم الجديد"; -"web_rename_hint" = "يغيّر كل موضع بهذه الكتابة تمامًا (number ليس Number)، في الشيفرة وفي المخطط الهيكلي. يبقى النص بين \"…\" والتعليقات كما هي."; -"web_rename_count_fmt" = "يظهر %d مرة."; -"web_rename_exists_fmt" = "«%@» موجود بالفعل: بعدها يصبح المتغيران متغيرًا واحدًا."; -"web_rename_invalid" = "يبدأ الاسم بحرف ويحتوي على حروف وأرقام و _ فقط."; -"web_rename_keyword_fmt" = "«%@» كلمة مفتاحية في Java."; -"web_rename_apply" = "إعادة تسمية"; -"web_rename_cancel" = "إلغاء"; diff --git a/web/src/i18n/da.strings b/web/src/i18n/da.strings deleted file mode 100644 index 4c798db5..00000000 --- a/web/src/i18n/da.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Tekster, der kun findes i webversionen. Alt andet kommer fra appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksportér…"; -"web_export_title" = "Gem som billede eller PDF, eller udskriv"; -"web_export_png" = "Billede (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Eksport mislykkedes: %@"; -"web_print_hint" = "Brug »Eksportér… → PDF« til at gemme diagrammet og udskrive det derfra."; -"web_delete" = "Slet"; -"web_language_title" = "Sprog i brugerfladen"; -"web_simulation_start" = "Start simulering"; -"web_simulation_end" = "Afslut simulering"; -"web_simulation_start_title" = "Kør struktogrammet trin for trin"; -"web_simulation_end_title" = "Afslut simuleringen og fortsæt redigeringen"; -"web_palette_hint" = "Træk ind i struktogrammet"; -"web_tabs_label" = "Diagrammer"; -"web_status_modified" = "ændret"; -"web_inspector_title" = "Egenskaber"; -"web_inspector_resize" = "Skift højden på objektinspektøren: træk eller stryg linjen op eller ned (dobbeltklik: standard)"; -"web_inspector_help_drop" = "Træk elementer fra paletten over på en flise: øverst = før, nederst = efter, på »ø« = erstat. Eksisterende elementer kan flyttes med musen."; -"web_inspector_help_inline" = "Dobbeltklik eller dobbelttryk på et element for at redigere teksten direkte i diagrammet (Enter anvender, Esc annullerer). Det gælder også case-navne."; -"web_inspector_help_files" = "Du kan også slippe filer i vinduet."; -"web_case_name" = "Navn på case"; -"web_case_name_placeholder" = "f.eks. 1, ok, default"; -"web_copied" = "Element kopieret."; -"web_discard_message_fmt" = "Ændringerne i »%@« går tabt."; -"web_err_unreadable_fmt" = "»%@« er ikke en læsbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "»%@« gemt."; -"web_saved_txt_fmt" = "Gemt som »%@«. Fjern filtypenavnet .txt for at bruge den."; -"web_drop_impossible" = "Kan ikke slippes her"; -"web_sim_delay" = "Pause pr. trin"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Sprog"; -"web_codegen_comments" = "Diagramtekst som kommentarer"; -"web_codegen_empty_else" = "Skriv tom else-gren i koden"; -"web_copied_check" = "Kopieret ✓"; -"web_copy_manual" = "Markeret, tryk Ctrl+C for at kopiere"; -"web_run_stopped_loop" = "Kørslen stoppet: mulig uendelig løkke."; -"web_run_cancelled" = "Kørslen annulleret."; -"web_run_error" = "Fejl under kørsel."; -"web_run_dropped_fmt" = "… %d tidligere linjer skjult"; -"web_sim_call_arguments_fmt" = "Kaldet passer ikke til hovedet i %@. Tjek værdierne i parenteserne."; -"web_sim_missing_return_fmt" = "%@ sluttede uden at returnere en værdi med return."; -"web_sim_file_not_found_fmt" = "Der findes ingen fil med navnet %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har ikke flere linjer."; -"web_sim_null_value_fmt" = "%@ er null."; -"web_sim_unknown_variable_case_fmt" = "Ukendt variabel: %1$@. Java skelner mellem store og små bogstaver: mente du %2$@?"; -"web_sim_written_file_fmt" = "%@ (gemt)"; -"web_site_home" = "Forside"; -"web_site_imprint" = "Kolofon"; -"web_site_privacy" = "Privatliv"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Skjul programmering"; -"web_scale_canvas" = "Tekststørrelse: struktogram"; -"web_scale_code" = "Tekststørrelse: programmering"; -"web_live_indent" = "Indryk"; -"web_live_indent_title" = "Indryk koden: fire mellemrum pr. niveau (markerede linjer, ellers alt)"; -"web_live_show" = "Vis programmering"; -"web_live_resize" = "Skift kodeområdets bredde (dobbeltklik: nulstil)"; -"web_live_hint" = "Koden vokser med dit diagram. Gråt = pladsholder: skriv i inspektøren, så vises teksten her. Klik på en linje for at vælge elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kode"; -"web_live_ph_end" = "slut"; -"web_live_language" = "Programmeringssprog"; -"web_live_java_class" = "Som klasse"; -"web_live_java_class_hint" = "Klassenavn = diagrammets titel"; -"web_live_java_main" = "Med main-metode"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Ægte Java (med Swing)"; -"web_live_swing_hint" = "Rigtig Java med vinduer (CheerpJ). Henter ca. 40 MB én gang ved første kørsel."; -"web_live_swing_windows" = "Java-vinduer"; -"web_live_swing_hide" = "Luk Java-vinduer"; -"web_live_swing_loading" = "Java indlæses (første gang ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompilerer …"; -"web_live_swing_running" = "Programmet kører."; -"web_live_swing_compile_failed" = "Kompileringsfejl, se ovenfor."; -"web_live_swing_failed" = "Java kunne ikke indlæses. Tjek internetforbindelsen."; -"web_live_hint_edit" = "Skriv Java-kode her, så bygger diagrammet sig selv. Eller arbejd i diagrammet, så vokser koden med. Gråt = pladsholder, skriv bare ovenpå."; -"web_live_edit_label" = "Java-kode"; -"web_parse_line_fmt" = "Linje %d: %@"; -"web_parse_missing" = "Her mangler stadig: %@"; -"web_parse_extra" = "For meget: %@"; -"web_parse_unclosed_string" = "Det afsluttende %@ mangler."; -"web_parse_else_without_if" = "Denne “else” har ingen “if”."; -"web_parse_case_outside_switch" = "“case” og “default” hører til i en “switch”."; -"web_parse_missing_do_while" = "Efter “do { … }” mangler “while (betingelse);”."; -"web_parse_more_methods" = "Indtil videre tæller kun én metode her. Opret andre underprogrammer i egne faner."; -"web_parse_missing_import" = "Øverst mangler %@ Uden denne linje kender Java ikke Scanner."; -"web_parse_missing_scanner" = "Før mangler %@ Uden den linje findes Scanneren ikke."; -"web_parse_unknown_type" = "Java kender ikke denne datatype. Mente du %@?"; -"web_quickfix_insert_fmt" = "Forslag: indsæt %@"; -"web_quickfix_replace_fmt" = "Forslag: %1$@ i stedet for %2$@"; -"web_parse_wrong_case" = "Java skelner mellem store og små bogstaver. Det rigtige er %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Vis strukturdiagram"; -"web_editor_hide" = "Skjul strukturdiagram"; -"web_keywords_title" = "Kode-nøgleord"; -"web_keywords_hint" = "if, while, for i stedet for elementnavne"; -"web_palette_compact" = "Elementer altid kompakte"; -"web_palette_compact_hint" = "Symbol med lille navn nedenunder, ellers kun når kolonnen er smal"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Anvendt software"; -"web_about_software_text" = "Kodeeditoren bruger CodeMirror og Lezer (MIT-licens). Med Swing-understøttelse indlæser VisuStruct CheerpJ fra Leaning Technologies (Community-licens) og javac fra OpenJDK."; -"web_about_licenses" = "Licenstekster"; -"web_live_run" = "Kør"; -"web_live_run_title" = "Kør programmet: output og input vises nedenfor"; -"web_live_run_finished" = "Programmet er færdigt."; -"web_live_run_stopped" = "Stoppet."; -"web_console" = "Konsol"; -"web_console_hide" = "Skjul konsollen"; -"web_console_resize" = "Ændr konsollens højde"; -"web_diagram_tools" = "Diagramværktøjer"; -"web_files_title" = "Filer"; -"web_files_show" = "Vis filer"; -"web_files_hide" = "Skjul filer"; -"web_files_resize" = "Ændr bredden på filer"; -"web_files_new_folder" = "Ny mappe"; -"web_files_switch" = "Vælg en anden mappe"; -"web_files_delete" = "Slet"; -"web_files_in_browser" = "I denne browsers lager"; -"web_files_save_hint_fmt" = "Når du gemmer, lægges programmet i %@."; -"web_files_empty" = "Intet gemt endnu. Når du gemmer, opretter VisuStruct her en mappe med dagens dato."; -"web_files_reconnect_hint" = "Efter genindlæsning spørger browseren igen, om VisuStruct må bruge din mappe."; -"web_files_reconnect_fmt" = "Åbn mappen “%@”"; -"web_files_connect_hint" = "Vælg en mappe som arbejdsområde. VisuStruct opretter en mappe for hver dag og gemmer diagram og kode."; -"web_files_connect" = "Vælg mappe"; -"web_files_browser_hint" = "Denne browser kan ikke åbne mapper på enheden (fx på iPad). Dine filer bliver i browserens lager."; -"web_files_browser" = "Gem i browseren"; -"web_files_delete_confirm_fmt" = "Vil du virkelig slette “%@”?"; -"web_files_folder_prompt" = "Navn på den nye mappe:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Gemt: %@"; -"web_files_create" = "Opret"; -"web_learn_ludwig_tip" = "Ludwigs tip"; -"web_game_title" = "Spil: Sandt eller falsk?"; -"web_game_intro" = "Ludwig viser dig værdier og en betingelse. Er betingelsen sand (true) eller falsk (false)? Ti runder, sværere og sværere."; -"web_game_start" = "Start spillet"; -"web_game_round_fmt" = "Runde %@ af %@"; -"web_game_score_fmt" = "Point: %@"; -"web_game_true" = "sand (true)"; -"web_game_false" = "falsk (false)"; -"web_game_right" = "Rigtigt!"; -"web_game_wrong" = "Ikke helt. Sådan regnes det ud:"; -"web_game_next" = "Videre"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ af %@ rigtige."; -"web_game_perfect" = "Alt rigtigt – Ludwig er dybt imponeret!"; -"web_game_good" = "Flot! Se på dine fejl og prøv igen."; -"web_game_practice" = "Øvelse gør mester. Læs sandhedstabellen igen, og spil en runde mere."; -"web_game_again" = "Spil igen"; -"web_game_wrong_short" = "Desværre forkert."; -"web_game_practice_short" = "Øvelse gør mester. Læs forklaringerne igen og spil en runde til."; -"web_game_clicks" = "Klik:"; -"web_game_nobody" = "Ingen lytter – der sker ingenting."; -"web_game_event_fmt" = "Klik på »%@«: lytteren hører det og kører sin kode."; -"web_game_try" = "Prøv nu selv knapperne i vinduet."; -"web_game_time_fmt" = "%@ s tilbage"; -"web_game_defused" = "✂️ Desarmeret! Du fandt fejlen."; -"web_game_boom_time" = "💥 Bum! Tiden er gået."; -"web_game_boom_wrong" = "💥 Bum! Det var den forkerte linje."; -"web_game_boom_part" = "💥 Bang! Rigtig linje, men fejlen er i en anden del."; -"web_game_boom_wrong_part" = "💥 Bang! Fejlen er i en anden linje."; -"web_game_bomb_parts_hint" = "Tryk præcis på den forkerte del."; -"web_game_fix" = "Den rigtige linje:"; -"web_game_order_hint" = "Tryk på trinnene i den rigtige rækkefølge."; -"web_debug_step" = "Trin"; -"web_debug_step_title" = "Trin: kør én sætning og hold pause igen"; -"web_debug_continue" = "Fortsæt til næste breakpoint"; -"web_debug_paused_fmt" = "Sat på pause før linje %d."; -"web_debug_gutter_title" = "Klik på et linjenummer for at sætte eller fjerne et breakpoint"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Skift bredde på struktogrammet"; -"web_palette_resize" = "Skift bredde på elementerne"; -"web_palette_hide" = "Skjul elementerne"; -"web_palette_show" = "Vis elementerne"; -"web_code_scopes" = "Farvede kodeblokke"; -"web_code_scopes_hint" = "Klasse, metode, løkke, forgrening i egne felter"; -"web_pseudocode" = "Pseudokode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Omdøb variabel …"; -"web_rename_title" = "Omdøb variabel"; -"web_rename_old" = "Navn"; -"web_rename_new" = "Nyt navn"; -"web_rename_hint" = "Ændrer hvert sted med præcis denne stavemåde (tal er ikke Tal), i koden og i strukturdiagrammet. Tekst i \"…\" og kommentarer bliver."; -"web_rename_count_fmt" = "Forekommer %d gange."; -"web_rename_exists_fmt" = "“%@” findes allerede: bagefter ville begge være én variabel."; -"web_rename_invalid" = "Et navn begynder med et bogstav og indeholder kun bogstaver, cifre og _."; -"web_rename_keyword_fmt" = "“%@” er et Java-nøgleord."; -"web_rename_apply" = "Omdøb"; -"web_rename_cancel" = "Annuller"; diff --git a/web/src/i18n/de.strings b/web/src/i18n/de.strings deleted file mode 100644 index 04a6ed97..00000000 --- a/web/src/i18n/de.strings +++ /dev/null @@ -1,800 +0,0 @@ -/* Texte, die es nur in der Web-Version gibt. Alle anderen kommen aus den Localizable.strings der App. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-Struktogramm"; -"web_key_ctrl" = "Strg"; -"web_key_delete" = "Entf"; -"web_key_shift" = "Umschalt"; -"web_export" = "Exportieren …"; -"web_export_title" = "Als Bild oder PDF speichern oder drucken"; -"web_export_png" = "Bild (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-Datei (.visustruct)"; -"web_export_failed_fmt" = "Export fehlgeschlagen: %@"; -"web_print_hint" = "Über „Exportieren … → PDF“ lässt sich das Diagramm speichern und dann drucken."; -"web_delete" = "Löschen"; -"web_language_title" = "Sprache der Oberfläche"; -"web_simulation_start" = "Simulation starten"; -"web_simulation_end" = "Simulation beenden"; -"web_simulation_start_title" = "Struktogramm Schritt für Schritt ausführen"; -"web_simulation_end_title" = "Simulation beenden und weiter bearbeiten"; -"web_palette_hint" = "Ins Struktogramm ziehen"; -"web_tabs_label" = "Diagramme"; -"web_status_modified" = "geändert"; -"web_inspector_title" = "Objektinspektor"; -"web_inspector_help_drop" = "Elemente aus dem Bereich „Elemente“ auf eine Kachel ziehen: oben = davor, unten = dahinter, auf „ø“ = ersetzen. Vorhandene Elemente lassen sich mit der Maus verschieben."; -"web_inspector_help_inline" = "Doppelklick oder Doppeltipp auf ein Element: Text direkt im Struktogramm ändern (Enter übernimmt, Esc bricht ab). Auch Fallnamen lassen sich so ändern."; -"web_inspector_help_files" = "Dateien können auch einfach ins Fenster gezogen werden."; -"web_case_name" = "Fallname"; -"web_case_name_placeholder" = "z. B. 1, ok, default"; -"web_copied" = "Element kopiert."; -"web_discard_message_fmt" = "Die Änderungen an „%@“ gehen verloren."; -"web_err_unreadable_fmt" = "„%@“ ist keine lesbare VisuStruct-Datei (%@)."; -"web_saved_fmt" = "„%@“ gespeichert."; -"web_saved_txt_fmt" = "Gespeichert als „%@“. Zum Verwenden die Endung .txt entfernen."; -"web_drop_impossible" = "Ablegen nicht möglich"; -"web_sim_delay" = "Pause pro Schritt"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Sprache"; -"web_codegen_comments" = "Diagramm-Text als Kommentare"; -"web_codegen_empty_else" = "Leeren else-Zweig im Code ausgeben"; -"web_copied_check" = "Kopiert ✓"; -"web_copy_manual" = "Markiert, mit Strg+C kopieren"; -"web_run_stopped_loop" = "Ausführung gestoppt: mögliche Endlosschleife."; -"web_run_cancelled" = "Ausführung abgebrochen."; -"web_run_error" = "Fehler beim Ausführen."; -"web_run_dropped_fmt" = "… %d frühere Zeilen ausgeblendet"; - -/* Lernbereich (Lektionen gibt es vorerst nur auf Deutsch) */ -"web_learn_title" = "Lernen"; -"web_learn_hide" = "Lernbereich einklappen"; -"web_learn_show" = "Lernbereich aufklappen: Lektionen, Aufgaben und Nachschlagen"; -"web_learn_lessons" = "Lektionen"; -"web_learn_tasks" = "Aufgaben"; -"web_learn_reference" = "Nachschlagen"; -"web_learn_intro" = "Schritt für Schritt lernen, mit Übungen. Dein Fortschritt wird in diesem Browser gemerkt."; -"web_learn_tasks_intro" = "Kleine Projekte ohne Anleitung. Jede Aufgabe nennt die Lektion, die du vorher abgeschlossen haben solltest."; -"web_learn_step_of_fmt" = "Schritt %d von %d"; -"web_learn_lesson_fmt" = "Lektion %d"; -"web_learn_all_lessons" = "Alle Lektionen"; -"web_learn_all_tasks" = "Alle Aufgaben"; -"web_learn_back" = "Zurück"; -"web_learn_next" = "Weiter"; -"web_learn_finish" = "Lektion abschließen"; -"web_learn_done" = "Abgeschlossen"; -"web_learn_after_fmt" = "Nach Lektion %d: %@"; -"web_learn_level_fmt" = "Schwierigkeit %d von 3"; -"web_learn_exercise" = "Übung"; -"web_learn_brag" = "Angeberwissen mit Ludwig"; -"web_learn_solved" = "geschafft"; -"web_learn_open_exercise" = "Im Editor öffnen"; -"web_learn_solve_in_editor" = "Löse diese Übung im %@."; -"web_learn_solve_in_code" = "Löse diese Übung im %@."; -"web_learn_solve_in_both" = "Löse diese Übung im %1$@ und im %2$@."; -"web_learn_open_in_editor" = "Im Editor öffnen"; -"web_learn_replay" = "Noch einmal abspielen"; -"web_learn_graphic_labels" = "👆 Klick aufs Bild: Beschriftung ein oder aus"; -"web_learn_check" = "Prüfen"; -"web_learn_hint" = "Tipp"; -"web_learn_reset" = "Von vorn"; -"web_learn_reset_confirm" = "Wirklich von vorn?"; -"web_learn_solution" = "Musterlösung ansehen"; -"web_learn_hint_more" = "Noch ein Tipp"; -"web_learn_hint_fmt" = "Tipp %d von %d"; -"web_learn_teacher" = "Für Lehrkräfte"; -"web_learn_teacher_intro" = "Mit dem Lehrercode werden bei allen Übungen die Musterlösungen sichtbar. Der Browser merkt sich das."; -"web_learn_teacher_code" = "Lehrercode"; -"web_learn_teacher_unlock" = "Freischalten"; -"web_learn_teacher_wrong" = "Der Code stimmt nicht."; -"web_learn_teacher_on" = "Lehrermodus ist an: Bei allen Übungen sind die Musterlösungen sichtbar."; -"web_learn_teacher_off" = "Lehrermodus beenden"; -"web_learn_open_first" = "Öffne die Übung zuerst im Editor und baue dort dein Struktogramm."; -"web_learn_empty" = "Dein Struktogramm ist noch leer. Ziehe zuerst Elemente aus dem Bereich „Elemente“ hinein."; -"web_learn_passed" = "Geschafft! Dein Struktogramm macht genau, was es soll."; -"web_learn_failed" = "Noch nicht ganz. Vergleiche die erwartete mit deiner Ausgabe:"; -"web_learn_missing_fmt" = "Dein Struktogramm braucht noch: %@"; -"web_learn_input_fmt" = "Eingabe: %@"; -"web_learn_no_input" = "Ohne Eingabe"; -"web_learn_expected" = "Erwartet"; -"web_learn_actual" = "Deine Ausgabe"; -"web_learn_missing_input" = "Dein Struktogramm fragt nach mehr Eingaben als vorgesehen."; -"web_learn_unused_input" = "Dein Struktogramm fragt nicht alle Eingaben ab."; -"web_learn_missing_line_break" = "Fast! Es fehlt nur ein Zeilenumbruch. System.out.print schreibt weiter in derselben Zeile, System.out.println beginnt danach eine neue."; -"web_learn_too_many_steps" = "Abbruch nach 100 000 Schritten. Läuft eine Schleife endlos?"; -"web_learn_score_fmt" = "%d von %d richtig"; -"web_learn_cloze_hint" = "Tippe die fehlenden Wörter in die Lücken und drücke „Prüfen“."; -"web_learn_cloze_score_fmt" = "%d von %d Lücken richtig"; -"web_learn_cloze_all_right" = "Alle Lücken richtig!"; -"web_learn_cloze_solution" = "Lösung zeigen"; -"web_learn_cloze_again" = "Nochmal"; -"web_learn_pap_hint" = "Tippe ein Element rechts an und dann seinen Platz im Plan. Du kannst es auch hinziehen. Ein belegter Platz gibt sein Element beim Antippen zurück."; -"web_learn_pap_score_fmt" = "%d von %d Elementen richtig"; -"web_learn_pap_all_right" = "Alles richtig! Dein PAP ist fertig."; -"web_learn_pap_missing_fmt" = "Es fehlen noch %d Elemente."; -"web_learn_pap_empty" = "Leerer Platz"; -"web_learn_pap_yes" = "ja"; -"web_learn_pap_no" = "nein"; -"web_learn_pap_legend_oval" = "Start oder Ende"; -"web_learn_pap_legend_rect" = "Anweisung"; -"web_learn_pap_legend_diamond" = "Verzweigung: Frage mit ja und nein"; -"web_learn_pap_legend_para" = "Ein- oder Ausgabe"; -"web_learn_review" = "Nachlesen"; -"web_learn_terms" = "Fachbegriffe"; -"web_inspector_resize" = "Höhe des Objektinspektors ändern: Linie nach oben oder unten ziehen oder wischen (Doppelklick: Standard)"; -"web_learn_resize" = "Breite des Lernbereichs ändern (Doppelklick: zurücksetzen)"; -"web_learn_competences_intro" = "Am Ende dieser Lektion kann ich …"; -"web_learn_rating_sun" = "kann ich sicher"; -"web_learn_rating_cloud" = "teilweise"; -"web_learn_rating_umbrella" = "noch nicht"; -"web_learn_review_title" = "Deine Checkliste im Vergleich"; -"web_learn_review_hint" = "Hier weichen deine Einschätzung und der Test voneinander ab. Lies den Schritt noch einmal nach und schätze dich in der Checkliste neu ein."; -"web_learn_review_match" = "Deine Einschätzung passt zu deinem Test. Gut gemacht!"; -"web_learn_review_overrated_fmt" = "Du hast ☀️ gewählt, aber Frage %@ war falsch."; -"web_learn_review_underrated" = "Alle Fragen dazu waren richtig. Du kannst mehr, als du denkst!"; -"web_learn_review_unrated" = "noch nicht eingeschätzt"; -"web_learn_back_to_checklist" = "Zurück zur Checkliste"; -"web_learn_step_fmt" = "Schritt %d: %@"; -"web_sim_call_arguments_fmt" = "Der Aufruf passt nicht zur Kopfzeile von %@. Prüfe die Werte in den Klammern."; -"web_sim_missing_return_fmt" = "%@ hat keinen Wert mit return zurückgegeben."; -"web_sim_file_not_found_fmt" = "Es gibt keine Datei %@."; -"web_sim_end_of_file_fmt" = "Die Datei %@ hat keine Zeile mehr."; -"web_sim_null_value_fmt" = "%@ ist null."; -"web_sim_unknown_variable_case_fmt" = "Unbekannte Variable: %1$@. Java unterscheidet Groß- und Kleinschreibung: Meintest du %2$@?"; -"web_sim_written_file_fmt" = "%@ (gespeichert)"; -"web_site_home" = "Startseite"; -"web_site_imprint" = "Impressum"; -"web_site_privacy" = "Datenschutz"; - -/* Code (früher „Code live“, dann „Programmieren“) */ -"web_live_title" = "Code"; -"web_live_hide" = "Code ausblenden"; -"web_scale_canvas" = "Textgröße Struktogramm"; -"web_scale_code" = "Textgröße Code"; -"web_live_indent" = "Einrücken"; -"web_live_indent_title" = "Code einrücken und formatieren: jede } auf eigener Zeile, Leerzeichen um Operatoren, jede Ebene vier Leerzeichen tiefer (markierte Zeilen, sonst alles)"; -"web_live_show" = "Code aufklappen"; -"web_live_resize" = "Breite des Codebereichs ändern (Doppelklick: zurücksetzen)"; -"web_live_hint" = "Der Code wächst mit deinem Struktogramm. Grau = Platzhalter: Schreib im Objektinspektor, dann steht dort dein Text. Klick auf eine Zeile markiert das Element."; -"web_live_ph_selector" = "Variable"; -"web_live_ph_code" = "Dein Code"; -"web_live_ph_end" = "Ende"; -"web_live_language" = "Programmiersprache"; -"web_live_java_class" = "Als Klasse"; -"web_live_java_class_hint" = "Klassenname = Titel des Struktogramms"; -"web_live_java_main" = "Mit main-Methode"; -"web_settings_display" = "Darstellung"; -"web_live_swing" = "Echtes Java (mit Swing)"; -"web_live_swing_hint" = "Echtes Java mit Fenstern (CheerpJ). Lädt beim ersten Ausführen einmalig ca. 40 MB."; -"web_live_swing_windows" = "Java-Fenster"; -"web_live_swing_hide" = "Java-Fenster schließen"; -"web_live_swing_loading" = "Java wird geladen (beim ersten Mal ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompiliere …"; -"web_live_swing_running" = "Programm läuft."; -"web_live_swing_compile_failed" = "Fehler beim Kompilieren, siehe oben."; -"web_live_swing_failed" = "Java konnte nicht geladen werden. Prüfe die Internetverbindung."; -"web_live_hint_edit" = "Tippe hier Java-Code, dann baut sich das Struktogramm mit. Oder arbeite im Struktogramm, dann wächst der Code mit. Grau = Platzhalter, einfach drüberschreiben."; -"web_live_edit_label" = "Java-Code"; -"web_parse_line_fmt" = "Zeile %d: %@"; -"web_parse_missing" = "Hier fehlt noch: %@"; -"web_parse_extra" = "Das ist zu viel: %@"; -"web_parse_unclosed_string" = "Hier fehlt das schließende %@."; -"web_parse_else_without_if" = "Zu diesem „else“ gehört kein „if“."; -"web_parse_case_outside_switch" = "„case“ und „default“ gehören in ein „switch“."; -"web_parse_missing_do_while" = "Nach „do { … }“ fehlt noch „while (Bedingung);“."; -"web_parse_more_methods" = "Hier zählt im Moment nur eine Methode. Lege weitere Unterprogramme als eigene Reiter an."; -"web_parse_missing_import" = "Ganz oben fehlt %@ Ohne diese Zeile kennt Java den Scanner nicht."; -"web_parse_missing_scanner" = "Vorher fehlt %@ Ohne diese Zeile gibt es den Scanner nicht."; -"web_parse_unknown_type" = "Diesen Datentyp kennt Java nicht. Meintest du %@?"; -"web_quickfix_insert_fmt" = "Vorschlag: %@ einfügen"; -"web_quickfix_replace_fmt" = "Vorschlag: %1$@ statt %2$@"; -"web_parse_wrong_case" = "Java unterscheidet Groß- und Kleinschreibung. Richtig ist %@."; -"web_editor_title" = "Struktogramm"; -"web_editor_show" = "Struktogramm aufklappen"; -"web_editor_hide" = "Struktogramm einklappen"; -"web_keywords_title" = "Code-Keywords"; -"web_keywords_hint" = "if, while, for statt Verzweigung, For-Schleife …"; -"web_palette_compact" = "Elemente immer kompakt"; -"web_palette_compact_hint" = "Symbol mit kleinem Namen darunter, sonst nur bei schmaler Spalte"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Verwendete Software"; -"web_about_software_text" = "Der Code-Editor nutzt CodeMirror und Lezer (MIT-Lizenz). Mit Swing-Unterstützung lädt VisuStruct CheerpJ von Leaning Technologies (Community-Lizenz) und javac aus OpenJDK."; -"web_about_licenses" = "Lizenztexte"; -"web_live_run" = "Ausführen"; -"web_live_run_title" = "Programm ausführen: Ausgaben und Eingaben erscheinen darunter"; -"web_live_run_finished" = "Programm beendet."; -"web_live_run_stopped" = "Gestoppt."; -"web_console" = "Konsole"; -"web_console_hide" = "Konsole ausblenden"; -"web_console_resize" = "Höhe der Konsole ändern"; -"web_diagram_tools" = "Werkzeuge für das Struktogramm"; -"web_files_title" = "Dateien"; -"web_files_show" = "Dateien einblenden"; -"web_files_hide" = "Dateien ausblenden"; -"web_files_resize" = "Breite der Dateien ändern"; -"web_files_new_folder" = "Neuer Ordner"; -"web_files_switch" = "Anderen Ordner wählen"; -"web_files_delete" = "Löschen"; -"web_files_in_browser" = "Im Speicher dieses Browsers"; -"web_files_save_hint_fmt" = "Speichern legt das Programm in %@ ab."; -"web_files_empty" = "Noch nichts gespeichert. Beim Speichern legt VisuStruct hier einen Ordner mit dem heutigen Datum an."; -"web_files_reconnect_hint" = "Der Browser fragt nach dem Neuladen noch einmal, ob VisuStruct deinen Ordner benutzen darf."; -"web_files_reconnect_fmt" = "Ordner „%@“ öffnen"; -"web_files_connect_hint" = "Wähle einen Ordner als Arbeitsbereich, am besten einen neuen Ordner „VisuStruct-Dateien“ in deinen Dokumenten. VisuStruct legt darin für jeden Tag einen Ordner an und speichert Struktogramm und Code."; -"web_files_connect" = "Ordner wählen"; -"web_files_browser_hint" = "Dieser Browser kann keine Ordner auf dem Gerät öffnen (z. B. auf dem iPad). Deine Dateien bleiben dann im Speicher dieses Browsers."; -"web_files_browser" = "Im Browser speichern"; -"web_files_delete_confirm_fmt" = "„%@“ wirklich löschen?"; -"web_files_folder_prompt" = "Name des neuen Ordners:"; -"web_files_default_name" = "Programm"; -"web_files_saved_fmt" = "Gespeichert: %@"; -"web_files_create" = "Anlegen"; -"web_learn_ludwig_tip" = "Ludwigs Tipp"; -"web_game_title" = "Spiel: Wahr oder falsch?"; -"web_game_intro" = "Ludwig zeigt dir Werte und eine Bedingung. Ist die Bedingung wahr (true) oder falsch (false)? Zehn Runden, es wird von Runde zu Runde kniffliger."; -"web_game_start" = "Spiel starten"; -"web_game_round_fmt" = "Runde %@ von %@"; -"web_game_score_fmt" = "Punkte: %@"; -"web_game_true" = "wahr (true)"; -"web_game_false" = "falsch (false)"; -"web_game_right" = "Richtig!"; -"web_game_wrong" = "Leider falsch. So wird gerechnet:"; -"web_game_next" = "Weiter"; -"web_game_finish" = "Ergebnis"; -"web_game_done_fmt" = "%@ von %@ richtig."; -"web_game_perfect" = "Alles richtig – Ludwig ist schwer beeindruckt!"; -"web_game_good" = "Stark! Schau dir die Fehler noch einmal an und versuch es nochmal."; -"web_game_practice" = "Übung macht den Meister. Lies dir die Wahrheitstabelle oben noch einmal durch und spiel noch eine Runde."; -"web_game_again" = "Noch einmal"; -"web_game_wrong_short" = "Leider falsch."; -"web_game_practice_short" = "Übung macht den Meister. Lies dir die Erklärungen noch einmal durch und spiel noch eine Runde."; -"web_game_clicks" = "Klicks:"; -"web_game_nobody" = "Niemand hört zu – nichts passiert."; -"web_game_event_fmt" = "Klick auf „%@“: Der Zuhörer hört es und führt seinen Code aus."; -"web_game_try" = "Probier die Knöpfe im Fenster jetzt selbst aus."; -"web_game_time_fmt" = "Noch %@ s"; -"web_game_defused" = "✂️ Entschärft! Du hast den Fehler gefunden."; -"web_game_boom_time" = "💥 Bumm! Die Zeit ist abgelaufen."; -"web_game_boom_wrong" = "💥 Bumm! Das war die falsche Zeile."; -"web_game_boom_part" = "💥 Bumm! Die Zeile stimmt, aber der Fehler steckt in einem anderen Teil."; -"web_game_boom_wrong_part" = "💥 Bumm! Der Fehler steckt in einer anderen Zeile."; -"web_game_bomb_parts_hint" = "Tippe genau auf den falschen Teil."; -"web_game_fix" = "So stimmt die Zeile:"; -"web_game_order_hint" = "Tippe die Schritte in der richtigen Reihenfolge an."; -"web_debug_step" = "Einzelschritt"; -"web_debug_step_title" = "Einzelschritt: eine Anweisung ausführen und wieder anhalten"; -"web_debug_continue" = "Weiter bis zum nächsten Haltepunkt"; -"web_debug_paused_fmt" = "Angehalten vor Zeile %d."; -"web_debug_gutter_title" = "Auf die Zeilennummer klicken: Haltepunkt setzen oder entfernen"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Caesar-Scheibe"; -"web_learn_caesar_hint" = "Drehe die innere Scheibe oder tippe auf −1 und +1. Außen steht der Klartext, innen der Geheimtext."; -"web_learn_caesar_key_fmt" = "Schlüssel %d"; -"web_learn_caesar_plain" = "Klartext"; -"web_learn_caesar_secret" = "Geheimtext"; -"web_learn_caesar_encrypt" = "Verschlüsseln"; -"web_learn_caesar_decrypt" = "Entschlüsseln"; -"web_learn_caesar_back" = "Scheibe eins zurück"; -"web_learn_caesar_forward" = "Scheibe eins weiter"; -"web_learn_rescue_station_fmt" = "Station %@ von %@: %@"; -"web_learn_rescue_number" = "Deine Zahl"; -"web_learn_rescue_not_number" = "Gib eine ganze Zahl ein."; -"web_learn_rescue_wrong" = "Noch nicht. Lies den Code noch einmal Zeichen für Zeichen."; -"web_learn_rescue_letter_moved" = "Der Buchstabe wandert ins Kreuzworträtsel."; -"web_learn_rescue_alphabet" = "Aus deiner Zahl wird ein Buchstabe:"; -"web_learn_rescue_to_crossword" = "Zum Kreuzworträtsel"; -"web_learn_rescue_crossword" = "Das Kreuzworträtsel"; -"web_learn_rescue_across" = "→ Waagrecht"; -"web_learn_rescue_down" = "↓ Senkrecht"; -"web_learn_rescue_cell_fmt" = "Feld Zeile %@, Spalte %@"; -"web_learn_rescue_keyword" = "Geheimes Wort:"; -"web_learn_rescue_cw_wrong" = "Die rot markierten Felder stimmen noch nicht."; -"web_learn_rescue_cw_done" = "Gelöst! Die gelben Felder ergeben das geheime Wort."; -"web_learn_rescue_to_gate" = "Zum Tor"; -"web_learn_rescue_gate" = "Das Tor"; -"web_learn_rescue_word" = "Geheimes Wort"; -"web_learn_rescue_open" = "Tor öffnen"; -"web_learn_rescue_gate_case" = "Fast! equals unterscheidet Groß- und Kleinbuchstaben. Schreib das Wort genau so wie im Rätsel."; -"web_learn_rescue_gate_wrong" = "Das Tor bleibt zu. Schau noch einmal auf die gelben Felder."; -"web_learn_rescue_freed" = "🎉 Das Tor ist offen!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "Womit möchtest du arbeiten?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: Struktogramme und Java mit dem ganzen Kurs. Wechseln geht jederzeit im Zahnrad."; -"web_visubot_world_visubot_hint" = "VisuBot: den Roboter mBot2 programmieren, mit eigenen Lektionen und Aufgaben. Wechseln geht jederzeit im Zahnrad."; -"web_visubot_connect" = "Mit echtem mBot2 verbinden"; -"web_visubot_upload_button" = "Übertragen"; -"web_visubot_blocks" = "Blöcke"; -"web_visubot_blocks_title" = "Das Struktogramm als Blöcke zeigen"; -"web_visubot_blocks_back" = "Struktogramm"; -"web_visubot_blocks_back_title" = "Zurück zum Struktogramm"; -"web_visubot_blocks_start" = "Wenn das Programm startet"; -"web_visubot_blocks_empty" = "Hier kommt ein Block hin"; -"web_visubot_blocks_hint" = "Dasselbe Programm als Blöcke. Ziehe Blöcke aus „Elemente“ hinein, verschiebe sie oder wirf sie in den Papierkorb. Zahlen und Texte in den weißen Feldern änderst du per Klick direkt im Block, alles andere unten im Objektinspektor. Sensoren ziehst du auf eine Bedingung oder genau auf ein Feld, das sie ersetzen sollen. Operatoren und Variablen ziehst du genauso auf ein Feld oder eine Bedingung."; -"web_visubot_blocks_trash" = "Papierkorb"; -"web_visubot_blocks_count" = "zähle mit"; -"web_visubot_blocks_count_from" = "von"; -"web_visubot_blocks_count_below" = "bis unter"; -"web_visubot_blocks_count_to" = "bis"; -"web_visubot_blocks_count_above" = "bis über"; -"web_visubot_blocks_count_step" = "in Schritten von"; -"web_visubot_blocks_repeat" = "wiederhole"; -"web_visubot_blocks_if" = "wenn"; -"web_visubot_blocks_else" = "sonst"; -"web_visubot_blocks_while" = "solange"; -"web_visubot_blocks_endless" = "wiederhole immer"; -"web_visubot_blocks_do" = "mache"; -"web_visubot_blocks_not" = "nicht"; -"web_visubot_blocks_and" = "und"; -"web_visubot_blocks_or" = "oder"; -"web_visubot_blocks_break" = "verlasse die Schleife"; -"web_visubot_blocks_repeat_times" = "mal"; -"web_visubot_blocks_random_from" = "Zufallszahl von"; -"web_visubot_blocks_random_to" = "bis"; -"web_visubot_operators" = "Operatoren"; -"web_visubot_variables" = "Variablen"; -"web_visubot_control" = "Steuerung"; -"web_visubot_el_repeat" = "wiederhole 10 mal"; -"web_visubot_upload_title" = "Programm auf den mBot2 übertragen"; -"web_visubot_upload_step1" = "Schließe den mBot2 mit dem USB-Kabel an und schalte ihn ein. Das geht nur in Chrome oder Edge."; -"web_visubot_upload_step2" = "Klicke auf „Jetzt übertragen“ und wähle „USB Serial“. Der mBot2 speichert dein Programm und startet neu."; -"web_visubot_upload_step3" = "Zieh das Kabel ab und setze den mBot2 an den Start. Drückst du Taste A, fährt dein Programm los. Es bleibt gespeichert, auch nach dem Ausschalten."; -"web_visubot_upload_start" = "Jetzt übertragen"; -"web_visubot_upload_busy" = "Wird übertragen … Kabel nicht abziehen."; -"web_visubot_upload_done" = "Fertig! Kabel ab, mBot2 an den Start, Taste A drücken."; -"web_visubot_upload_failed" = "Das Übertragen hat nicht geklappt. Starte den mBot2 neu und versuche es noch einmal."; -"web_visubot_upload_label" = "A = Start"; -"web_visubot_upload_error_input" = "Dein Programm fragt nach einer Eingabe. Am mBot2 gibt es keine Tastatur, deshalb geht das nicht."; -"web_visubot_upload_error_files" = "Dein Programm arbeitet mit Dateien. Das geht am mBot2 nicht."; -"web_visubot_upload_error_empty" = "Dein Programm ist noch leer."; -"web_visubot_disconnect" = "Trennen"; -"web_visubot_connecting" = "Verbinde … (der CyberPi startet kurz neu)"; -"web_visubot_connected" = "Echter mBot2 fährt mit"; -"web_visubot_no_serial" = "Echter mBot2: nur in Chrome oder Edge am Computer."; -"web_visubot_link_error" = "Verbindung getrennt"; -"web_visubot_world" = "VisuBot-Welt"; -"web_visubot_no_3d" = "Die 3D-Ansicht kann in diesem Browser nicht starten."; -"web_visubot_distance" = "Abstand"; -"web_visubot_line" = "Linie"; -"web_visubot_bumps" = "an die Wand gestoßen"; -"web_visubot_el_forward" = "fahre vor"; -"web_visubot_el_backward" = "fahre zurück"; -"web_visubot_el_right" = "drehe rechts"; -"web_visubot_el_left" = "drehe links"; -"web_visubot_el_led" = "LEDs an"; -"web_visubot_el_tone" = "Ton spielen"; -"web_visubot_drive" = "Fahrt"; -"web_visubot_depth_fmt" = "Die Schleifen müssen %d-fach ineinander stecken."; -"web_visubot_tap_part" = "Dreh den mBot2 mit der Maus oder dem Finger. Tippst du ein Teil an, siehst du seinen Namen."; -"web_visubot_quiz_right" = "Richtig!"; -"web_visubot_quiz_wrong_fmt" = "Das ist: %@. Versuch es noch einmal."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Bildschirm"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Tasten A, B und Home"; -"web_visubot_part_leds" = "RGB-LEDs"; -"web_visubot_part_lightSensor" = "Lichtsensor"; -"web_visubot_part_microphone" = "Mikrofon"; -"web_visubot_part_shield" = "mBot2-Shield mit Akku"; -"web_visubot_part_powerSwitch" = "Ein-Aus-Schalter"; -"web_visubot_part_motors" = "Encoder-Motoren"; -"web_visubot_part_wheels" = "Räder"; -"web_visubot_part_ultrasonic" = "Ultraschallsensor"; -"web_visubot_part_lineSensor" = "Liniensensor (Quad-RGB)"; -"web_visubot_part_caster" = "Kugelrolle"; -"web_visubot_part_chassis" = "Chassis"; -"web_visubot_world_fmt" = "Welt %d"; -"web_visubot_goal_reached" = "Ziel erreicht"; -"web_visubot_goal_missed" = "Ziel nicht erreicht"; -"web_visubot_commands" = "mBot2-Befehle"; -"web_visubot_sensors" = "mBot2-Sensoren"; -"web_visubot_sensor_distance" = "Abstand"; -"web_visubot_sensor_line" = "Linie"; -"web_visubot_el_led_off" = "LEDs aus"; -"web_visubot_el_led_color" = "LED-Farbe setzen"; -"web_visubot_el_wait" = "kurz warten"; -"web_visubot_el_emotion" = "Gefühl zeigen"; -"web_visubot_el_text" = "Text anzeigen"; -"web_visubot_sensor_color" = "Farbe ist rot?"; -"web_visubot_sensor_button_a" = "Taste A gedrückt?"; -"web_visubot_sensor_button_b" = "Taste B gedrückt?"; -"web_visubot_sensor_joy_up" = "Joystick oben?"; -"web_visubot_sensor_joy_down" = "Joystick unten?"; -"web_visubot_sensor_joy_left" = "Joystick links?"; -"web_visubot_sensor_joy_right" = "Joystick rechts?"; -"web_visubot_sensor_brightness" = "Helligkeit"; -"web_visubot_sensor_loudness" = "Lautstärke"; -"web_visubot_sensor_pitch" = "Neigung vorn/hinten"; -"web_visubot_sensor_roll" = "Neigung seitlich"; -"web_visubot_sensor_shake" = "geschüttelt?"; -"web_visubot_el_drive" = "Räder einzeln"; -"web_visubot_el_stop" = "anhalten"; -"web_visubot_el_timer" = "Stoppuhr auf 0"; -"web_visubot_sensor_line_left" = "Linie links?"; -"web_visubot_sensor_line_right" = "Linie rechts?"; -"web_visubot_sensor_timer" = "Stoppuhr (s)"; -"web_visubot_sensor_battery" = "Akku (%)"; -"web_visubot_actions" = "Befehle"; -"web_visubot_led_on" = "LEDs an"; -"web_visubot_led_off" = "LEDs aus"; -"web_visubot_tone_yes" = "Ton"; -"web_visubot_tone_no" = "kein Ton"; -"web_visubot_controls" = "Sensoren ausprobieren"; -"web_visubot_controls_hint" = "Ohne echten mBot2: Hier stellst du ein, was die Sensoren messen. Tasten, Joystick und Schütteln gelten eine Sekunde lang."; -"web_visubot_shake" = "schütteln"; -"web_visubot_check" = "mBot2 testen"; -"web_visubot_check_hint" = "Probiere jeden Aktor einmal aus. Die Sensorwerte laufen live mit: Halte die Hand vor den Ultraschall, drück die Tasten, neige oder schüttle den mBot2. Heb ihn für die Räder hoch."; -"web_visubot_check_forward" = "Räder 1 s vorwärts"; -"web_visubot_check_spin" = "1 s auf der Stelle drehen"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Wo speicherst du deine Arbeit?"; -"web_learn_start_hint" = "Lernstand und Programme liegen in einem Ordner auf deinem Gerät. Der Lernstand steht dort in der Datei learnProgress.json."; -"web_learn_start_continue_fmt" = "Weiter mit „%@“"; -"web_learn_start_open" = "Vorhandenen Ordner öffnen"; -"web_learn_start_create_fmt" = "Neuen Ordner „%@“ anlegen"; -"web_learn_start_create_hint" = "Danach wählst du, wo er liegen soll, z. B. in „Dokumente“."; -"web_learn_start_folder" = "VisuStruct-Dateien"; -"web_learn_start_without" = "Ohne Ordner weiter"; -"web_learn_start_without_hint" = "Dann ist dein Lernstand nach dem Schließen weg."; -"web_visubot_preview_title" = "mBot2 live"; -"web_visubot_preview_hint" = "Zeigt sofort, wohin dein Programm den mBot2 fährt."; -"web_visubot_preview_sim_title" = "Simulation"; -"web_visubot_preview_sim_hint" = "Hier fährt der mBot2 mit, während die Simulation läuft."; -"web_visubot_world_in_preview" = "Der mBot2 fährt im Fenster „Simulation“."; -"web_visubot_preview_play" = "Fahrt abspielen"; -"web_visubot_preview_stop" = "Anhalten"; -"web_visubot_preview_hide" = "mBot2 live einklappen"; -"web_visubot_preview_show" = "mBot2 live zeigen"; -"web_visubot_preview_resize" = "Größe von mBot2 live ändern"; -"web_visubot_preview_stack" = "Unter den Code im Bereich Code"; -"web_visubot_preview_side" = "Als eigene Spalte"; -"web_visubot_preview_distance_fmt" = "Strecke: %d cm"; -"web_visubot_preview_input" = "braucht eine Eingabe, dafür die Simulation starten"; -"web_visubot_preview_limit" = "läuft sehr lange (Endlosschleife?), gezeigt ist der Anfang"; -"web_visubot_group_drive" = "Fahren und Drehen"; -"web_visubot_group_wheels" = "Räder einzeln"; -"web_visubot_group_leds" = "Licht"; -"web_visubot_group_sound" = "Ton"; -"web_visubot_group_screen" = "Bildschirm"; -"web_visubot_group_emotion" = "Gefühle"; -"web_visubot_group_wait" = "Warten und Stoppuhr"; -"web_visubot_group_distance" = "Abstand"; -"web_visubot_group_line" = "Linie"; -"web_visubot_group_color" = "Farben"; -"web_visubot_group_buttons" = "Tasten und Joystick"; -"web_visubot_group_light" = "Licht und Lautstärke"; -"web_visubot_group_tilt" = "Neigung und Schütteln"; -"web_visubot_group_timer" = "Stoppuhr und Akku"; -"web_swap_to_code" = "Code groß, Struktogramm schmal"; -"web_swap_to_editor" = "Struktogramm groß, Code schmal"; -"web_editor_resize" = "Breite des Struktogramms ändern"; -"web_palette_resize" = "Breite der Elemente ändern (Doppelklick: zurücksetzen)"; -"web_palette_hide" = "Elemente einklappen"; -"web_palette_show" = "Elemente aufklappen"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: Klassen in Java programmieren und als Klassendiagramm sehen oder zeichnen (ab Klasse 12). Wechseln geht jederzeit im Zahnrad."; -"web_visuobject_subtitle" = "Klassendiagramm"; -"web_visuobject_code" = "Java-Code"; -"web_visuobject_diagram" = "Klassendiagramm"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Neues Interface ins Diagramm und in den Code"; -"web_visuobject_layout" = "Neu anordnen"; -"web_visuobject_layout_tip" = "Alle Klassen automatisch anordnen: Oberklassen oben"; -"web_visuobject_add_field" = "neues Attribut …"; -"web_visuobject_add_method" = "neue Methode …"; -"web_visuobject_delete_class" = "Klasse löschen"; -"web_visuobject_new_class_name" = "NeueKlasse"; -"web_visuobject_new_interface_name" = "NeuesInterface"; -"web_visuobject_example" = "Beispiel"; -"web_visuobject_example_tip" = "Beispielprojekt Zoo laden: Tier und Hund im Package tiere, Besitzer in personen"; -"web_visuobject_new_tip" = "Neu: leeres Projekt (Rückgängig holt das alte zurück)"; -"web_visuobject_empty" = "Noch keine Klasse. Klicke links auf „+ Klasse“ oder tippe zum Beispiel „public class Hund { }“ in eine Datei."; -"web_visuobject_edit_hint" = "Doppelklick ändert. Schreibweise wie in UML: „- name: String“ oder „+ bellen(): void“. Leer lassen löscht."; -"web_visuobject_member_label" = "Attribut oder Methode in UML-Schreibweise"; -"web_visuobject_name_label" = "Name der Klasse"; -"web_visuobject_bad_name" = "„%@“ ist kein gültiger Name oder gibt es schon."; -"web_visuobject_bad_line" = "„%@“ verstehe ich nicht. Beispiel: - name: String oder + bellen(): void"; -"web_visuobject_open_failed" = "Die Datei konnte nicht gelesen werden."; -"web_visuobject_field_ph" = "- name: String"; -"web_visuobject_seq_diagram" = "Sequenzdiagramm"; -"web_visuobject_seq_empty" = "Das Sequenzdiagramm entsteht beim Debuggen: Klicke oben auf „Debuggen“ und gehe Schritt für Schritt durch das Programm."; -"web_visuobject_seq_no_calls" = "Noch keine Aufrufe eigener Methoden."; -"web_visuobject_seq_truncated" = "… gekürzt: Ab hier sind es zu viele Aufrufe."; -"web_visuobject_seq_hint" = "Jeder Pfeil ist ein Aufruf einer eigenen Methode, gestrichelt kommt die Rückgabe zurück. Klick auf einen Pfeil zeigt die Zeile im Code."; -"web_visuobject_dbg" = "Debuggen"; -"web_visuobject_dbg_tip" = "Programm Schritt für Schritt ausführen. Haltepunkt: auf die Zeilennummer klicken. Das Objektdiagramm zeigt dabei die Objekte im Speicher."; -"web_visuobject_dbg_continue" = "Weiter"; -"web_visuobject_dbg_continue_tip" = "Weiter bis zum nächsten Haltepunkt (F8)"; -"web_visuobject_dbg_pause" = "Pause"; -"web_visuobject_dbg_pause_tip" = "Programm anhalten"; -"web_visuobject_dbg_over" = "Schritt"; -"web_visuobject_dbg_over_tip" = "Nächste Zeile; Methoden laufen ganz durch (F6)"; -"web_visuobject_dbg_into" = "Hinein"; -"web_visuobject_dbg_into_tip" = "In die aufgerufene Methode hineingehen (F5)"; -"web_visuobject_dbg_out" = "Heraus"; -"web_visuobject_dbg_out_tip" = "Methode zu Ende ausführen und zurück zum Aufrufer (F7)"; -"web_visuobject_dbg_input" = "Eingabe für den Scanner, dann Enter"; -"web_visuobject_dbg_running" = "Debugger: Programm läuft …"; -"web_visuobject_dbg_waiting" = "Debugger: Das Programm wartet auf deine Eingabe."; -"web_visuobject_dbg_paused_fmt" = "Debugger: angehalten in %1$@, Zeile %2$@"; -"web_visuobject_dbg_exception" = "Programm mit einer Exception abgebrochen."; -"web_visuobject_dbg_code_changed" = "Code geändert: Debugger beendet."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Aufrufstapel"; -"web_visuobject_dbg_vars" = "Variablen"; -"web_visuobject_dbg_var_name" = "Name"; -"web_visuobject_dbg_var_type" = "Typ"; -"web_visuobject_dbg_no_vars" = "Keine Variablen"; -"web_visuobject_dbg_err_syntax" = "%1$@, Zeile %2$@: Das versteht der Debugger nicht (%3$@). Mit ▶ Start siehst du die Meldung von Java."; -"web_visuobject_dbg_err_symbol" = "%1$@, Zeile %2$@: %3$@ ist unbekannt."; -"web_visuobject_dbg_err_import" = "%1$@, Zeile %2$@: Ganz oben fehlt %3$@ Ohne diese Zeile kennt Java die Klasse nicht."; -"web_visuobject_dbg_err_typename" = "%1$@, Zeile %2$@: Diesen Datentyp kennt Java nicht. Meintest du %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, Zeile %2$@: %3$@ kann der Debugger noch nicht. Mit ▶ Start läuft das Programm in echtem Java."; -"web_visuobject_dbg_err_type" = "%1$@, Zeile %2$@: Hier passt etwas nicht zusammen (%3$@)."; -"web_visuobject_obj_diagram" = "Objektdiagramm"; -"web_visuobject_obj_new" = "Neues Objekt der Klasse"; -"web_visuobject_obj_add" = "Objekt"; -"web_visuobject_obj_empty" = "Noch keine Objekte. Wähle oben eine Klasse und klicke auf „+ Objekt“."; -"web_visuobject_obj_no_classes" = "Lege zuerst im Klassendiagramm eine Klasse an (nicht abstract)."; -"web_visuobject_obj_hint" = "Klicke auf ein Objekt, dann kannst du hier Name, Klasse und Werte ändern. Vom Griff am Objekt zu einem anderen ziehen verbindet sie über ein Referenzattribut."; -"web_visuobject_obj_live_hint" = "Live aus dem Debugger: So sehen die Objekte gerade im Speicher aus."; -"web_visuobject_obj_link" = "Zu einem anderen Objekt ziehen: Referenz setzen"; -"web_visuobject_obj_name" = "Name"; -"web_visuobject_obj_class" = "Klasse"; -"web_visuobject_obj_attribute" = "Attribut"; -"web_visuobject_obj_value" = "Wert"; -"web_visuobject_obj_delete" = "Objekt löschen"; -"web_visuobject_obj_bad_name" = "„%@“ ist kein gültiger Name für ein Objekt."; -"web_visuobject_obj_name_taken" = "Ein Objekt „%@“ gibt es schon."; -"web_visuobject_obj_no_ref" = "%1$@ hat kein Attribut, das auf %2$@ zeigen kann."; -"web_visuobject_tool_note" = "Notiz"; -"web_visuobject_tool_note_tip" = "Notiz: Klicke ins Klassendiagramm, dann tippe den Text. Mit dem Griff ziehst du eine gestrichelte Linie zu einer Klasse."; -"web_visuobject_note_ph" = "Notiz …"; -"web_visuobject_note_text" = "Text"; -"web_visuobject_note_attached" = "Verbunden mit"; -"web_visuobject_note_attach" = "Zu einer Klasse ziehen: gestrichelte Linie"; -"web_visuobject_note_delete" = "Notiz löschen"; -"web_visuobject_run" = "Start"; -"web_visuobject_run_tip" = "Programm mit echtem Java starten: alle Klassen werden übersetzt, gestartet wird die Klasse mit main (bevorzugt die offene)."; -"web_visuobject_stop" = "Stop"; -"web_visuobject_stop_tip" = "Programm beenden und seine Fenster schließen"; -"web_visuobject_stopped" = "Programm beendet."; -"web_visuobject_no_main" = "Keine Klasse mit public static void main(String[] args) gefunden."; -"web_visuobject_running_fmt" = "%@ läuft …"; -"web_visuobject_console_clear" = "Leeren"; -"web_visuobject_section_fields" = "Attribute"; -"web_visuobject_section_refs" = "Referenzattribute"; -"web_visuobject_section_ctors" = "Konstruktor"; -"web_visuobject_section_methods" = "Methoden"; -"web_visuobject_insp_sections" = "Abschnitts-Kommentare einfügen"; -"web_visuobject_insp_sort" = "Einsortieren"; -"web_visuobject_insp_sections_tip" = "Schreibt // Attribute, // Referenzattribute, // Konstruktor und // Methoden in die Klasse und sortiert alles darunter. Neues aus dem Klassendiagramm kommt dann gleich an die richtige Stelle."; -"web_visuobject_insp_doc" = "Beschreibung (Javadoc)"; -"web_visuobject_insp_doc_ph" = "Wofür ist die Klasse da? Steht im Code als Javadoc-Kommentar über der Klasse."; -"web_visuobject_insp_visibility" = "Sichtbarkeit"; -"web_visuobject_insp_type" = "Typ"; -"web_visuobject_insp_init" = "Startwert"; -"web_visuobject_insp_no_fields" = "Noch keine Attribute."; -"web_visuobject_insp_add_field" = "Attribut"; -"web_visuobject_insp_params" = "Parameter"; -"web_visuobject_insp_return" = "Rückgabetyp"; -"web_visuobject_insp_constructor" = "Konstruktor"; -"web_visuobject_insp_no_methods" = "Noch keine Methoden."; -"web_visuobject_insp_add_method" = "Methode"; -"web_visuobject_insp_generate" = "Erzeugen:"; -"web_visuobject_insp_gen_constructor" = "Konstruktor aus Attributen"; -"web_visuobject_insp_gen_constructor_tip" = "Wie in Eclipse „Generate Constructor using Fields“: ein Konstruktor, der alle Attribute (ohne static) setzt."; -"web_visuobject_insp_gen_accessors" = "Getter und Setter"; -"web_visuobject_insp_gen_accessors_tip" = "Wie in Eclipse „Generate Getters and Setters“: für jedes Attribut get…/set… (final-Attribute nur Getter)."; -"web_visuobject_insp_bad_params" = "Parameter so schreiben: name: Typ, zweiter: int (gelesen wurde „%@“)."; -"web_visuobject_insp_fields" = "Attribute"; -"web_visuobject_insp_methods" = "Methoden"; -"web_visuobject_inspector_resize" = "Größe ändern: Grenze zwischen Klassendiagramm und Objektinspektor nach oben oder unten ziehen (Doppelklick = Standard)"; -"web_visuobject_dep_title" = "Abhängigkeit «%@»"; -"web_visuobject_dep_create_text" = "%1$@ erzeugt in %3$@ ein Objekt der Klasse %2$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ benutzt die Klasse %2$@ in %3$@."; -"web_visuobject_dep_hint" = "Das ist keine Assoziation: %@ merkt sich das %@-Objekt nicht in einem Attribut, es lebt nur in der Methode. Darum gestrichelt."; -"web_visuobject_dep_to_assoc" = "Als Attribut merken (Assoziation zu %@)"; -"web_visuobject_end_mult_ph" = "Mult."; -"web_visuobject_end_tip" = "Klick (Linie markiert) oder Rechtsklick: Rolle, Multiplizität und Navigierbarkeit an diesem Ende"; -"web_visuobject_end_role_ph" = "z. B. meineKatzen"; -"web_visuobject_new_menu" = "Neu"; -"web_visuobject_new_menu_tip" = "Neues Projekt, Source Folder, Package, Klasse oder Interface anlegen"; -"web_visuobject_new_project" = "Java-Projekt"; -"web_visuobject_new_folder" = "Source Folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Klasse"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "Neues Java-Projekt"; -"web_visuobject_new_project_hint" = "Im Workspace bekommt jedes Projekt einen eigenen Ordner mit src."; -"web_visuobject_new_folder_title" = "Neuer Source Folder"; -"web_visuobject_new_folder_hint" = "Ein weiterer Ordner für Quellcode neben src, z. B. test für Testklassen."; -"web_visuobject_new_package_title" = "Neues Package"; -"web_visuobject_new_package_hint" = "Package-Namen schreibt man klein, Teile mit Punkt: tiere oder schule.verwaltung."; -"web_visuobject_new_class_title" = "Neue Java-Klasse"; -"web_visuobject_new_class_hint" = "Klassennamen beginnen mit einem Großbuchstaben, z. B. Hund oder Bankkonto."; -"web_visuobject_new_interface_title" = "Neues Java-Interface"; -"web_visuobject_new_interface_hint" = "Ein Interface legt Methoden fest, die Klassen später implementieren."; -"web_visuobject_new_root" = "Source Folder"; -"web_visuobject_new_name" = "Name"; -"web_visuobject_new_modifiers" = "Modifier"; -"web_visuobject_new_package_private" = "package (ohne Modifier)"; -"web_visuobject_new_superclass" = "Oberklasse"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Erweitert Interfaces (extends)"; -"web_visuobject_new_stubs" = "Welche Methoden-Rümpfe sollen entstehen?"; -"web_visuobject_new_stub_constructors" = "Konstruktoren der Oberklasse"; -"web_visuobject_new_stub_inherited" = "Geerbte abstrakte Methoden"; -"web_visuobject_new_comment_legend" = "Kommentar"; -"web_visuobject_new_comment" = "Javadoc-Kommentar mit @author"; -"web_visuobject_new_author" = "Dein Name"; -"web_visuobject_new_finish" = "Fertigstellen"; -"web_visuobject_new_name_missing" = "Bitte gib einen Namen ein."; -"web_visuobject_new_exists" = "„%@“ gibt es im Projekt schon."; -"web_visuobject_new_upper" = "Klassennamen beginnen in Java mit einem Großbuchstaben."; -"web_visuobject_bad_folder" = "„%@“ ist kein gültiger Ordnername oder gibt es schon."; -"web_visuobject_inspector_hide" = "Objektinspektor einklappen"; -"web_visuobject_inspector_show" = "Objektinspektor aufklappen"; -"web_visuobject_inspector_empty" = "Klicke im Diagramm auf eine Beziehung (Linie), dann kannst du sie hier bearbeiten."; -"web_visuobject_rel_end_a" = "Ende A: %@ (Linie beginnt hier)"; -"web_visuobject_rel_end_b" = "Ende B: %@ (Linie endet hier)"; -"web_visuobject_ws_title" = "Workspace-Ordner auf deinem Rechner"; -"web_visuobject_ws_question" = "Wo speicherst du deine Projekte?"; -"web_visuobject_ws_hint" = "Wie in Eclipse: ein Workspace-Ordner auf deinem Rechner, darin ein Ordner je Projekt mit src und den .java-Dateien."; -"web_visuobject_ws_create" = "Ordner „%@“ anlegen"; -"web_visuobject_ws_open" = "Vorhandenen Workspace öffnen"; -"web_visuobject_ws_skip" = "Ohne Ordner (nur im Browser)"; -"web_visuobject_ws_choose" = "Workspace wählen"; -"web_visuobject_ws_reopen" = "Workspace „%@“ öffnen"; -"web_visuobject_ws_switch" = "Wechseln"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Projekt als .zip herunterladen, z. B. zum Abgeben"; -"web_visuobject_ws_saved" = "Gespeichert in %@/%@"; -"web_visuobject_ws_created" = "Neues Projekt „%@“ im Workspace"; -"web_visuobject_ws_failed" = "Speichern im Workspace hat nicht geklappt: %@"; -"web_visuobject_ws_taken" = "Im Workspace gibt es schon ein Projekt „%@“."; -"web_visuobject_ws_open_project" = "Projekt %@ öffnen"; -"web_visuobject_save_ws_tip" = "Im Workspace speichern (Strg+S)"; -"web_visuobject_unsaved" = "nicht gespeichert"; -"web_visuobject_rel_nav_navigable" = "navigierbar (Pfeil)"; -"web_visuobject_rel_nav_non-navigable" = "nicht navigierbar (Kreuz ×)"; -"web_visuobject_rel_nav_unspecified" = "nicht spezifiziert (offen)"; -"web_visuobject_rel_reading_flip" = "umdrehen"; -"web_visuobject_rel_reading_auto" = "Mit einem Namen liest sich die Beziehung in Zugrichtung."; -"web_visuobject_tools" = "Werkzeuge"; -"web_visuobject_tool_select" = "Auswahl"; -"web_visuobject_tool_select_tip" = "Auswählen und verschieben"; -"web_visuobject_tool_class" = "Klasse"; -"web_visuobject_tool_class_tip" = "Klasse: auf die Fläche ziehen oder wählen und auf die Fläche klicken"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: auf die Fläche ziehen oder wählen und auf die Fläche klicken"; -"web_visuobject_tool_link" = "Assoziation"; -"web_visuobject_tool_link_tip" = "Assoziation ohne Navigation: einfache Linie zwischen zwei Klassen ziehen"; -"web_visuobject_rel_link" = "Assoziation (ohne Navigation)"; -"web_visuobject_tool_not_link" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_association" = "Gerichtet"; -"web_visuobject_tool_association_tip" = "Gerichtete Assoziation: von der Klasse, die die andere kennt, zur anderen ziehen (Pfeil)"; -"web_visuobject_tool_aggregation" = "Aggregation"; -"web_visuobject_tool_aggregation_tip" = "Aggregation: vom Ganzen zum Teil ziehen (hat, Teile leben weiter)"; -"web_visuobject_tool_composition" = "Komposition"; -"web_visuobject_tool_composition_tip" = "Komposition: vom Ganzen zum Teil ziehen (besteht aus, Teile gehören fest dazu)"; -"web_visuobject_tool_extends" = "Vererbung"; -"web_visuobject_tool_extends_tip" = "Vererbung: von der Unterklasse zur Oberklasse ziehen (erbt von)"; -"web_visuobject_tool_implements" = "Realisierung"; -"web_visuobject_tool_implements_tip" = "Realisierung: von der Klasse zum Interface ziehen (implementiert)"; -"web_visuobject_tool_pick_second" = "Jetzt die zweite Klasse anklicken (von %@ aus). Esc bricht ab."; -"web_visuobject_tool_not_association" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_aggregation" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_composition" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_extends" = "%@ kann nicht von %@ erben: Klassen erben von Klassen, Interfaces von Interfaces. Für ein Interface nimm Realisierung."; -"web_visuobject_tool_not_implements" = "%@ kann %@ nicht implementieren: Nur eine Klasse implementiert ein Interface."; -"web_visuobject_inspector" = "Objektinspektor"; -"web_visuobject_connect" = "Ziehen: Beziehung zu einer anderen Klasse"; -"web_visuobject_rel_association" = "Assoziation"; -"web_visuobject_rel_directed" = "Gerichtete Assoziation"; -"web_visuobject_rel_aggregation" = "Aggregation (hat, Teile leben weiter)"; -"web_visuobject_rel_composition" = "Komposition (besteht aus, Teile gehören fest dazu)"; -"web_visuobject_rel_extends" = "Vererbung (erbt von)"; -"web_visuobject_rel_implements" = "Realisierung (implementiert)"; -"web_visuobject_rel_extends_text" = "%@ erbt von %@ (extends im Code)."; -"web_visuobject_rel_implements_text" = "%@ implementiert das Interface %@ (implements im Code)."; -"web_visuobject_rel_kind" = "Art"; -"web_visuobject_rel_name" = "Name der Beziehung"; -"web_visuobject_rel_name_ph" = "z. B. besitzt"; -"web_visuobject_rel_reading" = "Leserichtung"; -"web_visuobject_rel_navigable" = "Navigierbarkeit"; -"web_visuobject_rel_role" = "Rolle"; -"web_visuobject_rel_multiplicity" = "Multiplizität"; -"web_visuobject_rel_role_field" = "= Attribut %@.%@ im Code"; -"web_visuobject_rel_role_only_uml" = "nur im Diagramm (kein Attribut, solange der Pfeil nur in eine Richtung zeigt)"; -"web_visuobject_rel_delete" = "Beziehung löschen"; -"web_visuobject_rel_impossible" = "Von %@ zu %@ gibt es keine passende Beziehung."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Breite ziehen (Doppelklick: Standard)"; -"web_visuobject_pane_show" = "Bereich aufklappen"; -"web_visuobject_pane_hide" = "Bereich einklappen"; -"web_visuobject_close_tab" = "%@ schließen"; -"web_visuobject_collapse" = "Einklappen"; -"web_visuobject_expand" = "Aufklappen"; -"web_visuobject_tree_empty" = "Noch leer. Lege mit „+ Neu“ ein Package oder eine Klasse an."; -"web_visuobject_delete" = "Löschen"; -"web_visuobject_default_package" = "(Standard-Package)"; -"web_visuobject_bad_package" = "„%@“ ist kein gültiger Package-Name oder gibt es schon. Package-Namen schreibt man klein, z. B. tiere."; -"web_visuobject_new_package_name" = "neuespackage"; -"web_visuobject_no_file" = "Keine Datei offen"; -"web_visuobject_project_name" = "MeinProjekt"; -"web_visuobject_explorer_show" = "Package Explorer zeigen"; -"web_visuobject_explorer_hide" = "Package Explorer einklappen"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Projekt als .zip speichern: Projektname/src/package/Klasse.java"; -"web_visuobject_open_tip" = "Projekt öffnen (.zip wie aus Eclipse oder BlueJ) oder .java-Dateien ins Projekt holen"; -"web_visuobject_method_ph" = "+ bellen(): void"; -"web_code_scopes" = "Blöcke farbig"; -"web_code_scopes_hint" = "Code und Struktogramm: Schleife rosa, Verzweigung blau, Methode gelb, Klasse grün"; -"web_pseudocode" = "Pseudocode"; - -/* VisuFlow (PAP zeichnen) */ -"web_visuflow_subtitle" = "PAP zeichnen"; -"web_visuflow_world_hint" = "VisuFlow: einen Programmablaufplan (PAP, englisch flowchart) frei zeichnen und als Bild oder PDF exportieren. Wechseln geht jederzeit im Zahnrad."; -"web_visuflow_palette" = "Symbole"; -"web_visuflow_palette_hint" = "Antippen hängt das Symbol unter das gewählte an und verbindet es mit einem Pfeil. Du kannst es auch auf die Fläche ziehen."; -"web_visuflow_shape_start" = "Start"; -"web_visuflow_shape_ende" = "Ende"; -"web_visuflow_shape_anweisung" = "Anweisung"; -"web_visuflow_shape_ein" = "Ein-/Ausgabe"; -"web_visuflow_shape_verzweigung" = "Verzweigung"; -"web_visuflow_shape_unterprogramm" = "Unterprogramm"; -"web_visuflow_shape_start_tip" = "Oval: Hier beginnt der Ablauf"; -"web_visuflow_shape_ende_tip" = "Oval: Hier endet der Ablauf"; -"web_visuflow_shape_anweisung_tip" = "Rechteck: eine Anweisung, zum Beispiel „Wasser kochen“"; -"web_visuflow_shape_ein_tip" = "Parallelogramm: Eingabe oder Ausgabe, zum Beispiel „Eingabe: Zahl“"; -"web_visuflow_shape_verzweigung_tip" = "Raute: eine Frage mit den Ausgängen ja und nein"; -"web_visuflow_shape_unterprogramm_tip" = "Rechteck mit zwei Strichen: ruft ein Unterprogramm auf"; -"web_visuflow_text_start" = "Start"; -"web_visuflow_text_ende" = "Ende"; -"web_visuflow_text_anweisung" = "Anweisung"; -"web_visuflow_text_ein" = "Eingabe: Zahl"; -"web_visuflow_text_verzweigung" = "Bedingung?"; -"web_visuflow_text_unterprogramm" = "Unterprogramm"; -"web_visuflow_new_tip" = "Neu: leerer PAP mit Start (Rückgängig holt den alten zurück)"; -"web_visuflow_open_tip" = "PAP-Datei (.visuflow) öffnen"; -"web_visuflow_save_tip" = "PAP als Datei (.visuflow) speichern"; -"web_visuflow_delete" = "Löschen"; -"web_visuflow_delete_tip" = "Gewähltes Symbol oder gewählten Pfeil löschen (Entf)"; -"web_visuflow_swap" = "ja/nein tauschen"; -"web_visuflow_swap_tip" = "Raute: „ja“ und „nein“ tauschen die Seiten"; -"web_visuflow_example" = "Beispiel"; -"web_visuflow_example_tip" = "Beispiel „Tee kochen“ aus Lektion 1 laden (Rückgängig holt den alten PAP zurück)"; -"web_visuflow_example_title" = "Tee kochen"; -"web_visuflow_example_water" = "Wasser kochen"; -"web_visuflow_example_bag" = "Teebeutel in die Tasse"; -"web_visuflow_example_hot" = "Tee noch zu heiß?"; -"web_visuflow_example_wait" = "Warten"; -"web_visuflow_example_drink" = "Tee trinken"; -"web_visuflow_title_label" = "Titel des PAP"; -"web_visuflow_title_ph" = "Titel des PAP"; -"web_visuflow_canvas_label" = "Zeichenfläche des PAP"; -"web_visuflow_open_error" = "Die Datei ist kein VisuFlow-PAP."; -"web_visuflow_saved" = "Gespeichert als %@"; -"web_visuflow_status_empty" = "Zieh ein Symbol aus der Leiste auf die Fläche oder tippe es an."; -"web_visuflow_status_idle" = "Symbol antippen wählt es aus. Vom Punkt am Ausgang ziehst du einen Pfeil zum nächsten Symbol."; -"web_visuflow_status_node" = "Ziehen verschiebt das Symbol, nochmal antippen ändert den Text. Vom Punkt am Ausgang einen Pfeil ziehen."; -"web_visuflow_status_edge" = "Pfeil gewählt. Löschen entfernt ihn."; -"web_visuflow_status_connect" = "Loslassen über dem Symbol, zu dem der Pfeil führt."; -"web_visuflow_edit_label" = "Text im Symbol"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Variable umbenennen …"; -"web_rename_title" = "Variable umbenennen"; -"web_rename_old" = "Name"; -"web_rename_new" = "Neuer Name"; -"web_rename_hint" = "Ändert jede Stelle mit genau dieser Schreibweise (zahl ist nicht Zahl), im Code und im Struktogramm. Text in \"…\" und Kommentare bleiben."; -"web_rename_count_fmt" = "Kommt %d-mal vor."; -"web_rename_exists_fmt" = "„%@“ gibt es schon: Danach wären beide eine Variable."; -"web_rename_invalid" = "Ein Name beginnt mit einem Buchstaben und enthält nur Buchstaben, Ziffern und _."; -"web_rename_keyword_fmt" = "„%@“ ist ein Java-Schlüsselwort."; -"web_rename_apply" = "Umbenennen"; -"web_rename_cancel" = "Abbrechen"; -"web_visubot_world_visucode_hint" = "Andere Programme wie %1$@ oder %2$@ findest du auf visucode.de."; diff --git a/web/src/i18n/el.strings b/web/src/i18n/el.strings deleted file mode 100644 index 2ef745bf..00000000 --- a/web/src/i18n/el.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Κείμενα που υπάρχουν μόνο στην έκδοση web. Όλα τα άλλα προέρχονται από το Localizable.strings της εφαρμογής. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Διάγραμμα VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Εξαγωγή…"; -"web_export_title" = "Αποθήκευση ως εικόνα ή PDF ή εκτύπωση"; -"web_export_png" = "Εικόνα (PNG)"; -"web_export_svg" = "Διανυσματικό γραφικό (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Αρχείο VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Η εξαγωγή απέτυχε: %@"; -"web_print_hint" = "Με «Εξαγωγή… → PDF» μπορείτε να αποθηκεύσετε το διάγραμμα και να το εκτυπώσετε από εκεί."; -"web_delete" = "Διαγραφή"; -"web_language_title" = "Γλώσσα περιβάλλοντος"; -"web_simulation_start" = "Έναρξη προσομοίωσης"; -"web_simulation_end" = "Τέλος προσομοίωσης"; -"web_simulation_start_title" = "Εκτέλεση του διαγράμματος βήμα προς βήμα"; -"web_simulation_end_title" = "Τέλος προσομοίωσης και συνέχεια επεξεργασίας"; -"web_palette_hint" = "Σύρετε στο διάγραμμα"; -"web_tabs_label" = "Διαγράμματα"; -"web_status_modified" = "τροποποιήθηκε"; -"web_inspector_title" = "Ιδιότητες"; -"web_inspector_resize" = "Αλλαγή ύψους του επιθεωρητή αντικειμένων: σύρετε τη γραμμή πάνω ή κάτω (διπλό κλικ: προεπιλογή)"; -"web_inspector_help_drop" = "Σύρετε στοιχεία από την παλέτα σε ένα πλακίδιο: πάνω = πριν, κάτω = μετά, πάνω στο «ø» = αντικατάσταση. Τα υπάρχοντα στοιχεία μετακινούνται με το ποντίκι."; -"web_inspector_help_inline" = "Διπλό κλικ ή διπλό πάτημα σε ένα στοιχείο για επεξεργασία του κειμένου απευθείας στο διάγραμμα (Enter εφαρμογή, Esc ακύρωση). Το ίδιο και για τα ονόματα περιπτώσεων."; -"web_inspector_help_files" = "Μπορείτε επίσης να σύρετε αρχεία στο παράθυρο."; -"web_case_name" = "Όνομα περίπτωσης"; -"web_case_name_placeholder" = "π.χ. 1, ok, default"; -"web_copied" = "Το στοιχείο αντιγράφηκε."; -"web_discard_message_fmt" = "Οι αλλαγές στο «%@» θα χαθούν."; -"web_err_unreadable_fmt" = "Το «%@» δεν είναι αναγνώσιμο αρχείο VisuStruct (%@)."; -"web_saved_fmt" = "Το «%@» αποθηκεύτηκε."; -"web_saved_txt_fmt" = "Αποθηκεύτηκε ως «%@». Αφαιρέστε την κατάληξη .txt για να το χρησιμοποιήσετε."; -"web_drop_impossible" = "Δεν είναι δυνατή η απόθεση εδώ"; -"web_sim_delay" = "Παύση ανά βήμα"; -"web_sim_delay_value_fmt" = "%@ δευτ."; -"web_codegen_language" = "Γλώσσα"; -"web_codegen_comments" = "Κείμενο διαγράμματος ως σχόλια"; -"web_codegen_empty_else" = "Εμφάνιση κενού κλάδου else στον κώδικα"; -"web_copied_check" = "Αντιγράφηκε ✓"; -"web_copy_manual" = "Επιλέχθηκε, πατήστε Ctrl+C για αντιγραφή"; -"web_run_stopped_loop" = "Η εκτέλεση διακόπηκε: πιθανός ατέρμονος βρόχος."; -"web_run_cancelled" = "Η εκτέλεση ακυρώθηκε."; -"web_run_error" = "Σφάλμα κατά την εκτέλεση."; -"web_run_dropped_fmt" = "… %d προηγούμενες γραμμές κρυμμένες"; -"web_sim_call_arguments_fmt" = "Η κλήση δεν ταιριάζει με την κεφαλίδα του %@. Έλεγξε τις τιμές μέσα στις παρενθέσεις."; -"web_sim_missing_return_fmt" = "Το %@ τελείωσε χωρίς να επιστρέψει τιμή με return."; -"web_sim_file_not_found_fmt" = "Δεν υπάρχει αρχείο με όνομα %@."; -"web_sim_end_of_file_fmt" = "Το αρχείο %@ δεν έχει άλλες γραμμές."; -"web_sim_null_value_fmt" = "Το %@ είναι null."; -"web_sim_unknown_variable_case_fmt" = "Άγνωστη μεταβλητή: %1$@. Η Java ξεχωρίζει κεφαλαία και πεζά: μήπως εννοούσες %2$@;"; -"web_sim_written_file_fmt" = "%@ (αποθηκεύτηκε)"; -"web_site_home" = "Αρχική σελίδα"; -"web_site_imprint" = "Στοιχεία εκδότη"; -"web_site_privacy" = "Απόρρητο"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Προγραμματισμός"; -"web_live_hide" = "Απόκρυψη προγραμματισμού"; -"web_scale_canvas" = "Μέγεθος κειμένου: διάγραμμα"; -"web_scale_code" = "Μέγεθος κειμένου: προγραμματισμός"; -"web_live_indent" = "Εσοχή"; -"web_live_indent_title" = "Εσοχή κώδικα: τέσσερα κενά ανά επίπεδο (επιλεγμένες γραμμές, αλλιώς όλα)"; -"web_live_show" = "Εμφάνιση προγραμματισμού"; -"web_live_resize" = "Αλλαγή πλάτους περιοχής κώδικα (διπλό κλικ: επαναφορά)"; -"web_live_hint" = "Ο κώδικας μεγαλώνει μαζί με το διάγραμμα. Γκρι = σύμβολο κράτησης: γράψε στον επιθεωρητή και το κείμενό σου εμφανίζεται εδώ. Κάνε κλικ σε μια γραμμή για να επιλέξεις το στοιχείο."; -"web_live_ph_selector" = "μεταβλητή"; -"web_live_ph_code" = "Ο κώδικάς σου"; -"web_live_ph_end" = "τέλος"; -"web_live_language" = "Γλώσσα προγραμματισμού"; -"web_live_java_class" = "Ως κλάση"; -"web_live_java_class_hint" = "Όνομα κλάσης = τίτλος διαγράμματος"; -"web_live_java_main" = "Με μέθοδο main"; -"web_settings_display" = "Εμφάνιση"; -"web_live_swing" = "Πραγματική Java (με Swing)"; -"web_live_swing_hint" = "Πραγματική Java με παράθυρα (CheerpJ). Κατεβάζει μία φορά περίπου 40 MB στην πρώτη εκτέλεση."; -"web_live_swing_windows" = "Παράθυρα Java"; -"web_live_swing_hide" = "Κλείσιμο παραθύρων Java"; -"web_live_swing_loading" = "Φόρτωση Java (την πρώτη φορά περίπου 40 MB) …"; -"web_live_swing_compiling" = "Μεταγλώττιση …"; -"web_live_swing_running" = "Το πρόγραμμα εκτελείται."; -"web_live_swing_compile_failed" = "Σφάλμα μεταγλώττισης, δες παραπάνω."; -"web_live_swing_failed" = "Δεν ήταν δυνατή η φόρτωση της Java. Έλεγξε τη σύνδεση στο διαδίκτυο."; -"web_live_hint_edit" = "Γράψε εδώ κώδικα Java και το διάγραμμα φτιάχνεται μόνο του. Ή δούλεψε στο διάγραμμα και ο κώδικας μεγαλώνει μαζί. Γκρι = σύμβολο κράτησης θέσης, απλώς γράψε από πάνω."; -"web_live_edit_label" = "Κώδικας Java"; -"web_parse_line_fmt" = "Γραμμή %d: %@"; -"web_parse_missing" = "Λείπει ακόμα εδώ: %@"; -"web_parse_extra" = "Περισσεύει: %@"; -"web_parse_unclosed_string" = "Λείπει το %@ που κλείνει."; -"web_parse_else_without_if" = "Αυτό το «else» δεν έχει «if»."; -"web_parse_case_outside_switch" = "Τα «case» και «default» ανήκουν μέσα σε «switch»."; -"web_parse_missing_do_while" = "Μετά το «do { … }» λείπει το «while (συνθήκη);»."; -"web_parse_more_methods" = "Προς το παρόν μετράει μόνο μία μέθοδος. Δημιούργησε τα άλλα υποπρογράμματα σε ξεχωριστές καρτέλες."; -"web_parse_missing_import" = "Λείπει στην αρχή το %@ Χωρίς αυτή τη γραμμή η Java δεν γνωρίζει το Scanner."; -"web_parse_missing_scanner" = "Λείπει πρώτα %@ Χωρίς αυτή τη γραμμή ο Scanner δεν υπάρχει."; -"web_parse_unknown_type" = "Η Java δεν γνωρίζει αυτόν τον τύπο δεδομένων. Μήπως εννοείς %@;"; -"web_quickfix_insert_fmt" = "Πρόταση: εισαγωγή %@"; -"web_quickfix_replace_fmt" = "Πρόταση: %1$@ αντί για %2$@"; -"web_parse_wrong_case" = "Η Java ξεχωρίζει κεφαλαία και πεζά. Το σωστό είναι %@."; -"web_editor_title" = "Διάγραμμα"; -"web_editor_show" = "Εμφάνιση διαγράμματος"; -"web_editor_hide" = "Απόκρυψη διαγράμματος"; -"web_keywords_title" = "Λέξεις-κλειδιά κώδικα"; -"web_keywords_hint" = "if, while, for αντί για ονόματα στοιχείων"; -"web_palette_compact" = "Στοιχεία πάντα συμπαγή"; -"web_palette_compact_hint" = "Σύμβολο με μικρό όνομα από κάτω, αλλιώς μόνο όταν η στήλη είναι στενή"; -"web_about_version_fmt" = "Έκδοση %@"; -"web_about_software" = "Λογισμικό που χρησιμοποιείται"; -"web_about_software_text" = "Ο επεξεργαστής κώδικα χρησιμοποιεί τα CodeMirror και Lezer (άδεια MIT). Με την υποστήριξη Swing, το VisuStruct φορτώνει το CheerpJ της Leaning Technologies (άδεια Community) και το javac από το OpenJDK."; -"web_about_licenses" = "Κείμενα αδειών"; -"web_live_run" = "Εκτέλεση"; -"web_live_run_title" = "Εκτέλεση προγράμματος: η έξοδος και η είσοδος εμφανίζονται παρακάτω"; -"web_live_run_finished" = "Το πρόγραμμα τελείωσε."; -"web_live_run_stopped" = "Διακόπηκε."; -"web_console" = "Κονσόλα"; -"web_console_hide" = "Απόκρυψη κονσόλας"; -"web_console_resize" = "Αλλαγή ύψους κονσόλας"; -"web_diagram_tools" = "Εργαλεία διαγράμματος"; -"web_files_title" = "Αρχεία"; -"web_files_show" = "Εμφάνιση αρχείων"; -"web_files_hide" = "Απόκρυψη αρχείων"; -"web_files_resize" = "Αλλαγή πλάτους αρχείων"; -"web_files_new_folder" = "Νέος φάκελος"; -"web_files_switch" = "Επιλογή άλλου φακέλου"; -"web_files_delete" = "Διαγραφή"; -"web_files_in_browser" = "Στον χώρο αυτού του προγράμματος περιήγησης"; -"web_files_save_hint_fmt" = "Η αποθήκευση βάζει το πρόγραμμα στο %@."; -"web_files_empty" = "Δεν έχει αποθηκευτεί τίποτα ακόμη. Κατά την αποθήκευση το VisuStruct δημιουργεί εδώ έναν φάκελο με τη σημερινή ημερομηνία."; -"web_files_reconnect_hint" = "Μετά την επαναφόρτωση, το πρόγραμμα περιήγησης ρωτά ξανά αν το VisuStruct μπορεί να χρησιμοποιήσει τον φάκελό σου."; -"web_files_reconnect_fmt" = "Άνοιγμα φακέλου «%@»"; -"web_files_connect_hint" = "Επίλεξε έναν φάκελο ως χώρο εργασίας. Το VisuStruct δημιουργεί εκεί έναν φάκελο για κάθε ημέρα και αποθηκεύει διάγραμμα και κώδικα."; -"web_files_connect" = "Επιλογή φακέλου"; -"web_files_browser_hint" = "Αυτό το πρόγραμμα περιήγησης δεν μπορεί να ανοίξει φακέλους της συσκευής (π.χ. στο iPad). Τα αρχεία σου μένουν στον χώρο του."; -"web_files_browser" = "Αποθήκευση στο πρόγραμμα περιήγησης"; -"web_files_delete_confirm_fmt" = "Να διαγραφεί οριστικά το «%@»;"; -"web_files_folder_prompt" = "Όνομα νέου φακέλου:"; -"web_files_default_name" = "Πρόγραμμα"; -"web_files_saved_fmt" = "Αποθηκεύτηκε: %@"; -"web_files_create" = "Δημιουργία"; -"web_learn_ludwig_tip" = "Η συμβουλή του Ludwig"; -"web_game_title" = "Παιχνίδι: Αληθές ή ψευδές;"; -"web_game_intro" = "Ο Ludwig σου δείχνει τιμές και μια συνθήκη. Είναι αληθής (true) ή ψευδής (false); Δέκα γύροι, όλο και δυσκολότεροι."; -"web_game_start" = "Έναρξη παιχνιδιού"; -"web_game_round_fmt" = "Γύρος %@ από %@"; -"web_game_score_fmt" = "Πόντοι: %@"; -"web_game_true" = "αληθές (true)"; -"web_game_false" = "ψευδές (false)"; -"web_game_right" = "Σωστά!"; -"web_game_wrong" = "Όχι ακριβώς. Έτσι υπολογίζεται:"; -"web_game_next" = "Επόμενο"; -"web_game_finish" = "Αποτέλεσμα"; -"web_game_done_fmt" = "%@ από %@ σωστά."; -"web_game_perfect" = "Όλα σωστά – ο Ludwig εντυπωσιάστηκε!"; -"web_game_good" = "Μπράβο! Δες ξανά τα λάθη σου και δοκίμασε πάλι."; -"web_game_practice" = "Η εξάσκηση φέρνει την τελειότητα. Διάβασε ξανά τον πίνακα αλήθειας και παίξε άλλον έναν γύρο."; -"web_game_again" = "Παίξε ξανά"; -"web_game_wrong_short" = "Δυστυχώς λάθος."; -"web_game_practice_short" = "Η εξάσκηση φέρνει την τελειότητα. Διάβασε ξανά τις εξηγήσεις και παίξε άλλον έναν γύρο."; -"web_game_clicks" = "Κλικ:"; -"web_game_nobody" = "Κανείς δεν ακούει – δεν συμβαίνει τίποτα."; -"web_game_event_fmt" = "Κλικ στο «%@»: ο ακροατής το ακούει και εκτελεί τον κώδικά του."; -"web_game_try" = "Δοκίμασε τώρα μόνος σου τα κουμπιά στο παράθυρο."; -"web_game_time_fmt" = "Απομένουν %@ δ"; -"web_game_defused" = "✂️ Εξουδετερώθηκε! Βρήκες το λάθος."; -"web_game_boom_time" = "💥 Μπαμ! Ο χρόνος τελείωσε."; -"web_game_boom_wrong" = "💥 Μπαμ! Ήταν λάθος γραμμή."; -"web_game_boom_part" = "💥 Μπαμ! Σωστή γραμμή, αλλά το λάθος είναι σε άλλο κομμάτι της."; -"web_game_boom_wrong_part" = "💥 Μπαμ! Το λάθος είναι σε άλλη γραμμή."; -"web_game_bomb_parts_hint" = "Πάτησε ακριβώς το λάθος κομμάτι."; -"web_game_fix" = "Η σωστή γραμμή:"; -"web_game_order_hint" = "Πάτησε τα βήματα με τη σωστή σειρά."; -"web_debug_step" = "Βήμα"; -"web_debug_step_title" = "Βήμα: εκτέλεση μίας εντολής και νέα παύση"; -"web_debug_continue" = "Συνέχεια έως το επόμενο σημείο διακοπής"; -"web_debug_paused_fmt" = "Παύση πριν από τη γραμμή %d."; -"web_debug_gutter_title" = "Κάνε κλικ σε αριθμό γραμμής για να βάλεις ή να βγάλεις σημείο διακοπής"; -"web_swap_to_code" = "Μεγάλος προγραμματισμός"; -"web_swap_to_editor" = "Μεγάλο διάγραμμα"; -"web_editor_resize" = "Αλλαγή πλάτους διαγράμματος"; -"web_palette_resize" = "Αλλαγή πλάτους στοιχείων"; -"web_palette_hide" = "Απόκρυψη στοιχείων"; -"web_palette_show" = "Εμφάνιση στοιχείων"; -"web_code_scopes" = "Χρωματιστά μπλοκ κώδικα"; -"web_code_scopes_hint" = "Κλάση, μέθοδος, βρόχος, διακλάδωση σε δικά τους πλαίσια"; -"web_pseudocode" = "Ψευδοκώδικας"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Μετονομασία μεταβλητής …"; -"web_rename_title" = "Μετονομασία μεταβλητής"; -"web_rename_old" = "Όνομα"; -"web_rename_new" = "Νέο όνομα"; -"web_rename_hint" = "Αλλάζει κάθε σημείο με ακριβώς αυτή τη γραφή (number δεν είναι Number), στον κώδικα και στο δομόγραμμα. Το κείμενο σε \"…\" και τα σχόλια μένουν."; -"web_rename_count_fmt" = "Εμφανίζεται %d φορές."; -"web_rename_exists_fmt" = "Το «%@» υπάρχει ήδη: μετά θα ήταν και τα δύο μία μεταβλητή."; -"web_rename_invalid" = "Ένα όνομα αρχίζει με γράμμα και περιέχει μόνο γράμματα, ψηφία και _."; -"web_rename_keyword_fmt" = "Το «%@» είναι λέξη-κλειδί της Java."; -"web_rename_apply" = "Μετονομασία"; -"web_rename_cancel" = "Άκυρο"; diff --git a/web/src/i18n/en.strings b/web/src/i18n/en.strings deleted file mode 100644 index 7670ebc2..00000000 --- a/web/src/i18n/en.strings +++ /dev/null @@ -1,800 +0,0 @@ -/* Texts that exist only in the web version. Everything else comes from the app's Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct structure chart"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Export…"; -"web_export_title" = "Save as image or PDF, or print"; -"web_export_png" = "Image (PNG)"; -"web_export_svg" = "Vector graphic (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct file (.visustruct)"; -"web_export_failed_fmt" = "Export failed: %@"; -"web_print_hint" = "Use “Export… → PDF” to save the diagram and print it from there."; -"web_delete" = "Delete"; -"web_language_title" = "Interface language"; -"web_simulation_start" = "Start simulation"; -"web_simulation_end" = "End simulation"; -"web_simulation_start_title" = "Run the structure chart step by step"; -"web_simulation_end_title" = "End the simulation and continue editing"; -"web_palette_hint" = "Drag into the structure chart"; -"web_tabs_label" = "Diagrams"; -"web_status_modified" = "modified"; -"web_inspector_title" = "Object Inspector"; -"web_inspector_help_drop" = "Drag elements from the “Elements” area onto a tile: top = before, bottom = after, onto “ø” = replace. Existing elements can be moved with the mouse."; -"web_inspector_help_inline" = "Double-click or double-tap an element to edit its text right in the structure chart (Enter applies, Esc cancels). Case labels work the same way."; -"web_inspector_help_files" = "You can also drop files into the window."; -"web_case_name" = "Case name"; -"web_case_name_placeholder" = "e.g. 1, ok, default"; -"web_copied" = "Element copied."; -"web_discard_message_fmt" = "The changes to “%@” will be lost."; -"web_err_unreadable_fmt" = "“%@” is not a readable VisuStruct file (%@)."; -"web_saved_fmt" = "“%@” saved."; -"web_saved_txt_fmt" = "Saved as “%@”. Remove the .txt extension to use it."; -"web_drop_impossible" = "Cannot drop here"; -"web_sim_delay" = "Pause per step"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Language"; -"web_codegen_comments" = "Diagram text as comments"; -"web_codegen_empty_else" = "Emit empty else branch in code"; -"web_copied_check" = "Copied ✓"; -"web_copy_manual" = "Selected, press Ctrl+C to copy"; -"web_run_stopped_loop" = "Execution stopped: possible infinite loop."; -"web_run_cancelled" = "Execution cancelled."; -"web_run_error" = "Error while running."; -"web_run_dropped_fmt" = "… %d earlier lines hidden"; - -/* Learning area (lessons are German only for now) */ -"web_learn_title" = "Learn"; -"web_learn_hide" = "Collapse the learning area"; -"web_learn_show" = "Expand the learning area: lessons, exercises and reference"; -"web_learn_lessons" = "Lessons"; -"web_learn_tasks" = "Exercises"; -"web_learn_reference" = "Reference"; -"web_learn_intro" = "Learn step by step, with exercises. Your progress is remembered in this browser."; -"web_learn_tasks_intro" = "Small projects without instructions. Each one names the lesson you should finish first."; -"web_learn_step_of_fmt" = "Step %d of %d"; -"web_learn_lesson_fmt" = "Lesson %d"; -"web_learn_all_lessons" = "All lessons"; -"web_learn_all_tasks" = "All exercises"; -"web_learn_back" = "Back"; -"web_learn_next" = "Next"; -"web_learn_finish" = "Finish lesson"; -"web_learn_done" = "Finished"; -"web_learn_after_fmt" = "After lesson %d: %@"; -"web_learn_level_fmt" = "Difficulty %d of 3"; -"web_learn_exercise" = "Exercise"; -"web_learn_brag" = "Fun facts with Ludwig"; -"web_learn_solved" = "solved"; -"web_learn_open_exercise" = "Open in editor"; -"web_learn_solve_in_editor" = "Solve this exercise in the %@ area."; -"web_learn_solve_in_code" = "Solve this exercise in the %@ area."; -"web_learn_solve_in_both" = "Solve this exercise in the %1$@ and %2$@ areas."; -"web_learn_open_in_editor" = "Open in editor"; -"web_learn_replay" = "Play again"; -"web_learn_graphic_labels" = "👆 Click the picture to show or hide the labels"; -"web_learn_check" = "Check"; -"web_learn_hint" = "Hint"; -"web_learn_reset" = "Start over"; -"web_learn_reset_confirm" = "Really start over?"; -"web_learn_solution" = "Show sample solution"; -"web_learn_hint_more" = "Another hint"; -"web_learn_hint_fmt" = "Hint %d of %d"; -"web_learn_teacher" = "For teachers"; -"web_learn_teacher_intro" = "The teacher code shows the sample solutions for all exercises. The browser remembers it."; -"web_learn_teacher_code" = "Teacher code"; -"web_learn_teacher_unlock" = "Unlock"; -"web_learn_teacher_wrong" = "The code is not correct."; -"web_learn_teacher_on" = "Teacher mode is on: sample solutions are visible for all exercises."; -"web_learn_teacher_off" = "Leave teacher mode"; -"web_learn_open_first" = "Open the exercise in the editor first and build your structure chart there."; -"web_learn_empty" = "Your structure chart is still empty. Drag elements from the “Elements” area into it first."; -"web_learn_passed" = "Well done! Your structure chart does exactly what it should."; -"web_learn_failed" = "Not quite yet. Compare the expected output with yours:"; -"web_learn_missing_fmt" = "Your structure chart still needs: %@"; -"web_learn_input_fmt" = "Input: %@"; -"web_learn_no_input" = "No input"; -"web_learn_expected" = "Expected"; -"web_learn_actual" = "Your output"; -"web_learn_missing_input" = "Your structure chart asks for more input than planned."; -"web_learn_unused_input" = "Your structure chart does not ask for all inputs."; -"web_learn_missing_line_break" = "Almost! Only a line break is missing. System.out.print keeps writing on the same line, System.out.println starts a new line afterwards."; -"web_learn_too_many_steps" = "Stopped after 100,000 steps. Is a loop running forever?"; -"web_learn_score_fmt" = "%d of %d correct"; -"web_learn_cloze_hint" = "Type the missing words into the gaps and press “Check”."; -"web_learn_cloze_score_fmt" = "%d of %d gaps correct"; -"web_learn_cloze_all_right" = "All gaps correct!"; -"web_learn_cloze_solution" = "Show solution"; -"web_learn_cloze_again" = "Try again"; -"web_learn_pap_hint" = "Tap an element on the right and then its place in the chart. You can also drag it there. Tapping a filled place gives its element back."; -"web_learn_pap_score_fmt" = "%d of %d elements correct"; -"web_learn_pap_all_right" = "All correct! Your flowchart is finished."; -"web_learn_pap_missing_fmt" = "%d elements are still missing."; -"web_learn_pap_empty" = "Empty place"; -"web_learn_pap_yes" = "yes"; -"web_learn_pap_no" = "no"; -"web_learn_pap_legend_oval" = "Start or end"; -"web_learn_pap_legend_rect" = "Statement"; -"web_learn_pap_legend_diamond" = "Decision: question with yes and no"; -"web_learn_pap_legend_para" = "Input or output"; -"web_learn_review" = "Review"; -"web_learn_terms" = "Technical terms"; -"web_inspector_resize" = "Change the height of the object inspector: drag or swipe the line up or down (double-click: default)"; -"web_learn_resize" = "Change the width of the learning area (double-click: reset)"; -"web_learn_competences_intro" = "At the end of this lesson I can …"; -"web_learn_rating_sun" = "I can do this confidently"; -"web_learn_rating_cloud" = "partly"; -"web_learn_rating_umbrella" = "not yet"; -"web_learn_review_title" = "Your checklist compared"; -"web_learn_review_hint" = "Here your self-assessment and the quiz do not match. Read the step again and rate yourself again in the checklist."; -"web_learn_review_match" = "Your self-assessment matches your quiz. Well done!"; -"web_learn_review_overrated_fmt" = "You chose ☀️, but question %@ was wrong."; -"web_learn_review_underrated" = "All questions on this were right. You can do more than you think!"; -"web_learn_review_unrated" = "not rated yet"; -"web_learn_back_to_checklist" = "Back to checklist"; -"web_learn_step_fmt" = "Step %d: %@"; -"web_sim_call_arguments_fmt" = "The call does not match the header of %@. Check the values in the parentheses."; -"web_sim_missing_return_fmt" = "%@ ended without returning a value with return."; -"web_sim_file_not_found_fmt" = "There is no file called %@."; -"web_sim_end_of_file_fmt" = "The file %@ has no more lines."; -"web_sim_null_value_fmt" = "%@ is null."; -"web_sim_unknown_variable_case_fmt" = "Unknown variable: %1$@. Java is case-sensitive: did you mean %2$@?"; -"web_sim_written_file_fmt" = "%@ (saved)"; -"web_site_home" = "Home page"; -"web_site_imprint" = "Imprint"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Code"; -"web_live_hide" = "Hide code"; -"web_scale_canvas" = "Text size: structure chart"; -"web_scale_code" = "Text size: Code"; -"web_live_indent" = "Indent"; -"web_live_indent_title" = "Indent and format the code: every } on its own line, spaces around operators, four spaces per level (selected lines, otherwise everything)"; -"web_live_show" = "Show code"; -"web_live_resize" = "Change width of the code area (double-click: reset)"; -"web_live_hint" = "The code grows with your diagram. Grey = placeholder: type in the object inspector and your text appears there. Click a line to select its element."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Your code"; -"web_live_ph_end" = "end"; -"web_live_language" = "Programming language"; -"web_live_java_class" = "As a class"; -"web_live_java_class_hint" = "Class name = title of the diagram"; -"web_live_java_main" = "With main method"; -"web_settings_display" = "Display"; -"web_live_swing" = "Real Java (with Swing)"; -"web_live_swing_hint" = "Real Java with windows (CheerpJ). Downloads about 40 MB once, on the first run."; -"web_live_swing_windows" = "Java windows"; -"web_live_swing_hide" = "Close Java windows"; -"web_live_swing_loading" = "Loading Java (about 40 MB the first time) …"; -"web_live_swing_compiling" = "Compiling …"; -"web_live_swing_running" = "Program is running."; -"web_live_swing_compile_failed" = "Compile error, see above."; -"web_live_swing_failed" = "Java could not be loaded. Check your internet connection."; -"web_live_hint_edit" = "Type Java code here and the diagram builds itself. Or work in the diagram and the code grows along. Grey = placeholder, just type over it."; -"web_live_edit_label" = "Java code"; -"web_parse_line_fmt" = "Line %d: %@"; -"web_parse_missing" = "Still missing here: %@"; -"web_parse_extra" = "One too many: %@"; -"web_parse_unclosed_string" = "The closing %@ is missing here."; -"web_parse_else_without_if" = "This “else” has no matching “if”."; -"web_parse_case_outside_switch" = "“case” and “default” belong inside a “switch”."; -"web_parse_missing_do_while" = "After “do { … }” you still need “while (condition);”."; -"web_parse_more_methods" = "Only one method counts here for now. Create further subprograms as separate tabs."; -"web_parse_missing_import" = "%@ is missing at the very top. Without this line Java doesn’t know Scanner."; -"web_parse_missing_scanner" = "%@ is missing before this line. Without it the Scanner doesn’t exist."; -"web_parse_unknown_type" = "Java doesn’t know this data type. Did you mean %@?"; -"web_quickfix_insert_fmt" = "Suggestion: insert %@"; -"web_quickfix_replace_fmt" = "Suggestion: %1$@ instead of %2$@"; -"web_parse_wrong_case" = "Java is case-sensitive. The right spelling is %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Show diagram"; -"web_editor_hide" = "Hide diagram"; -"web_keywords_title" = "Code keywords"; -"web_keywords_hint" = "if, while, for instead of Branch, For loop …"; -"web_palette_compact" = "Elements always compact"; -"web_palette_compact_hint" = "Symbol with a small name below, otherwise only when the column is narrow"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Third-party software"; -"web_about_software_text" = "The code editor uses CodeMirror and Lezer (MIT License). With Swing support, VisuStruct loads CheerpJ by Leaning Technologies (Community License) and javac from OpenJDK."; -"web_about_licenses" = "License texts"; -"web_live_run" = "Run"; -"web_live_run_title" = "Run the program: output and input appear below"; -"web_live_run_finished" = "Program finished."; -"web_live_run_stopped" = "Stopped."; -"web_console" = "Console"; -"web_console_hide" = "Hide console"; -"web_console_resize" = "Resize console"; -"web_diagram_tools" = "Diagram tools"; -"web_files_title" = "Files"; -"web_files_show" = "Show files"; -"web_files_hide" = "Hide files"; -"web_files_resize" = "Resize files"; -"web_files_new_folder" = "New folder"; -"web_files_switch" = "Choose another folder"; -"web_files_delete" = "Delete"; -"web_files_in_browser" = "In this browser’s storage"; -"web_files_save_hint_fmt" = "Saving puts the program in %@."; -"web_files_empty" = "Nothing saved yet. When you save, VisuStruct creates a folder with today’s date here."; -"web_files_reconnect_hint" = "After reloading, the browser asks once more whether VisuStruct may use your folder."; -"web_files_reconnect_fmt" = "Open folder “%@”"; -"web_files_connect_hint" = "Choose a folder as your workspace, ideally a new folder “VisuStruct-Files” in your Documents. VisuStruct creates a folder for each day in it and saves both diagram and code."; -"web_files_connect" = "Choose folder"; -"web_files_browser_hint" = "This browser can’t open folders on the device (for example on iPad). Your files then stay in this browser’s storage."; -"web_files_browser" = "Save in the browser"; -"web_files_delete_confirm_fmt" = "Really delete “%@”?"; -"web_files_folder_prompt" = "Name of the new folder:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Saved: %@"; -"web_files_create" = "Create"; -"web_learn_ludwig_tip" = "Ludwig’s tip"; -"web_game_title" = "Game: True or false?"; -"web_game_intro" = "Ludwig shows you values and a condition. Is the condition true or false? Ten rounds, getting trickier each time."; -"web_game_start" = "Start game"; -"web_game_round_fmt" = "Round %@ of %@"; -"web_game_score_fmt" = "Points: %@"; -"web_game_true" = "true"; -"web_game_false" = "false"; -"web_game_right" = "Correct!"; -"web_game_wrong" = "Not quite. This is how it works out:"; -"web_game_next" = "Next"; -"web_game_finish" = "Result"; -"web_game_done_fmt" = "%@ of %@ correct."; -"web_game_perfect" = "All correct – Ludwig is seriously impressed!"; -"web_game_good" = "Great! Have another look at your mistakes and try again."; -"web_game_practice" = "Practice makes perfect. Read the truth table above once more and play another round."; -"web_game_again" = "Play again"; -"web_game_wrong_short" = "Sorry, that's wrong."; -"web_game_practice_short" = "Practice makes perfect. Read the explanations again and play another round."; -"web_game_clicks" = "Clicks:"; -"web_game_nobody" = "Nobody is listening – nothing happens."; -"web_game_event_fmt" = "Click on “%@”: the listener hears it and runs its code."; -"web_game_try" = "Now try the buttons in the window yourself."; -"web_game_time_fmt" = "%@ s left"; -"web_game_defused" = "✂️ Defused! You found the mistake."; -"web_game_boom_time" = "💥 Boom! Time is up."; -"web_game_boom_wrong" = "💥 Boom! That was the wrong line."; -"web_game_boom_part" = "💥 Boom! Right line, but the mistake is in another part of it."; -"web_game_boom_wrong_part" = "💥 Boom! The mistake is in another line."; -"web_game_bomb_parts_hint" = "Tap exactly the wrong part."; -"web_game_fix" = "The correct line:"; -"web_game_order_hint" = "Tap the steps in the right order."; -"web_debug_step" = "Step"; -"web_debug_step_title" = "Step: run one statement and pause again"; -"web_debug_continue" = "Continue to the next breakpoint"; -"web_debug_paused_fmt" = "Paused before line %d."; -"web_debug_gutter_title" = "Click a line number to set or remove a breakpoint"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Caesar wheel"; -"web_learn_caesar_hint" = "Turn the inner wheel or tap −1 and +1. The outer ring shows the plain text, the inner ring the secret text."; -"web_learn_caesar_key_fmt" = "Key %d"; -"web_learn_caesar_plain" = "Plain text"; -"web_learn_caesar_secret" = "Secret text"; -"web_learn_caesar_encrypt" = "Encrypt"; -"web_learn_caesar_decrypt" = "Decrypt"; -"web_learn_caesar_back" = "Turn wheel back one"; -"web_learn_caesar_forward" = "Turn wheel forward one"; -"web_learn_rescue_station_fmt" = "Station %@ of %@: %@"; -"web_learn_rescue_number" = "Your number"; -"web_learn_rescue_not_number" = "Enter a whole number."; -"web_learn_rescue_wrong" = "Not yet. Read the code again, character by character."; -"web_learn_rescue_letter_moved" = "The letter goes into the crossword."; -"web_learn_rescue_alphabet" = "Your number turns into a letter:"; -"web_learn_rescue_to_crossword" = "To the crossword"; -"web_learn_rescue_crossword" = "The crossword"; -"web_learn_rescue_across" = "→ Across"; -"web_learn_rescue_down" = "↓ Down"; -"web_learn_rescue_cell_fmt" = "Square row %@, column %@"; -"web_learn_rescue_keyword" = "Secret word:"; -"web_learn_rescue_cw_wrong" = "The squares marked red are not right yet."; -"web_learn_rescue_cw_done" = "Solved! The yellow squares spell the secret word."; -"web_learn_rescue_to_gate" = "To the gate"; -"web_learn_rescue_gate" = "The gate"; -"web_learn_rescue_word" = "Secret word"; -"web_learn_rescue_open" = "Open the gate"; -"web_learn_rescue_gate_case" = "Almost! equals tells capital and small letters apart. Write the word exactly as in the crossword."; -"web_learn_rescue_gate_wrong" = "The gate stays shut. Look at the yellow squares again."; -"web_learn_rescue_freed" = "🎉 The gate is open!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "What do you want to work with?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: structure charts and Java with the whole course. You can switch any time in the gear menu."; -"web_visubot_world_visubot_hint" = "VisuBot: program the mBot2 robot, with its own lessons and tasks. You can switch any time in the gear menu."; -"web_visubot_connect" = "Connect real mBot2"; -"web_visubot_upload_button" = "Upload"; -"web_visubot_blocks" = "Blocks"; -"web_visubot_blocks_title" = "Show the structure chart as blocks"; -"web_visubot_blocks_back" = "Structure chart"; -"web_visubot_blocks_back_title" = "Back to the structure chart"; -"web_visubot_blocks_start" = "When the program starts"; -"web_visubot_blocks_empty" = "A block goes here"; -"web_visubot_blocks_hint" = "The same program as blocks. Drag blocks in from “Elements”, move them around or drop them in the trash. Click a number or text in a white field to change it right in the block; everything else in the Object Inspector below. Drop sensors onto a condition, or right onto the field they should replace. Drag operators and variables onto a field or a condition in the same way."; -"web_visubot_blocks_trash" = "Trash"; -"web_visubot_blocks_count" = "count with"; -"web_visubot_blocks_count_from" = "from"; -"web_visubot_blocks_count_below" = "to below"; -"web_visubot_blocks_count_to" = "to"; -"web_visubot_blocks_count_above" = "to above"; -"web_visubot_blocks_count_step" = "by"; -"web_visubot_blocks_repeat" = "repeat"; -"web_visubot_blocks_if" = "if"; -"web_visubot_blocks_else" = "else"; -"web_visubot_blocks_while" = "while"; -"web_visubot_blocks_endless" = "forever"; -"web_visubot_blocks_do" = "do"; -"web_visubot_blocks_not" = "not"; -"web_visubot_blocks_and" = "and"; -"web_visubot_blocks_or" = "or"; -"web_visubot_blocks_break" = "leave the loop"; -"web_visubot_blocks_repeat_times" = "times"; -"web_visubot_blocks_random_from" = "random number from"; -"web_visubot_blocks_random_to" = "to"; -"web_visubot_operators" = "Operators"; -"web_visubot_variables" = "Variables"; -"web_visubot_control" = "Control"; -"web_visubot_el_repeat" = "repeat 10 times"; -"web_visubot_upload_title" = "Upload program to the mBot2"; -"web_visubot_upload_step1" = "Connect the mBot2 with the USB cable and switch it on. This only works in Chrome or Edge."; -"web_visubot_upload_step2" = "Click “Upload now” and choose “USB Serial”. The mBot2 saves your program and restarts."; -"web_visubot_upload_step3" = "Unplug the cable and put the mBot2 at the start. When you press button A, your program starts. It stays saved, even after switching off."; -"web_visubot_upload_start" = "Upload now"; -"web_visubot_upload_busy" = "Uploading … do not unplug the cable."; -"web_visubot_upload_done" = "Done! Unplug the cable, put the mBot2 at the start, press button A."; -"web_visubot_upload_failed" = "The upload did not work. Restart the mBot2 and try again."; -"web_visubot_upload_label" = "A = Start"; -"web_visubot_upload_error_input" = "Your program asks for an input. The mBot2 has no keyboard, so this does not work."; -"web_visubot_upload_error_files" = "Your program works with files. This does not work on the mBot2."; -"web_visubot_upload_error_empty" = "Your program is still empty."; -"web_visubot_disconnect" = "Disconnect"; -"web_visubot_connecting" = "Connecting … (the CyberPi restarts briefly)"; -"web_visubot_connected" = "Real mBot2 drives along"; -"web_visubot_no_serial" = "Real mBot2: only in Chrome or Edge on a computer."; -"web_visubot_link_error" = "Connection lost"; -"web_visubot_world" = "VisuBot world"; -"web_visubot_no_3d" = "The 3D view cannot start in this browser."; -"web_visubot_distance" = "Distance"; -"web_visubot_line" = "Line"; -"web_visubot_bumps" = "bumped into the wall"; -"web_visubot_el_forward" = "move forward"; -"web_visubot_el_backward" = "move back"; -"web_visubot_el_right" = "turn right"; -"web_visubot_el_left" = "turn left"; -"web_visubot_el_led" = "LEDs on"; -"web_visubot_el_tone" = "play a tone"; -"web_visubot_drive" = "Drive"; -"web_visubot_depth_fmt" = "The loops must be nested %d deep."; -"web_visubot_tap_part" = "Rotate the mBot2 with the mouse or your finger. Tap a part to see its name."; -"web_visubot_quiz_right" = "Correct!"; -"web_visubot_quiz_wrong_fmt" = "That is: %@. Try again."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Screen"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Buttons A, B and Home"; -"web_visubot_part_leds" = "RGB LEDs"; -"web_visubot_part_lightSensor" = "Light sensor"; -"web_visubot_part_microphone" = "Microphone"; -"web_visubot_part_shield" = "mBot2 shield with battery"; -"web_visubot_part_powerSwitch" = "Power switch"; -"web_visubot_part_motors" = "Encoder motors"; -"web_visubot_part_wheels" = "Wheels"; -"web_visubot_part_ultrasonic" = "Ultrasonic sensor"; -"web_visubot_part_lineSensor" = "Line sensor (quad RGB)"; -"web_visubot_part_caster" = "Ball caster"; -"web_visubot_part_chassis" = "Chassis"; -"web_visubot_world_fmt" = "World %d"; -"web_visubot_goal_reached" = "Goal reached"; -"web_visubot_goal_missed" = "Goal not reached"; -"web_visubot_commands" = "mBot2 commands"; -"web_visubot_sensors" = "mBot2 sensors"; -"web_visubot_sensor_distance" = "Distance"; -"web_visubot_sensor_line" = "Line"; -"web_visubot_el_led_off" = "LEDs off"; -"web_visubot_el_led_color" = "set LED colour"; -"web_visubot_el_wait" = "wait a moment"; -"web_visubot_el_emotion" = "show a feeling"; -"web_visubot_el_text" = "show text"; -"web_visubot_sensor_color" = "Colour is red?"; -"web_visubot_sensor_button_a" = "Button A pressed?"; -"web_visubot_sensor_button_b" = "Button B pressed?"; -"web_visubot_sensor_joy_up" = "Joystick up?"; -"web_visubot_sensor_joy_down" = "Joystick down?"; -"web_visubot_sensor_joy_left" = "Joystick left?"; -"web_visubot_sensor_joy_right" = "Joystick right?"; -"web_visubot_sensor_brightness" = "Brightness"; -"web_visubot_sensor_loudness" = "Loudness"; -"web_visubot_sensor_pitch" = "Tilt forward/back"; -"web_visubot_sensor_roll" = "Tilt sideways"; -"web_visubot_sensor_shake" = "Shaken?"; -"web_visubot_el_drive" = "wheels separately"; -"web_visubot_el_stop" = "stop"; -"web_visubot_el_timer" = "reset stopwatch"; -"web_visubot_sensor_line_left" = "Line left?"; -"web_visubot_sensor_line_right" = "Line right?"; -"web_visubot_sensor_timer" = "Stopwatch (s)"; -"web_visubot_sensor_battery" = "Battery (%)"; -"web_visubot_actions" = "Commands"; -"web_visubot_led_on" = "LEDs on"; -"web_visubot_led_off" = "LEDs off"; -"web_visubot_tone_yes" = "tone"; -"web_visubot_tone_no" = "no tone"; -"web_visubot_controls" = "Try the sensors"; -"web_visubot_controls_hint" = "Without a real mBot2: set here what the sensors measure. Buttons, joystick and shaking last for one second."; -"web_visubot_shake" = "shake"; -"web_visubot_check" = "Test the mBot2"; -"web_visubot_check_hint" = "Try each actuator once. The sensor values update live: hold your hand in front of the ultrasonic sensor, press the buttons, tilt or shake the mBot2. Lift it up for the wheels."; -"web_visubot_check_forward" = "Wheels 1 s forward"; -"web_visubot_check_spin" = "Turn on the spot for 1 s"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Where do you keep your work?"; -"web_learn_start_hint" = "Your learning progress and programs live in a folder on your device. The progress is stored there in the file learnProgress.json."; -"web_learn_start_continue_fmt" = "Continue with “%@”"; -"web_learn_start_open" = "Open existing folder"; -"web_learn_start_create_fmt" = "Create new folder “%@”"; -"web_learn_start_create_hint" = "Next, choose where it should go, e.g. in “Documents”."; -"web_learn_start_folder" = "VisuStruct-Files"; -"web_learn_start_without" = "Continue without a folder"; -"web_learn_start_without_hint" = "Your learning progress is then lost when you close the page."; -"web_visubot_preview_title" = "mBot2 live"; -"web_visubot_preview_hint" = "Shows right away where your program drives the mBot2."; -"web_visubot_preview_sim_title" = "Simulation"; -"web_visubot_preview_sim_hint" = "The mBot2 drives here while the simulation runs."; -"web_visubot_world_in_preview" = "The mBot2 drives in the “Simulation” window."; -"web_visubot_preview_play" = "Play the drive"; -"web_visubot_preview_stop" = "Stop"; -"web_visubot_preview_hide" = "Collapse mBot2 live"; -"web_visubot_preview_show" = "Show mBot2 live"; -"web_visubot_preview_resize" = "Resize mBot2 live"; -"web_visubot_preview_stack" = "Below the code in the Code area"; -"web_visubot_preview_side" = "As a column of its own"; -"web_visubot_preview_distance_fmt" = "Distance: %d cm"; -"web_visubot_preview_input" = "needs an input, start the simulation for that"; -"web_visubot_preview_limit" = "runs for a very long time (endless loop?), showing the start"; -"web_visubot_group_drive" = "Driving and turning"; -"web_visubot_group_wheels" = "Wheels separately"; -"web_visubot_group_leds" = "Light"; -"web_visubot_group_sound" = "Sound"; -"web_visubot_group_screen" = "Screen"; -"web_visubot_group_emotion" = "Emotions"; -"web_visubot_group_wait" = "Waiting and stopwatch"; -"web_visubot_group_distance" = "Distance"; -"web_visubot_group_line" = "Line"; -"web_visubot_group_color" = "Colours"; -"web_visubot_group_buttons" = "Buttons and joystick"; -"web_visubot_group_light" = "Light and loudness"; -"web_visubot_group_tilt" = "Tilt and shaking"; -"web_visubot_group_timer" = "Stopwatch and battery"; -"web_swap_to_code" = "Code large, structogram narrow"; -"web_swap_to_editor" = "Structogram large, code narrow"; -"web_editor_resize" = "Resize the structogram"; -"web_palette_resize" = "Resize the elements (double-click: reset)"; -"web_palette_hide" = "Hide elements"; -"web_palette_show" = "Show elements"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: write classes in Java and see or draw them as a class diagram (from year 12). You can switch any time in the gear menu."; -"web_visuobject_subtitle" = "Class diagram"; -"web_visuobject_code" = "Java code"; -"web_visuobject_diagram" = "Class diagram"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Add a new interface to the diagram and the code"; -"web_visuobject_layout" = "Arrange again"; -"web_visuobject_layout_tip" = "Arrange all classes automatically: superclasses at the top"; -"web_visuobject_add_field" = "new attribute …"; -"web_visuobject_add_method" = "new method …"; -"web_visuobject_delete_class" = "Delete class"; -"web_visuobject_new_class_name" = "NewClass"; -"web_visuobject_new_interface_name" = "NewInterface"; -"web_visuobject_example" = "Example"; -"web_visuobject_example_tip" = "Load the example project Zoo: Tier and Hund in package tiere, Besitzer in personen"; -"web_visuobject_new_tip" = "New: empty project (Undo brings the old one back)"; -"web_visuobject_empty" = "No class yet. Click “+ Class” on the left or type for example “public class Dog { }” into a file."; -"web_visuobject_edit_hint" = "Double-click to change. Write it as in UML: “- name: String” or “+ bark(): void”. Leave it empty to delete."; -"web_visuobject_member_label" = "Attribute or method in UML notation"; -"web_visuobject_name_label" = "Class name"; -"web_visuobject_bad_name" = "“%@” is not a valid name or already exists."; -"web_visuobject_bad_line" = "I don’t understand “%@”. Example: - name: String or + bark(): void"; -"web_visuobject_open_failed" = "The file could not be read."; -"web_visuobject_field_ph" = "- name: String"; -"web_visuobject_seq_diagram" = "Sequence diagram"; -"web_visuobject_seq_empty" = "The sequence diagram is built while debugging: click “Debug” above and step through the program."; -"web_visuobject_seq_no_calls" = "No calls of your own methods yet."; -"web_visuobject_seq_truncated" = "… shortened: too many calls from here on."; -"web_visuobject_seq_hint" = "Each arrow is a call of one of your methods, the dashed arrow brings the return value back. Click an arrow to see the line in the code."; -"web_visuobject_dbg" = "Debug"; -"web_visuobject_dbg_tip" = "Run the program step by step. Breakpoint: click the line number. The object diagram shows the objects in memory."; -"web_visuobject_dbg_continue" = "Resume"; -"web_visuobject_dbg_continue_tip" = "Run to the next breakpoint (F8)"; -"web_visuobject_dbg_pause" = "Pause"; -"web_visuobject_dbg_pause_tip" = "Pause the program"; -"web_visuobject_dbg_over" = "Step"; -"web_visuobject_dbg_over_tip" = "Next line; methods run completely (F6)"; -"web_visuobject_dbg_into" = "Into"; -"web_visuobject_dbg_into_tip" = "Step into the called method (F5)"; -"web_visuobject_dbg_out" = "Out"; -"web_visuobject_dbg_out_tip" = "Finish the method and return to the caller (F7)"; -"web_visuobject_dbg_input" = "Input for the Scanner, then Enter"; -"web_visuobject_dbg_running" = "Debugger: program running …"; -"web_visuobject_dbg_waiting" = "Debugger: the program is waiting for your input."; -"web_visuobject_dbg_paused_fmt" = "Debugger: paused in %1$@, line %2$@"; -"web_visuobject_dbg_exception" = "Program stopped with an exception."; -"web_visuobject_dbg_code_changed" = "Code changed: debugger stopped."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Call stack"; -"web_visuobject_dbg_vars" = "Variables"; -"web_visuobject_dbg_var_name" = "Name"; -"web_visuobject_dbg_var_type" = "Type"; -"web_visuobject_dbg_no_vars" = "No variables"; -"web_visuobject_dbg_err_syntax" = "%1$@, line %2$@: the debugger does not understand this (%3$@). ▶ Start shows Java's message."; -"web_visuobject_dbg_err_symbol" = "%1$@, line %2$@: %3$@ is unknown."; -"web_visuobject_dbg_err_import" = "%1$@, line %2$@: %3$@ is missing at the very top. Without this line Java doesn’t know the class."; -"web_visuobject_dbg_err_typename" = "%1$@, line %2$@: Java doesn’t know this data type. Did you mean %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, line %2$@: the debugger cannot do %3$@ yet. ▶ Start runs the program in real Java."; -"web_visuobject_dbg_err_type" = "%1$@, line %2$@: something does not fit here (%3$@)."; -"web_visuobject_obj_diagram" = "Object diagram"; -"web_visuobject_obj_new" = "New object of class"; -"web_visuobject_obj_add" = "Object"; -"web_visuobject_obj_empty" = "No objects yet. Choose a class above and click “+ Object”."; -"web_visuobject_obj_no_classes" = "First create a class (not abstract) in the class diagram."; -"web_visuobject_obj_hint" = "Click an object to change its name, class and values here. Drag the handle to another object to connect them through a reference attribute."; -"web_visuobject_obj_live_hint" = "Live from the debugger: this is how the objects look in memory right now."; -"web_visuobject_obj_link" = "Drag to another object: set a reference"; -"web_visuobject_obj_name" = "Name"; -"web_visuobject_obj_class" = "Class"; -"web_visuobject_obj_attribute" = "Attribute"; -"web_visuobject_obj_value" = "Value"; -"web_visuobject_obj_delete" = "Delete object"; -"web_visuobject_obj_bad_name" = "“%@” is not a valid object name."; -"web_visuobject_obj_name_taken" = "There is already an object “%@”."; -"web_visuobject_obj_no_ref" = "%1$@ has no attribute that can refer to %2$@."; -"web_visuobject_tool_note" = "Note"; -"web_visuobject_tool_note_tip" = "Note: click into the class diagram, then type the text. Drag the handle to a class for a dashed line."; -"web_visuobject_note_ph" = "Note …"; -"web_visuobject_note_text" = "Text"; -"web_visuobject_note_attached" = "Attached to"; -"web_visuobject_note_attach" = "Drag to a class: dashed line"; -"web_visuobject_note_delete" = "Delete note"; -"web_visuobject_run" = "Run"; -"web_visuobject_run_tip" = "Run the program with real Java: all classes are compiled, the class with main is started (preferably the open one)."; -"web_visuobject_stop" = "Stop"; -"web_visuobject_stop_tip" = "End the program and close its windows"; -"web_visuobject_stopped" = "Program stopped."; -"web_visuobject_no_main" = "No class with public static void main(String[] args) found."; -"web_visuobject_running_fmt" = "%@ is running …"; -"web_visuobject_console_clear" = "Clear"; -"web_visuobject_section_fields" = "Attributes"; -"web_visuobject_section_refs" = "Reference attributes"; -"web_visuobject_section_ctors" = "Constructor"; -"web_visuobject_section_methods" = "Methods"; -"web_visuobject_insp_sections" = "Insert section comments"; -"web_visuobject_insp_sort" = "Sort into sections"; -"web_visuobject_insp_sections_tip" = "Writes // Attributes, // Reference attributes, // Constructor and // Methods into the class and sorts everything below them. New members from the class diagram then go straight to the right place."; -"web_visuobject_insp_doc" = "Description (Javadoc)"; -"web_visuobject_insp_doc_ph" = "What is the class for? Written in the code as a Javadoc comment above the class."; -"web_visuobject_insp_visibility" = "Visibility"; -"web_visuobject_insp_type" = "Type"; -"web_visuobject_insp_init" = "Initial value"; -"web_visuobject_insp_no_fields" = "No attributes yet."; -"web_visuobject_insp_add_field" = "Attribute"; -"web_visuobject_insp_params" = "Parameters"; -"web_visuobject_insp_return" = "Return type"; -"web_visuobject_insp_constructor" = "Constructor"; -"web_visuobject_insp_no_methods" = "No methods yet."; -"web_visuobject_insp_add_method" = "Method"; -"web_visuobject_insp_generate" = "Generate:"; -"web_visuobject_insp_gen_constructor" = "Constructor from attributes"; -"web_visuobject_insp_gen_constructor_tip" = "Like Eclipse “Generate Constructor using Fields”: a constructor that sets all attributes (except static)."; -"web_visuobject_insp_gen_accessors" = "Getters and setters"; -"web_visuobject_insp_gen_accessors_tip" = "Like Eclipse “Generate Getters and Setters”: get…/set… for each attribute (final attributes get only a getter)."; -"web_visuobject_insp_bad_params" = "Write parameters like this: name: Type, second: int (read “%@”)."; -"web_visuobject_insp_fields" = "Attributes"; -"web_visuobject_insp_methods" = "Methods"; -"web_visuobject_inspector_resize" = "Resize: drag the border between class diagram and object inspector up or down (double-click = default)"; -"web_visuobject_dep_title" = "Dependency «%@»"; -"web_visuobject_dep_create_text" = "%1$@ creates an object of class %2$@ in %3$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ uses the class %2$@ in %3$@."; -"web_visuobject_dep_hint" = "This is not an association: %@ does not keep the %@ object in an attribute, it only lives inside the method. That is why the line is dashed."; -"web_visuobject_dep_to_assoc" = "Keep as attribute (association to %@)"; -"web_visuobject_end_mult_ph" = "mult."; -"web_visuobject_end_tip" = "Click (line selected) or right-click: role, multiplicity and navigability at this end"; -"web_visuobject_end_role_ph" = "e.g. myCats"; -"web_visuobject_new_menu" = "New"; -"web_visuobject_new_menu_tip" = "Create a new project, source folder, package, class or interface"; -"web_visuobject_new_project" = "Java project"; -"web_visuobject_new_folder" = "Source folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Class"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "New Java project"; -"web_visuobject_new_project_hint" = "In the workspace every project gets its own folder with src."; -"web_visuobject_new_folder_title" = "New source folder"; -"web_visuobject_new_folder_hint" = "Another folder for source code next to src, e.g. test for test classes."; -"web_visuobject_new_package_title" = "New package"; -"web_visuobject_new_package_hint" = "Package names are lower case, parts separated by dots: animals or school.office."; -"web_visuobject_new_class_title" = "New Java class"; -"web_visuobject_new_class_hint" = "Class names start with a capital letter, e.g. Dog or BankAccount."; -"web_visuobject_new_interface_title" = "New Java interface"; -"web_visuobject_new_interface_hint" = "An interface defines methods that classes implement later."; -"web_visuobject_new_root" = "Source folder"; -"web_visuobject_new_name" = "Name"; -"web_visuobject_new_modifiers" = "Modifiers"; -"web_visuobject_new_package_private" = "package (no modifier)"; -"web_visuobject_new_superclass" = "Superclass"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Extends interfaces (extends)"; -"web_visuobject_new_stubs" = "Which method stubs should be created?"; -"web_visuobject_new_stub_constructors" = "Constructors from superclass"; -"web_visuobject_new_stub_inherited" = "Inherited abstract methods"; -"web_visuobject_new_comment_legend" = "Comment"; -"web_visuobject_new_comment" = "Javadoc comment with @author"; -"web_visuobject_new_author" = "Your name"; -"web_visuobject_new_finish" = "Finish"; -"web_visuobject_new_name_missing" = "Please enter a name."; -"web_visuobject_new_exists" = "“%@” already exists in the project."; -"web_visuobject_new_upper" = "In Java, class names start with a capital letter."; -"web_visuobject_bad_folder" = "“%@” is not a valid folder name or already exists."; -"web_visuobject_inspector_hide" = "Collapse object inspector"; -"web_visuobject_inspector_show" = "Expand object inspector"; -"web_visuobject_inspector_empty" = "Click a relationship (line) in the diagram to edit it here."; -"web_visuobject_rel_end_a" = "End A: %@ (line starts here)"; -"web_visuobject_rel_end_b" = "End B: %@ (line ends here)"; -"web_visuobject_ws_title" = "Workspace folder on your computer"; -"web_visuobject_ws_question" = "Where do you keep your projects?"; -"web_visuobject_ws_hint" = "Like in Eclipse: a workspace folder on your computer with one folder per project, holding src and the .java files."; -"web_visuobject_ws_create" = "Create folder “%@”"; -"web_visuobject_ws_open" = "Open existing workspace"; -"web_visuobject_ws_skip" = "Without a folder (browser only)"; -"web_visuobject_ws_choose" = "Choose workspace"; -"web_visuobject_ws_reopen" = "Open workspace “%@”"; -"web_visuobject_ws_switch" = "Switch"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Download the project as .zip, e.g. to hand it in"; -"web_visuobject_ws_saved" = "Saved to %@/%@"; -"web_visuobject_ws_created" = "New project “%@” in the workspace"; -"web_visuobject_ws_failed" = "Saving to the workspace failed: %@"; -"web_visuobject_ws_taken" = "The workspace already has a project “%@”."; -"web_visuobject_ws_open_project" = "Open project %@"; -"web_visuobject_save_ws_tip" = "Save to the workspace (Ctrl+S)"; -"web_visuobject_unsaved" = "not saved"; -"web_visuobject_rel_nav_navigable" = "navigable (arrow)"; -"web_visuobject_rel_nav_non-navigable" = "non-navigable (cross ×)"; -"web_visuobject_rel_nav_unspecified" = "unspecified (open)"; -"web_visuobject_rel_reading_flip" = "reverse"; -"web_visuobject_rel_reading_auto" = "With a name, the relationship reads in the direction you dragged."; -"web_visuobject_tools" = "Tools"; -"web_visuobject_tool_select" = "Select"; -"web_visuobject_tool_select_tip" = "Select and move"; -"web_visuobject_tool_class" = "Class"; -"web_visuobject_tool_class_tip" = "Class: drag onto the canvas, or pick it and click on the canvas"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: drag onto the canvas, or pick it and click on the canvas"; -"web_visuobject_tool_link" = "Association"; -"web_visuobject_tool_link_tip" = "Association without navigation: draw a plain line between two classes"; -"web_visuobject_rel_link" = "Association (no navigation)"; -"web_visuobject_tool_not_link" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_association" = "Directed"; -"web_visuobject_tool_association_tip" = "Directed association: drag from the class that knows the other one to the other (arrow)"; -"web_visuobject_tool_aggregation" = "Aggregation"; -"web_visuobject_tool_aggregation_tip" = "Aggregation: drag from the whole to the part (has, parts live on)"; -"web_visuobject_tool_composition" = "Composition"; -"web_visuobject_tool_composition_tip" = "Composition: drag from the whole to the part (consists of, parts belong to it)"; -"web_visuobject_tool_extends" = "Inheritance"; -"web_visuobject_tool_extends_tip" = "Inheritance: drag from the subclass to the superclass (extends)"; -"web_visuobject_tool_implements" = "Realization"; -"web_visuobject_tool_implements_tip" = "Realization: drag from the class to the interface (implements)"; -"web_visuobject_tool_pick_second" = "Now click the second class (from %@). Esc cancels."; -"web_visuobject_tool_not_association" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_aggregation" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_composition" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_extends" = "%@ cannot inherit from %@: classes extend classes, interfaces extend interfaces. For an interface use realization."; -"web_visuobject_tool_not_implements" = "%@ cannot implement %@: only a class implements an interface."; -"web_visuobject_inspector" = "Object inspector"; -"web_visuobject_connect" = "Drag: relationship to another class"; -"web_visuobject_rel_association" = "Association"; -"web_visuobject_rel_directed" = "Directed association"; -"web_visuobject_rel_aggregation" = "Aggregation (has, parts live on)"; -"web_visuobject_rel_composition" = "Composition (consists of, parts belong to it)"; -"web_visuobject_rel_extends" = "Inheritance (extends)"; -"web_visuobject_rel_implements" = "Realization (implements)"; -"web_visuobject_rel_extends_text" = "%@ inherits from %@ (extends in the code)."; -"web_visuobject_rel_implements_text" = "%@ implements the interface %@ (implements in the code)."; -"web_visuobject_rel_kind" = "Kind"; -"web_visuobject_rel_name" = "Name of the relationship"; -"web_visuobject_rel_name_ph" = "e.g. owns"; -"web_visuobject_rel_reading" = "Reading direction"; -"web_visuobject_rel_navigable" = "Navigability"; -"web_visuobject_rel_role" = "Role"; -"web_visuobject_rel_multiplicity" = "Multiplicity"; -"web_visuobject_rel_role_field" = "= attribute %@.%@ in the code"; -"web_visuobject_rel_role_only_uml" = "diagram only (no attribute while the arrow points one way)"; -"web_visuobject_rel_delete" = "Delete relationship"; -"web_visuobject_rel_impossible" = "There is no suitable relationship from %@ to %@."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Drag to resize (double-click: default)"; -"web_visuobject_pane_show" = "Expand area"; -"web_visuobject_pane_hide" = "Collapse area"; -"web_visuobject_close_tab" = "Close %@"; -"web_visuobject_collapse" = "Collapse"; -"web_visuobject_expand" = "Expand"; -"web_visuobject_tree_empty" = "Still empty. Create a package or class with “+ New”."; -"web_visuobject_delete" = "Delete"; -"web_visuobject_default_package" = "(default package)"; -"web_visuobject_bad_package" = "“%@” is not a valid package name or already exists. Package names are lower case, e.g. animals."; -"web_visuobject_new_package_name" = "newpackage"; -"web_visuobject_no_file" = "No file open"; -"web_visuobject_project_name" = "MyProject"; -"web_visuobject_explorer_show" = "Show Package Explorer"; -"web_visuobject_explorer_hide" = "Collapse Package Explorer"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Save the project as .zip: ProjectName/src/package/Class.java"; -"web_visuobject_open_tip" = "Open a project (.zip as from Eclipse or BlueJ) or add .java files to the project"; -"web_visuobject_method_ph" = "+ bark(): void"; -"web_code_scopes" = "Colored blocks"; -"web_code_scopes_hint" = "Code and structogram: loop pink, branch blue, method yellow, class green"; -"web_pseudocode" = "Pseudocode"; - -/* VisuFlow (draw flowcharts) */ -"web_visuflow_subtitle" = "Draw flowcharts"; -"web_visuflow_world_hint" = "VisuFlow: draw a program flowchart freely and export it as an image or PDF. You can switch at any time in the gear menu."; -"web_visuflow_palette" = "Symbols"; -"web_visuflow_palette_hint" = "Tap a symbol to add it below the selected one, joined by an arrow. You can also drag it onto the canvas."; -"web_visuflow_shape_start" = "Start"; -"web_visuflow_shape_ende" = "End"; -"web_visuflow_shape_anweisung" = "Statement"; -"web_visuflow_shape_ein" = "Input/Output"; -"web_visuflow_shape_verzweigung" = "Decision"; -"web_visuflow_shape_unterprogramm" = "Subroutine"; -"web_visuflow_shape_start_tip" = "Oval: the flow starts here"; -"web_visuflow_shape_ende_tip" = "Oval: the flow ends here"; -"web_visuflow_shape_anweisung_tip" = "Rectangle: one statement, for example “Boil water”"; -"web_visuflow_shape_ein_tip" = "Parallelogram: input or output, for example “Input: number”"; -"web_visuflow_shape_verzweigung_tip" = "Diamond: a question with the exits yes and no"; -"web_visuflow_shape_unterprogramm_tip" = "Rectangle with two bars: calls a subroutine"; -"web_visuflow_text_start" = "Start"; -"web_visuflow_text_ende" = "End"; -"web_visuflow_text_anweisung" = "Statement"; -"web_visuflow_text_ein" = "Input: number"; -"web_visuflow_text_verzweigung" = "Condition?"; -"web_visuflow_text_unterprogramm" = "Subroutine"; -"web_visuflow_new_tip" = "New: empty flowchart with Start (Undo brings the old one back)"; -"web_visuflow_open_tip" = "Open a flowchart file (.visuflow)"; -"web_visuflow_save_tip" = "Save the flowchart as a file (.visuflow)"; -"web_visuflow_delete" = "Delete"; -"web_visuflow_delete_tip" = "Delete the selected symbol or arrow (Del)"; -"web_visuflow_swap" = "swap yes/no"; -"web_visuflow_swap_tip" = "Diamond: “yes” and “no” swap sides"; -"web_visuflow_example" = "Example"; -"web_visuflow_example_tip" = "Load the example “Make tea” from lesson 1 (Undo brings the old flowchart back)"; -"web_visuflow_example_title" = "Make tea"; -"web_visuflow_example_water" = "Boil water"; -"web_visuflow_example_bag" = "Tea bag into the cup"; -"web_visuflow_example_hot" = "Tea still too hot?"; -"web_visuflow_example_wait" = "Wait"; -"web_visuflow_example_drink" = "Drink tea"; -"web_visuflow_title_label" = "Flowchart title"; -"web_visuflow_title_ph" = "Flowchart title"; -"web_visuflow_canvas_label" = "Flowchart canvas"; -"web_visuflow_open_error" = "This file is not a VisuFlow flowchart."; -"web_visuflow_saved" = "Saved as %@"; -"web_visuflow_status_empty" = "Drag a symbol from the bar onto the canvas or tap it."; -"web_visuflow_status_idle" = "Tap a symbol to select it. Drag from the dot at its exit to draw an arrow to the next symbol."; -"web_visuflow_status_node" = "Drag to move the symbol, tap again to change the text. Drag an arrow from the dot at the exit."; -"web_visuflow_status_edge" = "Arrow selected. Delete removes it."; -"web_visuflow_status_connect" = "Let go over the symbol the arrow leads to."; -"web_visuflow_edit_label" = "Text in the symbol"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Rename variable …"; -"web_rename_title" = "Rename variable"; -"web_rename_old" = "Name"; -"web_rename_new" = "New name"; -"web_rename_hint" = "Changes every place with exactly this spelling (number is not Number), in the code and in the structogram. Text in \"…\" and comments stay."; -"web_rename_count_fmt" = "Used %d times."; -"web_rename_exists_fmt" = "“%@” already exists: afterwards both would be one variable."; -"web_rename_invalid" = "A name starts with a letter and contains only letters, digits and _."; -"web_rename_keyword_fmt" = "“%@” is a Java keyword."; -"web_rename_apply" = "Rename"; -"web_rename_cancel" = "Cancel"; -"web_visubot_world_visucode_hint" = "Other programs like %1$@ or %2$@ are on visucode.de."; diff --git a/web/src/i18n/es.strings b/web/src/i18n/es.strings deleted file mode 100644 index 354bbf43..00000000 --- a/web/src/i18n/es.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Textos que solo existen en la versión web. Todo lo demás procede de los Localizable.strings de la app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagrama de VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Supr"; -"web_key_shift" = "Mayús"; -"web_export" = "Exportar…"; -"web_export_title" = "Guardar como imagen o PDF, o imprimir"; -"web_export_png" = "Imagen (PNG)"; -"web_export_svg" = "Gráfico vectorial (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Archivo VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Error al exportar: %@"; -"web_print_hint" = "Usa “Exportar… → PDF” para guardar el diagrama e imprimirlo desde allí."; -"web_delete" = "Eliminar"; -"web_language_title" = "Idioma de la interfaz"; -"web_simulation_start" = "Iniciar simulación"; -"web_simulation_end" = "Terminar simulación"; -"web_simulation_start_title" = "Ejecutar el diagrama paso a paso"; -"web_simulation_end_title" = "Terminar la simulación y seguir editando"; -"web_palette_hint" = "Arrastrar al diagrama"; -"web_tabs_label" = "Diagramas"; -"web_status_modified" = "modificado"; -"web_inspector_title" = "Propiedades"; -"web_inspector_resize" = "Cambiar la altura del inspector de objetos: arrastra o desliza la línea hacia arriba o abajo (doble clic: estándar)"; -"web_inspector_help_drop" = "Arrastra elementos de la paleta sobre una pieza: arriba = antes, abajo = después, sobre “ø” = reemplazar. Los elementos existentes se pueden mover con el ratón."; -"web_inspector_help_inline" = "Haz doble clic o doble toque en un elemento para editar su texto directamente en el diagrama (Intro aplica, Esc cancela). También los nombres de los casos."; -"web_inspector_help_files" = "También puedes soltar archivos en la ventana."; -"web_case_name" = "Nombre del caso"; -"web_case_name_placeholder" = "p. ej. 1, ok, default"; -"web_copied" = "Elemento copiado."; -"web_discard_message_fmt" = "Se perderán los cambios en “%@”."; -"web_err_unreadable_fmt" = "“%@” no es un archivo de VisuStruct legible (%@)."; -"web_saved_fmt" = "“%@” guardado."; -"web_saved_txt_fmt" = "Guardado como “%@”. Quita la extensión .txt para usarlo."; -"web_drop_impossible" = "No se puede soltar aquí"; -"web_sim_delay" = "Pausa por paso"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Lenguaje"; -"web_codegen_comments" = "Texto del diagrama como comentarios"; -"web_codegen_empty_else" = "Generar rama else vacía en el código"; -"web_copied_check" = "Copiado ✓"; -"web_copy_manual" = "Seleccionado, pulsa Ctrl+C para copiar"; -"web_run_stopped_loop" = "Ejecución detenida: posible bucle infinito."; -"web_run_cancelled" = "Ejecución cancelada."; -"web_run_error" = "Error durante la ejecución."; -"web_run_dropped_fmt" = "… %d líneas anteriores ocultas"; -"web_sim_call_arguments_fmt" = "La llamada no coincide con la cabecera de %@. Revisa los valores entre paréntesis."; -"web_sim_missing_return_fmt" = "%@ terminó sin devolver un valor con return."; -"web_sim_file_not_found_fmt" = "No existe ningún archivo llamado %@."; -"web_sim_end_of_file_fmt" = "El archivo %@ no tiene más líneas."; -"web_sim_null_value_fmt" = "%@ es null."; -"web_sim_unknown_variable_case_fmt" = "Variable desconocida: %1$@. Java distingue entre mayúsculas y minúsculas: ¿querías decir %2$@?"; -"web_sim_written_file_fmt" = "%@ (guardado)"; -"web_site_home" = "Página de inicio"; -"web_site_imprint" = "Aviso legal"; -"web_site_privacy" = "Privacidad"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programación"; -"web_live_hide" = "Ocultar programación"; -"web_scale_canvas" = "Tamaño del texto: diagrama"; -"web_scale_code" = "Tamaño del texto: programación"; -"web_live_indent" = "Sangrar"; -"web_live_indent_title" = "Sangrar el código: cuatro espacios por nivel (líneas seleccionadas, si no, todo)"; -"web_live_show" = "Mostrar programación"; -"web_live_resize" = "Cambiar el ancho del área de código (doble clic: restablecer)"; -"web_live_hint" = "El código crece con tu diagrama. Gris = marcador: escribe en el inspector y tu texto aparecerá ahí. Haz clic en una línea para seleccionar su elemento."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Tu código"; -"web_live_ph_end" = "fin"; -"web_live_language" = "Lenguaje de programación"; -"web_live_java_class" = "Como clase"; -"web_live_java_class_hint" = "Nombre de la clase = título del diagrama"; -"web_live_java_main" = "Con método main"; -"web_settings_display" = "Visualización"; -"web_live_swing" = "Java real (con Swing)"; -"web_live_swing_hint" = "Java real con ventanas (CheerpJ). Descarga unos 40 MB una sola vez, en la primera ejecución."; -"web_live_swing_windows" = "Ventanas de Java"; -"web_live_swing_hide" = "Cerrar ventanas de Java"; -"web_live_swing_loading" = "Cargando Java (unos 40 MB la primera vez) …"; -"web_live_swing_compiling" = "Compilando …"; -"web_live_swing_running" = "El programa se está ejecutando."; -"web_live_swing_compile_failed" = "Error de compilación, mira arriba."; -"web_live_swing_failed" = "No se pudo cargar Java. Comprueba la conexión a internet."; -"web_live_hint_edit" = "Escribe aquí código Java y el diagrama se construye solo. O trabaja en el diagrama y el código crece con él. Gris = marcador, escribe encima."; -"web_live_edit_label" = "Código Java"; -"web_parse_line_fmt" = "Línea %d: %@"; -"web_parse_missing" = "Aquí falta: %@"; -"web_parse_extra" = "Sobra: %@"; -"web_parse_unclosed_string" = "Falta el %@ de cierre."; -"web_parse_else_without_if" = "Este «else» no tiene un «if»."; -"web_parse_case_outside_switch" = "«case» y «default» van dentro de un «switch»."; -"web_parse_missing_do_while" = "Después de «do { … }» falta «while (condición);»."; -"web_parse_more_methods" = "Por ahora solo cuenta un método. Crea otros subprogramas en pestañas propias."; -"web_parse_missing_import" = "Falta %@ al principio. Sin esta línea, Java no conoce Scanner."; -"web_parse_missing_scanner" = "Falta antes %@ Sin esta línea, el Scanner no existe."; -"web_parse_unknown_type" = "Java no conoce este tipo de datos. ¿Querías decir %@?"; -"web_quickfix_insert_fmt" = "Sugerencia: insertar %@"; -"web_quickfix_replace_fmt" = "Sugerencia: %1$@ en lugar de %2$@"; -"web_parse_wrong_case" = "Java distingue entre mayúsculas y minúsculas. Lo correcto es %@."; -"web_editor_title" = "Diagrama"; -"web_editor_show" = "Mostrar diagrama"; -"web_editor_hide" = "Ocultar diagrama"; -"web_keywords_title" = "Palabras clave de código"; -"web_keywords_hint" = "if, while, for en lugar de los nombres de los elementos"; -"web_palette_compact" = "Elementos siempre compactos"; -"web_palette_compact_hint" = "Símbolo con el nombre pequeño debajo; si no, solo con la columna estrecha"; -"web_about_version_fmt" = "Versión %@"; -"web_about_software" = "Software utilizado"; -"web_about_software_text" = "El editor de código usa CodeMirror y Lezer (licencia MIT). Con la compatibilidad con Swing, VisuStruct carga CheerpJ de Leaning Technologies (licencia Community) y javac de OpenJDK."; -"web_about_licenses" = "Textos de las licencias"; -"web_live_run" = "Ejecutar"; -"web_live_run_title" = "Ejecutar el programa: la salida y la entrada aparecen abajo"; -"web_live_run_finished" = "Programa terminado."; -"web_live_run_stopped" = "Detenido."; -"web_console" = "Consola"; -"web_console_hide" = "Ocultar consola"; -"web_console_resize" = "Cambiar la altura de la consola"; -"web_diagram_tools" = "Herramientas del diagrama"; -"web_files_title" = "Archivos"; -"web_files_show" = "Mostrar archivos"; -"web_files_hide" = "Ocultar archivos"; -"web_files_resize" = "Cambiar el ancho de archivos"; -"web_files_new_folder" = "Nueva carpeta"; -"web_files_switch" = "Elegir otra carpeta"; -"web_files_delete" = "Eliminar"; -"web_files_in_browser" = "En el almacenamiento de este navegador"; -"web_files_save_hint_fmt" = "Al guardar, el programa se coloca en %@."; -"web_files_empty" = "Aún no hay nada guardado. Al guardar, VisuStruct crea aquí una carpeta con la fecha de hoy."; -"web_files_reconnect_hint" = "Tras recargar, el navegador vuelve a preguntar si VisuStruct puede usar tu carpeta."; -"web_files_reconnect_fmt" = "Abrir la carpeta «%@»"; -"web_files_connect_hint" = "Elige una carpeta como espacio de trabajo. VisuStruct crea en ella una carpeta para cada día y guarda el diagrama y el código."; -"web_files_connect" = "Elegir carpeta"; -"web_files_browser_hint" = "Este navegador no puede abrir carpetas del dispositivo (por ejemplo en el iPad). Tus archivos quedan en el almacenamiento de este navegador."; -"web_files_browser" = "Guardar en el navegador"; -"web_files_delete_confirm_fmt" = "¿Eliminar «%@»?"; -"web_files_folder_prompt" = "Nombre de la nueva carpeta:"; -"web_files_default_name" = "Programa"; -"web_files_saved_fmt" = "Guardado: %@"; -"web_files_create" = "Crear"; -"web_learn_ludwig_tip" = "El consejo de Ludwig"; -"web_game_title" = "Juego: ¿Verdadero o falso?"; -"web_game_intro" = "Ludwig te muestra valores y una condición. ¿Es verdadera (true) o falsa (false)? Diez rondas, cada vez más difíciles."; -"web_game_start" = "Empezar el juego"; -"web_game_round_fmt" = "Ronda %@ de %@"; -"web_game_score_fmt" = "Puntos: %@"; -"web_game_true" = "verdadero (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "¡Correcto!"; -"web_game_wrong" = "No del todo. Así se calcula:"; -"web_game_next" = "Siguiente"; -"web_game_finish" = "Resultado"; -"web_game_done_fmt" = "%@ de %@ correctas."; -"web_game_perfect" = "¡Todo correcto! Ludwig está impresionado."; -"web_game_good" = "¡Muy bien! Revisa tus errores e inténtalo de nuevo."; -"web_game_practice" = "La práctica hace al maestro. Vuelve a leer la tabla de verdad y juega otra ronda."; -"web_game_again" = "Jugar otra vez"; -"web_game_wrong_short" = "Lo siento, es incorrecto."; -"web_game_practice_short" = "La práctica hace al maestro. Vuelve a leer las explicaciones y juega otra ronda."; -"web_game_clicks" = "Clics:"; -"web_game_nobody" = "Nadie escucha: no pasa nada."; -"web_game_event_fmt" = "Clic en «%@»: el oyente lo oye y ejecuta su código."; -"web_game_try" = "Ahora prueba tú los botones de la ventana."; -"web_game_time_fmt" = "Quedan %@ s"; -"web_game_defused" = "✂️ ¡Desactivada! Has encontrado el error."; -"web_game_boom_time" = "💥 ¡Bum! Se acabó el tiempo."; -"web_game_boom_wrong" = "💥 ¡Bum! Esa no era la línea."; -"web_game_boom_part" = "💥 ¡Bum! La línea es correcta, pero el error está en otra parte."; -"web_game_boom_wrong_part" = "💥 ¡Bum! El error está en otra línea."; -"web_game_bomb_parts_hint" = "Toca exactamente la parte incorrecta."; -"web_game_fix" = "La línea correcta:"; -"web_game_order_hint" = "Toca los pasos en el orden correcto."; -"web_debug_step" = "Paso a paso"; -"web_debug_step_title" = "Paso a paso: ejecutar una instrucción y detenerse de nuevo"; -"web_debug_continue" = "Continuar hasta el siguiente punto de interrupción"; -"web_debug_paused_fmt" = "Detenido antes de la línea %d."; -"web_debug_gutter_title" = "Haz clic en un número de línea para poner o quitar un punto de interrupción"; -"web_swap_to_code" = "Programación grande"; -"web_swap_to_editor" = "Diagrama grande"; -"web_editor_resize" = "Cambiar el ancho del diagrama"; -"web_palette_resize" = "Cambiar el ancho de los elementos"; -"web_palette_hide" = "Ocultar elementos"; -"web_palette_show" = "Mostrar elementos"; -"web_code_scopes" = "Bloques de código en color"; -"web_code_scopes_hint" = "Clase, método, bucle, condición en sus propias cajas"; -"web_pseudocode" = "Pseudocódigo"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Renombrar variable …"; -"web_rename_title" = "Renombrar variable"; -"web_rename_old" = "Nombre"; -"web_rename_new" = "Nombre nuevo"; -"web_rename_hint" = "Cambia cada lugar con exactamente esta escritura (numero no es Numero), en el código y en el estructograma. El texto entre \"…\" y los comentarios no cambian."; -"web_rename_count_fmt" = "Aparece %d veces."; -"web_rename_exists_fmt" = "«%@» ya existe: después ambas serían una sola variable."; -"web_rename_invalid" = "Un nombre empieza con una letra y solo contiene letras, cifras y _."; -"web_rename_keyword_fmt" = "«%@» es una palabra clave de Java."; -"web_rename_apply" = "Renombrar"; -"web_rename_cancel" = "Cancelar"; diff --git a/web/src/i18n/fr.strings b/web/src/i18n/fr.strings deleted file mode 100644 index b0c86e4a..00000000 --- a/web/src/i18n/fr.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Textes propres à la version web. Tout le reste provient des Localizable.strings de l’app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Organigramme VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Suppr"; -"web_key_shift" = "Maj"; -"web_export" = "Exporter…"; -"web_export_title" = "Enregistrer comme image ou PDF, ou imprimer"; -"web_export_png" = "Image (PNG)"; -"web_export_svg" = "Graphique vectoriel (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Fichier VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Échec de l’exportation : %@"; -"web_print_hint" = "Utilisez « Exporter… → PDF » pour enregistrer le diagramme, puis imprimez-le."; -"web_delete" = "Supprimer"; -"web_language_title" = "Langue de l’interface"; -"web_simulation_start" = "Lancer la simulation"; -"web_simulation_end" = "Terminer la simulation"; -"web_simulation_start_title" = "Exécuter l’organigramme étape par étape"; -"web_simulation_end_title" = "Terminer la simulation et reprendre l’édition"; -"web_palette_hint" = "Glisser dans l’organigramme"; -"web_tabs_label" = "Diagrammes"; -"web_status_modified" = "modifié"; -"web_inspector_title" = "Propriétés"; -"web_inspector_resize" = "Modifier la hauteur de l’inspecteur d’objets : faire glisser la ligne vers le haut ou le bas (double-clic : par défaut)"; -"web_inspector_help_drop" = "Glissez des éléments de la palette sur une tuile : en haut = avant, en bas = après, sur « ø » = remplacer. Les éléments existants se déplacent à la souris."; -"web_inspector_help_inline" = "Double-cliquez ou touchez deux fois un élément pour modifier son texte directement dans le diagramme (Entrée valide, Échap annule). Idem pour les noms de cas."; -"web_inspector_help_files" = "Vous pouvez aussi déposer des fichiers dans la fenêtre."; -"web_case_name" = "Nom du cas"; -"web_case_name_placeholder" = "p. ex. 1, ok, default"; -"web_copied" = "Élément copié."; -"web_discard_message_fmt" = "Les modifications de « %@ » seront perdues."; -"web_err_unreadable_fmt" = "« %@ » n’est pas un fichier VisuStruct lisible (%@)."; -"web_saved_fmt" = "« %@ » enregistré."; -"web_saved_txt_fmt" = "Enregistré sous « %@ ». Supprimez l’extension .txt pour l’utiliser."; -"web_drop_impossible" = "Impossible de déposer ici"; -"web_sim_delay" = "Pause par étape"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Langage"; -"web_codegen_comments" = "Texte du diagramme en commentaires"; -"web_codegen_empty_else" = "Générer une branche else vide dans le code"; -"web_copied_check" = "Copié ✓"; -"web_copy_manual" = "Sélectionné, appuyez sur Ctrl+C pour copier"; -"web_run_stopped_loop" = "Exécution arrêtée : boucle infinie possible."; -"web_run_cancelled" = "Exécution annulée."; -"web_run_error" = "Erreur lors de l’exécution."; -"web_run_dropped_fmt" = "… %d lignes précédentes masquées"; -"web_sim_call_arguments_fmt" = "L’appel ne correspond pas à l’en-tête de %@. Vérifie les valeurs entre parenthèses."; -"web_sim_missing_return_fmt" = "%@ s’est terminé sans renvoyer de valeur avec return."; -"web_sim_file_not_found_fmt" = "Il n’existe aucun fichier nommé %@."; -"web_sim_end_of_file_fmt" = "Le fichier %@ n’a plus de ligne."; -"web_sim_null_value_fmt" = "%@ vaut null."; -"web_sim_unknown_variable_case_fmt" = "Variable inconnue : %1$@. Java distingue majuscules et minuscules : vouliez-vous dire %2$@ ?"; -"web_sim_written_file_fmt" = "%@ (enregistré)"; -"web_site_home" = "Accueil"; -"web_site_imprint" = "Mentions légales"; -"web_site_privacy" = "Confidentialité"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmation"; -"web_live_hide" = "Masquer la programmation"; -"web_scale_canvas" = "Taille du texte : structogramme"; -"web_scale_code" = "Taille du texte : programmation"; -"web_live_indent" = "Indenter"; -"web_live_indent_title" = "Indenter le code : quatre espaces par niveau (lignes sélectionnées, sinon tout)"; -"web_live_show" = "Afficher la programmation"; -"web_live_resize" = "Modifier la largeur de la zone de code (double-clic : réinitialiser)"; -"web_live_hint" = "Le code évolue avec ton diagramme. Gris = espace réservé : écris dans l’inspecteur et ton texte apparaît ici. Clique sur une ligne pour sélectionner son élément."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Ton code"; -"web_live_ph_end" = "fin"; -"web_live_language" = "Langage de programmation"; -"web_live_java_class" = "Comme classe"; -"web_live_java_class_hint" = "Nom de la classe = titre du diagramme"; -"web_live_java_main" = "Avec méthode main"; -"web_settings_display" = "Affichage"; -"web_live_swing" = "Vrai Java (avec Swing)"; -"web_live_swing_hint" = "Du vrai Java avec fenêtres (CheerpJ). Télécharge environ 40 Mo une seule fois, au premier lancement."; -"web_live_swing_windows" = "Fenêtres Java"; -"web_live_swing_hide" = "Fermer les fenêtres Java"; -"web_live_swing_loading" = "Chargement de Java (environ 40 Mo la première fois) …"; -"web_live_swing_compiling" = "Compilation …"; -"web_live_swing_running" = "Le programme s’exécute."; -"web_live_swing_compile_failed" = "Erreur de compilation, voir ci-dessus."; -"web_live_swing_failed" = "Impossible de charger Java. Vérifie la connexion Internet."; -"web_live_hint_edit" = "Tape ici du code Java et le diagramme se construit tout seul. Ou travaille dans le diagramme et le code suit. Gris = espace réservé, il suffit d’écrire par-dessus."; -"web_live_edit_label" = "Code Java"; -"web_parse_line_fmt" = "Ligne %d : %@"; -"web_parse_missing" = "Il manque encore ici : %@"; -"web_parse_extra" = "En trop : %@"; -"web_parse_unclosed_string" = "Le %@ de fermeture manque ici."; -"web_parse_else_without_if" = "Ce « else » n’a pas de « if »."; -"web_parse_case_outside_switch" = "« case » et « default » vont dans un « switch »."; -"web_parse_missing_do_while" = "Après « do { … } », il manque « while (condition); »."; -"web_parse_more_methods" = "Pour l’instant, une seule méthode compte ici. Crée les autres sous-programmes dans des onglets séparés."; -"web_parse_missing_import" = "Il manque %@ tout en haut. Sans cette ligne, Java ne connaît pas Scanner."; -"web_parse_missing_scanner" = "Il manque avant %@ Sans cette ligne, le Scanner n’existe pas."; -"web_parse_unknown_type" = "Java ne connaît pas ce type de données. Vouliez-vous dire %@ ?"; -"web_quickfix_insert_fmt" = "Suggestion : insérer %@"; -"web_quickfix_replace_fmt" = "Suggestion : %1$@ au lieu de %2$@"; -"web_parse_wrong_case" = "Java distingue majuscules et minuscules. La bonne écriture est %@."; -"web_editor_title" = "Diagramme"; -"web_editor_show" = "Afficher le diagramme"; -"web_editor_hide" = "Masquer le diagramme"; -"web_keywords_title" = "Mots-clés du code"; -"web_keywords_hint" = "if, while, for au lieu des noms des éléments"; -"web_palette_compact" = "Éléments toujours compacts"; -"web_palette_compact_hint" = "Symbole avec le nom en petit dessous, sinon seulement si la colonne est étroite"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Logiciels utilisés"; -"web_about_software_text" = "L’éditeur de code utilise CodeMirror et Lezer (licence MIT). Avec la prise en charge de Swing, VisuStruct charge CheerpJ de Leaning Technologies (licence Community) et javac d’OpenJDK."; -"web_about_licenses" = "Textes des licences"; -"web_live_run" = "Exécuter"; -"web_live_run_title" = "Exécuter le programme : les sorties et les entrées apparaissent en dessous"; -"web_live_run_finished" = "Programme terminé."; -"web_live_run_stopped" = "Arrêté."; -"web_console" = "Console"; -"web_console_hide" = "Masquer la console"; -"web_console_resize" = "Redimensionner la console"; -"web_diagram_tools" = "Outils du diagramme"; -"web_files_title" = "Fichiers"; -"web_files_show" = "Afficher les fichiers"; -"web_files_hide" = "Masquer les fichiers"; -"web_files_resize" = "Redimensionner les fichiers"; -"web_files_new_folder" = "Nouveau dossier"; -"web_files_switch" = "Choisir un autre dossier"; -"web_files_delete" = "Supprimer"; -"web_files_in_browser" = "Dans le stockage de ce navigateur"; -"web_files_save_hint_fmt" = "L’enregistrement place le programme dans %@."; -"web_files_empty" = "Rien d’enregistré pour l’instant. À l’enregistrement, VisuStruct crée ici un dossier à la date du jour."; -"web_files_reconnect_hint" = "Après un rechargement, le navigateur redemande si VisuStruct peut utiliser ton dossier."; -"web_files_reconnect_fmt" = "Ouvrir le dossier « %@ »"; -"web_files_connect_hint" = "Choisis un dossier comme espace de travail. VisuStruct y crée un dossier pour chaque jour et enregistre le diagramme et le code."; -"web_files_connect" = "Choisir un dossier"; -"web_files_browser_hint" = "Ce navigateur ne peut pas ouvrir de dossiers de l’appareil (par exemple sur iPad). Tes fichiers restent alors dans le stockage de ce navigateur."; -"web_files_browser" = "Enregistrer dans le navigateur"; -"web_files_delete_confirm_fmt" = "Supprimer vraiment « %@ » ?"; -"web_files_folder_prompt" = "Nom du nouveau dossier :"; -"web_files_default_name" = "Programme"; -"web_files_saved_fmt" = "Enregistré : %@"; -"web_files_create" = "Créer"; -"web_learn_ludwig_tip" = "Le conseil de Ludwig"; -"web_game_title" = "Jeu : Vrai ou faux ?"; -"web_game_intro" = "Ludwig te montre des valeurs et une condition. Est-elle vraie (true) ou fausse (false) ? Dix manches, de plus en plus difficiles."; -"web_game_start" = "Commencer le jeu"; -"web_game_round_fmt" = "Manche %@ sur %@"; -"web_game_score_fmt" = "Points : %@"; -"web_game_true" = "vrai (true)"; -"web_game_false" = "faux (false)"; -"web_game_right" = "Correct !"; -"web_game_wrong" = "Pas tout à fait. Voici le calcul :"; -"web_game_next" = "Suivant"; -"web_game_finish" = "Résultat"; -"web_game_done_fmt" = "%@ sur %@ correctes."; -"web_game_perfect" = "Tout juste – Ludwig est très impressionné !"; -"web_game_good" = "Bravo ! Regarde encore tes erreurs et réessaie."; -"web_game_practice" = "C’est en forgeant qu’on devient forgeron. Relis la table de vérité et rejoue une partie."; -"web_game_again" = "Rejouer"; -"web_game_wrong_short" = "Dommage, c'est faux."; -"web_game_practice_short" = "C'est en forgeant qu'on devient forgeron. Relis les explications et rejoue une partie."; -"web_game_clicks" = "Clics :"; -"web_game_nobody" = "Personne n'écoute – rien ne se passe."; -"web_game_event_fmt" = "Clic sur « %@ » : l'écouteur l'entend et exécute son code."; -"web_game_try" = "Essaie maintenant toi-même les boutons de la fenêtre."; -"web_game_time_fmt" = "Encore %@ s"; -"web_game_defused" = "✂️ Désamorcée ! Tu as trouvé l’erreur."; -"web_game_boom_time" = "💥 Boum ! Le temps est écoulé."; -"web_game_boom_wrong" = "💥 Boum ! Ce n’était pas la bonne ligne."; -"web_game_boom_part" = "💥 Boum ! Bonne ligne, mais l’erreur est dans une autre partie."; -"web_game_boom_wrong_part" = "💥 Boum ! L’erreur est dans une autre ligne."; -"web_game_bomb_parts_hint" = "Touche exactement la partie fausse."; -"web_game_fix" = "La ligne correcte :"; -"web_game_order_hint" = "Touche les étapes dans le bon ordre."; -"web_debug_step" = "Pas à pas"; -"web_debug_step_title" = "Pas à pas : exécuter une instruction et s’arrêter à nouveau"; -"web_debug_continue" = "Continuer jusqu’au prochain point d’arrêt"; -"web_debug_paused_fmt" = "Arrêté avant la ligne %d."; -"web_debug_gutter_title" = "Clique sur un numéro de ligne pour poser ou retirer un point d’arrêt"; -"web_swap_to_code" = "Programmation en grand"; -"web_swap_to_editor" = "Structogramme en grand"; -"web_editor_resize" = "Modifier la largeur du structogramme"; -"web_palette_resize" = "Modifier la largeur des éléments"; -"web_palette_hide" = "Masquer les éléments"; -"web_palette_show" = "Afficher les éléments"; -"web_code_scopes" = "Blocs de code en couleur"; -"web_code_scopes_hint" = "Classe, méthode, boucle, condition dans leurs propres cadres"; -"web_pseudocode" = "Pseudo-code"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Renommer la variable …"; -"web_rename_title" = "Renommer la variable"; -"web_rename_old" = "Nom"; -"web_rename_new" = "Nouveau nom"; -"web_rename_hint" = "Modifie chaque endroit avec exactement cette écriture (nombre n’est pas Nombre), dans le code et dans le structogramme. Le texte entre \"…\" et les commentaires restent."; -"web_rename_count_fmt" = "Apparaît %d fois."; -"web_rename_exists_fmt" = "« %@ » existe déjà : ensuite les deux seraient une seule variable."; -"web_rename_invalid" = "Un nom commence par une lettre et ne contient que des lettres, des chiffres et _."; -"web_rename_keyword_fmt" = "« %@ » est un mot-clé Java."; -"web_rename_apply" = "Renommer"; -"web_rename_cancel" = "Annuler"; diff --git a/web/src/i18n/he.strings b/web/src/i18n/he.strings deleted file mode 100644 index 00295d53..00000000 --- a/web/src/i18n/he.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* טקסטים שקיימים רק בגרסת הרשת. כל השאר מגיעים מקובץ Localizable.strings של האפליקציה. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "תרשים VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "ייצוא…"; -"web_export_title" = "שמירה כתמונה או כ‑PDF, או הדפסה"; -"web_export_png" = "תמונה (PNG)"; -"web_export_svg" = "גרפיקה וקטורית (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "קובץ VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "הייצוא נכשל: %@"; -"web_print_hint" = "השתמשו ב„ייצוא… → PDF” כדי לשמור את התרשים ולהדפיס אותו משם."; -"web_delete" = "מחיקה"; -"web_language_title" = "שפת הממשק"; -"web_simulation_start" = "התחלת סימולציה"; -"web_simulation_end" = "סיום סימולציה"; -"web_simulation_start_title" = "הרצת התרשים צעד אחר צעד"; -"web_simulation_end_title" = "סיום הסימולציה והמשך עריכה"; -"web_palette_hint" = "גררו לתוך התרשים"; -"web_tabs_label" = "תרשימים"; -"web_status_modified" = "שונה"; -"web_inspector_title" = "מאפיינים"; -"web_inspector_resize" = "שינוי גובה מפקח האובייקטים: גרור את הקו למעלה או למטה (לחיצה כפולה: ברירת מחדל)"; -"web_inspector_help_drop" = "גררו רכיבים מהלוח אל אריח: למעלה = לפני, למטה = אחרי, על „ø” = החלפה. ניתן להזיז רכיבים קיימים בעזרת העכבר."; -"web_inspector_help_inline" = "לחיצה כפולה או הקשה כפולה על רכיב לעריכת הטקסט ישירות בתרשים (Enter להחלה, Esc לביטול). כך גם שמות המקרים."; -"web_inspector_help_files" = "אפשר גם לגרור קבצים לתוך החלון."; -"web_case_name" = "שם המקרה"; -"web_case_name_placeholder" = "למשל 1, ok, default"; -"web_copied" = "הרכיב הועתק."; -"web_discard_message_fmt" = "השינויים ב„%@” יאבדו."; -"web_err_unreadable_fmt" = "„%@” אינו קובץ VisuStruct קריא (%@)."; -"web_saved_fmt" = "„%@” נשמר."; -"web_saved_txt_fmt" = "נשמר בשם „%@”. הסירו את הסיומת .txt כדי להשתמש בו."; -"web_drop_impossible" = "לא ניתן לשחרר כאן"; -"web_sim_delay" = "השהיה לכל צעד"; -"web_sim_delay_value_fmt" = "%@ שנ׳"; -"web_codegen_language" = "שפה"; -"web_codegen_comments" = "טקסט התרשים כהערות"; -"web_codegen_empty_else" = "יצירת ענף else ריק בקוד"; -"web_copied_check" = "הועתק ✓"; -"web_copy_manual" = "נבחר, הקישו Ctrl+C להעתקה"; -"web_run_stopped_loop" = "ההרצה נעצרה: ייתכן שיש לולאה אינסופית."; -"web_run_cancelled" = "ההרצה בוטלה."; -"web_run_error" = "שגיאה בזמן ההרצה."; -"web_run_dropped_fmt" = "… %d שורות קודמות מוסתרות"; -"web_sim_call_arguments_fmt" = "הקריאה אינה תואמת את הכותרת של %@. בדוק את הערכים בסוגריים."; -"web_sim_missing_return_fmt" = "%@ הסתיים בלי להחזיר ערך עם return."; -"web_sim_file_not_found_fmt" = "אין קובץ בשם %@."; -"web_sim_end_of_file_fmt" = "בקובץ %@ אין עוד שורות."; -"web_sim_null_value_fmt" = "%@ הוא null."; -"web_sim_unknown_variable_case_fmt" = "משתנה לא מוכר: %1$@. Java מבחינה בין אותיות גדולות לקטנות: האם התכוונת ל־%2$@?"; -"web_sim_written_file_fmt" = "%@ (נשמר)"; -"web_site_home" = "דף הבית"; -"web_site_imprint" = "פרטי המפרסם"; -"web_site_privacy" = "פרטיות"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "תכנות"; -"web_live_hide" = "הסתרת תכנות"; -"web_scale_canvas" = "גודל טקסט: תרשים"; -"web_scale_code" = "גודל טקסט: תכנות"; -"web_live_indent" = "הזחה"; -"web_live_indent_title" = "הזחת הקוד: ארבעה רווחים לכל רמה (השורות המסומנות, אחרת הכול)"; -"web_live_show" = "הצגת תכנות"; -"web_live_resize" = "שינוי רוחב אזור הקוד (לחיצה כפולה: איפוס)"; -"web_live_hint" = "הקוד גדל יחד עם התרשים. אפור = ממלא מקום: כתבו במפקח והטקסט יופיע כאן. לחצו על שורה כדי לבחור את הרכיב."; -"web_live_ph_selector" = "משתנה"; -"web_live_ph_code" = "הקוד שלך"; -"web_live_ph_end" = "סוף"; -"web_live_language" = "שפת תכנות"; -"web_live_java_class" = "כמחלקה"; -"web_live_java_class_hint" = "שם המחלקה = כותרת התרשים"; -"web_live_java_main" = "עם מתודת main"; -"web_settings_display" = "תצוגה"; -"web_live_swing" = "Java אמיתית (עם Swing)"; -"web_live_swing_hint" = "ג׳אווה אמיתית עם חלונות (CheerpJ). מורידה פעם אחת כ-40 MB בהרצה הראשונה."; -"web_live_swing_windows" = "חלונות ג׳אווה"; -"web_live_swing_hide" = "סגירת חלונות ג׳אווה"; -"web_live_swing_loading" = "טוען ג׳אווה (בפעם הראשונה כ-40 MB) …"; -"web_live_swing_compiling" = "מהדר …"; -"web_live_swing_running" = "התוכנית רצה."; -"web_live_swing_compile_failed" = "שגיאת הידור, ראה למעלה."; -"web_live_swing_failed" = "לא ניתן לטעון את ג׳אווה. בדוק את החיבור לאינטרנט."; -"web_live_hint_edit" = "הקלד כאן קוד Java והתרשים נבנה בעצמו. או עבוד בתרשים והקוד יגדל איתו. אפור = ממלא מקום, פשוט כתוב מעליו."; -"web_live_edit_label" = "קוד Java"; -"web_parse_line_fmt" = "שורה %d: %@"; -"web_parse_missing" = "עדיין חסר כאן: %@"; -"web_parse_extra" = "מיותר: %@"; -"web_parse_unclosed_string" = "חסר כאן ה־%@ הסוגר."; -"web_parse_else_without_if" = "ל־“else” הזה אין “if”."; -"web_parse_case_outside_switch" = "“case” ו־“default” שייכים בתוך “switch”."; -"web_parse_missing_do_while" = "אחרי “do { … }” עדיין חסר “while (תנאי);”."; -"web_parse_more_methods" = "כרגע נחשבת כאן רק מתודה אחת. צור תוכניות משנה נוספות בכרטיסיות נפרדות."; -"web_parse_missing_import" = "חסר בראש הקוד %@ בלי השורה הזו Java לא מכירה את Scanner."; -"web_parse_missing_scanner" = "חסר קודם %@ בלי השורה הזו אין Scanner."; -"web_parse_unknown_type" = "Java לא מכירה את סוג הנתונים הזה. האם התכוונת ל-%@?"; -"web_quickfix_insert_fmt" = "הצעה: להוסיף %@"; -"web_quickfix_replace_fmt" = "הצעה: %1$@ במקום %2$@"; -"web_parse_wrong_case" = "Java מבחינה בין אותיות גדולות לקטנות. הנכון הוא %@."; -"web_editor_title" = "תרשים"; -"web_editor_show" = "הצגת התרשים"; -"web_editor_hide" = "הסתרת התרשים"; -"web_keywords_title" = "מילות מפתח של קוד"; -"web_keywords_hint" = "if, while, for במקום שמות הרכיבים"; -"web_palette_compact" = "רכיבים תמיד בתצוגה מצומצמת"; -"web_palette_compact_hint" = "סמל עם שם קטן מתחתיו, אחרת רק כשהעמודה צרה"; -"web_about_version_fmt" = "גרסה %@"; -"web_about_software" = "תוכנה בשימוש"; -"web_about_software_text" = "עורך הקוד משתמש ב-CodeMirror וב-Lezer (רישיון MIT). עם תמיכה ב-Swing, VisuStruct טוען את CheerpJ של Leaning Technologies (רישיון Community) ואת javac מ-OpenJDK."; -"web_about_licenses" = "נוסחי הרישיונות"; -"web_live_run" = "הרצה"; -"web_live_run_title" = "הרצת התוכנית: הפלט והקלט מופיעים למטה"; -"web_live_run_finished" = "התוכנית הסתיימה."; -"web_live_run_stopped" = "נעצר."; -"web_console" = "מסוף"; -"web_console_hide" = "הסתרת המסוף"; -"web_console_resize" = "שינוי גובה המסוף"; -"web_diagram_tools" = "כלי התרשים"; -"web_files_title" = "קבצים"; -"web_files_show" = "הצגת קבצים"; -"web_files_hide" = "הסתרת קבצים"; -"web_files_resize" = "שינוי רוחב הקבצים"; -"web_files_new_folder" = "תיקייה חדשה"; -"web_files_switch" = "בחירת תיקייה אחרת"; -"web_files_delete" = "מחיקה"; -"web_files_in_browser" = "באחסון של דפדפן זה"; -"web_files_save_hint_fmt" = "השמירה מכניסה את התוכנית אל %@."; -"web_files_empty" = "עדיין לא נשמר דבר. בשמירה VisuStruct יוצר כאן תיקייה עם התאריך של היום."; -"web_files_reconnect_hint" = "אחרי טעינה מחדש הדפדפן שואל שוב אם VisuStruct רשאי להשתמש בתיקייה."; -"web_files_reconnect_fmt" = "פתיחת התיקייה „%@“"; -"web_files_connect_hint" = "בחרו תיקייה כסביבת עבודה. VisuStruct יוצר בה תיקייה לכל יום ושומר את התרשים ואת הקוד."; -"web_files_connect" = "בחירת תיקייה"; -"web_files_browser_hint" = "דפדפן זה אינו יכול לפתוח תיקיות במכשיר (למשל ב-iPad). הקבצים נשארים באחסון של הדפדפן."; -"web_files_browser" = "שמירה בדפדפן"; -"web_files_delete_confirm_fmt" = "למחוק את „%@“?"; -"web_files_folder_prompt" = "שם התיקייה החדשה:"; -"web_files_default_name" = "תוכנית"; -"web_files_saved_fmt" = "נשמר: %@"; -"web_files_create" = "יצירה"; -"web_learn_ludwig_tip" = "הטיפ של לודוויג"; -"web_game_title" = "משחק: אמת או שקר?"; -"web_game_intro" = "לודוויג מראה ערכים ותנאי. האם התנאי אמת (true) או שקר (false)? עשרה סבבים, כל פעם קשה יותר."; -"web_game_start" = "התחלת המשחק"; -"web_game_round_fmt" = "סבב %@ מתוך %@"; -"web_game_score_fmt" = "נקודות: %@"; -"web_game_true" = "אמת (true)"; -"web_game_false" = "שקר (false)"; -"web_game_right" = "נכון!"; -"web_game_wrong" = "לא בדיוק. כך מחשבים:"; -"web_game_next" = "הבא"; -"web_game_finish" = "תוצאה"; -"web_game_done_fmt" = "%@ מתוך %@ נכונות."; -"web_game_perfect" = "הכול נכון – לודוויג מתרשם מאוד!"; -"web_game_good" = "מצוין! עברו שוב על הטעויות ונסו שוב."; -"web_game_practice" = "תרגול עושה מושלם. קראו שוב את טבלת האמת ושחקו עוד סבב."; -"web_game_again" = "לשחק שוב"; -"web_game_wrong_short" = "לצערי לא נכון."; -"web_game_practice_short" = "תרגול מביא לשלמות. קרא שוב את ההסברים ושחק סיבוב נוסף."; -"web_game_clicks" = "לחיצות:"; -"web_game_nobody" = "אף אחד לא מאזין – שום דבר לא קורה."; -"web_game_event_fmt" = "לחיצה על „%@”: המאזין שומע ומריץ את הקוד שלו."; -"web_game_try" = "עכשיו נסה בעצמך את הכפתורים בחלון."; -"web_game_time_fmt" = "נותרו %@ שנ׳"; -"web_game_defused" = "✂️ נוטרלה! מצאת את הטעות."; -"web_game_boom_time" = "💥 בום! הזמן נגמר."; -"web_game_boom_wrong" = "💥 בום! זו הייתה השורה הלא נכונה."; -"web_game_boom_part" = "💥 בום! השורה נכונה, אבל הטעות בחלק אחר שלה."; -"web_game_boom_wrong_part" = "💥 בום! הטעות בשורה אחרת."; -"web_game_bomb_parts_hint" = "הקש בדיוק על החלק השגוי."; -"web_game_fix" = "השורה הנכונה:"; -"web_game_order_hint" = "הקש על השלבים בסדר הנכון."; -"web_debug_step" = "צעד"; -"web_debug_step_title" = "צעד: להריץ הוראה אחת ולעצור שוב"; -"web_debug_continue" = "להמשיך עד נקודת העצירה הבאה"; -"web_debug_paused_fmt" = "נעצר לפני שורה %d."; -"web_debug_gutter_title" = "לחיצה על מספר שורה מוסיפה או מסירה נקודת עצירה"; -"web_swap_to_code" = "תכנות גדול"; -"web_swap_to_editor" = "תרשים גדול"; -"web_editor_resize" = "שינוי רוחב התרשים"; -"web_palette_resize" = "שינוי רוחב הרכיבים"; -"web_palette_hide" = "הסתרת הרכיבים"; -"web_palette_show" = "הצגת הרכיבים"; -"web_code_scopes" = "בלוקי קוד צבעוניים"; -"web_code_scopes_hint" = "מחלקה, מתודה, לולאה, הסתעפות בתיבות משלהן"; -"web_pseudocode" = "פסאודו-קוד"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "שינוי שם המשתנה …"; -"web_rename_title" = "שינוי שם המשתנה"; -"web_rename_old" = "שם"; -"web_rename_new" = "שם חדש"; -"web_rename_hint" = "משנה כל מקום עם בדיוק הכתיב הזה (number אינו Number), בקוד ובתרשים המבנה. טקסט בתוך \"…\" והערות נשארים."; -"web_rename_count_fmt" = "מופיע %d פעמים."; -"web_rename_exists_fmt" = "„%@” כבר קיים: אחר כך שניהם יהיו משתנה אחד."; -"web_rename_invalid" = "שם מתחיל באות ומכיל רק אותיות, ספרות ו־_."; -"web_rename_keyword_fmt" = "„%@” היא מילת מפתח של Java."; -"web_rename_apply" = "שינוי שם"; -"web_rename_cancel" = "ביטול"; diff --git a/web/src/i18n/it.strings b/web/src/i18n/it.strings deleted file mode 100644 index 7bf030d6..00000000 --- a/web/src/i18n/it.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Testi presenti solo nella versione web. Tutto il resto proviene dai Localizable.strings dell’app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagramma VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Canc"; -"web_key_shift" = "Maiusc"; -"web_export" = "Esporta…"; -"web_export_title" = "Salva come immagine o PDF, oppure stampa"; -"web_export_png" = "Immagine (PNG)"; -"web_export_svg" = "Grafica vettoriale (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "File VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Esportazione non riuscita: %@"; -"web_print_hint" = "Usa “Esporta… → PDF” per salvare il diagramma e stamparlo da lì."; -"web_delete" = "Elimina"; -"web_language_title" = "Lingua dell’interfaccia"; -"web_simulation_start" = "Avvia simulazione"; -"web_simulation_end" = "Termina simulazione"; -"web_simulation_start_title" = "Esegui il diagramma passo dopo passo"; -"web_simulation_end_title" = "Termina la simulazione e continua a modificare"; -"web_palette_hint" = "Trascina nel diagramma"; -"web_tabs_label" = "Diagrammi"; -"web_status_modified" = "modificato"; -"web_inspector_title" = "Proprietà"; -"web_inspector_resize" = "Cambia l’altezza dell’ispettore oggetti: trascina o scorri la linea su o giù (doppio clic: predefinito)"; -"web_inspector_help_drop" = "Trascina gli elementi dalla tavolozza su un riquadro: in alto = prima, in basso = dopo, su “ø” = sostituisci. Gli elementi esistenti si possono spostare con il mouse."; -"web_inspector_help_inline" = "Doppio clic o doppio tocco su un elemento per modificarne il testo direttamente nel diagramma (Invio applica, Esc annulla). Anche i nomi dei casi."; -"web_inspector_help_files" = "Puoi anche trascinare file nella finestra."; -"web_case_name" = "Nome del caso"; -"web_case_name_placeholder" = "es. 1, ok, default"; -"web_copied" = "Elemento copiato."; -"web_discard_message_fmt" = "Le modifiche a “%@” andranno perse."; -"web_err_unreadable_fmt" = "“%@” non è un file VisuStruct leggibile (%@)."; -"web_saved_fmt" = "“%@” salvato."; -"web_saved_txt_fmt" = "Salvato come “%@”. Rimuovi l’estensione .txt per usarlo."; -"web_drop_impossible" = "Impossibile rilasciare qui"; -"web_sim_delay" = "Pausa per passo"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Linguaggio"; -"web_codegen_comments" = "Testo del diagramma come commenti"; -"web_codegen_empty_else" = "Genera ramo else vuoto nel codice"; -"web_copied_check" = "Copiato ✓"; -"web_copy_manual" = "Selezionato, premi Ctrl+C per copiare"; -"web_run_stopped_loop" = "Esecuzione interrotta: possibile ciclo infinito."; -"web_run_cancelled" = "Esecuzione annullata."; -"web_run_error" = "Errore durante l’esecuzione."; -"web_run_dropped_fmt" = "… %d righe precedenti nascoste"; -"web_sim_call_arguments_fmt" = "La chiamata non corrisponde all’intestazione di %@. Controlla i valori tra parentesi."; -"web_sim_missing_return_fmt" = "%@ è terminato senza restituire un valore con return."; -"web_sim_file_not_found_fmt" = "Non esiste nessun file chiamato %@."; -"web_sim_end_of_file_fmt" = "Il file %@ non ha più righe."; -"web_sim_null_value_fmt" = "%@ è null."; -"web_sim_unknown_variable_case_fmt" = "Variabile sconosciuta: %1$@. Java distingue tra maiuscole e minuscole: intendevi %2$@?"; -"web_sim_written_file_fmt" = "%@ (salvato)"; -"web_site_home" = "Pagina iniziale"; -"web_site_imprint" = "Note legali"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmazione"; -"web_live_hide" = "Nascondi programmazione"; -"web_scale_canvas" = "Dimensione testo: diagramma"; -"web_scale_code" = "Dimensione testo: programmazione"; -"web_live_indent" = "Indenta"; -"web_live_indent_title" = "Indenta il codice: quattro spazi per livello (righe selezionate, altrimenti tutto)"; -"web_live_show" = "Mostra programmazione"; -"web_live_resize" = "Modifica la larghezza dell’area codice (doppio clic: ripristina)"; -"web_live_hint" = "Il codice cresce con il tuo diagramma. Grigio = segnaposto: scrivi nell’ispettore e il tuo testo appare qui. Clicca su una riga per selezionarne l’elemento."; -"web_live_ph_selector" = "variabile"; -"web_live_ph_code" = "Il tuo codice"; -"web_live_ph_end" = "fine"; -"web_live_language" = "Linguaggio di programmazione"; -"web_live_java_class" = "Come classe"; -"web_live_java_class_hint" = "Nome della classe = titolo del diagramma"; -"web_live_java_main" = "Con metodo main"; -"web_settings_display" = "Visualizzazione"; -"web_live_swing" = "Java vero (con Swing)"; -"web_live_swing_hint" = "Java vero con finestre (CheerpJ). Scarica circa 40 MB una sola volta, alla prima esecuzione."; -"web_live_swing_windows" = "Finestre Java"; -"web_live_swing_hide" = "Chiudi finestre Java"; -"web_live_swing_loading" = "Caricamento di Java (circa 40 MB la prima volta) …"; -"web_live_swing_compiling" = "Compilazione …"; -"web_live_swing_running" = "Il programma è in esecuzione."; -"web_live_swing_compile_failed" = "Errore di compilazione, vedi sopra."; -"web_live_swing_failed" = "Impossibile caricare Java. Controlla la connessione a internet."; -"web_live_hint_edit" = "Scrivi qui codice Java e il diagramma si costruisce da solo. Oppure lavora nel diagramma e il codice cresce con lui. Grigio = segnaposto, basta scriverci sopra."; -"web_live_edit_label" = "Codice Java"; -"web_parse_line_fmt" = "Riga %d: %@"; -"web_parse_missing" = "Qui manca ancora: %@"; -"web_parse_extra" = "Di troppo: %@"; -"web_parse_unclosed_string" = "Manca il %@ di chiusura."; -"web_parse_else_without_if" = "Questo «else» non ha un «if»."; -"web_parse_case_outside_switch" = "«case» e «default» vanno dentro uno «switch»."; -"web_parse_missing_do_while" = "Dopo «do { … }» manca ancora «while (condizione);»."; -"web_parse_more_methods" = "Per ora qui conta un solo metodo. Crea gli altri sottoprogrammi in schede separate."; -"web_parse_missing_import" = "Manca %@ in cima. Senza questa riga Java non conosce Scanner."; -"web_parse_missing_scanner" = "Manca prima %@ Senza questa riga lo Scanner non esiste."; -"web_parse_unknown_type" = "Java non conosce questo tipo di dato. Intendevi %@?"; -"web_quickfix_insert_fmt" = "Suggerimento: inserisci %@"; -"web_quickfix_replace_fmt" = "Suggerimento: %1$@ invece di %2$@"; -"web_parse_wrong_case" = "Java distingue tra maiuscole e minuscole. La forma corretta è %@."; -"web_editor_title" = "Diagramma"; -"web_editor_show" = "Mostra diagramma"; -"web_editor_hide" = "Nascondi diagramma"; -"web_keywords_title" = "Parole chiave del codice"; -"web_keywords_hint" = "if, while, for al posto dei nomi degli elementi"; -"web_palette_compact" = "Elementi sempre compatti"; -"web_palette_compact_hint" = "Simbolo con il nome piccolo sotto, altrimenti solo con la colonna stretta"; -"web_about_version_fmt" = "Versione %@"; -"web_about_software" = "Software utilizzato"; -"web_about_software_text" = "L’editor di codice usa CodeMirror e Lezer (licenza MIT). Con il supporto Swing, VisuStruct carica CheerpJ di Leaning Technologies (licenza Community) e javac di OpenJDK."; -"web_about_licenses" = "Testi delle licenze"; -"web_live_run" = "Esegui"; -"web_live_run_title" = "Esegui il programma: output e input compaiono sotto"; -"web_live_run_finished" = "Programma terminato."; -"web_live_run_stopped" = "Fermato."; -"web_console" = "Console"; -"web_console_hide" = "Nascondi console"; -"web_console_resize" = "Ridimensiona la console"; -"web_diagram_tools" = "Strumenti del diagramma"; -"web_files_title" = "File"; -"web_files_show" = "Mostra file"; -"web_files_hide" = "Nascondi file"; -"web_files_resize" = "Ridimensiona file"; -"web_files_new_folder" = "Nuova cartella"; -"web_files_switch" = "Scegli un’altra cartella"; -"web_files_delete" = "Elimina"; -"web_files_in_browser" = "Nella memoria di questo browser"; -"web_files_save_hint_fmt" = "Salvando, il programma va in %@."; -"web_files_empty" = "Ancora niente di salvato. Quando salvi, VisuStruct crea qui una cartella con la data di oggi."; -"web_files_reconnect_hint" = "Dopo il ricaricamento il browser chiede di nuovo se VisuStruct può usare la tua cartella."; -"web_files_reconnect_fmt" = "Apri la cartella «%@»"; -"web_files_connect_hint" = "Scegli una cartella come spazio di lavoro. VisuStruct vi crea una cartella per ogni giorno e salva diagramma e codice."; -"web_files_connect" = "Scegli cartella"; -"web_files_browser_hint" = "Questo browser non può aprire cartelle del dispositivo (per esempio su iPad). I tuoi file restano nella memoria di questo browser."; -"web_files_browser" = "Salva nel browser"; -"web_files_delete_confirm_fmt" = "Eliminare davvero «%@»?"; -"web_files_folder_prompt" = "Nome della nuova cartella:"; -"web_files_default_name" = "Programma"; -"web_files_saved_fmt" = "Salvato: %@"; -"web_files_create" = "Crea"; -"web_learn_ludwig_tip" = "Il consiglio di Ludwig"; -"web_game_title" = "Gioco: Vero o falso?"; -"web_game_intro" = "Ludwig ti mostra dei valori e una condizione. È vera (true) o falsa (false)? Dieci turni, sempre più difficili."; -"web_game_start" = "Inizia il gioco"; -"web_game_round_fmt" = "Turno %@ di %@"; -"web_game_score_fmt" = "Punti: %@"; -"web_game_true" = "vero (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "Giusto!"; -"web_game_wrong" = "Non proprio. Ecco il calcolo:"; -"web_game_next" = "Avanti"; -"web_game_finish" = "Risultato"; -"web_game_done_fmt" = "%@ su %@ giuste."; -"web_game_perfect" = "Tutto giusto – Ludwig è davvero colpito!"; -"web_game_good" = "Ottimo! Riguarda gli errori e riprova."; -"web_game_practice" = "La pratica rende perfetti. Rileggi la tabella di verità e gioca un altro turno."; -"web_game_again" = "Gioca ancora"; -"web_game_wrong_short" = "Peccato, è sbagliato."; -"web_game_practice_short" = "La pratica rende perfetti. Rileggi le spiegazioni e gioca un altro turno."; -"web_game_clicks" = "Clic:"; -"web_game_nobody" = "Nessuno ascolta: non succede nulla."; -"web_game_event_fmt" = "Clic su «%@»: l'ascoltatore lo sente ed esegue il suo codice."; -"web_game_try" = "Ora prova tu i pulsanti della finestra."; -"web_game_time_fmt" = "Ancora %@ s"; -"web_game_defused" = "✂️ Disinnescata! Hai trovato l’errore."; -"web_game_boom_time" = "💥 Bum! Il tempo è scaduto."; -"web_game_boom_wrong" = "💥 Bum! Era la riga sbagliata."; -"web_game_boom_part" = "💥 Bum! La riga è giusta, ma l’errore è in un’altra parte."; -"web_game_boom_wrong_part" = "💥 Bum! L’errore è in un’altra riga."; -"web_game_bomb_parts_hint" = "Tocca esattamente la parte sbagliata."; -"web_game_fix" = "La riga corretta:"; -"web_game_order_hint" = "Tocca i passaggi nell’ordine giusto."; -"web_debug_step" = "Passo singolo"; -"web_debug_step_title" = "Passo singolo: esegui un’istruzione e fermati di nuovo"; -"web_debug_continue" = "Continua fino al prossimo punto di interruzione"; -"web_debug_paused_fmt" = "Fermo prima della riga %d."; -"web_debug_gutter_title" = "Clicca sul numero di riga per mettere o togliere un punto di interruzione"; -"web_swap_to_code" = "Programmazione grande"; -"web_swap_to_editor" = "Diagramma grande"; -"web_editor_resize" = "Modifica la larghezza del diagramma"; -"web_palette_resize" = "Modifica la larghezza degli elementi"; -"web_palette_hide" = "Nascondi elementi"; -"web_palette_show" = "Mostra elementi"; -"web_code_scopes" = "Blocchi di codice colorati"; -"web_code_scopes_hint" = "Classe, metodo, ciclo, selezione in riquadri propri"; -"web_pseudocode" = "Pseudocodice"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Rinomina variabile …"; -"web_rename_title" = "Rinomina variabile"; -"web_rename_old" = "Nome"; -"web_rename_new" = "Nuovo nome"; -"web_rename_hint" = "Cambia ogni punto con esattamente questa scrittura (numero non è Numero), nel codice e nello strutturogramma. Il testo tra \"…\" e i commenti restano."; -"web_rename_count_fmt" = "Compare %d volte."; -"web_rename_exists_fmt" = "«%@» esiste già: dopo le due sarebbero una sola variabile."; -"web_rename_invalid" = "Un nome inizia con una lettera e contiene solo lettere, cifre e _."; -"web_rename_keyword_fmt" = "«%@» è una parola chiave di Java."; -"web_rename_apply" = "Rinomina"; -"web_rename_cancel" = "Annulla"; diff --git a/web/src/i18n/ja.strings b/web/src/i18n/ja.strings deleted file mode 100644 index 71bb54c7..00000000 --- a/web/src/i18n/ja.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Web 版にのみ存在するテキスト。その他はすべてアプリの Localizable.strings から取得されます。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 構造図"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Delete"; -"web_key_shift" = "Shift"; -"web_export" = "エクスポート…"; -"web_export_title" = "画像または PDF として保存、または印刷"; -"web_export_png" = "画像(PNG)"; -"web_export_svg" = "ベクター画像(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct ファイル(.visustruct)"; -"web_export_failed_fmt" = "エクスポートに失敗しました:%@"; -"web_print_hint" = "「エクスポート… → PDF」で図を保存し、そこから印刷してください。"; -"web_delete" = "削除"; -"web_language_title" = "表示言語"; -"web_simulation_start" = "シミュレーション開始"; -"web_simulation_end" = "シミュレーション終了"; -"web_simulation_start_title" = "構造図をステップごとに実行"; -"web_simulation_end_title" = "シミュレーションを終了して編集を続ける"; -"web_palette_hint" = "構造図にドラッグ"; -"web_tabs_label" = "図"; -"web_status_modified" = "変更あり"; -"web_inspector_title" = "プロパティ"; -"web_inspector_resize" = "オブジェクトインスペクターの高さを変更:線を上下にドラッグまたはスワイプ(ダブルクリック:標準)"; -"web_inspector_help_drop" = "パレットの要素をタイルにドラッグ:上 = 前に、下 = 後に、「ø」の上 = 置き換え。既存の要素はマウスで移動できます。"; -"web_inspector_help_inline" = "要素をダブルクリック/ダブルタップすると、図の中で直接テキストを編集できます(Enter で確定、Esc で取消)。case 名も同様です。"; -"web_inspector_help_files" = "ファイルをウインドウにドロップすることもできます。"; -"web_case_name" = "ケース名"; -"web_case_name_placeholder" = "例:1, ok, default"; -"web_copied" = "要素をコピーしました。"; -"web_discard_message_fmt" = "「%@」への変更は失われます。"; -"web_err_unreadable_fmt" = "「%@」は読み込める VisuStruct ファイルではありません(%@)。"; -"web_saved_fmt" = "「%@」を保存しました。"; -"web_saved_txt_fmt" = "「%@」として保存しました。使用するには拡張子 .txt を削除してください。"; -"web_drop_impossible" = "ここにはドロップできません"; -"web_sim_delay" = "ステップごとの待ち時間"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "言語"; -"web_codegen_comments" = "図のテキストをコメントとして出力"; -"web_codegen_empty_else" = "空の else 分岐もコードに出力"; -"web_copied_check" = "コピーしました ✓"; -"web_copy_manual" = "選択済み。Ctrl+C でコピーしてください"; -"web_run_stopped_loop" = "実行を停止しました:無限ループの可能性があります。"; -"web_run_cancelled" = "実行を中止しました。"; -"web_run_error" = "実行中にエラーが発生しました。"; -"web_run_dropped_fmt" = "… 以前の %d 行は非表示"; -"web_sim_call_arguments_fmt" = "呼び出しが %@ の見出しと一致しません。かっこ内の値を確認してください。"; -"web_sim_missing_return_fmt" = "%@ は return で値を返さずに終了しました。"; -"web_sim_file_not_found_fmt" = "%@ という名前のファイルはありません。"; -"web_sim_end_of_file_fmt" = "ファイル %@ にはもう行がありません。"; -"web_sim_null_value_fmt" = "%@ は null です。"; -"web_sim_unknown_variable_case_fmt" = "不明な変数: %1$@。Java は大文字と小文字を区別します。%2$@ のことですか?"; -"web_sim_written_file_fmt" = "%@(保存済み)"; -"web_site_home" = "ホームページ"; -"web_site_imprint" = "運営者情報"; -"web_site_privacy" = "プライバシー"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "プログラミング"; -"web_live_hide" = "プログラミングを隠す"; -"web_scale_canvas" = "文字サイズ: 構造化チャート"; -"web_scale_code" = "文字サイズ: プログラミング"; -"web_live_indent" = "インデント"; -"web_live_indent_title" = "コードをインデント: 1 レベルにつき空白 4 つ(選択した行、なければ全体)"; -"web_live_show" = "プログラミングを表示"; -"web_live_resize" = "コード領域の幅を変更(ダブルクリック:リセット)"; -"web_live_hint" = "コードは図と一緒に育ちます。灰色=プレースホルダー:インスペクタに入力するとここに表示されます。行をクリックすると要素を選択します。"; -"web_live_ph_selector" = "変数"; -"web_live_ph_code" = "ここにコード"; -"web_live_ph_end" = "終了"; -"web_live_language" = "プログラミング言語"; -"web_live_java_class" = "クラスとして"; -"web_live_java_class_hint" = "クラス名 = 図のタイトル"; -"web_live_java_main" = "main メソッド付き"; -"web_settings_display" = "表示"; -"web_live_swing" = "本物の Java(Swing 対応)"; -"web_live_swing_hint" = "ウィンドウ付きの本物の Java(CheerpJ)。初回実行時に一度だけ約 40 MB をダウンロードします。"; -"web_live_swing_windows" = "Java ウィンドウ"; -"web_live_swing_hide" = "Java ウィンドウを閉じる"; -"web_live_swing_loading" = "Java を読み込み中(初回は約 40 MB)…"; -"web_live_swing_compiling" = "コンパイル中…"; -"web_live_swing_running" = "プログラム実行中。"; -"web_live_swing_compile_failed" = "コンパイルエラー。上を見てください。"; -"web_live_swing_failed" = "Java を読み込めませんでした。インターネット接続を確認してください。"; -"web_live_hint_edit" = "ここに Java コードを入力すると、図が一緒にできあがります。図で作業すると、コードも一緒に育ちます。灰色=プレースホルダー、そのまま上書きできます。"; -"web_live_edit_label" = "Java コード"; -"web_parse_line_fmt" = "%d 行目: %@"; -"web_parse_missing" = "ここにまだ足りません: %@"; -"web_parse_extra" = "多すぎます: %@"; -"web_parse_unclosed_string" = "閉じる %@ がありません。"; -"web_parse_else_without_if" = "この「else」に対応する「if」がありません。"; -"web_parse_case_outside_switch" = "「case」と「default」は「switch」の中に書きます。"; -"web_parse_missing_do_while" = "「do { … }」の後に「while (条件);」が必要です。"; -"web_parse_more_methods" = "今はここでメソッドを 1 つだけ使います。ほかのサブプログラムは別のタブに作ってください。"; -"web_parse_missing_import" = "先頭に %@ がありません。この行がないと Java は Scanner を知りません。"; -"web_parse_missing_scanner" = "先に %@ がありません。この行がないと Scanner はありません。"; -"web_parse_unknown_type" = "Java はこのデータ型を知りません。%@ のことですか?"; -"web_quickfix_insert_fmt" = "提案: %@ を挿入"; -"web_quickfix_replace_fmt" = "提案: %2$@ の代わりに %1$@"; -"web_parse_wrong_case" = "Java は大文字と小文字を区別します。正しくは %@ です。"; -"web_editor_title" = "構造図"; -"web_editor_show" = "構造図を表示"; -"web_editor_hide" = "構造図を隠す"; -"web_keywords_title" = "コードのキーワード"; -"web_keywords_hint" = "要素名の代わりに if, while, for"; -"web_palette_compact" = "要素を常にコンパクト表示"; -"web_palette_compact_hint" = "記号の下に小さく名前を表示。オフの場合は列が狭いときのみ"; -"web_about_version_fmt" = "バージョン %@"; -"web_about_software" = "使用しているソフトウェア"; -"web_about_software_text" = "コードエディターは CodeMirror と Lezer(MIT ライセンス)を使用しています。Swing サポートを有効にすると、VisuStruct は Leaning Technologies の CheerpJ(Community ライセンス)と OpenJDK の javac を読み込みます。"; -"web_about_licenses" = "ライセンス文"; -"web_live_run" = "実行"; -"web_live_run_title" = "プログラムを実行:出力と入力は下に表示されます"; -"web_live_run_finished" = "プログラムが終了しました。"; -"web_live_run_stopped" = "停止しました。"; -"web_console" = "コンソール"; -"web_console_hide" = "コンソールを隠す"; -"web_console_resize" = "コンソールの高さを変更"; -"web_diagram_tools" = "図のツール"; -"web_files_title" = "ファイル"; -"web_files_show" = "ファイルを表示"; -"web_files_hide" = "ファイルを隠す"; -"web_files_resize" = "ファイルの幅を変更"; -"web_files_new_folder" = "新しいフォルダ"; -"web_files_switch" = "別のフォルダを選ぶ"; -"web_files_delete" = "削除"; -"web_files_in_browser" = "このブラウザのストレージ"; -"web_files_save_hint_fmt" = "保存すると、プログラムは %@ に入ります。"; -"web_files_empty" = "まだ何も保存されていません。保存すると、VisuStruct がここに今日の日付のフォルダを作ります。"; -"web_files_reconnect_hint" = "再読み込み後、VisuStruct がフォルダを使ってよいか、ブラウザがもう一度たずねます。"; -"web_files_reconnect_fmt" = "フォルダ「%@」を開く"; -"web_files_connect_hint" = "作業フォルダを選んでください。VisuStruct が日付ごとのフォルダを作り、図とコードを保存します。"; -"web_files_connect" = "フォルダを選ぶ"; -"web_files_browser_hint" = "このブラウザは端末のフォルダを開けません(例:iPad)。ファイルはこのブラウザのストレージに残ります。"; -"web_files_browser" = "ブラウザに保存"; -"web_files_delete_confirm_fmt" = "「%@」を削除しますか?"; -"web_files_folder_prompt" = "新しいフォルダの名前:"; -"web_files_default_name" = "プログラム"; -"web_files_saved_fmt" = "保存しました:%@"; -"web_files_create" = "作成"; -"web_learn_ludwig_tip" = "ルートヴィヒのヒント"; -"web_game_title" = "ゲーム:真か偽か?"; -"web_game_intro" = "ルートヴィヒが値と条件を見せます。条件は真(true)?それとも偽(false)?10ラウンド、だんだん難しくなります。"; -"web_game_start" = "ゲームを始める"; -"web_game_round_fmt" = "ラウンド %@ / %@"; -"web_game_score_fmt" = "ポイント:%@"; -"web_game_true" = "真(true)"; -"web_game_false" = "偽(false)"; -"web_game_right" = "正解!"; -"web_game_wrong" = "残念。計算はこうなります:"; -"web_game_next" = "次へ"; -"web_game_finish" = "結果"; -"web_game_done_fmt" = "%@ / %@ 正解。"; -"web_game_perfect" = "全問正解――ルートヴィヒも感心しています!"; -"web_game_good" = "すごい!間違えたところを見直して、もう一度やってみよう。"; -"web_game_practice" = "練習あるのみ。上の真理値表をもう一度読んで、もう一回挑戦しよう。"; -"web_game_again" = "もう一度"; -"web_game_wrong_short" = "残念、不正解です。"; -"web_game_practice_short" = "習うより慣れよ。説明をもう一度読んで、もう一回遊んでみよう。"; -"web_game_clicks" = "クリック:"; -"web_game_nobody" = "だれも聞いていません。何も起こりません。"; -"web_game_event_fmt" = "「%@」をクリック: リスナーが気づいて自分のコードを実行します。"; -"web_game_try" = "ウィンドウのボタンを自分で試してみよう。"; -"web_game_time_fmt" = "残り %@ 秒"; -"web_game_defused" = "✂️ 解除成功!間違いを見つけました。"; -"web_game_boom_time" = "💥 ドカン!時間切れです。"; -"web_game_boom_wrong" = "💥 ドカン!その行ではありません。"; -"web_game_boom_part" = "💥 ドカン!行は合っているけど、間違いは別の部分にあるよ。"; -"web_game_boom_wrong_part" = "💥 ドカン!間違いは別の行にあるよ。"; -"web_game_bomb_parts_hint" = "間違っている部分をぴったりタップしよう。"; -"web_game_fix" = "正しい行:"; -"web_game_order_hint" = "正しい順番でステップをタップしよう。"; -"web_debug_step" = "ステップ実行"; -"web_debug_step_title" = "ステップ実行:命令を1つ実行して再び止まる"; -"web_debug_continue" = "次のブレークポイントまで続行"; -"web_debug_paused_fmt" = "%d 行目の前で一時停止中。"; -"web_debug_gutter_title" = "行番号をクリックするとブレークポイントを設定・解除できます"; -"web_swap_to_code" = "プログラミングを大きく"; -"web_swap_to_editor" = "構造図を大きく"; -"web_editor_resize" = "構造図の幅を変更"; -"web_palette_resize" = "要素の幅を変更"; -"web_palette_hide" = "要素を隠す"; -"web_palette_show" = "要素を表示"; -"web_code_scopes" = "コードブロックを色分け"; -"web_code_scopes_hint" = "クラス、メソッド、ループ、分岐をそれぞれの枠で表示"; -"web_pseudocode" = "疑似コード"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "変数の名前を変更 …"; -"web_rename_title" = "変数の名前を変更"; -"web_rename_old" = "名前"; -"web_rename_new" = "新しい名前"; -"web_rename_hint" = "つづりが完全に同じ箇所をすべて、コードと構造図で変更します(number と Number は別です)。\"…\" 内の文字列とコメントは変わりません。"; -"web_rename_count_fmt" = "%d 回使われています。"; -"web_rename_exists_fmt" = "「%@」はすでにあります:変更すると 2 つの変数が 1 つになります。"; -"web_rename_invalid" = "名前は文字で始まり、文字・数字・_ だけを含みます。"; -"web_rename_keyword_fmt" = "「%@」は Java のキーワードです。"; -"web_rename_apply" = "名前を変更"; -"web_rename_cancel" = "キャンセル"; diff --git a/web/src/i18n/ko.strings b/web/src/i18n/ko.strings deleted file mode 100644 index 4c7a28f0..00000000 --- a/web/src/i18n/ko.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* 웹 버전에만 있는 텍스트입니다. 나머지는 모두 앱의 Localizable.strings에서 가져옵니다. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 구조도"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "내보내기…"; -"web_export_title" = "이미지 또는 PDF로 저장하거나 인쇄"; -"web_export_png" = "이미지(PNG)"; -"web_export_svg" = "벡터 그래픽(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 파일(.visustruct)"; -"web_export_failed_fmt" = "내보내기 실패: %@"; -"web_print_hint" = "“내보내기… → PDF”로 다이어그램을 저장한 뒤 인쇄하세요."; -"web_delete" = "삭제"; -"web_language_title" = "인터페이스 언어"; -"web_simulation_start" = "시뮬레이션 시작"; -"web_simulation_end" = "시뮬레이션 종료"; -"web_simulation_start_title" = "구조도를 단계별로 실행"; -"web_simulation_end_title" = "시뮬레이션을 끝내고 계속 편집"; -"web_palette_hint" = "구조도로 드래그하세요"; -"web_tabs_label" = "다이어그램"; -"web_status_modified" = "수정됨"; -"web_inspector_title" = "속성"; -"web_inspector_resize" = "객체 검사기 높이 변경: 선을 위아래로 드래그하거나 스와이프 (더블 클릭: 기본값)"; -"web_inspector_help_drop" = "팔레트의 요소를 타일로 드래그하세요: 위쪽 = 앞에, 아래쪽 = 뒤에, “ø” 위 = 바꾸기. 기존 요소는 마우스로 옮길 수 있습니다."; -"web_inspector_help_inline" = "요소를 두 번 클릭하거나 두 번 탭하면 다이어그램에서 바로 텍스트를 편집할 수 있습니다(Enter 적용, Esc 취소). case 이름도 마찬가지입니다."; -"web_inspector_help_files" = "파일을 창에 끌어다 놓을 수도 있습니다."; -"web_case_name" = "케이스 이름"; -"web_case_name_placeholder" = "예: 1, ok, default"; -"web_copied" = "요소를 복사했습니다."; -"web_discard_message_fmt" = "“%@”의 변경 사항이 사라집니다."; -"web_err_unreadable_fmt" = "“%@”은(는) 읽을 수 있는 VisuStruct 파일이 아닙니다(%@)."; -"web_saved_fmt" = "“%@” 저장됨."; -"web_saved_txt_fmt" = "“%@”(으)로 저장했습니다. 사용하려면 .txt 확장자를 제거하세요."; -"web_drop_impossible" = "여기에 놓을 수 없음"; -"web_sim_delay" = "단계별 대기 시간"; -"web_sim_delay_value_fmt" = "%@초"; -"web_codegen_language" = "언어"; -"web_codegen_comments" = "다이어그램 텍스트를 주석으로"; -"web_codegen_empty_else" = "빈 else 분기도 코드에 출력"; -"web_copied_check" = "복사됨 ✓"; -"web_copy_manual" = "선택됨, Ctrl+C로 복사하세요"; -"web_run_stopped_loop" = "실행 중지: 무한 반복일 수 있습니다."; -"web_run_cancelled" = "실행이 취소되었습니다."; -"web_run_error" = "실행 중 오류가 발생했습니다."; -"web_run_dropped_fmt" = "… 이전 %d줄 숨김"; -"web_sim_call_arguments_fmt" = "호출이 %@의 머리글과 맞지 않습니다. 괄호 안의 값을 확인하세요."; -"web_sim_missing_return_fmt" = "%@이(가) return으로 값을 반환하지 않고 끝났습니다."; -"web_sim_file_not_found_fmt" = "%@(이)라는 파일이 없습니다."; -"web_sim_end_of_file_fmt" = "파일 %@에 더 이상 줄이 없습니다."; -"web_sim_null_value_fmt" = "%@은(는) null입니다."; -"web_sim_unknown_variable_case_fmt" = "알 수 없는 변수: %1$@. Java는 대문자와 소문자를 구분합니다. %2$@을(를) 뜻했나요?"; -"web_sim_written_file_fmt" = "%@ (저장됨)"; -"web_site_home" = "홈페이지"; -"web_site_imprint" = "사이트 정보"; -"web_site_privacy" = "개인정보"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "프로그래밍"; -"web_live_hide" = "프로그래밍 숨기기"; -"web_scale_canvas" = "글자 크기: 구조도"; -"web_scale_code" = "글자 크기: 프로그래밍"; -"web_live_indent" = "들여쓰기"; -"web_live_indent_title" = "코드 들여쓰기: 단계마다 공백 4칸 (선택한 줄, 없으면 전체)"; -"web_live_show" = "프로그래밍 표시"; -"web_live_resize" = "코드 영역 너비 변경(더블클릭: 초기화)"; -"web_live_hint" = "코드는 다이어그램과 함께 자랍니다. 회색 = 자리 표시자: 속성 창에 입력하면 여기에 표시됩니다. 줄을 클릭하면 해당 요소가 선택됩니다."; -"web_live_ph_selector" = "변수"; -"web_live_ph_code" = "코드 작성"; -"web_live_ph_end" = "끝"; -"web_live_language" = "프로그래밍 언어"; -"web_live_java_class" = "클래스로"; -"web_live_java_class_hint" = "클래스 이름 = 순서도 제목"; -"web_live_java_main" = "main 메서드 포함"; -"web_settings_display" = "표시"; -"web_live_swing" = "실제 Java (Swing 포함)"; -"web_live_swing_hint" = "창이 있는 진짜 Java(CheerpJ). 처음 실행할 때 한 번 약 40MB를 내려받습니다."; -"web_live_swing_windows" = "Java 창"; -"web_live_swing_hide" = "Java 창 닫기"; -"web_live_swing_loading" = "Java 불러오는 중(처음에는 약 40MB) …"; -"web_live_swing_compiling" = "컴파일 중 …"; -"web_live_swing_running" = "프로그램 실행 중."; -"web_live_swing_compile_failed" = "컴파일 오류, 위를 보세요."; -"web_live_swing_failed" = "Java를 불러올 수 없습니다. 인터넷 연결을 확인하세요."; -"web_live_hint_edit" = "여기에 Java 코드를 입력하면 순서도가 함께 만들어집니다. 또는 순서도에서 작업하면 코드가 함께 자랍니다. 회색 = 자리표시자, 그대로 덮어쓰면 됩니다."; -"web_live_edit_label" = "Java 코드"; -"web_parse_line_fmt" = "%d행: %@"; -"web_parse_missing" = "여기에 아직 없음: %@"; -"web_parse_extra" = "하나 더 많음: %@"; -"web_parse_unclosed_string" = "닫는 %@가 없습니다."; -"web_parse_else_without_if" = "이 “else”에 맞는 “if”가 없습니다."; -"web_parse_case_outside_switch" = "“case”와 “default”는 “switch” 안에 있어야 합니다."; -"web_parse_missing_do_while" = "“do { … }” 다음에 “while (조건);”이 필요합니다."; -"web_parse_more_methods" = "지금은 메서드 하나만 사용됩니다. 다른 서브프로그램은 별도 탭으로 만드세요."; -"web_parse_missing_import" = "맨 위에 %@ 이(가) 없습니다. 이 줄이 없으면 Java는 Scanner를 모릅니다."; -"web_parse_missing_scanner" = "먼저 %@ 이(가) 없습니다. 이 줄이 없으면 Scanner가 없습니다."; -"web_parse_unknown_type" = "Java는 이 데이터 형식을 알지 못합니다. %@을(를) 의미했나요?"; -"web_quickfix_insert_fmt" = "제안: %@ 삽입"; -"web_quickfix_replace_fmt" = "제안: %2$@ 대신 %1$@"; -"web_parse_wrong_case" = "Java는 대문자와 소문자를 구분합니다. 올바른 표기는 %@입니다."; -"web_editor_title" = "구조도"; -"web_editor_show" = "구조도 표시"; -"web_editor_hide" = "구조도 숨기기"; -"web_keywords_title" = "코드 키워드"; -"web_keywords_hint" = "요소 이름 대신 if, while, for"; -"web_palette_compact" = "요소 항상 간단히 보기"; -"web_palette_compact_hint" = "기호 아래에 작은 이름 표시, 아니면 열이 좁을 때만"; -"web_about_version_fmt" = "버전 %@"; -"web_about_software" = "사용된 소프트웨어"; -"web_about_software_text" = "코드 편집기는 CodeMirror와 Lezer(MIT 라이선스)를 사용합니다. Swing 지원을 켜면 VisuStruct는 Leaning Technologies의 CheerpJ(Community 라이선스)와 OpenJDK의 javac를 불러옵니다."; -"web_about_licenses" = "라이선스 전문"; -"web_live_run" = "실행"; -"web_live_run_title" = "프로그램 실행: 출력과 입력이 아래에 표시됩니다"; -"web_live_run_finished" = "프로그램이 끝났습니다."; -"web_live_run_stopped" = "중지됨."; -"web_console" = "콘솔"; -"web_console_hide" = "콘솔 숨기기"; -"web_console_resize" = "콘솔 크기 조절"; -"web_diagram_tools" = "다이어그램 도구"; -"web_files_title" = "파일"; -"web_files_show" = "파일 보기"; -"web_files_hide" = "파일 숨기기"; -"web_files_resize" = "파일 너비 조절"; -"web_files_new_folder" = "새 폴더"; -"web_files_switch" = "다른 폴더 선택"; -"web_files_delete" = "삭제"; -"web_files_in_browser" = "이 브라우저 저장소"; -"web_files_save_hint_fmt" = "저장하면 프로그램이 %@에 들어갑니다."; -"web_files_empty" = "아직 저장된 것이 없습니다. 저장하면 VisuStruct가 여기에 오늘 날짜 폴더를 만듭니다."; -"web_files_reconnect_hint" = "새로 고침 후 브라우저가 VisuStruct의 폴더 사용 여부를 다시 묻습니다."; -"web_files_reconnect_fmt" = "“%@” 폴더 열기"; -"web_files_connect_hint" = "작업 공간으로 쓸 폴더를 선택하세요. VisuStruct가 날짜별 폴더를 만들고 다이어그램과 코드를 저장합니다."; -"web_files_connect" = "폴더 선택"; -"web_files_browser_hint" = "이 브라우저는 기기의 폴더를 열 수 없습니다 (예: iPad). 파일은 이 브라우저 저장소에 남습니다."; -"web_files_browser" = "브라우저에 저장"; -"web_files_delete_confirm_fmt" = "“%@”을(를) 삭제할까요?"; -"web_files_folder_prompt" = "새 폴더 이름:"; -"web_files_default_name" = "프로그램"; -"web_files_saved_fmt" = "저장됨: %@"; -"web_files_create" = "만들기"; -"web_learn_ludwig_tip" = "루트비히의 팁"; -"web_game_title" = "게임: 참일까 거짓일까?"; -"web_game_intro" = "루트비히가 값과 조건을 보여 줍니다. 조건이 참(true)일까요, 거짓(false)일까요? 10라운드, 점점 어려워집니다."; -"web_game_start" = "게임 시작"; -"web_game_round_fmt" = "라운드 %@ / %@"; -"web_game_score_fmt" = "점수: %@"; -"web_game_true" = "참 (true)"; -"web_game_false" = "거짓 (false)"; -"web_game_right" = "정답!"; -"web_game_wrong" = "아쉽네요. 이렇게 계산합니다:"; -"web_game_next" = "다음"; -"web_game_finish" = "결과"; -"web_game_done_fmt" = "%@ / %@ 정답."; -"web_game_perfect" = "모두 정답 – 루트비히가 감탄했어요!"; -"web_game_good" = "훌륭해요! 틀린 것을 다시 보고 한 번 더 해 보세요."; -"web_game_practice" = "연습이 실력을 만듭니다. 위의 진리표를 다시 읽고 한 번 더 해 보세요."; -"web_game_again" = "다시 하기"; -"web_game_wrong_short" = "아쉽지만 틀렸어요."; -"web_game_practice_short" = "연습이 완벽을 만듭니다. 설명을 다시 읽고 한 판 더 해 보세요."; -"web_game_clicks" = "클릭:"; -"web_game_nobody" = "아무도 듣지 않아요. 아무 일도 일어나지 않습니다."; -"web_game_event_fmt" = "„%@” 클릭: 리스너가 듣고 자기 코드를 실행합니다."; -"web_game_try" = "이제 창의 버튼을 직접 눌러 보세요."; -"web_game_time_fmt" = "%@초 남음"; -"web_game_defused" = "✂️ 해체 성공! 오류를 찾았어요."; -"web_game_boom_time" = "💥 펑! 시간이 다 됐어요."; -"web_game_boom_wrong" = "💥 펑! 틀린 줄이었어요."; -"web_game_boom_part" = "💥 펑! 줄은 맞지만, 오류는 다른 부분에 있어요."; -"web_game_boom_wrong_part" = "💥 펑! 오류는 다른 줄에 있어요."; -"web_game_bomb_parts_hint" = "틀린 부분을 정확히 눌러 보세요."; -"web_game_fix" = "올바른 줄:"; -"web_game_order_hint" = "올바른 순서대로 단계를 눌러 보세요."; -"web_debug_step" = "한 단계"; -"web_debug_step_title" = "한 단계: 문장 하나를 실행하고 다시 멈춥니다"; -"web_debug_continue" = "다음 중단점까지 계속"; -"web_debug_paused_fmt" = "%d번째 줄 앞에서 멈춤."; -"web_debug_gutter_title" = "줄 번호를 클릭하면 중단점을 설정하거나 지웁니다"; -"web_swap_to_code" = "프로그래밍 크게"; -"web_swap_to_editor" = "구조도 크게"; -"web_editor_resize" = "구조도 너비 변경"; -"web_palette_resize" = "요소 너비 변경"; -"web_palette_hide" = "요소 숨기기"; -"web_palette_show" = "요소 표시"; -"web_code_scopes" = "코드 블록 색상 표시"; -"web_code_scopes_hint" = "클래스, 메서드, 반복, 분기를 각각의 상자로 표시"; -"web_pseudocode" = "의사 코드"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "변수 이름 바꾸기 …"; -"web_rename_title" = "변수 이름 바꾸기"; -"web_rename_old" = "이름"; -"web_rename_new" = "새 이름"; -"web_rename_hint" = "철자가 정확히 같은 모든 곳을 코드와 구조도에서 바꿉니다(number와 Number는 다릅니다). \"…\" 안의 텍스트와 주석은 그대로입니다."; -"web_rename_count_fmt" = "%d번 사용됨."; -"web_rename_exists_fmt" = "“%@”이(가) 이미 있습니다: 바꾸면 두 변수가 하나가 됩니다."; -"web_rename_invalid" = "이름은 문자로 시작하고 문자, 숫자, _만 포함합니다."; -"web_rename_keyword_fmt" = "“%@”은(는) Java 키워드입니다."; -"web_rename_apply" = "이름 바꾸기"; -"web_rename_cancel" = "취소"; diff --git a/web/src/i18n/nb.strings b/web/src/i18n/nb.strings deleted file mode 100644 index 2ca6f435..00000000 --- a/web/src/i18n/nb.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Tekster som bare finnes i nettversjonen. Alt annet kommer fra appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksporter…"; -"web_export_title" = "Lagre som bilde eller PDF, eller skriv ut"; -"web_export_png" = "Bilde (PNG)"; -"web_export_svg" = "Vektorgrafikk (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Eksporten mislyktes: %@"; -"web_print_hint" = "Bruk «Eksporter… → PDF» for å lagre diagrammet og skrive det ut derfra."; -"web_delete" = "Slett"; -"web_language_title" = "Språk i grensesnittet"; -"web_simulation_start" = "Start simulering"; -"web_simulation_end" = "Avslutt simulering"; -"web_simulation_start_title" = "Kjør struktogrammet trinn for trinn"; -"web_simulation_end_title" = "Avslutt simuleringen og fortsett redigeringen"; -"web_palette_hint" = "Dra inn i struktogrammet"; -"web_tabs_label" = "Diagrammer"; -"web_status_modified" = "endret"; -"web_inspector_title" = "Egenskaper"; -"web_inspector_resize" = "Endre høyden på objektinspektøren: dra eller sveip linjen opp eller ned (dobbeltklikk: standard)"; -"web_inspector_help_drop" = "Dra elementer fra paletten til en rute: øverst = før, nederst = etter, på «ø» = erstatt. Eksisterende elementer kan flyttes med musen."; -"web_inspector_help_inline" = "Dobbeltklikk eller dobbelttrykk på et element for å redigere teksten direkte i diagrammet (Enter bruker, Esc avbryter). Det gjelder også case-navn."; -"web_inspector_help_files" = "Du kan også slippe filer i vinduet."; -"web_case_name" = "Navn på case"; -"web_case_name_placeholder" = "f.eks. 1, ok, default"; -"web_copied" = "Element kopiert."; -"web_discard_message_fmt" = "Endringene i «%@» går tapt."; -"web_err_unreadable_fmt" = "«%@» er ikke en lesbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "«%@» lagret."; -"web_saved_txt_fmt" = "Lagret som «%@». Fjern filendelsen .txt for å bruke den."; -"web_drop_impossible" = "Kan ikke slippes her"; -"web_sim_delay" = "Pause per trinn"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Språk"; -"web_codegen_comments" = "Diagramtekst som kommentarer"; -"web_codegen_empty_else" = "Skriv ut tom else-gren i koden"; -"web_copied_check" = "Kopiert ✓"; -"web_copy_manual" = "Markert, trykk Ctrl+C for å kopiere"; -"web_run_stopped_loop" = "Kjøringen stoppet: mulig uendelig løkke."; -"web_run_cancelled" = "Kjøringen avbrutt."; -"web_run_error" = "Feil under kjøring."; -"web_run_dropped_fmt" = "… %d tidligere linjer skjult"; -"web_sim_call_arguments_fmt" = "Kallet passer ikke til hodet i %@. Sjekk verdiene i parentesene."; -"web_sim_missing_return_fmt" = "%@ ble avsluttet uten å returnere en verdi med return."; -"web_sim_file_not_found_fmt" = "Det finnes ingen fil som heter %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har ikke flere linjer."; -"web_sim_null_value_fmt" = "%@ er null."; -"web_sim_unknown_variable_case_fmt" = "Ukjent variabel: %1$@. Java skiller mellom store og små bokstaver: mente du %2$@?"; -"web_sim_written_file_fmt" = "%@ (lagret)"; -"web_site_home" = "Forside"; -"web_site_imprint" = "Kolofon"; -"web_site_privacy" = "Personvern"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Skjul programmering"; -"web_scale_canvas" = "Tekststørrelse: strukturdiagram"; -"web_scale_code" = "Tekststørrelse: programmering"; -"web_live_indent" = "Rykk inn"; -"web_live_indent_title" = "Rykk inn koden: fire mellomrom per nivå (merkede linjer, ellers alt)"; -"web_live_show" = "Vis programmering"; -"web_live_resize" = "Endre bredden på kodeområdet (dobbeltklikk: tilbakestill)"; -"web_live_hint" = "Koden vokser med diagrammet ditt. Grått = plassholder: skriv i inspektøren, så vises teksten her. Klikk på en linje for å velge elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kode"; -"web_live_ph_end" = "slutt"; -"web_live_language" = "Programmeringsspråk"; -"web_live_java_class" = "Som klasse"; -"web_live_java_class_hint" = "Klassenavn = diagrammets tittel"; -"web_live_java_main" = "Med main-metode"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Ekte Java (med Swing)"; -"web_live_swing_hint" = "Ekte Java med vinduer (CheerpJ). Laster ned ca. 40 MB én gang ved første kjøring."; -"web_live_swing_windows" = "Java-vinduer"; -"web_live_swing_hide" = "Lukk Java-vinduer"; -"web_live_swing_loading" = "Java lastes inn (første gang ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompilerer …"; -"web_live_swing_running" = "Programmet kjører."; -"web_live_swing_compile_failed" = "Kompileringsfeil, se over."; -"web_live_swing_failed" = "Java kunne ikke lastes inn. Sjekk internettforbindelsen."; -"web_live_hint_edit" = "Skriv Java-kode her, så bygger diagrammet seg selv. Eller arbeid i diagrammet, så vokser koden med. Grått = plassholder, bare skriv over."; -"web_live_edit_label" = "Java-kode"; -"web_parse_line_fmt" = "Linje %d: %@"; -"web_parse_missing" = "Her mangler fortsatt: %@"; -"web_parse_extra" = "For mye: %@"; -"web_parse_unclosed_string" = "Det avsluttende %@ mangler."; -"web_parse_else_without_if" = "Denne «else» har ingen «if»."; -"web_parse_case_outside_switch" = "«case» og «default» hører hjemme i en «switch»."; -"web_parse_missing_do_while" = "Etter «do { … }» mangler «while (betingelse);»."; -"web_parse_more_methods" = "Foreløpig teller bare én metode her. Lag andre underprogrammer i egne faner."; -"web_parse_missing_import" = "Øverst mangler %@ Uten denne linjen kjenner ikke Java Scanner."; -"web_parse_missing_scanner" = "Før mangler %@ Uten den linjen finnes ikke Scanneren."; -"web_parse_unknown_type" = "Java kjenner ikke denne datatypen. Mente du %@?"; -"web_quickfix_insert_fmt" = "Forslag: sett inn %@"; -"web_quickfix_replace_fmt" = "Forslag: %1$@ i stedet for %2$@"; -"web_parse_wrong_case" = "Java skiller mellom store og små bokstaver. Riktig er %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Vis strukturdiagram"; -"web_editor_hide" = "Skjul strukturdiagram"; -"web_keywords_title" = "Kode-nøkkelord"; -"web_keywords_hint" = "if, while, for i stedet for elementnavn"; -"web_palette_compact" = "Elementer alltid kompakte"; -"web_palette_compact_hint" = "Symbol med lite navn under, ellers bare når kolonnen er smal"; -"web_about_version_fmt" = "Versjon %@"; -"web_about_software" = "Programvare som brukes"; -"web_about_software_text" = "Kodeeditoren bruker CodeMirror og Lezer (MIT-lisens). Med Swing-støtte laster VisuStruct inn CheerpJ fra Leaning Technologies (Community-lisens) og javac fra OpenJDK."; -"web_about_licenses" = "Lisenstekster"; -"web_live_run" = "Kjør"; -"web_live_run_title" = "Kjør programmet: utdata og inndata vises nedenfor"; -"web_live_run_finished" = "Programmet er ferdig."; -"web_live_run_stopped" = "Stoppet."; -"web_console" = "Konsoll"; -"web_console_hide" = "Skjul konsollen"; -"web_console_resize" = "Endre høyden på konsollen"; -"web_diagram_tools" = "Diagramverktøy"; -"web_files_title" = "Filer"; -"web_files_show" = "Vis filer"; -"web_files_hide" = "Skjul filer"; -"web_files_resize" = "Endre bredden på filer"; -"web_files_new_folder" = "Ny mappe"; -"web_files_switch" = "Velg en annen mappe"; -"web_files_delete" = "Slett"; -"web_files_in_browser" = "I denne nettleserens lagring"; -"web_files_save_hint_fmt" = "Lagring legger programmet i %@."; -"web_files_empty" = "Ingenting lagret ennå. Når du lagrer, lager VisuStruct en mappe med dagens dato her."; -"web_files_reconnect_hint" = "Etter ny innlasting spør nettleseren igjen om VisuStruct kan bruke mappen din."; -"web_files_reconnect_fmt" = "Åpne mappen «%@»"; -"web_files_connect_hint" = "Velg en mappe som arbeidsområde. VisuStruct lager en mappe for hver dag der og lagrer diagram og kode."; -"web_files_connect" = "Velg mappe"; -"web_files_browser_hint" = "Denne nettleseren kan ikke åpne mapper på enheten (f.eks. på iPad). Filene dine blir i nettleserens lagring."; -"web_files_browser" = "Lagre i nettleseren"; -"web_files_delete_confirm_fmt" = "Vil du virkelig slette «%@»?"; -"web_files_folder_prompt" = "Navn på den nye mappen:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Lagret: %@"; -"web_files_create" = "Lag"; -"web_learn_ludwig_tip" = "Ludwigs tips"; -"web_game_title" = "Spill: Sant eller usant?"; -"web_game_intro" = "Ludwig viser deg verdier og en betingelse. Er betingelsen sann (true) eller usann (false)? Ti runder, stadig vanskeligere."; -"web_game_start" = "Start spillet"; -"web_game_round_fmt" = "Runde %@ av %@"; -"web_game_score_fmt" = "Poeng: %@"; -"web_game_true" = "sann (true)"; -"web_game_false" = "usann (false)"; -"web_game_right" = "Riktig!"; -"web_game_wrong" = "Ikke helt. Slik regnes det ut:"; -"web_game_next" = "Neste"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ av %@ riktige."; -"web_game_perfect" = "Alt riktig – Ludwig er imponert!"; -"web_game_good" = "Bra! Se på feilene dine og prøv igjen."; -"web_game_practice" = "Øvelse gjør mester. Les sannhetstabellen igjen og spill en runde til."; -"web_game_again" = "Spill igjen"; -"web_game_wrong_short" = "Dessverre feil."; -"web_game_practice_short" = "Øvelse gjør mester. Les forklaringene igjen og spill en runde til."; -"web_game_clicks" = "Klikk:"; -"web_game_nobody" = "Ingen lytter – ingenting skjer."; -"web_game_event_fmt" = "Klikk på «%@»: lytteren hører det og kjører koden sin."; -"web_game_try" = "Prøv nå knappene i vinduet selv."; -"web_game_time_fmt" = "%@ s igjen"; -"web_game_defused" = "✂️ Desarmert! Du fant feilen."; -"web_game_boom_time" = "💥 Bom! Tiden er ute."; -"web_game_boom_wrong" = "💥 Bom! Det var feil linje."; -"web_game_boom_part" = "💥 Pang! Riktig linje, men feilen er i en annen del."; -"web_game_boom_wrong_part" = "💥 Pang! Feilen er i en annen linje."; -"web_game_bomb_parts_hint" = "Trykk nøyaktig på den feile delen."; -"web_game_fix" = "Riktig linje:"; -"web_game_order_hint" = "Trykk på trinnene i riktig rekkefølge."; -"web_debug_step" = "Steg"; -"web_debug_step_title" = "Steg: kjør én setning og stopp igjen"; -"web_debug_continue" = "Fortsett til neste stoppunkt"; -"web_debug_paused_fmt" = "Stoppet før linje %d."; -"web_debug_gutter_title" = "Klikk på et linjenummer for å sette eller fjerne et stoppunkt"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Endre bredden på struktogrammet"; -"web_palette_resize" = "Endre bredden på elementene"; -"web_palette_hide" = "Skjul elementene"; -"web_palette_show" = "Vis elementene"; -"web_code_scopes" = "Fargede kodeblokker"; -"web_code_scopes_hint" = "Klasse, metode, løkke, forgrening i egne bokser"; -"web_pseudocode" = "Pseudokode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Gi variabelen nytt navn …"; -"web_rename_title" = "Gi variabelen nytt navn"; -"web_rename_old" = "Navn"; -"web_rename_new" = "Nytt navn"; -"web_rename_hint" = "Endrer hvert sted med nøyaktig denne skrivemåten (tall er ikke Tall), i koden og i strukturdiagrammet. Tekst i \"…\" og kommentarer blir værende."; -"web_rename_count_fmt" = "Forekommer %d ganger."; -"web_rename_exists_fmt" = "«%@» finnes allerede: etterpå ville begge være én variabel."; -"web_rename_invalid" = "Et navn begynner med en bokstav og inneholder bare bokstaver, sifre og _."; -"web_rename_keyword_fmt" = "«%@» er et nøkkelord i Java."; -"web_rename_apply" = "Gi nytt navn"; -"web_rename_cancel" = "Avbryt"; diff --git a/web/src/i18n/nl.strings b/web/src/i18n/nl.strings deleted file mode 100644 index ab624ca9..00000000 --- a/web/src/i18n/nl.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Teksten die alleen in de webversie bestaan. Al het andere komt uit de Localizable.strings van de app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-structogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exporteren…"; -"web_export_title" = "Opslaan als afbeelding of PDF, of afdrukken"; -"web_export_png" = "Afbeelding (PNG)"; -"web_export_svg" = "Vectorafbeelding (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-bestand (.visustruct)"; -"web_export_failed_fmt" = "Exporteren mislukt: %@"; -"web_print_hint" = "Gebruik “Exporteren… → PDF” om het diagram op te slaan en het daarna af te drukken."; -"web_delete" = "Verwijderen"; -"web_language_title" = "Taal van de interface"; -"web_simulation_start" = "Simulatie starten"; -"web_simulation_end" = "Simulatie beëindigen"; -"web_simulation_start_title" = "Het structogram stap voor stap uitvoeren"; -"web_simulation_end_title" = "Simulatie beëindigen en verder bewerken"; -"web_palette_hint" = "Naar het structogram slepen"; -"web_tabs_label" = "Diagrammen"; -"web_status_modified" = "gewijzigd"; -"web_inspector_title" = "Eigenschappen"; -"web_inspector_resize" = "Hoogte van de objectinspector wijzigen: sleep of veeg de lijn omhoog of omlaag (dubbelklik: standaard)"; -"web_inspector_help_drop" = "Sleep elementen uit het palet op een tegel: boven = ervoor, onder = erna, op “ø” = vervangen. Bestaande elementen kun je met de muis verplaatsen."; -"web_inspector_help_inline" = "Dubbelklik of dubbeltik op een element om de tekst direct in het diagram te bewerken (Enter past toe, Esc annuleert). Ook voor case-namen."; -"web_inspector_help_files" = "Je kunt ook bestanden in het venster neerzetten."; -"web_case_name" = "Naam van de case"; -"web_case_name_placeholder" = "bijv. 1, ok, default"; -"web_copied" = "Element gekopieerd."; -"web_discard_message_fmt" = "De wijzigingen in “%@” gaan verloren."; -"web_err_unreadable_fmt" = "“%@” is geen leesbaar VisuStruct-bestand (%@)."; -"web_saved_fmt" = "“%@” opgeslagen."; -"web_saved_txt_fmt" = "Opgeslagen als “%@”. Verwijder de extensie .txt om het te gebruiken."; -"web_drop_impossible" = "Hier neerzetten is niet mogelijk"; -"web_sim_delay" = "Pauze per stap"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Taal"; -"web_codegen_comments" = "Diagramtekst als commentaar"; -"web_codegen_empty_else" = "Lege else-tak in code genereren"; -"web_copied_check" = "Gekopieerd ✓"; -"web_copy_manual" = "Geselecteerd, druk op Ctrl+C om te kopiëren"; -"web_run_stopped_loop" = "Uitvoering gestopt: mogelijk oneindige lus."; -"web_run_cancelled" = "Uitvoering geannuleerd."; -"web_run_error" = "Fout tijdens het uitvoeren."; -"web_run_dropped_fmt" = "… %d eerdere regels verborgen"; -"web_sim_call_arguments_fmt" = "De aanroep past niet bij de kop van %@. Controleer de waarden tussen de haakjes."; -"web_sim_missing_return_fmt" = "%@ is geëindigd zonder met return een waarde terug te geven."; -"web_sim_file_not_found_fmt" = "Er is geen bestand met de naam %@."; -"web_sim_end_of_file_fmt" = "Het bestand %@ heeft geen regels meer."; -"web_sim_null_value_fmt" = "%@ is null."; -"web_sim_unknown_variable_case_fmt" = "Onbekende variabele: %1$@. Java is hoofdlettergevoelig: bedoelde je %2$@?"; -"web_sim_written_file_fmt" = "%@ (opgeslagen)"; -"web_site_home" = "Startpagina"; -"web_site_imprint" = "Colofon"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmeren"; -"web_live_hide" = "Programmeren verbergen"; -"web_scale_canvas" = "Tekstgrootte: structuurdiagram"; -"web_scale_code" = "Tekstgrootte: programmeren"; -"web_live_indent" = "Inspringen"; -"web_live_indent_title" = "Code laten inspringen: vier spaties per niveau (geselecteerde regels, anders alles)"; -"web_live_show" = "Programmeren tonen"; -"web_live_resize" = "Breedte van het codegebied wijzigen (dubbelklik: herstellen)"; -"web_live_hint" = "De code groeit mee met je diagram. Grijs = plaatshouder: typ in de inspector en je tekst verschijnt hier. Klik op een regel om het element te selecteren."; -"web_live_ph_selector" = "variabele"; -"web_live_ph_code" = "Jouw code"; -"web_live_ph_end" = "einde"; -"web_live_language" = "Programmeertaal"; -"web_live_java_class" = "Als klasse"; -"web_live_java_class_hint" = "Klassenaam = titel van het diagram"; -"web_live_java_main" = "Met main-methode"; -"web_settings_display" = "Weergave"; -"web_live_swing" = "Echte Java (met Swing)"; -"web_live_swing_hint" = "Echt Java met vensters (CheerpJ). Downloadt eenmalig ca. 40 MB bij de eerste keer uitvoeren."; -"web_live_swing_windows" = "Java-vensters"; -"web_live_swing_hide" = "Java-vensters sluiten"; -"web_live_swing_loading" = "Java wordt geladen (de eerste keer ca. 40 MB) …"; -"web_live_swing_compiling" = "Compileren …"; -"web_live_swing_running" = "Programma draait."; -"web_live_swing_compile_failed" = "Compileerfout, zie hierboven."; -"web_live_swing_failed" = "Java kon niet worden geladen. Controleer de internetverbinding."; -"web_live_hint_edit" = "Typ hier Java-code en het diagram bouwt zichzelf op. Of werk in het diagram en de code groeit mee. Grijs = tijdelijke tekst, typ er gewoon overheen."; -"web_live_edit_label" = "Java-code"; -"web_parse_line_fmt" = "Regel %d: %@"; -"web_parse_missing" = "Hier ontbreekt nog: %@"; -"web_parse_extra" = "Te veel: %@"; -"web_parse_unclosed_string" = "De afsluitende %@ ontbreekt."; -"web_parse_else_without_if" = "Bij deze “else” hoort geen “if”."; -"web_parse_case_outside_switch" = "“case” en “default” horen in een “switch”."; -"web_parse_missing_do_while" = "Na “do { … }” ontbreekt nog “while (voorwaarde);”."; -"web_parse_more_methods" = "Voorlopig telt hier maar één methode. Maak andere subprogramma’s in aparte tabbladen."; -"web_parse_missing_import" = "Bovenaan ontbreekt %@ Zonder deze regel kent Java Scanner niet."; -"web_parse_missing_scanner" = "Eerst ontbreekt %@ Zonder deze regel bestaat de Scanner niet."; -"web_parse_unknown_type" = "Java kent dit gegevenstype niet. Bedoelde je %@?"; -"web_quickfix_insert_fmt" = "Suggestie: %@ invoegen"; -"web_quickfix_replace_fmt" = "Suggestie: %1$@ in plaats van %2$@"; -"web_parse_wrong_case" = "Java is hoofdlettergevoelig. Juist is %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Diagram tonen"; -"web_editor_hide" = "Diagram verbergen"; -"web_keywords_title" = "Code-sleutelwoorden"; -"web_keywords_hint" = "if, while, for in plaats van elementnamen"; -"web_palette_compact" = "Elementen altijd compact"; -"web_palette_compact_hint" = "Symbool met kleine naam eronder, anders alleen bij een smalle kolom"; -"web_about_version_fmt" = "Versie %@"; -"web_about_software" = "Gebruikte software"; -"web_about_software_text" = "De code-editor gebruikt CodeMirror en Lezer (MIT-licentie). Met Swing-ondersteuning laadt VisuStruct CheerpJ van Leaning Technologies (Community-licentie) en javac uit OpenJDK."; -"web_about_licenses" = "Licentieteksten"; -"web_live_run" = "Uitvoeren"; -"web_live_run_title" = "Programma uitvoeren: uitvoer en invoer verschijnen eronder"; -"web_live_run_finished" = "Programma klaar."; -"web_live_run_stopped" = "Gestopt."; -"web_console" = "Console"; -"web_console_hide" = "Console verbergen"; -"web_console_resize" = "Hoogte van de console wijzigen"; -"web_diagram_tools" = "Diagramgereedschap"; -"web_files_title" = "Bestanden"; -"web_files_show" = "Bestanden tonen"; -"web_files_hide" = "Bestanden verbergen"; -"web_files_resize" = "Breedte van bestanden wijzigen"; -"web_files_new_folder" = "Nieuwe map"; -"web_files_switch" = "Andere map kiezen"; -"web_files_delete" = "Verwijderen"; -"web_files_in_browser" = "In de opslag van deze browser"; -"web_files_save_hint_fmt" = "Opslaan zet het programma in %@."; -"web_files_empty" = "Nog niets opgeslagen. Bij het opslaan maakt VisuStruct hier een map met de datum van vandaag."; -"web_files_reconnect_hint" = "Na het herladen vraagt de browser nog eens of VisuStruct je map mag gebruiken."; -"web_files_reconnect_fmt" = "Map „%@” openen"; -"web_files_connect_hint" = "Kies een map als werkruimte. VisuStruct maakt daarin een map voor elke dag en slaat diagram en code op."; -"web_files_connect" = "Map kiezen"; -"web_files_browser_hint" = "Deze browser kan geen mappen op het apparaat openen (bijv. op de iPad). Je bestanden blijven in de opslag van deze browser."; -"web_files_browser" = "Opslaan in de browser"; -"web_files_delete_confirm_fmt" = "„%@” echt verwijderen?"; -"web_files_folder_prompt" = "Naam van de nieuwe map:"; -"web_files_default_name" = "Programma"; -"web_files_saved_fmt" = "Opgeslagen: %@"; -"web_files_create" = "Maken"; -"web_learn_ludwig_tip" = "Ludwigs tip"; -"web_game_title" = "Spel: Waar of onwaar?"; -"web_game_intro" = "Ludwig laat je waarden en een voorwaarde zien. Is de voorwaarde waar (true) of onwaar (false)? Tien rondes, steeds lastiger."; -"web_game_start" = "Spel starten"; -"web_game_round_fmt" = "Ronde %@ van %@"; -"web_game_score_fmt" = "Punten: %@"; -"web_game_true" = "waar (true)"; -"web_game_false" = "onwaar (false)"; -"web_game_right" = "Goed!"; -"web_game_wrong" = "Helaas fout. Zo reken je het uit:"; -"web_game_next" = "Verder"; -"web_game_finish" = "Resultaat"; -"web_game_done_fmt" = "%@ van %@ goed."; -"web_game_perfect" = "Alles goed – Ludwig is diep onder de indruk!"; -"web_game_good" = "Sterk! Bekijk je fouten nog eens en probeer het opnieuw."; -"web_game_practice" = "Oefening baart kunst. Lees de waarheidstabel nog eens en speel nog een ronde."; -"web_game_again" = "Nog een keer"; -"web_game_wrong_short" = "Helaas, fout."; -"web_game_practice_short" = "Oefening baart kunst. Lees de uitleg nog eens en speel nog een ronde."; -"web_game_clicks" = "Klikken:"; -"web_game_nobody" = "Niemand luistert – er gebeurt niets."; -"web_game_event_fmt" = "Klik op ‘%@’: de luisteraar hoort het en voert zijn code uit."; -"web_game_try" = "Probeer de knoppen in het venster nu zelf uit."; -"web_game_time_fmt" = "Nog %@ s"; -"web_game_defused" = "✂️ Onschadelijk gemaakt! Je hebt de fout gevonden."; -"web_game_boom_time" = "💥 Boem! De tijd is om."; -"web_game_boom_wrong" = "💥 Boem! Dat was de verkeerde regel."; -"web_game_boom_part" = "💥 Boem! Goede regel, maar de fout zit in een ander deel."; -"web_game_boom_wrong_part" = "💥 Boem! De fout zit in een andere regel."; -"web_game_bomb_parts_hint" = "Tik precies op het foute deel."; -"web_game_fix" = "De juiste regel:"; -"web_game_order_hint" = "Tik de stappen in de juiste volgorde aan."; -"web_debug_step" = "Stap"; -"web_debug_step_title" = "Stap: één opdracht uitvoeren en weer pauzeren"; -"web_debug_continue" = "Doorgaan tot het volgende breekpunt"; -"web_debug_paused_fmt" = "Gepauzeerd vóór regel %d."; -"web_debug_gutter_title" = "Klik op een regelnummer om een breekpunt te plaatsen of te verwijderen"; -"web_swap_to_code" = "Programmeren groot"; -"web_swap_to_editor" = "Structogram groot"; -"web_editor_resize" = "Breedte van het structogram wijzigen"; -"web_palette_resize" = "Breedte van de elementen wijzigen"; -"web_palette_hide" = "Elementen verbergen"; -"web_palette_show" = "Elementen tonen"; -"web_code_scopes" = "Gekleurde codeblokken"; -"web_code_scopes_hint" = "Klasse, methode, lus, keuze in eigen kaders"; -"web_pseudocode" = "Pseudocode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Variabele hernoemen …"; -"web_rename_title" = "Variabele hernoemen"; -"web_rename_old" = "Naam"; -"web_rename_new" = "Nieuwe naam"; -"web_rename_hint" = "Wijzigt elke plek met precies deze schrijfwijze (getal is niet Getal), in de code en in het structogram. Tekst tussen \"…\" en commentaar blijven."; -"web_rename_count_fmt" = "Komt %d keer voor."; -"web_rename_exists_fmt" = "‘%@’ bestaat al: daarna zouden beide één variabele zijn."; -"web_rename_invalid" = "Een naam begint met een letter en bevat alleen letters, cijfers en _."; -"web_rename_keyword_fmt" = "‘%@’ is een Java-sleutelwoord."; -"web_rename_apply" = "Hernoemen"; -"web_rename_cancel" = "Annuleren"; diff --git a/web/src/i18n/pl.strings b/web/src/i18n/pl.strings deleted file mode 100644 index 0e59a42e..00000000 --- a/web/src/i18n/pl.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Teksty dostępne tylko w wersji webowej. Wszystko inne pochodzi z plików Localizable.strings aplikacji. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagram VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksportuj…"; -"web_export_title" = "Zapisz jako obraz lub PDF albo wydrukuj"; -"web_export_png" = "Obraz (PNG)"; -"web_export_svg" = "Grafika wektorowa (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Plik VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Eksport nie powiódł się: %@"; -"web_print_hint" = "Użyj „Eksportuj… → PDF“, aby zapisać diagram i wydrukować go z pliku."; -"web_delete" = "Usuń"; -"web_language_title" = "Język interfejsu"; -"web_simulation_start" = "Uruchom symulację"; -"web_simulation_end" = "Zakończ symulację"; -"web_simulation_start_title" = "Uruchom diagram krok po kroku"; -"web_simulation_end_title" = "Zakończ symulację i wróć do edycji"; -"web_palette_hint" = "Przeciągnij do diagramu"; -"web_tabs_label" = "Diagramy"; -"web_status_modified" = "zmieniony"; -"web_inspector_title" = "Właściwości"; -"web_inspector_resize" = "Zmień wysokość inspektora obiektów: przeciągnij lub przesuń linię w górę lub w dół (dwuklik: domyślnie)"; -"web_inspector_help_drop" = "Przeciągnij elementy z palety na kafelek: góra = przed, dół = za, na „ø“ = zastąp. Istniejące elementy można przesuwać myszą."; -"web_inspector_help_inline" = "Kliknij dwukrotnie lub stuknij dwa razy element, aby edytować tekst bezpośrednio w diagramie (Enter zatwierdza, Esc anuluje). Dotyczy też nazw przypadków."; -"web_inspector_help_files" = "Pliki można też upuścić w oknie."; -"web_case_name" = "Nazwa przypadku"; -"web_case_name_placeholder" = "np. 1, ok, default"; -"web_copied" = "Skopiowano element."; -"web_discard_message_fmt" = "Zmiany w „%@“ zostaną utracone."; -"web_err_unreadable_fmt" = "„%@“ nie jest czytelnym plikiem VisuStruct (%@)."; -"web_saved_fmt" = "Zapisano „%@“."; -"web_saved_txt_fmt" = "Zapisano jako „%@“. Usuń rozszerzenie .txt, aby go użyć."; -"web_drop_impossible" = "Nie można tu upuścić"; -"web_sim_delay" = "Pauza na krok"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Język"; -"web_codegen_comments" = "Tekst diagramu jako komentarze"; -"web_codegen_empty_else" = "Generuj pustą gałąź else w kodzie"; -"web_copied_check" = "Skopiowano ✓"; -"web_copy_manual" = "Zaznaczono, naciśnij Ctrl+C, aby skopiować"; -"web_run_stopped_loop" = "Wykonanie zatrzymane: możliwa pętla nieskończona."; -"web_run_cancelled" = "Wykonanie anulowane."; -"web_run_error" = "Błąd podczas wykonywania."; -"web_run_dropped_fmt" = "… ukryto wcześniejsze wiersze: %d"; -"web_sim_call_arguments_fmt" = "Wywołanie nie pasuje do nagłówka %@. Sprawdź wartości w nawiasach."; -"web_sim_missing_return_fmt" = "%@ zakończył się bez zwrócenia wartości przez return."; -"web_sim_file_not_found_fmt" = "Nie ma pliku o nazwie %@."; -"web_sim_end_of_file_fmt" = "Plik %@ nie ma już więcej wierszy."; -"web_sim_null_value_fmt" = "%@ ma wartość null."; -"web_sim_unknown_variable_case_fmt" = "Nieznana zmienna: %1$@. Java rozróżnia wielkość liter: czy chodziło o %2$@?"; -"web_sim_written_file_fmt" = "%@ (zapisano)"; -"web_site_home" = "Strona główna"; -"web_site_imprint" = "Nota prawna"; -"web_site_privacy" = "Prywatność"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programowanie"; -"web_live_hide" = "Ukryj programowanie"; -"web_scale_canvas" = "Rozmiar tekstu: diagram"; -"web_scale_code" = "Rozmiar tekstu: programowanie"; -"web_live_indent" = "Wcięcia"; -"web_live_indent_title" = "Popraw wcięcia kodu: cztery spacje na poziom (zaznaczone wiersze, w przeciwnym razie wszystko)"; -"web_live_show" = "Pokaż programowanie"; -"web_live_resize" = "Zmień szerokość obszaru kodu (dwuklik: resetuj)"; -"web_live_hint" = "Kod rośnie razem z diagramem. Szary = symbol zastępczy: wpisz tekst w inspektorze, a pojawi się tutaj. Kliknij wiersz, aby zaznaczyć jego element."; -"web_live_ph_selector" = "zmienna"; -"web_live_ph_code" = "Twój kod"; -"web_live_ph_end" = "koniec"; -"web_live_language" = "Język programowania"; -"web_live_java_class" = "Jako klasa"; -"web_live_java_class_hint" = "Nazwa klasy = tytuł diagramu"; -"web_live_java_main" = "Z metodą main"; -"web_settings_display" = "Wygląd"; -"web_live_swing" = "Prawdziwa Java (ze Swing)"; -"web_live_swing_hint" = "Prawdziwa Java z oknami (CheerpJ). Przy pierwszym uruchomieniu jednorazowo pobiera ok. 40 MB."; -"web_live_swing_windows" = "Okna Java"; -"web_live_swing_hide" = "Zamknij okna Java"; -"web_live_swing_loading" = "Ładowanie Javy (za pierwszym razem ok. 40 MB) …"; -"web_live_swing_compiling" = "Kompilowanie …"; -"web_live_swing_running" = "Program działa."; -"web_live_swing_compile_failed" = "Błąd kompilacji, zobacz powyżej."; -"web_live_swing_failed" = "Nie udało się załadować Javy. Sprawdź połączenie z internetem."; -"web_live_hint_edit" = "Wpisz tu kod Java, a diagram zbuduje się sam. Albo pracuj w diagramie, a kod będzie rósł razem z nim. Szary = symbol zastępczy, po prostu pisz."; -"web_live_edit_label" = "Kod Java"; -"web_parse_line_fmt" = "Wiersz %d: %@"; -"web_parse_missing" = "Tu jeszcze brakuje: %@"; -"web_parse_extra" = "Za dużo: %@"; -"web_parse_unclosed_string" = "Brakuje zamykającego %@."; -"web_parse_else_without_if" = "Ten „else” nie ma pasującego „if”."; -"web_parse_case_outside_switch" = "„case” i „default” należą do „switch”."; -"web_parse_missing_do_while" = "Po „do { … }” brakuje jeszcze „while (warunek);”."; -"web_parse_more_methods" = "Na razie liczy się tu tylko jedna metoda. Utwórz kolejne podprogramy w osobnych kartach."; -"web_parse_missing_import" = "Na samej górze brakuje %@ Bez tej linii Java nie zna klasy Scanner."; -"web_parse_missing_scanner" = "Wcześniej brakuje %@ Bez tej linii Scanner nie istnieje."; -"web_parse_unknown_type" = "Java nie zna tego typu danych. Czy chodziło ci o %@?"; -"web_quickfix_insert_fmt" = "Propozycja: wstaw %@"; -"web_quickfix_replace_fmt" = "Propozycja: %1$@ zamiast %2$@"; -"web_parse_wrong_case" = "Java rozróżnia wielkość liter. Poprawnie: %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Pokaż diagram"; -"web_editor_hide" = "Ukryj diagram"; -"web_keywords_title" = "Słowa kluczowe kodu"; -"web_keywords_hint" = "if, while, for zamiast nazw elementów"; -"web_palette_compact" = "Elementy zawsze kompaktowe"; -"web_palette_compact_hint" = "Symbol z małą nazwą pod spodem, w przeciwnym razie tylko przy wąskiej kolumnie"; -"web_about_version_fmt" = "Wersja %@"; -"web_about_software" = "Używane oprogramowanie"; -"web_about_software_text" = "Edytor kodu korzysta z CodeMirror i Lezer (licencja MIT). Z obsługą Swing VisuStruct wczytuje CheerpJ firmy Leaning Technologies (licencja Community) oraz javac z OpenJDK."; -"web_about_licenses" = "Teksty licencji"; -"web_live_run" = "Uruchom"; -"web_live_run_title" = "Uruchom program: wyjście i wejście pojawią się poniżej"; -"web_live_run_finished" = "Program zakończony."; -"web_live_run_stopped" = "Zatrzymano."; -"web_console" = "Konsola"; -"web_console_hide" = "Ukryj konsolę"; -"web_console_resize" = "Zmień wysokość konsoli"; -"web_diagram_tools" = "Narzędzia diagramu"; -"web_files_title" = "Pliki"; -"web_files_show" = "Pokaż pliki"; -"web_files_hide" = "Ukryj pliki"; -"web_files_resize" = "Zmień szerokość plików"; -"web_files_new_folder" = "Nowy folder"; -"web_files_switch" = "Wybierz inny folder"; -"web_files_delete" = "Usuń"; -"web_files_in_browser" = "W pamięci tej przeglądarki"; -"web_files_save_hint_fmt" = "Zapis umieszcza program w %@."; -"web_files_empty" = "Nic jeszcze nie zapisano. Przy zapisie VisuStruct tworzy tu folder z dzisiejszą datą."; -"web_files_reconnect_hint" = "Po przeładowaniu przeglądarka ponownie pyta, czy VisuStruct może używać twojego folderu."; -"web_files_reconnect_fmt" = "Otwórz folder „%@”"; -"web_files_connect_hint" = "Wybierz folder jako obszar roboczy. VisuStruct tworzy w nim folder na każdy dzień oraz zapisuje diagram i kod."; -"web_files_connect" = "Wybierz folder"; -"web_files_browser_hint" = "Ta przeglądarka nie może otwierać folderów na urządzeniu (np. na iPadzie). Twoje pliki pozostają w pamięci tej przeglądarki."; -"web_files_browser" = "Zapisz w przeglądarce"; -"web_files_delete_confirm_fmt" = "Na pewno usunąć „%@”?"; -"web_files_folder_prompt" = "Nazwa nowego folderu:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Zapisano: %@"; -"web_files_create" = "Utwórz"; -"web_learn_ludwig_tip" = "Wskazówka Ludwiga"; -"web_game_title" = "Gra: Prawda czy fałsz?"; -"web_game_intro" = "Ludwig pokazuje wartości i warunek. Czy warunek jest prawdziwy (true), czy fałszywy (false)? Dziesięć rund, coraz trudniejszych."; -"web_game_start" = "Zacznij grę"; -"web_game_round_fmt" = "Runda %@ z %@"; -"web_game_score_fmt" = "Punkty: %@"; -"web_game_true" = "prawda (true)"; -"web_game_false" = "fałsz (false)"; -"web_game_right" = "Dobrze!"; -"web_game_wrong" = "Niestety źle. Tak to się liczy:"; -"web_game_next" = "Dalej"; -"web_game_finish" = "Wynik"; -"web_game_done_fmt" = "%@ z %@ dobrze."; -"web_game_perfect" = "Wszystko dobrze – Ludwig jest pod wrażeniem!"; -"web_game_good" = "Świetnie! Przejrzyj błędy i spróbuj jeszcze raz."; -"web_game_practice" = "Trening czyni mistrza. Przeczytaj jeszcze raz tabelę prawdy i zagraj ponownie."; -"web_game_again" = "Zagraj ponownie"; -"web_game_wrong_short" = "Niestety źle."; -"web_game_practice_short" = "Praktyka czyni mistrza. Przeczytaj jeszcze raz wyjaśnienia i zagraj kolejną rundę."; -"web_game_clicks" = "Kliknięcia:"; -"web_game_nobody" = "Nikt nie słucha – nic się nie dzieje."; -"web_game_event_fmt" = "Kliknięcie „%@”: słuchacz to słyszy i wykonuje swój kod."; -"web_game_try" = "Teraz sam wypróbuj przyciski w oknie."; -"web_game_time_fmt" = "Zostało %@ s"; -"web_game_defused" = "✂️ Rozbrojona! Znalazłeś błąd."; -"web_game_boom_time" = "💥 Bum! Czas minął."; -"web_game_boom_wrong" = "💥 Bum! To była zła linia."; -"web_game_boom_part" = "💥 Bum! Dobra linia, ale błąd jest w innej części."; -"web_game_boom_wrong_part" = "💥 Bum! Błąd jest w innej linii."; -"web_game_bomb_parts_hint" = "Stuknij dokładnie w błędną część."; -"web_game_fix" = "Poprawna linia:"; -"web_game_order_hint" = "Stuknij kroki we właściwej kolejności."; -"web_debug_step" = "Krok"; -"web_debug_step_title" = "Krok: wykonaj jedną instrukcję i zatrzymaj się ponownie"; -"web_debug_continue" = "Dalej do następnego punktu przerwania"; -"web_debug_paused_fmt" = "Zatrzymano przed wierszem %d."; -"web_debug_gutter_title" = "Kliknij numer wiersza, aby ustawić lub usunąć punkt przerwania"; -"web_swap_to_code" = "Programowanie duże"; -"web_swap_to_editor" = "Diagram duży"; -"web_editor_resize" = "Zmień szerokość diagramu"; -"web_palette_resize" = "Zmień szerokość elementów"; -"web_palette_hide" = "Ukryj elementy"; -"web_palette_show" = "Pokaż elementy"; -"web_code_scopes" = "Kolorowe bloki kodu"; -"web_code_scopes_hint" = "Klasa, metoda, pętla, rozgałęzienie we własnych ramkach"; -"web_pseudocode" = "Pseudokod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Zmień nazwę zmiennej …"; -"web_rename_title" = "Zmień nazwę zmiennej"; -"web_rename_old" = "Nazwa"; -"web_rename_new" = "Nowa nazwa"; -"web_rename_hint" = "Zmienia każde miejsce o dokładnie tej pisowni (liczba to nie Liczba), w kodzie i w strukturogramie. Tekst w \"…\" i komentarze zostają."; -"web_rename_count_fmt" = "Występuje %d razy."; -"web_rename_exists_fmt" = "„%@” już istnieje: potem obie byłyby jedną zmienną."; -"web_rename_invalid" = "Nazwa zaczyna się od litery i zawiera tylko litery, cyfry i _."; -"web_rename_keyword_fmt" = "„%@” to słowo kluczowe Javy."; -"web_rename_apply" = "Zmień nazwę"; -"web_rename_cancel" = "Anuluj"; diff --git a/web/src/i18n/pt-PT.strings b/web/src/i18n/pt-PT.strings deleted file mode 100644 index 14917714..00000000 --- a/web/src/i18n/pt-PT.strings +++ /dev/null @@ -1,798 +0,0 @@ -/* Textos que só existem na versão web. Tudo o resto vem dos Localizable.strings da app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagrama estruturado VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exportar…"; -"web_export_title" = "Guardar como imagem ou PDF, ou imprimir"; -"web_export_png" = "Imagem (PNG)"; -"web_export_svg" = "Gráfico vetorial (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Ficheiro VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Falha na exportação: %@"; -"web_print_hint" = "Use “Exportar… → PDF” para guardar o diagrama e imprimi-lo a partir daí."; -"web_delete" = "Eliminar"; -"web_language_title" = "Idioma da interface"; -"web_simulation_start" = "Iniciar simulação"; -"web_simulation_end" = "Terminar simulação"; -"web_simulation_start_title" = "Executar o diagrama passo a passo"; -"web_simulation_end_title" = "Terminar a simulação e continuar a editar"; -"web_palette_hint" = "Arrastar para o diagrama"; -"web_tabs_label" = "Diagramas"; -"web_status_modified" = "alterado"; -"web_inspector_title" = "Inspetor de objetos"; -"web_inspector_help_drop" = "Arraste elementos da paleta para um bloco: em cima = antes, em baixo = depois, sobre “ø” = substituir. Os elementos existentes podem ser movidos com o rato."; -"web_inspector_help_inline" = "Faça duplo clique ou toque duplo num elemento para editar o texto diretamente no diagrama (Enter aplica, Esc cancela). Os nomes dos casos também."; -"web_inspector_help_files" = "Também pode largar ficheiros na janela."; -"web_case_name" = "Nome do caso"; -"web_case_name_placeholder" = "p. ex. 1, ok, default"; -"web_copied" = "Elemento copiado."; -"web_discard_message_fmt" = "As alterações a “%@” serão perdidas."; -"web_err_unreadable_fmt" = "“%@” não é um ficheiro VisuStruct legível (%@)."; -"web_saved_fmt" = "“%@” guardado."; -"web_saved_txt_fmt" = "Guardado como “%@”. Remova a extensão .txt para o utilizar."; -"web_drop_impossible" = "Não é possível largar aqui"; -"web_sim_delay" = "Pausa por passo"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Linguagem"; -"web_codegen_comments" = "Texto do diagrama como comentários"; -"web_codegen_empty_else" = "Gerar ramo else vazio no código"; -"web_copied_check" = "Copiado ✓"; -"web_copy_manual" = "Selecionado, prima Ctrl+C para copiar"; -"web_run_stopped_loop" = "Execução interrompida: possível ciclo infinito."; -"web_run_cancelled" = "Execução cancelada."; -"web_run_error" = "Erro durante a execução."; -"web_run_dropped_fmt" = "… %d linhas anteriores ocultas"; -"web_learn_title" = "Aprender"; -"web_learn_hide" = "Recolher a área de aprendizagem"; -"web_learn_show" = "Expandir a área de aprendizagem: lições, exercícios e consulta"; -"web_learn_lessons" = "Lições"; -"web_learn_tasks" = "Exercícios"; -"web_learn_reference" = "Consulta"; -"web_learn_intro" = "Aprende passo a passo, com exercícios. O teu progresso fica guardado neste navegador."; -"web_learn_tasks_intro" = "Pequenos projetos sem instruções. Cada um indica a lição que deves terminar primeiro."; -"web_learn_step_of_fmt" = "Passo %d de %d"; -"web_learn_lesson_fmt" = "Lição %d"; -"web_learn_all_lessons" = "Todas as lições"; -"web_learn_all_tasks" = "Todos os exercícios"; -"web_learn_back" = "Voltar"; -"web_learn_next" = "Seguinte"; -"web_learn_finish" = "Terminar a lição"; -"web_learn_done" = "Concluída"; -"web_learn_after_fmt" = "Depois da lição %d: %@"; -"web_learn_level_fmt" = "Dificuldade %d de 3"; -"web_learn_exercise" = "Exercício"; -"web_learn_brag" = "Curiosidades com o Ludwig"; -"web_learn_solved" = "resolvido"; -"web_learn_open_exercise" = "Abrir no editor"; -"web_learn_solve_in_editor" = "Resolve este exercício na área %@."; -"web_learn_solve_in_code" = "Resolve este exercício na área %@."; -"web_learn_solve_in_both" = "Resolve este exercício nas áreas %1$@ e %2$@."; -"web_learn_open_in_editor" = "Abrir no editor"; -"web_learn_replay" = "Repetir animação"; -"web_learn_graphic_labels" = "👆 Clica na imagem para mostrar ou esconder as legendas"; -"web_learn_check" = "Verificar"; -"web_learn_hint" = "Dica"; -"web_learn_reset" = "Recomeçar"; -"web_learn_reset_confirm" = "Queres mesmo recomeçar?"; -"web_learn_solution" = "Mostrar a solução"; -"web_learn_hint_more" = "Outra dica"; -"web_learn_hint_fmt" = "Dica %d de %d"; -"web_learn_teacher" = "Para professores"; -"web_learn_teacher_intro" = "O código de professor mostra as soluções de todos os exercícios. O navegador memoriza-o."; -"web_learn_teacher_code" = "Código de professor"; -"web_learn_teacher_unlock" = "Desbloquear"; -"web_learn_teacher_wrong" = "O código não está correto."; -"web_learn_teacher_on" = "O modo de professor está ativo: as soluções de todos os exercícios estão visíveis."; -"web_learn_teacher_off" = "Sair do modo de professor"; -"web_learn_open_first" = "Abre primeiro o exercício no editor e constrói lá o teu estrutograma."; -"web_learn_empty" = "O teu estrutograma ainda está vazio. Arrasta primeiro elementos da área «Elementos» para ele."; -"web_learn_passed" = "Muito bem! O teu estrutograma faz exatamente o que deve."; -"web_learn_failed" = "Ainda não está bem. Compara a saída esperada com a tua:"; -"web_learn_missing_fmt" = "O teu estrutograma ainda precisa de: %@"; -"web_learn_input_fmt" = "Entrada: %@"; -"web_learn_no_input" = "Sem entrada"; -"web_learn_expected" = "Esperado"; -"web_learn_actual" = "A tua saída"; -"web_learn_missing_input" = "O teu estrutograma pede mais entradas do que o previsto."; -"web_learn_unused_input" = "O teu estrutograma não pede todas as entradas."; -"web_learn_missing_line_break" = "Quase! Só falta uma quebra de linha. System.out.print continua a escrever na mesma linha, System.out.println começa depois uma linha nova."; -"web_learn_too_many_steps" = "Parado após 100 000 passos. Há um ciclo que nunca termina?"; -"web_learn_score_fmt" = "%d de %d certas"; -"web_learn_cloze_hint" = "Escreve as palavras que faltam nos espaços e carrega em “Verificar”."; -"web_learn_cloze_score_fmt" = "%d de %d espaços certos"; -"web_learn_cloze_all_right" = "Todos os espaços certos!"; -"web_learn_cloze_solution" = "Mostrar solução"; -"web_learn_cloze_again" = "De novo"; -"web_learn_pap_hint" = "Toca num elemento à direita e depois no seu lugar no fluxograma. Também o podes arrastar. Tocar num lugar ocupado devolve o elemento."; -"web_learn_pap_score_fmt" = "%d de %d elementos certos"; -"web_learn_pap_all_right" = "Tudo certo! O teu fluxograma está pronto."; -"web_learn_pap_missing_fmt" = "Ainda faltam %d elementos."; -"web_learn_pap_empty" = "Lugar vazio"; -"web_learn_pap_yes" = "sim"; -"web_learn_pap_no" = "não"; -"web_learn_pap_legend_oval" = "Início ou fim"; -"web_learn_pap_legend_rect" = "Instrução"; -"web_learn_pap_legend_diamond" = "Decisão: pergunta com sim e não"; -"web_learn_pap_legend_para" = "Entrada ou saída"; -"web_learn_review" = "Rever"; -"web_learn_terms" = "Termos técnicos"; -"web_inspector_resize" = "Alterar a altura do inspetor de objetos: arrastar ou deslizar a linha para cima ou para baixo (duplo clique: padrão)"; -"web_learn_resize" = "Alterar a largura da área de aprendizagem (duplo clique: repor)"; -"web_learn_competences_intro" = "No fim desta lição consigo …"; -"web_learn_rating_sun" = "consigo com segurança"; -"web_learn_rating_cloud" = "em parte"; -"web_learn_rating_umbrella" = "ainda não"; -"web_learn_review_title" = "A tua lista de verificação em comparação"; -"web_learn_review_hint" = "Aqui a tua autoavaliação e o teste não coincidem. Lê o passo outra vez e volta a avaliar-te na lista de verificação."; -"web_learn_review_match" = "A tua autoavaliação corresponde ao teu teste. Muito bem!"; -"web_learn_review_overrated_fmt" = "Escolheste ☀️, mas a pergunta %@ estava errada."; -"web_learn_review_underrated" = "Todas as perguntas sobre isto estavam certas. Sabes mais do que pensas!"; -"web_learn_review_unrated" = "ainda não avaliado"; -"web_learn_back_to_checklist" = "Voltar à lista de verificação"; -"web_learn_step_fmt" = "Passo %d: %@"; -"web_sim_call_arguments_fmt" = "A chamada não corresponde ao cabeçalho de %@. Verifica os valores entre parênteses."; -"web_sim_missing_return_fmt" = "%@ terminou sem devolver um valor com return."; -"web_sim_file_not_found_fmt" = "Não existe nenhum ficheiro chamado %@."; -"web_sim_end_of_file_fmt" = "O ficheiro %@ não tem mais linhas."; -"web_sim_null_value_fmt" = "%@ é null."; -"web_sim_unknown_variable_case_fmt" = "Variável desconhecida: %1$@. O Java distingue maiúsculas de minúsculas: querias dizer %2$@?"; -"web_sim_written_file_fmt" = "%@ (guardado)"; -"web_site_home" = "Página inicial"; -"web_site_imprint" = "Contacto"; -"web_site_privacy" = "Privacidade"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Código"; -"web_live_hide" = "Ocultar código"; -"web_scale_canvas" = "Tamanho do texto: estrutograma"; -"web_scale_code" = "Tamanho do texto: Código"; -"web_live_indent" = "Indentar"; -"web_live_indent_title" = "Indentar e formatar o código: cada } na sua própria linha, espaços à volta dos operadores, quatro espaços por nível (linhas selecionadas, senão tudo)"; -"web_live_show" = "Mostrar código"; -"web_live_resize" = "Alterar a largura da área de código (duplo clique: repor)"; -"web_live_hint" = "O código cresce com o teu diagrama. Cinzento = marcador: escreve no inspetor de objetos e o teu texto aparece aí. Clica numa linha para selecionar o elemento."; -"web_live_ph_selector" = "variável"; -"web_live_ph_code" = "O teu código"; -"web_live_ph_end" = "fim"; -"web_live_language" = "Linguagem de programação"; -"web_live_java_class" = "Como classe"; -"web_live_java_class_hint" = "Nome da classe = título do diagrama"; -"web_live_java_main" = "Com método main"; -"web_settings_display" = "Apresentação"; -"web_live_swing" = "Java verdadeiro (com Swing)"; -"web_live_swing_hint" = "Java verdadeiro com janelas (CheerpJ). Descarrega cerca de 40 MB uma vez, na primeira execução."; -"web_live_swing_windows" = "Janelas Java"; -"web_live_swing_hide" = "Fechar janelas Java"; -"web_live_swing_loading" = "A carregar o Java (cerca de 40 MB na primeira vez) …"; -"web_live_swing_compiling" = "A compilar …"; -"web_live_swing_running" = "O programa está a correr."; -"web_live_swing_compile_failed" = "Erro de compilação, vê acima."; -"web_live_swing_failed" = "Não foi possível carregar o Java. Verifica a ligação à internet."; -"web_live_hint_edit" = "Escreve aqui código Java e o diagrama constrói-se sozinho. Ou trabalha no diagrama e o código cresce com ele. Cinzento = marcador, basta escrever por cima."; -"web_live_edit_label" = "Código Java"; -"web_parse_line_fmt" = "Linha %d: %@"; -"web_parse_missing" = "Ainda falta aqui: %@"; -"web_parse_extra" = "Está a mais: %@"; -"web_parse_unclosed_string" = "Falta aqui o %@ de fecho."; -"web_parse_else_without_if" = "Este “else” não tem um “if” correspondente."; -"web_parse_case_outside_switch" = "“case” e “default” pertencem a um “switch”."; -"web_parse_missing_do_while" = "Depois de “do { … }” ainda falta “while (condição);”."; -"web_parse_more_methods" = "Por agora só conta um método aqui. Cria outros subprogramas em separadores próprios."; -"web_parse_missing_import" = "Falta %@ mesmo no início. Sem esta linha, o Java não conhece o Scanner."; -"web_parse_missing_scanner" = "Falta antes %@ Sem esta linha, o Scanner não existe."; -"web_parse_unknown_type" = "O Java não conhece este tipo de dados. Querias dizer %@?"; -"web_quickfix_insert_fmt" = "Sugestão: inserir %@"; -"web_quickfix_replace_fmt" = "Sugestão: %1$@ em vez de %2$@"; -"web_parse_wrong_case" = "O Java distingue maiúsculas de minúsculas. O correto é %@."; -"web_editor_title" = "Diagrama"; -"web_editor_show" = "Mostrar diagrama"; -"web_editor_hide" = "Ocultar diagrama"; -"web_keywords_title" = "Palavras-chave do código"; -"web_keywords_hint" = "if, while, for em vez de Decisão, Ciclo …"; -"web_palette_compact" = "Elementos sempre compactos"; -"web_palette_compact_hint" = "Símbolo com o nome pequeno por baixo; caso contrário, só com a coluna estreita"; -"web_about_version_fmt" = "Versão %@"; -"web_about_software" = "Software utilizado"; -"web_about_software_text" = "O editor de código usa o CodeMirror e o Lezer (licença MIT). Com o suporte para Swing, o VisuStruct carrega o CheerpJ da Leaning Technologies (licença Community) e o javac do OpenJDK."; -"web_about_licenses" = "Textos das licenças"; -"web_live_run" = "Executar"; -"web_live_run_title" = "Executar o programa: as saídas e entradas aparecem em baixo"; -"web_live_run_finished" = "Programa terminado."; -"web_live_run_stopped" = "Parado."; -"web_console" = "Consola"; -"web_console_hide" = "Ocultar consola"; -"web_console_resize" = "Alterar a altura da consola"; -"web_diagram_tools" = "Ferramentas do diagrama"; -"web_files_title" = "Ficheiros"; -"web_files_show" = "Mostrar ficheiros"; -"web_files_hide" = "Ocultar ficheiros"; -"web_files_resize" = "Alterar a largura dos ficheiros"; -"web_files_new_folder" = "Nova pasta"; -"web_files_switch" = "Escolher outra pasta"; -"web_files_delete" = "Eliminar"; -"web_files_in_browser" = "No armazenamento deste navegador"; -"web_files_save_hint_fmt" = "Guardar coloca o programa em %@."; -"web_files_empty" = "Ainda nada guardado. Ao guardar, o VisuStruct cria aqui uma pasta com a data de hoje."; -"web_files_reconnect_hint" = "Depois de recarregar, o navegador pergunta outra vez se o VisuStruct pode usar a tua pasta."; -"web_files_reconnect_fmt" = "Abrir a pasta “%@”"; -"web_files_connect_hint" = "Escolhe uma pasta como área de trabalho, de preferência uma pasta nova “VisuStruct-Ficheiros” nos teus Documentos. O VisuStruct cria lá uma pasta para cada dia e guarda o diagrama e o código."; -"web_files_connect" = "Escolher pasta"; -"web_files_browser_hint" = "Este navegador não consegue abrir pastas no dispositivo (por exemplo no iPad). Os teus ficheiros ficam no armazenamento deste navegador."; -"web_files_browser" = "Guardar no navegador"; -"web_files_delete_confirm_fmt" = "Eliminar mesmo “%@”?"; -"web_files_folder_prompt" = "Nome da nova pasta:"; -"web_files_default_name" = "Programa"; -"web_files_saved_fmt" = "Guardado: %@"; -"web_files_create" = "Criar"; -"web_learn_ludwig_tip" = "Dica do Ludwig"; -"web_game_title" = "Jogo: Verdadeiro ou falso?"; -"web_game_intro" = "O Ludwig mostra-te valores e uma condição. A condição é verdadeira (true) ou falsa (false)? Dez rondas, cada vez mais difíceis."; -"web_game_start" = "Começar o jogo"; -"web_game_round_fmt" = "Ronda %@ de %@"; -"web_game_score_fmt" = "Pontos: %@"; -"web_game_true" = "verdadeiro (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "Certo!"; -"web_game_wrong" = "Não exatamente. É assim que se calcula:"; -"web_game_next" = "Seguinte"; -"web_game_finish" = "Resultado"; -"web_game_done_fmt" = "%@ de %@ certas."; -"web_game_perfect" = "Tudo certo – o Ludwig está mesmo impressionado!"; -"web_game_good" = "Muito bem! Revê os teus erros e tenta outra vez."; -"web_game_practice" = "A prática faz o mestre. Lê outra vez a tabela de verdade acima e joga mais uma ronda."; -"web_game_again" = "Jogar outra vez"; -"web_game_wrong_short" = "Infelizmente errado."; -"web_game_practice_short" = "A prática leva à perfeição. Lê outra vez as explicações e joga mais uma ronda."; -"web_game_clicks" = "Cliques:"; -"web_game_nobody" = "Ninguém está a ouvir – não acontece nada."; -"web_game_event_fmt" = "Clique em “%@”: o ouvinte ouve e executa o seu código."; -"web_game_try" = "Agora experimenta tu os botões da janela."; -"web_game_time_fmt" = "Faltam %@ s"; -"web_game_defused" = "✂️ Desativada! Encontraste o erro."; -"web_game_boom_time" = "💥 Bum! O tempo acabou."; -"web_game_boom_wrong" = "💥 Bum! Era a linha errada."; -"web_game_boom_part" = "💥 Bum! A linha está certa, mas o erro está noutra parte dela."; -"web_game_boom_wrong_part" = "💥 Bum! O erro está noutra linha."; -"web_game_bomb_parts_hint" = "Toca exatamente na parte errada."; -"web_game_fix" = "A linha correta:"; -"web_game_order_hint" = "Toca nos passos pela ordem certa."; -"web_debug_step" = "Passo a passo"; -"web_debug_step_title" = "Passo a passo: executar uma instrução e parar outra vez"; -"web_debug_continue" = "Continuar até ao próximo ponto de paragem"; -"web_debug_paused_fmt" = "Parado antes da linha %d."; -"web_debug_gutter_title" = "Clica num número de linha para pôr ou tirar um ponto de paragem"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Disco de César"; -"web_learn_caesar_hint" = "Roda o disco interior ou toca em −1 e +1. Por fora está o texto simples, por dentro o texto cifrado."; -"web_learn_caesar_key_fmt" = "Chave %d"; -"web_learn_caesar_plain" = "Texto simples"; -"web_learn_caesar_secret" = "Texto cifrado"; -"web_learn_caesar_encrypt" = "Cifrar"; -"web_learn_caesar_decrypt" = "Decifrar"; -"web_learn_caesar_back" = "Rodar o disco uma letra para trás"; -"web_learn_caesar_forward" = "Rodar o disco uma letra para a frente"; -"web_learn_rescue_station_fmt" = "Estação %@ de %@: %@"; -"web_learn_rescue_number" = "O teu número"; -"web_learn_rescue_not_number" = "Escreve um número inteiro."; -"web_learn_rescue_wrong" = "Ainda não. Lê o código outra vez, carácter a carácter."; -"web_learn_rescue_letter_moved" = "A letra vai para as palavras cruzadas."; -"web_learn_rescue_alphabet" = "O teu número transforma-se numa letra:"; -"web_learn_rescue_to_crossword" = "Para as palavras cruzadas"; -"web_learn_rescue_crossword" = "As palavras cruzadas"; -"web_learn_rescue_across" = "→ Horizontais"; -"web_learn_rescue_down" = "↓ Verticais"; -"web_learn_rescue_cell_fmt" = "Quadrado linha %@, coluna %@"; -"web_learn_rescue_keyword" = "Palavra secreta:"; -"web_learn_rescue_cw_wrong" = "Os quadrados marcados a vermelho ainda não estão certos."; -"web_learn_rescue_cw_done" = "Resolvido! Os quadrados amarelos formam a palavra secreta."; -"web_learn_rescue_to_gate" = "Para o portão"; -"web_learn_rescue_gate" = "O portão"; -"web_learn_rescue_word" = "Palavra secreta"; -"web_learn_rescue_open" = "Abrir o portão"; -"web_learn_rescue_gate_case" = "Quase! O equals distingue maiúsculas de minúsculas. Escreve a palavra exatamente como nas palavras cruzadas."; -"web_learn_rescue_gate_wrong" = "O portão continua fechado. Olha outra vez para os quadrados amarelos."; -"web_learn_rescue_freed" = "🎉 O portão está aberto!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "Com o que queres trabalhar?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: estrutogramas e Java com o curso completo. Podes mudar a qualquer momento na roda dentada."; -"web_visubot_world_visubot_hint" = "VisuBot: programar o robô mBot2, com lições e tarefas próprias. Podes mudar a qualquer momento na roda dentada."; -"web_visubot_connect" = "Ligar o mBot2 real"; -"web_visubot_upload_button" = "Transferir"; -"web_visubot_blocks" = "Blocos"; -"web_visubot_blocks_title" = "Mostrar o diagrama como blocos"; -"web_visubot_blocks_back" = "Diagrama"; -"web_visubot_blocks_back_title" = "Voltar ao diagrama"; -"web_visubot_blocks_start" = "Quando o programa começa"; -"web_visubot_blocks_empty" = "Aqui vai um bloco"; -"web_visubot_blocks_hint" = "O mesmo programa em blocos. Arrasta blocos de “Elementos”, muda-os de lugar ou deita-os no lixo. Clica num número ou texto nos campos brancos para o alterar no próprio bloco; o resto altera-se em baixo, no Inspetor de objetos. Arrasta os sensores para uma condição ou mesmo para o campo que devem substituir. Arrasta operadores e variáveis da mesma forma para um campo ou uma condição."; -"web_visubot_blocks_trash" = "Lixo"; -"web_visubot_blocks_count" = "contar com"; -"web_visubot_blocks_count_from" = "de"; -"web_visubot_blocks_count_below" = "até abaixo de"; -"web_visubot_blocks_count_to" = "até"; -"web_visubot_blocks_count_above" = "até acima de"; -"web_visubot_blocks_count_step" = "em passos de"; -"web_visubot_blocks_repeat" = "repetir"; -"web_visubot_blocks_if" = "se"; -"web_visubot_blocks_else" = "senão"; -"web_visubot_blocks_while" = "enquanto"; -"web_visubot_blocks_endless" = "repetir sempre"; -"web_visubot_blocks_do" = "faz"; -"web_visubot_blocks_not" = "não"; -"web_visubot_blocks_and" = "e"; -"web_visubot_blocks_or" = "ou"; -"web_visubot_blocks_break" = "sair do ciclo"; -"web_visubot_blocks_repeat_times" = "vezes"; -"web_visubot_blocks_random_from" = "número aleatório de"; -"web_visubot_blocks_random_to" = "a"; -"web_visubot_operators" = "Operadores"; -"web_visubot_variables" = "Variáveis"; -"web_visubot_control" = "Controlo"; -"web_visubot_el_repeat" = "repetir 10 vezes"; -"web_visubot_upload_title" = "Transferir o programa para o mBot2"; -"web_visubot_upload_step1" = "Liga o mBot2 com o cabo USB e acende-o. Só funciona no Chrome ou no Edge."; -"web_visubot_upload_step2" = "Clica em “Transferir agora” e escolhe “USB Serial”. O mBot2 guarda o teu programa e reinicia."; -"web_visubot_upload_step3" = "Desliga o cabo e põe o mBot2 na partida. Quando carregas no botão A, o programa arranca. Fica guardado, mesmo depois de desligar."; -"web_visubot_upload_start" = "Transferir agora"; -"web_visubot_upload_busy" = "A transferir … não desligues o cabo."; -"web_visubot_upload_done" = "Pronto! Desliga o cabo, põe o mBot2 na partida e carrega no botão A."; -"web_visubot_upload_failed" = "A transferência não funcionou. Reinicia o mBot2 e tenta outra vez."; -"web_visubot_upload_label" = "A = Início"; -"web_visubot_upload_error_input" = "O teu programa pede uma entrada. O mBot2 não tem teclado, por isso não funciona."; -"web_visubot_upload_error_files" = "O teu programa trabalha com ficheiros. Isso não funciona no mBot2."; -"web_visubot_upload_error_empty" = "O teu programa ainda está vazio."; -"web_visubot_disconnect" = "Desligar"; -"web_visubot_connecting" = "A ligar … (o CyberPi reinicia brevemente)"; -"web_visubot_connected" = "O mBot2 real conduz também"; -"web_visubot_no_serial" = "mBot2 real: só no Chrome ou Edge num computador."; -"web_visubot_link_error" = "Ligação perdida"; -"web_visubot_world" = "Mundo VisuBot"; -"web_visubot_no_3d" = "A vista 3D não consegue arrancar neste navegador."; -"web_visubot_distance" = "Distância"; -"web_visubot_line" = "Linha"; -"web_visubot_bumps" = "bateu na parede"; -"web_visubot_el_forward" = "andar para a frente"; -"web_visubot_el_backward" = "andar para trás"; -"web_visubot_el_right" = "rodar à direita"; -"web_visubot_el_left" = "rodar à esquerda"; -"web_visubot_el_led" = "LEDs ligados"; -"web_visubot_el_tone" = "tocar um som"; -"web_visubot_drive" = "Percurso"; -"web_visubot_depth_fmt" = "Os ciclos têm de estar encaixados %d vezes."; -"web_visubot_tap_part" = "Roda o mBot2 com o rato ou o dedo. Toca numa peça para ver o nome."; -"web_visubot_quiz_right" = "Certo!"; -"web_visubot_quiz_wrong_fmt" = "Isso é: %@. Tenta outra vez."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Ecrã"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Botões A, B e Home"; -"web_visubot_part_leds" = "LEDs RGB"; -"web_visubot_part_lightSensor" = "Sensor de luz"; -"web_visubot_part_microphone" = "Microfone"; -"web_visubot_part_shield" = "Shield do mBot2 com bateria"; -"web_visubot_part_powerSwitch" = "Interruptor"; -"web_visubot_part_motors" = "Motores com encoder"; -"web_visubot_part_wheels" = "Rodas"; -"web_visubot_part_ultrasonic" = "Sensor ultrassónico"; -"web_visubot_part_lineSensor" = "Sensor de linha (quad RGB)"; -"web_visubot_part_caster" = "Roda livre"; -"web_visubot_part_chassis" = "Chassi"; -"web_visubot_world_fmt" = "Mundo %d"; -"web_visubot_goal_reached" = "Objetivo alcançado"; -"web_visubot_goal_missed" = "Objetivo não alcançado"; -"web_visubot_commands" = "Comandos do mBot2"; -"web_visubot_sensors" = "Sensores do mBot2"; -"web_visubot_sensor_distance" = "Distância"; -"web_visubot_sensor_line" = "Linha"; -"web_visubot_el_led_off" = "LEDs desligados"; -"web_visubot_el_led_color" = "cor do LED"; -"web_visubot_el_wait" = "esperar um pouco"; -"web_visubot_el_emotion" = "mostrar um sentimento"; -"web_visubot_el_text" = "mostrar texto"; -"web_visubot_sensor_color" = "A cor é vermelha?"; -"web_visubot_sensor_button_a" = "Botão A premido?"; -"web_visubot_sensor_button_b" = "Botão B premido?"; -"web_visubot_sensor_joy_up" = "Joystick para cima?"; -"web_visubot_sensor_joy_down" = "Joystick para baixo?"; -"web_visubot_sensor_joy_left" = "Joystick para a esquerda?"; -"web_visubot_sensor_joy_right" = "Joystick para a direita?"; -"web_visubot_sensor_brightness" = "Luminosidade"; -"web_visubot_sensor_loudness" = "Volume do som"; -"web_visubot_sensor_pitch" = "Inclinação frente/trás"; -"web_visubot_sensor_roll" = "Inclinação lateral"; -"web_visubot_sensor_shake" = "Abanado?"; -"web_visubot_el_drive" = "rodas separadas"; -"web_visubot_el_stop" = "parar"; -"web_visubot_el_timer" = "cronómetro a 0"; -"web_visubot_sensor_line_left" = "Linha à esquerda?"; -"web_visubot_sensor_line_right" = "Linha à direita?"; -"web_visubot_sensor_timer" = "Cronómetro (s)"; -"web_visubot_sensor_battery" = "Bateria (%)"; -"web_visubot_actions" = "Comandos"; -"web_visubot_led_on" = "LEDs ligados"; -"web_visubot_led_off" = "LEDs desligados"; -"web_visubot_tone_yes" = "som"; -"web_visubot_tone_no" = "sem som"; -"web_visubot_controls" = "Experimentar os sensores"; -"web_visubot_controls_hint" = "Sem um mBot2 real: define aqui o que os sensores medem. Botões, joystick e abanar duram um segundo."; -"web_visubot_shake" = "abanar"; -"web_visubot_check" = "Testar o mBot2"; -"web_visubot_check_hint" = "Experimenta cada atuador uma vez. Os valores dos sensores atualizam-se em direto: põe a mão à frente do sensor ultrassónico, carrega nos botões, inclina ou abana o mBot2. Levanta-o para testar as rodas."; -"web_visubot_check_forward" = "Rodas 1 s para a frente"; -"web_visubot_check_spin" = "Rodar no lugar 1 s"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Onde guardas o teu trabalho?"; -"web_learn_start_hint" = "O teu progresso e os teus programas ficam numa pasta no teu dispositivo. O progresso fica no ficheiro learnProgress.json."; -"web_learn_start_continue_fmt" = "Continuar com “%@”"; -"web_learn_start_open" = "Abrir pasta existente"; -"web_learn_start_create_fmt" = "Criar nova pasta “%@”"; -"web_learn_start_create_hint" = "Depois escolhes onde fica, por exemplo em “Documentos”."; -"web_learn_start_folder" = "VisuStruct-Ficheiros"; -"web_learn_start_without" = "Continuar sem pasta"; -"web_learn_start_without_hint" = "Assim o teu progresso perde-se ao fechar a página."; -"web_visubot_preview_title" = "mBot2 ao vivo"; -"web_visubot_preview_hint" = "Mostra logo para onde o teu programa leva o mBot2."; -"web_visubot_preview_sim_title" = "Simulação"; -"web_visubot_preview_sim_hint" = "O mBot2 anda aqui enquanto a simulação corre."; -"web_visubot_world_in_preview" = "O mBot2 anda na janela «Simulação»."; -"web_visubot_preview_play" = "Reproduzir o percurso"; -"web_visubot_preview_stop" = "Parar"; -"web_visubot_preview_hide" = "Recolher mBot2 ao vivo"; -"web_visubot_preview_show" = "Mostrar mBot2 ao vivo"; -"web_visubot_preview_resize" = "Alterar o tamanho de mBot2 ao vivo"; -"web_visubot_preview_stack" = "Por baixo do código na área Código"; -"web_visubot_preview_side" = "Como coluna própria"; -"web_visubot_preview_distance_fmt" = "Percurso: %d cm"; -"web_visubot_preview_input" = "precisa de uma entrada, inicia a simulação para isso"; -"web_visubot_preview_limit" = "demora muito (ciclo infinito?), mostra-se o início"; -"web_visubot_group_drive" = "Andar e rodar"; -"web_visubot_group_wheels" = "Rodas separadas"; -"web_visubot_group_leds" = "Luz"; -"web_visubot_group_sound" = "Som"; -"web_visubot_group_screen" = "Ecrã"; -"web_visubot_group_emotion" = "Emoções"; -"web_visubot_group_wait" = "Esperar e cronómetro"; -"web_visubot_group_distance" = "Distância"; -"web_visubot_group_line" = "Linha"; -"web_visubot_group_color" = "Cores"; -"web_visubot_group_buttons" = "Botões e joystick"; -"web_visubot_group_light" = "Luz e volume"; -"web_visubot_group_tilt" = "Inclinação e abanar"; -"web_visubot_group_timer" = "Cronómetro e bateria"; -"web_swap_to_code" = "Código grande, diagrama estreito"; -"web_swap_to_editor" = "Diagrama grande, código estreito"; -"web_editor_resize" = "Alterar a largura do diagrama"; -"web_palette_resize" = "Alterar a largura dos elementos (duplo clique: repor)"; -"web_palette_hide" = "Ocultar elementos"; -"web_palette_show" = "Mostrar elementos"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: programar classes em Java e vê-las ou desenhá-las como diagrama de classes (a partir do 12.º ano). Podes mudar a qualquer momento na roda dentada."; -"web_visuobject_subtitle" = "Diagrama de classes"; -"web_visuobject_code" = "Código Java"; -"web_visuobject_diagram" = "Diagrama de classes"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Nova interface no diagrama e no código"; -"web_visuobject_layout" = "Reorganizar"; -"web_visuobject_layout_tip" = "Organizar todas as classes automaticamente: superclasses em cima"; -"web_visuobject_add_field" = "novo atributo …"; -"web_visuobject_add_method" = "novo método …"; -"web_visuobject_delete_class" = "Apagar classe"; -"web_visuobject_new_class_name" = "NovaClasse"; -"web_visuobject_new_interface_name" = "NovaInterface"; -"web_visuobject_example" = "Exemplo"; -"web_visuobject_example_tip" = "Carregar o projeto de exemplo Zoo: Tier e Hund no package tiere, Besitzer em personen"; -"web_visuobject_new_tip" = "Novo: projeto vazio (Anular recupera o anterior)"; -"web_visuobject_empty" = "Ainda não há nenhuma classe. Clica em «+ Classe» à esquerda ou escreve, por exemplo, «public class Cao { }» num ficheiro."; -"web_visuobject_edit_hint" = "Duplo clique para alterar. Escreve como em UML: «- nome: String» ou «+ ladrar(): void». Deixar vazio apaga."; -"web_visuobject_member_label" = "Atributo ou método em notação UML"; -"web_visuobject_name_label" = "Nome da classe"; -"web_visuobject_bad_name" = "«%@» não é um nome válido ou já existe."; -"web_visuobject_bad_line" = "Não percebo «%@». Exemplo: - nome: String ou + ladrar(): void"; -"web_visuobject_open_failed" = "Não foi possível ler o ficheiro."; -"web_visuobject_field_ph" = "- nome: String"; -"web_visuobject_seq_diagram" = "Diagrama de sequência"; -"web_visuobject_seq_empty" = "O diagrama de sequência surge ao depurar: clica em “Depurar” em cima e percorre o programa passo a passo."; -"web_visuobject_seq_no_calls" = "Ainda não há chamadas dos teus métodos."; -"web_visuobject_seq_truncated" = "… encurtado: a partir daqui há chamadas demais."; -"web_visuobject_seq_hint" = "Cada seta é uma chamada de um dos teus métodos; a seta tracejada traz o valor de retorno. Clica numa seta para ver a linha no código."; -"web_visuobject_dbg" = "Depurar"; -"web_visuobject_dbg_tip" = "Executar o programa passo a passo. Ponto de paragem: clica no número da linha. O diagrama de objetos mostra os objetos na memória."; -"web_visuobject_dbg_continue" = "Continuar"; -"web_visuobject_dbg_continue_tip" = "Continuar até ao próximo ponto de paragem (F8)"; -"web_visuobject_dbg_pause" = "Pausa"; -"web_visuobject_dbg_pause_tip" = "Pausar o programa"; -"web_visuobject_dbg_over" = "Passo"; -"web_visuobject_dbg_over_tip" = "Próxima linha; os métodos correm até ao fim (F6)"; -"web_visuobject_dbg_into" = "Entrar"; -"web_visuobject_dbg_into_tip" = "Entrar no método chamado (F5)"; -"web_visuobject_dbg_out" = "Sair"; -"web_visuobject_dbg_out_tip" = "Terminar o método e voltar a quem o chamou (F7)"; -"web_visuobject_dbg_input" = "Entrada para o Scanner, depois Enter"; -"web_visuobject_dbg_running" = "Depurador: o programa está a correr …"; -"web_visuobject_dbg_waiting" = "Depurador: o programa espera pela tua entrada."; -"web_visuobject_dbg_paused_fmt" = "Depurador: parado em %1$@, linha %2$@"; -"web_visuobject_dbg_exception" = "O programa terminou com uma exceção."; -"web_visuobject_dbg_code_changed" = "Código alterado: depurador terminado."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Pilha de chamadas"; -"web_visuobject_dbg_vars" = "Variáveis"; -"web_visuobject_dbg_var_name" = "Nome"; -"web_visuobject_dbg_var_type" = "Tipo"; -"web_visuobject_dbg_no_vars" = "Sem variáveis"; -"web_visuobject_dbg_err_syntax" = "%1$@, linha %2$@: o depurador não entende isto (%3$@). ▶ Executar mostra a mensagem do Java."; -"web_visuobject_dbg_err_symbol" = "%1$@, linha %2$@: %3$@ é desconhecido."; -"web_visuobject_dbg_err_import" = "%1$@, linha %2$@: falta %3$@ mesmo no início. Sem esta linha, o Java não conhece a classe."; -"web_visuobject_dbg_err_typename" = "%1$@, linha %2$@: o Java não conhece este tipo de dados. Querias dizer %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, linha %2$@: o depurador ainda não consegue %3$@. ▶ Executar corre o programa em Java real."; -"web_visuobject_dbg_err_type" = "%1$@, linha %2$@: algo não encaixa aqui (%3$@)."; -"web_visuobject_obj_diagram" = "Diagrama de objetos"; -"web_visuobject_obj_new" = "Novo objeto da classe"; -"web_visuobject_obj_add" = "Objeto"; -"web_visuobject_obj_empty" = "Ainda sem objetos. Escolhe uma classe em cima e clica em “+ Objeto”."; -"web_visuobject_obj_no_classes" = "Cria primeiro uma classe (não abstrata) no diagrama de classes."; -"web_visuobject_obj_hint" = "Clica num objeto para mudar aqui o nome, a classe e os valores. Arrasta a pega até outro objeto para os ligar através de um atributo de referência."; -"web_visuobject_obj_live_hint" = "Ao vivo do depurador: é assim que os objetos estão agora na memória."; -"web_visuobject_obj_link" = "Arrastar até outro objeto: definir referência"; -"web_visuobject_obj_name" = "Nome"; -"web_visuobject_obj_class" = "Classe"; -"web_visuobject_obj_attribute" = "Atributo"; -"web_visuobject_obj_value" = "Valor"; -"web_visuobject_obj_delete" = "Apagar objeto"; -"web_visuobject_obj_bad_name" = "“%@” não é um nome válido para um objeto."; -"web_visuobject_obj_name_taken" = "Já existe um objeto “%@”."; -"web_visuobject_obj_no_ref" = "%1$@ não tem nenhum atributo que possa apontar para %2$@."; -"web_visuobject_tool_note" = "Nota"; -"web_visuobject_tool_note_tip" = "Nota: clica no diagrama de classes e escreve o texto. Arrasta a pega até uma classe para uma linha tracejada."; -"web_visuobject_note_ph" = "Nota …"; -"web_visuobject_note_text" = "Texto"; -"web_visuobject_note_attached" = "Ligada a"; -"web_visuobject_note_attach" = "Arrastar até uma classe: linha tracejada"; -"web_visuobject_note_delete" = "Apagar nota"; -"web_visuobject_run" = "Executar"; -"web_visuobject_run_tip" = "Executar o programa com Java verdadeiro: todas as classes são compiladas, é iniciada a classe com main (de preferência a aberta)."; -"web_visuobject_stop" = "Parar"; -"web_visuobject_stop_tip" = "Terminar o programa e fechar as suas janelas"; -"web_visuobject_stopped" = "Programa terminado."; -"web_visuobject_no_main" = "Nenhuma classe com public static void main(String[] args) encontrada."; -"web_visuobject_running_fmt" = "%@ está a correr …"; -"web_visuobject_console_clear" = "Limpar"; -"web_visuobject_section_fields" = "Atributos"; -"web_visuobject_section_refs" = "Atributos de referência"; -"web_visuobject_section_ctors" = "Construtor"; -"web_visuobject_section_methods" = "Métodos"; -"web_visuobject_insp_sections" = "Inserir comentários de secção"; -"web_visuobject_insp_sort" = "Ordenar por secções"; -"web_visuobject_insp_sections_tip" = "Escreve // Atributos, // Atributos de referência, // Construtor e // Métodos na classe e ordena tudo por baixo. O que for criado no diagrama de classes vai logo para o sítio certo."; -"web_visuobject_insp_doc" = "Descrição (Javadoc)"; -"web_visuobject_insp_doc_ph" = "Para que serve a classe? Fica no código como comentário Javadoc por cima da classe."; -"web_visuobject_insp_visibility" = "Visibilidade"; -"web_visuobject_insp_type" = "Tipo"; -"web_visuobject_insp_init" = "Valor inicial"; -"web_visuobject_insp_no_fields" = "Ainda sem atributos."; -"web_visuobject_insp_add_field" = "Atributo"; -"web_visuobject_insp_params" = "Parâmetros"; -"web_visuobject_insp_return" = "Tipo de retorno"; -"web_visuobject_insp_constructor" = "Construtor"; -"web_visuobject_insp_no_methods" = "Ainda sem métodos."; -"web_visuobject_insp_add_method" = "Método"; -"web_visuobject_insp_generate" = "Gerar:"; -"web_visuobject_insp_gen_constructor" = "Construtor a partir dos atributos"; -"web_visuobject_insp_gen_constructor_tip" = "Como no Eclipse “Generate Constructor using Fields”: um construtor que define todos os atributos (exceto static)."; -"web_visuobject_insp_gen_accessors" = "Getters e setters"; -"web_visuobject_insp_gen_accessors_tip" = "Como no Eclipse “Generate Getters and Setters”: get…/set… para cada atributo (atributos final só com getter)."; -"web_visuobject_insp_bad_params" = "Escreve os parâmetros assim: nome: Tipo, segundo: int (lido “%@”)."; -"web_visuobject_insp_fields" = "Atributos"; -"web_visuobject_insp_methods" = "Métodos"; -"web_visuobject_inspector_resize" = "Redimensionar: arrasta a fronteira entre o diagrama de classes e o inspetor para cima ou para baixo (duplo clique = padrão)"; -"web_visuobject_dep_title" = "Dependência «%@»"; -"web_visuobject_dep_create_text" = "%1$@ cria um objeto da classe %2$@ em %3$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ usa a classe %2$@ em %3$@."; -"web_visuobject_dep_hint" = "Isto não é uma associação: %@ não guarda o objeto %@ num atributo, ele só existe dentro do método. Por isso a linha é tracejada."; -"web_visuobject_dep_to_assoc" = "Guardar como atributo (associação a %@)"; -"web_visuobject_end_mult_ph" = "mult."; -"web_visuobject_end_tip" = "Clique (linha selecionada) ou clique direito: papel, multiplicidade e navegabilidade nesta extremidade"; -"web_visuobject_end_role_ph" = "p. ex. osMeusGatos"; -"web_visuobject_new_menu" = "Novo"; -"web_visuobject_new_menu_tip" = "Criar um novo projeto, source folder, package, classe ou interface"; -"web_visuobject_new_project" = "Projeto Java"; -"web_visuobject_new_folder" = "Source folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Classe"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "Novo projeto Java"; -"web_visuobject_new_project_hint" = "No workspace cada projeto tem uma pasta própria com src."; -"web_visuobject_new_folder_title" = "Novo source folder"; -"web_visuobject_new_folder_hint" = "Outra pasta para código ao lado de src, por exemplo test para classes de teste."; -"web_visuobject_new_package_title" = "Novo package"; -"web_visuobject_new_package_hint" = "Os nomes de package escrevem-se em minúsculas, partes com ponto: animais ou escola.secretaria."; -"web_visuobject_new_class_title" = "Nova classe Java"; -"web_visuobject_new_class_hint" = "Os nomes de classe começam com maiúscula, por exemplo Cao ou ContaBancaria."; -"web_visuobject_new_interface_title" = "Nova interface Java"; -"web_visuobject_new_interface_hint" = "Uma interface define métodos que as classes implementam depois."; -"web_visuobject_new_root" = "Source folder"; -"web_visuobject_new_name" = "Nome"; -"web_visuobject_new_modifiers" = "Modificadores"; -"web_visuobject_new_package_private" = "package (sem modificador)"; -"web_visuobject_new_superclass" = "Superclasse"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Estende interfaces (extends)"; -"web_visuobject_new_stubs" = "Que esqueletos de métodos criar?"; -"web_visuobject_new_stub_constructors" = "Construtores da superclasse"; -"web_visuobject_new_stub_inherited" = "Métodos abstratos herdados"; -"web_visuobject_new_comment_legend" = "Comentário"; -"web_visuobject_new_comment" = "Comentário Javadoc com @author"; -"web_visuobject_new_author" = "O teu nome"; -"web_visuobject_new_finish" = "Concluir"; -"web_visuobject_new_name_missing" = "Escreve um nome."; -"web_visuobject_new_exists" = "“%@” já existe no projeto."; -"web_visuobject_new_upper" = "Em Java, os nomes de classe começam com maiúscula."; -"web_visuobject_bad_folder" = "“%@” não é um nome de pasta válido ou já existe."; -"web_visuobject_inspector_hide" = "Recolher o inspetor de objetos"; -"web_visuobject_inspector_show" = "Expandir o inspetor de objetos"; -"web_visuobject_inspector_empty" = "Clica numa relação (linha) no diagrama para a editares aqui."; -"web_visuobject_rel_end_a" = "Extremidade A: %@ (a linha começa aqui)"; -"web_visuobject_rel_end_b" = "Extremidade B: %@ (a linha acaba aqui)"; -"web_visuobject_ws_title" = "Pasta do workspace no teu computador"; -"web_visuobject_ws_question" = "Onde guardas os teus projetos?"; -"web_visuobject_ws_hint" = "Como no Eclipse: uma pasta de workspace no teu computador, com uma pasta por projeto com src e os ficheiros .java."; -"web_visuobject_ws_create" = "Criar a pasta “%@”"; -"web_visuobject_ws_open" = "Abrir um workspace existente"; -"web_visuobject_ws_skip" = "Sem pasta (só no browser)"; -"web_visuobject_ws_choose" = "Escolher workspace"; -"web_visuobject_ws_reopen" = "Abrir o workspace “%@”"; -"web_visuobject_ws_switch" = "Mudar"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Descarregar o projeto como .zip, por exemplo para o entregar"; -"web_visuobject_ws_saved" = "Guardado em %@/%@"; -"web_visuobject_ws_created" = "Novo projeto “%@” no workspace"; -"web_visuobject_ws_failed" = "Não foi possível guardar no workspace: %@"; -"web_visuobject_ws_taken" = "O workspace já tem um projeto “%@”."; -"web_visuobject_ws_open_project" = "Abrir o projeto %@"; -"web_visuobject_save_ws_tip" = "Guardar no workspace (Ctrl+S)"; -"web_visuobject_unsaved" = "não guardado"; -"web_visuobject_rel_nav_navigable" = "navegável (seta)"; -"web_visuobject_rel_nav_non-navigable" = "não navegável (cruz ×)"; -"web_visuobject_rel_nav_unspecified" = "não especificado (aberto)"; -"web_visuobject_rel_reading_flip" = "inverter"; -"web_visuobject_rel_reading_auto" = "Com um nome, a relação lê-se no sentido em que arrastaste."; -"web_visuobject_tools" = "Ferramentas"; -"web_visuobject_tool_select" = "Seleção"; -"web_visuobject_tool_select_tip" = "Selecionar e mover"; -"web_visuobject_tool_class" = "Classe"; -"web_visuobject_tool_class_tip" = "Classe: arrasta para a área ou escolhe e clica na área"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: arrasta para a área ou escolhe e clica na área"; -"web_visuobject_tool_link" = "Associação"; -"web_visuobject_tool_link_tip" = "Associação sem navegação: desenha uma linha simples entre duas classes"; -"web_visuobject_rel_link" = "Associação (sem navegação)"; -"web_visuobject_tool_not_link" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_association" = "Dirigida"; -"web_visuobject_tool_association_tip" = "Associação dirigida: arrasta da classe que conhece a outra para a outra (seta)"; -"web_visuobject_tool_aggregation" = "Agregação"; -"web_visuobject_tool_aggregation_tip" = "Agregação: arrasta do todo para a parte (tem, as partes continuam a existir)"; -"web_visuobject_tool_composition" = "Composição"; -"web_visuobject_tool_composition_tip" = "Composição: arrasta do todo para a parte (é composto por, as partes pertencem-lhe)"; -"web_visuobject_tool_extends" = "Herança"; -"web_visuobject_tool_extends_tip" = "Herança: arrasta da subclasse para a superclasse (extends)"; -"web_visuobject_tool_implements" = "Realização"; -"web_visuobject_tool_implements_tip" = "Realização: arrasta da classe para a interface (implements)"; -"web_visuobject_tool_pick_second" = "Agora clica na segunda classe (a partir de %@). Esc cancela."; -"web_visuobject_tool_not_association" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_aggregation" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_composition" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_extends" = "%@ não pode herdar de %@: classes herdam de classes, interfaces de interfaces. Para uma interface usa a realização."; -"web_visuobject_tool_not_implements" = "%@ não pode implementar %@: só uma classe implementa uma interface."; -"web_visuobject_inspector" = "Inspetor de objetos"; -"web_visuobject_connect" = "Arrastar: relação com outra classe"; -"web_visuobject_rel_association" = "Associação"; -"web_visuobject_rel_directed" = "Associação dirigida"; -"web_visuobject_rel_aggregation" = "Agregação (tem, as partes continuam a existir)"; -"web_visuobject_rel_composition" = "Composição (é composto por, as partes pertencem-lhe)"; -"web_visuobject_rel_extends" = "Herança (extends)"; -"web_visuobject_rel_implements" = "Realização (implements)"; -"web_visuobject_rel_extends_text" = "%@ herda de %@ (extends no código)."; -"web_visuobject_rel_implements_text" = "%@ implementa a interface %@ (implements no código)."; -"web_visuobject_rel_kind" = "Tipo"; -"web_visuobject_rel_name" = "Nome da relação"; -"web_visuobject_rel_name_ph" = "por exemplo, possui"; -"web_visuobject_rel_reading" = "Sentido de leitura"; -"web_visuobject_rel_navigable" = "Navegabilidade"; -"web_visuobject_rel_role" = "Papel"; -"web_visuobject_rel_multiplicity" = "Multiplicidade"; -"web_visuobject_rel_role_field" = "= atributo %@.%@ no código"; -"web_visuobject_rel_role_only_uml" = "só no diagrama (sem atributo enquanto a seta só aponta num sentido)"; -"web_visuobject_rel_delete" = "Eliminar a relação"; -"web_visuobject_rel_impossible" = "Não há uma relação adequada de %@ para %@."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Arrastar para mudar a largura (duplo clique: padrão)"; -"web_visuobject_pane_show" = "Expandir a área"; -"web_visuobject_pane_hide" = "Recolher a área"; -"web_visuobject_close_tab" = "Fechar %@"; -"web_visuobject_collapse" = "Recolher"; -"web_visuobject_expand" = "Expandir"; -"web_visuobject_tree_empty" = "Ainda vazio. Cria um package ou uma classe com «+ Novo»."; -"web_visuobject_delete" = "Eliminar"; -"web_visuobject_default_package" = "(package predefinido)"; -"web_visuobject_bad_package" = "«%@» não é um nome de package válido ou já existe. Os nomes de package escrevem-se em minúsculas, por exemplo animais."; -"web_visuobject_new_package_name" = "novopackage"; -"web_visuobject_no_file" = "Nenhum ficheiro aberto"; -"web_visuobject_project_name" = "MeuProjeto"; -"web_visuobject_explorer_show" = "Mostrar o Package Explorer"; -"web_visuobject_explorer_hide" = "Recolher o Package Explorer"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Guardar o projeto como .zip: NomeDoProjeto/src/package/Classe.java"; -"web_visuobject_open_tip" = "Abrir um projeto (.zip como do Eclipse ou BlueJ) ou juntar ficheiros .java ao projeto"; -"web_visuobject_method_ph" = "+ ladrar(): void"; -"web_code_scopes" = "Blocos coloridos"; -"web_code_scopes_hint" = "Código e diagrama: ciclo rosa, decisão azul, método amarelo, classe verde"; -"web_pseudocode" = "Pseudocódigo"; - -/* VisuFlow (desenhar fluxogramas) */ -"web_visuflow_subtitle" = "Desenhar fluxogramas"; -"web_visuflow_world_hint" = "VisuFlow: desenhar livremente um fluxograma e exportá-lo como imagem ou PDF. Podes mudar a qualquer momento na roda dentada."; -"web_visuflow_palette" = "Símbolos"; -"web_visuflow_palette_hint" = "Toca num símbolo para o juntar abaixo do selecionado, ligado por uma seta. Também o podes arrastar para a área."; -"web_visuflow_shape_start" = "Início"; -"web_visuflow_shape_ende" = "Fim"; -"web_visuflow_shape_anweisung" = "Instrução"; -"web_visuflow_shape_ein" = "Entrada/Saída"; -"web_visuflow_shape_verzweigung" = "Decisão"; -"web_visuflow_shape_unterprogramm" = "Subprograma"; -"web_visuflow_shape_start_tip" = "Oval: aqui começa o fluxo"; -"web_visuflow_shape_ende_tip" = "Oval: aqui termina o fluxo"; -"web_visuflow_shape_anweisung_tip" = "Retângulo: uma instrução, por exemplo «Ferver água»"; -"web_visuflow_shape_ein_tip" = "Paralelogramo: entrada ou saída, por exemplo «Entrada: número»"; -"web_visuflow_shape_verzweigung_tip" = "Losango: uma pergunta com as saídas sim e não"; -"web_visuflow_shape_unterprogramm_tip" = "Retângulo com duas barras: chama um subprograma"; -"web_visuflow_text_start" = "Início"; -"web_visuflow_text_ende" = "Fim"; -"web_visuflow_text_anweisung" = "Instrução"; -"web_visuflow_text_ein" = "Entrada: número"; -"web_visuflow_text_verzweigung" = "Condição?"; -"web_visuflow_text_unterprogramm" = "Subprograma"; -"web_visuflow_new_tip" = "Novo: fluxograma vazio com Início (Anular recupera o anterior)"; -"web_visuflow_open_tip" = "Abrir um ficheiro de fluxograma (.visuflow)"; -"web_visuflow_save_tip" = "Guardar o fluxograma como ficheiro (.visuflow)"; -"web_visuflow_delete" = "Apagar"; -"web_visuflow_delete_tip" = "Apagar o símbolo ou a seta selecionada (Del)"; -"web_visuflow_swap" = "trocar sim/não"; -"web_visuflow_swap_tip" = "Losango: «sim» e «não» trocam de lado"; -"web_visuflow_example" = "Exemplo"; -"web_visuflow_example_tip" = "Carregar o exemplo «Fazer chá» da lição 1 (Anular recupera o fluxograma anterior)"; -"web_visuflow_example_title" = "Fazer chá"; -"web_visuflow_example_water" = "Ferver água"; -"web_visuflow_example_bag" = "Saqueta de chá na chávena"; -"web_visuflow_example_hot" = "O chá ainda está quente demais?"; -"web_visuflow_example_wait" = "Esperar"; -"web_visuflow_example_drink" = "Beber o chá"; -"web_visuflow_title_label" = "Título do fluxograma"; -"web_visuflow_title_ph" = "Título do fluxograma"; -"web_visuflow_canvas_label" = "Área do fluxograma"; -"web_visuflow_open_error" = "Este ficheiro não é um fluxograma VisuFlow."; -"web_visuflow_saved" = "Guardado como %@"; -"web_visuflow_status_empty" = "Arrasta um símbolo da barra para a área ou toca nele."; -"web_visuflow_status_idle" = "Toca num símbolo para o selecionar. Arrasta a partir do ponto na saída para desenhar uma seta até ao próximo símbolo."; -"web_visuflow_status_node" = "Arrasta para mover o símbolo, toca outra vez para mudar o texto. Arrasta uma seta a partir do ponto na saída."; -"web_visuflow_status_edge" = "Seta selecionada. Apagar remove-a."; -"web_visuflow_status_connect" = "Larga sobre o símbolo para onde a seta vai."; -"web_visuflow_edit_label" = "Texto no símbolo"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Mudar o nome da variável …"; -"web_rename_title" = "Mudar o nome da variável"; -"web_rename_old" = "Nome"; -"web_rename_new" = "Novo nome"; -"web_rename_hint" = "Altera todos os sítios com exatamente esta escrita (numero não é Numero), no código e no estruturograma. Texto entre \"…\" e comentários ficam iguais."; -"web_rename_count_fmt" = "Aparece %d vezes."; -"web_rename_exists_fmt" = "“%@” já existe: depois as duas seriam uma só variável."; -"web_rename_invalid" = "Um nome começa com uma letra e só contém letras, algarismos e _."; -"web_rename_keyword_fmt" = "“%@” é uma palavra-chave de Java."; -"web_rename_apply" = "Mudar o nome"; -"web_rename_cancel" = "Cancelar"; -"web_visubot_world_visucode_hint" = "Outros programas como %1$@ ou %2$@ estão em visucode.de."; diff --git a/web/src/i18n/ru.strings b/web/src/i18n/ru.strings deleted file mode 100644 index fc0ac558..00000000 --- a/web/src/i18n/ru.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Тексты, которые есть только в веб-версии. Все остальные берутся из Localizable.strings приложения. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Структурная схема VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Экспорт…"; -"web_export_title" = "Сохранить как изображение или PDF либо распечатать"; -"web_export_png" = "Изображение (PNG)"; -"web_export_svg" = "Векторная графика (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Файл VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Ошибка экспорта: %@"; -"web_print_hint" = "Через «Экспорт… → PDF» можно сохранить схему и затем распечатать её."; -"web_delete" = "Удалить"; -"web_language_title" = "Язык интерфейса"; -"web_simulation_start" = "Запустить симуляцию"; -"web_simulation_end" = "Завершить симуляцию"; -"web_simulation_start_title" = "Выполнить схему шаг за шагом"; -"web_simulation_end_title" = "Завершить симуляцию и продолжить редактирование"; -"web_palette_hint" = "Перетащите в схему"; -"web_tabs_label" = "Схемы"; -"web_status_modified" = "изменено"; -"web_inspector_title" = "Свойства"; -"web_inspector_resize" = "Изменить высоту инспектора объектов: перетащите линию вверх или вниз (двойной щелчок: по умолчанию)"; -"web_inspector_help_drop" = "Перетаскивайте элементы из палитры на плитку: сверху = перед, снизу = после, на «ø» = заменить. Существующие элементы можно перемещать мышью."; -"web_inspector_help_inline" = "Дважды щёлкните или коснитесь элемента, чтобы изменить текст прямо в диаграмме (Enter — применить, Esc — отмена). Так же меняются имена вариантов."; -"web_inspector_help_files" = "Файлы также можно перетащить в окно."; -"web_case_name" = "Имя варианта"; -"web_case_name_placeholder" = "напр. 1, ok, default"; -"web_copied" = "Элемент скопирован."; -"web_discard_message_fmt" = "Изменения в «%@» будут потеряны."; -"web_err_unreadable_fmt" = "«%@» не является читаемым файлом VisuStruct (%@)."; -"web_saved_fmt" = "«%@» сохранён."; -"web_saved_txt_fmt" = "Сохранено как «%@». Удалите расширение .txt, чтобы использовать файл."; -"web_drop_impossible" = "Сюда нельзя поместить"; -"web_sim_delay" = "Пауза на шаг"; -"web_sim_delay_value_fmt" = "%@ с"; -"web_codegen_language" = "Язык"; -"web_codegen_comments" = "Текст схемы как комментарии"; -"web_codegen_empty_else" = "Выводить пустую ветку else в коде"; -"web_copied_check" = "Скопировано ✓"; -"web_copy_manual" = "Выделено, нажмите Ctrl+C для копирования"; -"web_run_stopped_loop" = "Выполнение остановлено: возможен бесконечный цикл."; -"web_run_cancelled" = "Выполнение отменено."; -"web_run_error" = "Ошибка при выполнении."; -"web_run_dropped_fmt" = "… скрыто более ранних строк: %d"; -"web_sim_call_arguments_fmt" = "Вызов не соответствует заголовку %@. Проверь значения в скобках."; -"web_sim_missing_return_fmt" = "%@ завершилась, не вернув значение через return."; -"web_sim_file_not_found_fmt" = "Файла с именем %@ нет."; -"web_sim_end_of_file_fmt" = "В файле %@ больше нет строк."; -"web_sim_null_value_fmt" = "%@ равно null."; -"web_sim_unknown_variable_case_fmt" = "Неизвестная переменная: %1$@. Java различает заглавные и строчные буквы: может быть, %2$@?"; -"web_sim_written_file_fmt" = "%@ (сохранён)"; -"web_site_home" = "Главная страница"; -"web_site_imprint" = "Выходные данные"; -"web_site_privacy" = "Конфиденциальность"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Программирование"; -"web_live_hide" = "Скрыть программирование"; -"web_scale_canvas" = "Размер текста: структурная схема"; -"web_scale_code" = "Размер текста: программирование"; -"web_live_indent" = "Отступы"; -"web_live_indent_title" = "Расставить отступы: четыре пробела на уровень (выделенные строки, иначе всё)"; -"web_live_show" = "Показать программирование"; -"web_live_resize" = "Изменить ширину области кода (двойной щелчок: сбросить)"; -"web_live_hint" = "Код растёт вместе с диаграммой. Серый = заполнитель: пиши в инспекторе, и текст появится здесь. Щёлкни строку, чтобы выбрать элемент."; -"web_live_ph_selector" = "переменная"; -"web_live_ph_code" = "Твой код"; -"web_live_ph_end" = "конец"; -"web_live_language" = "Язык программирования"; -"web_live_java_class" = "Как класс"; -"web_live_java_class_hint" = "Имя класса = заголовок диаграммы"; -"web_live_java_main" = "С методом main"; -"web_settings_display" = "Отображение"; -"web_live_swing" = "Настоящая Java (со Swing)"; -"web_live_swing_hint" = "Настоящая Java с окнами (CheerpJ). При первом запуске однократно загружает около 40 МБ."; -"web_live_swing_windows" = "Окна Java"; -"web_live_swing_hide" = "Закрыть окна Java"; -"web_live_swing_loading" = "Загрузка Java (в первый раз около 40 МБ) …"; -"web_live_swing_compiling" = "Компиляция …"; -"web_live_swing_running" = "Программа выполняется."; -"web_live_swing_compile_failed" = "Ошибка компиляции, см. выше."; -"web_live_swing_failed" = "Не удалось загрузить Java. Проверь подключение к интернету."; -"web_live_hint_edit" = "Набери здесь код Java, и диаграмма построится сама. Или работай в диаграмме, и код будет расти вместе с ней. Серый = заполнитель, просто пиши поверх."; -"web_live_edit_label" = "Код Java"; -"web_parse_line_fmt" = "Строка %d: %@"; -"web_parse_missing" = "Здесь ещё не хватает: %@"; -"web_parse_extra" = "Лишнее: %@"; -"web_parse_unclosed_string" = "Не хватает закрывающей %@."; -"web_parse_else_without_if" = "У этого «else» нет «if»."; -"web_parse_case_outside_switch" = "«case» и «default» должны быть внутри «switch»."; -"web_parse_missing_do_while" = "После «do { … }» не хватает «while (условие);»."; -"web_parse_more_methods" = "Пока здесь учитывается только один метод. Создай другие подпрограммы в отдельных вкладках."; -"web_parse_missing_import" = "В самом начале не хватает %@ Без этой строки Java не знает Scanner."; -"web_parse_missing_scanner" = "Сначала не хватает %@ Без этой строки Scanner не существует."; -"web_parse_unknown_type" = "Java не знает такого типа данных. Может быть, %@?"; -"web_quickfix_insert_fmt" = "Предложение: вставить %@"; -"web_quickfix_replace_fmt" = "Предложение: %1$@ вместо %2$@"; -"web_parse_wrong_case" = "Java различает заглавные и строчные буквы. Правильно: %@."; -"web_editor_title" = "Диаграмма"; -"web_editor_show" = "Показать диаграмму"; -"web_editor_hide" = "Скрыть диаграмму"; -"web_keywords_title" = "Ключевые слова кода"; -"web_keywords_hint" = "if, while, for вместо названий элементов"; -"web_palette_compact" = "Элементы всегда компактно"; -"web_palette_compact_hint" = "Значок с маленьким названием под ним, иначе только при узкой колонке"; -"web_about_version_fmt" = "Версия %@"; -"web_about_software" = "Используемое ПО"; -"web_about_software_text" = "Редактор кода использует CodeMirror и Lezer (лицензия MIT). С поддержкой Swing VisuStruct загружает CheerpJ от Leaning Technologies (лицензия Community) и javac из OpenJDK."; -"web_about_licenses" = "Тексты лицензий"; -"web_live_run" = "Запустить"; -"web_live_run_title" = "Запустить программу: вывод и ввод появятся ниже"; -"web_live_run_finished" = "Программа завершена."; -"web_live_run_stopped" = "Остановлено."; -"web_console" = "Консоль"; -"web_console_hide" = "Скрыть консоль"; -"web_console_resize" = "Изменить высоту консоли"; -"web_diagram_tools" = "Инструменты диаграммы"; -"web_files_title" = "Файлы"; -"web_files_show" = "Показать файлы"; -"web_files_hide" = "Скрыть файлы"; -"web_files_resize" = "Изменить ширину файлов"; -"web_files_new_folder" = "Новая папка"; -"web_files_switch" = "Выбрать другую папку"; -"web_files_delete" = "Удалить"; -"web_files_in_browser" = "В хранилище этого браузера"; -"web_files_save_hint_fmt" = "При сохранении программа попадает в %@."; -"web_files_empty" = "Пока ничего не сохранено. При сохранении VisuStruct создаёт здесь папку с сегодняшней датой."; -"web_files_reconnect_hint" = "После перезагрузки браузер ещё раз спросит, может ли VisuStruct использовать твою папку."; -"web_files_reconnect_fmt" = "Открыть папку «%@»"; -"web_files_connect_hint" = "Выбери папку как рабочее пространство. VisuStruct создаёт в ней папку для каждого дня и сохраняет диаграмму и код."; -"web_files_connect" = "Выбрать папку"; -"web_files_browser_hint" = "Этот браузер не может открывать папки на устройстве (напр. на iPad). Твои файлы остаются в хранилище этого браузера."; -"web_files_browser" = "Сохранять в браузере"; -"web_files_delete_confirm_fmt" = "Действительно удалить «%@»?"; -"web_files_folder_prompt" = "Имя новой папки:"; -"web_files_default_name" = "Программа"; -"web_files_saved_fmt" = "Сохранено: %@"; -"web_files_create" = "Создать"; -"web_learn_ludwig_tip" = "Совет Людвига"; -"web_game_title" = "Игра: Истина или ложь?"; -"web_game_intro" = "Людвиг показывает значения и условие. Условие истинно (true) или ложно (false)? Десять раундов, всё сложнее."; -"web_game_start" = "Начать игру"; -"web_game_round_fmt" = "Раунд %@ из %@"; -"web_game_score_fmt" = "Очки: %@"; -"web_game_true" = "истина (true)"; -"web_game_false" = "ложь (false)"; -"web_game_right" = "Верно!"; -"web_game_wrong" = "Не совсем. Вот как это вычисляется:"; -"web_game_next" = "Дальше"; -"web_game_finish" = "Результат"; -"web_game_done_fmt" = "%@ из %@ верно."; -"web_game_perfect" = "Всё верно – Людвиг впечатлён!"; -"web_game_good" = "Отлично! Посмотри на ошибки и попробуй ещё раз."; -"web_game_practice" = "Повторение – мать учения. Перечитай таблицу истинности и сыграй ещё раз."; -"web_game_again" = "Играть снова"; -"web_game_wrong_short" = "К сожалению, неверно."; -"web_game_practice_short" = "Повторение — мать учения. Перечитай объяснения и сыграй ещё раунд."; -"web_game_clicks" = "Клики:"; -"web_game_nobody" = "Никто не слушает — ничего не происходит."; -"web_game_event_fmt" = "Клик по «%@»: слушатель слышит это и выполняет свой код."; -"web_game_try" = "Теперь попробуй кнопки в окне сам."; -"web_game_time_fmt" = "Осталось %@ с"; -"web_game_defused" = "✂️ Обезврежено! Ты нашёл ошибку."; -"web_game_boom_time" = "💥 Бум! Время вышло."; -"web_game_boom_wrong" = "💥 Бум! Это была не та строка."; -"web_game_boom_part" = "💥 Бум! Строка верная, но ошибка в другой её части."; -"web_game_boom_wrong_part" = "💥 Бум! Ошибка в другой строке."; -"web_game_bomb_parts_hint" = "Нажми точно на неправильную часть."; -"web_game_fix" = "Правильная строка:"; -"web_game_order_hint" = "Нажимай на шаги в правильном порядке."; -"web_debug_step" = "Шаг"; -"web_debug_step_title" = "Шаг: выполнить одну инструкцию и снова остановиться"; -"web_debug_continue" = "Продолжить до следующей точки останова"; -"web_debug_paused_fmt" = "Остановлено перед строкой %d."; -"web_debug_gutter_title" = "Нажми на номер строки, чтобы поставить или убрать точку останова"; -"web_swap_to_code" = "Программирование крупно"; -"web_swap_to_editor" = "Структограмма крупно"; -"web_editor_resize" = "Изменить ширину структограммы"; -"web_palette_resize" = "Изменить ширину элементов"; -"web_palette_hide" = "Скрыть элементы"; -"web_palette_show" = "Показать элементы"; -"web_code_scopes" = "Цветные блоки кода"; -"web_code_scopes_hint" = "Класс, метод, цикл, ветвление в отдельных рамках"; -"web_pseudocode" = "Псевдокод"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Переименовать переменную …"; -"web_rename_title" = "Переименовать переменную"; -"web_rename_old" = "Имя"; -"web_rename_new" = "Новое имя"; -"web_rename_hint" = "Меняет каждое место с точно таким написанием (number — не Number), в коде и на структурограмме. Текст в \"…\" и комментарии остаются."; -"web_rename_count_fmt" = "Встречается %d раз(а)."; -"web_rename_exists_fmt" = "«%@» уже есть: после этого обе станут одной переменной."; -"web_rename_invalid" = "Имя начинается с буквы и содержит только буквы, цифры и _."; -"web_rename_keyword_fmt" = "«%@» — ключевое слово Java."; -"web_rename_apply" = "Переименовать"; -"web_rename_cancel" = "Отмена"; diff --git a/web/src/i18n/sv.strings b/web/src/i18n/sv.strings deleted file mode 100644 index 9de9c094..00000000 --- a/web/src/i18n/sv.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Texter som bara finns i webbversionen. Allt annat kommer från appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exportera…"; -"web_export_title" = "Spara som bild eller PDF, eller skriv ut"; -"web_export_png" = "Bild (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Exporten misslyckades: %@"; -"web_print_hint" = "Använd ”Exportera… → PDF” för att spara diagrammet och skriva ut det därifrån."; -"web_delete" = "Radera"; -"web_language_title" = "Gränssnittets språk"; -"web_simulation_start" = "Starta simulering"; -"web_simulation_end" = "Avsluta simulering"; -"web_simulation_start_title" = "Kör struktogrammet steg för steg"; -"web_simulation_end_title" = "Avsluta simuleringen och fortsätt redigera"; -"web_palette_hint" = "Dra in i struktogrammet"; -"web_tabs_label" = "Diagram"; -"web_status_modified" = "ändrad"; -"web_inspector_title" = "Egenskaper"; -"web_inspector_resize" = "Ändra objektinspektörens höjd: dra eller svep linjen uppåt eller nedåt (dubbelklick: standard)"; -"web_inspector_help_drop" = "Dra element från paletten till en ruta: överst = före, nederst = efter, på ”ø” = ersätt. Befintliga element kan flyttas med musen."; -"web_inspector_help_inline" = "Dubbelklicka eller dubbeltryck på ett element för att redigera texten direkt i diagrammet (Enter verkställer, Esc avbryter). Gäller även case-namn."; -"web_inspector_help_files" = "Du kan också släppa filer i fönstret."; -"web_case_name" = "Namn på case"; -"web_case_name_placeholder" = "t.ex. 1, ok, default"; -"web_copied" = "Elementet kopierat."; -"web_discard_message_fmt" = "Ändringarna i ”%@” går förlorade."; -"web_err_unreadable_fmt" = "”%@” är inte en läsbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "”%@” sparad."; -"web_saved_txt_fmt" = "Sparad som ”%@”. Ta bort filändelsen .txt för att använda den."; -"web_drop_impossible" = "Kan inte släppas här"; -"web_sim_delay" = "Paus per steg"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Språk"; -"web_codegen_comments" = "Diagramtext som kommentarer"; -"web_codegen_empty_else" = "Skriv ut tom else-gren i koden"; -"web_copied_check" = "Kopierat ✓"; -"web_copy_manual" = "Markerat, tryck Ctrl+C för att kopiera"; -"web_run_stopped_loop" = "Körningen stoppades: möjlig oändlig loop."; -"web_run_cancelled" = "Körningen avbröts."; -"web_run_error" = "Fel vid körning."; -"web_run_dropped_fmt" = "… %d tidigare rader dolda"; -"web_sim_call_arguments_fmt" = "Anropet passar inte till huvudet i %@. Kontrollera värdena inom parentes."; -"web_sim_missing_return_fmt" = "%@ avslutades utan att returnera ett värde med return."; -"web_sim_file_not_found_fmt" = "Det finns ingen fil som heter %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har inga fler rader."; -"web_sim_null_value_fmt" = "%@ är null."; -"web_sim_unknown_variable_case_fmt" = "Okänd variabel: %1$@. Java skiljer på stora och små bokstäver: menade du %2$@?"; -"web_sim_written_file_fmt" = "%@ (sparad)"; -"web_site_home" = "Startsida"; -"web_site_imprint" = "Kontaktuppgifter"; -"web_site_privacy" = "Integritet"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Dölj programmering"; -"web_scale_canvas" = "Textstorlek: strukturdiagram"; -"web_scale_code" = "Textstorlek: programmering"; -"web_live_indent" = "Indrag"; -"web_live_indent_title" = "Gör indrag i koden: fyra blanksteg per nivå (markerade rader, annars allt)"; -"web_live_show" = "Visa programmering"; -"web_live_resize" = "Ändra kodområdets bredd (dubbelklick: återställ)"; -"web_live_hint" = "Koden växer med ditt diagram. Grått = platshållare: skriv i inspektören så visas texten här. Klicka på en rad för att markera elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kod"; -"web_live_ph_end" = "slut"; -"web_live_language" = "Programmeringsspråk"; -"web_live_java_class" = "Som klass"; -"web_live_java_class_hint" = "Klassnamn = diagrammets titel"; -"web_live_java_main" = "Med main-metod"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Riktig Java (med Swing)"; -"web_live_swing_hint" = "Riktig Java med fönster (CheerpJ). Laddar ner ca 40 MB en gång vid första körningen."; -"web_live_swing_windows" = "Java-fönster"; -"web_live_swing_hide" = "Stäng Java-fönster"; -"web_live_swing_loading" = "Java laddas (första gången ca 40 MB) …"; -"web_live_swing_compiling" = "Kompilerar …"; -"web_live_swing_running" = "Programmet körs."; -"web_live_swing_compile_failed" = "Kompileringsfel, se ovan."; -"web_live_swing_failed" = "Java kunde inte laddas. Kontrollera internetanslutningen."; -"web_live_hint_edit" = "Skriv Java-kod här så bygger diagrammet sig självt. Eller arbeta i diagrammet så växer koden med. Grått = platshållare, skriv bara över."; -"web_live_edit_label" = "Java-kod"; -"web_parse_line_fmt" = "Rad %d: %@"; -"web_parse_missing" = "Här saknas ännu: %@"; -"web_parse_extra" = "För mycket: %@"; -"web_parse_unclosed_string" = "Det avslutande %@ saknas."; -"web_parse_else_without_if" = "Denna ”else” har ingen ”if”."; -"web_parse_case_outside_switch" = "”case” och ”default” hör hemma i en ”switch”."; -"web_parse_missing_do_while" = "Efter ”do { … }” saknas ”while (villkor);”."; -"web_parse_more_methods" = "För tillfället räknas bara en metod här. Skapa andra underprogram i egna flikar."; -"web_parse_missing_import" = "Högst upp saknas %@ Utan den raden känner Java inte till Scanner."; -"web_parse_missing_scanner" = "Innan saknas %@ Utan den raden finns ingen Scanner."; -"web_parse_unknown_type" = "Java känner inte till den här datatypen. Menade du %@?"; -"web_quickfix_insert_fmt" = "Förslag: infoga %@"; -"web_quickfix_replace_fmt" = "Förslag: %1$@ i stället för %2$@"; -"web_parse_wrong_case" = "Java skiljer på stora och små bokstäver. Rätt är %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Visa strukturdiagram"; -"web_editor_hide" = "Dölj strukturdiagram"; -"web_keywords_title" = "Kodnyckelord"; -"web_keywords_hint" = "if, while, for i stället för elementnamn"; -"web_palette_compact" = "Element alltid kompakta"; -"web_palette_compact_hint" = "Symbol med litet namn under, annars bara när kolumnen är smal"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Programvara som används"; -"web_about_software_text" = "Kodredigeraren använder CodeMirror och Lezer (MIT-licens). Med Swing-stöd laddar VisuStruct CheerpJ från Leaning Technologies (Community-licens) och javac från OpenJDK."; -"web_about_licenses" = "Licenstexter"; -"web_live_run" = "Kör"; -"web_live_run_title" = "Kör programmet: utdata och indata visas nedanför"; -"web_live_run_finished" = "Programmet är klart."; -"web_live_run_stopped" = "Stoppat."; -"web_console" = "Konsol"; -"web_console_hide" = "Dölj konsolen"; -"web_console_resize" = "Ändra konsolens höjd"; -"web_diagram_tools" = "Diagramverktyg"; -"web_files_title" = "Filer"; -"web_files_show" = "Visa filer"; -"web_files_hide" = "Dölj filer"; -"web_files_resize" = "Ändra bredd på filer"; -"web_files_new_folder" = "Ny mapp"; -"web_files_switch" = "Välj en annan mapp"; -"web_files_delete" = "Ta bort"; -"web_files_in_browser" = "I den här webbläsarens lagring"; -"web_files_save_hint_fmt" = "Sparandet lägger programmet i %@."; -"web_files_empty" = "Inget sparat ännu. När du sparar skapar VisuStruct en mapp med dagens datum här."; -"web_files_reconnect_hint" = "Efter omladdning frågar webbläsaren igen om VisuStruct får använda din mapp."; -"web_files_reconnect_fmt" = "Öppna mappen ”%@”"; -"web_files_connect_hint" = "Välj en mapp som arbetsyta. VisuStruct skapar en mapp för varje dag där och sparar diagram och kod."; -"web_files_connect" = "Välj mapp"; -"web_files_browser_hint" = "Den här webbläsaren kan inte öppna mappar på enheten (t.ex. på iPad). Dina filer stannar i webbläsarens lagring."; -"web_files_browser" = "Spara i webbläsaren"; -"web_files_delete_confirm_fmt" = "Vill du verkligen ta bort ”%@”?"; -"web_files_folder_prompt" = "Namn på den nya mappen:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Sparat: %@"; -"web_files_create" = "Skapa"; -"web_learn_ludwig_tip" = "Ludwigs tips"; -"web_game_title" = "Spel: Sant eller falskt?"; -"web_game_intro" = "Ludwig visar värden och ett villkor. Är villkoret sant (true) eller falskt (false)? Tio rundor, allt svårare."; -"web_game_start" = "Starta spelet"; -"web_game_round_fmt" = "Runda %@ av %@"; -"web_game_score_fmt" = "Poäng: %@"; -"web_game_true" = "sant (true)"; -"web_game_false" = "falskt (false)"; -"web_game_right" = "Rätt!"; -"web_game_wrong" = "Inte riktigt. Så räknar man:"; -"web_game_next" = "Nästa"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ av %@ rätt."; -"web_game_perfect" = "Allt rätt – Ludwig är imponerad!"; -"web_game_good" = "Snyggt! Titta på dina misstag och försök igen."; -"web_game_practice" = "Övning ger färdighet. Läs sanningstabellen igen och spela en runda till."; -"web_game_again" = "Spela igen"; -"web_game_wrong_short" = "Tyvärr fel."; -"web_game_practice_short" = "Övning ger färdighet. Läs förklaringarna igen och spela en runda till."; -"web_game_clicks" = "Klick:"; -"web_game_nobody" = "Ingen lyssnar – ingenting händer."; -"web_game_event_fmt" = "Klick på ”%@”: lyssnaren hör det och kör sin kod."; -"web_game_try" = "Prova nu själv knapparna i fönstret."; -"web_game_time_fmt" = "%@ s kvar"; -"web_game_defused" = "✂️ Desarmerad! Du hittade felet."; -"web_game_boom_time" = "💥 Pang! Tiden är ute."; -"web_game_boom_wrong" = "💥 Pang! Det var fel rad."; -"web_game_boom_part" = "💥 Pang! Rätt rad, men felet sitter i en annan del."; -"web_game_boom_wrong_part" = "💥 Pang! Felet sitter på en annan rad."; -"web_game_bomb_parts_hint" = "Tryck exakt på den felaktiga delen."; -"web_game_fix" = "Rätt rad:"; -"web_game_order_hint" = "Tryck på stegen i rätt ordning."; -"web_debug_step" = "Steg"; -"web_debug_step_title" = "Steg: kör en sats och pausa igen"; -"web_debug_continue" = "Fortsätt till nästa brytpunkt"; -"web_debug_paused_fmt" = "Pausad före rad %d."; -"web_debug_gutter_title" = "Klicka på ett radnummer för att sätta eller ta bort en brytpunkt"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Ändra bredd på struktogrammet"; -"web_palette_resize" = "Ändra bredd på elementen"; -"web_palette_hide" = "Dölj elementen"; -"web_palette_show" = "Visa elementen"; -"web_code_scopes" = "Färgade kodblock"; -"web_code_scopes_hint" = "Klass, metod, loop, förgrening i egna rutor"; -"web_pseudocode" = "Pseudokod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Byt namn på variabel …"; -"web_rename_title" = "Byt namn på variabel"; -"web_rename_old" = "Namn"; -"web_rename_new" = "Nytt namn"; -"web_rename_hint" = "Ändrar varje ställe med exakt denna stavning (tal är inte Tal), i koden och i strukturdiagrammet. Text inom \"…\" och kommentarer blir kvar."; -"web_rename_count_fmt" = "Förekommer %d gånger."; -"web_rename_exists_fmt" = "”%@” finns redan: efteråt skulle båda vara en variabel."; -"web_rename_invalid" = "Ett namn börjar med en bokstav och innehåller bara bokstäver, siffror och _."; -"web_rename_keyword_fmt" = "”%@” är ett nyckelord i Java."; -"web_rename_apply" = "Byt namn"; -"web_rename_cancel" = "Avbryt"; diff --git a/web/src/i18n/tr.strings b/web/src/i18n/tr.strings deleted file mode 100644 index 9dcae9ee..00000000 --- a/web/src/i18n/tr.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Yalnızca web sürümünde bulunan metinler. Geri kalan her şey uygulamanın Localizable.strings dosyalarından gelir. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct yapı şeması"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Dışa aktar…"; -"web_export_title" = "Görsel veya PDF olarak kaydet ya da yazdır"; -"web_export_png" = "Görsel (PNG)"; -"web_export_svg" = "Vektör grafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct dosyası (.visustruct)"; -"web_export_failed_fmt" = "Dışa aktarma başarısız: %@"; -"web_print_hint" = "Diyagramı kaydetmek için “Dışa aktar… → PDF” seçeneğini kullanın ve oradan yazdırın."; -"web_delete" = "Sil"; -"web_language_title" = "Arayüz dili"; -"web_simulation_start" = "Simülasyonu başlat"; -"web_simulation_end" = "Simülasyonu bitir"; -"web_simulation_start_title" = "Yapı şemasını adım adım çalıştır"; -"web_simulation_end_title" = "Simülasyonu bitir ve düzenlemeye devam et"; -"web_palette_hint" = "Yapı şemasına sürükleyin"; -"web_tabs_label" = "Diyagramlar"; -"web_status_modified" = "değiştirildi"; -"web_inspector_title" = "Özellikler"; -"web_inspector_resize" = "Nesne denetçisinin yüksekliğini değiştir: çizgiyi yukarı veya aşağı sürükle ya da kaydır (çift tıklama: varsayılan)"; -"web_inspector_help_drop" = "Paletteki öğeleri bir kutucuğa sürükleyin: üst = önce, alt = sonra, “ø” üzerine = değiştir. Mevcut öğeler fareyle taşınabilir."; -"web_inspector_help_inline" = "Metni doğrudan diyagramda düzenlemek için bir öğeye çift tıklayın veya çift dokunun (Enter uygular, Esc iptal eder). Durum adları da böyle değişir."; -"web_inspector_help_files" = "Dosyaları pencereye de bırakabilirsiniz."; -"web_case_name" = "Durum adı"; -"web_case_name_placeholder" = "ör. 1, ok, default"; -"web_copied" = "Öğe kopyalandı."; -"web_discard_message_fmt" = "“%@” üzerindeki değişiklikler kaybolacak."; -"web_err_unreadable_fmt" = "“%@” okunabilir bir VisuStruct dosyası değil (%@)."; -"web_saved_fmt" = "“%@” kaydedildi."; -"web_saved_txt_fmt" = "“%@” olarak kaydedildi. Kullanmak için .txt uzantısını kaldırın."; -"web_drop_impossible" = "Buraya bırakılamaz"; -"web_sim_delay" = "Adım başına duraklama"; -"web_sim_delay_value_fmt" = "%@ sn"; -"web_codegen_language" = "Dil"; -"web_codegen_comments" = "Diyagram metni yorum olarak"; -"web_codegen_empty_else" = "Kodda boş else dalı oluştur"; -"web_copied_check" = "Kopyalandı ✓"; -"web_copy_manual" = "Seçildi, kopyalamak için Ctrl+C tuşlarına basın"; -"web_run_stopped_loop" = "Çalıştırma durduruldu: olası sonsuz döngü."; -"web_run_cancelled" = "Çalıştırma iptal edildi."; -"web_run_error" = "Çalıştırma sırasında hata."; -"web_run_dropped_fmt" = "… önceki %d satır gizlendi"; -"web_sim_call_arguments_fmt" = "Çağrı %@ başlığıyla eşleşmiyor. Parantez içindeki değerleri kontrol et."; -"web_sim_missing_return_fmt" = "%@ return ile bir değer döndürmeden bitti."; -"web_sim_file_not_found_fmt" = "%@ adında bir dosya yok."; -"web_sim_end_of_file_fmt" = "%@ dosyasında başka satır yok."; -"web_sim_null_value_fmt" = "%@ null."; -"web_sim_unknown_variable_case_fmt" = "Bilinmeyen değişken: %1$@. Java büyük ve küçük harfleri ayırt eder: %2$@ mı demek istedin?"; -"web_sim_written_file_fmt" = "%@ (kaydedildi)"; -"web_site_home" = "Ana sayfa"; -"web_site_imprint" = "Künye"; -"web_site_privacy" = "Gizlilik"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programlama"; -"web_live_hide" = "Programlamayı gizle"; -"web_scale_canvas" = "Yazı boyutu: yapı diyagramı"; -"web_scale_code" = "Yazı boyutu: programlama"; -"web_live_indent" = "Girinti"; -"web_live_indent_title" = "Kodu girintile: her seviye için dört boşluk (seçili satırlar, yoksa tümü)"; -"web_live_show" = "Programlamayı göster"; -"web_live_resize" = "Kod alanının genişliğini değiştir (çift tıklama: sıfırla)"; -"web_live_hint" = "Kod diyagramınla birlikte büyür. Gri = yer tutucu: denetçiye yaz, metnin burada görünür. Öğesini seçmek için bir satıra tıkla."; -"web_live_ph_selector" = "değişken"; -"web_live_ph_code" = "Kodun"; -"web_live_ph_end" = "bitiş"; -"web_live_language" = "Programlama dili"; -"web_live_java_class" = "Sınıf olarak"; -"web_live_java_class_hint" = "Sınıf adı = diyagram başlığı"; -"web_live_java_main" = "main metoduyla"; -"web_settings_display" = "Görünüm"; -"web_live_swing" = "Gerçek Java (Swing ile)"; -"web_live_swing_hint" = "Pencereli gerçek Java (CheerpJ). İlk çalıştırmada bir kez yaklaşık 40 MB indirir."; -"web_live_swing_windows" = "Java pencereleri"; -"web_live_swing_hide" = "Java pencerelerini kapat"; -"web_live_swing_loading" = "Java yükleniyor (ilk seferde yaklaşık 40 MB) …"; -"web_live_swing_compiling" = "Derleniyor …"; -"web_live_swing_running" = "Program çalışıyor."; -"web_live_swing_compile_failed" = "Derleme hatası, yukarıya bak."; -"web_live_swing_failed" = "Java yüklenemedi. İnternet bağlantını kontrol et."; -"web_live_hint_edit" = "Buraya Java kodu yaz, diyagram kendiliğinden oluşsun. Ya da diyagramda çalış, kod onunla büyüsün. Gri = yer tutucu, üzerine yazman yeterli."; -"web_live_edit_label" = "Java kodu"; -"web_parse_line_fmt" = "Satır %d: %@"; -"web_parse_missing" = "Burada hâlâ eksik: %@"; -"web_parse_extra" = "Fazla: %@"; -"web_parse_unclosed_string" = "Kapanış %@ eksik."; -"web_parse_else_without_if" = "Bu “else” için bir “if” yok."; -"web_parse_case_outside_switch" = "“case” ve “default” bir “switch” içinde olmalı."; -"web_parse_missing_do_while" = "“do { … }” sonrasında “while (koşul);” eksik."; -"web_parse_more_methods" = "Şimdilik burada yalnızca bir metot sayılır. Diğer alt programları ayrı sekmelerde oluştur."; -"web_parse_missing_import" = "En üstte %@ eksik. Bu satır olmadan Java Scanner’ı tanımaz."; -"web_parse_missing_scanner" = "Önce %@ eksik. Bu satır olmadan Scanner yoktur."; -"web_parse_unknown_type" = "Java bu veri türünü tanımıyor. %@ mı demek istedin?"; -"web_quickfix_insert_fmt" = "Öneri: %@ ekle"; -"web_quickfix_replace_fmt" = "Öneri: %2$@ yerine %1$@"; -"web_parse_wrong_case" = "Java büyük ve küçük harfleri ayırt eder. Doğrusu %@."; -"web_editor_title" = "Diyagram"; -"web_editor_show" = "Diyagramı göster"; -"web_editor_hide" = "Diyagramı gizle"; -"web_keywords_title" = "Kod anahtar sözcükleri"; -"web_keywords_hint" = "Öğe adları yerine if, while, for"; -"web_palette_compact" = "Öğeler her zaman kompakt"; -"web_palette_compact_hint" = "Simgenin altında küçük ad; aksi halde yalnızca sütun dar olduğunda"; -"web_about_version_fmt" = "Sürüm %@"; -"web_about_software" = "Kullanılan yazılımlar"; -"web_about_software_text" = "Kod düzenleyici CodeMirror ve Lezer kullanır (MIT lisansı). Swing desteğiyle VisuStruct, Leaning Technologies’in CheerpJ’sini (Community lisansı) ve OpenJDK’dan javac’ı yükler."; -"web_about_licenses" = "Lisans metinleri"; -"web_live_run" = "Çalıştır"; -"web_live_run_title" = "Programı çalıştır: çıktı ve girdi aşağıda görünür"; -"web_live_run_finished" = "Program bitti."; -"web_live_run_stopped" = "Durduruldu."; -"web_console" = "Konsol"; -"web_console_hide" = "Konsolu gizle"; -"web_console_resize" = "Konsolu yeniden boyutlandır"; -"web_diagram_tools" = "Diyagram araçları"; -"web_files_title" = "Dosyalar"; -"web_files_show" = "Dosyaları göster"; -"web_files_hide" = "Dosyaları gizle"; -"web_files_resize" = "Dosyaların genişliğini değiştir"; -"web_files_new_folder" = "Yeni klasör"; -"web_files_switch" = "Başka bir klasör seç"; -"web_files_delete" = "Sil"; -"web_files_in_browser" = "Bu tarayıcının belleğinde"; -"web_files_save_hint_fmt" = "Kaydetme programı %@ içine koyar."; -"web_files_empty" = "Henüz bir şey kaydedilmedi. Kaydettiğinde VisuStruct burada bugünün tarihiyle bir klasör oluşturur."; -"web_files_reconnect_hint" = "Yeniden yüklemeden sonra tarayıcı, VisuStruct’ın klasörünü kullanıp kullanamayacağını bir kez daha sorar."; -"web_files_reconnect_fmt" = "“%@” klasörünü aç"; -"web_files_connect_hint" = "Çalışma alanı olarak bir klasör seç. VisuStruct içinde her gün için bir klasör oluşturur, diyagramı ve kodu kaydeder."; -"web_files_connect" = "Klasör seç"; -"web_files_browser_hint" = "Bu tarayıcı cihazdaki klasörleri açamaz (ör. iPad’de). Dosyaların bu tarayıcının belleğinde kalır."; -"web_files_browser" = "Tarayıcıya kaydet"; -"web_files_delete_confirm_fmt" = "“%@” gerçekten silinsin mi?"; -"web_files_folder_prompt" = "Yeni klasörün adı:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Kaydedildi: %@"; -"web_files_create" = "Oluştur"; -"web_learn_ludwig_tip" = "Ludwig’in ipucu"; -"web_game_title" = "Oyun: Doğru mu yanlış mı?"; -"web_game_intro" = "Ludwig sana değerler ve bir koşul gösterir. Koşul doğru (true) mu yanlış (false) mı? On tur, giderek zorlaşır."; -"web_game_start" = "Oyunu başlat"; -"web_game_round_fmt" = "Tur %@ / %@"; -"web_game_score_fmt" = "Puan: %@"; -"web_game_true" = "doğru (true)"; -"web_game_false" = "yanlış (false)"; -"web_game_right" = "Doğru!"; -"web_game_wrong" = "Maalesef yanlış. Şöyle hesaplanır:"; -"web_game_next" = "İleri"; -"web_game_finish" = "Sonuç"; -"web_game_done_fmt" = "%@ / %@ doğru."; -"web_game_perfect" = "Hepsi doğru – Ludwig çok etkilendi!"; -"web_game_good" = "Harika! Hatalarına bir daha bak ve yeniden dene."; -"web_game_practice" = "Pratik mükemmelleştirir. Yukarıdaki doğruluk tablosunu tekrar oku ve bir tur daha oyna."; -"web_game_again" = "Tekrar oyna"; -"web_game_wrong_short" = "Maalesef yanlış."; -"web_game_practice_short" = "Pratik mükemmelleştirir. Açıklamaları tekrar oku ve bir tur daha oyna."; -"web_game_clicks" = "Tıklamalar:"; -"web_game_nobody" = "Kimse dinlemiyor – hiçbir şey olmuyor."; -"web_game_event_fmt" = "“%@” tıklandı: dinleyici bunu duyar ve kodunu çalıştırır."; -"web_game_try" = "Şimdi penceredeki düğmeleri kendin dene."; -"web_game_time_fmt" = "%@ sn kaldı"; -"web_game_defused" = "✂️ Etkisiz hâle getirildi! Hatayı buldun."; -"web_game_boom_time" = "💥 Bum! Süre doldu."; -"web_game_boom_wrong" = "💥 Bum! Yanlış satırdı."; -"web_game_boom_part" = "💥 Bum! Satır doğru, ama hata başka bir parçada."; -"web_game_boom_wrong_part" = "💥 Bum! Hata başka bir satırda."; -"web_game_bomb_parts_hint" = "Tam olarak yanlış parçaya dokun."; -"web_game_fix" = "Doğru satır:"; -"web_game_order_hint" = "Adımlara doğru sırayla dokun."; -"web_debug_step" = "Adım"; -"web_debug_step_title" = "Adım: bir komutu çalıştır ve yeniden dur"; -"web_debug_continue" = "Bir sonraki kesme noktasına kadar devam et"; -"web_debug_paused_fmt" = "%d. satırdan önce duraklatıldı."; -"web_debug_gutter_title" = "Kesme noktası koymak veya kaldırmak için satır numarasına tıkla"; -"web_swap_to_code" = "Programlama büyük"; -"web_swap_to_editor" = "Yapı diyagramı büyük"; -"web_editor_resize" = "Diyagram genişliğini değiştir"; -"web_palette_resize" = "Öğelerin genişliğini değiştir"; -"web_palette_hide" = "Öğeleri gizle"; -"web_palette_show" = "Öğeleri göster"; -"web_code_scopes" = "Renkli kod blokları"; -"web_code_scopes_hint" = "Sınıf, metot, döngü, dallanma kendi kutularında"; -"web_pseudocode" = "Sözde kod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Değişkeni yeniden adlandır …"; -"web_rename_title" = "Değişkeni yeniden adlandır"; -"web_rename_old" = "Ad"; -"web_rename_new" = "Yeni ad"; -"web_rename_hint" = "Tam olarak bu yazılışa sahip her yeri değiştirir (sayi, Sayi değildir), kodda ve yapı diyagramında. \"…\" içindeki metin ve yorumlar kalır."; -"web_rename_count_fmt" = "%d kez geçiyor."; -"web_rename_exists_fmt" = "“%@” zaten var: sonra ikisi tek bir değişken olur."; -"web_rename_invalid" = "Bir ad harfle başlar ve yalnızca harf, rakam ve _ içerir."; -"web_rename_keyword_fmt" = "“%@” bir Java anahtar sözcüğüdür."; -"web_rename_apply" = "Yeniden adlandır"; -"web_rename_cancel" = "İptal"; diff --git a/web/src/i18n/uk.strings b/web/src/i18n/uk.strings deleted file mode 100644 index 902f348a..00000000 --- a/web/src/i18n/uk.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* Тексти, які є лише у веб-версії. Усі інші беруться з Localizable.strings застосунку. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Структурна схема VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Експорт…"; -"web_export_title" = "Зберегти як зображення або PDF чи надрукувати"; -"web_export_png" = "Зображення (PNG)"; -"web_export_svg" = "Векторна графіка (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Файл VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Помилка експорту: %@"; -"web_print_hint" = "Через «Експорт… → PDF» можна зберегти схему, а потім надрукувати її."; -"web_delete" = "Видалити"; -"web_language_title" = "Мова інтерфейсу"; -"web_simulation_start" = "Запустити симуляцію"; -"web_simulation_end" = "Завершити симуляцію"; -"web_simulation_start_title" = "Виконати схему крок за кроком"; -"web_simulation_end_title" = "Завершити симуляцію та продовжити редагування"; -"web_palette_hint" = "Перетягніть у схему"; -"web_tabs_label" = "Схеми"; -"web_status_modified" = "змінено"; -"web_inspector_title" = "Властивості"; -"web_inspector_resize" = "Змінити висоту інспектора об’єктів: перетягніть лінію вгору або вниз (подвійне клацання: типово)"; -"web_inspector_help_drop" = "Перетягуйте елементи з палітри на плитку: зверху = перед, знизу = після, на «ø» = замінити. Наявні елементи можна переміщувати мишею."; -"web_inspector_help_inline" = "Двічі клацніть або торкніться елемента, щоб змінити текст прямо в діаграмі (Enter — застосувати, Esc — скасувати). Так само змінюються назви варіантів."; -"web_inspector_help_files" = "Файли також можна перетягнути у вікно."; -"web_case_name" = "Назва випадку"; -"web_case_name_placeholder" = "напр. 1, ok, default"; -"web_copied" = "Елемент скопійовано."; -"web_discard_message_fmt" = "Зміни в «%@» буде втрачено."; -"web_err_unreadable_fmt" = "«%@» не є придатним для читання файлом VisuStruct (%@)."; -"web_saved_fmt" = "«%@» збережено."; -"web_saved_txt_fmt" = "Збережено як «%@». Видаліть розширення .txt, щоб використати файл."; -"web_drop_impossible" = "Сюди не можна помістити"; -"web_sim_delay" = "Пауза на крок"; -"web_sim_delay_value_fmt" = "%@ с"; -"web_codegen_language" = "Мова"; -"web_codegen_comments" = "Текст схеми як коментарі"; -"web_codegen_empty_else" = "Виводити порожню гілку else в коді"; -"web_copied_check" = "Скопійовано ✓"; -"web_copy_manual" = "Виділено, натисніть Ctrl+C, щоб скопіювати"; -"web_run_stopped_loop" = "Виконання зупинено: можливий нескінченний цикл."; -"web_run_cancelled" = "Виконання скасовано."; -"web_run_error" = "Помилка під час виконання."; -"web_run_dropped_fmt" = "… приховано попередніх рядків: %d"; -"web_sim_call_arguments_fmt" = "Виклик не відповідає заголовку %@. Перевір значення в дужках."; -"web_sim_missing_return_fmt" = "%@ завершилася, не повернувши значення через return."; -"web_sim_file_not_found_fmt" = "Файлу з назвою %@ немає."; -"web_sim_end_of_file_fmt" = "У файлі %@ більше немає рядків."; -"web_sim_null_value_fmt" = "%@ дорівнює null."; -"web_sim_unknown_variable_case_fmt" = "Невідома змінна: %1$@. Java розрізняє великі й малі літери: можливо, %2$@?"; -"web_sim_written_file_fmt" = "%@ (збережено)"; -"web_site_home" = "Головна сторінка"; -"web_site_imprint" = "Вихідні дані"; -"web_site_privacy" = "Конфіденційність"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Програмування"; -"web_live_hide" = "Сховати програмування"; -"web_scale_canvas" = "Розмір тексту: структурна схема"; -"web_scale_code" = "Розмір тексту: програмування"; -"web_live_indent" = "Відступи"; -"web_live_indent_title" = "Розставити відступи: чотири пробіли на рівень (виділені рядки, інакше все)"; -"web_live_show" = "Показати програмування"; -"web_live_resize" = "Змінити ширину області коду (подвійний клік: скинути)"; -"web_live_hint" = "Код росте разом із діаграмою. Сірий = заповнювач: пиши в інспекторі, і текст з’явиться тут. Клацни рядок, щоб вибрати елемент."; -"web_live_ph_selector" = "змінна"; -"web_live_ph_code" = "Твій код"; -"web_live_ph_end" = "кінець"; -"web_live_language" = "Мова програмування"; -"web_live_java_class" = "Як клас"; -"web_live_java_class_hint" = "Назва класу = заголовок діаграми"; -"web_live_java_main" = "З методом main"; -"web_settings_display" = "Вигляд"; -"web_live_swing" = "Справжня Java (зі Swing)"; -"web_live_swing_hint" = "Справжня Java з вікнами (CheerpJ). Під час першого запуску одноразово завантажує близько 40 МБ."; -"web_live_swing_windows" = "Вікна Java"; -"web_live_swing_hide" = "Закрити вікна Java"; -"web_live_swing_loading" = "Завантаження Java (уперше близько 40 МБ) …"; -"web_live_swing_compiling" = "Компіляція …"; -"web_live_swing_running" = "Програма виконується."; -"web_live_swing_compile_failed" = "Помилка компіляції, див. вище."; -"web_live_swing_failed" = "Не вдалося завантажити Java. Перевір підключення до інтернету."; -"web_live_hint_edit" = "Набери тут код Java, і діаграма будується сама. Або працюй у діаграмі, і код росте разом із нею. Сірий = заповнювач, просто пиши поверх."; -"web_live_edit_label" = "Код Java"; -"web_parse_line_fmt" = "Рядок %d: %@"; -"web_parse_missing" = "Тут ще бракує: %@"; -"web_parse_extra" = "Зайве: %@"; -"web_parse_unclosed_string" = "Бракує закривного %@."; -"web_parse_else_without_if" = "Цей «else» не має «if»."; -"web_parse_case_outside_switch" = "«case» і «default» мають бути всередині «switch»."; -"web_parse_missing_do_while" = "Після «do { … }» бракує «while (умова);»."; -"web_parse_more_methods" = "Поки що тут враховується лише один метод. Інші підпрограми створи в окремих вкладках."; -"web_parse_missing_import" = "На самому початку бракує %@ Без цього рядка Java не знає Scanner."; -"web_parse_missing_scanner" = "Спочатку бракує %@ Без цього рядка Scanner не існує."; -"web_parse_unknown_type" = "Java не знає такого типу даних. Можливо, %@?"; -"web_quickfix_insert_fmt" = "Пропозиція: вставити %@"; -"web_quickfix_replace_fmt" = "Пропозиція: %1$@ замість %2$@"; -"web_parse_wrong_case" = "Java розрізняє великі й малі літери. Правильно: %@."; -"web_editor_title" = "Діаграма"; -"web_editor_show" = "Показати діаграму"; -"web_editor_hide" = "Сховати діаграму"; -"web_keywords_title" = "Ключові слова коду"; -"web_keywords_hint" = "if, while, for замість назв елементів"; -"web_palette_compact" = "Елементи завжди компактно"; -"web_palette_compact_hint" = "Значок із маленькою назвою під ним, інакше лише за вузької колонки"; -"web_about_version_fmt" = "Версія %@"; -"web_about_software" = "Використане програмне забезпечення"; -"web_about_software_text" = "Редактор коду використовує CodeMirror і Lezer (ліцензія MIT). З підтримкою Swing VisuStruct завантажує CheerpJ від Leaning Technologies (ліцензія Community) і javac з OpenJDK."; -"web_about_licenses" = "Тексти ліцензій"; -"web_live_run" = "Запустити"; -"web_live_run_title" = "Запустити програму: виведення і введення з’являться нижче"; -"web_live_run_finished" = "Програму завершено."; -"web_live_run_stopped" = "Зупинено."; -"web_console" = "Консоль"; -"web_console_hide" = "Сховати консоль"; -"web_console_resize" = "Змінити висоту консолі"; -"web_diagram_tools" = "Інструменти діаграми"; -"web_files_title" = "Файли"; -"web_files_show" = "Показати файли"; -"web_files_hide" = "Сховати файли"; -"web_files_resize" = "Змінити ширину файлів"; -"web_files_new_folder" = "Нова тека"; -"web_files_switch" = "Вибрати іншу теку"; -"web_files_delete" = "Видалити"; -"web_files_in_browser" = "У сховищі цього браузера"; -"web_files_save_hint_fmt" = "Під час збереження програма потрапляє в %@."; -"web_files_empty" = "Ще нічого не збережено. Під час збереження VisuStruct створює тут теку з сьогоднішньою датою."; -"web_files_reconnect_hint" = "Після перезавантаження браузер ще раз запитає, чи може VisuStruct використовувати твою теку."; -"web_files_reconnect_fmt" = "Відкрити теку «%@»"; -"web_files_connect_hint" = "Вибери теку як робочий простір. VisuStruct створює в ній теку для кожного дня і зберігає діаграму та код."; -"web_files_connect" = "Вибрати теку"; -"web_files_browser_hint" = "Цей браузер не може відкривати теки на пристрої (напр. на iPad). Твої файли залишаються у сховищі цього браузера."; -"web_files_browser" = "Зберегти в браузері"; -"web_files_delete_confirm_fmt" = "Справді видалити «%@»?"; -"web_files_folder_prompt" = "Назва нової теки:"; -"web_files_default_name" = "Програма"; -"web_files_saved_fmt" = "Збережено: %@"; -"web_files_create" = "Створити"; -"web_learn_ludwig_tip" = "Порада Людвіга"; -"web_game_title" = "Гра: Істина чи хиба?"; -"web_game_intro" = "Людвіг показує значення й умову. Умова істинна (true) чи хибна (false)? Десять раундів, щоразу складніше."; -"web_game_start" = "Почати гру"; -"web_game_round_fmt" = "Раунд %@ з %@"; -"web_game_score_fmt" = "Бали: %@"; -"web_game_true" = "істина (true)"; -"web_game_false" = "хиба (false)"; -"web_game_right" = "Правильно!"; -"web_game_wrong" = "Не зовсім. Ось як це обчислюється:"; -"web_game_next" = "Далі"; -"web_game_finish" = "Результат"; -"web_game_done_fmt" = "%@ з %@ правильно."; -"web_game_perfect" = "Усе правильно – Людвіг вражений!"; -"web_game_good" = "Чудово! Переглянь помилки й спробуй ще раз."; -"web_game_practice" = "Практика робить майстра. Перечитай таблицю істинності й зіграй ще раз."; -"web_game_again" = "Грати ще"; -"web_game_wrong_short" = "На жаль, неправильно."; -"web_game_practice_short" = "Повторення — мати навчання. Перечитай пояснення і зіграй ще раунд."; -"web_game_clicks" = "Кліки:"; -"web_game_nobody" = "Ніхто не слухає — нічого не відбувається."; -"web_game_event_fmt" = "Клік по «%@»: слухач чує це і виконує свій код."; -"web_game_try" = "Тепер спробуй кнопки у вікні сам."; -"web_game_time_fmt" = "Залишилось %@ с"; -"web_game_defused" = "✂️ Знешкоджено! Ти знайшов помилку."; -"web_game_boom_time" = "💥 Бум! Час вийшов."; -"web_game_boom_wrong" = "💥 Бум! Це був не той рядок."; -"web_game_boom_part" = "💥 Бум! Рядок правильний, але помилка в іншій його частині."; -"web_game_boom_wrong_part" = "💥 Бум! Помилка в іншому рядку."; -"web_game_bomb_parts_hint" = "Натисни точно на неправильну частину."; -"web_game_fix" = "Правильний рядок:"; -"web_game_order_hint" = "Натискай на кроки в правильному порядку."; -"web_debug_step" = "Крок"; -"web_debug_step_title" = "Крок: виконати одну інструкцію й знову зупинитися"; -"web_debug_continue" = "Далі до наступної точки зупинки"; -"web_debug_paused_fmt" = "Зупинено перед рядком %d."; -"web_debug_gutter_title" = "Клацни номер рядка, щоб поставити або прибрати точку зупинки"; -"web_swap_to_code" = "Програмування великим"; -"web_swap_to_editor" = "Структурограма великою"; -"web_editor_resize" = "Змінити ширину структурограми"; -"web_palette_resize" = "Змінити ширину елементів"; -"web_palette_hide" = "Сховати елементи"; -"web_palette_show" = "Показати елементи"; -"web_code_scopes" = "Кольорові блоки коду"; -"web_code_scopes_hint" = "Клас, метод, цикл, розгалуження в окремих рамках"; -"web_pseudocode" = "Псевдокод"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Перейменувати змінну …"; -"web_rename_title" = "Перейменувати змінну"; -"web_rename_old" = "Назва"; -"web_rename_new" = "Нова назва"; -"web_rename_hint" = "Змінює кожне місце з точно таким написанням (number — це не Number), у коді та на структурограмі. Текст у \"…\" і коментарі лишаються."; -"web_rename_count_fmt" = "Трапляється %d раз(и)."; -"web_rename_exists_fmt" = "«%@» уже існує: після цього обидві стануть однією змінною."; -"web_rename_invalid" = "Назва починається з літери й містить лише літери, цифри та _."; -"web_rename_keyword_fmt" = "«%@» — ключове слово Java."; -"web_rename_apply" = "Перейменувати"; -"web_rename_cancel" = "Скасувати"; diff --git a/web/src/i18n/zh-Hans.strings b/web/src/i18n/zh-Hans.strings deleted file mode 100644 index bc3b7ed5..00000000 --- a/web/src/i18n/zh-Hans.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* 仅存在于网页版中的文本。其他所有文本均来自应用的 Localizable.strings。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 结构图"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "导出…"; -"web_export_title" = "保存为图片或 PDF,或打印"; -"web_export_png" = "图片(PNG)"; -"web_export_svg" = "矢量图(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 文件(.visustruct)"; -"web_export_failed_fmt" = "导出失败:%@"; -"web_print_hint" = "请通过“导出… → PDF”保存图表,然后从那里打印。"; -"web_delete" = "删除"; -"web_language_title" = "界面语言"; -"web_simulation_start" = "开始模拟"; -"web_simulation_end" = "结束模拟"; -"web_simulation_start_title" = "逐步运行结构图"; -"web_simulation_end_title" = "结束模拟并继续编辑"; -"web_palette_hint" = "拖入结构图"; -"web_tabs_label" = "图表"; -"web_status_modified" = "已修改"; -"web_inspector_title" = "属性"; -"web_inspector_resize" = "更改对象检查器的高度:上下拖动或滑动分隔线(双击:默认)"; -"web_inspector_help_drop" = "将元素从面板拖到块上:上部 = 插入到前面,下部 = 插入到后面,拖到“ø”上 = 替换。已有元素可用鼠标移动。"; -"web_inspector_help_inline" = "双击或双点元素即可直接在图中编辑文字(Enter 确认,Esc 取消)。case 名称也一样。"; -"web_inspector_help_files" = "也可以直接将文件拖入窗口。"; -"web_case_name" = "分支名称"; -"web_case_name_placeholder" = "例如 1, ok, default"; -"web_copied" = "已复制元素。"; -"web_discard_message_fmt" = "对“%@”的更改将会丢失。"; -"web_err_unreadable_fmt" = "“%@”不是可读取的 VisuStruct 文件(%@)。"; -"web_saved_fmt" = "已保存“%@”。"; -"web_saved_txt_fmt" = "已另存为“%@”。去掉 .txt 扩展名后即可使用。"; -"web_drop_impossible" = "无法放置在此处"; -"web_sim_delay" = "每步暂停"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "语言"; -"web_codegen_comments" = "将图表文字作为注释"; -"web_codegen_empty_else" = "在代码中输出空的 else 分支"; -"web_copied_check" = "已复制 ✓"; -"web_copy_manual" = "已选中,按 Ctrl+C 复制"; -"web_run_stopped_loop" = "执行已停止:可能存在无限循环。"; -"web_run_cancelled" = "执行已取消。"; -"web_run_error" = "运行时出错。"; -"web_run_dropped_fmt" = "… 已隐藏之前的 %d 行"; -"web_sim_call_arguments_fmt" = "调用与 %@ 的首行不匹配。请检查括号中的值。"; -"web_sim_missing_return_fmt" = "%@ 结束时没有用 return 返回值。"; -"web_sim_file_not_found_fmt" = "没有名为 %@ 的文件。"; -"web_sim_end_of_file_fmt" = "文件 %@ 没有更多行了。"; -"web_sim_null_value_fmt" = "%@ 是 null。"; -"web_sim_unknown_variable_case_fmt" = "未知变量:%1$@。Java 区分大小写:你是指 %2$@ 吗?"; -"web_sim_written_file_fmt" = "%@(已保存)"; -"web_site_home" = "主页"; -"web_site_imprint" = "版权信息"; -"web_site_privacy" = "隐私"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "编程"; -"web_live_hide" = "隐藏编程"; -"web_scale_canvas" = "文字大小:结构图"; -"web_scale_code" = "文字大小:编程"; -"web_live_indent" = "缩进"; -"web_live_indent_title" = "缩进代码:每层四个空格(选中的行,否则全部)"; -"web_live_show" = "显示编程"; -"web_live_resize" = "更改代码区宽度(双击:重置)"; -"web_live_hint" = "代码随图一起生成。灰色 = 占位符:在检查器中输入,文字就会出现在这里。点击某行可选中对应元素。"; -"web_live_ph_selector" = "变量"; -"web_live_ph_code" = "你的代码"; -"web_live_ph_end" = "结束"; -"web_live_language" = "编程语言"; -"web_live_java_class" = "作为类"; -"web_live_java_class_hint" = "类名 = 结构图标题"; -"web_live_java_main" = "包含 main 方法"; -"web_settings_display" = "显示"; -"web_live_swing" = "真正的 Java(含 Swing)"; -"web_live_swing_hint" = "带窗口的真正 Java(CheerpJ)。首次运行时一次性下载约 40 MB。"; -"web_live_swing_windows" = "Java 窗口"; -"web_live_swing_hide" = "关闭 Java 窗口"; -"web_live_swing_loading" = "正在加载 Java(首次约 40 MB)…"; -"web_live_swing_compiling" = "正在编译…"; -"web_live_swing_running" = "程序正在运行。"; -"web_live_swing_compile_failed" = "编译错误,见上方。"; -"web_live_swing_failed" = "无法加载 Java。请检查网络连接。"; -"web_live_hint_edit" = "在这里输入 Java 代码,结构图会随之生成。或者在结构图中操作,代码也会一起增长。灰色 = 占位符,直接覆盖输入即可。"; -"web_live_edit_label" = "Java 代码"; -"web_parse_line_fmt" = "第 %d 行:%@"; -"web_parse_missing" = "这里还缺少:%@"; -"web_parse_extra" = "多余:%@"; -"web_parse_unclosed_string" = "这里缺少结尾的 %@。"; -"web_parse_else_without_if" = "这个“else”没有对应的“if”。"; -"web_parse_case_outside_switch" = "“case”和“default”应写在“switch”里面。"; -"web_parse_missing_do_while" = "“do { … }”之后还缺少“while (条件);”。"; -"web_parse_more_methods" = "目前这里只计入一个方法。请在单独的标签页中创建其他子程序。"; -"web_parse_missing_import" = "最上面缺少 %@ 没有这一行,Java 不认识 Scanner。"; -"web_parse_missing_scanner" = "前面缺少 %@ 没有这一行就没有 Scanner。"; -"web_parse_unknown_type" = "Java 不认识这个数据类型。你是想写 %@ 吗?"; -"web_quickfix_insert_fmt" = "建议:插入 %@"; -"web_quickfix_replace_fmt" = "建议:用 %1$@ 代替 %2$@"; -"web_parse_wrong_case" = "Java 区分大小写。正确写法是 %@。"; -"web_editor_title" = "结构图"; -"web_editor_show" = "显示结构图"; -"web_editor_hide" = "隐藏结构图"; -"web_keywords_title" = "代码关键字"; -"web_keywords_hint" = "用 if、while、for 代替元素名称"; -"web_palette_compact" = "元素始终紧凑显示"; -"web_palette_compact_hint" = "图标下方显示小字名称;否则仅在栏较窄时"; -"web_about_version_fmt" = "版本 %@"; -"web_about_software" = "使用的软件"; -"web_about_software_text" = "代码编辑器使用 CodeMirror 和 Lezer(MIT 许可证)。启用 Swing 支持后,VisuStruct 会加载 Leaning Technologies 的 CheerpJ(Community 许可证)和 OpenJDK 的 javac。"; -"web_about_licenses" = "许可证文本"; -"web_live_run" = "运行"; -"web_live_run_title" = "运行程序:输出和输入显示在下方"; -"web_live_run_finished" = "程序已结束。"; -"web_live_run_stopped" = "已停止。"; -"web_console" = "控制台"; -"web_console_hide" = "隐藏控制台"; -"web_console_resize" = "调整控制台高度"; -"web_diagram_tools" = "图表工具"; -"web_files_title" = "文件"; -"web_files_show" = "显示文件"; -"web_files_hide" = "隐藏文件"; -"web_files_resize" = "调整文件栏宽度"; -"web_files_new_folder" = "新建文件夹"; -"web_files_switch" = "选择其他文件夹"; -"web_files_delete" = "删除"; -"web_files_in_browser" = "在此浏览器的存储中"; -"web_files_save_hint_fmt" = "保存时程序将放入 %@。"; -"web_files_empty" = "还没有保存任何内容。保存时,VisuStruct 会在这里创建一个以今天日期命名的文件夹。"; -"web_files_reconnect_hint" = "重新加载后,浏览器会再次询问 VisuStruct 是否可以使用你的文件夹。"; -"web_files_reconnect_fmt" = "打开文件夹“%@”"; -"web_files_connect_hint" = "选择一个文件夹作为工作区。VisuStruct 会在其中为每一天创建文件夹,并保存图和代码。"; -"web_files_connect" = "选择文件夹"; -"web_files_browser_hint" = "此浏览器无法打开设备上的文件夹(例如 iPad)。你的文件会保留在此浏览器的存储中。"; -"web_files_browser" = "保存在浏览器中"; -"web_files_delete_confirm_fmt" = "确定删除“%@”?"; -"web_files_folder_prompt" = "新文件夹名称:"; -"web_files_default_name" = "程序"; -"web_files_saved_fmt" = "已保存:%@"; -"web_files_create" = "新建"; -"web_learn_ludwig_tip" = "路德维希的提示"; -"web_game_title" = "游戏:真还是假?"; -"web_game_intro" = "路德维希给你看一些值和一个条件。这个条件是真(true)还是假(false)?共十轮,越来越难。"; -"web_game_start" = "开始游戏"; -"web_game_round_fmt" = "第 %@ 轮,共 %@ 轮"; -"web_game_score_fmt" = "得分:%@"; -"web_game_true" = "真 (true)"; -"web_game_false" = "假 (false)"; -"web_game_right" = "答对了!"; -"web_game_wrong" = "不太对。是这样计算的:"; -"web_game_next" = "下一轮"; -"web_game_finish" = "结果"; -"web_game_done_fmt" = "答对 %@ / %@。"; -"web_game_perfect" = "全部正确——路德维希被你折服了!"; -"web_game_good" = "很棒!再看看错题,然后再试一次。"; -"web_game_practice" = "熟能生巧。再读一遍上面的真值表,再玩一轮吧。"; -"web_game_again" = "再玩一次"; -"web_game_wrong_short" = "很遗憾,答错了。"; -"web_game_practice_short" = "熟能生巧。再读一遍解释,再玩一轮吧。"; -"web_game_clicks" = "点击:"; -"web_game_nobody" = "没有人在监听——什么也不会发生。"; -"web_game_event_fmt" = "点击“%@”:监听器听到后执行它的代码。"; -"web_game_try" = "现在自己试试窗口里的按钮吧。"; -"web_game_time_fmt" = "还剩 %@ 秒"; -"web_game_defused" = "✂️ 拆除成功!你找到了错误。"; -"web_game_boom_time" = "💥 嘭!时间到了。"; -"web_game_boom_wrong" = "💥 嘭!这不是出错的那一行。"; -"web_game_boom_part" = "💥 砰!这一行对了,但错误在另一部分。"; -"web_game_boom_wrong_part" = "💥 砰!错误在另一行。"; -"web_game_bomb_parts_hint" = "准确点击出错的部分。"; -"web_game_fix" = "正确的写法:"; -"web_game_order_hint" = "按正确的顺序点击各个步骤。"; -"web_debug_step" = "单步"; -"web_debug_step_title" = "单步:执行一条语句后再次暂停"; -"web_debug_continue" = "继续运行到下一个断点"; -"web_debug_paused_fmt" = "在第 %d 行之前暂停。"; -"web_debug_gutter_title" = "点击行号可设置或删除断点"; -"web_swap_to_code" = "编程放大"; -"web_swap_to_editor" = "结构图放大"; -"web_editor_resize" = "调整结构图宽度"; -"web_palette_resize" = "调整元素宽度"; -"web_palette_hide" = "隐藏元素"; -"web_palette_show" = "显示元素"; -"web_code_scopes" = "代码块彩色显示"; -"web_code_scopes_hint" = "类、方法、循环、分支各有自己的框"; -"web_pseudocode" = "伪代码"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "重命名变量 …"; -"web_rename_title" = "重命名变量"; -"web_rename_old" = "名称"; -"web_rename_new" = "新名称"; -"web_rename_hint" = "在代码和结构图中更改所有拼写完全相同的地方(number 不是 Number)。\"…\" 中的文本和注释保持不变。"; -"web_rename_count_fmt" = "出现 %d 次。"; -"web_rename_exists_fmt" = "“%@”已存在:之后两者会成为同一个变量。"; -"web_rename_invalid" = "名称以字母开头,只包含字母、数字和 _。"; -"web_rename_keyword_fmt" = "“%@”是 Java 关键字。"; -"web_rename_apply" = "重命名"; -"web_rename_cancel" = "取消"; diff --git a/web/src/i18n/zh-Hant.strings b/web/src/i18n/zh-Hant.strings deleted file mode 100644 index 41b3a56b..00000000 --- a/web/src/i18n/zh-Hant.strings +++ /dev/null @@ -1,201 +0,0 @@ -/* 僅存在於網頁版的文字。其他所有文字皆來自 App 的 Localizable.strings。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 結構圖"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "匯出…"; -"web_export_title" = "儲存為圖片或 PDF,或列印"; -"web_export_png" = "圖片(PNG)"; -"web_export_svg" = "向量圖形(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 檔案(.visustruct)"; -"web_export_failed_fmt" = "匯出失敗:%@"; -"web_print_hint" = "請透過「匯出… → PDF」儲存圖表,再從那裡列印。"; -"web_delete" = "刪除"; -"web_language_title" = "介面語言"; -"web_simulation_start" = "開始模擬"; -"web_simulation_end" = "結束模擬"; -"web_simulation_start_title" = "逐步執行結構圖"; -"web_simulation_end_title" = "結束模擬並繼續編輯"; -"web_palette_hint" = "拖曳到結構圖中"; -"web_tabs_label" = "圖表"; -"web_status_modified" = "已修改"; -"web_inspector_title" = "屬性"; -"web_inspector_resize" = "變更物件檢閱器的高度:上下拖曳或滑動分隔線(按兩下:預設)"; -"web_inspector_help_drop" = "將元素從面板拖曳到區塊上:上方 = 插入在前,下方 = 插入在後,拖到「ø」上 = 取代。現有元素可用滑鼠移動。"; -"web_inspector_help_inline" = "雙擊或雙點元素即可直接在圖中編輯文字(Enter 確認,Esc 取消)。case 名稱也一樣。"; -"web_inspector_help_files" = "也可以直接將檔案拖曳到視窗中。"; -"web_case_name" = "分支名稱"; -"web_case_name_placeholder" = "例如 1, ok, default"; -"web_copied" = "已複製元素。"; -"web_discard_message_fmt" = "對「%@」所做的變更將會遺失。"; -"web_err_unreadable_fmt" = "「%@」不是可讀取的 VisuStruct 檔案(%@)。"; -"web_saved_fmt" = "已儲存「%@」。"; -"web_saved_txt_fmt" = "已儲存為「%@」。移除 .txt 副檔名後即可使用。"; -"web_drop_impossible" = "無法放置在此處"; -"web_sim_delay" = "每步暫停"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "語言"; -"web_codegen_comments" = "將圖表文字作為註解"; -"web_codegen_empty_else" = "在程式碼中輸出空的 else 分支"; -"web_copied_check" = "已複製 ✓"; -"web_copy_manual" = "已選取,按 Ctrl+C 複製"; -"web_run_stopped_loop" = "執行已停止:可能是無限迴圈。"; -"web_run_cancelled" = "執行已取消。"; -"web_run_error" = "執行時發生錯誤。"; -"web_run_dropped_fmt" = "… 已隱藏先前的 %d 行"; -"web_sim_call_arguments_fmt" = "呼叫與 %@ 的首行不相符。請檢查括號中的值。"; -"web_sim_missing_return_fmt" = "%@ 結束時沒有用 return 傳回值。"; -"web_sim_file_not_found_fmt" = "沒有名為 %@ 的檔案。"; -"web_sim_end_of_file_fmt" = "檔案 %@ 沒有更多行了。"; -"web_sim_null_value_fmt" = "%@ 是 null。"; -"web_sim_unknown_variable_case_fmt" = "未知變數:%1$@。Java 區分大小寫:你是指 %2$@ 嗎?"; -"web_sim_written_file_fmt" = "%@(已儲存)"; -"web_site_home" = "首頁"; -"web_site_imprint" = "版權資訊"; -"web_site_privacy" = "隱私"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "程式設計"; -"web_live_hide" = "隱藏程式設計"; -"web_scale_canvas" = "文字大小:結構圖"; -"web_scale_code" = "文字大小:程式設計"; -"web_live_indent" = "縮排"; -"web_live_indent_title" = "縮排程式碼:每層四個空格(選取的行,否則全部)"; -"web_live_show" = "顯示程式設計"; -"web_live_resize" = "變更程式碼區寬度(按兩下:重設)"; -"web_live_hint" = "程式碼會隨圖一起產生。灰色 = 預留位置:在檢閱器中輸入,文字就會出現在這裡。點一下某行可選取對應元素。"; -"web_live_ph_selector" = "變數"; -"web_live_ph_code" = "你的程式碼"; -"web_live_ph_end" = "結束"; -"web_live_language" = "程式語言"; -"web_live_java_class" = "作為類別"; -"web_live_java_class_hint" = "類別名稱 = 結構圖標題"; -"web_live_java_main" = "包含 main 方法"; -"web_settings_display" = "顯示"; -"web_live_swing" = "真正的 Java(含 Swing)"; -"web_live_swing_hint" = "帶視窗的真正 Java(CheerpJ)。首次執行時一次性下載約 40 MB。"; -"web_live_swing_windows" = "Java 視窗"; -"web_live_swing_hide" = "關閉 Java 視窗"; -"web_live_swing_loading" = "正在載入 Java(首次約 40 MB)…"; -"web_live_swing_compiling" = "正在編譯…"; -"web_live_swing_running" = "程式執行中。"; -"web_live_swing_compile_failed" = "編譯錯誤,見上方。"; -"web_live_swing_failed" = "無法載入 Java。請檢查網路連線。"; -"web_live_hint_edit" = "在這裡輸入 Java 程式碼,結構圖會隨之產生。或者在結構圖中操作,程式碼也會一起成長。灰色 = 預留位置,直接覆寫輸入即可。"; -"web_live_edit_label" = "Java 程式碼"; -"web_parse_line_fmt" = "第 %d 行:%@"; -"web_parse_missing" = "這裡還缺少:%@"; -"web_parse_extra" = "多餘:%@"; -"web_parse_unclosed_string" = "這裡缺少結尾的 %@。"; -"web_parse_else_without_if" = "這個「else」沒有對應的「if」。"; -"web_parse_case_outside_switch" = "「case」和「default」應寫在「switch」裡面。"; -"web_parse_missing_do_while" = "「do { … }」之後還缺少「while (條件);」。"; -"web_parse_more_methods" = "目前這裡只計入一個方法。請在單獨的分頁中建立其他子程式。"; -"web_parse_missing_import" = "最上面缺少 %@ 沒有這一行,Java 不認識 Scanner。"; -"web_parse_missing_scanner" = "前面缺少 %@ 沒有這一行就沒有 Scanner。"; -"web_parse_unknown_type" = "Java 不認識這個資料型別。你是想寫 %@ 嗎?"; -"web_quickfix_insert_fmt" = "建議:插入 %@"; -"web_quickfix_replace_fmt" = "建議:用 %1$@ 代替 %2$@"; -"web_parse_wrong_case" = "Java 區分大小寫。正確寫法是 %@。"; -"web_editor_title" = "結構圖"; -"web_editor_show" = "顯示結構圖"; -"web_editor_hide" = "隱藏結構圖"; -"web_keywords_title" = "程式碼關鍵字"; -"web_keywords_hint" = "用 if、while、for 取代元素名稱"; -"web_palette_compact" = "元素一律精簡顯示"; -"web_palette_compact_hint" = "圖示下方顯示小字名稱;否則僅在欄位較窄時"; -"web_about_version_fmt" = "版本 %@"; -"web_about_software" = "使用的軟體"; -"web_about_software_text" = "程式碼編輯器使用 CodeMirror 與 Lezer(MIT 授權)。啟用 Swing 支援後,VisuStruct 會載入 Leaning Technologies 的 CheerpJ(Community 授權)與 OpenJDK 的 javac。"; -"web_about_licenses" = "授權條款全文"; -"web_live_run" = "執行"; -"web_live_run_title" = "執行程式:輸出與輸入顯示在下方"; -"web_live_run_finished" = "程式已結束。"; -"web_live_run_stopped" = "已停止。"; -"web_console" = "主控台"; -"web_console_hide" = "隱藏主控台"; -"web_console_resize" = "調整主控台高度"; -"web_diagram_tools" = "圖表工具"; -"web_files_title" = "檔案"; -"web_files_show" = "顯示檔案"; -"web_files_hide" = "隱藏檔案"; -"web_files_resize" = "調整檔案欄寬度"; -"web_files_new_folder" = "新增資料夾"; -"web_files_switch" = "選擇其他資料夾"; -"web_files_delete" = "刪除"; -"web_files_in_browser" = "在此瀏覽器的儲存空間中"; -"web_files_save_hint_fmt" = "儲存時程式會放入 %@。"; -"web_files_empty" = "還沒有儲存任何內容。儲存時,VisuStruct 會在這裡建立一個以今天日期命名的資料夾。"; -"web_files_reconnect_hint" = "重新載入後,瀏覽器會再次詢問 VisuStruct 是否可以使用你的資料夾。"; -"web_files_reconnect_fmt" = "開啟資料夾「%@」"; -"web_files_connect_hint" = "選擇一個資料夾作為工作區。VisuStruct 會在其中為每一天建立資料夾,並儲存圖與程式碼。"; -"web_files_connect" = "選擇資料夾"; -"web_files_browser_hint" = "此瀏覽器無法開啟裝置上的資料夾(例如 iPad)。你的檔案會保留在此瀏覽器的儲存空間中。"; -"web_files_browser" = "儲存在瀏覽器中"; -"web_files_delete_confirm_fmt" = "確定刪除「%@」?"; -"web_files_folder_prompt" = "新資料夾名稱:"; -"web_files_default_name" = "程式"; -"web_files_saved_fmt" = "已儲存:%@"; -"web_files_create" = "新增"; -"web_learn_ludwig_tip" = "路德維希的提示"; -"web_game_title" = "遊戲:真還是假?"; -"web_game_intro" = "路德維希給你看一些值和一個條件。這個條件是真(true)還是假(false)?共十輪,越來越難。"; -"web_game_start" = "開始遊戲"; -"web_game_round_fmt" = "第 %@ 輪,共 %@ 輪"; -"web_game_score_fmt" = "得分:%@"; -"web_game_true" = "真 (true)"; -"web_game_false" = "假 (false)"; -"web_game_right" = "答對了!"; -"web_game_wrong" = "不太對。是這樣計算的:"; -"web_game_next" = "下一輪"; -"web_game_finish" = "結果"; -"web_game_done_fmt" = "答對 %@ / %@。"; -"web_game_perfect" = "全部正確——路德維希被你折服了!"; -"web_game_good" = "很棒!再看看錯題,然後再試一次。"; -"web_game_practice" = "熟能生巧。再讀一遍上面的真值表,再玩一輪吧。"; -"web_game_again" = "再玩一次"; -"web_game_wrong_short" = "很可惜,答錯了。"; -"web_game_practice_short" = "熟能生巧。再讀一遍說明,再玩一輪吧。"; -"web_game_clicks" = "點擊:"; -"web_game_nobody" = "沒有人在監聽——什麼也不會發生。"; -"web_game_event_fmt" = "點擊「%@」:監聽器聽到後執行它的程式碼。"; -"web_game_try" = "現在自己試試視窗裡的按鈕吧。"; -"web_game_time_fmt" = "還剩 %@ 秒"; -"web_game_defused" = "✂️ 拆除成功!你找到了錯誤。"; -"web_game_boom_time" = "💥 砰!時間到了。"; -"web_game_boom_wrong" = "💥 砰!這不是出錯的那一行。"; -"web_game_boom_part" = "💥 砰!這一行對了,但錯誤在另一部分。"; -"web_game_boom_wrong_part" = "💥 砰!錯誤在另一行。"; -"web_game_bomb_parts_hint" = "準確點擊出錯的部分。"; -"web_game_fix" = "正確的寫法:"; -"web_game_order_hint" = "按正確的順序點擊各個步驟。"; -"web_debug_step" = "單步"; -"web_debug_step_title" = "單步:執行一條敘述後再次暫停"; -"web_debug_continue" = "繼續執行到下一個中斷點"; -"web_debug_paused_fmt" = "在第 %d 行之前暫停。"; -"web_debug_gutter_title" = "點擊行號可設定或刪除中斷點"; -"web_swap_to_code" = "程式設計放大"; -"web_swap_to_editor" = "結構圖放大"; -"web_editor_resize" = "調整結構圖寬度"; -"web_palette_resize" = "調整元素寬度"; -"web_palette_hide" = "隱藏元素"; -"web_palette_show" = "顯示元素"; -"web_code_scopes" = "程式碼區塊彩色顯示"; -"web_code_scopes_hint" = "類別、方法、迴圈、分支各有自己的框"; -"web_pseudocode" = "虛擬碼"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "重新命名變數 …"; -"web_rename_title" = "重新命名變數"; -"web_rename_old" = "名稱"; -"web_rename_new" = "新名稱"; -"web_rename_hint" = "在程式碼和結構圖中更改所有拼寫完全相同的地方(number 不是 Number)。\"…\" 中的文字和註解保持不變。"; -"web_rename_count_fmt" = "出現 %d 次。"; -"web_rename_exists_fmt" = "「%@」已存在:之後兩者會成為同一個變數。"; -"web_rename_invalid" = "名稱以字母開頭,只包含字母、數字和 _。"; -"web_rename_keyword_fmt" = "「%@」是 Java 關鍵字。"; -"web_rename_apply" = "重新命名"; -"web_rename_cancel" = "取消"; diff --git a/web/src/learn/boolGame.ts b/web/src/learn/boolGame.ts deleted file mode 100644 index 7b144d46..00000000 --- a/web/src/learn/boolGame.ts +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Spiel „Wahr oder falsch?“ (Block `::: spiel` im Lernbereich): Werte und eine Bedingung zeigen, - * die Antwort prüfen und die Auswertung Schritt für Schritt erklären. - */ - -export type GameVar = { name: string; kind: 'int'; min: number; max: number } | { name: string; kind: 'boolean' } - -export interface BoolGame { - vars: GameVar[] - /** Bedingungen vom leichten zum schweren. */ - conditions: string[] -} - -export type Values = Record - -export class GameFormatError extends Error {} - -/** Zeilen `alter: 8..20`, `schueler: boolean` und `? bedingung`. */ -export function parseBoolGame(lines: string[], file: string): BoolGame { - const vars: GameVar[] = [] - const conditions: string[] = [] - for (const raw of lines) { - const line = raw.trim() - if (!line) continue - if (line.startsWith('?')) { - const condition = line.slice(1).trim() - evaluate(condition, sampleValues(vars)) // Fehler früh melden - conditions.push(condition) - continue - } - const m = /^([A-Za-z_]\w*)\s*:\s*(?:(-?\d+)\s*\.\.\s*(-?\d+)|(boolean))$/.exec(line) - if (!m) throw new GameFormatError(`${file}: Unbekannte Zeile im Spiel: „${line}“`) - vars.push(m[4] ? { name: m[1], kind: 'boolean' } : { name: m[1], kind: 'int', min: Number(m[2]), max: Number(m[3]) }) - } - if (!conditions.length) throw new GameFormatError(`${file}: Das Spiel braucht mindestens eine Zeile „? bedingung“.`) - return { vars, conditions } -} - -function sampleValues(vars: GameVar[]): Values { - return Object.fromEntries(vars.map((v) => [v.name, v.kind === 'boolean' ? true : v.min])) -} - -// MARK: - Auswerten - -type Token = { kind: 'num'; value: number } | { kind: 'bool'; value: boolean } | { kind: 'name'; value: string } | { kind: 'op'; value: string } - -function tokenize(text: string): Token[] { - const out: Token[] = [] - const re = /\s*(?:(\d+)|(true|false)\b|([A-Za-z_]\w*)|(&&|\|\||==|!=|<=|>=|<|>|!|\(|\)))/y - let pos = 0 - while (pos < text.length) { - if (/^\s*$/.test(text.slice(pos))) break - re.lastIndex = pos - const m = re.exec(text) - if (!m) throw new GameFormatError(`Unerwartetes Zeichen in „${text}“ bei „${text.slice(pos)}“`) - pos = re.lastIndex - if (m[1] !== undefined) out.push({ kind: 'num', value: Number(m[1]) }) - else if (m[2] !== undefined) out.push({ kind: 'bool', value: m[2] === 'true' }) - else if (m[3] !== undefined) out.push({ kind: 'name', value: m[3] }) - else out.push({ kind: 'op', value: m[4] }) - } - return out -} - -/** Bedingung mit diesen Werten auswerten (nur ganze Zahlen, Wahrheitswerte, Vergleiche, !, && und ||). */ -export function evaluate(text: string, values: Values): boolean { - const tokens = tokenize(text) - let i = 0 - const peek = () => tokens[i] - const isOp = (v: string) => peek()?.kind === 'op' && peek()!.value === v - const fail = (): never => { - throw new GameFormatError(`Bedingung nicht lesbar: „${text}“`) - } - - const primary = (): number | boolean => { - const tok = tokens[i++] ?? fail() - if (tok.kind === 'num' || tok.kind === 'bool') return tok.value - if (tok.kind === 'name') { - if (!(tok.value in values)) throw new GameFormatError(`Unbekannte Variable „${tok.value}“ in „${text}“`) - return values[tok.value] - } - if (tok.value === '(') { - const v = or() - if (!isOp(')')) fail() - i++ - return v - } - if (tok.value === '!') { - const v = primary() - if (typeof v !== 'boolean') fail() - return !v - } - return fail() - } - const comparison = (): number | boolean => { - const left = primary() - const op = peek() - if (op?.kind === 'op' && ['==', '!=', '<', '<=', '>', '>='].includes(op.value)) { - i++ - const right = primary() - switch (op.value) { - case '==': - return left === right - case '!=': - return left !== right - } - if (typeof left !== 'number' || typeof right !== 'number') fail() - const [a, b] = [left as number, right as number] - return op.value === '<' ? a < b : op.value === '<=' ? a <= b : op.value === '>' ? a > b : a >= b - } - return left - } - const and = (): number | boolean => { - let v = comparison() - while (isOp('&&')) { - i++ - const r = comparison() - v = (v as boolean) && (r as boolean) - } - return v - } - const or = (): number | boolean => { - let v = and() - while (isOp('||')) { - i++ - const r = and() - v = (v as boolean) || (r as boolean) - } - return v - } - const result = or() - if (i !== tokens.length || typeof result !== 'boolean') return fail() - return result -} - -// MARK: - Erklären - -/** - * Auswertung in Zwischenschritten, z. B. `alter >= 12 && alter < 18` → - * `15 >= 12 && 15 < 18` → `true && true` → `true`. - */ -export function explain(text: string, values: Values): string[] { - const steps = [text] - const substituted = text.replace(/\b[A-Za-z_]\w*\b/g, (name) => (name in values ? String(values[name]) : name)) - if (substituted !== text) steps.push(substituted) - // Jeden Vergleich durch sein Ergebnis ersetzen. - const compared = substituted.replace(/(-?\d+|true|false)\s*(==|!=|<=|>=|<|>)\s*(-?\d+|true|false)/g, (m) => String(evaluate(m, {}))) - if (compared !== steps[steps.length - 1]) steps.push(compared) - const result = String(evaluate(text, values)) - if (result !== steps[steps.length - 1]) steps.push(result) - return steps -} - -// MARK: - Runden - -/** Zahlen, mit denen in der Bedingung verglichen wird: dort und knapp daneben sind die spannenden Fälle. */ -function edgeValues(condition: string, name: string): number[] { - const out: number[] = [] - const re = new RegExp(`\\b${name}\\s*(?:==|!=|<=|>=|<|>)\\s*(-?\\d+)|(-?\\d+)\\s*(?:==|!=|<=|>=|<|>)\\s*${name}\\b`, 'g') - for (const m of condition.matchAll(re)) { - const c = Number(m[1] ?? m[2]) - out.push(c - 1, c, c + 1) - } - return out -} - -export interface Round { - condition: string - values: Values - answer: boolean -} - -/** Runde `index` von `count`: je später, desto schwerer die Bedingung. */ -export function makeRound(game: BoolGame, index: number, count: number, random: () => number = Math.random): Round { - const n = game.conditions.length - const base = Math.floor((index * n) / count) - const pick = Math.min(n - 1, base + (random() < 0.5 ? 0 : 1)) - const condition = game.conditions[pick] - const values: Values = {} - for (const v of game.vars) { - if (v.kind === 'boolean') { - values[v.name] = random() < 0.5 - continue - } - const edges = edgeValues(condition, v.name).filter((x) => x >= v.min && x <= v.max) - values[v.name] = edges.length && random() < 0.6 ? edges[Math.floor(random() * edges.length)] : v.min + Math.floor(random() * (v.max - v.min + 1)) - } - return { condition, values, answer: evaluate(condition, values) } -} - -/** Nur die Variablen, die in der Bedingung vorkommen. */ -export function usedValues(round: Round): [string, number | boolean][] { - return Object.entries(round.values).filter(([name]) => new RegExp(`\\b${name}\\b`).test(round.condition)) -} diff --git a/web/src/learn/cloze.ts b/web/src/learn/cloze.ts deleted file mode 100644 index 6d6d4893..00000000 --- a/web/src/learn/cloze.ts +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Lückentext: fertiger Code, in dem einzelne Wörter fehlen. - * - * ::: lückentext - * titel: Lückentext: Zählschleife - * auftrag: Ergänze die fehlenden Wörter. - * ```java - * for ([[int]] i = 1; i [[<=]] 10; [[i++ | i += 1 | i = i + 1]]) { - * ``` - * erklärung: Wird gezeigt, wenn alle Lücken stimmen. - * ::: - * - * Mehrere Codeblöcke stehen untereinander, z. B. eine Methode und das Hauptprogramm. - * `[[a]]` ist eine Lücke. Weitere richtige Antworten stehen mit ` | ` (Leerzeichen davor und danach) - * dahinter, damit `||` und `|` selbst als Antwort gehen: `[[i++ | i += 1]]`. - */ -export interface Cloze { - title: string - task: string - /** Codeblöcke (z. B. Methode und Hauptprogramm wie in eigenen Reitern), darin Zeilen aus Text und Lücken. */ - blocks: ClozePart[][][] - explanation?: string -} - -export type ClozePart = string | { answers: string[] } - -const gapPattern = /\[\[(.+?)\]\]/g - -export function parseCloze(inner: string[], file: string): Cloze { - const cloze: Cloze = { title: '', task: '', blocks: [] } - const codes: string[][] = [] - let inCode = false - for (const raw of inner) { - if (/^\s*```/.test(raw)) { - if (!inCode) codes.push([]) - inCode = !inCode - continue - } - if (inCode) { - codes[codes.length - 1].push(raw.replace(/\s+$/, '')) - continue - } - const line = raw.trim() - if (!line) continue - const m = /^([a-zäöü]+):\s*(.*)$/i.exec(line) - if (!m) throw new Error(`${file}: Unbekannte Zeile im Lückentext: „${line}“.`) - const value = m[2].trim() - switch (m[1].toLowerCase()) { - case 'titel': cloze.title = value; break - case 'auftrag': cloze.task = value; break - case 'erklärung': - case 'erklaerung': cloze.explanation = value; break - default: throw new Error(`${file}: Unbekannter Schlüssel „${m[1]}“ im Lückentext.`) - } - } - if (inCode) throw new Error(`${file}: Der Codeblock im Lückentext wird nicht mit \`\`\` geschlossen.`) - if (!cloze.title) throw new Error(`${file}: Ein Lückentext braucht einen Titel.`) - if (!codes.some((c) => c.some((l) => l.trim()))) throw new Error(`${file}: Lückentext „${cloze.title}“ braucht einen Codeblock.`) - cloze.blocks = codes.map((code) => { - // Gemeinsame Einrückung entfernen, damit der Code links beginnt. - const indent = Math.min(...code.filter((l) => l.trim()).map((l) => /^ */.exec(l)![0].length)) - return code.map((l) => splitGaps(l.slice(indent), file, cloze.title)) - }) - if (!clozeGaps(cloze).length) { - throw new Error(`${file}: Lückentext „${cloze.title}“ braucht mindestens eine Lücke [[…]].`) - } - return cloze -} - -function splitGaps(line: string, file: string, title: string): ClozePart[] { - const parts: ClozePart[] = [] - let at = 0 - for (const m of line.matchAll(gapPattern)) { - if (m.index! > at) parts.push(line.slice(at, m.index)) - const answers = m[1].split(/\s+\|\s+/).map((a) => a.trim()).filter(Boolean) - if (!answers.length) throw new Error(`${file}: Leere Lücke im Lückentext „${title}“.`) - parts.push({ answers }) - at = m.index! + m[0].length - } - if (at < line.length) parts.push(line.slice(at)) - if (parts.some((p) => typeof p === 'string' && (p.includes('[[') || p.includes(']]')))) { - throw new Error(`${file}: Unvollständige Lücke im Lückentext „${title}“: „${line}“.`) - } - return parts -} - -/** Alle Lücken in Reihenfolge. */ -export function clozeGaps(cloze: Cloze): string[][] { - return cloze.blocks.flat().flatMap((l) => l.flatMap((p) => (typeof p === 'string' ? [] : [p.answers]))) -} - -/** Leerzeichen zählen nicht: `i = i + 1` und `i=i+1` sind gleich. Groß/klein zählt (wie in Java). */ -export function clozeAnswerRight(answers: string[], input: string): boolean { - const norm = (s: string) => s.replace(/\s+/g, '') - return norm(input) !== '' && answers.some((a) => norm(a) === norm(input)) -} diff --git a/web/src/learn/content.ts b/web/src/learn/content.ts deleted file mode 100644 index 8feda1c0..00000000 --- a/web/src/learn/content.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { registerTextFile } from '../core/simulation' -import { decodeDocument } from '../core/xmlCodec' -import { parseWorldMap, type RobotWorldSetup } from '../core/robotWorld' -import { setMBotColorLanguage } from '../core/mbot' -import type { VSDocument } from '../core/model' -import { parseGlossary, type GlossaryEntry } from './glossary' -import { parseLesson, parseTask, type Lesson, type Task } from './lessonFormat' - -/** - * Lektionen, Aufgaben und Diagramme aus `web/lessons` (werden beim Bauen eingebettet). - * Deutsch steht in `de/`, Englisch in `en/`, Portugiesisch (Portugal) in `pt/`; alle anderen Oberflächensprachen - * zeigen Englisch. Fehlt eine Datei in einer Sprache, gilt die englische (Holger-Entscheidung: Rückfall immer Englisch). - * Diagramme und Bilder mit übersetztem Text liegen unter gleichem Namen in `diagramme//` und `bilder//`. - */ - -export type ContentLanguage = 'de' | 'en' | 'pt' -export const contentLanguages: ContentLanguage[] = ['de', 'en', 'pt'] - -const lessonFiles = import.meta.glob('../../lessons/*/*.md', { query: '?raw', import: 'default', eager: true }) as Record -const taskFiles = import.meta.glob('../../lessons/*/aufgaben/*.md', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> -// VisuBot (mBot2-Modus): eigene Lektionen und Aufgaben in `/visubot/`, die normalen bleiben unberührt. -const visuBotLessonFiles = import.meta.glob('../../lessons/*/visubot/*.md', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> -// VisuBot mit Block-Ansicht (Holger 5.11.): kurze Lektionen für die 5./6. Klasse in `/visubot/bloecke/`. -const blockLessonFiles = import.meta.glob('../../lessons/*/visubot/bloecke/*.md', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> -// Dazu eigene Aufgaben zum Knobeln in `/visubot/bloecke/aufgaben/` (Holger 5.11.). -const blockTaskFiles = import.meta.glob('../../lessons/*/visubot/bloecke/aufgaben/*.md', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> -// VisuBot im Kurs: zusätzliche Schritte für normale Lektionen, Dateiname beginnt mit deren Nummer (`13-for-schleife.md`). -const visuBotTaskFiles = import.meta.glob('../../lessons/*/visubot/aufgaben/*.md', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> -const diagramFiles = import.meta.glob('../../lessons/diagramme/**/*.visustruct', { query: '?raw', import: 'default', eager: true }) as Record< - string, - string -> - -// VisuBot: Weltkarten für Übungen (`welt: labyrinth`), Zeichen wie in `parseWorldMap`. -const worldFiles = import.meta.glob('../../lessons/welten/*.txt', { query: '?raw', import: 'default', eager: true }) as Record - -const imageFiles = import.meta.glob('../../lessons/bilder/**/*.{jpg,png,webp,svg}', { query: '?url', import: 'default', eager: true }) as Record< - string, - string -> - -const baseName = (path: string) => path.replace(/^.*\//, '') - -/** Dateien von `lang`, ergänzt um die englischen, die es dort nicht gibt. */ -function filesFor(files: Record, folder: RegExp, lang: ContentLanguage): [string, string][] { - const pick = (l: string) => new Map(Object.entries(files).filter(([p]) => folder.exec(p)?.[1] === l).map(([p, s]) => [baseName(p), s])) - const merged = new Map([...pick('en'), ...(lang === 'en' ? [] : pick(lang))]) - return [...merged] -} - -/** Inhaltssprache zur Oberflächensprache: Deutsch für Deutsch, Portugiesisch für Portugiesisch, sonst Englisch. */ -export function contentLanguageFor(uiLanguage: string): ContentLanguage { - return uiLanguage === 'de' ? 'de' : uiLanguage === 'pt-PT' ? 'pt' : 'en' -} - -export let language: ContentLanguage = 'en' -export let lessons: Lesson[] = [] -export let tasks: Task[] = [] -export let reference = '' -export let glossary: GlossaryEntry[] = [] -/** - * Zwei Welten (Holger, 27.10.): VisuStruct (`off`, der Kurs ohne Roboter) oder VisuBot (`on`). - * VisuBot zeigt seit 3.11. nur die Roboter-Lektionen; für reines Java verweisen sie auf VisuStruct. - */ -export type VisuBotMode = 'off' | 'on' -export let visuBot: VisuBotMode = 'off' - -/** - * Block-Ansicht in VisuBot (Holger 5.11.): Sie ist für Jüngere (5./6. Klasse) gedacht. Ist sie an, zeigt der Lernbereich - * kurze Lektionen zum Ausprobieren aus `visubot/bloecke/` statt der Java-Reihe. Gibt es in der Sprache (und auf Englisch) - * keine, bleibt es bei der normalen Reihe. - */ -export let blockLessons = false - -/** Block-Lektionen ein- oder ausschalten; `true`, wenn sich der Inhalt dadurch ändert. */ -export function useBlockLessons(on: boolean): boolean { - if (on === blockLessons) return false - blockLessons = on - lessons = [] - useContentLanguage(language) - return visuBot === 'on' -} - -/** Zeigt der Lernbereich gerade die Block-Lektionen? */ -export const showingBlockLessons = () => visuBot === 'on' && blockLessons && lessons.some((l) => l.blocks) - -/** VisuBot-Modus für den Lernbereich wählen; Nachschlagen und Fachbegriffe bleiben dieselben. */ -export function useVisuBotContent(mode: VisuBotMode) { - if (mode === visuBot) return - visuBot = mode - lessons = [] - useContentLanguage(language) -} - -/** Wählt die Sprache der Lektionen; die Exporte oben zeigen danach die Inhalte dieser Sprache. */ -export function useContentLanguage(lang: ContentLanguage) { - setMBotColorLanguage(lang) - if (lang === language && lessons.length > 0) return - language = lang - const pages = filesFor(lessonFiles, /\/lessons\/([^/]+)\/[^/]+\.md$/, lang) - const parseLessons = (files: [string, string][], renumber?: Map) => - files - .filter(([name]) => /^\d+/.test(name)) - .map(([name, source]) => parseLesson(renumbered(source, renumber), `${lang}/${name}`)) - .sort((a, b) => a.number - b.number) - const parseTasks = (files: [string, string][], folder: string, renumber?: Map) => - files.map(([name, source]) => { - const task = parseTask(renumbered(source, renumber), `${lang}/${folder}${name}`) - return renumber && task.after !== undefined ? { ...task, after: renumber.get(task.after) ?? task.after } : task - }) - const normalTaskFiles = filesFor(taskFiles, /\/lessons\/([^/]+)\/aufgaben\/[^/]+\.md$/, lang) - const juniorFiles = visuBot === 'on' && blockLessons ? filesFor(blockLessonFiles, /\/lessons\/([^/]+)\/visubot\/bloecke\/[^/]+\.md$/, lang) : [] - if (juniorFiles.some(([name]) => /^\d+/.test(name))) { - // Block-Lektionen: eigene kurze Reihe und eigene, kindgerechte Aufgaben (die normalen sind für Ältere) - const series = visuBotSeries(juniorFiles) - lessons = parseLessons(juniorFiles, series.renumber) - .map((l) => ({ ...l, number: series.number.get(l.id)!, robot: true, blocks: true })) - .sort((a, b) => a.number - b.number) - const juniorTaskFiles = filesFor(blockTaskFiles, /\/lessons\/([^/]+)\/visubot\/bloecke\/aufgaben\/[^/]+\.md$/, lang) - tasks = parseTasks(juniorTaskFiles, 'visubot/bloecke/aufgaben/', series.renumber).map((t) => ({ ...t, robot: true })) - } else if (visuBot === 'on') { - // Seit 3.11. (Holger): VisuBot zeigt nur die Roboter-Lektionen und -Aufgaben, normales Java steht in VisuStruct. - const robotLessonFiles = filesFor(visuBotLessonFiles, /\/lessons\/([^/]+)\/visubot\/[^/]+\.md$/, lang) - const series = visuBotSeries(robotLessonFiles) - lessons = parseLessons(robotLessonFiles, series.renumber) - .map((l) => ({ ...l, number: series.number.get(l.id)!, robot: true })) - .sort((a, b) => a.number - b.number) - const robotTaskFiles = filesFor(visuBotTaskFiles, /\/lessons\/([^/]+)\/visubot\/aufgaben\/[^/]+\.md$/, lang) - tasks = parseTasks(robotTaskFiles, 'visubot/aufgaben/', series.renumber).map((t) => ({ ...t, robot: true })) - } else { - // Gleiches Durchzählen wie bei VisuBot: seit 4.11. ist Lektion 1 geteilt (`01-was-ist-…`, `01-weitere-darstellungen`). - const lessonFiles = pages.filter(([name]) => /^\d+/.test(name)) - const series = visuBotSeries(lessonFiles) - lessons = parseLessons(lessonFiles, series.renumber) - .map((l) => ({ ...l, number: series.number.get(l.id)! })) - .sort((a, b) => a.number - b.number) - tasks = parseTasks(normalTaskFiles, 'aufgaben/', series.renumber) - } - tasks.sort((a, b) => (a.after ?? 0) - (b.after ?? 0) || a.level - b.level || a.title.localeCompare(b.title, lang)) - // Textdateien aus den Lektionen kann die Simulation mit `Files.readString(Path.of("name"))` lesen. - for (const block of [...lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks)), ...tasks.flatMap((t) => t.blocks)]) { - if (block.kind === 'textFile') registerTextFile(block.name, block.content) - } - reference = pages.find(([name]) => name === 'nachschlagen.md')?.[1] ?? '' - // VisuBot: mBot2-Befehle und -Sensoren als eigene Kategorien - if (visuBot !== 'off') { - const robotReference = filesFor(visuBotLessonFiles, /\/lessons\/([^/]+)\/visubot\/[^/]+\.md$/, lang).find(([name]) => name === 'nachschlagen.md')?.[1] - if (robotReference) reference = `${reference.trimEnd()}\n\n${robotReference}` - } - glossary = parseGlossary(pages.find(([name]) => name === 'begriffe.md')?.[1] ?? '') -} - -/** - * Nummern der Lektionsreihe (VisuBot und seit 4.11. auch VisuStruct): die Roboter-Lektionen in Dateireihenfolge, durchgezählt ab 1. So kann eine neue Lektion - * mit gleicher Nummer und späterem Namen (`07-linie.md`, `07-wand-oder-linie.md`) eingeschoben werden, ohne Dateien - * umzubenennen (Lernstand hängt am Dateinamen). Verweise „Lektion N“ in den Texten meinen die Dateinummer N und - * werden auf die Nummer in der Reihe umgeschrieben (bei gleicher Nummer zählt die erste Datei). - */ -export function visuBotSeries(files: [string, string][]): { number: Map; renumber: Map } { - const names = files - .map(([name]) => name) - .filter((name) => /^\d+/.test(name)) - .sort((a, b) => Number(/^\d+/.exec(a)![0]) - Number(/^\d+/.exec(b)![0]) || a.localeCompare(b)) - const number = new Map() - const renumber = new Map() - names.forEach((name, i) => { - number.set(name.replace(/\.md$/, ''), i + 1) - const fileNumber = Number(/^\d+/.exec(name)![0]) - if (!renumber.has(fileNumber)) renumber.set(fileNumber, i + 1) - }) - return { number, renumber } -} - -/** Verweise wie „Lektion 5“, „Lektionen 6 bis 13“, „Lesson 5“ oder „lição 5“ auf die neue Nummer umschreiben. */ -function renumbered(source: string, map?: Map): string { - if (!map) return source - return source.replace( - /\b(Lektion(?:en)?|Lessons?|Li[cç](?:ão|ões))(\s+\d+(?:\s*(?:,|bis|und|and|to|a|e|–|-)\s*\d+)*)/gi, - (_, word: string, numbers: string) => word + numbers.replace(/\d+/g, (n) => String(map.get(Number(n)) ?? n)), - ) -} - -useContentLanguage('en') - -/** Deutsch: `folder/name`, sonst die englische Fassung. Sonst `folder//name`, dann `folder/en/name`, dann `folder/name`. */ -function localized(files: Map, file: string): string | undefined { - if (language === 'de') return files.get(file) ?? files.get(`en/${file}`) - return (language === 'en' ? undefined : files.get(`${language}/${file}`)) ?? files.get(`en/${file}`) ?? files.get(file) -} - -const relative = (path: string, folder: string) => path.slice(path.indexOf(folder) + folder.length) - -const diagrams = new Map(Object.entries(diagramFiles).map(([path, xml]) => [relative(path, '/diagramme/'), xml])) - -export function diagram(file: string): VSDocument | undefined { - const xml = localized(diagrams, file) - return xml === undefined ? undefined : decodeDocument(xml) -} - -const images = new Map(Object.entries(imageFiles).map(([path, url]) => [relative(path, '/bilder/'), url])) - -/** Adresse eines Bildes aus `web/lessons/bilder` (übersetzt aus `bilder/`, falls vorhanden). */ -export function image(file: string): string | undefined { - return localized(images, file) -} - -const worldMaps = new Map(Object.entries(worldFiles).map(([path, text]) => [baseName(path).replace(/\.txt$/, ''), text])) -const parsedWorlds = new Map() - -/** Welt aus `web/lessons/welten/.txt`; `undefined`, wenn es sie nicht gibt. */ -export function world(name: string): RobotWorldSetup | undefined { - const text = worldMaps.get(name) - if (text === undefined) return undefined - let setup = parsedWorlds.get(name) - if (!setup) parsedWorlds.set(name, (setup = parseWorldMap(text))) - return setup -} - -export const worldNames = () => [...worldMaps.keys()] diff --git a/web/src/learn/driveGame.ts b/web/src/learn/driveGame.ts deleted file mode 100644 index e3e288aa..00000000 --- a/web/src/learn/driveGame.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Spiel „Wo landet der mBot2?“ (Block `::: spiel fahrt`): Jede Runde zeigt Java-Code für den mBot2 und eine kleine - * Karte. Man tippt auf das Feld, auf dem der mBot2 am Ende steht; danach fährt er die Strecke vor. - * - * Format (Schlüsselwörter auf Deutsch, auch in den Übersetzungen): - * - * titel: Spiel: Wo landet der mBot2? - * intro: … - * frage: Tippe auf das Feld, auf dem der mBot2 am Ende steht. (für alle Runden, je Runde überschreibbar) - * feld: 30 (Zentimeter je Feld, 10, 20 oder 30; Standard 30) - * - * runde: - * mbot.forward(60); Code, mit vier Leerzeichen eingerückt - * karte: ..... | ..... | ..^.. Zeilen von hinten nach vorn, Zeichen wie in den Weltkarten - * ziel: 2,0 Spalte,Zeile ab 0, wo er am Ende steht (der Test rechnet nach) - * > Erklärung - * - * Kartenzeichen: `.` Boden, `#` Wand, `=` schwarzes Feld, `r g b y` farbige Felder, `^ > v <` Start mit Blickrichtung. - */ - -import { parseWorldMap, type Pose, type RobotWorldSetup } from '../core/robotWorld' -import { previewRun, type PreviewResult } from '../core/robotPreview' -import { parseLiveJava } from '../core/javaParser' -import { documentFromJava } from '../core/liveSource' -import type { LiveLabels } from '../core/codeGenerator' - -export interface DriveRound { - code: string - /** Kartenzeilen, oben ist hinten. */ - map: string[] - /** Spalte und Zeile (ab 0) des Zielfelds. */ - target: { col: number; row: number } - question: string - explanation: string -} - -export interface DriveGame { - title: string - intro: string - /** Zentimeter je Feld. */ - field: number - rounds: DriveRound[] -} - -export class DriveGameError extends Error {} - -export function parseDriveGame(lines: string[], file: string): DriveGame { - const fail = (msg: string): never => { - throw new DriveGameError(`${file}: ${msg}`) - } - let title = '' - let intro = '' - let question = '' - let field = 30 - const rounds: (Omit & { target?: DriveRound['target'] })[] = [] - let current: (typeof rounds)[number] | undefined - let inCode = false - for (const raw of lines) { - const line = raw.trim() - if (current && inCode && (/^ {4}|^\t/.test(raw) || line === '')) { - current.code += (current.code ? '\n' : '') + raw.replace(/^( {4}|\t)/, '') - continue - } - inCode = false - if (!line) continue - const key = /^(titel|intro|frage|feld|runde|karte|ziel):\s*(.*)$/.exec(line) - if (key) { - const [, name, value] = key - if (name === 'runde') { - current = { code: '', map: [], question, explanation: '' } - rounds.push(current) - inCode = true - } else if (!current) { - if (name === 'titel') title = value - else if (name === 'intro') intro = value - else if (name === 'frage') question = value - else if (name === 'feld') field = [10, 20, 30].includes(Number(value)) ? Number(value) : fail('„feld:“ ist 10, 20 oder 30 (Zentimeter).') - else fail(`„${name}:“ gehört in eine Runde.`) - } else if (name === 'frage') current.question = value - else if (name === 'karte') current.map = value.split('|').map((r) => r.trim()) - else if (name === 'ziel') { - const m = /^(\d+)\s*,\s*(\d+)$/.exec(value) - if (!m) fail(`„ziel: ${value}“ muss Spalte,Zeile sein, zum Beispiel „ziel: 2,0“.`) - current.target = { col: Number(m![1]), row: Number(m![2]) } - } else fail(`„${name}:“ gehört vor die erste Runde.`) - continue - } - if (current && line.startsWith('>')) current.explanation += (current.explanation ? ' ' : '') + line.slice(1).trim() - else fail(`Unbekannte Zeile im Spiel: „${line}“`) - } - if (!title) fail('Das Spiel braucht eine Zeile „titel:“.') - if (!rounds.length) fail('Das Spiel braucht mindestens eine „runde:“.') - rounds.forEach((round, i) => { - round.code = round.code.replace(/\n+$/, '') - if (!round.code.trim()) fail(`Runde ${i + 1}: Code fehlt.`) - if (!round.map.length || round.map.some((r) => r.length !== round.map[0].length)) fail(`Runde ${i + 1}: „karte:“ fehlt oder die Zeilen sind verschieden lang.`) - if (round.map.join('').replace(/[^\^> v <).`) - if (/[^.#=rgby^>= round.map.length || round.target!.col >= round.map[0].length) fail(`Runde ${i + 1}: „ziel:“ liegt außerhalb der Karte.`) - if (!round.question) fail(`Runde ${i + 1}: keine Frage.`) - }) - return { title, intro, field, rounds: rounds as DriveRound[] } -} - -/** Die Welt einer Runde: jedes Kartenzeichen ist ein Feld von `field` cm. */ -export function driveWorld(round: DriveRound, field: number): RobotWorldSetup { - return parseWorldMap([`raster: ${field / 10}`, ...round.map].join('\n')) -} - -/** Feld (Spalte, Zeile) unter einer Lage, oder undefined außerhalb der Karte. */ -export function cellOf(pose: Pose, round: DriveRound, field: number): { col: number; row: number } | undefined { - const width = round.map[0].length * field - const depth = round.map.length * field - const col = Math.floor((pose.x + width / 2) / field) - const row = Math.floor((depth / 2 - pose.y) / field) - if (col < 0 || row < 0 || col >= round.map[0].length || row >= round.map.length) return undefined - return { col, row } -} - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - -export interface DriveResult { - run: PreviewResult - end: Pose - cell?: { col: number; row: number } - problems: string[] -} - -/** Den Code der Runde in der Welt durchfahren, wie die Vorschau im Editor. */ -export function driveRound(round: DriveRound, field: number): DriveResult { - const parsed = parseLiveJava(round.code, labels) - const problems = parsed.problems.map((p) => (typeof p === 'string' ? p : JSON.stringify(p))) - const doc = documentFromJava({ caption: '', elements: [] }, parsed) - const run = previewRun(doc, undefined, driveWorld(round, field)) - const end = run.frames[run.frames.length - 1].pose - return { run, end, cell: cellOf(end, round, field), problems } -} diff --git a/web/src/learn/glossary.ts b/web/src/learn/glossary.ts deleted file mode 100644 index 506da425..00000000 --- a/web/src/learn/glossary.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** Fachbegriffe aus `lessons//begriffe.md`: `### Begriff`, optional `auch: Form, Form` (englisch `also:`), danach die Erklärung. */ - -export interface GlossaryEntry { - term: string - /** Alle Schreibweisen, die im Text erkannt werden (der Begriff selbst zuerst). */ - forms: string[] - text: string -} - -export function parseGlossary(source: string): GlossaryEntry[] { - const entries: GlossaryEntry[] = [] - for (const line of source.replace(/\r\n/g, '\n').split('\n')) { - const heading = /^###\s+(.+)$/.exec(line) - const entry = entries.at(-1) - if (heading) { - const term = heading[1].trim() - entries.push({ term, forms: [term], text: '' }) - } else if (entry && /^(auch|also):/i.test(line)) { - entry.forms.push(...line.replace(/^(auch|also):/i, '').split(',').map((f) => f.trim()).filter(Boolean)) - } else if (entry && line.trim() !== '') { - entry.text = (entry.text + ' ' + line.trim()).trim() - } - } - return entries.filter((e) => e.text !== '') -} - -const escapeRegExp = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - -/** Findet ganze Wörter (nicht Teil eines längeren Worts oder einer Bindestrich-Zusammensetzung). */ -export function termPattern(entries: GlossaryEntry[]): RegExp | undefined { - const forms = entries.flatMap((e) => e.forms).sort((a, b) => b.length - a.length) - if (forms.length === 0) return undefined - return new RegExp(`(? e.forms.includes(word)) -} diff --git a/web/src/learn/lessonFormat.ts b/web/src/learn/lessonFormat.ts deleted file mode 100644 index ca8d2968..00000000 --- a/web/src/learn/lessonFormat.ts +++ /dev/null @@ -1,543 +0,0 @@ -import type { ExerciseTest } from '../core/exerciseCheck' -import { driveSignature, isMBotPart, MBOT_PARTS, MBOT_SENSORS, parseActionSpec, parseDriveSpec, type MBotPart, type MBotSensorValue } from '../core/mbot' -import { parseBoolGame, type BoolGame } from './boolGame' -import { isRoundGameKind, parseRoundGame, type RoundGame } from './roundGame' -import { parseCrosswordGame, parseRescueGame, type CrosswordGame, type RescueGame } from './rescueGame' -import { parseDriveGame, type DriveGame } from './driveGame' -import { parseCloze, type Cloze } from './cloze' -import { parsePap, type Pap } from './pap' - -/** - * Lektionen und Aufgaben als Markdown mit Kopfzeilen und `:::`-Blöcken (siehe `lessons/README.md`). - * Jede `## Überschrift` beginnt einen neuen Schritt. - */ - -export type Block = - | { kind: 'markdown'; text: string } - | { kind: 'diagram'; file: string; caption: string } - | { kind: 'exercise'; exercise: Exercise } - | { kind: 'checklist'; items: ChecklistItem[] } - /** Kompetenzen am Anfang der Lektion („Diese Kompetenzen erwerbe ich jetzt“). */ - | { kind: 'competences'; items: string[] } - | { kind: 'quiz'; questions: QuizQuestion[] } - | { kind: 'brag'; text: string; tip?: boolean } - | { kind: 'images'; files: string[]; caption: string } - /** `labeled`: beschriftete Fassung, die ein Klick auf die Grafik ein- und ausblendet. - * `animated` (`::: grafik datei.svg animiert`): SVG mit Animation, die einmal abläuft und sich wiederholen lässt. */ - | { kind: 'graphic'; file: string; labeled?: string; animated?: boolean; caption: string } - | { kind: 'game'; game: BoolGame } - | { kind: 'roundGame'; game: RoundGame } - | { kind: 'rescueGame'; game: RescueGame } - | { kind: 'driveGame'; game: DriveGame } - | { kind: 'crosswordGame'; game: CrosswordGame } - | { kind: 'caesar'; key: number; text: string } - /** VisuBot: drehbares 3D-Modell des mBot2, Teile antippen zeigt ihre Erklärung. */ - /** VisuBot: der mBot2 in 3D; die Kamera fährt zu den Teilen in `focus` (Tour wie bei JavaPicoGoat). */ - | { kind: 'robotModel'; focus: MBotPart[]; view?: string; spin?: boolean } - /** VisuBot: Teile im Modell anklicken („Wo ist der Ultraschallsensor?“). */ - | { kind: 'robotQuiz'; tasks: { part: MBotPart; question: string }[]; done?: string } - /** Fertiger Code mit Lücken für einzelne Wörter. */ - | { kind: 'cloze'; cloze: Cloze } - /** Programmablaufplan mit leeren Plätzen, die fertigen Elemente legt man hinein. */ - | { kind: 'pap'; pap: Pap } - /** Eine Textdatei zum Einlesen: `Files.readString(Path.of("name"))` liefert ihren Inhalt. */ - | { kind: 'textFile'; name: string; content: string } - -export interface Exercise { - id: string - title: string - task: string - /** Startdiagramm (Datei in `lessons/diagramme`); sonst leeres Diagramm. */ - start?: string - tests: ExerciseTest[] - /** Elementarten, die vorkommen müssen (Schlüssel wie in der Palette, z. B. `verzweigung`). */ - requires: string[] - /** Gestufte Tipps (eine `tipp:`-Zeile je Tipp), vom allgemeinen zum genauen. */ - hints: string[] - /** Musterlösung (Datei in `lessons/diagramme`), nur im Lehrermodus sichtbar. */ - solution?: string - /** Unterprogramme zur Musterlösung (Dateien in `lessons/diagramme`), je eins pro Reiter. */ - subprograms: string[] - /** `code: java`: Die Übung wird in Code live auf Java gelöst; „Übung öffnen“ öffnet die Spalte gleich mit. */ - code?: 'java' - /** `bereich: struktogramm | code | beide`: wo man die Übung löst; „Im Editor öffnen“ öffnet genau diese Bereiche. Ohne Angabe: `code` bei `code: java`, sonst `struktogramm`. */ - area?: ExerciseArea - /** VisuBot: Schleifen müssen mindestens so tief ineinander stecken (`schleifentiefe: 2`). */ - loopDepth?: number -} - -export type ExerciseArea = 'struktogramm' | 'code' | 'beide' - -/** Bereich, in dem man die Übung löst (siehe `bereich:`). */ -export function exerciseArea(ex: Pick): ExerciseArea { - return ex.area ?? (ex.code === 'java' ? 'code' : 'struktogramm') -} - -export interface ChecklistItem { - text: string - /** Titel des Schritts, in dem der Punkt erklärt wird (`- Text → Schritt`). */ - ref?: string - /** Index dieses Schritts in der Lektion, von `parseLesson` aufgelöst. */ - step?: number -} - -export interface QuizQuestion { - question: string - options: { text: string; correct: boolean }[] - /** Zuordnung: Zu jedem linken Begriff wird der passende rechte gewählt. */ - pairs?: { left: string; right: string }[] - explanation?: string - /** Schritt, den die Frage prüft (`→ Titel`); verbindet sie mit dem Checklistenpunkt zu diesem Schritt. */ - ref?: string - step?: number -} - -export interface Step { - title: string - blocks: Block[] -} - -export interface Lesson { - id: string - number: number - title: string - section: string - summary: string - steps: Step[] - /** VisuBot-Lektion in der durchgehenden Reihe der VisuBot-Welt (bekommt ein 🤖 in der Liste). */ - robot?: boolean - /** Kurze Block-Lektion für Jüngere (VisuBot mit Block-Ansicht, `visubot/bloecke/`). */ - blocks?: boolean -} - -export interface Task { - id: string - title: string - /** Nummer der Lektion, die man vorher abgeschlossen haben sollte. */ - after?: number - level: number - blocks: Block[] - /** VisuBot-Aufgabe in der durchgehenden Reihe der VisuBot-Welt. */ - robot?: boolean -} - -export class FormatError extends Error {} - -function frontMatter(source: string, file: string): { meta: Map; body: string } { - const m = /^---\n([\s\S]*?)\n---\n?/.exec(source.replace(/\r\n/g, '\n')) - if (!m) throw new FormatError(`${file}: Kopfzeilen (--- … ---) fehlen.`) - const meta = new Map() - for (const line of m[1].split('\n')) { - const kv = /^([a-zäöüß]+):\s*(.*)$/i.exec(line.trim()) - if (kv) meta.set(kv[1].toLowerCase(), kv[2].trim()) - } - return { meta, body: source.replace(/\r\n/g, '\n').slice(m[0].length) } -} - -/** `3, 4 -> 7 | 8` = Eingaben 3 und 4, erwartete Ausgabezeilen 7 und 8. */ -export function parseTest(spec: string, file: string): ExerciseTest { - const parts = spec.split(/\s*(?:->|→)\s*/) - if (parts.length !== 2) throw new FormatError(`${file}: Test „${spec}“ braucht genau ein „->“.`) - const list = (s: string, sep: RegExp) => (s.trim() === '' || s.trim() === '-' ? [] : s.split(sep).map((x) => x.trim())) - return { inputs: list(parts[0], /\s*,\s*/), expected: list(parts[1], /\s*\|\s*/) } -} - -/** `getLoudness=0/80, isButtonA=true -> forward 30, ledOn` (siehe `messwerte:`). */ -export function parseSensorTest(value: string): ExerciseTest { - const arrow = /^(.*?)\s*(?:->|→)\s*(.*)$/.exec(value.trim()) - if (!arrow) throw new Error(`„messwerte: ${value}“ braucht ein „->“.`) - const sensors: Record = {} - for (const part of arrow[1].split(/\s*,\s*/).filter(Boolean)) { - const m = /^(\w+)\s*=\s*(.+)$/.exec(part) - if (!m || !(m[1] in MBOT_SENSORS)) throw new Error(`Unbekannter Sensor in „${part}“.`) - sensors[m[1]] = m[2].split('/').map((v) => { - const t = v.trim() - if (t === 'true' || t === 'false') return t === 'true' - if (/^-?\d+(\.\d+)?$/.test(t)) return Number(t) - // Text, z. B. eine Farbe: getColor=rot - if (typeof MBOT_SENSORS[m[1]].simulated === 'string') return t - throw new Error(`Messwert „${t}“ ist keine Zahl und nicht true/false.`) - }) - } - return robotExpectation({ inputs: [], expected: [], sensors }, arrow[2]) -} - -/** Rechte Seite von `welt: … ->` und `messwerte: … ->`: Fahrt sowie `ledOn`, `ledOff`, `tone`, `noTone`, `none` (fährt nicht), `drives` (fährt irgendwie), `screen "Text"`. */ -function robotExpectation(test: ExerciseTest, spec: string): ExerciseTest { - const drive: string[] = [] - // `screen "Abstand: 42 cm"`: dieser Text steht am Ende auf dem Bildschirm (darf Kommas enthalten) - spec = spec.replace(/\bscreen\s+("(?:[^"\\]|\\.)*")/u, (_, text: string) => { - test.screen = JSON.parse(text) as string - return '' - }) - for (const token of spec.split(/\s*,\s*/).filter(Boolean)) { - if (token === 'ledOn' || token === 'ledOff') test.led = token === 'ledOn' - else if (token === 'tone' || token === 'noTone') test.tone = token === 'tone' - else if (token === 'none') test.drive = [] - else if (token === 'drives') test.drives = true - else drive.push(token) - } - if (drive.length) test.drive = driveSignature(parseDriveSpec(drive.join(', '))) - return test -} - -function parseExercise(id: string, lines: string[], file: string): Exercise { - const ex: Exercise = { id, title: '', task: '', tests: [], requires: [], hints: [], subprograms: [] } - let last: 'task' | 'hint' | undefined - for (const raw of lines) { - const kv = /^(titel|auftrag|start|test|datei|fahrt|befehle|welt|messwerte|schleifentiefe|braucht|tipp|lösung|loesung|unterprogramm|code|bereich):\s*(.*)$/i.exec(raw.trim()) - if (kv) { - const [, key, value] = kv - last = undefined - switch (key.toLowerCase()) { - case 'titel': ex.title = value; break - case 'auftrag': ex.task = value; last = 'task'; break - case 'start': ex.start = value; break - case 'test': ex.tests.push(parseTest(value, file)); break - case 'datei': { - // `datei: neu.txt -> Heu | Äpfel`: Diese Datei soll das Programm im letzten Test speichern. - const m = /^([\w.-]+)\s*(?:->|→)\s*(.*)$/.exec(value.trim()) - if (!m || !ex.tests.length) throw new FormatError(`${file}: Übung „${id}“: „datei:“ braucht vorher einen Test und die Form „name.txt -> Zeile | Zeile“.`) - const expected = ex.tests[ex.tests.length - 1] - expected.files = [...(expected.files ?? []), { name: m[1], lines: m[2].trim() === '' ? [] : m[2].split(/\s*\|\s*/).map((x) => x.trim()) }] - break - } - case 'fahrt': - // VisuBot: `fahrt: 4 x (forward 50, turnRight 90)` – so soll der Roboter fahren (ohne Ausgabe, ohne Eingabe). - try { - ex.tests.push({ inputs: [], expected: [], drive: driveSignature(parseDriveSpec(value)) }) - } catch (err) { - throw new FormatError(`${file}: Übung „${id}“: ${(err as Error).message}`) - } - break - case 'welt': { - // VisuBot: `welt: wand-nah, wand-weit` – in jeder dieser Welten muss der Roboter ohne Anstoßen ins Ziel. - // `welt: wand-nah -> turnRight` – in dieser Welt soll er genau so fahren (für Verzweigungen mit Sensoren). - const arrow = /^([\w-]+)\s*(?:->|→)\s*(.+)$/.exec(value.trim()) - if (arrow) { - try { - ex.tests.push(robotExpectation({ inputs: [], expected: [], world: arrow[1] }, arrow[2])) - } catch (err) { - throw new FormatError(`${file}: Übung „${id}“: ${(err as Error).message}`) - } - break - } - for (const name of value.split(/\s*,\s*/).filter(Boolean)) { - if (!/^[\w-]+$/.test(name)) throw new FormatError(`${file}: Übung „${id}“: Weltname „${name}“ (nur Buchstaben, Ziffern, -).`) - ex.tests.push({ inputs: [], expected: [], world: name }) - } - break - } - case 'befehle': - // VisuBot: `befehle: 3 x (setLed 255 0 0, pause 500, ledOff, pause 500)` – genau diese Befehle für LEDs, Ton und Bildschirm. - try { - ex.tests.push({ inputs: [], expected: [], actions: parseActionSpec(value) }) - } catch (err) { - throw new FormatError(`${file}: Übung „${id}“: ${(err as Error).message}`) - } - break - case 'messwerte': - // VisuBot: `messwerte: getLoudness=0/80, isButtonA=false -> forward, ledOn` – mit diesen Sensorwerten - // (nacheinander, durch / getrennt) soll der Roboter so fahren bzw. leuchten (`ledOn`, `ledOff`, `tone`, `noTone`, `none`). - try { - ex.tests.push(parseSensorTest(value)) - } catch (err) { - throw new FormatError(`${file}: Übung „${id}“: ${(err as Error).message}`) - } - break - case 'schleifentiefe': - ex.loopDepth = Number(value) - if (!Number.isInteger(ex.loopDepth) || ex.loopDepth < 1) throw new FormatError(`${file}: Übung „${id}“: „schleifentiefe:“ braucht eine Zahl ab 1.`) - break - case 'braucht': ex.requires.push(...value.split(/\s*,\s*/).filter(Boolean)); break - case 'tipp': ex.hints.push(value); last = 'hint'; break - case 'lösung': - case 'loesung': ex.solution = value; break - case 'unterprogramm': ex.subprograms.push(...value.split(/\s*,\s*/).filter(Boolean)); break - case 'code': - if (value.trim().toLowerCase() !== 'java') throw new FormatError(`${file}: Übung „${id}“: „code:“ kennt nur „java“.`) - ex.code = 'java' - break - case 'bereich': { - const area = value.trim().toLowerCase() - if (area !== 'struktogramm' && area !== 'code' && area !== 'beide') throw new FormatError(`${file}: Übung „${id}“: „bereich:“ kennt nur „struktogramm“, „code“ oder „beide“.`) - ex.area = area - break - } - } - } else if (last && raw.trim() !== '') { - // Fortsetzungszeilen für Auftrag und Tipp - if (last === 'task') ex.task += '\n' + raw.trim() - else ex.hints[ex.hints.length - 1] += '\n' + raw.trim() - } else if (raw.trim() === '' && last === 'task') { - ex.task += '\n' - } - } - if (!ex.title) throw new FormatError(`${file}: Übung „${id}“ hat keinen Titel.`) - if (!ex.task.trim()) throw new FormatError(`${file}: Übung „${id}“ hat keinen Auftrag.`) - ex.task = ex.task.trim() - return ex -} - -function parseQuiz(lines: string[], file: string): QuizQuestion[] { - const questions: QuizQuestion[] = [] - for (const raw of lines) { - const line = raw.trim() - if (line.startsWith('?')) { - questions.push({ question: line.slice(1).trim(), options: [] }) - } else if (/^- \[[ xX]\]/.test(line)) { - const q = questions.at(-1) - if (!q) throw new FormatError(`${file}: Antwort ohne Frage im Quiz.`) - q.options.push({ text: line.slice(5).trim(), correct: line[3].toLowerCase() === 'x' }) - } else if (line.startsWith('= ')) { - const q = questions.at(-1) - const m = /^=\s+(.+?)\s+->\s+(.+)$/.exec(line) - if (!q || !m) throw new FormatError(`${file}: Zuordnung „${line}“ braucht die Form „= links -> rechts“ nach einer Frage.`) - ;(q.pairs ??= []).push({ left: m[1].trim(), right: m[2].trim() }) - } else if (line.startsWith('→')) { - const q = questions.at(-1) - if (!q) throw new FormatError(`${file}: „→ Schritt“ ohne Frage im Quiz.`) - q.ref = line.slice(1).trim() - } else if (line.startsWith('>')) { - const q = questions.at(-1) - if (q) q.explanation = ((q.explanation ? q.explanation + ' ' : '') + line.slice(1).trim()).trim() - } - } - for (const q of questions) { - if (q.pairs) { - const rights = new Set(q.pairs.map((p) => p.right)) - if (q.options.length > 0 || q.pairs.length < 2 || rights.size !== q.pairs.length) { - throw new FormatError(`${file}: Zuordnung „${q.question}“ braucht mindestens zwei Paare mit verschiedenen rechten Seiten und keine Antworten.`) - } - continue - } - if (q.options.length < 2 || !q.options.some((o) => o.correct)) { - throw new FormatError(`${file}: Quizfrage „${q.question}“ braucht mindestens zwei Antworten, davon eine richtige.`) - } - } - return questions -} - -/** Text in Blöcke zerlegen: normales Markdown und `::: art …` bis `:::`. */ -export function parseBlocks(body: string, file: string): Block[] { - const blocks: Block[] = [] - const lines = body.split('\n') - let markdown: string[] = [] - const flush = () => { - const text = markdown.join('\n').trim() - if (text) blocks.push({ kind: 'markdown', text }) - markdown = [] - } - for (let i = 0; i < lines.length; i++) { - const open = /^:::\s*([a-zäöüß][a-zäöüß0-9]*)\s*(.*)$/i.exec(lines[i].trim()) - if (!open) { - markdown.push(lines[i]) - continue - } - flush() - const inner: string[] = [] - let j = i + 1 - while (j < lines.length && lines[j].trim() !== ':::') inner.push(lines[j++]) - if (j >= lines.length) throw new FormatError(`${file}: Block „::: ${open[1]}“ wird nicht mit „:::“ geschlossen.`) - const kind = open[1].toLowerCase() - const arg = open[2].trim() - switch (kind) { - case 'diagramm': - if (!arg) throw new FormatError(`${file}: „::: diagramm“ braucht einen Dateinamen.`) - blocks.push({ kind: 'diagram', file: arg, caption: inner.join(' ').trim() }) - break - case 'übung': - case 'uebung': - if (!arg) throw new FormatError(`${file}: „::: übung“ braucht eine Kennung.`) - blocks.push({ kind: 'exercise', exercise: parseExercise(arg, inner, file) }) - break - case 'checkliste': - blocks.push({ kind: 'checklist', items: inner.map((l) => l.trim()).filter((l) => l.startsWith('- ')).map((l) => checklistItem(l.slice(2).trim())) }) - break - case 'kompetenzen': - blocks.push({ kind: 'competences', items: inner.map((l) => l.trim()).filter((l) => l.startsWith('- ')).map((l) => l.slice(2).trim()) }) - break - case 'quiz': - blocks.push({ kind: 'quiz', questions: parseQuiz(inner, file) }) - break - case 'bild': - if (!arg) throw new FormatError(`${file}: „::: bild“ braucht einen Dateinamen.`) - blocks.push({ kind: 'images', files: arg.split(/\s+/), caption: inner.join(' ').trim() }) - break - case 'grafik': - if (!arg) throw new FormatError(`${file}: „::: grafik“ braucht einen Dateinamen.`) - { - const [graphic, second] = arg.split(/\s+/) - const extra = second === 'animiert' ? { animated: true } : second ? { labeled: second } : {} - blocks.push({ kind: 'graphic', file: graphic, ...extra, caption: inner.join(' ').trim() }) - } - break - case 'angeberwissen': - blocks.push({ kind: 'brag', text: inner.join('\n').trim() }) - break - case 'spiel': - try { - if (isRoundGameKind(arg)) blocks.push({ kind: 'roundGame', game: parseRoundGame(arg, inner, file) }) - else if (arg === 'prinzessin') blocks.push({ kind: 'rescueGame', game: parseRescueGame(inner, file) }) - else if (arg === 'fahrt') blocks.push({ kind: 'driveGame', game: parseDriveGame(inner, file) }) - else if (arg === 'kreuzwort') blocks.push({ kind: 'crosswordGame', game: parseCrosswordGame(inner, file) }) - else if (arg) throw new FormatError(`${file}: Unbekanntes Spiel „${arg}“ (zuhoerer, vertrag, layout, bombe, frage, reihenfolge, prinzessin, fahrt oder kreuzwort).`) - else blocks.push({ kind: 'game', game: parseBoolGame(inner, file) }) - } catch (err) { - throw new FormatError((err as Error).message) - } - break - case 'lückentext': - case 'lueckentext': - try { - blocks.push({ kind: 'cloze', cloze: parseCloze(inner, file) }) - } catch (err) { - throw new FormatError((err as Error).message) - } - break - case 'pap': - try { - blocks.push({ kind: 'pap', pap: parsePap(inner, file) }) - } catch (err) { - throw new FormatError((err as Error).message) - } - break - case 'ludwig': - blocks.push({ kind: 'brag', text: inner.join('\n').trim(), tip: true }) - break - case 'caesar': { - const key = arg ? Number(arg) : 3 - if (!Number.isInteger(key) || key < 0 || key > 25) throw new FormatError(`${file}: „::: caesar“ braucht einen Schlüssel von 0 bis 25.`) - blocks.push({ kind: 'caesar', key, text: inner.join(' ').trim() }) - break - } - case 'mbot2': { - // `fokus: cyberpi, screen`, `ansicht: unten`, `drehen: ja` - const block: Extract = { kind: 'robotModel', focus: [] } - for (const line of inner.filter((l) => l.trim())) { - const m = /^\s*(fokus|focus|ansicht|view|drehen|spin):\s*(.*)$/i.exec(line) - if (!m) throw new FormatError(`${file}: „::: mbot2“ kennt nur Zeilen „fokus:“, „ansicht:“ und „drehen:“.`) - const key = m[1].toLowerCase() - if (key === 'fokus' || key === 'focus') block.focus = robotParts(m[2], file) - else if (key === 'ansicht' || key === 'view') block.view = m[2].trim().toLowerCase() - else block.spin = /^(ja|yes|true|sim)$/i.test(m[2].trim()) - } - blocks.push(block) - break - } - case 'mbot2quiz': { - // `- ultrasonic: Wo ist der **Ultraschallsensor**?` und `fertig: Super!` - const block: Extract = { kind: 'robotQuiz', tasks: [] } - for (const line of inner.filter((l) => l.trim())) { - const task = /^\s*-\s*(\w+)\s*:\s*(.+)$/.exec(line) - const done = /^\s*(fertig|done):\s*(.+)$/i.exec(line) - if (task) block.tasks.push({ part: robotParts(task[1], file)[0], question: task[2].trim() }) - else if (done) block.done = done[2].trim() - else throw new FormatError(`${file}: „::: mbot2quiz“ braucht Zeilen „- teil: Frage“ und optional „fertig: Text“.`) - } - if (!block.tasks.length) throw new FormatError(`${file}: „::: mbot2quiz“ hat keine Fragen.`) - blocks.push(block) - break - } - case 'datei': { - if (!/^[\w.-]+\.(txt|csv|json)$/u.test(arg)) throw new FormatError(`${file}: „::: datei“ braucht einen Dateinamen wie brief.txt, liste.csv oder daten.json.`) - // Der Inhalt bleibt genau so, wie er dasteht, nur ohne Leerzeilen am Anfang und Ende. - while (inner.length && inner[0].trim() === '') inner.shift() - while (inner.length && inner[inner.length - 1].trim() === '') inner.pop() - blocks.push({ kind: 'textFile', name: arg, content: inner.join('\n') }) - break - } - default: - throw new FormatError(`${file}: Unbekannter Block „::: ${open[1]}“.`) - } - i = j - } - flush() - return blocks -} - -function checklistItem(line: string): ChecklistItem { - const m = /^(.*?)\s+→\s+(.+)$/.exec(line) - return m ? { text: m[1], ref: m[2].trim() } : { text: line } -} - -/** Verweise aus Checklisten auf den erklärenden Schritt auflösen. */ -function resolveChecklistRefs(steps: Step[], file: string) { - const index = new Map(steps.map((s, i) => [s.title.toLowerCase(), i])) - for (const block of steps.flatMap((s) => s.blocks)) { - if (block.kind !== 'checklist') continue - for (const item of block.items) { - if (item.ref === undefined) continue - item.step = index.get(item.ref.toLowerCase()) - if (item.step === undefined) throw new FormatError(`${file}: Checkliste verweist auf unbekannten Schritt „${item.ref}“.`) - } - } - const checked = new Set(steps.flatMap((s) => s.blocks).flatMap((b) => (b.kind === 'checklist' ? b.items.map((i) => i.step) : []))) - for (const block of steps.flatMap((s) => s.blocks)) { - if (block.kind !== 'quiz') continue - for (const q of block.questions) { - if (q.ref === undefined) continue - q.step = index.get(q.ref.toLowerCase()) - if (q.step === undefined) throw new FormatError(`${file}: Quizfrage „${q.question}“ verweist auf unbekannten Schritt „${q.ref}“.`) - if (!checked.has(q.step)) throw new FormatError(`${file}: Zu „${q.ref}“ (Quizfrage „${q.question}“) gibt es keinen Checklistenpunkt.`) - } - } -} - -export function parseLesson(source: string, file: string): Lesson { - const { meta, body } = frontMatter(source, file) - const name = file.replace(/^.*\//, '').replace(/\.md$/, '') - const number = Number(/^(\d+)/.exec(name)?.[1]) - if (!Number.isFinite(number)) throw new FormatError(`${file}: Dateiname muss mit der Nummer beginnen (z. B. 01-…).`) - const steps: Step[] = [] - let intro: string[] = [] - let current: { title: string; lines: string[] } | undefined - const close = () => { - if (current) steps.push({ title: current.title, blocks: parseBlocks(current.lines.join('\n'), file) }) - } - for (const line of body.split('\n')) { - const h = /^##\s+(.*)$/.exec(line) - if (h) { - close() - current = { title: h[1].trim(), lines: [] } - } else if (current) { - current.lines.push(line) - } else { - intro.push(line) - } - } - close() - if (steps.length === 0) throw new FormatError(`${file}: Keine Schritte (## Überschrift) gefunden.`) - resolveChecklistRefs(steps, file) - const title = meta.get('titel') - if (!title) throw new FormatError(`${file}: „titel:“ fehlt.`) - return { - id: name, - number, - title, - section: meta.get('bereich') ?? '', - summary: meta.get('kurz') ?? intro.join(' ').trim(), - steps, - } -} - -export function parseTask(source: string, file: string): Task { - const { meta, body } = frontMatter(source, file) - const title = meta.get('titel') - if (!title) throw new FormatError(`${file}: „titel:“ fehlt.`) - const after = meta.get('nach') - return { - id: file.replace(/^.*\//, '').replace(/\.md$/, ''), - title, - after: after ? Number(after) : undefined, - level: Number(meta.get('stufe') ?? '1') || 1, - blocks: parseBlocks(body, file), - } -} - -/** `cyberpi, screen` → geprüfte Teilnamen des mBot2-Modells. */ -function robotParts(list: string, file: string): MBotPart[] { - const names = list.split(/\s*,\s*/).map((x) => x.trim()).filter(Boolean) - for (const n of names) if (!isMBotPart(n)) throw new FormatError(`${file}: Unbekanntes Teil „${n}“. Bekannt: ${MBOT_PARTS.join(', ')}.`) - return names as MBotPart[] -} diff --git a/web/src/learn/markdown.ts b/web/src/learn/markdown.ts deleted file mode 100644 index fd2c068b..00000000 --- a/web/src/learn/markdown.ts +++ /dev/null @@ -1,103 +0,0 @@ -/** Kleines Markdown für Lektionstexte: Absätze, ## bis ####, Listen, Zitat als Hinweis, Code, Tabellen, **fett**, *kursiv*, `code`, [Link](url), - * [Text](?visustruct&lektion=09-…) als Verweis auf eine Lektion der anderen Welt, - * [Wort]{.blau} als farbige Markierung (blau, orange, gruen) passend zu farbigen Kästen, [Wort]{teil=cyberpi} zeigt auf ein Teil des mBot2. */ - -const escape = (s: string) => s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') - -export function inline(text: string): string { - const codes: string[] = [] - let s = escape(text).replace(/`([^`]+)`/g, (_, c: string) => { - codes.push(c) - return `\u0000${codes.length - 1}\u0000` - }) - s = s - .replace(/\*\*(.+?)\*\*/g, '$1') - .replace(/(^|[^*])\*([^*\s][^*]*?)\*/g, '$1$2') - .replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, label: string, url: string) => { - // `?visustruct&lektion=09-…` öffnet eine Lektion der anderen Welt in einem neuen Fenster (VisuBot verweist auf VisuStruct). - const world = /^\?(visustruct|visubot)(&lektion=[\w-]+)?$/.test(url) - const safe = world || /^(https?:|mailto:|#|\.{0,2}\/)/.test(url) ? url : '#' - const external = world || /^https?:/.test(safe) ? ' target="_blank" rel="noopener noreferrer"' : '' - return `${label}` - }) - .replace(/\[([^\]]+)\]\{\.(blau|orange|gruen|gelb|rosa)\}/g, '$1') - // VisuBot: [Ultraschallsensor]{teil=ultrasonic} leuchtet im 3D-Modell auf, wenn man darauf zeigt. - .replace(/\[([^\]]+)\]\{(?:teil|part)=([\w,]+)\}/g, '$1') - return s.replace(/\u0000(\d+)\u0000/g, (_, i: string) => `${codes[Number(i)]}`) -} - -export function markdownToHTML(source: string): string { - const out: string[] = [] - const lines = source.replace(/\r\n/g, '\n').split('\n') - let i = 0 - while (i < lines.length) { - const line = lines[i] - if (line.trim() === '') { - i++ - continue - } - if (line.startsWith('```')) { - // ```java editor: Unter dem Beispiel steht „Im Editor öffnen“, dann kann man es in Programmieren ausprobieren. - const [lang, extra] = line.slice(3).trim().split(/\s+/) - const java = lang === 'java' - const open = java && extra === 'editor' - const code: string[] = [] - i++ - while (i < lines.length && !lines[i].startsWith('```')) code.push(lines[i++]) - i++ - out.push(`
${escape(code.join('\n'))}
`) - continue - } - const heading = /^(#{2,4})\s+(.*)$/.exec(line) - if (heading) { - const level = heading[1].length - // `### Licht {.bot-licht}`: Klasse an der Überschrift (Farbpunkt der VisuBot-Gruppe im Nachschlagen) - const cls = /\s*\{\.([a-z][a-z-]*)\}$/.exec(heading[2]) - const text = cls ? heading[2].slice(0, cls.index) : heading[2] - out.push(`${inline(text)}`) - i++ - continue - } - if (/^\s*([-*]|\d+\.)\s+/.test(line)) { - const ordered = /^\s*\d+\./.test(line) - const items: string[] = [] - while (i < lines.length && /^\s*([-*]|\d+\.)\s+/.test(lines[i])) { - let item = lines[i].replace(/^\s*([-*]|\d+\.)\s+/, '') - i++ - while (i < lines.length && /^\s{2,}\S/.test(lines[i]) && !/^\s*([-*]|\d+\.)\s+/.test(lines[i])) item += ' ' + lines[i++].trim() - items.push(`
  • ${inline(item)}
  • `) - } - out.push(ordered ? `
      ${items.join('')}
    ` : `
      ${items.join('')}
    `) - continue - } - if (line.startsWith('|') && /^\|?[\s:|-]*-{3,}[\s:|-]*$/.test(lines[i + 1] ?? '')) { - const cells = (row: string) => row.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => inline(c.trim())) - const head = cells(line) - i += 2 - const rows: string[][] = [] - while (i < lines.length && lines[i].startsWith('|')) rows.push(cells(lines[i++])) - out.push( - `
    ${head.map((c) => ``).join('')}${rows - .map((r) => `${r.map((c) => ``).join('')}`) - .join('')}
    ${c}
    ${c}
    `, - ) - continue - } - if (line.startsWith('>')) { - const quote: string[] = [] - while (i < lines.length && lines[i].startsWith('>')) quote.push(lines[i++].replace(/^>\s?/, '')) - out.push(``) - continue - } - const para: string[] = [] - while ( - i < lines.length && - lines[i].trim() !== '' && - !/^(```|#{3,4}\s|>|\||\s*([-*]|\d+\.)\s+)/.test(lines[i]) - ) { - para.push(lines[i++].trim()) - } - out.push(`

    ${inline(para.join(' '))}

    `) - } - return out.join('\n') -} diff --git a/web/src/learn/pap.ts b/web/src/learn/pap.ts deleted file mode 100644 index e6560d72..00000000 --- a/web/src/learn/pap.ts +++ /dev/null @@ -1,157 +0,0 @@ -/** - * PAP bauen: Ein Programmablaufplan mit leeren Plätzen, die fertigen Elemente legt man selbst hinein. - * - * ::: pap - * titel: Übung: Kuchen backen als PAP - * auftrag: Lege jedes Element an seinen Platz. - * start: Start - * anweisung: Teig rühren - * frage: Teig glatt? | nein -> Teig rühren - * frage: Stäbchen sauber? | nein: 5 Minuten weiterbacken - * ausgabe: „Fertig!“ - * ende: Ende - * erklärung: Wird gezeigt, wenn alles stimmt. - * legende: ja - * ::: - * - * Die Zeilen stehen von oben nach unten in der Hauptspalte. Eine Frage (Raute) hat zwei Ausgänge: Einer geht nach unten - * weiter, der andere steht hinter `|`. `nein -> Text` führt mit einem Pfeil zurück nach oben zu dem Element mit diesem - * Text. `nein: Text` führt zu einer Anweisung rechts daneben, von dort geht ein Pfeil zurück zur Raute (Schleife wie - * „1 Minute warten“ in Lektion 1). Statt `nein` geht auch `ja`; der andere Ausgang ist dann der nach unten. - */ -export type PapShape = 'oval' | 'rechteck' | 'raute' | 'parallelogramm' - -export interface PapNode { - shape: PapShape - text: string - /** Nur Raute: welcher Ausgang zur Seite geht. */ - sideExit?: 'ja' | 'nein' - /** Rücksprung nach oben: Index des Ziels in `nodes`. */ - backTo?: number - /** Anweisung rechts neben der Raute (Schleife), Index in `nodes`. */ - side?: number - /** Steht rechts neben einer Raute statt in der Hauptspalte. */ - isSide?: boolean -} - -export interface Pap { - title: string - task: string - nodes: PapNode[] - explanation?: string - /** `legende: ja` zeigt unter dem Plan, was die Symbole bedeuten. */ - legend?: boolean -} - -const shapeOf: Record = { - start: 'oval', - ende: 'oval', - anweisung: 'rechteck', - eingabe: 'parallelogramm', - ausgabe: 'parallelogramm', - frage: 'raute', -} - -export function parsePap(inner: string[], file: string): Pap { - const pap: Pap = { title: '', task: '', nodes: [] } - const pending: { node: number; target: string; line: string }[] = [] - for (const raw of inner) { - const line = raw.trim() - if (!line) continue - const m = /^([a-zäöü]+):\s*(.*)$/i.exec(line) - if (!m) throw new Error(`${file}: Unbekannte Zeile im PAP: „${line}“.`) - const key = m[1].toLowerCase() - const value = m[2].trim() - if (key === 'titel') pap.title = value - else if (key === 'auftrag') pap.task = value - else if (key === 'erklärung' || key === 'erklaerung') pap.explanation = value - else if (key === 'legende') pap.legend = /^(ja|yes|true|sim)$/i.test(value) - else if (key in shapeOf) { - const [text, exit] = value.split(/\s+\|\s+/) - if (!text) throw new Error(`${file}: Leeres Element im PAP: „${line}“.`) - const node: PapNode = { shape: shapeOf[key], text: text.trim() } - pap.nodes.push(node) - if (key !== 'frage') { - if (exit) throw new Error(`${file}: Nur eine Frage hat einen zweiten Ausgang: „${line}“.`) - continue - } - const e = /^(ja|nein)\s*(->|:)\s*(.+)$/i.exec(exit ?? '') - if (!e) throw new Error(`${file}: Eine Frage braucht „| nein -> Text“ oder „| nein: Text“: „${line}“.`) - node.sideExit = e[1].toLowerCase() as 'ja' | 'nein' - if (e[2] === ':') { - node.side = pap.nodes.length - pap.nodes.push({ shape: 'rechteck', text: e[3].trim(), isSide: true }) - } else { - pending.push({ node: pap.nodes.length - 1, target: e[3].trim(), line }) - } - } else throw new Error(`${file}: Unbekannter Schlüssel „${m[1]}“ im PAP.`) - } - for (const p of pending) { - const target = pap.nodes.findIndex((n, i) => i < p.node && !n.isSide && n.text === p.target) - if (target < 0) throw new Error(`${file}: Rücksprung zu „${p.target}“: kein Element mit diesem Text weiter oben („${p.line}“).`) - pap.nodes[p.node].backTo = target - } - if (!pap.title) throw new Error(`${file}: Ein PAP braucht einen Titel.`) - if (pap.nodes.length < 3) throw new Error(`${file}: PAP „${pap.title}“ braucht mindestens drei Elemente.`) - return pap -} - -/** Ein Element passt auf einen Platz, wenn Form und Text stimmen (zwei gleiche Elemente sind austauschbar). */ -export function papFits(pap: Pap, slot: number, card: number): boolean { - const a = pap.nodes[slot] - const b = pap.nodes[card] - return a.shape === b.shape && a.text === b.text -} - -/** Lage im Bild (viewBox 400 breit): Mittelpunkt, Breite, Höhe. */ -export interface PapBox { - x: number - y: number - w: number - h: number -} - -export const PAP_WIDTH = 400 -const MAIN_X = 140 -const MAIN_W = 180 -const SIDE_X = 327 -const SIDE_W = 130 -const GAP = 20 - -export function papHeight(shape: PapShape): number { - return shape === 'raute' ? 56 : shape === 'oval' ? 30 : 36 -} - -/** Plätze der Elemente und Gesamthöhe; Seitenkästen stehen auf Höhe ihrer Raute. */ -export function papLayout(pap: Pap): { boxes: PapBox[]; height: number } { - const boxes: PapBox[] = [] - let y = 10 - pap.nodes.forEach((n, i) => { - if (n.isSide) return - const h = papHeight(n.shape) - // Platz für den Pfeil, der von oben in eine Schleife zurückkommt. - if (pap.nodes.some((o) => o.backTo === i) || n.side !== undefined) y += 12 - boxes[i] = { x: MAIN_X, y: y + h / 2, w: n.shape === 'raute' ? 170 : n.shape === 'oval' ? 110 : MAIN_W, h } - if (n.side !== undefined) boxes[n.side] = { x: SIDE_X, y: y + h / 2, w: SIDE_W, h: 40 } - y += h + GAP - }) - return { boxes, height: y - GAP + 10 } -} - -/** Text auf höchstens zwei Zeilen umbrechen, wenn er bei Schriftgröße 13 nicht in `width` passt. */ -export function papLines(text: string, width: number): string[] { - const fits = (s: string) => s.length * 6.6 <= width - if (fits(text)) return [text] - const words = text.split(' ') - let best: string[] = [text] - let bestLong = Infinity - for (let i = 1; i < words.length; i++) { - const lines = [words.slice(0, i).join(' '), words.slice(i).join(' ')] - const long = Math.max(...lines.map((l) => l.length)) - if (long < bestLong) { - best = lines - bestLong = long - } - } - return best -} diff --git a/web/src/learn/progress.ts b/web/src/learn/progress.ts deleted file mode 100644 index b1b154ff..00000000 --- a/web/src/learn/progress.ts +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Fortschritt im Lernbereich. Er steht nur in `learnProgress.json` im Workspace; der Browser merkt sich - * ihn nicht (nur den Ordner). Ohne Workspace gilt er bis zum Neuladen. - */ - -/** Selbsteinschätzung: Sonne = kann ich sicher, Wolke = teilweise, Regenschirm = noch nicht. */ -export type Rating = 'sun' | 'cloud' | 'umbrella' - -export interface LessonProgress { - step: number - done?: boolean - /** Selbsteinschätzung je Checklistenpunkt; `true` stammt aus der Zeit der Häkchen und gilt als Sonne. */ - checks?: Record - /** Gewählte Antwort je Frage („Block-Frage“ → Antwortindex). */ - quiz?: Record -} - -export interface ExerciseProgress { - done?: boolean - /** Letzter Stand des Diagramms als .visustruct-XML. */ - xml?: string -} - -interface Store { - lessons: Record - exercises: Record - /** Zuletzt geöffnete Ansicht. */ - view?: { tab: string; lesson?: string; task?: string } -} - -/** Früher lag der Lernstand zusätzlich im Browser; dieser Rest wird beim Start gelöscht. */ -const legacyKey = 'visustruct.learn.progress' - -/** Umbenannte Lektionen (alte Kennung → neue), damit der Fortschritt erhalten bleibt. */ -const renamedLessons: Record = { - '04-sequenz': '05-sequenz', - '05-variablen-und-datentypen': '04-variablen-und-datentypen', - '07-verzweigung': '08-verzweigung', - '08-bedingungen-verknuepfen': '09-bedingungen-verknuepfen', - '09-fallauswahl': '10-fallauswahl', - '10-while-schleife': '11-while-schleife', - '11-do-while-schleife': '12-do-while-schleife', - '12-for-schleife': '13-for-schleife', - '13-verschachteln': '14-verschachteln', - '14-arrays': '15-arrays', - '15-aufruf-und-aussprung': '18-aufruf-und-aussprung', - '16-aufruf-und-aussprung': '18-aufruf-und-aussprung', - '17-aufruf-und-aussprung': '18-aufruf-und-aussprung', - '17-suchen-und-sortieren': '19-suchen-und-sortieren', - '18-suchen-und-sortieren': '19-suchen-und-sortieren', - '16-simulation-und-schreibtischtest': '07-simulation-und-schreibtischtest', - '17-java': '20-java', - '18-java': '20-java', - '19-java': '20-java', - '18-python-und-javascript': '21-python-und-javascript', - '19-python-und-javascript': '21-python-und-javascript', - '20-python-und-javascript': '21-python-und-javascript', - '22-erstes-fenster': '28-erstes-fenster', - '23-knopf-im-fenster': '29-knopf-im-fenster', - '24-der-knopf-reagiert': '30-der-knopf-reagiert', - '23-erstes-fenster': '28-erstes-fenster', - '24-knopf-im-fenster': '29-knopf-im-fenster', - '25-der-knopf-reagiert': '30-der-knopf-reagiert', - '26-zahl-verdoppeln': '31-zahl-verdoppeln', - '27-klickzaehler': '32-klickzaehler', - '28-ampel': '33-ampel', - '29-notenrechner': '34-notenrechner', - '30-taschenrechner': '35-taschenrechner', - '31-zahlenraten': '36-zahlenraten', - // VisuBot ab 0.4.4 (neue Lektion 13 Einzelne LEDs) und ab 0.5 (Grundlagen-Lektionen 3, 9, 17, 19, 22) - '13-lautsprecher': '16-lautsprecher', - '14-bildschirm': '18-bildschirm', - '15-motoren': '20-motoren', - '16-stoppuhr-akku': '21-stoppuhr-akku', - '03-sensoren': '04-sensoren', - '04-bis-zur-wand': '05-bis-zur-wand', - '05-abstand': '06-abstand', - '06-linie': '07-linie', - '07-farben': '08-farben', - '08-tasten-joystick': '10-tasten-joystick', - '09-licht': '11-licht', - '10-lautstaerke': '12-lautstaerke', - '11-neigung': '13-neigung', - '12-leds': '14-leds', - '13-einzelne-leds': '15-einzelne-leds', - '14-lautsprecher': '16-lautsprecher', - '15-bildschirm': '18-bildschirm', - '16-motoren': '20-motoren', - '17-stoppuhr-akku': '21-stoppuhr-akku', -} - -function migrate(s: Store): Store { - const lessons = { ...s.lessons } - for (const [old, now] of Object.entries(renamedLessons)) { - if (s.lessons[old] && !s.lessons[now]) lessons[now] = s.lessons[old] - if (s.lessons[old]) delete lessons[old] - } - const view = s.view?.lesson && renamedLessons[s.view.lesson] ? { ...s.view, lesson: renamedLessons[s.view.lesson] } : s.view - return { ...s, lessons, view } -} - -function forgetLegacy() { - try { - localStorage.removeItem(legacyKey) - } catch { - /* ohne Speicher: nichts zu löschen */ - } -} - -const empty = (): Store => ({ lessons: {}, exercises: {} }) - -forgetLegacy() -let store = empty() -const listeners = new Set<() => void>() - -function save() { - for (const f of listeners) f() -} - -/** Kennung der Lernstand-Datei, damit eine fremde JSON-Datei nicht eingelesen wird. */ -export const progressFormat = 'visustruct-lernstand' - -function or(a?: boolean, b?: boolean): boolean | undefined { - return a || b || undefined -} - -function mergeLesson(mine: LessonProgress | undefined, theirs: LessonProgress): LessonProgress { - if (!mine) return theirs - const checks = { ...theirs.checks } - for (const [k, v] of Object.entries(mine.checks ?? {})) checks[k] = v || checks[k] || false - return { - step: Math.max(mine.step ?? 0, theirs.step ?? 0), - done: or(mine.done, theirs.done), - checks: Object.keys(checks).length ? checks : undefined, - quiz: mine.quiz || theirs.quiz ? { ...theirs.quiz, ...mine.quiz } : undefined, - } -} - -/** Zwei Stände zusammenführen: erledigt bleibt erledigt, der weiteste Schritt zählt, eigene Diagramme gehen vor. */ -export function mergeProgress(mine: Pick, theirs: Pick) { - const lessons = { ...mine.lessons } - for (const [id, l] of Object.entries(theirs.lessons)) lessons[id] = mergeLesson(mine.lessons[id], l) - const exercises = { ...mine.exercises } - for (const [id, e] of Object.entries(theirs.exercises)) { - const m = mine.exercises[id] - exercises[id] = m ? { done: or(m.done, e.done), xml: m.xml ?? e.xml } : e - } - return { lessons, exercises } -} - -export const progress = { - lesson(id: string): LessonProgress { - return store.lessons[id] ?? { step: 0 } - }, - updateLesson(id: string, change: Partial) { - store.lessons[id] = { ...progress.lesson(id), ...change } - save() - }, - exercise(id: string): ExerciseProgress { - return store.exercises[id] ?? {} - }, - updateExercise(id: string, change: Partial) { - store.exercises[id] = { ...progress.exercise(id), ...change } - save() - }, - resetExercise(id: string) { - delete store.exercises[id] - save() - }, - view() { - return store.view - }, - /** Schon etwas gelernt oder geübt (z. B. für die Warnung beim Schließen ohne Ordner)? */ - hasContent(): boolean { - return Object.values(store.lessons).some((l) => l.step > 0 || l.done || l.checks || l.quiz) || Object.keys(store.exercises).length > 0 - }, - setView(view: Store['view']) { - store.view = view - save() - }, - /** Bei jeder Änderung, z. B. um die Datei im Workspace nachzuziehen. */ - onChange(f: () => void): () => void { - listeners.add(f) - return () => listeners.delete(f) - }, - /** Inhalt von `learnProgress.json`. */ - toFile(): string { - return JSON.stringify({ format: progressFormat, version: 1, saved: new Date().toISOString(), lessons: store.lessons, exercises: store.exercises, view: store.view }, null, 2) - }, - /** - * `learnProgress.json` übernehmen: Die Datei ersetzt den Stand, nur mit `keepSession` kommt hinzu, was in dieser - * Sitzung vor dem Öffnen des Workspace gelernt wurde. Falsche Dateien bleiben unbeachtet. - */ - loadFile(text: string, keepSession = false): boolean { - let data: Partial & { format?: string } - try { - data = JSON.parse(text) as typeof data - } catch { - return false - } - if (data?.format !== progressFormat) return false - const file = migrate({ lessons: data.lessons ?? {}, exercises: data.exercises ?? {}, view: data.view }) - store = keepSession ? { ...mergeProgress(store, file), view: file.view ?? store.view } : file - return true - }, - /** Neuer Workspace ohne Datei: mit leerem Lernstand beginnen (bzw. den dieser Sitzung behalten). */ - clear() { - store = empty() - }, -} diff --git a/web/src/learn/rescueGame.ts b/web/src/learn/rescueGame.ts deleted file mode 100644 index 2ca15992..00000000 --- a/web/src/learn/rescueGame.ts +++ /dev/null @@ -1,273 +0,0 @@ -/** - * Spiel „Prinzessin retten“ (Block `::: spiel prinzessin`): An jeder Station kommt eine Zahl heraus, - * `alphabet.charAt(zahl)` macht daraus einen Buchstaben, der ein Feld im Kreuzworträtsel füllt. Die markierten - * Felder des Rätsels ergeben das geheime Wort, das am Ende das Tor öffnet. - * - * Format (Schlüsselwörter auf Deutsch, auch in den Übersetzungen): - * - * titel: Spiel: Prinzessin retten - * intro: Der Fuchs hat … - * alphabet: ABCDEFGHIJKLMNOPQRSTUVWXYZ (ohne Angabe A bis Z) - * - * station: Am Burggraben (Name der Station) - * String name = " Zora "; (Code, mit vier Leerzeichen eingerückt) - * frage: Welche Zahl steht in `zahl`? - * zahl: 4 - * feld: 5 1 (Zeile und Spalte im Rätsel, ab 1) - * tipp: … (nach einer falschen Antwort) - * > Erklärung - * - * kreuzwort: Löse das Rätsel … (Text über dem Rätsel) - * - → 1 3 BOOLEAN: Datentyp für wahr oder falsch (→ waagrecht, ↓ senkrecht, Zeile, Spalte) - * lösung: 1 3, 4 3 (Felder des geheimen Worts in seiner Reihenfolge) - * - * tor: Vor dir ist das Tor … (Text vor dem Schloss) - * if (eingabe.equals("???")) { (Code, ___ wird zum Eingabefeld) - * ende: Zora ist frei! - */ - -export interface RescueStation { - name: string - code: string - question: string - answer: number - /** Feld im Rätsel (ab 0). */ - row: number - col: number - hint: string - explanation: string -} - -export interface CrosswordWord { - /** Nummer im Rätsel, wie sie im Feld steht. */ - number: number - across: boolean - row: number - col: number - word: string - clue: string -} - -export interface RescueGame { - title: string - intro: string - alphabet: string - stations: RescueStation[] - crosswordText: string - rows: number - cols: number - words: CrosswordWord[] - /** Felder des geheimen Worts (ab 0). */ - solution: { row: number; col: number }[] - keyword: string - gateText: string - gateCode: string - end: string -} - -export class RescueGameError extends Error {} - -export const gatePlaceholder = '___' - -/** Das Rätsel als Gitter: `grid[zeile][spalte]` ist der Buchstabe oder `''`. */ -export function crosswordGrid(game: Pick): string[][] { - const grid = Array.from({ length: game.rows }, () => Array(game.cols).fill('')) - for (const w of game.words) [...w.word].forEach((c, k) => (grid[w.row + (w.across ? 0 : k)][w.col + (w.across ? k : 0)] = c)) - return grid -} - -export function parseRescueGame(lines: string[], file: string): RescueGame { - const fail = (msg: string): never => { - throw new RescueGameError(`${file}: ${msg}`) - } - const cell = (text: string, where: string) => { - const m = /^(\d+)\s+(\d+)$/.exec(text.trim()) - if (!m || Number(m[1]) < 1 || Number(m[2]) < 1) return fail(`${where}: „${text}“ ist kein Feld (Zeile Spalte, ab 1).`) - return { row: Number(m[1]) - 1, col: Number(m[2]) - 1 } - } - let title = '' - let intro = '' - let alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - let crosswordText = '' - let gateText = '' - let gateCode = '' - let end = '' - const stations: (Omit & { answer?: number; row?: number; col?: number })[] = [] - const raw: RawWord[] = [] - let solution: { row: number; col: number }[] = [] - /** Wohin eingerückte Zeilen gehören. */ - let codeTarget: 'station' | 'gate' | undefined - let section: 'start' | 'station' | 'crossword' | 'gate' = 'start' - for (const line of lines) { - const text = line.trim() - if (codeTarget && (/^( {4}|\t)/.test(line) || text === '')) { - const code = line.replace(/^( {4}|\t)/, '') - if (codeTarget === 'station') { - const s = stations[stations.length - 1] - s.code += (s.code ? '\n' : '') + code - } else gateCode += (gateCode ? '\n' : '') + code - continue - } - codeTarget = undefined - if (!text) continue - const key = /^(titel|intro|alphabet|station|frage|zahl|feld|tipp|kreuzwort|lösung|loesung|tor|ende):\s*(.*)$/.exec(text) - if (key) { - const [, name, value] = key - if (name === 'station') { - stations.push({ name: value, code: '', question: '', hint: '', explanation: '' }) - section = 'station' - codeTarget = 'station' - } else if (name === 'kreuzwort') { - crosswordText = value - section = 'crossword' - } else if (name === 'tor') { - gateText = value - section = 'gate' - codeTarget = 'gate' - } else if (name === 'titel') title = value - else if (name === 'intro') intro = value - else if (name === 'alphabet') alphabet = value - else if (name === 'ende') end = value - else if (name === 'lösung' || name === 'loesung') { - if (section !== 'crossword') fail('„lösung:“ gehört zum Kreuzworträtsel.') - solution = value.split(',').map((c) => cell(c, 'lösung')) - } else { - if (section !== 'station') fail(`„${name}:“ gehört in eine Station.`) - const s = stations[stations.length - 1] - if (name === 'frage') s.question = value - else if (name === 'tipp') s.hint = value - else if (name === 'zahl') s.answer = /^-?\d+$/.test(value) ? Number(value) : fail(`Station „${s.name}“: „zahl:“ muss eine ganze Zahl sein.`) - else Object.assign(s, cell(value, `Station „${s.name}“`)) - } - continue - } - if (section === 'crossword') { - raw.push(crosswordWord(text, fail)) - continue - } - if (section === 'station' && text.startsWith('>')) { - const s = stations[stations.length - 1] - s.explanation += (s.explanation ? ' ' : '') + text.slice(1).trim() - continue - } - fail(`Unbekannte Zeile im Spiel: „${text}“`) - } - - if (!title) fail('Das Spiel braucht eine Zeile „titel:“.') - if (!stations.length) fail('Das Spiel braucht mindestens eine „station:“.') - if (!raw.length) fail('Das Spiel braucht ein „kreuzwort:“ mit Wörtern.') - if (!solution.length) fail('Das Kreuzworträtsel braucht eine Zeile „lösung:“.') - if (!gateCode.includes(gatePlaceholder)) fail(`Der Code bei „tor:“ braucht ${gatePlaceholder} für das Eingabefeld.`) - if (new Set(alphabet).size !== alphabet.length) fail('Im Alphabet kommt ein Zeichen doppelt vor.') - - const { rows, cols, words, keyword, grid } = layoutCrossword(raw, solution, fail) - const letterAt = (r: number, c: number) => grid[r]?.[c] ?? '' - - const checked = stations.map((s) => { - if (!s.code) fail(`Station „${s.name}“: Code fehlt.`) - if (!s.question) fail(`Station „${s.name}“: „frage:“ fehlt.`) - if (s.answer === undefined) fail(`Station „${s.name}“: „zahl:“ fehlt.`) - if (s.row === undefined) fail(`Station „${s.name}“: „feld:“ fehlt.`) - const letter = alphabet.charAt(s.answer!) - if (!letter) fail(`Station „${s.name}“: alphabet.charAt(${s.answer}) gibt es nicht.`) - const target = letterAt(s.row!, s.col!) - if (target !== letter) fail(`Station „${s.name}“: alphabet.charAt(${s.answer}) ist ${letter}, im Feld ${s.row! + 1} ${s.col! + 1} steht aber „${target}“.`) - s.code = s.code.replace(/\n+$/, '') - return s as RescueStation - }) - return { title, intro, alphabet, stations: checked, crosswordText, rows, cols, words, solution, keyword, gateText, gateCode: gateCode.replace(/\n+$/, ''), end } -} - -type RawWord = { across: boolean; row: number; col: number; word: string; clue: string } - -/** Zeile eines Rätselworts: `- → 1 3 STRING: Hinweis` (→ waagrecht, ↓ senkrecht, Zeile, Spalte ab 1). */ -const crosswordLine = /^-\s+([→↓])\s+(\d+)\s+(\d+)\s+([A-ZÄÖÜ]+):\s*(.+)$/u - -function crosswordWord(text: string, fail: (msg: string) => never): RawWord { - const m = crosswordLine.exec(text) - if (!m) fail(`Kreuzworträtsel: „${text}“ verstehe ich nicht (z. B. „- → 1 3 STRING: Hinweis“).`) - return { across: m![1] === '→', row: Number(m![2]) - 1, col: Number(m![3]) - 1, word: m![4], clue: m![5] } -} - -/** Wörter ins Gitter legen (Kreuzungen müssen passen), nummerieren und das geheime Wort ablesen. */ -function layoutCrossword(raw: RawWord[], solution: { row: number; col: number }[], fail: (msg: string) => never) { - // Wörter ins Gitter legen: Kreuzungen müssen passen. - const rows = Math.max(...raw.map((w) => w.row + (w.across ? 1 : w.word.length))) - const cols = Math.max(...raw.map((w) => w.col + (w.across ? w.word.length : 1))) - const grid = Array.from({ length: rows }, () => Array(cols).fill('')) - for (const w of raw) { - ;[...w.word].forEach((c, k) => { - const r = w.row + (w.across ? 0 : k) - const col = w.col + (w.across ? k : 0) - if (grid[r][col] && grid[r][col] !== c) fail(`Kreuzworträtsel: ${w.word} passt in Zeile ${r + 1}, Spalte ${col + 1} nicht (dort steht ${grid[r][col]}).`) - grid[r][col] = c - }) - } - // Nummern wie in der Zeitung: von oben nach unten, links nach rechts; zwei Wörter im selben Feld teilen sich eine. - const starts = [...new Set(raw.map((w) => w.row * cols + w.col))].sort((a, b) => a - b) - const words: CrosswordWord[] = raw.map((w) => ({ ...w, number: starts.indexOf(w.row * cols + w.col) + 1 })) - const letterAt = (r: number, c: number) => grid[r]?.[c] ?? '' - - const keyword = solution - .map(({ row, col }) => letterAt(row, col) || fail(`lösung: In Zeile ${row + 1}, Spalte ${col + 1} ist kein Feld.`)) - .join('') - - return { rows, cols, words, keyword, grid } -} - -/** - * Spiel „Begriffe-Rätsel“ (Block `::: spiel kreuzwort`): ein Kreuzworträtsel mit Fachbegriffen, die markierten - * Felder ergeben ein Lösungswort. - * - * titel: Spiel: Begriffe-Rätsel - * intro: Fülle das Rätsel mit Fachbegriffen … - * - → 1 3 SENSOR: Bauteil, das etwas misst (→ waagrecht, ↓ senkrecht, Zeile, Spalte ab 1) - * lösung: 1 3, 4 3 (Felder des Lösungsworts in seiner Reihenfolge) - * ende: Gelöst! … (Text nach dem Lösen) - */ -export interface CrosswordGame { - title: string - intro: string - rows: number - cols: number - words: CrosswordWord[] - solution: { row: number; col: number }[] - keyword: string - end: string -} - -export function parseCrosswordGame(lines: string[], file: string): CrosswordGame { - const fail = (msg: string): never => { - throw new RescueGameError(`${file}: ${msg}`) - } - let title = '' - let intro = '' - let end = '' - let solution: { row: number; col: number }[] = [] - const raw: RawWord[] = [] - for (const line of lines) { - const text = line.trim() - if (!text) continue - const key = /^(titel|intro|lösung|loesung|ende):\s*(.*)$/.exec(text) - if (key) { - const [, name, value] = key - if (name === 'titel') title = value - else if (name === 'intro') intro = value - else if (name === 'ende') end = value - else - solution = value.split(',').map((c) => { - const m = /^(\d+)\s+(\d+)$/.exec(c.trim()) - if (!m || Number(m[1]) < 1 || Number(m[2]) < 1) return fail(`lösung: „${c.trim()}“ ist kein Feld (Zeile Spalte, ab 1).`) - return { row: Number(m[1]) - 1, col: Number(m[2]) - 1 } - }) - continue - } - if (text.startsWith('-')) raw.push(crosswordWord(text, fail)) - else fail(`Unbekannte Zeile im Spiel: „${text}“`) - } - if (!title) fail('Das Spiel braucht eine Zeile „titel:“.') - if (raw.length < 2) fail('Das Rätsel braucht mindestens zwei Wörter.') - if (!solution.length) fail('Das Rätsel braucht eine Zeile „lösung:“.') - const { rows, cols, words, keyword } = layoutCrossword(raw, solution, fail) - return { title, intro, rows, cols, words, solution, keyword, end } -} diff --git a/web/src/learn/roundGame.ts b/web/src/learn/roundGame.ts deleted file mode 100644 index 86079029..00000000 --- a/web/src/learn/roundGame.ts +++ /dev/null @@ -1,581 +0,0 @@ -/** - * Rundenspiele zu Swing (Block `::: spiel zuhoerer|vertrag|layout`): In jeder Runde Code zeigen, eine - * Frage mit Antworten stellen und die richtige Antwort erklären. - * - * - `zuhoerer` („Wer hört zu?“): Ein nachgebautes Fenster mit den Knöpfen aus dem Code. Nach der Antwort - * werden die Klicks abgespielt: Das Ereignis läuft zum Zuhörer, dessen Zeilen leuchten auf. - * - `vertrag` („Vertrag erfüllt?“): Klassen, die ein Interface erfüllen sollen. - * - `layout` („Wo landet der Knopf?“): Die Antworten sind kleine Fenster (`grid: A B / C D`, …). - * - `bombe` („Bombe entschärfen“): Code mit genau einer falschen Zeile und ein Countdown (`zeit:` Sekunden je - * Runde). Man tippt auf die Zeile; `fehler:` ist ihre Nummer (ab 1), `besser:` die richtige Zeile. Mit `daten: csv` - * ist der „Code“ eine CSV-Datei statt Java. Mit `stelle:` (z. B. `stelle: i++`) tippt man in dieser Runde nicht - * ganze Zeilen an, sondern Teile davon (siehe `bombParts`); `stelle:` ist der falsche Teil der Zeile `fehler:`. - * - `frage`: Code (auch leer) und eine Frage mit Antworten. `\n` in einer Antwort beginnt eine neue Zeile; solche - * Antworten erscheinen als kleine Datei. - * - `reihenfolge` („Wer ist dran?“): Die Antworten stehen in der richtigen Reihenfolge da (ohne `[x]`), das Spiel - * mischt sie. Man tippt sie der Reihe nach an; ein falscher Tipp beendet die Runde. - * - * Format (Schlüsselwörter wie in den anderen Blöcken auf Deutsch, auch in den Übersetzungen): - * - * titel: Spiel: Vertrag erfüllt? - * intro: Ludwig zeigt dir … - * antworten: erfüllt | nicht erfüllt (für alle Runden, dann je Runde `richtig: …`) - * frage: … (für alle Runden, je Runde überschreibbar) - * - * runde: - * Code, mit vier Leerzeichen eingerückt - * klicks: A, B (nur zuhoerer: Beschriftungen der Knöpfe) - * fehler: 2 (nur bombe, statt Antworten) - * besser: System.out.println(i); (nur bombe) - * - Antwort - * - [x] richtige Antwort - * > Erklärung - */ - -export type RoundGameKind = 'zuhoerer' | 'vertrag' | 'layout' | 'bombe' | 'frage' | 'reihenfolge' - -export interface RoundOption { - text: string - right: boolean -} - -export interface Round { - code: string - question: string - options: RoundOption[] - explanation: string - /** Beschriftungen der geklickten Knöpfe (nur `zuhoerer`). */ - clicks: string[] - /** Nur `bombe`: Nummer der falschen Zeile (ab 1) und die richtige Zeile. */ - wrongLine?: number - fix?: string - /** Nur `bombe`: Der falsche Teil der Zeile (`stelle:`); dann tippt man Teile statt ganzer Zeilen an. */ - wrongPart?: string -} - -export interface RoundGame { - kind: RoundGameKind - title: string - intro: string - /** Nur `bombe`: Sekunden je Runde. */ - seconds?: number - /** Nur `bombe`: Was in den Runden steht, Java (Standard) oder eine CSV-Datei. */ - data?: 'csv' - rounds: Round[] -} - -export class RoundGameError extends Error {} - -export function isRoundGameKind(arg: string): arg is RoundGameKind { - return arg === 'zuhoerer' || arg === 'vertrag' || arg === 'layout' || arg === 'bombe' || arg === 'frage' || arg === 'reihenfolge' -} - -export function parseRoundGame(kind: RoundGameKind, lines: string[], file: string): RoundGame { - const fail = (msg: string): never => { - throw new RoundGameError(`${file}: ${msg}`) - } - let title = '' - let intro = '' - let question = '' - let answers: string[] = [] - let seconds: number | undefined - let data: 'csv' | undefined - const rounds: (Round & { rightText?: string })[] = [] - let current: (Round & { rightText?: string }) | undefined - let inCode = false - for (const raw of lines) { - const line = raw.trim() - if (current && inCode && (/^ {4}|^\t/.test(raw) || line === '')) { - current.code += (current.code ? '\n' : '') + raw.replace(/^( {4}|\t)/, '') - continue - } - inCode = false - if (!line) continue - const key = /^(titel|intro|antworten|frage|runde|klicks|richtig|zeit|fehler|besser|daten|stelle):\s*(.*)$/.exec(line) - if (key) { - const [, name, value] = key - if (name === 'runde') { - current = { code: '', question, options: [], explanation: '', clicks: [] } - rounds.push(current) - inCode = true - } else if (!current) { - if (name === 'titel') title = value - else if (name === 'intro') intro = value - else if (name === 'antworten') answers = value.split('|').map((a) => a.trim()) - else if (name === 'frage') question = value - else if (name === 'zeit') seconds = Number(value) - else if (name === 'daten') data = value === 'csv' ? value : fail(`„daten:“ kennt nur „csv“.`) - else fail(`„${name}:“ gehört in eine Runde.`) - } else if (name === 'frage') current.question = value - else if (name === 'fehler') current.wrongLine = Number(value) - else if (name === 'besser') current.fix = value - else if (name === 'stelle') current.wrongPart = value.trim() - else if (name === 'klicks') current.clicks = value.split(',').map((c) => c.trim()).filter(Boolean) - else if (name === 'richtig') current.rightText = value - else fail(`„${name}:“ gehört vor die erste Runde.`) - continue - } - if (!current) fail(`Unbekannte Zeile vor der ersten Runde: „${line}“`) - const round = current! - const option = /^-\s+(\[x\]\s+)?(.+)$/.exec(line) - if (option) round.options.push({ text: option[2].trim().replaceAll('\\n', '\n'), right: !!option[1] }) - else if (line.startsWith('>')) round.explanation += (round.explanation ? ' ' : '') + line.slice(1).trim() - else fail(`Unbekannte Zeile im Spiel: „${line}“`) - } - if (!title) fail('Das Spiel braucht eine Zeile „titel:“.') - if (!rounds.length) fail('Das Spiel braucht mindestens eine „runde:“.') - rounds.forEach((round, i) => { - round.code = round.code.replace(/\n+$/, '') - if (kind === 'bombe') { - const lines = round.code.split('\n') - const n = round.wrongLine - if (!n || !Number.isInteger(n) || n < 1 || n > lines.length || !lines[n - 1].trim()) fail(`Runde ${i + 1}: „fehler:“ muss eine Zeile des Codes sein.`) - if (!round.fix) fail(`Runde ${i + 1}: „besser:“ fehlt.`) - if (round.options.length) fail(`Runde ${i + 1}: Bei „bombe“ gibt es keine Antworten, man tippt auf die Zeile.`) - if (!round.question) fail(`Runde ${i + 1}: keine Frage.`) - if (round.wrongPart !== undefined) { - if (data) fail(`Runde ${i + 1}: „stelle:“ geht nur bei Java, nicht bei CSV.`) - const parts = bombParts(lines[n! - 1]).filter((s) => s.part).map((s) => s.text) - if (parts.filter((p) => p === round.wrongPart).length !== 1) - fail(`Runde ${i + 1}: „stelle: ${round.wrongPart}“ ist kein Teil der Zeile ${n}. Teile: ${parts.map((p) => `„${p}“`).join(', ')}`) - } - return - } - if (kind === 'reihenfolge') { - if (round.options.length < 3) fail(`Runde ${i + 1}: „reihenfolge“ braucht mindestens drei Schritte.`) - if (round.options.some((o) => o.right)) fail(`Runde ${i + 1}: Bei „reihenfolge“ gibt es kein [x], die Schritte stehen der Reihe nach da.`) - if (new Set(round.options.map((o) => o.text)).size !== round.options.length) fail(`Runde ${i + 1}: zwei Schritte sind gleich.`) - if (!round.question) fail(`Runde ${i + 1}: keine Frage.`) - return - } - if (!round.options.length) { - if (!answers.length) fail(`Runde ${i + 1}: keine Antworten.`) - if (!round.rightText || !answers.includes(round.rightText)) fail(`Runde ${i + 1}: „richtig:“ muss eine der Antworten sein.`) - round.options = answers.map((text) => ({ text, right: text === round.rightText })) - } - if (round.options.filter((o) => o.right).length !== 1) fail(`Runde ${i + 1}: genau eine Antwort muss richtig sein.`) - if (!round.question) fail(`Runde ${i + 1}: keine Frage.`) - if (kind === 'layout') round.options.forEach((o) => parseLayoutPicture(o.text, `Runde ${i + 1}`, fail)) - if (kind === 'zuhoerer') { - try { - const window = parseSwingWindow(round.code) - for (const c of round.clicks) if (!window.buttons.some((b) => b.caption === c)) fail(`Runde ${i + 1}: Knopf „${c}“ gibt es nicht.`) - simulate(window, round.clicks) - } catch (err) { - if (err instanceof RoundGameError) throw err - fail(`Runde ${i + 1}: ${(err as Error).message}`) - } - } - delete round.rightText - }) - if (kind === 'bombe' && !(seconds && seconds > 0)) fail('„bombe“ braucht „zeit:“ mit den Sekunden je Runde.') - return { kind, title, intro, ...(kind === 'bombe' ? { seconds, ...(data ? { data } : {}) } : {}), rounds } -} - -// MARK: - Bombe: Teile einer Zeile - -/** Ein Stück einer Codezeile; `part` heißt: Man kann es antippen. */ -export interface LineSegment { - text: string - part: boolean -} - -/** Stellen auf oberster Ebene (nicht in Klammern oder Strings), an denen `test` zutrifft. */ -function topLevel(text: string, test: (text: string, i: number) => boolean): number[] { - const hits: number[] = [] - let depth = 0 - let quote = '' - for (let i = 0; i < text.length; i++) { - const c = text[i] - if (quote) { - if (c === '\\') i++ - else if (c === quote) quote = '' - continue - } - if (c === '"' || c === "'") quote = c - else if ('([{'.includes(c)) depth++ - else if (')]}'.includes(c)) depth-- - else if (depth === 0 && test(text, i)) hits.push(i) - } - return hits -} - -/** Die passende schließende Klammer zur öffnenden an `open`, oder -1. */ -function closing(text: string, open: number): number { - let depth = 0 - let quote = '' - for (let i = open; i < text.length; i++) { - const c = text[i] - if (quote) { - if (c === '\\') i++ - else if (c === quote) quote = '' - } else if (c === '"' || c === "'") quote = c - else if ('([{'.includes(c)) depth++ - else if (')]}'.includes(c) && --depth === 0) return i - } - return -1 -} - -/** Bereiche [von, bis) zwischen den Trennstellen, ohne Leerzeichen am Rand. */ -function pieces(text: string, offset: number, cuts: number[], width: number): [number, number][] { - const out: [number, number][] = [] - let start = 0 - for (const end of [...cuts, text.length]) { - let a = start - let b = end - while (a < b && /\s/.test(text[a])) a++ - while (b > a && /\s/.test(text[b - 1])) b-- - if (b > a) out.push([offset + a, offset + b]) - start = end + width - } - return out -} - -/** Ein `+` zwischen zwei Teilen (nicht `++` oder `+=`). */ -const isPlus = (t: string, i: number) => t[i] === '+' && t[i - 1] !== '+' && t[i + 1] !== '+' && t[i + 1] !== '=' -/** Ein einzelnes `=` (keine Vergleiche wie `==`, `<=`, `!=`). */ -const isAssign = (t: string, i: number) => t[i] === '=' && !'=<>!+-*/%'.includes(t[i - 1] ?? '') && t[i + 1] !== '=' - -/** - * Teilt eine Java-Zeile für „Bombe entschärfen“ in antippbare Teile: - * - `for (start; bedingung; schritt) {` → die drei Felder, - * - `if (…)`, `while (…)` → die Bedingung, - * - `ziel = wert;` → Ziel und Wert, - * - `aufruf(a + b, c);` → Aufruf und die Argumente, Argumente zusätzlich an `+` geteilt, - * - sonst die ganze Zeile. - */ -export function bombParts(line: string): LineSegment[] { - const indent = /^\s*/.exec(line)![0] - const body = line.slice(indent.length).replace(/\s+$/, '') - const ranges = ((): [number, number][] => { - const head = /^(?:\}\s*)?(?:else\s+)?(for|if|while)\s*\(/.exec(body) - if (head) { - const open = head[0].length - 1 - const close = closing(body, open) - if (close < 0) return [] - const inner = body.slice(open + 1, close) - if (head[1] !== 'for') return pieces(inner, open + 1, [], 0) - const cuts = topLevel(inner, (t, i) => t[i] === ';') - return cuts.length === 2 ? pieces(inner, open + 1, cuts, 1) : [] - } - if (!body.endsWith(';')) return [] - const stmt = body.slice(0, -1) - const assign = topLevel(stmt, isAssign) - if (assign.length === 1) return pieces(stmt, 0, assign, 1) - if (assign.length) return [] - const open = stmt.indexOf('(') - if (open <= 0 || closing(stmt, open) !== stmt.length - 1 || /^(new|return)\b/.test(stmt)) return [] - const callee = pieces(stmt.slice(0, open), 0, [], 0) - const args = stmt.slice(open + 1, -1) - const argRanges = pieces(args, open + 1, topLevel(args, (t, i) => t[i] === ','), 1).flatMap(([a, b]) => { - const arg = stmt.slice(a, b) - return pieces(arg, a, topLevel(arg, isPlus), 1) - }) - return [...callee, ...argRanges] - })() - const whole = ranges.length ? ranges : body ? [[0, body.length] as [number, number]] : [] - const segments: LineSegment[] = indent ? [{ text: indent, part: false }] : [] - let at = 0 - for (const [a, b] of whole) { - if (a > at) segments.push({ text: body.slice(at, a), part: false }) - segments.push({ text: body.slice(a, b), part: true }) - at = b - } - if (at < body.length) segments.push({ text: body.slice(at), part: false }) - return segments -} - -// MARK: - Layout-Bilder - -export type LayoutPicture = - | { kind: 'grid'; rows: string[][] } - | { kind: 'flow'; rows: string[][] } - | { kind: 'border'; areas: Partial> } - -/** `grid: A B / C D`, `flow: A B C`, `border: N=A C=B S=C`. */ -export function parseLayoutPicture(text: string, where = '', fail: (msg: string) => never = (m) => { throw new RoundGameError(m) }): LayoutPicture { - const m = /^(grid|flow|border):\s*(.+)$/.exec(text) - if (!m) return fail(`${where}: Antwort „${text}“ ist kein Fensterbild (grid:, flow: oder border:).`) - if (m[1] === 'border') { - const areas: Partial> = {} - for (const part of m[2].split(/\s+/)) { - const a = /^([NSWEC])=(.+)$/.exec(part) - if (!a) fail(`${where}: „${part}“ im BorderLayout-Bild verstehe ich nicht (z. B. N=A).`) - areas[a![1] as 'N'] = a![2] - } - return { kind: 'border', areas } - } - const rows = m[2].split('/').map((r) => r.trim().split(/\s+/).filter(Boolean)) - return { kind: m[1] as 'grid' | 'flow', rows } -} - -// MARK: - Nachgebautes Fenster für „Wer hört zu?“ - -export interface SwingButton { - name: string - caption: string -} - -export interface SwingListener { - button: string - /** Zeilen des Zuhörers im Code (ab 0, einschließlich). */ - from: number - to: number - statements: SwingStatement[] -} - -/** Eine Anweisung im Zuhörer: einfach (`text.setText(…)`) oder `if (…) { … } else { … }`. */ -export type SwingStatement = string | { condition: string; then: SwingStatement[]; otherwise: SwingStatement[] } - -export interface SwingWindow { - title: string - buttons: SwingButton[] - /** Beschriftungen (JLabel) mit ihrem Starttext, in der Reihenfolge im Code. */ - labels: { name: string; text: string }[] - ints: Record - listeners: SwingListener[] -} - -const str = '"((?:[^"\\\\]|\\\\.)*)"' - -/** - * Liest Knöpfe, Beschriftungen, int-Variablen und Zuhörer aus einfachem Swing-Code. Ein Zuhörer steht direkt am - * Knopf (`knopf.addActionListener(e -> …)`) oder als Variable (`ActionListener z = e -> …;`), die mit - * `knopf.addActionListener(z);` an mehreren Knöpfen hängen kann. - */ -export function parseSwingWindow(code: string): SwingWindow { - const lines = code.split('\n') - const win: SwingWindow = { title: '', buttons: [], labels: [], ints: {}, listeners: [] } - const named = new Map>() - /** Zuhörer ab Zeile i, `rest` ist der Text hinter dem Pfeil; `end` schließt einen Block ab (`});` oder `};`). */ - const lambda = (i: number, rest: string, end: string): Omit => { - if (rest.startsWith('{')) { - let j = i + 1 - const body: string[] = [] - while (j < lines.length && lines[j].trim() !== end) body.push(lines[j++].trim()) - if (j >= lines.length) throw new Error(`Dem Zuhörer fehlt „${end}“.`) - return { from: i, to: j, statements: parseStatements(body.join(' ')) } - } - const inner = end === '});' ? rest.replace(/\)\s*;\s*$/, '') : rest.replace(/;\s*$/, '') - return { from: i, to: i, statements: parseStatements(inner) } - } - const button = (name: string) => { - if (!win.buttons.some((b) => b.name === name)) throw new Error(`Knopf ${name} ist nicht angelegt.`) - return name - } - for (let i = 0; i < lines.length; i++) { - const line = lines[i].trim() - let m: RegExpExecArray | null - if ((m = new RegExp(`^JFrame\\s+\\w+\\s*=\\s*new JFrame\\(${str}\\)`).exec(line))) win.title = m[1] - else if ((m = new RegExp(`^JButton\\s+(\\w+)\\s*=\\s*new JButton\\(${str}\\)`).exec(line))) win.buttons.push({ name: m[1], caption: m[2] }) - else if ((m = new RegExp(`^JLabel\\s+(\\w+)\\s*=\\s*new JLabel\\(${str}`).exec(line))) win.labels.push({ name: m[1], text: m[2] }) - else if ((m = /^(?:static\s+)?int\s+(\w+)\s*=\s*(-?\d+)\s*;/.exec(line))) win.ints[m[1]] = Number(m[2]) - else if ((m = /^ActionListener\s+(\w+)\s*=\s*\w+\s*->\s*(.*)$/.exec(line))) { - const listener = lambda(i, m[2], '};') - named.set(m[1], listener) - i = listener.to - } else if ((m = /^(\w+)\.addActionListener\(\s*(\w+)\s*\)\s*;$/.exec(line))) { - const listener = named.get(m[2]) - if (!listener) throw new Error(`Zuhörer ${m[2]} ist nicht angelegt.`) - win.listeners.push({ button: button(m[1]), ...listener }) - } else if ((m = /^(\w+)\.addActionListener\(\s*\w+\s*->\s*(.*)$/.exec(line))) { - const listener = lambda(i, m[2], '});') - win.listeners.push({ button: button(m[1]), ...listener }) - i = listener.to - } - } - return win -} - -/** Zerlegt den Rumpf eines Zuhörers in Anweisungen, `if`/`else` mit oder ohne geschweifte Klammern. */ -function parseStatements(text: string): SwingStatement[] { - const out: SwingStatement[] = [] - let pos = 0 - const skip = () => { - while (pos < text.length && /\s/.test(text[pos])) pos++ - } - /** Text bis zur passenden schließenden Klammer; `pos` steht auf der öffnenden. */ - const balanced = (open: string, close: string): string => { - let depth = 0 - let quote = false - const start = pos + 1 - for (; pos < text.length; pos++) { - const c = text[pos] - if (quote) { - if (c === '\\') pos++ - else if (c === '"') quote = false - } else if (c === '"') quote = true - else if (c === open) depth++ - else if (c === close && --depth === 0) return text.slice(start, pos++) - } - throw new Error(`Es fehlt „${close}“.`) - } - /** Eine einfache Anweisung bis zum Semikolon außerhalb von Klammern und Text. */ - const simple = (): string => { - let depth = 0 - let quote = false - const start = pos - for (; pos < text.length; pos++) { - const c = text[pos] - if (quote) { - if (c === '\\') pos++ - else if (c === '"') quote = false - } else if (c === '"') quote = true - else if (c === '(') depth++ - else if (c === ')') depth-- - else if (c === ';' && depth === 0) return text.slice(start, pos++).trim() - } - return text.slice(start).trim() - } - const one = (): SwingStatement[] => { - skip() - if (text[pos] === '{') return parseStatements(balanced('{', '}')) - if (/^if\s*\(/.test(text.slice(pos))) { - pos = text.indexOf('(', pos) - const condition = balanced('(', ')').trim() - const then = one() - skip() - let otherwise: SwingStatement[] = [] - if (/^else\b/.test(text.slice(pos))) { - pos += 4 - otherwise = one() - } - return [{ condition, then, otherwise }] - } - const s = simple() - return s ? [s] : [] - } - for (skip(); pos < text.length; skip()) out.push(...one()) - return out -} - -export interface ClickStep { - button: string - /** Zuhörer, der lief; `undefined`: Niemand hört zu. */ - listener?: SwingListener - /** Texte aller Beschriftungen und Knöpfe nach dem Klick (Name → Text). */ - texts: Record - console: string[] -} - -export interface SwingState { - texts: Record - ints: Record -} - -export function startState(win: SwingWindow): SwingState { - const texts: Record = {} - for (const b of win.buttons) texts[b.name] = b.caption - for (const l of win.labels) texts[l.name] = l.text - return { texts, ints: { ...win.ints } } -} - -/** Ein Klick auf den Knopf mit dieser Beschriftung (verändert `state`). */ -export function click(win: SwingWindow, state: SwingState, caption: string): ClickStep { - const button = win.buttons.find((b) => b.caption === caption) ?? win.buttons.find((b) => state.texts[b.name] === caption) - if (!button) throw new Error(`Knopf „${caption}“ gibt es nicht.`) - return clickButton(win, state, button.name) -} - -/** Klick auf den Knopf mit diesem Variablennamen. */ -export function clickButton(win: SwingWindow, state: SwingState, name: string): ClickStep { - const listener = win.listeners.find((l) => l.button === name) - const output: string[] = [] - // `e.getActionCommand()` ist wie in Java die Beschriftung des Knopfs beim Klick. - const event: SwingEvent = { source: name, command: state.texts[name] } - for (const s of listener?.statements ?? []) run(s, state, output, event) - return { button: name, listener, texts: { ...state.texts }, console: output } -} - -export function simulate(win: SwingWindow, clicks: string[]): ClickStep[] { - const state = startState(win) - return clicks.map((c) => click(win, state, c)) -} - -interface SwingEvent { - source: string - command: string -} - -function run(statement: SwingStatement, state: SwingState, output: string[], event: SwingEvent) { - if (typeof statement !== 'string') { - for (const s of condition(statement.condition, state, event) ? statement.then : statement.otherwise) run(s, state, output, event) - return - } - let m: RegExpExecArray | null - if ((m = /^(\w+)\.setText\((.*)\)$/.exec(statement))) { - if (!(m[1] in state.texts)) throw new Error(`${m[1]} ist keine Beschriftung und kein Knopf.`) - state.texts[m[1]] = String(evaluate(m[2], state, event)) - } else if ((m = /^System\.out\.println\((.*)\)$/.exec(statement))) output.push(String(evaluate(m[1], state, event))) - else if ((m = /^(\w+)\s*(\+\+|--)$/.exec(statement))) state.ints[intName(m[1], state)] += m[2] === '++' ? 1 : -1 - else if ((m = /^(\w+)\s*([+-]?=)\s*(.+)$/.exec(statement))) { - const name = intName(m[1], state) - const value = Number(evaluate(m[3], state, event)) - state.ints[name] = m[2] === '=' ? value : m[2] === '+=' ? state.ints[name] + value : state.ints[name] - value - } else throw new Error(`Diese Anweisung kann das Spiel nicht nachspielen: „${statement}“`) -} - -/** `e.getSource() == knopf`, `text.equals("+")`, `zahl > 3` */ -function condition(text: string, state: SwingState, event: SwingEvent): boolean { - let m: RegExpExecArray | null - if ((m = /^e\.getSource\(\)\s*(==|!=)\s*(\w+)$/.exec(text))) { - if (!(m[2] in state.texts)) throw new Error(`${m[2]} ist kein Knopf.`) - return (event.source === m[2]) === (m[1] === '==') - } - if ((m = /^(!?)(.+)\.equals\((.+)\)$/.exec(text))) return (String(evaluate(m[2], state, event)) === String(evaluate(m[3], state, event))) !== (m[1] === '!') - if ((m = /^(.+?)\s*(==|!=|<=|>=|<|>)\s*(.+)$/.exec(text))) { - const a = Number(evaluate(m[1], state, event)) - const b = Number(evaluate(m[3], state, event)) - return { '==': a === b, '!=': a !== b, '<=': a <= b, '>=': a >= b, '<': a < b, '>': a > b }[m[2]]! - } - throw new Error(`Diese Bedingung kann das Spiel nicht nachspielen: „${text}“`) -} - -function intName(name: string, state: SwingState): string { - if (!(name in state.ints)) throw new Error(`Variable ${name} ist nicht angelegt.`) - return name -} - -/** `"Text" + zahl`, `"" + (a + b)`, `zahl * 2`, `e.getActionCommand()`: von links nach rechts wie in Java. */ -function evaluate(expr: string, state: SwingState, event?: SwingEvent): string | number { - const tokens = expr.match(new RegExp(`${str}|\\d+|e\\.getActionCommand\\(\\)|\\w+(?:\\.getText\\(\\))?|[-+*/()]`, 'g')) ?? [] - let pos = 0 - const atom = (): string | number => { - const tok = tokens[pos++] - if (tok === undefined) throw new Error(`Unvollständiger Ausdruck „${expr}“`) - if (tok === '(') { - const v = sum() - pos++ - return v - } - if (tok.startsWith('"')) return JSON.parse(tok) - if (/^\d+$/.test(tok)) return Number(tok) - if (tok === 'e.getActionCommand()') { - if (!event) throw new Error('e.getActionCommand() gibt es nur im Zuhörer.') - return event.command - } - if (tok.endsWith('.getText()')) return state.texts[tok.slice(0, -10)] ?? '' - if (tok in state.ints) return state.ints[tok] - throw new Error(`Unbekannt: „${tok}“`) - } - const product = (): string | number => { - let v = atom() - while (tokens[pos] === '*' || tokens[pos] === '/') { - const op = tokens[pos++] - const r = Number(atom()) - v = op === '*' ? Number(v) * r : Math.trunc(Number(v) / r) - } - return v - } - const sum = (): string | number => { - let v = product() - while (tokens[pos] === '+' || tokens[pos] === '-') { - const op = tokens[pos++] - const r = product() - if (op === '+') v = typeof v === 'string' || typeof r === 'string' ? `${v}${r}` : v + r - else v = Number(v) - Number(r) - } - return v - } - return sum() -} diff --git a/web/src/learn/teacher.ts b/web/src/learn/teacher.ts deleted file mode 100644 index d68550fe..00000000 --- a/web/src/learn/teacher.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Lehrermodus: Mit dem Lehrercode werden die Musterlösungen sichtbar. - * Im Code steht nur der SHA-256-Hash des Lehrercodes. Das hält Schüler ab, ist aber kein echter Schutz, - * denn alles läuft im Browser. - */ - -const codeHash = 'f4ecc8e489ae7865308288c7d0fa313333cf2be3ff529b23bc214bee010310e0' -const storageKey = 'visustruct.learn.teacher' -/** Gilt auch, wenn der Browser nichts speichern darf, dann bis zum Neuladen. */ -let unlocked = false - -export function isTeacher(): boolean { - if (unlocked) return true - try { - return localStorage.getItem(storageKey) === codeHash - } catch { - return false - } -} - -async function sha256(text: string): Promise { - const bytes = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(text)) - return [...new Uint8Array(bytes)].map((b) => b.toString(16).padStart(2, '0')).join('') -} - -/** Prüft den Code und merkt sich den Lehrermodus im Browser. */ -export async function unlockTeacher(code: string): Promise { - let ok = false - try { - ok = (await sha256(code.trim())) === codeHash - } catch { - return false - } - if (ok) { - unlocked = true - try { - localStorage.setItem(storageKey, codeHash) - } catch { - /* ohne Speicher gilt der Lehrermodus bis zum Neuladen */ - } - } - return ok -} - -export function lockTeacher(): void { - unlocked = false - try { - localStorage.removeItem(storageKey) - } catch { - /* nichts zu tun */ - } -} diff --git a/web/src/main.ts b/web/src/main.ts deleted file mode 100644 index 9baf5b86..00000000 --- a/web/src/main.ts +++ /dev/null @@ -1,85 +0,0 @@ -import './ui/styles.css' -import { App, applyCanvasStubTexts } from './ui/app' -import { installTooltips } from './ui/tooltip' -import { preferredLanguage, setCodeKeywords, setLanguage, storedCodeKeywords } from './ui/i18n' -import { setVisuBotMode, setVisuFlowWorld, setVisuObjectWorld, visuFlowWorld, visuObjectWorld } from './ui/visubotView' -import { hostWorld } from './ui/site' - -// Nach einem Update kann eine noch offene oder zwischengespeicherte Seite Dateien der alten Version -// nachladen wollen, die es nicht mehr gibt. Dann einmal neu laden statt leer zu bleiben. -const reloadKey = 'visustruct.reloadedAfterError' - -function reloadOnce(): boolean { - try { - const last = Number(sessionStorage.getItem(reloadKey)) - if (last && Date.now() - last < 30_000) return false - sessionStorage.setItem(reloadKey, String(Date.now())) - } catch { - return false - } - location.reload() - return true -} - -/** Statt einer leeren Seite: kurzer Hinweis mit Knopf zum Neuladen (Texte fest, die Übersetzungen fehlen ja evtl.). */ -function showStartupError(error: unknown) { - console.error(error) - const de = (navigator.language || '').toLowerCase().startsWith('de') - const box = document.createElement('div') - box.className = 'startup-error' - const title = document.createElement('h1') - title.textContent = de ? 'VisuStruct konnte nicht starten' : 'VisuStruct could not start' - const text = document.createElement('p') - text.textContent = de - ? 'Wahrscheinlich gibt es gerade eine neue Version. Lade die Seite neu. Hilft das nicht, drücke Strg+Umschalt+R (Mac: ⌘⇧R).' - : 'There is probably a new version. Reload the page. If that does not help, press Ctrl+Shift+R (Mac: ⌘⇧R).' - const button = document.createElement('button') - button.textContent = de ? 'Neu laden' : 'Reload' - button.addEventListener('click', () => location.reload()) - const detail = document.createElement('pre') - detail.textContent = String(error instanceof Error ? error.message : error) - box.append(title, text, button, detail) - ;(document.getElementById('app') ?? document.body).replaceWith(box) -} - -window.addEventListener('vite:preloadError', (event) => { - if (reloadOnce()) event.preventDefault() -}) - -// Erst die Sprache (sie übernimmt ein altes „Java (Keywords)“), dann den Schalter Code-Keywords. -const language = preferredLanguage() -Promise.all([setLanguage(language, false), setCodeKeywords(storedCodeKeywords(), false)]) - .then(() => { - applyCanvasStubTexts() - installTooltips() - // Welten VisuObject (…/app/?visuobject) und VisuFlow (?visuflow) haben eine eigene Oberfläche; ?visustruct und ?visubot wählt die App selbst. - const query = new URLSearchParams(location.search) - // Auf einer Welt-Subdomain (visubot.visucode.de …) ohne Welt in der Adresse gilt immer die Welt der Seite; gefragt wird nicht. - // VisuFlow gibt es dort nur mit ?visuflow in der Adresse. Ohne Subdomain fragt der Start-Dialog. - const anyWorld = ['visustruct', 'visubot', 'visuobject', 'visuflow'].some((k) => query.has(k)) - const site = hostWorld() - if (!anyWorld && site) { - if (site === 'visubot') setVisuBotMode('on') - else if (site === 'visuobject') setVisuObjectWorld() - else setVisuBotMode('off') - } - if (query.has('visuobject')) setVisuObjectWorld() - if (query.has('visuflow')) setVisuFlowWorld() - const root = document.getElementById('app')! - const own = !query.has('visustruct') && !query.has('visubot') - if (visuObjectWorld() && own) return import('./ui/objectApp').then(({ ObjectApp }) => void new ObjectApp(root)) - if (visuFlowWorld() && own) return import('./ui/flowApp').then(({ FlowApp }) => void new FlowApp(root)) - new App(root) - }) - .then(() => { - try { - sessionStorage.removeItem(reloadKey) - } catch { - /* egal */ - } - }) - .catch((error: unknown) => { - // Fehlende Datei nach einem Update: einmal neu laden, sonst Hinweis zeigen. - if (/dynamically imported module|Importing a module script failed|error loading dynamically/i.test(String(error)) && reloadOnce()) return - showStartupError(error) - }) diff --git a/web/src/ui/app.ts b/web/src/ui/app.ts deleted file mode 100644 index b6954198..00000000 --- a/web/src/ui/app.ts +++ /dev/null @@ -1,3130 +0,0 @@ -import { canvasTextLines, setCanvasStubTexts } from '../core/diagramText' -import { worldHomeURL } from './site' -import { - appendCaseToFallauswahl, - canDeleteElement, - deleteAtPath, - elementAt, - insertAfterSibling, - insertAt, - insertBeforeSibling, - isPlaceholderSlot, - moveAfterSibling, - moveBeforeSibling, - moveReplacingPlaceholder, - newEmptyDocument, - multiwayAncestorPath, - caseIndexInMultiway, - removeCaseFromFallauswahl, - replaceAtPath, - sanitizedSelection, - setFallauswahlCaseName, - setTextLines, - tryElementAt, -} from '../core/editing' -import * as LineEditor from '../core/lineEditorText' -import { diagramDisplayMetrics, LayoutEngine, type Layout, type LayoutNode, type Rect } from '../core/layout' -import { ElementType, makeElement, type VSDocument, type VSElement } from '../core/model' -import { paletteElement, paletteItems, type PaletteItem } from '../core/palette' -import { createCanvasMeasure } from '../core/textMeasurement' -import { decodeDocument, encodeDocument } from '../core/xmlCodec' -import { DiagramRenderer, displayElement, el, flattenNodes, samePath, scopeKind } from './renderer' -import { caseLabelAt, caseLabelRect, openInlineEditor, textRect, type InlineEditor, type InlineField } from './inlineEdit' -import { CodePanel } from './codePanel' -import { BlockView, programVariables, replaceBlockSensor, statementVariables, replaceBlockValue, withCountLoopDefaults, withoutBlockIcons, type BlockDrop } from './blockView' -import { blockViewLabels } from './blockPicture' -import { freshVariable, isBoolExpression, operatorBlocks, operatorName, repeatLoopLines, variableBlocks, variableName } from 'visublocks' -import { icons } from './icons' -import { FilesPanel, type WorkspaceFile } from './filesPanel' -import { LearnPanel, sidebarIcon, type LearnHost } from './learnPanel' -import type { UiArea } from './termTips' -import { LiveCodePanel } from './liveCodePanel' -import { codeWords, countInDocument, documentVariableNames, documentWords, elementVariableNames, nameRanges, renameInDocument, variableNames } from '../core/rename' -import { askRename, showRenameMenu } from './renameDialog' -import { RobotPreviewPanel } from './robotPreview' -import { codeScopes, setCodeScopes } from './javaEditor' -import type { LiveTarget } from '../core/codeGenerator' -import { exerciseArea, type Exercise } from '../learn/lessonFormat' -import { progress } from '../learn/progress' -import { codeKeywords, currentLanguage, offeredLanguages, setCodeKeywords, setLanguage, t, type LanguageCode } from './i18n' -import * as Export from './exporter' -import { SimulationPanel } from './simulationPanel' -import { mbotLinkText, setVisuBotMode, setVisuFlowWorld, setVisuObjectWorld, visuBotBrand, toggleMBotLink, visuBotEnabled, visuBotGroupColor, visuBotGroupIcon, visuBotMode, visuBotSensors, visuBotTemplates, VisuBotView, worldChooserHTML, worldTitleHTML, worldChosen, type WorldChoice } from './visubotView' -import { canConnectMBot, mbotLink } from '../core/mbotLink' -import { uploadToMBot } from './mbotUpload' -import { lessons, tasks, useBlockLessons, useVisuBotContent, world } from '../learn/content' -import type { RobotWorldSetup } from '../core/robotWorld' -import { parseSubprogramHeader, subprogramName } from '../core/simulation' - -type DropTarget = - | { kind: 'before' | 'after' | 'replace'; path: number[]; frame: Rect } - | { kind: 'rootAppend'; frame: Rect } - /** VisuBot: Sensorwert in die Bedingung (bzw. den Text) eines Elements schreiben */ - | { kind: 'condition'; path: number[]; frame: Rect } - /** Blöcke: verschobenes Element in den Papierkorb */ - | { kind: 'trash'; frame: Rect } - /** Blöcke: Sensor genau auf ein Wertfeld bzw. einen anderen Sensor */ - | { kind: 'slot'; path: number[]; slot: 'value' | 'sensor'; index: number; frame: Rect } - -/** - * Aus der Palette: neues Element (auch die VisuBot-Befehle) samt Name für das Ziehbild, - * oder ein VisuBot-Sensor wie `mbot.getDistance()`, der in eine Bedingung wandert. - */ -type DragSource = - | { kind: 'palette'; make: () => VSElement; label: string } - | { kind: 'sensor'; text: string; label: string } - | { kind: 'move'; path: number[] } - -/** Elemente, in deren Text ein Sensorwert passt: Bedingungen und Anweisungen (`int abstand = mbot.getDistance()`). */ -const takesSensor = [ElementType.verzweigung, ElementType.whileSchleife, ElementType.doUntilSchleife, ElementType.anweisung] - -interface DragState { - source: DragSource - pointerId: number - startX: number - startY: number - active: boolean - ghost?: HTMLElement - target?: DropTarget -} - -// Minimal-Typen der File System Access API (Chrome/Edge); Safari/Firefox fallen auf Download zurück. -interface FileHandle { - name: string - getFile(): Promise - createWritable(): Promise<{ write(data: Blob): Promise; close(): Promise }> -} -type PickerWindow = Window & { - showOpenFilePicker?: (o: object) => Promise - showSaveFilePicker?: (o: object) => Promise -} -const pickerTypes = () => [{ description: t('web_file_type'), accept: { 'application/xml': ['.visustruct', '.strk'] } }] - -/** `element_*` aus Localizable.strings. */ -const typeName = (type: ElementType) => t(`element_${ElementType[type]}`) - -/** `InspectorElementHints.conditionHintLine`: Beispiel über dem Editor. */ -const conditionHints: Partial> = { - [ElementType.verzweigung]: 'age >= 18', - [ElementType.whileSchleife]: 'i < 10', - [ElementType.doUntilSchleife]: 'again == 1', - [ElementType.fallauswahl]: 'day', -} - -/** Hinweis unter der Art (wie im Swift-Inspector, Englisch und code-nah). */ -const ioModeHints: Record = { - statement: 'int number = 5 | double x = 1.5 | String text = "Your text" | boolean ok = true | const double PI = 3.14', - input: 'input: int age "Enter age" | input: double price "Enter price" | input: String name "Enter name"', - output: 'output: number | output: "Your text" | output: "Your age: " + age', -} - -/** `lineEditorFixedPrefixLabel`: Schlüsselwort vor dem Editor, das nicht mitgetippt wird. */ -function fixedPrefix(el: VSElement): string | undefined { - switch (el.type) { - case ElementType.anweisung: { - const mode = LineEditor.ioMode(el.textLines.join('\n')) - return mode === 'input' ? 'input:' : mode === 'output' ? 'output:' : undefined - } - case ElementType.verzweigung: return 'if' - case ElementType.whileSchleife: - case ElementType.doUntilSchleife: - return 'while' - case ElementType.fallauswahl: return 'switch' - default: return undefined - } -} - -/** Text zum Bearbeiten (Inspektor und direkt im Struktogramm): ohne festes Schlüsselwort, leere Anweisung leer statt „ø“. */ -function editableText(el: VSElement): string { - const prefix = fixedPrefix(el) - const text = el.textLines.join('\n') - const shown = prefix ? LineEditor.strippingPrefixForEditing(prefix, text) : text - return el.type === ElementType.anweisung && shown.trim() === 'ø' ? '' : shown -} - -/** Bearbeiteten Text zurückschreiben (Gegenstück zu `editableText`). */ -function storingText(doc: VSDocument, path: number[], value: string): VSDocument | undefined { - const el = tryElementAt(doc, path) - if (!el) return undefined - const p = fixedPrefix(el) - if (el.type === ElementType.anweisung && !p && value.trim() === '') return setTextLines(doc, path, ['ø']) - const stored = p ? LineEditor.prefixingForEditing(p, LineEditor.strippingPrefixForEditing(p, value)) : value - return setTextLines(doc, path, stored.split('\n')) -} - -/** `InspectorElementHints.lineEditorPlaceholder`. */ -function placeholderFor(el: VSElement): string { - switch (el.type) { - case ElementType.anweisung: { - const mode = LineEditor.ioMode(el.textLines.join('\n')) - return mode === 'input' ? 'int age "Enter age"' : mode === 'output' ? '"Your age: " + age' : t('inspector_placeholder_statement') - } - case ElementType.verzweigung: - case ElementType.whileSchleife: - case ElementType.doUntilSchleife: - case ElementType.endlosschleife: - return t('inspector_placeholder_condition') - case ElementType.fallauswahl: return t('inspector_placeholder_selector') - case ElementType.aufruf: return t('inspector_placeholder_call') - case ElementType.aussprung: return t('inspector_placeholder_break') - default: return '' - } -} - -/** Kleines DOM-Hilfsmittel für den Inspector. */ -/** Aus `package.json`, siehe `vite.config.ts`. */ -declare const __APP_VERSION__: string - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -/** Ein Reiter wie `DiagramSheetTab.swift`: Dokument, Verlauf, Auswahl, verknüpfte Datei. */ -interface Tab { - doc: VSDocument - serial: number - /** Dateiname mit Endung; `undefined` = unbenannt. */ - fileName?: string - fileHandle?: FileHandle - /** Programm im Workspace (Spalte „Dateien“): Speichern schreibt dorthin, mit .java daneben. */ - workspace?: WorkspaceFile - dirty: boolean - undoStack: VSDocument[] - redoStack: VSDocument[] - selected?: number[] - /** Reiter gehört zu einer Übung im Lernbereich; ihr Stand wird im Browser gemerkt. */ - exerciseId?: string -} - -/** Unter dieser Breite der Struktogramm-Spalte stehen die Elemente kompakt. */ -const compactPaletteBelow = 760 - -/** `VisuStructUIScaleStep`: Textgröße des Struktogramms. */ -const zoomSteps = [ - { value: 1, label: 'settings_ui_scale_small' }, - { value: 1.22, label: 'settings_ui_scale_medium' }, - { value: 1.5, label: 'settings_ui_scale_large' }, - { value: 1.82, label: 'settings_ui_scale_extra_large' }, -] - -/** Schriftgröße in Code live (Code, Konsole), unabhängig vom Struktogramm. */ -const codeSizes = [ - { value: 14, label: 'settings_ui_scale_small' }, - { value: 17, label: 'settings_ui_scale_medium' }, - { value: 20, label: 'settings_ui_scale_large' }, - { value: 24, label: 'settings_ui_scale_extra_large' }, -] - -function applyCodeSize(px: number) { - document.documentElement.style.setProperty('--code-size', `${px}px`) -} - -const isMac = /Mac|iPhone|iPad/.test(navigator.platform) -/** Tastenkürzel im Tooltip, z. B. „Strg+S“ bzw. „⌘S“. */ -/** Startseite (oder ein Abschnitt ihrer Fußzeile) in der passenden Sprache der Webseite; im VisuBot-Modus die VisuBot-Startseite. */ -function siteURL(section?: 'contact' | 'privacy'): string { - return worldHomeURL(visuBotEnabled() ? 'visubot' : 'visustruct', currentLanguage(), section) -} - -/** Symbolknopf; der Name (mit Tastenkürzel) erscheint als Tooltip. */ -function iconButton(action: string, label: string, icon: string, keys?: string): string { - const tip = keys ? `${label} (${keys})` : label - return `` -} - -function shortcut(key: string, shift = false): string { - if (isMac) return `${shift ? '⇧' : ''}⌘${key}` - return [t('web_key_ctrl'), ...(shift ? [t('web_key_shift')] : []), key].join('+') -} -const e = escapeHTML -const zoomKey = 'visustruct.ui.scale.canvas' -const codeSizeKey = 'visustruct.ui.scale.code' -const learnKey = 'visustruct.learn.open' -const filesKey = 'visustruct.files.open' -const filesWidthKey = 'visustruct.files.width' -const learnWidthKey = 'visustruct.learn.width' -const liveKey = 'visustruct.live.open' -const liveWidthKey = 'visustruct.live.width' -/** VisuBot: Fenster „mBot2 live“ offen, unter dem Code statt als eigene Spalte, Breite bzw. Höhe. */ -const previewKey = 'visustruct.robotPreview.open' -/** „Programmieren groß“: Code ist die Hauptfläche, das Struktogramm schmal (Breite merken). */ -const codeMainKey = 'visustruct.codeMain' -/** VisuBot: Struktogramm als Blöcke zeigen (`1`). */ -const blocksKey = 'visustruct.visubot.blocks' -/** Selbst gezogene Breite der Elemente (leer = automatisch). */ -const paletteWidthKey = 'visustruct.palette.width' -const editorWidthKey = 'visustruct.editor.width' -/** Zugeklappte Gruppen der mBot2-Elemente. */ -const collapsedGroupsKey = 'visustruct.visubot.collapsedGroups' -const previewStackedKey = 'visustruct.robotPreview.stacked' -const previewWidthKey = 'visustruct.robotPreview.width' -const simWorkKey = 'visustruct.simulation.diagram' -/** Selbst gezogene Höhe des Objektinspektors unter dem Diagramm in px (leer = Standard). */ -const inspectorHeightKey = 'visustruct.inspector.height' -const previewHeightKey = 'visustruct.robotPreview.height' -const editorHiddenKey = 'visustruct.editor.hidden' -const compactPaletteKey = 'visustruct.palette.compact' -/** Elemente eingeklappt (schmale Leiste), z. B. wenn Code und Struktogramm nebeneinander Platz brauchen. */ -const paletteHiddenKey = 'visustruct.palette.hidden' -export const gearIcon = - '' - -/** Wie `VisuStructEditHistory.defaultMaxSteps`. */ -const maxUndoSteps = 80 - -export class App { - private tabs: Tab[] = [] - private tabIndex = 0 - private nextTabSerial = 1 - private clipboard?: VSElement - private zoom = 1 - private get tab(): Tab { - return this.tabs[this.tabIndex] - } - private get doc() { - return this.tab.doc - } - private set doc(v: VSDocument) { - this.tab.doc = v - } - private get fileName(): string { - return this.tab.fileName ?? `${tabTitle(this.tab)}.visustruct` - } - private get fileHandle() { - return this.tab.fileHandle - } - private set fileHandle(v: FileHandle | undefined) { - this.tab.fileHandle = v - } - private get dirty() { - return this.tab.dirty - } - private set dirty(v: boolean) { - this.tab.dirty = v - } - private get undoStack() { - return this.tab.undoStack - } - private get redoStack() { - return this.tab.redoStack - } - private get selected() { - return this.tab.selected - } - private set selected(v: number[] | undefined) { - this.tab.selected = v - } - private simulating = false - private exportButton!: HTMLButtonElement - private closeExport?: (ev: PointerEvent) => void - private learn: LearnPanel - private shell: HTMLElement - private railButton?: HTMLButtonElement - private liveRailButton?: HTMLButtonElement - private editorRailButton?: HTMLButtonElement - /** Werkzeugleiste über allen Spalten. */ - private topbar = h('div', { class: 'topbar' }) - /** Fußzeile unter allen Spalten. */ - private bottombar = h('div', { class: 'bottombar' }) - private editorHidden = false - /** Elemente ein- oder aufklappen (gesetzt, sobald die Elemente gebaut sind). */ - private setPaletteHidden?: (hidden: boolean) => void - private files!: FilesPanel - private filesRailButton?: HTMLButtonElement - private closeSettings?: (ev: PointerEvent) => void - private escapeSettings?: (ev: KeyboardEvent) => void - private exerciseSaveTimer?: number - private paletteObserver?: ResizeObserver - private resizeObserver?: ResizeObserver - private renderedHostSize = { width: -1, height: -1 } - /** Im Simulationsmodus markierter Schritt (ersetzt die Auswahl). */ - private simPath?: number[] - private sim!: SimulationPanel - private simButton!: HTMLButtonElement - private pseudocodeBox!: HTMLInputElement - /** VisuBot: dasselbe Struktogramm als Scratch-artige Blöcke (statt der Kästen). */ - private blockMode = false - /** VisuBot: Liste „Variablen“ in den Elementen und die zuletzt gezeigten Variablen-Bausteine */ - private varList?: HTMLUListElement - private shownVars = '' - private blocks?: BlockView - private blocksButton!: HTMLButtonElement - private coding = false - private code!: CodePanel - private codeButton!: HTMLButtonElement - /** Ansicht „Code live“ rechts neben dem Editor. */ - private liveCoding = false - /** Laufende Eingabe in „Code live“: fasst Tastendrücke zu Rückgängig-Schritten zusammen. */ - private codeTyping?: { tab: Tab; start: number; last: number } - private live: LiveCodePanel - private preview = new RobotPreviewPanel() - private previewOpen = false - /** „mBot2 live“ war zu und wurde nur für die Simulation geöffnet: danach wieder schließen. */ - private previewOpenedForSim = false - private codeMain = false - private previewStacked = false - private previewResizer!: HTMLElement - - private measure = createCanvasMeasure() - private metrics = diagramDisplayMetrics(true) - private engine = new LayoutEngine(this.metrics, this.measure) - private renderer = new DiagramRenderer(this.metrics, this.measure) - private layout?: Layout - private flat: LayoutNode[] = [] - - private canvasHost!: HTMLElement - private svg?: SVGSVGElement - private overlay?: SVGGElement - private inspector!: HTMLElement - private captionInput!: HTMLInputElement - private undoButton!: HTMLButtonElement - private redoButton!: HTMLButtonElement - private deleteButton!: HTMLButtonElement - private statusLine!: HTMLElement - private drag?: DragState - /** Offene Bearbeitung direkt im Struktogramm. */ - private inline?: InlineEditor - /** Letzter Klick/Tipp ins Struktogramm (Doppelklick selbst erkennen: der erste Klick zeichnet neu). */ - private lastCanvasTap = { spot: '', time: 0, x: 0, y: 0 } - /** Zweiter Tipp erkannt: beim Loslassen das Eingabefeld öffnen (dann behält es den Fokus). */ - private pendingInline?: { pointerId: number; path: number[]; point: { x: number; y: number } } - /** Nur in der claude.ai-Vorschau: Dateien über die `downloads`-Fähigkeit anbieten. */ - private downloads: { save(o: { filename: string; data: Blob }): Promise } | null = null - private readonly inViewer = 'claude' in window - /** VisuBot-Modus: Welt mit dem mBot2 im Simulationspanel (bleibt beim Neuaufbau erhalten). */ - private visuBot?: VisuBotView - private stopLinkListener?: () => void - - constructor(private root: HTMLElement) { - this.tabs.push(this.makeTab()) - // Link „VisuBot ausprobieren“ der Webseite: …/app/?visubot schaltet den Kurs mit mBot2-Schritten ein. - // Links der Webseite: …/app/?visubot öffnet die Welt VisuBot, …/app/?visustruct die Welt VisuStruct. - const query = new URLSearchParams(location.search) - if (query.has('visubot')) setVisuBotMode('on') - else if (query.has('visustruct')) setVisuBotMode('off') - useVisuBotContent(visuBotMode()) - // Block-Ansicht an: kurze Block-Lektionen für Jüngere - useBlockLessons(storedValue(blocksKey) === '1') - // Lernbereich links neben dem Editor; bleibt beim Neuaufbau der Oberfläche (Sprachwechsel) erhalten. - this.learn = new LearnPanel(this.learnHost()) - // Verweis aus einer VisuBot-Lektion: …/app/?visustruct&lektion=09-bedingungen-verknuepfen öffnet diese Lektion. - const wantedLesson = query.get('lektion') ?? undefined - if (wantedLesson) this.learn.showLesson(wantedLesson) - this.shell = document.createElement('div') - this.shell.className = 'shell' - root.replaceWith(this.shell) - this.live = new LiveCodePanel( - () => this.setLiveCoding(false), - (path) => { - this.selected = path - this.render() - }, - (doc, path) => this.commitFromCode(doc, path), - (redo) => (redo ? this.redo() : this.undo()), - ) - this.live.onRobotRun = (stepping) => this.runRobot(stepping) - this.live.onSwap = () => this.setCodeMain(!this.codeMain) - this.live.onRename = () => (this.codeTyping = undefined) - this.live.onReference = () => { - this.learn.showReference() - this.setLearning(true) - } - this.files = new FilesPanel({ - open: (xml, file, java) => this.openFromWorkspace(xml, file, java), - hide: () => this.setFilesOpen(false), - message: (text) => this.showMessage(text), - progressLoaded: () => { - this.learn.restore() - // Der Lernstand aus dem Ordner bringt die zuletzt offene Lektion mit; der Verweis geht vor. - if (wantedLesson) this.learn.showLesson(wantedLesson) - }, - chooseWorld: (mode) => this.chooseWorld(mode, false), - }) - // Oben die Werkzeugleiste, darunter vier Spalten: Dateien | Lernen | Struktogramm | Code live, jede einklappbar. - const columns = h('div', { class: 'columns' }) - columns.append( - this.filesRail(), - this.files.element, - this.panelResizer(this.files.element, 'files-resizer', filesWidthKey, t('web_files_resize'), 1), - this.learnRail(), - this.learn.element, - this.learnResizer(), - this.editorRail(), - root, - this.panelResizer(root, 'editor-resizer', editorWidthKey, t('web_editor_resize'), () => (this.codeMain ? -1 : 1), () => this.live.element), - this.liveResizer(), - this.live.element, - this.liveRail(), - (this.previewResizer = this.panelResizer(this.preview.element, 'preview-resizer', previewWidthKey, t('web_visubot_preview_resize'), -1)), - this.preview.element, - this.preview.rail, - ) - this.setupPreview() - this.shell.append(this.topbar, columns, this.bottombar) - const learnOpen = storedValue(learnKey) - // Link „Zu den Lektionen“ der Webseite: …/app/?learn. Danach aus der Adresse nehmen, - // sonst öffnet jedes Neuladen den Lernbereich wieder, obwohl man ihn eingeklappt hat. - const fromSite = new URLSearchParams(location.search).has('learn') || wantedLesson !== undefined - this.showWorldInURL() - this.setLearning(fromSite || (learnOpen === null ? window.innerWidth > 1100 : learnOpen === '1'), fromSite) - this.setFilesOpen(storedValue(filesKey) === '1') - const z = Number(storedValue(zoomKey)) - if (zoomSteps.some((s) => s.value === z)) this.zoom = z - this.setLiveCoding(storedValue(liveKey) === '1') - this.setEditorHidden(storedValue(editorHiddenKey) === '1') - this.buildUI() - this.setCodeMain(storedValue(codeMainKey) === '1') - this.render() - window.addEventListener('pointermove', (e) => this.onPointerMove(e)) - window.addEventListener('pointerup', (e) => this.onPointerUp(e)) - window.addEventListener('pointercancel', () => this.endDrag()) - window.addEventListener('keydown', (e) => this.onKeyDown(e)) - window.addEventListener('beforeunload', (e) => { - // Ohne geöffneten Ordner geht der Lernstand beim Schließen verloren: der Browser merkt ihn sich nicht. - if (this.tabs.some((t) => t.dirty && !t.exerciseId) || (!this.files.ready && progress.hasContent())) e.preventDefault() - }) - this.setupFileDrop() - // Nicht jede Vorschau mit `window.claude` kennt `use` (z. B. die Dateiansicht der Claude-App): dann ohne. - const claude = (window as { claude?: { use?: (name: string) => Promise } }).claude - if (typeof claude?.use === 'function') - claude - .use('downloads') - .then((d) => (this.downloads = d as App['downloads'])) - .catch(() => undefined) - } - - // MARK: - UI - - private buildUI() { - this.root.className = 'app' - this.root.innerHTML = ` -
    - ${visuBotEnabled() ? visuBotBrand : 'VisuStruct'} ${e(t('web_brand_suffix'))} -
    - ${iconButton('new', t('toolbar_new'), icons.newFile)} - ${iconButton('open', t('menu_open'), icons.open, shortcut('O'))} - ${iconButton('save', t('menu_save'), icons.save, shortcut('S'))} -
    -
    - - -
    -
    - - - - -
    - - -
    -
    - - - - ` - // Die Werkzeugleiste steht über allen Spalten, nicht im Editor. - this.topbar.replaceChildren(this.root.querySelector('.toolbar')!) - // Die Fußzeile ebenso unter allen Spalten. - this.bottombar.replaceChildren(this.root.querySelector('.statusbar')!) - const $ = (s: string) => - (this.root.querySelector(s) ?? this.topbar.querySelector(s) ?? this.bottombar.querySelector(s)) as T - this.canvasHost = $('.canvas') - this.blocks = new BlockView({ - // wie ein Klick ins Struktogramm: auswählen, mit Maus/Stift auch verschieben - press: (path, e) => { - if (this.simulating) return - if (document.activeElement instanceof HTMLElement) document.activeElement.blur() - const wasSelected = this.selected && samePath(path, this.selected) - this.selected = path - this.render() - if (e.pointerType !== 'touch' && !(isPlaceholderSlot(this.doc, path) && !wasSelected)) this.beginDrag(e, { kind: 'move', path }) - else e.preventDefault() - }, - // Wert im Block geändert (Zahl oder Text): wie Tippen in den Eigenschaften, ein Rückgängig-Schritt - editValue: (path, index, text) => { - // Zählschleife: nur Zahlen eintippen, der Java-Kopf (`int i = 0` …) entsteht von selbst - const lines = replaceBlockValue(this.doc, path, index, text) - if (lines) this.commit(setTextLines(this.doc, path, lines), path) - else this.render() - }, - labels: blockViewLabels(), - }) - this.canvasHost.after(this.blocks.element) - this.inspector = $('.inspector') - this.captionInput = $('.caption') - this.undoButton = $('[data-action=undo]') - this.redoButton = $('[data-action=redo]') - this.deleteButton = $('[data-action=delete]') - this.statusLine = $('.status') - this.simButton = $('[data-action=simulate]') - // Pseudocode (z. B. „Wasserkocher einschalten“): kein Java, die Simulation fragt die Bedingungen. - this.pseudocodeBox = $('[data-action=pseudocode]') - this.blocksButton = $('[data-action=blocks]') - this.blocksButton.addEventListener('click', () => this.setBlockMode(!this.blockMode, true)) - this.pseudocodeBox.addEventListener('change', () => this.commit({ ...this.doc, pseudocode: this.pseudocodeBox.checked || undefined })) - $('[data-action=hide-editor]').addEventListener('click', () => this.setEditorHidden(true)) - $('[data-action=swap-main]').addEventListener('click', () => this.setCodeMain(!this.codeMain)) - this.labelSwap() - this.sim = new SimulationPanel( - () => this.doc, - (path) => { - this.simPath = path - if (this.simulating) this.renderDiagramOnly() - this.scrollToSimPath() - }, - (name) => this.findSubprogram(name), - () => this.setSimulating(false), - (transcript) => { - if (this.simulating && this.liveCoding) this.live.showTranscript(transcript) - }, - ) - this.root.append(this.sim.element, this.simResizer(), this.inspectorResizer()) - if (visuBotEnabled()) { - this.visuBot ??= new VisuBotView() - this.sim.setRobot(this.visuBot) - } else { - this.preview.hostSimulation(undefined) - this.visuBot?.dispose() - this.visuBot = undefined - } - this.simButton.addEventListener('click', () => this.setSimulating(!this.simulating)) - this.codeButton = $('[data-action=code]') - this.code = new CodePanel( - () => this.doc, - (filename, text) => this.offerDownload(filename, new Blob([text], { type: 'text/plain' })), - () => this.setCoding(false), - (on) => this.commit({ ...this.doc, pseudocode: on || undefined }), - () => this.tabs.filter((tab) => tab !== this.tab && parseSubprogramHeader(tab.doc.caption)).map((tab) => tab.doc), - ) - this.root.append(this.code.element) - this.codeButton.addEventListener('click', () => this.setCoding(!this.coding)) - - // Schmale Struktogramm-Spalte: Elemente kompakt (Symbol, Name klein darunter). - this.paletteObserver?.disconnect() - // In den Einstellungen lässt sie sich auch dauerhaft einschalten. - // Selbst gezogene Breite der Elemente: schmal heißt kompakt, breit heißt mit Namen daneben. - const paletteWidth = () => Number(storedValue(paletteWidthKey)) || 0 - const updatePalette = () => - this.root.classList.toggle( - 'compact-palette', - storedValue(compactPaletteKey) === '1' || (paletteWidth() > 0 ? paletteWidth() < 150 : this.root.getBoundingClientRect().width < compactPaletteBelow), - ) - const paletteHidden = () => storedValue(paletteHiddenKey) === '1' - const setPaletteWidth = (width: number | undefined, remember: boolean) => { - const w = width === undefined ? undefined : Math.round(Math.max(80, Math.min(360, width))) - // Eingeklappt bleibt nur die schmale Leiste; die gezogene Breite gilt wieder beim Aufklappen. - if (paletteHidden()) this.root.style.setProperty('--palette-width', '36px') - else if (w === undefined) this.root.style.removeProperty('--palette-width') - else this.root.style.setProperty('--palette-width', `${w}px`) - if (remember) storeValue(paletteWidthKey, w === undefined ? '' : String(w)) - updatePalette() - } - const setPaletteHidden = (hidden: boolean) => { - storeValue(paletteHiddenKey, hidden ? '1' : '0') - this.root.classList.toggle('palette-hidden', hidden) - setPaletteWidth(paletteWidth() > 0 ? paletteWidth() : undefined, false) - } - setPaletteHidden(paletteHidden()) - this.setPaletteHidden = setPaletteHidden - $('[data-action=hide-palette]').addEventListener('click', () => setPaletteHidden(true)) - $('[data-action=show-palette]').addEventListener('click', () => setPaletteHidden(false)) - // Trennlinie zwischen Elementen und Struktogramm: ziehen, Pfeiltasten, Doppelklick setzt zurück. - const paletteBar = $('.palette-resizer') - const paletteEl = $('.palette') - const dir = () => (getComputedStyle(this.shell).direction === 'rtl' ? -1 : 1) - paletteBar.addEventListener('pointerdown', (ev) => { - if (ev.button !== 0) return - ev.preventDefault() - paletteBar.setPointerCapture(ev.pointerId) - const startX = ev.clientX - const start = paletteEl.getBoundingClientRect().width - const s = dir() - this.shell.classList.add('resizing') - const move = (m: PointerEvent) => setPaletteWidth(start + s * (m.clientX - startX), false) - const up = (u: PointerEvent) => { - paletteBar.removeEventListener('pointermove', move) - paletteBar.removeEventListener('pointerup', up) - paletteBar.removeEventListener('pointercancel', up) - this.shell.classList.remove('resizing') - const end = start + s * (u.clientX - startX) - // Ganz an den Rand gezogen: Elemente einklappen, die bisherige Breite bleibt gemerkt. - if (end < 50) setPaletteHidden(true) - else setPaletteWidth(end, true) - } - paletteBar.addEventListener('pointermove', move) - paletteBar.addEventListener('pointerup', up) - paletteBar.addEventListener('pointercancel', up) - }) - paletteBar.addEventListener('dblclick', () => setPaletteWidth(undefined, true)) - paletteBar.addEventListener('keydown', (k) => { - const step = k.key === 'ArrowRight' ? 20 : k.key === 'ArrowLeft' ? -20 : 0 - if (!step) return - k.preventDefault() - setPaletteWidth(paletteEl.getBoundingClientRect().width + dir() * step, true) - }) - this.paletteObserver = new ResizeObserver(updatePalette) - this.paletteObserver.observe(this.root) - const compactPalette = $('[data-action=compact-palette]') - compactPalette.checked = storedValue(compactPaletteKey) === '1' - compactPalette.addEventListener('change', () => { - storeValue(compactPaletteKey, compactPalette.checked ? '1' : '0') - updatePalette() - }) - - const addPaletteItem = (list: HTMLUListElement, icon: PaletteItem, label: string, make: () => VSElement, data: string) => { - const li = document.createElement('li') - li.className = 'palette-item' - li.dataset.item = data - // Gleiche Farbe wie der Kasten im Struktogramm und der Block im Code (Einstellung „Blöcke farbig“) - li.dataset.scope = scopeKind(make().type) ?? 'method' - li.append(this.paletteIcon(icon), document.createTextNode(label)) - li.addEventListener('pointerdown', (e) => this.beginDrag(e, { kind: 'palette', make, label })) - // Tastatur/Klick ohne Ziehen: hinter der Auswahl einfügen (bzw. Platzhalter ersetzen). - li.tabIndex = 0 - li.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') this.insertNearSelection(make()) - }) - list.append(li) - } - const list = $('.palette-basic') - for (const { item } of paletteItems) addPaletteItem(list, item, typeName(paletteElement(item).type), () => paletteElement(item), item) - - // VisuBot: fertige mBot2-Kästen unter den normalen Elementen. - const robotOn = visuBotEnabled() - // Blöcke gibt es (vorerst) nur in VisuBot - this.blocksButton.hidden = !robotOn - this.setBlockMode(robotOn && storedValue(blocksKey) === '1', false) - this.root.classList.toggle('visubot-mode', robotOn) - this.shell.classList.toggle('robot-world', robotOn) - if (!robotOn) this.preview.pause() - document.documentElement.classList.toggle('visubot-theme', robotOn) - const robotList = $('.palette-visubot') - robotList.hidden = !robotOn - $('.palette-visubot-title').hidden = !robotOn - if (robotOn) { - const icon = { [ElementType.verzweigung]: 'verzweigung', [ElementType.whileSchleife]: 'whileSchleife' } as Partial> - // Gruppen wie im Nachschlagen (Fahren und Drehen, Abstand, Linie …), jede auf- und zuklappbar - const collapsed = new Set((storedValue(collapsedGroupsKey) ?? '').split(',').filter(Boolean)) - const groupTitle = (list: HTMLUListElement, group: string) => { - const li = document.createElement('li') - li.className = 'palette-group' - li.dataset.botColor = visuBotGroupColor(group) - const button = document.createElement('button') - button.type = 'button' - button.textContent = [visuBotGroupIcon[group], t(`web_visubot_group_${group}`)].filter(Boolean).join(' ') - const show = (open: boolean) => { - button.setAttribute('aria-expanded', String(open)) - for (const item of list.querySelectorAll(`[data-group="${group}"]`)) item.hidden = !open - } - button.addEventListener('click', () => { - const open = button.getAttribute('aria-expanded') !== 'true' - if (open) collapsed.delete(group) - else collapsed.add(group) - storeValue(collapsedGroupsKey, [...collapsed].join(',')) - show(open) - }) - li.append(button) - list.append(li) - return () => show(!collapsed.has(group)) - } - const grouped = (list: HTMLUListElement, items: T[], add: (item: T) => HTMLElement) => { - for (const group of new Set(items.map((i) => i.group))) { - const apply = groupTitle(list, group) - for (const item of items.filter((i) => i.group === group)) { - const el = add(item) - el.dataset.group = group - el.dataset.botColor = visuBotGroupColor(group) - } - apply() - } - } - grouped(robotList, visuBotTemplates, (tpl) => { - addPaletteItem(robotList, icon[tpl.type] ?? 'anweisung', t(tpl.key), tpl.make, tpl.key) - return robotList.lastElementChild as HTMLElement - }) - // Sensoren: Werte zum Einbauen in eine Bedingung, keine eigenen Kästen - const sensorList = $('.palette-visubot-sensors') - sensorList.replaceChildren() - grouped(sensorList, visuBotSensors, (sensor) => { - const label = t(sensor.key) - const li = document.createElement('li') - li.className = 'palette-item palette-sensor' - li.dataset.item = sensor.key - li.title = sensor.text - const code = document.createElement('code') - code.textContent = sensor.text - // in den Blöcken: Ja/Nein-Sensoren sechseckig, Messwerte rund (wie die Bausteine darin) - if (/^mbot\.is/.test(sensor.text)) code.classList.add('sensor-bool') - li.append(document.createTextNode(label), code) - li.addEventListener('pointerdown', (e) => this.beginDrag(e, { kind: 'sensor', text: sensor.text, label: sensor.text })) - li.tabIndex = 0 - li.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') this.insertSensor(sensor.text) - }) - sensorList.append(li) - return li - }) - } - $('.palette-visubot-sensors').hidden = !robotOn - $('.palette-visubot-sensors-title').hidden = !robotOn - for (const part of ['ops', 'vars', 'control']) { - $(`.palette-visubot-${part}`).hidden = !robotOn - $(`.palette-visubot-${part}-title`).hidden = !robotOn - } - if (robotOn) { - const lang = currentLanguage() - // Operatoren (VisuBlocks): auf ein Wertfeld oder eine Bedingung ziehen wie einen Sensor - const opList = $('.palette-visubot-ops') - opList.replaceChildren( - ...operatorBlocks.map((op) => - this.expressionItem(operatorName(op, lang), op.java, op.shape === 'bool' ? 'bool' : 'round', 'op', op.id === 'random' ? '🎲 1 … 10' : undefined), - ), - ) - // Variablen: anlegen, setzen, ändern; die angelegten Variablen kommen in render() als Bausteine dazu - const varList = $('.palette-visubot-vars') - varList.replaceChildren() - for (const b of variableBlocks) { - const make = () => { - // neue Variable mit freiem Namen; setzen/ändern nimmt die zuletzt angelegte (keine Zählvariable) - const own = statementVariables(this.doc) - const name = b.id === 'declare' ? freshVariable(programVariables(this.doc)) : (own[own.length - 1] ?? 'zahl') - return makeElement(ElementType.anweisung, { textLines: [b.java(name)] }) - } - addPaletteItem(varList, 'anweisung', variableName(b, lang), make, `visublocks_var_${b.id}`) - ;(varList.lastElementChild as HTMLElement).dataset.botColor = 'variable' - } - this.varList = varList - this.shownVars = '' - // Steuerung: „wiederhole 10 mal“ (in Java eine for-Schleife, deren Zählvariable man nicht braucht) - const controlList = $('.palette-visubot-control') - controlList.replaceChildren() - addPaletteItem(controlList, 'forZaehlSchleife', t('web_visubot_el_repeat'), () => ({ ...paletteElement('forZaehlSchleife'), textLines: repeatLoopLines(10) }), 'web_visubot_el_repeat') - } - - this.tabStrip = $('.tabs') - this.exportButton = $('[data-action=export]') - const exportMenu = $('.export-menu') - this.exportButton.setAttribute('aria-haspopup', 'menu') - this.exportButton.addEventListener('click', () => { - exportMenu.hidden = !exportMenu.hidden - if (!exportMenu.hidden) exportMenu.querySelector('button')?.focus() - }) - exportMenu.addEventListener('click', (ev) => { - const kind = (ev.target as HTMLElement).closest('[data-export]')?.dataset.export as ExportKind | undefined - if (!kind) return - exportMenu.hidden = true - void this.exportDiagram(kind) - }) - exportMenu.addEventListener('keydown', (ev) => { - if (ev.key !== 'Escape') return - exportMenu.hidden = true - this.exportButton.focus() - }) - if (this.closeExport) document.removeEventListener('pointerdown', this.closeExport) - this.closeExport = (ev: PointerEvent) => { - if (!exportMenu.hidden && !exportMenu.parentElement!.contains(ev.target as Node)) exportMenu.hidden = true - } - document.addEventListener('pointerdown', this.closeExport) - const language = $('[data-action=language]') - language.value = currentLanguage() - language.addEventListener('change', () => void this.changeLanguage(() => setLanguage(language.value as LanguageCode), 'language')) - // Echter mBot2: Knopf in der Werkzeugleiste (neben der Simulation) und in den Einstellungen - const linkButtons = [$('[data-action=mbot-link]'), $('[data-action=mbot-link-settings]')] - const linkInfo = $('.settings-mbot-link small') - const showLink = () => { - const on = mbotLink.connected - const label = mbotLink.connecting ? t('web_visubot_connecting') : on ? t('web_visubot_disconnect') : t('web_visubot_connect') - linkButtons[0].hidden = !visuBotEnabled() || !canConnectMBot() - uploadButton.hidden = linkButtons[0].hidden - linkButtons[0].classList.toggle('connected', on) - const icon = mbotLink.connecting ? '⏳' : on ? '🟢' : '🔌' - linkButtons[0].textContent = `${icon} mBot2` - linkButtons[0].title = label - linkButtons[1].textContent = `${icon} ${label}` - linkButtons[1].disabled = !canConnectMBot() - linkButtons[1].parentElement!.hidden = !visuBotEnabled() - linkInfo.textContent = canConnectMBot() ? mbotLinkText() : t('web_visubot_no_serial') - } - for (const b of linkButtons) b.addEventListener('click', () => void toggleMBotLink()) - const uploadButton = $('[data-action=mbot-upload]') - uploadButton.addEventListener('click', () => - uploadToMBot( - this.doc, - this.tabs.filter((tab) => tab !== this.tab).map((tab) => tab.doc), - ), - ) - this.stopLinkListener?.() - this.stopLinkListener = mbotLink.listen(showLink) - showLink() - for (const b of this.topbar.querySelectorAll('[data-action=visubot] [data-world]')) - b.addEventListener('click', () => void this.chooseWorld(b.dataset.world as WorldChoice, true)) - const keywords = $('[data-action=keywords]') - keywords.checked = codeKeywords() - keywords.addEventListener('change', () => void this.changeLanguage(() => setCodeKeywords(keywords.checked), 'keywords')) - const scopes = $('[data-action=code-scopes]') - scopes.checked = codeScopes() - scopes.addEventListener('change', () => setCodeScopes(scopes.checked)) - const liveLanguage = $('[data-action=live-language]') - liveLanguage.value = this.live.language - const javaClass = $('[data-action=java-class]') - const javaMain = $('[data-action=java-main]') - const javaSwing = $('[data-action=java-swing]') - javaSwing.checked = this.live.swingSupport - javaSwing.addEventListener('change', () => this.live.setSwingSupport(javaSwing.checked)) - const showJavaForm = () => { - javaClass.checked = this.live.javaForm.asClass - javaMain.checked = this.live.javaForm.withMain - // Die Java-Einstellungen nur zeigen, wenn Java gewählt ist. - $('.settings-menu .java-only').hidden = this.live.language !== 'java' - } - showJavaForm() - liveLanguage.addEventListener('change', () => { - this.live.setTarget(liveLanguage.value as LiveTarget) - showJavaForm() - }) - for (const box of [javaClass, javaMain]) { - box.addEventListener('change', () => this.live.setJavaForm({ asClass: javaClass.checked, withMain: javaMain.checked })) - } - const zoom = $('[data-action=zoom]') - zoom.value = String(this.zoom) - zoom.addEventListener('change', () => { - this.zoom = Number(zoom.value) - storeValue(zoomKey, zoom.value) - this.renderDiagramOnly() - }) - const codeSize = $('[data-action=code-size]') - const storedSize = Number(storedValue(codeSizeKey)) - codeSize.value = String(codeSizes.some((s) => s.value === storedSize) ? storedSize : codeSizes[0].value) - applyCodeSize(Number(codeSize.value)) - codeSize.addEventListener('change', () => { - storeValue(codeSizeKey, codeSize.value) - applyCodeSize(Number(codeSize.value)) - }) - this.setupSettingsMenu($('[data-action=settings]'), $('.settings-menu')) - $('[data-action=about]').addEventListener('click', () => { - $('.settings-menu').hidden = true - this.showAbout() - }) - $('[data-action=cut]').addEventListener('click', () => this.cutSelection()) - $('[data-action=copy]').addEventListener('click', () => this.copySelection()) - $('[data-action=paste]').addEventListener('click', () => this.paste()) - - const fileInput = $('input[type=file]') - fileInput.addEventListener('change', async () => { - const f = fileInput.files?.[0] - fileInput.value = '' - if (f) this.loadXML(await f.text(), f.name) - }) - - $('[data-action=new]').addEventListener('click', () => this.addTab()) - $('[data-action=open]').addEventListener('click', () => this.open(fileInput)) - $('[data-action=save]').addEventListener('click', () => this.save()) - this.undoButton.addEventListener('click', () => this.undo()) - this.redoButton.addEventListener('click', () => this.redo()) - this.deleteButton.addEventListener('click', () => this.deleteSelection()) - - let captionBefore: VSDocument | undefined - this.captionInput.addEventListener('focus', () => (captionBefore = this.doc)) - this.captionInput.addEventListener('input', () => { - this.doc = { ...this.doc, caption: this.captionInput.value } - this.dirty = true - this.updateStatus() - // Der Titel ist in „Code live“ der Klassenname. - this.refreshLive() - }) - this.captionInput.addEventListener('change', () => { - if (captionBefore && captionBefore.caption !== this.doc.caption) this.pushUndo(captionBefore) - captionBefore = this.doc - }) - - this.canvasHost.addEventListener('pointerdown', (e) => this.onCanvasPointerDown(e)) - this.canvasHost.addEventListener('contextmenu', (e) => this.onCanvasContextMenu(e)) - this.resizeObserver?.disconnect() - // Nur bei echter Größenänderung neu zeichnen, sonst schaukeln sich Scrollbalken (Windows) gegenseitig auf - this.resizeObserver = new ResizeObserver(() => { - const { clientWidth, clientHeight } = this.canvasHost - if (clientWidth === this.renderedHostSize.width && clientHeight === this.renderedHostSize.height) return - this.renderDiagramOnly() - }) - this.resizeObserver.observe(this.canvasHost) - } - - /** Sprache wechseln wie in den App-Einstellungen: Oberfläche neu aufbauen, Reiter und Verlauf bleiben. */ - /** Sprache oder Code-Keywords umstellen und alle Texte neu aufbauen; das Einstellungsmenü bleibt offen. */ - /** `?visubot` oder `?visustruct` in der Adresse, damit man den Link mit der richtigen Welt weitergeben kann. */ - private showWorldInURL() { - const params = new URLSearchParams(location.search) - for (const key of ['learn', 'lektion', 'visubot', 'visustruct', 'visuobject', 'visuflow']) params.delete(key) - const rest = params.toString() - const world = visuBotMode() === 'on' ? 'visubot' : 'visustruct' - history.replaceState(history.state, '', `${location.pathname}?${world}${rest ? `&${rest}` : ''}${location.hash}`) - } - - /** VisuStruct oder VisuBot: Lernbereich, Elemente und Simulator neu aufbauen. */ - private async chooseWorld(mode: WorldChoice, fromSettings: boolean) { - if (mode === 'object') { - // VisuObject hat eine eigene Oberfläche: neu laden. Ungespeichertes fragt der Browser vorher ab. - setVisuObjectWorld() - location.href = `${location.pathname}?visuobject${location.hash}` - return - } - if (mode === 'flow') { - // VisuFlow (PAP zeichnen) hat ebenfalls eine eigene Oberfläche. - setVisuFlowWorld() - location.href = `${location.pathname}?visuflow${location.hash}` - return - } - if (mode === visuBotMode() && worldChosen()) return - await this.changeLanguage( - async () => { - setVisuBotMode(mode) - useVisuBotContent(mode) - this.showWorldInURL() - }, - fromSettings ? `visubot] [data-world=${mode}` : undefined, - ) - } - - private async changeLanguage(change: () => Promise, control?: string) { - this.setSimulating(false) - this.setCoding(false) - await change() - applyCanvasStubTexts() - this.sim.end() - this.live.build() - this.preview.relabel() - this.placePreview() - this.files.build() - this.buildUI() - this.render() - this.learn.render() - this.labelLearnRail() - this.labelLiveRail() - this.labelEditorRail() - this.labelFilesRail() - if (control === undefined) return - this.topbar.querySelector('[data-action=settings]')?.click() - this.topbar.querySelector(`[data-action=${control}]`)?.focus() - } - - /** Eingeklappte Bereiche öffnen (Zeiger im Lektionstext); zurück kommt, wie man genau diese wieder einklappt. */ - private revealAreas(areas: UiArea[]): () => void { - const undo: (() => void)[] = [] - for (const area of areas) { - if (area === 'editor' && this.editorHidden) { - this.setEditorHidden(false) - undo.push(() => this.setEditorHidden(true)) - } else if (area === 'palette' && this.root.classList.contains('palette-hidden')) { - this.setPaletteHidden?.(false) - undo.push(() => this.setPaletteHidden?.(true)) - } else if (area === 'livecode' && !this.liveCoding) { - this.setLiveCoding(true) - undo.push(() => this.setLiveCoding(false)) - } else if (area === 'files' && !this.shell.classList.contains('files-open')) { - this.setFilesOpen(true) - undo.push(() => this.setFilesOpen(false)) - } - } - return () => undo.reverse().forEach((f) => f()) - } - - // MARK: - Dateien - - private setFilesOpen(on: boolean) { - this.shell.classList.toggle('files-open', on) - storeValue(filesKey, on ? '1' : '0') - this.keepAColumnOpen() - if (this.canvasHost) this.renderDiagramOnly() - } - - private filesRail(): HTMLElement { - this.filesRailButton = h('button', { class: 'learn-rail files-rail' }) as HTMLButtonElement - this.labelFilesRail() - this.filesRailButton.addEventListener('click', () => this.setFilesOpen(true)) - return this.filesRailButton - } - - private labelFilesRail() { - if (!this.filesRailButton) return - this.filesRailButton.title = t('web_files_show') - this.filesRailButton.innerHTML = `${sidebarIcon}📁 ${e(t('web_files_title'))}` - } - - /** Programm aus dem Workspace öffnen; gespeicherter Code (.java) behält seine Kommentare. */ - private openFromWorkspace(xml: string, file: WorkspaceFile, java: string | undefined) { - const already = this.tabs.find((tab) => tab.workspace?.dir === file.dir && tab.workspace.base === file.base) - if (already) { - this.tabIndex = this.tabs.indexOf(already) - this.render() - return - } - if (xml) this.loadXML(xml, `${file.base}.visustruct`) - else if (java !== undefined) { - if (this.dirty || this.tab.fileName || this.tab.exerciseId) this.addTab() - this.doc = this.live.documentFromJavaText(java) - this.tab.fileName = `${file.base}.visustruct` - this.resetHistory() - } - this.tab.workspace = file - this.tab.fileHandle = undefined - if (java !== undefined) { - this.live.adoptJava(this.doc, java) - this.setLiveCoding(true) - } - this.render() - } - - /** Speichern in den Workspace: in den Ordner des Programms, sonst Klasse/Tagesordner. `false` = dort nicht möglich. */ - private async saveToWorkspace(): Promise { - if (!this.files.ready || (this.tab.fileHandle && !this.tab.workspace)) return false - const title = this.doc.caption.trim() || tabTitle(this.tab) - const java = this.live.language === 'java' ? this.live.javaFor(this.doc, this.tab) : undefined - try { - const place = await this.files.save(title, encodeDocument(this.doc), java, this.tab.workspace) - if (!place) { - this.setFilesOpen(true) - return true - } - this.tab.workspace = place - this.tab.fileName = `${place.base}.visustruct` - this.dirty = false - this.render() - this.showMessage(t('web_files_saved_fmt', place.path)) - } catch (err) { - this.showMessage((err as Error).message) - } - return true - } - - // MARK: - Lernbereich - - private setLearning(on: boolean, remember = true) { - this.shell.classList.toggle('learning', on) - if (remember) storeValue(learnKey, on ? '1' : '0') - this.keepAColumnOpen() - if (this.canvasHost) this.renderDiagramOnly() - } - - // MARK: - Spalte Struktogramm - - /** Struktogramm-Spalte einklappen, z. B. um nur Code live (wie in einer IDE) oder nur Lektionen zu sehen. */ - private setEditorHidden(hidden: boolean) { - this.editorHidden = hidden - this.shell.classList.toggle('editor-hidden', hidden) - storeValue(editorHiddenKey, hidden ? '1' : '0') - // Ohne Struktogramm soll der Code zu sehen sein, nicht eine leere Fläche. - if (hidden && !this.liveCoding && !this.shell.classList.contains('learning')) this.setLiveCoding(true) - if (!hidden && this.canvasHost) this.render() - } - - /** Mindestens eine Spalte bleibt offen; sonst kommt das Struktogramm zurück. */ - private keepAColumnOpen() { - if (this.editorHidden && !this.liveCoding && !this.shell.classList.contains('learning') && !this.shell.classList.contains('files-open')) { - this.setEditorHidden(false) - } - } - - private editorRail(): HTMLElement { - this.editorRailButton = h('button', { class: 'learn-rail editor-rail' }) as HTMLButtonElement - this.labelEditorRail() - this.editorRailButton.addEventListener('click', () => this.setEditorHidden(false)) - return this.editorRailButton - } - - private labelEditorRail() { - if (!this.editorRailButton) return - this.editorRailButton.title = t('web_editor_show') - this.editorRailButton.innerHTML = `${sidebarIcon}✎ ${e(t('web_editor_title'))}` - } - - /** „Über VisuStruct“ wie in der App (dieselben Texte aus `Localizable.strings`). */ - private showAbout() { - const link = (label: string, href: string, text: string) => - `` - const dialog = h('dialog', { class: 'about', 'aria-labelledby': 'about-title' }) as HTMLDialogElement - dialog.innerHTML = ` -
    -

    ${e(t('about_nav_title'))}

    - -
    -
    -
    - -
    -

    VisuStruct ${e(t('web_brand_suffix'))}

    -

    ${e(t('web_about_version_fmt').replace('%@', __APP_VERSION__))}

    -

    Holger Sebastiao

    - ${link('about_web_label', 'https://visustruct.visucode.de', 'visustruct.visucode.de')} - ${link('about_developer_label', 'https://www.sebastiao.org', 'www.sebastiao.org')} - ${link('about_project_github_label', 'https://github.com/code4teaching/VisuStruct', 'code4teaching/VisuStruct')} -
    -
    -

    ${e(t('about_dateiformat_heading'))}

    -

    ${e(t('about_dateiformat_part1'))}.visustruct${e(t('about_dateiformat_part2'))}

    -

    ${e(t('about_dateiformat_strk'))}

    -

    ${e(t('web_about_software'))}

    -

    ${e(t('web_about_software_text'))} ${e(t('web_about_licenses'))}

    -

    ${e(t('about_copyright'))}

    -
    ` - dialog.querySelector('[data-action=about-done]')!.addEventListener('click', () => dialog.close()) - // Klick auf den abgedunkelten Rand schließt ebenfalls. - dialog.addEventListener('click', (ev) => ev.target === dialog && dialog.close()) - dialog.addEventListener('close', () => dialog.remove()) - document.body.append(dialog) - dialog.showModal() - } - - /** Schmale Leiste am Rand, solange der Lernbereich eingeklappt ist. */ - /** Zahnrad oben rechts: Textgröße und Sprache. Schließt bei Klick daneben und mit Escape. */ - private setupSettingsMenu(button: HTMLButtonElement, menu: HTMLElement) { - const show = (open: boolean) => { - menu.hidden = !open - button.setAttribute('aria-expanded', String(open)) - } - button.addEventListener('click', () => show(menu.hidden)) - // Nach einem Sprachwechsel wird die Leiste neu gebaut: alten Zuhörer entfernen. - if (this.closeSettings) document.removeEventListener('pointerdown', this.closeSettings) - if (this.escapeSettings) document.removeEventListener('keydown', this.escapeSettings) - this.closeSettings = (ev: PointerEvent) => { - if (!menu.hidden && !button.parentElement!.contains(ev.target as Node)) show(false) - } - document.addEventListener('pointerdown', this.closeSettings) - // Am Dokument statt am Menü: Safari gibt einem angeklickten Haken keinen Fokus. - this.escapeSettings = (ev: KeyboardEvent) => { - if (ev.key !== 'Escape' || menu.hidden) return - show(false) - button.focus() - } - document.addEventListener('keydown', this.escapeSettings) - } - - private learnRail(): HTMLElement { - this.railButton = h('button', { class: 'learn-rail' }) as HTMLButtonElement - this.labelLearnRail() - this.railButton.addEventListener('click', () => this.setLearning(true)) - return this.railButton - } - - /** Wie beim Lernbereich: eingeklappt bleibt „Code live“ als schmale Leiste am rechten Rand. */ - private liveRail(): HTMLElement { - this.liveRailButton = h('button', { class: 'learn-rail live-rail' }) as HTMLButtonElement - this.labelLiveRail() - this.liveRailButton.addEventListener('click', () => this.setLiveCoding(true)) - return this.liveRailButton - } - - private labelLiveRail() { - if (!this.liveRailButton) return - this.liveRailButton.title = t('web_live_show') - this.liveRailButton.innerHTML = `${sidebarIcon}</> ${e(t('web_live_title'))}` - } - - private labelLearnRail() { - if (!this.railButton) return - this.railButton.title = t('web_learn_show') - this.railButton.innerHTML = `${sidebarIcon}🎓 ${e(t('web_learn_title'))}` - } - - private learnHost(): LearnHost { - return { - collapse: () => this.setLearning(false), - showEditor: () => { - if (this.editorHidden) this.setEditorHidden(false) - }, - showLiveCode: () => { - if (!this.liveCoding) this.setLiveCoding(true) - }, - showJava: () => this.openJavaColumn(), - showFiles: () => { - if (!this.shell.classList.contains('files-open')) this.setFilesOpen(true) - }, - reveal: (areas) => this.revealAreas(areas), - openExercise: (ex: Exercise, doc: VSDocument) => { - // Die Übung sagt, wo man sie löst (`bereich:`). Holger 2026-10-31: Lektionen bleiben offen, sonst ist nur - // dieser Bereich zu sehen (bei `beide` Struktogramm und Code); Dateien, mBot2 live usw. gehen zu. - const area = exerciseArea(ex) - if (area !== 'code') this.setEditorHidden(false) - // Java-Übungen stellen Code auf Java; sonst bleibt die Sprache (z. B. Python oder JavaScript in Lektion 21). - if (area !== 'struktogramm') { - if (ex.code === 'java') this.openJavaColumn() - else if (!this.liveCoding) this.setLiveCoding(true) - } - if (this.coding) this.setCoding(false) - if (this.shell.classList.contains('files-open')) this.setFilesOpen(false) - if (this.previewOpen) this.setPreviewOpen(false) - if (area === 'struktogramm' && this.liveCoding) this.setLiveCoding(false) - if (area === 'code' && !this.editorHidden) this.setEditorHidden(true) - const existing = this.tabs.findIndex((tab) => tab.exerciseId === ex.id) - if (existing >= 0) { - this.switchTab(existing) - this.setSimulating(false) - return - } - const tab = this.makeTab() - tab.doc = doc - tab.exerciseId = ex.id - tab.selected = doc.elements.length > 0 ? [0] : undefined - // Ein unberührter leerer Reiter wird ersetzt statt einen weiteren zu öffnen. - if (this.isPristine(this.tab)) this.tabs[this.tabIndex] = tab - else this.tabs.push(tab) - this.tabIndex = this.tabs.indexOf(tab) - this.setSimulating(false) - this.render() - }, - openJavaCode: (java: string) => { - this.openJavaColumn() - const tab = this.makeTab() - tab.doc = this.live.documentFromJavaText(java) - // Ein unberührter leerer Reiter wird ersetzt statt einen weiteren zu öffnen. - if (this.isPristine(this.tab)) this.tabs[this.tabIndex] = tab - else this.tabs.push(tab) - this.tabIndex = this.tabs.indexOf(tab) - this.setSimulating(false) - // Der Code bleibt so stehen, wie er in der Lektion steht. - this.live.adoptJava(tab.doc, java) - this.render() - }, - openDiagram: (doc: VSDocument, fileName: string) => { - this.setEditorHidden(false) - // Tab nach der Diagrammüberschrift benennen, nicht nach der Datei (l1-tee) - this.loadXML(encodeDocument(doc), doc.caption.trim() ? `${doc.caption.trim()}.visustruct` : fileName) - }, - exerciseDocument: (id: string) => { - const own = this.tabs.find((tab) => tab.exerciseId === id) - if (own) return own.doc - // Ohne „Übung öffnen“ gebaut: Das offene Struktogramm wird zur Übung, statt eine Fehlermeldung zu zeigen. - const tab = this.tab - if (tab.exerciseId || this.isPristine(tab)) return undefined - tab.exerciseId = id - progress.updateExercise(id, { xml: encodeDocument(tab.doc) }) - return tab.doc - }, - subprogramsFor: (id: string) => { - const own = this.tabs.find((tab) => tab.exerciseId === id) - return (name) => this.findSubprogram(name, own) - }, - stopSimulation: () => this.setSimulating(false), - resetExercise: (id: string, doc: VSDocument) => { - const tab = this.tabs.find((x) => x.exerciseId === id) - if (!tab) return - tab.undoStack.push(tab.doc) - tab.doc = doc - tab.redoStack = [] - tab.selected = [0] - if (tab === this.tab) this.render() - }, - preview: (doc: VSDocument) => { - const image = Export.renderExportSVG(doc, this.metrics, this.measure, 0) - if (!image) return undefined - // Kleine Diagramme nicht über ihre natürliche Größe hinaus vergrößern. - const width = image.svg.getAttribute('width') - if (width) { - image.svg.style.maxWidth = `${width}px` - // Breite Diagramme nicht bis zur Unlesbarkeit verkleinern: lieber seitlich scrollen. - image.svg.style.minWidth = `${Math.round(Number(width) * 0.6)}px` - } - image.svg.removeAttribute('width') - image.svg.removeAttribute('height') - return image.svg - }, - } - } - - private isPristine(tab: Tab): boolean { - return !tab.dirty && !tab.fileName && !tab.exerciseId && tab.undoStack.length === 0 - } - - /** Stand einer Übung merken (kurz verzögert, damit Tippen flüssig bleibt). */ - private rememberExercise() { - const tab = this.tab - if (!tab.exerciseId) return - clearTimeout(this.exerciseSaveTimer) - this.exerciseSaveTimer = window.setTimeout(() => { - if (tab.exerciseId) progress.updateExercise(tab.exerciseId, { xml: encodeDocument(tab.doc) }) - }, 300) - } - - /** Kleine Vorschau des Elements als Symbol in der Palette. */ - private paletteIcon(item: PaletteItem): SVGSVGElement { - const s = el('svg', { viewBox: '0 0 28 20', width: 28, height: 20, class: 'icon' }) - const stroke = { fill: 'none', stroke: 'currentColor', 'stroke-width': 1.5 } - s.append(el('rect', { x: 1, y: 1, width: 26, height: 18, ...stroke })) - const path = (d: string) => s.append(el('path', { d, ...stroke })) - switch (item) { - case 'verzweigung': - path('M1 1 L14 10 L27 1 M14 10 V19') - break - case 'fallauswahl': - path('M1 1 L20 10 L27 1 M1 10 H27 M10 10 V19 M20 10 V19') - break - case 'forZaehlSchleife': - case 'whileSchleife': - path('M7 7 H27 M7 7 V19') - break - case 'doUntilSchleife': - path('M7 1 V13 H27') - break - case 'endlosschleife': - path('M7 6 H27 M7 6 V14 H27') - break - case 'aufruf': - path('M5 1 V19 M23 1 V19') - break - case 'aussprung': - path('M6 1 L1 10 L6 19') - break - } - return s - } - - // MARK: - Rendering - - private render() { - // Gruppensymbole, die ältere Block-Ansichten in Befehle geschrieben haben, wieder entfernen - const withoutIcons = withoutBlockIcons(this.doc) - if (withoutIcons) { - this.doc = withoutIcons - this.dirty = true - } - const displayDoc: VSDocument = { ...this.doc, elements: this.doc.elements.map(displayElement) } - this.layout = this.engine.layout(displayDoc) - this.flat = flattenNodes(this.layout.elements) - if (this.selected && !tryElementAt(this.doc, this.selected)) this.selected = undefined - - // Wie `DiagramCanvasView`: mindestens so groß wie Struktur und sichtbarer Bereich (Ablegen unterhalb = ans Ende). - const z = this.zoom - const host = this.canvasHost - this.renderedHostSize = { width: host.clientWidth, height: host.clientHeight } - // Abgerundet, damit das Diagramm den sichtbaren Bereich nie um Bruchteile eines Pixels überragt - // (sonst erscheinen bei 125 % Skalierung unter Windows Scrollbalken ohne Grund) - const size = { - width: Math.max(320, this.layout.canvasSize.width, Math.floor(host.clientWidth / z)), - height: Math.max(240, this.layout.canvasSize.height + 60, Math.floor(host.clientHeight / z)), - } - this.pseudocodeBox.checked = !!this.doc.pseudocode - this.renderer.usesSimulationHighlight = this.simulating - this.renderer.fillsSelection = this.liveCoding - this.svg = this.renderer.render(this.layout, size, this.simulating ? this.simPath : this.selected) - // Textgröße wie `canvasScaleStep`: Layout und Linien wachsen gleichmäßig mit. - this.svg.setAttribute('width', String(Math.floor(size.width * z))) - this.svg.setAttribute('height', String(Math.floor(size.height * z))) - this.overlay = el('g', { class: 'drop-overlay' }) - this.svg.append(this.overlay) - // Während der Bearbeitung im Diagramm nur das Bild tauschen, das Eingabefeld bleibt (mit Fokus). - const oldSvg = this.inline?.element.isConnected ? this.canvasHost.querySelector(':scope > svg') : null - if (oldSvg) oldSvg.replaceWith(this.svg) - else this.canvasHost.replaceChildren(this.svg) - this.refreshVarPalette() - if (this.blockMode) this.blocks?.render(this.doc, this.simulating ? this.simPath : this.selected, this.simulating, z) - - if (document.activeElement !== this.captionInput) this.captionInput.value = this.doc.caption - this.undoButton.disabled = this.simulating || this.undoStack.length === 0 - this.redoButton.disabled = this.simulating || this.redoStack.length === 0 - this.deleteButton.disabled = this.simulating || !this.selected || !canDeleteElement(this.doc, this.selected) - this.captionInput.readOnly = this.simulating - this.codeButton.disabled = this.simulating - this.exportButton.disabled = this.simulating - if (this.coding) this.code.refresh() - this.refreshLive() - this.refreshPreview() - this.renderTabs() - this.renderInspector() - this.updateStatus() - } - - private refreshLive() { - if (!this.liveCoding) return - this.live.refresh(this.doc, this.simulating ? this.simPath : this.selected, { - owner: this.tab, - subprograms: new Set(this.tabs.filter((tab) => tab !== this.tab).flatMap((tab) => parseSubprogramHeader(tab.doc.caption)?.name ?? [])), - editable: !this.simulating, - resolve: (name) => this.findSubprogram(name), - }) - } - - private updateStatus() { - this.rememberExercise() - document.title = `${this.dirty ? '• ' : ''}${this.fileName} – VisuStruct Web` - this.statusLine.textContent = `${this.fileName}${this.dirty ? ` (${t('web_status_modified')})` : ''}` - } - - /** Wie `InspectorPanelBody` in Swift: Art (Anweisung/Ein-/Ausgabe), festes Schlüsselwort, Zählschleife, Fälle. */ - private renderInspector() { - const path = this.selected - const element = path ? tryElementAt(this.doc, path) : undefined - const box = this.inspector - if (!path || !element) { - box.replaceChildren( - h('h2', {}, t('web_inspector_title')), - h('p', { class: 'hint' }, t('inspector_no_selection')), - h('p', { class: 'hint' }, t('web_inspector_help_drop')), - h('p', { class: 'hint' }, t('web_inspector_help_inline')), - h('p', { class: 'hint' }, t('web_inspector_help_files')), - ) - return - } - box.replaceChildren(h('h2', {}, `${t('web_inspector_title')} · ${typeName(element.type)}`)) - - if (element.type === ElementType.anweisung) { - const mode = LineEditor.ioMode(element.textLines.join('\n')) - const seg = h('div', { class: 'segmented', role: 'group', 'aria-label': t('inspector_io_mode_title') }) - for (const [m, label] of [['statement', t('inspector_io_mode_statement')], ['input', t('inspector_io_mode_input')], ['output', t('inspector_io_mode_output')]] as const) { - const b = h('button', { 'aria-pressed': String(m === mode) }, label) - b.addEventListener('click', () => { - // Aktuellen Text lesen: das Textfeld kann ihn seit dem Aufbau geändert haben. - const current = tryElementAt(this.doc, path)?.textLines.join('\n') ?? '' - if (m === LineEditor.ioMode(current)) return - const text = LineEditor.applyingIOMode(m, current) - this.commit(setTextLines(this.doc, path, text.split('\n'))) - this.inspector.querySelector('textarea')?.focus() - }) - seg.append(b) - } - box.append(seg, h('p', { class: 'hint mono-hint' }, ioModeHints[mode])) - } - - if (element.type === ElementType.forSchleife) { - const row = h('div', { class: 'for-fields' }) - const parts = LineEditor.paddedForLoopParts(element.textLines) - const labels = [ - [t('inspector_for_init'), t('inspector_for_placeholder_init')], - [t('inspector_for_condition'), t('inspector_for_placeholder_condition')], - [t('inspector_for_step'), t('inspector_for_placeholder_step')], - ] - labels.forEach(([label, placeholder], i) => { - const input = h('input', { placeholder, spellcheck: 'false', autocomplete: 'off' }) as HTMLInputElement - input.value = parts[i] - this.liveEdit(input, (value) => { - const el = tryElementAt(this.doc, path) - if (!el) return undefined - const lines = LineEditor.paddedForLoopParts(el.textLines) - lines[i] = LineEditor.normalizedForLoopPart(value) - return setTextLines(this.doc, path, lines) - }) - row.append(h('label', {}, label, input)) - }) - box.append(row) - } else if (element.type !== ElementType.leerElement) { - const hint = conditionHints[element.type] - if (hint) box.append(h('p', { class: 'hint mono-hint' }, hint)) - const prefix = fixedPrefix(element) - const ta = h('textarea', { rows: '3', spellcheck: 'false' }) as HTMLTextAreaElement - // Leere Anweisung: im Struktogramm „ø“, im Textfeld leer (nichts erst löschen müssen). - ta.value = editableText(element) - ta.placeholder = placeholderFor(element) - this.liveEdit(ta, (value) => storingText(this.doc, path, value)) - const row = h('div', { class: 'editor-row' }) - if (prefix) row.append(h('span', { class: 'fixed-prefix' }, prefix)) - row.append(ta) - box.append(row) - } - - if (elementVariableNames(element).length > 0) { - const rename = h('button', { class: 'inspector-rename' }, t('web_rename_menu')) - rename.addEventListener('click', () => this.renameVariable(path)) - box.append(rename) - } - this.renderCaseControls(path) - } - - // MARK: - Umbenennen - - /** - * Fenster „Variable umbenennen“ für das Element an `path`: Mit getipptem Code wird dort umbenannt - * (Kommentare bleiben, das Struktogramm folgt), sonst im Struktogramm. - */ - private renameVariable(path: number[]) { - const element = tryElementAt(this.doc, path) - if (!element || this.simulating) return - const code = this.liveCoding ? this.live.editableCode : undefined - const own = elementVariableNames(element) - const all = code !== undefined ? variableNames([code]) : documentVariableNames(this.doc) - const names = [...own, ...all.filter((n) => !own.includes(n))] - if (names.length === 0) return - const existing = code !== undefined ? codeWords(code).map((w) => w.name) : documentWords(this.doc) - void askRename({ - names, - existing, - count: (name) => (code !== undefined ? nameRanges(code, name).length : countInDocument(this.doc, name)), - }).then((r) => { - if (!r) return - if (code !== undefined) this.live.renameInCode(r.from, r.to) - else { - const next = renameInDocument(this.doc, r.from, r.to) - if (next.count > 0) this.commit(next.doc) - } - }) - } - - /** Rechtsklick auf ein Element: auswählen und „Variable umbenennen …“ anbieten. */ - private onCanvasContextMenu(e: MouseEvent) { - if (this.simulating || this.inline) return - const p = this.svgPoint(e.clientX, e.clientY) - const node = p && this.nodeAt(p.x, p.y) - const element = node && tryElementAt(this.doc, node.idPath) - if (!node || !element || elementVariableNames(element).length === 0) return - e.preventDefault() - this.selected = node.idPath - this.render() - showRenameMenu(e.clientX, e.clientY, t('web_rename_menu'), () => this.renameVariable(node.idPath)) - } - - /** Fallname und „Fall hinzufügen/entfernen“, wenn die Auswahl in einer Fallauswahl liegt. */ - private renderCaseControls(path: number[]) { - const fa = multiwayAncestorPath(path, this.doc) - if (!fa) return - const cases = tryElementAt(this.doc, fa)?.cases ?? [] - const caseIdx = caseIndexInMultiway(path, fa) - const section = h('div', { class: 'case-controls' }) - if (caseIdx !== undefined && cases[caseIdx]) { - const input = h('input', { placeholder: t('web_case_name_placeholder'), spellcheck: 'false', autocomplete: 'off' }) as HTMLInputElement - input.value = cases[caseIdx].name - this.liveEdit(input, (value) => (value.trim() === '' ? undefined : setFallauswahlCaseName(this.doc, fa, caseIdx, value))) - section.append(h('label', {}, t('web_case_name'), input)) - } - const add = h('button', {}, `+ ${t('inspector_add_case')}`) - add.addEventListener('click', () => { - const r = appendCaseToFallauswahl(this.doc, fa) - this.commit(r.doc, r.path) - }) - const remove = h('button', { class: 'danger-text' }, `- ${t('inspector_remove_case')}`) as HTMLButtonElement - remove.disabled = cases.length <= 2 || caseIdx === undefined - remove.addEventListener('click', () => { - if (caseIdx === undefined) return - const r = removeCaseFromFallauswahl(this.doc, fa, caseIdx) - this.commit(r.doc, r.path) - }) - section.append(h('div', { class: 'group' }, add, remove)) - this.inspector.append(section) - } - - /** Eingabefeld, das beim Tippen das Diagramm ändert; ein Rückgängig-Schritt pro Bearbeitung. */ - private liveEdit(field: HTMLInputElement | HTMLTextAreaElement, apply: (value: string) => VSDocument | undefined) { - let before: VSDocument | undefined - field.addEventListener('focus', () => (before = this.doc)) - field.addEventListener('input', () => { - // Typografische Anführungszeichen sofort durch gerade ersetzen (gleiche Länge, Cursor bleibt). - const normalized = LineEditor.normalizingQuotes(field.value) - if (normalized !== field.value) { - const pos = field.selectionStart - field.value = normalized - field.setSelectionRange(pos, pos) - } - const next = apply(normalized) - if (!next) return - this.doc = next - this.dirty = true - this.renderDiagramOnly() - }) - field.addEventListener('blur', () => { - if (before && before !== this.doc) this.pushUndo(before) - before = undefined - // Inspector nicht neu aufbauen: ein Klick auf einen seiner Knöpfe soll sein Ziel behalten. - this.renderDiagramOnly() - }) - } - - // MARK: - Direkt im Struktogramm bearbeiten - - /** - * Eingabefeld über dem Text des Elements öffnen (Doppelklick, Doppeltipp oder Enter). - * `point` (Diagramm-Koordinaten) entscheidet bei der Fallauswahl, ob ein Fallname gemeint ist. - */ - private startInlineEdit(path: number[], point?: { x: number; y: number }) { - if (this.simulating || this.blockMode) return - this.inline?.finish(true) - const node = this.flat.find((n) => samePath(n.idPath, path)) - const element = tryElementAt(this.doc, path) - if (!node || !element || element.type === ElementType.leerElement) return - const lh = this.metrics.lineHeight - const base = { host: this.canvasHost, zoom: this.zoom, fontSize: this.metrics.fontSize, lineHeight: lh } - const open = (o: { rect: Rect; prefix?: string; fields: InlineField[]; center: boolean; apply: (values: string[]) => VSDocument | undefined }) => { - this.inline = openInlineEditor({ - ...base, - ...o, - onDone: (values) => { - this.inline = undefined - const next = values && o.apply(values) - const before = tryElementAt(this.doc, path) - const after = next && tryElementAt(next, path) - const changed = next && JSON.stringify([before?.textLines, before?.cases?.map((c) => c.name)]) !== JSON.stringify([after?.textLines, after?.cases?.map((c) => c.name)]) - if (changed) this.commit(next, path) - else this.render() - }, - }) - } - - const caseIdx = point ? caseLabelAt(node, point.x, point.y, lh) : undefined - if (caseIdx !== undefined && element.cases?.[caseIdx]) { - return open({ - rect: caseLabelRect(node, caseIdx, lh), - fields: [{ value: element.cases[caseIdx].name, placeholder: t('web_case_name_placeholder'), label: t('web_case_name') }], - center: true, - apply: ([v]) => (v.trim() === '' ? undefined : setFallauswahlCaseName(this.doc, path, caseIdx, v.trim())), - }) - } - if (element.type === ElementType.forSchleife) { - const parts = LineEditor.paddedForLoopParts(element.textLines) - const labels: [string, string][] = [ - [t('inspector_for_init'), t('inspector_for_placeholder_init')], - [t('inspector_for_condition'), t('inspector_for_placeholder_condition')], - [t('inspector_for_step'), t('inspector_for_placeholder_step')], - ] - return open({ - rect: textRect(node, lh), - prefix: 'for', - fields: labels.map(([label, placeholder], i) => ({ value: parts[i], placeholder, label })), - center: false, - apply: (values) => setTextLines(this.doc, path, values.map((v) => LineEditor.normalizedForLoopPart(v))), - }) - } - open({ - rect: textRect(node, lh), - prefix: fixedPrefix(element), - fields: [{ value: editableText(element), placeholder: placeholderFor(element), label: typeName(element.type) }], - center: element.type === ElementType.anweisung || element.type === ElementType.aufruf || element.type === ElementType.aussprung, - apply: ([v]) => storingText(this.doc, path, v), - }) - } - - /** VisuBot: Struktogramm als Blöcke (`on`) oder als Kästen; `remember` speichert die Wahl. */ - private setBlockMode(on: boolean, remember: boolean) { - this.blockMode = on - if (remember) storeValue(blocksKey, on ? '1' : '0') - // Die Lektionen folgen der Block-Ansicht (Holger 5.11.): Blöcke = kurze Lektionen für Jüngere - if (useBlockLessons(on)) this.learn?.render() - this.root.classList.toggle('block-mode', on) - this.canvasHost.hidden = on - if (this.blocks) this.blocks.element.hidden = !on - // Der Knopf nennt, wohin er führt (wie in VisuMicro): in den Blöcken zurück zum Struktogramm - const label = t(on ? 'web_visubot_blocks_back' : 'web_visubot_blocks') - this.blocksButton.innerHTML = `${e(label)}` - this.blocksButton.setAttribute('aria-pressed', String(on)) - this.blocksButton.dataset.tip = t(on ? 'web_visubot_blocks_back_title' : 'web_visubot_blocks_title') - if (remember) this.render() - } - - /** Beim Tippen: nur das Diagramm neu zeichnen, damit das Textfeld den Fokus behält. */ - private renderDiagramOnly() { - const sel = this.selected - const keepInspector = this.inspector - const placeholder = document.createElement('aside') - this.inspector = placeholder - this.render() - this.inspector = keepInspector - this.selected = sel - } - - // MARK: - Simulation - - /** Wie `SimulationController.begin/end`: Diagramm nur anzeigen, rechts das Simulationsfeld. */ - private setSimulating(on: boolean) { - if (on === this.simulating) return - if (document.activeElement instanceof HTMLElement) document.activeElement.blur() - this.endDrag() - this.simulating = on - this.root.classList.toggle('simulating', on) - // Erst nach dem Aufklappen von „mBot2 live“ steht die Breite fest. - if (on) requestAnimationFrame(() => this.fixSimSplit()) - this.simButton.setAttribute('aria-pressed', String(on)) - // Beschriftung statt Symbol allein: „Simulation starten“ wird zu „Simulation beenden“. - this.simButton.innerHTML = `${on ? icons.stop : icons.play}${e(t(on ? 'web_simulation_end' : 'web_simulation_start'))}` - this.simButton.dataset.tip = t(on ? 'web_simulation_end_title' : 'web_simulation_start_title') - this.simButton.setAttribute('aria-label', t(on ? 'web_simulation_end' : 'web_simulation_start')) - if (on) { - this.setCoding(false) - this.setEditorHidden(false) - // VisuBot: Die Simulation zeigt die Welten der Übung in diesem Reiter (sonst die freie Fläche). - this.visuBot?.setWorlds(exerciseWorlds(this.tab.exerciseId)) - // VisuBot: Der mBot2 der Simulation fährt im Fenster „mBot2 live“ (das dafür aufgeht). - if (this.visuBot && !this.previewOpen) { - this.setPreviewOpen(true) - this.previewOpenedForSim = true - } - this.placeSimulationRobot() - this.sim.begin() - } else { - this.sim.end() - this.placeSimulationRobot() - if (this.previewOpenedForSim) this.setPreviewOpen(false) - this.simPath = undefined - if (!this.selected && this.doc.elements.length > 0) this.selected = [0] - } - this.render() - } - - /** - * ▶ in Programmieren mit mBot2-Befehlen: Simulation öffnen und dort ausführen, damit die 3D-Welt - * (und der verbundene mBot2) mitfährt und der aktuelle Befehl in Struktogramm und Code markiert ist. - */ - private runRobot(stepping: boolean): boolean { - if (!this.visuBot || !/\bmbot\s*\./u.test(JSON.stringify(this.tabs.map((tab) => tab.doc)))) return false - if (this.simulating) this.sim.reset() - else this.setSimulating(true) - if (stepping) this.sim.step() - else this.sim.play() - return true - } - - // MARK: - Code - - private setCoding(on: boolean) { - if (on === this.coding) return - // Beide Code-Spalten nebeneinander wären zu eng. - if (on) { - this.setLiveCoding(false) - this.setEditorHidden(false) - } - this.coding = on - this.root.classList.toggle('coding', on) - this.codeButton.setAttribute('aria-pressed', String(on)) - if (on) this.code.refresh() - else this.code.close() - this.renderDiagramOnly() - } - - // MARK: - Code live - - /** Übungen mit `code: java`: Spalte Code öffnen und auf Java stellen, wie über das Zahnrad. */ - private openJavaColumn() { - if (this.live.language !== 'java') { - const sel = '[data-action=live-language]' - const select = this.topbar.querySelector(sel) ?? this.root.querySelector(sel) - if (select) { - select.value = 'java' - select.dispatchEvent(new Event('change')) - } else this.live.setTarget('java') - } - if (!this.liveCoding) this.setLiveCoding(true) - } - - // MARK: - Programmieren groß - - /** ⇄: Programmieren wird die große Fläche links, das Struktogramm schmal rechts daneben (Breite ziehbar); noch ein Klick tauscht zurück. */ - private setCodeMain(on: boolean) { - this.codeMain = on - storeValue(codeMainKey, on ? '1' : '0') - if (on && !this.liveCoding) this.setLiveCoding(true) - if (on && this.editorHidden) this.setEditorHidden(false) - this.shell.classList.toggle('code-main', on) - this.labelSwap() - if (this.canvasHost) this.render() - } - - private labelSwap() { - const tip = t(this.codeMain ? 'web_swap_to_editor' : 'web_swap_to_code') - for (const b of this.shell.querySelectorAll('[data-action=swap-main]')) { - b.dataset.tip = tip - b.setAttribute('aria-label', tip) - b.setAttribute('aria-pressed', String(this.codeMain)) - } - } - - // MARK: - mBot2 live - - private setupPreview() { - // Selbst auf- oder zugeklappt: bleibt so, auch nach der Simulation. - const open = (on: boolean) => { - this.previewOpenedForSim = false - this.setPreviewOpen(on) - } - this.preview.onHide = () => open(false) - this.preview.rail.addEventListener('click', () => open(true)) - this.preview.onToggleStacked = () => { - this.previewStacked = !this.previewStacked - storeValue(previewStackedKey, this.previewStacked ? '1' : '0') - // Unter dem Code geht nur mit offener Spalte „Code“. - if (this.previewStacked && !this.liveCoding) this.setLiveCoding(true) - this.placePreview() - } - this.previewStacked = storedValue(previewStackedKey) === '1' - const saved = storedValue(previewKey) - this.previewOpen = saved === null || saved === '1' - // Höhe unter dem Code: an der Linie darüber ziehen (nach oben = größer), Doppelklick setzt zurück. - const bar = this.preview.heightBar - const panel = this.preview.element - const apply = (height: number | undefined, remember: boolean) => { - const max = Math.max(160, (panel.parentElement?.getBoundingClientRect().height ?? 600) - 120) - const value = height === undefined ? undefined : Math.round(Math.max(160, Math.min(height, max))) - panel.style.height = value === undefined ? '' : `${value}px` - if (remember) storeValue(previewHeightKey, value === undefined ? '' : String(value)) - } - const savedHeight = Number(storedValue(previewHeightKey)) - if (savedHeight > 0) panel.style.height = `${savedHeight}px` - bar.addEventListener('pointerdown', (ev) => { - if (ev.button !== 0) return - ev.preventDefault() - bar.setPointerCapture(ev.pointerId) - const startY = ev.clientY - const startHeight = panel.getBoundingClientRect().height - this.shell.classList.add('resizing') - const move = (m: PointerEvent) => apply(startHeight - (m.clientY - startY), false) - const up = (u: PointerEvent) => { - bar.removeEventListener('pointermove', move) - bar.removeEventListener('pointerup', up) - bar.removeEventListener('pointercancel', up) - this.shell.classList.remove('resizing') - apply(startHeight - (u.clientY - startY), true) - } - bar.addEventListener('pointermove', move) - bar.addEventListener('pointerup', up) - bar.addEventListener('pointercancel', up) - }) - bar.addEventListener('dblclick', () => apply(undefined, true)) - bar.addEventListener('keydown', (k) => { - const step = k.key === 'ArrowUp' ? 20 : k.key === 'ArrowDown' ? -20 : 0 - if (!step) return - k.preventDefault() - apply(panel.getBoundingClientRect().height + step, true) - }) - this.placePreview() - } - - private setPreviewOpen(on: boolean) { - this.previewOpen = on - storeValue(previewKey, on ? '1' : '0') - if (!on) this.preview.pause() - this.placePreview() - this.placeSimulationRobot() - if (this.canvasHost) this.render() - } - - /** Simulation in VisuBot: ihre 3D-Ansicht im offenen Fenster „mBot2 live“, sonst im Simulationspanel. */ - private placeSimulationRobot() { - const robot = this.visuBot - const inPreview = !!robot && this.simulating && this.previewOpen - this.preview.hostSimulation(inPreview ? robot.viewport : undefined) - if (!inPreview) robot?.restoreViewport() - } - - /** Eigene Spalte ganz rechts oder unten in „Code“ (nur wenn diese Spalte offen ist). */ - private placePreview() { - const stacked = this.previewStacked && this.liveCoding - this.preview.setStacked(this.previewStacked) - this.shell.classList.toggle('preview', this.previewOpen) - this.shell.classList.toggle('preview-stacked', stacked) - if (stacked) { - if (this.preview.element.parentElement !== this.live.element) this.live.element.append(this.preview.element) - } else if (this.preview.element.previousElementSibling !== this.previewResizer) this.previewResizer.after(this.preview.element) - } - - /** Nach jeder Änderung: „mBot2 live“ rechnet das Programm neu durch (nur in VisuBot und wenn es offen ist). */ - private refreshPreview() { - if (!this.previewOpen || !visuBotEnabled()) return - this.preview.update( - this.doc, - (name) => this.findSubprogram(name), - exerciseWorlds(this.tab.exerciseId), - this.tab.exerciseId ?? '', - this.tabs.map((tab) => tab.doc), - ) - } - - private setLiveCoding(on: boolean) { - if (on && this.coding) this.setCoding(false) - this.liveCoding = on - this.shell.classList.toggle('live', on) - this.placePreview() - if (!on) this.live.hideConsole() - storeValue(liveKey, on ? '1' : '0') - this.keepAColumnOpen() - if (this.canvasHost) this.render() - } - - /** Markierten Schritt sichtbar halten, wenn das Diagramm größer als das Fenster ist. */ - /** Ein anderer Reiter, dessen Titel oder Dateiname zum Aufruf passt, z. B. `zeigeTitel()`. */ - /** - * Linie zwischen Diagramm und Steuerung während der Simulation. Gemerkt wird die Breite des Diagramms (`--sim-work`), - * die Steuerung füllt den Rest: Zieht man an der Linie zwischen Steuerung und Code, ändert sich so die Steuerung - * und nicht das Diagramm (Holger 2026-11-04). Doppelklick stellt den Standard wieder her. - */ - private simResizer(): HTMLElement { - const bar = h('div', { class: 'sim-resizer', role: 'separator', 'aria-orientation': 'vertical', tabindex: '0', title: t('web_visuobject_resize') }) - const root = this.root - const clamp = (width: number) => { - const total = root.getBoundingClientRect().width - return Math.round(Math.max(120, total > 0 ? Math.min(width, total - 240) : width)) - } - const apply = (width: number | undefined, remember: boolean) => { - if (remember) storeValue(simWorkKey, width === undefined ? '' : String(clamp(width))) - if (width === undefined) this.fixSimSplit() - else root.style.setProperty('--sim-work', `${clamp(width)}px`) - } - const work = () => root.getBoundingClientRect().width - this.sim.element.getBoundingClientRect().width - const sign = () => (getComputedStyle(this.shell).direction === 'rtl' ? -1 : 1) - bar.addEventListener('pointerdown', (e) => { - if (e.button !== 0) return - e.preventDefault() - bar.setPointerCapture(e.pointerId) - const startX = e.clientX - const startWidth = work() - const s = sign() - this.shell.classList.add('resizing') - const move = (ev: PointerEvent) => apply(startWidth + s * (ev.clientX - startX), false) - const up = (ev: PointerEvent) => { - bar.removeEventListener('pointermove', move) - bar.removeEventListener('pointerup', up) - bar.removeEventListener('pointercancel', up) - this.shell.classList.remove('resizing') - apply(startWidth + s * (ev.clientX - startX), true) - } - bar.addEventListener('pointermove', move) - bar.addEventListener('pointerup', up) - bar.addEventListener('pointercancel', up) - }) - bar.addEventListener('dblclick', () => apply(undefined, true)) - bar.addEventListener('keydown', (e) => { - const step = e.key === 'ArrowRight' ? 20 : e.key === 'ArrowLeft' ? -20 : 0 - if (!step) return - e.preventDefault() - apply(work() + sign() * step, true) - }) - return bar - } - - /** - * Waagerechte Linie zwischen Diagramm und Objektinspektor: mit Maus, Finger (wischen) oder Pfeiltasten - * nach oben oder unten ziehen ändert die Höhe des Objektinspektors (Holger 2026-11-05). Doppelklick = Standard. - */ - private inspectorResizer(): HTMLElement { - const label = t('web_inspector_resize') - const bar = h('div', { class: 'insp-resizer', role: 'separator', 'aria-orientation': 'horizontal', tabindex: '0', title: label, 'aria-label': label }) - const root = this.root - const clamp = (height: number) => { - const total = root.getBoundingClientRect().height - return Math.round(Math.max(48, total > 0 ? Math.min(height, total - 160) : height)) - } - const apply = (height: number | undefined, remember: boolean) => { - if (height === undefined) { - root.classList.remove('insp-sized') - root.style.removeProperty('--insp-height') - } else { - root.classList.add('insp-sized') - root.style.setProperty('--insp-height', `${clamp(height)}px`) - } - if (remember) storeValue(inspectorHeightKey, height === undefined ? '' : String(clamp(height))) - } - const current = () => this.inspector.getBoundingClientRect().height - bar.addEventListener('pointerdown', (e) => { - if (e.button !== 0) return - e.preventDefault() - bar.setPointerCapture(e.pointerId) - const startY = e.clientY - const startHeight = current() - this.shell.classList.add('resizing') - const move = (ev: PointerEvent) => apply(startHeight - (ev.clientY - startY), false) - const up = (ev: PointerEvent) => { - bar.removeEventListener('pointermove', move) - bar.removeEventListener('pointerup', up) - bar.removeEventListener('pointercancel', up) - this.shell.classList.remove('resizing') - apply(startHeight - (ev.clientY - startY), true) - } - bar.addEventListener('pointermove', move) - bar.addEventListener('pointerup', up) - bar.addEventListener('pointercancel', up) - }) - bar.addEventListener('dblclick', () => apply(undefined, true)) - bar.addEventListener('keydown', (e) => { - const step = e.key === 'ArrowUp' ? 20 : e.key === 'ArrowDown' ? -20 : 0 - if (!step) return - e.preventDefault() - apply(current() + step, true) - }) - const saved = Number(storedValue(inspectorHeightKey)) - if (saved > 0) apply(saved, false) - return bar - } - - /** Beim Start der Simulation: Diagramm bekommt die gemerkte Breite, sonst so viel, dass die Steuerung ihre Standardbreite hat. */ - private fixSimSplit() { - const root = this.root - const total = root.getBoundingClientRect().width - const saved = Number(storedValue(simWorkKey)) - const control = root.classList.contains('visubot-mode') ? Math.min(440, total * 0.45) : 320 - const width = saved > 0 ? saved : total - control - root.style.setProperty('--sim-work', `${Math.round(Math.max(120, total > 0 ? Math.min(width, total - 240) : width))}px`) - } - - /** Senkrechte Trennlinie: Breite des Lernbereichs mit Maus, Finger oder Pfeiltasten ändern, Doppelklick setzt zurück. */ - private learnResizer(): HTMLElement { - return this.panelResizer(this.learn.element, 'learn-resizer', learnWidthKey, t('web_learn_resize'), 1) - } - - /** Wie beim Lernbereich, nur am anderen Rand: Ziehen nach links macht den Code breiter. */ - private liveResizer(): HTMLElement { - return this.panelResizer(this.live.element, 'live-resizer', liveWidthKey, t('web_live_resize'), -1, () => this.root) - } - - /** - * Struktogramm und Programmieren teilen sich den Platz, den die anderen Spalten übrig lassen. Ist der zu knapp, - * geben beim Ziehen an der Linie dazwischen erst „mBot2 live“, dann Lernbereich und Dateien Breite ab (bis zu ihrer - * Mindestbreite), sonst ließe sich die Aufteilung bei vier offenen Spalten kaum verändern. - */ - private yieldingPanels(): { panel: HTMLElement; key: string }[] { - return [ - { panel: this.preview.element, key: previewWidthKey }, - { panel: this.learn.element, key: learnWidthKey }, - { panel: this.files.element, key: filesWidthKey }, - ].filter(({ panel }) => panel.offsetParent !== null && panel.parentElement === this.live.element.parentElement) - } - - /** `side`: 1 = Bereich liegt am Anfang der Zeile (links), -1 = am Ende (rechts); als Funktion, wenn ⇄ die Seite wechselt. */ - /** `partner`: die breitenvariable Spalte auf der anderen Seite der Linie; dann geben beim Ziehen auch weitere Spalten Platz ab. */ - private panelResizer( - panel: HTMLElement, - className: string, - widthKey: string, - title: string, - side: 1 | -1 | (() => 1 | -1), - partner?: () => HTMLElement, - ): HTMLElement { - const bar = h('div', { class: className, role: 'separator', 'aria-orientation': 'vertical', tabindex: '0', title }) - // Höchstens so breit, dass die Spalte daneben noch 240 px behält; feste Breiten anderer Bereiche zählen mit. - const clamp = (width: number) => { - const total = panel.parentElement?.getBoundingClientRect().width ?? 0 - if (total === 0) return clampPanelWidth(width) - const others = [this.files.element, this.learn.element, this.live.element, this.preview.element] - .filter((p) => p !== panel && p.offsetParent !== null && getComputedStyle(p).flexGrow === '0') - .reduce((sum, p) => sum + p.getBoundingClientRect().width, 0) - const min = parseFloat(getComputedStyle(panel).minWidth) || 200 - return Math.round(Math.max(min, Math.min(width, total - others - 240))) - } - const apply = (width: number | undefined, remember: boolean) => { - panel.style.width = width === undefined ? '' : `${clamp(width)}px` - if (remember) storeValue(widthKey, width === undefined ? '' : String(clamp(width))) - } - const saved = Number(storedValue(widthKey)) - if (saved > 0) apply(saved, false) - // Im Rechts-nach-links-Layout sind die Seiten vertauscht. - const sign = () => { - const s = typeof side === 'function' ? side() : side - return getComputedStyle(this.shell).direction === 'rtl' ? -s : s - } - bar.addEventListener('pointerdown', (e) => { - if (e.button !== 0) return - e.preventDefault() - bar.setPointerCapture(e.pointerId) - const startX = e.clientX - const startWidth = panel.getBoundingClientRect().width - const s = sign() - this.shell.classList.add('resizing') - const resize = partner ? this.pushResize(panel, widthKey, partner(), startWidth) : undefined - const drag = (ev: PointerEvent, remember: boolean) => (resize ?? apply)(startWidth + s * (ev.clientX - startX), remember) - const move = (ev: PointerEvent) => drag(ev, false) - const up = (ev: PointerEvent) => { - bar.removeEventListener('pointermove', move) - bar.removeEventListener('pointerup', up) - bar.removeEventListener('pointercancel', up) - this.shell.classList.remove('resizing') - drag(ev, true) - } - bar.addEventListener('pointermove', move) - bar.addEventListener('pointerup', up) - bar.addEventListener('pointercancel', up) - }) - bar.addEventListener('dblclick', () => apply(undefined, true)) - bar.addEventListener('keydown', (e) => { - const step = e.key === 'ArrowRight' ? 20 : e.key === 'ArrowLeft' ? -20 : 0 - if (!step) return - e.preventDefault() - apply(panel.getBoundingClientRect().width + sign() * step, true) - }) - return bar - } - - /** - * Ziehen an der Linie zwischen Struktogramm und Programmieren: `panel` hat eine feste Breite, `partner` füllt den Rest. - * Reicht der Platz des Partners (oder von `panel`) nicht, schrumpfen die anderen Spalten; ihre Breite wird gemerkt. - */ - private pushResize(panel: HTMLElement, widthKey: string, partner: HTMLElement, startWidth: number): (width: number, remember: boolean) => void { - const minOf = (p: HTMLElement, fallback: number) => Math.max(parseFloat(getComputedStyle(p).minWidth) || 0, fallback) - const panelMin = minOf(panel, 200) - const slack = Math.max(0, partner.getBoundingClientRect().width - minOf(partner, 240)) - const yielders = this.yieldingPanels().map((y) => ({ ...y, start: y.panel.getBoundingClientRect().width, min: minOf(y.panel, 200), changed: false })) - const spare = yielders.reduce((sum, y) => sum + Math.max(0, y.start - y.min), 0) - return (wanted, remember) => { - const width = Math.round(Math.max(panelMin, Math.min(wanted, startWidth + slack + spare))) - // Was weder `panel` noch der Partner aufnehmen kann, geben die anderen Spalten ab, die nächste zuerst. - let need = Math.max(0, width - startWidth - slack, panelMin - wanted) - for (const y of yielders) { - const give = Math.min(need, Math.max(0, y.start - y.min)) - need -= give - y.changed ||= give > 0 - if (!y.changed) continue - y.panel.style.width = `${Math.round(y.start - give)}px` - if (remember) storeValue(y.key, String(Math.round(y.start - give))) - } - panel.style.width = `${width}px` - if (remember) storeValue(widthKey, String(width)) - } - } - - private findSubprogram(name: string, current: Tab | undefined = this.tab): VSDocument | undefined { - const key = subprogramKey(name) - if (key === '') return undefined - const match = this.tabs.find( - (tab) => tab !== current && (subprogramKey(tab.doc.caption) === key || subprogramKey(tabTitle(tab)) === key), - ) - return match?.doc - } - - private scrollToSimPath() { - const path = this.simPath - if (!this.simulating || !path) return - if (this.blockMode) return this.blocks?.scrollTo(path) - const node = this.flat.find((n) => samePath(n.idPath, path)) - if (!node) return - const host = this.canvasHost - const z = this.zoom - const f = { x: node.frame.x * z, y: node.frame.y * z, width: node.frame.width * z, height: node.frame.height * z } - const pad = 24 - if (f.y < host.scrollTop) host.scrollTop = Math.max(0, f.y - pad) - else if (f.y + Math.min(f.height, host.clientHeight - 2 * pad) > host.scrollTop + host.clientHeight) { - host.scrollTop = f.y - pad - } - if (f.x < host.scrollLeft || f.x > host.scrollLeft + host.clientWidth - pad) host.scrollLeft = Math.max(0, f.x - pad) - } - - // MARK: - Dokument - - private commit(next: VSDocument, select?: number[]) { - this.codeTyping = undefined - this.pushUndo(this.doc) - this.doc = next - this.dirty = true - if (select) this.selected = select - this.render() - } - - /** - * Getippter Code in „Code live“ ergab ein neues Struktogramm. Wer am Stück tippt, bekommt einen - * Rückgängig-Schritt pro kurzer Pause statt einen pro Buchstabe. - */ - private commitFromCode(next: VSDocument, select?: number[]) { - const now = Date.now() - const typing = this.codeTyping - if (!typing || typing.tab !== this.tab || now - typing.last > 1000 || now - typing.start > 5000) { - this.pushUndo(this.doc) - this.codeTyping = { tab: this.tab, start: now, last: now } - } else typing.last = now - this.doc = next - this.dirty = true - this.selected = sanitizedSelection(select ?? this.selected, next) - this.render() - } - - private pushUndo(prev: VSDocument) { - this.undoStack.push(prev) - while (this.undoStack.length > maxUndoSteps) this.undoStack.shift() - this.tab.redoStack = [] - } - - private undo() { - const prev = this.undoStack.pop() - if (!prev) return - this.codeTyping = undefined - this.redoStack.push(this.doc) - this.doc = prev - this.dirty = true - this.selected = sanitizedSelection(this.selected, this.doc) - this.render() - } - - private redo() { - const next = this.redoStack.pop() - if (!next) return - this.codeTyping = undefined - this.undoStack.push(this.doc) - this.doc = next - this.dirty = true - this.selected = sanitizedSelection(this.selected, this.doc) - this.render() - } - - private resetHistory() { - this.setSimulating(false) - this.tab.undoStack = [] - this.tab.redoStack = [] - this.selected = undefined - this.dirty = false - this.render() - } - - // MARK: - Reiter (DiagramSheetTabsModel.swift) - - private tabStrip!: HTMLElement - - private makeTab(): Tab { - return { doc: newEmptyDocument(), serial: this.nextTabSerial++, dirty: false, undoStack: [], redoStack: [], selected: [0] } - } - - private addTab(tab = this.makeTab()) { - this.setSimulating(false) - this.tabs.push(tab) - this.tabIndex = this.tabs.length - 1 - this.render() - } - - private switchTab(index: number) { - if (index === this.tabIndex || !this.tabs[index]) return - if (document.activeElement instanceof HTMLElement) document.activeElement.blur() - this.setSimulating(false) - this.tabIndex = index - this.render() - } - - private async closeTab(index: number) { - if (this.tabs.length <= 1) return - const tab = this.tabs[index] - if (tab.dirty && !tab.exerciseId) { - this.switchTab(index) - if (!(await this.confirmDiscard())) return - } - this.setSimulating(false) - this.tabs.splice(index, 1) - if (this.tabIndex > index || this.tabIndex >= this.tabs.length) this.tabIndex = Math.max(0, this.tabIndex - 1) - this.render() - } - - private renderTabs() { - const strip = this.tabStrip - strip.replaceChildren() - this.tabs.forEach((tab, i) => { - const item = document.createElement('div') - item.className = 'tab' + (i === this.tabIndex ? ' active' : '') - item.setAttribute('role', 'tab') - item.setAttribute('aria-selected', String(i === this.tabIndex)) - const label = document.createElement('button') - label.className = 'tab-label' - label.textContent = tabTitle(tab) - label.title = tab.fileName ?? tabTitle(tab) - label.addEventListener('click', () => this.switchTab(i)) - item.append(label) - // Wie bei VS Code: ungespeichert zeigt rechts einen Punkt, beim Darüberfahren wird er zum ✕. - const unsaved = tab.dirty && !tab.exerciseId - if (this.tabs.length > 1) { - const close = document.createElement('button') - close.className = 'tab-close' + (unsaved ? ' unsaved' : '') - close.innerHTML = '✕' - close.title = t('a11y_close_tab') - close.setAttribute('aria-label', `${t('a11y_close_tab')}: ${tabTitle(tab)}`) - close.addEventListener('click', () => this.closeTab(i)) - item.append(close) - } else if (unsaved) { - item.append(Object.assign(document.createElement('span'), { className: 'tab-close unsaved', innerHTML: '●' })) - } - if (unsaved) label.setAttribute('aria-label', `${tabTitle(tab)} (${t('web_status_modified')})`) - strip.append(item) - }) - const add = document.createElement('button') - add.className = 'tab-add' - add.textContent = '+' - add.title = t('a11y_new_diagram_tab') - add.setAttribute('aria-label', t('a11y_new_diagram_tab')) - add.addEventListener('click', () => this.addTab()) - strip.append(add) - } - - // MARK: - Zwischenablage (Element) - - private copySelection() { - const el = this.selected && tryElementAt(this.doc, this.selected) - if (!el) return false - this.clipboard = structuredClone(el) - this.showMessage(t('web_copied')) - return true - } - - private cutSelection() { - if (this.selected && canDeleteElement(this.doc, this.selected) && this.copySelection()) this.deleteSelection() - } - - private paste() { - if (this.clipboard) this.insertNearSelection(structuredClone(this.clipboard)) - } - - private deleteSelection() { - if (!this.selected || !canDeleteElement(this.doc, this.selected)) return - this.commit(deleteAtPath(this.doc, this.selected)) - this.selected = undefined - this.render() - } - - /** Einfügen ohne Ziehen: Platzhalter ersetzen bzw. hinter der Auswahl (oder ans Ende) einfügen. */ - /** Baustein für „Operatoren“ bzw. eine Variable: Name und Java, zum Ziehen auf ein Feld oder eine Bedingung */ - private expressionItem(label: string, java: string, shape: 'round' | 'bool', kind: 'op' | 'var', shown = java): HTMLLIElement { - const li = document.createElement('li') - li.className = `palette-item palette-sensor palette-expr palette-${kind}` - li.dataset.botColor = kind === 'op' ? 'operator' : 'variable' - li.title = java - const code = document.createElement('code') - code.textContent = shown - if (shape === 'bool') code.classList.add('sensor-bool') - li.append(document.createTextNode(label), code) - li.addEventListener('pointerdown', (e) => this.beginDrag(e, { kind: 'sensor', text: java, label: shown })) - li.tabIndex = 0 - li.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') this.insertSensor(java) - }) - return li - } - - /** VisuBot: jede angelegte Variable als Baustein unter „Variablen“ (nur neu bauen, wenn sich etwas ändert) */ - private refreshVarPalette() { - if (!this.varList) return - const vars = programVariables(this.doc) - const key = vars.join(',') - if (key === this.shownVars) return - this.shownVars = key - this.varList.querySelectorAll('.palette-var').forEach((li) => li.remove()) - this.varList.append(...vars.map((v) => this.expressionItem(v, v, 'round', 'var'))) - } - - /** - * Blöcke: Operator, Variable oder Sensor auf einen Block (nicht auf ein Feld). Eine Bedingung wird ersetzt; - * „und“/„oder“/„nicht“ verbinden sich mit der alten Bedingung. Auf eine Anweisung passen nur Sensoren. - */ - private dropOnCondition(text: string, path: number[]) { - const el = elementAt(this.doc, path) - if (el.type === ElementType.anweisung) { - if (/^mbot\./.test(text)) this.insertSensor(text, path) - return - } - const old = el.textLines.join(' ').trim() - const next = - !old || old === 'true' - ? text - : text === 'true && true' - ? `${old} && true` - : text === 'true || true' - ? `${old} || true` - : text === '!true' - ? `!(${old})` - : text - this.commit(setTextLines(this.doc, path, [next]), path) - } - - /** - * VisuBot: Sensorwert in den Text eines Elements schreiben (ohne Ziel: in das gewählte Element). - * Leere Bedingung → nur der Wert; sonst kommt er hinten dazu. Den Vergleich (`< 20`) schreiben die Schüler selbst. - */ - private insertSensor(text: string, path = this.selected) { - if (!path || isPlaceholderSlot(this.doc, path)) return - const el = elementAt(this.doc, path) - if (!takesSensor.includes(el.type)) return - const old = (el.textLines ?? []).join(' ').trim() - this.commit(setTextLines(this.doc, path, [old ? `${old} ${text}` : text]), path) - // Gleich weiterschreiben können, z. B. ` < 20` - const ta = this.inspector.querySelector('textarea') - if (ta) { - ta.focus() - ta.setSelectionRange(ta.value.length, ta.value.length) - } - } - - private insertNearSelection(element: VSElement) { - const path = this.selected - if (path && isPlaceholderSlot(this.doc, path)) return this.commit(replaceAtPath(this.doc, path, element), path) - if (path) { - const next = [...path.slice(0, -1), path[path.length - 1] + 1] - return this.commit(insertAfterSibling(this.doc, element, path), next) - } - if (this.doc.elements.length === 1 && isPlaceholderSlot(this.doc, [0])) { - return this.commit(replaceAtPath(this.doc, [0], element), [0]) - } - this.commit(insertAt(this.doc, [], this.doc.elements.length, element), [this.doc.elements.length]) - } - - // MARK: - Dateien - - /** Rückfrage im Fenster selbst (`window.confirm` ist in eingebetteten Ansichten gesperrt). */ - private confirmDiscard(): Promise { - if (!this.dirty) return Promise.resolve(true) - return new Promise((resolve) => { - const box = document.createElement('div') - box.className = 'confirm' - box.innerHTML = `
    -

    ${e(t('close_unsaved_title'))}

    -

    ${e(t('web_discard_message_fmt', this.fileName))}

    -
    -
    ` - box.addEventListener('click', (e) => { - const r = (e.target as HTMLElement).dataset.r - if (r === undefined && e.target !== box) return - box.remove() - resolve(r === '1') - }) - document.body.append(box) - box.querySelector('[data-r="0"]')!.focus() - }) - } - - private showMessage(text: string) { - this.statusLine.textContent = text - this.statusLine.classList.add('message') - setTimeout(() => { - this.statusLine.classList.remove('message') - this.updateStatus() - }, 6000) - } - - /** Wie `openIncomingDocument`: ungespeicherte Änderungen oder eine verknüpfte Datei → neuer Reiter. */ - private loadXML(xml: string, name: string, handle?: FileHandle) { - let doc: VSDocument - try { - doc = decodeDocument(xml) - } catch (err) { - this.showMessage(t('web_err_unreadable_fmt', name, (err as Error).message)) - return - } - // Eine Übung bleibt in ihrem Reiter, auch wenn sie noch leer ist. - if (this.dirty || this.tab.fileName || this.tab.exerciseId) this.addTab() - this.doc = doc - this.tab.fileName = name.replace(/\.(strk|xml)$/i, '.visustruct') - this.fileHandle = handle && /\.visustruct$/i.test(name) ? handle : undefined - this.resetHistory() - } - - private async open(fallback: HTMLInputElement) { - const w = window as PickerWindow - if (!w.showOpenFilePicker || this.inViewer) return fallback.click() - try { - const [handle] = await w.showOpenFilePicker({ types: pickerTypes() }) - const file = await handle.getFile() - this.loadXML(await file.text(), file.name, handle) - } catch { - /* abgebrochen */ - } - } - - private async save() { - if (!this.downloads && (await this.saveToWorkspace())) return - const blob = new Blob([encodeDocument(this.doc)], { type: 'application/xml' }) - const w = window as PickerWindow - try { - if (this.downloads) { - // Die Vorschau erlaubt nur bekannte Endungen; die echte Web-App speichert direkt als .visustruct. - const filename = this.fileName + '.txt' - await this.downloads.save({ filename, data: blob }) - this.tab.fileName ??= this.fileName - this.dirty = false - this.render() - this.showMessage(t('web_saved_txt_fmt', filename)) - return - } - if (!this.fileHandle && w.showSaveFilePicker && !this.inViewer) { - this.fileHandle = await w.showSaveFilePicker({ suggestedName: this.fileName, types: pickerTypes() }) - this.tab.fileName = this.fileHandle.name - } - if (this.fileHandle) { - const out = await this.fileHandle.createWritable() - await out.write(blob) - await out.close() - } else { - const a = document.createElement('a') - a.href = URL.createObjectURL(blob) - a.download = this.fileName - a.click() - setTimeout(() => URL.revokeObjectURL(a.href), 1000) - } - this.tab.fileName ??= this.fileName - this.dirty = false - this.render() - } catch { - /* abgebrochen */ - } - } - - /** Datei zum Herunterladen anbieten (in der Vorschau über die `downloads`-Fähigkeit). */ - // MARK: - Export (wie `DiagramExporter`) - - private async exportDiagram(kind: ExportKind) { - // Die Datei selbst zum Weitergeben, z. B. per Mail oder in die Lernplattform; ändert nicht, wo gespeichert wird. - if (kind === 'visustruct') { - const base = Export.exportFilenameBase(this.doc, tabTitle(this.tab)) - return this.offerDownload(`${base}.visustruct`, new Blob([encodeDocument(this.doc)], { type: 'application/xml' })) - } - const image = Export.renderExportSVG(this.doc, this.metrics, this.measure) - if (!image) { - this.showMessage(t(kind === 'print' ? 'err_diagram_too_small_print' : 'err_diagram_too_small_export')) - return - } - const base = Export.exportFilenameBase(this.doc, tabTitle(this.tab)) - // Wie `displayScale` × Textgröße: scharf auch auf hochauflösenden Bildschirmen. - const scale = Math.max(2, window.devicePixelRatio || 1) * this.zoom - try { - switch (kind) { - case 'svg': { - image.svg.setAttribute('width', String(image.width * this.zoom)) - image.svg.setAttribute('height', String(image.height * this.zoom)) - await this.offerDownload(`${base}.svg`, new Blob([Export.svgMarkup(image)], { type: 'image/svg+xml' })) - break - } - case 'png': { - const canvas = await Export.rasterize(image, scale) - await this.offerDownload(`${base}.png`, await Export.canvasToPNG(canvas)) - break - } - case 'pdf': { - const canvas = await Export.rasterize(image, Math.max(scale, 3)) - const pdf = await Export.buildDiagramPDF(Export.canvasRGB(canvas), canvas.width, canvas.height, image, Export.exportVisibleDiagramTitle(this.doc)) - await this.offerDownload(`${base}.pdf`, pdf) - break - } - case 'print': - this.printDiagram(image) - break - } - } catch (err) { - this.showMessage(t('web_export_failed_fmt', err instanceof Error ? err.message : String(err))) - } - } - - /** Drucken: nur das Export-Bild auf die Seite (siehe `@media print` in styles.css). */ - private printDiagram(image: Export.ExportImage) { - const sheet = document.createElement('div') - sheet.className = 'print-sheet' - image.svg.removeAttribute('width') - image.svg.removeAttribute('height') - sheet.append(image.svg) - document.body.append(sheet) - const cleanup = () => sheet.remove() - window.addEventListener('afterprint', cleanup, { once: true }) - try { - window.print() - } catch { - cleanup() - this.showMessage(`${t('err_print_unavailable')} ${t('web_print_hint')}`) - return - } - // Manche Browser senden kein `afterprint`. - setTimeout(cleanup, 60_000) - } - - private async offerDownload(filename: string, blob: Blob) { - try { - if (this.downloads) { - try { - await this.downloads.save({ filename, data: blob }) - this.showMessage(t('web_saved_fmt', filename)) - } catch { - // Die Vorschau erlaubt nur bekannte Endungen. - await this.downloads.save({ filename: filename + '.txt', data: blob }) - this.showMessage(t('web_saved_txt_fmt', filename + '.txt')) - } - return - } - const a = document.createElement('a') - a.href = URL.createObjectURL(blob) - a.download = filename - a.click() - setTimeout(() => URL.revokeObjectURL(a.href), 1000) - } catch { - this.showMessage(t('err_save_failed_fmt', filename)) - } - } - - private setupFileDrop() { - window.addEventListener('dragover', (e) => { - if (e.dataTransfer?.types.includes('Files')) { - e.preventDefault() - this.root.classList.add('file-drag') - } - }) - window.addEventListener('dragleave', (e) => { - if (e.relatedTarget === null) this.root.classList.remove('file-drag') - }) - window.addEventListener('drop', async (e) => { - this.root.classList.remove('file-drag') - const f = e.dataTransfer?.files[0] - if (!f) return - e.preventDefault() - this.loadXML(await f.text(), f.name) - }) - } - - private onKeyDown(e: KeyboardEvent) { - const mod = e.metaKey || e.ctrlKey - // Auch der Code-Editor in „Code live“ (contenteditable): Löschen und ⌘Z gehören dort dem Text. - const typing = - e.target instanceof HTMLTextAreaElement || e.target instanceof HTMLInputElement || (e.target as HTMLElement | null)?.isContentEditable === true - if (mod && e.key.toLowerCase() === 's') { - e.preventDefault() - this.save() - } else if (mod && e.key.toLowerCase() === 'p') { - e.preventDefault() - if (!this.simulating) void this.exportDiagram('print') - } else if (mod && e.key.toLowerCase() === 'o') { - e.preventDefault() - this.open(this.root.querySelector('input[type=file]')!) - } else if (typing || this.simulating) { - return - } else if (mod && e.key.toLowerCase() === 'z') { - e.preventDefault() - if (e.shiftKey) this.redo() - else this.undo() - } else if (mod && e.key.toLowerCase() === 'c' && !getSelection()?.toString()) { - if (this.copySelection()) e.preventDefault() - } else if (mod && e.key.toLowerCase() === 'x') { - e.preventDefault() - this.cutSelection() - } else if (mod && e.key.toLowerCase() === 'v') { - e.preventDefault() - this.paste() - } else if (mod && e.key.toLowerCase() === 'y') { - e.preventDefault() - this.redo() - } else if (e.key === 'Enter' && this.selected && !this.blockMode && !mod) { - e.preventDefault() - this.startInlineEdit(this.selected) - } else if (e.key === 'F2' && this.selected) { - e.preventDefault() - this.renameVariable(this.selected) - } else if (e.key === 'Delete' || e.key === 'Backspace') { - e.preventDefault() - this.deleteSelection() - } else if (e.key === 'Escape') { - if (this.drag) this.endDrag() - else { - this.selected = undefined - this.render() - } - } - } - - // MARK: - Auswahl und Drag & Drop (Zeiger-Ereignisse: Maus, Stift und Finger gleich) - - private svgPoint(clientX: number, clientY: number): { x: number; y: number } | undefined { - if (!this.svg) return undefined - const r = this.svg.getBoundingClientRect() - const x = clientX - r.left - const y = clientY - r.top - if (x < 0 || y < 0 || x > r.width || y > r.height) return undefined - return { x: x / this.zoom, y: y / this.zoom } - } - - /** Tiefster Knoten unter dem Punkt (Kinder liegen über ihren Eltern). */ - private nodeAt(x: number, y: number): LayoutNode | undefined { - for (let i = this.flat.length - 1; i >= 0; i--) { - const f = this.flat[i].frame - if (x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + f.height) return this.flat[i] - } - return undefined - } - - private onCanvasPointerDown(e: PointerEvent) { - if (this.simulating) return - const p = this.svgPoint(e.clientX, e.clientY) - if (!p) return - // Offene Texteingabe abschließen (zählt dann als ein Rückgängig-Schritt). - this.inline?.finish(true) - if (document.activeElement instanceof HTMLElement) document.activeElement.blur() - const node = this.nodeAt(p.x, p.y) - const wasSelected = node && this.selected && samePath(node.idPath, this.selected) - // Doppelklick bzw. Doppeltipp auf dasselbe Element: Text direkt im Struktogramm bearbeiten. - const spot = node ? node.idPath.join(',') : '' - const now = Date.now() - const last = this.lastCanvasTap - const twice = !!node && e.button === 0 && spot === last.spot && now - last.time < 500 && Math.hypot(e.clientX - last.x, e.clientY - last.y) < 24 - this.lastCanvasTap = { spot, time: twice ? 0 : now, x: e.clientX, y: e.clientY } - this.selected = node?.idPath - this.render() - if (twice && node) { - e.preventDefault() - this.pendingInline = { pointerId: e.pointerId, path: node.idPath, point: p } - return - } - // Verschieben mit Maus/Stift; mit dem Finger würde es mit dem Scrollen kollidieren. - if (node && e.pointerType !== 'touch' && !(isPlaceholderSlot(this.doc, node.idPath) && !wasSelected)) { - this.beginDrag(e, { kind: 'move', path: node.idPath }) - } - } - - private beginDrag(e: PointerEvent, source: DragSource) { - if (e.button !== 0 || this.simulating) return - e.preventDefault() - this.drag = { source, pointerId: e.pointerId, startX: e.clientX, startY: e.clientY, active: false } - } - - private onPointerMove(e: PointerEvent) { - const d = this.drag - if (!d || e.pointerId !== d.pointerId) return - if (!d.active) { - if (Math.hypot(e.clientX - d.startX, e.clientY - d.startY) < 5) return - d.active = true - d.ghost = this.makeGhost(d.source) - document.body.append(d.ghost) - this.root.classList.add('dragging') - if (this.blockMode && d.source.kind === 'move') this.blocks?.setMoving(true) - } - d.ghost!.style.transform = `translate(${e.clientX + 12}px, ${e.clientY + 12}px)` - this.autoScroll(e.clientX, e.clientY) - if (this.blockMode) { - d.target = this.blockDropAt(e.clientX, e.clientY, d.source) - } else { - const p = this.svgPoint(e.clientX, e.clientY) - d.target = p ? this.dropTargetAt(p.x, p.y, d.source) : undefined - } - this.showDropTarget(d.target) - } - - private onPointerUp(e: PointerEvent) { - const pending = this.pendingInline - if (pending && pending.pointerId === e.pointerId) { - this.pendingInline = undefined - this.startInlineEdit(pending.path, pending.point) - return - } - const d = this.drag - if (!d || e.pointerId !== d.pointerId) return - if (d.active && d.target) this.applyDrop(d.source, d.target) - else if (!d.active && d.source.kind === 'palette') this.insertNearSelection(d.source.make()) - else if (!d.active && d.source.kind === 'sensor') this.insertSensor(d.source.text) - this.endDrag() - } - - private endDrag() { - this.drag?.ghost?.remove() - this.drag = undefined - this.root.classList.remove('dragging') - this.blocks?.setMoving(false) - this.showDropTarget(undefined) - } - - /** Blöcke: Ablegestelle unter dem Zeiger, in derselben Form wie im Struktogramm. */ - private blockDropAt(clientX: number, clientY: number, source: DragSource): DropTarget | undefined { - const drop = this.blocks?.dropAt(clientX, clientY, { - moving: source.kind === 'move' ? source.path : undefined, - // Sensoren auch auf Anweisungen, Operatoren und Variablen nur auf Bedingungen (oder genau auf ein Feld) - sensor: - source.kind === 'sensor' - ? (path) => { - const type = elementAt(this.doc, path).type - return /^mbot\./.test(source.text) ? takesSensor.includes(type) : type !== ElementType.anweisung && takesSensor.includes(type) - } - : undefined, - }) - if (!drop) return undefined - return { ...drop, frame: { x: 0, y: 0, width: 0, height: 0 } } - } - - private makeGhost(source: DragSource): HTMLElement { - const g = document.createElement('div') - g.className = 'drag-ghost' - if (source.kind === 'palette' || source.kind === 'sensor') { - g.textContent = source.label - if (source.kind === 'sensor' && this.blockMode) { - const sensor = /^mbot\./.test(source.text) - const bool = sensor ? /^mbot\.is/.test(source.text) : isBoolExpression(source.text) - g.classList.add('drag-sensor', ...(bool ? ['sensor-bool'] : []), ...(sensor ? [] : [/^[A-Za-z_]\w*$/.test(source.text) ? 'drag-var' : 'drag-op'])) - } - // in den Blöcken sieht auch das Ziehbild wie ein Block aus - if (source.kind === 'palette' && this.blockMode) { - g.classList.add('drag-block') - g.dataset.scope = scopeKind(source.make().type) ?? 'method' - } - } else { - const e = elementAt(this.doc, source.path) - g.textContent = `${typeName(e.type)}: ${canvasTextLines(e)[0]}` - } - return g - } - - private autoScroll(clientX: number, clientY: number) { - const host = this.blockMode && this.blocks ? this.blocks.element : this.canvasHost - const r = host.getBoundingClientRect() - const edge = 40 - const step = 12 - if (clientY > r.bottom - edge && clientY < r.bottom + edge) host.scrollTop += step - else if (clientY < r.top + edge && clientY > r.top - edge) host.scrollTop -= step - if (clientX > r.right - edge && clientX < r.right + edge) host.scrollLeft += step - else if (clientX < r.left + edge && clientX > r.left - edge) host.scrollLeft -= step - } - - private dropTargetAt(x: number, y: number, source: DragSource): DropTarget | undefined { - const roots = this.layout?.elements ?? [] - const node = this.nodeAt(x, y) - if (source.kind === 'sensor') { - // Nur auf ein Element mit Bedingung oder eine Anweisung, nicht dazwischen - if (!node || isPlaceholderSlot(this.doc, node.idPath)) return undefined - return takesSensor.includes(elementAt(this.doc, node.idPath).type) ? { kind: 'condition', path: node.idPath, frame: node.frame } : undefined - } - if (!node) { - const last = roots[roots.length - 1] - if (!last) return undefined - const bottom = last.frame.y + last.frame.height - if (y > bottom) { - if (source.kind === 'move' && source.path.length === 1 && source.path[0] === roots.length - 1) return undefined - return { kind: 'rootAppend', frame: { x: last.frame.x, y: bottom, width: last.frame.width, height: 0 } } - } - if (y < roots[0].frame.y) return { kind: 'before', path: [0], frame: roots[0].frame } - return undefined - } - const path = node.idPath - if (source.kind === 'move' && path.length >= source.path.length && source.path.every((v, i) => path[i] === v)) { - return undefined // nicht in sich selbst ablegen - } - if (isPlaceholderSlot(this.doc, path)) return { kind: 'replace', path, frame: node.frame } - const upper = y < node.frame.y + node.frame.height / 2 - return { kind: upper ? 'before' : 'after', path, frame: node.frame } - } - - private showDropTarget(t: DropTarget | undefined) { - if (this.blockMode) { - this.blocks?.showDrop(t as BlockDrop | undefined) - return - } - if (!this.overlay) return - this.overlay.replaceChildren() - if (!t) return - const f = t.frame - if (t.kind === 'replace' || t.kind === 'condition') { - this.overlay.append(el('rect', { x: f.x + 2, y: f.y + 2, width: f.width - 4, height: f.height - 4, class: 'drop-replace' })) - return - } - const y = t.kind === 'before' ? f.y : f.y + f.height - this.overlay.append(el('rect', { x: f.x, y: y - 3, width: f.width, height: 6, rx: 3, class: 'drop-line' })) - } - - private applyDrop(source: DragSource, t: DropTarget) { - const doc = this.doc - try { - if (source.kind === 'sensor') { - if (t.kind === 'condition') { - if (this.blockMode) this.dropOnCondition(source.text, t.path) - else this.insertSensor(source.text, t.path) - } - if (t.kind === 'slot') { - // Blöcke: Wert bzw. Sensor durch Sensor, Operator oder Variable ersetzen (`mbot.forward(mbot.getDistance())`) - const next = t.slot === 'value' ? replaceBlockValue(doc, t.path, t.index, source.text) : replaceBlockSensor(doc, t.path, t.index, source.text) - if (next) this.commit(setTextLines(doc, t.path, next), t.path) - } - return - } - if (t.kind === 'condition' || t.kind === 'slot') return - if (t.kind === 'trash') { - if (source.kind === 'move' && canDeleteElement(doc, source.path)) this.commit(deleteAtPath(doc, source.path)) - return - } - if (source.kind === 'palette') { - const made = source.make() - // Blöcke: neue for-Schleife zählt gleich von 0 bis unter 10 (Kinder tippen nur noch Zahlen) - const element = this.blockMode ? withCountLoopDefaults(doc, t.kind === 'rootAppend' ? [doc.elements.length] : t.path, made) : made - switch (t.kind) { - case 'replace': - return this.commit(replaceAtPath(doc, t.path, element), t.path) - case 'before': - return this.commit(insertBeforeSibling(doc, element, t.path), t.path) - case 'after': - return this.commit(insertAfterSibling(doc, element, t.path), [...t.path.slice(0, -1), t.path[t.path.length - 1] + 1]) - case 'rootAppend': - return this.commit(insertAt(doc, [], doc.elements.length, element), [doc.elements.length]) - } - } - const from = source.path - switch (t.kind) { - case 'replace': - return this.commit(moveReplacingPlaceholder(doc, from, t.path)) - case 'before': - return this.commit(moveBeforeSibling(doc, from, t.path)) - case 'after': - return this.commit(moveAfterSibling(doc, from, t.path)) - case 'rootAppend': { - const moving = elementAt(doc, from) - return this.commit(insertAt(deleteAtPath(doc, from), [], Number.MAX_SAFE_INTEGER, moving)) - } - } - } catch (err) { - console.warn('Ablegen nicht möglich', err) - } finally { - if (source.kind === 'move') { - this.selected = undefined - this.render() - } - } - } -} - -type ExportKind = 'png' | 'svg' | 'pdf' | 'print' | 'visustruct' - -/** Wie `DiagramCanvasView.displayTextLines`: Texte leerer Elemente in der gewählten Sprache. */ -export function applyCanvasStubTexts() { - setCanvasStubTexts({ - [ElementType.anweisung]: t('canvas_stub_anweisung'), - [ElementType.verzweigung]: t('canvas_stub_condition'), - [ElementType.fallauswahl]: t('canvas_stub_fallauswahl'), - [ElementType.forSchleife]: t('canvas_stub_for'), - [ElementType.whileSchleife]: t('canvas_stub_while'), - [ElementType.doUntilSchleife]: t('canvas_stub_while'), - [ElementType.endlosschleife]: t('canvas_stub_endless'), - [ElementType.aussprung]: t('canvas_stub_break'), - [ElementType.aufruf]: t('canvas_stub_call'), - }) -} - -/** Gleiche Namen für Aufruf und Reiter: ohne `()`, ohne Groß- und Kleinschreibung. */ -function subprogramKey(text: string): string { - return subprogramName(text).toLowerCase() -} - -function tabTitle(tab: Tab): string { - if (tab.fileName) return tab.fileName.replace(/\.(visustruct|strk)$/i, '') - return tab.doc.caption.trim() || t('tab_title_format', tab.serial) -} - -/** Breite des Lernbereichs: mindestens 260 px, dem Editor bleiben mindestens 420 px. */ -function clampPanelWidth(width: number): number { - return Math.round(Math.max(200, Math.min(width, window.innerWidth - 300))) -} - -function storedValue(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function storeValue(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } -} - -function escapeHTML(s: string): string { - return s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) -} - -/** Welten einer VisuBot-Übung (`welt:`), in der Reihenfolge der Tests. */ -function exerciseWorlds(id: string | undefined): RobotWorldSetup[] { - if (!id) return [] - for (const block of [...lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks)), ...tasks.flatMap((t) => t.blocks)]) { - if (block.kind !== 'exercise' || block.exercise.id !== id) continue - return block.exercise.tests.flatMap((test) => { - const setup = test.world ? world(test.world) : undefined - return setup ? [setup] : [] - }) - } - return [] -} diff --git a/web/src/ui/blockPicture.ts b/web/src/ui/blockPicture.ts deleted file mode 100644 index d733cead..00000000 --- a/web/src/ui/blockPicture.ts +++ /dev/null @@ -1,53 +0,0 @@ -import type { LiveLabels } from '../core/codeGenerator' -import { parseLiveJava } from '../core/javaParser' -import { documentFromJava } from '../core/liveSource' -import { BlockView, type BlockViewOptions } from './blockView' -import { t } from './i18n' - -/** Beschriftungen der Block-Ansicht (übersetzt), für den Editor und für Bilder in Lektionen. */ -export function blockViewLabels(): BlockViewOptions['labels'] { - return { - start: t('web_visubot_blocks_start'), - else: t('web_visubot_blocks_else'), - empty: t('web_visubot_blocks_empty'), - hint: t('web_visubot_blocks_hint'), - trash: t('web_visubot_blocks_trash'), - count: t('web_visubot_blocks_count'), - countFrom: t('web_visubot_blocks_count_from'), - countBelow: t('web_visubot_blocks_count_below'), - countTo: t('web_visubot_blocks_count_to'), - countAbove: t('web_visubot_blocks_count_above'), - countStep: t('web_visubot_blocks_count_step'), - repeat: t('web_visubot_blocks_repeat'), - repeatTimes: t('web_visubot_blocks_repeat_times'), - randomFrom: t('web_visubot_blocks_random_from'), - randomTo: t('web_visubot_blocks_random_to'), - keywords: { - if: t('web_visubot_blocks_if'), - while: t('web_visubot_blocks_while'), - endless: t('web_visubot_blocks_endless'), - do: t('web_visubot_blocks_do'), - '!': t('web_visubot_blocks_not'), - '&&': t('web_visubot_blocks_and'), - '||': t('web_visubot_blocks_or'), - break: t('web_visubot_blocks_break'), - }, - } -} - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - -/** - * Java-Schnipsel einer Lektion (z. B. eine Runde von „Wo landet der mBot2?“) als Blöcke wie im Editor, nur zum Ansehen. - * Für die Block-Lektionen (Holger 5.11.): Jüngere sehen dort Blöcke statt Java. `undefined`, wenn der Code nicht passt. - */ -export function blockPicture(java: string): HTMLElement | undefined { - const parsed = parseLiveJava(java, labels) - if (parsed.problems.length > 0 || parsed.elements.length === 0) return undefined - const doc = documentFromJava({ caption: '', elements: [] }, parsed) - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: { ...blockViewLabels(), hint: '' } }) - view.render(doc, undefined, true) - view.element.hidden = false - view.element.classList.add('bk-picture') - return view.element -} diff --git a/web/src/ui/blockView.ts b/web/src/ui/blockView.ts deleted file mode 100644 index b1a17b58..00000000 --- a/web/src/ui/blockView.ts +++ /dev/null @@ -1,765 +0,0 @@ -import { canvasTextLines, keywordSplit } from '../core/diagramText' -import { isPlaceholderElement } from '../core/editing' -import { ElementType, type VSDocument, type VSElement } from '../core/model' -import { samePath } from './renderer' -import { countLoopLines, countVar, declaredVariables, insertExpression, isRepeatLoop, javaTypes, randomJava, randomPattern, readCountLoop, readRandom, setCountField, usesVariable, type CountLoop } from 'visublocks' -import { visuBotGroupColor, visuBotGroupIcon, visuBotSensors, visuBotTemplates } from './visubotView' - -/** - * Blöcke (VisuBot): dasselbe Struktogramm als Scratch-artige Blöcke, Aussehen wie der Block-Modus von VisuMicro. - * Anweisungen sind Stapelblöcke, Schleifen und Verzweigungen Klammerblöcke („C-Blöcke“) mit Maul. - * Die Farben sind die des Struktogramms (Anweisung gelb, Schleife rosa, Verzweigung blau); mBot2-Befehle - * tragen zusätzlich den Streifen und das Symbol ihrer Gruppe wie in der Palette. - * Blöcke lassen sich auswählen, aus der Palette hineinziehen, verschieben und in den Papierkorb werfen; - * Zahlen und Texte (weiße Felder) ändert man per Klick direkt im Block. - */ - -/** Ein Wort auf einem Block: Text, Schlüsselwort (fett), Wert (weißes Feld) oder Sensor (türkiser Baustein). */ -export interface BlockToken { - kind: 'text' | 'kw' | 'lit' | 'str' | 'sensor' | 'var' | 'random' - text: string - /** Variable, die gerade angelegt oder gesetzt wird: Baustein, aber kein Feld */ - fixed?: boolean - /** Zufallszahl: von, bis */ - from?: string - to?: string -} - -const sensorCalls = new Set(visuBotSensors.map((s) => s.text.match(/^mbot\.(\w+)\(\)/)?.[1]).filter((n): n is string => !!n)) - -/** - * Stellen in einer Zeile, die im Block besonders aussehen: Zufallszahl, Zeichenkette, Sensor (bzw. andere - * `mbot.…()`), Zahl, true/false/null und angelegte Variablen. - */ -const scanPattern = () => - new RegExp( - `(${randomPattern().source})|("(?:[^"\\\\]|\\\\.)*")|(mbot\\.(\\w+)\\(\\))|(\\b\\d+(?:\\.\\d+)?\\b)|\\b(true|false|null)\\b|\\b([A-Za-z_]\\w*)\\b`, - 'g', - ) - -/** Eine besondere Stelle: `field` = man kann sie per Klick ändern oder etwas darauf ziehen */ -interface Hit { - start: number - end: number - kind: 'random' | 'str' | 'sensor' | 'lit' | 'var' - text: string - field: boolean -} - -/** mBot2-Befehle, deren Zahl man in der leeren Klammer ergänzt (aus „Elemente“ kommen sie als `mbot.forward()`) */ -const emptyArgCommands = new Set(['forward', 'backward', 'turnRight', 'turnLeft']) - -const typeBefore = new RegExp(`\\b(?:${javaTypes.join('|')})\\s+$`) - -function scan(line: string, vars: ReadonlySet): Hit[] { - const out: Hit[] = [] - for (const m of line.matchAll(scanPattern())) { - const start = m.index ?? 0 - const end = start + m[0].length - const hit = (kind: Hit['kind'], field = true) => out.push({ start, end, kind, text: m[0], field }) - if (m[1]) hit('random') - else if (m[4]) hit('str') - else if (m[5]) { - // andere mbot-Aufrufe (mbot.stop()) sind kein Sensor; Befehle mit leerer Klammer (`mbot.forward()`) - // bekommen dort ein leeres Feld, damit Kinder die Zahl nur noch eintippen - if (sensorCalls.has(m[6])) hit('sensor') - else if (emptyArgCommands.has(m[6])) out.push({ start: end - 1, end: end - 1, kind: 'lit', text: '', field: true }) - } else if (m[7] || m[8]) hit('lit') - else if (m[9] && vars.has(m[9]) && line[start - 1] !== '.') { - // Name, der gerade angelegt (`int zahl`) oder gesetzt (`zahl = …`) wird: Baustein, aber kein Feld - const target = typeBefore.test(line.slice(0, start)) || /^\s*=(?!=)/.test(line.slice(end)) - hit('var', !target) - } - } - return out -} - -/** Felder einer Zeile in Block-Reihenfolge: jede Zufallszahl hat zwei (von, bis) */ -interface ValueField { - hit: Hit - /** bei der Zufallszahl: 0 = von, 1 = bis */ - part?: 0 | 1 -} - -function valueFields(line: string, vars: ReadonlySet): ValueField[] { - const out: ValueField[] = [] - for (const hit of scan(line, vars)) { - if (!hit.field || hit.kind === 'sensor') continue - if (hit.kind === 'random') out.push({ hit, part: 0 }, { hit, part: 1 }) - else out.push({ hit }) - } - return out -} - -const noVars: ReadonlySet = new Set() - -/** - * Den `index`-ten Wert (Zahl, Text, true/false/null, Variable, von/bis einer Zufallszahl) in den Zeilen - * eines Elements ersetzen. Die Werte stehen in den Blöcken in derselben Reihenfolge wie im gespeicherten - * Text (auch bei `for` und `while`). Ein Operator bekommt Klammern, wenn er sie braucht. - */ -export function replaceValue(lines: string[], index: number, value: string, vars = noVars): string[] | undefined { - let n = 0 - for (let l = 0; l < lines.length; l++) { - for (const f of valueFields(lines[l], vars)) { - if (n++ !== index) continue - const out = [...lines] - if (f.part !== undefined) { - // Zufallszahl: nur ganze Zahlen als Grenzen - const r = readRandom(f.hit.text) - if (!r || !/^-?\d+$/.test(value.trim())) return undefined - const v = Number(value) - out[l] = lines[l].slice(0, f.hit.start) + randomJava(f.part === 0 ? v : r.from, f.part === 1 ? v : r.to) + lines[l].slice(f.hit.end) - } else out[l] = insertExpression(lines[l], f.hit.start, f.hit.end, value) - return out - } - } - return undefined -} - -/** Den `index`-ten Sensor (`mbot.getDistance()` …) in den Zeilen eines Elements ersetzen. */ -export function replaceSensor(lines: string[], index: number, value: string, vars = noVars): string[] | undefined { - let n = 0 - for (let l = 0; l < lines.length; l++) { - for (const hit of scan(lines[l], vars)) { - if (hit.kind !== 'sensor' || n++ !== index) continue - const out = [...lines] - out[l] = insertExpression(lines[l], hit.start, hit.end, value) - return out - } - } - return undefined -} - -/** Alle Zeilen des Programms (für die angelegten Variablen) */ -function allLines(els: VSElement[], out: string[] = []): string[] { - for (const e of els) { - out.push(...e.textLines) - if (e.loopBody) allLines(e.loopBody, out) - for (const c of e.cases ?? []) allLines(c.elements, out) - } - return out -} - -/** Variablen, die in Anweisungen angelegt werden (ohne Zählvariablen von Schleifen) */ -export function statementVariables(doc: VSDocument): string[] { - const lines: string[] = [] - const walk = (els: VSElement[]) => { - for (const e of els) { - if (e.type === ElementType.anweisung) lines.push(...e.textLines) - if (e.loopBody) walk(e.loopBody) - for (const c of e.cases ?? []) walk(c.elements) - } - } - walk(doc.elements) - return declaredVariables(lines) -} - -/** Namen der angelegten Variablen im ganzen Programm (`int zahl = 0`, auch Zählvariablen) */ -export const programVariables = (doc: VSDocument): string[] => declaredVariables(allLines(doc.elements)) - -/** Element am Pfad und Zahl der umgebenden for-Schleifen (für den Namen der Zählvariable: i, j, k …) */ -export function elementWithForDepth(doc: VSDocument, path: number[]): { element?: VSElement; depth: number } { - let els = doc.elements - let depth = 0 - for (let k = 0; k < path.length; k += 2) { - const e = els[path[k]] - if (!e || k === path.length - 1) return { element: e, depth } - if (e.type === ElementType.forSchleife) depth++ - const next = e.loopBody && path[k + 1] === 0 ? e.loopBody : e.cases?.[path[k + 1]]?.elements - if (!next) return { depth } - els = next - } - return { depth } -} - -/** Zählschleife eines for-Elements (leere Schleife → Standard mit passender Variable), sonst `undefined` */ -export function countLoopOf(element: VSElement, depth: number): CountLoop | undefined { - return element.type === ElementType.forSchleife ? readCountLoop(element.textLines, countVar(depth)) : undefined -} - -/** - * Wert Nummer `index` im Block am Pfad ersetzen. Zählschleifen haben die Felder Start, Ende, Schritt und - * schreiben den ganzen Java-Kopf neu (`0` beim Start → `int i = 0`); sonst wie `replaceValue`. - */ -export function replaceBlockValue(doc: VSDocument, path: number[], index: number, value: string): string[] | undefined { - const { element, depth } = elementWithForDepth(doc, path) - if (!element) return undefined - const loop = countLoopOf(element, depth) - if (loop) { - const next = setCountField(loop, index, value) - return next && countLoopLines(next) - } - return replaceValue(element.textLines, index, value, new Set(programVariables(doc))) -} - -/** Sensor Nummer `index` im Block am Pfad ersetzen (auch durch einen Operator oder eine Variable) */ -export function replaceBlockSensor(doc: VSDocument, path: number[], index: number, value: string): string[] | undefined { - const { element } = elementWithForDepth(doc, path) - return element && replaceSensor(element.textLines, index, value, new Set(programVariables(doc))) -} - -/** Neue for-Schleife aus der Palette in den Blöcken: gleich „von 0 bis unter 10 in Schritten von 1“ */ -export function withCountLoopDefaults(doc: VSDocument, path: number[], element: VSElement): VSElement { - if (element.type !== ElementType.forSchleife || element.textLines.some((l) => l.trim() !== '')) return element - const { depth } = elementWithForDepth(doc, [...path.slice(0, -1), Number.MAX_SAFE_INTEGER]) - return { ...element, textLines: countLoopLines(readCountLoop([], countVar(depth))!) } -} - -/** Eingabe im Wertfeld → Java-Text: Zahl bzw. true/false/null bleibt, Text kommt in Anführungszeichen. */ -export function valueText(kind: 'lit' | 'str', input: string): string | undefined { - if (kind === 'str') return JSON.stringify(input) - const v = input.trim().replace(',', '.') - return /^-?\d+(\.\d+)?$/.test(v) || /^(true|false|null)$/.test(v) ? v : undefined -} - -/** Zeile → Wörter: führendes Schlüsselwort, Zeichenketten, Zahlen, Sensoren, Variablen und Zufallszahlen. */ -export function blockTokens(line: string, vars = noVars): BlockToken[] { - const out: BlockToken[] = [] - const split = keywordSplit(line) - let rest = line - if (split) { - out.push({ kind: 'kw', text: split.keyword }) - rest = split.rest ? ` ${split.rest}` : '' - } - let at = 0 - const text = (s: string) => { - if (!s) return - const last = out[out.length - 1] - if (last?.kind === 'text') last.text += s - else out.push({ kind: 'text', text: s }) - } - for (const h of scan(rest, vars)) { - text(rest.slice(at, h.start)) - if (h.kind === 'random') { - const r = readRandom(h.text)! - out.push({ kind: 'random', text: h.text, from: String(r.from), to: String(r.to) }) - } else out.push(h.kind === 'var' && !h.field ? { kind: 'var', text: h.text, fixed: true } : { kind: h.kind, text: h.text }) - at = h.end - } - text(rest.slice(at)) - return out -} - -const commandGroups = new Map() -for (const tpl of visuBotTemplates) { - const name = tpl.make().textLines[0]?.match(/^mbot\.(\w+)/)?.[1] - if (name && !commandGroups.has(name)) commandGroups.set(name, tpl.group) -} - -/** Gruppe eines mBot2-Befehls (`mbot.forward(50)` → `drive`), sonst `undefined`. */ -export function blockGroup(element: VSElement): string | undefined { - if (element.type !== ElementType.anweisung) return undefined - const name = element.textLines.join(' ').trim().match(/^mbot\.(\w+)/)?.[1] - return name ? commandGroups.get(name) : undefined -} - -/** Pfad des Elements und laufende Nummer seiner Werte */ -interface ValueSlot { - path: number[] - n: number - /** laufende Nummer der Sensoren */ - s: number -} - -const iconPrefix = new RegExp(`^(?:${Object.values(visuBotGroupIcon).map((i) => i.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|')})\\s+(?=mbot\\.)`, 'u') - -/** - * Bis 0.15.19 schrieben die Blöcke das Gruppensymbol versehentlich in den Befehl (`🚗 mbot.forward(50)`). - * Entfernt es aus allen Anweisungen; `undefined`, wenn nichts zu tun ist. - */ -export function withoutBlockIcons(doc: VSDocument): VSDocument | undefined { - let changed = false - const clean = (els: VSElement[]): VSElement[] => - els.map((e) => { - const textLines = e.type === ElementType.anweisung ? e.textLines.map((l) => l.replace(iconPrefix, '')) : e.textLines - if (textLines.some((l, i) => l !== e.textLines[i])) changed = true - return { - ...e, - textLines, - loopBody: e.loopBody && clean(e.loopBody), - cases: e.cases?.map((c) => ({ ...c, elements: clean(c.elements) })), - } - }) - const elements = clean(doc.elements) - return changed ? { ...doc, elements } : undefined -} - -export interface BlockViewOptions { - /** Zeiger auf einem Block: auswählen und ggf. Ziehen beginnen. */ - press: (path: number[], event: PointerEvent) => void - /** Wert Nummer `index` im Element `path` ändern (`text` ist schon Java: `50`, `"Hallo"`). */ - editValue: (path: number[], index: number, text: string) => void - /** Texte der Knöpfe/Beschriftungen (übersetzt). */ - labels: { - start: string - else: string - empty: string - hint: string - trash: string - /** Zählschleife: „zähle mit“ i „von“ 0 „bis unter“/„bis“/„bis über“ 10 „in Schritten von“ 1 */ - count: string - countFrom: string - countBelow: string - countTo: string - countAbove: string - countStep: string - /** „wiederhole“ [10] „mal“ */ - repeat: string - repeatTimes: string - /** „Zufallszahl von“ [1] „bis“ [10] */ - randomFrom: string - randomTo: string - /** Schlüsselwörter im Block übersetzt: `if` → „wenn“, `while` → „solange“, `endless` → „wiederhole immer“, `do` → „mache“. */ - keywords?: Record - } -} - -/** - * Wohin ein gezogener Block fällt: vor/hinter einen Block, an die Stelle eines leeren Platzes, ans Ende, - * in die Bedingung (Sensor) oder in den Papierkorb (nur beim Verschieben). - */ -export type BlockDrop = - | { kind: 'before' | 'after' | 'replace' | 'condition'; path: number[] } - /** Sensor auf ein Feld: Wert bzw. Sensor Nummer `index` im Element `path` ersetzen */ - | { kind: 'slot'; path: number[]; slot: 'value' | 'sensor'; index: number } - | { kind: 'rootAppend' } - | { kind: 'trash' } - -export interface BlockDropQuery { - /** Verschieben: dieser Block (er kann nicht in sich selbst fallen) */ - moving?: number[] - /** Sensor: passt er in diesen Block? (Bedingung oder Anweisung) */ - sensor?: (path: number[]) => boolean -} - -const pathOf = (el: Element | null | undefined): number[] | undefined => - el instanceof HTMLElement && el.dataset.path ? (JSON.parse(el.dataset.path) as number[]) : undefined -const startsWith = (path: number[], prefix: number[]) => path.length >= prefix.length && prefix.every((v, i) => path[i] === v) -const upperHalf = (el: Element, clientY: number) => { - const r = el.getBoundingClientRect() - return clientY < r.top + r.height / 2 -} - -export class BlockView { - readonly element: HTMLDivElement - private boxes: { path: number[]; el: HTMLElement }[] = [] - private enabled = true - /** gerade offenes Wertfeld: solange wird nicht neu gezeichnet */ - private editing = false - /** Schachtelung der for-Schleifen beim Zeichnen (Zählvariable i, j, k …) */ - private forDepth = 0 - /** angelegte Variablen des Programms (orange Bausteine) */ - private vars: ReadonlySet = new Set() - private pending?: () => void - - constructor(private options: BlockViewOptions) { - this.element = document.createElement('div') - this.element.className = 'blocks-view' - this.element.hidden = true - this.element.addEventListener('pointerdown', (e) => { - if (!this.enabled) return - if ((e.target as Element).closest('.bk-input')) return - const field = (e.target as Element).closest('[data-value]') - if (field && !this.editing) { - // Wert anklicken: direkt im Block ändern, nicht ziehen - e.preventDefault() - e.stopPropagation() - this.editField(field) - return - } - const box = (e.target as Element).closest('[data-path]') - if (!box) return - this.options.press(pathOf(box)!, e) - }) - } - - /** Ablegestelle unter dem Zeiger (`target` = Element unter dem Zeiger, sonst `elementFromPoint`). */ - dropAt(clientX: number, clientY: number, query: BlockDropQuery, target?: Element | null): BlockDrop | undefined { - const hit = target ?? document.elementFromPoint(clientX, clientY) - if (!hit || !this.element.contains(hit)) return undefined - if (hit.closest('.bk-trash')) return query.moving ? { kind: 'trash' } : undefined - const drop = this.rawDrop(hit, clientY, query) - if (!drop || !query.moving || !('path' in drop)) return drop - // nicht in sich selbst ablegen - return startsWith(drop.path, query.moving) ? undefined : drop - } - - private rawDrop(hit: Element, clientY: number, query: BlockDropQuery): BlockDrop | undefined { - const box = hit.closest('[data-path]') - if (query.sensor) { - // genau auf ein weißes Feld oder einen Sensor: diesen ersetzen - const field = hit.closest('[data-value], [data-sensor]') - if (field) { - const slot = field.dataset.value ? 'value' : 'sensor' - const { path, index } = JSON.parse(field.dataset.value ?? field.dataset.sensor!) as { path: number[]; index: number } - return { kind: 'slot', path, slot, index } - } - const path = pathOf(box) - return path && !box!.classList.contains('bk-empty') && query.sensor(path) ? { kind: 'condition', path } : undefined - } - if (!box) { - if (hit.closest('.bk-hat')) return { kind: 'before', path: [0] } - return hit.closest('.bk-stack, .blocks-view') ? { kind: 'rootAppend' } : undefined - } - const path = pathOf(box)! - if (box.classList.contains('bk-empty')) return { kind: 'replace', path } - const part = hit.closest('.bk-head, .bk-foot, .bk-clause, .bk-body') - const own = part && part.closest('[data-path]') === box ? part : undefined - if (!box.classList.contains('bk-c') || !own) return { kind: upperHalf(box, clientY) ? 'before' : 'after', path } - if (own.classList.contains('bk-clause')) return this.firstInto(own.nextElementSibling) - if (own.classList.contains('bk-body')) { - // freie Fläche im Maul: hinter den letzten Block darin - const last = [...own.children].filter((c) => c instanceof HTMLElement && c.dataset.path).pop() - const lastPath = pathOf(last) - if (!lastPath) return undefined - return last!.classList.contains('bk-empty') ? { kind: 'replace', path: lastPath } : { kind: 'after', path: lastPath } - } - if (own.classList.contains('bk-foot')) return { kind: 'after', path } - // Kopf: obere Hälfte davor, untere Hälfte als ersten Block ins Maul - return upperHalf(own, clientY) ? { kind: 'before', path } : this.firstInto(own.parentElement?.querySelector(':scope > .bk-body')) - } - - private firstInto(body: Element | null | undefined): BlockDrop | undefined { - const first = body?.firstElementChild - const path = pathOf(first) - if (!path) return undefined - return first!.classList.contains('bk-empty') ? { kind: 'replace', path } : { kind: 'before', path } - } - - /** Wertfeld durch ein Eingabefeld ersetzen; Enter oder Verlassen übernimmt, Escape bricht ab. */ - private editField(field: HTMLElement) { - const { path, index } = JSON.parse(field.dataset.value!) as { path: number[]; index: number } - const kind = field.classList.contains('tk-str') ? 'str' : 'lit' - const shown = field.textContent ?? '' - const input = document.createElement('input') - input.className = `bk-input tk-${kind}` - input.value = kind === 'str' ? (JSON.parse(shown) as string) : shown - const fit = () => (input.style.width = `calc(${Math.max(2, input.value.length + 1)}ch + 18px)`) - fit() - input.setAttribute('aria-label', shown) - input.addEventListener('input', fit) - this.editing = true - let done = false - const finish = (save: boolean) => { - if (done) return - done = true - this.editing = false - const text = save ? valueText(kind, input.value) : undefined - if (text !== undefined && text !== shown) this.options.editValue(path, index, text) - else if (this.pending) this.pending() - else input.replaceWith(field) - } - input.addEventListener('keydown', (e) => { - e.stopPropagation() - if (e.key === 'Enter') finish(true) - else if (e.key === 'Escape') finish(false) - }) - input.addEventListener('blur', () => finish(true)) - field.replaceWith(input) - input.focus() - input.select() - } - - /** Ablegestelle zeigen (Linie bzw. hervorgehobener Block); `undefined` räumt auf. */ - showDrop(drop: BlockDrop | undefined) { - this.element.querySelectorAll('.bk-drop').forEach((e) => e.classList.remove('bk-drop')) - this.element.querySelector('.bk-drop-line')?.remove() - this.element.querySelector('.bk-trash')?.classList.toggle('bk-trash-hot', drop?.kind === 'trash') - if (!drop || drop.kind === 'trash') return - if (drop.kind === 'slot') { - const key = JSON.stringify({ path: drop.path, index: drop.index }) - const attr = drop.slot === 'value' ? 'data-value' : 'data-sensor' - for (const f of this.element.querySelectorAll(`[${attr}]`)) { - if (f.getAttribute(attr) === key) f.classList.add('bk-drop') - } - return - } - const stack = this.element.querySelector('.bk-stack') - if (!stack) return - let box: HTMLElement | undefined - let below = true - if (drop.kind === 'rootAppend') { - box = [...stack.children].filter((c): c is HTMLElement => c instanceof HTMLElement && !!c.dataset.path).pop() - } else { - box = this.boxes.find((b) => samePath(b.path, drop.path))?.el - if (box && (drop.kind === 'replace' || drop.kind === 'condition')) { - box.classList.add('bk-drop') - return - } - below = drop.kind === 'after' - } - if (!box) return - const s = stack.getBoundingClientRect() - const r = box.getBoundingClientRect() - const line = document.createElement('div') - line.className = 'bk-drop-line' - line.style.left = `${r.left - s.left}px` - line.style.top = `${(below ? r.bottom : r.top) - s.top - 3}px` - line.style.width = `${Math.max(r.width, 120)}px` - stack.append(line) - } - - /** Beim Verschieben eines Blocks: Papierkorb zeigen. */ - setMoving(on: boolean) { - this.element.classList.toggle('bk-moving', on) - } - - /** Neu zeichnen; `mark` ist die Auswahl bzw. in der Simulation der laufende Schritt. */ - render(doc: VSDocument, mark: number[] | undefined, simulating: boolean, zoom = 1) { - if (this.editing) { - this.pending = () => this.render(doc, mark, simulating, zoom) - return - } - this.pending = undefined - this.enabled = !simulating - this.boxes = [] - this.forDepth = 0 - this.vars = new Set(programVariables(doc)) - this.element.style.setProperty('--bk-zoom', String(zoom)) - this.element.classList.toggle('bk-simulating', simulating) - const hint = document.createElement('p') - hint.className = 'bk-hint' - hint.textContent = this.options.labels.hint - const hat = document.createElement('div') - hat.className = 'bk bk-hat' - const head = document.createElement('div') - head.className = 'bk-head' - head.append(this.word({ kind: 'kw', text: `🏁 ${this.options.labels.start}` })) - hat.append(head) - const stack = document.createElement('div') - stack.className = 'bk-stack' - stack.append(hat, ...this.list(doc.elements, [])) - const trash = document.createElement('div') - trash.className = 'bk-trash' - trash.textContent = `🗑️ ${this.options.labels.trash}` - this.element.replaceChildren(hint, stack, trash) - for (const { path, el } of this.boxes) { - if (mark && samePath(path, mark)) el.classList.add(simulating ? 'bk-exec' : 'bk-sel') - } - } - - /** Den markierten Block sichtbar machen (Simulation). */ - scrollTo(path: number[]) { - const box = this.boxes.find((b) => samePath(b.path, path)) - box?.el.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - } - - private list(elements: VSElement[], parent: number[]): HTMLElement[] { - return elements.map((e, i) => this.block(e, [...parent, i])) - } - - private block(e: VSElement, path: number[]): HTMLElement { - const box = document.createElement('div') - // Werte im Block zählen wie im gespeicherten Text (für das Ändern per Klick) - const v: ValueSlot = { path, n: 0, s: 0 } - box.dataset.path = JSON.stringify(path) - this.boxes.push({ path, el: box }) - if (isPlaceholderElement(e)) { - box.className = 'bk-empty' - box.textContent = this.options.labels.empty - return box - } - // Kopie: das Symbol der Gruppe gehört nur auf den Block, nie in den gespeicherten Befehl - const lines = [...canvasTextLines(e)] - switch (e.type) { - case ElementType.forSchleife: { - box.className = 'bk bk-c bk-loop' - const loop = countLoopOf(e, this.forDepth) - const repeat = loop && isRepeatLoop(loop) && !usesVariable(allLines(e.loopBody ?? []), loop.v) - const head = loop ? (repeat ? this.repeatHead(loop, v) : this.countHead(loop, v)) : this.head(lines, v) - this.forDepth++ - const body = this.body(e.loopBody ?? [], [...path, 0]) - this.forDepth-- - box.append(head, body, this.foot([], v)) - break - } - case ElementType.whileSchleife: - case ElementType.endlosschleife: { - box.className = 'bk bk-c bk-loop' - // Endlosschleife: Kopf nur das Schlüsselwort („wiederhole immer“) - const head = e.type === ElementType.endlosschleife ? this.keywordHead('endless') : this.head(lines, v) - box.append(head, this.body(e.loopBody ?? [], [...path, 0]), this.foot([], v)) - break - } - case ElementType.doUntilSchleife: - // Bedingung erst am Ende geprüft: oben „do“, unten „while …“ wie im Struktogramm - box.className = 'bk bk-c bk-loop' - box.append(this.head(['do'], v), this.body(e.loopBody ?? [], [...path, 0]), this.foot(lines, v)) - break - case ElementType.verzweigung: { - box.className = 'bk bk-c bk-if' - const cases = e.cases ?? [] - box.append(this.head(lines, v)) - cases.forEach((c, i) => { - if (i > 0) box.append(this.clause(i === 1 && cases.length === 2 ? this.options.labels.else : c.name)) - box.append(this.body(c.elements, [...path, i])) - }) - box.append(this.foot([], v)) - break - } - case ElementType.fallauswahl: - box.className = 'bk bk-c bk-if' - box.append(this.head(lines, v)) - ;(e.cases ?? []).forEach((c, i) => { - box.append(this.clause(c.name.toLowerCase() === 'default' ? 'default' : `case ${c.name}`), this.body(c.elements, [...path, i])) - }) - box.append(this.foot([], v)) - break - case ElementType.aussprung: - box.className = 'bk bk-cap' - box.append(this.head(lines, v)) - break - case ElementType.aufruf: - box.className = 'bk bk-simple bk-call' - box.append(this.head(lines, v)) - break - default: { - box.className = 'bk bk-simple bk-stmt' - const group = blockGroup(e) - if (group) { - box.dataset.botColor = visuBotGroupColor(group) - const icon = visuBotGroupIcon[group] - if (icon) lines[0] = `${icon} ${lines[0]}` - } - box.append(this.head(lines, v)) - } - } - return box - } - - private head(lines: string[], v?: ValueSlot): HTMLElement { - const head = document.createElement('div') - head.className = 'bk-head' - for (const line of lines) { - const row = document.createElement('span') - row.className = 'bk-line' - for (const token of blockTokens(line, this.vars)) { - if (token.kind === 'random') { - row.append(this.randomWord(token, v)) - continue - } - // Schlüsselwörter in der Sprache der Oberfläche (Holger 5.11.: für Jüngere „wenn“ statt „if“) - const word = this.word(this.translated(token)) - if (v && (token.kind === 'lit' || token.kind === 'str' || (token.kind === 'var' && !token.fixed))) { - word.dataset.value = JSON.stringify({ path: v.path, index: v.n++ }) - } - if (v && token.kind === 'sensor') word.dataset.sensor = JSON.stringify({ path: v.path, index: v.s++ }) - // Ja/Nein-Sensoren (isButtonA, isLineLeft …) sechseckig wie Bedingungen in Scratch, Messwerte rund - if (token.kind === 'sensor' && /^mbot\.is/.test(token.text)) word.classList.add('tk-bool') - row.append(word) - } - head.append(row) - } - return head - } - - /** Zufallszahl als runder Operator-Baustein: „Zufallszahl von [1] bis [10]“ mit zwei Feldern */ - private randomWord(token: BlockToken, v?: ValueSlot): HTMLElement { - const l = this.options.labels - const pill = document.createElement('span') - pill.className = 'tk tk-op' - const field = (text: string) => { - const w = this.word({ kind: 'lit', text }) - if (v) w.dataset.value = JSON.stringify({ path: v.path, index: v.n++ }) - return w - } - pill.append(`${l.randomFrom} `, field(token.from!), ` ${l.randomTo} `, field(token.to!)) - return pill - } - - /** Feld des Kopfs einer Zählschleife: Zahl, Sensor oder Variable */ - private loopField(text: string, index: number, v: ValueSlot): HTMLElement { - const sensor = /^mbot\./.test(text) - const w = this.word({ kind: sensor ? 'sensor' : /^[A-Za-z_]/.test(text) ? 'var' : 'lit', text }) - if (sensor) w.dataset.sensor = JSON.stringify({ path: v.path, index: 0 }) - else w.dataset.value = JSON.stringify({ path: v.path, index }) - return w - } - - /** „wiederhole [10] mal“: Zählschleife, deren Variable im Rumpf nicht vorkommt; Feld 1 = Ende */ - private repeatHead(c: CountLoop, v: ValueSlot): HTMLElement { - const l = this.options.labels - const head = document.createElement('div') - head.className = 'bk-head' - const row = document.createElement('span') - row.className = 'bk-line' - row.append(this.word({ kind: 'kw', text: l.repeat }), this.word({ kind: 'text', text: ' ' }), this.loopField(c.end, 1, v), this.word({ kind: 'text', text: ` ${l.repeatTimes}` })) - head.append(row) - return head - } - - /** Zählschleife: „zähle mit i von [0] bis unter [10] in Schritten von [1]“, Felder 0 Start, 1 Ende, 2 Schritt */ - private countHead(c: CountLoop, v: ValueSlot): HTMLElement { - const l = this.options.labels - const head = document.createElement('div') - head.className = 'bk-head' - const row = document.createElement('span') - row.className = 'bk-line' - const field = (text: string, index: number) => this.loopField(text, index, v) - const to = c.op === '<' ? l.countBelow : c.op === '>' ? l.countAbove : l.countTo - row.append( - this.word({ kind: 'kw', text: l.count }), - this.word({ kind: 'text', text: ' ' }), - this.word({ kind: 'var', text: c.v }), - this.word({ kind: 'text', text: ` ${l.countFrom} ` }), - field(c.start, 0), - this.word({ kind: 'text', text: ` ${to} ` }), - field(c.end, 1), - this.word({ kind: 'text', text: ` ${l.countStep} ` }), - field(c.step, 2), - ) - head.append(row) - return head - } - - private foot(lines: string[], v?: ValueSlot): HTMLElement { - const foot = this.head(lines, v) - foot.className = 'bk-foot' - return foot - } - - /** Schlüsselwörter und `!`, `&&`, `||` in der Sprache der Oberfläche („nicht“, „und“, „oder“), falls übersetzt. */ - private translated(token: BlockToken): BlockToken { - const words = this.options.labels.keywords - if (!words) return token - if (token.kind === 'kw') return { ...token, text: words[token.text] ?? token.text } - if (token.kind !== 'text') return token - // Aussprung: „break“ als ganzer Satz („verlasse die Schleife“) - if (token.text.trim() === 'break' && words.break) return { kind: 'kw', text: words.break } - const text = token.text - .replace(/&&/g, words['&&'] ? ` ${words['&&']} ` : '&&') - .replace(/\|\|/g, words['||'] ? ` ${words['||']} ` : '||') - .replace(/!(?!=)/g, words['!'] ? `${words['!']} ` : '!') - .replace(/ {2,}/g, ' ') - return { ...token, text } - } - - private keywordHead(keyword: string): HTMLElement { - const head = document.createElement('div') - head.className = 'bk-head' - head.append(this.word({ kind: 'kw', text: this.options.labels.keywords?.[keyword] ?? keyword })) - return head - } - - private clause(label: string): HTMLElement { - const c = document.createElement('div') - c.className = 'bk-head bk-clause' - c.append(this.word({ kind: 'kw', text: label })) - return c - } - - private body(elements: VSElement[], parent: number[]): HTMLElement { - const body = document.createElement('div') - body.className = 'bk-body' - body.append(...this.list(elements, parent)) - return body - } - - private word(token: BlockToken): HTMLElement { - const span = document.createElement('span') - span.className = `tk tk-${token.kind}` - span.textContent = token.text - return span - } -} diff --git a/web/src/ui/caesarDisc.ts b/web/src/ui/caesarDisc.ts deleted file mode 100644 index 66148825..00000000 --- a/web/src/ui/caesarDisc.ts +++ /dev/null @@ -1,185 +0,0 @@ -import { t } from './i18n' - -/** Digitale Caesar-Scheibe im Lernbereich: innere Scheibe drehen, Text live ver- und entschlüsseln. */ - -const letters = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ') -const step = 360 / letters.length -const svgNS = 'http://www.w3.org/2000/svg' - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function s(tag: string, attrs: Record = {}, text?: string): SVGElement { - const e = document.createElementNS(svgNS, tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, String(v)) - if (text !== undefined) e.textContent = text - return e -} - -/** Verschiebt Buchstaben A–Z (groß und klein) um `key` Stellen, alles andere bleibt. */ -export function caesar(text: string, key: number): string { - const k = ((key % 26) + 26) % 26 - return Array.from(text) - .map((c) => { - const code = c.charCodeAt(0) - if (code >= 65 && code <= 90) return String.fromCharCode(((code - 65 + k) % 26) + 65) - if (code >= 97 && code <= 122) return String.fromCharCode(((code - 97 + k) % 26) + 97) - return c - }) - .join('') -} - -/** Ein Ring mit 26 Buchstaben auf dem Radius `r`, Buchstabe 0 oben. */ -function ring(r: number, cls: string): { group: SVGElement; cells: SVGElement[] } { - const group = s('g', { class: cls }) - const cells = letters.map((letter, i) => { - const cell = s('g', { transform: `rotate(${i * step})` }) - cell.append(s('text', { x: 0, y: -r, 'text-anchor': 'middle', 'dominant-baseline': 'central' }, letter)) - group.append(cell) - return cell - }) - return { group, cells } -} - -export function renderCaesarDisc(startKey: number, startText: string): HTMLElement { - let key = startKey - let decrypt = false - let rotation = -key * step - - const box = h('section', { class: 'learn-game learn-caesar' }) - box.append(h('div', { class: 'learn-game-head' }, h('strong', {}, t('web_learn_caesar_title')))) - box.append(h('p', { class: 'learn-game-status' }, t('web_learn_caesar_hint'))) - - const svg = s('svg', { viewBox: '-200 -200 400 400', class: 'learn-caesar-disc', role: 'img' }) as SVGSVGElement - svg.append(s('circle', { r: 196, class: 'outer-disc' })) - // Sichtfenster oben: Dieses Buchstabenpaar gehört zusammen - svg.append(s('path', { d: arcWedge(196, 92, step / 2), class: 'window' })) - const outer = ring(168, 'outer-letters') - svg.append(outer.group) - for (let i = 0; i < letters.length; i++) { - svg.append(s('line', { x1: 0, y1: -144, x2: 0, y2: -196, transform: `rotate(${(i + 0.5) * step})`, class: 'tick' })) - } - const inner = s('g', { class: 'inner' }) - inner.append(s('circle', { r: 142, class: 'inner-disc' })) - const innerRing = ring(118, 'inner-letters') - inner.append(innerRing.group) - for (let i = 0; i < letters.length; i++) { - inner.append(s('line', { x1: 0, y1: -94, x2: 0, y2: -142, transform: `rotate(${(i + 0.5) * step})`, class: 'tick' })) - } - inner.append(s('circle', { r: 94, class: 'hub' })) - svg.append(inner) - const keyText = s('text', { x: 0, y: 6, 'text-anchor': 'middle', 'dominant-baseline': 'central', class: 'key' }) - svg.append(keyText) - svg.append(s('text', { x: 0, y: -30, 'text-anchor': 'middle', class: 'key-label' }, 'k')) - - const minus = h('button', { type: 'button', 'aria-label': t('web_learn_caesar_back') }, '−1') - const plus = h('button', { type: 'button', 'aria-label': t('web_learn_caesar_forward') }, '+1') - const controls = h('div', { class: 'learn-caesar-controls' }, minus, plus) - - const mode = h('div', { class: 'learn-caesar-mode', role: 'group' }) - const encButton = h('button', { type: 'button' }, t('web_learn_caesar_encrypt')) - const decButton = h('button', { type: 'button' }, t('web_learn_caesar_decrypt')) - mode.append(encButton, decButton) - - const inputLabel = h('span') - const input = h('input', { type: 'text', autocomplete: 'off', autocapitalize: 'characters', spellcheck: 'false' }) as HTMLInputElement - input.value = startText || 'LUDWIG' - const outputLabel = h('span') - const output = h('output', { class: 'learn-caesar-output' }) - const fields = h('div', { class: 'learn-caesar-fields' }, h('label', {}, inputLabel, input), h('div', {}, outputLabel, output)) - - box.append(h('div', { class: 'learn-caesar-stage' }, svg, controls), mode, fields) - - const mark = (index?: number) => { - outer.cells.forEach((c, i) => c.classList.toggle('marked', i === index)) - innerRing.cells.forEach((c, i) => c.classList.toggle('marked', index !== undefined && i === (index + key) % 26)) - } - - const update = () => { - keyText.textContent = String(key) - inner.setAttribute('transform', `rotate(${rotation})`) - svg.setAttribute('aria-label', t('web_learn_caesar_key_fmt', key)) - inputLabel.textContent = t(decrypt ? 'web_learn_caesar_secret' : 'web_learn_caesar_plain') - outputLabel.textContent = t(decrypt ? 'web_learn_caesar_plain' : 'web_learn_caesar_secret') - encButton.classList.toggle('active', !decrypt) - decButton.classList.toggle('active', decrypt) - output.textContent = caesar(input.value, decrypt ? -key : key) || ' ' - // Das zuletzt getippte Zeichen auf der Scheibe zeigen: außen Klartext, innen Geheimtext - const last = Array.from(input.value.toUpperCase()).reverse().find((c) => letters.includes(c)) - if (last === undefined) return mark() - const index = letters.indexOf(last) - mark(decrypt ? (index - key + 26) % 26 : index) - } - - const setKey = (k: number) => { - key = ((k % 26) + 26) % 26 - rotation = -key * step - update() - } - - minus.addEventListener('click', () => setKey(key - 1)) - plus.addEventListener('click', () => setKey(key + 1)) - encButton.addEventListener('click', () => { - if (decrypt) input.value = output.textContent?.trim() ?? '' - decrypt = false - update() - }) - decButton.addEventListener('click', () => { - if (!decrypt) input.value = output.textContent?.trim() ?? '' - decrypt = true - update() - }) - input.addEventListener('input', update) - outer.cells.forEach((cell, i) => cell.addEventListener('click', () => mark(i))) - - // Innere Scheibe mit Maus oder Finger drehen, beim Loslassen rastet sie auf einem Buchstaben ein - let dragFrom: number | undefined - const angleAt = (e: PointerEvent) => { - const r = svg.getBoundingClientRect() - return (Math.atan2(e.clientY - (r.top + r.height / 2), e.clientX - (r.left + r.width / 2)) * 180) / Math.PI - } - inner.addEventListener('pointerdown', (e) => { - const pe = e as PointerEvent - dragFrom = angleAt(pe) - rotation - inner.classList.add('dragging') - svg.setPointerCapture(pe.pointerId) - pe.preventDefault() - }) - svg.addEventListener('pointermove', (e) => { - if (dragFrom === undefined) return - rotation = angleAt(e) - dragFrom - inner.setAttribute('transform', `rotate(${rotation})`) - const k = Math.round(-rotation / step) - if (((k % 26) + 26) % 26 !== key) { - key = ((k % 26) + 26) % 26 - const keep = rotation - update() - rotation = keep - inner.setAttribute('transform', `rotate(${rotation})`) - } - }) - const release = () => { - if (dragFrom === undefined) return - dragFrom = undefined - inner.classList.remove('dragging') - setKey(key) - } - svg.addEventListener('pointerup', release) - svg.addEventListener('pointercancel', release) - - update() - return box -} - -/** Keil um die obere Mitte, von Radius `r1` bis `r2`, halbe Breite `half` Grad. */ -function arcWedge(r1: number, r2: number, half: number): string { - const p = (r: number, a: number) => { - const rad = ((a - 90) * Math.PI) / 180 - return `${(r * Math.cos(rad)).toFixed(2)} ${(r * Math.sin(rad)).toFixed(2)}` - } - return `M ${p(r1, -half)} A ${r1} ${r1} 0 0 1 ${p(r1, half)} L ${p(r2, half)} A ${r2} ${r2} 0 0 0 ${p(r2, -half)} Z` -} diff --git a/web/src/ui/clozeView.ts b/web/src/ui/clozeView.ts deleted file mode 100644 index 683fb2d7..00000000 --- a/web/src/ui/clozeView.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { clozeAnswerRight, type Cloze } from '../learn/cloze' -import { inline } from '../learn/markdown' -import { t } from './i18n' - -/** Lückentext im Lernbereich: fertiger Code, die fehlenden Wörter tippt man direkt in die Lücken. */ - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function rich(tag: string, attrs: Record, markdown: string): HTMLElement { - const e = h(tag, attrs) - e.innerHTML = inline(markdown) - return e -} - -export function renderCloze(cloze: Cloze): HTMLElement { - const box = h('section', { class: 'learn-cloze' }) - box.append(h('strong', { class: 'learn-cloze-title' }, cloze.title)) - if (cloze.task) box.append(rich('p', {}, cloze.task)) - box.append(h('p', { class: 'learn-cloze-hint' }, t('web_learn_cloze_hint'))) - - const gaps: { input: HTMLInputElement; answers: string[] }[] = [] - for (const lines of cloze.blocks) { - const code = h('pre', { class: 'learn-cloze-code mono', dir: 'ltr' }) - for (const line of lines) { - const row = h('div', { class: 'ln' }) - for (const part of line) { - if (typeof part === 'string') { - row.append(part) - continue - } - const input = h('input', { - type: 'text', - class: 'learn-cloze-gap mono', - autocomplete: 'off', - autocapitalize: 'off', - autocorrect: 'off', - spellcheck: 'false', - enterkeyhint: 'done', - 'aria-label': `${gaps.length + 1}`, - }) as HTMLInputElement - input.style.width = `${Math.max(3, ...part.answers.map((a) => a.length)) + 1}ch` - gaps.push({ input, answers: part.answers }) - row.append(input) - } - // Leere Zeilen sollen ihre Höhe behalten. - if (!line.length) row.append('\n') - code.append(row) - } - box.append(code) - } - - const status = h('p', { class: 'learn-cloze-status', role: 'status' }) - const explanation = cloze.explanation ? rich('p', { class: 'learn-explanation' }, cloze.explanation) : undefined - if (explanation) explanation.hidden = true - const check = h('button', { class: 'learn-cloze-check' }, t('web_learn_check')) - const solution = h('button', {}, t('web_learn_cloze_solution')) - const again = h('button', {}, t('web_learn_cloze_again')) - solution.hidden = true - again.hidden = true - const actions = h('div', { class: 'learn-cloze-actions' }, check, solution, again) - box.append(actions, status) - if (explanation) box.append(explanation) - - const runCheck = () => { - let right = 0 - for (const gap of gaps) { - const ok = clozeAnswerRight(gap.answers, gap.input.value) - gap.input.classList.toggle('right', ok) - gap.input.classList.toggle('wrong', !ok) - if (ok) right++ - } - const all = right === gaps.length - status.textContent = all ? t('web_learn_cloze_all_right') : t('web_learn_cloze_score_fmt', right, gaps.length) - status.className = `learn-cloze-status ${all ? 'right' : 'wrong'}` - if (explanation) explanation.hidden = !all - if (all) { - gaps.forEach((g) => (g.input.disabled = true)) - check.hidden = true - again.hidden = false - } else { - // Nach einem Fehlversuch darf man sich die Lösung zeigen lassen. - solution.hidden = false - gaps.find((g) => g.input.classList.contains('wrong'))?.input.focus() - } - } - check.addEventListener('click', runCheck) - solution.addEventListener('click', () => { - for (const gap of gaps) { - gap.input.value = gap.answers[0] - gap.input.classList.remove('right', 'wrong') - gap.input.classList.add('shown') - gap.input.disabled = true - } - status.textContent = '' - status.className = 'learn-cloze-status' - if (explanation) explanation.hidden = false - check.hidden = true - solution.hidden = true - again.hidden = false - }) - again.addEventListener('click', () => { - for (const gap of gaps) { - gap.input.value = '' - gap.input.classList.remove('right', 'wrong', 'shown') - gap.input.disabled = false - } - status.textContent = '' - status.className = 'learn-cloze-status' - if (explanation) explanation.hidden = true - check.hidden = false - solution.hidden = true - again.hidden = true - gaps[0]?.input.focus() - }) - gaps.forEach((gap, i) => { - gap.input.addEventListener('input', () => gap.input.classList.remove('right', 'wrong')) - gap.input.addEventListener('keydown', (e) => { - if (e.key !== 'Enter') return - e.preventDefault() - // Enter springt zur nächsten Lücke, in der letzten wird geprüft. - if (i < gaps.length - 1) gaps[i + 1].input.focus() - else runCheck() - }) - }) - return box -} diff --git a/web/src/ui/codePanel.ts b/web/src/ui/codePanel.ts deleted file mode 100644 index 3a9a6d16..00000000 --- a/web/src/ui/codePanel.ts +++ /dev/null @@ -1,398 +0,0 @@ -import { t } from './i18n' -import { generateCode, generateJavaClassFile, sanitizedJavaClassName, type CodeTarget } from '../core/codeGenerator' -import { subprogramName } from '../core/simulation' -import type { VSDocument } from '../core/model' - -/** Port von `CodeGeneratorPanels.swift` und `Runner/JavaScriptRunner.swift`: Code ansehen, kopieren, speichern, JavaScript ausführen. */ - -const keys = { - target: 'visustruct.codegen.target', - javaMode: 'visustruct.codegen.javaOutputMode', - comments: 'visustruct.codegen.includeDiagramComments', - emptyElse: 'visustruct.codegen.includeEmptyElse', -} - -function stored(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function store(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } -} - -const stopMessage = () => t('web_run_stopped_loop') -const cancelMessage = () => t('web_run_cancelled') -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') - -/** Wie `javaScriptWithEndlessLoopGuard` in Swift: `while (true)` bricht nach 100 000 Durchläufen ab. */ -export function javaScriptWithEndlessLoopGuard(js: string): string { - let index = 0 - return js.replace(/^(\s*)while\s*\(\s*true\s*\)(?:\s*\/\*.*?\*\/)?\s*\{/gm, (_m, indent: string) => { - const g = `__visuStructLoopGuard${index++}` - return `${indent}for (let ${g} = 0; ${g} < 100000 || (() => { throw new Error(${JSON.stringify(stopMessage())}); })(); ${g}++) {` - }) -} - -/** `prompt(…)` wird zu einer Eingabe im Fenster; dafür läuft der Code als `async`-Funktion. */ -export function javaScriptRunnerCode(js: string): string { - return javaScriptWithEndlessLoopGuard(js).replace(/\bprompt\s*\(/g, 'await __visuStructPrompt(') -} - -/** Läuft im Worker: `console.log` und `prompt` gehen per Nachricht an die Seite. */ -const workerSource = ` -let answer -let lines = [] -// Text von \`process.stdout.write\` ohne neue Zeile (wie \`System.out.print\`). -let partial = '' -const flush = (all) => { - if (all && partial) { - lines.push(partial) - partial = '' - } - if (lines.length) postMessage({ type: 'log', lines }) - lines = [] -} -const send = (type, text) => { - flush(true) - postMessage({ type, text }) -} -const fmt = (a) => (typeof a === 'string' ? a : JSON.stringify(a)) -// Ausgaben gesammelt senden, sonst bremsen viele kleine Nachrichten (z. B. in Endlosschleifen) die Seite aus. -console.log = (...args) => { - lines.push(partial + args.map(fmt).join(' ')) - partial = '' - if (lines.length >= 500) flush() -} -const process = { stdout: { write: (text) => { - const parts = (partial + String(text)).split('\\n') - partial = parts.pop() - lines.push(...parts) - if (lines.length >= 500) flush() -} } } -setInterval(flush, 50) -const __visuStructPrompt = (label) => new Promise((resolve, reject) => { - answer = (value) => (value === null ? reject(new Error(${JSON.stringify(cancelMessage())})) : resolve(value)) - send('prompt', String(label ?? '')) -}) -onmessage = async (e) => { - if (e.data.type === 'answer') return answer && answer(e.data.value) - try { - const AsyncFunction = (async () => {}).constructor - await new AsyncFunction('__visuStructPrompt', e.data.code)(__visuStructPrompt) - send('done') - } catch (err) { - send('error', String(err)) - } -} -` - -type RunnerMessage = { type: 'log' | 'prompt' | 'error' | 'done'; text?: string; lines?: string[] } - -/** Höchstens so viele Zeilen anzeigen (Endlosschleifen erzeugen sonst riesige Ausgaben). */ -const maxOutputLines = 2000 - -export class CodePanel { - readonly element: HTMLElement - private target: CodeTarget - private javaMode: 'snippet' | 'class' - private comments: boolean - private emptyElse: boolean - private code = '' - - private codeView!: HTMLElement - private javaModeRow!: HTMLElement - private runButton!: HTMLButtonElement - private saveButton!: HTMLButtonElement - private copyButton!: HTMLButtonElement - private runner!: HTMLElement - private runOutput!: HTMLElement - private runInput!: HTMLFormElement - private pseudocodeBox!: HTMLInputElement - private pseudocodeHint!: HTMLElement - private worker?: Worker - private outputLines: string[] = [] - private droppedLines = 0 - private outputFrame?: number - - constructor( - private currentDocument: () => VSDocument, - private saveFile: (filename: string, text: string) => void, - private onClose: () => void, - private setPseudocode: (on: boolean) => void, - /** Unterprogramme aus den anderen Reitern, die in den Code gehören. */ - private subprograms: () => VSDocument[] = () => [], - ) { - const t = stored(keys.target) - this.target = t === 'python' || t === 'javascript' ? t : 'java' - this.javaMode = stored(keys.javaMode) === 'class' ? 'class' : 'snippet' - this.comments = stored(keys.comments) === 'true' - this.emptyElse = stored(keys.emptyElse) !== 'false' - this.element = document.createElement('aside') - this.element.className = 'codepanel' - this.build() - } - - /** Neu erzeugen, z. B. nach jeder Änderung am Diagramm. */ - refresh() { - const doc = this.currentDocument() - const options = this.options() - this.code = - this.target === 'java' && this.javaMode === 'class' - ? generateJavaClassFile(doc, this.classStem(), options, this.subprograms()) - : generateCode(doc, this.target, options, this.subprograms()) - this.codeView.textContent = this.code - this.javaModeRow.hidden = this.target !== 'java' - this.saveButton.hidden = this.target !== 'java' - // Pseudocode ist kein Programm: nicht ausführen. - this.runButton.hidden = this.target !== 'javascript' || !!doc.pseudocode - if (this.runButton.hidden) this.stopRun(true) - this.pseudocodeBox.checked = !!doc.pseudocode - this.pseudocodeHint.hidden = !doc.pseudocode - for (const b of this.element.querySelectorAll('[data-target]')) { - b.setAttribute('aria-pressed', String(b.dataset.target === this.target)) - } - for (const b of this.element.querySelectorAll('[data-java-mode]')) { - b.setAttribute('aria-pressed', String(b.dataset.javaMode === this.javaMode)) - } - } - - close() { - this.stopRun(true) - } - - private options() { - return { - includeDiagramTextAsComments: this.comments, - includeEmptyElseBranch: this.emptyElse, - pseudocodeNote: t('codegen_pseudocode_note'), - } - } - - private classStem(): string { - return subprogramName(this.currentDocument().caption) || 'Main' - } - - private build() { - this.element.innerHTML = ` -
    -

    ${esc(t('codegen_placeholder_title'))}

    - -
    -
    - -
    -
    - -
    - - - - -
    - - - -
    -
    
    -      
    -      

    ${esc(t('codegen_generated_hint'))}

    - ` - const q = (s: string) => this.element.querySelector(s) as T - this.codeView = q('.code-view') - this.javaModeRow = q('.java-mode') - this.runButton = q('[data-act=run]') - this.saveButton = q('[data-act=save]') - this.copyButton = q('[data-act=copy]') - this.runner = q('.runner') - this.runOutput = q('.runner-out') - this.runInput = q('.runner-input') - this.pseudocodeBox = q('[data-opt=pseudocode]') - this.pseudocodeHint = q('.pseudocode-hint') - this.pseudocodeBox.addEventListener('change', () => this.setPseudocode(this.pseudocodeBox.checked)) - - q('.code-close').addEventListener('click', () => this.onClose()) - for (const b of this.element.querySelectorAll('[data-target]')) { - b.addEventListener('click', () => { - this.target = b.dataset.target as CodeTarget - store(keys.target, this.target) - this.refresh() - }) - } - for (const b of this.element.querySelectorAll('[data-java-mode]')) { - b.addEventListener('click', () => { - this.javaMode = b.dataset.javaMode as 'snippet' | 'class' - store(keys.javaMode, this.javaMode) - this.refresh() - }) - } - const comments = q('[data-opt=comments]') - comments.checked = this.comments - comments.addEventListener('change', () => { - this.comments = comments.checked - store(keys.comments, String(this.comments)) - this.refresh() - }) - const emptyElse = q('[data-opt=emptyElse]') - emptyElse.checked = this.emptyElse - emptyElse.addEventListener('change', () => { - this.emptyElse = emptyElse.checked - store(keys.emptyElse, String(this.emptyElse)) - this.refresh() - }) - - this.copyButton.addEventListener('click', () => this.copy()) - this.saveButton.addEventListener('click', () => { - // Die Klasse muss so heißen wie die Datei; deshalb immer die vollständige Klasse speichern. - const className = sanitizedJavaClassName(this.classStem()) - const source = generateJavaClassFile(this.currentDocument(), this.classStem(), this.options()) - this.saveFile(`${className}.java`, source) - }) - this.runButton.addEventListener('click', () => this.run()) - q('[data-act=stop]').addEventListener('click', () => this.stopRun(false)) - q('[data-act=cancel]').addEventListener('click', () => this.answer(null)) - this.runInput.addEventListener('submit', (e) => { - e.preventDefault() - this.answer(this.runInput.querySelector('input')!.value) - }) - } - - private async copy() { - let ok = false - try { - await navigator.clipboard.writeText(this.code) - ok = true - } catch { - // Ohne Zwischenablage-Recht: Code markieren, damit Strg+C reicht. - const range = document.createRange() - range.selectNodeContents(this.codeView) - const sel = getSelection() - sel?.removeAllRanges() - sel?.addRange(range) - ok = document.execCommand?.('copy') ?? false - } - this.copyButton.textContent = t(ok ? 'web_copied_check' : 'web_copy_manual') - setTimeout(() => (this.copyButton.textContent = t('codegen_copy')), 2000) - } - - // MARK: - JavaScript ausführen - - private run() { - this.stopRun(true) - this.runner.hidden = false - this.runOutput.textContent = '' - this.outputLines = [] - this.droppedLines = 0 - this.runOutput.classList.remove('error') - const code = javaScriptRunnerCode(this.code) - const onMessage = (m: RunnerMessage) => this.onRunnerMessage(m) - try { - const url = URL.createObjectURL(new Blob([workerSource], { type: 'text/javascript' })) - const worker = new Worker(url) - URL.revokeObjectURL(url) - worker.onmessage = (e) => onMessage(e.data as RunnerMessage) - worker.onerror = (e) => { - e.preventDefault() - onMessage({ type: 'error', text: e.message || t('web_run_error') }) - } - worker.postMessage({ type: 'run', code }) - this.worker = worker - } catch { - this.runInPage(code, onMessage) - } - this.setRunning(true) - } - - /** Falls Worker gesperrt sind: im Fenster selbst ausführen (Endlosschleifen-Schutz wie in der App). */ - private runInPage(code: string, onMessage: (m: RunnerMessage) => void) { - const fakeConsole = { - log: (...args: unknown[]) => - onMessage({ type: 'log', lines: [args.map((a) => (typeof a === 'string' ? a : JSON.stringify(a))).join(' ')] }), - } - const prompt = (label: string) => - new Promise((resolve, reject) => { - this.pendingAnswer = (v) => (v === null ? reject(new Error(cancelMessage())) : resolve(v)) - onMessage({ type: 'prompt', text: label }) - }) - const AsyncFunction = (async () => {}).constructor as new (...args: string[]) => (...a: unknown[]) => Promise - new AsyncFunction('__visuStructPrompt', 'console', code)(prompt, fakeConsole) - .then(() => onMessage({ type: 'done' })) - .catch((err: unknown) => onMessage({ type: 'error', text: String(err) })) - } - - private pendingAnswer?: (value: string | null) => void - - private onRunnerMessage(m: RunnerMessage) { - switch (m.type) { - case 'log': - this.appendOutput(m.lines ?? []) - break - case 'prompt': { - this.runInput.hidden = false - this.runInput.querySelector('label')!.textContent = m.text ?? '' - const field = this.runInput.querySelector('input')! - field.value = '' - field.focus() - break - } - case 'error': - this.appendOutput([m.text ?? '']) - this.runOutput.classList.add('error') - this.stopRun(false) - break - case 'done': - this.stopRun(false) - break - } - } - - private appendOutput(lines: string[]) { - this.outputLines.push(...lines) - if (this.outputLines.length > maxOutputLines) { - this.droppedLines += this.outputLines.length - maxOutputLines - this.outputLines.splice(0, this.outputLines.length - maxOutputLines) - } - if (this.outputFrame !== undefined) return - this.outputFrame = requestAnimationFrame(() => { - this.outputFrame = undefined - const head = this.droppedLines > 0 ? t('web_run_dropped_fmt', this.droppedLines) + '\n' : '' - this.runOutput.textContent = head + this.outputLines.join('\n') + (this.outputLines.length ? '\n' : '') - this.runOutput.scrollTop = this.runOutput.scrollHeight - }) - } - - private answer(value: string | null) { - this.runInput.hidden = true - if (this.worker) this.worker.postMessage({ type: 'answer', value }) - else this.pendingAnswer?.(value) - this.pendingAnswer = undefined - } - - private stopRun(hide: boolean) { - this.worker?.terminate() - this.worker = undefined - this.pendingAnswer = undefined - if (this.runInput) this.runInput.hidden = true - if (hide && this.runner) this.runner.hidden = true - this.setRunning(false) - } - - private setRunning(running: boolean) { - const stop = this.element.querySelector('[data-act=stop]') - if (stop) stop.disabled = !running - } -} diff --git a/web/src/ui/driveGameView.ts b/web/src/ui/driveGameView.ts deleted file mode 100644 index 51a62dc8..00000000 --- a/web/src/ui/driveGameView.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { blockPicture } from './blockPicture' -import { inline } from '../learn/markdown' -import { driveRound, driveWorld, type DriveGame } from '../learn/driveGame' -import type { Pose } from '../core/robotWorld' -import { t } from './i18n' - -/** Spiel „Wo landet der mBot2?“: Code lesen, Zielfeld antippen, dann fährt der mBot2 die Strecke auf der Karte vor. */ - -const SVG = 'http://www.w3.org/2000/svg' - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function s(tag: string, attrs: Record = {}): SVGElement { - const e = document.createElementNS(SVG, tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, String(v)) - return e -} - -function rich(tag: string, attrs: Record, markdown: string): HTMLElement { - const e = h(tag, attrs) - e.innerHTML = inline(markdown) - return e -} - -const FIELD_FILL: Record = { '#': '#6b7280', '=': '#111827', r: '#ef4444', g: '#22c55e', b: '#3b82f6', y: '#facc15' } -/** Millisekunden je Bild der Fahrt. */ -const frameMs = 450 - -/** `asBlocks`: Code der Runde als Blöcke zeigen (Block-Lektionen für Jüngere), sonst als Java. */ -export function renderDriveGame(game: DriveGame, ludwigURL: string, asBlocks = false): HTMLElement { - const box = h('section', { class: 'learn-game learn-round-game learn-drive-game' }) - const head = h('div', { class: 'learn-game-head' }) - head.append(h('img', { src: ludwigURL, alt: '', width: '40', height: '40' }), h('strong', {}, game.title)) - const status = h('p', { class: 'learn-game-status' }) - const stage = h('div', { class: 'learn-game-stage' }) - box.append(head, status, stage) - const total = game.rounds.length - const F = game.field - let index = 0 - let score = 0 - let timer = 0 - const showStatus = () => { - status.textContent = `${t('web_game_round_fmt', index + 1, total)} · ${t('web_game_score_fmt', score)}` - } - const next = () => { - const b = h('button', { class: 'primary' }, index + 1 < total ? t('web_game_next') : t('web_game_finish')) - b.addEventListener('click', () => { - index++ - if (index < total) showRound() - else showEnd() - }) - return b - } - - const showRound = () => { - window.clearInterval(timer) - showStatus() - const round = game.rounds[index] - const cols = round.map[0].length - const rows = round.map.length - const width = cols * F - const depth = rows * F - // Weltkoordinaten (Mitte 0,0, y nach hinten) → SVG (oben links 0,0, y nach unten) - const sx = (x: number) => x + width / 2 - const sy = (y: number) => depth / 2 - y - const world = driveWorld(round, F) - const result = driveRound(round, F) - const svg = s('svg', { viewBox: `-2 -2 ${width + 4} ${depth + 4}`, class: 'learn-drive-map', role: 'group', 'aria-label': round.question }) - const cells: SVGElement[][] = [] - round.map.forEach((line, r) => { - cells.push([]) - Array.from(line).forEach((ch, c) => { - const cell = s('rect', { x: c * F, y: r * F, width: F, height: F, class: 'cell', fill: FIELD_FILL[ch] ?? '#ffffff', tabindex: 0, role: 'button', 'aria-label': `${c + 1}, ${r + 1}` }) - const pick = () => choose(c, r) - cell.addEventListener('click', pick) - cell.addEventListener('keydown', (e) => { - if ((e as KeyboardEvent).key === 'Enter' || (e as KeyboardEvent).key === ' ') { - e.preventDefault() - pick() - } - }) - svg.append(cell) - cells[r].push(cell) - }) - }) - const trail = s('polyline', { class: 'trail', points: '', fill: 'none' }) - const robot = s('g', { class: 'robot' }) - const body = Math.min(F * 0.7, 20) - robot.append( - s('rect', { x: -body / 2, y: -body / 2, width: body, height: body, rx: body / 5, class: 'body' }), - s('path', { d: `M ${-body / 3} ${body / 6} L 0 ${-body / 2.4} L ${body / 3} ${body / 6} Z`, class: 'nose' }), - ) - // Kurs ohne Sprung von 359° auf 0°, damit die Drehung den kurzen Weg nimmt - let heading = world.start.heading - const place = (pose: Pose) => { - heading += ((((pose.heading - heading) % 360) + 540) % 360) - 180 - robot.style.transform = `translate(${sx(pose.x)}px, ${sy(pose.y)}px) rotate(${heading}deg)` - } - place(world.start) - svg.append(trail, robot) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const code = - (asBlocks && blockPicture(round.code)) || - h('pre', { class: 'learn-game-code mono', dir: 'ltr' }, ...round.code.split('\n').map((l) => h('div', { class: 'ln' }, l || ' '))) - let chosen = false - const choose = (c: number, r: number) => { - if (chosen) return - chosen = true - svg.classList.add('done') - const right = c === round.target.col && r === round.target.row - cells[r][c].classList.add(right ? 'right' : 'wrong') - if (right) score++ - showStatus() - // Jetzt fährt der mBot2 vor; danach leuchtet das richtige Feld. - const frames = result.run.frames - let f = 0 - const points: string[] = [`${sx(world.start.x)},${sy(world.start.y)}`] - const step = () => { - f++ - if (f >= frames.length) { - window.clearInterval(timer) - cells[round.target.row][round.target.col].classList.add('right') - const n = next() - feedback.className = `learn-game-feedback ${right ? 'right' : 'wrong'}` - feedback.replaceChildren(h('p', {}, right ? t('web_game_right') : t('web_game_wrong_short')), rich('div', { class: 'learn-game-explain' }, round.explanation), n) - n.focus({ preventScroll: true }) - return - } - const pose = frames[f].pose - place(pose) - points.push(`${sx(pose.x)},${sy(pose.y)}`) - trail.setAttribute('points', points.join(' ')) - } - timer = window.setInterval(() => { - if (!box.isConnected) return window.clearInterval(timer) - step() - }, frameMs) - } - stage.replaceChildren(code, rich('p', { class: 'learn-game-question' }, round.question), svg, feedback) - } - - const showEnd = () => { - window.clearInterval(timer) - status.textContent = '' - const again = h('button', { class: 'primary' }, t('web_game_again')) - again.addEventListener('click', () => { - index = score = 0 - showRound() - }) - stage.replaceChildren( - h('p', { class: 'learn-game-result' }, t('web_game_done_fmt', score, total)), - h('p', {}, t(score === total ? 'web_game_perfect' : score >= total * 0.7 ? 'web_game_good' : 'web_game_practice_short')), - again, - ) - } - const start = h('button', { class: 'primary' }, t('web_game_start')) - start.addEventListener('click', showRound) - stage.append(rich('p', {}, game.intro), start) - return box -} diff --git a/web/src/ui/exporter.ts b/web/src/ui/exporter.ts deleted file mode 100644 index 43f9f64b..00000000 --- a/web/src/ui/exporter.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { LayoutEngine, type Metrics } from '../core/layout' -import type { VSDocument } from '../core/model' -import type { MeasureText } from '../core/textMeasurement' -import { diagramFontFamily } from '../core/textMeasurement' -import { t as tr } from './i18n' -import { DiagramRenderer, displayElement, el } from './renderer' - -/** Port von `Export/DiagramSnapshotAndExport.swift` und `DiagramExporter.swift`: Bild, PDF und Druck. */ - -const exportInset = { left: 12, top: 10 } -const exportOuterMargin = 8 -const exportPixelCap = 8000 -const titleFontSize = 16 -const titlePadX = 12 -const background = '#fafafa' - -/** Wie `exportVisibleDiagramTitle`: leerer Titel → Platzhalter wie im Titelfeld. */ -export function exportVisibleDiagramTitle(doc: VSDocument): string { - const t = doc.caption.trim() - return t === '' ? tr('diagram_title_placeholder') : t -} - -/** Wie `sanitizedExportFilenameSlug` (Stil „Ohne Datum“). */ -export function exportFilenameSlug(raw: string): string { - let s = raw.trim().replace(/\.visustruct$/i, '') - if (s === '') return '' - s = s.replace(/\s/g, '-').replace(/[/:\\*?"<>|]/g, '-') - while (s.includes('--')) s = s.replace(/--/g, '-') - s = s.replace(/^-+|-+$/g, '') - if (s.length > 120) s = s.slice(0, 120).replace(/^-+|-+$/g, '') - return s -} - -export function exportFilenameBase(doc: VSDocument, tabTitle: string): string { - return exportFilenameSlug(doc.caption) || exportFilenameSlug(tabTitle) || 'VisuStruct' -} - -/** Titel auf höchstens zwei Zeilen umbrechen (`lineLimit(2)`). */ -function wrapTitle(title: string, width: number, measure: MeasureText): string[] { - const words = title.split(/\s+/) - const lines: string[] = [] - let line = '' - for (const w of words) { - const next = line ? `${line} ${w}` : w - if (line && measure(next, titleFontSize, 'bold') > width) { - lines.push(line) - line = w - } else { - line = next - } - } - if (line) lines.push(line) - if (lines.length > 2) { - let last = lines.slice(1).join(' ') - while (last.length > 1 && measure(last + '…', titleFontSize, 'bold') > width) last = last.slice(0, -1) - return [lines[0], last + '…'] - } - return lines -} - -export interface ExportImage { - svg: SVGSVGElement - width: number - height: number -} - -/** - * Diagramm mit sichtbarem Titel, ohne Auswahl (gleiche Grundlage für PNG, SVG, PDF und Druck). - * `minSize` wie in der App 320; die Vorschau im Lernbereich nimmt 0, damit kein Leerraum entsteht. - */ -export function renderExportSVG(doc: VSDocument, metrics: Metrics, measure: MeasureText, minSize = 320): ExportImage | undefined { - const displayDoc: VSDocument = { ...doc, elements: doc.elements.map(displayElement) } - const layout = new LayoutEngine(metrics, measure).layout(displayDoc, exportInset) - const w = Math.min(exportPixelCap, Math.max(minSize, layout.canvasSize.width)) - const h = Math.min(exportPixelCap, Math.max(minSize, layout.canvasSize.height)) - if (w <= 2 || h <= 2) return undefined - - const lines = wrapTitle(exportVisibleDiagramTitle(doc), w - 2 * titlePadX, measure) - const lineHeight = Math.ceil(titleFontSize * 1.2) - const titleH = Math.min(56, lines.length * lineHeight + 2) - const total = titleH + h - - const diagram = new DiagramRenderer(metrics, measure).render(layout, { width: w, height: h }, undefined, exportOuterMargin) - diagram.setAttribute('y', String(titleH)) - diagram.removeAttribute('class') - - const svg = el('svg', { - xmlns: 'http://www.w3.org/2000/svg', - width: w, - height: total, - viewBox: `0 0 ${w} ${total}`, - }) - svg.append(el('rect', { x: 0, y: 0, width: w, height: total, fill: background })) - const text = el('text', { - x: titlePadX, - y: 0, - 'font-family': diagramFontFamily, - 'font-size': titleFontSize, - 'font-weight': 600, - fill: 'black', - }) - lines.forEach((line, i) => { - const span = el('tspan', { x: titlePadX, y: i * lineHeight + titleFontSize }) - span.textContent = line - text.append(span) - }) - svg.append(text, diagram) - return { svg, width: w, height: total } -} - -export function svgMarkup(image: ExportImage): string { - return '\n' + new XMLSerializer().serializeToString(image.svg) -} - -/** Rastert das Export-SVG (Faktor wie `displayScale`, begrenzt auf 8000 Pixel pro Seite). */ -export async function rasterize(image: ExportImage, scale: number): Promise { - const s = Math.max(1, Math.min(scale, exportPixelCap / image.width, exportPixelCap / image.height)) - const url = URL.createObjectURL(new Blob([svgMarkup(image)], { type: 'image/svg+xml' })) - try { - const img = new Image() - img.src = url - await img.decode() - const canvas = document.createElement('canvas') - canvas.width = Math.round(image.width * s) - canvas.height = Math.round(image.height * s) - const ctx = canvas.getContext('2d')! - ctx.fillStyle = background - ctx.fillRect(0, 0, canvas.width, canvas.height) - ctx.drawImage(img, 0, 0, canvas.width, canvas.height) - return canvas - } finally { - URL.revokeObjectURL(url) - } -} - -export function canvasToPNG(canvas: HTMLCanvasElement): Promise { - return new Promise((resolve, reject) => - canvas.toBlob((b) => (b ? resolve(b) : reject(new Error('PNG konnte nicht erzeugt werden.'))), 'image/png'), - ) -} - -// MARK: - PDF (A4, eine Seite) - -export interface Rect { - x: number - y: number - width: number - height: number -} - -/** Wie `A4SinglePageLayout.pageAndImageDrawRect`: Hoch- oder Querformat, je nachdem, wo das Bild größer wird. */ -export function a4PageAndImageRect(imageWidth: number, imageHeight: number): { page: { width: number; height: number }; draw: Rect } { - const margin = 32 - const fudge = 0.98 - const iw = Math.max(1, imageWidth) - const ih = Math.max(1, imageHeight) - const fit = (page: { width: number; height: number }) => { - const cw = page.width - 2 * margin - const ch = page.height - 2 * margin - const s = Math.min(cw / iw, ch / ih) * fudge - return { s, cw, ch } - } - const portrait = { width: 595, height: 842 } - const landscape = { width: 842, height: 595 } - const p = fit(portrait) - const l = fit(landscape) - const [page, f] = l.s > p.s ? [landscape, l] : [portrait, p] - const sw = iw * f.s - const sh = ih * f.s - return { page, draw: { x: margin + (f.cw - sw) / 2, y: margin + (f.ch - sh) / 2, width: sw, height: sh } } -} - -async function deflate(data: Uint8Array): Promise { - const stream = new Blob([data as BlobPart]).stream().pipeThrough(new CompressionStream('deflate')) - return new Uint8Array(await new Response(stream).arrayBuffer()) -} - -/** PDF-Literal-String (UTF-16BE mit BOM, damit Umlaute im Titel stimmen). */ -function pdfTextString(s: string): string { - let hex = 'FEFF' - for (let i = 0; i < s.length; i++) hex += s.charCodeAt(i).toString(16).padStart(4, '0').toUpperCase() - return `<${hex}>` -} - -/** - * Einseitiges PDF mit dem Diagramm als verlustfreiem Rasterbild (wie `writeTemporaryDiagramPDF`). - * `rgb` enthält 3 Byte pro Pixel, Zeile für Zeile von oben. - */ -export async function buildDiagramPDF(rgb: Uint8Array, pixelWidth: number, pixelHeight: number, imageSize: { width: number; height: number }, title: string): Promise { - const { page, draw } = a4PageAndImageRect(imageSize.width, imageSize.height) - const image = await deflate(rgb) - const fmt = (n: number) => (Math.round(n * 100) / 100).toString() - // PDF-Ursprung unten links. - const content = `q ${fmt(draw.width)} 0 0 ${fmt(draw.height)} ${fmt(draw.x)} ${fmt(page.height - draw.y - draw.height)} cm /Im0 Do Q` - const enc = new TextEncoder() - const objects: (string | Uint8Array)[][] = [ - ['<< /Type /Catalog /Pages 2 0 R >>'], - ['<< /Type /Pages /Kids [3 0 R] /Count 1 >>'], - [`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${page.width} ${page.height}] /Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>`], - [ - `<< /Type /XObject /Subtype /Image /Width ${pixelWidth} /Height ${pixelHeight} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode /Length ${image.length} >>\nstream\n`, - image, - '\nendstream', - ], - [`<< /Length ${content.length} >>\nstream\n${content}\nendstream`], - [`<< /Creator ${pdfTextString('VisuStruct')} /Title ${pdfTextString(title)} >>`], - ] - const parts: Uint8Array[] = [] - let offset = 0 - const push = (p: string | Uint8Array) => { - const bytes = typeof p === 'string' ? enc.encode(p) : p - parts.push(bytes) - offset += bytes.length - } - push('%PDF-1.4\n%\xE2\xE3\xCF\xD3\n') - const offsets: number[] = [] - objects.forEach((obj, i) => { - offsets.push(offset) - push(`${i + 1} 0 obj\n`) - obj.forEach(push) - push('\nendobj\n') - }) - const xref = offset - push(`xref\n0 ${objects.length + 1}\n0000000000 65535 f \n`) - for (const o of offsets) push(`${String(o).padStart(10, '0')} 00000 n \n`) - push(`trailer\n<< /Size ${objects.length + 1} /Root 1 0 R /Info ${objects.length} 0 R >>\nstartxref\n${xref}\n%%EOF\n`) - return new Blob(parts as BlobPart[], { type: 'application/pdf' }) -} - -export function canvasRGB(canvas: HTMLCanvasElement): Uint8Array { - const { data } = canvas.getContext('2d')!.getImageData(0, 0, canvas.width, canvas.height) - const rgb = new Uint8Array(canvas.width * canvas.height * 3) - for (let i = 0, j = 0; i < data.length; i += 4, j += 3) { - rgb[j] = data[i] - rgb[j + 1] = data[i + 1] - rgb[j + 2] = data[i + 2] - } - return rgb -} diff --git a/web/src/ui/filesPanel.ts b/web/src/ui/filesPanel.ts deleted file mode 100644 index b9aca548..00000000 --- a/web/src/ui/filesPanel.ts +++ /dev/null @@ -1,485 +0,0 @@ -import { progress } from '../learn/progress' -import { compareEntries, programBaseName, todayFolder, uniqueBaseName, validFolderName } from '../core/workspace' -import { t } from './i18n' -import { icons } from './icons' -import { sidebarIcon } from './learnPanel' -import { visucodeLinkHTML, worldBrandHTML, worldChooserHTML, type WorldChoice } from './visubotView' -import { hostWorld } from './site' -import { - browserWorkspace, - canPickFolder, - forgetFolder, - pickFolder, - reopenFolder, - storedFolder, - type Workspace, - type WsDir, -} from './workspaceStore' - -/** - * Spalte „Dateien“: ein Workspace als Baum. Oben die Klassen (z. B. 11FI1), darunter je Tag ein Ordner - * (beim Speichern automatisch angelegt), darin die Programme: Struktogramm (.visustruct) und Code (.java). - */ - -/** Wo ein Programm im Workspace liegt. */ -export interface WorkspaceFile { - dir: WsDir - /** Name ohne Endung; dazu gehören `.visustruct` und `.java`. */ - base: string - /** Für die Anzeige, z. B. `11FI1/2026-10-23/Rechner`. */ - path: string -} - -/** Lernstand im Workspace (ein Lernstand je Workspace). */ -export const progressFile = 'learnProgress.json' -/** Früherer Name der Lernstand-Datei (bis 0.3.31); wird übernommen und dann entfernt. */ -const legacyProgressFile = 'lernstand.json' - -export interface FilesHost { - open(xml: string, file: WorkspaceFile, java: string | undefined): void - hide(): void - message(text: string): void - /** Lernstand aus dem Workspace übernommen: Lernbereich neu zeichnen. */ - progressLoaded(): void - /** Start-Dialog: VisuStruct oder VisuBot gewählt. */ - chooseWorld(mode: WorldChoice): Promise -} - -const modeKey = 'visustruct.files.mode' -const expandedKey = 'visustruct.files.expanded' - -function storedList(key: string): string[] { - try { - const list: unknown = JSON.parse(stored(key) ?? '[]') - return Array.isArray(list) ? list.filter((x): x is string => typeof x === 'string') : [] - } catch { - return [] - } -} - -function stored(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function store(key: string, value: string | null) { - try { - if (value === null) localStorage.removeItem(key) - else localStorage.setItem(key, value) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } -} - -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') - -interface AskOptions { - title: string - hint?: string - /** Knöpfe, die ihren Text zurückgeben (z. B. vorhandene Klassen). */ - choices?: string[] - /** Eingabefeld mit diesem Platzhalter. */ - input?: string - ok?: string - danger?: boolean -} - -/** Rückfrage im Fenster selbst (`window.prompt`/`confirm` sind in eingebetteten Ansichten gesperrt). */ -function ask(o: AskOptions): Promise { - return new Promise((resolve) => { - const box = document.createElement('div') - box.className = 'confirm' - const choices = (o.choices ?? []).map((c) => ``).join('') - box.innerHTML = `` - const form = box.querySelector('form')! - const field = form.querySelector('input') - const done = (value: string | undefined) => { - box.remove() - resolve(value) - } - form.addEventListener('submit', (e) => { - e.preventDefault() - if (!field) return done('') - if (field.value.trim()) done(field.value) - else field.focus() - }) - box.addEventListener('click', (e) => { - const target = e.target as HTMLElement - const choice = target.closest('[data-choice]')?.dataset.choice - if (choice !== undefined) done(choice) - else if (target === box || target.closest('[data-cancel]')) done(undefined) - }) - box.addEventListener('keydown', (e) => e.key === 'Escape' && done(undefined)) - document.body.append(box) - ;(field ?? form.querySelector('[data-cancel]'))!.focus() - }) -} - -export class FilesPanel { - readonly element: HTMLElement - private body!: HTMLElement - private workspace?: Workspace - /** Gemerkter Ordner, für den der Browser erst wieder fragen muss. */ - private pending?: FileSystemDirectoryHandle - /** Aufgeklappte Ordner als Pfade, z. B. `2026-10-23`. */ - private expanded = new Set(storedList(expandedKey)) - private rendering = 0 - /** Workspace, mit dem der Lernstand gerade abgeglichen wird. */ - private progressWorkspace?: Workspace - /** Schon ein Workspace in dieser Sitzung: dann zählt beim Wechsel nur noch die Datei. */ - private hadWorkspace = false - private progressTimer?: number - - constructor(private host: FilesHost) { - this.element = document.createElement('aside') - this.element.className = 'files' - this.build() - progress.onChange(() => this.progressChanged()) - void this.restore() - } - - private rememberExpanded() { - store(expandedKey, JSON.stringify([...this.expanded])) - } - - get ready(): boolean { - return !!this.workspace - } - - /** Neu aufbauen, z. B. nach einem Sprachwechsel. */ - build() { - this.element.innerHTML = ` -
    -

    ${esc(t('web_files_title'))}

    - - - -
    -
    - ` - this.body = this.element.querySelector('.files-body')! - const on = (name: string, f: () => void) => this.element.querySelector(`[data-files=${name}]`)!.addEventListener('click', f) - on('hide', () => this.host.hide()) - on('new-folder', () => void this.newFolder()) - on('switch', () => void this.switchWorkspace()) - this.body.addEventListener('click', (e) => void this.onTreeClick(e)) - void this.render() - } - - // MARK: - Workspace wählen - - private async restore() { - if (stored(modeKey) === 'browser') { - this.workspace = browserWorkspace() - } else if (canPickFolder()) { - const folder = await storedFolder() - if (folder?.ready) this.workspace = await reopenFolder(folder.handle) - else this.pending = folder?.handle - } - await this.render() - if (!this.workspace) void this.startDialog() - } - - /** - * Beim Start ohne Workspace: fragen, wo Lernstand und Programme liegen. Der Browser merkt sich nur den Ordner, - * ohne Ordner ist der Lernstand nach dem Schließen weg. - */ - private async startDialog() { - const folderName = t('web_learn_start_folder') - const button = (start: string, label: string, primary = false, hint?: string) => - `${hint ? `

    ${esc(hint)}

    ` : ''}` - const options = canPickFolder() - ? [ - this.pending ? button('reconnect', `${icons.open} ${esc(t('web_learn_start_continue_fmt', this.pending.name))}`, true) : '', - button('pick', `${icons.open} ${esc(t('web_learn_start_open'))}`, !this.pending), - button('create', `${icons.newFolder} ${esc(t('web_learn_start_create_fmt', folderName))}`, false, t('web_learn_start_create_hint')), - ] - : [button('browser', esc(t('web_files_browser')), true, t('web_files_browser_hint'))] - const box = document.createElement('div') - box.className = 'confirm' - box.innerHTML = `` - const close = () => box.remove() - box.addEventListener('click', (e) => { - const world = (e.target as HTMLElement).closest('[data-world]')?.dataset.world as WorldChoice | undefined - if (world) { - void this.host.chooseWorld(world).then(() => (box.querySelector('.start-world')!.innerHTML = worldChooserHTML())) - return - } - const start = (e.target as HTMLElement).closest('[data-start]')?.dataset.start - if (!start) return - close() - if (start === 'reconnect') void this.reconnect() - else if (start === 'pick') void this.connectFolder() - else if (start === 'create') void this.connectFolder(folderName) - else if (start === 'browser') void this.useBrowser() - }) - box.addEventListener('keydown', (e) => e.key === 'Escape' && close()) - document.body.append(box) - box.querySelector('.files-start')?.focus() - } - - private async connectFolder(create?: string) { - try { - this.workspace = await pickFolder(create) - this.pending = undefined - store(modeKey, 'folder') - } catch { - /* abgebrochen */ - } - await this.render() - } - - private async reconnect() { - if (!this.pending) return - this.workspace = await reopenFolder(this.pending) - if (this.workspace) this.pending = undefined - await this.render() - } - - private async useBrowser() { - this.workspace = browserWorkspace() - store(modeKey, 'browser') - await this.render() - } - - /** Anderen Ordner wählen (bzw. am iPad: zurück zur Auswahl). */ - private async switchWorkspace() { - if (canPickFolder()) return this.connectFolder() - this.workspace = undefined - store(modeKey, null) - await forgetFolder() - await this.render() - } - - // MARK: - Lernstand - - /** - * Neuer Workspace: der Lernstand kommt allein aus `learnProgress.json` (oder der alten `lernstand.json`). - * Nur beim ersten Workspace der Sitzung bleibt dazu, was schon vorher gelernt wurde; danach wird die Datei geschrieben. - */ - private async syncProgress(ws: Workspace | undefined) { - this.progressWorkspace = ws - if (!ws) return - const first = !this.hadWorkspace - this.hadWorkspace = true - try { - const name = (await ws.root.has(progressFile)) ? progressFile : (await ws.root.has(legacyProgressFile)) ? legacyProgressFile : undefined - if (!name || !progress.loadFile(await ws.root.read(name), first)) { - if (!first) progress.clear() - } - this.host.progressLoaded() - await this.writeProgress(ws) - if (await ws.root.has(legacyProgressFile)) { - await ws.root.remove(legacyProgressFile) - await this.render() - } - } catch { - /* Ordner nicht lesbar: Lernstand nur bis zum Neuladen */ - } - } - - private async writeProgress(ws: Workspace) { - if (ws !== this.progressWorkspace) return - try { - const fresh = !(await ws.root.has(progressFile)) - await ws.root.write(progressFile, progress.toFile()) - if (fresh) await this.render() - } catch { - /* z. B. Zugriff nach dem Neuladen noch nicht erlaubt */ - } - } - - /** Änderungen im Lernbereich gesammelt (nach einer Sekunde Ruhe) in die Datei schreiben. */ - private progressChanged() { - const ws = this.progressWorkspace - if (!ws) return - window.clearTimeout(this.progressTimer) - this.progressTimer = window.setTimeout(() => void this.writeProgress(ws), 1000) - } - - // MARK: - Baum - - async render() { - const ticket = ++this.rendering - const ws = this.workspace - if (ws !== this.progressWorkspace) void this.syncProgress(ws) - this.element.querySelector('[data-files=new-folder]')!.hidden = !ws - this.element.querySelector('[data-files=switch]')!.hidden = !ws - if (!ws) { - this.body.innerHTML = this.startHTML() - return - } - const rows: string[] = [] - const walk = async (dir: WsDir, path: string, depth: number) => { - const entries = (await dir.list()).sort(compareEntries) - for (const entry of entries) { - const full = path ? `${path}/${entry.name}` : entry.name - if (entry.kind === 'directory') { - const open = this.expanded.has(full) - rows.push( - `
    - - -
    `, - ) - if (open) await walk(await dir.dir(entry.name), full, depth + 1) - } else if (/\.(visustruct|java|json)$/i.test(entry.name)) { - // Zu einem Programm gehören .visustruct und .java: angezeigt wird das Programm einmal. - if (/\.java$/i.test(entry.name) && entries.some((e) => e.name === entry.name.replace(/\.java$/i, '.visustruct'))) continue - const program = /\.(visustruct|java)$/i.test(entry.name) - rows.push( - `
    - - ${program ? `` : ''} -
    `, - ) - } - } - } - try { - await walk(ws.root, '', 0) - } catch (err) { - rows.push(`

    ${esc((err as Error).message)}

    `) - } - if (ticket !== this.rendering) return - const where = ws.kind === 'folder' ? `📁 ${esc(ws.root.name)}` : esc(t('web_files_in_browser')) - const hint = t('web_files_save_hint_fmt', todayFolder()) - this.body.innerHTML = ` -

    ${where}

    -
    ${rows.join('') || `

    ${esc(t('web_files_empty'))}

    `}
    -

    ${esc(hint)}

    - ` - } - - private startHTML(): string { - if (this.pending) { - return `

    ${esc(t('web_files_reconnect_hint'))}

    - - ` - } - if (canPickFolder()) { - return `

    ${esc(t('web_files_connect_hint'))}

    - ` - } - return `

    ${esc(t('web_files_browser_hint'))}

    - ` - } - - private async onTreeClick(e: Event) { - const target = e.target as HTMLElement - const start = target.closest('[data-start]')?.dataset.start - if (start === 'pick') return this.connectFolder() - if (start === 'reconnect') return this.reconnect() - if (start === 'browser') return this.useBrowser() - const row = target.closest('.files-row') - if (!row || !this.workspace) return - const path = row.dataset.path! - if (target.closest('.files-delete')) return this.remove(row) - if (row.dataset.kind === 'dir') { - if (this.expanded.has(path)) this.expanded.delete(path) - else this.expanded.add(path) - this.rememberExpanded() - return this.render() - } - if (row.dataset.kind === 'program') return this.openProgram(path) - } - - private async locate(path: string): Promise<{ dir: WsDir; name: string }> { - const parts = path.split('/') - let dir = this.workspace!.root - for (const part of parts.slice(0, -1)) dir = await dir.dir(part) - return { dir, name: parts.at(-1)! } - } - - private async openProgram(path: string) { - try { - const { dir, name } = await this.locate(path) - const base = name.replace(/\.(visustruct|java)$/i, '') - const xml = (await dir.has(`${base}.visustruct`)) ? await dir.read(`${base}.visustruct`) : undefined - const java = (await dir.has(`${base}.java`)) ? await dir.read(`${base}.java`) : undefined - if (xml === undefined && java === undefined) return - const file = { dir, base, path: path.replace(/\.(visustruct|java)$/i, '') } - this.host.open(xml ?? '', file, java) - } catch (err) { - this.host.message((err as Error).message) - } - } - - private async remove(row: HTMLElement) { - const path = row.dataset.path! - const program = row.dataset.kind === 'program' - const label = program ? path.replace(/\.(visustruct|java)$/i, '') : path - if ((await ask({ title: t('web_files_delete_confirm_fmt', label), ok: t('web_files_delete'), danger: true })) === undefined) return - try { - const { dir, name } = await this.locate(path) - if (program) { - const base = name.replace(/\.(visustruct|java)$/i, '') - for (const ext of ['.visustruct', '.java']) if (await dir.has(base + ext)) await dir.remove(base + ext) - } else { - await dir.remove(name) - } - } catch (err) { - this.host.message((err as Error).message) - } - await this.render() - } - - private async newFolder() { - if (!this.workspace) return - const name = validFolderName((await ask({ title: t('web_files_folder_prompt'), input: '', ok: t('web_files_create') })) ?? '') - if (!name) return - try { - await this.workspace.root.dir(name, true) - } catch (err) { - this.host.message((err as Error).message) - } - await this.render() - } - - // MARK: - Speichern - - /** - * Programm speichern: in seinen bisherigen Ordner, sonst in den Tagesordner im Workspace (wird angelegt). - * `undefined`, wenn noch kein Workspace gewählt ist. - */ - async save(title: string, xml: string, java: string | undefined, file?: WorkspaceFile): Promise { - const ws = this.workspace - if (!ws) return undefined - let target = file - if (!target) { - const day = todayFolder() - const dir = await ws.root.dir(day, true) - const base = uniqueBaseName(programBaseName(title, t('web_files_default_name')), (await dir.list()).map((e) => e.name)) - target = { dir, base, path: `${day}/${base}` } - this.expanded.add(day) - this.rememberExpanded() - } - await target.dir.write(`${target.base}.visustruct`, xml) - if (java !== undefined) await target.dir.write(`${target.base}.java`, java) - await this.render() - return target - } -} diff --git a/web/src/ui/flowApp.ts b/web/src/ui/flowApp.ts deleted file mode 100644 index b2d55148..00000000 --- a/web/src/ui/flowApp.ts +++ /dev/null @@ -1,792 +0,0 @@ -import { - addNode, - connect, - emptyFlow, - FLOW_FILL, - FLOW_GRID, - FLOW_LINE_HEIGHT, - FLOW_SHAPES, - flowBox, - flowFromJSON, - flowPorts, - flowToJSON, - freePort, - hasInput, - moveNode, - portPoint, - portSide, - removeEdge, - removeNode, - routeEdge, - setText, - swapYesNo, - type FlowBox, - type FlowDoc, - type FlowNode, - type FlowPort, - type FlowShape, -} from '../core/flowModel' -import { gearIcon } from './app' -import { buildDiagramPDF, canvasRGB, canvasToPNG, exportFilenameSlug, rasterize, svgMarkup, type ExportImage } from './exporter' -import { currentLanguage, t } from './i18n' -import { worldHomeURL } from './site' -import { icons } from './icons' -import { setVisuBotMode, setVisuObjectWorld, worldChooserHTML, worldTitleHTML, type WorldChoice } from './visubotView' - -/** - * Welt VisuFlow (…/app/?visuflow): einen PAP (flowchart) frei zeichnen. Symbole aus der Leiste ziehen oder antippen, - * Text eintippen, Pfeile vom Punkt am Ausgang zum nächsten Symbol ziehen, als Datei speichern und als Bild/PDF exportieren. - */ - -declare const __APP_VERSION__: string - -const SVG = 'http://www.w3.org/2000/svg' -const docKey = 'visuflow.doc' -const maxUndoSteps = 100 -const fontSize = 14 -const font = `${fontSize}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif` -const ink = '#1c1c1e' - -const e = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) - -function stored(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function store(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher nur für diese Sitzung */ - } -} - -function siteURL(): string { - return worldHomeURL('visustruct', currentLanguage()) -} - -function download(name: string, data: BlobPart, type: string) { - const url = URL.createObjectURL(new Blob([data], { type })) - const a = document.createElement('a') - a.href = url - a.download = name - a.click() - setTimeout(() => URL.revokeObjectURL(url), 1000) -} - -function s(tag: string, attrs: Record = {}): SVGElement { - const el = document.createElementNS(SVG, tag) - for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, String(v)) - return el -} - -let measureCtx: CanvasRenderingContext2D | null | undefined -function measure(text: string): number { - if (measureCtx === undefined) measureCtx = document.createElement('canvas').getContext('2d') - if (!measureCtx) return text.length * fontSize * 0.55 - measureCtx.font = font - return measureCtx.measureText(text).width -} - -const shapeName = (shape: FlowShape) => t(`web_visuflow_shape_${shape}`) -const defaultText = (shape: FlowShape) => t(`web_visuflow_text_${shape}`) - -/** Umriss eines Symbols nach DIN 66001. */ -function shapePath(shape: FlowShape, b: { x: number; y: number; w: number; h: number }): SVGElement { - const l = b.x - b.w / 2 - const r = b.x + b.w / 2 - const top = b.y - b.h / 2 - const bot = b.y + b.h / 2 - if (shape === 'verzweigung') return s('path', { d: `M${b.x} ${top} L${r} ${b.y} L${b.x} ${bot} L${l} ${b.y} Z` }) - if (shape === 'ein') return s('path', { d: `M${l + 14} ${top} L${r} ${top} L${r - 14} ${bot} L${l} ${bot} Z` }) - if (shape === 'unterprogramm') return s('path', { d: `M${l} ${top} H${r} V${bot} H${l} Z M${l + 10} ${top} V${bot} M${r - 10} ${top} V${bot}` }) - return s('rect', { x: l, y: top, width: b.w, height: b.h, rx: shape === 'anweisung' ? 2 : b.h / 2 }) -} - -function textLines(box: FlowBox): SVGElement { - const text = s('text', { 'text-anchor': 'middle', 'font-size': fontSize, fill: ink }) - box.lines.forEach((line, i) => { - const span = s('tspan', { x: box.x, y: box.y + 5 + (i - (box.lines.length - 1) / 2) * FLOW_LINE_HEIGHT }) - span.textContent = line - text.append(span) - }) - return text -} - -/** Kleines Symbol für die Leiste. */ -function paletteIcon(shape: FlowShape): string { - const svg = s('svg', { viewBox: '0 0 64 36', width: 64, height: 36, 'aria-hidden': 'true' }) - const p = shapePath(shape, { x: 32, y: 18, w: shape === 'verzweigung' ? 54 : 56, h: shape === 'verzweigung' ? 32 : 26 }) - p.setAttribute('fill', FLOW_FILL[shape]) - p.setAttribute('stroke', ink) - p.setAttribute('stroke-width', '1.5') - svg.append(p) - return svg.outerHTML -} - -/** Beispiel aus Lektion 1: Tee kochen mit Schleife „Tee noch zu heiß?“. */ -function exampleFlow(): FlowDoc { - let doc = emptyFlow(t('web_visuflow_example_title')) - const ids: string[] = [] - const add = (shape: FlowShape, x: number, y: number, text: string) => { - const r = addNode(doc, shape, x, y, text) - doc = r.doc - ids.push(r.id) - } - add('start', 240, 60, defaultText('start')) - add('anweisung', 240, 160, t('web_visuflow_example_water')) - add('anweisung', 240, 260, t('web_visuflow_example_bag')) - add('verzweigung', 240, 380, t('web_visuflow_example_hot')) - add('anweisung', 480, 380, t('web_visuflow_example_wait')) - add('anweisung', 240, 500, t('web_visuflow_example_drink')) - add('ende', 240, 600, defaultText('ende')) - const [start, water, bag, hot, wait, drink, end] = ids - doc = connect(doc, start, 'out', water) - doc = connect(doc, water, 'out', bag) - doc = connect(doc, bag, 'out', hot) - doc = swapYesNo(doc, hot) - doc = connect(doc, hot, 'ja', wait) - doc = connect(doc, wait, 'out', hot) - doc = connect(doc, hot, 'nein', drink) - doc = connect(doc, drink, 'out', end) - return doc -} - -function startFlow(): FlowDoc { - return addNode(emptyFlow(), 'start', 240, 60, defaultText('start')).doc -} - -type Selection = { kind: 'node' | 'edge'; id: string } - -export class FlowApp { - private shell: HTMLElement - private svg!: SVGSVGElement - private scroller!: HTMLElement - private status!: HTMLElement - private titleInput!: HTMLInputElement - private doc: FlowDoc - private selection?: Selection - private history: string[] = [] - private future: string[] = [] - private editor?: HTMLTextAreaElement - /** Pfeil, der gerade gezogen wird. */ - private dragLine?: SVGElement - - constructor(root: HTMLElement) { - document.documentElement.classList.add('visuflow-theme') - history.replaceState(history.state, '', `${location.pathname}?visuflow${location.hash}`) - this.doc = flowFromJSON(stored(docKey) ?? '') ?? exampleFlow() - this.shell = document.createElement('div') - this.shell.className = 'shell visuflow' - root.replaceWith(this.shell) - this.build() - this.render() - window.addEventListener('keydown', (ev) => this.onKey(ev)) - } - - // MARK: - Aufbau - - private build() { - const button = (action: string, label: string, icon: string, tip = label) => - `` - const palette = FLOW_SHAPES.map( - (shape) => ``, - ).join('') - this.shell.innerHTML = ` -
    - VisuFlow ${e(t('web_visuflow_subtitle'))} -
    - ${button('new', t('toolbar_new'), icons.newFile, t('web_visuflow_new_tip'))} - ${button('open', t('menu_open'), icons.open, t('web_visuflow_open_tip'))} - ${button('save', t('menu_save'), icons.save, t('web_visuflow_save_tip'))} - ${button('undo', t('toolbar_undo'), icons.undo)} - ${button('redo', t('toolbar_redo'), icons.redo)} - ${button('delete', t('web_visuflow_delete'), icons.trash, t('web_visuflow_delete_tip'))} - - -
    -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - -
    -

    -
    -
    - ` - const $ = (q: string) => this.shell.querySelector(q)! - this.svg = $('.vf-canvas') - this.scroller = $('.vf-scroll') - this.status = $('.vf-status') - this.titleInput = $('.vf-title') - const on = (action: string, run: () => void) => $(`[data-action=${action}]`).addEventListener('click', run) - on('new', () => this.replace(startFlow())) - on('example', () => this.replace(exampleFlow())) - on('open', () => $('input[type=file]').click()) - on('save', () => this.save()) - on('undo', () => this.undo(false)) - on('redo', () => this.undo(true)) - on('delete', () => this.deleteSelection()) - on('swap', () => { - if (this.selection?.kind === 'node') this.change(swapYesNo(this.doc, this.selection.id)) - }) - const fileInput = $('input[type=file]') - fileInput.addEventListener('change', async () => { - const f = fileInput.files?.[0] - fileInput.value = '' - if (!f) return - const doc = flowFromJSON(await f.text()) - if (!doc) return this.showStatus(t('web_visuflow_open_error')) - if (!doc.title) doc.title = f.name.replace(/\.(visuflow|json)$/i, '') - this.replace(doc) - }) - - let titleBefore: string | undefined - this.titleInput.addEventListener('focus', () => (titleBefore = flowToJSON(this.doc))) - this.titleInput.addEventListener('input', () => { - this.doc = { ...this.doc, title: this.titleInput.value } - store(docKey, flowToJSON(this.doc)) - }) - this.titleInput.addEventListener('change', () => { - if (titleBefore !== undefined && titleBefore !== flowToJSON(this.doc)) this.pushUndo(titleBefore) - titleBefore = flowToJSON(this.doc) - }) - - // Export - const exportButton = $('[data-action=export]') - const exportMenu = $('.vf-menu') - const showExport = (show: boolean) => { - exportMenu.hidden = !show - exportButton.setAttribute('aria-expanded', String(show)) - } - exportButton.addEventListener('click', () => showExport(exportMenu.hidden)) - exportMenu.addEventListener('click', (ev) => { - const kind = (ev.target as HTMLElement).closest('[data-export]')?.dataset.export - if (!kind) return - showExport(false) - void this.export(kind as 'png' | 'svg' | 'pdf') - }) - // Einstellungen mit der Welten-Auswahl - const settings = $('[data-action=settings]') - const menu = $('.settings-menu') - settings.addEventListener('click', () => { - menu.hidden = !menu.hidden - settings.setAttribute('aria-expanded', String(!menu.hidden)) - }) - document.addEventListener('pointerdown', (ev) => { - const target = ev.target as HTMLElement - if (!menu.hidden && !target.closest('.settings')) menu.hidden = true - if (!exportMenu.hidden && !target.closest('.vf-export')) showExport(false) - }) - for (const b of menu.querySelectorAll('[data-world]')) b.addEventListener('click', () => this.chooseWorld(b.dataset.world as WorldChoice)) - - for (const b of this.shell.querySelectorAll('.vf-shape')) this.setupPaletteButton(b) - this.svg.addEventListener('pointerdown', (ev) => this.onPointerDown(ev)) - this.svg.addEventListener('dblclick', (ev) => { - const id = (ev.target as Element).closest('[data-node]')?.dataset.node - if (id) this.editText(id) - }) - } - - private chooseWorld(world: WorldChoice) { - if (world === 'flow') return - if (world === 'object') { - setVisuObjectWorld() - location.href = `${location.pathname}?visuobject${location.hash}` - return - } - setVisuBotMode(world) - location.href = `${location.pathname}?${world === 'on' ? 'visubot' : 'visustruct'}${location.hash}` - } - - // MARK: - Zustand und Rückgängig - - private pushUndo(before: string) { - this.history.push(before) - if (this.history.length > maxUndoSteps) this.history.shift() - this.future = [] - } - - private change(next: FlowDoc, select?: Selection | null) { - if (next === this.doc) return - this.pushUndo(flowToJSON(this.doc)) - this.doc = next - if (select !== undefined) this.selection = select ?? undefined - this.afterChange() - } - - private replace(doc: FlowDoc) { - this.change(doc, null) - this.scroller.scrollTo(0, 0) - } - - private afterChange() { - store(docKey, flowToJSON(this.doc)) - if (this.selection && !this.find(this.selection)) this.selection = undefined - this.render() - } - - private find(sel: Selection) { - return sel.kind === 'node' ? this.doc.nodes.find((n) => n.id === sel.id) : this.doc.edges.find((x) => x.id === sel.id) - } - - private undo(redo: boolean) { - this.closeEditor(true) - const from = redo ? this.future : this.history - const to = redo ? this.history : this.future - const text = from.pop() - if (text === undefined) return - to.push(flowToJSON(this.doc)) - this.doc = flowFromJSON(text) ?? this.doc - this.afterChange() - } - - private deleteSelection() { - const sel = this.selection - if (!sel) return - this.change(sel.kind === 'node' ? removeNode(this.doc, sel.id) : removeEdge(this.doc, sel.id), null) - } - - private save() { - const base = exportFilenameSlug(this.doc.title) || 'PAP' - download(`${base}.visuflow`, flowToJSON(this.doc), 'application/json') - this.showStatus(t('web_visuflow_saved', `${base}.visuflow`)) - } - - // MARK: - Zeichnen - - private boxes(doc = this.doc) { - return new Map(doc.nodes.map((n) => [n.id, flowBox(n, measure)])) - } - - /** Zeichnet den Plan; `exporting` ohne Punkte an den Ausgängen und ohne Auswahl. */ - private drawInto(svg: SVGSVGElement, exporting: boolean) { - const boxes = this.boxes() - const yes = t('web_learn_pap_yes') - const no = t('web_learn_pap_no') - svg.innerHTML = `` - const edges = s('g', { class: 'vf-edges' }) - const labels = s('g', { class: 'vf-labels', 'font-size': 12, 'font-weight': 600 }) - const nodes = s('g', { class: 'vf-nodes' }) - const ports = s('g', { class: 'vf-ports' }) - let lane = 0 - for (const edge of this.doc.edges) { - const from = this.doc.nodes.find((n) => n.id === edge.from)! - const a = boxes.get(edge.from)! - const b = boxes.get(edge.to)! - const side = portSide(this.doc, from, edge.port) - const back = b.y - b.h / 2 < portPoint(a, side)[1] + FLOW_GRID - const pts = routeEdge(a, side, b, back ? lane++ : 0) - const points = pts.map((p) => p.join(',')).join(' ') - const g = s('g', { 'data-edge': edge.id, class: `vf-edge${!exporting && this.selection?.kind === 'edge' && this.selection.id === edge.id ? ' selected' : ''}` }) - if (!exporting) g.append(s('polyline', { points, class: 'vf-edge-hit', fill: 'none', stroke: 'transparent', 'stroke-width': 16 })) - g.append(s('polyline', { points, class: 'vf-edge-line', fill: 'none', stroke: ink, 'stroke-width': 2, 'marker-end': 'url(#vf-arrow)' })) - edges.append(g) - } - for (const node of this.doc.nodes) { - const b = boxes.get(node.id)! - const selected = !exporting && this.selection?.kind === 'node' && this.selection.id === node.id - const g = s('g', { 'data-node': node.id, class: `vf-node${selected ? ' selected' : ''}` }) - const p = shapePath(node.shape, b) - p.setAttribute('fill', FLOW_FILL[node.shape]) - p.setAttribute('stroke', ink) - p.setAttribute('stroke-width', '2') - g.append(p, textLines(b)) - nodes.append(g) - for (const port of flowPorts(node.shape)) { - const side = portSide(this.doc, node, port) - const [px, py] = portPoint(b, side) - const used = this.doc.edges.some((x) => x.from === node.id && x.port === port) - if (port !== 'out' && (used || !exporting)) { - const lbl = s('text', side === 'bottom' ? { x: px + 7, y: py + 15 } : { x: px + 6, y: py - 7 }) - lbl.setAttribute('fill', port === 'ja' ? '#248a3d' : '#d70015') - lbl.textContent = port === 'ja' ? yes : no - labels.append(lbl) - } - if (exporting) continue - const pg = s('g', { 'data-port': port, 'data-from': node.id, class: `vf-port${used ? ' used' : ''}` }) - pg.append(s('circle', { cx: px, cy: py, r: 16, fill: 'transparent' }), s('circle', { cx: px, cy: py, r: 6, class: 'vf-port-dot' })) - ports.append(pg) - } - } - svg.append(edges, nodes, labels, ports) - return boxes - } - - private render() { - this.closeEditor(true) - const boxes = this.drawInto(this.svg, false) - let w = 0 - let h = 0 - for (const b of boxes.values()) { - w = Math.max(w, b.x + b.w / 2) - h = Math.max(h, b.y + b.h / 2) - } - // Platz zum Weiterzeichnen nach rechts und unten. - const width = Math.max(this.scroller.clientWidth, w + 300) - const height = Math.max(this.scroller.clientHeight, h + 300) - this.svg.setAttribute('width', String(width)) - this.svg.setAttribute('height', String(height)) - this.svg.setAttribute('viewBox', `0 0 ${width} ${height}`) - if (document.activeElement !== this.titleInput) this.titleInput.value = this.doc.title - const sel = this.selection - const node = sel?.kind === 'node' ? this.doc.nodes.find((n) => n.id === sel.id) : undefined - this.shell.querySelector('[data-action=swap]')!.disabled = node?.shape !== 'verzweigung' - this.shell.querySelector('[data-action=delete]')!.disabled = !sel - this.shell.querySelector('[data-action=undo]')!.disabled = this.history.length === 0 - this.shell.querySelector('[data-action=redo]')!.disabled = this.future.length === 0 - this.showStatus(t(this.doc.nodes.length === 0 ? 'web_visuflow_status_empty' : sel?.kind === 'edge' ? 'web_visuflow_status_edge' : sel ? 'web_visuflow_status_node' : 'web_visuflow_status_idle')) - } - - private showStatus(text: string) { - this.status.textContent = text - } - - // MARK: - Leiste - - /** Symbol antippen hängt es an das gewählte an, Ziehen legt es auf die Fläche. */ - private setupPaletteButton(button: HTMLButtonElement) { - const shape = button.dataset.shape as FlowShape - button.addEventListener('pointerdown', (ev) => { - if (ev.button !== 0) return - ev.preventDefault() - const start = { x: ev.clientX, y: ev.clientY } - let ghost: HTMLElement | undefined - const move = (m: PointerEvent) => { - if (!ghost && Math.hypot(m.clientX - start.x, m.clientY - start.y) < 6) return - if (!ghost) { - ghost = document.createElement('div') - ghost.className = 'vf-ghost' - ghost.innerHTML = paletteIcon(shape) - document.body.append(ghost) - } - ghost.style.left = `${m.clientX}px` - ghost.style.top = `${m.clientY}px` - } - const up = (u: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - window.removeEventListener('pointercancel', up) - if (!ghost) return this.appendShape(shape) - ghost.remove() - if (u.type === 'pointercancel') return - const r = this.scroller.getBoundingClientRect() - if (u.clientX < r.left || u.clientX > r.right || u.clientY < r.top || u.clientY > r.bottom) return - const p = this.toCanvas(u) - const { doc, id } = addNode(this.doc, shape, p.x, p.y, defaultText(shape)) - this.change(doc, { kind: 'node', id }) - this.editText(id) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - window.addEventListener('pointercancel', up) - }) - button.addEventListener('keydown', (ev) => { - if (ev.key !== 'Enter' && ev.key !== ' ') return - ev.preventDefault() - this.appendShape(shape) - }) - } - - /** Neues Symbol unter (oder bei „nein“ rechts neben) das gewählte und gleich mit einem Pfeil verbinden. */ - private appendShape(shape: FlowShape) { - const sel = this.selection - const from = sel?.kind === 'node' ? this.doc.nodes.find((n) => n.id === sel.id) : undefined - const port = from && hasInput(shape) ? freePort(this.doc, from) : undefined - const probe = flowBox({ id: '', shape, x: 0, y: 0, text: defaultText(shape) }, measure) - let x = 240 - let y = 60 - if (from && port) { - const b = flowBox(from, measure) - if (portSide(this.doc, from, port) === 'right') { - x = b.x + b.w / 2 + 3 * FLOW_GRID + probe.w / 2 - y = b.y - } else { - x = b.x - y = b.y + b.h / 2 + 3 * FLOW_GRID + probe.h / 2 - } - } else if (this.doc.nodes.length) { - const boxes = [...this.boxes().values()] - const low = boxes.reduce((a, b) => (b.y + b.h / 2 > a.y + a.h / 2 ? b : a)) - x = from ? flowBox(from, measure).x : low.x - y = low.y + low.h / 2 + 3 * FLOW_GRID + probe.h / 2 - } - let { doc, id } = addNode(this.doc, shape, x, y, defaultText(shape)) - if (from && port) doc = connect(doc, from.id, port, id) - this.change(doc, { kind: 'node', id }) - const node = this.svg.querySelector(`[data-node="${id}"]`) - node?.scrollIntoView?.({ block: 'nearest', inline: 'nearest' }) - this.editText(id) - } - - // MARK: - Zeichenfläche - - private toCanvas(ev: { clientX: number; clientY: number }) { - const r = this.svg.getBoundingClientRect() - return { x: ev.clientX - r.left, y: ev.clientY - r.top } - } - - private nodeAt(p: { x: number; y: number }, except?: string): FlowNode | undefined { - const boxes = this.boxes() - return [...this.doc.nodes].reverse().find((n) => { - const b = boxes.get(n.id)! - return n.id !== except && Math.abs(p.x - b.x) <= b.w / 2 + 4 && Math.abs(p.y - b.y) <= b.h / 2 + 4 - }) - } - - private onPointerDown(ev: PointerEvent) { - if (ev.button !== 0) return - const target = ev.target as Element - const port = target.closest('[data-port]') - if (port) return this.dragArrow(ev, port.dataset.from!, port.dataset.port as FlowPort) - const nodeId = target.closest('[data-node]')?.dataset.node - if (nodeId) return this.dragNode(ev, nodeId) - const edgeId = target.closest('[data-edge]')?.dataset.edge - this.closeEditor(true) - this.selection = edgeId ? { kind: 'edge', id: edgeId } : undefined - this.render() - } - - private dragNode(ev: PointerEvent, id: string) { - ev.preventDefault() - const wasSelected = this.selection?.kind === 'node' && this.selection.id === id - this.closeEditor(true) - this.selection = { kind: 'node', id } - this.render() - const node = this.doc.nodes.find((n) => n.id === id)! - const start = this.toCanvas(ev) - const before = flowToJSON(this.doc) - const origin = { x: node.x, y: node.y } - let moved = false - const move = (m: PointerEvent) => { - const p = this.toCanvas(m) - if (!moved && Math.hypot(p.x - start.x, p.y - start.y) < 5) return - moved = true - this.doc = moveNode(this.doc, id, origin.x + p.x - start.x, origin.y + p.y - start.y) - this.render() - } - const up = () => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - window.removeEventListener('pointercancel', up) - if (moved) { - if (flowToJSON(this.doc) !== before) this.pushUndo(before) - this.afterChange() - } else if (wasSelected) this.editText(id) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - window.addEventListener('pointercancel', up) - } - - /** Pfeil vom Ausgang ziehen und über dem Zielsymbol loslassen. */ - private dragArrow(ev: PointerEvent, from: string, port: FlowPort) { - ev.preventDefault() - this.closeEditor(true) - const node = this.doc.nodes.find((n) => n.id === from)! - const [sx, sy] = portPoint(flowBox(node, measure), portSide(this.doc, node, port)) - this.dragLine = s('line', { x1: sx, y1: sy, x2: sx, y2: sy, class: 'vf-drag-line', 'marker-end': 'url(#vf-arrow)' }) - this.svg.append(this.dragLine) - this.showStatus(t('web_visuflow_status_connect')) - let hover: SVGElement | null = null - const move = (m: PointerEvent) => { - const p = this.toCanvas(m) - this.dragLine!.setAttribute('x2', String(p.x)) - this.dragLine!.setAttribute('y2', String(p.y)) - const target = this.nodeAt(p, from) - hover?.classList.remove('drop-target') - hover = target && hasInput(target.shape) ? this.svg.querySelector(`[data-node="${target.id}"]`) : null - hover?.classList.add('drop-target') - } - const up = (u: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - window.removeEventListener('pointercancel', up) - this.dragLine?.remove() - this.dragLine = undefined - const target = u.type === 'pointercancel' ? undefined : this.nodeAt(this.toCanvas(u), from) - if (target && hasInput(target.shape)) this.change(connect(this.doc, from, port, target.id), { kind: 'node', id: target.id }) - else this.render() - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - window.addEventListener('pointercancel', up) - } - - // MARK: - Text eintippen - - private editText(id: string) { - this.closeEditor(true) - const node = this.doc.nodes.find((n) => n.id === id) - if (!node) return - const b = flowBox(node, measure) - const area = document.createElement('textarea') - area.className = 'vf-editor' - area.value = node.text - area.setAttribute('aria-label', t('web_visuflow_edit_label')) - const inner = node.shape === 'verzweigung' ? 0.62 : 0.92 - const w = Math.max(110, b.w * inner) - area.style.left = `${this.svg.getBoundingClientRect().left - this.scroller.getBoundingClientRect().left + this.scroller.scrollLeft + b.x - w / 2}px` - area.style.top = `${this.svg.getBoundingClientRect().top - this.scroller.getBoundingClientRect().top + this.scroller.scrollTop + b.y - Math.max(28, b.lines.length * FLOW_LINE_HEIGHT + 10) / 2}px` - area.style.width = `${w}px` - area.style.height = `${Math.max(28, b.lines.length * FLOW_LINE_HEIGHT + 10)}px` - area.dataset.node = id - this.scroller.append(area) - this.editor = area - area.focus() - area.select() - const fit = () => { - area.style.height = 'auto' - area.style.height = `${area.scrollHeight}px` - } - area.addEventListener('input', fit) - area.addEventListener('keydown', (k) => { - k.stopPropagation() - if (k.key === 'Enter' && !k.shiftKey) { - k.preventDefault() - this.closeEditor(true) - } else if (k.key === 'Escape') { - k.preventDefault() - this.closeEditor(false) - } - }) - area.addEventListener('blur', () => this.closeEditor(true)) - } - - private closeEditor(commit: boolean) { - const area = this.editor - if (!area) return - this.editor = undefined - area.remove() - const id = area.dataset.node! - const node = this.doc.nodes.find((n) => n.id === id) - const text = area.value.trim() - if (commit && node && text !== node.text) this.change(setText(this.doc, id, text)) - } - - // MARK: - Tastatur - - private onKey(ev: KeyboardEvent) { - const typing = (ev.target as HTMLElement).closest?.('input, textarea, [contenteditable]') - const mod = ev.ctrlKey || ev.metaKey - if (mod && ev.key.toLowerCase() === 's') { - ev.preventDefault() - return this.save() - } - if (typing) return - if (mod && ev.key.toLowerCase() === 'z') { - ev.preventDefault() - return this.undo(ev.shiftKey) - } - if (mod && ev.key.toLowerCase() === 'y') { - ev.preventDefault() - return this.undo(true) - } - const sel = this.selection - if (!sel) return - if (ev.key === 'Delete' || ev.key === 'Backspace') { - ev.preventDefault() - return this.deleteSelection() - } - if (sel.kind !== 'node') return - if (ev.key === 'Enter' || ev.key === 'F2') { - ev.preventDefault() - return this.editText(sel.id) - } - const step = { ArrowLeft: [-1, 0], ArrowRight: [1, 0], ArrowUp: [0, -1], ArrowDown: [0, 1] }[ev.key] - if (!step) return - ev.preventDefault() - const node = this.doc.nodes.find((n) => n.id === sel.id)! - this.change(moveNode(this.doc, sel.id, node.x + step[0] * FLOW_GRID, node.y + step[1] * FLOW_GRID)) - } - - // MARK: - Export - - /** Plan ohne Auswahl und Punkte, zugeschnitten, mit Titel oben. */ - private exportImage(): ExportImage | undefined { - if (this.doc.nodes.length === 0) return undefined - const svg = document.createElementNS(SVG, 'svg') as SVGSVGElement - const boxes = this.drawInto(svg, true) - let x0 = Infinity - let y0 = Infinity - let x1 = -Infinity - let y1 = -Infinity - for (const b of boxes.values()) { - x0 = Math.min(x0, b.x - b.w / 2) - y0 = Math.min(y0, b.y - b.h / 2) - x1 = Math.max(x1, b.x + b.w / 2) - y1 = Math.max(y1, b.y + b.h / 2) - } - // Pfeile laufen rechts außen vorbei, Beschriftungen stehen neben den Ausgängen. - for (const pl of svg.querySelectorAll('polyline')) { - for (const pair of (pl.getAttribute('points') ?? '').split(' ')) { - const [px, py] = pair.split(',').map(Number) - x0 = Math.min(x0, px) - x1 = Math.max(x1, px + 4) - y0 = Math.min(y0, py) - y1 = Math.max(y1, py) - } - } - x1 += 30 - const margin = 20 - const title = this.doc.title.trim() - const titleH = title ? 32 : 0 - const width = Math.ceil(x1 - x0 + 2 * margin) - const height = Math.ceil(y1 - y0 + 2 * margin + titleH) - svg.setAttribute('xmlns', SVG) - svg.setAttribute('width', String(width)) - svg.setAttribute('height', String(height)) - svg.setAttribute('viewBox', `${x0 - margin} ${y0 - margin - titleH} ${width} ${height}`) - svg.setAttribute('font-family', font.replace(/^\d+px /, '')) - const bg = s('rect', { x: x0 - margin, y: y0 - margin - titleH, width, height, fill: '#ffffff' }) - svg.insertBefore(bg, svg.firstChild!.nextSibling) - if (title) { - const text = s('text', { x: x0 - margin + 12, y: y0 - margin - titleH + 26, 'font-size': 16, 'font-weight': 700, fill: ink }) - text.textContent = title - svg.append(text) - } - return { svg, width, height } - } - - private async export(kind: 'png' | 'svg' | 'pdf') { - const image = this.exportImage() - if (!image) return this.showStatus(t('web_visuflow_status_empty')) - const base = exportFilenameSlug(this.doc.title) || 'PAP' - if (kind === 'svg') return download(`${base}.svg`, svgMarkup(image), 'image/svg+xml') - const canvas = await rasterize(image, kind === 'png' ? 2 : 3) - if (kind === 'png') return download(`${base}.png`, await canvasToPNG(canvas), 'image/png') - const pdf = await buildDiagramPDF(canvasRGB(canvas), canvas.width, canvas.height, image, this.doc.title.trim() || 'PAP') - download(`${base}.pdf`, pdf, 'application/pdf') - } -} diff --git a/web/src/ui/i18n.ts b/web/src/ui/i18n.ts deleted file mode 100644 index ae913265..00000000 --- a/web/src/ui/i18n.ts +++ /dev/null @@ -1,187 +0,0 @@ -/** - * Sprachen wie `AppLanguage.swift`: Texte aus den `Localizable.strings` der App, dazu - * `src/i18n/.strings` für Texte, die es nur in der Web-Version gibt. - * Fehlt ein Text in einer Sprache, gilt der englische (nie der Schlüssel selbst). - */ - -export const languages = [ - { code: 'de', name: 'Deutsch' }, - { code: 'en', name: 'English' }, - { code: 'es', name: 'Español' }, - { code: 'fr', name: 'Français' }, - { code: 'it', name: 'Italiano' }, - { code: 'pl', name: 'Polski' }, - { code: 'tr', name: 'Türkçe' }, - { code: 'ko', name: '한국어' }, - { code: 'ja', name: '日本語' }, - { code: 'el', name: 'Ελληνικά' }, - { code: 'uk', name: 'Українська' }, - { code: 'ar', name: 'العربية' }, - { code: 'ru', name: 'Русский' }, - { code: 'he', name: 'עברית' }, - { code: 'nl', name: 'Nederlands' }, - { code: 'sv', name: 'Svenska' }, - { code: 'da', name: 'Dansk' }, - { code: 'nb', name: 'Norsk bokmål' }, - { code: 'zh-Hans', name: '中文(简体)' }, - { code: 'zh-Hant', name: '中文(繁體)' }, - { code: 'pt-PT', name: 'Português' }, - { code: 'java', name: 'Java (Keywords)' }, -] as const - -export type LanguageCode = (typeof languages)[number]['code'] - -/** - * Sprachen, die die Web-App zur Auswahl anbietet (Holger, 2026-10-22). Die übrigen bleiben übersetzt und lassen sich hier wieder einschalten. - * Java ist keine Sprache mehr, sondern der Schalter „Code-Keywords“ in den Einstellungen (Holger, 2026-10-23). - */ -const offered: readonly LanguageCode[] = ['de', 'en', 'pt-PT'] -export const offeredLanguages = languages.filter((l) => offered.includes(l.code)) -const isOffered = (code: string): code is LanguageCode => offered.includes(code as LanguageCode) - -const appStrings = import.meta.glob('../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/*.lproj/Localizable.strings', { - query: '?raw', - import: 'default', -}) as Record Promise> -const javaStrings = import.meta.glob('../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Java.strings', { - query: '?raw', - import: 'default', -}) as Record Promise> -const webStrings = import.meta.glob('../i18n/*.strings', { query: '?raw', import: 'default' }) as Record< - string, - () => Promise -> - -function loader(table: Record Promise>, suffix: string): (() => Promise) | undefined { - return Object.entries(table).find(([path]) => path.endsWith(suffix))?.[1] -} - -/** Liest das Apple-Format `"schlüssel" = "wert";` (Kommentare werden übersprungen). */ -export function parseStrings(source: string): Map { - const map = new Map() - const withoutComments = source.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '') - const re = /"((?:[^"\\]|\\.)*)"\s*=\s*"((?:[^"\\]|\\.)*)"\s*;/g - const unescape = (s: string) => - s.replace(/\\(u[0-9a-fA-F]{4}|.)/g, (_, c: string) => - c === 'n' ? '\n' : c === 't' ? '\t' : c.startsWith('u') && c.length === 5 ? String.fromCharCode(parseInt(c.slice(1), 16)) : c, - ) - for (const m of withoutComments.matchAll(re)) map.set(unescape(m[1]), unescape(m[2])) - return map -} - -async function loadTable(code: LanguageCode): Promise> { - const app = code === 'java' ? loader(javaStrings, '/Java.strings') : loader(appStrings, `/${code}.lproj/Localizable.strings`) - const web = loader(webStrings, `/${code}.strings`) - const table = new Map() - for (const load of [app, web]) { - if (!load) continue - for (const [k, v] of parseStrings(await load())) table.set(k, v) - } - return table -} - -let current: LanguageCode = 'en' -let table = new Map() -let english = new Map() - -const storageKey = 'appLanguageCode' -const keywordsKey = 'visustruct.keywords' - -/** Code-Keywords: Elemente heißen if, while, for … statt Verzweigung, While-Schleife …, unabhängig von der Sprache. */ -let keywords = new Map() -let keywordsOn = false - -/** Nur Beschriftungen im Struktogramm aus `Java.strings`: Elementnamen und Platzhalter. */ -const isKeywordLabel = (key: string) => key.startsWith('element_') || key.startsWith('canvas_stub_') - -/** Früher war „Java (Keywords)“ eine Sprache: gespeichert oder per ?lang=java wird daraus Englisch mit Code-Keywords. */ -function migrateJavaLanguage(): boolean { - try { - const java = new URLSearchParams(location.search).get('lang') === 'java' || localStorage.getItem(storageKey) === 'java' - if (java) { - localStorage.setItem(storageKey, 'en') - localStorage.setItem(keywordsKey, '1') - } - return java - } catch { - return false - } -} - -export function storedCodeKeywords(): boolean { - try { - return localStorage.getItem(keywordsKey) === '1' - } catch { - return false - } -} - -export async function setCodeKeywords(on: boolean, remember = true) { - if (on && keywords.size === 0) { - if (english.size === 0) english = await loadTable('en') - const load = loader(javaStrings, '/Java.strings') - // Nur echte Keywords: Wo Java.strings nur Englisch ist (z. B. „Statement“), bleibt die gewählte Sprache. - if (load) for (const [k, v] of parseStrings(await load())) if (isKeywordLabel(k) && v !== english.get(k)) keywords.set(k, v) - } - keywordsOn = on - if (remember) { - try { - localStorage.setItem(keywordsKey, on ? '1' : '0') - } catch { - /* nur für diese Sitzung */ - } - } -} - -export function codeKeywords(): boolean { - return keywordsOn -} - -/** Wie `AppLanguage.deviceDefault`: erste Browsersprache, die VisuStruct anbietet, sonst Englisch. */ -export function preferredLanguage(): LanguageCode { - if (migrateJavaLanguage()) return 'en' - // Link von der Webseite, z. B. visustruct.visucode.de/app/?lang=de - const fromURL = new URLSearchParams(location.search).get('lang') - if (fromURL && isOffered(fromURL)) return fromURL - try { - const saved = localStorage.getItem(storageKey) - if (saved && isOffered(saved)) return saved - } catch { - /* ohne Speicher */ - } - for (const tag of navigator.languages ?? [navigator.language]) { - const t = tag.toLowerCase() - if (t.startsWith('pt')) return 'pt-PT' - const hit = offeredLanguages.find((l) => l.code === t.split('-')[0]) - if (hit) return hit.code - } - return 'en' -} - -export async function setLanguage(code: LanguageCode, remember = true) { - if (english.size === 0) english = await loadTable('en') - table = code === 'en' ? english : await loadTable(code) - current = code - if (remember) { - try { - localStorage.setItem(storageKey, code) - } catch { - /* nur für diese Sitzung */ - } - } - const root = document.documentElement - root.lang = code === 'java' ? 'en' : code - root.dir = code === 'ar' || code === 'he' ? 'rtl' : 'ltr' -} - -export function currentLanguage(): LanguageCode { - return current -} - -/** Übersetzung zu `key`; `%@`, `%d` und `%1$@` werden der Reihe nach durch `args` ersetzt. */ -export function t(key: string, ...args: (string | number)[]): string { - const text = (keywordsOn ? keywords.get(key) : undefined) ?? table.get(key) ?? english.get(key) ?? key - if (args.length === 0) return text - let next = 0 - return text.replace(/%(?:(\d+)\$)?[@d]/g, (_, pos: string | undefined) => String(args[pos ? Number(pos) - 1 : next++] ?? '')) -} diff --git a/web/src/ui/icons.ts b/web/src/ui/icons.ts deleted file mode 100644 index cbfc7541..00000000 --- a/web/src/ui/icons.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** Symbole für Werkzeugleisten: 24er-Raster, Strich in Textfarbe. Name steht im Tooltip (`data-tip`). */ - -const svg = (body: string, filled = false) => - `` - -export const icons = { - newFile: svg(''), - open: svg(''), - save: svg(''), - undo: svg(''), - redo: svg(''), - cut: svg(''), - copy: svg(''), - paste: svg(''), - trash: svg(''), - share: svg(''), - book: svg(''), - code: svg(''), - play: svg('', true), - /** Einzelschritt (wie „Step over“ in Entwicklungsumgebungen): Bogen über eine Zeile zur nächsten. */ - step: svg(''), - indent: svg(''), - stop: svg('', true), - folder: svg(''), - newFolder: svg(''), - diagram: svg(''), - fileText: svg(''), - pencil: svg(''), -} diff --git a/web/src/ui/inlineEdit.ts b/web/src/ui/inlineEdit.ts deleted file mode 100644 index 2e685072..00000000 --- a/web/src/ui/inlineEdit.ts +++ /dev/null @@ -1,189 +0,0 @@ -import type { LayoutNode, Rect } from '../core/layout' -import { normalizingQuotes } from '../core/lineEditorText' -import { ElementType } from '../core/model' -import { diagramFontFamily } from '../core/textMeasurement' - -/** Text direkt im Struktogramm bearbeiten (Doppelklick/Doppeltipp), ohne Umweg über den Objektinspektor. */ - -export interface InlineField { - value: string - placeholder: string - label: string -} - -export interface InlineEditOptions { - host: HTMLElement - /** Fläche im Diagramm (Diagramm-Koordinaten, ohne Zoom). */ - rect: Rect - zoom: number - fontSize: number - lineHeight: number - /** Festes Schlüsselwort davor (`if`, `while`, `for`, `input:` …), nicht bearbeitbar. */ - prefix?: string - /** Ein Feld: Textfeld (Umschalt+Enter = neue Zeile). Mehrere: Eingabezeilen nebeneinander (Zählschleife). */ - fields: InlineField[] - /** Text mittig wie bei Anweisungen. */ - center: boolean - /** `undefined` = abgebrochen (Escape). */ - onDone(values: string[] | undefined): void -} - -export interface InlineEditor { - element: HTMLElement - /** Bearbeitung beenden; `save` übernimmt den Text. */ - finish(save: boolean): void -} - -/** Rand bei Aufruf (`callSideBarInset` im Renderer). */ -const callBar = 14 - -/** Fläche, in der der Text eines Elements steht (dort erscheint das Eingabefeld). */ -export function textRect(node: LayoutNode, lineHeight: number): Rect { - const r = node.frame - switch (node.element.type) { - case ElementType.aufruf: - return { x: r.x + callBar, y: r.y, width: Math.max(40, r.width - 2 * callBar), height: r.height } - case ElementType.aussprung: - return { x: r.x + callBar, y: r.y, width: Math.max(40, r.width - callBar), height: r.height } - case ElementType.verzweigung: - return { x: r.x, y: r.y, width: r.width, height: node.headerBandHeight } - case ElementType.fallauswahl: - return { x: r.x, y: r.y, width: r.width, height: Math.max(lineHeight + 8, node.headerBandHeight - caseBandHeight(lineHeight)) } - case ElementType.forSchleife: - case ElementType.whileSchleife: - case ElementType.endlosschleife: { - const bodyTop = node.children[0]?.frame.y ?? r.y + r.height - return { x: r.x, y: r.y, width: r.width, height: Math.max(lineHeight + 8, bodyTop - r.y) } - } - case ElementType.doUntilSchleife: - return { x: r.x, y: r.y + r.height - node.headerBandHeight, width: r.width, height: node.headerBandHeight } - default: - return r - } -} - -/** Höhe des Bands mit den Fallnamen unter dem Dach der Fallauswahl (wie im Renderer). */ -export function caseBandHeight(lineHeight: number): number { - return Math.max(26, lineHeight + 10) -} - -/** Fallauswahl: Index des Falls, wenn der Punkt auf einer Fallbeschriftung liegt. */ -export function caseLabelAt(node: LayoutNode, x: number, y: number, lineHeight: number): number | undefined { - if (node.element.type !== ElementType.fallauswahl) return undefined - const bottom = node.frame.y + node.headerBandHeight - if (y > bottom || y < bottom - caseBandHeight(lineHeight)) return undefined - const i = node.caseColumnFrames.findIndex((c) => x >= c.x && x <= c.x + c.width) - return i >= 0 ? i : undefined -} - -/** Fläche einer Fallbeschriftung. */ -export function caseLabelRect(node: LayoutNode, index: number, lineHeight: number): Rect { - const col = node.caseColumnFrames[index] - const band = caseBandHeight(lineHeight) - return { x: col.x, y: node.frame.y + node.headerBandHeight - band, width: col.width, height: band } -} - -export function openInlineEditor(o: InlineEditOptions): InlineEditor { - const z = o.zoom - const box = document.createElement('div') - box.className = 'inline-edit' - if (o.center) box.classList.add('inline-edit-center') - const minH = 40 - const top = o.rect.y * z - const height = Math.max(minH, o.rect.height * z) - Object.assign(box.style, { - left: `${o.rect.x * z}px`, - top: `${top - Math.max(0, (minH - o.rect.height * z) / 2)}px`, - width: `${Math.max(o.fields.length > 1 ? 300 : 120, o.rect.width * z)}px`, - minHeight: `${height}px`, - fontFamily: diagramFontFamily, - fontSize: `${o.fontSize * z}px`, - lineHeight: `${o.lineHeight * z}px`, - }) - if (o.prefix) { - const p = document.createElement('span') - p.className = 'inline-edit-prefix' - p.textContent = o.prefix - box.append(p) - } - const inputs: (HTMLInputElement | HTMLTextAreaElement)[] = [] - o.fields.forEach((f, i) => { - const multi = o.fields.length === 1 - const input = multi ? document.createElement('textarea') : document.createElement('input') - input.value = f.value - input.placeholder = f.placeholder - input.spellcheck = false - input.setAttribute('autocomplete', 'off') - input.setAttribute('autocapitalize', 'off') - input.setAttribute('autocorrect', 'off') - input.setAttribute('aria-label', f.label) - if (input instanceof HTMLTextAreaElement) input.rows = Math.max(1, f.value.split('\n').length) - if (i > 0) { - const sep = document.createElement('span') - sep.className = 'inline-edit-sep' - sep.textContent = ';' - box.append(sep) - } - box.append(input) - inputs.push(input) - }) - - const fit = (input: HTMLInputElement | HTMLTextAreaElement) => { - if (!(input instanceof HTMLTextAreaElement)) return - input.rows = Math.max(1, input.value.split('\n').length) - input.style.height = 'auto' - input.style.height = `${input.scrollHeight}px` - } - - let done = false - const finish = (save: boolean) => { - if (done) return - done = true - box.remove() - o.onDone(save ? inputs.map((i) => i.value) : undefined) - } - - for (const input of inputs as HTMLInputElement[]) { - input.addEventListener('input', () => { - // Typografische Anführungszeichen sofort durch gerade ersetzen (gleiche Länge, Cursor bleibt). - const normalized = normalizingQuotes(input.value) - if (normalized !== input.value) { - const pos = input.selectionStart - input.value = normalized - input.setSelectionRange(pos, pos) - } - fit(input) - }) - input.addEventListener('keydown', (ev) => { - ev.stopPropagation() - if (ev.key === 'Escape') { - ev.preventDefault() - finish(false) - } else if (ev.key === 'Enter' && !(ev.shiftKey && o.fields.length === 1) && !ev.isComposing) { - ev.preventDefault() - finish(true) - } - }) - } - // Daneben tippen übernimmt; Wechsel zwischen den Feldern der Zählschleife nicht. - box.addEventListener('focusout', (ev) => { - const next = ev.relatedTarget - if (next instanceof Node && box.contains(next)) return - // Kurz warten: Manche Browser verschieben den Fokus beim Öffnen noch einmal. - setTimeout(() => { - if (!done && !box.contains(document.activeElement)) finish(true) - }, 0) - }) - // Zeiger im Feld gehört dem Feld, nicht dem Struktogramm darunter. - for (const type of ['pointerdown', 'click', 'dblclick', 'contextmenu'] as const) { - box.addEventListener(type, (ev) => ev.stopPropagation()) - } - - o.host.append(box) - inputs.forEach(fit) - const first = inputs[0] - first.focus({ preventScroll: true }) - first.select() - box.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - return { element: box, finish } -} diff --git a/web/src/ui/javaEditor.ts b/web/src/ui/javaEditor.ts deleted file mode 100644 index b9b828e9..00000000 --- a/web/src/ui/javaEditor.ts +++ /dev/null @@ -1,687 +0,0 @@ -import { autocompletion, closeBrackets, closeBracketsKeymap, completionKeymap, snippetCompletion, type Completion, type CompletionContext } from '@codemirror/autocomplete' -import { defaultKeymap, indentWithTab } from '@codemirror/commands' -import { java } from '@codemirror/lang-java' -import { bracketMatching, HighlightStyle, indentOnInput, indentUnit, syntaxHighlighting, syntaxTree } from '@codemirror/language' -import { Annotation, ChangeSet, type ChangeSpec, Compartment, countColumn, EditorSelection, EditorState, Prec, RangeSet, RangeSetBuilder, StateEffect, StateField, type Extension, type Text } from '@codemirror/state' -import { Decoration, EditorView, gutter, GutterMarker, gutterLineClass, highlightActiveLineGutter, keymap, layer, lineNumbers, RectangleMarker, WidgetType, type BlockInfo, type DecorationSet } from '@codemirror/view' -import { tags } from '@lezer/highlight' -import { formatJavaEdits } from '../core/javaFormat' -import { indentJava } from '../core/javaIndent' -import { memberCompletionsAt, type JavaMember, type MemberLanguage, type OwnTypes } from '../core/javaMembers' -import { currentLanguage } from './i18n' - -/** - * Code-Editor der Ansicht „Code live“ (CodeMirror 6): farbige Java-Syntax, Vorschläge beim Tippen - * (Vorlagen für `if`, `for`, … mit Platzhaltern) und Markierungen, die `liveCodePanel` vorgibt. - */ - -export interface EditorMarks { - /** Graue Platzhalter-Wörter (Zeichenpositionen). */ - placeholders: { from: number; to: number }[] - /** Rote Wellenlinie: was noch fehlt oder zu viel ist. */ - problems: { from: number; to: number }[] - /** Zeilen des ausgewählten Elements (ab 0, einschließlich). */ - lines?: { from: number; to: number } - /** Debugger: Zeile, die als Nächstes ausgeführt wird (ab 0, gelb). */ - current?: number - /** Rote Zeilennummer: In dieser Zeile steckt ein Fehler (ab 0). */ - errorLines?: number[] - /** Glühbirne am Zeilenende: Ein Klick übernimmt den Vorschlag. */ - fixes?: QuickFix[] -} - -/** Vorschlag für einen Fehler: an `at` erscheint 💡, ein Klick ersetzt `from`–`to` durch `insert`. */ -export interface QuickFix { - at: number - title: string - from: number - to: number - insert: string -} - -/** Wörter der Vorlagen in der Sprache der Oberfläche. */ -export interface SnippetWords { - condition: string - selector: string - code: string - start: string - step: string - /** Erklärung je Vorlage, z. B. „Verzweigung“. */ - branch: string - branchElse: string - switchCase: string - forLoop: string - whileLoop: string - doLoop: string - output: string - input: string - /** VisuBot: `mb` → `mbot` anbieten. */ - robot?: boolean -} - -export interface JavaEditorHooks { - /** Getippter Text hat sich geändert. */ - onChange(): void - /** Cursor bewegt (ohne Textänderung). */ - onCaret(): void - /** ⌘Z / ⌘⇧Z: gilt für Code und Struktogramm gemeinsam. */ - onUndo(redo: boolean): void - words(): SnippetWords - /** Klick auf die Zeilennummer: Zeile für den Haltepunkt (Anfang des Elements) oder keine. */ - breakpointLine(line: number): number | undefined - /** Haltepunkte geändert. */ - onBreakpoints(): void - /** Eigene Klassen für Vorschläge nach dem Punkt (VisuObject); `pos` = Cursor. */ - ownTypes?(pos: number): OwnTypes | undefined - /** F2 bzw. Rechtsklick: Variable an `pos` umbenennen; `menu` = Stelle des Rechtsklicks. `true` = übernommen. */ - onRename?(pos: number, menu?: { x: number; y: number }): boolean -} - -/** Eigene Textänderungen (z. B. nach einer Änderung im Struktogramm) lösen kein `onChange` aus. */ -const fromOutside = Annotation.define() -const setMarks = StateEffect.define() - -const phMark = Decoration.mark({ class: 'cm-ph' }) -const errMark = Decoration.mark({ class: 'cm-err' }) -const currentLine = Decoration.line({ class: 'cm-debug-line' }) - -/** 💡 hinter der Zeile mit dem Fehler, wie die Glühbirne in Eclipse. */ -class QuickFixWidget extends WidgetType { - constructor(readonly fix: QuickFix) { - super() - } - eq(other: QuickFixWidget) { - return JSON.stringify(other.fix) === JSON.stringify(this.fix) - } - toDOM(view: EditorView) { - const button = document.createElement('button') - button.type = 'button' - button.className = 'cm-quickfix' - button.textContent = '💡' - button.dataset.tip = this.fix.title - button.setAttribute('aria-label', this.fix.title) - button.addEventListener('mousedown', (e) => e.preventDefault()) - button.addEventListener('click', () => { - const { from, to, insert } = this.fix - if (to > view.state.doc.length) return - view.dispatch({ changes: { from, to, insert }, selection: { anchor: from + insert.length }, userEvent: 'input' }) - view.focus() - }) - return button - } - ignoreEvent() { - return true - } -} - -interface MarkState { - marks: EditorMarks - decorations: DecorationSet -} - -function buildDecorations(state: EditorState, marks: EditorMarks): DecorationSet { - const len = state.doc.length - const ranges: { from: number; to: number; deco: Decoration }[] = [] - if (marks.current !== undefined && marks.current < state.doc.lines) { - ranges.push({ from: state.doc.line(marks.current + 1).from, to: state.doc.line(marks.current + 1).from, deco: currentLine }) - } - for (const p of marks.placeholders) if (p.to <= len && p.from < p.to) ranges.push({ from: p.from, to: p.to, deco: phMark }) - for (const p of marks.problems) { - const from = Math.min(p.from, len) - const to = Math.min(Math.max(p.to, from), len) - if (from < to) ranges.push({ from, to, deco: errMark }) - } - for (const f of marks.fixes ?? []) { - if (f.at <= len) ranges.push({ from: f.at, to: f.at, deco: Decoration.widget({ widget: new QuickFixWidget(f), side: 1 }) }) - } - // Zeilen-Dekorationen vor Markierungen an derselben Stelle. - const isLine = (d: Decoration) => d === currentLine - ranges.sort((a, b) => a.from - b.from || Number(isLine(b.deco)) - Number(isLine(a.deco)) || a.to - b.to) - const builder = new RangeSetBuilder() - for (const r of ranges) builder.add(r.from, r.to, r.deco) - return builder.finish() -} - -const marksField = StateField.define({ - create: () => ({ marks: { placeholders: [], problems: [] }, decorations: Decoration.none }), - update(value, tr) { - for (const e of tr.effects) if (e.is(setMarks)) return { marks: e.value, decorations: buildDecorations(tr.state, e.value) } - if (!tr.docChanged) return value - // Bis zur nächsten Auswertung mit dem Text mitwandern. - const map = (r: { from: number; to: number }) => ({ from: tr.changes.mapPos(r.from, 1), to: tr.changes.mapPos(r.to, -1) }) - const marks = { ...value.marks, placeholders: value.marks.placeholders.map(map).filter((r) => r.from < r.to), problems: [], errorLines: [], fixes: [] } - return { marks, decorations: buildDecorations(tr.state, marks) } - }, - provide: (f) => EditorView.decorations.from(f, (v) => v.decorations), -}) - -/** Rote Zeilennummer bei einem Fehler, wie in Xcode; anders als der runde Haltepunkt. */ -class ErrorLineMarker extends GutterMarker { - elementClass = 'cm-error-line' -} -const errorLineMarker = new ErrorLineMarker() -const errorLineGutter = gutterLineClass.compute([marksField], (state) => { - const lines = [...new Set(state.field(marksField).marks.errorLines ?? [])].filter((l) => l < state.doc.lines).sort((a, b) => a - b) - return RangeSet.of(lines.map((l) => errorLineMarker.range(state.doc.line(l + 1).from))) -}) - -// MARK: - Farbige Blöcke wie in BlueJ - -/** Art des Blocks → Farbe (`cm-scope-…` in styles.css). */ -const scopeKinds: Record = { - ClassDeclaration: 'class', - InterfaceDeclaration: 'class', - EnumDeclaration: 'class', - MethodDeclaration: 'method', - ConstructorDeclaration: 'method', - ForStatement: 'loop', - EnhancedForStatement: 'loop', - WhileStatement: 'loop', - DoStatement: 'loop', - IfStatement: 'if', - SwitchStatement: 'if', - TryStatement: 'try', -} - -/** - * Zeile der `}`, die den Block schließt, der in der Zeile von `pos` mit `{` beginnt. Bei Pseudocode - * (`while (Tee noch zu heiß) {`) bricht der Java-Parser den Block nach der ersten Zeile ab; so reicht der Kasten - * trotzdem bis zur schließenden Klammer wie im Struktogramm. - */ -function closingLine(doc: Text, pos: number): number { - const first = doc.lineAt(pos) - if (!first.text.trimEnd().endsWith('{')) return first.number - let depth = 0 - for (let n = first.number; n <= doc.lines; n++) { - for (const ch of doc.line(n).text.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/g, '')) { - if (ch === '{') depth++ - else if (ch === '}' && --depth === 0) return n - } - } - return first.number -} - -/** - * Jeder Block (Klasse, Methode, Schleife, Verzweigung …) bekommt einen eigenen Kasten, der bei seiner - * Einrückung beginnt; die Kästen liegen ineinander. Das ausgewählte Element bekommt einen Rahmen darüber. - */ -function scopeMarkers(view: EditorView): RectangleMarker[] { - const { state } = view - const doc = state.doc - const lineElt = view.contentDOM.querySelector('.cm-line') - if (!lineElt) return [] - const style = getComputedStyle(lineElt) - const scroller = view.scrollDOM.getBoundingClientRect() - const content = view.contentDOM.getBoundingClientRect() - const baseLeft = scroller.left - view.scrollDOM.scrollLeft - const baseTop = scroller.top - view.scrollDOM.scrollTop - const textLeft = content.left - baseLeft + parseFloat(style.paddingLeft) + parseFloat(style.borderLeftWidth) - const right = content.right - baseLeft - 6 - const top0 = view.documentTop - baseTop - const tabSize = state.tabSize - const charWidth = view.defaultCharacterWidth - const box = (className: string, fromLine: number, toLine: number, inset: number) => { - const first = doc.line(fromLine) - const col = countColumn(/^\s*/.exec(first.text)![0], tabSize) - const left = textLeft + col * charWidth - 7 - const top = view.lineBlockAt(first.from).top + top0 + inset - const bottom = view.lineBlockAt(doc.line(toLine).from).bottom + top0 - inset - return new RectangleMarker(className, left, top, Math.max(right - left, 20), Math.max(bottom - top, 4)) - } - const markers: RectangleMarker[] = [] - // Anweisungen ohne Klasse und Methode (Einstellung ohne `class`/`main`): ein gelber Kasten wie die Methode, - // damit sie wie die Anweisungen im Struktogramm aussehen. - const top = syntaxTree(state).topNode - const loose: { from: number; to: number }[] = [] - for (let c = top.firstChild; c; c = c.nextSibling) { - if (scopeKinds[c.name] === 'class' || c.name === 'MethodDeclaration') { - loose.length = 0 - break - } - if (c.name !== 'ImportDeclaration' && c.name !== 'PackageDeclaration' && !c.name.includes('Comment')) loose.push(c) - } - if (loose.length) markers.push(box('cm-scope cm-scope-method', doc.lineAt(loose[0].from).number, doc.lineAt(loose[loose.length - 1].to).number, 1)) - // Äußere Blöcke zuerst: Die inneren liegen darüber. - const blocks: { kind: string; from: number; to: number }[] = [] - syntaxTree(state).iterate({ - from: view.viewport.from, - to: view.viewport.to, - enter: (node) => { - const kind = scopeKinds[node.name] - if (!kind) return - // `else if` gehört zum Kasten des ersten `if`. - if (node.name === 'IfStatement' && node.node.parent?.name === 'IfStatement') return - const from = doc.lineAt(node.from).number - const to = Math.max(doc.lineAt(node.to).number, closingLine(doc, node.from)) - blocks.push({ kind, from, to }) - markers.push(box(`cm-scope cm-scope-${kind}`, from, to, 1)) - }, - }) - // Anweisungen in Schleifen und Verzweigungen bekommen wie im Struktogramm einen eigenen gelben Streifen. - for (let n = 1; n <= doc.lines; n++) { - let inner: { kind: string; from: number; to: number } | undefined - for (const b of blocks) if (b.from <= n && n <= b.to && (!inner || b.from >= inner.from)) inner = b - if (!inner || (inner.kind !== 'loop' && inner.kind !== 'if') || n === inner.from) continue - const text = doc.line(n).text.trim() - if (!text || text.startsWith('}') || text.endsWith('{') || text.startsWith('//') || text.startsWith('case ') || text.startsWith('default')) continue - markers.push(box('cm-scope cm-scope-method cm-scope-stmt', n, n, 2)) - } - const sel = state.field(marksField).marks.lines - if (sel && sel.from < doc.lines) markers.push(box('cm-scope cm-scope-sel', sel.from + 1, Math.min(sel.to + 1, doc.lines), 0)) - return markers -} - -const codeScopesKey = 'visustruct.code.scopes' - -/** Einstellung „Blöcke farbig“: ohne Speicher eingeschaltet. */ -export function codeScopes(): boolean { - try { - return localStorage.getItem(codeScopesKey) !== '0' - } catch { - return true - } -} - -/** Schaltet die farbigen Kästen in allen Code-Editoren um (der Rahmen der Auswahl bleibt). */ -export function setCodeScopes(on: boolean) { - try { - localStorage.setItem(codeScopesKey, on ? '1' : '0') - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } - document.documentElement.classList.toggle('no-code-scopes', !on) -} - -if (typeof document !== 'undefined') document.documentElement.classList.toggle('no-code-scopes', !codeScopes()) - -const scopeLayer = layer({ - above: false, - class: 'cm-scopeLayer', - markers: scopeMarkers, - update: (u) => - u.docChanged || - u.viewportChanged || - u.geometryChanged || - syntaxTree(u.state) !== syntaxTree(u.startState) || - u.transactions.some((t) => t.effects.some((e) => e.is(setMarks))), -}) - -// MARK: - Haltepunkte - -/** Roter Punkt vor der Zeilennummer: Hier hält „Ausführen“ an. */ -class BreakpointMarker extends GutterMarker { - toDOM() { - const dot = document.createElement('span') - dot.className = 'cm-breakpoint' - return dot - } -} -const breakpointMarker = new BreakpointMarker() -const toggleBreakpoint = StateEffect.define() -const resetBreakpoints = StateEffect.define() - -/** Haltepunkte als Positionen am Zeilenanfang: Sie wandern beim Tippen mit ihrer Zeile mit. */ -const breakpointsField = StateField.define>({ - create: () => RangeSet.empty, - update(set, tr) { - set = set.map(tr.changes) - for (const e of tr.effects) { - if (e.is(resetBreakpoints)) set = RangeSet.of(e.value.map((from) => breakpointMarker.range(from)), true) - if (!e.is(toggleBreakpoint)) continue - let has = false - set.between(e.value, e.value, () => { - has = true - }) - set = has ? set.update({ filter: (from) => from !== e.value }) : set.update({ add: [breakpointMarker.range(e.value)] }) - } - return set - }, -}) - -function breakpointGutter(hooks: JavaEditorHooks): Extension { - const toggle = (view: EditorView, block: BlockInfo) => { - const line = hooks.breakpointLine(view.state.doc.lineAt(block.from).number - 1) - if (line === undefined || line >= view.state.doc.lines) return false - view.dispatch({ effects: toggleBreakpoint.of(view.state.doc.line(line + 1).from) }) - queueMicrotask(() => hooks.onBreakpoints()) - return true - } - const handlers = { mousedown: toggle } - return [ - breakpointsField, - gutter({ - class: 'cm-breakpoint-gutter', - markers: (v) => v.state.field(breakpointsField), - initialSpacer: () => breakpointMarker, - domEventHandlers: handlers, - }), - lineNumbers({ domEventHandlers: handlers }), - ] -} - -/** Platzhalter am Cursor (auch direkt davor oder dahinter). */ -function placeholderAt(state: EditorState, pos: number) { - return state.field(marksField).marks.placeholders.find((p) => pos >= p.from && pos <= p.to) -} - -/** Wie in einem HTML-Formular: Wer in einen grauen Platzhalter tippt, ersetzt das ganze Wort. */ -const placeholderTyping = EditorView.inputHandler.of((view, from, to, text) => { - if (from !== to) return false - const ph = placeholderAt(view.state, from) - if (!ph) return false - view.dispatch({ changes: { from: ph.from, to: ph.to, insert: text }, selection: { anchor: ph.from + text.length }, userEvent: 'input.type' }) - return true -}) - -/** Einfügen und Hineinziehen ersetzen den Platzhalter genauso (sonst bleibt z. B. `// Dein Code` vor dem Text stehen). */ -const placeholderPaste = EditorState.transactionFilter.of((tr) => { - if (!tr.docChanged || !(tr.isUserEvent('input.paste') || tr.isUserEvent('input.drop'))) return tr - const changes: { from: number; to: number; text: string }[] = [] - tr.changes.iterChanges((from, to, _fromB, _toB, inserted) => changes.push({ from, to, text: inserted.toString() })) - if (changes.length !== 1 || changes[0].from !== changes[0].to) return tr - const ph = placeholderAt(tr.startState, changes[0].from) - if (!ph) return tr - const { text } = changes[0] - return { changes: { from: ph.from, to: ph.to, insert: text }, selection: { anchor: ph.from + text.length }, userEvent: 'input.paste', scrollIntoView: true } -}) - -const deletePlaceholder = (forward: boolean) => (view: EditorView) => { - const sel = view.state.selection.main - if (!sel.empty) return false - const ph = placeholderAt(view.state, sel.head) - if (!ph || (forward ? sel.head >= ph.to : sel.head <= ph.from)) return false - view.dispatch({ changes: { from: ph.from, to: ph.to }, selection: { anchor: ph.from }, userEvent: 'delete' }) - return true -} - -/** Klick mitten in einen Platzhalter wählt das ganze Wort aus. */ -const placeholderClick = EditorView.updateListener.of((u) => { - if (!u.selectionSet || u.docChanged || !u.transactions.some((t) => t.isUserEvent('select.pointer'))) return - const sel = u.state.selection.main - if (!sel.empty) return - const ph = u.state.field(marksField).marks.placeholders.find((p) => sel.head > p.from && sel.head < p.to) - // Nicht während der laufenden Aktualisierung neu auslösen. - if (ph) queueMicrotask(() => u.view.dispatch({ selection: EditorSelection.range(ph.from, ph.to) })) -}) - -const highlight = HighlightStyle.define([ - // Wie in Eclipse: int, boolean … sowie true, false und null sind Schlüsselwörter; Klassennamen bleiben schwarz. - { tag: [tags.keyword, tags.controlKeyword, tags.definitionKeyword, tags.modifier, tags.operatorKeyword, tags.standard(tags.typeName), tags.bool, tags.null], color: 'var(--syn-keyword)', fontWeight: '600' }, - { tag: [tags.string, tags.character], color: 'var(--syn-string)' }, - { tag: tags.number, color: 'var(--syn-number)' }, - { tag: [tags.lineComment, tags.blockComment], color: 'var(--syn-comment)', fontStyle: 'italic' }, -]) - -/** Vorlagen und Wörter für die Vorschläge. */ -export function completions(words: SnippetWords): Completion[] { - const w = words - // `${…}` = Feld zum Ausfüllen (Tab springt weiter); stehen gelassen bleibt es ein grauer Platzhalter. - const body = `\${// ${w.code}}` - const snippet = (label: string, template: string, detail: string, boost = 0) => - snippetCompletion(template, { label, detail, type: 'keyword', boost }) - return [ - snippet('if', `if (\${${w.condition}}) {\n\t${body}\n}`, w.branch, 2), - snippet('if … else', `if (\${${w.condition}}) {\n\t${body}\n} else {\n\t\${// ${w.code}}\n}`, w.branchElse, 1), - snippet('for', `for (int \${i} = \${0}; \${i} < \${10}; \${i}++) {\n\t${body}\n}`, w.forLoop, 2), - snippet('while', `while (\${${w.condition}}) {\n\t${body}\n}`, w.whileLoop, 2), - snippet('do … while', `do {\n\t${body}\n} while (\${${w.condition}});`, w.doLoop, 1), - snippet('switch', `switch (\${${w.selector}}) {\n\tcase \${1} -> {\n\t\t${body}\n\t}\n\tdefault -> {\n\t\t\${// ${w.code}}\n\t}\n}`, w.switchCase, 1), - snippet('System.out.println', 'System.out.println(${});', w.output, 3), - // Kürzel wie in Eclipse (`syso`, `sysout`) und IntelliJ/NetBeans (`sout`, `psvm`). - ...['syso', 'sysout', 'sout'].map((label) => snippet(label, 'System.out.println(${});', `System.out.println · ${w.output}`, 2)), - ...['main', 'psvm'].map((label) => snippet(label, `public static void main(String[] args) {\n\t${body}\n}`, 'public static void main(String[] args)', 2)), - scannerCompletion(w.input), - snippet('scanner.nextInt', 'int ${zahl} = scanner.nextInt();', w.input, 1), - snippet('scanner.next', 'String ${text} = scanner.next();', w.input), - snippet('scanner.nextDouble', 'double ${wert} = scanner.nextDouble();', w.input), - snippet('try … catch', `try {\n\t${body}\n} catch (\${Exception} e) {\n\t\${// ${w.code}}\n}`, 'try … catch', 1), - snippet('public class', `public class \${Main} {\n\t${body}\n}`, 'public class', -2), - // Bei `import` gleich die üblichen Zeilen anbieten. - ...['import java.util.Scanner;', 'import javax.swing.*;', 'import java.awt.*;', 'import java.awt.event.*;'].map((label, i) => - snippet(label, label, 'import', 1 - i), - ), - // Schlüsselwörter, auch für Klassen und Methoden (z. B. `pu` → public). - ...['int', 'double', 'String', 'boolean', 'char', 'long', 'final', 'break;', 'continue;', 'return', 'true', 'false', 'else', 'null', 'new', - 'public', 'private', 'static', 'void', 'class', 'extends', 'implements', 'interface', 'this'].map((label) => ({ - label, - type: 'keyword', - boost: -1, - })), - // `Scanner` allein nicht: dort steht die ganze Zeile (scannerCompletion) oben. - ...['Math', 'JFrame', 'JButton', 'JLabel', 'JTextField', 'ActionListener', 'ActionEvent', 'Color', 'FlowLayout', 'GridLayout', 'BorderLayout'].map((label) => ({ - label, - type: 'type', - boost: -2, - })), - // VisuBot: der Roboter heißt immer `mbot`; danach kommt mit `.` die Befehlsliste. - ...(w.robot ? [{ label: 'mbot', detail: 'mBot2', type: 'variable', boost: 3 }] : []), - ...['Math.random()', 'Math.abs(', 'Math.sqrt(', 'Math.max(', 'Math.min(', '.length()', '.equals('].map((label) => ({ label, type: 'function', boost: -2 })), - ] -} - -/** Fehlt `import java.util.Scanner;` (oder `java.util.*`), die Zeile dafür; sonst nichts. */ -export function scannerImport(text: string): ChangeSpec | undefined { - if (/^\s*import\s+java\.util\.(?:Scanner|\*)\s*;/m.test(text)) return undefined - // Hinter `package …;`, sonst ganz oben; vor anderen Imports ohne Leerzeile, vor dem Programm mit einer. - const pkg = /^\s*package\s+[\w.]+\s*;[^\n]*\n?/.exec(text) - const at = pkg ? pkg[0].length : 0 - const rest = text.slice(at).replace(/^\s*\n/, '') - return { from: at, insert: `import java.util.Scanner;\n${/^\s*import\b/.test(rest) ? '' : '\n'}` } -} - -/** - * `Scanner` → die ganze Zeile `Scanner scanner = new Scanner(System.in);` (Name wie in den Lektionen und im erzeugten Code, - * als Feld zum Überschreiben). Fehlt der Import, kommt er oben dazu. - */ -function scannerCompletion(input: string): Completion { - const line = snippetCompletion('Scanner ${scanner} = new Scanner(System.in);', { - label: 'Scanner scanner = new Scanner(System.in);', - detail: input, - type: 'keyword', - boost: 3, - }) - const insertLine = line.apply as (view: EditorView, completion: Completion, from: number, to: number) => void - return { - ...line, - apply(view, completion, from, to) { - const add = scannerImport(view.state.doc.toString()) - if (!add) return insertLine(view, completion, from, to) - const changes = view.state.changes(add) - view.dispatch({ changes, userEvent: 'input.complete' }) - insertLine(view, completion, changes.mapPos(from), changes.mapPos(to, 1)) - }, - } -} - -/** Vorschlag hinter dem Punkt: Methoden mit Klammern (Cursor dazwischen, wenn Werte nötig sind), Felder wie `length` ohne. */ -export function memberCompletion(member: JavaMember): Completion { - const label = member.kind === 'field' ? member.name : `${member.name}(${member.params ? '…' : ''})` - const completion: Completion = { - label, - detail: member.returns === 'void' || !member.returns ? member.description : member.description ? `${member.returns} · ${member.description}` : member.returns, - type: member.kind === 'field' ? 'property' : 'method', - } - if (member.kind === 'field') return { ...completion, apply: member.name } - return member.params ? snippetCompletion(`${member.name}(\${})`, completion) : { ...completion, apply: `${member.name}()` } -} - -function memberLanguage(): MemberLanguage { - const lang = currentLanguage() - return lang === 'de' ? 'de' : lang === 'pt-PT' ? 'pt' : 'en' -} - -/** Variablen aus dem eigenen Code: `int zahl = …`, `String[] namen`, Parameter. */ -function declaredNames(text: string): Completion[] { - const names = new Set() - for (const m of text.matchAll(/\b(?:int|long|double|float|boolean|char|String|Scanner)(?:\s*\[\s*\])*\s+([A-Za-z_]\w*)/g)) names.add(m[1]) - return [...names].map((label) => ({ label, type: 'variable', boost: 1 })) -} - -export class JavaEditor { - readonly view: EditorView - private readOnly = new Compartment() - - constructor(parent: HTMLElement, hooks: JavaEditorHooks, label: string) { - const source = (context: CompletionContext) => { - const word = context.matchBefore(/[\w.]+/) - if (!word || (word.from === word.to && !context.explicit)) return null - // In Zeichenketten und Kommentaren nichts vorschlagen. - const line = context.state.doc.lineAt(context.pos) - const before = line.text.slice(0, context.pos - line.from) - if (/\/\//.test(before) || (before.match(/"/g)?.length ?? 0) % 2 === 1) return null - // Hinter `variable.` die Methoden ihres Typs (aus der Deklaration im Code), z. B. `eingabe.` → nextInt(). - // Reihenfolge wie in der Liste (das Häufigste zuerst) statt alphabetisch. - const members = memberCompletionsAt(context.state.doc.toString(), context.pos, memberLanguage(), hooks.ownTypes?.(context.pos)) - if (members) return { from: members.from, options: members.members.map((m, i) => ({ ...memberCompletion(m), boost: 50 - i })), validFor: /^\w*$/ } - // Hinter `void` tippt man den Namen der Methode selbst: keine main-Vorlage anbieten. - const afterVoid = /\bvoid\s+[\w.]*$/.test(before) - const options = completions(hooks.words()).filter((c) => !(afterVoid && (c.label === 'main' || c.label === 'psvm'))) - return { - from: word.from, - options: [...options, ...declaredNames(context.state.doc.toString())], - // Ohne Punkt: beim Tippen von `.` neu fragen, damit die Methoden des Typs kommen. - validFor: /^\w*$/, - } - } - const extensions: Extension[] = [ - breakpointGutter(hooks), - highlightActiveLineGutter(), - java(), - syntaxHighlighting(highlight), - indentUnit.of(' '), - EditorState.tabSize.of(4), - indentOnInput(), - bracketMatching(), - closeBrackets(), - marksField, - errorLineGutter, - scopeLayer, - placeholderTyping, - placeholderPaste, - placeholderClick, - this.readOnly.of(EditorState.readOnly.of(false)), - autocompletion({ override: [source], icons: false }), - Prec.highest( - keymap.of([ - { key: 'Mod-z', run: () => (hooks.onUndo(false), true), preventDefault: true }, - { key: 'Mod-Shift-z', run: () => (hooks.onUndo(true), true), preventDefault: true }, - { key: 'Mod-y', run: () => (hooks.onUndo(true), true), preventDefault: true }, - { key: 'Backspace', run: deletePlaceholder(false) }, - { key: 'Delete', run: deletePlaceholder(true) }, - { key: 'F2', run: (view) => hooks.onRename?.(view.state.selection.main.head) ?? false, preventDefault: true }, - ]), - ), - keymap.of([...closeBracketsKeymap, ...completionKeymap, indentWithTab, ...defaultKeymap]), - EditorView.domEventHandlers({ - contextmenu: (e, view) => { - const pos = view.posAtCoords({ x: e.clientX, y: e.clientY }) - if (pos === null || !hooks.onRename?.(pos, { x: e.clientX, y: e.clientY })) return false - e.preventDefault() - return true - }, - }), - EditorView.contentAttributes.of({ 'aria-label': label, autocapitalize: 'off', autocorrect: 'off', spellcheck: 'false' }), - EditorView.updateListener.of((u) => { - // Danach, denn die Rückmeldung zeichnet den Editor neu (Markierungen). - if (u.docChanged && !u.transactions.some((t) => t.annotation(fromOutside))) queueMicrotask(() => hooks.onChange()) - else if (u.selectionSet && !u.docChanged) queueMicrotask(() => hooks.onCaret()) - }), - ] - this.view = new EditorView({ parent, state: EditorState.create({ doc: '', extensions }) }) - } - - get text(): string { - return this.view.state.doc.toString() - } - - get caret(): number { - return this.view.state.selection.main.head - } - - get hasFocus(): boolean { - return this.view.hasFocus - } - - /** Zeilen mit Haltepunkt (ab 0). */ - get breakpoints(): Set { - const lines = new Set() - const doc = this.view.state.doc - this.view.state.field(breakpointsField).between(0, doc.length, (from) => { - lines.add(doc.lineAt(from).number - 1) - }) - return lines - } - - /** Haltepunkte einer anderen Datei zeigen (Zeilen ab 0). */ - setBreakpoints(lines: Iterable) { - const doc = this.view.state.doc - const from = [...new Set(lines)].filter((l) => l >= 0 && l < doc.lines).map((l) => doc.line(l + 1).from) - this.view.dispatch({ effects: resetBreakpoints.of(from) }) - } - - lineAt(offset: number): number { - return this.view.state.doc.lineAt(Math.min(offset, this.view.state.doc.length)).number - 1 - } - - /** Text von außen setzen (Struktogramm geändert, Rückgängig); der Cursor bleibt möglichst an seiner Stelle. */ - setText(text: string) { - const caret = Math.min(this.caret, text.length) - // Nur den geänderten Teil ersetzen: Haltepunkte davor und danach bleiben an ihrer Zeile. - const old = this.text - let start = 0 - while (start < old.length && start < text.length && old[start] === text[start]) start++ - let end = 0 - while (end < old.length - start && end < text.length - start && old[old.length - 1 - end] === text[text.length - 1 - end]) end++ - this.view.dispatch({ - changes: { from: start, to: old.length - end, insert: text.slice(start, text.length - end) }, - selection: { anchor: caret }, - annotations: fromOutside.of(true), - }) - } - - /** Umbenennen: alle Stellen in einem Schritt ersetzen (wie Tippen, das Struktogramm folgt). */ - replaceRanges(ranges: { from: number; to: number }[], insert: string) { - if (ranges.length === 0) return - this.view.dispatch({ changes: ranges.map((r) => ({ from: r.from, to: r.to, insert })), userEvent: 'input.rename' }) - } - - /** Knopf „Einrücken“: die markierten Zeilen, ohne Markierung den ganzen Code, erst formatieren, dann einrücken. */ - indent() { - const doc = this.view.state.doc - const sel = this.view.state.selection.main - const from = sel.empty ? 1 : doc.lineAt(sel.from).number - const to = sel.empty ? doc.lines : doc.lineAt(sel.to).number - // `}` auf eine eigene Zeile, Leerzeichen um Operatoren: nur Leerraum zwischen den Zeichen ändert sich. - const format = ChangeSet.of(formatJavaEdits(doc.toString(), from, to), doc.length) - const formatted = format.apply(doc) - const fromLine = formatted.lineAt(format.mapPos(doc.line(from).from, -1)).number - const toLine = formatted.lineAt(format.mapPos(doc.line(to).to, 1)).number - // Einrücken ändert nur die Leerzeichen am Zeilenanfang: Haltepunkte und Cursor bleiben in ihrer Zeile. - const changes: ChangeSpec[] = [] - indentJava(formatted.toString(), fromLine, toLine) - .split('\n') - .forEach((text, i) => { - const line = formatted.line(i + 1) - const old = /^\s*/.exec(line.text)![0] - const now = /^\s*/.exec(text)![0] - if (old !== now) changes.push({ from: line.from, to: line.from + old.length, insert: now }) - }) - const all = format.compose(ChangeSet.of(changes, formatted.length)) - if (!all.empty) this.view.dispatch({ changes: all, userEvent: 'input.indent' }) - this.view.focus() - } - - setMarks(marks: EditorMarks, scrollToSelection: boolean) { - const effects: StateEffect[] = [setMarks.of(marks)] - if (marks.current !== undefined && marks.current < this.view.state.doc.lines) { - effects.push(EditorView.scrollIntoView(this.view.state.doc.line(marks.current + 1).from, { y: 'nearest' })) - } else if (scrollToSelection && marks.lines && marks.lines.from < this.view.state.doc.lines) { - effects.push(EditorView.scrollIntoView(this.view.state.doc.line(marks.lines.from + 1).from, { y: 'nearest' })) - } - this.view.dispatch({ effects }) - } - - setReadOnly(readOnly: boolean) { - this.view.dispatch({ effects: this.readOnly.reconfigure(EditorState.readOnly.of(readOnly)) }) - } -} diff --git a/web/src/ui/javaRuntime.ts b/web/src/ui/javaRuntime.ts deleted file mode 100644 index 5b0dba01..00000000 --- a/web/src/ui/javaRuntime.ts +++ /dev/null @@ -1,298 +0,0 @@ -import { textFileEntries } from '../core/simulation' -/** - * Echtes Java (mit Swing) im Browser über CheerpJ (https://cheerpj.com), nur wenn im Zahnrad-Menü - * „„Echtes Java (mit Swing)““ an ist. Erst dann werden Laufzeit und Compiler geladen (einmalig, danach Browser-Cache). - * - * - Laufzeit: vom CDN des Herstellers (Community-Lizenz: kostenlos für Einzelne und Open Source, nur über dieses CDN). - * - Compiler: javac aus tools.jar (OpenJDK 8), aus dem JavaFiddle-Repo von Leaning Technologies (Apache-2.0). - * - Ausgaben von System.out landen in dem Element mit der id „console“ (so macht es CheerpJ). - */ - -const loaderURL = 'https://cjrtnc.leaningtech.com/4.3/loader.js' -const toolsURL = 'https://raw.githubusercontent.com/leaningtech/javafiddle/main/static/tools.jar' -const toolsJar = '/str/tools.jar' - -/** - * Startet die Programme der Schüler. Alle Läufe gehen über diese eine Klasse (als Bibliothek geladen), - * damit sie dieselben Fenster sehen: Ein eigener `cheerpjRunMain` pro Lauf sieht die Fenster des vorigen nicht. - */ -const runnerSource = `public class VisuStructRunner { - public static void mkdirs(String dir) { - new java.io.File(dir).mkdirs(); - } - - public static void copy(String from, String to) throws Exception { - java.nio.file.Files.copy(java.nio.file.Paths.get(from), java.nio.file.Paths.get(to), java.nio.file.StandardCopyOption.REPLACE_EXISTING); - } - - /** Namen der Dateien im Ordner, durch Zeilenumbrüche getrennt. */ - public static String list(String dir) { - String[] names = new java.io.File(dir).list(); - return names == null ? "" : String.join("\\n", names); - } - - /** Quelltext für javac schreiben (Ordner werden angelegt), z. B. ein Projekt mit Packages. */ - public static void write(String path, String text) throws Exception { - java.io.File f = new java.io.File(path); - f.getParentFile().mkdirs(); - java.nio.file.Files.write(f.toPath(), text.getBytes("UTF-8")); - } - - public static String read(String path) throws Exception { - return new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get(path)), "UTF-8"); - } - - public static void closeAll() { - for (java.awt.Window w : java.awt.Window.getWindows()) w.dispose(); - } - - public static int openWindows() { - // Erst die Ereignisschlange abarbeiten lassen: main öffnet Fenster meist per invokeLater. - try { - javax.swing.SwingUtilities.invokeAndWait(() -> {}); - } catch (Exception e) { - } - int n = 0; - for (java.awt.Window w : java.awt.Window.getWindows()) if (w.isShowing()) n++; - return n; - } - - public static void run(String dir, String name) { - closeAll(); - try { - java.net.URLClassLoader loader = new java.net.URLClassLoader(new java.net.URL[] { new java.io.File(dir).toURI().toURL() }); - loader.loadClass(name).getMethod("main", String[].class).invoke(null, (Object) new String[0]); - } catch (java.lang.reflect.InvocationTargetException e) { - e.getCause().printStackTrace(); - } catch (NoSuchMethodException e) { - System.err.println("Die Klasse " + name + " hat keine Methode public static void main(String[] args)."); - } catch (Exception e) { - e.printStackTrace(); - } - } -} -` - -interface Runner { - mkdirs(dir: string): Promise - copy(from: string, to: string): Promise - list(dir: string): Promise - read(path: string): Promise - write(path: string, text: string): Promise - closeAll(): Promise - openWindows(): Promise - run(dir: string, name: string): Promise -} - -declare global { - function cheerpjInit(options?: object): Promise - function cheerpjCreateDisplay(width: number, height: number, parent?: HTMLElement): HTMLElement - function cheerpjRunMain(className: string, classPath: string, ...args: string[]): Promise - function cheerpjRunLibrary(classPath: string): Promise>> - function cheerpOSAddStringFile(path: string, data: string | Uint8Array): void -} - -let ready: Promise | undefined -let run = 0 - -function loadScript(src: string): Promise { - return new Promise((resolve, reject) => { - const script = document.createElement('script') - script.src = src - script.onload = () => resolve() - script.onerror = () => reject(new Error(src)) - document.head.append(script) - }) -} - -/** Laufzeit und Compiler laden; `display`: Fläche für die Swing-Fenster. Mehrfach aufrufbar. */ -export function prepareJava(display: HTMLElement): Promise { - ready ??= (async () => { - await loadScript(loaderURL) - await cheerpjInit({ status: 'none' }) - cheerpjCreateDisplay(-1, -1, display) - const response = await fetch(toolsURL) - if (!response.ok) throw new Error(toolsURL) - cheerpOSAddStringFile(toolsJar, new Uint8Array(await response.arrayBuffer())) - cheerpOSAddStringFile('/str/VisuStructRunner.java', runnerSource) - if ((await cheerpjRunMain('com.sun.tools.javac.Main', toolsJar, '/str/VisuStructRunner.java', '-d', '/files/')) !== 0) { - throw new Error('javac') - } - const lib = await cheerpjRunLibrary('/files/') - return (await lib.VisuStructRunner) as Runner - })() - ready.catch(() => (ready = undefined)) - return ready -} - -/** Name der Klasse, die gestartet wird: die öffentliche, sonst die erste. */ -export function mainClassName(source: string): string { - const m = source.match(/public\s+(?:final\s+)?class\s+(\w+)/) ?? source.match(/\bclass\s+(\w+)/) - return m ? m[1] : 'Main' -} - -/** Liest oder schreibt der Code Dateien? */ -export function usesFiles(source: string): boolean { - return /\bFiles\s*\.\s*\w+\s*\(|\bnew\s+(?:File|FileReader|FileWriter|PrintWriter|BufferedReader)\s*\(/.test(source) -} - -/** Dateinamen wie `"liste.txt"` im Code: Sie zeigen im Browser in den Ordner `dir`. */ -export function filesInto(source: string, dir: string): string { - return source.replace(/"([\w.-]+\.(?:txt|csv|json))"/g, `"${dir}$1"`) -} - -/** Der Compiler hier ist javac 8: `Files.readString` und `Files.writeString` (ab Java 11) werden umgeschrieben. */ -export function java8Files(source: string): string { - source = source.replace( - /\bFiles\s*\.\s*readString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*\)/g, - 'new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get($1)), "UTF-8")', - ) - const write = /\bFiles\s*\.\s*writeString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*,/g - let out = '' - let last = 0 - for (let m = write.exec(source); m; m = write.exec(source)) { - // Das zweite Argument reicht bis zur schließenden Klammer von `writeString(`. - let depth = 1 - let i = m.index + m[0].length - let quote = false - for (; i < source.length && depth > 0; i++) { - const c = source[i] - if (quote) { - if (c === '\\') i++ - else if (c === '"') quote = false - } else if (c === '"') quote = true - else if (c === '(') depth++ - else if (c === ')') depth-- - } - if (depth > 0) break - const text = source.slice(m.index + m[0].length, i - 1).trim() - out += source.slice(last, m.index) + `java.nio.file.Files.write(java.nio.file.Paths.get(${m[1]}), (${text}).getBytes("UTF-8"))` - last = i - write.lastIndex = i - } - return out + source.slice(last) -} - -/** Code ohne Klasse (Snippet oder nur Methoden) in eine Klasse mit main packen. */ -export function asProgram(source: string): string { - if (/\bclass\s+\w+/.test(source)) return source - // Getippte Imports gehören vor die Klasse, nicht in `main`. - const own: string[] = [] - source = source.replace(/^[ \t]*import\s+[\w.]+(?:\.\*)?\s*;[ \t]*$/gm, (line) => (own.push(line.trim()), '')) - const imports = [...own, 'import java.util.*;', 'import java.awt.*;', 'import java.awt.event.*;', 'import javax.swing.*;'].join('\n') + '\n\n' - if (/\bstatic\s+void\s+main\s*\(/.test(source)) return `${imports}public class Main {\n${source}\n}\n` - // Dateien lesen und schreiben kann eine IOException werfen; ohne `throws` meldet javac einen Fehler. - const throwsClause = usesFiles(source) ? ' throws Exception' : '' - return `${imports}public class Main {\n public static void main(String[] args)${throwsClause} {\n${source}\n }\n}\n` -} - -/** Fenster des vorigen Laufs schließen. */ -export async function closeWindows() { - if (ready) await (await ready).closeAll() -} - -/** Geschrieben hat das Programm, was neu ist oder sich geändert hat. */ -async function writtenFiles(runner: Runner, data: string, before: Map): Promise> { - const written = new Map() - for (const file of (await runner.list(data)).split('\n').filter(Boolean)) { - const content = await runner.read(data + file) - if (before.get(file) !== content) written.set(file, content) - } - return written -} - -/** - * Ein Swing-Programm läuft nach main weiter und speichert vielleicht erst beim Klick. Solange seine Fenster offen sind - * und kein neuer Lauf beginnt, meldet `onChange` jede Datei, die danach neu gespeichert wird. Liefert die Funktion zum Beenden. - */ -export function watchWrittenFiles(shown: Map, onChange: (files: Map) => void): () => void { - const mine = run - const data = `/files/run${mine}/data/` - const before = new Map([...textFileEntries(), ...shown]) - let stopped = false - const tick = async () => { - if (stopped || run !== mine || !ready) return - try { - const runner = await ready - const changed = await writtenFiles(runner, data, before) - for (const [file, content] of changed) before.set(file, content) - if (changed.size) onChange(changed) - if ((await runner.openWindows()) === 0) return - } catch { - return - } - timer = window.setTimeout(tick, 1500) - } - let timer = window.setTimeout(tick, 1500) - return () => { - stopped = true - window.clearTimeout(timer) - } -} - -export type RunResult = { result: 'compile-error' | 'windows' | 'finished'; files: Map } - -/** - * Kompilieren und starten. `compile-error`: javac meldet Fehler (die stehen dann in der Konsole). - * Sonst nach dem Ende von main: `windows`, wenn noch Fenster offen sind (das Programm läuft weiter), oder `finished`. - */ -export async function compileAndRun(source: string, onCompiled: () => void): Promise { - const runner = await ready! - // Jeder Lauf in ein eigenes Verzeichnis mit eigenem Klassenlader, damit nichts vom vorigen Lauf übrig bleibt. - const dir = `/files/run${++run}/` - await runner.mkdirs(dir) - // Die Textdateien der Lektionen kommen in einen beschreibbaren Ordner des Laufs; `"brief.txt"` im Code zeigt dorthin. - const data = `${dir}data/` - const files = new Map(textFileEntries()) - await runner.mkdirs(data) - for (const [file, content] of files) { - cheerpOSAddStringFile(`/str/${file}`, content) - await runner.copy(`/str/${file}`, data + file) - } - const program = asProgram(java8Files(filesInto(source, data))) - const name = mainClassName(program) - cheerpOSAddStringFile(`/str/${name}.java`, program) - if ((await cheerpjRunMain('com.sun.tools.javac.Main', toolsJar, `/str/${name}.java`, '-d', dir, '-encoding', 'UTF-8')) !== 0) { - return { result: 'compile-error', files: new Map() } - } - // Die Fensterfläche muss vor dem Start sichtbar sein: Bei 0×0 Pixeln zeichnet Swing nichts. - onCompiled() - await runner.run(dir, name) - const written = await writtenFiles(runner, data, files) - if ((await runner.openWindows()) > 0) return { result: 'windows', files: written } - await new Promise((resolve) => setTimeout(resolve, 500)) - return { result: (await runner.openWindows()) > 0 ? 'windows' : 'finished', files: written } -} - -/** Eine Java-Datei eines Projekts (VisuObject): `pkg` wie in `package …;`, leer = Standard-Package. */ -export interface JavaSource { - pkg: string - name: string - text: string -} - -/** - * Projekt mit mehreren Klassen und Packages übersetzen und `main` der Klasse `mainClass` - * (voller Name, z. B. `spiel.Spielstarter`) starten. Rückgabe wie bei `compileAndRun`. - */ -export async function compileAndRunProject(files: JavaSource[], mainClass: string, onCompiled: () => void): Promise { - const runner = await ready! - const dir = `/files/run${++run}/` - const src = `${dir}src/` - const paths: string[] = [] - for (const f of files) { - const path = `${src}${f.pkg ? f.pkg.replace(/\./g, '/') + '/' : ''}${f.name}.java` - await runner.write(path, java8Files(f.text)) - paths.push(path) - } - const out = `${dir}classes/` - await runner.mkdirs(out) - if ((await cheerpjRunMain('com.sun.tools.javac.Main', toolsJar, ...paths, '-d', out, '-encoding', 'UTF-8')) !== 0) { - return { result: 'compile-error', files: new Map() } - } - onCompiled() - await runner.run(out, mainClass) - if ((await runner.openWindows()) > 0) return { result: 'windows', files: new Map() } - await new Promise((resolve) => setTimeout(resolve, 500)) - return { result: (await runner.openWindows()) > 0 ? 'windows' : 'finished', files: new Map() } -} diff --git a/web/src/ui/learnPanel.ts b/web/src/ui/learnPanel.ts deleted file mode 100644 index e7e99668..00000000 --- a/web/src/ui/learnPanel.ts +++ /dev/null @@ -1,1075 +0,0 @@ -import { checkExercise, fileLines, loopDepth, onlyLineBreaksMissing, usedElementTypes, type TestOutcome } from '../core/exerciseCheck' -import { compressDrive } from '../core/mbot' -import { isPlaceholderElement } from '../core/editing' -import { ElementType, makeElement, type VSDocument } from '../core/model' -import type { SubprogramResolver } from '../core/simulation' -import { decodeDocument } from '../core/xmlCodec' -import { explain, makeRound, usedValues, type BoolGame } from '../learn/boolGame' -import { contentLanguageFor, diagram, showingBlockLessons, glossary, image, lessons, reference, tasks, useContentLanguage, world } from '../learn/content' -import { exerciseArea, type Block, type ChecklistItem, type Exercise, type Lesson, type QuizQuestion, type Task } from '../learn/lessonFormat' -import { inline, markdownToHTML } from '../learn/markdown' -import ludwigURL from './ludwig.webp' -import { progress, type Rating } from '../learn/progress' -import { isTeacher, lockTeacher, unlockTeacher } from '../learn/teacher' -import { currentLanguage, t } from './i18n' -import { noticeText } from './simulationPanel' -import { renderCaesarDisc } from './caesarDisc' -import { renderRobotModel, renderRobotQuiz, wireRobotLinks } from './visubotModel' -import { renderRoundGame } from './roundGameView' -import { renderCrosswordGame, renderRescueGame } from './rescueGameView' -import { renderDriveGame } from './driveGameView' -import { renderCloze } from './clozeView' -import { renderPap } from './papView' -import { textFileView } from './textFileView' -import { attachPointers, attachTermTips, markPointers, markTerms, pointsIntoEditor, pointsIntoFiles, pointsIntoLiveCode, pointsIntoPalette, type UiArea } from './termTips' - -/** - * Lernbereich neben dem Editor (Vorbild: PicoJavaGoat): Lektionen in kurzen Schritten mit Übungen, - * Checkliste und Wissenstest, dazu Aufgaben und Nachschlagen. Übungen prüft die Simulation. - */ - -export interface LearnHost { - /** Übung im Editor öffnen (vorhandenen Reiter wählen oder neuen anlegen). */ - openExercise(exercise: Exercise, doc: VSDocument): void - /** Java-Beispiel aus dem Text in einem neuen Reiter öffnen, Spalte „Code“ auf Java. */ - openJavaCode(java: string): void - /** Beispieldiagramm in einem neuen Reiter öffnen. */ - openDiagram(doc: VSDocument, fileName: string): void - /** Diagramm des Reiters, in dem die Übung offen ist. */ - exerciseDocument(id: string): VSDocument | undefined - /** Unterprogramme für Aufrufe in der Übung: die anderen offenen Reiter. */ - subprogramsFor(id: string): SubprogramResolver - /** Eingeklappte Struktogramm-Spalte wieder zeigen. */ - showEditor(): void - /** Spalte „Code“ öffnen. */ - showLiveCode(): void - /** Spalte „Code“ auf Java öffnen (der Struktogramm-Editor bleibt, wie er ist). */ - showJava(): void - /** Spalte Dateien öffnen (die anderen Spalten bleiben, wie sie sind). */ - showFiles(): void - /** Eingeklappte Bereiche für einen Zeiger im Text öffnen; zurück kommt, wie man genau diese wieder einklappt. */ - reveal(areas: UiArea[]): () => void - /** Simulation beenden, damit man nach dem Prüfen gleich korrigieren kann. */ - stopSimulation(): void - /** Reiter einer Übung auf den Startzustand setzen (falls offen). */ - resetExercise(id: string, doc: VSDocument): void - preview(doc: VSDocument): SVGSVGElement | undefined - /** Lernbereich zur schmalen Leiste einklappen. */ - collapse(): void -} - -/** Seitenleisten-Symbol wie in Finder, Mail und Safari (kein ✕: Einklappen ist kein Schließen). */ -export const sidebarIcon = - '' - -type TabName = 'lessons' | 'tasks' | 'reference' - -const sections = (): { name: string; lessons: Lesson[] }[] => { - const out: { name: string; lessons: Lesson[] }[] = [] - for (const l of lessons) { - const last = out.at(-1) - if (last && last.name === l.section) last.lessons.push(l) - else out.push({ name: l.section, lessons: [l] }) - } - return out -} - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function html(tag: string, attrs: Record, markup: string): HTMLElement { - const e = h(tag, attrs) - e.innerHTML = markup - return e -} - -const typeName = (type: ElementType) => t(`element_${ElementType[type]}`) - -function emptyDocument(title: string): VSDocument { - return { caption: title, elements: [makeElement(ElementType.anweisung, { textLines: ['ø'] })] } -} - -export function startDocument(ex: Exercise): VSDocument { - const start = ex.start ? diagram(ex.start) : undefined - return start ? { ...start, caption: start.caption || ex.title } : emptyDocument(ex.title) -} - -export class LearnPanel { - readonly element: HTMLElement - private tab: TabName = 'lessons' - /** Wohin „Zurück zur Checkliste“ nach dem Nachlesen führt. */ - private returnTo?: { lesson: string; step: number; scroll: number } - private lessonId?: string - private taskId?: string - private body!: HTMLElement - private tips: { hide(): void } - private pointers: { off(): void } - - constructor(private host: LearnHost) { - this.element = h('aside', { class: 'learn', 'aria-label': t('web_learn_title') }) - this.tips = attachTermTips(this.element) - this.pointers = attachPointers(this.element, (areas) => this.host.reveal(areas)) - useContentLanguage(contentLanguageFor(currentLanguage())) - this.restore() - } - - /** Zuletzt geöffnete Ansicht aus dem Lernstand übernehmen und neu zeichnen, z. B. nach dem Einlesen der Datei. */ - restore() { - const view = progress.view() - if (view) { - this.tab = (['lessons', 'tasks', 'reference'] as const).find((x) => x === view.tab) ?? 'lessons' - this.lessonId = lessons.some((l) => l.id === view.lesson) ? view.lesson : undefined - this.taskId = tasks.some((x) => x.id === view.task) ? view.task : undefined - } - this.render() - } - - /** Neu aufbauen (z. B. nach einem Sprachwechsel). */ - render() { - this.tips.hide() - this.pointers.off() - useContentLanguage(contentLanguageFor(currentLanguage())) - const tabs = h('div', { class: 'segmented learn-tabs', role: 'tablist' }) - for (const [name, label] of [ - ['lessons', t('web_learn_lessons')], - ['tasks', t('web_learn_tasks')], - ['reference', t('web_learn_reference')], - ] as const) { - const b = h('button', { role: 'tab', 'aria-pressed': String(this.tab === name), 'data-learn-tab': name }, label) - b.addEventListener('click', () => { - if (this.tab === name) { - // Zweiter Klick: zurück zur Übersicht - if (name === 'lessons') this.lessonId = undefined - if (name === 'tasks') this.taskId = undefined - } - this.tab = name - this.persist() - this.render() - }) - tabs.append(b) - } - const hide = html('button', { class: 'learn-hide', title: t('web_learn_hide'), 'aria-label': t('web_learn_hide') }, sidebarIcon) - hide.addEventListener('click', () => this.host.collapse()) - const head = h('div', { class: 'learn-head' }, tabs, hide) - this.body = h('div', { class: 'learn-body' }) - this.element.replaceChildren(head, this.body) - if (this.tab === 'lessons') { - const lesson = lessons.find((l) => l.id === this.lessonId) - if (lesson) this.renderLesson(lesson) - else this.renderLessonList() - } else if (this.tab === 'tasks') { - const task = tasks.find((x) => x.id === this.taskId) - if (task) this.renderTask(task) - else this.renderTaskList() - } else { - const terms = glossary.map((e) => `- **${e.term}:** ${e.text}`).join('\n') - const text = html('div', { class: 'learn-text learn-reference' }, markdownToHTML(`${reference}\n\n## ${t('web_learn_terms')}\n\n${terms}`)) - this.body.append(text) - this.foldReference(text) - } - } - - /** Nachschlagen: jede Kategorie (## Überschrift) als aufklappbarer Abschnitt; offene Kategorien merkt sich der Browser. */ - private foldReference(root: HTMLElement) { - const key = 'visustruct.reference.open' - let open: string[] = [] - try { - open = JSON.parse(localStorage.getItem(key) ?? '[]') - } catch { - /* ohne Speicher alles zu */ - } - for (const heading of [...root.querySelectorAll('h2')]) { - const title = heading.textContent ?? '' - const details = h('details', { class: 'reference-group' }) as HTMLDetailsElement - details.open = open.includes(title) - const summary = h('summary', {}, title) - heading.replaceWith(details) - details.append(summary) - while (details.nextSibling && details.nextSibling.nodeName !== 'H2') details.append(details.nextSibling) - details.addEventListener('toggle', () => { - open = details.open ? [...new Set([...open, title])] : open.filter((x) => x !== title) - try { - localStorage.setItem(key, JSON.stringify(open)) - } catch { - /* nur für diese Sitzung */ - } - }) - } - } - - /** Reiter „Nachschlagen“ zeigen, z. B. vom Knopf in „Code“ aus. */ - showReference() { - this.tab = 'reference' - this.persist() - this.render() - } - - private persist() { - progress.setView({ tab: this.tab, lesson: this.lessonId, task: this.taskId }) - } - - /** Eine Lektion öffnen, z. B. aus einem Verweis der anderen Welt (`?visustruct&lektion=…`); unbekannte Kennungen bleiben unbeachtet. */ - showLesson(id: string) { - if (lessons.some((l) => l.id === id)) this.openLesson(id) - } - - private openLesson(id: string | undefined) { - this.tab = 'lessons' - this.lessonId = id - this.returnTo = undefined - this.persist() - this.render() - this.body.scrollTop = 0 - } - - // MARK: - Lektionen - - private renderLessonList() { - this.body.append(h('p', { class: 'hint' }, t('web_learn_intro'))) - for (const section of sections()) { - this.body.append(h('h3', { class: 'learn-section' }, section.name)) - const list = h('ol', { class: 'learn-list' }) - for (const l of section.lessons) { - const p = progress.lesson(l.id) - const status = p.done ? '✓' : p.step > 0 ? t('web_learn_step_of_fmt', p.step + 1, l.steps.length) : '' - const item = h( - 'button', - { class: 'learn-item' + (p.done ? ' done' : ''), 'data-lesson': l.id }, - h('span', { class: 'learn-num' }, String(l.number)), - h('span', { class: 'learn-item-text' }, h('strong', {}, l.robot ? `🤖 ${l.title}` : l.title), h('small', {}, l.summary)), - h('span', { class: 'learn-status' }, status), - ) - item.addEventListener('click', () => this.openLesson(l.id)) - list.append(h('li', {}, item)) - } - this.body.append(list) - } - this.body.append(this.renderTeacherBox()) - } - - private renderLesson(lesson: Lesson) { - const p = progress.lesson(lesson.id) - const index = Math.min(p.step, lesson.steps.length - 1) - const step = lesson.steps[index] - - const back = h('button', { class: 'learn-back' }, `← ${t('web_learn_all_lessons')}`) - back.addEventListener('click', () => this.openLesson(undefined)) - const bar = h('nav', { class: 'learn-progress', 'aria-label': t('web_learn_step_of_fmt', index + 1, lesson.steps.length) }) - bar.append( - ...lesson.steps.map((s, i) => { - const where = t('web_learn_step_fmt', i + 1, s.title) - const state = i < index ? 'past' : i === index ? 'current' : '' - // 🤖-Schritte (VisuBot im Kurs) türkis wie der mBot2 - const b = h('button', { class: s.title.startsWith('🤖') ? `${state} robot-step` : state, title: where, 'aria-label': where }, String(i + 1)) - if (i === index) b.setAttribute('aria-current', 'step') - b.addEventListener('click', () => this.goToStep(lesson, i)) - return b - }), - ) - // Kopf mit Zurück/Weiter bleibt oben stehen, nur der Inhalt darunter scrollt. - this.body.append( - h( - 'div', - { class: 'learn-lesson-head' }, - h('div', { class: 'learn-head-row' }, back, this.stepNav(lesson, index, p.done, 'learn-nav learn-nav-top')), - h('p', { class: 'learn-kicker' }, `${t('web_learn_lesson_fmt', lesson.number)} · ${lesson.section}${lesson.robot ? ' · 🤖 VisuBot' : ''}`), - // Dieselbe Nummer wie in der Liste, gut sichtbar neben dem Titel. - h('h2', { class: 'learn-title' }, h('span', { class: 'learn-num', 'aria-hidden': 'true' }, String(lesson.number)), lesson.title), - bar, - h('h3', { class: 'learn-step-title' }, `${index + 1}. ${step.title}`), - ), - ) - const ret = this.returnTo?.lesson === lesson.id && this.returnTo.step !== index ? this.returnTo : undefined - const backToChecklist = () => { - const b = h('button', { class: 'learn-return' }, `↩ ${t('web_learn_back_to_checklist')}`) - b.addEventListener('click', () => { - this.returnTo = undefined - this.goToStep(lesson, ret!.step) - this.body.scrollTop = ret!.scroll - }) - return b - } - if (ret) this.body.append(backToChecklist()) - step.blocks.forEach((block, bi) => this.body.append(this.renderBlock(block, `${index}-${bi}`, lesson))) - markPointers(this.body) - wireRobotLinks(this.body) - this.openNeededColumns(step.blocks) - markTerms(this.body, glossary) - - this.body.append(this.stepNav(lesson, index, p.done, 'learn-nav')) - if (ret) this.body.append(backToChecklist()) - } - - /** Zurück/Weiter (im letzten Schritt „Fertig“): oben im Kopf und unten nach dem Text. */ - private stepNav(lesson: Lesson, index: number, done: boolean | undefined, class_: string) { - const nav = h('div', { class: class_ }) - const prev = h('button', {}, `← ${t('web_learn_back')}`) as HTMLButtonElement - prev.disabled = index === 0 - prev.addEventListener('click', () => this.goToStep(lesson, index - 1)) - nav.append(prev) - if (index < lesson.steps.length - 1) { - const next = h('button', { class: 'primary' }, `${t('web_learn_next')} →`) - next.addEventListener('click', () => this.goToStep(lesson, index + 1)) - nav.append(next) - } else { - const finish = h('button', { class: 'primary' }, done ? `✓ ${t('web_learn_done')}` : t('web_learn_finish')) - finish.addEventListener('click', () => { - progress.updateLesson(lesson.id, { done: true }) - const following = lessons.find((l) => l.number === lesson.number + 1) - this.openLesson(following?.id) - }) - nav.append(finish) - } - return nav - } - - private goToStep(lesson: Lesson, step: number) { - progress.updateLesson(lesson.id, { step }) - this.render() - this.body.scrollTop = 0 - } - - // MARK: - Aufgaben - - /** Ein Schritt oder eine Aufgabe öffnet die Spalten, die sie gerade braucht. */ - private openNeededColumns(blocks: Block[]) { - const exercises = blocks.flatMap((b) => (b.kind === 'exercise' ? [b.exercise] : [])) - // Erklärt der Text Bereiche des Editors (z. B. Lektion 2), muss der Editor zu sehen sein. - if (pointsIntoEditor(this.body)) { - this.host.showEditor() - // Zeigt der Text auf die Elemente, dürfen sie nicht eingeklappt sein. - if (pointsIntoPalette(this.body)) this.host.reveal(['palette']) - } - // Geht es um die Spalte Dateien (Lektion 3), öffnet sie sich. - else if (pointsIntoFiles(this.body)) this.host.showFiles() - // Eine Struktogramm-Übung braucht den Struktogramm-Editor. - else if (exercises.some((ex) => ex.code !== 'java')) this.host.showEditor() - // Java (Beispiel als ```java oder Übung mit `code: java`) braucht die Spalte „Code“. - else if (exercises.length > 0 || this.body.querySelector('.lesson-code[data-lang=java]')) this.host.showJava() - // Dasselbe für Ausführen, Konsole und Einzelschritt in der Spalte „Code“. - if (pointsIntoLiveCode(this.body)) this.host.showLiveCode() - } - - private renderTaskList() { - this.body.append(h('p', { class: 'hint' }, t('web_learn_tasks_intro'))) - const list = h('ul', { class: 'learn-list' }) - for (const task of tasks) { - // Aufgaben ohne geprüfte Übung (z. B. Swing) gelten nie als erledigt. - const exercises = this.taskExercises(task) - const done = exercises.length > 0 && exercises.every((ex) => progress.exercise(ex.id).done) - const after = lessons.find((l) => l.number === task.after) - const item = h( - 'button', - { class: 'learn-item' + (done ? ' done' : ''), 'data-task': task.id }, - h('span', { class: 'learn-num', 'aria-label': t('web_learn_level_fmt', task.level) }, '★'.repeat(task.level)), - h( - 'span', - { class: 'learn-item-text' }, - h('strong', {}, task.title), - h('small', {}, after ? t('web_learn_after_fmt', after.number, after.title) : ''), - ), - h('span', { class: 'learn-status' }, done ? '✓' : ''), - ) - item.addEventListener('click', () => { - this.taskId = task.id - this.persist() - this.render() - this.body.scrollTop = 0 - }) - list.append(h('li', {}, item)) - } - this.body.append(list, this.renderTeacherBox()) - } - - /** Lehrkräfte schalten mit dem Lehrercode die Musterlösungen frei. */ - private renderTeacherBox(): HTMLElement { - const box = h('details', { class: 'learn-teacher' }, h('summary', {}, t('web_learn_teacher'))) - if (isTeacher()) { - box.setAttribute('open', '') - const off = h('button', {}, t('web_learn_teacher_off')) - off.addEventListener('click', () => { - lockTeacher() - this.render() - }) - box.append(h('p', {}, t('web_learn_teacher_on')), off) - return box - } - const input = h('input', { type: 'password', autocomplete: 'off', 'aria-label': t('web_learn_teacher_code'), placeholder: t('web_learn_teacher_code') }) as HTMLInputElement - const status = h('p', { class: 'learn-result', role: 'status' }) - const form = h('form', { class: 'group' }, input, h('button', { type: 'submit' }, t('web_learn_teacher_unlock'))) - form.addEventListener('submit', async (e) => { - e.preventDefault() - if (await unlockTeacher(input.value)) this.render() - else { - status.className = 'learn-result info' - status.textContent = t('web_learn_teacher_wrong') - } - }) - box.append(h('p', { class: 'hint' }, t('web_learn_teacher_intro')), form, status) - return box - } - - private taskExercises(task: Task): Exercise[] { - return task.blocks.flatMap((b) => (b.kind === 'exercise' ? [b.exercise] : [])) - } - - private renderTask(task: Task) { - const back = h('button', { class: 'learn-back' }, `← ${t('web_learn_all_tasks')}`) - back.addEventListener('click', () => { - this.taskId = undefined - this.persist() - this.render() - }) - const after = lessons.find((l) => l.number === task.after) - this.body.append(back, h('h2', { class: 'learn-title' }, task.title)) - if (after) { - const link = h('button', { class: 'link-button' }, t('web_learn_after_fmt', after.number, after.title)) - link.addEventListener('click', () => this.openLesson(after.id)) - this.body.append(h('p', { class: 'learn-kicker' }, link)) - } - task.blocks.forEach((block, i) => this.body.append(this.renderBlock(block, `task-${i}`))) - markPointers(this.body) - this.openNeededColumns(task.blocks) - markTerms(this.body, glossary) - } - - // MARK: - Blöcke - - private renderBlock(block: Block, key: string, lesson?: Lesson): HTMLElement { - switch (block.kind) { - case 'markdown': - return this.withOpenButtons(html('div', { class: 'learn-text' }, markdownToHTML(block.text))) - case 'diagram': - return this.renderDiagram(block.file, block.caption) - case 'exercise': - return this.renderExercise(block.exercise) - case 'competences': - return this.renderCompetences(block.items) - case 'checklist': - return this.renderChecklist(block.items, key, lesson) - case 'quiz': - return this.renderQuiz(block.questions, key, lesson) - case 'brag': - return this.renderBrag(block.text, block.tip) - case 'images': - return this.renderImages(block.files, block.caption) - case 'graphic': - return block.animated ? this.renderAnimation(block.file, block.caption) : this.renderGraphic(block.file, block.caption, block.labeled) - case 'game': - return this.renderGame(block.game) - case 'roundGame': - return renderRoundGame(block.game, ludwigURL, showingBlockLessons()) - case 'rescueGame': - return renderRescueGame(block.game, ludwigURL) - case 'driveGame': - return renderDriveGame(block.game, ludwigURL, showingBlockLessons()) - case 'crosswordGame': - return renderCrosswordGame(block.game, ludwigURL) - case 'caesar': - return renderCaesarDisc(block.key, block.text) - case 'cloze': - return renderCloze(block.cloze) - case 'pap': - return renderPap(block.pap) - case 'textFile': - return textFileView(block.name, block.content) - case 'robotModel': - return renderRobotModel(block.focus, block.view, block.spin) - case 'robotQuiz': - return renderRobotQuiz(block.tasks, block.done) - } - } - - /** Ludwig der Geißbock erzählt etwas zum Angeben oder gibt einen Tipp. */ - private renderBrag(text: string, tip = false): HTMLElement { - const box = h('aside', { class: 'learn-brag' }) - box.append(h('img', { src: ludwigURL, alt: '', width: '48', height: '48' })) - const body = h('div', {}, h('p', { class: 'learn-brag-title' }, t(tip ? 'web_learn_ludwig_tip' : 'web_learn_brag'))) - body.append(html('div', { class: 'learn-text' }, markdownToHTML(text))) - box.append(body) - return box - } - - /** Spiel „Wahr oder falsch?“: zehn Runden, die Bedingungen werden schwerer. */ - private renderGame(game: BoolGame): HTMLElement { - const rounds = 10 - const box = h('section', { class: 'learn-game' }) - const head = h('div', { class: 'learn-game-head' }) - head.append(h('img', { src: ludwigURL, alt: '', width: '40', height: '40' }), h('strong', {}, t('web_game_title'))) - const status = h('p', { class: 'learn-game-status' }) - const stage = h('div', { class: 'learn-game-stage' }) - box.append(head, status, stage) - let index = 0 - let score = 0 - - const showRound = () => { - const round = makeRound(game, index, rounds) - status.textContent = `${t('web_game_round_fmt', index + 1, rounds)} · ${t('web_game_score_fmt', score)}` - const values = h('div', { class: 'learn-game-values mono' }) - for (const [name, value] of usedValues(round)) values.append(h('code', {}, `${name} = ${value}`)) - const answers = h('div', { class: 'group learn-game-answers' }) - const yes = h('button', { class: 'learn-game-true' }, `✓ ${t('web_game_true')}`) as HTMLButtonElement - const no = h('button', { class: 'learn-game-false' }, `✗ ${t('web_game_false')}`) as HTMLButtonElement - answers.append(yes, no) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const answer = (guess: boolean) => { - yes.disabled = no.disabled = true - const right = guess === round.answer - if (right) score++ - status.textContent = `${t('web_game_round_fmt', index + 1, rounds)} · ${t('web_game_score_fmt', score)}` - feedback.className = `learn-game-feedback ${right ? 'right' : 'wrong'}` - const steps = h('div', { class: 'learn-game-steps mono' }) - explain(round.condition, round.values).forEach((s, i) => steps.append(h('div', {}, `${i ? '→ ' : ''}${s}`))) - const next = h('button', { class: 'primary' }, index + 1 < rounds ? t('web_game_next') : t('web_game_finish')) - next.addEventListener('click', () => { - index++ - if (index < rounds) showRound() - else showEnd() - }) - feedback.replaceChildren(h('p', {}, right ? t('web_game_right') : t('web_game_wrong')), steps, next) - next.focus() - } - yes.addEventListener('click', () => answer(true)) - no.addEventListener('click', () => answer(false)) - stage.replaceChildren(values, h('div', { class: 'learn-game-condition mono' }, round.condition), answers, feedback) - } - const showEnd = () => { - status.textContent = '' - const again = h('button', { class: 'primary' }, t('web_game_again')) - again.addEventListener('click', () => { - index = score = 0 - showRound() - }) - stage.replaceChildren( - h('p', { class: 'learn-game-result' }, t('web_game_done_fmt', score, rounds)), - h('p', {}, t(score === rounds ? 'web_game_perfect' : score >= rounds * 0.7 ? 'web_game_good' : 'web_game_practice')), - again, - ) - } - const start = h('button', { class: 'primary' }, t('web_game_start')) - start.addEventListener('click', showRound) - stage.append(h('p', {}, t('web_game_intro')), start) - return box - } - - private renderImages(files: string[], caption: string): HTMLElement { - const figure = h('figure', { class: 'learn-figure' }) - const row = h('div', { class: 'learn-images' }) - for (const f of files) { - const src = image(f) - if (src) row.append(h('img', { src, alt: '', loading: 'lazy' })) - } - figure.append(row) - if (caption) figure.append(html('figcaption', {}, inline(caption))) - return figure - } - - /** Erklärgrafik in voller Breite (meist SVG aus `lessons/bilder`). */ - private renderGraphic(file: string, caption: string, labeled?: string): HTMLElement { - const figure = h('figure', { class: 'learn-figure' }) - const src = image(file) - const labeledSrc = labeled ? image(labeled) : undefined - if (src && labeledSrc) { - // Ein Klick blendet die Beschriftung ein und wieder aus. - const img = h('img', { class: 'learn-graphic', src, alt: '', loading: 'lazy' }) as HTMLImageElement - const toggle = h('button', { class: 'learn-graphic-toggle', type: 'button', title: t('web_learn_graphic_labels'), 'aria-pressed': 'false' }, img) - toggle.addEventListener('click', () => { - const on = toggle.getAttribute('aria-pressed') !== 'true' - toggle.setAttribute('aria-pressed', String(on)) - img.src = on ? labeledSrc : src - }) - figure.append(toggle, h('p', { class: 'learn-graphic-hint' }, t('web_learn_graphic_labels'))) - } else if (src) figure.append(h('img', { class: 'learn-graphic', src, alt: '', loading: 'lazy' })) - if (caption) figure.append(html('figcaption', {}, inline(caption))) - return figure - } - - /** Animierte Erklärgrafik: Das SVG kommt direkt in die Seite, damit sich die CSS-Animation neu starten lässt. - * Sie startet, sobald die Grafik zu sehen ist; „Noch einmal abspielen“ spielt sie von vorn. - * Die SVGs stammen aus `lessons/bilder` und benennen ihre Klassen mit eigenem Präfix (z. B. `fz-`). - * Abspielen heißt: Klasse `play` am Wurzelelement, also z. B. `fz-play` bei `class="fz-svg"`. */ - private renderAnimation(file: string, caption: string): HTMLElement { - const figure = h('figure', { class: 'learn-figure' }) - const src = image(file) - if (!src) return figure - const stage = h('div', { class: 'learn-graphic learn-animation' }) - const replay = h('button', { class: 'learn-replay', type: 'button' }, `↻ ${t('web_learn_replay')}`) - let play = () => {} - fetch(src) - .then((r) => r.text()) - .then((text) => { - stage.innerHTML = text - const svg = stage.querySelector('svg') - const root = svg?.getAttribute('class')?.match(/(\S+-)svg\b/)?.[1] - if (!svg || !root) return - play = () => { - svg.classList.remove(`${root}play`) - void svg.getBoundingClientRect() - svg.classList.add(`${root}play`) - } - if (typeof IntersectionObserver === 'undefined') return play() - const seen = new IntersectionObserver((entries) => { - if (!entries.some((e) => e.isIntersecting)) return - seen.disconnect() - play() - }, { threshold: 0.6 }) - seen.observe(stage) - }) - .catch(() => stage.append(h('img', { src, alt: '' }))) - replay.addEventListener('click', () => play()) - figure.append(stage, replay) - if (caption) figure.append(html('figcaption', {}, inline(caption))) - return figure - } - - /** Unter jedes Java-Beispiel mit ```java editor kommt „Im Editor öffnen“. */ - private withOpenButtons(text: HTMLElement): HTMLElement { - for (const pre of text.querySelectorAll('pre.lesson-code[data-open=editor]')) { - const code = pre.textContent ?? '' - const open = h('button', { class: 'learn-open-code' }, t('web_learn_open_in_editor')) - open.addEventListener('click', () => this.host.openJavaCode(code)) - pre.after(open) - } - return text - } - - private renderDiagram(file: string, caption: string): HTMLElement { - const doc = diagram(file) - const figure = h('figure', { class: 'learn-figure' }) - if (!doc) return figure - const svg = this.host.preview(doc) - if (svg) figure.append(h('div', { class: 'learn-preview', dir: 'ltr' }, svg)) - if (caption) figure.append(html('figcaption', {}, inline(caption))) - const open = h('button', {}, t('web_learn_open_in_editor')) - open.addEventListener('click', () => this.host.openDiagram(doc, file)) - figure.append(open) - return figure - } - - private renderExercise(ex: Exercise): HTMLElement { - const card = h('section', { class: 'learn-exercise', 'data-exercise': ex.id }) - const state = () => progress.exercise(ex.id) - const badge = h('span', { class: 'learn-badge' }, `✓ ${t('web_learn_solved')}`) - badge.hidden = !state().done - card.append( - h('h4', {}, `${t('web_learn_exercise')}: ${ex.title}`, badge), - solveWhere(ex), - html('div', { class: 'learn-text' }, markdownToHTML(ex.task)), - ) - - const result = h('div', { class: 'learn-result', role: 'status' }) - // Tipps nacheinander aufdecken, vom allgemeinen zum genauen. Die Lösung gibt es nur im Lehrermodus. - const hints = h('div', { class: 'learn-hints' }) - - const actions = h('div', { class: 'group' }) - const open = h('button', { class: 'primary' }, t('web_learn_open_exercise')) - open.addEventListener('click', () => { - const saved = state().xml - let doc = startDocument(ex) - if (saved) { - try { - doc = decodeDocument(saved) - } catch { - /* gespeicherter Stand unlesbar: von vorn */ - } - } - this.host.openExercise(ex, doc) - }) - const check = h('button', {}, t('web_learn_check')) - check.addEventListener('click', () => { - const doc = this.host.exerciseDocument(ex.id) - if (!doc) { - result.className = 'learn-result info' - result.textContent = t('web_learn_open_first') - return - } - if (doc.elements.every(isPlaceholderElement)) { - result.className = 'learn-result info' - result.textContent = t('web_learn_empty') - return - } - this.host.stopSimulation() - const outcomes = checkExercise(doc, ex.tests, this.host.subprogramsFor(ex.id), world) - const used = usedElementTypes(doc) - const missing = ex.requires - .map((r) => (r === 'forZaehlSchleife' ? 'forSchleife' : r)) - .filter((r) => r in ElementType && !used.has(ElementType[r as keyof typeof ElementType])) - const tooFlat = ex.loopDepth !== undefined && loopDepth(doc.elements) < ex.loopDepth ? ex.loopDepth : undefined - const passed = outcomes.every((o) => o.passed) && missing.length === 0 && !tooFlat - this.showResult(result, outcomes, missing, passed, tooFlat) - if (passed) { - progress.updateExercise(ex.id, { done: true }) - badge.hidden = false - } - }) - actions.append(open, check) - if (ex.hints.length > 0) { - const tip = h('button', {}, t('web_learn_hint')) - tip.addEventListener('click', () => { - const shown = hints.children.length - if (shown >= ex.hints.length) return - const label = ex.hints.length > 1 ? `**${t('web_learn_hint_fmt', shown + 1, ex.hints.length)}:** ` : '' - hints.append(html('aside', { class: 'lesson-note' }, markdownToHTML(label + ex.hints[shown]))) - tip.textContent = t('web_learn_hint_more') - tip.toggleAttribute('disabled', shown + 1 >= ex.hints.length) - }) - actions.append(tip) - } - const reset = h('button', { class: 'danger-text' }, t('web_learn_reset')) - reset.addEventListener('click', () => { - if (reset.dataset.confirm !== '1') { - reset.dataset.confirm = '1' - reset.textContent = t('web_learn_reset_confirm') - setTimeout(() => { - reset.dataset.confirm = '' - reset.textContent = t('web_learn_reset') - }, 4000) - return - } - progress.resetExercise(ex.id) - this.host.resetExercise(ex.id, startDocument(ex)) - badge.hidden = true - result.replaceChildren() - result.className = 'learn-result' - reset.dataset.confirm = '' - reset.textContent = t('web_learn_reset') - }) - actions.append(reset) - card.append(actions, hints, result) - - if (isTeacher() && ex.solution) { - const solution = diagram(ex.solution) - if (solution) { - const details = h('details', { class: 'learn-solution' }, h('summary', {}, t('web_learn_solution'))) as HTMLDetailsElement - details.addEventListener('toggle', () => { - if (!details.open || details.querySelector('.learn-preview')) return - const svg = this.host.preview(solution) - if (svg) details.append(h('div', { class: 'learn-preview', dir: 'ltr' }, svg)) - const openSolution = h('button', {}, t('web_learn_open_in_editor')) - openSolution.addEventListener('click', () => { - this.host.openDiagram(solution, ex.solution!) - for (const file of ex.subprograms) { - const sub = diagram(file) - if (sub) this.host.openDiagram(sub, file) - } - }) - for (const file of ex.subprograms) { - const sub = diagram(file) - const subSvg = sub && this.host.preview(sub) - if (subSvg) details.append(h('div', { class: 'learn-preview', dir: 'ltr' }, subSvg)) - } - details.append(openSolution) - }) - card.append(details) - } - } - return card - } - - private showResult(box: HTMLElement, outcomes: TestOutcome[], missing: string[], passed: boolean, depth?: number) { - box.className = 'learn-result ' + (passed ? 'ok' : 'fail') - box.replaceChildren(h('p', { class: 'learn-result-head' }, passed ? `🎉 ${t('web_learn_passed')}` : t('web_learn_failed'))) - for (const m of missing) box.append(h('p', {}, `✗ ${t('web_learn_missing_fmt', typeName(ElementType[m as keyof typeof ElementType]))}`)) - if (depth) box.append(h('p', {}, `✗ ${t('web_visubot_depth_fmt', depth)}`)) - const list = h('ul', { class: 'learn-tests', dir: 'auto' }) - for (const o of outcomes) { - const worldIndex = o.test.world ? outcomes.filter((x) => x.test.world).indexOf(o) + 1 : 0 - const input = o.test.world - ? `🗺 ${t('web_visubot_world_fmt', worldIndex)}` - : o.test.actions - ? `🤖 ${t('web_visubot_actions')}` - : o.test.sensors - ? `🎛 ${Object.entries(o.test.sensors).map(([name, values]) => `mbot.${name}() = ${values.join(' → ')}`).join(', ')}` - : o.test.drive - ? `🤖 ${t('web_visubot_drive')}` - : o.test.inputs.length ? t('web_learn_input_fmt', o.test.inputs.join(', ')) : t('web_learn_no_input') - const lines: (string | Node)[] = [`${o.passed ? '✓' : '✗'} ${input}`] - const detail = h('div', { class: 'learn-test-detail' }) - if (o.test.drive) { - detail.append( - h('span', {}, `${t('web_learn_expected')}: `, h('code', {}, compressDrive(o.test.drive) || '–')), - h('span', {}, `${t('web_learn_actual')}: `, h('code', {}, compressDrive(o.result.drive ?? []) || '–')), - ) - } - if (o.test.actions) { - detail.append( - h('span', {}, `${t('web_learn_expected')}: `, h('code', {}, compressDrive(o.test.actions) || '–')), - h('span', {}, `${t('web_learn_actual')}: `, h('code', {}, compressDrive(o.result.actions ?? []) || '–')), - ) - } - if (o.test.led !== undefined) { - const led = (on: boolean | undefined) => t(on ? 'web_visubot_led_on' : 'web_visubot_led_off') - detail.append(h('span', {}, `💡 ${t('web_learn_expected')}: ${led(o.test.led)} · ${t('web_learn_actual')}: ${led(o.result.led)}`)) - } - if (o.test.tone !== undefined) { - const tone = (on: boolean | undefined) => t(on ? 'web_visubot_tone_yes' : 'web_visubot_tone_no') - detail.append(h('span', {}, `🔊 ${t('web_learn_expected')}: ${tone(o.test.tone)} · ${t('web_learn_actual')}: ${tone(o.result.tone)}`)) - } - if (o.test.screen !== undefined) - detail.append(h('span', {}, `📺 ${t('web_learn_expected')}: `, h('code', {}, o.test.screen), ` · ${t('web_learn_actual')}: `, h('code', {}, o.result.screen ?? '–'))) - if (o.test.world) { - if (world(o.test.world)?.goals?.length || world(o.test.world)?.targets?.length) - detail.append(h('span', {}, o.result.atGoal ? `✓ ${t('web_visubot_goal_reached')}` : `✗ ${t('web_visubot_goal_missed')}`)) - if (o.result.bumps) detail.append(h('span', { class: 'learn-error' }, `⚠︎ ${t('web_visubot_bumps')}: ${o.result.bumps}`)) - } - if ((!o.test.drive && !o.test.world && !o.test.sensors && !o.test.actions) || o.result.outputs.length) detail.append( - h('span', {}, `${t('web_learn_expected')}: `, h('code', {}, o.test.expected.join(' | ') || '–')), - h('span', {}, `${t('web_learn_actual')}: `, h('code', {}, o.result.outputs.join(' | ') || '–')), - ) - for (const f of o.test.files ?? []) { - detail.append( - h('span', {}, `📄 ${f.name}, ${t('web_learn_expected')}: `, h('code', {}, f.lines.join(' | ') || '–')), - h('span', {}, `📄 ${f.name}, ${t('web_learn_actual')}: `, h('code', {}, fileLines(o.result.files?.get(f.name)).join(' | ') || '–')), - ) - } - if (!o.passed && !o.result.error && onlyLineBreaksMissing(o.result.outputs, o.test.expected)) - detail.append(h('span', { class: 'learn-error' }, t('web_learn_missing_line_break'))) - if (o.result.error) detail.append(h('span', { class: 'learn-error' }, noticeText(o.result.error))) - if (o.result.missingInput) detail.append(h('span', { class: 'learn-error' }, t('web_learn_missing_input'))) - if (o.result.tooManySteps) detail.append(h('span', { class: 'learn-error' }, t('web_learn_too_many_steps'))) - if (!o.passed && o.result.unusedInputs) detail.append(h('span', { class: 'learn-error' }, t('web_learn_unused_input'))) - list.append(h('li', { class: o.passed ? 'ok' : 'fail' }, ...lines, detail)) - } - box.append(list) - } - - private renderCompetences(items: string[]): HTMLElement { - // Steht als eigener erster Schritt „Diese Kompetenzen erwerbe ich jetzt“; der Kasten braucht keine eigene Überschrift. - const box = h('aside', { class: 'learn-competences' }, h('p', { class: 'learn-competences-title' }, t('web_learn_competences_intro'))) - const list = h('ul', {}) - for (const item of items) list.append(html('li', {}, inline(item))) - box.append(list) - return box - } - - /** „Ich kann …“ mit Selbsteinschätzung rechts daneben: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. */ - private renderChecklist(items: ChecklistItem[], key: string, lesson?: Lesson): HTMLElement { - const ratings: { value: Rating; icon: string; label: string }[] = [ - { value: 'umbrella', icon: '☂️', label: t('web_learn_rating_umbrella') }, - { value: 'cloud', icon: '⛅', label: t('web_learn_rating_cloud') }, - { value: 'sun', icon: '☀️', label: t('web_learn_rating_sun') }, - ] - const wrap = h('div', { class: 'learn-checklist-wrap' }) - const legend = h('p', { class: 'learn-rating-legend' }) - for (const r of ratings) legend.append(h('span', {}, `${r.icon} ${r.label}`)) - const box = h('ul', { class: 'learn-checklist' }) - wrap.append(legend, box) - items.forEach((item, i) => { - const id = `${key}-${i}` - const stored = lesson ? progress.lesson(lesson.id).checks?.[id] : undefined - let current: Rating | undefined = stored === true ? 'sun' : stored || undefined - const text = html('span', { class: 'learn-check-text' }, inline(item.text)) - const group = h('span', { class: 'learn-rating', role: 'group', 'aria-label': text.textContent ?? '' }) - const buttons = ratings.map((r) => { - const b = h('button', { class: 'learn-rating-button', title: r.label, 'aria-label': r.label }, r.icon) - b.addEventListener('click', () => { - current = current === r.value ? undefined : r.value - show() - if (!lesson) return - const checks = { ...(progress.lesson(lesson.id).checks ?? {}) } - if (current) checks[id] = current - else delete checks[id] - progress.updateLesson(lesson.id, { checks: Object.keys(checks).length ? checks : undefined }) - }) - return b - }) - const show = () => { - buttons.forEach((b, bi) => b.setAttribute('aria-pressed', String(ratings[bi].value === current))) - li.dataset.rating = current ?? '' - } - group.append(...buttons) - const li = h('li', {}, text, group) - if (lesson && item.step !== undefined) { - const step = item.step - const where = t('web_learn_step_fmt', step + 1, lesson.steps[step].title) - const link = h('button', { class: 'link-button learn-check-link', title: where, 'aria-label': `${t('web_learn_review')}: ${where}` }, t('web_learn_review')) - link.addEventListener('click', () => { - this.returnTo = { lesson: lesson.id, step: progress.lesson(lesson.id).step, scroll: this.body.scrollTop } - this.goToStep(lesson, step) - }) - li.append(link) - } - show() - box.append(li) - }) - return wrap - } - - private renderQuiz(questions: QuizQuestion[], key: string, lesson?: Lesson): HTMLElement { - const box = h('div', { class: 'learn-quiz' }) - const score = h('p', { class: 'learn-score', role: 'status' }) - const answers = () => (lesson ? progress.lesson(lesson.id).quiz ?? {} : {}) - const review = h('div', { class: 'learn-review' }) - const updateScore = () => { - const a = answers() - const chosen = (q: QuizQuestion, qi: number) => - q.pairs ? q.pairs.every((_, pi) => a[`${key}-${qi}-${pi}`] !== undefined) : a[`${key}-${qi}`] !== undefined - const correct = (q: QuizQuestion, qi: number) => - !!(q.pairs ? q.pairs.every((_, pi) => matchOrder(q)[a[`${key}-${qi}-${pi}`]] === q.pairs![pi].right) : q.options[a[`${key}-${qi}`]]?.correct) - const answered = questions.filter(chosen).length - const right = questions.filter(correct).length - const complete = answered === questions.length - score.textContent = complete ? t('web_learn_score_fmt', right, questions.length) : '' - review.replaceChildren() - if (complete && lesson) this.fillChecklistReview(review, lesson, questions, (qi) => correct(questions[qi], qi)) - } - questions.forEach((q, qi) => { - const id = `${key}-${qi}` - const fieldset = h('fieldset', {}, html('legend', {}, inline(q.question))) - const explanation = html('p', { class: 'learn-explanation' }, inline(q.explanation ?? '')) - if (q.pairs) { - fieldset.append(this.renderMatch(q, id, answers, lesson, explanation, updateScore), explanation) - box.append(fieldset) - return - } - const showAnswer = (chosen: number | undefined) => { - fieldset.querySelectorAll('button').forEach((b, oi) => { - b.classList.toggle('right', chosen !== undefined && q.options[oi].correct) - b.classList.toggle('wrong', chosen === oi && !q.options[oi].correct) - b.setAttribute('aria-pressed', String(chosen === oi)) - }) - explanation.hidden = chosen === undefined || !q.explanation - } - q.options.forEach((o, oi) => { - const b = html('button', { class: 'learn-option' }, inline(o.text)) - b.addEventListener('click', () => { - if (lesson) progress.updateLesson(lesson.id, { quiz: { ...answers(), [id]: oi } }) - showAnswer(oi) - updateScore() - }) - fieldset.append(b) - }) - fieldset.append(explanation) - showAnswer(answers()[id]) - box.append(fieldset) - }) - box.append(score, review) - updateScore() - return box - } - - /** - * Nach dem Wissenstest die Checkliste noch einmal zeigen, mit Abweichungen: ☀️ gewählt, aber eine Frage zu diesem - * Schritt falsch, oder ⛅/☂️ gewählt und alle Fragen dazu richtig. Fragen verweisen mit `→ Schritt` auf den Schritt. - */ - private fillChecklistReview(box: HTMLElement, lesson: Lesson, questions: QuizQuestion[], correct: (qi: number) => boolean) { - const icons: Record = { sun: '☀️', cloud: '⛅', umbrella: '☂️' } - const checks = progress.lesson(lesson.id).checks ?? {} - const rows: HTMLElement[] = [] - let deviations = 0 - lesson.steps.forEach((step, si) => - step.blocks.forEach((block, bi) => { - if (block.kind !== 'checklist') return - block.items.forEach((item, i) => { - const stored = checks[`${si}-${bi}-${i}`] - const rating: Rating | undefined = stored === true ? 'sun' : stored || undefined - const linked = item.step === undefined ? [] : questions.map((q, qi) => (q.step === item.step ? qi : -1)).filter((qi) => qi >= 0) - const wrong = linked.filter((qi) => !correct(qi)) - let note = '' - if (rating === 'sun' && wrong.length) note = t('web_learn_review_overrated_fmt', wrong.map((qi) => qi + 1).join(', ')) - else if (rating && rating !== 'sun' && linked.length && !wrong.length) note = t('web_learn_review_underrated') - if (note) deviations++ - const li = h('li', { class: note ? 'learn-review-deviation' : '' }) - const text = html('span', { class: 'learn-check-text' }, inline(item.text)) - if (note) text.append(h('br', {}), h('em', {}, note)) - li.append(text, h('span', { class: 'learn-review-icon', 'aria-label': rating ? t(`web_learn_rating_${rating}`) : t('web_learn_review_unrated') }, rating ? icons[rating] : '–')) - if (note && rating === 'sun' && item.step !== undefined) { - const target = item.step - const link = h('button', { class: 'link-button learn-check-link' }, t('web_learn_review')) - link.addEventListener('click', () => { - this.returnTo = { lesson: lesson.id, step: progress.lesson(lesson.id).step, scroll: this.body.scrollTop } - this.goToStep(lesson, target) - }) - li.append(link) - } - rows.push(li) - }) - }), - ) - if (!rows.length) return - box.append( - h('p', { class: 'learn-review-title' }, `📋 ${t('web_learn_review_title')}`), - h('p', {}, deviations ? t('web_learn_review_hint') : t('web_learn_review_match')), - h('ul', { class: 'learn-checklist' }, ...rows), - ) - } - - /** Zuordnung: neben jedem linken Begriff eine Auswahl mit allen rechten Begriffen. */ - private renderMatch( - q: QuizQuestion, - id: string, - answers: () => Record, - lesson: Lesson | undefined, - explanation: HTMLElement, - updateScore: () => void, - ): HTMLElement { - const pairs = q.pairs! - const order = matchOrder(q) - const grid = h('div', { class: 'learn-match' }) - // Ohne Lektion (Aufgaben) merkt sich die Zuordnung ihre Antworten nur, solange sie angezeigt wird. - let local: Record = {} - const read = () => (lesson ? answers() : local) - const write = (next: Record) => (lesson ? progress.updateLesson(lesson.id, { quiz: next }) : (local = next)) - const value = (pi: number) => read()[`${id}-${pi}`] - const selects = pairs.map((p) => { - const select = h('select', { class: 'learn-match-select' }) as HTMLSelectElement - select.append(h('option', { value: '' }, '–')) - order.forEach((text, oi) => select.append(h('option', { value: String(oi) }, text))) - const label = html('label', { class: 'learn-match-left' }, inline(p.left)) - label.append(select) - grid.append(label) - return select - }) - const show = () => { - const complete = pairs.every((_, pi) => value(pi) !== undefined) - selects.forEach((select, pi) => { - const v = value(pi) - select.value = v === undefined ? '' : String(v) - select.classList.toggle('right', complete && order[v] === pairs[pi].right) - select.classList.toggle('wrong', complete && order[v] !== pairs[pi].right) - }) - explanation.hidden = !complete || !q.explanation - } - selects.forEach((select, pi) => - select.addEventListener('change', () => { - const next = { ...read() } - if (select.value === '') delete next[`${id}-${pi}`] - else next[`${id}-${pi}`] = Number(select.value) - write(next) - show() - updateScore() - }), - ) - show() - return grid - } -} - -/** Rechte Seiten einer Zuordnung in fester, alphabetischer Reihenfolge, damit die Auswahl nichts verrät. */ -function matchOrder(q: QuizQuestion): string[] { - return (q.pairs ?? []).map((p) => p.right).sort((a, b) => a.localeCompare(b, 'de')) -} - -/** - * Schild „Löse diese Übung im Struktogramm/im Code“. Die Bereichsnamen sind die Überschriften auf dem Bildschirm - * und stehen in der Farbe ihres Bereichs (Struktogramm grün, Code violett). - */ -function solveWhere(ex: Exercise): HTMLElement { - const area = exerciseArea(ex) - const key = area === 'beide' ? 'web_learn_solve_in_both' : area === 'code' ? 'web_learn_solve_in_code' : 'web_learn_solve_in_editor' - const names = { editor: t('web_editor_title'), code: t('web_live_title') } - const args = area === 'beide' ? ['\u0001editor', '\u0001code'] : [area === 'code' ? '\u0001code' : '\u0001editor'] - const p = h('p', { class: `learn-where learn-where-${area}` }) - for (const part of t(key, ...args).split(/(\u0001(?:editor|code))/)) { - if (part === '\u0001editor' || part === '\u0001code') { - const which = part.slice(1) as 'editor' | 'code' - p.append(h('span', { class: `area-name area-${which}` }, names[which])) - } else if (part) p.append(part) - } - return p -} - diff --git a/web/src/ui/liveCodePanel.ts b/web/src/ui/liveCodePanel.ts deleted file mode 100644 index f80bf046..00000000 --- a/web/src/ui/liveCodePanel.ts +++ /dev/null @@ -1,1023 +0,0 @@ -import { t } from './i18n' -import { - generateLiveCode, - livePlaceholderEnd, - livePlaceholderStart, - type LiveLabels, - type LiveSpan, - type LiveTarget, -} from '../core/codeGenerator' -import { parseLiveJava, type JavaProblem, type ParsedJava } from '../core/javaParser' -import { documentFromJava, liveJava, mergeLiveJava, plainLiveJava, type JavaForm } from '../core/liveSource' -import type { VSDocument } from '../core/model' -import { describeValue, SimulationEngine, type SimStep, type SubprogramResolver } from '../core/simulation' -import { gridTable, noticeText, type ConsoleTranscript } from './simulationPanel' -import { JavaEditor, type QuickFix, type SnippetWords } from './javaEditor' -import { icons } from './icons' -import { indentJava } from '../core/javaIndent' -import { sidebarIcon } from './learnPanel' -import { closeWindows, compileAndRun, prepareJava, watchWrittenFiles } from './javaRuntime' -import { writtenFilesView } from './textFileView' -import { codeWords, nameAt, nameRanges } from '../core/rename' -import { askRename, showRenameMenu } from './renameDialog' -import { visuBotEnabled } from './visubotView' - -/** - * Ansicht „Code live“: rechts neben dem Struktogramm der Java-, JavaScript- oder Python-Code, der bei jeder Änderung mitwächst. - * In Java kann man den Code auch selbst tippen: Dann baut sich das Struktogramm mit. - */ - -const targetKey = 'visustruct.live.target' -const javaClassKey = 'visustruct.live.javaClass' -const javaMainKey = 'visustruct.live.javaMain' -const swingKey = 'visustruct.live.swing' -const consoleHeightKey = 'visustruct.console.height' -const minConsoleHeight = 150 - -function storedFlag(key: string): boolean { - try { - return localStorage.getItem(key) === '1' - } catch { - return false - } -} - -function storedValue(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function storeValue(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } -} - -function storedTarget(): LiveTarget { - try { - const stored = localStorage.getItem(targetKey) - return stored === 'javascript' || stored === 'python' ? stored : 'java' - } catch { - return 'java' - } -} - -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>') - -/** Eine Codezeile als HTML; Platzhalter grau. */ -function lineHTML(line: string): string { - let out = '' - let rest = line - for (;;) { - const start = rest.indexOf(livePlaceholderStart) - if (start < 0) break - const end = rest.indexOf(livePlaceholderEnd, start) - if (end < 0) break - out += esc(rest.slice(0, start)) + `${esc(rest.slice(start + 1, end))}` - rest = rest.slice(end + 1) - } - return out + esc(rest) -} - -const samePath = (a: number[], b: number[]) => a.length === b.length && a.every((x, i) => x === b[i]) - -/** Innerstes Element, zu dem die Zeile gehört. */ -function spanAtLine(spans: LiveSpan[], line: number): LiveSpan | undefined { - let best: LiveSpan | undefined - for (const s of spans) { - if (line < s.from || line > s.to) continue - if (!best || s.to - s.from < best.to - best.from || (s.to - s.from === best.to - best.from && s.path.length > best.path.length)) best = s - } - return best -} - -/** Fehler erst nach einer kurzen Pause zeigen: Beim Tippen ist eine Zeile fast immer kurz „falsch“. */ -const problemDelay = 900 - -export interface LiveContext { - /** Gehört der Code zu diesem Reiter? Getippter Text wird je Reiter gemerkt. */ - owner: object - /** Unterprogramme aus den anderen Reitern. */ - subprograms: Set - /** Zeilen anklickbar und Code bearbeitbar (nicht während der Simulation). */ - editable: boolean - /** Für „Ausführen“: Unterprogramme in anderen Reitern. */ - resolve: SubprogramResolver -} - -/** So viele Schritte am Stück, dann kurz Luft für die Oberfläche (und für „Stopp“ bei Endlosschleifen). */ -const runChunk = 2000 - -export class LiveCodePanel { - readonly element: HTMLElement - private view!: HTMLElement - private editor!: HTMLElement - private code!: JavaEditor - private problemLine!: HTMLElement - private hint!: HTMLElement - private spans: LiveSpan[] = [] - private highlighted?: string - private html = '' - private target = storedTarget() - private last?: { doc: VSDocument; highlight?: number[]; context: LiveContext } - /** Getippter Code zu einem Dokument (auch für Rückgängig). */ - private textFor = new WeakMap() - private form: JavaForm = { asClass: storedFlag(javaClassKey), withMain: storedFlag(javaMainKey) } - /** Zuletzt gezeigter Code je Reiter: Grundlage, wenn sich das Struktogramm danach ändert. */ - private shown = new WeakMap() - private parsed?: ParsedJava - private placeholders: { from: number; to: number }[] = [] - private problems: JavaProblem[] = [] - private problemTimer?: number - private showProblems = true - /** Zeile (ab 0), in der die Simulation mit einem Fehler stehen geblieben ist: rote Zeilennummer. */ - private runErrorLine: number | undefined - /** - * Laufendes Programm. `paused`: Debugger hält vor einer Zeile an (Haltepunkt oder Einzelschritt). - * `stepping`: nach einer Eingabe wieder anhalten statt weiterlaufen. - */ - private run?: { - engine: SimulationEngine - doc: VSDocument - printed: number - timer?: number - paused: boolean - stepping: boolean - /** Einmal angehalten: Variablen bleiben bis zum Ende sichtbar. */ - debugged: boolean - /** Werte beim letzten Anhalten, um Änderungen hervorzuheben. */ - values?: Map - /** Text von `System.out.print("Note: ")`: steht während der Eingabe direkt vor dem Eingabefeld. */ - heldLine?: string - } - /** Zeile, die als Nächstes ausgeführt wird (gelb), solange der Debugger anhält. */ - private debugLine?: number - private breakpoints = new Set() - /** „Echtes Java (mit Swing)“ (Zahnrad-Menü): Java läuft dann als echtes Java mit CheerpJ. */ - private swing = storedFlag(swingKey) - /** Fläche für die Swing-Fenster; bleibt über `build()` hinweg bestehen, weil CheerpJ sie einmal übernimmt. */ - private swingElement: HTMLElement - private swingDisplay: HTMLElement - /** Beendet das Nachsehen, ob ein laufendes Swing-Programm noch Dateien speichert. */ - private stopWatchingFiles?: () => void - private runButton!: HTMLButtonElement - private stepButton!: HTMLButtonElement - private stopButton!: HTMLButtonElement - private indentButton!: HTMLButtonElement - private debugPanel!: HTMLElement - private debugVars!: HTMLElement - /** Konsole unter dem Code, darüber eine Trennlinie zum Verschieben. */ - private consoleElement: HTMLElement - private consoleResizer: HTMLElement - private consoleLines!: HTMLElement - private consoleForm!: HTMLFormElement - private consoleNotice!: HTMLElement - - /** Ausführen mit Roboter übernehmen; `true` = erledigt (setzt die App im VisuBot-Modus). */ - onRobotRun?: (stepping: boolean) => boolean - /** Knopf „Nachschlagen“: zeigt die Befehle im Lernbereich. */ - onReference?: () => void - /** ⇄: Programmieren groß und Struktogramm schmal bzw. zurück */ - onSwap?: () => void - /** Vor dem Umbenennen: eigener Rückgängig-Schritt statt Teil des Tippens davor. */ - onRename?: () => void - - constructor( - private onHide: () => void, - /** Klick auf eine Zeile wählt das Element im Struktogramm aus. */ - private onSelect: (path: number[]) => void, - /** Getippter Code ergab ein neues Struktogramm. */ - private onCode: (doc: VSDocument, select: number[] | undefined) => void, - private onUndo: (redo: boolean) => void, - ) { - this.element = document.createElement('aside') - this.element.className = 'livecode' - this.consoleElement = document.createElement('section') - this.consoleElement.className = 'console' - this.consoleElement.hidden = true - this.consoleResizer = this.makeConsoleResizer() - this.swingElement = document.createElement('section') - this.swingElement.className = 'console livecode-swing' - this.swingElement.hidden = true - this.swingDisplay = document.createElement('div') - this.swingDisplay.className = 'livecode-swing-display' - this.build() - } - - private get labels(): LiveLabels { - // Dieselben Wörter wie im Struktogramm und im Objektinspektor. - return { - condition: t('canvas_stub_condition'), - selector: t('web_live_ph_selector'), - code: t('web_live_ph_code'), - start: t('inspector_for_init'), - step: t('inspector_for_step'), - end: t('web_live_ph_end'), - } - } - - /** Neu aufbauen, z. B. nach einem Sprachwechsel. */ - build() { - if (this.run) window.clearTimeout(this.run.timer) - this.element.innerHTML = ` -
    -

    ${esc(t('web_live_title'))}

    - - - - - - - - -
    -

    -
    
    -      
    -

    - - ` - this.consoleElement.innerHTML = ` -
    -

    ${esc(t('web_console'))}

    - -
    -
    - -

    - ` - this.view = this.element.querySelector('.livecode-view')! - this.editor = this.element.querySelector('.livecode-editor')! - this.problemLine = this.element.querySelector('.livecode-problem')! - this.hint = this.element.querySelector('.hint')! - this.element.querySelector('.learn-hide')!.addEventListener('click', () => this.onHide()) - this.element.querySelector('.livecode-reference')!.addEventListener('click', () => this.onReference?.()) - this.element.querySelector('.livecode-swap')!.addEventListener('click', () => this.onSwap?.()) - this.runButton = this.element.querySelector('[data-run=play]')! - this.stepButton = this.element.querySelector('[data-run=step]')! - this.stopButton = this.element.querySelector('[data-run=stop]')! - this.indentButton = this.element.querySelector('.livecode-indent')! - this.indentButton.addEventListener('click', () => this.code.indent()) - this.debugPanel = this.element.querySelector('.livecode-debug')! - this.debugVars = this.element.querySelector('.livecode-vars')! - this.swingElement.innerHTML = ` -
    -

    ${esc(t('web_live_swing_windows'))}

    - -
    - ` - this.swingElement.append(this.swingDisplay) - this.swingElement.querySelector('.console-hide')!.addEventListener('click', () => { - this.swingElement.hidden = true - void closeWindows() - }) - const c = this.consoleElement - this.consoleResizer.title = t('web_console_resize') - this.element.append(this.swingElement, this.consoleResizer, c) - this.consoleLines = c.querySelector('.livecode-console-lines')! - this.consoleForm = c.querySelector('.livecode-console-input')! - this.consoleNotice = c.querySelector('.livecode-console-notice')! - c.querySelector('.console-hide')!.addEventListener('click', () => this.hideConsole()) - this.runButton.addEventListener('click', () => (this.run?.paused ? this.resume() : this.startRun(false))) - this.stepButton.addEventListener('click', () => (this.run?.paused ? this.stepOnce() : this.startRun(true))) - this.stopButton.addEventListener('click', () => this.stopRun(true)) - this.consoleForm.addEventListener('submit', (e) => { - e.preventDefault() - this.submitInput() - }) - this.run = undefined - this.view.addEventListener('click', (e) => { - const row = (e.target as HTMLElement).closest('.ln') - if (!row || !this.view.classList.contains('selectable')) return - const span = spanAtLine(this.spans, Number(row.dataset.line)) - if (span) this.onSelect(span.path) - }) - this.code = new JavaEditor( - this.editor, - { - onChange: () => this.codeChanged(), - onCaret: () => this.caretMoved(), - onUndo: (redo) => this.onUndo(redo), - words: () => this.snippetWords, - breakpointLine: (line) => spanAtLine(this.spans, line)?.from, - onBreakpoints: () => (this.breakpoints = this.code.breakpoints), - onRename: (pos, menu) => this.renameAt(pos, menu), - }, - t('web_live_edit_label'), - ) - this.editor.querySelector('.cm-gutters')!.title = t('web_debug_gutter_title') - this.breakpoints = new Set() - this.debugLine = undefined - this.highlighted = undefined - this.html = '' - this.parsed = undefined - this.showMode() - } - - /** Java-Code zum Speichern: wie in Code live (mit getippten Kommentaren), sonst neu erzeugt. */ - javaFor(doc: VSDocument, owner: object): string { - const known = this.textFor.get(doc) - if (known !== undefined) return known - const before = this.shown.get(owner) - if (before) return mergeLiveJava(before.text, before.doc, doc, this.labels, {}, this.form) - return plainLiveJava(doc, this.labels, this.form) - } - - /** Gespeicherten Code (.java) zum geöffneten Struktogramm übernehmen, samt Kommentaren. */ - adoptJava(doc: VSDocument, text: string) { - this.textFor.set(doc, text) - this.parsed = undefined - if (this.last) this.refresh(this.last.doc, this.last.highlight, this.last.context) - } - - /** Struktogramm aus einer .java-Datei ohne passendes .visustruct. */ - documentFromJavaText(text: string): VSDocument { - return documentFromJava({ caption: '', elements: [] }, parseLiveJava(text, this.labels)) - } - - /** Java als Snippet, mit `main`, in einer Klasse (Zahnrad-Menü). */ - get javaForm(): JavaForm { - return this.form - } - - setJavaForm(form: JavaForm) { - this.form = form - try { - localStorage.setItem(javaClassKey, form.asClass ? '1' : '0') - localStorage.setItem(javaMainKey, form.withMain ? '1' : '0') - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } - // Neu erzeugen: Der bisherige Text hat noch die alte Form. - this.textFor = new WeakMap() - this.shown = new WeakMap() - this.parsed = undefined - if (this.last) this.refresh(this.last.doc, this.last.highlight, this.last.context) - } - - /** „Echtes Java (mit Swing)“ (Zahnrad-Menü): erst beim nächsten Ausführen wird Java geladen. */ - get swingSupport(): boolean { - return this.swing - } - - setSwingSupport(on: boolean) { - this.swing = on - try { - localStorage.setItem(swingKey, on ? '1' : '0') - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } - if (!on) this.swingElement.hidden = true - } - - /** Programmiersprache (im Zahnrad-Menü). */ - get language(): LiveTarget { - return this.target - } - - setTarget(target: LiveTarget) { - this.target = target - try { - localStorage.setItem(targetKey, target) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } - this.showMode() - if (this.last) this.refresh(this.last.doc, this.last.highlight, this.last.context) - } - - /** Java: Code selbst tippen. JavaScript und Python: vorerst nur Anzeige. */ - private showMode() { - const java = this.target === 'java' - this.view.hidden = java - this.editor.hidden = !java - this.problemLine.hidden = !java - this.indentButton.hidden = !java - this.hint.textContent = t(java ? 'web_live_hint_edit' : 'web_live_hint') - this.element.querySelector('.livecode-lang')!.textContent = { java: 'Java', javascript: 'JavaScript', python: 'Python' }[this.target] - } - - /** `highlight`: ausgewähltes Element bzw. Schritt der Simulation. */ - refresh(doc: VSDocument, highlight: number[] | undefined, context: LiveContext) { - this.last = { doc, highlight, context } - if (this.target === 'java') this.refreshEditor(doc, highlight, context) - else this.refreshView(doc, highlight, context.editable) - } - - private refreshView(doc: VSDocument, highlight: number[] | undefined, selectable: boolean) { - const live = generateLiveCode(doc, this.target, this.labels) - this.spans = live.spans - const span = highlight && live.spans.find((s) => samePath(s.path, highlight)) - this.view.classList.toggle('selectable', selectable) - const html = live.text - .split('\n') - .map((line, i) => { - const marked = span && i >= span.from && i <= span.to - return `
    ${lineHTML(line) || ' '}
    ` - }) - .join('') - // Unverändert nicht neu aufbauen: Sonst geht ein Klick verloren, wenn das Verlassen des Objektinspektors - // zwischen Drücken und Loslassen neu zeichnet, und markierter Text bleibt markiert. - if (html !== this.html) this.view.innerHTML = html - this.html = html - // Nur scrollen, wenn sich die Markierung bewegt hat, sonst springt die Ansicht beim Tippen. - const key = span ? `${span.from}-${span.to}` : undefined - if (key && key !== this.highlighted) { - this.view.querySelector(`.ln[data-line="${span!.from}"]`)?.scrollIntoView({ block: 'nearest' }) - } - this.highlighted = key - } - - // MARK: - Ausführen - - /** - * Das Programm mit der Simulation ausführen: Ausgaben und Eingaben in der Konsole. - * Es hält an Haltepunkten an; `stepping`: gleich vor der ersten Zeile anhalten (Einzelschritt). - */ - private startRun(stepping: boolean) { - // VisuBot: Programme mit mBot2-Befehlen laufen in der Simulation (3D-Welt, echter mBot2, Markierung) - if (this.onRobotRun?.(stepping)) return - this.runErrorLine = undefined - // Wie javac: Ohne Import, ohne angelegten Scanner, mit vertipptem Datentyp oder falscher Groß- und Kleinschreibung - // (`system.out`) startet das Programm nicht. - const blocking = ['web_parse_missing_import', 'web_parse_missing_scanner', 'web_parse_unknown_type', 'web_parse_wrong_case'] - const missing = this.target === 'java' ? this.javaProblems.find((p) => blocking.includes(p.key)) : undefined - if (missing) { - this.stopRun(false) - this.setConsoleVisible(true) - this.consoleLines.replaceChildren() - this.consoleNotice.textContent = t('web_parse_line_fmt', this.code.lineAt(missing.from) + 1, t(missing.key, missing.arg ?? '')) - this.consoleNotice.classList.add('error') - this.showProblems = true - this.drawMarks(this.last?.highlight) - return - } - // Debuggen geht nur mit der Simulation; ohne Haltepunkt und Einzelschritt läuft Swing-Code als echtes Java. - if (this.swing && this.target === 'java' && !stepping && this.code.breakpoints.size === 0) return void this.startJavaRun() - if (!this.last) return - this.stopRun(false) - this.run = { - engine: new SimulationEngine(this.last.doc, this.last.context.resolve), - doc: this.last.doc, - printed: 0, - paused: false, - stepping, - debugged: false, - } - this.breakpoints = this.code.breakpoints - this.debugPanel.hidden = true - this.setConsoleVisible(true) - this.consoleLines.replaceChildren() - this.consoleNotice.textContent = '' - this.consoleNotice.classList.remove('error') - this.setRunning(true) - if (stepping) this.pause() - else this.continueRun(false) - } - - /** Mit „Echtes Java (mit Swing)“: den getippten Java-Code mit javac übersetzen und als echtes Java starten. */ - private async startJavaRun() { - this.stopRun(false) - this.stopWatchingFiles?.() - this.setConsoleVisible(true) - this.consoleLines.replaceChildren() - // CheerpJ schreibt System.out in das Element mit dieser id. - this.consoleLines.id = 'console' - this.consoleForm.hidden = true - this.consoleNotice.classList.remove('error') - this.runButton.disabled = this.stepButton.disabled = true - try { - this.consoleNotice.textContent = t('web_live_swing_loading') - await prepareJava(this.swingDisplay) - this.consoleNotice.textContent = t('web_live_swing_compiling') - const { result, files } = await compileAndRun(this.code.text, () => (this.swingElement.hidden = false)) - this.showWrittenFiles(files) - this.consoleNotice.textContent = t( - { 'compile-error': 'web_live_swing_compile_failed', windows: 'web_live_swing_running', finished: 'web_live_run_finished' }[result], - ) - this.consoleNotice.classList.toggle('error', result === 'compile-error') - // Ohne offenes Fenster braucht es die Fensterfläche nicht. - this.swingElement.hidden = result !== 'windows' - if (result === 'windows') this.stopWatchingFiles = watchWrittenFiles(files, (later) => this.showWrittenFiles(later)) - } catch { - this.consoleNotice.textContent = t('web_live_swing_failed') - this.consoleNotice.classList.add('error') - } - this.runButton.disabled = this.stepButton.disabled = false - } - - /** `fromPause`: Der erste Schritt läuft auch auf einem Haltepunkt, sonst bliebe „Weiter“ dort stehen. */ - private continueRun(fromPause: boolean) { - const run = this.run - if (!run) return - const { engine } = run - for (let i = 0; i < runChunk && engine.canStep; i++) { - if (!(fromPause && i === 0) && this.atBreakpoint(engine.steps[engine.state.stepIndex])) { - this.printNewLines() - return this.pause() - } - engine.step() - } - if (this.afterSteps()) return - run.timer = window.setTimeout(() => this.continueRun(false), 0) - } - - /** Ausgaben zeigen; Eingabe, Ende oder Fehler behandeln. `true`: Es geht nicht von selbst weiter. */ - private afterSteps(): boolean { - const run = this.run! - const state = run.engine.state - this.printNewLines() - if (state.inputRequest) { - this.askInput() - return true - } - if (state.isHalted || !run.engine.canStep) { - if (state.isHalted) this.runErrorLine = this.lineOf(run.engine.currentStepPath) - this.showWrittenFiles(state.files) - const notice = state.isHalted && state.notice ? noticeText(state.notice) : undefined - const where = this.runErrorLine - this.finishRun(notice ? (where !== undefined ? t('web_parse_line_fmt', where + 1, notice) : notice) : t('web_live_run_finished'), state.isHalted) - return true - } - return false - } - - /** Haltepunkt vor diesem Schritt? Im Unterprogramm nicht (dessen Zeilen stehen nicht hier), bei `for` nur vor der Bedingung. */ - private atBreakpoint(step: SimStep | undefined): boolean { - if (!step || step.callPath || step.kind === 'forLoopIncrement' || step.kind === 'forLoopStart' || step.kind === 'callEnd') return false - const line = this.lineOf(step.path) - return line !== undefined && this.breakpoints.has(line) - } - - private lineOf(path: number[] | undefined): number | undefined { - return path && this.spans.find((s) => samePath(s.path, path))?.from - } - - /** Debugger hält an: Zeile gelb, Element im Struktogramm ausgewählt, Variablen zeigen. */ - private pause() { - const run = this.run! - run.paused = true - run.debugged = true - const path = run.engine.currentStepPath - this.debugLine = this.lineOf(path) - this.consoleNotice.classList.remove('error') - this.consoleNotice.textContent = this.debugLine === undefined ? '' : t('web_debug_paused_fmt', this.debugLine + 1) - this.showVariables() - this.setRunning(true) - const current = this.last?.highlight - if (path && !(current && samePath(current, path))) this.onSelect(path) - else this.drawMarks(this.last?.highlight) - } - - /** Weiter bis zum nächsten Haltepunkt oder zum Ende. */ - private resume() { - const run = this.run - if (!run?.paused) return - run.paused = false - run.stepping = false - this.leavePause() - this.continueRun(true) - } - - /** Einzelschritt: einen Schritt ausführen und wieder anhalten. */ - private stepOnce() { - const run = this.run - if (!run?.paused) return - run.paused = false - run.stepping = true - this.leavePause() - const { engine } = run - const kind = engine.steps[engine.state.stepIndex]?.kind - engine.step() - // `for`: Zählen bzw. Anfangswert und Prüfen der Bedingung sind ein Schritt (dieselbe Zeile). - if ((kind === 'forLoopStart' || kind === 'forLoopIncrement') && engine.canStep && engine.steps[engine.state.stepIndex]?.kind === 'forLoopCondition') { - engine.step() - } - if (!this.afterSteps()) this.pause() - } - - private leavePause() { - this.debugLine = undefined - this.consoleNotice.textContent = '' - this.setRunning(true) - this.drawMarks(this.last?.highlight) - } - - /** Variablen wie in der Simulation; seit dem letzten Anhalten geänderte Werte leuchten kurz auf. */ - private showVariables() { - const run = this.run! - const variables = run.engine.state.variables - this.debugPanel.hidden = false - const names = [...variables.keys()].sort((a, b) => a.localeCompare(b)) - if (names.length === 0) { - this.debugVars.innerHTML = `

    ${esc(t('simulation_variables_placeholder'))}

    ` - } else { - const table = document.createElement('table') - for (const name of names) { - const value = variables.get(name)! - const text = describeValue(value) - const row = table.insertRow() - row.insertCell().textContent = name - const cell = row.insertCell() - if (value.kind === 'grid') cell.append(gridTable(value)) - else cell.textContent = text - if (run.values && run.values.get(name) !== text) row.classList.add('changed') - } - this.debugVars.replaceChildren(table) - } - // Beim ersten Anhalten ist alles neu: dann nichts hervorheben. - run.values = new Map(names.map((name) => [name, describeValue(variables.get(name)!)])) - } - - /** `typed`: Eingabezeile mit Frage und der getippten Antwort (farbig). */ - private printNewLines(typed?: { prompt: string; value: string; heldLine?: string }) { - const run = this.run - if (!run) return - const lines = run.engine.state.outputLines - for (; run.printed < lines.length; run.printed++) this.consoleLine(lines[run.printed]) - if (typed) { - // Nach `System.out.print("Note: ")` steht die Eingabe in derselben Zeile. - const div = this.consoleLine(typed.heldLine ?? (typed.prompt ? `${typed.prompt} ` : '')) - const value = document.createElement('span') - value.className = 'typed' - value.textContent = typed.value - div.append(value) - } - this.consoleLines.scrollTop = this.consoleLines.scrollHeight - } - - /** Nach dem Lauf: die Dateien, die das Programm gespeichert hat. */ - private showWrittenFiles(files: Map) { - if (!files.size) return - this.consoleLines.append(...writtenFilesView(files)) - this.consoleLines.scrollTop = this.consoleLines.scrollHeight - } - - private consoleLine(text: string): HTMLElement { - const div = document.createElement('div') - div.textContent = text - this.consoleLines.append(div) - return div - } - - private askInput() { - const run = this.run! - const request = run.engine.state.inputRequest! - const error = run.engine.state.inputErrorNotice - // Die angefangene Zeile wandert vor das Eingabefeld, damit die Frage auch in einer kleinen Konsole zu sehen ist. - const last = this.consoleLines.lastElementChild - if (request.continuesLine && run.heldLine === undefined && last) { - run.heldLine = last.textContent ?? '' - last.remove() - } - this.consoleForm.hidden = false - this.consoleForm.querySelector('span')!.textContent = run.heldLine ?? request.prompt - const field = this.consoleForm.querySelector('input')! - field.inputMode = request.valueType === 'string' || request.valueType === 'boolean' ? 'text' : 'decimal' - const errorLine = this.consoleForm.querySelector('.livecode-console-error')! - errorLine.textContent = error ? noticeText(error) : '' - errorLine.hidden = !error - requestAnimationFrame(() => field.focus()) - } - - private submitInput() { - const run = this.run - if (!run?.engine.state.inputRequest) return - const field = this.consoleForm.querySelector('input')! - const { prompt } = run.engine.state.inputRequest - run.engine.provideInput(field.value) - if (run.engine.state.inputRequest) return this.askInput() - const heldLine = run.heldLine - run.heldLine = undefined - this.printNewLines({ prompt, value: field.value, heldLine }) - field.value = '' - this.consoleForm.hidden = true - if (!run.stepping) return this.continueRun(false) - if (!this.afterSteps()) this.pause() - } - - private stopRun(byUser: boolean) { - if (!this.run) return - window.clearTimeout(this.run.timer) - if (this.run.heldLine !== undefined) this.consoleLine(this.run.heldLine) - if (byUser) this.finishRun(t('web_live_run_stopped'), false) - this.run = undefined - this.debugLine = undefined - this.debugPanel.hidden = true - this.setRunning(false) - this.drawMarks(this.last?.highlight) - } - - private finishRun(message: string, error: boolean) { - if (this.run) window.clearTimeout(this.run.timer) - this.consoleForm.hidden = true - this.consoleNotice.textContent = message - this.consoleNotice.classList.toggle('error', error) - // Nach dem Debuggen die Werte am Ende zeigen. - if (this.run?.debugged) this.showVariables() - else this.debugPanel.hidden = true - this.run = undefined - this.debugLine = undefined - this.setRunning(false) - this.drawMarks(this.last?.highlight) - } - - /** Während der Simulation: Konsole zeigt deren Ausgaben und Eingaben (beim Start leer). */ - showTranscript(transcript: ConsoleTranscript) { - this.stopRun(false) - this.setConsoleVisible(true) - this.consoleForm.hidden = true - this.consoleLines.replaceChildren() - for (const line of transcript.lines) { - const div = this.consoleLine(line.text) - if (line.typed !== undefined) { - const value = document.createElement('span') - value.className = 'typed' - value.textContent = line.typed - div.append(value) - } - } - if (transcript.pending !== undefined) this.consoleLine(transcript.pending).classList.add('pending') - this.consoleLines.scrollTop = this.consoleLines.scrollHeight - this.consoleNotice.textContent = transcript.notice ?? '' - this.consoleNotice.classList.toggle('error', transcript.error) - } - - /** Konsole schließen; ein laufendes Programm hält dabei an. */ - hideConsole() { - this.stopRun(false) - this.setConsoleVisible(false) - } - - private setConsoleVisible(on: boolean) { - if (this.consoleElement.hidden === !on) return - this.consoleElement.hidden = !on - this.consoleResizer.hidden = !on - } - - /** Waagrechte Trennlinie zwischen Code und Konsole: nach oben ziehen macht die Konsole höher. */ - private makeConsoleResizer(): HTMLElement { - const bar = document.createElement('div') - bar.className = 'console-resizer' - bar.hidden = true - bar.tabIndex = 0 - bar.setAttribute('role', 'separator') - bar.setAttribute('aria-orientation', 'horizontal') - const panel = this.consoleElement - // Mindestens so hoch, dass über dem Eingabefeld noch zwei Zeilen Ausgabe zu sehen sind. - const clamp = (height: number) => Math.round(Math.max(minConsoleHeight, Math.min(height, this.element.getBoundingClientRect().height - 160))) - const apply = (height: number | undefined, remember: boolean) => { - panel.style.height = height === undefined ? '' : `${clamp(height)}px` - if (remember) storeValue(consoleHeightKey, height === undefined ? '' : String(clamp(height))) - } - const saved = Number(storedValue(consoleHeightKey)) - if (saved > 0) panel.style.height = `${Math.max(minConsoleHeight, saved)}px` - bar.addEventListener('pointerdown', (e) => { - if (e.button !== 0) return - e.preventDefault() - bar.setPointerCapture(e.pointerId) - const startY = e.clientY - const startHeight = panel.getBoundingClientRect().height - bar.classList.add('dragging') - const move = (ev: PointerEvent) => apply(startHeight - (ev.clientY - startY), false) - const up = (ev: PointerEvent) => { - bar.removeEventListener('pointermove', move) - bar.removeEventListener('pointerup', up) - bar.removeEventListener('pointercancel', up) - bar.classList.remove('dragging') - apply(startHeight - (ev.clientY - startY), true) - } - bar.addEventListener('pointermove', move) - bar.addEventListener('pointerup', up) - bar.addEventListener('pointercancel', up) - }) - bar.addEventListener('dblclick', () => apply(undefined, true)) - bar.addEventListener('keydown', (e) => { - const step = e.key === 'ArrowUp' ? 20 : e.key === 'ArrowDown' ? -20 : 0 - if (!step) return - e.preventDefault() - apply(panel.getBoundingClientRect().height + step, true) - }) - return bar - } - - private setRunning(on: boolean) { - const paused = !!this.run?.paused - this.runButton.disabled = on && !paused - this.stepButton.disabled = on && !paused - this.stopButton.disabled = !on - this.indentButton.disabled = on - const tip = t(paused ? 'web_debug_continue' : 'web_live_run_title') - this.runButton.dataset.tip = tip - this.runButton.setAttribute('aria-label', paused ? tip : t('web_live_run')) - this.element.classList.toggle('running', on && !paused) - this.element.classList.toggle('debugging', paused) - // Während das Programm läuft, bleibt der Code, wie er ist. - this.code.setReadOnly(on || !this.last?.context.editable) - } - - // MARK: - Java tippen - - /** Code zum Dokument: selbst getippt, an eine Änderung im Struktogramm angepasst, oder neu erzeugt. */ - private textForDocument(doc: VSDocument, context: LiveContext): string { - const known = this.textFor.get(doc) - if (known !== undefined) return known - const before = this.shown.get(context.owner) - if (before) return indentChanged(before.text, mergeLiveJava(before.text, before.doc, doc, this.labels, { subprograms: context.subprograms }, this.form)) - return plainLiveJava(doc, this.labels, this.form) - } - - private refreshEditor(doc: VSDocument, highlight: number[] | undefined, context: LiveContext) { - const text = this.textForDocument(doc, context) - this.textFor.set(doc, text) - this.shown.set(context.owner, { doc, text }) - // Struktogramm geändert oder anderer Reiter: Das laufende Programm passt nicht mehr dazu. - if (this.run && this.run.doc !== doc) this.stopRun(false) - this.code.setReadOnly(!context.editable || !!this.run) - if (this.code.text !== text) { - this.code.setText(text) - this.analyze(text, context) - } else if (!this.parsed) this.analyze(text, context) - // Unverändert erzeugter Code: Zeilen genau wie beim Erzeugen (auch bei Kästen mit mehreren Zeilen). - const generated = liveJava(doc, this.labels, this.form) - this.spans = text === generated.text ? generated.spans : (this.parsed?.spans ?? []) - this.drawMarks(highlight) - } - - private analyze(text: string, context: LiveContext) { - this.parsed = parseLiveJava(text, this.labels, { subprograms: context.subprograms }) - this.placeholders = this.parsed.placeholders - this.problems = this.parsed.problems - } - - /** Pseudocode ist kein Java: keine Fehler anzeigen und den Start nicht verhindern. */ - private get javaProblems(): JavaProblem[] { - return this.last?.doc.pseudocode ? [] : this.problems - } - - private drawMarks(highlight: number[] | undefined) { - const span = highlight && this.spans.find((s) => samePath(s.path, highlight)) - const key = span ? `${span.from}-${span.to}` : undefined - // Während der Simulation ist die Markierung der aktuelle Befehl: gelb wie beim Debuggen - const simulated = this.last?.context.editable === false && span ? span.from : undefined - this.code.setMarks( - { - placeholders: this.placeholders, - problems: this.showProblems ? this.javaProblems : [], - lines: span && simulated === undefined ? { from: span.from, to: span.to } : undefined, - current: this.debugLine ?? simulated, - errorLines: this.errorLines(), - fixes: this.showProblems ? this.quickFixes() : [], - }, - key !== undefined && key !== this.highlighted && !this.code.hasFocus, - ) - const first = this.showProblems ? this.javaProblems[0] : undefined - this.problemLine.textContent = first ? t('web_parse_line_fmt', this.code.lineAt(first.from) + 1, t(first.key, first.arg ?? '')) : '' - this.highlighted = key - } - - /** Rote Zeilennummern: Zeilen mit Syntaxfehlern und die Zeile, an der das Programm abgebrochen ist. */ - private errorLines(): number[] { - const lines = this.showProblems ? this.javaProblems.map((p) => this.code.lineAt(p.from)) : [] - if (this.runErrorLine !== undefined) lines.push(this.runErrorLine) - return lines - } - - /** Glühbirnen: Für diese Fehler weiß VisuStruct genau, was fehlt. Pro Zeile höchstens ein Vorschlag. */ - private quickFixes(): QuickFix[] { - const text = this.code.text - const fixes: QuickFix[] = [] - for (const p of this.javaProblems) { - const lineStart = text.lastIndexOf('\n', p.from - 1) + 1 - const lineEnd = text.indexOf('\n', p.from) < 0 ? text.length : text.indexOf('\n', p.from) - if (fixes.some((f) => f.at === lineEnd)) continue - const insert = (from: number, what: string, shown = what.trim()) => - fixes.push({ at: lineEnd, title: t('web_quickfix_insert_fmt', shown), from, to: from, insert: what }) - const arg = p.arg ?? '' - if (p.key === 'web_parse_missing_import') { - // Hinter den vorhandenen Importen, sonst ganz oben - const imports = [...text.matchAll(/^import\s[^\n]*\n/gm)] - const last = imports[imports.length - 1] - insert(last ? last.index! + last[0].length : 0, `${arg}\n`) - } else if (p.key === 'web_parse_missing_scanner') { - const indent = /^[ \t]*/.exec(text.slice(lineStart))![0] - insert(lineStart, `${indent}${arg}\n`) - } else if ((p.key === 'web_parse_unknown_type' || p.key === 'web_parse_wrong_case') && arg) { - fixes.push({ at: lineEnd, title: t('web_quickfix_replace_fmt', arg, text.slice(p.from, p.to)), from: p.from, to: p.to, insert: arg }) - } else if (p.key === 'web_parse_missing' && (arg === ';' || arg === ')')) { - insert(p.to, arg) - } - } - return fixes - } - - /** Nach jeder Eingabe: Code lesen, Struktogramm neu, Element unter dem Cursor markieren. */ - private codeChanged() { - const context = this.last?.context - const doc = this.last?.doc - if (!context || !doc) return - const text = this.code.text - this.analyze(text, context) - const next = documentFromJava(doc, this.parsed!) - // Nur Kommentare oder Leerzeichen geändert: Struktogramm bleibt dasselbe. - const same = JSON.stringify([next.caption, next.elements]) === JSON.stringify([doc.caption, doc.elements]) - const result = same ? doc : next - this.textFor.set(result, text) - this.shown.set(context.owner, { doc: result, text }) - this.showProblems = false - this.runErrorLine = undefined - window.clearTimeout(this.problemTimer) - this.problemTimer = window.setTimeout(() => { - this.showProblems = true - this.drawMarks(this.last?.highlight) - }, problemDelay) - const line = this.code.lineAt(this.code.caret) - const path = spanAtLine(this.parsed!.spans, line)?.path - if (same) { - this.spans = this.parsed!.spans - this.drawMarks(this.last?.highlight) - if (path) this.onSelect(path) - } else this.onCode(result, path) - } - - /** Getippter Java-Code, solange man ihn bearbeiten kann (sonst gilt das Struktogramm). */ - get editableCode(): string | undefined { - return this.target === 'java' && this.last?.context.editable ? this.code.text : undefined - } - - /** Variable im Code umbenennen; das Struktogramm folgt wie beim Tippen. */ - renameInCode(from: string, to: string) { - this.onRename?.() - this.code.replaceRanges(nameRanges(this.code.text, from), to) - } - - /** F2 oder Rechtsklick im Code: Fenster „Variable umbenennen“ für das Wort an `pos`. */ - private renameAt(pos: number, menu?: { x: number; y: number }): boolean { - const text = this.editableCode - const word = text !== undefined ? nameAt(text, pos) : undefined - if (text === undefined || !word) return false - const ask = () => - void askRename({ - names: [word.name], - existing: codeWords(this.code.text).map((w) => w.name), - count: (name) => nameRanges(this.code.text, name).length, - }).then((r) => { - if (r) this.renameInCode(r.from, r.to) - this.code.view.focus() - }) - if (menu) showRenameMenu(menu.x, menu.y, t('web_rename_menu'), ask) - else ask() - return true - } - - /** Cursor in einer Zeile: passendes Element im Struktogramm markieren. */ - private caretMoved() { - const span = spanAtLine(this.spans, this.code.lineAt(this.code.caret)) - const current = this.last?.highlight - if (span && this.last?.context.editable && !(current && samePath(current, span.path))) this.onSelect(span.path) - } - - private get snippetWords(): SnippetWords { - const l = this.labels - return { - condition: l.condition, - selector: l.selector, - code: l.code, - start: l.start, - step: l.step, - branch: t('element_verzweigung'), - branchElse: t('element_verzweigung'), - switchCase: t('element_fallauswahl'), - forLoop: t('element_forSchleife'), - whileLoop: t('element_whileSchleife'), - doLoop: t('element_doUntilSchleife'), - output: t('inspector_io_mode_output'), - input: t('inspector_io_mode_input'), - robot: visuBotEnabled(), - } - } -} - -/** Nach einer Änderung im Struktogramm: nur die geänderten Zeilen passend zum getippten Code einrücken. */ -function indentChanged(before: string, after: string): string { - const a = before.split('\n') - const b = after.split('\n') - let start = 0 - while (start < a.length && start < b.length && a[start] === b[start]) start++ - if (start === b.length && a.length === b.length) return after - let end = 0 - while (end < a.length - start && end < b.length - start && a[a.length - 1 - end] === b[b.length - 1 - end]) end++ - return b.length - end > start ? indentJava(after, start + 1, b.length - end) : after -} diff --git a/web/src/ui/ludwig.webp b/web/src/ui/ludwig.webp deleted file mode 100644 index 28cfd94a..00000000 Binary files a/web/src/ui/ludwig.webp and /dev/null differ diff --git a/web/src/ui/mbot2Model.ts b/web/src/ui/mbot2Model.ts deleted file mode 100644 index 6139de55..00000000 --- a/web/src/ui/mbot2Model.ts +++ /dev/null @@ -1,419 +0,0 @@ -import * as THREE from 'three' -import { RoundedBoxGeometry } from 'three/examples/jsm/geometries/RoundedBoxGeometry.js' -import type { MBotPart } from '../core/mbot' - -/** - * Der mBot2 aus Grundformen nachgebaut (keine Modelldaten von Makeblock), nach Fotos von Holgers Roboter. Maße in cm: - * türkises Aluminium-Chassis (Wanne mit Seitenwangen, vorn runde „Ohren“, hinten Platte mit „M“), darauf das - * durchsichtige Shield mit Akku, quer hinten oben der CyberPi, vorn der schwarze Ultraschallsensor 2. Unten hellblaue - * Getriebe mit silbernen Motoren, Kugelrolle und vorn der Quad-RGB-Sensor. - * Ursprung ist die Mitte der Radachse auf dem Boden (um sie dreht sich der Roboter). Vorn ist −z, oben +y. - */ - -export type RobotPart = MBotPart - -export interface MBot2Model { - group: THREE.Group - parts: Map - /** Teilgruppen, z. B. zum Hervorheben */ - partGroups: Record - leds: THREE.MeshStandardMaterial[] - /** Bildschirm des CyberPi (Canvas, kann neu bemalt werden) */ - screen: { canvas: HTMLCanvasElement; texture: THREE.CanvasTexture } -} - -const blueAlu = 0x0fa3c6 - -function material(color: number, extra: THREE.MeshStandardMaterialParameters = {}) { - return new THREE.MeshStandardMaterial({ color, roughness: 0.5, ...extra }) -} - -/** Bildschirm des CyberPi: ohne Text das Startgesicht, sonst der Text von `mbot.showText` (umgebrochen). */ -export function drawScreen(canvas: HTMLCanvasElement, text?: string) { - const g = canvas.getContext('2d') - if (!g) return - const s = canvas.width - const bg = g.createLinearGradient(0, 0, 0, s) - bg.addColorStop(0, '#0f2f5c') - bg.addColorStop(1, '#09182e') - g.fillStyle = bg - g.fillRect(0, 0, s, s) - if (text !== undefined) { - g.fillStyle = '#ffffff' - g.textAlign = 'center' - g.textBaseline = 'middle' - const size = text.length <= 8 ? 0.16 : 0.11 - g.font = `bold ${Math.round(s * size)}px system-ui, sans-serif` - // Wortweise umbrechen, höchstens vier Zeilen - const lines: string[] = [] - for (const word of text.split(/\s+/)) { - const last = lines[lines.length - 1] - if (last !== undefined && g.measureText(`${last} ${word}`).width < s * 0.9) lines[lines.length - 1] = `${last} ${word}` - else lines.push(word) - } - const shown = lines.slice(0, 4) - const step = s * size * 1.2 - shown.forEach((line, i) => g.fillText(line, s / 2, s / 2 + (i - (shown.length - 1) / 2) * step, s * 0.94)) - return - } - // Freundliches Gesicht wie beim Start des CyberPi - g.fillStyle = '#5ee7ff' - for (const x of [0.33, 0.67]) { - g.beginPath() - g.ellipse(s * x, s * 0.4, s * 0.08, s * 0.12, 0, 0, Math.PI * 2) - g.fill() - } - g.strokeStyle = '#5ee7ff' - g.lineWidth = s * 0.045 - g.lineCap = 'round' - g.beginPath() - g.arc(s / 2, s * 0.55, s * 0.18, Math.PI * 0.2, Math.PI * 0.8) - g.stroke() - g.fillStyle = '#ffffff' - g.font = `bold ${Math.round(s * 0.11)}px system-ui, sans-serif` - g.textAlign = 'center' - g.fillText('VisuBot', s / 2, s * 0.9) -} - -/** Etikett als Textur (Akku, Shield). */ -function labelTexture(lines: { text: string; size: number; bold?: boolean }[], w: number, h: number, fg: string, bg: string | undefined) { - const canvas = document.createElement('canvas') - canvas.width = w - canvas.height = h - const g = canvas.getContext('2d') - if (g) { - if (bg) { - g.fillStyle = bg - g.fillRect(0, 0, w, h) - } - g.fillStyle = fg - g.textAlign = 'center' - g.textBaseline = 'middle' - lines.forEach((l, i) => { - g.font = `${l.bold ? 'bold ' : ''}${l.size}px system-ui, sans-serif` - g.fillText(l.text, w / 2, (h * (i + 0.5)) / lines.length) - }) - } - const texture = new THREE.CanvasTexture(canvas) - texture.colorSpace = THREE.SRGBColorSpace - return texture -} - -/** Radkappe mit dem Gittermuster des mBot2-Rads (Kreise und Quadrate). */ -function rimTexture() { - const canvas = document.createElement('canvas') - canvas.width = canvas.height = 256 - const g = canvas.getContext('2d') - if (g) { - g.fillStyle = '#16181b' - g.fillRect(0, 0, 256, 256) - g.strokeStyle = '#2c3036' - g.lineWidth = 9 - for (let i = 0; i < 6; i++) - for (let k = 0; k < 6; k++) { - const x = 38 + i * 36 - const y = 38 + k * 36 - g.beginPath() - if ((i + k) % 2) g.arc(x, y, 12, 0, Math.PI * 2) - else g.rect(x - 11, y - 11, 22, 22) - g.stroke() - } - } - const texture = new THREE.CanvasTexture(canvas) - texture.colorSpace = THREE.SRGBColorSpace - return texture -} - -/** Seitenwange des Chassis in der z/y-Ebene: Rechteck, vorn ein rundes Ohr mit Lochkreis, hinten ein schräger Arm. */ -function sideWallShape(): THREE.Shape { - const s = new THREE.Shape() - s.moveTo(-7.2, 1.4) - s.lineTo(4.8, 1.4) - // hinterer Arm schräg nach oben - s.lineTo(9.6, 4.6) - s.lineTo(8.8, 5.9) - s.lineTo(4.6, 3.6) - s.lineTo(4.6, 4.7) - s.lineTo(-6.6, 4.7) - // vorderes Ohr - s.absarc(-8.4, 3.1, 1.9, Math.PI * 0.35, Math.PI * 1.55, false) - s.lineTo(-7.2, 1.4) - const hole = (z: number, y: number, r: number) => { - const p = new THREE.Path() - p.absarc(z, y, r, 0, Math.PI * 2, true) - s.holes.push(p) - } - hole(-8.4, 3.1, 0.55) - for (let k = 0; k < 8; k++) { - const a = (k / 8) * Math.PI * 2 - if (Math.cos(a) > 0.6) continue - hole(-8.4 + Math.cos(a) * 1.25, 3.1 + Math.sin(a) * 1.25, 0.22) - } - for (let c = 0; c < 4; c++) for (let r = 0; r < 3; r++) hole(-5.6 + c * 0.95, 2.1 + r * 0.95, 0.25) - for (let k = 0; k < 3; k++) hole(6.0 + k * 1.05, 4.0 + k * 0.7, 0.22) - return s -} - -export function buildMBot2(): MBot2Model { - const group = new THREE.Group() - const parts = new Map() - const partGroups = {} as Record - const part = (name: RobotPart, parent: THREE.Object3D = group) => { - const g = new THREE.Group() - g.name = name - partGroups[name] = g - parts.set(g, name) - parent.add(g) - return g - } - const mesh = (geometry: THREE.BufferGeometry, mat: THREE.Material, x: number, y: number, z: number, parent: THREE.Object3D) => { - const m = new THREE.Mesh(geometry, mat) - m.position.set(x, y, z) - parent.add(m) - return m - } - const alu = material(blueAlu, { metalness: 0.7, roughness: 0.3 }) - const black = material(0x1b1d21, { roughness: 0.6 }) - const darkPlastic = material(0x2a2e35, { roughness: 0.45 }) - const silver = material(0xc9ced6, { metalness: 0.9, roughness: 0.25 }) - const rubber = material(0x141414, { roughness: 0.9 }) - const greyButton = material(0x9aa0a7, { roughness: 0.55 }) - const white = material(0xf2f2f2, { roughness: 0.5 }) - const pcb = material(0x1d2226, { roughness: 0.7 }) - // Rauchglas-Gehäuse von Shield und CyberPi - const smoke = new THREE.MeshPhysicalMaterial({ color: 0x262c31, transparent: true, opacity: 0.55, roughness: 0.12, metalness: 0.1, clearcoat: 1, depthWrite: false }) - - // Chassis: Wanne aus Blech, zwei Seitenwangen mit Ohren und Armen, vorn und hinten eine Platte - const chassis = part('chassis') - const floor = new THREE.Shape() - floor.moveTo(-4.9, -7.2) - floor.lineTo(4.9, -7.2) - floor.lineTo(4.9, 4.8) - floor.lineTo(-4.9, 4.8) - // Ausschnitt in der Mitte: Von unten sieht man die Motoren. - const opening = new THREE.Path() - opening.moveTo(-3.6, -3.4) - opening.lineTo(-3.6, 2.8) - opening.lineTo(3.6, 2.8) - opening.lineTo(3.6, -3.4) - floor.holes.push(opening) - const floorGeo = new THREE.ExtrudeGeometry(floor, { depth: 0.22, bevelEnabled: false }) - floorGeo.rotateX(Math.PI / 2) - mesh(floorGeo, alu, 0, 1.62, 0, chassis) - const wallGeo = new THREE.ExtrudeGeometry(sideWallShape(), { depth: 0.22, bevelEnabled: false, curveSegments: 16 }) - wallGeo.rotateY(-Math.PI / 2) - for (const side of [-1, 1]) mesh(wallGeo, alu, side * 4.9 + (side > 0 ? 0.22 : 0), 0, 0, chassis) - // Vorderplatte mit Lächeln - mesh(new THREE.BoxGeometry(9.8, 3.3, 0.22), alu, 0, 3.05, -7.2, chassis) - const smile = new THREE.Mesh(new THREE.TorusGeometry(1.1, 0.16, 6, 20, Math.PI * 0.8), black) - smile.rotation.z = Math.PI * 1.1 - smile.position.set(0, 2.9, -7.33) - chassis.add(smile) - // Hinterplatte schräg mit „M“-Ausschnitt - const back = new THREE.Shape() - back.moveTo(-4.9, 0) - back.lineTo(4.9, 0) - back.lineTo(4.9, 3.4) - back.lineTo(-4.9, 3.4) - const m = new THREE.Path() - m.moveTo(-1.1, 0.6) - m.lineTo(-1.1, 2.6) - m.lineTo(-0.5, 2.6) - m.lineTo(0, 1.9) - m.lineTo(0.5, 2.6) - m.lineTo(1.1, 2.6) - m.lineTo(1.1, 0.6) - m.lineTo(0.6, 0.6) - m.lineTo(0.6, 1.8) - m.lineTo(0, 1.1) - m.lineTo(-0.6, 1.8) - m.lineTo(-0.6, 0.6) - back.holes.push(m) - const backPlate = new THREE.Mesh(new THREE.ExtrudeGeometry(back, { depth: 0.22, bevelEnabled: false }), alu) - backPlate.rotation.x = -0.35 - backPlate.position.set(0, 1.5, 4.8) - chassis.add(backPlate) - - // Antrieb: hellblaue Getriebe an den Rädern, silberne Motoren quer und gegeneinander versetzt - const motors = part('motors') - const gearMat = material(0x8cc4ea, { roughness: 0.55 }) - for (const side of [-1, 1]) { - mesh(new RoundedBoxGeometry(2.0, 2.8, 3.0, 2, 0.2), gearMat, side * 3.7, 3.3, 0, motors) - const motor = mesh(new THREE.CylinderGeometry(0.75, 0.75, 2.8, 20), silver, side * 1.55, 3.0, side * 0.8, motors) - motor.rotation.z = Math.PI / 2 - const cap = mesh(new THREE.CylinderGeometry(0.8, 0.8, 0.5, 20), gearMat, side * 2.6, 3.0, side * 0.8, motors) - cap.rotation.z = Math.PI / 2 - } - - // Räder: schwarzer Reifen, schwarze Felge mit Gittermuster - const wheels = part('wheels') - const rim = material(0xffffff, { map: rimTexture(), roughness: 0.6 }) - for (const side of [-1, 1]) { - const wheel = new THREE.Group() - const tire = new THREE.Mesh(new THREE.CylinderGeometry(3.3, 3.3, 2.2, 44), rubber) - tire.rotation.z = Math.PI / 2 - wheel.add(tire) - const lip = new THREE.Mesh(new THREE.TorusGeometry(2.75, 0.32, 10, 44), rubber) - lip.rotation.y = Math.PI / 2 - lip.position.x = side * 1.1 - wheel.add(lip) - const face = new THREE.Mesh(new THREE.CircleGeometry(2.6, 40), rim) - face.rotation.y = (side * Math.PI) / 2 - face.position.x = side * 1.0 - wheel.add(face) - const hub = new THREE.Mesh(new THREE.CylinderGeometry(0.3, 0.3, 0.3, 12), silver) - hub.rotation.z = Math.PI / 2 - hub.position.x = side * 1.05 - wheel.add(hub) - wheel.position.set(side * 6.45, 3.3, 0) - wheel.name = side < 0 ? 'wheelLeft' : 'wheelRight' - wheels.add(wheel) - } - - // Kugelrolle vorn unter dem Boden - const caster = part('caster') - const casterHousing = mesh(new THREE.SphereGeometry(1.3, 20, 10, 0, Math.PI * 2, 0, Math.PI / 2), black, 0, 0.75, -4.6, caster) - casterHousing.scale.set(1.3, 0.7, 1) - mesh(new THREE.SphereGeometry(0.75, 20, 14), black, 0, 0.75, -4.6, caster) - for (const x of [-0.9, 0.9]) mesh(new THREE.CylinderGeometry(0.25, 0.25, 0.2, 12), silver, x, 1.28, -5.6, caster) - - // Quad-RGB-Liniensensor: schwarze Platine, die vorn unter der Vorderplatte herausschaut - const lineSensor = part('lineSensor') - mesh(new RoundedBoxGeometry(8.0, 0.3, 2.4, 2, 0.1), pcb, 0, 1.25, -8.0, lineSensor) - for (let i = 0; i < 4; i++) { - const x = -3.3 + i * 2.2 - mesh(new THREE.BoxGeometry(0.6, 0.12, 0.6), material(0xf5f5f5, { emissive: 0xffffff, emissiveIntensity: 0.5 }), x, 1.06, -8.3, lineSensor) - mesh(new THREE.SphereGeometry(0.13, 8, 6), material(0xff4040, { emissive: 0xff2a2a, emissiveIntensity: 1.4 }), x, 1.06, -7.6, lineSensor) - } - - // Shield: durchsichtiges Gehäuse, darin Platine und Akku - const shield = part('shield') - const shell = new THREE.Mesh(new RoundedBoxGeometry(9.4, 3.4, 11.2, 3, 0.5), smoke) - shell.position.set(0, 6.5, -1.4) - shell.renderOrder = 1 - shield.add(shell) - mesh(new THREE.BoxGeometry(9.0, 0.16, 10.8), pcb, 0, 5.1, -1.4, shield) - for (const [x, z] of [[-2, -4], [1.5, -3.6], [-1.6, -2.2], [1.8, -2.4]]) mesh(new THREE.CylinderGeometry(0.45, 0.45, 0.4, 14), black, x, 5.35, z, shield) - const batteryLabel = labelTexture( - [ - { text: 'makeblock', size: 56, bold: true }, - { text: 'mBot2 One Robot Per Child', size: 30 }, - ], - 512, - 160, - '#e9edf0', - '#777d84', - ) - const battery = new THREE.Mesh(new THREE.CylinderGeometry(1.05, 1.05, 7.0, 28), new THREE.MeshStandardMaterial({ map: batteryLabel, roughness: 0.4, metalness: 0.3 })) - battery.rotation.z = Math.PI / 2 - battery.rotation.x = -Math.PI / 2 - battery.position.set(0, 6.3, 0.6) - shield.add(battery) - // Hinten: Motoranschlüsse M1, M2 und zwei mBuild-Buchsen mit Kabeln - for (const x of [-3.2, -2.3]) mesh(new THREE.BoxGeometry(0.6, 0.5, 0.3), white, x, 5.6, 4.25, shield) - for (const x of [0.4, 2.2]) { - mesh(new THREE.BoxGeometry(1.5, 0.45, 0.35), white, x, 5.6, 4.25, shield) - const cable = mesh(new THREE.TorusGeometry(0.7, 0.12, 6, 12, Math.PI), black, x, 5.0, 4.5, shield) - cable.rotation.y = Math.PI / 2 - } - // Links: Schalter ON/OFF und die Servo-Anschlüsse S1, S2 - const powerSwitch = part('powerSwitch') - mesh(new THREE.BoxGeometry(0.2, 0.5, 1.2), darkPlastic, -4.72, 5.8, -3.4, powerSwitch) - mesh(new THREE.BoxGeometry(0.3, 0.38, 0.45), white, -4.8, 5.8, -3.6, powerSwitch) - const switchLabel = new THREE.Mesh( - new THREE.PlaneGeometry(1.4, 0.4), - new THREE.MeshBasicMaterial({ map: labelTexture([{ text: 'ON/OFF', size: 44, bold: true }], 256, 72, '#e9edf0', undefined), transparent: true }), - ) - switchLabel.rotation.y = -Math.PI / 2 - switchLabel.position.set(-4.76, 5.25, -3.4) - powerSwitch.add(switchLabel) - for (const z of [-1.9, -0.9]) mesh(new THREE.BoxGeometry(0.3, 0.45, 0.7), white, -4.6, 5.8, z, shield) - - // CyberPi quer hinten oben: Joystick links, Bildschirm in der Mitte, zwei Tasten rechts - const cyberpiBody = new THREE.Group() - group.add(cyberpiBody) - const cyberpi = part('cyberpi', cyberpiBody) - const case_ = new THREE.Mesh(new RoundedBoxGeometry(7.9, 1.0, 3.1, 3, 0.25), smoke) - case_.renderOrder = 1 - cyberpi.add(case_) - mesh(new THREE.BoxGeometry(7.6, 0.12, 2.9), pcb, 0, -0.1, 0, cyberpi) - const nameLabel = new THREE.Mesh( - new THREE.PlaneGeometry(1.6, 0.4), - new THREE.MeshBasicMaterial({ map: labelTexture([{ text: 'CyberPi', size: 44 }], 256, 64, '#c9d1d8', undefined), transparent: true }), - ) - nameLabel.position.set(-2.6, 0, 1.56) - cyberpi.add(nameLabel) - // USB-C rechts an der Stirnseite (daneben die Home-Taste, siehe Tasten) - mesh(new THREE.BoxGeometry(0.12, 0.3, 0.9), black, 3.98, 0.05, -0.7, cyberpi) - - const screenPart = part('screen', cyberpiBody) - const canvas = document.createElement('canvas') - canvas.width = canvas.height = 256 - drawScreen(canvas) - const texture = new THREE.CanvasTexture(canvas) - texture.colorSpace = THREE.SRGBColorSpace - mesh(new THREE.BoxGeometry(3.0, 0.08, 2.6), material(0xdfe3e6), 0.1, 0.47, 0, screenPart) - const screen = new THREE.Mesh( - new THREE.PlaneGeometry(2.7, 2.3), - new THREE.MeshStandardMaterial({ map: texture, emissive: 0xffffff, emissiveMap: texture, emissiveIntensity: 0.9, roughness: 0.15 }), - ) - screen.rotation.x = -Math.PI / 2 - screen.position.set(0.1, 0.52, 0) - screenPart.add(screen) - - const joystick = part('joystick', cyberpiBody) - mesh(new THREE.CylinderGeometry(0.75, 0.75, 0.12, 24), material(0x8a9096), -2.75, 0.52, 0, joystick) - mesh(new THREE.CylinderGeometry(0.22, 0.22, 0.5, 12), material(0x6c7278), -2.75, 0.75, 0, joystick) - mesh(new THREE.CylinderGeometry(0.55, 0.6, 0.32, 24), greyButton, -2.75, 1.05, 0, joystick) - - // Tasten: Quadrat (zur Fahrtrichtung) und Dreieck; Home sitzt an der Seite - const buttons = part('buttons', cyberpiBody) - mesh(new RoundedBoxGeometry(0.85, 0.3, 0.85, 2, 0.15), greyButton, 2.6, 0.58, -0.62, buttons) - const triangle = mesh(new THREE.CylinderGeometry(0.55, 0.55, 0.3, 3), greyButton, 2.6, 0.58, 0.68, buttons) - triangle.rotation.y = Math.PI / 2 - mesh(new THREE.CylinderGeometry(0.24, 0.24, 0.22, 14).rotateZ(Math.PI / 2), greyButton, 3.98, 0.05, 0.5, buttons) - - // Fünf RGB-LEDs als Lichtleiste an der hinteren Längskante des CyberPi (Foto IMG_4737) - const ledPart = part('leds', cyberpiBody) - mesh(new THREE.BoxGeometry(6.6, 0.3, 0.06), material(0xeeeeee, { transparent: true, opacity: 0.3, roughness: 0.2 }), 0, -0.12, 1.68, ledPart) - const leds: THREE.MeshStandardMaterial[] = [] - for (let i = 0; i < 5; i++) { - const led = new THREE.MeshStandardMaterial({ color: 0xdddddd, emissiveIntensity: 1, roughness: 0.3, toneMapped: false }) - leds.push(led) - mesh(new THREE.BoxGeometry(0.7, 0.22, 0.08), led, -2.8 + i * 1.4, -0.12, 1.63, ledPart) - } - // Lichtsensor und Mikrofon neben dem Joystick - for (const [x, name] of [[-1.75, 'lightSensor'], [-1.75, 'microphone']] as const) { - const z = name === 'lightSensor' ? -1.05 : 1.05 - mesh(new THREE.CylinderGeometry(0.14, 0.14, 0.08, 10), material(0x0a0a0a, { roughness: 0.2 }), x, 0.52, z, part(name, cyberpiBody)) - } - cyberpiBody.position.set(0, 8.55, 3.15) - - // Ultraschallsensor 2 vorn: schwarzes Gehäuse, zwei schwarze Schallwandler mit grauem Ring - const ultrasonic = part('ultrasonic') - mesh(new RoundedBoxGeometry(6.4, 2.8, 1.3, 3, 0.4), black, 0, 0, 0, ultrasonic) - for (const side of [-1, 1]) { - const can = mesh(new THREE.CylinderGeometry(1.2, 1.2, 1.3, 32), black, side * 1.6, 0, -1.1, ultrasonic) - can.rotation.x = Math.PI / 2 - const ring = mesh(new THREE.TorusGeometry(0.98, 0.17, 10, 32), material(0x9ea4aa, { metalness: 0.5, roughness: 0.35 }), side * 1.6, 0, -1.76, ultrasonic) - ring.name = 'sonarRing' - const grid = mesh(new THREE.CircleGeometry(0.85, 28), material(0x3a3f45, { roughness: 0.9 }), side * 1.6, 0, -1.7, ultrasonic) - grid.rotation.y = Math.PI - } - // Halterung mit zwei Schrauben unter dem Sensor - mesh(new RoundedBoxGeometry(2.6, 1.5, 0.3, 2, 0.12), black, 0, -1.6, -0.3, ultrasonic) - for (const x of [-0.7, 0.7]) mesh(new THREE.CylinderGeometry(0.28, 0.28, 0.2, 12).rotateX(Math.PI / 2), silver, x, -1.75, -0.5, ultrasonic) - ultrasonic.position.set(0, 6.4, -8.0) - - // Jedes Teil bekommt eigene Materialien, damit beim Hervorheben nur dieses Teil leuchtet (LEDs bleiben geteilt). - group.traverse((o) => { - const mesh = o as THREE.Mesh - if (!mesh.isMesh) return - const transparent = (mesh.material as THREE.Material).transparent - mesh.castShadow = !transparent - mesh.receiveShadow = true - const m = mesh.material as THREE.MeshStandardMaterial - if (!leds.includes(m)) mesh.material = m.clone() - }) - return { group, parts, partGroups, leds, screen: { canvas, texture } } -} diff --git a/web/src/ui/mbotUpload.ts b/web/src/ui/mbotUpload.ts deleted file mode 100644 index 3d227555..00000000 --- a/web/src/ui/mbotUpload.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { mbotColorLanguage } from '../core/mbot' -import { mbotLink } from '../core/mbotLink' -import { mbotProgram } from '../core/mbotProgram' -import type { VSDocument } from '../core/model' -import { t } from './i18n' - -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>') - -/** - * VisuBot: „Auf mBot2 übertragen“. Übersetzt das Programm für den CyberPi, speichert es dort fest und erklärt, - * wie es weitergeht: Kabel ab, mBot2 an den Start, Taste A. - */ -export function uploadToMBot(doc: VSDocument, subprograms: VSDocument[]) { - const dialog = document.createElement('dialog') - dialog.className = 'about mbot-upload' - dialog.setAttribute('aria-labelledby', 'mbot-upload-title') - dialog.innerHTML = ` -
    -

    ⬆ ${esc(t('web_visubot_upload_title'))}

    - -
    -
    -
      -
    1. ${esc(t('web_visubot_upload_step1'))}
    2. -
    3. ${esc(t('web_visubot_upload_step2'))}
    4. -
    5. ${esc(t('web_visubot_upload_step3'))}
    6. -
    - -

    - -
    ` - const status = dialog.querySelector('.mbot-upload-status')! - const bar = dialog.querySelector('progress')! - const start = dialog.querySelector('[data-action=upload-start]')! - let busy = false - const close = () => !busy && dialog.close() - dialog.querySelector('[data-action=upload-close]')!.addEventListener('click', close) - dialog.addEventListener('cancel', (ev) => busy && ev.preventDefault()) - dialog.addEventListener('click', (ev) => ev.target === dialog && close()) - dialog.addEventListener('close', () => dialog.remove()) - - const program = mbotProgram(doc, subprograms, mbotColorLanguage(), t('web_visubot_upload_label')) - if ('error' in program) { - status.textContent = `⚠︎ ${t(`web_visubot_upload_error_${program.error}`)}` - start.hidden = true - } - - start.addEventListener('click', async () => { - if (!('source' in program)) return - busy = true - start.disabled = true - bar.hidden = false - bar.value = 0 - status.textContent = t('web_visubot_upload_busy') - try { - await mbotLink.upload(program.source, (done, total) => (bar.value = done / total)) - bar.value = 1 - status.textContent = `✓ ${t('web_visubot_upload_done')}` - status.classList.add('done') - } catch (e) { - const error = e as Error - // Auswahl des Anschlusses abgebrochen: nichts melden - status.textContent = error.name === 'NotFoundError' ? '' : `⚠︎ ${t('web_visubot_upload_failed')} (${error.message})` - bar.hidden = true - await mbotLink.disconnect(error.name === 'NotFoundError' ? undefined : error.message).catch(() => undefined) - } finally { - busy = false - start.disabled = false - } - }) - document.body.append(dialog) - dialog.showModal() -} diff --git a/web/src/ui/objectApp.ts b/web/src/ui/objectApp.ts deleted file mode 100644 index 68b088a4..00000000 --- a/web/src/ui/objectApp.ts +++ /dev/null @@ -1,3452 +0,0 @@ -import { - addInterface, - addMember, - classModifiers, - deleteClass, - deleteMember, - parseClasses, - parseUmlLine, - removeInterface, - renameClass, - replaceMember, - setAbstract, - setFieldType, - setFinal, - setPublic, - setSuperclass, - umlText, - type UmlClass, - type UmlMember, - type Visibility, -} from '../core/classModel' -import { - addFile, - addPackage, - addSourceFolder, - barePackage, - classFileText, - defaultRoot, - diskPath, - exampleProject, - fileNameOf, - filesIn, - isFolderName, - isPackageName, - moveFile, - packageKey, - packageOf, - packagesOf, - pathFor, - projectClasses, - projectFromFiles, - projectFromJSON, - projectToJSON, - removeFile, - removePackage, - removeSourceFolder, - renamePackage, - renameSourceFolder, - rootOf, - sourceFoldersOf, - updateFile, - type JavaProject, - type UmlNote, - type ProjectClass, - type UmlObject, -} from '../core/javaProject' -import { - associations, - dependencies, - ensureImport, - fieldNameFor, - fieldTypeFor, - generalizations, - isMany, - multiplicities, - relationKey, - renameInfo, - withInfo, - type Assoc, - type AssocInfo, - type AssocKind, - type Dependency, - type EndNav, - type Generalization, -} from '../core/umlRelations' -import { gearIcon } from './app' -import { currentLanguage, t } from './i18n' -import { worldHomeURL } from './site' -import { icons } from './icons' -import { codeScopes, JavaEditor, setCodeScopes } from './javaEditor' -import { closeWindows, compileAndRunProject, prepareJava } from './javaRuntime' -import { ObjectDiagram, type LiveState } from './objectDiagram' -import { sequenceSVG, type SequenceData } from './sequenceDiagram' -import { MiniJava, MiniJavaError, type FrameView, type HeapObject, type StepMode } from '../core/miniJava' -import type { OwnTypes } from '../core/javaMembers' -import { ownTypes } from '../core/ownTypes' -import { hasSections, keepSections, withSections, type SectionLabels } from '../core/sections' -import { ObjectLayout } from './objectLayout' -import { setVisuBotMode, setVisuFlowWorld, worldChooserHTML, worldTitleHTML, type WorldChoice } from './visubotView' -import { classSource, newWizard, type NewKind } from './objectNew' -import { addFieldConstructor, addGettersSetters, addToString, classDoc, fieldInfo, paramsText, parseParams, setClassDoc, setField, setMethod, type FieldInfo, type MethodInfo } from '../core/memberEdit' -import { freeProjectName, listProjects, readProject, safeFolderName, workspaceFolderName, writeProject, type Positions } from './objectWorkspace' -import { canPickFolder, pickFolder, reopenFolder, storedFolder, type WsDir } from './workspaceStore' - -/** - * Welt VisuObject: links der Package Explorer (Projekt › src › Packages › Dateien › Klassen › Mitglieder), - * in der Mitte der Java-Code der geöffneten Datei, rechts das Klassendiagramm aller Dateien. - * Tippen baut das Diagramm neu, eine Änderung im Diagramm schreibt nur die betroffene Stelle im Code neu. - * Siehe docs/visuobject/PLAN.md. - */ - -declare const __APP_VERSION__: string - -const projectKey = 'visuobject.project' -/** Stufe 1 hatte nur eine Datei. */ -const oldCodeKey = 'visuobject.code' -const activeKey = 'visuobject.active' -const positionsKey = 'visuobject.positions' -const treeKey = 'visuobject.tree' -const tabsKey = 'visuobject.tabs' -/** Zuletzt geöffnetes Projekt im Workspace. */ -const wsProjectKey = 'visuobject.wsProject' -/** `0`: Objektinspektor eingeklappt. */ -const inspectorKey = 'visuobject.inspector' -/** Höhe des Objektinspektors in px, leer = so hoch wie sein Inhalt. */ -const inspectorHeightKey = 'visuobject.inspectorHeight' -/** `1`: ohne Workspace-Ordner arbeiten, nicht mehr fragen. */ -const noWorkspaceKey = 'visuobject.noWorkspace' -const diagramModeKey = 'visuobject.diagram' -/** Haltepunkte je Datei: Pfad → Zeilen (ab 0). */ -const breakpointsKey = 'visuobject.breakpoints' -/** Gemerkter Workspace-Ordner, getrennt vom Ordner „VisuStruct-Dateien“. */ -const workspaceSlot = 'visuobject-workspace' -const maxUndoSteps = 80 - -type Point = { x: number; y: number } - -const e = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) -const identifier = /^[\p{L}_$][\p{L}\p{N}_$]*$/u - -function stored(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function store(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher nur für diese Sitzung */ - } -} - -function siteURL(): string { - return worldHomeURL('visuobject', currentLanguage()) -} - -function download(name: string, data: BlobPart, type: string) { - const url = URL.createObjectURL(new Blob([data], { type })) - const a = document.createElement('a') - a.href = url - a.download = name - a.click() - setTimeout(() => URL.revokeObjectURL(url), 1000) -} - -/** Was im Diagramm ausgewählt ist: eine Klasse, evtl. eines ihrer Mitglieder (Index in `members`). */ -interface Selection { - cls: string - member?: number -} - -/** Knoten im Package Explorer: `project`, `src`, `pkg:tiere`, `file:tiere/Hund.java`, `class:Hund`. */ -type NodeId = string - -type DiagramMode = 'class' | 'object' | 'sequence' - -const chevron = '' - -export class ObjectApp { - private shell: HTMLElement - private editor!: JavaEditor - private canvas!: HTMLElement - private lines!: SVGSVGElement - private frames!: SVGSVGElement - private status!: HTMLElement - private tree!: HTMLElement - private tabs!: HTMLElement - /** Offene Dateien als Reiter über dem Code. */ - private openTabs: string[] = [] - private project: JavaProject - /** Geöffnete Datei (Pfad unter `src/`). */ - private active?: string - /** Im Explorer gewähltes Package: Dort landen neue Klassen. */ - private currentPackage?: string - private classes: ProjectClass[] = [] - private positions = new Map() - private selection?: Selection - /** Gewählte Beziehung: `assoc:Klasse.attribut` oder `gen:extends:Hund:Tier`. */ - private relSelection?: string - private inspector!: HTMLElement - /** Gewähltes Werkzeug der Leiste über dem Diagramm. */ - private tool: Tool = 'select' - /** Beziehungswerkzeug: erste Klasse ist angeklickt, die zweite fehlt noch. */ - private pendingFrom?: string - /** Der Klick nach einem Werkzeug-Einsatz wählt nichts aus. */ - private suppressClick = false - /** `Klasse.attribut` aller Attribute, die als Rolle an einer Linie stehen. */ - private treeSelection?: NodeId - private open = new Map() - private history: string[] = [] - private future: string[] = [] - private lastTyping = 0 - /** Gerade bearbeitetes Eingabefeld im Diagramm oder Explorer: dann nicht neu zeichnen. */ - private editing = false - private objectDiagram!: ObjectDiagram - /** Im Diagramm-Bereich: Klassendiagramm oder Objektdiagramm. */ - private diagramMode: DiagramMode = (['object', 'sequence'] as const).find((m) => m === stored(diagramModeKey)) ?? 'class' - /** Letzter Mitschnitt fürs Sequenzdiagramm (bleibt nach dem Programmende stehen). */ - private sequence?: SequenceData - /** Gewählte Notiz im Diagramm. */ - private noteSelection?: string - /** Workspace-Ordner wie in Eclipse (nur Chrome/Edge); ohne ihn bleibt das Projekt im Browser. */ - private workspace?: WsDir - /** Gemerkter Ordner, für den der Browser erst wieder fragen muss. */ - private pendingFolder?: FileSystemDirectoryHandle - private projects: string[] = [] - /** Ordner des geöffneten Projekts im Workspace; nach dem Umbenennen bis zum Speichern noch der alte. */ - private folder?: string - /** Stand beim letzten Speichern bzw. Laden: Projekt als JSON und Text je Datei. */ - private saved?: string - private savedFiles = new Map() - private wsBox!: HTMLElement - private breakpoints = new Map>() - /** Laufender Debugger: Programm, Takt, Stand fürs Objektdiagramm. */ - private dbg?: { - vm: MiniJava - timer?: number - state: 'running' | 'paused' | 'input' - frames: FrameView[] - frame: number - places: Map - values: Map - vars: Map - } - - constructor(root: HTMLElement) { - document.documentElement.classList.add('visuobject-theme') - history.replaceState(history.state, '', `${location.pathname}?visuobject${location.hash}`) - try { - const saved = JSON.parse(stored(positionsKey) ?? '{}') as Record - for (const [name, p] of Object.entries(saved)) if (Number.isFinite(p?.x) && Number.isFinite(p?.y)) this.positions.set(name, p) - const tree = JSON.parse(stored(treeKey) ?? '{}') as Record - for (const [id, on] of Object.entries(tree)) this.open.set(id, !!on) - const bps = JSON.parse(stored(breakpointsKey) ?? '{}') as Record - for (const [path, lines] of Object.entries(bps)) if (Array.isArray(lines)) this.breakpoints.set(path, new Set(lines.filter(Number.isInteger))) - } catch { - /* alte oder kaputte Einstellungen: Standard */ - } - const old = stored(oldCodeKey) - this.project = projectFromJSON(stored(projectKey) ?? '') ?? (old !== null ? projectFromFiles('Projekt', [{ name: '', text: old }]) : exampleProject()) - const active = stored(activeKey) - this.active = this.project.files.some((f) => f.path === active) ? active! : this.project.files[0]?.path - try { - const tabs = JSON.parse(stored(tabsKey) ?? '[]') as unknown - if (Array.isArray(tabs)) this.openTabs = tabs.filter((p): p is string => typeof p === 'string' && this.project.files.some((f) => f.path === p)) - } catch { - /* ohne Reiter */ - } - this.shell = document.createElement('div') - this.shell.className = 'shell visuobject' - root.replaceWith(this.shell) - this.build() - new ObjectLayout(this.shell.querySelector('.vo-columns')!, () => this.drawLines()) - this.showActive() - this.refresh() - window.addEventListener('keydown', (ev) => this.onKey(ev)) - window.addEventListener('resize', () => this.drawLines()) - window.addEventListener('beforeunload', (ev) => { - if (this.isDirty()) ev.preventDefault() - }) - void this.restoreWorkspace() - } - - // MARK: - Aufbau - - private build() { - const button = (action: string, label: string, icon: string, tip = label) => - `` - this.shell.innerHTML = ` -
    - VisuObject ${e(t('web_visuobject_subtitle'))} -
    - ${button('new', t('toolbar_new'), icons.newFile, t('web_visuobject_new_tip'))} - ${button('open', t('menu_open'), icons.open, t('web_visuobject_open_tip'))} - ${button('save', t('menu_save'), icons.save, t('web_visuobject_save_tip'))} - ${button('undo', t('toolbar_undo'), icons.undo)} - ${button('redo', t('toolbar_redo'), icons.redo)} - -
    -
    - - - - -
    -
    - - -
    -
    -
    -
    - -
    -

    ${e(t('web_visuobject_explorer'))}

    - -
    - -
    -
    - - -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    - -
    - -
    - -
    -
    - - - -
    -
    - -
    - -
    - - -
    -
    - - -
    - -
    -
    -

    ${e(t('web_visuobject_inspector'))}

    - -
    -
    -
    -

    ${e(t('web_visuobject_edit_hint'))}
    -
    -
    ` - this.canvas = this.shell.querySelector('.vo-canvas:not(.vo-objcanvas):not(.vo-seqcanvas)')! - this.shell.querySelector('.vo-seqcanvas')!.addEventListener('click', (ev) => { - const i = (ev.target as Element).closest('[data-ev]')?.dataset.ev - const pos = i !== undefined ? this.sequence?.events[Number(i)]?.pos : undefined - if (pos?.file) this.showLine(pos.file, pos.line) - }) - const objCanvas = this.shell.querySelector('.vo-objcanvas')! - this.objectDiagram = new ObjectDiagram({ - canvas: objCanvas, - lines: objCanvas.querySelector('.vo-objlines')!, - inspector: this.shell.querySelector('.vo-inspector')!, - classes: () => this.classes, - objects: () => this.project.objects ?? [], - setObjects: (objects) => this.setProject({ ...this.project, objects: objects.length ? objects : undefined }), - message: (text) => this.message(text), - }) - for (const b of this.shell.querySelectorAll('[data-diagram]')) b.addEventListener('click', () => this.setDiagramMode(b.dataset.diagram as DiagramMode)) - this.shell.querySelector('[data-obj-add]')!.addEventListener('click', () => { - const cls = this.shell.querySelector('[data-obj-class]')!.value - if (cls) this.objectDiagram.add(cls) - }) - this.lines = this.canvas.querySelector('.vo-lines')! - this.frames = this.canvas.querySelector('.vo-frames')! - this.status = this.shell.querySelector('.vo-status')! - this.inspector = this.shell.querySelector('.vo-inspector')! - // Der Objektinspektor hat einen festen Platz unter dem Diagramm und klappt mit „–“ ein. - const dock = this.shell.querySelector('.vo-dock')! - const dockToggle = dock.querySelector('[data-inspector=toggle]')! - const setDock = (open: boolean, redraw = true) => { - dock.classList.toggle('collapsed', !open) - dockToggle.textContent = open ? '–' : '+' - dockToggle.setAttribute('aria-expanded', String(open)) - const label = t(open ? 'web_visuobject_inspector_hide' : 'web_visuobject_inspector_show') - dockToggle.setAttribute('aria-label', label) - dockToggle.dataset.tip = label - store(inspectorKey, open ? '1' : '0') - if (redraw) this.drawLines() - } - dockToggle.addEventListener('click', () => setDock(dock.classList.contains('collapsed'))) - dock.querySelector('.vo-dock-bar')!.addEventListener('dblclick', () => setDock(dock.classList.contains('collapsed'))) - setDock(stored(inspectorKey) !== '0', false) - // Grenze zwischen Diagramm und Objektinspektor nach oben oder unten schieben (Doppelklick = Standard). - const split = this.shell.querySelector('.vo-hsplit')! - const pane = this.shell.querySelector('[data-pane=diagram]')! - const setHeight = (h: number | undefined) => { - if (h === undefined) { - this.inspector.style.removeProperty('height') - this.inspector.style.removeProperty('max-height') - store(inspectorHeightKey, '') - } else { - const max = Math.max(120, pane.clientHeight - 200) - const value = Math.round(Math.min(max, Math.max(60, h))) - this.inspector.style.height = `${value}px` - this.inspector.style.maxHeight = 'none' - store(inspectorHeightKey, String(value)) - } - this.drawLines() - } - const savedHeight = Number(stored(inspectorHeightKey)) - if (savedHeight > 0) { - this.inspector.style.height = `${savedHeight}px` - this.inspector.style.maxHeight = 'none' - } - split.addEventListener('pointerdown', (ev) => { - if (dock.classList.contains('collapsed') || ev.button !== 0) return - ev.preventDefault() - split.setPointerCapture(ev.pointerId) - const startY = ev.clientY - const startH = this.inspector.offsetHeight - split.classList.add('dragging') - const move = (m: PointerEvent) => setHeight(startH - (m.clientY - startY)) - const up = () => { - split.classList.remove('dragging') - split.removeEventListener('pointermove', move) - split.removeEventListener('pointerup', up) - split.removeEventListener('pointercancel', up) - } - split.addEventListener('pointermove', move) - split.addEventListener('pointerup', up) - split.addEventListener('pointercancel', up) - }) - split.addEventListener('dblclick', () => setHeight(undefined)) - split.addEventListener('keydown', (ev) => { - const d = ev.key === 'ArrowUp' ? 20 : ev.key === 'ArrowDown' ? -20 : 0 - if (!d) return - ev.preventDefault() - setHeight(this.inspector.offsetHeight + d) - }) - this.tree = this.shell.querySelector('.vo-tree')! - this.tabs = this.shell.querySelector('.vo-tabs')! - this.wsBox = this.shell.querySelector('.vo-ws')! - this.wsBox.addEventListener('click', (ev) => void this.onWorkspaceClick(ev)) - this.editor = new JavaEditor( - this.shell.querySelector('.vo-editor')!, - { - onChange: () => this.typed(), - onCaret: () => this.caretMoved(), - onUndo: (redo) => this.undo(redo), - words: () => ({ - condition: t('canvas_stub_condition'), - selector: t('web_live_ph_selector'), - code: t('web_live_ph_code'), - start: t('inspector_for_init'), - step: t('inspector_for_step'), - branch: t('element_verzweigung'), - branchElse: t('element_verzweigung'), - switchCase: t('element_fallauswahl'), - forLoop: t('element_forSchleife'), - whileLoop: t('element_whileSchleife'), - doLoop: t('element_doUntilSchleife'), - output: t('inspector_io_mode_output'), - input: t('inspector_io_mode_input'), - }), - breakpointLine: (line) => line, - onBreakpoints: () => this.saveBreakpoints(), - ownTypes: (pos) => this.ownTypesAt(pos), - }, - t('web_visuobject_code'), - ) - const on = (action: string, run: () => void) => this.shell.querySelector(`[data-action=${action}]`)!.addEventListener('click', run) - on('new', () => void this.newProject({ name: t('web_visuobject_project_name'), files: [], packages: [] })) - on('example', () => void this.newProject(exampleProject(), 'tiere/Tier.java')) - on('open', () => this.openFiles()) - on('save', () => void this.save()) - on('undo', () => this.undo(false)) - on('redo', () => this.undo(true)) - const newButton = this.shell.querySelector('[data-action=new-menu]')! - const newMenu = this.shell.querySelector('.vo-new-menu')! - const showNewMenu = (show: boolean) => { - newMenu.hidden = !show - newButton.setAttribute('aria-expanded', String(show)) - if (show) newMenu.querySelector('button')?.focus() - } - newButton.addEventListener('click', () => showNewMenu(newMenu.hidden)) - newMenu.addEventListener('click', (ev) => { - const kind = (ev.target as HTMLElement).closest('[data-new]')?.dataset.new as NewKind | undefined - if (!kind) return - showNewMenu(false) - void this.newFromWizard(kind) - }) - newMenu.addEventListener('keydown', (ev) => { - const items = [...newMenu.querySelectorAll('button')] - const i = items.indexOf(document.activeElement as HTMLElement) - if (ev.key === 'Escape') { - showNewMenu(false) - newButton.focus() - } else if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') { - ev.preventDefault() - items[(i + (ev.key === 'ArrowDown' ? 1 : items.length - 1)) % items.length]?.focus() - } - }) - document.addEventListener('pointerdown', (ev) => { - if (!newMenu.hidden && !(ev.target as HTMLElement).closest('.vo-new')) showNewMenu(false) - }) - on('layout', () => { - this.positions.clear() - this.savePositions() - this.render() - }) - const settings = this.shell.querySelector('[data-action=settings]')! - const menu = this.shell.querySelector('.settings-menu')! - settings.addEventListener('click', () => { - menu.hidden = !menu.hidden - settings.setAttribute('aria-expanded', String(!menu.hidden)) - }) - document.addEventListener('pointerdown', (ev) => { - if (!menu.hidden && !(ev.target as HTMLElement).closest('.settings')) menu.hidden = true - }) - for (const b of menu.querySelectorAll('[data-world]')) b.addEventListener('click', () => this.chooseWorld(b.dataset.world as WorldChoice)) - const scopes = menu.querySelector('[data-action=code-scopes]')! - scopes.checked = codeScopes() - scopes.addEventListener('change', () => setCodeScopes(scopes.checked)) - this.shell.querySelector('[data-action=run]')!.addEventListener('click', () => void this.runProgram()) - this.shell.querySelector('[data-action=stop]')!.addEventListener('click', () => (this.dbg ? this.stopDebug('web_visuobject_stopped') : void this.stopProgram())) - this.shell.querySelector('[data-action=debug]')!.addEventListener('click', () => void this.startDebug()) - for (const b of this.shell.querySelectorAll('[data-dbg]')) b.addEventListener('click', () => this.debugCommand(b.dataset.dbg as StepMode | 'continue' | 'pause')) - const consoleInput = this.shell.querySelector('.vo-console-input')! - consoleInput.addEventListener('submit', (ev) => { - ev.preventDefault() - const field = consoleInput.querySelector('input')! - this.debugInput(field.value) - field.value = '' - }) - window.addEventListener('keydown', (ev) => { - const command = ({ F5: 'into', F6: 'over', F7: 'out', F8: 'continue' } as const)[ev.key as 'F5'] - if (!command || !this.dbg) return - ev.preventDefault() - this.debugCommand(command) - }) - const consolePane = this.shell.querySelector('.vo-console')! - consolePane.querySelector('[data-console=clear]')!.addEventListener('click', () => consolePane.querySelector('.vo-console-lines')!.replaceChildren()) - consolePane.querySelector('[data-console=hide]')!.addEventListener('click', () => (consolePane.hidden = true)) - this.canvas.addEventListener('pointerdown', (ev) => this.onPointerDown(ev)) - this.canvas.addEventListener('click', (ev) => this.onClick(ev)) - this.canvas.addEventListener('contextmenu', (ev) => { - const el = (ev.target as Element).closest('[data-end]') - if (!el) return - ev.preventDefault() - if (this.relSelection !== el.dataset.rel) this.selectRelation(el.dataset.rel!) - this.openEnd(el.dataset.rel!, el.dataset.end as 'a' | 'b', ev) - }) - this.tree.addEventListener('click', (ev) => this.onTreeClick(ev)) - this.tabs.addEventListener('click', (ev) => this.onTabClick(ev)) - for (const b of this.shell.querySelectorAll('.vo-tool')) b.addEventListener('pointerdown', (ev) => this.onToolDown(ev, b.dataset.tool as Tool)) - this.tree.addEventListener('keydown', (ev) => this.onTreeKey(ev)) - } - - // MARK: - Ausführen (echtes Java über CheerpJ) - - /** Klasse mit `public static void main`: bevorzugt in der offenen Datei. Voller Name mit Package. */ - private mainClass(): string | undefined { - const withMain = this.classes.filter((c) => c.members.some((m) => m.kind === 'method' && m.name === 'main' && m.isStatic)) - const cls = withMain.find((c) => c.path === this.active) ?? withMain[0] - return cls && (cls.pkg ? `${cls.pkg}.${cls.name}` : cls.name) - } - - private async runProgram() { - const pane = this.shell.querySelector('.vo-console')! - const lines = pane.querySelector('.vo-console-lines')! - const status = pane.querySelector('.vo-console-status')! - const swing = pane.querySelector('.vo-swing')! - const run = this.shell.querySelector('[data-action=run]')! - const stop = this.shell.querySelector('[data-action=stop]')! - pane.hidden = false - const say = (key: string, error = false, ...args: string[]) => { - status.textContent = t(key, ...args) - status.classList.toggle('error', error) - } - const main = this.mainClass() - if (!main) return say('web_visuobject_no_main', true) - // Erst speichern, was im Editor steht, dann wie in Eclipse alle Dateien übersetzen. - if (this.workspace) await this.writeCurrent() - await closeWindows().catch(() => undefined) - lines.replaceChildren() - // CheerpJ schreibt System.out und System.err in das Element mit dieser id. - for (const el of document.querySelectorAll('#console')) el.removeAttribute('id') - lines.id = 'console' - swing.hidden = true - run.disabled = true - stop.disabled = false - try { - say('web_live_swing_loading') - await prepareJava(swing.querySelector('.livecode-swing-display')!) - say('web_live_swing_compiling') - const files = this.project.files.map((f) => ({ pkg: barePackage(packageOf(f.path)), name: fileNameOf(f.path).replace(/\.java$/i, ''), text: f.text })) - const { result } = await compileAndRunProject(files, main, () => { - swing.hidden = false - say('web_visuobject_running_fmt', false, main) - }) - say({ 'compile-error': 'web_live_swing_compile_failed', windows: 'web_live_swing_running', finished: 'web_live_run_finished' }[result], result === 'compile-error') - swing.hidden = result !== 'windows' - stop.disabled = result !== 'windows' - } catch { - say('web_live_swing_failed', true) - stop.disabled = true - } - run.disabled = false - } - - /** Stop: Fenster des Programms schließen (eine Endlosschleife in main hält CheerpJ nicht an). */ - private async stopProgram() { - await closeWindows().catch(() => undefined) - const pane = this.shell.querySelector('.vo-console')! - pane.querySelector('.vo-swing')!.hidden = true - const status = pane.querySelector('.vo-console-status')! - status.textContent = t('web_visuobject_stopped') - status.classList.remove('error') - this.shell.querySelector('[data-action=stop]')!.disabled = true - } - - // MARK: - Debugger (eigener Interpreter, Schritt für Schritt) - - private saveBreakpoints() { - if (!this.active) return - const lines = this.editor.breakpoints - if (lines.size) this.breakpoints.set(this.active, lines) - else this.breakpoints.delete(this.active) - const known = new Set(this.project.files.map((f) => f.path)) - store(breakpointsKey, JSON.stringify(Object.fromEntries([...this.breakpoints].filter(([p]) => known.has(p)).map(([p, l]) => [p, [...l]])))) - } - - private consolePane() { - const pane = this.shell.querySelector('.vo-console')! - return { - pane, - lines: pane.querySelector('.vo-console-lines')!, - status: pane.querySelector('.vo-console-status')!, - input: pane.querySelector('.vo-console-input')!, - } - } - - private debugStatus(text: string, error = false) { - const { status } = this.consolePane() - status.textContent = text - status.classList.toggle('error', error) - } - - private consoleWrite(text: string, kind: 'out' | 'err' | 'in' = 'out') { - const { lines } = this.consolePane() - const last = lines.lastElementChild as HTMLElement | null - if (last?.dataset.kind === kind) last.textContent += text - else { - const span = document.createElement('span') - span.dataset.kind = kind - span.className = `vo-out-${kind}` - span.textContent = text - lines.append(span) - } - lines.scrollTop = lines.scrollHeight - } - - private async startDebug() { - if (this.dbg) this.stopDebug() - const { pane, lines, input } = this.consolePane() - pane.hidden = false - pane.querySelector('.vo-swing')!.hidden = true - await closeWindows().catch(() => undefined) - if (this.workspace) await this.writeCurrent() - lines.replaceChildren() - lines.removeAttribute('id') - input.hidden = true - const main = this.mainClass() - if (!main) return this.debugStatus(t('web_visuobject_no_main'), true) - let vm: MiniJava - try { - vm = new MiniJava( - this.project.files.map((f) => ({ path: f.path, source: f.text })), - { out: (text, err) => this.consoleWrite(text, err ? 'err' : 'out') }, - ) - vm.start(main.split('.').pop()) - } catch (err) { - if (err instanceof MiniJavaError) return this.debugError(err) - throw err - } - this.dbg = { vm, state: 'running', frames: [], frame: 0, places: new Map(), values: new Map(), vars: new Map() } - this.shell.querySelector('.vo-dbg-controls')!.hidden = false - this.shell.querySelector('[data-action=stop]')!.disabled = false - this.shell.querySelector('[data-action=run]')!.disabled = true - this.sequence = undefined - if (this.diagramMode === 'class') this.setDiagramMode('object') - vm.go(this.allBreakpoints().size ? 'run' : 'into') - this.debugRun() - } - - private allBreakpoints(): Set { - const out = new Set() - for (const [path, lines] of this.breakpoints) for (const l of lines) out.add(`${path}:${l}`) - return out - } - - private debugCommand(command: StepMode | 'continue' | 'pause') { - const dbg = this.dbg - if (!dbg) return - if (command === 'pause') { - if (dbg.state !== 'running') return - dbg.vm.pause() - clearTimeout(dbg.timer) - return this.debugRun() - } - if (dbg.state !== 'paused') return - dbg.vm.go(command === 'continue' ? 'run' : command) - this.debugRun() - } - - /** Programm in kleinen Portionen laufen lassen, damit die Seite bedienbar bleibt (Pause, Stop). */ - private debugRun() { - const dbg = this.dbg - if (!dbg) return - dbg.state = 'running' - this.showDebugButtons() - this.debugStatus(t('web_visuobject_dbg_running')) - const bps = this.allBreakpoints() - const start = performance.now() - for (;;) { - const s = dbg.vm.run(bps, 2000) - if (s.k === 'running') { - if (performance.now() - start < 15) continue - dbg.timer = window.setTimeout(() => this.debugRun(), 0) - return - } - if (s.k === 'sleep') { - dbg.timer = window.setTimeout(() => this.debugRun(), Math.min(s.ms, 10000)) - return - } - if (s.k === 'input') { - dbg.state = 'input' - this.showDebugButtons() - this.showDebugState() - this.debugStatus(t('web_visuobject_dbg_waiting')) - const { input } = this.consolePane() - input.hidden = false - input.querySelector('input')!.focus() - return - } - if (s.k === 'paused') { - dbg.state = 'paused' - dbg.frame = 0 - this.showDebugButtons() - this.showDebugState() - this.debugStatus(t('web_visuobject_dbg_paused_fmt', s.pos.file.split('/').pop() ?? '', String(s.pos.line + 1))) - return - } - if (s.k === 'done') return this.stopDebug('web_live_run_finished') - if (s.k === 'exception') { - this.consoleWrite(`${s.text} -`, 'err') - if (s.pos) this.showLine(s.pos.file, s.pos.line) - return this.stopDebug('web_visuobject_dbg_exception', true) - } - return this.debugError(s.error) - } - } - - private debugInput(line: string) { - const dbg = this.dbg - if (!dbg || dbg.state !== 'input') return - this.consoleWrite(`${line} -`, 'in') - this.consolePane().input.hidden = true - dbg.vm.input(line) - this.debugRun() - } - - private debugError(err: MiniJavaError) { - const line = err.pos ? String(err.pos.line + 1) : '' - const file = err.pos?.file.split('/').pop() ?? '' - const what = err.detail.replace(/^expected:/, '') - const key = { syntax: 'web_visuobject_dbg_err_syntax', symbol: 'web_visuobject_dbg_err_symbol', unsupported: 'web_visuobject_dbg_err_unsupported', nomain: 'web_visuobject_no_main', type: 'web_visuobject_dbg_err_type', import: 'web_visuobject_dbg_err_import', typename: 'web_visuobject_dbg_err_typename' }[err.code] - if (err.pos) this.showLine(err.pos.file, err.pos.line) - this.stopDebug() - this.debugStatus(t(key, file, line, what), true) - } - - private showDebugButtons() { - const dbg = this.dbg - const paused = dbg?.state === 'paused' - for (const b of this.shell.querySelectorAll('[data-dbg]')) b.disabled = !dbg || (b.dataset.dbg === 'pause' ? dbg.state !== 'running' : !paused) - } - - /** Zeile im Editor zeigen (Datei wechseln, gelb markieren). */ - private showLine(path: string, line: number | undefined) { - if (this.project.files.some((f) => f.path === path) && path !== this.active) this.openFile(path) - this.editor.setMarks({ placeholders: [], problems: [], current: line }, true) - } - - /** Halt: gelbe Zeile, Objektdiagramm mit den Objekten im Speicher, Variablen und Aufrufstapel. */ - private showDebugState() { - const dbg = this.dbg - if (!dbg) return - dbg.frames = dbg.vm.stack() - const heap = dbg.vm.heap() - const frame = dbg.frames[dbg.frame] - if (frame) this.showLine(frame.pos.file, frame.pos.line) - const first = dbg.values.size === 0 && dbg.vars.size === 0 - const changed = new Set() - const values = new Map() - for (const o of heap) - for (const f of o.fields) { - const key = `dbg${o.id}.${f.name}` - values.set(key, f.value) - if (!first && dbg.values.get(key) !== f.value) changed.add(key) - } - dbg.values = values - const changedVars = new Set() - const vars = new Map() - dbg.frames.forEach((f, i) => { - for (const v of f.vars) { - const key = `${dbg.frames.length - i}|${f.title}|${v.name}` - vars.set(key, v.value) - if (!first && dbg.vars.get(key) !== v.value) changedVars.add(`${f.title}|${v.name}`) - } - }) - dbg.vars = vars - const live: LiveState = { - objects: this.liveObjects(heap), - changed, - frames: dbg.frames, - frame: dbg.frame, - changedVars, - onFrame: (i) => { - dbg.frame = i - live.frame = i - const f = dbg.frames[i] - if (f) this.showLine(f.pos.file, f.pos.line) - this.objectDiagram.renderInspector() - }, - move: (id, p) => dbg.places.set(id, p), - } - this.objectDiagram.live = live - this.sequence = dbg.vm.sequence() - if (this.diagramMode === 'object') this.objectDiagram.render() - if (this.diagramMode === 'sequence') this.renderSequence() - } - - /** Sequenzdiagramm aus dem letzten Debugger-Lauf; beim Anhalten ist der letzte Pfeil gelb. */ - private renderSequence() { - const box = this.shell.querySelector('.vo-seqcanvas')! - const svg = box.querySelector('.vo-seq')! - const empty = box.querySelector('.vo-empty')! - const data = this.sequence - empty.hidden = !!data && data.events.length > 0 - empty.textContent = t(data ? 'web_visuobject_seq_no_calls' : 'web_visuobject_seq_empty') - if (!data || !data.events.length) { - svg.innerHTML = '' - svg.setAttribute('width', '0') - } else { - const live = this.dbg?.state === 'paused' || this.dbg?.state === 'input' - const { svg: inner, width, height } = sequenceSVG(data, live ? data.events.length - 1 : undefined) - svg.innerHTML = inner + (data.truncated ? `${e(t('web_visuobject_seq_truncated'))}` : '') - svg.setAttribute('width', String(width)) - svg.setAttribute('height', String(height)) - if (live) svg.querySelector('.vo-seq-msg.current')?.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - } - this.renderSequenceInspector() - } - - private renderSequenceInspector() { - if (this.objectDiagram.live) return this.objectDiagram.renderInspector() - this.inspector.innerHTML = `

    ${e(t('web_visuobject_seq_hint'))}

    ` - } - - /** Objekte aus dem Speicher als Objektdiagramm; Lage wie selbst gezeichnet (gleicher Name) oder frei daneben. */ - private liveObjects(heap: HeapObject[]): UmlObject[] { - const dbg = this.dbg! - const drawn = this.project.objects ?? [] - const rows = (o: HeapObject) => 40 + o.fields.length * 20 - const used: { x: number; y: number; w: number; h: number }[] = [] - const out: UmlObject[] = [] - const pending: HeapObject[] = [] - for (const o of heap) { - const id = `dbg${o.id}` - const p = dbg.places.get(id) ?? drawn.find((d) => d.name === o.name && d.cls === o.cls) - if (!p) { - pending.push(o) - continue - } - dbg.places.set(id, { x: p.x, y: p.y }) - used.push({ x: p.x, y: p.y, w: 260, h: rows(o) }) - } - for (const o of pending) { - const h = rows(o) - let spot = { x: 24, y: 24 } - search: for (let y = 24; y < 6000; y += 30) - for (let x = 24; x <= 24 + 3 * 320; x += 320) - if (used.every((r) => x + 260 + 30 <= r.x || r.x + r.w + 30 <= x || y + h + 30 <= r.y || r.y + r.h + 30 <= y)) { - spot = { x, y } - break search - } - dbg.places.set(`dbg${o.id}`, spot) - used.push({ ...spot, w: 260, h }) - } - for (const o of heap) { - const id = `dbg${o.id}` - out.push({ id, name: o.name, cls: o.cls, ...dbg.places.get(id)!, values: Object.fromEntries(o.fields.map((f) => [f.name, f.value])) }) - } - return out - } - - private stopDebug(statusKey?: string, error = false) { - const dbg = this.dbg - if (dbg) clearTimeout(dbg.timer) - this.dbg = undefined - if (dbg) this.sequence = dbg.vm.sequence() - this.objectDiagram.live = undefined - this.consolePane().input.hidden = true - this.shell.querySelector('.vo-dbg-controls')!.hidden = true - this.shell.querySelector('[data-action=stop]')!.disabled = true - this.shell.querySelector('[data-action=run]')!.disabled = false - if (!error) this.editor.setMarks({ placeholders: [], problems: [] }, false) - if (statusKey) this.debugStatus(t(statusKey), error) - if (this.diagramMode === 'object') this.objectDiagram.render() - if (this.diagramMode === 'sequence') this.renderSequence() - } - - private chooseWorld(world: WorldChoice) { - if (world === 'object') return - if (world === 'flow') { - setVisuFlowWorld() - location.href = `${location.pathname}?visuflow${location.hash}` - return - } - setVisuBotMode(world) - location.href = `${location.pathname}?${world === 'on' ? 'visubot' : 'visustruct'}${location.hash}` - } - - // MARK: - Projekt und Code - - private get activeFile() { - return this.project.files.find((f) => f.path === this.active) - } - - /** Geöffnete Datei in den Editor. */ - private showActive() { - const file = this.activeFile - this.editor.setText(file?.text ?? '') - this.editor.setBreakpoints(file ? (this.breakpoints.get(file.path) ?? []) : []) - this.editor.setReadOnly(!file) - if (file && !this.openTabs.includes(file.path)) this.openTabs.push(file.path) - this.renderTabs() - store(activeKey, this.active ?? '') - } - - /** Reiter wie in WebGoatCode: eine pro offener Datei, × schließt sie. */ - private renderTabs() { - this.openTabs = this.openTabs.filter((p) => this.project.files.some((f) => f.path === p)) - store(tabsKey, JSON.stringify(this.openTabs)) - if (this.openTabs.length === 0) { - this.tabs.innerHTML = `${e(t('web_visuobject_no_file'))}` - return - } - this.tabs.innerHTML = this.openTabs - .map((path) => { - const on = path === this.active - // Wie in VS Code: ● statt ×, solange die Datei nicht gespeichert ist. - const dirty = this.isDirty(path) - return `
    - - -
    ` - }) - .join('') - } - - private onTabClick(ev: MouseEvent) { - const target = ev.target as HTMLElement - const path = target.closest('[data-tab]')?.dataset.tab - if (!path) return - if (target.closest('[data-close]')) return this.closeTab(path) - this.treeSelection = `file:${path}` - this.openFile(path) - } - - private closeTab(path: string) { - const i = this.openTabs.indexOf(path) - if (i < 0) return - this.openTabs.splice(i, 1) - if (path === this.active) { - this.active = this.openTabs[Math.min(i, this.openTabs.length - 1)] - this.lastTyping = 0 - this.showActive() - this.refresh() - } else this.renderTabs() - } - - private snapshot(): string { - return JSON.stringify({ project: this.project, active: this.active }) - } - - private remember() { - this.history.push(this.snapshot()) - if (this.history.length > maxUndoSteps) this.history.shift() - this.future = [] - } - - /** Projekt von außen ändern (Diagramm, Explorer, Neu, Beispiel, Öffnen): mit Rückgängig. */ - private setProject(next: JavaProject, active = this.active) { - this.remember() - this.lastTyping = 0 - this.project = next - this.active = next.files.some((f) => f.path === active) ? active : next.files[0]?.path - this.showActive() - this.refresh() - } - - private openFile(path: string) { - if (path === this.active) return - this.active = path - this.lastTyping = 0 - this.showActive() - const pkg = packageOf(path) - this.currentPackage = pkg - this.open.set(`pkg:${pkg}`, true) - this.refresh() - } - - private typed() { - const file = this.activeFile - if (!file) return - // Schnelles Tippen zählt als ein Schritt für Rückgängig. - const now = Date.now() - if (now - this.lastTyping > 1500) this.remember() - this.lastTyping = now - this.project = updateFile(this.project, file.path, this.editor.text) - this.saveBreakpoints() - if (this.dbg) this.stopDebug('web_visuobject_dbg_code_changed') - this.refresh() - // Klasse im Code umbenannt: kurz nach dem Tippen heißen Reiter und Datei mit (wie in Eclipse beim Speichern). - clearTimeout(this.renameTimer) - this.renameTimer = window.setTimeout(() => this.syncFileName(), 700) - } - - private renameTimer?: number - - /** Heißt die Datei wie keine ihrer Klassen mehr, bekommt sie den Namen der (ersten öffentlichen) Klasse. */ - private syncFileName() { - const file = this.activeFile - if (!file || this.editing) return - const old = fileNameOf(file.path).replace(/\.java$/i, '') - const inFile = this.classes.filter((c) => c.path === file.path) - if (inFile.length === 0 || inFile.some((c) => c.name === old)) return - const name = (/\bpublic\s+(?:abstract\s+|final\s+)*(?:class|interface|enum)\s+(\w+)/.exec(file.text)?.[1] ?? inFile[0].name) - if (!identifier.test(name) || inFile.filter((c) => c.name === name).length !== 1) return - const to = pathFor(packageOf(file.path), `${name}.java`) - if (this.project.files.some((f) => f.path === to)) return - let next = moveFile(this.project, file.path, to) - // Platz im Diagramm und UML-Zusätze gehören jetzt zum neuen Namen. - const p = this.positions.get(old) - if (p && !this.positions.has(name)) this.positions.set(name, p) - for (const key of Object.keys(next.uml ?? {})) if (key.startsWith(`${old}.`)) next = { ...next, uml: renameInfo(next.uml!, key, name + key.slice(old.length)) } - this.openTabs = this.openTabs.map((t) => (t === file.path ? to : t)) - if (this.treeSelection === `file:${file.path}`) this.treeSelection = `file:${to}` - if (this.isOpen(`file:${file.path}`)) this.open.set(`file:${to}`, true) - this.project = next - this.active = to - store(activeKey, to) - this.savePositions() - this.refresh() - } - - private undo(redo: boolean) { - const from = redo ? this.future : this.history - const to = redo ? this.history : this.future - const snap = from.pop() - if (snap === undefined) return - to.push(this.snapshot()) - const data = JSON.parse(snap) as { project: JavaProject; active?: string } - this.project = data.project - this.active = data.active - this.lastTyping = 0 - this.showActive() - this.refresh() - } - - private refresh() { - store(projectKey, projectToJSON(this.project)) - this.renderTabs() - this.classes = projectClasses(this.project) - if (this.selection && !this.findClass(this.selection.cls)) this.selection = undefined - if (this.relSelection && !this.findRelation(this.relSelection) && !this.findDependency(this.relSelection)) this.relSelection = undefined - if (!this.editing) { - this.render() - this.renderTree() - } - } - - /** Eigene Klassen für Vorschläge nach dem Punkt; die offene Datei frisch aus dem Editor gelesen. */ - private ownTypesAt(pos: number): OwnTypes | undefined { - const file = this.activeFile - if (!file) return undefined - const text = this.editor.view.state.doc.toString() - const pkg = barePackage(packageOf(file.path)) - const mine = parseClasses(text).map((c) => ({ ...c, pkg })) - const names = new Set(mine.map((c) => c.name)) - const here = mine.find((c) => c.headerFrom <= pos && pos <= c.bodyEnd) - const lang = currentLanguage() === 'de' ? 'de' : currentLanguage() === 'pt-PT' ? 'pt' : 'en' - return ownTypes([...mine, ...this.classes.filter((c) => c.path !== file.path && !names.has(c.name))], { cls: here?.name, pkg }, lang) - } - - private findClass(name: string): ProjectClass | undefined { - return this.classes.find((c) => c.name === name) - } - - /** Cursor im Code: passende Klasse bzw. passendes Mitglied im Diagramm markieren. */ - private caretMoved() { - const pos = this.editor.caret - const cls = this.classes.find((c) => c.path === this.active && pos >= c.from && pos <= c.to) - const member = cls?.members.findIndex((m) => pos >= m.from && pos <= m.to) - const next = cls ? { cls: cls.name, member: member !== undefined && member >= 0 ? member : undefined } : undefined - if (next?.cls === this.selection?.cls && next?.member === this.selection?.member) return - this.selection = next - if (!this.editing) { - this.render() - this.renderTree() - } - } - - /** Klasse bzw. Mitglied im Code zeigen: Datei öffnen und markieren. */ - private showInCode(cls: ProjectClass, member?: UmlMember) { - if (cls.path !== this.active) { - this.active = cls.path - this.lastTyping = 0 - this.showActive() - } - const view = this.editor.view - const [from, to] = member ? [member.headerFrom, member.headerTo] : [cls.nameFrom, cls.nameTo] - const end = Math.min(to, view.state.doc.length) - view.dispatch({ selection: { anchor: Math.min(from, end), head: end }, scrollIntoView: true }) - } - - /** Meldung unter dem Diagramm: rot bei Fehlern, grau bei Hinweisen. */ - private message(text: string, hint = false) { - this.status.textContent = text - this.status.classList.toggle('hint', hint) - } - - // MARK: - Datei - - private openFiles() { - const input = document.createElement('input') - input.type = 'file' - input.multiple = true - input.accept = '.zip,.java,.visuobject,application/zip,text/plain' - input.addEventListener('change', () => void this.readFiles([...(input.files ?? [])])) - input.click() - } - - private async readFiles(files: File[]) { - if (files.length === 0) return - try { - const zip = files.find((f) => /\.zip$/i.test(f.name)) - if (zip) { - const { projectFromZip } = await import('./projectZip') - const project = projectFromZip(new Uint8Array(await zip.arrayBuffer()), zip.name.replace(/\.zip$/i, '')) - await this.newProject(project) - return - } - const saved = files.find((f) => /\.visuobject$/i.test(f.name)) - if (saved) { - const project = projectFromJSON(await saved.text()) - if (!project) throw new Error('format') - await this.newProject(project) - return - } - // Einzelne .java-Dateien kommen ins aktuelle Projekt (Package aus der `package`-Zeile). - const added = projectFromFiles(this.project.name, await Promise.all(files.map(async (f) => ({ name: f.name, text: await f.text() })))) - let next = this.project - for (const f of added.files) next = addFile(next, f.path, f.text) - this.setProject(next, added.files[0]?.path) - } catch { - this.message(t('web_visuobject_open_failed')) - } - } - - /** In den Workspace, ohne ihn als ZIP. */ - private async save() { - if (this.workspace) await this.writeCurrent() - else await this.downloadZip() - } - - /** Als ZIP wie in Eclipse oder BlueJ: `Projekt/src/paket/Klasse.java`. */ - private async downloadZip() { - const { projectToZip } = await import('./projectZip') - const data = projectToZip(this.project) - download(`${this.project.name || 'Projekt'}.zip`, data.slice().buffer, 'application/zip') - } - - // MARK: - Workspace - - /** Ungespeichert: das ganze Projekt oder eine Datei (nur mit Workspace). */ - private isDirty(path?: string): boolean { - if (!this.workspace || this.saved === undefined) return false - if (path === undefined) return this.saved !== projectToJSON(this.project) - return this.savedFiles.get(path) !== this.project.files.find((f) => f.path === path)?.text - } - - private markSaved() { - this.saved = projectToJSON(this.project) - this.savedFiles = new Map(this.project.files.map((f) => [f.path, f.text])) - } - - /** Beim Start: gemerkten Workspace wieder öffnen; braucht der Browser einen Klick, wartet ein Knopf. */ - private async restoreWorkspace() { - if (!canPickFolder()) return - const folder = await storedFolder(workspaceSlot) - if (folder?.ready) { - const ws = await reopenFolder(folder.handle) - if (ws) return this.useWorkspace(ws.root, true) - } - this.pendingFolder = folder?.handle - this.renderWorkspace() - } - - /** `restoring`: nach dem Neuladen; ungespeicherte Änderungen aus dem Browser bleiben dann erhalten. */ - private async useWorkspace(root: WsDir, restoring = false) { - this.workspace = root - this.pendingFolder = undefined - store(noWorkspaceKey, '0') - try { - this.projects = await listProjects(root) - const last = stored(wsProjectKey) - const local = this.project - if (restoring && last && this.projects.includes(last)) { - const view = { active: this.active, tabs: [...this.openTabs], positions: new Map(this.positions) } - await this.loadProject(last) - // Nach dem Neuladen wie vorher: dieselben Reiter und Kästen. - this.positions = view.positions - this.savePositions() - // Was seit dem letzten Speichern getippt wurde, steht noch im Browser (mit Workspace immer dessen Projekt). - if (projectToJSON(local) !== this.saved) { - this.project = local - } - this.openTabs = view.tabs.filter((p) => this.project.files.some((f) => f.path === p)) - this.active = this.project.files.some((f) => f.path === view.active) ? view.active : this.project.files[0]?.path - this.showActive() - this.refresh() - } else if (!this.projects.some((p) => p.toLowerCase() === safeFolderName(local.name).toLowerCase()) && local.files.length) { - // Das Projekt aus dem Browser kommt mit in den neuen Workspace. - this.folder = safeFolderName(local.name) - await this.writeCurrent(false) - } else if (this.projects.length) await this.loadProject(this.projects.includes(last ?? '') ? last! : this.projects[0]) - else { - this.folder = safeFolderName(local.name) - await this.writeCurrent(false) - } - } catch (err) { - this.message(t('web_visuobject_ws_failed', String((err as Error)?.message ?? err))) - } - this.renderWorkspace() - this.refresh() - } - - private async loadProject(name: string) { - const { project, positions } = await readProject(this.workspace!, name) - this.folder = name - this.project = project - this.markSaved() - this.message('') - this.history = [] - this.future = [] - this.lastTyping = 0 - this.selection = undefined - this.relSelection = undefined - this.treeSelection = undefined - this.currentPackage = undefined - this.positions = new Map(Object.entries(positions)) - this.savePositions() - this.openTabs = [] - this.active = project.files[0]?.path - store(wsProjectKey, name) - this.showActive() - this.refresh() - } - - /** Geöffnetes Projekt in seinen Ordner schreiben; nach dem Umbenennen zieht es in den neuen Ordner. */ - private async writeCurrent(report = true): Promise { - const root = this.workspace - if (!root) return false - const target = safeFolderName(this.project.name) - const old = this.folder - const names = new Set(this.classes.map((c) => c.name)) - const positions: Positions = Object.fromEntries([...this.positions].filter(([n]) => names.has(n))) - if (target !== old && this.projects.some((p) => p !== old && p.toLowerCase() === target.toLowerCase())) { - this.message(t('web_visuobject_ws_taken', this.project.name)) - return false - } - try { - await writeProject(root, target, this.project, positions) - if (old && old !== target && this.projects.includes(old)) await root.remove(old) - this.folder = target - this.projects = await listProjects(root) - store(wsProjectKey, target) - this.markSaved() - if (report) this.message(t('web_visuobject_ws_saved', root.name, target), true) - this.refresh() - return true - } catch (err) { - this.message(t('web_visuobject_ws_failed', String((err as Error)?.message ?? err))) - return false - } - } - - /** Anderes Projekt öffnen; das geöffnete wird vorher gespeichert (wie „Save all“ in Eclipse). */ - private async switchProject(name: string) { - if (!this.workspace || name === this.folder) return - if (this.isDirty() && !(await this.writeCurrent(false))) return - try { - await this.loadProject(name) - } catch (err) { - this.message(t('web_visuobject_ws_failed', String((err as Error)?.message ?? err))) - } - } - - /** Neu, Beispiel, ZIP öffnen: im Workspace ein eigenes Projekt, sonst ersetzt es das alte (mit Rückgängig). */ - private async newProject(project: JavaProject, active?: string, named = false) { - if (!this.workspace) { - this.setProject(project, active ?? project.files[0]?.path) - if (project.files.length === 0 && !named) this.editProjectName() - return - } - if (this.isDirty() && !(await this.writeCurrent(false))) return - const name = freeProjectName(safeFolderName(project.name), this.projects) - this.project = { ...project, name } - this.folder = name - this.history = [] - this.future = [] - this.openTabs = [] - this.positions.clear() - this.savePositions() - this.active = project.files.some((f) => f.path === active) ? active : project.files[0]?.path - this.showActive() - this.refresh() - if (!(await this.writeCurrent(false))) return - this.message(t('web_visuobject_ws_created', name), true) - if (project.files.length === 0 && !named) this.editProjectName() - } - - // MARK: - Neu (wie in Eclipse) - - /** Source Folder und Package, in die etwas Neues kommt: das markierte, sonst das der offenen Datei. */ - private get currentPlace(): { root: string; pkg: string } { - const key = this.currentPackage ?? (this.active ? packageOf(this.active) : (packagesOf(this.project)[0] ?? '')) - return { root: rootOf(key), pkg: barePackage(key) } - } - - private async newFromWizard(kind: NewKind) { - const here = this.currentPlace - const freeName = (base: string, taken: (n: string) => boolean) => { - let name = base - for (let n = 2; taken(name); n++) name = base + n - return name - } - const result = await newWizard(kind, { - roots: sourceFoldersOf(this.project), - packagesIn: (root) => packagesOf(this.project, root).map(barePackage), - classes: this.classes, - root: here.root, - pkg: here.pkg, - suggest: (k) => - k === 'project' - ? freeProjectName(t('web_visuobject_project_name'), this.workspace ? [...this.projects, this.folder ?? ''] : []) - : k === 'folder' - ? freeName('test', (n) => sourceFoldersOf(this.project).includes(n)) - : k === 'package' - ? freeName(t('web_visuobject_new_package_name'), (n) => packagesOf(this.project).includes(packageKey(here.root, n))) - : this.uniqueClassName(t(k === 'class' ? 'web_visuobject_new_class_name' : 'web_visuobject_new_interface_name')), - check: (k, name, root, pkg) => { - if (!name) return t('web_visuobject_new_name_missing') - if (k === 'project') return this.workspace && this.projects.some((p) => p !== this.folder && p.toLowerCase() === safeFolderName(name).toLowerCase()) ? t('web_visuobject_ws_taken', name) : undefined - if (k === 'folder') return !isFolderName(name) || sourceFoldersOf(this.project).includes(name) ? t('web_visuobject_bad_folder', name) : undefined - if (k === 'package') return !isPackageName(name) || packagesOf(this.project).includes(packageKey(root, name)) ? t('web_visuobject_bad_package', name) : undefined - if (!identifier.test(name)) return t('web_visuobject_bad_name', name) - if (this.findClass(name) || this.project.files.some((f) => f.path === pathFor(packageKey(root, pkg), `${name}.java`))) return t('web_visuobject_new_exists', name) - if (name.charAt(0) !== name.charAt(0).toUpperCase()) return t('web_visuobject_new_upper') - return undefined - }, - }) - if (!result) return - const { name, root, pkg } = result - if (kind === 'project') return this.newProject({ name, files: [], packages: [] }, undefined, true) - this.message('') - if (kind === 'folder') { - this.currentPackage = packageKey(name, '') - this.treeSelection = `root:${name}` - this.open.set(`root:${name}`, true) - return this.setProject(addSourceFolder(this.project, name)) - } - if (kind === 'package') { - const key = packageKey(root, name) - this.currentPackage = key - this.treeSelection = `pkg:${key}` - this.open.set(`root:${root}`, true) - return this.setProject(addPackage(this.project, key)) - } - const key = packageKey(root, pkg) - const path = pathFor(key, `${name}.java`) - this.currentPackage = key - this.selection = { cls: name } - this.open.set(`root:${root}`, true) - this.open.set(`pkg:${key}`, true) - this.setProject(addFile(this.project, path, this.sectioned(classSource(result.options!), name)), path) - const cls = this.findClass(name) - if (cls) this.showInCode(cls) - this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(name)}"]`)?.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - } - - /** Wie bei „+ Package“: Der Name des neuen Projekts ist gleich zum Überschreiben markiert. */ - private editProjectName() { - this.treeSelection = 'project' - this.renderTree() - this.startEditing(this.tree.querySelector('[data-node="project"] > .vo-row .vo-label')) - } - - private renderWorkspace() { - const box = this.wsBox - box.hidden = !canPickFolder() - const save = this.shell.querySelector('[data-action=save]')! - save.dataset.tip = t(this.workspace ? 'web_visuobject_save_ws_tip' : 'web_visuobject_save_tip') - if (box.hidden) return - const btn = (action: string, label: string, cls = 'text-button', tip?: string) => - `` - if (this.workspace) { - box.className = 'vo-ws' - box.innerHTML = `${e(this.workspace.name)} - ${btn('zip', t('web_visuobject_ws_zip'), 'text-button', t('web_visuobject_ws_zip_tip'))} - ${btn('switch', t('web_visuobject_ws_switch'), 'text-button')}` - } else if (this.pendingFolder) { - box.className = 'vo-ws' - box.innerHTML = `${btn('reopen', t('web_visuobject_ws_reopen', this.pendingFolder.name), 'text-button primary')}${btn('switch', t('web_visuobject_ws_switch'), 'text-button')}` - } else if (stored(noWorkspaceKey) === '1') { - box.className = 'vo-ws' - box.innerHTML = btn('open', t('web_visuobject_ws_choose'), 'text-button', t('web_visuobject_ws_hint')) - } else { - box.className = 'vo-ws ask' - box.innerHTML = `

    ${e(t('web_visuobject_ws_question'))}

    -

    ${e(t('web_visuobject_ws_hint'))}

    - ${btn('create', t('web_visuobject_ws_create', workspaceFolderName), 'text-button primary')} - ${btn('open', t('web_visuobject_ws_open'))} - ${btn('skip', t('web_visuobject_ws_skip'))}` - } - } - - private async onWorkspaceClick(ev: MouseEvent) { - const action = (ev.target as HTMLElement).closest('[data-ws]')?.dataset.ws - if (!action) return - if (action === 'zip') return this.downloadZip() - if (action === 'skip') { - store(noWorkspaceKey, '1') - return this.renderWorkspace() - } - try { - if (action === 'reopen' && this.pendingFolder) { - const ws = await reopenFolder(this.pendingFolder) - if (ws) await this.useWorkspace(ws.root, true) - return - } - if (this.isDirty() && !(await this.writeCurrent(false))) return - const ws = await pickFolder(action === 'create' ? workspaceFolderName : undefined, workspaceSlot) - store(wsProjectKey, '') - await this.useWorkspace(ws.root) - } catch (err) { - // Abbrechen im Ordner-Dialog ist kein Fehler. - if ((err as DOMException)?.name !== 'AbortError') this.message(t('web_visuobject_ws_failed', String((err as Error)?.message ?? err))) - } - } - - // MARK: - Diagramm → Code - - /** Datei der Klasse ändern; sie wird dabei geöffnet. */ - private changeFile(path: string, run: (source: string) => string, select?: Selection) { - const file = this.project.files.find((f) => f.path === path) - if (!file) return - if (select) this.selection = select - this.message('') - this.setProject(updateFile(this.project, path, run(file.text)), path) - } - - private uniqueClassName(base: string): string { - let name = base - for (let n = 2; this.findClass(name) || this.project.files.some((f) => fileNameOf(f.path) === `${name}.java`); n++) name = base + n - return name - } - - private newClass(kind: 'class' | 'interface', at?: Point) { - const name = this.uniqueClassName(t(kind === 'class' ? 'web_visuobject_new_class_name' : 'web_visuobject_new_interface_name')) - if (at) { - this.positions.set(name, { x: Math.max(0, Math.round(at.x)), y: Math.max(0, Math.round(at.y)) }) - } - const pkg = this.currentPackage ?? (this.active ? packageOf(this.active) : (packagesOf(this.project)[0] ?? '')) - const path = pathFor(pkg, `${name}.java`) - this.selection = { cls: name } - this.open.set(`pkg:${pkg}`, true) - this.setProject(addFile(this.project, path, this.sectioned(classFileText(pkg, name, kind), name)), path) - this.savePositions() - const cls = this.findClass(name) - if (cls) this.showInCode(cls) - const box = this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(name)}"]`) - box?.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - this.startEditing(box?.querySelector('.uml-name') ?? null) - } - - /** Klasse umbenennen; heißt die Datei wie die Klasse, heißt sie danach wie der neue Name. */ - private renameClassTo(cls: ProjectClass, name: string) { - if (name === cls.name) return - if (!identifier.test(name) || this.findClass(name)) return this.message(t('web_visuobject_bad_name', name)) - const file = this.project.files.find((f) => f.path === cls.path)! - let next = updateFile(this.project, cls.path, renameClass(file.text, cls, name)) - let path = cls.path - if (fileNameOf(cls.path) === `${cls.name}.java`) { - path = pathFor(packageOf(cls.path), `${name}.java`) - if (!next.files.some((f) => f.path === path)) next = moveFile(next, cls.path, path) - else path = cls.path - } - const p = this.positions.get(cls.name) - if (p) this.positions.set(name, p) - for (const key of Object.keys(next.uml ?? {})) if (key.startsWith(`${cls.name}.`)) next = { ...next, uml: renameInfo(next.uml!, key, name + key.slice(cls.name.length)) } - this.selection = { cls: name } - this.message('') - this.setProject(next, path) - } - - /** Klasse löschen; bleibt in der Datei keine Klasse übrig, verschwindet die Datei. */ - private deleteClassFromProject(cls: ProjectClass) { - const file = this.project.files.find((f) => f.path === cls.path)! - const text = deleteClass(file.text, cls) - const empty = parseClasses(text).length === 0 - this.selection = undefined - this.message('') - this.setProject(empty ? removeFile(this.project, cls.path) : updateFile(this.project, cls.path, text)) - } - - /** Eingabe im Diagramm oder Explorer übernehmen. `target` sagt, was bearbeitet wurde. */ - private commit(target: HTMLElement, value: string) { - const text = value.trim() - const node = target.closest('[data-node]')?.dataset.node - if (node) return this.commitTree(node, text) - const box = target.closest('.uml-class') - const cls = box && this.findClass(box.dataset.class!) - if (!cls) return - if (target.classList.contains('uml-name')) return this.renameClassTo(cls, text) - const add = target.dataset.add - if (add) { - if (!text) return - const line = parseUmlLine(text) - if (!line) return this.message(t('web_visuobject_bad_line', text)) - // „+ Attribut“ ohne Klammern bleibt ein Attribut, „+ Methode“ ohne Klammern wird eine Methode. - const typed = add === 'method' && line.kind === 'field' ? `${text}()` : text - this.changeFile(cls.path, (s) => this.sorted(addMember(s, cls, typed), cls.name), { cls: cls.name }) - return - } - const index = Number(target.dataset.member) - const member = cls.members[index] - if (!member) return - if (!text) { - this.changeFile(cls.path, (s) => deleteMember(s, member), { cls: cls.name }) - return - } - if (text === umlText(member)) return - const line = parseUmlLine(text) - if (!line) return this.message(t('web_visuobject_bad_line', text)) - if (member.kind === 'field' && line.kind === 'field' && line.name !== member.name && this.project.uml?.[relationKey(cls.name, member.name)]) { - const file = this.project.files.find((f) => f.path === cls.path)! - this.selection = { cls: cls.name, member: index } - this.setProject({ ...updateFile(this.project, cls.path, replaceMember(file.text, cls, member, text)), uml: renameInfo(this.project.uml!, relationKey(cls.name, member.name), relationKey(cls.name, line.name)) }, cls.path) - return - } - this.changeFile(cls.path, (s) => replaceMember(s, cls, member, text), { cls: cls.name, member: index }) - } - - private commitTree(node: NodeId, text: string) { - const [kind, ...rest] = node.split(':') - const id = rest.join(':') - if (kind === 'project') { - if (!text || text === this.project.name) return - const taken = this.projects.some((p) => p !== this.folder && p.toLowerCase() === safeFolderName(text).toLowerCase()) - if (taken) return this.message(t('web_visuobject_ws_taken', text)) - this.setProject({ ...this.project, name: text }) - } else if (kind === 'root') { - if (!text || text === id || id === defaultRoot) return - if (!isFolderName(text) || sourceFoldersOf(this.project).includes(text)) return this.message(t('web_visuobject_bad_folder', text)) - if (this.currentPackage && rootOf(this.currentPackage) === id) this.currentPackage = packageKey(text, barePackage(this.currentPackage)) - this.treeSelection = `root:${text}` - const moved = (p?: string) => (p && rootOf(p) === id ? `${text}:${p.slice(id.length + 1)}` : p) - this.openTabs = this.openTabs.map((p) => moved(p)!) - this.setProject(renameSourceFolder(this.project, id, text), moved(this.active)) - } else if (kind === 'pkg') { - const to = packageKey(rootOf(id), text) - if (!text || to === id) return - if (!isPackageName(text) || packagesOf(this.project).includes(to)) return this.message(t('web_visuobject_bad_package', text)) - if (this.currentPackage === id) this.currentPackage = to - this.treeSelection = `pkg:${to}` - this.open.set(`pkg:${to}`, true) - this.setProject(renamePackage(this.project, id, to), this.active && packageOf(this.active) === id ? pathFor(to, fileNameOf(this.active)) : this.active) - } else if (kind === 'file') { - const name = text.replace(/\.java$/i, '') - const cls = this.classes.find((c) => c.path === id && `${c.name}.java` === fileNameOf(id)) - if (cls) return this.renameClassTo(cls, name) - if (!identifier.test(name)) return this.message(t('web_visuobject_bad_name', name)) - const to = pathFor(packageOf(id), `${name}.java`) - if (to !== id && !this.project.files.some((f) => f.path === to)) this.setProject(moveFile(this.project, id, to), to) - } else if (kind === 'class') { - const cls = this.findClass(id) - if (cls) this.renameClassTo(cls, text) - } - } - - private startEditing(target: HTMLElement | null) { - if (!target || this.editing) return - const isName = target.classList.contains('uml-name') || target.classList.contains('vo-label') - const input = document.createElement('input') - input.className = 'uml-input' - input.spellcheck = false - input.autocapitalize = 'off' - input.setAttribute('autocomplete', 'off') - input.setAttribute('aria-label', t(isName ? 'web_visuobject_name_label' : 'web_visuobject_member_label')) - if (target.dataset.add) input.placeholder = t(target.dataset.add === 'field' ? 'web_visuobject_field_ph' : 'web_visuobject_method_ph') - input.value = target.dataset.add ? '' : (target.dataset.value ?? target.textContent ?? '') - input.size = Math.max(input.value.length, input.placeholder.length, 8) - target.replaceChildren(input) - this.editing = true - let done = false - const finish = (keep: boolean) => { - if (done) return - done = true - this.editing = false - if (keep) this.commit(target, input.value) - this.render() - this.renderTree() - } - input.addEventListener('keydown', (ev) => { - ev.stopPropagation() - if (ev.key === 'Enter') finish(true) - else if (ev.key === 'Escape') finish(false) - }) - input.addEventListener('click', (ev) => ev.stopPropagation()) - input.addEventListener('input', () => (input.size = Math.max(input.value.length, input.placeholder.length, 8))) - input.addEventListener('blur', () => finish(true)) - input.focus() - input.select() - } - - private onKey(ev: KeyboardEvent) { - if ((ev.metaKey || ev.ctrlKey) && ev.key.toLowerCase() === 's') { - ev.preventDefault() - void this.save() - return - } - if (ev.key === 'Escape' && this.endPop) { - this.closeEnd() - return - } - if (ev.key === 'Escape' && this.tool !== 'select') { - this.setTool('select') - return - } - if (this.editing || this.editor.hasFocus) return - const target = ev.target as HTMLElement - if (this.diagramMode === 'object' && !target.closest('input, select, textarea') && !(ev.metaKey || ev.ctrlKey) && this.objectDiagram.onKey(ev)) return - if (target.closest('input, select, textarea, .vo-tree')) return - const mod = ev.metaKey || ev.ctrlKey - if (mod && ev.key.toLowerCase() === 'z') { - ev.preventDefault() - this.undo(ev.shiftKey) - return - } - if (!this.canvas.contains(document.activeElement) && document.activeElement !== document.body) return - if (this.noteSelection && this.findNote(this.noteSelection)) { - if (ev.key === 'Delete' || ev.key === 'Backspace') { - ev.preventDefault() - this.deleteNote(this.noteSelection) - } else if (ev.key === 'Enter' || ev.key === 'F2') { - ev.preventDefault() - this.editNote(this.noteSelection) - } - return - } - if (this.relSelection && (ev.key === 'Delete' || ev.key === 'Backspace')) { - ev.preventDefault() - this.deleteRelation(this.relSelection) - return - } - const sel = this.selection - const cls = sel && this.findClass(sel.cls) - if (!cls) return - if (ev.key === 'Delete' || ev.key === 'Backspace') { - ev.preventDefault() - const member = sel.member !== undefined ? cls.members[sel.member] : undefined - if (member) this.changeFile(cls.path, (s) => deleteMember(s, member), { cls: cls.name }) - else this.deleteClassFromProject(cls) - } else if (ev.key === 'Enter' || ev.key === 'F2') { - ev.preventDefault() - const box = this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(cls.name)}"]`) - this.startEditing(box?.querySelector(sel.member !== undefined ? `[data-member="${sel.member}"]` : '.uml-name') ?? null) - } - } - - // MARK: - Package Explorer - - private isOpen(id: NodeId): boolean { - // Projekt, src, Packages und Klassen offen; Dateien zu, außer der geöffneten. Andere Projekte öffnet erst ein Klick. - if (id.startsWith('wsproject:')) return false - const value = this.open.get(id) - if (value !== undefined) return value - if (id.startsWith('file:')) return id === `file:${this.active}` - return true - } - - private toggle(id: NodeId) { - this.open.set(id, !this.isOpen(id)) - store(treeKey, JSON.stringify(Object.fromEntries(this.open))) - this.renderTree() - } - - private renderTree() { - const focused = (document.activeElement as HTMLElement | null)?.closest('[data-node]')?.dataset.node - const sel = this.treeSelection ?? (this.selection ? `class:${this.selection.cls}` : this.active ? `file:${this.active}` : undefined) - const row = (id: NodeId, depth: number, badge: string, label: string, opts: { children?: string; value?: string; extra?: string; title?: string; action?: boolean } = {}) => { - const hasChildren = opts.children !== undefined - const isOpen = hasChildren && this.isOpen(id) - const selected = id === sel - return `
  • -
    - ${hasChildren ? `` : ''} - ${badge}${e(label)}${opts.extra ?? ''} - ${opts.action ? `` : ''} -
    - ${isOpen && opts.children ? `
      ${opts.children}
    ` : ''} -
  • ` - } - const memberRow = (cls: ProjectClass, m: UmlMember, i: number, depth: number) => { - const vis = { '+': 'public', '-': 'private', '#': 'protected', '~': 'package' }[m.visibility] - const params = m.params.map((p) => p.type).join(', ') - const label = m.kind === 'field' ? m.name : `${m.name}(${params})` - const type = m.kind === 'constructor' ? '' : `: ${e(m.type)}` - return row(`member:${cls.name}:${i}`, depth, ``, label, { extra: type, title: umlText(m) }) - } - const classRow = (cls: ProjectClass, depth: number) => { - const letter = cls.kind === 'interface' ? 'I' : cls.isAbstract ? 'A' : 'C' - const members = cls.members.map((m, i) => memberRow(cls, m, i, depth + 1)).join('') - return row(`class:${cls.name}`, depth, ``, cls.name, { children: members }) - } - const fileRow = (path: string, depth: number) => { - const inFile = this.classes.filter((c) => c.path === path) - return row(`file:${path}`, depth, '', fileNameOf(path), { children: inFile.map((c) => classRow(c, depth + 1)).join(''), action: true }) - } - const packagesIn = (root: string) => - packagesOf(this.project, root) - .map((pkg) => { - const files = filesIn(this.project, pkg).map((f) => fileRow(f.path, 3)).join('') - const bare = barePackage(pkg) - const label = bare || t('web_visuobject_default_package') - return row(`pkg:${pkg}`, 2, ``, label, { children: files, value: bare, action: true }) - }) - .join('') - // Source Folders wie in Eclipse: `src`, dazu z. B. `test`. - const src = sourceFoldersOf(this.project) - .map((root) => - row(`root:${root}`, 1, '', root, { - children: packagesIn(root) || `
  • ${e(t('web_visuobject_tree_empty'))}
  • `, - action: root !== defaultRoot, - }), - ) - .join('') - const current = row('project', 0, '', this.project.name, { children: src, extra: this.isDirty() ? ` ●` : '' }) - // Im Workspace stehen alle Projekte untereinander wie in Eclipse; nur das geöffnete klappt auf. - const others = this.workspace ? this.projects.filter((p) => p !== this.folder) : [] - const all = [...others.map((name) => ({ name, html: row(`wsproject:${name}`, 0, '', name, { children: '', title: t('web_visuobject_ws_open_project', name) }) })), { name: this.folder ?? this.project.name, html: current }] - all.sort((a, b) => a.name.localeCompare(b.name)) - this.tree.innerHTML = `
      ${all.map((x) => x.html).join('')}
    ` - if (!this.tree.querySelector('[tabindex="0"]')) this.tree.querySelector('.vo-row')?.setAttribute('tabindex', '0') - if (focused) this.tree.querySelector(`[data-node="${CSS.escape(focused)}"] > .vo-row`)?.focus({ preventScroll: true }) - } - - private treeClick = { node: '', time: 0 } - - private onTreeClick(ev: MouseEvent) { - if (this.editing) return - const target = ev.target as HTMLElement - const item = target.closest('[data-node]') - const id = item?.dataset.node - if (!item || !id) return - if (target.closest('[data-toggle]')) return id.startsWith('wsproject:') ? void this.switchProject(id.slice(10)) : this.toggle(id) - if (target.closest('[data-delete]')) return this.deleteNode(id) - // Doppelklick selbst erkennen (der erste Klick zeichnet neu): Name ändern. - const now = Date.now() - const twice = this.treeClick.node === id && now - this.treeClick.time < 450 - this.treeClick = { node: id, time: twice ? 0 : now } - if (twice && !id.startsWith('member:') && !id.startsWith('wsproject:') && id !== `root:${defaultRoot}`) { - this.startEditing(this.tree.querySelector(`[data-node="${CSS.escape(id)}"] > .vo-row .vo-label`)) - return - } - this.selectNode(id) - } - - private selectNode(id: NodeId) { - const [kind, ...rest] = id.split(':') - const value = rest.join(':') - this.treeSelection = undefined - if (kind === 'wsproject') return void this.switchProject(value) - if (kind === 'pkg') { - this.currentPackage = value - this.treeSelection = id - } else if (kind === 'file') { - this.currentPackage = packageOf(value) - this.treeSelection = id - this.openFile(value) - } else if (kind === 'class' || kind === 'member') { - const [name, index] = value.split(':') - const cls = this.findClass(name) - if (!cls) return - const member = index !== undefined ? cls.members[Number(index)] : undefined - this.selection = { cls: cls.name, member: index !== undefined ? Number(index) : undefined } - this.currentPackage = packageOf(cls.path) - this.showInCode(cls, member) - if (index !== undefined) this.treeSelection = id - } else { - this.treeSelection = id - } - this.render() - this.renderTree() - } - - private deleteNode(id: NodeId) { - const [kind, ...rest] = id.split(':') - const value = rest.join(':') - this.treeSelection = undefined - if (kind === 'file') this.setProject(removeFile(this.project, value)) - else if (kind === 'root') { - if (this.currentPackage && rootOf(this.currentPackage) === value) this.currentPackage = undefined - this.setProject(removeSourceFolder(this.project, value)) - } - else if (kind === 'pkg') { - if (this.currentPackage === value) this.currentPackage = undefined - this.setProject(removePackage(this.project, value)) - } - } - - /** Pfeiltasten wie in einem Dateibaum: hoch/runter, rechts öffnet, links schließt, Eingabe wählt. */ - private onTreeKey(ev: KeyboardEvent) { - if (this.editing) return - const rows = [...this.tree.querySelectorAll('.vo-row')] - const current = (document.activeElement as HTMLElement | null)?.closest('.vo-row') - const index = current ? rows.indexOf(current) : -1 - const id = current?.closest('[data-node]')?.dataset.node - const focus = (i: number) => { - const next = rows[Math.max(0, Math.min(rows.length - 1, i))] - for (const r of rows) r.tabIndex = -1 - next.tabIndex = 0 - next.focus() - } - if (ev.key === 'ArrowDown') focus(index + 1) - else if (ev.key === 'ArrowUp') focus(index - 1) - else if (id && ev.key === 'ArrowRight' && current?.querySelector('[data-toggle]') && !this.isOpen(id)) this.toggle(id) - else if (id && ev.key === 'ArrowLeft' && current?.querySelector('[data-toggle]') && this.isOpen(id)) this.toggle(id) - else if (id && ev.key === 'Enter') this.selectNode(id) - else if (id && ev.key === 'F2') this.startEditing(current!.querySelector('.vo-label')) - else if (id && (ev.key === 'Delete' || ev.key === 'Backspace') && current?.querySelector('[data-delete]')) this.deleteNode(id) - else return - ev.preventDefault() - } - - // MARK: - Maus im Diagramm - - private drag?: { name: string; box: HTMLElement; start: Point; origin: Point; moved: boolean } - - private onPointerDown(ev: PointerEvent) { - this.closeEnd() - const target = ev.target as HTMLElement - if (this.tool !== 'select' && ev.button === 0 && !target.closest('.vo-dock, .vo-relmenu, input, select, button:not(.uml-connect)')) { - this.suppressClick = true - return this.useTool(ev) - } - const noteHandle = target.closest('[data-note-connect]') - if (noteHandle && ev.button === 0) return this.startNoteConnect(ev, noteHandle.closest('.uml-note')!.dataset.note!) - const noteBox = target.closest('.uml-note') - if (noteBox && ev.button === 0 && !target.closest('textarea')) return this.dragNote(ev, noteBox) - const handle = target.closest('[data-connect]') - if (handle && ev.button === 0) return this.startConnect(ev, handle.closest('.uml-class')!.dataset.class!) - if (target.closest('input, select, button, label')) return - const box = target.closest('.uml-class') - if (!box || ev.button !== 0) return - const origin = this.positions.get(box.dataset.class!) ?? { x: box.offsetLeft, y: box.offsetTop } - this.drag = { name: box.dataset.class!, box, start: { x: ev.clientX, y: ev.clientY }, origin, moved: false } - const move = (m: PointerEvent) => { - const d = this.drag - if (!d) return - const dx = m.clientX - d.start.x - const dy = m.clientY - d.start.y - if (!d.moved && Math.hypot(dx, dy) < 4) return - d.moved = true - const p = { x: Math.max(0, Math.round(d.origin.x + dx)), y: Math.max(0, Math.round(d.origin.y + dy)) } - this.positions.set(d.name, p) - d.box.style.left = `${p.x}px` - d.box.style.top = `${p.y}px` - this.drawLines() - } - const up = () => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - if (this.drag?.moved) this.savePositions() - // Klick nach dem Ziehen nicht als Auswahl werten. - setTimeout(() => (this.drag = undefined), 0) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - private lastClick = { spot: '', time: 0 } - - private onClick(ev: MouseEvent) { - if (this.suppressClick) { - this.suppressClick = false - return - } - if (this.drag?.moved || this.editing) return - const target = ev.target as HTMLElement - if (target.closest('input, select, label, textarea')) return - const noteEl = target.closest('.uml-note') - if (noteEl) { - const id = noteEl.dataset.note! - const now = Date.now() - const twice = this.lastClick.spot === `note|${id}` && now - this.lastClick.time < 450 - this.lastClick = { spot: `note|${id}`, time: twice ? 0 : now } - if (twice) return this.editNote(id) - return this.selectNote(id) - } - this.noteSelection = undefined - const action = target.closest('[data-class-action]') - const box = target.closest('.uml-class') - const cls = box && this.findClass(box.dataset.class!) - if (action && cls) { - if (action.dataset.classAction === 'delete') this.deleteClassFromProject(cls) - return - } - const add = target.closest('[data-add]') - if (add) { - this.startEditing(add) - return - } - const rel = target.closest('[data-rel]')?.dataset.rel - const endEl = target.closest('[data-end]') - if (rel && !cls && endEl && this.relSelection === rel) { - this.openEnd(rel, endEl.dataset.end as 'a' | 'b', ev, endEl.dataset.part as 'role' | 'mult' | undefined) - return - } - if (rel && !cls) { - this.selectRelation(rel) - return - } - if (!cls) { - this.relSelection = undefined - this.selection = undefined - this.render() - this.renderTree() - return - } - const row = target.closest('[data-member]') - const index = row ? Number(row.dataset.member) : undefined - // Doppelklick selbst erkennen: Der erste Klick zeichnet neu, dann meldet der Browser kein `dblclick`. - const spot = `${cls.name}|${index ?? (target.closest('.uml-name') ? 'name' : '')}` - const now = Date.now() - const twice = spot === this.lastClick.spot && now - this.lastClick.time < 450 && !spot.endsWith('|') - this.lastClick = { spot, time: twice ? 0 : now } - if (twice) { - const again = this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(cls.name)}"]`) - this.startEditing(again?.querySelector(index !== undefined ? `[data-member="${index}"]` : '.uml-name') ?? null) - return - } - this.selection = { cls: cls.name, member: index } - this.relSelection = undefined - this.treeSelection = undefined - this.currentPackage = packageOf(cls.path) - this.showInCode(cls, index !== undefined ? cls.members[index] : undefined) - this.render() - this.renderTree() - this.canvas.focus({ preventScroll: true }) - } - - // MARK: - Zeichnen - - private setDiagramMode(mode: DiagramMode) { - this.diagramMode = mode - store(diagramModeKey, mode) - this.setTool('select') - this.render() - } - - /** Umschalter, Werkzeuge und Fläche passend zum Diagramm zeigen. */ - private showDiagramMode() { - const object = this.diagramMode === 'object' - const cls = this.diagramMode === 'class' - for (const b of this.shell.querySelectorAll('[data-diagram]')) b.setAttribute('aria-selected', String(b.dataset.diagram === this.diagramMode)) - this.shell.querySelector('.vo-palette:not(.vo-obj-palette)')!.hidden = !cls - this.shell.querySelector('.vo-obj-palette')!.hidden = !object - this.canvas.hidden = !cls - this.shell.querySelector('.vo-objcanvas')!.hidden = !object - this.shell.querySelector('.vo-seqcanvas')!.hidden = this.diagramMode !== 'sequence' - this.shell.querySelector('[data-action=layout]')!.hidden = !cls - if (object) { - const select = this.shell.querySelector('[data-obj-class]')! - const names = [...new Set(this.classes.filter((c) => c.kind === 'class' && !c.isAbstract).map((c) => c.name))] - const keep = names.includes(select.value) ? select.value : names[0] - select.innerHTML = names.map((n) => `${e(n)}`).join('') - this.shell.querySelector('[data-obj-add]')!.disabled = names.length === 0 - } - } - - private savePositions() { - const names = new Set(this.classes.map((c) => c.name)) - store(positionsKey, JSON.stringify(Object.fromEntries([...this.positions].filter(([n]) => names.has(n))))) - } - - private render() { - this.showDiagramMode() - if (this.diagramMode === 'object') return this.objectDiagram.render() - if (this.diagramMode === 'sequence') return this.renderSequence() - for (const old of this.canvas.querySelectorAll('.uml-class, .uml-note, .vo-empty')) old.remove() - for (const note of this.project.notes ?? []) this.canvas.append(this.noteBox(note)) - if (this.classes.length === 0) { - const empty = document.createElement('p') - empty.className = 'vo-empty' - empty.textContent = t('web_visuobject_empty') - if (!this.project.notes?.length) this.canvas.append(empty) - this.drawLines() - return - } - const boxes = new Map() - for (const cls of this.classes) { - if (boxes.has(cls.name)) continue - const box = this.classBox(cls) - boxes.set(cls.name, box) - this.canvas.append(box) - } - this.placeNew(boxes) - this.drawLines() - } - - // MARK: - Notizen - - /** UML-Notiz: Kasten mit Eselsohr, Text wie getippt (Zeilenumbrüche bleiben). */ - private noteBox(note: UmlNote): HTMLElement { - const box = document.createElement('div') - const selected = this.noteSelection === note.id - box.className = `uml-note${selected ? ' selected' : ''}` - box.dataset.note = note.id - box.style.left = `${note.x}px` - box.style.top = `${note.y}px` - box.innerHTML = `${selected ? `` : ''}
    ${e(note.text || t('web_visuobject_note_ph'))}
    ` - return box - } - - private findNote(id: string | undefined): UmlNote | undefined { - return id ? this.project.notes?.find((n) => n.id === id) : undefined - } - - private setNotes(notes: UmlNote[]) { - this.setProject({ ...this.project, notes: notes.length ? notes : undefined }) - } - - private changeNote(id: string, change: Partial) { - this.setNotes((this.project.notes ?? []).map((n) => (n.id === id ? { ...n, ...change } : n))) - } - - private addNote(at: Point) { - const id = `n${Date.now().toString(36)}` - this.noteSelection = id - this.selection = undefined - this.relSelection = undefined - this.setNotes([...(this.project.notes ?? []), { id, text: '', x: Math.max(0, Math.round(at.x)), y: Math.max(0, Math.round(at.y)) }]) - this.editNote(id) - } - - private deleteNote(id: string) { - this.noteSelection = undefined - this.setNotes((this.project.notes ?? []).filter((n) => n.id !== id)) - } - - private selectNote(id: string) { - this.noteSelection = id - this.selection = undefined - this.relSelection = undefined - this.treeSelection = undefined - this.render() - this.renderTree() - this.canvas.focus({ preventScroll: true }) - } - - /** Text direkt im Kasten ändern; Strg+Enter oder Klick daneben übernimmt, Esc bricht ab. */ - private editNote(id: string) { - const note = this.findNote(id) - const box = this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(id)}"]`) - if (!note || !box) return - const area = document.createElement('textarea') - area.className = 'uml-note-edit' - area.value = note.text - area.placeholder = t('web_visuobject_note_ph') - area.rows = Math.max(3, note.text.split('\n').length + 1) - box.querySelector('.uml-note-text')!.replaceWith(area) - this.editing = true - let done = false - const finish = (keep: boolean) => { - if (done) return - done = true - this.editing = false - if (keep && area.value !== note.text) this.changeNote(id, { text: area.value }) - else this.render() - } - area.addEventListener('keydown', (ev) => { - ev.stopPropagation() - if (ev.key === 'Escape') finish(false) - else if (ev.key === 'Enter' && (ev.metaKey || ev.ctrlKey)) finish(true) - }) - area.addEventListener('blur', () => finish(true)) - area.focus() - } - - /** Vom Griff an der Notiz zu einer Klasse ziehen: gestrichelte Linie, wie in Astah. */ - private startNoteConnect(ev: PointerEvent, id: string) { - ev.preventDefault() - const box = this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(id)}"]`)! - const ghost = document.createElementNS('http://www.w3.org/2000/svg', 'line') - ghost.setAttribute('class', 'vo-rel vo-note-link ghost') - this.lines.append(ghost) - const start = { x: box.offsetLeft + box.offsetWidth, y: box.offsetTop + box.offsetHeight / 2 } - const targetAt = (m: PointerEvent) => document.elementFromPoint(m.clientX, m.clientY)?.closest('.uml-class') ?? undefined - const move = (m: PointerEvent) => { - const r = this.canvas.getBoundingClientRect() - ghost.setAttribute('x1', String(start.x)) - ghost.setAttribute('y1', String(start.y)) - ghost.setAttribute('x2', String(m.clientX - r.left + this.canvas.scrollLeft)) - ghost.setAttribute('y2', String(m.clientY - r.top + this.canvas.scrollTop)) - for (const el of this.canvas.querySelectorAll('.uml-class.drop')) el.classList.remove('drop') - targetAt(m)?.classList.add('drop') - } - const up = (m: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - ghost.remove() - const target = targetAt(m) - target?.classList.remove('drop') - this.suppressClick = true - const note = this.findNote(id) - const cls = target?.dataset.class - if (note && cls && !note.attach?.includes(cls)) this.changeNote(id, { attach: [...(note.attach ?? []), cls] }) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - private dragNote(ev: PointerEvent, box: HTMLElement) { - const note = this.findNote(box.dataset.note) - if (!note) return - const start = { x: ev.clientX, y: ev.clientY } - let moved = false - let p = { x: note.x, y: note.y } - const move = (m: PointerEvent) => { - const dx = m.clientX - start.x - const dy = m.clientY - start.y - if (!moved && Math.hypot(dx, dy) < 4) return - moved = true - p = { x: Math.max(0, Math.round(note.x + dx)), y: Math.max(0, Math.round(note.y + dy)) } - box.style.left = `${p.x}px` - box.style.top = `${p.y}px` - this.drawLines() - } - const up = () => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - if (!moved) return - this.suppressClick = true - this.changeNote(note.id, p) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - /** Gestrichelte Linien von Notizen zu ihren Klassen. */ - private noteLinksSVG(boxes: Map): string { - const out: string[] = [] - for (const note of this.project.notes ?? []) { - const nb = this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(note.id)}"]`) - if (!nb) continue - for (const cls of note.attach ?? []) { - const cb = boxes.get(cls) - if (!cb) continue - const ra = { x: nb.offsetLeft, y: nb.offsetTop, w: nb.offsetWidth, h: nb.offsetHeight } - const rb = { x: cb.offsetLeft, y: cb.offsetTop, w: cb.offsetWidth, h: cb.offsetHeight } - const ca = { x: ra.x + ra.w / 2, y: ra.y + ra.h / 2 } - const cc = { x: rb.x + rb.w / 2, y: rb.y + rb.h / 2 } - const a = edgePoint(ra, ca, cc) - const b = edgePoint(rb, cc, ca) - out.push(``) - } - } - return out.join('') - } - - private renderNoteInspector(note: UmlNote) { - this.inspector.dataset.rel = `note:${note.id}` - const classes = [...new Set(this.classes.map((c) => c.name))] - this.inspector.innerHTML = ` -

    ${e(t('web_visuobject_tool_note'))}

    - - ${classes.length ? `
    ${e(t('web_visuobject_note_attached'))}${classes.map((c) => ``).join('')}
    ` : ''} -
    ` - this.inspector.querySelector('[data-field=note-text]')!.addEventListener('change', (ev) => this.changeNote(note.id, { text: (ev.target as HTMLTextAreaElement).value })) - for (const box of this.inspector.querySelectorAll('[data-field=note-attach]')) - box.addEventListener('change', () => { - const attach = [...this.inspector.querySelectorAll('[data-field=note-attach]')].filter((b) => b.checked).map((b) => b.value) - this.changeNote(note.id, { attach: attach.length ? attach : undefined }) - }) - this.inspector.querySelector('[data-inspector=delete-note]')!.addEventListener('click', () => this.deleteNote(note.id)) - } - - private classBox(cls: ProjectClass): HTMLElement { - const sel = this.selection?.cls === cls.name ? this.selection : undefined - const box = document.createElement('div') - box.className = `uml-class${sel ? ' selected' : ''}${cls.kind === 'interface' ? ' interface' : ''}${cls.isAbstract ? ' abstract' : ''}` - box.dataset.class = cls.name - const p = this.positions.get(cls.name) - if (p) { - box.style.left = `${p.x}px` - box.style.top = `${p.y}px` - } - const row = (m: UmlMember, i: number) => { - const text = umlText(m) - const cls2 = `uml-member${m.isStatic ? ' static' : ''}${m.isAbstract ? ' abstract' : ''}${sel?.member === i ? ' selected' : ''}` - return `
  • ${e(text)}
  • ` - } - // Referenzattribute bleiben im Kasten, wie in Astah; die Linie trägt keine Rolle. - const fields = cls.members.map((m, i) => (m.kind === 'field' ? row(m, i) : '')).join('') - const methods = cls.members.map((m, i) => (m.kind !== 'field' ? row(m, i) : '')).join('') - const add = (kind: 'field' | 'method') => - sel ? `
  • ${e(t(kind === 'field' ? 'web_visuobject_add_field' : 'web_visuobject_add_method'))}
  • ` : '' - box.innerHTML = ` - ${sel && sel.member === undefined ? `` : ''} -
    ${cls.kind === 'interface' ? '«interface»' : cls.isAbstract ? '{abstract}' : ''}${e(cls.name)}
    - ${cls.kind === 'interface' && !fields ? '' : `
      ${fields}${add('field')}
    `} -
      ${methods}${add('method')}
    ` - return box - } - - /** - * Klassen ohne Position anordnen: jedes Package als eigener Block, die Blöcke nebeneinander, - * solange sie in die Breite passen, sonst darunter. Im Block stehen Oberklassen und Interfaces oben. - * Eine neue Klasse in einem schon gezeichneten Package kommt neben ihre Geschwister. - */ - private placeNew(boxes: Map) { - if (this.classes.every((c) => this.positions.has(c.name))) return - const parents = (c: ProjectClass) => [c.superclass, ...c.interfaces].filter((n): n is string => !!n && boxes.has(n)) - const level = new Map() - const depth = (c: ProjectClass, seen: Set): number => { - if (level.has(c.name)) return level.get(c.name)! - if (seen.has(c.name)) return 0 - seen.add(c.name) - // Nur Eltern im selben Package zählen für die Ebene im Block. - const up = parents(c) - .map((n) => this.findClass(n)!) - .filter((p) => p.pkg === c.pkg) - .map((p) => depth(p, seen)) - const d = up.length ? Math.max(...up) + 1 : 0 - level.set(c.name, d) - return d - } - for (const c of this.classes) depth(c, new Set()) - type Rect = { x: number; y: number; w: number; h: number } - const gap = 48 - const width = Math.max(this.canvas.clientWidth, 400) - 24 - const apart = (a: Rect, b: Rect, m: number) => a.x + a.w + m <= b.x || b.x + b.w + m <= a.x || a.y + a.h + m <= b.y || b.y + b.h + m <= a.y - const size = (c: ProjectClass) => ({ w: boxes.get(c.name)!.offsetWidth, h: boxes.get(c.name)!.offsetHeight }) - const put = (c: ProjectClass, x: number, y: number) => { - this.positions.set(c.name, { x, y }) - const box = boxes.get(c.name)! - box.style.left = `${x}px` - box.style.top = `${y}px` - } - // Rahmen eines Packages mit Reiter: 16 px Rand, oben 24 px mehr für den Namen. - const frameOf = (pkg: string): Rect | undefined => { - const rs = this.classes.filter((c) => c.pkg === pkg && boxes.has(c.name) && this.positions.has(c.name)).map((c) => ({ ...this.positions.get(c.name)!, ...size(c) })) - if (rs.length === 0) return undefined - const x = Math.min(...rs.map((r) => r.x)) - 16 - const y = Math.min(...rs.map((r) => r.y)) - 40 - return { x, y, w: Math.max(...rs.map((r) => r.x + r.w)) + 16 - x, h: Math.max(...rs.map((r) => r.y + r.h)) + 16 - y } - } - const packages = this.diagramPackages().filter((pkg) => this.classes.some((c) => c.pkg === pkg && boxes.has(c.name))) - for (const pkg of packages) { - const fresh = this.classes.filter((c) => c.pkg === pkg && boxes.has(c.name) && !this.positions.has(c.name)) - if (fresh.length === 0) continue - const others = packages.filter((p) => p !== pkg).map(frameOf).filter((r): r is Rect => !!r) - const own = frameOf(pkg) - if (own) { - // Neben die Geschwister, ohne in ein anderes Package zu ragen. - for (const c of fresh) { - const { w, h } = size(c) - const taken = this.classes.filter((o) => o.pkg === pkg && this.positions.has(o.name) && boxes.has(o.name)).map((o) => ({ ...this.positions.get(o.name)!, ...size(o) })) - let spot: { x: number; y: number } | undefined - for (let y = own.y + 40; !spot && y < own.y + 4000; y += 20) - for (let x = own.x + 16; x === own.x + 16 || x + w <= width; x += 20) { - const r = { x, y, w, h } - const outer = { x: x - 16, y: y - 40, w: w + 32, h: h + 56 } - if (taken.every((t) => apart(r, t, gap)) && others.every((o) => apart(outer, o, 24))) { - spot = { x, y } - break - } - } - spot ??= { x: own.x + 16, y: own.y + own.h + gap } - put(c, spot.x, spot.y) - } - continue - } - // Ganzer Block: Reihen nach Ebene, in der Reihe nebeneinander. - const rows: ProjectClass[][] = [] - for (const c of fresh) (rows[level.get(c.name)!] ??= []).push(c) - const local = new Map() - let y = 0 - let blockW = 0 - for (const row of rows) { - if (!row) continue - let x = 0 - let rowH = 0 - for (const c of row) { - const { w, h } = size(c) - local.set(c.name, { x, y }) - // Breiter Abstand nebeneinander: Platz für Rolle und Multiplizität an der Linie. - x += w + gap * 3 - rowH = Math.max(rowH, h) - } - blockW = Math.max(blockW, x - gap * 3) - y += rowH + gap * 1.5 - } - const blockH = y - gap * 1.5 - const outerW = blockW + 32 - const outerH = blockH + 56 - let spot: { x: number; y: number } | undefined - for (let oy = 4; !spot && oy < 20000; oy += 20) - for (let ox = 16; ox === 16 || ox + outerW <= width; ox += 20) - if (others.every((o) => apart({ x: ox, y: oy, w: outerW, h: outerH }, o, 32))) { - spot = { x: ox, y: oy } - break - } - spot ??= { x: 16, y: 4 } - for (const c of fresh) put(c, spot.x + 16 + local.get(c.name)!.x, spot.y + 40 + local.get(c.name)!.y) - } - this.savePositions() - } - - private drawLines() { - const boxes = new Map() - for (const b of this.canvas.querySelectorAll('.uml-class')) boxes.set(b.dataset.class!, b) - let width = 0 - let height = 0 - for (const b of boxes.values()) { - width = Math.max(width, b.offsetLeft + b.offsetWidth + 40) - height = Math.max(height, b.offsetTop + b.offsetHeight + 40) - } - for (const svg of [this.lines, this.frames]) { - svg.setAttribute('width', String(width)) - svg.setAttribute('height', String(height)) - } - this.frames.innerHTML = this.packageFrames(boxes) - const parts: string[] = [ - ` - - - `, - ] - const assocs = associations(this.classes, this.project.uml) - const gens = generalizations(this.classes) - // Mehrere Linien zwischen denselben Klassen leicht versetzen. - const pairCount = new Map() - const offset = (a: string, b: string) => { - const key = [a, b].sort().join('|') - const n = pairCount.get(key) ?? 0 - pairCount.set(key, n + 1) - return n * 16 - } - for (const g of gens) { - const a = boxes.get(g.from) - const b = boxes.get(g.to) - if (a && b && a !== b) parts.push(this.generalizationSVG(g, a, b, offset(g.from, g.to))) - } - for (const r of this.shownAssociations(assocs)) { - const a = boxes.get(r.from) - const b = boxes.get(r.to) - if (a && b && a !== b) parts.push(this.associationSVG(r, a, b, offset(r.from, r.to))) - } - for (const d of this.dependencies()) { - const a = boxes.get(d.from) - const b = boxes.get(d.to) - if (a && b && a !== b) parts.push(this.dependencySVG(d, a, b, offset(d.from, d.to))) - } - parts.push(this.noteLinksSVG(boxes)) - this.lines.innerHTML = parts.join('') - this.renderInspector() - } - - /** Java-Packages fürs Diagramm: Gleich benannte Packages in `src` und `test` sind dasselbe Package. */ - private diagramPackages(): string[] { - return [...new Set(packagesOf(this.project).map(barePackage))] - } - - /** UML-Package: Rahmen um seine Klassen mit Reiter oben links. */ - private packageFrames(boxes: Map): string { - const out: string[] = [] - for (const pkg of this.diagramPackages()) { - if (!pkg) continue - const els = this.classes.filter((c) => c.pkg === pkg).map((c) => boxes.get(c.name)).filter((b): b is HTMLElement => !!b) - if (els.length === 0) continue - const x = Math.min(...els.map((b) => b.offsetLeft)) - 16 - const y = Math.min(...els.map((b) => b.offsetTop)) - 16 - const right = Math.max(...els.map((b) => b.offsetLeft + b.offsetWidth)) + 16 - const bottom = Math.max(...els.map((b) => b.offsetTop + b.offsetHeight)) + 16 - const tab = Math.max(70, pkg.length * 7.6 + 20) - const current = this.currentPackage !== undefined && barePackage(this.currentPackage) === pkg ? ' current' : '' - out.push(` - - - ${e(pkg)} - `) - } - return out.join('') - } - - /** Linie zwischen den Rändern zweier Kästen, senkrecht um `offset` versetzt. */ - private segment(a: HTMLElement, b: HTMLElement, offset: number) { - const rect = (el: HTMLElement) => ({ x: el.offsetLeft, y: el.offsetTop, w: el.offsetWidth, h: el.offsetHeight }) - const ra = rect(a) - const rb = rect(b) - const ca = { x: ra.x + ra.w / 2, y: ra.y + ra.h / 2 } - const cb = { x: rb.x + rb.w / 2, y: rb.y + rb.h / 2 } - const len = Math.hypot(cb.x - ca.x, cb.y - ca.y) || 1 - const nx = (-(cb.y - ca.y) / len) * offset - const ny = ((cb.x - ca.x) / len) * offset - const from = edgePoint(ra, { x: ca.x + nx, y: ca.y + ny }, { x: cb.x + nx, y: cb.y + ny }) - const to = edgePoint(rb, { x: cb.x + nx, y: cb.y + ny }, { x: ca.x + nx, y: ca.y + ny }) - const l = Math.hypot(to.x - from.x, to.y - from.y) || 1 - // u: Einheitsvektor von `from` nach `to`, n: senkrecht dazu - return { from, to, length: l, u: { x: (to.x - from.x) / l, y: (to.y - from.y) / l }, n: { x: -(to.y - from.y) / l, y: (to.x - from.x) / l } } - } - - private hitLine(id: string, from: Point, to: Point, label: string): string { - return `${e(label)}` - } - - private dependencies(): Dependency[] { - return dependencies(this.classes, (c) => this.project.files.find((f) => f.path === (c as ProjectClass).path)?.text) - } - - private findDependency(id: string): Dependency | undefined { - return this.dependencies().find((d) => `dep:${d.from}:${d.to}` === id) - } - - /** Abhängigkeit: gestrichelte Linie mit offener Pfeilspitze und «create» bzw. «use» in der Mitte. */ - private dependencySVG(d: Dependency, a: HTMLElement, b: HTMLElement, offset: number): string { - const { from, to, n } = this.segment(a, b, offset) - const id = `dep:${d.from}:${d.to}` - const selected = this.relSelection === id ? ' selected' : '' - const mid = { x: (from.x + to.x) / 2 + n.x * 12, y: (from.y + to.y) / 2 + n.y * 12 } - const anchor = Math.abs(n.x) < 0.35 ? 'middle' : n.x > 0 ? 'start' : 'end' - const label = t(`web_visuobject_dep_${d.kind}_text`, d.from, d.to, d.methods.join(', ')) - return ` - - «${d.kind}» - ${this.hitLine(id, from, to, label)} - ` - } - - private generalizationSVG(g: Generalization, a: HTMLElement, b: HTMLElement, offset: number): string { - const { from, to, u } = this.segment(a, b, offset) - // Linie endet an der Grundseite des hohlen Dreiecks, die Spitze berührt die Oberklasse. - const base = { x: to.x - u.x * 12, y: to.y - u.y * 12 } - const id = `gen:${g.kind}:${g.from}:${g.to}` - const selected = this.relSelection === id ? ' selected' : '' - const label = t(g.kind === 'implements' ? 'web_visuobject_rel_implements_text' : 'web_visuobject_rel_extends_text', g.from, g.to) - return ` - - ${this.hitLine(id, from, to, label)} - ` - } - - /** - * Assoziation: Raute am Ganzen (Aggregation hohl, Komposition gefüllt), Pfeilspitzen je nach Navigierbarkeit, - * an jedem Ende Multiplizität und Rolle gleich weit von der Klasse auf beiden Seiten der Linie, - * in der Mitte der Name mit Leserichtung. - */ - /** - * Wie Astah: Stehen die Referenzattribute im Kasten, gibt es je Klassenpaar nur eine Linie, - * ohne Rollen (das Attribut steht ja im Kasten). Vorne steht die Linie mit Name oder anderem UML-Zusatz. - * Bei mehreren Attributen (p1, p2, p3) entfallen auch die Multiplizitäten. - */ - private shownAssociations(assocs: Assoc[]): (Assoc & { several?: boolean })[] { - const pairs = new Map() - for (const r of assocs) { - const key = [r.from, r.to].sort().join('|') - pairs.set(key, [...(pairs.get(key) ?? []), r]) - } - return [...pairs.values()].map((group) => { - const info = (r: Assoc) => (r.name ? 2 : 0) + (r.kind !== 'association' || r.navTo !== 'navigable' || r.navFrom !== (r.back ? 'navigable' : 'unspecified') ? 1 : 0) - const main = [...group].sort((x, y) => info(y) - info(x))[0] - const toward = (from: string) => group.filter((r) => r.from === from).length + group.filter((r) => r.back && r.to === from).length - const several = toward(main.from) > 1 || toward(main.to) > 1 - // Gibt es auch Attribute in der Gegenrichtung, ist auch dieses Ende navigierbar. - const backward = group.some((r) => r.from === main.to) - return { ...main, navFrom: backward && main.navFrom === 'unspecified' ? 'navigable' : main.navFrom, several } - }) - } - - private associationSVG(r: Assoc & { several?: boolean }, a: HTMLElement, b: HTMLElement, offset: number): string { - const { from, to, length, u, n } = this.segment(a, b, offset) - const id = `assoc:${r.key}` - const selected = this.relSelection === id ? ' selected' : '' - const diamond = r.kind !== 'association' - const start = diamond ? { x: from.x + u.x * 20, y: from.y + u.y * 20 } : from - let svg = `` - // Nicht navigierbar: kleines Kreuz × kurz vor der Klasse. - const cross = (at: Point, dir: Point) => { - const c = { x: at.x + dir.x * 12, y: at.y + dir.y * 12 } - const a = { x: (dir.x + n.x) * 5, y: (dir.y + n.y) * 5 } - const b = { x: (dir.x - n.x) * 5, y: (dir.y - n.y) * 5 } - return `` - } - if (r.navTo === 'non-navigable') svg += cross(to, { x: -u.x, y: -u.y }) - if (r.navFrom === 'non-navigable') svg += cross(start, u) - if (diamond) { - const p = (along: number, side: number) => `${from.x + u.x * along + n.x * side},${from.y + u.y * along + n.y * side}` - svg += `` - } - // Beschriftung an einem Ende: Multiplizität auf der einen, Rolle auf der anderen Seite, gleich weit weg. - // Ist die Linie markiert, stehen leere Felder hellgrau da; ein Klick darauf bearbeitet das Ende (wie in Astah). - const end = (at: Point, dir: Point, along: number, endId: 'a' | 'b', multiplicity?: string, role?: string) => { - // Waagerechte Linie: Text beginnt dicht an der Klasse und wächst von ihr weg. - const flat = Math.abs(n.x) < 0.35 - const base = flat ? { x: at.x + dir.x * 8, y: at.y + dir.y * 8 } : { x: at.x + dir.x * along, y: at.y + dir.y * along } - const text = (side: number, value: string | undefined, cls: 'mult' | 'role') => { - const empty = !value - if (empty && (!selected || cls === 'role')) return '' - const x = base.x + n.x * side - const y = base.y + n.y * side - const anchor = flat ? (dir.x > 0 ? 'start' : 'end') : n.x * side > 0 ? 'start' : 'end' - const shown = empty ? t(cls === 'role' ? 'web_visuobject_rel_role' : 'web_visuobject_end_mult_ph') : value - return `${e(shown!)}` - } - // Unsichtbarer Kreis am Ende: Rechtsklick dort öffnet das Ende auch ohne Markierung. - const c = { x: at.x + dir.x * 16, y: at.y + dir.y * 16 } - const hit = `${e(t('web_visuobject_end_tip'))}` - return hit + text(12, multiplicity, 'mult') + text(-12, role, 'role') - } - const room = Math.min(30, length / 3) - // Rolle mit Sichtbarkeit, denn das Attribut steht nicht mehr im Kasten. - // Attribute stehen im Kasten: an der Linie nur die Multiplizität (bei p1, p2, p3 keine). - svg += end(to, { x: -u.x, y: -u.y }, room, 'b', r.several ? undefined : r.multiplicity) - svg += end(from, u, diamond ? Math.min(42, length / 3) : room, 'a', r.several ? undefined : r.sourceMultiplicity) - if (r.name) { - // Leserichtung als Dreieck, das in Richtung der gelesenen Klasse zeigt. - const dir = r.reading === 'backward' ? { x: -u.x, y: -u.y } : u - const arrow = !r.reading ? '' : Math.abs(dir.x) >= Math.abs(dir.y) ? (dir.x > 0 ? ' ▶' : '◀ ') : dir.y > 0 ? ' ▼' : ' ▲' - const text = arrow === '◀ ' ? `◀ ${r.name}` : `${r.name}${arrow}` - const free = this.freeMiddle(from, to, [r.from, r.to]) - const mid = { x: free.x + n.x * 14, y: free.y + n.y * 14 } - const anchor = Math.abs(n.x) < 0.35 ? 'middle' : n.x > 0 ? 'start' : 'end' - svg += `${e(text)}` - } - const label = t(`web_visuobject_rel_${r.kind}`) + `: ${r.from} – ${r.to}` - return `${svg}${this.hitLine(id, from, to, label)}` - } - - /** Mitte des längsten Stücks der Linie, das durch keinen anderen Kasten läuft (für den Namen). */ - private freeMiddle(from: Point, to: Point, ends: string[]): Point { - const others = [...this.canvas.querySelectorAll('.uml-class')] - .filter((b) => !ends.includes(b.dataset.class!)) - .map((b) => ({ x: b.offsetLeft - 8, y: b.offsetTop - 8, w: b.offsetWidth + 16, h: b.offsetHeight + 16 })) - // Die Enden gehören Multiplizität und Rolle. - const len = Math.hypot(to.x - from.x, to.y - from.y) - if (len > 120) { - const k = 48 / len - ;[from, to] = [ - { x: from.x + (to.x - from.x) * k, y: from.y + (to.y - from.y) * k }, - { x: to.x - (to.x - from.x) * k, y: to.y - (to.y - from.y) * k }, - ] - } - const steps = 40 - let best = { start: 0, length: 0 } - let run = -1 - for (let i = 0; i <= steps; i++) { - const x = from.x + ((to.x - from.x) * i) / steps - const y = from.y + ((to.y - from.y) * i) / steps - const blocked = others.some((r) => x >= r.x && x <= r.x + r.w && y >= r.y && y <= r.y + r.h) - if (!blocked && run < 0) run = i - if ((blocked || i === steps) && run >= 0) { - const end = blocked ? i - 1 : i - if (end - run > best.length) best = { start: run, length: end - run } - run = -1 - } - } - const at = (best.start + best.length / 2) / steps - return { x: from.x + (to.x - from.x) * at, y: from.y + (to.y - from.y) * at } - } - - // MARK: - Beziehungen bearbeiten - - private findRelation(id: string): Assoc | Generalization | undefined { - if (id.startsWith('assoc:')) return associations(this.classes, this.project.uml).find((r) => `assoc:${r.key}` === id || `assoc:${r.back?.key}` === id) - return generalizations(this.classes).find((g) => `gen:${g.kind}:${g.from}:${g.to}` === id) - } - - private selectRelation(id: string) { - this.noteSelection = undefined - this.relSelection = id - this.selection = undefined - this.treeSelection = undefined - this.message('') - this.render() - this.renderTree() - this.canvas.focus({ preventScroll: true }) - } - - /** Datei mit der Klasse `name` frisch lesen und ändern; so passen die Stellen auch nach einer vorigen Änderung. */ - private editClass(project: JavaProject, name: string, run: (text: string, cls: UmlClass) => string): JavaProject { - for (const f of project.files) { - const cls = parseClasses(f.text).find((c) => c.name === name) - if (cls) return updateFile(project, f.path, this.sorted(run(f.text, cls), name)) - } - return project - } - - private sectionLabels(): SectionLabels { - return { fields: t('web_visuobject_section_fields'), refs: t('web_visuobject_section_refs'), ctors: t('web_visuobject_section_ctors'), methods: t('web_visuobject_section_methods') } - } - - /** Neue Mitglieder unter den passenden Abschnitts-Kommentar (nur wenn die Klasse welche hat). */ - private sorted(text: string, name: string): string { - return keepSections(text, name, new Set(this.classes.map((c) => c.name)), this.sectionLabels()) - } - - /** Neue Klasse gleich mit `// Attribute`, `// Referenzattribute`, `// Konstruktor`, `// Methoden`. */ - private sectioned(text: string, name: string): string { - const cls = parseClasses(text).find((c) => c.name === name) - return cls ? withSections(text, cls, new Set([...this.classes.map((c) => c.name), name]), this.sectionLabels()) : text - } - - private fieldOf(cls: UmlClass, name: string) { - return cls.members.find((m) => m.kind === 'field' && m.name === name) - } - - /** Import ergänzen, wenn `user` die Klasse `used` aus einem anderen Package braucht. */ - private importFor(project: JavaProject, user: string, used: string): JavaProject { - const a = this.findClass(user) - const b = this.findClass(used) - if (!a || !b || a.pkg === b.pkg || !b.pkg) return project - return this.editClass(project, user, (text) => ensureImport(text, `${b.pkg}.${b.name}`)) - } - - /** Attribut für eine Assoziation anlegen (Typ der Klasse oder Liste davon). */ - private addAssociationField(project: JavaProject, from: string, to: string, many: boolean, wanted?: string): { project: JavaProject; field: string } { - const cls = projectClasses(project).find((c) => c.name === from)! - // Eine schon eingetragene Rolle wird der Name des Attributs. - const field = wanted && identifier.test(wanted) && !cls.members.some((m) => m.name === wanted) ? wanted : fieldNameFor(cls, to, many) - const { type, init } = fieldTypeFor(to, many) - let next = this.editClass(project, from, (text, c) => addMember(text, c, `- ${field}: ${type}`)) - if (init) { - next = this.editClass(next, from, (text, c) => { - const m = this.fieldOf(c, field) - return m ? setFieldType(text, m, type, init) : text - }) - next = this.editClass(next, from, (text) => ensureImport(text, 'java.util.ArrayList')) - } - return { project: this.importFor(next, from, to), field } - } - - private setUml(project: JavaProject, key: string, change: Partial): JavaProject { - return { ...project, uml: withInfo(project.uml ?? {}, key, change) } - } - - /** Neue Beziehung aus dem Ziehen von Klasse zu Klasse. */ - private createRelation(from: string, to: string, kind: RelationTool) { - const a = this.findClass(from) - const b = this.findClass(to) - if (!a || !b) return - let next = this.project - let id: string - if (kind === 'extends' && a.kind === 'class') { - next = this.importFor(this.editClass(next, from, (text, c) => setSuperclass(text, c, to)), from, to) - id = `gen:extends:${from}:${to}` - } else if (kind === 'extends' || kind === 'implements') { - next = this.importFor(this.editClass(next, from, (text, c) => addInterface(text, c, to)), from, to) - id = `gen:${a.kind === 'interface' ? 'extends' : 'implements'}:${from}:${to}` - } else { - const added = this.addAssociationField(next, from, to, false) - const key = relationKey(from, added.field) - next = kind === 'association' ? added.project : kind === 'link' ? this.setUml(added.project, key, { targetEnd: 'unspecified' }) : this.setUml(added.project, key, { kind }) - id = `assoc:${relationKey(from, added.field)}` - } - this.relSelection = id - this.selection = undefined - this.message('') - this.setProject(next, a.path) - this.canvas.focus({ preventScroll: true }) - } - - private deleteRelation(id: string) { - const rel = this.findRelation(id) - if (!rel) return - let next = this.project - if ('key' in rel) { - const drop = (p: JavaProject, cls: string, field: string) => - this.editClass(p, cls, (text, c) => { - const m = this.fieldOf(c, field) - return m ? deleteMember(text, m) : text - }) - next = drop(next, rel.from, rel.role) - if (rel.back) next = drop(next, rel.to, rel.back.field.name) - const uml = { ...(next.uml ?? {}) } - delete uml[rel.key] - if (rel.back) delete uml[rel.back.key] - next = { ...next, uml } - } else if (rel.kind === 'extends' && this.findClass(rel.from)?.kind === 'class') { - next = this.editClass(next, rel.from, (text, c) => setSuperclass(text, c, undefined)) - } else { - next = this.editClass(next, rel.from, (text, c) => removeInterface(text, c, rel.to)) - } - this.relSelection = undefined - this.setProject(next) - } - - /** Eine Einstellung im Objektinspektor ändern. */ - private changeAssoc(r: Assoc, what: string, value: string) { - let next = this.project - let key = r.key - const rename = (cls: string, oldName: string, name: string): boolean => { - if (!identifier.test(name)) { - this.message(t('web_visuobject_bad_name', name)) - return false - } - const c = this.findClass(cls) - if (c && c.members.some((m) => m.name === name)) { - this.message(t('web_visuobject_bad_name', name)) - return false - } - next = this.editClass(next, cls, (text, fresh) => { - const m = this.fieldOf(fresh, oldName) - return m ? replaceMember(text, fresh, m, `${m.visibility} ${name}: ${m.type}`) : text - }) - next = { ...next, uml: renameInfo(next.uml ?? {}, relationKey(cls, oldName), relationKey(cls, name)) } - return true - } - /** Multiplizität am Attribut `field` von `cls` (Typ `target`): bei Wechsel einzeln ↔ viele den Typ ändern. */ - const multiplicity = (cls: string, field: string, target: string, value: string, metaKey: string) => { - const many = isMany(value) - next = this.editClass(next, cls, (text, fresh) => { - const m = this.fieldOf(fresh, field) - if (!m) return text - const current = isMany(r.key === metaKey ? r.multiplicity : (r.sourceMultiplicity ?? '1')) - if (current === many) return text - const { type, init } = fieldTypeFor(target, many) - return setFieldType(text, m, type, init) - }) - if (many) next = this.editClass(next, cls, (text) => ensureImport(text, 'java.util.ArrayList')) - const derived = many ? '0..*' : '1' - next = this.setUml(next, metaKey, { multiplicity: value === derived ? undefined : value }) - } - switch (what) { - case 'kind': - next = this.setUml(next, key, { kind: value as AssocKind }) - break - case 'name': - next = this.setUml(next, key, { name: value.trim() || undefined }) - break - case 'reading': - // Umdrehen: gegen die Zugrichtung lesen und zurück. - next = this.setUml(next, key, { reading: r.reading === 'backward' ? undefined : 'backward' }) - break - case 'targetEnd': - next = this.setUml(next, key, { targetEnd: value as EndNav, navigable: undefined }) - break - case 'sourceEnd': { - if (value === 'navigable' && !r.back) { - // Navigierbar zurück heißt im Code: die andere Klasse bekommt ein Attribut. - const added = this.addAssociationField(next, r.to, r.from, isMany(r.sourceMultiplicity ?? '1'), r.sourceRole) - next = this.setUml(added.project, key, { sourceEnd: undefined, sourceRole: undefined, sourceMultiplicity: undefined }) - if (r.sourceMultiplicity && r.sourceMultiplicity !== (isMany(r.sourceMultiplicity) ? '0..*' : '1')) next = this.setUml(next, relationKey(r.to, added.field), { multiplicity: r.sourceMultiplicity }) - } else if (value !== 'navigable') { - if (r.back) { - const back = r.back - next = this.editClass(next, r.to, (text, c) => { - const m = this.fieldOf(c, back.field.name) - return m ? deleteMember(text, m) : text - }) - const uml = { ...(next.uml ?? {}) } - delete uml[back.key] - next = { ...next, uml } - next = this.setUml(next, key, { sourceRole: r.sourceRole, sourceMultiplicity: r.sourceMultiplicity }) - } - next = this.setUml(next, key, { sourceEnd: value as EndNav }) - } - break - } - case 'role': - if (value === r.role) return - if (!rename(r.from, r.role, value)) return - key = relationKey(r.from, value) - break - case 'multiplicity': - if (!value.trim()) return - multiplicity(r.from, r.role, r.to, value.trim(), key) - break - case 'sourceRole': - if (r.back) { - if (value === r.back.field.name) return - if (!rename(r.to, r.back.field.name, value)) return - } else next = this.setUml(next, key, { sourceRole: value.trim() || undefined }) - break - case 'sourceMultiplicity': - if (!value.trim()) return - if (r.back) multiplicity(r.to, r.back.field.name, r.from, value.trim(), r.back.key) - else next = this.setUml(next, key, { sourceMultiplicity: value.trim() }) - break - } - this.relSelection = `assoc:${key}` - this.message('') - this.setProject(next) - const now = this.findRelation(this.relSelection) - if (now && 'key' in now && `assoc:${now.key}` !== this.relSelection) { - this.relSelection = `assoc:${now.key}` - this.render() - } - } - - /** Objektinspektor rechts im Diagramm: zeigt die gewählte Beziehung. */ - /** - * Kleines Feld an einem Ende der Linie wie in Astah: Rolle, Multiplizität als Knöpfe, Navigierbarkeit. - * Ende A ist die Klasse, bei der die Linie beginnt, Ende B die, bei der sie endet. - */ - private openEnd(id: string, which: 'a' | 'b', ev: MouseEvent, part?: 'role' | 'mult') { - this.closeEnd() - const rel = this.findRelation(id) - if (!rel || !('key' in rel)) return - const isA = which === 'a' - const keys = isA ? { role: 'sourceRole', mult: 'sourceMultiplicity', nav: 'sourceEnd' } : { role: 'role', mult: 'multiplicity', nav: 'targetEnd' } - const role = (isA ? rel.sourceRole : rel.role) ?? '' - const mult = (isA ? rel.sourceMultiplicity : rel.multiplicity) ?? '' - const nav = isA ? rel.navFrom : rel.navTo - const cls = isA ? rel.from : rel.to - const pop = document.createElement('div') - pop.className = 'vo-endpop' - pop.setAttribute('role', 'dialog') - pop.setAttribute('aria-label', t(isA ? 'web_visuobject_rel_end_a' : 'web_visuobject_rel_end_b', cls)) - pop.innerHTML = `

    ${e(t(isA ? 'web_visuobject_rel_end_a' : 'web_visuobject_rel_end_b', cls))}

    - -
    ${e(t('web_visuobject_rel_multiplicity'))}${multiplicities.map((m) => ``).join('')}
    -
    ${e(t('web_visuobject_rel_navigable'))}${(['navigable', 'non-navigable', 'unspecified'] as const).map((v) => ``).join('')}
    ` - const stage = this.shell.querySelector('.vo-stage')! - const r = stage.getBoundingClientRect() - stage.append(pop) - const x = Math.min(Math.max(8, ev.clientX - r.left + 8), r.width - pop.offsetWidth - 8) - const y = Math.min(Math.max(8, ev.clientY - r.top + 8), r.height - pop.offsetHeight - 8) - pop.style.left = `${Math.max(8, x)}px` - pop.style.top = `${Math.max(8, y)}px` - const apply = (field: string, value: string) => { - const current = this.findRelation(id) - if (current && 'key' in current) this.changeAssoc(current, field, value) - // Nach dem Neuzeichnen bleibt das Feld offen, mit dem neuen Stand. - const focusPart = (document.activeElement as HTMLElement | null)?.dataset?.endField as 'role' | 'mult' | undefined - if (this.relSelection) this.reopenEnd(this.relSelection, which, pop, focusPart) - } - for (const input of pop.querySelectorAll('[data-end-field]')) { - input.addEventListener('change', () => apply(input.dataset.endField === 'role' ? keys.role : keys.mult, input.value.trim())) - input.addEventListener('keydown', (k) => { - k.stopPropagation() - if (k.key === 'Enter') input.blur() - if (k.key === 'Escape') this.closeEnd() - }) - } - for (const b of pop.querySelectorAll('[data-mult]')) b.addEventListener('click', () => apply(keys.mult, b.dataset.mult!)) - for (const b of pop.querySelectorAll('[data-nav]')) b.addEventListener('click', () => apply(keys.nav, b.dataset.nav!)) - pop.addEventListener('pointerdown', (p) => p.stopPropagation()) - pop.addEventListener('click', (c) => c.stopPropagation()) - this.endPop = pop - const first = pop.querySelector(`[data-end-field="${part === 'mult' ? 'mult' : 'role'}"]`) - first?.focus() - first?.select() - } - - /** Feld am Ende nach einer Änderung an derselben Stelle neu öffnen. */ - private reopenEnd(id: string, which: 'a' | 'b', old: HTMLElement, part?: 'role' | 'mult') { - const r = this.shell.querySelector('.vo-stage')!.getBoundingClientRect() - const fake = { clientX: r.left + parseFloat(old.style.left) - 8, clientY: r.top + parseFloat(old.style.top) - 8 } as MouseEvent - this.openEnd(id, which, fake, part) - } - - private endPop?: HTMLElement - - private closeEnd() { - this.endPop?.remove() - this.endPop = undefined - } - - /** - * Objektinspektor unter dem Diagramm. Ende A ist die Klasse, bei der die Linie beginnt (dort steht das Attribut), - * Ende B die Klasse, bei der sie endet. - */ - private renderInspector() { - if (this.diagramMode === 'object') return this.objectDiagram.renderInspector() - if (this.diagramMode === 'sequence') return this.renderSequenceInspector() - const note = this.findNote(this.noteSelection) - if (note) return this.renderNoteInspector(note) - const dep = this.relSelection?.startsWith('dep:') ? this.findDependency(this.relSelection) : undefined - if (dep) return this.renderDependency(dep) - const rel = this.relSelection ? this.findRelation(this.relSelection) : undefined - if (this.endPop && (!rel || !this.endPop.isConnected)) this.closeEnd() - const selectedClass = !rel && this.selection ? this.findClass(this.selection.cls) : undefined - if (selectedClass) return this.renderClassInspector(selectedClass) - if (!rel) { - delete this.inspector.dataset.rel - this.inspector.innerHTML = `

    ${e(t('web_visuobject_inspector_empty'))}

    ` - return - } - // Fokus nach dem Neuzeichnen wieder auf dasselbe Feld (z. B. nach Tab ins nächste). - const focused = this.inspector.contains(document.activeElement) ? ((document.activeElement as HTMLElement).dataset.field ?? (document.activeElement as HTMLElement).dataset.inspector) : undefined - this.inspector.dataset.rel = this.relSelection - const remove = `` - if (!('key' in rel)) { - const text = t(rel.kind === 'implements' ? 'web_visuobject_rel_implements_text' : 'web_visuobject_rel_extends_text', rel.from, rel.to) - this.inspector.innerHTML = `

    ${e(t(rel.kind === 'implements' ? 'web_visuobject_rel_implements' : 'web_visuobject_rel_extends'))}

    ${e(text)}

    ${remove}
    ` - } else { - const option = (value: string, label: string, current: string) => `` - const kinds = (['association', 'aggregation', 'composition'] as const).map((k) => option(k, t(`web_visuobject_rel_${k}`).replace(/\s*\(.*\)$/, ''), rel.kind)).join('') - const navs = (current: EndNav) => (['navigable', 'non-navigable', 'unspecified'] as const).map((v) => option(v, t(`web_visuobject_rel_nav_${v}`), current)).join('') - const [first, second] = rel.reading === 'backward' ? [rel.to, rel.from] : [rel.from, rel.to] - const reading = rel.name - ? `

    ${e(t('web_visuobject_rel_reading'))}: ${e(first)} ▶ ${e(second)}

    ` - : `

    ${e(t('web_visuobject_rel_reading_auto'))}

    ` - const list = `${multiplicities.map((m) => `` - const endFields = (title: string, roleKey: string, role: string | undefined, multKey: string, mult: string | undefined, roleHint: string, navKey: string, nav: EndNav) => ` -
    - ${e(title)} - - - - ${e(roleHint)} -
    ` - this.inspector.innerHTML = ` -
    - - - ${reading} - ${remove} -
    -
    - ${endFields(t('web_visuobject_rel_end_a', rel.from), 'sourceRole', rel.sourceRole, 'sourceMultiplicity', rel.sourceMultiplicity, rel.back ? t('web_visuobject_rel_role_field', rel.to, rel.back.field.name) : t('web_visuobject_rel_role_only_uml'), 'sourceEnd', rel.navFrom)} - ${endFields(t('web_visuobject_rel_end_b', rel.to), 'role', rel.role, 'multiplicity', rel.multiplicity, t('web_visuobject_rel_role_field', rel.from, rel.role), 'targetEnd', rel.navTo)} -
    - ${list}` - } - for (const el of this.inspector.querySelectorAll('[data-field]')) { - el.addEventListener('change', () => { - const current = this.relSelection && this.findRelation(this.relSelection) - if (current && 'key' in current) this.changeAssoc(current, el.dataset.field!, el.value) - }) - if (el instanceof HTMLInputElement) el.addEventListener('keydown', (ev) => ev.key === 'Enter' && el.blur()) - } - this.inspector.querySelector('[data-inspector=delete]')!.addEventListener('click', () => this.deleteRelation(this.relSelection!)) - if (focused) this.inspector.querySelector(`[data-field="${focused}"], [data-inspector="${focused}"]`)?.focus({ preventScroll: true }) - this.inspector.querySelector('[data-inspector=flip]')?.addEventListener('click', () => { - const current = this.relSelection && this.findRelation(this.relSelection) - if (current && 'key' in current) this.changeAssoc(current, 'reading', '') - }) - } - - /** Gewählter Reiter im Klassen-Inspektor. */ - private classTab: 'class' | 'fields' | 'methods' = 'class' - - /** - * Klassen-Inspektor wie in Eclipse/Astah mit drei Reitern: Klasse (Name, Package, Modifier, Oberklasse, - * Interfaces, Javadoc), Attribute und Methoden als Tabellen, dazu die Erzeuger aus „Source“. - * Jede Änderung schreibt sofort an die passende Stelle im Code. - */ - private renderClassInspector(cls: ProjectClass) { - const active = document.activeElement as HTMLElement | null - const focused = this.inspector.contains(active) ? (active?.dataset.field ? `${active.dataset.row ?? ''}|${active.dataset.field}` : undefined) : undefined - const text = this.project.files.find((f) => f.path === cls.path)?.text ?? '' - const mods = classModifiers(text, cls) - const isClass = cls.kind === 'class' - const option = (value: string, label: string, on: boolean) => `` - const visOptions = (v: Visibility, withPackage = true) => - (['+', '-', '#', '~'] as const) - .filter((x) => withPackage || x !== '~') - .map((x) => option(x, `${x} ${{ '+': 'public', '-': 'private', '#': 'protected', '~': 'package' }[x]}`, x === v)) - .join('') - const fields = cls.members.map((m, i) => ({ m, i })).filter((x) => x.m.kind === 'field') - const methods = cls.members.map((m, i) => ({ m, i })).filter((x) => x.m.kind !== 'field') - const tab = (id: typeof this.classTab, label: string) => `` - const types = `${['int', 'double', 'boolean', 'char', 'long', 'String', ...this.classes.map((c) => c.name), ...this.classes.map((c) => `ArrayList<${c.name}>`)].map((x) => `` - let body = '' - if (this.classTab === 'class') { - const supers = this.classes.filter((c) => c.kind === 'class' && c.name !== cls.name) - const ifaces = this.classes.filter((c) => c.kind === 'interface' && c.name !== cls.name) - const here = packageOf(cls.path) - const places = sourceFoldersOf(this.project).flatMap((root) => { - const list = packagesOf(this.project, root) - return list.includes(packageKey(root, '')) ? list : [...list, packageKey(root, '')] - }) - body = ` -
    - - - - ${isClass ? `` : ''} -
    -
    - ${isClass ? `` : ''} - ${ifaces.length ? `
    ${e(t(isClass ? 'web_visuobject_new_interfaces' : 'web_visuobject_new_extends_interfaces'))}${ifaces.map((c) => ``).join('')}
    ` : ''} -
    - -
    ${isClass ? `` : ''}
    ` - } else if (this.classTab === 'fields') { - const rows = fields - .map(({ m, i }) => { - const f = fieldInfo(text, m) - return ` - - - - - - - - ` - }) - .join('') - body = `${rows || ``}
    ${e(t('web_visuobject_insp_visibility'))}${e(t('web_visuobject_new_name'))}${e(t('web_visuobject_insp_type'))}staticfinal${e(t('web_visuobject_insp_init'))}
    ${e(t('web_visuobject_insp_no_fields'))}
    -
    ` - } else { - const canAbstract = isClass - const rows = methods - .map(({ m, i }) => { - const ctor = m.kind === 'constructor' - return ` - - - - - - - - ` - }) - .join('') - body = `${rows || ``}
    ${e(t('web_visuobject_insp_visibility'))}${e(t('web_visuobject_new_name'))}${e(t('web_visuobject_insp_params'))}${e(t('web_visuobject_insp_return'))}staticabstract
    ${e(t('web_visuobject_insp_no_methods'))}
    -
    - - ${isClass ? `${e(t('web_visuobject_insp_generate'))} - - - ` : ''} -
    ` - } - this.inspector.dataset.rel = `class:${cls.name}` - this.inspector.innerHTML = ` -
    -

    ${e(t(isClass ? 'web_visuobject_new_class' : 'web_visuobject_new_interface'))} ${e(cls.name)}

    -
    ${tab('class', t(isClass ? 'web_visuobject_new_class' : 'web_visuobject_new_interface'))}${tab('fields', `${t('web_visuobject_insp_fields')} (${fields.length})`)}${tab('methods', `${t('web_visuobject_insp_methods')} (${methods.length})`)}
    -
    - ${body}${types}` - const edit = (run: (source: string, c: UmlClass) => string, extra?: (p: JavaProject) => JavaProject) => { - this.message('') - let next = this.editClass(this.project, cls.name, run) - if (extra) next = extra(next) - this.setProject(next, cls.path) - } - const member = (c: UmlClass, el: HTMLElement) => c.members[Number(el.dataset.row)] - for (const b of this.inspector.querySelectorAll('[data-class-tab]')) - b.addEventListener('click', () => { - this.classTab = b.dataset.classTab as typeof this.classTab - this.renderInspector() - }) - for (const el of this.inspector.querySelectorAll('[data-field]')) { - el.addEventListener('change', () => { - const field = el.dataset.field! - const on = el instanceof HTMLInputElement && el.checked - const value = el.value.trim() - if (field === 'name') { - if (value && value !== cls.name) this.renameClassTo(cls, value) - } else if (field === 'package') { - const to = pathFor(el.value, fileNameOf(cls.path)) - if (to === cls.path) return - if (this.project.files.some((f) => f.path === to)) return this.message(t('web_visuobject_new_exists', fileNameOf(to))) - this.openTabs = this.openTabs.map((p) => (p === cls.path ? to : p)) - this.currentPackage = el.value - this.setProject(moveFile(this.project, cls.path, to), to) - } else if (field === 'visibility') edit((src, c) => setPublic(src, c, el.value === 'public')) - else if (field === 'abstract') - edit((src, c) => { - const base = on ? setFinal(src, c, false) : src - return setAbstract(base, parseClasses(base).find((x) => x.name === c.name) ?? c, on) - }) - else if (field === 'final') edit((src, c) => setFinal(src, c, on)) - else if (field === 'superclass') { - const sup = el.value || undefined - edit((src, c) => setSuperclass(src, c, sup), (p) => (sup ? this.importFor(p, cls.name, sup) : p)) - } else if (field === 'iface') { - const name = el.value - edit((src, c) => (on ? addInterface(src, c, name) : removeInterface(src, c, name)), (p) => (on ? this.importFor(p, cls.name, name) : p)) - } else if (field === 'doc') edit((src, c) => setClassDoc(src, c, el.value)) - else if (field.startsWith('f-')) { - if ((field === 'f-name' && !identifier.test(value)) || (field === 'f-type' && !value)) return this.message(t('web_visuobject_bad_name', value)) - const change: Partial = - field === 'f-vis' ? { visibility: el.value as Visibility } : field === 'f-name' ? { name: value } : field === 'f-type' ? { type: value } : field === 'f-static' ? { isStatic: on } : field === 'f-final' ? { isFinal: on } : { init: el.value } - const old = cls.members[Number(el.dataset.row)]?.name - edit( - (src, c) => setField(src, member(c, el), change), - (p) => (field === 'f-name' && old && p.uml?.[relationKey(cls.name, old)] ? { ...p, uml: renameInfo(p.uml, relationKey(cls.name, old), relationKey(cls.name, value)) } : p), - ) - } else if (field.startsWith('m-')) { - let change: Partial - if (field === 'm-params') { - const params = parseParams(el.value) - if (!params) return this.message(t('web_visuobject_insp_bad_params', el.value)) - change = { params } - } else if (field === 'm-name') { - if (!identifier.test(value)) return this.message(t('web_visuobject_bad_name', value)) - change = { name: value } - } else change = field === 'm-vis' ? { visibility: el.value as Visibility } : field === 'm-type' ? { type: value || 'void' } : field === 'm-static' ? { isStatic: on } : { isAbstract: on } - edit((src, c) => { - const out = setMethod(src, c, member(c, el), change) - // Abstrakte Methode in einer normalen Klasse: Die Klasse muss dann auch abstract sein. - if (field === 'm-abstract' && on && !c.isAbstract) { - const again = parseClasses(out).find((x) => x.name === c.name) - return again ? setAbstract(setFinal(out, again, false), parseClasses(setFinal(out, again, false)).find((x) => x.name === c.name)!, true) : out - } - return out - }) - } - }) - if (el instanceof HTMLInputElement && el.type === 'text') el.addEventListener('keydown', (ev) => ev.key === 'Enter' && el.blur()) - } - const on = (action: string, run: (b: HTMLElement) => void) => { - for (const b of this.inspector.querySelectorAll(`[data-inspector="${action}"]`)) b.addEventListener('click', () => run(b)) - } - on('delete-class', () => this.deleteClassFromProject(cls)) - on('sections', () => edit((src, c) => withSections(src, c, new Set(this.classes.map((x) => x.name)), this.sectionLabels()))) - on('delete-member', (b) => edit((src, c) => deleteMember(src, member(c, b)))) - on('add-field', () => edit((src, c) => addMember(src, c, `- ${this.freeMember(cls, 'attribut')}: int`))) - on('add-method', () => edit((src, c) => addMember(src, c, `+ ${this.freeMember(cls, 'methode')}(): void`))) - on('gen-constructor', () => edit((src, c) => addFieldConstructor(src, c))) - on('gen-accessors', () => edit((src, c) => addGettersSetters(src, c))) - on('gen-tostring', () => edit((src, c) => addToString(src, c))) - if (focused) { - const [row, field] = focused.split('|') - this.inspector.querySelector(row ? `[data-row="${row}"][data-field="${field}"]` : `[data-field="${field}"]`)?.focus({ preventScroll: true }) - } - } - - /** Freier Name für ein neues Mitglied: `attribut`, `attribut2` … */ - private freeMember(cls: ProjectClass, base: string): string { - let name = base - for (let n = 2; cls.members.some((m) => m.name === name); n++) name = base + n - return name - } - - /** Abhängigkeit im Inspektor: erklären, warum es keine Assoziation ist, und sie auf Wunsch zu einer machen. */ - private renderDependency(d: Dependency) { - this.closeEnd() - this.inspector.dataset.rel = this.relSelection - this.inspector.innerHTML = `
    -

    ${e(t('web_visuobject_dep_title', d.kind))}

    -

    ${e(t(`web_visuobject_dep_${d.kind}_text`, d.from, d.to, d.methods.join(', ')))}

    -
    -

    ${e(t('web_visuobject_dep_hint', d.from, d.to))}

    -
    ` - this.inspector.querySelector('[data-inspector=to-assoc]')!.addEventListener('click', () => { - const added = this.addAssociationField(this.project, d.from, d.to, false) - this.relSelection = `assoc:${relationKey(d.from, added.field)}` - this.setProject(added.project, this.findClass(d.from)?.path) - }) - } - - // MARK: - Werkzeugleiste - - private setTool(tool: Tool) { - this.tool = tool - this.pendingFrom = undefined - for (const el of this.canvas.querySelectorAll('.uml-class.source')) el.classList.remove('source') - for (const b of this.shell.querySelectorAll('.vo-tool')) b.setAttribute('aria-pressed', String(b.dataset.tool === tool)) - this.canvas.dataset.tool = tool - this.message(tool === 'select' ? '' : t(`web_visuobject_tool_${tool}_tip`), true) - } - - /** Werkzeug anklicken wählt es; Klasse oder Interface lässt sich auch direkt auf die Fläche ziehen. */ - private onToolDown(ev: PointerEvent, tool: Tool) { - if (ev.button !== 0) return - if (tool !== 'class' && tool !== 'interface') return this.setTool(this.tool === tool ? 'select' : tool) - ev.preventDefault() - const start = { x: ev.clientX, y: ev.clientY } - let ghost: HTMLElement | undefined - const move = (m: PointerEvent) => { - if (!ghost && Math.hypot(m.clientX - start.x, m.clientY - start.y) < 6) return - if (!ghost) { - ghost = document.createElement('div') - ghost.className = `vo-tool-ghost${tool === 'interface' ? ' interface' : ''}` - ghost.innerHTML = `${tool === 'interface' ? '«interface»' : ''}${e(t(tool === 'class' ? 'web_visuobject_new_class_name' : 'web_visuobject_new_interface_name'))}` - document.body.append(ghost) - } - ghost.style.left = `${m.clientX}px` - ghost.style.top = `${m.clientY}px` - } - const up = (m: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - if (!ghost) return this.setTool(this.tool === tool ? 'select' : tool) - ghost.remove() - const r = this.canvas.getBoundingClientRect() - if (m.clientX < r.left || m.clientX > r.right || m.clientY < r.top || m.clientY > r.bottom) return - this.setTool('select') - this.newClass(tool, { x: m.clientX - r.left + this.canvas.scrollLeft - 60, y: m.clientY - r.top + this.canvas.scrollTop - 14 }) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - /** Mit gewähltem Werkzeug auf die Fläche: Klasse anlegen oder Beziehung beginnen bzw. beenden. */ - private useTool(ev: PointerEvent) { - ev.preventDefault() - const tool = this.tool - const r = this.canvas.getBoundingClientRect() - const at = { x: ev.clientX - r.left + this.canvas.scrollLeft, y: ev.clientY - r.top + this.canvas.scrollTop } - if (tool === 'class' || tool === 'interface') { - this.setTool('select') - this.newClass(tool, { x: at.x - 60, y: at.y - 14 }) - return - } - if (tool === 'note') { - this.setTool('select') - this.addNote({ x: at.x - 20, y: at.y - 14 }) - return - } - const box = (ev.target as HTMLElement).closest('.uml-class') - if (!box) { - if (this.pendingFrom) this.setTool(tool) - return - } - const name = box.dataset.class! - if (this.pendingFrom && this.pendingFrom !== name) { - const from = this.pendingFrom - this.connectWith(from, name, tool as RelationTool) - return - } - this.startConnect(ev, name, tool as RelationTool) - } - - /** Beziehung mit dem Werkzeug anlegen, wenn sie zwischen diesen Klassen geht. */ - private connectWith(from: string, to: string, kind: RelationTool) { - const a = this.findClass(from) - const b = this.findClass(to) - this.setTool('select') - if (!a || !b) return - if (!relationChoices(a, b).includes(kind)) return this.message(t(`web_visuobject_tool_not_${kind}`, from, to)) - this.createRelation(from, to, kind) - } - - // MARK: - Beziehung ziehen - - /** Vom Griff am gewählten Kasten zu einer anderen Klasse ziehen, dann die Art wählen. */ - private startConnect(ev: PointerEvent, from: string, kind?: RelationTool) { - ev.preventDefault() - const ghost = document.createElementNS('http://www.w3.org/2000/svg', 'line') - ghost.setAttribute('class', 'vo-rel ghost') - this.lines.append(ghost) - const box = this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(from)}"]`)! - const local = (m: PointerEvent) => { - const r = this.canvas.getBoundingClientRect() - return { x: m.clientX - r.left + this.canvas.scrollLeft, y: m.clientY - r.top + this.canvas.scrollTop } - } - const start = { x: box.offsetLeft + box.offsetWidth, y: box.offsetTop + box.offsetHeight / 2 } - const targetAt = (m: PointerEvent) => { - const el = document.elementFromPoint(m.clientX, m.clientY)?.closest('.uml-class') - return el && el.dataset.class !== from ? el : undefined - } - const move = (m: PointerEvent) => { - const p = local(m) - ghost.setAttribute('x1', String(start.x)) - ghost.setAttribute('y1', String(start.y)) - ghost.setAttribute('x2', String(p.x)) - ghost.setAttribute('y2', String(p.y)) - for (const el of this.canvas.querySelectorAll('.uml-class.drop')) el.classList.remove('drop') - targetAt(m)?.classList.add('drop') - } - const up = (m: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - ghost.remove() - const target = targetAt(m) - target?.classList.remove('drop') - if (target && kind) this.connectWith(from, target.dataset.class!, kind) - else if (target) this.showRelationMenu(from, target.dataset.class!, local(m)) - else if (kind) { - // Nicht gezogen: jetzt die zweite Klasse anklicken. - this.pendingFrom = from - this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(from)}"]`)?.classList.add('source') - this.message(t('web_visuobject_tool_pick_second', from), true) - } - } - move(ev) - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - private showRelationMenu(from: string, to: string, at: Point) { - const a = this.findClass(from) - const b = this.findClass(to) - if (!a || !b) return - const choices = relationChoices(a, b).map((kind): [RelationTool, string] => [kind, kind === 'association' ? 'web_visuobject_rel_directed' : `web_visuobject_rel_${kind}`]) - if (choices.length === 0) return this.message(t('web_visuobject_rel_impossible', from, to)) - const menu = document.createElement('div') - menu.className = 'vo-relmenu' - menu.setAttribute('role', 'menu') - menu.style.left = `${at.x}px` - menu.style.top = `${at.y}px` - menu.innerHTML = `

    ${e(t('web_visuobject_rel_menu', from, to))}

    ` + choices.map(([kind, label]) => ``).join('') - this.canvas.append(menu) - const close = () => { - menu.remove() - document.removeEventListener('pointerdown', outside, true) - document.removeEventListener('keydown', escape, true) - } - const outside = (ev: Event) => { - if (!menu.contains(ev.target as Node)) close() - } - const escape = (ev: KeyboardEvent) => { - if (ev.key === 'Escape') { - ev.stopPropagation() - close() - } - } - menu.addEventListener('click', (ev) => { - const kind = (ev.target as HTMLElement).closest('[data-kind]')?.dataset.kind - if (!kind) return - close() - this.createRelation(from, to, kind as RelationTool) - }) - document.addEventListener('pointerdown', outside, true) - document.addEventListener('keydown', escape, true) - menu.querySelector('button')?.focus() - } -} - -/** `link`: Assoziation ohne Navigation (einfache Linie), `association`: gerichtet mit Pfeil. */ -type RelationTool = AssocKind | 'link' | 'extends' | 'implements' -type Tool = 'select' | 'class' | 'interface' | 'note' | RelationTool -const paletteTools: Tool[] = ['select', 'class', 'interface', 'note', 'link', 'association', 'aggregation', 'composition', 'extends', 'implements'] - -/** Welche Beziehungen von `a` nach `b` gehen: Attribute nur in Klassen, erben nur Klasse von Klasse bzw. Interface von Interface. */ -function relationChoices(a: UmlClass, b: UmlClass): RelationTool[] { - const out: RelationTool[] = [] - if (a.kind === 'class') out.push('link', 'association', 'aggregation', 'composition') - if (a.kind === b.kind && (a.kind === 'class' ? a.superclass !== b.name : !a.interfaces.includes(b.name))) out.push('extends') - if (a.kind === 'class' && b.kind === 'interface' && !a.interfaces.includes(b.name)) out.push('implements') - return out -} - -/** Symbol in der Werkzeugleiste. */ -function toolIcon(tool: Tool): string { - if (tool === 'select') - return '' - if (tool === 'class' || tool === 'interface') - return `` - if (tool === 'note') - return '' - return relationIcon(tool) -} - -/** Kleines Symbol im Menü: Linie mit offener Pfeilspitze, hohler oder gefüllter Raute, hohlem Dreieck. */ -function relationIcon(kind: string): string { - const line = (x1: number, x2: number, dashed = false) => `` - const shape = - kind === 'link' - ? line(1, 27) - : kind === 'association' - ? `${line(1, 27)}` - : kind === 'aggregation' || kind === 'composition' - ? `${line(13, 27)}` - : `${line(1, 18, kind === 'implements')}` - return `` -} - -/** Schnittpunkt der Strecke von der Mitte `inside` Richtung `toward` mit dem Rand des Rechtecks. */ -function edgePoint(r: { x: number; y: number; w: number; h: number }, inside: Point, toward: Point): Point { - const dx = toward.x - inside.x - const dy = toward.y - inside.y - const sx = dx === 0 ? Infinity : r.w / 2 / Math.abs(dx) - const sy = dy === 0 ? Infinity : r.h / 2 / Math.abs(dy) - const s = Math.min(sx, sy, 1) - return { x: Math.round(inside.x + dx * s), y: Math.round(inside.y + dy * s) } -} diff --git a/web/src/ui/objectDiagram.ts b/web/src/ui/objectDiagram.ts deleted file mode 100644 index 917ebaed..00000000 --- a/web/src/ui/objectDiagram.ts +++ /dev/null @@ -1,415 +0,0 @@ -/** - * VisuObject: Objektdiagramm wie in Astah. Objekte `s1 : Spieler` mit den Werten ihrer Attribute; - * ein Referenzattribut ist eine Linie zum Objekt, dessen Name im Wert steht. Selbst gezeichnet - * (gespeichert im Projekt) oder später live aus dem Debugger (`live`, nur lesen). - */ - -import type { ProjectClass, UmlObject } from '../core/javaProject' -import type { FrameView } from '../core/miniJava' -import { freeObjectName, isA, javaDefault, linkedValue, objectFields, refNames, type ObjectField } from '../core/objectModel' -import { t } from './i18n' -import { icons } from './icons' - -export type { Point } - -const e = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) -const identifier = /^[\p{L}_$][\p{L}\p{N}_$]*$/u - -type Point = { x: number; y: number } -type Rect = { x: number; y: number; w: number; h: number } - -export interface ObjectDiagramHost { - canvas: HTMLElement - lines: SVGSVGElement - inspector: HTMLElement - classes(): ProjectClass[] - objects(): UmlObject[] - setObjects(objects: UmlObject[]): void - message(text: string): void -} - -export interface LiveState { - objects: UmlObject[] - /** Geänderte Werte seit dem letzten Halt: `objektId.attribut`. */ - changed: Set - /** Aufrufstapel, oberste Methode zuerst; `frame` ist die gewählte Zeile. */ - frames: FrameView[] - frame: number - /** Geänderte Variablen: `Methode|Name`. */ - changedVars: Set - onFrame(index: number): void - move(id: string, p: Point): void -} - -export class ObjectDiagram { - selected?: string - /** Stand aus dem Debugger: wird gezeigt statt der gezeichneten Objekte; nur verschieben, nicht ändern. */ - live?: LiveState - private suppressClick = false - private lastClick = { id: '', time: 0 } - - constructor(private host: ObjectDiagramHost) { - host.canvas.addEventListener('pointerdown', (ev) => this.onPointerDown(ev)) - host.canvas.addEventListener('click', (ev) => this.onClick(ev)) - } - - private get objects(): UmlObject[] { - return this.live?.objects ?? this.host.objects() - } - - private find(id: string | undefined) { - return id ? this.objects.find((o) => o.id === id) : undefined - } - - private change(id: string, change: Partial) { - this.host.setObjects(this.host.objects().map((o) => (o.id === id ? { ...o, ...change } : o))) - } - - private fields(o: UmlObject): ObjectField[] { - return objectFields(this.host.classes(), o.cls) - } - - private valueOf(o: UmlObject, f: ObjectField): string { - return o.values[f.name] ?? javaDefault(f.type) - } - - // MARK: - Zeichnen - - render() { - const { canvas } = this.host - for (const old of canvas.querySelectorAll('.uml-object, .vo-empty')) old.remove() - if (this.objects.length === 0) { - const empty = document.createElement('p') - empty.className = 'vo-empty' - empty.textContent = t(this.host.classes().some((c) => c.kind === 'class' && !c.isAbstract) ? 'web_visuobject_obj_empty' : 'web_visuobject_obj_no_classes') - canvas.append(empty) - } - for (const o of this.objects) canvas.append(this.box(o)) - this.drawLinks() - this.renderInspector() - } - - private box(o: UmlObject): HTMLElement { - const box = document.createElement('div') - const selected = this.selected === o.id && !this.live - const known = this.host.classes().some((c) => c.name === o.cls) - box.className = `uml-class uml-object${selected ? ' selected' : ''}${known ? '' : ' unknown'}` - box.dataset.obj = o.id - box.style.left = `${o.x}px` - box.style.top = `${o.y}px` - const rows = this.fields(o) - .map((f) => { - const changed = this.live?.changed.has(`${o.id}.${f.name}`) - return `
  • ${e(f.name)} = ${e(this.valueOf(o, f))}
  • ` - }) - .join('') - box.innerHTML = `${selected ? `` : ''} -
    ${e(o.name)} : ${e(o.cls)}
    -
      ${rows}
    ` - return box - } - - /** Links: vom Objekt mit dem Referenzattribut zum Objekt, dessen Name im Wert steht; am Ziel der Attributname. */ - private drawLinks() { - const { canvas, lines } = this.host - const boxes = new Map() - for (const b of canvas.querySelectorAll('.uml-object')) boxes.set(b.dataset.obj!, b) - let width = 0 - let height = 0 - for (const b of boxes.values()) { - width = Math.max(width, b.offsetLeft + b.offsetWidth + 40) - height = Math.max(height, b.offsetTop + b.offsetHeight + 40) - } - lines.setAttribute('width', String(width)) - lines.setAttribute('height', String(height)) - const rect = (b: HTMLElement): Rect => ({ x: b.offsetLeft, y: b.offsetTop, w: b.offsetWidth, h: b.offsetHeight }) - const parts: string[] = [] - const pairs = new Map() - for (const o of this.objects) { - const from = boxes.get(o.id) - if (!from) continue - for (const f of this.fields(o)) { - if (!f.target) continue - for (const name of refNames(this.valueOf(o, f))) { - const target = this.objects.find((x) => x.name === name) - const to = target && boxes.get(target.id) - if (!to || to === from) continue - const key = [o.id, target.id].sort().join('|') - const n = pairs.get(key) ?? 0 - pairs.set(key, n + 1) - const ra = rect(from) - const rb = rect(to) - const ca = { x: ra.x + ra.w / 2, y: ra.y + ra.h / 2 } - const cb = { x: rb.x + rb.w / 2, y: rb.y + rb.h / 2 } - const len = Math.hypot(cb.x - ca.x, cb.y - ca.y) || 1 - const nx = (-(cb.y - ca.y) / len) * n * 14 - const ny = ((cb.x - ca.x) / len) * n * 14 - const a = edgePoint(ra, { x: ca.x + nx, y: ca.y + ny }, { x: cb.x + nx, y: cb.y + ny }) - const b = edgePoint(rb, { x: cb.x + nx, y: cb.y + ny }, { x: ca.x + nx, y: ca.y + ny }) - const ux = (a.x - b.x) / (Math.hypot(a.x - b.x, a.y - b.y) || 1) - const uy = (a.y - b.y) / (Math.hypot(a.x - b.x, a.y - b.y) || 1) - const label = { x: b.x + ux * 22 - uy * 10, y: b.y + uy * 22 + ux * 10 } - const changed = this.live?.changed.has(`${o.id}.${f.name}`) - parts.push(``) - parts.push(`${e(f.name)}`) - } - } - } - lines.innerHTML = parts.join('') - } - - // MARK: - Objektinspektor - - renderInspector() { - const { inspector } = this.host - if (this.live) return this.renderDebugger(this.live) - const o = this.find(this.selected) - if (!o) { - delete inspector.dataset.rel - inspector.innerHTML = `

    ${e(t('web_visuobject_obj_hint'))}

    ` - return - } - inspector.dataset.rel = `obj:${o.id}` - const classes = this.host.classes().filter((c) => c.kind === 'class') - const others = this.objects.filter((x) => x.id !== o.id) - const field = (f: ObjectField) => { - const value = this.valueOf(o, f) - if (f.target && !f.many) { - const choices = others.filter((x) => isA(this.host.classes(), x.cls, f.target!)) - return `` - } - return `` - } - const rows = this.fields(o) - .map((f) => `${e(f.name)} : ${e(f.type)}${field(f)}`) - .join('') - inspector.innerHTML = ` -
    - - -
    - - ${rows || ``}
    ${e(t('web_visuobject_obj_attribute'))}${e(t('web_visuobject_obj_value'))}
    ${e(t('web_visuobject_insp_no_fields'))}
    -
    ` - inspector.querySelector('[data-field=name]')!.addEventListener('change', (ev) => { - const name = (ev.target as HTMLInputElement).value.trim() - if (!identifier.test(name)) return this.host.message(t('web_visuobject_obj_bad_name', name)) - if (others.some((x) => x.name === name)) return this.host.message(t('web_visuobject_obj_name_taken', name)) - this.host.message('') - // Referenzen auf den alten Namen mitnehmen. - const old = o.name - this.host.setObjects( - this.host.objects().map((x) => { - const values = Object.fromEntries( - Object.entries(x.values).map(([k, v]) => [k, refNames(v).includes(old) ? v.replace(new RegExp(`(^|[\\s,\\[])${old}(?=$|[\\s,\\]])`, 'gu'), `$1${name}`) : v]), - ) - return x.id === o.id ? { ...x, name, values } : { ...x, values } - }), - ) - }) - inspector.querySelector('[data-field=cls]')!.addEventListener('change', (ev) => this.change(o.id, { cls: (ev.target as HTMLSelectElement).value })) - for (const el of inspector.querySelectorAll('[data-objval]')) - el.addEventListener('change', () => this.change(o.id, { values: { ...o.values, [el.dataset.objval!]: el.value.trim() } })) - inspector.querySelector('[data-obj-delete]')!.addEventListener('click', () => this.remove(o.id)) - } - - /** Beim Debuggen: Aufrufstapel und Variablen der gewählten Methode (wie in Eclipse). */ - private renderDebugger(live: LiveState) { - const { inspector } = this.host - delete inspector.dataset.rel - const frame = live.frames[live.frame] - const stack = live.frames - .map( - (f, i) => - `
  • `, - ) - .join('') - const vars = (frame?.vars ?? []) - .map( - (v) => - `${e(v.name)}${e(v.type)}${e(v.value)}`, - ) - .join('') - inspector.innerHTML = ` -
    -

    ${e(t('web_visuobject_dbg_stack'))}

      ${stack || `
    1. ${e(t('web_visuobject_obj_live_hint'))}
    2. `}
    -

    ${e(t('web_visuobject_dbg_vars'))}

    - - ${vars || ``}
    ${e(t('web_visuobject_dbg_var_name'))}${e(t('web_visuobject_dbg_var_type'))}${e(t('web_visuobject_obj_value'))}
    ${e(t('web_visuobject_dbg_no_vars'))}
    -
    -
    ` - for (const b of inspector.querySelectorAll('[data-frame]')) b.addEventListener('click', () => live.onFrame(Number(b.dataset.frame))) - } - - // MARK: - Bearbeiten - - /** Neues Objekt der Klasse an einer freien Stelle, Werte wie in Java nach `new`. */ - add(cls: string) { - const objects = this.host.objects() - const name = freeObjectName( - cls, - objects.map((o) => o.name), - ) - const taken = [...this.host.canvas.querySelectorAll('.uml-object')].map((b) => ({ x: b.offsetLeft, y: b.offsetTop, w: b.offsetWidth, h: b.offsetHeight })) - let spot: Point = { x: 24, y: 24 } - search: for (let y = 24; y < 4000; y += 40) - for (let x = 24; x < Math.max(400, this.host.canvas.clientWidth - 200); x += 40) - if (taken.every((r) => x + 200 + 40 <= r.x || r.x + r.w + 40 <= x || y + 110 + 30 <= r.y || r.y + r.h + 30 <= y)) { - spot = { x, y } - break search - } - const id = `o${Date.now().toString(36)}` - this.selected = id - this.host.setObjects([...objects, { id, name, cls, x: spot.x, y: spot.y, values: {} }]) - } - - remove(id: string) { - const gone = this.host.objects().find((o) => o.id === id) - if (!gone) return - this.selected = undefined - // Referenzen auf das gelöschte Objekt werden null bzw. fallen aus der Liste. - this.host.setObjects( - this.host - .objects() - .filter((o) => o.id !== id) - .map((o) => ({ - ...o, - values: Object.fromEntries( - Object.entries(o.values).map(([k, v]) => { - const names = refNames(v) - if (!names.includes(gone.name)) return [k, v] - return [k, /^\[/.test(v.trim()) ? `[${names.filter((n) => n !== gone.name).join(', ')}]` : 'null'] - }), - ), - })), - ) - } - - onKey(ev: KeyboardEvent): boolean { - if (this.live || !this.selected) return false - if (ev.key === 'Delete' || ev.key === 'Backspace') { - ev.preventDefault() - this.remove(this.selected) - return true - } - return false - } - - private select(id: string | undefined) { - this.selected = id - this.render() - this.host.canvas.focus({ preventScroll: true }) - } - - // MARK: - Maus - - private onPointerDown(ev: PointerEvent) { - if (ev.button !== 0) return - const target = ev.target as HTMLElement - const handle = target.closest('[data-obj-connect]') - const box = target.closest('.uml-object') - if (handle && box && !this.live) return this.startLink(ev, box) - if (box) this.drag(ev, box) - } - - private onClick(ev: MouseEvent) { - if (this.suppressClick) { - this.suppressClick = false - return - } - if (this.live) return - const box = (ev.target as HTMLElement).closest('.uml-object') - const id = box?.dataset.obj - const now = Date.now() - const twice = !!id && this.lastClick.id === id && now - this.lastClick.time < 450 - this.lastClick = { id: id ?? '', time: twice ? 0 : now } - this.select(id) - if (twice) this.host.inspector.querySelector('[data-field=name]')?.select() - } - - private drag(ev: PointerEvent, box: HTMLElement) { - const o = this.find(box.dataset.obj) - if (!o) return - const start = { x: ev.clientX, y: ev.clientY } - let moved = false - let p: Point = { x: o.x, y: o.y } - const move = (m: PointerEvent) => { - const dx = m.clientX - start.x - const dy = m.clientY - start.y - if (!moved && Math.hypot(dx, dy) < 4) return - moved = true - p = { x: Math.max(0, Math.round(o.x + dx)), y: Math.max(0, Math.round(o.y + dy)) } - box.style.left = `${p.x}px` - box.style.top = `${p.y}px` - this.drawLinks() - } - const up = () => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - if (!moved) return - this.suppressClick = true - if (this.live) { - Object.assign(o, p) - return this.live.move(o.id, p) - } - this.selected = o.id - this.change(o.id, p) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } - - /** Vom Griff zu einem anderen Objekt ziehen: passendes Referenzattribut bekommt dessen Namen. */ - private startLink(ev: PointerEvent, box: HTMLElement) { - ev.preventDefault() - const from = this.find(box.dataset.obj) - if (!from) return - const { canvas, lines } = this.host - const ghost = document.createElementNS('http://www.w3.org/2000/svg', 'line') - ghost.setAttribute('class', 'vo-rel ghost') - lines.append(ghost) - const start = { x: box.offsetLeft + box.offsetWidth, y: box.offsetTop + box.offsetHeight / 2 } - const targetAt = (m: PointerEvent) => { - const el = document.elementFromPoint(m.clientX, m.clientY)?.closest('.uml-object') - return el && el !== box ? el : undefined - } - const move = (m: PointerEvent) => { - const r = canvas.getBoundingClientRect() - ghost.setAttribute('x1', String(start.x)) - ghost.setAttribute('y1', String(start.y)) - ghost.setAttribute('x2', String(m.clientX - r.left + canvas.scrollLeft)) - ghost.setAttribute('y2', String(m.clientY - r.top + canvas.scrollTop)) - for (const el of canvas.querySelectorAll('.uml-object.drop')) el.classList.remove('drop') - targetAt(m)?.classList.add('drop') - } - const up = (m: PointerEvent) => { - window.removeEventListener('pointermove', move) - window.removeEventListener('pointerup', up) - ghost.remove() - const el = targetAt(m) - el?.classList.remove('drop') - this.suppressClick = true - const to = el && this.find(el.dataset.obj) - if (!to) return - const classes = this.host.classes() - const fits = this.fields(from).filter((f) => f.target && isA(classes, to.cls, f.target)) - // Erst ein freies Attribut nehmen (null), sonst eine Liste, sonst das erste passende. - const field = fits.find((f) => !f.many && refNames(this.valueOf(from, f)).length === 0) ?? fits.find((f) => f.many) ?? fits[0] - if (!field) return this.host.message(t('web_visuobject_obj_no_ref', from.cls, to.cls)) - this.host.message('') - this.change(from.id, { values: { ...from.values, [field.name]: linkedValue(field, this.valueOf(from, field), to.name) } }) - } - window.addEventListener('pointermove', move) - window.addEventListener('pointerup', up) - } -} - -/** Schnittpunkt der Strecke von `inside` (nahe der Mitte) Richtung `toward` mit dem Rand des Rechtecks. */ -function edgePoint(r: Rect, inside: Point, toward: Point): Point { - const dx = toward.x - inside.x - const dy = toward.y - inside.y - const sx = dx === 0 ? Infinity : r.w / 2 / Math.abs(dx) - const sy = dy === 0 ? Infinity : r.h / 2 / Math.abs(dy) - const s = Math.min(sx, sy, 1) - return { x: Math.round(inside.x + dx * s), y: Math.round(inside.y + dy * s) } -} diff --git a/web/src/ui/objectLayout.ts b/web/src/ui/objectLayout.ts deleted file mode 100644 index 4e8f7667..00000000 --- a/web/src/ui/objectLayout.ts +++ /dev/null @@ -1,136 +0,0 @@ -/** - * VisuObject: Aufteilung wie in WebGoatCode. Projekt, Java-Code und Klassendiagramm liegen flach - * nebeneinander; jeder Bereich klappt mit „–“ ein, übrig bleibt ein schmaler Streifen mit seinem Namen. - * Schieber ändern die Breite des Projekts und das Verhältnis von Code und Diagramm (Doppelklick = Standard). - * Am Handy liegen die Bereiche untereinander, dort gelten die Spalten nicht (siehe styles.css). - */ - -export type PaneId = 'explorer' | 'code' | 'diagram' - -const key = 'visuobject.layout' -/** Alter Schalter aus der ersten Fassung: `0` = Projekt eingeklappt. */ -const oldExplorerKey = 'visuobject.explorer' -const strip = '40px' -const defaults = { explorer: 260, split: 45 } - -const clamp = (n: number, min: number, max: number, fallback: number) => (Number.isFinite(n) ? Math.min(max, Math.max(min, Math.round(n))) : fallback) -const clampExplorer = (n: number) => clamp(n, 180, 480, defaults.explorer) -const clampSplit = (n: number) => clamp(n, 20, 80, defaults.split) - -export class ObjectLayout { - private collapsed = new Set() - /** Breite des Projekts in px. */ - private explorer = defaults.explorer - /** Anteil des Codes an Code + Diagramm in Prozent. */ - private split = defaults.split - - constructor( - private root: HTMLElement, - private changed: () => void, - ) { - try { - const saved = JSON.parse(localStorage.getItem(key) ?? 'null') as { collapsed?: PaneId[]; explorer?: number; split?: number } | null - if (saved) { - for (const id of saved.collapsed ?? []) if (id === 'explorer' || id === 'code' || id === 'diagram') this.collapsed.add(id) - this.explorer = clampExplorer(Number(saved.explorer ?? defaults.explorer)) - this.split = clampSplit(Number(saved.split ?? defaults.split)) - } else if (localStorage.getItem(oldExplorerKey) === '0') this.collapsed.add('explorer') - } catch { - /* ohne gespeicherte Aufteilung geht es auch */ - } - for (const b of root.querySelectorAll('[data-collapse]')) b.addEventListener('click', () => this.toggle(b.dataset.collapse as PaneId, true)) - for (const b of root.querySelectorAll('[data-expand]')) b.addEventListener('click', () => this.toggle(b.dataset.expand as PaneId, false)) - const explorerSplitter = root.querySelector('.vo-splitter[data-split=explorer]')! - const codeSplitter = root.querySelector('.vo-splitter[data-split=code]')! - this.splitter(explorerSplitter, { - at: (ev) => ev.clientX - root.getBoundingClientRect().left, - get: () => this.explorer, - set: (n) => (this.explorer = clampExplorer(n)), - reset: defaults.explorer, - step: 20, - }) - this.splitter(codeSplitter, { - at: (ev) => { - const code = root.querySelector('[data-pane=code]')!.getBoundingClientRect() - const diagram = root.querySelector('[data-pane=diagram]')!.getBoundingClientRect() - return ((ev.clientX - code.left) / (diagram.right - code.left)) * 100 - }, - get: () => this.split, - set: (n) => (this.split = clampSplit(n)), - reset: defaults.split, - step: 5, - }) - this.apply() - } - - isCollapsed(id: PaneId): boolean { - return this.collapsed.has(id) - } - - /** Bereich ein- oder ausklappen; der letzte offene Bereich bleibt offen. */ - toggle(id: PaneId, hide = !this.collapsed.has(id)) { - if (hide && !this.collapsed.has(id) && this.collapsed.size >= 2) return - if (hide) this.collapsed.add(id) - else this.collapsed.delete(id) - this.apply() - // Fokus dorthin, wo man weitermacht: aufgeklappt in den Bereich, eingeklappt auf den Streifen. - const pane = this.root.querySelector(`[data-pane=${id}]`)! - if (hide) pane.querySelector('.vo-strip')?.focus() - else pane.querySelector('[data-collapse]')?.focus() - } - - private apply() { - const c = this.collapsed - const bothRightGone = c.has('code') && c.has('diagram') - const explorer = c.has('explorer') ? strip : bothRightGone ? '1fr' : `${this.explorer}px` - const code = c.has('code') ? strip : `minmax(0, ${this.split}fr)` - const diagram = c.has('diagram') ? strip : `minmax(0, ${100 - this.split}fr)` - this.root.style.setProperty('--vo-cols', `${explorer} 1px ${code} 1px ${diagram}`) - for (const pane of this.root.querySelectorAll('[data-pane]')) pane.classList.toggle('collapsed', c.has(pane.dataset.pane as PaneId)) - const explorerSplitter = this.root.querySelector('.vo-splitter[data-split=explorer]')! - const codeSplitter = this.root.querySelector('.vo-splitter[data-split=code]')! - explorerSplitter.classList.toggle('fixed', c.has('explorer') || bothRightGone) - codeSplitter.classList.toggle('fixed', c.has('code') || c.has('diagram')) - explorerSplitter.setAttribute('aria-valuenow', String(this.explorer)) - codeSplitter.setAttribute('aria-valuenow', String(this.split)) - try { - localStorage.setItem(key, JSON.stringify({ collapsed: [...c], explorer: this.explorer, split: this.split })) - } catch { - /* nicht schlimm */ - } - this.changed() - } - - private splitter(el: HTMLElement, o: { at(ev: PointerEvent): number; get(): number; set(n: number): void; reset: number; step: number }) { - el.addEventListener('pointerdown', (ev) => { - if (el.classList.contains('fixed')) return - ev.preventDefault() - el.setPointerCapture(ev.pointerId) - this.root.classList.add('vo-resizing') - const move = (m: PointerEvent) => { - o.set(o.at(m)) - this.apply() - } - const up = () => { - this.root.classList.remove('vo-resizing') - el.removeEventListener('pointermove', move) - el.removeEventListener('pointerup', up) - el.removeEventListener('pointercancel', up) - } - el.addEventListener('pointermove', move) - el.addEventListener('pointerup', up) - el.addEventListener('pointercancel', up) - }) - el.addEventListener('dblclick', () => { - o.set(o.reset) - this.apply() - }) - el.addEventListener('keydown', (ev) => { - const d = ev.key === 'ArrowLeft' ? -o.step : ev.key === 'ArrowRight' ? o.step : 0 - if (!d) return - ev.preventDefault() - o.set(o.get() + d) - this.apply() - }) - } -} diff --git a/web/src/ui/objectNew.ts b/web/src/ui/objectNew.ts deleted file mode 100644 index 983341f8..00000000 --- a/web/src/ui/objectNew.ts +++ /dev/null @@ -1,243 +0,0 @@ -/** - * VisuObject: „Neu“ im Package Explorer wie in Eclipse (New Java Project, Source Folder, Package, - * Class, Interface). Jede Art hat ein kleines Fenster; für Klassen mit Modifier, abstract/final, - * Oberklasse, Interfaces und Methoden-Rümpfen. `classSource` schreibt daraus den Java-Code. - */ - -import type { ProjectClass } from '../core/javaProject' -import { t } from './i18n' - -export type NewKind = 'project' | 'folder' | 'package' | 'class' | 'interface' - -export interface NewClassOptions { - kind: 'class' | 'interface' - /** Java-Package ohne Source Folder; leer = Standard-Package. */ - pkg: string - name: string - isPublic: boolean - isAbstract: boolean - isFinal: boolean - superclass?: ProjectClass - interfaces: ProjectClass[] - main: boolean - constructors: boolean - inherited: boolean - comment: boolean - author: string -} - -const defaultValue = (type: string) => - type === 'boolean' ? 'false' : /^(int|long|short|byte|double|float)$/.test(type) ? '0' : type === 'char' ? "'\\0'" : 'null' - -const paramList = (params: { name: string; type: string }[]) => params.map((p) => `${p.type} ${p.name}`).join(', ') - -/** Abstrakte Methoden aus Oberklasse und Interfaces, die die neue Klasse schreiben muss (ohne doppelte). */ -function inheritedAbstract(o: NewClassOptions): { name: string; type: string; params: { name: string; type: string }[]; visibility: string }[] { - const out = new Map() - const add = (cls: ProjectClass, fromInterface: boolean) => { - for (const m of cls.members) - if (m.kind === 'method' && !m.isStatic && (fromInterface || m.isAbstract)) { - const sig = `${m.name}(${m.params.map((p) => p.type).join(',')})` - if (!out.has(sig)) out.set(sig, { name: m.name, type: m.type || 'void', params: m.params, visibility: fromInterface || m.visibility === '+' ? 'public' : m.visibility === '#' ? 'protected' : '' }) - } - } - if (o.superclass) add(o.superclass, false) - for (const i of o.interfaces) add(i, true) - return [...out.values()] -} - -/** Java-Datei der neuen Klasse bzw. des neuen Interfaces. */ -export function classSource(o: NewClassOptions): string { - const lines: string[] = [] - if (o.pkg) lines.push(`package ${o.pkg};`, '') - const imports = [o.superclass, ...o.interfaces].filter((c): c is ProjectClass => !!c && !!c.pkg && c.pkg !== o.pkg).map((c) => `import ${c.pkg}.${c.name};`) - if (imports.length) lines.push(...[...new Set(imports)].sort(), '') - if (o.comment) { - lines.push('/**', ` * ${o.name}`, ' *') - lines.push(` * @author ${o.author.trim() || '…'}`, ' */') - } - const mods = [o.isPublic ? 'public' : '', o.kind === 'class' && o.isAbstract ? 'abstract' : '', o.kind === 'class' && o.isFinal ? 'final' : ''].filter(Boolean).join(' ') - const names = o.interfaces.map((i) => i.name) - const head = - o.kind === 'class' - ? `${mods ? mods + ' ' : ''}class ${o.name}${o.superclass ? ` extends ${o.superclass.name}` : ''}${names.length ? ` implements ${names.join(', ')}` : ''}` - : `${mods ? mods + ' ' : ''}interface ${o.name}${names.length ? ` extends ${names.join(', ')}` : ''}` - lines.push(`${head} {`) - const body: string[][] = [] - if (o.kind === 'class') { - if (o.constructors && o.superclass) { - const ctors = o.superclass.members.filter((m) => m.kind === 'constructor') - for (const c of ctors.length ? ctors : [{ params: [] as { name: string; type: string }[] }]) - body.push([` public ${o.name}(${paramList(c.params)}) {`, ` super(${c.params.map((p) => p.name).join(', ')});`, ' }']) - } else if (o.constructors) body.push([` public ${o.name}() {`, ' }']) - if (o.inherited && !o.isAbstract) - for (const m of inheritedAbstract(o)) - body.push([' @Override', ` ${m.visibility ? m.visibility + ' ' : ''}${m.type} ${m.name}(${paramList(m.params)}) {`, ...(m.type === 'void' ? [] : [` return ${defaultValue(m.type)};`]), ' }']) - if (o.main) body.push([' public static void main(String[] args) {', ' }']) - } - if (body.length) lines.push('', body.map((b) => b.join('\n')).join('\n\n')) - lines.push('}', '') - return lines.join('\n') -} - -// MARK: - Fenster - -const esc = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) - -export interface WizardContext { - roots: string[] - /** Java-Packages (ohne Source Folder) je Source Folder, `''` = Standard-Package. */ - packagesIn(root: string): string[] - classes: ProjectClass[] - root: string - pkg: string - /** Fehlermeldung zum Namen oder `undefined`, wenn er passt. */ - check(kind: NewKind, name: string, root: string, pkg: string): string | undefined - suggest(kind: NewKind): string -} - -export interface WizardResult { - kind: NewKind - name: string - root: string - pkg: string - options?: NewClassOptions -} - -const authorKey = 'visuobject.author' -function storedAuthor(): string { - try { - return localStorage.getItem(authorKey) ?? '' - } catch { - return '' - } -} - -/** Fenster „Neu …“; `undefined` bei Abbrechen. Rückfragen nie mit `window.prompt` (in eingebetteten Ansichten gesperrt). */ -export function newWizard(kind: NewKind, ctx: WizardContext): Promise { - return new Promise((resolve) => { - const box = document.createElement('div') - box.className = 'confirm vo-wizard-back' - const isType = kind === 'class' || kind === 'interface' - const rootSelect = `` - const pkgSelect = `` - const others = ctx.classes - const superOptions = others.filter((c) => c.kind === 'class').map((c) => ``).join('') - const interfaces = others.filter((c) => c.kind === 'interface') - const interfaceBoxes = interfaces.length - ? `
    ${esc(t(kind === 'class' ? 'web_visuobject_new_interfaces' : 'web_visuobject_new_extends_interfaces'))}${interfaces.map((c) => ``).join('')}
    ` - : '' - const check = (name: string, label: string, on = false) => `` - const typeFields = isType - ? `${rootSelect}${pkgSelect} - -
    ${esc(t('web_visuobject_new_modifiers'))} - - ${kind === 'class' ? `${check('abstract', 'abstract')}${check('final', 'final')}` : ''} -
    - ${kind === 'class' ? `` : ''} - ${interfaceBoxes} - ${ - kind === 'class' - ? `
    ${esc(t('web_visuobject_new_stubs'))} - ${check('main', 'public static void main(String[] args)')} - ${check('constructors', esc(t('web_visuobject_new_stub_constructors')))} - ${check('inherited', esc(t('web_visuobject_new_stub_inherited')), true)} -
    ` - : '' - } -
    ${esc(t('web_visuobject_new_comment_legend'))} - ${check('comment', esc(t('web_visuobject_new_comment')))} - -
    ` - : `${kind === 'package' ? rootSelect : ''}` - box.innerHTML = `` - const form = box.querySelector('form')! - const field = (name: string) => form.querySelector(`[name="${name}"]`) - const nameInput = field('name')! - const rootInput = field('root') - const pkgInput = field('pkg') - const error = form.querySelector('.vo-wizard-error')! - nameInput.value = ctx.suggest(kind) - const fillPackages = () => { - if (!pkgInput) return - const pkgs = ctx.packagesIn(rootInput?.value ?? ctx.root) - const list = pkgs.includes('') ? pkgs : [...pkgs, ''] - const keep = pkgInput.value || ctx.pkg - pkgInput.innerHTML = list.map((p) => ``).join('') - } - fillPackages() - rootInput?.addEventListener('change', fillPackages) - // abstract und final schließen sich aus, wie in Eclipse. - const abstract = field('abstract') - const final = field('final') - abstract?.addEventListener('change', () => abstract.checked && final && (final.checked = false)) - final?.addEventListener('change', () => final.checked && abstract && (abstract.checked = false)) - const validate = () => { - const message = ctx.check(kind, nameInput.value.trim(), rootInput?.value ?? ctx.root, pkgInput?.value ?? ctx.pkg) - error.textContent = message ?? '' - return !message - } - nameInput.addEventListener('input', validate) - pkgInput?.addEventListener('change', validate) - const done = (value: WizardResult | undefined) => { - box.remove() - resolve(value) - } - form.addEventListener('submit', (ev) => { - ev.preventDefault() - if (!validate()) return nameInput.focus() - const name = nameInput.value.trim() - const root = rootInput?.value ?? ctx.root - const pkg = pkgInput?.value ?? '' - if (!isType) return done({ kind, name, root, pkg }) - const author = field('author')!.value - try { - localStorage.setItem(authorKey, author) - } catch { - /* nicht schlimm */ - } - const on = (n: string) => !!field(n)?.checked - const byName = (n: string) => ctx.classes.find((c) => c.name === n) - const superName = field('superclass')?.value - done({ - kind, - name, - root, - pkg, - options: { - kind, - pkg, - name, - isPublic: field('visibility')!.value === 'public', - isAbstract: on('abstract'), - isFinal: on('final'), - superclass: superName ? byName(superName) : undefined, - interfaces: [...form.querySelectorAll('[name=iface]:checked')].map((i) => byName(i.value)).filter((c): c is ProjectClass => !!c), - main: on('main'), - constructors: on('constructors'), - inherited: on('inherited'), - comment: on('comment'), - author, - }, - }) - }) - box.addEventListener('click', (ev) => { - const target = ev.target as HTMLElement - if (target === box || target.closest('[data-cancel]')) done(undefined) - }) - box.addEventListener('keydown', (ev) => { - ev.stopPropagation() - if (ev.key === 'Escape') done(undefined) - }) - document.body.append(box) - nameInput.focus() - nameInput.select() - }) -} diff --git a/web/src/ui/objectWorkspace.ts b/web/src/ui/objectWorkspace.ts deleted file mode 100644 index 7f12ccad..00000000 --- a/web/src/ui/objectWorkspace.ts +++ /dev/null @@ -1,181 +0,0 @@ -/** - * VisuObject-Workspace wie in Eclipse: ein Ordner, darin ein Ordner je Projekt mit - * `src/paket/Klasse.java`, daneben `visuobject.json` (UML-Zusatz und Lage der Kästen) - * sowie `.project` und `.classpath`, damit Eclipse das Projekt direkt importieren kann. - * Beim Lesen zählt der Ordner unter dem Source Folder als Package, wie in Eclipse. - * Welche Ordner Source Folders sind (`src`, `test` …), steht in `.classpath`. - */ - -import { defaultRoot, isFolderName, notesFromJSON, objectsFromJSON, packageKey, packageOf, packagesOf, pathFor, rootOf, sourceFoldersOf, umlFromJSON, type JavaProject } from '../core/javaProject' -import type { WsDir } from './workspaceStore' - -export const workspaceFolderName = 'VisuObject-Workspace' -const umlFile = 'visuobject.json' - -export type Positions = Record - -export const safeFolderName = (name: string) => name.replace(/[\\/:*?"<>|]/g, '_').trim() || 'Projekt' - -async function tryDir(dir: WsDir, name: string): Promise { - try { - return await dir.dir(name) - } catch { - return undefined - } -} - -/** Projektordner im Workspace: alle Ordner mit `src` oder `visuobject.json`, sortiert. */ -export async function listProjects(root: WsDir): Promise { - const out: string[] = [] - for (const entry of await root.list()) { - if (entry.kind !== 'directory') continue - const dir = await root.dir(entry.name) - if ((await dir.has('src')) || (await dir.has(umlFile))) out.push(entry.name) - } - return out.sort((a, b) => a.localeCompare(b)) -} - -/** Source Folders aus `.classpath` (``); ohne Datei nur `src`. */ -export function classpathRoots(xml: string): string[] { - const roots = [...xml.matchAll(/]*>/g)] - .map((m) => m[0]) - .filter((tag) => /\bkind\s*=\s*"src"/.test(tag) && !/\bcombineaccessrules\b/.test(tag)) - .map((tag) => /\bpath\s*=\s*"([^"]+)"/.exec(tag)?.[1] ?? '') - .filter((p) => p && !p.startsWith('/') && !p.includes(':') && p.split('/').every(isFolderName)) - return roots.length ? [...new Set(roots)] : [defaultRoot] -} - -/** Source-Einträge in `.classpath` ersetzen, alles andere (z. B. Bibliotheken aus Eclipse) bleibt. */ -export function withClasspathRoots(xml: string, roots: string[]): string { - const entries = roots.map((r) => `\t\n`).join('') - const rest = xml.replace(/[ \t]*]*\bkind\s*=\s*"src"[^>]*\/>[ \t]*\n?/g, (tag) => (/\bcombineaccessrules\b/.test(tag) ? tag : '')) - return rest.replace(/(]*>[ \t]*\n?)/, `$1${entries}`) -} - -export async function readProject(root: WsDir, name: string): Promise<{ project: JavaProject; positions: Positions }> { - const dir = await root.dir(name) - const project: JavaProject = { name, files: [], packages: [] } - const roots = (await dir.has('.classpath')) ? classpathRoots(await dir.read('.classpath')) : [defaultRoot] - const walk = async (d: WsDir, sourceFolder: string, pkg: string) => { - const entries = await d.list() - const key = packageKey(sourceFolder, pkg) - if (pkg && !entries.some((x) => x.kind === 'file' && /\.java$/i.test(x.name))) project.packages.push(key) - for (const entry of entries) { - if (entry.kind === 'directory') await walk(await d.dir(entry.name), sourceFolder, pkg ? `${pkg}.${entry.name}` : entry.name) - else if (/\.java$/i.test(entry.name)) project.files.push({ path: pathFor(key, entry.name), text: await d.read(entry.name) }) - } - } - for (const r of roots) { - let d: WsDir | undefined = dir - for (const part of r.split('/')) d = d && (await tryDir(d, part)) - if (d) await walk(d, r, '') - } - const extra = roots.filter((r) => r !== defaultRoot) - if (extra.length) project.sourceFolders = extra - // Ein Ordner mit nur Unterordnern (z. B. `de` über `de.tiere`) ist kein eigenes Package. - project.packages = project.packages.filter((p) => !project.packages.some((q) => q.startsWith(p + '.')) && !project.files.some((f) => packageOf(f.path).startsWith(p + '.'))) - project.files.sort((a, b) => a.path.localeCompare(b.path)) - let positions: Positions = {} - if (await dir.has(umlFile)) { - try { - const data = JSON.parse(await dir.read(umlFile)) as { uml?: unknown; positions?: unknown; notes?: unknown; objects?: unknown } - const uml = umlFromJSON(data.uml) - if (uml) project.uml = uml - const notes = notesFromJSON(data.notes) - if (notes) project.notes = notes - const objects = objectsFromJSON(data.objects) - if (objects) project.objects = objects - if (data.positions && typeof data.positions === 'object') { - for (const [cls, p] of Object.entries(data.positions as Record)) - if (typeof p?.x === 'number' && typeof p?.y === 'number') positions[cls] = { x: p.x, y: p.y } - } - } catch { - positions = {} - } - } - return { project, positions } -} - -const eclipseProject = (name: string) => ` - - ${name.replace(/[&<>]/g, (c) => ({ '&': '&', '<': '<', '>': '>' })[c]!)} - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - -` - -const eclipseClasspath = ` - - - - -` - -/** - * Projekt in seinen Ordner schreiben. Dateien und Packages, die es nicht mehr gibt, verschwinden; - * alles andere im Ordner (z. B. `bin` von Eclipse) bleibt. - */ -export async function writeProject(root: WsDir, folder: string, project: JavaProject, positions: Positions): Promise { - const dir = await root.dir(folder, true) - const roots = sourceFoldersOf(project) - const oldClasspath = (await dir.has('.classpath')) ? await dir.read('.classpath') : undefined - // Source Folders, die es nicht mehr gibt, verschwinden samt Inhalt (nur solche, die `.classpath` kannte). - for (const old of oldClasspath ? classpathRoots(oldClasspath) : []) - if (!roots.includes(old) && !old.includes('/') && (await dir.has(old))) await dir.remove(old) - for (const sourceFolder of roots) { - let src = dir - for (const part of sourceFolder.split('/')) src = await src.dir(part, true) - const files = project.files.filter((f) => rootOf(f.path) === sourceFolder) - const wanted = new Set(files.map((f) => f.path.slice(f.path.indexOf(':') + 1))) - const keepDirs = new Set() - for (const key of packagesOf(project, sourceFolder)) { - const pkg = key.slice(key.indexOf(':') + 1) - if (!pkg) continue - const parts = pkg.split('.') - for (let i = 1; i <= parts.length; i++) keepDirs.add(parts.slice(0, i).join('/')) - } - // Erst aufräumen, dann schreiben: So bleibt keine alte Fassung einer verschobenen Datei liegen. - const clean = async (d: WsDir, path: string) => { - for (const entry of await d.list()) { - const full = path ? `${path}/${entry.name}` : entry.name - if (entry.kind === 'directory') { - if (keepDirs.has(full)) await clean(await d.dir(entry.name), full) - else if (!roots.some((r) => `${sourceFolder}/${full}` === r || r.startsWith(`${sourceFolder}/${full}/`))) await d.remove(entry.name) - } else if (/\.java$/i.test(entry.name) && !wanted.has(full)) await d.remove(entry.name) - } - } - await clean(src, '') - for (const key of packagesOf(project, sourceFolder)) { - const pkg = key.slice(key.indexOf(':') + 1) - let d = src - for (const part of pkg ? pkg.split('.') : []) d = await d.dir(part, true) - for (const f of files.filter((x) => packageOf(x.path) === key)) { - const name = f.path.slice(f.path.lastIndexOf('/') + 1).slice(f.path.includes('/') ? 0 : f.path.indexOf(':') + 1) - if (!(await d.has(name)) || (await d.read(name)) !== f.text) await d.write(name, f.text) - } - } - } - await dir.write(umlFile, JSON.stringify({ format: 'visuobject', version: 1, uml: project.uml ?? {}, ...(project.notes?.length ? { notes: project.notes } : {}), ...(project.objects?.length ? { objects: project.objects } : {}), positions }, null, 2) + '\n') - if (!(await dir.has('.project'))) await dir.write('.project', eclipseProject(project.name)) - const classpath = withClasspathRoots(oldClasspath ?? eclipseClasspath, roots) - if (classpath !== oldClasspath) await dir.write('.classpath', classpath) -} - -/** Freier Projektname im Workspace: `MeinProjekt`, `MeinProjekt2` … */ -export function freeProjectName(base: string, taken: string[]): string { - const lower = new Set(taken.map((n) => n.toLowerCase())) - let name = base - for (let n = 2; lower.has(name.toLowerCase()); n++) name = base + n - return name -} diff --git a/web/src/ui/papView.ts b/web/src/ui/papView.ts deleted file mode 100644 index 70b9fe8a..00000000 --- a/web/src/ui/papView.ts +++ /dev/null @@ -1,359 +0,0 @@ -import { inline } from '../learn/markdown' -import { papFits, papLayout, papLines, type Pap, type PapBox, type PapShape } from '../learn/pap' -import { t } from './i18n' - -/** - * PAP bauen im Lernbereich: Der Plan steht mit leeren Plätzen da, darunter liegen die fertigen Elemente gemischt. - * Element antippen und dann den Platz (oder Element auf den Platz ziehen). Ein belegter Platz gibt sein Element beim - * Antippen zurück. - */ - -const SVG = 'http://www.w3.org/2000/svg' -let papCount = 0 -const FILL: Record = { oval: '#d1f2d9', rechteck: '#e5f0ff', raute: '#fff4d6', parallelogramm: '#f2e6ff' } - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function s(tag: string, attrs: Record = {}): SVGElement { - const e = document.createElementNS(SVG, tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, String(v)) - return e -} - -function shapeEl(shape: PapShape, b: PapBox): SVGElement { - const l = b.x - b.w / 2 - const r = b.x + b.w / 2 - const t0 = b.y - b.h / 2 - const t1 = b.y + b.h / 2 - if (shape === 'raute') return s('path', { d: `M${b.x} ${t0} L${r} ${b.y} L${b.x} ${t1} L${l} ${b.y} Z` }) - if (shape === 'parallelogramm') return s('path', { d: `M${l + 10} ${t0} L${r} ${t0} L${r - 10} ${t1} L${l} ${t1} Z` }) - return s('rect', { x: l, y: t0, width: b.w, height: b.h, rx: shape === 'oval' ? b.h / 2 : 0 }) -} - -function textEl(text: string, b: PapBox, shape: PapShape): SVGElement { - const lines = papLines(text, b.w - (shape === 'raute' ? 50 : 14)) - const g = s('text', { 'text-anchor': 'middle', 'font-size': lines.length > 1 ? 12 : 13 }) - lines.forEach((line, i) => { - const span = s('tspan', { x: b.x, y: b.y + 4.5 + (i - (lines.length - 1) / 2) * 14 }) - span.textContent = line - g.append(span) - }) - return g -} - -function arrow(points: [number, number][], id: string): SVGElement { - return s('polyline', { points: points.map((p) => p.join(',')).join(' '), fill: 'none', 'marker-end': `url(#${id})` }) -} - -function label(text: string, x: number, y: number, yes: boolean): SVGElement { - const e = s('text', { x, y, 'font-size': 12, 'font-weight': 600, fill: yes ? '#248a3d' : '#d70015' }) - e.textContent = text - return e -} - -/** Element als kleines Bild für die Ablage unten. */ -function cardSVG(shape: PapShape, text: string): SVGElement { - const lines = papLines(text, 110) - const long = Math.max(...lines.map((l) => l.length)) - const w = Math.max(70, long * 6.6 + (shape === 'raute' ? 56 : 26)) - const hgt = shape === 'raute' ? 48 : lines.length > 1 ? 40 : 30 - const svg = s('svg', { viewBox: `0 0 ${w + 4} ${hgt + 4}`, width: w + 4, height: hgt + 4, 'aria-hidden': 'true' }) - const b = { x: (w + 4) / 2, y: (hgt + 4) / 2, w, h: hgt } - const shapeE = shapeEl(shape, b) - shapeE.setAttribute('fill', FILL[shape]) - shapeE.setAttribute('stroke', '#1c1c1e') - shapeE.setAttribute('stroke-width', '1.5') - svg.append(shapeE, textEl(text, { ...b, w: lines.length > 1 ? 124 : w }, shape)) - return svg -} - -function shuffled(n: number): number[] { - const order = [...Array(n).keys()] - for (let i = n - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)) - ;[order[i], order[j]] = [order[j], order[i]] - } - // Nie schon in der richtigen Reihenfolge anbieten. - if (n > 1 && order.every((v, i) => v === i)) order.reverse() - return order -} - -/** Legende: Symbol und Bedeutung für alle vier Formen. */ -function legend(): HTMLElement { - const list = h('dl', { class: 'learn-pap-legend' }) - const items: [PapShape, string][] = [ - ['oval', 'web_learn_pap_legend_oval'], - ['rechteck', 'web_learn_pap_legend_rect'], - ['raute', 'web_learn_pap_legend_diamond'], - ['parallelogramm', 'web_learn_pap_legend_para'], - ] - for (const [shape, key] of items) { - const w = shape === 'raute' ? 44 : 40 - const svg = s('svg', { viewBox: `0 0 ${w + 4} 28`, width: w + 4, height: 28, 'aria-hidden': 'true' }) - const el = shapeEl(shape, { x: (w + 4) / 2, y: 14, w, h: shape === 'raute' ? 24 : 18 }) - el.setAttribute('fill', FILL[shape]) - el.setAttribute('stroke', '#1c1c1e') - el.setAttribute('stroke-width', '1.5') - svg.append(el) - list.append(h('dt', {}, svg), h('dd', {}, t(key))) - } - return list -} - -export function renderPap(pap: Pap): HTMLElement { - const box = h('section', { class: 'learn-pap' }) - box.append(h('strong', { class: 'learn-pap-title' }, pap.title)) - if (pap.task) { - const p = h('p') - p.innerHTML = inline(pap.task) - box.append(p) - } - box.append(h('p', { class: 'learn-pap-hint' }, t('web_learn_pap_hint'))) - - const { boxes, height } = papLayout(pap) - // Nur so breit wie der Inhalt, damit der Plan in der schmalen Lernspalte möglichst groß wird. - const shown = boxes.filter(Boolean) - const minX = Math.min(...shown.map((b) => b.x - b.w / 2)) - 4 - const hasRaute = pap.nodes.some((n) => n.shape === 'raute') - const maxX = Math.max(...shown.map((b) => b.x + b.w / 2), hasRaute ? Math.max(...shown.map((b) => b.x)) + 85 + 40 : 0) + 4 - const svg = s('svg', { viewBox: `${minX} 0 ${maxX - minX} ${height}`, class: 'learn-pap-plan', role: 'group', 'aria-label': pap.title }) - const arrowId = `pap-arrow-${++papCount}` - svg.innerHTML = - `` - const lines = s('g', { stroke: '#1c1c1e', 'stroke-width': 2 }) - const labels = s('g') - const yes = t('web_learn_pap_yes') - const no = t('web_learn_pap_no') - const top = (i: number) => boxes[i].y - boxes[i].h / 2 - const bottom = (i: number) => boxes[i].y + boxes[i].h / 2 - const main = pap.nodes.map((_, i) => i).filter((i) => !pap.nodes[i].isSide) - main.forEach((i, k) => { - const n = pap.nodes[i] - const b = boxes[i] - const next = main[k + 1] - if (next !== undefined) { - lines.append(arrow([[b.x, bottom(i)], [b.x, top(next) - 2]], arrowId)) - if (n.shape === 'raute') labels.append(label(n.sideExit === 'nein' ? yes : no, b.x + 6, bottom(i) + 13, n.sideExit === 'nein')) - } - if (n.shape !== 'raute') return - const right = b.x + b.w / 2 - labels.append(label(n.sideExit === 'ja' ? yes : no, right + 5, b.y - 6, n.sideExit === 'ja')) - if (n.backTo !== undefined) { - const y = top(n.backTo) - 14 - lines.append(arrow([[right, b.y], [right + 22, b.y], [right + 22, y], [b.x, y]], arrowId)) - } else if (n.side !== undefined) { - const sb = boxes[n.side] - const y = top(i) - 14 - lines.append(arrow([[right, b.y], [sb.x - sb.w / 2 - 2, b.y]], arrowId)) - lines.append(arrow([[sb.x, sb.y - sb.h / 2], [sb.x, y], [b.x, y]], arrowId)) - } - }) - svg.append(lines, labels) - - const placed: (number | undefined)[] = pap.nodes.map(() => undefined) - const slots: SVGElement[] = [] - const cards: HTMLButtonElement[] = [] - let selected: number | undefined - - const status = h('p', { class: 'learn-pap-status', role: 'status' }) - const explanation = pap.explanation ? h('p', { class: 'learn-explanation' }) : undefined - if (explanation) { - explanation.innerHTML = inline(pap.explanation!) - explanation.hidden = true - } - let locked = false - - const clearMarks = () => { - slots.forEach((g) => g.classList.remove('right', 'wrong')) - status.textContent = '' - status.className = 'learn-pap-status' - } - - const drawSlot = (i: number) => { - const g = slots[i] - g.replaceChildren() - const n = pap.nodes[i] - const card = placed[i] - const shape = shapeEl(n.shape, boxes[i]) - shape.setAttribute('class', 'learn-pap-shape') - if (card === undefined) { - shape.setAttribute('fill', '#f7f7f9') - shape.setAttribute('stroke-dasharray', '6 4') - g.append(shape) - g.setAttribute('aria-label', t('web_learn_pap_empty')) - } else { - const c = pap.nodes[card] - shape.setAttribute('fill', FILL[c.shape]) - g.append(shape, textEl(c.text, boxes[i], n.shape)) - g.setAttribute('aria-label', c.text) - } - } - - const refresh = () => { - pap.nodes.forEach((_, i) => drawSlot(i)) - cards.forEach((c, i) => { - c.hidden = placed.includes(i) - c.classList.toggle('selected', selected === i) - }) - } - - const place = (slot: number, card: number) => { - if (locked) return - const before = placed.indexOf(card) - if (before >= 0) placed[before] = undefined - placed[slot] = card - selected = undefined - clearMarks() - refresh() - } - - pap.nodes.forEach((_, i) => { - const g = s('g', { class: 'learn-pap-slot', 'data-slot': i, tabindex: 0, role: 'button' }) - const activate = () => { - if (locked) return - if (selected !== undefined) place(i, selected) - else if (placed[i] !== undefined) { - placed[i] = undefined - clearMarks() - refresh() - } - } - g.addEventListener('click', activate) - g.addEventListener('keydown', (e) => { - if ((e as KeyboardEvent).key !== 'Enter' && (e as KeyboardEvent).key !== ' ') return - e.preventDefault() - activate() - }) - slots.push(g) - svg.append(g) - }) - - const tray = h('div', { class: 'learn-pap-tray' }) - let dragged = false - for (const i of shuffled(pap.nodes.length)) { - const n = pap.nodes[i] - const card = h('button', { class: 'learn-pap-card', type: 'button', 'aria-label': n.text }, cardSVG(n.shape, n.text)) as HTMLButtonElement - cards[i] = card - card.addEventListener('click', () => { - if (dragged || locked) return - selected = selected === i ? undefined : i - refresh() - }) - // Ziehen mit Maus, Stift oder Finger. - card.addEventListener('pointerdown', (down) => { - if (locked) return - dragged = false - let ghost: HTMLElement | undefined - const move = (e: PointerEvent) => { - if (!ghost && Math.hypot(e.clientX - down.clientX, e.clientY - down.clientY) < 6) return - if (!ghost) { - dragged = true - ghost = card.cloneNode(true) as HTMLElement - ghost.classList.add('learn-pap-ghost') - document.body.append(ghost) - card.classList.add('dragging') - } - ghost.style.left = `${e.clientX}px` - ghost.style.top = `${e.clientY}px` - slots.forEach((g) => g.classList.remove('over')) - target(e)?.classList.add('over') - } - const target = (e: PointerEvent) => { - if (ghost) ghost.style.display = 'none' - const el = document.elementFromPoint(e.clientX, e.clientY)?.closest('[data-slot]') as SVGElement | null - if (ghost) ghost.style.display = '' - return el && slots.includes(el) ? el : undefined - } - const up = (e: PointerEvent) => { - card.removeEventListener('pointermove', move) - card.removeEventListener('pointerup', up) - card.removeEventListener('pointercancel', up) - slots.forEach((g) => g.classList.remove('over')) - card.classList.remove('dragging') - if (!ghost) return - const slot = e.type === 'pointerup' ? target(e) : undefined - ghost.remove() - if (slot) place(Number(slot.getAttribute('data-slot')), i) - // Der Klick nach dem Loslassen soll nichts auswählen. - setTimeout(() => (dragged = false)) - } - card.setPointerCapture(down.pointerId) - card.addEventListener('pointermove', move) - card.addEventListener('pointerup', up) - card.addEventListener('pointercancel', up) - }) - tray.append(card) - } - - const check = h('button', { class: 'learn-pap-check' }, t('web_learn_check')) - const solution = h('button', {}, t('web_learn_cloze_solution')) - const again = h('button', {}, t('web_learn_cloze_again')) - solution.hidden = true - again.hidden = true - - const finish = () => { - locked = true - check.hidden = true - solution.hidden = true - again.hidden = false - tray.hidden = true - work.classList.add('done') - if (explanation) explanation.hidden = false - } - - check.addEventListener('click', () => { - const missing = placed.filter((p) => p === undefined).length - let right = 0 - placed.forEach((card, i) => { - const ok = card !== undefined && papFits(pap, i, card) - slots[i].classList.toggle('right', ok) - slots[i].classList.toggle('wrong', card !== undefined && !ok) - if (ok) right++ - }) - const all = right === pap.nodes.length - status.textContent = all - ? t('web_learn_pap_all_right') - : missing - ? t('web_learn_pap_missing_fmt', missing) - : t('web_learn_pap_score_fmt', right, pap.nodes.length) - status.className = `learn-pap-status ${all ? 'right' : 'wrong'}` - if (all) finish() - else if (!missing) solution.hidden = false - }) - solution.addEventListener('click', () => { - pap.nodes.forEach((_, i) => (placed[i] = i)) - selected = undefined - refresh() - clearMarks() - slots.forEach((g) => g.classList.add('shown')) - finish() - }) - again.addEventListener('click', () => { - pap.nodes.forEach((_, i) => (placed[i] = undefined)) - locked = false - selected = undefined - slots.forEach((g) => g.classList.remove('shown')) - clearMarks() - refresh() - check.hidden = false - again.hidden = true - tray.hidden = false - work.classList.remove('done') - if (explanation) explanation.hidden = true - }) - - refresh() - // Plan links, Bausteine rechts daneben: so sieht man am Laptop beides zugleich. - const plan = h('div', { class: 'learn-pap-left' }) - plan.append(svg) - if (pap.legend) plan.append(legend()) - const work = h('div', { class: 'learn-pap-work' }, plan, tray) - box.append(work, h('div', { class: 'learn-pap-actions' }, check, solution, again), status) - if (explanation) box.append(explanation) - return box -} diff --git a/web/src/ui/projectZip.ts b/web/src/ui/projectZip.ts deleted file mode 100644 index bfc8283b..00000000 --- a/web/src/ui/projectZip.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { strFromU8, strToU8, unzipSync, zipSync } from 'fflate' -import { barePackage, declaredPackage, defaultRoot, diskPath, isFolderName, packageKey, pathFor, notesFromJSON, objectsFromJSON, projectFromFiles, rootOf, sourceFoldersOf, umlFromJSON, type JavaProject } from '../core/javaProject' - -/** - * VisuObject-Projekt als ZIP wie in Eclipse oder BlueJ: `Name/src/paket/Klasse.java`. - * Beim Öffnen zählt die Zeile `package …;` in jeder Datei, nicht der Ordner im ZIP. - * Was nur UML kennt (Aggregation, Name der Beziehung …) liegt daneben in `Name/visuobject.json`. - */ - -const umlFile = 'visuobject.json' - -export function projectToZip(project: JavaProject): Uint8Array { - const folder = project.name.replace(/[\\/:*?"<>|]/g, '_') || 'Projekt' - const entries: Record = {} - for (const f of project.files) entries[`${folder}/${diskPath(f.path)}`] = new Uint8Array(strToU8(f.text)) // eigene Kopie: fflate prüft instanceof Uint8Array - // Leere Packages und Source Folders als Ordner behalten. - const dirs = [...project.packages.map((p) => `${folder}/${rootOf(p)}/${barePackage(p).replace(/\./g, '/')}/`), ...sourceFoldersOf(project).map((r) => `${folder}/${r}/`)] - for (const dir of dirs) if (!Object.keys(entries).some((k) => k.startsWith(dir))) entries[dir.replace(/\/\/$/, '/')] = new Uint8Array() - if ((project.uml && Object.keys(project.uml).length) || project.notes?.length || project.objects?.length) - entries[`${folder}/${umlFile}`] = new Uint8Array( - strToU8(JSON.stringify({ format: 'visuobject', version: 1, uml: project.uml ?? {}, ...(project.notes?.length ? { notes: project.notes } : {}), ...(project.objects?.length ? { objects: project.objects } : {}) }, null, 2)), - ) - return zipSync(entries, { level: 6 }) -} - -export function projectFromZip(data: Uint8Array, fallbackName: string): JavaProject { - const entries = unzipSync(data) - const javaFiles = Object.entries(entries) - .filter(([name]) => /\.java$/i.test(name) && !name.startsWith('__MACOSX/')) - .map(([name, bytes]) => ({ name, text: strFromU8(bytes) })) - const top = new Set(Object.keys(entries).map((n) => n.split('/')[0])) - const name = top.size === 1 && Object.keys(entries).some((n) => n.includes('/')) ? [...top][0] : fallbackName - // Nur Dateinamen weitergeben: Das Package kommt aus dem Code. Der Source Folder ist der Ordner über den Package-Ordnern. - const single = name !== fallbackName - const project = projectFromFiles(name, []) - for (const f of javaFiles) { - const one = projectFromFiles(name, [{ name: f.name.slice(f.name.lastIndexOf('/') + 1), text: f.text }]).files[0] - const dirs = f.name.split('/').slice(single ? 1 : 0, -1) - const pkgDirs = declaredPackage(one.text).split('.').filter(Boolean) - const tail = dirs.slice(dirs.length - pkgDirs.length).join('/') - const root = tail === pkgDirs.join('/') ? dirs.slice(0, dirs.length - pkgDirs.length).join('/') : '' - const sourceFolder = root && root !== defaultRoot && root.split('/').every(isFolderName) && !root.includes(':') ? root : defaultRoot - const path = pathFor(packageKey(sourceFolder, declaredPackage(one.text)), one.path.slice(one.path.lastIndexOf('/') + 1)) - project.files = [...project.files.filter((x) => x.path !== path), { ...one, path }] - if (sourceFolder !== defaultRoot && !project.sourceFolders?.includes(sourceFolder)) project.sourceFolders = [...(project.sourceFolders ?? []), sourceFolder] - } - const extra = Object.entries(entries).find(([n]) => n === umlFile || n.endsWith(`/${umlFile}`)) - if (extra) { - try { - const data = JSON.parse(strFromU8(extra[1])) as { uml?: unknown; notes?: unknown; objects?: unknown } - const uml = umlFromJSON(data.uml) - if (uml) project.uml = uml - const notes = notesFromJSON(data.notes) - if (notes) project.notes = notes - const objects = objectsFromJSON(data.objects) - if (objects) project.objects = objects - } catch { - /* ohne UML-Zusatz geht es auch */ - } - } - return project -} diff --git a/web/src/ui/renameDialog.ts b/web/src/ui/renameDialog.ts deleted file mode 100644 index 04c64ad0..00000000 --- a/web/src/ui/renameDialog.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { checkNewName } from '../core/rename' -import { t } from './i18n' - -export interface RenameRequest { - /** Namen zur Auswahl (Struktogramm); vorne der vorgeschlagene. Im Code genau einer: das Wort unter dem Cursor. */ - names: string[] - /** Alle Namen im Programm, um „gibt es schon“ zu erkennen. */ - existing: string[] - /** Stellen mit diesem Namen (für den Hinweis). */ - count(name: string): number -} - -const esc = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) - -/** Fenster „Variable umbenennen“: Name wählen, neuen Namen tippen, Enter übernimmt, Esc bricht ab. */ -export function askRename(request: RenameRequest): Promise<{ from: string; to: string } | undefined> { - return new Promise((resolve) => { - const box = document.createElement('div') - box.className = 'confirm' - const single = request.names.length === 1 - box.innerHTML = `` - const form = box.querySelector('form')! - const old = box.querySelector('label input, label select')! - const input = box.querySelector('.rename-new')! - const info = box.querySelector('.rename-info')! - const apply = box.querySelector('button[type=submit]')! - - const update = () => { - const from = old.value - const to = input.value.trim() - const check = checkNewName(from, to, request.existing) - const count = t('web_rename_count_fmt', request.count(from)) - info.classList.toggle('rename-warning', !check.ok ? to !== '' && to !== from : check.warning === 'exists') - if (!check.ok) { - info.textContent = check.reason === 'invalid' ? t('web_rename_invalid') : check.reason === 'keyword' ? t('web_rename_keyword_fmt', to) : count - } else info.textContent = check.warning === 'exists' ? `${count} ${t('web_rename_exists_fmt', to)}` : count - apply.disabled = !check.ok - } - const close = (result?: { from: string; to: string }) => { - box.remove() - resolve(result) - } - input.value = old.value - old.addEventListener('change', () => { - input.value = old.value - update() - }) - input.addEventListener('input', update) - form.addEventListener('submit', (e) => { - e.preventDefault() - const to = input.value.trim() - if (checkNewName(old.value, to, request.existing).ok) close({ from: old.value, to }) - }) - box.addEventListener('click', (e) => { - if (e.target === box || (e.target as HTMLElement).dataset.r === '0') close() - }) - box.addEventListener('keydown', (e) => { - if (e.key === 'Escape') { - e.preventDefault() - e.stopPropagation() - close() - } - }) - update() - document.body.append(box) - input.focus() - input.select() - }) -} - -/** Kleines Menü beim Rechtsklick mit „Umbenennen …“; schließt bei Klick daneben und mit Esc. */ -export function showRenameMenu(x: number, y: number, label: string, pick: () => void) { - document.querySelector('.rename-menu')?.remove() - const menu = document.createElement('div') - menu.className = 'rename-menu' - menu.setAttribute('role', 'menu') - const item = document.createElement('button') - item.setAttribute('role', 'menuitem') - item.textContent = label - menu.append(item) - menu.style.left = `${Math.min(x, window.innerWidth - 220)}px` - menu.style.top = `${Math.min(y, window.innerHeight - 50)}px` - const close = () => { - menu.remove() - document.removeEventListener('pointerdown', outside, true) - document.removeEventListener('keydown', escape, true) - } - const outside = (e: Event) => { - if (!menu.contains(e.target as Node)) close() - } - const escape = (e: KeyboardEvent) => { - if (e.key === 'Escape') close() - } - item.addEventListener('click', () => { - close() - pick() - }) - document.addEventListener('pointerdown', outside, true) - document.addEventListener('keydown', escape, true) - document.body.append(menu) - item.focus() -} diff --git a/web/src/ui/renderer.ts b/web/src/ui/renderer.ts deleted file mode 100644 index 2fa84b3c..00000000 --- a/web/src/ui/renderer.ts +++ /dev/null @@ -1,386 +0,0 @@ -import { canvasTextLines, keywordSplit } from '../core/diagramText' -import type { Layout, LayoutNode, Metrics, Rect } from '../core/layout' -import { ElementType, type VSElement } from '../core/model' -import { diagramFontFamily, fontExtents, type MeasureText } from '../core/textMeasurement' - -/** SVG-Port von `DiagramCanvasRenderer.swift` (Kacheln, Linien, Text). */ - -const SVG_NS = 'http://www.w3.org/2000/svg' - -/** `DiagramCanvasInk` (bei Diagramm-Stufe „klein“, Faktor 1). */ -const Ink = { - diagramLineWidth: 2, - outerFrameLineWidth: 2, - selectionLineWidth: 3.5, - canvasOuterMargin: 16, - cellTextInsetX: 18, - cellTextInsetY: 15, - multiHeaderFirstLineY: 14, - branchLabelInsetX: 10, - fallauswahlCaseLabelBand: 26, - callSideBarInset: 14, - callTextInsetFromSideBar: 20, - loopBodyLeadingInset: 20, -} - -type Omission = { top?: boolean; left?: boolean; bottom?: boolean; right?: boolean } - -/** Farbe wie bei den Codeblöcken (`cm-scope-…`): Schleife rosa, Verzweigung blau, Anweisungen gelb wie die Methode im Code. */ -export function scopeKind(type: ElementType): string | undefined { - switch (type) { - case ElementType.forSchleife: - case ElementType.whileSchleife: - case ElementType.doUntilSchleife: - case ElementType.endlosschleife: - return 'loop' - case ElementType.verzweigung: - case ElementType.fallauswahl: - return 'if' - default: - return undefined - } -} - -export function el( - tag: K, - attrs: Record = {}, -): SVGElementTagNameMap[K] { - const e = document.createElementNS(SVG_NS, tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, String(v)) - return e -} - -export class DiagramRenderer { - constructor( - private metrics: Metrics, - private measure: MeasureText, - ) {} - - private get lineHeight() { - return this.metrics.lineHeight - } - private get fontSize() { - return this.metrics.fontSize - } - - /** Im Simulationsmodus bedeutet die Markierung „aktueller Schritt“ statt Bearbeitungsauswahl. */ - usesSimulationHighlight = false - /** Code live offen: das Element der Cursorzeile wie die Codezeile hellblau füllen. */ - fillsSelection = false - - render( - layout: Layout, - size: { width: number; height: number }, - selectedPath?: number[], - /** Export: `exportCanvasOuterMarginPt` statt des Rands im Editor. */ - outerMargin = Ink.canvasOuterMargin, - ): SVGSVGElement { - const svg = el('svg', { - width: size.width, - height: size.height, - viewBox: `0 0 ${size.width} ${size.height}`, - 'font-family': diagramFontFamily, - 'font-size': this.fontSize, - class: 'diagram', - }) - svg.append(el('rect', { x: 0, y: 0, width: size.width, height: size.height, fill: '#fafafa' })) - const m = outerMargin - svg.append( - el('rect', { - x: m, - y: m, - width: layout.canvasSize.width - 2 * m - 1, - height: layout.canvasSize.height - 2 * m - 1, - fill: 'none', - stroke: 'black', - 'stroke-width': Ink.outerFrameLineWidth, - }), - ) - const g = el('g') - for (const n of layout.elements) this.drawNode(n, g, selectedPath, {}, undefined, 'method') - svg.append(g) - return svg - } - - private line(g: SVGElement, points: [number, number][]) { - g.append( - el('polyline', { - points: points.map((p) => p.join(',')).join(' '), - fill: 'none', - stroke: 'black', - 'stroke-width': Ink.diagramLineWidth, - 'stroke-linejoin': 'miter', - }), - ) - } - - private strokeCellBorder(g: SVGElement, r: Rect, o: Omission) { - const x0 = r.x - const y0 = r.y - const x1 = r.x + r.width - const y1 = r.y + r.height - if (!o.top && !o.left && !o.bottom && !o.right) { - g.append(el('rect', { x: x0, y: y0, width: r.width, height: r.height, fill: 'none', stroke: 'black', 'stroke-width': Ink.diagramLineWidth })) - return - } - if (!o.top) this.line(g, [[x0, y0], [x1, y0]]) - if (!o.right) this.line(g, [[x1, y0], [x1, y1]]) - if (!o.bottom) this.line(g, [[x1, y1], [x0, y1]]) - if (!o.left) this.line(g, [[x0, y1], [x0, y0]]) - } - - private textWidth(line: string): number { - const parts = keywordSplit(line) - if (parts) { - const kw = this.measure(parts.keyword, this.fontSize, 'bold') - return kw + (parts.rest === '' ? 0 : this.measure(' ' + parts.rest, this.fontSize, 'medium')) - } - return this.measure(line, this.fontSize, 'medium') - } - - /** Zeile mit fett gedrucktem Schlüsselwort, Ursprung oben links (`anchor: .topLeading`). */ - private drawKeywordLine(g: SVGElement, line: string, x: number, y: number) { - // Grundlinie selbst setzen: Safari kennt `dominant-baseline: text-before-edge` nicht - const t = el('text', { x, y: y + fontExtents(this.fontSize, 'medium').ascent, 'font-weight': 500 }) - t.style.whiteSpace = 'pre' - const parts = keywordSplit(line) - if (parts) { - const kw = el('tspan', { 'font-weight': 700 }) - kw.textContent = parts.keyword - t.append(kw) - if (parts.rest !== '') { - const rest = el('tspan', {}) - // geschütztes Leerzeichen: Safari verschluckt sonst das führende Leerzeichen im tspan - rest.textContent = '\u00a0' + parts.rest - t.append(rest) - } - } else { - t.textContent = line - } - g.append(t) - } - - private drawLabel(g: SVGElement, text: string, x: number, y: number, bold: boolean, baseline: 'top' | 'bottom') { - const extents = fontExtents(this.fontSize, bold ? 'bold' : 'medium') - const t = el('text', { - x, - y: baseline === 'top' ? y + extents.ascent : y - extents.descent, - 'font-weight': bold ? 700 : 500, - }) - t.style.whiteSpace = 'pre' - t.textContent = text - g.append(t) - } - - private roofMetrics(node: LayoutNode, r: Rect, headerH: number, dividerX: number) { - const roofBottom = r.y + headerH - const yInset = node.dividerYInset ?? 0 - const apexPadding = 12 - const blockH = Math.max(node.element.textLines.length, 1) * this.lineHeight - const topStart = r.y + Ink.multiHeaderFirstLineY - if (node.element.type === ElementType.fallauswahl) { - const caseBand = Math.max(Ink.fallauswahlCaseLabelBand, this.lineHeight + 10) - const titleBottomLimit = roofBottom - caseBand - const apexY = Math.min(roofBottom + yInset, titleBottomLimit - 8) - const apexClampedY = Math.max(apexY, topStart + blockH + 6) - return { apex: { x: dividerX, y: apexClampedY }, maxTextBottom: titleBottomLimit - apexPadding, roofBottom } - } - const apex = { x: dividerX, y: roofBottom + yInset } - return { apex, maxTextBottom: apex.y - apexPadding, roofBottom } - } - - private drawNode( - node: LayoutNode, - g: SVGElement, - selectedPath: number[] | undefined, - omission: Omission, - parentType?: ElementType, - parentScope?: string, - ) { - const r = node.frame - const e = node.element - const maxX = r.x + r.width - const maxY = r.y + r.height - const isMulti = e.type === ElementType.verzweigung || e.type === ElementType.fallauswahl - const headerH = isMulti ? node.headerBandHeight : 0 - const isSelected = selectedPath !== undefined && samePath(node.idPath, selectedPath) - const lines = canvasTextLines(e) - - const cell = el('g', { 'data-path': node.idPath.join(',') }) - g.append(cell) - const sim = this.usesSimulationHighlight - // Hintergrundfarbe aus der Datei (`bgcolor`), die Auswahl hat Vorrang. - const bg = e.backgroundColor - const own = bg && bg.a > 0 ? `rgba(${bg.r}, ${bg.g}, ${bg.b}, ${bg.a / 255})` : 'white' - const fill = isSelected && (sim || this.fillsSelection) ? 'rgba(199, 230, 255, 0.55)' : own - // Anweisung, Aufruf, Aussprung immer gelb; nur der leere Platzhalter ø nimmt die Farbe des Blocks. - const scope = scopeKind(e.type) ?? (e.type === ElementType.leerElement ? parentScope : 'method') - // Fläche in der Farbe des Blocks per CSS (Einstellung „Blöcke farbig“); im Export bleibt sie weiß. - const area = el('rect', { x: r.x, y: r.y, width: r.width, height: r.height, fill }) - if (fill === 'white' && scope) area.setAttribute('class', `ns-scope ns-scope-${scope}`) - cell.append(area) - this.strokeCellBorder(cell, r, omission) - - const bar = Ink.callSideBarInset - if (e.type === ElementType.aufruf) { - this.line(cell, [[r.x + bar, r.y], [r.x + bar, maxY]]) - this.line(cell, [[maxX - bar, r.y], [maxX - bar, maxY]]) - } else if (e.type === ElementType.aussprung) { - this.line(cell, [[r.x + bar, r.y], [r.x, r.y + r.height / 2], [r.x + bar, maxY]]) - } - - if (isMulti && node.dividerX !== undefined) { - const dividerX = node.dividerX - const roof = this.roofMetrics(node, r, headerH, dividerX) - const blockH = Math.max(lines.length, 1) * this.lineHeight - const topStart = r.y + Ink.multiHeaderFirstLineY - this.line(cell, [[r.x, r.y], [roof.apex.x, roof.apex.y], [maxX, r.y]]) - if (e.type === ElementType.fallauswahl) this.line(cell, [[r.x, roof.roofBottom], [maxX, roof.roofBottom]]) - - let firstY: number - if (topStart + blockH <= roof.maxTextBottom) firstY = topStart - else firstY = Math.max(r.y + 2, roof.maxTextBottom - (Math.max(lines.length, 1) - 1) * this.lineHeight - this.lineHeight) - lines.forEach((line, i) => { - const tw = this.textWidth(line) - let tx: number - if (e.type === ElementType.verzweigung) { - const lo = r.x + Ink.cellTextInsetX - const hi = maxX - tw - Ink.cellTextInsetX - tx = Math.min(Math.max(dividerX - tw / 2, lo), hi) - } else { - tx = r.x + r.width / 2 - tw / 2 - } - this.drawKeywordLine(cell, line, tx, firstY + i * this.lineHeight) - }) - } else if (e.type === ElementType.doUntilSchleife) { - // „do“ und Fuß werden nach den Kindern gezeichnet. - } else if (e.type === ElementType.anweisung || e.type === ElementType.aufruf || e.type === ElementType.aussprung) { - const ix = Ink.cellTextInsetX - let contentLeft = r.x + ix - let contentRight = maxX - ix - if (e.type === ElementType.aufruf) { - contentLeft = r.x + bar + Ink.callTextInsetFromSideBar - contentRight = maxX - bar - Ink.callTextInsetFromSideBar - } else if (e.type === ElementType.aussprung) { - contentLeft = r.x + bar + ix - } - const contentWidth = Math.max(0, contentRight - contentLeft) - let y = r.y + r.height / 2 - (Math.max(1, lines.length) * this.lineHeight) / 2 - for (const line of lines) { - this.drawKeywordLine(cell, line, contentLeft + (contentWidth - this.textWidth(line)) / 2, y) - y += this.lineHeight - } - } else { - const total = Math.max(1, lines.length) * this.lineHeight - let y = parentType === ElementType.doUntilSchleife ? r.y + r.height / 2 - total / 2 : r.y + Ink.cellTextInsetY - for (const line of lines) { - this.drawKeywordLine(cell, line, r.x + Ink.cellTextInsetX, y) - y += this.lineHeight - } - } - - node.children.forEach((c, idx) => { - const childOmission: Omission = e.type === ElementType.doUntilSchleife && idx === 0 ? { top: true, left: true } : {} - this.drawNode(c, g, selectedPath, childOmission, e.type, scope) - }) - - // Ab hier über den Kindern zeichnen. - const over = el('g') - g.append(over) - - if (e.type === ElementType.doUntilSchleife) { - const dividerX = node.children[0]?.frame.x ?? r.x + (node.loopBodyLeadingInset > 0 ? node.loopBodyLeadingInset : Ink.loopBodyLeadingInset) - this.drawLabel(over, 'do', r.x + 4, r.y + this.metrics.doUntilDoLabelTopInset, true, 'top') - const ySep = maxY - node.headerBandHeight - this.line(over, [[dividerX, ySep], [maxX, ySep]]) - this.line(over, [[dividerX, r.y], [dividerX, ySep]]) - let y = ySep + this.metrics.doUntilFooterTopInset - for (const line of lines) { - this.drawKeywordLine(over, line, r.x + Ink.cellTextInsetX, y) - y += this.lineHeight - } - } - - if (isMulti && node.dividerX !== undefined && node.caseColumnFrames.length > 1) { - const roof = this.roofMetrics(node, r, headerH, node.dividerX) - const apex = roof.apex - node.caseColumnFrames.forEach((col, idx) => { - if (idx >= node.caseColumnFrames.length - 1) return - const xSep = col.x + col.width - const yRaw = yOnHeaderDiagonal(xSep, r, apex) - const yTop = Number.isFinite(yRaw) ? Math.min(Math.max(yRaw, r.y), maxY) : r.y - this.line(over, [[xSep, maxY], [xSep, yTop]]) - }) - const cases = e.cases - if (cases && cases.length === node.caseColumnFrames.length) { - const labelY = e.type === ElementType.fallauswahl ? roof.roofBottom - 6 : r.y + headerH - 8 - const lx = Ink.branchLabelInsetX - if (e.type === ElementType.verzweigung && cases.length === 2) { - this.drawLabel(over, 'true', r.x + lx, labelY, true, 'bottom') - const right = node.caseColumnFrames[1] - const rw = this.measure('false', this.fontSize, 'bold') - this.drawLabel(over, 'false', right.x + right.width - lx - rw, labelY, true, 'bottom') - } else { - cases.forEach((c, idx) => { - const col = node.caseColumnFrames[idx] - const bold = ['default', 'true', 'false'].includes(c.name.toLowerCase()) - const tw = this.measure(c.name, this.fontSize, bold ? 'bold' : 'medium') - this.drawLabel(over, c.name, col.x + col.width / 2 - tw / 2, labelY, bold, 'bottom') - }) - } - } - } - - if (isSelected) { - over.append( - el('rect', { - x: r.x, - y: r.y, - width: r.width, - height: r.height, - class: 'selection-frame', - fill: 'none', - stroke: sim ? 'rgba(66, 140, 242, 0.78)' : 'rgba(0, 122, 255, 0.9)', - 'stroke-width': Ink.selectionLineWidth, - }), - ) - } - } -} - -function yOnHeaderDiagonal(xSep: number, rect: Rect, apex: { x: number; y: number }): number { - const topY = rect.y - if (xSep <= apex.x) { - const run = apex.x - rect.x - if (Math.abs(run) < 0.5) return apex.y - return topY + ((apex.y - topY) / run) * (xSep - rect.x) - } - const run = rect.x + rect.width - apex.x - if (Math.abs(run) < 0.5) return apex.y - return apex.y + ((topY - apex.y) / run) * (xSep - apex.x) -} - -export function samePath(a: number[], b: number[]): boolean { - return a.length === b.length && a.every((v, i) => v === b[i]) -} - -/** Alle Knoten in Zeichenreihenfolge (Eltern vor Kindern). */ -export function flattenNodes(nodes: LayoutNode[]): LayoutNode[] { - const out: LayoutNode[] = [] - const walk = (n: LayoutNode) => { - out.push(n) - n.children.forEach(walk) - } - nodes.forEach(walk) - return out -} - -/** Wie `displayDocumentForLayout`: Layout rechnet mit den angezeigten Zeilen (Schlüsselwörter, Platzhalter). */ -export function displayElement(e: VSElement): VSElement { - return { - ...e, - textLines: canvasTextLines(e), - loopBody: e.loopBody?.map(displayElement), - cases: e.cases?.map((c) => ({ name: c.name, elements: c.elements.map(displayElement) })), - } -} diff --git a/web/src/ui/rescueGameView.ts b/web/src/ui/rescueGameView.ts deleted file mode 100644 index caef98a4..00000000 --- a/web/src/ui/rescueGameView.ts +++ /dev/null @@ -1,339 +0,0 @@ -import { inline } from '../learn/markdown' -import { crosswordGrid, gatePlaceholder, type CrosswordGame, type RescueGame } from '../learn/rescueGame' -import { t } from './i18n' - -/** Spiel „Prinzessin retten“: Stationen mit Zahlen, Kreuzworträtsel und das Tor mit dem geheimen Wort. */ - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function rich(tag: string, attrs: Record, markdown: string): HTMLElement { - const e = h(tag, attrs) - e.innerHTML = inline(markdown) - return e -} - -function codeBlock(code: string): HTMLElement { - return h('pre', { class: 'learn-game-code mono', dir: 'ltr' }, ...code.split('\n').map((line) => h('div', { class: 'ln' }, line || ' '))) -} - -export function renderRescueGame(game: RescueGame, ludwigURL: string): HTMLElement { - const box = h('section', { class: 'learn-game learn-rescue' }) - const head = h('div', { class: 'learn-game-head' }) - head.append(h('img', { src: ludwigURL, alt: '', width: '40', height: '40' }), h('strong', {}, game.title)) - const total = game.stations.length - - // Der Weg zum Turm: Ludwig rückt mit jeder gelösten Station weiter. - const dots = game.stations.map(() => h('span', { class: 'learn-rescue-dot' })) - const ludwig = h('img', { class: 'learn-rescue-ludwig', src: ludwigURL, alt: '', width: '32', height: '32' }) - const tower = h('span', { class: 'learn-rescue-tower', 'aria-hidden': 'true' }, '🏰', h('span', { class: 'learn-rescue-zora' }, '🐐'), h('span', { class: 'learn-rescue-fox' }, '🦊')) - const zoraFree = h('span', { class: 'learn-rescue-free' }, '🐐') - const path = h('div', { class: 'learn-rescue-path', 'aria-hidden': 'true' }, ludwig, ...dots, tower) - const status = h('p', { class: 'learn-game-status' }) - const stage = h('div', { class: 'learn-game-stage' }) - const crossword = crosswordView(game) - box.append(head, path, status, stage) - - let solved = 0 - const moveLudwig = () => { - dots.forEach((d, i) => d.classList.toggle('done', i < solved)) - // Ludwig steht hinter dem letzten gelösten Punkt. - if (solved) dots[solved - 1].after(ludwig) - else path.prepend(ludwig) - } - - const alphabetView = () => { - const cells = [...game.alphabet].map((c, i) => h('span', { class: 'learn-rescue-letter' }, h('b', {}, c), h('small', {}, String(i)))) - return { element: h('div', { class: 'learn-rescue-alphabet mono', dir: 'ltr' }, ...cells), cells } - } - - const showStation = (index: number) => { - const station = game.stations[index] - status.textContent = t('web_learn_rescue_station_fmt', index + 1, total, station.name) - const alphabet = alphabetView() - const input = h('input', { type: 'text', inputmode: 'numeric', autocomplete: 'off', class: 'learn-rescue-number mono', 'aria-label': t('web_learn_rescue_number') }) as HTMLInputElement - const check = h('button', { class: 'primary' }, t('web_learn_check')) as HTMLButtonElement - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const submit = () => { - const value = input.value.trim() - if (!/^-?\d+$/.test(value)) { - feedback.className = 'learn-game-feedback wrong' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_not_number'))) - return - } - if (Number(value) !== station.answer) { - feedback.className = 'learn-game-feedback wrong' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_wrong')), ...(station.hint ? [rich('div', { class: 'learn-game-explain' }, `**${t('web_learn_hint')}:** ${station.hint}`)] : [])) - input.select() - return - } - input.disabled = check.disabled = true - const letter = game.alphabet.charAt(station.answer) - alphabet.cells[station.answer].classList.add('hit') - crossword.give(station.row, station.col, letter) - solved++ - moveLudwig() - const last = index + 1 === total - const next = h('button', { class: 'primary' }, last ? t('web_learn_rescue_to_crossword') : t('web_game_next')) - next.addEventListener('click', () => (last ? showCrossword() : showStation(index + 1))) - feedback.className = 'learn-game-feedback right' - feedback.replaceChildren( - h('p', {}, t('web_game_right')), - h('p', { class: 'learn-rescue-charat mono', dir: 'ltr' }, `alphabet.charAt(${station.answer})`, h('span', { 'aria-hidden': 'true' }, ' → '), h('code', {}, `'${letter}'`)), - rich('div', { class: 'learn-game-explain' }, station.explanation), - h('p', { class: 'learn-rescue-moved' }, t('web_learn_rescue_letter_moved')), - next, - ) - next.focus({ preventScroll: true }) - } - check.addEventListener('click', submit) - input.addEventListener('keydown', (e) => { - if (e.key === 'Enter') submit() - }) - stage.replaceChildren( - codeBlock(station.code), - rich('p', { class: 'learn-game-question' }, station.question), - h('div', { class: 'learn-rescue-answer' }, input, check), - feedback, - h('p', { class: 'learn-rescue-label' }, t('web_learn_rescue_alphabet')), - codeBlock(`String alphabet = "${game.alphabet}";`), - alphabet.element, - crossword.element, - ) - input.focus({ preventScroll: true }) - } - - const showCrossword = () => { - status.textContent = t('web_learn_rescue_crossword') - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const check = h('button', { class: 'primary' }, t('web_learn_check')) as HTMLButtonElement - check.addEventListener('click', () => { - if (!crossword.check()) { - feedback.className = 'learn-game-feedback wrong' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_cw_wrong'))) - return - } - check.disabled = true - crossword.lock() - const next = h('button', { class: 'primary' }, t('web_learn_rescue_to_gate')) - next.addEventListener('click', showGate) - feedback.className = 'learn-game-feedback right' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_cw_done')), next) - next.focus({ preventScroll: true }) - }) - crossword.unlock() - stage.replaceChildren(rich('p', {}, game.crosswordText), crossword.element, check, feedback) - crossword.focusFirst() - } - - const showGate = () => { - status.textContent = t('web_learn_rescue_gate') - const input = h('input', { type: 'text', autocomplete: 'off', autocapitalize: 'off', spellcheck: 'false', class: 'learn-rescue-word mono', size: String(game.keyword.length + 2), 'aria-label': t('web_learn_rescue_word') }) as HTMLInputElement - const lines = game.gateCode.split('\n').map((line) => { - const at = line.indexOf(gatePlaceholder) - if (at < 0) return h('div', { class: 'ln' }, line || ' ') - return h('div', { class: 'ln' }, line.slice(0, at), input, line.slice(at + gatePlaceholder.length)) - }) - const open = h('button', { class: 'primary' }, t('web_learn_rescue_open')) as HTMLButtonElement - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const submit = () => { - const value = input.value.trim() - if (value !== game.keyword) { - const almost = value.toUpperCase() === game.keyword.toUpperCase() - feedback.className = 'learn-game-feedback wrong' - feedback.replaceChildren(h('p', {}, t(almost ? 'web_learn_rescue_gate_case' : 'web_learn_rescue_gate_wrong'))) - input.select() - return - } - input.disabled = open.disabled = true - box.classList.add('freed') - ludwig.after(zoraFree) - status.textContent = '' - const again = h('button', {}, t('web_game_again')) - again.addEventListener('click', restart) - feedback.className = 'learn-game-feedback right' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_freed')), rich('div', { class: 'learn-game-explain' }, game.end), again) - } - open.addEventListener('click', submit) - input.addEventListener('keydown', (e) => { - if (e.key === 'Enter') submit() - }) - stage.replaceChildren(rich('p', {}, game.gateText), h('div', { class: 'learn-game-code mono learn-rescue-gate', dir: 'ltr' }, ...lines), open, feedback) - input.focus({ preventScroll: true }) - } - - const restart = () => { - solved = 0 - box.classList.remove('freed') - zoraFree.remove() - crossword.reset() - moveLudwig() - showStation(0) - } - - const start = h('button', { class: 'primary' }, t('web_game_start')) - start.addEventListener('click', restart) - stage.append(rich('p', {}, game.intro), start) - return box -} - -/** Spiel „Begriffe-Rätsel“: nur das Kreuzworträtsel mit Lösungswort. */ -export function renderCrosswordGame(game: CrosswordGame, ludwigURL: string): HTMLElement { - const box = h('section', { class: 'learn-game learn-rescue learn-crossword-game' }) - const head = h('div', { class: 'learn-game-head' }) - head.append(h('img', { src: ludwigURL, alt: '', width: '40', height: '40' }), h('strong', {}, game.title)) - const crossword = crosswordView(game) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const check = h('button', { class: 'primary' }, t('web_learn_check')) as HTMLButtonElement - check.addEventListener('click', () => { - if (!crossword.check()) { - feedback.className = 'learn-game-feedback wrong' - feedback.replaceChildren(h('p', {}, t('web_learn_rescue_cw_wrong'))) - return - } - check.disabled = true - crossword.lock() - const again = h('button', {}, t('web_game_again')) - again.addEventListener('click', () => { - crossword.reset() - crossword.unlock() - check.disabled = false - feedback.className = 'learn-game-feedback' - feedback.replaceChildren() - crossword.focusFirst() - }) - feedback.className = 'learn-game-feedback right' - feedback.replaceChildren( - h('p', {}, t('web_learn_rescue_cw_done')), - h('p', { class: 'learn-game-result' }, h('span', { class: 'mono', dir: 'ltr' }, game.keyword)), - ...(game.end ? [rich('p', {}, game.end)] : []), - again, - ) - }) - crossword.unlock() - box.append(head, rich('p', {}, game.intro), crossword.element, check, feedback) - return box -} - -/** Das Kreuzworträtsel: erst nur zum Anschauen, dann zum Ausfüllen. */ -function crosswordView(game: Pick) { - const grid = crosswordGrid(game) - const keyCells = new Map(game.solution.map((s, i) => [`${s.row} ${s.col}`, i])) - const numbers = new Map() - for (const w of game.words) numbers.set(`${w.row} ${w.col}`, w.number) - const inputs = new Map() - const board = h('div', { class: 'learn-cw-grid', dir: 'ltr' }) - board.style.gridTemplateColumns = `repeat(${game.cols}, var(--cw-cell))` - /** Richtung beim Tippen: waagrecht, außer das Feld gehört nur zu einem senkrechten Wort. */ - let across = true - const wordsAt = (r: number, c: number) => - game.words.filter((w) => (w.across ? w.row === r && c >= w.col && c < w.col + w.word.length : w.col === c && r >= w.row && r < w.row + w.word.length)) - const step = (r: number, c: number, d: number) => { - const next = across ? `${r} ${c + d}` : `${r + d} ${c}` - inputs.get(next)?.focus() - } - grid.forEach((row, r) => - row.forEach((letter, c) => { - if (!letter) return board.append(h('span', { class: 'learn-cw-empty' })) - const key = `${r} ${c}` - const input = h('input', { type: 'text', maxlength: '1', autocomplete: 'off', autocapitalize: 'characters', spellcheck: 'false', 'aria-label': t('web_learn_rescue_cell_fmt', r + 1, c + 1) }) as HTMLInputElement - input.disabled = true - input.addEventListener('focus', () => { - const ws = wordsAt(r, c) - if (!ws.some((w) => w.across === across)) across = ws[0].across - input.select() - }) - input.addEventListener('input', () => { - input.value = input.value.slice(-1).toUpperCase() - input.classList.remove('wrong') - updateKeyword() - if (input.value) step(r, c, 1) - }) - input.addEventListener('keydown', (e) => { - if (e.key === 'Backspace' && !input.value) step(r, c, -1) - else if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { - across = true - step(r, c, e.key === 'ArrowRight' ? 1 : -1) - } else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') { - across = false - step(r, c, e.key === 'ArrowDown' ? 1 : -1) - } else return - e.preventDefault() - }) - inputs.set(key, input) - const keyIndex = keyCells.get(key) - const cell = h('span', { class: `learn-cw-cell${keyIndex !== undefined ? ' key' : ''}` }) - const number = numbers.get(key) - if (number) cell.append(h('small', { class: 'learn-cw-number' }, String(number))) - if (keyIndex !== undefined) cell.append(h('small', { class: 'learn-cw-keyindex' }, String(keyIndex + 1))) - cell.append(input) - board.append(cell) - }), - ) - const clueList = (isAcross: boolean) => { - const list = h('ol', { class: 'learn-cw-clues' }) - for (const w of game.words.filter((w) => w.across === isAcross).sort((a, b) => a.number - b.number)) { - const item = rich('li', { value: String(w.number) }, `${w.clue} (${w.word.length})`) - item.addEventListener('click', () => { - across = w.across - inputs.get(`${w.row} ${w.col}`)?.focus() - }) - list.append(item) - } - return h('div', {}, h('strong', {}, t(isAcross ? 'web_learn_rescue_across' : 'web_learn_rescue_down')), list) - } - const keyBoxes = game.solution.map(() => h('span', { class: 'learn-cw-keybox' })) - const updateKeyword = () => game.solution.forEach((s, i) => (keyBoxes[i].textContent = inputs.get(`${s.row} ${s.col}`)!.value || ' ')) - const element = h( - 'div', - { class: 'learn-cw' }, - h('div', { class: 'learn-cw-board' }, board, h('p', { class: 'learn-cw-keyword' }, `${t('web_learn_rescue_keyword')} `, h('span', { class: 'mono', dir: 'ltr' }, ...keyBoxes))), - h('div', { class: 'learn-cw-cluebox' }, clueList(true), clueList(false)), - ) - const given = new Set() - return { - element, - /** Buchstabe aus einer Station: steht fest im Feld. */ - give(r: number, c: number, letter: string) { - const input = inputs.get(`${r} ${c}`)! - input.value = letter - input.parentElement!.classList.add('given', 'arrived') - given.add(`${r} ${c}`) - updateKeyword() - }, - unlock() { - for (const [key, input] of inputs) input.disabled = given.has(key) - }, - lock() { - for (const input of inputs.values()) input.disabled = true - }, - focusFirst() { - ;[...inputs].find(([key]) => !given.has(key))?.[1].focus({ preventScroll: true }) - }, - /** Markiert falsche Felder; `true`, wenn alles stimmt. */ - check(): boolean { - let right = true - for (const [key, input] of inputs) { - const [r, c] = key.split(' ').map(Number) - const bad = input.value !== grid[r][c] - input.classList.toggle('wrong', bad) - if (bad) right = false - } - return right - }, - reset() { - given.clear() - for (const input of inputs.values()) { - input.value = '' - input.disabled = true - input.classList.remove('wrong') - input.parentElement!.classList.remove('given', 'arrived') - } - updateKeyword() - }, - } -} diff --git a/web/src/ui/robot3d.ts b/web/src/ui/robot3d.ts deleted file mode 100644 index 64ab61e5..00000000 --- a/web/src/ui/robot3d.ts +++ /dev/null @@ -1,443 +0,0 @@ -import * as THREE from 'three' -import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' -import { RoomEnvironment } from 'three/examples/jsm/environments/RoomEnvironment.js' -import { buildMBot2, drawScreen, type MBot2Model, type RobotPart } from './mbot2Model' -import { CELL, LINE_WIDTH, type Point, type Pose, type RobotWorldSetup } from '../core/robotWorld' - -/** - * 3D-Ansicht des VisuBot-Simulators mit three.js (MIT). Der mBot2 ist aus einfachen Formen nachgebaut - * (keine Makeblock-Modelldaten). Einheit: 1 = 1 cm. Boden-y der Welt wird zu −z, damit man von hinten auf den Roboter schaut. - */ - -const toScene = (p: Point, height = 0) => new THREE.Vector3(p.x, height, -p.y) -const rad = (deg: number) => (deg * Math.PI) / 180 - -const colors = { - floor: 0xf4f1ea, - grid: 0xd6d0c4, - wall: 0xc9a46b, - tape: 0x222222, - trail: 0xe8590c, -} - -export type { RobotPart } - -/** Abstand der Räder (Mitte zu Mitte) und Radius, damit sich die Räder passend zur Fahrt drehen. */ -const TRACK = 12.9 -const WHEEL_RADIUS = 3.3 - -export class Robot3D { - readonly canvas: HTMLCanvasElement - private renderer: THREE.WebGLRenderer - private scene = new THREE.Scene() - private camera = new THREE.PerspectiveCamera(45, 1, 1, 2000) - private controls: OrbitControls - private model: MBot2Model - private robot: THREE.Group - /** Spur als flaches Band (Linienbreite lässt WebGL nicht zu): fertige Stücke plus eins, das gerade wächst. */ - private trailGroup = new THREE.Group() - private trailPiece = new THREE.BoxGeometry(1, 0.15, 1.4) - private trailMaterial = new THREE.MeshStandardMaterial({ color: colors.trail }) - private liveSegment: THREE.Mesh - private worldGroup = new THREE.Group() - private frame?: number - private sun: THREE.DirectionalLight - private resize: ResizeObserver - private shown: Pose = { x: 0, y: 0, heading: 0 } - private anim?: { from: Pose; to: Pose; start: number; ms: number } - private trail: Point[] = [] - private wasShown = false - /** Zuletzt gezeigte Lage, um die Räder mitzudrehen */ - private wheelPose?: Pose - /** Kamerafahrt zu einem Teil (Tour durch das Modell) */ - private flight?: { from: { pos: THREE.Vector3; target: THREE.Vector3 }; to: { pos: THREE.Vector3; target: THREE.Vector3 }; start: number; ms: number } - - constructor(private host: HTMLElement) { - this.canvas = document.createElement('canvas') - this.canvas.className = 'visubot-canvas' - host.append(this.canvas) - this.renderer = new THREE.WebGLRenderer({ canvas: this.canvas, antialias: true, alpha: true }) - this.renderer.setPixelRatio(Math.min(2, window.devicePixelRatio)) - this.renderer.toneMapping = THREE.ACESFilmicToneMapping - this.renderer.shadowMap.enabled = true - this.renderer.shadowMap.type = THREE.PCFSoftShadowMap - // Spiegelungen auf Aluminium und Kunststoff aus einer einfachen Raumumgebung - const pmrem = new THREE.PMREMGenerator(this.renderer) - this.scene.environment = pmrem.fromScene(new RoomEnvironment(), 0.04).texture - this.scene.environmentIntensity = 0.7 - pmrem.dispose() - this.scene.add(new THREE.HemisphereLight(0xffffff, 0x8a8170, 0.9)) - const sun = new THREE.DirectionalLight(0xffffff, 1.8) - sun.position.set(-40, 90, 50) - sun.castShadow = true - sun.shadow.mapSize.set(1024, 1024) - sun.shadow.camera.left = sun.shadow.camera.bottom = -30 - sun.shadow.camera.right = sun.shadow.camera.top = 30 - sun.shadow.camera.far = 300 - sun.shadow.bias = -0.0005 - this.sun = sun - this.scene.add(sun, sun.target) - this.scene.add(this.worldGroup) - this.model = buildMBot2() - this.robot = this.model.group - this.scene.add(this.robot) - this.scene.add(this.trailGroup) - this.liveSegment = new THREE.Mesh(this.trailPiece, this.trailMaterial) - this.scene.add(this.liveSegment) - this.camera.position.set(0, 75, 95) - this.controls = new OrbitControls(this.camera, this.canvas) - this.controls.target.set(0, 0, -15) - // Auch von unten anschauen (dort ist der Boden durchsichtig) - this.controls.maxPolarAngle = Math.PI - 0.05 - this.controls.minDistance = 12 - this.controls.maxDistance = 600 - this.controls.update() - // Wer selbst dreht, hält die Kamerafahrt und das langsame Kreisen an. - this.controls.addEventListener('start', () => { - this.flight = undefined - this.controls.autoRotate = false - }) - this.resize = new ResizeObserver(() => this.fit()) - this.resize.observe(host) - this.fit() - this.loop() - } - - dispose() { - if (this.frame !== undefined) cancelAnimationFrame(this.frame) - this.resize.disconnect() - this.controls.dispose() - this.renderer.dispose() - this.renderer.forceContextLoss() - this.canvas.remove() - } - - /** Boden, Wände und Klebeband neu aufbauen. */ - setWorld(setup: RobotWorldSetup) { - this.worldGroup.clear() - // Kamera so, dass der ganze Boden schräg von hinten zu sehen ist - const m = Math.max(setup.width, setup.depth, 80) - this.flight = undefined - this.controls.autoRotate = false - this.controls.target.set(0, 0, 0) - this.camera.position.set(0, m * 0.95, m * 0.5) - this.controls.update() - const floor = new THREE.Mesh(new THREE.PlaneGeometry(setup.width, setup.depth), new THREE.MeshStandardMaterial({ color: colors.floor, roughness: 0.9 })) - floor.rotation.x = -Math.PI / 2 - floor.receiveShadow = true - this.worldGroup.add(floor) - // Raster im Abstand von 10 cm (ein Schritt von mbot.forward()). - const grid: number[] = [] - for (let x = -setup.width / 2; x <= setup.width / 2 + 1e-6; x += 10) grid.push(x, 0.05, -setup.depth / 2, x, 0.05, setup.depth / 2) - for (let z = -setup.depth / 2; z <= setup.depth / 2 + 1e-6; z += 10) grid.push(-setup.width / 2, 0.05, z, setup.width / 2, 0.05, z) - const gridGeometry = new THREE.BufferGeometry() - gridGeometry.setAttribute('position', new THREE.Float32BufferAttribute(grid, 3)) - this.worldGroup.add(new THREE.LineSegments(gridGeometry, new THREE.LineBasicMaterial({ color: colors.grid }))) - const border = [ - { from: { x: -setup.width / 2, y: -setup.depth / 2 }, to: { x: setup.width / 2, y: -setup.depth / 2 } }, - { from: { x: setup.width / 2, y: -setup.depth / 2 }, to: { x: setup.width / 2, y: setup.depth / 2 } }, - { from: { x: setup.width / 2, y: setup.depth / 2 }, to: { x: -setup.width / 2, y: setup.depth / 2 } }, - { from: { x: -setup.width / 2, y: setup.depth / 2 }, to: { x: -setup.width / 2, y: -setup.depth / 2 } }, - ] - const wallMaterial = new THREE.MeshStandardMaterial({ color: colors.wall }) - for (const w of [...border.map((b) => ({ ...b, low: true })), ...setup.walls.map((b) => ({ ...b, low: false }))]) { - const length = Math.hypot(w.to.x - w.from.x, w.to.y - w.from.y) - const height = w.low ? 4 : 15 - const box = new THREE.Mesh(new THREE.BoxGeometry(length, height, 2), wallMaterial) - box.position.copy(toScene({ x: (w.from.x + w.to.x) / 2, y: (w.from.y + w.to.y) / 2 }, height / 2)) - box.rotation.y = Math.atan2(w.to.y - w.from.y, w.to.x - w.from.x) - this.worldGroup.add(box) - } - // Wandklötze, schwarze Felder und Ziele aus einer Weltkarte (wie die Bäume und Kleeblätter bei Kara) - const blockGeometry = new THREE.BoxGeometry(CELL, 12, CELL) - for (const b of setup.blocks ?? []) { - const box = new THREE.Mesh(blockGeometry, wallMaterial) - box.position.copy(toScene(b, 6)) - box.castShadow = box.receiveShadow = true - this.worldGroup.add(box) - } - const cellGeometry = new THREE.BoxGeometry(CELL, 0.1, CELL) - const markMaterial = new THREE.MeshStandardMaterial({ color: colors.tape }) - for (const m of setup.marks ?? []) { - const cell = new THREE.Mesh(cellGeometry, markMaterial) - cell.position.copy(toScene(m, 0.1)) - this.worldGroup.add(cell) - } - const goalMaterial = new THREE.MeshStandardMaterial({ color: colors.tape }) - const flagMaterial = new THREE.MeshStandardMaterial({ color: 0x2fb344, emissive: 0x1b7a2c, emissiveIntensity: 0.4 }) - const goals = setup.goals ?? [] - const isGoal = (x: number, y: number) => goals.some((g) => Math.abs(g.x - x) < 1e-6 && Math.abs(g.y - y) < 1e-6) - for (const g of goals) { - const cell = new THREE.Mesh(cellGeometry, goalMaterial) - cell.position.copy(toScene(g, 0.1)) - this.worldGroup.add(cell) - // Eine Fahne je Zielfläche: nur im Feld hinten rechts - if (isGoal(g.x + CELL, g.y) || isGoal(g.x, g.y + CELL)) continue - // Kleine Zielfahne in der Ecke, damit man das Ziel von den schwarzen Feldern unterscheidet - const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 9, 8), new THREE.MeshStandardMaterial({ color: 0xdddddd })) - pole.position.copy(toScene({ x: g.x + CELL / 2 - 1, y: g.y + CELL / 2 - 1 }, 4.5)) - const flag = new THREE.Mesh(new THREE.BoxGeometry(4, 2.6, 0.2), flagMaterial) - flag.position.copy(toScene({ x: g.x + CELL / 2 - 3, y: g.y + CELL / 2 - 1 }, 7.6)) - this.worldGroup.add(pole, flag) - } - // Farbige Felder für den Farbsensor - const fieldColors = { red: 0xe03131, green: 0x2fb344, blue: 0x1c7ed6, yellow: 0xfab005 } - for (const c of setup.colors ?? []) { - const cell = new THREE.Mesh(cellGeometry, new THREE.MeshStandardMaterial({ color: fieldColors[c.color] })) - cell.position.copy(toScene(c.at, 0.1)) - this.worldGroup.add(cell) - } - // Haltefelder: hellgrün, der Liniensensor sieht sie nicht - const targetMaterial = new THREE.MeshStandardMaterial({ color: 0x2fb344, transparent: true, opacity: 0.3, depthWrite: false }) - for (const t of setup.targets ?? []) { - const cell = new THREE.Mesh(cellGeometry, targetMaterial) - cell.position.copy(toScene(t, 0.12)) - this.worldGroup.add(cell) - } - const tape = new THREE.MeshStandardMaterial({ color: colors.tape }) - for (const line of setup.lines) { - for (let i = 0; i + 1 < line.length; i++) { - const a = line[i] - const b = line[i + 1] - const length = Math.hypot(b.x - a.x, b.y - a.y) + LINE_WIDTH - const strip = new THREE.Mesh(new THREE.BoxGeometry(length, 0.1, LINE_WIDTH), tape) - strip.position.copy(toScene({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }, 0.1)) - strip.rotation.y = Math.atan2(b.y - a.y, b.x - a.x) - this.worldGroup.add(strip) - } - } - } - - /** Roboter sofort an `pose` stellen (Neustart). */ - place(pose: Pose, trail: Point[]) { - this.anim = undefined - this.shown = { ...pose } - this.trail = trail.map((p) => ({ ...p })) - this.trailGroup.clear() - this.applyPose(this.shown) - } - - /** Zu `pose` fahren bzw. drehen, in `ms` Millisekunden; `trail` ist die Spur danach. */ - moveTo(pose: Pose, trail: Point[], ms: number) { - if (this.anim) this.shown = { ...this.anim.to } - this.trail = trail.map((p) => ({ ...p })) - let to = { ...pose } - // Kürzester Weg beim Drehen, aber volle Umdrehungen (turnRight(360)) bleiben sichtbar. - const diff = to.heading - this.shown.heading - if (diff > 180) to = { ...to, heading: to.heading - 360 } - else if (diff < -180) to = { ...to, heading: to.heading + 360 } - this.anim = { from: { ...this.shown }, to, start: performance.now(), ms: Math.max(1, ms) } - } - - /** Nur der Roboter auf einer runden Scheibe, z. B. zum Kennenlernen in einer Lektion. */ - showcase() { - this.worldGroup.clear() - const disc = new THREE.Mesh(new THREE.CircleGeometry(16, 48), new THREE.MeshStandardMaterial({ color: 0xe9e4da, roughness: 0.95 })) - disc.rotation.x = -Math.PI / 2 - disc.receiveShadow = true - this.worldGroup.add(disc) - this.place({ x: 0, y: 0, heading: 0 }, [{ x: 0, y: 0 }]) - this.controls.target.set(0, 5, -2) - this.camera.position.set(-20, 18, -24) - this.controls.update() - } - - /** Teile hervorheben (leicht leuchten lassen), leere Liste = keins. */ - highlight(part: RobotPart | RobotPart[] | undefined) { - const lit = new Set(part === undefined ? [] : Array.isArray(part) ? part : [part]) - for (const [name, group] of Object.entries(this.model.partGroups) as [RobotPart, THREE.Group][]) { - group.traverse((o) => { - const mesh = o as THREE.Mesh - if (!mesh.isMesh) return - const m = mesh.material as THREE.MeshStandardMaterial - // Beschriftungen (MeshBasicMaterial) leuchten nicht mit - if (!m.emissive) return - m.userData.baseEmissive ??= { color: m.emissive.getHex(), intensity: m.emissiveIntensity } - const base = m.userData.baseEmissive as { color: number; intensity: number } - if (lit.has(name)) { - m.emissive.setHex(0xffb000) - m.emissiveIntensity = Math.max(base.intensity, 0.22) - } else { - m.emissive.setHex(base.color) - m.emissiveIntensity = base.intensity - } - }) - } - } - - /** - * Kamera fährt zu den Teilen (leer = ganzer Roboter). `view`: Blickrichtung vorn, hinten, oben, unten, seite; - * sonst schräg von vorn links. `spin`: danach langsam um den Roboter kreisen. - */ - focusOn(parts: RobotPart[], view = '', spin = false, ms = 900) { - const box = new THREE.Box3() - this.robot.updateMatrixWorld(true) - for (const p of parts) box.expandByObject(this.model.partGroups[p]) - if (box.isEmpty()) box.setFromObject(this.robot) - const target = box.getCenter(new THREE.Vector3()) - const size = box.getSize(new THREE.Vector3()).length() - const direction = new THREE.Vector3( - ...(( - { - vorn: [0, 0.35, -1], - front: [0, 0.35, -1], - hinten: [0, 0.55, 1], - back: [0, 0.55, 1], - oben: [0, 1, 0.12], - top: [0, 1, 0.12], - unten: [0.05, -1, -0.3], - bottom: [0.05, -1, -0.3], - seite: [-1, 0.5, 0.15], - side: [-1, 0.5, 0.15], - } as Record - )[view] ?? [-0.75, 0.6, -0.85]), - ).normalize() - const distance = Math.max(14, size * (parts.length ? 1.8 : 1.35) + 6) - const pos = target.clone().addScaledVector(direction, distance) - this.flight = { - from: { pos: this.camera.position.clone(), target: this.controls.target.clone() }, - to: { pos, target }, - start: performance.now(), - ms, - } - this.controls.autoRotate = spin - this.controls.autoRotateSpeed = 1.2 - } - - /** Text auf dem Bildschirm des CyberPi; `undefined` = Startgesicht. */ - setScreen(text: string | undefined) { - if (text === this.shownScreen) return - this.shownScreen = text - drawScreen(this.model.screen.canvas, text) - this.model.screen.texture.needsUpdate = true - } - - private shownScreen?: string - - /** Alle LEDs auf dem CyberPi in einer Farbe; `undefined` = aus. */ - setLed(rgb: [number, number, number] | undefined) { - this.setLeds(this.model.leds.map(() => rgb)) - } - - /** - * Jede LED einzeln (Nummer 1 bis 5). LED 1 sitzt von hinten gesehen links, auf der Seite des Joysticks (am echten mBot2 bestätigt) - * (Modell: kleinstes x). - */ - setLeds(leds: readonly ([number, number, number] | undefined)[]) { - for (const [i, m] of this.model.leds.entries()) { - const rgb = leds[i] - if (rgb) { - m.emissive.setRGB(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255) - m.color.setRGB(rgb[0] / 255, rgb[1] / 255, rgb[2] / 255) - } else { - m.emissive.setRGB(0, 0, 0) - m.color.set(0xdddddd) - } - } - } - - /** Welches Teil liegt unter dem Punkt (Bildschirmkoordinaten)? */ - partAt(clientX: number, clientY: number): RobotPart | undefined { - const r = this.canvas.getBoundingClientRect() - const ray = new THREE.Raycaster() - ray.setFromCamera(new THREE.Vector2(((clientX - r.left) / r.width) * 2 - 1, -((clientY - r.top) / r.height) * 2 + 1), this.camera) - for (const hit of ray.intersectObject(this.robot, true)) { - for (let o: THREE.Object3D | null = hit.object; o; o = o.parent) { - const part = this.model.parts.get(o) - if (part) return part - } - } - return undefined - } - - - private fit() { - const w = Math.max(1, this.host.clientWidth) - const h = Math.max(1, this.host.clientHeight) - this.renderer.setSize(w, h, false) - this.camera.aspect = w / h - this.camera.updateProjectionMatrix() - } - - private loop = () => { - // Aus der Seite entfernt (z. B. andere Lektion): aufräumen statt weiterzuzeichnen. - if (this.canvas.isConnected) this.wasShown = true - else if (this.wasShown) return this.dispose() - this.frame = requestAnimationFrame(this.loop) - let current = this.shown - if (this.anim) { - const k = Math.min(1, (performance.now() - this.anim.start) / this.anim.ms) - const e = k < 0.5 ? 2 * k * k : 1 - (-2 * k + 2) ** 2 / 2 - const { from, to } = this.anim - current = { x: from.x + (to.x - from.x) * e, y: from.y + (to.y - from.y) * e, heading: from.heading + (to.heading - from.heading) * e } - if (k >= 1) { - this.shown = { ...to } - this.anim = undefined - } - } - this.applyPose(current) - const flying = !!this.flight - if (this.flight) { - const k = Math.min(1, (performance.now() - this.flight.start) / this.flight.ms) - const e = k < 0.5 ? 4 * k * k * k : 1 - (-2 * k + 2) ** 3 / 2 - const { from, to } = this.flight - this.controls.target.lerpVectors(from.target, to.target, e) - // Auf einem Bogen um das Ziel fliegen, nicht quer durch den Roboter - const a = from.pos.clone().sub(from.target) - const b = to.pos.clone().sub(to.target) - const dir = a.clone().normalize().lerp(b.clone().normalize(), e) - // Genau gegenüber (z. B. von oben nach unten): seitlich vorbei statt durch die Mitte - if (dir.lengthSq() < 1e-4) dir.set(-1, 0, 0) - dir.normalize() - const len = a.length() + (b.length() - a.length()) * e - this.camera.position.copy(this.controls.target).addScaledVector(dir, len) - if (k >= 1) this.flight = undefined - } - if (flying || this.controls.autoRotate) this.controls.update() - this.renderer.render(this.scene, this.camera) - } - - private applyPose(p: Pose) { - this.turnWheels(p) - this.robot.position.copy(toScene(p)) - this.robot.rotation.y = -rad(p.heading) - // Schatten folgt dem Roboter - this.sun.target.position.copy(this.robot.position) - this.sun.position.copy(this.robot.position).add(new THREE.Vector3(-40, 90, 50)) - // Spur bis zur gezeigten Position: das letzte Stück wächst mit, bis der Roboter ankommt. - const done = this.anim ? this.trail.length - 2 : this.trail.length - 1 - while (this.trailGroup.children.length > Math.max(0, done)) this.trailGroup.remove(this.trailGroup.children[this.trailGroup.children.length - 1]) - for (let i = this.trailGroup.children.length; i < done; i++) { - const piece = new THREE.Mesh(this.trailPiece, this.trailMaterial) - this.placeSegment(piece, this.trail[i], this.trail[i + 1]) - this.trailGroup.add(piece) - } - const from = this.anim ? this.trail[this.trail.length - 2] : undefined - this.liveSegment.visible = !!from - if (from) this.placeSegment(this.liveSegment, from, p) - } - - /** Räder drehen sich mit: vorwärts beide gleich, beim Drehen gegeneinander. */ - private turnWheels(p: Pose) { - const last = this.wheelPose - this.wheelPose = { ...p } - if (!last) return - const dx = p.x - last.x - const dy = p.y - last.y - const h = rad(last.heading) - const ds = dx * Math.sin(h) + dy * Math.cos(h) - const turn = rad(p.heading - last.heading) * (TRACK / 2) - const left = this.robot.getObjectByName('wheelLeft') - const right = this.robot.getObjectByName('wheelRight') - if (left) left.rotation.x -= (ds + turn) / WHEEL_RADIUS - if (right) right.rotation.x -= (ds - turn) / WHEEL_RADIUS - } - - private placeSegment(mesh: THREE.Object3D, a: Point, b: Point) { - const length = Math.hypot(b.x - a.x, b.y - a.y) - mesh.visible = length > 0.01 - mesh.scale.set(length + 1.4, 1, 1) - mesh.position.copy(toScene({ x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 }, 0.2)) - mesh.rotation.y = Math.atan2(b.y - a.y, b.x - a.x) - } -} diff --git a/web/src/ui/robotPreview.ts b/web/src/ui/robotPreview.ts deleted file mode 100644 index 343cbdc5..00000000 --- a/web/src/ui/robotPreview.ts +++ /dev/null @@ -1,253 +0,0 @@ -import type { VSDocument } from '../core/model' -import { previewRun, type PreviewFrame, type PreviewResult } from '../core/robotPreview' -import { emptyWorld, type RobotWorldSetup } from '../core/robotWorld' -import type { SubprogramResolver } from '../core/simulation' -import { t } from './i18n' -import { icons } from './icons' -import { sidebarIcon } from './learnPanel' -import type { Robot3D } from './robot3d' -import { noticeText } from './simulationPanel' -import { playTone } from './visubotView' - -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') - -/** Zeit je Bild beim Abspielen. */ -const frameMs = 300 - -/** - * VisuBot: „mBot2 live“ neben dem Bearbeiten. Nach jeder Änderung rechnet es das Programm im Hintergrund durch - * und zeigt, wo der mBot2 am Ende steht und welche Spur er fährt; ▶ spielt die Fahrt ab. - * Steht als eigene Spalte oder unter dem Code in „Code“ (`stacked`). - */ -export class RobotPreviewPanel { - readonly element: HTMLElement - /** Eingeklappt: schmale Leiste am rechten Rand. */ - readonly rail: HTMLButtonElement - /** Waagrechte Trennlinie über dem Fenster, wenn es unter dem Code steht. */ - readonly heightBar: HTMLElement - onHide = () => {} - onToggleStacked = () => {} - private stage: HTMLElement - private status: HTMLElement - private picker: HTMLElement - private playButton: HTMLButtonElement - private stackButton: HTMLButtonElement - private view?: Robot3D - private loading?: Promise - private worlds: RobotWorldSetup[] = [] - private worldsKey?: string - private setup: RobotWorldSetup = emptyWorld - private result?: PreviewResult - private lastKey?: string - private pending?: number - private playTimer?: number - private input?: { doc: VSDocument; resolve?: SubprogramResolver } - /** Während der Simulation: deren 3D-Ansicht mit Welten und Messwerten statt der eigenen. */ - private simulation?: HTMLElement - - constructor() { - this.element = document.createElement('aside') - this.element.className = 'robotpreview' - this.element.innerHTML = ` - -
    -

    🤖 ${esc(t('web_visubot_preview_title'))}

    - - - -
    -

    ${esc(t('web_visubot_preview_hint'))}

    - -
    -

    ` - const q = (s: string) => this.element.querySelector(s) as T - this.heightBar = q('.robotpreview-height') - this.stage = q('.robotpreview-stage') - this.status = q('.robotpreview-status') - this.picker = q('.visubot-worlds') - this.playButton = q('.robotpreview-play') - this.stackButton = q('.robotpreview-stack') - this.playButton.addEventListener('click', () => (this.playTimer === undefined ? this.play() : this.stopPlaying(true))) - this.stackButton.addEventListener('click', () => this.onToggleStacked()) - q('.learn-hide').addEventListener('click', () => this.onHide()) - this.rail = document.createElement('button') - this.rail.className = 'learn-rail robotpreview-rail' - this.setStacked(false) - this.relabel() - } - - /** - * Simulation läuft: ihre 3D-Ansicht (`viewport` aus dem Simulationspanel) hier zeigen, die eigene Vorschau ruht. - * `undefined`: wieder die Vorschau (die Ansicht der Simulation hängt der Aufrufer zurück). - */ - hostSimulation(viewport: HTMLElement | undefined) { - if (viewport === this.simulation) return - this.simulation?.remove() - this.simulation = viewport - this.element.classList.toggle('simulation', !!viewport) - if (viewport) { - this.pause() - this.status.after(viewport) - } - this.relabel() - } - - /** Nach einem Sprachwechsel: Beschriftungen neu. */ - relabel() { - const q = (s: string) => this.element.querySelector(s) as T - const sim = !!this.simulation - q('h2').textContent = `🤖 ${t(sim ? 'web_visubot_preview_sim_title' : 'web_visubot_preview_title')}` - q('.hint').textContent = t(sim ? 'web_visubot_preview_sim_hint' : 'web_visubot_preview_hint') - this.heightBar.title = t('web_visubot_preview_resize') - const hide = q('.learn-hide') - hide.title = t('web_visubot_preview_hide') - hide.setAttribute('aria-label', t('web_visubot_preview_hide')) - this.rail.title = t('web_visubot_preview_show') - this.rail.innerHTML = `${sidebarIcon}🤖 ${esc(t('web_visubot_preview_title'))}` - this.setStacked(this.stackButton.textContent === '▥') - this.stopPlaying(false) - this.worldsKey = undefined - this.lastKey = undefined - } - - /** Beschriftung des Knopfs zum Umstellen: unter den Code bzw. wieder als eigene Spalte. */ - setStacked(stacked: boolean) { - const key = stacked ? 'web_visubot_preview_side' : 'web_visubot_preview_stack' - this.stackButton.innerHTML = stacked ? '▥' : '⬓' - this.stackButton.className = 'icon robotpreview-stack' - this.stackButton.dataset.tip = t(key) - this.stackButton.setAttribute('aria-label', t(key)) - } - - /** 3D-Ansicht erst bauen, wenn das Fenster sichtbar ist. */ - private show() { - this.loading ??= import('./robot3d') - .then(({ Robot3D }) => { - this.view = new Robot3D(this.stage) - this.view.setWorld(this.setup) - this.showFrame(this.result?.frames.at(-1)) - }) - .catch(() => { - this.stage.textContent = t('web_visubot_no_3d') - }) - } - - /** - * Nach einer Änderung (nur wenn das Fenster offen ist): Programm neu durchrechnen, kurz verzögert, - * damit schnelles Tippen nicht bremst. `worlds`: Welten der geöffneten Übung (leer = freie Fläche). - */ - update(doc: VSDocument, resolve: SubprogramResolver | undefined, worlds: RobotWorldSetup[], worldsKey: string, allDocs: VSDocument[]) { - if (this.simulation) return - this.show() - if (worldsKey !== this.worldsKey) { - this.worldsKey = worldsKey - this.setWorlds(worlds) - } - this.input = { doc, resolve } - const key = JSON.stringify(allDocs) + '\n' + JSON.stringify(doc) - if (key === this.lastKey) return - this.lastKey = key - window.clearTimeout(this.pending) - this.pending = window.setTimeout(() => this.recompute(), 120) - } - - private setWorlds(worlds: RobotWorldSetup[]) { - this.worlds = worlds - this.picker.replaceChildren() - this.picker.hidden = worlds.length < 2 - worlds.forEach((setup, i) => { - const b = document.createElement('button') - b.textContent = t('web_visubot_world_fmt', i + 1) - b.addEventListener('click', () => this.useWorld(setup)) - this.picker.append(b) - }) - this.useWorld(worlds[0] ?? emptyWorld) - } - - private useWorld(setup: RobotWorldSetup) { - this.worlds.forEach((w, i) => this.picker.children[i]?.classList.toggle('active', w === setup)) - if (setup === this.setup && this.result) return - this.setup = setup - this.view?.setWorld(setup) - this.recompute() - } - - private recompute() { - this.pending = undefined - this.stopPlaying(false) - if (!this.input) return - try { - this.result = previewRun(this.input.doc, this.input.resolve, this.setup) - } catch { - this.result = undefined - } - this.showFrame(this.result?.frames.at(-1)) - this.updateStatus() - } - - private showFrame(frame: PreviewFrame | undefined, ms = 0) { - if (!this.view) return - const f = frame ?? { pose: { ...this.setup.start }, trail: [{ x: this.setup.start.x, y: this.setup.start.y }], leds: Array(5).fill(undefined), screen: undefined } - if (ms > 0) this.view.moveTo(f.pose, f.trail, ms) - else this.view.place(f.pose, f.trail) - this.view.setLeds(f.leds) - this.view.setScreen(f.screen) - } - - /** ▶: die Fahrt Bild für Bild vom Start an zeigen. */ - play() { - const frames = this.result?.frames - if (!frames || frames.length < 2 || !this.view) return - this.stopPlaying(false) - let i = 0 - this.showFrame(frames[0]) - this.playButton.innerHTML = icons.stop - this.playButton.dataset.tip = t('web_visubot_preview_stop') - this.playButton.setAttribute('aria-label', t('web_visubot_preview_stop')) - const next = () => { - i++ - if (i >= frames.length) { - this.stopPlaying(false) - return - } - const f = frames[i] - if (f.tone) playTone(f.tone[0], f.tone[1]) - this.showFrame(f, frameMs - 30) - this.playTimer = window.setTimeout(next, frameMs) - } - this.playTimer = window.setTimeout(next, frameMs) - } - - /** Abspielen beenden; `showEnd`: gleich das Ende der Fahrt zeigen. */ - private stopPlaying(showEnd: boolean) { - if (this.playTimer !== undefined) window.clearTimeout(this.playTimer) - this.playTimer = undefined - this.playButton.innerHTML = icons.play - this.playButton.dataset.tip = t('web_visubot_preview_play') - this.playButton.setAttribute('aria-label', t('web_visubot_preview_play')) - if (showEnd) this.showFrame(this.result?.frames.at(-1)) - } - - private updateStatus() { - const r = this.result - this.playButton.disabled = !r || r.frames.length < 2 - if (!r) { - this.status.textContent = '' - return - } - const parts = [t('web_visubot_preview_distance_fmt', r.distance)] - if (r.bumps > 0) parts.push(`⚠︎ ${t('web_visubot_bumps')}: ${r.bumps}`) - if (r.stop === 'input') parts.push(t('web_visubot_preview_input')) - else if (r.stop === 'limit') parts.push(t('web_visubot_preview_limit')) - else if (r.stop === 'error' && r.notice) parts.push(`⚠︎ ${noticeText(r.notice)}`) - this.status.textContent = parts.join(' · ') - } - - /** Beim Schließen: Abspielen und Rechnen anhalten. */ - pause() { - window.clearTimeout(this.pending) - this.pending = undefined - this.lastKey = undefined - this.stopPlaying(false) - } -} diff --git a/web/src/ui/roundGameView.ts b/web/src/ui/roundGameView.ts deleted file mode 100644 index 88a8d05b..00000000 --- a/web/src/ui/roundGameView.ts +++ /dev/null @@ -1,353 +0,0 @@ -import { inline } from '../learn/markdown' -import { bombParts, click, clickButton, parseLayoutPicture, parseSwingWindow, startState, type ClickStep, type RoundGame, type SwingWindow } from '../learn/roundGame' -import { blockPicture } from './blockPicture' -import { t } from './i18n' - -/** Rundenspiele „Wer hört zu?“, „Vertrag erfüllt?“, „Wo landet der Knopf?“, „Bombe entschärfen“ und „Wer ist dran?“ im Lernbereich. */ - -function h(tag: string, attrs: Record = {}, ...children: (Node | string)[]): HTMLElement { - const e = document.createElement(tag) - for (const [k, v] of Object.entries(attrs)) e.setAttribute(k, v) - e.append(...children) - return e -} - -function rich(tag: string, attrs: Record, markdown: string): HTMLElement { - const e = h(tag, attrs) - e.innerHTML = inline(markdown) - return e -} - -const stepDelay = 1100 - -/** `asBlocks`: Code der Runden bei „frage“ als Blöcke zeigen (Block-Lektionen für Jüngere), sonst als Java. */ -export function renderRoundGame(game: RoundGame, ludwigURL: string, asBlocks = false): HTMLElement { - const box = h('section', { class: `learn-game learn-round-game ${game.kind}` }) - const head = h('div', { class: 'learn-game-head' }) - head.append(h('img', { src: ludwigURL, alt: '', width: '40', height: '40' }), h('strong', {}, game.title)) - const status = h('p', { class: 'learn-game-status' }) - const stage = h('div', { class: 'learn-game-stage' }) - box.append(head, status, stage) - const total = game.rounds.length - let index = 0 - let score = 0 - let timers: number[] = [] - const clearTimers = () => { - timers.forEach((id) => { - window.clearTimeout(id) - window.clearInterval(id) - }) - timers = [] - } - const showStatus = () => { - status.textContent = `${t('web_game_round_fmt', index + 1, total)} · ${t('web_game_score_fmt', score)}` - } - - const next = () => { - const b = h('button', { class: 'primary' }, index + 1 < total ? t('web_game_next') : t('web_game_finish')) - b.addEventListener('click', () => { - index++ - if (index < total) showRound() - else showEnd() - }) - return b - } - - /** Bombe: Auf die falsche Zeile tippen, bevor der Countdown abläuft. */ - const showBombRound = () => { - const round = game.rounds[index] - const seconds = game.seconds! - const fuse = h('div', { class: 'learn-bomb-fuse' }, h('span', {})) - const left = h('span', { class: 'learn-bomb-time' }) - const bomb = h('div', { class: 'learn-bomb', 'aria-live': 'polite' }, h('span', { class: 'learn-bomb-icon', 'aria-hidden': 'true' }, '💣'), left, fuse) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - // Mit `stelle:` tippt man Teile der Zeilen an (z. B. nur `i++`), sonst ganze Zeilen. - const byParts = round.wrongPart !== undefined - const targets: { button: HTMLButtonElement; line: number; right: boolean }[] = [] - const rows = round.code.split('\n').map((line, i) => { - if (!byParts) { - const b = h('button', { class: 'ln', type: 'button' }, line || ' ') as HTMLButtonElement - b.disabled = !line.trim() - targets.push({ button: b, line: i + 1, right: i + 1 === round.wrongLine }) - return b - } - const row = h('div', { class: 'ln parts' }) - if (!line.trim()) row.append(' ') - for (const seg of bombParts(line)) { - if (!seg.part) { - row.append(seg.text) - continue - } - const b = h('button', { class: 'part', type: 'button' }, seg.text) as HTMLButtonElement - targets.push({ button: b, line: i + 1, right: i + 1 === round.wrongLine && seg.text === round.wrongPart }) - row.append(b) - } - return row - }) - for (const target of targets) - target.button.addEventListener('click', () => finish(target.right ? 'right' : target.line === round.wrongLine ? 'part' : 'wrong', target.button)) - const code = h('div', { class: `learn-game-code learn-bomb-code mono${byParts ? ' by-parts' : ''}`, dir: 'ltr' }, ...rows) - const started = Date.now() - const tick = () => { - // Lektion verlassen: Countdown anhalten - if (!box.isConnected && Date.now() - started > 1000) return clearTimers() - const rest = Math.max(0, seconds - (Date.now() - started) / 1000) - left.textContent = t('web_game_time_fmt', Math.ceil(rest)) - ;(fuse.firstChild as HTMLElement).style.width = `${(rest / seconds) * 100}%` - bomb.classList.toggle('hurry', rest <= 5) - if (rest <= 0) finish('time') - } - const finish = (how: 'right' | 'wrong' | 'part' | 'time', chosen?: HTMLButtonElement) => { - clearTimers() - for (const target of targets) { - target.button.disabled = true - if (target.right) target.button.classList.add('right') - } - if (byParts) rows[round.wrongLine! - 1].classList.add('right') - if (chosen && how !== 'right') chosen.classList.add('wrong') - const right = how === 'right' - if (right) score++ - showStatus() - bomb.classList.add(right ? 'defused' : 'boom') - bomb.firstChild!.textContent = right ? '✂️' : '💥' - feedback.className = `learn-game-feedback ${right ? 'right' : 'wrong'}` - const n = next() - feedback.replaceChildren( - h('p', {}, t(right ? 'web_game_defused' : how === 'time' ? 'web_game_boom_time' : how === 'part' ? 'web_game_boom_part' : byParts ? 'web_game_boom_wrong_part' : 'web_game_boom_wrong')), - rich('div', { class: 'learn-game-explain' }, round.explanation), - h('p', { class: 'learn-bomb-fix' }, `${t('web_game_fix')} `, h('code', {}, round.fix!.trim())), - n, - ) - n.focus({ preventScroll: true }) - } - const question = rich('p', { class: 'learn-game-question' }, round.question) - if (byParts) question.append(' ', h('span', { class: 'learn-bomb-hint' }, t('web_game_bomb_parts_hint'))) - stage.replaceChildren(bomb, question, code, feedback) - tick() - const id = window.setInterval(tick, 200) - timers.push(id) - } - - /** Reihenfolge: Die gemischten Schritte der Reihe nach antippen, ein falscher Tipp beendet die Runde. */ - const showOrderRound = () => { - const round = game.rounds[index] - const parts: HTMLElement[] = [] - if (round.code.trim()) parts.push(codeView(round.code).element) - parts.push(rich('p', { class: 'learn-game-question' }, round.question), h('p', { class: 'learn-order-hint' }, t('web_game_order_hint'))) - const list = h('div', { class: 'learn-game-options learn-order' }) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - let done = 0 - const buttons = shuffled(round.options.length).map((k) => { - const badge = h('span', { class: 'learn-order-badge', 'aria-hidden': 'true' }) - const text = h('span', {}) - text.innerHTML = inline(round.options[k].text) - const b = h('button', {}, badge, text) as HTMLButtonElement - b.addEventListener('click', () => { - if (k !== done) { - b.classList.add('wrong') - return finish(false) - } - b.classList.add('right') - badge.textContent = String(++done) - b.disabled = true - if (done === round.options.length) finish(true) - }) - list.append(b) - return { k, b, badge } - }) - const finish = (right: boolean) => { - // Zum Schluss stehen die Schritte richtig sortiert und nummeriert da. - for (const { k, b, badge } of [...buttons].sort((x, y) => x.k - y.k)) { - b.disabled = true - badge.textContent = String(k + 1) - list.append(b) - } - if (right) score++ - showStatus() - feedback.className = `learn-game-feedback ${right ? 'right' : 'wrong'}` - const n = next() - feedback.replaceChildren(h('p', {}, right ? t('web_game_right') : t('web_game_wrong_short')), rich('div', { class: 'learn-game-explain' }, round.explanation), n) - n.focus({ preventScroll: true }) - } - stage.replaceChildren(...parts, list, feedback) - } - - const showRound = () => { - clearTimers() - if (game.kind === 'bombe' || game.kind === 'reihenfolge') { - showStatus() - if (game.kind === 'bombe') showBombRound() - else showOrderRound() - return - } - const round = game.rounds[index] - showStatus() - const code = codeView(round.code) - const parts: HTMLElement[] = [] - let swing: ReturnType | undefined - if (game.kind === 'zuhoerer') { - swing = swingView(parseSwingWindow(round.code), code) - parts.push(h('div', { class: 'learn-swing-row' }, code.element, swing.element)) - if (round.clicks.length) parts.push(h('p', { class: 'learn-swing-clicks' }, `${t('web_game_clicks')} `, ...round.clicks.flatMap((c, i) => [...(i ? [' → '] : []), h('code', {}, c)]))) - } else if (round.code.trim()) parts.push((asBlocks && game.kind === 'frage' && blockPicture(round.code)) || code.element) - parts.push(rich('p', { class: 'learn-game-question' }, round.question)) - const answers = h('div', { class: `learn-game-options ${game.kind === 'layout' ? 'pictures' : ''}` }) - const feedback = h('div', { class: 'learn-game-feedback', role: 'status' }) - const buttons = round.options.map((option) => { - const b = h('button', {}) as HTMLButtonElement - if (game.kind === 'layout') b.append(layoutPicture(option.text)) - else if (option.text.includes('\n')) b.append(filePicture(option.text)) - else b.innerHTML = inline(option.text) - b.addEventListener('click', () => answer(option.right, b)) - answers.append(b) - return b - }) - const answer = (right: boolean, chosen: HTMLButtonElement) => { - buttons.forEach((b, i) => { - b.disabled = true - if (round.options[i].right) b.classList.add('right') - }) - if (!right) chosen.classList.add('wrong') - if (right) score++ - showStatus() - feedback.className = `learn-game-feedback ${right ? 'right' : 'wrong'}` - const n = next() - feedback.replaceChildren(h('p', {}, right ? t('web_game_right') : t('web_game_wrong_short')), rich('div', { class: 'learn-game-explain' }, round.explanation)) - feedback.append(n) - if (swing) timers = swing.replay(round.clicks, () => n.before(h('p', { class: 'learn-swing-try' }, t('web_game_try')))) - n.focus({ preventScroll: true }) - } - stage.replaceChildren(...parts, answers, feedback) - } - - const showEnd = () => { - clearTimers() - status.textContent = '' - const again = h('button', { class: 'primary' }, t('web_game_again')) - again.addEventListener('click', () => { - index = score = 0 - showRound() - }) - stage.replaceChildren( - h('p', { class: 'learn-game-result' }, t('web_game_done_fmt', score, total)), - h('p', {}, t(score === total ? 'web_game_perfect' : score >= total * 0.7 ? 'web_game_good' : 'web_game_practice_short')), - again, - ) - } - const start = h('button', { class: 'primary' }, t('web_game_start')) - start.addEventListener('click', showRound) - stage.append(rich('p', {}, game.intro), start) - return box -} - -/** Die Zahlen 0 bis n-1 gemischt, nie in der richtigen Reihenfolge. */ -function shuffled(n: number): number[] { - const order = [...Array(n).keys()] - while (order.every((k, i) => k === i)) { - for (let i = n - 1; i > 0; i--) { - const j = Math.floor(Math.random() * (i + 1)) - ;[order[i], order[j]] = [order[j], order[i]] - } - } - return order -} - -function codeView(code: string) { - const lines = code.split('\n').map((line) => h('div', { class: 'ln' }, line || ' ')) - const element = h('pre', { class: 'learn-game-code mono', dir: 'ltr' }, ...lines) - return { - element, - highlight(from?: number, to?: number) { - lines.forEach((l, i) => l.classList.toggle('hit', from !== undefined && i >= from && i <= (to ?? from))) - }, - } -} - -/** Nachgebautes Fenster mit Knöpfen und Beschriftungen; spielt Klicks ab. */ -function swingView(win: SwingWindow, code: ReturnType) { - const state = startState(win) - const element = h('div', { class: 'learn-swing-window', dir: 'ltr' }) - element.append(h('div', { class: 'learn-swing-title' }, win.title || ' ')) - const body = h('div', { class: 'learn-swing-body' }) - const views = new Map() - for (const l of win.labels) { - const v = h('div', { class: 'learn-swing-label' }, state.texts[l.name]) - views.set(l.name, v) - body.append(v) - } - const buttons = win.buttons.map((b) => { - const v = h('button', { class: 'learn-swing-button', disabled: '' }, state.texts[b.name]) as HTMLButtonElement - views.set(b.name, v) - body.append(v) - return v - }) - const event = h('p', { class: 'learn-swing-event', role: 'status' }) - const consoleBox = h('pre', { class: 'learn-swing-console mono', hidden: '' }) - element.append(body, event, consoleBox) - - const show = (step: ClickStep) => { - const view = views.get(step.button)! - view.classList.add('pressed') - window.setTimeout(() => view.classList.remove('pressed'), 350) - code.highlight(step.listener?.from, step.listener?.to) - event.textContent = step.listener ? t('web_game_event_fmt', view.textContent ?? '') : t('web_game_nobody') - event.className = `learn-swing-event ${step.listener ? 'heard' : 'nobody'}` - for (const [name, text] of Object.entries(step.texts)) { - const v = views.get(name) - if (v && v.textContent !== text) { - v.textContent = text - v.classList.remove('changed') - void v.offsetWidth // Animation neu starten - v.classList.add('changed') - } - } - if (step.console.length) { - consoleBox.hidden = false - consoleBox.textContent += step.console.map((l) => `${l}\n`).join('') - } - } - const press = (caption: string) => show(click(win, state, caption)) - - return { - element, - /** Die Klicks der Runde nacheinander abspielen, danach die Knöpfe zum Ausprobieren freigeben. */ - replay(clicks: string[], done: () => void): number[] { - const ids = clicks.map((c, i) => window.setTimeout(() => press(c), (i + 0.3) * stepDelay)) - ids.push( - window.setTimeout(() => { - win.buttons.forEach((b, i) => { - buttons[i].disabled = false - buttons[i].addEventListener('click', () => show(clickButton(win, state, b.name))) - }) - done() - }, (clicks.length + 0.3) * stepDelay), - ) - return ids - }, - } -} - -/** Mehrzeilige Antwort als kleine Datei; `(leer)` u. Ä. ohne Zeilen bleibt Text. */ -function filePicture(text: string): HTMLElement { - return h('pre', { class: 'learn-game-file mono', dir: 'ltr' }, text) -} - -/** Kleines Fenster als Antwort: GridLayout, FlowLayout oder BorderLayout. */ -function layoutPicture(text: string): HTMLElement { - const pic = parseLayoutPicture(text) - const frame = h('div', { class: 'learn-layout-window', dir: 'ltr' }, h('div', { class: 'learn-layout-title' })) - const body = h('div', { class: `learn-layout-body ${pic.kind}` }) - const cell = (name: string, area?: string) => h('span', { class: 'learn-layout-cell', ...(area ? { style: `grid-area:${area}` } : {}) }, name) - if (pic.kind === 'border') { - for (const [area, name] of Object.entries(pic.areas)) body.append(cell(name!, area)) - } else if (pic.kind === 'grid') { - const cols = Math.max(...pic.rows.map((r) => r.length)) - body.style.gridTemplateColumns = `repeat(${cols}, 1fr)` - for (const row of pic.rows) for (let i = 0; i < cols; i++) body.append(row[i] ? cell(row[i]) : h('span', {})) - } else { - for (const row of pic.rows) body.append(h('div', { class: 'learn-layout-row' }, ...row.map((n) => cell(n)))) - } - frame.append(body) - return frame -} - diff --git a/web/src/ui/sequenceDiagram.ts b/web/src/ui/sequenceDiagram.ts deleted file mode 100644 index d8d9ceea..00000000 --- a/web/src/ui/sequenceDiagram.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * VisuObject: Sequenzdiagramm aus dem Ablauf des Debuggers. Jedes Objekt (bzw. jede Klasse bei - * static) hat eine Lebenslinie; Aufrufe sind Pfeile mit gefüllter Spitze, Rückgaben gestrichelt, - * `new` ein gestrichelter «create»-Pfeil zum Kasten, der erst dort beginnt. Aktivierungsbalken - * zeigen, solange eine Methode läuft. - */ - -import type { SeqEvent, SeqParticipant } from '../core/miniJava' - -const e = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) - -const colW = 180 -const left = 30 -const top = 12 -const headH = 30 -const rowH = 34 -const barW = 10 - -export interface SequenceData { - participants: SeqParticipant[] - events: SeqEvent[] - truncated: boolean -} - -/** Etwa die Breite eines Texts in 12px-Schrift (für Kästen und Schleifen). */ -const textWidth = (s: string) => s.length * 7 + 16 - -/** Diagramm als SVG; `current` = Nummer des zuletzt ausgeführten Ereignisses (gelb). */ -export function sequenceSVG(data: SequenceData, current?: number): { svg: string; width: number; height: number } { - const index = new Map(data.participants.map((p, i) => [p.key, i])) - const cx = (key: string) => left + (index.get(key) ?? 0) * colW + colW / 2 - const created = new Map() - const parts: string[] = [] - const bars: string[] = [] - const heads: string[] = [] - // Offene Aktivierungen: Teilnehmer, Aufrufer, Beginn, Ebene (verschachtelt auf derselben Linie). - const open: { key: string; caller: string; y: number; level: number }[] = [] - const mainKey = data.participants[0]?.isStatic ? data.participants[0].key : '' - // Ebene der Aktivierung; main hat seinen Balken von Anfang an. - const level = (key: string) => open.filter((a) => a.key === key).length + (key === mainKey ? 1 : 0) - const barX = (key: string, lv: number) => cx(key) - barW / 2 + Math.max(0, lv - 1) * 6 - let y = top + headH + 24 - data.events.forEach((ev, i) => { - const on = i === current ? ' current' : '' - if (ev.k === 'return') { - const a = open.pop() - if (!a) return - if (ev.hidden) { - bars.push(``) - return - } - const from = barX(a.key, a.level) - const toLevel = level(a.caller) - const toX = a.caller === a.key ? barX(a.caller, toLevel) + barW : toLevel ? barX(a.caller, toLevel) + (cx(a.caller) < cx(a.key) ? barW : 0) : cx(a.caller) - const x1 = cx(a.caller) < cx(a.key) ? from : from + barW - bars.push(``) - if (a.caller !== a.key) - parts.push( - `${arrowOpen(toX, y, toX < x1 ? -1 : 1)}${ev.label ? `${e(ev.label)}` : ''}`, - ) - y += rowH - return - } - const fromLevel = level(ev.from) - const self = ev.from === ev.to - // Neuer Kasten braucht Platz: Pfeil in seine Mitte, darüber Luft für die Beschriftung. - if (ev.k === 'create') { - y += headH / 2 + 8 - created.set(ev.to, y) - } - const toLevel = level(ev.to) + 1 - const fx = fromLevel ? barX(ev.from, fromLevel) + (cx(ev.to) >= cx(ev.from) ? barW : 0) : cx(ev.from) - if (self) { - const x = fx - const bx = barX(ev.to, toLevel) + barW - parts.push( - `${arrowFilled(bx, y + 4, -1)}${e(ev.label)}`, - ) - open.push({ key: ev.to, caller: ev.from, y: y + 4 - 6, level: toLevel }) - y += rowH - return - } - const right = cx(ev.to) > cx(ev.from) - const tx = ev.k === 'create' ? cx(ev.to) + (right ? -textWidth(labelOf(data, ev.to)) / 2 : textWidth(labelOf(data, ev.to)) / 2) : barX(ev.to, toLevel) + (right ? 0 : barW) - const cls = ev.k === 'create' ? ' create' : '' - const label = ev.k === 'create' ? `«create» ${ev.label}` : ev.label - parts.push( - `${ev.k === 'create' ? arrowOpen(tx, y, right ? 1 : -1) : arrowFilled(tx, y, right ? 1 : -1)}${ev.k === 'create' ? `${e(label)}` : `${e(label)}`}`, - ) - open.push({ key: ev.to, caller: ev.from, y: ev.k === 'create' ? y + headH / 2 : y, level: toLevel }) - y += ev.k === 'create' ? rowH + headH / 2 : rowH - }) - // Noch laufende Methoden (angehalten): Balken bis unten. - for (const a of open) bars.push(``) - const bottom = y + 10 - // main läuft die ganze Zeit: Balken auf der ersten Lebenslinie. - const main = data.participants[0] - if (main?.isStatic) bars.unshift(``) - for (const p of data.participants) { - const x = cx(p.key) - const head = created.get(p.key) - const hy = head !== undefined ? head - headH / 2 : top - const w = Math.min(colW - 12, textWidth(p.label)) - heads.push( - `${e(p.label)}`, - ) - } - const width = left * 2 + data.participants.length * colW - const height = bottom + 20 - return { svg: `${heads.join('')}${bars.join('')}${parts.join('')}`, width, height } -} - -function labelOf(data: SequenceData, key: string) { - return data.participants.find((p) => p.key === key)?.label ?? '' -} - -function arrowFilled(x: number, y: number, dir: number) { - return `` -} - -function arrowOpen(x: number, y: number, dir: number) { - return `` -} diff --git a/web/src/ui/simulationPanel.ts b/web/src/ui/simulationPanel.ts deleted file mode 100644 index 5a490f20..00000000 --- a/web/src/ui/simulationPanel.ts +++ /dev/null @@ -1,427 +0,0 @@ -import { usedMBotSensors } from '../core/mbot' -import type { VSDocument } from '../core/model' -import { describeValue, SimulationEngine, type SimNotice, type SimValue, type SubprogramResolver } from '../core/simulation' -import { currentLanguage, t } from './i18n' -import { writtenFilesView } from './textFileView' -import type { VisuBotView } from './visubotView' - -/** Was eine Konsole zeigen würde: Ausgaben, Eingaben mit Antwort, offene Frage, Ende oder Fehler. */ -export interface ConsoleTranscript { - lines: { text: string; typed?: string }[] - /** Frage einer Eingabe, auf die die Simulation gerade wartet. */ - pending?: string - notice?: string - error: boolean -} - -/** `sim_notice_*` aus Localizable.strings. */ -export function noticeText(n: SimNotice): string { - switch (n.kind) { - case 'finished': return t('sim_notice_finished') - case 'infiniteLoopWithoutBody': return t('sim_notice_infinite_loop_empty') - case 'unsupportedStatement': return t('sim_notice_unsupported_statement_fmt', n.text) - case 'invalidExpression': return t('sim_notice_invalid_expression_fmt', n.text) - case 'unknownVariable': return n.similar ? t('web_sim_unknown_variable_case_fmt', n.text, n.similar) : t('sim_notice_unknown_variable_fmt', n.text) - case 'divisionByZero': return t('sim_notice_division_by_zero') - case 'wholeNumberExpected': return t('sim_notice_whole_number_expected') - case 'numberExpected': return t('sim_notice_number_expected') - case 'booleanExpected': return t('sim_notice_boolean_expected') - case 'subprogramNotFound': return t('sim_notice_subprogram_not_found_fmt', n.text) - case 'callTooDeep': return t('sim_notice_call_too_deep') - case 'callArguments': return t('web_sim_call_arguments_fmt', n.text) - case 'missingReturn': return t('web_sim_missing_return_fmt', n.text) - case 'fileNotFound': return t('web_sim_file_not_found_fmt', n.text) - case 'endOfFile': return t('web_sim_end_of_file_fmt', n.text) - case 'nullValue': return t('web_sim_null_value_fmt', n.text) - case 'other': return n.text - } -} - -const esc = (s: string) => s.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') - -/** Port von `SimulationController.swift` und `SimulationPanels.swift`: Steuerung, Eingabe, Variablen, Ausgabe. */ - -type HighlightMode = 'lastExecuted' | 'nextToExecute' - -const delayOptions = ['0.2', '0.5', '1.0', '1.5', '2.0'] -const decimal = (d: string) => Number(d).toLocaleString(currentLanguage() === 'java' ? 'en' : currentLanguage(), { minimumFractionDigits: 1 }) -const delayKey = 'visustruct.simulation.stepDelay' -const highlightKey = 'visustruct.simulation.highlightMode' - -function stored(key: string): string | null { - try { - return localStorage.getItem(key) - } catch { - return null - } -} - -function store(key: string, value: string) { - try { - localStorage.setItem(key, value) - } catch { - /* ohne Speicher: nur für diese Sitzung */ - } -} - -export class SimulationPanel { - readonly element: HTMLElement - private engine: SimulationEngine - private isPlaying = false - private playTimer?: number - private wasPlayingBeforeInput = false - private delay = delayOptions.includes(stored(delayKey) ?? '') ? stored(delayKey)! : '0.5' - private highlightMode: HighlightMode = stored(highlightKey) === 'nextToExecute' ? 'nextToExecute' : 'lastExecuted' - - private buttons!: Record<'play' | 'pause' | 'step' | 'stop', HTMLButtonElement> - private inputSection!: HTMLElement - private trace!: HTMLElement - private variables!: HTMLElement - private output!: HTMLElement - private transcript: ConsoleTranscript['lines'] = [] - private printed = 0 - private notice!: HTMLElement - private editButton!: HTMLButtonElement - /** VisuBot: Roboter-Welt, die bei jedem Schritt mitfährt. */ - private robot?: VisuBotView - /** Echter mBot2 fährt gerade einen Schritt. */ - private liveBusy = false - - constructor( - private currentDocument: () => VSDocument, - /** Markierung des aktuellen Schritts im Diagramm. */ - private onHighlight: (path: number[] | undefined) => void, - /** Diagramm zu einem Aufruf-Kasten, z. B. ein anderer offener Reiter. */ - private findSubprogram: SubprogramResolver = () => undefined, - /** Simulation beenden und weiter bearbeiten. */ - private onEdit: () => void = () => {}, - /** Konsole in Code live: zeigt dieselben Ausgaben und Eingaben wie beim Ausführen. */ - private onTranscript: (transcript: ConsoleTranscript) => void = () => {}, - ) { - this.engine = new SimulationEngine(currentDocument(), findSubprogram) - this.element = document.createElement('aside') - this.element.className = 'simpanel' - this.build() - } - - // MARK: - Modus - - /** VisuBot ein- oder ausschalten: die Welt steht unter der Steuerung. */ - setRobot(robot: VisuBotView | undefined) { - if (this.robot === robot) return - this.robot?.element.remove() - this.robot = robot - if (robot) { - this.element.querySelector('section')!.after(robot.element) - robot.onWorldChange = () => this.restartEngine() - robot.reset() - } - } - - /** Beim Öffnen der Simulation sichtbar: erst dann wird die 3D-Ansicht gebaut. */ - showRobot() { - this.robot?.show() - } - - begin() { - this.pause() - this.restartEngine() - this.showRobot() - } - - end() { - this.pause() - } - - // MARK: - Steuerung - - step() { - if (this.robot?.live) { - void this.stepLive() - return - } - if (!this.engine.canStep) { - this.pause() - return - } - const wasPlaying = this.isPlaying - const lastExecutedPath = this.engine.currentStepPath - this.robot?.prepare(this.engine) - this.engine.step() - this.robot?.follow(this.engine.state.robotActions, wasPlaying ? Number(this.delay) * 850 : 450) - this.afterStep(wasPlaying, lastExecutedPath) - } - - /** Echter mBot2: Sensoren lesen, Schritt ausführen, warten bis er fertig gefahren ist. */ - private async stepLive() { - if (this.liveBusy) return - if (!this.engine.canStep) { - this.pause() - return - } - this.liveBusy = true - const robot = this.robot! - const wasPlaying = this.isPlaying - const lastExecutedPath = this.engine.currentStepPath - try { - await robot.prepareLive(this.engine, usedMBotSensors(JSON.stringify(this.currentDocument()))) - this.engine.step() - robot.follow(this.engine.state.robotActions, 450) - await robot.drive(this.engine.state.robotActions) - } catch (e) { - robot.liveFailed(e) - this.isPlaying = false - } finally { - this.liveBusy = false - } - this.afterStep(wasPlaying, lastExecutedPath) - } - - private afterStep(wasPlaying: boolean, lastExecutedPath: number[] | undefined) { - this.collectOutput() - if (this.highlightMode === 'nextToExecute' && this.engine.canStep) { - this.onHighlight(this.engine.currentStepPath) - } else { - // Letztes ausgeführtes Element bleibt markiert (auch am Ende oder bei einer Eingabe). - this.onHighlight(lastExecutedPath ?? this.engine.currentStepPath) - } - // Eingabe angefordert: Abspielen pausieren, nach OK automatisch weiterlaufen. - if (this.engine.state.inputRequest && wasPlaying) this.wasPlayingBeforeInput = true - if (!this.engine.canStep) this.pause() - this.update() - } - - reset() { - this.pause() - this.restartEngine() - } - - play() { - if (this.isPlaying || !this.engine.canStep) return - this.isPlaying = true - const tick = async () => { - this.playTimer = undefined - if (!this.isPlaying) return - if (this.robot?.live) await this.stepLive() - else this.step() - if (this.isPlaying && this.engine.canStep) { - this.playTimer = window.setTimeout(() => void tick(), Number(this.delay) * 1000) - } else { - this.isPlaying = false - this.update() - } - } - void tick() - } - - pause() { - if (this.playTimer !== undefined) window.clearTimeout(this.playTimer) - this.playTimer = undefined - this.isPlaying = false - this.update() - } - - private submitInput(field: HTMLInputElement) { - const shouldResume = this.isPlaying || this.wasPlayingBeforeInput - this.pause() - const prompt = this.engine.state.inputRequest?.prompt ?? '' - const continuesLine = this.engine.state.inputRequest?.continuesLine - this.engine.provideInput(field.value) - if (!this.engine.state.inputRequest) { - const last = this.transcript[this.transcript.length - 1] - if (continuesLine && last && last.typed === undefined) last.typed = field.value - else this.transcript.push({ text: prompt ? `${prompt} ` : '', typed: field.value }) - field.value = '' - } - this.onHighlight(this.engine.currentStepPath) - if (shouldResume && this.engine.canStep) { - this.wasPlayingBeforeInput = false - this.play() - } - this.update() - } - - private restartEngine() { - this.engine = new SimulationEngine(this.currentDocument(), this.findSubprogram) - this.transcript = [] - this.printed = 0 - this.wasPlayingBeforeInput = false - this.robot?.reset() - this.onHighlight(this.engine.currentStepPath) - this.update() - } - - // MARK: - Ansicht - - private build() { - this.element.innerHTML = ` -
    -

    ${esc(t('simulation_controls'))}

    -
    - - - - -
    - - -
    -

    - -

    ${esc(t('simulation_input'))}

    -

    ${esc(t('simulation_trace'))}

    -

    ${esc(t('simulation_variables'))}

    -

    ${esc(t('simulation_output'))}

    - ` - const q = (s: string) => this.element.querySelector(s) as T - this.buttons = { - play: q('[data-sim=play]'), - pause: q('[data-sim=pause]'), - step: q('[data-sim=step]'), - stop: q('[data-sim=stop]'), - } - this.buttons.play.addEventListener('click', () => this.play()) - this.buttons.pause.addEventListener('click', () => this.pause()) - this.buttons.step.addEventListener('click', () => this.step()) - this.buttons.stop.addEventListener('click', () => this.reset()) - this.editButton = q('.sim-edit') - this.editButton.addEventListener('click', () => this.onEdit()) - - const highlight = q('[data-sim=highlight]') - highlight.value = this.highlightMode - highlight.addEventListener('change', () => { - this.highlightMode = highlight.value as HighlightMode - store(highlightKey, this.highlightMode) - }) - const delay = q('[data-sim=delay]') - delay.value = this.delay - delay.addEventListener('change', () => { - this.delay = delay.value - store(delayKey, this.delay) - }) - - this.inputSection = q('.sim-input > div') - this.trace = q('.sim-trace') - this.variables = q('.sim-vars') - this.output = q('.sim-output') - this.notice = q('.sim-notice') - } - - private collectOutput() { - const lines = this.engine.state.outputLines - for (; this.printed < lines.length; this.printed++) this.transcript.push({ text: lines[this.printed] }) - } - - private update() { - if (!this.buttons) return - const { state, canStep } = this.engine - this.onTranscript({ - lines: this.transcript, - pending: state.inputRequest?.continuesLine ? undefined : state.inputRequest?.prompt, - notice: state.notice ? noticeText(state.notice) : undefined, - error: state.isHalted, - }) - this.buttons.play.disabled = this.isPlaying || !canStep - this.buttons.pause.disabled = !this.isPlaying - this.buttons.step.disabled = this.isPlaying || !canStep - - this.notice.textContent = state.notice ? noticeText(state.notice) : '' - this.notice.hidden = !state.notice - this.notice.classList.toggle('error', state.isHalted) - // Nach einem Fehler geht es nur mit Bearbeiten weiter; der Knopf steht direkt unter der Meldung. - this.editButton.hidden = !state.isHalted - - this.updateInput() - - this.trace.textContent = state.lastTrace ?? t('simulation_trace_placeholder') - this.trace.classList.toggle('placeholder', !state.lastTrace) - - const names = [...state.variables.keys()].sort((a, b) => a.localeCompare(b)) - if (names.length === 0) { - this.variables.innerHTML = `

    ${esc(t('simulation_variables_placeholder'))}

    ` - } else { - const table = document.createElement('table') - for (const name of names) { - const row = table.insertRow() - row.insertCell().textContent = name - const value = state.variables.get(name)! - const cell = row.insertCell() - // 2D-Array wie eine Tabelle: jede Zeile in einer eigenen Zeile - if (value.kind === 'grid') { - cell.append(gridTable(value)) - } else { - cell.textContent = describeValue(value) - } - } - this.variables.replaceChildren(table) - } - - if (state.outputLines.length === 0 && state.files.size === 0) { - this.output.innerHTML = `

    ${esc(t('simulation_output_placeholder'))}

    ` - } else { - this.output.replaceChildren( - ...state.outputLines.map((line) => { - const div = document.createElement('div') - div.textContent = line - return div - }), - ...writtenFilesView(state.files), - ) - this.output.scrollTop = this.output.scrollHeight - } - } - - private updateInput() { - const request = this.engine.state.inputRequest - const host = this.inputSection - host.parentElement!.classList.toggle('active', !!request) - if (!request) { - host.innerHTML = `

    ${esc(t('simulation_input_placeholder'))}

    ` - return - } - // Feld behalten, damit Tippen und Fokus nicht verloren gehen. - let form = host.querySelector('form') - if (!form) { - form = document.createElement('form') - form.innerHTML = `

    ` - const field = form.querySelector('input')! - form.addEventListener('submit', (e) => { - e.preventDefault() - this.submitInput(field) - }) - host.replaceChildren(form) - requestAnimationFrame(() => field.focus()) - } - // Ohne eigene Frage fragt die Ausgabe direkt davor (z. B. `System.out.print("Note für Mathe: ")`). - const lines = this.engine.state.outputLines - form.querySelector('.sim-prompt')!.textContent = request.prompt || (lines[lines.length - 1] ?? '') - const field = form.querySelector('input')! - field.inputMode = request.valueType === 'string' || request.valueType === 'boolean' ? 'text' : 'decimal' - const error = this.engine.state.inputErrorNotice - const errorLine = form.querySelector('.sim-input-error')! - errorLine.textContent = error ? noticeText(error) : '' - errorLine.hidden = !error - } -} - -/** Ein 2D-Array als kleine Tabelle: eine Tabellenzeile pro Zeile des Arrays. */ -export function gridTable(value: SimValue & { kind: 'grid' }): HTMLTableElement { - const table = document.createElement('table') - table.className = 'sim-grid' - for (const row of value.value) { - const tr = table.insertRow() - for (const x of row) { - const td = tr.insertCell() - td.textContent = String(x) - if (x === true) td.className = 'sim-grid-true' - } - } - return table -} diff --git a/web/src/ui/site.ts b/web/src/ui/site.ts deleted file mode 100644 index 54211816..00000000 --- a/web/src/ui/site.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Webseiten der Welten auf visucode.de: visustruct.visucode.de, visubot.visucode.de, visuobject.visucode.de. - * Jede Subdomain hat ihre eigene Startseite (/de/, /en/, /pt/ …) und eine eigene Kopie der Web-App unter /app/. - */ -export type SiteWorld = 'visustruct' | 'visubot' | 'visuobject' - -export const SITE_DOMAIN = 'visucode.de' - -/** Welt der Subdomain, auf der die App gerade läuft (z. B. `visubot` auf visubot.visucode.de), sonst `undefined`. */ -export function hostWorld(host: string = location.hostname): SiteWorld | undefined { - const m = /^(visustruct|visubot|visuobject)\.visucode\.de$/.exec(host.toLowerCase()) - return m ? (m[1] as SiteWorld) : undefined -} - -/** Startseite von VisuCode (visucode.de): dort wählt man ein anderes Programm (Deutsch in der Wurzel, sonst /en/, /pt/). */ -export function visucodeURL(languageCode: string): string { - const lang = siteLanguage(languageCode) - return `https://${SITE_DOMAIN}/${lang === 'de' ? '' : `${lang}/`}` -} - -/** Sprachordner der Webseite zur App-Sprache (die Seiten gibt es auf Deutsch, Englisch und Portugiesisch). */ -export function siteLanguage(code: string): 'de' | 'en' | 'pt' { - return code === 'pt-PT' ? 'pt' : code === 'de' ? 'de' : 'en' -} - -/** - * Startseite einer Welt, auf Wunsch mit Abschnitt (#contact, #privacy). - * Läuft die App unter /app/ auf der Subdomain dieser Welt, relativ (`../de/index.html`), sonst absolut. - */ -export function worldHomeURL( - world: SiteWorld, - languageCode: string, - section?: string, - where: { hostname: string; pathname: string } = location, -): string { - const lang = siteLanguage(languageCode) - const hash = section ? `#${section}` : '' - const local = hostWorld(where.hostname) === world && /\/app\/(index\.html)?$/.test(where.pathname) - return local ? `../${lang}/index.html${hash}` : `https://${world}.${SITE_DOMAIN}/${lang}/${hash}` -} diff --git a/web/src/ui/styles.css b/web/src/ui/styles.css deleted file mode 100644 index acad71fd..00000000 --- a/web/src/ui/styles.css +++ /dev/null @@ -1,7094 +0,0 @@ -:root { - --bg: #f2f2f7; - --panel: #ffffff; - --text: #1c1c1e; - --muted: #6e6e73; - --border: #d1d1d6; - --accent: #007aff; - --accent-soft: rgba(0, 122, 255, 0.18); - --accent-faint: rgba(0, 122, 255, 0.08); - --accent-glow: rgba(0, 122, 255, 0.35); - /* Farben der Bereiche (auch in VisuBot gleich): Überschrift, Leiste und Schild „Löse diese Übung im …“ */ - --area-editor: #2b8a3e; - --area-code: #7c3aed; - color-scheme: light; -} - -/* VisuBot-Modus: Navy und Lila aus dem VisuBot-Logo, Orange wie auf visucode.de - (Vollton #c2410c, Verlauf #ea580c → #c2410c; weiße Schrift darauf 5,2 : 1) */ -:root.visubot-theme { - --bg: #f1f0f8; - --text: #2f3454; - --muted: #6a6890; - --border: #d6d3e8; - --accent: #c2410c; - --accent-soft: rgba(234, 88, 12, 0.2); - --accent-faint: rgba(234, 88, 12, 0.08); - --accent-glow: rgba(194, 65, 12, 0.35); -} -:root.visubot-theme .selection-frame { - stroke: rgba(194, 65, 12, 0.9); -} - -* { - box-sizing: border-box; -} - -html, -body { - margin: 0; - height: 100%; - background: var(--bg); - color: var(--text); - font: 14px/1.4 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} - -.app { - display: grid; - /* Wie in der App: Eingabe unter dem Diagramm, damit das Diagramm die ganze Breite bekommt */ - grid-template: 'tools tools' auto 'pal work' 1fr 'pal insp' auto / var(--palette-width, 210px) minmax(0, 1fr); - height: 100%; - flex: 1; - min-width: 0; -} - -/* Oben die Werkzeugleiste, darunter die Spalten Lernen | Struktogramm | Code live */ -.shell { - display: flex; - flex-direction: column; - height: 100vh; - height: 100dvh; -} -.columns { - display: flex; - flex: 1; - min-height: 0; -} -.columns > .app { - min-width: 240px; -} - -.toolbar { - display: flex; - flex-wrap: wrap; - gap: 8px 16px; - align-items: center; - padding: 8px 12px; - background: var(--panel); - border-bottom: 1px solid var(--border); -} - -/* Symbolknöpfe: Name im Tooltip (tooltip.ts) */ -button.icon { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 34px; - height: 32px; - padding: 0 7px; -} -button.icon svg { - display: block; -} -.tooltip { - position: fixed; - z-index: 1000; - max-width: 260px; - padding: 4px 8px; - border-radius: 6px; - background: #1c1c1e; - color: #fff; - font-size: 12px; - line-height: 1.35; - pointer-events: none; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); -} -.tooltip[hidden] { - display: none; -} -/* Werkzeuge des Struktogramms: eigene Zeile über dem Titel */ -.diagram-tools { - grid-area: tools; - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 4px; - padding: 5px 8px; - background: var(--panel); - border-bottom: 1px solid var(--border); -} -.diagram-tools button.icon { - min-width: 30px; - height: 30px; - padding: 0 5px; -} -.diagram-tools .sim-toggle { - margin-inline-start: auto; - display: inline-flex; - align-items: center; - gap: 6px; - height: 30px; - padding: 0 10px; - white-space: nowrap; -} -/* Wird die Werkzeugzeile schmal (z. B. iPad hochkant mit Lernbereich), bleibt nur das Symbol, damit alles in eine Zeile passt. */ -.diagram-tools { - container-type: inline-size; -} -@container (max-width: 520px) { - .diagram-tools .sim-toggle span { - display: none; - } -} -.diagram-tools .sep { - width: 1px; - height: 20px; - margin-inline: 3px; - background: var(--border); -} -.menu-host { - position: relative; -} -.export-menu { - position: absolute; - top: calc(100% + 4px); - inset-inline-end: 0; - z-index: 30; - display: grid; - min-width: 180px; - padding: 4px; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); -} -.export-menu[hidden] { - display: none; -} -.export-menu button { - border: none; - background: none; - text-align: start; - padding: 7px 10px; - border-radius: 6px; -} -.export-menu button:hover, -.export-menu button:focus-visible { - background: var(--accent-soft); -} - -/* Zahnrad oben rechts: Textgröße und Sprache */ -.settings { - position: relative; - margin-inline-start: auto; -} -.settings > button { - display: inline-flex; - align-items: center; - padding: 6px 8px; -} -.settings-menu { - position: absolute; - top: calc(100% + 6px); - inset-inline-end: 0; - z-index: 30; - display: grid; - width: 290px; - max-width: calc(100vw - 24px); - max-height: calc(100dvh - 80px); - overflow-y: auto; - padding: 4px 0; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); -} -.settings-menu[hidden] { - display: none; -} -/* Abschnitte mit Überschrift, durch feine Linien getrennt. */ -.settings-menu section, -.settings-menu footer { - display: grid; - gap: 10px; - padding: 10px 14px 12px; -} -.settings-menu section + section, -.settings-menu footer { - border-top: 1px solid var(--border); -} -.settings-menu footer { - display: flex; - align-items: center; - justify-content: space-between; - gap: 10px; -} -.settings-version { - font-size: 12px; - color: var(--muted); -} -.settings-menu section[hidden] { - display: none; -} -.settings-menu h3 { - margin: 0; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--muted); -} -.settings-menu label { - display: grid; - gap: 4px; - font-size: 13px; - font-weight: 600; - color: var(--muted); -} -.settings-menu select { - width: 100%; - max-width: none; -} -.settings-menu label.check { - grid-template-columns: auto 1fr; - align-items: start; - gap: 8px; - font-weight: 500; - color: var(--text); - cursor: pointer; -} -.settings-menu label.check input { - margin: 2px 0 0; -} -.settings-menu label small, -.settings-menu label.check small { - display: block; - font-weight: 400; - color: var(--muted); -} - -.settings-menu .about-button { - justify-self: start; - padding: 6px 10px; - font-size: 13px; -} - -/* „Über VisuStruct“: ohne Innenabstand, damit ein Klick auf den Dialog selbst nur der Rand sein kann. */ -dialog.about { - width: min(560px, calc(100vw - 32px)); - max-height: calc(100dvh - 48px); - padding: 0; - color: var(--text); - background: var(--panel); - border: 1px solid var(--border); - border-radius: 12px; - box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); -} -dialog.about::backdrop { - background: rgba(0, 0, 0, 0.35); -} -dialog.about header { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; - padding: 12px 16px; - border-bottom: 1px solid var(--border); -} -dialog.about h2 { - margin: 0; - font-size: 16px; -} -.about-body { - padding: 20px 24px 24px; - font-size: 14px; - line-height: 1.5; -} -.about-body p { - margin: 0 0 8px; - color: var(--muted); -} -.about-head { - display: flex; - gap: 16px; - padding-bottom: 16px; - margin-bottom: 8px; - border-bottom: 1px solid var(--border); -} -.about-head .logo { - flex: none; - width: 40px; - height: 40px; - color: var(--accent); -} -.about-head h3 { - margin: 0 0 6px; - font-size: 20px; -} -.about-head h3 small { - font-weight: 400; - color: var(--muted); -} -.about-body .about-version { - font-variant-numeric: tabular-nums; -} -.about-body .about-author { - color: var(--text); - font-weight: 600; -} -.about-link { - margin: 0 0 2px; -} -.about-body h4 { - margin: 16px 0 6px; - font-size: 15px; -} -.about-body code { - font-weight: 600; - color: var(--text); -} -.about-body .about-copy { - margin-top: 16px; -} - -.brand { - font-weight: 700; - font-size: 16px; - display: flex; - align-items: center; - gap: 8px; -} -.visubot-logo { - width: auto; - height: 30px; - margin: -4px 0; -} -.visubot-bot { - /* wie der Schriftzug „Bot“ auf visucode.de */ - color: #c2410c; - background: linear-gradient(90deg, #ea580c, #c2410c); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; - margin-left: -8px; -} -.brand small { - font-weight: 500; - color: var(--muted); - font-size: 12px; -} -.logo { - width: 20px; - height: 20px; - border: 2px solid currentColor; - background: linear-gradient(to bottom right, transparent 47%, currentColor 47% 53%, transparent 53%) left / 50% 60% no-repeat, - linear-gradient(to bottom left, transparent 47%, currentColor 47% 53%, transparent 53%) right / 50% 60% no-repeat; -} - -.group { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -button, -select { - font: inherit; - padding: 6px 12px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); - color: var(--text); - cursor: pointer; - min-height: 34px; -} -select { - max-width: 180px; -} -button:hover:not(:disabled), -select:hover { - border-color: var(--accent); -} -button:disabled { - opacity: 0.4; - cursor: default; -} - -.palette, -.inspector { - background: var(--panel); - padding: 12px; - overflow-y: auto; -} -.palette { - grid-area: pal; - border-inline-end: 1px solid var(--border); -} -.inspector { - grid-area: insp; - border-top: 1px solid var(--border); - max-height: 34vh; -} - -h2 { - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); - margin: 0 0 4px; -} - -.hint { - color: var(--muted); - font-size: 12px; - margin: 0 0 10px; -} - -.palette ul { - list-style: none; - margin: 0; - padding: 0; - display: grid; - gap: 6px; -} - -.palette-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 10px; - border: 1px solid var(--border); - border-radius: 8px; - cursor: grab; - line-height: 1.25; - user-select: none; - -webkit-user-select: none; - touch-action: none; /* Ziehen mit dem Finger statt Scrollen */ -} -.palette-item:hover, -.palette-item:focus-visible { - border-color: var(--accent); - outline: none; -} -.palette-item .icon { - flex: none; - color: var(--text); -} - -.workspace { - grid-area: work; - display: flex; - flex-direction: column; - min-width: 0; - min-height: 0; -} - -.caption { - font: 600 18px system-ui, -apple-system, sans-serif; - border: none; - border-bottom: 1px solid var(--border); - padding: 10px 16px; - background: var(--panel); - outline: none; -} - -.canvas { - flex: 1; - overflow: auto; - position: relative; - /* Doppeltipp öffnet das Eingabefeld statt zu zoomen */ - touch-action: manipulation; - /* Platz für den Scrollbalken immer freihalten: unter Windows verschiebt er sonst das Layout */ - scrollbar-gutter: stable; - background: #fafafa; -} -/* Text direkt im Struktogramm bearbeiten (Doppelklick/Doppeltipp) */ -.inline-edit { - position: absolute; - z-index: 5; - box-sizing: border-box; - display: flex; - align-items: center; - gap: 4px; - padding: 4px 8px; - background: #fff; - border: 2px solid var(--accent); - border-radius: 6px; - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); -} -.inline-edit-center { - justify-content: center; -} -.inline-edit-prefix, -.inline-edit-sep { - font-weight: 700; - color: #000; - flex: none; -} -.inline-edit textarea, -.inline-edit input { - flex: 1 1 auto; - min-width: 3ch; - margin: 0; - padding: 2px 4px; - border: 1px solid #c8ccd2; - border-radius: 4px; - background: #fff; - color: #000; - font: inherit; - line-height: inherit; - resize: none; - overflow: hidden; - white-space: pre; -} -.inline-edit-center textarea { - text-align: center; -} -.inline-edit textarea:focus, -.inline-edit input:focus { - outline: none; - border-color: var(--accent); -} - -.canvas svg { - display: block; - user-select: none; - -webkit-user-select: none; -} - -.dragging, -.dragging * { - cursor: grabbing !important; -} - -.drop-line { - fill: var(--accent); -} -.drop-replace { - fill: var(--accent-soft); - stroke: var(--accent); - stroke-width: 3; -} - -.drag-ghost { - position: fixed; - left: 0; - top: 0; - z-index: 10; - pointer-events: none; - padding: 6px 10px; - border-radius: 8px; - background: var(--accent); - color: white; - font-weight: 600; - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); - white-space: nowrap; - max-width: 260px; - overflow: hidden; - text-overflow: ellipsis; -} - -.inspector label { - display: block; - font-weight: 600; - margin: 8px 0 4px; -} -.inspector textarea { - width: 100%; - font: 14px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - padding: 8px; - border: 1px solid var(--border); - border-radius: 8px; - resize: vertical; -} - -.statusbar { - display: flex; - flex-wrap: wrap; - gap: 2px 16px; - align-items: baseline; - padding: 4px 12px; - font-size: 12px; - color: var(--muted); - background: var(--panel); - border-top: 1px solid var(--border); -} -.status { - flex: 1 1 auto; - min-width: 0; -} -.site-links { - display: flex; - gap: 12px; - margin-inline-start: auto; -} -.site-links a { - color: inherit; -} -.site-links a:hover, -.site-links a:focus-visible { - color: var(--accent); -} -a.brand { - color: inherit; - text-decoration: none; -} - -.file-drag .canvas { - outline: 3px dashed var(--accent); - outline-offset: -8px; -} - -/* Kompakte Elemente: Ist die Struktogramm-Spalte schmal (z. B. mit „Programmieren“ daneben), - steht der Name klein unter dem Symbol, damit das Struktogramm mehr Platz bekommt. */ -@media (min-width: 821px) { - .app.compact-palette { - --palette-width: 100px; - } - .app.compact-palette .palette { - padding: 8px 6px; - } - .app.compact-palette .palette h2 { - font-size: 10px; - letter-spacing: 0; - text-align: center; - } - .app.compact-palette .palette .hint { - display: none; - } - .app.compact-palette .palette-item { - flex-direction: column; - gap: 3px; - padding: 6px 2px; - font-size: 10.5px; - line-height: 1.15; - text-align: center; - overflow-wrap: break-word; - } -} - -/* Schmale Fenster (iPhone, iPad hochkant): Palette oben, Eigenschaften unten */ -@media (max-width: 820px) { - .palette-item { - white-space: nowrap; - } - .app { - grid-template: 'tools' auto 'pal' auto 'work' 1fr 'insp' auto / minmax(0, 1fr); - } - .palette, - .inspector { - min-width: 0; - } - .palette { - border-inline-end: none; - border-bottom: 1px solid var(--border); - padding: 8px; - } - .palette .hint, - .palette h2 { - display: none; - } - .palette ul { - display: flex; - overflow-x: auto; - overflow-y: hidden; - } - .palette-item { - white-space: nowrap; - } - .inspector { - max-height: 30vh; - } -} - -.status.message { - color: var(--text); - font-weight: 600; -} - -.confirm { - position: fixed; - inset: 0; - z-index: 20; - display: grid; - place-items: center; - background: rgba(0, 0, 0, 0.25); - padding: 16px; -} -.confirm-card { - background: var(--panel); - border-radius: 12px; - padding: 16px 18px; - max-width: 360px; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); -} -.confirm-card p { - margin: 0 0 8px; -} -.confirm-card .group { - justify-content: flex-end; - margin-top: 12px; -} -/* „Variable umbenennen“ */ -.rename-card { - width: min(380px, 100%); -} -.rename-card label { - display: block; - margin-top: 8px; - font-size: 13px; -} -.rename-card input, -.rename-card select { - width: 100%; - box-sizing: border-box; - margin-top: 4px; - font-size: 15px; -} -.rename-card .rename-info { - min-height: 1.3em; - margin-top: 8px; -} -.rename-card .rename-warning { - color: #c93400; -} -.rename-menu { - position: fixed; - z-index: 30; - padding: 4px; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 8px; - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2); -} -.rename-menu button { - display: block; - width: 100%; - text-align: left; - border: none; - background: none; - padding: 6px 12px; - border-radius: 6px; -} -.rename-menu button:hover, -.rename-menu button:focus-visible { - background: var(--accent-soft); -} -.inspector-rename { - margin-top: 8px; -} -.files-ask { - width: min(360px, 100%); -} -.files-ask input { - width: 100%; - box-sizing: border-box; - margin-top: 4px; -} -.files-choices { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin: 4px 0 10px; -} -.files-choice { - display: inline-flex; - align-items: center; - gap: 6px; -} -.files-choice svg { - width: 16px; - height: 16px; -} -button.danger { - background: #d70015; - border-color: #d70015; - color: white; -} - -/* Simulation: Diagramm nur anzeigen, rechts das Simulationsfeld */ -.simpanel { - display: none; - grid-area: insp; - background: var(--panel); - border-inline-start: 1px solid var(--border); - padding: 12px; - overflow-y: auto; -} -.app.simulating { - grid-template: 'tools tools' auto 'work insp' 1fr / minmax(0, var(--sim-work, 1fr)) minmax(240px, 1fr); -} -/* Linie zwischen Diagramm und Steuerung: zum Ziehen, liegt auf dem linken Rand der Steuerung. */ -.sim-resizer { - display: none; - grid-area: insp; - justify-self: start; - width: 10px; - margin-inline-start: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.app.simulating .sim-resizer { - display: block; -} -.sim-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.sim-resizer:hover::after, -.sim-resizer:focus-visible::after, -.shell.resizing .sim-resizer::after { - background: var(--accent); -} -.sim-resizer:focus-visible { - outline: none; -} -/* - * Linie zwischen Diagramm und Objektinspektor: liegt auf dem oberen Rand des Objektinspektors, ist für Finger - * 20 px hoch und zeigt einen kleinen Griff, damit man sie auf dem Tablet findet. - */ -.insp-resizer { - grid-area: insp; - align-self: start; - height: 20px; - margin-top: -10px; - z-index: 5; - cursor: row-resize; - touch-action: none; - position: relative; -} -.insp-resizer::before { - content: ''; - position: absolute; - inset-inline: 0; - top: 9px; - height: 2px; - background: transparent; - transition: background 0.15s; -} -.insp-resizer::after { - content: ''; - position: absolute; - left: 50%; - top: 7px; - width: 36px; - height: 6px; - margin-left: -18px; - border-radius: 3px; - background: var(--border); - transition: background 0.15s; -} -.insp-resizer:hover::before, -.insp-resizer:focus-visible::before, -.shell.resizing .insp-resizer::before, -.insp-resizer:hover::after, -.insp-resizer:focus-visible::after, -.shell.resizing .insp-resizer::after { - background: var(--accent); -} -.insp-resizer:focus-visible { - outline: none; -} -.app.insp-sized .inspector, -.app.insp-sized.coding .inspector { - height: var(--insp-height); - max-height: none; -} -.app.simulating .palette, -.app.simulating .inspector, -.app.simulating .insp-resizer { - display: none; -} -.app.simulating .simpanel { - display: block; -} -.app.simulating .caption { - cursor: default; -} -button.sim-toggle { - font-weight: 600; -} -/* Starten und Beenden sehen gleich aus, nur Symbol und Text wechseln. */ -button.sim-toggle { - background: var(--accent); - border-color: var(--accent); - color: white; -} -.simpanel section { - margin-bottom: 14px; -} -.sim-buttons { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 6px; - margin: 6px 0 10px; -} -.sim-option { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - margin-bottom: 6px; - font-size: 13px; -} -.mono, -.sim-vars td { - font: 13px/1.5 ui-monospace, 'SF Mono', Menlo, Consolas, monospace; -} -.sim-trace, -.sim-output, -.sim-vars { - background: var(--bg); - border-radius: 8px; - padding: 8px 10px; - overflow-wrap: anywhere; -} -.sim-output { - max-height: 220px; - overflow-y: auto; - white-space: pre-wrap; -} -.sim-vars table { - border-collapse: collapse; - width: 100%; -} -.sim-vars td { - padding: 2px 0; - vertical-align: top; -} -.sim-vars td:first-child { - color: var(--muted); - padding-inline-end: 12px; - width: 1%; - white-space: nowrap; -} -/* 2D-Array unter Variablen: kleine Tabelle, Zeile für Zeile */ -.sim-vars table.sim-grid { - width: auto; -} -.sim-vars .sim-grid td, -.sim-vars .sim-grid td:first-child { - border: 1px solid var(--border); - color: inherit; - font-size: 11px; - padding: 0 4px; - text-align: center; - width: auto; -} -.sim-vars .sim-grid td.sim-grid-true { - font-weight: 700; -} -.simpanel .placeholder { - color: var(--muted); - font: 13px system-ui, -apple-system, sans-serif; - margin: 0; -} -.sim-notice { - margin: 0 0 14px; - padding: 8px 10px; - border-radius: 8px; - background: rgba(52, 199, 89, 0.15); - font-weight: 600; -} -.sim-edit { - width: 100%; - margin: -6px 0 14px; -} -.sim-notice.error { - background: rgba(255, 59, 48, 0.15); - color: #b0000f; -} -.sim-input.active { - padding: 8px 10px; - margin-left: -10px; - margin-right: -10px; - border-radius: 10px; - background: var(--accent-faint); - outline: 2px solid var(--accent); -} -.sim-prompt { - margin: 4px 0 6px; - font-weight: 600; -} -.sim-input-row { - display: flex; - gap: 6px; -} -.sim-input-row input { - flex: 1; - min-width: 0; - font: inherit; - padding: 6px 10px; - border: 1px solid var(--border); - border-radius: 8px; -} -.sim-input-error { - color: #b0000f; - margin: 6px 0 0; - font-size: 13px; -} - -@media (max-width: 820px) { - .app.simulating { - grid-template: 'tools' auto 'work' minmax(200px, 1fr) 'insp' auto / minmax(0, 1fr); - } - .simpanel { - border-inline-start: none; - border-top: 1px solid var(--border); - max-height: 48vh; - } - .app.simulating .sim-resizer { - display: none; - } -} - -/* Code-Generator: rechts unter den Eigenschaften, Bearbeiten bleibt möglich */ -.codepanel { - display: none; - grid-area: code; - background: var(--panel); - border-inline-start: 1px solid var(--border); - padding: 12px; - overflow-y: auto; - min-height: 0; -} -.app.coding { - grid-template: - 'tools tools tools' auto - 'pal work code' 1fr - 'pal insp code' auto / var(--palette-width, 210px) minmax(0, 1fr) 400px; -} -.app.coding .codepanel { - display: block; -} -.app.coding .inspector { - max-height: 34vh; -} -button[data-action='code'][aria-pressed='true'] { - border-color: var(--accent); - background: var(--accent-soft); -} -.code-head, -.runner-head { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 8px; -} -.code-close { - min-height: 28px; - padding: 2px 10px; -} -.segmented { - display: flex; - margin-bottom: 8px; -} -.segmented button { - flex: 1; - border-radius: 0; - min-height: 30px; - padding: 4px 8px; -} -.segmented button:first-child { - border-radius: 8px 0 0 8px; -} -.segmented button:last-child { - border-radius: 0 8px 8px 0; -} -.segmented button + button { - border-inline-start: none; -} -.segmented button[aria-pressed='true'] { - background: var(--accent); - border-color: var(--accent); - color: white; -} -.segmented[hidden] { - display: none; -} -.check { - display: flex; - gap: 8px; - align-items: center; - font-size: 13px; - margin-bottom: 4px; -} -.code-actions { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin: 10px 0; -} -.code-actions [hidden] { - display: none; -} -button.primary { - background: var(--accent); - border-color: var(--accent); - color: white; -} -.code-view, -.runner-out { - margin: 0; - padding: 10px 12px; - border-radius: 10px; - background: var(--bg); - font: 13px/1.5 ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - overflow: auto; - white-space: pre; - tab-size: 4; -} -.runner { - margin: 12px 0; -} -.runner-out { - background: #0b1220; - color: #e5e7eb; - min-height: 3em; - max-height: 240px; - white-space: pre-wrap; -} -.runner-out.error { - outline: 2px solid #ff453a; -} -.runner-input { - margin-top: 8px; -} -.runner-input label { - display: block; - font-weight: 600; - margin-bottom: 4px; -} - -@media (max-width: 820px) { - .app.coding { - grid-template: 'tools' auto 'pal' auto 'work' minmax(240px, 1fr) 'insp' auto 'code' auto / minmax(0, 1fr); - } - .codepanel { - border-inline-start: none; - border-top: 1px solid var(--border); - max-height: 50vh; - } -} - -/* Reiter (mehrere Diagramme) */ -.tabs { - display: flex; - align-items: flex-end; - gap: 2px; - padding: 6px 8px 0; - background: var(--bg); - border-bottom: 1px solid var(--border); - overflow-x: auto; - /* Nur waagerecht scrollen: sonst zeigt Windows hier einen winzigen senkrechten Scrollbalken */ - overflow-y: hidden; - scrollbar-width: thin; -} -.tab { - display: flex; - align-items: center; - max-width: 220px; - border: 1px solid var(--border); - border-bottom: none; - border-radius: 8px 8px 0 0; - background: rgba(255, 255, 255, 0.5); -} -.tab.active { - background: var(--panel); - margin-bottom: -1px; - padding-bottom: 1px; -} -.tab button { - border: none; - background: none; - min-height: 30px; - border-radius: 8px 8px 0 0; -} -.tab-label { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - padding: 4px 6px 4px 12px; -} -.tab.active .tab-label { - font-weight: 600; -} -.tab-close { - padding: 2px 8px; - color: var(--muted); - font-size: 11px; -} -.tab-close:hover { - color: var(--text); -} -.tab-dot, -.tab-close.unsaved .tab-x { - display: none; -} -.tab-close.unsaved .tab-dot { - display: inline; - color: var(--text); - font-size: 10px; -} -.tab-close.unsaved:hover .tab-dot { - display: none; -} -button.tab-close.unsaved:hover .tab-x { - display: inline; -} -span.tab-close { - padding: 2px 10px 2px 4px; -} -.tab-add { - min-height: 28px; - padding: 2px 10px; - margin: 0 0 3px 4px; - border-radius: 8px; -} - -/* Inspector wie in der App */ -.inspector .segmented { - margin: 6px 0 4px; - max-width: 420px; -} -.mono-hint { - font: 11px/1.45 ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - overflow-wrap: anywhere; -} -.editor-row { - display: flex; - gap: 8px; - align-items: flex-start; -} -.editor-row textarea { - flex: 1; - min-width: 0; -} -.fixed-prefix { - font: 14px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - color: var(--muted); - padding-top: 9px; -} -.inspector input:not([type]) { - width: 100%; - font: 14px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - padding: 7px 8px; - border: 1px solid var(--border); - border-radius: 8px; -} -.for-fields { - display: grid; - gap: 4px; -} -.for-fields label, -.case-controls label { - margin: 4px 0 0; - font-size: 12px; - color: var(--muted); - font-weight: 600; -} -.case-controls { - margin-top: 12px; - padding-top: 10px; - border-top: 1px solid var(--border); -} -.case-controls .group { - margin-top: 8px; -} -button.danger-text:not(:disabled) { - color: #d70015; -} - -/* Drucken: nur das Export-Bild, eingepasst auf die Seite. */ -.print-sheet { - display: none; -} -/* Dateien: Workspace als Baum (Klasse → Tag → Programme) */ -.files { - display: none; - flex: 0 1 auto; - flex-direction: column; - width: 260px; - min-width: 200px; - min-height: 0; - background: var(--panel); - border-inline-end: 1px solid var(--border); -} -.shell.files-open .files { - display: flex; -} -.shell.files-open .files-rail { - display: none; -} -.files-head { - display: flex; - align-items: center; - gap: 4px; - margin: 10px 12px 6px; -} -.files-head h2 { - flex: 1; - margin: 0; - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); -} -.files-head button.icon { - min-width: 30px; - height: 30px; - padding: 0 5px; -} -.files-head [hidden] { - display: none; -} -.files-body { - flex: 1; - min-height: 0; - overflow: auto; - padding: 0 8px 12px; - font-size: 14px; -} -.files-where { - margin: 0 4px 6px; - font-size: 12px; - color: var(--muted); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.files-hint, -.files-empty { - margin: 8px 4px; - font-size: 13px; - color: var(--muted); -} -.start-ask .files-start { - margin-bottom: 2px; -} -.start-ask .hint { - font-size: 13px; - color: var(--muted); -} -.start-without { - display: block; - margin: 12px auto 0; - border: none; - background: none; - color: var(--accent); - text-decoration: underline; -} -.start-without + .hint { - text-align: center; -} -.files-start { - display: flex; - align-items: center; - gap: 6px; - width: 100%; - margin: 8px 0; - justify-content: center; -} -.files-row { - display: flex; - align-items: center; - border-radius: 6px; -} -.files-row:hover, -.files-row:focus-within { - background: var(--accent-soft); -} -.files-row.current > .files-name { - font-weight: 600; -} -.files-name { - flex: 1; - min-width: 0; - display: flex; - align-items: center; - gap: 6px; - padding: 4px 6px 4px calc(6px + var(--depth) * 16px); - border: none; - background: none; - text-align: start; - font: inherit; - color: var(--text); - cursor: pointer; -} -.files-name:disabled { - cursor: default; - color: var(--muted); -} -.files-name svg { - flex: none; - width: 16px; - height: 16px; - color: var(--muted); -} -.files-name span { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.files-name small { - margin-inline-start: auto; - padding: 0 6px; - border-radius: 6px; - background: var(--accent); - color: white; - font-size: 11px; - font-weight: 600; -} -.files-row .files-delete { - visibility: hidden; - min-width: 26px; - height: 26px; - padding: 0 4px; - border: none; - background: none; - color: var(--muted); -} -.files-row:hover .files-delete, -.files-row:focus-within .files-delete { - visibility: visible; -} -.files-error { - color: #c62828; -} -.files-resizer { - display: none; - flex: none; - width: 10px; - margin-inline: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.shell.files-open .files-resizer { - display: block; -} -.files-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; -} -.files-resizer:hover::after, -.files-resizer:focus-visible::after { - background: var(--accent); -} - -/* Lernbereich */ -/* Lernen und Code live geben nach, wenn das Fenster zu schmal wird; das Struktogramm behält mindestens 240 px. */ -.learn { - display: none; - flex: 0 1 auto; - width: 400px; - min-width: 280px; - flex-direction: column; - background: var(--panel); - border-inline-end: 1px solid var(--border); - min-height: 0; -} -.shell.learning .learn { - display: flex; -} -/* Trennlinie zum Ziehen; breite Trefferfläche, damit es auch mit dem Finger geht. */ -.learn-resizer { - display: none; - flex: none; - width: 10px; - margin-inline: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.shell.learning .learn-resizer { - display: block; -} -.learn-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.learn-resizer:hover::after, -.learn-resizer:focus-visible::after, -.shell.resizing .learn-resizer::after { - background: var(--accent); -} -.learn-resizer:focus-visible { - outline: none; -} -.shell.resizing { - cursor: col-resize; - user-select: none; -} -/* Kopfzeile: Reiter und Einklapp-Knopf (Seitenleisten-Symbol wie auf dem Mac). */ -.learn-head { - display: flex; - align-items: flex-start; - gap: 6px; - margin: 10px 12px 0; -} -.learn-tabs { - flex: 1; - min-width: 0; -} -.learn-hide { - flex: none; - min-height: 30px; - padding: 4px 7px; - display: inline-flex; - align-items: center; - color: var(--muted); -} -/* Eingeklappt: schmale Leiste am Rand, ein Tipp klappt den Lernbereich wieder auf. */ -.learn-rail { - flex: none; - display: flex; - flex-direction: column; - align-items: center; - gap: 10px; - width: 36px; - padding: 10px 0; - border: none; - border-radius: 0; - border-inline-end: 1px solid var(--border); - background: var(--panel); - cursor: pointer; -} -.learn-rail:hover { - background: var(--accent-soft); -} -.learn-rail span { - writing-mode: vertical-rl; - white-space: nowrap; -} -.shell.learning .learn-rail:not(.live-rail):not(.editor-rail):not(.files-rail):not(.robotpreview-rail):not(.palette-rail) { - display: none; -} -/* Code live eingeklappt: dieselbe Leiste, nur am rechten Rand. */ -.live-rail { - border-inline-end: none; - border-inline-start: 1px solid var(--border); -} -.live-rail svg { - transform: scaleX(-1); -} -.shell.live .live-rail { - display: none; -} -/* Struktogramm eingeklappt: Leiste in der Mitte. Lernen behält seine (verschiebbare) Breite, Code live füllt den Rest. */ -.editor-rail { - display: none; - border-inline-start: 1px solid var(--border); -} -.shell.editor-hidden .editor-rail { - display: flex; -} -.shell.editor-hidden .app, -.shell.editor-hidden .live-resizer, -.shell.editor-hidden:not(.live) .learn-resizer { - display: none; -} -.shell.editor-hidden .livecode, -.shell.editor-hidden:not(.live) .learn { - flex: 1 1 0; - width: auto !important; - min-width: 0; -} -.learn-body { - overflow-y: auto; - /* Sonst: Scrollbalken erscheint → Vorschaubilder schmaler und kürzer → Scrollbalken verschwindet → … */ - scrollbar-gutter: stable; - padding: 4px 16px 24px; - flex: 1; - min-height: 0; - line-height: 1.5; -} -.learn-body h2.learn-title { - display: flex; - align-items: center; - gap: 8px; - font-size: 20px; - text-transform: none; - letter-spacing: 0; - color: var(--text); - margin: 2px 0 10px; -} -.learn-section { - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); - margin: 18px 0 6px; -} -.learn-list { - list-style: none; - padding: 0; - margin: 0; - display: grid; - gap: 6px; -} -.learn-item { - display: flex; - align-items: center; - gap: 10px; - width: 100%; - text-align: start; - padding: 8px 10px; -} -.learn-item.done { - border-color: #34c759; -} -.learn-num { - flex: none; - min-width: 28px; - height: 28px; - border-radius: 14px; - background: var(--accent-soft); - color: var(--accent); - display: grid; - place-items: center; - font-weight: 700; - font-size: 12px; - padding: 0 6px; -} -.learn-item.done .learn-num { - background: rgba(52, 199, 89, 0.18); - color: #248a3d; -} -.learn-item-text { - flex: 1; - display: grid; - min-width: 0; -} -.learn-item-text small { - color: var(--muted); -} -.learn-status { - flex: none; - color: var(--muted); - font-size: 12px; -} -.learn-item.done .learn-status { - color: #248a3d; - font-weight: 700; -} -.learn-back, -.link-button { - border: none; - background: none; - padding: 6px 0; - min-height: 0; - color: var(--accent); -} -.link-button { - padding: 0; - text-align: start; -} -/* Lektionskopf bleibt oben stehen, nur der Schritt darunter scrollt. */ -.learn-lesson-head { - position: sticky; - top: -4px; - z-index: 3; - margin: -4px -16px 10px; - padding: 4px 16px 8px; - background: var(--panel); - border-bottom: 1px solid var(--border); -} -.learn-lesson-head .learn-progress { - margin-bottom: 8px; -} -.learn-lesson-head .learn-step-title { - margin: 0; -} -.learn-head-row { - display: flex; - align-items: center; - justify-content: space-between; - gap: 8px; - flex-wrap: wrap; -} -.learn-nav.learn-nav-top { - margin-top: 0; - gap: 6px; -} -.learn-nav-top button { - padding: 3px 10px; - min-height: 0; - font-size: 13px; -} -.learn-kicker { - margin: 4px 0 0; - color: var(--muted); - font-size: 12px; -} -.learn-progress { - display: flex; - gap: 4px; - margin-bottom: 14px; -} -.learn-progress button { - flex: 1 1 0; - min-width: 0; - padding: 3px 0; - font-size: 13px; - font-variant-numeric: tabular-nums; - border-radius: 6px; -} -.learn-progress button.past { - border-color: var(--accent); - color: var(--accent); -} -.learn-progress button.current { - border-color: var(--accent); - background: var(--accent); - color: #fff; - font-weight: 600; -} -.learn-progress button.robot-step:not(.current) { - border-color: #0fa3c6; - background: rgba(15, 163, 198, 0.12); -} -.learn-progress button.robot-step.current { - border-color: #0fa3c6; - background: #0fa3c6; -} -.learn-step-title { - font-size: 16px; - margin: 0 0 6px; -} -.learn-text p, -.learn-text ul, -.learn-text ol { - margin: 0 0 10px; -} -.learn-text ul, -.learn-text ol { - padding-inline-start: 22px; -} -.learn-text code, -.learn-tests code { - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-size: 12.5px; - background: var(--bg); - border-radius: 4px; - padding: 1px 4px; -} -.learn-text h3 { - font-size: 15px; - margin: 18px 0 4px; -} -/* Nachschlagen: Kategorien zum Aufklappen */ -.reference-group { - border: 1px solid var(--border); - border-radius: 8px; - margin: 0 0 8px; - padding: 0 12px; -} -.reference-group summary { - cursor: pointer; - font-weight: 600; - padding: 9px 0; -} -.reference-group[open] summary { - border-bottom: 1px solid var(--border); -} -.reference-group > h3:first-of-type { - margin-top: 10px; -} -.lesson-code { - background: var(--bg); - border-radius: 8px; - padding: 8px 10px; - margin: 0 0 10px; - overflow-x: auto; - overflow-y: hidden; - font-size: 12.5px; -} -.learn-open-code { - display: block; - margin: -4px 0 12px; -} -.lesson-code code { - background: none; - padding: 0; -} -.lesson-note { - border-inline-start: 3px solid var(--accent); - background: var(--accent-soft); - border-radius: 6px; - padding: 8px 10px; - margin: 0 0 10px; -} -.lesson-note p:last-child { - margin-bottom: 0; -} -.learn-images { - display: flex; - gap: 8px; - flex-wrap: wrap; -} -.learn-images img { - width: 150px; - height: 200px; - object-fit: cover; - border-radius: 8px; -} -.learn-file { - margin: 0 0 14px; - border: 1px solid var(--border); - border-radius: 10px; - overflow: hidden; - background: var(--panel); -} -.learn-file figcaption { - display: flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - background: var(--bg); - border-bottom: 1px solid var(--border); - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-size: 12.5px; - font-weight: 600; -} -.learn-file-text { - margin: 0; - padding: 8px 10px; - overflow-x: auto; - font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; - font-size: 12.5px; - line-height: 1.5; - white-space: pre; -} -.learn-file.written { - margin: 8px 0; - border-color: #15803d; -} -.learn-file.written figcaption { - background: #dcfce7; -} -.learn-file-text .ln { - display: inline-block; - min-width: 2ch; - margin-inline-end: 10px; - color: var(--muted); - text-align: end; - user-select: none; -} - -.learn-brag { - display: grid; - grid-template-columns: 48px minmax(0, 1fr); - gap: 10px; - align-items: start; - border: 1px solid var(--border); - border-radius: 10px; - padding: 10px 12px; - margin: 0 0 14px; - background: var(--accent-soft); -} -.learn-brag img { - width: 48px; - height: 48px; - border-radius: 50%; -} -.learn-brag-title { - margin: 0 0 4px; - font-weight: 700; -} -.learn-brag .learn-text p:last-child { - margin-bottom: 0; -} -.learn-game { - border: 2px solid var(--accent); - border-radius: 12px; - padding: 12px 14px; - margin: 0 0 14px; -} -.learn-game-head { - display: flex; - align-items: center; - gap: 10px; - font-size: 1.05em; -} -.learn-game-head img { - width: 40px; - height: 40px; - border-radius: 50%; -} -.learn-game-status { - margin: 6px 0; - color: var(--muted); - font-size: 0.9em; -} -.learn-game-values { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-bottom: 8px; -} -.learn-game-values code { - background: var(--accent-soft); - border-radius: 6px; - padding: 2px 8px; -} -.learn-game-condition { - font-size: 1.15em; - font-weight: 600; - padding: 10px 12px; - border-radius: 8px; - background: var(--bg, #f2f2f7); - margin-bottom: 10px; - overflow-wrap: anywhere; -} -.learn-game-answers button { - flex: 1; - font-weight: 600; - padding-block: 10px; -} -.learn-game-true:not(:disabled) { - border-color: #15803d; - color: #15803d; -} -.learn-game-false:not(:disabled) { - border-color: #b91c1c; - color: #b91c1c; -} -.learn-game-feedback:empty { - display: none; -} -.learn-game-feedback { - margin-top: 10px; - padding: 10px 12px; - border-radius: 8px; -} -.learn-game-feedback.right { - background: #dcfce7; -} -.learn-game-feedback.wrong { - background: #fee2e2; -} -.learn-game-feedback p { - margin: 0 0 6px; - font-weight: 700; -} -.learn-game-steps { - margin-bottom: 10px; - line-height: 1.6; -} -.learn-game-result { - font-size: 1.2em; - font-weight: 700; -} -.learn-game-question { - font-weight: 600; - margin: 10px 0 8px; -} -.learn-game-code { - margin: 0 0 8px; - padding: 8px 0; - border-radius: 8px; - background: var(--bg, #f2f2f7); - font-size: 13px; - line-height: 1.5; - overflow-x: auto; -} -.learn-game-code .ln { - padding: 0 10px 0 calc(10px + 2ch); - text-indent: -2ch; - border-inline-start: 3px solid transparent; - white-space: pre-wrap; - overflow-wrap: anywhere; - transition: background 0.2s; -} -.learn-game-code .ln.hit { - background: #fef3c7; - border-inline-start-color: #d97706; -} -/* Bombe entschärfen: Zeilen sind Knöpfe, darüber der Countdown */ -.learn-bomb { - display: flex; - align-items: center; - gap: 10px; - margin-bottom: 6px; - font-weight: 700; -} -.learn-bomb-icon { - font-size: 28px; - line-height: 1; -} -.learn-bomb.hurry .learn-bomb-icon { - animation: learn-bomb-shake 0.25s infinite alternate; -} -.learn-bomb.boom .learn-bomb-icon { - animation: learn-bomb-boom 0.5s ease-out; -} -@keyframes learn-bomb-shake { - from { transform: rotate(-8deg); } - to { transform: rotate(8deg); } -} -@keyframes learn-bomb-boom { - from { transform: scale(0.6); } - 60% { transform: scale(1.5); } - to { transform: scale(1); } -} -.learn-bomb-time { - min-width: 7ch; - font-variant-numeric: tabular-nums; -} -.learn-bomb-fuse { - flex: 1; - height: 8px; - border-radius: 4px; - background: #e5e7eb; - overflow: hidden; -} -.learn-bomb-fuse span { - display: block; - height: 100%; - background: #f59e0b; - transition: width 0.2s linear; -} -.learn-bomb.hurry .learn-bomb-fuse span { - background: #dc2626; -} -.learn-bomb.defused .learn-bomb-fuse span { - background: #15803d; -} -.learn-bomb-code .ln { - display: block; - width: 100%; - margin: 0; - border-radius: 0; - border-block: 0; - border-inline-end: 0; - background: none; - color: inherit; - font: inherit; - line-height: inherit; - min-height: 0; - height: auto; - padding-block: 1px; - text-align: start; - cursor: pointer; -} -.learn-bomb-code .ln:not(:disabled):hover { - background: #fef3c7; -} -.learn-bomb-code .ln:disabled { - cursor: default; - opacity: 1; -} -.learn-bomb-code .ln.right, -.learn-bomb-code .ln.right:hover { - background: #dcfce7; - border-inline-start-color: #15803d; -} -.learn-bomb-code .ln.wrong, -.learn-bomb-code .ln.wrong:hover { - background: #fee2e2; - border-inline-start-color: #b91c1c; -} -.learn-bomb-fix code { - white-space: pre-wrap; -} -/* Bombe mit `stelle:`: Teile der Zeilen sind Knöpfe, groß genug zum Antippen */ -.learn-bomb-code.by-parts .ln.parts { - cursor: default; - padding-block: 3px; - line-height: 1.9; -} -.learn-bomb-code.by-parts .ln.parts:hover { - background: none; -} -.learn-bomb-code.by-parts .ln.parts.right { - background: #f0fdf4; -} -.learn-bomb-code .part { - display: inline; - margin: 0; - padding: 2px 3px; - min-height: 0; - height: auto; - border: 1px dashed #94a3b8; - border-radius: 4px; - background: none; - color: inherit; - font: inherit; - line-height: inherit; - cursor: pointer; - -webkit-box-decoration-break: clone; - box-decoration-break: clone; -} -.learn-bomb-code .part:not(:disabled):hover { - background: #fef3c7; - border-color: #d97706; -} -.learn-bomb-code .part:disabled { - cursor: default; - opacity: 1; -} -.learn-bomb-code .part.right { - background: #dcfce7; - border: 2px solid #15803d; -} -.learn-bomb-code .part.wrong { - background: #fee2e2; - border: 2px solid #b91c1c; -} -.learn-bomb-hint { - color: var(--muted, #6b7280); - font-size: 0.92em; -} -.learn-game-options { - display: grid; - gap: 6px; -} -.learn-game-options button { - text-align: start; - padding: 8px 12px; -} -.learn-game-options.pictures { - grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); -} -.learn-game-options.pictures button { - padding: 6px; -} -.learn-game-options button.right { - border-color: #15803d; - background: #dcfce7; - color: inherit; - opacity: 1; -} -.learn-game-options button.wrong { - border-color: #b91c1c; - background: #fee2e2; - color: inherit; - opacity: 1; -} -.learn-order-hint { - margin: 0 0 6px; - font-size: 13px; - opacity: 0.75; -} -.learn-order button { - display: flex; - align-items: center; - gap: 10px; -} -.learn-order-badge { - flex: none; - display: inline-grid; - place-items: center; - width: 24px; - height: 24px; - border-radius: 50%; - border: 1.5px solid currentColor; - font-size: 13px; - font-weight: 700; - opacity: 0.5; -} -.learn-order button.right .learn-order-badge, -.learn-order button:disabled .learn-order-badge:not(:empty) { - opacity: 1; -} -.learn-game-file { - margin: 0; - padding: 2px 0; - font-size: 13px; - line-height: 1.45; - white-space: pre; - background: none; -} -.learn-game-explain { - margin-bottom: 10px; -} -/* „Wer hört zu?“: Code und nachgebautes Fenster nebeneinander (schmal: untereinander) */ -.learn-swing-row { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); - gap: 8px; - align-items: start; -} -.learn-swing-window { - border: 1px solid var(--border); - border-radius: 8px; - overflow: hidden; - background: #fff; - color: #1c1c1e; -} -.learn-swing-title { - padding: 3px 8px; - font-size: 12px; - text-align: center; - background: #e5e5ea; - border-bottom: 1px solid #d1d1d6; - min-height: 1.4em; -} -.learn-swing-body { - display: grid; - gap: 6px; - padding: 10px; -} -.learn-swing-label { - padding: 4px 2px; - min-height: 1.4em; -} -.learn-swing-label.changed, -.learn-swing-button.changed { - animation: swing-changed 1s ease-out; -} -@keyframes swing-changed { - from { - background: rgba(255, 214, 10, 0.8); - } -} -.learn-swing-button { - padding: 6px 10px; - background: #fff; - color: #1c1c1e; - border: 1px solid #a1a1aa; - border-radius: 6px; -} -.learn-swing-button:disabled { - opacity: 1; - cursor: default; -} -.learn-swing-button.pressed { - background: var(--accent); - border-color: var(--accent); - color: #fff; - transform: scale(0.96); -} -.learn-swing-event { - margin: 0; - padding: 0 10px 8px; - font-size: 12px; - min-height: 1.4em; -} -.learn-swing-event.heard { - color: #b45309; - font-weight: 600; -} -.learn-swing-event.nobody { - color: #6b7280; - font-style: italic; -} -.learn-swing-console { - margin: 0 10px 10px; - padding: 6px 8px; - border-radius: 6px; - background: #1c1c1e; - color: #f2f2f7; - font-size: 12px; -} -.learn-swing-console[hidden] { - display: none; -} -.learn-swing-clicks { - margin: 8px 0 0; -} -.learn-swing-try { - font-weight: normal !important; - font-style: italic; -} -/* „Wo landet der Knopf?“: kleine Fenster als Antworten */ -.learn-layout-window { - width: 100%; - border: 1px solid #a1a1aa; - border-radius: 6px; - overflow: hidden; - background: #fff; - color: #1c1c1e; -} -.learn-layout-title { - height: 10px; - background: #e5e5ea; - border-bottom: 1px solid #d1d1d6; -} -.learn-layout-body { - height: 80px; - padding: 3px; - gap: 3px; - font-size: 12px; -} -.learn-layout-body.grid { - display: grid; - grid-auto-rows: 1fr; -} -.learn-layout-body.flow { - display: flex; - flex-direction: column; - gap: 3px; -} -.learn-layout-row { - display: flex; - justify-content: center; - gap: 3px; -} -.learn-layout-body.border { - display: grid; - grid-template: 'N N N' auto 'W C E' 1fr 'S S S' auto / auto 1fr auto; -} -.learn-layout-cell { - display: flex; - align-items: center; - justify-content: center; - min-width: 22px; - padding: 2px 6px; - border: 1px solid #a1a1aa; - border-radius: 4px; - background: #f4f4f5; - font-weight: 600; -} -.learn-figure { - margin: 0 0 14px; - display: grid; - gap: 6px; - justify-items: start; -} -.learn-preview { - width: 100%; - border: 1px solid var(--border); - border-radius: 8px; - overflow-x: auto; - overflow-y: hidden; - background: #fafafa; -} -.learn-preview > svg { - display: block; - width: 100%; - margin: 0 auto; - height: auto; -} -.learn-figure figcaption { - color: var(--muted); - font-size: 13px; -} -.learn-exercise { - border: 1px solid var(--accent); - border-radius: 10px; - padding: 10px 12px; - margin: 0 0 14px; - display: grid; - gap: 8px; -} -.learn-exercise h4 { - margin: 0; - font-size: 15px; - display: flex; - gap: 8px; - align-items: center; -} -.learn-badge { - font-size: 12px; - color: #248a3d; - background: rgba(52, 199, 89, 0.18); - border-radius: 10px; - padding: 1px 8px; -} -.learn-where { - margin: 0; - font-size: 13px; - font-weight: 600; - color: var(--muted); -} -.area-name { - display: inline-block; - color: #fff; - border-radius: 10px; - padding: 0 8px; -} -.area-name.area-editor { - background: var(--area-editor); -} -.area-name.area-code { - background: var(--area-code); -} -/* Jeder Bereich zeigt seine Farbe: oben ein farbiger Rand, eingeklappt die Leiste. */ -.columns > .app { - border-top: 3px solid var(--area-editor); -} -.livecode { - border-top: 3px solid var(--area-code); -} -.diagram-title { - margin: 0; - font-size: 16px; - color: var(--area-editor); -} -.livecode-head h2, -.live-rail span { - color: var(--area-code); -} -.editor-rail span { - color: var(--area-editor); -} -.live-rail, -.editor-rail { - font-weight: 600; -} -.learn-exercise .learn-text p:last-child { - margin-bottom: 0; -} -.learn-result:empty { - display: none; -} -.learn-result { - border-radius: 8px; - padding: 8px 10px; -} -.learn-result.ok { - background: rgba(52, 199, 89, 0.14); -} -.learn-result.fail { - background: rgba(255, 149, 0, 0.14); -} -.learn-result.info { - background: var(--bg); -} -.learn-result-head { - margin: 0 0 6px; - font-weight: 600; -} -.learn-tests { - list-style: none; - padding: 0; - margin: 0; - display: grid; - gap: 6px; -} -.learn-test-detail { - display: grid; - padding-inline-start: 16px; - font-size: 13px; -} -.learn-error { - color: #c4291c; -} -.learn-solution summary { - cursor: pointer; - color: var(--accent); -} -.learn-solution[open] { - display: grid; - gap: 6px; - justify-items: start; -} -.learn-checklist { - list-style: none; - padding: 0; - margin: 0 0 14px; - display: grid; - gap: 6px; -} -.learn-checklist li { - display: flex; - gap: 8px; - align-items: baseline; - justify-content: space-between; -} -.learn-checklist li { - align-items: center; - justify-content: flex-start; - padding: 4px 6px; - border-radius: 8px; -} -.learn-check-text { - flex: 1; -} -.learn-checklist li[data-rating='sun'] { - background: color-mix(in srgb, #f5b800 14%, transparent); -} -.learn-checklist li[data-rating='cloud'] { - background: color-mix(in srgb, #8a97a8 14%, transparent); -} -.learn-checklist li[data-rating='umbrella'] { - background: color-mix(in srgb, #3b82f6 12%, transparent); -} -.learn-rating { - display: inline-flex; - gap: 2px; - flex: none; -} -.learn-rating-button { - min-width: 34px; - min-height: 34px; - padding: 2px; - font-size: 1.15em; - line-height: 1; - border-radius: 8px; - opacity: 0.45; - filter: grayscale(0.7); -} -.learn-rating-button[aria-pressed='true'] { - opacity: 1; - filter: none; - border-color: var(--accent); - background: var(--accent-soft); -} -.learn-rating-legend { - display: flex; - flex-wrap: wrap; - gap: 4px 14px; - margin: 0 0 8px; - font-size: 0.9em; - color: var(--muted, inherit); -} -.learn-review { - margin: 12px 0 0; -} -.learn-review:empty { - display: none; -} -.learn-review-title { - margin: 0 0 4px; - font-weight: 700; -} -.learn-review-icon { - flex: none; - width: 1.6em; - text-align: center; -} -.learn-checklist li.learn-review-deviation { - border: 1px solid var(--accent); - background: var(--accent-soft); -} -.learn-competences { - border: 1px solid var(--accent); - border-radius: 10px; - padding: 10px 12px; - margin: 0 0 14px; - background: var(--accent-soft); -} -.learn-competences-title { - margin: 0 0 4px; - font-weight: 700; -} -.learn-competences ul { - margin: 0; - padding-left: 1.3em; -} -.learn-return { - width: 100%; - margin: 0 0 12px; - border-color: var(--accent); - background: var(--accent-soft); -} -.learn-nav + .learn-return { - margin: 12px 0 0; -} -.learn-check-link { - flex: none; - font-size: 13px; -} -.learn-quiz fieldset { - border: 1px solid var(--border); - border-radius: 10px; - padding: 8px 12px 10px; - margin: 0 0 10px; - display: grid; - gap: 6px; -} -.learn-quiz legend { - font-weight: 600; - padding: 0 4px; -} -.learn-option { - text-align: start; -} -.learn-option.right { - border-color: #34c759; - background: rgba(52, 199, 89, 0.14); -} -.learn-option.wrong { - border-color: #ff3b30; - background: rgba(255, 59, 48, 0.1); -} -.learn-match { - display: grid; - gap: 6px; -} -.learn-match-left { - display: grid; - grid-template-columns: 8.5em minmax(0, 1fr); - align-items: center; - gap: 8px; -} -.learn-match-select { - min-width: 0; - width: 100%; - max-width: none; -} -.learn-match-select.right { - border-color: #34c759; - background: rgba(52, 199, 89, 0.14); -} -.learn-match-select.wrong { - border-color: #ff3b30; - background: rgba(255, 59, 48, 0.1); -} -.learn-explanation { - margin: 0; - color: var(--muted); - font-size: 13px; -} -.learn-score { - font-weight: 600; -} -.learn-nav { - display: flex; - justify-content: space-between; - gap: 8px; - margin-top: 18px; -} -@media (max-width: 1100px) { - .learn { - width: 340px; - } -} -@media (max-width: 900px) { - .learn { - width: 300px; - } -} -/* Code live: rechts neben dem Editor, wächst bei jeder Änderung mit */ -.livecode { - display: none; - flex: 0 1 auto; - width: 420px; - min-width: 200px; - flex-direction: column; - gap: 6px; - padding: 10px 12px 12px; - background: var(--panel); - border-inline-start: 1px solid var(--border); - min-height: 0; -} -.shell.live .livecode { - display: flex; -} -.livecode-head { - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 6px; -} -.livecode-head h2 { - margin: 0; - font-size: 16px; -} -.livecode-lang { - margin-inline-start: auto; - padding: 2px 8px; - border-radius: 6px; - background: var(--accent-soft); - color: var(--accent); - font-size: 12px; - font-weight: 600; -} -/* Seitenleisten-Symbol gespiegelt: der Bereich liegt rechts. */ -.livecode .learn-hide svg { - transform: scaleX(-1); -} -.livecode .hint { - margin: 0; -} -.livecode-view { - flex: 1; - min-height: 0; - padding: 10px 0; - font-size: var(--code-size, 14px); -} -.livecode-view .ln { - padding: 0 12px; - min-height: 1.5em; - border-inline-start: 3px solid transparent; -} -.livecode-view.selectable .ln { - cursor: pointer; -} -.livecode-view .ln.sel { - background: var(--accent-soft); - border-inline-start-color: var(--accent); -} -.livecode-view .ph { - color: var(--muted); - font-style: italic; -} -/* Java selbst tippen: Code-Editor (CodeMirror) */ -.livecode-editor { - flex: 1; - min-height: 0; - padding: 0; - font-size: var(--code-size, 14px); - white-space: normal; - overflow: hidden; - /* Farben wie in Eclipse: Text blau, Schlüsselwörter lila, Kommentare grün (Rot bleibt für Fehler) */ - --syn-keyword: #7f0055; - --syn-string: #2a00ff; - --syn-number: #986801; - --syn-comment: #3f7f5f; -} -.livecode-editor[hidden], -.livecode-view[hidden], -.livecode-problem[hidden] { - display: none; -} -.livecode-editor .cm-editor { - height: 100%; - background: transparent; -} -.livecode-editor .cm-editor.cm-focused { - outline: 2px solid var(--accent-soft); - outline-offset: -2px; - border-radius: 10px; -} -.livecode-editor .cm-scroller { - font: inherit; - font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - line-height: 1.5; -} -.livecode-editor .cm-gutters { - background: transparent; - border-inline-end: 1px solid var(--border); - color: var(--muted); -} -.livecode-editor .cm-lineNumbers .cm-gutterElement { - padding: 0 8px 0 10px; - min-width: 2ch; -} -.livecode-editor .cm-activeLineGutter { - background: transparent; - color: var(--text); -} -.livecode-editor .cm-content { - padding: 10px 0; - caret-color: var(--text); -} -.livecode-editor .cm-line { - padding: 0 12px; - border-inline-start: 3px solid transparent; -} -/* Farbige Blöcke wie in BlueJ: Klasse grün, Methode gelb, Schleife rosa, Verzweigung blau, try lila. - Zarte Fläche, feiner Rand und links ein kräftiger Streifen in der Farbe des Blocks. */ -.livecode-editor .cm-scopeLayer .cm-scope { - border: 1px solid color-mix(in srgb, var(--scope) 28%, transparent); - border-left: 3px solid var(--scope); - border-radius: 8px; - background: color-mix(in srgb, var(--scope) 9%, #fff); -} -.livecode-editor .cm-scopeLayer .cm-scope-class { - --scope: #34c47c; -} -.livecode-editor .cm-scopeLayer .cm-scope-method { - --scope: #f2b100; -} -.livecode-editor .cm-scopeLayer .cm-scope-loop { - --scope: #ec5fa6; -} -.livecode-editor .cm-scopeLayer .cm-scope-if { - --scope: #3b9cf0; -} -.livecode-editor .cm-scopeLayer .cm-scope-try { - --scope: #9a7cf0; -} -/* Einstellung „Blöcke farbig“ ausgeschaltet: nur der Rahmen der Auswahl bleibt */ -:root.no-code-scopes .livecode-editor .cm-scopeLayer .cm-scope:not(.cm-scope-sel) { - display: none; -} -/* Struktogramm in denselben Farben: Anweisungen gelb wie die Methode, Schleife rosa, Verzweigung/Fallauswahl blau. - Etwas kräftiger als im Code, weil der farbige Streifen links fehlt. */ -:root:not(.no-code-scopes) .ns-scope-method { - fill: color-mix(in srgb, #f2b100 14%, #fff); -} -:root:not(.no-code-scopes) .ns-scope-loop { - fill: color-mix(in srgb, #ec5fa6 14%, #fff); -} -:root:not(.no-code-scopes) .ns-scope-if { - fill: color-mix(in srgb, #3b9cf0 14%, #fff); -} -/* Elemente-Spalte in denselben Farben */ -:root:not(.no-code-scopes) .palette-item[data-scope='method'] { - background: color-mix(in srgb, #f2b100 14%, #fff); -} -:root:not(.no-code-scopes) .palette-item[data-scope='loop'] { - background: color-mix(in srgb, #ec5fa6 14%, #fff); -} -:root:not(.no-code-scopes) .palette-item[data-scope='if'] { - background: color-mix(in srgb, #3b9cf0 14%, #fff); -} -/* Ausgewähltes Element: Rahmen in der Akzentfarbe über den Blöcken */ -.livecode-editor .cm-scopeLayer .cm-scope-sel { - background: transparent; - border: 2px solid var(--accent); - box-shadow: 0 0 0 3px var(--accent-faint); -} -/* Debugger: nächste Zeile gelb, Haltepunkte als rote Punkte vor der Zeilennummer */ -.livecode-editor .cm-line.cm-debug-line { - background: rgba(255, 214, 10, 0.35); - border-inline-start-color: #e0a800; -} -.livecode-editor .cm-breakpoint-gutter .cm-gutterElement, -.livecode-editor .cm-lineNumbers .cm-gutterElement { - cursor: pointer; -} -.livecode-editor .cm-breakpoint-gutter .cm-gutterElement { - display: flex; - align-items: center; - justify-content: center; - width: 14px; - padding-inline-start: 4px; -} -/* Fehler: rote Zeilennummer (der Haltepunkt bleibt der runde Punkt) */ -.livecode-editor .cm-lineNumbers .cm-gutterElement.cm-error-line { - color: #fff; - background: #d33; - border-radius: 4px; - font-weight: 600; -} -.livecode-editor .cm-breakpoint { - display: inline-block; - width: 10px; - height: 10px; - border-radius: 50%; - background: #d33; -} -.livecode-editor .cm-breakpoint-gutter .cm-gutterElement:not(:has(.cm-breakpoint)):hover::before { - content: ''; - width: 10px; - height: 10px; - border-radius: 50%; - background: rgba(221, 51, 51, 0.3); -} -.livecode-debug { - flex: none; - max-height: 30%; - overflow-y: auto; -} -.livecode-debug[hidden] { - display: none; -} -.livecode-debug h2 { - margin: 0 0 4px; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); -} -.livecode-vars tr.changed td { - animation: var-changed 1.6s ease-out; - font-weight: 700; -} -@keyframes var-changed { - from { - background: rgba(255, 214, 10, 0.7); - } - to { - background: transparent; - } -} -.livecode-editor .cm-ph, -.livecode-editor .cm-ph * { - color: var(--muted) !important; - font-style: italic; - font-weight: normal !important; -} -.livecode-editor .cm-quickfix { - margin-inline-start: 8px; - padding: 0 3px; - border: 1px solid #e0a800; - border-radius: 4px; - background: rgba(255, 214, 10, 0.25); - font-size: 12px; - line-height: 1.3; - cursor: pointer; -} -.livecode-editor .cm-quickfix:hover { - background: rgba(255, 214, 10, 0.55); -} -.livecode-editor .cm-err { - text-decoration: underline wavy #d33; - text-decoration-skip-ink: none; - text-underline-offset: 3px; -} -.livecode-editor .cm-selectionBackground, -.livecode-editor .cm-focused .cm-selectionBackground { - background: color-mix(in srgb, var(--accent) 25%, transparent) !important; -} -.livecode-editor .cm-matchingBracket { - background: color-mix(in srgb, var(--accent) 20%, transparent); - outline: 1px solid color-mix(in srgb, var(--accent) 50%, transparent); -} -/* Vorschläge: groß genug für Finger auf dem Tablet */ -.cm-tooltip.cm-tooltip-autocomplete { - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12); - overflow: hidden; -} -.cm-tooltip.cm-tooltip-autocomplete > ul { - font: 14px/1.4 ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - max-height: 16em; -} -.cm-tooltip.cm-tooltip-autocomplete > ul > li { - padding: 4px 10px; -} -.cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected] { - background: var(--accent); - color: white; -} -.cm-tooltip-autocomplete .cm-completionDetail { - margin-inline-start: 12px; - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - font-style: normal; - color: var(--muted); -} -.cm-tooltip-autocomplete li[aria-selected] .cm-completionDetail { - color: rgba(255, 255, 255, 0.85); -} -.livecode-run { - padding: 3px 10px; - font-size: 13px; - margin-inline-start: 6px; -} -/* Konsole in Code live unter dem Code: Ausgaben, Eingabefeld, Ende oder Fehler. */ -.console { - display: flex; - flex: none; - flex-direction: column; - gap: 6px; - height: 180px; - min-height: 80px; - padding: 6px 10px 8px; - background: var(--bg); - border: 1px solid var(--border); - border-radius: 8px; -} -.console[hidden], -.console-resizer[hidden] { - display: none; -} -.console-head { - display: flex; - align-items: center; - gap: 8px; -} -.console-head h2 { - margin: 0; - font-size: 12px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); -} -.console-hide { - margin-inline-start: auto; - padding: 0 8px; - font-size: 14px; -} -.console-resizer { - flex: none; - height: 10px; - margin-block: -5px; - cursor: row-resize; - touch-action: none; - position: relative; -} -.console-resizer::after { - content: ''; - position: absolute; - inset-inline: 0; - top: 4px; - height: 2px; -} -.console-resizer:hover::after, -.console-resizer:focus-visible::after, -.console-resizer.dragging::after { - background: var(--accent); -} -.console-resizer:focus-visible { - outline: none; -} -.livecode-console-lines { - flex: 1; - min-height: 0; - overflow: auto; - font-size: var(--code-size, 14px); - white-space: pre-wrap; -} -.livecode-console-lines .typed { - color: var(--accent); -} -/* Simulation wartet auf eine Eingabe (im Simulationsbereich). */ -.livecode-console-lines .pending::after { - content: '▌'; - color: var(--accent); - animation: blink 1s steps(1) infinite; -} -@keyframes blink { - 50% { - opacity: 0; - } -} -.livecode-console-input { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; -} -.livecode-console-input[hidden] { - display: none; -} -.livecode-console-input label { - display: flex; - flex: 1; - align-items: center; - gap: 6px; - min-width: 0; - font-size: var(--code-size, 14px); -} -.livecode-console-input input { - flex: 1; - min-width: 60px; - padding: 4px 8px; - font: inherit; -} -.livecode-console-error, -.livecode-console-notice { - margin: 0; - font-size: 13px; - color: var(--muted); -} -.livecode-console-error { - flex-basis: 100%; - color: #c62828; -} -.livecode-console-notice.error { - color: #c62828; -} -.livecode-problem { - margin: 0; - min-height: 1.4em; - font-size: 13px; - color: #c62828; -} -.live-resizer { - display: none; - flex: none; - width: 10px; - margin-inline: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.shell.live .live-resizer { - display: block; -} -.live-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.live-resizer:hover::after, -.live-resizer:focus-visible::after, -.shell.resizing .live-resizer::after { - background: var(--accent); -} -.live-resizer:focus-visible { - outline: none; -} -@media (max-width: 1100px) { - .livecode { - width: 340px; - } -} - -/* Erst auf dem Handy untereinander; auf dem iPad hochkant bleibt der Lernbereich daneben. */ -@media (max-width: 700px) { - .shell.learning .learn-resizer, - .shell.live .live-resizer { - display: none; - } - .shell.files-open .files-resizer { - display: none; - } - .shell .files { - width: auto !important; - max-height: 40vh; - border-inline-end: none; - border-bottom: 1px solid var(--border); - } - .shell .learn, - .shell .livecode, - .columns > .app { - width: auto !important; - min-width: 0; - } - .livecode { - height: 40vh; - height: 40dvh; - border-inline-start: none; - border-top: 1px solid var(--border); - } - .learn-rail { - flex-direction: row; - justify-content: center; - width: auto; - padding: 4px 0; - border-inline-end: none; - border-bottom: 1px solid var(--border); - } - .learn-rail span { - writing-mode: horizontal-tb; - } - .live-rail { - border-inline-start: none; - border-bottom: none; - border-top: 1px solid var(--border); - } - .columns { - flex-direction: column; - } - .editor-rail { - border-inline-start: none; - border-bottom: 1px solid var(--border); - } - .learn { - width: auto; - height: 45vh; - height: 45dvh; - border-inline-end: none; - border-bottom: 1px solid var(--border); - } - .columns > .app, - .shell.editor-hidden .livecode { - flex: 1; - min-height: 0; - height: auto; - } -} - -@media print { - @page { - margin: 12mm; - } - body > *:not(.print-sheet) { - display: none !important; - } - body { - background: white !important; - } - .print-sheet { - display: block; - } - .print-sheet svg { - display: block; - width: 100%; - height: auto; - max-height: 100vh; - } -} -/* Fachbegriffe im Lernbereich: Erklärung beim Hovern, Antippen oder mit der Tastatur */ -.learn .term { - text-decoration: underline dotted var(--accent); - text-decoration-thickness: 2px; - text-underline-offset: 3px; - cursor: help; - border-radius: 3px; -} -.learn .term:focus-visible, -.learn .term[aria-expanded='true'] { - outline: none; - background: var(--accent-soft); -} -.term-tip { - position: fixed; - z-index: 1000; - max-width: min(300px, calc(100vw - 16px)); - padding: 8px 10px; - background: var(--panel); - color: var(--text); - border: 1px solid var(--border); - border-radius: 8px; - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18); - font-size: 14px; - line-height: 1.4; -} -.term-tip[hidden] { - display: none; -} -/* Zeiger aus dem Lernbereich auf einen Bereich des Editors */ -.learn .ui-pointer { - color: var(--accent); - font-weight: 600; - border-bottom: 2px solid var(--accent-soft); - cursor: pointer; - border-radius: 3px; -} -.learn .ui-pointer:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 1px; -} -.ui-spot { - outline: 3px solid var(--accent) !important; - outline-offset: -3px; - box-shadow: inset 0 0 0 9999px var(--accent-faint); - animation: ui-spot-pulse 1.2s ease-in-out infinite; -} -@keyframes ui-spot-pulse { - 50% { - outline-color: var(--accent-glow); - } -} -@media (prefers-reduced-motion: reduce) { - .ui-spot { - animation: none; - } -} -/* Erklärgrafiken und Tabellen in Lektionen */ -.learn-graphic { - display: block; - width: 100%; - max-width: 440px; - height: auto; - margin: 0 auto; - border-radius: 8px; - border: 1px solid var(--border); -} -.learn-animation { - background: #fff; - overflow: hidden; -} -.learn-animation > svg { - display: block; - width: 100%; - height: auto; -} -.learn-replay { - justify-self: center; -} -.learn-graphic-toggle { - display: block; - width: 100%; - padding: 0; - border: 0; - background: none; - cursor: zoom-in; -} -.learn-graphic-toggle[aria-pressed='true'] { - cursor: zoom-out; -} -.learn-graphic-hint { - margin: 4px 0 0; - text-align: center; - font-size: 0.85em; - color: var(--muted); -} -.lesson-table { - overflow-x: auto; - margin: 8px 0; -} -.lesson-table table { - border-collapse: collapse; - font-size: 13px; - font-variant-numeric: tabular-nums; -} -.lesson-table th, -.lesson-table td { - border: 1px solid var(--border); - padding: 3px 8px; - text-align: start; - white-space: nowrap; -} -.lesson-table th { - background: var(--bg); -} - -.learn-hints .lesson-note { - margin-top: 8px; -} - -.learn-teacher { - margin-top: 24px; - border-top: 1px solid var(--border); - padding-top: 12px; -} - -.learn-teacher summary { - cursor: pointer; - color: var(--muted); -} - -.learn-teacher input { - flex: 1; - min-width: 0; -} -.shell.editor-hidden:not(.live):not(.learning) .files { - flex: 1 1 0; - width: auto !important; -} - -/* Swing-Fenster aus echtem Java (CheerpJ) über der Konsole. */ -.livecode-swing { - height: 380px; -} -.livecode-swing[hidden] { - display: none; -} -.livecode-swing-display { - position: relative; - flex: 1; - min-height: 0; - overflow: hidden; - border-radius: 6px; - background: #e9ecf0; -} - -/* Caesar-Scheibe im Lernbereich: außen Klartext (fest), innen Geheimtext (drehbar) */ -.learn-caesar-stage { - display: flex; - flex-direction: column; - align-items: center; - gap: 8px; -} -.learn-caesar-disc { - width: min(100%, 320px); - touch-action: none; - user-select: none; - -webkit-user-select: none; -} -.learn-caesar-disc text { - font: 600 19px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.learn-caesar-disc .outer-disc { - fill: #f3e6c8; - stroke: #8a6d3b; - stroke-width: 3; -} -.learn-caesar-disc .outer-letters text { - fill: #3d2f14; - cursor: pointer; -} -.learn-caesar-disc .inner { - cursor: grab; - transition: transform 0.25s ease; -} -.learn-caesar-disc .inner.dragging { - cursor: grabbing; - transition: none; -} -.learn-caesar-disc .inner-disc { - fill: #6b4f2a; - stroke: #3d2f14; - stroke-width: 2; -} -.learn-caesar-disc .inner-letters text { - fill: #fff8e8; -} -.learn-caesar-disc .tick { - stroke: #8a6d3b; - stroke-width: 1; - opacity: 0.6; -} -.learn-caesar-disc .hub { - fill: #f3e6c8; - stroke: #3d2f14; - stroke-width: 2; -} -.learn-caesar-disc .window { - fill: var(--accent-soft); - stroke: var(--accent); - stroke-width: 2.5; -} -.learn-caesar-disc .marked text { - fill: var(--accent); - font-size: 23px; -} -.learn-caesar-disc .inner-letters .marked text { - fill: #ffd54a; -} -.learn-caesar-disc .key { - font-size: 40px; - font-weight: 700; - fill: #3d2f14; -} -.learn-caesar-disc .key-label { - font-size: 15px; - fill: #6e5a33; -} -.learn-caesar-controls, -.learn-caesar-mode { - display: flex; - justify-content: center; - gap: 8px; -} -.learn-caesar-controls button { - min-width: 56px; - font-size: 1.05em; -} -.learn-caesar-mode { - margin: 10px 0 6px; -} -.learn-caesar-mode button.active { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.learn-caesar-fields { - display: grid; - gap: 8px; -} -.learn-caesar-fields label, -.learn-caesar-fields > div { - display: grid; - gap: 3px; - color: var(--muted); - font-size: 0.9em; -} -.learn-caesar-fields input, -.learn-caesar-output { - font: 600 1.15em ui-monospace, Menlo, Consolas, monospace; - letter-spacing: 0.08em; - padding: 6px 8px; - border: 1px solid var(--border); - border-radius: 8px; - color: var(--text); - background: var(--panel); - overflow-wrap: anywhere; -} -.learn-caesar-output { - background: var(--bg); -} -/* Prinzessin retten: Weg zum Turm, Alphabet mit Indizes, Kreuzworträtsel */ -.learn-rescue-path { - display: flex; - align-items: center; - gap: 6px; - margin: 8px 0 2px; -} -.learn-rescue-ludwig { - width: 32px; - height: 32px; - border-radius: 50%; - flex: none; -} -.learn-rescue-dot { - flex: 1; - height: 6px; - max-width: 48px; - border-radius: 3px; - background: var(--border); - transition: background 0.3s; -} -.learn-rescue-dot.done { - background: #15803d; -} -.learn-rescue-tower { - position: relative; - font-size: 34px; - line-height: 1; - margin-inline-start: auto; -} -.learn-rescue-zora, -.learn-rescue-fox { - position: absolute; - font-size: 15px; -} -.learn-rescue-zora { - top: -4px; - left: 50%; - transform: translateX(-50%); -} -.learn-rescue-fox { - bottom: -2px; - right: -12px; - transition: transform 0.8s, opacity 0.8s; -} -.learn-rescue.freed .learn-rescue-fox { - transform: translateX(60px); - opacity: 0; -} -.learn-rescue.freed .learn-rescue-zora { - display: none; -} -.learn-rescue-free { - font-size: 24px; - line-height: 1; - animation: learn-rescue-jump 0.6s ease-out 3; -} -@keyframes learn-rescue-jump { - 50% { transform: translateY(-12px); } -} -.learn-rescue-answer { - display: flex; - gap: 8px; - align-items: center; -} -.learn-rescue-number { - width: 6ch; - font-size: 1.1em; - padding: 6px 8px; - text-align: center; -} -.learn-rescue-label { - margin: 14px 0 4px; - color: var(--muted); - font-size: 0.9em; -} -.learn-rescue-alphabet { - display: flex; - flex-wrap: wrap; - gap: 2px; - margin-bottom: 12px; -} -.learn-rescue-letter { - display: inline-flex; - flex-direction: column; - align-items: center; - min-width: 22px; - padding: 2px 0; - border-radius: 4px; - background: var(--bg); -} -.learn-rescue-letter small { - font-size: 10px; - color: var(--muted); -} -.learn-rescue-letter.hit { - background: #fde68a; - outline: 2px solid #d97706; -} -.learn-rescue-charat { - font-weight: 600; -} -.learn-rescue-moved { - font-weight: 400 !important; -} -.learn-rescue-word { - font: inherit; - padding: 1px 4px; - border: 1px solid var(--accent); - border-radius: 4px; -} -.learn-rescue-gate { - padding: 8px 0; - margin-bottom: 8px; -} -.learn-cw { - --cw-cell: 30px; - display: flex; - flex-wrap: wrap; - gap: 12px 20px; - margin-bottom: 10px; -} -.learn-cw-grid { - display: grid; - gap: 2px; -} -.learn-cw-cell { - position: relative; - width: var(--cw-cell); - height: var(--cw-cell); -} -.learn-cw-cell input { - width: 100%; - height: 100%; - box-sizing: border-box; - padding: 0; - border: 1px solid #6e6e73; - border-radius: 3px; - background: #fff; - color: #1c1c1e; - text-align: center; - font: 600 16px/1 ui-monospace, SFMono-Regular, Menlo, monospace; - text-transform: uppercase; -} -.learn-cw-cell input:disabled { - opacity: 1; - -webkit-text-fill-color: #1c1c1e; -} -.learn-cw-cell.key input { - background: #fde68a; -} -.learn-cw-cell.given input { - color: #1d4ed8; - -webkit-text-fill-color: #1d4ed8; -} -.learn-cw-cell.arrived input { - animation: learn-cw-arrive 0.6s ease-out; -} -.learn-cw-cell input.wrong { - border-color: #b91c1c; - background: #fee2e2; -} -@keyframes learn-cw-arrive { - from { transform: scale(1.6); } -} -.learn-cw-number, -.learn-cw-keyindex { - position: absolute; - font-size: 8px; - line-height: 1; - pointer-events: none; - color: #6e6e73; - z-index: 1; -} -.learn-cw-number { - top: 2px; - left: 2px; -} -.learn-cw-keyindex { - bottom: 2px; - right: 2px; - color: #b45309; -} -.learn-cw-keyword { - margin: 8px 0 0; - font-weight: 600; -} -.learn-cw-keybox { - display: inline-block; - width: 22px; - height: 24px; - margin-inline-end: 2px; - border-bottom: 2px solid #d97706; - background: #fef3c7; - text-align: center; - line-height: 24px; - white-space: pre; -} -.learn-cw-cluebox { - flex: 1; - min-width: 200px; -} -.learn-cw-clues { - margin: 4px 0 10px; - padding-inline-start: 24px; -} -.learn-cw-clues li { - cursor: pointer; -} -@media (max-width: 380px) { - .learn-cw { - --cw-cell: 28px; - } -} -/* Lückentext: fertiger Code mit Eingabefeldern für die fehlenden Wörter */ -.learn-cloze { - border: 2px solid var(--accent); - border-radius: 12px; - padding: 12px 14px; - margin: 0 0 14px; -} -.learn-cloze-title { - display: block; - font-size: 1.05em; - margin-bottom: 4px; -} -.learn-cloze p { - margin: 0 0 8px; -} -.learn-cloze-hint { - color: var(--muted); - font-size: 0.9em; -} -.learn-cloze-code { - margin: 0 0 10px; - padding: 8px 10px; - border-radius: 8px; - background: var(--bg, #f2f2f7); - font-size: 13px; - line-height: 1.9; - overflow-x: auto; -} -.learn-cloze-code .ln { - white-space: pre; - min-height: 1.9em; -} -.learn-cloze-gap { - font-size: 13px; - padding: 1px 3px; - margin: 0 1px; - min-width: 0; - max-width: none; - border: 1px solid #8e8e93; - border-bottom-width: 2px; - border-radius: 4px; - background: #fff; - color: #1c1c1e; - text-align: center; -} -.learn-cloze-gap.right { - border-color: #34c759; - background: rgba(52, 199, 89, 0.14); -} -.learn-cloze-gap.wrong { - border-color: #ff3b30; - background: rgba(255, 59, 48, 0.1); -} -.learn-cloze-gap.shown { - border-color: #d97706; - background: #fef3c7; -} -.learn-cloze-gap:disabled { - opacity: 1; - -webkit-text-fill-color: currentColor; -} -.learn-cloze-actions { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 8px; -} -.learn-cloze-check { - font-weight: 600; -} -.learn-cloze-status:empty { - display: none; -} -.learn-cloze-status { - padding: 8px 12px; - border-radius: 8px; - font-weight: 600; -} -.learn-cloze-status.right { - background: #dcfce7; -} -.learn-cloze-status.wrong { - background: #fee2e2; -} -/* PAP bauen: Plan mit leeren Plätzen, darunter die fertigen Elemente */ -.learn-pap { - border: 2px solid var(--accent); - border-radius: 12px; - padding: 12px 14px; - margin: 0 0 14px; -} -.learn-pap-title { - display: block; - font-size: 1.05em; - margin-bottom: 4px; -} -.learn-pap p { - margin: 0 0 8px; -} -.learn-pap-hint { - color: var(--muted); - font-size: 0.9em; -} -.learn-pap-plan { - display: block; - width: 100%; - max-width: 420px; - margin: 0 auto 10px; - background: #fff; - border-radius: 8px; - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - fill: #1c1c1e; -} -.learn-pap-slot { - cursor: pointer; - outline: none; -} -.learn-pap-slot .learn-pap-shape { - stroke: #8e8e93; - stroke-width: 2; -} -.learn-pap-slot:focus-visible .learn-pap-shape, -.learn-pap-slot.over .learn-pap-shape { - stroke: var(--accent); - stroke-width: 3; -} -.learn-pap-slot.right .learn-pap-shape { - stroke: #34c759; - stroke-width: 3; -} -.learn-pap-slot.wrong .learn-pap-shape { - stroke: #ff3b30; - stroke-width: 3; -} -.learn-pap-slot.shown .learn-pap-shape { - stroke: #d97706; -} -.learn-pap-legend { - display: grid; - grid-template-columns: auto 1fr; - gap: 4px 10px; - align-items: center; - max-width: 420px; - margin: 0 auto 12px; - padding: 8px 10px; - border-radius: 8px; - background: var(--bg, #f2f2f7); - font-size: 0.9em; -} -.learn-pap-legend dt { - line-height: 0; -} -.learn-pap-legend dd { - margin: 0; -} -.learn-pap-work { - display: grid; - grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); - gap: 8px; - align-items: start; - margin-bottom: 10px; -} -.learn-pap-work.done { - grid-template-columns: minmax(0, 1fr); -} -.learn-pap-left .learn-pap-plan { - margin-bottom: 8px; -} -.learn-pap-tray { - position: sticky; - top: 0; - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; - max-height: 100vh; - overflow-y: auto; - padding: 6px 2px; - border-radius: 8px; - background: var(--bg, #f2f2f7); -} -.learn-pap-tray:empty, -.learn-pap-tray:not(:has(.learn-pap-card:not([hidden]))) { - display: none; -} -.learn-pap-card svg { - max-width: 100%; - height: auto; -} -.learn-pap-card { - padding: 2px; - max-width: 100%; - border: 2px solid transparent; - border-radius: 10px; - background: none; - line-height: 0; - touch-action: none; - cursor: grab; - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - fill: #1c1c1e; -} -.learn-pap-card.selected { - border-color: var(--accent); - background: var(--accent-soft); -} -.learn-pap-card.dragging { - opacity: 0.3; -} -.learn-pap-ghost { - position: fixed; - z-index: 1000; - pointer-events: none; - transform: translate(-50%, -50%); - opacity: 0.9; -} -.learn-pap-actions { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-bottom: 8px; -} -.learn-pap-check { - font-weight: 600; -} -.learn-pap-status:empty { - display: none; -} -.learn-pap-status { - padding: 8px 12px; - border-radius: 8px; - font-weight: 600; -} -.learn-pap-status.right { - background: #dcfce7; -} -.learn-pap-status.wrong { - background: #fee2e2; -} -/* Farbige Markierung im Text, passend zu farbigen Kästen in Lektion 1 (gelb, blau, rosa wie „Blöcke farbig“) */ -.learn-mark { - color: inherit; - padding: 0 3px; - border-radius: 3px; -} -.learn-mark.blau { - background: #e4f1fd; -} -.learn-mark.orange { - background: #ffedd5; -} -.learn-mark.gruen { - background: #dcfce7; -} -.learn-mark.gelb { - background: #fdf4db; -} -.learn-mark.rosa { - background: #fce9f2; -} - -/* Hinweis statt leerer Seite, wenn die App nicht starten konnte (main.ts) */ -.startup-error { - max-width: 520px; - margin: 15vh auto 0; - padding: 0 16px; - font: 16px/1.5 system-ui, -apple-system, sans-serif; - color: var(--text); -} -.startup-error h1 { - font-size: 22px; -} -.startup-error button { - font-size: 16px; - padding: 8px 16px; -} -.startup-error pre { - margin-top: 24px; - color: var(--muted); - font-size: 12px; - white-space: pre-wrap; -} - -/* VisuBot: Welt mit dem mBot2 im Simulationspanel, zusätzliche Elemente in der Palette */ -.visubot-stage { - position: relative; - height: 280px; - border: 1px solid var(--border); - border-radius: 8px; - overflow: hidden; - background: linear-gradient(#dfe9f3, #f6f3ec); -} -.visubot-live { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; - margin: 0 0 8px; - font-size: 12px; - color: var(--muted); -} -.visubot-live:empty { - display: none; -} -.visubot-live.connected { - color: #0f8a5f; - font-weight: 600; -} -.visubot-worlds { - display: flex; - gap: 6px; - margin: 0 0 6px; -} -.visubot-worlds button.active { - background: var(--accent); - border-color: var(--accent); - color: white; -} -.visubot-canvas { - display: block; - width: 100%; - height: 100%; - touch-action: none; -} -.visubot-status { - font-size: 12px; - color: var(--muted); - margin: 6px 0 0; -} -.visubot-controls { - margin-top: 8px; - font-size: 13px; -} -.visubot-controls summary { - cursor: pointer; - font-weight: 600; -} -.visubot-controls .hint { - color: var(--muted); - font-size: 12px; - margin: 4px 0 6px; -} -.visubot-controls label { - display: grid; - grid-template-columns: 9.5em 1fr 2.5em; - align-items: center; - gap: 6px; - margin: 2px 0; -} -.visubot-controls output { - text-align: end; - font-variant-numeric: tabular-nums; -} -.visubot-presses { - display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 6px; -} -.visubot-presses button { - min-width: 2.2em; -} -.visubot-presses button.pressed { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.visubot-check { - margin-top: 8px; - font-size: 13px; -} -.visubot-check summary { - cursor: pointer; - font-weight: 600; -} -.visubot-check .hint { - color: var(--muted); - font-size: 12px; - margin: 4px 0 6px; -} -.visubot-check-sensors { - margin-top: 8px; - border-collapse: collapse; - font-variant-numeric: tabular-nums; -} -.visubot-check-sensors th { - text-align: start; - font-weight: normal; - padding: 1px 12px 1px 0; -} -.palette-visubot-title { - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.04em; - color: var(--muted); - margin: 14px 0 6px; -} -.palette-sensor { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 2px; - border-style: dashed; -} -.palette-sensor code { - font-size: 12px; - color: var(--muted); -} -.palette-visubot .palette-item { - border-color: #a7a0d3; - background: color-mix(in srgb, #a7a0d3 16%, transparent); -} -.visubot-model { - margin: 12px 0; -} -.visubot-model .visubot-stage { - height: 300px; -} -.visubot-model-hint { - font-size: 13px; - margin: 4px 0 0; -} -.visubot-part-label { - position: absolute; - pointer-events: none; - padding: 2px 8px; - border-radius: 6px; - background: rgba(20, 24, 30, 0.85); - color: white; - font-size: 13px; - white-space: nowrap; - z-index: 1; -} -/* Wörter im Text, die auf ein Teil des mBot2 zeigen (wie bei JavaPicoGoat gelb unterstrichen) */ -.robot-part-link { - text-decoration: underline 2px #f2b705; - text-underline-offset: 3px; - cursor: pointer; -} -.robot-part-link:hover, -.robot-part-link:focus-visible { - background: color-mix(in srgb, #f2b705 25%, transparent); - outline: none; -} -.visubot-quiz-question { - font-size: 15px; - margin: 12px 0 6px; -} -.visubot-quiz-question.done { - color: var(--success, #2f9e44); -} -.visubot-quiz-feedback { - min-height: 1.4em; - margin: 6px 0; -} -.visubot-quiz-feedback.right { - color: var(--success, #2f9e44); -} -.visubot-quiz-feedback.wrong { - color: var(--danger, #c92a2a); -} - -/* Echter mBot2: Knopf neben „Simulation starten“ und in den Einstellungen */ -.diagram-tools .mbot-link { - margin-inline-start: auto; - height: 30px; - padding: 0 10px; - white-space: nowrap; -} -.diagram-tools .mbot-link:not([hidden]) + .sim-toggle, -.diagram-tools .mbot-upload:not([hidden]) + .sim-toggle { - margin-inline-start: 6px; -} -.diagram-tools .mbot-upload { - margin-inline-start: 6px; - height: 30px; - padding: 0 10px; - white-space: nowrap; -} -/* Übertragen auf den mBot2 */ -.mbot-upload-steps { - margin: 0 0 12px; - padding-inline-start: 22px; - line-height: 1.5; -} -.mbot-upload progress { - width: 100%; - height: 10px; -} -.mbot-upload-status.done { - color: var(--success, #2f9e44); - font-weight: 600; -} -.settings-mbot-link { - display: grid; - gap: 4px; - margin-top: 8px; -} -.settings-mbot-link[hidden] { - display: none; -} -.settings-mbot-link small { - font-size: 12px; - color: var(--muted); -} -/* VisuStruct oder VisuBot (Zahnrad und Start-Dialog) */ -.world-chooser { - margin-bottom: 4px; -} -/* Vier Welten: zwei Reihen mit je zwei Knöpfen, damit sie ins Zahnrad-Menü passen. */ -.segmented.world-chooser { - display: grid; - grid-template-columns: 1fr 1fr; -} -.segmented.world-chooser button { - border-radius: 0; - border-inline-start: 1px solid var(--border); -} -.segmented.world-chooser button:nth-child(even) { - border-inline-start: none; -} -.segmented.world-chooser button:nth-child(n + 3) { - border-top: none; -} -.segmented.world-chooser button:first-child { - border-start-start-radius: 8px; -} -.segmented.world-chooser button:nth-child(2) { - border-start-end-radius: 8px; -} -.segmented.world-chooser button:nth-last-child(2) { - border-end-start-radius: 8px; -} -.segmented.world-chooser button:last-child { - border-end-end-radius: 8px; -} -.segmented.world-chooser button[aria-pressed='true'] { - border-color: var(--accent); -} -.world-hint { - display: block; - font-size: 12px; - color: var(--muted); - margin-bottom: 8px; -} -.start-world { - margin-bottom: 14px; -} -/* Start-Dialog auf einer Welt-Subdomain: oben der Name der Welt */ -.start-brand { - display: flex; - align-items: center; - gap: 6px; - font-size: 22px; - font-weight: 700; - margin: 0 0 12px; -} -/* „Struct“ im Verlauf wie auf visucode.de (MARKE.md: Blau → Violett) */ -.visustruct-struct { - color: #4f46e5; - background: linear-gradient(90deg, #2563eb, #7c3aed); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; -} -/* Auf einer Welt-Subdomain statt der Welt-Frage: Marke „VisuCode“ als Link auf visucode.de */ -.visucode-more { - margin: 6px 0 8px; -} -.visucode-more small { - display: block; - font-size: 12px; - color: var(--muted); -} -/* Schriftzug wie auf visucode.de (MARKE.md): „Visu“ in Textfarbe, „Code“ im Verlauf durch alle Produktfarben */ -.visucode-brand { - font-weight: 800; - font-size: 16px; - letter-spacing: -0.01em; - color: var(--text); - text-decoration: none; -} -.visucode-brand span { - color: #7c3aed; - background: linear-gradient(90deg, #2563eb, #c026d3, #e11d48, #ea580c, #eab308, #84cc16, #16a34a, #0f766e, #0284c7); - -webkit-background-clip: text; - background-clip: text; - -webkit-text-fill-color: transparent; -} -.visucode-more .visucode-credit { - margin-top: 4px; -} -.visucode-more small a { - color: inherit; -} -.visucode-brand:hover { - text-decoration: underline; -} - -/* VisuBot: „mBot2 live“ als eigene Spalte ganz rechts oder unten in „Programmieren“. */ -.robotpreview, -.robotpreview-rail, -.preview-resizer { - display: none; -} -.shell.robot-world.preview .robotpreview { - display: flex; -} -.shell.robot-world:not(.preview) .robotpreview-rail { - display: flex; - border-inline-end: none; - border-inline-start: 1px solid var(--border); -} -.robotpreview-rail svg { - transform: scaleX(-1); -} -.shell.robot-world.preview:not(.preview-stacked) .preview-resizer { - display: block; -} -.robotpreview { - flex: 0 1 auto; - width: 360px; - min-width: 220px; - flex-direction: column; - gap: 6px; - padding: 10px 12px 12px; - background: var(--panel); - border-inline-start: 1px solid var(--border); - min-height: 0; - overflow-y: auto; -} -.robotpreview .hint { - margin: 0; - font-size: 0.85em; - color: var(--muted, inherit); -} -.robotpreview .robotpreview-stage { - flex: 1 1 auto; - height: auto; - min-height: 160px; -} -.robotpreview .robotpreview-status { - margin: 0; - min-height: 1.3em; -} -.robotpreview-height { - display: none; -} -/* Während der Simulation fährt hier deren mBot2 (Welten, 3D-Ansicht und Messwerte aus dem Simulationspanel). */ -.robotpreview.simulation > .visubot-worlds, -.robotpreview.simulation > .robotpreview-stage, -.robotpreview.simulation > .robotpreview-status, -.robotpreview.simulation .robotpreview-play { - display: none; -} -.robotpreview .visubot-viewport { - display: flex; - flex: 1 1 auto; - flex-direction: column; - min-height: 0; -} -.robotpreview .visubot-viewport .visubot-stage { - flex: 1 1 auto; - height: auto; - min-height: 160px; -} -.visubot-elsewhere { - display: none; - margin: 0 0 8px; - font-size: 12px; - color: var(--muted); -} -.visubot:not(:has(> .visubot-viewport)) .visubot-elsewhere { - display: block; -} -/* Unter dem Code: volle Breite der Spalte, Höhe an der Linie darüber verschieben. */ -.shell.preview-stacked .robotpreview { - flex: 0 0 auto; - width: auto !important; - height: 320px; - min-width: 0; - margin: 4px -12px -12px; - padding-top: 0; - border-inline-start: none; - border-top: 1px solid var(--border); - overflow: visible; -} -.shell.preview-stacked .robotpreview-height { - display: block; - height: 10px; - margin: -5px 0 0; - cursor: row-resize; - touch-action: none; - position: relative; -} -.robotpreview-height::after { - content: ''; - position: absolute; - inset-inline: 0; - top: 4px; - height: 2px; - background: transparent; - transition: background 0.15s; -} -.robotpreview-height:hover::after, -.robotpreview-height:focus-visible::after, -.shell.resizing .robotpreview-height::after { - background: var(--accent); -} -.robotpreview-height:focus-visible { - outline: none; -} -.preview-resizer { - flex: none; - width: 10px; - margin-inline: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.preview-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.preview-resizer:hover::after, -.preview-resizer:focus-visible::after, -.shell.resizing .preview-resizer::after { - background: var(--accent); -} -.preview-resizer:focus-visible { - outline: none; -} - -/* mBot2-Elemente: Gruppen wie im Nachschlagen, auf- und zuklappbar */ -.palette-group { - grid-column: 1 / -1; -} -.palette-group button { - display: flex; - align-items: center; - gap: 6px; - width: 100%; - margin-top: 4px; - padding: 2px 0; - border: none; - background: none; - color: var(--muted); - font-size: 12px; - font-weight: 600; - text-align: start; - cursor: pointer; -} -.palette-group button::before { - content: '▾'; - transition: transform 0.15s; -} -.palette-group button[aria-expanded='false']::before { - transform: rotate(-90deg); -} -.palette-item[hidden] { - display: none; -} - -/* ⇄ „Programmieren groß“: Code füllt den Platz, das Struktogramm wird schmal (an der Linie dazwischen ziehbar). */ -.editor-resizer { - display: none; - flex: none; - width: 10px; - margin-inline: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.editor-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.editor-resizer:hover::after, -.editor-resizer:focus-visible::after, -.shell.resizing .editor-resizer::after { - background: var(--accent); -} -.editor-resizer:focus-visible { - outline: none; -} -.shell.code-main.live:not(.editor-hidden) .editor-resizer { - display: block; -} -.shell.code-main.live:not(.editor-hidden) .live-resizer { - display: none; -} -.shell.code-main.live:not(.editor-hidden) .app { - flex: 0 1 auto; - width: 460px; - min-width: 300px; -} -.shell.code-main.live:not(.editor-hidden) .livecode { - flex: 1 1 0; - width: auto !important; - min-width: 0; -} -/* ⇄ tauscht auch die Plätze: Programmieren links, Struktogramm rechts daneben, danach mBot2 live. */ -.shell.code-main.live:not(.editor-hidden) .livecode { - order: 1; -} -.shell.code-main.live:not(.editor-hidden) .editor-resizer { - order: 2; -} -.shell.code-main.live:not(.editor-hidden) .app { - order: 3; - border-inline-start: 1px solid var(--border); -} -.shell.code-main.live:not(.editor-hidden) .live-rail, -.shell.code-main.live:not(.editor-hidden) .preview-resizer, -.shell.code-main.live:not(.editor-hidden) .robotpreview, -.shell.code-main.live:not(.editor-hidden) .robotpreview-rail { - order: 4; -} -[data-action='swap-main'] { - font-size: 16px; - line-height: 1; -} -[data-action='swap-main'][aria-pressed='true'] { - color: var(--accent); -} - -/* Trennlinie zwischen Elementen und Struktogramm (liegt über dem rechten Rand der Elemente) */ -.palette-resizer { - grid-area: pal; - justify-self: end; - width: 10px; - margin-inline-end: -5px; - z-index: 5; - cursor: col-resize; - touch-action: none; - position: relative; -} -.palette-resizer::after { - content: ''; - position: absolute; - inset-block: 0; - inset-inline-start: 4px; - width: 2px; - background: transparent; - transition: background 0.15s; -} -.palette-resizer:hover::after, -.palette-resizer:focus-visible::after, -.shell.resizing .palette-resizer::after { - background: var(--accent); -} -.palette-resizer:focus-visible { - outline: none; -} -.app.simulating .palette-resizer { - display: none; -} - -/* Welt VisuObject in Petrol: Java-Code links, Klassendiagramm rechts (objectApp.ts) */ -:root.visuobject-theme { - --bg: #edf4f6; - --text: #17292e; - --muted: #566a70; - --border: #cddde1; - --accent: #0e7c86; - --accent-soft: rgba(14, 124, 134, 0.2); - --accent-faint: rgba(14, 124, 134, 0.08); - --accent-glow: rgba(14, 124, 134, 0.35); -} -.visuobject-obj { - color: var(--accent); -} -.visuobject .brand .logo { - color: var(--accent); -} -.shell.visuobject button.text-button { - padding: 5px 10px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); - color: var(--text); - font: inherit; - cursor: pointer; -} -.shell.visuobject button.text-button:hover { - border-color: var(--accent); - color: var(--accent); -} -/* Aufteilung wie in WebGoatCode: flache Bereiche, 1 px Linie dazwischen (objectLayout.ts setzt --vo-cols) */ -:root.visuobject-theme { - --vo-bar: #e3edf0; -} -.vo-columns { - display: grid; - grid-template-columns: var(--vo-cols, 260px 1px minmax(0, 45fr) 1px minmax(0, 55fr)); - flex: 1; - min-height: 0; - border-top: 1px solid var(--border); -} -.vo-columns.vo-resizing { - cursor: col-resize; - user-select: none; -} -.vo-pane { - display: flex; - flex-direction: column; - min-width: 0; - min-height: 0; - background: var(--panel); -} -.vo-pane.collapsed > :not(.vo-strip) { - display: none; -} -.vo-strip { - display: none; - flex: 1; - align-items: center; - justify-content: center; - padding: 12px 0; - border: 0; - background: var(--vo-bar); - color: var(--muted); - font: 600 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - writing-mode: vertical-rl; - cursor: pointer; -} -.vo-pane.collapsed > .vo-strip { - display: flex; -} -.vo-strip:hover, -.vo-strip:focus-visible { - color: var(--accent); - background: var(--accent-faint); - outline: none; -} -.vo-splitter { - position: relative; - z-index: 4; - background: var(--border); - cursor: col-resize; - touch-action: none; - outline: none; -} -.vo-splitter::after { - content: ''; - position: absolute; - inset: 0 -4px; -} -.vo-splitter:hover, -.vo-splitter:focus-visible, -.vo-resizing .vo-splitter { - background: var(--accent); -} -.vo-splitter.fixed { - cursor: default; - background: var(--border); -} -/* Kopfleiste jedes Bereichs: links Titel oder Reiter, rechts Knöpfe, ganz rechts – zum Einklappen */ -.vo-bar { - display: flex; - flex-wrap: wrap; - align-items: stretch; - min-height: 40px; - background: var(--vo-bar); - border-bottom: 1px solid var(--border); - font-size: 14px; -} -.vo-bar-title { - display: flex; - flex: 1; - align-items: center; - margin: 0; - text-transform: none; - letter-spacing: normal; - padding: 0 12px; - font-size: 14px; - font-weight: 600; - color: var(--text); -} -.vo-bar-actions { - display: flex; - flex: 0 1 auto; - flex-wrap: wrap; - align-items: center; - justify-content: flex-end; - gap: 6px; - padding: 4px 4px 4px 0; -} -.shell.visuobject .vo-bar button.text-button { - padding: 2px 8px; - border-radius: 6px; -} -.vo-collapse { - padding: 0 12px; - border: 0; - background: none; - color: var(--muted); - font-size: 16px; - font-weight: 700; - cursor: pointer; -} -.vo-collapse:hover, -.vo-collapse:focus-visible { - color: var(--accent); -} -/* Datei-Reiter wie index.html | style.css */ -.vo-tabs { - display: flex; - flex: 1; - min-width: 0; - overflow-x: auto; -} -.vo-tab { - display: flex; - align-items: stretch; - border-right: 1px solid var(--border); - border-bottom: 3px solid transparent; -} -.vo-tab.active { - background: var(--panel); - border-bottom-color: var(--accent); -} -.vo-tab [role='tab'] { - padding: 0 4px 0 14px; - border: 0; - background: none; - color: var(--text); - font: 13px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - white-space: nowrap; - cursor: pointer; -} -.vo-tab.active [role='tab'] { - font-weight: 700; -} -.vo-tab [role='tab']:hover { - color: var(--accent); -} -.vo-tab-close { - padding: 0 10px 0 6px; - border: 0; - background: none; - color: var(--muted); - font-size: 15px; - cursor: pointer; - visibility: hidden; -} -.vo-tab.active .vo-tab-close, -.vo-tab:hover .vo-tab-close { - visibility: visible; -} -.vo-tab-close:hover { - color: var(--accent); -} -.vo-tab-close .vo-dot, -.vo-tab-close.unsaved .vo-x { - display: none; -} -.vo-tab-close.unsaved { - visibility: visible; - color: var(--text); -} -.vo-tab-close.unsaved .vo-dot { - display: inline; - font-size: 11px; -} -.vo-tab-close.unsaved:hover .vo-dot { - display: none; -} -.vo-tab-close.unsaved:hover .vo-x { - display: inline; -} -.vo-no-file { - display: flex; - align-items: center; - padding: 0 12px; - color: var(--muted); -} -.vo-editor { - flex: 1; - min-height: 0; - overflow: auto; - font-size: 14px; -} -.vo-foot { - display: flex; - flex-wrap: wrap; - align-items: baseline; - gap: 4px 12px; - padding: 6px 12px; - border-top: 1px solid var(--border); - background: var(--vo-bar); -} -.vo-hint { - color: var(--muted); - flex: 1 1 260px; -} -.vo-canvas { - position: relative; - flex: 1; - min-height: 0; - overflow: auto; - background: radial-gradient(circle, var(--border) 1px, transparent 1px) 0 0 / 20px 20px; - outline: none; -} -.vo-frames { - position: absolute; - inset: 0 auto auto 0; - z-index: 0; - pointer-events: none; - overflow: visible; -} -.vo-package-frame { - fill: rgba(14, 124, 134, 0.035); - stroke: var(--muted); - stroke-width: 1.2; -} -.vo-package.current .vo-package-frame { - stroke: var(--accent); - fill: var(--accent-faint); -} -.vo-package-name { - font: 600 12px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - fill: var(--muted); -} -.vo-package.current .vo-package-name { - fill: var(--accent); -} -.vo-lines { - position: absolute; - inset: 0 auto auto 0; - z-index: 3; - pointer-events: none; - overflow: visible; -} -.vo-rel { - stroke: var(--text); - stroke-width: 1.4; - fill: none; -} -.vo-rel.ghost { - stroke: var(--accent); - stroke-dasharray: 4 4; -} -.vo-hit { - stroke: transparent; - stroke-width: 12; - pointer-events: stroke; - cursor: pointer; -} -.vo-relation:hover .vo-rel, -.vo-relation:hover .vo-diamond { - stroke: var(--accent); -} -.vo-relation.selected .vo-rel, -.vo-relation.selected .vo-diamond { - stroke: var(--accent); - stroke-width: 2.4; -} -.vo-relation.selected .vo-label-svg { - fill: var(--accent); -} -.vo-diamond { - fill: var(--panel); - stroke: var(--text); - stroke-width: 1.4; -} -.vo-diamond.filled { - fill: var(--text); -} -.vo-relation.selected .vo-diamond.filled { - fill: var(--accent); -} -.vo-label-svg.name { - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - font-style: italic; - fill: var(--text); -} -.vo-rel.dashed { - stroke-dasharray: 6 4; -} -.vo-hollow { - fill: none; - stroke: var(--text); - stroke-width: 1.4; -} -.vo-open { - fill: none; - stroke: var(--text); - stroke-width: 1.4; -} -.vo-label-svg { - font: 12px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - fill: var(--muted); - paint-order: stroke; - stroke: var(--panel); - stroke-width: 4px; -} -.vo-empty { - margin: 24px; - max-width: 40ch; - color: var(--muted); -} -.vo-status { - margin: 0; - color: #b3261e; -} -.vo-status:empty { - display: none; -} -.vo-status.hint { - color: var(--accent); -} -.uml-class { - position: absolute; - left: 16px; - top: 16px; - min-width: 150px; - background: var(--panel); - border: 1.5px solid var(--text); - font: 13px/1.5 ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - cursor: grab; - user-select: none; - touch-action: none; -} -/* Griff am gewählten Kasten: von hier eine Beziehung ziehen */ -.uml-connect { - position: absolute; - right: -11px; - top: 50%; - width: 20px; - height: 20px; - margin-top: -10px; - padding: 0; - border: 2px solid var(--panel); - border-radius: 50%; - background: var(--accent); - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.25); - cursor: crosshair; - touch-action: none; -} -.uml-connect::after { - content: ''; - position: absolute; - inset: 5px 4px 5px 5px; - background: #fff; - clip-path: polygon(0 35%, 55% 35%, 55% 0, 100% 50%, 55% 100%, 55% 65%, 0 65%); -} -.uml-class.drop { - border-color: var(--accent); - box-shadow: 0 0 0 4px var(--accent-soft); -} -.vo-relmenu { - position: absolute; - z-index: 10; - display: grid; - min-width: 220px; - padding: 6px; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); - font: 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-relmenu p { - margin: 2px 8px 6px; - color: var(--muted); - font-size: 12px; -} -.vo-relmenu button { - display: flex; - align-items: center; - gap: 10px; - padding: 6px 8px; - border: 0; - border-radius: 6px; - background: none; - color: var(--text); - font: inherit; - text-align: left; - cursor: pointer; -} -.vo-relmenu button:hover, -.vo-relmenu button:focus-visible { - background: var(--accent-faint); - color: var(--accent); - outline: none; -} -.vo-relicon { - flex: none; -} -/* Werkzeugleiste über dem Diagramm (wie in Astah): Auswahl, Klasse, Interface, Beziehungen */ -.vo-palette { - display: flex; - flex-wrap: wrap; - align-items: stretch; - gap: 2px; - padding: 4px 6px; - border-bottom: 1px solid var(--border); - background: var(--panel); -} -.vo-palette-sep { - width: 1px; - margin: 4px 4px; - background: var(--border); -} -.vo-tool { - display: grid; - justify-items: center; - gap: 1px; - min-width: 44px; - padding: 3px 3px 2px; - border: 1px solid transparent; - border-radius: 7px; - background: none; - color: var(--text); - font: 11px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - cursor: pointer; - touch-action: none; - user-select: none; -} -.vo-tool:hover { - background: var(--accent-faint); - color: var(--accent); -} -.vo-tool[aria-pressed='true'] { - border-color: var(--accent); - background: var(--accent-soft); - color: var(--accent); -} -.vo-tool[data-tool='class'], -.vo-tool[data-tool='interface'] { - cursor: grab; -} -.vo-tool-ghost { - position: fixed; - z-index: 1000; - display: grid; - justify-items: center; - min-width: 120px; - padding: 4px 10px 14px; - margin: -14px 0 0 -60px; - border: 1.5px dashed var(--accent); - background: rgba(255, 255, 255, 0.9); - font: 13px ui-monospace, 'SF Mono', Menlo, Consolas, monospace; - pointer-events: none; -} -.vo-tool-ghost small { - color: var(--muted); - font-size: 11px; -} -.vo-canvas:not([data-tool='select'])[data-tool] { - cursor: crosshair; -} -.vo-canvas:not([data-tool='select'])[data-tool] .uml-class { - cursor: crosshair; -} -.vo-canvas:not([data-tool='select'])[data-tool] .uml-class:hover, -.uml-class.source { - border-color: var(--accent); -} -.uml-class.source { - box-shadow: 0 0 0 4px var(--accent-soft); -} -/* Objektinspektor rechts im Diagramm */ -.vo-stage { - position: relative; - display: flex; - flex: 1; - min-height: 0; -} -.vo-stage .vo-canvas { - flex: 1; -} -.vo-hsplit { - flex: 0 0 20px; - margin: -10px 0; - position: relative; - z-index: 5; - cursor: row-resize; - touch-action: none; -} -.vo-hsplit::before { - content: ''; - position: absolute; - left: 0; - right: 0; - top: 9px; - height: 2px; - background: transparent; -} -/* Griff in der Mitte: zeigt auf dem Tablet, wo man den Objektinspektor nach oben oder unten wischt. */ -.vo-hsplit::after { - content: ''; - position: absolute; - left: 50%; - top: 7px; - width: 36px; - height: 6px; - margin-left: -18px; - border-radius: 3px; - background: var(--border); -} -.vo-hsplit:hover::before, -.vo-hsplit:focus-visible::before, -.vo-hsplit.dragging::before, -.vo-hsplit:hover::after, -.vo-hsplit:focus-visible::after, -.vo-hsplit.dragging::after { - background: var(--accent); -} -.vo-dock.collapsed + .vo-hsplit::after, -.vo-hsplit:has(+ .vo-dock.collapsed)::after { - display: none; -} -.vo-hsplit:focus-visible { - outline: none; -} -.vo-dock.collapsed + .vo-hsplit, -.vo-hsplit:has(+ .vo-dock.collapsed) { - cursor: default; - pointer-events: none; -} -.vo-dock { - border-top: 1px solid var(--border); - background: var(--panel); -} -.vo-dock-bar { - min-height: 30px; -} -.vo-dock-bar .vo-bar-title { - font-size: 13px; -} -.vo-dock.collapsed .vo-inspector { - display: none; -} -.vo-inspector { - display: grid; - align-content: start; - gap: 8px; - height: 32vh; - overflow: auto; - padding: 8px 12px 10px; - font: 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-inspector-row > label.check, -.vo-inspector label.check { - flex: 0 0 auto; - display: inline-flex; - align-items: center; - gap: 6px; - padding-bottom: 6px; - color: var(--text); - font-size: 13px; -} -.vo-inspector input[type='checkbox'] { - width: auto; - margin: 0; - accent-color: var(--accent); -} -.vo-inspector fieldset.vo-inspector-ifaces { - display: flex; - flex-wrap: wrap; - gap: 4px 14px; -} -.vo-inspector-empty { - color: var(--muted); -} -.vo-inspector-row { - display: flex; - flex-wrap: wrap; - align-items: end; - gap: 8px 12px; -} -.vo-inspector-row > label { - flex: 1 1 160px; - max-width: 260px; -} -.vo-inspector-ends { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); - gap: 8px; -} -.vo-inspector label { - display: grid; - gap: 3px; - color: var(--muted); - font-size: 12px; -} -.vo-inspector input, -.vo-inspector select { - box-sizing: border-box; - width: 100%; - min-width: 0; - padding: 4px 6px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-inspector input:focus, -.vo-inspector select:focus { - border-color: var(--accent); - outline: 2px solid var(--accent-soft); -} -.vo-inspector fieldset { - display: grid; - grid-template-columns: minmax(0, 1fr) 84px; - align-items: start; - gap: 6px; - min-width: 0; - margin: 0; - padding: 6px 8px 8px; - border: 1px solid var(--border); - border-radius: 8px; -} -.vo-inspector fieldset .vo-wide { - grid-column: 1 / -1; -} -.vo-reading { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 4px 8px; - color: var(--muted); -} -.vo-reading b { - color: var(--text); -} -.shell.visuobject .vo-reading button.text-button { - padding: 1px 8px; - font-size: 12px; -} -.vo-inspector legend { - padding: 0 4px; - color: var(--text); - font-weight: 600; -} -.vo-inspector small { - color: var(--muted); - font-size: 11px; -} -.vo-inspector p { - margin: 0; -} -.vo-inspector-kind { - font-weight: 600; - padding-top: 4px; -} -.shell.visuobject .vo-inspector button.vo-danger { - display: inline-flex; - align-items: center; - justify-self: start; - gap: 6px; - color: #b3261e; -} -.vo-inspector .vo-danger svg { - width: 15px; - height: 15px; -} -.vo-insp-head { - position: sticky; - top: -8px; - z-index: 1; - margin-top: -8px; - padding-top: 8px; - background: var(--panel); - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - gap: 6px 12px; - border-bottom: 1px solid var(--border); -} -.vo-insp-head .vo-inspector-kind { - padding: 0; -} -.vo-insp-tabs { - display: flex; - gap: 2px; -} -.shell.visuobject .vo-insp-tabs button { - margin-bottom: -1px; - padding: 5px 12px; - border: 1px solid transparent; - border-bottom: none; - border-radius: 6px 6px 0 0; - background: none; - color: var(--muted); - font: 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - cursor: pointer; -} -.shell.visuobject .vo-insp-tabs button[aria-selected='true'] { - border-color: var(--border); - background: var(--panel); - color: var(--text); - font-weight: 600; -} -.vo-members { - width: 100%; - border-collapse: collapse; -} -.vo-members th { - padding: 0 4px 3px; - color: var(--muted); - font-size: 12px; - font-weight: 500; - text-align: left; -} -.vo-members td { - padding: 2px 4px; - vertical-align: middle; -} -.vo-members td.c { - width: 52px; - text-align: center; -} -.vo-members td:first-child { - width: 112px; -} -.vo-members td:last-child { - width: 32px; -} -.vo-members .icon svg { - width: 15px; - height: 15px; -} -.vo-doc textarea { - box-sizing: border-box; - width: 100%; - max-width: 780px; - padding: 4px 6px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - resize: vertical; -} -.vo-insp-sep { - align-self: stretch; - width: 1px; - margin: 0 4px; - background: var(--border); -} -.vo-insp-label { - align-self: center; - color: var(--muted); - font-size: 12px; -} -.uml-class.selected { - border-color: var(--accent); - box-shadow: 0 0 0 3px var(--accent-soft); - z-index: 2; -} -.uml-head { - display: grid; - justify-items: center; - padding: 4px 10px; - font-weight: 700; -} -.uml-head small { - font-weight: 400; - font-size: 11px; - color: var(--muted); -} -.uml-class.abstract .uml-name, -.uml-member.abstract { - font-style: italic; -} -.uml-member.static { - text-decoration: underline; -} -.uml-class ul { - list-style: none; - margin: 0; - padding: 3px 0; - border-top: 1.5px solid var(--text); - min-height: 12px; -} -.uml-class.selected ul { - border-top-color: var(--accent); -} -.uml-class li { - padding: 0 10px; - white-space: nowrap; -} -.uml-member.selected { - background: var(--accent-soft); -} -.uml-add { - color: var(--accent); - font-style: italic; - cursor: pointer; - font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - font-size: 12px; -} -.uml-add:hover { - text-decoration: underline; -} -.uml-input { - font: inherit; - padding: 0 4px; - margin: 0 -5px; - border: 1px solid var(--accent); - border-radius: 4px; - outline: none; - max-width: 340px; -} -.uml-tools { - display: flex; - flex-wrap: wrap; - gap: 6px 10px; - align-items: center; - padding: 6px 10px; - border-top: 1px dashed var(--border); - font: 12px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - cursor: default; -} -.uml-tools label { - display: inline-flex; - gap: 4px; - align-items: center; -} -.uml-tools button.icon svg { - width: 16px; - height: 16px; -} -/* Package Explorer: Projekt › src › Packages › Dateien › Klassen › Attribute/Methoden */ -.vo-ws { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - padding: 8px 10px 4px; - font-size: 13px; -} -.vo-ws[hidden] { - display: none; -} -.vo-ws.ask { - margin: 8px 10px 4px; - padding: 10px; - border: 1px solid var(--accent); - border-radius: 8px; - background: var(--bg, #fff); -} -.vo-ws.ask p { - flex-basis: 100%; - margin: 0; -} -.vo-ws.ask .hint { - color: var(--muted); - font-size: 12px; - margin-bottom: 4px; -} -.vo-ws-name { - display: inline-flex; - align-items: center; - gap: 6px; - margin-right: auto; - font-weight: 600; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.shell.visuobject .vo-ws button.text-button { - padding: 2px 8px; - border-radius: 6px; -} -.shell.visuobject .vo-ws button.text-button.primary { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.vo-lines .vo-end-hit { - fill: transparent; - pointer-events: all; - cursor: context-menu; -} -.vo-lines .vo-label-svg.editable { - pointer-events: all; - cursor: pointer; -} -.vo-lines .vo-label-svg.editable:hover { - fill: var(--accent); - text-decoration: underline; -} -.vo-lines .vo-label-svg.placeholder { - fill: var(--muted); - opacity: 0.6; - font-style: italic; -} -.vo-endpop { - position: absolute; - z-index: 12; - display: grid; - gap: 8px; - width: min(330px, calc(100% - 16px)); - box-sizing: border-box; - padding: 10px 12px; - background: var(--panel); - border: 1px solid var(--accent); - border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.16); - font: 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-endpop p { - margin: 0; -} -.vo-endpop label { - display: grid; - gap: 3px; - color: var(--muted); - font-size: 12px; -} -.vo-endpop input { - box-sizing: border-box; - min-width: 0; - padding: 4px 6px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-endpop-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 4px; -} -.vo-endpop-row > span { - flex-basis: 100%; - color: var(--muted); - font-size: 12px; -} -.vo-endpop-row button { - padding: 2px 8px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: 12px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - cursor: pointer; -} -.vo-endpop-row button[aria-pressed='true'] { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -.vo-endpop-row input { - width: 64px; -} -.vo-new { - position: relative; -} -.vo-new-menu { - position: absolute; - top: calc(100% + 4px); - left: 0; - z-index: 30; - display: grid; - min-width: 190px; - padding: 4px; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 8px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.14); -} -.vo-new-menu[hidden] { - display: none; -} -.vo-new-menu button { - display: flex; - align-items: center; - gap: 8px; - padding: 6px 8px; - border: 0; - border-radius: 6px; - background: none; - color: var(--text); - font: 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - text-align: left; - cursor: pointer; -} -.vo-new-menu button:hover, -.vo-new-menu button:focus-visible { - background: var(--accent-soft); - outline: none; -} -.vo-badge.folder.source { - box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.35), inset -5px -4px 0 #8a6d2b; -} -.confirm-card.vo-wizard { - display: grid; - gap: 10px; - width: min(520px, 100%); - max-width: none; - max-height: calc(100vh - 32px); - overflow: auto; - box-sizing: border-box; - font: 14px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-wizard h2 { - margin: 0; - font-size: 17px; -} -.vo-wizard .hint { - margin: 0; - color: var(--muted); - font-size: 13px; -} -.vo-wizard > label { - display: grid; - grid-template-columns: 130px minmax(0, 1fr); - align-items: center; - gap: 8px; - color: var(--muted); - font-size: 13px; -} -.vo-wizard input[type='text'], -.vo-wizard select { - box-sizing: border-box; - width: 100%; - min-width: 0; - padding: 5px 7px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: inherit; -} -.vo-wizard fieldset { - margin: 0; - padding: 6px 10px 8px; - border: 1px solid var(--border); - border-radius: 8px; -} -.vo-wizard legend { - padding: 0 4px; - color: var(--muted); - font-size: 13px; -} -.vo-wizard-row { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px 16px; -} -.vo-wizard-row select, -.vo-wizard-row input[type='text'] { - width: auto; - flex: 1 1 160px; -} -.vo-wizard-list { - display: grid; - gap: 4px; -} -.vo-wizard .check { - display: inline-flex; - align-items: center; - gap: 6px; -} -.vo-wizard code { - font-size: 12px; -} -.vo-wizard-error { - min-height: 1em; - margin: 0; - color: #b3261e; - font-size: 13px; -} -.vo-wizard .group button.primary { - background: var(--accent); - border-color: var(--accent); - color: #fff; -} -@media (max-width: 560px) { - .vo-wizard > label { - grid-template-columns: 1fr; - } -} -.vo-dirty { - color: var(--accent); -} -.vo-tree-tools { - display: flex; - flex-wrap: wrap; - gap: 6px; - padding: 8px 10px 4px; -} -.shell.visuobject .vo-tree-tools button.text-button { - padding: 2px 8px; - border-radius: 6px; -} -.vo-tree { - flex: 1; - min-height: 0; - overflow: auto; - padding: 6px; - font: 13px/1.3 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.vo-tree ul { - list-style: none; - margin: 0; - padding: 0; -} -.vo-row { - display: flex; - align-items: center; - gap: 6px; - min-height: 28px; - padding: 0 6px 0 calc(4px + var(--depth, 0) * 14px); - border-radius: 7px; - cursor: pointer; - user-select: none; - white-space: nowrap; - outline: none; -} -.vo-row:hover { - background: var(--accent-faint); -} -.vo-row:focus-visible { - box-shadow: inset 0 0 0 2px var(--accent-soft); -} -.vo-row.active .vo-label { - font-weight: 600; -} -.vo-row.selected { - background: var(--accent-soft); - color: var(--text); -} -.vo-twisty, -.vo-twisty-space { - flex: none; - width: 18px; - height: 18px; -} -.vo-twisty { - display: grid; - place-items: center; - padding: 0; - border: 0; - border-radius: 4px; - background: none; - color: var(--muted); - cursor: pointer; -} -.vo-twisty svg { - transition: transform 0.15s ease; -} -[aria-expanded='true'] > .vo-row .vo-twisty svg { - transform: rotate(90deg); -} -.vo-twisty:hover { - color: var(--accent); -} -.vo-label { - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; -} -.vo-type { - color: var(--muted); - font-size: 12px; - overflow: hidden; - text-overflow: ellipsis; -} -.vo-row .uml-input { - margin: 0; - font: inherit; -} -.vo-row-delete { - margin-left: auto; - visibility: hidden; -} -.vo-tree button.vo-row-delete { - width: 24px; - height: 24px; -} -.vo-tree button.vo-row-delete svg { - width: 14px; - height: 14px; -} -.vo-row:hover .vo-row-delete, -.vo-row.selected .vo-row-delete { - visibility: visible; -} -.vo-tree-empty { - padding: 6px 10px 6px 52px; - color: var(--muted); - font-size: 12px; - white-space: normal; -} -/* Symbole im Baum: modern statt Eclipse-Pixelgrafik */ -.vo-badge { - flex: none; - display: inline-grid; - place-items: center; - width: 16px; - height: 16px; - border-radius: 4px; - font: 700 10px/1 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - color: #fff; -} -.vo-badge.project { - background: var(--accent); - border-radius: 5px; - box-shadow: inset 0 -5px 0 rgba(0, 0, 0, 0.18); -} -.vo-badge.project.closed { - opacity: 0.55; -} -.vo-badge.folder { - width: 17px; - height: 13px; - border-radius: 2px 4px 3px 3px; - background: #e0a63b; - box-shadow: inset 0 3px 0 rgba(255, 255, 255, 0.35); -} -.vo-badge.package { - width: 14px; - height: 14px; - border-radius: 3px; - background: #b07a2a; - transform: rotate(45deg) scale(0.82); -} -.vo-badge.package.empty { - background: none; - border: 1.5px solid #b07a2a; -} -.vo-badge.file { - background: #e66a2c; - border-radius: 3px 6px 3px 3px; -} -.vo-badge.type.class { - background: #2f7d32; - border-radius: 50%; -} -.vo-badge.type.class.abstract { - background: #6a9c3a; - font-style: italic; -} -.vo-badge.type.interface { - background: #7b4fb0; - border-radius: 50%; -} -.vo-badge.field, -.vo-badge.method { - width: 9px; - height: 9px; - margin: 0 3px; -} -.vo-badge.field { - border-radius: 2px; -} -.vo-badge.method { - border-radius: 50%; -} -.vo-badge.public { - background: #2f9e44; -} -.vo-badge.private { - background: #d6453d; -} -.vo-badge.protected { - background: #e0a63b; -} -.vo-badge.package.field, -.vo-badge.package.method { - background: #3b82c4; - transform: none; - border: 0; -} -.vo-badge.static { - box-shadow: 0 0 0 2px var(--panel), 0 0 0 3px currentColor; - color: var(--muted); -} -@media (max-width: 760px) { - /* Am Handy untereinander; eingeklappt bleibt ein waagerechter Streifen */ - .vo-columns { - grid-template-columns: 1fr; - grid-auto-rows: auto; - overflow: auto; - } - .vo-splitter { - display: none; - } - .vo-explorer:not(.collapsed) { - height: 260px; - } - .vo-code:not(.collapsed) { - height: 320px; - } - .vo-diagram:not(.collapsed) { - min-height: 420px; - } - .vo-pane { - border-bottom: 1px solid var(--border); - } - .vo-strip { - writing-mode: horizontal-tb; - padding: 10px; - } -} - - -/* VisuObject: Start/Stop und Konsole unter dem Code (echtes Java über CheerpJ). */ -.shell.visuobject .vo-run-group button.vo-run { - color: var(--accent); - font-weight: 600; -} -.vo-console { - display: flex; - flex-direction: column; - height: 32vh; - min-height: 120px; - border-top: 1px solid var(--border); -} -.vo-console[hidden], -.vo-swing[hidden] { - display: none; -} -.vo-console .vo-dock-bar { - gap: 10px; -} -.vo-console-status { - flex: 1; - color: var(--muted); - font-size: 12px; -} -.vo-console-status.error { - color: #b3261e; -} -.vo-swing { - display: flex; - height: 60%; - padding: 6px; -} -.vo-console-lines { - flex: 1; - min-height: 0; - overflow: auto; - margin: 0; - padding: 6px 12px; - font-size: 13px; - white-space: pre-wrap; -} - -/* UML-Notiz: Kasten mit Eselsohr oben rechts, gestrichelt an Klassen geheftet. */ -.uml-note { - position: absolute; - min-width: 120px; - max-width: 260px; - padding: 8px 22px 8px 10px; - background: #fffbe6; - border: 1.2px solid var(--text); - clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%); - font: 13px/1.45 system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - color: #17292e; - cursor: grab; - user-select: none; - touch-action: none; - z-index: 1; -} -.uml-note::after { - content: ''; - position: absolute; - top: 0; - right: 0; - width: 14px; - height: 14px; - background: linear-gradient(to bottom left, transparent 50%, #e9dfae 50%); - border-left: 1.2px solid var(--text); - border-bottom: 1.2px solid var(--text); -} -.uml-note.selected { - outline: 3px solid var(--accent-soft); - border-color: var(--accent); - clip-path: none; -} -.uml-note .uml-connect { - z-index: 2; -} -.uml-note-text { - white-space: pre-wrap; - overflow-wrap: anywhere; -} -.uml-note-text.placeholder { - color: #8a8a8a; - font-style: italic; -} -.uml-note-edit { - box-sizing: border-box; - width: 220px; - border: 1px solid var(--accent); - background: #fffef5; - font: inherit; - resize: both; -} -.vo-note-link { - stroke-dasharray: 5 4; -} - -/* VisuObject: Umschalter Klassendiagramm ⇄ Objektdiagramm und Objekte wie in Astah. */ -.vo-canvas[hidden], -.vo-palette[hidden] { - display: none; -} -.vo-diagram-switch { - display: flex; - gap: 2px; - padding: 2px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); -} -.shell.visuobject .vo-diagram-switch button { - padding: 3px 10px; - border: none; - border-radius: 6px; - background: none; - color: var(--muted); - font: 600 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; - cursor: pointer; -} -.shell.visuobject .vo-diagram-switch button[aria-selected='true'] { - background: var(--accent); - color: #fff; -} -.vo-obj-palette { - align-items: center; - gap: 8px; - padding: 6px 10px; -} -.vo-obj-new { - display: flex; - align-items: center; - gap: 6px; - font-size: 13px; -} -.vo-obj-new select { - padding: 3px 6px; - border: 1px solid var(--border); - border-radius: 6px; - background: var(--panel); - color: var(--text); - font: 13px system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif; -} -.uml-object .uml-objname { - text-decoration: underline; - font-weight: 600; -} -.uml-object.unknown { - border-style: dashed; -} -.uml-object .uml-member.changed, -.vo-objlink.changed { - animation: vo-changed 1.2s ease-out; -} -.uml-object .uml-member.changed { - background: #fff3b0; -} -@keyframes vo-changed { - from { - background: #ffe066; - } -} -.vo-obj-values td:first-child { - width: auto; - white-space: nowrap; -} -.vo-obj-values td:last-child { - width: 50%; -} - -/* VisuObject-Debugger: Knöpfe, Eingabe in der Konsole, Aufrufstapel und Variablen im Inspektor. */ -.vo-dbg-controls { - display: inline-flex; - gap: 2px; -} -.vo-dbg-controls[hidden] { - display: none; -} -.vo-console-lines .vo-out-err { - color: #b3261e; -} -.vo-console-lines .vo-out-in { - color: var(--accent); - font-weight: 600; -} -.vo-console-input { - display: flex; - align-items: center; - gap: 6px; - padding: 4px 12px 8px; - color: var(--accent); -} -.vo-console-input[hidden] { - display: none; -} -.vo-console-input input { - flex: 1; - font-size: 13px; - padding: 4px 6px; - border: 1px solid var(--accent); - border-radius: 4px; -} -.vo-dbg { - display: grid; - grid-template-columns: minmax(160px, 1fr) minmax(220px, 2fr); - gap: 12px; - padding: 8px 12px; -} -.vo-dbg h3 { - margin: 0 0 6px; - font-size: 12px; - font-weight: 600; - color: var(--muted); -} -.vo-dbg ol { - list-style: none; - margin: 0; - padding: 0; -} -.vo-dbg-frame { - display: block; - width: 100%; - text-align: start; - padding: 3px 6px; - border: 0; - border-radius: 4px; - background: none; - color: inherit; - font: inherit; - cursor: pointer; -} -.vo-dbg-frame.on { - background: var(--accent-soft); -} -.vo-dbg-frame small { - color: var(--muted); -} -.vo-dbg-vars tr.changed td { - background: rgba(255, 214, 10, 0.35); -} -@media (max-width: 760px) { - .vo-dbg { - grid-template-columns: 1fr; - } -} - -/* VisuObject: Sequenzdiagramm aus dem Debugger. */ -.vo-seqcanvas { - overflow: auto; -} -.vo-seqcanvas .vo-empty[hidden] { - display: none; -} -.vo-seq { - display: block; - font: 12px ui-monospace, SFMono-Regular, Menlo, monospace; -} -.vo-seq-head rect { - fill: var(--panel); - stroke: var(--text); - stroke-width: 1.2; -} -.vo-seq-head text { - fill: var(--text); - text-decoration: underline; -} -.vo-seq-head.static text { - text-decoration: none; - font-weight: 600; -} -.vo-seq-life { - stroke: var(--muted); - stroke-dasharray: 5 4; -} -.vo-seq-bar { - fill: var(--panel); - stroke: var(--text); - stroke-width: 1; -} -.vo-seq-bar.running { - fill: var(--accent-soft); -} -.vo-seq-msg { - cursor: pointer; -} -.vo-seq-msg line, -.vo-seq-msg path { - stroke: var(--text); - stroke-width: 1.2; -} -.vo-seq-msg .head.filled { - fill: var(--text); -} -.vo-seq-msg text { - fill: var(--text); -} -.vo-seq-msg.return line, -.vo-seq-msg.create line { - stroke-dasharray: 6 4; -} -.vo-seq-msg.exception line, -.vo-seq-msg.exception text { - stroke: #b3261e; - fill: #b3261e; -} -.vo-seq-msg.current line, -.vo-seq-msg.current path { - stroke: #e0a800; - stroke-width: 2.4; -} -.vo-seq-msg.current .head.filled { - fill: #e0a800; -} -.vo-seq-msg.current text { - font-weight: 700; -} -.vo-seq-msg:hover text { - fill: var(--accent); -} -.vo-seq-more { - fill: var(--muted); -} - -/* Elemente einklappen: Knopf neben der Überschrift, eingeklappt bleibt eine schmale Leiste. */ -.palette-head { - display: flex; - align-items: center; - gap: 4px; - margin: 0 0 4px; -} -.palette-head h2 { - flex: 1; - min-width: 0; - margin: 0; -} -.palette-hide { - min-height: 26px; - padding: 2px 5px; -} -.app.compact-palette .palette-head { - flex-direction: column; - gap: 2px; -} -.palette-rail { - display: none; - grid-area: pal; - width: auto; -} -.app.palette-hidden .palette, -.app.palette-hidden .palette-resizer { - display: none; -} -.app.palette-hidden:not(.simulating) .palette-rail { - display: flex; -} -/* Schmale Fenster: Elemente stehen als Zeile oben, dort gibt es kein Einklappen. */ -@media (max-width: 820px) { - .palette-head { - display: none; - } - .app.palette-hidden .palette { - display: block; - } - .app.palette-hidden .palette-rail { - display: none !important; - } -} -/* Haken „Pseudocode“ links vom ⇄ in der Werkzeugzeile. */ -.diagram-tools .pseudocode-toggle { - gap: 4px; - margin: 0 4px; - white-space: nowrap; - cursor: pointer; -} - -/* Welt VisuFlow (flowApp.ts): PAP zeichnen, Farbe Violett */ -:root.visuflow-theme { - --bg: #f3f0fb; - --text: #221a35; - --muted: #625a75; - --border: #ddd6ee; - --accent: #6f42c1; - --accent-soft: rgba(111, 66, 193, 0.2); - --accent-faint: rgba(111, 66, 193, 0.08); - --accent-glow: rgba(111, 66, 193, 0.35); -} -.visuflow-flow { - color: var(--accent); -} -.vf-logo { - width: 22px; - height: 22px; - background: no-repeat center / contain - url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236f42c1' stroke-width='2'%3E%3Crect x='6' y='1.5' width='12' height='5' rx='2.5'/%3E%3Cpath d='M12 6.5v3M12 9.5l5 4-5 4-5-4z M12 17.5v2'/%3E%3Crect x='7' y='19.5' width='10' height='3' rx='0.5'/%3E%3C/svg%3E"); -} -.shell.visuflow button.text-button { - padding: 5px 10px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); - color: var(--text); - font: inherit; - cursor: pointer; -} -.shell.visuflow button.text-button:hover:not(:disabled) { - border-color: var(--accent); - color: var(--accent); -} -.shell.visuflow button:disabled { - opacity: 0.45; - cursor: default; -} -.vf-export { - position: relative; -} -.vf-menu { - position: absolute; - top: calc(100% + 6px); - inset-inline-start: 0; - z-index: 30; - display: grid; - min-width: 190px; - padding: 4px; - background: var(--panel); - border: 1px solid var(--border); - border-radius: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); -} -.vf-menu[hidden] { - display: none; -} -.vf-menu button { - padding: 8px 10px; - border: 0; - border-radius: 6px; - background: none; - color: var(--text); - font: inherit; - text-align: start; - cursor: pointer; -} -.vf-menu button:hover, -.vf-menu button:focus-visible { - background: var(--accent-faint); - color: var(--accent); -} -.vf-main { - display: flex; - flex: 1; - min-height: 0; - border-top: 1px solid var(--border); -} -.vf-palette { - display: flex; - flex-direction: column; - gap: 8px; - width: 190px; - flex-shrink: 0; - padding: 10px; - overflow-x: hidden; - overflow-y: auto; - background: var(--panel); - border-inline-end: 1px solid var(--border); -} -.vf-palette h2 { - margin: 0; - font-size: 11px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--muted); -} -.vf-shapes { - display: grid; - gap: 6px; -} -.vf-shape { - display: flex; - align-items: center; - gap: 8px; - padding: 4px 6px; - border: 1px solid var(--border); - border-radius: 8px; - background: var(--panel); - color: var(--text); - font: inherit; - font-size: 13px; - text-align: start; - cursor: grab; - touch-action: none; - user-select: none; -} -.vf-shape:hover, -.vf-shape:focus-visible { - border-color: var(--accent); - background: var(--accent-faint); -} -.vf-shape svg { - flex-shrink: 0; - width: 48px; - height: 27px; -} -.vf-hint { - margin: 4px 0 0; - font-size: 12px; - line-height: 1.4; - color: var(--muted); -} -.vf-stage { - display: flex; - flex-direction: column; - flex: 1; - min-width: 0; -} -.vf-titlebar { - padding: 8px 12px; - background: var(--panel); - border-bottom: 1px solid var(--border); -} -.vf-title { - width: min(420px, 100%); - padding: 6px 8px; - border: 1px solid var(--border); - border-radius: 6px; - font: inherit; - font-weight: 600; -} -.vf-scroll { - position: relative; - flex: 1; - overflow: auto; - background-color: #ffffff; - background-image: radial-gradient(circle, #d9d4e6 1px, transparent 1.2px); - background-size: 20px 20px; - background-attachment: local; -} -.vf-canvas { - display: block; - touch-action: none; - user-select: none; - -webkit-user-select: none; -} -.vf-node { - cursor: move; -} -.vf-node.selected > :first-child { - stroke: var(--accent); - stroke-width: 3; - filter: drop-shadow(0 0 4px var(--accent-glow)); -} -.vf-node.drop-target > :first-child { - stroke: var(--accent); - stroke-width: 3; - stroke-dasharray: 6 3; -} -.vf-edge { - cursor: pointer; -} -.vf-edge.selected .vf-edge-line { - stroke: var(--accent); - stroke-width: 3; -} -.vf-port { - cursor: crosshair; -} -.vf-port-dot { - fill: #ffffff; - stroke: var(--accent); - stroke-width: 2; -} -.vf-port.used .vf-port-dot { - fill: var(--accent); - opacity: 0.55; -} -.vf-port:hover .vf-port-dot { - fill: var(--accent); - opacity: 1; -} -.vf-drag-line { - stroke: var(--accent); - stroke-width: 2; - stroke-dasharray: 6 4; - pointer-events: none; -} -.vf-editor { - position: absolute; - z-index: 5; - box-sizing: border-box; - padding: 4px 6px; - border: 2px solid var(--accent); - border-radius: 6px; - background: #ffffff; - color: var(--text); - font: 14px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; - line-height: 16px; - text-align: center; - resize: none; - overflow: hidden; -} -.vf-ghost { - position: fixed; - z-index: 100; - pointer-events: none; - transform: translate(-50%, -50%) scale(1.6); - opacity: 0.85; -} -.vf-status { - margin: 0; - padding: 6px 12px; - font-size: 12px; - color: var(--muted); - background: var(--panel); - border-top: 1px solid var(--border); -} -@media (max-width: 700px) { - .vf-main { - flex-direction: column; - } - .vf-palette { - flex-direction: row; - width: auto; - overflow-x: auto; - border-inline-end: 0; - border-bottom: 1px solid var(--border); - } - .vf-palette h2, - .vf-hint { - display: none; - } - .vf-shapes { - display: flex; - } - .vf-shape { - flex-direction: column; - gap: 2px; - font-size: 11px; - } -} - -/* VisuBot-Befehle nach Gruppen: Streifen links in der Palette, Punkt an der Gruppe und im Nachschlagen. - Die Kästen bleiben gelb (Anweisung); mit „Blöcke farbig“ aus gehen auch die Streifen aus. */ -[data-bot-color='fahren'], .bot-fahren { --bot: #c2410c; } -[data-bot-color='licht'], .bot-licht { --bot: #84cc16; } -[data-bot-color='ton'], .bot-ton { --bot: #c026d3; } -[data-bot-color='anzeige'], .bot-anzeige { --bot: #4f46e5; } -[data-bot-color='warten'], .bot-warten { --bot: #6b7280; } -[data-bot-color='sensor'], .bot-sensor { --bot: #0891b2; } -[data-bot-color='operator'], .bot-operator { --bot: #16a34a; } -[data-bot-color='variable'], .bot-variable { --bot: #ea580c; } -:root:not(.no-code-scopes) .palette-item[data-bot-color] { - border-left: 4px solid var(--bot); -} -:root:not(.no-code-scopes) .palette-group[data-bot-color] button::after, -:root:not(.no-code-scopes) .learn-text h3[class^='bot-']::before { - content: ''; - display: inline-block; - width: 9px; - height: 9px; - flex-shrink: 0; - border-radius: 50%; - background: var(--bot); -} -:root:not(.no-code-scopes) .palette-group[data-bot-color] button::before { - order: -2; -} -:root:not(.no-code-scopes) .palette-group[data-bot-color] button::after { - order: -1; -} -:root:not(.no-code-scopes) .learn-text h3[class^='bot-']::before { - margin-inline-end: 7px; - vertical-align: 1px; -} - -/* Spiel „Wo landet der mBot2?“ */ -.learn-drive-map { - display: block; - width: 100%; - max-width: 320px; - margin: 8px auto; - touch-action: manipulation; -} -.learn-drive-map .cell { - stroke: #cbd5e1; - stroke-width: 0.6; - cursor: pointer; -} -.learn-drive-map:not(.done) .cell:hover, -.learn-drive-map .cell:focus-visible { - stroke: var(--accent); - stroke-width: 2; - outline: none; -} -.learn-drive-map.done .cell { - cursor: default; -} -.learn-drive-map .cell.right { - stroke: #16a34a; - stroke-width: 3; -} -.learn-drive-map .cell.wrong { - stroke: #dc2626; - stroke-width: 3; -} -.learn-drive-map .trail { - stroke: var(--accent); - stroke-width: 2; - stroke-dasharray: 3 2; - pointer-events: none; -} -.learn-drive-map .robot { - transition: transform 0.4s ease-in-out; - pointer-events: none; -} -.learn-drive-map .robot .body { - fill: #0ea5b7; - stroke: #0f172a; - stroke-width: 1; -} -.learn-drive-map .robot .nose { - fill: #ffffff; -} -@media (prefers-reduced-motion: reduce) { - .learn-drive-map .robot { - transition: none; - } -} - -/* VisuBot: Struktogramm als Blöcke (blockView.ts), Aussehen wie der Block-Modus von VisuMicro. - Farben des Struktogramms: Anweisung gelb, Schleife rosa, Verzweigung/Fallauswahl blau; Befehle des mBot2 - zusätzlich mit dem Streifen ihrer Gruppe. Schwarze Schrift, weiße Felder für Werte. */ -.diagram-tools .blocks-toggle { - display: inline-flex; - align-items: center; - gap: 5px; - height: 30px; - margin-inline-start: 6px; - padding: 0 10px; - white-space: nowrap; -} -.diagram-tools .blocks-toggle[hidden] { - display: none; -} -@container (max-width: 520px) { - .diagram-tools .blocks-toggle span + span { - display: none; - } -} -.blocks-view { - --bk-stmt: #f2b100; - --bk-loop: #ec5fa6; - --bk-if: #3b9cf0; - --bk-plain: #9aa3ae; - --bk-bump: 6px; - --bk-workspace: #fafafa; - flex: 1; - min-height: 0; - overflow: auto; - scrollbar-gutter: stable; - background: var(--bk-workspace); - padding: 10px 16px 32px; - font: 500 calc(15px * var(--bk-zoom, 1)) / 1.5 system-ui, -apple-system, sans-serif; - user-select: none; - -webkit-user-select: none; -} -.blocks-view[hidden] { - display: none; -} -.bk-hint { - margin: 0 0 6px; - max-width: 44em; - color: var(--muted); - font-size: 13px; - line-height: 1.4; -} -.bk-stack { - padding-top: 14px; -} -.bk { - --bk-color: var(--bk-stmt); - --bk-c: color-mix(in srgb, var(--bk-color) 42%, #fff); - --bk-e: color-mix(in srgb, var(--bk-color) 80%, #000); - position: relative; - width: fit-content; - min-width: 12em; - max-width: 100%; - border: 1px solid var(--bk-e); - border-radius: 5px; - background: var(--bk-c); - color: #000; - cursor: pointer; -} -.bk-loop { --bk-color: var(--bk-loop); } -.bk-if { --bk-color: var(--bk-if); } -.bk-hat { --bk-color: var(--bk-stmt); cursor: default; } -/* Einstellung „Blöcke farbig“ aus: alle Blöcke grau */ -:root.no-code-scopes .bk { --bk-color: var(--bk-plain); } -/* mBot2-Befehle: Streifen links in der Farbe der Gruppe, der Block selbst bleibt gelb */ -:root:not(.no-code-scopes) .bk[data-bot-color] { - border-left: 6px solid var(--bot); -} -/* gestapelte Blöcke liegen aneinander; die Nase des oberen greift in den unteren */ -.bk + .bk, -.bk + .bk-empty, -.bk-empty + .bk { - margin-top: -1px; -} -.bk-simple::after, -.bk-c::after, -.bk-hat::after, -.bk-c > .bk-head:first-child::after, -.bk-clause::after { - content: ''; - position: absolute; - z-index: 1; - left: 12px; - bottom: calc(-1 * var(--bk-bump) - 1px); - width: 1.6em; - height: var(--bk-bump); - background: var(--bk-c); - border: 1px solid var(--bk-e); - border-top: none; - border-radius: 0 0 6px 6px; -} -.bk-c > .bk-head:first-child::after, -.bk-clause::after { - left: calc(16px + 12px); -} -.bk-head, -.bk-foot { - position: relative; - display: flex; - flex-direction: column; - padding: calc(var(--bk-bump) - 1px) 10px 4px; -} -.bk-foot { - min-height: calc(var(--bk-bump) + 10px); - padding-top: var(--bk-bump); -} -.bk-line { - white-space: pre-wrap; -} -/* Hut-Block: runder Kopf wie „Wenn die grüne Flagge angeklickt“ */ -.bk-hat { - border-top-left-radius: 22px; - border-top-right-radius: 10px; -} -.bk-hat > .bk-head { - flex-direction: row; - flex-wrap: wrap; - column-gap: 1em; - padding-top: 8px; - padding-left: 14px; -} -.bk-hat::before { - content: ''; - position: absolute; - left: -1px; - top: -9px; - width: 7em; - height: 18px; - border: 1px solid var(--bk-e); - border-bottom: none; - border-radius: 50% 50% 0 0 / 100% 100% 0 0; - background: var(--bk-c); - clip-path: inset(0 0 8px 0); -} -/* Maul eines C-Blocks: links der Arm in Blockfarbe, innen die Arbeitsfläche */ -.bk-body { - margin-left: 16px; - margin-right: -1px; - padding-right: 2em; - min-height: 1em; - background: var(--bk-workspace); - border: 1px solid var(--bk-e); - border-right: none; - border-radius: 4px 0 0 4px; - color: var(--text); -} -.bk-body > :first-child { - margin-top: -1px; -} -/* break: Abschluss ohne Nase unten */ -.bk-cap { - border-bottom-left-radius: 2px; - border-bottom-right-radius: 10px; -} -/* Aufruf: senkrechte Linien wie der Kasten im Struktogramm */ -.bk-call > .bk-head { - margin: 0 6px; - border-inline: 2px solid var(--bk-e); -} -/* leerer Platz (Platzhalter): gestrichelt */ -.bk-empty { - width: fit-content; - min-width: 12em; - margin: 6px 8px; - padding: 2px 10px; - border: 1.5px dashed var(--border); - border-radius: 6px; - color: var(--muted); - font-style: italic; - font-size: 0.9em; - cursor: pointer; -} -.bk .tk-kw { - font-weight: 700; -} -/* Werte: weiße, runde Felder – hier wird man sie später direkt ändern */ -.bk .tk-lit, -.bk .tk-str { - padding: 0 6px; - border-radius: 999px; - background: #fff; - box-decoration-break: clone; - -webkit-box-decoration-break: clone; -} -/* leere Klammer (`mbot.forward()`): leeres weißes Feld zum Eintippen */ -.bk .tk-lit:empty { - display: inline-block; - min-width: 2.5ch; - min-height: 1.2em; - vertical-align: middle; -} -/* Variablen (zahl, i …): orange Bausteine */ -.bk .tk-var { - padding: 0 6px; - border-radius: 999px; - background: #fdba74; - color: #431407; - font-weight: 600; -} -/* Operatoren (Zufallszahl): grüner runder Baustein mit eigenen Feldern */ -.bk .tk-op { - padding: 1px 6px; - border: 1px solid #15803d; - border-radius: 999px; - background: #86efac; - color: #052e16; -} -/* Sensoren: türkiser Baustein wie in der Palette */ -.bk .tk-sensor { - padding: 0 6px; - border: 1px solid color-mix(in srgb, #0891b2 80%, #000); - border-radius: 999px; - background: color-mix(in srgb, #0891b2 35%, #fff); -} -:root.no-code-scopes .bk .tk-sensor { - border-color: #6b7280; - background: #e5e7eb; -} -/* Auswahl und laufender Schritt der Simulation */ -.bk-sel { - outline: 3px solid var(--accent); - outline-offset: 1px; - z-index: 2; -} -.bk-exec { - outline: 3px solid var(--text); - outline-offset: 1px; - box-shadow: 0 0 12px 4px var(--accent-glow); - z-index: 2; -} -.blocks-view.bk-simulating .bk, -.blocks-view.bk-simulating .bk-empty { - cursor: default; -} -/* Ziehen in den Blöcken: Linie an der Ablegestelle, Rahmen um einen leeren Platz bzw. die Bedingung */ -.bk-stack { - position: relative; -} -.bk-drop-line { - position: absolute; - z-index: 5; - height: 6px; - border-radius: 3px; - background: var(--accent); - box-shadow: 0 0 0 2px #fff; - pointer-events: none; -} -.bk-drop, -.bk-empty.bk-drop { - outline: 3px dashed var(--accent); - outline-offset: 1px; - z-index: 2; -} -/* Papierkorb: erscheint beim Verschieben eines Blocks unten rechts */ -.bk-trash { - display: none; - position: sticky; - bottom: 0; - margin: 16px 0 0 auto; - width: fit-content; - padding: 10px 16px; - border: 2px dashed var(--border); - border-radius: 12px; - background: var(--panel); - color: var(--muted); - font-size: 14px; -} -.blocks-view.bk-moving .bk-trash { - display: block; -} -.bk-trash.bk-trash-hot { - border-color: #dc2626; - background: #fee2e2; - color: #991b1b; -} -.blocks-view:not(.bk-simulating) .bk:not(.bk-hat) { - touch-action: pan-y; -} -/* Werte anklicken und direkt im Block ändern */ -.blocks-view:not(.bk-simulating) .bk [data-value] { - cursor: text; -} -.blocks-view:not(.bk-simulating) .bk [data-value]:hover { - box-shadow: 0 0 0 2px var(--accent); -} -.bk-input { - box-sizing: border-box; - min-width: 2ch; - padding: 0 6px; - border: 2px solid var(--accent); - border-radius: 999px; - background: #fff; - color: #000; - font: inherit; - text-align: center; - outline: none; -} -/* Stufe 4: Sensoren als Bausteine. Ja/Nein-Sensoren sechseckig wie Bedingungen in Scratch, Messwerte rund. */ -.bk .tk-sensor.tk-bool { - padding: 0 12px; - border: none; - border-radius: 0; - clip-path: polygon(8px 0, calc(100% - 8px) 0, 100% 50%, calc(100% - 8px) 100%, 8px 100%, 0 50%); -} -.bk .tk-sensor.bk-drop, -.bk [data-value].bk-drop { - outline: 3px dashed var(--accent); - outline-offset: 1px; - clip-path: none; -} -/* Elemente-Spalte in den Blöcken: Sensoren sehen aus wie die Bausteine, in die sie sich verwandeln */ -.app.block-mode .palette-sensor code { - display: inline-block; - margin-top: 2px; - padding: 0 8px; - border: 1px solid color-mix(in srgb, #0891b2 80%, #000); - border-radius: 999px; - background: color-mix(in srgb, #0891b2 35%, #fff); - color: #000; -} -.app.block-mode .palette-sensor code.sensor-bool, -.drag-ghost.drag-sensor.sensor-bool { - padding: 0 12px; - border: none; - border-radius: 0; - clip-path: polygon(8px 0, calc(100% - 8px) 0, 100% 50%, calc(100% - 8px) 100%, 8px 100%, 0 50%); -} -.drag-ghost.drag-sensor { - border: 1px solid color-mix(in srgb, #0891b2 80%, #000); - border-radius: 999px; - background: color-mix(in srgb, #0891b2 35%, #fff); - color: #000; - font-family: ui-monospace, monospace; -} -/* Blöcke an: auch die Elemente-Spalte zeigt Blöcke (Farbe, Nase, Klammerblöcke mit Maul) statt der Kästen-Symbole */ -.app.block-mode .palette-item:not(.palette-sensor), -.drag-ghost.drag-block { - --bk-color: #f2b100; - --bk-c: color-mix(in srgb, var(--bk-color) 42%, #fff); - --bk-e: color-mix(in srgb, var(--bk-color) 80%, #000); - border: 1px solid var(--bk-e); - border-radius: 5px; - background: var(--bk-c); - color: #000; - font-weight: 600; -} -.app.block-mode .palette-item[data-scope='loop'], -.drag-ghost.drag-block[data-scope='loop'] { - --bk-color: #ec5fa6; -} -.app.block-mode .palette-item[data-scope='if'], -.drag-ghost.drag-block[data-scope='if'] { - --bk-color: #3b9cf0; -} -:root.no-code-scopes .app.block-mode .palette-item:not(.palette-sensor), -:root.no-code-scopes .drag-ghost.drag-block { - --bk-color: #9aa3ae; -} -.app.block-mode .palette-item:not(.palette-sensor) .icon { - display: none; -} -.app.block-mode .palette-item:not(.palette-sensor):hover, -.app.block-mode .palette-item:not(.palette-sensor):focus-visible { - border-color: var(--bk-e); - box-shadow: 0 0 0 2px var(--accent); -} -.app.block-mode .palette-item:not(.palette-sensor) { - position: relative; - margin-bottom: 4px; -} -/* VisuBot-Befehle behalten ihren Gruppenstreifen */ -:root:not(.no-code-scopes) .app.block-mode .palette-item[data-bot-color] { - border-left: 6px solid var(--bot); -} -/* Nase unten wie bei den Blöcken */ -.app.block-mode .palette-item:not(.palette-sensor)::after, -.drag-ghost.drag-block::after { - content: ''; - position: absolute; - z-index: 1; - left: 12px; - bottom: -7px; - width: 1.6em; - height: 6px; - background: var(--bk-c); - border: 1px solid var(--bk-e); - border-top: none; - border-radius: 0 0 6px 6px; -} -/* Schleifen und Verzweigungen: Klammerblock mit leerem Maul */ -.app.block-mode .palette-item[data-scope='loop'], -.app.block-mode .palette-item[data-scope='if'], -.drag-ghost.drag-block[data-scope='loop'], -.drag-ghost.drag-block[data-scope='if'] { - padding-bottom: 26px; -} -.app.block-mode .palette-item[data-scope='loop']::before, -.app.block-mode .palette-item[data-scope='if']::before, -.drag-ghost.drag-block[data-scope='loop']::before, -.drag-ghost.drag-block[data-scope='if']::before { - content: ''; - position: absolute; - left: 14px; - right: -1px; - bottom: 8px; - height: 12px; - background: var(--panel); - border: 1px solid var(--bk-e); - border-right: none; - border-radius: 4px 0 0 4px; -} -.drag-ghost.drag-block { - padding: 6px 12px; - min-width: 9em; -} - -/* VisuBlocks: Operatoren grün, Variablen orange – in den Elementen (Blöcke an) und beim Ziehen */ -.app.block-mode .palette-op code, -.drag-ghost.drag-sensor.drag-op { - border-color: #15803d; - background: #86efac; -} -.app.block-mode .palette-var code, -.drag-ghost.drag-sensor.drag-var { - border-color: #c2410c; - background: #fdba74; -} - -/* Block-Lektionen für Jüngere: Programm einer Spielrunde als Blöcke, nur zum Ansehen */ -.blocks-view.bk-picture { - flex: none; - overflow: visible; - padding: 4px 8px 10px; - border-radius: 10px; - font-size: 14px; - margin-bottom: 8px; -} -.bk-picture .bk-hint, -.bk-picture .bk-trash { - display: none; -} -.bk-picture .bk-stack { - padding-top: 6px; -} diff --git a/web/src/ui/termTips.ts b/web/src/ui/termTips.ts deleted file mode 100644 index 088e6e1d..00000000 --- a/web/src/ui/termTips.ts +++ /dev/null @@ -1,311 +0,0 @@ -import { entryFor, termPattern, type GlossaryEntry } from '../learn/glossary' - -/** - * Fachbegriffe im Lernbereich: das erste Vorkommen je Schritt wird markiert und zeigt seine Erklärung - * beim Hovern (Maus), beim Antippen (Tablet) und beim Ansteuern mit der Tastatur. - */ - -/** Hier wird nichts markiert: Code, Links, Überschriften, Knöpfe und fett Gesetztes (dort wird der Begriff gerade erklärt). */ -const skip = 'code, pre, a, button, h1, h2, h3, h4, strong, .term, .ui-pointer' - -/** Markiert in den `.learn-text`-Blöcken unter `root` jeden Begriff höchstens einmal. */ -export function markTerms(root: HTMLElement, entries: GlossaryEntry[]) { - const texts = [...root.querySelectorAll('.learn-text')] - const introduced = new Set() - for (const strong of texts.flatMap((t) => [...t.querySelectorAll('strong')])) { - const pattern = termPattern(entries) - const found = pattern?.exec(strong.textContent ?? '') - const entry = found ? entryFor(entries, found[0]) : undefined - if (entry) introduced.add(entry) - } - let open = entries.filter((e) => !introduced.has(e)) - for (const block of texts) { - const walker = document.createTreeWalker(block, NodeFilter.SHOW_TEXT) - const nodes: Text[] = [] - while (walker.nextNode()) { - const node = walker.currentNode as Text - if (!node.parentElement?.closest(skip)) nodes.push(node) - } - for (let node of nodes) { - for (;;) { - const pattern = termPattern(open) - const found = pattern?.exec(node.data) - const entry = found ? entryFor(open, found[0]) : undefined - if (!found || !entry) break - const word = node.splitText(found.index) - node = word.splitText(found[0].length) - const mark = document.createElement('span') - mark.className = 'term' - mark.tabIndex = 0 - mark.setAttribute('role', 'button') - mark.setAttribute('aria-describedby', 'term-tip') - mark.dataset.term = entry.term - mark.dataset.text = entry.text - word.replaceWith(mark) - mark.append(word) - open = open.filter((e) => e !== entry) - } - } - } -} - -/** Ein gemeinsames Erklärungsfeld für alle markierten Begriffe in `container`. */ -export function attachTermTips(container: HTMLElement) { - const tip = document.createElement('div') - tip.id = 'term-tip' - tip.className = 'term-tip' - tip.setAttribute('role', 'tooltip') - tip.hidden = true - document.body.append(tip) - let current: HTMLElement | undefined - let pinned = false - - const show = (term: HTMLElement) => { - current = term - tip.replaceChildren() - const title = document.createElement('strong') - title.textContent = term.dataset.term ?? '' - tip.append(title, document.createTextNode(' ' + (term.dataset.text ?? ''))) - tip.hidden = false - const r = term.getBoundingClientRect() - const w = tip.offsetWidth - const ht = tip.offsetHeight - const left = Math.max(8, Math.min(r.left, window.innerWidth - w - 8)) - const below = r.bottom + 6 - tip.style.left = `${left}px` - tip.style.top = `${below + ht > window.innerHeight - 8 ? Math.max(8, r.top - ht - 6) : below}px` - term.setAttribute('aria-expanded', 'true') - } - const hide = () => { - current?.setAttribute('aria-expanded', 'false') - current = undefined - pinned = false - tip.hidden = true - } - const termOf = (e: Event) => (e.target as Element | null)?.closest?.('.term') ?? undefined - - container.addEventListener('pointerover', (e) => { - const term = termOf(e) - if (term && e.pointerType === 'mouse' && !pinned) show(term) - }) - container.addEventListener('pointerout', (e) => { - if (termOf(e) && e.pointerType === 'mouse' && !pinned) hide() - }) - container.addEventListener('click', (e) => { - const term = termOf(e) - if (!term) return - if (pinned && current === term) return hide() - show(term) - pinned = true - }) - container.addEventListener('focusin', (e) => { - const term = termOf(e) - if (term && !pinned) show(term) - }) - container.addEventListener('focusout', (e) => { - if (termOf(e) && !pinned) hide() - }) - container.addEventListener('keydown', (e) => { - const term = termOf(e) - if (e.key === 'Escape' && current) hide() - else if (term && (e.key === 'Enter' || e.key === ' ')) { - e.preventDefault() - term.click() - } - }) - container.addEventListener('scroll', hide, true) - document.addEventListener('pointerdown', (e) => { - if (current && !termOf(e) && !tip.contains(e.target as Node)) hide() - }) - /** Nach dem Neuzeichnen des Lernbereichs gehört das Feld zu keinem Begriff mehr. */ - return { hide } -} - -/** - * Zeiger auf den Editor: Ein Link `[Elemente](#zeige-elemente)` im Lektionstext hebt beim Hovern den echten Bereich - * im Editor neben dem Lernbereich hervor. Ein Klick (oder Antippen) öffnet außerdem die Spalte, in der der Bereich - * liegt, auch wenn sie eingeklappt ist. - */ -export const uiTargets: Record = { - leiste: '.topbar > .toolbar', - // Die ganze Spalte Struktogramm-Editor; eingeklappt leuchtet ihre schmale Leiste. - editor: '.editor-rail, .columns > .app', - werkzeuge: '.diagram-tools', - // Eingeklappt leuchtet die schmale Leiste, sonst die Spalte. - dateien: '.files-rail, .files', - codelive: '.live-rail, .livecode', - ausfuehren: '[data-run=play]', - konsole: '.livecode .console', - einzelschritt: '[data-run=step]', - elemente: '.palette', - struktogramm: '.canvas', - objektinspektor: '.inspector', - reiter: '.tabs', - titel: '.caption', - neu: '[data-action=new]', - oeffnen: '[data-action=open]', - speichern: '[data-action=save]', - exportieren: '[data-action=export]', - rueckgaengig: '[data-action=undo]', - loeschen: '[data-action=delete]', - // Textgröße und Sprache liegen im Menü hinter dem Zahnrad. - textgroesse: '[data-action=settings]', - einstellungen: '[data-action=settings]', - lernen: '.learn .learn-hide', - code: '[data-action=code]', - simulation: '[data-action=simulate]', - // Nur in VisuBot: Knopf „Blöcke“ in den Werkzeugen. - bloecke: '[data-action=blocks]', -} - -/** Zeiger auf diese Bereiche brauchen die Struktogramm-Spalte. */ -const editorTargets = new Set(['editor', 'werkzeuge', 'elemente', 'struktogramm', 'objektinspektor', 'reiter', 'titel', 'rueckgaengig', 'loeschen', 'exportieren', 'code', 'simulation', 'bloecke']) - -/** Zeiger auf diese Bereiche brauchen die offene Spalte „Code“ (die schmale Leiste allein reicht nicht). */ -const liveCodeTargets = new Set(['ausfuehren', 'konsole', 'einzelschritt']) - -function pointsInto(root: HTMLElement, targets: Set): boolean { - return [...root.querySelectorAll('.ui-pointer')].some((p) => targets.has(p.dataset.target ?? '')) -} - -/** Eingeklappte Bereiche, die ein Zeiger öffnen kann: Struktogramm-Editor, Elemente, „Code“, Dateien. */ -export type UiArea = 'editor' | 'palette' | 'livecode' | 'files' - -/** Was offen sein muss, damit man den Bereich des Zeigers sieht (die Elemente liegen im Struktogramm-Editor). */ -export function areasOf(key: string): UiArea[] { - if (key === 'elemente') return ['editor', 'palette'] - if (editorTargets.has(key)) return ['editor'] - if (key === 'codelive' || liveCodeTargets.has(key)) return ['livecode'] - if (key === 'dateien') return ['files'] - return [] -} - -/** Zeigt der Text auf die Elemente? */ -export function pointsIntoPalette(root: HTMLElement): boolean { - return pointsInto(root, new Set(['elemente'])) -} - -/** Zeigt der Text auf die Spalte Dateien? */ -export function pointsIntoFiles(root: HTMLElement): boolean { - return pointsInto(root, new Set(['dateien'])) -} - -/** Zeigt der Text auf einen Bereich der Struktogramm-Spalte? */ -export function pointsIntoEditor(root: HTMLElement): boolean { - return pointsInto(root, editorTargets) -} - -/** Zeigt der Text auf einen Bereich der Spalte „Code“? */ -export function pointsIntoLiveCode(root: HTMLElement): boolean { - return pointsInto(root, liveCodeTargets) -} - -export function markPointers(root: HTMLElement) { - for (const a of root.querySelectorAll('a[href^="#zeige-"]')) { - const key = a.getAttribute('href')!.slice('#zeige-'.length) - if (!(key in uiTargets)) continue - const span = document.createElement('span') - span.className = 'ui-pointer' - span.tabIndex = 0 - span.setAttribute('role', 'button') - span.dataset.target = key - span.append(...a.childNodes) - a.replaceWith(span) - } -} - -/** - * `reveal`: eingeklappte Bereiche öffnen; zurück kommt, wie man sie wieder einklappt. Hovern öffnet nur so lange, - * wie die Maus auf dem Wort bleibt; Klicken, Antippen oder Enter lässt den Bereich offen. - */ -export function attachPointers(container: HTMLElement, reveal: (areas: UiArea[]) => () => void = () => () => {}) { - let lit: Element | undefined - let timer: number | undefined - /** Nur vom Hovern geöffnet: so wieder einklappen. */ - let undoHover: (() => void) | undefined - let hoverTimer: number | undefined - let leaveTimer: number | undefined - const off = () => { - lit?.classList.remove('ui-spot') - lit = undefined - window.clearTimeout(timer) - } - const on = (pointer: HTMLElement, forAWhile: boolean) => { - off() - // Bei mehreren Treffern (Spalte oder eingeklappte Leiste) zählt der sichtbare. - const target = [...document.querySelectorAll(uiTargets[pointer.dataset.target ?? ''] ?? ':not(*)')].find((el) => el.getClientRects().length > 0) - if (!target) return - lit = target - target.classList.add('ui-spot') - if (forAWhile) timer = window.setTimeout(off, 2500) - } - /** Nach dem Öffnen hat der Bereich erst im nächsten Bild seinen Platz. */ - const showSoon = (pointer: HTMLElement, forAWhile: boolean) => - requestAnimationFrame(() => { - on(pointer, forAWhile) - lit?.scrollIntoView({ block: 'nearest', inline: 'nearest' }) - }) - const closeHovered = () => { - window.clearTimeout(hoverTimer) - window.clearTimeout(leaveTimer) - undoHover?.() - undoHover = undefined - } - const openAndShow = (pointer: HTMLElement) => { - window.clearTimeout(hoverTimer) - window.clearTimeout(leaveTimer) - // Was das Hovern schon geöffnet hat, bleibt jetzt offen. - undoHover = undefined - reveal(areasOf(pointer.dataset.target ?? '')) - on(pointer, true) - showSoon(pointer, true) - } - const pointerOf = (e: Event) => (e.target as Element | null)?.closest?.('.ui-pointer') ?? undefined - container.addEventListener('pointerover', (e) => { - const p = pointerOf(e) - if (!p || e.pointerType !== 'mouse') return - window.clearTimeout(leaveTimer) - on(p, false) - // Kurz warten: Wer mit der Maus nur über den Text fährt, soll keine Spalten auf- und zuklappen. - window.clearTimeout(hoverTimer) - hoverTimer = window.setTimeout(() => { - if (undoHover) return - const areas = areasOf(p.dataset.target ?? '') - if (areas.length === 0) return - undoHover = reveal(areas) - showSoon(p, false) - }, 250) - }) - container.addEventListener('pointerout', (e) => { - if (!pointerOf(e) || e.pointerType !== 'mouse') return - off() - window.clearTimeout(hoverTimer) - // Etwas Spielraum, falls sich der Text beim Öffnen unter der Maus verschiebt. - window.clearTimeout(leaveTimer) - leaveTimer = window.setTimeout(closeHovered, 400) - }) - container.addEventListener('click', (e) => { - const p = pointerOf(e) - if (p) openAndShow(p) - }) - container.addEventListener('focusin', (e) => { - const p = pointerOf(e) - if (p) on(p, false) - }) - container.addEventListener('focusout', (e) => { - if (pointerOf(e)) off() - }) - container.addEventListener('keydown', (e) => { - const p = pointerOf(e) - if (p && (e.key === 'Enter' || e.key === ' ')) { - e.preventDefault() - openAndShow(p) - } - }) - return { - off: () => { - closeHovered() - off() - }, - } -} diff --git a/web/src/ui/textFileView.ts b/web/src/ui/textFileView.ts deleted file mode 100644 index ea0926f9..00000000 --- a/web/src/ui/textFileView.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { t } from './i18n' - -/** Eine Textdatei wie im Editor: Name oben, darunter der Inhalt mit Zeilennummern. `written`: vom Programm gespeichert. */ -export function textFileView(name: string, content: string, written = false): HTMLElement { - const box = document.createElement('figure') - box.className = written ? 'learn-file written' : 'learn-file' - const caption = document.createElement('figcaption') - const icon = document.createElement('span') - icon.className = 'learn-file-icon' - icon.setAttribute('aria-hidden', 'true') - icon.textContent = '📄' - caption.append(icon, written ? t('web_sim_written_file_fmt', name) : name) - const pre = document.createElement('pre') - pre.className = 'learn-file-text' - pre.dir = 'ltr' - // Am Ende steht meist ein Zeilenumbruch; dahinter beginnt keine neue Zeile. - const lines = content.replace(/\n$/, '').split('\n') - lines.forEach((line, i) => { - const n = document.createElement('span') - n.className = 'ln' - n.textContent = String(i + 1) - pre.append(n, line + '\n') - }) - box.append(caption, pre) - return box -} - -/** Alle Dateien, die ein Programm gespeichert hat. */ -export function writtenFilesView(files: Map): HTMLElement[] { - return [...files].map(([name, content]) => textFileView(name, content, true)) -} diff --git a/web/src/ui/tooltip.ts b/web/src/ui/tooltip.ts deleted file mode 100644 index f9c67552..00000000 --- a/web/src/ui/tooltip.ts +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Tooltips für Symbolknöpfe: Name aus `data-tip`, erscheint kurz nach dem Überfahren oder beim Fokus mit der Tastatur. - * Ein einziges schwebendes Element, damit es am Fensterrand nicht abgeschnitten wird. - */ - -let tip: HTMLElement | undefined -let timer: number | undefined -let current: HTMLElement | undefined - -function show(target: HTMLElement) { - const text = target.dataset.tip - if (!text) return - tip ??= Object.assign(document.createElement('div'), { className: 'tooltip', role: 'tooltip' }) - if (!tip.isConnected) document.body.append(tip) - tip.textContent = text - tip.hidden = false - const r = target.getBoundingClientRect() - const w = tip.offsetWidth - const h = tip.offsetHeight - const left = Math.min(Math.max(8, r.left + r.width / 2 - w / 2), window.innerWidth - w - 8) - const below = r.bottom + 6 + h < window.innerHeight - tip.style.left = `${left}px` - tip.style.top = `${below ? r.bottom + 6 : r.top - h - 6}px` -} - -function hide() { - window.clearTimeout(timer) - timer = undefined - current = undefined - if (tip) tip.hidden = true -} - -export function installTooltips() { - document.addEventListener('pointerover', (e) => { - if (e.pointerType === 'touch') return - const target = (e.target as Element).closest('[data-tip]') - if (target === current) return - hide() - if (!target) return - current = target - timer = window.setTimeout(() => show(target), 350) - }) - document.addEventListener('focusin', (e) => { - const target = (e.target as Element).closest('[data-tip]') - hide() - if (target?.matches(':focus-visible')) show(target) - }) - for (const type of ['pointerdown', 'focusout', 'scroll', 'keydown']) document.addEventListener(type, hide, true) -} diff --git a/web/src/ui/visubot.webp b/web/src/ui/visubot.webp deleted file mode 100644 index 7dc5b040..00000000 Binary files a/web/src/ui/visubot.webp and /dev/null differ diff --git a/web/src/ui/visubotModel.ts b/web/src/ui/visubotModel.ts deleted file mode 100644 index 3ad21f73..00000000 --- a/web/src/ui/visubotModel.ts +++ /dev/null @@ -1,157 +0,0 @@ -import type { MBotPart } from '../core/mbot' -import { markdownToHTML } from '../learn/markdown' -import { t } from './i18n' -import type { Robot3D } from './robot3d' - -/** - * Tour durch den mBot2 wie bei JavaPicoGoat: Jeder Schritt einer Lektion zeigt das Modell, die Kamera fährt zu den Teilen, - * um die es geht. Gelb unterstrichene Wörter im Text lassen ihr Teil aufleuchten, Fahren mit der Maus zeigt den Namen. - */ - -export const partName = (part: MBotPart) => t(`web_visubot_part_${part}`) - -interface ModelView { - box: HTMLElement - /** Das Modell, sobald three.js geladen ist */ - ready: Promise - /** Teile, die der Schritt zeigt (leuchten, wenn sonst nichts gewählt ist) */ - focus: MBotPart[] - view?: string -} - -const models = new WeakMap() - -/** Bühne mit dem Modell; `onPick` bekommt das angeklickte Teil. */ -function modelStage(focus: MBotPart[], view: string | undefined, spin: boolean, onPick?: (part: MBotPart) => void): ModelView { - const box = document.createElement('div') - box.className = 'visubot-model' - const stage = document.createElement('div') - stage.className = 'visubot-stage' - const label = document.createElement('div') - label.className = 'visubot-part-label' - label.hidden = true - stage.append(label) - const showLabel = (part: MBotPart, x: number, y: number) => { - label.hidden = false - label.textContent = partName(part) - label.style.left = `${Math.min(x + 12, stage.clientWidth - 140)}px` - label.style.top = `${y + 12}px` - } - const hint = document.createElement('p') - hint.className = 'visubot-model-hint hint' - hint.textContent = t('web_visubot_tap_part') - hint.hidden = !!onPick - box.append(stage, hint) - const ready = import('./robot3d') - .then(({ Robot3D }) => { - const robot = new Robot3D(stage) - robot.showcase() - robot.highlight(focus) - // Geht es um die LEDs, leuchten sie blau wie nach dem Einschalten. - if (focus.includes('leds')) robot.setLed([40, 90, 255]) - // Zuerst etwas weiter weg, dann zu den Teilen fliegen: so sieht man, wo sie am Roboter sitzen. - robot.focusOn([], view === 'unten' || view === 'bottom' ? '' : view, false, 1) - robot.focusOn(focus, view, spin) - let down: { x: number; y: number } | undefined - stage.addEventListener('pointerdown', (e) => (down = { x: e.clientX, y: e.clientY })) - stage.addEventListener('pointerup', (e) => { - if (!down || Math.hypot(e.clientX - down.x, e.clientY - down.y) > 5) return - const part = robot.partAt(e.clientX, e.clientY) - if (!part) return - if (onPick) return onPick(part) - // Antippen zeigt das Teil und seinen Namen (auch ohne Maus) - robot.highlight(part) - const r = stage.getBoundingClientRect() - showLabel(part, e.clientX - r.left, e.clientY - r.top) - }) - // Name des Teils unter der Maus - stage.addEventListener('pointermove', (e) => { - // Im Suchspiel verrät der Name die Antwort. - if (e.pointerType !== 'mouse' || onPick) return - const part = robot.partAt(e.clientX, e.clientY) - label.hidden = !part - if (!part) return - const r = stage.getBoundingClientRect() - showLabel(part, e.clientX - r.left, e.clientY - r.top) - }) - stage.addEventListener('pointerleave', () => (label.hidden = true)) - return robot - }) - .catch((err: unknown) => { - console.error(err) - stage.textContent = t('web_visubot_no_3d') - return undefined - }) - const model: ModelView = { box, ready, focus, view } - models.set(box, model) - return model -} - -/** Lektionsblock `::: mbot2`. */ -export function renderRobotModel(focus: MBotPart[], view?: string, spin = false): HTMLElement { - return modelStage(focus, view, spin).box -} - -/** - * Gelb unterstrichene Wörter (`[Wort]{teil=…}`) mit dem Modell im selben Schritt verbinden: - * Zeigen lässt das Teil aufleuchten, Antippen fährt die Kamera hin. - */ -export function wireRobotLinks(root: HTMLElement) { - const box = root.querySelector('.visubot-model') - const model = box && models.get(box) - for (const link of root.querySelectorAll('[data-robot-part]')) { - const parts = (link.dataset.robotPart ?? '').split(',').filter(Boolean) as MBotPart[] - link.title = parts.map(partName).join(', ') - if (!model) continue - const show = () => void model.ready.then((r) => r?.highlight(parts)) - const back = () => void model.ready.then((r) => r?.highlight(model.focus)) - link.addEventListener('mouseenter', show) - link.addEventListener('focus', show) - link.addEventListener('mouseleave', back) - link.addEventListener('blur', back) - link.addEventListener('click', () => { - void model.ready.then((r) => { - r?.highlight(parts) - r?.focusOn(parts, model.view) - }) - model.box.scrollIntoView({ block: 'nearest', behavior: 'smooth' }) - }) - } -} - -/** Lektionsblock `::: mbot2quiz`: nacheinander das gesuchte Teil im Modell anklicken. */ -export function renderRobotQuiz(tasks: { part: MBotPart; question: string }[], done?: string): HTMLElement { - let index = 0 - const question = document.createElement('div') - question.className = 'visubot-quiz-question' - const feedback = document.createElement('p') - feedback.className = 'visubot-quiz-feedback' - feedback.setAttribute('aria-live', 'polite') - const ask = () => { - question.innerHTML = markdownToHTML(`**${index + 1}/${tasks.length}** ${tasks[index].question}`) - } - const model = modelStage([], undefined, false, (part) => { - if (index >= tasks.length) return - void model.ready.then((r) => r?.highlight(part)) - if (part !== tasks[index].part) { - feedback.className = 'visubot-quiz-feedback wrong' - feedback.textContent = t('web_visubot_quiz_wrong_fmt', partName(part)) - return - } - index++ - feedback.className = 'visubot-quiz-feedback right' - if (index < tasks.length) { - feedback.textContent = `✓ ${t('web_visubot_quiz_right')}` - ask() - } else { - feedback.textContent = '' - question.innerHTML = markdownToHTML(`✓ ${done ?? t('web_visubot_quiz_right')}`) - question.classList.add('done') - } - }) - ask() - const box = document.createElement('div') - box.className = 'visubot-quiz' - box.append(question, model.box, feedback) - return box -} diff --git a/web/src/ui/visubotView.ts b/web/src/ui/visubotView.ts deleted file mode 100644 index 63ed2404..00000000 --- a/web/src/ui/visubotView.ts +++ /dev/null @@ -1,536 +0,0 @@ -import { MBOT_SENSORS, mbotColorName, type MBotAction, type MBotSensorValues } from '../core/mbot' -import { HELLO, mbotLink, showLabel } from '../core/mbotLink' -import { ElementType, makeElement, type VSElement } from '../core/model' -import { emptyWorld, ROBOT_STEP_MS, RobotWorld, type RobotWorldSetup } from '../core/robotWorld' -import type { SimulationEngine } from '../core/simulation' -import { currentLanguage, t } from './i18n' -import { hostWorld, visucodeURL } from './site' -import type { VisuBotMode } from '../learn/content' -import type { Robot3D } from './robot3d' -import visuBotLogoURL from './visubot.webp' - -/** - * Welt, gespeichert im Browser: VisuStruct (`0`), VisuBot (`1`), VisuObject (`2`) oder VisuFlow (`3`). - * Der frühere Modus „Im Kurs“ (`kurs`) gilt jetzt als VisuBot. - */ -declare const __APP_VERSION__: string - -const modeKey = 'visustruct.visubot' - -function storedMode(): string | null { - try { - return localStorage.getItem(modeKey) - } catch { - return null - } -} - -export function visuBotMode(): VisuBotMode { - const value = storedMode() - return value === '1' || value === 'kurs' ? 'on' : 'off' -} - -/** Welt VisuObject: Klassendiagramm und Java statt Struktogramm (eigene Oberfläche, `objectApp.ts`). */ -export const visuObjectWorld = () => storedMode() === '2' - -export function setVisuObjectWorld() { - try { - localStorage.setItem(modeKey, '2') - } catch { - /* ohne Speicher nur für diese Sitzung */ - } -} - -/** Welt VisuFlow: PAP (flowchart) zeichnen, eigene Oberfläche (`flowApp.ts`). */ -export const visuFlowWorld = () => storedMode() === '3' - -export function setVisuFlowWorld() { - try { - localStorage.setItem(modeKey, '3') - } catch { - /* ohne Speicher nur für diese Sitzung */ - } -} - -/** Welt aus der Auswahl: `off` VisuStruct, `on` VisuBot, `object` VisuObject, `flow` VisuFlow. */ -export type WorldChoice = VisuBotMode | 'object' | 'flow' - -/** Hat man schon einmal zwischen VisuStruct und VisuBot gewählt? Sonst fragt der Start-Dialog. */ -export const worldChosen = () => storedMode() !== null - -export function setVisuBotMode(mode: VisuBotMode) { - try { - localStorage.setItem(modeKey, mode === 'on' ? '1' : '0') - } catch { - /* ohne Speicher nur für diese Sitzung */ - } -} - -/** Kopfzeile im VisuBot-Modus: der Roboter aus dem VisuBot-Logo und „Visu“ + orangefarbenes „Bot“. */ -export const visuBotBrand = `VisuBot` - -/** Gibt es mBot2-Kästen und den 3D-Simulator? (Nur in der Welt VisuBot.) */ -export const visuBotEnabled = () => visuBotMode() !== 'off' - -const escapeHTML = (s: string) => s.replace(/[&<>"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]!) - -/** - * Auswahl der Welt für Zahnrad und Start-Dialog; die Knöpfe tragen `data-world`. - * Auf einer Welt-Subdomain (visubot.visucode.de …) hat man die Welt schon gewählt: Dort gibt es nur die Welten dieser - * Seite, also keine Knöpfe, nur einen Link auf VisuCode. - */ -export function worldChooserHTML(): string { - const current: WorldChoice = visuObjectWorld() ? 'object' : visuFlowWorld() ? 'flow' : visuBotMode() - const site = hostWorld() - const all: [WorldChoice, string][] = [['off', 'VisuStruct'], ['on', 'VisuBot'], ['object', 'VisuObject'], ['flow', 'VisuFlow']] - const shown = site ? [] : all - const button = ([mode, label]: [WorldChoice, string]) => - `` - const hint = { off: 'web_visubot_world_visustruct_hint', on: 'web_visubot_world_visubot_hint', object: 'web_visuobject_world_hint', flow: 'web_visuflow_world_hint' }[current] - const buttons = shown.length - ? `
    ${shown.map(button).join('')}
    ` - : '' - return site - ? `${buttons}${visucodeLinkHTML()}` - : `${buttons} - ${escapeHTML(t(hint))}` -} - -/** Name der Welt der Subdomain für den Start-Dialog, z. B. „Visu“ + orangefarbenes „Bot“. */ -export function worldBrandHTML(): string { - const site = hostWorld() - return site === 'visubot' ? visuBotBrand : site === 'visuobject' ? 'VisuObject' : 'VisuStruct' -} - -/** Überschrift „Womit möchtest du arbeiten?“ im Zahnrad; entfällt auf den Welt-Subdomains (dort gibt es nichts zu wählen). */ -export function worldTitleHTML(): string { - const site = hostWorld() - return site ? '' : `

    ${escapeHTML(t('web_visubot_world_title'))}

    ` -} - -/** Marke „VisuCode“ als Link auf visucode.de, wo man ein anderes Programm wählt. */ -export function visucodeLinkHTML(): string { - const url = visucodeURL(currentLanguage()) - // Nur die anderen Programme nennen: wer in VisuBot ist, braucht keinen Hinweis auf VisuBot. - const own = { visustruct: 'VisuStruct', visubot: 'VisuBot', visuobject: 'VisuObject' }[hostWorld() ?? 'visustruct'] - const others = ['VisuStruct', 'VisuBot', 'VisuObject', 'VisuStudio'].filter((name) => name !== own) - return `

    VisuCode - ${escapeHTML(t('web_visubot_world_visucode_hint').replace('%1$@', others.slice(0, -1).join(', ')).replace('%2$@', others.at(-1)!)).replace('visucode.de', `visucode.de`)} - ${escapeHTML(t('web_about_version_fmt').replace('%@', __APP_VERSION__))} · Holger Sebastiao · sebastiao.org

    ` -} - -/** - * Simulator-Bereich im Simulationspanel: Welt von oben/schräg in 3D, darunter Sensorwerte. - * three.js wird erst geladen, wenn der Bereich das erste Mal gebraucht wird. - */ -export class VisuBotView { - readonly element: HTMLElement - world: RobotWorld - private view?: Robot3D - private loading?: Promise - private status: HTMLElement - private stage: HTMLElement - private picker: HTMLElement - private applied = 0 - /** Befehle, die der echte mBot2 schon gefahren ist. */ - private sent = 0 - private liveStatus: HTMLElement - /** Welten-Knöpfe, 3D-Ansicht und Messwerte: stehen während der Simulation im Fenster „mBot2 live“. */ - readonly viewport: HTMLElement - private stopListening: () => void - private realSensors?: MBotSensorValues - /** Regler und Knöpfe „Sensoren ausprobieren“: Werte, die keine Welt liefert. */ - private controls: MBotSensorValues = { getBrightness: 50, getLoudness: 0, getPitch: 0, getRoll: 0 } - private pressTimers = new Map() - /** „mBot2 testen“: alle Sensoren live und Knöpfe für die Aktoren (nur mit echtem mBot2) */ - private check: HTMLDetailsElement - private checking = false - /** Nach dem Umschalten der Welt: Simulation von vorn (setzt das Simulationspanel). */ - onWorldChange?: () => void - - constructor(setup: RobotWorldSetup = emptyWorld) { - this.world = new RobotWorld(setup) - this.element = document.createElement('section') - this.element.className = 'visubot' - this.element.innerHTML = `

    ${t('web_visubot_world')}

    ${t('web_visubot_world_in_preview')}

    ${controlsHTML()}${checkHTML()}` - this.stage = this.element.querySelector('.visubot-stage')! - this.picker = this.element.querySelector('.visubot-worlds')! - this.status = this.element.querySelector('.visubot-status')! - this.liveStatus = this.element.querySelector('.visubot-live')! - this.viewport = this.element.querySelector('.visubot-viewport')! - this.stopListening = mbotLink.listen(() => this.updateLive()) - this.setupControls() - this.check = this.element.querySelector('.visubot-check')! - this.setupCheck() - this.updateLive() - this.updateStatus() - } - - /** 3D-Ansicht aufbauen (beim ersten Einblenden). */ - show() { - this.loading ??= import('./robot3d').then(({ Robot3D }) => { - this.view = new Robot3D(this.stage) - this.view.setWorld(this.world.setup) - this.view.place(this.world.pose, this.world.trail) - this.view.setLeds(this.world.leds) - }).catch(() => { - this.stage.textContent = t('web_visubot_no_3d') - }) - } - - /** 3D-Ansicht zurück ins Simulationspanel (wenn „mBot2 live“ zu ist oder die Simulation endet). */ - restoreViewport() { - if (this.viewport.parentElement !== this.element) this.liveStatus.after(this.viewport) - } - - /** - * Welten einer Übung (Labyrinth usw.): Knöpfe zum Umschalten über der Ansicht; leer = freie Fläche. - * Programme laufen in der gewählten Welt. - */ - setWorlds(worlds: RobotWorldSetup[]) { - this.picker.replaceChildren() - this.picker.hidden = worlds.length < 2 - worlds.forEach((setup, i) => { - const b = document.createElement('button') - b.textContent = t('web_visubot_world_fmt', i + 1) - b.addEventListener('click', () => this.useWorld(setup, b)) - this.picker.append(b) - }) - this.useWorld(worlds[0] ?? emptyWorld, this.picker.firstElementChild as HTMLElement | null) - } - - private useWorld(setup: RobotWorldSetup, button: HTMLElement | null) { - for (const b of this.picker.children) b.classList.toggle('active', b === button) - if (setup === this.world.setup) return - this.world = new RobotWorld(setup) - this.applied = 0 - this.sent = 0 - this.view?.setWorld(setup) - this.view?.place(this.world.pose, this.world.trail) - this.view?.setLed(undefined) - this.view?.setScreen(undefined) - this.updateStatus() - this.onWorldChange?.() - } - - reset() { - this.world.reset() - this.applied = 0 - this.sent = 0 - this.realSensors = undefined - this.view?.place(this.world.pose, this.world.trail) - this.view?.setLed(undefined) - this.view?.setScreen(undefined) - this.updateStatus() - } - - /** Vor einem Schritt: Sensorwerte der Welt und der Regler an die Simulation geben. */ - prepare(engine: SimulationEngine) { - engine.robotSensors = { ...this.controls, ...this.world.sensors() } - } - - private setupControls() { - for (const range of this.element.querySelectorAll('.visubot-controls input[type=range]')) { - const out = range.parentElement!.querySelector('output')! - const update = () => { - this.controls[range.name] = Number(range.value) - out.textContent = range.value - } - range.addEventListener('input', update) - update() - } - // Tasten, Joystick, Schütteln: eine Sekunde lang „gedrückt“, damit die Simulation es im nächsten Schritt sieht - for (const button of this.element.querySelectorAll('.visubot-controls button[name]')) { - button.addEventListener('click', () => { - const name = button.name - this.controls[name] = true - button.classList.add('pressed') - window.clearTimeout(this.pressTimers.get(name)) - this.pressTimers.set( - name, - window.setTimeout(() => { - this.controls[name] = false - button.classList.remove('pressed') - }, 1000), - ) - }) - } - } - - private setupCheck() { - for (const button of this.check.querySelectorAll('button[data-py]')) { - button.addEventListener('click', () => { - // `data-after`: nach `data-wait` Millisekunden zurück (Räder anhalten, wieder „Hallo VisuBot!“ zeigen) - const { after, wait } = button.dataset - void mbotLink - .run(button.dataset.py!) - .then(() => (after ? new Promise((r) => setTimeout(r, Number(wait))).then(() => mbotLink.run(after)) : undefined)) - .catch((e: Error) => this.liveFailed(e)) - }) - } - this.check.addEventListener('toggle', () => void this.readCheck()) - } - - /** Solange „mBot2 testen“ offen ist: alle Sensoren reihum lesen und zeigen. */ - private async readCheck() { - if (this.checking) return - this.checking = true - try { - while (this.check.open && mbotLink.connected) { - for (const cell of this.check.querySelectorAll('[data-sensor]')) { - if (!this.check.open || !mbotLink.connected) break - const name = cell.dataset.sensor! - const value = await mbotLink.ask(MBOT_SENSORS[name].python).catch(() => undefined) - if (value === undefined) continue - cell.textContent = - typeof MBOT_SENSORS[name].simulated === 'boolean' - ? value === true || (typeof value === 'number' && value !== 0) ? '✓' : '–' - : String(typeof value === 'number' ? Math.round(value * 10) / 10 : value) - } - await new Promise((r) => setTimeout(r, 200)) - } - } finally { - this.checking = false - } - } - - /** Fährt der echte mBot2 mit? */ - get live(): boolean { - return mbotLink.connected - } - - /** - * Vor einem Schritt mit dem echten mBot2: seine Sensoren lesen (statt Welt und Reglern). - * Abstand und Linie immer, die übrigen nur, wenn das Programm sie benutzt (`used`), damit ein Schritt schnell bleibt. - */ - async prepareLive(engine: SimulationEngine, used: string[] = []) { - const values: MBotSensorValues = {} - for (const name of new Set(['getDistance', 'getLine', ...used])) { - const value = await mbotLink.ask(MBOT_SENSORS[name].python) - // Ja/Nein-Sensoren antworten mit 0 oder 1 statt False/True (getestet am echten mBot2). - if (typeof MBOT_SENSORS[name].simulated === 'boolean') values[name] = value === true || (typeof value === 'number' && value !== 0) - else if (typeof MBOT_SENSORS[name].simulated === 'string') values[name] = typeof value === 'string' ? value : String(MBOT_SENSORS[name].simulated) - else values[name] = typeof value === 'number' ? (name === 'getDistance' ? Math.round(value) : value) : 0 - } - this.realSensors = values - engine.robotSensors = { ...this.controls, ...this.world.sensors(), ...values } - } - - /** Nach einem Schritt: neue Befehle an den echten mBot2; kehrt zurück, wenn er fertig gefahren ist. */ - async drive(actions: MBotAction[]) { - for (; this.sent < actions.length; this.sent++) { - const action = actions[this.sent] - if (action.python) await mbotLink.run(action.python) - else if (action.method === 'pause') await new Promise((r) => setTimeout(r, action.args[0])) - } - } - - /** Nach einem Fehler der Verbindung: trennen und Grund zeigen. */ - liveFailed(error: unknown) { - void mbotLink.disconnect(error instanceof Error ? error.message : String(error)) - } - - private updateLive() { - this.liveStatus.textContent = mbotLinkText() - this.liveStatus.classList.toggle('connected', mbotLink.connected) - this.check.hidden = !mbotLink.connected - if (!mbotLink.connected) this.check.open = false - this.updateStatus() - } - - /** Nach einem Schritt: neue Befehle ausführen und in `ms` Millisekunden zeigen. */ - follow(actions: MBotAction[], ms: number) { - for (; this.applied < actions.length; this.applied++) { - const action = actions[this.applied] - this.world.apply(action) - // Ton im Browser hören (der echte mBot2 spielt ihn selbst) - if (action.method === 'playTone' && !this.live) playTone(action.args[0], action.args[1]) - this.view?.moveTo(this.world.pose, this.world.trail, ms) - this.view?.setLeds(this.world.leds) - this.view?.setScreen(this.world.screen) - } - // Mit `mbot.drive` fährt er auch ohne neuen Befehl weiter - const moving = this.world.wheels.some((p) => p !== 0) - this.world.tick(ROBOT_STEP_MS) - if (moving) this.view?.moveTo(this.world.pose, this.world.trail, ms) - this.updateStatus() - } - - dispose() { - this.stopListening() - for (const timer of this.pressTimers.values()) window.clearTimeout(timer) - this.view?.dispose() - this.view = undefined - this.loading = undefined - } - - private updateStatus() { - const line = this.world.lineState().toString(2).padStart(4, '0') - const parts = [ - `${t('web_visubot_distance')}: ${this.world.distance()} cm`, - `${t('web_visubot_line')}: ${line}`, - ] - if (this.world.bumps > 0) parts.push(`⚠︎ ${t('web_visubot_bumps')}: ${this.world.bumps}`) - if (this.world.screen !== undefined) parts.push(`📺 ${this.world.screen}`) - if (this.realSensors && mbotLink.connected) - parts.push(`🤖 ${t('web_visubot_distance')}: ${this.realSensors.getDistance} cm · ${t('web_visubot_line')}: ${Number(this.realSensors.getLine).toString(2).padStart(4, '0')}`) - this.status.textContent = parts.join(' · ') - } -} - -let audio: AudioContext | undefined - -/** Kurzer Ton über die Lautsprecher des Computers, wie der Lautsprecher des CyberPi. */ -export function playTone(hz: number, ms: number) { - try { - audio ??= new AudioContext() - const osc = audio.createOscillator() - const gain = audio.createGain() - osc.type = 'square' - osc.frequency.value = Math.max(20, Math.min(20000, hz)) - gain.gain.value = 0.05 - osc.connect(gain).connect(audio.destination) - const end = audio.currentTime + Math.min(Math.max(ms, 0), 5000) / 1000 - osc.start() - osc.stop(end) - } catch { - /* ohne Ton weiter */ - } -} - -/** Regler und Knöpfe für Sensoren, die keine Welt liefert (Licht, Lautstärke, Neigung, Tasten, Joystick, Schütteln). */ -function controlsHTML(): string { - const range = (name: string, key: string, min: number, max: number, value: number) => - `` - const press = (name: string, text: string, key: string) => `` - return `
    🎛 ${t('web_visubot_controls')} -

    ${t('web_visubot_controls_hint')}

    - ${range('getBrightness', 'web_visubot_sensor_brightness', 0, 100, 50)} - ${range('getLoudness', 'web_visubot_sensor_loudness', 0, 100, 0)} - ${range('getPitch', 'web_visubot_sensor_pitch', -90, 90, 0)} - ${range('getRoll', 'web_visubot_sensor_roll', -90, 90, 0)} -
    - ${press('isButtonA', 'A', 'web_visubot_sensor_button_a')}${press('isButtonB', 'B', 'web_visubot_sensor_button_b')} - ${press('isJoystickUp', '↑', 'web_visubot_sensor_joy_up')}${press('isJoystickDown', '↓', 'web_visubot_sensor_joy_down')} - ${press('isJoystickLeft', '←', 'web_visubot_sensor_joy_left')}${press('isJoystickRight', '→', 'web_visubot_sensor_joy_right')} - ${press('isShaken', `〰 ${t('web_visubot_shake')}`, 'web_visubot_sensor_shake')} -
    -
    ` -} - -/** „mBot2 testen“: jeden Aktor einmal auslösen, jeden Sensor live ablesen (wie die Labor-Seite). */ -function checkHTML(): string { - const attr = (py: string) => py.replace(/"/g, '"') - const act = (py: string, key: string, after?: string, wait = 1000) => - `` - const stop = "mbot2.EM_stop('ALL')" - const sensors = visuBotSensors - .map((s) => `${t(s.key)}…`) - .join('') - return `` -} - -/** Kurzer Zustand der Verbindung zum echten mBot2 (leer, wenn nicht verbunden und kein Fehler). */ -export function mbotLinkText(): string { - if (mbotLink.connecting) return t('web_visubot_connecting') - if (mbotLink.connected) return `✓ ${t('web_visubot_connected')}` - return mbotLink.error ? `⚠︎ ${t('web_visubot_link_error')}: ${mbotLink.error}` : '' -} - -/** Mit dem echten mBot2 verbinden bzw. trennen (Knopf in der Werkzeugleiste und in den Einstellungen). */ -export async function toggleMBotLink() { - if (mbotLink.connected || mbotLink.connecting) return mbotLink.disconnect() - try { - await mbotLink.connect() - } catch (e) { - // Abbrechen der Auswahl ist kein Fehler - await mbotLink.disconnect((e as Error).name === 'NotFoundError' ? undefined : (e as Error).message) - } -} - -/** Zusätzliche Elemente im VisuBot-Modus: fertig beschriftete Kästen zum Reinziehen. */ -export interface VisuBotTemplate { - /** Schlüssel des Namens in der Palette */ - key: string - /** Gruppe in der Palette wie im Nachschlagen (`web_visubot_group_…`) */ - group: string - type: ElementType - make: () => VSElement -} - -const statement = (text: string) => () => makeElement(ElementType.anweisung, { textLines: [text] }) - -export const visuBotTemplates: VisuBotTemplate[] = [ - { key: 'web_visubot_el_forward', group: 'drive', type: ElementType.anweisung, make: statement('mbot.forward()') }, - { key: 'web_visubot_el_backward', group: 'drive', type: ElementType.anweisung, make: statement('mbot.backward()') }, - { key: 'web_visubot_el_right', group: 'drive', type: ElementType.anweisung, make: statement('mbot.turnRight()') }, - { key: 'web_visubot_el_left', group: 'drive', type: ElementType.anweisung, make: statement('mbot.turnLeft()') }, - { key: 'web_visubot_el_drive', group: 'wheels', type: ElementType.anweisung, make: statement('mbot.drive(30, 30)') }, - { key: 'web_visubot_el_stop', group: 'wheels', type: ElementType.anweisung, make: statement('mbot.stop()') }, - { key: 'web_visubot_el_led', group: 'leds', type: ElementType.anweisung, make: statement('mbot.setLed(255, 0, 0)') }, - { key: 'web_visubot_el_led_off', group: 'leds', type: ElementType.anweisung, make: statement('mbot.ledOff()') }, - { key: 'web_visubot_el_led_color', group: 'leds', type: ElementType.anweisung, make: statement('mbot.led(0).setColor("grün")') }, - { key: 'web_visubot_el_tone', group: 'sound', type: ElementType.anweisung, make: statement('mbot.playTone(440, 300)') }, - { key: 'web_visubot_el_text', group: 'screen', type: ElementType.anweisung, make: statement('mbot.showText("Hallo")') }, - { key: 'web_visubot_el_emotion', group: 'emotion', type: ElementType.anweisung, make: statement('mbot.emotion("smile")') }, - { key: 'web_visubot_el_wait', group: 'wait', type: ElementType.anweisung, make: statement('mbot.pause(100)') }, - { key: 'web_visubot_el_timer', group: 'wait', type: ElementType.anweisung, make: statement('mbot.resetTimer()') }, -] - -/** Farbe der Gruppe (Streifen in der Palette, Punkt im Nachschlagen `{.bot-…}`): Fahren orange, Licht gelbgrün, - * Ton magenta, Bildschirm/Gefühle indigo, Warten grau, alle Sensoren türkis. Der Kasten selbst bleibt gelb. */ -export function visuBotGroupColor(group: string): string { - const colors: Record = { drive: 'fahren', wheels: 'fahren', leds: 'licht', sound: 'ton', screen: 'anzeige', emotion: 'anzeige', wait: 'warten' } - return colors[group] ?? 'sensor' -} - -/** Symbol jeder Gruppe (Palette und Nachschlagen): Symbole merkt man sich leichter als viele Farbtöne. */ -export const visuBotGroupIcon: Record = { - drive: '🚗', - wheels: '⚙️', - leds: '💡', - sound: '🔊', - screen: '🖥️', - emotion: '😊', - wait: '⏱️', - distance: '📏', - line: '➖', - color: '🎨', - buttons: '🕹️', - light: '🔆', - tilt: '📐', - timer: '🔋', -} - -/** Sensoren: Werte, die die Schüler selbst in eine Bedingung einbauen (`mbot.getDistance() < 20`). */ -export const visuBotSensors: { key: string; group: string; text: string }[] = [ - { key: 'web_visubot_sensor_distance', group: 'distance', text: 'mbot.getDistance()' }, - { key: 'web_visubot_sensor_line', group: 'line', text: 'mbot.getLine()' }, - { key: 'web_visubot_sensor_line_left', group: 'line', text: 'mbot.isLineLeft()' }, - { key: 'web_visubot_sensor_line_right', group: 'line', text: 'mbot.isLineRight()' }, - // Farbname in der Sprache der Lektionen ("rot" oder "red") - { key: 'web_visubot_sensor_color', group: 'color', get text() { return `mbot.getColor().equals("${mbotColorName('red')}")` } }, - { key: 'web_visubot_sensor_button_a', group: 'buttons', text: 'mbot.isButtonA()' }, - { key: 'web_visubot_sensor_button_b', group: 'buttons', text: 'mbot.isButtonB()' }, - { key: 'web_visubot_sensor_joy_up', group: 'buttons', text: 'mbot.isJoystickUp()' }, - { key: 'web_visubot_sensor_joy_down', group: 'buttons', text: 'mbot.isJoystickDown()' }, - { key: 'web_visubot_sensor_joy_left', group: 'buttons', text: 'mbot.isJoystickLeft()' }, - { key: 'web_visubot_sensor_joy_right', group: 'buttons', text: 'mbot.isJoystickRight()' }, - { key: 'web_visubot_sensor_brightness', group: 'light', text: 'mbot.getBrightness()' }, - { key: 'web_visubot_sensor_loudness', group: 'light', text: 'mbot.getLoudness()' }, - { key: 'web_visubot_sensor_pitch', group: 'tilt', text: 'mbot.getPitch()' }, - { key: 'web_visubot_sensor_roll', group: 'tilt', text: 'mbot.getRoll()' }, - { key: 'web_visubot_sensor_shake', group: 'tilt', text: 'mbot.isShaken()' }, - { key: 'web_visubot_sensor_timer', group: 'timer', text: 'mbot.getTimer()' }, - { key: 'web_visubot_sensor_battery', group: 'timer', text: 'mbot.getBattery()' }, -] diff --git a/web/src/ui/workspaceStore.ts b/web/src/ui/workspaceStore.ts deleted file mode 100644 index b3181773..00000000 --- a/web/src/ui/workspaceStore.ts +++ /dev/null @@ -1,220 +0,0 @@ -/** - * Speicherort des Workspace: ein echter Ordner (Chrome/Edge, File System Access API) oder, - * wo der Browser das nicht kann (iPad, Safari, Firefox), ein Ordnerbaum in IndexedDB. - * Beide sehen für die Spalte „Dateien“ gleich aus (`WsDir`). - */ - -export interface WsEntry { - kind: 'file' | 'directory' - name: string -} - -export interface WsDir { - readonly name: string - list(): Promise - /** Unterordner; ohne `create` Fehler, wenn er fehlt. */ - dir(name: string, create?: boolean): Promise - read(name: string): Promise - write(name: string, text: string): Promise - has(name: string): Promise - /** Datei oder Ordner samt Inhalt. */ - remove(name: string): Promise -} - -// MARK: - Echter Ordner - -// Teile der File System Access API, die TypeScript noch nicht kennt. -type PermissionHandle = FileSystemDirectoryHandle & { - queryPermission?(o: { mode: 'readwrite' }): Promise - requestPermission?(o: { mode: 'readwrite' }): Promise - values(): AsyncIterable -} -type DirectoryPickerWindow = Window & { showDirectoryPicker?: (o?: object) => Promise } - -export const canPickFolder = () => typeof (window as DirectoryPickerWindow).showDirectoryPicker === 'function' - -class NativeDir implements WsDir { - constructor(readonly handle: FileSystemDirectoryHandle) {} - - get name() { - return this.handle.name - } - - async list(): Promise { - const out: WsEntry[] = [] - for await (const entry of (this.handle as PermissionHandle).values()) { - if (entry.name.startsWith('.')) continue - out.push({ kind: entry.kind, name: entry.name }) - } - return out - } - - async dir(name: string, create = false): Promise { - return new NativeDir(await this.handle.getDirectoryHandle(name, { create })) - } - - async read(name: string): Promise { - return (await (await this.handle.getFileHandle(name)).getFile()).text() - } - - async write(name: string, text: string): Promise { - const file = await this.handle.getFileHandle(name, { create: true }) - const out = await file.createWritable() - await out.write(text) - await out.close() - } - - async has(name: string): Promise { - try { - await this.handle.getFileHandle(name) - return true - } catch { - try { - await this.handle.getDirectoryHandle(name) - return true - } catch { - return false - } - } - } - - async remove(name: string): Promise { - await this.handle.removeEntry(name, { recursive: true }) - } -} - -// MARK: - Im Browser (IndexedDB) - -const dbName = 'visustruct-workspace' - -function openDB(): Promise { - return new Promise((resolve, reject) => { - const req = indexedDB.open(dbName, 1) - req.onupgradeneeded = () => { - req.result.createObjectStore('files') - req.result.createObjectStore('meta') - } - req.onsuccess = () => resolve(req.result) - req.onerror = () => reject(req.error) - }) -} - -async function run(store: 'files' | 'meta', mode: IDBTransactionMode, body: (s: IDBObjectStore) => IDBRequest): Promise { - const db = await openDB() - return new Promise((resolve, reject) => { - const req = body(db.transaction(store, mode).objectStore(store)) - req.onsuccess = () => resolve(req.result) - req.onerror = () => reject(req.error) - }) -} - -/** Pfade wie `11FI1/2026-10-23/Rechner.visustruct`; ein Ordner ist ein Eintrag mit `/` am Ende. */ -class BrowserDir implements WsDir { - constructor( - readonly name: string, - private path: string, - ) {} - - private key(name: string) { - return this.path + name - } - - async list(): Promise { - const keys = (await run('files', 'readonly', (s) => s.getAllKeys())) as string[] - const seen = new Map() - for (const key of keys) { - if (!key.startsWith(this.path) || key === this.path) continue - const rest = key.slice(this.path.length) - const slash = rest.indexOf('/') - const name = slash < 0 ? rest : rest.slice(0, slash) - if (!seen.has(name)) seen.set(name, { kind: slash < 0 ? 'file' : 'directory', name }) - } - return [...seen.values()] - } - - async dir(name: string, create = false): Promise { - const key = this.key(name) + '/' - const exists = (await run('files', 'readonly', (s) => s.count(key))) > 0 - if (!exists) { - if (!create) throw new DOMException(name, 'NotFoundError') - await run('files', 'readwrite', (s) => s.put('', key)) - } - return new BrowserDir(name, key) - } - - async read(name: string): Promise { - const text = await run('files', 'readonly', (s) => s.get(this.key(name))) - if (typeof text !== 'string') throw new DOMException(name, 'NotFoundError') - return text - } - - async write(name: string, text: string): Promise { - await run('files', 'readwrite', (s) => s.put(text, this.key(name))) - } - - async has(name: string): Promise { - const n = await run('files', 'readonly', (s) => s.count(this.key(name))) - const d = await run('files', 'readonly', (s) => s.count(this.key(name) + '/')) - return n + d > 0 - } - - async remove(name: string): Promise { - const prefix = this.key(name) - await run('files', 'readwrite', (s) => s.delete(prefix)) - // Ordner: alles darunter (Schlüssel von `prefix/` bis `prefix/￿`). - await run('files', 'readwrite', (s) => s.delete(IDBKeyRange.bound(prefix + '/', prefix + '/￿'))) - } -} - -// MARK: - Welcher Workspace - -export type WorkspaceKind = 'folder' | 'browser' - -export interface Workspace { - kind: WorkspaceKind - root: WsDir -} - -/** - * Ordner wählen (nur Chrome/Edge). Der Ordner wird gemerkt, damit er nach dem Neuladen wieder da ist. - * Mit `create` wird darin ein Ordner dieses Namens angelegt (oder der vorhandene genommen) und benutzt. - */ -export async function pickFolder(create?: string, slot = 'folder'): Promise { - const picked = await (window as DirectoryPickerWindow).showDirectoryPicker!({ id: slot === 'folder' ? 'visustruct-workspace' : slot, mode: 'readwrite' }) - const handle = create && picked.name !== create ? await picked.getDirectoryHandle(create, { create: true }) : picked - await run('meta', 'readwrite', (s) => s.put(handle, slot)) - return { kind: 'folder', root: new NativeDir(handle) } -} - -export function browserWorkspace(): Workspace { - return { kind: 'browser', root: new BrowserDir('VisuStruct', '') } -} - -/** - * Gemerkter Ordner: `ready` = sofort nutzbar; sonst muss der Browser erst (mit einem Klick) fragen dürfen. - * `slot` trennt die Ordner der Welten (VisuStruct: `folder`, VisuObject: eigener Workspace). - */ -export async function storedFolder(slot = 'folder'): Promise<{ handle: FileSystemDirectoryHandle; ready: boolean } | undefined> { - try { - const handle = (await run('meta', 'readonly', (s) => s.get(slot))) as PermissionHandle | undefined - if (!handle) return undefined - const state = (await handle.queryPermission?.({ mode: 'readwrite' })) ?? 'granted' - return { handle, ready: state === 'granted' } - } catch { - return undefined - } -} - -/** Nach dem Neuladen: Zugriff auf den gemerkten Ordner erneut erlauben (braucht einen Klick). */ -export async function reopenFolder(handle: FileSystemDirectoryHandle): Promise { - const state = (await (handle as PermissionHandle).requestPermission?.({ mode: 'readwrite' })) ?? 'granted' - return state === 'granted' ? { kind: 'folder', root: new NativeDir(handle) } : undefined -} - -export async function forgetFolder(slot = 'folder'): Promise { - try { - await run('meta', 'readwrite', (s) => s.delete(slot)) - } catch { - /* nichts gemerkt */ - } -} diff --git a/web/start-visubot.png b/web/start-visubot.png deleted file mode 100644 index b27a9279..00000000 Binary files a/web/start-visubot.png and /dev/null differ diff --git a/web/start.png b/web/start.png deleted file mode 100644 index 850e0151..00000000 Binary files a/web/start.png and /dev/null differ diff --git a/web/test/blockLessons.test.ts b/web/test/blockLessons.test.ts deleted file mode 100644 index 4ba704f5..00000000 --- a/web/test/blockLessons.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { checkExercise, usedElementTypes } from '../src/core/exerciseCheck' -import { ElementType } from '../src/core/model' -import { driveRound } from '../src/learn/driveGame' -import { diagram, lessons, showingBlockLessons, tasks, useBlockLessons, useContentLanguage, useVisuBotContent, world } from '../src/learn/content' - -/** - * Block-Lektionen für die 5./6. Klasse (Holger 5.11.): Ist in VisuBot die Block-Ansicht an, zeigt der Lernbereich diese - * kurzen Lektionen. Statt Wissenstest gibt es nur „Das schaffst du heute“, Ausprobieren, ein Spiel und „Das hab ich geschafft“. - */ -describe('Block-Lektionen (de)', () => { - beforeAll(() => { - useVisuBotContent('on') - useContentLanguage('de') - useBlockLessons(true) - }) - afterAll(() => { - useBlockLessons(false) - useVisuBotContent('off') - }) - - it('erscheinen nur mit Block-Ansicht in VisuBot, mit eigenen Aufgaben', () => { - expect(showingBlockLessons()).toBe(true) - expect(lessons.length).toBeGreaterThanOrEqual(8) - expect(lessons.every((l) => l.blocks && l.robot)).toBe(true) - lessons.forEach((l, i) => expect(l.number, l.id).toBe(i + 1)) - expect(tasks.length).toBeGreaterThanOrEqual(4) - expect(tasks.every((t) => t.robot && t.id.startsWith('block-'))).toBe(true) - useBlockLessons(false) - expect(lessons.some((l) => l.blocks)).toBe(false) - expect(lessons.length).toBeGreaterThan(20) - useVisuBotContent('off') - useBlockLessons(true) - expect(showingBlockLessons()).toBe(false) - useVisuBotContent('on') - expect(showingBlockLessons()).toBe(true) - }) - - it('sind kurz: Kompetenzen vorn, ein Spiel, die Checkliste am Ende, kein Wissenstest', () => { - for (const l of lessons) { - const kinds = l.steps.map((s) => s.blocks.map((b) => b.kind as string)) - expect(l.steps.length, l.id).toBeLessThanOrEqual(7) - expect(l.steps[0].title, l.id).toBe('Das schaffst du heute') - expect(kinds[0], l.id).toContain('competences') - expect(l.steps[l.steps.length - 1].title, l.id).toBe('Das hab ich geschafft') - expect(kinds[kinds.length - 1], l.id).toContain('checklist') - expect(kinds.flat().filter((k) => k === 'checklist').length, l.id).toBe(1) - expect(kinds.flat(), l.id).not.toContain('quiz') - expect(kinds.slice(0, -1).flat().some((k) => k === 'driveGame' || k === 'game' || k === 'roundGame'), `${l.id}: Spiel`).toBe(true) - } - }) - - it('jede Übung prüft den mBot2, und die Musterlösung besteht sie', () => { - let checked = 0 - const sources = [...lessons.map((l) => ({ id: l.id, blocks: l.steps.flatMap((s) => s.blocks) })), ...tasks.map((t) => ({ id: t.id, blocks: t.blocks }))] - for (const l of sources) { - for (const block of l.blocks) { - if (block.kind !== 'exercise') continue - const ex = block.exercise - const where = `${l.id}: ${ex.id}` - expect(ex.id.startsWith('vbb-'), where).toBe(true) - expect(ex.tests.some((t) => t.drive || t.world || t.sensors || t.actions), where).toBe(true) - expect(ex.hints.length, where).toBeGreaterThanOrEqual(2) - const doc = diagram(ex.solution!)! - expect(doc, where).toBeDefined() - for (const o of checkExercise(doc, ex.tests, undefined, world)) expect(o.passed, `${where}: ${JSON.stringify(o.result)}`).toBe(true) - for (const r of ex.requires) expect(usedElementTypes(doc).has(ElementType[r as keyof typeof ElementType]), where).toBe(true) - expect(checkExercise({ caption: '', elements: [] }, ex.tests, undefined, world).every((o) => o.passed), where).toBe(false) - checked++ - } - } - expect(checked).toBeGreaterThanOrEqual(23) - }) - - it('jede Runde von „Wo landet der mBot2?“ endet auf dem angegebenen Feld', () => { - let checked = 0 - for (const l of lessons) { - for (const block of l.steps.flatMap((s) => s.blocks)) { - if (block.kind !== 'driveGame') continue - for (const [i, round] of block.game.rounds.entries()) { - const where = `${l.id}, Runde ${i + 1}` - const result = driveRound(round, block.game.field) - expect(result.problems, where).toEqual([]) - expect(result.run.bumps, where).toBe(0) - expect(result.cell, where).toEqual(round.target) - checked++ - } - } - } - expect(checked).toBeGreaterThanOrEqual(19) - }) -}) diff --git a/web/test/blockView.test.ts b/web/test/blockView.test.ts deleted file mode 100644 index 636e7b30..00000000 --- a/web/test/blockView.test.ts +++ /dev/null @@ -1,251 +0,0 @@ -// @vitest-environment jsdom -import { describe, expect, it } from 'vitest' -import type { LiveLabels } from '../src/core/codeGenerator' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava } from '../src/core/liveSource' -import { blockGroup, blockTokens, BlockView, programVariables, replaceBlockValue, replaceSensor, replaceValue, valueText, withoutBlockIcons } from '../src/ui/blockView' -import { ElementType, makeElement } from '../src/core/model' - -const blockLabels = { - start: 'Start', - else: 'else', - empty: 'leer', - hint: '', - trash: 'Papierkorb', - count: 'zähle mit', - countFrom: 'von', - countBelow: 'bis unter', - countTo: 'bis', - countAbove: 'bis über', - countStep: 'in Schritten von', - repeat: 'wiederhole', - repeatTimes: 'mal', - randomFrom: 'Zufallszahl von', - randomTo: 'bis', -} -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } -const main = (body: string) => `public class Main {\n public static void main(String[] args) {\n${body}\n }\n}` -const docOf = (java: string) => { - const parsed = parseLiveJava(java, labels, {}) - expect(parsed.problems).toEqual([]) - return documentFromJava({ caption: '', elements: [] }, parsed) -} - -describe('Blöcke (VisuBot)', () => { - it('teilt eine Zeile in Schlüsselwort, Werte und Sensoren', () => { - expect(blockTokens('while mbot.getDistance() > 20')).toEqual([ - { kind: 'kw', text: 'while' }, - { kind: 'text', text: ' ' }, - { kind: 'sensor', text: 'mbot.getDistance()' }, - { kind: 'text', text: ' > ' }, - { kind: 'lit', text: '20' }, - ]) - expect(blockTokens('mbot.showText("Hallo 1")')).toEqual([ - { kind: 'text', text: 'mbot.showText(' }, - { kind: 'str', text: '"Hallo 1"' }, - { kind: 'text', text: ')' }, - ]) - // Befehle sind keine Sensoren, Ziffern in Namen keine Werte - expect(blockTokens('mbot.stop(); x2 = 3')).toEqual([ - { kind: 'text', text: 'mbot.stop(); x2 = ' }, - { kind: 'lit', text: '3' }, - ]) - }) - - it('kennt die Gruppe jedes mBot2-Befehls', () => { - const st = (text: string) => makeElement(ElementType.anweisung, { textLines: [text] }) - expect(blockGroup(st('mbot.forward(50)'))).toBe('drive') - expect(blockGroup(st('mbot.led(2).setColor("rot")'))).toBe('leds') - expect(blockGroup(st('mbot.pause(100)'))).toBe('wait') - expect(blockGroup(st('x = 1'))).toBeUndefined() - }) - - it('zeichnet Schleifen und Verzweigungen als Klammerblöcke mit Pfaden', () => { - const doc = docOf(main('for (int i = 0; i < 4; i++) {\n mbot.forward(50);\n if (mbot.getDistance() < 10) {\n mbot.stop();\n } else {\n mbot.turnRight(90);\n }\n}')) - const selected: number[][] = [] - const view = new BlockView({ press: (p) => selected.push(p), editValue: () => {}, labels: blockLabels }) - view.render(doc, [0, 0, 1], false) - const root = view.element - const loop = root.querySelector('.bk-loop')! - expect(loop.querySelector(':scope > .bk-head')!.textContent).toBe('wiederhole 4 mal') - const stmt = loop.querySelector('.bk-stmt')! - expect(stmt.dataset.path).toBe('[0,0,0]') - expect(stmt.dataset.botColor).toBe('fahren') - const branch = loop.querySelector('.bk-if')! - expect(branch.classList.contains('bk-sel')).toBe(true) - expect(branch.querySelector('.tk-sensor')!.textContent).toBe('mbot.getDistance()') - expect(branch.querySelector('.bk-clause')!.textContent).toBe('else') - expect(branch.querySelectorAll(':scope > .bk-body')).toHaveLength(2) - branch.querySelectorAll('.bk-stmt')[1].dispatchEvent(new Event('pointerdown', { bubbles: true })) - expect(selected).toEqual([[0, 0, 1, 1, 0]]) - - // In der Simulation: laufender Schritt hervorgehoben, Klicks wählen nichts aus - view.render(doc, [0, 0, 0], true) - expect(root.querySelector('.bk-exec')!.getAttribute('data-path')).toBe('[0,0,0]') - root.querySelector('.bk-stmt')!.dispatchEvent(new Event('pointerdown', { bubbles: true })) - expect(selected).toHaveLength(1) - }) - it('findet die Ablegestelle in Blöcken', () => { - const doc = docOf(main('mbot.forward(50);\nwhile (mbot.getDistance() > 10) {\n mbot.forward(10);\n mbot.pause(100);\n}\nmbot.stop();')) - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: blockLabels }) - view.render(doc, undefined, false) - const root = view.element - const at = (el: Element | null, y: number, query = {}) => view.dropAt(0, y, query, el) - const loop = root.querySelector('.bk-loop')! - const first = root.querySelector('[data-path="[0]"]')! - // jsdom kennt keine Maße: y < 0 ist die obere Hälfte, y = 0 die untere - expect(at(first, -1)).toEqual({ kind: 'before', path: [0] }) - expect(at(first, 0)).toEqual({ kind: 'after', path: [0] }) - // Kopf der Schleife: unten = erster Block im Maul, Fuß = hinter die Schleife, freie Fläche im Maul = hinter den letzten - expect(at(loop.querySelector(':scope > .bk-head'), 0)).toEqual({ kind: 'before', path: [1, 0, 0] }) - expect(at(loop.querySelector(':scope > .bk-head'), -1)).toEqual({ kind: 'before', path: [1] }) - expect(at(loop.querySelector(':scope > .bk-foot'), 0)).toEqual({ kind: 'after', path: [1] }) - expect(at(loop.querySelector(':scope > .bk-body'), 0)).toEqual({ kind: 'after', path: [1, 0, 1] }) - expect(at(root.querySelector('.bk-hat'), 0)).toEqual({ kind: 'before', path: [0] }) - expect(at(root.querySelector('.bk-stack'), 0)).toEqual({ kind: 'rootAppend' }) - // Verschieben: nicht in sich selbst, Papierkorb nur beim Verschieben - expect(at(loop.querySelector('[data-path="[1,0,0]"]'), 0, { moving: [1] })).toBeUndefined() - expect(at(root.querySelector('.bk-trash'), 0, { moving: [0] })).toEqual({ kind: 'trash' }) - expect(at(root.querySelector('.bk-trash'), 0)).toBeUndefined() - // Sensor: nur in passende Blöcke - expect(at(loop.querySelector('.bk-head'), 0, { sensor: (p: number[]) => p.length === 1 })).toEqual({ kind: 'condition', path: [1] }) - expect(at(loop.querySelector('[data-path="[1,0,0]"]'), 0, { sensor: (p: number[]) => p.length === 1 })).toBeUndefined() - }) - it('ändert Werte direkt im Block', () => { - // dieselbe Reihenfolge wie im gespeicherten Text, auch bei for (drei Zeilen) und Sensoren dazwischen - expect(replaceValue(['int i = 0;', 'i < 4;', 'i++'], 1, '8')).toEqual(['int i = 0;', 'i < 8;', 'i++']) - expect(replaceValue(['mbot.getDistance() < 10 && x == 2'], 1, '5')).toEqual(['mbot.getDistance() < 10 && x == 5']) - expect(replaceValue(['mbot.led(0).setColor("rot")'], 1, '"grün"')).toEqual(['mbot.led(0).setColor("grün")']) - expect(replaceValue(['x = 1'], 3, '2')).toBeUndefined() - expect(valueText('lit', ' 2,5 ')).toBe('2.5') - expect(valueText('lit', 'abc')).toBeUndefined() - expect(valueText('str', 'Hallo "du"')).toBe('"Hallo \\"du\\""') - - const doc = docOf(main('for (int i = 0; i < 4; i++) {\n mbot.forward(50);\n}')) - const edits: [number[], number, string][] = [] - const view = new BlockView({ press: () => {}, editValue: (p, i, t) => edits.push([p, i, t]), labels: blockLabels }) - document.body.append(view.element) - view.element.hidden = false - view.render(doc, undefined, false) - const four = [...view.element.querySelectorAll('.tk-lit')].find((e) => e.textContent === '4')! - four.dispatchEvent(new Event('pointerdown', { bubbles: true })) - const input = view.element.querySelector('.bk-input')! - expect(input.value).toBe('4') - // solange das Feld offen ist, wird nicht neu gezeichnet - view.render(doc, undefined, false) - expect(view.element.querySelector('.bk-input')).toBe(input) - input.value = '6' - input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' })) - expect(edits).toEqual([[[0], 1, '6']]) - view.element.remove() - }) - it('setzt Sensoren als Bausteine in Felder', () => { - expect(replaceSensor(['mbot.getDistance() < 10 && mbot.isButtonA()'], 1, 'mbot.isButtonB()')).toEqual(['mbot.getDistance() < 10 && mbot.isButtonB()']) - // andere mbot-Befehle zählen nicht als Sensor - expect(replaceSensor(['mbot.stop()'], 0, 'mbot.getTimer()')).toBeUndefined() - expect(replaceValue(['mbot.forward(50)'], 0, 'mbot.getDistance()')).toEqual(['mbot.forward(mbot.getDistance())']) - - const doc = docOf(main('while (mbot.getDistance() > 10 && !mbot.isButtonA()) {\n mbot.forward(50);\n}')) - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: blockLabels }) - view.render(doc, undefined, false) - const root = view.element - const bool = root.querySelector('.tk-sensor.tk-bool')! - expect(bool.textContent).toBe('mbot.isButtonA()') - expect(JSON.parse(bool.dataset.sensor!)).toEqual({ path: [0], index: 1 }) - const sensor = { sensor: () => true } - expect(view.dropAt(0, 0, sensor, bool)).toEqual({ kind: 'slot', path: [0], slot: 'sensor', index: 1 }) - const fifty = [...root.querySelectorAll('.tk-lit')].find((e) => e.textContent === '50')! - expect(view.dropAt(0, 0, sensor, fifty)).toEqual({ kind: 'slot', path: [0, 0, 0], slot: 'value', index: 0 }) - // neben ein Feld: wie bisher in die Bedingung - expect(view.dropAt(0, 0, sensor, root.querySelector('.bk-loop > .bk-head'))).toEqual({ kind: 'condition', path: [0] }) - view.showDrop({ kind: 'slot', path: [0], slot: 'sensor', index: 1 }) - expect(bool.classList.contains('bk-drop')).toBe(true) - }) - it('schreibt das Gruppensymbol nie in den Befehl', () => { - const doc = docOf(main('mbot.forward(50);\nwhile (true) {\n mbot.pause(100);\n}')) - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: blockLabels }) - view.render(doc, undefined, false) - view.render(doc, undefined, false) - expect(doc.elements[0].textLines).toEqual(['mbot.forward(50)']) - expect(view.element.querySelector('.bk-stmt')!.textContent).toBe('🚗 mbot.forward(50)') - // alte Dateien mit Symbol werden gesäubert - const old = { ...doc, elements: [{ ...doc.elements[0], textLines: ['🚗 mbot.forward(50)'] }, { ...doc.elements[1], loopBody: [{ ...doc.elements[1].loopBody![0], textLines: ['⏱️ mbot.pause(100)'] }] }] } - const clean = withoutBlockIcons(old)! - expect(clean.elements[0].textLines).toEqual(['mbot.forward(50)']) - expect(clean.elements[1].loopBody![0].textLines).toEqual(['mbot.pause(100)']) - expect(withoutBlockIcons(doc)).toBeUndefined() - }) - - it('Variablen und Operatoren als Bausteine (Stufe 5)', () => { - const doc = docOf(main('int zahl = 3;\nfor (int i = 0; i < 5; i++) {\n mbot.forward(i);\n}\nzahl = zahl + 1;\nmbot.pause((int) (Math.random() * 10) + 1);')) - expect(programVariables(doc)).toEqual(['zahl', 'i']) - const vars = new Set(programVariables(doc)) - // angelegte bzw. gesetzte Variable: Baustein ohne Feld; rechts davon ein Feld - expect(blockTokens('zahl = zahl + 1', vars)).toEqual([ - { kind: 'var', text: 'zahl', fixed: true }, - { kind: 'text', text: ' = ' }, - { kind: 'var', text: 'zahl' }, - { kind: 'text', text: ' + ' }, - { kind: 'lit', text: '1' }, - ]) - // Wertfelder: zahl (0), 1 (1); Variable auf ein Feld ziehen - expect(replaceValue(['zahl = zahl + 1'], 1, 'i', vars)).toEqual(['zahl = zahl + i']) - expect(replaceValue(['zahl = zahl + 1'], 0, '2 * 3', vars)).toEqual(['zahl = (2 * 3) + 1']) - expect(replaceValue(['mbot.forward(50)'], 0, '0 + 0', vars)).toEqual(['mbot.forward(0 + 0)']) - // Zufallszahl: zwei Felder von/bis - const rnd = ['mbot.pause((int) (Math.random() * 10) + 1)'] - expect(replaceValue(rnd, 1, '6')).toEqual(['mbot.pause((int) (Math.random() * 6) + 1)']) - expect(replaceValue(rnd, 0, '5')).toEqual(['mbot.pause((int) (Math.random() * 6) + 5)']) - expect(replaceValue(rnd, 0, 'zahl', vars)).toBeUndefined() - // leere Klammer: ein leeres Feld, aber nicht bei mbot.stop() - expect(replaceValue(['mbot.forward()'], 0, '50')).toEqual(['mbot.forward(50)']) - expect(replaceValue(['mbot.stop()'], 0, '50')).toBeUndefined() - - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: blockLabels }) - view.render(doc, undefined, false) - const root = view.element - // die Schleife benutzt i → „zähle mit“; i im Befehl als oranger Baustein mit Feld - expect(root.querySelector('.bk-loop > .bk-head')!.textContent).toBe('zähle mit i von 0 bis unter 5 in Schritten von 1') - const iField = root.querySelector('.bk-loop .bk-stmt .tk-var')! - expect(iField.textContent).toBe('i') - expect(iField.dataset.value).toBe(JSON.stringify({ path: [1, 0, 0], index: 0 })) - const rndPill = root.querySelector('.tk-op')! - expect(rndPill.textContent).toBe('Zufallszahl von 1 bis 10') - expect(rndPill.querySelectorAll('[data-value]').length).toBe(2) - // Zählschleife über das Feld: Variable als Ende - expect(replaceBlockValue(doc, [1], 1, 'zahl')).toEqual(['int i = 0', 'i < zahl', 'i++']) - }) -}) - - -describe('Blöcke: Schlüsselwörter übersetzt', () => { - it('zeigt wenn/sonst/solange/wiederhole immer statt if/else/while/endless', () => { - const doc = docOf(main('while (true) {\n if (mbot.getDistance() < 20) {\n mbot.turnRight();\n } else {\n mbot.forward();\n }\n}\nwhile (mbot.getDistance() > 15) {\n mbot.forward();\n}')) - const keywords = { if: 'wenn', while: 'solange', endless: 'wiederhole immer', do: 'mache' } - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: { ...blockLabels, else: 'sonst', keywords } }) - view.render(doc, undefined, false) - const words = [...view.element.querySelectorAll('.tk-kw')].map((w) => w.textContent) - expect(words).toEqual(['🏁 Start', 'wiederhole immer', 'wenn', 'sonst', 'solange']) - // Werte bleiben anklickbar wie vorher - expect(view.element.querySelectorAll('[data-value]').length).toBe(5) - }) -}) - -describe('Blöcke: nicht, und, oder', () => { - it('zeigt „nicht“, „und“, „oder“ statt !, &&, || (nicht bei !=)', () => { - const doc = docOf(main('while (!mbot.isButtonA() && mbot.getDistance() != 5 || mbot.isButtonB()) {\n mbot.pause(100);\n}')) - const keywords = { while: 'solange', '!': 'nicht', '&&': 'und', '||': 'oder' } - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: { ...blockLabels, keywords } }) - view.render(doc, undefined, false) - const head = view.element.querySelector('.bk-loop .bk-head')!.textContent! - expect(head).toBe('solange nicht mbot.isButtonA() und mbot.getDistance() != 5 oder mbot.isButtonB()') - }) - - it('zeigt den Aussprung als „verlasse die Schleife“', () => { - const doc = docOf(main('while (true) {\n if (mbot.isButtonA()) {\n break;\n }\n mbot.forward();\n}')) - const view = new BlockView({ press: () => {}, editValue: () => {}, labels: { ...blockLabels, keywords: { break: 'verlasse die Schleife' } } }) - view.render(doc, undefined, false) - expect(view.element.textContent).toContain('verlasse die Schleife') - expect(view.element.textContent).not.toContain('break') - }) -}) diff --git a/web/test/boolGame.test.ts b/web/test/boolGame.test.ts deleted file mode 100644 index 59e6936b..00000000 --- a/web/test/boolGame.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { evaluate, explain, makeRound, parseBoolGame } from '../src/learn/boolGame' - -describe('Spiel „Wahr oder falsch?“', () => { - it('wertet Vergleiche, &&, || und ! wie Java aus', () => { - expect(evaluate('alter >= 12 && alter < 18', { alter: 15 })).toBe(true) - expect(evaluate('alter >= 12 && alter < 18', { alter: 18 })).toBe(false) - expect(evaluate('tag == 6 || tag == 7', { tag: 7 })).toBe(true) - expect(evaluate('!(note >= 1 && note <= 6)', { note: 0 })).toBe(true) - expect(evaluate('a || b && c', { a: false, b: true, c: false })).toBe(false) - expect(evaluate('(alter >= 12 && alter < 18) || schueler', { alter: 30, schueler: true })).toBe(true) - expect(evaluate('!schueler', { schueler: false })).toBe(true) - }) - - it('erklärt die Auswertung in Schritten', () => { - expect(explain('alter >= 12 && alter < 18', { alter: 15 })).toEqual(['alter >= 12 && alter < 18', '15 >= 12 && 15 < 18', 'true && true', 'true']) - expect(explain('!schueler', { schueler: true })).toEqual(['!schueler', '!true', 'false']) - }) - - it('liest den Block und erzeugt Runden vom leichten zum schweren', () => { - const game = parseBoolGame(['alter: 8..20', 'schueler: boolean', '? alter >= 18', '? alter >= 12 && schueler'], 'x.md') - expect(game.vars).toHaveLength(2) - expect(makeRound(game, 0, 10, () => 0).condition).toBe('alter >= 18') - const last = makeRound(game, 9, 10, () => 0.9) - expect(last.condition).toBe('alter >= 12 && schueler') - expect(last.answer).toBe(evaluate(last.condition, last.values)) - }) - - it('meldet Fehler im Block', () => { - expect(() => parseBoolGame(['? x > 1'], 'x.md')).toThrow(/Unbekannte Variable/) - expect(() => parseBoolGame(['alter: viel'], 'x.md')).toThrow(/Unbekannte Zeile/) - }) -}) diff --git a/web/test/classModel.test.ts b/web/test/classModel.test.ts deleted file mode 100644 index 2acba0b0..00000000 --- a/web/test/classModel.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { addClass, addMember, deleteClass, deleteMember, exampleJava, parseClasses, parseUmlLine, relations, renameClass, replaceMember, setAbstract, setSuperclass, umlText } from '../src/core/classModel' - -const byName = (source: string, name: string) => parseClasses(source).find((c) => c.name === name)! - -describe('Klassendiagramm aus Java', () => { - it('liest das Beispiel Tier, Hund, Besitzer', () => { - const classes = parseClasses(exampleJava) - expect(classes.map((c) => c.name)).toEqual(['Tier', 'Hund', 'Besitzer']) - expect(classes[0].members.map(umlText)).toEqual(['- name: String', '- alter: int', '+ Tier(name: String, alter: int)', '+ getName(): String', '+ lautGeben(): void']) - expect(classes[1].superclass).toBe('Tier') - expect(relations(classes)).toEqual([ - { kind: 'extends', from: 'Hund', to: 'Tier' }, - { kind: 'association', from: 'Besitzer', to: 'Hund', role: 'hunde', multiplicity: '0..*' }, - ]) - }) - - it('kennt Interfaces, abstrakte Klassen, static und Arrays', () => { - const src = `interface Haustier { void streicheln(); } -abstract class Tier implements Haustier { - protected static int anzahl = 0; - int[] werte = {1, 2}, mehr; - @Override public void streicheln() { if (true) { } } - abstract String laut(); -} -class Zoo { private Tier[] tiere; Tier chef; }` - const [h, tier, zoo] = parseClasses(src) - expect(h.kind).toBe('interface') - expect(h.members[0]).toMatchObject({ name: 'streicheln', isAbstract: true, visibility: '+' }) - expect(tier.isAbstract).toBe(true) - expect(tier.interfaces).toEqual(['Haustier']) - expect(tier.members.map(umlText)).toEqual(['# anzahl: int', '~ werte: int[]', '~ mehr: int[]', '+ streicheln(): void', '~ laut(): String']) - expect(tier.members[0].isStatic).toBe(true) - expect(tier.members[1].shared).toBe(true) - expect(tier.members[4].isAbstract).toBe(true) - expect(relations([h, tier, zoo]).map((r) => `${r.from}-${r.kind}-${r.to}${r.multiplicity ? ' ' + r.multiplicity : ''}`)).toEqual([ - 'Tier-implements-Haustier', - 'Zoo-association-Tier 0..*', - 'Zoo-association-Tier 1', - ]) - }) - - it('übersteht halb getippten Code und Kommentare', () => { - const src = `// class Falsch {}\npublic class Auto {\n private String marke; /* } */\n public void fahren() {\n String s = "}";\n` - const [auto] = parseClasses(src) - expect(auto.name).toBe('Auto') - expect(auto.members.map((m) => m.name)).toEqual(['marke', 'fahren']) - }) -}) - -describe('UML-Zeile lesen', () => { - it('versteht UML- und Java-Schreibweise', () => { - expect(parseUmlLine('- name: String')).toMatchObject({ kind: 'field', visibility: '-', name: 'name', type: 'String' }) - expect(parseUmlLine('int alter')).toMatchObject({ kind: 'field', name: 'alter', type: 'int' }) - expect(parseUmlLine('+ bellen(laut: int, wie: String): boolean')).toMatchObject({ - kind: 'method', - name: 'bellen', - type: 'boolean', - params: [{ name: 'laut', type: 'int' }, { name: 'wie', type: 'String' }], - }) - expect(parseUmlLine('public void fressen(int menge)')).toMatchObject({ kind: 'method', visibility: '+', name: 'fressen', type: 'void', params: [{ name: 'menge', type: 'int' }] }) - expect(parseUmlLine('static zaehler: int')).toMatchObject({ isStatic: true, name: 'zaehler' }) - expect(parseUmlLine('')).toBeUndefined() - }) -}) - -describe('Java aus dem Klassendiagramm', () => { - it('legt Klassen an und schreibt Attribute und Methoden hinein', () => { - let src = addClass('', 'Hund') - expect(src).toBe('public class Hund {\n}\n') - src = addMember(src, byName(src, 'Hund'), '- name: String') - src = addMember(src, byName(src, 'Hund'), '+ bellen(): void') - src = addMember(src, byName(src, 'Hund'), '- alter: int') - src = addMember(src, byName(src, 'Hund'), '+ getAlter(): int') - expect(src).toBe(`public class Hund { - private String name; - private int alter; - - public void bellen() { - // Dein Code - } - - public int getAlter() { - return 0; - } -} -`) - src = addClass(src, 'Tier') - src = setSuperclass(src, byName(src, 'Hund'), 'Tier') - expect(src.startsWith('public class Hund extends Tier {')).toBe(true) - src = setSuperclass(src, byName(src, 'Hund'), undefined) - expect(src.startsWith('public class Hund {')).toBe(true) - src = setAbstract(src, byName(src, 'Tier'), true) - expect(src).toContain('public abstract class Tier {') - }) - - it('ändert nur den Kopf: Rumpf und Startwert bleiben', () => { - let src = exampleJava - const tier = byName(src, 'Tier') - src = replaceMember(src, tier, tier.members[1], '- alter: double') - src = replaceMember(src, byName(src, 'Tier'), byName(src, 'Tier').members[4], '+ lautGeben(wie: String): void') - expect(src).toContain(' private double alter;\n') - expect(src).toContain(' public void lautGeben(String wie) {\n System.out.println("...");') - // ohne Sichtbarkeit getippt: alte bleibt - src = replaceMember(src, byName(src, 'Tier'), byName(src, 'Tier').members[0], 'spitzname: String') - expect(src).toContain(' private String spitzname;\n') - }) - - it('benennt Klasse samt Konstruktor um und löscht Teile', () => { - let src = renameClass(exampleJava, byName(exampleJava, 'Tier'), 'Lebewesen') - expect(src).toContain('public class Lebewesen {') - expect(src).toContain('public Lebewesen(String name, int alter) {') - const getter = byName(src, 'Lebewesen').members.find((m) => m.name === 'getName')! - src = deleteMember(src, getter) - expect(src).not.toContain('getName') - src = deleteClass(src, byName(src, 'Besitzer')) - expect(parseClasses(src).map((c) => c.name)).toEqual(['Lebewesen', 'Hund']) - expect(src.endsWith('}\n')).toBe(true) - }) - - it('Interface-Methoden ohne Rumpf', () => { - let src = addClass('', 'Haustier', 'interface') - src = addMember(src, byName(src, 'Haustier'), 'streicheln(): void') - expect(src).toBe('public interface Haustier {\n void streicheln();\n}\n') - }) -}) - -describe('Modifier der Klasse', () => { - it('public und final setzen und lesen', async () => { - const { classModifiers, setFinal, setPublic, setAbstract } = await import('../src/core/classModel') - let src = 'public abstract class Tier {\n}\n' - const c = () => parseClasses(src)[0] - src = setFinal(src, c(), true) - expect(src).toBe('public final class Tier {\n}\n') - expect(classModifiers(src, c())).toEqual({ isPublic: true, isFinal: true }) - src = setPublic(src, c(), false) - expect(src).toBe('final class Tier {\n}\n') - src = setPublic(setFinal(src, c(), false), parseClasses(setFinal(src, c(), false))[0], true) - expect(src).toBe('public class Tier {\n}\n') - expect(setAbstract(src, c(), true)).toBe('public abstract class Tier {\n}\n') - }) -}) - -describe('Neue Attribute', () => { - it('ohne Sichtbarkeit private, Methoden public', () => { - let src = 'public class Hund {\n}\n' - src = addMember(src, parseClasses(src)[0], 'name: String') - src = addMember(src, parseClasses(src)[0], 'bellen(): void') - expect(src).toContain('private String name;') - expect(src).toContain('public void bellen()') - }) -}) diff --git a/web/test/codeGenerator.test.ts b/web/test/codeGenerator.test.ts deleted file mode 100644 index 33cba7e3..00000000 --- a/web/test/codeGenerator.test.ts +++ /dev/null @@ -1,355 +0,0 @@ -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { generateCode, generateJavaClassFile, generateLiveCode, generateLiveJava, sanitizedJavaClassName, type CodeTarget } from '../src/core/codeGenerator' -import { ElementType, makeElement } from '../src/core/model' -import { decodeDocument } from '../src/core/xmlCodec' - -// Port von CodeGeneratorTests.swift. - -const examples = join(__dirname, '..', '..', 'VisuStruct-Dateien') -const text = (name: string) => readFileSync(join(examples, name), 'utf8') -const example = (name: string) => decodeDocument(text(name)) - -describe('Codegenerierung', () => { - it('Java-Klasse entspricht den mitgelieferten .java-Dateien', () => { - for (const name of ['Collatz', 'Fibonacci']) { - expect(generateJavaClassFile(example(`${name}.visustruct`), name), name).toBe(text(`${name}.java`)) - } - }) - - it('Java-Klasse mit Eingabe importiert Scanner genau einmal', () => { - const generated = generateJavaClassFile(example('2026-04-23-NumberEven.visustruct'), 'NumberEven') - expect(generated.startsWith('import java.util.Scanner;\n\npublic class NumberEven {\n')).toBe(true) - expect(generated.split('new Scanner(System.in)').length - 1).toBe(1) - expect(generated).toContain('if (number % 2 == 0) {') - }) - - it('Collatz in Python und JavaScript', () => { - const doc = example('Collatz.visustruct') - const python = generateCode(doc, 'python') - expect(python).toContain('while number != 1:') - expect(python).toContain('print(number)') - expect(python).not.toContain('{') - const js = generateCode(doc, 'javascript') - expect(js.startsWith('"use strict";')).toBe(true) - expect(js).toContain('while (number != 1) {') - expect(js).toContain('console.log(number);') - // int-Variable: Ganzzahldivision wie in Java - expect(js).toContain('number = Math.trunc(number / 2);') - }) - - it('Python: Java-Schreibweise wird übersetzt', () => { - const stmt = (t: string) => makeElement(ElementType.anweisung, { textLines: [t] }) - const doc = { - caption: '', - elements: [ - stmt('int n = 7'), - stmt('boolean ok = true'), - makeElement(ElementType.forSchleife, { - textLines: ['int i = 1', 'i <= n', 'i++'], - loopBody: [stmt('n--')], - }), - makeElement(ElementType.verzweigung, { - textLines: ['n > 2 && !ok || n == 0'], - cases: [ - { name: 'true', elements: [stmt('print: "Hälfte: " + n / 2')] }, - { name: 'false', elements: [stmt('ø')] }, - ], - }), - ], - } - const python = generateCode(doc, 'python') - expect(python).toContain('ok = True') - expect(python).toContain('for i in range(1, n + 1):') - expect(python).toContain(' n -= 1') - expect(python).toContain('if n > 2 and not ok or n == 0:') - expect(python).toContain('print("Hälfte: " + str(n // 2))') - expect(python).toMatch(/else:\n {4}# empty element\n {4}pass/) - }) - - it('Verzweigung allein im false-Zweig wird zu else if bzw. elif', () => { - const stmt = (t: string) => makeElement(ElementType.anweisung, { textLines: [t] }) - const branch = (cond: string, yes: string, no: ReturnType[]) => - makeElement(ElementType.verzweigung, { textLines: [cond], cases: [{ name: 'true', elements: [stmt(yes)] }, { name: 'false', elements: no }] }) - const chain = branch('wahl == 1', 'output: "Wasser"', [branch('wahl == 2', 'output: "Saft"', [stmt('output: "Kakao"')])]) - const doc = { caption: '', elements: [stmt('int wahl = 2'), chain] } - expect(generateCode(doc, 'java')).toContain( - 'if (wahl == 1) {\n System.out.println("Wasser");\n} else if (wahl == 2) {\n System.out.println("Saft");\n} else {\n System.out.println("Kakao");\n}', - ) - expect(generateCode(doc, 'javascript')).toContain('} else if (wahl == 2) {') - expect(generateCode(doc, 'python')).toContain('if wahl == 1:\n print("Wasser")\nelif wahl == 2:\n print("Saft")\nelse:\n print("Kakao")') - // Steht noch etwas neben der inneren Verzweigung, bleibt der Block verschachtelt. - const mixed = branch('wahl == 1', 'output: "Wasser"', [stmt('output: "Moment"'), branch('wahl == 2', 'output: "Saft"', [stmt('ø')])]) - expect(generateCode({ caption: '', elements: [stmt('int wahl = 2'), mixed] }, 'java')).toContain('} else {\n System.out.println("Moment");\n if (wahl == 2) {') - }) - - it('Pseudocode bekommt einen Hinweis über dem Code', () => { - const doc = { caption: '', pseudocode: true, elements: [makeElement(ElementType.anweisung, { textLines: ['Tasse hinstellen'] })] } - expect(generateCode(doc, 'python', { pseudocodeNote: 'Pseudocode' })).toBe('# Pseudocode\n\nTasse hinstellen') - expect(generateCode(doc, 'java', { pseudocodeNote: 'Pseudocode' }).startsWith('// Pseudocode\n\n')).toBe(true) - expect(generateCode({ ...doc, pseudocode: undefined }, 'python')).toBe('Tasse hinstellen') - const withInput = { ...doc, elements: [makeElement(ElementType.anweisung, { textLines: ['input: int n'] })] } - const javaClass = generateJavaClassFile(withInput, 'Tee', { pseudocodeNote: 'Pseudocode' }) - expect(javaClass.split('new Scanner(System.in)').length - 1).toBe(1) - expect(javaClass).toContain(' // Pseudocode\n') - }) - - it('Unterprogramme mit Kopfzeile werden zu Methoden und Funktionen', () => { - const stmt = (t: string) => makeElement(ElementType.anweisung, { textLines: [t] }) - const call = (t: string) => makeElement(ElementType.aufruf, { textLines: [t] }) - const quadrat = { caption: 'int quadrat(int x)', elements: [stmt('int y = x * x'), stmt('return y')] } - const zeige = { caption: 'void zeige(String text)', elements: [stmt('input: int n "Zahl?"'), stmt('output: text + n')] } - const main = { caption: 'Main', elements: [call('int q = quadrat(3)'), call('zeige("q=")'), stmt('output: q')] } - expect(generateCode(quadrat, 'java')).toBe('public static int quadrat(int x) {\n int y = x * x;\n return y;\n}') - expect(generateCode(main, 'python', {}, [quadrat])).toBe( - 'def quadrat(x):\n y = x * x\n return y\n\nq = quadrat(3)\nzeige("q=")\nprint(q)', - ) - expect(generateCode(main, 'javascript', {}, [quadrat])).toContain('function quadrat(x) {\n let y = x * x;\n return y;\n}\n\nlet q = quadrat(3);') - const javaClass = generateJavaClassFile(main, 'Main', {}, [quadrat, zeige]) - expect(javaClass).toContain(' static Scanner scanner = new Scanner(System.in);\n') - expect(javaClass.split('new Scanner(System.in)').length - 1).toBe(1) - expect(javaClass).toContain(' public static int quadrat(int x) {\n int y = x * x;\n return y;\n }\n') - expect(javaClass).toContain(' public static void zeige(String text) {\n') - const feld = { caption: 'Feld', elements: [stmt('String[] spalten = {"A", "B"}'), stmt('output: spalten[1] + (2 + 1)')] } - expect(generateCode(feld, 'python')).toContain('print(spalten[1] + str((2 + 1)))') - const leeren = { caption: 'void leeren(boolean[] reihe, int[][] tabelle)', elements: [stmt('reihe[0] = false')] } - expect(generateCode(leeren, 'java')).toContain('public static void leeren(boolean[] reihe, int[][] tabelle) {') - expect(generateCode(leeren, 'python')).toContain('def leeren(reihe, tabelle):') - // Ohne Unterprogramme bleibt die Klasse wie bisher. - expect(generateJavaClassFile(main, 'Main')).not.toContain('public static int') - }) - - it('bereinigt Java-Klassennamen', () => { - expect(sanitizedJavaClassName('Fibonacci')).toBe('Fibonacci') - const s = sanitizedJavaClassName('2 Summe bilden!') - expect(s).not.toBe('') - expect(s).not.toContain(' ') - expect(/^\d/.test(s)).toBe(false) - }) - - it('leeres Dokument erzeugt einen Platzhalter', () => { - for (const target of ['java', 'python', 'javascript'] as CodeTarget[]) { - expect(generateCode({ caption: '', elements: [] }, target).trim(), target).not.toBe('') - } - }) - - it('Eingabe, Fallauswahl und Schleifen in allen Sprachen', () => { - const doc = example('SummeBilden/switch-case.visustruct') - expect(generateCode(doc, 'python')).toMatch(/match .*:\n/) - expect(generateCode(doc, 'javascript')).toContain('break;') - const pin = example('2026-04-23-CheckPin.visustruct') - expect(generateCode(pin, 'javascript')).toContain('prompt(') - expect(generateCode(pin, 'python')).toContain('input(') - expect(generateCode(example('SummeBilden/for-loop.visustruct'), 'python')).toMatch(/for \w+ in range\(/) - }) - - it('JavaScript schneidet int / int wie Java ab', () => { - const s = (t: string) => makeElement(ElementType.anweisung, { textLines: [t] }) - const js = generateCode( - { caption: '', elements: [s('int a = 7'), s('int b = 2'), s('int q = a / b'), s('q /= 2'), s('double e = a / 2.0'), s('print: "x/y " + a / b')] }, - 'javascript', - ) - expect(js).toContain('let q = Math.trunc(a / b);') - expect(js).toContain('q = Math.trunc(q / 2);') - expect(js).toContain('a / 2.0') - expect(js).toContain('"x/y " + Math.trunc(a / b)') - // Wie in Java von links nach rechts: (x * x) / 2 und (a / b) * 2.5 - const chains = generateCode( - { caption: '', elements: [s('int x = 3'), s('int b = 2'), s('int y = x * x / 2'), s('double d = x / b * 2.5'), s('double e = 2.5 * x / b'), s('int r = x * b % 2')] }, - 'javascript', - ) - expect(chains).toContain('let y = Math.trunc(x * x / 2);') - expect(chains).toContain('let d = Math.trunc(x / b) * 2.5;') - expect(chains).toContain('let e = 2.5 * x / b;') - expect(chains).toContain('let r = x * b % 2;') - }) -}) - -describe('Codegenerierung: 2D-Arrays', () => { - const stmt = (t: string) => makeElement(ElementType.anweisung, { textLines: [t] }) - const doc = { - caption: 'Kino', - elements: [ - stmt('boolean[][] sitze = new boolean[3][4]'), - stmt('int[][] t = {{1, 2}, {3, 4}}'), - makeElement(ElementType.forSchleife, { textLines: ['int r = 0', 'r < sitze.length', 'r++'], loopBody: [ - makeElement(ElementType.forSchleife, { textLines: ['int p = 0', 'p < sitze[r].length', 'p++'], loopBody: [stmt('sitze[r][p] = true')] }), - ] }), - ], - } - - it('übersetzt Anlegen und Längen in alle Zielsprachen', () => { - const java = generateCode(doc, 'java') - expect(java).toContain('boolean[][] sitze = new boolean[3][4];') - expect(java).toContain('r < sitze.length;') - const python = generateCode(doc, 'python') - expect(python).toContain('sitze = [[False] * 4 for _ in range(3)]') - expect(python).toContain('t = [[1, 2], [3, 4]]') - expect(python).toContain('for p in range(len(sitze[r])):') - const js = generateCode(doc, 'javascript') - expect(js).toContain('let sitze = Array.from({ length: 3 }, () => new Array(4).fill(false));') - expect(js).toContain('let t = [[1, 2], [3, 4]];') - }) -}) - -describe('Codegenerierung: String-Methoden', () => { - const doc = { - caption: 'Wort', - elements: [ - makeElement(ElementType.anweisung, { - textLines: ['output: w.length() + w.charAt(w.length() - 1) + w.substring(1, 3) + w.substring(2) + w.indexOf("a") + w.trim().toUpperCase()'], - }), - makeElement(ElementType.anweisung, { textLines: ['boolean b = w.contains("x")'] }), - ], - } - - it('übersetzt sie nach Python und JavaScript', () => { - const python = generateCode(doc, 'python') - expect(python).toContain('len(w)') - expect(python).toContain('w[len(w) - 1]') - expect(python).toContain('w[1:3]') - expect(python).toContain('w[2:]') - expect(python).toContain('w.find("a")') - expect(python).toContain('w.strip().upper()') - expect(python).toContain('("x" in w)') - const js = generateCode(doc, 'javascript') - expect(js).toContain('w.length + w.charAt(w.length - 1)') - expect(js).toContain('w.includes("x")') - }) -}) - -describe('Code live (Anfänger-Ansicht)', () => { - const labels = { condition: 'Bedingung', selector: 'Variable', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - const plain = (s: string) => s.replace(//g, '«').replace(//g, '»') - const stub = () => makeElement(ElementType.anweisung, { textLines: ['ø'] }) - - it('leere Anweisung wird zu `;`, Text bekommt ein Semikolon', () => { - const doc = { caption: '', elements: [makeElement(ElementType.anweisung), makeElement(ElementType.anweisung, { textLines: ['int zahl = 0'] })] } - expect(plain(generateLiveJava(doc, labels).text)).toBe(';\nint zahl = 0;') - }) - - it('neue Verzweigung: `if (Bedingung)` mit Platzhaltern in den Blöcken', () => { - const doc = { - caption: '', - elements: [makeElement(ElementType.verzweigung, { cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [stub()] }] })], - } - expect(plain(generateLiveJava(doc, labels).text)).toBe( - 'if («Bedingung») {\n «// Dein Code»\n} else {\n «// Dein Code»\n}', - ) - }) - - it('leere Schleifenfelder werden Platzhalter, ausgefüllte bleiben', () => { - const doc = { - caption: '', - elements: [makeElement(ElementType.forSchleife, { textLines: ['int i = 0', '', 'i++'], loopBody: [stub()] })], - } - expect(plain(generateLiveJava(doc, labels).text).split('\n')[0]).toBe('for (int i = 0; «Bedingung»; i++) {') - }) - - it('ordnet jedem Element seine Zeilen zu (auch nach Scanner-Vorspann und bei else if)', () => { - const inner = makeElement(ElementType.verzweigung, { textLines: ['a < 0'], cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [stub()] }] }) - const doc = { - caption: '', - elements: [ - makeElement(ElementType.anweisung, { textLines: ['input: int a'] }), - makeElement(ElementType.verzweigung, { textLines: ['a > 0'], cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [inner] }] }), - ], - } - const live = generateLiveJava(doc, labels) - const lines = plain(live.text).split('\n') - const linesOf = (path: number[]) => { - const s = live.spans.find((x) => x.path.join() === path.join())! - return lines.slice(s.from, s.to + 1) - } - expect(linesOf([0])).toEqual(['System.out.println("a: ");', 'int a = scanner.nextInt();']) - expect(linesOf([1])[0]).toBe('if (a > 0) {') - expect(linesOf([1, 1, 0])[0]).toBe('} else if (a < 0) {') - expect(linesOf([1, 1, 0, 0, 0])).toEqual([' «// Dein Code»']) - }) - - it('der normale Code-Generator bleibt unverändert', () => { - const doc = { caption: '', elements: [makeElement(ElementType.verzweigung, { cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [stub()] }] })] } - expect(generateCode(doc, 'java')).toBe('if (true) {\n // empty element\n} else {\n // empty element\n}') - }) -}) - -describe('Code live in JavaScript', () => { - const labels = { condition: 'Bedingung', selector: 'Variable', code: 'Dein Code', start: 'Start', step: 'Schritt', end: 'Ende' } - const plain = (s: string) => s.replace(//g, '«').replace(//g, '»') - const stub = () => makeElement(ElementType.anweisung, { textLines: ['ø'] }) - - it('let statt int, Platzhalter wie in Java', () => { - const doc = { - caption: '', - elements: [ - makeElement(ElementType.anweisung, { textLines: ['int zahl = 0'] }), - makeElement(ElementType.verzweigung, { cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [stub()] }] }), - makeElement(ElementType.forSchleife, { textLines: ['int i = 0', '', ''], loopBody: [stub()] }), - ], - } - const live = generateLiveCode(doc, 'javascript', labels) - expect(plain(live.text)).toBe( - '"use strict";\n\nlet zahl = 0;\nif («Bedingung») {\n «// Dein Code»\n} else {\n «// Dein Code»\n}\n' + - 'for (let i = 0; «Bedingung»; «Schritt») {\n «// Dein Code»\n}', - ) - expect(live.spans.find((s) => s.path.join() === '0')).toMatchObject({ from: 2, to: 2 }) - }) -}) - -describe('Code live in Python', () => { - const labels = { condition: 'Bedingung', selector: 'Variable', code: 'Dein Code', start: 'Start', step: 'Schritt', end: 'Ende' } - const plain = (s: string) => s.replace(//g, '«').replace(//g, '»') - const stub = () => makeElement(ElementType.anweisung, { textLines: ['ø'] }) - const py = (elements: ReturnType[]) => plain(generateLiveCode({ caption: '', elements }, 'python', labels).text) - - it('pass statt `;`, `if Bedingung:` und `pass # Dein Code` in leeren Blöcken', () => { - expect( - py([ - makeElement(ElementType.anweisung), - makeElement(ElementType.anweisung, { textLines: ['int zahl = 0'] }), - makeElement(ElementType.verzweigung, { cases: [{ name: 'true', elements: [stub()] }, { name: 'false', elements: [stub()] }] }), - ]), - ).toBe('pass\nzahl = 0\nif «Bedingung»:\n «pass # Dein Code»\nelse:\n «pass # Dein Code»') - }) - - it('Zählschleife: range mit Platzhaltern, solange Felder fehlen', () => { - const loop = (lines: string[]) => py([makeElement(ElementType.forSchleife, { textLines: lines, loopBody: [stub()] })]).split('\n')[0] - expect(loop(['', '', ''])).toBe('for «Variable» in range(«Start», «Ende»):') - expect(loop(['int i = 0', '', ''])).toBe('for i in range(0, «Ende»):') - expect(loop(['int i = 1', 'i <= 10', ''])).toBe('for i in range(1, 11):') - expect(loop(['int i = 0', 'i < 10', 'i++'])).toBe('for i in range(10):') - }) - - it('Do-while und Unterprogramm', () => { - const doc = { - caption: 'int doppelt(int x)', - elements: [makeElement(ElementType.doUntilSchleife, { loopBody: [stub()] })], - } - const live = generateLiveCode(doc, 'python', labels) - expect(plain(live.text)).toBe('def doppelt(x):\n while True:\n «pass # Dein Code»\n if not («Bedingung»):\n break') - expect(live.spans.find((s) => s.path.join() === '0')).toMatchObject({ from: 1, to: 4 }) - }) -}) - -describe('Codegenerierung: Zeichen und Zeichencodes', () => { - const doc = { - caption: 'Caesar', - elements: [ - makeElement(ElementType.anweisung, { textLines: ['int code = (int) wort.charAt(i)'] }), - makeElement(ElementType.anweisung, { textLines: ['int z = (int) (x / 2)'] }), - makeElement(ElementType.anweisung, { textLines: ['geheim = geheim + (char) (code + 3)'] }), - ], - } - - it('übersetzt (int) und (char) nach Python und JavaScript', () => { - const python = generateCode(doc, 'python') - expect(python).toContain('code = ord(wort[i])') - expect(python).toContain('z = int(x / 2)') - expect(python).toContain('chr(code + 3)') - const js = generateCode(doc, 'javascript') - expect(js).toContain('wort.charAt(i).charCodeAt(0)') - expect(js).toContain('Math.floor(x / 2)') - expect(js).toContain('String.fromCharCode(code + 3)') - }) -}) diff --git a/web/test/codeKeywords.test.ts b/web/test/codeKeywords.test.ts deleted file mode 100644 index 8a5592dc..00000000 --- a/web/test/codeKeywords.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { afterEach, describe, expect, it } from 'vitest' -import { preferredLanguage, setCodeKeywords, setLanguage, storedCodeKeywords, t } from '../src/ui/i18n' - -describe('Code-Keywords (unabhängig von der Sprache)', () => { - afterEach(() => localStorage.clear()) - - it('ersetzt nur die Elementnamen durch Java-Keywords', async () => { - await setLanguage('de', false) - await setCodeKeywords(true, false) - expect(t('element_verzweigung')).toBe('if') - expect(t('element_whileSchleife')).toBe('while') - expect(t('element_anweisung')).toBe('Anweisung') - expect(t('settings_language_title')).toBe('Sprache') - await setCodeKeywords(false, false) - expect(t('element_verzweigung')).toBe('Verzweigung') - }) - - it('macht aus der alten Sprache „Java (Keywords)“ Englisch mit Code-Keywords', () => { - localStorage.setItem('appLanguageCode', 'java') - expect(preferredLanguage()).toBe('en') - expect(storedCodeKeywords()).toBe(true) - }) -}) diff --git a/web/test/countLoop.test.ts b/web/test/countLoop.test.ts deleted file mode 100644 index 21ec9f8a..00000000 --- a/web/test/countLoop.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { countLoopLines, countVar, readCountLoop, setCountField } from 'visublocks' -import { elementWithForDepth, replaceBlockValue, withCountLoopDefaults } from '../src/ui/blockView' -import { ElementType, makeElement, type VSDocument } from '../src/core/model' - -describe('VisuBlocks: Zählschleife', () => { - it('liest einfache for-Köpfe', () => { - expect(readCountLoop(['int i = 0', 'i < 10', 'i++'])).toEqual({ v: 'i', start: '0', op: '<', end: '10', step: '1' }) - expect(readCountLoop(['int j = 1; j <= 5; j += 2'])).toEqual({ v: 'j', start: '1', op: '<=', end: '5', step: '2' }) - expect(readCountLoop(['int i = 10', 'i > 0', 'i--'])).toEqual({ v: 'i', start: '10', op: '>', end: '0', step: '1' }) - expect(readCountLoop(['int i = 0', 'i < mbot.getDistance()', 'i = i + 3'])?.end).toBe('mbot.getDistance()') - // leere Schleife → Standard - expect(readCountLoop(['', '', ''], 'k')).toEqual({ v: 'k', start: '0', op: '<', end: '10', step: '1' }) - }) - - it('lässt kompliziertere Köpfe als Text', () => { - expect(readCountLoop(['int i = 0', 'i < anzahl', 'i++'])?.end).toBe('anzahl') - expect(readCountLoop(['int i = 0', 'j < 10', 'i++'])).toBeUndefined() - expect(readCountLoop(['int i = 0', 'i < 10', 'i--'])).toBeUndefined() - expect(readCountLoop(['int i = 0', 'i < 10', 'i *= 2'])).toBeUndefined() - }) - - it('schreibt den Java-Kopf aus den Zahlen', () => { - const c = readCountLoop(['', '', ''])! - expect(countLoopLines(c)).toEqual(['int i = 0', 'i < 10', 'i++']) - expect(countLoopLines(setCountField(c, 2, '2')!)).toEqual(['int i = 0', 'i < 10', 'i += 2']) - expect(countLoopLines({ v: 'i', start: '10', op: '>=', end: '1', step: '3' })).toEqual(['int i = 10', 'i >= 1', 'i -= 3']) - expect(setCountField(c, 0, '2.5')).toBeUndefined() - expect(setCountField(c, 2, '0')).toBeUndefined() - expect(setCountField(c, 1, 'mbot.getTimer()')?.end).toBe('mbot.getTimer()') - expect(countVar(1)).toBe('j') - }) - - it('Start 0 im Block ergibt im Code int i = 0, auch in leeren und verschachtelten Schleifen', () => { - const inner = makeElement(ElementType.forSchleife, { textLines: ['', '', ''], loopBody: [] }) - const outer = makeElement(ElementType.forSchleife, { textLines: ['', '', ''], loopBody: [inner] }) - const doc = { elements: [outer] } as unknown as VSDocument - expect(elementWithForDepth(doc, [0, 0, 0]).depth).toBe(1) - expect(replaceBlockValue(doc, [0], 0, '0')).toEqual(['int i = 0', 'i < 10', 'i++']) - expect(replaceBlockValue(doc, [0, 0, 0], 1, '5')).toEqual(['int j = 0', 'j < 5', 'j++']) - expect(replaceBlockValue(doc, [0], 0, '"Hallo"')).toBeUndefined() - // neue Schleife aus „Elemente“ im Rumpf der äußeren: gleich mit j - const fresh = makeElement(ElementType.forSchleife, { textLines: ['', '', ''], loopBody: [] }) - expect(withCountLoopDefaults(doc, [0, 0, 1], fresh).textLines).toEqual(['int j = 0', 'j < 10', 'j++']) - expect(withCountLoopDefaults(doc, [1], fresh).textLines).toEqual(['int i = 0', 'i < 10', 'i++']) - }) -}) diff --git a/web/test/driveGame.test.ts b/web/test/driveGame.test.ts deleted file mode 100644 index 55b4f593..00000000 --- a/web/test/driveGame.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { afterAll, describe, expect, it } from 'vitest' -import { lessons, useContentLanguage, useVisuBotContent } from '../src/learn/content' -import { driveRound, parseDriveGame } from '../src/learn/driveGame' - -describe('Spiel „Wo landet der mBot2?“', () => { - it('liest Runden und meldet Fehler im Format', () => { - const game = parseDriveGame(['titel: t', 'frage: Wo?', 'runde:', ' mbot.forward(60);', 'karte: ... | ... | .^.', 'ziel: 1,0', '> Zwei Felder.'], 't.md') - expect(game.field).toBe(30) - expect(game.rounds[0]).toMatchObject({ code: 'mbot.forward(60);', map: ['...', '...', '.^.'], target: { col: 1, row: 0 }, question: 'Wo?', explanation: 'Zwei Felder.' }) - expect(() => parseDriveGame(['titel: t', 'frage: Wo?', 'runde:', ' mbot.forward();', 'karte: ... | ...', 'ziel: 1,0'], 't.md')).toThrow(/Start/) - expect(() => parseDriveGame(['titel: t', 'frage: Wo?', 'runde:', ' mbot.forward();', 'karte: .^.', 'ziel: 5,0'], 't.md')).toThrow(/außerhalb/) - expect(() => parseDriveGame(['titel: t', 'feld: 25', 'runde:'], 't.md')).toThrow(/feld/) - }) - - it('rechnet die Fahrt mit der Simulation, auch mit Wänden', () => { - const game = parseDriveGame( - ['titel: t', 'frage: Wo?', 'runde:', ' while (mbot.getDistance() > 15) {', ' mbot.forward(30);', ' }', 'karte: ### | ... | ... | .^.', 'ziel: 1,1'], - 't.md', - ) - const result = driveRound(game.rounds[0], game.field) - expect(result.cell).toEqual({ col: 1, row: 1 }) - expect(result.run.bumps).toBe(0) - }) - - describe.each(['de', 'en'] as const)('in den VisuBot-Lektionen (%s)', (lang) => { - afterAll(() => useVisuBotContent('off')) - it('jede Runde endet ohne Anstoßen mitten auf dem angegebenen Feld', () => { - useVisuBotContent('on') - useContentLanguage(lang) - let checked = 0 - for (const lesson of lessons) { - for (const block of lesson.steps.flatMap((s) => s.blocks)) { - if (block.kind !== 'driveGame') continue - for (const [i, round] of block.game.rounds.entries()) { - const where = `${lesson.id}, Runde ${i + 1}` - const result = driveRound(round, block.game.field) - expect(result.problems, where).toEqual([]) - expect(result.run.stop, where).toBe('end') - expect(result.run.bumps, where).toBe(0) - expect(result.cell, where).toEqual(round.target) - const F = block.game.field - const cx = -(round.map[0].length * F) / 2 + (round.target.col + 0.5) * F - const cy = (round.map.length * F) / 2 - (round.target.row + 0.5) * F - expect(Math.hypot(result.end.x - cx, result.end.y - cy), `${where}: nicht mitten im Feld`).toBeLessThan(1) - checked++ - } - } - } - expect(checked).toBeGreaterThanOrEqual(20) - }) - }) -}) diff --git a/web/test/editing.test.ts b/web/test/editing.test.ts deleted file mode 100644 index 622d9153..00000000 --- a/web/test/editing.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { - deleteAtPath, - elementAt, - insertAfterSibling, - moveAfterSibling, - moveReplacingPlaceholder, - newEmptyDocument, - replaceAtPath, -} from '../src/core/editing' -import { LayoutEngine, diagramDisplayMetrics, type LayoutNode } from '../src/core/layout' -import { ElementType } from '../src/core/model' -import { paletteElement } from '../src/core/palette' -import { decodeDocument, encodeDocument } from '../src/core/xmlCodec' -import { displayElement, flattenNodes } from '../src/ui/renderer' - -const examples = join(__dirname, '..', '..', 'VisuStruct-Dateien') - -describe('Bearbeiten', () => { - it('ersetzt den Platzhalter und fügt dahinter ein', () => { - let doc = newEmptyDocument() - doc = replaceAtPath(doc, [0], paletteElement('verzweigung')) - doc = insertAfterSibling(doc, paletteElement('anweisung'), [0]) - expect(doc.elements.map((e) => e.type)).toEqual([ElementType.verzweigung, ElementType.anweisung]) - // Platzhalter im true-Zweig durch eine Schleife ersetzen - doc = replaceAtPath(doc, [0, 0, 0], paletteElement('whileSchleife')) - expect(elementAt(doc, [0, 0, 0]).type).toBe(ElementType.whileSchleife) - expect(elementAt(doc, [0, 0, 0, 0, 0]).textLines).toEqual(['ø']) - }) - - it('verschiebt Elemente und setzt leere Zweige wieder auf ø', () => { - let doc = newEmptyDocument() - doc = replaceAtPath(doc, [0], paletteElement('verzweigung')) - doc = insertAfterSibling(doc, { ...paletteElement('anweisung'), textLines: ['x = 1'] }, [0]) - doc = moveReplacingPlaceholder(doc, [1], [0, 1, 0]) - expect(doc.elements.length).toBe(1) - expect(elementAt(doc, [0, 1, 0]).textLines).toEqual(['x = 1']) - doc = moveAfterSibling(doc, [0, 1, 0], [0]) - expect(elementAt(doc, [0, 1, 0]).textLines).toEqual(['ø']) - expect(elementAt(doc, [1]).textLines).toEqual(['x = 1']) - doc = deleteAtPath(doc, [1]) - expect(doc.elements.length).toBe(1) - }) - - it('bearbeitete Dateien bleiben im Swift-Format lesbar', () => { - const doc = decodeDocument(readFileSync(join(examples, 'Collatz.visustruct'), 'utf8')) - const edited = insertAfterSibling(doc, paletteElement('fallauswahl'), [0]) - expect(decodeDocument(encodeDocument(edited))).toEqual(edited) - }) -}) - -describe('Layout', () => { - it('Collatz: Kinder liegen innerhalb ihrer Eltern, Geschwister gleich breit', () => { - const doc = decodeDocument(readFileSync(join(examples, 'Collatz.visustruct'), 'utf8')) - const engine = new LayoutEngine(diagramDisplayMetrics(true)) - const layout = engine.layout({ ...doc, elements: doc.elements.map(displayElement) }) - const check = (n: LayoutNode) => { - for (const c of n.children) { - expect(c.frame.x).toBeGreaterThanOrEqual(n.frame.x - 1e-6) - expect(c.frame.x + c.frame.width).toBeLessThanOrEqual(n.frame.x + n.frame.width + 1e-6) - expect(c.frame.y + c.frame.height).toBeLessThanOrEqual(n.frame.y + n.frame.height + 1e-6) - check(c) - } - } - layout.elements.forEach(check) - expect(new Set(layout.elements.map((n) => n.frame.width)).size).toBe(1) - for (const n of flattenNodes(layout.elements)) expect(Number.isFinite(n.frame.height)).toBe(true) - }) -}) diff --git a/web/test/editingParity.test.ts b/web/test/editingParity.test.ts deleted file mode 100644 index 061b0fd2..00000000 --- a/web/test/editingParity.test.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - appendCaseToFallauswahl, - caseIndexInMultiway, - elementAt, - multiwayAncestorPath, - newEmptyDocument, - removeCaseFromFallauswahl, - sanitizedSelection, - setFallauswahlCaseName, -} from '../src/core/editing' -import * as L from '../src/core/lineEditorText' -import { ElementType, makeElement, type VSDocument } from '../src/core/model' - -// Port von MultiwayPathTests.swift und LineEditorTextTests.swift, dazu Fälle hinzufügen/entfernen/umbenennen. - -function multiwayDocument(): VSDocument { - const statement = makeElement(ElementType.anweisung, { textLines: ['x = 1'] }) - const inner = makeElement(ElementType.fallauswahl, { - textLines: ['y'], - cases: [ - { name: '1', elements: [statement] }, - { name: 'default', elements: [statement] }, - ], - }) - const outer = makeElement(ElementType.fallauswahl, { - textLines: ['x'], - cases: [ - { name: '1', elements: [statement] }, - { name: 'default', elements: [inner] }, - ], - }) - return { ...newEmptyDocument(), elements: [statement, outer] } -} - -describe('Fallauswahl', () => { - it('findet die innerste Fallauswahl', () => { - const doc = multiwayDocument() - expect(multiwayAncestorPath(undefined, doc)).toBeUndefined() - expect(multiwayAncestorPath([0], doc)).toBeUndefined() - expect(multiwayAncestorPath([1], doc)).toEqual([1]) - expect(multiwayAncestorPath([1, 0, 0], doc)).toEqual([1]) - expect(multiwayAncestorPath([1, 1, 0], doc)).toEqual([1, 1, 0]) - expect(multiwayAncestorPath([1, 1, 0, 1, 0], doc)).toEqual([1, 1, 0]) - }) - - it('Spaltenindex', () => { - expect(caseIndexInMultiway([1], [1])).toBeUndefined() - expect(caseIndexInMultiway(undefined, [1])).toBeUndefined() - expect(caseIndexInMultiway([1, 1, 0], [1])).toBe(1) - }) - - it('fügt Fälle vor default ein, entfernt und benennt sie', () => { - let doc = multiwayDocument() - const added = appendCaseToFallauswahl(doc, [1]) - doc = added.doc - expect(elementAt(doc, [1]).cases!.map((c) => c.name)).toEqual(['1', '2', 'default']) - expect(added.path).toEqual([1, 1, 0]) - expect(elementAt(doc, added.path).textLines).toEqual(['ø']) - doc = setFallauswahlCaseName(doc, [1], 1, ' zwei ') - expect(elementAt(doc, [1]).cases![1].name).toBe('zwei') - expect(() => setFallauswahlCaseName(doc, [1], 1, ' ')).toThrow() - const removed = removeCaseFromFallauswahl(doc, [1], 2) - expect(elementAt(removed.doc, [1]).cases!.map((c) => c.name)).toEqual(['1', 'zwei']) - expect(removed.path).toEqual([1, 1, 0]) - expect(() => removeCaseFromFallauswahl(removed.doc, [1], 0)).toThrow() - }) - - it('bereinigt die Auswahl', () => { - const doc = multiwayDocument() - expect(sanitizedSelection(undefined, doc)).toEqual([0]) - expect(sanitizedSelection([9], doc)).toEqual([0]) - expect(sanitizedSelection([1, 0, 0], doc)).toEqual([1, 0, 0]) - expect(sanitizedSelection([0], { caption: '', elements: [] })).toBeUndefined() - }) -}) - -describe('Zeileneditor', () => { - it('erkennt die Art', () => { - expect(L.ioMode('input: int age')).toBe('input') - expect(L.ioMode(' OUTPUT: x')).toBe('output') - expect(L.ioMode('print: x')).toBe('output') - expect(L.ioMode('x = 1')).toBe('statement') - }) - - it('tauscht das Präfix und behält den Inhalt', () => { - expect(L.applyingIOMode('input', 'print: age')).toBe('input: age') - expect(L.applyingIOMode('output', 'age')).toBe('output: age') - expect(L.applyingIOMode('statement', 'input: age ')).toBe('age') - expect(L.applyingIOMode('input', '')).toBe('input: ') - }) - - it('behält beim Tippen Leerzeichen', () => { - expect(L.strippingPrefixForEditing('input:', 'input: age ')).toBe('age ') - expect(L.strippingPrefixForEditing('if', 'if (a > 3)')).toBe('a > 3') - expect(L.strippingPrefixForEditing('if', 'if a > 3 ')).toBe('a > 3 ') - expect(L.strippingPrefixForEditing('while', 'until x == 1')).toBe('x == 1') - expect(L.strippingPrefixForEditing('if', 'iffy')).toBe('iffy') - }) - - it('setzt das Präfix', () => { - expect(L.prefixingForEditing('while', '')).toBe('while ') - expect(L.prefixingForEditing('while', 'i < 3')).toBe('while i < 3') - }) - - it('normalisiert Anführungszeichen', () => { - expect(L.normalizingQuotes('print: „Hallo“ ‚x’')).toBe('print: "Hallo" \'x\'') - }) - - it('Felder der Zählschleife', () => { - expect(L.paddedForLoopParts([])).toEqual(['', '', '']) - expect(L.paddedForLoopParts(['a', 'b', 'c', 'd'])).toEqual(['a', 'b', 'c']) - expect(L.normalizedForLoopPart('int i = 0;; ')).toBe('int i = 0') - }) -}) diff --git a/web/test/exporter.test.ts b/web/test/exporter.test.ts deleted file mode 100644 index 1bbfd73d..00000000 --- a/web/test/exporter.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -// @vitest-environment node -import { describe, expect, it } from 'vitest' -import { a4PageAndImageRect, buildDiagramPDF, exportFilenameSlug } from '../src/ui/exporter' - -describe('Export (DiagramSnapshotAndExport.swift)', () => { - it('bildet Dateinamen wie sanitizedExportFilenameSlug', () => { - expect(exportFilenameSlug(' Summe bilden / Teil 2 ')).toBe('Summe-bilden-Teil-2') - expect(exportFilenameSlug('Collatz.visustruct')).toBe('Collatz') - expect(exportFilenameSlug('a: b?')).toBe('a-b') - expect(exportFilenameSlug(' ')).toBe('') - }) - - it('wählt Hoch- oder Querformat wie A4SinglePageLayout', () => { - const tall = a4PageAndImageRect(300, 800) - expect(tall.page).toEqual({ width: 595, height: 842 }) - const wide = a4PageAndImageRect(1200, 400) - expect(wide.page).toEqual({ width: 842, height: 595 }) - // Zentriert innerhalb von 32 pt Rand, Seitenverhältnis bleibt. - expect(wide.draw.x).toBeGreaterThanOrEqual(32) - expect(wide.draw.width / wide.draw.height).toBeCloseTo(3, 5) - expect(wide.draw.x * 2 + wide.draw.width).toBeCloseTo(842, 5) - }) - - it('schreibt ein gültiges einseitiges PDF', async () => { - const rgb = new Uint8Array(4 * 2 * 3).fill(200) - const blob = await buildDiagramPDF(rgb, 4, 2, { width: 400, height: 200 }, 'Prüfung') - const bytes = new Uint8Array(await blob.arrayBuffer()) - const text = new TextDecoder('latin1').decode(bytes) - expect(text.startsWith('%PDF-1.4')).toBe(true) - expect(text.trimEnd().endsWith('%%EOF')).toBe(true) - expect(text).toContain('/MediaBox [0 0 842 595]') - // Jeder xref-Eintrag zeigt auf den Anfang seines Objekts. - const xrefAt = Number(/startxref\n(\d+)/.exec(text)![1]) - expect(text.slice(xrefAt, xrefAt + 4)).toBe('xref') - const entries = text.slice(xrefAt).split('\n').slice(3, 9) - entries.forEach((line, i) => { - const off = Number(line.slice(0, 10)) - expect(text.slice(off, off + `${i + 1} 0 obj`.length)).toBe(`${i + 1} 0 obj`) - }) - // Titel als UTF-16 („ü“ = 00FC). - expect(text).toContain('/Title s.length * 7 - -function sample() { - let doc = emptyFlow('Test') - const ids: string[] = [] - for (const [shape, y] of [['start', 60], ['verzweigung', 200], ['anweisung', 340], ['ende', 460]] as const) { - const r = addNode(doc, shape, 240, y, shape) - doc = r.doc - ids.push(r.id) - } - return { doc, ids } -} - -describe('VisuFlow (PAP)', () => { - it('verbindet Ausgänge mit Eingängen, ein Pfeil pro Ausgang', () => { - const { doc, ids } = sample() - let d = connect(doc, ids[0], 'out', ids[1]) - d = connect(d, ids[1], 'ja', ids[2]) - d = connect(d, ids[1], 'ja', ids[3]) - expect(d.edges.filter((e) => e.from === ids[1])).toEqual([expect.objectContaining({ port: 'ja', to: ids[3] })]) - expect(freePort(d, d.nodes[1])).toBe('nein') - // Start hat keinen Eingang, Ende keinen Ausgang, kein Pfeil auf sich selbst. - expect(connect(d, ids[2], 'out', ids[0])).toBe(d) - expect(connect(d, ids[3], 'out', ids[2])).toBe(d) - expect(connect(d, ids[2], 'out', ids[2])).toBe(d) - }) - - it('Löschen nimmt die Pfeile mit, Datei geht hin und zurück', () => { - const { doc, ids } = sample() - let d = connect(connect(doc, ids[0], 'out', ids[1]), ids[1], 'nein', ids[2]) - d = swapYesNo(d, ids[1]) - const back = flowFromJSON(flowToJSON(d))! - expect(back.title).toBe('Test') - expect(back.nodes).toEqual(d.nodes) - expect(back.edges.map((e) => [e.from, e.port, e.to])).toEqual(d.edges.map((e) => [e.from, e.port, e.to])) - expect(portSide(back, back.nodes[1], 'ja')).toBe('right') - expect(removeNode(d, ids[1]).edges).toEqual([]) - expect(flowFromJSON('kein json')).toBeUndefined() - }) - - it('Pfeil zurück nach oben läuft rechts an beiden Symbolen vorbei', () => { - const { doc } = sample() - const top = flowBox(doc.nodes[1], measure) - const low = flowBox(doc.nodes[2], measure) - const down = routeEdge(top, 'bottom', low) - expect(down).toEqual([[240, top.y + top.h / 2], [240, low.y - low.h / 2]]) - const up = routeEdge(low, 'bottom', top) - const right = Math.max(top.x + top.w / 2, low.x + low.w / 2) - expect(up.some(([x]) => x > right)).toBe(true) - expect(up.at(-1)).toEqual([240, top.y - top.h / 2]) - }) -}) diff --git a/web/test/glossary.test.ts b/web/test/glossary.test.ts deleted file mode 100644 index cf09f948..00000000 --- a/web/test/glossary.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -// @vitest-environment jsdom -import { describe, expect, it } from 'vitest' -import { contentLanguages, glossary, lessons, useContentLanguage } from '../src/learn/content' -import { markdownToHTML } from '../src/learn/markdown' -import { parseGlossary } from '../src/learn/glossary' -import { markPointers, markTerms, uiTargets } from '../src/ui/termTips' - -const entries = parseGlossary(`Vorspann wird ignoriert. - -### Variable -auch: Variablen -Eine beschriftete Kiste. - -### Schleife -auch: Schleifen -Wiederholt Schritte. -`) - -function step(html: string): HTMLElement { - const root = document.createElement('div') - root.innerHTML = html - markTerms(root, entries) - return root -} - -describe('Fachbegriffe', () => { - it('liest Begriffe, Formen und Erklärung', () => { - expect(entries).toEqual([ - { term: 'Variable', forms: ['Variable', 'Variablen'], text: 'Eine beschriftete Kiste.' }, - { term: 'Schleife', forms: ['Schleife', 'Schleifen'], text: 'Wiederholt Schritte.' }, - ]) - }) - - it.each(contentLanguages)('jeder Begriff in begriffe.md hat eine Erklärung (%s)', (lang) => { - useContentLanguage(lang) - expect(glossary.length).toBeGreaterThan(10) - for (const e of glossary) expect(e.text, e.term).not.toBe('') - }) - - it('markiert nur das erste Vorkommen je Schritt', () => { - const root = step('

    Zwei Variablen und noch eine Variable.

    Variable

    ') - const marks = [...root.querySelectorAll('.term')] - expect(marks.map((m) => m.textContent)).toEqual(['Variablen']) - expect((marks[0] as HTMLElement).dataset.text).toBe('Eine beschriftete Kiste.') - }) - - it('lässt fett erklärte Begriffe, Code und Zusammensetzungen aus', () => { - const root = step('

    Eine Variable ist … `x` ist eine Variable. Schleife While-Schleife Schleifenzähler

    ') - expect(root.querySelectorAll('.term').length).toBe(0) - }) - - it('markiert nur in Lektionstexten', () => { - const root = step('

    Variable

    Eine Schleife.

    ') - expect([...root.querySelectorAll('.term')].map((m) => m.textContent)).toEqual(['Schleife']) - }) -}) - -describe('Zeiger auf den Editor', () => { - it.each(contentLanguages)('jeder Zeiger in einer Lektion nennt einen bekannten Bereich (%s)', (lang) => { - useContentLanguage(lang) - for (const l of lessons) - for (const s of l.steps) - for (const b of s.blocks) - if (b.kind === 'markdown') - for (const m of b.text.matchAll(/\(#zeige-([^)]*)\)/g)) expect(Object.keys(uiTargets), `${l.id} / ${s.title}`).toContain(m[1]) - }) - - it('macht aus dem Link einen Zeiger und lässt ihn bei den Fachbegriffen aus', () => { - const root = document.createElement('div') - root.innerHTML = `
    ${markdownToHTML('Links die [Elemente](#zeige-elemente) und eine Variable.')}
    ` - markPointers(root) - markTerms(root, entries) - expect(root.querySelector('a')).toBeNull() - expect(root.querySelector('.ui-pointer')?.dataset.target).toBe('elemente') - expect([...root.querySelectorAll('.term')].map((m) => m.textContent)).toEqual(['Variable']) - }) -}) - -describe('Tabellen im Lektionstext', () => { - it('wird zu einer HTML-Tabelle', () => { - const html = markdownToHTML('| Schritt | i |\n|---|---|\n| `i = 1` | 1 |\n| `i++` | 2 |') - expect(html).toContain('Schritt') - expect(html).toContain('i = 11') - expect(html.match(//g)?.length).toBe(3) - }) -}) diff --git a/web/test/i18n.test.ts b/web/test/i18n.test.ts deleted file mode 100644 index ff0d7f5c..00000000 --- a/web/test/i18n.test.ts +++ /dev/null @@ -1,42 +0,0 @@ -// @vitest-environment node -import { readdirSync, readFileSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { languages, parseStrings } from '../src/ui/i18n' - -const appDir = join(__dirname, '../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct') -const webDir = join(__dirname, '../src/i18n') -const read = (p: string) => parseStrings(readFileSync(p, 'utf8')) -const placeholders = (s: string) => (s.match(/%(\d+\$)?[@d]/g) ?? []).sort().join() - -describe('Sprachen (AppLanguage.swift)', () => { - const english = new Map([...read(join(appDir, 'en.lproj/Localizable.strings')), ...read(join(webDir, 'en.strings'))]) - const webEnglish = read(join(webDir, 'en.strings')) - - it('jeder Schlüssel im Code hat einen englischen Text', () => { - const srcDir = join(__dirname, '../src/ui') - const used = new Set() - for (const f of readdirSync(srcDir).filter((f) => f.endsWith('.ts'))) { - for (const m of readFileSync(join(srcDir, f), 'utf8').matchAll(/\bt\(\s*'([a-z0-9_]+)'/g)) used.add(m[1]) - } - expect(used.size).toBeGreaterThan(50) - expect([...used].filter((k) => !english.has(k))).toEqual([]) - }) - - it('jede Sprache hat alle Web-Texte mit denselben Platzhaltern', () => { - for (const { code } of languages) { - if (code === 'java') continue - const web = read(join(webDir, `${code}.strings`)) - // Lernbereich, VisuBot, VisuObject und VisuFlow gibt es auf Deutsch, Englisch und Portugiesisch; sonst Englisch. - const required = [...webEnglish.keys()].filter((k) => !k.startsWith('web_learn_') && !k.startsWith('web_visubot_') && !k.startsWith('web_visuobject_') && !k.startsWith('web_visuflow_')) - expect(required.filter((k) => !web.has(k)), code).toEqual([]) - for (const [k, v] of web) expect(placeholders(v), `${code} ${k}`).toBe(placeholders(webEnglish.get(k)!)) - } - }) - - it('liest Escapes wie Apple', () => { - const m = parseStrings('/* x */\n"a" = "Zeile\\nzwei \\"q\\"";\n// y\n"b"="c";') - expect(m.get('a')).toBe('Zeile\nzwei "q"') - expect(m.get('b')).toBe('c') - }) -}) diff --git a/web/test/inlineEdit.test.ts b/web/test/inlineEdit.test.ts deleted file mode 100644 index 5dd235f9..00000000 --- a/web/test/inlineEdit.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { caseBandHeight, caseLabelAt, caseLabelRect, textRect } from '../src/ui/inlineEdit' -import type { LayoutNode } from '../src/core/layout' -import { ElementType, makeElement } from '../src/core/model' - -const node = (type: ElementType, extra: Partial = {}): LayoutNode => ({ - idPath: [0], - element: makeElement(type), - frame: { x: 10, y: 20, width: 300, height: 120 }, - headerBandHeight: 60, - topBandHeight: 0, - loopBodyLeadingInset: 20, - caseColumnFrames: [], - children: [], - ...extra, -}) - -describe('Text direkt im Struktogramm bearbeiten', () => { - it('Anweisung: ganzes Feld', () => { - expect(textRect(node(ElementType.anweisung), 20)).toEqual({ x: 10, y: 20, width: 300, height: 120 }) - }) - - it('Schleife: nur der Kopf über dem Rumpf', () => { - const body = node(ElementType.anweisung, { frame: { x: 30, y: 60, width: 280, height: 80 } }) - expect(textRect(node(ElementType.whileSchleife, { children: [body] }), 20)).toEqual({ x: 10, y: 20, width: 300, height: 40 }) - }) - - it('Do-while: der Fuß', () => { - expect(textRect(node(ElementType.doUntilSchleife, { headerBandHeight: 30 }), 20)).toEqual({ x: 10, y: 110, width: 300, height: 30 }) - }) - - it('Fallauswahl: Fallname unter dem Dach erkennen', () => { - const cols = [ - { x: 10, y: 80, width: 100, height: 60 }, - { x: 110, y: 80, width: 100, height: 60 }, - { x: 210, y: 80, width: 100, height: 60 }, - ] - const fa = node(ElementType.fallauswahl, { caseColumnFrames: cols }) - const band = caseBandHeight(20) - expect(caseLabelAt(fa, 150, 80 - 5, 20)).toBe(1) - expect(caseLabelAt(fa, 150, 80 - band - 5, 20)).toBeUndefined() - expect(caseLabelAt(node(ElementType.verzweigung, { caseColumnFrames: cols }), 150, 75, 20)).toBeUndefined() - expect(caseLabelRect(fa, 2, 20)).toEqual({ x: 210, y: 80 - band, width: 100, height: band }) - }) -}) diff --git a/web/test/javaEditor.test.ts b/web/test/javaEditor.test.ts deleted file mode 100644 index 5df79b76..00000000 --- a/web/test/javaEditor.test.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { completions, JavaEditor } from '../src/ui/javaEditor' -import { parseLiveJava } from '../src/core/javaParser' -import type { LiveLabels } from '../src/core/codeGenerator' - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } -const hooks = { onChange() {}, onCaret() {}, onUndo() {}, words: () => ({}) as never, breakpointLine: (line: number) => line, onBreakpoints() {} } - -function editorWith(text: string) { - const editor = new JavaEditor(document.createElement('div'), hooks, 'Code') - editor.setText(text) - editor.setMarks({ placeholders: parseLiveJava(text, labels).placeholders, problems: [] }, false) - return editor -} - -describe('Code-Editor: Platzhalter', () => { - it('Einfügen hinter „// Dein Code“ ersetzt den Platzhalter', () => { - const editor = editorWith('// Dein Code') - const end = editor.text.length - editor.view.dispatch({ changes: { from: end, insert: 'import javax.swing.*;\n' }, userEvent: 'input.paste' }) - expect(editor.text).toBe('import javax.swing.*;\n') - editor.view.destroy() - }) - - it('Einfügen an anderer Stelle lässt den Text stehen', () => { - const editor = editorWith('int x = 1;') - editor.view.dispatch({ changes: { from: 0, insert: '// a\n' }, userEvent: 'input.paste' }) - expect(editor.text).toBe('// a\nint x = 1;') - editor.view.destroy() - }) -}) - -describe('Code-Editor: Kürzel', () => { - it('syso, sout und main stehen in den Vorschlägen', () => { - const words = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: '', step: '', branch: '', branchElse: '', switchCase: '', forLoop: '', whileLoop: '', doLoop: '', output: 'Ausgabe', input: 'Eingabe' } - const labels = completions(words).map((c) => c.label) - expect(labels).toEqual(expect.arrayContaining(['syso', 'sysout', 'sout', 'main', 'psvm'])) - }) - - it('Schlüsselwörter für Klassen und Methoden stehen in den Vorschlägen', () => { - const words = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: '', step: '', branch: '', branchElse: '', switchCase: '', forLoop: '', whileLoop: '', doLoop: '', output: 'Ausgabe', input: 'Eingabe' } - const labels = completions(words).map((c) => c.label) - expect(labels).toEqual(expect.arrayContaining(['public', 'private', 'static', 'void', 'class', 'public class', 'try … catch', 'JButton'])) - expect(new Set(labels).size).toBe(labels.length) - }) - - it('mbot nur in VisuBot', () => { - const words = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: '', step: '', branch: '', branchElse: '', switchCase: '', forLoop: '', whileLoop: '', doLoop: '', output: 'Ausgabe', input: 'Eingabe' } - expect(completions(words).map((c) => c.label)).not.toContain('mbot') - expect(completions({ ...words, robot: true }).map((c) => c.label)).toContain('mbot') - }) -}) - -describe('Code-Editor: Einrücken', () => { - it('rückt den ganzen Code ein, ohne Markierung', () => { - const editor = editorWith('if (x > 0) {\nx--;\n }') - editor.indent() - expect(editor.text).toBe('if (x > 0) {\n x--;\n}') - editor.view.destroy() - }) - - it('formatiert dabei: } auf eigener Zeile, Leerzeichen um >=', () => { - const editor = editorWith('if (punkte>=50) {\n a();} else {\n b();}') - editor.indent() - expect(editor.text).toBe('if (punkte >= 50) {\n a();\n} else {\n b();\n}') - editor.view.destroy() - }) -}) - -describe('Code-Editor: Scanner-Zeile', () => { - const words = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: '', step: '', branch: '', branchElse: '', switchCase: '', forLoop: '', whileLoop: '', doLoop: '', output: 'Ausgabe', input: 'Eingabe' } - const scannerLine = () => completions(words).find((c) => c.label === 'Scanner scanner = new Scanner(System.in);')! - const accept = (text: string) => { - const editor = editorWith(text) - const from = editor.text.indexOf('Scanner') - const apply = scannerLine().apply as (view: unknown, c: unknown, from: number, to: number) => void - apply(editor.view, scannerLine(), from, from + 'Scanner'.length) - const result = editor.text - editor.view.destroy() - return result - } - - it('ergänzt die Zeile und den fehlenden Import', () => { - expect(accept('public class Main {\n public static void main(String[] args) {\n Scanner\n }\n}')).toBe( - 'import java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n Scanner scanner = new Scanner(System.in);\n }\n}', - ) - }) - - it('lässt einen vorhandenen Import stehen', () => { - expect(accept('import java.util.*;\n\nScanner')).toBe('import java.util.*;\n\nScanner scanner = new Scanner(System.in);') - }) - - it('setzt den Import zu den anderen Imports', () => { - expect(accept('import javax.swing.*;\n\nScanner')).toBe('import java.util.Scanner;\nimport javax.swing.*;\n\nScanner scanner = new Scanner(System.in);') - }) - - it('der erzeugte Code hat keinen Fehler', () => { - const code = accept('public class Main {\n public static void main(String[] args) {\n Scanner\n int zahl = scanner.nextInt();\n }\n}') - expect(parseLiveJava(code, labels).problems).toEqual([]) - }) -}) diff --git a/web/test/javaFormat.test.ts b/web/test/javaFormat.test.ts deleted file mode 100644 index 97d543bc..00000000 --- a/web/test/javaFormat.test.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { formatJava } from '../src/core/javaFormat' - -describe('Formatieren', () => { - it('schließende Klammern auf eigener Zeile, Leerzeichen um Operatoren (Beispiel von Holger)', () => { - const code = `import java.util.Scanner; -Scanner scanner = new Scanner(System.in); -int punkte = scanner.nextInt(); -if (punkte>=50) { - System.out.println("Bestanden");} else { - System.out.println("Nicht bestanden");}` - expect(formatJava(code)).toBe(`import java.util.Scanner; -Scanner scanner = new Scanner(System.in); -int punkte = scanner.nextInt(); -if (punkte >= 50) { - System.out.println("Bestanden"); -} else { - System.out.println("Nicht bestanden"); -}`) - }) - - it('Klassen, Schleifen, try/catch, Lambda, Generics und Array-Initialisierer', () => { - const code = `public class A{ -public static void main(String[] args){ -int[] a={1,2,3}; int x=-1; double d=(double) -x*2; -for(int i=0;i l=new ArrayList<>(); Map> m = new HashMap<>(); -do{x--;}while(x>0); -try{x=1/x;}catch(Exception e){x=0;}finally{x++;} -button.addActionListener(e->{x++;}); -if(x<5&&!b){y=x>3?1:0;}else if(x==2) {return;} -for (int z:a) {} -switch(x){case 1->{x=2;} default->x=3;} -String s="a{b}x>=1"+x; // Kommentar {x=1;} -} -}` - expect(formatJava(code)).toBe(`public class A { - public static void main(String[] args) { - int[] a = {1, 2, 3}; - int x = -1; - double d = (double) -x * 2; - for (int i = 0; i < a.length; i++) { - x += a[i]; - } - List l = new ArrayList<>(); - Map> m = new HashMap<>(); - do { - x--; - } while (x > 0); - try { - x = 1 / x; - } catch (Exception e) { - x = 0; - } finally { - x++; - } - button.addActionListener(e -> { - x++; - }); - if (x < 5 && !b) { - y = x > 3 ? 1 : 0; - } else if (x == 2) { - return; - } - for (int z : a) {} - switch (x) { - case 1 -> { - x = 2; - } - default -> x = 3; - } - String s = "a{b}x>=1" + x; // Kommentar {x=1;} - } -}`) - }) - - it('gut formatierter Code bleibt, wie er ist', () => { - const code = `import java.util.*; - -public class Main { - public static void main(String[] args) { - switch (x) { - case 1: - x = 2; - break; - default: - x = 0; - } - /* - * Blockkommentar - */ - if (Alter über 18?) { - System.out.println("Erwachsen"); - } - } -}` - expect(formatJava(code)).toBe(code) - }) - - it('nur die angegebenen Zeilen', () => { - expect(formatJava('if (a) {b();}\nif (c) {d();}', 2, 2)).toBe('if (a) {b();}\nif (c) {\n d();\n}') - }) -}) diff --git a/web/test/javaIndent.test.ts b/web/test/javaIndent.test.ts deleted file mode 100644 index d01939cd..00000000 --- a/web/test/javaIndent.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { indentJava } from '../src/core/javaIndent' - -const flat = (code: string) => code.split('\n').map((l) => l.trim()).join('\n') - -describe('Einrücken', () => { - it('Blöcke, switch, try/catch und Lambda wie in Eclipse', () => { - const code = `public class A { - public static void main(String[] args) { - int x = 3; // Kommentar mit { - if (x > 2) { - System.out.println("a { b"); - } else { - System.out.println("b"); - } - switch (x) { - case 1 -> { - System.out.println("1"); - } - default -> { - x++; - } - } - switch (x) { - case 1: - x = 2; - break; - default: - x = 0; - } - try { - x = 1 / x; - } catch (ArithmeticException e) { - x = 0; - } - button.addActionListener(e -> { - x++; - }); - /* - * Blockkommentar - */ - } -}` - expect(indentJava(flat(code))).toBe(code) - }) - - it('Pseudocode und halbfertiger Code', () => { - expect(indentJava('if (Alter über 18?) {\nSystem.out.println("Erwachsen");\n} else {\n x\n}')).toBe( - 'if (Alter über 18?) {\n System.out.println("Erwachsen");\n} else {\n x\n}', - ) - expect(indentJava('while (x > 0) {\nx--;')).toBe('while (x > 0) {\n x--;') - expect(indentJava('}\n}\nx++;')).toBe('}\n}\nx++;') - }) - - it('nur die angegebenen Zeilen', () => { - expect(indentJava('if (a) {\nb();\nc();\n}', 3, 3)).toBe('if (a) {\nb();\n c();\n}') - }) -}) diff --git a/web/test/javaMembers.test.ts b/web/test/javaMembers.test.ts deleted file mode 100644 index e7794490..00000000 --- a/web/test/javaMembers.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { declaredType, memberCompletionsAt, receiverType } from '../src/core/javaMembers' -import { memberCompletion } from '../src/ui/javaEditor' - -/** Vorschläge am Ende von `code`. */ -function names(code: string, lang: 'de' | 'en' | 'pt' = 'de') { - return memberCompletionsAt(code, code.length, lang)?.members.map((m) => m.name) -} - -const program = (body: string) => `import java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n ${body}` - -describe('Vorschläge nach dem Punkt', () => { - it('schlägt nach mbot.led(0). setColor vor', () => { - expect(names(program('mbot.led(0).'))).toEqual(['setColor']) - }) - - it('Scanner mit beliebigem Namen: nextInt, nextLine …', () => { - const list = names(program('Scanner eingabe = new Scanner(System.in);\n int x = eingabe.')) - expect(list).toEqual(expect.arrayContaining(['nextInt', 'nextLine', 'nextDouble', 'hasNextLine', 'next'])) - }) - - it('angefangener Name: from zeigt hinter den Punkt', () => { - const code = program('Scanner sc = new Scanner(System.in);\n sc.nex') - expect(memberCompletionsAt(code, code.length)?.from).toBe(code.length - 3) - }) - - it('String, String-Array und Element', () => { - const decl = 'String name = "Ludwig";\n String[] namen = {"a"};\n ' - expect(names(program(decl + 'name.'))).toEqual(expect.arrayContaining(['length', 'charAt', 'equals', 'substring'])) - expect(names(program(decl + 'namen.'))).toEqual(['length']) - expect(names(program(decl + 'namen[0].'))).toContain('charAt') - expect(names(program(decl + 'args.'))).toEqual(['length']) - }) - - it('Ketten: eingabe.nextLine(). → String-Methoden, System.out. → println', () => { - expect(names(program('Scanner e = new Scanner(System.in);\n e.nextLine().'))).toContain('trim') - expect(names(program('System.'))).toEqual(expect.arrayContaining(['out', 'in'])) - expect(names(program('System.out.'))).toEqual(expect.arrayContaining(['println', 'print', 'printf'])) - expect(names(program('"Hallo".'))).toContain('toUpperCase') - }) - - it('Klassen mit Punkt: Math, Integer, Color', () => { - expect(names(program('double z = Math.'))).toEqual(expect.arrayContaining(['random', 'sqrt', 'pow', 'PI'])) - expect(names(program('int z = Integer.'))).toContain('parseInt') - expect(names(program('knopf.setBackground(Color.'))).toContain('RED') - }) - - it('Swing: JFrame, JButton, JLabel, JTextField mit geerbten Methoden', () => { - const swing = 'JFrame fenster = new JFrame("Hallo");\n JButton knopf = new JButton("OK");\n JTextField feld = new JTextField(10);\n JLabel l = new JLabel();\n ' - expect(names(program(swing + 'fenster.'))).toEqual(expect.arrayContaining(['setSize', 'setVisible', 'add', 'setLayout', 'setDefaultCloseOperation', 'setTitle'])) - expect(names(program(swing + 'knopf.'))).toEqual(expect.arrayContaining(['addActionListener', 'setText', 'setBackground'])) - expect(names(program(swing + 'feld.'))).toEqual(expect.arrayContaining(['getText', 'setText'])) - expect(names(program(swing + 'l.'))).toContain('setText') - expect(names(program(swing + 'fenster.setDefaultCloseOperation(JFrame.'))).toEqual(['EXIT_ON_CLOSE']) - }) - - it('Typen aus for-each, catch und var', () => { - expect(declaredType('for (String wort : woerter) {', 'wort')).toBe('String') - expect(declaredType('} catch (NumberFormatException fehler) {', 'fehler')).toBe('NumberFormatException') - expect(names('try { } catch (NumberFormatException fehler) {\n fehler.')).toContain('getMessage') - expect(declaredType('var sc = new Scanner(System.in);', 'sc')).toBe('Scanner') - expect(declaredType('int[][] feld = new int[3][3];', 'feld')).toBe('int[][]') - expect(receiverType('int[][] feld;', 'feld[0]')?.type).toBe('int[]') - }) - - it('Unbekanntes und Zahlen: keine Liste (die normalen Vorschläge bleiben)', () => { - expect(names(program('int zahl = 3;\n zahl.'))).toBeUndefined() - expect(names(program('unbekannt.'))).toBeUndefined() - expect(names(program('double d = 3.'))).toBeUndefined() - }) - - it('Beschreibung in der eingestellten Sprache, Englisch als Rückfall', () => { - const code = program('Scanner eingabe = new Scanner(System.in);\n eingabe.') - const de = memberCompletionsAt(code, code.length, 'de')!.members.find((m) => m.name === 'nextInt')! - const en = memberCompletionsAt(code, code.length, 'en')!.members.find((m) => m.name === 'nextInt')! - expect(de.description).toBe('Ganze Zahl einlesen') - expect(en.description).toBe('Read a whole number') - }) - - it('Vorschlag: Klammern, Felder ohne Klammern', () => { - const code = program('Scanner eingabe = new Scanner(System.in);\n String t = "";\n String[] a = {};\n ') - const at = (expr: string) => memberCompletionsAt(code + expr, (code + expr).length)!.members - const nextInt = memberCompletion(at('eingabe.').find((m) => m.name === 'nextInt')!) - expect(nextInt.label).toBe('nextInt()') - expect(nextInt.apply).toBe('nextInt()') - expect(nextInt.detail).toBe('int · Ganze Zahl einlesen') - expect(memberCompletion(at('t.').find((m) => m.name === 'charAt')!).label).toBe('charAt(…)') - expect(memberCompletion(at('a.')[0]).apply).toBe('length') - }) -}) diff --git a/web/test/javaParser.test.ts b/web/test/javaParser.test.ts deleted file mode 100644 index 567b1251..00000000 --- a/web/test/javaParser.test.ts +++ /dev/null @@ -1,401 +0,0 @@ -import { readdirSync, readFileSync, statSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import type { LiveLabels } from '../src/core/codeGenerator' -import { setTextLines, insertAfterSibling } from '../src/core/editing' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava, mergeLiveJava, plainLiveJava, type JavaForm } from '../src/core/liveSource' -import { ElementType, makeElement, type VSDocument } from '../src/core/model' -import { decodeDocument } from '../src/core/xmlCodec' -import { SimulationEngine } from '../src/core/simulation' -import { onlyLineBreaksMissing, sameOutput } from '../src/core/exerciseCheck' -import { generateCode } from '../src/core/codeGenerator' -import { indentJava } from '../src/core/javaIndent' - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } -const parse = (src: string, subprograms?: Set) => parseLiveJava(src, labels, { subprograms }) -const doc = (src: string): VSDocument => documentFromJava({ caption: '', elements: [] }, parse(src)) -const forms: JavaForm[] = [ - { asClass: false, withMain: false }, - { asClass: false, withMain: true }, - { asClass: true, withMain: false }, - { asClass: true, withMain: true }, -] -const types = (src: string) => parse(src).elements.map((e) => e.type) - -/** Alle Struktogramme im Repo: Beispiele, Lektionen, Übungen. */ -function diagramFiles(dir: string, out: string[] = []): string[] { - for (const name of readdirSync(dir)) { - if (name === 'node_modules' || name.startsWith('.')) continue - const path = join(dir, name) - if (statSync(path).isDirectory()) diagramFiles(path, out) - else if (name.endsWith('.visustruct')) out.push(path) - } - return out -} - -describe('Java tippen → Struktogramm', () => { - it('der Knopf „Einrücken“ lässt erzeugten Code unverändert', () => { - for (const file of diagramFiles(join(__dirname, '..', '..'))) { - const original = decodeDocument(readFileSync(file, 'utf8')) - for (const form of forms) { - const code = plainLiveJava(original, labels, form) - expect(indentJava(code), `${file} ${JSON.stringify(form)}`).toBe(code) - } - } - }) - - it('jedes Struktogramm im Repo: Code → Struktogramm → derselbe Code', () => { - const files = diagramFiles(join(__dirname, '..', '..')) - expect(files.length).toBeGreaterThan(100) - for (const file of files) { - const original = decodeDocument(readFileSync(file, 'utf8')) - for (const form of forms) { - const code = plainLiveJava(original, labels, form) - const back = documentFromJava(original, parse(code)) - expect(plainLiveJava(back, labels, form), `${file} ${JSON.stringify(form)}`).toBe(code) - expect(back.caption, file).toBe(original.caption) - } - const code = plainLiveJava(original, labels) - const parsed = parse(code) - // Fehlendes `;` erzeugt der Generator selbst bei Pseudocode wie „Switch on the kettle“. - // Übungen „Warum läuft das nicht?“ starten absichtlich mit falscher Schreibweise (`system.out`, `.Length`). - if (!file.endsWith('-laeuft-nicht-start.visustruct')) expect(parsed.problems.filter((p) => p.arg !== ';'), file).toEqual([]) - } - }) - - it('Grundformen werden zu den passenden Kästen', () => { - const src = [ - 'int x = 5;', - 'System.out.println("Hallo " + x);', - 'if (x > 3) { x--; } else { x++; }', - 'while (x < 10) { x++; }', - 'do { x--; } while (x > 0);', - 'for (int i = 0; i < 3; i++) { x += i; }', - 'while (true) { break; }', - 'switch (x) { case 1 -> { x = 2; } default -> { x = 0; } }', - ].join('\n') - expect(types(src)).toEqual([ - ElementType.anweisung, - ElementType.anweisung, - ElementType.verzweigung, - ElementType.whileSchleife, - ElementType.doUntilSchleife, - ElementType.forSchleife, - ElementType.endlosschleife, - ElementType.fallauswahl, - ]) - const els = parse(src).elements - expect(els[0].textLines).toEqual(['int x = 5']) - expect(els[1].textLines).toEqual(['output: "Hallo " + x']) - expect(els[5].textLines).toEqual(['int i = 0', 'i < 3', 'i++']) - expect(els[7].cases?.map((c) => c.name)).toEqual(['1', 'default']) - }) - - it('println ist schon beim Tippen eine Ausgabe', () => { - expect(parse('System.out.println();').elements[0].textLines).toEqual(['output: ']) - expect(parse('System.out.println(').elements[0].textLines).toEqual(['output: ']) - expect(parse('System.out.println("Hal').elements[0].textLines).toEqual(['output: "Hal']) - expect(plainLiveJava(doc('System.out.println();'), labels)).toBe('System.out.println();') - }) - - it('Eingabe mit Hinweis wird ein Eingabe-Kasten', () => { - const els = parse('System.out.println("Wie alt bist du?");\nint alter = scanner.nextInt();').elements - expect(els.map((e) => e.textLines)).toEqual([['input: int alter "Wie alt bist du?"']]) - expect(parse('System.out.println("n: ");\nint n = scanner.nextInt();').elements[0].textLines).toEqual(['input: int n']) - expect(parse('String w = scanner.nextLine();').elements[0].textLines).toEqual(['input: String w']) - }) - - it('Klasse mit main und Unterprogramm-Kopf', () => { - const cls = parse('import java.util.Scanner;\npublic class A {\n public static void main(String[] args) {\n int x = 1;\n }\n}') - expect(cls.caption).toBeUndefined() - expect(cls.elements[0].textLines).toEqual(['int x = 1']) - const method = parse('public static int quadrat(int x) {\n return x * x;\n}') - expect(method.caption).toBe('int quadrat(int x)') - expect(method.elements[0].textLines).toEqual(['return x * x']) - }) - - it('Klasse und main: Titel = Klassenname, in beide Richtungen', () => { - const doc0: VSDocument = { caption: 'Rechner', elements: [makeElement(ElementType.anweisung, { textLines: ['input: int x'] })] } - expect(plainLiveJava(doc0, labels, { asClass: true, withMain: true })).toBe( - [ - 'import java.util.Scanner;', - '', - 'public class Rechner {', - ' public static void main(String[] args) {', - ' Scanner scanner = new Scanner(System.in);', - '', - ' System.out.println("x: ");', - ' int x = scanner.nextInt();', - ' }', - '}', - ].join('\n'), - ) - // Der Import steht auch um `main` herum ganz oben, im Snippet in der ersten Zeile. - expect(plainLiveJava(doc0, labels, { asClass: false, withMain: true }).split('\n').slice(0, 3)).toEqual([ - 'import java.util.Scanner;', - '', - 'public static void main(String[] args) {', - ]) - expect(plainLiveJava(doc0, labels).split('\n')[0]).toBe('import java.util.Scanner;') - // Klassenname tippen ändert den Titel … - expect(documentFromJava(doc0, parse('public class Taschenrechner {\n}')).caption).toBe('Taschenrechner') - // … aber aus „Mein Programm“ wird `Mein_Programm`, und der Titel bleibt. - expect(documentFromJava({ ...doc0, caption: 'Mein Programm' }, parse('public class Mein_Programm {\n}')).caption).toBe('Mein Programm') - // Klasse ohne main: Anweisungen direkt in der Klasse. - expect(parse('public class A {\n int x = 1;\n}').elements[0].textLines).toEqual(['int x = 1']) - }) - - it('Scanner ohne import ist ein Fehler, wie bei javac', () => { - const keys = (src: string) => parse(src).problems.map((p) => p.key) - expect(keys('Scanner scanner = new Scanner(System.in);\nint x = scanner.nextInt();')).toEqual(['web_parse_missing_import']) - expect(keys('import java.util.Scanner;\nScanner scanner = new Scanner(System.in);')).toEqual([]) - expect(keys('import java.util.*;\npublic class A {\n public static void main(String[] args) {\n Scanner s = new Scanner(System.in);\n }\n}')).toEqual([]) - // Nur im Kommentar oder in einem Text zählt nicht. - expect(keys('// import java.util.Scanner;\nScanner s = new Scanner(System.in);')).toEqual(['web_parse_missing_import']) - expect(keys('System.out.println("Scanner");')).toEqual([]) - }) - - it('\\n in einer Ausgabe beginnt eine neue Zeile; fehlende Zeilenumbrüche werden erkannt', () => { - const run = (src: string) => { - const engine = new SimulationEngine(doc(src)) - let steps = 0 - while (engine.canStep && steps++ < 1000) engine.step() - return engine.state.outputLines - } - const expected = ['Ludwig ist 7 Jahre alt.', 'Größe: 1.2 m'] - expect(run('System.out.print("Ludwig ist 7 Jahre alt.\\n");\nSystem.out.print("Größe: 1.2 m");')).toEqual(expected) - expect(run('System.out.println("A\\nB");\nSystem.out.print("C");')).toEqual(['A', 'B', 'C']) - const joined = run('System.out.print("Ludwig ist 7 Jahre alt.");\nSystem.out.print("Größe: 1.2 m");') - expect(sameOutput(joined, expected)).toBe(false) - expect(onlyLineBreaksMissing(joined, expected)).toBe(true) - expect(onlyLineBreaksMissing(['Ludwig ist 7 Jahre alt. Größe: 1.3 m'], expected)).toBe(false) - expect(onlyLineBreaksMissing(expected, expected)).toBe(false) - }) - - it('System.out.print schreibt ohne neue Zeile, auch mit Array-Fach', () => { - const d = doc( - 'int[] intAllgemeinFach = {3, 5};\n' + - 'for (int i = 0; i < intAllgemeinFach.length; i++) {\n' + - ' System.out.print(intAllgemeinFach[i] + ": ");\n' + - ' System.out.println("ok");\n' + - '}\n' + - 'System.out.print("A");\n' + - 'System.out.print("B");\n' + - 'System.out.println();', - ) - const engine = new SimulationEngine(d) - let steps = 0 - while (engine.canStep && steps++ < 1000) engine.step() - expect(engine.message).not.toMatch(/Nicht unterstützte|Unsupported/u) - expect(engine.state.outputLines).toEqual(['3: ok', '5: ok', 'AB']) - const java = generateCode(d, 'java') - expect(java).toContain('System.out.print(intAllgemeinFach[i] + ": ");') - expect(generateCode(d, 'javascript')).toContain('process.stdout.write(String(intAllgemeinFach[i] + ": "));') - expect(generateCode(d, 'python')).toContain('print(str(intAllgemeinFach[i]) + ": ", end="")') - }) - - it('Eingabe nach einer Ausgabe fragt nicht noch einmal nach dem Variablennamen', () => { - const ask = (src: string) => { - const engine = new SimulationEngine(doc(src)) - while (engine.canStep && !engine.state.inputRequest) engine.step() - return { request: engine.state.inputRequest, lines: engine.state.outputLines } - } - const fach = 'String[] fach = {"Mathe"};\nint[] note = new int[1];\nint i = 0;\n' - expect(ask(fach + 'System.out.println("Note für Fach " + fach[i]);\nnote[i] = scanner.nextInt();')).toEqual({ - request: { variableName: 'note[i]', valueType: 'int', prompt: '' }, - lines: ['Note für Fach Mathe'], - }) - expect(ask(fach + 'System.out.print(fach[i] + ": ");\nnote[i] = scanner.nextInt();')).toEqual({ - request: { variableName: 'note[i]', valueType: 'int', prompt: '', continuesLine: true }, - lines: ['Mathe: '], - }) - // Ohne Ausgabe davor bleibt die Frage `name:`. - expect(ask(fach + 'note[i] = scanner.nextInt();').request?.prompt).toBe('note[i]:') - }) - - it('Arrays ohne import ist ein Fehler, wie bei javac', () => { - const keys = (src: string) => parse(src).problems.map((p) => p.key) - expect(keys('int[] a = {1, 2};\nSystem.out.println(Arrays.toString(a));')).toEqual(['web_parse_missing_import']) - expect(keys('import java.util.Arrays;\nint[] a = {1, 2};\nSystem.out.println(Arrays.toString(a));')).toEqual([]) - expect(keys('int[] a = {1, 2};\nSystem.out.println(a);')).toEqual([]) - }) - - it('Aufruf eines Unterprogramms aus einem anderen Reiter', () => { - expect(parse('int y = quadrat(4);', new Set(['quadrat'])).elements[0].type).toBe(ElementType.aufruf) - expect(parse('int y = quadrat(4);').elements[0].type).toBe(ElementType.anweisung) - }) - - it('klassisches switch mit break', () => { - const els = parse('switch (n) {\n case 1:\n x = 1;\n break;\n default:\n x = 0;\n}').elements - expect(els[0].cases?.map((c) => [c.name, c.elements.map((e) => e.textLines[0])])).toEqual([ - ['1', ['x = 1']], - ['default', ['x = 0']], - ]) - }) - - it('unfertiger Code beim Tippen: Struktogramm entsteht trotzdem', () => { - // Nach `if (x > 0) {` ist die Verzweigung schon da. - const open = parse('if (x > 0) {') - expect(open.elements.map((e) => e.type)).toEqual([ElementType.verzweigung]) - expect(open.problems.map((p) => p.arg)).toEqual(['}']) - // Fehlende `)` und fehlendes `;`. - const half = parse('if (x > 0 {\n System.out.println(x)\n}') - expect(half.elements[0].textLines).toEqual(['x > 0']) - expect(half.elements[0].cases![0].elements[0].textLines).toEqual(['output: x']) - expect(half.problems.map((p) => p.arg)).toEqual([')', ';']) - // Nur das Schlüsselwort. - expect(types('while')).toEqual([ElementType.whileSchleife]) - expect(types('for (int i = 0; i <')).toEqual([ElementType.forSchleife]) - // Offene Zeichenkette. - const str = parse('System.out.println("Hallo') - expect(str.elements).toHaveLength(1) - expect(str.problems.some((p) => p.key === 'web_parse_unclosed_string')).toBe(true) - // Zu viele Klammern. - expect(parse('x = 1;\n}').problems.map((p) => p.key)).toEqual(['web_parse_extra']) - }) - - it('Swing: Lambda und anonyme Klasse mit Rumpf sind eine Anweisung', () => { - const lambda = parse('knopf.addActionListener(e -> {\n knopf.setText("Ich wurde geklickt");\n});\nfenster.setVisible(true);') - expect(lambda.problems).toEqual([]) - expect(lambda.elements.map((e) => e.textLines)).toEqual([ - ['knopf.addActionListener(e -> { knopf.setText("Ich wurde geklickt"); })'], - ['fenster.setVisible(true)'], - ]) - const anonym = parse( - 'knopf.addActionListener(new ActionListener() {\n public void actionPerformed(ActionEvent e) {\n knopf.setText("Ich wurde geklickt");\n }\n});', - ) - expect(anonym.problems).toEqual([]) - expect(anonym.elements).toHaveLength(1) - // Noch nicht fertig getippt: Die fehlende `}` wird gemeldet. - expect(parse('knopf.addActionListener(e -> {\n knopf.setText("x");').problems.map((p) => p.arg)).toContain('}') - }) - - it('try/catch/finally ist ein Anweisungskasten', () => { - const r = parse('try {\n int z = Integer.parseInt(text);\n} catch (NumberFormatException e) {\n System.out.println("Keine Zahl");\n} finally {\n fertig = true;\n}\nSystem.out.println("Weiter");') - expect(r.problems).toEqual([]) - expect(r.elements.map((e) => e.textLines)).toEqual([ - ['try { int z = Integer.parseInt(text); } catch (NumberFormatException e) { System.out.println("Keine Zahl"); } finally { fertig = true; }'], - ['output: "Weiter"'], - ]) - expect(parse('try {\n int z = 1;').problems.map((p) => p.arg)).toEqual(['}']) - }) - - it('Platzhalter wie „Bedingung“ gelten als leer und werden markiert', () => { - const src = 'if (Bedingung) {\n // Dein Code\n}' - const p = parse(src) - expect(p.elements[0].textLines).toEqual(['']) - expect(p.placeholders.map((r) => src.slice(r.from, r.to))).toEqual(['Bedingung', '// Dein Code']) - expect(p.elements[0].cases![0].elements[0].textLines).toEqual(['ø']) - }) - - it('Zeilen der Elemente für die Markierung', () => { - const p = parse('int x = 1;\nwhile (x < 3) {\n x++;\n}') - expect(p.spans).toEqual([ - { path: [0], from: 0, to: 0 }, - { path: [1], from: 1, to: 3 }, - { path: [1, 0, 0], from: 2, to: 2 }, - ]) - }) -}) - -describe('Struktogramm ändern → getippter Code bleibt', () => { - const typed = [ - '// Mein erstes Programm', - 'int x = 1; // Startwert', - '', - 'while (x < 10) {', - ' // verdoppeln', - ' x = x * 2;', - '}', - 'System.out.println(x);', - ].join('\n') - const before = doc(typed) - - it('Text eines Kastens ändern: Kommentare bleiben', () => { - const after = setTextLines(before, [1, 0, 0], ['x = x * 3']) - expect(mergeLiveJava(typed, before, after, labels)).toBe(typed.replace('x = x * 2;', 'x = x * 3;')) - // Der Kommentar am Zeilenende wandert mit. - const start = setTextLines(before, [0], ['int x = 2']) - expect(mergeLiveJava(typed, before, start, labels)).toBe(typed.replace('int x = 1;', 'int x = 2;')) - }) - - it('Element einfügen und löschen', () => { - const added = insertAfterSibling(before, makeElement(ElementType.anweisung, { textLines: ['output: "fertig"'] }), [2]) - expect(mergeLiveJava(typed, before, added, labels)).toBe(typed + '\nSystem.out.println("fertig");') - const removed: VSDocument = { ...before, elements: before.elements.slice(0, 2) } - expect(mergeLiveJava(typed, before, removed, labels)).toBe(typed.split('\n').slice(0, -1).join('\n')) - }) - - it('unveränderter erzeugter Code wird einfach neu erzeugt', () => { - const code = plainLiveJava(before, labels) - const after = setTextLines(before, [0], ['int x = 3']) - expect(mergeLiveJava(code, before, after, labels)).toBe(plainLiveJava(after, labels)) - }) - - it('Farben der Kästen bleiben beim Tippen erhalten', () => { - const colored: VSDocument = { ...before, elements: before.elements.map((e, i) => (i === 1 ? { ...e, backgroundColor: { r: 1, g: 0, b: 0, a: 1 } } : e)) } - const next = documentFromJava(colored, parse(typed.replace('x < 10', 'x < 20'))) - expect(next.elements[1].backgroundColor).toEqual({ r: 1, g: 0, b: 0, a: 1 }) - expect(next.elements[1].textLines).toEqual(['x < 20']) - }) -}) - -describe('Scanner ohne Variable', () => { - const L = { code: 'Dein Code' } as Parameters[1] - const main = (body: string) => `import java.util.Scanner;\n\npublic class Main {\n public static void main(String[] args) {\n${body}\n }\n}` - const keys = (src: string) => parseLiveJava(src, L).problems.filter((p) => p.key === 'web_parse_missing_scanner').map((p) => [p.key, p.arg]) - - it('meldet eingabe.next() ohne angelegten Scanner', () => { - expect(keys(main(' String s = eingabe.next();'))).toEqual([['web_parse_missing_scanner', 'Scanner eingabe = new Scanner(System.in);']]) - }) - - it('nimmt angelegte Scanner, Attribute und Parameter an', () => { - expect(keys(main(' Scanner eingabe = new Scanner(System.in);\n String s = eingabe.next();\n while (eingabe.hasNextInt()) eingabe.nextInt();'))).toEqual([]) - expect(keys(`import java.util.Scanner;\n\npublic class Main {\n static Scanner sc = new Scanner(System.in);\n\n static int lies(Scanner in) {\n return in.nextInt();\n }\n\n public static void main(String[] args) {\n String s = sc.nextLine();\n }\n}`)).toEqual([]) - }) -}) - -describe('boolean und vertippte Datentypen', () => { - const typo = (src: string) => parse(src).problems.find((p) => p.key === 'web_parse_unknown_type')?.arg - it('meldet vertippte Grundtypen mit Vorschlag', () => { - expect(typo('booelan wpfAlleUeber5;')).toBe('boolean') - expect(typo('Int x = 3;')).toBe('int') - expect(typo('string s = "a";')).toBe('String') - expect(typo('dobule d = 2;')).toBe('double') - expect(typo('for (Int i = 0; i < 3; i++) {\n}')).toBe('int') - }) - it('lässt gültige Typen und eigene Klassen in Ruhe', () => { - for (const src of ['boolean b = true;', 'Boolean b = true;', 'Integer i = 3;', 'Kind k = new Kind();', 'Auto a;', 'String s;', 'return x;']) - expect(typo(src)).toBeUndefined() - }) - it('simuliert boolean ohne Startwert, Vergleiche und Ausgaben von Bedingungen', () => { - const src = 'boolean fertig;\nint x = 3;\nSystem.out.println(fertig);\nfertig = x < 5;\nif (fertig == true) {\n System.out.println("ja " + fertig);\n}\nSystem.out.println(!fertig);\nSystem.out.println(x + 1 > 3);\nSystem.out.println("klein: " + (x < 3));\nint a, b;\nSystem.out.println(a + b);' - const engine = new SimulationEngine(doc(src)) - let steps = 0 - while (engine.canStep && steps++ < 1000) engine.step() - expect(engine.state.outputLines).toEqual(['false', 'ja true', 'false', 'true', 'klein: false', '0']) - }) -}) - -describe('Groß- und Kleinschreibung', () => { - const wrongCase = (src: string) => parse(src).problems.find((p) => p.key === 'web_parse_wrong_case')?.arg - it('meldet falsch geschriebene Java-Wörter mit der richtigen Schreibweise', () => { - expect(wrongCase('system.out.println(1);')).toBe('System') - expect(wrongCase('System.Out.println(1);')).toBe('out') - expect(wrongCase('System.out.Println(1);')).toBe('println') - expect(wrongCase('double w = math.sqrt(4);')).toBe('Math') - expect(wrongCase('int[] a = {1};\nint n = a.Length;')).toBe('length') - expect(wrongCase('String s = "a";\nchar c = s.charat(0);')).toBe('charAt') - }) - it('lässt richtige Schreibweise und eigene Variablen in Ruhe', () => { - for (const src of ['System.out.println(1);', 'String string = "a";\nint n = string.length();', 'int zahl = 1;\nint Zahl = 2;\nSystem.out.println(zahl + Zahl);']) - expect(wrongCase(src)).toBeUndefined() - }) - it('die Simulation nennt bei einer unbekannten Variable den Namen in anderer Schreibweise', () => { - const engine = new SimulationEngine(doc('int zahl = 4;\nSystem.out.println(Zahl);')) - let steps = 0 - while (engine.canStep && steps++ < 100) engine.step() - expect(engine.state.notice).toEqual({ kind: 'unknownVariable', text: 'Zahl', similar: 'zahl' }) - }) -}) diff --git a/web/test/javaProject.test.ts b/web/test/javaProject.test.ts deleted file mode 100644 index 25aa98f0..00000000 --- a/web/test/javaProject.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { - addPackage, - classFileText, - exampleProject, - moveFile, - packageOf, - packagesOf, - projectClasses, - projectFromFiles, - projectFromJSON, - projectToJSON, - removeFile, - removePackage, - renamePackage, - withPackageLine, -} from '../src/core/javaProject' -import { projectFromZip, projectToZip } from '../src/ui/projectZip' - -describe('javaProject', () => { - it('liest Packages aus Pfaden', () => { - expect(packageOf('tiere/haus/Hund.java')).toBe('tiere.haus') - expect(packageOf('Main.java')).toBe('') - expect(packagesOf(exampleProject())).toEqual(['personen', 'tiere']) - }) - - it('zeigt das Standard-Package nur mit Dateien und leere Packages auch', () => { - let p = addPackage({ name: 'P', files: [], packages: [] }, 'leer') - expect(packagesOf(p)).toEqual(['leer']) - p = { ...p, files: [{ path: 'Main.java', text: 'public class Main {}' }] } - expect(packagesOf(p)).toEqual(['leer', '']) - }) - - it('setzt, ändert und entfernt die package-Zeile', () => { - expect(withPackageLine('public class A {}\n', 'x')).toBe('package x;\n\npublic class A {}\n') - expect(withPackageLine('package x;\n\npublic class A {}\n', 'y.z')).toBe('package y.z;\n\npublic class A {}\n') - expect(withPackageLine('// Kopf\npackage x;\n\npublic class A {}\n', '')).toBe('// Kopf\npublic class A {}\n') - expect(classFileText('tiere', 'Katze', 'class')).toBe('package tiere;\n\npublic class Katze {\n}\n') - }) - - it('kennt alle Klassen mit Datei und Package', () => { - const classes = projectClasses(exampleProject()) - expect(classes.map((c) => `${c.pkg}.${c.name}`).sort()).toEqual(['personen.Besitzer', 'tiere.Hund', 'tiere.Tier']) - expect(classes.find((c) => c.name === 'Hund')?.superclass).toBe('Tier') - }) - - it('verschiebt Dateien und benennt Packages um', () => { - let p = moveFile(exampleProject(), 'tiere/Hund.java', 'haustiere/Hund.java') - expect(p.files.find((f) => f.path === 'haustiere/Hund.java')?.text.startsWith('package haustiere;')).toBe(true) - p = renamePackage(p, 'tiere', 'zoo.tiere') - expect(p.files.find((f) => f.path === 'zoo/tiere/Tier.java')?.text.startsWith('package zoo.tiere;')).toBe(true) - }) - - it('behält ein Package nach dem Löschen der letzten Datei', () => { - let p = removeFile(exampleProject(), 'personen/Besitzer.java') - expect(packagesOf(p)).toContain('personen') - p = removePackage(p, 'personen') - expect(packagesOf(p)).toEqual(['tiere']) - }) - - it('macht aus einzelnen Dateien ein Projekt', () => { - const p = projectFromFiles('P', [{ name: 'code.txt', text: 'package a.b;\r\npublic class X {}' }]) - expect(p.files).toEqual([{ path: 'a/b/X.java', text: 'package a.b;\npublic class X {}' }]) - }) - - it('speichert als JSON und als ZIP', () => { - const p = addPackage(exampleProject(), 'leer') - expect(projectFromJSON(projectToJSON(p))).toEqual(p) - expect(projectFromJSON('kaputt')).toBeUndefined() - const back = projectFromZip(projectToZip(p), 'X') - expect(back.name).toBe('Zoo') - expect(back.uml).toEqual(p.uml) - expect(back.files.map((f) => f.path).sort()).toEqual(p.files.map((f) => f.path).sort()) - expect(back.files.find((f) => f.path === 'tiere/Hund.java')?.text).toBe(p.files[1].text) - }) -}) diff --git a/web/test/javaRuntime.test.ts b/web/test/javaRuntime.test.ts deleted file mode 100644 index 4dd92fe3..00000000 --- a/web/test/javaRuntime.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { asProgram, mainClassName } from '../src/ui/javaRuntime' - -describe('Java mit CheerpJ', () => { - it('nimmt die öffentliche Klasse als Startklasse', () => { - expect(mainClassName('class Hilfe {}\npublic class Zaehler { }')).toBe('Zaehler') - expect(mainClassName('class Main { }')).toBe('Main') - }) - - it('packt Code ohne Klasse in public class Main', () => { - const snippet = asProgram('System.out.println("Hallo");') - expect(snippet).toContain('public class Main {') - expect(snippet).toContain('public static void main(String[] args) {\nSystem.out.println("Hallo");') - const withMain = asProgram('public static void main(String[] args) {\n}') - expect(withMain.match(/static void main/g)).toHaveLength(1) - expect(mainClassName(withMain)).toBe('Main') - expect(asProgram('public class A {}')).toBe('public class A {}') - // Getippter Import kommt vor die Klasse, nicht in main. - const imported = asProgram('import java.util.Scanner;\nScanner s = new Scanner(System.in);') - expect(imported.startsWith('import java.util.Scanner;\n')).toBe(true) - expect(imported.split('public class Main')[1]).not.toContain('import') - }) -}) diff --git a/web/test/layoutColumns.test.ts b/web/test/layoutColumns.test.ts deleted file mode 100644 index 8e88b331..00000000 --- a/web/test/layoutColumns.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { expect, test } from 'vitest' -import { readdirSync, readFileSync, statSync } from 'node:fs' -import { join } from 'node:path' -import { decodeDocument } from '../src/core/xmlCodec' -import { LayoutEngine, type LayoutNode } from '../src/core/layout' - -/** Spalten einer Verzweigung/Fallauswahl müssen die ganze Breite des Kastens füllen - * (sonst bleibt rechts Kopffarbe stehen und das if sieht aus wie eine Schleife, Lektion 14). */ -test('Spalten füllen die Verzweigung in allen Lektionsdiagrammen', () => { - const gaps: string[] = [] - const check = (n: LayoutNode, file: string) => { - if (n.caseColumnFrames.length) { - const last = n.caseColumnFrames[n.caseColumnFrames.length - 1] - const end = last.x + last.width - if (Math.abs(end - (n.frame.x + n.frame.width)) > 1) gaps.push(`${file}: ${n.element.textLines[0]}`) - } - n.children.forEach((c) => check(c, file)) - } - const walk = (dir: string) => { - for (const f of readdirSync(dir)) { - const p = join(dir, f) - if (statSync(p).isDirectory()) walk(p) - else if (p.endsWith('.visustruct')) { - const doc = decodeDocument(readFileSync(p, 'utf8')) - new LayoutEngine().layout(doc).elements.forEach((n) => check(n, p)) - } - } - } - walk('lessons/diagramme') - expect(gaps).toEqual([]) -}) diff --git a/web/test/lessonEnd.ts b/web/test/lessonEnd.ts deleted file mode 100644 index e1657546..00000000 --- a/web/test/lessonEnd.ts +++ /dev/null @@ -1,24 +0,0 @@ -import type { Lesson } from '../src/learn/lessonFormat' - -const GAMES = ['game', 'roundGame', 'rescueGame', 'driveGame', 'crosswordGame'] - -/** - * Jede Lektion endet so (Holger 2026-11-03): vorher mindestens ein Spiel, dann die Checkliste, zuletzt der Wissenstest. Checkliste und Wissenstest stehen nie im selben Schritt und nie zusammen mit anderem Inhalt. - * Liefert die Verstöße als lesbare Sätze, leer heißt alles in Ordnung. - */ -export function lessonEndProblems(lesson: Lesson): string[] { - const kinds = lesson.steps.map((s) => s.blocks.map((b) => b.kind as string)) - const where = (i: number) => `${lesson.id} / ${lesson.steps[i]?.title ?? '–'}` - const lists = kinds.flatMap((k, i) => (k.includes('checklist') ? [i] : [])) - if (lists.length !== 1) return [`${lesson.id}: ${lists.length} Checklisten statt einer`] - const ci = lists[0] - const last = kinds.length - 1 - const only = (i: number, kind: string) => kinds[i].includes(kind) && kinds[i].every((k) => k === kind || k === 'markdown') - const problems: string[] = [] - if (!only(ci, 'checklist')) problems.push(`${where(ci)}: Im Schritt der Checkliste steht noch anderes (${kinds[ci].join(', ')})`) - if (ci !== last - 1) problems.push(`${where(ci)}: Nach der Checkliste muss genau ein Schritt folgen, der Wissenstest`) - if (!only(last, 'quiz')) problems.push(`${where(last)}: Der letzte Schritt muss der Wissenstest sein, nur mit Fragen (${kinds[last].join(', ')})`) - const games = kinds.flatMap((k, i) => (k.some((x) => GAMES.includes(x)) ? [i] : [])) - if (!games.some((i) => i < ci)) problems.push(`${lesson.id}: Vor der Checkliste fehlt ein Spiel`) - return problems -} diff --git a/web/test/lessons.test.ts b/web/test/lessons.test.ts deleted file mode 100644 index 20adb2f5..00000000 --- a/web/test/lessons.test.ts +++ /dev/null @@ -1,573 +0,0 @@ -import { readdirSync, readFileSync } from 'node:fs' -import { join } from 'node:path' -import { beforeAll, describe, expect, it } from 'vitest' -import { decodeDocument } from '../src/core/xmlCodec' -import { checkExercise, runWithInputs, usedElementTypes } from '../src/core/exerciseCheck' -import type { LiveLabels } from '../src/core/codeGenerator' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava, plainLiveJava } from '../src/core/liveSource' -import { ElementType, makeElement, type VSElement } from '../src/core/model' -import { subprogramName } from '../src/core/simulation' -import { contentLanguages, diagram, image, lessons, reference, tasks, useContentLanguage } from '../src/learn/content' -import { exerciseArea, parseBlocks, parseLesson, parseTest, type Block } from '../src/learn/lessonFormat' -import { markdownToHTML } from '../src/learn/markdown' -import { clozeAnswerRight, clozeGaps, type Cloze } from '../src/learn/cloze' -import { papFits, papLayout, parsePap } from '../src/learn/pap' -import { bombParts, parseRoundGame, parseSwingWindow, simulate } from '../src/learn/roundGame' - -import { lessonEndProblems } from './lessonEnd' -const allBlocks = (): { where: string; block: Block }[] => [ - ...lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks.map((block) => ({ where: `${l.id} / ${s.title}`, block })))), - ...tasks.flatMap((t) => t.blocks.map((block) => ({ where: t.id, block }))), -] - -describe.each(contentLanguages)('Lernbereich: Inhalte (%s)', (lang) => { - beforeAll(() => useContentLanguage(lang)) - - it('Lektionen sind lückenlos nummeriert und haben Schritte', () => { - expect(lessons.length).toBeGreaterThanOrEqual(3) - lessons.forEach((l, i) => { - expect(l.number, l.id).toBe(i + 1) - expect(l.steps.length, l.id).toBeGreaterThan(1) - expect(l.section, l.id).not.toBe('') - }) - expect(reference).toContain({ de: '### Verzweigung', en: '### Branch', pt: '### Decisão' }[lang]) - }) - - it('kein Schritt heißt „Profiwissen: …“ (Profitipps stehen im Angeberwissen)', () => { - for (const lesson of lessons) { - for (const step of lesson.steps) expect(step.title, lesson.id).not.toMatch(/^(Profiwissen|Expert knowledge|Saber de profissional)\b/i) - } - }) - - it('jede Lektion hat mindestens ein Spiel, und es steht vor der Checkliste', () => { - for (const lesson of lessons) { - const kinds = lesson.steps.flatMap((s) => s.blocks.map((b) => b.kind)) - const game = kinds.findIndex((k) => k === 'game' || k === 'roundGame' || k === 'rescueGame' || k === 'driveGame' || k === 'crosswordGame') - expect(game, `${lesson.id}: kein Spiel`).toBeGreaterThanOrEqual(0) - expect(game, `${lesson.id}: Spiel nach der Checkliste`).toBeLessThan(kinds.indexOf('checklist')) - } - }) - - it('jede Lektion endet mit eigenen Schritten für Checkliste und Wissenstest', () => { - expect(lessons.flatMap(lessonEndProblems)).toEqual([]) - }) - - it('die Checkliste steht in einem eigenen Schritt „Checkliste“', () => { - const title = { de: 'Checkliste', en: 'Checklist', pt: 'Lista de verificação' }[lang] - for (const lesson of lessons) { - const step = lesson.steps.find((s) => s.blocks.some((b) => b.kind === 'checklist')) - expect(step?.title, lesson.id).toBe(title) - } - }) - - it('kein Text endet mit einem Doppelpunkt, ohne dass etwas folgt', () => { - // „probiere Folgendes aus:“ ohne Liste darunter (Lektion 3 hatte das) - const problems: string[] = [] - for (const lesson of lessons) { - for (const step of lesson.steps) { - step.blocks.forEach((block, i) => { - if (block.kind !== 'markdown') return - const parts = block.text.trim().split(/\n\s*\n/).map((p) => p.trim()) - const next = step.blocks[i + 1] - parts.forEach((part, k) => { - if (!part.endsWith(':') || part.includes('\n')) return - const follows = k + 1 < parts.length || (next && next.kind !== 'markdown' && next.kind !== 'checklist') - if (!follows) problems.push(`${lesson.id} / ${step.title}: ${part}`) - }) - }) - } - } - expect(problems).toEqual([]) - }) - - it('jedes verwendete Diagramm existiert und lässt sich laden', () => { - for (const { where, block } of allBlocks()) { - const files = - block.kind === 'diagram' ? [block.file] : block.kind === 'exercise' ? [block.exercise.start, block.exercise.solution, ...block.exercise.subprograms] : [] - for (const f of files.filter(Boolean)) expect(diagram(f!), `${where}: ${f}`).toBeDefined() - } - }) - - it('jede Übung hat mindestens zwei gestufte Tipps', () => { - const few = allBlocks().flatMap(({ where, block }) => (block.kind === 'exercise' && block.exercise.hints.length < 2 ? [`${where}: ${block.exercise.id}`] : [])) - expect(few).toEqual([]) - }) - - it('jedes verwendete Bild existiert', () => { - for (const { where, block } of allBlocks()) { - if (block.kind === 'images') for (const f of block.files) expect(image(f), `${where}: ${f}`).toBeDefined() - if (block.kind === 'graphic') expect(image(block.file), `${where}: ${block.file}`).toBeDefined() - if (block.kind === 'graphic' && block.labeled) expect(image(block.labeled), `${where}: ${block.labeled}`).toBeDefined() - } - }) - - it('jede Grafik-Unterschrift ist kurz (sonst fehlt das schließende „:::“)', () => { - for (const { where, block } of allBlocks()) { - if (block.kind === 'graphic' || block.kind === 'images') expect(block.caption.length, `${where}: ${block.caption.slice(0, 60)}`).toBeLessThan(400) - } - }) - - it('keine Schleife in einem Diagramm ist leer (Inhalt gehört in )', () => { - const loops = [ElementType.forSchleife, ElementType.whileSchleife, ElementType.doUntilSchleife, ElementType.endlosschleife] - const check = (els: VSElement[], where: string) => { - for (const e of els) { - if (loops.includes(e.type)) expect(e.loopBody?.length ?? 0, `${where}: ${e.textLines.join(' ')}`).toBeGreaterThan(0) - if (e.loopBody) check(e.loopBody, where) - for (const c of e.cases ?? []) check(c.elements, where) - } - } - for (const { where, block } of allBlocks()) { - const files = - block.kind === 'diagram' ? [block.file] : block.kind === 'exercise' ? [block.exercise.start, block.exercise.solution] : [] - for (const f of files.filter(Boolean)) { - const d = diagram(f!) - if (d) check(d.elements, `${where}: ${f}`) - } - } - }) - - it('jeder Punkt einer Checkliste verweist auf den Schritt, der ihn erklärt', () => { - for (const l of lessons) - for (const s of l.steps) - for (const b of s.blocks) - if (b.kind === 'checklist') for (const item of b.items) expect(item.step, `${l.id}: ${item.text}`).toBeDefined() - }) - - it('Kennungen von Übungen sind eindeutig', () => { - const ids = allBlocks().flatMap(({ block }) => (block.kind === 'exercise' ? [block.exercise.id] : [])) - expect(new Set(ids).size).toBe(ids.length) - }) - - it('jede Musterlösung besteht ihre eigenen Tests', () => { - let checked = 0 - for (const { where, block } of allBlocks()) { - if (block.kind !== 'exercise') continue - const ex = block.exercise - expect(ex.tests.length, `${where}: ${ex.id} hat keinen Test`).toBeGreaterThan(0) - for (const r of ex.requires) expect(r in ElementType || r === 'forZaehlSchleife', `${where}: braucht ${r}`).toBe(true) - if (!ex.solution) continue - const doc = diagram(ex.solution)! - const subs = ex.subprograms.map((f) => diagram(f)!) - const resolve = (name: string) => subs.find((s) => subprogramName(s.caption).toLowerCase() === subprogramName(name).toLowerCase()) - for (const outcome of checkExercise(doc, ex.tests, resolve)) { - expect(outcome.passed, `${where}: ${ex.id} ${JSON.stringify(outcome)}`).toBe(true) - } - for (const r of ex.requires) expect(usedElementTypes(doc).has(ElementType[r as keyof typeof ElementType])).toBe(true) - checked++ - } - expect(checked).toBeGreaterThanOrEqual(4) - }) - - it('Java-Übungen (code: java) lassen sich in Java lösen', () => { - const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - let checked = 0 - for (const { where, block } of allBlocks()) { - if (block.kind !== 'exercise' || block.exercise.code !== 'java') continue - const ex = block.exercise - expect(ex.solution, `${where}: ${ex.id} braucht eine Musterlösung`).toBeDefined() - // Die Musterlösung so, wie man sie in Java tippen würde, und daraus wieder das Struktogramm - const parsed = parseLiveJava(plainLiveJava(diagram(ex.solution!)!, labels), labels) - expect(parsed.problems, `${where}: ${ex.id}`).toEqual([]) - const doc = documentFromJava({ caption: '', elements: [] }, parsed) - for (const outcome of checkExercise(doc, ex.tests)) expect(outcome.passed, `${where}: ${ex.id} ${JSON.stringify(outcome)}`).toBe(true) - checked++ - } - expect(checked).toBeGreaterThanOrEqual(4) - }) - - it('ein leeres Startdiagramm besteht keine Übung', () => { - for (const { block } of allBlocks()) { - if (block.kind !== 'exercise') continue - const start = block.exercise.start ? diagram(block.exercise.start)! : { caption: '', elements: [] } - expect(checkExercise(start, block.exercise.tests).every((o) => o.passed), block.exercise.id).toBe(false) - } - }) -}) - -describe.each(['en', 'pt'] as const)('Lernbereich: Übersetzung (%s)', (lang) => { - const lessonFiles = import.meta.glob('../lessons/*/*.md', { query: '?raw', import: 'default', eager: true }) as Record - const taskFiles = import.meta.glob('../lessons/*/aufgaben/*.md', { query: '?raw', import: 'default', eager: true }) as Record - const names = (files: Record, lang: string) => - Object.keys(files).filter((p) => p.includes(`/lessons/${lang}/`)).map((p) => p.slice(p.indexOf(`/${lang}/`) + lang.length + 2)).sort() - - it('jede deutsche Lektion und Aufgabe gibt es auch übersetzt', () => { - expect(names(lessonFiles, lang)).toEqual(names(lessonFiles, 'de')) - expect(names(taskFiles, lang)).toEqual(names(taskFiles, 'de')) - }) - - it('beide Sprachen haben dieselben Übungen mit gleich vielen Tests', () => { - const exercises = (l: 'de' | 'en' | 'pt') => { - useContentLanguage(l) - return allBlocks().flatMap(({ block }) => - block.kind === 'exercise' ? [`${block.exercise.id}: ${block.exercise.tests.length}`] : [], - ).sort() - } - const de = exercises('de') - expect(exercises(lang)).toEqual(de) - useContentLanguage('de') - }) - - it('beide Sprachen haben gleich viele Lückentexte mit gleich vielen Lücken', () => { - const clozes = (l: 'de' | 'en' | 'pt') => { - useContentLanguage(l) - return allBlocks().flatMap(({ where, block }) => (block.kind === 'cloze' ? [`${where.split(' / ')[0]}: ${clozeGaps(block.cloze).length}`] : [])) - } - const de = clozes('de') - expect(clozes(lang)).toEqual(de) - useContentLanguage('de') - }) -}) - -describe('Lernbereich: Format', () => { - it('liest Tests mit Eingaben und mehreren Ausgabezeilen', () => { - expect(parseTest('3, 4 -> 7 | fertig', 'x')).toEqual({ inputs: ['3', '4'], expected: ['7', 'fertig'] }) - expect(parseTest('- -> Hallo', 'x')).toEqual({ inputs: [], expected: ['Hallo'] }) - }) - - it('liest Angeberwissen-Kästen', () => { - expect(parseBlocks('::: angeberwissen\nZeile eins\nZeile zwei\n:::', 'x.md')).toEqual([{ kind: 'brag', text: 'Zeile eins\nZeile zwei' }]) - }) - - it('meldet fehlerhafte Blöcke verständlich', () => { - expect(() => parseBlocks('::: diagramm a.visustruct\n', 'x.md')).toThrow(/nicht mit „:::“ geschlossen/) - expect(() => parseBlocks('::: quiz\n? Frage\n- [ ] a\n- [ ] b\n:::', 'x.md')).toThrow(/eine richtige/) - expect(() => parseBlocks('::: tabelle\n:::', 'x.md')).toThrow(/Unbekannter Block/) - }) - - it('liest Zuordnungen im Quiz', () => { - const [block] = parseBlocks('::: quiz\n? Ordne zu.\n= Fallauswahl -> Mehrfachauswahl\n= Schleife -> Wiederholungsstruktur\n> Erklärung\n:::', 'x.md') - expect(block).toEqual({ - kind: 'quiz', - questions: [{ - question: 'Ordne zu.', - options: [], - pairs: [{ left: 'Fallauswahl', right: 'Mehrfachauswahl' }, { left: 'Schleife', right: 'Wiederholungsstruktur' }], - explanation: 'Erklärung', - }], - }) - expect(() => parseBlocks('::: quiz\n? Ordne zu.\n= a -> b\n:::', 'x.md')).toThrow(/mindestens zwei Paare/) - expect(() => parseBlocks('::: quiz\n? Ordne zu.\n= a -> b\n= c -> b\n:::', 'x.md')).toThrow(/verschiedenen rechten/) - expect(() => parseBlocks('::: quiz\n? Ordne zu.\n= a b\n:::', 'x.md')).toThrow(/links -> rechts/) - }) - - it('liest bereich: und leitet den Bereich sonst aus code: ab', () => { - const ex = (extra: string) => { - const block = parseBlocks(`::: übung x\ntitel: T\nauftrag: A\ntest: - -> 1\n${extra}:::`, 'x.md')[0] - if (block.kind !== 'exercise') throw new Error('keine Übung') - return exerciseArea(block.exercise) - } - expect(ex('')).toBe('struktogramm') - expect(ex('code: java\n')).toBe('code') - expect(ex('bereich: beide\n')).toBe('beide') - expect(ex('code: java\nbereich: Beide\n')).toBe('beide') - expect(() => ex('bereich: links\n')).toThrow(/bereich:/) - }) - - it('löst Checklisten-Verweise auf und meldet unbekannte Schritte', () => { - const source = (ref: string) => `---\ntitel: T\n---\n## Erster Schritt\nText\n## Checkliste\n::: checkliste\n- Ich kann es. → ${ref}\n- Ohne Verweis\n:::\n` - const block = parseLesson(source('erster schritt'), '01-t.md').steps[1].blocks[0] - expect(block).toEqual({ kind: 'checklist', items: [{ text: 'Ich kann es.', ref: 'erster schritt', step: 0 }, { text: 'Ohne Verweis' }] }) - expect(() => parseLesson(source('Gibt es nicht'), '01-t.md')).toThrow(/unbekannten Schritt/) - }) - - it('liest Lückentexte', () => { - const [block] = parseBlocks( - '::: lückentext\ntitel: T\nauftrag: Ergänze.\n```java\n [[int]] x = 1;\n if (a [[||]] b) x[[++ | += 1]];\n```\nerklärung: E\n:::', - 'x.md', - ) - expect(block).toEqual({ - kind: 'cloze', - cloze: { - title: 'T', - task: 'Ergänze.', - blocks: [[[{ answers: ['int'] }, ' x = 1;'], ['if (a ', { answers: ['||'] }, ' b) x', { answers: ['++', '+= 1'] }, ';']]], - explanation: 'E', - }, - }) - expect(clozeAnswerRight(['i = i + 1'], 'i=i+1')).toBe(true) - expect(clozeAnswerRight(['int'], 'Int')).toBe(false) - expect(clozeAnswerRight(['int'], ' ')).toBe(false) - expect(() => parseBlocks('::: lückentext\ntitel: T\n```\nint x;\n```\n:::', 'x.md')).toThrow(/mindestens eine Lücke/) - expect(() => parseBlocks('::: lückentext\ntitel: T\n```\n[[int]] x;\n:::', 'x.md')).toThrow(/nicht mit/) - }) - - it('Markdown maskiert HTML', () => { - expect(markdownToHTML('x **fett** `a<b>x</b> fett a<b

    ') - expect(markdownToHTML('[x](javascript:alert(1))')).toContain('href="#"') - expect(markdownToHTML('[**Sequenz**]{.blau} und [rot]{.rot}')).toBe('

    Sequenz und [rot]{.rot}

    ') - }) -}) - -describe('Prüfen mit der Simulation', () => { - const doc = (lines: string[]) => ({ - caption: '', - elements: lines.map((t) => makeElement(ElementType.anweisung, { textLines: [t] })), - }) - - it('gibt Eingaben der Reihe nach und vergleicht die Ausgabe', () => { - const d = doc(['input: int a "a"', 'input: int b "b"', 'int s = a + b', 'output: s']) - expect(runWithInputs(d, ['3', '4']).outputs).toEqual(['7']) - expect(checkExercise(d, [{ inputs: ['3', '4'], expected: ['7'] }])[0].passed).toBe(true) - expect(checkExercise(d, [{ inputs: ['3', '4'], expected: ['8'] }])[0].passed).toBe(false) - expect(runWithInputs(d, ['3']).missingInput).toBe(true) - }) - - it('erkennt Fehler und vergleicht 2.0 wie 2', () => { - expect(runWithInputs(doc(['output: x']), []).error?.kind).toBe('unknownVariable') - expect(checkExercise(doc(['double x = 4.0 / 2', 'output: x']), [{ inputs: [], expected: ['2'] }])[0].passed).toBe(true) - }) -}) - -describe('Lernbereich: Grafiken', () => { - // Als geladen, zeigt der Browser ein SVG nur an, wenn es gültiges XML ist (z. B. `<` statt `<`). - const svgs = import.meta.glob('../lessons/bilder/**/*.svg', { query: '?raw', import: 'default', eager: true }) as Record - - it.each(Object.keys(svgs))('%s ist gültiges XML', (file) => { - const parsed = new DOMParser().parseFromString(svgs[file], 'image/svg+xml') - expect(parsed.getElementsByTagName('parsererror').length).toBe(0) - }) -}) - -describe('Lehrermodus', () => { - // Der richtige Code steht absichtlich nirgends im Repository, auch nicht hier. - it('bleibt mit einem falschen Code aus', async () => { - const { isTeacher, lockTeacher, unlockTeacher } = await import('../src/learn/teacher') - lockTeacher() - expect(await unlockTeacher('falsch')).toBe(false) - expect(await unlockTeacher('')).toBe(false) - expect(isTeacher()).toBe(false) - }) -}) - -describe('Lückentexte', () => { - const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - /** Jeder Codeblock mit der ersten Antwort in jeder Lücke, nur Lücke `gap` bekommt Antwort `answer`. */ - const filled = (cloze: Cloze, gap = -1, answer = 0) => { - let n = 0 - return cloze.blocks.map((lines) => lines.map((l) => l.map((p) => (typeof p === 'string' ? p : p.answers[n++ === gap ? answer : 0])).join('')).join('\n')) - } - const problems = (cloze: Cloze, gap = -1, answer = 0) => filled(cloze, gap, answer).flatMap((code) => parseLiveJava(code, labels).problems) - it.each(contentLanguages)('mit jeder richtigen Antwort ist der Java-Code fehlerfrei (%s)', (lang) => { - useContentLanguage(lang) - let checked = 0 - for (const { where, block } of allBlocks()) { - if (block.kind !== 'cloze') continue - const cloze = block.cloze - clozeGaps(cloze).forEach((answers, gi) => - answers.forEach((_, ai) => expect(problems(cloze, gi, ai), `${where}\n${filled(cloze, gi, ai).join('\n')}`).toEqual([])), - ) - checked++ - } - expect(checked).toBeGreaterThanOrEqual(8) - useContentLanguage('de') - }) -}) - -describe('Java-Beispiele mit „Im Editor öffnen“', () => { - it.each(contentLanguages)('```java editor lässt sich ohne Fehler parsen (%s)', (lang) => { - useContentLanguage(lang) - const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - let checked = 0 - for (const block of lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks))) { - if (block.kind !== 'markdown') continue - for (const m of block.text.matchAll(/^```java editor\n([\s\S]*?)^```/gm)) { - expect(parseLiveJava(m[1], labels).problems, m[1]).toEqual([]) - expect(markdownToHTML(m[0])).toContain('data-open="editor"') - checked++ - } - } - expect(checked).toBeGreaterThanOrEqual(1) - }) -}) - -describe('Bombe entschärfen', () => { - it.each(contentLanguages)('mit der verbesserten Zeile meldet Java keinen Fehler mehr (%s)', (lang) => { - useContentLanguage(lang) - const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - let checked = 0 - for (const block of lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks))) { - if (block.kind !== 'roundGame' || block.game.kind !== 'bombe') continue - for (const round of block.game.rounds) { - const lines = round.code.split('\n') - const wrong = lines[round.wrongLine! - 1] - expect(round.fix!.trim(), round.code).not.toBe(wrong.trim()) - lines[round.wrongLine! - 1] = wrong.match(/^\s*/)![0] + round.fix!.trim() - // CSV: Die richtige Zeile hat so viele Felder wie die Kopfzeile, die falsche nicht. - if (block.game.data === 'csv') { - const fields = (line: string) => line.split(';').length - expect(lines.every((l) => fields(l) === fields(lines[0])), lines.join('\n')).toBe(true) - expect(fields(wrong) === fields(lines[0]) && wrong.split(';').slice(1).every((v) => /^\d+$/.test(v.trim())), round.code).toBe(false) - } else expect(parseLiveJava(lines.join('\n'), labels).problems, lines.join('\n')).toEqual([]) - checked++ - } - } - expect(checked).toBeGreaterThanOrEqual(8) - }) -}) - -describe('Bombe: Teile einer Zeile (stelle:)', () => { - const show = (line: string) => bombParts(line).map((s) => (s.part ? `[${s.text}]` : s.text)).join('') - it('teilt for-Kopf, Bedingung, Zuweisung und Aufruf', () => { - expect(show(' for (int i = 10; i >= 1; i++) {')).toBe(' for ([int i = 10]; [i >= 1]; [i++]) {') - expect(show('if (punkte[i] > max) {')).toBe('if ([punkte[i] > max]) {') - expect(show('summe = summe + i;')).toBe('[summe] = [summe + i];') - expect(show('System.out.println(zahl + " x " + i);')).toBe('[System.out.println]([zahl] + [" x "] + [i]);') - expect(show('for (int i = 1, i <= 5, i++) {')).toBe('[for (int i = 1, i <= 5, i++) {]') - expect(show(' }')).toBe(' [}]') - }) - it('„stelle:“ muss ein Teil der falschen Zeile sein', () => { - const lines = (stelle: string) => ['titel: x', 'zeit: 20', 'runde:', ' for (int i = 0; i <= 5; i++) {', ' }', 'frage: ?', 'fehler: 1', `stelle: ${stelle}`, 'besser: for (int i = 0; i < 5; i++) {'] - expect(parseRoundGame('bombe', lines('i <= 5'), 't.md').rounds[0].wrongPart).toBe('i <= 5') - expect(() => parseRoundGame('bombe', lines('<= 5'), 't.md')).toThrow(/kein Teil der Zeile 1/) - }) -}) - -describe('Rundenspiele (Swing)', () => { - it('„reihenfolge“: Schritte stehen der Reihe nach da, ohne [x] und mindestens drei', () => { - const game = parseRoundGame('reihenfolge', ['titel: Spiel: Wer ist dran?', 'runde:', 'frage: Was zuerst?', '- Klick', '- `model.erhoehen()`', '- Anzeige', '> So.'], 't.md') - expect(game.rounds[0].options.map((o) => o.text)).toEqual(['Klick', '`model.erhoehen()`', 'Anzeige']) - expect(game.rounds[0].code).toBe('') - expect(() => parseRoundGame('reihenfolge', ['titel: x', 'runde:', 'frage: ?', '- a', '- b'], 't.md')).toThrow(/mindestens drei/) - expect(() => parseRoundGame('reihenfolge', ['titel: x', 'runde:', 'frage: ?', '- a', '- [x] b', '- c'], 't.md')).toThrow(/kein \[x\]/) - expect(() => parseRoundGame('reihenfolge', ['titel: x', 'runde:', 'frage: ?', '- a', '- a', '- c'], 't.md')).toThrow(/gleich/) - }) - - it('ein Zuhörer für mehrere Knöpfe mit getActionCommand, getSource und if/else', () => { - const win = parseSwingWindow( - [ - 'JLabel anzeige = new JLabel("");', - 'JButton eins = new JButton("1");', - 'JButton zwei = new JButton("2");', - 'JButton weg = new JButton("C");', - 'ActionListener taste = e -> {', - ' if (e.getSource() == weg) {', - ' anzeige.setText("");', - ' } else if (e.getActionCommand().equals("2")) anzeige.setText(anzeige.getText() + "zwei");', - ' else anzeige.setText(anzeige.getText() + e.getActionCommand());', - '};', - 'eins.addActionListener(taste);', - 'zwei.addActionListener(taste);', - 'weg.addActionListener(taste);', - ].join('\n'), - ) - expect(win.listeners.map((l) => [l.button, l.from, l.to])).toEqual([['eins', 4, 9], ['zwei', 4, 9], ['weg', 4, 9]]) - const steps = simulate(win, ['1', '2', '1']) - expect(steps.at(-1)!.texts.anzeige).toBe('1zwei1') - expect(simulate(win, ['1', 'C', '1']).at(-1)!.texts.anzeige).toBe('1') - }) - - it.each(contentLanguages)('„Wer hört zu?“: die richtige Antwort ergibt sich beim Nachspielen (%s)', (lang) => { - useContentLanguage(lang) - let checked = 0 - for (const block of lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks))) { - if (block.kind !== 'roundGame' || block.game.kind !== 'zuhoerer') continue - for (const round of block.game.rounds) { - const steps = simulate(parseSwingWindow(round.code), round.clicks) - const last = steps.at(-1)! - const results = [...Object.values(last.texts), String(steps.flatMap((s) => s.console).length)] - const right = round.options.find((o) => o.right)!.text - expect(results, `${round.code}\n→ ${right}`).toContain(right) - checked++ - } - } - expect(checked).toBeGreaterThan(5) - }) -}) - -describe('PAP bauen', () => { - const lines = (text: string) => text.trim().split('\n').map((l) => l.trim()) - - it('liest Rücksprung und Seitenkasten', () => { - const pap = parsePap( - lines(` - titel: Test - start: Start - anweisung: Rühren - frage: Glatt? | nein -> Rühren - frage: Fertig? | nein: Warten - ende: Ende - `), - 't.md', - ) - expect(pap.nodes.map((n) => n.shape)).toEqual(['oval', 'rechteck', 'raute', 'raute', 'rechteck', 'oval']) - expect(pap.nodes[2].backTo).toBe(1) - expect(pap.nodes[3].side).toBe(4) - expect(pap.nodes[4].isSide).toBe(true) - const { boxes } = papLayout(pap) - expect(boxes).toHaveLength(6) - expect(boxes[4].y).toBe(boxes[3].y) - }) - - it('meldet Rücksprung ohne Ziel und Fragen ohne zweiten Ausgang', () => { - expect(() => parsePap(['titel: x', 'start: S', 'frage: F? | nein -> Gibt es nicht', 'ende: E'], 't.md')).toThrow(/Rücksprung/) - expect(() => parsePap(['titel: x', 'start: S', 'frage: F?', 'ende: E'], 't.md')).toThrow(/zweiten Ausgang|braucht/) - }) - - it('gleiche Elemente sind austauschbar', () => { - const pap = parsePap(['titel: x', 'start: S', 'anweisung: A', 'anweisung: A', 'ende: E'], 't.md') - expect(papFits(pap, 1, 2)).toBe(true) - expect(papFits(pap, 0, 3)).toBe(false) - }) - - it('jede Sprache hat in Lektion 1 einen PAP zum Bauen', () => { - for (const lang of contentLanguages) { - useContentLanguage(lang) - const paps = allBlocks().filter(({ block }) => block.kind === 'pap') - expect(paps.length, lang).toBe(1) - expect(paps[0].block.kind === 'pap' && paps[0].block.pap.legend, lang).toBe(true) - } - }) -}) - -describe('Kompetenzen und Selbsteinschätzung', () => { - const lesson = (quizRef: string) => - parseLesson( - `---\ntitel: T\n---\n\n## Erster\n\n::: kompetenzen\n- etwas Neues\n:::\n\n## Checkliste\n\n::: checkliste\n- Ich kann etwas. → Erster\n:::\n\n## Test\n\n::: quiz\n? Frage\n→ ${quizRef}\n- [x] ja\n- [ ] nein\n:::\n`, - '01-t.md', - ) - - it('liest Kompetenzen und verbindet Quizfragen mit dem Checklistenpunkt', () => { - const l = lesson('Erster') - expect(l.steps[0].blocks[0]).toEqual({ kind: 'competences', items: ['etwas Neues'] }) - const quiz = l.steps[2].blocks[0] - expect(quiz.kind === 'quiz' && quiz.questions[0].step).toBe(0) - }) - - it('meldet Quizfragen ohne passenden Schritt oder Checklistenpunkt', () => { - expect(() => lesson('Gibt es nicht')).toThrow(/unbekannten Schritt/) - expect(() => lesson('Test')).toThrow(/keinen Checklistenpunkt/) - }) - - it('Lektion 1 ist geteilt: PAP und Pseudocode sind Lektion 2, die Verweise zählen mit', () => { - for (const lang of contentLanguages) { - useContentLanguage(lang) - expect(lessons[1].id, lang).toBe('01-weitere-darstellungen') - expect(lessons.find((l) => l.id === '02-der-editor')!.number, lang).toBe(3) - // „Lektion 18“ (Datei 18, Aussprung) heißt jetzt Lektion 19. - const text = JSON.stringify(lessons[1].steps) - expect(text, lang).toMatch(/\b(Lektion|lesson|lição) 19\b/i) - expect(text, lang).not.toMatch(/\b(Lektion|lesson|lição) 18\b/i) - } - }) - - it('Lektion 1 hat Kompetenzen und Ich-kann-Sätze in allen Sprachen', () => { - for (const lang of contentLanguages) { - useContentLanguage(lang) - const l1 = lessons.find((l) => l.number === 1)! - expect(l1.steps[0].blocks[0].kind, lang).toBe('competences') - } - }) -}) - -describe('Übungen „Warum läuft das nicht?“', () => { - it('der Start läuft wirklich nicht', () => { - const dir = join(__dirname, '..', 'lessons', 'diagramme') - const files = [dir, join(dir, 'en'), join(dir, 'pt')].flatMap((d) => readdirSync(d).filter((f) => f.endsWith('-laeuft-nicht-start.visustruct') || f === 'l04-gross-klein-start.visustruct').map((f) => join(d, f))) - expect(files.length).toBe(48) - for (const file of files) { - const doc = decodeDocument(readFileSync(file, 'utf8')) - expect(runWithInputs(doc, ['14']).error, file).toBeDefined() - } - }) -}) diff --git a/web/test/mbot.test.ts b/web/test/mbot.test.ts deleted file mode 100644 index 26bdc1c4..00000000 --- a/web/test/mbot.test.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { LiveLabels } from '../src/core/codeGenerator' -import { memberCompletionsAt } from '../src/core/javaMembers' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava } from '../src/core/liveSource' -import { compressDrive, driveSignature, parseDriveSpec, parseMBotCommand, substituteMBotSensors } from '../src/core/mbot' -import { checkExercise, loopDepth } from '../src/core/exerciseCheck' -import { SimulationEngine } from '../src/core/simulation' - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - -function run(java: string, sensors: Record = {}) { - const parsed = parseLiveJava(java, labels, {}) - expect(parsed.problems).toEqual([]) - const engine = new SimulationEngine(documentFromJava({ caption: '', elements: [] }, parsed)) - engine.robotSensors = sensors - for (let i = 0; i < 1000 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted, engine.message).toBe(false) - return engine.state -} - -const main = (body: string) => `public class Main {\n public static void main(String[] args) {\n${body}\n }\n}` - -describe('mBot2', () => { - it('erkennt Befehle mit Argumenten', () => { - expect(parseMBotCommand('mbot.setLed(255, 0, x + 1)')).toEqual({ method: 'setLed', args: ['255', '0', 'x + 1'] }) - expect(parseMBotCommand('mbot.stop()')).toEqual({ method: 'stop', args: [] }) - expect(parseMBotCommand('x = 3')).toBe(undefined) - }) - - it('ersetzt Sensoren durch Zahlen', () => { - expect(substituteMBotSensors('mbot.getDistance() < 20', () => 15)).toBe('15 < 20') - expect(substituteMBotSensors('mbot.unknown()', () => 1)).toBe('mbot.unknown()') - }) - - it('überlädt forward und backward mit Einheit', () => { - const state = run(main(` mbot.forward(2, "Umdrehungen");\n mbot.forward(30, "cm");\n mbot.backward(1, "rotations");\n mbot.forward(15);`)) - expect(state.robotActions.map((a) => a.python)).toEqual(['mbot2.straight(40.84)', 'mbot2.straight(30)', 'mbot2.straight(-20.42)', 'mbot2.straight(15)']) - expect(state.robotActions[0].label).toBe('2 Umdrehungen vorwärts (40.84 cm)') - expect(state.robotActions[2].label).toBe('1 rotation rückwärts (20.42 cm)') - expect(driveSignature(state.robotActions)).toEqual(['forward 65.42']) - }) - - it('meldet eine unbekannte Einheit', () => { - const parsed = parseLiveJava(main(' mbot.forward(2, "Meter");'), labels, {}) - const engine = new SimulationEngine(documentFromJava({ caption: '', elements: [] }, parsed)) - for (let i = 0; i < 100 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted).toBe(true) - expect(engine.message).toContain('Umdrehungen') - }) - - it('fährt ein Quadrat und merkt sich die Python-Zeilen', () => { - const state = run(main(` for (int i = 0; i < 4; i++) {\n mbot.forward(20);\n mbot.turnRight(90);\n }`)) - expect(state.robotActions.map((a) => a.python)).toEqual(Array(4).fill(['mbot2.straight(20)', 'mbot2.turn(90)']).flat()) - expect(state.outputLines).toEqual([]) - }) - - it('liest den Abstand in Bedingungen und Variablen', () => { - const state = run(main(` int abstand = mbot.getDistance();\n if (mbot.getDistance() < 20) {\n mbot.stop();\n } else {\n mbot.forward(abstand - 20);\n }`), { getDistance: 15 }) - expect(state.robotActions.map((a) => a.method)).toEqual(['stop']) - expect(run(main(` int abstand = mbot.getDistance();\n if (mbot.getDistance() < 20) {\n mbot.stop();\n } else {\n mbot.forward(abstand - 20);\n }`)).robotActions[0].python).toBe('mbot2.straight(80)') - }) - - it('meldet falsche Befehle als Fehler', () => { - const engine = new SimulationEngine(documentFromJava({ caption: '', elements: [] }, parseLiveJava(main(' mbot.fly(3);'), labels, {}))) - engine.step() - expect(engine.state.isHalted).toBe(true) - }) - - it('schlägt nach mbot. die Befehle vor', () => { - const code = main(' mbot.') - const names = memberCompletionsAt(code, code.indexOf('mbot.') + 5)?.members.map((m) => m.name) - expect(names).toContain('forward') - expect(names).toContain('getDistance') - }) - - it('fasst die Fahrt zusammen und liest erwartete Fahrten', () => { - const square = parseDriveSpec('4 x (5 x (forward), turnRight)') - expect(driveSignature(square)).toEqual(Array(4).fill(['forward 50', 'turnRight 90']).flat()) - expect(driveSignature(parseDriveSpec('turnLeft 90, turnLeft 90, forward 10, backward 10'))).toEqual(['turnRight 180']) - expect(compressDrive(driveSignature(parseDriveSpec('4 x (forward 50, turnRight 90)')))).toBe('4 x (forward 50, turnRight 90)') - expect(() => parseDriveSpec('fly 3')).toThrow() - }) - - it('prüft eine Übung über die Fahrt und die Schleifentiefe', () => { - const parsed = parseLiveJava(main(' for (int i = 0; i < 4; i++) {\n for (int k = 0; k < 5; k++) {\n mbot.forward();\n }\n mbot.turnRight();\n }'), labels, {}) - const doc = documentFromJava({ caption: '', elements: [] }, parsed) - const drive = driveSignature(parseDriveSpec('4 x (forward 50, turnRight 90)')) - expect(checkExercise(doc, [{ inputs: [], expected: [], drive }])[0].passed).toBe(true) - expect(checkExercise(doc, [{ inputs: [], expected: [], drive: drive.slice(2) }])[0].passed).toBe(false) - expect(loopDepth(doc.elements)).toBe(2) - }) -}) diff --git a/web/test/mbotLink.test.ts b/web/test/mbotLink.test.ts deleted file mode 100644 index 3b4a619d..00000000 --- a/web/test/mbotLink.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { ASK, pythonFrame, readAnswers, RUN } from '../src/core/mbotLink' - -const bytes = (hex: string) => hex.split(' ').map((h) => parseInt(h, 16)) -const hex = (a: Uint8Array) => Array.from(a, (b) => b.toString(16).padStart(2, '0')).join(' ') - -// Rahmen aus Holgers Protokoll mit dem echten mBot2 (25.10.2026) -describe('mBot2 über USB', () => { - it('verpackt Python-Zeilen wie die Firmware sie erwartet', () => { - expect(hex(pythonFrame('mbot2.turn(90)', RUN, 8))).toBe('f3 07 14 00 28 04 08 00 0e 00 6d 62 6f 74 32 2e 74 75 72 6e 28 39 30 29 d7 f4') - expect(hex(pythonFrame('mbuild.ultrasonic2.get(1)', ASK, 10))).toBe( - 'f3 12 1f 00 28 01 0a 00 19 00 6d 62 75 69 6c 64 2e 75 6c 74 72 61 73 6f 6e 69 63 32 2e 67 65 74 28 31 29 5d f4', - ) - }) - - it('liest Antworten, auch zerstückelt und nach Textmüll', () => { - const answer = (body: string) => { - const b = bytes(body) - const len = b.length - return [0xf3, (0xf3 + len) & 0xff, len, 0, ...b, b.reduce((x, y) => x + y, 0) & 0xff, 0xf4] - } - const distance = answer('28 01 0a 00 0c 00 7b 22 72 65 74 22 3a 39 33 2e 39 7d') - const none = answer('28 01 02 00 0c 00 7b 22 72 65 74 22 3a 4e 6f 6e 65 7d') - const ping = answer('0d 00 00') - const rx = [0x29, 0x81, ...ping, ...distance.slice(0, 7)] - expect(readAnswers(rx)).toEqual([]) - rx.push(...distance.slice(7), ...none) - expect(readAnswers(rx)).toEqual([ - { idx: 10, value: 93.9 }, - { idx: 2, value: null }, - ]) - expect(rx).toEqual([]) - }) -}) diff --git a/web/test/mbotProgram.test.ts b/web/test/mbotProgram.test.ts deleted file mode 100644 index e7e30171..00000000 --- a/web/test/mbotProgram.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { MBOT_COMMANDS, MBOT_SENSORS } from '../src/core/mbot' -import { frame, pythonFrame, RUN, UPLOAD_PREPARE, uploadFrames } from '../src/core/mbotLink' -import { javaConcatenation, mbotProgram } from '../src/core/mbotProgram' -import { ElementType, type VSDocument } from '../src/core/model' -import { diagram, lessons, tasks, useContentLanguage, useVisuBotContent } from '../src/learn/content' -// Rahmen, die mBlock 5 beim Hochladen schickt (aus github.com/weare2plus3/cyberpi-uploader, MIT) -import fixture from './mbotUpload.fixture.json' - -const hex = (bytes: Uint8Array) => Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('') - -describe('Übertragen auf den mBot2', () => { - it('schickt dieselben Rahmen wie mBlock', () => { - expect(hex(frame([0x0d, 0x00, 0x00]))).toBe(fixture.PING) - expect(hex(pythonFrame(UPLOAD_PREPARE[0], RUN, 0))).toBe(fixture.EXEC1) - expect(hex(pythonFrame(UPLOAD_PREPARE[1], RUN, 0))).toBe(fixture.EXEC2) - const source = new TextDecoder().decode(Uint8Array.from(fixture.source.match(/../g)!.map((h) => parseInt(h, 16)))) - expect(uploadFrames(source).map(hex)).toEqual(fixture.frames) - }) - - it('verkettet Text wie Java', () => { - expect(javaConcatenation('mbot.showText("Akku: " + mbot.getBattery() + " %")')).toBe('mbot.showText("Akku: " + str(mbot.getBattery()) + " %")') - expect(javaConcatenation('mbot.showText(a + b + "x")')).toBe('mbot.showText(str(a + b) + "x")') - expect(javaConcatenation('x = "a" + len(t)')).toBe('x = "a" + str(len(t))') - expect(javaConcatenation('if a + 1 > 3:')).toBe('if a + 1 > 3:') - expect(javaConcatenation('print("Summe: " + str(summe))')).toBe('print("Summe: " + str(summe))') - expect(javaConcatenation('mbot.forward(i * 10 + 5)')).toBe('mbot.forward(i * 10 + 5)') - }) - - it('macht aus der Fallauswahl if/elif, MicroPython kennt kein match', () => { - const doc: VSDocument = { - caption: '', - elements: [ - { - type: ElementType.fallauswahl, - textLines: ['mbot.getColor()'], - cases: [ - { name: 'rot', elements: [{ type: ElementType.anweisung, textLines: ['mbot.turnRight()'] }] }, - { name: 'blau', elements: [{ type: ElementType.anweisung, textLines: ['mbot.turnLeft()'] }] }, - { name: 'sonst', elements: [{ type: ElementType.anweisung, textLines: ['mbot.forward()'] }] }, - ], - }, - ], - } as VSDocument - const r = mbotProgram(doc, [], 'de') - expect('source' in r && r.source).toContain(' wahl1 = mbot.getColor()\n if wahl1 == "rot":\n mbot.turnRight()\n elif wahl1 == "blau":') - expect('source' in r && r.source).not.toContain('match ') - }) - - it('kennt jeden Befehl und Sensor und startet mit Taste A', () => { - const r = mbotProgram({ caption: '', elements: [{ type: ElementType.anweisung, textLines: ['mbot.forward()'] }] } as VSDocument, [], 'de') - if (!('source' in r)) throw new Error(r.error) - for (const name of [...Object.keys(MBOT_COMMANDS).filter((n) => n !== 'ledColor'), ...Object.keys(MBOT_SENSORS)]) expect(r.source).toContain(` def ${name}(self`) - expect(r.source).toContain('@event.is_press("a")') - expect(r.source).toContain('A = Start') - expect(r.source).toContain('def programm():\n mbot.forward()') - expect(r.source).toContain('"red": "rot"') - const en = mbotProgram({ caption: '', elements: [{ type: ElementType.anweisung, textLines: ['mbot.forward()'] }] } as VSDocument, [], 'en') - expect('source' in en && en.source).toContain('"red": "red"') - }) - - it('lehnt leere Programme und Eingaben ab', () => { - expect(mbotProgram({ caption: '', elements: [] } as VSDocument, [], 'de')).toEqual({ error: 'empty' }) - expect(mbotProgram({ caption: '', elements: [{ type: ElementType.anweisung, textLines: ['input: int zahl'] }] } as VSDocument, [], 'de')).toEqual({ error: 'input' }) - }) - - describe('Musterlösungen', () => { - beforeAll(() => { - useVisuBotContent('on') - useContentLanguage('de') - }) - afterAll(() => useVisuBotContent('off')) - - it('jede VisuBot-Musterlösung wird ein Programm (außer mit Eingabe)', () => { - const blocks = [...lessons.filter((l) => l.robot).flatMap((l) => l.steps.flatMap((s) => s.blocks)), ...tasks.filter((t) => t.robot).flatMap((t) => t.blocks)] - let count = 0 - for (const b of blocks) { - if (b.kind !== 'exercise' || !b.exercise.solution) continue - const r = mbotProgram(diagram(b.exercise.solution)!, b.exercise.subprograms.map((f) => diagram(f)!), 'de') - if ('error' in r) { - expect(r.error, b.exercise.id).toBe('input') - continue - } - expect(r.source, b.exercise.id).not.toMatch(/\bmatch |empty element\n(?!\s*pass)/u) - count++ - } - expect(count).toBeGreaterThan(50) - }) - }) -}) diff --git a/web/test/mbotUpload.fixture.json b/web/test/mbotUpload.fixture.json deleted file mode 100644 index e709ecbf..00000000 --- a/web/test/mbotUpload.fixture.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "PING": "f3f603000d00000df4", - "EXEC1": "f3202d002804000027007472793a0a20202020696d706f727420636f6e6669670a6578636570743a0a2020202070617373b5f4", - "EXEC2": "f33c49002804000043007472793a0a20202020636f6e6669672e77726974655f636f6e66696728227265706c5f656e61626c65222c2046616c7365290a6578636570743a0a202020207061737389f4", - "source": "232048656c6c6f20437962657250693a2074657874206f6e2073637265656e2c204c45447320616e64206120736f756e642e0a696d706f7274206576656e742c20637962657270690a0a0a406576656e742e73746172740a646566206f6e5f737461727428293a0a20202020637962657270692e646973706c61792e636c65617228290a20202020637962657270692e646973706c61792e73686f775f6c6162656c282248656c6c6f21222c2031362c202263656e74657222290a20202020637962657270692e6c65642e6f6e28302c203235352c203029202020202020202020202320616c6c204c45447320677265656e0a20202020637962657270692e617564696f2e706c6179282268656c6c6f22290a", - "frames": [ - "f314210001005e011b000044010000426748612f666c6173682f5f78785f6d61696e2e7079e8f4", - "f34d5a0001005e02540000000000232048656c6c6f20437962657250693a2074657874206f6e2073637265656e2c204c45447320616e64206120736f756e642e0a696d706f7274206576656e742c20637962657270690a0a0a406576656e82f4", - "f34d5a0001005e02540050000000742e73746172740a646566206f6e5f737461727428293a0a20202020637962657270692e646973706c61792e636c65617228290a20202020637962657270692e646973706c61792e73686f775f6c61625ef4", - "f34d5a0001005e025400a0000000656c282248656c6c6f21222c2031362c202263656e74657222290a20202020637962657270692e6c65642e6f6e28302c203235352c203029202020202020202020202320616c6c204c45447320677265e1f4", - "f34d5a0001005e025400f0000000656e0a20202020637962657270692e617564696f2e706c6179282268656c6c6f22290a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a89f4", - "f3010e0001005e020800400100000a0a0a0ad2f4" - ] -} \ No newline at end of file diff --git a/web/test/memberEdit.test.ts b/web/test/memberEdit.test.ts deleted file mode 100644 index 11e1aff4..00000000 --- a/web/test/memberEdit.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { parseClasses } from '../src/core/classModel' -import { addFieldConstructor, addGettersSetters, addToString, classDoc, fieldInfo, parseParams, setClassDoc, setField, setMethod } from '../src/core/memberEdit' - -const src = `/** - * Ein Hund. - */ -public class Hund { - private String name = "Bello"; - private static int anzahl; - - public void bellen() { - System.out.println("Wuff"); - } -} -` -const cls = (s: string) => parseClasses(s)[0] - -describe('memberEdit', () => { - it('liest und schreibt Attribute mit Startwert und final', () => { - const f = cls(src).members[0] - expect(fieldInfo(src, f)).toEqual({ visibility: '-', name: 'name', type: 'String', isStatic: false, isFinal: false, init: '"Bello"' }) - const out = setField(src, f, { isFinal: true, visibility: '#' }) - expect(out).toContain(' protected final String name = "Bello";\n') - expect(fieldInfo(out, cls(out).members[0]).isFinal).toBe(true) - }) - - it('ändert den Methodenkopf, der Rumpf bleibt; abstract macht ;', () => { - const m = cls(src).members[2] - let out = setMethod(src, cls(src), m, { type: 'String', name: 'laut', params: parseParams('mal: int')! }) - expect(out).toContain('public String laut(int mal) {\n System.out.println("Wuff");') - out = setMethod(out, cls(out), cls(out).members[2], { isAbstract: true }) - expect(out).toContain('public abstract String laut(int mal);') - out = setMethod(out, cls(out), cls(out).members[2], { isAbstract: false }) - expect(out).toContain('public String laut(int mal) {\n return null;\n }') - }) - - it('Javadoc lesen und schreiben', () => { - expect(classDoc(src, cls(src))).toBe('Ein Hund.') - const out = setClassDoc(src, cls(src), 'Ein treuer Hund.\n@author Holger') - expect(out.startsWith('/**\n * Ein treuer Hund.\n * @author Holger\n */\npublic class Hund {')).toBe(true) - expect(setClassDoc(out, cls(out), '').startsWith('public class Hund {')).toBe(true) - }) - - it('erzeugt Konstruktor, Getter/Setter und toString wie Eclipse', () => { - let out = addFieldConstructor(src, cls(src)) - expect(out).toContain(' public Hund(String name) {\n this.name = name;\n }\n}') - out = addGettersSetters(out, cls(out)) - expect(out).toContain('public String getName() {\n return name;\n }') - expect(out).toContain('public void setName(String name) {\n this.name = name;\n }') - expect(addGettersSetters(out, cls(out))).toBe(out) - out = addToString(out, cls(out)) - expect(out).toContain('return "Hund [" + "name=" + name + "]";') - expect(parseClasses(out)[0].members.map((m) => m.name)).toEqual(['name', 'anzahl', 'bellen', 'Hund', 'getName', 'setName', 'toString']) - }) -}) diff --git a/web/test/miniJava.test.ts b/web/test/miniJava.test.ts deleted file mode 100644 index d542c3b4..00000000 --- a/web/test/miniJava.test.ts +++ /dev/null @@ -1,283 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { MiniJava, MiniJavaError, formatDouble } from '../src/core/miniJava' - -function run(files: Record | string, input: string[] = []) { - const list = typeof files === 'string' ? [{ path: 'Main.java', source: files }] : Object.entries(files).map(([path, source]) => ({ path, source })) - let out = '' - const vm = new MiniJava(list, { out: (t) => (out += t) }) - vm.start() - vm.go('run') - for (let guard = 0; guard < 1000; guard++) { - const s = vm.run(new Set(), 100000) - if (s.k === 'input') vm.input(input.length ? input.shift()! : null) - else if (s.k === 'sleep' || s.k === 'running') continue - else if (s.k === 'exception') return out + s.text - else if (s.k === 'error') throw s.error - else return out - } - throw new Error('läuft zu lange') -} - -const main = (body: string, extra = '') => `public class Main {\n${extra}\npublic static void main(String[] args) {\n${body}\n}\n}` - -describe('miniJava', () => { - it('rechnet wie Java', () => { - expect(run(main('System.out.println(7 / 2); System.out.println(7 / 2.0); System.out.println(7 % 3); System.out.println(1 + 2 + "x" + 1 + 2);'))).toBe('3\n3.5\n1\n3x12\n') - expect(run(main("double d = 5; System.out.println(d); int i = (int) 3.9; System.out.println(i); char c = 'A'; c++; System.out.println(c); System.out.println(c + 1);"))).toBe('5.0\n3\nB\n67\n') - expect(run(main('System.out.println(Integer.MAX_VALUE + 1); System.out.println(0.1 + 0.2); System.out.println(1e7);'))).toBe('-2147483648\n0.30000000000000004\n1.0E7\n') - expect(run(main('int x = 5; x += 2.7; System.out.println(x); String s = "a"; s += 1; System.out.println(s);'))).toBe('7\na1\n') - }) - - it('Schleifen, switch, Arrays', () => { - const src = 'import java.util.Arrays;\n' + main(` - int summe = 0; - for (int i = 1; i <= 10; i++) { if (i % 2 == 0) continue; summe += i; } - System.out.println(summe); - int[] a = {3, 1, 2}; - Arrays.sort(a); - System.out.println(Arrays.toString(a) + " " + a.length); - int[][] g = new int[2][3]; - g[1][2] = 4; - System.out.println(g[1][2] + g[0][0]); - String tag = "Mo"; - switch (tag) { case "Mo": System.out.println("Montag"); case "Di": System.out.println("weiter"); break; default: System.out.println("nein"); } - int k = 0; while (true) { k++; if (k > 3) break; } System.out.println(k); - do { k--; } while (k > 0); System.out.println(k); - `) - expect(run(src)).toBe('25\n[1, 2, 3] 3\n4\nMontag\nweiter\n4\n0\n') - }) - - it('Klassen, Konstruktoren, Vererbung, toString', () => { - const files = { - 'Tier.java': `public abstract class Tier { - private String name; - protected int beine = 4; - public Tier(String name) { this.name = name; } - public String getName() { return name; } - public abstract String laut(); - public String toString() { return name + " sagt " + laut(); } - }`, - 'Hund.java': `public class Hund extends Tier { - public Hund(String name) { super(name); } - public String laut() { return "Wuff"; } - }`, - 'Vogel.java': `public class Vogel extends Tier { - public Vogel() { this("Piepmatz"); beine = 2; } - public Vogel(String n) { super(n); } - @Override public String laut() { return "Piep"; } - public String toString() { return super.toString() + " (" + beine + " Beine)"; } - }`, - 'Main.java': `import java.util.ArrayList; - public class Main { - static int zaehler = 0; - public static void main(String[] args) { - ArrayList tiere = new ArrayList<>(); - tiere.add(new Hund("Bello")); - tiere.add(new Vogel()); - for (Tier t : tiere) { System.out.println(t); zaehler++; } - System.out.println(zaehler + " " + tiere.size() + " " + (tiere.get(0) instanceof Hund)); - Tier t = tiere.get(1); - Vogel v = (Vogel) t; - System.out.println(v.getName()); - } - }`, - } - expect(run(files)).toBe('Bello sagt Wuff\nPiepmatz sagt Piep (2 Beine)\n2 2 true\nPiepmatz\n') - }) - - it('Exceptions mit try/catch/finally und ungefangen', () => { - const src = main(` - try { int x = 5 / 0; } catch (ArithmeticException e) { System.out.println("Fehler: " + e.getMessage()); } finally { System.out.println("fertig"); } - try { throw new KontoException("leer"); } catch (Exception e) { System.out.println(e.getMessage()); } - int[] a = new int[2]; - a[2] = 1; - `) - const files = { 'Main.java': src, 'KontoException.java': 'public class KontoException extends Exception { public KontoException(String m) { super(m); } }' } - expect(run(files)).toBe('Fehler: / by zero\nfertig\nleer\nException in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2\n\tat Main.main(Main.java:8)') - }) - - it('Scanner mit nextInt und nextLine wie in Java', () => { - const src = `import java.util.Scanner; - public class Main { public static void main(String[] args) { - Scanner sc = new Scanner(System.in); - System.out.print("Zahl: "); - int z = sc.nextInt(); - String rest = sc.nextLine(); - String name = sc.nextLine(); - double d = sc.nextDouble(); - System.out.println(z * 2 + "|" + rest + "|" + name + "|" + d); - } }` - expect(run(src, ['21', 'Anna Lena', '2,5'])).toBe('Zahl: 42||Anna Lena|2.5\n') - }) - - it('printf und String-Methoden', () => { - expect( - run(main('System.out.printf("%5.2f|%-4d|%s%n", 3.14159, 7, "x"); String s = "Hallo"; System.out.println(s.charAt(1) + s.substring(2).toUpperCase() + s.indexOf("l") + s.equals("Hallo"));')), - ).toBe(' 3,14|7 |x\naLLO2true\n') - }) - - it('statische Methoden, Rekursion, enum', () => { - const src = `public class Main { - enum Farbe { ROT, GRUEN } - static int fak(int n) { return n <= 1 ? 1 : n * fak(n - 1); } - public static void main(String[] args) { - System.out.println(fak(5)); - Farbe f = Farbe.GRUEN; - switch (f) { case ROT: System.out.println("r"); break; case GRUEN: System.out.println("g"); break; } - System.out.println(f + " " + f.ordinal()); - } - }` - expect(() => run(src)).toThrow(MiniJavaError) // innere Klassen kann der Debugger nicht - const files = { 'Farbe.java': 'public enum Farbe { ROT, GRUEN }', 'Main.java': src.replace('enum Farbe { ROT, GRUEN }', '') } - expect(run(files)).toBe('120\ng\nGRUEN 1\n') - }) - - it('meldet Endlosrekursion als StackOverflowError', () => { - expect(run(main('f();', 'static void f() { f(); }'))).toMatch(/^Exception in thread "main" java.lang.StackOverflowError/) - }) - - it('hält an Haltepunkten und geht Schritt für Schritt', () => { - const src = `public class Main { - public static void main(String[] args) { - Konto k = new Konto(); - k.einzahlen(5); - k.einzahlen(7); - System.out.println(k.stand); - } -}` - const konto = `public class Konto { - int stand; - void einzahlen(int b) { - stand = stand + b; - } -}` - let out = '' - const vm = new MiniJava( - [ - { path: 'Main.java', source: src }, - { path: 'Konto.java', source: konto }, - ], - { out: (t) => (out += t) }, - ) - vm.start() - vm.go('run') - const bp = new Set(['Konto.java:3']) - let s = vm.run(bp) - expect(s).toEqual({ k: 'paused', pos: { file: 'Konto.java', line: 3 } }) - let stack = vm.stack() - expect(stack.map((f) => f.title)).toEqual(['Konto.einzahlen()', 'Main.main()']) - expect(stack[0].vars).toEqual([ - { name: 'this', type: 'Konto', value: 'k', ref: expect.any(Number) }, - { name: 'b', type: 'int', value: '5' }, - ]) - expect(vm.heap()).toEqual([{ id: expect.any(Number), name: 'k', cls: 'Konto', fields: [{ name: 'stand', type: 'int', value: '0' }] }]) - // Schritt: schließende Klammer der Methode, dann zurück in main - vm.go('into') - expect(vm.run(bp)).toEqual({ k: 'paused', pos: { file: 'Konto.java', line: 4 } }) - expect(vm.heap()[0].fields[0].value).toBe('5') - vm.go('out') - expect(vm.run(bp)).toEqual({ k: 'paused', pos: { file: 'Main.java', line: 4 } }) - vm.go('over') - // Ein Haltepunkt in der Methode hält auch beim Überspringen an (wie in Eclipse). - expect(vm.run(bp)).toEqual({ k: 'paused', pos: { file: 'Konto.java', line: 3 } }) - vm.go('out') - expect(vm.run(bp)).toEqual({ k: 'paused', pos: { file: 'Main.java', line: 5 } }) - expect(vm.heap()[0].fields[0].value).toBe('12') - vm.go('run') - s = vm.run(bp) - expect(s.k).toBe('done') - expect(out).toBe('12\n') - stack = vm.stack() - expect(stack).toEqual([]) - }) - - it('benennt Objekte nach Variablen und Attributen', () => { - const src = `import java.util.ArrayList; -public class Main { - public static void main(String[] args) { - Team t = new Team(); - t.add(new Spieler("Anna")); - t.add(new Spieler("Ben")); - Spieler s1 = t.liste.get(0); - System.out.println(s1); - } -} -class Team { ArrayList liste = new ArrayList<>(); Spieler kapitaen; void add(Spieler s) { liste.add(s); kapitaen = s; } } -class Spieler { String name; Spieler(String n) { name = n; } public String toString() { return name; } }` - const vm = new MiniJava([{ path: 'Main.java', source: src }], { out: () => {} }) - vm.start() - vm.go('run') - expect(vm.run(new Set(['Main.java:6']))).toMatchObject({ k: 'paused' }) - const heap = vm.heap() - expect(heap.map((o) => `${o.name}:${o.cls}`)).toEqual(['t:Team', 'spieler:Spieler', 'kapitaen:Spieler']) - expect(heap[0].fields).toEqual([ - { name: 'liste', type: 'ArrayList', value: '[spieler, kapitaen]' }, - { name: 'kapitaen', type: 'Spieler', value: 'kapitaen' }, - ]) - expect(heap[1].fields).toEqual([{ name: 'name', type: 'String', value: '"Anna"' }]) - }) - - it('formatiert Kommazahlen wie Java', () => { - expect([1, 0.5, 100, 1e-4, 12345678.9, -2].map(formatDouble)).toEqual(['1.0', '0.5', '100.0', '1.0E-4', '1.23456789E7', '-2.0']) - }) - - it('schneidet Aufrufe für das Sequenzdiagramm mit', () => { - const src = `public class Main { - public static void main(String[] args) { - Team t = new Team(); - Spieler s1 = new Spieler("Anna"); - t.aufnehmen(s1); - System.out.println(t.anzahl()); - } -} -class Team { int n; void aufnehmen(Spieler s) { n++; s.begruessen(); } int anzahl() { return n; } } -class Spieler { String name; Spieler(String n) { name = n; } void begruessen() { } }` - const vm = new MiniJava([{ path: 'Main.java', source: src }], { out: () => {} }) - vm.start() - vm.go('run') - expect(vm.run(new Set()).k).toBe('done') - const { participants, events } = vm.sequence() - expect(participants.map((p) => p.label)).toEqual(['Main', 't : Team', 's1 : Spieler']) - const key = Object.fromEntries(participants.map((p) => [p.key, p.label])) - expect(events.map((e) => (e.k === 'return' ? `return ${e.label}${e.hidden ? ' (hidden)' : ''}` : `${key[e.from]} -> ${key[e.to]}: ${e.label}`))).toEqual([ - 'Main -> t : Team: new Team()', - 'return (hidden)', - 'Main -> s1 : Spieler: new Spieler("Anna")', - 'return (hidden)', - 'Main -> t : Team: aufnehmen(s1)', - 't : Team -> s1 : Spieler: begruessen()', - 'return ', - 'return ', - 'Main -> t : Team: anzahl()', - 'return 1', - ]) - }) - - it('Scanner ohne Import ist wie bei javac ein Fehler', () => { - const body = 'Scanner eingabe = new Scanner(System.in); System.out.println(eingabe.next());' - let err: unknown - try { - run(main(body), ['Hallo']) - } catch (e) { - err = e - } - expect(err).toBeInstanceOf(MiniJavaError) - expect((err as MiniJavaError).code).toBe('import') - expect((err as MiniJavaError).detail).toBe('import java.util.Scanner;') - expect(run('import java.util.Scanner;\n' + main(body), ['Hallo'])).toBe('Hallo\n') - expect(run('import java.util.*;\n' + main(body), ['Hallo'])).toBe('Hallo\n') - }) - - it('boolean ohne Startwert und vertippte Datentypen wie bei javac', () => { - expect(run(main('boolean b; b = 3 > 2; System.out.println(b == true); boolean c = !b || false; if (!c) System.out.println(c);'))).toBe('true\nfalse\n') - let err: unknown - try { - run(main('booelan wpfAlleUeber5 = true;')) - } catch (e) { - err = e - } - expect((err as MiniJavaError).code).toBe('typename') - expect((err as MiniJavaError).detail).toBe('boolean') - expect(run({ 'Main.java': main('Kind k = new Kind(); System.out.println(k.name);'), 'Kind.java': 'public class Kind { String name = "Mia"; }' })).toBe('Mia\n') - }) -}) diff --git a/web/test/objectModel.test.ts b/web/test/objectModel.test.ts deleted file mode 100644 index 0d83f945..00000000 --- a/web/test/objectModel.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { parseClasses } from '../src/core/classModel' -import { freeObjectName, isA, linkedValue, objectFields, refNames } from '../src/core/objectModel' - -const classes = parseClasses(`class Person { private String name; static int anzahl; } -class Spieler extends Person { private int punkte; private Team team; private ArrayList freunde; } -class Team { }`) - -describe('Objektdiagramm', () => { - it('Attribute samt Oberklasse, ohne static', () => { - expect(objectFields(classes, 'Spieler').map((f) => `${f.name}:${f.target ?? ''}:${f.many}`)).toEqual(['name::false', 'punkte::false', 'team:Team:false', 'freunde:Spieler:true']) - }) - it('Referenzen lesen und verbinden', () => { - expect(refNames('[s1, s2]')).toEqual(['s1', 's2']) - expect(refNames('null')).toEqual([]) - const [, , team, freunde] = objectFields(classes, 'Spieler') - expect(linkedValue(team, 'null', 't1')).toBe('t1') - expect(linkedValue(freunde, '[s1]', 's2')).toBe('[s1, s2]') - expect(linkedValue(freunde, '[s1]', 's1')).toBe('[s1]') - }) - it('Vererbung und Namen', () => { - expect(isA(classes, 'Spieler', 'Person')).toBe(true) - expect(isA(classes, 'Team', 'Person')).toBe(false) - expect(freeObjectName('Spieler', ['spieler1'])).toBe('spieler2') - }) -}) diff --git a/web/test/objectNew.test.ts b/web/test/objectNew.test.ts deleted file mode 100644 index 9968224f..00000000 --- a/web/test/objectNew.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { exampleProject, projectClasses } from '../src/core/javaProject' -import { classSource } from '../src/ui/objectNew' - -const zoo = projectClasses(exampleProject()) -const tier = zoo.find((c) => c.name === 'Tier')! - -describe('objectNew', () => { - it('schreibt eine Klasse wie der Eclipse-Assistent', () => { - const text = classSource({ kind: 'class', pkg: 'haus', name: 'Katze', isPublic: true, isAbstract: false, isFinal: false, superclass: tier, interfaces: [], main: true, constructors: true, inherited: true, comment: true, author: 'Holger' }) - expect(text).toContain('package haus;') - expect(text).toContain('import tiere.Tier;') - expect(text).toContain('@author Holger') - expect(text).toContain('public class Katze extends Tier {') - expect(text).toContain('public Katze(String name, int alter) {\n super(name, alter);') - expect(text).toContain('public static void main(String[] args) {') - expect(projectClasses({ name: 'x', packages: [], files: [{ path: 'haus/Katze.java', text }] })[0].superclass).toBe('Tier') - }) - - it('Interface mit extends, Klasse mit geerbten Methoden', () => { - const iface = classSource({ kind: 'interface', pkg: '', name: 'Laut', isPublic: true, isAbstract: false, isFinal: false, interfaces: [], main: false, constructors: false, inherited: false, comment: false, author: '' }) - expect(iface).toBe('public interface Laut {\n}\n') - const laut = projectClasses({ name: 'x', packages: [], files: [{ path: 'Laut.java', text: 'public interface Laut {\n int lautstaerke();\n}\n' }] })[0] - const cls = classSource({ kind: 'class', pkg: '', name: 'Kuh', isPublic: false, isAbstract: false, isFinal: true, interfaces: [laut], main: false, constructors: false, inherited: true, comment: false, author: '' }) - expect(cls).toContain('final class Kuh implements Laut {') - expect(cls).toContain('@Override\n public int lautstaerke() {\n return 0;') - }) -}) diff --git a/web/test/objectWorkspace.test.ts b/web/test/objectWorkspace.test.ts deleted file mode 100644 index 9f1b4aec..00000000 --- a/web/test/objectWorkspace.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { exampleProject } from '../src/core/javaProject' -import { freeProjectName, listProjects, readProject, writeProject } from '../src/ui/objectWorkspace' -import type { WsDir, WsEntry } from '../src/ui/workspaceStore' - -/** Ordner im Speicher: Pfad → Text, Ordner enden auf `/`. */ -class MemDir implements WsDir { - constructor( - readonly name: string, - private files: Map, - private path = '', - ) {} - async list(): Promise { - const seen = new Map() - for (const key of this.files.keys()) { - if (!key.startsWith(this.path) || key === this.path) continue - const rest = key.slice(this.path.length) - const slash = rest.indexOf('/') - const name = slash < 0 ? rest : rest.slice(0, slash) - if (!name.startsWith('.') && !seen.has(name)) seen.set(name, { kind: slash < 0 ? 'file' : 'directory', name }) - } - return [...seen.values()] - } - async dir(name: string, create = false): Promise { - const key = `${this.path}${name}/` - if (![...this.files.keys()].some((k) => k.startsWith(key))) { - if (!create) throw new Error('missing') - this.files.set(key, '') - } - return new MemDir(name, this.files, key) - } - async read(name: string) { - const text = this.files.get(this.path + name) - if (text === undefined) throw new Error('missing') - return text - } - async write(name: string, text: string) { - this.files.set(this.path + name, text) - } - async has(name: string) { - return [...this.files.keys()].some((k) => k === this.path + name || k.startsWith(`${this.path}${name}/`)) - } - async remove(name: string) { - for (const k of [...this.files.keys()]) if (k === this.path + name || k.startsWith(`${this.path}${name}/`)) this.files.delete(k) - } -} - -describe('objectWorkspace', () => { - it('schreibt ein Projekt wie Eclipse und liest es wieder', async () => { - const files = new Map() - const root = new MemDir('VisuObject-Workspace', files) - const zoo = { ...exampleProject(), packages: ['leer'] } - await writeProject(root, 'Zoo', zoo, { Hund: { x: 10, y: 20 } }) - expect(files.has('Zoo/src/tiere/Hund.java')).toBe(true) - expect(files.has('Zoo/.project')).toBe(true) - expect(files.get('Zoo/.classpath')).toContain('kind="src" path="src"') - expect(await listProjects(root)).toEqual(['Zoo']) - const back = await readProject(root, 'Zoo') - expect(back.project.files.map((f) => f.path).sort()).toEqual(zoo.files.map((f) => f.path).sort()) - expect(back.project.packages).toEqual(['leer']) - expect(back.project.uml).toEqual(zoo.uml) - expect(back.positions).toEqual({ Hund: { x: 10, y: 20 } }) - }) - - it('räumt gelöschte Dateien und Packages weg, lässt bin stehen', async () => { - const files = new Map() - const root = new MemDir('ws', files) - const zoo = exampleProject() - await writeProject(root, 'Zoo', zoo, {}) - files.set('Zoo/bin/tiere/Hund.class', 'x') - await writeProject(root, 'Zoo', { ...zoo, files: zoo.files.filter((f) => !f.path.startsWith('personen/')) }, {}) - expect([...files.keys()].some((k) => k.startsWith('Zoo/src/personen'))).toBe(false) - expect(files.has('Zoo/bin/tiere/Hund.class')).toBe(true) - }) - - it('liest und schreibt mehrere Source Folders über .classpath', async () => { - const files = new Map() - const root = new MemDir('ws', files) - const zoo = { ...exampleProject(), sourceFolders: ['test'], files: [...exampleProject().files, { path: 'test:tiere/HundTest.java', text: 'package tiere;\n\npublic class HundTest {\n}\n' }] } - await writeProject(root, 'Zoo', zoo, {}) - expect(files.has('Zoo/test/tiere/HundTest.java')).toBe(true) - expect(files.get('Zoo/.classpath')).toContain('') - const back = await readProject(root, 'Zoo') - expect(back.project.files.map((f) => f.path)).toContain('test:tiere/HundTest.java') - expect(back.project.sourceFolders).toEqual(['test']) - await writeProject(root, 'Zoo', { ...back.project, sourceFolders: [], files: back.project.files.filter((f) => !f.path.startsWith('test:')) }, {}) - expect([...files.keys()].some((k) => k.startsWith('Zoo/test'))).toBe(false) - expect(files.get('Zoo/.classpath')).not.toContain('path="test"') - }) - - it('findet freie Namen', () => { - expect(freeProjectName('MeinProjekt', ['meinprojekt', 'MeinProjekt2'])).toBe('MeinProjekt3') - }) -}) diff --git a/web/test/ownTypes.test.ts b/web/test/ownTypes.test.ts deleted file mode 100644 index f74d3f28..00000000 --- a/web/test/ownTypes.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { parseClasses } from '../src/core/classModel' -import { memberCompletionsAt } from '../src/core/javaMembers' -import { ownTypes } from '../src/core/ownTypes' - -const spieler = `package spiel; -public class Spieler extends Person { - private int punkte; - public String name; - int level; - protected boolean aktiv; - public static int anzahl; - public Spieler() {} - public int getPunkte() { return punkte; } - public void setPunkte(int punkte) { this.punkte = punkte; } - private void geheim() {} - public static Spieler neu() { return new Spieler(); } -}` -const person = `package spiel; -public class Person { - public String getVorname() { return ""; } -}` -const fenster = `package gui; -public class Fenster extends JFrame { -}` - -const types = [...parseClasses(spieler).map((c) => ({ ...c, pkg: 'spiel' })), ...parseClasses(person).map((c) => ({ ...c, pkg: 'spiel' })), ...parseClasses(fenster).map((c) => ({ ...c, pkg: 'gui' }))] -const names = (code: string, place: { cls?: string; pkg: string }) => memberCompletionsAt(code, code.length, 'de', ownTypes(types, place))?.members.map((m) => m.name) - -describe('Vorschläge nach dem Punkt für eigene Klassen', () => { - it('zeigt Methoden und sichtbare Attribute samt Oberklasse', () => { - const code = 'Spieler s1 = new Spieler();\ns1.' - expect(names(code, { cls: 'Main', pkg: 'spiel' })).toEqual(['name', 'level', 'aktiv', 'getPunkte', 'setPunkte', 'getVorname']) - // Anderes Package: nur public. - expect(names(code, { cls: 'Main', pkg: 'app' })).toEqual(['name', 'getPunkte', 'setPunkte', 'getVorname']) - }) - it('this. zeigt auch private, Klasse. die static-Mitglieder', () => { - expect(names('this.', { cls: 'Spieler', pkg: 'spiel' })).toContain('geheim') - expect(names('Spieler.', { cls: 'Main', pkg: 'app' })).toEqual(['anzahl', 'neu']) - // private wird nicht vererbt: in Person ist punkte über einen Spieler nicht erreichbar. - expect(names('Spieler s = null;\ns.', { cls: 'Person', pkg: 'spiel' })).not.toContain('punkte') - }) - it('folgt Ketten und Standard-Oberklassen', () => { - expect(names('Spieler s = null;\ns.name.', { pkg: 'app' })).toContain('length') - expect(names('Fenster f = new Fenster();\nf.', { pkg: 'app' })).toContain('setVisible') - }) - it('kennt ArrayList mit dem Typ der Elemente', () => { - const code = 'private ArrayList spieler = new ArrayList<>();\n' - expect(names(code + 'spieler.', { pkg: 'app' })).toEqual(['add', 'get', 'size', 'set', 'remove', 'contains', 'indexOf', 'isEmpty', 'clear']) - expect(names(code + 'spieler.get(0).', { pkg: 'app' })).toContain('getPunkte') - expect(memberCompletionsAt(code + 'spieler.', code.length + 9, 'de')?.members.find((m) => m.name === 'get')?.returns).toBe('Spieler') - }) -}) diff --git a/web/test/progressMerge.test.ts b/web/test/progressMerge.test.ts deleted file mode 100644 index 6156011e..00000000 --- a/web/test/progressMerge.test.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { mergeProgress, progress } from '../src/learn/progress' - -describe('Lernstand zusammenführen', () => { - it('erledigt bleibt erledigt, der weiteste Schritt zählt', () => { - const mine = { lessons: { a: { step: 2, checks: { x: true } } }, exercises: { e: { xml: '' } } } - const theirs = { lessons: { a: { step: 5, done: true, checks: { x: false, y: true } }, b: { step: 1 } }, exercises: { e: { done: true, xml: '' }, f: { done: true } } } - const m = mergeProgress(mine, theirs) - expect(m.lessons.a).toEqual({ step: 5, done: true, checks: { x: true, y: true }, quiz: undefined }) - expect(m.lessons.b).toEqual({ step: 1 }) - expect(m.exercises.e).toEqual({ done: true, xml: '' }) - expect(m.exercises.f).toEqual({ done: true }) - }) - - it('die eigene Selbsteinschätzung geht vor', () => { - const m = mergeProgress({ lessons: { a: { step: 1, checks: { x: 'cloud' as const } } }, exercises: {} }, { lessons: { a: { step: 1, checks: { x: 'sun' as const, y: 'umbrella' as const } } }, exercises: {} }) - expect(m.lessons.a.checks).toEqual({ x: 'cloud', y: 'umbrella' }) - }) - - it('übernimmt die Datei, wenn im Browser nichts ist', () => { - const theirs = { lessons: { a: { step: 3, done: true } }, exercises: { e: { done: true, xml: '' } } } - expect(mergeProgress({ lessons: {}, exercises: {} }, theirs)).toEqual(theirs) - }) -}) - -describe('Lernstand aus der Datei', () => { - const file = (lessons: object, extra: object = {}) => JSON.stringify({ format: 'visustruct-lernstand', version: 1, lessons, exercises: {}, ...extra }) - - it('die Datei ersetzt den Stand, alte Reste verschwinden', () => { - expect(progress.loadFile(file({ a: { step: 4, done: true } }))).toBe(true) - progress.updateLesson('b', { step: 2 }) - expect(progress.loadFile(file({ c: { step: 1 } }))).toBe(true) - expect(progress.lesson('c').step).toBe(1) - expect(progress.lesson('a').step).toBe(0) - expect(JSON.parse(progress.toFile()).lessons.b).toBeUndefined() - }) - - it('behält auf Wunsch, was in der Sitzung vor dem Öffnen gelernt wurde, und übernimmt die Ansicht', () => { - progress.clear() - progress.updateLesson('a', { step: 3 }) - progress.loadFile(file({ b: { step: 2 } }, { view: { tab: 'tasks' } }), true) - expect(progress.lesson('a').step).toBe(3) - expect(progress.lesson('b').step).toBe(2) - expect(progress.view()).toEqual({ tab: 'tasks' }) - }) - - it('übernimmt umbenannte VisuBot-Lektionen und ignoriert fremde Dateien', () => { - progress.loadFile(file({ '13-lautsprecher': { step: 5 }, '03-sensoren': { step: 2 }, '17-stoppuhr-akku': { step: 1, done: true } })) - expect(progress.lesson('16-lautsprecher').step).toBe(5) - expect(progress.lesson('04-sensoren').step).toBe(2) - expect(progress.lesson('21-stoppuhr-akku').done).toBe(true) - expect(progress.lesson('03-sensoren').step).toBe(0) - expect(progress.loadFile('{"lessons":{}}')).toBe(false) - }) - - it('merkt sich nichts im Browser', () => { - progress.updateLesson('x', { step: 1 }) - expect(localStorage.getItem('visustruct.learn.progress')).toBeNull() - }) -}) diff --git a/web/test/pythonExport.test.ts b/web/test/pythonExport.test.ts deleted file mode 100644 index 4524f200..00000000 --- a/web/test/pythonExport.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { spawnSync } from 'node:child_process' -import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' -import { tmpdir } from 'node:os' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { generateCode } from '../src/core/codeGenerator' -import { fileLines, sameOutput, usedElementTypes } from '../src/core/exerciseCheck' -import { ElementType } from '../src/core/model' -import { parseSubprogramHeader, textFileEntries } from '../src/core/simulation' -import { contentLanguages, diagram, lessons, tasks, useContentLanguage } from '../src/learn/content' - -// Jede Musterlösung als echtes Python ausführen: gleiche Ausgabe wie in der Simulation. - -const python = spawnSync('python3', ['--version']).status === 0 - -/** Eingaben ohne Hinweistext lesen; `print("x: ")` direkt vor einem `input(` ist nur ein Hinweis. */ -function runnable(code: string): string { - const lines = code.split('\n') - const kept = lines.filter((line, i) => !(/^\s*print\("[^"]*"\)$/.test(line) && lines[i + 1]?.includes('input('))) - return 'import sys\ninput = lambda prompt="": sys.stdin.readline().rstrip("\\n")\n' + kept.join('\n') + '\n' -} - -describe.skipIf(!python)('Python-Export', () => { - it.each(contentLanguages)('jede Musterlösung läuft als Python mit derselben Ausgabe (%s)', (lang) => { - useContentLanguage(lang) - // Textdateien der Lektionen liegen im Arbeitsordner, wie bei `open("brief.txt")` erwartet. - const cwd = mkdtempSync(join(tmpdir(), 'visustruct-')) - for (const [name, content] of textFileEntries()) writeFileSync(join(cwd, name), content) - let checked = 0 - const blocks = [...lessons.flatMap((l) => l.steps.flatMap((s) => s.blocks)), ...tasks.flatMap((t) => t.blocks)] - for (const block of blocks) { - if (block.kind !== 'exercise' || !block.exercise.solution) continue - const doc = diagram(block.exercise.solution)! - const subs = block.exercise.subprograms.map((f) => diagram(f)!) - // Unterprogramme ohne Kopfzeile teilen sich die Variablen mit dem Hauptprogramm; das gibt es in Python nicht. - if (usedElementTypes(doc).has(ElementType.aufruf) && !(subs.length > 0 && subs.every((d) => parseSubprogramHeader(d.caption)))) continue - const code = generateCode(doc, 'python', {}, subs) - for (const test of block.exercise.tests) { - for (const f of test.files ?? []) rmSync(join(cwd, f.name), { force: true }) - const run = spawnSync('python3', ['-c', runnable(code)], { input: test.inputs.join('\n') + '\n', encoding: 'utf8', cwd }) - const outputs = run.stdout.split('\n').slice(0, -1) - const where = `${block.exercise.id} mit ${JSON.stringify(test.inputs)}\n${code}\n${run.stderr}` - expect(run.status, where).toBe(0) - expect(sameOutput(outputs, test.expected), `${where}\n${JSON.stringify(outputs)}`).toBe(true) - for (const f of test.files ?? []) { - const written = existsSync(join(cwd, f.name)) ? readFileSync(join(cwd, f.name), 'utf8') : undefined - expect(sameOutput(fileLines(written), f.lines), `${where}\n${f.name}: ${JSON.stringify(written)}`).toBe(true) - } - } - checked++ - } - expect(checked).toBeGreaterThan(20) - // Startet für jeden Testfall einen eigenen Python-Prozess; mit wachsender Zahl an Übungen dauert das länger als 5 s. - }, 60_000) -}) diff --git a/web/test/rename.test.ts b/web/test/rename.test.ts deleted file mode 100644 index 69ce6b44..00000000 --- a/web/test/rename.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { ElementType, makeElement, type VSDocument } from '../src/core/model' -import { checkNewName, documentVariableNames, nameAt, renameInDocument, renameInText, variableNames } from '../src/core/rename' - -describe('Umbenennen', () => { - it('ändert nur genau dieselbe Schreibweise, ganze Wörter', () => { - const code = 'int zahl = 3;\nint Zahl = zahl + 1;\nint zahlen = zahl;' - expect(renameInText(code, 'zahl', 'anzahl')).toEqual({ text: 'int anzahl = 3;\nint Zahl = anzahl + 1;\nint zahlen = anzahl;', count: 3 }) - }) - - it('lässt Zeichenketten, Zeichen, Kommentare und Felder hinter dem Punkt', () => { - const code = 'int length = 2; // length\nSystem.out.println("length " + length + feld.length + \'x\');\n/* length */' - const r = renameInText(code, 'length', 'laenge') - expect(r.count).toBe(2) - expect(r.text).toBe('int laenge = 2; // length\nSystem.out.println("length " + laenge + feld.length + \'x\');\n/* length */') - }) - - it('findet den Namen unter dem Cursor, aber keine Schlüsselwörter', () => { - const code = 'int zahl = 3;' - expect(nameAt(code, 6)?.name).toBe('zahl') - expect(nameAt(code, 8)?.name).toBe('zahl') - expect(nameAt(code, 1)).toBeUndefined() - }) - - it('nennt Variablen, keine Typen, Klassen oder Methoden', () => { - const names = variableNames(['Scanner sc = new Scanner(System.in);', 'int zahl = sc.nextInt();', 'String[] namen;', 'zahl = Math.max(zahl, Zahl);']) - expect(names).toEqual(['sc', 'zahl', 'namen', 'Zahl']) - }) - - it('benennt im ganzen Struktogramm um, auch in Kopf und Schleifen', () => { - const doc: VSDocument = { - caption: 'summe(int zahl)', - elements: [ - makeElement(ElementType.anweisung, { textLines: ['int Zahl = zahl'] }), - makeElement(ElementType.whileSchleife, { textLines: ['zahl > 0'], loopBody: [makeElement(ElementType.anweisung, { textLines: ['zahl = zahl - 1'] })] }), - ], - } - const r = renameInDocument(doc, 'zahl', 'wert') - expect(r.count).toBe(5) - expect(r.doc.caption).toBe('summe(int wert)') - expect(r.doc.elements[0].textLines).toEqual(['int Zahl = wert']) - expect(r.doc.elements[1].loopBody![0].textLines).toEqual(['wert = wert - 1']) - expect(documentVariableNames(r.doc)).toContain('Zahl') - }) - - it('prüft den neuen Namen', () => { - expect(checkNewName('Zahl', 'zahl', ['zahl', 'Zahl'])).toEqual({ ok: true, warning: 'exists' }) - expect(checkNewName('zahl', 'int', [])).toEqual({ ok: false, reason: 'keyword' }) - expect(checkNewName('zahl', '2te', [])).toEqual({ ok: false, reason: 'invalid' }) - expect(checkNewName('zahl', 'größe', [])).toEqual({ ok: true }) - }) -}) diff --git a/web/test/rescueGame.test.ts b/web/test/rescueGame.test.ts deleted file mode 100644 index 60ef642d..00000000 --- a/web/test/rescueGame.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { contentLanguages, lessons, useContentLanguage } from '../src/learn/content' -import { crosswordGrid, parseCrosswordGame, parseRescueGame, RescueGameError } from '../src/learn/rescueGame' - -const game = (extra = '', station = 'zahl: 1\nfeld: 1 2') => - `titel: Spiel -intro: Los -station: Eins - int zahl = "AB".indexOf("B"); -frage: Welche Zahl? -${station} -> Weil B bei 1 steht. -kreuzwort: Rätsel -- → 1 1 ABC: Anfang des Alphabets -- ↓ 1 3 CD: Danach -lösung: 1 2, 2 3 -${extra} -tor: Tor - String w = "___"; -ende: Frei`.split('\n') - -describe('Spiel „Prinzessin retten“', () => { - it('liest Stationen, Rätsel und Lösungswort', () => { - const g = parseRescueGame(game(), 'x.md') - expect(g.stations[0]).toMatchObject({ answer: 1, row: 0, col: 1, code: 'int zahl = "AB".indexOf("B");' }) - expect(g.keyword).toBe('BD') - expect(g.words.map((w) => w.number)).toEqual([1, 2]) - expect(crosswordGrid(g)).toEqual([ - ['A', 'B', 'C'], - ['', '', 'D'], - ]) - }) - - it('prüft, ob alphabet.charAt(zahl) ins Feld passt', () => { - expect(() => parseRescueGame(game('', 'zahl: 2\nfeld: 1 2'), 'x.md')).toThrow(/charAt\(2\) ist C/) - expect(() => parseRescueGame(game('', 'zahl: 40\nfeld: 1 2'), 'x.md')).toThrow(RescueGameError) - }) - - it('meldet Kreuzungen, die nicht passen', () => { - const lines = game().map((l) => (l.startsWith('- ↓') ? '- ↓ 1 3 XD: Falsch' : l)) - expect(() => parseRescueGame(lines, 'x.md')).toThrow(/XD passt/) - }) - - it.each(contentLanguages)('Lektion 17 (%s): Das Tor öffnet BREAK', (lang) => { - useContentLanguage(lang) - const blocks = lessons.find((l) => l.id.startsWith('17'))!.steps.flatMap((s) => s.blocks) - const block = blocks.find((b) => b.kind === 'rescueGame') - expect(block?.kind).toBe('rescueGame') - if (block?.kind !== 'rescueGame') return - expect(block.game.keyword).toBe('BREAK') - expect(block.game.stations).toHaveLength(6) - }) -}) - -describe('Begriffe-Rätsel (::: spiel kreuzwort)', () => { - it('legt die Wörter ins Gitter und liest das Lösungswort', () => { - const game = parseCrosswordGame(['titel: t', 'intro: i', '- → 1 1 SENSOR: misst', '- ↓ 1 1 STOP: hält an', 'lösung: 1 2, 2 1, 1 1'], 't.md') - expect(game.rows).toBe(4) - expect(game.cols).toBe(6) - expect(game.keyword).toBe('ETS') - expect(game.words.map((w) => w.number)).toEqual([1, 1]) - expect(() => parseCrosswordGame(['titel: t', '- → 1 1 SENSOR: misst', '- ↓ 1 1 AKTOR: tut', 'lösung: 1 1'], 't.md')).toThrow(/passt/) - expect(() => parseCrosswordGame(['titel: t', '- → 1 1 SENSOR: misst', '- ↓ 1 1 STOP: hält an'], 't.md')).toThrow(/lösung/) - }) -}) diff --git a/web/test/robotPreview.test.ts b/web/test/robotPreview.test.ts deleted file mode 100644 index fbcc3409..00000000 --- a/web/test/robotPreview.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { LiveLabels } from '../src/core/codeGenerator' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava } from '../src/core/liveSource' -import { previewRun } from '../src/core/robotPreview' -import { emptyWorld } from '../src/core/robotWorld' - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } -const doc = (java: string) => documentFromJava({ caption: '', elements: [] }, parseLiveJava(java, labels)) - -describe('mBot2-Vorschau beim Bearbeiten', () => { - it('rechnet das ganze Programm durch und merkt sich jedes Bild der Fahrt', () => { - const r = previewRun(doc('for (int i = 0; i < 4; i++) {\n mbot.forward(30);\n mbot.turnRight();\n}'), undefined, emptyWorld) - expect(r.stop).toBe('end') - expect(r.frames).toHaveLength(9) - expect(r.distance).toBe(120) - const last = r.frames.at(-1)! - expect(last.pose.x).toBeCloseTo(0) - expect(last.pose.y).toBeCloseTo(0) - }) - - it('leeres Programm: der mBot2 bleibt am Start', () => { - const r = previewRun(doc(''), undefined, emptyWorld) - expect(r.stop).toBe('end') - expect(r.frames).toHaveLength(1) - expect(r.distance).toBe(0) - }) - - it('Sensoren kommen aus der Welt: bis zur Wand und nicht weiter', () => { - const r = previewRun(doc('while (mbot.getDistance() > 10) {\n mbot.forward();\n}'), undefined, emptyWorld) - expect(r.stop).toBe('end') - expect(r.bumps).toBe(0) - expect(r.frames.at(-1)!.pose.y).toBeGreaterThan(70) - }) - - it('merkt Töne, LEDs und Bildschirm', () => { - const r = previewRun(doc('mbot.playTone(440, 200);\nmbot.setLed(255, 0, 0);\nmbot.showText("Hallo");'), undefined, emptyWorld) - expect(r.frames[1].tone).toEqual([440, 200]) - expect(r.frames.at(-1)!.leds.some((c) => c !== undefined)).toBe(true) - expect(r.frames.at(-1)!.screen).toBe('Hallo') - }) - - it('hält bei einer Eingabe und bei einer Endlosschleife an', () => { - expect(previewRun(doc('Scanner scanner = new Scanner(System.in);\nint n = scanner.nextInt();\nmbot.forward(n);'), undefined, emptyWorld).stop).toBe('input') - expect(previewRun(doc('while (true) {\n mbot.turnRight();\n}'), undefined, emptyWorld).stop).toBe('limit') - }) -}) diff --git a/web/test/robotWorld.test.ts b/web/test/robotWorld.test.ts deleted file mode 100644 index 9d55faec..00000000 --- a/web/test/robotWorld.test.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { makeMBotAction, mbotArguments } from '../src/core/mbot' -import { world } from '../src/learn/content' -import { RobotWorld, parseWorldMap } from '../src/core/robotWorld' - -const act = (w: RobotWorld, method: string, ...args: number[]) => w.apply(makeMBotAction(method, args)) - -describe('VisuBot-Welt', () => { - it('fährt ein Quadrat zurück zum Start', () => { - const w = new RobotWorld() - for (let i = 0; i < 4; i++) { - act(w, 'forward', 50) - act(w, 'turnRight', 90) - } - expect(w.pose.x).toBeCloseTo(0) - expect(w.pose.y).toBeCloseTo(0) - expect(w.pose.heading).toBeCloseTo(0) - expect(w.trail[1]).toEqual({ x: 0, y: 50 }) - expect(w.trail[2].x).toBeCloseTo(50) - }) - - it('misst den Abstand zum Rand und hält vor der Wand', () => { - const w = new RobotWorld() - expect(w.distance()).toBe(91) - act(w, 'forward', 500) - expect(w.pose.y).toBeCloseTo(90) - expect(w.bumps).toBe(1) - }) - - it('sieht eine Wand vor sich', () => { - const w = new RobotWorld({ width: 200, depth: 200, start: { x: 0, y: 0, heading: 0 }, walls: [{ from: { x: -20, y: 40 }, to: { x: 20, y: 40 } }], lines: [] }) - expect(w.distance()).toBe(31) - act(w, 'turnLeft', 90) - expect(w.distance()).toBe(91) - }) - - it('erkennt eine Linie unter dem Sensor', () => { - const line = [{ x: 0, y: -100 }, { x: 0, y: 100 }] - const w = new RobotWorld({ width: 200, depth: 200, start: { x: 0, y: 0, heading: 0 }, walls: [], lines: [line] }) - expect(w.lineState()).toBe(0b0110) - w.pose = { x: 3, y: 0, heading: 0 } - expect(w.lineState() & 0b1000).toBe(0b1000) - w.pose = { x: 30, y: 0, heading: 0 } - expect(w.lineState()).toBe(0) - }) -}) - -describe('Weltkarten', () => { - const step = (method: string, args: number[] = []) => ({ method, args: mbotArguments(method, args)!, label: method }) - - it('liest Wände, Felder, Ziel und Start', () => { - const w = parseWorldMap('// Kommentar\n#.=\n.Z.\n.>.\n') - expect(w.width).toBe(30) - expect(w.depth).toBe(30) - expect(w.start).toEqual({ x: 0, y: -10, heading: 90 }) - expect(w.blocks).toEqual([{ x: -10, y: 10 }]) - expect(w.marks).toEqual([{ x: 10, y: 10 }]) - expect(w.goals).toEqual([{ x: 0, y: 0 }]) - expect(() => parseWorldMap('...\n')).toThrow(/Start/) - expect(() => parseWorldMap('^^\n')).toThrow(/Start/) - }) - - it('Wandklötze bremsen und werden gemessen', () => { - const r = new RobotWorld(parseWorldMap('.#.\n...\n...\n.^.\n')) - expect(r.distance()).toBe(25 - 9) - r.apply(step('forward', [30])) - expect(r.bumps).toBe(1) - expect(r.pose.y).toBeCloseTo(-15 + 25 - 10, 5) - }) - - // Die Musterlösungen der Lektionen, hier direkt in TypeScript: so passen Karten und Schwellen zusammen. - const untilWall = (r: RobotWorld) => { - for (let i = 0; i < 200 && r.distance() > 15; i++) r.apply(step('forward')) - } - const maze = (r: RobotWorld) => { - for (let i = 0; i < 500 && r.lineState() === 0; i++) { - if (r.distance() > 15) r.apply(step('forward')) - else { - r.apply(step('turnRight')) - if (r.distance() < 15) r.apply(step('turnRight', [180])) - } - } - } - - it.each(['wand-kurz', 'wand-mittel', 'wand-lang'])('%s: bis zur Wand endet im Ziel', (name) => { - const r = new RobotWorld(world(name)!) - untilWall(r) - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - }) - - it.each(['labyrinth-1', 'labyrinth-2'])('%s: Wand-Regel findet das Ziel ohne Anstoßen', (name) => { - const r = new RobotWorld(world(name)!) - maze(r) - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - // Nur geradeaus ohne Sensor kommt nicht an - const blind = new RobotWorld(world(name)!) - blind.apply(step('forward', [100])) - expect(blind.atGoal()).toBe(false) - }) - - it('Raster 3: ein Zeichen wird 30 cm groß, Start in der Mitte, Haltefeld unsichtbar', () => { - const w = parseWorldMap('raster: 3\n#o\n>.\n') - expect(w.width).toBe(60) - expect(w.depth).toBe(60) - expect(w.start).toEqual({ x: -15, y: -15, heading: 90 }) - expect(w.blocks).toHaveLength(9) - expect(w.targets).toHaveLength(9) - const r = new RobotWorld(w) - expect(r.atGoal()).toBe(false) - r.apply(step('turnLeft')) - expect(r.distance()).toBe(6) - r.apply(step('turnRight')) - r.apply(step('forward', [30])) - r.apply(step('turnLeft')) - expect(r.lineState()).toBe(0) - expect(r.atGoal()).toBe(false) - r.apply(step('forward', [30])) - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - }) - - // Kara-Aufgaben: ein Feld = 30 cm, Baum im Nachbarfeld heißt Abstand < 20. - const go = (r: RobotWorld, ...moves: [string, number?][]) => moves.forEach(([m, a]) => r.apply(step(m, a === undefined ? [] : [a]))) - const around = (r: RobotWorld) => go(r, ['turnLeft'], ['forward', 30], ['turnRight'], ['forward', 60], ['turnRight'], ['forward', 30], ['turnLeft']) - const aroundRight = (r: RobotWorld) => go(r, ['turnRight'], ['forward', 30], ['turnLeft'], ['forward', 60], ['turnLeft'], ['forward', 30], ['turnRight']) - - it('slalom: abwechselnd oben und unten herum ins Ziel', () => { - const r = new RobotWorld(world('slalom')!) - for (let i = 0; i < 2; i++) { - around(r) - aroundRight(r) - } - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - const straight = new RobotWorld(world('slalom')!) - straight.apply(step('forward', [240])) - expect(straight.bumps).toBe(1) - }) - - it.each(['wald-1', 'wald-2'])('%s: Bäume umfahren bis zum Ziel', (name) => { - const r = new RobotWorld(world(name)!) - for (let i = 0; i < 50 && r.lineState() === 0; i++) { - if (r.distance() < 20) around(r) - else r.apply(step('forward', [30])) - } - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - }) - - it.each(['tunnel-1', 'tunnel-2'])('%s: hält im ersten Tunnelfeld', (name) => { - const r = new RobotWorld(world(name)!) - let left = 99 - let right = 99 - for (let i = 0; i < 50 && !(left < 20 && right < 20); i++) { - r.apply(step('forward', [30])) - r.apply(step('turnLeft')) - left = r.distance() - r.apply(step('turnRight', [180])) - right = r.distance() - r.apply(step('turnLeft')) - } - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - // Nur eine Seite prüfen reicht nicht - const half = new RobotWorld(world(name)!) - for (let i = 0; i < 50; i++) { - half.apply(step('forward', [30])) - half.apply(step('turnLeft')) - const d = half.distance() - half.apply(step('turnRight')) - if (d < 20) break - } - expect(half.atGoal()).toBe(false) - }) - - it.each(['ausgang-1', 'ausgang-2'])('%s: durch jede Lücke nach oben ins Ziel', (name) => { - const r = new RobotWorld(world(name)!) - for (let i = 0; i < 100 && r.lineState() === 0; i++) { - r.apply(step('turnLeft')) - if (r.distance() > 20) { - r.apply(step('forward', [60])) - r.apply(step('turnRight')) - } else { - r.apply(step('turnRight')) - r.apply(step('forward', [30])) - } - } - expect(r.atGoal()).toBe(true) - expect(r.bumps).toBe(0) - }) -}) diff --git a/web/test/sections.test.ts b/web/test/sections.test.ts deleted file mode 100644 index 7aa9a504..00000000 --- a/web/test/sections.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { addMember, parseClasses } from '../src/core/classModel' -import { keepSections, withSections } from '../src/core/sections' - -const de = { fields: 'Attribute', refs: 'Referenzattribute', ctors: 'Konstruktor', methods: 'Methoden' } -const names = new Set(['Spieler', 'Mannschaft']) -const src = `public class Spieler { - public int getPunkte() { - return punkte; - } - - private int punkte; - /** Das Team. */ - private Mannschaft team; - - @Override - public String toString() { - return ""; - } - public Spieler() { - } -} -` - -describe('Abschnitts-Kommentare', () => { - it('ordnet Attribute, Referenzattribute, Konstruktor und Methoden', () => { - const out = withSections(src, parseClasses(src)[0], names, de) - expect(out).toBe(`public class Spieler { - // Attribute - private int punkte; - - // Referenzattribute - /** Das Team. */ - private Mannschaft team; - - // Konstruktor - public Spieler() { - } - - // Methoden - public int getPunkte() { - return punkte; - } - - @Override - public String toString() { - return ""; - } -} -`) - // Zweimal ordnen ändert nichts mehr. - expect(withSections(out, parseClasses(out)[0], names, de)).toBe(out) - }) - it('neues Attribut aus dem Diagramm landet unter seinem Kommentar', () => { - const start = withSections(src, parseClasses(src)[0], names, de) - let next = addMember(start, parseClasses(start)[0], '- level: int') - next = keepSections(next, 'Spieler', names, de) - expect(next).toContain('// Attribute\n private int punkte;\n private int level;\n\n // Referenzattribute') - next = keepSections(addMember(next, parseClasses(next)[0], '- mitspieler: ArrayList'), 'Spieler', names, de) - expect(next).toContain('private Mannschaft team;\n private ArrayList mitspieler;\n\n // Konstruktor') - }) - it('leere Klasse bekommt alle vier Kommentare; ohne Kommentare bleibt alles', () => { - const empty = 'public class A {\n}\n' - expect(withSections(empty, parseClasses(empty)[0], names, de)).toBe('public class A {\n // Attribute\n\n // Referenzattribute\n\n // Konstruktor\n\n // Methoden\n}\n') - expect(keepSections(src, 'Spieler', names, de)).toBe(src) - }) -}) diff --git a/web/test/simulation.test.ts b/web/test/simulation.test.ts deleted file mode 100644 index 132976c5..00000000 --- a/web/test/simulation.test.ts +++ /dev/null @@ -1,763 +0,0 @@ -import { readFileSync } from 'node:fs' -import { join } from 'node:path' -import { describe, expect, it } from 'vitest' -import { ElementType, makeElement, type VSDocument, type VSElement } from '../src/core/model' -import { registerTextFile, SimulationEngine, type SimState } from '../src/core/simulation' -import { decodeDocument } from '../src/core/xmlCodec' - -// Port von SimulationEngineTests.swift und SimulationConditionTests.swift. - -const examples = join(__dirname, '..', '..', 'VisuStruct-Dateien') -const example = (name: string) => decodeDocument(readFileSync(join(examples, name), 'utf8')) - -const doc = (elements: VSElement[]): VSDocument => ({ caption: '', elements }) -const stmt = (text: string) => makeElement(ElementType.anweisung, { textLines: [text] }) - -/** Lässt die Simulation bis zum Ende laufen und beantwortet `input:`-Anfragen der Reihe nach. */ -function runSimulation(document: VSDocument, inputs: string[] = [], maxSteps = 10_000): SimState & { message?: string } { - const engine = new SimulationEngine(document) - const remaining = [...inputs] - let steps = 0 - while (steps < maxSteps) { - if (engine.state.inputRequest) { - const next = remaining.shift() - if (next === undefined) throw new Error('Simulation wartet auf Eingabe, aber es sind keine Eingaben mehr übrig.') - engine.provideInput(next) - continue - } - if (!engine.canStep) break - engine.step() - steps += 1 - } - expect(steps).toBeLessThan(maxSteps) - return { ...engine.state, message: engine.message } -} - -describe('Simulation', () => { - it('Collatz gibt die Folge aus', () => { - const state = runSimulation(example('Collatz.visustruct')) - expect(state.outputLines).toEqual( - ['58', '29', '88', '44', '22', '11', '34', '17', '52', '26', '13', '40', '20', '10', '5', '16', '8', '4', '2', '1'], - ) - expect(state.variables.get('number')).toEqual({ kind: 'int', value: 1 }) - }) - - it('Fibonacci gibt die ersten zehn Zahlen aus', () => { - expect(runSimulation(example('Fibonacci.visustruct')).outputLines).toEqual( - ['1', '1', '2', '3', '5', '8', '13', '21', '34', '55'], - ) - }) - - it('NumberEven nutzt die Eingabe', () => { - const d = example('2026-04-23-NumberEven.visustruct') - expect(runSimulation(d, ['4']).outputLines).toEqual(['number is even']) - expect(runSimulation(d, ['7']).outputLines).toEqual(['number is odd']) - }) - - it('weist ungültige Ganzzahl-Eingaben zurück', () => { - const engine = new SimulationEngine(doc([stmt('input: int n'), stmt('print: n * 2')])) - engine.step() - expect(engine.state.inputRequest?.variableName).toBe('n') - engine.provideInput('abc') - expect(engine.state.inputErrorNotice).toEqual({ kind: 'wholeNumberExpected' }) - expect(engine.state.inputRequest).toBeDefined() - engine.provideInput('21') - expect(engine.state.inputRequest).toBeUndefined() - engine.step() - expect(engine.state.outputLines).toEqual(['42']) - }) - - it('zählende Schleifen', () => { - const forLoop = doc([ - stmt('int sum = 0'), - makeElement(ElementType.forSchleife, { textLines: ['int i = 1', 'i <= 4', 'i++'], loopBody: [stmt('sum += i')] }), - ]) - expect(runSimulation(forLoop).variables.get('sum')).toEqual({ kind: 'int', value: 10 }) - - const doUntil = doc([ - stmt('int k = 0'), - makeElement(ElementType.doUntilSchleife, { textLines: ['k < 3'], loopBody: [stmt('k++')] }), - ]) - expect(runSimulation(doUntil).variables.get('k')).toEqual({ kind: 'int', value: 3 }) - }) - - it('Fallauswahl springt in den default-Fall', () => { - const d = (day: number) => - doc([ - stmt(`int day = ${day}`), - makeElement(ElementType.fallauswahl, { - textLines: ['day'], - cases: [ - { name: '1', elements: [stmt('print: "Montag"')] }, - { name: '2', elements: [stmt('print: "Dienstag"')] }, - { name: 'default', elements: [stmt('print: "anderer Tag"')] }, - ], - }), - ]) - expect(runSimulation(d(2)).outputLines).toEqual(['Dienstag']) - expect(runSimulation(d(5)).outputLines).toEqual(['anderer Tag']) - }) - - it('überspringt den Platzhalter ø', () => { - const d = (a: number) => - doc([ - stmt('ø'), - stmt(`int a = ${a}`), - makeElement(ElementType.verzweigung, { - textLines: ['a > 3'], - cases: [ - { name: 'true', elements: [stmt('print: "groß"')] }, - { name: 'false', elements: [stmt('ø')] }, - ], - }), - ]) - const big = runSimulation(d(5)) - expect(big.outputLines).toEqual(['groß']) - expect(big.isHalted).toBe(false) - const small = runSimulation(d(1)) - expect(small.outputLines).toEqual([]) - expect(small.isHalted).toBe(false) - expect(small.message).toBe('Simulation finished.') - }) - - it('rechnet mit boolean-Variablen', () => { - const loop = makeElement(ElementType.whileSchleife, { - textLines: ['!fertig'], - loopBody: [stmt('i++'), stmt('fertig = i >= 3')], - }) - const state = runSimulation( - doc([ - stmt('int i = 0'), - stmt('boolean fertig = false'), - loop, - stmt('bool gross = i > 2 && fertig'), - stmt('print: "fertig: " + fertig'), - makeElement(ElementType.verzweigung, { - textLines: ['gross == true'], - cases: [ - { name: 'true', elements: [stmt('print: "ja"')] }, - { name: 'false', elements: [stmt('print: "nein"')] }, - ], - }), - stmt('gross = !gross'), - stmt('print: gross'), - ]), - ) - expect(state.isHalted).toBe(false) - expect(state.variables.get('i')).toEqual({ kind: 'int', value: 3 }) - expect(state.outputLines).toEqual(['fertig: true', 'ja', 'false']) - }) - - it('liest boolean ein', () => { - const engine = new SimulationEngine(doc([stmt('input: boolean ok "Okay?"'), stmt('print: ok')])) - engine.step() - expect(engine.state.inputRequest?.valueType).toBe('boolean') - engine.provideInput('vielleicht') - expect(engine.state.inputErrorNotice).toEqual({ kind: 'booleanExpected' }) - engine.provideInput('Wahr') - engine.step() - expect(engine.state.outputLines).toEqual(['true']) - }) - - it('Division durch null meldet einen Fehler', () => { - const engine = new SimulationEngine(doc([stmt('int x = 1 / 0')])) - engine.step() - expect(engine.state.notice).toEqual({ kind: 'divisionByZero' }) - expect(engine.message).toBe('Division by zero.') - expect(engine.state.variables.has('x')).toBe(false) - }) - - it('meldet das Ende nach dem letzten Schritt', () => { - const engine = new SimulationEngine(doc([stmt('int x = 1')])) - engine.step() - expect(engine.message).toBe('Simulation finished.') - }) - - it('gibt ein ganzes Array wie Java als Referenz aus, den Inhalt erst mit Arrays.toString', () => { - const state = runSimulation( - doc([ - stmt('int[] zahlenreihe = {1, 2, 3}'), - stmt('String[] namen = {"Ada", "Bob"}'), - stmt('double[][] t = new double[2][2]'), - stmt('output: zahlenreihe'), - stmt('zahlenreihe[0] = 9'), - stmt('output: "Reihe: " + zahlenreihe'), - stmt('output: namen'), - stmt('output: t'), - stmt('output: Arrays.toString(zahlenreihe)'), - stmt('output: Arrays.toString(namen)'), - stmt('output: Arrays.deepToString(t)'), - stmt('output: Arrays.toString(t)'), - ]), - ) - const [ref, refAgain, names, grid, ...rest] = state.outputLines - expect(ref).toMatch(/^\[I@[0-9a-f]+$/u) - expect(refAgain).toBe('Reihe: ' + ref) - expect(names).toMatch(/^\[Ljava\.lang\.String;@[0-9a-f]+$/u) - expect(grid).toMatch(/^\[\[D@[0-9a-f]+$/u) - expect(rest.slice(0, 3)).toEqual(['[9, 2, 3]', '[Ada, Bob]', '[[0.0, 0.0], [0.0, 0.0]]']) - expect(rest[3]).toMatch(/^\[\[D@[0-9a-f]+, \[D@[0-9a-f]+\]$/u) - // Die Variablentabelle zeigt weiter den Inhalt. - expect(state.variables.get('zahlenreihe')).toEqual({ kind: 'intArray', value: [9, 2, 3] }) - }) - - it('Werte und Arrays', () => { - const state = runSimulation( - doc([ - stmt('double d = 7.0 / 2'), - stmt('int[] a = {3, 4, 5}'), - stmt('a[1] = a[0] * 10'), - stmt('String s = "x"'), - stmt('print: "len=" + a.length + " a=" + Arrays.toString(a) + " d=" + d'), - stmt('d += 1'), - ]), - ) - expect(state.outputLines).toEqual(['len=3 a=[3, 30, 5] d=3.5']) - expect(state.variables.get('d')).toEqual({ kind: 'double', value: 4.5 }) - expect(state.lastTrace).toBe('d = 3.5 + 1.0 -> 4.5') - }) -}) - -describe('Bedingungen', () => { - const branch = (condition: string, setup = ['int a = 5', 'String name = "Anna"']) => - doc([ - ...setup.map(stmt), - makeElement(ElementType.verzweigung, { - textLines: [condition], - cases: [ - { name: 'true', elements: [stmt('print: "ja"')] }, - { name: 'false', elements: [stmt('print: "nein"')] }, - ], - }), - ]) - - const check = (condition: string, expected: boolean) => { - const state = runSimulation(branch(condition)) - expect(state.message === 'Simulation finished.' ? undefined : state.message, condition).toBeUndefined() - expect(state.outputLines, condition).toEqual([expected ? 'ja' : 'nein']) - } - - it('boolesche Operatoren', () => { - check('a > 3 && a < 10', true) - check('a < 3 || a == 5', true) - check('a != 5', false) - }) - - it('String-Vergleiche', () => { - check('name == "Anna"', true) - check('name != "Anna"', false) - check('"Anna" == name', true) - check('name.equals("Anna")', true) - check('!name.equals("Bob")', true) - check('name == "Anna" && a == 5', true) - }) - - it('Operatoren in String-Literalen sind Text', () => { - const state = runSimulation(branch('text == "a && b || c"', ['String text = "a && b || c"'])) - expect(state.outputLines).toEqual(['ja']) - }) - - it('Klammern und Negation', () => { - check('(a < 3 || a == 5) && a > 0', true) - check('(a < 3 || a > 10) && a > 0', false) - check('!(a > 3)', false) - check('!(a > 3 && a < 4)', true) - check('(a + 1) * 2 > 11', true) - check('((a == 5))', true) - }) - - it('Schlüsselwort mit getrennt geklammerten Teilen', () => { - check('if (a > 3) && (a < 10)', true) - check('if (a > 3) && (a > 10)', false) - }) - - it('while-Schleife mit String-Bedingung', () => { - const d = doc([ - stmt('String answer = "nein"'), - stmt('int tries = 0'), - makeElement(ElementType.whileSchleife, { - textLines: ['answer != "ja"'], - loopBody: [stmt('input: answer'), stmt('tries++')], - }), - ]) - expect(runSimulation(d, ['vielleicht', 'ja']).variables.get('tries')).toEqual({ kind: 'int', value: 2 }) - }) - - it('String mit Zahl vergleichen ist ein Fehler', () => { - const state = runSimulation(branch('name == 5')) - expect(state.isHalted).toBe(true) - expect(state.outputLines).toEqual([]) - }) - - it('bleibt beim fehlerhaften Schritt stehen', () => { - const engine = new SimulationEngine(doc([stmt('print: "vorher"'), stmt('int x = 1 / 0'), stmt('print: "danach"')])) - engine.step() - engine.step() - expect(engine.state.isHalted).toBe(true) - expect(engine.canStep).toBe(false) - expect(engine.currentStepPath).toEqual([1]) - expect(engine.message).toBe('Division by zero.') - engine.step() - expect(engine.state.outputLines).toEqual(['vorher']) - expect(engine.message).toBe('Division by zero.') - engine.reset() - expect(engine.state.isHalted).toBe(false) - expect(engine.canStep).toBe(true) - }) - - it('teilt ganze Zahlen wie Java', () => { - const state = runSimulation( - doc([ - stmt('int a = 7'), - stmt('int b = 2'), - stmt('int q = a / b'), - stmt('double d = a / b'), - stmt('double e = a / 2.0'), - stmt('int n = -7 / 2'), - stmt('double[] w = {1.5, 2.5}'), - stmt('print: "q=" + a / b + " d=" + d + " e=" + e + " n=" + n'), - stmt('double m = (w[0] + w[1]) / 2'), - stmt('int k = 10 / 4 * 2'), - ]), - ) - expect(state.variables.get('q')).toEqual({ kind: 'int', value: 3 }) - expect(state.variables.get('d')).toEqual({ kind: 'double', value: 3 }) - expect(state.variables.get('e')).toEqual({ kind: 'double', value: 3.5 }) - expect(state.variables.get('n')).toEqual({ kind: 'int', value: -3 }) - expect(state.outputLines).toEqual(['q=3 d=3.0 e=3.5 n=-3']) - expect(state.variables.get('m')).toEqual({ kind: 'double', value: 2 }) - expect(state.variables.get('k')).toEqual({ kind: 'int', value: 4 }) - }) - - it('rechnet mit mehreren Arrayfächern und erlaubt Leerzeichen im Index des Ziels', () => { - const state = runSimulation(doc([stmt('int[] a = {5, 3, 1}'), stmt('int d = a[0] - a[1]'), stmt('int i = 0'), stmt('a[i + 1] = a[i] + a[i + 2]')])) - expect(state.variables.get('d')).toEqual({ kind: 'int', value: 2 }) - expect(state.variables.get('a')).toEqual({ kind: 'intArray', value: [5, 6, 1] }) - }) - - it('Aussprung verlässt die innerste Schleife', () => { - const jump = makeElement(ElementType.aussprung, { textLines: ['Schleife verlassen'] }) - const document = doc([ - stmt('int summe = 0'), - makeElement(ElementType.endlosschleife, { - loopBody: [ - stmt('input: int zahl "Zahl?"'), - makeElement(ElementType.verzweigung, { - textLines: ['zahl == 0'], - cases: [ - { name: 'true', elements: [jump] }, - { name: 'false', elements: [stmt('summe = summe + zahl')] }, - ], - }), - ], - }), - makeElement(ElementType.forSchleife, { - textLines: ['int i = 0', 'i < 10', 'i++'], - loopBody: [ - makeElement(ElementType.verzweigung, { - textLines: ['i == 2'], - cases: [ - { name: 'true', elements: [jump] }, - { name: 'false', elements: [stmt('ø')] }, - ], - }), - ], - }), - stmt('print: "Summe: " + summe + " i=" + i'), - jump, - stmt('print: "nie"'), - ]) - const state = runSimulation(document, ['3', '4', '0']) - expect(state.outputLines).toEqual(['Summe: 7 i=2']) - expect(state.message).toBe('Simulation finished.') - }) - - it('Aufruf führt das Unterprogramm aus', () => { - const call = (name: string) => makeElement(ElementType.aufruf, { textLines: [name] }) - const titel: VSDocument = { caption: 'zeigeTitel()', elements: [stmt('print: "== Quiz =="'), stmt('int punkte = 0')] } - const zaehlen: VSDocument = { - caption: 'zaehlen', - elements: [ - makeElement(ElementType.whileSchleife, { textLines: ['true'], loopBody: [stmt('punkte++'), makeElement(ElementType.aussprung, {})] }), - stmt('print: "punkte=" + punkte'), - makeElement(ElementType.aussprung, {}), - stmt('print: "nie"'), - ], - } - const subprograms: Record = { zeigetitel: titel, zaehlen } - const engine = new SimulationEngine( - doc([call('zeigeTitel()'), call('zaehlen();'), call('zaehlen()'), call('fehlt()'), stmt('print: "Ende " + punkte')]), - (name) => subprograms[name.toLowerCase()], - ) - const paths: (number[] | undefined)[] = [] - while (engine.canStep) { - engine.step() - paths.push(engine.currentStepPath) - if (engine.state.notice?.kind === 'subprogramNotFound') expect(engine.message).toBe('No open diagram is called fehlt(). The call was skipped.') - } - expect(engine.state.outputLines).toEqual(['== Quiz ==', 'punkte=1', 'punkte=2', 'Ende 2']) - expect(engine.message).toBe('Simulation finished.') - // Im Unterprogramm bleibt der Aufruf-Kasten markiert - expect(paths[0]).toEqual([0]) - }) - - describe('Unterprogramme mit Kopfzeile', () => { - const call = (text: string) => makeElement(ElementType.aufruf, { textLines: [text] }) - const branch = (condition: string, yes: VSElement[], no: VSElement[]) => - makeElement(ElementType.verzweigung, { textLines: [condition], cases: [{ name: 'true', elements: yes }, { name: 'false', elements: no }] }) - const run = (main: VSElement[], subs: VSDocument[], inputs: string[] = []) => { - const engine = new SimulationEngine(doc(main), (name) => subs.find((d) => d.caption.includes(name + '('))) - const remaining = [...inputs] - for (let i = 0; i < 10_000 && (engine.canStep || engine.state.inputRequest); i++) { - if (engine.state.inputRequest) engine.provideInput(remaining.shift()!) - else engine.step() - } - return engine - } - - it('übergibt Werte, gibt mit return zurück und hält Variablen lokal', () => { - const quadrat: VSDocument = { caption: 'int quadrat(int x)', elements: [stmt('int y = x * x'), stmt('return y')] } - const engine = run([stmt('int x = 3'), stmt('int y = 10'), call('int q = quadrat(x + 1)'), stmt('print: x + " " + y + " " + q')], [quadrat]) - expect(engine.state.outputLines).toEqual(['3 10 16']) - expect(engine.state.variables.has('q')).toBe(true) - expect(engine.message).toBe('Simulation finished.') - }) - - it('ruft void-Unterprogramme mit Parametern auf und beendet sie mit return', () => { - const zeige: VSDocument = { - caption: 'void zeigeSumme(int a, int b)', - elements: [branch('a < 0', [stmt('return')], []), stmt('int summe = a + b'), stmt('print: "Summe: " + summe')], - } - const engine = run([call('zeigeSumme(3, 4)'), call('zeigeSumme(-1, 4);'), stmt('print: "Ende"')], [zeige]) - expect(engine.state.outputLines).toEqual(['Summe: 7', 'Ende']) - expect(engine.state.variables.has('summe')).toBe(false) - }) - - it('rechnet rekursiv mit eigenen Variablen je Aufruf', () => { - const fakultaet: VSDocument = { - caption: 'int fakultaet(int n)', - elements: [branch('n <= 1', [stmt('return 1')], [call('int rest = fakultaet(n - 1)'), stmt('return n * rest')])], - } - const engine = run([stmt('input: int n "Zahl?"'), call('int f = fakultaet(n)'), stmt('print: f')], [fakultaet], ['5']) - expect(engine.state.outputLines).toEqual(['120']) - }) - - it('meldet falsche Werte und fehlendes return', () => { - const quadrat: VSDocument = { caption: 'int quadrat(int x)', elements: [stmt('int y = x * x')] } - let engine = run([call('quadrat(1, 2)')], [quadrat]) - expect(engine.state.notice).toEqual({ kind: 'callArguments', text: 'quadrat' }) - engine = run([call('int q = quadrat(2)')], [quadrat]) - expect(engine.state.notice).toEqual({ kind: 'missingReturn', text: 'quadrat' }) - expect(engine.message).toBe('quadrat ended without returning a value with return.') - }) - - it('übergibt Zahlen als Kopie und Arrays als Referenz', () => { - const verdopple: VSDocument = { caption: 'void verdopple(int x)', elements: [stmt('x = x * 2'), stmt('print: "innen: " + x')] } - const leeren: VSDocument = { - caption: 'void leeren(boolean[] reihe)', - elements: [ - makeElement(ElementType.forSchleife, { textLines: ['int i = 0', 'i < reihe.length', 'i++'], loopBody: [stmt('reihe[i] = false')] }), - ], - } - const engine = run( - [ - stmt('int zahl = 5'), - call('verdopple(zahl)'), - stmt('print: "außen: " + zahl'), - stmt('boolean[] saal = {true, false, true}'), - call('leeren(saal)'), - stmt('print: saal[0] + " " + saal[2]'), - ], - [verdopple, leeren], - ) - expect(engine.state.outputLines).toEqual(['innen: 10', 'außen: 5', 'false false']) - expect(engine.state.variables.has('reihe')).toBe(false) - expect(engine.message).toBe('Simulation finished.') - }) - - it('gibt geänderte 2D-Arrays zurück und prüft den Array-Typ', () => { - const verkaufe: VSDocument = { caption: 'void verkaufe(boolean[][] sitze, int r, int p)', elements: [stmt('sitze[r][p] = true')] } - let engine = run([stmt('boolean[][] kino = new boolean[2][3]'), call('verkaufe(kino, 1, 2)'), stmt('print: kino[1][2]')], [verkaufe]) - expect(engine.state.outputLines).toEqual(['true']) - engine = run([stmt('int[] zahlen = {1, 2}'), call('verkaufe(zahlen, 0, 0)')], [verkaufe]) - expect(engine.state.notice?.kind).toBe('invalidExpression') - }) - - it('beendet mit return im Hauptprogramm das Programm', () => { - const engine = run([stmt('print: "a"'), stmt('return'), stmt('print: "b"')], []) - expect(engine.state.outputLines).toEqual(['a']) - expect(engine.message).toBe('Simulation finished.') - }) - }) - - it('bricht ab, wenn sich ein Unterprogramm selbst aufruft', () => { - const self: VSDocument = { caption: 'rekursion', elements: [makeElement(ElementType.aufruf, { textLines: ['rekursion()'] })] } - const engine = new SimulationEngine(self, () => self) - for (let i = 0; i < 1000 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted).toBe(true) - expect(engine.state.notice?.kind).toBe('callTooDeep') - }) -}) - -describe('Zweidimensionale Arrays und boolean-Arrays', () => { - it('legt ein 2D-Array an, schreibt und liest Fächer und kennt beide Längen', () => { - const state = runSimulation( - doc([ - stmt('boolean[][] sitze = new boolean[3][4]'), - stmt('sitze[1][2] = true'), - stmt('int r = 2'), - stmt('sitze[r][r + 1] = !sitze[1][2]'), - stmt('int reihen = sitze.length'), - stmt('int plaetze = sitze[0].length'), - stmt('int[][] t = {{1, 2, 3}, {4, 5, 6}}'), - stmt('t[1][0] = t[0][2] * 10'), - stmt('output: sitze[1][2] + " " + t[1][0] + " " + t[1][2]'), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - expect(state.variables.get('sitze')).toEqual({ - kind: 'grid', - elementType: 'boolean', - value: [ - [false, false, false, false], - [false, false, true, false], - [false, false, false, false], - ], - }) - expect(state.variables.get('reihen')).toEqual({ kind: 'int', value: 3 }) - expect(state.variables.get('plaetze')).toEqual({ kind: 'int', value: 4 }) - expect(state.variables.get('t')).toEqual({ kind: 'grid', elementType: 'int', value: [[1, 2, 3], [30, 5, 6]] }) - expect(state.outputLines).toEqual(['true 30 6']) - }) - - it('zählt freie Plätze mit verschachtelten Schleifen und boolean-Bedingung', () => { - const state = runSimulation( - doc([ - stmt('boolean[][] sitze = {{true, false}, {false, false}}'), - stmt('int frei = 0'), - makeElement(ElementType.forSchleife, { - textLines: ['int r = 0', 'r < sitze.length', 'r++'], - loopBody: [ - makeElement(ElementType.forSchleife, { - textLines: ['int p = 0', 'p < sitze[r].length', 'p++'], - loopBody: [ - makeElement(ElementType.verzweigung, { - textLines: ['!sitze[r][p]'], - cases: [ - { name: 'true', elements: [stmt('frei = frei + 1')] }, - { name: 'false', elements: [stmt('ø')] }, - ], - }), - ], - }), - ], - }), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - expect(state.variables.get('frei')).toEqual({ kind: 'int', value: 3 }) - }) - - it('kennt boolean-Arrays mit einer Dimension', () => { - const state = runSimulation(doc([stmt('boolean[] an = new boolean[3]'), stmt('an[1] = true'), stmt('output: an[1] + " " + an[2]')])) - expect(state.outputLines).toEqual(['true false']) - }) - - it('meldet Fächer außerhalb des 2D-Arrays', () => { - const state = runSimulation(doc([stmt('int[][] t = new int[2][2]'), stmt('t[2][0] = 1')])) - expect(state.notice?.kind).toBe('invalidExpression') - }) -}) - -describe('Zeichenketten', () => { - it('kennt die wichtigsten String-Methoden', () => { - const state = runSimulation( - doc([ - stmt('String wort = "Ziegenbock"'), - stmt('int n = wort.length()'), - stmt('char erster = wort.charAt(0)'), - stmt('char letzter = wort.charAt(wort.length() - 1)'), - stmt('String teil = wort.substring(0, 6)'), - stmt('String rest = wort.substring(6)'), - stmt('int pos = wort.indexOf("bock")'), - stmt('int fehlt = wort.indexOf("x")'), - stmt('String gross = wort.toUpperCase()'), - stmt('boolean hat = wort.contains("gen")'), - stmt('String[] woerter = "Ludwig ist ein Geißbock".split(" ")'), - stmt('output: woerter.length + " " + woerter[3] + " " + wort.substring(0, n - 4) + wort.charAt(1)'), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - const v = (name: string) => state.variables.get(name) - expect(v('n')).toEqual({ kind: 'int', value: 10 }) - expect(v('erster')).toEqual({ kind: 'string', value: 'Z' }) - expect(v('letzter')).toEqual({ kind: 'string', value: 'k' }) - expect(v('teil')).toEqual({ kind: 'string', value: 'Ziegen' }) - expect(v('rest')).toEqual({ kind: 'string', value: 'bock' }) - expect(v('pos')).toEqual({ kind: 'int', value: 6 }) - expect(v('fehlt')).toEqual({ kind: 'int', value: -1 }) - expect(v('gross')).toEqual({ kind: 'string', value: 'ZIEGENBOCK' }) - expect(v('hat')).toEqual({ kind: 'boolean', value: true }) - expect(state.outputLines).toEqual(['4 Geißbock Ziegeni']) - }) - - it('kennt weitere String-Methoden, parseInt, valueOf und Textdateien', () => { - registerTextFile('brief.txt', 'Liebe Ziege,\nder Fuchs hat dich eingesperrt.\nLudwig') - const state = runSimulation( - doc([ - stmt('String wort = "Ziegenbock"'), - stmt('int hinten = wort.lastIndexOf("e")'), - stmt('int ab = wort.indexOf("e", 3)'), - stmt('boolean leer = "".isEmpty()'), - stmt('boolean gleich = wort.equalsIgnoreCase("ZIEGENBOCK")'), - stmt('int vorher = "Anna".compareTo("Ben")'), - stmt('int laenger = "Ab".compareTo("A")'), - stmt('String linie = "-".repeat(4)'), - stmt('String ganz = "Ziegen".concat("bock")'), - stmt('int zahl = Integer.parseInt("41") + 1'), - stmt('double haelfte = Double.parseDouble("7") / 2'), - stmt('String text = String.valueOf(zahl) + "!"'), - stmt('String brief = Files.readString(Path.of("brief.txt"))'), - stmt('String[] zeilen = brief.split("\\n")'), - stmt('output: zeilen.length + " " + zeilen[2].toUpperCase() + " " + brief.indexOf("Fuchs")'), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - const v = (name: string) => state.variables.get(name) - expect(v('hinten')).toEqual({ kind: 'int', value: 4 }) - expect(v('ab')).toEqual({ kind: 'int', value: 4 }) - expect(v('leer')).toEqual({ kind: 'boolean', value: true }) - expect(v('gleich')).toEqual({ kind: 'boolean', value: true }) - expect(v('vorher')).toEqual({ kind: 'int', value: -1 }) - expect(v('laenger')).toEqual({ kind: 'int', value: 1 }) - expect(v('linie')).toEqual({ kind: 'string', value: '----' }) - expect(v('ganz')).toEqual({ kind: 'string', value: 'Ziegenbock' }) - expect(v('zahl')).toEqual({ kind: 'int', value: 42 }) - expect(v('haelfte')).toEqual({ kind: 'double', value: 3.5 }) - expect(v('text')).toEqual({ kind: 'string', value: '42!' }) - expect(state.outputLines).toEqual(['3 LUDWIG 17']) - }) - - it('addiert in einer Ausgabe wie Java erst die Zahlen und hängt ab dem ersten Text an', () => { - const state = runSimulation(doc([stmt('int a = 7'), stmt('double b = 0.5'), stmt('output: a + 1'), stmt('output: a + b + "!"'), stmt('output: "a = " + a + 1')])) - expect(state.outputLines).toEqual(['8', '7.5!', 'a = 71']) - }) - - it('meldet eine Datei, die es nicht gibt, und Text, der keine Zahl ist', () => { - expect(runSimulation(doc([stmt('String t = Files.readString(Path.of("gibtsnicht.txt"))')])).notice).toEqual({ kind: 'fileNotFound', text: 'gibtsnicht.txt' }) - expect(runSimulation(doc([stmt('int n = Integer.parseInt("zwölf")')])).notice?.kind).toBe('wholeNumberExpected') - }) - - it('liest Dateien mit Scanner und BufferedReader und schreibt sie erst beim close()', () => { - registerTextFile('vorrat.txt', 'Heu\nÄpfel') - const state = runSimulation( - doc([ - stmt('Scanner datei = new Scanner(new File("vorrat.txt"))'), - stmt('PrintWriter aus = new PrintWriter("kopie.txt")'), - makeElement(ElementType.whileSchleife, { textLines: ['datei.hasNextLine()'], loopBody: [stmt('aus.println(datei.nextLine())')] }), - stmt('aus.println("Kuchen")'), - stmt('datei.close()'), - stmt('output: "vorher: " + "x"'), - stmt('aus.close()'), - stmt('BufferedReader leser = new BufferedReader(new FileReader("kopie.txt"))'), - stmt('String zeile = leser.readLine()'), - makeElement(ElementType.whileSchleife, { textLines: ['zeile != null'], loopBody: [stmt('output: zeile'), stmt('zeile = leser.readLine()')] }), - stmt('leser.close()'), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - expect(state.outputLines).toEqual(['vorher: x', 'Heu', 'Äpfel', 'Kuchen']) - expect(state.files.get('kopie.txt')).toBe('Heu\nÄpfel\nKuchen\n') - }) - - it('setzt Texte mit \\" wie Java zusammen (JSON schreiben)', () => { - const state = runSimulation(doc([stmt('String name = "Zora"'), stmt('String eintrag = "{\\"name\\": \\"" + name + "\\"}"'), stmt('output: eintrag')])) - expect(state.outputLines).toEqual(['{"name": "Zora"}']) - }) - - it('rechnet wie Java mit Zeichencodes: (int), (char) und Zeichenliterale', () => { - const state = runSimulation( - doc([ - stmt('String wort = "XYZ"'), - stmt('int code = (int) wort.charAt(0)'), - stmt("char c = 'B'"), - stmt("int abstand = c - 'A'"), - stmt('char neu = (char) ((code - 65 + 3) % 26 + 65)'), - stmt('output: "Geheim: " + (char) (code + 1) + neu'), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - expect(state.variables.get('code')).toEqual({ kind: 'int', value: 88 }) - expect(state.variables.get('abstand')).toEqual({ kind: 'int', value: 1 }) - expect(state.variables.get('neu')).toEqual({ kind: 'string', value: 'A' }) - expect(state.outputLines).toEqual(['Geheim: YA']) - }) - - it('vergleicht Zeichen in einer Schleife', () => { - const state = runSimulation( - doc([ - stmt('String wort = "Kaffee"'), - stmt('int anzahl = 0'), - makeElement(ElementType.forSchleife, { - textLines: ['int i = 0', 'i < wort.length()', 'i++'], - loopBody: [ - makeElement(ElementType.verzweigung, { - textLines: ["wort.charAt(i) == 'e'"], - cases: [ - { name: 'true', elements: [stmt('anzahl = anzahl + 1')] }, - { name: 'false', elements: [stmt('ø')] }, - ], - }), - ], - }), - ]), - ) - expect(state.notice).toEqual({ kind: 'finished' }) - expect(state.variables.get('anzahl')).toEqual({ kind: 'int', value: 2 }) - }) - - it('meldet charAt außerhalb des Worts', () => { - const state = runSimulation(doc([stmt('String w = "abc"'), stmt('char c = w.charAt(3)')])) - expect(state.notice?.kind).toBe('invalidExpression') - }) -}) - -describe('Pseudocode', () => { - const lesson = (name: string) => decodeDocument(readFileSync(join(__dirname, '..', 'lessons', 'diagramme', name), 'utf8')) - - it('geht die Kästen von Lektion 1 ohne Fehler durch', () => { - for (const name of ['l1-tee.visustruct', 'l1-sequenz.visustruct', 'l1-grundbausteine.visustruct']) { - const state = runSimulation(lesson(name), ['true', 'false', 'false', 'false']) - expect(state.notice?.kind, name).toBe('finished') - } - expect(runSimulation(lesson('l1-tee.visustruct')).lastTrace).toBe('Teebeutel herausnehmen') - }) - - it('fragt bei einer Bedingung nach dem Weg', () => { - const branch = makeElement(ElementType.verzweigung, { - textLines: ['Regnet es?'], - cases: [ - { name: 'true', elements: [stmt('Schirm mitnehmen')] }, - { name: 'false', elements: [stmt('Sonnenbrille aufsetzen')] }, - ], - }) - const engine = new SimulationEngine({ caption: '', elements: [branch, stmt('Losgehen')], pseudocode: true }) - engine.step() - expect(engine.state.inputRequest?.prompt).toBe('Regnet es? (true/false)') - engine.provideInput('vielleicht') - expect(engine.state.inputErrorNotice?.kind).toBe('booleanExpected') - engine.provideInput('nein') - engine.step() - expect(engine.state.lastTrace).toBe('Sonnenbrille aufsetzen') - engine.step() - expect(engine.message).toBe('Simulation finished.') - }) - - it('wiederholt, solange man true antwortet', () => { - const loop = makeElement(ElementType.whileSchleife, { textLines: ['Teller nicht leer?'] }) - loop.loopBody = [stmt('Einen Bissen essen')] - const state = runSimulation({ caption: '', elements: [loop], pseudocode: true }, ['ja', 'ja', 'nein']) - expect(state.notice?.kind).toBe('finished') - expect(state.lastTrace).toBe('Teller nicht leer? -> false') - }) -}) diff --git a/web/test/site.test.ts b/web/test/site.test.ts deleted file mode 100644 index ee4c931a..00000000 --- a/web/test/site.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { hostWorld, siteLanguage, visucodeURL, worldHomeURL } from '../src/ui/site' - -describe('Webseiten auf visucode.de', () => { - it('erkennt die Welt an der Subdomain', () => { - expect(hostWorld('visubot.visucode.de')).toBe('visubot') - expect(hostWorld('VisuStruct.visucode.de')).toBe('visustruct') - expect(hostWorld('visuobject.visucode.de')).toBe('visuobject') - expect(hostWorld('visucode.de')).toBeUndefined() - expect(hostWorld('localhost')).toBeUndefined() - expect(hostWorld('visubot.visucode.de.example.com')).toBeUndefined() - }) - - it('nimmt de, en oder pt als Sprachordner', () => { - expect(siteLanguage('de')).toBe('de') - expect(siteLanguage('pt-PT')).toBe('pt') - expect(siteLanguage('fr')).toBe('en') - }) - - it('verlinkt die Startseite relativ nur auf der eigenen Subdomain', () => { - const bot = { hostname: 'visubot.visucode.de', pathname: '/app/index.html' } - expect(worldHomeURL('visubot', 'de', undefined, bot)).toBe('../de/index.html') - expect(worldHomeURL('visubot', 'pt-PT', 'privacy', bot)).toBe('../pt/index.html#privacy') - expect(worldHomeURL('visustruct', 'de', 'contact', bot)).toBe('https://visustruct.visucode.de/de/#contact') - const preview = { hostname: 'localhost', pathname: '/' } - expect(worldHomeURL('visuobject', 'en', undefined, preview)).toBe('https://visuobject.visucode.de/en/') - }) -}) - -describe('visucodeURL', () => { - it('führt auf visucode.de in der Sprache der App', () => { - expect(visucodeURL('de')).toBe('https://visucode.de/') - expect(visucodeURL('en')).toBe('https://visucode.de/en/') - expect(visucodeURL('pt-PT')).toBe('https://visucode.de/pt/') - expect(visucodeURL('fr')).toBe('https://visucode.de/en/') - }) -}) diff --git a/web/test/umlNotes.test.ts b/web/test/umlNotes.test.ts deleted file mode 100644 index ff158464..00000000 --- a/web/test/umlNotes.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { exampleProject, notesFromJSON, projectFromJSON, projectToJSON } from '../src/core/javaProject' -import { projectFromZip, projectToZip } from '../src/ui/projectZip' - -describe('Notizen im Klassendiagramm', () => { - const notes = [{ id: 'n1', text: 'Oberklasse\naller Tiere', x: 300, y: 40, attach: ['Tier'] }] - it('bleiben beim Speichern als JSON und ZIP erhalten', () => { - const project = { ...exampleProject(), notes } - expect(projectFromJSON(projectToJSON(project))?.notes).toEqual(notes) - expect(projectFromZip(projectToZip(project), 'x').notes).toEqual(notes) - }) - it('lässt kaputte Einträge weg', () => { - expect(notesFromJSON([{ id: 'a', text: 'ok', x: 1, y: 2 }, { id: 'b' }, null, 'x'])).toEqual([{ id: 'a', text: 'ok', x: 1, y: 2 }]) - expect(notesFromJSON({})).toBeUndefined() - }) -}) diff --git a/web/test/umlRelations.test.ts b/web/test/umlRelations.test.ts deleted file mode 100644 index 2c71e3cb..00000000 --- a/web/test/umlRelations.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { addInterface, parseClasses, removeInterface, setFieldType } from '../src/core/classModel' -import { associations, ensureImport, fieldNameFor, isMany, renameInfo, withInfo } from '../src/core/umlRelations' - -const code = `class Besitzer { - private ArrayList hunde = new ArrayList<>(); -} -class Hund { - private Besitzer besitzer; - private String name; -} -interface Laut {} -class Katze {} -` - -describe('umlRelations', () => { - it('erkennt viele an der Obergrenze', () => { - expect(['1', '0..1'].map(isMany)).toEqual([false, false]) - expect(['*', '0..*', '1..*', '2', '1..n'].map(isMany)).toEqual([true, true, true, true, true]) - }) - - it('macht aus zwei Attributen, die aufeinander zeigen, eine Linie mit zwei Pfeilen', () => { - const [r, ...rest] = associations(parseClasses(code)) - expect(rest).toEqual([]) - expect(r).toMatchObject({ from: 'Besitzer', to: 'Hund', role: 'hunde', multiplicity: '0..*', navFrom: 'navigable', navTo: 'navigable', sourceRole: 'besitzer', sourceMultiplicity: '1' }) - }) - - it('nimmt das Attribut mit UML-Zusatz nach vorne (Raute am Ganzen)', () => { - const [r] = associations(parseClasses(code), { 'Hund.besitzer': { kind: 'composition', multiplicity: '0..1' } }) - expect(r).toMatchObject({ key: 'Hund.besitzer', from: 'Hund', kind: 'composition', multiplicity: '0..1' }) - // Eine Multiplizität, die nicht zum Typ passt, gilt nicht. - const [s] = associations(parseClasses(code), { 'Besitzer.hunde': { name: 'führt aus', multiplicity: '1' } }) - expect(s).toMatchObject({ from: 'Besitzer', name: 'führt aus', multiplicity: '0..*' }) - }) - - it('schreibt UML-Zusatz ohne leere Felder', () => { - let uml = withInfo({}, 'A.b', { kind: 'aggregation', name: '' }) - expect(uml).toEqual({ 'A.b': { kind: 'aggregation' } }) - uml = withInfo(uml, 'A.b', { kind: 'association' }) - expect(uml).toEqual({}) - expect(renameInfo({ 'A.b': { name: 'x' } }, 'A.b', 'A.c')).toEqual({ 'A.c': { name: 'x' } }) - }) - - it('findet freie Attributnamen', () => { - const hund = parseClasses(code).find((c) => c.name === 'Hund')! - expect(fieldNameFor(hund, 'Besitzer', false)).toBe('besitzer2') - expect(fieldNameFor(hund, 'Katze', true)).toBe('katzeListe') - }) - - it('ergänzt Imports nur einmal', () => { - const text = 'package a;\n\nimport java.util.List;\n\npublic class X {}\n' - const once = ensureImport(text, 'b.Y') - expect(once).toBe('package a;\n\nimport java.util.List;\nimport b.Y;\n\npublic class X {}\n') - expect(ensureImport(once, 'b.Y')).toBe(once) - expect(ensureImport('package a;\n\npublic class X {}\n', 'b.Y')).toBe('package a;\n\nimport b.Y;\n\npublic class X {}\n') - }) - - it('ändert implements und den Typ eines Attributs', () => { - const katze = () => parseClasses(source).find((c) => c.name === 'Katze')! - let source = code - source = addInterface(source, katze(), 'Laut') - expect(source).toContain('class Katze implements Laut {}') - source = addInterface(source, katze(), 'Comparable') - expect(source).toContain('class Katze implements Laut, Comparable {}') - source = removeInterface(source, katze(), 'Laut') - expect(source).toContain('class Katze implements Comparable {}') - source = removeInterface(source, katze(), 'Comparable') - expect(source).toContain('class Katze {}') - const hund = parseClasses(code).find((c) => c.name === 'Hund')! - const field = hund.members.find((m) => m.name === 'besitzer')! - expect(setFieldType(code, field, 'ArrayList', 'new ArrayList<>()')).toContain('private ArrayList besitzer = new ArrayList<>();') - const besitzer = parseClasses(code).find((c) => c.name === 'Besitzer')! - expect(setFieldType(code, besitzer.members[0], 'Hund')).toContain(' private Hund hunde;\n') - }) -}) - -describe('dependencies', () => { - it('new in einer Methode ist «create», ein Parameter «use», ein Attribut keine Abhängigkeit', async () => { - const { parseClasses } = await import('../src/core/classModel') - const { dependencies } = await import('../src/core/umlRelations') - const texts: Record = { - Spielstarter: 'public class Spielstarter {\n public static void main(String[] args) {\n Ratespiel spiel = new Ratespiel();\n // new Punkte() zählt nicht\n }\n}\n', - Ratespiel: 'public class Ratespiel {\n private Punkte punkte;\n public void zeige(Anzeige a) {\n }\n}\n', - Punkte: 'public class Punkte {\n}\n', - Anzeige: 'public class Anzeige {\n}\n', - } - const classes = Object.values(texts).flatMap((x) => parseClasses(x)) - const deps = dependencies(classes, (c) => texts[c.name]) - expect(deps).toEqual([ - { from: 'Spielstarter', to: 'Ratespiel', kind: 'create', methods: ['main()'] }, - { from: 'Ratespiel', to: 'Anzeige', kind: 'use', methods: ['zeige()'] }, - ]) - }) -}) diff --git a/web/test/visubotLessons.test.ts b/web/test/visubotLessons.test.ts deleted file mode 100644 index a6b867f2..00000000 --- a/web/test/visubotLessons.test.ts +++ /dev/null @@ -1,192 +0,0 @@ -import { afterAll, beforeAll, describe, expect, it } from 'vitest' -import { checkExercise, loopDepth, usedElementTypes } from '../src/core/exerciseCheck' -import { ElementType } from '../src/core/model' -import { diagram, lessons, reference, tasks, useContentLanguage, useVisuBotContent, world } from '../src/learn/content' -import type { Block, Exercise } from '../src/learn/lessonFormat' -import { subprogramName } from '../src/core/simulation' -import type { LiveLabels } from '../src/core/codeGenerator' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava, plainLiveJava } from '../src/core/liveSource' -import { isMBotPart, MBOT_PARTS } from '../src/core/mbot' - -/** Unterprogramme einer Übung (`unterprogramm:`) für Aufrufe in der Musterlösung */ -import { lessonEndProblems } from './lessonEnd' -const resolver = (ex: Exercise) => { - const subs = ex.subprograms.map((f) => diagram(f)!) - return (name: string) => subs.find((s) => subprogramName(s.caption).toLowerCase() === subprogramName(name).toLowerCase()) -} - -const robotLessons = () => lessons.filter((l) => l.robot) - -const allBlocks = (): { where: string; block: Block }[] => [ - ...robotLessons().flatMap((l) => l.steps.flatMap((s) => s.blocks.map((block) => ({ where: `${l.id} / ${s.title}`, block })))), - ...tasks.filter((t) => t.robot).flatMap((t) => t.blocks.map((block) => ({ where: t.id, block }))), -] - -describe.each(['de', 'en'] as const)('VisuBot-Lektionen (%s)', (lang) => { - beforeAll(() => { - useVisuBotContent('on') - useContentLanguage(lang) - }) - afterAll(() => useVisuBotContent('off')) - - it('zeigt nur die Roboter-Lektionen und -Aufgaben, lückenlos nummeriert, in Teilen', () => { - expect(lessons.length).toBeGreaterThanOrEqual(33) - expect(lessons.every((l) => l.robot)).toBe(true) - lessons.forEach((l, i) => { - expect(l.number, l.id).toBe(i + 1) - expect(l.section, l.id).not.toBe('VisuBot') - }) - // vier Teile plus der Ausblick „Wie geht es weiter?“ ganz am Ende - expect(new Set(lessons.map((l) => l.section)).size).toBe(5) - expect(lessons[lessons.length - 1].id).toBe('23-wie-geht-es-weiter') - // die neue Lektion „Und, oder, nicht“ steht nach der Linie, ohne dass Dateien umbenannt werden - const at = (id: string) => lessons.findIndex((l) => l.id === id) - expect(at('07-und-oder')).toBe(at('07-linie') + 1) - expect(at('08-farben')).toBe(at('07-und-oder') + 1) - // Einstieg vorn, Testen nach den Variablen, do-while nach der While-Schleife, break nach den Tasten - expect(at('00-struktogramm-und-editor')).toBe(0) - // PAP und Pseudocode direkt nach dem Quadrat - expect(at('02-quadrat-als-pap')).toBe(at('02-das-quadrat') + 1) - expect(at('03-zeile-fuer-zeile-testen')).toBe(at('03-variablen-und-ausgabe') + 1) - expect(at('05-erst-fahren-dann-pruefen')).toBe(at('05-bis-zur-wand') + 1) - expect(at('10-unterbrechen-mit-break')).toBe(at('10-tasten-joystick') + 1) - // Grundlagen kleinschrittig: Zählen, Konstanten, else if, Maximum, Merker, Zustände - expect(at('05-zaehlen-und-summieren')).toBe(at('05-erst-fahren-dann-pruefen') + 1) - expect(at('06-konstanten-final')).toBe(at('06-abstand') + 1) - expect(at('06-nah-mittel-weit')).toBe(at('06-konstanten-final') + 1) - expect(at('08-groesster-wert')).toBe(at('08-farben') + 1) - expect(at('11-merker-boolean')).toBe(at('11-licht') + 1) - expect(at('21-zustaende')).toBe(at('21-stoppuhr-akku') + 1) - expect(tasks.length).toBeGreaterThanOrEqual(16) - expect(tasks.every((t) => t.robot)).toBe(true) - for (const t of tasks) if (t.after !== undefined) expect(lessons.some((l) => l.number === t.after), t.id).toBe(true) - }) - - it('Verweise auf VisuStruct zeigen auf vorhandene Lektionen', () => { - const targets = lessons.flatMap((l) => [...JSON.stringify(l.steps).matchAll(/\?visustruct&lektion=([\w-]+)/g)].map((m) => ({ lesson: l.id, target: m[1] }))) - expect(targets.length).toBeGreaterThanOrEqual(9) - useVisuBotContent('off') - const ids = new Set(lessons.map((l) => l.id)) - useVisuBotContent('on') - for (const { lesson, target } of targets) expect(ids.has(target), `${lesson} → ${target}`).toBe(true) - }) - - it('Verweise auf Lektionen zeigen auf die neue Nummer', () => { - const quadrat = lessons.find((l) => l.id === '02-das-quadrat')! - const tour = lessons.find((l) => l.id === '01-der-mbot2')! - const word = { de: 'Lektion', en: 'Lesson' }[lang] - expect(JSON.stringify(tour.steps).toLowerCase()).toContain(`${word} ${quadrat.number}`.toLowerCase()) - // eingeschobene Lektionen verschieben die Nummern: Motoren (Datei 20) ist Lektion 31 - const motoren = lessons.find((l) => l.id === '20-motoren')! - const sensoren = lessons.find((l) => l.id === '04-sensoren')! - expect(motoren.number).toBe(31) - expect(JSON.stringify(sensoren.steps).toLowerCase()).toContain(`${word} ${motoren.number}`.toLowerCase()) - }) - - it('jede Übung prüft eine Fahrt (ohne Roboter: eine Ausgabe), und die Musterlösung besteht sie', () => { - let checked = 0 - for (const { where, block } of allBlocks()) { - if (block.kind !== 'exercise') continue - const ex = block.exercise - expect(ex.id.startsWith('vb-'), `${where}: Kennung beginnt mit vb-`).toBe(true) - const robot = ex.tests.some((t) => t.drive || t.world || t.sensors || t.actions) - const doc = diagram(ex.solution!)! - expect(doc, `${where}: ${ex.solution}`).toBeDefined() - expect(robot, `${where}: fahrt:, befehle:, welt: oder messwerte:`).toBe(true) - expect(ex.hints.length).toBeGreaterThanOrEqual(2) - for (const o of checkExercise(doc, ex.tests, resolver(ex), world)) expect(o.passed, `${where}: ${JSON.stringify(o.result)}`).toBe(true) - for (const r of ex.requires) expect(usedElementTypes(doc).has(ElementType[r as keyof typeof ElementType])).toBe(true) - if (ex.loopDepth) expect(loopDepth(doc.elements)).toBeGreaterThanOrEqual(ex.loopDepth) - expect(checkExercise({ caption: '', elements: [] }, ex.tests, undefined, world).every((o) => o.passed)).toBe(false) - checked++ - } - expect(checked).toBeGreaterThanOrEqual(5) - }) - - it('Java-Übungen (code: java) lassen sich in Java tippen und bestehen', () => { - const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } - let checked = 0 - for (const { where, block } of allBlocks()) { - if (block.kind !== 'exercise' || block.exercise.code !== 'java') continue - const parsed = parseLiveJava(plainLiveJava(diagram(block.exercise.solution!)!, labels), labels) - expect(parsed.problems, where).toEqual([]) - const doc = documentFromJava({ caption: '', elements: [] }, parsed) - for (const o of checkExercise(doc, block.exercise.tests, undefined, world)) expect(o.passed, `${where}: ${JSON.stringify(o.result)}`).toBe(true) - checked++ - } - expect(checked).toBeGreaterThanOrEqual(20) - }) - - it('ab Lektion 4 hat jede Roboter-Lektion eine Übung „Warum läuft das nicht?“, deren Start die Tests nicht besteht', () => { - for (const l of robotLessons().filter((l) => Number(/^\d+/.exec(l.id)![0]) >= 4)) { - const blocks = l.steps.flatMap((s) => s.blocks) - const ex = blocks.find((b) => b.kind === 'exercise' && b.exercise.id === `vb-fehler-${l.id}`) - expect(ex, l.id).toBeDefined() - } - for (const { where, block } of allBlocks()) { - if (block.kind !== 'exercise' || !block.exercise.start) continue - const start = diagram(block.exercise.start) - expect(start, `${where}: ${block.exercise.start}`).toBeDefined() - expect(checkExercise(start!, block.exercise.tests, resolver(block.exercise), world).every((o) => o.passed), where).toBe(false) - } - }) - - it('jede Roboter-Lektion hat mindestens ein Spiel, und es steht vor der Checkliste', () => { - for (const lesson of robotLessons()) { - const kinds = lesson.steps.flatMap((s) => s.blocks.map((b) => b.kind)) - const game = kinds.findIndex((k) => k === 'game' || k === 'roundGame' || k === 'rescueGame' || k === 'driveGame' || k === 'crosswordGame') - expect(game, `${lesson.id}: kein Spiel`).toBeGreaterThanOrEqual(0) - expect(game, `${lesson.id}: Spiel nach der Checkliste`).toBeLessThan(kinds.indexOf('checklist')) - } - }) - - it('jede Roboter-Lektion endet mit eigenen Schritten für Checkliste und Wissenstest', () => { - expect(robotLessons().flatMap(lessonEndProblems)).toEqual([]) - }) - - it('Checklisten verweisen auf Schritte', () => { - for (const l of robotLessons()) - for (const s of l.steps) - for (const b of s.blocks) if (b.kind === 'checklist') for (const item of b.items) expect(item.step, `${l.id}: ${item.text}`).toBeDefined() - }) - - it('Lektion 1 ist eine Tour durch alle Teile, jedes gezeigte Teil ist im Text verlinkt', () => { - const tour = robotLessons().find((l) => l.id === '01-der-mbot2')! - const blocks = tour.steps.flatMap((s) => s.blocks) - const shown = new Set(blocks.flatMap((b) => (b.kind === 'robotModel' ? b.focus : b.kind === 'robotQuiz' ? b.tasks.map((t) => t.part) : []))) - expect([...shown].sort()).toEqual([...MBOT_PARTS].sort()) - expect(blocks.some((b) => b.kind === 'robotQuiz')).toBe(true) - for (const step of tour.steps) { - const text = JSON.stringify(step.blocks) - const linked = new Set([...text.matchAll(/\{(?:teil|part)=([\w,]+)\}/g)].flatMap((m) => m[1].split(','))) - for (const b of step.blocks) if (b.kind === 'robotModel') for (const p of b.focus) expect(linked, `${step.title}: ${p}`).toContain(p) - for (const p of linked) expect(isMBotPart(p), `${step.title}: ${p}`).toBe(true) - } - }) -}) - -describe.each(['de', 'en'] as const)('VisuStruct ohne Roboter, VisuBot nur mit Roboter (%s)', (lang) => { - const robotTasks = ['treppe', 'labyrinth', 'knight-rider', 'morsen'] - beforeAll(() => useContentLanguage(lang)) - afterAll(() => useVisuBotContent('off')) - - it('VisuStruct zeigt keine Roboter-Lektionen, -Aufgaben oder -Befehle', () => { - useVisuBotContent('off') - expect(lessons.length).toBeGreaterThanOrEqual(40) - expect(lessons.some((l) => l.section === 'VisuBot' || l.steps.some((s) => s.title.includes('🤖')))).toBe(false) - expect(tasks.some((t) => robotTasks.includes(t.id))).toBe(false) - expect(reference).not.toContain('mBot2') - }) - - it('VisuBot zeigt nur Roboter-Lektionen und -Aufgaben, Nachschlagen mit den mBot2-Kategorien', () => { - useVisuBotContent('on') - expect(lessons.filter((l) => l.robot).length).toBeGreaterThanOrEqual(33) - expect(lessons.filter((l) => !l.robot)).toEqual([]) - expect(tasks.map((t) => t.id)).toEqual(expect.arrayContaining(robotTasks)) - expect(tasks.filter((t) => !t.robot)).toEqual([]) - expect(reference).toContain('## 🤖 mBot2') - expect(reference).toContain('## 📡 mBot2') - expect(reference).toContain('### ' + { de: 'Verzweigung', en: 'Branch' }[lang]) - }) -}) diff --git a/web/test/visubotSensors.test.ts b/web/test/visubotSensors.test.ts deleted file mode 100644 index 074366f6..00000000 --- a/web/test/visubotSensors.test.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { describe, expect, it } from 'vitest' -import type { LiveLabels } from '../src/core/codeGenerator' -import { checkExercise } from '../src/core/exerciseCheck' -import { parseLiveJava } from '../src/core/javaParser' -import { documentFromJava } from '../src/core/liveSource' -import { actionSignature, mbotColorName, parseActionSpec, setMBotColorLanguage, usedMBotSensors } from '../src/core/mbot' -import { parseWorldMap, RobotWorld } from '../src/core/robotWorld' -import { SimulationEngine } from '../src/core/simulation' -import { parseSensorTest } from '../src/learn/lessonFormat' - -const labels: LiveLabels = { condition: 'Bedingung', selector: 'Wert', code: 'Dein Code', start: 'Startwert', step: 'Schritt', end: 'Ende' } -const main = (body: string) => `public class Main {\n public static void main(String[] args) {\n${body}\n }\n}` -const docOf = (java: string) => { - const parsed = parseLiveJava(java, labels, {}) - expect(parsed.problems).toEqual([]) - return documentFromJava({ caption: '', elements: [] }, parsed) -} - -function run(java: string, sensors: Record = {}) { - const engine = new SimulationEngine(docOf(java)) - engine.robotSensors = sensors - for (let i = 0; i < 1000 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted, engine.message).toBe(false) - return engine.state -} - -describe('mBot2-Sensoren', () => { - it('liest Wahrheitswerte wie Taste A in Bedingungen', () => { - const code = main(' if (mbot.isButtonA()) {\n mbot.forward();\n } else {\n mbot.turnRight();\n }') - expect(run(code, { isButtonA: true }).robotActions.map((a) => a.method)).toEqual(['forward']) - expect(run(code).robotActions.map((a) => a.method)).toEqual(['turnRight']) - const negated = main(' if (!mbot.isButtonB() && mbot.getBrightness() < 30) {\n mbot.setLed(255, 255, 255);\n }') - expect(run(negated, { getBrightness: 10 }).robotActions.map((a) => a.method)).toEqual(['setLed']) - expect(run(negated, { getBrightness: 10, isButtonB: true }).robotActions).toEqual([]) - }) - - it('speichert einen Sensorwert in einer boolean-Variable', () => { - const state = run(main(' boolean laut = mbot.getLoudness() > 50;\n boolean rot = mbot.getColor().equals("red");\n System.out.println(laut);\n System.out.println(rot);'), { getLoudness: 80, getColor: 'red' }) - expect(state.outputLines).toEqual(['true', 'true']) - }) - - it('liefert die Farbe als Text in der Sprache der Lektionen', () => { - const code = main(' String farbe = mbot.getColor();\n System.out.println(farbe);\n if (!mbot.getColor().equals("rot")) {\n mbot.forward();\n }') - setMBotColorLanguage('de') - try { - expect(run(code, { getColor: 'red' }).outputLines).toEqual(['rot']) - expect(run(code, { getColor: 'red' }).robotActions).toEqual([]) - // Messwerte in Lektionen dürfen deutsch oder englisch sein, der echte Sensor antwortet englisch - expect(run(code, { getColor: 'weiß' }).robotActions.map((a) => a.method)).toEqual(['forward']) - expect(mbotColorName('green')).toBe('grün') - } finally { - setMBotColorLanguage('en') - } - expect(run(code, { getColor: 'rot' }).outputLines).toEqual(['red']) - expect(parseSensorTest('getColor=rot/weiß -> none').sensors).toEqual({ getColor: ['rot', 'weiß'] }) - }) - - it('findet die benutzten Sensoren', () => { - expect(usedMBotSensors('if (mbot.getColor().equals("rot") || mbot.getPitch() > 20) mbot.forward(); mbot.getColor()').sort()).toEqual(['getColor', 'getPitch']) - }) - - it('erkennt farbige Felder unter dem Sensor', () => { - const world = new RobotWorld(parseWorldMap('...\n.r.\n...\n.^.')) - expect(world.sensors().getColor).toBe('white') - world.drive(10) - expect(world.colorUnder()).toBe('red') - expect(world.sensors()).toMatchObject({ getColor: 'red', getLine: 0 }) - expect(new RobotWorld(parseWorldMap('...\n.x.\n...\n.^.')).sensors().getColor).toBe('white') - }) - - it('prüft Übungen mit Messwerten, auch nacheinander', () => { - const wait = docOf(main(' while (mbot.getLoudness() < 50) {\n mbot.pause(100);\n }\n mbot.forward(30);')) - const test = parseSensorTest('getLoudness=0/80 -> forward 30') - expect(checkExercise(wait, [test])[0].passed).toBe(true) - const light = docOf(main(' if (mbot.getBrightness() < 30) {\n mbot.setLed(255, 255, 255);\n } else {\n mbot.ledOff();\n }')) - const tests = [parseSensorTest('getBrightness=10 -> ledOn, none'), parseSensorTest('getBrightness=80 -> ledOff')] - expect(checkExercise(light, tests).map((o) => o.passed)).toEqual([true, true]) - expect(checkExercise(light, [parseSensorTest('getBrightness=80 -> ledOn')])[0].passed).toBe(false) - expect(() => parseSensorTest('getFoo=1 -> forward')).toThrow() - }) - - it('zeigt Text mit Zahlen und Sensorwerten auf dem Bildschirm', () => { - const state = run(main(' int i = 3;\n mbot.showText("Abstand: " + mbot.getDistance());\n mbot.showText(i);'), { getDistance: 42 }) - expect(state.robotActions.map((a) => a.text)).toEqual(['Abstand: 42', '3']) - expect(state.robotActions[0].python).toBe(`exec("cyberpi.display.clear(); cyberpi.display.show_label(\\"Abstand: 42\\", 24, 'center')")`) - expect(actionSignature(state.robotActions)).toEqual(['showText "Abstand: 42"', 'showText "3"']) - }) - - it('zeigt Gefühle und lehnt unbekannte ab', () => { - const state = run(main(' mbot.emotion("smile");')) - expect(state.robotActions[0].python).toBe('cyberpi.emotion.smile()') - expect(actionSignature(state.robotActions)).toEqual(['emotion "smile"']) - const engine = new SimulationEngine(docOf(main(' mbot.emotion("tanzen");'))) - for (let i = 0; i < 10 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted).toBe(true) - }) - - it('steuert einzelne LEDs 0–4 mit Farbnamen und lehnt andere ab', () => { - const state = run(main(' for (int i = 0; i < 5; i++) {\n mbot.led(i).setColor("rot");\n }\n mbot.led(2).setColor("aus");\n mbot.led(4).setColor("blue");')) - expect(state.robotActions[0].python).toBe('cyberpi.led.on(255, 0, 0, id=1)') - expect(state.robotActions[5].python).toBe('cyberpi.led.on(0, 0, 0, id=3)') - expect(actionSignature(state.robotActions.slice(5))).toEqual(['ledColor 2 "aus"', 'ledColor 4 "blue"']) - const world = new RobotWorld() - for (const a of state.robotActions) world.apply(a) - expect(world.leds).toEqual([[255, 0, 0], [255, 0, 0], undefined, [255, 0, 0], [0, 0, 255]]) - world.apply({ method: 'ledOff', args: [], label: '' }) - expect(world.leds.every((l) => l === undefined)).toBe(true) - expect(parseActionSpec('ledColor 1 "grün", pause 100, ledColor 1 "aus"')).toEqual(['ledColor 1 "grün"', 'pause 100', 'ledColor 1 "aus"']) - expect(() => parseActionSpec('ledColor 5 "rot"')).toThrow() - for (const wrong of ['mbot.led(5).setColor("rot");', 'mbot.led(0).setColor("kariert");']) { - const engine = new SimulationEngine(docOf(main(` ${wrong}`))) - for (let i = 0; i < 10 && engine.canStep; i++) engine.step() - expect(engine.state.isHalted, wrong).toBe(true) - } - }) - - it('liest erwartete Befehle mit Wiederholung und Text', () => { - expect(parseActionSpec('2 x (setLed 0 0 255, pause 500), showText "Los, los!"')).toEqual(['setLed 0 0 255', 'pause 500', 'setLed 0 0 255', 'pause 500', 'showText "Los, los!"']) - expect(() => parseActionSpec('setLed 1 2')).toThrow() - }) -}) - -describe('mBot2-Motoren mit Geschwindigkeit', () => { - it('fährt mit drive weiter, bis stop kommt, und lenkt mit verschiedenen Rädern', () => { - const world = new RobotWorld() - world.apply({ method: 'drive', args: [50, 50], label: '' }) - world.tick(1000) - expect(world.pose.y).toBeCloseTo(10) - world.apply({ method: 'stop', args: [], label: '' }) - world.tick(1000) - expect(world.pose.y).toBeCloseTo(10) - world.apply({ method: 'drive', args: [10, 40], label: '' }) - world.tick(500) - expect(world.pose.heading).toBeGreaterThan(180) // nach links gedreht - expect(world.timer).toBe(2.5) - world.apply({ method: 'resetTimer', args: [], label: '' }) - expect(world.timer).toBe(0) - }) - - it('bleibt vor einer Wand stehen und zählt das Anstoßen einmal', () => { - const world = new RobotWorld(parseWorldMap('###\n...\n.^.')) - world.apply({ method: 'drive', args: [100, 100], label: '' }) - world.tick(3000) - expect(world.bumps).toBe(1) - expect(world.distance()).toBeLessThan(2) - }) - - it('liest Linien aus der Weltkarte und meldet die mittleren Fühler', () => { - const setup = parseWorldMap('.....\n.....\n..^..\nlinie: 2,2 2,0') - expect(setup.lines).toHaveLength(1) - const sensors = new RobotWorld(setup).sensors() - expect(sensors.isLineLeft).toBe(true) - expect(sensors.isLineRight).toBe(true) - }) - - it('prüft Bildschirmtext und ob er überhaupt fährt', () => { - const test = parseSensorTest('getBattery=15 -> drives, screen "Akku: 15 %, leer"') - expect(test.screen).toBe('Akku: 15 %, leer') - expect(test.drives).toBe(true) - const doc = docOf(main(' mbot.showText("Akku: " + mbot.getBattery() + " %, leer");\n mbot.forward();')) - expect(checkExercise(doc, [test])[0].passed).toBe(true) - expect(checkExercise(docOf(main(' mbot.showText("Akku");')), [test])[0].passed).toBe(false) - }) -}) diff --git a/web/test/workspace.test.ts b/web/test/workspace.test.ts deleted file mode 100644 index 73301b05..00000000 --- a/web/test/workspace.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { describe, expect, it } from 'vitest' -import { compareEntries, programBaseName, todayFolder, uniqueBaseName, validFolderName } from '../src/core/workspace' - -describe('Workspace', () => { - it('Tagesordner in Ortszeit', () => { - expect(todayFolder(new Date(2026, 9, 23, 23, 30))).toBe('2026-10-23') - expect(todayFolder(new Date(2026, 0, 5))).toBe('2026-01-05') - }) - - it('Ordner vor Dateien, Tage neueste zuerst', () => { - const entries = [ - { kind: 'file', name: 'lernstand.json' }, - { kind: 'directory', name: '2026-10-22' }, - { kind: 'directory', name: '2026-10-23' }, - { kind: 'directory', name: '11FI2' }, - { kind: 'directory', name: '11FI10' }, - ] - expect(entries.sort(compareEntries).map((e) => e.name)).toEqual(['11FI2', '11FI10', '2026-10-23', '2026-10-22', 'lernstand.json']) - }) - - it('Dateiname aus dem Titel', () => { - expect(programBaseName('Rechner', 'Programm')).toBe('Rechner') - expect(programBaseName(' Ja/Nein: Test? ', 'Programm')).toBe('Ja-Nein- Test-') - expect(programBaseName('', 'Programm')).toBe('Programm') - expect(programBaseName('Alt.visustruct', 'Programm')).toBe('Alt') - }) - - it('freier Name für Struktogramm und Code', () => { - expect(uniqueBaseName('Rechner', ['Andere.visustruct'])).toBe('Rechner') - expect(uniqueBaseName('Rechner', ['rechner.visustruct', 'Rechner 2.java'])).toBe('Rechner 3') - }) - - it('Ordnernamen', () => { - expect(validFolderName(' 11FI1 ')).toBe('11FI1') - expect(validFolderName('a/b')).toBe('ab') - expect(validFolderName('..')).toBeUndefined() - expect(validFolderName(' ')).toBeUndefined() - }) -}) diff --git a/web/test/xmlCodec.test.ts b/web/test/xmlCodec.test.ts deleted file mode 100644 index a6832ec3..00000000 --- a/web/test/xmlCodec.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { readdirSync, readFileSync, statSync } from 'node:fs' -import { join, relative } from 'node:path' -import { describe, expect, it } from 'vitest' -import { decodeJavaString, encodeJavaString } from '../src/core/javaStringCoding' -import { decodeDocument, encodeDocument } from '../src/core/xmlCodec' - -const examplesDir = join(__dirname, '..', '..', 'VisuStruct-Dateien') - -function allDiagramFiles(dir: string): string[] { - return readdirSync(dir) - .flatMap((name) => { - const p = join(dir, name) - if (statSync(p).isDirectory()) return allDiagramFiles(p) - return /\.(visustruct|strk)$/.test(name) ? [p] : [] - }) - .sort() -} - -describe('Java-Zeichenkodierung', () => { - it('kodiert wie Java encodeS', () => { - expect(encodeJavaString('')).toBe('-1;') - expect(encodeJavaString('a ä')).toBe('97;32;228;') - expect(encodeJavaString('😀')).toBe('128512;') - }) - it('dekodiert wie Java decodeS', () => { - expect(decodeJavaString('-1;')).toBe('') - expect(decodeJavaString('97;32;228;')).toBe('a ä') - expect(decodeJavaString('128512;')).toBe('😀') - }) -}) - -describe('Beispieldateien aus VisuStruct-Dateien', () => { - const files = allDiagramFiles(examplesDir) - - it('findet Beispieldateien', () => { - expect(files.length).toBeGreaterThan(0) - }) - - for (const file of files) { - const name = relative(examplesDir, file) - it(`${name}: Laden und Speichern ergibt dieselbe Datei wie die Swift-App`, () => { - const original = readFileSync(file, 'utf8') - const doc = decodeDocument(original) - expect(doc.elements.length).toBeGreaterThan(0) - const reencoded = encodeDocument(doc) - // Verlustfrei: erneutes Laden ergibt dasselbe Modell … - expect(decodeDocument(reencoded)).toEqual(doc) - // … und die Bytes entsprechen dem Swift-Encoder (der die Beispieldateien geschrieben hat). - // Unter Windows gespeicherte Dateien haben CRLF; Swift schreibt wie wir LF. - expect(reencoded).toBe(original.replace(/\r\n/g, '\n')) - }) - } -}) - -describe('Pseudocode-Markierung', () => { - it('bleibt beim Speichern erhalten und fehlt sonst', () => { - const marked = { caption: 'Tee', pseudocode: true, elements: [] } - const xml = encodeDocument(marked) - expect(xml).toContain('pseudocode="true"') - expect(decodeDocument(xml).pseudocode).toBe(true) - const plain = encodeDocument({ caption: 'Tee', elements: [] }) - expect(plain).not.toContain('pseudocode') - expect(decodeDocument(plain).pseudocode).toBeUndefined() - }) -}) diff --git a/web/tsconfig.json b/web/tsconfig.json deleted file mode 100644 index 6aaadbd1..00000000 --- a/web/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ESNext", - "moduleResolution": "Bundler", - "lib": ["ES2022", "DOM", "DOM.Iterable"], - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "skipLibCheck": true, - "types": ["vite/client", "node"] - }, - "include": ["src", "test"] -} diff --git a/web/vite.config.ts b/web/vite.config.ts deleted file mode 100644 index 27eb57af..00000000 --- a/web/vite.config.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { existsSync, readFileSync } from 'node:fs' -import { join } from 'node:path' -import type { Plugin } from 'vite' -import { defineConfig } from 'vitest/config' -import { viteSingleFile } from 'vite-plugin-singlefile' -import pkg from './package.json' - -/** - * `licenses.txt` neben der Web-App: die Lizenztexte aller mitgelieferten Pakete (Abhängigkeiten ohne devDependencies - * aus package-lock.json), dazu Hinweise auf das, was erst zur Laufzeit von fremden Servern geladen wird. - * Die MIT-Lizenz von CodeMirror und Lezer verlangt, dass ihr Text mit ausgeliefert wird. - */ -const CYBERPI_UPLOADER_LICENSE = `MIT License - -Copyright (c) 2026 TWO PLUS THREE LIMITED - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE.` - -function thirdPartyLicenses(): Plugin { - return { - name: 'third-party-licenses', - generateBundle() { - const lock = JSON.parse(readFileSync('package-lock.json', 'utf8')) as { packages: Record } - const parts = [ - 'VisuStruct Web – verwendete Software / Third-party software', - '', - 'VisuStruct: MIT License, Copyright (c) 2026 Holger Sebastiao, https://github.com/code4teaching/VisuStruct-SwiftUI', - '', - 'Nur mit „Swing-Unterstützung“ wird zur Laufzeit geladen / Loaded at run time only with “Swing support”:', - '- CheerpJ by Leaning Technologies, https://cheerpj.com (CheerpJ Community License, from the CheerpJ CDN)', - '- javac from OpenJDK 8 (tools.jar from https://github.com/leaningtech/javafiddle, Apache License 2.0; OpenJDK: GPLv2 with Classpath Exception)', - '', - 'Übertragen auf den mBot2 / Upload to the mBot2: Ablauf nach / protocol after cyberpi-uploader, https://github.com/weare2plus3/cyberpi-uploader', - ...CYBERPI_UPLOADER_LICENSE.split('\n'), - '', - 'Mitgelieferte Pakete / Bundled packages:', - ] - const entries = Object.entries(lock.packages) - .filter(([path, p]) => path.startsWith('node_modules/') && !p.dev) - .sort(([a], [b]) => a.localeCompare(b)) - for (const [path, p] of entries) { - const name = path.replace(/^.*node_modules\//, '') - const file = ['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'LICENCE'].map((f) => join(path, f)).find((f) => existsSync(f)) - parts.push('', '='.repeat(72), `${name} ${p.version ?? ''} (${p.license ?? 'see below'})`, '='.repeat(72), '') - parts.push(file ? readFileSync(file, 'utf8').trim() : 'License text not found in the package.') - } - this.emitFile({ type: 'asset', fileName: 'licenses.txt', source: parts.join('\n') + '\n' }) - }, - } -} - -// `npm run build` → dist/ (relative paths, e.g. for website/app/). -// `npm run build:single` → one self-contained index.html (preview/sharing). -export default defineConfig(({ mode }) => ({ - base: './', - // Versionsnummer der Web-App für „Über VisuStruct“; vor jeder Veröffentlichung in package.json erhöhen. - define: { __APP_VERSION__: JSON.stringify(pkg.version) }, - plugins: mode === 'single' ? [viteSingleFile()] : [thirdPartyLicenses()], - build: { outDir: mode === 'single' ? 'dist-single' : 'dist' }, - // Beispieldateien liegen eine Ebene höher in `VisuStruct-Dateien`. - server: { fs: { allow: ['..'] } }, - // VisuBlocks kommt als TypeScript-Quelle (github:code4teaching/visublocks): von Vitest mit übersetzen lassen - test: { environment: 'jsdom', server: { deps: { inline: ['visublocks'] } } }, -})) diff --git a/website/app/assets/Java-Bpx1Vwvg.js b/website/app/assets/Java-Bpx1Vwvg.js deleted file mode 100644 index 587f4c9b..00000000 --- a/website/app/assets/Java-Bpx1Vwvg.js +++ /dev/null @@ -1,226 +0,0 @@ -const e=`/* VisuStruct Java-Desktop-Preset (kein .lproj — gültig für App Store; Tabelle „Java“). */ - -/* Palette — short labels like Java */ -"palette_header" = "Blocks"; - -/* Actions */ -"action_share_png" = "Share Graphic…"; -"action_share_document" = "Share File…"; -"action_export_pdf" = "Preview PDF…"; -"action_generate_code" = "Generate Code…"; -"action_simulation" = "Simulation"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "The simulation mode will run the diagram step by step, highlight the current element, and show variable values and output."; -"simulation_controls" = "Controls"; -"simulation_highlight_label" = "Highlight"; -"simulation_highlight_last" = "Last step"; -"simulation_highlight_next" = "Next step"; -"simulation_not_available" = "Play runs the simulation automatically with a 0.5 second pause per step."; -"simulation_play" = "Play"; -"simulation_pause" = "Pause"; -"simulation_step" = "Step"; -"simulation_stop" = "Stop"; -"simulation_input" = "Input"; -"simulation_input_placeholder" = "No input required."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Please enter a whole number."; -"simulation_trace" = "Last Step"; -"simulation_trace_placeholder" = "The last executed step will be explained here."; -"simulation_variables" = "Variables"; -"simulation_variables_placeholder" = "Current variable values will be shown here later."; -"simulation_output" = "Output"; -"simulation_output_placeholder" = "The simulation output will appear here later."; -"action_delete_selection" = "Delete Selection"; -"ctx_copy" = "Copy"; -"ctx_cut" = "Cut"; -"ctx_paste" = "Paste"; -"action_settings" = "Settings"; -"action_about" = "About VisuStruct"; -"recent_files_menu" = "Recent Files"; -"recent_files_empty" = "No recent files"; -"recent_files_clear" = "Clear List"; -"recent_files_unavailable" = "File is no longer available."; - -/* In-app settings */ -"settings_language_title" = "Language"; -"settings_language_de" = "German"; -"settings_language_en" = "English"; -"settings_language_es" = "Spanish"; -"settings_language_fr" = "French"; -"settings_language_it" = "Italian"; -"settings_language_pl" = "Polish"; -"settings_language_tr" = "Turkish"; -"settings_language_ko" = "Korean"; -"settings_language_ja" = "Japanese"; -"settings_language_el" = "Greek"; -"settings_language_uk" = "Ukrainian"; -"settings_language_ar" = "Arabic"; -"settings_language_ru" = "Russian"; -"settings_language_he" = "Hebrew"; -"settings_language_zh_hans" = "Chinese (Simplified)"; -"settings_language_zh_hant" = "Chinese (Traditional)"; -"settings_language_ptpt" = "Portuguese"; -"settings_language_java" = "Java (keywords)"; - - - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "File name suggestion"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "With date"; -"settings_export_filename_title_only" = "Without date"; -"settings_export_filename_footer_with_date" = "With date: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Without date: Filename.visustruct"; - - -/* Error overlay */ -"error_title" = "Error"; -"error_back_editor" = "Back to Editor"; - -/* Toolbar */ -"toolbar_undo" = "Undo"; -"toolbar_redo" = "Redo"; -"toolbar_new" = "New"; -"menu_open" = "Open…"; -"menu_save" = "Save"; -"menu_save_as" = "Save As…"; -"menu_print" = "Print…"; -"menu_file" = "File"; -"menu_new" = "New"; -"menu_close" = "Close"; - -/* Accessibility */ -"a11y_generate_code" = "Generate code from the structure chart"; -"a11y_generate_code_locked" = "Generate code, available after purchase"; -"a11y_unsaved_changes" = "Unsaved changes"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Generate code"; -"codegen_gate_body" = "Java, Python, and JavaScript code from your diagram will unlock via in-app purchase (coming soon)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Code generator"; -"codegen_placeholder_body" = "Generated code will appear here."; -"codegen_placeholder_compare" = "Switch between Java, Python, and JavaScript to compare syntax (sample below)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condition) {\\n statement();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if condition:\\n statement()\\nelse:\\n alternative()"; -"codegen_generated_hint" = "Preview from the current structure chart (Java 17, Python 3.10+, modern JavaScript)."; -"codegen_copy" = "Copy"; -"codegen_run_in_browser" = "Test in browser"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "New diagram tab"; -"a11y_close_tab" = "Close tab"; - -"close_unsaved_title" = "Unsaved Changes"; -"close_unsaved_message" = "This diagram contains unsaved changes. Do you want to save them before closing?"; -"close_unsaved_discard" = "Discard"; -"close_unsaved_cancel" = "Cancel"; -"a11y_placeholder_drop" = "Drop an element here to replace the placeholder. Press and hold to drag and move."; -"a11y_node_drop" = "Press and hold a block, then drag: drop on the top or bottom edge of a target to insert before or after. Center drop only on a ø placeholder."; -"a11y_codegen_panel_drag" = "Drag to move code generator panel"; - -/* Diagram */ -"diagram_title_label" = "Title"; -"diagram_title_placeholder" = "No title"; - -/* Inspector */ -"inspector_no_selection" = "No element selected — tap a tile."; -"inspector_apply_text" = "Apply Text"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; -"inspector_io_mode_title" = "Kind"; -"inspector_io_mode_statement" = "Statement"; -"inspector_io_mode_input" = "Input"; -"inspector_io_mode_output" = "Output"; -"inspector_io_mode_help_java_like" = "Input: input: … | Output: output: …"; -"inspector_add_case" = "Add Case"; -"inspector_remove_case" = "Remove Case"; -"inspector_group_element" = "Element text"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Insert condition…"; -"inspector_placeholder_selector" = "Insert selector…"; -"inspector_placeholder_call" = "Insert call…"; -"inspector_placeholder_break" = "Comment (optional)"; - -/* Element types — JAVA_STANDARD_PALETTE_LABELS (palette + inspector title) */ -"element_anweisung" = "Statement"; -"element_verzweigung" = "if"; -"element_fallauswahl" = "switch"; -"element_forSchleife" = "for"; -"element_whileSchleife" = "while"; -"element_doUntilSchleife" = "do"; -"element_endlosschleife" = "while(true)"; -"element_aussprung" = "break"; -"element_aufruf" = "call"; -"element_leerElement" = "ø"; - -/* Tabs */ -"tab_title_format" = "Diagram %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Empty structure chart"; -"canvas_empty_hint" = "Drag a block from “Blocks” here."; - -/* New block stubs — PRESETS[PRESET_ENGLISH_JAVA] */ -"canvas_stub_anweisung" = "Statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "selector"; -"canvas_stub_for" = "i = 0; i < n; i++"; -"canvas_stub_while" = "condition"; -"canvas_stub_until" = "condition"; -"canvas_stub_endless" = "∞"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "method()"; - -/* About */ -"about_version_fmt" = "Version %@ (%@)"; -"about_nav_title" = "About VisuStruct"; -"about_features_heading" = "Features"; -"about_bullet_palette" = "Insert blocks from the palette or drag them into the diagram"; -"about_bullet_edit" = "Edit the chart: change text, select elements, and remove them if needed"; -"about_bullet_tabs" = "Several diagrams in tabs — each with its own title"; -"about_bullet_file" = "Save and reopen diagrams as .visustruct files"; -"about_bullet_share" = "Share the structure chart as an image (PNG)"; -"about_dateiformat_heading" = "File format"; -"about_dateiformat_part1" = "VisuStruct’s native format uses the extension "; -"about_dateiformat_part2" = ". You can keep diagrams and exchange them with other apps that support the same format."; -"about_dateiformat_strk" = "Files with the .strk extension can also be opened."; -"about_project_github_label" = "Java app (source & releases):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Developer:"; -"about_lineage_heading" = "Lineage"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Done"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Cancel insert"; -"phone_show_text_editor" = "Edit text"; - -/* Alerts */ -"err_save_failed_fmt" = "Could not save: %@"; -"err_share_prepare_failed_fmt" = "Could not prepare sharing: %@"; -"err_diagram_too_small_export" = "The diagram is too small — nothing to export."; -"err_image_render_failed" = "Could not create the image."; -"err_diagram_too_small_print" = "The diagram is too small — nothing to print."; -"err_print_unavailable" = "Printing is not available on this device."; - -"inspector_header" = "Object Inspector"; -`;export{e as default}; diff --git a/website/app/assets/Localizable-1QZ0wfvJ.js b/website/app/assets/Localizable-1QZ0wfvJ.js deleted file mode 100644 index 5db9238a..00000000 --- a/website/app/assets/Localizable-1QZ0wfvJ.js +++ /dev/null @@ -1,240 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Элементы"; -"inspector_header" = "Инспектор объектов"; - -/* Actions */ -"action_share_png" = "Поделиться изображением…"; -"action_share_document" = "Поделиться файлом…"; -"action_export_pdf" = "Предпросмотр PDF…"; -"action_generate_code" = "Сгенерировать код…"; -"action_simulation" = "Симуляция"; -"action_diagram" = "Диаграмма"; -"simulation_placeholder_body" = "Режим симуляции выполняет диаграмму шаг за шагом, подсвечивает текущий элемент и показывает значения переменных и вывод."; -"simulation_controls" = "Управление"; -"simulation_highlight_label" = "Подсветка"; -"simulation_highlight_last" = "Последний шаг"; -"simulation_highlight_next" = "Следующий шаг"; -"simulation_not_available" = "Play автоматически выполняет симуляцию с паузой 0,5 секунды на шаг."; -"simulation_play" = "Пуск"; -"simulation_pause" = "Пауза"; -"simulation_step" = "Шаг"; -"simulation_stop" = "Стоп"; -"simulation_input" = "Ввод"; -"simulation_input_placeholder" = "Ввод не требуется."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Введите целое число."; -"simulation_trace" = "Последний шаг"; -"simulation_trace_placeholder" = "Ещё не выполнено ни одного шага."; -"simulation_variables" = "Переменные"; -"simulation_variables_placeholder" = "Переменных пока нет."; -"simulation_output" = "Вывод"; -"simulation_output_placeholder" = "Вывода пока нет."; -"action_delete_selection" = "Удалить выделение"; -"ctx_copy" = "Копировать"; -"ctx_cut" = "Вырезать"; -"ctx_paste" = "Вставить"; -"action_settings" = "Настройки"; -"action_about" = "О VisuStruct"; -"recent_files_menu" = "Недавние файлы"; -"recent_files_empty" = "Нет недавних файлов"; -"recent_files_clear" = "Очистить список"; -"recent_files_unavailable" = "Файл больше недоступен."; - -/* In-app settings */ -"settings_language_title" = "Язык"; -"settings_language_de" = "Немецкий"; -"settings_language_en" = "Английский"; -"settings_language_es" = "Испанский"; -"settings_language_fr" = "Французский"; -"settings_language_it" = "Итальянский"; -"settings_language_pl" = "Польский"; -"settings_language_tr" = "Турецкий"; -"settings_language_ko" = "Корейский"; -"settings_language_ja" = "Японский"; -"settings_language_el" = "Греческий"; -"settings_language_uk" = "Украинский"; -"settings_language_ar" = "Арабский"; -"settings_language_ru" = "Русский"; -"settings_language_zh_hans" = "Китайский (упрощённый)"; -"settings_language_zh_hant" = "Китайский (традиционный)"; -"settings_language_ptpt" = "Португальский"; -"settings_language_java" = "Java (ключевые слова)"; - - -/* Error overlay */ -"error_title" = "Ошибка"; -"error_back_editor" = "Назад в редактор"; - -/* Toolbar */ -"toolbar_undo" = "Отменить"; -"toolbar_redo" = "Повторить"; -"toolbar_new" = "Новый"; -"menu_open" = "Открыть…"; -"menu_save" = "Сохранить"; -"menu_save_as" = "Сохранить как…"; -"menu_print" = "Печать…"; -"menu_file" = "Файл"; -"menu_new" = "Создать"; -"menu_close" = "Закрыть"; - -/* Accessibility */ -"a11y_generate_code" = "Сгенерировать код по схеме"; -"a11y_generate_code_locked" = "Сгенерировать код (после покупки)"; -"a11y_unsaved_changes" = "Несохранённые изменения"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Сгенерировать код"; -"codegen_gate_body" = "Код Java, Python и JavaScript по вашей схеме будет разблокирован через покупку в приложении (скоро)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Генератор кода"; -"codegen_placeholder_body" = "Сгенерированный код появится здесь."; -"codegen_placeholder_compare" = "Переключайтесь между Java, Python и JavaScript, чтобы сравнить синтаксис (пример ниже)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (условие) {\\n statement();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if условие:\\n statement()\\nelse:\\n alternative()"; -"codegen_copy" = "Копировать"; -"codegen_run_in_browser" = "Проверить в браузере"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Новая вкладка схемы"; -"a11y_close_tab" = "Закрыть вкладку"; - -"close_unsaved_title" = "Несохранённые изменения"; -"close_unsaved_message" = "В этой диаграмме есть несохранённые изменения. Сохранить их перед закрытием?"; -"close_unsaved_discard" = "Отбросить"; -"close_unsaved_cancel" = "Отмена"; -"a11y_placeholder_drop" = "Перетащите элемент сюда, чтобы заменить заполнитель. Нажмите и удерживайте, чтобы перемещать."; -"a11y_node_drop" = "Нажмите и удерживайте блок, затем перетащите: отпустите на верхнем или нижнем крае цели, чтобы вставить до или после. Сброс в центр — только на заполнителе ø."; -"a11y_codegen_panel_drag" = "Перетащите панель генератора кода"; - -/* Diagram */ -"diagram_title_label" = "Название"; -"diagram_title_placeholder" = "Без названия"; - -/* Inspector */ -"inspector_no_selection" = "Элемент не выбран — нажмите на плитку."; -"inspector_apply_text" = "Применить текст"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Вид"; -"inspector_io_mode_statement" = "Инструкция"; -"inspector_io_mode_input" = "Ввод"; -"inspector_io_mode_output" = "Вывод"; -"inspector_io_mode_help_java_like" = "Ввод: input: … | Вывод: output: …"; -"inspector_add_case" = "Добавить вариант"; -"inspector_remove_case" = "Удалить вариант"; -"inspector_group_element" = "Текст элемента"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Вставить условие…"; -"inspector_placeholder_selector" = "Вставить селектор…"; -"inspector_placeholder_call" = "Вставить вызов…"; -"inspector_placeholder_break" = "Комментарий (необязательно)"; - -/* Element types (Russian + keyword hint) */ -"element_anweisung" = "Оператор (statement)"; -"element_verzweigung" = "Условие (if/else)"; -"element_fallauswahl" = "Выбор (switch)"; -"element_forSchleife" = "Цикл (for)"; -"element_whileSchleife" = "Цикл (while)"; -"element_doUntilSchleife" = "Цикл (do-while)"; -"element_endlosschleife" = "Бесконечный цикл (while true)"; -"element_aussprung" = "Выход (break)"; -"element_aufruf" = "Вызов (call)"; -"element_leerElement" = "Пустой элемент"; - -/* Tabs */ -"tab_title_format" = "Схема %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Пустая структурная схема"; -"canvas_empty_hint" = "Перетащите элемент из «Элементы» сюда."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Выбор (switch)"; -"canvas_stub_anweisung" = "Оператор (statement)"; -"canvas_stub_condition" = "Условие"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "бесконечно"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Вызов()"; - -/* About */ -"about_version_fmt" = "Версия %@ (%@)"; -"about_nav_title" = "О VisuStruct"; -"about_features_heading" = "Возможности"; -"about_bullet_palette" = "Вставляйте блоки из палитры или перетаскивайте их в схему"; -"about_bullet_edit" = "Редактируйте схему: меняйте текст, выбирайте элементы и удаляйте при необходимости"; -"about_bullet_tabs" = "Несколько схем во вкладках — каждая со своим названием"; -"about_bullet_file" = "Сохраняйте и открывайте схемы как файлы .visustruct"; -"about_bullet_share" = "Делитесь схемой как изображением (PNG)"; -"about_dateiformat_heading" = "Формат файла"; -"about_dateiformat_part1" = "Родной формат VisuStruct использует расширение "; -"about_dateiformat_part2" = ". Вы можете сохранять схемы и обмениваться ими с другими приложениями, поддерживающими этот формат."; -"about_dateiformat_strk" = "Файлы с расширением .strk также можно открывать."; -"about_project_github_label" = "Java‑приложение (исходники и релизы):"; -"about_web_label" = "Веб:"; -"about_developer_label" = "Разработчик:"; -"about_lineage_heading" = "Происхождение"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Готово"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Отменить вставку"; -"phone_show_text_editor" = "Редактировать текст"; -"phone_hide_text_editor" = "Свернуть"; -"phone_title_done" = "Готово"; - -/* Alerts */ -"err_save_failed_fmt" = "Не удалось сохранить: %@"; -"err_share_prepare_failed_fmt" = "Не удалось подготовить общий доступ: %@"; -"err_diagram_too_small_export" = "Схема слишком мала — нечего экспортировать."; -"err_image_render_failed" = "Не удалось создать изображение."; -"err_diagram_too_small_print" = "Схема слишком мала — нечего печатать."; -"err_print_unavailable" = "Печать недоступна на этом устройстве."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Имя файла (предложение)"; -"settings_export_filename_picker" = "Формат"; -"settings_export_filename_date_title" = "С датой"; -"settings_export_filename_title_only" = "Без даты"; -"settings_export_filename_footer_with_date" = "С датой: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Без даты: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Симуляция завершена."; -"sim_notice_infinite_loop_empty" = "У бесконечного цикла нет тела."; -"sim_notice_unsupported_statement_fmt" = "Неподдерживаемая инструкция: %@"; -"sim_notice_invalid_expression_fmt" = "Недопустимое выражение: %@"; -"sim_notice_unknown_variable_fmt" = "Неизвестная переменная: %@"; -"sim_notice_division_by_zero" = "Деление на ноль."; -"sim_notice_whole_number_expected" = "Введите целое число."; -"sim_notice_number_expected" = "Введите число."; -"sim_notice_boolean_expected" = "Введите true или false."; -"sim_notice_subprogram_not_found_fmt" = "Нет открытой диаграммы с именем %@. Вызов пропущен."; -"sim_notice_call_too_deep" = "Слишком много вложенных вызовов. Подпрограмма вызывает сама себя?"; - -"codegen_pseudocode_toggle" = "Псевдокод (не исполняемый код)"; -"codegen_pseudocode_note" = "Псевдокод – не исполняемая программа"; -"codegen_pseudocode_hint" = "Эта диаграмма отмечена как псевдокод. Код — лишь набросок, его нельзя запустить."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-BAutdlBb.js b/website/app/assets/Localizable-BAutdlBb.js deleted file mode 100644 index ab6b7d13..00000000 --- a/website/app/assets/Localizable-BAutdlBb.js +++ /dev/null @@ -1,153 +0,0 @@ -const e=`/* NOTE: Initial Danish localization. */ -"palette_header" = "Elementer"; -"inspector_header" = "Objektinspektør"; - -/* Actions */ -"action_share_png" = "Del billede…"; -"action_share_document" = "Del fil…"; -"action_export_pdf" = "Vis PDF…"; -"action_generate_code" = "Generér kode…"; -"action_simulation" = "Simulering"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "Simuleringstilstanden kører diagrammet trin for trin, fremhæver det aktuelle element og viser variabelværdier og output."; -"simulation_controls" = "Kontroller"; -"simulation_highlight_label" = "Fremhævning"; -"simulation_highlight_last" = "Sidste trin"; -"simulation_highlight_next" = "Næste trin"; -"simulation_not_available" = "Play kører simuleringen automatisk med en pause på 0,5 sekund pr. trin."; -"simulation_play" = "Afspil"; -"simulation_pause" = "Pause"; -"simulation_step" = "Trin"; -"simulation_stop" = "Stop"; -"simulation_input" = "Input"; -"simulation_input_placeholder" = "Ingen input påkrævet."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Indtast et heltal."; -"simulation_trace" = "Sidste trin"; -"simulation_trace_placeholder" = "Intet trin udført endnu."; -"simulation_variables" = "Variabler"; -"simulation_variables_placeholder" = "Ingen variabler endnu."; -"simulation_output" = "Output"; -"simulation_output_placeholder" = "Intet output endnu."; -"action_delete_selection" = "Slet markering"; -"ctx_copy" = "Kopiér"; -"ctx_cut" = "Klip"; -"ctx_paste" = "Indsæt"; -"action_settings" = "Indstillinger"; -"action_about" = "Om VisuStruct"; -"recent_files_menu" = "Seneste filer"; -"recent_files_empty" = "Ingen seneste filer"; -"recent_files_clear" = "Ryd liste"; -"recent_files_unavailable" = "Filen er ikke længere tilgængelig."; - -/* In-app settings */ -"settings_language_title" = "Sprog"; - -/* Toolbar */ -"toolbar_undo" = "Fortryd"; -"toolbar_redo" = "Annuller fortryd"; -"toolbar_new" = "Ny"; -"menu_open" = "Åbn…"; -"menu_save" = "Gem"; -"menu_save_as" = "Gem som…"; -"menu_print" = "Udskriv…"; -"menu_file" = "Arkiv"; -"menu_new" = "Ny"; -"menu_close" = "Luk"; - -/* Inspector */ -"inspector_no_selection" = "Intet valgt — tryk på en flise."; -"inspector_apply_text" = "Anvend tekst"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Type"; -"inspector_io_mode_statement" = "Sætning"; -"inspector_io_mode_input" = "Input"; -"inspector_io_mode_output" = "Output"; -"inspector_io_mode_help_java_like" = "Input: input: … | Output: output: …"; -"inspector_add_case" = "Tilføj case"; -"inspector_remove_case" = "Fjern case"; -"inspector_group_element" = "Elementtekst"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Indtast condition…"; -"inspector_placeholder_selector" = "Indtast selector…"; -"inspector_placeholder_call" = "Indtast call…"; -"inspector_placeholder_break" = "Kommentar (valgfrit)"; - -/* Element types */ -"element_anweisung" = "Sætning"; -"element_verzweigung" = "Betingelse (if/else)"; -"element_fallauswahl" = "Valg (switch)"; -"element_forSchleife" = "For-løkke"; -"element_whileSchleife" = "While-løkke"; -"element_doUntilSchleife" = "Do-while-løkke"; -"element_endlosschleife" = "Uendelig løkke"; -"element_aussprung" = "Break"; -"element_aufruf" = "Kald"; -"element_leerElement" = "Tomt element"; - -/* Diagram */ -"diagram_title_label" = "Titel"; -"diagram_title_placeholder" = "Ingen titel"; -"canvas_empty_title" = "Tomt struktogram"; -"canvas_empty_hint" = "Træk et element fra “Elementer” hertil."; - -/* Diagram stubs */ -"canvas_stub_anweisung" = "statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "switch"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Kald()"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Forslag til filnavn"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Med dato"; -"settings_export_filename_title_only" = "Uden dato"; -"settings_export_filename_footer_with_date" = "Med dato: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Uden dato: Filename.visustruct"; - -"close_unsaved_title" = "Ikke-gemte ændringer"; -"close_unsaved_message" = "Dette diagram indeholder ikke-gemte ændringer. Vil du gemme dem før lukning?"; -"close_unsaved_discard" = "Kassér"; -"close_unsaved_cancel" = "Annuller"; - -/* Accessibility — code generator floating panel */ -"a11y_codegen_panel_drag" = "Træk for at flytte kodegeneratorpanelet"; -"about_dateiformat_strk" = "Filer med endelsen .strk kan også åbnes."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simuleringen er færdig."; -"sim_notice_infinite_loop_empty" = "Den uendelige løkke har intet indhold."; -"sim_notice_unsupported_statement_fmt" = "Sætning understøttes ikke: %@"; -"sim_notice_invalid_expression_fmt" = "Ugyldigt udtryk: %@"; -"sim_notice_unknown_variable_fmt" = "Ukendt variabel: %@"; -"sim_notice_division_by_zero" = "Division med nul."; -"sim_notice_whole_number_expected" = "Indtast et heltal."; -"sim_notice_number_expected" = "Indtast et tal."; -"sim_notice_boolean_expected" = "Indtast true eller false."; -"sim_notice_subprogram_not_found_fmt" = "Intet åbent diagram hedder %@. Kaldet blev sprunget over."; -"sim_notice_call_too_deep" = "For mange indlejrede kald. Kalder et underprogram sig selv?"; -"phone_hide_text_editor" = "Skjul"; -"phone_title_done" = "Færdig"; - -"codegen_pseudocode_toggle" = "Pseudokode (ikke kørbar kode)"; -"codegen_pseudocode_note" = "Pseudokode – ikke et kørbart program"; -"codegen_pseudocode_hint" = "Dette diagram er markeret som pseudokode. Koden er kun en skitse og kan ikke køres."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-BJ55WGVD.js b/website/app/assets/Localizable-BJ55WGVD.js deleted file mode 100644 index 2d294726..00000000 --- a/website/app/assets/Localizable-BJ55WGVD.js +++ /dev/null @@ -1,153 +0,0 @@ -const e=`/* NOTE: Initial Dutch localization. */ -"palette_header" = "Elementen"; -"inspector_header" = "Objectinspector"; - -/* Actions */ -"action_share_png" = "Deel afbeelding…"; -"action_share_document" = "Deel bestand…"; -"action_export_pdf" = "PDF-voorbeeld…"; -"action_generate_code" = "Genereer code…"; -"action_simulation" = "Simulatie"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "De simulatiemodus voert het diagram stap voor stap uit, markeert het huidige element en toont variabele waarden en uitvoer."; -"simulation_controls" = "Besturing"; -"simulation_highlight_label" = "Highlight"; -"simulation_highlight_last" = "Last step"; -"simulation_highlight_next" = "Next step"; -"simulation_not_available" = "Play voert de simulatie automatisch uit met een pauze van 0,5 seconde per stap."; -"simulation_play" = "Afspelen"; -"simulation_pause" = "Pauze"; -"simulation_step" = "Stap"; -"simulation_stop" = "Stop"; -"simulation_input" = "Invoer"; -"simulation_input_placeholder" = "Geen invoer vereist."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Voer een geheel getal in."; -"simulation_trace" = "Laatste stap"; -"simulation_trace_placeholder" = "Nog geen stap uitgevoerd."; -"simulation_variables" = "Variabelen"; -"simulation_variables_placeholder" = "Nog geen variabelen."; -"simulation_output" = "Uitvoer"; -"simulation_output_placeholder" = "Nog geen uitvoer."; -"action_delete_selection" = "Selectie verwijderen"; -"ctx_copy" = "Kopiëren"; -"ctx_cut" = "Knippen"; -"ctx_paste" = "Plakken"; -"action_settings" = "Instellingen"; -"action_about" = "Over VisuStruct"; -"recent_files_menu" = "Recente bestanden"; -"recent_files_empty" = "Geen recente bestanden"; -"recent_files_clear" = "Lijst wissen"; -"recent_files_unavailable" = "Bestand is niet meer beschikbaar."; - -/* In-app settings */ -"settings_language_title" = "Taal"; - -/* Toolbar */ -"toolbar_undo" = "Ongedaan maken"; -"toolbar_redo" = "Opnieuw"; -"toolbar_new" = "Nieuw"; -"menu_open" = "Open…"; -"menu_save" = "Bewaar"; -"menu_save_as" = "Bewaar als…"; -"menu_print" = "Print…"; -"menu_file" = "Bestand"; -"menu_new" = "Nieuw"; -"menu_close" = "Sluit"; - -/* Inspector */ -"inspector_no_selection" = "Geen element geselecteerd — tik op een tegel."; -"inspector_apply_text" = "Tekst toepassen"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Soort"; -"inspector_io_mode_statement" = "Opdracht"; -"inspector_io_mode_input" = "Invoer"; -"inspector_io_mode_output" = "Uitvoer"; -"inspector_io_mode_help_java_like" = "Invoer: input: … | Uitvoer: output: …"; -"inspector_add_case" = "Case toevoegen"; -"inspector_remove_case" = "Case verwijderen"; -"inspector_group_element" = "Elementtekst"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Voer condition in…"; -"inspector_placeholder_selector" = "Voer selector in…"; -"inspector_placeholder_call" = "Voer call in…"; -"inspector_placeholder_break" = "Opmerking (optioneel)"; - -/* Element types */ -"element_anweisung" = "Instructie"; -"element_verzweigung" = "Voorwaarde (if/else)"; -"element_fallauswahl" = "Keuze (switch)"; -"element_forSchleife" = "For-lus"; -"element_whileSchleife" = "While-lus"; -"element_doUntilSchleife" = "Do-while-lus"; -"element_endlosschleife" = "Oneindige lus"; -"element_aussprung" = "Break"; -"element_aufruf" = "Aanroep"; -"element_leerElement" = "Leeg element"; - -/* Diagram */ -"diagram_title_label" = "Titel"; -"diagram_title_placeholder" = "Geen titel"; -"canvas_empty_title" = "Leeg structogram"; -"canvas_empty_hint" = "Sleep een element uit “Elementen” hierheen."; - -/* Diagram stubs */ -"canvas_stub_anweisung" = "statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "switch"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Aanroep()"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Bestandsnaamsuggestie"; -"settings_export_filename_picker" = "Indeling"; -"settings_export_filename_date_title" = "Met datum"; -"settings_export_filename_title_only" = "Zonder datum"; -"settings_export_filename_footer_with_date" = "Met datum: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Zonder datum: Filename.visustruct"; - -"close_unsaved_title" = "Niet-opgeslagen wijzigingen"; -"close_unsaved_message" = "Dit diagram bevat niet-opgeslagen wijzigingen. Wil je ze opslaan voordat je sluit?"; -"close_unsaved_discard" = "Verwerpen"; -"close_unsaved_cancel" = "Annuleren"; - -/* Accessibility — code generator floating panel */ -"a11y_codegen_panel_drag" = "Sleep om het codegeneratorpaneel te verplaatsen"; -"about_dateiformat_strk" = "Bestanden met de extensie .strk kunnen ook worden geopend."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulatie voltooid."; -"sim_notice_infinite_loop_empty" = "De oneindige lus heeft geen inhoud."; -"sim_notice_unsupported_statement_fmt" = "Niet-ondersteunde opdracht: %@"; -"sim_notice_invalid_expression_fmt" = "Ongeldige expressie: %@"; -"sim_notice_unknown_variable_fmt" = "Onbekende variabele: %@"; -"sim_notice_division_by_zero" = "Deling door nul."; -"sim_notice_whole_number_expected" = "Voer een geheel getal in."; -"sim_notice_number_expected" = "Voer een getal in."; -"sim_notice_boolean_expected" = "Voer true of false in."; -"sim_notice_subprogram_not_found_fmt" = "Er is geen open diagram met de naam %@. De aanroep is overgeslagen."; -"sim_notice_call_too_deep" = "Te veel geneste aanroepen. Roept een subprogramma zichzelf aan?"; -"phone_hide_text_editor" = "Inklappen"; -"phone_title_done" = "Gereed"; - -"codegen_pseudocode_toggle" = "Pseudocode (geen uitvoerbare code)"; -"codegen_pseudocode_note" = "Pseudocode – geen uitvoerbaar programma"; -"codegen_pseudocode_hint" = "Dit diagram is gemarkeerd als pseudocode. De code is alleen een schets en kan niet worden uitgevoerd."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-BM86Igqt.js b/website/app/assets/Localizable-BM86Igqt.js deleted file mode 100644 index e5a53b6f..00000000 --- a/website/app/assets/Localizable-BM86Igqt.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Öğeler"; -"inspector_header" = "Nesne denetçisi"; - -/* Actions */ -"action_share_png" = "Görseli paylaş…"; -"action_share_document" = "Dosyayı paylaş…"; -"action_export_pdf" = "PDF önizleme…"; -"action_generate_code" = "Kod üret…"; -"action_simulation" = "Simülasyon"; -"action_diagram" = "Diyagram"; -"simulation_placeholder_body" = "Simülasyon modu diyagramı adım adım çalıştırır, geçerli öğeyi vurgular ve değişken değerlerini ve çıktıyı gösterir."; -"simulation_controls" = "Kontroller"; -"simulation_highlight_label" = "Vurgu"; -"simulation_highlight_last" = "Son adım"; -"simulation_highlight_next" = "Sonraki adım"; -"simulation_not_available" = "Play simülasyonu adım başına 0,5 saniye duraklamayla otomatik çalıştırır."; -"simulation_play" = "Oynat"; -"simulation_pause" = "Duraklat"; -"simulation_step" = "Adım"; -"simulation_stop" = "Durdur"; -"simulation_input" = "Girdi"; -"simulation_input_placeholder" = "Girdi gerekli değil."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Lütfen bir tam sayı girin."; -"simulation_trace" = "Son Adım"; -"simulation_trace_placeholder" = "Henüz adım çalıştırılmadı."; -"simulation_variables" = "Değişkenler"; -"simulation_variables_placeholder" = "Henüz değişken yok."; -"simulation_output" = "Çıktı"; -"simulation_output_placeholder" = "Henüz çıktı yok."; -"action_delete_selection" = "Seçimi sil"; -"ctx_copy" = "Kopyala"; -"ctx_cut" = "Kes"; -"ctx_paste" = "Yapıştır"; -"action_settings" = "Ayarlar"; -"action_about" = "VisuStruct Hakkında"; -"recent_files_menu" = "Son Kullanılanlar"; -"recent_files_empty" = "Son kullanılan dosya yok"; -"recent_files_clear" = "Listeyi temizle"; -"recent_files_unavailable" = "Dosya artık kullanılamıyor."; - -/* In-app settings */ -"settings_language_title" = "Dil"; -"settings_language_de" = "Almanca"; -"settings_language_en" = "İngilizce"; -"settings_language_es" = "İspanyolca"; -"settings_language_fr" = "Fransızca"; -"settings_language_it" = "İtalyanca"; -"settings_language_pl" = "Lehçe"; -"settings_language_tr" = "Türkçe"; -"settings_language_ko" = "Korece"; -"settings_language_ja" = "Japonca"; -"settings_language_el" = "Yunanca"; -"settings_language_uk" = "Ukraynaca"; -"settings_language_ar" = "Arapça"; -"settings_language_ru" = "Rusça"; -"settings_language_he" = "İbranice"; -"settings_language_zh_hans" = "Çince (Basitleştirilmiş)"; -"settings_language_zh_hant" = "Çince (Geleneksel)"; -"settings_language_ptpt" = "Portekizce"; -"settings_language_java" = "Java (anahtar kelimeler)"; - - -/* Error overlay */ -"error_title" = "Hata"; -"error_back_editor" = "Düzenleyiciye dön"; - -/* Toolbar */ -"toolbar_undo" = "Geri al"; -"toolbar_redo" = "Yinele"; -"toolbar_new" = "Yeni"; -"menu_open" = "Aç…"; -"menu_save" = "Kaydet"; -"menu_save_as" = "Farklı kaydet…"; -"menu_print" = "Yazdır…"; -"menu_file" = "Dosya"; -"menu_new" = "Yeni"; -"menu_close" = "Kapat"; - -/* Accessibility */ -"a11y_generate_code" = "Yapı şemasından kod üret"; -"a11y_generate_code_locked" = "Kod üret, satın alma sonrası kullanılabilir"; -"a11y_unsaved_changes" = "Kaydedilmemiş değişiklikler"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Kod üret"; -"codegen_gate_body" = "Diyagramınızdan Java, Python ve JavaScript kodu uygulama içi satın alma ile açılacak (yakında)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Kod üretici"; -"codegen_placeholder_body" = "Üretilen kod burada görünecek."; -"codegen_placeholder_compare" = "Sözdizimini karşılaştırmak için Java, Python ve JavaScript arasında geçiş yapın (aşağıda örnek)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (koşul) {\\n ifade();\\n} else {\\n alternatif();\\n}"; -"codegen_preview_python" = "if koşul:\\n ifade()\\nelse:\\n alternatif()"; -"codegen_copy" = "Kopyala"; -"codegen_run_in_browser" = "Tarayıcıda test et"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Yeni diyagram sekmesi"; -"a11y_close_tab" = "Sekmeyi kapat"; - -"close_unsaved_title" = "Kaydedilmemiş değişiklikler"; -"close_unsaved_message" = "Bu diyagram kaydedilmemiş değişiklikler içeriyor. Kapatmadan önce kaydetmek ister misiniz?"; -"close_unsaved_discard" = "Vazgeç"; -"close_unsaved_cancel" = "İptal"; -"a11y_placeholder_drop" = "Yer tutucuyu değiştirmek için buraya bir öğe bırakın. Sürükleyip taşımak için basılı tutun."; -"a11y_node_drop" = "Bir bloğa basılı tutup sürükleyin: önce/sonra eklemek için hedefin üst veya alt kenarına bırakın. Ortaya bırakma yalnızca ø yer tutucuda."; -"a11y_codegen_panel_drag" = "Kod üretici panelini taşımak için sürükleyin"; - -/* Diagram */ -"diagram_title_label" = "Başlık"; -"diagram_title_placeholder" = "Başlıksız"; - -/* Inspector */ -"inspector_no_selection" = "Seçili öğe yok — bir kutucuğa dokunun."; -"inspector_apply_text" = "Metni uygula"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Tür"; -"inspector_io_mode_statement" = "Komut"; -"inspector_io_mode_input" = "Giriş"; -"inspector_io_mode_output" = "Çıkış"; -"inspector_io_mode_help_java_like" = "Giriş: input: … | Çıkış: output: …"; -"inspector_add_case" = "Durum ekle"; -"inspector_remove_case" = "Durumu kaldır"; -"inspector_group_element" = "Öğe metni"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Koşul ekle…"; -"inspector_placeholder_selector" = "Seçici ekle…"; -"inspector_placeholder_call" = "Çağrı ekle…"; -"inspector_placeholder_break" = "Açıklama (isteğe bağlı)"; - -/* Element types */ -"element_anweisung" = "İfade"; -"element_verzweigung" = "Dal"; -"element_fallauswahl" = "Seçim"; -"element_forSchleife" = "for döngüsü"; -"element_whileSchleife" = "while döngüsü"; -"element_doUntilSchleife" = "do-while döngüsü"; -"element_endlosschleife" = "Sonsuz döngü"; -"element_aussprung" = "Çık"; -"element_aufruf" = "Çağrı"; -"element_leerElement" = "Boş öğe"; - -/* Tabs */ -"tab_title_format" = "Diyagram %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Boş yapı şeması"; -"canvas_empty_hint" = "“Öğeler”den bir öğeyi buraya sürükleyin."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Seçim"; -"canvas_stub_anweisung" = "İfade"; -"canvas_stub_condition" = "Koşul"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "sonsuz"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Çağrı()"; - -/* About */ -"about_version_fmt" = "Sürüm %@ (%@)"; -"about_nav_title" = "VisuStruct Hakkında"; -"about_features_heading" = "Özellikler"; -"about_bullet_palette" = "Paletten blok ekleyin veya diyagrama sürükleyin"; -"about_bullet_edit" = "Şemayı düzenleyin: metni değiştirin, öğeleri seçin ve gerekirse silin"; -"about_bullet_tabs" = "Sekmelerde birden fazla diyagram — her birinin kendi başlığı var"; -"about_bullet_file" = "Diyagramları .visustruct dosyaları olarak kaydedin ve yeniden açın"; -"about_bullet_share" = "Yapı şemasını görsel olarak paylaşın (PNG)"; -"about_dateiformat_heading" = "Dosya biçimi"; -"about_dateiformat_part1" = "VisuStruct’ün yerel biçimi şu uzantıyı kullanır: "; -"about_dateiformat_part2" = ". Diyagramları saklayabilir ve aynı biçimi destekleyen diğer uygulamalarla paylaşabilirsiniz."; -"about_dateiformat_strk" = ".strk uzantılı dosyalar da açılabilir."; -"about_project_github_label" = "Java uygulaması (kaynak ve sürümler):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Geliştirici:"; -"about_lineage_heading" = "Köken"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Bitti"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Ekleme iptal"; -"phone_show_text_editor" = "Metni düzenle"; -"phone_hide_text_editor" = "Daralt"; -"phone_title_done" = "Bitti"; - -/* Alerts */ -"err_save_failed_fmt" = "Kaydedilemedi: %@"; -"err_share_prepare_failed_fmt" = "Paylaşım hazırlanamadı: %@"; -"err_diagram_too_small_export" = "Diyagram çok küçük — dışa aktarılacak bir şey yok."; -"err_image_render_failed" = "Görsel oluşturulamadı."; -"err_diagram_too_small_print" = "Diyagram çok küçük — yazdırılacak bir şey yok."; -"err_print_unavailable" = "Bu aygıtta yazdırma kullanılamıyor."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Dosya adı önerisi"; -"settings_export_filename_picker" = "Biçim"; -"settings_export_filename_date_title" = "Tarihli"; -"settings_export_filename_title_only" = "Tarihsiz"; -"settings_export_filename_footer_with_date" = "Tarihli: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Tarihsiz: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simülasyon tamamlandı."; -"sim_notice_infinite_loop_empty" = "Sonsuz döngünün gövdesi yok."; -"sim_notice_unsupported_statement_fmt" = "Desteklenmeyen komut: %@"; -"sim_notice_invalid_expression_fmt" = "Geçersiz ifade: %@"; -"sim_notice_unknown_variable_fmt" = "Bilinmeyen değişken: %@"; -"sim_notice_division_by_zero" = "Sıfıra bölme."; -"sim_notice_whole_number_expected" = "Lütfen bir tam sayı girin."; -"sim_notice_number_expected" = "Lütfen bir sayı girin."; -"sim_notice_boolean_expected" = "true veya false girin."; -"sim_notice_subprogram_not_found_fmt" = "%@ adında açık bir diyagram yok. Çağrı atlandı."; -"sim_notice_call_too_deep" = "Çok fazla iç içe çağrı. Bir alt program kendini mi çağırıyor?"; - -"codegen_pseudocode_toggle" = "Sözde kod (çalıştırılamaz kod)"; -"codegen_pseudocode_note" = "Sözde kod – çalıştırılabilir bir program değil"; -"codegen_pseudocode_hint" = "Bu diyagram sözde kod olarak işaretlendi. Kod yalnızca bir taslaktır ve çalıştırılamaz."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-BYJK0mdR.js b/website/app/assets/Localizable-BYJK0mdR.js deleted file mode 100644 index b6e04f44..00000000 --- a/website/app/assets/Localizable-BYJK0mdR.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Elementy"; -"inspector_header" = "Inspektor obiektów"; - -/* Actions */ -"action_share_png" = "Udostępnij obraz…"; -"action_share_document" = "Udostępnij plik…"; -"action_export_pdf" = "Podgląd PDF…"; -"action_generate_code" = "Generuj kod…"; -"action_simulation" = "Symulacja"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "Tryb symulacji uruchamia diagram krok po kroku, podświetla bieżący element oraz pokazuje wartości zmiennych i wyjście."; -"simulation_controls" = "Sterowanie"; -"simulation_highlight_label" = "Podświetlenie"; -"simulation_highlight_last" = "Ostatni krok"; -"simulation_highlight_next" = "Następny krok"; -"simulation_not_available" = "Play uruchamia symulację automatycznie z pauzą 0,5 sekundy na krok."; -"simulation_play" = "Odtwórz"; -"simulation_pause" = "Pauza"; -"simulation_step" = "Krok"; -"simulation_stop" = "Stop"; -"simulation_input" = "Wejście"; -"simulation_input_placeholder" = "Brak wymaganych danych wejściowych."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Wpisz liczbę całkowitą."; -"simulation_trace" = "Ostatni krok"; -"simulation_trace_placeholder" = "Nie wykonano jeszcze żadnego kroku."; -"simulation_variables" = "Zmienne"; -"simulation_variables_placeholder" = "Brak zmiennych."; -"simulation_output" = "Wyjście"; -"simulation_output_placeholder" = "Brak wyjścia."; -"action_delete_selection" = "Usuń zaznaczenie"; -"ctx_copy" = "Kopiuj"; -"ctx_cut" = "Wytnij"; -"ctx_paste" = "Wklej"; -"action_settings" = "Ustawienia"; -"action_about" = "O VisuStruct"; -"recent_files_menu" = "Ostatnie pliki"; -"recent_files_empty" = "Brak ostatnich plików"; -"recent_files_clear" = "Wyczyść listę"; -"recent_files_unavailable" = "Plik nie jest już dostępny."; - -/* In-app settings */ -"settings_language_title" = "Język"; -"settings_language_de" = "Niemiecki"; -"settings_language_en" = "Angielski"; -"settings_language_es" = "Hiszpański"; -"settings_language_fr" = "Francuski"; -"settings_language_it" = "Włoski"; -"settings_language_pl" = "Polski"; -"settings_language_tr" = "Turecki"; -"settings_language_ko" = "Koreański"; -"settings_language_ja" = "Japoński"; -"settings_language_el" = "Grecki"; -"settings_language_uk" = "Ukraiński"; -"settings_language_ar" = "Arabski"; -"settings_language_ru" = "Rosyjski"; -"settings_language_he" = "Hebrajski"; -"settings_language_zh_hans" = "Chiński (uproszczony)"; -"settings_language_zh_hant" = "Chiński (tradycyjny)"; -"settings_language_ptpt" = "Portugalski"; -"settings_language_java" = "Java (słowa kluczowe)"; - - -/* Error overlay */ -"error_title" = "Błąd"; -"error_back_editor" = "Powrót do edytora"; - -/* Toolbar */ -"toolbar_undo" = "Cofnij"; -"toolbar_redo" = "Ponów"; -"toolbar_new" = "Nowe"; -"menu_open" = "Otwórz…"; -"menu_save" = "Zapisz"; -"menu_save_as" = "Zapisz jako…"; -"menu_print" = "Drukuj…"; -"menu_file" = "Plik"; -"menu_new" = "Nowy"; -"menu_close" = "Zamknij"; - -/* Accessibility */ -"a11y_generate_code" = "Generuj kod z diagramu"; -"a11y_generate_code_locked" = "Generuj kod, dostępne po zakupie"; -"a11y_unsaved_changes" = "Niezapisane zmiany"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Generuj kod"; -"codegen_gate_body" = "Kod Java, Python i JavaScript z Twojego diagramu odblokuje zakup w aplikacji (wkrótce)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Generator kodu"; -"codegen_placeholder_body" = "Tutaj pojawi się wygenerowany kod."; -"codegen_placeholder_compare" = "Przełączaj między Java, Python i JavaScript, aby porównać składnię (przykład poniżej)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (warunek) {\\n instrukcja();\\n} else {\\n alternatywa();\\n}"; -"codegen_preview_python" = "if warunek:\\n instrukcja()\\nelse:\\n alternatywa()"; -"codegen_copy" = "Kopiuj"; -"codegen_run_in_browser" = "Testuj w przeglądarce"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Nowa karta diagramu"; -"a11y_close_tab" = "Zamknij kartę"; - -"close_unsaved_title" = "Niezapisane zmiany"; -"close_unsaved_message" = "Ten diagram zawiera niezapisane zmiany. Czy chcesz je zapisać przed zamknięciem?"; -"close_unsaved_discard" = "Odrzuć"; -"close_unsaved_cancel" = "Anuluj"; -"a11y_placeholder_drop" = "Upuść element tutaj, aby zastąpić symbol zastępczy. Naciśnij i przytrzymaj, aby przeciągnąć i przenieść."; -"a11y_node_drop" = "Naciśnij i przytrzymaj blok, a następnie przeciągnij: upuść na górnej lub dolnej krawędzi celu, aby wstawić przed lub po. Upuszczenie na środku tylko na symbolu ø."; -"a11y_codegen_panel_drag" = "Przeciągnij, aby przesunąć panel generatora kodu"; - -/* Diagram */ -"diagram_title_label" = "Tytuł"; -"diagram_title_placeholder" = "Bez tytułu"; - -/* Inspector */ -"inspector_no_selection" = "Nie wybrano elementu — stuknij kafelek."; -"inspector_apply_text" = "Zastosuj tekst"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Rodzaj"; -"inspector_io_mode_statement" = "Instrukcja"; -"inspector_io_mode_input" = "Wejście"; -"inspector_io_mode_output" = "Wyjście"; -"inspector_io_mode_help_java_like" = "Wejście: input: … | Wyjście: output: …"; -"inspector_add_case" = "Dodaj przypadek"; -"inspector_remove_case" = "Usuń przypadek"; -"inspector_group_element" = "Tekst elementu"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Wstaw warunek…"; -"inspector_placeholder_selector" = "Wstaw selektor…"; -"inspector_placeholder_call" = "Wstaw wywołanie…"; -"inspector_placeholder_break" = "Komentarz (opcjonalnie)"; - -/* Element types */ -"element_anweisung" = "Instrukcja"; -"element_verzweigung" = "Rozgałęzienie"; -"element_fallauswahl" = "Wybór"; -"element_forSchleife" = "Pętla for"; -"element_whileSchleife" = "Pętla while"; -"element_doUntilSchleife" = "Pętla do-while"; -"element_endlosschleife" = "Pętla nieskończona"; -"element_aussprung" = "Wyjście"; -"element_aufruf" = "Wywołanie"; -"element_leerElement" = "Pusty element"; - -/* Tabs */ -"tab_title_format" = "Diagram %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Pusty diagram"; -"canvas_empty_hint" = "Przeciągnij element z „Elementy” tutaj."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Wybór"; -"canvas_stub_anweisung" = "Instrukcja"; -"canvas_stub_condition" = "Warunek"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "nieskończona"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Wywołanie()"; - -/* About */ -"about_version_fmt" = "Wersja %@ (%@)"; -"about_nav_title" = "O VisuStruct"; -"about_features_heading" = "Funkcje"; -"about_bullet_palette" = "Wstawiaj bloki z palety lub przeciągaj je do diagramu"; -"about_bullet_edit" = "Edytuj diagram: zmieniaj tekst, wybieraj elementy i usuwaj je w razie potrzeby"; -"about_bullet_tabs" = "Kilka diagramów w kartach — każdy z własnym tytułem"; -"about_bullet_file" = "Zapisuj i otwieraj diagramy jako pliki .visustruct"; -"about_bullet_share" = "Udostępniaj diagram jako obraz (PNG)"; -"about_dateiformat_heading" = "Format pliku"; -"about_dateiformat_part1" = "Natywny format VisuStruct używa rozszerzenia "; -"about_dateiformat_part2" = ". Możesz zachować diagramy i wymieniać je z innymi aplikacjami obsługującymi ten sam format."; -"about_dateiformat_strk" = "Można także otwierać pliki z rozszerzeniem .strk."; -"about_project_github_label" = "Aplikacja Java (źródła i wydania):"; -"about_web_label" = "WWW:"; -"about_developer_label" = "Programista:"; -"about_lineage_heading" = "Pochodzenie"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Gotowe"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Anuluj wstawianie"; -"phone_show_text_editor" = "Edytuj tekst"; -"phone_hide_text_editor" = "Zwiń"; -"phone_title_done" = "Gotowe"; - -/* Alerts */ -"err_save_failed_fmt" = "Nie można zapisać: %@"; -"err_share_prepare_failed_fmt" = "Nie można przygotować udostępniania: %@"; -"err_diagram_too_small_export" = "Diagram jest zbyt mały — nic do eksportu."; -"err_image_render_failed" = "Nie można utworzyć obrazu."; -"err_diagram_too_small_print" = "Diagram jest zbyt mały — nic do drukowania."; -"err_print_unavailable" = "Drukowanie nie jest dostępne na tym urządzeniu."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Propozycja nazwy pliku"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Z datą"; -"settings_export_filename_title_only" = "Bez daty"; -"settings_export_filename_footer_with_date" = "Z datą: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Bez daty: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Symulacja zakończona."; -"sim_notice_infinite_loop_empty" = "Pętla nieskończona nie ma treści."; -"sim_notice_unsupported_statement_fmt" = "Nieobsługiwana instrukcja: %@"; -"sim_notice_invalid_expression_fmt" = "Nieprawidłowe wyrażenie: %@"; -"sim_notice_unknown_variable_fmt" = "Nieznana zmienna: %@"; -"sim_notice_division_by_zero" = "Dzielenie przez zero."; -"sim_notice_whole_number_expected" = "Wprowadź liczbę całkowitą."; -"sim_notice_number_expected" = "Wprowadź liczbę."; -"sim_notice_boolean_expected" = "Wpisz true lub false."; -"sim_notice_subprogram_not_found_fmt" = "Żaden otwarty diagram nie nazywa się %@. Wywołanie pominięto."; -"sim_notice_call_too_deep" = "Zbyt wiele zagnieżdżonych wywołań. Czy podprogram wywołuje sam siebie?"; - -"codegen_pseudocode_toggle" = "Pseudokod (kod niewykonywalny)"; -"codegen_pseudocode_note" = "Pseudokod – to nie jest wykonywalny program"; -"codegen_pseudocode_hint" = "Ten diagram jest oznaczony jako pseudokod. Kod jest tylko szkicem i nie można go uruchomić."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-BudCXCER.js b/website/app/assets/Localizable-BudCXCER.js deleted file mode 100644 index 5dd80e26..00000000 --- a/website/app/assets/Localizable-BudCXCER.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "요소"; -"inspector_header" = "개체 검사기"; - -/* Actions */ -"action_share_png" = "이미지 공유…"; -"action_share_document" = "파일 공유…"; -"action_export_pdf" = "PDF 미리보기…"; -"action_generate_code" = "코드 생성…"; -"action_simulation" = "시뮬레이션"; -"action_diagram" = "다이어그램"; -"simulation_placeholder_body" = "시뮬레이션 모드는 다이어그램을 단계별로 실행하고 현재 요소를 강조 표시하며 변수 값과 출력을 보여줍니다."; -"simulation_controls" = "제어"; -"simulation_highlight_label" = "강조 표시"; -"simulation_highlight_last" = "마지막 단계"; -"simulation_highlight_next" = "다음 단계"; -"simulation_not_available" = "Play는 각 단계마다 0.5초씩 쉬면서 시뮬레이션을 자동으로 실행합니다."; -"simulation_play" = "재생"; -"simulation_pause" = "일시정지"; -"simulation_step" = "단계"; -"simulation_stop" = "정지"; -"simulation_input" = "입력"; -"simulation_input_placeholder" = "입력이 필요하지 않습니다."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "정수를 입력하세요."; -"simulation_trace" = "마지막 단계"; -"simulation_trace_placeholder" = "아직 실행된 단계가 없습니다."; -"simulation_variables" = "변수"; -"simulation_variables_placeholder" = "아직 변수가 없습니다."; -"simulation_output" = "출력"; -"simulation_output_placeholder" = "아직 출력이 없습니다."; -"action_delete_selection" = "선택 삭제"; -"ctx_copy" = "복사"; -"ctx_cut" = "잘라내기"; -"ctx_paste" = "붙여넣기"; -"action_settings" = "설정"; -"action_about" = "VisuStruct 정보"; -"recent_files_menu" = "최근 파일"; -"recent_files_empty" = "최근 파일 없음"; -"recent_files_clear" = "목록 지우기"; -"recent_files_unavailable" = "파일을 더 이상 사용할 수 없습니다."; - -/* In-app settings */ -"settings_language_title" = "언어"; -"settings_language_de" = "독일어"; -"settings_language_en" = "영어"; -"settings_language_es" = "스페인어"; -"settings_language_fr" = "프랑스어"; -"settings_language_it" = "이탈리아어"; -"settings_language_pl" = "폴란드어"; -"settings_language_tr" = "터키어"; -"settings_language_ko" = "한국어"; -"settings_language_ja" = "일본어"; -"settings_language_el" = "그리스어"; -"settings_language_uk" = "우크라이나어"; -"settings_language_ar" = "아랍어"; -"settings_language_ru" = "러시아어"; -"settings_language_he" = "히브리어"; -"settings_language_zh_hans" = "중국어(간체)"; -"settings_language_zh_hant" = "중국어(번체)"; -"settings_language_ptpt" = "포르투갈어"; -"settings_language_java" = "Java(키워드)"; - - -/* Error overlay */ -"error_title" = "오류"; -"error_back_editor" = "편집기로 돌아가기"; - -/* Toolbar */ -"toolbar_undo" = "실행 취소"; -"toolbar_redo" = "다시 실행"; -"toolbar_new" = "새로 만들기"; -"menu_open" = "열기…"; -"menu_save" = "저장"; -"menu_save_as" = "다른 이름으로 저장…"; -"menu_print" = "인쇄…"; -"menu_file" = "파일"; -"menu_new" = "새로 만들기"; -"menu_close" = "닫기"; - -/* Accessibility */ -"a11y_generate_code" = "구조도에서 코드 생성"; -"a11y_generate_code_locked" = "코드 생성(구매 후 사용 가능)"; -"a11y_unsaved_changes" = "저장되지 않은 변경 사항"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "코드 생성"; -"codegen_gate_body" = "다이어그램에서 생성된 Java, Python, JavaScript 코드는 앱 내 구매로 잠금 해제됩니다(곧 제공)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "코드 생성기"; -"codegen_placeholder_body" = "생성된 코드는 여기에 표시됩니다."; -"codegen_placeholder_compare" = "Java, Python, JavaScript 간에 전환하여 문법을 비교하세요(아래 예시)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (조건) {\\n 문장();\\n} else {\\n 대안();\\n}"; -"codegen_preview_python" = "if 조건:\\n 문장()\\nelse:\\n 대안()"; -"codegen_copy" = "복사"; -"codegen_run_in_browser" = "브라우저에서 테스트"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "새 다이어그램 탭"; -"a11y_close_tab" = "탭 닫기"; - -"close_unsaved_title" = "저장되지 않은 변경사항"; -"close_unsaved_message" = "이 다이어그램에 저장되지 않은 변경사항이 있습니다. 닫기 전에 저장하시겠습니까?"; -"close_unsaved_discard" = "버리기"; -"close_unsaved_cancel" = "취소"; -"a11y_placeholder_drop" = "자리 표시자를 바꾸려면 여기에 요소를 놓으세요. 길게 눌러 드래그하여 이동할 수 있습니다."; -"a11y_node_drop" = "블록을 길게 누른 뒤 드래그하세요: 대상의 위/아래 가장자리에 놓으면 앞/뒤에 삽입됩니다. 가운데 놓기는 ø 자리 표시자에서만 가능합니다."; -"a11y_codegen_panel_drag" = "코드 생성기 패널을 움직이려면 드래그하세요"; - -/* Diagram */ -"diagram_title_label" = "제목"; -"diagram_title_placeholder" = "제목 없음"; - -/* Inspector */ -"inspector_no_selection" = "선택된 요소가 없습니다 — 타일을 탭하세요."; -"inspector_apply_text" = "텍스트 적용"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "종류"; -"inspector_io_mode_statement" = "명령문"; -"inspector_io_mode_input" = "입력"; -"inspector_io_mode_output" = "출력"; -"inspector_io_mode_help_java_like" = "입력: input: … | 출력: output: …"; -"inspector_add_case" = "케이스 추가"; -"inspector_remove_case" = "케이스 제거"; -"inspector_group_element" = "요소 텍스트"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "조건 입력…"; -"inspector_placeholder_selector" = "선택식 입력…"; -"inspector_placeholder_call" = "호출 입력…"; -"inspector_placeholder_break" = "주석(선택)"; - -/* Element types (Korean + keyword hint) */ -"element_anweisung" = "문장 (statement)"; -"element_verzweigung" = "조건문 (if/else)"; -"element_fallauswahl" = "선택문 (switch)"; -"element_forSchleife" = "반복문 (for)"; -"element_whileSchleife" = "반복문 (while)"; -"element_doUntilSchleife" = "반복문 (do-while)"; -"element_endlosschleife" = "무한 반복 (while true)"; -"element_aussprung" = "탈출 (break)"; -"element_aufruf" = "호출 (call)"; -"element_leerElement" = "빈 요소"; - -/* Tabs */ -"tab_title_format" = "다이어그램 %d"; - -/* Empty canvas */ -"canvas_empty_title" = "빈 구조도"; -"canvas_empty_hint" = "“요소”에서 블록을 여기로 드래그하세요."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "선택문 (switch)"; -"canvas_stub_anweisung" = "문장 (statement)"; -"canvas_stub_condition" = "조건"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "무한"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "호출()"; - -/* About */ -"about_version_fmt" = "버전 %@ (%@)"; -"about_nav_title" = "VisuStruct 정보"; -"about_features_heading" = "기능"; -"about_bullet_palette" = "팔레트에서 블록을 추가하거나 다이어그램으로 드래그하세요"; -"about_bullet_edit" = "다이어그램 편집: 텍스트 변경, 요소 선택, 필요 시 삭제"; -"about_bullet_tabs" = "탭에서 여러 다이어그램 — 각각 제목 포함"; -"about_bullet_file" = ".visustruct 파일로 저장하고 다시 열기"; -"about_bullet_share" = "구조도를 이미지(PNG)로 공유"; -"about_dateiformat_heading" = "파일 형식"; -"about_dateiformat_part1" = "VisuStruct의 기본 형식은 확장자 "; -"about_dateiformat_part2" = "를 사용합니다. 같은 형식을 지원하는 다른 앱과 교환할 수 있습니다."; -"about_dateiformat_strk" = ".strk 확장자 파일도 열 수 있습니다."; -"about_project_github_label" = "Java 앱(소스 및 릴리스):"; -"about_web_label" = "웹:"; -"about_developer_label" = "개발자:"; -"about_lineage_heading" = "계보"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java 업스트림:"; -"about_done" = "완료"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "삽입 취소"; -"phone_show_text_editor" = "텍스트 편집"; -"phone_hide_text_editor" = "접기"; -"phone_title_done" = "완료"; - -/* Alerts */ -"err_save_failed_fmt" = "저장할 수 없음: %@"; -"err_share_prepare_failed_fmt" = "공유를 준비할 수 없음: %@"; -"err_diagram_too_small_export" = "다이어그램이 너무 작아 내보낼 수 없습니다."; -"err_image_render_failed" = "이미지를 만들 수 없습니다."; -"err_diagram_too_small_print" = "다이어그램이 너무 작아 인쇄할 수 없습니다."; -"err_print_unavailable" = "이 기기에서는 인쇄를 사용할 수 없습니다."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "파일 이름 제안"; -"settings_export_filename_picker" = "형식"; -"settings_export_filename_date_title" = "날짜 포함"; -"settings_export_filename_title_only" = "날짜 없음"; -"settings_export_filename_footer_with_date" = "날짜 포함: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "날짜 없음: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "시뮬레이션이 끝났습니다."; -"sim_notice_infinite_loop_empty" = "무한 루프에 본문이 없습니다."; -"sim_notice_unsupported_statement_fmt" = "지원되지 않는 명령문: %@"; -"sim_notice_invalid_expression_fmt" = "잘못된 식: %@"; -"sim_notice_unknown_variable_fmt" = "알 수 없는 변수: %@"; -"sim_notice_division_by_zero" = "0으로 나눌 수 없습니다."; -"sim_notice_whole_number_expected" = "정수를 입력하세요."; -"sim_notice_number_expected" = "숫자를 입력하세요."; -"sim_notice_boolean_expected" = "true 또는 false를 입력하세요."; -"sim_notice_subprogram_not_found_fmt" = "%@(이)라는 열린 다이어그램이 없습니다. 호출을 건너뛰었습니다."; -"sim_notice_call_too_deep" = "중첩 호출이 너무 많습니다. 서브프로그램이 자기 자신을 호출하나요?"; - -"codegen_pseudocode_toggle" = "의사 코드(실행할 수 없는 코드)"; -"codegen_pseudocode_note" = "의사 코드 – 실행 가능한 프로그램이 아닙니다"; -"codegen_pseudocode_hint" = "이 다이어그램은 의사 코드로 표시되어 있습니다. 코드는 초안일 뿐이며 실행할 수 없습니다."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-ByPZS4RL.js b/website/app/assets/Localizable-ByPZS4RL.js deleted file mode 100644 index e17162dd..00000000 --- a/website/app/assets/Localizable-ByPZS4RL.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Éléments"; -"inspector_header" = "Inspecteur d’objets"; - -/* Actions */ -"action_share_png" = "Partager l’image…"; -"action_share_document" = "Partager le fichier…"; -"action_export_pdf" = "Aperçu PDF…"; -"action_generate_code" = "Générer le code…"; -"action_simulation" = "Simulation"; -"action_diagram" = "Diagramme"; -"simulation_placeholder_body" = "Le mode simulation exécute le diagramme étape par étape, met en évidence l’élément actuel et affiche les valeurs des variables et les sorties."; -"simulation_controls" = "Commandes"; -"simulation_highlight_label" = "Surlignage"; -"simulation_highlight_last" = "Dernière étape"; -"simulation_highlight_next" = "Étape suivante"; -"simulation_not_available" = "Play exécute automatiquement la simulation avec une pause de 0,5 seconde par étape."; -"simulation_play" = "Lecture"; -"simulation_pause" = "Pause"; -"simulation_step" = "Pas"; -"simulation_stop" = "Arrêt"; -"simulation_input" = "Entrée"; -"simulation_input_placeholder" = "Aucune entrée requise."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Veuillez saisir un nombre entier."; -"simulation_trace" = "Dernière étape"; -"simulation_trace_placeholder" = "Aucune étape exécutée pour l’instant."; -"simulation_variables" = "Variables"; -"simulation_variables_placeholder" = "Aucune variable pour l’instant."; -"simulation_output" = "Sortie"; -"simulation_output_placeholder" = "Aucune sortie pour l’instant."; -"action_delete_selection" = "Supprimer la sélection"; -"ctx_copy" = "Copier"; -"ctx_cut" = "Couper"; -"ctx_paste" = "Coller"; -"action_settings" = "Réglages"; -"action_about" = "À propos de VisuStruct"; -"recent_files_menu" = "Fichiers récents"; -"recent_files_empty" = "Aucun fichier récent"; -"recent_files_clear" = "Effacer la liste"; -"recent_files_unavailable" = "Le fichier n’est plus disponible."; - -/* In-app settings */ -"settings_language_title" = "Langue"; -"settings_language_de" = "Allemand"; -"settings_language_en" = "Anglais"; -"settings_language_es" = "Espagnol"; -"settings_language_fr" = "Français"; -"settings_language_it" = "Italien"; -"settings_language_pl" = "Polonais"; -"settings_language_tr" = "Turc"; -"settings_language_ko" = "Coréen"; -"settings_language_ja" = "Japonais"; -"settings_language_el" = "Grec"; -"settings_language_uk" = "Ukrainien"; -"settings_language_ar" = "Arabe"; -"settings_language_ru" = "Russe"; -"settings_language_he" = "Hébreu"; -"settings_language_zh_hans" = "Chinois (simplifié)"; -"settings_language_zh_hant" = "Chinois (traditionnel)"; -"settings_language_ptpt" = "Portugais"; -"settings_language_java" = "Java (mots‑clés)"; - - -/* Error overlay */ -"error_title" = "Erreur"; -"error_back_editor" = "Retour à l’éditeur"; - -/* Toolbar */ -"toolbar_undo" = "Annuler"; -"toolbar_redo" = "Rétablir"; -"toolbar_new" = "Nouveau"; -"menu_open" = "Ouvrir…"; -"menu_save" = "Enregistrer"; -"menu_save_as" = "Enregistrer sous…"; -"menu_print" = "Imprimer…"; -"menu_file" = "Fichier"; -"menu_new" = "Nouveau"; -"menu_close" = "Fermer"; - -/* Accessibility */ -"a11y_generate_code" = "Générer du code à partir de l’organigramme"; -"a11y_generate_code_locked" = "Générer du code, disponible après achat"; -"a11y_unsaved_changes" = "Modifications non enregistrées"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Générer le code"; -"codegen_gate_body" = "Le code Java, Python et JavaScript à partir de votre diagramme sera déverrouillé via un achat intégré (bientôt)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Générateur de code"; -"codegen_placeholder_body" = "Le code généré apparaîtra ici."; -"codegen_placeholder_compare" = "Passez entre Java, Python et JavaScript pour comparer la syntaxe (exemple ci‑dessous)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condition) {\\n instruction();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if condition:\\n instruction()\\nelse:\\n alternative()"; -"codegen_copy" = "Copier"; -"codegen_run_in_browser" = "Tester dans le navigateur"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Nouvel onglet de diagramme"; -"a11y_close_tab" = "Fermer l’onglet"; - -"close_unsaved_title" = "Modifications non enregistrées"; -"close_unsaved_message" = "Ce diagramme contient des modifications non enregistrées. Voulez-vous les enregistrer avant de fermer ?"; -"close_unsaved_discard" = "Ignorer"; -"close_unsaved_cancel" = "Annuler"; -"a11y_placeholder_drop" = "Déposez un élément ici pour remplacer l’espace réservé. Appuyez longuement pour glisser et déplacer."; -"a11y_node_drop" = "Appuyez longuement sur un bloc, puis faites glisser : déposez sur le bord haut ou bas pour insérer avant ou après. Dépôt au centre uniquement sur un espace réservé ø."; -"a11y_codegen_panel_drag" = "Glisser pour déplacer le panneau du générateur de code"; - -/* Diagram */ -"diagram_title_label" = "Titre"; -"diagram_title_placeholder" = "Sans titre"; - -/* Inspector */ -"inspector_no_selection" = "Aucun élément sélectionné — touchez une tuile."; -"inspector_apply_text" = "Appliquer le texte"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Type"; -"inspector_io_mode_statement" = "Instruction"; -"inspector_io_mode_input" = "Entrée"; -"inspector_io_mode_output" = "Sortie"; -"inspector_io_mode_help_java_like" = "Entrée: input: … | Sortie: output: …"; -"inspector_add_case" = "Ajouter un cas"; -"inspector_remove_case" = "Supprimer le cas"; -"inspector_group_element" = "Texte de l’élément"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Insérer une condition…"; -"inspector_placeholder_selector" = "Insérer un sélecteur…"; -"inspector_placeholder_call" = "Insérer un appel…"; -"inspector_placeholder_break" = "Commentaire (facultatif)"; - -/* Element types */ -"element_anweisung" = "Instruction"; -"element_verzweigung" = "Branche"; -"element_fallauswahl" = "Sélection"; -"element_forSchleife" = "Boucle for"; -"element_whileSchleife" = "Boucle while"; -"element_doUntilSchleife" = "Boucle do‑while"; -"element_endlosschleife" = "Boucle infinie"; -"element_aussprung" = "Sortie"; -"element_aufruf" = "Appel"; -"element_leerElement" = "Élément vide"; - -/* Tabs */ -"tab_title_format" = "Diagramme %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Organigramme vide"; -"canvas_empty_hint" = "Glissez un élément depuis « Éléments » ici."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Sélection"; -"canvas_stub_anweisung" = "Instruction"; -"canvas_stub_condition" = "Condition"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "infini"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Appel()"; - -/* About */ -"about_version_fmt" = "Version %@ (%@)"; -"about_nav_title" = "À propos de VisuStruct"; -"about_features_heading" = "Fonctionnalités"; -"about_bullet_palette" = "Insérer des blocs depuis la palette ou les glisser dans le diagramme"; -"about_bullet_edit" = "Modifier le diagramme : changer le texte, sélectionner des éléments et les supprimer si nécessaire"; -"about_bullet_tabs" = "Plusieurs diagrammes en onglets — chacun avec son titre"; -"about_bullet_file" = "Enregistrer et rouvrir des diagrammes en fichiers .visustruct"; -"about_bullet_share" = "Partager le diagramme comme image (PNG)"; -"about_dateiformat_heading" = "Format de fichier"; -"about_dateiformat_part1" = "Le format natif de VisuStruct utilise l’extension "; -"about_dateiformat_part2" = ". Vous pouvez conserver vos diagrammes et les échanger avec d’autres apps compatibles."; -"about_dateiformat_strk" = "Les fichiers avec l’extension .strk peuvent également être ouverts."; -"about_project_github_label" = "Application Java (code & versions) :"; -"about_web_label" = "Web :"; -"about_developer_label" = "Développeur :"; -"about_lineage_heading" = "Origine"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Upstream Java :"; -"about_done" = "Terminé"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Annuler l’insertion"; -"phone_show_text_editor" = "Modifier le texte"; -"phone_hide_text_editor" = "Réduire"; -"phone_title_done" = "OK"; - -/* Alerts */ -"err_save_failed_fmt" = "Impossible d’enregistrer : %@"; -"err_share_prepare_failed_fmt" = "Impossible de préparer le partage : %@"; -"err_diagram_too_small_export" = "Le diagramme est trop petit — rien à exporter."; -"err_image_render_failed" = "Impossible de créer l’image."; -"err_diagram_too_small_print" = "Le diagramme est trop petit — rien à imprimer."; -"err_print_unavailable" = "L’impression n’est pas disponible sur cet appareil."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Suggestion de nom de fichier"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Avec date"; -"settings_export_filename_title_only" = "Sans date"; -"settings_export_filename_footer_with_date" = "Avec date: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Sans date: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulation terminée."; -"sim_notice_infinite_loop_empty" = "La boucle infinie n’a pas de corps."; -"sim_notice_unsupported_statement_fmt" = "Instruction non prise en charge : %@"; -"sim_notice_invalid_expression_fmt" = "Expression non valide : %@"; -"sim_notice_unknown_variable_fmt" = "Variable inconnue : %@"; -"sim_notice_division_by_zero" = "Division par zéro."; -"sim_notice_whole_number_expected" = "Saisissez un nombre entier."; -"sim_notice_number_expected" = "Saisissez un nombre."; -"sim_notice_boolean_expected" = "Saisissez true ou false."; -"sim_notice_subprogram_not_found_fmt" = "Aucun diagramme ouvert ne s’appelle %@. L’appel a été ignoré."; -"sim_notice_call_too_deep" = "Trop d’appels imbriqués. Un sous-programme s’appelle-t-il lui-même ?"; - -"codegen_pseudocode_toggle" = "Pseudo-code (code non exécutable)"; -"codegen_pseudocode_note" = "Pseudo-code – pas un programme exécutable"; -"codegen_pseudocode_hint" = "Ce diagramme est marqué comme pseudo-code. Le code n’est qu’une ébauche et ne peut pas être exécuté."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-CHv0OL9S.js b/website/app/assets/Localizable-CHv0OL9S.js deleted file mode 100644 index e34630d6..00000000 --- a/website/app/assets/Localizable-CHv0OL9S.js +++ /dev/null @@ -1,256 +0,0 @@ -const e=`/* Paleta */ -"palette_header" = "Elementos"; -"inspector_header" = "Inspetor de objetos"; - -/* Ações */ -"action_share_png" = "Partilhar gráfico…"; -"action_share_document" = "Partilhar ficheiro…"; -"action_export_pdf" = "Pré-visualizar PDF…"; -"action_generate_code" = "Gerar código…"; -"action_simulation" = "Simulação"; -"action_diagram" = "Diagrama"; -"simulation_placeholder_body" = "O modo de simulação executa o diagrama passo a passo, destaca o elemento atual e mostra os valores das variáveis e a saída."; -"simulation_controls" = "Controlos"; -"simulation_highlight_label" = "Destaque"; -"simulation_highlight_last" = "Último passo"; -"simulation_highlight_next" = "Próximo passo"; -"simulation_not_available" = "Play executa a simulação automaticamente com uma pausa de 0,5 segundos por passo."; -"simulation_play" = "Reproduzir"; -"simulation_pause" = "Pausa"; -"simulation_step" = "Passo"; -"simulation_stop" = "Parar"; -"simulation_input" = "Entrada"; -"simulation_input_placeholder" = "Nenhuma entrada necessária."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Introduza um número inteiro."; -"simulation_trace" = "Último passo"; -"simulation_trace_placeholder" = "Ainda não foi executado nenhum passo."; -"simulation_variables" = "Variáveis"; -"simulation_variables_placeholder" = "Ainda não há variáveis."; -"simulation_output" = "Saída"; -"simulation_output_placeholder" = "Ainda não há saída."; -"action_delete_selection" = "Eliminar seleção"; -"ctx_copy" = "Copiar"; -"ctx_cut" = "Cortar"; -"ctx_paste" = "Colar"; -"action_settings" = "Definições"; -"action_about" = "Acerca do VisuStruct"; -"recent_files_menu" = "Recentes"; -"recent_files_empty" = "Sem ficheiros recentes"; -"recent_files_clear" = "Limpar lista"; -"recent_files_unavailable" = "O ficheiro já não está disponível."; - -/* Definições (na app) */ -"settings_language_title" = "Idioma"; -"settings_language_de" = "Alemão"; -"settings_language_en" = "Inglês"; -"settings_language_es" = "Espanhol"; -"settings_language_fr" = "Francês"; -"settings_language_it" = "Italiano"; -"settings_language_pl" = "Polaco"; -"settings_language_tr" = "Turco"; -"settings_language_ko" = "Coreano"; -"settings_language_ja" = "Japonês"; -"settings_language_el" = "Grego"; -"settings_language_uk" = "Ucraniano"; -"settings_language_ar" = "Árabe"; -"settings_language_ru" = "Russo"; -"settings_language_he" = "Hebraico"; -"settings_language_zh_hans" = "Chinês (simplificado)"; -"settings_language_zh_hant" = "Chinês (tradicional)"; -"settings_language_ptpt" = "Português"; -"settings_language_java" = "Java (palavras-chave)"; - - - -/* Erro */ -"error_title" = "Erro"; -"error_back_editor" = "Voltar ao editor"; - -/* Barra de ferramentas */ -"toolbar_undo" = "Anular"; -"toolbar_redo" = "Refazer"; -"toolbar_new" = "Novo"; -"menu_open" = "Abrir…"; -"menu_save" = "Guardar"; -"menu_save_as" = "Guardar como…"; -"menu_print" = "Imprimir…"; -"menu_file" = "Ficheiro"; -"menu_new" = "Novo"; -"menu_close" = "Fechar"; - -/* Acessibilidade */ -"a11y_generate_code" = "Gerar código a partir do diagrama"; -"a11y_generate_code_locked" = "Gerar código, disponível após comprar"; -"a11y_unsaved_changes" = "Alterações não guardadas"; - -/* Gerador de código (compra na app em breve) */ -"codegen_gate_title" = "Gerar código"; -"codegen_gate_body" = "O código Java, Python e JavaScript a partir do diagrama ficará disponível com uma compra na aplicação (em breve)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Gerador de código"; -"codegen_placeholder_body" = "O código gerado aparecerá aqui."; -"codegen_placeholder_compare" = "Alterne entre Java, Python e JavaScript para comparar a sintaxe (exemplo abaixo)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condicao) {\\n instrucao();\\n} else {\\n alternativa();\\n}"; -"codegen_preview_python" = "if condicao:\\n instrucao()\\nelse:\\n alternativa()"; -"codegen_generated_hint" = "Pré-visualização a partir do diagrama atual (Java 17, Python 3.10+, JavaScript moderno)."; -"codegen_copy" = "Copiar"; -"codegen_run_in_browser" = "Testar no navegador"; -"codegen_save_java" = "Guardar Java…"; -"codegen_java_output_mode" = "Saída Java"; -"codegen_java_output_snippet" = "Excerto"; -"codegen_java_output_class" = "Classe"; -"a11y_new_diagram_tab" = "Novo separador de diagrama"; -"a11y_close_tab" = "Fechar separador"; - -"close_unsaved_title" = "Alterações não guardadas"; -"close_unsaved_message" = "Este diagrama contém alterações não guardadas. Pretende guardá-las antes de fechar?"; -"close_unsaved_discard" = "Descartar"; -"close_unsaved_cancel" = "Cancelar"; -"a11y_placeholder_drop" = "Largue aqui um elemento para substituir o marcador. Mantenha premido para arrastar e mover."; -"a11y_node_drop" = "Mantenha premido um bloco e arraste: largue na borda superior ou inferior do destino (antes/depois). No centro só em marcador ø."; -"a11y_codegen_panel_drag" = "Arraste para mover o painel do gerador de código"; - -/* Diagrama */ -"diagram_title_label" = "Título"; -"diagram_title_placeholder" = "Sem título"; - -/* Inspetor */ -"inspector_no_selection" = "Nenhum elemento selecionado — toque num bloco."; -"inspector_apply_text" = "Aplicar texto"; -"inspector_for_init" = "Início"; -"inspector_for_condition" = "Condição"; -"inspector_for_step" = "Passo"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Tipo"; -"inspector_io_mode_statement" = "Instrução"; -"inspector_io_mode_input" = "Entrada"; -"inspector_io_mode_output" = "Saída"; -"inspector_io_mode_help_java_like" = "Entrada: input: … | Saída: output: …"; -"inspector_add_case" = "Adicionar caso"; -"inspector_remove_case" = "Remover caso"; -"inspector_group_element" = "Texto do elemento"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Inserir condição…"; -"inspector_placeholder_selector" = "Inserir seletor…"; -"inspector_placeholder_call" = "Inserir chamada…"; -"inspector_placeholder_break" = "Comentário (opcional)"; - -/* Tipos de elemento */ -"element_anweisung" = "Instrução"; -"element_verzweigung" = "Decisão"; -"element_fallauswahl" = "Seleção"; -"element_forSchleife" = "Ciclo for"; -"element_whileSchleife" = "Ciclo while"; -"element_doUntilSchleife" = "Ciclo do-while"; -"element_endlosschleife" = "Ciclo infinito"; -"element_aussprung" = "Interrupção"; -"element_aufruf" = "Chamada"; -"element_leerElement" = "Elemento vazio"; - -/* Separadores */ -"tab_title_format" = "Diagrama %d"; - -/* Ecrã vazio */ -"canvas_empty_title" = "Diagrama vazio"; -"canvas_empty_hint" = "Arraste um elemento de «Elementos» para aqui."; - -/* Diagrama: textos de elementos novos e cabeçalhos de coluna (exibição) */ -"canvas_stub_fallauswahl" = "Seleção"; -"canvas_stub_anweisung" = "Instrução"; -"canvas_stub_condition" = "Condição"; -"canvas_stub_for" = "for 0 < i < quantidade"; -"canvas_stub_while" = "enquanto …"; -"canvas_stub_until" = "até …"; -"canvas_stub_endless" = "infinito"; -"canvas_stub_break" = "parar"; -"canvas_stub_call" = "Chamada()"; - -/* Acerca */ -"about_version_fmt" = "Versão %@ (%@)"; -"about_nav_title" = "Acerca do VisuStruct"; -"about_features_heading" = "Funcionalidades"; -"about_bullet_palette" = "Inserir blocos a partir da paleta ou arrastá-los para o diagrama"; -"about_bullet_edit" = "Editar o diagrama: alterar textos, selecionar elementos e removê-los se necessário"; -"about_bullet_tabs" = "Vários diagramas em separadores — cada um com o seu título"; -"about_bullet_file" = "Guardar e reabrir diagramas como ficheiros .visustruct"; -"about_bullet_share" = "Partilhar o diagrama estruturado como imagem (PNG)"; -"about_dateiformat_heading" = "Formato do ficheiro"; -"about_dateiformat_part1" = "O formato nativo do VisuStruct utiliza a extensão "; -"about_dateiformat_part2" = ". Pode guardar diagramas e trocá-los com outras apps que suportem o mesmo formato."; -"about_dateiformat_strk" = "Também é possível abrir ficheiros com a extensão .strk."; -"about_project_github_label" = "App Java (código-fonte e releases):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Programador:"; -"about_lineage_heading" = "Origem"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Upstream Java:"; -"about_done" = "Concluído"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: inserir por toque */ -"phone_insert_cancel" = "Cancelar inserção"; -"phone_show_text_editor" = "Editar texto"; -"phone_hide_text_editor" = "Recolher"; -"phone_title_done" = "OK"; - -/* Alertas */ -"err_save_failed_fmt" = "Não foi possível guardar: %@"; -"err_share_prepare_failed_fmt" = "Não foi possível preparar a partilha: %@"; -"err_diagram_too_small_export" = "O diagrama é demasiado pequeno — nada para exportar."; -"err_image_render_failed" = "Não foi possível criar a imagem."; -"err_diagram_too_small_print" = "O diagrama é demasiado pequeno — nada para imprimir."; -"err_print_unavailable" = "A impressão não está disponível neste dispositivo."; - -"settings_ui_scale_section" = "Tamanho do texto"; -"settings_ui_scale_canvas" = "Diagrama"; -"settings_ui_scale_inspector" = "Introdução de texto em baixo"; -"settings_ui_scale_small" = "Pequeno"; -"settings_ui_scale_medium" = "Médio"; -"settings_ui_scale_large" = "Grande"; -"settings_ui_scale_extra_large" = "Muito grande"; - -"settings_export_filename_section" = "Sugestão de nome de ficheiro"; -"settings_export_filename_picker" = "Formato"; -"settings_export_filename_date_title" = "Com data"; -"settings_export_filename_title_only" = "Sem data"; -"settings_export_filename_footer_with_date" = "Com data: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Sem data: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulação terminada."; -"sim_notice_infinite_loop_empty" = "O ciclo infinito não tem corpo."; -"sim_notice_unsupported_statement_fmt" = "Instrução não suportada: %@"; -"sim_notice_invalid_expression_fmt" = "Expressão inválida: %@"; -"sim_notice_unknown_variable_fmt" = "Variável desconhecida: %@"; -"sim_notice_division_by_zero" = "Divisão por zero."; -"sim_notice_whole_number_expected" = "Introduza um número inteiro."; -"sim_notice_number_expected" = "Introduza um número."; -"sim_notice_boolean_expected" = "Introduza true ou false."; -"sim_notice_subprogram_not_found_fmt" = "Nenhum diagrama aberto se chama %@. A chamada foi ignorada."; -"sim_notice_call_too_deep" = "Demasiadas chamadas aninhadas. Um subprograma chama-se a si próprio?"; - -"codegen_pseudocode_toggle" = "Pseudocódigo (código não executável)"; -"codegen_pseudocode_note" = "Pseudocódigo – não é um programa executável"; -"codegen_pseudocode_hint" = "Este diagrama está marcado como pseudocódigo. O código é apenas um esboço e não pode ser executado."; - -"course_title" = "Curso"; -"course_intro" = "Aprender a programar passo a passo: do primeiro diagrama ao teu próprio programa em Java."; -"course_bullet_lessons" = "Todas as lições com explicações, testes e exercícios"; -"course_bullet_java" = "Tarefas de programação «Agora em Java» em cada lição"; -"course_bullet_games" = "Jogos de aprendizagem e dicas graduais"; -"course_bullet_once" = "Compra uma vez e usa no iPhone, iPad e Mac"; -"course_unlocked" = "Curso desbloqueado – obrigado!"; -"course_free_note" = "Criar diagramas, a simulação e a exportação de código continuam gratuitos."; -"course_restore" = "Restaurar compras"; -"course_restore_done" = "A tua compra do curso foi restaurada."; -"course_restore_none" = "Não foi encontrada nenhuma compra do curso para esta Conta Apple."; -"course_unavailable" = "Neste momento não é possível carregar o curso da App Store. Verifica a ligação à internet e tenta mais tarde."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-CaVEL1es.js b/website/app/assets/Localizable-CaVEL1es.js deleted file mode 100644 index 74e69fc5..00000000 --- a/website/app/assets/Localizable-CaVEL1es.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "元素"; -"inspector_header" = "对象检查器"; - -/* Actions */ -"action_share_png" = "分享图片…"; -"action_share_document" = "分享文件…"; -"action_export_pdf" = "PDF 预览…"; -"action_generate_code" = "生成代码…"; -"action_simulation" = "模拟"; -"action_diagram" = "图表"; -"simulation_placeholder_body" = "模拟模式会逐步运行图表,突出显示当前元素,并显示变量值和输出。"; -"simulation_controls" = "控制"; -"simulation_highlight_label" = "高亮"; -"simulation_highlight_last" = "上一步"; -"simulation_highlight_next" = "下一步"; -"simulation_not_available" = "Play 会自动运行模拟,每步暂停 0.5 秒。"; -"simulation_play" = "播放"; -"simulation_pause" = "暂停"; -"simulation_step" = "单步"; -"simulation_stop" = "停止"; -"simulation_input" = "输入"; -"simulation_input_placeholder" = "不需要输入。"; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "请输入一个整数。"; -"simulation_trace" = "上一步"; -"simulation_trace_placeholder" = "尚未执行任何步骤。"; -"simulation_variables" = "变量"; -"simulation_variables_placeholder" = "暂无变量。"; -"simulation_output" = "输出"; -"simulation_output_placeholder" = "暂无输出。"; -"action_delete_selection" = "删除所选"; -"ctx_copy" = "复制"; -"ctx_cut" = "剪切"; -"ctx_paste" = "粘贴"; -"action_settings" = "设置"; -"action_about" = "关于 VisuStruct"; -"recent_files_menu" = "最近文件"; -"recent_files_empty" = "没有最近文件"; -"recent_files_clear" = "清除列表"; -"recent_files_unavailable" = "文件不再可用。"; - -/* In-app settings */ -"settings_language_title" = "语言"; -"settings_language_de" = "德语"; -"settings_language_en" = "英语"; -"settings_language_es" = "西班牙语"; -"settings_language_fr" = "法语"; -"settings_language_it" = "意大利语"; -"settings_language_pl" = "波兰语"; -"settings_language_tr" = "土耳其语"; -"settings_language_ko" = "韩语"; -"settings_language_ja" = "日语"; -"settings_language_el" = "希腊语"; -"settings_language_uk" = "乌克兰语"; -"settings_language_ar" = "阿拉伯语"; -"settings_language_ru" = "俄语"; -"settings_language_he" = "希伯来语"; -"settings_language_zh_hans" = "中文(简体)"; -"settings_language_zh_hant" = "中文(繁體)"; -"settings_language_ptpt" = "葡萄牙语"; -"settings_language_java" = "Java(关键词)"; - - -/* Error overlay */ -"error_title" = "错误"; -"error_back_editor" = "返回编辑器"; - -/* Toolbar */ -"toolbar_undo" = "撤销"; -"toolbar_redo" = "重做"; -"toolbar_new" = "新建"; -"menu_open" = "打开…"; -"menu_save" = "保存"; -"menu_save_as" = "另存为…"; -"menu_print" = "打印…"; -"menu_file" = "文件"; -"menu_new" = "新建"; -"menu_close" = "关闭"; - -/* Accessibility */ -"a11y_generate_code" = "从结构图生成代码"; -"a11y_generate_code_locked" = "生成代码(购买后可用)"; -"a11y_unsaved_changes" = "未保存的更改"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "生成代码"; -"codegen_gate_body" = "将通过应用内购买解锁从图表生成的 Java、Python 和 JavaScript 代码(即将推出)。"; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "代码生成器"; -"codegen_placeholder_body" = "生成的代码会显示在这里。"; -"codegen_placeholder_compare" = "在 Java、Python 和 JavaScript 之间切换以比较语法(下方示例)。"; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (条件) {\\n 语句();\\n} else {\\n 替代();\\n}"; -"codegen_preview_python" = "if 条件:\\n 语句()\\nelse:\\n 替代()"; -"codegen_copy" = "复制"; -"codegen_run_in_browser" = "在浏览器中测试"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "新建图表标签页"; -"a11y_close_tab" = "关闭标签页"; - -"close_unsaved_title" = "未保存的更改"; -"close_unsaved_message" = "此图表包含未保存的更改。关闭前要保存吗?"; -"close_unsaved_discard" = "放弃"; -"close_unsaved_cancel" = "取消"; -"a11y_placeholder_drop" = "将元素拖放到这里以替换占位符。长按可拖动移动。"; -"a11y_node_drop" = "长按块并拖动:在目标上边缘或下边缘放下以插入到前/后。仅在 ø 占位符上可居中放下。"; -"a11y_codegen_panel_drag" = "拖移以移动代码生成器面板"; - -/* Diagram */ -"diagram_title_label" = "标题"; -"diagram_title_placeholder" = "无标题"; - -/* Inspector */ -"inspector_no_selection" = "未选择元素 — 点击一个块。"; -"inspector_apply_text" = "应用文本"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "类型"; -"inspector_io_mode_statement" = "语句"; -"inspector_io_mode_input" = "输入"; -"inspector_io_mode_output" = "输出"; -"inspector_io_mode_help_java_like" = "输入: input: … | 输出: output: …"; -"inspector_add_case" = "添加分支"; -"inspector_remove_case" = "移除分支"; -"inspector_group_element" = "元素文本"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "输入条件…"; -"inspector_placeholder_selector" = "输入选择式…"; -"inspector_placeholder_call" = "输入调用…"; -"inspector_placeholder_break" = "注释(可选)"; - -/* Element types */ -"element_anweisung" = "语句"; -"element_verzweigung" = "分支"; -"element_fallauswahl" = "选择"; -"element_forSchleife" = "for 循环"; -"element_whileSchleife" = "while 循环"; -"element_doUntilSchleife" = "do-while 循环"; -"element_endlosschleife" = "无限循环"; -"element_aussprung" = "跳出"; -"element_aufruf" = "调用"; -"element_leerElement" = "空元素"; - -/* Tabs */ -"tab_title_format" = "图表 %d"; - -/* Empty canvas */ -"canvas_empty_title" = "空结构图"; -"canvas_empty_hint" = "从“元素”中拖拽一个块到这里。"; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "选择"; -"canvas_stub_anweisung" = "语句"; -"canvas_stub_condition" = "条件"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "无限"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "调用()"; - -/* About */ -"about_version_fmt" = "版本 %@ (%@)"; -"about_nav_title" = "关于 VisuStruct"; -"about_features_heading" = "功能"; -"about_bullet_palette" = "从调色板插入块,或拖入图表"; -"about_bullet_edit" = "编辑图表:修改文本、选择元素,必要时删除"; -"about_bullet_tabs" = "多个图表标签页——每个都有自己的标题"; -"about_bullet_file" = "将图表保存并以 .visustruct 文件重新打开"; -"about_bullet_share" = "将结构图分享为图片(PNG)"; -"about_dateiformat_heading" = "文件格式"; -"about_dateiformat_part1" = "VisuStruct 的原生格式使用扩展名 "; -"about_dateiformat_part2" = "。你可以保存图表,并与支持相同格式的应用交换。"; -"about_dateiformat_strk" = "也可以打开 .strk 扩展名的文件。"; -"about_project_github_label" = "Java 应用(源码与版本):"; -"about_web_label" = "网页:"; -"about_developer_label" = "开发者:"; -"about_lineage_heading" = "来源"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java 上游:"; -"about_done" = "完成"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "取消插入"; -"phone_show_text_editor" = "编辑文本"; -"phone_hide_text_editor" = "收起"; -"phone_title_done" = "完成"; - -/* Alerts */ -"err_save_failed_fmt" = "无法保存:%@"; -"err_share_prepare_failed_fmt" = "无法准备分享:%@"; -"err_diagram_too_small_export" = "图表太小——无内容可导出。"; -"err_image_render_failed" = "无法创建图片。"; -"err_diagram_too_small_print" = "图表太小——无内容可打印。"; -"err_print_unavailable" = "此设备不支持打印。"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "文件名建议"; -"settings_export_filename_picker" = "格式"; -"settings_export_filename_date_title" = "含日期"; -"settings_export_filename_title_only" = "不含日期"; -"settings_export_filename_footer_with_date" = "含日期: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "不含日期: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "模拟已结束。"; -"sim_notice_infinite_loop_empty" = "无限循环没有循环体。"; -"sim_notice_unsupported_statement_fmt" = "不支持的语句:%@"; -"sim_notice_invalid_expression_fmt" = "无效的表达式:%@"; -"sim_notice_unknown_variable_fmt" = "未知变量:%@"; -"sim_notice_division_by_zero" = "除数为零。"; -"sim_notice_whole_number_expected" = "请输入整数。"; -"sim_notice_number_expected" = "请输入数字。"; -"sim_notice_boolean_expected" = "请输入 true 或 false。"; -"sim_notice_subprogram_not_found_fmt" = "没有名为 %@ 的已打开图表。已跳过该调用。"; -"sim_notice_call_too_deep" = "嵌套调用过多。是否有子程序调用了自身?"; - -"codegen_pseudocode_toggle" = "伪代码(不可运行的代码)"; -"codegen_pseudocode_note" = "伪代码 – 不是可运行的程序"; -"codegen_pseudocode_hint" = "此图已标记为伪代码。代码只是草稿,无法运行。"; -`;export{e as default}; diff --git a/website/app/assets/Localizable-CkJEmEEP.js b/website/app/assets/Localizable-CkJEmEEP.js deleted file mode 100644 index 32b34aa8..00000000 --- a/website/app/assets/Localizable-CkJEmEEP.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "要素"; -"inspector_header" = "オブジェクトインスペクタ"; - -/* Actions */ -"action_share_png" = "画像を共有…"; -"action_share_document" = "ファイルを共有…"; -"action_export_pdf" = "PDF をプレビュー…"; -"action_generate_code" = "コード生成…"; -"action_simulation" = "シミュレーション"; -"action_diagram" = "図"; -"simulation_placeholder_body" = "シミュレーションモードは図をステップごとに実行し、現在の要素を強調表示して、変数の値と出力を表示します。"; -"simulation_controls" = "操作"; -"simulation_highlight_label" = "ハイライト"; -"simulation_highlight_last" = "前のステップ"; -"simulation_highlight_next" = "次のステップ"; -"simulation_not_available" = "Play は各ステップごとに0.5秒停止しながらシミュレーションを自動実行します。"; -"simulation_play" = "再生"; -"simulation_pause" = "一時停止"; -"simulation_step" = "ステップ"; -"simulation_stop" = "停止"; -"simulation_input" = "入力"; -"simulation_input_placeholder" = "入力は不要です。"; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "整数を入力してください。"; -"simulation_trace" = "最後のステップ"; -"simulation_trace_placeholder" = "まだステップは実行されていません。"; -"simulation_variables" = "変数"; -"simulation_variables_placeholder" = "まだ変数はありません。"; -"simulation_output" = "出力"; -"simulation_output_placeholder" = "まだ出力はありません。"; -"action_delete_selection" = "選択を削除"; -"ctx_copy" = "コピー"; -"ctx_cut" = "切り取り"; -"ctx_paste" = "貼り付け"; -"action_settings" = "設定"; -"action_about" = "VisuStruct について"; -"recent_files_menu" = "最近使ったファイル"; -"recent_files_empty" = "最近使ったファイルはありません"; -"recent_files_clear" = "リストを消去"; -"recent_files_unavailable" = "ファイルは利用できなくなりました。"; - -/* In-app settings */ -"settings_language_title" = "言語"; -"settings_language_de" = "ドイツ語"; -"settings_language_en" = "英語"; -"settings_language_es" = "スペイン語"; -"settings_language_fr" = "フランス語"; -"settings_language_it" = "イタリア語"; -"settings_language_pl" = "ポーランド語"; -"settings_language_tr" = "トルコ語"; -"settings_language_ko" = "韓国語"; -"settings_language_ja" = "日本語"; -"settings_language_el" = "ギリシャ語"; -"settings_language_uk" = "ウクライナ語"; -"settings_language_ar" = "アラビア語"; -"settings_language_ru" = "ロシア語"; -"settings_language_he" = "ヘブライ語"; -"settings_language_zh_hans" = "中国語(簡体字)"; -"settings_language_zh_hant" = "中国語(繁体字)"; -"settings_language_ptpt" = "ポルトガル語"; -"settings_language_java" = "Java(キーワード)"; - - -/* Error overlay */ -"error_title" = "エラー"; -"error_back_editor" = "エディタに戻る"; - -/* Toolbar */ -"toolbar_undo" = "取り消し"; -"toolbar_redo" = "やり直し"; -"toolbar_new" = "新規"; -"menu_open" = "開く…"; -"menu_save" = "保存"; -"menu_save_as" = "別名で保存…"; -"menu_print" = "印刷…"; -"menu_file" = "ファイル"; -"menu_new" = "新規"; -"menu_close" = "閉じる"; - -/* Accessibility */ -"a11y_generate_code" = "構造図からコードを生成"; -"a11y_generate_code_locked" = "コード生成(購入後に利用可能)"; -"a11y_unsaved_changes" = "未保存の変更"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "コード生成"; -"codegen_gate_body" = "図から生成される Java、Python、JavaScript のコードはアプリ内課金で利用可能になります(近日公開)。"; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "コード生成"; -"codegen_placeholder_body" = "生成されたコードはここに表示されます。"; -"codegen_placeholder_compare" = "Java、Python、JavaScript を切り替えて構文を比較できます(下の例)。"; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (条件) {\\n 文();\\n} else {\\n 代替();\\n}"; -"codegen_preview_python" = "if 条件:\\n 文()\\nelse:\\n 代替()"; -"codegen_copy" = "コピー"; -"codegen_run_in_browser" = "ブラウザでテスト"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "新しい図のタブ"; -"a11y_close_tab" = "タブを閉じる"; - -"close_unsaved_title" = "未保存の変更"; -"close_unsaved_message" = "この図には未保存の変更があります。閉じる前に保存しますか?"; -"close_unsaved_discard" = "破棄"; -"close_unsaved_cancel" = "キャンセル"; -"a11y_placeholder_drop" = "要素をここにドロップしてプレースホルダーを置き換えます。長押ししてドラッグし移動できます。"; -"a11y_node_drop" = "ブロックを長押ししてドラッグ:対象の上端/下端にドロップすると前/後に挿入。中央へのドロップは ø プレースホルダーのみ。"; -"a11y_codegen_panel_drag" = "ドラッグしてコード生成パネルを移動"; - -/* Diagram */ -"diagram_title_label" = "タイトル"; -"diagram_title_placeholder" = "タイトルなし"; - -/* Inspector */ -"inspector_no_selection" = "要素が選択されていません — タイルをタップしてください。"; -"inspector_apply_text" = "テキストを適用"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "種類"; -"inspector_io_mode_statement" = "文"; -"inspector_io_mode_input" = "入力"; -"inspector_io_mode_output" = "出力"; -"inspector_io_mode_help_java_like" = "入力: input: … | 出力: output: …"; -"inspector_add_case" = "ケースを追加"; -"inspector_remove_case" = "ケースを削除"; -"inspector_group_element" = "要素テキスト"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "条件を入力…"; -"inspector_placeholder_selector" = "セレクタを入力…"; -"inspector_placeholder_call" = "呼び出しを入力…"; -"inspector_placeholder_break" = "コメント(任意)"; - -/* Element types (Japanese + keyword hint) */ -"element_anweisung" = "文 (statement)"; -"element_verzweigung" = "条件分岐 (if/else)"; -"element_fallauswahl" = "選択 (switch)"; -"element_forSchleife" = "繰り返し (for)"; -"element_whileSchleife" = "繰り返し (while)"; -"element_doUntilSchleife" = "繰り返し (do-while)"; -"element_endlosschleife" = "無限ループ (while true)"; -"element_aussprung" = "脱出 (break)"; -"element_aufruf" = "呼び出し (call)"; -"element_leerElement" = "空の要素"; - -/* Tabs */ -"tab_title_format" = "図 %d"; - -/* Empty canvas */ -"canvas_empty_title" = "空の構造図"; -"canvas_empty_hint" = "「要素」からブロックをここにドラッグしてください。"; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "選択 (switch)"; -"canvas_stub_anweisung" = "文 (statement)"; -"canvas_stub_condition" = "条件"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "無限"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "呼び出し()"; - -/* About */ -"about_version_fmt" = "バージョン %@ (%@)"; -"about_nav_title" = "VisuStruct について"; -"about_features_heading" = "機能"; -"about_bullet_palette" = "パレットからブロックを挿入、または図へドラッグ&ドロップ"; -"about_bullet_edit" = "図を編集:テキスト変更、要素選択、必要に応じて削除"; -"about_bullet_tabs" = "複数の図をタブで管理(それぞれタイトル付き)"; -"about_bullet_file" = ".visustruct ファイルとして保存し再度開く"; -"about_bullet_share" = "構造図を画像(PNG)として共有"; -"about_dateiformat_heading" = "ファイル形式"; -"about_dateiformat_part1" = "VisuStruct のネイティブ形式は拡張子 "; -"about_dateiformat_part2" = " を使用します。同じ形式をサポートする他のアプリと交換できます。"; -"about_dateiformat_strk" = ".strk 拡張子のファイルも開けます。"; -"about_project_github_label" = "Java アプリ(ソース&リリース):"; -"about_web_label" = "Web:"; -"about_developer_label" = "開発者:"; -"about_lineage_heading" = "系譜"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "完了"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "挿入をキャンセル"; -"phone_show_text_editor" = "テキストを編集"; -"phone_hide_text_editor" = "折りたたむ"; -"phone_title_done" = "完了"; - -/* Alerts */ -"err_save_failed_fmt" = "保存できませんでした:%@"; -"err_share_prepare_failed_fmt" = "共有の準備ができませんでした:%@"; -"err_diagram_too_small_export" = "図が小さすぎます — エクスポートできません。"; -"err_image_render_failed" = "画像を作成できませんでした。"; -"err_diagram_too_small_print" = "図が小さすぎます — 印刷できません。"; -"err_print_unavailable" = "このデバイスでは印刷できません。"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "ファイル名の候補"; -"settings_export_filename_picker" = "形式"; -"settings_export_filename_date_title" = "日付あり"; -"settings_export_filename_title_only" = "日付なし"; -"settings_export_filename_footer_with_date" = "日付あり: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "日付なし: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "シミュレーションが終了しました。"; -"sim_notice_infinite_loop_empty" = "無限ループに本体がありません。"; -"sim_notice_unsupported_statement_fmt" = "サポートされていない文: %@"; -"sim_notice_invalid_expression_fmt" = "無効な式: %@"; -"sim_notice_unknown_variable_fmt" = "不明な変数: %@"; -"sim_notice_division_by_zero" = "0 で割ることはできません。"; -"sim_notice_whole_number_expected" = "整数を入力してください。"; -"sim_notice_number_expected" = "数値を入力してください。"; -"sim_notice_boolean_expected" = "true または false を入力してください。"; -"sim_notice_subprogram_not_found_fmt" = "%@ という名前の開いている図はありません。呼び出しをスキップしました。"; -"sim_notice_call_too_deep" = "呼び出しの入れ子が多すぎます。サブプログラムが自分自身を呼び出していませんか?"; - -"codegen_pseudocode_toggle" = "疑似コード(実行できないコード)"; -"codegen_pseudocode_note" = "疑似コード – 実行可能なプログラムではありません"; -"codegen_pseudocode_hint" = "この図は疑似コードとしてマークされています。コードは下書きにすぎず、実行できません。"; -`;export{e as default}; diff --git a/website/app/assets/Localizable-CxCno28C.js b/website/app/assets/Localizable-CxCno28C.js deleted file mode 100644 index 61b9585a..00000000 --- a/website/app/assets/Localizable-CxCno28C.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "العناصر"; -"inspector_header" = "مفتش الكائنات"; - -/* Actions */ -"action_share_png" = "مشاركة الصورة…"; -"action_share_document" = "مشاركة الملف…"; -"action_export_pdf" = "معاينة PDF…"; -"action_generate_code" = "إنشاء الشفرة…"; -"action_simulation" = "محاكاة"; -"action_diagram" = "مخطط"; -"simulation_placeholder_body" = "يشغّل وضع المحاكاة المخطط خطوة بخطوة، ويبرز العنصر الحالي، ويعرض قيم المتغيرات والمخرجات."; -"simulation_controls" = "عناصر التحكم"; -"simulation_highlight_label" = "تمييز"; -"simulation_highlight_last" = "آخر خطوة"; -"simulation_highlight_next" = "الخطوة التالية"; -"simulation_not_available" = "يقوم Play بتشغيل المحاكاة تلقائيًا مع توقف 0.5 ثانية لكل خطوة."; -"simulation_play" = "تشغيل"; -"simulation_pause" = "إيقاف مؤقت"; -"simulation_step" = "خطوة"; -"simulation_stop" = "إيقاف"; -"simulation_input" = "إدخال"; -"simulation_input_placeholder" = "لا يلزم إدخال."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "يرجى إدخال عدد صحيح."; -"simulation_trace" = "آخر خطوة"; -"simulation_trace_placeholder" = "لم يُنفَّذ أي خطوة بعد."; -"simulation_variables" = "المتغيرات"; -"simulation_variables_placeholder" = "لا توجد متغيرات بعد."; -"simulation_output" = "المخرجات"; -"simulation_output_placeholder" = "لا يوجد إخراج بعد."; -"action_delete_selection" = "حذف التحديد"; -"ctx_copy" = "نسخ"; -"ctx_cut" = "قص"; -"ctx_paste" = "لصق"; -"action_settings" = "الإعدادات"; -"action_about" = "حول VisuStruct"; -"recent_files_menu" = "الملفات الأخيرة"; -"recent_files_empty" = "لا توجد ملفات حديثة"; -"recent_files_clear" = "مسح القائمة"; -"recent_files_unavailable" = "لم يعد الملف متاحًا."; - -/* In-app settings */ -"settings_language_title" = "اللغة"; -"settings_language_de" = "الألمانية"; -"settings_language_en" = "الإنجليزية"; -"settings_language_es" = "الإسبانية"; -"settings_language_fr" = "الفرنسية"; -"settings_language_it" = "الإيطالية"; -"settings_language_pl" = "البولندية"; -"settings_language_tr" = "التركية"; -"settings_language_ko" = "الكورية"; -"settings_language_ja" = "اليابانية"; -"settings_language_el" = "اليونانية"; -"settings_language_uk" = "الأوكرانية"; -"settings_language_ar" = "العربية"; -"settings_language_ru" = "الروسية"; -"settings_language_he" = "العبرية"; -"settings_language_zh_hans" = "الصينية (مبسطة)"; -"settings_language_zh_hant" = "الصينية (تقليدية)"; -"settings_language_ptpt" = "البرتغالية"; -"settings_language_java" = "Java (كلمات مفتاحية)"; - - -/* Error overlay */ -"error_title" = "خطأ"; -"error_back_editor" = "العودة إلى المحرر"; - -/* Toolbar */ -"toolbar_undo" = "تراجع"; -"toolbar_redo" = "إعادة"; -"toolbar_new" = "جديد"; -"menu_open" = "فتح…"; -"menu_save" = "حفظ"; -"menu_save_as" = "حفظ باسم…"; -"menu_print" = "طباعة…"; -"menu_file" = "ملف"; -"menu_new" = "جديد"; -"menu_close" = "إغلاق"; - -/* Accessibility */ -"a11y_generate_code" = "إنشاء شفرة من المخطط"; -"a11y_generate_code_locked" = "إنشاء الشفرة، متاح بعد الشراء"; -"a11y_unsaved_changes" = "تغييرات غير محفوظة"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "إنشاء الشفرة"; -"codegen_gate_body" = "سيتم فتح شفرة Java وPython وJavaScript من المخطط عبر عملية شراء داخل التطبيق (قريبًا)."; -"codegen_gate_ok" = "حسنًا"; -"codegen_placeholder_title" = "مولّد الشفرة"; -"codegen_placeholder_body" = "ستظهر الشفرة المُولَّدة هنا."; -"codegen_placeholder_compare" = "بدّل بين Java وPython وJavaScript لمقارنة الصياغة (مثال بالأسفل)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (الشرط) {\\n statement();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if الشرط:\\n statement()\\nelse:\\n alternative()"; -"codegen_copy" = "نسخ"; -"codegen_run_in_browser" = "اختبار في المتصفح"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "علامة تبويب جديدة للمخطط"; -"a11y_close_tab" = "إغلاق علامة التبويب"; - -"close_unsaved_title" = "تغييرات غير محفوظة"; -"close_unsaved_message" = "يحتوي هذا المخطط على تغييرات غير محفوظة. هل تريد حفظها قبل الإغلاق؟"; -"close_unsaved_discard" = "تجاهل"; -"close_unsaved_cancel" = "إلغاء"; -"a11y_placeholder_drop" = "أفلت عنصرًا هنا لاستبدال العنصر النائب. اضغط مطولًا للسحب والتحريك."; -"a11y_node_drop" = "اضغط مطولًا على كتلة ثم اسحب: أفلت على الحافة العلوية أو السفلية لإدراجها قبل أو بعد. الإسقاط في الوسط فقط على عنصر نائب ø."; -"a11y_codegen_panel_drag" = "اسحب لتحريك لوحة مولّد الشفرة"; - -/* Diagram */ -"diagram_title_label" = "العنوان"; -"diagram_title_placeholder" = "بدون عنوان"; - -/* Inspector */ -"inspector_no_selection" = "لا يوجد عنصر محدد — اضغط على كتلة."; -"inspector_apply_text" = "تطبيق النص"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "النوع"; -"inspector_io_mode_statement" = "تعليمة"; -"inspector_io_mode_input" = "إدخال"; -"inspector_io_mode_output" = "إخراج"; -"inspector_io_mode_help_java_like" = "إدخال: input: … | إخراج: output: …"; -"inspector_add_case" = "إضافة حالة"; -"inspector_remove_case" = "إزالة الحالة"; -"inspector_group_element" = "نص العنصر"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "أدخل condition…"; -"inspector_placeholder_selector" = "أدخل selector…"; -"inspector_placeholder_call" = "أدخل call…"; -"inspector_placeholder_break" = "تعليق (اختياري)"; - -/* Element types — keep Java-like (LTR) to reduce RTL confusion */ -"element_anweisung" = "Statement"; -"element_verzweigung" = "if"; -"element_fallauswahl" = "switch"; -"element_forSchleife" = "for"; -"element_whileSchleife" = "while"; -"element_doUntilSchleife" = "do"; -"element_endlosschleife" = "while(true)"; -"element_aussprung" = "break"; -"element_aufruf" = "استدعاء"; -"element_leerElement" = "ø"; - -/* Tabs */ -"tab_title_format" = "مخطط %d"; - -/* Empty canvas */ -"canvas_empty_title" = "مخطط فارغ"; -"canvas_empty_hint" = "اسحب عنصرًا من «العناصر» إلى هنا."; - -/* Diagram: new element stubs & column headers — keep Java-like */ -"canvas_stub_anweisung" = "Statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "selector"; -"canvas_stub_for" = "i = 0; i < n; i++"; -"canvas_stub_while" = "condition"; -"canvas_stub_until" = "condition"; -"canvas_stub_endless" = "∞"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "استدعاء()"; - -/* About */ -"about_version_fmt" = "الإصدار %@ (%@)"; -"about_nav_title" = "حول VisuStruct"; -"about_features_heading" = "الميزات"; -"about_bullet_palette" = "أدرج الكتل من اللوحة أو اسحبها إلى المخطط"; -"about_bullet_edit" = "حرّر المخطط: غيّر النص، حدّد العناصر وأزلها عند الحاجة"; -"about_bullet_tabs" = "عدة مخططات في علامات تبويب — لكل مخطط عنوانه"; -"about_bullet_file" = "احفظ وأعد فتح المخططات كملفات .visustruct"; -"about_bullet_share" = "شارك المخطط كصورة (PNG)"; -"about_dateiformat_heading" = "تنسيق الملف"; -"about_dateiformat_part1" = "يستخدم تنسيق VisuStruct الأصلي الامتداد "; -"about_dateiformat_part2" = ". يمكنك حفظ المخططات وتبادلها مع تطبيقات أخرى تدعم نفس التنسيق."; -"about_dateiformat_strk" = "يمكن أيضًا فتح الملفات ذات الامتداد .strk."; -"about_project_github_label" = "تطبيق Java (المصدر والإصدارات):"; -"about_web_label" = "الويب:"; -"about_developer_label" = "المطوّر:"; -"about_lineage_heading" = "الأصل"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "تم"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "إلغاء الإدراج"; -"phone_show_text_editor" = "تحرير النص"; -"phone_hide_text_editor" = "طيّ"; -"phone_title_done" = "تم"; - -/* Alerts */ -"err_save_failed_fmt" = "تعذر الحفظ: %@"; -"err_share_prepare_failed_fmt" = "تعذر تجهيز المشاركة: %@"; -"err_diagram_too_small_export" = "المخطط صغير جدًا — لا يوجد ما يمكن تصديره."; -"err_image_render_failed" = "تعذر إنشاء الصورة."; -"err_diagram_too_small_print" = "المخطط صغير جدًا — لا يوجد ما يمكن طباعته."; -"err_print_unavailable" = "الطباعة غير متاحة على هذا الجهاز."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "اقتراح اسم الملف"; -"settings_export_filename_picker" = "النمط"; -"settings_export_filename_date_title" = "مع التاريخ"; -"settings_export_filename_title_only" = "بدون تاريخ"; -"settings_export_filename_footer_with_date" = "مع التاريخ: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "بدون تاريخ: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "انتهت المحاكاة."; -"sim_notice_infinite_loop_empty" = "الحلقة اللانهائية ليس لها جسم."; -"sim_notice_unsupported_statement_fmt" = "تعليمة غير مدعومة: %@"; -"sim_notice_invalid_expression_fmt" = "تعبير غير صالح: %@"; -"sim_notice_unknown_variable_fmt" = "متغير غير معروف: %@"; -"sim_notice_division_by_zero" = "القسمة على صفر."; -"sim_notice_whole_number_expected" = "أدخل عددًا صحيحًا."; -"sim_notice_number_expected" = "أدخل رقمًا."; -"sim_notice_boolean_expected" = "أدخل true أو false."; -"sim_notice_subprogram_not_found_fmt" = "لا يوجد مخطط مفتوح باسم %@. تم تخطي الاستدعاء."; -"sim_notice_call_too_deep" = "استدعاءات متداخلة كثيرة جدًا. هل يستدعي برنامج فرعي نفسه؟"; - -"codegen_pseudocode_toggle" = "شبه كود (ليس كودًا قابلًا للتشغيل)"; -"codegen_pseudocode_note" = "شبه كود – ليس برنامجًا قابلًا للتشغيل"; -"codegen_pseudocode_hint" = "هذا المخطط معلَّم كشبه كود. الكود مجرد مسودة ولا يمكن تشغيله."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-DBLIUcBd.js b/website/app/assets/Localizable-DBLIUcBd.js deleted file mode 100644 index 3e1e935f..00000000 --- a/website/app/assets/Localizable-DBLIUcBd.js +++ /dev/null @@ -1,255 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Elements"; -"inspector_header" = "Object Inspector"; - -/* Actions */ -"action_share_png" = "Share Graphic…"; -"action_share_document" = "Share File…"; -"action_export_pdf" = "Preview PDF…"; -"action_generate_code" = "Generate Code…"; -"action_simulation" = "Simulation"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "The simulation mode will run the diagram step by step, highlight the current element, and show variable values and output."; -"simulation_controls" = "Controls"; -"simulation_highlight_label" = "Highlight"; -"simulation_highlight_last" = "Last step"; -"simulation_highlight_next" = "Next step"; -"simulation_not_available" = "Play runs the simulation automatically with a 0.5 second pause per step."; -"simulation_play" = "Play"; -"simulation_pause" = "Pause"; -"simulation_step" = "Step"; -"simulation_stop" = "Stop"; -"simulation_input" = "Input"; -"simulation_input_placeholder" = "No input required."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Please enter a whole number."; -"simulation_trace" = "Last Step"; -"simulation_trace_placeholder" = "No step executed yet."; -"simulation_variables" = "Variables"; -"simulation_variables_placeholder" = "No variables yet."; -"simulation_output" = "Output"; -"simulation_output_placeholder" = "No output yet."; -"action_delete_selection" = "Delete Selection"; -"ctx_copy" = "Copy"; -"ctx_cut" = "Cut"; -"ctx_paste" = "Paste"; -"action_settings" = "Settings"; -"action_about" = "About VisuStruct"; -"recent_files_menu" = "Recent Files"; -"recent_files_empty" = "No recent files"; -"recent_files_clear" = "Clear List"; -"recent_files_unavailable" = "File is no longer available."; - -/* In-app settings */ -"settings_language_title" = "Language"; -"settings_language_de" = "German"; -"settings_language_en" = "English"; -"settings_language_es" = "Spanish"; -"settings_language_fr" = "French"; -"settings_language_it" = "Italian"; -"settings_language_pl" = "Polish"; -"settings_language_tr" = "Turkish"; -"settings_language_ko" = "Korean"; -"settings_language_ja" = "Japanese"; -"settings_language_el" = "Greek"; -"settings_language_uk" = "Ukrainian"; -"settings_language_ar" = "Arabic"; -"settings_language_ru" = "Russian"; -"settings_language_he" = "Hebrew"; -"settings_language_zh_hans" = "Chinese (Simplified)"; -"settings_language_zh_hant" = "Chinese (Traditional)"; -"settings_language_ptpt" = "Portuguese"; -"settings_language_java" = "Java (keywords)"; - - - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "File name suggestion"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "With date"; -"settings_export_filename_title_only" = "Without date"; -"settings_export_filename_footer_with_date" = "With date: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Without date: Filename.visustruct"; - - -/* Error overlay */ -"error_title" = "Error"; -"error_back_editor" = "Back to Editor"; - -/* Toolbar */ -"toolbar_undo" = "Undo"; -"toolbar_redo" = "Redo"; -"toolbar_new" = "New"; -"menu_open" = "Open…"; -"menu_save" = "Save"; -"menu_save_as" = "Save As…"; -"menu_print" = "Print…"; -"menu_file" = "File"; -"menu_new" = "New"; -"menu_close" = "Close"; - -/* Accessibility */ -"a11y_generate_code" = "Generate code from the structure chart"; -"a11y_generate_code_locked" = "Generate code, available after purchase"; -"a11y_unsaved_changes" = "Unsaved changes"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Generate code"; -"codegen_gate_body" = "Java, Python, and JavaScript code from your diagram will unlock via in-app purchase (coming soon)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Code generator"; -"codegen_placeholder_body" = "Generated code will appear here."; -"codegen_placeholder_compare" = "Switch between Java, Python, and JavaScript to compare syntax (sample below)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condition) {\\n statement();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if condition:\\n statement()\\nelse:\\n alternative()"; -"codegen_generated_hint" = "Preview from the current structure chart (Java 17, Python 3.10+, modern JavaScript)."; -"codegen_copy" = "Copy"; -"codegen_run_in_browser" = "Test in browser"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "New diagram tab"; -"a11y_close_tab" = "Close tab"; - -"close_unsaved_title" = "Unsaved Changes"; -"close_unsaved_message" = "This diagram contains unsaved changes. Do you want to save them before closing?"; -"close_unsaved_discard" = "Discard"; -"close_unsaved_cancel" = "Cancel"; -"a11y_placeholder_drop" = "Drop an element here to replace the placeholder. Press and hold to drag and move."; -"a11y_node_drop" = "Press and hold a block, then drag: drop on the top or bottom edge of a target to insert before or after. Center drop only on a ø placeholder."; -"a11y_codegen_panel_drag" = "Drag to move code generator panel"; - -/* Diagram */ -"diagram_title_label" = "Title"; -"diagram_title_placeholder" = "No title"; - -/* Inspector */ -"inspector_no_selection" = "No element selected — tap a tile."; -"inspector_apply_text" = "Apply Text"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; -"inspector_io_mode_title" = "Kind"; -"inspector_io_mode_statement" = "Statement"; -"inspector_io_mode_input" = "Input"; -"inspector_io_mode_output" = "Output"; -"inspector_io_mode_help_java_like" = "Input: input: … | Output: output: …"; -"inspector_add_case" = "Add Case"; -"inspector_remove_case" = "Remove Case"; -"inspector_group_element" = "Element text"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Insert condition…"; -"inspector_placeholder_selector" = "Insert selector…"; -"inspector_placeholder_call" = "Insert call…"; -"inspector_placeholder_break" = "Comment (optional)"; - -/* Element types */ -"element_anweisung" = "Statement"; -"element_verzweigung" = "Branch"; -"element_fallauswahl" = "Switch"; -"element_forSchleife" = "For loop"; -"element_whileSchleife" = "While loop"; -"element_doUntilSchleife" = "Do-while loop"; -"element_endlosschleife" = "Endless loop"; -"element_aussprung" = "Break"; -"element_aufruf" = "Call"; -"element_leerElement" = "Empty element"; - -/* Tabs */ -"tab_title_format" = "Diagram %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Empty structure chart"; -"canvas_empty_hint" = "Drag an element from “Elements” here."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Switch"; -"canvas_stub_anweisung" = "Statement"; -"canvas_stub_condition" = "Condition"; -"canvas_stub_for" = "for 0 < i < count"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Call()"; - -/* About */ -"about_version_fmt" = "Version %@ (%@)"; -"about_nav_title" = "About VisuStruct"; -"about_features_heading" = "Features"; -"about_bullet_palette" = "Insert blocks from the palette or drag them into the diagram"; -"about_bullet_edit" = "Edit the chart: change text, select elements, and remove them if needed"; -"about_bullet_tabs" = "Several diagrams in tabs — each with its own title"; -"about_bullet_file" = "Save and reopen diagrams as .visustruct files"; -"about_bullet_share" = "Share the structure chart as an image (PNG)"; -"about_dateiformat_heading" = "File format"; -"about_dateiformat_part1" = "VisuStruct’s native format uses the extension "; -"about_dateiformat_part2" = ". You can keep diagrams and exchange them with other apps that support the same format."; -"about_dateiformat_strk" = "Files with the .strk extension can also be opened."; -"about_project_github_label" = "Java app (source & releases):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Developer:"; -"about_lineage_heading" = "Lineage"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Done"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Cancel insert"; -"phone_show_text_editor" = "Edit text"; -"phone_hide_text_editor" = "Collapse"; -"phone_title_done" = "Done"; - -/* Alerts */ -"err_save_failed_fmt" = "Could not save: %@"; -"err_share_prepare_failed_fmt" = "Could not prepare sharing: %@"; -"err_diagram_too_small_export" = "The diagram is too small — nothing to export."; -"err_image_render_failed" = "Could not create the image."; -"err_diagram_too_small_print" = "The diagram is too small — nothing to print."; -"err_print_unavailable" = "Printing is not available on this device."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulation finished."; -"sim_notice_infinite_loop_empty" = "Infinite loop has no body."; -"sim_notice_unsupported_statement_fmt" = "Unsupported statement: %@"; -"sim_notice_invalid_expression_fmt" = "Invalid expression: %@"; -"sim_notice_unknown_variable_fmt" = "Unknown variable: %@"; -"sim_notice_division_by_zero" = "Division by zero."; -"sim_notice_whole_number_expected" = "Please enter a whole number."; -"sim_notice_number_expected" = "Please enter a number."; -"sim_notice_boolean_expected" = "Please enter true or false."; -"sim_notice_subprogram_not_found_fmt" = "No open diagram is called %@. The call was skipped."; -"sim_notice_call_too_deep" = "Too many nested calls. Does a subprogram call itself?"; - -"codegen_pseudocode_toggle" = "Pseudocode (not runnable code)"; -"codegen_pseudocode_note" = "Pseudocode – not a runnable program"; -"codegen_pseudocode_hint" = "This diagram is marked as pseudocode. The code is only a sketch and cannot be run."; - -"course_title" = "Course"; -"course_intro" = "Learn programming step by step: from the first structure chart to your own Java programs."; -"course_bullet_lessons" = "All lessons with explanations, quizzes and exercises"; -"course_bullet_java" = "“Now in Java” programming tasks in every lesson"; -"course_bullet_games" = "Learning games and step-by-step hints"; -"course_bullet_once" = "Pay once, use it on iPhone, iPad and Mac"; -"course_unlocked" = "Course unlocked – thank you!"; -"course_free_note" = "Creating structure charts, simulation and code export stay free."; -"course_restore" = "Restore purchases"; -"course_restore_done" = "Your course purchase has been restored."; -"course_restore_none" = "No course purchase was found for this Apple Account."; -"course_unavailable" = "The course can’t be loaded from the App Store right now. Please check your internet connection and try again later."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-DSvkBkp-.js b/website/app/assets/Localizable-DSvkBkp-.js deleted file mode 100644 index d381e295..00000000 --- a/website/app/assets/Localizable-DSvkBkp-.js +++ /dev/null @@ -1,153 +0,0 @@ -const e=`/* NOTE: Initial Norwegian Bokmål localization. */ -"palette_header" = "Elementer"; -"inspector_header" = "Objektinspektør"; - -/* Actions */ -"action_share_png" = "Del bilde…"; -"action_share_document" = "Del fil…"; -"action_export_pdf" = "Forhåndsvis PDF…"; -"action_generate_code" = "Generer kode…"; -"action_simulation" = "Simulering"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "Simuleringsmodus kjører diagrammet trinn for trinn, markerer gjeldende element og viser variabelverdier og utdata."; -"simulation_controls" = "Kontroller"; -"simulation_highlight_label" = "Markering"; -"simulation_highlight_last" = "Siste steg"; -"simulation_highlight_next" = "Neste steg"; -"simulation_not_available" = "Play kjører simuleringen automatisk med en pause på 0,5 sekund per steg."; -"simulation_play" = "Spill av"; -"simulation_pause" = "Pause"; -"simulation_step" = "Trinn"; -"simulation_stop" = "Stopp"; -"simulation_input" = "Inndata"; -"simulation_input_placeholder" = "Ingen inndata kreves."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Skriv inn et heltall."; -"simulation_trace" = "Siste steg"; -"simulation_trace_placeholder" = "Ingen trinn er utført ennå."; -"simulation_variables" = "Variabler"; -"simulation_variables_placeholder" = "Ingen variabler ennå."; -"simulation_output" = "Utdata"; -"simulation_output_placeholder" = "Ingen utdata ennå."; -"action_delete_selection" = "Slett markering"; -"ctx_copy" = "Kopier"; -"ctx_cut" = "Klipp ut"; -"ctx_paste" = "Lim inn"; -"action_settings" = "Innstillinger"; -"action_about" = "Om VisuStruct"; -"recent_files_menu" = "Nylige filer"; -"recent_files_empty" = "Ingen nylige filer"; -"recent_files_clear" = "Tøm liste"; -"recent_files_unavailable" = "Filen er ikke lenger tilgjengelig."; - -/* In-app settings */ -"settings_language_title" = "Språk"; - -/* Toolbar */ -"toolbar_undo" = "Angre"; -"toolbar_redo" = "Gjør om"; -"toolbar_new" = "Ny"; -"menu_open" = "Åpne…"; -"menu_save" = "Lagre"; -"menu_save_as" = "Lagre som…"; -"menu_print" = "Skriv ut…"; -"menu_file" = "Arkiv"; -"menu_new" = "Ny"; -"menu_close" = "Lukk"; - -/* Inspector */ -"inspector_no_selection" = "Ingen valgt — trykk på en rute."; -"inspector_apply_text" = "Bruk tekst"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Type"; -"inspector_io_mode_statement" = "Setning"; -"inspector_io_mode_input" = "Inndata"; -"inspector_io_mode_output" = "Utdata"; -"inspector_io_mode_help_java_like" = "Inndata: input: … | Utdata: output: …"; -"inspector_add_case" = "Legg til case"; -"inspector_remove_case" = "Fjern case"; -"inspector_group_element" = "Elementtekst"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Skriv condition…"; -"inspector_placeholder_selector" = "Skriv selector…"; -"inspector_placeholder_call" = "Skriv call…"; -"inspector_placeholder_break" = "Kommentar (valgfritt)"; - -/* Element types */ -"element_anweisung" = "Setning"; -"element_verzweigung" = "Betingelse (if/else)"; -"element_fallauswahl" = "Valg (switch)"; -"element_forSchleife" = "For-løkke"; -"element_whileSchleife" = "While-løkke"; -"element_doUntilSchleife" = "Do-while-løkke"; -"element_endlosschleife" = "Uendelig løkke"; -"element_aussprung" = "Break"; -"element_aufruf" = "Kall"; -"element_leerElement" = "Tomt element"; - -/* Diagram */ -"diagram_title_label" = "Tittel"; -"diagram_title_placeholder" = "Ingen tittel"; -"canvas_empty_title" = "Tomt struktogram"; -"canvas_empty_hint" = "Dra et element fra “Elementer” hit."; - -/* Diagram stubs */ -"canvas_stub_anweisung" = "statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "switch"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Kall()"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Forslag til filnavn"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Med dato"; -"settings_export_filename_title_only" = "Uten dato"; -"settings_export_filename_footer_with_date" = "Med dato: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Uten dato: Filename.visustruct"; - -"close_unsaved_title" = "Ulagrede endringer"; -"close_unsaved_message" = "Dette diagrammet inneholder ulagrede endringer. Vil du lagre dem før du lukker?"; -"close_unsaved_discard" = "Forkast"; -"close_unsaved_cancel" = "Avbryt"; - -/* Accessibility — code generator floating panel */ -"a11y_codegen_panel_drag" = "Dra for å flytte kodegeneratorpanelet"; -"about_dateiformat_strk" = "Filer med endelsen .strk kan også åpnes."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simuleringen er ferdig."; -"sim_notice_infinite_loop_empty" = "Den uendelige løkken har ikke noe innhold."; -"sim_notice_unsupported_statement_fmt" = "Setning støttes ikke: %@"; -"sim_notice_invalid_expression_fmt" = "Ugyldig uttrykk: %@"; -"sim_notice_unknown_variable_fmt" = "Ukjent variabel: %@"; -"sim_notice_division_by_zero" = "Divisjon med null."; -"sim_notice_whole_number_expected" = "Skriv inn et heltall."; -"sim_notice_number_expected" = "Skriv inn et tall."; -"sim_notice_boolean_expected" = "Skriv inn true eller false."; -"sim_notice_subprogram_not_found_fmt" = "Ingen åpne diagrammer heter %@. Kallet ble hoppet over."; -"sim_notice_call_too_deep" = "For mange nøstede kall. Kaller et underprogram seg selv?"; -"phone_hide_text_editor" = "Skjul"; -"phone_title_done" = "Ferdig"; - -"codegen_pseudocode_toggle" = "Pseudokode (ikke kjørbar kode)"; -"codegen_pseudocode_note" = "Pseudokode – ikke et kjørbart program"; -"codegen_pseudocode_hint" = "Dette diagrammet er merket som pseudokode. Koden er bare en skisse og kan ikke kjøres."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-DXAPah1g.js b/website/app/assets/Localizable-DXAPah1g.js deleted file mode 100644 index 0e75f8f3..00000000 --- a/website/app/assets/Localizable-DXAPah1g.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Elementi"; -"inspector_header" = "Ispettore oggetti"; - -/* Actions */ -"action_share_png" = "Condividi immagine…"; -"action_share_document" = "Condividi file…"; -"action_export_pdf" = "Anteprima PDF…"; -"action_generate_code" = "Genera codice…"; -"action_simulation" = "Simulazione"; -"action_diagram" = "Diagramma"; -"simulation_placeholder_body" = "La modalità simulazione esegue il diagramma passo dopo passo, evidenzia l’elemento corrente e mostra i valori delle variabili e l’output."; -"simulation_controls" = "Controlli"; -"simulation_highlight_label" = "Evidenziazione"; -"simulation_highlight_last" = "Ultimo passo"; -"simulation_highlight_next" = "Prossimo passo"; -"simulation_not_available" = "Play esegue automaticamente la simulazione con una pausa di 0,5 secondi per passo."; -"simulation_play" = "Avvia"; -"simulation_pause" = "Pausa"; -"simulation_step" = "Passo"; -"simulation_stop" = "Stop"; -"simulation_input" = "Input"; -"simulation_input_placeholder" = "Nessun input richiesto."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Inserisci un numero intero."; -"simulation_trace" = "Ultimo passo"; -"simulation_trace_placeholder" = "Nessun passo ancora eseguito."; -"simulation_variables" = "Variabili"; -"simulation_variables_placeholder" = "Nessuna variabile per ora."; -"simulation_output" = "Output"; -"simulation_output_placeholder" = "Nessun output per ora."; -"action_delete_selection" = "Elimina selezione"; -"ctx_copy" = "Copia"; -"ctx_cut" = "Taglia"; -"ctx_paste" = "Incolla"; -"action_settings" = "Impostazioni"; -"action_about" = "Informazioni su VisuStruct"; -"recent_files_menu" = "File recenti"; -"recent_files_empty" = "Nessun file recente"; -"recent_files_clear" = "Cancella elenco"; -"recent_files_unavailable" = "Il file non è più disponibile."; - -/* In-app settings */ -"settings_language_title" = "Lingua"; -"settings_language_de" = "Tedesco"; -"settings_language_en" = "Inglese"; -"settings_language_es" = "Spagnolo"; -"settings_language_fr" = "Francese"; -"settings_language_it" = "Italiano"; -"settings_language_pl" = "Polacco"; -"settings_language_tr" = "Turco"; -"settings_language_ko" = "Coreano"; -"settings_language_ja" = "Giapponese"; -"settings_language_el" = "Greco"; -"settings_language_uk" = "Ucraino"; -"settings_language_ar" = "Arabo"; -"settings_language_ru" = "Russo"; -"settings_language_he" = "Ebraico"; -"settings_language_zh_hans" = "Cinese (semplificato)"; -"settings_language_zh_hant" = "Cinese (tradizionale)"; -"settings_language_ptpt" = "Portoghese"; -"settings_language_java" = "Java (parole chiave)"; - - -/* Error overlay */ -"error_title" = "Errore"; -"error_back_editor" = "Torna all’editor"; - -/* Toolbar */ -"toolbar_undo" = "Annulla"; -"toolbar_redo" = "Ripeti"; -"toolbar_new" = "Nuovo"; -"menu_open" = "Apri…"; -"menu_save" = "Salva"; -"menu_save_as" = "Salva con nome…"; -"menu_print" = "Stampa…"; -"menu_file" = "File"; -"menu_new" = "Nuovo"; -"menu_close" = "Chiudi"; - -/* Accessibility */ -"a11y_generate_code" = "Genera codice dal diagramma"; -"a11y_generate_code_locked" = "Genera codice, disponibile dopo l’acquisto"; -"a11y_unsaved_changes" = "Modifiche non salvate"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Genera codice"; -"codegen_gate_body" = "Il codice Java, Python e JavaScript dal diagramma si sbloccherà con un acquisto in-app (in arrivo)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Generatore di codice"; -"codegen_placeholder_body" = "Il codice generato apparirà qui."; -"codegen_placeholder_compare" = "Passa tra Java, Python e JavaScript per confrontare la sintassi (esempio sotto)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condizione) {\\n istruzione();\\n} else {\\n alternativa();\\n}"; -"codegen_preview_python" = "if condizione:\\n istruzione()\\nelse:\\n alternativa()"; -"codegen_copy" = "Copia"; -"codegen_run_in_browser" = "Prova nel browser"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Nuova scheda diagramma"; -"a11y_close_tab" = "Chiudi scheda"; - -"close_unsaved_title" = "Modifiche non salvate"; -"close_unsaved_message" = "Questo diagramma contiene modifiche non salvate. Vuoi salvarle prima di chiudere?"; -"close_unsaved_discard" = "Scarta"; -"close_unsaved_cancel" = "Annulla"; -"a11y_placeholder_drop" = "Rilascia un elemento qui per sostituire il segnaposto. Tieni premuto per trascinare e spostare."; -"a11y_node_drop" = "Tieni premuto un blocco, poi trascina: rilascia sul bordo superiore o inferiore per inserire prima o dopo. Rilascio al centro solo su un segnaposto ø."; -"a11y_codegen_panel_drag" = "Trascina per spostare il pannello del generatore di codice"; - -/* Diagram */ -"diagram_title_label" = "Titolo"; -"diagram_title_placeholder" = "Senza titolo"; - -/* Inspector */ -"inspector_no_selection" = "Nessun elemento selezionato — tocca un riquadro."; -"inspector_apply_text" = "Applica testo"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Tipo"; -"inspector_io_mode_statement" = "Istruzione"; -"inspector_io_mode_input" = "Input"; -"inspector_io_mode_output" = "Output"; -"inspector_io_mode_help_java_like" = "Input: input: … | Output: output: …"; -"inspector_add_case" = "Aggiungi caso"; -"inspector_remove_case" = "Rimuovi caso"; -"inspector_group_element" = "Testo dell’elemento"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Inserisci condizione…"; -"inspector_placeholder_selector" = "Inserisci selettore…"; -"inspector_placeholder_call" = "Inserisci chiamata…"; -"inspector_placeholder_break" = "Commento (opzionale)"; - -/* Element types */ -"element_anweisung" = "Istruzione"; -"element_verzweigung" = "Ramo"; -"element_fallauswahl" = "Selezione"; -"element_forSchleife" = "Ciclo for"; -"element_whileSchleife" = "Ciclo while"; -"element_doUntilSchleife" = "Ciclo do-while"; -"element_endlosschleife" = "Ciclo infinito"; -"element_aussprung" = "Uscita"; -"element_aufruf" = "Chiamata"; -"element_leerElement" = "Elemento vuoto"; - -/* Tabs */ -"tab_title_format" = "Diagramma %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Diagramma vuoto"; -"canvas_empty_hint" = "Trascina un elemento da “Elementi” qui."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Selezione"; -"canvas_stub_anweisung" = "Istruzione"; -"canvas_stub_condition" = "Condizione"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "infinito"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Chiamata()"; - -/* About */ -"about_version_fmt" = "Versione %@ (%@)"; -"about_nav_title" = "Informazioni su VisuStruct"; -"about_features_heading" = "Funzionalità"; -"about_bullet_palette" = "Inserisci blocchi dalla tavolozza o trascinali nel diagramma"; -"about_bullet_edit" = "Modifica il diagramma: cambia il testo, seleziona elementi e rimuovili se necessario"; -"about_bullet_tabs" = "Più diagrammi in schede — ognuno con il proprio titolo"; -"about_bullet_file" = "Salva e riapri diagrammi come file .visustruct"; -"about_bullet_share" = "Condividi il diagramma come immagine (PNG)"; -"about_dateiformat_heading" = "Formato file"; -"about_dateiformat_part1" = "Il formato nativo di VisuStruct usa l’estensione "; -"about_dateiformat_part2" = ". Puoi conservare i diagrammi e scambiarli con altre app che supportano lo stesso formato."; -"about_dateiformat_strk" = "È possibile aprire anche i file con estensione .strk."; -"about_project_github_label" = "App Java (sorgente e release):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Sviluppatore:"; -"about_lineage_heading" = "Origine"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Fine"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Annulla inserimento"; -"phone_show_text_editor" = "Modifica testo"; -"phone_hide_text_editor" = "Comprimi"; -"phone_title_done" = "Fine"; - -/* Alerts */ -"err_save_failed_fmt" = "Impossibile salvare: %@"; -"err_share_prepare_failed_fmt" = "Impossibile preparare la condivisione: %@"; -"err_diagram_too_small_export" = "Il diagramma è troppo piccolo — niente da esportare."; -"err_image_render_failed" = "Impossibile creare l’immagine."; -"err_diagram_too_small_print" = "Il diagramma è troppo piccolo — niente da stampare."; -"err_print_unavailable" = "La stampa non è disponibile su questo dispositivo."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Suggerimento nome file"; -"settings_export_filename_picker" = "Formato"; -"settings_export_filename_date_title" = "Con data"; -"settings_export_filename_title_only" = "Senza data"; -"settings_export_filename_footer_with_date" = "Con data: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Senza data: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulazione terminata."; -"sim_notice_infinite_loop_empty" = "Il ciclo infinito non ha corpo."; -"sim_notice_unsupported_statement_fmt" = "Istruzione non supportata: %@"; -"sim_notice_invalid_expression_fmt" = "Espressione non valida: %@"; -"sim_notice_unknown_variable_fmt" = "Variabile sconosciuta: %@"; -"sim_notice_division_by_zero" = "Divisione per zero."; -"sim_notice_whole_number_expected" = "Inserisci un numero intero."; -"sim_notice_number_expected" = "Inserisci un numero."; -"sim_notice_boolean_expected" = "Inserisci true o false."; -"sim_notice_subprogram_not_found_fmt" = "Nessun diagramma aperto si chiama %@. La chiamata è stata saltata."; -"sim_notice_call_too_deep" = "Troppe chiamate annidate. Un sottoprogramma chiama se stesso?"; - -"codegen_pseudocode_toggle" = "Pseudocodice (codice non eseguibile)"; -"codegen_pseudocode_note" = "Pseudocodice – non è un programma eseguibile"; -"codegen_pseudocode_hint" = "Questo diagramma è contrassegnato come pseudocodice. Il codice è solo una bozza e non può essere eseguito."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-Dk8F86qS.js b/website/app/assets/Localizable-Dk8F86qS.js deleted file mode 100644 index f5ef6ff8..00000000 --- a/website/app/assets/Localizable-Dk8F86qS.js +++ /dev/null @@ -1,255 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Elemente"; -"inspector_header" = "Objektinspektor"; - -/* Aktionen */ -"action_share_png" = "Grafik teilen …"; -"action_share_document" = "Datei teilen …"; -"action_export_pdf" = "PDF-Vorschau …"; -"action_generate_code" = "Code erzeugen …"; -"action_simulation" = "Simulation"; -"action_diagram" = "Diagramm"; -"simulation_placeholder_body" = "Der Simulationsmodus führt das Diagramm Schritt für Schritt aus, markiert das aktuelle Element und zeigt Variablenwerte und Ausgaben."; -"simulation_controls" = "Steuerung"; -"simulation_highlight_label" = "Markierung"; -"simulation_highlight_last" = "Letzter Schritt"; -"simulation_highlight_next" = "Nächster Schritt"; -"simulation_not_available" = "Play führt die Simulation automatisch mit 0,5 Sekunden Pause pro Schritt aus."; -"simulation_play" = "Start"; -"simulation_pause" = "Pause"; -"simulation_step" = "Schritt"; -"simulation_stop" = "Stopp"; -"simulation_input" = "Eingabe"; -"simulation_input_placeholder" = "Keine Eingabe erforderlich."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Bitte eine ganze Zahl eingeben."; -"simulation_trace" = "Letzter Schritt"; -"simulation_trace_placeholder" = "Noch kein Schritt ausgeführt."; -"simulation_variables" = "Variablen"; -"simulation_variables_placeholder" = "Noch keine Variablen."; -"simulation_output" = "Ausgabe"; -"simulation_output_placeholder" = "Noch keine Ausgabe."; -"action_delete_selection" = "Auswahl löschen"; -"ctx_copy" = "Kopieren"; -"ctx_cut" = "Ausschneiden"; -"ctx_paste" = "Einfügen"; -"action_settings" = "Einstellungen"; -"action_about" = "Über VisuStruct"; -"recent_files_menu" = "Zuletzt verwendet"; -"recent_files_empty" = "Keine zuletzt verwendeten Dateien"; -"recent_files_clear" = "Liste leeren"; -"recent_files_unavailable" = "Datei ist nicht mehr verfügbar."; - -/* Einstellungen (In-App) */ -"settings_language_title" = "Sprache"; -"settings_language_de" = "Deutsch"; -"settings_language_en" = "Englisch"; -"settings_language_es" = "Spanisch"; -"settings_language_fr" = "Französisch"; -"settings_language_it" = "Italienisch"; -"settings_language_pl" = "Polnisch"; -"settings_language_tr" = "Türkisch"; -"settings_language_ko" = "Koreanisch"; -"settings_language_ja" = "Japanisch"; -"settings_language_el" = "Griechisch"; -"settings_language_uk" = "Ukrainisch"; -"settings_language_ar" = "Arabisch"; -"settings_language_ru" = "Russisch"; -"settings_language_he" = "Hebräisch"; -"settings_language_zh_hans" = "Chinesisch (vereinfacht)"; -"settings_language_zh_hant" = "Chinesisch (traditionell)"; -"settings_language_ptpt" = "Portugiesisch"; -"settings_language_java" = "Java (Schlüsselwörter)"; - - - -"settings_ui_scale_section" = "Textgröße"; -"settings_ui_scale_canvas" = "Struktogramm"; -"settings_ui_scale_inspector" = "Eingabe unten"; -"settings_ui_scale_small" = "Klein"; -"settings_ui_scale_medium" = "Mittel"; -"settings_ui_scale_large" = "Groß"; -"settings_ui_scale_extra_large" = "Sehr groß"; - -"settings_export_filename_section" = "Dateivorschlag"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Mit Datum"; -"settings_export_filename_title_only" = "Ohne Datum"; -"settings_export_filename_footer_with_date" = "Mit Datum: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Ohne Datum: Filename.visustruct"; - - -/* Fehler-Overlay */ -"error_title" = "Fehler"; -"error_back_editor" = "Zurück zum Editor"; - -/* Toolbar */ -"toolbar_undo" = "Rückgängig"; -"toolbar_redo" = "Wiederholen"; -"toolbar_new" = "Neu"; -"menu_open" = "Öffnen …"; -"menu_save" = "Speichern"; -"menu_save_as" = "Speichern unter …"; -"menu_print" = "Drucken …"; -"menu_file" = "Ablage"; -"menu_new" = "Neu"; -"menu_close" = "Schließen"; - -/* Barrierefreiheit */ -"a11y_generate_code" = "Code aus dem Struktogramm erzeugen"; -"a11y_generate_code_locked" = "Code erzeugen, nach dem Kauf verfügbar"; -"a11y_unsaved_changes" = "Ungespeicherte Änderungen"; - -/* Code-Generator (IAP folgt) */ -"codegen_gate_title" = "Code erzeugen"; -"codegen_gate_body" = "Java-, Python- und JavaScript-Code aus dem Struktogramm wird über einen In-App-Kauf freigeschaltet (demnächst)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Code-Generator"; -"codegen_placeholder_body" = "Hier erscheint später der erzeugte Code."; -"codegen_placeholder_compare" = "Zwischen Java, Python und JavaScript wechseln, um die Syntax zu vergleichen (Beispiel unten)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (bedingung) {\\n anweisung();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if bedingung:\\n anweisung()\\nelse:\\n alternative()"; -"codegen_generated_hint" = "Vorschau aus dem aktuellen Struktogramm (Java 17, Python 3.10+, modernes JavaScript)."; -"codegen_copy" = "Kopieren"; -"codegen_run_in_browser" = "Im Browser testen"; -"codegen_save_java" = "Java speichern …"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Weiteres Struktogramm"; -"a11y_close_tab" = "Reiter schließen"; - -"close_unsaved_title" = "Ungespeicherte Änderungen"; -"close_unsaved_message" = "Dieses Diagramm enthält ungespeicherte Änderungen. Möchtest du sie vor dem Schließen speichern?"; -"close_unsaved_discard" = "Verwerfen"; -"close_unsaved_cancel" = "Abbrechen"; -"a11y_placeholder_drop" = "Element hier ablegen, um den Platzhalter zu ersetzen. Lang drücken und ziehen zum Verschieben."; -"a11y_node_drop" = "Block lang drücken, dann ziehen: am Ziel am oberen oder unteren Rand ablegen (davor/danach). In die Mitte nur auf Platzhalter ø."; -"a11y_codegen_panel_drag" = "Codegenerator-Panel verschieben"; - -/* Diagramm */ -"diagram_title_label" = "Titel"; -"diagram_title_placeholder" = "Ohne Titel"; - -/* Inspector */ -"inspector_no_selection" = "Kein Element gewählt — tippe auf eine Kachel."; -"inspector_apply_text" = "Text übernehmen"; -"inspector_for_init" = "Start"; -"inspector_for_condition" = "Bedingung"; -"inspector_for_step" = "Schritt"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; -"inspector_io_mode_title" = "Art"; -"inspector_io_mode_statement" = "Anweisung"; -"inspector_io_mode_input" = "Eingabe"; -"inspector_io_mode_output" = "Ausgabe"; -"inspector_io_mode_help_java_like" = "Eingabe: input: … | Ausgabe: output: …"; -"inspector_add_case" = "Weiteren Fall hinzufügen"; -"inspector_remove_case" = "Fall entfernen"; -"inspector_group_element" = "Elementtext"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Bedingung einfügen …"; -"inspector_placeholder_selector" = "Auswahl einfügen …"; -"inspector_placeholder_call" = "Aufruf einfügen …"; -"inspector_placeholder_break" = "Kommentar (optional)"; - -/* Elementtypen (Palette + Inspector) */ -"element_anweisung" = "Anweisung"; -"element_verzweigung" = "Verzweigung"; -"element_fallauswahl" = "Fallauswahl"; -"element_forSchleife" = "For-Schleife"; -"element_whileSchleife" = "While-Schleife"; -"element_doUntilSchleife" = "Do-while-Schleife"; -"element_endlosschleife" = "Endlosschleife"; -"element_aussprung" = "Aussprung"; -"element_aufruf" = "Aufruf"; -"element_leerElement" = "Leeres Element"; - -/* Tabs */ -"tab_title_format" = "Diagramm %d"; - -/* Leeres Canvas */ -"canvas_empty_title" = "Leeres Struktogramm"; -"canvas_empty_hint" = "Element aus „Elemente“ hierher ziehen."; - -/* Diagramm: Texte neuer Elemente & Spaltenköpfe (Anzeige) */ -"canvas_stub_fallauswahl" = "Fallauswahl"; -"canvas_stub_anweisung" = "Anweisung"; -"canvas_stub_condition" = "Bedingung"; -"canvas_stub_for" = "for 0 < i < anzahl"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Aufruf()"; - -/* Über */ -"about_version_fmt" = "Version %@ (%@)"; -"about_nav_title" = "Über VisuStruct"; -"about_features_heading" = "Funktionen"; -"about_bullet_palette" = "Bausteine aus der Palette einfügen oder per Drag & Drop ins Diagramm ziehen"; -"about_bullet_edit" = "Struktogramm bearbeiten: Texte anpassen, Elemente auswählen und bei Bedarf entfernen"; -"about_bullet_tabs" = "Mehrere Diagramme in Tabs – jeweils mit eigenem Titel"; -"about_bullet_file" = "Diagramme als .visustruct-Datei speichern und wieder öffnen"; -"about_bullet_share" = "Struktogramm als Grafik teilen (PNG)"; -"about_dateiformat_heading" = "Dateiformat"; -"about_dateiformat_part1" = "Das native Dateiformat von VisuStruct nutzt die Endung "; -"about_dateiformat_part2" = ". So kannst du Diagramme sichern und mit anderen Programmen austauschen, die dasselbe Format unterstützen."; -"about_dateiformat_strk" = "Zusätzlich können Dateien mit der Endung .strk geöffnet werden."; -"about_project_github_label" = "Java-App (Quellen & Releases):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Entwickler:"; -"about_lineage_heading" = "Herkunft"; -"about_lineage_body" = "VisuStruct entstand ursprünglich als Java-Desktop-App und bleibt für den schulischen Einsatz plattformübergreifend verfügbar. Die aktuelle Swift/SwiftUI-Version ist die moderne native App für Mac, iPad und iPhone und bleibt kompatibel mit dem .visustruct-Dateiformat. Sie basiert auf der ursprünglichen Open-Source-Arbeit von Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java-Upstream:"; -"about_done" = "Fertig"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: Einfügen per Tap */ -"phone_insert_cancel" = "Einfügen abbrechen"; -"phone_show_text_editor" = "Text bearbeiten"; -"phone_hide_text_editor" = "Einklappen"; -"phone_title_done" = "Fertig"; - -/* Meldungen */ -"err_save_failed_fmt" = "Speichern fehlgeschlagen: %@"; -"err_share_prepare_failed_fmt" = "Teilen konnte nicht vorbereitet werden: %@"; -"err_diagram_too_small_export" = "Diagramm ist zu klein — nichts zu exportieren."; -"err_image_render_failed" = "Grafik konnte nicht erzeugt werden."; -"err_diagram_too_small_print" = "Diagramm ist zu klein — nichts zu drucken."; -"err_print_unavailable" = "Drucken ist auf diesem Gerät nicht verfügbar."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulation beendet."; -"sim_notice_infinite_loop_empty" = "Die Endlosschleife hat keinen Rumpf."; -"sim_notice_unsupported_statement_fmt" = "Nicht unterstützte Anweisung: %@"; -"sim_notice_invalid_expression_fmt" = "Ungültiger Ausdruck: %@"; -"sim_notice_unknown_variable_fmt" = "Unbekannte Variable: %@"; -"sim_notice_division_by_zero" = "Division durch null."; -"sim_notice_whole_number_expected" = "Bitte eine ganze Zahl eingeben."; -"sim_notice_number_expected" = "Bitte eine Zahl eingeben."; -"sim_notice_boolean_expected" = "Bitte true oder false eingeben."; -"sim_notice_subprogram_not_found_fmt" = "Kein offenes Diagramm heißt %@. Der Aufruf wurde übersprungen."; -"sim_notice_call_too_deep" = "Zu viele verschachtelte Aufrufe. Ruft sich ein Unterprogramm selbst auf?"; - -"codegen_pseudocode_toggle" = "Pseudocode (kein lauffähiger Code)"; -"codegen_pseudocode_note" = "Pseudocode – kein lauffähiges Programm"; -"codegen_pseudocode_hint" = "Dieses Diagramm ist als Pseudocode markiert. Der Code ist nur eine Skizze und lässt sich nicht ausführen."; - -"course_title" = "Kurs"; -"course_intro" = "Programmieren lernen, Schritt für Schritt: vom ersten Struktogramm bis zum eigenen Java-Programm."; -"course_bullet_lessons" = "Alle Lektionen mit Erklärungen, Wissenstests und Übungen"; -"course_bullet_java" = "In jeder Lektion Programmieraufgaben „Jetzt in Java“"; -"course_bullet_games" = "Lernspiele und gestufte Tipps"; -"course_bullet_once" = "Einmal kaufen, auf iPhone, iPad und Mac nutzen"; -"course_unlocked" = "Kurs freigeschaltet – danke!"; -"course_free_note" = "Struktogramme erstellen, Simulation und Code-Export bleiben kostenlos."; -"course_restore" = "Käufe wiederherstellen"; -"course_restore_done" = "Dein Kurs-Kauf wurde wiederhergestellt."; -"course_restore_none" = "Für diesen Apple Account wurde kein Kurs-Kauf gefunden."; -"course_unavailable" = "Der Kurs kann gerade nicht aus dem App Store geladen werden. Bitte prüfe die Internetverbindung und versuche es später noch einmal."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-DqIDp_hh.js b/website/app/assets/Localizable-DqIDp_hh.js deleted file mode 100644 index 7a8b5baa..00000000 --- a/website/app/assets/Localizable-DqIDp_hh.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "元素"; -"inspector_header" = "物件檢閱器"; - -/* Actions */ -"action_share_png" = "分享圖片…"; -"action_share_document" = "分享檔案…"; -"action_export_pdf" = "PDF 預覽…"; -"action_generate_code" = "產生程式碼…"; -"action_simulation" = "模擬"; -"action_diagram" = "圖表"; -"simulation_placeholder_body" = "模擬模式會逐步執行圖表,醒目提示目前元素,並顯示變數值和輸出。"; -"simulation_controls" = "控制"; -"simulation_highlight_label" = "醒目提示"; -"simulation_highlight_last" = "上一步"; -"simulation_highlight_next" = "下一步"; -"simulation_not_available" = "Play 會自動執行模擬,每步暫停 0.5 秒。"; -"simulation_play" = "播放"; -"simulation_pause" = "暫停"; -"simulation_step" = "單步"; -"simulation_stop" = "停止"; -"simulation_input" = "輸入"; -"simulation_input_placeholder" = "不需要輸入。"; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "請輸入一個整數。"; -"simulation_trace" = "上一步"; -"simulation_trace_placeholder" = "尚未執行任何步驟。"; -"simulation_variables" = "變數"; -"simulation_variables_placeholder" = "暫無變數。"; -"simulation_output" = "輸出"; -"simulation_output_placeholder" = "暫無輸出。"; -"action_delete_selection" = "刪除選取項目"; -"ctx_copy" = "複製"; -"ctx_cut" = "剪下"; -"ctx_paste" = "貼上"; -"action_settings" = "設定"; -"action_about" = "關於 VisuStruct"; -"recent_files_menu" = "最近檔案"; -"recent_files_empty" = "沒有最近檔案"; -"recent_files_clear" = "清除列表"; -"recent_files_unavailable" = "檔案已無法使用。"; - -/* In-app settings */ -"settings_language_title" = "語言"; -"settings_language_de" = "德文"; -"settings_language_en" = "英文"; -"settings_language_es" = "西班牙文"; -"settings_language_fr" = "法文"; -"settings_language_it" = "義大利文"; -"settings_language_pl" = "波蘭文"; -"settings_language_tr" = "土耳其文"; -"settings_language_ko" = "韓文"; -"settings_language_ja" = "日文"; -"settings_language_el" = "希臘文"; -"settings_language_uk" = "烏克蘭文"; -"settings_language_ar" = "阿拉伯文"; -"settings_language_ru" = "俄文"; -"settings_language_he" = "希伯來文"; -"settings_language_zh_hans" = "中文(簡體)"; -"settings_language_zh_hant" = "中文(繁體)"; -"settings_language_ptpt" = "葡萄牙文"; -"settings_language_java" = "Java(關鍵字)"; - - -/* Error overlay */ -"error_title" = "錯誤"; -"error_back_editor" = "回到編輯器"; - -/* Toolbar */ -"toolbar_undo" = "復原"; -"toolbar_redo" = "重做"; -"toolbar_new" = "新增"; -"menu_open" = "開啟…"; -"menu_save" = "儲存"; -"menu_save_as" = "另存新檔…"; -"menu_print" = "列印…"; -"menu_file" = "檔案"; -"menu_new" = "新增"; -"menu_close" = "關閉"; - -/* Accessibility */ -"a11y_generate_code" = "從結構圖產生程式碼"; -"a11y_generate_code_locked" = "產生程式碼(購買後可用)"; -"a11y_unsaved_changes" = "未儲存的變更"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "產生程式碼"; -"codegen_gate_body" = "將透過 App 內購買解鎖從圖表產生的 Java、Python 與 JavaScript 程式碼(即將推出)。"; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "程式碼產生器"; -"codegen_placeholder_body" = "產生的程式碼會顯示在這裡。"; -"codegen_placeholder_compare" = "在 Java、Python 與 JavaScript 之間切換以比較語法(下方範例)。"; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (條件) {\\n 陳述式();\\n} else {\\n 替代();\\n}"; -"codegen_preview_python" = "if 條件:\\n 陳述式()\\nelse:\\n 替代()"; -"codegen_copy" = "複製"; -"codegen_run_in_browser" = "在瀏覽器中測試"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "新增圖表分頁"; -"a11y_close_tab" = "關閉分頁"; - -"close_unsaved_title" = "未儲存的變更"; -"close_unsaved_message" = "此圖表包含未儲存的變更。關閉前要儲存嗎?"; -"close_unsaved_discard" = "捨棄"; -"close_unsaved_cancel" = "取消"; -"a11y_placeholder_drop" = "將元素拖放到這裡以取代佔位符。長按可拖曳移動。"; -"a11y_node_drop" = "長按區塊並拖曳:放到目標上緣或下緣以插入到前/後。僅能在 ø 佔位符上置中放下。"; -"a11y_codegen_panel_drag" = "拖移以移動程式碼產生器面板"; - -/* Diagram */ -"diagram_title_label" = "標題"; -"diagram_title_placeholder" = "無標題"; - -/* Inspector */ -"inspector_no_selection" = "未選取任何元素 — 點一下區塊。"; -"inspector_apply_text" = "套用文字"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "類型"; -"inspector_io_mode_statement" = "敘述"; -"inspector_io_mode_input" = "輸入"; -"inspector_io_mode_output" = "輸出"; -"inspector_io_mode_help_java_like" = "輸入: input: … | 輸出: output: …"; -"inspector_add_case" = "新增分支"; -"inspector_remove_case" = "移除分支"; -"inspector_group_element" = "元素文字"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "輸入條件…"; -"inspector_placeholder_selector" = "輸入選擇式…"; -"inspector_placeholder_call" = "輸入呼叫…"; -"inspector_placeholder_break" = "註解(選填)"; - -/* Element types */ -"element_anweisung" = "陳述式"; -"element_verzweigung" = "分支"; -"element_fallauswahl" = "選擇"; -"element_forSchleife" = "for 迴圈"; -"element_whileSchleife" = "while 迴圈"; -"element_doUntilSchleife" = "do-while 迴圈"; -"element_endlosschleife" = "無限迴圈"; -"element_aussprung" = "跳出"; -"element_aufruf" = "呼叫"; -"element_leerElement" = "空元素"; - -/* Tabs */ -"tab_title_format" = "圖表 %d"; - -/* Empty canvas */ -"canvas_empty_title" = "空結構圖"; -"canvas_empty_hint" = "從「元素」拖曳一個區塊到這裡。"; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "選擇"; -"canvas_stub_anweisung" = "陳述式"; -"canvas_stub_condition" = "條件"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "無限"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "呼叫()"; - -/* About */ -"about_version_fmt" = "版本 %@ (%@)"; -"about_nav_title" = "關於 VisuStruct"; -"about_features_heading" = "功能"; -"about_bullet_palette" = "從調色盤插入區塊,或拖曳到圖表"; -"about_bullet_edit" = "編輯圖表:修改文字、選取元素,必要時刪除"; -"about_bullet_tabs" = "多個圖表分頁——每個都有自己的標題"; -"about_bullet_file" = "將圖表儲存並以 .visustruct 檔案重新開啟"; -"about_bullet_share" = "將結構圖分享為圖片(PNG)"; -"about_dateiformat_heading" = "檔案格式"; -"about_dateiformat_part1" = "VisuStruct 的原生格式使用副檔名 "; -"about_dateiformat_part2" = "。你可以保存圖表,並與支援相同格式的其他 App 交換。"; -"about_dateiformat_strk" = "也可以開啟 .strk 副檔名的檔案。"; -"about_project_github_label" = "Java App(原始碼與版本):"; -"about_web_label" = "網站:"; -"about_developer_label" = "開發者:"; -"about_lineage_heading" = "沿革"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java 上游:"; -"about_done" = "完成"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "取消插入"; -"phone_show_text_editor" = "編輯文字"; -"phone_hide_text_editor" = "收合"; -"phone_title_done" = "完成"; - -/* Alerts */ -"err_save_failed_fmt" = "無法儲存:%@"; -"err_share_prepare_failed_fmt" = "無法準備分享:%@"; -"err_diagram_too_small_export" = "圖表太小——沒有可匯出的內容。"; -"err_image_render_failed" = "無法建立圖片。"; -"err_diagram_too_small_print" = "圖表太小——沒有可列印的內容。"; -"err_print_unavailable" = "此裝置不支援列印。"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "檔名建議"; -"settings_export_filename_picker" = "格式"; -"settings_export_filename_date_title" = "含日期"; -"settings_export_filename_title_only" = "不含日期"; -"settings_export_filename_footer_with_date" = "含日期: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "不含日期: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "模擬已結束。"; -"sim_notice_infinite_loop_empty" = "無限迴圈沒有迴圈主體。"; -"sim_notice_unsupported_statement_fmt" = "不支援的敘述:%@"; -"sim_notice_invalid_expression_fmt" = "無效的運算式:%@"; -"sim_notice_unknown_variable_fmt" = "未知變數:%@"; -"sim_notice_division_by_zero" = "除數為零。"; -"sim_notice_whole_number_expected" = "請輸入整數。"; -"sim_notice_number_expected" = "請輸入數字。"; -"sim_notice_boolean_expected" = "請輸入 true 或 false。"; -"sim_notice_subprogram_not_found_fmt" = "沒有名為 %@ 的已開啟圖表。已略過該呼叫。"; -"sim_notice_call_too_deep" = "巢狀呼叫過多。是否有子程式呼叫了自己?"; - -"codegen_pseudocode_toggle" = "虛擬碼(無法執行的程式碼)"; -"codegen_pseudocode_note" = "虛擬碼 – 不是可執行的程式"; -"codegen_pseudocode_hint" = "此圖已標示為虛擬碼。程式碼只是草稿,無法執行。"; -`;export{e as default}; diff --git a/website/app/assets/Localizable-LSTVB2Q5.js b/website/app/assets/Localizable-LSTVB2Q5.js deleted file mode 100644 index 1ac68831..00000000 --- a/website/app/assets/Localizable-LSTVB2Q5.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Elementos"; -"inspector_header" = "Inspector de objetos"; - -/* Actions */ -"action_share_png" = "Compartir imagen…"; -"action_share_document" = "Compartir archivo…"; -"action_export_pdf" = "Vista previa PDF…"; -"action_generate_code" = "Generar código…"; -"action_simulation" = "Simulación"; -"action_diagram" = "Diagrama"; -"simulation_placeholder_body" = "El modo de simulación ejecuta el diagrama paso a paso, resalta el elemento actual y muestra los valores de variables y las salidas."; -"simulation_controls" = "Controles"; -"simulation_highlight_label" = "Resaltado"; -"simulation_highlight_last" = "Último paso"; -"simulation_highlight_next" = "Siguiente paso"; -"simulation_not_available" = "Play ejecuta la simulación automáticamente con una pausa de 0,5 segundos por paso."; -"simulation_play" = "Reproducir"; -"simulation_pause" = "Pausa"; -"simulation_step" = "Paso"; -"simulation_stop" = "Detener"; -"simulation_input" = "Entrada"; -"simulation_input_placeholder" = "No se requiere entrada."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Introduce un número entero."; -"simulation_trace" = "Último paso"; -"simulation_trace_placeholder" = "Aún no se ha ejecutado ningún paso."; -"simulation_variables" = "Variables"; -"simulation_variables_placeholder" = "Aún no hay variables."; -"simulation_output" = "Salida"; -"simulation_output_placeholder" = "Aún no hay salida."; -"action_delete_selection" = "Eliminar selección"; -"ctx_copy" = "Copiar"; -"ctx_cut" = "Cortar"; -"ctx_paste" = "Pegar"; -"action_settings" = "Ajustes"; -"action_about" = "Acerca de VisuStruct"; -"recent_files_menu" = "Recientes"; -"recent_files_empty" = "No hay archivos recientes"; -"recent_files_clear" = "Borrar lista"; -"recent_files_unavailable" = "El archivo ya no está disponible."; - -/* In-app settings */ -"settings_language_title" = "Idioma"; -"settings_language_de" = "Alemán"; -"settings_language_en" = "Inglés"; -"settings_language_es" = "Español"; -"settings_language_fr" = "Francés"; -"settings_language_it" = "Italiano"; -"settings_language_pl" = "Polaco"; -"settings_language_tr" = "Turco"; -"settings_language_ko" = "Coreano"; -"settings_language_ja" = "Japonés"; -"settings_language_el" = "Griego"; -"settings_language_uk" = "Ucraniano"; -"settings_language_ar" = "Árabe"; -"settings_language_ru" = "Ruso"; -"settings_language_he" = "Hebreo"; -"settings_language_zh_hans" = "Chino (simplificado)"; -"settings_language_zh_hant" = "Chino (tradicional)"; -"settings_language_ptpt" = "Portugués"; -"settings_language_java" = "Java (palabras clave)"; - - -/* Error overlay */ -"error_title" = "Error"; -"error_back_editor" = "Volver al editor"; - -/* Toolbar */ -"toolbar_undo" = "Deshacer"; -"toolbar_redo" = "Rehacer"; -"toolbar_new" = "Nuevo"; -"menu_open" = "Abrir…"; -"menu_save" = "Guardar"; -"menu_save_as" = "Guardar como…"; -"menu_print" = "Imprimir…"; -"menu_file" = "Archivo"; -"menu_new" = "Nuevo"; -"menu_close" = "Cerrar"; - -/* Accessibility */ -"a11y_generate_code" = "Generar código a partir del diagrama"; -"a11y_generate_code_locked" = "Generar código, disponible tras la compra"; -"a11y_unsaved_changes" = "Cambios sin guardar"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Generar código"; -"codegen_gate_body" = "El código Java, Python y JavaScript a partir de tu diagrama estará disponible mediante una compra integrada (próximamente)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Generador de código"; -"codegen_placeholder_body" = "Aquí aparecerá el código generado."; -"codegen_placeholder_compare" = "Cambia entre Java, Python y JavaScript para comparar la sintaxis (ejemplo abajo)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condición) {\\n instrucción();\\n} else {\\n alternativa();\\n}"; -"codegen_preview_python" = "if condición:\\n instrucción()\\nelse:\\n alternativa()"; -"codegen_copy" = "Copiar"; -"codegen_run_in_browser" = "Probar en el navegador"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Nueva pestaña de diagrama"; -"a11y_close_tab" = "Cerrar pestaña"; - -"close_unsaved_title" = "Cambios sin guardar"; -"close_unsaved_message" = "Este diagrama contiene cambios sin guardar. ¿Quieres guardarlos antes de cerrar?"; -"close_unsaved_discard" = "Descartar"; -"close_unsaved_cancel" = "Cancelar"; -"a11y_placeholder_drop" = "Suelta un elemento aquí para reemplazar el marcador. Mantén pulsado para arrastrar y mover."; -"a11y_node_drop" = "Mantén pulsado un bloque y arrastra: suelta en el borde superior o inferior para insertar antes o después. Suelta en el centro solo sobre un marcador ø."; -"a11y_codegen_panel_drag" = "Arrastra para mover el panel del generador de código"; - -/* Diagram */ -"diagram_title_label" = "Título"; -"diagram_title_placeholder" = "Sin título"; - -/* Inspector */ -"inspector_no_selection" = "Ningún elemento seleccionado — toca una pieza."; -"inspector_apply_text" = "Aplicar texto"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Tipo"; -"inspector_io_mode_statement" = "Instrucción"; -"inspector_io_mode_input" = "Entrada"; -"inspector_io_mode_output" = "Salida"; -"inspector_io_mode_help_java_like" = "Entrada: input: … | Salida: output: …"; -"inspector_add_case" = "Añadir caso"; -"inspector_remove_case" = "Eliminar caso"; -"inspector_group_element" = "Texto del elemento"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Insertar condición…"; -"inspector_placeholder_selector" = "Insertar selector…"; -"inspector_placeholder_call" = "Insertar llamada…"; -"inspector_placeholder_break" = "Comentario (opcional)"; - -/* Element types */ -"element_anweisung" = "Instrucción"; -"element_verzweigung" = "Decisión"; -"element_fallauswahl" = "Selección"; -"element_forSchleife" = "Bucle for"; -"element_whileSchleife" = "Bucle while"; -"element_doUntilSchleife" = "Bucle do-while"; -"element_endlosschleife" = "Bucle infinito"; -"element_aussprung" = "Salida"; -"element_aufruf" = "Llamada"; -"element_leerElement" = "Elemento vacío"; - -/* Tabs */ -"tab_title_format" = "Diagrama %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Diagrama vacío"; -"canvas_empty_hint" = "Arrastra un elemento de «Elementos» aquí."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Selección"; -"canvas_stub_anweisung" = "Instrucción"; -"canvas_stub_condition" = "Condición"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "infinito"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Llamada()"; - -/* About */ -"about_version_fmt" = "Versión %@ (%@)"; -"about_nav_title" = "Acerca de VisuStruct"; -"about_features_heading" = "Funciones"; -"about_bullet_palette" = "Insertar bloques desde la paleta o arrastrarlos al diagrama"; -"about_bullet_edit" = "Editar el diagrama: cambiar texto, seleccionar elementos y eliminarlos si es necesario"; -"about_bullet_tabs" = "Varios diagramas en pestañas — cada uno con su propio título"; -"about_bullet_file" = "Guardar y volver a abrir diagramas como archivos .visustruct"; -"about_bullet_share" = "Compartir el diagrama como imagen (PNG)"; -"about_dateiformat_heading" = "Formato de archivo"; -"about_dateiformat_part1" = "El formato nativo de VisuStruct usa la extensión "; -"about_dateiformat_part2" = ". Puedes guardar diagramas e intercambiarlos con otras apps compatibles."; -"about_dateiformat_strk" = "También se pueden abrir archivos con la extensión .strk."; -"about_project_github_label" = "App Java (código y versiones):"; -"about_web_label" = "Web:"; -"about_developer_label" = "Desarrollador:"; -"about_lineage_heading" = "Origen"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Hecho"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Cancelar inserción"; -"phone_show_text_editor" = "Editar texto"; -"phone_hide_text_editor" = "Contraer"; -"phone_title_done" = "Listo"; - -/* Alerts */ -"err_save_failed_fmt" = "No se pudo guardar: %@"; -"err_share_prepare_failed_fmt" = "No se pudo preparar para compartir: %@"; -"err_diagram_too_small_export" = "El diagrama es demasiado pequeño — nada que exportar."; -"err_image_render_failed" = "No se pudo crear la imagen."; -"err_diagram_too_small_print" = "El diagrama es demasiado pequeño — nada que imprimir."; -"err_print_unavailable" = "La impresión no está disponible en este dispositivo."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Sugerencia de nombre de archivo"; -"settings_export_filename_picker" = "Formato"; -"settings_export_filename_date_title" = "Con fecha"; -"settings_export_filename_title_only" = "Sin fecha"; -"settings_export_filename_footer_with_date" = "Con fecha: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Sin fecha: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simulación terminada."; -"sim_notice_infinite_loop_empty" = "El bucle infinito no tiene cuerpo."; -"sim_notice_unsupported_statement_fmt" = "Instrucción no admitida: %@"; -"sim_notice_invalid_expression_fmt" = "Expresión no válida: %@"; -"sim_notice_unknown_variable_fmt" = "Variable desconocida: %@"; -"sim_notice_division_by_zero" = "División por cero."; -"sim_notice_whole_number_expected" = "Introduce un número entero."; -"sim_notice_number_expected" = "Introduce un número."; -"sim_notice_boolean_expected" = "Introduce true o false."; -"sim_notice_subprogram_not_found_fmt" = "Ningún diagrama abierto se llama %@. Se omitió la llamada."; -"sim_notice_call_too_deep" = "Demasiadas llamadas anidadas. ¿Un subprograma se llama a sí mismo?"; - -"codegen_pseudocode_toggle" = "Pseudocódigo (código no ejecutable)"; -"codegen_pseudocode_note" = "Pseudocódigo – no es un programa ejecutable"; -"codegen_pseudocode_hint" = "Este diagrama está marcado como pseudocódigo. El código es solo un esbozo y no se puede ejecutar."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-M33nNbEe.js b/website/app/assets/Localizable-M33nNbEe.js deleted file mode 100644 index ca1fdc43..00000000 --- a/website/app/assets/Localizable-M33nNbEe.js +++ /dev/null @@ -1,153 +0,0 @@ -const e=`/* NOTE: Initial Swedish localization. */ -"palette_header" = "Element"; -"inspector_header" = "Objektinspektör"; - -/* Actions */ -"action_share_png" = "Dela bild…"; -"action_share_document" = "Dela fil…"; -"action_export_pdf" = "Förhandsvisa PDF…"; -"action_generate_code" = "Generera kod…"; -"action_simulation" = "Simulering"; -"action_diagram" = "Diagram"; -"simulation_placeholder_body" = "Simuleringsläget kör diagrammet steg för steg, markerar det aktuella elementet och visar variabelvärden och utdata."; -"simulation_controls" = "Kontroller"; -"simulation_highlight_label" = "Markering"; -"simulation_highlight_last" = "Senaste steg"; -"simulation_highlight_next" = "Nästa steg"; -"simulation_not_available" = "Play kör simuleringen automatiskt med 0,5 sekunders paus per steg."; -"simulation_play" = "Spela"; -"simulation_pause" = "Paus"; -"simulation_step" = "Steg"; -"simulation_stop" = "Stopp"; -"simulation_input" = "Inmatning"; -"simulation_input_placeholder" = "Ingen inmatning krävs."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Ange ett heltal."; -"simulation_trace" = "Senaste steg"; -"simulation_trace_placeholder" = "Inget steg har körts ännu."; -"simulation_variables" = "Variabler"; -"simulation_variables_placeholder" = "Inga variabler ännu."; -"simulation_output" = "Utdata"; -"simulation_output_placeholder" = "Ingen utmatning ännu."; -"action_delete_selection" = "Radera markering"; -"ctx_copy" = "Kopiera"; -"ctx_cut" = "Klipp ut"; -"ctx_paste" = "Klistra in"; -"action_settings" = "Inställningar"; -"action_about" = "Om VisuStruct"; -"recent_files_menu" = "Senaste filer"; -"recent_files_empty" = "Inga senaste filer"; -"recent_files_clear" = "Rensa lista"; -"recent_files_unavailable" = "Filen är inte längre tillgänglig."; - -/* In-app settings */ -"settings_language_title" = "Språk"; - -/* Toolbar */ -"toolbar_undo" = "Ångra"; -"toolbar_redo" = "Gör om"; -"toolbar_new" = "Ny"; -"menu_open" = "Öppna…"; -"menu_save" = "Spara"; -"menu_save_as" = "Spara som…"; -"menu_print" = "Skriv ut…"; -"menu_file" = "Arkiv"; -"menu_new" = "Ny"; -"menu_close" = "Stäng"; - -/* Inspector */ -"inspector_no_selection" = "Ingen markering — tryck på en ruta."; -"inspector_apply_text" = "Tillämpa text"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Typ"; -"inspector_io_mode_statement" = "Sats"; -"inspector_io_mode_input" = "Inmatning"; -"inspector_io_mode_output" = "Utmatning"; -"inspector_io_mode_help_java_like" = "Inmatning: input: … | Utmatning: output: …"; -"inspector_add_case" = "Lägg till case"; -"inspector_remove_case" = "Ta bort case"; -"inspector_group_element" = "Elementtext"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Skriv condition…"; -"inspector_placeholder_selector" = "Skriv selector…"; -"inspector_placeholder_call" = "Skriv call…"; -"inspector_placeholder_break" = "Kommentar (valfritt)"; - -/* Element types */ -"element_anweisung" = "Sats"; -"element_verzweigung" = "Villkor (if/else)"; -"element_fallauswahl" = "Val (switch)"; -"element_forSchleife" = "For-loop"; -"element_whileSchleife" = "While-loop"; -"element_doUntilSchleife" = "Do-while-loop"; -"element_endlosschleife" = "Oändlig loop"; -"element_aussprung" = "Break"; -"element_aufruf" = "Anrop"; -"element_leerElement" = "Tomt element"; - -/* Diagram */ -"diagram_title_label" = "Titel"; -"diagram_title_placeholder" = "Ingen titel"; -"canvas_empty_title" = "Tomt struktogram"; -"canvas_empty_hint" = "Dra ett element från “Element” hit."; - -/* Diagram stubs */ -"canvas_stub_anweisung" = "statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "switch"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "endless"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Anrop()"; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Förslag på filnamn"; -"settings_export_filename_picker" = "Format"; -"settings_export_filename_date_title" = "Med datum"; -"settings_export_filename_title_only" = "Utan datum"; -"settings_export_filename_footer_with_date" = "Med datum: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Utan datum: Filename.visustruct"; - -"close_unsaved_title" = "Osparade ändringar"; -"close_unsaved_message" = "Det här diagrammet innehåller osparade ändringar. Vill du spara dem innan du stänger?"; -"close_unsaved_discard" = "Kasta"; -"close_unsaved_cancel" = "Avbryt"; - -/* Accessibility — code generator floating panel */ -"a11y_codegen_panel_drag" = "Dra för att flytta kodgeneratorpanelen"; -"about_dateiformat_strk" = "Filer med tillägget .strk kan också öppnas."; - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Simuleringen är klar."; -"sim_notice_infinite_loop_empty" = "Den oändliga loopen saknar innehåll."; -"sim_notice_unsupported_statement_fmt" = "Sats som inte stöds: %@"; -"sim_notice_invalid_expression_fmt" = "Ogiltigt uttryck: %@"; -"sim_notice_unknown_variable_fmt" = "Okänd variabel: %@"; -"sim_notice_division_by_zero" = "Division med noll."; -"sim_notice_whole_number_expected" = "Ange ett heltal."; -"sim_notice_number_expected" = "Ange ett tal."; -"sim_notice_boolean_expected" = "Ange true eller false."; -"sim_notice_subprogram_not_found_fmt" = "Inget öppet diagram heter %@. Anropet hoppades över."; -"sim_notice_call_too_deep" = "För många nästlade anrop. Anropar ett underprogram sig självt?"; -"phone_hide_text_editor" = "Fäll ihop"; -"phone_title_done" = "Klar"; - -"codegen_pseudocode_toggle" = "Pseudokod (inte körbar kod)"; -"codegen_pseudocode_note" = "Pseudokod – inte ett körbart program"; -"codegen_pseudocode_hint" = "Det här diagrammet är markerat som pseudokod. Koden är bara en skiss och kan inte köras."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-R-16GCrk.js b/website/app/assets/Localizable-R-16GCrk.js deleted file mode 100644 index 2d8f645d..00000000 --- a/website/app/assets/Localizable-R-16GCrk.js +++ /dev/null @@ -1,241 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "רכיבים"; -"inspector_header" = "מפקח אובייקטים"; - -/* Actions */ -"action_share_png" = "שיתוף תמונה…"; -"action_share_document" = "שיתוף קובץ…"; -"action_export_pdf" = "תצוגה מקדימה של PDF…"; -"action_generate_code" = "יצירת קוד…"; -"action_simulation" = "סימולציה"; -"action_diagram" = "תרשים"; -"simulation_placeholder_body" = "מצב הסימולציה מריץ את התרשים צעד אחר צעד, מדגיש את הרכיב הנוכחי ומציג ערכי משתנים ופלט."; -"simulation_controls" = "פקדים"; -"simulation_highlight_label" = "הדגשה"; -"simulation_highlight_last" = "הצעד האחרון"; -"simulation_highlight_next" = "הצעד הבא"; -"simulation_not_available" = "Play מריץ את הסימולציה אוטומטית עם השהיה של 0.5 שניות לכל צעד."; -"simulation_play" = "הפעלה"; -"simulation_pause" = "השהיה"; -"simulation_step" = "צעד"; -"simulation_stop" = "עצירה"; -"simulation_input" = "קלט"; -"simulation_input_placeholder" = "לא נדרש קלט."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "הזינו מספר שלם."; -"simulation_trace" = "הצעד האחרון"; -"simulation_trace_placeholder" = "עדיין לא בוצע אף צעד."; -"simulation_variables" = "משתנים"; -"simulation_variables_placeholder" = "עדיין אין משתנים."; -"simulation_output" = "פלט"; -"simulation_output_placeholder" = "עדיין אין פלט."; -"action_delete_selection" = "מחיקת בחירה"; -"ctx_copy" = "העתק"; -"ctx_cut" = "גזור"; -"ctx_paste" = "הדבק"; -"action_settings" = "הגדרות"; -"action_about" = "אודות VisuStruct"; -"recent_files_menu" = "קבצים אחרונים"; -"recent_files_empty" = "אין קבצים אחרונים"; -"recent_files_clear" = "נקה רשימה"; -"recent_files_unavailable" = "הקובץ אינו זמין עוד."; - -/* In-app settings */ -"settings_language_title" = "שפה"; -"settings_language_de" = "גרמנית"; -"settings_language_en" = "אנגלית"; -"settings_language_es" = "ספרדית"; -"settings_language_fr" = "צרפתית"; -"settings_language_it" = "איטלקית"; -"settings_language_pl" = "פולנית"; -"settings_language_tr" = "טורקית"; -"settings_language_ko" = "קוריאנית"; -"settings_language_ja" = "יפנית"; -"settings_language_el" = "יוונית"; -"settings_language_uk" = "אוקראינית"; -"settings_language_ar" = "ערבית"; -"settings_language_ru" = "רוסית"; -"settings_language_he" = "עברית"; -"settings_language_zh_hans" = "סינית (מפושטת)"; -"settings_language_zh_hant" = "סינית (מסורתית)"; -"settings_language_ptpt" = "פורטוגזית"; -"settings_language_java" = "Java (מילות מפתח)"; - - -/* Error overlay */ -"error_title" = "שגיאה"; -"error_back_editor" = "חזרה לעורך"; - -/* Toolbar */ -"toolbar_undo" = "בטל"; -"toolbar_redo" = "בצע שוב"; -"toolbar_new" = "חדש"; -"menu_open" = "פתח…"; -"menu_save" = "שמור"; -"menu_save_as" = "שמור בשם…"; -"menu_print" = "הדפס…"; -"menu_file" = "קובץ"; -"menu_new" = "חדש"; -"menu_close" = "סגור"; - -/* Accessibility */ -"a11y_generate_code" = "יצירת קוד מהתרשים"; -"a11y_generate_code_locked" = "יצירת קוד, זמין לאחר רכישה"; -"a11y_unsaved_changes" = "שינויים שלא נשמרו"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "יצירת קוד"; -"codegen_gate_body" = "קוד Java, Python ו‑JavaScript מהתרשים ייפתח באמצעות רכישה בתוך האפליקציה (בקרוב)."; -"codegen_gate_ok" = "אישור"; -"codegen_placeholder_title" = "מחולל קוד"; -"codegen_placeholder_body" = "הקוד שייווצר יופיע כאן."; -"codegen_placeholder_compare" = "עברו בין Java, Python ו‑JavaScript להשוואת תחביר (דוגמה למטה)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (condition) {\\n statement();\\n} else {\\n alternative();\\n}"; -"codegen_preview_python" = "if condition:\\n statement()\\nelse:\\n alternative()"; -"codegen_copy" = "העתק"; -"codegen_run_in_browser" = "בדיקה בדפדפן"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "כרטיסיית תרשים חדשה"; -"a11y_close_tab" = "סגור כרטיסייה"; - -"close_unsaved_title" = "שינויים שלא נשמרו"; -"close_unsaved_message" = "בתרשים זה יש שינויים שלא נשמרו. לשמור אותם לפני הסגירה?"; -"close_unsaved_discard" = "התעלם"; -"close_unsaved_cancel" = "ביטול"; -"a11y_placeholder_drop" = "שחררו רכיב כאן כדי להחליף את מציין המקום. לחצו והחזיקו כדי לגרור ולהזיז."; -"a11y_node_drop" = "לחצו והחזיקו בלוק ואז גררו: שחררו על הקצה העליון או התחתון כדי להוסיף לפני או אחרי. שחרור במרכז רק על מציין מקום ø."; -"a11y_codegen_panel_drag" = "גרור כדי להזיז את לוח מחולל הקוד"; - -/* Diagram */ -"diagram_title_label" = "כותרת"; -"diagram_title_placeholder" = "ללא כותרת"; - -/* Inspector */ -"inspector_no_selection" = "לא נבחר רכיב — הקישו על אריח."; -"inspector_apply_text" = "החל טקסט"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "סוג"; -"inspector_io_mode_statement" = "הוראה"; -"inspector_io_mode_input" = "קלט"; -"inspector_io_mode_output" = "פלט"; -"inspector_io_mode_help_java_like" = "קלט: input: … | פלט: output: …"; -"inspector_add_case" = "הוסף מקרה"; -"inspector_remove_case" = "הסר מקרה"; -"inspector_group_element" = "טקסט רכיב"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "הוסף condition…"; -"inspector_placeholder_selector" = "הוסף selector…"; -"inspector_placeholder_call" = "הוסף call…"; -"inspector_placeholder_break" = "הערה (רשות)"; - -/* Element types — keep Java-like (LTR) to reduce RTL confusion */ -"element_anweisung" = "Statement"; -"element_verzweigung" = "if"; -"element_fallauswahl" = "switch"; -"element_forSchleife" = "for"; -"element_whileSchleife" = "while"; -"element_doUntilSchleife" = "do"; -"element_endlosschleife" = "while(true)"; -"element_aussprung" = "break"; -"element_aufruf" = "קריאה"; -"element_leerElement" = "ø"; - -/* Tabs */ -"tab_title_format" = "תרשים %d"; - -/* Empty canvas */ -"canvas_empty_title" = "תרשים ריק"; -"canvas_empty_hint" = "גררו בלוק מתוך „רכיבים“ לכאן."; - -/* Diagram: new element stubs & column headers — keep Java-like */ -"canvas_stub_anweisung" = "Statement"; -"canvas_stub_condition" = "condition"; -"canvas_stub_fallauswahl" = "selector"; -"canvas_stub_for" = "i = 0; i < n; i++"; -"canvas_stub_while" = "condition"; -"canvas_stub_until" = "condition"; -"canvas_stub_endless" = "∞"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "קריאה()"; - -/* About */ -"about_version_fmt" = "גרסה %@ (%@)"; -"about_nav_title" = "אודות VisuStruct"; -"about_features_heading" = "תכונות"; -"about_bullet_palette" = "הוספת בלוקים מהפלטה או גרירה אל התרשים"; -"about_bullet_edit" = "עריכת התרשים: שינוי טקסט, בחירת רכיבים ומחיקה לפי הצורך"; -"about_bullet_tabs" = "כמה תרשימים בכרטיסיות — לכל אחד כותרת משלו"; -"about_bullet_file" = "שמירה ופתיחה של תרשימים כקבצי ‎.visustruct"; -"about_bullet_share" = "שיתוף התרשים כתמונה (PNG)"; -"about_dateiformat_heading" = "פורמט קובץ"; -"about_dateiformat_part1" = "הפורמט המקורי של VisuStruct משתמש בסיומת "; -"about_dateiformat_part2" = ". ניתן לשמור תרשימים ולהחליף אותם עם אפליקציות אחרות שתומכות באותו פורמט."; -"about_dateiformat_strk" = "ניתן לפתוח גם קבצים עם הסיומת .strk."; -"about_project_github_label" = "אפליקציית Java (מקור וגרסאות):"; -"about_web_label" = "אתר:"; -"about_developer_label" = "מפתח:"; -"about_lineage_heading" = "מקור"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "סיום"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "בטל הוספה"; -"phone_show_text_editor" = "עריכת טקסט"; -"phone_hide_text_editor" = "כיווץ"; -"phone_title_done" = "סיום"; - -/* Alerts */ -"err_save_failed_fmt" = "לא ניתן לשמור: %@"; -"err_share_prepare_failed_fmt" = "לא ניתן להכין שיתוף: %@"; -"err_diagram_too_small_export" = "התרשים קטן מדי — אין מה לייצא."; -"err_image_render_failed" = "לא ניתן ליצור תמונה."; -"err_diagram_too_small_print" = "התרשים קטן מדי — אין מה להדפיס."; -"err_print_unavailable" = "הדפסה אינה זמינה במכשיר זה."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "הצעת שם קובץ"; -"settings_export_filename_picker" = "פורמט"; -"settings_export_filename_date_title" = "עם תאריך"; -"settings_export_filename_title_only" = "בלי תאריך"; -"settings_export_filename_footer_with_date" = "עם תאריך: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "בלי תאריך: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "הסימולציה הסתיימה."; -"sim_notice_infinite_loop_empty" = "ללולאה האינסופית אין גוף."; -"sim_notice_unsupported_statement_fmt" = "הוראה לא נתמכת: %@"; -"sim_notice_invalid_expression_fmt" = "ביטוי לא חוקי: %@"; -"sim_notice_unknown_variable_fmt" = "משתנה לא ידוע: %@"; -"sim_notice_division_by_zero" = "חלוקה באפס."; -"sim_notice_whole_number_expected" = "יש להזין מספר שלם."; -"sim_notice_number_expected" = "יש להזין מספר."; -"sim_notice_boolean_expected" = "הזן true או false."; -"sim_notice_subprogram_not_found_fmt" = "אין תרשים פתוח בשם %@. הקריאה דולגה."; -"sim_notice_call_too_deep" = "יותר מדי קריאות מקוננות. האם תת־תוכנית קוראת לעצמה?"; - -"codegen_pseudocode_toggle" = "פסאודו-קוד (קוד שאינו ניתן להרצה)"; -"codegen_pseudocode_note" = "פסאודו-קוד – לא תוכנית שניתן להריץ"; -"codegen_pseudocode_hint" = "תרשים זה מסומן כפסאודו-קוד. הקוד הוא רק טיוטה ולא ניתן להריץ אותו."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-UGog_aku.js b/website/app/assets/Localizable-UGog_aku.js deleted file mode 100644 index 0caa0638..00000000 --- a/website/app/assets/Localizable-UGog_aku.js +++ /dev/null @@ -1,238 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Елементи"; -"inspector_header" = "Інспектор об’єктів"; - -/* Actions */ -"action_share_png" = "Поділитися зображенням…"; -"action_share_document" = "Поділитися файлом…"; -"action_export_pdf" = "Перегляд PDF…"; -"action_generate_code" = "Згенерувати код…"; -"action_simulation" = "Симуляція"; -"action_diagram" = "Діаграма"; -"simulation_placeholder_body" = "Режим симуляції виконує діаграму крок за кроком, підсвічує поточний елемент і показує значення змінних та вивід."; -"simulation_controls" = "Керування"; -"simulation_highlight_label" = "Підсвічування"; -"simulation_highlight_last" = "Останній крок"; -"simulation_highlight_next" = "Наступний крок"; -"simulation_not_available" = "Play автоматично виконує симуляцію з паузою 0,5 секунди на крок."; -"simulation_play" = "Відтворити"; -"simulation_pause" = "Пауза"; -"simulation_step" = "Крок"; -"simulation_stop" = "Стоп"; -"simulation_input" = "Ввід"; -"simulation_input_placeholder" = "Ввід не потрібен."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Введіть ціле число."; -"simulation_trace" = "Останній крок"; -"simulation_trace_placeholder" = "Ще не виконано жодного кроку."; -"simulation_variables" = "Змінні"; -"simulation_variables_placeholder" = "Змінних поки немає."; -"simulation_output" = "Вивід"; -"simulation_output_placeholder" = "Виведення поки немає."; -"action_delete_selection" = "Видалити вибране"; -"ctx_copy" = "Копіювати"; -"ctx_cut" = "Вирізати"; -"ctx_paste" = "Вставити"; -"action_settings" = "Налаштування"; -"action_about" = "Про VisuStruct"; -"recent_files_menu" = "Останні файли"; -"recent_files_empty" = "Немає останніх файлів"; -"recent_files_clear" = "Очистити список"; -"recent_files_unavailable" = "Файл більше недоступний."; - -/* In-app settings */ -"settings_language_title" = "Мова"; -"settings_language_de" = "Німецька"; -"settings_language_en" = "Англійська"; -"settings_language_es" = "Іспанська"; -"settings_language_fr" = "Французька"; -"settings_language_it" = "Італійська"; -"settings_language_pl" = "Польська"; -"settings_language_tr" = "Турецька"; -"settings_language_ko" = "Корейська"; -"settings_language_ja" = "Японська"; -"settings_language_el" = "Грецька"; -"settings_language_uk" = "Українська"; -"settings_language_zh_hans" = "Китайська (спрощена)"; -"settings_language_zh_hant" = "Китайська (традиційна)"; -"settings_language_ptpt" = "Португальська"; -"settings_language_java" = "Java (ключові слова)"; - - -/* Error overlay */ -"error_title" = "Помилка"; -"error_back_editor" = "Назад до редактора"; - -/* Toolbar */ -"toolbar_undo" = "Скасувати"; -"toolbar_redo" = "Повторити"; -"toolbar_new" = "Нове"; -"menu_open" = "Відкрити…"; -"menu_save" = "Зберегти"; -"menu_save_as" = "Зберегти як…"; -"menu_print" = "Друк…"; -"menu_file" = "Файл"; -"menu_new" = "Створити"; -"menu_close" = "Закрити"; - -/* Accessibility */ -"a11y_generate_code" = "Згенерувати код зі структурної схеми"; -"a11y_generate_code_locked" = "Згенерувати код, доступно після покупки"; -"a11y_unsaved_changes" = "Незбережені зміни"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Згенерувати код"; -"codegen_gate_body" = "Код Java, Python і JavaScript з вашої схеми буде розблоковано через покупку в застосунку (незабаром)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Генератор коду"; -"codegen_placeholder_body" = "Згенерований код з’явиться тут."; -"codegen_placeholder_compare" = "Перемикайтеся між Java, Python і JavaScript, щоб порівняти синтаксис (приклад нижче)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (умова) {\\n інструкція();\\n} else {\\n альтернатива();\\n}"; -"codegen_preview_python" = "if умова:\\n інструкція()\\nelse:\\n альтернатива()"; -"codegen_copy" = "Копіювати"; -"codegen_run_in_browser" = "Перевірити в браузері"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Нова вкладка схеми"; -"a11y_close_tab" = "Закрити вкладку"; - -"close_unsaved_title" = "Незбережені зміни"; -"close_unsaved_message" = "Ця діаграма містить незбережені зміни. Зберегти їх перед закриттям?"; -"close_unsaved_discard" = "Відхилити"; -"close_unsaved_cancel" = "Скасувати"; -"a11y_placeholder_drop" = "Перетягніть елемент сюди, щоб замінити заповнювач. Натисніть і утримуйте, щоб перемістити."; -"a11y_node_drop" = "Натисніть і утримуйте блок, потім перетягніть: скиньте на верхній або нижній край цілі, щоб вставити до або після. Скидання в центрі лише на заповнювач ø."; -"a11y_codegen_panel_drag" = "Перетягніть панель генератора коду"; - -/* Diagram */ -"diagram_title_label" = "Назва"; -"diagram_title_placeholder" = "Без назви"; - -/* Inspector */ -"inspector_no_selection" = "Елемент не вибрано — торкніться плитки."; -"inspector_apply_text" = "Застосувати текст"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Вид"; -"inspector_io_mode_statement" = "Інструкція"; -"inspector_io_mode_input" = "Введення"; -"inspector_io_mode_output" = "Виведення"; -"inspector_io_mode_help_java_like" = "Java: input: int zahl | output: number"; -"inspector_add_case" = "Додати випадок"; -"inspector_remove_case" = "Видалити випадок"; -"inspector_group_element" = "Текст елемента"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Вставити умову…"; -"inspector_placeholder_selector" = "Вставити selector…"; -"inspector_placeholder_call" = "Вставити виклик…"; -"inspector_placeholder_break" = "Коментар (необов'язково)"; - -/* Element types (Ukrainian + keyword hint) */ -"element_anweisung" = "Інструкція (statement)"; -"element_verzweigung" = "Умова (if/else)"; -"element_fallauswahl" = "Вибір (switch)"; -"element_forSchleife" = "Цикл (for)"; -"element_whileSchleife" = "Цикл (while)"; -"element_doUntilSchleife" = "Цикл (do-while)"; -"element_endlosschleife" = "Нескінченний цикл (while true)"; -"element_aussprung" = "Вихід (break)"; -"element_aufruf" = "Виклик (call)"; -"element_leerElement" = "Порожній елемент"; - -/* Tabs */ -"tab_title_format" = "Схема %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Порожня структурна схема"; -"canvas_empty_hint" = "Перетягніть елемент з «Елементи» сюди."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Вибір (switch)"; -"canvas_stub_anweisung" = "Інструкція (statement)"; -"canvas_stub_condition" = "Умова"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "нескінченний"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Виклик()"; - -/* About */ -"about_version_fmt" = "Версія %@ (%@)"; -"about_nav_title" = "Про VisuStruct"; -"about_features_heading" = "Можливості"; -"about_bullet_palette" = "Вставляйте блоки з палітри або перетягуйте їх у схему"; -"about_bullet_edit" = "Редагуйте схему: змінюйте текст, вибирайте елементи та видаляйте за потреби"; -"about_bullet_tabs" = "Кілька схем у вкладках — кожна з власною назвою"; -"about_bullet_file" = "Зберігайте та відкривайте схеми як файли .visustruct"; -"about_bullet_share" = "Поділіться схемою як зображенням (PNG)"; -"about_dateiformat_heading" = "Формат файлу"; -"about_dateiformat_part1" = "Рідний формат VisuStruct використовує розширення "; -"about_dateiformat_part2" = ". Ви можете зберігати схеми та обмінюватися ними з іншими застосунками, що підтримують цей формат."; -"about_dateiformat_strk" = "Файли з розширенням .strk також можна відкривати."; -"about_project_github_label" = "Java застосунок (код і релізи):"; -"about_web_label" = "Веб:"; -"about_developer_label" = "Розробник:"; -"about_lineage_heading" = "Походження"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Готово"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Скасувати вставку"; -"phone_show_text_editor" = "Редагувати текст"; -"phone_hide_text_editor" = "Згорнути"; -"phone_title_done" = "Готово"; - -/* Alerts */ -"err_save_failed_fmt" = "Не вдалося зберегти: %@"; -"err_share_prepare_failed_fmt" = "Не вдалося підготувати поширення: %@"; -"err_diagram_too_small_export" = "Схема занадто мала — нічого експортувати."; -"err_image_render_failed" = "Не вдалося створити зображення."; -"err_diagram_too_small_print" = "Схема занадто мала — нічого друкувати."; -"err_print_unavailable" = "Друк недоступний на цьому пристрої."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Запропонована назва файлу"; -"settings_export_filename_picker" = "Формат"; -"settings_export_filename_date_title" = "З датою"; -"settings_export_filename_title_only" = "Без дати"; -"settings_export_filename_footer_with_date" = "З датою: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Без дати: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Симуляцію завершено."; -"sim_notice_infinite_loop_empty" = "Нескінченний цикл не має тіла."; -"sim_notice_unsupported_statement_fmt" = "Непідтримувана інструкція: %@"; -"sim_notice_invalid_expression_fmt" = "Неприпустимий вираз: %@"; -"sim_notice_unknown_variable_fmt" = "Невідома змінна: %@"; -"sim_notice_division_by_zero" = "Ділення на нуль."; -"sim_notice_whole_number_expected" = "Введіть ціле число."; -"sim_notice_number_expected" = "Введіть число."; -"sim_notice_boolean_expected" = "Введіть true або false."; -"sim_notice_subprogram_not_found_fmt" = "Немає відкритої діаграми з назвою %@. Виклик пропущено."; -"sim_notice_call_too_deep" = "Забагато вкладених викликів. Підпрограма викликає сама себе?"; - -"codegen_pseudocode_toggle" = "Псевдокод (не виконуваний код)"; -"codegen_pseudocode_note" = "Псевдокод – не виконувана програма"; -"codegen_pseudocode_hint" = "Цю діаграму позначено як псевдокод. Код — лише ескіз, його не можна запустити."; -`;export{e as default}; diff --git a/website/app/assets/Localizable-rqA-20hM.js b/website/app/assets/Localizable-rqA-20hM.js deleted file mode 100644 index 71a0cbb2..00000000 --- a/website/app/assets/Localizable-rqA-20hM.js +++ /dev/null @@ -1,238 +0,0 @@ -const e=`/* Palette */ -"palette_header" = "Στοιχεία"; -"inspector_header" = "Επιθεωρητής αντικειμένων"; - -/* Actions */ -"action_share_png" = "Κοινή χρήση εικόνας…"; -"action_share_document" = "Κοινή χρήση αρχείου…"; -"action_export_pdf" = "Προεπισκόπηση PDF…"; -"action_generate_code" = "Δημιουργία κώδικα…"; -"action_simulation" = "Προσομοίωση"; -"action_diagram" = "Διάγραμμα"; -"simulation_placeholder_body" = "Η λειτουργία προσομοίωσης εκτελεί το διάγραμμα βήμα προς βήμα, επισημαίνει το τρέχον στοιχείο και εμφανίζει τις τιμές των μεταβλητών και την έξοδο."; -"simulation_controls" = "Έλεγχος"; -"simulation_highlight_label" = "Επισήμανση"; -"simulation_highlight_last" = "Τελευταίο βήμα"; -"simulation_highlight_next" = "Επόμενο βήμα"; -"simulation_not_available" = "Το Play εκτελεί αυτόματα την προσομοίωση με παύση 0,5 δευτερολέπτων ανά βήμα."; -"simulation_play" = "Αναπαραγωγή"; -"simulation_pause" = "Παύση"; -"simulation_step" = "Βήμα"; -"simulation_stop" = "Διακοπή"; -"simulation_input" = "Είσοδος"; -"simulation_input_placeholder" = "Δεν απαιτείται είσοδος."; -"simulation_input_submit" = "OK"; -"simulation_input_invalid_int" = "Πληκτρολογήστε έναν ακέραιο αριθμό."; -"simulation_trace" = "Τελευταίο βήμα"; -"simulation_trace_placeholder" = "Δεν έχει εκτελεστεί ακόμη κανένα βήμα."; -"simulation_variables" = "Μεταβλητές"; -"simulation_variables_placeholder" = "Δεν υπάρχουν ακόμη μεταβλητές."; -"simulation_output" = "Έξοδος"; -"simulation_output_placeholder" = "Δεν υπάρχει ακόμη έξοδος."; -"action_delete_selection" = "Διαγραφή επιλογής"; -"ctx_copy" = "Αντιγραφή"; -"ctx_cut" = "Αποκοπή"; -"ctx_paste" = "Επικόλληση"; -"action_settings" = "Ρυθμίσεις"; -"action_about" = "Σχετικά με το VisuStruct"; -"recent_files_menu" = "Πρόσφατα αρχεία"; -"recent_files_empty" = "Δεν υπάρχουν πρόσφατα αρχεία"; -"recent_files_clear" = "Εκκαθάριση λίστας"; -"recent_files_unavailable" = "Το αρχείο δεν είναι πλέον διαθέσιμο."; - -/* In-app settings */ -"settings_language_title" = "Γλώσσα"; -"settings_language_de" = "Γερμανικά"; -"settings_language_en" = "Αγγλικά"; -"settings_language_es" = "Ισπανικά"; -"settings_language_fr" = "Γαλλικά"; -"settings_language_it" = "Ιταλικά"; -"settings_language_pl" = "Πολωνικά"; -"settings_language_tr" = "Τουρκικά"; -"settings_language_ko" = "Κορεατικά"; -"settings_language_ja" = "Ιαπωνικά"; -"settings_language_el" = "Ελληνικά"; -"settings_language_uk" = "Ουκρανικά"; -"settings_language_zh_hans" = "Κινέζικα (Απλοποιημένα)"; -"settings_language_zh_hant" = "Κινέζικα (Παραδοσιακά)"; -"settings_language_ptpt" = "Πορτογαλικά"; -"settings_language_java" = "Java (λέξεις-κλειδιά)"; - - -/* Error overlay */ -"error_title" = "Σφάλμα"; -"error_back_editor" = "Πίσω στον επεξεργαστή"; - -/* Toolbar */ -"toolbar_undo" = "Αναίρεση"; -"toolbar_redo" = "Επανάληψη"; -"toolbar_new" = "Νέο"; -"menu_open" = "Άνοιγμα…"; -"menu_save" = "Αποθήκευση"; -"menu_save_as" = "Αποθήκευση ως…"; -"menu_print" = "Εκτύπωση…"; -"menu_file" = "Αρχείο"; -"menu_new" = "Νέο"; -"menu_close" = "Κλείσιμο"; - -/* Accessibility */ -"a11y_generate_code" = "Δημιουργία κώδικα από το διάγραμμα"; -"a11y_generate_code_locked" = "Δημιουργία κώδικα, διαθέσιμο μετά την αγορά"; -"a11y_unsaved_changes" = "Μη αποθηκευμένες αλλαγές"; - -/* Code generator (IAP to follow) */ -"codegen_gate_title" = "Δημιουργία κώδικα"; -"codegen_gate_body" = "Ο κώδικας Java, Python και JavaScript από το διάγραμμά σας θα ξεκλειδωθεί μέσω αγοράς εντός εφαρμογής (σύντομα)."; -"codegen_gate_ok" = "OK"; -"codegen_placeholder_title" = "Γεννήτρια κώδικα"; -"codegen_placeholder_body" = "Ο παραγόμενος κώδικας θα εμφανίζεται εδώ."; -"codegen_placeholder_compare" = "Εναλλαγή μεταξύ Java, Python και JavaScript για σύγκριση σύνταξης (παράδειγμα παρακάτω)."; -"codegen_target_java" = "Java"; -"codegen_target_python" = "Python"; -"codegen_target_javascript" = "JavaScript"; -"codegen_preview_java" = "if (συνθήκη) {\\n εντολή();\\n} else {\\n εναλλακτική();\\n}"; -"codegen_preview_python" = "if συνθήκη:\\n εντολή()\\nelse:\\n εναλλακτική()"; -"codegen_copy" = "Αντιγραφή"; -"codegen_run_in_browser" = "Δοκιμή στο πρόγραμμα περιήγησης"; -"codegen_save_java" = "Save Java…"; -"codegen_java_output_mode" = "Java output"; -"codegen_java_output_snippet" = "Snippet"; -"codegen_java_output_class" = "Class"; -"a11y_new_diagram_tab" = "Νέα καρτέλα διαγράμματος"; -"a11y_close_tab" = "Κλείσιμο καρτέλας"; - -"close_unsaved_title" = "Μη αποθηκευμένες αλλαγές"; -"close_unsaved_message" = "Αυτό το διάγραμμα περιέχει μη αποθηκευμένες αλλαγές. Θέλετε να τις αποθηκεύσετε πριν το κλείσιμο;"; -"close_unsaved_discard" = "Απόρριψη"; -"close_unsaved_cancel" = "Άκυρο"; -"a11y_placeholder_drop" = "Αποθέστε ένα στοιχείο εδώ για να αντικαταστήσετε το σύμβολο κράτησης. Πατήστε παρατεταμένα για μετακίνηση."; -"a11y_node_drop" = "Πατήστε παρατεταμένα ένα μπλοκ και σύρετε: αποθέστε στην πάνω/κάτω ακμή για εισαγωγή πριν/μετά. Κεντρική απόθεση μόνο σε ø."; -"a11y_codegen_panel_drag" = "Σύρετε για να μετακινήσετε το πλαίσιο δημιουργίας κώδικα"; - -/* Diagram */ -"diagram_title_label" = "Τίτλος"; -"diagram_title_placeholder" = "Χωρίς τίτλο"; - -/* Inspector */ -"inspector_no_selection" = "Δεν έχει επιλεγεί στοιχείο — πατήστε ένα πλακίδιο."; -"inspector_apply_text" = "Εφαρμογή κειμένου"; -"inspector_for_init" = "Init"; -"inspector_for_condition" = "Condition"; -"inspector_for_step" = "Step"; -"inspector_for_placeholder_init" = "int i = 0"; -"inspector_for_placeholder_condition" = "i < 5"; -"inspector_for_placeholder_step" = "i++"; - -"inspector_io_mode_title" = "Είδος"; -"inspector_io_mode_statement" = "Εντολή"; -"inspector_io_mode_input" = "Είσοδος"; -"inspector_io_mode_output" = "Έξοδος"; -"inspector_io_mode_help_java_like" = "Java: input: int zahl | output: number"; -"inspector_add_case" = "Προσθήκη περίπτωσης"; -"inspector_remove_case" = "Αφαίρεση περίπτωσης"; -"inspector_group_element" = "Κείμενο στοιχείου"; -"inspector_placeholder_statement" = "int sum = number1 + number2"; -"inspector_placeholder_condition" = "Εισαγωγή συνθήκης…"; -"inspector_placeholder_selector" = "Εισαγωγή selector…"; -"inspector_placeholder_call" = "Εισαγωγή κλήσης…"; -"inspector_placeholder_break" = "Σχόλιο (προαιρετικό)"; - -/* Element types (Greek + keyword hint) */ -"element_anweisung" = "Εντολή (statement)"; -"element_verzweigung" = "Συνθήκη (if/else)"; -"element_fallauswahl" = "Επιλογή (switch)"; -"element_forSchleife" = "Επανάληψη (for)"; -"element_whileSchleife" = "Επανάληψη (while)"; -"element_doUntilSchleife" = "Επανάληψη (do-while)"; -"element_endlosschleife" = "Ατέρμονος βρόχος (while true)"; -"element_aussprung" = "Έξοδος (break)"; -"element_aufruf" = "Κλήση (call)"; -"element_leerElement" = "Κενό στοιχείο"; - -/* Tabs */ -"tab_title_format" = "Διάγραμμα %d"; - -/* Empty canvas */ -"canvas_empty_title" = "Κενό διάγραμμα"; -"canvas_empty_hint" = "Σύρετε ένα στοιχείο από τα «Στοιχεία» εδώ."; - -/* Diagram: new element stubs & column headers (display) */ -"canvas_stub_fallauswahl" = "Επιλογή (switch)"; -"canvas_stub_anweisung" = "Εντολή (statement)"; -"canvas_stub_condition" = "Συνθήκη"; -"canvas_stub_for" = "for 0 < i < n"; -"canvas_stub_while" = "while …"; -"canvas_stub_until" = "until …"; -"canvas_stub_endless" = "ατέρμονο"; -"canvas_stub_break" = "break"; -"canvas_stub_call" = "Κλήση()"; - -/* About */ -"about_version_fmt" = "Έκδοση %@ (%@)"; -"about_nav_title" = "Σχετικά με το VisuStruct"; -"about_features_heading" = "Δυνατότητες"; -"about_bullet_palette" = "Εισαγωγή μπλοκ από την παλέτα ή μεταφορά τους στο διάγραμμα"; -"about_bullet_edit" = "Επεξεργασία διαγράμματος: αλλαγή κειμένου, επιλογή στοιχείων και διαγραφή αν χρειάζεται"; -"about_bullet_tabs" = "Πολλά διαγράμματα σε καρτέλες — το καθένα με τίτλο"; -"about_bullet_file" = "Αποθήκευση και άνοιγμα διαγραμμάτων ως αρχεία .visustruct"; -"about_bullet_share" = "Κοινή χρήση διαγράμματος ως εικόνα (PNG)"; -"about_dateiformat_heading" = "Μορφή αρχείου"; -"about_dateiformat_part1" = "Η εγγενής μορφή του VisuStruct χρησιμοποιεί την επέκταση "; -"about_dateiformat_part2" = ". Μπορείτε να αποθηκεύετε και να ανταλλάσσετε διαγράμματα με άλλες εφαρμογές που υποστηρίζουν την ίδια μορφή."; -"about_dateiformat_strk" = "Μπορούν να ανοίξουν και αρχεία με επέκταση .strk."; -"about_project_github_label" = "Εφαρμογή Java (πηγαίος κώδικας & εκδόσεις):"; -"about_web_label" = "Ιστός:"; -"about_developer_label" = "Προγραμματιστής:"; -"about_lineage_heading" = "Καταγωγή"; -"about_lineage_body" = "VisuStruct originally began as a Java desktop app and remains available across platforms for use in schools. The current Swift/SwiftUI version is the modern native app for Mac, iPad, and iPhone and remains compatible with the .visustruct file format. It builds on the original open-source work by Kevin Krummenauer (MIT)."; -"about_lineage_upstream_label" = "Java upstream:"; -"about_done" = "Τέλος"; -"about_copyright" = "Copyright © 2026 Holger Sebastiao"; - -/* iPhone: tap-to-insert */ -"phone_insert_cancel" = "Ακύρωση εισαγωγής"; -"phone_show_text_editor" = "Επεξεργασία κειμένου"; -"phone_hide_text_editor" = "Σύμπτυξη"; -"phone_title_done" = "Τέλος"; - -/* Alerts */ -"err_save_failed_fmt" = "Δεν ήταν δυνατή η αποθήκευση: %@"; -"err_share_prepare_failed_fmt" = "Δεν ήταν δυνατή η προετοιμασία κοινής χρήσης: %@"; -"err_diagram_too_small_export" = "Το διάγραμμα είναι πολύ μικρό — δεν υπάρχει τίποτα για εξαγωγή."; -"err_image_render_failed" = "Δεν ήταν δυνατή η δημιουργία εικόνας."; -"err_diagram_too_small_print" = "Το διάγραμμα είναι πολύ μικρό — δεν υπάρχει τίποτα για εκτύπωση."; -"err_print_unavailable" = "Η εκτύπωση δεν είναι διαθέσιμη σε αυτή τη συσκευή."; - -"settings_ui_scale_section" = "Text size"; -"settings_ui_scale_canvas" = "Structure chart"; -"settings_ui_scale_inspector" = "Text entry below"; -"settings_ui_scale_small" = "Small"; -"settings_ui_scale_medium" = "Medium"; -"settings_ui_scale_large" = "Large"; -"settings_ui_scale_extra_large" = "Extra large"; - -"settings_export_filename_section" = "Πρόταση ονόματος αρχείου"; -"settings_export_filename_picker" = "Μορφή"; -"settings_export_filename_date_title" = "Με ημερομηνία"; -"settings_export_filename_title_only" = "Χωρίς ημερομηνία"; -"settings_export_filename_footer_with_date" = "Με ημερομηνία: yyyy-mm-dd-Filename.visustruct"; -"settings_export_filename_footer_without_date" = "Χωρίς ημερομηνία: Filename.visustruct"; - - -/* Simulation: Meldungen */ -"sim_notice_finished" = "Η προσομοίωση ολοκληρώθηκε."; -"sim_notice_infinite_loop_empty" = "Ο ατέρμονος βρόχος δεν έχει σώμα."; -"sim_notice_unsupported_statement_fmt" = "Μη υποστηριζόμενη εντολή: %@"; -"sim_notice_invalid_expression_fmt" = "Μη έγκυρη έκφραση: %@"; -"sim_notice_unknown_variable_fmt" = "Άγνωστη μεταβλητή: %@"; -"sim_notice_division_by_zero" = "Διαίρεση με το μηδέν."; -"sim_notice_whole_number_expected" = "Εισαγάγετε έναν ακέραιο αριθμό."; -"sim_notice_number_expected" = "Εισαγάγετε έναν αριθμό."; -"sim_notice_boolean_expected" = "Πληκτρολογήστε true ή false."; -"sim_notice_subprogram_not_found_fmt" = "Κανένα ανοιχτό διάγραμμα δεν ονομάζεται %@. Η κλήση παραλείφθηκε."; -"sim_notice_call_too_deep" = "Πάρα πολλές ένθετες κλήσεις. Καλεί ένα υποπρόγραμμα τον εαυτό του;"; - -"codegen_pseudocode_toggle" = "Ψευδοκώδικας (μη εκτελέσιμος κώδικας)"; -"codegen_pseudocode_note" = "Ψευδοκώδικας – όχι εκτελέσιμο πρόγραμμα"; -"codegen_pseudocode_hint" = "Αυτό το διάγραμμα έχει επισημανθεί ως ψευδοκώδικας. Ο κώδικας είναι μόνο ένα σχέδιο και δεν μπορεί να εκτελεστεί."; -`;export{e as default}; diff --git a/website/app/assets/ada-lovelace-DS_988PF.jpg b/website/app/assets/ada-lovelace-DS_988PF.jpg deleted file mode 100644 index 1c5802bd..00000000 Binary files a/website/app/assets/ada-lovelace-DS_988PF.jpg and /dev/null differ diff --git a/website/app/assets/ar-AgbDXJ1P.js b/website/app/assets/ar-AgbDXJ1P.js deleted file mode 100644 index 0da61abc..00000000 --- a/website/app/assets/ar-AgbDXJ1P.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* نصوص موجودة في نسخة الويب فقط. أما بقية النصوص فتأتي من ملف Localizable.strings الخاص بالتطبيق. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "مخطط VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "تصدير…"; -"web_export_title" = "حفظ كصورة أو PDF، أو طباعة"; -"web_export_png" = "صورة (PNG)"; -"web_export_svg" = "رسم متجهي (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "ملف VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "فشل التصدير: %@"; -"web_print_hint" = "استخدم «تصدير… → PDF» لحفظ المخطط ثم طباعته من هناك."; -"web_delete" = "حذف"; -"web_language_title" = "لغة الواجهة"; -"web_simulation_start" = "بدء المحاكاة"; -"web_simulation_end" = "إنهاء المحاكاة"; -"web_simulation_start_title" = "تشغيل المخطط خطوة بخطوة"; -"web_simulation_end_title" = "إنهاء المحاكاة ومتابعة التحرير"; -"web_palette_hint" = "اسحب إلى المخطط"; -"web_tabs_label" = "المخططات"; -"web_status_modified" = "معدَّل"; -"web_inspector_title" = "الخصائص"; -"web_inspector_resize" = "تغيير ارتفاع مفتش الكائنات: اسحب الخط لأعلى أو لأسفل (نقر مزدوج: الافتراضي)"; -"web_inspector_help_drop" = "اسحب العناصر من اللوحة إلى كتلة: أعلى = قبل، أسفل = بعد، على «ø» = استبدال. يمكن نقل العناصر الموجودة بالفأرة."; -"web_inspector_help_inline" = "انقر نقرًا مزدوجًا أو المس مرتين على عنصر لتعديل نصه مباشرة في المخطط (Enter للتطبيق، Esc للإلغاء). وكذلك أسماء الحالات."; -"web_inspector_help_files" = "يمكنك أيضًا إفلات الملفات في النافذة."; -"web_case_name" = "اسم الحالة"; -"web_case_name_placeholder" = "مثلًا 1، ok، default"; -"web_copied" = "تم نسخ العنصر."; -"web_discard_message_fmt" = "ستُفقد التغييرات على «%@»."; -"web_err_unreadable_fmt" = "«%@» ليس ملف VisuStruct قابلًا للقراءة (%@)."; -"web_saved_fmt" = "تم حفظ «%@»."; -"web_saved_txt_fmt" = "تم الحفظ باسم «%@». احذف الامتداد .txt لاستخدامه."; -"web_drop_impossible" = "لا يمكن الإفلات هنا"; -"web_sim_delay" = "التوقف لكل خطوة"; -"web_sim_delay_value_fmt" = "%@ ث"; -"web_codegen_language" = "اللغة"; -"web_codegen_comments" = "نص المخطط كتعليقات"; -"web_codegen_empty_else" = "إخراج فرع else فارغ في الشفرة"; -"web_copied_check" = "تم النسخ ✓"; -"web_copy_manual" = "تم التحديد، اضغط Ctrl+C للنسخ"; -"web_run_stopped_loop" = "توقف التنفيذ: حلقة لا نهائية محتملة."; -"web_run_cancelled" = "تم إلغاء التنفيذ."; -"web_run_error" = "خطأ أثناء التشغيل."; -"web_run_dropped_fmt" = "… تم إخفاء %d من الأسطر السابقة"; -"web_sim_call_arguments_fmt" = "الاستدعاء لا يطابق ترويسة %@. تحقق من القيم بين القوسين."; -"web_sim_missing_return_fmt" = "انتهى %@ دون إرجاع قيمة باستخدام return."; -"web_sim_file_not_found_fmt" = "لا يوجد ملف باسم %@."; -"web_sim_end_of_file_fmt" = "لا يوجد سطر آخر في الملف %@."; -"web_sim_null_value_fmt" = "القيمة في %@ هي null."; -"web_sim_unknown_variable_case_fmt" = "متغير غير معروف: %1$@. تميّز Java بين الأحرف الكبيرة والصغيرة: هل تقصد %2$@؟"; -"web_sim_written_file_fmt" = "%@ (محفوظ)"; -"web_site_home" = "الصفحة الرئيسية"; -"web_site_imprint" = "بيانات الناشر"; -"web_site_privacy" = "الخصوصية"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "البرمجة"; -"web_live_hide" = "إخفاء البرمجة"; -"web_scale_canvas" = "حجم النص: المخطط"; -"web_scale_code" = "حجم النص: البرمجة"; -"web_live_indent" = "إزاحة"; -"web_live_indent_title" = "إزاحة الشيفرة: أربع مسافات لكل مستوى (الأسطر المحددة، وإلا فالكل)"; -"web_live_show" = "إظهار البرمجة"; -"web_live_resize" = "تغيير عرض منطقة الشيفرة (نقر مزدوج: إعادة تعيين)"; -"web_live_hint" = "تنمو الشيفرة مع مخططك. الرمادي = عنصر نائب: اكتب في المفتش ليظهر نصك هنا. انقر على سطر لتحديد عنصره."; -"web_live_ph_selector" = "المتغير"; -"web_live_ph_code" = "شيفرتك"; -"web_live_ph_end" = "النهاية"; -"web_live_language" = "لغة البرمجة"; -"web_live_java_class" = "كصنف"; -"web_live_java_class_hint" = "اسم الصنف = عنوان المخطط"; -"web_live_java_main" = "مع الطريقة main"; -"web_settings_display" = "العرض"; -"web_live_swing" = "جافا حقيقية (مع Swing)"; -"web_live_swing_hint" = "جافا حقيقية مع نوافذ (CheerpJ). تُنزّل نحو 40 ميغابايت مرة واحدة عند أول تشغيل."; -"web_live_swing_windows" = "نوافذ جافا"; -"web_live_swing_hide" = "إغلاق نوافذ جافا"; -"web_live_swing_loading" = "جارٍ تحميل جافا (نحو 40 ميغابايت في المرة الأولى) …"; -"web_live_swing_compiling" = "جارٍ الترجمة …"; -"web_live_swing_running" = "البرنامج قيد التشغيل."; -"web_live_swing_compile_failed" = "خطأ في الترجمة، انظر أعلاه."; -"web_live_swing_failed" = "تعذّر تحميل جافا. تحقّق من الاتصال بالإنترنت."; -"web_live_hint_edit" = "اكتب هنا شيفرة Java وسيُبنى المخطط تلقائيًا. أو اعمل في المخطط وستنمو الشيفرة معه. الرمادي = عنصر نائب، اكتب فوقه مباشرة."; -"web_live_edit_label" = "شيفرة Java"; -"web_parse_line_fmt" = "السطر %d: %@"; -"web_parse_missing" = "ما زال ينقص هنا: %@"; -"web_parse_extra" = "زائد: %@"; -"web_parse_unclosed_string" = "ينقص %@ الإغلاق هنا."; -"web_parse_else_without_if" = "لا يوجد «if» لهذا «else»."; -"web_parse_case_outside_switch" = "«case» و«default» مكانهما داخل «switch»."; -"web_parse_missing_do_while" = "بعد «do { … }» ينقص «while (الشرط);»."; -"web_parse_more_methods" = "حاليًا تُحتسب طريقة واحدة فقط هنا. أنشئ البرامج الفرعية الأخرى في علامات تبويب منفصلة."; -"web_parse_missing_import" = "ينقص في الأعلى %@ بدون هذا السطر لا تعرف Java الصنف Scanner."; -"web_parse_missing_scanner" = "ينقص قبل ذلك %@ بدون هذا السطر لا يوجد Scanner."; -"web_parse_unknown_type" = "لا تعرف Java نوع البيانات هذا. هل تقصد %@؟"; -"web_quickfix_insert_fmt" = "اقتراح: إدراج %@"; -"web_quickfix_replace_fmt" = "اقتراح: %1$@ بدلاً من %2$@"; -"web_parse_wrong_case" = "تميّز Java بين الأحرف الكبيرة والصغيرة. الصحيح هو %@."; -"web_editor_title" = "المخطط"; -"web_editor_show" = "إظهار المخطط"; -"web_editor_hide" = "إخفاء المخطط"; -"web_keywords_title" = "كلمات الشيفرة المفتاحية"; -"web_keywords_hint" = "if و while و for بدلًا من أسماء العناصر"; -"web_palette_compact" = "العناصر مضغوطة دائمًا"; -"web_palette_compact_hint" = "رمز مع اسم صغير تحته، وإلا فقط عندما يكون العمود ضيقًا"; -"web_about_version_fmt" = "الإصدار %@"; -"web_about_software" = "البرمجيات المستخدمة"; -"web_about_software_text" = "يستخدم محرر الشيفرة CodeMirror و Lezer (ترخيص MIT). مع دعم Swing يحمّل VisuStruct برنامج CheerpJ من Leaning Technologies (ترخيص Community) و javac من OpenJDK."; -"web_about_licenses" = "نصوص التراخيص"; -"web_live_run" = "تشغيل"; -"web_live_run_title" = "تشغيل البرنامج: تظهر المخرجات والمدخلات بالأسفل"; -"web_live_run_finished" = "انتهى البرنامج."; -"web_live_run_stopped" = "تم الإيقاف."; -"web_console" = "وحدة التحكم"; -"web_console_hide" = "إخفاء وحدة التحكم"; -"web_console_resize" = "تغيير ارتفاع وحدة التحكم"; -"web_diagram_tools" = "أدوات المخطط"; -"web_files_title" = "الملفات"; -"web_files_show" = "إظهار الملفات"; -"web_files_hide" = "إخفاء الملفات"; -"web_files_resize" = "تغيير عرض الملفات"; -"web_files_new_folder" = "مجلد جديد"; -"web_files_switch" = "اختيار مجلد آخر"; -"web_files_delete" = "حذف"; -"web_files_in_browser" = "في تخزين هذا المتصفح"; -"web_files_save_hint_fmt" = "الحفظ يضع البرنامج في %@."; -"web_files_empty" = "لم يُحفظ شيء بعد. عند الحفظ ينشئ VisuStruct هنا مجلداً بتاريخ اليوم."; -"web_files_reconnect_hint" = "بعد إعادة التحميل يسأل المتصفح مرة أخرى إن كان يمكن لـ VisuStruct استخدام مجلدك."; -"web_files_reconnect_fmt" = "فتح المجلد «%@»"; -"web_files_connect_hint" = "اختر مجلداً كمساحة عمل. ينشئ VisuStruct فيه مجلداً لكل يوم ويحفظ المخطط والشيفرة."; -"web_files_connect" = "اختيار مجلد"; -"web_files_browser_hint" = "لا يستطيع هذا المتصفح فتح مجلدات الجهاز (مثلاً على iPad). تبقى ملفاتك في تخزين هذا المتصفح."; -"web_files_browser" = "الحفظ في المتصفح"; -"web_files_delete_confirm_fmt" = "هل تريد حذف «%@» فعلاً؟"; -"web_files_folder_prompt" = "اسم المجلد الجديد:"; -"web_files_default_name" = "برنامج"; -"web_files_saved_fmt" = "تم الحفظ: %@"; -"web_files_create" = "إنشاء"; -"web_learn_ludwig_tip" = "نصيحة لودفيغ"; -"web_game_title" = "لعبة: صحيح أم خطأ؟"; -"web_game_intro" = "يعرض لك لودفيغ قيماً وشرطاً. هل الشرط صحيح (true) أم خطأ (false)؟ عشر جولات تزداد صعوبة."; -"web_game_start" = "ابدأ اللعبة"; -"web_game_round_fmt" = "الجولة %@ من %@"; -"web_game_score_fmt" = "النقاط: %@"; -"web_game_true" = "صحيح (true)"; -"web_game_false" = "خطأ (false)"; -"web_game_right" = "صحيح!"; -"web_game_wrong" = "ليس تماماً. هكذا يُحسب:"; -"web_game_next" = "التالي"; -"web_game_finish" = "النتيجة"; -"web_game_done_fmt" = "%@ من %@ صحيحة."; -"web_game_perfect" = "كلها صحيحة – لودفيغ معجب جداً!"; -"web_game_good" = "رائع! راجع أخطاءك وحاول مرة أخرى."; -"web_game_practice" = "التمرين يصنع الإتقان. اقرأ جدول الحقيقة مرة أخرى والعب جولة أخرى."; -"web_game_again" = "العب مرة أخرى"; -"web_game_wrong_short" = "للأسف خطأ."; -"web_game_practice_short" = "التمرين يصنع الإتقان. اقرأ الشروح مرة أخرى والعب جولة أخرى."; -"web_game_clicks" = "النقرات:"; -"web_game_nobody" = "لا أحد يستمع – لا يحدث شيء."; -"web_game_event_fmt" = "نقرة على «%@»: المستمع يسمعها وينفّذ شيفرته."; -"web_game_try" = "جرّب الآن أزرار النافذة بنفسك."; -"web_game_time_fmt" = "متبقٍ %@ ث"; -"web_game_defused" = "✂️ تم تعطيلها! وجدت الخطأ."; -"web_game_boom_time" = "💥 بوم! انتهى الوقت."; -"web_game_boom_wrong" = "💥 بوم! كان ذلك السطر الخطأ."; -"web_game_boom_part" = "💥 بوم! السطر صحيح، لكن الخطأ في جزء آخر منه."; -"web_game_boom_wrong_part" = "💥 بوم! الخطأ في سطر آخر."; -"web_game_bomb_parts_hint" = "اضغط على الجزء الخاطئ بالضبط."; -"web_game_fix" = "السطر الصحيح:"; -"web_game_order_hint" = "اضغط على الخطوات بالترتيب الصحيح."; -"web_debug_step" = "خطوة"; -"web_debug_step_title" = "خطوة: تنفيذ تعليمة واحدة ثم التوقف مجدداً"; -"web_debug_continue" = "متابعة حتى نقطة التوقف التالية"; -"web_debug_paused_fmt" = "متوقف قبل السطر %d."; -"web_debug_gutter_title" = "انقر على رقم السطر لوضع نقطة توقف أو إزالتها"; -"web_swap_to_code" = "البرمجة كبيرة"; -"web_swap_to_editor" = "المخطط كبير"; -"web_editor_resize" = "تغيير عرض المخطط"; -"web_palette_resize" = "تغيير عرض العناصر"; -"web_palette_hide" = "إخفاء العناصر"; -"web_palette_show" = "إظهار العناصر"; -"web_code_scopes" = "كتل الشيفرة ملوّنة"; -"web_code_scopes_hint" = "الصنف والدالة والحلقة والتفرّع في مربعات خاصة"; -"web_pseudocode" = "شبه كود"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "إعادة تسمية المتغير …"; -"web_rename_title" = "إعادة تسمية المتغير"; -"web_rename_old" = "الاسم"; -"web_rename_new" = "الاسم الجديد"; -"web_rename_hint" = "يغيّر كل موضع بهذه الكتابة تمامًا (number ليس Number)، في الشيفرة وفي المخطط الهيكلي. يبقى النص بين \\"…\\" والتعليقات كما هي."; -"web_rename_count_fmt" = "يظهر %d مرة."; -"web_rename_exists_fmt" = "«%@» موجود بالفعل: بعدها يصبح المتغيران متغيرًا واحدًا."; -"web_rename_invalid" = "يبدأ الاسم بحرف ويحتوي على حروف وأرقام و _ فقط."; -"web_rename_keyword_fmt" = "«%@» كلمة مفتاحية في Java."; -"web_rename_apply" = "إعادة تسمية"; -"web_rename_cancel" = "إلغاء"; -`;export{e as default}; diff --git a/website/app/assets/aufgabe-mehrwertsteuer-C-TFTrWz.svg b/website/app/assets/aufgabe-mehrwertsteuer-C-TFTrWz.svg deleted file mode 100644 index 57094a66..00000000 --- a/website/app/assets/aufgabe-mehrwertsteuer-C-TFTrWz.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - zu: Handy - - - - aufklappen - - - - - - - - - - - - - - - NEU - - auf: Tablet - - - - - KASSENBON - Falt-Handy - netto1000,00 € - +19% MwSt190,00 € - - SUMME1190,00 € - - - - So setzt sich der Preis zusammen: - - netto 1000 € - - 190 € - - - - - - - - - - - Finanzamt - - - - - - brutto 1190 € - Das zahlst du an der Kasse. - diff --git a/website/app/assets/aufgabe-mehrwertsteuer-CnrZ00ny.svg b/website/app/assets/aufgabe-mehrwertsteuer-CnrZ00ny.svg deleted file mode 100644 index 920b4411..00000000 --- a/website/app/assets/aufgabe-mehrwertsteuer-CnrZ00ny.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - phone - - - - unfold - - - - - - - - - - - - - - - NEW - - tablet - - - - - RECEIPT - Folding phone - net€1,000.00 - +19% VAT€190.00 - - TOTAL€1,190.00 - - - - This is how the price is made up: - - net €1,000 - - €190 - - - - - - - - - - - Tax office - - - - - - gross €1,190 - You pay this at the till. - diff --git a/website/app/assets/aufgabe-mehrwertsteuer-KUkerRBd.svg b/website/app/assets/aufgabe-mehrwertsteuer-KUkerRBd.svg deleted file mode 100644 index 70b4b6bb..00000000 --- a/website/app/assets/aufgabe-mehrwertsteuer-KUkerRBd.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - 12:34 - - - telemóvel - - - - abrir - - - - - - - - - - - - - - - NOVO - - tablet - - - - - TALÃO - Telem. dobrável - s/ IVA1000,00 € - +23% IVA230,00 € - - TOTAL1230,00 € - - - - Assim se forma o preço: - - sem IVA 1000 € - - 230 € - - - - - - - - - - - Finanças - - - - - - com IVA 1230 € - É isto que pagas na caixa. - diff --git a/website/app/assets/ben-shneiderman-Cev5hrqS.jpg b/website/app/assets/ben-shneiderman-Cev5hrqS.jpg deleted file mode 100644 index 369a8ef0..00000000 Binary files a/website/app/assets/ben-shneiderman-Cev5hrqS.jpg and /dev/null differ diff --git a/website/app/assets/caesar-ascii-Bix93F5i.svg b/website/app/assets/caesar-ascii-Bix93F5i.svg deleted file mode 100644 index d6beace8..00000000 --- a/website/app/assets/caesar-ascii-Bix93F5i.svg +++ /dev/null @@ -1,98 +0,0 @@ - - -A -65 - -B -66 - -C -67 - -D -68 - -E -69 - -F -70 - -G -71 - -H -72 - -I -73 - -J -74 - -K -75 - -L -76 - -M -77 - -N -78 - -O -79 - -P -80 - -Q -81 - -R -82 - -S -83 - -T -84 - -U -85 - -V -86 - -W -87 - -X -88 - -Y -89 - -Z -90 -(int) 'X' -→ -88 -88 - 65 -→ -23 -23 + 3 -→ -26 -26 % 26 -→ -0 -0 + 65 -→ -65 -(char) 65 -→ -'A' - \ No newline at end of file diff --git a/website/app/assets/caesar-scheibe-CMRPZI6Y.svg b/website/app/assets/caesar-scheibe-CMRPZI6Y.svg deleted file mode 100644 index 8d8484de..00000000 --- a/website/app/assets/caesar-scheibe-CMRPZI6Y.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - -A -B -C -D -E -F -G -H -I -J -K -L -M -N -O -P -Q -R -S -T -U -V -W -X -Y -Z - -A -B -C -D -E -F -G -H -I -J -K -L -M -N -O -P -Q -R -S -T -U -V -W -X -Y -Z - - - - \ No newline at end of file diff --git a/website/app/assets/caesar-verschiebung-DIR-lDbs.svg b/website/app/assets/caesar-verschiebung-DIR-lDbs.svg deleted file mode 100644 index 81035fc3..00000000 --- a/website/app/assets/caesar-verschiebung-DIR-lDbs.svg +++ /dev/null @@ -1,133 +0,0 @@ - - - -A - -B - -C - -D - -E - -F - -G - -H - -I - -J - -K - -L - -M - -D - -E - -F - -G - -H - -I - -J - -K - -L - -M - -N - -O - -P - - - - - - - - - - - - - - -N - -O - -P - -Q - -R - -S - -T - -U - -V - -W - -X - -Y - -Z - -Q - -R - -S - -T - -U - -V - -W - -X - -Y - -Z - -A - -B - -C - - - - - - - - - - - - - - \ No newline at end of file diff --git a/website/app/assets/da-DCJmT-QF.js b/website/app/assets/da-DCJmT-QF.js deleted file mode 100644 index 5229bc7a..00000000 --- a/website/app/assets/da-DCJmT-QF.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Tekster, der kun findes i webversionen. Alt andet kommer fra appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksportér…"; -"web_export_title" = "Gem som billede eller PDF, eller udskriv"; -"web_export_png" = "Billede (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Eksport mislykkedes: %@"; -"web_print_hint" = "Brug »Eksportér… → PDF« til at gemme diagrammet og udskrive det derfra."; -"web_delete" = "Slet"; -"web_language_title" = "Sprog i brugerfladen"; -"web_simulation_start" = "Start simulering"; -"web_simulation_end" = "Afslut simulering"; -"web_simulation_start_title" = "Kør struktogrammet trin for trin"; -"web_simulation_end_title" = "Afslut simuleringen og fortsæt redigeringen"; -"web_palette_hint" = "Træk ind i struktogrammet"; -"web_tabs_label" = "Diagrammer"; -"web_status_modified" = "ændret"; -"web_inspector_title" = "Egenskaber"; -"web_inspector_resize" = "Skift højden på objektinspektøren: træk eller stryg linjen op eller ned (dobbeltklik: standard)"; -"web_inspector_help_drop" = "Træk elementer fra paletten over på en flise: øverst = før, nederst = efter, på »ø« = erstat. Eksisterende elementer kan flyttes med musen."; -"web_inspector_help_inline" = "Dobbeltklik eller dobbelttryk på et element for at redigere teksten direkte i diagrammet (Enter anvender, Esc annullerer). Det gælder også case-navne."; -"web_inspector_help_files" = "Du kan også slippe filer i vinduet."; -"web_case_name" = "Navn på case"; -"web_case_name_placeholder" = "f.eks. 1, ok, default"; -"web_copied" = "Element kopieret."; -"web_discard_message_fmt" = "Ændringerne i »%@« går tabt."; -"web_err_unreadable_fmt" = "»%@« er ikke en læsbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "»%@« gemt."; -"web_saved_txt_fmt" = "Gemt som »%@«. Fjern filtypenavnet .txt for at bruge den."; -"web_drop_impossible" = "Kan ikke slippes her"; -"web_sim_delay" = "Pause pr. trin"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Sprog"; -"web_codegen_comments" = "Diagramtekst som kommentarer"; -"web_codegen_empty_else" = "Skriv tom else-gren i koden"; -"web_copied_check" = "Kopieret ✓"; -"web_copy_manual" = "Markeret, tryk Ctrl+C for at kopiere"; -"web_run_stopped_loop" = "Kørslen stoppet: mulig uendelig løkke."; -"web_run_cancelled" = "Kørslen annulleret."; -"web_run_error" = "Fejl under kørsel."; -"web_run_dropped_fmt" = "… %d tidligere linjer skjult"; -"web_sim_call_arguments_fmt" = "Kaldet passer ikke til hovedet i %@. Tjek værdierne i parenteserne."; -"web_sim_missing_return_fmt" = "%@ sluttede uden at returnere en værdi med return."; -"web_sim_file_not_found_fmt" = "Der findes ingen fil med navnet %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har ikke flere linjer."; -"web_sim_null_value_fmt" = "%@ er null."; -"web_sim_unknown_variable_case_fmt" = "Ukendt variabel: %1$@. Java skelner mellem store og små bogstaver: mente du %2$@?"; -"web_sim_written_file_fmt" = "%@ (gemt)"; -"web_site_home" = "Forside"; -"web_site_imprint" = "Kolofon"; -"web_site_privacy" = "Privatliv"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Skjul programmering"; -"web_scale_canvas" = "Tekststørrelse: struktogram"; -"web_scale_code" = "Tekststørrelse: programmering"; -"web_live_indent" = "Indryk"; -"web_live_indent_title" = "Indryk koden: fire mellemrum pr. niveau (markerede linjer, ellers alt)"; -"web_live_show" = "Vis programmering"; -"web_live_resize" = "Skift kodeområdets bredde (dobbeltklik: nulstil)"; -"web_live_hint" = "Koden vokser med dit diagram. Gråt = pladsholder: skriv i inspektøren, så vises teksten her. Klik på en linje for at vælge elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kode"; -"web_live_ph_end" = "slut"; -"web_live_language" = "Programmeringssprog"; -"web_live_java_class" = "Som klasse"; -"web_live_java_class_hint" = "Klassenavn = diagrammets titel"; -"web_live_java_main" = "Med main-metode"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Ægte Java (med Swing)"; -"web_live_swing_hint" = "Rigtig Java med vinduer (CheerpJ). Henter ca. 40 MB én gang ved første kørsel."; -"web_live_swing_windows" = "Java-vinduer"; -"web_live_swing_hide" = "Luk Java-vinduer"; -"web_live_swing_loading" = "Java indlæses (første gang ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompilerer …"; -"web_live_swing_running" = "Programmet kører."; -"web_live_swing_compile_failed" = "Kompileringsfejl, se ovenfor."; -"web_live_swing_failed" = "Java kunne ikke indlæses. Tjek internetforbindelsen."; -"web_live_hint_edit" = "Skriv Java-kode her, så bygger diagrammet sig selv. Eller arbejd i diagrammet, så vokser koden med. Gråt = pladsholder, skriv bare ovenpå."; -"web_live_edit_label" = "Java-kode"; -"web_parse_line_fmt" = "Linje %d: %@"; -"web_parse_missing" = "Her mangler stadig: %@"; -"web_parse_extra" = "For meget: %@"; -"web_parse_unclosed_string" = "Det afsluttende %@ mangler."; -"web_parse_else_without_if" = "Denne “else” har ingen “if”."; -"web_parse_case_outside_switch" = "“case” og “default” hører til i en “switch”."; -"web_parse_missing_do_while" = "Efter “do { … }” mangler “while (betingelse);”."; -"web_parse_more_methods" = "Indtil videre tæller kun én metode her. Opret andre underprogrammer i egne faner."; -"web_parse_missing_import" = "Øverst mangler %@ Uden denne linje kender Java ikke Scanner."; -"web_parse_missing_scanner" = "Før mangler %@ Uden den linje findes Scanneren ikke."; -"web_parse_unknown_type" = "Java kender ikke denne datatype. Mente du %@?"; -"web_quickfix_insert_fmt" = "Forslag: indsæt %@"; -"web_quickfix_replace_fmt" = "Forslag: %1$@ i stedet for %2$@"; -"web_parse_wrong_case" = "Java skelner mellem store og små bogstaver. Det rigtige er %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Vis strukturdiagram"; -"web_editor_hide" = "Skjul strukturdiagram"; -"web_keywords_title" = "Kode-nøgleord"; -"web_keywords_hint" = "if, while, for i stedet for elementnavne"; -"web_palette_compact" = "Elementer altid kompakte"; -"web_palette_compact_hint" = "Symbol med lille navn nedenunder, ellers kun når kolonnen er smal"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Anvendt software"; -"web_about_software_text" = "Kodeeditoren bruger CodeMirror og Lezer (MIT-licens). Med Swing-understøttelse indlæser VisuStruct CheerpJ fra Leaning Technologies (Community-licens) og javac fra OpenJDK."; -"web_about_licenses" = "Licenstekster"; -"web_live_run" = "Kør"; -"web_live_run_title" = "Kør programmet: output og input vises nedenfor"; -"web_live_run_finished" = "Programmet er færdigt."; -"web_live_run_stopped" = "Stoppet."; -"web_console" = "Konsol"; -"web_console_hide" = "Skjul konsollen"; -"web_console_resize" = "Ændr konsollens højde"; -"web_diagram_tools" = "Diagramværktøjer"; -"web_files_title" = "Filer"; -"web_files_show" = "Vis filer"; -"web_files_hide" = "Skjul filer"; -"web_files_resize" = "Ændr bredden på filer"; -"web_files_new_folder" = "Ny mappe"; -"web_files_switch" = "Vælg en anden mappe"; -"web_files_delete" = "Slet"; -"web_files_in_browser" = "I denne browsers lager"; -"web_files_save_hint_fmt" = "Når du gemmer, lægges programmet i %@."; -"web_files_empty" = "Intet gemt endnu. Når du gemmer, opretter VisuStruct her en mappe med dagens dato."; -"web_files_reconnect_hint" = "Efter genindlæsning spørger browseren igen, om VisuStruct må bruge din mappe."; -"web_files_reconnect_fmt" = "Åbn mappen “%@”"; -"web_files_connect_hint" = "Vælg en mappe som arbejdsområde. VisuStruct opretter en mappe for hver dag og gemmer diagram og kode."; -"web_files_connect" = "Vælg mappe"; -"web_files_browser_hint" = "Denne browser kan ikke åbne mapper på enheden (fx på iPad). Dine filer bliver i browserens lager."; -"web_files_browser" = "Gem i browseren"; -"web_files_delete_confirm_fmt" = "Vil du virkelig slette “%@”?"; -"web_files_folder_prompt" = "Navn på den nye mappe:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Gemt: %@"; -"web_files_create" = "Opret"; -"web_learn_ludwig_tip" = "Ludwigs tip"; -"web_game_title" = "Spil: Sandt eller falsk?"; -"web_game_intro" = "Ludwig viser dig værdier og en betingelse. Er betingelsen sand (true) eller falsk (false)? Ti runder, sværere og sværere."; -"web_game_start" = "Start spillet"; -"web_game_round_fmt" = "Runde %@ af %@"; -"web_game_score_fmt" = "Point: %@"; -"web_game_true" = "sand (true)"; -"web_game_false" = "falsk (false)"; -"web_game_right" = "Rigtigt!"; -"web_game_wrong" = "Ikke helt. Sådan regnes det ud:"; -"web_game_next" = "Videre"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ af %@ rigtige."; -"web_game_perfect" = "Alt rigtigt – Ludwig er dybt imponeret!"; -"web_game_good" = "Flot! Se på dine fejl og prøv igen."; -"web_game_practice" = "Øvelse gør mester. Læs sandhedstabellen igen, og spil en runde mere."; -"web_game_again" = "Spil igen"; -"web_game_wrong_short" = "Desværre forkert."; -"web_game_practice_short" = "Øvelse gør mester. Læs forklaringerne igen og spil en runde til."; -"web_game_clicks" = "Klik:"; -"web_game_nobody" = "Ingen lytter – der sker ingenting."; -"web_game_event_fmt" = "Klik på »%@«: lytteren hører det og kører sin kode."; -"web_game_try" = "Prøv nu selv knapperne i vinduet."; -"web_game_time_fmt" = "%@ s tilbage"; -"web_game_defused" = "✂️ Desarmeret! Du fandt fejlen."; -"web_game_boom_time" = "💥 Bum! Tiden er gået."; -"web_game_boom_wrong" = "💥 Bum! Det var den forkerte linje."; -"web_game_boom_part" = "💥 Bang! Rigtig linje, men fejlen er i en anden del."; -"web_game_boom_wrong_part" = "💥 Bang! Fejlen er i en anden linje."; -"web_game_bomb_parts_hint" = "Tryk præcis på den forkerte del."; -"web_game_fix" = "Den rigtige linje:"; -"web_game_order_hint" = "Tryk på trinnene i den rigtige rækkefølge."; -"web_debug_step" = "Trin"; -"web_debug_step_title" = "Trin: kør én sætning og hold pause igen"; -"web_debug_continue" = "Fortsæt til næste breakpoint"; -"web_debug_paused_fmt" = "Sat på pause før linje %d."; -"web_debug_gutter_title" = "Klik på et linjenummer for at sætte eller fjerne et breakpoint"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Skift bredde på struktogrammet"; -"web_palette_resize" = "Skift bredde på elementerne"; -"web_palette_hide" = "Skjul elementerne"; -"web_palette_show" = "Vis elementerne"; -"web_code_scopes" = "Farvede kodeblokke"; -"web_code_scopes_hint" = "Klasse, metode, løkke, forgrening i egne felter"; -"web_pseudocode" = "Pseudokode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Omdøb variabel …"; -"web_rename_title" = "Omdøb variabel"; -"web_rename_old" = "Navn"; -"web_rename_new" = "Nyt navn"; -"web_rename_hint" = "Ændrer hvert sted med præcis denne stavemåde (tal er ikke Tal), i koden og i strukturdiagrammet. Tekst i \\"…\\" og kommentarer bliver."; -"web_rename_count_fmt" = "Forekommer %d gange."; -"web_rename_exists_fmt" = "“%@” findes allerede: bagefter ville begge være én variabel."; -"web_rename_invalid" = "Et navn begynder med et bogstav og indeholder kun bogstaver, cifre og _."; -"web_rename_keyword_fmt" = "“%@” er et Java-nøgleord."; -"web_rename_apply" = "Omdøb"; -"web_rename_cancel" = "Annuller"; -`;export{e as default}; diff --git a/website/app/assets/de-Bw3STH-g.js b/website/app/assets/de-Bw3STH-g.js deleted file mode 100644 index b0dfc833..00000000 --- a/website/app/assets/de-Bw3STH-g.js +++ /dev/null @@ -1,801 +0,0 @@ -const e=`/* Texte, die es nur in der Web-Version gibt. Alle anderen kommen aus den Localizable.strings der App. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-Struktogramm"; -"web_key_ctrl" = "Strg"; -"web_key_delete" = "Entf"; -"web_key_shift" = "Umschalt"; -"web_export" = "Exportieren …"; -"web_export_title" = "Als Bild oder PDF speichern oder drucken"; -"web_export_png" = "Bild (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-Datei (.visustruct)"; -"web_export_failed_fmt" = "Export fehlgeschlagen: %@"; -"web_print_hint" = "Über „Exportieren … → PDF“ lässt sich das Diagramm speichern und dann drucken."; -"web_delete" = "Löschen"; -"web_language_title" = "Sprache der Oberfläche"; -"web_simulation_start" = "Simulation starten"; -"web_simulation_end" = "Simulation beenden"; -"web_simulation_start_title" = "Struktogramm Schritt für Schritt ausführen"; -"web_simulation_end_title" = "Simulation beenden und weiter bearbeiten"; -"web_palette_hint" = "Ins Struktogramm ziehen"; -"web_tabs_label" = "Diagramme"; -"web_status_modified" = "geändert"; -"web_inspector_title" = "Objektinspektor"; -"web_inspector_help_drop" = "Elemente aus dem Bereich „Elemente“ auf eine Kachel ziehen: oben = davor, unten = dahinter, auf „ø“ = ersetzen. Vorhandene Elemente lassen sich mit der Maus verschieben."; -"web_inspector_help_inline" = "Doppelklick oder Doppeltipp auf ein Element: Text direkt im Struktogramm ändern (Enter übernimmt, Esc bricht ab). Auch Fallnamen lassen sich so ändern."; -"web_inspector_help_files" = "Dateien können auch einfach ins Fenster gezogen werden."; -"web_case_name" = "Fallname"; -"web_case_name_placeholder" = "z. B. 1, ok, default"; -"web_copied" = "Element kopiert."; -"web_discard_message_fmt" = "Die Änderungen an „%@“ gehen verloren."; -"web_err_unreadable_fmt" = "„%@“ ist keine lesbare VisuStruct-Datei (%@)."; -"web_saved_fmt" = "„%@“ gespeichert."; -"web_saved_txt_fmt" = "Gespeichert als „%@“. Zum Verwenden die Endung .txt entfernen."; -"web_drop_impossible" = "Ablegen nicht möglich"; -"web_sim_delay" = "Pause pro Schritt"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Sprache"; -"web_codegen_comments" = "Diagramm-Text als Kommentare"; -"web_codegen_empty_else" = "Leeren else-Zweig im Code ausgeben"; -"web_copied_check" = "Kopiert ✓"; -"web_copy_manual" = "Markiert, mit Strg+C kopieren"; -"web_run_stopped_loop" = "Ausführung gestoppt: mögliche Endlosschleife."; -"web_run_cancelled" = "Ausführung abgebrochen."; -"web_run_error" = "Fehler beim Ausführen."; -"web_run_dropped_fmt" = "… %d frühere Zeilen ausgeblendet"; - -/* Lernbereich (Lektionen gibt es vorerst nur auf Deutsch) */ -"web_learn_title" = "Lernen"; -"web_learn_hide" = "Lernbereich einklappen"; -"web_learn_show" = "Lernbereich aufklappen: Lektionen, Aufgaben und Nachschlagen"; -"web_learn_lessons" = "Lektionen"; -"web_learn_tasks" = "Aufgaben"; -"web_learn_reference" = "Nachschlagen"; -"web_learn_intro" = "Schritt für Schritt lernen, mit Übungen. Dein Fortschritt wird in diesem Browser gemerkt."; -"web_learn_tasks_intro" = "Kleine Projekte ohne Anleitung. Jede Aufgabe nennt die Lektion, die du vorher abgeschlossen haben solltest."; -"web_learn_step_of_fmt" = "Schritt %d von %d"; -"web_learn_lesson_fmt" = "Lektion %d"; -"web_learn_all_lessons" = "Alle Lektionen"; -"web_learn_all_tasks" = "Alle Aufgaben"; -"web_learn_back" = "Zurück"; -"web_learn_next" = "Weiter"; -"web_learn_finish" = "Lektion abschließen"; -"web_learn_done" = "Abgeschlossen"; -"web_learn_after_fmt" = "Nach Lektion %d: %@"; -"web_learn_level_fmt" = "Schwierigkeit %d von 3"; -"web_learn_exercise" = "Übung"; -"web_learn_brag" = "Angeberwissen mit Ludwig"; -"web_learn_solved" = "geschafft"; -"web_learn_open_exercise" = "Im Editor öffnen"; -"web_learn_solve_in_editor" = "Löse diese Übung im %@."; -"web_learn_solve_in_code" = "Löse diese Übung im %@."; -"web_learn_solve_in_both" = "Löse diese Übung im %1$@ und im %2$@."; -"web_learn_open_in_editor" = "Im Editor öffnen"; -"web_learn_replay" = "Noch einmal abspielen"; -"web_learn_graphic_labels" = "👆 Klick aufs Bild: Beschriftung ein oder aus"; -"web_learn_check" = "Prüfen"; -"web_learn_hint" = "Tipp"; -"web_learn_reset" = "Von vorn"; -"web_learn_reset_confirm" = "Wirklich von vorn?"; -"web_learn_solution" = "Musterlösung ansehen"; -"web_learn_hint_more" = "Noch ein Tipp"; -"web_learn_hint_fmt" = "Tipp %d von %d"; -"web_learn_teacher" = "Für Lehrkräfte"; -"web_learn_teacher_intro" = "Mit dem Lehrercode werden bei allen Übungen die Musterlösungen sichtbar. Der Browser merkt sich das."; -"web_learn_teacher_code" = "Lehrercode"; -"web_learn_teacher_unlock" = "Freischalten"; -"web_learn_teacher_wrong" = "Der Code stimmt nicht."; -"web_learn_teacher_on" = "Lehrermodus ist an: Bei allen Übungen sind die Musterlösungen sichtbar."; -"web_learn_teacher_off" = "Lehrermodus beenden"; -"web_learn_open_first" = "Öffne die Übung zuerst im Editor und baue dort dein Struktogramm."; -"web_learn_empty" = "Dein Struktogramm ist noch leer. Ziehe zuerst Elemente aus dem Bereich „Elemente“ hinein."; -"web_learn_passed" = "Geschafft! Dein Struktogramm macht genau, was es soll."; -"web_learn_failed" = "Noch nicht ganz. Vergleiche die erwartete mit deiner Ausgabe:"; -"web_learn_missing_fmt" = "Dein Struktogramm braucht noch: %@"; -"web_learn_input_fmt" = "Eingabe: %@"; -"web_learn_no_input" = "Ohne Eingabe"; -"web_learn_expected" = "Erwartet"; -"web_learn_actual" = "Deine Ausgabe"; -"web_learn_missing_input" = "Dein Struktogramm fragt nach mehr Eingaben als vorgesehen."; -"web_learn_unused_input" = "Dein Struktogramm fragt nicht alle Eingaben ab."; -"web_learn_missing_line_break" = "Fast! Es fehlt nur ein Zeilenumbruch. System.out.print schreibt weiter in derselben Zeile, System.out.println beginnt danach eine neue."; -"web_learn_too_many_steps" = "Abbruch nach 100 000 Schritten. Läuft eine Schleife endlos?"; -"web_learn_score_fmt" = "%d von %d richtig"; -"web_learn_cloze_hint" = "Tippe die fehlenden Wörter in die Lücken und drücke „Prüfen“."; -"web_learn_cloze_score_fmt" = "%d von %d Lücken richtig"; -"web_learn_cloze_all_right" = "Alle Lücken richtig!"; -"web_learn_cloze_solution" = "Lösung zeigen"; -"web_learn_cloze_again" = "Nochmal"; -"web_learn_pap_hint" = "Tippe ein Element rechts an und dann seinen Platz im Plan. Du kannst es auch hinziehen. Ein belegter Platz gibt sein Element beim Antippen zurück."; -"web_learn_pap_score_fmt" = "%d von %d Elementen richtig"; -"web_learn_pap_all_right" = "Alles richtig! Dein PAP ist fertig."; -"web_learn_pap_missing_fmt" = "Es fehlen noch %d Elemente."; -"web_learn_pap_empty" = "Leerer Platz"; -"web_learn_pap_yes" = "ja"; -"web_learn_pap_no" = "nein"; -"web_learn_pap_legend_oval" = "Start oder Ende"; -"web_learn_pap_legend_rect" = "Anweisung"; -"web_learn_pap_legend_diamond" = "Verzweigung: Frage mit ja und nein"; -"web_learn_pap_legend_para" = "Ein- oder Ausgabe"; -"web_learn_review" = "Nachlesen"; -"web_learn_terms" = "Fachbegriffe"; -"web_inspector_resize" = "Höhe des Objektinspektors ändern: Linie nach oben oder unten ziehen oder wischen (Doppelklick: Standard)"; -"web_learn_resize" = "Breite des Lernbereichs ändern (Doppelklick: zurücksetzen)"; -"web_learn_competences_intro" = "Am Ende dieser Lektion kann ich …"; -"web_learn_rating_sun" = "kann ich sicher"; -"web_learn_rating_cloud" = "teilweise"; -"web_learn_rating_umbrella" = "noch nicht"; -"web_learn_review_title" = "Deine Checkliste im Vergleich"; -"web_learn_review_hint" = "Hier weichen deine Einschätzung und der Test voneinander ab. Lies den Schritt noch einmal nach und schätze dich in der Checkliste neu ein."; -"web_learn_review_match" = "Deine Einschätzung passt zu deinem Test. Gut gemacht!"; -"web_learn_review_overrated_fmt" = "Du hast ☀️ gewählt, aber Frage %@ war falsch."; -"web_learn_review_underrated" = "Alle Fragen dazu waren richtig. Du kannst mehr, als du denkst!"; -"web_learn_review_unrated" = "noch nicht eingeschätzt"; -"web_learn_back_to_checklist" = "Zurück zur Checkliste"; -"web_learn_step_fmt" = "Schritt %d: %@"; -"web_sim_call_arguments_fmt" = "Der Aufruf passt nicht zur Kopfzeile von %@. Prüfe die Werte in den Klammern."; -"web_sim_missing_return_fmt" = "%@ hat keinen Wert mit return zurückgegeben."; -"web_sim_file_not_found_fmt" = "Es gibt keine Datei %@."; -"web_sim_end_of_file_fmt" = "Die Datei %@ hat keine Zeile mehr."; -"web_sim_null_value_fmt" = "%@ ist null."; -"web_sim_unknown_variable_case_fmt" = "Unbekannte Variable: %1$@. Java unterscheidet Groß- und Kleinschreibung: Meintest du %2$@?"; -"web_sim_written_file_fmt" = "%@ (gespeichert)"; -"web_site_home" = "Startseite"; -"web_site_imprint" = "Impressum"; -"web_site_privacy" = "Datenschutz"; - -/* Code (früher „Code live“, dann „Programmieren“) */ -"web_live_title" = "Code"; -"web_live_hide" = "Code ausblenden"; -"web_scale_canvas" = "Textgröße Struktogramm"; -"web_scale_code" = "Textgröße Code"; -"web_live_indent" = "Einrücken"; -"web_live_indent_title" = "Code einrücken und formatieren: jede } auf eigener Zeile, Leerzeichen um Operatoren, jede Ebene vier Leerzeichen tiefer (markierte Zeilen, sonst alles)"; -"web_live_show" = "Code aufklappen"; -"web_live_resize" = "Breite des Codebereichs ändern (Doppelklick: zurücksetzen)"; -"web_live_hint" = "Der Code wächst mit deinem Struktogramm. Grau = Platzhalter: Schreib im Objektinspektor, dann steht dort dein Text. Klick auf eine Zeile markiert das Element."; -"web_live_ph_selector" = "Variable"; -"web_live_ph_code" = "Dein Code"; -"web_live_ph_end" = "Ende"; -"web_live_language" = "Programmiersprache"; -"web_live_java_class" = "Als Klasse"; -"web_live_java_class_hint" = "Klassenname = Titel des Struktogramms"; -"web_live_java_main" = "Mit main-Methode"; -"web_settings_display" = "Darstellung"; -"web_live_swing" = "Echtes Java (mit Swing)"; -"web_live_swing_hint" = "Echtes Java mit Fenstern (CheerpJ). Lädt beim ersten Ausführen einmalig ca. 40 MB."; -"web_live_swing_windows" = "Java-Fenster"; -"web_live_swing_hide" = "Java-Fenster schließen"; -"web_live_swing_loading" = "Java wird geladen (beim ersten Mal ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompiliere …"; -"web_live_swing_running" = "Programm läuft."; -"web_live_swing_compile_failed" = "Fehler beim Kompilieren, siehe oben."; -"web_live_swing_failed" = "Java konnte nicht geladen werden. Prüfe die Internetverbindung."; -"web_live_hint_edit" = "Tippe hier Java-Code, dann baut sich das Struktogramm mit. Oder arbeite im Struktogramm, dann wächst der Code mit. Grau = Platzhalter, einfach drüberschreiben."; -"web_live_edit_label" = "Java-Code"; -"web_parse_line_fmt" = "Zeile %d: %@"; -"web_parse_missing" = "Hier fehlt noch: %@"; -"web_parse_extra" = "Das ist zu viel: %@"; -"web_parse_unclosed_string" = "Hier fehlt das schließende %@."; -"web_parse_else_without_if" = "Zu diesem „else“ gehört kein „if“."; -"web_parse_case_outside_switch" = "„case“ und „default“ gehören in ein „switch“."; -"web_parse_missing_do_while" = "Nach „do { … }“ fehlt noch „while (Bedingung);“."; -"web_parse_more_methods" = "Hier zählt im Moment nur eine Methode. Lege weitere Unterprogramme als eigene Reiter an."; -"web_parse_missing_import" = "Ganz oben fehlt %@ Ohne diese Zeile kennt Java den Scanner nicht."; -"web_parse_missing_scanner" = "Vorher fehlt %@ Ohne diese Zeile gibt es den Scanner nicht."; -"web_parse_unknown_type" = "Diesen Datentyp kennt Java nicht. Meintest du %@?"; -"web_quickfix_insert_fmt" = "Vorschlag: %@ einfügen"; -"web_quickfix_replace_fmt" = "Vorschlag: %1$@ statt %2$@"; -"web_parse_wrong_case" = "Java unterscheidet Groß- und Kleinschreibung. Richtig ist %@."; -"web_editor_title" = "Struktogramm"; -"web_editor_show" = "Struktogramm aufklappen"; -"web_editor_hide" = "Struktogramm einklappen"; -"web_keywords_title" = "Code-Keywords"; -"web_keywords_hint" = "if, while, for statt Verzweigung, For-Schleife …"; -"web_palette_compact" = "Elemente immer kompakt"; -"web_palette_compact_hint" = "Symbol mit kleinem Namen darunter, sonst nur bei schmaler Spalte"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Verwendete Software"; -"web_about_software_text" = "Der Code-Editor nutzt CodeMirror und Lezer (MIT-Lizenz). Mit Swing-Unterstützung lädt VisuStruct CheerpJ von Leaning Technologies (Community-Lizenz) und javac aus OpenJDK."; -"web_about_licenses" = "Lizenztexte"; -"web_live_run" = "Ausführen"; -"web_live_run_title" = "Programm ausführen: Ausgaben und Eingaben erscheinen darunter"; -"web_live_run_finished" = "Programm beendet."; -"web_live_run_stopped" = "Gestoppt."; -"web_console" = "Konsole"; -"web_console_hide" = "Konsole ausblenden"; -"web_console_resize" = "Höhe der Konsole ändern"; -"web_diagram_tools" = "Werkzeuge für das Struktogramm"; -"web_files_title" = "Dateien"; -"web_files_show" = "Dateien einblenden"; -"web_files_hide" = "Dateien ausblenden"; -"web_files_resize" = "Breite der Dateien ändern"; -"web_files_new_folder" = "Neuer Ordner"; -"web_files_switch" = "Anderen Ordner wählen"; -"web_files_delete" = "Löschen"; -"web_files_in_browser" = "Im Speicher dieses Browsers"; -"web_files_save_hint_fmt" = "Speichern legt das Programm in %@ ab."; -"web_files_empty" = "Noch nichts gespeichert. Beim Speichern legt VisuStruct hier einen Ordner mit dem heutigen Datum an."; -"web_files_reconnect_hint" = "Der Browser fragt nach dem Neuladen noch einmal, ob VisuStruct deinen Ordner benutzen darf."; -"web_files_reconnect_fmt" = "Ordner „%@“ öffnen"; -"web_files_connect_hint" = "Wähle einen Ordner als Arbeitsbereich, am besten einen neuen Ordner „VisuStruct-Dateien“ in deinen Dokumenten. VisuStruct legt darin für jeden Tag einen Ordner an und speichert Struktogramm und Code."; -"web_files_connect" = "Ordner wählen"; -"web_files_browser_hint" = "Dieser Browser kann keine Ordner auf dem Gerät öffnen (z. B. auf dem iPad). Deine Dateien bleiben dann im Speicher dieses Browsers."; -"web_files_browser" = "Im Browser speichern"; -"web_files_delete_confirm_fmt" = "„%@“ wirklich löschen?"; -"web_files_folder_prompt" = "Name des neuen Ordners:"; -"web_files_default_name" = "Programm"; -"web_files_saved_fmt" = "Gespeichert: %@"; -"web_files_create" = "Anlegen"; -"web_learn_ludwig_tip" = "Ludwigs Tipp"; -"web_game_title" = "Spiel: Wahr oder falsch?"; -"web_game_intro" = "Ludwig zeigt dir Werte und eine Bedingung. Ist die Bedingung wahr (true) oder falsch (false)? Zehn Runden, es wird von Runde zu Runde kniffliger."; -"web_game_start" = "Spiel starten"; -"web_game_round_fmt" = "Runde %@ von %@"; -"web_game_score_fmt" = "Punkte: %@"; -"web_game_true" = "wahr (true)"; -"web_game_false" = "falsch (false)"; -"web_game_right" = "Richtig!"; -"web_game_wrong" = "Leider falsch. So wird gerechnet:"; -"web_game_next" = "Weiter"; -"web_game_finish" = "Ergebnis"; -"web_game_done_fmt" = "%@ von %@ richtig."; -"web_game_perfect" = "Alles richtig – Ludwig ist schwer beeindruckt!"; -"web_game_good" = "Stark! Schau dir die Fehler noch einmal an und versuch es nochmal."; -"web_game_practice" = "Übung macht den Meister. Lies dir die Wahrheitstabelle oben noch einmal durch und spiel noch eine Runde."; -"web_game_again" = "Noch einmal"; -"web_game_wrong_short" = "Leider falsch."; -"web_game_practice_short" = "Übung macht den Meister. Lies dir die Erklärungen noch einmal durch und spiel noch eine Runde."; -"web_game_clicks" = "Klicks:"; -"web_game_nobody" = "Niemand hört zu – nichts passiert."; -"web_game_event_fmt" = "Klick auf „%@“: Der Zuhörer hört es und führt seinen Code aus."; -"web_game_try" = "Probier die Knöpfe im Fenster jetzt selbst aus."; -"web_game_time_fmt" = "Noch %@ s"; -"web_game_defused" = "✂️ Entschärft! Du hast den Fehler gefunden."; -"web_game_boom_time" = "💥 Bumm! Die Zeit ist abgelaufen."; -"web_game_boom_wrong" = "💥 Bumm! Das war die falsche Zeile."; -"web_game_boom_part" = "💥 Bumm! Die Zeile stimmt, aber der Fehler steckt in einem anderen Teil."; -"web_game_boom_wrong_part" = "💥 Bumm! Der Fehler steckt in einer anderen Zeile."; -"web_game_bomb_parts_hint" = "Tippe genau auf den falschen Teil."; -"web_game_fix" = "So stimmt die Zeile:"; -"web_game_order_hint" = "Tippe die Schritte in der richtigen Reihenfolge an."; -"web_debug_step" = "Einzelschritt"; -"web_debug_step_title" = "Einzelschritt: eine Anweisung ausführen und wieder anhalten"; -"web_debug_continue" = "Weiter bis zum nächsten Haltepunkt"; -"web_debug_paused_fmt" = "Angehalten vor Zeile %d."; -"web_debug_gutter_title" = "Auf die Zeilennummer klicken: Haltepunkt setzen oder entfernen"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Caesar-Scheibe"; -"web_learn_caesar_hint" = "Drehe die innere Scheibe oder tippe auf −1 und +1. Außen steht der Klartext, innen der Geheimtext."; -"web_learn_caesar_key_fmt" = "Schlüssel %d"; -"web_learn_caesar_plain" = "Klartext"; -"web_learn_caesar_secret" = "Geheimtext"; -"web_learn_caesar_encrypt" = "Verschlüsseln"; -"web_learn_caesar_decrypt" = "Entschlüsseln"; -"web_learn_caesar_back" = "Scheibe eins zurück"; -"web_learn_caesar_forward" = "Scheibe eins weiter"; -"web_learn_rescue_station_fmt" = "Station %@ von %@: %@"; -"web_learn_rescue_number" = "Deine Zahl"; -"web_learn_rescue_not_number" = "Gib eine ganze Zahl ein."; -"web_learn_rescue_wrong" = "Noch nicht. Lies den Code noch einmal Zeichen für Zeichen."; -"web_learn_rescue_letter_moved" = "Der Buchstabe wandert ins Kreuzworträtsel."; -"web_learn_rescue_alphabet" = "Aus deiner Zahl wird ein Buchstabe:"; -"web_learn_rescue_to_crossword" = "Zum Kreuzworträtsel"; -"web_learn_rescue_crossword" = "Das Kreuzworträtsel"; -"web_learn_rescue_across" = "→ Waagrecht"; -"web_learn_rescue_down" = "↓ Senkrecht"; -"web_learn_rescue_cell_fmt" = "Feld Zeile %@, Spalte %@"; -"web_learn_rescue_keyword" = "Geheimes Wort:"; -"web_learn_rescue_cw_wrong" = "Die rot markierten Felder stimmen noch nicht."; -"web_learn_rescue_cw_done" = "Gelöst! Die gelben Felder ergeben das geheime Wort."; -"web_learn_rescue_to_gate" = "Zum Tor"; -"web_learn_rescue_gate" = "Das Tor"; -"web_learn_rescue_word" = "Geheimes Wort"; -"web_learn_rescue_open" = "Tor öffnen"; -"web_learn_rescue_gate_case" = "Fast! equals unterscheidet Groß- und Kleinbuchstaben. Schreib das Wort genau so wie im Rätsel."; -"web_learn_rescue_gate_wrong" = "Das Tor bleibt zu. Schau noch einmal auf die gelben Felder."; -"web_learn_rescue_freed" = "🎉 Das Tor ist offen!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "Womit möchtest du arbeiten?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: Struktogramme und Java mit dem ganzen Kurs. Wechseln geht jederzeit im Zahnrad."; -"web_visubot_world_visubot_hint" = "VisuBot: den Roboter mBot2 programmieren, mit eigenen Lektionen und Aufgaben. Wechseln geht jederzeit im Zahnrad."; -"web_visubot_connect" = "Mit echtem mBot2 verbinden"; -"web_visubot_upload_button" = "Übertragen"; -"web_visubot_blocks" = "Blöcke"; -"web_visubot_blocks_title" = "Das Struktogramm als Blöcke zeigen"; -"web_visubot_blocks_back" = "Struktogramm"; -"web_visubot_blocks_back_title" = "Zurück zum Struktogramm"; -"web_visubot_blocks_start" = "Wenn das Programm startet"; -"web_visubot_blocks_empty" = "Hier kommt ein Block hin"; -"web_visubot_blocks_hint" = "Dasselbe Programm als Blöcke. Ziehe Blöcke aus „Elemente“ hinein, verschiebe sie oder wirf sie in den Papierkorb. Zahlen und Texte in den weißen Feldern änderst du per Klick direkt im Block, alles andere unten im Objektinspektor. Sensoren ziehst du auf eine Bedingung oder genau auf ein Feld, das sie ersetzen sollen. Operatoren und Variablen ziehst du genauso auf ein Feld oder eine Bedingung."; -"web_visubot_blocks_trash" = "Papierkorb"; -"web_visubot_blocks_count" = "zähle mit"; -"web_visubot_blocks_count_from" = "von"; -"web_visubot_blocks_count_below" = "bis unter"; -"web_visubot_blocks_count_to" = "bis"; -"web_visubot_blocks_count_above" = "bis über"; -"web_visubot_blocks_count_step" = "in Schritten von"; -"web_visubot_blocks_repeat" = "wiederhole"; -"web_visubot_blocks_if" = "wenn"; -"web_visubot_blocks_else" = "sonst"; -"web_visubot_blocks_while" = "solange"; -"web_visubot_blocks_endless" = "wiederhole immer"; -"web_visubot_blocks_do" = "mache"; -"web_visubot_blocks_not" = "nicht"; -"web_visubot_blocks_and" = "und"; -"web_visubot_blocks_or" = "oder"; -"web_visubot_blocks_break" = "verlasse die Schleife"; -"web_visubot_blocks_repeat_times" = "mal"; -"web_visubot_blocks_random_from" = "Zufallszahl von"; -"web_visubot_blocks_random_to" = "bis"; -"web_visubot_operators" = "Operatoren"; -"web_visubot_variables" = "Variablen"; -"web_visubot_control" = "Steuerung"; -"web_visubot_el_repeat" = "wiederhole 10 mal"; -"web_visubot_upload_title" = "Programm auf den mBot2 übertragen"; -"web_visubot_upload_step1" = "Schließe den mBot2 mit dem USB-Kabel an und schalte ihn ein. Das geht nur in Chrome oder Edge."; -"web_visubot_upload_step2" = "Klicke auf „Jetzt übertragen“ und wähle „USB Serial“. Der mBot2 speichert dein Programm und startet neu."; -"web_visubot_upload_step3" = "Zieh das Kabel ab und setze den mBot2 an den Start. Drückst du Taste A, fährt dein Programm los. Es bleibt gespeichert, auch nach dem Ausschalten."; -"web_visubot_upload_start" = "Jetzt übertragen"; -"web_visubot_upload_busy" = "Wird übertragen … Kabel nicht abziehen."; -"web_visubot_upload_done" = "Fertig! Kabel ab, mBot2 an den Start, Taste A drücken."; -"web_visubot_upload_failed" = "Das Übertragen hat nicht geklappt. Starte den mBot2 neu und versuche es noch einmal."; -"web_visubot_upload_label" = "A = Start"; -"web_visubot_upload_error_input" = "Dein Programm fragt nach einer Eingabe. Am mBot2 gibt es keine Tastatur, deshalb geht das nicht."; -"web_visubot_upload_error_files" = "Dein Programm arbeitet mit Dateien. Das geht am mBot2 nicht."; -"web_visubot_upload_error_empty" = "Dein Programm ist noch leer."; -"web_visubot_disconnect" = "Trennen"; -"web_visubot_connecting" = "Verbinde … (der CyberPi startet kurz neu)"; -"web_visubot_connected" = "Echter mBot2 fährt mit"; -"web_visubot_no_serial" = "Echter mBot2: nur in Chrome oder Edge am Computer."; -"web_visubot_link_error" = "Verbindung getrennt"; -"web_visubot_world" = "VisuBot-Welt"; -"web_visubot_no_3d" = "Die 3D-Ansicht kann in diesem Browser nicht starten."; -"web_visubot_distance" = "Abstand"; -"web_visubot_line" = "Linie"; -"web_visubot_bumps" = "an die Wand gestoßen"; -"web_visubot_el_forward" = "fahre vor"; -"web_visubot_el_backward" = "fahre zurück"; -"web_visubot_el_right" = "drehe rechts"; -"web_visubot_el_left" = "drehe links"; -"web_visubot_el_led" = "LEDs an"; -"web_visubot_el_tone" = "Ton spielen"; -"web_visubot_drive" = "Fahrt"; -"web_visubot_depth_fmt" = "Die Schleifen müssen %d-fach ineinander stecken."; -"web_visubot_tap_part" = "Dreh den mBot2 mit der Maus oder dem Finger. Tippst du ein Teil an, siehst du seinen Namen."; -"web_visubot_quiz_right" = "Richtig!"; -"web_visubot_quiz_wrong_fmt" = "Das ist: %@. Versuch es noch einmal."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Bildschirm"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Tasten A, B und Home"; -"web_visubot_part_leds" = "RGB-LEDs"; -"web_visubot_part_lightSensor" = "Lichtsensor"; -"web_visubot_part_microphone" = "Mikrofon"; -"web_visubot_part_shield" = "mBot2-Shield mit Akku"; -"web_visubot_part_powerSwitch" = "Ein-Aus-Schalter"; -"web_visubot_part_motors" = "Encoder-Motoren"; -"web_visubot_part_wheels" = "Räder"; -"web_visubot_part_ultrasonic" = "Ultraschallsensor"; -"web_visubot_part_lineSensor" = "Liniensensor (Quad-RGB)"; -"web_visubot_part_caster" = "Kugelrolle"; -"web_visubot_part_chassis" = "Chassis"; -"web_visubot_world_fmt" = "Welt %d"; -"web_visubot_goal_reached" = "Ziel erreicht"; -"web_visubot_goal_missed" = "Ziel nicht erreicht"; -"web_visubot_commands" = "mBot2-Befehle"; -"web_visubot_sensors" = "mBot2-Sensoren"; -"web_visubot_sensor_distance" = "Abstand"; -"web_visubot_sensor_line" = "Linie"; -"web_visubot_el_led_off" = "LEDs aus"; -"web_visubot_el_led_color" = "LED-Farbe setzen"; -"web_visubot_el_wait" = "kurz warten"; -"web_visubot_el_emotion" = "Gefühl zeigen"; -"web_visubot_el_text" = "Text anzeigen"; -"web_visubot_sensor_color" = "Farbe ist rot?"; -"web_visubot_sensor_button_a" = "Taste A gedrückt?"; -"web_visubot_sensor_button_b" = "Taste B gedrückt?"; -"web_visubot_sensor_joy_up" = "Joystick oben?"; -"web_visubot_sensor_joy_down" = "Joystick unten?"; -"web_visubot_sensor_joy_left" = "Joystick links?"; -"web_visubot_sensor_joy_right" = "Joystick rechts?"; -"web_visubot_sensor_brightness" = "Helligkeit"; -"web_visubot_sensor_loudness" = "Lautstärke"; -"web_visubot_sensor_pitch" = "Neigung vorn/hinten"; -"web_visubot_sensor_roll" = "Neigung seitlich"; -"web_visubot_sensor_shake" = "geschüttelt?"; -"web_visubot_el_drive" = "Räder einzeln"; -"web_visubot_el_stop" = "anhalten"; -"web_visubot_el_timer" = "Stoppuhr auf 0"; -"web_visubot_sensor_line_left" = "Linie links?"; -"web_visubot_sensor_line_right" = "Linie rechts?"; -"web_visubot_sensor_timer" = "Stoppuhr (s)"; -"web_visubot_sensor_battery" = "Akku (%)"; -"web_visubot_actions" = "Befehle"; -"web_visubot_led_on" = "LEDs an"; -"web_visubot_led_off" = "LEDs aus"; -"web_visubot_tone_yes" = "Ton"; -"web_visubot_tone_no" = "kein Ton"; -"web_visubot_controls" = "Sensoren ausprobieren"; -"web_visubot_controls_hint" = "Ohne echten mBot2: Hier stellst du ein, was die Sensoren messen. Tasten, Joystick und Schütteln gelten eine Sekunde lang."; -"web_visubot_shake" = "schütteln"; -"web_visubot_check" = "mBot2 testen"; -"web_visubot_check_hint" = "Probiere jeden Aktor einmal aus. Die Sensorwerte laufen live mit: Halte die Hand vor den Ultraschall, drück die Tasten, neige oder schüttle den mBot2. Heb ihn für die Räder hoch."; -"web_visubot_check_forward" = "Räder 1 s vorwärts"; -"web_visubot_check_spin" = "1 s auf der Stelle drehen"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Wo speicherst du deine Arbeit?"; -"web_learn_start_hint" = "Lernstand und Programme liegen in einem Ordner auf deinem Gerät. Der Lernstand steht dort in der Datei learnProgress.json."; -"web_learn_start_continue_fmt" = "Weiter mit „%@“"; -"web_learn_start_open" = "Vorhandenen Ordner öffnen"; -"web_learn_start_create_fmt" = "Neuen Ordner „%@“ anlegen"; -"web_learn_start_create_hint" = "Danach wählst du, wo er liegen soll, z. B. in „Dokumente“."; -"web_learn_start_folder" = "VisuStruct-Dateien"; -"web_learn_start_without" = "Ohne Ordner weiter"; -"web_learn_start_without_hint" = "Dann ist dein Lernstand nach dem Schließen weg."; -"web_visubot_preview_title" = "mBot2 live"; -"web_visubot_preview_hint" = "Zeigt sofort, wohin dein Programm den mBot2 fährt."; -"web_visubot_preview_sim_title" = "Simulation"; -"web_visubot_preview_sim_hint" = "Hier fährt der mBot2 mit, während die Simulation läuft."; -"web_visubot_world_in_preview" = "Der mBot2 fährt im Fenster „Simulation“."; -"web_visubot_preview_play" = "Fahrt abspielen"; -"web_visubot_preview_stop" = "Anhalten"; -"web_visubot_preview_hide" = "mBot2 live einklappen"; -"web_visubot_preview_show" = "mBot2 live zeigen"; -"web_visubot_preview_resize" = "Größe von mBot2 live ändern"; -"web_visubot_preview_stack" = "Unter den Code im Bereich Code"; -"web_visubot_preview_side" = "Als eigene Spalte"; -"web_visubot_preview_distance_fmt" = "Strecke: %d cm"; -"web_visubot_preview_input" = "braucht eine Eingabe, dafür die Simulation starten"; -"web_visubot_preview_limit" = "läuft sehr lange (Endlosschleife?), gezeigt ist der Anfang"; -"web_visubot_group_drive" = "Fahren und Drehen"; -"web_visubot_group_wheels" = "Räder einzeln"; -"web_visubot_group_leds" = "Licht"; -"web_visubot_group_sound" = "Ton"; -"web_visubot_group_screen" = "Bildschirm"; -"web_visubot_group_emotion" = "Gefühle"; -"web_visubot_group_wait" = "Warten und Stoppuhr"; -"web_visubot_group_distance" = "Abstand"; -"web_visubot_group_line" = "Linie"; -"web_visubot_group_color" = "Farben"; -"web_visubot_group_buttons" = "Tasten und Joystick"; -"web_visubot_group_light" = "Licht und Lautstärke"; -"web_visubot_group_tilt" = "Neigung und Schütteln"; -"web_visubot_group_timer" = "Stoppuhr und Akku"; -"web_swap_to_code" = "Code groß, Struktogramm schmal"; -"web_swap_to_editor" = "Struktogramm groß, Code schmal"; -"web_editor_resize" = "Breite des Struktogramms ändern"; -"web_palette_resize" = "Breite der Elemente ändern (Doppelklick: zurücksetzen)"; -"web_palette_hide" = "Elemente einklappen"; -"web_palette_show" = "Elemente aufklappen"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: Klassen in Java programmieren und als Klassendiagramm sehen oder zeichnen (ab Klasse 12). Wechseln geht jederzeit im Zahnrad."; -"web_visuobject_subtitle" = "Klassendiagramm"; -"web_visuobject_code" = "Java-Code"; -"web_visuobject_diagram" = "Klassendiagramm"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Neues Interface ins Diagramm und in den Code"; -"web_visuobject_layout" = "Neu anordnen"; -"web_visuobject_layout_tip" = "Alle Klassen automatisch anordnen: Oberklassen oben"; -"web_visuobject_add_field" = "neues Attribut …"; -"web_visuobject_add_method" = "neue Methode …"; -"web_visuobject_delete_class" = "Klasse löschen"; -"web_visuobject_new_class_name" = "NeueKlasse"; -"web_visuobject_new_interface_name" = "NeuesInterface"; -"web_visuobject_example" = "Beispiel"; -"web_visuobject_example_tip" = "Beispielprojekt Zoo laden: Tier und Hund im Package tiere, Besitzer in personen"; -"web_visuobject_new_tip" = "Neu: leeres Projekt (Rückgängig holt das alte zurück)"; -"web_visuobject_empty" = "Noch keine Klasse. Klicke links auf „+ Klasse“ oder tippe zum Beispiel „public class Hund { }“ in eine Datei."; -"web_visuobject_edit_hint" = "Doppelklick ändert. Schreibweise wie in UML: „- name: String“ oder „+ bellen(): void“. Leer lassen löscht."; -"web_visuobject_member_label" = "Attribut oder Methode in UML-Schreibweise"; -"web_visuobject_name_label" = "Name der Klasse"; -"web_visuobject_bad_name" = "„%@“ ist kein gültiger Name oder gibt es schon."; -"web_visuobject_bad_line" = "„%@“ verstehe ich nicht. Beispiel: - name: String oder + bellen(): void"; -"web_visuobject_open_failed" = "Die Datei konnte nicht gelesen werden."; -"web_visuobject_field_ph" = "- name: String"; -"web_visuobject_seq_diagram" = "Sequenzdiagramm"; -"web_visuobject_seq_empty" = "Das Sequenzdiagramm entsteht beim Debuggen: Klicke oben auf „Debuggen“ und gehe Schritt für Schritt durch das Programm."; -"web_visuobject_seq_no_calls" = "Noch keine Aufrufe eigener Methoden."; -"web_visuobject_seq_truncated" = "… gekürzt: Ab hier sind es zu viele Aufrufe."; -"web_visuobject_seq_hint" = "Jeder Pfeil ist ein Aufruf einer eigenen Methode, gestrichelt kommt die Rückgabe zurück. Klick auf einen Pfeil zeigt die Zeile im Code."; -"web_visuobject_dbg" = "Debuggen"; -"web_visuobject_dbg_tip" = "Programm Schritt für Schritt ausführen. Haltepunkt: auf die Zeilennummer klicken. Das Objektdiagramm zeigt dabei die Objekte im Speicher."; -"web_visuobject_dbg_continue" = "Weiter"; -"web_visuobject_dbg_continue_tip" = "Weiter bis zum nächsten Haltepunkt (F8)"; -"web_visuobject_dbg_pause" = "Pause"; -"web_visuobject_dbg_pause_tip" = "Programm anhalten"; -"web_visuobject_dbg_over" = "Schritt"; -"web_visuobject_dbg_over_tip" = "Nächste Zeile; Methoden laufen ganz durch (F6)"; -"web_visuobject_dbg_into" = "Hinein"; -"web_visuobject_dbg_into_tip" = "In die aufgerufene Methode hineingehen (F5)"; -"web_visuobject_dbg_out" = "Heraus"; -"web_visuobject_dbg_out_tip" = "Methode zu Ende ausführen und zurück zum Aufrufer (F7)"; -"web_visuobject_dbg_input" = "Eingabe für den Scanner, dann Enter"; -"web_visuobject_dbg_running" = "Debugger: Programm läuft …"; -"web_visuobject_dbg_waiting" = "Debugger: Das Programm wartet auf deine Eingabe."; -"web_visuobject_dbg_paused_fmt" = "Debugger: angehalten in %1$@, Zeile %2$@"; -"web_visuobject_dbg_exception" = "Programm mit einer Exception abgebrochen."; -"web_visuobject_dbg_code_changed" = "Code geändert: Debugger beendet."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Aufrufstapel"; -"web_visuobject_dbg_vars" = "Variablen"; -"web_visuobject_dbg_var_name" = "Name"; -"web_visuobject_dbg_var_type" = "Typ"; -"web_visuobject_dbg_no_vars" = "Keine Variablen"; -"web_visuobject_dbg_err_syntax" = "%1$@, Zeile %2$@: Das versteht der Debugger nicht (%3$@). Mit ▶ Start siehst du die Meldung von Java."; -"web_visuobject_dbg_err_symbol" = "%1$@, Zeile %2$@: %3$@ ist unbekannt."; -"web_visuobject_dbg_err_import" = "%1$@, Zeile %2$@: Ganz oben fehlt %3$@ Ohne diese Zeile kennt Java die Klasse nicht."; -"web_visuobject_dbg_err_typename" = "%1$@, Zeile %2$@: Diesen Datentyp kennt Java nicht. Meintest du %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, Zeile %2$@: %3$@ kann der Debugger noch nicht. Mit ▶ Start läuft das Programm in echtem Java."; -"web_visuobject_dbg_err_type" = "%1$@, Zeile %2$@: Hier passt etwas nicht zusammen (%3$@)."; -"web_visuobject_obj_diagram" = "Objektdiagramm"; -"web_visuobject_obj_new" = "Neues Objekt der Klasse"; -"web_visuobject_obj_add" = "Objekt"; -"web_visuobject_obj_empty" = "Noch keine Objekte. Wähle oben eine Klasse und klicke auf „+ Objekt“."; -"web_visuobject_obj_no_classes" = "Lege zuerst im Klassendiagramm eine Klasse an (nicht abstract)."; -"web_visuobject_obj_hint" = "Klicke auf ein Objekt, dann kannst du hier Name, Klasse und Werte ändern. Vom Griff am Objekt zu einem anderen ziehen verbindet sie über ein Referenzattribut."; -"web_visuobject_obj_live_hint" = "Live aus dem Debugger: So sehen die Objekte gerade im Speicher aus."; -"web_visuobject_obj_link" = "Zu einem anderen Objekt ziehen: Referenz setzen"; -"web_visuobject_obj_name" = "Name"; -"web_visuobject_obj_class" = "Klasse"; -"web_visuobject_obj_attribute" = "Attribut"; -"web_visuobject_obj_value" = "Wert"; -"web_visuobject_obj_delete" = "Objekt löschen"; -"web_visuobject_obj_bad_name" = "„%@“ ist kein gültiger Name für ein Objekt."; -"web_visuobject_obj_name_taken" = "Ein Objekt „%@“ gibt es schon."; -"web_visuobject_obj_no_ref" = "%1$@ hat kein Attribut, das auf %2$@ zeigen kann."; -"web_visuobject_tool_note" = "Notiz"; -"web_visuobject_tool_note_tip" = "Notiz: Klicke ins Klassendiagramm, dann tippe den Text. Mit dem Griff ziehst du eine gestrichelte Linie zu einer Klasse."; -"web_visuobject_note_ph" = "Notiz …"; -"web_visuobject_note_text" = "Text"; -"web_visuobject_note_attached" = "Verbunden mit"; -"web_visuobject_note_attach" = "Zu einer Klasse ziehen: gestrichelte Linie"; -"web_visuobject_note_delete" = "Notiz löschen"; -"web_visuobject_run" = "Start"; -"web_visuobject_run_tip" = "Programm mit echtem Java starten: alle Klassen werden übersetzt, gestartet wird die Klasse mit main (bevorzugt die offene)."; -"web_visuobject_stop" = "Stop"; -"web_visuobject_stop_tip" = "Programm beenden und seine Fenster schließen"; -"web_visuobject_stopped" = "Programm beendet."; -"web_visuobject_no_main" = "Keine Klasse mit public static void main(String[] args) gefunden."; -"web_visuobject_running_fmt" = "%@ läuft …"; -"web_visuobject_console_clear" = "Leeren"; -"web_visuobject_section_fields" = "Attribute"; -"web_visuobject_section_refs" = "Referenzattribute"; -"web_visuobject_section_ctors" = "Konstruktor"; -"web_visuobject_section_methods" = "Methoden"; -"web_visuobject_insp_sections" = "Abschnitts-Kommentare einfügen"; -"web_visuobject_insp_sort" = "Einsortieren"; -"web_visuobject_insp_sections_tip" = "Schreibt // Attribute, // Referenzattribute, // Konstruktor und // Methoden in die Klasse und sortiert alles darunter. Neues aus dem Klassendiagramm kommt dann gleich an die richtige Stelle."; -"web_visuobject_insp_doc" = "Beschreibung (Javadoc)"; -"web_visuobject_insp_doc_ph" = "Wofür ist die Klasse da? Steht im Code als Javadoc-Kommentar über der Klasse."; -"web_visuobject_insp_visibility" = "Sichtbarkeit"; -"web_visuobject_insp_type" = "Typ"; -"web_visuobject_insp_init" = "Startwert"; -"web_visuobject_insp_no_fields" = "Noch keine Attribute."; -"web_visuobject_insp_add_field" = "Attribut"; -"web_visuobject_insp_params" = "Parameter"; -"web_visuobject_insp_return" = "Rückgabetyp"; -"web_visuobject_insp_constructor" = "Konstruktor"; -"web_visuobject_insp_no_methods" = "Noch keine Methoden."; -"web_visuobject_insp_add_method" = "Methode"; -"web_visuobject_insp_generate" = "Erzeugen:"; -"web_visuobject_insp_gen_constructor" = "Konstruktor aus Attributen"; -"web_visuobject_insp_gen_constructor_tip" = "Wie in Eclipse „Generate Constructor using Fields“: ein Konstruktor, der alle Attribute (ohne static) setzt."; -"web_visuobject_insp_gen_accessors" = "Getter und Setter"; -"web_visuobject_insp_gen_accessors_tip" = "Wie in Eclipse „Generate Getters and Setters“: für jedes Attribut get…/set… (final-Attribute nur Getter)."; -"web_visuobject_insp_bad_params" = "Parameter so schreiben: name: Typ, zweiter: int (gelesen wurde „%@“)."; -"web_visuobject_insp_fields" = "Attribute"; -"web_visuobject_insp_methods" = "Methoden"; -"web_visuobject_inspector_resize" = "Größe ändern: Grenze zwischen Klassendiagramm und Objektinspektor nach oben oder unten ziehen (Doppelklick = Standard)"; -"web_visuobject_dep_title" = "Abhängigkeit «%@»"; -"web_visuobject_dep_create_text" = "%1$@ erzeugt in %3$@ ein Objekt der Klasse %2$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ benutzt die Klasse %2$@ in %3$@."; -"web_visuobject_dep_hint" = "Das ist keine Assoziation: %@ merkt sich das %@-Objekt nicht in einem Attribut, es lebt nur in der Methode. Darum gestrichelt."; -"web_visuobject_dep_to_assoc" = "Als Attribut merken (Assoziation zu %@)"; -"web_visuobject_end_mult_ph" = "Mult."; -"web_visuobject_end_tip" = "Klick (Linie markiert) oder Rechtsklick: Rolle, Multiplizität und Navigierbarkeit an diesem Ende"; -"web_visuobject_end_role_ph" = "z. B. meineKatzen"; -"web_visuobject_new_menu" = "Neu"; -"web_visuobject_new_menu_tip" = "Neues Projekt, Source Folder, Package, Klasse oder Interface anlegen"; -"web_visuobject_new_project" = "Java-Projekt"; -"web_visuobject_new_folder" = "Source Folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Klasse"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "Neues Java-Projekt"; -"web_visuobject_new_project_hint" = "Im Workspace bekommt jedes Projekt einen eigenen Ordner mit src."; -"web_visuobject_new_folder_title" = "Neuer Source Folder"; -"web_visuobject_new_folder_hint" = "Ein weiterer Ordner für Quellcode neben src, z. B. test für Testklassen."; -"web_visuobject_new_package_title" = "Neues Package"; -"web_visuobject_new_package_hint" = "Package-Namen schreibt man klein, Teile mit Punkt: tiere oder schule.verwaltung."; -"web_visuobject_new_class_title" = "Neue Java-Klasse"; -"web_visuobject_new_class_hint" = "Klassennamen beginnen mit einem Großbuchstaben, z. B. Hund oder Bankkonto."; -"web_visuobject_new_interface_title" = "Neues Java-Interface"; -"web_visuobject_new_interface_hint" = "Ein Interface legt Methoden fest, die Klassen später implementieren."; -"web_visuobject_new_root" = "Source Folder"; -"web_visuobject_new_name" = "Name"; -"web_visuobject_new_modifiers" = "Modifier"; -"web_visuobject_new_package_private" = "package (ohne Modifier)"; -"web_visuobject_new_superclass" = "Oberklasse"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Erweitert Interfaces (extends)"; -"web_visuobject_new_stubs" = "Welche Methoden-Rümpfe sollen entstehen?"; -"web_visuobject_new_stub_constructors" = "Konstruktoren der Oberklasse"; -"web_visuobject_new_stub_inherited" = "Geerbte abstrakte Methoden"; -"web_visuobject_new_comment_legend" = "Kommentar"; -"web_visuobject_new_comment" = "Javadoc-Kommentar mit @author"; -"web_visuobject_new_author" = "Dein Name"; -"web_visuobject_new_finish" = "Fertigstellen"; -"web_visuobject_new_name_missing" = "Bitte gib einen Namen ein."; -"web_visuobject_new_exists" = "„%@“ gibt es im Projekt schon."; -"web_visuobject_new_upper" = "Klassennamen beginnen in Java mit einem Großbuchstaben."; -"web_visuobject_bad_folder" = "„%@“ ist kein gültiger Ordnername oder gibt es schon."; -"web_visuobject_inspector_hide" = "Objektinspektor einklappen"; -"web_visuobject_inspector_show" = "Objektinspektor aufklappen"; -"web_visuobject_inspector_empty" = "Klicke im Diagramm auf eine Beziehung (Linie), dann kannst du sie hier bearbeiten."; -"web_visuobject_rel_end_a" = "Ende A: %@ (Linie beginnt hier)"; -"web_visuobject_rel_end_b" = "Ende B: %@ (Linie endet hier)"; -"web_visuobject_ws_title" = "Workspace-Ordner auf deinem Rechner"; -"web_visuobject_ws_question" = "Wo speicherst du deine Projekte?"; -"web_visuobject_ws_hint" = "Wie in Eclipse: ein Workspace-Ordner auf deinem Rechner, darin ein Ordner je Projekt mit src und den .java-Dateien."; -"web_visuobject_ws_create" = "Ordner „%@“ anlegen"; -"web_visuobject_ws_open" = "Vorhandenen Workspace öffnen"; -"web_visuobject_ws_skip" = "Ohne Ordner (nur im Browser)"; -"web_visuobject_ws_choose" = "Workspace wählen"; -"web_visuobject_ws_reopen" = "Workspace „%@“ öffnen"; -"web_visuobject_ws_switch" = "Wechseln"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Projekt als .zip herunterladen, z. B. zum Abgeben"; -"web_visuobject_ws_saved" = "Gespeichert in %@/%@"; -"web_visuobject_ws_created" = "Neues Projekt „%@“ im Workspace"; -"web_visuobject_ws_failed" = "Speichern im Workspace hat nicht geklappt: %@"; -"web_visuobject_ws_taken" = "Im Workspace gibt es schon ein Projekt „%@“."; -"web_visuobject_ws_open_project" = "Projekt %@ öffnen"; -"web_visuobject_save_ws_tip" = "Im Workspace speichern (Strg+S)"; -"web_visuobject_unsaved" = "nicht gespeichert"; -"web_visuobject_rel_nav_navigable" = "navigierbar (Pfeil)"; -"web_visuobject_rel_nav_non-navigable" = "nicht navigierbar (Kreuz ×)"; -"web_visuobject_rel_nav_unspecified" = "nicht spezifiziert (offen)"; -"web_visuobject_rel_reading_flip" = "umdrehen"; -"web_visuobject_rel_reading_auto" = "Mit einem Namen liest sich die Beziehung in Zugrichtung."; -"web_visuobject_tools" = "Werkzeuge"; -"web_visuobject_tool_select" = "Auswahl"; -"web_visuobject_tool_select_tip" = "Auswählen und verschieben"; -"web_visuobject_tool_class" = "Klasse"; -"web_visuobject_tool_class_tip" = "Klasse: auf die Fläche ziehen oder wählen und auf die Fläche klicken"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: auf die Fläche ziehen oder wählen und auf die Fläche klicken"; -"web_visuobject_tool_link" = "Assoziation"; -"web_visuobject_tool_link_tip" = "Assoziation ohne Navigation: einfache Linie zwischen zwei Klassen ziehen"; -"web_visuobject_rel_link" = "Assoziation (ohne Navigation)"; -"web_visuobject_tool_not_link" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_association" = "Gerichtet"; -"web_visuobject_tool_association_tip" = "Gerichtete Assoziation: von der Klasse, die die andere kennt, zur anderen ziehen (Pfeil)"; -"web_visuobject_tool_aggregation" = "Aggregation"; -"web_visuobject_tool_aggregation_tip" = "Aggregation: vom Ganzen zum Teil ziehen (hat, Teile leben weiter)"; -"web_visuobject_tool_composition" = "Komposition"; -"web_visuobject_tool_composition_tip" = "Komposition: vom Ganzen zum Teil ziehen (besteht aus, Teile gehören fest dazu)"; -"web_visuobject_tool_extends" = "Vererbung"; -"web_visuobject_tool_extends_tip" = "Vererbung: von der Unterklasse zur Oberklasse ziehen (erbt von)"; -"web_visuobject_tool_implements" = "Realisierung"; -"web_visuobject_tool_implements_tip" = "Realisierung: von der Klasse zum Interface ziehen (implementiert)"; -"web_visuobject_tool_pick_second" = "Jetzt die zweite Klasse anklicken (von %@ aus). Esc bricht ab."; -"web_visuobject_tool_not_association" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_aggregation" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_composition" = "%@ ist ein Interface und kann keine Attribute haben. Zieh die Beziehung von einer Klasse aus."; -"web_visuobject_tool_not_extends" = "%@ kann nicht von %@ erben: Klassen erben von Klassen, Interfaces von Interfaces. Für ein Interface nimm Realisierung."; -"web_visuobject_tool_not_implements" = "%@ kann %@ nicht implementieren: Nur eine Klasse implementiert ein Interface."; -"web_visuobject_inspector" = "Objektinspektor"; -"web_visuobject_connect" = "Ziehen: Beziehung zu einer anderen Klasse"; -"web_visuobject_rel_association" = "Assoziation"; -"web_visuobject_rel_directed" = "Gerichtete Assoziation"; -"web_visuobject_rel_aggregation" = "Aggregation (hat, Teile leben weiter)"; -"web_visuobject_rel_composition" = "Komposition (besteht aus, Teile gehören fest dazu)"; -"web_visuobject_rel_extends" = "Vererbung (erbt von)"; -"web_visuobject_rel_implements" = "Realisierung (implementiert)"; -"web_visuobject_rel_extends_text" = "%@ erbt von %@ (extends im Code)."; -"web_visuobject_rel_implements_text" = "%@ implementiert das Interface %@ (implements im Code)."; -"web_visuobject_rel_kind" = "Art"; -"web_visuobject_rel_name" = "Name der Beziehung"; -"web_visuobject_rel_name_ph" = "z. B. besitzt"; -"web_visuobject_rel_reading" = "Leserichtung"; -"web_visuobject_rel_navigable" = "Navigierbarkeit"; -"web_visuobject_rel_role" = "Rolle"; -"web_visuobject_rel_multiplicity" = "Multiplizität"; -"web_visuobject_rel_role_field" = "= Attribut %@.%@ im Code"; -"web_visuobject_rel_role_only_uml" = "nur im Diagramm (kein Attribut, solange der Pfeil nur in eine Richtung zeigt)"; -"web_visuobject_rel_delete" = "Beziehung löschen"; -"web_visuobject_rel_impossible" = "Von %@ zu %@ gibt es keine passende Beziehung."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Breite ziehen (Doppelklick: Standard)"; -"web_visuobject_pane_show" = "Bereich aufklappen"; -"web_visuobject_pane_hide" = "Bereich einklappen"; -"web_visuobject_close_tab" = "%@ schließen"; -"web_visuobject_collapse" = "Einklappen"; -"web_visuobject_expand" = "Aufklappen"; -"web_visuobject_tree_empty" = "Noch leer. Lege mit „+ Neu“ ein Package oder eine Klasse an."; -"web_visuobject_delete" = "Löschen"; -"web_visuobject_default_package" = "(Standard-Package)"; -"web_visuobject_bad_package" = "„%@“ ist kein gültiger Package-Name oder gibt es schon. Package-Namen schreibt man klein, z. B. tiere."; -"web_visuobject_new_package_name" = "neuespackage"; -"web_visuobject_no_file" = "Keine Datei offen"; -"web_visuobject_project_name" = "MeinProjekt"; -"web_visuobject_explorer_show" = "Package Explorer zeigen"; -"web_visuobject_explorer_hide" = "Package Explorer einklappen"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Projekt als .zip speichern: Projektname/src/package/Klasse.java"; -"web_visuobject_open_tip" = "Projekt öffnen (.zip wie aus Eclipse oder BlueJ) oder .java-Dateien ins Projekt holen"; -"web_visuobject_method_ph" = "+ bellen(): void"; -"web_code_scopes" = "Blöcke farbig"; -"web_code_scopes_hint" = "Code und Struktogramm: Schleife rosa, Verzweigung blau, Methode gelb, Klasse grün"; -"web_pseudocode" = "Pseudocode"; - -/* VisuFlow (PAP zeichnen) */ -"web_visuflow_subtitle" = "PAP zeichnen"; -"web_visuflow_world_hint" = "VisuFlow: einen Programmablaufplan (PAP, englisch flowchart) frei zeichnen und als Bild oder PDF exportieren. Wechseln geht jederzeit im Zahnrad."; -"web_visuflow_palette" = "Symbole"; -"web_visuflow_palette_hint" = "Antippen hängt das Symbol unter das gewählte an und verbindet es mit einem Pfeil. Du kannst es auch auf die Fläche ziehen."; -"web_visuflow_shape_start" = "Start"; -"web_visuflow_shape_ende" = "Ende"; -"web_visuflow_shape_anweisung" = "Anweisung"; -"web_visuflow_shape_ein" = "Ein-/Ausgabe"; -"web_visuflow_shape_verzweigung" = "Verzweigung"; -"web_visuflow_shape_unterprogramm" = "Unterprogramm"; -"web_visuflow_shape_start_tip" = "Oval: Hier beginnt der Ablauf"; -"web_visuflow_shape_ende_tip" = "Oval: Hier endet der Ablauf"; -"web_visuflow_shape_anweisung_tip" = "Rechteck: eine Anweisung, zum Beispiel „Wasser kochen“"; -"web_visuflow_shape_ein_tip" = "Parallelogramm: Eingabe oder Ausgabe, zum Beispiel „Eingabe: Zahl“"; -"web_visuflow_shape_verzweigung_tip" = "Raute: eine Frage mit den Ausgängen ja und nein"; -"web_visuflow_shape_unterprogramm_tip" = "Rechteck mit zwei Strichen: ruft ein Unterprogramm auf"; -"web_visuflow_text_start" = "Start"; -"web_visuflow_text_ende" = "Ende"; -"web_visuflow_text_anweisung" = "Anweisung"; -"web_visuflow_text_ein" = "Eingabe: Zahl"; -"web_visuflow_text_verzweigung" = "Bedingung?"; -"web_visuflow_text_unterprogramm" = "Unterprogramm"; -"web_visuflow_new_tip" = "Neu: leerer PAP mit Start (Rückgängig holt den alten zurück)"; -"web_visuflow_open_tip" = "PAP-Datei (.visuflow) öffnen"; -"web_visuflow_save_tip" = "PAP als Datei (.visuflow) speichern"; -"web_visuflow_delete" = "Löschen"; -"web_visuflow_delete_tip" = "Gewähltes Symbol oder gewählten Pfeil löschen (Entf)"; -"web_visuflow_swap" = "ja/nein tauschen"; -"web_visuflow_swap_tip" = "Raute: „ja“ und „nein“ tauschen die Seiten"; -"web_visuflow_example" = "Beispiel"; -"web_visuflow_example_tip" = "Beispiel „Tee kochen“ aus Lektion 1 laden (Rückgängig holt den alten PAP zurück)"; -"web_visuflow_example_title" = "Tee kochen"; -"web_visuflow_example_water" = "Wasser kochen"; -"web_visuflow_example_bag" = "Teebeutel in die Tasse"; -"web_visuflow_example_hot" = "Tee noch zu heiß?"; -"web_visuflow_example_wait" = "Warten"; -"web_visuflow_example_drink" = "Tee trinken"; -"web_visuflow_title_label" = "Titel des PAP"; -"web_visuflow_title_ph" = "Titel des PAP"; -"web_visuflow_canvas_label" = "Zeichenfläche des PAP"; -"web_visuflow_open_error" = "Die Datei ist kein VisuFlow-PAP."; -"web_visuflow_saved" = "Gespeichert als %@"; -"web_visuflow_status_empty" = "Zieh ein Symbol aus der Leiste auf die Fläche oder tippe es an."; -"web_visuflow_status_idle" = "Symbol antippen wählt es aus. Vom Punkt am Ausgang ziehst du einen Pfeil zum nächsten Symbol."; -"web_visuflow_status_node" = "Ziehen verschiebt das Symbol, nochmal antippen ändert den Text. Vom Punkt am Ausgang einen Pfeil ziehen."; -"web_visuflow_status_edge" = "Pfeil gewählt. Löschen entfernt ihn."; -"web_visuflow_status_connect" = "Loslassen über dem Symbol, zu dem der Pfeil führt."; -"web_visuflow_edit_label" = "Text im Symbol"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Variable umbenennen …"; -"web_rename_title" = "Variable umbenennen"; -"web_rename_old" = "Name"; -"web_rename_new" = "Neuer Name"; -"web_rename_hint" = "Ändert jede Stelle mit genau dieser Schreibweise (zahl ist nicht Zahl), im Code und im Struktogramm. Text in \\"…\\" und Kommentare bleiben."; -"web_rename_count_fmt" = "Kommt %d-mal vor."; -"web_rename_exists_fmt" = "„%@“ gibt es schon: Danach wären beide eine Variable."; -"web_rename_invalid" = "Ein Name beginnt mit einem Buchstaben und enthält nur Buchstaben, Ziffern und _."; -"web_rename_keyword_fmt" = "„%@“ ist ein Java-Schlüsselwort."; -"web_rename_apply" = "Umbenennen"; -"web_rename_cancel" = "Abbrechen"; -"web_visubot_world_visucode_hint" = "Andere Programme wie %1$@ oder %2$@ findest du auf visucode.de."; -`;export{e as default}; diff --git a/website/app/assets/el-MJ37lw3F.js b/website/app/assets/el-MJ37lw3F.js deleted file mode 100644 index 3db10676..00000000 --- a/website/app/assets/el-MJ37lw3F.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Κείμενα που υπάρχουν μόνο στην έκδοση web. Όλα τα άλλα προέρχονται από το Localizable.strings της εφαρμογής. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Διάγραμμα VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Εξαγωγή…"; -"web_export_title" = "Αποθήκευση ως εικόνα ή PDF ή εκτύπωση"; -"web_export_png" = "Εικόνα (PNG)"; -"web_export_svg" = "Διανυσματικό γραφικό (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Αρχείο VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Η εξαγωγή απέτυχε: %@"; -"web_print_hint" = "Με «Εξαγωγή… → PDF» μπορείτε να αποθηκεύσετε το διάγραμμα και να το εκτυπώσετε από εκεί."; -"web_delete" = "Διαγραφή"; -"web_language_title" = "Γλώσσα περιβάλλοντος"; -"web_simulation_start" = "Έναρξη προσομοίωσης"; -"web_simulation_end" = "Τέλος προσομοίωσης"; -"web_simulation_start_title" = "Εκτέλεση του διαγράμματος βήμα προς βήμα"; -"web_simulation_end_title" = "Τέλος προσομοίωσης και συνέχεια επεξεργασίας"; -"web_palette_hint" = "Σύρετε στο διάγραμμα"; -"web_tabs_label" = "Διαγράμματα"; -"web_status_modified" = "τροποποιήθηκε"; -"web_inspector_title" = "Ιδιότητες"; -"web_inspector_resize" = "Αλλαγή ύψους του επιθεωρητή αντικειμένων: σύρετε τη γραμμή πάνω ή κάτω (διπλό κλικ: προεπιλογή)"; -"web_inspector_help_drop" = "Σύρετε στοιχεία από την παλέτα σε ένα πλακίδιο: πάνω = πριν, κάτω = μετά, πάνω στο «ø» = αντικατάσταση. Τα υπάρχοντα στοιχεία μετακινούνται με το ποντίκι."; -"web_inspector_help_inline" = "Διπλό κλικ ή διπλό πάτημα σε ένα στοιχείο για επεξεργασία του κειμένου απευθείας στο διάγραμμα (Enter εφαρμογή, Esc ακύρωση). Το ίδιο και για τα ονόματα περιπτώσεων."; -"web_inspector_help_files" = "Μπορείτε επίσης να σύρετε αρχεία στο παράθυρο."; -"web_case_name" = "Όνομα περίπτωσης"; -"web_case_name_placeholder" = "π.χ. 1, ok, default"; -"web_copied" = "Το στοιχείο αντιγράφηκε."; -"web_discard_message_fmt" = "Οι αλλαγές στο «%@» θα χαθούν."; -"web_err_unreadable_fmt" = "Το «%@» δεν είναι αναγνώσιμο αρχείο VisuStruct (%@)."; -"web_saved_fmt" = "Το «%@» αποθηκεύτηκε."; -"web_saved_txt_fmt" = "Αποθηκεύτηκε ως «%@». Αφαιρέστε την κατάληξη .txt για να το χρησιμοποιήσετε."; -"web_drop_impossible" = "Δεν είναι δυνατή η απόθεση εδώ"; -"web_sim_delay" = "Παύση ανά βήμα"; -"web_sim_delay_value_fmt" = "%@ δευτ."; -"web_codegen_language" = "Γλώσσα"; -"web_codegen_comments" = "Κείμενο διαγράμματος ως σχόλια"; -"web_codegen_empty_else" = "Εμφάνιση κενού κλάδου else στον κώδικα"; -"web_copied_check" = "Αντιγράφηκε ✓"; -"web_copy_manual" = "Επιλέχθηκε, πατήστε Ctrl+C για αντιγραφή"; -"web_run_stopped_loop" = "Η εκτέλεση διακόπηκε: πιθανός ατέρμονος βρόχος."; -"web_run_cancelled" = "Η εκτέλεση ακυρώθηκε."; -"web_run_error" = "Σφάλμα κατά την εκτέλεση."; -"web_run_dropped_fmt" = "… %d προηγούμενες γραμμές κρυμμένες"; -"web_sim_call_arguments_fmt" = "Η κλήση δεν ταιριάζει με την κεφαλίδα του %@. Έλεγξε τις τιμές μέσα στις παρενθέσεις."; -"web_sim_missing_return_fmt" = "Το %@ τελείωσε χωρίς να επιστρέψει τιμή με return."; -"web_sim_file_not_found_fmt" = "Δεν υπάρχει αρχείο με όνομα %@."; -"web_sim_end_of_file_fmt" = "Το αρχείο %@ δεν έχει άλλες γραμμές."; -"web_sim_null_value_fmt" = "Το %@ είναι null."; -"web_sim_unknown_variable_case_fmt" = "Άγνωστη μεταβλητή: %1$@. Η Java ξεχωρίζει κεφαλαία και πεζά: μήπως εννοούσες %2$@;"; -"web_sim_written_file_fmt" = "%@ (αποθηκεύτηκε)"; -"web_site_home" = "Αρχική σελίδα"; -"web_site_imprint" = "Στοιχεία εκδότη"; -"web_site_privacy" = "Απόρρητο"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Προγραμματισμός"; -"web_live_hide" = "Απόκρυψη προγραμματισμού"; -"web_scale_canvas" = "Μέγεθος κειμένου: διάγραμμα"; -"web_scale_code" = "Μέγεθος κειμένου: προγραμματισμός"; -"web_live_indent" = "Εσοχή"; -"web_live_indent_title" = "Εσοχή κώδικα: τέσσερα κενά ανά επίπεδο (επιλεγμένες γραμμές, αλλιώς όλα)"; -"web_live_show" = "Εμφάνιση προγραμματισμού"; -"web_live_resize" = "Αλλαγή πλάτους περιοχής κώδικα (διπλό κλικ: επαναφορά)"; -"web_live_hint" = "Ο κώδικας μεγαλώνει μαζί με το διάγραμμα. Γκρι = σύμβολο κράτησης: γράψε στον επιθεωρητή και το κείμενό σου εμφανίζεται εδώ. Κάνε κλικ σε μια γραμμή για να επιλέξεις το στοιχείο."; -"web_live_ph_selector" = "μεταβλητή"; -"web_live_ph_code" = "Ο κώδικάς σου"; -"web_live_ph_end" = "τέλος"; -"web_live_language" = "Γλώσσα προγραμματισμού"; -"web_live_java_class" = "Ως κλάση"; -"web_live_java_class_hint" = "Όνομα κλάσης = τίτλος διαγράμματος"; -"web_live_java_main" = "Με μέθοδο main"; -"web_settings_display" = "Εμφάνιση"; -"web_live_swing" = "Πραγματική Java (με Swing)"; -"web_live_swing_hint" = "Πραγματική Java με παράθυρα (CheerpJ). Κατεβάζει μία φορά περίπου 40 MB στην πρώτη εκτέλεση."; -"web_live_swing_windows" = "Παράθυρα Java"; -"web_live_swing_hide" = "Κλείσιμο παραθύρων Java"; -"web_live_swing_loading" = "Φόρτωση Java (την πρώτη φορά περίπου 40 MB) …"; -"web_live_swing_compiling" = "Μεταγλώττιση …"; -"web_live_swing_running" = "Το πρόγραμμα εκτελείται."; -"web_live_swing_compile_failed" = "Σφάλμα μεταγλώττισης, δες παραπάνω."; -"web_live_swing_failed" = "Δεν ήταν δυνατή η φόρτωση της Java. Έλεγξε τη σύνδεση στο διαδίκτυο."; -"web_live_hint_edit" = "Γράψε εδώ κώδικα Java και το διάγραμμα φτιάχνεται μόνο του. Ή δούλεψε στο διάγραμμα και ο κώδικας μεγαλώνει μαζί. Γκρι = σύμβολο κράτησης θέσης, απλώς γράψε από πάνω."; -"web_live_edit_label" = "Κώδικας Java"; -"web_parse_line_fmt" = "Γραμμή %d: %@"; -"web_parse_missing" = "Λείπει ακόμα εδώ: %@"; -"web_parse_extra" = "Περισσεύει: %@"; -"web_parse_unclosed_string" = "Λείπει το %@ που κλείνει."; -"web_parse_else_without_if" = "Αυτό το «else» δεν έχει «if»."; -"web_parse_case_outside_switch" = "Τα «case» και «default» ανήκουν μέσα σε «switch»."; -"web_parse_missing_do_while" = "Μετά το «do { … }» λείπει το «while (συνθήκη);»."; -"web_parse_more_methods" = "Προς το παρόν μετράει μόνο μία μέθοδος. Δημιούργησε τα άλλα υποπρογράμματα σε ξεχωριστές καρτέλες."; -"web_parse_missing_import" = "Λείπει στην αρχή το %@ Χωρίς αυτή τη γραμμή η Java δεν γνωρίζει το Scanner."; -"web_parse_missing_scanner" = "Λείπει πρώτα %@ Χωρίς αυτή τη γραμμή ο Scanner δεν υπάρχει."; -"web_parse_unknown_type" = "Η Java δεν γνωρίζει αυτόν τον τύπο δεδομένων. Μήπως εννοείς %@;"; -"web_quickfix_insert_fmt" = "Πρόταση: εισαγωγή %@"; -"web_quickfix_replace_fmt" = "Πρόταση: %1$@ αντί για %2$@"; -"web_parse_wrong_case" = "Η Java ξεχωρίζει κεφαλαία και πεζά. Το σωστό είναι %@."; -"web_editor_title" = "Διάγραμμα"; -"web_editor_show" = "Εμφάνιση διαγράμματος"; -"web_editor_hide" = "Απόκρυψη διαγράμματος"; -"web_keywords_title" = "Λέξεις-κλειδιά κώδικα"; -"web_keywords_hint" = "if, while, for αντί για ονόματα στοιχείων"; -"web_palette_compact" = "Στοιχεία πάντα συμπαγή"; -"web_palette_compact_hint" = "Σύμβολο με μικρό όνομα από κάτω, αλλιώς μόνο όταν η στήλη είναι στενή"; -"web_about_version_fmt" = "Έκδοση %@"; -"web_about_software" = "Λογισμικό που χρησιμοποιείται"; -"web_about_software_text" = "Ο επεξεργαστής κώδικα χρησιμοποιεί τα CodeMirror και Lezer (άδεια MIT). Με την υποστήριξη Swing, το VisuStruct φορτώνει το CheerpJ της Leaning Technologies (άδεια Community) και το javac από το OpenJDK."; -"web_about_licenses" = "Κείμενα αδειών"; -"web_live_run" = "Εκτέλεση"; -"web_live_run_title" = "Εκτέλεση προγράμματος: η έξοδος και η είσοδος εμφανίζονται παρακάτω"; -"web_live_run_finished" = "Το πρόγραμμα τελείωσε."; -"web_live_run_stopped" = "Διακόπηκε."; -"web_console" = "Κονσόλα"; -"web_console_hide" = "Απόκρυψη κονσόλας"; -"web_console_resize" = "Αλλαγή ύψους κονσόλας"; -"web_diagram_tools" = "Εργαλεία διαγράμματος"; -"web_files_title" = "Αρχεία"; -"web_files_show" = "Εμφάνιση αρχείων"; -"web_files_hide" = "Απόκρυψη αρχείων"; -"web_files_resize" = "Αλλαγή πλάτους αρχείων"; -"web_files_new_folder" = "Νέος φάκελος"; -"web_files_switch" = "Επιλογή άλλου φακέλου"; -"web_files_delete" = "Διαγραφή"; -"web_files_in_browser" = "Στον χώρο αυτού του προγράμματος περιήγησης"; -"web_files_save_hint_fmt" = "Η αποθήκευση βάζει το πρόγραμμα στο %@."; -"web_files_empty" = "Δεν έχει αποθηκευτεί τίποτα ακόμη. Κατά την αποθήκευση το VisuStruct δημιουργεί εδώ έναν φάκελο με τη σημερινή ημερομηνία."; -"web_files_reconnect_hint" = "Μετά την επαναφόρτωση, το πρόγραμμα περιήγησης ρωτά ξανά αν το VisuStruct μπορεί να χρησιμοποιήσει τον φάκελό σου."; -"web_files_reconnect_fmt" = "Άνοιγμα φακέλου «%@»"; -"web_files_connect_hint" = "Επίλεξε έναν φάκελο ως χώρο εργασίας. Το VisuStruct δημιουργεί εκεί έναν φάκελο για κάθε ημέρα και αποθηκεύει διάγραμμα και κώδικα."; -"web_files_connect" = "Επιλογή φακέλου"; -"web_files_browser_hint" = "Αυτό το πρόγραμμα περιήγησης δεν μπορεί να ανοίξει φακέλους της συσκευής (π.χ. στο iPad). Τα αρχεία σου μένουν στον χώρο του."; -"web_files_browser" = "Αποθήκευση στο πρόγραμμα περιήγησης"; -"web_files_delete_confirm_fmt" = "Να διαγραφεί οριστικά το «%@»;"; -"web_files_folder_prompt" = "Όνομα νέου φακέλου:"; -"web_files_default_name" = "Πρόγραμμα"; -"web_files_saved_fmt" = "Αποθηκεύτηκε: %@"; -"web_files_create" = "Δημιουργία"; -"web_learn_ludwig_tip" = "Η συμβουλή του Ludwig"; -"web_game_title" = "Παιχνίδι: Αληθές ή ψευδές;"; -"web_game_intro" = "Ο Ludwig σου δείχνει τιμές και μια συνθήκη. Είναι αληθής (true) ή ψευδής (false); Δέκα γύροι, όλο και δυσκολότεροι."; -"web_game_start" = "Έναρξη παιχνιδιού"; -"web_game_round_fmt" = "Γύρος %@ από %@"; -"web_game_score_fmt" = "Πόντοι: %@"; -"web_game_true" = "αληθές (true)"; -"web_game_false" = "ψευδές (false)"; -"web_game_right" = "Σωστά!"; -"web_game_wrong" = "Όχι ακριβώς. Έτσι υπολογίζεται:"; -"web_game_next" = "Επόμενο"; -"web_game_finish" = "Αποτέλεσμα"; -"web_game_done_fmt" = "%@ από %@ σωστά."; -"web_game_perfect" = "Όλα σωστά – ο Ludwig εντυπωσιάστηκε!"; -"web_game_good" = "Μπράβο! Δες ξανά τα λάθη σου και δοκίμασε πάλι."; -"web_game_practice" = "Η εξάσκηση φέρνει την τελειότητα. Διάβασε ξανά τον πίνακα αλήθειας και παίξε άλλον έναν γύρο."; -"web_game_again" = "Παίξε ξανά"; -"web_game_wrong_short" = "Δυστυχώς λάθος."; -"web_game_practice_short" = "Η εξάσκηση φέρνει την τελειότητα. Διάβασε ξανά τις εξηγήσεις και παίξε άλλον έναν γύρο."; -"web_game_clicks" = "Κλικ:"; -"web_game_nobody" = "Κανείς δεν ακούει – δεν συμβαίνει τίποτα."; -"web_game_event_fmt" = "Κλικ στο «%@»: ο ακροατής το ακούει και εκτελεί τον κώδικά του."; -"web_game_try" = "Δοκίμασε τώρα μόνος σου τα κουμπιά στο παράθυρο."; -"web_game_time_fmt" = "Απομένουν %@ δ"; -"web_game_defused" = "✂️ Εξουδετερώθηκε! Βρήκες το λάθος."; -"web_game_boom_time" = "💥 Μπαμ! Ο χρόνος τελείωσε."; -"web_game_boom_wrong" = "💥 Μπαμ! Ήταν λάθος γραμμή."; -"web_game_boom_part" = "💥 Μπαμ! Σωστή γραμμή, αλλά το λάθος είναι σε άλλο κομμάτι της."; -"web_game_boom_wrong_part" = "💥 Μπαμ! Το λάθος είναι σε άλλη γραμμή."; -"web_game_bomb_parts_hint" = "Πάτησε ακριβώς το λάθος κομμάτι."; -"web_game_fix" = "Η σωστή γραμμή:"; -"web_game_order_hint" = "Πάτησε τα βήματα με τη σωστή σειρά."; -"web_debug_step" = "Βήμα"; -"web_debug_step_title" = "Βήμα: εκτέλεση μίας εντολής και νέα παύση"; -"web_debug_continue" = "Συνέχεια έως το επόμενο σημείο διακοπής"; -"web_debug_paused_fmt" = "Παύση πριν από τη γραμμή %d."; -"web_debug_gutter_title" = "Κάνε κλικ σε αριθμό γραμμής για να βάλεις ή να βγάλεις σημείο διακοπής"; -"web_swap_to_code" = "Μεγάλος προγραμματισμός"; -"web_swap_to_editor" = "Μεγάλο διάγραμμα"; -"web_editor_resize" = "Αλλαγή πλάτους διαγράμματος"; -"web_palette_resize" = "Αλλαγή πλάτους στοιχείων"; -"web_palette_hide" = "Απόκρυψη στοιχείων"; -"web_palette_show" = "Εμφάνιση στοιχείων"; -"web_code_scopes" = "Χρωματιστά μπλοκ κώδικα"; -"web_code_scopes_hint" = "Κλάση, μέθοδος, βρόχος, διακλάδωση σε δικά τους πλαίσια"; -"web_pseudocode" = "Ψευδοκώδικας"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Μετονομασία μεταβλητής …"; -"web_rename_title" = "Μετονομασία μεταβλητής"; -"web_rename_old" = "Όνομα"; -"web_rename_new" = "Νέο όνομα"; -"web_rename_hint" = "Αλλάζει κάθε σημείο με ακριβώς αυτή τη γραφή (number δεν είναι Number), στον κώδικα και στο δομόγραμμα. Το κείμενο σε \\"…\\" και τα σχόλια μένουν."; -"web_rename_count_fmt" = "Εμφανίζεται %d φορές."; -"web_rename_exists_fmt" = "Το «%@» υπάρχει ήδη: μετά θα ήταν και τα δύο μία μεταβλητή."; -"web_rename_invalid" = "Ένα όνομα αρχίζει με γράμμα και περιέχει μόνο γράμματα, ψηφία και _."; -"web_rename_keyword_fmt" = "Το «%@» είναι λέξη-κλειδί της Java."; -"web_rename_apply" = "Μετονομασία"; -"web_rename_cancel" = "Άκυρο"; -`;export{e as default}; diff --git a/website/app/assets/en-gIMn8Pa3.js b/website/app/assets/en-gIMn8Pa3.js deleted file mode 100644 index d49ea542..00000000 --- a/website/app/assets/en-gIMn8Pa3.js +++ /dev/null @@ -1,801 +0,0 @@ -const e=`/* Texts that exist only in the web version. Everything else comes from the app's Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct structure chart"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Export…"; -"web_export_title" = "Save as image or PDF, or print"; -"web_export_png" = "Image (PNG)"; -"web_export_svg" = "Vector graphic (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct file (.visustruct)"; -"web_export_failed_fmt" = "Export failed: %@"; -"web_print_hint" = "Use “Export… → PDF” to save the diagram and print it from there."; -"web_delete" = "Delete"; -"web_language_title" = "Interface language"; -"web_simulation_start" = "Start simulation"; -"web_simulation_end" = "End simulation"; -"web_simulation_start_title" = "Run the structure chart step by step"; -"web_simulation_end_title" = "End the simulation and continue editing"; -"web_palette_hint" = "Drag into the structure chart"; -"web_tabs_label" = "Diagrams"; -"web_status_modified" = "modified"; -"web_inspector_title" = "Object Inspector"; -"web_inspector_help_drop" = "Drag elements from the “Elements” area onto a tile: top = before, bottom = after, onto “ø” = replace. Existing elements can be moved with the mouse."; -"web_inspector_help_inline" = "Double-click or double-tap an element to edit its text right in the structure chart (Enter applies, Esc cancels). Case labels work the same way."; -"web_inspector_help_files" = "You can also drop files into the window."; -"web_case_name" = "Case name"; -"web_case_name_placeholder" = "e.g. 1, ok, default"; -"web_copied" = "Element copied."; -"web_discard_message_fmt" = "The changes to “%@” will be lost."; -"web_err_unreadable_fmt" = "“%@” is not a readable VisuStruct file (%@)."; -"web_saved_fmt" = "“%@” saved."; -"web_saved_txt_fmt" = "Saved as “%@”. Remove the .txt extension to use it."; -"web_drop_impossible" = "Cannot drop here"; -"web_sim_delay" = "Pause per step"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Language"; -"web_codegen_comments" = "Diagram text as comments"; -"web_codegen_empty_else" = "Emit empty else branch in code"; -"web_copied_check" = "Copied ✓"; -"web_copy_manual" = "Selected, press Ctrl+C to copy"; -"web_run_stopped_loop" = "Execution stopped: possible infinite loop."; -"web_run_cancelled" = "Execution cancelled."; -"web_run_error" = "Error while running."; -"web_run_dropped_fmt" = "… %d earlier lines hidden"; - -/* Learning area (lessons are German only for now) */ -"web_learn_title" = "Learn"; -"web_learn_hide" = "Collapse the learning area"; -"web_learn_show" = "Expand the learning area: lessons, exercises and reference"; -"web_learn_lessons" = "Lessons"; -"web_learn_tasks" = "Exercises"; -"web_learn_reference" = "Reference"; -"web_learn_intro" = "Learn step by step, with exercises. Your progress is remembered in this browser."; -"web_learn_tasks_intro" = "Small projects without instructions. Each one names the lesson you should finish first."; -"web_learn_step_of_fmt" = "Step %d of %d"; -"web_learn_lesson_fmt" = "Lesson %d"; -"web_learn_all_lessons" = "All lessons"; -"web_learn_all_tasks" = "All exercises"; -"web_learn_back" = "Back"; -"web_learn_next" = "Next"; -"web_learn_finish" = "Finish lesson"; -"web_learn_done" = "Finished"; -"web_learn_after_fmt" = "After lesson %d: %@"; -"web_learn_level_fmt" = "Difficulty %d of 3"; -"web_learn_exercise" = "Exercise"; -"web_learn_brag" = "Fun facts with Ludwig"; -"web_learn_solved" = "solved"; -"web_learn_open_exercise" = "Open in editor"; -"web_learn_solve_in_editor" = "Solve this exercise in the %@ area."; -"web_learn_solve_in_code" = "Solve this exercise in the %@ area."; -"web_learn_solve_in_both" = "Solve this exercise in the %1$@ and %2$@ areas."; -"web_learn_open_in_editor" = "Open in editor"; -"web_learn_replay" = "Play again"; -"web_learn_graphic_labels" = "👆 Click the picture to show or hide the labels"; -"web_learn_check" = "Check"; -"web_learn_hint" = "Hint"; -"web_learn_reset" = "Start over"; -"web_learn_reset_confirm" = "Really start over?"; -"web_learn_solution" = "Show sample solution"; -"web_learn_hint_more" = "Another hint"; -"web_learn_hint_fmt" = "Hint %d of %d"; -"web_learn_teacher" = "For teachers"; -"web_learn_teacher_intro" = "The teacher code shows the sample solutions for all exercises. The browser remembers it."; -"web_learn_teacher_code" = "Teacher code"; -"web_learn_teacher_unlock" = "Unlock"; -"web_learn_teacher_wrong" = "The code is not correct."; -"web_learn_teacher_on" = "Teacher mode is on: sample solutions are visible for all exercises."; -"web_learn_teacher_off" = "Leave teacher mode"; -"web_learn_open_first" = "Open the exercise in the editor first and build your structure chart there."; -"web_learn_empty" = "Your structure chart is still empty. Drag elements from the “Elements” area into it first."; -"web_learn_passed" = "Well done! Your structure chart does exactly what it should."; -"web_learn_failed" = "Not quite yet. Compare the expected output with yours:"; -"web_learn_missing_fmt" = "Your structure chart still needs: %@"; -"web_learn_input_fmt" = "Input: %@"; -"web_learn_no_input" = "No input"; -"web_learn_expected" = "Expected"; -"web_learn_actual" = "Your output"; -"web_learn_missing_input" = "Your structure chart asks for more input than planned."; -"web_learn_unused_input" = "Your structure chart does not ask for all inputs."; -"web_learn_missing_line_break" = "Almost! Only a line break is missing. System.out.print keeps writing on the same line, System.out.println starts a new line afterwards."; -"web_learn_too_many_steps" = "Stopped after 100,000 steps. Is a loop running forever?"; -"web_learn_score_fmt" = "%d of %d correct"; -"web_learn_cloze_hint" = "Type the missing words into the gaps and press “Check”."; -"web_learn_cloze_score_fmt" = "%d of %d gaps correct"; -"web_learn_cloze_all_right" = "All gaps correct!"; -"web_learn_cloze_solution" = "Show solution"; -"web_learn_cloze_again" = "Try again"; -"web_learn_pap_hint" = "Tap an element on the right and then its place in the chart. You can also drag it there. Tapping a filled place gives its element back."; -"web_learn_pap_score_fmt" = "%d of %d elements correct"; -"web_learn_pap_all_right" = "All correct! Your flowchart is finished."; -"web_learn_pap_missing_fmt" = "%d elements are still missing."; -"web_learn_pap_empty" = "Empty place"; -"web_learn_pap_yes" = "yes"; -"web_learn_pap_no" = "no"; -"web_learn_pap_legend_oval" = "Start or end"; -"web_learn_pap_legend_rect" = "Statement"; -"web_learn_pap_legend_diamond" = "Decision: question with yes and no"; -"web_learn_pap_legend_para" = "Input or output"; -"web_learn_review" = "Review"; -"web_learn_terms" = "Technical terms"; -"web_inspector_resize" = "Change the height of the object inspector: drag or swipe the line up or down (double-click: default)"; -"web_learn_resize" = "Change the width of the learning area (double-click: reset)"; -"web_learn_competences_intro" = "At the end of this lesson I can …"; -"web_learn_rating_sun" = "I can do this confidently"; -"web_learn_rating_cloud" = "partly"; -"web_learn_rating_umbrella" = "not yet"; -"web_learn_review_title" = "Your checklist compared"; -"web_learn_review_hint" = "Here your self-assessment and the quiz do not match. Read the step again and rate yourself again in the checklist."; -"web_learn_review_match" = "Your self-assessment matches your quiz. Well done!"; -"web_learn_review_overrated_fmt" = "You chose ☀️, but question %@ was wrong."; -"web_learn_review_underrated" = "All questions on this were right. You can do more than you think!"; -"web_learn_review_unrated" = "not rated yet"; -"web_learn_back_to_checklist" = "Back to checklist"; -"web_learn_step_fmt" = "Step %d: %@"; -"web_sim_call_arguments_fmt" = "The call does not match the header of %@. Check the values in the parentheses."; -"web_sim_missing_return_fmt" = "%@ ended without returning a value with return."; -"web_sim_file_not_found_fmt" = "There is no file called %@."; -"web_sim_end_of_file_fmt" = "The file %@ has no more lines."; -"web_sim_null_value_fmt" = "%@ is null."; -"web_sim_unknown_variable_case_fmt" = "Unknown variable: %1$@. Java is case-sensitive: did you mean %2$@?"; -"web_sim_written_file_fmt" = "%@ (saved)"; -"web_site_home" = "Home page"; -"web_site_imprint" = "Imprint"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Code"; -"web_live_hide" = "Hide code"; -"web_scale_canvas" = "Text size: structure chart"; -"web_scale_code" = "Text size: Code"; -"web_live_indent" = "Indent"; -"web_live_indent_title" = "Indent and format the code: every } on its own line, spaces around operators, four spaces per level (selected lines, otherwise everything)"; -"web_live_show" = "Show code"; -"web_live_resize" = "Change width of the code area (double-click: reset)"; -"web_live_hint" = "The code grows with your diagram. Grey = placeholder: type in the object inspector and your text appears there. Click a line to select its element."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Your code"; -"web_live_ph_end" = "end"; -"web_live_language" = "Programming language"; -"web_live_java_class" = "As a class"; -"web_live_java_class_hint" = "Class name = title of the diagram"; -"web_live_java_main" = "With main method"; -"web_settings_display" = "Display"; -"web_live_swing" = "Real Java (with Swing)"; -"web_live_swing_hint" = "Real Java with windows (CheerpJ). Downloads about 40 MB once, on the first run."; -"web_live_swing_windows" = "Java windows"; -"web_live_swing_hide" = "Close Java windows"; -"web_live_swing_loading" = "Loading Java (about 40 MB the first time) …"; -"web_live_swing_compiling" = "Compiling …"; -"web_live_swing_running" = "Program is running."; -"web_live_swing_compile_failed" = "Compile error, see above."; -"web_live_swing_failed" = "Java could not be loaded. Check your internet connection."; -"web_live_hint_edit" = "Type Java code here and the diagram builds itself. Or work in the diagram and the code grows along. Grey = placeholder, just type over it."; -"web_live_edit_label" = "Java code"; -"web_parse_line_fmt" = "Line %d: %@"; -"web_parse_missing" = "Still missing here: %@"; -"web_parse_extra" = "One too many: %@"; -"web_parse_unclosed_string" = "The closing %@ is missing here."; -"web_parse_else_without_if" = "This “else” has no matching “if”."; -"web_parse_case_outside_switch" = "“case” and “default” belong inside a “switch”."; -"web_parse_missing_do_while" = "After “do { … }” you still need “while (condition);”."; -"web_parse_more_methods" = "Only one method counts here for now. Create further subprograms as separate tabs."; -"web_parse_missing_import" = "%@ is missing at the very top. Without this line Java doesn’t know Scanner."; -"web_parse_missing_scanner" = "%@ is missing before this line. Without it the Scanner doesn’t exist."; -"web_parse_unknown_type" = "Java doesn’t know this data type. Did you mean %@?"; -"web_quickfix_insert_fmt" = "Suggestion: insert %@"; -"web_quickfix_replace_fmt" = "Suggestion: %1$@ instead of %2$@"; -"web_parse_wrong_case" = "Java is case-sensitive. The right spelling is %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Show diagram"; -"web_editor_hide" = "Hide diagram"; -"web_keywords_title" = "Code keywords"; -"web_keywords_hint" = "if, while, for instead of Branch, For loop …"; -"web_palette_compact" = "Elements always compact"; -"web_palette_compact_hint" = "Symbol with a small name below, otherwise only when the column is narrow"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Third-party software"; -"web_about_software_text" = "The code editor uses CodeMirror and Lezer (MIT License). With Swing support, VisuStruct loads CheerpJ by Leaning Technologies (Community License) and javac from OpenJDK."; -"web_about_licenses" = "License texts"; -"web_live_run" = "Run"; -"web_live_run_title" = "Run the program: output and input appear below"; -"web_live_run_finished" = "Program finished."; -"web_live_run_stopped" = "Stopped."; -"web_console" = "Console"; -"web_console_hide" = "Hide console"; -"web_console_resize" = "Resize console"; -"web_diagram_tools" = "Diagram tools"; -"web_files_title" = "Files"; -"web_files_show" = "Show files"; -"web_files_hide" = "Hide files"; -"web_files_resize" = "Resize files"; -"web_files_new_folder" = "New folder"; -"web_files_switch" = "Choose another folder"; -"web_files_delete" = "Delete"; -"web_files_in_browser" = "In this browser’s storage"; -"web_files_save_hint_fmt" = "Saving puts the program in %@."; -"web_files_empty" = "Nothing saved yet. When you save, VisuStruct creates a folder with today’s date here."; -"web_files_reconnect_hint" = "After reloading, the browser asks once more whether VisuStruct may use your folder."; -"web_files_reconnect_fmt" = "Open folder “%@”"; -"web_files_connect_hint" = "Choose a folder as your workspace, ideally a new folder “VisuStruct-Files” in your Documents. VisuStruct creates a folder for each day in it and saves both diagram and code."; -"web_files_connect" = "Choose folder"; -"web_files_browser_hint" = "This browser can’t open folders on the device (for example on iPad). Your files then stay in this browser’s storage."; -"web_files_browser" = "Save in the browser"; -"web_files_delete_confirm_fmt" = "Really delete “%@”?"; -"web_files_folder_prompt" = "Name of the new folder:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Saved: %@"; -"web_files_create" = "Create"; -"web_learn_ludwig_tip" = "Ludwig’s tip"; -"web_game_title" = "Game: True or false?"; -"web_game_intro" = "Ludwig shows you values and a condition. Is the condition true or false? Ten rounds, getting trickier each time."; -"web_game_start" = "Start game"; -"web_game_round_fmt" = "Round %@ of %@"; -"web_game_score_fmt" = "Points: %@"; -"web_game_true" = "true"; -"web_game_false" = "false"; -"web_game_right" = "Correct!"; -"web_game_wrong" = "Not quite. This is how it works out:"; -"web_game_next" = "Next"; -"web_game_finish" = "Result"; -"web_game_done_fmt" = "%@ of %@ correct."; -"web_game_perfect" = "All correct – Ludwig is seriously impressed!"; -"web_game_good" = "Great! Have another look at your mistakes and try again."; -"web_game_practice" = "Practice makes perfect. Read the truth table above once more and play another round."; -"web_game_again" = "Play again"; -"web_game_wrong_short" = "Sorry, that's wrong."; -"web_game_practice_short" = "Practice makes perfect. Read the explanations again and play another round."; -"web_game_clicks" = "Clicks:"; -"web_game_nobody" = "Nobody is listening – nothing happens."; -"web_game_event_fmt" = "Click on “%@”: the listener hears it and runs its code."; -"web_game_try" = "Now try the buttons in the window yourself."; -"web_game_time_fmt" = "%@ s left"; -"web_game_defused" = "✂️ Defused! You found the mistake."; -"web_game_boom_time" = "💥 Boom! Time is up."; -"web_game_boom_wrong" = "💥 Boom! That was the wrong line."; -"web_game_boom_part" = "💥 Boom! Right line, but the mistake is in another part of it."; -"web_game_boom_wrong_part" = "💥 Boom! The mistake is in another line."; -"web_game_bomb_parts_hint" = "Tap exactly the wrong part."; -"web_game_fix" = "The correct line:"; -"web_game_order_hint" = "Tap the steps in the right order."; -"web_debug_step" = "Step"; -"web_debug_step_title" = "Step: run one statement and pause again"; -"web_debug_continue" = "Continue to the next breakpoint"; -"web_debug_paused_fmt" = "Paused before line %d."; -"web_debug_gutter_title" = "Click a line number to set or remove a breakpoint"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Caesar wheel"; -"web_learn_caesar_hint" = "Turn the inner wheel or tap −1 and +1. The outer ring shows the plain text, the inner ring the secret text."; -"web_learn_caesar_key_fmt" = "Key %d"; -"web_learn_caesar_plain" = "Plain text"; -"web_learn_caesar_secret" = "Secret text"; -"web_learn_caesar_encrypt" = "Encrypt"; -"web_learn_caesar_decrypt" = "Decrypt"; -"web_learn_caesar_back" = "Turn wheel back one"; -"web_learn_caesar_forward" = "Turn wheel forward one"; -"web_learn_rescue_station_fmt" = "Station %@ of %@: %@"; -"web_learn_rescue_number" = "Your number"; -"web_learn_rescue_not_number" = "Enter a whole number."; -"web_learn_rescue_wrong" = "Not yet. Read the code again, character by character."; -"web_learn_rescue_letter_moved" = "The letter goes into the crossword."; -"web_learn_rescue_alphabet" = "Your number turns into a letter:"; -"web_learn_rescue_to_crossword" = "To the crossword"; -"web_learn_rescue_crossword" = "The crossword"; -"web_learn_rescue_across" = "→ Across"; -"web_learn_rescue_down" = "↓ Down"; -"web_learn_rescue_cell_fmt" = "Square row %@, column %@"; -"web_learn_rescue_keyword" = "Secret word:"; -"web_learn_rescue_cw_wrong" = "The squares marked red are not right yet."; -"web_learn_rescue_cw_done" = "Solved! The yellow squares spell the secret word."; -"web_learn_rescue_to_gate" = "To the gate"; -"web_learn_rescue_gate" = "The gate"; -"web_learn_rescue_word" = "Secret word"; -"web_learn_rescue_open" = "Open the gate"; -"web_learn_rescue_gate_case" = "Almost! equals tells capital and small letters apart. Write the word exactly as in the crossword."; -"web_learn_rescue_gate_wrong" = "The gate stays shut. Look at the yellow squares again."; -"web_learn_rescue_freed" = "🎉 The gate is open!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "What do you want to work with?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: structure charts and Java with the whole course. You can switch any time in the gear menu."; -"web_visubot_world_visubot_hint" = "VisuBot: program the mBot2 robot, with its own lessons and tasks. You can switch any time in the gear menu."; -"web_visubot_connect" = "Connect real mBot2"; -"web_visubot_upload_button" = "Upload"; -"web_visubot_blocks" = "Blocks"; -"web_visubot_blocks_title" = "Show the structure chart as blocks"; -"web_visubot_blocks_back" = "Structure chart"; -"web_visubot_blocks_back_title" = "Back to the structure chart"; -"web_visubot_blocks_start" = "When the program starts"; -"web_visubot_blocks_empty" = "A block goes here"; -"web_visubot_blocks_hint" = "The same program as blocks. Drag blocks in from “Elements”, move them around or drop them in the trash. Click a number or text in a white field to change it right in the block; everything else in the Object Inspector below. Drop sensors onto a condition, or right onto the field they should replace. Drag operators and variables onto a field or a condition in the same way."; -"web_visubot_blocks_trash" = "Trash"; -"web_visubot_blocks_count" = "count with"; -"web_visubot_blocks_count_from" = "from"; -"web_visubot_blocks_count_below" = "to below"; -"web_visubot_blocks_count_to" = "to"; -"web_visubot_blocks_count_above" = "to above"; -"web_visubot_blocks_count_step" = "by"; -"web_visubot_blocks_repeat" = "repeat"; -"web_visubot_blocks_if" = "if"; -"web_visubot_blocks_else" = "else"; -"web_visubot_blocks_while" = "while"; -"web_visubot_blocks_endless" = "forever"; -"web_visubot_blocks_do" = "do"; -"web_visubot_blocks_not" = "not"; -"web_visubot_blocks_and" = "and"; -"web_visubot_blocks_or" = "or"; -"web_visubot_blocks_break" = "leave the loop"; -"web_visubot_blocks_repeat_times" = "times"; -"web_visubot_blocks_random_from" = "random number from"; -"web_visubot_blocks_random_to" = "to"; -"web_visubot_operators" = "Operators"; -"web_visubot_variables" = "Variables"; -"web_visubot_control" = "Control"; -"web_visubot_el_repeat" = "repeat 10 times"; -"web_visubot_upload_title" = "Upload program to the mBot2"; -"web_visubot_upload_step1" = "Connect the mBot2 with the USB cable and switch it on. This only works in Chrome or Edge."; -"web_visubot_upload_step2" = "Click “Upload now” and choose “USB Serial”. The mBot2 saves your program and restarts."; -"web_visubot_upload_step3" = "Unplug the cable and put the mBot2 at the start. When you press button A, your program starts. It stays saved, even after switching off."; -"web_visubot_upload_start" = "Upload now"; -"web_visubot_upload_busy" = "Uploading … do not unplug the cable."; -"web_visubot_upload_done" = "Done! Unplug the cable, put the mBot2 at the start, press button A."; -"web_visubot_upload_failed" = "The upload did not work. Restart the mBot2 and try again."; -"web_visubot_upload_label" = "A = Start"; -"web_visubot_upload_error_input" = "Your program asks for an input. The mBot2 has no keyboard, so this does not work."; -"web_visubot_upload_error_files" = "Your program works with files. This does not work on the mBot2."; -"web_visubot_upload_error_empty" = "Your program is still empty."; -"web_visubot_disconnect" = "Disconnect"; -"web_visubot_connecting" = "Connecting … (the CyberPi restarts briefly)"; -"web_visubot_connected" = "Real mBot2 drives along"; -"web_visubot_no_serial" = "Real mBot2: only in Chrome or Edge on a computer."; -"web_visubot_link_error" = "Connection lost"; -"web_visubot_world" = "VisuBot world"; -"web_visubot_no_3d" = "The 3D view cannot start in this browser."; -"web_visubot_distance" = "Distance"; -"web_visubot_line" = "Line"; -"web_visubot_bumps" = "bumped into the wall"; -"web_visubot_el_forward" = "move forward"; -"web_visubot_el_backward" = "move back"; -"web_visubot_el_right" = "turn right"; -"web_visubot_el_left" = "turn left"; -"web_visubot_el_led" = "LEDs on"; -"web_visubot_el_tone" = "play a tone"; -"web_visubot_drive" = "Drive"; -"web_visubot_depth_fmt" = "The loops must be nested %d deep."; -"web_visubot_tap_part" = "Rotate the mBot2 with the mouse or your finger. Tap a part to see its name."; -"web_visubot_quiz_right" = "Correct!"; -"web_visubot_quiz_wrong_fmt" = "That is: %@. Try again."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Screen"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Buttons A, B and Home"; -"web_visubot_part_leds" = "RGB LEDs"; -"web_visubot_part_lightSensor" = "Light sensor"; -"web_visubot_part_microphone" = "Microphone"; -"web_visubot_part_shield" = "mBot2 shield with battery"; -"web_visubot_part_powerSwitch" = "Power switch"; -"web_visubot_part_motors" = "Encoder motors"; -"web_visubot_part_wheels" = "Wheels"; -"web_visubot_part_ultrasonic" = "Ultrasonic sensor"; -"web_visubot_part_lineSensor" = "Line sensor (quad RGB)"; -"web_visubot_part_caster" = "Ball caster"; -"web_visubot_part_chassis" = "Chassis"; -"web_visubot_world_fmt" = "World %d"; -"web_visubot_goal_reached" = "Goal reached"; -"web_visubot_goal_missed" = "Goal not reached"; -"web_visubot_commands" = "mBot2 commands"; -"web_visubot_sensors" = "mBot2 sensors"; -"web_visubot_sensor_distance" = "Distance"; -"web_visubot_sensor_line" = "Line"; -"web_visubot_el_led_off" = "LEDs off"; -"web_visubot_el_led_color" = "set LED colour"; -"web_visubot_el_wait" = "wait a moment"; -"web_visubot_el_emotion" = "show a feeling"; -"web_visubot_el_text" = "show text"; -"web_visubot_sensor_color" = "Colour is red?"; -"web_visubot_sensor_button_a" = "Button A pressed?"; -"web_visubot_sensor_button_b" = "Button B pressed?"; -"web_visubot_sensor_joy_up" = "Joystick up?"; -"web_visubot_sensor_joy_down" = "Joystick down?"; -"web_visubot_sensor_joy_left" = "Joystick left?"; -"web_visubot_sensor_joy_right" = "Joystick right?"; -"web_visubot_sensor_brightness" = "Brightness"; -"web_visubot_sensor_loudness" = "Loudness"; -"web_visubot_sensor_pitch" = "Tilt forward/back"; -"web_visubot_sensor_roll" = "Tilt sideways"; -"web_visubot_sensor_shake" = "Shaken?"; -"web_visubot_el_drive" = "wheels separately"; -"web_visubot_el_stop" = "stop"; -"web_visubot_el_timer" = "reset stopwatch"; -"web_visubot_sensor_line_left" = "Line left?"; -"web_visubot_sensor_line_right" = "Line right?"; -"web_visubot_sensor_timer" = "Stopwatch (s)"; -"web_visubot_sensor_battery" = "Battery (%)"; -"web_visubot_actions" = "Commands"; -"web_visubot_led_on" = "LEDs on"; -"web_visubot_led_off" = "LEDs off"; -"web_visubot_tone_yes" = "tone"; -"web_visubot_tone_no" = "no tone"; -"web_visubot_controls" = "Try the sensors"; -"web_visubot_controls_hint" = "Without a real mBot2: set here what the sensors measure. Buttons, joystick and shaking last for one second."; -"web_visubot_shake" = "shake"; -"web_visubot_check" = "Test the mBot2"; -"web_visubot_check_hint" = "Try each actuator once. The sensor values update live: hold your hand in front of the ultrasonic sensor, press the buttons, tilt or shake the mBot2. Lift it up for the wheels."; -"web_visubot_check_forward" = "Wheels 1 s forward"; -"web_visubot_check_spin" = "Turn on the spot for 1 s"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Where do you keep your work?"; -"web_learn_start_hint" = "Your learning progress and programs live in a folder on your device. The progress is stored there in the file learnProgress.json."; -"web_learn_start_continue_fmt" = "Continue with “%@”"; -"web_learn_start_open" = "Open existing folder"; -"web_learn_start_create_fmt" = "Create new folder “%@”"; -"web_learn_start_create_hint" = "Next, choose where it should go, e.g. in “Documents”."; -"web_learn_start_folder" = "VisuStruct-Files"; -"web_learn_start_without" = "Continue without a folder"; -"web_learn_start_without_hint" = "Your learning progress is then lost when you close the page."; -"web_visubot_preview_title" = "mBot2 live"; -"web_visubot_preview_hint" = "Shows right away where your program drives the mBot2."; -"web_visubot_preview_sim_title" = "Simulation"; -"web_visubot_preview_sim_hint" = "The mBot2 drives here while the simulation runs."; -"web_visubot_world_in_preview" = "The mBot2 drives in the “Simulation” window."; -"web_visubot_preview_play" = "Play the drive"; -"web_visubot_preview_stop" = "Stop"; -"web_visubot_preview_hide" = "Collapse mBot2 live"; -"web_visubot_preview_show" = "Show mBot2 live"; -"web_visubot_preview_resize" = "Resize mBot2 live"; -"web_visubot_preview_stack" = "Below the code in the Code area"; -"web_visubot_preview_side" = "As a column of its own"; -"web_visubot_preview_distance_fmt" = "Distance: %d cm"; -"web_visubot_preview_input" = "needs an input, start the simulation for that"; -"web_visubot_preview_limit" = "runs for a very long time (endless loop?), showing the start"; -"web_visubot_group_drive" = "Driving and turning"; -"web_visubot_group_wheels" = "Wheels separately"; -"web_visubot_group_leds" = "Light"; -"web_visubot_group_sound" = "Sound"; -"web_visubot_group_screen" = "Screen"; -"web_visubot_group_emotion" = "Emotions"; -"web_visubot_group_wait" = "Waiting and stopwatch"; -"web_visubot_group_distance" = "Distance"; -"web_visubot_group_line" = "Line"; -"web_visubot_group_color" = "Colours"; -"web_visubot_group_buttons" = "Buttons and joystick"; -"web_visubot_group_light" = "Light and loudness"; -"web_visubot_group_tilt" = "Tilt and shaking"; -"web_visubot_group_timer" = "Stopwatch and battery"; -"web_swap_to_code" = "Code large, structogram narrow"; -"web_swap_to_editor" = "Structogram large, code narrow"; -"web_editor_resize" = "Resize the structogram"; -"web_palette_resize" = "Resize the elements (double-click: reset)"; -"web_palette_hide" = "Hide elements"; -"web_palette_show" = "Show elements"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: write classes in Java and see or draw them as a class diagram (from year 12). You can switch any time in the gear menu."; -"web_visuobject_subtitle" = "Class diagram"; -"web_visuobject_code" = "Java code"; -"web_visuobject_diagram" = "Class diagram"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Add a new interface to the diagram and the code"; -"web_visuobject_layout" = "Arrange again"; -"web_visuobject_layout_tip" = "Arrange all classes automatically: superclasses at the top"; -"web_visuobject_add_field" = "new attribute …"; -"web_visuobject_add_method" = "new method …"; -"web_visuobject_delete_class" = "Delete class"; -"web_visuobject_new_class_name" = "NewClass"; -"web_visuobject_new_interface_name" = "NewInterface"; -"web_visuobject_example" = "Example"; -"web_visuobject_example_tip" = "Load the example project Zoo: Tier and Hund in package tiere, Besitzer in personen"; -"web_visuobject_new_tip" = "New: empty project (Undo brings the old one back)"; -"web_visuobject_empty" = "No class yet. Click “+ Class” on the left or type for example “public class Dog { }” into a file."; -"web_visuobject_edit_hint" = "Double-click to change. Write it as in UML: “- name: String” or “+ bark(): void”. Leave it empty to delete."; -"web_visuobject_member_label" = "Attribute or method in UML notation"; -"web_visuobject_name_label" = "Class name"; -"web_visuobject_bad_name" = "“%@” is not a valid name or already exists."; -"web_visuobject_bad_line" = "I don’t understand “%@”. Example: - name: String or + bark(): void"; -"web_visuobject_open_failed" = "The file could not be read."; -"web_visuobject_field_ph" = "- name: String"; -"web_visuobject_seq_diagram" = "Sequence diagram"; -"web_visuobject_seq_empty" = "The sequence diagram is built while debugging: click “Debug” above and step through the program."; -"web_visuobject_seq_no_calls" = "No calls of your own methods yet."; -"web_visuobject_seq_truncated" = "… shortened: too many calls from here on."; -"web_visuobject_seq_hint" = "Each arrow is a call of one of your methods, the dashed arrow brings the return value back. Click an arrow to see the line in the code."; -"web_visuobject_dbg" = "Debug"; -"web_visuobject_dbg_tip" = "Run the program step by step. Breakpoint: click the line number. The object diagram shows the objects in memory."; -"web_visuobject_dbg_continue" = "Resume"; -"web_visuobject_dbg_continue_tip" = "Run to the next breakpoint (F8)"; -"web_visuobject_dbg_pause" = "Pause"; -"web_visuobject_dbg_pause_tip" = "Pause the program"; -"web_visuobject_dbg_over" = "Step"; -"web_visuobject_dbg_over_tip" = "Next line; methods run completely (F6)"; -"web_visuobject_dbg_into" = "Into"; -"web_visuobject_dbg_into_tip" = "Step into the called method (F5)"; -"web_visuobject_dbg_out" = "Out"; -"web_visuobject_dbg_out_tip" = "Finish the method and return to the caller (F7)"; -"web_visuobject_dbg_input" = "Input for the Scanner, then Enter"; -"web_visuobject_dbg_running" = "Debugger: program running …"; -"web_visuobject_dbg_waiting" = "Debugger: the program is waiting for your input."; -"web_visuobject_dbg_paused_fmt" = "Debugger: paused in %1$@, line %2$@"; -"web_visuobject_dbg_exception" = "Program stopped with an exception."; -"web_visuobject_dbg_code_changed" = "Code changed: debugger stopped."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Call stack"; -"web_visuobject_dbg_vars" = "Variables"; -"web_visuobject_dbg_var_name" = "Name"; -"web_visuobject_dbg_var_type" = "Type"; -"web_visuobject_dbg_no_vars" = "No variables"; -"web_visuobject_dbg_err_syntax" = "%1$@, line %2$@: the debugger does not understand this (%3$@). ▶ Start shows Java's message."; -"web_visuobject_dbg_err_symbol" = "%1$@, line %2$@: %3$@ is unknown."; -"web_visuobject_dbg_err_import" = "%1$@, line %2$@: %3$@ is missing at the very top. Without this line Java doesn’t know the class."; -"web_visuobject_dbg_err_typename" = "%1$@, line %2$@: Java doesn’t know this data type. Did you mean %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, line %2$@: the debugger cannot do %3$@ yet. ▶ Start runs the program in real Java."; -"web_visuobject_dbg_err_type" = "%1$@, line %2$@: something does not fit here (%3$@)."; -"web_visuobject_obj_diagram" = "Object diagram"; -"web_visuobject_obj_new" = "New object of class"; -"web_visuobject_obj_add" = "Object"; -"web_visuobject_obj_empty" = "No objects yet. Choose a class above and click “+ Object”."; -"web_visuobject_obj_no_classes" = "First create a class (not abstract) in the class diagram."; -"web_visuobject_obj_hint" = "Click an object to change its name, class and values here. Drag the handle to another object to connect them through a reference attribute."; -"web_visuobject_obj_live_hint" = "Live from the debugger: this is how the objects look in memory right now."; -"web_visuobject_obj_link" = "Drag to another object: set a reference"; -"web_visuobject_obj_name" = "Name"; -"web_visuobject_obj_class" = "Class"; -"web_visuobject_obj_attribute" = "Attribute"; -"web_visuobject_obj_value" = "Value"; -"web_visuobject_obj_delete" = "Delete object"; -"web_visuobject_obj_bad_name" = "“%@” is not a valid object name."; -"web_visuobject_obj_name_taken" = "There is already an object “%@”."; -"web_visuobject_obj_no_ref" = "%1$@ has no attribute that can refer to %2$@."; -"web_visuobject_tool_note" = "Note"; -"web_visuobject_tool_note_tip" = "Note: click into the class diagram, then type the text. Drag the handle to a class for a dashed line."; -"web_visuobject_note_ph" = "Note …"; -"web_visuobject_note_text" = "Text"; -"web_visuobject_note_attached" = "Attached to"; -"web_visuobject_note_attach" = "Drag to a class: dashed line"; -"web_visuobject_note_delete" = "Delete note"; -"web_visuobject_run" = "Run"; -"web_visuobject_run_tip" = "Run the program with real Java: all classes are compiled, the class with main is started (preferably the open one)."; -"web_visuobject_stop" = "Stop"; -"web_visuobject_stop_tip" = "End the program and close its windows"; -"web_visuobject_stopped" = "Program stopped."; -"web_visuobject_no_main" = "No class with public static void main(String[] args) found."; -"web_visuobject_running_fmt" = "%@ is running …"; -"web_visuobject_console_clear" = "Clear"; -"web_visuobject_section_fields" = "Attributes"; -"web_visuobject_section_refs" = "Reference attributes"; -"web_visuobject_section_ctors" = "Constructor"; -"web_visuobject_section_methods" = "Methods"; -"web_visuobject_insp_sections" = "Insert section comments"; -"web_visuobject_insp_sort" = "Sort into sections"; -"web_visuobject_insp_sections_tip" = "Writes // Attributes, // Reference attributes, // Constructor and // Methods into the class and sorts everything below them. New members from the class diagram then go straight to the right place."; -"web_visuobject_insp_doc" = "Description (Javadoc)"; -"web_visuobject_insp_doc_ph" = "What is the class for? Written in the code as a Javadoc comment above the class."; -"web_visuobject_insp_visibility" = "Visibility"; -"web_visuobject_insp_type" = "Type"; -"web_visuobject_insp_init" = "Initial value"; -"web_visuobject_insp_no_fields" = "No attributes yet."; -"web_visuobject_insp_add_field" = "Attribute"; -"web_visuobject_insp_params" = "Parameters"; -"web_visuobject_insp_return" = "Return type"; -"web_visuobject_insp_constructor" = "Constructor"; -"web_visuobject_insp_no_methods" = "No methods yet."; -"web_visuobject_insp_add_method" = "Method"; -"web_visuobject_insp_generate" = "Generate:"; -"web_visuobject_insp_gen_constructor" = "Constructor from attributes"; -"web_visuobject_insp_gen_constructor_tip" = "Like Eclipse “Generate Constructor using Fields”: a constructor that sets all attributes (except static)."; -"web_visuobject_insp_gen_accessors" = "Getters and setters"; -"web_visuobject_insp_gen_accessors_tip" = "Like Eclipse “Generate Getters and Setters”: get…/set… for each attribute (final attributes get only a getter)."; -"web_visuobject_insp_bad_params" = "Write parameters like this: name: Type, second: int (read “%@”)."; -"web_visuobject_insp_fields" = "Attributes"; -"web_visuobject_insp_methods" = "Methods"; -"web_visuobject_inspector_resize" = "Resize: drag the border between class diagram and object inspector up or down (double-click = default)"; -"web_visuobject_dep_title" = "Dependency «%@»"; -"web_visuobject_dep_create_text" = "%1$@ creates an object of class %2$@ in %3$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ uses the class %2$@ in %3$@."; -"web_visuobject_dep_hint" = "This is not an association: %@ does not keep the %@ object in an attribute, it only lives inside the method. That is why the line is dashed."; -"web_visuobject_dep_to_assoc" = "Keep as attribute (association to %@)"; -"web_visuobject_end_mult_ph" = "mult."; -"web_visuobject_end_tip" = "Click (line selected) or right-click: role, multiplicity and navigability at this end"; -"web_visuobject_end_role_ph" = "e.g. myCats"; -"web_visuobject_new_menu" = "New"; -"web_visuobject_new_menu_tip" = "Create a new project, source folder, package, class or interface"; -"web_visuobject_new_project" = "Java project"; -"web_visuobject_new_folder" = "Source folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Class"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "New Java project"; -"web_visuobject_new_project_hint" = "In the workspace every project gets its own folder with src."; -"web_visuobject_new_folder_title" = "New source folder"; -"web_visuobject_new_folder_hint" = "Another folder for source code next to src, e.g. test for test classes."; -"web_visuobject_new_package_title" = "New package"; -"web_visuobject_new_package_hint" = "Package names are lower case, parts separated by dots: animals or school.office."; -"web_visuobject_new_class_title" = "New Java class"; -"web_visuobject_new_class_hint" = "Class names start with a capital letter, e.g. Dog or BankAccount."; -"web_visuobject_new_interface_title" = "New Java interface"; -"web_visuobject_new_interface_hint" = "An interface defines methods that classes implement later."; -"web_visuobject_new_root" = "Source folder"; -"web_visuobject_new_name" = "Name"; -"web_visuobject_new_modifiers" = "Modifiers"; -"web_visuobject_new_package_private" = "package (no modifier)"; -"web_visuobject_new_superclass" = "Superclass"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Extends interfaces (extends)"; -"web_visuobject_new_stubs" = "Which method stubs should be created?"; -"web_visuobject_new_stub_constructors" = "Constructors from superclass"; -"web_visuobject_new_stub_inherited" = "Inherited abstract methods"; -"web_visuobject_new_comment_legend" = "Comment"; -"web_visuobject_new_comment" = "Javadoc comment with @author"; -"web_visuobject_new_author" = "Your name"; -"web_visuobject_new_finish" = "Finish"; -"web_visuobject_new_name_missing" = "Please enter a name."; -"web_visuobject_new_exists" = "“%@” already exists in the project."; -"web_visuobject_new_upper" = "In Java, class names start with a capital letter."; -"web_visuobject_bad_folder" = "“%@” is not a valid folder name or already exists."; -"web_visuobject_inspector_hide" = "Collapse object inspector"; -"web_visuobject_inspector_show" = "Expand object inspector"; -"web_visuobject_inspector_empty" = "Click a relationship (line) in the diagram to edit it here."; -"web_visuobject_rel_end_a" = "End A: %@ (line starts here)"; -"web_visuobject_rel_end_b" = "End B: %@ (line ends here)"; -"web_visuobject_ws_title" = "Workspace folder on your computer"; -"web_visuobject_ws_question" = "Where do you keep your projects?"; -"web_visuobject_ws_hint" = "Like in Eclipse: a workspace folder on your computer with one folder per project, holding src and the .java files."; -"web_visuobject_ws_create" = "Create folder “%@”"; -"web_visuobject_ws_open" = "Open existing workspace"; -"web_visuobject_ws_skip" = "Without a folder (browser only)"; -"web_visuobject_ws_choose" = "Choose workspace"; -"web_visuobject_ws_reopen" = "Open workspace “%@”"; -"web_visuobject_ws_switch" = "Switch"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Download the project as .zip, e.g. to hand it in"; -"web_visuobject_ws_saved" = "Saved to %@/%@"; -"web_visuobject_ws_created" = "New project “%@” in the workspace"; -"web_visuobject_ws_failed" = "Saving to the workspace failed: %@"; -"web_visuobject_ws_taken" = "The workspace already has a project “%@”."; -"web_visuobject_ws_open_project" = "Open project %@"; -"web_visuobject_save_ws_tip" = "Save to the workspace (Ctrl+S)"; -"web_visuobject_unsaved" = "not saved"; -"web_visuobject_rel_nav_navigable" = "navigable (arrow)"; -"web_visuobject_rel_nav_non-navigable" = "non-navigable (cross ×)"; -"web_visuobject_rel_nav_unspecified" = "unspecified (open)"; -"web_visuobject_rel_reading_flip" = "reverse"; -"web_visuobject_rel_reading_auto" = "With a name, the relationship reads in the direction you dragged."; -"web_visuobject_tools" = "Tools"; -"web_visuobject_tool_select" = "Select"; -"web_visuobject_tool_select_tip" = "Select and move"; -"web_visuobject_tool_class" = "Class"; -"web_visuobject_tool_class_tip" = "Class: drag onto the canvas, or pick it and click on the canvas"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: drag onto the canvas, or pick it and click on the canvas"; -"web_visuobject_tool_link" = "Association"; -"web_visuobject_tool_link_tip" = "Association without navigation: draw a plain line between two classes"; -"web_visuobject_rel_link" = "Association (no navigation)"; -"web_visuobject_tool_not_link" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_association" = "Directed"; -"web_visuobject_tool_association_tip" = "Directed association: drag from the class that knows the other one to the other (arrow)"; -"web_visuobject_tool_aggregation" = "Aggregation"; -"web_visuobject_tool_aggregation_tip" = "Aggregation: drag from the whole to the part (has, parts live on)"; -"web_visuobject_tool_composition" = "Composition"; -"web_visuobject_tool_composition_tip" = "Composition: drag from the whole to the part (consists of, parts belong to it)"; -"web_visuobject_tool_extends" = "Inheritance"; -"web_visuobject_tool_extends_tip" = "Inheritance: drag from the subclass to the superclass (extends)"; -"web_visuobject_tool_implements" = "Realization"; -"web_visuobject_tool_implements_tip" = "Realization: drag from the class to the interface (implements)"; -"web_visuobject_tool_pick_second" = "Now click the second class (from %@). Esc cancels."; -"web_visuobject_tool_not_association" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_aggregation" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_composition" = "%@ is an interface and cannot have attributes. Drag the relationship from a class."; -"web_visuobject_tool_not_extends" = "%@ cannot inherit from %@: classes extend classes, interfaces extend interfaces. For an interface use realization."; -"web_visuobject_tool_not_implements" = "%@ cannot implement %@: only a class implements an interface."; -"web_visuobject_inspector" = "Object inspector"; -"web_visuobject_connect" = "Drag: relationship to another class"; -"web_visuobject_rel_association" = "Association"; -"web_visuobject_rel_directed" = "Directed association"; -"web_visuobject_rel_aggregation" = "Aggregation (has, parts live on)"; -"web_visuobject_rel_composition" = "Composition (consists of, parts belong to it)"; -"web_visuobject_rel_extends" = "Inheritance (extends)"; -"web_visuobject_rel_implements" = "Realization (implements)"; -"web_visuobject_rel_extends_text" = "%@ inherits from %@ (extends in the code)."; -"web_visuobject_rel_implements_text" = "%@ implements the interface %@ (implements in the code)."; -"web_visuobject_rel_kind" = "Kind"; -"web_visuobject_rel_name" = "Name of the relationship"; -"web_visuobject_rel_name_ph" = "e.g. owns"; -"web_visuobject_rel_reading" = "Reading direction"; -"web_visuobject_rel_navigable" = "Navigability"; -"web_visuobject_rel_role" = "Role"; -"web_visuobject_rel_multiplicity" = "Multiplicity"; -"web_visuobject_rel_role_field" = "= attribute %@.%@ in the code"; -"web_visuobject_rel_role_only_uml" = "diagram only (no attribute while the arrow points one way)"; -"web_visuobject_rel_delete" = "Delete relationship"; -"web_visuobject_rel_impossible" = "There is no suitable relationship from %@ to %@."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Drag to resize (double-click: default)"; -"web_visuobject_pane_show" = "Expand area"; -"web_visuobject_pane_hide" = "Collapse area"; -"web_visuobject_close_tab" = "Close %@"; -"web_visuobject_collapse" = "Collapse"; -"web_visuobject_expand" = "Expand"; -"web_visuobject_tree_empty" = "Still empty. Create a package or class with “+ New”."; -"web_visuobject_delete" = "Delete"; -"web_visuobject_default_package" = "(default package)"; -"web_visuobject_bad_package" = "“%@” is not a valid package name or already exists. Package names are lower case, e.g. animals."; -"web_visuobject_new_package_name" = "newpackage"; -"web_visuobject_no_file" = "No file open"; -"web_visuobject_project_name" = "MyProject"; -"web_visuobject_explorer_show" = "Show Package Explorer"; -"web_visuobject_explorer_hide" = "Collapse Package Explorer"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Save the project as .zip: ProjectName/src/package/Class.java"; -"web_visuobject_open_tip" = "Open a project (.zip as from Eclipse or BlueJ) or add .java files to the project"; -"web_visuobject_method_ph" = "+ bark(): void"; -"web_code_scopes" = "Colored blocks"; -"web_code_scopes_hint" = "Code and structogram: loop pink, branch blue, method yellow, class green"; -"web_pseudocode" = "Pseudocode"; - -/* VisuFlow (draw flowcharts) */ -"web_visuflow_subtitle" = "Draw flowcharts"; -"web_visuflow_world_hint" = "VisuFlow: draw a program flowchart freely and export it as an image or PDF. You can switch at any time in the gear menu."; -"web_visuflow_palette" = "Symbols"; -"web_visuflow_palette_hint" = "Tap a symbol to add it below the selected one, joined by an arrow. You can also drag it onto the canvas."; -"web_visuflow_shape_start" = "Start"; -"web_visuflow_shape_ende" = "End"; -"web_visuflow_shape_anweisung" = "Statement"; -"web_visuflow_shape_ein" = "Input/Output"; -"web_visuflow_shape_verzweigung" = "Decision"; -"web_visuflow_shape_unterprogramm" = "Subroutine"; -"web_visuflow_shape_start_tip" = "Oval: the flow starts here"; -"web_visuflow_shape_ende_tip" = "Oval: the flow ends here"; -"web_visuflow_shape_anweisung_tip" = "Rectangle: one statement, for example “Boil water”"; -"web_visuflow_shape_ein_tip" = "Parallelogram: input or output, for example “Input: number”"; -"web_visuflow_shape_verzweigung_tip" = "Diamond: a question with the exits yes and no"; -"web_visuflow_shape_unterprogramm_tip" = "Rectangle with two bars: calls a subroutine"; -"web_visuflow_text_start" = "Start"; -"web_visuflow_text_ende" = "End"; -"web_visuflow_text_anweisung" = "Statement"; -"web_visuflow_text_ein" = "Input: number"; -"web_visuflow_text_verzweigung" = "Condition?"; -"web_visuflow_text_unterprogramm" = "Subroutine"; -"web_visuflow_new_tip" = "New: empty flowchart with Start (Undo brings the old one back)"; -"web_visuflow_open_tip" = "Open a flowchart file (.visuflow)"; -"web_visuflow_save_tip" = "Save the flowchart as a file (.visuflow)"; -"web_visuflow_delete" = "Delete"; -"web_visuflow_delete_tip" = "Delete the selected symbol or arrow (Del)"; -"web_visuflow_swap" = "swap yes/no"; -"web_visuflow_swap_tip" = "Diamond: “yes” and “no” swap sides"; -"web_visuflow_example" = "Example"; -"web_visuflow_example_tip" = "Load the example “Make tea” from lesson 1 (Undo brings the old flowchart back)"; -"web_visuflow_example_title" = "Make tea"; -"web_visuflow_example_water" = "Boil water"; -"web_visuflow_example_bag" = "Tea bag into the cup"; -"web_visuflow_example_hot" = "Tea still too hot?"; -"web_visuflow_example_wait" = "Wait"; -"web_visuflow_example_drink" = "Drink tea"; -"web_visuflow_title_label" = "Flowchart title"; -"web_visuflow_title_ph" = "Flowchart title"; -"web_visuflow_canvas_label" = "Flowchart canvas"; -"web_visuflow_open_error" = "This file is not a VisuFlow flowchart."; -"web_visuflow_saved" = "Saved as %@"; -"web_visuflow_status_empty" = "Drag a symbol from the bar onto the canvas or tap it."; -"web_visuflow_status_idle" = "Tap a symbol to select it. Drag from the dot at its exit to draw an arrow to the next symbol."; -"web_visuflow_status_node" = "Drag to move the symbol, tap again to change the text. Drag an arrow from the dot at the exit."; -"web_visuflow_status_edge" = "Arrow selected. Delete removes it."; -"web_visuflow_status_connect" = "Let go over the symbol the arrow leads to."; -"web_visuflow_edit_label" = "Text in the symbol"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Rename variable …"; -"web_rename_title" = "Rename variable"; -"web_rename_old" = "Name"; -"web_rename_new" = "New name"; -"web_rename_hint" = "Changes every place with exactly this spelling (number is not Number), in the code and in the structogram. Text in \\"…\\" and comments stay."; -"web_rename_count_fmt" = "Used %d times."; -"web_rename_exists_fmt" = "“%@” already exists: afterwards both would be one variable."; -"web_rename_invalid" = "A name starts with a letter and contains only letters, digits and _."; -"web_rename_keyword_fmt" = "“%@” is a Java keyword."; -"web_rename_apply" = "Rename"; -"web_rename_cancel" = "Cancel"; -"web_visubot_world_visucode_hint" = "Other programs like %1$@ or %2$@ are on visucode.de."; -`;export{e as default}; diff --git a/website/app/assets/es-or5dA7qu.js b/website/app/assets/es-or5dA7qu.js deleted file mode 100644 index 086a99d8..00000000 --- a/website/app/assets/es-or5dA7qu.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Textos que solo existen en la versión web. Todo lo demás procede de los Localizable.strings de la app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagrama de VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Supr"; -"web_key_shift" = "Mayús"; -"web_export" = "Exportar…"; -"web_export_title" = "Guardar como imagen o PDF, o imprimir"; -"web_export_png" = "Imagen (PNG)"; -"web_export_svg" = "Gráfico vectorial (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Archivo VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Error al exportar: %@"; -"web_print_hint" = "Usa “Exportar… → PDF” para guardar el diagrama e imprimirlo desde allí."; -"web_delete" = "Eliminar"; -"web_language_title" = "Idioma de la interfaz"; -"web_simulation_start" = "Iniciar simulación"; -"web_simulation_end" = "Terminar simulación"; -"web_simulation_start_title" = "Ejecutar el diagrama paso a paso"; -"web_simulation_end_title" = "Terminar la simulación y seguir editando"; -"web_palette_hint" = "Arrastrar al diagrama"; -"web_tabs_label" = "Diagramas"; -"web_status_modified" = "modificado"; -"web_inspector_title" = "Propiedades"; -"web_inspector_resize" = "Cambiar la altura del inspector de objetos: arrastra o desliza la línea hacia arriba o abajo (doble clic: estándar)"; -"web_inspector_help_drop" = "Arrastra elementos de la paleta sobre una pieza: arriba = antes, abajo = después, sobre “ø” = reemplazar. Los elementos existentes se pueden mover con el ratón."; -"web_inspector_help_inline" = "Haz doble clic o doble toque en un elemento para editar su texto directamente en el diagrama (Intro aplica, Esc cancela). También los nombres de los casos."; -"web_inspector_help_files" = "También puedes soltar archivos en la ventana."; -"web_case_name" = "Nombre del caso"; -"web_case_name_placeholder" = "p. ej. 1, ok, default"; -"web_copied" = "Elemento copiado."; -"web_discard_message_fmt" = "Se perderán los cambios en “%@”."; -"web_err_unreadable_fmt" = "“%@” no es un archivo de VisuStruct legible (%@)."; -"web_saved_fmt" = "“%@” guardado."; -"web_saved_txt_fmt" = "Guardado como “%@”. Quita la extensión .txt para usarlo."; -"web_drop_impossible" = "No se puede soltar aquí"; -"web_sim_delay" = "Pausa por paso"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Lenguaje"; -"web_codegen_comments" = "Texto del diagrama como comentarios"; -"web_codegen_empty_else" = "Generar rama else vacía en el código"; -"web_copied_check" = "Copiado ✓"; -"web_copy_manual" = "Seleccionado, pulsa Ctrl+C para copiar"; -"web_run_stopped_loop" = "Ejecución detenida: posible bucle infinito."; -"web_run_cancelled" = "Ejecución cancelada."; -"web_run_error" = "Error durante la ejecución."; -"web_run_dropped_fmt" = "… %d líneas anteriores ocultas"; -"web_sim_call_arguments_fmt" = "La llamada no coincide con la cabecera de %@. Revisa los valores entre paréntesis."; -"web_sim_missing_return_fmt" = "%@ terminó sin devolver un valor con return."; -"web_sim_file_not_found_fmt" = "No existe ningún archivo llamado %@."; -"web_sim_end_of_file_fmt" = "El archivo %@ no tiene más líneas."; -"web_sim_null_value_fmt" = "%@ es null."; -"web_sim_unknown_variable_case_fmt" = "Variable desconocida: %1$@. Java distingue entre mayúsculas y minúsculas: ¿querías decir %2$@?"; -"web_sim_written_file_fmt" = "%@ (guardado)"; -"web_site_home" = "Página de inicio"; -"web_site_imprint" = "Aviso legal"; -"web_site_privacy" = "Privacidad"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programación"; -"web_live_hide" = "Ocultar programación"; -"web_scale_canvas" = "Tamaño del texto: diagrama"; -"web_scale_code" = "Tamaño del texto: programación"; -"web_live_indent" = "Sangrar"; -"web_live_indent_title" = "Sangrar el código: cuatro espacios por nivel (líneas seleccionadas, si no, todo)"; -"web_live_show" = "Mostrar programación"; -"web_live_resize" = "Cambiar el ancho del área de código (doble clic: restablecer)"; -"web_live_hint" = "El código crece con tu diagrama. Gris = marcador: escribe en el inspector y tu texto aparecerá ahí. Haz clic en una línea para seleccionar su elemento."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Tu código"; -"web_live_ph_end" = "fin"; -"web_live_language" = "Lenguaje de programación"; -"web_live_java_class" = "Como clase"; -"web_live_java_class_hint" = "Nombre de la clase = título del diagrama"; -"web_live_java_main" = "Con método main"; -"web_settings_display" = "Visualización"; -"web_live_swing" = "Java real (con Swing)"; -"web_live_swing_hint" = "Java real con ventanas (CheerpJ). Descarga unos 40 MB una sola vez, en la primera ejecución."; -"web_live_swing_windows" = "Ventanas de Java"; -"web_live_swing_hide" = "Cerrar ventanas de Java"; -"web_live_swing_loading" = "Cargando Java (unos 40 MB la primera vez) …"; -"web_live_swing_compiling" = "Compilando …"; -"web_live_swing_running" = "El programa se está ejecutando."; -"web_live_swing_compile_failed" = "Error de compilación, mira arriba."; -"web_live_swing_failed" = "No se pudo cargar Java. Comprueba la conexión a internet."; -"web_live_hint_edit" = "Escribe aquí código Java y el diagrama se construye solo. O trabaja en el diagrama y el código crece con él. Gris = marcador, escribe encima."; -"web_live_edit_label" = "Código Java"; -"web_parse_line_fmt" = "Línea %d: %@"; -"web_parse_missing" = "Aquí falta: %@"; -"web_parse_extra" = "Sobra: %@"; -"web_parse_unclosed_string" = "Falta el %@ de cierre."; -"web_parse_else_without_if" = "Este «else» no tiene un «if»."; -"web_parse_case_outside_switch" = "«case» y «default» van dentro de un «switch»."; -"web_parse_missing_do_while" = "Después de «do { … }» falta «while (condición);»."; -"web_parse_more_methods" = "Por ahora solo cuenta un método. Crea otros subprogramas en pestañas propias."; -"web_parse_missing_import" = "Falta %@ al principio. Sin esta línea, Java no conoce Scanner."; -"web_parse_missing_scanner" = "Falta antes %@ Sin esta línea, el Scanner no existe."; -"web_parse_unknown_type" = "Java no conoce este tipo de datos. ¿Querías decir %@?"; -"web_quickfix_insert_fmt" = "Sugerencia: insertar %@"; -"web_quickfix_replace_fmt" = "Sugerencia: %1$@ en lugar de %2$@"; -"web_parse_wrong_case" = "Java distingue entre mayúsculas y minúsculas. Lo correcto es %@."; -"web_editor_title" = "Diagrama"; -"web_editor_show" = "Mostrar diagrama"; -"web_editor_hide" = "Ocultar diagrama"; -"web_keywords_title" = "Palabras clave de código"; -"web_keywords_hint" = "if, while, for en lugar de los nombres de los elementos"; -"web_palette_compact" = "Elementos siempre compactos"; -"web_palette_compact_hint" = "Símbolo con el nombre pequeño debajo; si no, solo con la columna estrecha"; -"web_about_version_fmt" = "Versión %@"; -"web_about_software" = "Software utilizado"; -"web_about_software_text" = "El editor de código usa CodeMirror y Lezer (licencia MIT). Con la compatibilidad con Swing, VisuStruct carga CheerpJ de Leaning Technologies (licencia Community) y javac de OpenJDK."; -"web_about_licenses" = "Textos de las licencias"; -"web_live_run" = "Ejecutar"; -"web_live_run_title" = "Ejecutar el programa: la salida y la entrada aparecen abajo"; -"web_live_run_finished" = "Programa terminado."; -"web_live_run_stopped" = "Detenido."; -"web_console" = "Consola"; -"web_console_hide" = "Ocultar consola"; -"web_console_resize" = "Cambiar la altura de la consola"; -"web_diagram_tools" = "Herramientas del diagrama"; -"web_files_title" = "Archivos"; -"web_files_show" = "Mostrar archivos"; -"web_files_hide" = "Ocultar archivos"; -"web_files_resize" = "Cambiar el ancho de archivos"; -"web_files_new_folder" = "Nueva carpeta"; -"web_files_switch" = "Elegir otra carpeta"; -"web_files_delete" = "Eliminar"; -"web_files_in_browser" = "En el almacenamiento de este navegador"; -"web_files_save_hint_fmt" = "Al guardar, el programa se coloca en %@."; -"web_files_empty" = "Aún no hay nada guardado. Al guardar, VisuStruct crea aquí una carpeta con la fecha de hoy."; -"web_files_reconnect_hint" = "Tras recargar, el navegador vuelve a preguntar si VisuStruct puede usar tu carpeta."; -"web_files_reconnect_fmt" = "Abrir la carpeta «%@»"; -"web_files_connect_hint" = "Elige una carpeta como espacio de trabajo. VisuStruct crea en ella una carpeta para cada día y guarda el diagrama y el código."; -"web_files_connect" = "Elegir carpeta"; -"web_files_browser_hint" = "Este navegador no puede abrir carpetas del dispositivo (por ejemplo en el iPad). Tus archivos quedan en el almacenamiento de este navegador."; -"web_files_browser" = "Guardar en el navegador"; -"web_files_delete_confirm_fmt" = "¿Eliminar «%@»?"; -"web_files_folder_prompt" = "Nombre de la nueva carpeta:"; -"web_files_default_name" = "Programa"; -"web_files_saved_fmt" = "Guardado: %@"; -"web_files_create" = "Crear"; -"web_learn_ludwig_tip" = "El consejo de Ludwig"; -"web_game_title" = "Juego: ¿Verdadero o falso?"; -"web_game_intro" = "Ludwig te muestra valores y una condición. ¿Es verdadera (true) o falsa (false)? Diez rondas, cada vez más difíciles."; -"web_game_start" = "Empezar el juego"; -"web_game_round_fmt" = "Ronda %@ de %@"; -"web_game_score_fmt" = "Puntos: %@"; -"web_game_true" = "verdadero (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "¡Correcto!"; -"web_game_wrong" = "No del todo. Así se calcula:"; -"web_game_next" = "Siguiente"; -"web_game_finish" = "Resultado"; -"web_game_done_fmt" = "%@ de %@ correctas."; -"web_game_perfect" = "¡Todo correcto! Ludwig está impresionado."; -"web_game_good" = "¡Muy bien! Revisa tus errores e inténtalo de nuevo."; -"web_game_practice" = "La práctica hace al maestro. Vuelve a leer la tabla de verdad y juega otra ronda."; -"web_game_again" = "Jugar otra vez"; -"web_game_wrong_short" = "Lo siento, es incorrecto."; -"web_game_practice_short" = "La práctica hace al maestro. Vuelve a leer las explicaciones y juega otra ronda."; -"web_game_clicks" = "Clics:"; -"web_game_nobody" = "Nadie escucha: no pasa nada."; -"web_game_event_fmt" = "Clic en «%@»: el oyente lo oye y ejecuta su código."; -"web_game_try" = "Ahora prueba tú los botones de la ventana."; -"web_game_time_fmt" = "Quedan %@ s"; -"web_game_defused" = "✂️ ¡Desactivada! Has encontrado el error."; -"web_game_boom_time" = "💥 ¡Bum! Se acabó el tiempo."; -"web_game_boom_wrong" = "💥 ¡Bum! Esa no era la línea."; -"web_game_boom_part" = "💥 ¡Bum! La línea es correcta, pero el error está en otra parte."; -"web_game_boom_wrong_part" = "💥 ¡Bum! El error está en otra línea."; -"web_game_bomb_parts_hint" = "Toca exactamente la parte incorrecta."; -"web_game_fix" = "La línea correcta:"; -"web_game_order_hint" = "Toca los pasos en el orden correcto."; -"web_debug_step" = "Paso a paso"; -"web_debug_step_title" = "Paso a paso: ejecutar una instrucción y detenerse de nuevo"; -"web_debug_continue" = "Continuar hasta el siguiente punto de interrupción"; -"web_debug_paused_fmt" = "Detenido antes de la línea %d."; -"web_debug_gutter_title" = "Haz clic en un número de línea para poner o quitar un punto de interrupción"; -"web_swap_to_code" = "Programación grande"; -"web_swap_to_editor" = "Diagrama grande"; -"web_editor_resize" = "Cambiar el ancho del diagrama"; -"web_palette_resize" = "Cambiar el ancho de los elementos"; -"web_palette_hide" = "Ocultar elementos"; -"web_palette_show" = "Mostrar elementos"; -"web_code_scopes" = "Bloques de código en color"; -"web_code_scopes_hint" = "Clase, método, bucle, condición en sus propias cajas"; -"web_pseudocode" = "Pseudocódigo"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Renombrar variable …"; -"web_rename_title" = "Renombrar variable"; -"web_rename_old" = "Nombre"; -"web_rename_new" = "Nombre nuevo"; -"web_rename_hint" = "Cambia cada lugar con exactamente esta escritura (numero no es Numero), en el código y en el estructograma. El texto entre \\"…\\" y los comentarios no cambian."; -"web_rename_count_fmt" = "Aparece %d veces."; -"web_rename_exists_fmt" = "«%@» ya existe: después ambas serían una sola variable."; -"web_rename_invalid" = "Un nombre empieza con una letra y solo contiene letras, cifras y _."; -"web_rename_keyword_fmt" = "«%@» es una palabra clave de Java."; -"web_rename_apply" = "Renombrar"; -"web_rename_cancel" = "Cancelar"; -`;export{e as default}; diff --git a/website/app/assets/flowApp-BYyoSxMn.js b/website/app/assets/flowApp-BYyoSxMn.js deleted file mode 100644 index c948f479..00000000 --- a/website/app/assets/flowApp-BYyoSxMn.js +++ /dev/null @@ -1,50 +0,0 @@ -var dt=Object.defineProperty;var lt=(i,t,e)=>t in i?dt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var y=(i,t,e)=>lt(i,typeof t!="symbol"?t+"":t,e);import{t as h,g as ct,i as I,w as ht,a as ut,q as ft,h as pt,u as G,v as gt,x as wt,y as vt,z as mt,A as xt,n as yt,o as bt}from"./index-BqSgHxq8.js";const et=["start","anweisung","ein","verzweigung","unterprogramm","ende"],m=20,st={start:"#d1f2d9",ende:"#d1f2d9",anweisung:"#e5f0ff",ein:"#f2e6ff",verzweigung:"#fff4d6",unterprogramm:"#e5f0ff"},z=16,_t=170;function $t(i,t,e){const n=[];for(const s of i.split(` -`)){let o="";for(const r of s.split(/\s+/).filter(Boolean)){const a=o?`${o} ${r}`:r;o&&e(a)>t?(n.push(o),o=r):o=a}n.push(o)}return n.length?n:[""]}const U=i=>Math.round(i/m)*m;function C(i,t){const e=$t(i.text,_t,t),n=Math.max(0,...e.map(t)),s=e.length*z;let o,r;if(i.shape==="verzweigung")o=Math.max(140,n*1.7+30),r=Math.max(80,s*1.8+24);else{const a=i.shape==="ein"?44:i.shape==="unterprogramm"?40:i.shape==="anweisung"?24:36;o=Math.max(i.shape==="start"||i.shape==="ende"?100:120,n+a),r=Math.max(40,s+18)}return{x:i.x,y:i.y,w:Math.ceil(o/m)*m,h:Math.ceil(r/m)*m,lines:e}}function V(i){return i==="ende"?[]:i==="verzweigung"?["ja","nein"]:["out"]}const P=i=>i!=="start";function j(i,t,e){var s;if(e==="out")return"bottom";const n=((s=i.yesRight)==null?void 0:s.includes(t.id))??!1;return e==="ja"===n?"right":"bottom"}function F(i,t){return t==="bottom"?[i.x,i.y+i.h/2]:[i.x+i.w/2,i.y]}const Lt=i=>[i.x,i.y-i.h/2];function Et(i,t,e,n=0){const[s,o]=F(i,t),[r,a]=Lt(e),d=m,u=Math.max(i.x+i.w/2,e.x+e.w/2)+d*(1.5+n);if(t==="bottom"){if(a>=o+d){if(s===r)return[[s,o],[r,a]];const l=o+d;return[[s,o],[s,l],[r,l],[r,a]]}return[[s,o],[s,o+d],[u,o+d],[u,a-d],[r,a-d],[r,a]]}const p=e.x-e.w/2;if(p>=s+d&&Math.abs(e.y-o)=o+d){if(r>=s+d)return[[s,o],[r,o],[r,a]];const l=s+d;return[[s,o],[l,o],[l,a-d],[r,a-d],[r,a]]}const f=Math.max(s+d,u);return[[s,o],[f,o],[f,a-d],[r,a-d],[r,a]]}function nt(i=""){return{title:i,nodes:[],edges:[]}}function it(i,t){const e=new Set([...i.nodes.map(s=>s.id),...i.edges.map(s=>s.id)]);let n=1;for(;e.has(`${t}${n}`);)n++;return`${t}${n}`}function q(i,t,e,n,s){const o=it(i,"n");return{doc:{...i,nodes:[...i.nodes,{id:o,shape:t,x:U(e),y:U(n),text:s}]},id:o}}function J(i,t,e,n){return{...i,nodes:i.nodes.map(s=>s.id===t?{...s,x:Math.max(m*4,U(e)),y:Math.max(m*2,U(n))}:s)}}function kt(i,t,e){return{...i,nodes:i.nodes.map(n=>n.id===t?{...n,text:e}:n)}}function _(i,t,e,n){const s=i.nodes.find(a=>a.id===t),o=i.nodes.find(a=>a.id===n);if(!s||!o||t===n||!V(s.shape).includes(e)||!P(o.shape))return i;const r=i.edges.filter(a=>!(a.from===t&&a.port===e));return{...i,edges:[...r,{id:it(i,"e"),from:t,port:e,to:n}]}}function St(i,t){return V(t.shape).find(e=>!i.edges.some(n=>n.from===t.id&&n.port===e))}function Mt(i,t){var e;return{...i,nodes:i.nodes.filter(n=>n.id!==t),edges:i.edges.filter(n=>n.from!==t&&n.to!==t),yesRight:(e=i.yesRight)==null?void 0:e.filter(n=>n!==t)}}function At(i,t){return{...i,edges:i.edges.filter(e=>e.id!==t)}}function ot(i,t){const e=new Set(i.yesRight??[]);return e.has(t)?e.delete(t):e.add(t),{...i,yesRight:[...e]}}function b(i){return JSON.stringify({format:"visuflow",version:1,...i},null,2)}function W(i){let t;try{t=JSON.parse(i)}catch{return}if(!t||typeof t!="object")return;const e=t;if(!Array.isArray(e.nodes)||!Array.isArray(e.edges))return;const n=[];for(const o of e.nodes)!o||typeof o.id!="string"||!et.includes(o.shape)||!Number.isFinite(o.x)||!Number.isFinite(o.y)||n.some(r=>r.id===o.id)||n.push({id:o.id,shape:o.shape,x:o.x,y:o.y,text:typeof o.text=="string"?o.text:""});let s={title:typeof e.title=="string"?e.title:"",nodes:n,edges:[]};for(const o of e.edges)!o||typeof o.from!="string"||typeof o.to!="string"||typeof o.port!="string"||(s=_(s,o.from,o.port,o.to));return Array.isArray(e.yesRight)&&(s.yesRight=e.yesRight.filter(o=>typeof o=="string"&&n.some(r=>r.id===o))),s}const H="http://www.w3.org/2000/svg",X="visuflow.doc",It=100,Y=14,rt=`${Y}px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif`,T="#1c1c1e",w=i=>i.replace(/[&<>"]/g,t=>({"&":"&","<":"<",">":">",'"':"""})[t]);function Ct(i){try{return localStorage.getItem(i)}catch{return null}}function Z(i,t){try{localStorage.setItem(i,t)}catch{}}function Rt(){return yt("visustruct",bt())}function D(i,t,e){const n=URL.createObjectURL(new Blob([t],{type:e})),s=document.createElement("a");s.href=n,s.download=i,s.click(),setTimeout(()=>URL.revokeObjectURL(n),1e3)}function v(i,t={}){const e=document.createElementNS(H,i);for(const[n,s]of Object.entries(t))e.setAttribute(n,String(s));return e}let N;function R(i){return N===void 0&&(N=document.createElement("canvas").getContext("2d")),N?(N.font=rt,N.measureText(i).width):i.length*Y*.55}const Tt=i=>h(`web_visuflow_shape_${i}`),B=i=>h(`web_visuflow_text_${i}`);function at(i,t){const e=t.x-t.w/2,n=t.x+t.w/2,s=t.y-t.h/2,o=t.y+t.h/2;return i==="verzweigung"?v("path",{d:`M${t.x} ${s} L${n} ${t.y} L${t.x} ${o} L${e} ${t.y} Z`}):i==="ein"?v("path",{d:`M${e+14} ${s} L${n} ${s} L${n-14} ${o} L${e} ${o} Z`}):i==="unterprogramm"?v("path",{d:`M${e} ${s} H${n} V${o} H${e} Z M${e+10} ${s} V${o} M${n-10} ${s} V${o}`}):v("rect",{x:e,y:s,width:t.w,height:t.h,rx:i==="anweisung"?2:t.h/2})}function Bt(i){const t=v("text",{"text-anchor":"middle","font-size":Y,fill:T});return i.lines.forEach((e,n)=>{const s=v("tspan",{x:i.x,y:i.y+5+(n-(i.lines.length-1)/2)*z});s.textContent=e,t.append(s)}),t}function Q(i){const t=v("svg",{viewBox:"0 0 64 36",width:64,height:36,"aria-hidden":"true"}),e=at(i,{x:32,y:18,w:i==="verzweigung"?54:56,h:i==="verzweigung"?32:26});return e.setAttribute("fill",st[i]),e.setAttribute("stroke",T),e.setAttribute("stroke-width","1.5"),t.append(e),t.outerHTML}function tt(){let i=nt(h("web_visuflow_example_title"));const t=[],e=(p,f,l,c)=>{const g=q(i,p,f,l,c);i=g.doc,t.push(g.id)};e("start",240,60,B("start")),e("anweisung",240,160,h("web_visuflow_example_water")),e("anweisung",240,260,h("web_visuflow_example_bag")),e("verzweigung",240,380,h("web_visuflow_example_hot")),e("anweisung",480,380,h("web_visuflow_example_wait")),e("anweisung",240,500,h("web_visuflow_example_drink")),e("ende",240,600,B("ende"));const[n,s,o,r,a,d,u]=t;return i=_(i,n,"out",s),i=_(i,s,"out",o),i=_(i,o,"out",r),i=ot(i,r),i=_(i,r,"ja",a),i=_(i,a,"out",r),i=_(i,r,"nein",d),i=_(i,d,"out",u),i}function Nt(){return q(nt(),"start",240,60,B("start")).doc}class Pt{constructor(t){y(this,"shell");y(this,"svg");y(this,"scroller");y(this,"status");y(this,"titleInput");y(this,"doc");y(this,"selection");y(this,"history",[]);y(this,"future",[]);y(this,"editor");y(this,"dragLine");document.documentElement.classList.add("visuflow-theme"),history.replaceState(history.state,"",`${location.pathname}?visuflow${location.hash}`),this.doc=W(Ct(X)??"")??tt(),this.shell=document.createElement("div"),this.shell.className="shell visuflow",t.replaceWith(this.shell),this.build(),this.render(),window.addEventListener("keydown",e=>this.onKey(e))}build(){const t=(l,c,g,x=c)=>``,e=et.map(l=>``).join("");this.shell.innerHTML=` -
    - VisuFlow ${w(h("web_visuflow_subtitle"))} -
    - ${t("new",h("toolbar_new"),I.newFile,h("web_visuflow_new_tip"))} - ${t("open",h("menu_open"),I.open,h("web_visuflow_open_tip"))} - ${t("save",h("menu_save"),I.save,h("web_visuflow_save_tip"))} - ${t("undo",h("toolbar_undo"),I.undo)} - ${t("redo",h("toolbar_redo"),I.redo)} - ${t("delete",h("web_visuflow_delete"),I.trash,h("web_visuflow_delete_tip"))} - - -
    -
    - - -
    -
    - - -
    -
    -
    - -
    -
    -
    - -
    -

    -
    -
    - `;const n=l=>this.shell.querySelector(l);this.svg=n(".vf-canvas"),this.scroller=n(".vf-scroll"),this.status=n(".vf-status"),this.titleInput=n(".vf-title");const s=(l,c)=>n(`[data-action=${l}]`).addEventListener("click",c);s("new",()=>this.replace(Nt())),s("example",()=>this.replace(tt())),s("open",()=>n("input[type=file]").click()),s("save",()=>this.save()),s("undo",()=>this.undo(!1)),s("redo",()=>this.undo(!0)),s("delete",()=>this.deleteSelection()),s("swap",()=>{var l;((l=this.selection)==null?void 0:l.kind)==="node"&&this.change(ot(this.doc,this.selection.id))});const o=n("input[type=file]");o.addEventListener("change",async()=>{var g;const l=(g=o.files)==null?void 0:g[0];if(o.value="",!l)return;const c=W(await l.text());if(!c)return this.showStatus(h("web_visuflow_open_error"));c.title||(c.title=l.name.replace(/\.(visuflow|json)$/i,"")),this.replace(c)});let r;this.titleInput.addEventListener("focus",()=>r=b(this.doc)),this.titleInput.addEventListener("input",()=>{this.doc={...this.doc,title:this.titleInput.value},Z(X,b(this.doc))}),this.titleInput.addEventListener("change",()=>{r!==void 0&&r!==b(this.doc)&&this.pushUndo(r),r=b(this.doc)});const a=n("[data-action=export]"),d=n(".vf-menu"),u=l=>{d.hidden=!l,a.setAttribute("aria-expanded",String(l))};a.addEventListener("click",()=>u(d.hidden)),d.addEventListener("click",l=>{var g;const c=(g=l.target.closest("[data-export]"))==null?void 0:g.dataset.export;c&&(u(!1),this.export(c))});const p=n("[data-action=settings]"),f=n(".settings-menu");p.addEventListener("click",()=>{f.hidden=!f.hidden,p.setAttribute("aria-expanded",String(!f.hidden))}),document.addEventListener("pointerdown",l=>{const c=l.target;!f.hidden&&!c.closest(".settings")&&(f.hidden=!0),!d.hidden&&!c.closest(".vf-export")&&u(!1)});for(const l of f.querySelectorAll("[data-world]"))l.addEventListener("click",()=>this.chooseWorld(l.dataset.world));for(const l of this.shell.querySelectorAll(".vf-shape"))this.setupPaletteButton(l);this.svg.addEventListener("pointerdown",l=>this.onPointerDown(l)),this.svg.addEventListener("dblclick",l=>{var g;const c=(g=l.target.closest("[data-node]"))==null?void 0:g.dataset.node;c&&this.editText(c)})}chooseWorld(t){if(t!=="flow"){if(t==="object"){ft(),location.href=`${location.pathname}?visuobject${location.hash}`;return}pt(t),location.href=`${location.pathname}?${t==="on"?"visubot":"visustruct"}${location.hash}`}}pushUndo(t){this.history.push(t),this.history.length>It&&this.history.shift(),this.future=[]}change(t,e){t!==this.doc&&(this.pushUndo(b(this.doc)),this.doc=t,e!==void 0&&(this.selection=e??void 0),this.afterChange())}replace(t){this.change(t,null),this.scroller.scrollTo(0,0)}afterChange(){Z(X,b(this.doc)),this.selection&&!this.find(this.selection)&&(this.selection=void 0),this.render()}find(t){return t.kind==="node"?this.doc.nodes.find(e=>e.id===t.id):this.doc.edges.find(e=>e.id===t.id)}undo(t){this.closeEditor(!0);const e=t?this.future:this.history,n=t?this.history:this.future,s=e.pop();s!==void 0&&(n.push(b(this.doc)),this.doc=W(s)??this.doc,this.afterChange())}deleteSelection(){const t=this.selection;t&&this.change(t.kind==="node"?Mt(this.doc,t.id):At(this.doc,t.id),null)}save(){const t=G(this.doc.title)||"PAP";D(`${t}.visuflow`,b(this.doc),"application/json"),this.showStatus(h("web_visuflow_saved",`${t}.visuflow`))}boxes(t=this.doc){return new Map(t.nodes.map(e=>[e.id,C(e,R)]))}drawInto(t,e){var f,l;const n=this.boxes(),s=h("web_learn_pap_yes"),o=h("web_learn_pap_no");t.innerHTML=``;const r=v("g",{class:"vf-edges"}),a=v("g",{class:"vf-labels","font-size":12,"font-weight":600}),d=v("g",{class:"vf-nodes"}),u=v("g",{class:"vf-ports"});let p=0;for(const c of this.doc.edges){const g=this.doc.nodes.find(M=>M.id===c.from),x=n.get(c.from),$=n.get(c.to),k=j(this.doc,g,c.port),L=$.y-$.h/2M.join(",")).join(" "),E=v("g",{"data-edge":c.id,class:`vf-edge${!e&&((f=this.selection)==null?void 0:f.kind)==="edge"&&this.selection.id===c.id?" selected":""}`});e||E.append(v("polyline",{points:S,class:"vf-edge-hit",fill:"none",stroke:"transparent","stroke-width":16})),E.append(v("polyline",{points:S,class:"vf-edge-line",fill:"none",stroke:T,"stroke-width":2,"marker-end":"url(#vf-arrow)"})),r.append(E)}for(const c of this.doc.nodes){const g=n.get(c.id),x=!e&&((l=this.selection)==null?void 0:l.kind)==="node"&&this.selection.id===c.id,$=v("g",{"data-node":c.id,class:`vf-node${x?" selected":""}`}),k=at(c.shape,g);k.setAttribute("fill",st[c.shape]),k.setAttribute("stroke",T),k.setAttribute("stroke-width","2"),$.append(k,Bt(g)),d.append($);for(const L of V(c.shape)){const O=j(this.doc,c,L),[S,E]=F(g,O),M=this.doc.edges.some(A=>A.from===c.id&&A.port===L);if(L!=="out"&&(M||!e)){const A=v("text",O==="bottom"?{x:S+7,y:E+15}:{x:S+6,y:E-7});A.setAttribute("fill",L==="ja"?"#248a3d":"#d70015"),A.textContent=L==="ja"?s:o,a.append(A)}if(e)continue;const K=v("g",{"data-port":L,"data-from":c.id,class:`vf-port${M?" used":""}`});K.append(v("circle",{cx:S,cy:E,r:16,fill:"transparent"}),v("circle",{cx:S,cy:E,r:6,class:"vf-port-dot"})),u.append(K)}}return t.append(r,d,a,u),n}render(){this.closeEditor(!0);const t=this.drawInto(this.svg,!1);let e=0,n=0;for(const d of t.values())e=Math.max(e,d.x+d.w/2),n=Math.max(n,d.y+d.h/2);const s=Math.max(this.scroller.clientWidth,e+300),o=Math.max(this.scroller.clientHeight,n+300);this.svg.setAttribute("width",String(s)),this.svg.setAttribute("height",String(o)),this.svg.setAttribute("viewBox",`0 0 ${s} ${o}`),document.activeElement!==this.titleInput&&(this.titleInput.value=this.doc.title);const r=this.selection,a=(r==null?void 0:r.kind)==="node"?this.doc.nodes.find(d=>d.id===r.id):void 0;this.shell.querySelector("[data-action=swap]").disabled=(a==null?void 0:a.shape)!=="verzweigung",this.shell.querySelector("[data-action=delete]").disabled=!r,this.shell.querySelector("[data-action=undo]").disabled=this.history.length===0,this.shell.querySelector("[data-action=redo]").disabled=this.future.length===0,this.showStatus(h(this.doc.nodes.length===0?"web_visuflow_status_empty":(r==null?void 0:r.kind)==="edge"?"web_visuflow_status_edge":r?"web_visuflow_status_node":"web_visuflow_status_idle"))}showStatus(t){this.status.textContent=t}setupPaletteButton(t){const e=t.dataset.shape;t.addEventListener("pointerdown",n=>{if(n.button!==0)return;n.preventDefault();const s={x:n.clientX,y:n.clientY};let o;const r=d=>{!o&&Math.hypot(d.clientX-s.x,d.clientY-s.y)<6||(o||(o=document.createElement("div"),o.className="vf-ghost",o.innerHTML=Q(e),document.body.append(o)),o.style.left=`${d.clientX}px`,o.style.top=`${d.clientY}px`)},a=d=>{if(window.removeEventListener("pointermove",r),window.removeEventListener("pointerup",a),window.removeEventListener("pointercancel",a),!o)return this.appendShape(e);if(o.remove(),d.type==="pointercancel")return;const u=this.scroller.getBoundingClientRect();if(d.clientXu.right||d.clientYu.bottom)return;const p=this.toCanvas(d),{doc:f,id:l}=q(this.doc,e,p.x,p.y,B(e));this.change(f,{kind:"node",id:l}),this.editText(l)};window.addEventListener("pointermove",r),window.addEventListener("pointerup",a),window.addEventListener("pointercancel",a)}),t.addEventListener("keydown",n=>{n.key!=="Enter"&&n.key!==" "||(n.preventDefault(),this.appendShape(e))})}appendShape(t){var f;const e=this.selection,n=(e==null?void 0:e.kind)==="node"?this.doc.nodes.find(l=>l.id===e.id):void 0,s=n&&P(t)?St(this.doc,n):void 0,o=C({shape:t,x:0,y:0,text:B(t)},R);let r=240,a=60;if(n&&s){const l=C(n,R);j(this.doc,n,s)==="right"?(r=l.x+l.w/2+3*m+o.w/2,a=l.y):(r=l.x,a=l.y+l.h/2+3*m+o.h/2)}else if(this.doc.nodes.length){const c=[...this.boxes().values()].reduce((g,x)=>x.y+x.h/2>g.y+g.h/2?x:g);r=n?C(n,R).x:c.x,a=c.y+c.h/2+3*m+o.h/2}let{doc:d,id:u}=q(this.doc,t,r,a,B(t));n&&s&&(d=_(d,n.id,s,u)),this.change(d,{kind:"node",id:u});const p=this.svg.querySelector(`[data-node="${u}"]`);(f=p==null?void 0:p.scrollIntoView)==null||f.call(p,{block:"nearest",inline:"nearest"}),this.editText(u)}toCanvas(t){const e=this.svg.getBoundingClientRect();return{x:t.clientX-e.left,y:t.clientY-e.top}}nodeAt(t,e){const n=this.boxes();return[...this.doc.nodes].reverse().find(s=>{const o=n.get(s.id);return s.id!==e&&Math.abs(t.x-o.x)<=o.w/2+4&&Math.abs(t.y-o.y)<=o.h/2+4})}onPointerDown(t){var r,a;if(t.button!==0)return;const e=t.target,n=e.closest("[data-port]");if(n)return this.dragArrow(t,n.dataset.from,n.dataset.port);const s=(r=e.closest("[data-node]"))==null?void 0:r.dataset.node;if(s)return this.dragNode(t,s);const o=(a=e.closest("[data-edge]"))==null?void 0:a.dataset.edge;this.closeEditor(!0),this.selection=o?{kind:"edge",id:o}:void 0,this.render()}dragNode(t,e){var f;t.preventDefault();const n=((f=this.selection)==null?void 0:f.kind)==="node"&&this.selection.id===e;this.closeEditor(!0),this.selection={kind:"node",id:e},this.render();const s=this.doc.nodes.find(l=>l.id===e),o=this.toCanvas(t),r=b(this.doc),a={x:s.x,y:s.y};let d=!1;const u=l=>{const c=this.toCanvas(l);!d&&Math.hypot(c.x-o.x,c.y-o.y)<5||(d=!0,this.doc=J(this.doc,e,a.x+c.x-o.x,a.y+c.y-o.y),this.render())},p=()=>{window.removeEventListener("pointermove",u),window.removeEventListener("pointerup",p),window.removeEventListener("pointercancel",p),d?(b(this.doc)!==r&&this.pushUndo(r),this.afterChange()):n&&this.editText(e)};window.addEventListener("pointermove",u),window.addEventListener("pointerup",p),window.addEventListener("pointercancel",p)}dragArrow(t,e,n){t.preventDefault(),this.closeEditor(!0);const s=this.doc.nodes.find(p=>p.id===e),[o,r]=F(C(s,R),j(this.doc,s,n));this.dragLine=v("line",{x1:o,y1:r,x2:o,y2:r,class:"vf-drag-line","marker-end":"url(#vf-arrow)"}),this.svg.append(this.dragLine),this.showStatus(h("web_visuflow_status_connect"));let a=null;const d=p=>{const f=this.toCanvas(p);this.dragLine.setAttribute("x2",String(f.x)),this.dragLine.setAttribute("y2",String(f.y));const l=this.nodeAt(f,e);a==null||a.classList.remove("drop-target"),a=l&&P(l.shape)?this.svg.querySelector(`[data-node="${l.id}"]`):null,a==null||a.classList.add("drop-target")},u=p=>{var l;window.removeEventListener("pointermove",d),window.removeEventListener("pointerup",u),window.removeEventListener("pointercancel",u),(l=this.dragLine)==null||l.remove(),this.dragLine=void 0;const f=p.type==="pointercancel"?void 0:this.nodeAt(this.toCanvas(p),e);f&&P(f.shape)?this.change(_(this.doc,e,n,f.id),{kind:"node",id:f.id}):this.render()};window.addEventListener("pointermove",d),window.addEventListener("pointerup",u),window.addEventListener("pointercancel",u)}editText(t){this.closeEditor(!0);const e=this.doc.nodes.find(d=>d.id===t);if(!e)return;const n=C(e,R),s=document.createElement("textarea");s.className="vf-editor",s.value=e.text,s.setAttribute("aria-label",h("web_visuflow_edit_label"));const o=e.shape==="verzweigung"?.62:.92,r=Math.max(110,n.w*o);s.style.left=`${this.svg.getBoundingClientRect().left-this.scroller.getBoundingClientRect().left+this.scroller.scrollLeft+n.x-r/2}px`,s.style.top=`${this.svg.getBoundingClientRect().top-this.scroller.getBoundingClientRect().top+this.scroller.scrollTop+n.y-Math.max(28,n.lines.length*z+10)/2}px`,s.style.width=`${r}px`,s.style.height=`${Math.max(28,n.lines.length*z+10)}px`,s.dataset.node=t,this.scroller.append(s),this.editor=s,s.focus(),s.select();const a=()=>{s.style.height="auto",s.style.height=`${s.scrollHeight}px`};s.addEventListener("input",a),s.addEventListener("keydown",d=>{d.stopPropagation(),d.key==="Enter"&&!d.shiftKey?(d.preventDefault(),this.closeEditor(!0)):d.key==="Escape"&&(d.preventDefault(),this.closeEditor(!1))}),s.addEventListener("blur",()=>this.closeEditor(!0))}closeEditor(t){const e=this.editor;if(!e)return;this.editor=void 0,e.remove();const n=e.dataset.node,s=this.doc.nodes.find(r=>r.id===n),o=e.value.trim();t&&s&&o!==s.text&&this.change(kt(this.doc,n,o))}onKey(t){var a,d;const e=(d=(a=t.target).closest)==null?void 0:d.call(a,"input, textarea, [contenteditable]"),n=t.ctrlKey||t.metaKey;if(n&&t.key.toLowerCase()==="s")return t.preventDefault(),this.save();if(e)return;if(n&&t.key.toLowerCase()==="z")return t.preventDefault(),this.undo(t.shiftKey);if(n&&t.key.toLowerCase()==="y")return t.preventDefault(),this.undo(!0);const s=this.selection;if(!s)return;if(t.key==="Delete"||t.key==="Backspace")return t.preventDefault(),this.deleteSelection();if(s.kind!=="node")return;if(t.key==="Enter"||t.key==="F2")return t.preventDefault(),this.editText(s.id);const o={ArrowLeft:[-1,0],ArrowRight:[1,0],ArrowUp:[0,-1],ArrowDown:[0,1]}[t.key];if(!o)return;t.preventDefault();const r=this.doc.nodes.find(u=>u.id===s.id);this.change(J(this.doc,s.id,r.x+o[0]*m,r.y+o[1]*m))}exportImage(){if(this.doc.nodes.length===0)return;const t=document.createElementNS(H,"svg"),e=this.drawInto(t,!0);let n=1/0,s=1/0,o=-1/0,r=-1/0;for(const c of e.values())n=Math.min(n,c.x-c.w/2),s=Math.min(s,c.y-c.h/2),o=Math.max(o,c.x+c.w/2),r=Math.max(r,c.y+c.h/2);for(const c of t.querySelectorAll("polyline"))for(const g of(c.getAttribute("points")??"").split(" ")){const[x,$]=g.split(",").map(Number);n=Math.min(n,x),o=Math.max(o,x+4),s=Math.min(s,$),r=Math.max(r,$)}o+=30;const a=20,d=this.doc.title.trim(),u=d?32:0,p=Math.ceil(o-n+2*a),f=Math.ceil(r-s+2*a+u);t.setAttribute("xmlns",H),t.setAttribute("width",String(p)),t.setAttribute("height",String(f)),t.setAttribute("viewBox",`${n-a} ${s-a-u} ${p} ${f}`),t.setAttribute("font-family",rt.replace(/^\d+px /,""));const l=v("rect",{x:n-a,y:s-a-u,width:p,height:f,fill:"#ffffff"});if(t.insertBefore(l,t.firstChild.nextSibling),d){const c=v("text",{x:n-a+12,y:s-a-u+26,"font-size":16,"font-weight":700,fill:T});c.textContent=d,t.append(c)}return{svg:t,width:p,height:f}}async export(t){const e=this.exportImage();if(!e)return this.showStatus(h("web_visuflow_status_empty"));const n=G(this.doc.title)||"PAP";if(t==="svg")return D(`${n}.svg`,gt(e),"image/svg+xml");const s=await wt(e,t==="png"?2:3);if(t==="png")return D(`${n}.png`,await vt(s),"image/png");const o=await mt(xt(s),s.width,s.height,e,this.doc.title.trim()||"PAP");D(`${n}.pdf`,o,"application/pdf")}}export{Pt as FlowApp}; diff --git a/website/app/assets/fr-D5Wy8Mcd.js b/website/app/assets/fr-D5Wy8Mcd.js deleted file mode 100644 index e1b383dd..00000000 --- a/website/app/assets/fr-D5Wy8Mcd.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Textes propres à la version web. Tout le reste provient des Localizable.strings de l’app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Organigramme VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Suppr"; -"web_key_shift" = "Maj"; -"web_export" = "Exporter…"; -"web_export_title" = "Enregistrer comme image ou PDF, ou imprimer"; -"web_export_png" = "Image (PNG)"; -"web_export_svg" = "Graphique vectoriel (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Fichier VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Échec de l’exportation : %@"; -"web_print_hint" = "Utilisez « Exporter… → PDF » pour enregistrer le diagramme, puis imprimez-le."; -"web_delete" = "Supprimer"; -"web_language_title" = "Langue de l’interface"; -"web_simulation_start" = "Lancer la simulation"; -"web_simulation_end" = "Terminer la simulation"; -"web_simulation_start_title" = "Exécuter l’organigramme étape par étape"; -"web_simulation_end_title" = "Terminer la simulation et reprendre l’édition"; -"web_palette_hint" = "Glisser dans l’organigramme"; -"web_tabs_label" = "Diagrammes"; -"web_status_modified" = "modifié"; -"web_inspector_title" = "Propriétés"; -"web_inspector_resize" = "Modifier la hauteur de l’inspecteur d’objets : faire glisser la ligne vers le haut ou le bas (double-clic : par défaut)"; -"web_inspector_help_drop" = "Glissez des éléments de la palette sur une tuile : en haut = avant, en bas = après, sur « ø » = remplacer. Les éléments existants se déplacent à la souris."; -"web_inspector_help_inline" = "Double-cliquez ou touchez deux fois un élément pour modifier son texte directement dans le diagramme (Entrée valide, Échap annule). Idem pour les noms de cas."; -"web_inspector_help_files" = "Vous pouvez aussi déposer des fichiers dans la fenêtre."; -"web_case_name" = "Nom du cas"; -"web_case_name_placeholder" = "p. ex. 1, ok, default"; -"web_copied" = "Élément copié."; -"web_discard_message_fmt" = "Les modifications de « %@ » seront perdues."; -"web_err_unreadable_fmt" = "« %@ » n’est pas un fichier VisuStruct lisible (%@)."; -"web_saved_fmt" = "« %@ » enregistré."; -"web_saved_txt_fmt" = "Enregistré sous « %@ ». Supprimez l’extension .txt pour l’utiliser."; -"web_drop_impossible" = "Impossible de déposer ici"; -"web_sim_delay" = "Pause par étape"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Langage"; -"web_codegen_comments" = "Texte du diagramme en commentaires"; -"web_codegen_empty_else" = "Générer une branche else vide dans le code"; -"web_copied_check" = "Copié ✓"; -"web_copy_manual" = "Sélectionné, appuyez sur Ctrl+C pour copier"; -"web_run_stopped_loop" = "Exécution arrêtée : boucle infinie possible."; -"web_run_cancelled" = "Exécution annulée."; -"web_run_error" = "Erreur lors de l’exécution."; -"web_run_dropped_fmt" = "… %d lignes précédentes masquées"; -"web_sim_call_arguments_fmt" = "L’appel ne correspond pas à l’en-tête de %@. Vérifie les valeurs entre parenthèses."; -"web_sim_missing_return_fmt" = "%@ s’est terminé sans renvoyer de valeur avec return."; -"web_sim_file_not_found_fmt" = "Il n’existe aucun fichier nommé %@."; -"web_sim_end_of_file_fmt" = "Le fichier %@ n’a plus de ligne."; -"web_sim_null_value_fmt" = "%@ vaut null."; -"web_sim_unknown_variable_case_fmt" = "Variable inconnue : %1$@. Java distingue majuscules et minuscules : vouliez-vous dire %2$@ ?"; -"web_sim_written_file_fmt" = "%@ (enregistré)"; -"web_site_home" = "Accueil"; -"web_site_imprint" = "Mentions légales"; -"web_site_privacy" = "Confidentialité"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmation"; -"web_live_hide" = "Masquer la programmation"; -"web_scale_canvas" = "Taille du texte : structogramme"; -"web_scale_code" = "Taille du texte : programmation"; -"web_live_indent" = "Indenter"; -"web_live_indent_title" = "Indenter le code : quatre espaces par niveau (lignes sélectionnées, sinon tout)"; -"web_live_show" = "Afficher la programmation"; -"web_live_resize" = "Modifier la largeur de la zone de code (double-clic : réinitialiser)"; -"web_live_hint" = "Le code évolue avec ton diagramme. Gris = espace réservé : écris dans l’inspecteur et ton texte apparaît ici. Clique sur une ligne pour sélectionner son élément."; -"web_live_ph_selector" = "variable"; -"web_live_ph_code" = "Ton code"; -"web_live_ph_end" = "fin"; -"web_live_language" = "Langage de programmation"; -"web_live_java_class" = "Comme classe"; -"web_live_java_class_hint" = "Nom de la classe = titre du diagramme"; -"web_live_java_main" = "Avec méthode main"; -"web_settings_display" = "Affichage"; -"web_live_swing" = "Vrai Java (avec Swing)"; -"web_live_swing_hint" = "Du vrai Java avec fenêtres (CheerpJ). Télécharge environ 40 Mo une seule fois, au premier lancement."; -"web_live_swing_windows" = "Fenêtres Java"; -"web_live_swing_hide" = "Fermer les fenêtres Java"; -"web_live_swing_loading" = "Chargement de Java (environ 40 Mo la première fois) …"; -"web_live_swing_compiling" = "Compilation …"; -"web_live_swing_running" = "Le programme s’exécute."; -"web_live_swing_compile_failed" = "Erreur de compilation, voir ci-dessus."; -"web_live_swing_failed" = "Impossible de charger Java. Vérifie la connexion Internet."; -"web_live_hint_edit" = "Tape ici du code Java et le diagramme se construit tout seul. Ou travaille dans le diagramme et le code suit. Gris = espace réservé, il suffit d’écrire par-dessus."; -"web_live_edit_label" = "Code Java"; -"web_parse_line_fmt" = "Ligne %d : %@"; -"web_parse_missing" = "Il manque encore ici : %@"; -"web_parse_extra" = "En trop : %@"; -"web_parse_unclosed_string" = "Le %@ de fermeture manque ici."; -"web_parse_else_without_if" = "Ce « else » n’a pas de « if »."; -"web_parse_case_outside_switch" = "« case » et « default » vont dans un « switch »."; -"web_parse_missing_do_while" = "Après « do { … } », il manque « while (condition); »."; -"web_parse_more_methods" = "Pour l’instant, une seule méthode compte ici. Crée les autres sous-programmes dans des onglets séparés."; -"web_parse_missing_import" = "Il manque %@ tout en haut. Sans cette ligne, Java ne connaît pas Scanner."; -"web_parse_missing_scanner" = "Il manque avant %@ Sans cette ligne, le Scanner n’existe pas."; -"web_parse_unknown_type" = "Java ne connaît pas ce type de données. Vouliez-vous dire %@ ?"; -"web_quickfix_insert_fmt" = "Suggestion : insérer %@"; -"web_quickfix_replace_fmt" = "Suggestion : %1$@ au lieu de %2$@"; -"web_parse_wrong_case" = "Java distingue majuscules et minuscules. La bonne écriture est %@."; -"web_editor_title" = "Diagramme"; -"web_editor_show" = "Afficher le diagramme"; -"web_editor_hide" = "Masquer le diagramme"; -"web_keywords_title" = "Mots-clés du code"; -"web_keywords_hint" = "if, while, for au lieu des noms des éléments"; -"web_palette_compact" = "Éléments toujours compacts"; -"web_palette_compact_hint" = "Symbole avec le nom en petit dessous, sinon seulement si la colonne est étroite"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Logiciels utilisés"; -"web_about_software_text" = "L’éditeur de code utilise CodeMirror et Lezer (licence MIT). Avec la prise en charge de Swing, VisuStruct charge CheerpJ de Leaning Technologies (licence Community) et javac d’OpenJDK."; -"web_about_licenses" = "Textes des licences"; -"web_live_run" = "Exécuter"; -"web_live_run_title" = "Exécuter le programme : les sorties et les entrées apparaissent en dessous"; -"web_live_run_finished" = "Programme terminé."; -"web_live_run_stopped" = "Arrêté."; -"web_console" = "Console"; -"web_console_hide" = "Masquer la console"; -"web_console_resize" = "Redimensionner la console"; -"web_diagram_tools" = "Outils du diagramme"; -"web_files_title" = "Fichiers"; -"web_files_show" = "Afficher les fichiers"; -"web_files_hide" = "Masquer les fichiers"; -"web_files_resize" = "Redimensionner les fichiers"; -"web_files_new_folder" = "Nouveau dossier"; -"web_files_switch" = "Choisir un autre dossier"; -"web_files_delete" = "Supprimer"; -"web_files_in_browser" = "Dans le stockage de ce navigateur"; -"web_files_save_hint_fmt" = "L’enregistrement place le programme dans %@."; -"web_files_empty" = "Rien d’enregistré pour l’instant. À l’enregistrement, VisuStruct crée ici un dossier à la date du jour."; -"web_files_reconnect_hint" = "Après un rechargement, le navigateur redemande si VisuStruct peut utiliser ton dossier."; -"web_files_reconnect_fmt" = "Ouvrir le dossier « %@ »"; -"web_files_connect_hint" = "Choisis un dossier comme espace de travail. VisuStruct y crée un dossier pour chaque jour et enregistre le diagramme et le code."; -"web_files_connect" = "Choisir un dossier"; -"web_files_browser_hint" = "Ce navigateur ne peut pas ouvrir de dossiers de l’appareil (par exemple sur iPad). Tes fichiers restent alors dans le stockage de ce navigateur."; -"web_files_browser" = "Enregistrer dans le navigateur"; -"web_files_delete_confirm_fmt" = "Supprimer vraiment « %@ » ?"; -"web_files_folder_prompt" = "Nom du nouveau dossier :"; -"web_files_default_name" = "Programme"; -"web_files_saved_fmt" = "Enregistré : %@"; -"web_files_create" = "Créer"; -"web_learn_ludwig_tip" = "Le conseil de Ludwig"; -"web_game_title" = "Jeu : Vrai ou faux ?"; -"web_game_intro" = "Ludwig te montre des valeurs et une condition. Est-elle vraie (true) ou fausse (false) ? Dix manches, de plus en plus difficiles."; -"web_game_start" = "Commencer le jeu"; -"web_game_round_fmt" = "Manche %@ sur %@"; -"web_game_score_fmt" = "Points : %@"; -"web_game_true" = "vrai (true)"; -"web_game_false" = "faux (false)"; -"web_game_right" = "Correct !"; -"web_game_wrong" = "Pas tout à fait. Voici le calcul :"; -"web_game_next" = "Suivant"; -"web_game_finish" = "Résultat"; -"web_game_done_fmt" = "%@ sur %@ correctes."; -"web_game_perfect" = "Tout juste – Ludwig est très impressionné !"; -"web_game_good" = "Bravo ! Regarde encore tes erreurs et réessaie."; -"web_game_practice" = "C’est en forgeant qu’on devient forgeron. Relis la table de vérité et rejoue une partie."; -"web_game_again" = "Rejouer"; -"web_game_wrong_short" = "Dommage, c'est faux."; -"web_game_practice_short" = "C'est en forgeant qu'on devient forgeron. Relis les explications et rejoue une partie."; -"web_game_clicks" = "Clics :"; -"web_game_nobody" = "Personne n'écoute – rien ne se passe."; -"web_game_event_fmt" = "Clic sur « %@ » : l'écouteur l'entend et exécute son code."; -"web_game_try" = "Essaie maintenant toi-même les boutons de la fenêtre."; -"web_game_time_fmt" = "Encore %@ s"; -"web_game_defused" = "✂️ Désamorcée ! Tu as trouvé l’erreur."; -"web_game_boom_time" = "💥 Boum ! Le temps est écoulé."; -"web_game_boom_wrong" = "💥 Boum ! Ce n’était pas la bonne ligne."; -"web_game_boom_part" = "💥 Boum ! Bonne ligne, mais l’erreur est dans une autre partie."; -"web_game_boom_wrong_part" = "💥 Boum ! L’erreur est dans une autre ligne."; -"web_game_bomb_parts_hint" = "Touche exactement la partie fausse."; -"web_game_fix" = "La ligne correcte :"; -"web_game_order_hint" = "Touche les étapes dans le bon ordre."; -"web_debug_step" = "Pas à pas"; -"web_debug_step_title" = "Pas à pas : exécuter une instruction et s’arrêter à nouveau"; -"web_debug_continue" = "Continuer jusqu’au prochain point d’arrêt"; -"web_debug_paused_fmt" = "Arrêté avant la ligne %d."; -"web_debug_gutter_title" = "Clique sur un numéro de ligne pour poser ou retirer un point d’arrêt"; -"web_swap_to_code" = "Programmation en grand"; -"web_swap_to_editor" = "Structogramme en grand"; -"web_editor_resize" = "Modifier la largeur du structogramme"; -"web_palette_resize" = "Modifier la largeur des éléments"; -"web_palette_hide" = "Masquer les éléments"; -"web_palette_show" = "Afficher les éléments"; -"web_code_scopes" = "Blocs de code en couleur"; -"web_code_scopes_hint" = "Classe, méthode, boucle, condition dans leurs propres cadres"; -"web_pseudocode" = "Pseudo-code"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Renommer la variable …"; -"web_rename_title" = "Renommer la variable"; -"web_rename_old" = "Nom"; -"web_rename_new" = "Nouveau nom"; -"web_rename_hint" = "Modifie chaque endroit avec exactement cette écriture (nombre n’est pas Nombre), dans le code et dans le structogramme. Le texte entre \\"…\\" et les commentaires restent."; -"web_rename_count_fmt" = "Apparaît %d fois."; -"web_rename_exists_fmt" = "« %@ » existe déjà : ensuite les deux seraient une seule variable."; -"web_rename_invalid" = "Un nom commence par une lettre et ne contient que des lettres, des chiffres et _."; -"web_rename_keyword_fmt" = "« %@ » est un mot-clé Java."; -"web_rename_apply" = "Renommer"; -"web_rename_cancel" = "Annuler"; -`;export{e as default}; diff --git a/website/app/assets/grace-hopper-DltOT1I5.jpg b/website/app/assets/grace-hopper-DltOT1I5.jpg deleted file mode 100644 index d767c68d..00000000 Binary files a/website/app/assets/grace-hopper-DltOT1I5.jpg and /dev/null differ diff --git a/website/app/assets/he-CYY5d1yw.js b/website/app/assets/he-CYY5d1yw.js deleted file mode 100644 index 20966685..00000000 --- a/website/app/assets/he-CYY5d1yw.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* טקסטים שקיימים רק בגרסת הרשת. כל השאר מגיעים מקובץ Localizable.strings של האפליקציה. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "תרשים VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "ייצוא…"; -"web_export_title" = "שמירה כתמונה או כ‑PDF, או הדפסה"; -"web_export_png" = "תמונה (PNG)"; -"web_export_svg" = "גרפיקה וקטורית (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "קובץ VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "הייצוא נכשל: %@"; -"web_print_hint" = "השתמשו ב„ייצוא… → PDF” כדי לשמור את התרשים ולהדפיס אותו משם."; -"web_delete" = "מחיקה"; -"web_language_title" = "שפת הממשק"; -"web_simulation_start" = "התחלת סימולציה"; -"web_simulation_end" = "סיום סימולציה"; -"web_simulation_start_title" = "הרצת התרשים צעד אחר צעד"; -"web_simulation_end_title" = "סיום הסימולציה והמשך עריכה"; -"web_palette_hint" = "גררו לתוך התרשים"; -"web_tabs_label" = "תרשימים"; -"web_status_modified" = "שונה"; -"web_inspector_title" = "מאפיינים"; -"web_inspector_resize" = "שינוי גובה מפקח האובייקטים: גרור את הקו למעלה או למטה (לחיצה כפולה: ברירת מחדל)"; -"web_inspector_help_drop" = "גררו רכיבים מהלוח אל אריח: למעלה = לפני, למטה = אחרי, על „ø” = החלפה. ניתן להזיז רכיבים קיימים בעזרת העכבר."; -"web_inspector_help_inline" = "לחיצה כפולה או הקשה כפולה על רכיב לעריכת הטקסט ישירות בתרשים (Enter להחלה, Esc לביטול). כך גם שמות המקרים."; -"web_inspector_help_files" = "אפשר גם לגרור קבצים לתוך החלון."; -"web_case_name" = "שם המקרה"; -"web_case_name_placeholder" = "למשל 1, ok, default"; -"web_copied" = "הרכיב הועתק."; -"web_discard_message_fmt" = "השינויים ב„%@” יאבדו."; -"web_err_unreadable_fmt" = "„%@” אינו קובץ VisuStruct קריא (%@)."; -"web_saved_fmt" = "„%@” נשמר."; -"web_saved_txt_fmt" = "נשמר בשם „%@”. הסירו את הסיומת .txt כדי להשתמש בו."; -"web_drop_impossible" = "לא ניתן לשחרר כאן"; -"web_sim_delay" = "השהיה לכל צעד"; -"web_sim_delay_value_fmt" = "%@ שנ׳"; -"web_codegen_language" = "שפה"; -"web_codegen_comments" = "טקסט התרשים כהערות"; -"web_codegen_empty_else" = "יצירת ענף else ריק בקוד"; -"web_copied_check" = "הועתק ✓"; -"web_copy_manual" = "נבחר, הקישו Ctrl+C להעתקה"; -"web_run_stopped_loop" = "ההרצה נעצרה: ייתכן שיש לולאה אינסופית."; -"web_run_cancelled" = "ההרצה בוטלה."; -"web_run_error" = "שגיאה בזמן ההרצה."; -"web_run_dropped_fmt" = "… %d שורות קודמות מוסתרות"; -"web_sim_call_arguments_fmt" = "הקריאה אינה תואמת את הכותרת של %@. בדוק את הערכים בסוגריים."; -"web_sim_missing_return_fmt" = "%@ הסתיים בלי להחזיר ערך עם return."; -"web_sim_file_not_found_fmt" = "אין קובץ בשם %@."; -"web_sim_end_of_file_fmt" = "בקובץ %@ אין עוד שורות."; -"web_sim_null_value_fmt" = "%@ הוא null."; -"web_sim_unknown_variable_case_fmt" = "משתנה לא מוכר: %1$@. Java מבחינה בין אותיות גדולות לקטנות: האם התכוונת ל־%2$@?"; -"web_sim_written_file_fmt" = "%@ (נשמר)"; -"web_site_home" = "דף הבית"; -"web_site_imprint" = "פרטי המפרסם"; -"web_site_privacy" = "פרטיות"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "תכנות"; -"web_live_hide" = "הסתרת תכנות"; -"web_scale_canvas" = "גודל טקסט: תרשים"; -"web_scale_code" = "גודל טקסט: תכנות"; -"web_live_indent" = "הזחה"; -"web_live_indent_title" = "הזחת הקוד: ארבעה רווחים לכל רמה (השורות המסומנות, אחרת הכול)"; -"web_live_show" = "הצגת תכנות"; -"web_live_resize" = "שינוי רוחב אזור הקוד (לחיצה כפולה: איפוס)"; -"web_live_hint" = "הקוד גדל יחד עם התרשים. אפור = ממלא מקום: כתבו במפקח והטקסט יופיע כאן. לחצו על שורה כדי לבחור את הרכיב."; -"web_live_ph_selector" = "משתנה"; -"web_live_ph_code" = "הקוד שלך"; -"web_live_ph_end" = "סוף"; -"web_live_language" = "שפת תכנות"; -"web_live_java_class" = "כמחלקה"; -"web_live_java_class_hint" = "שם המחלקה = כותרת התרשים"; -"web_live_java_main" = "עם מתודת main"; -"web_settings_display" = "תצוגה"; -"web_live_swing" = "Java אמיתית (עם Swing)"; -"web_live_swing_hint" = "ג׳אווה אמיתית עם חלונות (CheerpJ). מורידה פעם אחת כ-40 MB בהרצה הראשונה."; -"web_live_swing_windows" = "חלונות ג׳אווה"; -"web_live_swing_hide" = "סגירת חלונות ג׳אווה"; -"web_live_swing_loading" = "טוען ג׳אווה (בפעם הראשונה כ-40 MB) …"; -"web_live_swing_compiling" = "מהדר …"; -"web_live_swing_running" = "התוכנית רצה."; -"web_live_swing_compile_failed" = "שגיאת הידור, ראה למעלה."; -"web_live_swing_failed" = "לא ניתן לטעון את ג׳אווה. בדוק את החיבור לאינטרנט."; -"web_live_hint_edit" = "הקלד כאן קוד Java והתרשים נבנה בעצמו. או עבוד בתרשים והקוד יגדל איתו. אפור = ממלא מקום, פשוט כתוב מעליו."; -"web_live_edit_label" = "קוד Java"; -"web_parse_line_fmt" = "שורה %d: %@"; -"web_parse_missing" = "עדיין חסר כאן: %@"; -"web_parse_extra" = "מיותר: %@"; -"web_parse_unclosed_string" = "חסר כאן ה־%@ הסוגר."; -"web_parse_else_without_if" = "ל־“else” הזה אין “if”."; -"web_parse_case_outside_switch" = "“case” ו־“default” שייכים בתוך “switch”."; -"web_parse_missing_do_while" = "אחרי “do { … }” עדיין חסר “while (תנאי);”."; -"web_parse_more_methods" = "כרגע נחשבת כאן רק מתודה אחת. צור תוכניות משנה נוספות בכרטיסיות נפרדות."; -"web_parse_missing_import" = "חסר בראש הקוד %@ בלי השורה הזו Java לא מכירה את Scanner."; -"web_parse_missing_scanner" = "חסר קודם %@ בלי השורה הזו אין Scanner."; -"web_parse_unknown_type" = "Java לא מכירה את סוג הנתונים הזה. האם התכוונת ל-%@?"; -"web_quickfix_insert_fmt" = "הצעה: להוסיף %@"; -"web_quickfix_replace_fmt" = "הצעה: %1$@ במקום %2$@"; -"web_parse_wrong_case" = "Java מבחינה בין אותיות גדולות לקטנות. הנכון הוא %@."; -"web_editor_title" = "תרשים"; -"web_editor_show" = "הצגת התרשים"; -"web_editor_hide" = "הסתרת התרשים"; -"web_keywords_title" = "מילות מפתח של קוד"; -"web_keywords_hint" = "if, while, for במקום שמות הרכיבים"; -"web_palette_compact" = "רכיבים תמיד בתצוגה מצומצמת"; -"web_palette_compact_hint" = "סמל עם שם קטן מתחתיו, אחרת רק כשהעמודה צרה"; -"web_about_version_fmt" = "גרסה %@"; -"web_about_software" = "תוכנה בשימוש"; -"web_about_software_text" = "עורך הקוד משתמש ב-CodeMirror וב-Lezer (רישיון MIT). עם תמיכה ב-Swing, VisuStruct טוען את CheerpJ של Leaning Technologies (רישיון Community) ואת javac מ-OpenJDK."; -"web_about_licenses" = "נוסחי הרישיונות"; -"web_live_run" = "הרצה"; -"web_live_run_title" = "הרצת התוכנית: הפלט והקלט מופיעים למטה"; -"web_live_run_finished" = "התוכנית הסתיימה."; -"web_live_run_stopped" = "נעצר."; -"web_console" = "מסוף"; -"web_console_hide" = "הסתרת המסוף"; -"web_console_resize" = "שינוי גובה המסוף"; -"web_diagram_tools" = "כלי התרשים"; -"web_files_title" = "קבצים"; -"web_files_show" = "הצגת קבצים"; -"web_files_hide" = "הסתרת קבצים"; -"web_files_resize" = "שינוי רוחב הקבצים"; -"web_files_new_folder" = "תיקייה חדשה"; -"web_files_switch" = "בחירת תיקייה אחרת"; -"web_files_delete" = "מחיקה"; -"web_files_in_browser" = "באחסון של דפדפן זה"; -"web_files_save_hint_fmt" = "השמירה מכניסה את התוכנית אל %@."; -"web_files_empty" = "עדיין לא נשמר דבר. בשמירה VisuStruct יוצר כאן תיקייה עם התאריך של היום."; -"web_files_reconnect_hint" = "אחרי טעינה מחדש הדפדפן שואל שוב אם VisuStruct רשאי להשתמש בתיקייה."; -"web_files_reconnect_fmt" = "פתיחת התיקייה „%@“"; -"web_files_connect_hint" = "בחרו תיקייה כסביבת עבודה. VisuStruct יוצר בה תיקייה לכל יום ושומר את התרשים ואת הקוד."; -"web_files_connect" = "בחירת תיקייה"; -"web_files_browser_hint" = "דפדפן זה אינו יכול לפתוח תיקיות במכשיר (למשל ב-iPad). הקבצים נשארים באחסון של הדפדפן."; -"web_files_browser" = "שמירה בדפדפן"; -"web_files_delete_confirm_fmt" = "למחוק את „%@“?"; -"web_files_folder_prompt" = "שם התיקייה החדשה:"; -"web_files_default_name" = "תוכנית"; -"web_files_saved_fmt" = "נשמר: %@"; -"web_files_create" = "יצירה"; -"web_learn_ludwig_tip" = "הטיפ של לודוויג"; -"web_game_title" = "משחק: אמת או שקר?"; -"web_game_intro" = "לודוויג מראה ערכים ותנאי. האם התנאי אמת (true) או שקר (false)? עשרה סבבים, כל פעם קשה יותר."; -"web_game_start" = "התחלת המשחק"; -"web_game_round_fmt" = "סבב %@ מתוך %@"; -"web_game_score_fmt" = "נקודות: %@"; -"web_game_true" = "אמת (true)"; -"web_game_false" = "שקר (false)"; -"web_game_right" = "נכון!"; -"web_game_wrong" = "לא בדיוק. כך מחשבים:"; -"web_game_next" = "הבא"; -"web_game_finish" = "תוצאה"; -"web_game_done_fmt" = "%@ מתוך %@ נכונות."; -"web_game_perfect" = "הכול נכון – לודוויג מתרשם מאוד!"; -"web_game_good" = "מצוין! עברו שוב על הטעויות ונסו שוב."; -"web_game_practice" = "תרגול עושה מושלם. קראו שוב את טבלת האמת ושחקו עוד סבב."; -"web_game_again" = "לשחק שוב"; -"web_game_wrong_short" = "לצערי לא נכון."; -"web_game_practice_short" = "תרגול מביא לשלמות. קרא שוב את ההסברים ושחק סיבוב נוסף."; -"web_game_clicks" = "לחיצות:"; -"web_game_nobody" = "אף אחד לא מאזין – שום דבר לא קורה."; -"web_game_event_fmt" = "לחיצה על „%@”: המאזין שומע ומריץ את הקוד שלו."; -"web_game_try" = "עכשיו נסה בעצמך את הכפתורים בחלון."; -"web_game_time_fmt" = "נותרו %@ שנ׳"; -"web_game_defused" = "✂️ נוטרלה! מצאת את הטעות."; -"web_game_boom_time" = "💥 בום! הזמן נגמר."; -"web_game_boom_wrong" = "💥 בום! זו הייתה השורה הלא נכונה."; -"web_game_boom_part" = "💥 בום! השורה נכונה, אבל הטעות בחלק אחר שלה."; -"web_game_boom_wrong_part" = "💥 בום! הטעות בשורה אחרת."; -"web_game_bomb_parts_hint" = "הקש בדיוק על החלק השגוי."; -"web_game_fix" = "השורה הנכונה:"; -"web_game_order_hint" = "הקש על השלבים בסדר הנכון."; -"web_debug_step" = "צעד"; -"web_debug_step_title" = "צעד: להריץ הוראה אחת ולעצור שוב"; -"web_debug_continue" = "להמשיך עד נקודת העצירה הבאה"; -"web_debug_paused_fmt" = "נעצר לפני שורה %d."; -"web_debug_gutter_title" = "לחיצה על מספר שורה מוסיפה או מסירה נקודת עצירה"; -"web_swap_to_code" = "תכנות גדול"; -"web_swap_to_editor" = "תרשים גדול"; -"web_editor_resize" = "שינוי רוחב התרשים"; -"web_palette_resize" = "שינוי רוחב הרכיבים"; -"web_palette_hide" = "הסתרת הרכיבים"; -"web_palette_show" = "הצגת הרכיבים"; -"web_code_scopes" = "בלוקי קוד צבעוניים"; -"web_code_scopes_hint" = "מחלקה, מתודה, לולאה, הסתעפות בתיבות משלהן"; -"web_pseudocode" = "פסאודו-קוד"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "שינוי שם המשתנה …"; -"web_rename_title" = "שינוי שם המשתנה"; -"web_rename_old" = "שם"; -"web_rename_new" = "שם חדש"; -"web_rename_hint" = "משנה כל מקום עם בדיוק הכתיב הזה (number אינו Number), בקוד ובתרשים המבנה. טקסט בתוך \\"…\\" והערות נשארים."; -"web_rename_count_fmt" = "מופיע %d פעמים."; -"web_rename_exists_fmt" = "„%@” כבר קיים: אחר כך שניהם יהיו משתנה אחד."; -"web_rename_invalid" = "שם מתחיל באות ומכיל רק אותיות, ספרות ו־_."; -"web_rename_keyword_fmt" = "„%@” היא מילת מפתח של Java."; -"web_rename_apply" = "שינוי שם"; -"web_rename_cancel" = "ביטול"; -`;export{e as default}; diff --git a/website/app/assets/hedy-lamarr-MFZMAlPQ.jpg b/website/app/assets/hedy-lamarr-MFZMAlPQ.jpg deleted file mode 100644 index e304bd1e..00000000 Binary files a/website/app/assets/hedy-lamarr-MFZMAlPQ.jpg and /dev/null differ diff --git a/website/app/assets/ike-nassi-D2w47gvX.jpg b/website/app/assets/ike-nassi-D2w47gvX.jpg deleted file mode 100644 index a0a78f37..00000000 Binary files a/website/app/assets/ike-nassi-D2w47gvX.jpg and /dev/null differ diff --git a/website/app/assets/index-BqSgHxq8.js b/website/app/assets/index-BqSgHxq8.js deleted file mode 100644 index 89271f35..00000000 --- a/website/app/assets/index-BqSgHxq8.js +++ /dev/null @@ -1,83699 +0,0 @@ -var E3=Object.defineProperty;var N3=(i,e,t)=>e in i?E3(i,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):i[e]=t;var y=(i,e,t)=>N3(i,typeof e!="symbol"?e+"":e,t);(function(){const e=document.createElement("link").relList;if(e&&e.supports&&e.supports("modulepreload"))return;for(const a of document.querySelectorAll('link[rel="modulepreload"]'))n(a);new MutationObserver(a=>{for(const s of a)if(s.type==="childList")for(const r of s.addedNodes)r.tagName==="LINK"&&r.rel==="modulepreload"&&n(r)}).observe(document,{childList:!0,subtree:!0});function t(a){const s={};return a.integrity&&(s.integrity=a.integrity),a.referrerPolicy&&(s.referrerPolicy=a.referrerPolicy),a.crossOrigin==="use-credentials"?s.credentials="include":a.crossOrigin==="anonymous"?s.credentials="omit":s.credentials="same-origin",s}function n(a){if(a.ep)return;a.ep=!0;const s=t(a);fetch(a.href,s)}})();const F3="modulepreload",R3=function(i,e){return new URL(i,e).href},Fu={},ee=function(e,t,n){let a=Promise.resolve();if(t&&t.length>0){let r=function(m){return Promise.all(m.map(c=>Promise.resolve(c).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};const o=document.getElementsByTagName("link"),l=document.querySelector("meta[property=csp-nonce]"),u=(l==null?void 0:l.nonce)||(l==null?void 0:l.getAttribute("nonce"));a=r(t.map(m=>{if(m=R3(m,n),m in Fu)return;Fu[m]=!0;const c=m.endsWith(".css"),d=c?'[rel="stylesheet"]':"";if(!!n)for(let f=o.length-1;f>=0;f--){const b=o[f];if(b.href===m&&(!c||b.rel==="stylesheet"))return}else if(document.querySelector(`link[href="${m}"]${d}`))return;const g=document.createElement("link");if(g.rel=c?"stylesheet":F3,c||(g.as="script"),g.crossOrigin="",g.href=m,u&&g.setAttribute("nonce",u),document.head.appendChild(g),c)return new Promise((f,b)=>{g.addEventListener("load",f),g.addEventListener("error",()=>b(new Error(`Unable to preload CSS for ${m}`)))})}))}function s(r){const o=new Event("vite:preloadError",{cancelable:!0});if(o.payload=r,window.dispatchEvent(o),!o.defaultPrevented)throw r}return a.then(r=>{for(const o of r||[])o.status==="rejected"&&s(o.reason);return e().catch(s)})};var S=(i=>(i[i.anweisung=0]="anweisung",i[i.verzweigung=1]="verzweigung",i[i.fallauswahl=2]="fallauswahl",i[i.forSchleife=3]="forSchleife",i[i.whileSchleife=4]="whileSchleife",i[i.doUntilSchleife=5]="doUntilSchleife",i[i.endlosschleife=6]="endlosschleife",i[i.aussprung=7]="aussprung",i[i.aufruf=8]="aufruf",i[i.leerElement=9]="leerElement",i))(S||{});function J3(i){return i===3||i===4||i===5||i===6}function G3(i){return i===1||i===2}function je(i,e={}){return{type:i,zoomX:0,zoomY:0,textLines:[""],...e}}function H3(i){const e=i.textLines;if(!e.every(t=>t.trim()===""))switch(i.type){case S.forSchleife:return e.length<=1?e:[q3(e)];case S.verzweigung:return xa("if",e);case S.whileSchleife:return xa("while",e);case S.doUntilSchleife:return xa("while",V3(e));case S.fallauswahl:return xa("switch",e);default:return e}}function V3(i){if(i.length===0)return i;const e=i[0].trim(),t=e.toLowerCase(),n=[...i];if(t==="until")n[0]="";else if(t.startsWith("until "))n[0]=e.slice(6).trim();else if(t.startsWith("until("))n[0]=e.slice(5);else return i;return n}function Go(i){const e=new Set(["if","for","while","until","switch"]),t=["input:","output:","print:"],n=i.trim(),a=n.toLowerCase();for(const o of t){if(a===o)return{keyword:n,rest:""};if(a.startsWith(o+" "))return{keyword:n.slice(0,o.length),rest:n.slice(o.length).trim()}}const s=n.indexOf(" ");if(s<0)return;const r=n.slice(0,s);if(e.has(r.toLowerCase()))return{keyword:r,rest:n.slice(s+1).trim()}}function xa(i,e){if(e.length===0)return e;const t=e[0].trim(),n=t.toLowerCase();if(n===i||n.startsWith(i+" "))return e;const a=[...e];return a[0]=i+" "+t,a}function q3(i){let e=[...i];for(;e.length<3;)e.push("");e=e.slice(0,3);const t=a=>{let s=a.trim();for(;s.endsWith(";");)s=s.slice(0,-1).trim();return s},n=e.map(t).join("; ").trim();return n===""?"for":"for "+n}const Nh={[S.anweisung]:"Anweisung",[S.verzweigung]:"Bedingung",[S.fallauswahl]:"Fallauswahl",[S.forSchleife]:"for 0 < i < anzahl",[S.whileSchleife]:"while …",[S.doUntilSchleife]:"while …",[S.endlosschleife]:"endless",[S.aussprung]:"break",[S.aufruf]:"Aufruf()",[S.leerElement]:"ø"};function U3(i){Object.assign(Nh,i)}function vr(i){return H3(i)??[Nh[i.type]]}const Fh="visucode.de";function En(i=location.hostname){const e=/^(visustruct|visubot|visuobject)\.visucode\.de$/.exec(i.toLowerCase());return e?e[1]:void 0}function Q3(i){const e=Rh(i);return`https://${Fh}/${e==="de"?"":`${e}/`}`}function Rh(i){return i==="pt-PT"?"pt":i==="de"?"de":"en"}function $3(i,e,t,n=location){const a=Rh(e),s=t?`#${t}`:"";return En(n.hostname)===i&&/\/app\/(index\.html)?$/.test(n.pathname)?`../${a}/index.html${s}`:`https://${i}.${Fh}/${a}/${s}`}class Ne extends Error{}const zn=i=>structuredClone(i);function sn(){return je(S.anweisung,{textLines:["ø"]})}function Y3(){return{caption:"",elements:[sn()]}}function wr(i){if(i.type===S.leerElement)return!0;if(i.type===S.anweisung){const e=i.textLines;return e.length===0||e.length===1&&e[0]==="ø"}return!1}function gn(i,e){const t=st(i,e);return t!==void 0&&wr(t)}function oa(i,e){if(e.length===0)throw new Ne("Invalid element path.");const[t,...n]=e;if(t<0||t>=i.length)throw new Ne("Invalid element path.");if(n.length===0)return{list:i,index:t};const[a,...s]=n;if(s.length===0)throw new Ne("Invalid element path.");return oa(Jh(i[t],a),s)}function Jh(i,e){if(J3(i.type)){if(e!==0||!i.loopBody)throw new Ne("Invalid element path.");return i.loopBody}if(G3(i.type)){if(!i.cases||e<0||e>=i.cases.length)throw new Ne("Invalid element path.");return i.cases[e].elements}throw new Ne("Invalid element path.")}function bt(i,e){const{list:t,index:n}=oa(i.elements,e);return t[n]}function st(i,e){try{return bt(i,e)}catch{return}}function Wn(i,e,t){const n=zn(i),{list:a,index:s}=oa(n.elements,e);return a[s]=zn(t),n}function pn(i,e,t){const n=zn(bt(i,e));return n.textLines=t.length===0?[""]:t,Wn(i,e,n)}function va(i,e){return e.length===0?!1:e.length===1&&i.elements.length===1?!gn(i,e):st(i,e)!==void 0}function L1(i,e){const t=zn(i),{list:n,index:a}=oa(t.elements,e);if(e.length===1&&n.length===1){if(wr(n[0]))throw new Ne("The last root element cannot be deleted.");return n[0]=sn(),t}return n.splice(a,1),e.length>1&&n.length===0&&n.push(sn()),t}function wi(i,e,t,n){const a=zn(i);let s;if(e.length===0)s=a.elements;else{const r=e.slice(0,-1),o=e[e.length-1],{list:l,index:u}=oa(a.elements,r);s=Jh(l[u],o)}return s.splice(Math.min(Math.max(0,t),s.length),0,zn(n)),a}function Gh(i,e,t){if(t.length===0)throw new Ne("Invalid element path.");return wi(i,t.slice(0,-1),t[t.length-1],e)}function Ho(i,e,t){if(t.length===0)throw new Ne("Invalid element path.");return wi(i,t.slice(0,-1),t[t.length-1]+1,e)}function x0(i,e){return i.lengthe[n]===t)}function v0(i,e){if(e.length===0)return i;const t=e[e.length-1],n=e.slice(0,-1);if(i.length<=n.length||!n.every((r,o)=>i[o]===r))return i;const a=n.length;if(i[a]<=t)return i;const s=[...i];return s[a]-=1,s}const w0=(i,e)=>i.length===e.length&&i.every((t,n)=>t===e[n]);function K3(i,e,t){if(w0(e,t))return i;if(x0(e,t))throw new Ne("Invalid element path.");const n=bt(i,e),a=L1(i,e);return Gh(a,n,v0(t,e))}function X3(i,e,t){if(w0(e,t))return i;if(x0(e,t))throw new Ne("Invalid element path.");const n=bt(i,e),a=L1(i,e);return Ho(a,n,v0(t,e))}function eb(i,e,t){if(!gn(i,t))throw new Ne("Invalid element path.");if(w0(e,t))return i;if(x0(e,t))throw new Ne("Invalid element path.");const n=bt(i,e),a=L1(i,e);return Wn(a,v0(t,e),n)}function tb(i,e){const t=zn(bt(i,e)),n=t.cases;if(t.type!==S.fallauswahl||!n||n.length===0)throw new Ne("Invalid element path.");const a=n[n.length-1].name.toLowerCase()==="default",s=a?n.length-1:n.length,r=String(a?n.length:n.length+1);return n.splice(s,0,{name:r,elements:[sn()]}),{doc:Wn(i,e,t),path:[...e,s,0]}}function nb(i,e,t){const n=zn(bt(i,e)),a=n.cases;if(n.type!==S.fallauswahl||!a)throw new Ne("Invalid element path.");if(a.length<=2)throw new Ne("A switch needs at least two cases.");if(t<0||t>=a.length)throw new Ne("Invalid element path.");a.splice(t,1);const s=Math.min(t,a.length-1);return{doc:Wn(i,e,n),path:[...e,s,0]}}function Ru(i,e,t,n){const a=zn(bt(i,e)),s=a.cases;if(a.type!==S.fallauswahl||!s)throw new Ne("Invalid element path.");if(t<0||t>=s.length)throw new Ne("Invalid element path.");const r=n.trim();if(r==="")throw new Ne("Invalid element path.");return s[t].name=r,Wn(i,e,a)}function ib(i,e){var t;if(!(!i||i.length===0))for(let n=i.length;n>=1;n--){const a=i.slice(0,n);if(((t=st(e,a))==null?void 0:t.type)===S.fallauswahl)return a}}function ab(i,e){if(!(!i||i.length<=e.length))return i[e.length]}function Gr(i,e){if(e.elements.length!==0)return!i||!st(e,i)?[0]:i}const Hh=i=>i!==void 0&&/\s/.test(i);function Ss(i){const e=i.trim().toLowerCase();return e.startsWith("input:")?"input":e.startsWith("output:")||e.startsWith("print:")?"output":"statement"}function sb(i){const e=i.trim(),t=e.toLowerCase();return t.startsWith("input:")?e.slice(6).trim():t.startsWith("output:")?e.slice(7).trim():t.startsWith("print:")?e.slice(6).trim():i}function Ju(i,e){const t=e.trim();return t===""?`${i} `:`${i} ${t}`}function rb(i,e){const t=sb(e);switch(i){case"statement":return t;case"input":return Ju("input:",t);case"output":return Ju("output:",t)}}const ls=i=>Hh(i[0])?i.slice(1):i;function ob(i){const e=i.replace(/^\s+/,""),t=e.toLowerCase();return t.startsWith("input:")?ls(e.slice(6)):t.startsWith("output:")?ls(e.slice(7)):t.startsWith("print:")?ls(e.slice(6)):i}function Gu(i,e){const t=e.replace(/^\s+/,"");if(!t.toLowerCase().startsWith(i.toLowerCase()))return e;const n=t.slice(i.length);if(!(n===""||Hh(n[0])||n[0]==="("))return e;const a=ls(n);return a.startsWith("(")&&a.endsWith(")")&&a.length>=2?a.slice(1,-1):a}function Vh(i,e){if(i==="input:"||i==="output:")return ob(e);const t=Gu(i,e);if(i==="while"&&t===e){const n=Gu("until",e);if(n!==e)return n}return t}function lb(i,e){return e===""?`${i} `:`${i} ${e}`}function y0(i){return i.replace(/[“”„«»]/g,'"').replace(/[’‘‚´]/g,"'")}function Hr(i){const e=i.length===0?[""]:i.slice(0,3);for(;e.length<3;)e.push("");return e}function Hu(i){let e=y0(i);for(;;){const t=e.trim();if(!t.endsWith(";"))return e;e=t.slice(0,-1).trim()}}const la='system-ui, -apple-system, "Segoe UI", Roboto, sans-serif';function ub(i,e){return`${e==="bold"?700:500} ${i}px ${la}`}const qh=(i,e,t)=>[...i].length*e*(t==="bold"?.68:.62);function mb(){if(typeof document>"u"||!document.body)return;const i="http://www.w3.org/2000/svg",e=document.createElementNS(i,"svg");e.setAttribute("aria-hidden","true"),e.setAttribute("width","0"),e.setAttribute("height","0"),e.style.cssText="position:absolute;left:-9999px;top:0;visibility:hidden;overflow:hidden";const t=document.createElementNS(i,"text");if(t.setAttribute("font-family",la),t.style.whiteSpace="pre",e.append(t),document.body.append(e),typeof t.getComputedTextLength!="function"||typeof t.getBBox!="function"){e.remove();return}if(t.textContent="Mg",t.setAttribute("font-size","20"),!(t.getComputedTextLength()>0)){e.remove();return}return t}let Vr;function Uh(){return Vr===void 0&&(Vr=mb()??null),Vr??void 0}function Qh(i,e,t){i.setAttribute("font-size",String(e)),i.setAttribute("font-weight",t==="bold"?"700":"500")}function Vu(i,e){const t=`${i}|${e}`;let n=qu.get(t);if(!n){const a=Uh();if(n={ascent:i*.95,descent:i*.24},a){Qh(a,i,e),a.setAttribute("y","0"),a.textContent="Mg";const s=a.getBBox();s.height>0&&(n={ascent:-s.y,descent:s.height+s.y})}qu.set(t,n)}return n}const qu=new Map;function cb(){const i=Uh();if(i){const n=new Map;return(a,s,r)=>{const o=`${s}|${r}|${a}`;let l=n.get(o);return l===void 0&&(Qh(i,s,r),i.textContent=a,l=a===""?0:i.getComputedTextLength(),n.size>2e3&&n.clear(),n.set(o,l)),l}}const e=typeof document<"u"?document.createElement("canvas").getContext("2d"):null;if(!e)return qh;const t=new Map;return(n,a,s)=>{const r=`${a}|${s}|${n}`;let o=t.get(r);return o===void 0&&(e.font=ub(a,s),o=e.measureText(n).width,t.size>2e3&&t.clear(),t.set(r,o)),o}}const $h={fontSize:15,lineHeight:20,charWidth:7,baseHeaderExtra:20,minSidePadding:42,cellTextInsetY:15,doUntilDoLabelTopInset:2,doUntilFooterTopInset:6,doUntilFooterBottomInset:8};function db(i){return{...$h,fontSize:14,lineHeight:21,charWidth:5.5*(14/12),baseHeaderExtra:21,minSidePadding:40}}const wa=i=>i===S.forSchleife||i===S.whileSchleife||i===S.doUntilSchleife||i===S.endlosschleife,gi=i=>i===S.fallauswahl||i===S.verzweigung;function ya(i,e,t,n=0){return{idPath:i,element:e,frame:t,headerBandHeight:n,topBandHeight:0,loopBodyLeadingInset:0,caseColumnFrames:[],children:[]}}function at(i){return{...i,frame:{...i.frame}}}class Yh{constructor(e=$h,t=qh){this.metrics=e,this.measure=t}layout(e,t={left:28,top:28}){const a=this.layoutList(e.elements,t.left,t.top,[]).map(l=>this.stretchLoopBodiesVertically(l));let s=0,r=0;const o=l=>{s=Math.max(s,l.frame.x+l.frame.width),r=Math.max(r,l.frame.y+l.frame.height),l.children.forEach(o)};return a.forEach(o),{canvasSize:{width:s+t.left,height:r+t.top},elements:a}}lineWidth(e){return Math.ceil(this.measure(e,this.metrics.fontSize,"medium"))}lineWidthBold(e){return Math.ceil(this.measure(e,this.metrics.fontSize,"bold"))}layoutList(e,t,n,a){let s=[],r=n;e.forEach((l,u)=>{const m=this.layoutElement(l,t,r,[...a,u]);s.push(m),r+=m.frame.height});const o=Math.max(0,...s.map(l=>l.frame.width));return o>0&&(s=s.map(l=>this.stretchNodeTreeToWidth(l,o))),s}stretchMultiwayNodeToWidth(e,t){const n=at(e);if(n.caseColumnFrames.length===0)return n;const a=n.caseColumnFrames.reduce((l,u)=>l+u.width,0);if(!(a>0))return n;const s=t/a;n.frame.width=t;let r=n.frame.x;const o=n.caseColumnFrames.map(l=>{const u=l.width*s,m={x:r,y:l.y,width:u,height:l.height};return r+=u,m});return n.caseColumnFrames=o,n.dividerX=o.length>0?o[o.length-1].x:n.frame.x+t,n.columns&&n.columns.length===o.length&&(n.columns=n.columns.map((l,u)=>{const m=o[u];return l.map(c=>{const d=at(c);return d.frame.x=m.x,d.frame.width=m.width,this.stretchDescendantsWidths(d,m.width)})}),n.children=n.columns.flat()),n}stretchLoopChildren(e,t){const n=this.effectiveLoopBodyLeading(e),a=Math.max(0,t-n);e.children=e.children.map(s=>{const r=at(s);return r.frame.x=e.frame.x+n,r.frame.width=a,this.stretchDescendantsWidths(r,a)})}stretchNodeTreeToWidth(e,t){const n=at(e);return n.frame.width>0?gi(n.element.type)&&n.caseColumnFrames.length>0?this.stretchMultiwayNodeToWidth(n,t):(n.frame.width=t,wa(n.element.type)?this.stretchLoopChildren(n,t):n.children=n.children.map(s=>this.stretchDescendantsWidths(s,t)),n):n}stretchDescendantsWidths(e,t){let n=at(e);return gi(n.element.type)?n=this.stretchNodeTreeToWidth(n,t):wa(n.element.type)?(n.frame.width=t,this.stretchLoopChildren(n,t)):(n.frame.width=t,n.children=n.children.map(a=>this.stretchDescendantsWidths(a,t))),n}stretchLoopBodiesVertically(e){const t=at(e);if(t.columns&&t.columns.length>0)return t.columns=t.columns.map(s=>s.map(r=>this.stretchLoopBodiesVertically(r))),t.children=t.columns.flat(),t;t.children=t.children.map(s=>this.stretchLoopBodiesVertically(s));const n=s=>s===S.anweisung||s===S.leerElement||s===S.aufruf||s===S.aussprung||wa(s);let a;switch(t.element.type){case S.forSchleife:case S.whileSchleife:case S.endlosschleife:{const s=t.element.type===S.endlosschleife?40:0;a=t.frame.y+t.frame.height-s;break}case S.doUntilSchleife:a=t.frame.y+t.frame.height-t.headerBandHeight;break}if(a!==void 0&&t.children.length>0){const s=t.children.length-1;let r=at(t.children[s]);const o=a-(r.frame.y+r.frame.height);o>.5&&n(r.element.type)&&(r.frame.height+=o,r=this.stretchLoopBodiesVertically(r),t.children=[...t.children.slice(0,s),r])}return t}headerHeight(e){const t=this.metrics,n=Math.max(1,e.textLines.length);if(e.type===S.doUntilSchleife)return t.doUntilFooterTopInset+n*t.lineHeight+t.doUntilFooterBottomInset+e.zoomY;let a;switch(e.type){case S.fallauswahl:a=40;break;case S.verzweigung:a=20;break;case S.forSchleife:case S.whileSchleife:case S.endlosschleife:a=t.baseHeaderExtra+4;break;default:a=14}return a+n*t.lineHeight+e.zoomY}doUntilBodyLeadingInset(){return Math.max(20,this.lineWidthBold("do")+12)}effectiveLoopBodyLeading(e){return e.element.type===S.doUntilSchleife?e.loopBodyLeadingInset>0?e.loopBodyLeadingInset:this.doUntilBodyLeadingInset():20}horizontalPaddingInsideCell(e){return gi(e)?Math.max(100,this.metrics.minSidePadding*2.45):this.metrics.minSidePadding}minWidth(e){const t=e.textLines.length===0?0:Math.max(...e.textLines.map(s=>this.lineWidth(s)));let n=t+e.zoomX+this.horizontalPaddingInsideCell(e.type);if(e.type===S.aufruf){const s=Math.max(68,this.metrics.minSidePadding*1.7),r=Math.max(8,this.metrics.minSidePadding*.2);n=Math.max(n,t+e.zoomX+s+r)}const a=e.type===S.leerElement?76:e.type===S.aufruf?104:72;return Math.max(a,n)}layoutElement(e,t,n,a){if(gi(e.type))return this.layoutMultiway(e,t,n,a);if(wa(e.type))return this.layoutLoop(e,t,n,a);const s=this.headerHeight(e);return ya(a,e,{x:t,y:n,width:this.minWidth(e),height:s},s)}layoutLoop(e,t,n,a){const s=b=>b.reduce((x,v)=>x+v.frame.height,0),r=b=>Math.max(0,...b.map(x=>x.frame.width)),o=e.loopBody??[];if(e.type===S.doUntilSchleife){const b=this.doUntilBodyLeadingInset(),x=e.zoomY,v=this.headerHeight(e),w=this.layoutList(o,t+b,n+x,[...a,0]),z=Math.max(this.minWidth(e),r(w)+b),I=s(w)+v+x,T=Math.max(0,z-b),k=ya(a,e,{x:t,y:n,width:z,height:I},v);return k.topBandHeight=x,k.loopBodyLeadingInset=b,k.children=w.map(C=>{const P=at(C);return P.frame.x=t+b,P.frame.width=T,this.stretchDescendantsWidths(P,T)}),k}const l=20,u=this.headerHeight(e),m=e.type===S.endlosschleife?40:0,c=this.layoutList(o,t+l,n+u,[...a,0]),d=Math.max(this.minWidth(e),r(c)+l),h=u+m+s(c),g=Math.max(0,d-l),f=ya(a,e,{x:t,y:n,width:d,height:h},u);return f.children=c.map(b=>{const x=at(b);return x.frame.x=t+l,x.frame.width=g,this.stretchDescendantsWidths(x,g)}),f}layoutMultiway(e,t,n,a){const s=this.headerHeight(e),r=e.cases??[],o=Math.max(1,r.length),l=Math.max(this.minWidth(e)/o,132);let u=[],m=[],c=0,d=0;if(r.forEach((b,x)=>{const v=t+c,w=this.layoutList(b.elements,v,n+s,[...a,x]),z=Math.max(0,...w.map(k=>k.frame.width));let I;e.type===S.verzweigung&&o===2?I=Math.max(z,(x===0?"true":"false").length*this.metrics.charWidth+24):I=Math.max(l,z);const T=w.reduce((k,C)=>k+C.frame.height,0);d=Math.max(d,T),u.push({x:v,y:n+s,width:I,height:T}),m.push(w.map(k=>{const C=at(k);return C.frame.x=v,C.frame.width=I,this.stretchDescendantsWidths(C,I)})),c+=I}),e.type===S.verzweigung&&o===2&&u.length===2&&!r.some(b=>Vo(b.elements))){const b=Math.max(u[0].width,u[1].width),x=t,v=t+b,w=v-u[1].x;u[0]={...u[0],x,width:b},u[1]={...u[1],x:v,width:b},c=b*2,m[0]=m[0].map(z=>{const I=at(z);return I.frame.x=x,I.frame.width=b,this.stretchDescendantsWidths(I,b)}),m[1]=m[1].map(z=>{const I=qo(z,w);return I.frame.width=b,this.stretchDescendantsWidths(I,b)})}if(m.length>0){const b=hb(m,n+s);m=b.columns.map(x=>x.map(v=>this.stretchLoopBodiesVertically(v)));for(let x=0;x0?Math.max(...b.columnHeights):d}let h=Math.max(c,this.minWidth(e));if(c>.5&&h>c+.5){const b=h/c;let x=t;const v=[],w=[];u.forEach((z,I)=>{const T=z.width*b;w.push(m[I].map(k=>{const C=at(k);return C.frame.x=x,C.frame.width=T,this.stretchDescendantsWidths(C,T)})),v.push({x,y:z.y,width:T,height:z.height}),x+=T}),u=v,m=w,c=x-t,h=c}const g=u.length>0?u[u.length-1].width:c/o,f=ya(a,e,{x:t,y:n,width:h,height:s+d},s);return f.caseColumnFrames=u,f.dividerX=u.length>0?u[u.length-1].x:t+c-g,f.dividerYInset=e.type===S.fallauswahl?-20:0,f.columns=m,f.children=m.flat(),f}}function Vo(i){return i.some(e=>gi(e.type)||Vo(e.loopBody??[])||(e.cases??[]).some(t=>Vo(t.elements)))}function Kh(i,e){if(Math.abs(e)<=1e-4)return i;const t=at(i),n=a=>{const s=at(a);return s.frame.y+=e,Kh(s,e)};return t.columns?(t.columns=t.columns.map(a=>a.map(n)),t.children=t.columns.flat()):t.children=t.children.map(n),t}function qo(i,e){if(Math.abs(e)<=1e-4)return i;const t=at(i);return t.frame.x+=e,t.columns?(t.columns=t.columns.map(n=>n.map(a=>qo(a,e))),t.children=t.columns.flat()):t.children=t.children.map(n=>qo(n,e)),t}function hb(i,e){if(i.length<2)return{columns:i,columnHeights:i.map(u=>u.reduce((m,c)=>m+c.frame.height,0))};const t=.5,n=[e];for(const u of i)for(const m of u)n.push(m.frame.y+m.frame.height);const a=Math.max(...n);n.push(a),n.sort((u,m)=>u-m);let s=[];for(const u of n)s.length>0&&Math.abs(u-s[s.length-1])<=t||s.push(u);(s.length===0||Math.abs(s[0]-e)>t)&&s.unshift(e),s.length===0?s=[e,a]:s[s.length-1]{let c=Math.max(0,m);for(;c{let g=at(d);const f=s[c],b=d.frame.y+d.frame.height,x=h===u.length-1?s.length-1:r(b,c+1),v=s[x],w=f-g.frame.y;g.frame.y=f,g.frame.height=Math.max(0,v-f),g=Kh(g,w),m.push(g),c=x}),o.push(m),l.push(s[s.length-1]-e)}return{columns:o,columnHeights:l}}const gb=[{item:"anweisung",label:"Anweisung"},{item:"verzweigung",label:"Verzweigung"},{item:"fallauswahl",label:"Fallauswahl"},{item:"forZaehlSchleife",label:"For-Schleife"},{item:"whileSchleife",label:"While-Schleife"},{item:"doUntilSchleife",label:"Do-while-Schleife"},{item:"endlosschleife",label:"Endlosschleife"},{item:"aufruf",label:"Aufruf"},{item:"aussprung",label:"Aussprung"}];function qr(i){const e=sn;switch(i){case"anweisung":return je(S.anweisung);case"verzweigung":return je(S.verzweigung,{cases:[{name:"true",elements:[e()]},{name:"false",elements:[e()]}]});case"fallauswahl":return je(S.fallauswahl,{cases:[{name:"1",elements:[e()]},{name:"2",elements:[e()]},{name:"default",elements:[e()]}]});case"forZaehlSchleife":return je(S.forSchleife,{textLines:["","",""],loopBody:[e()]});case"whileSchleife":return je(S.whileSchleife,{loopBody:[e()]});case"doUntilSchleife":return je(S.doUntilSchleife,{loopBody:[e()]});case"endlosschleife":return je(S.endlosschleife,{loopBody:[e()]});case"aussprung":return je(S.aussprung);case"aufruf":return je(S.aufruf)}}function Ts(i){if(i==="")return"-1;";let e="";for(const t of i)e+=t.codePointAt(0)+";";return e}function Uo(i){const e=i.split(";").filter(n=>n!=="");if(e.length===1&&e[0]==="-1")return"";let t="";for(const n of e){if(!/^[+-]?\d+$/.test(n))continue;const a=Number(n);a!==-1&&(a<0||a>1114111||a>=55296&&a<=57343||(t+=String.fromCodePoint(a)))}return t}class Uu extends Error{}function z0(i){const e=new DOMParser().parseFromString(i,"application/xml");if(e.getElementsByTagName("parsererror").length>0)throw new Uu("Could not read XML.");const t=e.documentElement;if(!t||t.tagName!=="struktogramm")throw new Uu("Invalid XML: root element is not .");const n={caption:$u(t,"caption")??"",elements:$o(t)},a=$u(t,"fontfamily");a!==void 0&&(n.fontFamily=a);const s=yi(t,"fontsize");s!==void 0&&(n.fontSize=s);const r=yi(t,"fontstyle");return r!==void 0&&(n.fontStyle=r),t.getAttribute("pseudocode")==="true"&&(n.pseudocode=!0),n}function x1(i){const e=(n,a)=>a===void 0?"":` ${n}="${Xh(a)}"`;let t=` -`;t+=" -`;for(const n of i.elements)t+=Qo(n," ");return t+=` -`,t}function Qo(i,e){let t=`${e} -`;for(const n of i.textLines)t+=`${e} ${pb(Ts(n))} -`;if(i.loopBody){t+=`${e} -`;for(const n of i.loopBody)t+=Qo(n,e+" ");t+=`${e} -`}if(i.cases)for(const n of i.cases){t+=`${e} -`;for(const a of n.elements)t+=Qo(a,e+" ");t+=`${e} -`}return t+=`${e} -`,t}function Xh(i){return i.replace(/&/g,"&").replace(/"/g,""").replace(//g,">")}function pb(i){return i.replace(/&/g,"&").replace(//g,">")}function Qu(i){return String(i.a<<24|i.r<<16|i.g<<8|i.b|0)}function us(i,e){return Array.from(i.children).filter(t=>t.tagName===e)}function $o(i){const e=[];for(const t of us(i,"strelem")){const n=fb(t);n&&e.push(n)}return e}function fb(i){const e=yi(i,"typ");if(e===void 0||S[e]===void 0)return;const t={type:e,zoomX:yi(i,"zx")??0,zoomY:yi(i,"zy")??0,textLines:us(i,"text").map(o=>Uo((o.textContent??"").trim()))},n=Yu(i.getAttribute("textcolor"));n&&(t.textColor=n);const a=Yu(i.getAttribute("bgcolor"));a&&(t.backgroundColor=a),t.textLines.length===0&&(t.textLines=[""]);const s=us(i,"schleifeninhalt")[0];if(s){const o=$o(s);o.length>0&&(t.loopBody=o)}const r=us(i,"fall").map(o=>({name:Uo(o.getAttribute("fallname")??""),elements:$o(o)}));return r.length>0&&(t.cases=r),t}function $u(i,e){const t=i.getAttribute(e);return t===null?void 0:Uo(t)}function yi(i,e){const t=i.getAttribute(e);if(!(t===null||!/^[+-]?\d+$/.test(t)))return Number(t)}function Yu(i){if(i===null||!/^[+-]?\d+$/.test(i))return;const e=Number(i);if(e<-2147483648||e>2147483647)return;const t=e>>>0;return{a:t>>>24&255,r:t>>>16&255,g:t>>>8&255,b:t&255}}const bb="http://www.w3.org/2000/svg",Fe={diagramLineWidth:2,outerFrameLineWidth:2,selectionLineWidth:3.5,canvasOuterMargin:16,cellTextInsetX:18,cellTextInsetY:15,multiHeaderFirstLineY:14,branchLabelInsetX:10,fallauswahlCaseLabelBand:26,callSideBarInset:14,callTextInsetFromSideBar:20,loopBodyLeadingInset:20};function Yo(i){switch(i){case S.forSchleife:case S.whileSchleife:case S.doUntilSchleife:case S.endlosschleife:return"loop";case S.verzweigung:case S.fallauswahl:return"if";default:return}}function Te(i,e={}){const t=document.createElementNS(bb,i);for(const[n,a]of Object.entries(e))t.setAttribute(n,String(a));return t}class eg{constructor(e,t){y(this,"usesSimulationHighlight",!1);y(this,"fillsSelection",!1);this.metrics=e,this.measure=t}get lineHeight(){return this.metrics.lineHeight}get fontSize(){return this.metrics.fontSize}render(e,t,n,a=Fe.canvasOuterMargin){const s=Te("svg",{width:t.width,height:t.height,viewBox:`0 0 ${t.width} ${t.height}`,"font-family":la,"font-size":this.fontSize,class:"diagram"});s.append(Te("rect",{x:0,y:0,width:t.width,height:t.height,fill:"#fafafa"}));const r=a;s.append(Te("rect",{x:r,y:r,width:e.canvasSize.width-2*r-1,height:e.canvasSize.height-2*r-1,fill:"none",stroke:"black","stroke-width":Fe.outerFrameLineWidth}));const o=Te("g");for(const l of e.elements)this.drawNode(l,o,n,{},void 0,"method");return s.append(o),s}line(e,t){e.append(Te("polyline",{points:t.map(n=>n.join(",")).join(" "),fill:"none",stroke:"black","stroke-width":Fe.diagramLineWidth,"stroke-linejoin":"miter"}))}strokeCellBorder(e,t,n){const a=t.x,s=t.y,r=t.x+t.width,o=t.y+t.height;if(!n.top&&!n.left&&!n.bottom&&!n.right){e.append(Te("rect",{x:a,y:s,width:t.width,height:t.height,fill:"none",stroke:"black","stroke-width":Fe.diagramLineWidth}));return}n.top||this.line(e,[[a,s],[r,s]]),n.right||this.line(e,[[r,s],[r,o]]),n.bottom||this.line(e,[[r,o],[a,o]]),n.left||this.line(e,[[a,o],[a,s]])}textWidth(e){const t=Go(e);return t?this.measure(t.keyword,this.fontSize,"bold")+(t.rest===""?0:this.measure(" "+t.rest,this.fontSize,"medium")):this.measure(e,this.fontSize,"medium")}drawKeywordLine(e,t,n,a){const s=Te("text",{x:n,y:a+Vu(this.fontSize,"medium").ascent,"font-weight":500});s.style.whiteSpace="pre";const r=Go(t);if(r){const o=Te("tspan",{"font-weight":700});if(o.textContent=r.keyword,s.append(o),r.rest!==""){const l=Te("tspan",{});l.textContent=" "+r.rest,s.append(l)}}else s.textContent=t;e.append(s)}drawLabel(e,t,n,a,s,r){const o=Vu(this.fontSize,s?"bold":"medium"),l=Te("text",{x:n,y:r==="top"?a+o.ascent:a-o.descent,"font-weight":s?700:500});l.style.whiteSpace="pre",l.textContent=t,e.append(l)}roofMetrics(e,t,n,a){const s=t.y+n,r=e.dividerYInset??0,o=12,l=Math.max(e.element.textLines.length,1)*this.lineHeight,u=t.y+Fe.multiHeaderFirstLineY;if(e.element.type===S.fallauswahl){const c=Math.max(Fe.fallauswahlCaseLabelBand,this.lineHeight+10),d=s-c,h=Math.min(s+r,d-8),g=Math.max(h,u+l+6);return{apex:{x:a,y:g},maxTextBottom:d-o,roofBottom:s}}const m={x:a,y:s+r};return{apex:m,maxTextBottom:m.y-o,roofBottom:s}}drawNode(e,t,n,a,s,r){var C;const o=e.frame,l=e.element,u=o.x+o.width,m=o.y+o.height,c=l.type===S.verzweigung||l.type===S.fallauswahl,d=c?e.headerBandHeight:0,h=n!==void 0&&Mn(e.idPath,n),g=vr(l),f=Te("g",{"data-path":e.idPath.join(",")});t.append(f);const b=this.usesSimulationHighlight,x=l.backgroundColor,v=x&&x.a>0?`rgba(${x.r}, ${x.g}, ${x.b}, ${x.a/255})`:"white",w=h&&(b||this.fillsSelection)?"rgba(199, 230, 255, 0.55)":v,z=Yo(l.type)??(l.type===S.leerElement?r:"method"),I=Te("rect",{x:o.x,y:o.y,width:o.width,height:o.height,fill:w});w==="white"&&z&&I.setAttribute("class",`ns-scope ns-scope-${z}`),f.append(I),this.strokeCellBorder(f,o,a);const T=Fe.callSideBarInset;if(l.type===S.aufruf?(this.line(f,[[o.x+T,o.y],[o.x+T,m]]),this.line(f,[[u-T,o.y],[u-T,m]])):l.type===S.aussprung&&this.line(f,[[o.x+T,o.y],[o.x,o.y+o.height/2],[o.x+T,m]]),c&&e.dividerX!==void 0){const P=e.dividerX,D=this.roofMetrics(e,o,d,P),B=Math.max(g.length,1)*this.lineHeight,j=o.y+Fe.multiHeaderFirstLineY;this.line(f,[[o.x,o.y],[D.apex.x,D.apex.y],[u,o.y]]),l.type===S.fallauswahl&&this.line(f,[[o.x,D.roofBottom],[u,D.roofBottom]]);let Z;j+B<=D.maxTextBottom?Z=j:Z=Math.max(o.y+2,D.maxTextBottom-(Math.max(g.length,1)-1)*this.lineHeight-this.lineHeight),g.forEach((F,U)=>{const ne=this.textWidth(F);let ge;if(l.type===S.verzweigung){const ie=o.x+Fe.cellTextInsetX,ue=u-ne-Fe.cellTextInsetX;ge=Math.min(Math.max(P-ne/2,ie),ue)}else ge=o.x+o.width/2-ne/2;this.drawKeywordLine(f,F,ge,Z+U*this.lineHeight)})}else if(l.type!==S.doUntilSchleife)if(l.type===S.anweisung||l.type===S.aufruf||l.type===S.aussprung){const P=Fe.cellTextInsetX;let D=o.x+P,B=u-P;l.type===S.aufruf?(D=o.x+T+Fe.callTextInsetFromSideBar,B=u-T-Fe.callTextInsetFromSideBar):l.type===S.aussprung&&(D=o.x+T+P);const j=Math.max(0,B-D);let Z=o.y+o.height/2-Math.max(1,g.length)*this.lineHeight/2;for(const F of g)this.drawKeywordLine(f,F,D+(j-this.textWidth(F))/2,Z),Z+=this.lineHeight}else{const P=Math.max(1,g.length)*this.lineHeight;let D=s===S.doUntilSchleife?o.y+o.height/2-P/2:o.y+Fe.cellTextInsetY;for(const B of g)this.drawKeywordLine(f,B,o.x+Fe.cellTextInsetX,D),D+=this.lineHeight}e.children.forEach((P,D)=>{const B=l.type===S.doUntilSchleife&&D===0?{top:!0,left:!0}:{};this.drawNode(P,t,n,B,l.type,z)});const k=Te("g");if(t.append(k),l.type===S.doUntilSchleife){const P=((C=e.children[0])==null?void 0:C.frame.x)??o.x+(e.loopBodyLeadingInset>0?e.loopBodyLeadingInset:Fe.loopBodyLeadingInset);this.drawLabel(k,"do",o.x+4,o.y+this.metrics.doUntilDoLabelTopInset,!0,"top");const D=m-e.headerBandHeight;this.line(k,[[P,D],[u,D]]),this.line(k,[[P,o.y],[P,D]]);let B=D+this.metrics.doUntilFooterTopInset;for(const j of g)this.drawKeywordLine(k,j,o.x+Fe.cellTextInsetX,B),B+=this.lineHeight}if(c&&e.dividerX!==void 0&&e.caseColumnFrames.length>1){const P=this.roofMetrics(e,o,d,e.dividerX),D=P.apex;e.caseColumnFrames.forEach((j,Z)=>{if(Z>=e.caseColumnFrames.length-1)return;const F=j.x+j.width,U=xb(F,o,D),ne=Number.isFinite(U)?Math.min(Math.max(U,o.y),m):o.y;this.line(k,[[F,m],[F,ne]])});const B=l.cases;if(B&&B.length===e.caseColumnFrames.length){const j=l.type===S.fallauswahl?P.roofBottom-6:o.y+d-8,Z=Fe.branchLabelInsetX;if(l.type===S.verzweigung&&B.length===2){this.drawLabel(k,"true",o.x+Z,j,!0,"bottom");const F=e.caseColumnFrames[1],U=this.measure("false",this.fontSize,"bold");this.drawLabel(k,"false",F.x+F.width-Z-U,j,!0,"bottom")}else B.forEach((F,U)=>{const ne=e.caseColumnFrames[U],ge=["default","true","false"].includes(F.name.toLowerCase()),ie=this.measure(F.name,this.fontSize,ge?"bold":"medium");this.drawLabel(k,F.name,ne.x+ne.width/2-ie/2,j,ge,"bottom")})}}h&&k.append(Te("rect",{x:o.x,y:o.y,width:o.width,height:o.height,class:"selection-frame",fill:"none",stroke:b?"rgba(66, 140, 242, 0.78)":"rgba(0, 122, 255, 0.9)","stroke-width":Fe.selectionLineWidth}))}}function xb(i,e,t){const n=e.y;if(i<=t.x){const s=t.x-e.x;return Math.abs(s)<.5?t.y:n+(t.y-n)/s*(i-e.x)}const a=e.x+e.width-t.x;return Math.abs(a)<.5?t.y:t.y+(n-t.y)/a*(i-t.x)}function Mn(i,e){return i.length===e.length&&i.every((t,n)=>t===e[n])}function vb(i){const e=[],t=n=>{e.push(n),n.children.forEach(t)};return i.forEach(t),e}function _s(i){var e,t;return{...i,textLines:vr(i),loopBody:(e=i.loopBody)==null?void 0:e.map(_s),cases:(t=i.cases)==null?void 0:t.map(n=>({name:n.name,elements:n.elements.map(_s)}))}}const za=14;function Ku(i,e){var n;const t=i.frame;switch(i.element.type){case S.aufruf:return{x:t.x+za,y:t.y,width:Math.max(40,t.width-2*za),height:t.height};case S.aussprung:return{x:t.x+za,y:t.y,width:Math.max(40,t.width-za),height:t.height};case S.verzweigung:return{x:t.x,y:t.y,width:t.width,height:i.headerBandHeight};case S.fallauswahl:return{x:t.x,y:t.y,width:t.width,height:Math.max(e+8,i.headerBandHeight-I0(e))};case S.forSchleife:case S.whileSchleife:case S.endlosschleife:{const a=((n=i.children[0])==null?void 0:n.frame.y)??t.y+t.height;return{x:t.x,y:t.y,width:t.width,height:Math.max(e+8,a-t.y)}}case S.doUntilSchleife:return{x:t.x,y:t.y+t.height-i.headerBandHeight,width:t.width,height:i.headerBandHeight};default:return t}}function I0(i){return Math.max(26,i+10)}function wb(i,e,t,n){if(i.element.type!==S.fallauswahl)return;const a=i.frame.y+i.headerBandHeight;if(t>a||te>=r.x&&e<=r.x+r.width);return s>=0?s:void 0}function yb(i,e,t){const n=i.caseColumnFrames[e],a=I0(t);return{x:n.x,y:i.frame.y+i.headerBandHeight-a,width:n.width,height:a}}function zb(i){const e=i.zoom,t=document.createElement("div");t.className="inline-edit",i.center&&t.classList.add("inline-edit-center");const n=40,a=i.rect.y*e,s=Math.max(n,i.rect.height*e);if(Object.assign(t.style,{left:`${i.rect.x*e}px`,top:`${a-Math.max(0,(n-i.rect.height*e)/2)}px`,width:`${Math.max(i.fields.length>1?300:120,i.rect.width*e)}px`,minHeight:`${s}px`,fontFamily:la,fontSize:`${i.fontSize*e}px`,lineHeight:`${i.lineHeight*e}px`}),i.prefix){const c=document.createElement("span");c.className="inline-edit-prefix",c.textContent=i.prefix,t.append(c)}const r=[];i.fields.forEach((c,d)=>{const g=i.fields.length===1?document.createElement("textarea"):document.createElement("input");if(g.value=c.value,g.placeholder=c.placeholder,g.spellcheck=!1,g.setAttribute("autocomplete","off"),g.setAttribute("autocapitalize","off"),g.setAttribute("autocorrect","off"),g.setAttribute("aria-label",c.label),g instanceof HTMLTextAreaElement&&(g.rows=Math.max(1,c.value.split(` -`).length)),d>0){const f=document.createElement("span");f.className="inline-edit-sep",f.textContent=";",t.append(f)}t.append(g),r.push(g)});const o=c=>{c instanceof HTMLTextAreaElement&&(c.rows=Math.max(1,c.value.split(` -`).length),c.style.height="auto",c.style.height=`${c.scrollHeight}px`)};let l=!1;const u=c=>{l||(l=!0,t.remove(),i.onDone(c?r.map(d=>d.value):void 0))};for(const c of r)c.addEventListener("input",()=>{const d=y0(c.value);if(d!==c.value){const h=c.selectionStart;c.value=d,c.setSelectionRange(h,h)}o(c)}),c.addEventListener("keydown",d=>{d.stopPropagation(),d.key==="Escape"?(d.preventDefault(),u(!1)):d.key==="Enter"&&!(d.shiftKey&&i.fields.length===1)&&!d.isComposing&&(d.preventDefault(),u(!0))});t.addEventListener("focusout",c=>{const d=c.relatedTarget;d instanceof Node&&t.contains(d)||setTimeout(()=>{!l&&!t.contains(document.activeElement)&&u(!0)},0)});for(const c of["pointerdown","click","dblclick","contextmenu"])t.addEventListener(c,d=>d.stopPropagation());i.host.append(t),r.forEach(o);const m=r[0];return m.focus({preventScroll:!0}),m.select(),t.scrollIntoView({block:"nearest",inline:"nearest"}),{element:t,finish:u}}const Ib=[{code:"de",name:"Deutsch"},{code:"en",name:"English"},{code:"es",name:"Español"},{code:"fr",name:"Français"},{code:"it",name:"Italiano"},{code:"pl",name:"Polski"},{code:"tr",name:"Türkçe"},{code:"ko",name:"한국어"},{code:"ja",name:"日本語"},{code:"el",name:"Ελληνικά"},{code:"uk",name:"Українська"},{code:"ar",name:"العربية"},{code:"ru",name:"Русский"},{code:"he",name:"עברית"},{code:"nl",name:"Nederlands"},{code:"sv",name:"Svenska"},{code:"da",name:"Dansk"},{code:"nb",name:"Norsk bokmål"},{code:"zh-Hans",name:"中文(简体)"},{code:"zh-Hant",name:"中文(繁體)"},{code:"pt-PT",name:"Português"},{code:"java",name:"Java (Keywords)"}],tg=["de","en","pt-PT"],ng=Ib.filter(i=>tg.includes(i.code)),Xu=i=>tg.includes(i),kb=Object.assign({"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/ar.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-CxCno28C.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/da.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-BAutdlBb.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/de.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-Dk8F86qS.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/el.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-rqA-20hM.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/en.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-DBLIUcBd.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/es.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-LSTVB2Q5.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/fr.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-ByPZS4RL.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/he.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-R-16GCrk.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/it.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-DXAPah1g.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/ja.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-CkJEmEEP.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/ko.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-BudCXCER.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/nb.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-DSvkBkp-.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/nl.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-BJ55WGVD.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/pl.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-BYJK0mdR.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/pt-PT.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-CHv0OL9S.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/ru.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-1QZ0wfvJ.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/sv.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-M33nNbEe.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/tr.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-BM86Igqt.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/uk.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-UGog_aku.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/zh-Hans.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-CaVEL1es.js"),[],import.meta.url).then(i=>i.default),"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/zh-Hant.lproj/Localizable.strings":()=>ee(()=>import("./Localizable-DqIDp_hh.js"),[],import.meta.url).then(i=>i.default)}),ig=Object.assign({"../../../VisuStruct-iOS/VisuStructXcodeOpen/VisuStruct/Java.strings":()=>ee(()=>import("./Java-Bpx1Vwvg.js"),[],import.meta.url).then(i=>i.default)}),Sb=Object.assign({"../i18n/ar.strings":()=>ee(()=>import("./ar-AgbDXJ1P.js"),[],import.meta.url).then(i=>i.default),"../i18n/da.strings":()=>ee(()=>import("./da-DCJmT-QF.js"),[],import.meta.url).then(i=>i.default),"../i18n/de.strings":()=>ee(()=>import("./de-Bw3STH-g.js"),[],import.meta.url).then(i=>i.default),"../i18n/el.strings":()=>ee(()=>import("./el-MJ37lw3F.js"),[],import.meta.url).then(i=>i.default),"../i18n/en.strings":()=>ee(()=>import("./en-gIMn8Pa3.js"),[],import.meta.url).then(i=>i.default),"../i18n/es.strings":()=>ee(()=>import("./es-or5dA7qu.js"),[],import.meta.url).then(i=>i.default),"../i18n/fr.strings":()=>ee(()=>import("./fr-D5Wy8Mcd.js"),[],import.meta.url).then(i=>i.default),"../i18n/he.strings":()=>ee(()=>import("./he-CYY5d1yw.js"),[],import.meta.url).then(i=>i.default),"../i18n/it.strings":()=>ee(()=>import("./it-B0g-vqj6.js"),[],import.meta.url).then(i=>i.default),"../i18n/ja.strings":()=>ee(()=>import("./ja-BNDo5HzU.js"),[],import.meta.url).then(i=>i.default),"../i18n/ko.strings":()=>ee(()=>import("./ko-D37jf4Vi.js"),[],import.meta.url).then(i=>i.default),"../i18n/nb.strings":()=>ee(()=>import("./nb-CsT7SL7q.js"),[],import.meta.url).then(i=>i.default),"../i18n/nl.strings":()=>ee(()=>import("./nl-CPGJmQ49.js"),[],import.meta.url).then(i=>i.default),"../i18n/pl.strings":()=>ee(()=>import("./pl-DDkdCN00.js"),[],import.meta.url).then(i=>i.default),"../i18n/pt-PT.strings":()=>ee(()=>import("./pt-PT-aHTlqnII.js"),[],import.meta.url).then(i=>i.default),"../i18n/ru.strings":()=>ee(()=>import("./ru-bst4sg1r.js"),[],import.meta.url).then(i=>i.default),"../i18n/sv.strings":()=>ee(()=>import("./sv-BZ9E37oq.js"),[],import.meta.url).then(i=>i.default),"../i18n/tr.strings":()=>ee(()=>import("./tr-C3W_u6Ol.js"),[],import.meta.url).then(i=>i.default),"../i18n/uk.strings":()=>ee(()=>import("./uk-D0HDyCtp.js"),[],import.meta.url).then(i=>i.default),"../i18n/zh-Hans.strings":()=>ee(()=>import("./zh-Hans-R_GuNvYU.js"),[],import.meta.url).then(i=>i.default),"../i18n/zh-Hant.strings":()=>ee(()=>import("./zh-Hant-DoE9BrE1.js"),[],import.meta.url).then(i=>i.default)});function ms(i,e){var t;return(t=Object.entries(i).find(([n])=>n.endsWith(e)))==null?void 0:t[1]}function ag(i){const e=new Map,t=i.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,""),n=/"((?:[^"\\]|\\.)*)"\s*=\s*"((?:[^"\\]|\\.)*)"\s*;/g,a=s=>s.replace(/\\(u[0-9a-fA-F]{4}|.)/g,(r,o)=>o==="n"?` -`:o==="t"?" ":o.startsWith("u")&&o.length===5?String.fromCharCode(parseInt(o.slice(1),16)):o);for(const s of t.matchAll(n))e.set(a(s[1]),a(s[2]));return e}async function Ko(i){const e=i==="java"?ms(ig,"/Java.strings"):ms(kb,`/${i}.lproj/Localizable.strings`),t=ms(Sb,`/${i}.strings`),n=new Map;for(const a of[e,t])if(a)for(const[s,r]of ag(await a()))n.set(s,r);return n}let sg="en",rg=new Map,s1=new Map;const As="appLanguageCode",k0="visustruct.keywords";let Xo=new Map,S0=!1;const Tb=i=>i.startsWith("element_")||i.startsWith("canvas_stub_");function _b(){try{const i=new URLSearchParams(location.search).get("lang")==="java"||localStorage.getItem(As)==="java";return i&&(localStorage.setItem(As,"en"),localStorage.setItem(k0,"1")),i}catch{return!1}}function Ab(){try{return localStorage.getItem(k0)==="1"}catch{return!1}}async function og(i,e=!0){if(i&&Xo.size===0){s1.size===0&&(s1=await Ko("en"));const t=ms(ig,"/Java.strings");if(t)for(const[n,a]of ag(await t()))Tb(n)&&a!==s1.get(n)&&Xo.set(n,a)}if(S0=i,e)try{localStorage.setItem(k0,i?"1":"0")}catch{}}function jb(){return S0}function Cb(){if(_b())return"en";const i=new URLSearchParams(location.search).get("lang");if(i&&Xu(i))return i;try{const e=localStorage.getItem(As);if(e&&Xu(e))return e}catch{}for(const e of navigator.languages??[navigator.language]){const t=e.toLowerCase();if(t.startsWith("pt"))return"pt-PT";const n=ng.find(a=>a.code===t.split("-")[0]);if(n)return n.code}return"en"}async function lg(i,e=!0){if(s1.size===0&&(s1=await Ko("en")),rg=i==="en"?s1:await Ko(i),sg=i,e)try{localStorage.setItem(As,i)}catch{}const t=document.documentElement;t.lang=i==="java"?"en":i,t.dir=i==="ar"||i==="he"?"rtl":"ltr"}function In(){return sg}function p(i,...e){const t=(S0?Xo.get(i):void 0)??rg.get(i)??s1.get(i)??i;if(e.length===0)return t;let n=0;return t.replace(/%(?:(\d+)\$)?[@d]/g,(a,s)=>String(e[s?Number(s)-1:n++]??""))}const js=243,Db=244,ug=40,Ur=4,Bb=1;function T0(i){const e=i.length,t=js+(e&255)+(e>>8)&255,n=i.reduce((a,s)=>a+s,0)&255;return Uint8Array.from([js,t,e&255,e>>8,...i,n,Db])}function Pb(i,e,t){const n=Array.from(new TextEncoder().encode(i));return T0([ug,e,t&255,t>>8&255,n.length&255,n.length>>8,...n])}function Ob(i){if(i[0]!==ug||i.length<6)return;const e=new TextDecoder().decode(Uint8Array.from(i.slice(6))),t=/"ret"\s*:\s*([^}]*)\}/.exec(e);return t?{idx:i[2]|i[3]<<8,value:Zb(t[1].trim())}:void 0}function Mb(i){const e=[];for(;;){const t=i.indexOf(js);if(t<0){i.length=0;break}if(i.splice(0,t),i.length<4)break;const n=i[2]|i[3]<<8;if(i[1]!==(js+i[2]+i[3]&255)||n>4096){i.splice(0,1);continue}if(i.lengthArray.from({length:e},(t,n)=>i>>>8*n&255);function Nb(i){let e=0;for(let t=0;t>>0;return zi(e,4)}const Qr=(i,e)=>T0([1,0,mg,i,...zi(e.length,2),...e]);function Fb(i,e=Lb){const t=new TextEncoder().encode(i),n=(4-t.length%ii+ii)%ii,a=new Uint8Array(t.length+n).fill(10);a.set(t);const s=[Qr(1,[0,...zi(a.length,4),...Nb(a),...Array.from(new TextEncoder().encode(e))])],r=a.length-4;for(let o=0;oi.length===3&&i[0]===13,Jb=i=>i.length===7||i.some((e,t)=>e===mg&&i[t+1]===240),cg=()=>typeof navigator>"u"?void 0:navigator.serial,$r=()=>cg()!==void 0,Gb=(...i)=>`exec(${JSON.stringify(i.join("; "))})`,yr=(i,e=24)=>Gb("cyberpi.display.clear()",`cyberpi.display.show_label(${JSON.stringify(i)}, ${e}, 'center')`),dg=yr("VisuBot",32);class Hb{constructor(){y(this,"port");y(this,"writer");y(this,"reader");y(this,"loop");y(this,"rx",[]);y(this,"idx",1);y(this,"frameWaiters",[]);y(this,"waiting",new Map);y(this,"queue",Promise.resolve());y(this,"listeners",new Set);y(this,"error");y(this,"connecting",!1)}listen(e){return this.listeners.add(e),()=>this.listeners.delete(e)}changed(){for(const e of this.listeners)e()}get connected(){return this.writer!==void 0&&!this.connecting}async connect(){const e=cg();if(!e)throw new Error("Web Serial");const t=await e.requestPort();await t.open({baudRate:115200}),this.port=t,this.writer=t.writable.getWriter(),this.error=void 0,this.connecting=!0,this.changed(),this.loop=this.readLoop(),window.addEventListener("pagehide",()=>void this.disconnect(),{once:!0}),await new Promise(n=>setTimeout(n,3e3)),this.writer&&(this.rx.length=0,this.connecting=!1,this.changed(),await this.run(dg))}async disconnect(e){if(!this.port)return this.error=e,this.changed();if(!e&&this.writer&&!this.connecting)for(const r of["mbot2.EM_stop('ALL')","cyberpi.led.off()",yr("Bis bald!")])await this.send(r,Ur,2e3).catch(()=>{});const t=this.port,n=this.writer,a=this.reader,s=this.loop;this.port=this.writer=this.reader=this.loop=void 0,this.error=e,this.connecting=!1;for(const r of this.waiting.values())r.reject(new Error(e??"getrennt"));this.waiting.clear();try{await(a==null?void 0:a.cancel()),await s,await(n==null?void 0:n.close())}catch{}try{n==null||n.releaseLock(),await t.close()}catch(r){console.warn("[mBot2] Schließen",r.message)}this.changed()}run(e){return this.send(e,Ur,3e4).then(()=>{})}ask(e){return this.send(e,Bb,3e3)}async upload(e,t=()=>{}){if(this.port||await this.connect(),!this.writer)throw new Error(this.error??"nicht verbunden");const n=Fb(e),a=6+n.length+3;let s=0;const r=()=>t(++s,a),o=async()=>{await this.sendFrame(T0(Wb),Rb),r();for(const l of Eb)await this.send(l,Ur,3e3),r()};await o(),await o();for(const l of n)await this.sendFrame(l,Jb),r();s=a-3,await o(),await this.restart()}sendFrame(e,t){const n=this.queue.then(async()=>{for(let a=0;a<3;a++){if(!this.writer)throw new Error("nicht verbunden");let s=()=>{};const r=new Promise(u=>s=u),o={match:t,resolve:s};this.frameWaiters.push(o),await this.writer.write(e);const l=await Promise.race([r.then(()=>!0),new Promise(u=>setTimeout(()=>u(!1),3e3))]);if(this.frameWaiters=this.frameWaiters.filter(u=>u!==o),l)return}throw new Error("keine Antwort")});return this.queue=n.catch(()=>{}),n}async restart(){const e=this.port;await this.disconnect("restart"),this.error=void 0,await new Promise(t=>setTimeout(t,300)),await e.open({baudRate:115200}),await new Promise(t=>setTimeout(t,500)),await e.close(),this.changed()}send(e,t,n){const a=this.queue.then(()=>new Promise((s,r)=>{if(!this.writer)return r(new Error("nicht verbunden"));const o=this.idx;this.idx=this.idx%65535+1;const l=setTimeout(()=>{this.waiting.delete(o),r(new Error("keine Antwort"))},n);this.waiting.set(o,{resolve:u=>(clearTimeout(l),s(u)),reject:u=>(clearTimeout(l),r(u))}),this.writer.write(Pb(e,t,o)).catch(u=>{this.waiting.delete(o),r(u)})}));return this.queue=a.catch(()=>{}),a}async readLoop(){var e;for(;(e=this.port)!=null&&e.readable;){const t=this.port.readable.getReader();this.reader=t;try{for(;;){const{value:n,done:a}=await t.read();if(a)break;this.rx.push(...n);for(const s of Mb(this.rx)){const r=this.frameWaiters.findIndex(u=>u.match(s));r>=0&&this.frameWaiters.splice(r,1)[0].resolve();const o=Ob(s);if(!o)continue;const l=this.waiting.get(o.idx);this.waiting.delete(o.idx),l==null||l.resolve(o.value)}}}catch(n){const a=["BufferOverrunError","FramingError","BreakError","ParityError"].includes(n.name);console.warn("[mBot2] Lesefehler",n.name,n.message),!a&&this.port&&this.disconnect(n.message)}finally{t.releaseLock()}if(!this.writer)break}}}const be=new Hb,hg=5,Vb=i=>Number.isInteger(i)&&i>=0&&iString(Math.round(i*100)/100),el=6.5,v1=Math.PI*el,Ub={cm:{cm:1,one:"cm",many:"cm"},umdrehungen:{cm:v1,one:"Umdrehung",many:"Umdrehungen"},umdrehung:{cm:v1,one:"Umdrehung",many:"Umdrehungen"},rotations:{cm:v1,one:"rotation",many:"rotations"},rotation:{cm:v1,one:"rotation",many:"rotations"},rotações:{cm:v1,one:"rotação",many:"rotações"},rotação:{cm:v1,one:"rotação",many:"rotações"}},Qb=["cm","Umdrehungen","rotations"];function $b(i,e,t){const n=Ub[t.trim().toLowerCase()];if(!n)return;const a=e*n.cm,s=tl(i,[a]);if(n.cm===1)return s;const r=e===1?n.one:n.many;return{...s,label:`${Ie(e)} ${r} ${i==="forward"?"vorwärts":"rückwärts"} (${Ie(a)} cm)`}}const Li={forward:{args:1,defaults:[em],python:([i])=>`mbot2.straight(${Ie(i)})`,label:([i])=>`${Ie(i)} cm vorwärts`},backward:{args:1,defaults:[em],python:([i])=>`mbot2.straight(${Ie(-i)})`,label:([i])=>`${Ie(i)} cm rückwärts`},turnLeft:{args:1,defaults:[90],python:([i])=>`mbot2.turn(${Ie(-i)})`,label:([i])=>`${Ie(i)}° nach links drehen`},turnRight:{args:1,defaults:[90],python:([i])=>`mbot2.turn(${Ie(i)})`,label:([i])=>`${Ie(i)}° nach rechts drehen`},stop:{args:0,python:()=>'mbot2.EM_stop("ALL")',label:()=>"anhalten"},drive:{args:2,python:([i,e])=>`mbot2.drive_power(${Ie(i)}, ${Ie(-e)})`,label:([i,e])=>`Räder links ${Ie(i)} %, rechts ${Ie(e)} %`},setLed:{args:3,python:([i,e,t])=>`cyberpi.led.on(${Ie(i)}, ${Ie(e)}, ${Ie(t)})`,label:([i,e,t])=>`LEDs (${Ie(i)}, ${Ie(e)}, ${Ie(t)})`},ledOff:{args:0,python:()=>"cyberpi.led.off()",label:()=>"LEDs aus"},ledColor:{args:1,text:!0,choices:Object.keys(Cs),valid:([i])=>Vb(i),python:([i],e)=>`cyberpi.led.on(${Cs[e].join(", ")}, id=${i+1})`,label:([i],e)=>`LED ${i}: ${e}`},playTone:{args:2,python:([i,e])=>`cyberpi.audio.play_tone(${Ie(i)}, ${Ie(e/1e3)})`,label:([i,e])=>`Ton ${Ie(i)} Hz, ${Ie(e)} ms`},pause:{args:1,label:([i])=>`${Ie(i)} ms warten`},resetTimer:{args:0,python:()=>"cyberpi.timer.reset()",label:()=>"Stoppuhr auf 0"},emotion:{args:0,text:!0,choices:qb,python:(i,e)=>`cyberpi.emotion.${e}()`,label:(i,e)=>`Gefühl: ${e}`},showText:{args:0,text:!0,python:(i,e)=>yr(e),label:(i,e)=>`Bildschirm: ${e}`}},tm={red:{de:"rot",en:"red"},green:{de:"grün",en:"green"},blue:{de:"blau",en:"blue"},yellow:{de:"gelb",en:"yellow"},cyan:{de:"türkis",en:"cyan"},purple:{de:"lila",en:"purple"},white:{de:"weiß",en:"white"},black:{de:"schwarz",en:"black"}};let _0="en";function Yb(i){_0=i==="de"?"de":"en"}const Kb=()=>_0;function gg(i){const e=i.trim().toLowerCase(),t=tm[e]??Object.values(tm).find(n=>n.de===e);return t?t[_0]:e}const w1=i=>({python:`cyberpi.controller.is_press('${i}')`,simulated:!1}),Bt={getDistance:{python:"mbuild.ultrasonic2.get(1)",simulated:100},getLine:{python:"mbuild.quad_rgb_sensor.get_line_sta('all', 1)",simulated:0},isLineLeft:{python:"mbuild.quad_rgb_sensor.is_line('L1', 1)",simulated:!1},isLineRight:{python:"mbuild.quad_rgb_sensor.is_line('R1', 1)",simulated:!1},getColor:{python:"mbuild.quad_rgb_sensor.get_color_sta('L1', 1)",simulated:"white"},isButtonA:w1("a"),isButtonB:w1("b"),isJoystickUp:w1("up"),isJoystickDown:w1("down"),isJoystickLeft:w1("left"),isJoystickRight:w1("right"),getBrightness:{python:"cyberpi.get_bri()",simulated:50},getLoudness:{python:"cyberpi.get_loudness('maximum')",simulated:0},getPitch:{python:"cyberpi.get_pitch()",simulated:0},getRoll:{python:"cyberpi.get_roll()",simulated:0},isShaken:{python:"cyberpi.is_shake()",simulated:!1},getBattery:{python:"cyberpi.get_battery()",simulated:100},getTimer:{python:"cyberpi.timer.get()",simulated:0}};function Xb(i){const e=new Set;for(const t of i.matchAll(/\bmbot\s*\.\s*([A-Za-z_]\w*)\s*\(\s*\)/gu))t[1]in Bt&&e.add(t[1]);return[...e]}function ex(i){const e=/^mbot\s*\.\s*led\s*\(([^()]*(?:\([^()]*\)[^()]*)*)\)\s*\.\s*setColor\s*\(([\s\S]*)\)\s*;?$/u.exec(i.trim());if(e)return{method:"ledColor",args:[e[1].trim(),e[2].trim()]};const t=/^mbot\s*\.\s*([A-Za-z_]\w*)\s*\(([\s\S]*)\)\s*;?$/u.exec(i.trim());if(!t)return;const n=t[2].trim();return{method:t[1],args:n===""?[]:ix(n)}}function Ds(i,e){const t=Li[i];if(!t)return;const n=e.length===0&&t.defaults?t.defaults:e;if(n.length===t.args)return!t.valid||t.valid(n)?n:void 0}function tl(i,e,t=""){var a;const n=Li[i];return{method:i,args:e,python:(a=n.python)==null?void 0:a.call(n,e,t),waitMs:i==="pause"?Math.max(0,e[0]):void 0,label:n.label(e,t),...n.text?{text:t}:{}}}function pg(i){return i.map(e=>[e.method,...e.args.map(Ie),...e.text!==void 0?[JSON.stringify(e.text)]:[]].join(" "))}function tx(i){let e=0;const t=i.trim(),n=()=>{const s=[];for(;;){for(;t[e]===" "||t[e]===",";)e++;if(e>=t.length||t[e]===")")return s;const r=/^(\d+)\s*[x×]\s*\(/u.exec(t.slice(e));if(r){e+=r[0].length;const c=n();if(t[e]!==")")throw new Error(`„)“ fehlt in „${i}“`);e++;for(let d=0;d{if(!(n in Bt))return t;const a=e(n);return typeof a=="string"?JSON.stringify(n==="getColor"?gg(a):a):String(a)}):i}function ix(i){const e=[];let t=0,n=!1,a=0;for(let s=0;s{const a=t==="turn"?((n+180)%360+360)%360-180:n;if(Math.abs(a)<1e-6)return[];const s=Math.round(Math.abs(a)*100)/100;return t==="drive"?[`${a>0?"forward":"backward"} ${s}`]:[a===-180?"turnRight 180":`${a>0?"turnRight":"turnLeft"} ${s}`]})}function fg(i){let e=0;const t=i.trim(),n=()=>{const s=[];for(;;){for(;t[e]===" "||t[e]===",";)e++;if(e>=t.length||t[e]===")")return s;const r=/^(\d+)\s*[x×]\s*\(/u.exec(t.slice(e));if(r){e+=r[0].length;const u=n();if(t[e]!==")")throw new Error(`„)“ fehlt in „${i}“`);e++;for(let m=0;ma===i[s%t])){const a=pi(i.slice(0,t));return`${n} x ${t===1?a:`(${a})`}`}}return i.join(", ")}const bg=["cyberpi","screen","joystick","buttons","leds","lightSensor","microphone","shield","powerSwitch","motors","wheels","ultrasonic","lineSensor","caster","chassis"],ax=i=>bg.includes(i),sx=100;function xg(i){switch(i.kind){case"finished":return"Simulation finished.";case"infiniteLoopWithoutBody":return"Infinite loop has no body.";case"unsupportedStatement":return`Unsupported statement: ${i.text}`;case"invalidExpression":return`Invalid expression: ${i.text}`;case"unknownVariable":return i.similar?`Unknown variable: ${i.text}. Java is case-sensitive: did you mean ${i.similar}?`:`Unknown variable: ${i.text}`;case"divisionByZero":return"Division by zero.";case"wholeNumberExpected":return"Please enter a whole number.";case"numberExpected":return"Please enter a number.";case"booleanExpected":return"Please enter true or false.";case"subprogramNotFound":return`No open diagram is called ${i.text}. The call was skipped.`;case"callTooDeep":return"Too many nested calls. Does a subprogram call itself?";case"callArguments":return`The call does not match the header of ${i.text}. Check the values in the parentheses.`;case"missingReturn":return`${i.text} ended without returning a value with return.`;case"fileNotFound":return`There is no file called ${i.text}.`;case"endOfFile":return`The file ${i.text} has no more lines.`;case"nullValue":return`${i.text} is null.`;case"other":return i.text}}class He extends Error{constructor(e){super(xg(e)),this.notice=e}}const tt=i=>new He({kind:"unsupportedStatement",text:i}),G=i=>new He({kind:"invalidExpression",text:i}),_1=(i,e)=>{const t=e&&[...e.keys()].find(n=>n!==i&&n.toLowerCase()===i.toLowerCase());return new He(t?{kind:"unknownVariable",text:i,similar:t}:{kind:"unknownVariable",text:i})},rx=()=>new He({kind:"divisionByZero"}),Ps=new Map;function ox(i,e){Ps.set(i,e)}const vg=()=>[...Ps];function nm(i){return i instanceof He?i.notice:{kind:"other",text:i instanceof Error?i.message:String(i)}}function lx(i){if(i===0)return"0";if(!Number.isFinite(i))return Number.isNaN(i)?"nan":i>0?"inf":"-inf";const e=Math.floor(Math.log10(Math.abs(Number(i.toPrecision(12)))));if(e<-4||e>=12){const[n,a]=i.toExponential(11).split("e"),s=n.includes(".")?n.replace(/0+$/,"").replace(/\.$/,""):n,r=Number(a);return`${s}e${r<0?"-":"+"}${String(Math.abs(r)).padStart(2,"0")}`}const t=i.toFixed(Math.max(0,11-e));return t.includes(".")?t.replace(/0+$/,"").replace(/\.$/,""):t}function im(i){return Number.isFinite(i)&&Math.round(i)===i?i.toFixed(1):lx(i)}function _e(i){switch(i.kind){case"int":return String(i.value);case"double":return im(i.value);case"string":return i.value;case"boolean":return String(i.value);case"intArray":return"["+i.value.map(String).join(", ")+"]";case"doubleArray":return"["+i.value.map(im).join(", ")+"]";case"stringArray":return"["+i.value.join(", ")+"]";case"booleanArray":return"["+i.value.map(String).join(", ")+"]";case"grid":return"["+ux(i).join(", ")+"]";case"reader":return`${i.className}(${i.file})`;case"writer":return`PrintWriter(${i.file})`;case"null":return"null"}}function ux(i){return i.value.map(e=>"["+e.map(t=>_e(nl(i.elementType,t))).join(", ")+"]")}function nl(i,e){switch(i){case"int":return{kind:"int",value:e};case"double":return{kind:"double",value:e};case"string":return{kind:"string",value:e};case"boolean":return{kind:"boolean",value:e}}}function am(i){return i==="string"?"":i==="boolean"?!1:0}const Os=new WeakMap;let mx=0;function sm(i){const e=s=>({int:"I",double:"D",boolean:"Z",string:"Ljava.lang.String;"})[s],t=i.kind==="grid"?"[["+e(i.elementType):i.kind==="intArray"?"[I":i.kind==="doubleArray"?"[D":i.kind==="booleanArray"?"[Z":i.kind==="stringArray"?"[Ljava.lang.String;":void 0;if(!t||!("value"in i))return _e(i);const n=i.value;let a=Os.get(n);return a||(a=(Math.imul(++mx,2654435761)>>>0&2147483647).toString(16),Os.set(n,a)),t+"@"+a}function rm(i,e){const t=Os.get(i);t&&Os.set(e,t)}function cs(i){return i.kind==="intArray"||i.kind==="doubleArray"||i.kind==="stringArray"||i.kind==="booleanArray"||i.kind==="grid"}function cx(i){return i.kind==="int"||i.kind==="double"?i.value:NaN}const _t=i=>({kind:"int",value:i}),At=i=>({kind:"double",value:i}),A0=i=>i!==void 0&&/\s/u.test(i),Ms=i=>new RegExp("\\p{L}","u").test(i),fi=i=>new RegExp("\\p{N}","u").test(i);function Ii(i){const e=Array.from(i);return e.length===0||!(Ms(e[0])||e[0]==="_")?!1:e.every(t=>Ms(t)||fi(t)||t==="_")}function nn(i){switch(i.toLowerCase()){case"int":return"int";case"double":return"double";case"string":return"string";case"char":return"string";case"boolean":case"bool":return"boolean";default:return}}function om(i){return i.kind==="int"?"int":i.kind==="double"?"double":i.kind==="boolean"?"boolean":"string"}function ct(i){const e=Array.from(i);if(e.length===3&&e[0]==="'"&&e[2]==="'")return e[1];if(e.length<2||e[0]!=='"'||e[e.length-1]!=='"')return;const t=e.slice(1,-1).join("");if(/^(?:[^"\\]|\\.)*$/u.test(t))return t.replaceAll('\\"','"').replaceAll("\\n",` -`)}function lm(i){return i.replaceAll("(int)","").replaceAll("(double)","").trim()}function j0(i){const e=new Array(i.length).fill(!1);let t=0,n=!1,a=!1;return i.forEach((s,r)=>{if(n){a?a=!1:s==="\\"?a=!0:s==='"'&&(n=!1);return}switch(s){case'"':n=!0;break;case"(":case"[":t+=1;break;case")":case"]":t-=1;break;default:e[r]=t===0}}),e}function Zs(i,e){const t=Array.from(i),n=Array.from(e),a=j0(t),s=[];let r=[],o=0;for(;ot[o+m]===u)&&a.slice(o,l).every(Boolean)){s.push(r.join("").trim()),r=[],o=l;continue}r.push(t[o]),o+=1}return s.push(r.join("").trim()),s}function dx(i){const e=Array.from(i),t=j0(e);for(let n=0;n=","<=","==","!="].includes(o)&&(a=o)}if(a===void 0&&(e[n]==="<"||e[n]===">")&&(a=e[n]),a===void 0)continue;const s=e.slice(0,n).join("").trim(),r=e.slice(n+a.length).join("").trim();return{lhs:s,op:a,rhs:r}}}function zr(i,e){let t=0,n=!1,a=!1;for(let s=e;se.length||!t.every((r,o)=>e[a+o]===r))continue;const s=a+t.length-1;if(zr(e,s)===e.length-1)return{lhs:e.slice(0,a).join("").trim(),rhs:e.slice(s+1,-1).join("").trim()}}}function Ia(i){const e=i.trim();for(const t of["if","while","until","switch"]){if(!e.startsWith(t))continue;const n=e.slice(t.length);if(!(n===""||A0(n[0])||n[0]==="("))continue;let a=n.trim();const s=wg(a);return s!==void 0&&(a=s),a===""?"true":a}return e===""?"true":e}function A1(i){const e=i.trim(),t=e.indexOf("[");if(!e.endsWith("]")||t<0)return;const n=e.slice(0,t).trim();if(!Ii(n))return;const a=[];let s=t;for(;s2))return{arrayName:n,indexExpressions:a}}function gx(i){const e=i.trim();if(!e.endsWith(")"))return;const t=/^([A-Za-z_]\w*(?:\s*\[[^\[\]]*\])*|"(?:[^"\\]|\\.)*")\s*\.\s*([A-Za-z_]\w*)\s*\(/u.exec(e);if(!t)return;const n=Array.from(e),a=Array.from(t[0]).length-1;if(zr(n,a)!==n.length-1)return;const s=n.slice(a+1,-1).join("").trim();return{target:t[1],method:t[2],args:s===""?[]:Zs(s,",")}}function um(i){return Ii(i)||A1(i)!==void 0}function Yr(i){const e=[];let t="",n=!1,a=!1,s=0;for(const r of i){if(a){t+=r,a=!1;continue}if(r==="\\"){t+=r,a=!0;continue}if(r==='"'){n=!n,t+=r;continue}if(!n&&r==="("&&(s+=1),!n&&r===")"&&(s-=1),r==="+"&&!n&&s===0){e.push(t),t="";continue}t+=r}if(n)throw G(i);return e.push(t),e}function px(i){const e=[];let t=0,n="";for(const a of i){if(a==="{"&&(t+=1),a==="}"&&(t-=1),a===","&&t===0){e.push(n),n="";continue}n+=a}return n.trim()!==""&&e.push(n),e}function mm(i){const e=Array.from(i),t=[];let n=0;for(;n1)throw G(i)}else if(!fi(e[n]))break;n+=1}const o=e.slice(s,n).join("");if(o==="."||!/^[0-9]*\.?[0-9]*$/.test(o))throw G(i);t.push({t:"number",literal:o,value:Number(o)});continue}if(Ms(a)||a==="_"){const s=n;for(;n=0&&s=0&&o=0&&s|&&|\|\||^\s*!/u.test(e)}function r1(i){let e=i.trim();for(;e.endsWith(";");)e=e.slice(0,-1).trim();return e}function dm(i){const e=i.slice(0,3);for(;e.length<3;)e.push("");return e}function al(i){const e=i.map(t=>r1(t).trim());return e.length===1&&e[0].includes(";")?dm(e[0].split(";").map(t=>t.trim())):dm(e)}const pt=i=>i.frame?{frame:i.frame,callPath:i.callPath}:{};function Yt(i,e=[],t={}){const n=[],a=s=>n.push(t.frame?{...s,...t}:s);return i.forEach((s,r)=>{const o=[...e,r],l=s.textLines.join(" ").trim();switch(s.type){case S.anweisung:l!==""&&l!=="ø"&&a({path:o,text:l,kind:"statement"});break;case S.verzweigung:a({path:o,text:l||"true",kind:"branch",cases:s.cases});break;case S.fallauswahl:a({path:o,text:l||"0",kind:"switchSelection",cases:s.cases});break;case S.forSchleife:a({path:o,text:l,kind:"forLoopStart",loopBody:s.loopBody,forParts:al(s.textLines)});break;case S.whileSchleife:a({path:o,text:l||"true",kind:"whileLoop",loopBody:s.loopBody});break;case S.doUntilSchleife:a({path:o,text:l||"true",kind:"doUntilLoop",loopBody:s.loopBody});break;case S.endlosschleife:a({path:o,text:l||"true",kind:"infiniteLoop",loopBody:s.loopBody});break;case S.aussprung:a({path:o,text:l,kind:"jump"});break;case S.aufruf:l!==""&&l!=="ø"&&a({path:o,text:l,kind:"call"});break}}),n}function bx(i,e){const t=i.trim().replaceAll(",",".");switch(e){case"int":if(!/^[+-]?[0-9]+$/.test(t))throw new He({kind:"wholeNumberExpected"});return _t(Number(t));case"double":if(!/^[+-]?([0-9]+\.?[0-9]*|\.[0-9]+)([eE][+-]?[0-9]+)?$/.test(t))throw new He({kind:"numberExpected"});return At(Number(t));case"string":return{kind:"string",value:i};case"boolean":switch(t.toLowerCase()){case"true":case"wahr":return{kind:"boolean",value:!0};case"false":case"falsch":return{kind:"boolean",value:!1};default:throw new He({kind:"booleanExpected"})}}}function hm(){return{stepIndex:0,variables:new Map,outputLines:[],isHalted:!1,files:new Map,robotActions:[]}}function xx(i,e){for(let t=0;tt===e.path[n])}function Tt(i){const e=/^(void|int|double|String|boolean)\s+([A-Za-z_]\w*)\s*\((.*)\)$/u.exec(r1(i));if(!e)return;const t=[];if(e[3].trim()!=="")for(const n of e[3].split(",")){const a=/^(int|double|String|boolean|char)\s*((?:\[\s*\]){0,2})\s+([A-Za-z_]\w*)$/u.exec(n.trim());if(!a)return;const s=(a[2].match(/\[/gu)??[]).length;t.push({type:nn(a[1]),name:a[3],...s>0?{dims:s}:{}})}return{returnType:nn(e[1]),name:e[2],params:t}}function yg(i){const e=/^(?:(?:(int|double|String|boolean)\s+)?([A-Za-z_]\w*)\s*=\s*)?([A-Za-z_]\w*)\s*\((.*)\)$/u.exec(r1(i));if(e)return{targetType:e[1]?nn(e[1]):void 0,target:e[2],name:e[3],args:e[4].trim()===""?[]:Zs(e[4],",")}}function Ls(i){var t,n;const e=r1(i);return((t=Tt(e))==null?void 0:t.name)??((n=yg(e))==null?void 0:n.name)??e.replace(/\(\s*\)$/u,"").trim()}class Wi{constructor(e,t=()=>{}){y(this,"steps");y(this,"state",hm());y(this,"nextFrame",1);y(this,"callDepth",0);y(this,"frames",new Map);y(this,"peeking",!1);y(this,"robotSensors",{});y(this,"pseudocode");y(this,"decided");y(this,"afterOutput",!1);this.resolveSubprogram=t,this.steps=Yt(e.elements),this.pseudocode=!!e.pseudocode}get currentStepPath(){const e=this.steps[this.state.stepIndex];return(e==null?void 0:e.callPath)??(e==null?void 0:e.path)}get canStep(){return!this.state.isHalted&&!this.state.inputRequest&&this.state.stepIndex ${_e(n)}`}catch(n){this.state.inputErrorNotice=nm(n);return}this.state.inputRequest=void 0,this.state.inputErrorNotice=void 0,this.state.stepIndex+=1,this.state.notice=this.finishedIfAtEnd()}}provideDecision(e){var s;if(((s=this.state.inputRequest)==null?void 0:s.valueType)==="string")return this.state.inputRequest=void 0,this.decided=e.trim(),this.step();const t=e.trim().toLowerCase(),n=["true","wahr","ja","yes","sim","verdadeiro"].includes(t),a=["false","falsch","nein","no","não","nao","falso"].includes(t);if(!n&&!a){this.state.inputErrorNotice={kind:"booleanExpected"};return}this.state.inputRequest=void 0,this.state.inputErrorNotice=void 0,this.decided=n,this.step()}condition(e){if(!this.pseudocode)return this.evaluateBool(e);const t=this.decided;if(typeof t=="boolean")return this.decided=void 0,t;this.state.inputRequest={variableName:"",valueType:"boolean",prompt:`${e} (true/false)`,decision:!0},this.state.inputErrorNotice=void 0,this.state.notice=void 0}finishedIfAtEnd(){return this.state.stepIndex>=this.steps.length?{kind:"finished"}:void 0}splice(e){this.steps.splice(this.state.stepIndex,1,...e)}noticeAfterEntering(e){return e.length>0?void 0:this.finishedIfAtEnd()}executeBranch(e){const t=e.cases;if(!t||t.length<2)throw tt(e.text);const n=Ia(e.text),a=this.condition(n);if(a===void 0)return;const s=a?0:1,r=Yt(t[s].elements,[...e.path,s],pt(e));this.splice(r),this.state.lastTrace=`${n} -> ${a}`,this.state.notice=this.noticeAfterEntering(r)}executeSwitchSelection(e){const t=e.cases;if(!t||t.length===0)throw tt(e.text);const n=Ia(e.text);let a;if(this.pseudocode){const r=this.decided;if(typeof r!="string"){const l=t.map(u=>u.name).join(" / ");this.state.inputRequest={variableName:"",valueType:"string",prompt:`${n} (${l})`,decision:!0},this.state.notice=void 0;return}this.decided=void 0;const o=t.findIndex(l=>l.name.trim().toLowerCase()===r.toLowerCase());a=o>=0?o:t.length-1}else a=xx(this.evaluateSwitchSelector(n),t)??t.length-1;const s=Yt(t[a].elements,[...e.path,a],pt(e));this.splice(s),this.state.lastTrace=`${n} -> ${t[a].name}`,this.state.notice=this.noticeAfterEntering(s)}executeForLoopStart(e){if(this.pseudocode)return this.splice([{...e,kind:"whileLoop"}]);const t=al(e.forParts??[e.text]);t[0]!==""&&this.executeStatement(t[0]),this.splice([{...pt(e),path:e.path,text:t[1],kind:"forLoopCondition",loopBody:e.loopBody,forParts:t}])}executeForLoopCondition(e){const t=al(e.forParts??[e.text]),n=t[1]===""?"true":t[1],a=this.condition(n);if(a!==void 0)if(this.state.lastTrace=`${n} -> ${a}`,a){const s=Yt(e.loopBody??[],[...e.path,0],pt(e));this.splice([...s,{...pt(e),path:e.path,text:t[2],kind:"forLoopIncrement",loopBody:e.loopBody,forParts:t},{...pt(e),path:e.path,text:t[1],kind:"forLoopCondition",loopBody:e.loopBody,forParts:t}])}else this.splice([]),this.state.notice=this.finishedIfAtEnd()}executeForLoopIncrement(e){const t=e.text.trim();t!==""&&this.executeIncrement(t),this.splice([])}executeWhileLoop(e){const t=Ia(e.text),n=this.condition(t);if(n!==void 0)if(this.state.lastTrace=`${t} -> ${n}`,n){const a=Yt(e.loopBody??[],[...e.path,0],pt(e));this.splice([...a,{...pt(e),path:e.path,text:e.text,kind:"whileLoop",loopBody:e.loopBody}]),this.state.notice=this.noticeAfterEntering(a)}else this.splice([]),this.state.notice=this.finishedIfAtEnd()}enterDoUntilLoop(e){const t=Yt(e.loopBody??[],[...e.path,0],pt(e));this.splice([...t,{...pt(e),path:e.path,text:e.text,kind:"doUntilCondition",loopBody:e.loopBody}]),this.state.notice=this.noticeAfterEntering(t)}executeDoUntilCondition(e){const t=Ia(e.text),n=this.condition(t);if(n!==void 0)if(this.state.lastTrace=`${t} -> ${n}`,n){const a=Yt(e.loopBody??[],[...e.path,0],pt(e));this.splice([...a,{...pt(e),path:e.path,text:e.text,kind:"doUntilCondition",loopBody:e.loopBody}])}else this.splice([]),this.state.notice=this.finishedIfAtEnd()}enterInfiniteLoop(e){const t=Yt(e.loopBody??[],[...e.path,0],pt(e));this.splice([...t,{...pt(e),path:e.path,text:e.text,kind:"infiniteLoop",loopBody:e.loopBody}]),this.state.lastTrace="true -> true",this.state.notice=t.length>0?void 0:{kind:"infiniteLoopWithoutBody"}}executeJump(e){const t=e.frame??0,n=o=>o.lengthl===e.path[u]);let a=this.steps.length,s="end";for(let o=this.state.stepIndex+1;o ${s}`,s==="return"&&this.finishFrame(r),this.state.notice=this.finishedIfAtEnd()}executeCall(e){const t=e.text,n=this.resolveSubprogram(Ls(t));if(!n){this.splice([]),this.state.lastTrace=`${t} -> ?`,this.state.notice={kind:"subprogramNotFound",text:t},this.state.stepIndex>=this.steps.length&&(this.state.notice={kind:"finished"});return}const a=yg(t),s=Tt(n.caption);if(s?((a==null?void 0:a.args.length)??0)!==s.params.length||(a==null?void 0:a.target)!==void 0&&!s.returnType:a!==void 0&&(a.args.length>0||a.target!==void 0))throw new He({kind:"callArguments",text:(s==null?void 0:s.name)??Ls(t)});if(this.callDepth>=sx)throw new He({kind:"callTooDeep"});const o=new Map,l=[];s==null||s.params.forEach((d,h)=>{const g=a.args[h].trim();if(!d.dims){o.set(d.name,this.evaluateExpression(g,d.type));return}const f=this.state.variables.get(g);if(!f)throw/^[A-Za-z_]\w*$/u.test(g)?_1(g,this.state.variables):G(g);if(!(d.dims===1?f.kind===`${d.type}Array`:f.kind==="grid"&&f.elementType===d.type))throw G(g);o.set(d.name,f),l.push({param:d.name,callerName:g})}),this.callDepth+=1;const u={frame:this.nextFrame++,callPath:e.callPath??e.path};s&&(this.frames.set(u.frame,{saved:this.state.variables,header:s,call:a,references:l}),this.state.variables=o);const m=Yt(n.elements,[],u);this.splice([...m,{...u,path:e.path,text:t,kind:"callEnd"}]);const c=s==null?void 0:s.params.map((d,h)=>`${d.name} = ${d.dims?a.args[h].trim():_e(o.get(d.name))}`).join(", ");this.state.lastTrace=c?`${t} -> ${c}`:`${t} -> call`}executeCallEnd(e){this.callDepth-=1,this.splice([]),this.state.lastTrace=`${e.text} -> return`,this.finishFrame(e),this.state.notice=this.finishedIfAtEnd()}executeReturn(e){const t=e.frame??0,n=r1(e.text).slice(6).trim(),a=this.frames.get(t);if(a&&n!==""){if(!a.header.returnType)throw G(e.text);a.returnValue=this.evaluateExpression(n,a.header.returnType)}const s=a!=null&&a.returnValue?`${e.text} -> ${_e(a.returnValue)}`:`${e.text} -> return`;if(t===0){this.steps.splice(this.state.stepIndex),this.state.lastTrace=s,this.state.notice={kind:"finished"};return}let r=this.state.stepIndex+1;for(;r0?new Map(t.saved):t.saved;for(const m of t.references){const c=this.state.variables.get(m.param);c&&n.set(m.callerName,c)}this.state.variables=n;const a=t.returnValue;if(t.header.returnType&&!a)throw new He({kind:"missingReturn",text:t.header.name});const s=(l=t.call)==null?void 0:l.target;if(!s||!a)return;let r=(u=t.call)==null?void 0:u.targetType;if(!r)try{r=this.inferTargetValueType(s)}catch{r=t.header.returnType}const o=r==="double"&&a.kind==="int"?At(a.value):r==="string"?{kind:"string",value:_e(a)}:a;if(om(o)!==r)throw G(e.text);this.assignValue(o,s),this.state.lastTrace=`${s} = ${e.text.slice(e.text.indexOf("=")+1).trim()} -> ${_e(o)}`}executeStatement(e){const t=this.state,n=e.trim(),a=n.toLowerCase(),s=this.afterOutput;if(this.afterOutput=!1,a.startsWith("input:")){t.inputRequest=this.parseInputRequest(n,s),t.outputLineOpen=!1,t.inputErrorNotice=void 0;return}const r=/^System\s*\.\s*out\s*\.\s*(println|print)\s*\(([\s\S]*)\)\s*;?$/u.exec(n);if(a.startsWith("output:")||a.startsWith("print:")||r){const v=r?r[2]:n.slice(a.startsWith("output:")?7:6),w=this.peek(()=>this.renderOutputExpression(v)),z=v.trim()===""?"":this.evaluateOutputExpression(v),I=(r==null?void 0:r[1])==="print",T=I&&z.endsWith(` -`),[k,...C]=(T?z.slice(0,-1):z).split(` -`);t.outputLineOpen&&t.outputLines.length?t.outputLines[t.outputLines.length-1]+=k:t.outputLines.push(k),t.outputLines.push(...C),t.outputLineOpen=I&&!T,this.afterOutput=!0,t.lastTrace=`${w} -> ${z}`;return}const o=ex(n);if(o){this.executeMBotCommand(o.method,o.args,n);return}if(this.executeFileStatement(n))return;if(n.endsWith("++")||n.endsWith("--")||n.includes("+=")||n.includes("-=")){this.executeIncrement(n);return}const l=/^(?:final\s+)?([A-Za-z]+)\s+([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)$/u.exec(r1(n).trim()),u=l?nn(l[1]):void 0;if(l&&u){const v=l[2].split(",").map(w=>w.trim());for(const w of v)t.variables.set(w,nl(u,am(u)));t.lastTrace=v.map(w=>`${w} = ${_e(t.variables.get(w))}`).join(", ");return}const m=n.indexOf("=");if(m<0)throw tt(n);const c=n.slice(0,m).trim(),d=n.slice(m+1),h=this.parseArrayDeclaration(c,d);if(h){t.variables.set(h.name,h.value),t.lastTrace=`${h.name} = new ${h.typeName}[${h.count}]`;return}const g=this.parseAssignmentTarget(c);let f;try{f=this.peek(()=>this.renderNumericExpression(d))}catch{f=d.trim()}let b=g.valueType;if(!b)try{b=this.inferTargetValueType(g.variableName)}catch{b="int"}const x=this.evaluateExpression(d,b);this.assignValue(x,g.variableName),t.lastTrace=`${g.variableName} = ${f} -> ${_e(x)}`}executeMBotCommand(e,t,n){var r;if((r=Li[e])!=null&&r.text){const o=Li[e];if(t.length!==o.args+1)throw G(n);const l=Ds(e,t.slice(0,-1).map(c=>this.evaluateDouble(c)));if(!l)throw G(n);const u=tl(e,l,this.evaluateOutputExpression(t[t.length-1])),m=o.choices;if(m&&!m.includes(u.text??""))throw G(`${n} (${m.join(", ")})`);this.state.robotActions.push(u),this.state.lastTrace=`${n} -> ${u.label}`;return}if((e==="forward"||e==="backward")&&t.length===2){const o=this.evaluateOutputExpression(t[1]),l=$b(e,this.evaluateDouble(t[0]),o);if(!l)throw G(`${n} (${Qb.map(u=>`"${u}"`).join(", ")})`);this.state.robotActions.push(l),this.state.lastTrace=`${n} -> ${l.label}`;return}const a=Ds(e,t.map(o=>this.evaluateDouble(o)));if(!a)throw G(n);const s=tl(e,a);this.state.robotActions.push(s),this.state.lastTrace=`${n} -> ${s.label}`}executeFileStatement(e){const t=this.state,n=r1(e).trim(),a=/^(?:(Scanner|BufferedReader|PrintWriter)\s+)?([A-Za-z_]\w*)\s*=\s*new\s+(Scanner|BufferedReader|PrintWriter)\s*\(([\s\S]*)\)$/u.exec(n);if(a){const[,m,c,d,h]=a;if(m&&m!==d)throw G(n);const g=d==="Scanner"?/^new\s+File\s*\(([\s\S]+)\)$/u.exec(h.trim()):d==="BufferedReader"?/^new\s+FileReader\s*\(([\s\S]+)\)$/u.exec(h.trim()):/^(?:new\s+(?:File|FileWriter)\s*\(([\s\S]+)\)|([\s\S]+))$/u.exec(h.trim());if(!g)throw tt(n);const f=this.fileNameValue(g[1]??g[2]);if(d==="PrintWriter")t.variables.set(c,{kind:"writer",file:f,text:""});else{const x=this.fileContent(f).split(/\r?\n/u);x[x.length-1]===""&&x.pop(),t.variables.set(c,{kind:"reader",className:d,file:f,lines:x,next:0})}return t.lastTrace=`${c} = new ${d}(${f})`,!0}const s=/^Files\s*\.\s*writeString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(([^()]+)\)\s*,([\s\S]+)\)$/u.exec(n);if(s){const m=this.fileNameValue(s[1]),c=this.evaluateOutputExpression(s[2]);return t.files.set(m,c),t.lastTrace=`${m} ← ${JSON.stringify(c)}`,!0}const r=/^([A-Za-z_]\w*)\s*\.\s*(println|print|close)\s*\(([\s\S]*)\)$/u.exec(n),o=r&&t.variables.get(r[1]);if(!r||!o)return!1;if(o.kind==="null")throw new He({kind:"nullValue",text:r[1]});if(o.kind==="reader"&&r[2]==="close")return t.lastTrace=`${r[1]}.close()`,!0;if(o.kind!=="writer")return!1;if(r[2]==="close")return t.files.set(o.file,o.text),t.lastTrace=`${o.file} ← ${JSON.stringify(o.text)}`,!0;const l=r[3].trim()===""?"":this.evaluateOutputExpression(r[3]),u=r[2]==="println"?l+` -`:l;return t.variables.set(r[1],{...o,text:o.text+u}),t.lastTrace=`${r[1]}.${r[2]}(${JSON.stringify(l)})`,!0}fileNameValue(e){const t=e.trim(),n=ct(t);if(n!==void 0)return n;const a=this.evaluateReference(t);if((a==null?void 0:a.kind)==="string")return a.value;throw G(t)}fileContent(e){const t=this.state.files.get(e)??Ps.get(e);if(t===void 0)throw new He({kind:"fileNotFound",text:e});return t}readerMethod(e,t,n,a){if(n.args.length)throw G(a);const s=t.next0?"+":"-";if(r.kind==="int"){t.set(s,_t(r.value+a)),this.state.lastTrace=`${s} = ${r.value} ${o} 1 -> ${r.value+a}`;return}if(r.kind==="double"){const l=r.value+a;t.set(s,At(l)),this.state.lastTrace=`${s} = ${_e(r)} ${o} 1 -> ${_e(At(l))}`;return}throw tt(e)}for(const[n,a]of[["+=",1],["-=",-1]]){const s=e.indexOf(n);if(s<0)continue;const r=e.slice(0,s).trim(),o=e.slice(s+2),l=t.get(r);if(!Ii(r)||!l)throw tt(e);const u=a>0?"+":"-";if(l.kind==="int"){const m=this.evaluateInt(o),c=l.value+a*m;t.set(r,_t(c)),this.state.lastTrace=`${r} = ${l.value} ${u} ${m} -> ${c}`;return}if(l.kind==="double"){const m=this.evaluateDouble(o),c=l.value+a*m;t.set(r,At(c)),this.state.lastTrace=`${r} = ${_e(l)} ${u} ${_e(At(m))} -> ${_e(At(c))}`;return}throw tt(e)}this.executeStatement(e)}parseAssignmentTarget(e){if(A1(e))return{variableName:e.trim()};const t=e.split(/\s+/u).filter(Boolean);let n,a;if(t.length===1)n=t[0];else if(t.length===2&&nn(t[0]))n=t[1],a=nn(t[0]);else if(t.length===3&&t[0]==="const"&&nn(t[1]))n=t[2],a=nn(t[1]);else throw tt(e);if(!um(n))throw tt(e);return{variableName:n,valueType:a}}parseInputRequest(e,t=!1){const n=e.slice(6).trim(),a=/^(\S+)(?:\s+(\S+))?(?:\s+([\s\S]*))?$/u.exec(n);if(!a)throw tt(e);const s=[a[1],a[2],a[3]].filter(c=>c!==void 0);let r,o,l;const u=nn(s[0]);if(s.length>=2&&u?(r=s[1],o=u,l=(s[2]??"").trim()):(r=s[0],o=this.inferTargetValueType(r),l=(s[1]??"").trim()),!um(r))throw tt(e);const m=ct(l)??l;return m===""&&t?this.state.outputLineOpen?{variableName:r,valueType:o,prompt:"",continuesLine:!0}:{variableName:r,valueType:o,prompt:""}:{variableName:r,valueType:o,prompt:m===""?`${r}:`:m}}parseArrayDeclaration(e,t){const n=e.split(/\s+/u).filter(Boolean);if(n.length!==2||!n[0].endsWith("[]"))return;const a=n[0].endsWith("[][]"),s=n[0].slice(0,a?-4:-2),r=nn(s),o=n[1];if(!r||!Ii(o))throw tt(e);if(a)return this.parseGridDeclaration(o,s,r,t);const l=t.trim();if(l.startsWith("{")&&l.endsWith("}")){const h=l.slice(1,-1).split(",").map(f=>f.trim());let g;switch(r){case"int":g={kind:"intArray",value:h.map(f=>this.evaluateInt(f))};break;case"double":g={kind:"doubleArray",value:h.map(f=>this.evaluateDouble(f))};break;case"string":g={kind:"stringArray",value:h.map(f=>ct(f)??this.evaluateOutputExpression(f))};break;case"boolean":g={kind:"booleanArray",value:h.map(f=>this.evaluateBool(f))};break}return{name:o,typeName:s,count:h.length,value:g}}const u=`new ${s}[`;if(!l.startsWith(u)){const h=this.evaluateStringMethod(l);if((h==null?void 0:h.kind)==="stringArray"&&r==="string")return{name:o,typeName:s,count:h.value.length,value:h}}if(!l.startsWith(u)||!l.endsWith("]"))throw tt(t);const m=l.slice(u.length,-1),c=this.evaluateInt(m);if(c<0)throw G(m);const d=r==="int"?{kind:"intArray",value:new Array(c).fill(0)}:r==="double"?{kind:"doubleArray",value:new Array(c).fill(0)}:r==="boolean"?{kind:"booleanArray",value:new Array(c).fill(!1)}:{kind:"stringArray",value:new Array(c).fill("")};return{name:o,typeName:s,count:c,value:d}}parseGridDeclaration(e,t,n,a){var c;const s=a.trim(),r=d=>{switch(n){case"int":return this.evaluateInt(d);case"double":return this.evaluateDouble(d);case"boolean":return this.evaluateBool(d);case"string":return ct(d.trim())??this.evaluateOutputExpression(d)}};if(s.startsWith("{")&&s.endsWith("}")){const d=px(s.slice(1,-1)).map(h=>{const g=h.trim();if(!g.startsWith("{")||!g.endsWith("}"))throw G(g);return g.slice(1,-1).trim()===""?[]:g.slice(1,-1).split(",").map(f=>r(f.trim()))});return{name:e,typeName:t,count:`${d.length}][${((c=d[0])==null?void 0:c.length)??0}`,value:{kind:"grid",elementType:n,value:d}}}const o=new RegExp(String.raw`^new\s+${t}\s*\[(.+)\]\s*\[(.+)\]$`,"u").exec(s);if(!o)throw tt(a);const l=this.evaluateInt(o[1]),u=this.evaluateInt(o[2]);if(l<0)throw G(o[1]);if(u<0)throw G(o[2]);const m={kind:"grid",elementType:n,value:Array.from({length:l},()=>new Array(u).fill(am(n)))};return{name:e,typeName:t,count:`${l}][${u}`,value:m}}arrayIndex(e){const t=this.evaluateInt(e);if(t<0)throw G(e);return t}evaluateReference(e){const t=e.trim(),n=this.state.variables.get(t);if(n)return n;const a=this.evaluateStringMethod(t);if(a)return a;const s=/^\(\s*char\s*\)\s*(.+)$/su.exec(t);if(s){const o=this.evaluateInt(s[1]);if(!(o>=0&&o<=1114111))throw G(t);return{kind:"string",value:String.fromCodePoint(o)}}const r=A1(t);if(r)return this.valueInArray(r.arrayName,r.indexExpressions.map(o=>this.arrayIndex(o)))}evaluateStringMethod(e){const t=gx(e);if(!t)return;const n=this.evaluateStaticCall(e,t);if(n)return n;const a=this.state.variables.get(t.target.trim());if((a==null?void 0:a.kind)==="reader")return this.readerMethod(t.target.trim(),a,t,e);if((a==null?void 0:a.kind)==="null")throw new He({kind:"nullValue",text:t.target.trim()});const s=ct(t.target)??this.stringValueOf(t.target);if(s===void 0)return;const r=t.args,o=d=>{const h=r[d];if(h===void 0)throw G(e);const g=ct(h.trim());if(g!==void 0)return g;const f=this.evaluateReference(h);if((f==null?void 0:f.kind)==="string")return f.value;throw G(h)},l=d=>{if(r[d]===void 0)throw G(e);return this.evaluateInt(r[d])},u=d=>{if(r.length!==d)throw G(e)},m=Array.from(s),c=(d,h)=>{if(d<0||d>h)throw G(e);return d};switch(t.method){case"length":return u(0),_t(m.length);case"charAt":return u(1),{kind:"string",value:m[c(l(0),m.length-1)]};case"substring":{if(r.length!==1&&r.length!==2)throw G(e);const d=c(l(0),m.length),h=r.length===2?c(l(1),m.length):m.length;if(h0&&h[h.length-1]==="";)h.pop();return{kind:"stringArray",value:h}}default:return}}evaluateStaticCall(e,t){const n=()=>{if(t.args.length!==1)throw G(e);return t.args[0]},a=s=>{const r=ct(s.trim());if(r!==void 0)return r;const o=this.evaluateReference(s);if((o==null?void 0:o.kind)==="string")return o.value;throw G(s)};switch(`${t.target}.${t.method}`){case"Integer.parseInt":{const s=a(n()).trim();if(!/^[+-]?\d+$/u.test(s))throw new He({kind:"wholeNumberExpected"});return _t(Number(s))}case"Double.parseDouble":{const s=Number(a(n()).trim());if(!Number.isFinite(s))throw new He({kind:"numberExpected"});return At(s)}case"String.valueOf":return{kind:"string",value:this.evaluateOutputExpression(n())};case"Arrays.toString":case"Arrays.deepToString":{const s=this.state.variables.get(n().trim());if(!s||!cs(s))throw G(e);return s.kind==="grid"&&t.method==="toString"?{kind:"string",value:"["+s.value.map(o=>sm({kind:`${s.elementType}Array`,value:o})).join(", ")+"]"}:{kind:"string",value:_e(s)}}case"Files.readString":{const s=/^(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)$/u.exec(n().trim());if(!s)throw G(e);const r=ct(s[1]),o=Ps.get(r);if(o===void 0)throw new He({kind:"fileNotFound",text:r});return{kind:"string",value:o}}default:return}}stringValueOf(e){const t=e.trim(),n=this.state.variables.get(t);if(n)return n.kind==="string"?n.value:void 0;const a=A1(t);if(!a)return;const s=this.valueInArray(a.arrayName,a.indexExpressions.map(r=>this.arrayIndex(r)));return s.kind==="string"?s.value:void 0}substituteStringCalls(e){if(!e.includes("("))return e;let t="",n=e;const a=/([A-Za-z_]\w*(?:\[[^\[\]]*\])*|"(?:[^"\\]|\\.)*")\s*\.\s*(length|indexOf|lastIndexOf|compareTo|charAt|parseInt|parseDouble)\s*\(/u;for(let s=a.exec(n);s;s=a.exec(n)){const r=s.index+s[0].length-1,o=zr(Array.from(n),Array.from(n.slice(0,r)).length);if(o===void 0)break;const l=Array.from(n),u=l.slice(Array.from(n.slice(0,s.index)).length,o+1).join(""),m=this.evaluateStringMethod(u),c=l.slice(0,Array.from(n.slice(0,s.index)).length).join(""),d=(m==null?void 0:m.kind)==="string"?il(m.value):void 0;(m==null?void 0:m.kind)==="int"?t+=c+String(m.value):(m==null?void 0:m.kind)==="double"?t+=c+(Number.isInteger(m.value)?m.value.toFixed(1):String(m.value)):d!==void 0?t+=c+String(d):t+=c+u,n=l.slice(o+1).join("")}return t+n}valueInArray(e,t){const n=this.state.variables.get(e);if(!n)throw _1(e,this.state.variables);const a=`${e}[${t.join("][")}]`;if(n.kind==="grid"){if(t.length!==2)throw G(a);const r=n.value[t[0]];if(!r||t[1]>=r.length)throw G(a);return nl(n.elementType,r[t[1]])}if(t.length!==1)throw G(a);const s=t[0];if(!cs(n))throw G(e);if(s>=n.value.length)throw G(a);switch(n.kind){case"intArray":return _t(n.value[s]);case"doubleArray":return At(n.value[s]);case"stringArray":return{kind:"string",value:n.value[s]};case"booleanArray":return{kind:"boolean",value:n.value[s]};default:throw G(e)}}inferTargetValueType(e){const t=e.trim(),n=this.state.variables.get(t);if(n)return om(n);const a=A1(t);if(a){const s=this.state.variables.get(a.arrayName);if(!s)throw _1(a.arrayName,this.state.variables);if(s.kind==="intArray")return"int";if(s.kind==="doubleArray")return"double";if(s.kind==="stringArray")return"string";if(s.kind==="booleanArray")return"boolean";if(s.kind==="grid")return s.elementType;throw G(a.arrayName)}throw tt(e)}assignValue(e,t){const n=t.trim(),a=A1(n);if(!a){this.state.variables.set(n,e);return}const s=a.indexExpressions.map(u=>this.arrayIndex(u)),r=this.state.variables.get(a.arrayName);if(!r)throw _1(a.arrayName,this.state.variables);if(r.kind==="grid"){if(s.length!==2||s[0]>=r.value.length||s[1]>=r.value[s[0]].length)throw G(n);const u=r.elementType==="double"&&e.kind==="int"||e.kind===r.elementType?e.value:void 0;if(u===void 0||cs(e))throw G(n);const m=r.value.map(c=>[...c]);m[s[0]][s[1]]=u,rm(r.value,m),this.state.variables.set(a.arrayName,{...r,value:m});return}if(s.length!==1)throw G(n);const o=s[0];let l;if(r.kind==="intArray"&&e.kind==="int")l={kind:"intArray",value:[...r.value]};else if(r.kind==="doubleArray"&&(e.kind==="double"||e.kind==="int"))l={kind:"doubleArray",value:[...r.value]};else if(r.kind==="stringArray"&&e.kind==="string")l={kind:"stringArray",value:[...r.value]};else if(r.kind==="booleanArray"&&e.kind==="boolean")l={kind:"booleanArray",value:[...r.value]};else throw G(n);if(o>=l.value.length)throw G(n);l.value[o]=e.value,rm(r.value,l.value),this.state.variables.set(a.arrayName,l)}evaluateOutputExpression(e){const t=this.withSensors(e).trim();if(cm(t)&&Yr(t).every(s=>ct(s.trim())===void 0)&&!this.evaluateReference(t))return String(this.evaluateBool(t));let n,a;for(const s of Yr(this.withSensors(e))){const r=s.trim(),o=ct(r),l=o!==void 0?{kind:"string",value:o}:this.evaluateReference(r)??(cm(r)?{kind:"boolean",value:this.evaluateBool(r)}:this.evaluateNumeric(r)),u=l.kind==="int"||l.kind==="double";if(a===void 0&&u){n?n=n.kind==="int"&&l.kind==="int"?_t(n.value+l.value):At(n.value+l.value):n=l;continue}a=(a??(n?_e(n):""))+sm(l),n=void 0}return a??(n?_e(n):"")}renderOutputExpression(e){return Yr(e).map(t=>{const n=t.trim();if(ct(n)!==void 0)return n;const a=this.evaluateReference(n);if(a)return _e(a);try{return this.renderNumericExpression(n)}catch{return n}}).join(" + ")}evaluateExpression(e,t){switch(t){case"int":return _t(this.evaluateInt(e));case"double":return At(this.evaluateDouble(e));case"string":{const n=e.trim(),a=ct(n);if(a!==void 0)return{kind:"string",value:a};if(n==="null")return{kind:"null"};const s=this.evaluateReference(n);return(s==null?void 0:s.kind)==="null"?s:s?{kind:"string",value:_e(s)}:{kind:"string",value:this.evaluateOutputExpression(e)}}case"boolean":return{kind:"boolean",value:this.evaluateBool(e)}}}evaluateNumeric(e){const{v:t,int:n}=this.parser(e).parseNumber();return n||Number.isFinite(t)&&Math.round(t)===t?_t(t):At(t)}parser(e){const t=this.withSensors(e);return new fx(mm(lm(this.substituteStringCalls(t))),this.state.variables)}withSensors(e){return nx(e,t=>this.robotSensors[t]??Bt[t].simulated)}evaluateInt(e){return this.parser(e).parseInt()}evaluateDouble(e){return this.parser(e).parseDouble()}evaluateBool(e){const t=this.withSensors(e).trim();if(t==="true")return!0;if(t==="false")return!1;const n=this.evaluateReference(t);if((n==null?void 0:n.kind)==="boolean")return n.value;const a=Zs(t,"||");if(a.length>1)return a.some(u=>this.evaluateBool(u));const s=Zs(t,"&&");if(s.length>1)return s.every(u=>this.evaluateBool(u));if(t.startsWith("!")&&!t.startsWith("!="))return!this.evaluateBool(t.slice(1));const r=wg(t);if(r!==void 0)return this.evaluateBool(r);const o=hx(t);if(o)return this.evaluateComparison(o.lhs,"==",o.rhs);const l=dx(t);if(l)return this.evaluateComparison(l.lhs,l.op,l.rhs);if(this.stringOperand(t)!==void 0)throw G(t);return this.evaluateDouble(t)!==0}evaluateComparison(e,t,n){if((e.trim()==="null"||n.trim()==="null")&&(t==="=="||t==="!=")){const m=d=>{var h;return d.trim()==="null"||((h=this.evaluateReference(d))==null?void 0:h.kind)==="null"},c=m(e)===m(n);return t==="=="?c:!c}const a=this.booleanOperand(e),s=this.booleanOperand(n);if((a!==void 0||s!==void 0)&&(t==="=="||t==="!=")){const m=a??this.evaluateBool(e),c=s??this.evaluateBool(n);return t==="=="?m===c:m!==c}const r=this.stringOperand(e),o=this.stringOperand(n);let l,u;if(r!==void 0||o!==void 0){if(r===void 0||o===void 0)throw G(`${e} ${t} ${n}`);l=r,u=o}else l=this.evaluateDouble(e),u=this.evaluateDouble(n);switch(t){case"==":return l===u;case"!=":return l!==u;case"<":return l":return l>u;case">=":return l>=u;default:throw G(`${e} ${t} ${n}`)}}booleanOperand(e){const t=e.trim();if(t==="true")return!0;if(t==="false")return!1;const n=this.evaluateReference(t);return(n==null?void 0:n.kind)==="boolean"?n.value:void 0}stringOperand(e){const t=e.trim(),n=ct(t);if(n!==void 0)return n;const a=this.evaluateReference(t);return(a==null?void 0:a.kind)==="string"?a.value:void 0}evaluateSwitchSelector(e){const t=this.withSensors(e).trim(),n=ct(t);if(n!==void 0)return{kind:"text",value:n};const a=this.state.variables.get(t);if(a)switch(a.kind){case"int":return{kind:"int",value:a.value};case"double":return{kind:"int",value:Math.trunc(a.value)};case"string":return{kind:"text",value:a.value};default:throw G(t)}return/^[+-]?[0-9]+$/.test(t)?{kind:"int",value:Number(t)}:{kind:"int",value:this.evaluateInt(t)}}renderNumericExpression(e){return mm(lm(e)).map(t=>{if(t.t==="number")return t.literal;if(t.t==="identifier"){const n=this.state.variables.get(t.name);if(!n)throw _1(t.name,this.state.variables);return _e(n)}return t.t}).join(" ").replaceAll("( ","(").replaceAll(" )",")").replaceAll(" . ",".").replaceAll("[ ","[").replaceAll(" ]","]")}}const zg={baseIndent:0,indentPerLevel:4,includeDiagramTextAsComments:!1,includeEmptyElseBranch:!0,pseudocodeNote:"Pseudocode – not a runnable program"};function Ws(i,e,t={},n=[]){const a=C0(i,e,t);let s="";e==="javascript"&&(s+=`"use strict"; - -`);const r=[i,...e==="java"?[]:n];if(e==="python"&&r.some(Lg)&&(s+=`import random - -`),e!=="java"&&(s+=ol(r,e)),e==="java"&&Fs(i)&&(s+=`// import java.util.Scanner; -`,s+=`Scanner scanner = new Scanner(System.in); - -`),i.pseudocode&&(s+=(e==="python"?"# ":"// ")+a.pseudocodeNote+` - -`),e!=="java")for(const u of n){if(u===i)continue;const m=Tt(u.caption);m&&(s+=sl(u,m,e,t,0)+` -`)}const o=Tt(i.caption),l={out:""};if(o)l.out=sl(i,o,e,t,a.baseIndent);else for(const u of i.elements)Tr(u,e,a,a.baseIndent,l);if(s+=l.out,s.trim()===""){const u=yx(e);return e==="javascript"?`"use strict"; - -`+u:u}return Fg(s.replace(/^[\n\r]+|[\n\r]+$/g,""),e)}function C0(i,e,t){const n={...zg,...t,jsIntVariables:new Set,arrayElementTypes:Cx(i),stringVariables:jx(i)};return e!=="java"&&(n.jsIntVariables=Yx(i)),n}const gm=i=>i==="string"?"String":i===void 0?"void":i;function Ig(i){const e=i.params.map(t=>`${gm(t.type)}${"[]".repeat(t.dims??0)} ${t.name}`).join(", ");return`public static ${gm(i.returnType)} ${i.name}(${e}) {`}function sl(i,e,t,n,a){const s=C0(i,t,n);for(const l of e.params)l.dims||(l.type==="int"&&s.jsIntVariables.add(l.name),l.type==="string"&&s.stringVariables.add(l.name));const r=e.params.map(l=>l.name).join(", "),o={out:""};for(const l of i.elements)Tr(l,t,s,a+s.indentPerLevel,o);switch(o.out.trim()===""&&(o.out=L(a+s.indentPerLevel)+(t==="python"?`pass -`:` -`)),t){case"java":return`${L(a)}${Ig(e)} -${o.out}${L(a)}} -`;case"python":return`${L(a)}def ${e.name}(${r}): -${o.out}`;case"javascript":return`${L(a)}function ${e.name}(${r}) { -${o.out}${L(a)}} -`}}const L=i=>i>0?" ".repeat(i):"";function ye(i,e){const t=i.exec(e);return t?Array.from(t,n=>n??""):void 0}function yx(i,e){return i==="python"?"# empty element":"// empty element"}const rl=i=>i.trim()==="ø";function kg(i,e){return L(i)+(e==="python"?`# empty element -`:`// empty element -`)}function zx(i){switch(i.type){case S.leerElement:return!0;case S.anweisung:case S.aufruf:return i.textLines.map(e=>e.trim()).every(e=>e===""||rl(e));default:return!1}}function Ix(i){return i.every(zx)}function D0(i,e,t){const n=i.map(a=>a.replace(/^[ \t]+|[ \t]+$/g,"")).filter(a=>a!=="");return n.length===0?"":e==="python"?L(t)+`""" -`+n.map(a=>L(t)+a+` -`).join("")+L(t)+`""" -`:n.length===1?L(t)+"/* "+n[0]+` */ -`:L(t)+`/* -`+n.map(a=>L(t)+" * "+a+` -`).join("")+L(t)+` */ -`}const kx=i=>i==="java"||i==="javascript";function Es(i){const e=i.trim();if(e==="")return"";if(e.startsWith("//")||e.startsWith("/*")||e.startsWith("*")||e.startsWith("#")||e.startsWith("{")||e.startsWith("}")||e.endsWith("{")||e.endsWith(";"))return e;const t=e.toLowerCase();for(const n of["if ","else","for ","while ","do ","switch ","case ","default"])if(t.startsWith(n))return e;return e+";"}function ds(i){const e=i.map(t=>t.trim()).filter(t=>t!=="");return Sx(e.length===0?"true":e.join(" "))}function Sx(i){const e=i.trim();for(const t of["if","while","until","switch"]){const n=Sg(t,e);if(n!==void 0)return n===""?"true":n}return e}function Sg(i,e){if(!e.toLowerCase().startsWith(i))return;const t=e.slice(i.length);if(!(t===""||/^\s/.test(t)||t[0]==="("))return;let n=t.trim();return n.startsWith("(")&&n.endsWith(")")&&n.length>=2&&(n=n.slice(1,-1).trim()),n}function Ut(i){let e=i.trim();for(;e.endsWith(";");)e=e.slice(0,-1).trim();return e}function Tx(i){const e=i.trim(),t=e.toLowerCase();let n;if(t.startsWith("output:"))n=e.slice(7).trim();else if(t.startsWith("print:"))n=e.slice(6).trim();else return;return n===""?void 0:n}const Ir=i=>i.name.includes("[")&&i.name.includes("]");function B0(i){const e=i.trim();if(e==="")return;const t=ye(/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s+([a-zA-Z_]\w*(?:\s*\[[^\]]+\])?)(?:\s+"([^"]*)")?\s*$/i,e);if(t){const s=t[2].replaceAll(" ","");if(s==="")return;const r=(t[3]??"").trim();return{type:Ns(t[1]),name:s,customPrompt:r===""?void 0:r}}const n=ye(/^\s*([a-zA-Z_]\w*(?:\s*\[[^\]]+\])?)\s+"([^"]*)"\s*$/i,e);if(n){const s=n[1].replaceAll(" ","");return["int","integer","long","float","double","string","boolean","char","bool","void"].includes(s.toLowerCase())?void 0:{type:s.includes("[")?"String":"int",name:s,customPrompt:n[2].trim()}}const a=_x(e);if(a)return{type:Ns(a[0]),name:a[1]}}function Ns(i){switch(i.trim().toLowerCase()){case"integer":case"int":return"int";case"long":return"long";case"float":return"float";case"double":return"double";case"string":return"String";case"boolean":case"bool":return"boolean";case"char":return"char";default:return i}}function _x(i){const e=i.split(/[ \t]+/).filter(Boolean);if(e.length===0)return;if(e.length>=2)return[e[0],e[1]];if(!["int","double","string","boolean","char","long","float","short","byte","void","bool","integer"].includes(e[0].toLowerCase()))return["int",e[0]]}function Ax(i,e,t,n,a){const s=Ut(i).trim();if(s.startsWith('"'))switch(e){case"java":return L(t)+"System.out.println("+s+`); -`;case"javascript":return L(t)+"console.log("+s+`); -`;case"python":return L(t)+"print("+Nn(s,n,a)+`) -`}}function Ei(i){return i.replaceAll("\\","\\\\").replaceAll('"','\\"').replaceAll(` -`,"\\n").replaceAll("\r","\\r")}function P0(i,e){const t=i.trim();if(!t.toLowerCase().startsWith("input:"))return;const n=t.slice(6).trim();if(n==="")return;const a=B0(n);if(a&&Ir(a)&&!/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s/i.test(n)){const s=e.get(a.name.slice(0,a.name.indexOf("[")).trim());if(s)return{...a,type:s}}return a}function jx(i){const e=new Set;return kn(i.elements,t=>{for(const n of t.textLines){const a=n.trim();if(a.toLowerCase().startsWith("input:")){const o=B0(a.slice(6).trim());(o==null?void 0:o.type)==="String"&&!Ir(o)&&e.add(o.name);continue}const s=ye(/^\s*(?:const\s+)?string\s+([a-zA-Z_]\w*)\s*=/i,a);s&&e.add(s[1]);const r=ye(/^\s*string\s*\[\s*\]\s*([a-zA-Z_]\w*)\s*=/i,a);r&&e.add(r[1]+"[]")}}),e}function Cx(i){const e=new Map;return kn(i.elements,t=>{for(const n of t.textLines){const a=ye(/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s*\[\s*\]\s*([a-zA-Z_]\w*)/i,n)??ye(/^\s*(integer|int|long|float|double|string|boolean|char|bool)\s+([a-zA-Z_]\w*)\s*\[\s*\]/i,n);a&&e.set(a[2],Ns(a[1]))}}),e}function Dx(i,e,t){const n=P0(i,t);if(!n)return;const a=n.customPrompt?n.customPrompt:n.name+": ",s=L(e)+'System.out.println("'+Ei(a)+`"); -`,r=o=>Ir(n)?n.name:`${o} ${n.name}`;switch(n.type){case"int":return s+L(e)+`${r("int")} = scanner.nextInt(); -`;case"double":return s+L(e)+`${r("double")} = scanner.nextDouble(); -`;case"String":return s+L(e)+`${r("String")} = scanner.next(); -`;case"boolean":return s+L(e)+`${r("boolean")} = scanner.nextBoolean(); -`;default:return}}function Bx(i,e,t){const n=P0(i,t);if(!n)return;const a=!!n.customPrompt,s=a?"":L(e)+'console.log("'+Ei(n.name+": ")+`"); -`,r=l=>{const u=a?n.customPrompt:n.name+": ";return`(prompt("${Ei(u)}") ?? "${l}")`},o=Ir(n)?n.name:`let ${n.name}`;switch(n.type){case"int":return s+L(e)+`${o} = parseInt(${r("0")}, 10); -`;case"double":return s+L(e)+`${o} = Number(${r("0")}); -`;case"String":return s+L(e)+`${o} = ${r("")}; -`;case"boolean":return s+L(e)+`${o} = ${r("")}.trim().toLowerCase() === "true"; -`;default:return}}function Px(i,e,t){const n=P0(i,t);if(!n)return;const a=!!n.customPrompt,s=a?"":L(e)+'print("'+Ei(n.name+": ")+`") -`,r=`input("${Ei(a?n.customPrompt:n.name+": ")}")`;switch(n.type){case"int":return s+L(e)+`${n.name} = int(${r}) -`;case"double":return s+L(e)+`${n.name} = float(${r}) -`;case"String":return s+L(e)+`${n.name} = ${r} -`;case"boolean":return s+L(e)+`${n.name} = ${r}.strip().lower() in ("true", "wahr") -`;default:return}}const Kr=String.raw`("(?:[^"\\]|\\.)*"|[A-Za-z_]\w*)`,Ox=[[new RegExp(String.raw`^new\s+Scanner\s*\(\s*new\s+File\s*\(\s*${Kr}\s*\)\s*\)$`),"Scanner"],[new RegExp(String.raw`^new\s+BufferedReader\s*\(\s*new\s+FileReader\s*\(\s*${Kr}\s*\)\s*\)$`),"BufferedReader"],[new RegExp(String.raw`^new\s+PrintWriter\s*\(\s*(?:new\s+(?:File|FileWriter)\s*\(\s*)?${Kr}\s*\)?\s*\)$`),"PrintWriter"]];function Tg(i){const e=i.trim();for(const[t,n]of Ox){const a=t.exec(e);if(a)return{kind:n,name:a[1]}}}function Mx(i,e,t,n,a){if(e==="java")return;const s=Ut(i).trim(),r=/^(?:(?:Scanner|BufferedReader|PrintWriter)\s+)?([A-Za-z_]\w*)\s*=\s*(new\s[\s\S]+)$/.exec(s),o=r&&Tg(r[2]);if(r&&o){const m=/^(?:Scanner|BufferedReader|PrintWriter)\s/.test(s);return e==="python"?L(t)+`${r[1]} = ${o.kind}(${o.name}) -`:L(t)+`${m?"let ":""}${r[1]} = new ${o.kind}(${o.name}); -`}const l=/^([A-Za-z_]\w*)\.(println|print)\s*\(([\s\S]*)\)$/.exec(s);if(l&&l[1]!=="System"){const m=l[3].trim(),c=e==="python"&&m!==""?Nn(m,n,a):m;return L(t)+`${l[1]}.${l[2]}(${c})${e==="javascript"?";":""} -`}const u=/^Files\.writeString\s*\(\s*(?:Path\.of|Paths\.get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*,\s*([\s\S]+)\)$/.exec(s);if(u){const m=u[2].trim();return e==="python"?L(t)+`open(${u[1]}, "w", encoding="utf-8").write(${Nn(m,n,a)}) -`:L(t)+`require("fs").writeFileSync(${u[1]}, ${m}); -`}}function Zx(i){const e=new Set;for(const t of i)kn(t.elements,n=>{for(const a of n.textLines){const s=/=\s*(new\s.+?);?\s*$/.exec(a),r=s&&Tg(s[1]);r&&e.add(r.kind)}});return e}const Lx={Scanner:`class Scanner: - def __init__(self, name): - self.lines = open(name, encoding="utf-8").read().splitlines() - def hasNextLine(self): - return len(self.lines) > 0 - def nextLine(self): - return self.lines.pop(0) - def close(self): - pass -`,BufferedReader:`class BufferedReader: - def __init__(self, name): - self.lines = open(name, encoding="utf-8").read().splitlines() - def readLine(self): - return self.lines.pop(0) if self.lines else None - def close(self): - pass -`,PrintWriter:`class PrintWriter: - def __init__(self, name): - self.file = open(name, "w", encoding="utf-8") - def println(self, text=""): - self.file.write(str(text) + "\\n") - def print(self, text): - self.file.write(str(text)) - def close(self): - self.file.close() -`},Wx={Scanner:`class Scanner { - constructor(name) { this.lines = require("fs").readFileSync(name, "utf8").split(/\\r?\\n/); if (this.lines.at(-1) === "") this.lines.pop(); } - hasNextLine() { return this.lines.length > 0; } - nextLine() { return this.lines.shift(); } - close() {} -} -`,BufferedReader:`class BufferedReader { - constructor(name) { this.lines = require("fs").readFileSync(name, "utf8").split(/\\r?\\n/); if (this.lines.at(-1) === "") this.lines.pop(); } - readLine() { return this.lines.length > 0 ? this.lines.shift() : null; } - close() {} -} -`,PrintWriter:`class PrintWriter { - constructor(name) { this.name = name; this.text = ""; } - println(text = "") { this.text += text + "\\n"; } - print(text) { this.text += text; } - close() { require("fs").writeFileSync(this.name, this.text); } -} -`};function ol(i,e){const t=Zx(i);if(t.size===0)return"";const n=e==="python"?Lx:Wx;return[...t].map(a=>n[a]).join(` -`)+` -`}function Ex(i,e,t,n,a){const s=/^System\s*\.\s*out\s*\.\s*print\s*\(([\s\S]*)\)$/.exec(Ut(i).trim());if(s){const o=s[1].trim();switch(e){case"java":return L(t)+"System.out.print("+o+`); -`;case"javascript":return L(t)+"process.stdout.write(String("+o+`)); -`;case"python":return L(t)+"print("+(o===""?"":Nn(o,n,a)+", ")+`end="") -`}}const r=Tx(i);if(r===void 0)return/^\s*(?:output|print):\s*$/i.test(i)?L(t)+{java:"System.out.println();",javascript:"console.log();",python:"print()"}[e]+` -`:void 0;switch(e){case"java":return L(t)+"System.out.println("+r+`); -`;case"javascript":return L(t)+"console.log("+r+`); -`;case"python":return L(t)+"print("+Nn(r,n,a)+`) -`}}function O0(i){const e=i.map(Ut).filter(t=>t!=="");return e.length===0?";;":e.join("; ")}function Nx(i){const t=O0(i).split(";"),n=_g(t[0]);return n!==void 0&&(t[0]=n),t.join(";")}function _g(i){const e=ye(/^(\s*)(?:integer|int|long|float|double|string|boolean|char|bool)\s+(\w+)(?:\s*=\s*(.*))?\s*$/i,i);if(!e)return;const t=e[2].trim();if(t==="")return;const n=(e[3]??"").trim();return i.includes("=")&&n!==""?e[1]+`let ${t} = ${n}`:e[1]+`let ${t}`}function Fx(i){const e=i.map(n=>Ut(n).trim()).filter(n=>n!=="");let t=e;if(e.length===1){let n=e[0];const a=Sg("for",n);a!==void 0&&(n=a),t=n.split(";").map(s=>s.trim())}if(!(t.length!==3||t.some(n=>n==="")))return[t[0],t[1],t[2]]}function Ag(i,e){let t=ye(/^\s*(\w+)\s*(\+\+|--)\s*$/,i)??ye(/^\s*(\+\+|--)\s*(\w+)\s*$/,i);if(t){const[n,a]=t[1]==="++"||t[1]==="--"?[t[2],t[1]]:[t[1],t[2]];return n===e?a==="++"?1:-1:void 0}if(t=ye(/^\s*(\w+)\s*(\+|-)=\s*(\d+)\s*$/,i),t)return t[1]===e?(t[2]==="+"?1:-1)*Number(t[3]):void 0;if(t=ye(/^\s*(\w+)\s*=\s*(\w+)\s*(\+|-)\s*(\d+)\s*$/,i),t)return t[1]===e&&t[2]===e?(t[3]==="+"?1:-1)*Number(t[4]):void 0}function ll(i,e){if(/^-?\d+$/.test(i))return String(Number(i)+e);const t=/^[\w.()[\]]+$/.test(i)?i:`(${i})`;return e>0?`${t} + ${e}`:`${t} - ${-e}`}function Rx(i,e){const t=Fx(i);if(!t)return;const[n,a,s]=t,r=ye(/^\s*(?:integer|int|long|short|byte|double|float)?\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i,n);if(!r)return;const o=r[1],l={variable:o,init:`${o} = ${We(r[2],e)}`,condition:a,increment:s},u=ye(/^\s*(\w+)\s*(<=|<|>=|>)\s*(.+)$/,a),m=Ag(s,o);if(!u||u[1]!==o||m===void 0||m===0)return l;const c=We(r[2],e);let d=We(u[3],e);const h=u[2];if(m>0&&h==="<=")d=ll(d,1);else if(m<0&&h===">=")d=ll(d,-1);else if(!(m>0&&h==="<")&&!(m<0&&h===">"))return l;const g=m===1?c==="0"?[d]:[c,d]:[c,d,String(m)];return l.rangeLine=`for ${o} in range(${g.join(", ")}):`,l}function jg(i){const e=Ut(i).trim();if(e==="")return;const t=ye(/^\s*const\s+(integer|int|long|float|double|string|boolean|char|bool)\s+(\w+)(?:\s*=\s*(.*))?$/i,e);if(!t)return;const n=t[2].trim();if(n==="")return;if(!e.includes("="))return{rawType:t[1].trim(),name:n};const a=(t[3]??"").trim();if(a!=="")return{rawType:t[1].trim(),name:n,rhs:a}}function Jx(i,e,t,n){var o;const a=jg(i);if(!a)return;const s=Ns(a.rawType),r=(o=a.rhs)==null?void 0:o.trim();switch(e){case"java":return L(t)+Es(r?`final ${s} ${a.name} = ${r}`:`final ${s} ${a.name}`)+` -`;case"javascript":return r?L(t)+Es(`const ${a.name} = ${r}`)+` -`:L(t)+`let ${a.name}; // from diagram: const ${s} ${a.name} -`;case"python":return r?L(t)+`${a.name} = ${Sr(r,n)} # const ${s} -`:L(t)+`${a.name} = None # const ${s}, uninitialized -`}}function Gx(i){const e=Ut(i).trim();if(e===""||Mg(e))return;const t=ye(/^\s*(?:integer|int|long|float|double|string|boolean|char|bool)(?:\s*\[\s*\]\s+(\w+)|\s+(\w+)(\s*\[\s*\])?)\s*=\s*(.*)$/i,e);if(!t)return;const n=t[1]===""?t[2]:t[1],a=t[4].trim();if(a!=="")return{name:n,rhs:a,isArray:t[1]!==""||t[3]!==""}}function kr(i){const e=ye(/^\s*\(\s*int(?:eger)?\s*\)\s*(.+?)\s*$/i,i),t=e==null?void 0:e[1].trim();if(!(!t||Cg(t)))return t.startsWith("(")&&ul(t,0)===t.length-1?t.slice(1,-1).trim():t}function ul(i,e){let t=0;for(let n=e;n/^'.'$/u.test(i)||/\.charAt\(.*\)$/.test(i);function Hx(i,e){if(i[e]==="("){const a=ul(i,e);return a<0?-1:a+1}if(i[e]==="'")return i[e+2]==="'"?e+3:-1;const t=/^(?:[A-Za-z_]\w*|\d+)/.exec(i.slice(e));if(!t)return-1;let n=e+t[0].length;for(;;){const a=/^\.[A-Za-z_]\w*/.exec(i.slice(n));if(a)n+=a[0].length;else if(i[n]==="("||i[n]==="["){const s=ul(i,n);if(s<0)return-1;n=s+1}else return n}}function ml(i,e){return M0(i,t=>{const n=/\(\s*(int|char)\s*\)\s*/g;let a="",s=0;for(let r=n.exec(t);r;r=n.exec(t)){const o=r.index+r[0].length,l=Hx(t,o);if(l<0)continue;const u=t.slice(o,l);if(r[1]==="int"&&!Cg(u))continue;const m=ml(u.startsWith("(")?u.slice(1,-1).trim():u,e),c=r[1]==="int"?e==="python"?`ord(${m})`:`${m}.charCodeAt(0)`:e==="python"?`chr(${m})`:`String.fromCharCode(${m})`;a+=t.slice(s,r.index)+c,s=l,n.lastIndex=l}return a+t.slice(s)})}function Sr(i,e=new Set){const t=i.trim();if(t==="null")return"None";const n=kr(t);return n!==void 0?`int(${Sr(n,e)})`:We(t,e)}function M0(i,e){let t="",n="",a=0;for(;a/^\d+$/.test(a)||e.has(a);return M0(t,a=>a.replace(/\bMath\.random\s*\(\s*\)/gi,"random.random()").replace(/===/g,"==").replace(/!==/g,"!=").replace(/\s*&&\s*/g," and ").replace(/\s*\|\|\s*/g," or ").replace(/!(?!=)\s*/g,"not ").replace(/\btrue\b/g,"True").replace(/\bfalse\b/g,"False").replace(/\bnull\b/g,"None").replace(new RegExp("(?n(r)&&n(o)?`${r} // ${o}`:s))}function Dg(i){const e=[];let t=0,n="",a=!1;for(let s=0;s/^"(?:[^"\\]|\\.)*"$/.test(i);function Nn(i,e,t){const n=Dg(i),a=s=>Z0(s)||t.has(s)||t.has(s.replace(/^([A-Za-z_]\w*)\[[^[\]]*\]$/u,"$1[]"));return n.length<2||!n.some(a)||n.some(s=>s==="")?We(i,e):n.map(s=>a(s)?s:`str(${We(s,e)})`).join(" + ")}function Bg(i,e,t=new Set){const n=Ut(i).trim(),a=ye(/^(\w+(?:\[[^\]]*\])?)\s*(\+\+|--)$/,n)??ye(/^(\+\+|--)\s*(\w+(?:\[[^\]]*\])?)$/,n);if(a){const[o,l]=a[1]==="++"||a[1]==="--"?[a[2],a[1]]:[a[1],a[2]];return`${We(o,e)} ${l==="++"?"+=":"-="} 1`}const s=ye(/^(\w+)\s*\/=\s*(.+)$/,n);if(s&&e.has(s[1]))return`${s[1]} //= ${We(s[2],e)}`;const r=ye(/^([\w[\]. ]+?)\s*(=|\+=|-=|\*=|\/=|%=)\s*(.+)$/,n);if(r&&!r[3].startsWith("=")){const[,o,l,u]=r;let m;if(l==="+="&&t.has(o.trim())){const c=Dg(u);m=Nn(`"" + ${u}`,e,t).replace(/^"" \+ /,""),c.length===1&&!Z0(u.trim())&&!t.has(u.trim())&&(m=`str(${We(u,e)})`)}else l==="="&&kr(u)===void 0&&u.trim()!=="null"?m=Nn(u,e,t):m=l==="="?Sr(u,e):We(u,e);return`${o} ${l} ${m}`}return We(n,e)}const Vx=i=>i.replace(/\bMath\.random\b(?!\s*\()/gi,"Math.random()");function Pg(i){const e=i.trim();return e.startsWith("{")&&e.endsWith("}")?"["+e.slice(1,-1)+"]":i}function Og(i){const e=ye(/^\s*new\s+(?:byte|short|int|long|float|double|boolean|char|string)\s*\[\s*(.+)\s*\]\s*$/i,i),t=e==null?void 0:e[1].trim();return t||void 0}function Mg(i){const e=ye(/^\s*(int|double|string|boolean)\s*\[\s*\]\s*\[\s*\]\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i,Ut(i).trim());return e?{elementType:e[1].toLowerCase(),name:e[2],rhs:e[3].trim()}:void 0}function qx(i){const e=ye(/^\s*new\s+(?:int|double|string|boolean)\s*\[\s*(.+?)\s*\]\s*\[\s*(.+?)\s*\]\s*$/i,i);return e?[e[1],e[2]]:void 0}const pm=i=>i.replaceAll("{","[").replaceAll("}","]");function fm(i,e){return i==="boolean"?e==="python"?"False":"false":i==="string"?'""':i==="double"?"0.0":"0"}function Ux(i,e,t){const n=qx(i.rhs);return e==="javascript"?n?`Array.from({ length: ${n[0]} }, () => new Array(${n[1]}).fill(${fm(i.elementType,e)}))`:pm(i.rhs):n?`[[${fm(i.elementType,e)}] * ${We(n[1],t)} for _ in range(${We(n[0],t)})]`:We(pm(i.rhs),t)}function Zg(i,e){const t=i.trim();if(e){const a=Og(t);return a!==void 0?`new Array(${a})`:Pg(t)}const n=kr(t);return n!==void 0?`Math.floor(${Zg(n,!1)})`:Vx(t)}function Qx(i,e,t){const n=i.trim();if(e){const a=Og(n);return a!==void 0?`[None] * ${a}`:We(Pg(n),t)}return Sr(n,t)}function $x(i,e,t,n,a){if(e==="java")return;const s=Mg(i);if(s){const u=Ux(s,e,n);return e==="javascript"?L(t)+`let ${s.name} = ${u}; -`:L(t)+`${s.name} = ${u} -`}const r=Gx(i);if(!r)return;if(e==="javascript")return L(t)+Es(`let ${r.name} = ${Zg(r.rhs,r.isArray)}`)+` -`;const l=!r.isArray&&kr(r.rhs)===void 0&&r.rhs.trim()!=="null"?Nn(r.rhs,n,a):Qx(r.rhs,r.isArray,n);return L(t)+`${r.name} = ${l} -`}function kn(i,e){for(const t of i){e(t),t.loopBody&&kn(t.loopBody,e);for(const n of t.cases??[])kn(n.elements,e)}}function Yx(i){const e=n=>["int","integer","long","short","byte"].includes(n.trim().toLowerCase()),t=new Set;return kn(i.elements,n=>{for(const a of n.textLines){const s=a.trim();if(s==="")continue;if(s.toLowerCase().startsWith("input:")){const l=B0(s.slice(6).trim());l&&e(l.type)&&!l.name.includes("[")&&t.add(l.name);continue}const r=jg(s);if(r&&e(r.rawType)){t.add(r.name);continue}const o=ye(/^\s*(integer|int|long|short|byte)\s+([a-zA-Z_]\w*)\s*=/i,s);o&&t.add(o[2].trim())}}),t}function cl(i,e){const t=r=>/^\d+$/.test(r)||e.has(r),n=String.raw`(?:[a-zA-Z_]\w*|\d+(?:\.\d+)?)`,a=new RegExp(String.raw`(?{const o=r.split(/\s*([*/%])\s*/);if(!o.some(m=>m==="/"))return r;let l=o[0],u=t(o[0]);for(let m=1;mr.replace(a,s).replace(new RegExp("(?t(l)&&t(u)?`${l} = Math.trunc(${l} / ${u})`:o))}function Fs(i){let e=!1;return kn(i.elements,t=>{t.type===S.anweisung&&t.textLines.some(n=>n.trim().toLowerCase().startsWith("input:"))&&(e=!0)}),e}const L0="import java.util.Arrays;";function W0(i){let e=!1,t=!1,n=!1,a=!1;return kn(i.elements,s=>{for(const r of s.textLines)/\bArrays\s*\./.test(r)&&(a=!0),/\bFiles\s*\./.test(r)&&(e=!0),/\bnew\s+(?:File|PrintWriter|FileReader|FileWriter|BufferedReader)\s*\(/.test(r)&&(t=!0),/\bnew\s+Scanner\s*\(\s*new\s+File\b/.test(r)&&(n=!0)}),[...t?["import java.io.*;"]:[],...e?["import java.nio.file.*;"]:[],...n?["import java.util.Scanner;"]:[],...a?[L0]:[]]}function Kx(i){return W0(i).some(e=>e!==L0)}function Lg(i){let e=!1;return kn(i.elements,t=>{t.textLines.some(n=>/\bMath\.random\s*\(/i.test(n))&&(e=!0)}),e}function ka(i,e){const t=i.trim();return t===""?e==="python"?'""':"0":e==="java"||new RegExp("^\\p{N}+$","u").test(t)||Z0(t)?t:'"'+t.replaceAll('"','\\"')+'"'}function ai(i,e){const t=i.trim();if(t==="")return"";switch(e){case"java":return"/* "+t+" */";case"python":return" # "+t;case"javascript":return" /* "+t+" */"}}function Xx(i,e,t,n,a){const s=i.map(r=>r.trim());if(s.every(r=>r==="")){t.live&&(a.out+=L(n)+(e==="python"?"pass":";")+` -`);return}if(t.includeDiagramTextAsComments){const r=s.filter(o=>o!==""&&!rl(o));r.length>0&&(a.out+=D0(r,e,n))}for(const r of s){if(r==="")continue;if(rl(r)){a.out+=t.live?N0(n,t.live):kg(n,e);continue}const o=t.jsIntVariables??new Set,l=t.arrayElementTypes??new Map,u=t.stringVariables??new Set,m=(e==="java"?Dx(r,n,l):void 0)??(e==="javascript"?Bx(r,n,l):void 0)??(e==="python"?Px(r,n,l):void 0)??Mx(r,e,n,o,u)??Ex(r,e,n,o,u)??Jx(r,e,n,o)??$x(r,e,n,o,u)??Ax(r,e,n,o,u);if(m!==void 0){a.out+=e==="javascript"?cl(m,o):m;continue}let c=r;kx(e)?c=Es(e==="javascript"?cl(r,o):r):e==="python"&&(c=Bg(r,o,u)),c!==""&&(a.out+=L(n)+c+` -`)}}function ki(i,e,t,n,a,s,r,o=(l=>(l=a.live)==null?void 0:l.labels.condition)()){if(a.includeDiagramTextAsComments&&(r.out+=D0(t,n,s)),o!==void 0&&F0(t)){r.out+=L(s)+i+o1(o)+e;return}const u=ds(t),m=a.jsIntVariables??new Set,c=n==="python"?We(u,m):n==="javascript"?cl(u,m):u;r.out+=L(s)+i+c+e}function D1(i,e,t,n,a){const s=a.out.length;for(const o of i)Tr(o,e,t,n,a);if(t.live&&a.out.length===s&&(a.out+=N0(n,t.live)),e!=="python")return;a.out.slice(s).split(` -`).map(o=>o.trim()).every(o=>o===""||o.startsWith("#"))&&(a.out+=L(n)+`pass -`)}function e4(i,e){var n;if(e.includeDiagramTextAsComments||i.length!==1)return;const t=i[0];return t.type===S.verzweigung&&(((n=t.cases)==null?void 0:n.length)??0)>=2?t:void 0}function dl(i,e,t,n,a,s){const r=i.cases;if(!r||r.length<2)return;const o=e==="python",l=n+t.indentPerLevel,u=!t.includeEmptyElseBranch&&Ix(r[1].elements),m=u?void 0:e4(r[1].elements,t);if(o){ki(s?"elif ":"if ",`: -`,i.textLines,e,t,n,a),D1(r[0].elements,e,t,l,a),m?dl(m,e,t,n,a,!0):u||(a.out+=L(n)+`else: -`,D1(r[1].elements,e,t,l,a));return}if(ki(s?"} else if (":"if (",`) { -`,i.textLines,e,t,n,a),D1(r[0].elements,e,t,l,a),m){const c=a.out.length;dl(m,e,t,n,a,!0),Eg(t,m,a,c)}else u||(a.out+=L(n)+`} else { -`,D1(r[1].elements,e,t,l,a)),a.out+=L(n)+`} -`}function Tr(i,e,t,n,a){const s=a.out.length;t4(i,e,t,n,a),Eg(t,i,a,s)}function t4(i,e,t,n,a){var m;const s=t.indentPerLevel,r=e==="python",o=(c,d)=>D1(c,e,t,d,a),l=(c,d)=>ki(c,d,i.textLines,e,t,n,a),u=(c=n)=>{t.includeDiagramTextAsComments&&(a.out+=D0(i.textLines,e,c))};switch(i.type){case S.leerElement:a.out+=t.live?N0(n,t.live):kg(n,e);return;case S.anweisung:case S.aufruf:Xx(i.textLines,e,t,n,a);return;case S.aussprung:{const c=ds(i.textLines);i.textLines.some(d=>d.trim()!=="")&&u(),r?a.out+=L(n)+(c===""?`break -`:`break # ${c} -`):a.out+=L(n)+(c===""?`break; -`:`break; // ${c} -`);return}case S.verzweigung:dl(i,e,t,n,a,!1);return;case S.fallauswahl:{const c=i.cases;if(!c||c.length===0)return;if(r&&t.pythonWithoutMatch){const d=`wahl${n/s+1}`;ki(d+" = ",` -`,i.textLines,e,t,n,a),c.forEach((h,g)=>{const f=g===c.length-1;f&&g===0?a.out+=L(n)+"if True:"+ai(h.name,e)+` -`:f?a.out+=L(n)+"else:"+ai(h.name,e)+` -`:a.out+=L(n)+(g===0?"if ":"elif ")+d+" == "+ka(h.name,e)+`: -`,o(h.elements,n+s)});return}ki(r?"match ":"switch (",r?`: -`:`) { -`,i.textLines,e,t,n,a,(m=t.live)==null?void 0:m.labels.selector),c.forEach((d,h)=>{const g=h===c.length-1;if(e==="java"){const f=g?"default -> { "+ai(d.name,e)+` -`:"case "+ka(d.name,e)+` -> { -`;a.out+=L(n+s)+f,o(d.elements,n+s*2),a.out+=L(n+s)+`} -`}else if(r){const f=g?"case _:"+ai(d.name,e):"case "+ka(d.name,e)+":";a.out+=L(n+s)+f+` -`,o(d.elements,n+s*2)}else{const f=g?"default:"+ai(d.name,e):"case "+ka(d.name,e)+":";a.out+=L(n+s)+f+` -`,o(d.elements,n+s*2),a.out+=L(n+s*2)+`break; -`}}),r||(a.out+=L(n)+`} -`);return}case S.forSchleife:if(e==="java"){u();const c=t.live?Wg(i.textLines,t.live.labels):O0(i.textLines);a.out+=L(n)+"for ("+c+`) { -`}else if(r){const c=t.jsIntVariables??new Set,d=Rx(i.textLines,c);if((d==null?void 0:d.rangeLine)!==void 0)u(),a.out+=L(n)+d.rangeLine+` -`;else if(d){u(),a.out+=L(n)+d.init+` -`,a.out+=L(n)+"while "+We(d.condition,c)+`: -`;for(const h of i.loopBody??[])Tr(h,e,t,n+s,a);a.out+=L(n+s)+Bg(d.increment,c)+` -`;return}else t.live?a.out+=L(n)+i4(i.textLines,t.live.labels,c)+` -`:l("for ",`: -`)}else{u();const c=t.live?n4(i.textLines,t.live.labels):Nx(i.textLines);a.out+=L(n)+"for ("+c+`) { -`}o(i.loopBody??[],n+s),r||(a.out+=L(n)+`} -`);return;case S.whileSchleife:l(r?"while ":"while (",r?`: -`:`) { -`),o(i.loopBody??[],n+s),r||(a.out+=L(n)+`} -`);return;case S.doUntilSchleife:{const c=t.live&&F0(i.textLines)?o1(t.live.labels.condition):ds(i.textLines);if(r){a.out+=L(n)+`while True: -`;const d=n+s;o(i.loopBody??[],d),u(d);const h=c.includes(ua)?c:We(c,t.jsIntVariables??new Set);a.out+=L(d)+"if not ("+h+`): -`,a.out+=L(d+s)+`break -`}else a.out+=L(n)+`do { -`,o(i.loopBody??[],n+s),u(),a.out+=L(n)+"} while ("+c+`); -`;return}case S.endlosschleife:{const c=ds(i.textLines),d=c===""||c==="true";d||u(),r?a.out+=L(n)+(d?`while True: -`:`while True: # ${c} -`):a.out+=L(n)+(d?`while (true) { -`:`while (true) /* ${c} */ { -`),o(i.loopBody??[],n+s),r||(a.out+=L(n)+`} -`);return}}}const ua="",E0="",o1=i=>ua+i+E0,N0=(i,e)=>L(i)+o1(e.target==="python"?`pass # ${e.labels.code}`:`// ${e.labels.code}`)+` -`,F0=i=>i.every(e=>e.trim()==="");function Wg(i,e){return F0(i)||i.length===3?[e.start,e.condition,e.step].map((n,a)=>{const s=Ut(i[a]??"");return s===""?o1(n):s}).join("; "):O0(i)}function n4(i,e){const t=Wg(i,e).split(";"),n=t[0].includes(ua)?void 0:_g(t[0]);return n!==void 0&&(t[0]=n),t.join(";")}function i4(i,e,t){var h;const[n="",a="",s=""]=i.length===3?i.map(g=>Ut(g)):[],r=ye(/^\s*(?:integer|int|long|short|byte|double|float)?\s*([a-zA-Z_]\w*)\s*=\s*(.+)$/i,n),o=(r==null?void 0:r[1])??((h=ye(/^\s*([a-zA-Z_]\w*)\s*(?:<=|<|>=|>)/,a))==null?void 0:h[1]),l=r?We(r[2],t):o1(e.start),u=ye(/^\s*(\w+)\s*(<=|<)\s*(.+)$/,a);let m=o1(e.end);u&&(o===void 0||u[1]===o)&&(m=We(u[3],t),u[2]==="<="&&(m=ll(m,1)));const c=o===void 0?void 0:Ag(s,o),d=[l,m,...c!==void 0&&c!==1?[String(c)]:[]];return`for ${o??o1(e.selector)} in range(${d.join(", ")}):`}function hl(i,e=[],t=new Map){return i.forEach((n,a)=>{var r;const s=[...e,a];t.has(n)||t.set(n,s),n.loopBody&&hl(n.loopBody,[...s,0],t),(r=n.cases)==null||r.forEach((o,l)=>hl(o.elements,[...s,l],t))}),t}function Eg(i,e,t,n){const a=i.live,s=a==null?void 0:a.paths.get(e);if(!a||!s||t.out.length===n)return;const r=o=>t.out.slice(0,o).split(` -`).length-1;a.spans.push({path:s,from:r(n),to:r(t.out.length-1)})}function Ng(i,e,t){const n={target:e,labels:t,paths:hl(i.elements),spans:[]},a=C0(i,e,{includeEmptyElseBranch:!0,live:n}),s=Tt(i.caption);let r="";if(e==="java"){const u=W0(i);for(const m of u)r+=m+` -`;Fs(i)?(u.includes("import java.util.Scanner;")||(r+=`import java.util.Scanner; -`),r+=`Scanner scanner = new Scanner(System.in); - -`):u.length&&(r+=` -`),s&&(r+=Ig(s)+` -`)}else{if(s)for(const m of s.params)m.dims||(m.type==="int"&&a.jsIntVariables.add(m.name),m.type==="string"&&a.stringVariables.add(m.name));const u=s==null?void 0:s.params.map(m=>m.name).join(", ");e==="javascript"?(r+=`"use strict"; - -`,r+=ol([i],"javascript"),s&&(r+=`function ${s.name}(${u}) { -`)):(Lg(i)&&(r+=`import random - -`),r+=ol([i],"python"),s&&(r+=`def ${s.name}(${u}): -`))}const o={out:""};D1(i.elements,e,a,s?a.indentPerLevel:0,o),s&&e!=="python"&&(o.out+=`} -`);const l=r.split(` -`).length-1;return{text:Fg((r+o.out).replace(/\n+$/,""),e),spans:n.spans.map(u=>({...u,from:u.from+l,to:u.to+l}))}}function Fg(i,e){switch(e){case"javascript":return r4(ml(i,"javascript")).replace(/\.length\(\)/g,".length").replace(/\.contains\(/g,".includes(");case"python":return a4(ml(i,"python")).replace(/\b([a-zA-Z_]\w*(?:\[[^[\]]*\])?)\.length(?:\(\)|\b)/g,"len($1)");case"java":return u4(i)}}function a4(i){for(let e=0;e<5;e++){const t=Rg(s4(i),"python");if(t===i)break;i=t}return i}const bm={indexOf:"find",lastIndexOf:"rfind",toUpperCase:"upper",toLowerCase:"lower",trim:"strip",startsWith:"startswith",endsWith:"endswith"};function s4(i){return R0(i,["charAt","substring","contains","isEmpty","equalsIgnoreCase","compareTo","repeat","concat",...Object.keys(bm)],(e,t,n)=>{switch(t){case"charAt":return`${e}[${n}]`;case"substring":{const a=o4(n);return a.length===2?`${e}[${a[0].trim()}:${a[1].trim()}]`:`${e}[${n.trim()}:]`}case"contains":return`(${n.trim()} in ${e})`;case"isEmpty":return`(len(${e}) == 0)`;case"equalsIgnoreCase":return`(${e}.lower() == ${n.trim()}.lower())`;case"compareTo":return`((${e} > ${n.trim()}) - (${e} < ${n.trim()}))`;case"repeat":return`(${e} * ${n.trim()})`;case"concat":return`(${e} + ${n.trim()})`;default:return`${e}.${bm[t]}(${n})`}})}function r4(i){for(let e=0;e<5;e++){const t=Rg(R0(i,["isEmpty","equalsIgnoreCase","compareTo","replace"],(n,a,s)=>{switch(a){case"isEmpty":return`(${n}.length === 0)`;case"equalsIgnoreCase":return`(${n}.toLowerCase() === ${s.trim()}.toLowerCase())`;case"compareTo":return`${n}.localeCompare(${s.trim()})`;default:return`${n}.replaceAll(${s})`}}),"javascript");if(t===i)break;i=t}return i}function Rg(i,e){const t=/\bFiles\.readString\(\s*(?:Path\.of|Paths\.get)\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*\)/g;return e==="python"?i.replace(t,'open($1, encoding="utf-8").read()').replace(/\bInteger\.parseInt\(/g,"int(").replace(/\bDouble\.parseDouble\(/g,"float(").replace(/\bString\.valueOf\(/g,"str("):i.replace(t,'require("fs").readFileSync($1, "utf8")').replace(/\bInteger\.parseInt\(/g,"parseInt(").replace(/\bDouble\.parseDouble\(/g,"parseFloat(").replace(/\bString\.valueOf\(/g,"String(")}function o4(i){let e=0;const t=[...i].findIndex(n=>n==="("?(e+=1,!1):n===")"?(e-=1,!1):n===","&&e===0);return t<0?[i]:[i.slice(0,t),i.slice(t+1)]}function R0(i,e,t){const n=new RegExp(`([A-Za-z_]\\w*(?:\\[[^[\\]]*\\]|\\.\\w+\\(\\))*|"(?:[^"\\\\]|\\\\.)*")\\.(${e.join("|")})\\(`,"g");let a="",s=0;for(let r=n.exec(i);r;r=n.exec(i)){const o=r.index+r[0].length-1;let l=0,u=-1;for(let c=o;ce.has(n.toLowerCase())?t:`${n}.length()`)}const m4=new Set("abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while true false null var record sealed permits yield module open opens requires exports to uses provides with transitive".split(" "));function _r(i){const e=i.trim();if(e==="")return"Main";let t="";return Array.from(e).forEach((n,a)=>{a===0?new RegExp("\\p{L}","u").test(n)||n==="_"||n==="$"?t+=n:new RegExp("\\p{Nd}","u").test(n)?t+="_"+n:t+="_":/[\p{L}\p{M}\p{N}]/u.test(n)||n==="_"||n==="$"?t+=n:t+="_"}),t=t.replace(/_{2,}/g,"_").replace(/^_+|_+$/g,""),t===""?"Main":(new RegExp("^\\p{Nd}","u").test(t)&&(t="_"+t),m4.has(t.toLowerCase())&&(t="_"+t),t)}function xm(i,e,t={},n=[]){const a={...zg,...t},s=_r(e),r=[i,...n].filter((z,I,T)=>T.indexOf(z)===I).map(z=>({d:z,header:Tt(z.caption)})).filter(z=>z.header!==void 0),o=r.map(z=>sl(z.d,z.header,"java",t,a.indentPerLevel)),l=Tt(i.caption),u=r.some(z=>Fs(z.d)),m=Ws(i,"java",a),c=a.indentPerLevel*2,d=Fs(i),h=d?c4(m):m,g=L(c),f=h.trim()===""?"":h.split(` -`).map(z=>z.trim()===""?g:g+z).join(` -`);let b="";const x=o.length>0&&(d||u),v=[...new Set([i,...r.map(z=>z.d)].flatMap(W0))],w=v.some(z=>z!==L0);(d||u)&&!v.includes("import java.util.Scanner;")&&v.push("import java.util.Scanner;");for(const z of v)b+=z+` -`;v.length&&(b+=` -`),b+=`public class ${s} { -`,x&&(b+=L(a.indentPerLevel)+`static Scanner scanner = new Scanner(System.in); - -`),l||(b+=L(a.indentPerLevel)+`public static void main(String[] args)${w?" throws Exception":""} { -`,d&&!x&&(b+=L(c)+`Scanner scanner = new Scanner(System.in); -`,f!==""&&(b+=` -`)),b+=f,f!==""&&!f.endsWith(` -`)&&(b+=` -`),b+=L(a.indentPerLevel)+`} -`);for(const[z,I]of o.entries())b+=(z>0||!l?` -`:"")+I;return b+=`} -`,b}function c4(i){const e=i.split(` -`);let t=0;for(e[t]==="// import java.util.Scanner;"&&t++;t=e.length?"":e.slice(t).join(` -`)}const Tn={target:"visustruct.codegen.target",javaMode:"visustruct.codegen.javaOutputMode",comments:"visustruct.codegen.includeDiagramComments",emptyElse:"visustruct.codegen.includeEmptyElse"};function Sa(i){try{return localStorage.getItem(i)}catch{return null}}function Ta(i,e){try{localStorage.setItem(i,e)}catch{}}const d4=()=>p("web_run_stopped_loop"),Jg=()=>p("web_run_cancelled"),Re=i=>i.replace(/&/g,"&").replace(//g,">").replace(/"/g,""");function h4(i){let e=0;return i.replace(/^(\s*)while\s*\(\s*true\s*\)(?:\s*\/\*.*?\*\/)?\s*\{/gm,(t,n)=>{const a=`__visuStructLoopGuard${e++}`;return`${n}for (let ${a} = 0; ${a} < 100000 || (() => { throw new Error(${JSON.stringify(d4())}); })(); ${a}++) {`})}function g4(i){return h4(i).replace(/\bprompt\s*\(/g,"await __visuStructPrompt(")}const p4=` -let answer -let lines = [] -// Text von \`process.stdout.write\` ohne neue Zeile (wie \`System.out.print\`). -let partial = '' -const flush = (all) => { - if (all && partial) { - lines.push(partial) - partial = '' - } - if (lines.length) postMessage({ type: 'log', lines }) - lines = [] -} -const send = (type, text) => { - flush(true) - postMessage({ type, text }) -} -const fmt = (a) => (typeof a === 'string' ? a : JSON.stringify(a)) -// Ausgaben gesammelt senden, sonst bremsen viele kleine Nachrichten (z. B. in Endlosschleifen) die Seite aus. -console.log = (...args) => { - lines.push(partial + args.map(fmt).join(' ')) - partial = '' - if (lines.length >= 500) flush() -} -const process = { stdout: { write: (text) => { - const parts = (partial + String(text)).split('\\n') - partial = parts.pop() - lines.push(...parts) - if (lines.length >= 500) flush() -} } } -setInterval(flush, 50) -const __visuStructPrompt = (label) => new Promise((resolve, reject) => { - answer = (value) => (value === null ? reject(new Error(${JSON.stringify(Jg())})) : resolve(value)) - send('prompt', String(label ?? '')) -}) -onmessage = async (e) => { - if (e.data.type === 'answer') return answer && answer(e.data.value) - try { - const AsyncFunction = (async () => {}).constructor - await new AsyncFunction('__visuStructPrompt', e.data.code)(__visuStructPrompt) - send('done') - } catch (err) { - send('error', String(err)) - } -} -`,Xr=2e3;class f4{constructor(e,t,n,a,s=()=>[]){y(this,"element");y(this,"target");y(this,"javaMode");y(this,"comments");y(this,"emptyElse");y(this,"code","");y(this,"codeView");y(this,"javaModeRow");y(this,"runButton");y(this,"saveButton");y(this,"copyButton");y(this,"runner");y(this,"runOutput");y(this,"runInput");y(this,"pseudocodeBox");y(this,"pseudocodeHint");y(this,"worker");y(this,"outputLines",[]);y(this,"droppedLines",0);y(this,"outputFrame");y(this,"pendingAnswer");this.currentDocument=e,this.saveFile=t,this.onClose=n,this.setPseudocode=a,this.subprograms=s;const r=Sa(Tn.target);this.target=r==="python"||r==="javascript"?r:"java",this.javaMode=Sa(Tn.javaMode)==="class"?"class":"snippet",this.comments=Sa(Tn.comments)==="true",this.emptyElse=Sa(Tn.emptyElse)!=="false",this.element=document.createElement("aside"),this.element.className="codepanel",this.build()}refresh(){const e=this.currentDocument(),t=this.options();this.code=this.target==="java"&&this.javaMode==="class"?xm(e,this.classStem(),t,this.subprograms()):Ws(e,this.target,t,this.subprograms()),this.codeView.textContent=this.code,this.javaModeRow.hidden=this.target!=="java",this.saveButton.hidden=this.target!=="java",this.runButton.hidden=this.target!=="javascript"||!!e.pseudocode,this.runButton.hidden&&this.stopRun(!0),this.pseudocodeBox.checked=!!e.pseudocode,this.pseudocodeHint.hidden=!e.pseudocode;for(const n of this.element.querySelectorAll("[data-target]"))n.setAttribute("aria-pressed",String(n.dataset.target===this.target));for(const n of this.element.querySelectorAll("[data-java-mode]"))n.setAttribute("aria-pressed",String(n.dataset.javaMode===this.javaMode))}close(){this.stopRun(!0)}options(){return{includeDiagramTextAsComments:this.comments,includeEmptyElseBranch:this.emptyElse,pseudocodeNote:p("codegen_pseudocode_note")}}classStem(){return Ls(this.currentDocument().caption)||"Main"}build(){this.element.innerHTML=` -
    -

    ${Re(p("codegen_placeholder_title"))}

    - -
    -
    - -
    -
    - -
    - - - - -
    - - - -
    -
    
    -      
    -      

    ${Re(p("codegen_generated_hint"))}

    - `;const e=a=>this.element.querySelector(a);this.codeView=e(".code-view"),this.javaModeRow=e(".java-mode"),this.runButton=e("[data-act=run]"),this.saveButton=e("[data-act=save]"),this.copyButton=e("[data-act=copy]"),this.runner=e(".runner"),this.runOutput=e(".runner-out"),this.runInput=e(".runner-input"),this.pseudocodeBox=e("[data-opt=pseudocode]"),this.pseudocodeHint=e(".pseudocode-hint"),this.pseudocodeBox.addEventListener("change",()=>this.setPseudocode(this.pseudocodeBox.checked)),e(".code-close").addEventListener("click",()=>this.onClose());for(const a of this.element.querySelectorAll("[data-target]"))a.addEventListener("click",()=>{this.target=a.dataset.target,Ta(Tn.target,this.target),this.refresh()});for(const a of this.element.querySelectorAll("[data-java-mode]"))a.addEventListener("click",()=>{this.javaMode=a.dataset.javaMode,Ta(Tn.javaMode,this.javaMode),this.refresh()});const t=e("[data-opt=comments]");t.checked=this.comments,t.addEventListener("change",()=>{this.comments=t.checked,Ta(Tn.comments,String(this.comments)),this.refresh()});const n=e("[data-opt=emptyElse]");n.checked=this.emptyElse,n.addEventListener("change",()=>{this.emptyElse=n.checked,Ta(Tn.emptyElse,String(this.emptyElse)),this.refresh()}),this.copyButton.addEventListener("click",()=>this.copy()),this.saveButton.addEventListener("click",()=>{const a=_r(this.classStem()),s=xm(this.currentDocument(),this.classStem(),this.options());this.saveFile(`${a}.java`,s)}),this.runButton.addEventListener("click",()=>this.run()),e("[data-act=stop]").addEventListener("click",()=>this.stopRun(!1)),e("[data-act=cancel]").addEventListener("click",()=>this.answer(null)),this.runInput.addEventListener("submit",a=>{a.preventDefault(),this.answer(this.runInput.querySelector("input").value)})}async copy(){var t;let e=!1;try{await navigator.clipboard.writeText(this.code),e=!0}catch{const n=document.createRange();n.selectNodeContents(this.codeView);const a=getSelection();a==null||a.removeAllRanges(),a==null||a.addRange(n),e=((t=document.execCommand)==null?void 0:t.call(document,"copy"))??!1}this.copyButton.textContent=p(e?"web_copied_check":"web_copy_manual"),setTimeout(()=>this.copyButton.textContent=p("codegen_copy"),2e3)}run(){this.stopRun(!0),this.runner.hidden=!1,this.runOutput.textContent="",this.outputLines=[],this.droppedLines=0,this.runOutput.classList.remove("error");const e=g4(this.code),t=n=>this.onRunnerMessage(n);try{const n=URL.createObjectURL(new Blob([p4],{type:"text/javascript"})),a=new Worker(n);URL.revokeObjectURL(n),a.onmessage=s=>t(s.data),a.onerror=s=>{s.preventDefault(),t({type:"error",text:s.message||p("web_run_error")})},a.postMessage({type:"run",code:e}),this.worker=a}catch{this.runInPage(e,t)}this.setRunning(!0)}runInPage(e,t){const n={log:(...r)=>t({type:"log",lines:[r.map(o=>typeof o=="string"?o:JSON.stringify(o)).join(" ")]})},a=r=>new Promise((o,l)=>{this.pendingAnswer=u=>u===null?l(new Error(Jg())):o(u),t({type:"prompt",text:r})}),s=(async()=>{}).constructor;new s("__visuStructPrompt","console",e)(a,n).then(()=>t({type:"done"})).catch(r=>t({type:"error",text:String(r)}))}onRunnerMessage(e){switch(e.type){case"log":this.appendOutput(e.lines??[]);break;case"prompt":{this.runInput.hidden=!1,this.runInput.querySelector("label").textContent=e.text??"";const t=this.runInput.querySelector("input");t.value="",t.focus();break}case"error":this.appendOutput([e.text??""]),this.runOutput.classList.add("error"),this.stopRun(!1);break;case"done":this.stopRun(!1);break}}appendOutput(e){this.outputLines.push(...e),this.outputLines.length>Xr&&(this.droppedLines+=this.outputLines.length-Xr,this.outputLines.splice(0,this.outputLines.length-Xr)),this.outputFrame===void 0&&(this.outputFrame=requestAnimationFrame(()=>{this.outputFrame=void 0;const t=this.droppedLines>0?p("web_run_dropped_fmt",this.droppedLines)+` -`:"";this.runOutput.textContent=t+this.outputLines.join(` -`)+(this.outputLines.length?` -`:""),this.runOutput.scrollTop=this.runOutput.scrollHeight}))}answer(e){var t;this.runInput.hidden=!0,this.worker?this.worker.postMessage({type:"answer",value:e}):(t=this.pendingAnswer)==null||t.call(this,e),this.pendingAnswer=void 0}stopRun(e){var t;(t=this.worker)==null||t.terminate(),this.worker=void 0,this.pendingAnswer=void 0,this.runInput&&(this.runInput.hidden=!0),e&&this.runner&&(this.runner.hidden=!0),this.setRunning(!1)}setRunning(e){const t=this.element.querySelector("[data-act=stop]");t&&(t.disabled=!e)}}const b4=["start","end","step"],Gg=(i="i")=>({v:i,start:"0",op:"<",end:"10",step:"1"}),Hg=i=>"ijkmn"[i]??`i${i}`,hs=String.raw`[A-Za-z_]\w*`,Rs=String.raw`-?\d+`;function Vg(i,e="i"){const t=(i.length===1?i[0].split(";"):i).map(c=>c.trim());if(t.every(c=>c===""))return Gg(e);if(t.length!==3)return;const[n,a,s]=t,r=n.match(new RegExp(`^(?:int\\s+)?(${hs})\\s*=\\s*(${Rs})$`));if(!r)return;const o=a.match(new RegExp(`^(${hs})\\s*(<=|>=|<|>)\\s*(${Rs}|mbot\\.\\w+\\(\\)|${hs})$`));if(!o||o[1]!==r[1])return;const l=o[2],u=l==="<"||l==="<=",m=x4(s,r[1]);if(!(!m||m.up!==u))return{v:r[1],start:r[2],op:l,end:o[3],step:m.by}}function x4(i,e){const t=i.replace(/\s+/g,"");if(t===`${e}++`||t===`++${e}`)return{up:!0,by:"1"};if(t===`${e}--`||t===`--${e}`)return{up:!1,by:"1"};const n=t.match(new RegExp(`^${e}(?:([+-])=|=${e}([+-]))(\\d+)$`));if(!(!n||Number(n[3])<=0))return{up:(n[1]??n[2])==="+",by:n[3]}}function J0(i){const e=i.op==="<"||i.op==="<=",t=i.step==="1"?`${i.v}${e?"++":"--"}`:`${i.v} ${e?"+=":"-="} ${i.step}`;return[`int ${i.v} = ${i.start}`,`${i.v} ${i.op} ${i.end}`,t]}function v4(i,e,t){const n=t.trim();switch(b4[e]){case"start":return new RegExp(`^${Rs}$`).test(n)?{...i,start:String(Number(n))}:void 0;case"end":return/^mbot\.\w+\(\)$/.test(n)||new RegExp(`^${hs}$`).test(n)&&n!==i.v?{...i,end:n}:new RegExp(`^${Rs}$`).test(n)?{...i,end:String(Number(n))}:void 0;case"step":return/^\d+$/.test(n)&&Number(n)>0?{...i,step:String(Number(n))}:void 0}}const w4=i=>i.start==="0"&&i.op==="<"&&i.step==="1",y4=(i=10,e="i")=>J0({...Gg(e),end:String(i)});function qg(i,e){const[t,n]=i<=e?[i,e]:[e,i];return`(int) (Math.random() * ${n-t+1}) + ${t}`}const z4=[{id:"add",java:"0 + 0",shape:"round",names:{de:"plus",en:"plus",pt:"mais"}},{id:"sub",java:"0 - 0",shape:"round",names:{de:"minus",en:"minus",pt:"menos"}},{id:"mul",java:"0 * 0",shape:"round",names:{de:"mal",en:"times",pt:"vezes"}},{id:"div",java:"0 / 1",shape:"round",names:{de:"geteilt durch",en:"divided by",pt:"a dividir por"}},{id:"mod",java:"0 % 2",shape:"round",names:{de:"Rest beim Teilen",en:"remainder",pt:"resto da divisão"}},{id:"random",java:qg(1,10),shape:"round",names:{de:"Zufallszahl",en:"random number",pt:"número aleatório"}},{id:"lt",java:"0 < 10",shape:"bool",names:{de:"kleiner als",en:"less than",pt:"menor que"}},{id:"gt",java:"0 > 10",shape:"bool",names:{de:"größer als",en:"greater than",pt:"maior que"}},{id:"eq",java:"0 == 10",shape:"bool",names:{de:"gleich",en:"equals",pt:"igual a"}},{id:"and",java:"true && true",shape:"bool",names:{de:"und",en:"and",pt:"e"}},{id:"or",java:"true || true",shape:"bool",names:{de:"oder",en:"or",pt:"ou"}},{id:"not",java:"!true",shape:"bool",names:{de:"nicht",en:"not",pt:"não"}}],I4=(i,e)=>i.names[e]??i.names.en,k4=i=>/^!|&&|\|\||[<>]=?|==|!=/.test(i.trim()),Ug=()=>/\(int\)\s*\(\s*Math\.random\(\)\s*\*\s*(\d+)\s*\)\s*\+\s*(-?\d+)/g;function Qg(i){const e=[...i.matchAll(Ug())][0];if(!e||e.index!==0||e[0].length!==i.length)return;const t=Number(e[2]);return{from:t,to:t+Number(e[1])-1}}function S4(i,e,t){const n=t.trim();if(!/\s(\+|-|\*|\/|%|<|>|<=|>=|==|!=|&&|\|\|)\s/.test(n)&&!/^\(int\)/.test(n))return!1;const a=i.trimEnd(),s=e.trimStart(),r=a===""||/(^|[^=!<>])=$/.test(a)||/[(,]$/.test(a)||/^(if|while|for|return)$/.test(a),o=s===""||/^[),;]/.test(s);return!(r&&o)}function $g(i,e,t,n){const a=i.slice(0,e),s=i.slice(t),r=S4(a,s,n)?`(${n.trim()})`:n.trim();return a+r+s}const Yg=["int","double","boolean","String","char","long","float"],T4=()=>new RegExp(String.raw`\b(?:${Yg.join("|")})\s+([A-Za-z_]\w*)\s*(?:=|;|$)`,"g");function Kg(i){const e=[];for(const t of i)for(const n of t.matchAll(T4()))e.includes(n[1])||e.push(n[1]);return e}function _4(i,e="zahl"){if(!i.includes(e))return e;let t=2;for(;i.includes(`${e}${t}`);)t++;return`${e}${t}`}const A4=[{id:"declare",java:i=>`int ${i} = 0`,names:{de:"Variable anlegen",en:"make a variable",pt:"criar variável"}},{id:"set",java:i=>`${i} = 0`,names:{de:"setze Variable auf",en:"set variable to",pt:"definir variável para"}},{id:"change",java:i=>`${i} = ${i} + 1`,names:{de:"ändere Variable um",en:"change variable by",pt:"alterar variável em"}}],j4=(i,e)=>i.names[e]??i.names.en;function C4(i,e){const t=new RegExp(String.raw`\b${e}\b`);return i.some(n=>t.test(n.replace(/"(?:[^"\\]|\\.)*"/g,'""')))}const t1=10;function Xg(i){let e=i.replace(/\r/g,"").split(` -`).filter(m=>!m.startsWith("//"));const t=e.filter(m=>/^linie:/.test(m)).map(m=>m.slice(6).trim());e=e.filter(m=>!/^linie:/.test(m));const n=e.findIndex(m=>/^raster:\s*\d+\s*$/.test(m));if(n>=0){const m=Number(e[n].split(":")[1]);e.splice(n,1),e=e.flatMap(c=>Array.from({length:m},(d,h)=>Array.from(c).map(g=>Array.from({length:m},(f,b)=>"^>v<".includes(g)?h===m>>1&&b===m>>1?g:".":g).join("")).join("")))}for(;e.length&&e[e.length-1].trim()==="";)e.pop();for(;e.length&&e[0].trim()==="";)e.shift();const s=Math.max(...e.map(m=>m.length))*t1,r=e.length*t1,o=(m,c)=>({x:-s/2+(m+.5)*t1,y:r/2-(c+.5)*t1}),l={width:s,depth:r,start:{x:0,y:0,heading:0},walls:[],lines:[],blocks:[],marks:[],goals:[],targets:[],colors:[]};let u=0;e.forEach((m,c)=>{Array.from(m).forEach((d,h)=>{const g=o(h,c);d==="#"?l.blocks.push(g):d==="="?l.marks.push(g):d==="Z"?l.goals.push(g):d==="o"?l.targets.push(g):d in vm?l.colors.push({at:g,color:vm[d]}):"^>v<".includes(d)&&(u++,l.start={...g,heading:{"^":0,">":90,v:180,"<":270}[d]})})});for(const m of t)l.lines.push(m.split(/\s+/).map(c=>{const[d,h]=c.split(",").map(Number);if(!Number.isFinite(d)||!Number.isFinite(h))throw new Error(`Linie: „${c}“ ist kein Feld (Spalte,Zeile).`);return o(d,h)}));if(u!==1)throw new Error(`Die Weltkarte braucht genau einen Start (^ > v <), gefunden: ${u}`);return l}const vm={r:"red",g:"green",b:"blue",y:"yellow"},si=(i,e)=>Math.abs(i.x-e.x)<=t1/2&&Math.abs(i.y-e.y)<=t1/2,wm=10,ym=9,D4=300,y1=8,B4=[-3.6,-1.2,1.2,3.6],P4=2.5,O4=20,M4=12,G0=100,Ni={width:200,depth:200,start:{x:0,y:0,heading:0},walls:[],lines:[]},zm=i=>i*Math.PI/180,z1=i=>({x:Math.sin(zm(i)),y:Math.cos(zm(i))}),_a=()=>Array.from({length:hg},()=>{}),Im=([i,e,t])=>i>0||e>0||t>0?[i,e,t]:void 0;class Js{constructor(e=Ni){y(this,"pose");y(this,"trail");y(this,"bumps",0);y(this,"leds",_a());y(this,"screen");y(this,"wheels",[0,0]);y(this,"time",0);y(this,"timerStart",0);y(this,"blocked",!1);y(this,"blockWallCache");this.setup=e,this.pose={...e.start},this.trail=[{x:e.start.x,y:e.start.y}]}reset(){this.pose={...this.setup.start},this.trail=[{x:this.pose.x,y:this.pose.y}],this.bumps=0,this.leds=_a(),this.screen=void 0,this.wheels=[0,0],this.time=0,this.timerStart=0,this.blocked=!1}get allWalls(){const e=this.setup.width/2,t=this.setup.depth/2,n=[{x:-e,y:-t},{x:e,y:-t},{x:e,y:t},{x:-e,y:t}];return[...this.setup.walls,...n.map((a,s)=>({from:a,to:n[(s+1)%4]})),...this.blockWalls]}get blockWalls(){return this.blockWallCache??(this.blockWallCache=(this.setup.blocks??[]).flatMap(e=>{const t=t1/2,n=[{x:e.x-t,y:e.y-t},{x:e.x+t,y:e.y-t},{x:e.x+t,y:e.y+t},{x:e.x-t,y:e.y+t}];return n.map((a,s)=>({from:a,to:n[(s+1)%4]}))})),this.blockWallCache}atGoal(){const e=z1(this.pose.heading),t={x:this.pose.x+e.x*y1,y:this.pose.y+e.y*y1};return(this.setup.goals??[]).some(n=>si(this.pose,n)||si(t,n))||(this.setup.targets??[]).some(n=>si(this.pose,n))}apply(e){switch(e.method){case"forward":return this.wheels=[0,0],this.drive(e.args[0]);case"backward":return this.wheels=[0,0],this.drive(-e.args[0]);case"turnRight":return this.wheels=[0,0],this.turn(e.args[0]);case"turnLeft":return this.wheels=[0,0],this.turn(-e.args[0]);case"stop":this.wheels=[0,0];return;case"drive":this.wheels=[km(e.args[0]),km(e.args[1])];return;case"pause":return this.tick(e.args[0]);case"resetTimer":this.timerStart=this.time;return;case"setLed":this.leds=_a().map(()=>Im(e.args));return;case"ledOff":this.leds=_a();return;case"ledColor":this.leds[e.args[0]]=Im(Cs[e.text??""]??[0,0,0]);return;case"showText":this.screen=e.text;return}}tick(e){if(e<=0)return;this.time+=e;const[t,n]=this.wheels.map(o=>o/100*O4);if(t===0&&n===0)return;const a=Math.ceil(e/10),s=e/1e3/a;let r=!1;for(let o=0;o{const r={x:this.pose.x+e.x*y1+t.x*a,y:this.pose.y+e.y*y1+t.y*a};([...this.setup.marks??[],...this.setup.goals??[]].some(l=>si(r,l))||this.setup.lines.some(l=>L4(r,l)<=P4/2))&&(n|=1<<3-s)}),n}colorUnder(){var n;const e=z1(this.pose.heading),t={x:this.pose.x+e.x*y1,y:this.pose.y+e.y*y1};return(n=(this.setup.colors??[]).find(a=>si(t,a.at)))==null?void 0:n.color}sensors(){const e=this.lineState();return{getDistance:this.distance(),getLine:e,getColor:this.colorUnder()??(e&6?"black":"white"),isLineLeft:(e&4)!==0,isLineRight:(e&2)!==0,getTimer:this.timer}}freeDistance(e,t){let n=1/0;for(const a of this.allWalls){const s=Z4(e,t,a.from,a.to);s!==void 0&&sMath.max(-100,Math.min(100,i));function Sm(i){const e=(i%360+360)%360;return Math.abs(e-360)<1e-9?0:e}function Z4(i,e,t,n){const a={x:n.x-t.x,y:n.y-t.y},s=e.x*a.y-e.y*a.x;if(Math.abs(s)<1e-12)return;const r={x:t.x-i.x,y:t.y-i.y},o=(r.x*a.y-r.y*a.x)/s,l=(r.x*e.y-r.y*e.x)/s;return o>=0&&l>=0&&l<=1?o:void 0}function L4(i,e){let t=1/0;for(let n=0;n+1jr()==="2";function gl(){try{localStorage.setItem(Ar,"2")}catch{}}const tp=()=>jr()==="3";function np(){try{localStorage.setItem(Ar,"3")}catch{}}const N4=()=>jr()!==null;function Ti(i){try{localStorage.setItem(Ar,i==="on"?"1":"0")}catch{}}const ip=`VisuBot`,jn=()=>Si()!=="off",Fi=i=>i.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]);function pl(){const i=ep()?"object":tp()?"flow":Si(),e=En(),n=e?[]:[["off","VisuStruct"],["on","VisuBot"],["object","VisuObject"],["flow","VisuFlow"]],a=([o,l])=>``,s={off:"web_visubot_world_visustruct_hint",on:"web_visubot_world_visubot_hint",object:"web_visuobject_world_hint",flow:"web_visuflow_world_hint"}[i],r=n.length?`
    ${n.map(a).join("")}
    `:"";return e?`${r}${ap()}`:`${r} - ${Fi(p(s))}`}function F4(){const i=En();return i==="visubot"?ip:i==="visuobject"?'VisuObject':'VisuStruct'}function R4(){return En()?"":`

    ${Fi(p("web_visubot_world_title"))}

    `}function ap(){const i=Q3(In()),e={visustruct:"VisuStruct",visubot:"VisuBot",visuobject:"VisuObject"}[En()??"visustruct"],t=["VisuStruct","VisuBot","VisuObject","VisuStudio"].filter(n=>n!==e);return`

    VisuCode - ${Fi(p("web_visubot_world_visucode_hint").replace("%1$@",t.slice(0,-1).join(", ")).replace("%2$@",t.at(-1))).replace("visucode.de",`visucode.de`)} - ${Fi(p("web_about_version_fmt").replace("%@","0.15.29"))} · Holger Sebastiao · sebastiao.org

    `}class J4{constructor(e=Ni){y(this,"element");y(this,"world");y(this,"view");y(this,"loading");y(this,"status");y(this,"stage");y(this,"picker");y(this,"applied",0);y(this,"sent",0);y(this,"liveStatus");y(this,"viewport");y(this,"stopListening");y(this,"realSensors");y(this,"controls",{getBrightness:50,getLoudness:0,getPitch:0,getRoll:0});y(this,"pressTimers",new Map);y(this,"check");y(this,"checking",!1);y(this,"onWorldChange");this.world=new Js(e),this.element=document.createElement("section"),this.element.className="visubot",this.element.innerHTML=`

    ${p("web_visubot_world")}

    ${p("web_visubot_world_in_preview")}

    ${G4()}${H4()}`,this.stage=this.element.querySelector(".visubot-stage"),this.picker=this.element.querySelector(".visubot-worlds"),this.status=this.element.querySelector(".visubot-status"),this.liveStatus=this.element.querySelector(".visubot-live"),this.viewport=this.element.querySelector(".visubot-viewport"),this.stopListening=be.listen(()=>this.updateLive()),this.setupControls(),this.check=this.element.querySelector(".visubot-check"),this.setupCheck(),this.updateLive(),this.updateStatus()}show(){this.loading??(this.loading=ee(async()=>{const{Robot3D:e}=await import("./robot3d-CJE1N58D.js");return{Robot3D:e}},[],import.meta.url).then(({Robot3D:e})=>{this.view=new e(this.stage),this.view.setWorld(this.world.setup),this.view.place(this.world.pose,this.world.trail),this.view.setLeds(this.world.leds)}).catch(()=>{this.stage.textContent=p("web_visubot_no_3d")}))}restoreViewport(){this.viewport.parentElement!==this.element&&this.liveStatus.after(this.viewport)}setWorlds(e){this.picker.replaceChildren(),this.picker.hidden=e.length<2,e.forEach((t,n)=>{const a=document.createElement("button");a.textContent=p("web_visubot_world_fmt",n+1),a.addEventListener("click",()=>this.useWorld(t,a)),this.picker.append(a)}),this.useWorld(e[0]??Ni,this.picker.firstElementChild)}useWorld(e,t){var n,a,s,r,o;for(const l of this.picker.children)l.classList.toggle("active",l===t);e!==this.world.setup&&(this.world=new Js(e),this.applied=0,this.sent=0,(n=this.view)==null||n.setWorld(e),(a=this.view)==null||a.place(this.world.pose,this.world.trail),(s=this.view)==null||s.setLed(void 0),(r=this.view)==null||r.setScreen(void 0),this.updateStatus(),(o=this.onWorldChange)==null||o.call(this))}reset(){var e,t,n;this.world.reset(),this.applied=0,this.sent=0,this.realSensors=void 0,(e=this.view)==null||e.place(this.world.pose,this.world.trail),(t=this.view)==null||t.setLed(void 0),(n=this.view)==null||n.setScreen(void 0),this.updateStatus()}prepare(e){e.robotSensors={...this.controls,...this.world.sensors()}}setupControls(){for(const e of this.element.querySelectorAll(".visubot-controls input[type=range]")){const t=e.parentElement.querySelector("output"),n=()=>{this.controls[e.name]=Number(e.value),t.textContent=e.value};e.addEventListener("input",n),n()}for(const e of this.element.querySelectorAll(".visubot-controls button[name]"))e.addEventListener("click",()=>{const t=e.name;this.controls[t]=!0,e.classList.add("pressed"),window.clearTimeout(this.pressTimers.get(t)),this.pressTimers.set(t,window.setTimeout(()=>{this.controls[t]=!1,e.classList.remove("pressed")},1e3))})}setupCheck(){for(const e of this.check.querySelectorAll("button[data-py]"))e.addEventListener("click",()=>{const{after:t,wait:n}=e.dataset;be.run(e.dataset.py).then(()=>t?new Promise(a=>setTimeout(a,Number(n))).then(()=>be.run(t)):void 0).catch(a=>this.liveFailed(a))});this.check.addEventListener("toggle",()=>void this.readCheck())}async readCheck(){if(!this.checking){this.checking=!0;try{for(;this.check.open&&be.connected;){for(const e of this.check.querySelectorAll("[data-sensor]")){if(!this.check.open||!be.connected)break;const t=e.dataset.sensor,n=await be.ask(Bt[t].python).catch(()=>{});n!==void 0&&(e.textContent=typeof Bt[t].simulated=="boolean"?n===!0||typeof n=="number"&&n!==0?"✓":"–":String(typeof n=="number"?Math.round(n*10)/10:n))}await new Promise(e=>setTimeout(e,200))}}finally{this.checking=!1}}}get live(){return be.connected}async prepareLive(e,t=[]){const n={};for(const a of new Set(["getDistance","getLine",...t])){const s=await be.ask(Bt[a].python);typeof Bt[a].simulated=="boolean"?n[a]=s===!0||typeof s=="number"&&s!==0:typeof Bt[a].simulated=="string"?n[a]=typeof s=="string"?s:String(Bt[a].simulated):n[a]=typeof s=="number"?a==="getDistance"?Math.round(s):s:0}this.realSensors=n,e.robotSensors={...this.controls,...this.world.sensors(),...n}}async drive(e){for(;this.sentsetTimeout(n,t.args[0]))}}liveFailed(e){be.disconnect(e instanceof Error?e.message:String(e))}updateLive(){this.liveStatus.textContent=rp(),this.liveStatus.classList.toggle("connected",be.connected),this.check.hidden=!be.connected,be.connected||(this.check.open=!1),this.updateStatus()}follow(e,t){var a,s,r,o;for(;this.appliedl!==0);this.world.tick(G0),n&&((o=this.view)==null||o.moveTo(this.world.pose,this.world.trail,t)),this.updateStatus()}dispose(){var e;this.stopListening();for(const t of this.pressTimers.values())window.clearTimeout(t);(e=this.view)==null||e.dispose(),this.view=void 0,this.loading=void 0}updateStatus(){const e=this.world.lineState().toString(2).padStart(4,"0"),t=[`${p("web_visubot_distance")}: ${this.world.distance()} cm`,`${p("web_visubot_line")}: ${e}`];this.world.bumps>0&&t.push(`⚠︎ ${p("web_visubot_bumps")}: ${this.world.bumps}`),this.world.screen!==void 0&&t.push(`📺 ${this.world.screen}`),this.realSensors&&be.connected&&t.push(`🤖 ${p("web_visubot_distance")}: ${this.realSensors.getDistance} cm · ${p("web_visubot_line")}: ${Number(this.realSensors.getLine).toString(2).padStart(4,"0")}`),this.status.textContent=t.join(" · ")}}let ri;function sp(i,e){try{ri??(ri=new AudioContext);const t=ri.createOscillator(),n=ri.createGain();t.type="square",t.frequency.value=Math.max(20,Math.min(2e4,i)),n.gain.value=.05,t.connect(n).connect(ri.destination);const a=ri.currentTime+Math.min(Math.max(e,0),5e3)/1e3;t.start(),t.stop(a)}catch{}}function G4(){const i=(t,n,a,s,r)=>``,e=(t,n,a)=>``;return`
    🎛 ${p("web_visubot_controls")} -

    ${p("web_visubot_controls_hint")}

    - ${i("getBrightness","web_visubot_sensor_brightness",0,100,50)} - ${i("getLoudness","web_visubot_sensor_loudness",0,100,0)} - ${i("getPitch","web_visubot_sensor_pitch",-90,90,0)} - ${i("getRoll","web_visubot_sensor_roll",-90,90,0)} -
    - ${e("isButtonA","A","web_visubot_sensor_button_a")}${e("isButtonB","B","web_visubot_sensor_button_b")} - ${e("isJoystickUp","↑","web_visubot_sensor_joy_up")}${e("isJoystickDown","↓","web_visubot_sensor_joy_down")} - ${e("isJoystickLeft","←","web_visubot_sensor_joy_left")}${e("isJoystickRight","→","web_visubot_sensor_joy_right")} - ${e("isShaken",`〰 ${p("web_visubot_shake")}`,"web_visubot_sensor_shake")} -
    -
    `}function H4(){const i=a=>a.replace(/"/g,"""),e=(a,s,r,o=1e3)=>``,t="mbot2.EM_stop('ALL')",n=V0.map(a=>`${p(a.key)}…`).join("");return``}function rp(){return be.connecting?p("web_visubot_connecting"):be.connected?`✓ ${p("web_visubot_connected")}`:be.error?`⚠︎ ${p("web_visubot_link_error")}: ${be.error}`:""}async function V4(){if(be.connected||be.connecting)return be.disconnect();try{await be.connect()}catch(i){await be.disconnect(i.name==="NotFoundError"?void 0:i.message)}}const ft=i=>()=>je(S.anweisung,{textLines:[i]}),op=[{key:"web_visubot_el_forward",group:"drive",type:S.anweisung,make:ft("mbot.forward()")},{key:"web_visubot_el_backward",group:"drive",type:S.anweisung,make:ft("mbot.backward()")},{key:"web_visubot_el_right",group:"drive",type:S.anweisung,make:ft("mbot.turnRight()")},{key:"web_visubot_el_left",group:"drive",type:S.anweisung,make:ft("mbot.turnLeft()")},{key:"web_visubot_el_drive",group:"wheels",type:S.anweisung,make:ft("mbot.drive(30, 30)")},{key:"web_visubot_el_stop",group:"wheels",type:S.anweisung,make:ft("mbot.stop()")},{key:"web_visubot_el_led",group:"leds",type:S.anweisung,make:ft("mbot.setLed(255, 0, 0)")},{key:"web_visubot_el_led_off",group:"leds",type:S.anweisung,make:ft("mbot.ledOff()")},{key:"web_visubot_el_led_color",group:"leds",type:S.anweisung,make:ft('mbot.led(0).setColor("grün")')},{key:"web_visubot_el_tone",group:"sound",type:S.anweisung,make:ft("mbot.playTone(440, 300)")},{key:"web_visubot_el_text",group:"screen",type:S.anweisung,make:ft('mbot.showText("Hallo")')},{key:"web_visubot_el_emotion",group:"emotion",type:S.anweisung,make:ft('mbot.emotion("smile")')},{key:"web_visubot_el_wait",group:"wait",type:S.anweisung,make:ft("mbot.pause(100)")},{key:"web_visubot_el_timer",group:"wait",type:S.anweisung,make:ft("mbot.resetTimer()")}];function fl(i){return{drive:"fahren",wheels:"fahren",leds:"licht",sound:"ton",screen:"anzeige",emotion:"anzeige",wait:"warten"}[i]??"sensor"}const H0={drive:"🚗",wheels:"⚙️",leds:"💡",sound:"🔊",screen:"🖥️",emotion:"😊",wait:"⏱️",distance:"📏",line:"➖",color:"🎨",buttons:"🕹️",light:"🔆",tilt:"📐",timer:"🔋"},V0=[{key:"web_visubot_sensor_distance",group:"distance",text:"mbot.getDistance()"},{key:"web_visubot_sensor_line",group:"line",text:"mbot.getLine()"},{key:"web_visubot_sensor_line_left",group:"line",text:"mbot.isLineLeft()"},{key:"web_visubot_sensor_line_right",group:"line",text:"mbot.isLineRight()"},{key:"web_visubot_sensor_color",group:"color",get text(){return`mbot.getColor().equals("${gg("red")}")`}},{key:"web_visubot_sensor_button_a",group:"buttons",text:"mbot.isButtonA()"},{key:"web_visubot_sensor_button_b",group:"buttons",text:"mbot.isButtonB()"},{key:"web_visubot_sensor_joy_up",group:"buttons",text:"mbot.isJoystickUp()"},{key:"web_visubot_sensor_joy_down",group:"buttons",text:"mbot.isJoystickDown()"},{key:"web_visubot_sensor_joy_left",group:"buttons",text:"mbot.isJoystickLeft()"},{key:"web_visubot_sensor_joy_right",group:"buttons",text:"mbot.isJoystickRight()"},{key:"web_visubot_sensor_brightness",group:"light",text:"mbot.getBrightness()"},{key:"web_visubot_sensor_loudness",group:"light",text:"mbot.getLoudness()"},{key:"web_visubot_sensor_pitch",group:"tilt",text:"mbot.getPitch()"},{key:"web_visubot_sensor_roll",group:"tilt",text:"mbot.getRoll()"},{key:"web_visubot_sensor_shake",group:"tilt",text:"mbot.isShaken()"},{key:"web_visubot_sensor_timer",group:"timer",text:"mbot.getTimer()"},{key:"web_visubot_sensor_battery",group:"timer",text:"mbot.getBattery()"}],q4=new Set(V0.map(i=>{var e;return(e=i.text.match(/^mbot\.(\w+)\(\)/))==null?void 0:e[1]}).filter(i=>!!i)),U4=()=>new RegExp(`(${Ug().source})|("(?:[^"\\\\]|\\\\.)*")|(mbot\\.(\\w+)\\(\\))|(\\b\\d+(?:\\.\\d+)?\\b)|\\b(true|false|null)\\b|\\b([A-Za-z_]\\w*)\\b`,"g"),Q4=new Set(["forward","backward","turnRight","turnLeft"]),$4=new RegExp(`\\b(?:${Yg.join("|")})\\s+$`);function q0(i,e){const t=[];for(const n of i.matchAll(U4())){const a=n.index??0,s=a+n[0].length,r=(o,l=!0)=>t.push({start:a,end:s,kind:o,text:n[0],field:l});if(n[1])r("random");else if(n[4])r("str");else if(n[5])q4.has(n[6])?r("sensor"):Q4.has(n[6])&&t.push({start:s-1,end:s-1,kind:"lit",text:"",field:!0});else if(n[7]||n[8])r("lit");else if(n[9]&&e.has(n[9])&&i[a-1]!=="."){const o=$4.test(i.slice(0,a))||/^\s*=(?!=)/.test(i.slice(s));r("var",!o)}}return t}function Y4(i,e){const t=[];for(const n of q0(i,e))!n.field||n.kind==="sensor"||(n.kind==="random"?t.push({hit:n,part:0},{hit:n,part:1}):t.push({hit:n}));return t}const U0=new Set;function K4(i,e,t,n=U0){let a=0;for(let s=0;s{for(const a of n){a.type===S.anweisung&&e.push(...a.textLines),a.loopBody&&t(a.loopBody);for(const s of a.cases??[])t(s.elements)}};return t(i.elements),Kg(e)}const Ri=i=>Kg(Gs(i.elements));function Q0(i,e){var a,s;let t=i.elements,n=0;for(let r=0;ra.trim()!==""))return t;const{depth:n}=Q0(i,[...e.slice(0,-1),Number.MAX_SAFE_INTEGER]);return{...t,textLines:J0(Vg([],Hg(n)))}}function iv(i,e){if(i==="str")return JSON.stringify(e);const t=e.trim().replace(",",".");return/^-?\d+(\.\d+)?$/.test(t)||/^(true|false|null)$/.test(t)?t:void 0}function av(i,e=U0){const t=[],n=Go(i);let a=i;n&&(t.push({kind:"kw",text:n.keyword}),a=n.rest?` ${n.rest}`:"");let s=0;const r=o=>{if(!o)return;const l=t[t.length-1];(l==null?void 0:l.kind)==="text"?l.text+=o:t.push({kind:"text",text:o})};for(const o of q0(a,e)){if(r(a.slice(s,o.start)),o.kind==="random"){const l=Qg(o.text);t.push({kind:"random",text:o.text,from:String(l.from),to:String(l.to)})}else t.push(o.kind==="var"&&!o.field?{kind:"var",text:o.text,fixed:!0}:{kind:o.kind,text:o.text});s=o.end}return r(a.slice(s)),t}const bl=new Map;var Wh,Eh;for(const i of op){const e=(Eh=(Wh=i.make().textLines[0])==null?void 0:Wh.match(/^mbot\.(\w+)/))==null?void 0:Eh[1];e&&!bl.has(e)&&bl.set(e,i.group)}function sv(i){var t;if(i.type!==S.anweisung)return;const e=(t=i.textLines.join(" ").trim().match(/^mbot\.(\w+)/))==null?void 0:t[1];return e?bl.get(e):void 0}const rv=new RegExp(`^(?:${Object.values(H0).map(i=>i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|")})\\s+(?=mbot\\.)`,"u");function ov(i){let e=!1;const t=a=>a.map(s=>{var o;const r=s.type===S.anweisung?s.textLines.map(l=>l.replace(rv,"")):s.textLines;return r.some((l,u)=>l!==s.textLines[u])&&(e=!0),{...s,textLines:r,loopBody:s.loopBody&&t(s.loopBody),cases:(o=s.cases)==null?void 0:o.map(l=>({...l,elements:t(l.elements)}))}}),n=t(i.elements);return e?{...i,elements:n}:void 0}const oi=i=>i instanceof HTMLElement&&i.dataset.path?JSON.parse(i.dataset.path):void 0,lv=(i,e)=>i.length>=e.length&&e.every((t,n)=>i[n]===t),_m=(i,e)=>{const t=i.getBoundingClientRect();return e{if(!this.enabled||t.target.closest(".bk-input"))return;const n=t.target.closest("[data-value]");if(n&&!this.editing){t.preventDefault(),t.stopPropagation(),this.editField(n);return}const a=t.target.closest("[data-path]");a&&this.options.press(oi(a),t)})}dropAt(e,t,n,a){const s=a??document.elementFromPoint(e,t);if(!s||!this.element.contains(s))return;if(s.closest(".bk-trash"))return n.moving?{kind:"trash"}:void 0;const r=this.rawDrop(s,t,n);return!r||!n.moving||!("path"in r)?r:lv(r.path,n.moving)?void 0:r}rawDrop(e,t,n){var l;const a=e.closest("[data-path]");if(n.sensor){const u=e.closest("[data-value], [data-sensor]");if(u){const c=u.dataset.value?"value":"sensor",{path:d,index:h}=JSON.parse(u.dataset.value??u.dataset.sensor);return{kind:"slot",path:d,slot:c,index:h}}const m=oi(a);return m&&!a.classList.contains("bk-empty")&&n.sensor(m)?{kind:"condition",path:m}:void 0}if(!a)return e.closest(".bk-hat")?{kind:"before",path:[0]}:e.closest(".bk-stack, .blocks-view")?{kind:"rootAppend"}:void 0;const s=oi(a);if(a.classList.contains("bk-empty"))return{kind:"replace",path:s};const r=e.closest(".bk-head, .bk-foot, .bk-clause, .bk-body"),o=r&&r.closest("[data-path]")===a?r:void 0;if(!a.classList.contains("bk-c")||!o)return{kind:_m(a,t)?"before":"after",path:s};if(o.classList.contains("bk-clause"))return this.firstInto(o.nextElementSibling);if(o.classList.contains("bk-body")){const u=[...o.children].filter(c=>c instanceof HTMLElement&&c.dataset.path).pop(),m=oi(u);return m?u.classList.contains("bk-empty")?{kind:"replace",path:m}:{kind:"after",path:m}:void 0}return o.classList.contains("bk-foot")?{kind:"after",path:s}:_m(o,t)?{kind:"before",path:s}:this.firstInto((l=o.parentElement)==null?void 0:l.querySelector(":scope > .bk-body"))}firstInto(e){const t=e==null?void 0:e.firstElementChild,n=oi(t);if(n)return t.classList.contains("bk-empty")?{kind:"replace",path:n}:{kind:"before",path:n}}editField(e){const{path:t,index:n}=JSON.parse(e.dataset.value),a=e.classList.contains("tk-str")?"str":"lit",s=e.textContent??"",r=document.createElement("input");r.className=`bk-input tk-${a}`,r.value=a==="str"?JSON.parse(s):s;const o=()=>r.style.width=`calc(${Math.max(2,r.value.length+1)}ch + 18px)`;o(),r.setAttribute("aria-label",s),r.addEventListener("input",o),this.editing=!0;let l=!1;const u=m=>{if(l)return;l=!0,this.editing=!1;const c=m?iv(a,r.value):void 0;c!==void 0&&c!==s?this.options.editValue(t,n,c):this.pending?this.pending():r.replaceWith(e)};r.addEventListener("keydown",m=>{m.stopPropagation(),m.key==="Enter"?u(!0):m.key==="Escape"&&u(!1)}),r.addEventListener("blur",()=>u(!0)),e.replaceWith(r),r.focus(),r.select()}showDrop(e){var l,u,m;if(this.element.querySelectorAll(".bk-drop").forEach(c=>c.classList.remove("bk-drop")),(l=this.element.querySelector(".bk-drop-line"))==null||l.remove(),(u=this.element.querySelector(".bk-trash"))==null||u.classList.toggle("bk-trash-hot",(e==null?void 0:e.kind)==="trash"),!e||e.kind==="trash")return;if(e.kind==="slot"){const c=JSON.stringify({path:e.path,index:e.index}),d=e.slot==="value"?"data-value":"data-sensor";for(const h of this.element.querySelectorAll(`[${d}]`))h.getAttribute(d)===c&&h.classList.add("bk-drop");return}const t=this.element.querySelector(".bk-stack");if(!t)return;let n,a=!0;if(e.kind==="rootAppend")n=[...t.children].filter(c=>c instanceof HTMLElement&&!!c.dataset.path).pop();else{if(n=(m=this.boxes.find(c=>Mn(c.path,e.path)))==null?void 0:m.el,n&&(e.kind==="replace"||e.kind==="condition")){n.classList.add("bk-drop");return}a=e.kind==="after"}if(!n)return;const s=t.getBoundingClientRect(),r=n.getBoundingClientRect(),o=document.createElement("div");o.className="bk-drop-line",o.style.left=`${r.left-s.left}px`,o.style.top=`${(a?r.bottom:r.top)-s.top-3}px`,o.style.width=`${Math.max(r.width,120)}px`,t.append(o)}setMoving(e){this.element.classList.toggle("bk-moving",e)}render(e,t,n,a=1){if(this.editing){this.pending=()=>this.render(e,t,n,a);return}this.pending=void 0,this.enabled=!n,this.boxes=[],this.forDepth=0,this.vars=new Set(Ri(e)),this.element.style.setProperty("--bk-zoom",String(a)),this.element.classList.toggle("bk-simulating",n);const s=document.createElement("p");s.className="bk-hint",s.textContent=this.options.labels.hint;const r=document.createElement("div");r.className="bk bk-hat";const o=document.createElement("div");o.className="bk-head",o.append(this.word({kind:"kw",text:`🏁 ${this.options.labels.start}`})),r.append(o);const l=document.createElement("div");l.className="bk-stack",l.append(r,...this.list(e.elements,[]));const u=document.createElement("div");u.className="bk-trash",u.textContent=`🗑️ ${this.options.labels.trash}`,this.element.replaceChildren(s,l,u);for(const{path:m,el:c}of this.boxes)t&&Mn(m,t)&&c.classList.add(n?"bk-exec":"bk-sel")}scrollTo(e){const t=this.boxes.find(n=>Mn(n.path,e));t==null||t.el.scrollIntoView({block:"nearest",inline:"nearest"})}list(e,t){return e.map((n,a)=>this.block(n,[...t,a]))}block(e,t){const n=document.createElement("div"),a={path:t,n:0,s:0};if(n.dataset.path=JSON.stringify(t),this.boxes.push({path:t,el:n}),wr(e))return n.className="bk-empty",n.textContent=this.options.labels.empty,n;const s=[...vr(e)];switch(e.type){case S.forSchleife:{n.className="bk bk-c bk-loop";const r=lp(e,this.forDepth),o=r&&w4(r)&&!C4(Gs(e.loopBody??[]),r.v),l=r?o?this.repeatHead(r,a):this.countHead(r,a):this.head(s,a);this.forDepth++;const u=this.body(e.loopBody??[],[...t,0]);this.forDepth--,n.append(l,u,this.foot([],a));break}case S.whileSchleife:case S.endlosschleife:{n.className="bk bk-c bk-loop";const r=e.type===S.endlosschleife?this.keywordHead("endless"):this.head(s,a);n.append(r,this.body(e.loopBody??[],[...t,0]),this.foot([],a));break}case S.doUntilSchleife:n.className="bk bk-c bk-loop",n.append(this.head(["do"],a),this.body(e.loopBody??[],[...t,0]),this.foot(s,a));break;case S.verzweigung:{n.className="bk bk-c bk-if";const r=e.cases??[];n.append(this.head(s,a)),r.forEach((o,l)=>{l>0&&n.append(this.clause(l===1&&r.length===2?this.options.labels.else:o.name)),n.append(this.body(o.elements,[...t,l]))}),n.append(this.foot([],a));break}case S.fallauswahl:n.className="bk bk-c bk-if",n.append(this.head(s,a)),(e.cases??[]).forEach((r,o)=>{n.append(this.clause(r.name.toLowerCase()==="default"?"default":`case ${r.name}`),this.body(r.elements,[...t,o]))}),n.append(this.foot([],a));break;case S.aussprung:n.className="bk bk-cap",n.append(this.head(s,a));break;case S.aufruf:n.className="bk bk-simple bk-call",n.append(this.head(s,a));break;default:{n.className="bk bk-simple bk-stmt";const r=sv(e);if(r){n.dataset.botColor=fl(r);const o=H0[r];o&&(s[0]=`${o} ${s[0]}`)}n.append(this.head(s,a))}}return n}head(e,t){const n=document.createElement("div");n.className="bk-head";for(const a of e){const s=document.createElement("span");s.className="bk-line";for(const r of av(a,this.vars)){if(r.kind==="random"){s.append(this.randomWord(r,t));continue}const o=this.word(this.translated(r));t&&(r.kind==="lit"||r.kind==="str"||r.kind==="var"&&!r.fixed)&&(o.dataset.value=JSON.stringify({path:t.path,index:t.n++})),t&&r.kind==="sensor"&&(o.dataset.sensor=JSON.stringify({path:t.path,index:t.s++})),r.kind==="sensor"&&/^mbot\.is/.test(r.text)&&o.classList.add("tk-bool"),s.append(o)}n.append(s)}return n}randomWord(e,t){const n=this.options.labels,a=document.createElement("span");a.className="tk tk-op";const s=r=>{const o=this.word({kind:"lit",text:r});return t&&(o.dataset.value=JSON.stringify({path:t.path,index:t.n++})),o};return a.append(`${n.randomFrom} `,s(e.from),` ${n.randomTo} `,s(e.to)),a}loopField(e,t,n){const a=/^mbot\./.test(e),s=this.word({kind:a?"sensor":/^[A-Za-z_]/.test(e)?"var":"lit",text:e});return a?s.dataset.sensor=JSON.stringify({path:n.path,index:0}):s.dataset.value=JSON.stringify({path:n.path,index:t}),s}repeatHead(e,t){const n=this.options.labels,a=document.createElement("div");a.className="bk-head";const s=document.createElement("span");return s.className="bk-line",s.append(this.word({kind:"kw",text:n.repeat}),this.word({kind:"text",text:" "}),this.loopField(e.end,1,t),this.word({kind:"text",text:` ${n.repeatTimes}`})),a.append(s),a}countHead(e,t){const n=this.options.labels,a=document.createElement("div");a.className="bk-head";const s=document.createElement("span");s.className="bk-line";const r=(l,u)=>this.loopField(l,u,t),o=e.op==="<"?n.countBelow:e.op===">"?n.countAbove:n.countTo;return s.append(this.word({kind:"kw",text:n.count}),this.word({kind:"text",text:" "}),this.word({kind:"var",text:e.v}),this.word({kind:"text",text:` ${n.countFrom} `}),r(e.start,0),this.word({kind:"text",text:` ${o} `}),r(e.end,1),this.word({kind:"text",text:` ${n.countStep} `}),r(e.step,2)),a.append(s),a}foot(e,t){const n=this.head(e,t);return n.className="bk-foot",n}translated(e){const t=this.options.labels.keywords;if(!t)return e;if(e.kind==="kw")return{...e,text:t[e.text]??e.text};if(e.kind!=="text")return e;if(e.text.trim()==="break"&&t.break)return{kind:"kw",text:t.break};const n=e.text.replace(/&&/g,t["&&"]?` ${t["&&"]} `:"&&").replace(/\|\|/g,t["||"]?` ${t["||"]} `:"||").replace(/!(?!=)/g,t["!"]?`${t["!"]} `:"!").replace(/ {2,}/g," ");return{...e,text:n}}keywordHead(e){var n;const t=document.createElement("div");return t.className="bk-head",t.append(this.word({kind:"kw",text:((n=this.options.labels.keywords)==null?void 0:n[e])??e})),t}clause(e){const t=document.createElement("div");return t.className="bk-head bk-clause",t.append(this.word({kind:"kw",text:e})),t}body(e,t){const n=document.createElement("div");return n.className="bk-body",n.append(...this.list(e,t)),n}word(e){const t=document.createElement("span");return t.className=`tk tk-${e.kind}`,t.textContent=e.text,t}}const mp=["boolean","int","double","String","char","long","float","short","byte"],uv=new Set([...mp,"Boolean","Integer","Double","Character","Long","Float","Short","Byte","var","void"]);function mv(i){if(uv.has(i))return;const e=i.toLowerCase();for(const t of mp)if(cv(e,t.toLowerCase())<=(t.length>=6?2:1))return t}function cv(i,e){const t=Array.from({length:i.length+1},(n,a)=>Array.from({length:e.length+1},(s,r)=>a===0?r:r===0?a:0));for(let n=1;n<=i.length;n++)for(let a=1;a<=e.length;a++){const s=i[n-1]===e[a-1]?0:1;t[n][a]=Math.min(t[n-1][a]+1,t[n][a-1]+1,t[n-1][a-1]+s),n>1&&a>1&&i[n-1]===e[a-2]&&i[n-2]===e[a-1]&&(t[n][a]=Math.min(t[n][a],t[n-2][a-2]+1))}return t[i.length][e.length]}const dv=[">>>=","<<=",">>=",">>>","->","::","++","--","==","!=","<=",">=","&&","||","+=","-=","*=","/=","%=","&=","|=","^=","<<"];function hv(i,e){const t=[];let n=0,a=0;const s=(r,o,l,u)=>t.push({kind:r,text:i.slice(o,l),start:o,end:l,line:u});for(;ni.startsWith(d,n))??r;n+=c.length,s("op",o,n,l)}return t}const Aa=new Set(["if","else","for","while","do","switch","case","default","break","return","try"]),eo=new Set(["public","private","protected","static","final","abstract"]),Am="(?:int|long|short|byte|float|double|boolean|char|String)";let gv=class{constructor(e,t,n){y(this,"toks");y(this,"comments");y(this,"i",0);y(this,"problems",[]);y(this,"placeholders",[]);y(this,"lines",new Map);y(this,"className");y(this,"imports",[]);y(this,"fileScanners",new Set);this.src=e,this.labels=t,this.opts=n;const a=hv(e,this.problems);this.toks=a.filter(s=>s.kind!=="comment"),this.comments=a.filter(s=>s.kind==="comment")}get tok(){return this.toks[this.i]}is(e,t=this.tok){return t!==void 0&&t.text===e&&t.kind!=="str"}next(){return this.toks[this.i++]}eat(e){return this.is(e)?this.next():void 0}prev(){return this.toks[this.i-1]}problem(e,t,n){const a=t??{start:this.src.length,end:this.src.length};this.problems.push({from:a.start,to:Math.max(a.end,a.start+1),key:e,arg:n})}missingAfter(e,t){const n=e?e.end:this.src.length;this.problems.push({from:Math.max(0,n-1),to:n,key:"web_parse_missing",arg:t})}text(e,t){let n="",a=e;for(const s of this.comments)s.end<=e||s.start>=t||(n+=this.src.slice(a,Math.max(a,s.start)),a=Math.max(a,s.end));return n+=this.src.slice(a,Math.max(a,t)),n.replace(/\s*\n\s*/g," ").trim()}slot(e,t,n){const a=this.text(e,t);if(a===n){const s=this.src.indexOf(n,e);return s>=0&&this.placeholders.push({from:s,to:s+n.length}),""}return a}record(e,t,n){return this.lines.set(e,[t,n]),{element:e,from:t,to:n}}lineOf(e){var t;return(e==null?void 0:e.line)??((t=this.toks[this.toks.length-1])==null?void 0:t.line)??0}checkImports(){const e=["File","PrintWriter","FileReader","FileWriter","BufferedReader","IOException","FileNotFoundException"].map(t=>[t,"java.io"]);for(const[t,n]of[["Scanner","java.util"],["Arrays","java.util"],["Files","java.nio.file"],["Path","java.nio.file"],["Paths","java.nio.file"],...e]){const a=this.imports.some(r=>r===`import${n}.${t};`||r===`import${n}.*;`),s=this.toks.find((r,o)=>{var l;return r.kind==="id"&&r.text===t&&(t!=="Arrays"&&n!=="java.nio.file"||((l=this.toks[o+1])==null?void 0:l.text)===".")});if(!a&&s){this.problem("web_parse_missing_import",s,n==="java.util"?`import ${n}.${t};`:`import ${n}.*;`);return}}}checkScanners(){var n;const e=new Set(["next","nextInt","nextDouble","nextLine","nextBoolean","nextLong","nextFloat","hasNext","hasNextInt","hasNextLine","hasNextDouble"]),t=a=>this.toks.some((s,r)=>{const o=this.toks[r-1];return s.kind==="id"&&s.text===a&&!!o&&(o.kind==="id"&&!Aa.has(o.text)&&o.text!=="new"||o.text===">"||o.text==="]")});for(let a=0;a{var s;return((s=this.toks[a-1])==null?void 0:s.text)==="class"}).map(n=>n.text));for(let n=0;nthis.toks.some((c,d)=>{var h;return c.text===m&&((h=this.toks[d-1])==null?void 0:h.kind)==="id"&&!Aa.has(this.toks[d-1].text)}),n=["println","print","printf"],a=["length","charAt","substring","indexOf","equals","equalsIgnoreCase","compareTo","contains","startsWith","endsWith","toUpperCase","toLowerCase","trim","isEmpty","split","replace","sqrt","pow","abs","max","min","random","round","parseInt","parseDouble","nextInt","nextDouble","nextLine","next","hasNext","hasNextInt","hasNextLine"],s=(m,c)=>c.find(d=>d!==m&&d.toLowerCase()===m.toLowerCase());for(let m=0;mo.text).join(""));continue}const a=this.classStart();if(a!==void 0){this.className??(this.className=a.name),this.i=a.index;const r=this.parseClassBody();e.push(...r.elements),t??(t=r.caption);continue}const s=this.methodStart();if(s){n&&this.problem("web_parse_more_methods",this.tok);const r=this.parseMethod(s);n||(t=s.caption,e.push(...r)),n=!0;continue}if(this.is("}")){this.problem("web_parse_extra",this.next(),"}");continue}e.push(...this.parseStatement())}return{caption:t,elements:this.finishList(e)}}skipPast(e){for(;this.tok&&!this.is(e);)this.next();this.eat(e)}classStart(){var n,a;let e=this.i;for(;this.toks[e]&&eo.has(this.toks[e].text);)e++;if(((n=this.toks[e])==null?void 0:n.text)!=="class"||((a=this.toks[e+1])==null?void 0:a.kind)!=="id")return;const t=this.toks[e+1].text;for(e+=2;this.toks[e]&&this.toks[e].text!=="{";)e++;return{name:t,index:e+1}}parseClassBody(){let e,t,n=!1;const a=[];for(;this.tok&&!this.is("}");){const s=this.methodStart();if(!s){this.is("{")?a.push(...this.parseBlock().elements):a.push(...this.parseStatement());continue}const r=this.tok,o=this.parseMethod(s);s.name==="main"?e??(e=o):t?n=!0:t={caption:s.caption,body:o},(e&&t||n)&&this.problem("web_parse_more_methods",r)}return this.eat("}"),e?{elements:e}:a.length>0||!t?{elements:a}:{caption:t.caption,elements:t.body}}methodStart(){var g,f,b,x,v,w,z,I,T,k,C;let e=this.i,t=0;for(;this.toks[e]&&eo.has(this.toks[e].text);)e++,t++;const n=this.toks[e];if((n==null?void 0:n.kind)!=="id"||Aa.has(n.text)||n.text==="new")return;for(e++;((g=this.toks[e])==null?void 0:g.text)==="["&&((f=this.toks[e+1])==null?void 0:f.text)==="]";)e+=2;const a=this.toks[e];if((a==null?void 0:a.kind)!=="id"||((b=this.toks[e+1])==null?void 0:b.text)!=="(")return;let s=0,r=e+1;for(;this.toks[r];r++){const P=this.toks[r].text;if(P==="(")s++;else{if(P===")"&&--s===0)break;if(P===";"||P==="{"||P==="}")break}}let o=r+1;if(((x=this.toks[r])==null?void 0:x.text)===")"&&((v=this.toks[o])==null?void 0:v.text)==="throws")for(o++;((w=this.toks[o])==null?void 0:w.kind)==="id"&&(((z=this.toks[o+1])==null?void 0:z.text)===","||((I=this.toks[o+1])==null?void 0:I.text)==="{"||((T=this.toks[o+1])==null?void 0:T.text)===".");)o+=this.toks[o+1].text==="{"?1:2;const l=this.toks[o],u=((k=this.toks[r])==null?void 0:k.text)===")"&&(l==null?void 0:l.text)==="{";if(!u&&!(t>0&&(l===void 0||this.toks[r]===void 0)))return;const m=((C=this.toks[r])==null?void 0:C.text)===")"?this.toks[r].start:this.src.length,c=this.text(this.toks[e+1].end,m),h=`${this.text(n.start,this.toks[e-1].end).replace(/\s+/g,"")} ${a.text}(${c.replace(/\s*,\s*/g,", ")})`;return this.i=Math.min(u?o:r+1,this.toks.length),{name:a.text,caption:a.text==="main"||!Tt(h)?void 0:h}}parseMethod(e){return this.is("{")?this.parseBlock().elements:(this.missingAfter(this.prev(),"{"),[])}parseBlock(){const e=this.next(),t=[];for(;this.tok&&!this.is("}")&&!(this.is("case")||this.is("default"));)t.push(...this.parseStatement());const n=this.eat("}");if(n||this.problem("web_parse_missing",e,"}"),t.length===0){const a=(n==null?void 0:n.start)??this.src.length,s=this.comments.find(r=>r.start>e.end&&r.startn.element)}parseStatement(){const e=this.tok;switch(e.kind==="id"?e.text:""){case"if":return[this.parseIf()];case"while":return[this.parseWhile()];case"do":return[this.parseDo()];case"for":return[this.parseFor()];case"switch":return[this.parseSwitch()];case"try":return this.parseTry();case"break":{this.next();const t=this.eat(";");t||this.missingAfter(e,";");const n=this.trailingComment(t??e);return[this.record(je(S.aussprung,{textLines:[n]}),e.line,e.line)]}case"else":return this.problem("web_parse_else_without_if",this.next()),[];case"case":case"default":for(this.problem("web_parse_case_outside_switch",this.next());this.tok&&!this.is(":")&&!this.is("->")&&!this.is(";");)this.next();return this.next(),[]}return this.is("{")?this.parseBlock().elements:this.is(";")?(this.next(),[this.record(je(S.anweisung),e.line,e.line)]):this.parseSimple()}parseTry(){const e=this.next();let t=e;const n=(r,o)=>{t=this.next();let u=1;for(;this.tok;){const m=this.next();if(t=m,m.kind!=="str"){if(m.text===r)u++;else if(m.text===o&&--u===0)return!0}}return this.missingAfter(t,o),!1};let a=!0;for(this.is("(")&&(a=n("(",")")),a&&this.is("{")?a=n("{","}"):a&&this.missingAfter(t,"{");a&&(this.is("catch")||this.is("finally"));){const r=this.next().text==="catch";t=this.prev(),r&&(this.is("(")?a=n("(",")"):this.missingAfter(t,"(")),a&&this.is("{")?a=n("{","}"):a&&this.missingAfter(t,"{")}const s=this.text(e.start,t.end).replace(/\s+/g," ");return[this.record(je(S.anweisung,{textLines:[s]}),e.line,t.line)]}trailingComment(e){const t=this.comments.find(a=>a.start>=e.end&&a.line===e.line),n=this.toks.find(a=>a.start>=e.end);return!t||n&&n.start1;)m.push("");const c=this.parseBody();return this.record(je(S.forSchleife,{textLines:m,loopBody:c.elements}),e.line,c.end)}parseSwitch(){const e=this.next(),t=this.parenthesized(e,this.labels.selector),n=[];let a=this.lineOf(this.prev());const s=this.eat("{");if(!s)this.missingAfter(this.prev(),"{");else{for(;this.tok&&!this.is("}");){if(!this.is("case")&&!this.is("default")){this.problem("web_parse_case_outside_switch",this.tok),this.parseStatement();continue}n.push(this.parseCase())}const o=this.eat("}");o||this.problem("web_parse_missing",s,"}"),a=this.lineOf(o??this.prev())}n.some(o=>o.isDefault)||n.push({name:"default",elements:[sn()],isDefault:!0});const r=[...n.filter(o=>!o.isDefault),...n.filter(o=>o.isDefault)].map(({name:o,elements:l})=>({name:o,elements:l}));return this.record(je(S.fallauswahl,{textLines:[t.text],cases:r}),e.line,a)}parseCase(){var l;const e=this.next(),t=e.text==="default",n=((l=this.tok)==null?void 0:l.start)??e.end;let a=n;for(;this.tok&&!this.is("->")&&!this.is(":")&&!this.is("{")&&!this.is("}");)a=this.next().end;const s=this.eat("->")??this.eat(":");s||this.missingAfter(this.prev(),"->");let r=t?"default":this.text(n,a),o=[];if((s==null?void 0:s.text)==="->")this.is("{")?(t&&(r=this.trailingComment(this.tok)||r),o=this.parseBlock().elements):this.tok&&!this.is("}")&&(o=this.parseStatement());else{for(t&&s&&(r=this.trailingComment(s)||r);this.tok&&!this.is("case")&&!this.is("default")&&!this.is("}");)o.push(...this.parseStatement());const u=o[o.length-1];(u==null?void 0:u.element.type)===S.aussprung&&u.element.textLines.every(m=>m==="")&&o.pop()}return{name:r,elements:this.finishList(o),isDefault:t}}parseSimple(){const e=this.next();let t=e;const n=e.text==="("||e.text==="["||e.text==="{"?[e.text]:[],a=()=>n.length>0&&n[n.length-1]==="{"&&e.text!=="{";let s;for(;this.tok;){const u=this.tok;if(u.text===";"&&u.kind==="op"&&!a()){s=this.next();break}if(u.line!==t.line&&!a()&&(n.length===0||u.kind==="id"&&Aa.has(u.text)||u.text==="}")||u.text==="}"&&n.length===0)break;(u.text==="("||u.text==="["||u.text==="{")&&n.push(u.text),(u.text===")"||u.text==="]"||u.text==="}")&&n.pop(),t=this.next()}s||this.missingAfter(t,";"),this.checkBrackets(e,t);const r=this.text(e.start,t.end),o=this.simpleElement(r);if(!o)return[];const l=this.record(o.element,e.line,t.line);return l.prompt=o.prompt,[l]}checkBrackets(e,t){const n=[],a={")":"(","]":"[","}":"{"},s={"(":")","[":"]","{":"}"};for(let r=this.toks.indexOf(e);r>=0&&rje(S.anweisung,{textLines:[u]}),a=/^System\.out\.println\s*\(([\s\S]*?)\)?$/.exec(e);if(a){const u=a[1].trim();if(u==="")return{element:n("output: ")};const m=/^"((?:[^"\\]|\\.)*)"$/.exec(u),c=m?pv(m[1]):void 0;return{element:n(`output: ${u}`),prompt:c!==void 0&&!c.includes('"')?c:void 0}}const s=new RegExp(`^(?:(${Am})\\s+)?([A-Za-z_]\\w*(?:\\s*\\[[^\\]]+\\])*)\\s*=\\s*\\w+\\.(nextInt|nextDouble|nextLine|next|nextBoolean)\\s*\\(\\s*\\)$`).exec(e);if(s&&!this.fileScanners.has(e.replace(/^[\s\S]*=\s*(\w+)\.\w+\s*\(\s*\)$/,"$1"))){const u={nextInt:"int",nextDouble:"double",nextLine:"String",next:"String",nextBoolean:"boolean"}[s[3]],m=s[2].replace(/\s+/g,"");if((s[1]===u||!s[1]&&m.includes("["))&&u)return{element:n(`input: ${u} ${m}`)}}const r=new RegExp(`^final\\s+(${Am}\\s+[\\s\\S]*)$`).exec(e);if(r)return{element:n(`const ${r[1]}`)};const o=/^(?:(?:\w+\s+)?\w+\s*=\s*)?([A-Za-z_]\w*)\s*\(/.exec(e);return o&&((l=this.opts.subprograms)!=null&&l.has(o[1]))?{element:je(S.aufruf,{textLines:[e]})}:{element:n(e)}}spansFor(e,t=[],n=[]){return e.forEach((a,s)=>{var l;const r=[...t,s],o=this.lines.get(a);o&&n.push({path:r,from:o[0],to:o[1]}),a.loopBody&&this.spansFor(a.loopBody,[...r,0],n),(l=a.cases)==null||l.forEach((u,m)=>this.spansFor(u.elements,[...r,m],n))}),n}isCodePlaceholder(e){return e.text.startsWith("//")&&e.text.slice(2).trim()===this.labels.code}markPlaceholderComments(){for(const e of this.comments)this.isCodePlaceholder(e)&&this.placeholders.push({from:e.start,to:e.end})}};function pv(i){return i.replace(/\\(.)/g,(e,t)=>({n:` -`,r:"\r",t:" "})[t]??t)}function Ji(i,e,t={}){const n=new gv(i,e,t),{caption:a,elements:s}=n.parseProgram();n.markPlaceholderComments(),n.checkImports(),n.checkScanners(),n.checkTypes(),n.checkCase();const r=n.spansFor(s),o=n.problems.sort((l,u)=>l.from-u.from);return{caption:a,className:n.className,elements:s,spans:r,placeholders:n.placeholders.sort((l,u)=>l.from-u.from),problems:o}}const $0={asClass:!1,withMain:!1},jm=i=>/^import\s+[\w.]+(?:\.\*)?\s*;$/.test(i.trim()),Cm=i=>i.map(e=>e.trim()===""?"":" "+e);function cp(i,e,t=$0){var u;const n=Ng(i,"java",e);let a=n.text.replaceAll(ua,"").replaceAll(E0,"").split(` -`),s=n.spans;const r=m=>s=s.map(c=>({...c,from:c.from+m,to:c.to+m})),o=Tt(i.caption)!==void 0,l=[];if(t.withMain&&!o||t.asClass){for(let m=a.findIndex(jm);m>=0;m=a.findIndex(jm))l.push(a[m].trim()),a.splice(m,1),s=s.map(c=>({...c,from:c.from>m?c.from-1:c.from,to:c.to>m?c.to-1:c.to}));l.length&&((u=a[0])==null?void 0:u.trim())===""&&(a.splice(0,1),s=s.map(m=>({...m,from:m.from>0?m.from-1:m.from,to:m.to>0?m.to-1:m.to}))),l.length&&l.push("")}return t.withMain&&!o&&(a=[`public static void main(String[] args)${Kx(i)?" throws Exception":""} {`,...Cm(a),"}"],r(1)),t.asClass&&(a=[`public class ${_r(o?"Main":i.caption)} {`,...Cm(a),"}"],r(1)),l.length&&(a=[...l,...a],r(l.length)),{text:a.join(` -`),spans:s}}function _i(i,e,t=$0){return cp(i,e,t).text}function to(i){const e=dp(i).code;return(e.trim()===""?i:e).replace(/\s+/g,"")}function dp(i){let e="";for(let t=0;tnew Array(a+1).fill(0));for(let u=n-1;u>=0;u--)for(let m=a-1;m>=0;m--)s[u][m]=t(i[u],e[m])?s[u+1][m+1]+1:Math.max(s[u+1][m],s[u][m+1]);const r=[];let o=0,l=0;for(;o=s[o][l+1])?r.push({kind:"del",a:o++}):r.push({kind:"ins",b:l++});return r}function Bm(i,e,t,n,a={},s=$0){const r=_i(t,n,s),o=_i(e,n,s);if(i===o)return r;const l=o.split(` -`),u=r.split(` -`),m=i.split(` -`),c=new Array(l.length).fill(-1);for(const v of Dm(l,m,(w,z)=>to(w)!==""&&to(w)===to(z)))v.kind==="keep"&&(c[v.a]=v.b);const d=[];let h=0,g="";const f=v=>{for(;hw===z))if(v.kind==="keep")c[v.a]>=0&&(f(c[v.a]+1),g="");else if(v.kind==="del"){const w=c[v.a];if(w<0)continue;f(w),g||(g=dp(m[w]).comment),h=w+1}else{const w=u[v.b];d.push(g&&w.trim()!==""?`${w} ${g}`:w),w.trim()!==""&&(g="")}f(m.length);const b=d.join(` -`),x=Ji(b,n,a);return _i(Gi(t,x),n,s)===r?b:r}function Gi(i,e){let t=e.caption??(Tt(i.caption)?"":i.caption);return!e.caption&&e.className&&_r(t)!==e.className&&(t=e.className),{...i,caption:t,elements:xl(i.elements,e.elements)}}function xl(i,e){return e.map((t,n)=>{const a=i[n];if(!a||a.type!==t.type)return t;const s={...t,zoomX:a.zoomX,zoomY:a.zoomY};return a.textColor&&(s.textColor=a.textColor),a.backgroundColor&&(s.backgroundColor=a.backgroundColor),t.loopBody&&(s.loopBody=xl(a.loopBody??[],t.loopBody)),t.cases&&(s.cases=t.cases.map((r,o)=>{var l,u;return{...r,elements:xl(((u=(l=a.cases)==null?void 0:l[o])==null?void 0:u.elements)??[],r.elements)}})),s})}function hp(){return{start:p("web_visubot_blocks_start"),else:p("web_visubot_blocks_else"),empty:p("web_visubot_blocks_empty"),hint:p("web_visubot_blocks_hint"),trash:p("web_visubot_blocks_trash"),count:p("web_visubot_blocks_count"),countFrom:p("web_visubot_blocks_count_from"),countBelow:p("web_visubot_blocks_count_below"),countTo:p("web_visubot_blocks_count_to"),countAbove:p("web_visubot_blocks_count_above"),countStep:p("web_visubot_blocks_count_step"),repeat:p("web_visubot_blocks_repeat"),repeatTimes:p("web_visubot_blocks_repeat_times"),randomFrom:p("web_visubot_blocks_random_from"),randomTo:p("web_visubot_blocks_random_to"),keywords:{if:p("web_visubot_blocks_if"),while:p("web_visubot_blocks_while"),endless:p("web_visubot_blocks_endless"),do:p("web_visubot_blocks_do"),"!":p("web_visubot_blocks_not"),"&&":p("web_visubot_blocks_and"),"||":p("web_visubot_blocks_or"),break:p("web_visubot_blocks_break")}}}const fv={condition:"Bedingung",selector:"Wert",code:"Dein Code",start:"Startwert",step:"Schritt",end:"Ende"};function gp(i){const e=Ji(i,fv);if(e.problems.length>0||e.elements.length===0)return;const t=Gi({caption:"",elements:[]},e),n=new up({press:()=>{},editValue:()=>{},labels:{...hp(),hint:""}});return n.render(t,void 0,!0),n.element.hidden=!1,n.element.classList.add("bk-picture"),n.element}const Pe=(i,e=!1)=>``,me={newFile:Pe(''),open:Pe(''),save:Pe(''),undo:Pe(''),redo:Pe(''),cut:Pe(''),copy:Pe(''),paste:Pe(''),trash:Pe(''),share:Pe(''),book:Pe(''),code:Pe(''),play:Pe('',!0),step:Pe(''),indent:Pe(''),stop:Pe('',!0),folder:Pe(''),newFolder:Pe(''),diagram:Pe(''),fileText:Pe('')},bv="visustruct.learn.progress",no={"04-sequenz":"05-sequenz","05-variablen-und-datentypen":"04-variablen-und-datentypen","07-verzweigung":"08-verzweigung","08-bedingungen-verknuepfen":"09-bedingungen-verknuepfen","09-fallauswahl":"10-fallauswahl","10-while-schleife":"11-while-schleife","11-do-while-schleife":"12-do-while-schleife","12-for-schleife":"13-for-schleife","13-verschachteln":"14-verschachteln","14-arrays":"15-arrays","15-aufruf-und-aussprung":"18-aufruf-und-aussprung","16-aufruf-und-aussprung":"18-aufruf-und-aussprung","17-aufruf-und-aussprung":"18-aufruf-und-aussprung","17-suchen-und-sortieren":"19-suchen-und-sortieren","18-suchen-und-sortieren":"19-suchen-und-sortieren","16-simulation-und-schreibtischtest":"07-simulation-und-schreibtischtest","17-java":"20-java","18-java":"20-java","19-java":"20-java","18-python-und-javascript":"21-python-und-javascript","19-python-und-javascript":"21-python-und-javascript","20-python-und-javascript":"21-python-und-javascript","22-erstes-fenster":"28-erstes-fenster","23-knopf-im-fenster":"29-knopf-im-fenster","24-der-knopf-reagiert":"30-der-knopf-reagiert","23-erstes-fenster":"28-erstes-fenster","24-knopf-im-fenster":"29-knopf-im-fenster","25-der-knopf-reagiert":"30-der-knopf-reagiert","26-zahl-verdoppeln":"31-zahl-verdoppeln","27-klickzaehler":"32-klickzaehler","28-ampel":"33-ampel","29-notenrechner":"34-notenrechner","30-taschenrechner":"35-taschenrechner","31-zahlenraten":"36-zahlenraten","13-lautsprecher":"16-lautsprecher","14-bildschirm":"18-bildschirm","15-motoren":"20-motoren","16-stoppuhr-akku":"21-stoppuhr-akku","03-sensoren":"04-sensoren","04-bis-zur-wand":"05-bis-zur-wand","05-abstand":"06-abstand","06-linie":"07-linie","07-farben":"08-farben","08-tasten-joystick":"10-tasten-joystick","09-licht":"11-licht","10-lautstaerke":"12-lautstaerke","11-neigung":"13-neigung","12-leds":"14-leds","13-einzelne-leds":"15-einzelne-leds","14-lautsprecher":"16-lautsprecher","15-bildschirm":"18-bildschirm","16-motoren":"20-motoren","17-stoppuhr-akku":"21-stoppuhr-akku"};function xv(i){var n;const e={...i.lessons};for(const[a,s]of Object.entries(no))i.lessons[a]&&!i.lessons[s]&&(e[s]=i.lessons[a]),i.lessons[a]&&delete e[a];const t=(n=i.view)!=null&&n.lesson&&no[i.view.lesson]?{...i.view,lesson:no[i.view.lesson]}:i.view;return{...i,lessons:e,view:t}}function vv(){try{localStorage.removeItem(bv)}catch{}}const pp=()=>({lessons:{},exercises:{}});vv();let nt=pp();const vl=new Set;function ja(){for(const i of vl)i()}const Pm="visustruct-lernstand";function fp(i,e){return i||e||void 0}function wv(i,e){if(!i)return e;const t={...e.checks};for(const[n,a]of Object.entries(i.checks??{}))t[n]=a||t[n]||!1;return{step:Math.max(i.step??0,e.step??0),done:fp(i.done,e.done),checks:Object.keys(t).length?t:void 0,quiz:i.quiz||e.quiz?{...e.quiz,...i.quiz}:void 0}}function yv(i,e){const t={...i.lessons};for(const[a,s]of Object.entries(e.lessons))t[a]=wv(i.lessons[a],s);const n={...i.exercises};for(const[a,s]of Object.entries(e.exercises)){const r=i.exercises[a];n[a]=r?{done:fp(r.done,s.done),xml:r.xml??s.xml}:s}return{lessons:t,exercises:n}}const ve={lesson(i){return nt.lessons[i]??{step:0}},updateLesson(i,e){nt.lessons[i]={...ve.lesson(i),...e},ja()},exercise(i){return nt.exercises[i]??{}},updateExercise(i,e){nt.exercises[i]={...ve.exercise(i),...e},ja()},resetExercise(i){delete nt.exercises[i],ja()},view(){return nt.view},hasContent(){return Object.values(nt.lessons).some(i=>i.step>0||i.done||i.checks||i.quiz)||Object.keys(nt.exercises).length>0},setView(i){nt.view=i,ja()},onChange(i){return vl.add(i),()=>vl.delete(i)},toFile(){return JSON.stringify({format:Pm,version:1,saved:new Date().toISOString(),lessons:nt.lessons,exercises:nt.exercises,view:nt.view},null,2)},loadFile(i,e=!1){let t;try{t=JSON.parse(i)}catch{return!1}if((t==null?void 0:t.format)!==Pm)return!1;const n=xv({lessons:t.lessons??{},exercises:t.exercises??{},view:t.view});return nt=e?{...yv(nt,n),view:n.view??nt.view}:n,!0},clear(){nt=pp()}};function Om(i=new Date){const e=t=>String(t).padStart(2,"0");return`${i.getFullYear()}-${e(i.getMonth()+1)}-${e(i.getDate())}`}const Mm=i=>/^\d{4}-\d{2}-\d{2}$/.test(i);function zv(i,e){return i.kind!==e.kind?i.kind==="directory"?-1:1:Mm(i.name)&&Mm(e.name)?e.name.localeCompare(i.name):i.name.localeCompare(e.name,void 0,{numeric:!0,sensitivity:"base"})}function Iv(i,e){return i.replace(/\.(visustruct|strk|java)$/i,"").replace(/[\\/:*?"<>|\u0000-\u001f]/g,"-").replace(/\s+/g," ").trim().replace(/^\.+/,"")||e}function kv(i,e){const t=new Set([...e].map(a=>a.toLowerCase())),n=a=>!t.has(`${a}.visustruct`.toLowerCase())&&!t.has(`${a}.java`.toLowerCase());if(n(i))return i;for(let a=2;;a++)if(n(`${i} ${a}`))return`${i} ${a}`}function Sv(i){const e=i.replace(/[\\/:*?"<>|\u0000-\u001f]/g,"").trim();return e&&e!=="."&&e!==".."?e:void 0}const Tv=40,_v=1e5,Av=new Set(["forward","backward","turnLeft","turnRight","stop","drive"]);function jv(i,e,t,n,a){const s=new Wi(i,t),r=[...e],o=new Js(n);let l=0;for(let u=0;u<_v;u++){const{state:m}=s;for(;l0&&o.tick(G0),s.robotSensors={...o.sensors(),...Cv(a,u)};const c=()=>({outputs:[...m.outputLines],files:new Map(m.files),drive:m.robotActions.length?Bs(m.robotActions):void 0,...n?{atGoal:o.atGoal(),bumps:o.bumps}:{},led:o.leds.some(d=>d!==void 0),tone:m.robotActions.some(d=>d.method==="playTone"),screen:o.screen,actions:pg(m.robotActions.filter(d=>!Av.has(d.method)))});if(m.inputRequest){const d=r.shift();if(d===void 0)return{...c(),missingInput:!0};if(s.provideInput(d),m.inputErrorNotice)return{...c(),error:m.inputErrorNotice};continue}if(m.isHalted)return{...c(),error:m.notice};if(!s.canStep)return{...c(),unusedInputs:r.length||void 0};s.step()}return{outputs:[...s.state.outputLines],files:new Map(s.state.files),drive:Bs(s.state.robotActions),tooManySteps:!0}}function Cv(i,e){const t=Math.floor(e/Tv);return Object.fromEntries(Object.entries(i??{}).map(([n,a])=>[n,a[Math.min(t,a.length-1)]]))}function bp(i){return i===void 0||i===""?[]:i.replace(/\n$/,"").split(` -`)}function Ca(i,e){return i.length!==e.length?!1:i.every((t,n)=>Zm(t)===Zm(e[n]))}function Dv(i,e){const t=n=>n.join("").replace(/\s+/g,"");return i.length>0&&i.length{var d,h,g;const s=a.world?n==null?void 0:n(a.world):void 0;if(a.world&&!s)throw new Error(`Weltkarte „${a.world}“ fehlt.`);const r=jv(i,a.inputs,t,s,a.sensors),o=(a.files??[]).every(f=>{var b;return Ca(bp((b=r.files)==null?void 0:b.get(f.name)),f.lines)}),l=!a.drive||Ca(r.drive??[],a.drive),u=!a.world||r.bumps===0&&(!((d=s==null?void 0:s.goals)!=null&&d.length||(h=s==null?void 0:s.targets)!=null&&h.length)||r.atGoal===!0),m=(a.led===void 0||r.led===a.led)&&(a.tone===void 0||r.tone===a.tone)&&(a.screen===void 0||r.screen===a.screen)&&(a.drives===void 0||(((g=r.drive)==null?void 0:g.length)??0)>0===a.drives)&&(!a.actions||Ca(r.actions??[],a.actions)),c=!r.error&&!r.missingInput&&!r.tooManySteps&&Ca(r.outputs,a.expected)&&o&&l&&u&&m;return{test:a,result:r,passed:c}})}function Pv(i){const e=new Set,t=n=>{for(const a of n){e.add(a.type),a.loopBody&&t(a.loopBody);for(const s of a.cases??[])t(s.elements)}};return t(i.elements),e}function wl(i){const e=[S.forSchleife,S.whileSchleife,S.doUntilSchleife,S.endlosschleife];let t=0;for(const n of i){const a=Math.max(wl(n.loopBody??[]),...(n.cases??[]).map(s=>wl(s.elements)));t=Math.max(t,a+(e.includes(n.type)?1:0))}return t}class Hi extends Error{}function Ov(i,e){const t=[],n=[];for(const a of i){const s=a.trim();if(!s)continue;if(s.startsWith("?")){const o=s.slice(1).trim();Hs(o,Mv(t)),n.push(o);continue}const r=/^([A-Za-z_]\w*)\s*:\s*(?:(-?\d+)\s*\.\.\s*(-?\d+)|(boolean))$/.exec(s);if(!r)throw new Hi(`${e}: Unbekannte Zeile im Spiel: „${s}“`);t.push(r[4]?{name:r[1],kind:"boolean"}:{name:r[1],kind:"int",min:Number(r[2]),max:Number(r[3])})}if(!n.length)throw new Hi(`${e}: Das Spiel braucht mindestens eine Zeile „? bedingung“.`);return{vars:t,conditions:n}}function Mv(i){return Object.fromEntries(i.map(e=>[e.name,e.kind==="boolean"?!0:e.min]))}function Zv(i){const e=[],t=/\s*(?:(\d+)|(true|false)\b|([A-Za-z_]\w*)|(&&|\|\||==|!=|<=|>=|<|>|!|\(|\)))/y;let n=0;for(;nt[n],s=d=>{var h;return((h=a())==null?void 0:h.kind)==="op"&&a().value===d},r=()=>{throw new Hi(`Bedingung nicht lesbar: „${i}“`)},o=()=>{const d=t[n++]??r();if(d.kind==="num"||d.kind==="bool")return d.value;if(d.kind==="name"){if(!(d.value in e))throw new Hi(`Unbekannte Variable „${d.value}“ in „${i}“`);return e[d.value]}if(d.value==="("){const h=m();return s(")")||r(),n++,h}if(d.value==="!"){const h=o();return typeof h!="boolean"&&r(),!h}return r()},l=()=>{const d=o(),h=a();if((h==null?void 0:h.kind)==="op"&&["==","!=","<","<=",">",">="].includes(h.value)){n++;const g=o();switch(h.value){case"==":return d===g;case"!=":return d!==g}(typeof d!="number"||typeof g!="number")&&r();const[f,b]=[d,g];return h.value==="<"?f"?f>b:f>=b}return d},u=()=>{let d=l();for(;s("&&");){n++;const h=l();d=d&&h}return d},m=()=>{let d=u();for(;s("||");){n++;const h=u();d=d||h}return d},c=m();return n!==t.length||typeof c!="boolean"?r():c}function Lv(i,e){const t=[i],n=i.replace(/\b[A-Za-z_]\w*\b/g,r=>r in e?String(e[r]):r);n!==i&&t.push(n);const a=n.replace(/(-?\d+|true|false)\s*(==|!=|<=|>=|<|>)\s*(-?\d+|true|false)/g,r=>String(Hs(r,{})));a!==t[t.length-1]&&t.push(a);const s=String(Hs(i,e));return s!==t[t.length-1]&&t.push(s),t}function Wv(i,e){const t=[],n=new RegExp(`\\b${e}\\s*(?:==|!=|<=|>=|<|>)\\s*(-?\\d+)|(-?\\d+)\\s*(?:==|!=|<=|>=|<|>)\\s*${e}\\b`,"g");for(const a of i.matchAll(n)){const s=Number(a[1]??a[2]);t.push(s-1,s,s+1)}return t}function Ev(i,e,t,n=Math.random){const a=i.conditions.length,s=Math.floor(e*a/t),r=Math.min(a-1,s+(n()<.5?0:1)),o=i.conditions[r],l={};for(const u of i.vars){if(u.kind==="boolean"){l[u.name]=n()<.5;continue}const m=Wv(o,u.name).filter(c=>c>=u.min&&c<=u.max);l[u.name]=m.length&&n()<.6?m[Math.floor(n()*m.length)]:u.min+Math.floor(n()*(u.max-u.min+1))}return{condition:o,values:l,answer:Hs(o,l)}}function Nv(i){return Object.entries(i.values).filter(([e])=>new RegExp(`\\b${e}\\b`).test(i.condition))}const Fv=`--- -titel: Was ist ein Struktogramm? -bereich: Erste Schritte -kurz: Algorithmen als Folge klarer Schritte darstellen und lesen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Algorithmus ist -- ein Struktogramm lesen -- die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen -::: - -## Ein Algorithmus ist eine genaue Anleitung - -Ein **Algorithmus** beschreibt Schritt für Schritt, wie man eine Aufgabe löst. Jeder Schritt muss so eindeutig sein, dass ihn jemand ohne Nachfragen ausführen kann, ein Mensch genauso wie ein Computer. - -Du kennst Algorithmen aus dem Alltag: ein Kochrezept, eine Wegbeschreibung oder die Anleitung für einen Schrank. - -::: diagramm l1-tee.visustruct -Tee kochen als Struktogramm: Jeder Kasten ist ein Schritt. -::: - -::: angeberwissen -Määäh, wer hat das erste Programm geschrieben? Eine Frau, und zwar schon 1843, lange vor dem ersten Computer! **Ada Lovelace** beschrieb Schritt für Schritt, wie die *Analytical Engine* (Analytische Maschine) von Charles Babbage die sogenannten Bernoulli-Zahlen ausrechnen sollte. Das war ein echter Algorithmus für eine Maschine. Die Befehle sollten auf Lochkarten stehen (mehr dazu in Lektion 3). Gebaut wurde die Maschine allerdings nie. - -Ada hatte noch eine geniale Idee: Eine solche Maschine kann mehr als rechnen. Sie könnte auch mit Buchstaben, Zeichen oder sogar Musik arbeiten. Genau das machen Computer heute. Ihr zu Ehren heißt eine Programmiersprache **Ada**. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), Mathematikerin (Bild: Aquarell von Alfred Edward Chalon, um 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), gemeinfrei). -::: - -## So liest du ein Struktogramm - -Ein Struktogramm wird **von oben nach unten** gelesen. Jeder Kasten ist ein Schritt. Erst wenn ein Schritt fertig ist, kommt der nächste dran. - -Die Darstellung stammt von **Isaac Nassi und Ben Shneiderman** (1973). Deshalb heißt sie auch Nassi-Shneiderman-Diagramm. In Deutschland ist sie als DIN 66261 genormt. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -Links Isaac „Ike“ Nassi (Foto: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/deed.de), zugeschnitten), rechts Ben Shneiderman (Foto: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> Ein Struktogramm hat immer genau einen Anfang (oben) und ein Ende (unten). Es gibt keine Pfeile und keine Sprünge kreuz und quer. Programme, die kreuz und quer springen, nennt man **Spaghetticode**, weil sich ihr Ablauf verheddert wie Nudeln auf dem Teller. Mehr dazu in der nächsten Lektion. - -::: angeberwissen -Määäh, aufgepasst! 1972 waren Nassi und Shneiderman noch Doktoranden an der Stony Brook University bei New York, Shneiderman etwa 25 und Nassi etwa 23. Also quasi zwei Studis, die nebenbei Informatikgeschichte geschrieben haben. -::: - -## Baustein 1: Die Sequenz - -Jeder Algorithmus lässt sich aus nur **drei Grundbausteinen** zusammensetzen: **Sequenz**, [**Verzweigung**]{.blau} und [**Wiederholung**]{.rosa}. Die Farben helfen dir beim Zuordnen: [Anweisungen]{.gelb} sind gelb, Verzweigungen blau, Schleifen rosa. Die Sequenz hat keine eigene Farbe, sie ist einfach die Reihe von Anweisungen untereinander. Dieselben Farben siehst du im Editor und später im Code. Du kannst sie in den Einstellungen unter „Blöcke farbig“ ausschalten. In diesem und den nächsten beiden Schritten lernst du sie einzeln kennen. - -Die **Sequenz** ist der einfachste Baustein: Mehrere Anweisungen werden **nacheinander** ausgeführt, jede genau einmal. - -So sieht sie aus: - -- Jede Anweisung ist ein **einfaches Rechteck**. -- Die Rechtecke stehen ohne Lücke **untereinander**. -- Ausgeführt wird von oben nach unten, eine Anweisung nach der anderen. - -::: diagramm l1-sequenz.visustruct -Vier Anweisungen als Sequenz. Erst wird aufgestanden, dann Zähne geputzt und so weiter. -::: - -> Auch das Tee-Beispiel von oben ist eine Sequenz. - -::: angeberwissen -15 Minuten! So lange hat Shneiderman für die erste Skizze mit Sequenz, Verzweigung und Schleife gebraucht. Ich brauche länger, um mich zwischen Heu und Klee zu entscheiden. -::: - -## Baustein 2: Die Verzweigung - -Oft hängt der nächste Schritt von einer **Bedingung** ab. Eine Bedingung ist eine Frage, die man nur mit **ja oder nein** beantworten kann, zum Beispiel *Regnet es?* In VisuStruct heißt ja **true** (wahr) und nein **false** (falsch). - -Für solche Entscheidungen gibt es die **Verzweigung**. So sieht sie aus: - -- Oben steht die Bedingung in einem **Dreieck**. -- Darunter teilt sich der Kasten in **zwei Spalten**: links der Weg für **true**, rechts der Weg für **false**. -- Es wird **immer nur einer** der beiden Wege ausgeführt. Danach geht es unter der Verzweigung für beide gemeinsam weiter. - -::: diagramm l1-verzweigung.visustruct -Regnet es, wird der linke Weg ausgeführt, sonst der rechte. Danach geht es in beiden Fällen mit „Losgehen“ weiter. -::: - -> Soll in einem Fall nichts passieren, bleibt dieser Weg leer. Das zeigt das Zeichen **ø**. - -::: angeberwissen -Rechtschreib-Alarm! Es heißt Shneiderman, ohne c. Wer „Schneiderman“ schreibt, bekommt von mir einen sanften Hornstupser. Er selbst weist übrigens darauf hin, dass ihm ständig ein c untergeschmuggelt wird. -::: - -## Baustein 3: Die Wiederholung - -Soll etwas **mehrmals** passieren, schreibt man es nicht mehrmals hin. Stattdessen benutzt man eine **Wiederholung**, auch **Schleife** genannt. - -So sieht sie aus: - -- Oben steht die Bedingung, zum Beispiel *Teller nicht leer*. -- Die Schritte, die wiederholt werden, stehen **eingerückt** darunter. Oben und links bildet der Kasten ein **gekipptes L** um sie. -- Vor jedem Durchlauf wird die Bedingung geprüft. Solange sie **true** ist, werden die Schritte im gekippten L noch einmal ausgeführt. Ist sie **false**, geht es unter der Schleife weiter. - -::: diagramm l1-wiederholung.visustruct -Solange der Teller nicht leer ist, wird ein Bissen gegessen. Danach wird der Teller abgeräumt. -::: - -> Es gibt noch weitere Arten von Schleifen. Du lernst sie in späteren Lektionen kennen. - -## Alle drei zusammen - -Richtig nützlich werden die Bausteine, wenn man sie kombiniert. Das Diagramm enthält alle drei: eine Sequenz von oben nach unten, eine [Verzweigung]{.blau} und eine [Schleife]{.rosa}. Gelb sind die einfachen [Anweisungen]{.gelb}. Auch die Anweisungen in einer Verzweigung oder Schleife sind gelb. Genau genommen ist das ganze Diagramm eine Sequenz, denn auch Verzweigung und Schleife stehen in der Reihe von oben nach unten. - -::: diagramm l1-grundbausteine.visustruct -Die Verzweigung erkennst du am Dreieck, die Schleife am gekippten L. Alles zusammen steht in einer Sequenz. -::: - -Die Kästen können auch **ineinander geschachtelt** werden: In einer Schleife oder einem Zweig können wieder Sequenzen, Verzweigungen und Schleifen stehen. - -::: angeberwissen -Trostpflaster für alle mit einer verpatzten Arbeit: Die berühmte Fachzeitschrift *Communications of the ACM* hat den Struktogramm-Artikel abgelehnt. Laut Nassi meinte der Gutachter sogar, sie sollten alle Exemplare einsammeln und verbrennen. Autsch! Er erschien 1973 dann in einem kleineren Heft und wird heute tausendfach zitiert. Ätsch! -::: - -## Erst die Struktur, dann der Code - -Beim Programmieren passieren zwei Dinge gleichzeitig: Du musst dir den Ablauf überlegen und die Schreibweise der Programmiersprache beachten. Mit einem Struktogramm trennst du beides. - -1. Du denkst zuerst über die **Logik** nach: Welche Schritte, welche Bedingungen, welche Wiederholungen? -2. Danach übersetzt du die fertige Struktur in **Java, Python oder JavaScript**. VisuStruct kann dir den Code sogar erzeugen. - -Mit der **Simulation** kannst du dein Struktogramm außerdem Schritt für Schritt ausführen und prüfen, ob es wirklich tut, was es soll. - -## Spiel: Welcher Baustein? - -::: spiel frage -titel: Spiel: Welcher Baustein? -intro: Ludwig beschreibt Dinge aus seinem Alltag. Welcher der drei Bausteine steckt darin? Acht Runden. -antworten: Sequenz | Verzweigung | Wiederholung - -runde: -frage: „Zahnpasta auf die Bürste, Zähne putzen, Mund ausspülen.“ -richtig: Sequenz -> Drei Schritte, jeder genau einmal und immer in dieser Reihenfolge. - -runde: -frage: „Wenn es regnet, nimm den Schirm mit, sonst die Sonnenbrille.“ -richtig: Verzweigung -> Eine Bedingung entscheidet, welcher der zwei Wege genommen wird. - -runde: -frage: „Rühre, bis der Teig glatt ist.“ -richtig: Wiederholung -> Das Rühren wird wiederholt, solange der Teig noch nicht glatt ist. - -runde: -frage: „Laufe zehn Runden um den Sportplatz.“ -richtig: Wiederholung -> Dieselbe Runde wird zehnmal gelaufen. - -runde: -frage: „Schuhe an, Jacke an, Tür auf, losgehen.“ -richtig: Sequenz -> Ein Schritt nach dem anderen, nichts wird übersprungen oder wiederholt. - -runde: -frage: „Ist die Ampel grün, gehst du über die Straße.“ -richtig: Verzweigung -> Auch ohne „sonst“ ist es eine Verzweigung. Der Sonst-Zweig ist einfach leer. - -runde: -frage: „Würfle so lange, bis du eine Sechs hast.“ -richtig: Wiederholung -> Du weißt vorher nicht, wie oft. Gewürfelt wird, bis die Bedingung erfüllt ist. - -runde: -frage: „Sagt jemand ‚Herein‘, gehst du hinein. Sonst wartest du vor der Tür.“ -richtig: Verzweigung -> Die Antwort entscheidet zwischen zwei Wegen: hineingehen oder warten. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Algorithmus ist. → Ein Algorithmus ist eine genaue Anleitung -- Ich kann ein Struktogramm von oben nach unten lesen. → So liest du ein Struktogramm -- Ich kann die drei Grundbausteine Sequenz, Verzweigung und Wiederholung nennen. → Baustein 1: Die Sequenz -- Ich kann jeden Baustein im Struktogramm an seiner Form erkennen. → Alle drei zusammen -::: - -## Wissenstest - -::: quiz -? In welcher Richtung wird ein Struktogramm gelesen? -→ So liest du ein Struktogramm -- [ ] Von links nach rechts -- [x] Von oben nach unten -- [ ] Von unten nach oben -> Jeder Kasten ist ein Schritt, und die Schritte folgen von oben nach unten aufeinander. - -? Welcher Baustein gehört NICHT zu den drei Grundbausteinen? -→ Baustein 1: Die Sequenz -- [ ] Sequenz -- [ ] Verzweigung -- [x] Sprung -- [ ] Wiederholung -> Sprünge gibt es im Struktogramm nicht. Sequenz, Verzweigung und Wiederholung reichen für jeden Algorithmus. - -? Woran erkennst du im Struktogramm eine Verzweigung? -→ Alle drei zusammen -- [ ] An einem gekippten L -- [x] An einem Dreieck mit der Bedingung und zwei Spalten darunter -- [ ] An mehreren Rechtecken untereinander -> Das gekippte L gehört zur Schleife, Rechtecke untereinander sind eine Sequenz. - -? Wann wird der Inhalt einer Schleife noch einmal ausgeführt? -→ Alle drei zusammen -- [x] Solange die Bedingung true ist -- [ ] Nur, wenn die Bedingung false ist -- [ ] Immer genau zweimal -> Vor jedem Durchlauf wird die Bedingung geprüft. Ist sie false, geht es unter der Schleife weiter. - -? Was ist ein Algorithmus? -→ Ein Algorithmus ist eine genaue Anleitung -- [x] Eine eindeutige Schritt-für-Schritt-Anleitung zum Lösen einer Aufgabe -- [ ] Ein bestimmtes Programm in Java -- [ ] Ein Bild, das ein Programm erklärt -> Ein Algorithmus ist unabhängig von der Programmiersprache. Ein Struktogramm ist eine Möglichkeit, ihn aufzuschreiben. -::: -`,Rv=`--- -titel: PAP und Pseudocode -bereich: Erste Schritte -kurz: Algorithmen als Programmablaufplan und als Pseudocode darstellen und mit dem Struktogramm vergleichen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen PAP (englisch *flowchart*) lesen und selbst zusammenbauen -- erklären, warum Sprünge im PAP zu Fehlern führen -- einen einfachen Algorithmus als Pseudocode lesen -::: - -## Andere Darstellungen: Der PAP - -Es gibt noch zwei weitere Arten, Algorithmen darzustellen: den **Programmablaufplan**, kurz **PAP**, und den **Pseudocode**. Beide wirst du im Unterricht treffen. Auf Englisch heißt der PAP **flowchart**, auf Deutsch auch Flussdiagramm. Das englische Wort begegnet dir oft, zum Beispiel in Programmen und im Internet. - -Hier siehst du noch einmal unser Struktogramm zum Teetrinken aus der letzten Lektion. Als Nächstes schauen wir uns den PAP an, und zwar mit demselben Beispiel. - -::: diagramm l1-grundbausteine.visustruct -Das Tee-Beispiel als Struktogramm: erst die Tasse, dann die Verzweigung für den Zucker, dann die Schleife. -::: - -### Derselbe Algorithmus als PAP - -::: grafik l01-pap.svg -Das Tee-Beispiel als Programmablaufplan. Der Pfeil von „1 Minute warten“ zurück nach oben bildet die Schleife. -::: - -### Die Elemente - -Ein PAP besteht aus wenigen Symbolen, die mit **Pfeilen** verbunden sind. Er ist nach DIN 66001 genormt. - -- **Oval:** Start oder Ende. Jeder PAP hat genau einen Start und ein Ende. -- **Rechteck:** eine Anweisung, zum Beispiel *Tasse auf den Tisch stellen*. -- **Raute:** eine Verzweigung. In der Raute steht eine Frage, heraus führen zwei Pfeile: **ja** und **nein**. -- **Parallelogramm:** eine Ein- oder Ausgabe. Im Tee-Beispiel kommt sie nicht vor. -- **Pfeil:** zeigt, welcher Schritt als Nächstes kommt. - -Eine Schleife hat im PAP kein eigenes Symbol. Du erkennst sie daran, dass ein Pfeil **zurück nach oben** führt. - -### Die Vorteile - -- **Sehr gut lesbar**, auch für Leute, die noch nie einen PAP gesehen haben. Man folgt einfach den Pfeilen mit dem Finger. -- Flussdiagramme benutzt man nicht nur beim Programmieren, sondern überall, wo ein Ablauf erklärt wird: in Bedienungsanleitungen, bei der Fehlersuche („Leuchtet die Lampe? Nein: Steckt der Stecker?“) oder in Firmen für Arbeitsabläufe. - -## Übung: Kuchen backen als PAP - -Jetzt baust du selbst einen PAP. Ludwig backt einen Kuchen und geht so vor: - -Er gibt die Zutaten in die Schüssel und rührt den Teig. Ist der Teig noch nicht glatt, rührt er weiter. Ist er glatt, füllt er den Teig in die Form und backt den Kuchen 30 Minuten. Dann prüft er mit einem Holzstäbchen: Klebt noch Teig daran, backt er 5 Minuten weiter und prüft noch einmal. Ist das Stäbchen sauber, nimmt er den Kuchen heraus und ruft: „Der Kuchen ist fertig!“ - -::: pap -titel: Übung: Kuchen backen als PAP -auftrag: Die Plätze im Plan sind leer. Lege jedes Element an seinen Platz. Die Form des Platzes hilft dir. -start: Start -anweisung: Zutaten in die Schüssel geben -anweisung: Teig rühren -frage: Teig glatt? | nein -> Teig rühren -anweisung: Teig in die Form füllen -anweisung: Kuchen 30 Minuten backen -frage: Stäbchen sauber? | nein: 5 Minuten weiterbacken -anweisung: Kuchen herausnehmen -ausgabe: „Der Kuchen ist fertig!“ -ende: Ende -erklärung: Zwei Schleifen, zwei Pfeile zurück nach oben: Beim Rühren führt der Pfeil zurück zu „Teig rühren“, beim Backen von „5 Minuten weiterbacken“ zurück zur Frage. Die Ausgabe steht im Parallelogramm. -legende: ja -::: - -**Eigene PAPs zeichnen:** Mit **VisuFlow** zeichnest du deinen PAP ganz leicht selbst. Die Symbole liegen fertig in einer Leiste: Antippen hängt das nächste Symbol an und verbindet es gleich mit einem Pfeil. Zum Start kannst du das Tee-Beispiel laden. Deinen PAP speicherst du als Datei und exportierst ihn als Bild oder PDF, zum Beispiel für dein Heft oder eine Präsentation. [VisuFlow öffnen](https://visustruct.visucode.de/app/?visuflow) - -## Nachteile des PAP: Sprünge - -Ein Pfeil im PAP darf von jedem Symbol zu jedem anderen führen. Das klingt praktisch, ist aber gefährlich. - -Früher hat man Programme genau so geschrieben. Jede Zeile hatte eine **Nummer**, und der Befehl **GOTO**, auf Deutsch „gehe zu“, sprang zu einer Zeilennummer. Jedes GEHE ZU ist ein Pfeil aus dem PAP. Unser Tee-Beispiel sähe so aus: - -\`\`\` -10 Tasse auf den Tisch stellen -20 WENN kein Zucker gewünscht, GEHE ZU 40 -30 Zucker hineingeben -40 WENN Tee nicht zu heiß, GEHE ZU 70 -50 1 Minute warten -60 GEHE ZU 40 -70 Tee trinken -\`\`\` - -### Eine Zeile mehr, und schon springt es falsch - -Jetzt verbesserst du den Algorithmus: Nach dem Zucker soll der Teebeutel heraus. Du fügst die neue Zeile 40 ein, und alle Zeilen darunter rutschen eine Nummer weiter. Die Sprünge zeigen aber noch auf die alten Nummern: - -\`\`\` -10 Tasse auf den Tisch stellen -20 WENN kein Zucker gewünscht, GEHE ZU 40 -30 Zucker hineingeben -40 Teebeutel herausnehmen -50 WENN Tee nicht zu heiß, GEHE ZU 70 -60 1 Minute warten -70 GEHE ZU 40 -80 Tee trinken -\`\`\` - -Spiel es durch: Zeile 70 schickt dich nicht mehr zur Prüfung in Zeile 50, sondern zum Teebeutel. Und wenn der Tee kühl genug ist, springt Zeile 50 zu Zeile 70 und nicht mehr zu *Tee trinken*. Du nimmst also immer wieder den Teebeutel heraus und trinkst nie. Bei jeder Änderung musst du jedes GEHE ZU prüfen. In einem großen Programm mit Hunderten Sprüngen geht dabei leicht etwas schief. - -### Kreuz und quer: Spaghetticode - -Mit Pfeilen kann man auch mitten aus einer Schleife herausspringen oder von außen mitten in eine Schleife hinein. Solche **Aussprünge** und Einsprünge machen den Plan schnell unübersichtlich. Bei vielen Sprüngen kreuzen sich die Pfeile, bis der Plan aussieht wie ein Teller Spaghetti. Deshalb nennt man das **Spaghetticode**. Den Ablauf kann man kaum noch verfolgen, und Fehler verstecken sich gut. - -Im Struktogramm gibt es keine Pfeile. Fügst du einen Kasten ein, rutscht alles darunter einfach mit, und kein Sprung kann auf die falsche Stelle zeigen. Für das vorzeitige Verlassen einer Schleife gibt es dort einen eigenen, ordentlichen Kasten, den Aussprung. Den lernst du in Lektion 18 kennen. - -::: angeberwissen -1968 schrieb der Informatiker Edsger Dijkstra einen berühmten Brief: „Go To Statement Considered Harmful“, auf Deutsch „GOTO gilt als schädlich“. Seitdem baut man Programme nur noch aus Sequenz, Verzweigung und Wiederholung. In Java ist \`goto\` bis heute ein reserviertes Wort, benutzen darf man es aber nicht. Mäh, Spaghetti esse ich lieber, als sie zu programmieren. -::: - -## Pseudocode und Vergleich - -**Pseudocode** ist Text, der wie ein Programm aussieht, aber in normaler Sprache geschrieben ist. Feste Wörter wie WENN und SOLANGE zeigen die Bausteine, die **Einrückung** zeigt, was dazugehört. Sprünge gibt es auch hier nicht. Das ist unser Tee-Beispiel: - -\`\`\` -Tasse auf den Tisch stellen -WENN Zucker gewünscht DANN - Zucker hineingeben -ENDE WENN -SOLANGE Tee noch zu heiß - 1 Minute warten -ENDE SOLANGE -Tee trinken -\`\`\` - -Pseudocode hat **keine feste Schreibweise**. Manche schreiben FALLS statt WENN, andere setzen einen Pfeil für die Zuweisung (\`zahl ← 5\`) oder schreiben ganz ohne ENDE-Zeilen. Das ist alles erlaubt. Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. Pseudocode ist also keine weitere Programmiersprache, die man lernen muss. - -::: angeberwissen -Kommt dir Pseudocode bekannt vor? Die Programmiersprache Python sieht ihm sehr ähnlich, und das ist Absicht: Ihr Erfinder Guido van Rossum wollte, dass sich Programme fast wie normaler Text lesen. Auch Python zeigt mit der Einrückung, was zusammengehört. Aber Vorsicht: Python ist eine echte Programmiersprache mit festen Regeln. Ein Leerzeichen zu viel, und der Computer meckert. Pseudocode ist da großzügiger, so wie ich beim Frühstück. -::: - -### Im Vergleich - -- **Struktogramm:** Kästen ineinander, die Schleife ist ein gekipptes L. Die Struktur siehst du auf einen Blick, wilde Sprünge sind unmöglich. -- **PAP:** Symbole mit Pfeilen, die Schleife ist ein Pfeil zurück nach oben. Sehr anschaulich, aber bei großen Plänen schnell unübersichtlich, und Sprünge kreuz und quer sind möglich. -- **Pseudocode:** Text mit Einrückung, die Schleife steht zwischen SOLANGE und ENDE SOLANGE. Schnell geschrieben und schon nah am Programmcode. - -> Alle drei beschreiben denselben Algorithmus. Wenn du eine Form lesen kannst, lernst du die anderen schnell. - -::: angeberwissen -Nassi und Shneiderman wollten den Pfeilen an den Kragen! Ihr Artikel von 1973 hieß „Flowchart Techniques for Structured Programming“. Flowchart ist das englische Wort für Ablaufplan. Mit Pfeilen kann man nämlich kreuz und quer springen, bis der Plan aussieht wie ein Teller Spaghetti. Im Struktogramm geht das gar nicht. Mäh, so mag ich das: ordentlich, wie mein Stall. -::: - -## Spiel: Welche Darstellung? - -::: spiel frage -titel: Spiel: Welche Darstellung? -intro: Ludwig beschreibt eine Darstellung, ohne ihren Namen zu verraten. Ist es das Struktogramm, der PAP oder der Pseudocode? Acht Runden. -antworten: Struktogramm | PAP | Pseudocode - -runde: -frage: „Bei mir zeigt ein Pfeil zurück nach oben, wenn etwas wiederholt wird.“ -richtig: PAP -> Im PAP hat die Schleife kein eigenes Symbol. Ein Pfeil führt zurück nach oben. - -runde: -frage: „Meine Schleife sieht aus wie ein gekipptes L.“ -richtig: Struktogramm -> Oben und links umschließt der Kasten die Schritte, die wiederholt werden. - -runde: -frage: „Ich bestehe aus Wörtern wie WENN und SOLANGE, und die Einrückung zeigt, was dazugehört.“ -richtig: Pseudocode -> Pseudocode ist Text in normaler Sprache, der wie ein Programm aufgebaut ist. - -runde: -frage: „Meine Verzweigung ist eine Raute mit den Ausgängen ja und nein.“ -richtig: PAP -> Im Struktogramm steht die Bedingung dagegen in einem Dreieck. - -runde: -frage: „Bei mir können sich die Pfeile kreuz und quer verheddern wie Spaghetti.“ -richtig: PAP -> Weil Pfeile überallhin führen dürfen, ist im PAP Spaghetticode möglich. - -runde: -frage: „Ich habe keine feste Schreibweise. FALLS statt WENN ist bei mir auch erlaubt.“ -richtig: Pseudocode -> Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. - -runde: -frage: „Die Bedingung steht oben in einem Dreieck, darunter teile ich mich in zwei Spalten.“ -richtig: Struktogramm -> So sieht die Verzweigung im Struktogramm aus: links true, rechts false. - -runde: -frage: „Fügst du einen Kasten ein, rutscht alles darunter einfach mit. Kein Sprung kann danebengehen.“ -richtig: Struktogramm -> Im Struktogramm gibt es keine Pfeile und keine Sprünge, deshalb kann nichts auf die falsche Stelle zeigen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Elemente eines PAP benennen und einen einfachen PAP lesen. → Andere Darstellungen: Der PAP -- Ich kann einen einfachen Algorithmus selbst als PAP zusammenbauen. → Übung: Kuchen backen als PAP -- Ich kann erklären, warum Sprünge mit GOTO zu Fehlern und Spaghetticode führen. → Nachteile des PAP: Sprünge -- Ich kann einen einfachen Algorithmus als Pseudocode lesen. → Pseudocode und Vergleich -::: - -## Wissenstest - -::: quiz -? Woran erkennst du in einem Programmablaufplan (PAP) eine Schleife? -→ Andere Darstellungen: Der PAP -- [ ] An einem gekippten L -- [x] An einem Pfeil, der zurück nach oben führt -- [ ] Am Wort SOLANGE -> Das gekippte L gehört zum Struktogramm, SOLANGE zum Pseudocode. - -? Ordne jedem Symbol im PAP seine Bedeutung zu. -→ Andere Darstellungen: Der PAP -= Oval -> Start oder Ende -= Rechteck -> Anweisung -= Raute -> Verzweigung -= Parallelogramm -> Ein- oder Ausgabe -> Die Raute hat zwei Ausgänge: ja und nein. - -? Wie heißt der Programmablaufplan auf Englisch? -→ Andere Darstellungen: Der PAP -- [x] Flowchart -- [ ] Structure chart -- [ ] Pseudocode -> Auf Deutsch sagt man auch Flussdiagramm. Structure chart ist das Struktogramm. - -? Du fügst in ein Programm mit GOTO eine neue Zeile ein. Was kann passieren? -→ Nachteile des PAP: Sprünge -- [x] Die Sprünge zeigen auf die falschen Zeilen, weil sich die Nummern verschoben haben -- [ ] Nichts, die Sprünge passen sich von selbst an -- [ ] Das Programm wird automatisch schneller -> Jedes GEHE ZU zeigt auf eine feste Zeilennummer. Rutschen die Zeilen weiter, springt das Programm an die falsche Stelle. - -? Was zeigt im Pseudocode, welche Zeilen zu einer Schleife gehören? -→ Pseudocode und Vergleich -- [ ] Die Farbe -- [x] Die Einrückung und das ENDE SOLANGE -- [ ] Ein Pfeil -> Alles zwischen SOLANGE und ENDE SOLANGE ist eingerückt und wird wiederholt. -::: -`,Jv=`--- -titel: Der Editor -bereich: Erste Schritte -kurz: Elemente einfügen, Text eingeben, verschieben und löschen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Element aus dem Bereich Elemente einfügen und einen Platzhalter ersetzen -- den Text eines Kastens ändern und zwischen Anweisung, Eingabe und Ausgabe umschalten -- Kästen verschieben, kopieren, löschen und Schritte rückgängig machen -::: - -## Die Oberfläche - -VisuStruct hat zwei Editoren: den [Struktogramm-Editor](#zeige-editor), um den es in dieser Lektion geht, und rechts den Bereich [Code](#zeige-codelive) für Java-Code (ab Lektion 4). Ist der Struktogramm-Editor eingeklappt, öffnet ihn diese Lektion von selbst. Die blauen Wörter zeigen dir die Bereiche, auch wenn sie gerade eingeklappt sind: Fährst du mit der Maus darüber, öffnet sich der Bereich neben dem Lernbereich und leuchtet auf. Klickst du darauf (oder tippst es an), bleibt er offen. - -Der Struktogramm-Editor besteht aus mehreren Bereichen. Fahre mit der Maus über ein blaues Wort oder tippe es an, dann leuchtet der Bereich im Editor auf, eingeklappte Bereiche wie die Elemente öffnen sich dabei. - -- **Ganz oben die [Leiste](#zeige-leiste):** Neu, Öffnen und Speichern. Ganz rechts das Zahnrad für die [Einstellungen](#zeige-einstellungen): Textgröße und Sprache. -- **Darunter die [Werkzeuge](#zeige-werkzeuge)** über den Elementen und dem Struktogramm: Rückgängig, Ausschneiden, Kopieren, Einfügen, Löschen, Exportieren, Code und Simulation. Die Knöpfe zeigen nur Symbole. Hältst du die Maus darüber, erscheint ihr Name. -- **Links die [Elemente](#zeige-elemente):** Hier liegen alle Bausteine, die du ins Struktogramm ziehen kannst. Manche nennen diesen Bereich auch Palette. -- **In der Mitte das [Struktogramm](#zeige-struktogramm):** Ein neues Diagramm enthält nur einen leeren Platzhalter, erkennbar an **ø**. -- **Unten der [Objektinspektor](#zeige-objektinspektor):** Hier bearbeitest du den Text des ausgewählten Elements. Ist er dir zu klein oder zu groß, ziehst du die Linie mit dem Griff darüber nach oben oder unten. - -> Diesen Lernbereich kannst du jederzeit mit dem [Seitenleisten-Symbol](#zeige-lernen) oben in diesem Bereich einklappen. Dann bleibt links nur die schmale Leiste „🎓 Lernen“, ein Klick darauf öffnet ihn wieder. Er bleibt neben dem Editor offen, damit du gleich ausprobieren kannst, was du liest. Genauso gibt es links die Spalte [📁 Dateien](#zeige-dateien) für deine gespeicherten Programme (Lektion 3) und rechts den Bereich [Code](#zeige-codelive) für Java (ab Lektion 4). - -## Ein Element einfügen - -Ziehe ein Element aus dem Bereich **Elemente** auf das Struktogramm. Beim Ziehen zeigt eine blaue Linie, wo es landet: - -- **Auf den Platzhalter ø:** Das Element ersetzt den Platzhalter. -- **Auf die obere Hälfte eines Kastens:** Das Element kommt davor. -- **Auf die untere Hälfte eines Kastens:** Das Element kommt dahinter. - -::: grafik l02-einfuegen.svg -Wohin du ziehst, entscheidet, wo das neue Element landet. -::: - -Es geht auch ohne Ziehen: **Klicke** auf ein Element im Bereich Elemente (oder tippe es an). Dann wird es hinter dem ausgewählten Kasten eingefügt. Ist der Platzhalter **ø** ausgewählt, ersetzt es ihn. - -Mit der Tastatur springst du mit der **Tab**-Taste zu einem Element und drückst **Enter**. Das neue Element landet dann ebenfalls hinter dem ausgewählten Kasten. - -::: angeberwissen -Kästen packen und herumziehen, so wie du gerade, hat einen schicken Namen: **direkte Manipulation**. Den Begriff hat 1982 Ben Shneiderman geprägt, genau, der vom Struktogramm. Der Mann war fleißiger als ich beim Grasen. -::: - -## Text eingeben - -Klicke einen Kasten an. Er wird blau umrandet, und im Objektinspektor unter dem Diagramm erscheint sein Text. Was du dort tippst, siehst du sofort im Diagramm. - -Schneller geht es mit einem **Doppelklick** auf den Kasten, auf dem Tablet mit einem **Doppeltipp**. Dann tippst du den Text direkt im Struktogramm. **Enter** übernimmt ihn, **Esc** bricht ab. Mit der Tastatur wählst du den Kasten aus und drückst **Enter**. - -Der Kasten aus dem Bereich Elemente heißt **Anweisung**. Oben wählst du, welche Art von Anweisung er sein soll: - -- **Anweisung:** etwas berechnen oder festlegen, z. B. \`int zahl = 5\`. Wie das geht, lernst du in Lektion 4. -- **Eingabe:** beim Ausführen einen Wert abfragen. Das kommt in Lektion 6. -- **Ausgabe:** etwas anzeigen. Im Diagramm steht dann \`output:\` davor, z. B. \`output: "Hallo"\`. - -Für den Anfang brauchst du nur die Ausgabe. Texte, die genau so angezeigt werden sollen, schreibst du in **Anführungszeichen**. - -::: diagramm l2-ausgabe.visustruct -Zwei Ausgaben nacheinander. Öffne das Diagramm im Editor und starte die [Simulation](#zeige-simulation) in den Werkzeugen, um die Ausgabe zu sehen. Genauer lernst du sie in Lektion 7 kennen. -::: - -## Verschieben, kopieren, löschen - -- **Verschieben:** Einen vorhandenen Kasten mit der Maus an eine neue Stelle ziehen. -- **Kopieren und Einfügen:** Kasten auswählen, dann Strg+C und Strg+V (am Mac ⌘C und ⌘V). Die Kopie kommt hinter den ausgewählten Kasten. Ohne Tastatur, zum Beispiel auf dem Tablet, tippst du in den [Werkzeugen](#zeige-werkzeuge) auf **Kopieren** und **Einfügen**. -- **Löschen:** Kasten auswählen und Entf drücken oder in den Werkzeugen auf den [Mülleimer](#zeige-loeschen) „Auswahl löschen“ klicken. -- **Rückgängig:** Strg+Z macht den letzten Schritt rückgängig, Strg+Umschalt+Z stellt ihn wieder her (am Mac ⌘Z und ⌘⇧Z). Ohne Tastatur tippst du in den Werkzeugen auf [Rückgängig](#zeige-rueckgaengig). - -## Übung: Hallo Welt - -::: übung l2-hallo -titel: Hallo Welt -auftrag: Baue ein Struktogramm, das **Hallo Welt** ausgibt. -Ziehe eine **Anweisung** aus den Elementen auf den Platzhalter **ø** und klicke den Kasten an. Im Objektinspektor unter dem Struktogramm stehen oben drei Knöpfe: Anweisung, Eingabe und Ausgabe. Klicke auf **Ausgabe**. Schreibe dann in das Textfeld darunter \`"Hallo Welt"\`, mit Anführungszeichen. -test: - -> Hallo Welt -braucht: anweisung -tipp: Du brauchst nur einen einzigen Kasten. Der Text, der erscheinen soll, steht im Objektinspektor im Eingabefeld. -tipp: Achte auf die Anführungszeichen. Ohne sie sucht VisuStruct nach einer Variablen mit dem Namen Hallo. -lösung: l2-hallo-loesung.visustruct -::: - -Klicke auf **Prüfen**, wenn du fertig bist. VisuStruct führt dein Struktogramm aus und vergleicht die Ausgabe. - -## Übung: Zwei Zeilen - -::: übung l2-zwei-zeilen -titel: Zwei Zeilen -auftrag: Gib zuerst **Hallo** und danach **Ich lerne Struktogramme** aus, jeweils in einer eigenen Zeile. -test: - -> Hallo | Ich lerne Struktogramme -tipp: Zwei Zeilen heißt: zwei Ausgaben. Die Kästen stehen untereinander und werden von oben nach unten ausgeführt. -tipp: Jede Ausgabe ist ein eigener Kasten. Ziehe die zweite Anweisung auf die untere Hälfte der ersten. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Spiel: Editor-Profi - -::: spiel frage -titel: Spiel: Editor-Profi -intro: Ludwig testet, ob du den Editor im Griff hast. Sieben Runden. - -runde: -frage: Du ziehst eine Anweisung auf den Platzhalter **ø**. Was passiert? -- [x] Die Anweisung ersetzt den Platzhalter. -- Die Anweisung kommt hinter den Platzhalter. -- Nichts, auf ø darf man nichts ziehen. -> Der Platzhalter hält nur den Platz frei. Das neue Element nimmt ihn ein. - -runde: -frage: Du ziehst ein Element auf die **obere** Hälfte eines Kastens. Wo landet es? -- [x] davor -- dahinter -- Es ersetzt den Kasten. -> Obere Hälfte heißt davor, untere Hälfte heißt dahinter. Die blaue Linie zeigt es dir beim Ziehen. - -runde: -frage: Du ziehst ein Element auf die **untere** Hälfte eines Kastens. Wo landet es? -- davor -- [x] dahinter -- ganz am Ende des Struktogramms -> Untere Hälfte heißt: direkt hinter diesen Kasten. - -runde: -frage: Du hast aus Versehen einen Kasten gelöscht. Was holt ihn zurück? -- Strg+C / ⌘C -- [x] Strg+Z / ⌘Z -- Strg+V / ⌘V -> Strg+Z / ⌘Z macht den letzten Schritt rückgängig. Strg+C / ⌘C kopiert, Strg+V / ⌘V fügt ein. - -runde: -frage: Beim Ausführen soll genau **Hallo** erscheinen. Was steht im Kasten? -- [x] output: "Hallo" -- output: Hallo -- "Hallo" -> Text, der genau so angezeigt werden soll, steht in Anführungszeichen. Das \`output:\` setzt der Editor davor, wenn du die Art Ausgabe wählst. - -runde: -frage: Wo änderst du den Text des ausgewählten Kastens? -- in den Elementen -- [x] im Objektinspektor -- in der Leiste ganz oben -> Der Objektinspektor unter dem Diagramm zeigt den Text des ausgewählten Kastens. - -runde: -frage: Du klickst im Bereich Elemente auf ein Element, ohne zu ziehen. Wo landet es? -- [x] hinter dem ausgewählten Kasten -- ganz am Anfang -- ganz am Ende -> Ohne Ziehen (Klick oder Tab und Enter) kommt das neue Element hinter den Kasten, der gerade ausgewählt ist. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Element aus dem Bereich Elemente einfügen und einen Platzhalter ersetzen. → Ein Element einfügen -- Ich kann den Text eines Kastens ändern und zwischen Anweisung, Eingabe und Ausgabe umschalten. → Text eingeben -- Ich kann Kästen verschieben, kopieren, löschen und Schritte rückgängig machen. → Verschieben, kopieren, löschen -::: - -## Wissenstest - -::: quiz -? Was bedeutet das Zeichen ø in einem Kasten? -→ Ein Element einfügen -- [ ] Der Kasten enthält einen Fehler. -- [x] Hier ist noch ein leerer Platzhalter. -- [ ] Das Programm endet hier. -> Ein Platzhalter wird ersetzt, wenn du ein Element darauf ziehst. - -? Du ziehst eine Anweisung auf die untere Hälfte eines Kastens. Wo landet sie? -→ Ein Element einfügen -- [ ] Davor -- [x] Dahinter -- [ ] Der Kasten wird ersetzt. - -? Wie gibst du den Text Guten Tag genau so aus? -→ Text eingeben -- [ ] output: Guten Tag -- [x] output: "Guten Tag" -- [ ] input: "Guten Tag" -> Ohne Anführungszeichen wird Guten Tag als Name einer Variablen verstanden. -::: -`,Gv=`--- -titel: Speichern, Öffnen, Exportieren -bereich: Erste Schritte -kurz: Dateien sichern, deinen Workspace nutzen, mehrere Diagramme öffnen und als Bild oder PDF weitergeben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einem Diagramm einen Titel geben -- ein Diagramm als .visustruct-Datei speichern und wieder öffnen -- ein Diagramm als Bild (PNG) oder PDF exportieren -- ein zweites Diagramm in einem neuen Reiter öffnen -- meinen Workspace \`VisuStruct-Dateien\` anlegen -- ein Programm in meinem Workspace speichern -::: - -## Die Datei .visustruct - -Struktogramme werden als **.visustruct**-Datei gespeichert. Dasselbe Format nutzen alle VisuStruct-Versionen: im Browser, auf Mac, iPad und iPhone und die Java-App am Rechner. Du kannst also in der Schule im Browser anfangen und zu Hause in der App weitermachen. - -::: angeberwissen -Zu Nassis Studienzeit gab es keine Dateien zum Speichern, sondern Lochkarten. Nassi erzählt, dass ein Date am Samstagabend so aussah: seine spätere Frau saß mit ihm im Rechenzentrum und stanzte seine Lochkarten. Romantik, Informatik-Style! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -So sah eine Lochkarte aus. Eine Karte war eine Zeile Programm, hier \`PRINT *, 'HALLO WELT'\` in der Sprache Fortran. Jede Spalte ist ein Zeichen, die Löcher darin sind sein Code. Ein ganzes Programm war ein Stapel solcher Karten. Fiel er herunter, musste man alles wieder sortieren. -::: - -::: angeberwissen -Ein Computerpionier aus unserer Gegend: **Konrad Zuse**. Am 12. Mai 1941 stellte er in Berlin die **Z3** vor, den ersten funktionsfähigen programmgesteuerten Digitalrechner der Welt. Sie rechnete schon im **Binärsystem**, also nur mit 0 und 1, mit rund 2000 Relais (elektrischen Schaltern). Ihr Programm stand nicht auf Lochkarten, sondern als Löcher in einem gebrauchten Kinofilm. Danach erfand Zuse den **Plankalkül**, eine der ersten Programmiersprachen überhaupt. Später lebte er bis zu seinem Tod 1995 in **Hünfeld** bei Fulda. Im Konrad-Zuse-Museum dort stehen Nachbauten der Z3. Mein Ausflugstipp für eure Klasse! Wann es offen hat, steht auf der Webseite der Stadt Hünfeld. -::: - -::: grafik l03-zuse-lochstreifen.svg -So ungefähr sah Zuses Programmstreifen aus: ein alter Kinofilm mit gestanzten Löchern. Jede Spalte ist ein Befehl aus 8 Stellen. Ein Loch heißt 1, kein Loch heißt 0. Die Maschine las den Streifen Befehl für Befehl. -::: - -## Speichern und Öffnen - -- **[Speichern](#zeige-speichern)** (Strg+S, am Mac ⌘S): Das Diagramm wird als Datei gespeichert. Ist die Spalte **Dateien** eingerichtet (siehe unten), landet es dort. Sonst fragt der Browser nach dem Ort oder legt die Datei in den Download-Ordner. -- **[Öffnen](#zeige-oeffnen)** (Strg+O, am Mac ⌘O): Eine gespeicherte Datei wieder laden. Du kannst eine Datei auch einfach ins Browserfenster ziehen. - -> Deine Übungen merkt sich der Browser automatisch. Das klappt aber nur in diesem Browser auf diesem Gerät. Wichtige Arbeiten solltest du deshalb immer zusätzlich als Datei speichern. - -## Die Spalte Dateien - -Links neben dem Lernbereich findest du die Spalte [📁 Dateien](#zeige-dateien). Dort sammelst du alle deine Programme an einem Ort, deinem **Workspace** (Arbeitsbereich). - -**Jetzt du:** Richte deinen Workspace jetzt gleich ein, bevor du weiterliest. Ab heute speicherst du alle deine Programme dort. - -1. Lege einen Workspace an: Öffne die Spalte und klicke auf **Ordner wählen**. Gehe im Fenster zu deinen **Dokumenten**, lege dort mit **Neuer Ordner** einen Ordner namens \`VisuStruct-Dateien\` an und wähle ihn aus. Erlaube VisuStruct, den Ordner zu benutzen. Das geht in Chrome und Edge. Auf dem iPad und in anderen Browsern klickst du stattdessen auf **Im Browser speichern**. -2. Speichere wie gewohnt. VisuStruct legt in deinem Workspace automatisch einen Ordner mit dem heutigen Datum an, zum Beispiel \`2026-10-23\`, und speichert das Programm darin. Der Dateiname kommt aus dem Titel. - -So sieht dein Workspace danach aus: - -\`\`\`text -VisuStruct-Dateien ← Workspace -└── 2026-10-23 ← Tag - └── Fakultaet.visustruct -\`\`\` - -Ein Klick auf ein Programm in der Spalte öffnet es wieder. So findest du später jede Stunde schnell wieder. - -Auch dein **Lernstand** landet im Workspace: in der Datei \`learnProgress.json\` stehen deine erledigten Lektionen und Übungen. Öffnest du den Workspace in einem anderen Browser, ist dein Lernstand wieder da. - -> Warum ein echter Ordner? Dein Lernstand und deine Programme liegen dann als Dateien darin. Den Ordner kannst du mitnehmen, zum Beispiel auf einem USB-Stick oder in deinem Cloud-Ordner wie OneDrive. So machst du zu Hause an einem anderen Rechner genau da weiter, wo du aufgehört hast. Speicherst du nur im Browser, bleibt alles in diesem einen Browser auf diesem Gerät. Löscht jemand die Browserdaten, ist auch dein Lernstand weg. - -## Mehrere Diagramme in Reitern - -Über dem Struktogramm siehst du die [Reiter](#zeige-reiter). Mit **+** oder [Neu](#zeige-neu) öffnest du ein weiteres, leeres Diagramm. Ein Punkt • vor dem Namen zeigt, dass es ungespeicherte Änderungen gibt. - -Den [Titel](#zeige-titel) eines Diagramms trägst du in das Feld über dem Struktogramm ein. Er erscheint auch beim Exportieren und wird als Dateiname vorgeschlagen. - -## Weitergeben: Bild, PDF oder Datei - -Über [Exportieren](#zeige-exportieren) bei den Werkzeugen (Symbol mit Pfeil nach oben) gibst du dein Struktogramm weiter: - -- **Bild (PNG):** zum Einfügen in ein Arbeitsblatt oder eine Präsentation -- **Vektorgrafik (SVG):** bleibt in jeder Größe scharf -- **PDF (A4):** eine Seite zum Abgeben oder Ausdrucken -- **VisuStruct-Datei (.visustruct):** zum Weitergeben an jemanden, der dein Struktogramm in VisuStruct öffnen und weiterbearbeiten soll, zum Beispiel per Mail oder über die Lernplattform -- **Drucken** (Strg+P, am Mac ⌘P): direkt auf den Drucker - -Unter dem [Zahnrad](#zeige-textgroesse) oben rechts stellst du bei Textgröße die Schriftgröße ein. Ein größerer Text wirkt auch beim Exportieren als Bild. - -## Ausprobieren - -Öffne eine deiner Übungen aus Lektion 2 und probiere Folgendes aus: - -1. Gib dem Diagramm im Feld [Titel](#zeige-titel) über dem Struktogramm einen Namen, zum Beispiel \`Hallo Welt\`. -2. [Speichere](#zeige-speichern) es in deinem Workspace. Schau in der Spalte [📁 Dateien](#zeige-dateien) nach: Steht es im Ordner von heute? -3. Öffne mit **+** einen zweiten Reiter und klicke dort in der Spalte Dateien auf dein Programm. Es öffnet sich wieder. -4. [Exportiere](#zeige-exportieren) es als **Bild (PNG)** und als **PDF (A4)**. Öffne beide Dateien und vergleiche sie. - -Klappt alles? Dann weißt du, wo deine Programme liegen, und kannst sie weitergeben. - -## Spiel: Wie gibst du es weiter? - -::: spiel frage -titel: Spiel: Wie gibst du es weiter? -intro: Ludwig will sein Struktogramm weitergeben. Welches Format passt am besten? Sechs Runden. -frage: Welches Format passt? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: Du willst morgen an deinem Struktogramm weiterarbeiten. -richtig: .visustruct -> Nur die .visustruct-Datei kann VisuStruct wieder öffnen und bearbeiten. - -runde: -frage: Das Struktogramm soll als Bild in eine Präsentation. -richtig: PNG -> Ein PNG-Bild lässt sich in jede Präsentation und jedes Arbeitsblatt einfügen. - -runde: -frage: Die Lehrkraft will deine Abgabe als eine A4-Seite ausdrucken. -richtig: PDF -> Das PDF ist genau eine A4-Seite zum Abgeben oder Ausdrucken. - -runde: -frage: Das Struktogramm kommt auf ein Plakat und soll auch riesig noch scharf sein. -richtig: SVG -> Eine Vektorgrafik bleibt in jeder Größe scharf. Ein PNG wird beim Vergrößern pixelig. - -runde: -frage: Deine Freundin soll das Struktogramm in ihrer VisuStruct-App auf dem iPad ändern. -richtig: .visustruct -> Alle VisuStruct-Versionen öffnen dieselbe .visustruct-Datei: im Browser, auf Mac, iPad und iPhone. - -runde: -frage: Du willst ein Bild schnell in einen Chat schicken. -richtig: PNG -> Ein PNG kann jeder sofort ansehen, ohne VisuStruct. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einem Diagramm einen Titel geben. → Ausprobieren -- Ich kann ein Diagramm als .visustruct-Datei speichern und wieder öffnen. → Speichern und Öffnen -- Ich kann ein Diagramm als Bild (PNG) oder PDF exportieren. → Weitergeben: Bild, PDF oder Datei -- Ich kann ein zweites Diagramm in einem neuen Reiter öffnen. → Mehrere Diagramme in Reitern -- Ich kann meinen Workspace \`VisuStruct-Dateien\` anlegen. → Die Spalte Dateien -- Ich kann ein Programm in meinem Workspace speichern. → Die Spalte Dateien -::: - -## Wissenstest - -::: quiz -? Welche Datei kannst du in der Mac-App und in der Java-App wieder öffnen? -→ Speichern und Öffnen -- [x] Die .visustruct-Datei -- [ ] Das exportierte PNG-Bild -- [ ] Das exportierte PDF -> Bilder und PDFs sind nur zum Ansehen. Weiterbearbeiten kannst du nur die .visustruct-Datei. - -? Deine Übungen werden im Browser gemerkt. Warum solltest du trotzdem speichern? -→ Speichern und Öffnen -- [ ] Weil die Übungen sonst nicht geprüft werden -- [x] Weil der Browser die Daten nur auf diesem Gerät behält und sie verloren gehen können -- [ ] Weil man ohne Speichern nicht exportieren kann - -? Du speicherst heute ein Programm in deinem Workspace. Wo landet es? -→ Die Spalte Dateien -- [ ] Im Download-Ordner -- [x] Im Workspace in einem Ordner mit dem heutigen Datum -- [ ] Direkt im Workspace ohne Unterordner -> VisuStruct legt für jeden Tag automatisch einen Datumsordner an. So bleiben die Programme nach Stunden sortiert. -::: -`,Hv=`--- -titel: Variablen und Datentypen -bereich: Grundlagen -kurz: Werte speichern, die Datentypen int, double, String und boolean kennen und rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable mit Datentyp, Namen und Wert anlegen -- erklären, dass Java Groß- und Kleinschreibung unterscheidet (\`zahl\` ist nicht \`Zahl\`) -- die Datentypen int, double, String und boolean unterscheiden -- erklären, dass = eine Zuweisung ist und kein Vergleich -- mit + - * / % rechnen und die ganzzahlige Division anwenden -- Variablen in Java anlegen und ausgeben -::: - -## Was ist eine Variable? - -Eine **Variable** ist wie eine beschriftete Kiste. Sie hat einen **Namen**, einen **Datentyp** und einen **Wert**. - -\`\`\` -int alter = 14 -\`\`\` - -Hier heißt die Variable \`alter\`, ihr Datentyp ist \`int\` (ganze Zahl), und ihr Wert ist 14. Über den Namen kannst du den Wert später wieder benutzen. - -::: grafik l04-variable.svg -Die Variable als Kiste: Name auf dem Etikett, Datentyp als Aufkleber, Wert darin. -::: - -> Variablennamen beginnen mit einem **Kleinbuchstaben** und enthalten keine Leerzeichen. Gute Namen sagen, was drinsteht: \`preis\` ist besser als \`p\`. - -::: angeberwissen -Im September 1999 ging die Mars-Sonde Mars Climate Orbiter verloren. Ein Team rechnete die Schubkraft in amerikanischen Einheiten (Pfund), das andere erwartete metrische Einheiten (Newton). In den Daten stand nur die Zahl, nicht die Einheit. Die Sonde flog zu tief in die Mars-Atmosphäre und verglühte, Schaden rund 125 Millionen Dollar. Ein sprechender Name wie \`impulsInNewtonSekunden\` hätte gesagt, was drinsteht. Ich schreibe auf mein Heufach übrigens immer \`heuInKilo\`. -::: - -Besteht ein Name aus mehreren Wörtern, schreibst du sie zusammen und beginnst jedes weitere Wort mit einem Großbuchstaben: \`preisMitSteuer\`, \`anzahlSchueler\`. Diese Schreibweise heißt **CamelCase**. - -::: angeberwissen -CamelCase heißt so, weil die Großbuchstaben in der Mitte wie die Höcker eines Kamels aus dem Wort ragen: \`preisMitSteuer\` hat zwei Höcker. Ich als Geißbock habe übrigens null Höcker, dafür zwei Hörner. Leider hat noch niemand die GeißbockSchreibweise erfunden. -::: - -::: grafik l04-camelcase.svg -Die Großbuchstaben M und S ragen heraus wie die zwei Höcker des Kamels. -::: - -## Groß- und Kleinschreibung - -Java unterscheidet **Groß- und Kleinschreibung**. Auf Englisch heißt das **case-sensitive**. Für Java sind \`zahl\`, \`Zahl\` und \`ZAHL\` drei verschiedene Namen, also drei verschiedene Kisten: - -\`\`\` -int zahl = 4 -int Zahl = 6 -\`\`\` - -Hier gibt es zwei Variablen. Schreibst du später \`ZAHL\`, findet Java keine Variable mit diesem Namen. Das Programm läuft nicht, und du bekommst eine Fehlermeldung. - -> Das gilt auch für Java-Wörter: \`String\` und \`System\` schreibt man groß, \`int\` und \`double\` klein. Lege in deinen eigenen Programmen nie zwei Variablen an, die sich nur in der Groß- und Kleinschreibung unterscheiden. Das verwirrt dich und alle, die deinen Code lesen. - -## Übung: Drei Namen - -::: übung l04-gross-klein -titel: Drei Namen -auftrag: Ludwig hat zwei Variablen angelegt: \`zahl\` mit dem Wert 4 und \`Zahl\` mit dem Wert 6. Das Struktogramm soll beide malnehmen und **Ergebnis: 24** ausgeben. Es läuft aber nicht. - Klicke auf „Im Editor öffnen“, dann auf Prüfen, und lies die Fehlermeldung. Repariere die Anweisung und klicke noch einmal auf Prüfen. -start: l04-gross-klein-start.visustruct -test: - -> Ergebnis: 24 -braucht: anweisung -tipp: Wie viele verschiedene Namen stehen im Struktogramm? \`zahl\`, \`Zahl\` und \`ZAHL\` sind für den Computer drei verschiedene Namen. -tipp: \`ZAHL\` gibt es nicht. Für 24 brauchst du 4 mal 6, also \`zahl\` mal \`Zahl\`: \`int ergebnis = zahl * Zahl\`. Mit \`zahl * zahl\` kommt 16 heraus. -lösung: l04-gross-klein-loesung.visustruct -::: - -## Die vier Datentypen - -Der Datentyp legt fest, welche Art von Wert in der Variablen stehen darf. - -- **int:** ganze Zahlen, z. B. \`int anzahl = 3\` oder \`int temperatur = -5\` -- **double:** Kommazahlen, z. B. \`double preis = 2.5\`. Statt Komma schreibst du einen **Punkt**. -- **String:** Text, z. B. \`String name = "Mia"\`. Text steht in Anführungszeichen. -- **boolean:** Wahrheitswerte, nur \`true\` (wahr) oder \`false\` (falsch) - -Es gibt noch mehr Datentypen. Einen wirst du bald brauchen: \`char\` für ein **einzelnes Zeichen**, z. B. \`char note = 'C'\`. Ein \`char\` steht in **einfachen** Anführungszeichen. Genauer lernst du ihn in Lektion 17 kennen. - -::: diagramm l05-datentypen.visustruct -Drei Variablen mit verschiedenen Datentypen. Öffne das Diagramm und starte die [Simulation](#zeige-simulation). -::: - -Wahrheitswerte begegnen dir vor allem in **Bedingungen**: \`alter >= 18\` ist entweder \`true\` oder \`false\`. Darum geht es in Lektion 8. Eine Variable vom Typ \`boolean\` bekommt einfach \`true\` oder \`false\`: - -\`\`\` -boolean volljaehrig = false -\`\`\` - -::: angeberwissen -Am 4. Juni 1996 explodierte die Rakete Ariane 5 schon 37 Sekunden nach dem Start. Schuld war eine einzige Zeile: Eine Kommazahl für die Geschwindigkeit wurde in eine Ganzzahl-Variable gesteckt, die für so große Werte zu klein war. Der Datentyp ist also keine Nebensache, er kann eine halbe Milliarde Euro kosten. -::: - -## Zuweisung mit = - -Das Zeichen \`=\` heißt **Zuweisung**. Es bedeutet nicht „ist gleich“ wie in Mathe, sondern: Berechne die rechte Seite und speichere das Ergebnis in der Variablen links. - -\`\`\` -int punkte = 10 -punkte = punkte + 5 -\`\`\` - -In Mathe wäre die zweite Zeile falsch, denn keine Zahl ist gleich sich selbst plus 5. Beim Programmieren liest du sie so: Nimm den alten Wert 10, rechne plus 5 und speichere 15 in \`punkte\`. Den Datentyp schreibst du nur beim ersten Mal, wenn die Variable angelegt wird. - -::: grafik l04-zuweisung.svg -Erst wird rechts gerechnet, dann landet das Ergebnis in der Variablen links. -::: - -## Rechnen - -Mit Zahlen kannst du rechnen: - -- \`+\` plus, \`-\` minus, \`*\` mal, \`/\` geteilt -- \`%\` Rest beim Teilen (gesprochen „Modulo“), z. B. ist \`17 % 5\` gleich 2 - -**Teilen mit Rest** kennst du aus der Grundschule, als du noch keine Kommazahlen hattest: 17 : 5 = 3 Rest 2. Die 5 passt dreimal in die 17, das sind 15. Bis 17 fehlen dann noch 2, das ist der Rest. Genau so rechnet der Computer mit ganzen Zahlen: \`/\` liefert das Ergebnis ohne Rest, \`%\` nur den Rest. - -| Grundschule | mit \`/\` | mit \`%\` | -|---|---|---| -| 17 : 5 = 3 Rest 2 | \`17 / 5\` ergibt 3 | \`17 % 5\` ergibt 2 | -| 20 : 5 = 4 Rest 0 | \`20 / 5\` ergibt 4 | \`20 % 5\` ergibt 0 | -| 3 : 5 = 0 Rest 3 | \`3 / 5\` ergibt 0 | \`3 % 5\` ergibt 3 | - -Ist der Rest 0, geht die Teilung auf. So prüfst du später, ob eine Zahl gerade ist: Bei geraden Zahlen ist \`zahl % 2\` gleich 0. - -Es gilt **Punkt vor Strich**, und Klammern werden zuerst ausgerechnet: \`2 * (3 + 4)\` ist 14. - -### Text und Ergebnis zusammen ausgeben - -Ein Ergebnis allein sagt wenig. Mit \`+\` hängst du einen Text und den Wert einer Variablen aneinander: - -\`\`\` -int alter = 14 -output: "Alter: " + alter -\`\`\` - -Das zeigt **Alter: 14**. Der Text in Anführungszeichen erscheint genau so, für \`alter\` setzt der Computer den Wert ein. Das Leerzeichen nach dem Doppelpunkt gehört zum Text, sonst steht dort \`Alter:14\`. - -## Übung: Rechteck - -::: übung l05-rechteck -titel: Rechteck berechnen -auftrag: Ein Rechteck ist 7 lang und 4 breit. Lege die Variablen \`laenge\` und \`breite\` an. Berechne die Fläche und den Umfang in eigenen Variablen und gib beide aus, so wie hier: -\`Fläche: 28\` und in der nächsten Zeile \`Umfang: 22\`. -test: - -> Fläche: 28 | Umfang: 22 -braucht: anweisung -tipp: Überlege zuerst, welche Variablen du brauchst: zwei für die Maße und zwei für die Ergebnisse. Alle sind ganze Zahlen. -tipp: Die Fläche ist Länge mal Breite, der Umfang ist 2 mal (Länge plus Breite). Für die Ausgabe schreibst du z. B. \`output: "Fläche: " + flaeche\`. Das \`+\` hängt dabei den Text und den Wert der Variablen aneinander. -lösung: l05-rechteck-loesung.visustruct -::: - -## Ganzzahlige Division - -Teilst du zwei ganze Zahlen und speicherst das Ergebnis in einer \`int\`-Variablen, fällt alles nach dem Komma weg. Es wird **nicht gerundet**. - -\`\`\` -int x = 7 / 2 ergibt 3 -int r = 7 % 2 ergibt 1 (der Rest) -double d = 7.0 / 2 ergibt 3.5 -\`\`\` - -Mit \`/\` und \`%\` zusammen kannst du gut umrechnen, zum Beispiel Minuten in Stunden und Minuten: - -::: diagramm l05-division.visustruct -135 Minuten sind 2 Stunden (135 / 60) und 15 Minuten Rest (135 % 60). -::: - -## Übung: Sekunden umrechnen - -::: übung l05-sekunden -titel: Sekunden umrechnen -auftrag: 200 Sekunden sollen als Minuten und Sekunden ausgegeben werden. Die erste und die letzte Anweisung sind schon da. Füge dazwischen Anweisungen ein, die \`minuten\` und \`rest\` berechnen. -start: l05-sekunden-start.visustruct -test: - -> 3 Minuten und 20 Sekunden -braucht: anweisung -tipp: Wie viele ganze Minuten passen in 200 Sekunden, und wie viele Sekunden bleiben übrig? Rechne es erst im Kopf aus. -tipp: Eine Minute hat 60 Sekunden. Nutze \`/\` für die ganzen Minuten und \`%\` für den Rest. Beide Variablen sind vom Typ \`int\`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Übung: Einkauf - -::: übung l05-einkauf -titel: Einkauf -auftrag: Du kaufst 3 Hefte zu je 2.5 Euro. Lege \`anzahl\` als \`int\` und \`preis\` als \`double\` an, berechne den Gesamtpreis in einer Variablen \`gesamtPreis\` und gib \`Gesamt: 7.5 Euro\` aus. -test: - -> Gesamt: 7.5 Euro -braucht: anweisung -tipp: Anzahl mal Preis ergibt den Gesamtpreis. Überlege für jede Variable, ob sie eine ganze Zahl oder eine Kommazahl ist. -tipp: Der Gesamtpreis ist eine Kommazahl, also braucht \`gesamtPreis\` den Typ \`double\`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Jetzt in Java - -Ein Struktogramm ist ein Plan. In **Java** wird daraus ein Programm, das der Computer ausführt. Dein Einkauf aus der Übung sieht so aus: - -\`\`\`java editor -int anzahl = 3; -double preis = 2.5; -double gesamtPreis = anzahl * preis; -System.out.println("Gesamt: " + gesamtPreis + " Euro"); -\`\`\` - -- Erst der Datentyp, dann der Name, dann der Wert, genau wie im Kasten. -- Jede Anweisung endet mit einem **Semikolon** \`;\`. -- Eine Ausgabe heißt in Java \`System.out.println(…)\`. - -Wie der Computer deinen Java-Code versteht und warum er auf Windows, macOS und Linux gleich läuft, erfährst du in der Lektion „Struktogramm und Code“. - -::: angeberwissen -Java hieß zuerst **Oak** (Eiche), nach dem Baum vor dem Bürofenster von James Gosling. Sein Team bei Sun Microsystems wollte ab 1991 kleine Geräte wie Fernseh-Boxen programmieren. Den Namen Oak hatte aber schon eine andere Firma. Also wurde es 1995 **Java**, wie der Kaffee von der Insel Java. -::: - -## Text in Java - -Variablen speichern nicht nur Zahlen. Für Text gibt es den Datentyp \`String\`: - -\`\`\`java editor -String name = "Mia"; -int alter = 14; -double groesse = 1.62; -System.out.println(name + " ist " + alter + " Jahre alt."); -System.out.println("Größe: " + groesse + " m"); -\`\`\` - -- Text steht in Anführungszeichen. -- \`String\` schreibst du groß, \`int\`, \`double\` und \`boolean\` klein. Java unterscheidet das. -- Mit \`+\` hängst du Text und Zahlen aneinander. - -Java tippst du im Bereich [Code](#zeige-codelive). Das Struktogramm entsteht dabei von selbst. „Im Editor öffnen“ holt das Beispiel in den Bereich Code. Klicke dort auf „Ausführen“, ändere dann Name, Alter oder Größe und probiere es noch einmal. - -::: übung l04-java-steckbrief -titel: Steckbrief in Java -bereich: beide -code: java -auftrag: Schreibe einen Steckbrief für Ludwig den Geißbock. Lege drei Variablen an: den Namen **Ludwig**, das Alter **7** und die Größe **1.2**. Gib dann diese zwei Zeilen aus: - **Ludwig ist 7 Jahre alt.** und **Größe: 1.2 m** - „Im Editor öffnen“ öffnet rechts auch den Bereich Code. Tippe dort deinen Code, das Struktogramm entsteht dabei von selbst. Klicke dann auf Prüfen. -test: - -> Ludwig ist 7 Jahre alt. | Größe: 1.2 m -braucht: anweisung -tipp: Du brauchst einen \`String\`, einen \`int\` und einen \`double\`. Jede Zeile endet mit einem Semikolon. -tipp: \`String name = "Ludwig";\`, \`int alter = 7;\` und \`double groesse = 1.2;\`. Die Ausgabe setzt du mit \`+\` zusammen: \`System.out.println(name + " ist " + alter + " Jahre alt.");\` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Lückentext: Datentypen - -::: lückentext -titel: Steckbrief in Java -auftrag: Ergänze die fehlenden Wörter. Ganz außen steht der Rahmen, den jedes Java-Programm in einer eigenen Datei braucht. Darin stehen die Variablen. -\`\`\`java -[[public]] class Steckbrief { - public static [[void]] main(String[] args) { - [[int]] alter = 14; - [[double]] groesse = 1.62; - [[String]] name = "Mia"; - [[boolean]] hatHaustier = true; - System.out.[[println]](name + " ist " + alter + " Jahre alt."); - } -} -\`\`\` -erklärung: Ganze Zahlen sind \`int\`, Kommazahlen \`double\`, Texte \`String\` (groß geschrieben) und Wahrheitswerte \`boolean\`. \`public class\` und \`public static void main\` bilden den Rahmen. Im Bereich Code darfst du ihn weglassen, mehr dazu in der Lektion „Struktogramm und Code“. -::: - -## Fehler finden: Sekunden - -::: übung l04-java-fehler -titel: Sekunden reparieren -code: java -auftrag: Das Programm soll 200 Sekunden in Minuten und Sekunden umrechnen und **3 min 20 s** ausgeben. Es gibt aber **20 min 3 s** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: \`/\` teilt ganzzahlig, \`%\` liefert den Rest. Welches Zeichen brauchst du für die Minuten, welches für die übrigen Sekunden? -tipp: Die beiden Zeichen sind vertauscht: \`int minuten = sekunden / 60;\` und \`int rest = sekunden % 60;\` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l04-java-laeuft-nicht -titel: Steckbrief reparieren -code: java -auftrag: Das Programm soll **Ludwig ist 7 Jahre alt.** ausgeben. Es läuft aber nicht, denn zwei Wörter sind falsch geschrieben. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig ist 7 Jahre alt. -braucht: anweisung -tipp: Java unterscheidet Groß- und Kleinschreibung. Vergleiche jedes Wort in der Ausgabe genau mit dem, was oben angelegt ist. -tipp: \`System\` schreibt man mit großem S. Die Variable heißt \`alter\` mit kleinem a: \`System.out.println(name + " ist " + alter + " Jahre alt.");\` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Datentyp? - -::: spiel frage -titel: Spiel: Welcher Datentyp? -intro: Ludwig hat Werte gesammelt. Welcher Datentyp passt zu jedem Wert? Acht Runden. -antworten: int | double | String | boolean - -runde: -frage: Welcher Datentyp passt zu \`17\`? -richtig: int -> Eine ganze Zahl ohne Komma ist ein \`int\`. - -runde: -frage: Welcher Datentyp passt zu \`3.5\`? -richtig: double -> Kommazahlen sind \`double\`. In Java steht statt des Kommas ein Punkt. - -runde: -frage: Welcher Datentyp passt zu \`"Hallo"\`? -richtig: String -> Text in Anführungszeichen ist ein \`String\`. - -runde: -frage: Welcher Datentyp passt zu \`true\`? -richtig: boolean -> \`true\` und \`false\` sind die beiden Wahrheitswerte, Datentyp \`boolean\`. - -runde: -frage: Welcher Datentyp passt zu \`"17"\`? -richtig: String -> Vorsicht, Anführungszeichen! Hier ist 17 ein Text, mit dem man nicht rechnen kann. - -runde: -frage: Welcher Datentyp passt zu \`-4\`? -richtig: int -> Auch negative ganze Zahlen sind \`int\`. - -runde: -frage: Welcher Datentyp passt zu \`0.0\`? -richtig: double -> Der Punkt macht es zur Kommazahl, auch wenn hinten nur eine 0 steht. - -runde: -frage: Welcher Datentyp passt zu \`"false"\`? -richtig: String -> In Anführungszeichen ist auch false nur ein Text. Der Wahrheitswert steht ohne Anführungszeichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable mit Datentyp, Namen und Wert anlegen. → Was ist eine Variable? -- Ich kann erklären, dass Java Groß- und Kleinschreibung unterscheidet (\`zahl\` ist nicht \`Zahl\`). → Groß- und Kleinschreibung -- Ich kann die Datentypen int, double, String und boolean unterscheiden. → Die vier Datentypen -- Ich kann erklären, dass = eine Zuweisung ist und kein Vergleich. → Zuweisung mit = -- Ich kann mit + - * / % rechnen und die ganzzahlige Division anwenden. → Rechnen -- Ich kann Variablen in Java anlegen und ausgeben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welcher Datentyp passt zu dem Wert 3.75? -→ Die vier Datentypen -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Kommazahlen haben den Typ double. In VisuStruct schreibst du sie mit Punkt. - -? Wie viele Variablen legen \`int zahl = 1\`, \`int Zahl = 2\` und \`int ZAHL = 3\` an? -→ Groß- und Kleinschreibung -- [ ] eine -- [ ] zwei -- [x] drei -> Java unterscheidet Groß- und Kleinschreibung. Die drei Namen sind verschieden, also gibt es drei Variablen. - -? Welchen Wert hat x nach \`int x = 17 / 5\`? -→ Rechnen -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> Bei ganzen Zahlen fällt alles nach dem Komma weg. Es wird nicht gerundet. - -? Was ergibt \`17 % 5\`? -→ Rechnen -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 geteilt durch 5 ist 3, Rest 2. Der Operator % liefert den Rest. - -? Am Anfang ist \`int punkte = 10\`. Was steht nach \`punkte = punkte * 2\` in punkte? -→ Zuweisung mit = -- [ ] 10 -- [ ] 12 -- [x] 20 -> Rechts wird mit dem alten Wert 10 gerechnet, das Ergebnis 20 wird in punkte gespeichert. - -? Welche Zeile legt in Java richtig eine Kommazahl an? -→ Jetzt in Java -- [ ] double preis = 2,5; -- [x] double preis = 2.5; -- [ ] Double preis = 2.5 -> Kommazahlen schreibst du mit Punkt, und am Ende steht ein Semikolon. -::: -`,Vv=`--- -titel: Sequenz -bereich: Grundlagen -kurz: Schritte nacheinander ausführen, auf die Reihenfolge achten und Werte tauschen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass eine Sequenz von oben nach unten ausgeführt wird -- erklären, warum die Reihenfolge der Schritte wichtig ist -- zwei Werte mit einer Hilfsvariablen tauschen -- eine Sequenz in Java schreiben -::: - -## Schritt für Schritt - -Eine **Sequenz** ist eine Folge von Schritten, die genau einmal und genau in dieser Reihenfolge ausgeführt werden. Im Struktogramm stehen die Kästen einfach untereinander. - -Jeder Kasten wird erst begonnen, wenn der Kasten darüber fertig ist. Es wird nichts übersprungen und nichts wiederholt. - -::: diagramm l05-sequenz.visustruct -Eine Sequenz aus vier Kästen: Zuerst bekommen \`laenge\` und \`breite\` ihre Werte, dann wird die Fläche berechnet, und zum Schluss wird sie ausgegeben. Jeder Kasten läuft genau einmal, von oben nach unten. Ausgabe: 15. -::: - -> Die Sequenz ist der einfachste der drei Grundbausteine. Fast jedes Struktogramm besteht zum größten Teil aus Sequenzen. - -## Die Reihenfolge zählt - -Beim Tee kochen merkst du sofort, wenn die Reihenfolge falsch ist. Bei Rechnungen ist es genauso, nur fällt es weniger auf. - -::: diagramm l04-rechnen.visustruct -Erst verdoppeln, dann 1 addieren: Das Ergebnis ist 7. Tauschst du die beiden mittleren Kästen, kommt 8 heraus. -::: - -Die Anweisung \`zahl = zahl * 2\` bedeutet: Nimm den Wert von \`zahl\`, rechne mal 2 und speichere das Ergebnis wieder in \`zahl\`. Was in \`zahl\` steht, hängt also davon ab, was vorher passiert ist. - -::: angeberwissen -Struktogramme sind kein reiner Schulkram. Laut Shneiderman haben auch Firmen wie IBM und Siemens damit ihre Programme geplant. Du lernst hier also Profi-Zeug. Määäh-ga! -::: - -## Übung: Reihenfolge ordnen - -::: übung l04-reihenfolge -titel: Reihenfolge ordnen -auftrag: Im Startdiagramm stehen die richtigen Kästen, aber in der falschen Reihenfolge. Verschiebe sie so, dass am Ende **20** ausgegeben wird. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Lies jeden Kasten und frage dich: Welche Variable braucht er, und wo bekommt diese Variable ihren Wert? -tipp: Eine Variable muss zuerst angelegt werden, bevor du mit ihr rechnest. Die Ausgabe kommt ganz zum Schluss. Rechne im Kopf nach: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Zwei Werte tauschen - -Stell dir zwei Gläser vor: eins mit Saft, eins mit Wasser. Willst du die Inhalte tauschen, brauchst du ein drittes, leeres Glas. - -Mit Variablen ist es genauso. Schreibst du einfach \`a = b\` und danach \`b = a\`, ist der alte Wert von \`a\` schon verloren. Am Ende haben beide Variablen denselben Wert. - -Die Lösung ist eine **Hilfsvariable**, die den alten Wert kurz aufbewahrt: - -::: grafik l05-tauschen.svg -Drei Schritte mit einem leeren Glas: erst Saft in hilf, dann Wasser nach links, dann den Saft aus hilf nach rechts. -::: - -::: diagramm l04-tauschen.visustruct -Zuerst wird a in hilf gemerkt. Dann bekommt a den Wert von b, und b bekommt den gemerkten Wert aus hilf. -::: - -## Übung: Gläser tauschen - -::: übung l04-glaeser-tauschen -titel: Gläser tauschen -auftrag: Im linken Glas ist Saft, im rechten Wasser. Tausche die Inhalte der Variablen \`links\` und \`rechts\`, bevor sie ausgegeben werden. -Ändere dazu nicht die ersten beiden Kästen, sondern füge dazwischen neue Anweisungen ein. -start: l04-tauschen-start.visustruct -test: - -> Links: Wasser | Rechts: Saft -braucht: anweisung -tipp: Wie tauschst du zwei volle Gläser im echten Leben? Du brauchst ein drittes, leeres Glas. -tipp: Lege mit \`String hilf = links\` eine Hilfsvariable an. Danach brauchst du noch zwei Zuweisungen. -lösung: l04-tauschen-loesung.visustruct -::: - -## Jetzt in Java - -Wie in Lektion 4 kannst du auch eine Sequenz direkt in Java schreiben. Das Tauschen von oben sieht so aus: - -\`\`\`java editor -int a = 5; -int b = 9; -int hilf = a; -a = b; -b = hilf; -System.out.println("a = " + a); -System.out.println("b = " + b); -\`\`\` - -- Jeder Kasten wird zu einer Zeile. Die Zeilen laufen von oben nach unten, genau wie die Kästen. -- Am Ende jeder Anweisung steht ein **Semikolon** \`;\`. -- Eine Ausgabe heißt in Java \`System.out.println(…)\`. Achte auf das große S bei \`System\`, Java unterscheidet Groß- und Kleinbuchstaben. - -Java tippst du im Bereich [Code](#zeige-codelive) rechts neben dem Struktogramm. Während du tippst, baut VisuStruct das Struktogramm mit. So siehst du sofort, welcher Kasten zu welcher Zeile gehört. - -::: übung l05-java-kino -titel: Kinokarten in Java -bereich: beide -code: java -auftrag: Eine Kinokarte kostet 9 Euro. Schreibe ein Java-Programm, das den Preis für 3 Karten ausrechnet und **Gesamt: 27** ausgibt. - „Im Editor öffnen“ öffnet rechts auch den Bereich Code. Tippe dort deinen Code, das Struktogramm entsteht dabei von selbst. Klicke dann auf Prüfen. -test: - -> Gesamt: 27 -braucht: anweisung -tipp: Du brauchst drei Variablen: den Preis, die Anzahl und das Ergebnis. Jede Zeile endet mit einem Semikolon. -tipp: \`int preis = 9;\` und \`int anzahl = 3;\` legen die Variablen an. \`int gesamt = preis * anzahl;\` rechnet, und \`System.out.println("Gesamt: " + gesamt);\` gibt das Ergebnis aus. -lösung: l05-java-kino-loesung.visustruct -::: - -## Lückentext: Gläser tauschen - -::: lückentext -titel: Zwei Werte tauschen -auftrag: Die Werte von \`a\` und \`b\` sollen getauscht werden. Ergänze die fehlenden Wörter. -\`\`\`java -int a = 3; -int b = 8; -[[int]] hilf = [[a]]; -a = [[b]]; -b = [[hilf]]; -System.out.println("a = " + a + ", b = " + b); -\`\`\` -erklärung: Erst rettest du \`a\` in die Hilfsvariable, dann bekommt \`a\` den Wert von \`b\`, und zuletzt bekommt \`b\` den geretteten Wert. Ausgabe: **a = 8, b = 3** -::: - -## Fehler finden: Guthaben - -::: übung l05-java-fehler -titel: Guthaben reparieren -code: java -auftrag: Auf der Mensakarte sind 20 Euro. Ein Essen kostet 7 Euro. Das Programm soll das Guthaben **nach** dem Bezahlen ausgeben, also **Übrig: 13**. Es gibt aber **Übrig: 20** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l05-java-fehler-start.visustruct -test: - -> Übrig: 13 -braucht: anweisung -tipp: Alle Zeilen stimmen, nur die Reihenfolge nicht. Wann wird das Guthaben ausgegeben, wann wird bezahlt? -tipp: Tausche die letzten beiden Zeilen: erst \`guthaben = guthaben - preis;\`, dann die Ausgabe. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l05-java-laeuft-nicht -titel: Pizza teilen -code: java -auftrag: Eine Pizza hat 8 Stücke, 4 Freunde teilen sie. Das Programm soll **Jeder bekommt 2 Stücke.** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Jeder bekommt 2 Stücke. -braucht: anweisung -tipp: Vergleiche den Namen in der Ausgabe Buchstabe für Buchstabe mit dem Namen, der in der dritten Zeile angelegt wird. -tipp: Die Variable heißt \`proPerson\` mit großem P (CamelCase). Schreibe sie in der Ausgabe genauso. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Kästen sortieren - -::: spiel reihenfolge -titel: Spiel: Kästen sortieren -intro: Ludwig hat die Kästen durcheinandergeworfen. Bring sie in die richtige Reihenfolge. Vier Runden. - -runde: -frage: Am Ende soll **7** ausgegeben werden. -- \`int zahl = 3\` -- \`zahl = zahl * 2\` -- \`zahl = zahl + 1\` -- \`output: zahl\` -> Erst verdoppeln, dann 1 dazu: 3 · 2 + 1 = 7. Andersherum käme (3 + 1) · 2 = 8 heraus. - -runde: -frage: Am Ende soll **8** ausgegeben werden. -- \`int zahl = 3\` -- \`zahl = zahl + 1\` -- \`zahl = zahl * 2\` -- \`output: zahl\` -> Dieselben Kästen, andere Reihenfolge, anderes Ergebnis: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a und b sollen ihre Werte tauschen. -- \`int hilf = a\` -- \`a = b\` -- \`b = hilf\` -- \`output: a + " " + b\` -> Zuerst wird a in hilf gerettet. Sonst ist der alte Wert weg, sobald a den Wert von b bekommt. - -runde: -frage: Ludwig kocht Tee. -- Wasser in den Wasserkocher füllen -- Wasserkocher einschalten -- Warten, bis das Wasser kocht -- Wasser über den Teebeutel gießen -- Tee ziehen lassen -> Jeder Schritt braucht den vorherigen. Genau wie im Struktogramm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass eine Sequenz von oben nach unten ausgeführt wird. → Schritt für Schritt -- Ich kann erklären, warum die Reihenfolge der Schritte wichtig ist. → Die Reihenfolge zählt -- Ich kann zwei Werte mit einer Hilfsvariablen tauschen. → Zwei Werte tauschen -- Ich kann eine Sequenz in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie oft wird jeder Kasten einer Sequenz ausgeführt? -→ Schritt für Schritt -- [ ] So oft, bis eine Bedingung falsch ist -- [x] Genau einmal -- [ ] Gar nicht, wenn er leer ist -> In einer Sequenz wird nichts wiederholt und nichts übersprungen. Für Wiederholungen gibt es Schleifen. - -? Am Anfang ist a = 5 und b = 9. Danach wird \`a = b\` und dann \`b = a\` ausgeführt. Was steht am Ende in den Variablen? -→ Zwei Werte tauschen -- [ ] a = 9 und b = 5 -- [x] a = 9 und b = 9 -- [ ] a = 5 und b = 5 -> Nach \`a = b\` ist der alte Wert 5 verloren. \`b = a\` kopiert dann nur die 9 zurück. - -? Wozu brauchst du beim Tauschen eine Hilfsvariable? -→ Zwei Werte tauschen -- [x] Damit ein Wert nicht überschrieben wird, bevor er gebraucht wird -- [ ] Damit das Struktogramm länger wird -- [ ] Weil man Variablen sonst nicht ausgeben kann - -? Was gibt die Sequenz \`int x = 4\`, \`x = x + 1\`, \`x = x * 3\`, \`output: x\` aus? -→ Die Reihenfolge zählt -- [ ] 13 -- [x] 15 -- [ ] 4 -> Erst wird x zu 5, dann zu 15. - -? Womit endet in Java jede Anweisung? -→ Jetzt in Java -- [ ] Mit einem Punkt . -- [x] Mit einem Semikolon ; -- [ ] Mit einem Doppelpunkt : -> Fehlt das Semikolon, zeigt VisuStruct unter dem Code einen Hinweis. -::: -`,qv=`--- -titel: Eingabe und Ausgabe -bereich: Grundlagen -kurz: Werte abfragen, Ergebnisse anzeigen und das EVA-Prinzip anwenden. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- das EVA-Prinzip erklären -- eine Eingabe mit Datentyp, Namen und Frage schreiben -- Text und Variablen in einer Ausgabe mit + verbinden -- ein Ergebnis zuerst ausrechnen und in einer Variablen speichern, dann diese Variable ausgeben, z. B. \`int summe = a + b\`, danach \`output: summe\` -- aus einem Aufgabentext Eingabe, Verarbeitung und Ausgabe herauslesen -- in Java mit dem Scanner Zahlen einlesen -- Kommentare mit \`//\` und \`/* */\` schreiben und damit meinen Code einteilen und erklären -::: - -## Das EVA-Prinzip - -Viele Programme arbeiten in drei Teilen: - -1. **Eingabe:** Das Programm fragt Werte ab, z. B. zwei Zahlen. -2. **Verarbeitung:** Es rechnet mit den Werten. -3. **Ausgabe:** Es zeigt das Ergebnis an. - -Die Anfangsbuchstaben ergeben **EVA**. Bisher standen die Werte fest im Struktogramm. Mit einer Eingabe kann bei jedem Durchlauf ein anderer Wert verwendet werden. - -::: diagramm l06-eva.visustruct -Oben die Eingabe, in der Mitte die Verarbeitung, unten die Ausgabe. Starte die Simulation und probiere verschiedene Zahlen aus. -::: - -## Eingabe mit input: - -Eine Eingabe fragt beim Ausführen einen Wert ab und speichert ihn in einer Variablen. Du schreibst den **Datentyp**, den **Namen** und eine **Frage** in Anführungszeichen: - -\`\`\` -input: int alter "Wie alt bist du?" -input: double preis "Preis in Euro?" -input: String name "Wie heißt du?" -\`\`\` - -Im Editor wählst du bei einer Anweisung oben die Art **Eingabe**. Das Wort \`input:\` steht dann schon davor, und du tippst nur noch \`int alter "Wie alt bist du?"\`. - -> Der Datentyp entscheidet, was eingegeben werden darf. Bei \`int\` meldet die Simulation einen Fehler, wenn jemand 2.5 oder Hallo eintippt. - -## Ausgabe mit output: - -Eine Ausgabe zeigt Text und Werte an. Text steht in Anführungszeichen, Variablen ohne. Mit \`+\` hängst du die Teile **aneinander**: - -\`\`\` -output: "Hallo " + name + "!" -output: "Du bist " + alter + " Jahre alt." -\`\`\` - -Achte auf die Leerzeichen innerhalb der Anführungszeichen. Ohne sie klebt alles zusammen, und du siehst \`HalloMia!\` statt \`Hallo Mia!\`. - -## Übung: Begrüßung - -::: übung l06-begruessung -titel: Begrüßung -auftrag: Frage nach dem Namen und begrüße die Person. Bei der Eingabe Ada soll **Hallo Ada!** ausgegeben werden. -test: Ada -> Hallo Ada! -test: Ben -> Hallo Ben! -braucht: anweisung -tipp: Zuerst kommt eine Eingabe für den Namen, danach eine Ausgabe, die den Namen benutzt. -tipp: Der Name ist Text, die Eingabe braucht also den Typ \`String\`. In der Ausgabe verbindest du \`"Hallo "\`, die Variable und \`"!"\` mit \`+\`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Vorsicht mit + in der Ausgabe - -In einer Ausgabe hängt \`+\` die Teile **immer** aneinander, auch bei Zahlen. Sind \`a\` gleich 3 und \`b\` gleich 4, dann zeigt - -\`\`\` -output: a + b -\`\`\` - -nicht 7, sondern **34**. Rechne deshalb vorher in einer Variablen und gib dann die Variable aus: - -\`\`\` -int summe = a + b -output: "Summe: " + summe -\`\`\` - -Das passt auch zum EVA-Prinzip: Gerechnet wird in der Verarbeitung, nicht in der Ausgabe. - -## Vom Aufgabentext zum Struktogramm - -Bevor du den ersten Kasten ziehst, liest du die Aufgabe genau und zerlegst sie. Das nennt man **Problemanalyse**. Drei Fragen helfen dir dabei: - -1. **Was ist gegeben?** Diese Werte werden zur **Eingabe**. Überlege auch den Datentyp. -2. **Was ist gesucht?** Das wird zur **Ausgabe**. -3. **Wie kommt man vom Gegebenen zum Gesuchten?** Das ist die **Verarbeitung**, oft eine Formel. - -Ein Beispiel: *Eine Kinokarte kostet 9 €. Das Programm fragt, wie viele Karten jemand kauft, und zeigt den Gesamtpreis an.* - -- Gegeben: die Anzahl der Karten → \`input: int anzahl "Wie viele Karten?"\` -- Gesucht: der Gesamtpreis → \`output: "Gesamtpreis: " + gesamt\` -- Weg: Anzahl mal 9 → \`int gesamt = anzahl * 9\` - -Jetzt musst du die drei Teile nur noch in der Reihenfolge E, V, A untereinander setzen. - -> Achte im Aufgabentext auf Signalwörter: *fragt*, *liest ein* oder *gibt ein* deuten auf eine Eingabe. *Zeigt*, *gibt aus* oder *meldet* deuten auf eine Ausgabe. - -## Übung: Rechner - -::: übung l06-rechner -titel: Summe und Produkt -auftrag: Frage zwei ganze Zahlen ab. Gib danach ihre Summe und ihr Produkt aus, jeweils in einer eigenen Zeile. -Bei 3 und 4 sieht das so aus: \`Summe: 7\` und \`Produkt: 12\`. -test: 3, 4 -> Summe: 7 | Produkt: 12 -test: 10, -2 -> Summe: 8 | Produkt: -20 -test: 0, 5 -> Summe: 5 | Produkt: 0 -braucht: anweisung -tipp: Plane die Reihenfolge: erst beide Zahlen einlesen, dann rechnen, dann ausgeben. -tipp: Du brauchst zwei Eingaben, zwei Anweisungen zum Rechnen und zwei Ausgaben. Rechne die Summe in einer eigenen Variablen, sonst hängt \`+\` die Zahlen aneinander. -lösung: l06-rechner-loesung.visustruct -::: - -## Übung: Mittelwert - -::: übung l06-mittelwert -titel: Mittelwert -auftrag: Frage zwei Zahlen ab, die auch Kommazahlen sein dürfen. Gib ihren Mittelwert aus, bei 2 und 3 also \`Mittelwert: 2.5\`. -test: 2, 3 -> Mittelwert: 2.5 -test: 4, 6 -> Mittelwert: 5.0 -test: 1.5, 2 -> Mittelwert: 1.75 -braucht: anweisung -tipp: Den Mittelwert zweier Zahlen bekommst du, wenn du sie addierst und das Ergebnis halbierst. -tipp: Für Kommazahlen nimmst du den Typ \`double\`. Der Mittelwert ist die Summe geteilt durch 2. Denk an die Klammern: \`(x + y) / 2\`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Jetzt in Java - -So sieht das EVA-Beispiel vom Anfang in Java aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Erste Zahl?"); -int a = scanner.nextInt(); -System.out.println("Zweite Zahl?"); -int b = scanner.nextInt(); -int summe = a + b; -System.out.println("Summe: " + summe); -\`\`\` - -- Für Eingaben braucht Java einen **Scanner**. Die ersten beiden Zeilen schreibst du einmal ganz oben, dann kannst du beliebig oft einlesen. -- \`scanner.nextInt()\` liest eine ganze Zahl, \`scanner.nextDouble()\` eine Kommazahl und \`scanner.next()\` ein Wort. -- Die Frage an den Benutzer ist eine normale Ausgabe direkt davor. VisuStruct macht aus beiden Zeilen zusammen einen Eingabe-Kasten. - -::: angeberwissen -Java sollte ursprünglich kleine Geräte steuern, und das tut es heute noch: Auf vielen SIM-Karten im Handy und auf Bankkarten läuft eine Mini-Version namens **Java Card**. Gut möglich, dass du gerade Java in der Hosentasche hast. -::: - -::: übung l06-java-tage -titel: Alter in Tagen -code: java -auftrag: Schreibe ein Java-Programm, das nach dem Alter in Jahren fragt und ausgibt, wie viele Tage das ungefähr sind. Rechne mit 365 Tagen pro Jahr. Bei 2 Jahren erscheint **Das sind etwa 730 Tage.** -test: 2 -> Das sind etwa 730 Tage. -test: 10 -> Das sind etwa 3650 Tage. -test: 1 -> Das sind etwa 365 Tage. -braucht: anweisung -tipp: Erst die beiden Scanner-Zeilen, dann Frage und Eingabe, dann rechnen, dann ausgeben. -tipp: \`int jahre = scanner.nextInt();\` liest ein. \`int tage = jahre * 365;\` rechnet, und \`System.out.println("Das sind etwa " + tage + " Tage.");\` gibt aus. -lösung: l06-java-tage-loesung.visustruct -::: - -## Kommentare im Code - -Im Code darfst du dir Notizen machen. Sie heißen **Kommentare** (englisch *comments*). Java überspringt sie beim Ausführen, das Programm und das Struktogramm bleiben also gleich. Es gibt zwei Arten: - -- \`//\` macht den Rest der Zeile zum Kommentar. Das ist ein **einzeiliger Kommentar**. -- \`/*\` beginnt einen **mehrzeiligen Kommentar**, \`*/\` beendet ihn. Alles dazwischen gehört zum Kommentar, egal wie viele Zeilen. Die Sternchen am Anfang der Zeilen dazwischen braucht Java nicht, sie sehen nur ordentlich aus. - -In fertigen Programmen siehst du manchmal auch \`/** … */\` mit zwei Sternchen am Anfang. Das ist ein **Javadoc-Kommentar**, aus dem Java eine Beschreibung des Programms erzeugen kann. Für dich reichen \`//\` und \`/* */\`. - -So sieht das EVA-Beispiel mit Kommentaren aus: - -\`\`\`java editor -/* - * Aufgabe: Das Programm fragt nach zwei Zahlen - * und gibt ihre Summe aus. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Eingabe -System.out.println("Erste Zahl?"); -int a = scanner.nextInt(); -System.out.println("Zweite Zahl?"); -int b = scanner.nextInt(); - -// Verarbeitung -int summe = a + b; // die beiden Zahlen addieren - -// Ausgabe -System.out.println("Summe: " + summe); // bei 3 und 4 erwarte ich: Summe: 7 - -// Nächstes Mal: auch Kommazahlen erlauben -\`\`\` - -Wofür Kommentare gut sind: - -- **Aufgabenstellung:** Oben im mehrzeiligen Kommentar steht, was das Programm können soll. -- **Einteilung:** \`// Eingabe\`, \`// Verarbeitung\` und \`// Ausgabe\` teilen den Code nach dem EVA-Prinzip ein. -- **Erklärung:** Ein Kommentar hinter einer Zeile sagt, was dein Algorithmus dort macht. -- **Erwartung:** Schreib auf, welche Ausgabe du erwartest. Beim Testen vergleichst du dann. -- **Klammern zuordnen:** Später kommen Blöcke in geschweiften Klammern \`{ }\` dazu. Dann zeigt z. B. \`} // Ende for\`, welche Klammer was schließt. -- **Gedanken festhalten:** Woran musst du denken, wenn du nächstes Mal weiterarbeitest? - -Klicke auf **Im Editor öffnen**, ändere einen Kommentar oder schreib einen neuen dazu. Das Struktogramm bleibt gleich, und VisuStruct behält deine Kommentare. - -> Schreib nicht hinter jede Zeile einen Kommentar. Erkläre, was dein Algorithmus macht und warum, nicht jedes einzelne Zeichen. - -::: angeberwissen -Beide Arten sind viel älter als Java: \`/* … */\` stammt aus der Sprache PL/I von 1964, \`//\` aus BCPL von 1967. Java hat sie über C und C++ geerbt. -::: - -## Lückentext: Kommentare - -::: lückentext -titel: Kommentare ergänzen -auftrag: Ergänze die Zeichen, damit oben ein mehrzeiliger Kommentar mit der Aufgabe steht und die anderen beiden Notizen einzeilige Kommentare sind. -\`\`\`java -[[/*]] - * Aufgabe: Das Programm rechnet Euro in Cent um. - [[*/]] -int euro = 3; -[[//]] Verarbeitung -int cent = euro * 100; -[[//]] Ausgabe, erwartet: 300 -System.out.println(cent); -\`\`\` -erklärung: \`/*\` öffnet den mehrzeiligen Kommentar und \`*/\` schließt ihn. \`//\` gilt nur bis zum Ende der Zeile, darum braucht jede Notiz ihr eigenes \`//\`. -::: - -## Lückentext: Eingabe - -::: lückentext -titel: Name und Alter einlesen -auftrag: Ergänze die fehlenden Wörter, damit das Programm einen Namen und ein Alter einliest. -\`\`\`java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("Wie heißt du?"); -[[String]] name = scanner.[[next]](); -System.out.println("Wie alt bist du?"); -int alter = scanner.[[nextInt]](); -System.out.println("Hallo " + name + ", nächstes Jahr bist du " + [[(alter + 1)]] + "."); -\`\`\` -erklärung: \`scanner.next()\` liest ein Wort, \`scanner.nextInt()\` eine ganze Zahl. Die Klammern um \`alter + 1\` sind wichtig: Ohne sie hängt Java erst \`alter\` und dann \`1\` an den Text an, aus 14 würde **141**. -::: - -## Fehler finden: Summe - -::: übung l06-java-fehler -titel: Summe reparieren -code: java -auftrag: Das Programm liest zwei Zahlen ein und soll ihre Summe ausgeben. Bei 3 und 4 erscheint aber **Summe: 34** statt **Summe: 7**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Summe: 7 -test: 10, 5 -> Summe: 15 -test: 0, 9 -> Summe: 9 -braucht: anweisung -tipp: Lies die Ausgabe von links nach rechts: Was ergibt \`"Summe: " + a\`? Und was passiert, wenn danach noch \`+ b\` kommt? -tipp: Setze Klammern, damit Java zuerst rechnet: \`System.out.println("Summe: " + (a + b));\` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l06-java-laeuft-nicht -titel: Alter im nächsten Jahr -code: java -auftrag: Das Programm fragt nach deinem Alter und soll sagen, wie alt du nächstes Jahr bist. Bei 14 also **Nächstes Jahr bist du 15**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> Nächstes Jahr bist du 15 -test: 9 -> Nächstes Jahr bist du 10 -braucht: anweisung -tipp: Die Eingabe landet in einer Variablen. Wie genau heißt sie, und wie heißt sie in der Ausgabe? -tipp: Die Variable heißt \`alter\` mit kleinem a: \`System.out.println("Nächstes Jahr bist du " + (alter + 1));\` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Was wird ausgegeben? - -::: spiel frage -titel: Spiel: Was wird ausgegeben? -intro: Ludwig hat Ein- und Ausgaben gebaut. Sag vorher, was passiert. Sieben Runden. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: Was wird ausgegeben? -- 7 -- [x] 34 -- a + b -> In einer Ausgabe hängt \`+\` die Teile immer aneinander, auch bei Zahlen. - -runde: - int a = 3 - int b = 4 - int summe = a + b - output: "Summe: " + summe -frage: Was wird ausgegeben? -- [x] Summe: 7 -- Summe: 34 -- Summe: summe -> Gerechnet wird vorher in einer Variablen. Die Ausgabe hängt nur noch Text und Ergebnis aneinander. - -runde: - input: String name "Wie heißt du?" - output: "Hallo" + name + "!" -frage: Du tippst Mia. Was wird ausgegeben? -- Hallo Mia! -- [x] HalloMia! -- Hallo name! -> Im Text \`"Hallo"\` fehlt das Leerzeichen am Ende. Darum klebt alles zusammen. - -runde: - input: int alter "Wie alt bist du?" -frage: Du tippst 2.5. Was passiert? -- alter ist 2. -- alter ist 2.5. -- [x] Die Simulation meldet einen Fehler. -> Der Datentyp \`int\` erlaubt nur ganze Zahlen. - -runde: - input: double preis "Preis in Euro?" -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- [x] Eingabe -- Verarbeitung -- Ausgabe -> \`input:\` fragt einen Wert ab. Das ist die Eingabe. - -runde: - double mittel = (a + b) / 2.0 -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- Eingabe -- [x] Verarbeitung -- Ausgabe -> Hier wird gerechnet. Das ist die Verarbeitung. - -runde: - output: "Mittelwert: " + mittel -frage: Zu welchem Teil von EVA gehört dieser Kasten? -- Eingabe -- Verarbeitung -- [x] Ausgabe -> \`output:\` zeigt das Ergebnis an. Das ist die Ausgabe. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann das EVA-Prinzip erklären. → Das EVA-Prinzip -- Ich kann eine Eingabe mit Datentyp, Namen und Frage schreiben. → Eingabe mit input: -- Ich kann Text und Variablen in einer Ausgabe mit + verbinden. → Vorsicht mit + in der Ausgabe -- Ich kann ein Ergebnis zuerst ausrechnen und in einer Variablen speichern, dann diese Variable ausgeben, z. B. \`int summe = a + b\`, danach \`output: summe\`. → Vorsicht mit + in der Ausgabe -- Ich kann aus einem Aufgabentext Eingabe, Verarbeitung und Ausgabe herauslesen. → Vom Aufgabentext zum Struktogramm -- Ich kann in Java mit dem Scanner Zahlen einlesen. → Jetzt in Java -- Ich kann Kommentare mit \`//\` und \`/* */\` schreiben und damit meinen Code einteilen und erklären. → Kommentare im Code -::: - -## Wissenstest - -::: quiz -? Wofür steht EVA? -→ Das EVA-Prinzip -- [x] Eingabe, Verarbeitung, Ausgabe -- [ ] Eingabe, Variable, Anweisung -- [ ] Erst Variablen anlegen -> Viele Programme fragen erst Werte ab, verarbeiten sie und zeigen dann das Ergebnis. - -? Die Variablen a und b haben die Werte 3 und 4. Was zeigt \`output: a + b\`? -→ Vorsicht mit + in der Ausgabe -- [ ] 7 -- [x] 34 -- [ ] a + b -> In der Ausgabe hängt + die Teile aneinander. Rechne die Summe vorher in einer Variablen aus. - -? Welche Eingabe passt, wenn ein Preis wie 4.99 eingegeben werden soll? -→ Eingabe mit input: -- [ ] input: int preis "Preis?" -- [x] input: double preis "Preis?" -- [ ] input: String "Preis?" -> 4.99 ist eine Kommazahl, also double. Außerdem braucht jede Eingabe einen Variablennamen. - -? Was zeigt \`output: "Alter: " + alter\`, wenn alter den Wert 12 hat? -→ Vorsicht mit + in der Ausgabe -- [x] Alter: 12 -- [ ] "Alter: " + 12 -- [ ] Alter: alter - -? Aufgabe: *Das Programm fragt nach dem Radius eines Kreises und zeigt seinen Umfang an.* Ordne jedem Teil des Textes zu, wohin er im EVA-Prinzip gehört. -→ Vom Aufgabentext zum Struktogramm -= fragt nach dem Radius -> Eingabe -= Umfang = 2 * 3.14 * Radius -> Verarbeitung -= zeigt seinen Umfang an -> Ausgabe -> Gegeben ist der Radius, gesucht der Umfang. Die Formel verbindet beides. - -? Was liest \`scanner.nextInt()\` ein? -→ Jetzt in Java -- [x] Eine ganze Zahl -- [ ] Eine Kommazahl -- [ ] Einen ganzen Satz -> Für Kommazahlen gibt es \`scanner.nextDouble()\`, für ein Wort \`scanner.next()\`. - -? Was macht Java mit einem Kommentar? -→ Kommentare im Code -- [ ] Es gibt ihn als Text aus. -- [x] Es überspringt ihn. -- [ ] Es meldet einen Fehler. -> Kommentare sind Notizen für Menschen. Das Programm läuft mit und ohne sie genau gleich. - -? Wo endet ein Kommentar, der mit \`/*\` beginnt? -→ Kommentare im Code -- [x] Bei \`*/\` -- [ ] Am Ende der Zeile -- [ ] Bei der nächsten Leerzeile -> Darum kann ein Kommentar mit \`/*\` über mehrere Zeilen gehen. Ein Kommentar mit \`//\` endet dagegen am Zeilenende. -::: -`,Uv=`--- -titel: Simulation und Schreibtischtest -bereich: Grundlagen -kurz: Ein Struktogramm Schritt für Schritt ausführen, von Hand nachrechnen und Fehler finden. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Simulation starten, anhalten, einzelne Schritte ausführen und stoppen -- während der Simulation Variablen, Ausgabe und den letzten Schritt ablesen -- einen Schreibtischtest als Tabelle von Hand durchführen -- Syntaxfehler, Laufzeitfehler und Logikfehler unterscheiden -- einen Fehler finden, indem ich die Werte mit meiner Erwartung vergleiche -- Java-Code mit dem Einzelschritt und Haltepunkten verfolgen -::: - -## Die Simulation starten - -Bevor es mit Verzweigungen und Schleifen weitergeht, lernst du dein wichtigstes Werkzeug kennen. Mit der Simulation prüfst du ab jetzt jede Übung selbst. - -Mit dem Knopf [▶︎ Simulation starten](#zeige-simulation) rechts bei den Werkzeugen führt VisuStruct dein Struktogramm aus. Solange die Simulation läuft, kannst du das Diagramm nicht bearbeiten. Derselbe Knopf heißt dann **■ Simulation beenden**, damit kehrst du zum Bearbeiten zurück. - -Rechts erscheint die **Steuerung**: - -- **Start** führt das Diagramm automatisch Schritt für Schritt aus. Wie lange zwischen zwei Schritten gewartet wird, stellst du bei **Pause pro Schritt** ein. -- **Pause** hält an. Danach geht es mit Start oder Schritt weiter. -- **Schritt** führt genau einen Schritt aus. So kannst du in Ruhe zuschauen. -- **Stopp** bricht ab und setzt alles auf den Anfang zurück. - -Öffne das folgende Diagramm im Editor und gehe es mit **Schritt** durch: - -::: diagramm sim-tausch.visustruct -Die Werte von a und b werden vertauscht. Die Variable hilf merkt sich dabei einen Wert. -::: - -## Was die Simulation anzeigt - -- **Markierung:** Ein Kasten im Diagramm ist farbig hervorgehoben. Bei **Letzter Schritt** ist es der Kasten, der gerade ausgeführt wurde. Bei **Nächster Schritt** ist es der Kasten, der als Nächstes drankommt. -- **Eingabe:** Fragt das Diagramm nach einem Wert, tippst du ihn hier ein und bestätigst mit **OK**. -- **Letzter Schritt:** Hier steht, was gerade passiert ist, z. B. \`a = 5 -> 5\` nach dem Kasten \`a = b\`. Links sind die Variablen schon durch ihre Werte ersetzt, rechts vom Pfeil steht das Ergebnis. -- **Variablen:** Alle Variablen mit ihren aktuellen Werten. -- **Ausgabe:** Alles, was mit \`output:\` ausgegeben wurde. - -Ist rechts der Bereich [Code](#zeige-codelive) geöffnet, läuft dort zusätzlich die [Konsole](#zeige-konsole) mit. Sie wird beim Start geleert und zeigt dann die Ausgaben und Eingaben so, wie sie auch beim echten Programm erscheinen würden. - -Am Ende erscheint **Simulation beendet**. Geht etwas schief, bleibt die Simulation auf dem fehlerhaften Kasten stehen und zeigt eine Meldung, z. B. **Unbekannte Variable: gesamt**. - -## Der Schreibtischtest - -Beim **Schreibtischtest** spielst du selbst den Computer. Du gehst das Struktogramm mit Stift und Papier durch und schreibst nach jedem Schritt die Werte der Variablen auf. Am besten machst du eine Tabelle: eine Spalte für den Schritt, eine für jede Variable und eine für die Ausgabe. - -::: grafik l07-schreibtischtest-de.svg -Ein Blatt Papier und ein Stift genügen: Du schreibst nach jedem Schritt die neuen Werte in eine neue Zeile. -::: - -Für das Diagramm oben sieht die Tabelle so aus: - -| Schritt | a | b | hilf | Ausgabe | -|---|---|---|---|---| -| \`int a = 3\` | 3 | – | – | | -| \`int b = 5\` | 3 | 5 | – | | -| \`int hilf = a\` | 3 | 5 | 3 | | -| \`a = b\` | 5 | 5 | 3 | | -| \`b = hilf\` | 5 | 3 | 3 | | -| \`output: "a = " + a\` | 5 | 3 | 3 | a = 5 | -| \`output: "b = " + b\` | 5 | 3 | 3 | b = 3 | - -Vergleiche danach deine Tabelle mit der Simulation. Stimmen die Werte unter **Variablen** nach jedem Schritt mit deiner Zeile überein? - -> Der Schreibtischtest lohnt sich besonders in Prüfungen. Dort hast du keinen Computer, aber du kannst trotzdem zeigen, was dein Struktogramm tut. - -::: angeberwissen -Früher war „Computer“ ein Beruf: Menschen, die mit Stift, Papier und Rechenmaschine rechneten. Eine der besten war die Mathematikerin Katherine Johnson bei der NASA. Sie berechnete Flugbahnen für Raumkapseln, später auch für den Flug von Apollo 11 zum Mond. - -1962 sollte John Glenn als erster Amerikaner die Erde umkreisen. Die Flugbahn hatte ein neuer IBM-Computer berechnet. Glenn traute der Maschine nicht ganz und sagte: „Holt das Mädchen, sie soll die Zahlen prüfen. Wenn sie sagt, sie stimmen, fliege ich.“ Katherine Johnson rechnete alles von Hand nach, also ein echter Schreibtischtest. Ihre Zahlen stimmten, und Glenn flog. - -Katherine Johnson war Schwarz, und im US-Bundesstaat Virginia trennten damals Gesetze Schwarze und Weiße. Im Film „Hidden Figures“ (2016) muss sie für jeden Toilettengang quer über das Gelände laufen. Das ist für den Film zugespitzt: Getrennte Toiletten gab es wirklich, aber Katherine Johnson hat das Schild nach eigener Aussage einfach ignoriert. - -Ihre Kollegin Dorothy Vaughan merkte früh, dass die IBM-Computer die Rechenarbeit übernehmen würden. Sie brachte sich und ihrem ganzen Team die Programmiersprache FORTRAN bei. So wurden aus menschlichen Computern Programmiererinnen. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), Mathematikerin bei der NASA (Foto: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), gemeinfrei). -::: - -## Drei Arten von Fehlern - -Fehler gehören zum Programmieren dazu, auch bei Profis. Man unterscheidet drei Arten, denn jede findest du auf einem anderen Weg: - -- **Syntaxfehler** (syntax error): Der Text hält sich nicht an die Regeln der Sprache, z. B. fehlt am Ende ein \`;\`. Das Programm startet gar nicht. -- **Laufzeitfehler** (runtime error): Das Programm startet, bricht aber an einer Stelle ab, z. B. beim Teilen durch 0. -- **Logikfehler** (logic error): Das Programm läuft bis zum Ende, aber das Ergebnis ist falsch, z. B. weil Klammern fehlen: \`a + b / 2\` statt \`(a + b) / 2\`. - -So zeigt dir VisuStruct die Fehler: - -- **Syntaxfehler:** Im Struktogramm passieren sie seltener, weil die Kästen die Form vorgeben. Im Bereich [Code](#zeige-codelive) wird die Stelle rot unterstrichen, die Zeilennummer wird rot, und dazu steht z. B. **Zeile 2: Hier fehlt noch: ;**. -- **Laufzeitfehler:** Die Simulation bleibt auf dem Kasten stehen und meldet z. B. **Division durch null.** Im Bereich Code wird die Zeilennummer rot, und die [Konsole](#zeige-konsole) nennt die Zeile: **Zeile 3: Division durch null.** -- **Logikfehler:** Hier meldet niemand etwas. Der Computer weiß nicht, was du eigentlich wolltest. Nur du kennst das richtige Ergebnis, darum hilft hier der Schreibtischtest. - -> Eine rote Zeilennummer zeigt einen Fehler. Der runde rote Punkt vor der Zeilennummer ist etwas anderes: ein Haltepunkt. Den lernst du unten bei „Jetzt in Java“ kennen. - -## Fehler finden - -Gibt ein Struktogramm etwas Falsches aus, ist das ein Logikfehler. Dann gehst du so vor: - -1. **Erwartung aufschreiben:** Was soll bei einer bestimmten Eingabe herauskommen? -2. **Schritt für Schritt ausführen:** Starte die Simulation mit **Schritt** und beobachte die Variablen. -3. **Die Stelle finden:** Wo weicht ein Wert zum ersten Mal von deiner Erwartung ab? Genau dort steckt der Fehler. - -Häufige Fehler in einer Sequenz sind: - -- Die **Reihenfolge** stimmt nicht: Eine Variable wird benutzt, bevor sie einen Wert hat. -- Ein Wert wird **überschrieben**, bevor er gebraucht wird. -- **Klammern** fehlen: \`a + b / 2\` ist etwas anderes als \`(a + b) / 2\`. -- Zwei ganze Zahlen werden geteilt: \`7 / 2\` ergibt \`3\`, weil bei \`int\` die Nachkommastellen wegfallen. - -> Später kommen Verzweigungen und Schleifen dazu. Dann zeigt dir die Simulation auch, welcher Weg genommen wird und wie oft eine Schleife läuft. - -::: angeberwissen -Am 9. September 1947 fand das Team von Grace Hopper im Rechner Harvard Mark II eine echte Motte, die zwischen zwei Kontakten klemmte. Sie klebten sie ins Logbuch und schrieben dazu: „Erster echter Fall, dass ein Bug gefunden wurde.“ Die Motte liegt heute in einem Museum in Washington. - -Grace Hopper war eine der ersten Programmiererinnen der Welt. 1952 baute sie einen der ersten **Compiler**: ein Programm, das Code in Maschinencode übersetzt. Sie fand, dass man Computern Befehle in fast normalem Englisch geben sollte. Daraus entstand die Programmiersprache COBOL, mit der viele Banken bis heute rechnen. Bei der US-Marine wurde sie sogar Admiralin. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), Informatikerin und Admiralin (Foto: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), gemeinfrei). -::: - -## Übung: Tauschen reparieren - -::: übung sim-tausch -titel: Tauschen reparieren -auftrag: Das Struktogramm soll zwei Zahlen einlesen und sie vertauscht wieder ausgeben. Bei 3 und 5 soll also erst **5** und dann **3** erscheinen. Es gibt aber zweimal 5 aus. - Mache einen Schreibtischtest oder nutze **Schritt** und repariere den Fehler. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Gehe das Diagramm mit **Schritt** durch und schreibe nach jedem Schritt die Werte von a und b auf. Wo verschwindet die 3? -tipp: Schau dir a nach dem Schritt \`a = b\` an. Wo ist der alte Wert von a geblieben? Merke ihn dir vorher in einer Hilfsvariablen, wie im Beispiel oben. -lösung: sim-tausch-loesung.visustruct -::: - -## Übung: Kinokarten reparieren - -::: übung sim-kino -titel: Kinokarten reparieren -auftrag: Eine Kinokarte kostet 8.50 €. Das Struktogramm soll nach der Anzahl fragen und den Gesamtpreis ausgeben, bei 2 Karten also \`Gesamt: 17.0\`. - Starte die Simulation. Sie bleibt mit einer Fehlermeldung stehen. Finde heraus, warum, und repariere das Struktogramm. -start: sim-kino-start.visustruct -test: 2 -> Gesamt: 17.0 -test: 3 -> Gesamt: 25.5 -test: 0 -> Gesamt: 0.0 -braucht: anweisung -tipp: Lies die Fehlermeldung der Simulation genau. Welche Variable kennt sie noch nicht? -tipp: Die Simulation führt die Kästen von oben nach unten aus. Hat gesamt schon einen Wert, wenn die Ausgabe drankommt? Du kannst Kästen mit der Maus verschieben. -lösung: sim-kino-loesung.visustruct -::: - -> Läuft eine Simulation endlos weiter, klicke auf **Stopp**. Beim **Prüfen** einer Übung bricht VisuStruct nach sehr vielen Schritten von selbst ab. - -## Jetzt in Java - -Auch Java-Code kannst du Schritt für Schritt ausführen. Im Bereich [Code](#zeige-codelive) heißt das Werkzeug dafür **Debugger**: - -- Klicke links auf eine Zeilennummer. Ein roter Punkt erscheint, das ist ein **Haltepunkt**. [▶︎ Ausführen](#zeige-ausfuehren) hält vor dieser Zeile an. -- Der Knopf [Einzelschritt](#zeige-einzelschritt) führt genau eine Zeile aus, wie **Schritt** in der Simulation. -- Unter dem Code steht, welchen Wert jede Variable gerade hat. Das ist dein Schreibtischtest, nur dass der Computer mitschreibt. - -::: übung l07-java-pizza -titel: Pizzabestellung reparieren -code: java -auftrag: Eine Pizza kostet 8 Euro, Extra-Käse 2 Euro. Für 3 Pizzen mit Extra-Käse soll das Programm **Gesamt: 30** ausgeben. Es gibt aber 14 aus. - Gehe den Java-Code mit dem Einzelschritt durch und beobachte \`gesamt\`. Finde den Fehler und repariere ihn im Code. -start: l07-java-pizza-start.visustruct -test: - -> Gesamt: 30 -braucht: anweisung -tipp: Welcher Wert steht nach der vierten Zeile in \`gesamt\`? Rechne im Kopf nach, was Java zuerst ausrechnet. -tipp: Wie in Mathe gilt Punkt vor Strich: \`preis + kaese * anzahl\` ist \`8 + 6\`. Setze Klammern: \`(preis + kaese) * anzahl\`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Lückentext: Schreibtischtest - -::: lückentext -titel: Was steht in den Variablen? -auftrag: Mach einen Schreibtischtest im Kopf. Trage hinter jeder Zeile ein, welchen Wert die Variable danach hat, und in der letzten Zeile, was ausgegeben wird. -\`\`\`java -int a = 5; -int b = 2; -a = a + b; // a ist jetzt [[7]] -b = a * 2; // b ist jetzt [[14]] -a = b - a; // a ist jetzt [[7]] -System.out.println(a + b); // Ausgabe: [[21]] -\`\`\` -erklärung: Jede Zeile rechnet mit den Werten, die gerade in den Variablen stehen: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, Ausgabe 7 + 14 = 21. Mit dem Einzelschritt im Bereich Code kannst du es nachprüfen. -::: - -## Fehler finden: Spielstand - -::: übung l07-java-fehler -titel: Spielstand reparieren -code: java -auftrag: Ludwig bekommt in einem Spiel erst 10 Punkte, dann 5 Punkte, dann verliert er 3 Punkte. Am Ende soll **Punkte: 12** erscheinen. Es erscheint aber **Punkte: 3**. - Gehe den Code mit dem Einzelschritt durch und beobachte \`punkte\`. Repariere die falsche Zeile und klicke auf Prüfen. -start: l07-java-fehler-start.visustruct -test: - -> Punkte: 12 -braucht: anweisung -tipp: Nach welcher Zeile hat \`punkte\` plötzlich den Wert 3? Was soll in dieser Zeile eigentlich passieren? -tipp: Ludwig verliert 3 Punkte, also wird vom alten Wert abgezogen: \`punkte = punkte - 3;\` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l07-java-laeuft-nicht -titel: Bonus reparieren -code: java -auftrag: Ludwig hat 10 Punkte und bekommt 5 Bonuspunkte. Am Ende soll **Punkte: 15** erscheinen. Der Code läuft aber nicht. Gehe ihn auch mit dem Einzelschritt durch: In welcher Zeile bleibt er stehen? - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Punkte: 15 -braucht: anweisung -tipp: Java arbeitet den Code von oben nach unten ab. Gibt es \`bonus\` schon, wenn damit gerechnet wird? -tipp: Lege \`bonus\` vor der Rechnung an: erst \`int bonus = 5;\`, dann \`punkte = punkte + bonus;\` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Mach im Kopf den Schreibtischtest und tippe auf die falsche Zeile, bevor die Zeit abläuft. Die Programme sind in Java, aber jede Zeile ist ein Kasten wie im Struktogramm. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int hilf = a; - a = b; - b = a; -frage: Soll die Werte von a und b tauschen. -fehler: 5 -besser: b = hilf; -> In a steht schon der Wert von b. Der alte Wert von a steckt in hilf. - -runde: - int a = 4; - int b = 6; - double mittel = a + b / 2; - System.out.println(mittel); -frage: Soll den Mittelwert 5.0 ausgeben. -fehler: 3 -besser: double mittel = (a + b) / 2.0; -> Ohne Klammern wird zuerst geteilt: 4 + 6 / 2 = 7. - -runde: - int karten = 3; - double preis = 8.5; - double gesamt = karten + preis; - System.out.println("Gesamt: " + gesamt); -frage: Soll den Preis für 3 Kinokarten zu je 8,50 € ausgeben. -fehler: 3 -besser: double gesamt = karten * preis; -> Für den Gesamtpreis wird malgenommen, nicht addiert. - -runde: - int minuten = 135; - int stunden = minuten / 60; - int rest = minuten / 60; - System.out.println(stunden + " h " + rest + " min"); -frage: Soll 2 h 15 min ausgeben. -fehler: 3 -besser: int rest = minuten % 60; -> Den Rest beim Teilen liefert \`%\`. Mit \`/\` stünde auch in rest eine 2. - -runde: - String name = "Ada"; - System.out.println("Hallo " + "name" + "!"); -frage: Soll „Hallo Ada!“ ausgeben. -fehler: 2 -besser: System.out.println("Hallo " + name + "!"); -> In Anführungszeichen ist name nur ein Text. Die Variable steht ohne Anführungszeichen. - -runde: - double preis = 100; - preis = preis * 0.1; - System.out.println(preis); -frage: Soll den Preis um 10 % erhöhen, also 110.0 ausgeben. -fehler: 2 -besser: preis = preis + preis * 0.1; -> Mal 0.1 ergibt nur die 10 %, also 10.0. Die müssen zum alten Preis dazukommen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Simulation starten, anhalten, einzelne Schritte ausführen und stoppen. → Die Simulation starten -- Ich kann während der Simulation Variablen, Ausgabe und den letzten Schritt ablesen. → Was die Simulation anzeigt -- Ich kann einen Schreibtischtest als Tabelle von Hand durchführen. → Der Schreibtischtest -- Ich kann Syntaxfehler, Laufzeitfehler und Logikfehler unterscheiden. → Drei Arten von Fehlern -- Ich kann einen Fehler finden, indem ich die Werte mit meiner Erwartung vergleiche. → Fehler finden -- Ich kann Java-Code mit dem Einzelschritt und Haltepunkten verfolgen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Mit welchem Knopf führst du genau einen Schritt aus? -→ Die Simulation starten -- [ ] Start -- [x] Schritt -- [ ] Stopp -> Start läuft automatisch weiter, Schritt führt nur einen Schritt aus. - -? Was schreibst du beim Schreibtischtest nach jedem Schritt auf? -→ Der Schreibtischtest -- [ ] Nur die Ausgabe am Ende -- [x] Die aktuellen Werte aller Variablen -- [ ] Den erzeugten Java-Code -> So siehst du genau, an welcher Stelle ein Wert anders ist als erwartet. - -? Erst \`int a = 3\`, dann \`int b = 5\`, dann \`a = b\`. Welche Werte haben a und b danach? -→ Der Schreibtischtest -- [ ] a = 5 und b = 3 -- [x] a = 5 und b = 5 -- [ ] a = 3 und b = 5 -> Der alte Wert von a ist überschrieben. Darum braucht man beim Tauschen eine Hilfsvariable. - -? Im Feld Letzter Schritt steht \`preis = 4 * 2.5 -> 10.0\`. Was bedeutet das? -→ Was die Simulation anzeigt -- [ ] Die Variable preis hat jetzt den Wert 4. -- [x] 4 * 2.5 wurde ausgerechnet und das Ergebnis 10.0 in preis gespeichert. -- [ ] Die Simulation hat einen Fehler gefunden. - -? Die Simulation meldet **Unbekannte Variable: summe**. Was ist ein wahrscheinlicher Grund? -→ Fehler finden -- [x] summe wird benutzt, bevor sie einen Wert bekommen hat. -- [ ] summe ist zu groß. -- [ ] Das Struktogramm hat zu viele Kästen. - -? Was macht ein roter Punkt vor einer Zeilennummer im Bereich Code? -→ Jetzt in Java -- [ ] Er markiert einen Fehler. -- [x] Das Programm hält vor dieser Zeile an. -- [ ] Die Zeile wird übersprungen. -> Das ist ein Haltepunkt. Mit ▶︎ oder dem Einzelschritt geht es weiter. -? Ordne jedem Fehler seine Fehlerart zu. -→ Drei Arten von Fehlern -= Am Zeilenende fehlt das Semikolon. -> Syntaxfehler -= Das Programm teilt durch 0 und bricht ab. -> Laufzeitfehler -= Der Durchschnitt ist zu groß, weil Klammern fehlen. -> Logikfehler - -? Das Programm läuft ohne Meldung bis zum Ende, gibt aber 12 statt 21 aus. Welche Fehlerart ist das? -→ Drei Arten von Fehlern -- [ ] Syntaxfehler -- [ ] Laufzeitfehler -- [x] Logikfehler -> Es gibt keine Meldung, das Ergebnis ist trotzdem falsch. So einen Fehler findest du nur mit deiner Erwartung und dem Schreibtischtest. -::: -`,Qv=`--- -titel: Verzweigung -bereich: Grundlagen -kurz: Mit einer Bedingung entscheiden, welcher von zwei Wegen ausgeführt wird. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass bei einer Verzweigung genau ein Zweig ausgeführt wird -- die Vergleichsoperatoren == != < <= > >= anwenden -- == und = unterscheiden -- eine Verzweigung mit einem leeren Zweig bauen -- eine Verzweigung mit if und else in Java schreiben -::: - -## Wenn, dann, sonst - -Bisher wurden alle Kästen immer ausgeführt. Oft soll ein Programm aber **entscheiden**: Wenn eine Bedingung erfüllt ist, tue das eine, sonst das andere. - -::: diagramm l07-alter-pruefen.visustruct -Je nach eingegebenem Alter erscheint die eine oder die andere Meldung. Probiere in der Simulation 17 und 18 aus. -::: - -Dafür gibt es die **Verzweigung**. Oben im Dreieck steht die Bedingung. Darunter teilt sich das Struktogramm in zwei Zweige: - -- **true** (links): wird ausgeführt, wenn die Bedingung wahr ist -- **false** (rechts): wird ausgeführt, wenn die Bedingung falsch ist - -Es wird immer **genau einer** der beiden Zweige ausgeführt. Danach geht es unter der Verzweigung weiter. - -## Eine Verzweigung einfügen - -Ziehe die **Verzweigung** aus dem Bereich Elemente ins Struktogramm. Klicke das Dreieck an und schreibe im Objektinspektor die Bedingung, z. B. \`alter >= 18\`. Das Wort \`if\` steht schon davor. - -In jedem Zweig liegt zuerst ein Platzhalter ø. Ziehe dort Anweisungen hinein. In einem Zweig dürfen auch mehrere Kästen untereinander stehen. - -## Vergleichsoperatoren - -Eine Bedingung ist meistens ein **Vergleich**. Sein Ergebnis ist immer \`true\` oder \`false\`. - -- \`==\` gleich, z. B. \`zahl == 0\` -- \`!=\` ungleich, z. B. \`antwort != 42\` -- \`<\` kleiner, \`<=\` kleiner oder gleich -- \`>\` größer, \`>=\` größer oder gleich - -> Verwechsle \`==\` nicht mit \`=\`. Ein einzelnes \`=\` ist eine Zuweisung und speichert einen Wert. Zum Vergleichen brauchst du zwei Gleichheitszeichen. - -Mit \`%\` prüfst du, ob eine Zahl durch eine andere teilbar ist: \`zahl % 2 == 0\` ist wahr, wenn \`zahl\` gerade ist. - -## Übung: Gerade oder ungerade - -::: übung l07-gerade -titel: Gerade oder ungerade -auftrag: Frage eine ganze Zahl ab. Gib **gerade** aus, wenn sie gerade ist, und sonst **ungerade**. -test: 4 -> gerade -test: 7 -> ungerade -test: 0 -> gerade -braucht: verzweigung -tipp: Hier gibt es zwei Möglichkeiten, also brauchst du eine Verzweigung. Woran erkennst du eine gerade Zahl? -tipp: Eine Zahl ist gerade, wenn beim Teilen durch 2 kein Rest bleibt. Die Bedingung lautet \`zahl % 2 == 0\`. -lösung: l07-gerade-loesung.visustruct -::: - -## Übung: Die größere Zahl - -::: übung l07-maximum -titel: Die größere Zahl -auftrag: Frage zwei ganze Zahlen ab und gib nur die größere von beiden aus. Sind beide gleich, gib diese Zahl aus. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: Eine Verzweigung vergleicht die beiden Zahlen. In jedem Zweig steht genau eine Ausgabe. -tipp: Vergleiche mit \`a > b\`. Im true-Zweig gibst du \`a\` aus, im false-Zweig \`b\`. -lösung: l07-maximum-loesung.visustruct -::: - -## Ein leerer Zweig - -Manchmal soll nur dann etwas passieren, wenn die Bedingung wahr ist, und sonst gar nichts. Dann bleibt der false-Zweig **leer**. Im Struktogramm steht dort der Platzhalter **ø**. - -::: diagramm l07-rabatt.visustruct -Ab 50 Euro gibt es 5 Euro Rabatt. Sonst passiert nichts, der false-Zweig ist leer. Die Ausgabe darunter wird in beiden Fällen ausgeführt. -::: - -::: angeberwissen -Eine einzige vergessene Verzweigung hat 2014 halb das Internet in Aufregung versetzt: die Sicherheitslücke **Heartbleed** in OpenSSL, einem Programm, das viele Webseiten für die Verschlüsselung benutzen. Ein Computer konnte dem Server sagen: „Schick mir das Wort *Hut* zurück, es hat 500 Buchstaben.“ Der Server hat nicht geprüft, ob die 500 zur echten Länge 3 passen, und einfach 500 Zeichen aus seinem Speicher zurückgeschickt. Darin standen manchmal Passwörter anderer Leute. Gefehlt hat nur ein if: Wenn die angegebene Länge größer ist als die echte, dann nichts zurückschicken. Mit Klammern hatte das übrigens nichts zu tun. Mäh, prüf deine Eingaben, bevor du ihnen glaubst. -::: - -## Übung: Betrag - -::: übung l07-betrag -titel: Betrag einer Zahl -auftrag: Frage eine ganze Zahl ab. Ist sie negativ, mache sie positiv. Gib danach die Zahl aus. Aus -5 wird also 5, und 7 bleibt 7. -Nutze eine Verzweigung mit einem leeren Zweig und nur **einer** Ausgabe unter der Verzweigung. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Nur negative Zahlen müssen verändert werden. Was passiert mit einer Zahl, wenn du sie mal -1 nimmst? -tipp: Die Bedingung ist \`zahl < 0\`. Im true-Zweig drehst du das Vorzeichen um, z. B. mit \`zahl = 0 - zahl\`. Der false-Zweig bleibt leer. -lösung: l07-betrag-loesung.visustruct -::: - -## Jetzt in Java - -So sieht die Altersprüfung vom Anfang dieser Lektion in Java aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie alt bist du?"); -int alter = scanner.nextInt(); -if (alter >= 18) { - System.out.println("Du bist volljährig."); -} else { - System.out.println("Du bist noch nicht volljährig."); -} -\`\`\` - -- Die Bedingung steht nach \`if\` in **runden Klammern** \`( )\`. -- Der true-Zweig steht in **geschweiften Klammern** \`{ }\`, der false-Zweig folgt nach \`else\` in eigenen geschweiften Klammern. Hinter einer geschweiften Klammer steht kein Semikolon. -- Ist der false-Zweig leer, lässt du \`else { }\` einfach weg. -- Die ersten beiden Zeilen braucht dein Programm einmal, sobald es etwas einliest. Danach liest \`scanner.nextInt()\` eine ganze Zahl ein. Die Frage davor ist eine normale Ausgabe. - -::: angeberwissen -Java hat auch ein Maskottchen: **Duke**, ein kleiner schwarz-weißer Kerl mit großer roter Nase. Er wurde 1992 im Team von Java gezeichnet und winkt seitdem von Konferenzen und T-Shirts. Ludwig findet, ein Geißbock hätte besser gepasst. -::: - -::: übung l08-java-bestanden -titel: Bestanden in Java -code: java -auftrag: Schreibe ein Java-Programm, das nach der Punktzahl fragt. Ab 50 Punkten gibt es **bestanden** aus, sonst **nicht bestanden**. - Tippe den Code im Bereich Code und klicke dann auf Prüfen. -test: 50 -> bestanden -test: 49 -> nicht bestanden -test: 100 -> bestanden -test: 0 -> nicht bestanden -braucht: verzweigung -tipp: Fang mit den beiden Zeilen für den Scanner an. Dann fragst du nach den Punkten, liest sie ein und vergleichst sie mit \`if\`. -tipp: Die Bedingung lautet \`punkte >= 50\`. In den Zweigen steht \`System.out.println("bestanden");\` und nach \`else\` \`System.out.println("nicht bestanden");\`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Lückentext: Gerade oder ungerade - -::: lückentext -titel: Gerade oder ungerade -auftrag: Ergänze die fehlenden Wörter und Zeichen. Das Programm soll ausgeben, ob eine Zahl gerade ist. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Zahl?"); -int zahl = scanner.nextInt(); -[[if]] (zahl [[%]] 2 [[==]] 0) [[{]] - System.out.println("gerade"); -[[}]] [[else]] { - System.out.println("ungerade"); -} -\`\`\` -erklärung: \`zahl % 2\` ist der Rest beim Teilen durch 2. Ist er 0, ist die Zahl gerade. Zum Vergleichen brauchst du \`==\`, ein einzelnes \`=\` ist eine Zuweisung. Beide Zweige stehen in geschweiften Klammern. -::: - -::: angeberwissen -Steht im Zweig nur eine einzige Anweisung, darf man in Java die geschweiften Klammern sogar weglassen. Wir machen das trotzdem nie, und zwar aus diesem Grund: - -\`\`\`java -if (alter >= 18) - System.out.println("Du bist volljährig."); - System.out.println("Du darfst den Film sehen."); -\`\`\` - -Ohne Klammern gehört nur die erste Zeile zum if. Die zweite läuft immer, auch für Zwölfjährige. Die Einrückung täuscht, denn Java achtet nicht auf Leerzeichen. - -Genau so ein Fehler steckte 2014 in iPhones und Macs, bekannt als **„goto fail“**. In der Sprache C stand unter einem if ohne Klammern die Zeile \`goto fail;\` aus Versehen zweimal. Die zweite lief immer und übersprang die Prüfung der Sicherheitszertifikate. In einem offenen WLAN hätte sich ein Angreifer so zum Beispiel als deine Bank ausgeben können. Mäh, Klammern kosten zwei Zeichen, eine Sicherheitslücke kostet viel mehr. -::: - -## Fehler finden: Eintritt - -::: übung l08-java-fehler -titel: Eintritt reparieren -code: java -auftrag: Im Zoo haben Kinder **unter** 6 Jahren freien Eintritt, alle anderen zahlen 4 Euro. Das Programm lässt aber auch Sechsjährige umsonst hinein. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l08-java-fehler-start.visustruct -test: 5 -> Eintritt frei -test: 6 -> 4 Euro -test: 10 -> 4 Euro -test: 0 -> Eintritt frei -braucht: verzweigung -tipp: Probiere das Programm mit 6 aus. Welcher Vergleichsoperator lässt die 6 noch durch? -tipp: „Unter 6“ heißt \`alter < 6\`. \`<=\` bedeutet „kleiner oder gleich“. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l08-java-laeuft-nicht -titel: Film ab 16 -code: java -auftrag: Der Film ist ab 16. Ludwig ist 15, also soll das Programm **Kein Einlass** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> Kein Einlass -braucht: verzweigung -tipp: Schau dir die Bedingung in der Klammer nach \`if\` genau an. Gibt es diese Variable? -tipp: Die Variable heißt \`alter\` mit kleinem a: \`if (alter >= 16) {\` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Weg? - -::: spiel frage -titel: Spiel: Welcher Weg? -intro: Ludwig hat Verzweigungen in Java geschrieben. Rechne die Bedingung im Kopf aus und sag vorher, was ausgegeben wird. Sieben Runden. - -runde: - int x = 5; - if (x > 5) { - System.out.println("groß"); - } else { - System.out.println("klein"); - } -frage: Was wird ausgegeben? -- groß -- [x] klein -- nichts -> 5 > 5 ist falsch, denn 5 ist nicht größer als 5. Also der false-Zweig. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("groß"); - } else { - System.out.println("klein"); - } -frage: Was wird ausgegeben? -- [x] groß -- klein -- groß und klein -> \`>=\` heißt „größer oder gleich“. 5 >= 5 ist wahr. - -runde: - int zahl = 7; - if (zahl % 2 == 0) { - System.out.println("gerade"); - } else { - System.out.println("ungerade"); - } -frage: Was wird ausgegeben? -- gerade -- [x] ungerade -- 1 -> 7 % 2 ist der Rest 1, also nicht 0. Die Zahl ist ungerade. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: Was wird ausgegeben? -- -4 -- [x] 4 -- nichts -> -4 < 0 ist wahr, also wird das Vorzeichen umgedreht. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: Was wird ausgegeben? -- -4 -- [x] 4 -- nichts -> 4 < 0 ist falsch. Der false-Zweig ist leer, das Programm macht einfach unten weiter. - -runde: - int note = 3; - if (note != 3) { - System.out.println("anders"); - } else { - System.out.println("drei"); - } -frage: Was wird ausgegeben? -- anders -- [x] drei -- nichts -> \`!=\` heißt „ungleich“. 3 != 3 ist falsch. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: Was wird ausgegeben? -- 8 -- [x] 12 -- 20 -> Das Programm gibt die größere der beiden Zahlen aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass bei einer Verzweigung genau ein Zweig ausgeführt wird. → Wenn, dann, sonst -- Ich kann die Vergleichsoperatoren == != < <= > >= anwenden. → Vergleichsoperatoren -- Ich kann == und = unterscheiden. → Vergleichsoperatoren -- Ich kann eine Verzweigung mit einem leeren Zweig bauen. → Ein leerer Zweig -- Ich kann eine Verzweigung mit if und else in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welcher Zweig wird ausgeführt, wenn die Bedingung falsch ist? -→ Wenn, dann, sonst -- [ ] Der true-Zweig -- [x] Der false-Zweig -- [ ] Beide Zweige -- [ ] Keiner, das Programm endet -> Bei false wird der rechte Zweig ausgeführt. Danach geht es unter der Verzweigung weiter. - -? Mit welcher Bedingung prüfst du, ob alter genau 18 ist? -→ Vergleichsoperatoren -- [ ] alter = 18 -- [x] alter == 18 -- [ ] alter >= 18 -> Ein einzelnes = ist eine Zuweisung. Zum Vergleichen brauchst du ==. - -? Was bedeutet ø in einem Zweig? -→ Ein leerer Zweig -- [x] In diesem Fall passiert nichts. -- [ ] Das Programm endet hier. -- [ ] Die Bedingung ist falsch. - -? Die Bedingung lautet \`punkte >= 50\`. Bei welchem Wert ist sie wahr? -→ Vergleichsoperatoren -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= heißt größer oder gleich. 50 gehört also dazu. - -? In Schulbüchern hat die Verzweigung oft noch einen anderen Namen. Welchen? -→ Wenn, dann, sonst -- [ ] Mehrfachauswahl -- [x] Bedingte Anweisung -- [ ] Wiederholungsstruktur -> Die Anweisungen in einem Zweig werden nur unter einer Bedingung ausgeführt. Darum heißt sie auch bedingte Anweisung. - -? Wo steht in Java die Bedingung einer Verzweigung? -→ Jetzt in Java -- [x] In runden Klammern direkt nach if -- [ ] In geschweiften Klammern nach else -- [ ] Hinter einem Semikolon -> \`if (alter >= 18) { … }\`: Die runden Klammern gehören zur Bedingung, die geschweiften zum Zweig. -::: -`,$v=`--- -titel: Bedingungen verknüpfen -bereich: Grundlagen -kurz: Mehrere Bedingungen mit und, oder und nicht verbinden und Wertebereiche prüfen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, wann eine Verknüpfung mit && wahr ist -- erklären, wann eine Verknüpfung mit || wahr ist -- eine Bedingung mit ! umdrehen -- prüfen, ob ein Wert in einem Bereich wie 1 bis 6 liegt -- die Wahrheitstabelle für &&, || und ! lesen -- das Ergebnis einer Bedingung in einer boolean-Variablen speichern -- zusammengesetzte Bedingungen sicher auswerten -- Bedingungen in Java mit && und || verknüpfen -::: - -## Und: && - -Manchmal reicht ein einzelner Vergleich nicht. Ein Jugendticket gibt es zum Beispiel nur, wenn jemand **mindestens 12 und jünger als 18** ist. - -Mit \`&&\` (und) verbindest du zwei Bedingungen. Das Ergebnis ist nur dann \`true\`, wenn **beide** Bedingungen wahr sind. - -::: diagramm l08-jugendticket.visustruct -Beide Vergleiche müssen stimmen. Probiere in der Simulation 11, 12, 17 und 18 aus. -::: - -## Oder: || - -Mit \`||\` (oder) ist das Ergebnis \`true\`, wenn **mindestens eine** der Bedingungen wahr ist. Es dürfen auch beide wahr sein. - -\`\`\` -tag == 6 || tag == 7 -\`\`\` - -Diese Bedingung ist wahr, wenn \`tag\` den Wert 6 oder den Wert 7 hat, also am Samstag oder Sonntag. - -> Die beiden senkrechten Striche \`|\` tippst du auf vielen Tastaturen mit AltGr und der Taste \`<\`, am Mac mit Option und 7. - -## Übung: Wochenende - -::: übung l08-wochenende -titel: Wochenende -auftrag: Frage einen Wochentag als Zahl ab (1 ist Montag, 7 ist Sonntag). Gib **Wochenende** aus, wenn es Samstag oder Sonntag ist, sonst **Werktag**. -test: 6 -> Wochenende -test: 7 -> Wochenende -test: 1 -> Werktag -test: 5 -> Werktag -braucht: verzweigung -tipp: Wochenende ist, wenn der Tag 6 **oder** 7 ist. Dafür brauchst du zwei Vergleiche in einer Bedingung. -tipp: Samstag ist 6, Sonntag ist 7. Verbinde die beiden Vergleiche mit \`||\`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Wertebereiche prüfen - -Schulnoten gehen von 1 bis 6. Ob eine Note **im Bereich** liegt, prüfst du mit zwei Vergleichen und \`&&\`: - -\`\`\` -note >= 1 && note <= 6 -\`\`\` - -::: grafik l08-zahlenstrahl.svg -Gültig ist der grüne Bereich von 1 bis 6, beide Ränder gehören dazu. -::: - -Umgekehrt ist eine Note **außerhalb** des Bereichs, wenn sie zu klein **oder** zu groß ist: - -::: diagramm l08-note.visustruct -Eine Note kleiner als 1 oder größer als 6 gibt es nicht. -::: - -> Die Schreibweise aus Mathe \`1 <= note <= 6\` funktioniert nicht. Du musst sie in zwei Vergleiche aufteilen und mit \`&&\` verbinden. - -## Übung: Gültige Note - -::: übung l08-note-gueltig -titel: Gültige Note -auftrag: Frage eine Note als ganze Zahl ab. Gib **gültig** aus, wenn sie zwischen 1 und 6 liegt (1 und 6 gehören dazu), sonst **ungültig**. -test: 1 -> gültig -test: 6 -> gültig -test: 3 -> gültig -test: 0 -> ungültig -test: 7 -> ungültig -braucht: verzweigung -tipp: Eine gültige Note muss zwei Dinge gleichzeitig erfüllen: nicht zu klein **und** nicht zu groß. -tipp: Die Note muss größer oder gleich 1 und gleichzeitig kleiner oder gleich 6 sein. -lösung: l08-gueltig-loesung.visustruct -::: - -## Nicht und Klammern - -Mit \`!\` (nicht) drehst du eine Bedingung um: Aus \`true\` wird \`false\` und umgekehrt. - -\`\`\` -!(note >= 1 && note <= 6) -\`\`\` - -Das ist wahr, wenn die Note **nicht** im Bereich liegt, und bedeutet dasselbe wie \`note < 1 || note > 6\`. - -Kommen \`&&\` und \`||\` zusammen vor, wird \`&&\` zuerst ausgewertet, so wie Punkt vor Strich. Setze lieber **Klammern**, dann ist klar, was zusammengehört: - -\`\`\` -(alter >= 12 && alter < 18) || schueler == 1 -\`\`\` - -## Die Wahrheitstabelle - -Eine **Wahrheitstabelle** zeigt auf einen Blick, was bei \`&&\`, \`||\` und \`!\` herauskommt. Links stehen alle vier Möglichkeiten für zwei Bedingungen \`a\` und \`b\`, rechts das Ergebnis: - -::: grafik l09-wahrheitstabelle.svg -Grün ist true, rot ist false. Bei && gibt es nur eine grüne Zeile, bei || nur eine rote. -::: - -Merksätze: - -- \`a && b\` ist nur wahr, wenn **beide** wahr sind. -- \`a || b\` ist nur falsch, wenn **beide** falsch sind. -- \`!a\` dreht den Wert um. - -::: angeberwissen -Der Datentyp \`boolean\` heißt nach **George Boole**. Der Engländer hat schon 1847 gezeigt, dass man mit wahr und falsch rechnen kann wie mit Zahlen, fast hundert Jahre bevor es den ersten Computer gab. Heute steckt seine Logik in jedem Chip. -::: - -## Wahrheitswerte in Variablen - -Das Ergebnis einer Bedingung ist ein **Wahrheitswert** vom Typ \`boolean\`. Du kannst ihn wie eine Zahl in einer Variablen speichern und später benutzen: - -\`\`\` -boolean wochenende = tag == 6 || tag == 7 -\`\`\` - -In der Verzweigung steht dann nur noch \`wochenende\`. Das liest sich fast wie ein Satz: „Wenn Wochenende, dann …“. Schreibe nicht \`wochenende == true\`, denn \`wochenende\` ist ja schon true oder false. - -Mit \`!wochenende\` fragst du nach dem Gegenteil, also nach einem Werktag. - -::: ludwig -Lange Bedingungen werden übersichtlich, wenn du Teile davon in boolean-Variablen mit sprechenden Namen packst, zum Beispiel \`boolean jugendlich = alter >= 12 && alter < 18\`. -::: - -## Spiel: Wahr oder falsch? - -Jetzt bist du dran. Rechne jede Bedingung im Kopf aus. Nach jeder Antwort siehst du, wie VisuStruct sie Schritt für Schritt auswertet. - -::: spiel -alter: 8..20 -tag: 1..7 -note: 0..7 -schueler: boolean -wochenende: boolean -? alter >= 18 -? tag == 6 -? note != 3 -? alter >= 12 && alter < 18 -? tag == 6 || tag == 7 -? schueler && wochenende -? !schueler -? note >= 1 && note <= 6 -? !(alter < 18) -? schueler || alter >= 18 -? (alter >= 12 && alter < 18) || schueler -? !(note >= 1 && note <= 6) -? !wochenende && tag != 3 -? alter >= 12 && alter < 18 && !schueler -::: - -## Übung: Schaltjahr - -::: übung l08-schaltjahr -titel: Schaltjahr -auftrag: Ein Jahr ist ein Schaltjahr, wenn es durch 4 teilbar ist, aber nicht durch 100. Jahre, die durch 400 teilbar sind, sind trotzdem Schaltjahre. -Frage eine Jahreszahl ab und gib **Schaltjahr** oder **kein Schaltjahr** aus. -test: 2024 -> Schaltjahr -test: 2023 -> kein Schaltjahr -test: 1900 -> kein Schaltjahr -test: 2000 -> Schaltjahr -braucht: verzweigung -tipp: Prüfe die Regel zuerst an Beispielen: 2024 ist ein Schaltjahr, 1900 nicht, 2000 schon. Teilbar heißt: Der Rest ist 0. -tipp: Teilbar durch 4 heißt \`jahr % 4 == 0\`, nicht teilbar durch 100 heißt \`jahr % 100 != 0\`. Verbinde diese beiden mit \`&&\`, setze Klammern darum und hänge mit \`||\` den Fall für 400 an. -tipp: Fast die Lösung: Eine einzige Verzweigung mit der Bedingung \`(jahr % 4 == 0 && jahr % 100 != 0) || jahr % 400 == 0\`. Im true-Zweig steht \`output: "Schaltjahr"\`, im false-Zweig \`output: "kein Schaltjahr"\`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Jetzt in Java - -Das Jugendticket vom Anfang sieht in Java so aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie alt bist du?"); -int alter = scanner.nextInt(); -if (alter >= 12 && alter < 18) { - System.out.println("Jugendticket"); -} else { - System.out.println("Kein Jugendticket"); -} -\`\`\` - -Die Zeichen \`&&\`, \`||\` und \`!\` sind genau die, die du schon im Struktogramm benutzt. Sie kommen aus Java. Die ganze Bedingung steht wie immer in den runden Klammern nach \`if\`. - -::: übung l09-java-schule -titel: Schule oder Freizeit -code: java -auftrag: Schreibe ein Java-Programm, das nach der Stunde fragt (0 bis 23). Von 8 bis einschließlich 15 Uhr gibt es **Schule** aus, sonst **Freizeit**. -test: 8 -> Schule -test: 15 -> Schule -test: 16 -> Freizeit -test: 7 -> Freizeit -braucht: verzweigung -tipp: Die Stunde muss zwei Bedingungen gleichzeitig erfüllen. Welcher Operator heißt „und“? -tipp: \`if (stunde >= 8 && stunde <= 15) { … } else { … }\` -lösung: l09-java-schule-loesung.visustruct -::: - -## Lückentext: Bedingungen - -::: lückentext -titel: Note und Wochenende -auftrag: Ergänze die fehlenden Operatoren und Wörter. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Note?"); -int note = scanner.nextInt(); -if (note >= 1 [[&&]] note [[<=]] 6) { - System.out.println("gültige Note"); -} else { - System.out.println("ungültige Note"); -} -System.out.println("Welcher Tag (1 bis 7)?"); -int tag = scanner.nextInt(); -[[boolean]] wochenende = tag == 6 [[||]] tag == 7; -if ([[!]]wochenende) { - System.out.println("Schultag"); -} -\`\`\` -erklärung: Eine Note ist gültig, wenn **beide** Bedingungen stimmen, also \`&&\`. Wochenende ist, wenn **eine** der beiden stimmt, also \`||\`. Das Ergebnis passt in eine \`boolean\`-Variable, und \`!\` dreht es um. -::: - -## Fehler finden: Schulfrei - -::: übung l09-java-fehler -titel: Schulfrei reparieren -code: java -auftrag: Das Programm soll bei Tag 6 (Samstag) und Tag 7 (Sonntag) **schulfrei** ausgeben, sonst **Schule**. Es gibt aber immer **Schule** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l09-java-fehler-start.visustruct -test: 6 -> schulfrei -test: 7 -> schulfrei -test: 3 -> Schule -test: 1 -> Schule -braucht: verzweigung -tipp: Kann \`tag\` gleichzeitig 6 **und** 7 sein? -tipp: Es reicht, wenn eine der beiden Bedingungen stimmt: \`tag == 6 || tag == 7\`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l09-java-laeuft-nicht -titel: Wochenende reparieren -code: java -auftrag: Tag 6 ist Samstag, Tag 7 ist Sonntag. Heute ist Tag 7, also soll **schulfrei** erscheinen. Das Programm läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> schulfrei -braucht: verzweigung -tipp: Die Bedingung besteht aus zwei Teilen. Vergleiche in beiden Teilen den Namen der Variablen. -tipp: Auch im zweiten Teil heißt die Variable \`tag\` mit kleinem t: \`if (tag == 6 || tag == 7) {\` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, wann eine Verknüpfung mit && wahr ist. → Und: && -- Ich kann erklären, wann eine Verknüpfung mit || wahr ist. → Oder: || -- Ich kann eine Bedingung mit ! umdrehen. → Nicht und Klammern -- Ich kann prüfen, ob ein Wert in einem Bereich wie 1 bis 6 liegt. → Wertebereiche prüfen -- Ich kann die Wahrheitstabelle für &&, || und ! lesen. → Die Wahrheitstabelle -- Ich kann das Ergebnis einer Bedingung in einer boolean-Variablen speichern. → Wahrheitswerte in Variablen -- Ich kann zusammengesetzte Bedingungen sicher auswerten (im Spiel mindestens 8 von 10 richtig). → Spiel: Wahr oder falsch? -- Ich kann Bedingungen in Java mit && und || verknüpfen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wann ist \`a && b\` wahr? -→ Und: && -- [ ] Wenn a oder b wahr ist -- [x] Nur wenn a und b beide wahr sind -- [ ] Wenn genau eins von beiden wahr ist -> Bei && müssen beide Teile stimmen. Ist einer falsch, ist das Ganze falsch. - -? Die Variable tag hat den Wert 6. Was ergibt \`tag == 6 || tag == 7\`? -→ Oder: || -- [x] true -- [ ] false -> Bei || reicht es, wenn eine Seite wahr ist. - -? Mit welcher Bedingung prüfst du, ob zahl zwischen 10 und 20 liegt (beide eingeschlossen)? -→ Wertebereiche prüfen -- [ ] 10 <= zahl <= 20 -- [ ] zahl >= 10 || zahl <= 20 -- [x] zahl >= 10 && zahl <= 20 -> Mit || wäre jede Zahl erlaubt, denn jede Zahl ist größer als 10 oder kleiner als 20. - -? Was ergibt \`!(5 > 3)\`? -→ Nicht und Klammern -- [ ] true -- [x] false -> 5 > 3 ist wahr, und ! dreht das um. - -? Wie schreibst du in Java „alter ist mindestens 12 und kleiner als 18“? -→ Jetzt in Java -- [ ] if (12 <= alter < 18) -- [x] if (alter >= 12 && alter < 18) -- [ ] if (alter >= 12 || alter < 18) -> Jeder Vergleich steht für sich, verbunden mit \`&&\`. -::: -`,Yv=`--- -titel: Fallauswahl -bereich: Grundlagen -kurz: Je nach Wert einer Variablen einen von vielen Fällen ausführen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Fallauswahl einfügen, Fälle hinzufügen und Fallnamen ändern -- erklären, wann der Fall default ausgeführt wird -- entscheiden, ob eine Fallauswahl oder eine Verzweigung besser passt -- eine Fallauswahl in Java mit switch schreiben -::: - -## Viele Möglichkeiten, eine Variable - -Ein Getränkeautomat hat drei Tasten: 1 für Wasser, 2 für Saft, 3 für Kakao. Mit Verzweigungen brauchst du dafür drei ineinander geschachtelte Kästen. Das wird schnell breit und unübersichtlich. - -::: diagramm l09-verschachtelt.visustruct -Der Automat mit drei verschachtelten Verzweigungen. Jede Verzweigung prüft wieder dieselbe Variable wahl. -::: - -Immer wenn du **eine Variable** mit **mehreren festen Werten** vergleichst, geht es einfacher: mit der **Fallauswahl**. - -## So ist die Fallauswahl aufgebaut - -Oben im Kasten steht die Variable, nach der ausgewählt wird. Darunter steht für jeden Fall eine eigene Spalte. Über jeder Spalte steht der Wert, zu dem sie gehört. - -::: diagramm l09-automat.visustruct -Derselbe Automat als Fallauswahl. Hat wahl den Wert 2, wird nur die Spalte 2 ausgeführt. -::: - -- Die Simulation sucht den Fall, dessen Name genau zum Wert passt. -- Passt kein Fall, wird **default** ausgeführt. Dieser Fall steht immer ganz rechts. -- Danach geht es unter der Fallauswahl weiter. Es wird immer nur **ein** Fall ausgeführt. - -> Eine neue Fallauswahl hat die Fälle 1, 2 und default. Klicke einen Fall an, um im Objektinspektor seinen **Fallnamen** zu ändern. Mit **Weiteren Fall hinzufügen** bekommst du eine neue Spalte vor default. - -## Fälle mit Texten - -Die Fälle müssen keine Zahlen sein. Du kannst auch nach dem Inhalt einer \`String\`-Variablen auswählen. Als Fallname schreibst du dann den Text in Anführungszeichen, zum Beispiel \`"rot"\`. So erkennt man wie im Code, dass es ein Text ist. - -::: diagramm l09-ampel.visustruct -Eine Ampel: Je nach eingegebener Farbe erscheint ein anderer Hinweis. Probiere in der Simulation auch eine Farbe aus, die es nicht gibt. -::: - -Groß- und Kleinschreibung zählt: \`Rot\` passt nicht zum Fall \`"rot"\`. - -## Übung: Note in Worten - -::: übung l09-note -titel: Note in Worten -auftrag: Frage eine Note als ganze Zahl ab und gib sie in Worten aus: 1 sehr gut, 2 gut, 3 befriedigend, 4 ausreichend, 5 mangelhaft, 6 ungenügend. - Bei jeder anderen Zahl soll **Keine gültige Note** erscheinen. -test: 1 -> sehr gut -test: 4 -> ausreichend -test: 6 -> ungenügend -test: 9 -> Keine gültige Note -braucht: fallauswahl -tipp: Für viele feste Werte ist die Fallauswahl übersichtlicher als viele Verzweigungen. Jede Note bekommt ihren eigenen Fall. -tipp: Füge mit „Weiteren Fall hinzufügen“ so lange Fälle hinzu, bis du die Fälle 1 bis 6 und default hast. Oben in die Fallauswahl schreibst du \`note\`. -lösung: l09-note-loesung.visustruct -::: - -## Übung: Taschenrechner - -::: übung l09-rechner -titel: Taschenrechner -auftrag: Frage nacheinander eine ganze Zahl \`a\`, ein Rechenzeichen als \`String\` und eine zweite ganze Zahl \`b\` ab. - Bei \`+\`, \`-\` und \`*\` gibst du das Ergebnis aus. Bei jedem anderen Zeichen erscheint **Unbekanntes Rechenzeichen**. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Lies erst alle drei Werte ein. Danach entscheidet eine Fallauswahl über das Rechenzeichen, was gerechnet wird. -tipp: Lege vor der Fallauswahl \`int ergebnis = 0\` an. In jedem Fall rechnest du zum Beispiel \`ergebnis = a + b\` und gibst danach \`ergebnis\` aus. Die Fallnamen sind \`"+"\`, \`"-"\` und \`"*"\` in Anführungszeichen, weil das Rechenzeichen ein \`String\` ist. -tipp: Das Gerüst von oben nach unten: drei Eingaben (\`int a\`, \`String zeichen\`, \`int b\`), dann \`int ergebnis = 0\`, dann eine Fallauswahl über \`zeichen\`. Im Fall \`"+"\` stehen \`ergebnis = a + b\` und \`output: ergebnis\`, bei \`"-"\` und \`"*"\` genauso. Im Sonst-Fall steht \`output: "Unbekanntes Rechenzeichen"\`. -lösung: l09-rechner-loesung.visustruct -::: - -## Fallauswahl oder Verzweigung? - -Die Fallauswahl prüft nur, ob ein Wert **genau gleich** einem Fallnamen ist. Daraus folgt: - -- **Fallauswahl:** eine Variable, mehrere feste Werte. Beispiele: Menüauswahl, Wochentag als Zahl, Noten. -- **Verzweigung:** Bereiche und verknüpfte Bedingungen. Beispiele: \`alter >= 18\`, \`temperatur < 0 || temperatur > 30\`. - -Für „Alter ab 18“ brauchst du also weiter die Verzweigung. Du kannst nicht für jedes mögliche Alter einen eigenen Fall anlegen. - -## Jetzt in Java - -Der Getränkeautomat sieht in Java so aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 Wasser, 2 Saft, 3 Kakao"); -int wahl = scanner.nextInt(); -switch (wahl) { - case 1 -> { - System.out.println("Hier ist dein Wasser."); - } - case 2 -> { - System.out.println("Hier ist dein Saft."); - } - case 3 -> { - System.out.println("Hier ist dein Kakao."); - } - default -> { - System.out.println("Diese Taste gibt es nicht."); - } -} -\`\`\` - -- Nach \`switch\` steht in runden Klammern die Variable, die verglichen wird. -- Jeder Fall beginnt mit \`case\`, dem Wert und einem Pfeil \`->\`. Dahinter steht in geschweiften Klammern, was passieren soll. -- \`default\` ist der Sonst-Fall. Er läuft, wenn kein Wert passt. - -::: angeberwissen -Den Pfeil \`case 1 ->\` gibt es in Java erst seit 2020 (Java 14). Davor schrieb man \`case 1:\` und musste jeden Fall mit \`break;\` beenden. Wer das vergaß, lief einfach in den nächsten Fall weiter. Der Pfeil hat also sehr vielen Programmierern Ärger erspart. -::: - -::: übung l10-java-wuerfel -titel: Würfel in Worten -code: java -auftrag: Schreibe ein Java-Programm mit \`switch\`, das eine Zahl einliest. Bei 1 gibt es **eins** aus, bei 2 **zwei**, bei 3 **drei** und sonst **kenne ich nicht**. -test: 1 -> eins -test: 2 -> zwei -test: 3 -> drei -test: 7 -> kenne ich nicht -braucht: fallauswahl -tipp: Du brauchst drei \`case\`-Fälle und einen \`default\`-Fall. -tipp: \`switch (zahl) { case 1 -> { System.out.println("eins"); } … default -> { System.out.println("kenne ich nicht"); } }\` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Lückentext: switch - -::: lückentext -titel: Monate in Worten -auftrag: Ergänze die fehlenden Wörter und Zeichen der Fallauswahl. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welcher Monat (1 bis 3)?"); -int monat = scanner.nextInt(); -[[switch]] (monat) { - [[case]] 1 -> { - System.out.println("Januar"); - } - case 2 [[->]] { - System.out.println("Februar"); - } - [[case]] 3 -> { - System.out.println("März"); - } - [[default]] -> { - System.out.println("Kenne ich nicht"); - } -} -\`\`\` -erklärung: Nach \`switch\` steht die Variable in runden Klammern. Jeder Fall beginnt mit \`case\`, dem Wert und dem Pfeil \`->\`. \`default\` läuft, wenn kein Fall passt. -::: - -## Fehler finden: Rechner - -::: übung l10-java-fehler -titel: Rechner reparieren -code: java -auftrag: Der Rechner liest zwei Zahlen und eine Wahl ein: 1 plus, 2 minus, 3 mal. Bei 7, 3 und Wahl 2 soll **4** herauskommen, er rechnet aber **-4**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Gibt es nicht -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Plus und mal stimmen. Schau dir den Fall \`case 2\` genau an. -tipp: Beim Minus zählt die Reihenfolge: \`a - b\` statt \`b - a\`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l10-java-laeuft-nicht -titel: Note reparieren -code: java -auftrag: Ludwig hat die Note 2 geschrieben. Das Programm soll **gut** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> gut -braucht: fallauswahl -tipp: Welche Variable prüft die Fallauswahl? Steht sie genauso oben? -tipp: In der Klammer nach \`switch\` muss \`note\` mit kleinem n stehen: \`switch (note) {\` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Fall? - -::: spiel frage -titel: Spiel: Welcher Fall? -intro: Ludwig drückt Tasten am Getränkeautomaten und an anderen Fallauswahlen. Was passiert? Sechs Runden. - -runde: - int wahl = 2; - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - case 2 -> { System.out.println("Saft"); } - case 3 -> { System.out.println("Kakao"); } - default -> { System.out.println("Diese Taste gibt es nicht."); } - } -frage: Was wird ausgegeben? -- Wasser -- [x] Saft -- Diese Taste gibt es nicht. -> wahl ist 2, also läuft genau der Fall \`case 2\`. - -runde: - int wahl = 4; - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - case 2 -> { System.out.println("Saft"); } - case 3 -> { System.out.println("Kakao"); } - default -> { System.out.println("Diese Taste gibt es nicht."); } - } -frage: Was wird ausgegeben? -- Kakao -- nichts -- [x] Diese Taste gibt es nicht. -> Kein Fall passt zu 4, also läuft \`default\`. - -runde: - String tag = "montag"; - switch (tag) { - case "Montag" -> { System.out.println("Mathe"); } - case "Dienstag" -> { System.out.println("Sport"); } - default -> { System.out.println("frei"); } - } -frage: Was wird ausgegeben? -- Mathe -- Sport -- [x] frei -> Bei Texten zählt jeder Buchstabe: "montag" mit kleinem m ist nicht "Montag". - -runde: - int note = 0; - switch (note) { - case 1 -> { System.out.println("sehr gut"); } - case 2 -> { System.out.println("gut"); } - case 3 -> { System.out.println("befriedigend"); } - } -frage: Was wird ausgegeben? -- sehr gut -- [x] nichts -- befriedigend -> Kein Fall passt, und es gibt kein \`default\`. Dann passiert einfach nichts. - -runde: -frage: Du willst prüfen, ob \`alter >= 18\` ist. Was passt besser? -- [x] eine Verzweigung -- eine Fallauswahl -> Eine Fallauswahl vergleicht nur mit festen Werten. Für „größer oder gleich“ brauchst du eine Verzweigung. - -runde: -frage: Ein Würfel zeigt 1 bis 6, und für jede Zahl soll ein anderes Wort erscheinen. Was passt besser? -- eine Verzweigung -- [x] eine Fallauswahl -> Eine Variable, sechs feste Werte: genau dafür ist die Fallauswahl da. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Fallauswahl einfügen, Fälle hinzufügen und Fallnamen ändern. → So ist die Fallauswahl aufgebaut -- Ich kann erklären, wann der Fall default ausgeführt wird. → So ist die Fallauswahl aufgebaut -- Ich kann entscheiden, ob eine Fallauswahl oder eine Verzweigung besser passt. → Fallauswahl oder Verzweigung? -- Ich kann eine Fallauswahl in Java mit switch schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Die Variable tag hat den Wert 5. Es gibt die Fälle 1, 2, 3 und default. Was wird ausgeführt? -→ So ist die Fallauswahl aufgebaut -- [ ] Gar nichts -- [ ] Der Fall 3 -- [x] Der Fall default -> Kein Fallname passt zu 5, deshalb greift default. - -? Wie viele Fälle werden bei einem Durchlauf der Fallauswahl ausgeführt? -→ So ist die Fallauswahl aufgebaut -- [x] Genau einer -- [ ] Alle Fälle ab dem passenden -- [ ] Alle Fälle -> Nach dem passenden Fall geht es direkt unter der Fallauswahl weiter. - -? Wofür ist eine Fallauswahl NICHT geeignet? -→ Fallauswahl oder Verzweigung? -- [ ] Menüpunkt 1, 2 oder 3 auswählen -- [x] Prüfen, ob eine Temperatur über 30 Grad liegt -- [ ] Eine Farbe wie rot, gelb oder grün auswerten -> „Über 30“ ist ein Bereich. Dafür brauchst du eine Verzweigung mit einem Vergleich. - -? Du gibst bei der Ampel \`Rot\` ein, der Fall heißt \`"rot"\`. Was passiert? -→ So ist die Fallauswahl aufgebaut -- [ ] Der Fall \`"rot"\` wird ausgeführt. -- [x] Der Fall default wird ausgeführt. -- [ ] Die Simulation meldet einen Fehler. -> Groß- und Kleinschreibung zählt. Rot und rot sind verschiedene Texte. - -? Unter welchem Namen findest du die Fallauswahl auch in Schulbüchern? -→ So ist die Fallauswahl aufgebaut -- [ ] Bedingte Anweisung -- [ ] Sequenz -- [x] Mehrfachauswahl -> Aus vielen Fällen wird einer ausgewählt. Darum heißt sie auch Mehrfachauswahl. - -? Wie heißt in Java der Sonst-Fall einer Fallauswahl? -→ Jetzt in Java -- [ ] else -- [x] default -- [ ] case 0 -::: -`,Kv=`--- -titel: While-Schleife -bereich: Grundlagen -kurz: Schritte wiederholen, solange eine Bedingung wahr ist. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die While-Schleife ihre Bedingung vor jedem Durchlauf prüft -- eine Zählvariable mit Startwert, Bedingung und Veränderung verwenden -- erkennen, wenn eine Schleife nie endet, und den Fehler beheben -- eine Schleife im Schreibtischtest Durchlauf für Durchlauf verfolgen -- eine While-Schleife in Java schreiben -::: - -## Wiederholen, solange etwas gilt - -Oft soll ein Programm etwas mehrmals tun. Statt denselben Kasten fünfmal hinzuschreiben, nimmst du eine **Schleife**. - -Die **While-Schleife** wiederholt ihren Inhalt, **solange** ihre Bedingung wahr ist. Die Bedingung steht oben im Kopf des Kastens. Der Inhalt steht eingerückt darunter, links von einer Klammer eingefasst. - -::: diagramm l10-countdown.visustruct -Ein Countdown: Solange zahl größer als 0 ist, wird zahl ausgegeben und um 1 kleiner. Danach erscheint Start! -::: - -## Die Bedingung kommt zuerst - -Die While-Schleife ist **kopfgesteuert**. Vor **jedem** Durchlauf wird die Bedingung geprüft: - -1. Bedingung prüfen. -2. Ist sie wahr, den Inhalt einmal ausführen und wieder bei 1 anfangen. -3. Ist sie falsch, geht es unter der Schleife weiter. - -::: grafik l10-kopfgesteuert.svg -Nach jedem Durchlauf geht es zurück zur Bedingung. Erst wenn sie false ist, geht es unter der Schleife weiter. -::: - -Im Countdown wird die Bedingung viermal geprüft: bei 3, 2 und 1 ist sie wahr, bei 0 ist sie falsch. - -> Ist die Bedingung schon beim ersten Prüfen falsch, wird der Inhalt **kein einziges Mal** ausgeführt. Starte den Countdown mit \`int zahl = 0\`, dann erscheint nur Start! - -## Eine Zählvariable - -Viele Schleifen zählen mit. Dafür brauchst du drei Dinge: - -- **Startwert vor der Schleife**, z. B. \`int i = 1\` -- **Bedingung im Kopf**, z. B. \`i <= 5\` -- **Veränderung im Inhalt**, z. B. \`i = i + 1\` (kurz \`i++\`) - -Fehlt einer dieser Teile, zählt die Schleife falsch oder gar nicht. - -::: angeberwissen -Zählschleifen sind uralt! Im Artikel von 1973 haben Nassi und Shneiderman ihre Idee unter anderem an der **Fakultät** gezeigt: 5! = 1 · 2 · 3 · 4 · 5 = 120. Hier ist meine Ziegen-Version davon. Probier sie mit **Schritt** in der Simulation aus und schau zu, wie \`fakultaet\` wächst. Määäh, das geht schnell! -::: - -::: diagramm l10-fakultaet.visustruct -Die Fakultät mit einer Zählvariable \`i\`, nachgebaut nach einem Beispiel aus dem Artikel von 1973. -::: - -## Übung: Zählen bis n - -::: übung l10-bis-n -titel: Zählen bis n -auftrag: Frage eine ganze Zahl \`n\` ab und gib alle Zahlen von 1 bis \`n\` aus, jede in einer eigenen Zeile. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: Du brauchst eine Zählvariable, die bei 1 beginnt und in jedem Durchlauf um 1 wächst. -tipp: Lege vor der Schleife \`int i = 1\` an. Die Bedingung ist \`i <= n\`. Im Inhalt gibst du \`i\` aus und erhöhst danach \`i\` um 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Vorsicht, Endlosschleife - -Wenn sich im Inhalt nichts an der Bedingung ändert, bleibt sie für immer wahr. Die Schleife hört nie auf. Das nennt man **Endlosschleife**. - -::: diagramm l10-endlos.visustruct -Hier fehlt zahl = zahl - 1. Die Schleife gibt immer wieder 3 aus und erreicht Start! nie. -::: - -Frage dich bei jeder Schleife: **Was im Inhalt sorgt dafür, dass die Bedingung irgendwann falsch wird?** Beim Prüfen einer Übung bricht VisuStruct nach sehr vielen Schritten ab und meldet eine mögliche Endlosschleife. - -## Übung: Verdoppeln bis 100 - -::: übung l10-verdoppeln -titel: Verdoppeln bis 100 -auftrag: Frage eine ganze Zahl ab. Verdopple sie so lange, bis sie mindestens 100 ist. - Gib am Ende aus, **wie oft** du verdoppelt hast. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: Die Schleife läuft, solange die Zahl noch zu klein ist. Zähle nebenbei mit, wie oft sie gelaufen ist. -tipp: Du brauchst eine zweite Variable \`int schritte = 0\`. Die Bedingung lautet \`zahl < 100\`. Im Inhalt verdoppelst du die Zahl mit \`zahl = zahl * 2\` und erhöhst \`schritte\` um 1. Ist die Zahl schon 100, läuft die Schleife gar nicht. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Schleifen im Schreibtischtest - -Bei einer Schleife wird der **Schreibtischtest** besonders wertvoll. Du siehst genau, wie oft die Schleife läuft und mit welchem Wert sie aufhört. Jede Prüfung der Bedingung bekommt eine eigene Zeile. - -::: diagramm l16-schreibtisch.visustruct -Eine While-Schleife zählt die Zahlen 1, 2 und 3 zusammen. -::: - -| Schritt | summe | i | Ausgabe | -|---|---|---|---| -| \`int summe = 0\` | 0 | – | | -| \`int i = 1\` | 0 | 1 | | -| \`i <= 3\` → wahr | 0 | 1 | | -| \`summe = summe + i\` | 1 | 1 | | -| \`i = i + 1\` | 1 | 2 | | -| \`i <= 3\` → wahr | 1 | 2 | | -| \`summe = summe + i\` | 3 | 2 | | -| \`i = i + 1\` | 3 | 3 | | -| \`i <= 3\` → wahr | 3 | 3 | | -| \`summe = summe + i\` | 6 | 3 | | -| \`i = i + 1\` | 6 | 4 | | -| \`i <= 3\` → falsch | 6 | 4 | | -| \`output: summe\` | 6 | 4 | 6 | - -Häufige Fehler in Schleifen sind: - -- Die Schleife läuft **einmal zu oft oder zu wenig**, z. B. \`<\` statt \`<=\`. -- Eine Variable wird in der Schleife **nicht verändert** oder in die falsche Richtung. Dann endet die Schleife nie. -- Eine Variable bekommt ihren Startwert **in** der Schleife statt davor und wird bei jedem Durchlauf zurückgesetzt. - -## Übung: Countdown reparieren - -::: übung l16-countdown -titel: Countdown reparieren -auftrag: Der Countdown soll vom Startwert herunterzählen und am Ende **Los!** ausgeben. Bei 3 also 3, 2, 1, Los! - Im Startdiagramm stecken zwei Fehler. Finde und behebe beide. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Los! -test: 1 -> 1 | Los! -test: 0 -> Los! -tipp: Es gibt zwei Fehler. Einer betrifft das Zählen, der andere die Ausgabe am Ende. -tipp: Starte die Simulation mit Startwert 3 und beobachte i unter Variablen. Wird i kleiner? Vergleiche außerdem die letzte Ausgabe genau mit dem Auftrag. -lösung: l16-countdown-loesung.visustruct -::: - -## Jetzt in Java - -Der Countdown vom Anfang dieser Lektion sieht in Java so aus: - -\`\`\`java editor -int zahl = 3; -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -\`\`\` - -- Nach \`while\` steht die Bedingung in runden Klammern, wie bei \`if\`. -- Alles, was wiederholt wird, steht in geschweiften Klammern. Das ist der Rumpf der Schleife. -- Der Rumpf ist eingerückt. So erkennst du wie im Struktogramm, was zur Schleife gehört. - -::: übung l11-java-gerade -titel: Gerade Zahlen in Java -code: java -auftrag: Schreibe ein Java-Programm, das eine ganze Zahl \`n\` einliest und mit einer While-Schleife alle geraden Zahlen von 2 bis höchstens \`n\` ausgibt. Bei 7 also 2, 4 und 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: Du brauchst eine Zählvariable, die bei 2 beginnt. Die Schleife läuft, solange sie nicht größer als \`n\` ist. -tipp: \`int zahl = 2;\` vor der Schleife, dann \`while (zahl <= n) { … }\`. Im Rumpf gibst du \`zahl\` aus und erhöhst sie mit \`zahl = zahl + 2;\`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Lückentext: while - -::: lückentext -titel: Summe von 1 bis 10 -auftrag: Das Programm soll die Zahlen von 1 bis 10 zusammenzählen. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -int summe = 0; -int zahl = 1; -[[while]] (zahl [[<=]] 10) [[{]] - summe = summe + [[zahl]]; - [[zahl]] = zahl + 1; -} -System.out.println("Summe: " + summe); -\`\`\` -erklärung: Die Schleife läuft, solange \`zahl\` höchstens 10 ist. In jedem Durchlauf kommt \`zahl\` zur Summe dazu, danach wird \`zahl\` um 1 größer. Ohne diese Zeile wäre es eine Endlosschleife. Ausgabe: **Summe: 55** -::: - -## Fehler finden: Sparen - -::: übung l11-java-fehler -titel: Sparplan reparieren -code: java -auftrag: Ludwig spart jede Woche 5 Euro. Das Programm fragt nach dem Sparziel und soll ausgeben, nach wie vielen Wochen er es erreicht hat. Bei 50 Euro sind das 10 Wochen, das Programm sagt aber **Wochen: 11**. - Gehe den Code mit dem Einzelschritt durch und beobachte \`geld\` und \`wochen\`. Repariere den Fehler und klicke auf Prüfen. -start: l11-java-fehler-start.visustruct -test: 50 -> Wochen: 10 -test: 12 -> Wochen: 3 -test: 5 -> Wochen: 1 -test: 1 -> Wochen: 1 -braucht: whileSchleife -tipp: Wie viel Geld hat Ludwig, wenn die Schleife zum letzten Mal läuft? Muss er dann wirklich noch eine Woche sparen? -tipp: Gespart wird nur, solange das Ziel noch **nicht erreicht** ist: \`while (geld < ziel)\`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l11-java-laeuft-nicht -titel: Countdown reparieren -code: java -auftrag: Der Countdown soll **3**, **2**, **1** und dann **Start!** ausgeben. Er läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Start! -braucht: whileSchleife -tipp: Der Fehler steckt in der Schleife. Welche Variable soll ausgegeben werden, und wie heißt sie wirklich? -tipp: Gib \`zahl\` mit kleinem z aus: \`System.out.println(zahl);\` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Wie oft? - -::: spiel frage -titel: Spiel: Wie oft? -intro: Ludwig hat While-Schleifen geschrieben. Mach im Kopf den Schreibtischtest. Sechs Runden. - -runde: - int zahl = 3; - while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; - } - System.out.println("Start!"); -frage: Was wird ausgegeben? -- [x] 3\\n2\\n1\\nStart! -- 3\\n2\\n1\\n0\\nStart! -- Start! -> Bei zahl gleich 0 ist \`zahl > 0\` falsch, die 0 wird also nicht mehr ausgegeben. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: Was wird ausgegeben? -- [x] nichts -- 10 -- 10 bis 4 -> Die Bedingung wird zuerst geprüft. 10 < 5 ist falsch, der Rumpf läuft kein einziges Mal. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: Was passiert? -- 1, 2 und 3 werden ausgegeben. -- [x] Die Schleife hört nie auf. -- Es wird nichts ausgegeben. -> i wird im Rumpf nie größer. Die Bedingung bleibt immer wahr: eine Endlosschleife. - -runde: - int zahl = 1; - while (zahl < 100) { - zahl = zahl * 2; - } - System.out.println(zahl); -frage: Was wird ausgegeben? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Erst bei 128 ist \`zahl < 100\` falsch. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: Was wird ausgegeben? -- 4 -- 5 -- [x] 6 -> i läuft 0, 2, 4, 6. Bei 6 ist \`i < 5\` falsch. Die 5 wird übersprungen. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: Wie oft läuft der Rumpf? -- 3-mal -- [x] 4-mal -- 10-mal -> Der Rumpf läuft bei i = 1, 4, 7 und 10. Danach ist i 13. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die While-Schleife ihre Bedingung vor jedem Durchlauf prüft. → Die Bedingung kommt zuerst -- Ich kann eine Zählvariable mit Startwert, Bedingung und Veränderung verwenden. → Eine Zählvariable -- Ich kann erkennen, wenn eine Schleife nie endet, und den Fehler beheben. → Vorsicht, Endlosschleife -- Ich kann eine Schleife im Schreibtischtest Durchlauf für Durchlauf verfolgen. → Schleifen im Schreibtischtest -- Ich kann eine While-Schleife in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet kopfgesteuert? -→ Die Bedingung kommt zuerst -- [x] Die Bedingung wird vor jedem Durchlauf geprüft. -- [ ] Die Bedingung wird nach jedem Durchlauf geprüft. -- [ ] Die Schleife läuft immer genau einmal. -> Der Kopf mit der Bedingung steht oben und wird zuerst ausgewertet. - -? \`int i = 5\`, danach eine While-Schleife mit der Bedingung \`i < 3\`. Wie oft läuft der Inhalt? -→ Die Bedingung kommt zuerst -- [x] Keinmal -- [ ] Einmal -- [ ] Dreimal -> Schon beim ersten Prüfen ist 5 < 3 falsch. - -? Warum endet eine Schleife mit der Bedingung \`zahl > 0\` nie, wenn im Inhalt nur \`output: zahl\` steht? -→ Vorsicht, Endlosschleife -- [ ] Weil output die Zahl vergrößert -- [x] Weil sich zahl nie ändert und die Bedingung wahr bleibt -- [ ] Weil die Bedingung falsch geschrieben ist -> Im Inhalt muss etwas passieren, das die Bedingung irgendwann falsch macht. - -? Wie nennt man Schleifen in Schulbüchern auch? -→ Die Bedingung kommt zuerst -- [x] Wiederholungsstrukturen -- [ ] Verzweigungen -- [ ] Unterprogramme -> Eine Schleife wiederholt Anweisungen. Darum heißt sie auch Wiederholungsstruktur. - -? Eine Schleife soll von 1 bis 5 zählen, läuft aber nur bis 4. Was ist der wahrscheinliche Fehler? -→ Eine Zählvariable -- [x] In der Bedingung steht i < 5 statt i <= 5. -- [ ] Die Schleife ist eine While-Schleife. -- [ ] Die Variable i heißt falsch. - -? Welche Zeile startet in Java eine While-Schleife richtig? -→ Jetzt in Java -- [ ] while zahl > 0 { -- [x] while (zahl > 0) { -- [ ] while (zahl > 0); -> Die Bedingung steht in runden Klammern, danach öffnet \`{\` den Rumpf. -::: -`,Xv=`--- -titel: Do-while-Schleife -bereich: Grundlagen -kurz: Erst ausführen, dann prüfen: Schleifen mit mindestens einem Durchlauf. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft -- erklären, warum der Inhalt mindestens einmal ausgeführt wird -- eine Eingabe so lange wiederholen lassen, bis sie gültig ist -- eine Do-while-Schleife in Java schreiben -::: - -## Erst machen, dann prüfen - -Die **Do-while-Schleife** ist **fußgesteuert**. Die Bedingung steht unten im Fuß des Kastens. Der Ablauf ist: - -1. Den Inhalt einmal ausführen. -2. Die Bedingung prüfen. -3. Ist sie wahr, wieder bei 1 anfangen. Ist sie falsch, geht es unter der Schleife weiter. - -::: grafik l11-vergleich.svg -Links prüft die While-Schleife zuerst, rechts führt die Do-while-Schleife zuerst aus. -::: - -Oben im Kasten steht \`do\`, unten im Fuß die Bedingung. Wenn du die Schleife anklickst, steht im Objektinspektor vor dem Textfeld das Wort \`while\`: Die Bedingung bedeutet „wiederhole, solange …“. - -> Die Bedingung sagt wie bei der While-Schleife, **wann wiederholt wird**. Solange sie wahr ist, läuft die Schleife weiter. - -::: angeberwissen -Python hat gar keine Do-while-Schleife. Wer dort „erst machen, dann prüfen“ will, schreibt \`while True:\` und verlässt die Schleife mit \`break\`. Java, C und JavaScript haben \`do … while\` dagegen schon seit Jahrzehnten. -::: - -## Mindestens ein Durchlauf - -Weil die Bedingung erst am Ende geprüft wird, läuft der Inhalt **immer mindestens einmal**. Das ist der wichtigste Unterschied zur While-Schleife. - -::: diagramm l11-einmal.visustruct -i ist 10 und damit nicht kleiner als 5. Trotzdem wird 10 einmal ausgegeben, denn geprüft wird erst danach. -::: - -Mit einer While-Schleife und derselben Bedingung \`i < 5\` würde hier gar nichts ausgegeben. - -## Typisch: Eingabe wiederholen - -Die Do-while-Schleife passt immer dann, wenn etwas zuerst passieren muss, bevor man es prüfen kann. Das beste Beispiel ist eine Eingabe: Erst wenn jemand etwas eingegeben hat, weißt du, ob es gültig ist. - -::: diagramm l11-eingabe-pruefen.visustruct -Die Frage kommt so lange wieder, bis eine Zahl von 1 bis 10 eingegeben wurde. Probiere in der Simulation zuerst 0 und dann 12 aus. -::: - -Die Bedingung beschreibt hier die **ungültige** Eingabe: \`zahl < 1 || zahl > 10\`. Solange die Eingabe ungültig ist, wird noch einmal gefragt. - -## Übung: Gültige Note - -::: übung l11-note -titel: Gültige Note -auftrag: Frage eine Note ab. Ist sie nicht zwischen 1 und 6, frage noch einmal. Ist sie gültig, gib sie aus, zum Beispiel **Deine Note: 3**. -test: 3 -> Deine Note: 3 -test: 0, 7, 2 -> Deine Note: 2 -test: 9, 6 -> Deine Note: 6 -test: -1, 1 -> Deine Note: 1 -braucht: doUntilSchleife -tipp: Die Note wird mindestens einmal abgefragt. Danach entscheidet die Bedingung, ob noch einmal gefragt wird. -tipp: Lege vor der Schleife \`int note = 0\` an. In die Schleife kommt die Eingabe \`input: note "Note von 1 bis 6"\`. Die Bedingung lautet \`note < 1 || note > 6\`. -lösung: l11-note-loesung.visustruct -::: - -## Übung: Summe bis 0 - -::: übung l11-summe -titel: Summe bis 0 -auftrag: Frage so lange Zahlen ab, bis eine 0 eingegeben wird. Gib danach die Summe aller eingegebenen Zahlen aus. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: Du brauchst eine Variable für die Summe, die vor der Schleife bei 0 beginnt. In jedem Durchlauf kommt die neue Zahl dazu. -tipp: Lege vor der Schleife \`int summe = 0\` und \`int zahl = 0\` an. In der Schleife fragst du \`zahl\` ab und rechnest \`summe = summe + zahl\`. Die 0 zu addieren schadet nicht. Die Schleife läuft weiter, solange \`zahl != 0\` ist. -tipp: Das Gerüst: \`int summe = 0\` und \`int zahl = 0\`, dann eine Do-while-Schleife mit der Bedingung \`zahl != 0\`. In ihr stehen die Eingabe von \`zahl\` und \`summe = summe + zahl\`. Unter der Schleife steht \`output: summe\`. -lösung: l11-summe-loesung.visustruct -::: - -## Jetzt in Java - -Die Schleife, die trotzdem einmal läuft, sieht in Java so aus: - -\`\`\`java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -\`\`\` - -- Der Rumpf steht zwischen \`do {\` und \`}\`, die Bedingung kommt erst danach. -- Hinter \`while (…)\` steht hier ein **Semikolon**. Das ist anders als bei der While-Schleife. - -::: übung l12-java-verdoppeln -titel: Mindestens einmal verdoppeln -code: java -auftrag: Schreibe ein Java-Programm, das eine Startzahl einliest. Mit einer Do-while-Schleife wird sie verdoppelt und ausgegeben, solange sie kleiner als 100 ist. Auch eine Zahl ab 100 wird mindestens einmal verdoppelt: Aus 200 wird 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: Im Rumpf verdoppelst du die Zahl und gibst sie aus. Die Bedingung steht am Ende. -tipp: \`do { zahl = zahl * 2; System.out.println(zahl); } while (zahl < 100);\` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Lückentext: do-while - -::: lückentext -titel: Verdreifachen -auftrag: Die Zahl wird verdreifacht und ausgegeben, solange sie kleiner als 100 ist. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -int zahl = 1; -[[do]] { - zahl = zahl * 3; - System.out.println(zahl); -} [[while]] (zahl [[<]] 100)[[;]] -\`\`\` -erklärung: Der Rumpf steht zwischen \`do {\` und \`}\`, die Bedingung erst danach. Hinter \`while (…)\` steht bei der Do-while-Schleife ein Semikolon. Ausgabe: 3, 9, 27, 81 und 243. -::: - -## Fehler finden: Halbieren - -::: übung l12-java-fehler -titel: Halbieren reparieren -code: java -auftrag: Das Programm liest eine Startzahl ein und teilt sie mit einer Do-while-Schleife immer wieder ganzzahlig durch 2. Jedes Ergebnis wird ausgegeben, bis **0** erreicht ist. Bei 10 also 5, 2, 1 und 0. Die 0 fehlt aber. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: Welchen Wert hat \`zahl\`, nachdem die 1 ausgegeben wurde? Läuft die Schleife dann noch einmal? -tipp: Die Schleife soll weiterlaufen, solange die Zahl größer als 0 ist: \`while (zahl > 0);\` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l12-java-laeuft-nicht -titel: Verdoppeln reparieren -code: java -auftrag: Das Programm soll ab 1 immer verdoppeln, solange die Zahl kleiner als 20 ist: **1**, **2**, **4**, **8**, **16**. Es gibt nur die 1 aus und bleibt dann stehen. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: Die erste Runde läuft noch. Der Fehler kommt erst, wenn die Bedingung unten zum ersten Mal geprüft wird. -tipp: In der Bedingung muss \`grenze\` mit kleinem g stehen: \`} while (zahl < grenze);\` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Mindestens einmal? - -::: spiel frage -titel: Spiel: Mindestens einmal? -intro: While oder do-while? Ludwig zeigt dir Schleifen. Sag vorher, was passiert. Sechs Runden. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: Was wird ausgegeben? -- nichts -- [x] 10 -- 10 bis 4 -> Erst machen, dann prüfen: Der Rumpf läuft einmal, danach ist 11 < 5 falsch. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: Was wird ausgegeben? -- [x] nichts -- 10 -- 11 -> Bei der While-Schleife kommt die Bedingung zuerst. 10 < 5 ist falsch, also läuft gar nichts. - -runde: - int zahl = 200; - do { - zahl = zahl * 2; - } while (zahl < 100); - System.out.println(zahl); -frage: Was wird ausgegeben? -- 200 -- [x] 400 -- 800 -> Einmal wird auf jeden Fall verdoppelt. Danach ist 400 < 100 falsch. - -runde: - int zahl = 30; - do { - zahl = zahl * 2; - System.out.println(zahl); - } while (zahl < 100); -frage: Was wird ausgegeben? -- 60 -- [x] 60\\n120 -- 60\\n120\\n240 -> Nach 60 ist 60 < 100 wahr, es geht weiter. Nach 120 ist Schluss. - -runde: -frage: Welche Zeile beendet eine Do-while-Schleife in Java richtig? -- [x] \`} while (i < 5);\` -- \`} while (i < 5)\` -- \`while (i < 5) {\` -> Hinter dem \`while (…)\` der Do-while-Schleife steht ein Semikolon. - -runde: -frage: Wofür nimmt man typischerweise eine Do-while-Schleife? -- [x] eine Eingabe wiederholen, bis sie gültig ist -- von 1 bis 10 zählen -- zwei Werte tauschen -> Gefragt wird mindestens einmal. Erst danach weiß man, ob die Eingabe gültig war. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft. → Erst machen, dann prüfen -- Ich kann erklären, warum der Inhalt mindestens einmal ausgeführt wird. → Mindestens ein Durchlauf -- Ich kann eine Eingabe so lange wiederholen lassen, bis sie gültig ist. → Typisch: Eingabe wiederholen -- Ich kann eine Do-while-Schleife in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet fußgesteuert? -→ Erst machen, dann prüfen -- [ ] Die Bedingung wird vor jedem Durchlauf geprüft. -- [x] Die Bedingung wird nach jedem Durchlauf geprüft. -- [ ] Die Schleife hat keine Bedingung. -> Die Bedingung steht im Fuß, also unten, und wird nach dem Inhalt ausgewertet. - -? Die Bedingung einer Do-while-Schleife ist von Anfang an falsch. Wie oft läuft der Inhalt? -→ Mindestens ein Durchlauf -- [ ] Keinmal -- [x] Genau einmal -- [ ] Endlos -> Der Inhalt läuft zuerst. Erst danach merkt die Schleife, dass sie aufhören soll. - -? Du willst eine Zahl von 1 bis 10 abfragen. Welche Bedingung gehört in den Fuß? -→ Typisch: Eingabe wiederholen -- [ ] \`zahl >= 1 && zahl <= 10\` -- [x] \`zahl < 1 || zahl > 10\` -- [ ] \`zahl == 10\` -> Wiederholt wird, solange die Eingabe ungültig ist, also kleiner als 1 oder größer als 10. - -? Wann ist eine Do-while-Schleife besser als eine While-Schleife? -→ Mindestens ein Durchlauf -- [x] Wenn der Inhalt auf jeden Fall einmal laufen muss, z. B. bei einer Eingabe -- [ ] Wenn die Schleife vielleicht gar nicht laufen soll -- [ ] Wenn man genau bis 10 zählen will - -? Was steht in Java am Ende einer Do-while-Schleife? -→ Jetzt in Java -- [ ] } while (bedingung) { -- [x] } while (bedingung); -- [ ] } until (bedingung); -> Nach der Bedingung kommt ein Semikolon. -::: -`,ew=`--- -titel: For-Schleife -bereich: Grundlagen -kurz: Mit Start, Bedingung und Schritt eine feste Anzahl von Durchläufen zählen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die drei Teile Start, Bedingung und Schritt der For-Schleife benennen -- erklären, in welcher Reihenfolge die drei Teile ausgeführt werden -- mit einer For-Schleife vorwärts, rückwärts und in größeren Schritten zählen -- eine For-Schleife in Java schreiben und ihre drei Felder erklären -::: - -## Zählen mit festem Plan - -In der While-Schleife hast du schon mitgezählt: Startwert vor der Schleife, Bedingung im Kopf, Veränderung im Inhalt. Die **For-Schleife** schreibt diese drei Teile zusammen in den Kopf. So siehst du auf einen Blick, wie gezählt wird. - -::: grafik l12-while-zu-for.svg animiert -Aus der While-Schleife wird eine For-Schleife: Start, Bedingung und Schritt wandern zusammen in den Kopf. -::: - -Wenn du eine For-Schleife anklickst, hat sie rechts drei Felder: - -- **Start:** \`int i = 1\` legt die Zählvariable an. -- **Bedingung:** \`i <= 5\` sagt, wie lange gezählt wird. -- **Schritt:** \`i++\` erhöht \`i\` nach jedem Durchlauf um 1. - -::: diagramm l12-eins-bis-fuenf.visustruct -Eine For-Schleife, die die Zahlen von 1 bis 5 ausgibt. -::: - -::: angeberwissen -Warum heißt die Zählvariable fast immer \`i\`? In der Mathematik steht \`i\` schon lange für einen Index. Und in FORTRAN, einer der ersten Programmiersprachen von 1957, waren alle Variablen, die mit I bis N anfingen, automatisch ganze Zahlen. Programmierer haben sich das bis heute nicht abgewöhnt. -::: - -## In welcher Reihenfolge? - -Die drei Teile kommen zu unterschiedlichen Zeiten dran: - -1. **Start** wird genau einmal ausgeführt, ganz am Anfang. -2. **Bedingung** wird vor jedem Durchlauf geprüft. Ist sie falsch, ist die Schleife fertig. -3. Nach dem Inhalt kommt der **Schritt**, dann wieder die Bedingung. - -::: grafik l12-for-ablauf.svg -Der Start kommt nur einmal dran. Danach wiederholen sich Bedingung, Inhalt und Schritt. -::: - -Die For-Schleife ist also kopfgesteuert wie die While-Schleife. Gehe das Beispiel in der Simulation mit **Schritt** durch und beobachte, wie sich \`i\` verändert. - -> Nimm die For-Schleife, wenn du schon vorher weißt, wie oft etwas passieren soll. Hängt das Ende von etwas ab, das erst während der Schleife passiert, zum Beispiel einer Eingabe, ist die While- oder Do-while-Schleife besser. - -## Beispiel: Summe von 1 bis n - -Eine For-Schleife eignet sich gut, um Zahlen aufzusummieren. Vor der Schleife steht \`int summe = 0\`. In jedem Durchlauf kommt die aktuelle Zahl \`i\` dazu. - -::: diagramm l12-summe.visustruct -Bei n = 4 rechnet die Schleife 0 + 1 + 2 + 3 + 4. Am Ende wird 10 ausgegeben. -::: - -## Andere Schritte - -Der Schritt muss nicht immer \`i++\` sein: - -- \`i--\` zählt rückwärts, zum Beispiel mit Start \`int i = 10\` und Bedingung \`i >= 1\`. -- \`i = i + 2\` springt in Zweierschritten, zum Beispiel 0, 2, 4, 6. - -Achte darauf, dass Start, Bedingung und Schritt zusammenpassen. Mit Start \`int i = 1\`, Bedingung \`i >= 1\` und Schritt \`i++\` wird die Bedingung nie falsch. - -::: übung l12-countdown -titel: Countdown -auftrag: Frage eine ganze Zahl \`n\` ab. Zähle mit einer For-Schleife von \`n\` rückwärts bis 1 und gib jede Zahl aus. Gib danach **Los!** aus. -test: 3 -> 3 | 2 | 1 | Los! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Los! -test: 1 -> 1 | Los! -braucht: forSchleife -tipp: Diesmal zählt die For-Schleife rückwärts. Überlege, wo sie beginnt, wann sie aufhört und wie sie zählt. -tipp: Start ist \`int i = n\`, die Bedingung \`i >= 1\` und der Schritt \`i--\`. Die Ausgabe Los! steht unter der Schleife, nicht darin. -lösung: l12-countdown-loesung.visustruct -::: - -## Übung: Einmaleins - -::: übung l12-einmaleins -titel: Einmaleins -auftrag: Frage eine Zahl ab und gib ihre Einmaleins-Reihe von 1 bis 10 aus. Jede Zeile sieht so aus: **3 x 1 = 3**, **3 x 2 = 6** und so weiter. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: Die Schleife zählt von 1 bis 10. In jedem Durchlauf gibst du eine Zeile der Reihe aus. -tipp: Rechne im Inhalt zuerst \`int produkt = zahl * i\`. Danach gibst du \`zahl + " x " + i + " = " + produkt\` aus. Denk an die Leerzeichen innerhalb der Anführungszeichen. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Übung: Summe reparieren - -::: übung l16-summe -titel: Summe reparieren -auftrag: Das Struktogramm soll alle Zahlen von 1 bis n zusammenzählen. Bei n = 4 soll also 1 + 2 + 3 + 4 = **10** herauskommen. Es gibt aber 6 aus. - Mache einen Schreibtischtest oder nutze **Schritt** und repariere den Fehler. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Gehe die Schleife mit n = 4 im Kopf oder mit **Schritt** durch. Welche Zahlen werden wirklich addiert? -tipp: Schau, mit welchem Wert von i die Schleife zuletzt läuft. Bei n = 4 muss auch i = 4 noch dazugezählt werden. -lösung: l16-summe-loesung.visustruct -::: - -## Jetzt in Java - -Die Summe von 1 bis n sieht in Java so aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Bis zu welcher Zahl?"); -int n = scanner.nextInt(); -int summe = 0; -for (int i = 1; i <= n; i++) { - summe = summe + i; -} -System.out.println(summe); -\`\`\` - -In den runden Klammern nach \`for\` stehen die drei Felder der For-Schleife, getrennt durch Semikolons: - -| Feld | In Java | Bedeutung | -|---|---|---| -| Startwert | \`int i = 1\` | \`i\` beginnt bei 1. | -| Bedingung | \`i <= n\` | Die Schleife läuft, solange das gilt. | -| Schritt | \`i++\` | Nach jedem Durchlauf wird \`i\` um 1 größer. | - -\`i++\` ist die Kurzform von \`i = i + 1\`. Rückwärts zählst du mit \`i--\`. - -::: übung l13-java-quadrate -titel: Quadratzahlen in Java -code: java -auftrag: Schreibe ein Java-Programm, das eine ganze Zahl \`n\` einliest und mit einer For-Schleife die Quadratzahlen von 1 bis \`n\` ausgibt, jede in einer eigenen Zeile. Bei 3 also 1, 4 und 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: Die Schleife zählt \`i\` von 1 bis \`n\`. Die Quadratzahl von \`i\` ist \`i * i\`. -tipp: \`for (int i = 1; i <= n; i++) { … }\` und im Rumpf \`System.out.println(i * i);\`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Lückentext: for - -::: lückentext -titel: Hoch- und runterzählen -auftrag: Die erste Schleife zählt die Zahlen von 1 bis 5 zusammen, die zweite zählt von 10 bis 0 rückwärts. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -int summe = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -\`\`\` -erklärung: In den runden Klammern stehen Startwert, Bedingung und Schritt, getrennt durch Semikolons. \`i++\` zählt hoch, \`i--\` zählt runter. Beim Rückwärtszählen bis 0 heißt die Bedingung \`i >= 0\`. -::: - -## Fehler finden: Einmaleins - -::: übung l13-java-fehler -titel: Einmaleins reparieren -code: java -auftrag: Das Programm fragt nach einer Reihe und soll sie vom 1-fachen bis zum 10-fachen ausgeben, bei 7 also 7, 14, … bis 70. Die 70 fehlt aber. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: Wie oft läuft die Schleife? Zähle nach, welche Werte \`i\` annimmt. -tipp: \`i\` soll auch den Wert 10 annehmen: \`i <= 10\` statt \`i < 10\`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l13-java-laeuft-nicht -titel: Treppe reparieren -code: java -auftrag: Ludwig klettert 5 Stufen hoch, jede ist 20 cm hoch. Das Programm soll **Höhe: 100 cm** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Höhe: 100 cm -braucht: forSchleife -tipp: Mit \`hoehe\` wird gerechnet, aber wo wird die Variable angelegt? Und mit welchem Wert startet Ludwig? -tipp: Lege \`hoehe\` vor der Schleife an: \`int hoehe = 0;\` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was das Programm tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sind die Teile einer Zeile umrandet, tippst du genau auf den falschen Teil, zum Beispiel nur auf \`i++\`. Acht Runden, je 25 Sekunden. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Soll 1, 2 und 3 ausgeben. -fehler: 2 -besser: System.out.println(i); -> Am Ende der Anweisung fehlt das Semikolon. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Soll 1 bis 5 ausgeben. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> Die drei Felder der For-Schleife werden mit Semikolons getrennt, nicht mit Kommas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Soll von 10 bis 1 rückwärts zählen. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> Mit \`i++\` wird \`i\` immer größer, die Bedingung \`i >= 1\` bleibt wahr: eine Endlosschleife. Rückwärts zählst du mit \`i--\`. - -runde: - int summe = 0; - for (int i = 1; i <= 4; i++) { - summe = i; - } - System.out.println(summe); -frage: Soll die Summe 1 + 2 + 3 + 4 = 10 ausgeben. -fehler: 3 -stelle: i -besser: summe = summe + i; -> \`summe = i\` überschreibt die Summe in jedem Durchlauf. Am Ende steht dort nur 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Määäh"); - } -frage: Soll genau fünfmal Määäh ausgeben. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> Von 0 bis einschließlich 5 sind es sechs Durchläufe. Wer bei 0 anfängt, nimmt \`<\`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Soll 1, 2 und 3 ausgeben. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> Java unterscheidet Groß- und Kleinbuchstaben: \`System\` schreibt man mit großem S. - -runde: - int zahl = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(zahl + " x " + i + " = " + zahl * zahl); - } -frage: Soll die Siebenerreihe ausgeben: 7 x 1 = 7, 7 x 2 = 14 und so weiter. -fehler: 3 -stelle: zahl * zahl -besser: System.out.println(zahl + " x " + i + " = " + zahl * i); -> \`zahl * zahl\` ist immer 49. Gerechnet werden muss \`zahl * i\`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Soll die geraden Zahlen 2, 4, 6, 8 und 10 ausgeben. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> \`i + 2\` rechnet nur, speichert aber nichts. Damit \`i\` wirklich wächst, braucht es \`i = i + 2\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die drei Teile Start, Bedingung und Schritt der For-Schleife benennen. → Zählen mit festem Plan -- Ich kann erklären, in welcher Reihenfolge die drei Teile ausgeführt werden. → In welcher Reihenfolge? -- Ich kann mit einer For-Schleife vorwärts, rückwärts und in größeren Schritten zählen. → Andere Schritte -- Ich kann eine For-Schleife in Java schreiben und ihre drei Felder erklären. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie oft wird der Start einer For-Schleife ausgeführt? -→ In welcher Reihenfolge? -- [x] Genau einmal -- [ ] Vor jedem Durchlauf -- [ ] Nach jedem Durchlauf -> Der Start legt die Zählvariable einmal am Anfang an. - -? Start \`int i = 0\`, Bedingung \`i < 3\`, Schritt \`i++\`. Welche Werte hat i im Inhalt? -→ Zählen mit festem Plan -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> Bei i = 3 ist die Bedingung falsch, deshalb läuft der Inhalt dann nicht mehr. - -? Start \`int i = 2\`, Bedingung \`i <= 10\`, Schritt \`i = i + 2\`. Wie oft läuft der Inhalt? -→ Andere Schritte -- [ ] 4-mal -- [x] 5-mal -- [ ] 10-mal -> i nimmt die Werte 2, 4, 6, 8 und 10 an. - -? Wann passt eine For-Schleife am besten? -→ Zählen mit festem Plan -- [x] Wenn vorher feststeht, wie oft wiederholt wird -- [ ] Wenn so lange gefragt wird, bis eine Eingabe gültig ist -- [ ] Wenn nur ein einziger Wert geprüft wird - -? Was bedeutet \`i++\` in einer For-Schleife? -→ Zählen mit festem Plan -- [ ] i wird verdoppelt. -- [x] i wird um 1 größer. -- [ ] i wird ausgegeben. -> \`i++\` ist die Kurzform von \`i = i + 1\`. -::: -`,tw=`--- -titel: Verschachteln -bereich: Grundlagen -kurz: Verzweigungen in Schleifen und Schleifen in Schleifen kombinieren. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Verzweigung in eine Schleife setzen -- eine Schleife in eine andere Schleife setzen -- erklären, dass die innere Schleife bei jedem Durchlauf der äußeren komplett durchläuft -- verschachtelte Schleifen und Verzweigungen in Java schreiben -::: - -## Bausteine ineinander - -Du kennst jetzt alle wichtigen Bausteine: Sequenz, Verzweigung, Fallauswahl und drei Arten von Schleifen. Richtig stark werden sie erst zusammen. - -Jeder Zweig und jeder Schleifeninhalt ist selbst wieder eine Sequenz. Dort darfst du jedes Element einsetzen, auch weitere Verzweigungen und Schleifen. Das nennt man **Verschachteln**. - -> Ziehe dazu ein Element einfach auf den Platzhalter ø im Inneren einer Schleife oder eines Zweigs. - -## Verzweigung in der Schleife - -Die Schleife geht alle Zahlen durch, die Verzweigung entscheidet bei jeder einzelnen Zahl, ob etwas passiert. So kannst du zum Beispiel zählen, wie viele gerade Zahlen es von 1 bis n gibt. - -::: diagramm l13-gerade-zaehlen.visustruct -Bei jedem Durchlauf prüft die Verzweigung mit i % 2 == 0, ob i gerade ist. Nur dann wird anzahl um 1 erhöht. Der false-Zweig bleibt leer. -::: - -Bei n = 7 sind das die Zahlen 2, 4 und 6, also wird 3 ausgegeben. - -## Übung: Gerade Eingaben zählen - -::: übung l13-gerade-eingaben -titel: Gerade Eingaben zählen -auftrag: Frage nacheinander genau **fünf** ganze Zahlen ab. Gib danach aus, wie viele davon gerade waren. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Fünfmal dasselbe tun heißt: Schleife. In jedem Durchlauf wird eine Zahl eingelesen und geprüft. -tipp: Eine For-Schleife von 1 bis 5. Im Inhalt steht zuerst die Eingabe \`input: int zahl "Zahl eingeben"\`, darunter eine Verzweigung mit \`zahl % 2 == 0\`. Im true-Zweig erhöhst du \`anzahl\` um 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## Schleife in der Schleife - -Steht eine Schleife in einer anderen, läuft die **innere Schleife bei jedem Durchlauf der äußeren komplett durch**. Damit kannst du Muster aus Zeilen und Spalten bauen. - -::: diagramm l13-dreieck.visustruct -Die äußere Schleife zählt die Zeilen. Die innere hängt so viele Sterne an den Text, wie die Zeilennummer angibt. Bei n = 3 entsteht ein Dreieck aus 1, 2 und 3 Sternen. -::: - -Mit \`String text = ""\` beginnt jede Zeile leer. \`text = text + "*"\` hängt einen Stern an. Erst wenn die Zeile fertig ist, wird sie ausgegeben. - -Erfahrene Programmierer nennen die Zählvariablen kurz \`i\` und \`j\`: außen \`i\`, innen \`j\`. Dann sieht es so aus, hier mit einer kleinen Einmaleins-Tabelle: - -::: grafik l13-verschachtelt-tabelle.svg animiert -Die äußere Schleife zählt mit \`i\` die Zeilen, die innere mit \`j\` die Zahlen in einer Zeile. Bei jeder neuen Zeile fängt \`j\` wieder bei 1 an. -::: - -## Übung: Rechteck - -::: übung l13-rechteck -titel: Rechteck -auftrag: Frage eine Breite und eine Höhe ab. Gib ein Rechteck aus \`#\` aus: so viele Zeilen wie die Höhe, und in jeder Zeile so viele \`#\` wie die Breite. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: Du brauchst zwei Schleifen ineinander: eine für die Zeilen und darin eine für die Zeichen einer Zeile. -tipp: Baue es wie das Dreieck. Der Unterschied: Die innere Schleife zählt immer bis \`breite\`, nicht bis zur Zeilennummer. Die äußere Schleife zählt bis \`hoehe\`. -tipp: Das Gerüst: Eingaben \`breite\` und \`hoehe\`. Äußere For-Schleife \`int zeile = 1\`, \`zeile <= hoehe\`, \`zeile++\`. Darin zuerst \`String text = ""\`, dann die innere For-Schleife \`int spalte = 1\`, \`spalte <= breite\`, \`spalte++\` mit \`text = text + "#"\`, und unter der inneren Schleife \`output: text\`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Den Überblick behalten - -Bei verschachtelten Diagrammen hilft dir die Darstellung: Was weiter innen steht, ist eingerückt und von seiner Schleife oder seinem Zweig umschlossen. - -- Lies von außen nach innen: Was wiederholt sich? Was wird bei jedem Durchlauf entschieden? -- Gib Zählvariablen sprechende Namen wie \`zeile\` und \`spalte\`. Zwei Schleifen dürfen nicht dieselbe Zählvariable verändern. -- Gehe ein Beispiel in der Simulation mit **Schritt** durch und beobachte die Variablen. - -::: angeberwissen -Genau dafür wurde das Struktogramm erfunden. Shneiderman fragte sich: Wenn man auf GOTO verzichten soll, warum dann nicht gleich auf die Pfeile im Flussdiagramm? Ergebnis: kein Pfeil-Spaghetti, und jeder Block bleibt brav in seinem Gatter. -::: - -## Jetzt in Java - -Das Zählen der geraden Zahlen sieht in Java so aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Bis zu welcher Zahl?"); -int n = scanner.nextInt(); -int anzahl = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - anzahl++; - } // Ende if -} // Ende for -System.out.println(anzahl); -\`\`\` - -Was im Struktogramm ineinander steckt, steht in Java in geschweiften Klammern ineinander. Jede Ebene ist vier Leerzeichen weiter eingerückt. Ist dein Code durcheinander, hilft der Knopf **Einrücken** oben im Bereich Code. - -Bei vielen schließenden Klammern \`}\` verliert man leicht den Überblick: Welche Klammer gehört zu welchem Block? Ein Kommentar dahinter sagt es dir, z. B. \`} // Ende if\` und \`} // Ende for\`. Das hilft dir auch, wenn du später weiterarbeitest. - -\`anzahl++\` ist die Kurzform von \`anzahl = anzahl + 1\`. - -::: übung l14-java-treppe -titel: Treppe aus Sternen -code: java -auftrag: Schreibe ein Java-Programm, das die Anzahl der Stufen einliest und eine Treppe aus Sternchen ausgibt. Bei 3 Stufen also \`*\`, \`**\` und \`***\`, jede Stufe in einer eigenen Zeile. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: Eine äußere Schleife zählt die Stufen. In jeder Stufe baut eine innere Schleife einen Text aus Sternchen zusammen. -tipp: In der äußeren Schleife: \`String zeile = "";\`, dann \`for (int j = 1; j <= i; j++) { zeile = zeile + "*"; }\` und danach \`System.out.println(zeile);\`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Lückentext: verschachteln - -::: lückentext -titel: Rechteck aus Sternen -auftrag: Das Programm gibt ein Rechteck aus 3 Zeilen mit je 4 Sternchen aus. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -for (int zeile = 1; zeile <= 3; zeile++) { - String text = [[""]]; - [[for]] (int spalte = 1; spalte [[<=]] 4; [[spalte++ | spalte += 1 | spalte = spalte + 1]]) { - text = text [[+]] "*"; - } - System.out.[[println]](text); -[[}]] -\`\`\` -erklärung: Die innere Schleife baut eine Zeile aus 4 Sternchen, die äußere wiederholt das dreimal. Jede Zeile beginnt mit einem leeren Text \`""\`. Die letzte Klammer schließt die äußere Schleife. -::: - -## Fehler finden: durch 3 teilbar - -::: übung l14-java-fehler -titel: Zählen reparieren -code: java -auftrag: Das Programm soll zählen, wie viele Zahlen von 1 bis \`n\` durch 3 teilbar sind, und **einmal** das Ergebnis ausgeben. Bei 10 also nur **Anzahl: 3**. Es gibt aber viel zu viele Zeilen aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l14-java-fehler-start.visustruct -test: 10 -> Anzahl: 3 -test: 3 -> Anzahl: 1 -test: 2 -> Anzahl: 0 -braucht: forSchleife, verzweigung -tipp: Achte auf die geschweiften Klammern: Steht die Ausgabe in der Schleife oder dahinter? -tipp: Verschiebe die Zeile \`System.out.println("Anzahl: " + anzahl);\` hinter die schließende Klammer der Schleife. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l14-java-laeuft-nicht -titel: Gerade Zahlen reparieren -code: java -auftrag: Das Programm soll die geraden Zahlen von 1 bis 10 ausgeben: **2**, **4**, **6**, **8**, **10**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: Der Fehler steckt ganz innen, in der Ausgabe. Wie heißt die Zählvariable der Schleife? -tipp: Die Zählvariable heißt \`i\` mit kleinem i: \`System.out.println(i);\` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Innen und außen - -::: spiel frage -titel: Spiel: Innen und außen -intro: Ludwig hat Bausteine ineinander gesteckt. Lies von außen nach innen und sag vorher, was passiert. Sechs Runden. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: Wie viele Sterne werden ausgegeben? -- 3 -- 5 -- [x] 6 -> Die innere Schleife läuft bei jedem der 3 Durchläufe der äußeren komplett durch: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Welche Zeile wird als vierte ausgegeben? -- 1 4 -- [x] 2 1 -- 2 2 -> Erst läuft j für i = 1 durch: 1 1, 1 2, 1 3. Danach beginnt j wieder bei 1, jetzt mit i = 2. - -runde: - int anzahl = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - anzahl++; - } - } - System.out.println(anzahl); -frage: Was wird ausgegeben? -- [x] 3 -- 10 -- 4 -> Die Verzweigung zählt nur 3, 6 und 9 mit. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("gerade"); - } else { - System.out.println(i); - } - } -frage: Was wird ausgegeben? -- 1\\n2\\n3\\n4 -- [x] 1\\ngerade\\n3\\ngerade -- gerade\\ngerade -> Bei jeder Zahl entscheidet die Verzweigung neu: ungerade Zahlen selbst, gerade als Wort. - -runde: - for (int i = 1; i <= 3; i++) { - String zeile = ""; - for (int j = 1; j <= i; j++) { - zeile = zeile + "*"; - } - System.out.println(zeile); - } -frage: Was wird ausgegeben? -- ***\\n***\\n*** -- [x] *\\n**\\n*** -- ***\\n**\\n* -> Die innere Schleife läuft bis i. Darum wird jede Zeile einen Stern länger. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: Was passiert? -- 1 bis 3 wird dreimal ausgegeben. -- 1 bis 3 wird einmal ausgegeben. -- [x] Java meldet einen Fehler. -> Zwei Schleifen ineinander brauchen zwei verschiedene Zählvariablen, zum Beispiel \`zeile\` und \`spalte\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Verzweigung in eine Schleife setzen. → Verzweigung in der Schleife -- Ich kann eine Schleife in eine andere Schleife setzen. → Schleife in der Schleife -- Ich kann erklären, dass die innere Schleife bei jedem Durchlauf der äußeren komplett durchläuft. → Schleife in der Schleife -- Ich kann verschachtelte Schleifen und Verzweigungen in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Die äußere Schleife läuft 3-mal, die innere 4-mal. Wie oft wird der Inhalt der inneren Schleife insgesamt ausgeführt? -→ Schleife in der Schleife -- [ ] 7-mal -- [x] 12-mal -- [ ] 4-mal -> Bei jedem der 3 äußeren Durchläufe läuft die innere Schleife 4-mal, also 3 · 4 = 12. - -? Woran erkennst du, ob eine Zahl gerade ist? -→ Verzweigung in der Schleife -- [ ] \`zahl / 2 == 0\` -- [x] \`zahl % 2 == 0\` -- [ ] \`zahl == 2\` -> % liefert den Rest beim Teilen. Bei geraden Zahlen ist der Rest beim Teilen durch 2 null. - -? In einer For-Schleife steht eine Verzweigung. Wie oft wird die Bedingung der Verzweigung geprüft? -→ Verzweigung in der Schleife -- [ ] Einmal -- [x] Einmal pro Durchlauf der Schleife -- [ ] Nur wenn sie wahr ist -> Die Verzweigung gehört zum Inhalt und kommt bei jedem Durchlauf dran. - -? Woran erkennst du in Java, was zu einer Schleife gehört? -→ Jetzt in Java -- [ ] An der Farbe -- [x] An den geschweiften Klammern und der Einrückung -- [ ] An den Semikolons -::: -`,nw=`--- -titel: Arrays -bereich: Grundlagen -kurz: Viele Werte unter einem Namen speichern, mit dem Index darauf zugreifen und sie mit einer Schleife durchlaufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Array mit Werten oder mit \`new int[5]\` anlegen -- erklären, dass der Index bei 0 beginnt und der letzte Index \`length - 1\` ist -- ein Array mit einer For-Schleife durchlaufen und füllen -- die Summe und das Maximum eines Arrays berechnen -- in Java ein Array anlegen und mit einer Schleife durchlaufen -::: - -## Viele Werte unter einem Namen - -Stell dir vor, du willst die Punkte von fünf Tests speichern. Mit fünf Variablen \`punkte1\` bis \`punkte5\` wird das schnell unübersichtlich. Ein **Array** ist eine Reihe von Fächern, die alle denselben Namen und denselben Datentyp haben. - -Du legst ein Array mit einer Anweisung an. Hinter den Datentyp schreibst du eckige Klammern \`[]\`: - -\`\`\` -int[] zahlen = {2, 4, 6, 8, 10} -String[] namen = {"Ada", "Ben", "Cem"} -\`\`\` - -Die Werte in den geschweiften Klammern stehen gleich in den Fächern. Du kannst auch ein leeres Array mit einer festen Anzahl von Fächern anlegen: - -\`\`\` -int[] punkte = new int[5] -String[] namen = new String[3] -\`\`\` - -Bei \`new int[5]\` stehen in allen fünf Fächern zuerst Nullen, bei \`new String[3]\` leere Texte. - -## Der Index beginnt bei 0 - -Jedes Fach hat eine Nummer, den **Index**. Gezählt wird ab **0**. Bei \`int[] zahlen = {2, 4, 6, 8, 10}\` gilt also: - -- \`zahlen[0]\` ist 2 -- \`zahlen[1]\` ist 4 -- \`zahlen[4]\` ist 10 - -::: grafik l14-array.svg -Fünf Fächer mit den Indizes 0 bis 4. Der Index steht über dem Fach, der Wert darin. -::: - -**Warum beginnt man bei 0?** In der Informatik ist der Index kein „wievieltes Fach“, sondern der **Abstand vom Anfang**. Das erste Fach liegt direkt am Anfang, also 0 Schritte entfernt. Das zweite liegt 1 Schritt entfernt, das dritte 2 Schritte. Du kennst das aus dem Aufzug: Das Erdgeschoss ist die **0**, der erste Stock liegt eine Etage darüber. - -Daraus folgt eine Regel, die du dir merken solltest: Bei 5 Fächern ist der **letzte Index 4**, also immer eins weniger als die Anzahl. - -::: angeberwissen -Dass ab 0 gezählt wird, ist ziemlich alt. Schon die Programmiersprache C von 1972 rechnet so: Der Computer kennt die Adresse des ersten Fachs und rechnet für \`zahlen[i]\` einfach „Anfang plus i Fächer weiter“. Bei i = 0 bleibt er am Anfang stehen, das spart eine Rechnung. Java, Python und JavaScript haben das übernommen. Ich zähle meine Hörner übrigens auch ab 0: Horn 0 links, Horn 1 rechts. -::: - -Mit dem Index liest du einen Wert oder schreibst einen neuen hinein: - -\`\`\` -output: zahlen[0] -zahlen[1] = 99 -\`\`\` - -> Ein Array mit 5 Fächern hat die Indizes 0 bis 4. \`zahlen[5]\` gibt es nicht. Die Simulation meldet dann einen ungültigen Ausdruck. - -## Die Länge eines Arrays - -Wie viele Fächer ein Array hat, verrät \`zahlen.length\`. Bei fünf Werten ist das 5. Der **letzte** Index ist deshalb immer \`zahlen.length - 1\`: - -\`\`\` -output: zahlen.length -output: zahlen[zahlen.length - 1] -\`\`\` - -Die zweite Zeile gibt den letzten Wert aus, egal wie lang das Array ist. VisuStruct versteht auch die Schreibweise \`zahlen.length()\`. - -::: übung l14-erster-letzter -titel: Erster und letzter Wert -auftrag: Lege das Array \`int[] werte = {4, 8, 15, 16, 23, 42}\` an. Gib danach in drei Zeilen aus: den ersten Wert, den letzten Wert und die Anzahl der Werte. - Für den letzten Wert sollst du die Länge benutzen und nicht einfach den Index 5 hinschreiben. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Gezählt wird ab 0. Das erste Fach hat also den Index 0. Und wie heißt der Index des letzten Fachs? -tipp: Der erste Wert ist \`werte[0]\`. Der letzte ist \`werte[werte.length - 1]\`. Die Anzahl ist \`werte.length\`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Ein Array mit der Schleife durchlaufen - -Seine Stärke zeigt ein Array zusammen mit einer **For-Schleife**. Die Zählvariable \`i\` läuft von 0 bis zum letzten Index und dient als Index: - -\`\`\` -int i = 0 -i < zahlen.length -i++ -\`\`\` - -Im ersten Durchlauf ist \`i\` gleich 0, im letzten gleich 4. Weil die Bedingung \`i < zahlen.length\` lautet, passt die Schleife zu jeder Länge. - -::: diagramm l14-array-durchlaufen.visustruct -Die For-Schleife gibt alle fünf Werte nacheinander aus. Schau in der Simulation unter Variablen zu, wie i von 0 bis 4 zählt. -::: - -So füllst du ein leeres Array auch mit Eingaben. Als Ziel der Eingabe schreibst du das Fach \`namen[i]\`: - -::: diagramm l14-namen-eingeben.visustruct -Drei Namen werden abgefragt und in die Fächer 0, 1 und 2 geschrieben. -::: - -::: übung l14-rueckwaerts -titel: Rückwärts ausgeben -auftrag: Lege ein Array für 4 ganze Zahlen an und lies die 4 Zahlen mit einer Schleife ein. Gib sie danach in **umgekehrter** Reihenfolge aus, jede in einer eigenen Zeile. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: Du brauchst zwei Schleifen: eine zum Einlesen von vorn nach hinten und eine zum Ausgeben von hinten nach vorn. -tipp: Mit \`int[] zahlen = new int[4]\` legst du das Array an. Die Eingabe lautet \`input: zahlen[i] "Zahl eingeben"\`. Für die zweite Schleife beginnst du bei \`int i = zahlen.length - 1\`, prüfst \`i >= 0\` und zählst mit \`i--\` herunter. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr** aus Wien war in den 1940er-Jahren ein berühmter Hollywood-Filmstar. In ihrer Freizeit erfand sie gern. 1942 bekam sie zusammen mit dem Komponisten George Antheil ein Patent auf eine Funkverbindung, die der Feind nicht stören sollte: Sender und Empfänger springen gemeinsam durch eine Liste von **88 Frequenzen**, so viele, wie ein Klavier Tasten hat. Die Liste stand auf einem Lochstreifen wie bei einem selbstspielenden Klavier. Beide gehen dieselbe Liste in derselben Reihenfolge durch, so wie deine Schleife mit \`i\` durch ein Array. Wer nur eine Frequenz abhört oder stört, bekommt fast nichts mit. - -Dieses **Frequenzsprungverfahren** (englisch *frequency hopping*) steckt heute zum Beispiel in Bluetooth. Ganz allein erfunden hat sie es nicht, es gab schon frühere Ideen. Ihr Patent gilt aber als wichtiger Vorläufer, und 1997 wurde sie dafür ausgezeichnet. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), Schauspielerin und Erfinderin, 1944 (Pressefoto: [unbekannt](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), gemeinfrei). -::: - -## Summe und Maximum - -Zwei Aufgaben kommen bei Arrays immer wieder vor: - -- **Summe:** Du beginnst mit \`summe = 0\` und zählst in der Schleife jeden Wert dazu. -- **Maximum:** Du merkst dir zuerst den Wert im Fach 0 als bisher größten. In der Schleife prüfst du jeden Wert. Ist er größer, wird er das neue Maximum. - -::: diagramm l14-summe-maximum.visustruct -Summe und Maximum in einem einzigen Durchlauf. Das Maximum beginnt mit punkte[0] und nicht mit 0, damit es auch bei negativen Zahlen stimmt. -::: - -::: übung l14-summe-max -titel: Summe und Maximum -auftrag: Lies 5 ganze Zahlen in ein Array ein. Gib danach **Summe: …** und in der nächsten Zeile **Maximum: …** aus. -test: 3, 9, 2, 9, 1 -> Summe: 24 | Maximum: 9 -test: 10, 20, 30, 40, 50 -> Summe: 150 | Maximum: 50 -test: -5, -2, -9, -1, -3 -> Summe: -20 | Maximum: -1 -braucht: forSchleife, verzweigung -tipp: Gehe das Array mit einer Schleife durch. Zähle jeden Wert zur Summe und merke dir den größten bisher gesehenen Wert. -tipp: Rechne Summe und Maximum in eigenen Variablen aus und gib sie erst am Ende aus, z. B. mit \`output: "Summe: " + summe\`. Beginnt dein Maximum bei 0, scheitert der dritte Test. -tipp: Das Gerüst: Array anlegen und mit einer Schleife einlesen. Dann \`int summe = 0\` und \`int max = zahlen[0]\`. Eine zweite Schleife rechnet \`summe = summe + zahlen[i]\`, und eine Verzweigung \`zahlen[i] > max\` setzt im true-Zweig \`max = zahlen[i]\`. Zum Schluss die beiden Ausgaben. -lösung: l14-summe-max-loesung.visustruct -::: - -## Jetzt in Java - -Das Durchlaufen eines Arrays sieht in Java so aus: - -\`\`\`java editor -int[] zahlen = {2, 4, 6, 8, 10}; -for (int i = 0; i < zahlen.length; i++) { - System.out.println(zahlen[i]); -} -\`\`\` - -- \`int[]\` heißt „Array aus ganzen Zahlen“. Die Werte stehen in geschweiften Klammern. -- Ein leeres Array mit 5 Fächern legst du mit \`new int[5]\` an. -- \`zahlen[i]\` ist das Fach mit dem Index \`i\`, \`zahlen.length\` die Länge. - -Gibst du das ganze Array aus, also \`System.out.println(zahlen);\`, zeigt Java nicht die Zahlen. Du siehst nur einen Text wie \`[I@1b6d3586\`. Das ist die **Referenz**, also die Stelle, an der das Array im Speicher liegt. Den Inhalt zeigt erst \`Arrays.toString(zahlen)\`, das ergibt \`[2, 4, 6, 8, 10]\`. Dafür schreibst du ganz oben \`import java.util.Arrays;\`. - -::: übung l15-java-noten -titel: Notensumme in Java -code: java -auftrag: Lege in Java das Array \`noten\` mit den Werten 2, 3, 1 und 4 an. Zähle alle Noten mit einer For-Schleife zusammen und gib **Summe: 10** aus. -test: - -> Summe: 10 -braucht: forSchleife -tipp: Lege vor der Schleife \`int summe = 0;\` an. Die Schleife läuft von 0 bis \`noten.length - 1\`. -tipp: \`int[] noten = {2, 3, 1, 4};\`, dann \`for (int i = 0; i < noten.length; i++) { summe = summe + noten[i]; }\` und zum Schluss \`System.out.println("Summe: " + summe);\`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Lückentext: Arrays - -::: lückentext -titel: Das Maximum suchen -auftrag: Das Programm sucht den größten Wert im Array. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -[[int]][] werte = {3, 7, 1, 9, 4}; -int max = werte[0]; -[[for]] (int i = 1; i < werte.[[length]]; i++) { - if (werte[i] [[>]] max) { - max = werte[i]; - } -} -System.out.println("Maximum: " + max); -\`\`\` -erklärung: \`int[]\` ist ein Array aus ganzen Zahlen, \`werte.length\` seine Länge. \`max\` beginnt mit dem ersten Wert, die Schleife vergleicht alle weiteren damit. Ausgabe: **Maximum: 9** -::: - -## Fehler finden: Temperaturen - -::: übung l15-java-fehler -titel: Höchste Temperatur reparieren -code: java -auftrag: Im Januar wurden die Temperaturen -5, -2, -8 und -3 Grad gemessen. Das Programm soll die höchste ausgeben, also **Höchste Temperatur: -2**. Es behauptet aber 0 Grad, obwohl es so warm nie war. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l15-java-fehler-start.visustruct -test: - -> Höchste Temperatur: -2 -braucht: forSchleife, verzweigung -tipp: Mit welchem Wert beginnt \`max\`? Ist irgendeine der Temperaturen größer als dieser Startwert? -tipp: \`max\` muss mit einem Wert aus dem Array beginnen: \`int max = temperaturen[0];\` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l15-java-laeuft-nicht -titel: Punkte zusammenzählen -code: java -auftrag: Im Array \`punkte\` stehen 4, 7, 2 und 5. Das Programm soll alle zusammenzählen und **Summe: 18** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Summe: 18 -braucht: forSchleife -tipp: Der Code-Bereich unterstreicht das falsche Wort rot. Wie schreibt man die Länge eines Arrays? -tipp: \`length\` schreibt man ganz klein: \`i < punkte.length\` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler** rund um Arrays, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Sind die Teile einer Zeile umrandet, tippst du genau auf den falschen Teil. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - for (int i = 0; i <= zahlen.length; i++) { - System.out.println(zahlen[i]); - } -frage: Soll alle fünf Werte ausgeben. -fehler: 2 -stelle: i <= zahlen.length -besser: for (int i = 0; i < zahlen.length; i++) { -> Mit \`<=\` läuft i bis 5. Das Fach \`zahlen[5]\` gibt es nicht. - -runde: - String[] namen = {"Ada", "Ben", "Cem"}; - System.out.println(namen[1]); -frage: Soll den ersten Namen ausgeben, also Ada. -fehler: 2 -stelle: namen[1] -besser: System.out.println(namen[0]); -> Gezählt wird ab 0. \`namen[1]\` ist schon Ben. - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - System.out.println(zahlen[zahlen.length]); -frage: Soll den letzten Wert ausgeben, also 10. -fehler: 2 -stelle: zahlen[zahlen.length] -besser: System.out.println(zahlen[zahlen.length - 1]); -> Der letzte Index ist immer eins weniger als die Länge. - -runde: - int[] punkte = {3, 9, 2}; - int summe = 0; - for (int i = 0; i < punkte.length; i++) { - summe = punkte[i]; - } - System.out.println(summe); -frage: Soll die Summe 14 ausgeben. -fehler: 4 -stelle: punkte[i] -besser: summe = summe + punkte[i]; -> So überschreibt jeder Wert den vorigen, am Ende steht nur 2 in summe. - -runde: - int[] punkte = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < punkte.length; i++) { - if (punkte[i] > max) { - max = punkte[i]; - } - } - System.out.println(max); -frage: Soll das Maximum -2 ausgeben. -fehler: 2 -stelle: 0 -besser: int max = punkte[0]; -> Keine der Zahlen ist größer als 0, also bliebe max bei 0. Das Maximum beginnt deshalb mit dem ersten Fach. - -runde: - int[] zahlen = {2, 4, 6, 8, 10}; - for (int i = zahlen.length; i >= 0; i--) { - System.out.println(zahlen[i]); - } -frage: Soll die Werte rückwärts ausgeben, von 10 bis 2. -fehler: 2 -stelle: int i = zahlen.length -besser: for (int i = zahlen.length - 1; i >= 0; i--) { -> Rückwärts beginnt man beim letzten Index, und der ist \`zahlen.length - 1\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Array mit Werten oder mit \`new int[5]\` anlegen. → Viele Werte unter einem Namen -- Ich kann erklären, dass der Index bei 0 beginnt und der letzte Index \`length - 1\` ist. → Der Index beginnt bei 0 -- Ich kann ein Array mit einer For-Schleife durchlaufen und füllen. → Ein Array mit der Schleife durchlaufen -- Ich kann die Summe und das Maximum eines Arrays berechnen. → Summe und Maximum -- Ich kann in Java ein Array anlegen und mit einer Schleife durchlaufen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was gibt \`output: zahlen[2]\` bei \`int[] zahlen = {5, 10, 15, 20}\` aus? -→ Der Index beginnt bei 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Gezählt wird ab 0: zahlen[0] ist 5, zahlen[1] ist 10, zahlen[2] ist 15. - -? Ein Array hat 8 Fächer. Welcher Index ist der letzte? -→ Der Index beginnt bei 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> Der letzte Index ist immer length - 1. - -? Welche Bedingung passt in eine For-Schleife, die alle Werte von \`zahlen\` durchläuft und bei \`int i = 0\` beginnt? -→ Ein Array mit der Schleife durchlaufen -- [x] i < zahlen.length -- [ ] i <= zahlen.length -- [ ] i > zahlen.length -> Mit <= würde die Schleife auch zahlen[length] lesen, und dieses Fach gibt es nicht. - -? Womit sollte das Maximum beginnen, bevor die Schleife startet? -→ Summe und Maximum -- [ ] Immer mit 0 -- [x] Mit dem ersten Wert des Arrays -- [ ] Mit der Länge des Arrays -> Beginnt es mit 0, wäre bei lauter negativen Zahlen 0 das Ergebnis, obwohl 0 gar nicht im Array steht. - -? Welches deutsche Wort wird oft für Array benutzt? -→ Viele Werte unter einem Namen -- [ ] Zweig -- [x] Feld -- [ ] Fall -> Ein Array heißt auf Deutsch oft Feld. Gemeint ist dasselbe: viele Fächer unter einem Namen. - -? Welche Zeile legt in Java ein Array mit drei Zahlen an? -→ Jetzt in Java -- [x] int[] zahlen = {1, 2, 3}; -- [ ] int zahlen = [1, 2, 3]; -- [ ] array zahlen = (1, 2, 3); -::: -`,iw=`--- -titel: Zweidimensionale Arrays -bereich: Grundlagen -kurz: Werte in Zeilen und Spalten speichern, wie die Sitze in einem Kinosaal, und mit zwei verschachtelten Schleifen durchlaufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein 2D-Array anlegen und erklären, was die beiden Zahlen bedeuten -- ein Fach mit Reihe und Platz lesen und schreiben -- \`sitze.length\` und \`sitze[0].length\` unterscheiden -- erklären, in welcher Reihenfolge zwei verschachtelte Schleifen die Fächer besuchen -- in Java ein zweidimensionales Array anlegen und durchlaufen -::: - -## Ein Kinosaal als Tabelle - -Ein Kinosaal hat mehrere **Reihen**, und jede Reihe hat mehrere **Plätze**. Für jeden Sitz wollen wir uns merken, ob die Karte schon verkauft ist. Das ist eine Ja-oder-nein-Frage, also passt der Datentyp \`boolean\`: \`true\` heißt verkauft, \`false\` heißt frei. - -Ein normales Array ist eine einzige Reihe von Fächern. Für den Saal brauchen wir eine **Tabelle** mit Zeilen und Spalten. Das ist ein **zweidimensionales Array**, kurz 2D-Array. Du schreibst dafür zwei Paar eckige Klammern: - -\`\`\` -boolean[][] sitze = new boolean[3][5] -\`\`\` - -Das ergibt 3 Reihen mit je 5 Plätzen, also 15 Fächer. Am Anfang steht in allen Fächern \`false\`: Alle Plätze sind frei. - -::: grafik kino-saal.svg -Der erste Index ist die Reihe, der zweite der Platz. Beide werden ab 0 gezählt. -::: - -## Ein Fach ansprechen - -Für ein Fach brauchst du jetzt **zwei** Indizes: zuerst die Reihe, dann den Platz. - -\`\`\` -sitze[1][2] = true -\`\`\` - -Damit ist in Reihe 1 der Platz 2 verkauft. Gelesen wird genauso: \`sitze[1][2]\` ist jetzt \`true\`. - -Auch die Größe kannst du abfragen: - -- \`sitze.length\` ist die Anzahl der **Reihen**, hier 3. -- \`sitze[0].length\` ist die Anzahl der **Plätze** in Reihe 0, hier 5. - -::: diagramm kino-verkaufen.visustruct -Drei Karten werden verkauft. Danach gibt das Diagramm die Größe des Saals aus und ob Reihe 1, Platz 2 verkauft ist. Schau in der Simulation unter Variablen zu: sitze wird Zeile für Zeile angezeigt. -::: - -> Wie beim normalen Array gibt es kein Fach \`sitze[3][0]\`, denn die Reihen heißen 0, 1 und 2. Die Simulation meldet dann einen ungültigen Ausdruck. - -## Zwei Schleifen ineinander - -Um **alle** Plätze anzusehen, brauchst du zwei For-Schleifen ineinander: - -- Die **äußere Schleife** zählt die Reihe: \`reihe\` läuft von 0 bis 2. -- Die **innere Schleife** zählt den Platz: \`platz\` läuft von 0 bis 4. - -::: grafik kino-schleifen.svg -Blau ist die äußere Schleife, Orange die innere. Die Zahlen zeigen, in welcher Reihenfolge die Plätze drankommen. -::: - -Für jede Reihe läuft die innere Schleife einmal **komplett** durch. Erst wenn \`platz\` alle fünf Plätze besucht hat, zählt die äußere Schleife \`reihe\` um eins weiter. Danach beginnt \`platz\` wieder bei 0. So kommen alle 3 · 5 = 15 Plätze genau einmal dran. - -Die Bedingungen lauten \`reihe < sitze.length\` und \`platz < sitze[reihe].length\`. So passen die Schleifen zu jeder Saalgröße. - -::: diagramm kino-saalplan.visustruct -Der Saalplan: Für jede Reihe wird eine Zeile aus X (verkauft) und O (frei) zusammengesetzt und ausgegeben. Gehe es mit Schritt durch und beobachte, wie platz immer wieder von vorn beginnt, während reihe nur langsam weiterzählt. -::: - -::: angeberwissen -Jedes Handyfoto ist ein 2D-Array! Ein Foto mit 4000 · 3000 Pixeln hat 3000 Zeilen mit je 4000 Fächern, also 12 Millionen Pixel. In jedem Fach steht die Farbe eines Punkts. Wenn du ein Foto heller machst, läuft das Handy mit zwei verschachtelten Schleifen über alle Pixel. Mein Profilbild hat übrigens nur ein Fach: braun. -::: - -## Übung: Freie Plätze zählen - -::: übung kino-frei -titel: Freie Plätze zählen -auftrag: Im Startdiagramm sind schon drei Karten verkauft. Zähle mit zwei verschachtelten Schleifen, wie viele Plätze noch **frei** sind, und gib z. B. \`Frei: 12\` aus. -start: kino-frei-start.visustruct -test: - -> Frei: 12 -braucht: forSchleife, verzweigung -tipp: Du gehst wie beim Saalplan mit zwei Schleifen über alle Plätze. Statt etwas auszugeben, zählst du mit. -tipp: Lege vor den Schleifen \`int frei = 0\` an. Ein Platz ist frei, wenn \`sitze[reihe][platz]\` falsch ist. Die Bedingung dafür lautet \`!sitze[reihe][platz]\`. -lösung: kino-frei-loesung.visustruct -::: - -## Übung: Karte verkaufen - -::: übung kino-karte -titel: Karte verkaufen -auftrag: Das Startdiagramm fragt nach Reihe und Platz. Ist der Platz schon verkauft, gib \`Schon verkauft\` aus. Sonst markiere ihn als verkauft und gib z. B. \`Verkauft: Reihe 0, Platz 4\` aus. -start: kino-karte-start.visustruct -test: 1, 2 -> Schon verkauft -test: 0, 4 -> Verkauft: Reihe 0, Platz 4 -test: 2, 0 -> Verkauft: Reihe 2, Platz 0 -braucht: verzweigung -tipp: Zuerst wird geprüft, ob der Platz verkauft ist. Je nach Antwort passiert etwas anderes. -tipp: Hier brauchst du keine Schleife. Eine Verzweigung mit der Bedingung \`sitze[reihe][platz]\` genügt. Im false-Zweig setzt du das Fach auf \`true\`. -lösung: kino-karte-loesung.visustruct -::: - -## Übung: Einmaleins-Tabelle - -::: übung kino-einmaleins -titel: Einmaleins-Tabelle -auftrag: Lege \`int[][] tabelle = new int[10][10]\` an und fülle sie mit zwei Schleifen mit dem kleinen Einmaleins. Frage danach zwei Zahlen von 1 bis 10 ab und lies das Ergebnis aus der Tabelle, z. B. \`3 · 4 = 12\`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: Erst füllen zwei verschachtelte Schleifen die Tabelle, danach liest du nur ein einziges Fach aus. -tipp: Die Indizes laufen von 0 bis 9, die Zahlen aber von 1 bis 10. Darum steht im Fach \`tabelle[i][j]\` der Wert \`(i + 1) * (j + 1)\`, und für 3 · 4 liest du \`tabelle[a - 1][b - 1]\`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Jetzt in Java - -Der Kinosaal sieht in Java so aus: - -\`\`\`java editor -boolean[][] sitze = new boolean[3][5]; -sitze[0][2] = true; -sitze[1][1] = true; -sitze[1][2] = true; -System.out.println("Reihen: " + sitze.length); -System.out.println("Plätze pro Reihe: " + sitze[0].length); -System.out.println("Reihe 1, Platz 2 verkauft? " + sitze[1][2]); -\`\`\` - -- Zwei Paar eckige Klammern \`[][]\` stehen für eine Tabelle: erst die Reihe, dann der Platz. -- \`sitze.length\` ist die Zahl der Reihen, \`sitze[0].length\` die Zahl der Plätze in einer Reihe. -- Mit festen Werten schreibst du jede Reihe in eigene geschweifte Klammern: \`int[][] t = {{1, 2}, {3, 4}};\` - -::: übung l16-java-punkte -titel: Punkte zusammenzählen -code: java -auftrag: Drei Spieler haben je zwei Runden gespielt. Lege in Java die Tabelle \`punkte\` mit den Reihen {3, 5}, {4, 1} und {2, 2} an. Zähle mit zwei verschachtelten For-Schleifen alle Punkte zusammen und gib **Summe: 17** aus. -test: - -> Summe: 17 -braucht: forSchleife -tipp: Die äußere Schleife geht durch die Reihen, die innere durch die Plätze einer Reihe. -tipp: \`int[][] punkte = {{3, 5}, {4, 1}, {2, 2}};\` und in der inneren Schleife \`summe = summe + punkte[i][j];\`. Die innere Schleife läuft bis \`punkte[i].length\`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Lückentext: Tabellen - -::: lückentext -titel: Alle Felder zusammenzählen -auftrag: Das Programm zählt alle Zahlen in der Tabelle zusammen. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -[[int]][][] feld = {{1, 2, 3}, {4, 5, 6}}; -int summe = 0; -for (int r = 0; r < feld.[[length]]; r++) { - for (int s = 0; s < feld[r].[[length]]; s++) { - summe = summe [[+]] feld[r][s]; - } -} -System.out.println("Summe: " + summe); -\`\`\` -erklärung: \`int[][]\` ist eine Tabelle. \`feld.length\` ist die Zahl der Reihen (2), \`feld[r].length\` die Zahl der Felder in Reihe \`r\` (3). Ausgabe: **Summe: 21** -::: - -## Fehler finden: Punkte - -::: übung l16-java-fehler -titel: Spieler 2 reparieren -code: java -auftrag: In der Tabelle \`punkte\` steht jede Reihe für einen Spieler. Das Programm soll die Punkte von Spieler 2 (Reihe 1) zusammenzählen, also **Spieler 2: 11**. Stattdessen meldet es einen Fehler. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l16-java-fehler-start.visustruct -test: - -> Spieler 2: 11 -braucht: forSchleife -tipp: Bei \`punkte[a][b]\` ist \`a\` die Reihe und \`b\` der Platz in der Reihe. Welche Zahl steht für die Reihe von Spieler 2? -tipp: Die Indizes sind vertauscht: \`punkte[1][j]\` statt \`punkte[j][1]\`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l16-java-laeuft-nicht -titel: Spielstand ablesen -code: java -auftrag: In der Tabelle \`punkte\` steht jede Reihe für einen Spieler, jede Spalte für eine Runde. Das Programm soll die Punkte von Spieler 2 in Runde 1 ausgeben, also **Spieler 2, Runde 1: 4**. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Spieler 2, Runde 1: 4 -braucht: anweisung -tipp: Die Fächer \`[1][0]\` stimmen. Schau dir den Namen der Tabelle an. -tipp: Die Tabelle heißt \`punkte\` mit kleinem p: \`punkte[1][0]\` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Welcher Platz? - -::: spiel frage -titel: Spiel: Welcher Platz? -intro: Ludwig verkauft Kinokarten. Erst die Reihe, dann der Platz, beide ab 0. Sieben Runden. - -runde: - boolean[][] sitze = new boolean[3][5]; - System.out.println(sitze.length); -frage: Was wird ausgegeben? -- [x] 3 -- 5 -- 15 -> \`sitze.length\` ist die Anzahl der Reihen. - -runde: - boolean[][] sitze = new boolean[3][5]; - System.out.println(sitze[0].length); -frage: Was wird ausgegeben? -- 3 -- [x] 5 -- 15 -> \`sitze[0]\` ist die Reihe 0. Ihre Länge ist die Anzahl der Plätze in dieser Reihe. - -runde: - int[][] tabelle = new int[4][6]; -frage: Wie viele Fächer hat die Tabelle? -- 10 -- [x] 24 -- 35 -> 4 Reihen mit je 6 Plätzen: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: Was wird ausgegeben? -- 2 -- [x] 3 -- 4 -> Reihe 1 ist \`{3, 4}\`, darin Platz 0 die 3. - -runde: - boolean[][] sitze = new boolean[3][5]; - sitze[3][0] = true; -frage: Was passiert? -- Der erste Platz in Reihe 3 ist verkauft. -- Der erste Platz in der letzten Reihe ist verkauft. -- [x] Es gibt einen Fehler, denn die Reihen heißen 0, 1 und 2. -> Wie beim normalen Array ist der letzte Index eins weniger als die Länge. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int summe = 0; - for (int reihe = 0; reihe < t.length; reihe++) { - for (int platz = 0; platz < t[reihe].length; platz++) { - summe = summe + t[reihe][platz]; - } - } - System.out.println(summe); -frage: Was wird ausgegeben? -- 6 -- 15 -- [x] 21 -> Die zwei Schleifen besuchen alle sechs Fächer: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Zwei Schleifen gehen den Saal \`new boolean[3][5]\` durch. Welches Fach kommt direkt nach \`sitze[0][4]\`? -- \`sitze[0][5]\` -- [x] \`sitze[1][0]\` -- \`sitze[1][4]\` -> Reihe 0 ist fertig. Die äußere Schleife zählt weiter, und der Platz beginnt wieder bei 0. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein 2D-Array anlegen und erklären, was die beiden Zahlen bedeuten. → Ein Kinosaal als Tabelle -- Ich kann ein Fach mit Reihe und Platz lesen und schreiben. → Ein Fach ansprechen -- Ich kann \`sitze.length\` und \`sitze[0].length\` unterscheiden. → Ein Fach ansprechen -- Ich kann erklären, in welcher Reihenfolge zwei verschachtelte Schleifen die Fächer besuchen. → Zwei Schleifen ineinander -- Ich kann in Java ein zweidimensionales Array anlegen und durchlaufen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Wie viele Fächer hat \`boolean[][] sitze = new boolean[4][6]\`? -→ Ein Kinosaal als Tabelle -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 Reihen mit je 6 Plätzen: 4 · 6 = 24. - -? Was steht nach \`new boolean[3][5]\` in allen Fächern? -→ Ein Kinosaal als Tabelle -- [ ] true -- [x] false -- [ ] Nichts, die Fächer sind leer -> Ein neues boolean-Array ist mit false gefüllt. Im Kino heißt das: alle Plätze frei. - -? Zwei verschachtelte Schleifen laufen über 3 Reihen mit 5 Plätzen. Wie oft wird der Inhalt der **inneren** Schleife ausgeführt? -→ Zwei Schleifen ineinander -- [ ] 3-mal -- [ ] 8-mal -- [x] 15-mal -> Für jede der 3 Reihen läuft die innere Schleife 5-mal: 3 · 5 = 15. - -? Ordne zu, was der Ausdruck im Kinosaal bedeutet. -→ Ein Fach ansprechen -= sitze.length -> Anzahl der Reihen -= sitze[0].length -> Anzahl der Plätze in Reihe 0 -= sitze[2][4] -> Reihe 2, Platz 4 -= sitze[1][0] = true -> In Reihe 1 ist Platz 0 verkauft - -? Was gibt \`sitze.length\` bei \`new boolean[3][5]\` an? -→ Ein Fach ansprechen -- [x] 3, die Zahl der Reihen -- [ ] 5, die Zahl der Plätze -- [ ] 15, alle Plätze zusammen -::: -`,aw=`--- -titel: Zeichenketten -bereich: Grundlagen -kurz: Mit Texten arbeiten, also einzelne Zeichen lesen, Teile herausschneiden, suchen, vergleichen und zerlegen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass auch die Zeichen eines Strings ab 0 gezählt werden -- mit \`length()\` und \`charAt(i)\` arbeiten -- einen \`char\` von einem String unterscheiden und mit \`(int)\` und \`(char)\` den Zeichencode nutzen -- mit \`substring\` einen Teil aus einem Text schneiden -- \`indexOf\`, \`contains\`, \`toUpperCase\` und \`equals\` anwenden -- einen Text mit einer Schleife Zeichen für Zeichen durchgehen -- einen Text mit \`split\` in ein String-Array zerlegen -- in Java einen Text einlesen und mit \`equals\` vergleichen -- weitere Methoden wie \`lastIndexOf\`, \`replace\`, \`compareTo\` und \`Integer.parseInt\` anwenden -- einen Text aus einer Datei mit \`split("\\n")\` in Zeilen zerlegen -- eine Methode in der Java-API nachschlagen und ihre Beschreibung lesen -::: - -## Text ist eine Reihe von Zeichen - -Einen \`String\` kennst du schon: \`String name = "Ludwig"\`. In der Informatik heißt so ein Text auch **Zeichenkette**, weil die Zeichen wie Perlen an einer Kette hintereinander hängen. Jedes Zeichen hat einen Platz mit einem Index, genau wie bei einem Array. Auch hier wird **ab 0 gezählt**: Das erste Zeichen ist 0 Schritte vom Anfang entfernt. - -::: grafik zk-zeichen.svg -Die acht Zeichen von „Geißbock“ mit ihren Indizes 0 bis 7. -::: - -Ein String bringt **Methoden** mit. Du schreibst den Namen der Variablen, einen Punkt und die Methode: - -- \`wort.length()\` liefert die Anzahl der Zeichen, hier 8. -- \`wort.charAt(0)\` liefert das Zeichen mit dem Index 0, hier \`'G'\`. - -> Bei Strings schreibst du \`length()\` **mit** Klammern, bei Arrays \`length\` **ohne**. Das ist in Java leider so. - -Das letzte Zeichen hat wie beim Array den Index \`length() - 1\`. - -## Ein einzelnes Zeichen: char - -Für ein einzelnes Zeichen hat Java einen eigenen Datentyp: **char**, kurz für englisch *character* (Zeichen). Ein \`char\` steht in **einfachen** Anführungszeichen, ein String in doppelten: - -- \`'C'\` ist ein \`char\`: genau ein Zeichen. -- \`"C"\` ist ein String: ein Text, der nur ein Zeichen lang ist. -- Ein \`char\` hat immer genau ein Zeichen. Einen leeren \`char\` wie \`''\` gibt es nicht, einen leeren String \`""\` schon. - -\`charAt\` liefert dir so ein Zeichen aus einem Text: - -\`\`\` -char erster = wort.charAt(0) -char letzter = wort.charAt(wort.length() - 1) -\`\`\` - -Zwei \`char\` vergleichst du mit \`==\`: \`wort.charAt(0) == 'G'\` ist \`true\`. - -Hinter jedem Zeichen steckt eine Zahl, sein **Zeichencode**. Welche Zahl zu welchem Zeichen gehört, steht in der ASCII-Tabelle: \`'A'\` hat den Code 65, \`'B'\` 66, \`'C'\` 67 und so weiter bis \`'Z'\` mit 90. Mit \`(int)\` bekommst du den Code eines Zeichens, mit \`(char)\` wird aus einem Code wieder ein Zeichen: - -\`\`\` -int code = (int) 'C' -code = code + 1 -char naechstes = (char) code -\`\`\` - -Danach ist \`code\` 68 und \`naechstes\` ist \`'D'\`. So verschiebst du einen Buchstaben im Alphabet, und genau das brauchst du später bei der Caesar-Verschlüsselung. - -## Teile herausschneiden - -Mit \`substring\` schneidest du einen Teil aus dem Text: - -- \`wort.substring(0, 4)\` liefert die Zeichen von Index 0 **bis vor** Index 4, also \`"Geiß"\`. -- \`wort.substring(4)\` liefert alles ab Index 4 bis zum Ende, also \`"bock"\`. - -Das Ende gehört **nicht** mehr dazu. Praktisch ist: \`substring(0, 4)\` liefert genau 4 Zeichen. - -::: diagramm zk-zeichen.visustruct -Alle Methoden aus dieser Lektion an einem Beispiel. Gehe es in der Simulation durch und vergleiche jede Ausgabe mit der Grafik. -::: - -## Suchen und vergleichen - -Weitere Methoden, die du oft brauchst: - -| Methode | Bedeutung | Beispiel mit \`"Geißbock"\` | -|---|---|---| -| \`indexOf("bock")\` | Wo beginnt der Text? | 4 | -| \`indexOf("x")\` | nicht gefunden | -1 | -| \`contains("eiß")\` | Kommt der Text vor? | true | -| \`toUpperCase()\` | alles groß | GEISSBOCK | -| \`toLowerCase()\` | alles klein | geißbock | -| \`equals("Geißbock")\` | gleicher Text? | true | - -> Zwei Strings vergleichst du in Java mit \`equals\`, nicht mit \`==\`. Zwei einzelne Zeichen (\`char\`) vergleichst du dagegen mit \`==\`, z. B. \`wort.charAt(i) == 'e'\`. - -::: angeberwissen -Probier mal \`"Geißbock".toUpperCase()\` aus. Heraus kommt GEISSBOCK, und das Wort ist plötzlich ein Zeichen **länger**! Ein großes ß gab es auf Schreibmaschinen nie, darum wird es zu SS. Erst seit 2017 ist das große ẞ offiziell erlaubt, aber Java bleibt beim SS. In meinem Namen kommt zum Glück kein ß vor, nur im Wort Geißbock. -::: - -## Zeichen für Zeichen - -Mit einer For-Schleife gehst du einen Text Zeichen für Zeichen durch. Der Index \`i\` läuft von 0 bis \`wort.length() - 1\`, genau wie bei einem Array: - -::: diagramm zk-zaehlen.visustruct -Zählt, wie oft der Buchstabe e vorkommt. Probiere in der Simulation „Kaffeebecher“ aus und beobachte i und anzahl. -::: - -## Zerlegen mit split - -Oft stehen mehrere Angaben in einer einzigen Zeile, getrennt durch ein Zeichen, z. B. \`"Ludwig;Geißbock;7"\`. Mit \`split\` zerlegst du die Zeile an diesem Zeichen. Heraus kommt ein **String-Array**: - -\`\`\` -String[] teile = zeile.split(";") -\`\`\` - -Danach ist \`teile[0]\` gleich \`"Ludwig"\`, \`teile[1]\` gleich \`"Geißbock"\` und \`teile[2]\` gleich \`"7"\`. Mit \`teile.length\` erfährst du, wie viele Teile es sind. - -::: diagramm zk-split.visustruct -Die Zeile wird am Semikolon zerlegt und jeder Teil mit seinem Index ausgegeben. -::: - -> So sind auch CSV-Dateien aufgebaut, die du aus Tabellenkalkulationen kennst: eine Zeile pro Datensatz, die Werte durch Semikolon getrennt. - -## Übung: Initialen - -::: übung zk-initialen -titel: Initialen -auftrag: Frage nach Vorname und Nachname und gib die Initialen aus. Bei Ludwig Geißbock also \`L. G.\` -test: Ludwig, Geißbock -> L. G. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: Die Initiale ist der erste Buchstabe eines Namens. Welche Methode liefert ein Zeichen an einer bestimmten Stelle? -tipp: Den ersten Buchstaben liefert \`vorname.charAt(0)\`. Die Ausgabe ist dann \`vorname.charAt(0) + ". " + nachname.charAt(0) + "."\` -lösung: zk-initialen-loesung.visustruct -::: - -## Übung: Rückwärts - -::: übung zk-rueckwaerts -titel: Rückwärts -auftrag: Frage nach einem Wort und gib es rückwärts aus. Aus Ludwig wird \`giwduL\`. -test: Ludwig -> giwduL -test: Anna -> annA -test: a -> a -braucht: forSchleife -tipp: Gehe das Wort Zeichen für Zeichen durch, aber von hinten nach vorn, und baue dabei einen neuen Text zusammen. -tipp: Lege \`String rueckwaerts = ""\` an. Die Schleife beginnt beim letzten Index \`wort.length() - 1\` und zählt mit \`i--\` bis 0 herunter. In jedem Durchlauf hängst du \`wort.charAt(i)\` hinten an. -tipp: Das Gerüst: Eingabe \`wort\`, dann \`String rueckwaerts = ""\`. For-Schleife mit Start \`int i = wort.length() - 1\`, Bedingung \`i >= 0\` und Schritt \`i--\`. Darin \`rueckwaerts = rueckwaerts + wort.charAt(i)\`. Unter der Schleife \`output: rueckwaerts\`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Übung: Wörter zählen - -::: übung zk-woerter -titel: Wörter zählen -auftrag: Frage nach einem Satz und gib aus, wie viele Wörter er hat, z. B. \`Wörter: 4\` bei „Ludwig ist ein Geißbock“. Die Wörter sind durch genau ein Leerzeichen getrennt. -test: Ludwig ist ein Geißbock -> Wörter: 4 -test: Hallo -> Wörter: 1 -test: Struktogramme machen Spaß -> Wörter: 3 -braucht: anweisung -tipp: Zwischen den Wörtern steht immer ein Leerzeichen. Welche Methode zerlegt einen Text an einem Zeichen? -tipp: Zerlege den Satz mit \`satz.split(" ")\` in ein String-Array. Seine Länge ist die Anzahl der Wörter. -tipp: Das ganze Programm hat drei Kästen: die Eingabe \`satz\`, dann \`String[] woerter = satz.split(" ")\` und zum Schluss \`output: "Wörter: " + woerter.length\`. -lösung: zk-woerter-loesung.visustruct -::: - -## Jetzt in Java - -Die Methoden in dieser Lektion sind schon Java. Ein ganzes Programm mit einem Text sieht so aus: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Wie heißt du?"); -String name = scanner.next(); -System.out.println("Hallo " + name.toUpperCase() + "!"); -System.out.println("Dein Name hat " + name.length() + " Buchstaben."); -\`\`\` - -- \`scanner.next()\` liest ein Wort, also Text bis zum nächsten Leerzeichen. Für Zahlen nimmst du weiter \`scanner.nextInt()\`. -- Text steht in **doppelten** Anführungszeichen \`"Ludwig"\`, ein einzelnes Zeichen in **einfachen** \`'L'\`. -- Hinter \`length()\`, \`toUpperCase()\` und den anderen Methoden stehen immer runde Klammern, auch wenn nichts darin steht. -- Zwei Texte vergleichst du mit \`equals\`. Dabei zählt jeder Buchstabe: \`"Ludwig".equals("ludwig")\` ist false. - -::: übung l17-java-passwort -titel: Passwort prüfen -code: java -auftrag: Schreibe in Java ein Programm, das nach dem Passwort fragt. Ist es **Ludwig**, gibt es **Willkommen!** aus, sonst **Falsches Passwort**. - Achte auf Groß- und Kleinschreibung: \`ludwig\` ist falsch. -test: Ludwig -> Willkommen! -test: ludwig -> Falsches Passwort -test: Geißbock -> Falsches Passwort -braucht: verzweigung -tipp: Lies das Passwort mit \`scanner.next()\` in eine String-Variable. Dann vergleichst du in einer Verzweigung. -tipp: Die Bedingung heißt \`eingabe.equals("Ludwig")\`. Mit \`==\` klappt der Vergleich von Texten in Java nicht zuverlässig. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Lückentext: Texte - -::: lückentext -titel: Ein Wort untersuchen -auftrag: Ergänze die fehlenden Methoden und Wörter. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Ein Wort?"); -[[String]] wort = scanner.next(); -System.out.println("Länge: " + wort.[[length]]()); -System.out.println("Groß: " + wort.[[toUpperCase]]()); -System.out.println("Erster Buchstabe: " + wort.[[charAt]](0)); -if (wort.[[equals]]("Ludwig")) { - System.out.println("Hallo Ludwig!"); -} -\`\`\` -erklärung: \`length()\` zählt die Zeichen, \`toUpperCase()\` macht alles groß, \`charAt(0)\` liefert das erste Zeichen. Texte vergleichst du mit \`equals\`, nicht mit \`==\`. -::: - -## Fehler finden: Initialen - -::: übung l17-java-fehler -titel: Initialen reparieren -code: java -auftrag: Das Programm liest Vor- und Nachnamen ein und soll die Initialen ausgeben. Bei Ludwig Geißbock also **L. G.** Es gibt aber **u. e.** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: Bei welchem Index beginnt Java zu zählen? -tipp: Das erste Zeichen hat den Index 0: \`vorname.charAt(0)\` und \`nachname.charAt(0)\`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l17-java-laeuft-nicht -titel: Buchstaben zählen -code: java -auftrag: Das Programm soll den ersten Buchstaben und die Länge von **Ludwig** ausgeben, also **L hat 6 Buchstaben**. Es läuft aber nicht, denn zwei Methoden sind falsch geschrieben. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L hat 6 Buchstaben -braucht: anweisung -tipp: Methodennamen sind auch CamelCase: Sie beginnen klein, jedes weitere Wort beginnt groß. -tipp: Richtig sind \`name.charAt(0)\` mit großem A und \`name.length()\` ganz klein. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Noch mehr Methoden - -Ein String kann noch viel mehr. Diese Methoden brauchst du auch oft: - -| Methode | Bedeutung | Beispiel | Ergebnis | -|---|---|---|---| -| \`lastIndexOf("e")\` | Wo steht das letzte e? | \`"Ziegenbock".lastIndexOf("e")\` | 4 | -| \`isEmpty()\` | Ist der Text leer? | \`"".isEmpty()\` | true | -| \`equalsIgnoreCase(…)\` | gleicher Text, egal ob groß oder klein? | \`"Ziegenbock".equalsIgnoreCase("ZIEGENBOCK")\` | true | -| \`replace(alt, neu)\` | ersetzt jede Stelle | \`"Ziegenbock".replace("bock", "käse")\` | Ziegenkäse | -| \`repeat(n)\` | den Text n-mal hintereinander | \`"Mäh".repeat(3)\` | MähMähMäh | -| \`concat(…)\` | hängt Text an, wie \`+\` | \`"Ziegen".concat("bock")\` | Ziegenbock | -| \`compareTo(…)\` | Wer kommt im Alphabet zuerst? | \`"Anna".compareTo("Ben")\` | -1 | - -\`compareTo\` liefert eine Zahl: **negativ**, wenn der Text im Alphabet vorher kommt, **0**, wenn beide gleich sind, und **positiv**, wenn er danach kommt. Damit kannst du Namen sortieren. - -Zwei Methoden gehören nicht zu einem Text, sondern zu einer Klasse. Davor steht deshalb der Name der Klasse: - -- \`Integer.parseInt("7")\` macht aus dem Text "7" die Zahl 7. Mit ihr kannst du rechnen. -- \`String.valueOf(7)\` macht aus der Zahl 7 den Text "7". - -::: diagramm zk-mehr.visustruct -Alle neuen Methoden an einem Beispiel. Gehe es in der Simulation durch und vergleiche jede Ausgabe mit der Tabelle. -::: - -## Text aus einer Datei - -Programme bekommen Text oft aus einer **Datei**. Prinzessin Zora hat Ludwig einen Brief geschrieben. Er liegt als Datei \`brief.txt\` bereit: - -::: datei brief.txt -Lieber Ludwig, -der Fuchs hat mich in seinen Turm gesperrt. -Die Tür hat ein Schloss mit einem geheimen Wort. -Das Wort findest du nur mit Java. -Bitte hilf mir! -Deine Prinzessin Zora -::: - -Diese Zeilen lesen die ganze Datei in einen String: - -\`\`\` -import java.nio.file.*; - -String brief = Files.readString(Path.of("brief.txt")); -\`\`\` - -Wie das Lesen genau funktioniert, lernst du später in einer eigenen Lektion über Dateien. Hier reicht: Danach steht der ganze Brief in \`brief\`, mit allen Zeilen. - -Am Ende jeder Zeile steht ein unsichtbares Zeichen für den **Zeilenumbruch**. In Java schreibst du es als \`"\\n"\`. Mit \`brief.split("\\n")\` zerlegst du den Brief deshalb in seine Zeilen. - -::: übung l17-java-brief -titel: Wo steckt der Fuchs? -code: java -auftrag: Im Startcode wird der Brief schon gelesen. Zerlege ihn in Zeilen und gib **Zeilen:** und die Anzahl aus. Suche dann die Zeile, in der **Fuchs** steht, und gib **Fuchs in Zeile** und ihre Nummer aus. Die erste Zeile hat die Nummer 1. -start: l17-java-brief-start.visustruct -test: - -> Zeilen: 6 | Fuchs in Zeile 2 -braucht: forSchleife, verzweigung -tipp: \`String[] zeilen = brief.split("\\n");\` zerlegt den Brief. Mit einer For-Schleife gehst du dann alle Zeilen durch. -tipp: In der Schleife prüfst du \`zeilen[i].contains("Fuchs")\`. Der Index beginnt bei 0, deshalb gibst du \`(i + 1)\` aus. Die Klammern brauchst du, damit Java rechnet und nicht 1 an den Text hängt. -lösung: l17-java-brief-loesung.visustruct -::: - -## Die Java-API - -Niemand kennt alle Methoden auswendig, auch Profis nicht. Sie schlagen nach, in der **Java-API**. API heißt Application Programming Interface. Gemeint ist die Beschreibung von allem, was Java schon mitbringt. - -Alle Methoden von String findest du hier: [String in der Java-API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Scrolle dort zur Tabelle **Method Summary**. Jede Zeile beschreibt eine Methode: - -| Spalte | Beispiel | Bedeutung | -|---|---|---| -| Modifier and Type | \`int\` | Was die Methode zurückgibt | -| Method | \`lastIndexOf(String str)\` | Name und Parameter mit ihrem Datentyp | -| Description | Returns the index within this string of the last occurrence … | Was die Methode macht | - -Die API ist auf Englisch. Dein Browser kann die Seite übersetzen. Die Namen der Methoden bleiben englisch, denn so schreibst du sie auch in Java. - -::: ludwig -Die Seite ist sehr lang. Mit Strg+F (am Mac ⌘+F) suchst du nach dem Namen der Methode und bist sofort da. -::: - -Schlag diese Methoden nach. Sie stehen nicht in dieser Lektion: - -::: quiz -? Schlag \`strip()\` nach. Was macht die Methode? -- [ ] Sie entfernt alle Leerzeichen im Text. -- [x] Sie entfernt die Leerzeichen am Anfang und am Ende. -- [ ] Sie schneidet das letzte Zeichen ab. -> Wie \`trim()\`. \`strip()\` kennt aber auch besondere Leerzeichen aus anderen Schriften. - -? Schlag \`isBlank()\` nach. Was liefert \`" ".isBlank()\`? -- [x] true -- [ ] false -- [ ] 3 -> isBlank prüft, ob der Text leer ist oder nur aus Leerzeichen besteht. \`" ".isEmpty()\` wäre dagegen false. - -? Schlag \`toCharArray()\` nach. Welchen Datentyp liefert die Methode? -- [ ] String -- [ ] String[] -- [x] char[] -> Ein Array mit allen Zeichen des Textes, für jedes Zeichen ein Fach. - -? Schlag \`indexOf\` mit zwei Parametern nach. Was liefert \`"Ziegenbock".indexOf("e", 3)\`? -- [ ] 2 -- [x] 4 -- [ ] -1 -> Die Suche beginnt erst bei Index 3. Das erste e ab dort steht bei Index 4. - -? Schlag \`startsWith\` nach. Welchen Datentyp liefert die Methode? -- [x] boolean -- [ ] int -- [ ] String -> In der Spalte „Modifier and Type“ steht boolean: Die Antwort ist true oder false. -::: - -## Spiel: Prinzessin retten - -Jetzt hilfst du Ludwig. Der Fuchs hat Prinzessin Zora in seinen Turm gesperrt, und das Tor öffnet sich nur mit einem geheimen Wort. - -::: spiel prinzessin -titel: Spiel: Prinzessin retten -intro: Auf dem Weg zum Turm kommt Ludwig an **sechs Stationen**. An jeder steht ein Stück Java-Code, und heraus kommt eine Zahl. Mit \`alphabet.charAt(zahl)\` wird aus der Zahl ein Buchstabe, der im **Kreuzworträtsel** landet. Löst du das Rätsel, ergeben die gelben Felder das geheime Wort für das Tor. - -station: Am Burggraben - String name = " Zora "; - int zahl = name.trim().length(); -frage: Welche Zahl steht in \`zahl\`? -zahl: 4 -feld: 5 1 -tipp: \`trim()\` schneidet die Leerzeichen vorne und hinten ab. Zähle danach die Zeichen. -> Nach \`trim()\` bleibt \`"Zora"\` übrig, und das sind 4 Zeichen. - -station: Im Fuchswald - String ort = "Fuchsbau"; - int zahl = ort.length(); -frage: Welche Zahl steht in \`zahl\`? -zahl: 8 -feld: 4 4 -tipp: \`length()\` zählt alle Zeichen. Zähle F-u-c-h-s-b-a-u. -> „Fuchsbau“ hat 8 Zeichen. - -station: Am Ziegenfelsen - String wort = "Ziegenbock"; - int zahl = wort.indexOf("bock"); -frage: Welche Zahl steht in \`zahl\`? -zahl: 6 -feld: 4 6 -tipp: \`indexOf\` gibt die Stelle zurück, an der „bock“ anfängt. Das Z hat den Index 0. -> Z i e g e n hat die Indizes 0 bis 5, also beginnt „bock“ bei Index 6. - -station: Auf der Turmtreppe - String wort = "Prinzessin"; - int zahl = wort.indexOf("s", 7); -frage: Welche Zahl steht in \`zahl\`? Schlag \`indexOf(String str, int fromIndex)\` in der Java-API nach. -zahl: 7 -feld: 6 6 -tipp: Die Suche beginnt bei Index 7, und Index 7 zählt schon mit. Schreib dir die Indizes unter die Buchstaben. -> P r i n z e s s i n hat die Indizes 0 bis 9. Ab Index 7 gesucht, steht das erste „s“ genau bei 7. - -station: Vor der Fuchshöhle - int zahl = "Zora".compareTo("Zoe"); -frage: Welche Zahl steht in \`zahl\`? -zahl: 13 -feld: 1 9 -tipp: \`compareTo\` sucht die erste Stelle, an der sich die Texte unterscheiden, und rechnet die Differenz der Zeichencodes aus. \`'r'\` ist 114, \`'e'\` ist 101. -> „Zo“ ist gleich, dann kommt \`'r'\` gegen \`'e'\`: 114 − 101 = 13. - -station: Am Turmtor - int zahl = Integer.parseInt("1" + "1") / 5; -frage: Welche Zahl steht in \`zahl\`? -zahl: 2 -feld: 6 5 -tipp: Zuerst werden die Texte verbunden, dann wird daraus eine Zahl. Bei \`int\` wird beim Teilen der Rest weggelassen. -> \`"1" + "1"\` ist \`"11"\`, \`parseInt\` macht daraus 11, und 11 / 5 ist bei ganzen Zahlen 2. - -kreuzwort: Die blauen Buchstaben hast du schon. Fülle die anderen Felder mit Fachbegriffen aus Java. Klick auf einen Hinweis, um zum Wort zu springen. -- → 1 3 BOOLEAN: Datentyp für wahr oder falsch -- → 4 1 STRING: Datentyp für Text, also für eine Zeichenkette -- → 6 5 CHAR: Datentyp für ein einzelnes Zeichen -- ↓ 1 6 LENGTH: Methode, die die Zeichen eines Strings zählt -- ↓ 2 1 ELSE: Der „sonst“-Teil einer Verzweigung -- ↓ 3 3 BREAK: Springt sofort aus einer Schleife heraus -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: Ludwig steht vor dem Tor. Das Schloss vergleicht dein Wort mit \`equals\`. Trag das geheime Wort ein. - String wort = "___"; - if (wort.equals(geheimesWort)) { - tor.oeffnen(); - } -ende: Zora ist frei, und der Fuchs ist schnell davongelaufen. Das geheime Wort war \`BREAK\`: Mit \`break\` kommt man aus jeder Schleife heraus, und Zora aus dem Turm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass auch die Zeichen eines Strings ab 0 gezählt werden. → Text ist eine Reihe von Zeichen -- Ich kann mit \`length()\` und \`charAt(i)\` arbeiten. → Text ist eine Reihe von Zeichen -- Ich kann einen \`char\` von einem String unterscheiden und mit \`(int)\` und \`(char)\` den Zeichencode nutzen. → Ein einzelnes Zeichen: char -- Ich kann mit \`substring\` einen Teil aus einem Text schneiden. → Teile herausschneiden -- Ich kann \`indexOf\`, \`contains\`, \`toUpperCase\` und \`equals\` anwenden. → Suchen und vergleichen -- Ich kann einen Text mit einer Schleife Zeichen für Zeichen durchgehen. → Zeichen für Zeichen -- Ich kann einen Text mit \`split\` in ein String-Array zerlegen. → Zerlegen mit split -- Ich kann in Java einen Text einlesen und mit \`equals\` vergleichen. → Jetzt in Java -- Ich kann weitere Methoden wie \`lastIndexOf\`, \`replace\`, \`compareTo\` und \`Integer.parseInt\` anwenden. → Noch mehr Methoden -- Ich kann einen Text aus einer Datei mit \`split("\\n")\` in Zeilen zerlegen. → Text aus einer Datei -- Ich kann eine Methode in der Java-API nachschlagen und ihre Beschreibung lesen. → Die Java-API -::: - -## Wissenstest - -::: quiz -? Was liefert \`"Ludwig".charAt(1)\`? -→ Text ist eine Reihe von Zeichen -- [ ] L -- [x] u -- [ ] d -> Gezählt wird ab 0: L hat den Index 0, u den Index 1. - -? Was ist \`'C'\`? -→ Ein einzelnes Zeichen: char -- [x] ein \`char\`, also genau ein Zeichen -- [ ] ein String -- [ ] eine Variable mit dem Namen C -> Einfache Anführungszeichen stehen für ein einzelnes Zeichen, also einen \`char\`. \`"C"\` in doppelten wäre ein String. - -? \`'A'\` hat den Zeichencode 65. Was ergibt \`(char) 67\`? -→ Ein einzelnes Zeichen: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 ist A, 66 ist B, 67 ist C. Mit \`(char)\` wird aus dem Code wieder ein Zeichen. - -? Was liefert \`"Struktogramm".substring(0, 6)\`? -→ Teile herausschneiden -- [x] Strukt -- [ ] Struktog -- [ ] truktog -> Die Zeichen mit den Indizes 0 bis 5. Das Ende 6 gehört nicht mehr dazu. - -? Ein Wort hat 7 Zeichen. Welchen Index hat das letzte Zeichen? -→ Text ist eine Reihe von Zeichen -- [ ] 7 -- [x] 6 -- [ ] 8 -> Wie beim Array: immer eins weniger als die Länge. - -? Wie vergleichst du in Java zwei Strings auf gleichen Inhalt? -→ Suchen und vergleichen -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Ordne jeder Methode zu, was sie bei \`"Geißbock"\` liefert. -→ Suchen und vergleichen -= length() -> 8 -= charAt(0) -> G -= indexOf("bock") -> 4 -= substring(4) -> bock - -? Welche Bedingung prüft in Java, ob in \`eingabe\` der Text Ludwig steht? -→ Jetzt in Java -- [ ] eingabe = "Ludwig" -- [ ] eingabe == 'Ludwig' -- [x] eingabe.equals("Ludwig") -> Texte vergleichst du mit equals. Einfache Anführungszeichen sind nur für ein einzelnes Zeichen. - -? Was liefert \`Integer.parseInt("12") + 1\`? -→ Noch mehr Methoden -- [ ] "121" -- [x] 13 -- [ ] eine Fehlermeldung -> parseInt macht aus dem Text eine Zahl. Danach rechnet Java ganz normal. -::: -`,sw=`--- -titel: Aufruf und Aussprung -bereich: Grundlagen -kurz: Große Aufgaben in Unterprogramme zerlegen und Schleifen vorzeitig verlassen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine große Aufgabe in Teilprobleme zerlegen -- einen Aufruf einfügen und ein Unterprogramm in einem eigenen Reiter bauen -- einem Unterprogramm mit Parametern Werte übergeben -- mit return einen Rückgabewert liefern und ihn beim Aufruf speichern -- erklären, warum ein Unterprogramm eine Zahl nicht verändern kann, ein Array aber schon -- erklären, was ein Aussprung macht und warum er in einer Verzweigung steht -- eine Schleife mit einem Aussprung oder über ihre Bedingung beenden -- in Java eine Methode erkennen und eine Schleife mit \`break\` verlassen -::: - -## Große Aufgaben zerlegen - -Lange Struktogramme werden schnell unübersichtlich. Deshalb zerlegst du eine große Aufgabe in **Teilprobleme**. Jedes Teilproblem bekommt einen eigenen Namen und ein eigenes Struktogramm, ein **Unterprogramm**. - -Ein Quiz besteht zum Beispiel aus drei Teilen: begrüßen, Fragen stellen, Ergebnis zeigen. Das Hauptprogramm nennt nur noch diese drei Teile: - -::: diagramm l15-hauptprogramm.visustruct -Das Hauptprogramm besteht nur aus drei Aufrufen. Was in jedem Teil passiert, steht in einem eigenen Struktogramm. -::: - -So siehst du auf einen Blick, wie das Programm aufgebaut ist. Die Einzelheiten stehen woanders. - -## Der Aufruf - -Ein **Aufruf** ist ein Kasten mit doppelten senkrechten Linien links und rechts. Er bedeutet: Führe hier das Unterprogramm mit diesem Namen aus und mache danach weiter. - -- Du ziehst **Aufruf** aus dem Bereich Elemente und schreibst den Namen hinein, z. B. \`zeigeBegruessung()\`. -- Die runden Klammern am Ende zeigen, dass es ein Unterprogramm ist. So schreibt man es auch in Java, Python und JavaScript. -- Das Unterprogramm selbst baust du als eigenes Struktogramm in einem neuen Reiter. Als Titel trägst du denselben Namen ein. - -::: diagramm l15-unterprogramm.visustruct -Das Unterprogramm zeigeBegruessung() als eigenes Struktogramm. -::: - -> **In der Simulation:** Erreicht die Simulation einen Aufruf, sucht sie unter den offenen Reitern das Struktogramm mit diesem Titel und führt es aus. Danach macht sie im Hauptprogramm weiter. Solange das Unterprogramm läuft, bleibt der Aufruf-Kasten markiert. Ein Unterprogramm wie dieses, nur mit Namen als Titel, teilt sich die Variablen mit dem Hauptprogramm. Wie ein Unterprogramm eigene Variablen bekommt, lernst du gleich. Gibt es keinen passenden Reiter, überspringt die Simulation den Aufruf und zeigt einen Hinweis. - -::: angeberwissen -Warum sagt dein Lehrer bei Java immer „Methode“ und nie „Funktion“ oder „Prozedur“? Weil in Java jedes Unterprogramm zu einer Klasse gehören muss, und Unterprogramme einer Klasse heißen Methoden. Den Unterschied gibt es trotzdem: Liefert eine Methode ein Ergebnis zurück, ist sie eigentlich eine Funktion. Steht \`void\` davor, liefert sie nichts und ist eigentlich eine Prozedur. Java ist da einfach bequem und nennt beide Methode. Ich auch: Meine Methode heißt Fressen. -::: - -## Übung: Aufruf einbauen - -::: übung l15-titel -titel: Begrüßung mit Aufruf -auftrag: Stell dir vor, das Unterprogramm \`zeigeTitel()\` gibt es schon. Baue das Hauptprogramm: Es ruft zuerst \`zeigeTitel()\` auf. Danach fragt es nach dem Namen und gibt **Hallo** und den Namen aus. - Bei der Eingabe Ada erscheint also **Hallo Ada**. -test: Ada -> Hallo Ada -test: Ben -> Hallo Ben -braucht: aufruf -tipp: Das Hauptprogramm hat drei Teile: den Aufruf, die Eingabe und die Ausgabe. -tipp: Ziehe einen Aufruf aus dem Bereich Elemente ganz nach oben und schreibe \`zeigeTitel()\` hinein. Darunter kommen die Eingabe \`input: String name "Wie heißt du?"\` und die Ausgabe. -lösung: l15-titel-loesung.visustruct -::: - -## Werte übergeben: Parameter - -Oft soll ein Unterprogramm mit Werten arbeiten, die erst beim Aufruf feststehen. Dann bekommt es als Titel eine **Kopfzeile**: - -\`\`\` -void zeigeSumme(int a, int b) -\`\`\` - -- \`void\` heißt: Das Unterprogramm liefert kein Ergebnis zurück. -- \`zeigeSumme\` ist der Name. -- In den Klammern stehen die **Parameter**: Variablen mit Datentyp, die beim Aufruf ihre Werte bekommen. Mehrere trennst du mit Komma. - -::: diagramm l15-zeige-summe.visustruct -Das Unterprogramm mit der Kopfzeile als Titel. Es rechnet mit a und b, als wären es normale Variablen. -::: - -Im Aufruf-Kasten schreibst du die Werte in die Klammern, z. B. \`zeigeSumme(3, 4)\`. Der erste Wert kommt in \`a\`, der zweite in \`b\`. Statt Zahlen gehen auch Variablen und Rechnungen wie \`zeigeSumme(x, x + 1)\`. - -::: diagramm l15-zeige-summe-haupt.visustruct -Das Hauptprogramm ruft zeigeSumme zweimal mit anderen Werten auf. Öffne beide Diagramme und starte hier die Simulation. -::: - -> **Lokale Variablen:** Die Parameter und alle Variablen, die im Unterprogramm entstehen, gehören nur dem Unterprogramm. Es bekommt **Kopien** der Werte, mehr dazu weiter unten bei der Wertübergabe (call by value). In der Simulation siehst du während des Aufrufs nur die Variablen des Unterprogramms. - -## Ergebnis zurückgeben: return - -Ein Unterprogramm kann auch ein Ergebnis liefern, den **Rückgabewert**. Dann steht in der Kopfzeile statt \`void\` der Datentyp des Ergebnisses: - -\`\`\` -int quadrat(int x) -\`\`\` - -Die Anweisung \`return ergebnis\` gibt den Wert zurück und beendet das Unterprogramm sofort. Beim Aufruf speicherst du das Ergebnis in einer Variablen: Nach \`int q = quadrat(5)\` steht in \`q\` der Wert 25. - -::: diagramm l15-quadrat.visustruct -Das Unterprogramm quadrat rechnet und gibt das Ergebnis mit return zurück. -::: - -::: diagramm l15-quadrat-haupt.visustruct -Das Hauptprogramm speichert den Rückgabewert in q. Öffne beide Diagramme und probiere die Simulation aus. -::: - -> Ein Unterprogramm mit Rückgabewert muss mit \`return\` enden. Fehlt das return, meldet die Simulation einen Fehler. - -In der **Code-Ansicht** wird aus der Kopfzeile eine Java-Methode (\`public static int quadrat(int x)\`), in Python ein \`def\` und in JavaScript eine \`function\`. Sind die Unterprogramme in anderen Reitern offen, stehen sie beim Hauptprogramm mit im Code. - -## Übung: Fläche berechnen - -::: übung l15-flaeche -titel: Fläche berechnen -auftrag: Baue zwei Diagramme. Das Unterprogramm \`int flaeche(int a, int b)\` gibt die Fläche eines Rechtecks zurück, also a mal b. Das Hauptprogramm fragt nach Länge und Breite, ruft \`flaeche\` auf und gibt **Fläche:** und das Ergebnis aus. - Bei 3 und 4 erscheint **Fläche: 12**. -test: 3, 4 -> Fläche: 12 -test: 5, 5 -> Fläche: 25 -test: 7, 0 -> Fläche: 0 -braucht: aufruf -tipp: Du baust zwei Diagramme: ein Unterprogramm, das nur rechnet und das Ergebnis zurückgibt, und ein Hauptprogramm für Eingabe und Ausgabe. -tipp: Lege mit **+** einen neuen Reiter an und gib ihm den Titel \`int flaeche(int a, int b)\`. Darin reicht eine Anweisung: \`return a * b\`. Im Hauptprogramm schreibst du in den Aufruf-Kasten \`int f = flaeche(laenge, breite)\`. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Kopie oder Original: call by value und call by reference - -Was passiert, wenn ein Unterprogramm seinen Parameter verändert? Das hängt davon ab, **was** übergeben wird. - -**Wertübergabe**, englisch **call by value**: Bei \`int\`, \`double\`, \`boolean\`, \`char\` und \`String\` bekommt das Unterprogramm eine **Kopie** des Werts. Ändert es seinen Parameter, bleibt die Variable des Aufrufers, wie sie war. - -**Referenzübergabe**, englisch **call by reference**: Bei einem **Array** bekommt das Unterprogramm keine Kopie, sondern einen Verweis auf dasselbe Array. Ändert es ein Fach, ist das Fach auch beim Aufrufer geändert. Die Kopfzeile dafür sieht so aus: - -\`\`\` -void leeren(boolean[] plaetze) -\`\`\` - -::: grafik uebergabe.svg -Oben bekommt verdopple eine Kopie von zahl. Unten zeigen reihe und plaetze auf dasselbe Array. -::: - -Probiere beides aus. Öffne die drei Diagramme und starte im Hauptprogramm die Simulation: - -::: diagramm uebergabe-verdopple.visustruct -verdopple ändert nur seine Kopie x. -::: - -::: diagramm uebergabe-leeren.visustruct -leeren setzt alle Plätze auf false, zum Beispiel wenn eine Vorstellung ausfällt. -::: - -::: diagramm uebergabe-haupt.visustruct -zahl ist nach dem Aufruf immer noch 5. Die Plätze in reihe sind nach dem Aufruf dagegen frei. -::: - -> Willst du mit einer Zahl aus einem Unterprogramm etwas zurückbekommen, nimmst du \`return\`, z. B. \`zahl = verdopple(zahl)\` mit der Kopfzeile \`int verdopple(int x)\`. Ein Array dagegen kann das Unterprogramm direkt verändern. - -::: angeberwissen -Ganz genau genommen kennt Java nur die Wertübergabe, also call by value. Bei einem Array wird aber nicht das ganze Array kopiert, sondern nur seine **Adresse** im Speicher. Mit der kopierten Adresse findet das Unterprogramm dasselbe Array. Das ist so, als würde ich dir eine Kopie meines Schließfachschlüssels geben: Der Schlüssel ist kopiert, das Heu im Fach gibt es nur einmal. -::: - -## Übung: Bonuspunkte - -::: übung uebergabe-bonus -titel: Bonuspunkte -auftrag: Das Hauptprogramm ruft \`bonus(punkte)\` auf und gibt danach alle Punkte aus. Baue das Unterprogramm \`void bonus(int[] punkte)\`, das jede Punktzahl im Array um 1 erhöht. Statt 3, 5 und 8 erscheinen dann **4**, **6** und **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: Das Hauptprogramm ist schon fertig. Du baust nur das Unterprogramm in einem neuen Reiter. -tipp: Lege mit **+** einen neuen Reiter mit dem Titel \`void bonus(int[] punkte)\` an. Darin läuft eine For-Schleife über alle Fächer und rechnet \`punkte[i] = punkte[i] + 1\`. Ein return brauchst du nicht, denn das Array wird direkt geändert. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## Der Aussprung - -Manchmal soll eine Schleife **vorzeitig** enden, mitten im Rumpf. Dafür gibt es den **Aussprung**. Er verlässt sofort die Schleife, in der er steht. Das Programm macht mit dem ersten Kasten nach der Schleife weiter. In Java, Python und JavaScript heißt das \`break\`. - -Den Aussprung erkennst du an der Spitze am linken Rand. In den Kasten kannst du einen kurzen Kommentar schreiben, z. B. \`Schleife verlassen\`. Er erscheint im Code als Kommentar hinter \`break\`. - -Ein Aussprung steht fast immer in einer **Verzweigung**: Nur wenn eine Bedingung zutrifft, wird die Schleife verlassen. - -Typisch ist die **Endlosschleife mit Aussprung**. Die Schleife selbst hat keine Bedingung. Sie läuft so lange, bis der Aussprung erreicht wird: - -::: diagramm l15-endlos-aussprung.visustruct -Die Schleife fragt immer wieder nach einer Zahl. Bei 0 springt sie heraus, sonst zählt sie die Zahl zur Summe. -::: - -Ohne Aussprung würde eine Endlosschleife wirklich nie enden. - -## Aussprung in der Simulation - -Die Simulation führt den Aussprung aus wie \`break\`: Sie verlässt die innerste Schleife und macht nach ihr weiter. Steht ein Aussprung außerhalb jeder Schleife, endet das Unterprogramm, im Hauptprogramm das ganze Programm. Dasselbe macht \`return\`. - -Oft geht es auch ganz ohne Aussprung: Du schreibst die Abbruchbedingung direkt in die Schleife. Das ist meist sogar übersichtlicher. - -## Übung: Summe bis 0 - -::: übung l15-bis-null -titel: Summe bis 0 -auftrag: Baue das Programm „Summe bis 0“ **ohne** Aussprung nach. Es fragt so lange nach ganzen Zahlen, bis jemand 0 eingibt. Danach gibt es **Summe:** und die Summe aller eingegebenen Zahlen aus. -test: 3, 4, 0 -> Summe: 7 -test: 0 -> Summe: 0 -test: 10, -2, 5, 0 -> Summe: 13 -tipp: Ohne Aussprung muss die Bedingung der Schleife selbst prüfen, ob die 0 eingegeben wurde. -tipp: Frage die erste Zahl schon vor der Schleife ab. Die While-Schleife läuft mit der Bedingung \`zahl != 0\`. Im Rumpf zählst du die Zahl zur Summe und fragst danach die nächste Zahl ab. Bei der zweiten Eingabe schreibst du nur \`input: zahl "…"\`, weil es \`zahl\` schon gibt. -lösung: l15-bis-null-loesung.visustruct -::: - -## Jetzt in Java - -Ein Unterprogramm heißt in Java **Methode**. Das Quadrat von oben sieht so aus: - -\`\`\`java -public static int quadrat(int x) { - int ergebnis = x * x; - return ergebnis; -} -\`\`\` - -Das Hauptprogramm ruft die Methode mit ihrem Namen auf und gibt ihr den Wert in runden Klammern mit: - -\`\`\`java -int q = quadrat(zahl); -System.out.println("Das Quadrat ist " + q); -\`\`\` - -- In der Kopfzeile steht vor dem Namen, was die Methode zurückgibt (\`int\`). Gibt sie nichts zurück, steht dort \`void\`. -- Die Parameter stehen mit Datentyp in den runden Klammern: \`(int x)\`. Mehrere trennst du mit Komma. -- Im Bereich Code schreibst du jede Methode in einen eigenen Reiter, so wie jedes Unterprogramm ein eigenes Struktogramm hat. -- Der Aussprung heißt in Java \`break\`. Mit \`while (true)\` baust du eine Schleife, die nur durch \`break\` endet. - -::: übung l18-java-kasse -titel: Kasse mit break -code: java -auftrag: Schreibe in Java eine Kasse. Sie fragt immer wieder nach einem Preis in ganzen Euro. Bei 0 verlässt sie die Schleife mit \`break\`. Danach gibt sie aus, wie viele Artikel es waren und was alles zusammen kostet. - Bei 3, 4 und dann 0 erscheint **Artikel: 2** und **Gesamt: 7**. -test: 3, 4, 0 -> Artikel: 2 | Gesamt: 7 -test: 0 -> Artikel: 0 | Gesamt: 0 -test: 10, 2, 5, 0 -> Artikel: 3 | Gesamt: 17 -braucht: endlosschleife, aussprung -tipp: Du brauchst zwei Variablen, die vor der Schleife mit 0 anfangen: eine für die Summe und eine für die Anzahl. -tipp: In \`while (true) { … }\` liest du den Preis ein. Gleich danach prüfst du \`if (preis == 0) { break; }\`. Erst dahinter zählst du den Preis zur Summe und die Anzahl um 1 hoch. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Lückentext: Methoden und break - -::: lückentext -titel: Methode und Aussprung -auftrag: Ergänze die fehlenden Wörter. Oben steht die Methode, die ein Quadrat ausrechnet. Darunter steht das Hauptprogramm, das sie aufruft. Im Bereich Code stehen beide in eigenen Reitern. -\`\`\`java -public static [[int]] quadrat(int x) { - [[return]] x * x; -} -\`\`\` -\`\`\`java -int zahl = 1; -while ([[true]]) { - int q = [[quadrat]](zahl); - if (q > 50) { - [[break]]; - } - System.out.println(q); - zahl = zahl + 1; -} -\`\`\` -erklärung: \`int\` vor dem Methodennamen heißt: Die Methode gibt eine ganze Zahl zurück, und zwar mit \`return\`. Gibt eine Methode nichts zurück, steht dort \`void\`. \`while (true)\` läuft endlos, bis \`break\` die Schleife verlässt. Ausgabe: 1, 4, 9, 16, 25, 36 und 49. -::: - -## Fehler finden: Raten - -::: übung l18-java-fehler -titel: Versuche zählen reparieren -code: java -auftrag: Beim Zahlenraten ist die gesuchte Zahl 7. Das Programm fragt so lange nach einem Tipp, bis die 7 kommt, und gibt dann die Zahl der Versuche aus. Bei 3, 5 und 7 waren es 3 Versuche, das Programm sagt aber **Versuche: 2**. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Versuche: 3 -test: 7 -> Versuche: 1 -test: 1, 2, 3, 7 -> Versuche: 4 -braucht: endlosschleife, aussprung -tipp: Was passiert beim richtigen Tipp? Wird er noch mitgezählt, bevor \`break\` die Schleife verlässt? -tipp: Zähle zuerst hoch und prüfe danach: \`versuche = versuche + 1;\` gehört vor das \`if\` mit dem \`break\`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Spiel: Was kommt zurück? - -::: spiel frage -titel: Spiel: Was kommt zurück? -intro: Ludwig ruft Methoden auf und springt aus Schleifen. Sag vorher, was ausgegeben wird. Sieben Runden. - -runde: - public static int quadrat(int x) { - return x * x; - } - - int q = quadrat(5); - System.out.println(q); -frage: Was wird ausgegeben? -- 5 -- 10 -- [x] 25 -> Der Aufruf gibt 5 in x, return liefert 5 · 5 zurück. - -runde: - public static void zeigeSumme(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - zeigeSumme(x, x + 1); -frage: Was wird ausgegeben? -- [x] 5 -- 23 -- 3 -> Der erste Wert 2 kommt in a, der zweite 3 in b. In Java rechnet \`a + b\` mit Zahlen ganz normal. - -runde: -frage: Was liefert eine Methode zurück, vor deren Namen \`void\` steht? -- eine ganze Zahl -- [x] nichts -- immer 0 -> \`void\` heißt: kein Rückgabewert. Steht dort \`int\`, kommt eine ganze Zahl zurück. - -runde: - public static void verdopple(int x) { - x = x * 2; - } - - int zahl = 5; - verdopple(zahl); - System.out.println(zahl); -frage: Was wird ausgegeben? -- [x] 5 -- 10 -- 0 -> Call by value: verdopple bekommt eine Kopie. zahl bleibt 5. - -runde: - public static void leeren(boolean[] plaetze) { - for (int i = 0; i < plaetze.length; i++) { - plaetze[i] = false; - } - } - - boolean[] reihe = {true, true}; - leeren(reihe); - System.out.println(reihe[0]); -frage: Was wird ausgegeben? -- true -- [x] false -> Bei einem Array zeigen reihe und plaetze auf dasselbe Array. Die Änderung sieht auch der Aufrufer. - -runde: - int summe = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - summe = summe + i; - } - System.out.println(summe); -frage: Was wird ausgegeben? -- [x] 6 -- 10 -- 55 -> Bei i = 4 springt \`break\` sofort aus der Schleife. Gezählt sind nur 1 + 2 + 3. - -runde: - int zahl = 1; - while (true) { - zahl = zahl * 3; - if (zahl > 20) { - break; - } - } - System.out.println(zahl); -frage: Was wird ausgegeben? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Erst 27 ist größer als 20, dann endet die Endlosschleife mit break. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine große Aufgabe in Teilprobleme zerlegen. → Große Aufgaben zerlegen -- Ich kann einen Aufruf einfügen und ein Unterprogramm in einem eigenen Reiter bauen. → Der Aufruf -- Ich kann einem Unterprogramm mit Parametern Werte übergeben. → Werte übergeben: Parameter -- Ich kann mit return einen Rückgabewert liefern und ihn beim Aufruf speichern. → Ergebnis zurückgeben: return -- Ich kann erklären, warum ein Unterprogramm eine Zahl nicht verändern kann, ein Array aber schon. → Kopie oder Original: call by value und call by reference -- Ich kann erklären, was ein Aussprung macht und warum er in einer Verzweigung steht. → Der Aussprung -- Ich kann eine Schleife mit einem Aussprung oder über ihre Bedingung beenden. → Aussprung in der Simulation -- Ich kann in Java eine Methode erkennen und eine Schleife mit \`break\` verlassen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Was bedeutet ein Aufruf-Kasten mit dem Text \`zeigeErgebnis()\`? -→ Der Aufruf -- [ ] Das Programm endet hier. -- [x] Hier wird das Unterprogramm zeigeErgebnis() ausgeführt, danach geht es weiter. -- [ ] Hier wird das Wort zeigeErgebnis() ausgegeben. - -? Wo geht es nach einem Aussprung weiter? -→ Der Aussprung -- [ ] Am Anfang der Schleife -- [x] Beim ersten Kasten nach der Schleife -- [ ] Ganz am Anfang des Programms -> Der Aussprung verlässt die Schleife, in der er steht. In Java, Python und JavaScript heißt er break. - -? Warum steht ein Aussprung meistens in einer Verzweigung? -→ Der Aussprung -- [x] Damit die Schleife nur verlassen wird, wenn eine Bedingung zutrifft -- [ ] Weil ein Aussprung sonst nicht gespeichert werden kann -- [ ] Weil eine Verzweigung selbst eine Schleife ist -> Ohne Verzweigung würde die Schleife schon im ersten Durchlauf verlassen. - -? Wo findet die Simulation das Unterprogramm zu einem Aufruf \`zeigeErgebnis()\`? -→ Der Aufruf -- [ ] Es muss im selben Struktogramm stehen. -- [x] In einem offenen Reiter mit dem Titel zeigeErgebnis() -- [ ] Die Simulation kann keine Unterprogramme ausführen. -> Baue jedes Unterprogramm in einem eigenen Reiter und gib ihm als Titel den Namen aus dem Aufruf. - -? Das Unterprogramm hat die Kopfzeile \`void zeigeSumme(int a, int b)\`. Welcher Aufruf passt? -→ Werte übergeben: Parameter -- [ ] zeigeSumme() -- [x] zeigeSumme(3, 4) -- [ ] int s = zeigeSumme(3, 4) -> Zwei Parameter brauchen zwei Werte. Und \`void\` heißt: Es kommt nichts zurück, das man speichern könnte. - -? Nach \`int q = quadrat(5)\` steht in q die 25. Was steht im Unterprogramm quadrat am Ende? -→ Ergebnis zurückgeben: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> Mit return gibt das Unterprogramm den Wert an den Aufruf zurück. Eine Ausgabe würde ihn nur anzeigen. - -? \`void verdopple(int x)\` rechnet \`x = x * 2\`. Im Hauptprogramm steht \`int zahl = 5\` und dann der Aufruf \`verdopple(zahl)\`. Welchen Wert hat zahl danach? -→ Kopie oder Original: call by value und call by reference -- [x] 5 -- [ ] 10 -- [ ] 0 -> Wertübergabe (call by value): verdopple hat nur eine Kopie von zahl verändert. - -? \`void leeren(boolean[] plaetze)\` setzt alle Fächer auf false. Was gilt nach \`leeren(reihe)\` für das Array reihe im Hauptprogramm? -→ Kopie oder Original: call by value und call by reference -- [ ] Es ist unverändert, weil das Unterprogramm eine Kopie bekommt. -- [x] Alle Fächer sind false, weil plaetze und reihe dasselbe Array sind. -- [ ] Das Array ist gelöscht. -> Referenzübergabe (call by reference): Ein Array wird als Referenz übergeben. Änderungen an den Fächern sieht auch der Aufrufer. - -? Wie heißt ein Unterprogramm in Java? -→ Jetzt in Java -- [ ] Klasse -- [x] Methode -- [ ] Schleife -> In Java ist ein Unterprogramm eine Methode, zum Beispiel die \`main\`-Methode. Je nach Art sagt man auch Prozedur oder Funktion. - -? Viele Bausteine haben in Schulbüchern einen zweiten Namen. Ordne jedem Begriff aus VisuStruct den anderen Namen zu. -→ Jetzt in Java -= Verzweigung -> Bedingte Anweisung -= Fallauswahl -> Mehrfachauswahl -= Schleife -> Wiederholungsstruktur -= Array -> Feld -= Unterprogramm -> Methode -> Beide Namen meinen dasselbe. Wenn du einen davon kennst, verstehst du auch den anderen. - -? Was steht in Java in der Kopfzeile einer Methode, die nichts zurückgibt? -→ Ergebnis zurückgeben: return -- [ ] int -- [x] void -- [ ] return -> void heißt „leer“: Die Methode macht etwas, gibt aber keinen Wert zurück. -::: -`,rw=`--- -titel: Suchen und Sortieren -bereich: Grundlagen -kurz: Mit Spielkarten verstehen, wie lineare und binäre Suche und Bubblesort arbeiten und warum manche Verfahren viel schneller sind. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, wie die lineare Suche ein Array durchsucht -- eine lineare Suche als Struktogramm bauen -- erklären, warum die binäre Suche nur bei sortierten Werten funktioniert -- begründen, warum die binäre Suche bei vielen Werten viel schneller ist -- einen Durchgang von Bubblesort mit Spielkarten vormachen -- in Java ein Array durchsuchen und Treffer zählen -::: - -## Eine Karte suchen - -Du hast einen Stapel Spielkarten verdeckt in einer Reihe vor dir liegen. Gesucht ist die 9. Was machst du? Du drehst eine Karte nach der anderen um, bis du die 9 findest oder alle Karten umgedreht hast. - -::: grafik ss-lineare-suche.svg -Die Karten liegen durcheinander. Jede umgedrehte Karte ist ein Vergleich: Ist das die 9? -::: - -Genau so arbeitet die **lineare Suche**: Sie geht das Array vom Index 0 bis zum Ende durch und vergleicht jedes Fach mit dem gesuchten Wert. Sie funktioniert immer, auch wenn die Werte durcheinander liegen. - -Im Array stehen unsere Karten als Zahlen. Bube, Dame und König lassen wir weg. - -::: diagramm ss-lineare-suche.visustruct -Die Schleife läuft, bis die Karte gefunden ist oder kein Fach mehr übrig ist. Die Variable vergleiche zählt mit, wie oft verglichen wurde. Suche in der Simulation die 2, die 27 und die 4. -::: - -> Ist die Karte nicht dabei, bleibt \`position\` bei **-1**. Einen Index -1 gibt es nicht, darum bedeutet -1 hier „nicht gefunden“. - -## Übung: Karte suchen - -::: übung ss-karte-suchen -titel: Karte suchen -auftrag: Lege das Array \`int[] karten = {8, 4, 11, 4, 6}\` an und frage nach einer Karte. Gib die Position der **ersten** passenden Karte aus, z. B. \`Position: 1\` bei der 4. Ist die Karte nicht dabei, gib \`Nicht gefunden\` aus. -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 8 -> Position: 0 -test: 9 -> Nicht gefunden -braucht: whileSchleife, verzweigung -tipp: Gehe das Array von vorn nach hinten durch und vergleiche jede Karte mit der gesuchten. -tipp: Baue das Beispiel oben nach. Die Variable vergleiche brauchst du hier nicht. Die Schleife muss aufhören, sobald \`position\` nicht mehr -1 ist, sonst findest du bei der 4 die zweite statt der ersten. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Binäre Suche: sortiert geht es schneller - -Jetzt liegen die Karten **sortiert** vor dir, von klein nach groß. Gesucht ist die 27. Du drehst nicht mehr vorne die erste Karte um, sondern die **mittlere**: - -- Ist sie die gesuchte, bist du fertig. -- Ist sie kleiner, kann die gesuchte Karte nur noch rechts liegen. Die ganze linke Hälfte fällt weg. -- Ist sie größer, fällt die rechte Hälfte weg. - -Mit dem Rest machst du dasselbe noch einmal. Bei jedem Vergleich halbiert sich der Stapel. - -::: grafik ss-binaere-suche.svg -Drei Vergleiche statt vierzehn: Mit jeder Mitte fällt die Hälfte der übrigen Karten weg. -::: - -Im Struktogramm merken sich zwei Variablen den Bereich, in dem noch gesucht wird: \`links\` und \`rechts\`. Die Mitte ist \`(links + rechts) / 2\`. Weil das eine Ganzzahldivision ist, kommt immer ein ganzer Index heraus. - -::: diagramm ss-binaere-suche.visustruct -Dasselbe Array wie bei der linearen Suche. Suche in der Simulation wieder die 27 und vergleiche die Ausgabe Vergleiche mit der linearen Suche. -::: - -> Die binäre Suche funktioniert **nur bei sortierten Werten**. Bei durcheinander liegenden Karten weißt du nach dem Blick auf die Mitte nicht, in welcher Hälfte du weitersuchen musst. - -::: angeberwissen -Ich denke mir eine Zahl von 1 bis 100, du darfst raten und ich sage nur „größer“ oder „kleiner“. Mit der binären Suche brauchst du höchstens **7** Versuche. Bei einer Zahl bis eine Million sind es höchstens 20, bei einer Milliarde 30. Jede Verdopplung kostet nur einen einzigen Versuch mehr. So suchen auch Datenbanken in Millionen von Einträgen in Sekundenbruchteilen. Ich suche übrigens linear: Ich fresse die Wiese von links nach rechts ab. -::: - -## Wie schnell ist das? - -Wie gut ein Verfahren ist, misst man an der Zahl der Vergleiche, die es **im schlimmsten Fall** braucht. Bei der linearen Suche ist das der Fall, dass die Karte ganz hinten liegt oder fehlt. - -::: grafik ss-vergleiche.svg -Bei 100 Karten braucht die lineare Suche bis zu 100 Vergleiche, die binäre höchstens 7. -::: - -| Anzahl Karten | Lineare Suche | Binäre Suche | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1 000 | 1 000 | 10 | -| 1 000 000 | 1 000 000 | 20 | - -Die lineare Suche wächst im gleichen Maß wie die Kartenzahl. Bei der binären Suche kommt bei doppelt so vielen Karten nur **ein** Vergleich dazu. - -## Sortieren mit Bubblesort - -Für die binäre Suche müssen die Werte sortiert sein. Eines der einfachsten Sortierverfahren ist **Bubblesort**. Du gehst die Reihe von links nach rechts durch und vergleichst immer **zwei Nachbarn**. Steht die größere Karte links, tauschst du die beiden. - -::: grafik ss-bubblesort.svg -Nach einem Durchgang steht die größte Karte ganz rechts. -::: - -Nach dem ersten Durchgang steht die größte Karte ganz rechts, wie eine Luftblase, die nach oben steigt. Daher kommt der Name. Danach wiederholst du den Durchgang für die übrigen Karten. Bei 5 Karten brauchst du 4 Durchgänge. - -Im Struktogramm stecken zwei For-Schleifen ineinander. Die äußere zählt die Durchgänge, die innere vergleicht die Nachbarn \`karten[i]\` und \`karten[i + 1]\`. Das Tauschen kennst du schon: Es braucht eine **Hilfsvariable**. - -::: diagramm ss-bubblesort.visustruct -Die innere Schleife wird mit jedem Durchgang kürzer, weil rechts schon die größten Karten richtig stehen. Gehe es in der Simulation mit Schritt durch und beobachte das Array unter Variablen. -::: - -Bubblesort ist leicht zu verstehen, aber langsam. Bei n Karten braucht es bis zu n · (n − 1) / 2 Vergleiche: bei 10 Karten 45, bei 100 Karten schon 4 950 und bei 1 000 Karten fast eine halbe Million. Schnellere Verfahren wie Quicksort lernst du vielleicht später kennen. - -## Übung: Absteigend sortieren - -::: übung ss-absteigend -titel: Absteigend sortieren -auftrag: Das Startdiagramm liest 5 Karten ein und sortiert sie mit Bubblesort **aufsteigend**, also von klein nach groß. Ändere es so, dass die Karten **absteigend** ausgegeben werden, die größte zuerst. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Schau dir die Bedingung in der Verzweigung an. Sie entscheidet, wann zwei Nachbarn getauscht werden. -tipp: Du musst nur ein einziges Zeichen ändern. Wann sollen zwei Nachbarn getauscht werden, wenn die größere Karte nach links soll? -tipp: Ändere in der Verzweigung \`karten[i] > karten[i + 1]\` in \`karten[i] < karten[i + 1]\`. Dann wird getauscht, wenn rechts die größere Karte liegt, und die große Karte wandert nach links. -lösung: ss-absteigend-loesung.visustruct -::: - -## Jetzt in Java - -Die lineare Suche aus der Übung sieht in Java so aus: - -\`\`\`java editor -int[] karten = {8, 4, 11, 4, 6}; -int gesucht = 4; -int position = -1; -int i = 0; -while (position == -1 && i < karten.length) { - if (karten[i] == gesucht) { - position = i; - } - i = i + 1; -} -System.out.println("Position: " + position); -\`\`\` - -- \`&&\` heißt „und“: Die Schleife läuft nur, solange noch nichts gefunden ist **und** es noch Karten gibt. -- Ist der false-Zweig leer, lässt du \`else\` einfach weg. -- Der Tausch beim Bubblesort braucht auch in Java eine Hilfsvariable: \`int hilf = karten[i];\` - -::: übung l19-java-zaehlen -titel: Karten zählen -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Schreibe in Java ein Programm, das nach einer Karte fragt und zählt, wie oft sie vorkommt. Es gibt **Anzahl:** und die Zahl aus. - Bei 4 erscheint **Anzahl: 2**. -test: 4 -> Anzahl: 2 -test: 8 -> Anzahl: 1 -test: 9 -> Anzahl: 0 -braucht: forSchleife, verzweigung -tipp: Anders als bei der Suche hörst du beim ersten Treffer nicht auf. Eine For-Schleife geht alle Karten durch. -tipp: \`for (int i = 0; i < karten.length; i++)\` und darin \`if (karten[i] == gesucht) { anzahl = anzahl + 1; }\`. \`anzahl\` startet vor der Schleife mit 0. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Lückentext: Bubblesort - -::: lückentext -titel: Karten sortieren -auftrag: Das Programm sortiert die Karten mit Bubblesort aufsteigend. Ergänze die fehlenden Wörter und Zeichen. -\`\`\`java -int[] karten = {5, 2, 8, 1}; -for (int runde = 0; runde < karten.[[length]] - 1; runde++) { - for (int i = 0; i < karten.length - 1; i++) { - if (karten[i] [[>]] karten[i + 1]) { - [[int]] hilf = karten[i]; - karten[i] = karten[i + 1]; - karten[i + 1] = [[hilf]]; - } - } -} -for (int i = 0; i < karten.length; i++) { - System.out.println(karten[i]); -} -\`\`\` -erklärung: Ist eine Karte größer als ihre rechte Nachbarin, werden die beiden getauscht, mit einer Hilfsvariablen wie beim Gläsertauschen. Nach \`length - 1\` Runden ist alles sortiert: 1, 2, 5, 8. -::: - -## Fehler finden: Suche - -::: übung l19-java-fehler -titel: Lineare Suche reparieren -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Das Programm soll die **erste** Position der gesuchten Karte ausgeben, bei 4 also **Position: 1**. Es gibt aber **Position: 3** aus. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Finde den Fehler, repariere ihn und klicke auf Prüfen. -start: l19-java-fehler-start.visustruct -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 9 -> Position: -1 -test: 8 -> Position: 0 -braucht: whileSchleife, verzweigung -tipp: Die Schleife läuft immer bis zur letzten Karte. Was passiert, wenn die 4 ein zweites Mal kommt? -tipp: Die Schleife soll aufhören, sobald etwas gefunden ist: \`while (position == -1 && i < karten.length)\`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Warum läuft das nicht? - -::: übung l19-java-laeuft-nicht -titel: Karte suchen -code: java -auftrag: Die Karten {8, 4, 11, 4, 6} liegen in einem Array. Das Programm sucht die 11 und soll **Position: 2** ausgeben. Es läuft aber nicht. - „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Position: 2 -braucht: forSchleife, verzweigung -tipp: Vergleiche die Namen in der Bedingung mit den Namen, die oben angelegt werden. -tipp: Die gesuchte Karte steht in \`gesucht\` mit kleinem g: \`if (karten[i] == gesucht) {\` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Spiel: Suchprofi - -::: spiel frage -titel: Spiel: Suchprofi -intro: Ludwig sucht und sortiert Karten. Wie schnell geht das, und was kommt heraus? Sieben Runden. - -runde: - int[] karten = {8, 4, 11, 4, 6}; - int gesucht = 4; - int position = -1; - int i = 0; - while (position == -1 && i < karten.length) { - if (karten[i] == gesucht) { - position = i; - } - i = i + 1; - } - System.out.println("Position: " + position); -frage: Was wird ausgegeben? -- [x] Position: 1 -- Position: 3 -- Position: 2 -> Die Suche hört bei der ersten 4 auf. Sie liegt im Fach 1, gezählt wird ab 0. - -runde: - int[] karten = {8, 4, 11, 4, 6}; - int gesucht = 7; -frage: Mit derselben linearen Suche: Was steht danach in position? -- 0 -- 5 -- [x] -1 -> Die 7 ist nicht dabei. position bleibt bei -1, das bedeutet „nicht gefunden“. - -runde: -frage: Bei welchen Karten funktioniert die binäre Suche? -- bei allen -- [x] nur bei sortierten -- nur bei weniger als 100 -> Nur wenn die Karten sortiert sind, weißt du nach dem Blick auf die Mitte, in welcher Hälfte du weitersuchen musst. - -runde: -frage: Wie viele Vergleiche braucht die binäre Suche bei 100 Karten höchstens? -- 50 -- [x] 7 -- 100 -> Jeder Vergleich halbiert den Stapel. 7-mal halbieren reicht bis 128 Karten, also auch für 100. - -runde: -frage: Statt 1 000 Karten sind es jetzt 2 000. Wie viele Vergleiche braucht die binäre Suche höchstens mehr? -- [x] einen -- doppelt so viele -- 1 000 -> Der erste Vergleich halbiert 2 000 auf 1 000. Danach geht es weiter wie vorher. - -runde: - int[] karten = {5, 3, 8, 1}; -frage: Ein Durchgang Bubblesort vergleicht von links nach rechts immer zwei Nachbarn. Wie liegen die Karten danach? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 und 3 tauschen, 5 und 8 bleiben, 8 und 1 tauschen. Die größte Karte ist ganz rechts. - -runde: -frage: Bubblesort tauscht \`karten[i]\` und \`karten[i + 1]\`. Was braucht es dafür? -- [x] eine Hilfsvariable -- ein zweites Array -- eine binäre Suche -> Wie beim Tauschen zweier Variablen: \`int hilf = karten[i];\`, sonst geht ein Wert verloren. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, wie die lineare Suche ein Array durchsucht. → Eine Karte suchen -- Ich kann eine lineare Suche als Struktogramm bauen. → Übung: Karte suchen -- Ich kann erklären, warum die binäre Suche nur bei sortierten Werten funktioniert. → Binäre Suche: sortiert geht es schneller -- Ich kann begründen, warum die binäre Suche bei vielen Werten viel schneller ist. → Wie schnell ist das? -- Ich kann einen Durchgang von Bubblesort mit Spielkarten vormachen. → Sortieren mit Bubblesort -- Ich kann in Java ein Array durchsuchen und Treffer zählen. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? Welche Voraussetzung braucht die binäre Suche? -→ Binäre Suche: sortiert geht es schneller -- [ ] Das Array muss genau 100 Fächer haben. -- [x] Die Werte müssen sortiert sein. -- [ ] Der gesuchte Wert muss vorkommen. -> Nur bei sortierten Werten weißt du nach dem Blick auf die Mitte, in welcher Hälfte du weitersuchst. - -? Ein sortiertes Array hat 1 000 Werte. Wie viele Vergleiche braucht die binäre Suche höchstens? -→ Wie schnell ist das? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1 000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: Nach etwa 10 Halbierungen bleibt ein Wert übrig. - -? Was vergleicht Bubblesort in jedem Schritt? -→ Sortieren mit Bubblesort -- [ ] Die erste und die letzte Karte -- [x] Zwei benachbarte Karten -- [ ] Jede Karte mit der Mitte -> Steht die größere Karte links, werden die Nachbarn getauscht. - -? Wo steht nach dem ersten Durchgang von Bubblesort die größte Karte? -→ Sortieren mit Bubblesort -- [ ] Ganz links -- [ ] In der Mitte -- [x] Ganz rechts -> Sie wird bei jedem Vergleich weitergetauscht und steigt wie eine Blase nach rechts. - -? Was bedeutet \`position = -1\` in der linearen Suche? -→ Eine Karte suchen -- [x] Die Karte wurde (noch) nicht gefunden. -- [ ] Die Karte liegt ganz hinten. -- [ ] Das Array ist leer. - -? Ordne jedem Verfahren die passende Beschreibung zu. -→ Eine Karte suchen -= Lineare Suche -> eine Karte nach der anderen ansehen -= Binäre Suche -> die Mitte ansehen und eine Hälfte weglassen -= Bubblesort -> Nachbarn vergleichen und tauschen - -? Warum nimmst du zum Zählen eine For-Schleife und keine Suche, die beim ersten Treffer aufhört? -→ Jetzt in Java -- [ ] Eine For-Schleife ist immer schneller. -- [x] Du musst jede Karte ansehen, sonst fehlen spätere Treffer. -- [ ] Eine While-Schleife kann keine Arrays durchlaufen. -> Die Karte kann mehrmals vorkommen. Darum gehst du alle Karten bis zum Ende durch. -::: -`,ow=`--- -titel: Struktogramm und Code -bereich: Vom Struktogramm zum Code -kurz: Alle Elemente in Java auf einen Blick, Code erzeugen, im Bereich Code Java schreiben, ausführen und Fehler suchen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Code-Ansicht öffnen und Java als Sprache wählen -- Ausgabe, Eingabe, Verzweigung und Schleifen in Java erkennen -- den Unterschied zwischen Snippet und Class erklären -- erklären, wie Java-Code mit Compiler, Bytecode und JVM auf jedem Betriebssystem läuft -- JDK und JRE unterscheiden -- erklären, warum man ein C++-Programm für jedes Betriebssystem neu kompilieren muss, ein Java-Programm aber nicht -- ein kurzes Java-Programm als Struktogramm nachbauen -- im Bereich Code Java tippen und das entstehende Struktogramm lesen -- ein Programm mit ▶︎ ausführen und Ein- und Ausgaben in der Konsole nutzen -- an der roten Zeilennummer einen Syntaxfehler oder Laufzeitfehler finden -- Haltepunkte setzen und ein Programm im Einzelschritt verfolgen -::: - -## Die Code-Ansicht - -In den Lektionen 4 bis 19 hast du jedes Element schon einmal in Java geschrieben. Hier siehst du alles auf einen Blick und lernst, wie VisuStruct aus einem ganzen Struktogramm ein Java-Programm macht. - -Ein Struktogramm beschreibt einen Ablauf. Damit ein Computer ihn ausführt, brauchst du ein Programm in einer Programmiersprache. VisuStruct schreibt diesen Code für dich. - -Klicke bei den Werkzeugen auf [Code](#zeige-code) (Symbol mit spitzen Klammern). Rechts erscheint der **Code-Generator**: - -- Oben wählst du die Sprache: **Java**, **Python** oder **JavaScript**. -- Bei Java wählst du zusätzlich **Snippet** (nur die Anweisungen) oder **Class** (eine vollständige Java-Datei). -- **Diagramm-Text als Kommentare** schreibt den Text jedes Kastens als Kommentar über den Code. -- **Kopieren** legt den Code in die Zwischenablage, **Java speichern …** speichert eine \`.java\`-Datei. - -Der Code ändert sich sofort mit, wenn du am Struktogramm etwas änderst. - -::: angeberwissen -Nassi hat später an der Programmiersprache Ada mitgearbeitet und war bei Apple für Software zuständig. Wer Struktogramme erfindet, landet also nicht im Ziegenstall, sondern bei Apple. -::: - -::: diagramm l17-beispiel.visustruct -Ein kleines Programm mit Eingabe, For-Schleife und Verzweigung. Öffne es und schau dir den Java-Code dazu an. -::: - -## So sieht das Beispiel in Java aus - -Für das Diagramm oben erzeugt VisuStruct als **Snippet** diesen Code: - -\`\`\` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Welche Reihe?"); -int zahl = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int ergebnis = zahl * i; - System.out.println(i + " mal " + zahl + " = " + ergebnis); -} -if (zahl > 10) { - System.out.println("Das war schwer!"); -} else { - System.out.println("Das war leicht."); -} -\`\`\` - -Jede Anweisung endet in Java mit einem **Semikolon** \`;\`. Was in einem Kasten steht, steht in Java in **geschweiften Klammern** \`{ }\`. Die Einrückung zeigt dir, was zusammengehört. - -## Die Elemente in Java - -| Element | Java | Lektion | -|---|---|---| -| Anweisung | \`int summe = 0;\` | 4, 5 | -| Ausgabe | \`System.out.println(summe);\` | 6 | -| Eingabe | \`int zahl = scanner.nextInt();\` | 6 | -| Verzweigung | \`if (bedingung) { … } else { … }\` | 8, 9 | -| Fallauswahl | \`switch (wert) { case 1 -> { … } default -> { … } }\` | 10 | -| While-Schleife | \`while (bedingung) { … }\` | 11 | -| Do-while-Schleife | \`do { … } while (bedingung);\` | 12 | -| For-Schleife | \`for (int i = 0; i < 5; i++) { … }\` | 13 | -| Endlosschleife | \`while (true) { … }\` | 11, 18 | -| Aussprung | \`break;\` | 18 | -| Aufruf | \`berechne();\` | 18 | -| Array | \`int[] zahlen = new int[5];\` | 15, 16 | -| Text (String) | \`String name = scanner.next();\` | 17 | - -Für Eingaben steht ganz oben einmal \`import java.util.Scanner;\` und \`Scanner scanner = new Scanner(System.in);\`. Vor jeder Eingabe gibt VisuStruct die Frage mit \`System.out.println(…)\` aus. - -Ist der Nein-Zweig einer Verzweigung leer, erzeugt VisuStruct trotzdem einen \`else\`-Teil, in dem nur der Kommentar \`// empty element\` steht. Wenn du **Leeren else-Zweig im Code ausgeben** ausschaltest, fällt der leere \`else\`-Teil weg. - -## Snippet oder Class - -Ein **Snippet** kannst du in ein vorhandenes Java-Programm in die \`main\`-Methode einfügen. Mit **Class** bekommst du eine vollständige Datei, die du sofort übersetzen und starten kannst: - -\`\`\` -import java.util.Scanner; - -public class Einmaleins { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Welche Reihe?"); - int zahl = scanner.nextInt(); - ... - } // Ende main -} // Ende Klasse -\`\`\` - -Die Kommentare \`// Ende main\` und \`// Ende Klasse\` hinter den letzten beiden Klammern sind nicht nötig, aber sehr hilfreich: So siehst du sofort, welche Klammer was schließt, und vergisst keine. - -Der Name der Klasse kommt aus dem **Titel** des Diagramms. Die Datei muss genauso heißen, hier also \`Einmaleins.java\`. Das erledigt **Java speichern …** für dich. - -> Der erzeugte Code ist ein guter Anfang, aber prüfe ihn. Legst du eine Variable zuerst an und liest sie später mit einer Eingabe neu ein, schreibt VisuStruct trotzdem \`int pin = scanner.nextInt();\`. Java meldet dann, dass es \`pin\` schon gibt. Lösche in dieser Zeile einfach das \`int\`. - -## Wie Java auf jedem Computer läuft - -Deine Datei \`Einmaleins.java\` ist **Quellcode**: Text für Menschen. Der Prozessor deines Computers versteht ihn nicht direkt. Deshalb läuft Java in zwei Schritten: - -1. Der **Compiler** \`javac\` übersetzt den Quellcode in **Bytecode**. Heraus kommt die Datei \`Einmaleins.class\`. -2. Die **JVM** (Java Virtual Machine) liest den Bytecode und übersetzt ihn beim Ausführen in **Maschinencode**, also in Befehle für den Prozessor deines Computers. - -::: grafik l20-jvm.svg -Einmal kompilieren: Derselbe Bytecode läuft auf jeder JVM. -::: - -Den Bytecode gibt es nur einmal. Die JVM gibt es dagegen für Windows, macOS, Linux und viele andere Systeme. Deshalb läuft dieselbe \`.class\`-Datei auf jedem Computer, auf dem eine JVM installiert ist. Das nennt man **plattformunabhängig**. Der Werbespruch dazu heißt **„Write once, run anywhere“**: einmal schreiben, überall ausführen. - -::: angeberwissen -Einen der ersten Compiler hat 1952 **Grace Hopper** gebaut, die du schon aus Lektion 7 kennst. Viele sagten ihr damals, Computer könnten nur rechnen und keine Programme übersetzen. Sie hat es trotzdem gemacht. -::: - -Zum Vergleich **C++**: Dort übersetzt der Compiler den Quellcode direkt in Maschinencode für genau einen Prozessor und ein Betriebssystem. Das fertige Programm läuft nur dort. Für Windows, macOS und Linux musst du es jeweils neu kompilieren. - -::: grafik l20-cpp.svg -C++ wird für jedes System einzeln zu Maschinencode kompiliert. -::: - -| | C++ | Java | -|---|---|---| -| Der Compiler erzeugt | Maschinencode | Bytecode | -| Wie oft kompilieren? | für jedes System neu | einmal | -| Was braucht der Computer zum Ausführen? | nichts weiter | eine JVM | - -Ist Java dadurch langsamer? Kaum. Programmteile, die oft laufen, zum Beispiel Schleifen, übersetzt der **JIT-Compiler** der JVM („just in time“, gerade rechtzeitig) einmal komplett in Maschinencode. Danach laufen sie fast so schnell wie C++. - -> Stell dir die JVM wie einen kleinen Computer aus Software vor, fast wie ein eigenes Betriebssystem nur für Java-Programme. Genau genommen ist sie aber kein Betriebssystem, sondern eine **virtuelle Maschine**: ein Programm, das selbst auf Windows, macOS oder Linux läuft und darin einen gedachten Computer nachspielt. - -Beim Installieren begegnen dir zwei Abkürzungen: - -| Abkürzung | Was steckt drin? | Wofür? | -|---|---|---| -| **JRE** (Java Runtime Environment) | die JVM und fertige Bausteine wie \`Scanner\` | Java-Programme ausführen | -| **JDK** (Java Development Kit) | die JRE und dazu den Compiler \`javac\` und weitere Werkzeuge | Java-Programme schreiben und übersetzen | - -Zum Programmieren brauchst du also das JDK. Heute lädt man meist nur noch das JDK herunter, die JRE steckt darin. Für VisuStruct im Browser musst du nichts davon installieren. - -::: angeberwissen -Mit einem JDK kannst du alles auch selbst in der Konsole machen, also im Terminal oder in der Eingabeaufforderung: - -\`\`\` -java -version -javac Einmaleins.java -java Einmaleins -\`\`\` - -- \`java -version\` zeigt, welche Java-Version installiert ist. Kommt eine Fehlermeldung, fehlt Java noch. -- \`javac Einmaleins.java\` startet den Compiler. Danach hast du zwei Dateien im Ordner: \`Einmaleins.java\` mit deinem Quellcode und neu \`Einmaleins.class\` mit dem Bytecode. -- \`java Einmaleins\` startet die JVM mit deinem Bytecode. Den Klassennamen schreibst du hier ohne \`.class\`. - -Klappt \`java -version\`, aber \`javac -version\` nicht, ist nur eine JRE installiert. Dann fehlt dir das JDK. -::: - -::: angeberwissen -Jede übersetzte Java-Datei (\`.class\`) beginnt mit denselben vier Bytes, in Hexadezimal geschrieben: \`CA FE BA BE\`. Daran erkennt der Computer, dass es Java ist. Das „Kaffee-Baby“ hat sich James Gosling ausgedacht, passend zum Kaffee im Namen. -::: - -## Der Java-Sprachmodus - -Unter dem [Zahnrad](#zeige-einstellungen) oben rechts kannst du **Code-Keywords** einschalten. Dann heißen die Bausteine im Bereich Elemente so wie die Befehle in Java: \`if\`, \`switch\`, \`for\`, \`while\`, \`do\`, \`while(true)\`, \`break\` und \`call\`. Die übrige Oberfläche bleibt in deiner Sprache. - -Das hilft dir, die Kästen und die Java-Befehle einander zuzuordnen. Dein Diagramm selbst bleibt dabei unverändert. - -## Java direkt schreiben - -Bisher hast du zuerst das Struktogramm gezeichnet und dann den Code angesehen. Im Bereich [Code](#zeige-codelive) geht es auch andersherum. Klicke rechts auf die schmale Leiste **Code**. Neben dem Struktogramm erscheint eine Spalte, in die du Java selbst tippst. - -- **Struktogramm zu Code:** Änderst du etwas im Struktogramm, ändert sich der Java-Code sofort mit. -- **Code zu Struktogramm:** Tippst du Java, baut VisuStruct beim Tippen das Struktogramm dazu. Fehlt noch etwas, zum Beispiel eine schließende Klammer \`}\`, steht unter dem Code ein Hinweis. -- **Vorschläge:** Beim Tippen schlägt VisuStruct Vorlagen vor, etwa für \`if\`, \`for\` oder \`while\`. Mit der Eingabetaste übernimmst du eine Vorlage. Grau geschriebene Platzhalter ersetzt du einfach durch deinen Text. -- **Einrücken:** Ist dein Code durcheinandergeraten, klicke oben im Bereich Code auf den Knopf **Einrücken**. Jede Ebene steht dann vier Leerzeichen tiefer, wie in Eclipse oder IntelliJ. Hast du Zeilen markiert, rückt VisuStruct nur diese ein. - -So kannst du beide Wege ausprobieren. Erst das Struktogramm zeichnen ist gut, um einen Ablauf zu planen. Direkt Code schreiben ist schneller, wenn du schon weißt, was du willst. Das Struktogramm hilft dir dann, deinen Code zu kontrollieren. - -## Das Programm ausführen - -Oben im Bereich Code startet [▶︎ Ausführen](#zeige-ausfuehren) dein Programm, **■ Stopp** bricht es ab, zum Beispiel bei einer Endlosschleife. Was dein Programm ausgibt, erscheint unten in der [Konsole](#zeige-konsole). Fragt es nach einer Eingabe, tippst du sie dort ein und drückst die Eingabetaste. - -Die Grenze zwischen Code und Konsole kannst du mit der Maus nach oben oder unten ziehen. - -Geht etwas schief, wird die Zeilennummer rot. Bei einem Syntaxfehler ist die Stelle zusätzlich rot unterstrichen, und es steht z. B. **Zeile 2: Hier fehlt noch: ;** darunter. Dann startet ▶︎ das Programm gar nicht erst. Bricht das Programm beim Ausführen ab, ist es ein Laufzeitfehler, und die Konsole nennt die Zeile, z. B. **Zeile 3: Division durch null.** Die drei Fehlerarten kennst du aus der Lektion „Simulation und Schreibtischtest“. - -Weiß VisuStruct genau, was fehlt, steht am Ende der Zeile eine 💡. Fährst du mit der Maus darauf, siehst du den Vorschlag, z. B. **Vorschlag: import java.util.Scanner; einfügen**. Ein Klick übernimmt ihn. Lies trotzdem nach, was eingefügt wurde, damit du es beim nächsten Mal selbst weißt. - -> Der Bereich Code versteht die Java-Befehle, die es auch als Kasten im Struktogramm gibt. Eigene Klassen und Objekte kennt es noch nicht. Fenster mit Swing gehen trotzdem: Schalte dafür im Zahnrad **Echtes Java (mit Swing)** ein (Lektion „Dein erstes Fenster“). - -::: angeberwissen -Profis nennen ein Programm, in dem man Code schreibt, ausführt und Fehler sucht, eine **IDE** (Integrated Development Environment). Mit dem Bereich Code ist VisuStruct eine kleine IDE mit eingebautem Struktogramm. -::: - -## Fehler suchen mit dem Debugger - -Manchmal läuft ein Programm, rechnet aber falsch. Das ist ein Logikfehler. Dann hilft der **Debugger**: Er führt dein Programm langsam aus, und du schaust ihm dabei zu. - -- **Haltepunkt:** Klicke links auf eine Zeilennummer. Ein roter Punkt erscheint. [▶︎ Ausführen](#zeige-ausfuehren) hält vor dieser Zeile an. Ein zweiter Klick nimmt den Punkt wieder weg. -- **Gelbe Zeile:** Sie wird als Nächstes ausgeführt. Im Struktogramm ist der passende Kasten markiert. -- **Variablen:** Unter dem Code steht, welchen Wert jede Variable gerade hat. Was sich seit dem letzten Anhalten geändert hat, leuchtet kurz gelb. -- **Einzelschritt:** Der Knopf [Einzelschritt](#zeige-einzelschritt) neben ▶︎ führt genau eine Zeile aus und hält wieder an. Ohne Haltepunkt startet er das Programm und hält vor der ersten Zeile. -- **Weiter:** ▶︎ läuft bis zum nächsten Haltepunkt oder bis zum Ende. **■ Stopp** bricht ab. - -Probier es mit diesem Programm aus. Setze einen Haltepunkt in Zeile 3 und drücke mehrmals ▶︎. Wie wachsen \`i\` und \`summe\`? - -\`\`\` -int summe = 0; -for (int i = 1; i <= 4; i++) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -\`\`\` - -::: ludwig -Das ist ein Schreibtischtest, nur dass der Computer für dich mitschreibt. Überleg dir vor jedem Schritt, welchen Wert die Variable gleich haben müsste. Stimmt die Tabelle nicht mit deiner Vermutung überein, hast du die Stelle mit dem Fehler gefunden. -::: - -## Übung: Erst Code, dann Struktogramm - -Diesmal zeichnest du nicht. Öffne den Bereich Code und tippe diesen Java-Code ab: - -\`\`\` -System.out.println("Startzahl?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -\`\`\` - -::: übung l20-countdown -titel: Countdown -bereich: beide -auftrag: Tippe den Java-Code oben im Bereich Code ab. Führe ihn mit ▶︎ aus und gib in der Konsole eine Startzahl ein. - Sieh dir dann das Struktogramm an, das dabei entstanden ist, und klicke auf Prüfen. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Öffne den Bereich Code mit der schmalen Leiste ganz rechts und tippe einfach los. -tipp: Achte auf das Semikolon am Ende jeder Anweisung und auf die geschweiften Klammern der While-Schleife. Unter dem Code steht ein Hinweis, wenn noch etwas fehlt. -lösung: l20-countdown-loesung.visustruct -::: - -## Übung: Java als Struktogramm - -Jetzt andersherum: Hier ist ein Stück Java-Code. Baue es als Struktogramm nach. - -\`\`\` -System.out.println("Zahl eingeben"); -int n = scanner.nextInt(); -int fakultaet = 1; -for (int i = 1; i <= n; i++) { - fakultaet = fakultaet * i; -} -System.out.println(fakultaet); -\`\`\` - -::: übung l17-fakultaet -titel: Fakultät aus Java -bereich: beide -auftrag: Baue den Java-Code oben als Struktogramm nach. Das Programm liest eine Zahl n ein und gibt das Produkt 1 · 2 · … · n aus. - Kontrolliere am Ende in der Code-Ansicht, ob dein Java-Code genauso aussieht. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Lies den Java-Code Zeile für Zeile und überlege, welcher Kasten zu jeder Zeile passt. -tipp: Die beiden ersten Java-Zeilen sind zusammen eine einzige Eingabe: \`input: int n "Zahl eingeben"\`. Die For-Schleife hat die drei Felder \`int i = 1\`, \`i <= n\` und \`i++\`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Übung: else if - -In Java schreibt man mehrere Fälle oft als \`else if\`: - -\`\`\` -if (alter < 6) { - System.out.println("Eintritt frei"); -} else if (alter < 18) { - System.out.println("Kinderticket: 4 Euro"); -} else { - System.out.println("Erwachsenenticket: 8 Euro"); -} -\`\`\` - -Im Struktogramm gibt es kein \`else if\`. Dort steckt eine zweite Verzweigung im Nein-Zweig der ersten. Steht im Nein-Zweig nur diese eine Verzweigung, macht VisuStruct beim Erzeugen des Codes daraus wieder \`else if\`, in Python \`elif\`. - -::: übung l17-ticket -titel: Ticketpreis -bereich: beide -auftrag: Frage nach dem Alter als ganze Zahl und baue den Java-Code oben als Struktogramm nach. -test: 5 -> Eintritt frei -test: 6 -> Kinderticket: 4 Euro -test: 17 -> Kinderticket: 4 Euro -test: 18 -> Erwachsenenticket: 8 Euro -braucht: verzweigung -tipp: Im Java-Code steckt in einem \`else\` ein weiteres \`if\`. Im Struktogramm ist das eine Verzweigung in einem Zweig. -tipp: Ziehe in den false-Zweig der ersten Verzweigung eine zweite Verzweigung mit der Bedingung \`alter < 18\`. -lösung: l17-ticket-loesung.visustruct -::: - -## Spiel: Welcher Kasten? - -::: spiel frage -titel: Spiel: Welcher Kasten? -intro: Ludwig zeigt dir Java. Welcher Kasten aus dem Struktogramm steckt dahinter? Acht Runden. -frage: Welcher Kasten ist das? - -runde: - do { - zahl = zahl * 2; - } while (zahl < 100); -- While-Schleife -- [x] Do-while-Schleife -- For-Schleife -> Die Bedingung steht unten. Der Rumpf läuft also mindestens einmal. - -runde: - switch (wahl) { - case 1 -> { System.out.println("Wasser"); } - default -> { System.out.println("Gibt es nicht."); } - } -- Verzweigung -- [x] Fallauswahl -- Aufruf -> Eine Variable wird mit festen Werten verglichen: Das ist die Fallauswahl. - -runde: - break; -- Aufruf -- Endlosschleife -- [x] Aussprung -> \`break\` verlässt sofort die Schleife, in der es steht. - -runde: - berechne(); -- [x] Aufruf -- Ausgabe -- Anweisung -> Name mit runden Klammern: Hier wird ein Unterprogramm aufgerufen. - -runde: - int zahl = scanner.nextInt(); -- Ausgabe -- [x] Eingabe -- Aufruf -> Der Scanner liest einen Wert ein. Das ist eine Eingabe. - -runde: - while (true) { - System.out.println("Hallo"); - } -- While-Schleife mit Bedingung -- [x] Endlosschleife -- Do-while-Schleife -> \`while (true)\` hat keine echte Bedingung. Nur ein break könnte sie beenden. - -runde: -frage: Du willst eine vollständige Java-Datei, die du sofort übersetzen und starten kannst. Was wählst du in der Code-Ansicht? -- Snippet -- [x] Class -> Ein Snippet sind nur die Anweisungen für die \`main\`-Methode. Class erzeugt die ganze Datei. - -runde: -frage: Im Debugger ist eine Zeile gelb. Was bedeutet das? -- Sie hat einen Fehler. -- [x] Sie wird als Nächstes ausgeführt. -- Dort steht ein Haltepunkt. -> Gelb ist die nächste Zeile. Ein Haltepunkt ist ein roter Punkt an der Zeilennummer. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Code-Ansicht öffnen und Java als Sprache wählen. → Die Code-Ansicht -- Ich kann Ausgabe, Eingabe, Verzweigung und Schleifen in Java erkennen. → Die Elemente in Java -- Ich kann den Unterschied zwischen Snippet und Class erklären. → Snippet oder Class -- Ich kann erklären, wie Java-Code mit Compiler, Bytecode und JVM auf jedem Betriebssystem läuft. → Wie Java auf jedem Computer läuft -- Ich kann JDK und JRE unterscheiden. → Wie Java auf jedem Computer läuft -- Ich kann erklären, warum man ein C++-Programm für jedes Betriebssystem neu kompilieren muss, ein Java-Programm aber nicht. → Wie Java auf jedem Computer läuft -- Ich kann ein kurzes Java-Programm als Struktogramm nachbauen. → Übung: Java als Struktogramm -- Ich kann im Bereich Code Java tippen und das entstehende Struktogramm lesen. → Java direkt schreiben -- Ich kann ein Programm mit ▶︎ ausführen und Ein- und Ausgaben in der Konsole nutzen. → Das Programm ausführen -- Ich kann an der roten Zeilennummer einen Syntaxfehler oder Laufzeitfehler finden. → Das Programm ausführen -- Ich kann Haltepunkte setzen und ein Programm im Einzelschritt verfolgen. → Fehler suchen mit dem Debugger -::: - -## Wissenstest - -::: quiz -? Wie sieht \`output: "Hallo"\` in Java aus? -→ Die Elemente in Java -- [ ] print("Hallo") -- [x] System.out.println("Hallo"); -- [ ] console.log("Hallo"); -> print ist Python, console.log ist JavaScript. - -? Woher kommt der Klassenname bei Class? -→ Snippet oder Class -- [ ] Er heißt immer Main. -- [x] Aus dem Titel des Diagramms -- [ ] Aus dem ersten Kasten -> Ohne Titel heißt die Klasse Main. - -? Welcher Java-Code gehört zu einer Do-while-Schleife? -→ Die Elemente in Java -- [ ] while (bedingung) { … } -- [x] do { … } while (bedingung); -- [ ] for (;;) { … } -> Bei do-while steht die Bedingung am Ende, deshalb läuft der Rumpf mindestens einmal. - -? Was wird aus einem Aussprung im Java-Code? -→ Die Elemente in Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? Was passiert an einem roten Punkt vor der Zeilennummer? -→ Fehler suchen mit dem Debugger -- [ ] Die Zeile wird übersprungen. -- [x] Das Programm hält vor dieser Zeile an. -- [ ] Die Zeile enthält einen Fehler. -> Das ist ein Haltepunkt. Danach geht es mit ▶︎ oder dem Einzelschritt weiter. - -? Warum läuft dieselbe \`.class\`-Datei auf Windows, macOS und Linux? -→ Wie Java auf jedem Computer läuft -- [ ] Der Compiler erzeugt für jedes Betriebssystem eine eigene Datei. -- [x] Auf jedem Betriebssystem führt eine JVM denselben Bytecode aus. -- [ ] Java-Programme brauchen kein Betriebssystem. -> Der Bytecode bleibt gleich. Nur die JVM gibt es für jedes System extra. - -? Was brauchst du, um Java-Programme zu schreiben und zu übersetzen? -→ Wie Java auf jedem Computer läuft -- [ ] nur die JRE -- [x] das JDK -- [ ] nur die JVM -> Die JRE reicht zum Ausführen. Den Compiler \`javac\` gibt es nur im JDK. - -? Du willst dein Programm auf Windows und auf dem Mac nutzen. Was stimmt? -→ Wie Java auf jedem Computer läuft -- [ ] Java musst du für jedes System neu kompilieren, C++ nicht. -- [x] C++ musst du für jedes System neu kompilieren, Java nicht. -- [ ] Beide musst du nur einmal kompilieren. -> C++ wird direkt zu Maschinencode für ein System. Java wird zu Bytecode, und den führt auf jedem System die JVM aus. - -? Die Zeilennummer 3 im Bereich Code ist rot. Was heißt das? -→ Das Programm ausführen -- [ ] In Zeile 3 ist ein Haltepunkt. -- [x] In Zeile 3 steckt ein Fehler. -- [ ] Zeile 3 wird als Nächstes ausgeführt. -> Der Haltepunkt ist ein runder roter Punkt, die nächste Zeile ist gelb. Eine rote Zeilennummer zeigt einen Syntaxfehler oder einen Laufzeitfehler. -::: -`,lw=`--- -titel: Python und JavaScript -bereich: Vom Struktogramm zum Code -kurz: Python- und JavaScript-Code aus dem Struktogramm erzeugen und JavaScript direkt im Browser ausführen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Python und JavaScript in der Code-Ansicht auswählen und vergleichen -- erklären, dass Python Blöcke durch Einrückung bildet und keine Datentypen braucht -- erklären, was VisuStruct beim Übersetzen nach Python anpasst -- JavaScript mit Im Browser testen ausführen -::: - -## Dasselbe Diagramm, drei Sprachen - -In der Code-Ansicht ([Code](#zeige-code) bei den Werkzeugen) wählst du oben die Sprache. Das Struktogramm bleibt gleich, nur die Schreibweise ändert sich. So kannst du die Sprachen gut vergleichen. - -::: diagramm l18-beispiel.visustruct -Eine Zahl wird so lange um 3 verkleinert, bis sie nicht mehr größer als 0 ist. -::: - -## Python - -Für das Diagramm oben erzeugt VisuStruct diesen Python-Code: - -\`\`\` -zahl = int(input("Startwert?")) -while zahl > 0: - print(zahl) - zahl = zahl - 3 -print("Ende") -\`\`\` - -Was in Python anders ist als in Java: - -- Es gibt **keine Semikolons** und **keine geschweiften Klammern**. Was zu einer Schleife oder Verzweigung gehört, erkennt Python allein an der **Einrückung**. Hinter \`while\`, \`if\` und \`else\` steht ein Doppelpunkt. -- Variablen bekommen **keinen Datentyp**: aus \`int zahl = 5\` wird \`zahl = 5\`. -- Die Ausgabe heißt \`print(…)\`, die Eingabe \`input(…)\`. Für eine Zahl steht \`int(…)\` darum herum. -- Arrays heißen in Python **Listen**: aus \`int[] zahlen = {2, 4, 6}\` wird \`zahlen = [2, 4, 6]\`, und aus \`zahlen.length\` wird \`len(zahlen)\`. -- Eine einfache For-Schleife wie \`int i = 0\`, \`i < 5\`, \`i++\` wird zu \`for i in range(5):\`. - -## Worauf du bei Python achten musst - -Python ist an einigen Stellen strenger als Java. VisuStruct übersetzt die Java-Schreibweise beim Export deshalb gleich mit: - -- **Text und Zahl mit +:** \`"Summe: " + summe\` wird zu \`"Summe: " + str(summe)\`. Ohne \`str(…)\` bricht Python ab. -- **i++ gibt es nicht:** daraus wird \`i += 1\`. -- **&&, || und !** heißen in Python \`and\`, \`or\` und \`not\`, \`true\` und \`false\` werden zu \`True\` und \`False\`. -- **Ganzzahlige Division:** In Python ergibt \`7 / 2\` immer 3.5. Teilt das Struktogramm zwei \`int\`-Werte, schreibt VisuStruct deshalb \`7 // 2\`, das ergibt wie in Java 3. -- **For-Schleifen:** Zählschleifen werden zu \`for … in range(…)\`, auch mit \`i <= n\`, Rückwärtszählen oder Schritten wie \`i = i + 2\`. Passt eine Schleife nicht dazu, wird daraus eine \`while\`-Schleife. -- **do-while gibt es nicht:** daraus wird \`while True:\` mit \`if not (…): break\` am Ende. - -Ist ein Diagramm als **Pseudocode** markiert, steht nur ein Hinweis über dem Code. Dann ist der Text eine Skizze und kein lauffähiges Programm. - -## JavaScript - -Dasselbe Diagramm in JavaScript: - -\`\`\` -"use strict"; - -let zahl = parseInt((prompt("Startwert?") ?? "0"), 10); -while (zahl > 0) { - console.log(zahl); - zahl = zahl - 3; -} -console.log("Ende"); -\`\`\` - -JavaScript sieht Java ähnlich: geschweifte Klammern, Semikolons, \`while (…)\` und \`if (…)\`. Die Unterschiede: - -- Variablen werden mit \`let\` angelegt, ohne Datentyp. Konstanten mit \`const\`. -- Die Ausgabe heißt \`console.log(…)\`, die Eingabe \`prompt(…)\`. Mit \`parseInt(…)\` wird aus der Eingabe eine ganze Zahl. -- Arrays stehen in eckigen Klammern: \`let zahlen = [2, 4, 6];\`. -- \`"use strict";\` ganz oben sorgt dafür, dass JavaScript bei Tippfehlern in Variablennamen meckert. -- JavaScript kennt nur einen Zahlentyp. Teilt das Struktogramm zwei \`int\`-Werte, schreibt VisuStruct \`Math.trunc(a / b)\`. Dann fallen die Nachkommastellen wie in Java weg. - -::: angeberwissen -JavaScript hat mit Java fast nichts zu tun. Brendan Eich hat es 1995 in nur zehn Tagen für den Browser Netscape gebaut. Erst hieß es Mocha, dann LiveScript. Weil Java gerade alle begeisterte, wurde es kurz vor dem Start aus Werbegründen in JavaScript umbenannt. Seitdem werden die beiden ständig verwechselt, bis heute. Programmierer sagen dazu: Java und JavaScript haben so viel gemeinsam wie Auto und Autogramm. -::: - -## JavaScript im Browser ausführen - -JavaScript ist die Sprache der Browser. Deshalb kann VisuStruct den Code **sofort ausführen**: - -::: angeberwissen -Warum sind Links im Browser eigentlich blau? Ben Shneidermans Team hat 1985 untersucht, welche Farbe man gut sieht und die beim Lesen nicht stört. Gewonnen hat Hellblau. Rot fiel zwar mehr auf, aber man konnte sich den Text danach schlechter merken. -::: - -1. Wähle in der Code-Ansicht **JavaScript**. -2. Klicke auf **Im Browser testen**. -3. Unter **Ausgabe** erscheint, was \`console.log\` ausgibt. Fragt das Programm mit \`prompt\` nach einem Wert, tippst du ihn ein und klickst auf **OK**. -4. Mit **Stopp** brichst du das Programm ab. - -Eine Endlosschleife \`while (true)\` hält VisuStruct nach 100 000 Durchläufen von selbst an und meldet **Ausführung gestoppt: mögliche Endlosschleife.** - -> Ein **Aufruf** braucht im JavaScript-Code ein Unterprogramm mit diesem Namen, sonst meldet JavaScript einen Fehler. Anders als die Simulation sucht der Browser nicht in den anderen Reitern. - -## Übung: Python als Struktogramm - -Dieses Python-Programm gibt alle geraden Zahlen von 0 bis zu einer Grenze aus: - -\`\`\` -grenze = int(input("Bis wohin?")) -for i in range(0, grenze + 1, 2): - print(i) -\`\`\` - -\`range(0, grenze + 1, 2)\` heißt: bei 0 anfangen, vor \`grenze + 1\` aufhören, in Zweierschritten zählen. - -::: übung l18-python -titel: Gerade Zahlen -bereich: beide -auftrag: Baue das Python-Programm oben als Struktogramm mit einer For-Schleife nach. Bei der Grenze 7 gibt es 0, 2, 4 und 6 aus. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: \`range(0, grenze + 1, 2)\` zählt von 0 bis zur Grenze in Zweierschritten. Das ist eine For-Schleife. -tipp: Die drei Felder der For-Schleife sind \`int i = 0\`, \`i <= grenze\` und \`i = i + 2\`. -lösung: l18-python-loesung.visustruct -::: - -## Übung: JavaScript als Struktogramm - -\`\`\` -let alter = parseInt((prompt("Wie alt bist du?") ?? "0"), 10); -if (alter >= 16) { - console.log("Du darfst wählen."); -} else { - let jahre = 16 - alter; - console.log("Noch " + jahre + " Jahre warten."); -} -\`\`\` - -::: übung l18-js -titel: Wählen ab 16 -bereich: beide -auftrag: Baue den JavaScript-Code oben als Struktogramm nach. Teste danach in der Code-Ansicht mit **JavaScript** und **Im Browser testen**, ob dein Code dasselbe ausgibt wie die Simulation. -test: 12 -> Noch 4 Jahre warten. -test: 16 -> Du darfst wählen. -test: 13 -> Noch 3 Jahre warten. -test: 40 -> Du darfst wählen. -braucht: verzweigung -tipp: Lies den Code Zeile für Zeile. \`if\` und \`else\` werden zu einer Verzweigung. -tipp: \`prompt\` und \`parseInt\` sind zusammen eine Eingabe: \`input: int alter "Wie alt bist du?"\`. Im false-Zweig stehen zwei Kästen: erst \`int jahre = 16 - alter\`, dann die Ausgabe. -lösung: l18-js-loesung.visustruct -::: - -## Spiel: Welche Sprache? - -::: spiel frage -titel: Spiel: Welche Sprache? -intro: Ludwig hat Zeilen aus drei Sprachen gemischt. Erkennst du, woher jede stammt? Acht Runden. -frage: In welcher Sprache ist das geschrieben? -antworten: Java | Python | JavaScript - -runde: - print(zahl) -richtig: Python -> Die Ausgabe heißt in Python \`print(…)\`, ohne Semikolon. - -runde: - console.log(zahl); -richtig: JavaScript -> \`console.log\` ist die Ausgabe in JavaScript. - -runde: - System.out.println(zahl); -richtig: Java -> Die lange Ausgabe mit \`System.out\` gibt es nur in Java. - -runde: - let zahlen = [2, 4, 6]; -richtig: JavaScript -> \`let\` und eckige Klammern: ein Array in JavaScript. - -runde: - for i in range(5): -richtig: Python -> Doppelpunkt statt geschweifter Klammern und \`range\`: typisch Python. - -runde: - int[] zahlen = {2, 4, 6}; -richtig: Java -> Nur Java schreibt den Datentyp \`int[]\` davor. - -runde: - zahl = int(input("Startwert?")) -richtig: Python -> Kein Datentyp vor der Variablen, kein Semikolon, und \`int(…)\` um die Eingabe. - -runde: - let zahl = parseInt(prompt("Startwert?"), 10); -richtig: JavaScript -> \`prompt\` fragt im Browser nach einem Wert, \`parseInt\` macht eine Zahl daraus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Python und JavaScript in der Code-Ansicht auswählen und vergleichen. → Dasselbe Diagramm, drei Sprachen -- Ich kann erklären, dass Python Blöcke durch Einrückung bildet und keine Datentypen braucht. → Python -- Ich kann erklären, was VisuStruct beim Übersetzen nach Python anpasst. → Worauf du bei Python achten musst -- Ich kann JavaScript mit Im Browser testen ausführen. → JavaScript im Browser ausführen -::: - -## Wissenstest - -::: quiz -? Woran erkennt Python, welche Zeilen zu einer Schleife gehören? -→ Python -- [ ] An den geschweiften Klammern -- [x] An der Einrückung -- [ ] Am Semikolon am Zeilenende - -? Wie heißt \`output: summe\` in JavaScript? -→ Dasselbe Diagramm, drei Sprachen -- [ ] print(summe) -- [ ] System.out.println(summe); -- [x] console.log(summe); - -? Welche Sprache kann VisuStruct direkt im Browser ausführen? -→ JavaScript im Browser ausführen -- [ ] Java -- [ ] Python -- [x] JavaScript -> Jeder Browser kann JavaScript ausführen. Für Java und Python brauchst du ein eigenes Programm. - -? Was passiert in Python bei \`print("Summe: " + summe)\`, wenn summe die Zahl 7 ist? -→ Worauf du bei Python achten musst -- [ ] Es wird Summe: 7 ausgegeben. -- [x] Python meldet einen Fehler, weil Text und Zahl nicht mit + verbunden werden können. -- [ ] Es wird Summe: ausgegeben. -> Mit str(summe) wird aus der Zahl ein Text. -::: -`,uw=`--- -titel: Wenn das Programm abstürzt: try und catch -bereich: Fehler abfangen -kurz: Mit echtem Java Fehlermeldungen lesen und Abstürze mit try und catch abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- echtes Java im Bereich Code einschalten -- aus einer Fehlermeldung die Exception und die Zeile ablesen -- eine Exception mit try und catch auffangen -- Text mit Integer.parseInt in eine Zahl umwandeln und die NumberFormatException abfangen -- ein Programm schreiben, das sicher teilt, auch durch 0 -::: - -## Abgestürzt! - -Dieses Programm soll drei Noten ausgeben. Tippe es im Bereich [Code](#zeige-codelive) ab und klicke auf [▶︎ Ausführen](#zeige-ausfuehren): - -\`\`\`java -int[] noten = {2, 3, 1}; -System.out.println("Erste Note: " + noten[0]); -System.out.println("Vierte Note: " + noten[3]); -System.out.println("Fertig!"); -\`\`\` - -Die erste Note erscheint, dann ist Schluss. VisuStruct meldet „Ungültiger Ausdruck: noten[3]“, und „Fertig!“ kommt nie an. Das Array hat nur die Fächer 0, 1 und 2, ein Fach 3 gibt es nicht. - -::: ludwig -Ist dir das auch schon passiert? Ein kleiner Fehler, und das ganze Programm ist weg. Mir ist das so oft passiert, dass ich vor dem Bildschirm geweint habe. Aber es gibt eine Lösung, und die heißt **try** und **catch**. -::: - -::: angeberwissen -1947 fand das Team von Grace Hopper im Rechner Mark II an der Universität Harvard eine Motte, die einen Fehler verursacht hatte. Sie klebten sie ins Logbuch und schrieben dazu: „First actual case of bug being found“. Seitdem heißen Programmfehler **Bugs** (englisch für Käfer). -::: - -## Echtes Java einschalten - -Der Bereich Code rechnet normalerweise mit derselben Simulation wie das Struktogramm. Die hält bei einem Fehler einfach an. Um zu sehen, was echtes Java macht, schaltest du echtes Java ein: - -1. Klicke oben rechts auf das [Zahnrad](#zeige-einstellungen) und wähle als **Programmiersprache** Java. -2. Setze im Abschnitt Java den Haken bei **Echtes Java (mit Swing)**. - -Beim ersten Ausführen lädt VisuStruct einmalig etwa 40 MB. Danach wird dein Code wie in einer richtigen Java-Entwicklungsumgebung übersetzt und ausgeführt. - -> Mit echtem Java funktionieren Eingaben über die Konsole (\`Scanner\`) noch nicht. Unsere Beispiele kommen deshalb ohne Eingabe aus. - -Schreib das Programm jetzt als ganze Klasse, damit die Zeilennummern stimmen, und führe es aus: - -\`\`\`java -public class Noten { - public static void main(String[] args) { - int[] noten = {2, 3, 1}; - System.out.println("Erste Note: " + noten[0]); - System.out.println("Vierte Note: " + noten[3]); - System.out.println("Fertig!"); - } -} -\`\`\` - -In der [Konsole](#zeige-konsole) steht jetzt: - -\`\`\` -Erste Note: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Noten.main(Noten.java:5) -\`\`\` - -## Die Fehlermeldung lesen - -So eine Meldung sieht erst einmal erschreckend aus. Sie verrät dir aber genau, was los ist: - -| Teil | Bedeutung | -|---|---| -| \`ArrayIndexOutOfBoundsException\` | Die Art des Fehlers: Der Index liegt außerhalb des Arrays. | -| \`3\` | Der falsche Index. | -| \`Noten.java:5\` | Die Zeile, in der es passiert ist: Zeile 5. | - -So einen Laufzeitfehler nennt Java eine **Exception** (englisch für Ausnahme). Man sagt: Java *wirft* eine Exception. Fängt sie niemand auf, bricht das Programm ab. Deshalb erscheint „Fertig!“ nicht. - -::: ludwig -Lies Fehlermeldungen immer von oben: Erst der Name der Exception, dann die Zeilennummer. Die Zeilennummern stehen im Bereich Code links neben dem Code. -::: - -## try und catch - -Mit **try** und **catch** fängst du die Exception auf. Ändere die Zeile mit \`noten[3]\` so: - -\`\`\`java -try { - System.out.println("Vierte Note: " + noten[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Diese Note gibt es nicht."); -} -System.out.println("Fertig!"); -\`\`\` - -- \`try\` heißt „versuche“. Java führt den Block in den geschweiften Klammern ganz normal aus. -- Wirft eine Zeile darin eine Exception, springt Java sofort in den \`catch\`-Block. Der Rest des \`try\`-Blocks wird übersprungen. -- In der Klammer hinter \`catch\` steht, welche Exception aufgefangen wird, und ein Name dafür, meistens \`e\`. -- Danach geht das Programm normal weiter. Jetzt erscheint auch „Fertig!“. - -Läuft im \`try\`-Block alles gut, wird der \`catch\`-Block einfach übersprungen. - -::: ludwig -try und catch sind wie das Netz unter dem Trapez im Zirkus: Solange alles klappt, merkst du nichts davon. Fällt jemand, fängt das Netz ihn auf, und die Vorstellung geht weiter. -::: - -> Im Struktogramm gibt es keinen Kasten für try und catch. VisuStruct zeigt den ganzen Block deshalb als eine Anweisung. Ausführen kannst du ihn nur mit echtem Java. - -::: angeberwissen -Im Juli 1969 landete Apollo 11 auf dem Mond. Kurz vor der Landung meldete der Bordcomputer plötzlich die Fehler **1202** und **1201**: Er hatte mehr zu tun, als er schaffen konnte. Abgestürzt ist er trotzdem nicht. Das Team von **Margaret Hamilton** hatte die Software so gebaut, dass sie solche Fehler erkennt, Unwichtiges liegen lässt und nur das Wichtigste weiterrechnet. Die Landung klappte. - -Margaret Hamilton leitete am MIT die Gruppe, die die Software für die Apollo-Raumschiffe schrieb. Sie plante immer ein, dass etwas schiefgehen kann, so wie du mit \`try\` und \`catch\`. Damit Software genauso ernst genommen wird wie Hardware, nannte sie ihre Arbeit **Software Engineering**. Heute heißt so ein ganzes Fach der Informatik. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (geb. 1936) im Jahr 1969 neben den ausgedruckten Programmen, die ihr Team für Apollo geschrieben hat (Foto: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), gemeinfrei). -::: - -## Text in eine Zahl umwandeln - -Oft steht eine Zahl zuerst als Text da, zum Beispiel später in einem Textfeld im Fenster. \`Integer.parseInt\` wandelt Text in eine ganze Zahl um: - -\`\`\`java -String eingabe = "zwölf"; -try { - int zahl = Integer.parseInt(eingabe); - System.out.println("Doppelt: " + zahl * 2); -} catch (NumberFormatException e) { - System.out.println("Das ist keine Zahl: " + eingabe); -} -\`\`\` - -„zwölf“ kann Java nicht umwandeln und wirft eine \`NumberFormatException\`. Das Programm gibt „Das ist keine Zahl: zwölf“ aus. Ändere die erste Zeile in \`String eingabe = "12";\` und führe es noch einmal aus: Jetzt erscheint „Doppelt: 24“. - -## Exceptions sind Objekte - -Das steht nicht im Lehrplan, ist aber praktisch zu wissen. - -::: angeberwissen -Jede Exception ist ein Objekt einer eigenen Klasse, zum Beispiel \`ArithmeticException\` für das Teilen durch 0. \`e.getMessage()\` liefert ihre Meldung, etwa \`/ by zero\`. - -Hinter einem \`try\` dürfen mehrere \`catch\`-Blöcke stehen, für jede Art von Fehler einer. \`catch (Exception e)\` fängt alle Exceptions auf, verrät dir dann aber weniger darüber, was passiert ist. Und ein Block \`finally { … }\` am Ende läuft immer, egal ob es einen Fehler gab oder nicht. -::: - -## Spiel: Fängt das Netz? - -::: spiel frage -titel: Spiel: Fängt das Netz? -intro: Ludwig turnt am Trapez. Fängt try und catch ihn auf, oder stürzt das Programm ab? Sieben Runden. - -runde: - int[] noten = {2, 3, 1}; - System.out.println(noten[3]); - System.out.println("Fertig!"); -frage: Was passiert mit echtem Java? -- Es erscheint 1 und Fertig! -- Es erscheint nur Fertig! -- [x] Das Programm stürzt mit einer ArrayIndexOutOfBoundsException ab. -> Ein Fach 3 gibt es nicht. Niemand fängt die Exception auf, also kommt Fertig! nie an. - -runde: - int[] noten = {2, 3, 1}; - try { - System.out.println(noten[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Diese Note gibt es nicht."); - } - System.out.println("Fertig!"); -frage: Was wird ausgegeben? -- Fertig! -- [x] Diese Note gibt es nicht.\\nFertig! -- Diese Note gibt es nicht. -> Der catch-Block fängt die Exception auf, danach geht das Programm normal weiter. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: Was wird ausgegeben? -- A\\nB\\nC -- [x] A\\nC -- C -> Bei 10 / 0 springt Java sofort in den catch-Block. Der Rest des try-Blocks, also B, wird übersprungen. - -runde: - String eingabe = "12"; - try { - int zahl = Integer.parseInt(eingabe); - System.out.println("Doppelt: " + zahl * 2); - } catch (NumberFormatException e) { - System.out.println("Das ist keine Zahl: " + eingabe); - } -frage: Was wird ausgegeben? -- [x] Doppelt: 24 -- Das ist keine Zahl: 12 -- Doppelt: 1212 -> „12“ lässt sich umwandeln. Läuft im try-Block alles gut, wird catch übersprungen. - -runde: -frage: Welche Exception wirft \`Integer.parseInt("2.5")\`? -- ArithmeticException -- [x] NumberFormatException -- keine, das Ergebnis ist 2 -> parseInt wandelt nur ganze Zahlen um. Mit Komma oder Punkt ist es kein gültiges Format. - -runde: -frage: Welche Exception wirft \`12 / 0\` mit \`int\`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Durch 0 teilen geht mit ganzen Zahlen nicht. Die Meldung lautet \`/ by zero\`. - -runde: - try { - int stuecke = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Fehler!"); - } - System.out.println("Fertig!"); -frage: Was passiert? -- Es erscheint Fehler! und Fertig! -- Es erscheint nur Fertig! -- [x] Das Programm stürzt ab. -> catch fängt nur die Exception, die in der Klammer steht. Eine ArithmeticException fällt durch das Netz. -::: - -## Aufgabe: Sicher teilen - -Dieses Programm stürzt ab: - -\`\`\`java -int kuchen = 12; -int personen = 0; -System.out.println("Jeder bekommt " + kuchen / personen + " Stücke."); -System.out.println("Guten Appetit!"); -\`\`\` - -1. Führe es mit echtem Java aus und lies die Fehlermeldung. Welche Exception ist es? -2. Fange sie mit try und catch ab. Statt des Absturzes soll „Ohne Personen kann man nicht teilen.“ erscheinen, und „Guten Appetit!“ soll trotzdem kommen. -3. Setze \`personen\` auf 4 und prüfe, dass dann „Jeder bekommt 3 Stücke.“ erscheint. - -::: ludwig -Die Exception heißt \`ArithmeticException\`. Schreib in den \`try\`-Block nur die Zeile, die abstürzen kann. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann echtes Java im Bereich Code einschalten. → Echtes Java einschalten -- Ich kann aus einer Fehlermeldung die Exception und die Zeile ablesen. → Die Fehlermeldung lesen -- Ich kann eine Exception mit try und catch auffangen. → try und catch -- Ich kann Text mit Integer.parseInt in eine Zahl umwandeln und die NumberFormatException abfangen. → Text in eine Zahl umwandeln -- Ich kann ein Programm schreiben, das sicher teilt, auch durch 0. → Aufgabe: Sicher teilen -::: - -## Wissenstest - -::: quiz -? Was passiert, wenn eine Exception nicht aufgefangen wird? -→ Die Fehlermeldung lesen -- [ ] Java überspringt die Zeile und macht weiter. -- [x] Das Programm bricht ab. -- [ ] Der Computer startet neu. -> Deshalb erscheint im ersten Beispiel „Fertig!“ nicht. - -? Was passiert im try-Block nach der Zeile, die eine Exception wirft? -→ try und catch -- [ ] Die restlichen Zeilen laufen trotzdem. -- [x] Der Rest wird übersprungen, Java springt in den catch-Block. -- [ ] Java beginnt den try-Block von vorne. - -? Welche Exception wirft Integer.parseInt("zwölf")? -→ Text in eine Zahl umwandeln -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> „zwölf“ hat kein Zahlenformat, das Java versteht. - -? Ordne jeder Exception ihre Ursache zu. -→ Die Fehlermeldung lesen -= ArrayIndexOutOfBoundsException -> Index außerhalb des Arrays -= ArithmeticException -> Teilen durch 0 -= NumberFormatException -> Text ist keine Zahl -::: -`,mw=`--- -titel: Text einlesen und speichern -bereich: Dateien -kurz: Mit Scanner eine Textdatei Zeile für Zeile lesen und mit PrintWriter eine Datei speichern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, warum Programme Daten in Dateien speichern -- eine Textdatei mit Scanner, hasNextLine und nextLine Zeile für Zeile lesen -- erklären, warum Java bei Dateien try und catch oder throws verlangt -- mit PrintWriter eine Datei schreiben und sie mit close() abschließen -- eine Datei lesen, ergänzen und nummeriert wieder speichern -::: - -## Warum Dateien? - -Alles, was in Variablen steht, ist weg, sobald das Programm endet. Soll sich ein Programm etwas merken, zum Beispiel einen Spielstand, eine Liste oder Noten, speichert es die Daten in einer **Datei**. Beim nächsten Start liest es die Datei wieder ein. - -Zora ist frei, und Ludwig plant ein Fest. Seine Einkaufsliste liegt als Datei \`einkauf.txt\` bereit: - -::: datei einkauf.txt -Heu -Äpfel -Karotten -Löwenzahn -::: - -Eine Datei mit der Endung \`.txt\` enthält nur Text, Zeile für Zeile. Du kannst sie mit jedem Editor öffnen, zum Beispiel mit dem Editor von Windows oder TextEdit am Mac. - -## Eine Datei lesen mit Scanner - -Den \`Scanner\` kennst du schon: \`new Scanner(System.in)\` liest von der Tastatur. Gibst du ihm statt \`System.in\` eine Datei, liest er aus der Datei: - -\`\`\`java -import java.io.*; -import java.util.Scanner; - -Scanner datei = new Scanner(new File("einkauf.txt")); -while (datei.hasNextLine()) { - String zeile = datei.nextLine(); - System.out.println(zeile); -} -datei.close(); -\`\`\` - -| Methode | Bedeutung | -|---|---| -| \`new File("einkauf.txt")\` | die Datei mit diesem Namen | -| \`hasNextLine()\` | Gibt es noch eine Zeile? \`true\` oder \`false\` | -| \`nextLine()\` | liefert die nächste Zeile und rückt eine Zeile weiter | -| \`close()\` | schließt die Datei, wenn du fertig bist | - -Die Schleife läuft, solange es noch eine Zeile gibt. In jedem Durchlauf holt \`nextLine()\` die nächste Zeile. \`File\` steht im Paket \`java.io\`, deshalb brauchst du \`import java.io.*;\`. - -::: diagramm dt-lesen.visustruct -So sieht das Lesen im Struktogramm aus. Probier es in der Simulation aus. -::: - -::: ludwig -Stell dir den Scanner wie einen Finger vor, der auf eine Zeile zeigt. \`nextLine()\` liest die Zeile unter dem Finger und schiebt ihn eine Zeile weiter. Ist der Finger unter der letzten Zeile, liefert \`hasNextLine()\` \`false\`. -::: - -## Was, wenn die Datei fehlt? - -Gibt es die Datei nicht, kann Java sie nicht lesen und wirft eine \`FileNotFoundException\`. Java verlangt, dass dein Programm damit rechnet. Entweder du fängst sie mit try und catch auf, wie in Lektion 22: - -\`\`\`java -try { - Scanner datei = new Scanner(new File("einkauf.txt")); - // ... lesen ... - datei.close(); -} catch (FileNotFoundException e) { - System.out.println("Die Datei einkauf.txt fehlt!"); -} -\`\`\` - -Oder du schreibst hinter \`main\` den Zusatz \`throws Exception\`. Das heißt: „Diese Methode kann eine Exception werfen, ich kümmere mich nicht darum.“ Dann bricht das Programm ab, wenn die Datei fehlt. - -\`\`\`java -public static void main(String[] args) throws Exception { -\`\`\` - -> VisuStruct schreibt \`throws Exception\` automatisch hinter \`main\`, sobald dein Programm mit Dateien arbeitet. In der Simulation meldet VisuStruct eine fehlende Datei mit „Es gibt keine Datei …“. - -## Übung: Einkaufsliste nummerieren - -::: übung l23-java-nummern -titel: Einkaufsliste nummerieren -code: java -auftrag: Lies \`einkauf.txt\` Zeile für Zeile und gib jede Zeile mit ihrer Nummer aus: **1. Heu**, **2. Äpfel** und so weiter. -start: l23-java-nummern-start.visustruct -test: - -> 1. Heu | 2. Äpfel | 3. Karotten | 4. Löwenzahn -braucht: whileSchleife -tipp: Lege vor der Schleife \`int nummer = 1;\` an und erhöhe die Nummer am Ende jedes Durchlaufs. -tipp: Die Ausgabe ist \`System.out.println(nummer + ". " + zeile);\` -lösung: l23-java-nummern-loesung.visustruct -::: - -## In eine Datei schreiben mit PrintWriter - -Zum Schreiben gibt es den \`PrintWriter\`. Seine Methoden heißen wie bei \`System.out\`: - -\`\`\`java -PrintWriter aus = new PrintWriter("gruss.txt"); -aus.println("Hallo Zora!"); -aus.println("Bis morgen, Ludwig"); -aus.close(); -\`\`\` - -Danach steht in \`gruss.txt\`: - -\`\`\` -Hallo Zora! -Bis morgen, Ludwig -\`\`\` - -- \`println\` schreibt den Text und beginnt danach eine neue Zeile, \`print\` schreibt ohne neue Zeile. -- Gibt es die Datei schon, wird sie **überschrieben**. Der alte Inhalt ist dann weg. -- Nach dem Lauf zeigt VisuStruct unter der Ausgabe, was in der Datei gespeichert wurde. - -::: ludwig -Vergiss \`close()\` nicht! Der PrintWriter sammelt den Text erst in einem Zwischenspeicher und schreibt ihn beim Schließen in die Datei. Ohne \`close()\` bleibt die Datei leer. Mir ist so schon eine ganze Geschichte verloren gegangen. -::: - -## Übung: Einkaufsliste ergänzen - -::: übung l23-java-ergaenzen -titel: Einkaufsliste ergänzen -code: java -auftrag: Ludwig braucht noch Kuchen. Lies \`einkauf.txt\` und schreibe jede Zeile in die neue Datei \`einkauf-neu.txt\`. Schreibe danach **Kuchen** als letzte Zeile dazu. Gib am Ende aus, wie viele Zeilen gespeichert wurden: **5 Zeilen gespeichert**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 Zeilen gespeichert -datei: einkauf-neu.txt -> Heu | Äpfel | Karotten | Löwenzahn | Kuchen -braucht: whileSchleife -tipp: In der Schleife liest du eine Zeile mit \`datei.nextLine()\` und schreibst sie mit \`aus.println(zeile)\` gleich wieder hinaus. -tipp: Nach der Schleife kommt \`aus.println("Kuchen");\`. Schließe beide Dateien mit \`close()\`, sonst bleibt \`einkauf-neu.txt\` leer. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Spiel: Was steht in der Datei? - -::: spiel frage -titel: Spiel: Was steht in der Datei? -intro: Ludwig schreibt Dateien mit dem PrintWriter. Lies den Code und sag voraus, was danach in der Datei steht. Sechs Runden. -frage: Was steht danach in der Datei? - -runde: - PrintWriter aus = new PrintWriter("gruss.txt"); - aus.println("Hallo"); - aus.println("Zora"); - aus.close(); -- [x] Hallo\\nZora -- HalloZora -- Zora -> \`println\` beginnt nach jedem Text eine neue Zeile. - -runde: - PrintWriter aus = new PrintWriter("wort.txt"); - aus.print("Ha"); - aus.print("llo"); - aus.println("!"); - aus.close(); -- Ha\\nllo\\n! -- [x] Hallo! -- Ha llo ! -> \`print\` schreibt ohne neue Zeile weiter, die Teile hängen direkt aneinander. - -runde: - PrintWriter aus = new PrintWriter("geheim.txt"); - aus.println("Der Schatz liegt im Turm"); -- Der Schatz liegt im Turm -- [x] Nichts, die Datei bleibt leer. -- Java meldet einen Fehler. -> Ohne \`close()\` landet der Text nie in der Datei. Er bleibt im Zwischenspeicher. - -runde: - // In notiz.txt steht vorher: Alt - PrintWriter aus = new PrintWriter("notiz.txt"); - aus.println("Neu"); - aus.close(); -- Alt\\nNeu -- [x] Neu -- Neu\\nAlt -> Ein neuer PrintWriter überschreibt die Datei. Der alte Inhalt ist weg. - -runde: - PrintWriter aus = new PrintWriter("zahlen.txt"); - for (int i = 1; i <= 3; i++) { - aus.println(i * 2); - } - aus.close(); -- 1\\n2\\n3 -- 246 -- [x] 2\\n4\\n6 -> Die Schleife schreibt 2, 4 und 6, jede Zahl in eine eigene Zeile. - -runde: - // In einkauf.txt steht: Heu, Äpfel (je eine Zeile) - Scanner datei = new Scanner(new File("einkauf.txt")); - PrintWriter aus = new PrintWriter("gross.txt"); - while (datei.hasNextLine()) { - aus.println(datei.nextLine().toUpperCase()); - } - datei.close(); - aus.close(); -- Heu\\nÄpfel -- [x] HEU\\nÄPFEL -- HEUÄPFEL -> Jede Zeile wird gelesen, in Großbuchstaben verwandelt und in eine eigene Zeile geschrieben. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, warum Programme Daten in Dateien speichern. → Warum Dateien? -- Ich kann eine Textdatei mit Scanner, hasNextLine und nextLine Zeile für Zeile lesen. → Eine Datei lesen mit Scanner -- Ich kann erklären, warum Java bei Dateien try und catch oder throws verlangt. → Was, wenn die Datei fehlt? -- Ich kann mit PrintWriter eine Datei schreiben und sie mit close() abschließen. → In eine Datei schreiben mit PrintWriter -- Ich kann eine Datei lesen, ergänzen und nummeriert wieder speichern. → Übung: Einkaufsliste ergänzen -::: - -## Wissenstest - -::: quiz -? Was liefert hasNextLine()? -→ Eine Datei lesen mit Scanner -- [ ] die nächste Zeile -- [x] true, wenn es noch eine Zeile gibt, sonst false -- [ ] die Anzahl der Zeilen -> Mit hasNextLine() prüft die Schleife, ob es weitergeht. - -? Was passiert, wenn du new PrintWriter("liste.txt") für eine Datei aufrufst, die es schon gibt? -→ In eine Datei schreiben mit PrintWriter -- [ ] Der neue Text wird hinten angehängt. -- [x] Die Datei wird überschrieben. -- [ ] Java meldet einen Fehler. - -? Warum bleibt eine Datei leer, obwohl println aufgerufen wurde? -→ In eine Datei schreiben mit PrintWriter -- [ ] Weil println nur in die Konsole schreibt. -- [x] Weil close() fehlt. -- [ ] Weil der Dateiname auf .txt endet. -> Erst close() schreibt den Zwischenspeicher in die Datei. - -? Ordne jeder Methode ihre Aufgabe zu. -→ Eine Datei lesen mit Scanner -= nextLine() -> liest die nächste Zeile -= println(text) -> schreibt eine Zeile -= close() -> schließt die Datei -::: -`,cw=`--- -titel: Zeile für Zeile mit BufferedReader -bereich: Dateien -kurz: Mit BufferedReader und readLine lesen, bis null kommt, und mit Files eine ganze Datei auf einmal lesen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Datei mit BufferedReader und readLine lesen -- das Dateiende erkennen, weil readLine dort null liefert -- zählen, wie oft ein Wort in einer Datei vorkommt -- eine ganze Datei mit Files.readString lesen und mit split in Zeilen zerlegen -- den längsten Eintrag einer Datei finden -::: - -## Der Kuchen ist weg! - -Am Morgen nach dem Fest ist Ludwigs Kuchen verschwunden. Zum Glück führt die Burg ein Logbuch. Jede Zeile beginnt mit dem Tag und der Uhrzeit: - -::: datei logbuch.txt -Mo 08:15 Ludwig füttert die Ziegen -Mo 12:30 Zora liest im Turm -Mo 18:00 Fuchs schleicht am Tor vorbei -Di 09:00 Ludwig backt einen Kuchen -Di 14:45 Hase bringt Karotten -Di 16:20 Fuchs fragt nach dem Rezept -Di 23:55 Der Kuchen ist weg -Mi 07:00 Zora findet Krümel vor dem Fuchsbau -::: - -Mit dem Scanner aus Lektion 23 könntest du es schon lesen. Profis nehmen für Dateien aber oft den **BufferedReader**. - -## Lesen mit BufferedReader - -\`\`\`java -import java.io.*; - -BufferedReader leser = new BufferedReader(new FileReader("logbuch.txt")); -String zeile = leser.readLine(); -while (zeile != null) { - System.out.println(zeile); - zeile = leser.readLine(); -} -leser.close(); -\`\`\` - -- \`new FileReader("logbuch.txt")\` öffnet die Datei, \`new BufferedReader(…)\` legt einen Puffer drumherum. Er holt die Datei in großen Stücken und ist deshalb bei langen Dateien schneller. -- \`readLine()\` liefert die nächste Zeile. Gibt es keine Zeile mehr, liefert es **null**. -- Die Schleife läuft deshalb, solange \`zeile != null\` ist. Am Ende jedes Durchlaufs liest sie die nächste Zeile. - -::: diagramm dt-buffered.visustruct -Erst eine Zeile lesen, dann prüfen, dann am Ende des Durchlaufs die nächste lesen. -::: - -**null** heißt „gar nichts“. Es ist nicht der leere Text \`""\`, sondern kein Text. Eine Variable mit \`null\` hat keine Methoden: \`zeile.length()\` bricht dann mit einer \`NullPointerException\` ab. Prüfe deshalb immer zuerst \`zeile != null\`. - -| | Scanner | BufferedReader | -|---|---|---| -| Anlegen | \`new Scanner(new File(…))\` | \`new BufferedReader(new FileReader(…))\` | -| Gibt es noch eine Zeile? | \`hasNextLine()\` | \`readLine()\` liefert \`null\` | -| Nächste Zeile | \`nextLine()\` | \`readLine()\` | -| Stärke | kann auch Zahlen lesen (\`nextInt()\`) | schnell, auch bei großen Dateien | - -::: ludwig -Die Schleife sieht anders aus als beim Scanner: Die erste Zeile wird **vor** der Schleife gelesen, jede weitere **am Ende** des Durchlaufs. Vergisst du das Lesen am Ende, liest das Programm immer wieder dieselbe Zeile und hört nie auf. -::: - -::: angeberwissen -Wo eine Zeile endet, ist nicht überall gleich: Windows schreibt zwei unsichtbare Zeichen, „Wagenrücklauf“ und „Zeilenvorschub“, Linux und macOS nur den Zeilenvorschub. Die Namen stammen von der Schreibmaschine, bei der der Papierwagen zurückfuhr und das Papier eine Zeile weiterrückte. \`readLine()\` kommt mit beidem klar. -::: - -## Übung: Wie oft kommt der Fuchs vor? - -::: übung l24-java-fuchs -titel: Wie oft kommt der Fuchs vor? -code: java -auftrag: Lies das Logbuch mit dem BufferedReader und zähle die Zeilen, in denen **Fuchs** vorkommt. Gib am Ende **Fuchs:** und die Anzahl aus. -start: l24-java-fuchs-start.visustruct -test: - -> Fuchs: 3 -braucht: whileSchleife, verzweigung -tipp: Lege vor der Schleife \`int anzahl = 0;\` an. In der Schleife prüfst du \`zeile.contains("Fuchs")\`. -tipp: Das \`zeile = leser.readLine();\` gehört ans Ende der Schleife, nach dem if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Automatisch schließen - -Damit \`close()\` auch nach einer Exception nicht vergessen wird, gibt es eine Kurzform von try: Was in den runden Klammern hinter \`try\` angelegt wird, schließt Java am Ende von selbst. - -\`\`\`java -try (BufferedReader leser = new BufferedReader(new FileReader("logbuch.txt"))) { - String zeile = leser.readLine(); - while (zeile != null) { - System.out.println(zeile); - zeile = leser.readLine(); - } -} catch (IOException e) { - System.out.println("Das Logbuch fehlt."); -} -\`\`\` - -Das heißt **try-with-resources**. \`IOException\` fängt alle Fehler beim Lesen und Schreiben auf, auch eine fehlende Datei. - -> Wie try und catch läuft auch try-with-resources nur mit echtem Java (Lektion 22). In der Simulation schreibst du \`close()\` selbst. - -## Die Abkürzung: Files - -Seit Java 11 geht es noch kürzer. \`Files.readString\` liest die ganze Datei auf einmal, das kennst du aus Lektion 17. Mit \`split("\\n")\` zerlegst du den Text in ein Array von Zeilen: - -\`\`\`java -import java.nio.file.*; - -String text = Files.readString(Path.of("logbuch.txt")); -String[] zeilen = text.split("\\n"); -for (int i = 0; i < zeilen.length; i++) { - System.out.println(zeilen[i]); -} -\`\`\` - -Jetzt stehen alle Zeilen im Array. Du kannst sie in beliebiger Reihenfolge ansehen, zum Beispiel rückwärts oder nur jede zweite. - -| Weg | Gut für | -|---|---| -| \`Scanner\` | kleine Dateien, Zahlen und Wörter lesen | -| \`BufferedReader\` | große Dateien, Zeile für Zeile | -| \`Files.readString\` | kleine Dateien, die ganz ins Programm passen | - -::: ludwig -Eine Datei mit einer Million Zeilen liest du besser mit dem BufferedReader Zeile für Zeile. Mit \`Files.readString\` müsste die ganze Datei auf einmal in den Speicher. -::: - -## Übung: Der längste Eintrag - -::: übung l24-java-laengster -titel: Der längste Eintrag -code: java -auftrag: Im Startcode stehen die Zeilen des Logbuchs schon im Array \`zeilen\`. Finde die längste Zeile und gib sie so aus: **Längster Eintrag:** und dann die Zeile. -start: l24-java-laengster-start.visustruct -test: - -> Längster Eintrag: Mi 07:00 Zora findet Krümel vor dem Fuchsbau -braucht: forSchleife, verzweigung -tipp: Merke dir die bisher längste Zeile in \`String laengster = zeilen[0];\` und gehe die anderen Zeilen mit einer for-Schleife durch. -tipp: Ist \`zeilen[i].length() > laengster.length()\`, wird \`zeilen[i]\` der neue Längste. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Spiel: Datei-Detektiv - -::: spiel frage -titel: Spiel: Datei-Detektiv -intro: Wer hat den Kuchen genommen? Jedes Programm durchsucht das **Logbuch** von oben. Sag voraus, was es ausgibt, und sammle so die Hinweise. Fünf Runden. -frage: Was gibt das Programm aus? - -runde: - String zeile = leser.readLine(); - zeile = leser.readLine(); - System.out.println(zeile.substring(9)); -- Ludwig füttert die Ziegen -- [x] Zora liest im Turm -- Mo 12:30 Zora liest im Turm -> Das zweite \`readLine()\` liefert die zweite Zeile. Ab Index 9 beginnt der Text nach der Uhrzeit. - -runde: - int n = 0; - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.startsWith("Di")) n++; - zeile = leser.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Vier Einträge stammen vom Dienstag. - -runde: - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.contains("Kuchen")) { - System.out.println(zeile.substring(3, 8)); - } - zeile = leser.readLine(); - } -- [x] 09:00\\n23:55 -- 09:00 -- Di\\nDi -> Zwei Zeilen enthalten „Kuchen“. \`substring(3, 8)\` schneidet die Uhrzeit heraus. - -runde: - String zeile = leser.readLine(); - String letzte = ""; - while (zeile != null) { - if (zeile.contains("Fuchs")) letzte = zeile; - zeile = leser.readLine(); - } - System.out.println(letzte); -- Di 16:20 Fuchs fragt nach dem Rezept -- [x] Mi 07:00 Zora findet Krümel vor dem Fuchsbau -- Mo 18:00 Fuchs schleicht am Tor vorbei -> \`letzte\` wird bei jedem Treffer überschrieben. Am Ende steht der letzte Treffer darin. „Fuchsbau“ enthält auch „Fuchs“. - -runde: - String zeile = leser.readLine(); - while (zeile != null) { - if (zeile.contains("Krümel")) { - int bei = zeile.indexOf("vor dem "); - System.out.println(zeile.substring(bei + 8)); - } - zeile = leser.readLine(); - } -- [x] Fuchsbau -- vor dem Fuchsbau -- Krümel -> Die Krümel liegen vor dem Fuchsbau. Der Fuchs hat nach dem Rezept gefragt, und um 23:55 war der Kuchen weg. Fall gelöst! -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Datei mit BufferedReader und readLine lesen. → Lesen mit BufferedReader -- Ich kann das Dateiende erkennen, weil readLine dort null liefert (zeile != null). → Lesen mit BufferedReader -- Ich kann zählen, wie oft ein Wort in einer Datei vorkommt. → Übung: Wie oft kommt der Fuchs vor? -- Ich kann eine ganze Datei mit Files.readString lesen und mit split in Zeilen zerlegen. → Die Abkürzung: Files -- Ich kann den längsten Eintrag einer Datei finden. → Übung: Der längste Eintrag -::: - -## Wissenstest - -::: quiz -? Was liefert readLine(), wenn die Datei zu Ende ist? -→ Lesen mit BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null heißt: Es gibt keinen Text mehr. - -? Warum steht in der Schleife am Ende noch einmal zeile = leser.readLine()? -→ Lesen mit BufferedReader -- [ ] Damit die Datei geschlossen wird. -- [x] Sonst bleibt zeile immer gleich, und die Schleife hört nie auf. -- [ ] Damit die erste Zeile doppelt ausgegeben wird. - -? Was macht try-with-resources? -→ Lesen mit BufferedReader -- [ ] Es liest die Datei schneller. -- [x] Es schließt die Datei am Ende von selbst. -- [ ] Es verhindert jede Exception. - -? Ordne jedem Weg zu, wofür er gut ist. -→ Die Abkürzung: Files -= BufferedReader -> große Dateien, Zeile für Zeile -= Files.readString -> kleine Dateien auf einmal -= Scanner -> Zahlen und Wörter lesen -::: -`,dw=`--- -titel: Tabellen als CSV -bereich: Dateien -kurz: Eine Tabelle als CSV-Datei lesen, jede Zeile mit split zerlegen und mit den Zahlen rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- den Aufbau einer CSV-Datei erklären: Kopfzeile, Datensätze, Trennzeichen -- eine Zeile mit split zerlegen und mit Integer.parseInt eine Zahl daraus machen -- die Kopfzeile überspringen, bevor ich rechne -- einen Durchschnitt aus einer Spalte einer CSV-Datei berechnen -- den besten Wert in einer Spalte einer CSV-Datei finden -- eine Stärke und eine Schwäche von CSV nennen -::: - -## Eine Tabelle in einer Textdatei - -Frau Ziege, die Lehrerin der Burgschule, hat die Noten ihrer Klasse als Tabelle gespeichert: - -| Name | Mathe | Deutsch | Englisch | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Fuchs | 4 | 3 | 5 | -| Hase | 2 | 1 | 3 | - -In der Datei sieht die Tabelle so aus: - -::: datei klasse.csv -Name;Mathe;Deutsch;Englisch -Ludwig;2;3;1 -Zora;1;2;2 -Fuchs;4;3;5 -Hase;2;1;3 -::: - -Das ist eine **CSV-Datei**. CSV heißt „Comma-Separated Values“, also „durch Kommas getrennte Werte“. Jede Zeile ist eine Zeile der Tabelle, die Spalten sind durch ein Trennzeichen getrennt. In Deutschland nimmt man meist das **Semikolon** \`;\`, weil das Komma schon in Zahlen wie \`2,5\` steckt. - -- Die erste Zeile ist die **Kopfzeile**. Sie sagt, was in den Spalten steht. -- Jede weitere Zeile ist ein **Datensatz**, hier eine Schülerin oder ein Schüler. -- Excel, LibreOffice und Numbers öffnen CSV-Dateien direkt als Tabelle. - -## Eine Zeile zerlegen mit split - -\`split(";")\` kennst du aus Lektion 17. Es zerlegt einen Text an jedem Semikolon und liefert ein Array: - -\`\`\`java -String zeile = "Zora;1;2;2"; -String[] teile = zeile.split(";"); -// teile[0] ist "Zora", teile[1] ist "1", teile[2] ist "2", teile[3] ist "2" -\`\`\` - -Die Noten sind danach noch **Text**. Rechnen kannst du erst, wenn du sie mit \`Integer.parseInt\` in eine Zahl verwandelst: - -\`\`\`java -int mathe = Integer.parseInt(teile[1]); -\`\`\` - -::: diagramm dt-csv.visustruct -Eine CSV-Zeile zerlegen und die Mathenote als Zahl lesen. -::: - -## Die ganze Datei lesen - -Für die ganze Datei kombinierst du den BufferedReader aus Lektion 24 mit \`split\`. Die Kopfzeile enthält keine Noten, deshalb liest du sie vor der Schleife und überspringst sie: - -\`\`\`java -BufferedReader leser = new BufferedReader(new FileReader("klasse.csv")); -String zeile = leser.readLine(); // Kopfzeile -zeile = leser.readLine(); // erster Datensatz -while (zeile != null) { - String[] teile = zeile.split(";"); - System.out.println(teile[0] + ": Mathe " + teile[1]); - zeile = leser.readLine(); -} -leser.close(); -\`\`\` - -::: ludwig -Ohne das zweite \`readLine()\` vor der Schleife landet die Kopfzeile in der Schleife. Dann versucht \`Integer.parseInt("Mathe")\` aus dem Wort eine Zahl zu machen, und Java bricht mit einer \`NumberFormatException\` ab. -::: - -## Übung: Notenschnitt in Mathe - -::: übung l25-java-schnitt -titel: Notenschnitt in Mathe -code: java -auftrag: Berechne den Notenschnitt in Mathe. Überspringe die Kopfzeile, addiere die Mathenoten (Spalte 1) und teile durch die Anzahl der Datensätze. Gib **Schnitt Mathe:** und den Schnitt aus. -start: l25-java-schnitt-start.visustruct -test: - -> Schnitt Mathe: 2.25 -braucht: whileSchleife -tipp: Lege vor der Schleife \`double summe = 0;\` und \`int anzahl = 0;\` an. Mit \`double\` wird beim Teilen nichts abgeschnitten. -tipp: In der Schleife: \`String[] teile = zeile.split(";");\`, dann \`summe = summe + Integer.parseInt(teile[1]);\` und \`anzahl++;\`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Übung: Beste Note in Englisch - -::: übung l25-java-beste -titel: Beste Note in Englisch -code: java -auftrag: Wer hat die beste Note in Englisch (Spalte 3)? Gib **Beste Note in Englisch:**, den Namen und in Klammern die Note aus. -start: l25-java-beste-start.visustruct -test: - -> Beste Note in Englisch: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Merke dir die bisher beste Note in \`int besteNote = 7;\` und den Namen in \`String besterName = "";\`. Jede echte Note ist besser als 7. -tipp: Ist \`note < besteNote\`, merkst du dir die neue Note und \`teile[0]\` als Namen. -lösung: l25-java-beste-loesung.visustruct -::: - -## Stärken und Schwächen von CSV - -| Gut | Schwierig | -|---|---| -| Tabellen brauchen wenig Platz. | Steht das Trennzeichen im Text, zum Beispiel \`Hase;sehr gut; fleißig\`, zerfällt die Zeile in zu viele Teile. | -| Excel und andere Programme öffnen die Datei direkt. | Alles ist eine flache Tabelle. Hat ein Tier mehrere Lieblingsessen, passt das nicht gut in eine Zelle. | -| Jedes Programm kann sie mit \`split\` lesen. | Die Datei sagt nicht, ob \`1\` eine Zahl oder ein Text ist. | - -::: ludwig -Excel speichert CSV-Dateien in Deutschland mit Semikolon, in England und den USA mit Komma. Wer eine Datei aus dem Ausland öffnet, sieht deshalb manchmal alles in einer einzigen Spalte. -::: - -## Spiel: Kaputte Zeile - -::: spiel bombe -titel: Spiel: Kaputte Zeile -intro: In jeder CSV-Datei ist **genau eine Zeile kaputt**, und die Bombe tickt. Vergleiche jede Zeile mit der Kopfzeile und tippe auf die kaputte Zeile, bevor die Zeit abläuft. Fünf Runden, je 20 Sekunden. -zeit: 20 -daten: csv - -runde: - Name;Mathe;Deutsch;Englisch - Ludwig;2;3;1 - Zora;1 - Fuchs;4;3;5 -frage: Jede Zeile braucht vier Spalten. -fehler: 3 -besser: Zora;1;2;2 -> Bei Zora fehlen zwei Noten. \`teile[3]\` gibt es dann nicht, und Java bricht mit einer \`ArrayIndexOutOfBoundsException\` ab. - -runde: - Name;Note;Bemerkung - Ludwig;2;arbeitet gut mit - Hase;1;sehr gut; fleißig - Zora;1;liest viel -frage: Jede Zeile braucht drei Spalten. -fehler: 3 -besser: Hase;1;sehr gut, fleißig -> Das Semikolon in der Bemerkung trennt eine vierte Spalte ab. Im Text darf das Trennzeichen nicht vorkommen. - -runde: - Name;Mathe;Deutsch;Englisch - Ludwig;2;3;1 - Zora;1;2;2 - Fuchs,4,3,5 -frage: Getrennt wird mit Semikolon. -fehler: 4 -besser: Fuchs;4;3;5 -> Hier stehen Kommas statt Semikolons. \`split(";")\` findet kein Semikolon und liefert die ganze Zeile als ein einziges Teil. - -runde: - Name;Mathe;Deutsch;Englisch - Hase;2;1;3 - Fuchs;vier;3;5 - Ludwig;2;3;1 -frage: Noten sind Zahlen. -fehler: 3 -besser: Fuchs;4;3;5 -> \`Integer.parseInt("vier")\` kann aus dem Wort keine Zahl machen und wirft eine \`NumberFormatException\`. - -runde: - Name;Mathe;Deutsch;Englisch - Zora;1;2;2 - Hase;2;1;3 - Ludwig;2;;1 -frage: Kein Feld darf leer sein. -fehler: 4 -besser: Ludwig;2;3;1 -> Zwischen den beiden Semikolons steht nichts. \`Integer.parseInt("")\` wirft eine \`NumberFormatException\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann den Aufbau einer CSV-Datei erklären: Kopfzeile, Datensätze, Trennzeichen. → Eine Tabelle in einer Textdatei -- Ich kann eine Zeile mit split zerlegen und mit Integer.parseInt eine Zahl daraus machen. → Eine Zeile zerlegen mit split -- Ich kann die Kopfzeile überspringen, bevor ich rechne. → Die ganze Datei lesen -- Ich kann einen Durchschnitt aus einer Spalte einer CSV-Datei berechnen. → Übung: Notenschnitt in Mathe -- Ich kann den besten Wert in einer Spalte einer CSV-Datei finden. → Übung: Beste Note in Englisch -- Ich kann eine Stärke und eine Schwäche von CSV nennen. → Stärken und Schwächen von CSV -::: - -## Wissenstest - -::: quiz -? Was liefert "Hase;2;1;3".split(";")[2]? -→ Eine Zeile zerlegen mit split -- [ ] "2" -- [x] "1" -- [ ] 1 -> Die Zählung beginnt bei 0. Und das Ergebnis ist ein Text, keine Zahl. - -? Warum liest das Programm vor der Schleife zweimal readLine()? -→ Die ganze Datei lesen -- [ ] Damit die Datei schneller gelesen wird. -- [x] Die erste Zeile ist die Kopfzeile ohne Noten. Sie wird übersprungen. -- [ ] Weil readLine immer zwei Zeilen braucht. - -? Was passiert bei Integer.parseInt("Mathe")? -→ Eine Zeile zerlegen mit split -- [ ] Es liefert 0. -- [x] Java wirft eine NumberFormatException. -- [ ] Es liefert die Länge des Wortes. - -? Ordne zu. -→ Stärken und Schwächen von CSV -= Kopfzeile -> sagt, was in den Spalten steht -= Datensatz -> eine Zeile mit Werten -= Trennzeichen -> trennt die Spalten, meist ; -::: -`,hw=`--- -titel: Daten als JSON -bereich: Dateien -kurz: JSON-Dateien verstehen, Werte mit indexOf und substring herausholen und selbst JSON schreiben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Objekte, Schlüssel, Werte und Listen in JSON erkennen -- mit indexOf und substring einen Wert aus einer JSON-Zeile holen -- alle Werte zu einem Schlüssel aus einer JSON-Datei ausgeben -- mit + und \\" einen JSON-Eintrag bauen und speichern -- Daten als JSON-Datei speichern -- eine Stärke und eine Schwäche von JSON nennen -::: - -## Daten mit Namen - -Der Burgzoo speichert seine Tiere in einer Datei \`zoo.json\`: - -::: datei zoo.json -{ - "zoo": "Burgzoo", - "tiere": [ - {"name": "Ludwig", "art": "Geißbock", "alter": 7}, - {"name": "Zora", "art": "Ziege", "alter": 6}, - {"name": "Fuchs", "art": "Rotfuchs", "alter": 4}, - {"name": "Hase", "art": "Feldhase", "alter": 2} - ] -} -::: - -Das ist **JSON**, die „JavaScript Object Notation“. Fast jede Web-App und jedes Spiel schickt und speichert Daten so. Anders als in einer CSV-Datei steht vor jedem Wert sein **Name**, der **Schlüssel**. - -| Zeichen | Bedeutung | Beispiel | -|---|---|---| -| \`{ … }\` | ein **Objekt**: Paare aus Schlüssel und Wert | \`{"name": "Zora", "alter": 6}\` | -| \`"schlüssel": wert\` | ein Paar, getrennt durch einen Doppelpunkt | \`"alter": 6\` | -| \`[ … ]\` | eine **Liste** von Werten, wie ein Array | \`"tiere": [ … ]\` | -| \`,\` | trennt die Paare und die Einträge einer Liste | | -| \`"…"\` | ein Text. Zahlen und \`true\`/\`false\` stehen ohne Anführungszeichen. | \`"Ziege"\`, \`6\` | - -Objekte und Listen lassen sich **verschachteln**: Das ganze Zoo-Objekt enthält die Liste \`tiere\`, und jedes Tier ist wieder ein Objekt. Wie bei Arrays zählt die Liste ab 0: \`tiere[1].name\` ist \`"Zora"\`. - -::: angeberwissen -JSON hat Douglas Crockford Anfang der 2000er bekannt gemacht. In die Lizenz seines JSON-Programms schrieb er: „Die Software soll für Gutes verwendet werden, nicht für Böses.“ Die Firma IBM fragte deshalb nach, ob sie es trotzdem benutzen darf. Er antwortete, IBM und seine Kunden dürften es auch für Böses verwenden. -::: - -## Einen Wert herausholen - -Profis lesen JSON mit einer Bibliothek, zum Beispiel **Gson**. Sie gehört nicht zu Java und muss erst eingebunden werden. Für unsere Zoo-Datei reichen die String-Methoden aus Lektion 17, denn jedes Tier steht in einer eigenen Zeile. - -Ein Anführungszeichen im Text schreibst du in Java als \`\\"\`. Der Text \`"\\"name\\": \\""\` ist also \`"name": "\`, genau das, was vor jedem Namen steht. - -\`\`\`java -String zeile = "{\\"name\\": \\"Zora\\", \\"art\\": \\"Ziege\\", \\"alter\\": 6},"; -int start = zeile.indexOf("\\"name\\": \\"") + 9; -int ende = zeile.indexOf("\\"", start); -String name = zeile.substring(start, ende); -\`\`\` - -- \`indexOf("\\"name\\": \\"")\` findet den Anfang von \`"name": "\`. Das sind 9 Zeichen, also beginnt der Name 9 Zeichen weiter. -- \`indexOf("\\"", start)\` sucht **ab** \`start\` das nächste Anführungszeichen. Dort endet der Name. -- \`substring(start, ende)\` schneidet den Namen heraus: \`Zora\`. - -::: diagramm dt-json.visustruct -Den Namen aus einer JSON-Zeile holen. -::: - -## Übung: Alle Namen aus dem Zoo - -::: übung l26-java-namen -titel: Alle Namen aus dem Zoo -code: java -auftrag: Lies \`zoo.json\` Zeile für Zeile. Gib in jeder Zeile, die \`"name"\` enthält, nur den Namen aus. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Fuchs | Hase -braucht: whileSchleife, verzweigung -tipp: Prüfe mit \`zeile.contains("\\"name\\"")\`, ob in der Zeile ein Name steht. Die anderen Zeilen überspringst du. -tipp: Im if: \`int start = zeile.indexOf("\\"name\\": \\"") + 9;\`, dann \`int ende = zeile.indexOf("\\"", start);\` und \`System.out.println(zeile.substring(start, ende));\`. -lösung: l26-java-namen-loesung.visustruct -::: - -## JSON schreiben - -Zum Schreiben setzt du den Text mit \`+\` zusammen und schreibst ihn mit dem PrintWriter aus Lektion 23 in die Datei. Texte im JSON brauchen Anführungszeichen, also \`\\"\` im Java-Text, Zahlen nicht: - -\`\`\`java -String name = "Zora"; -int punkte = 15; -String eintrag = "{\\"name\\": \\"" + name + "\\", \\"punkte\\": " + punkte + "}"; -// eintrag ist {"name": "Zora", "punkte": 15} -\`\`\` - -Bei einer Liste steht zwischen den Einträgen ein Komma, hinter dem letzten aber **keins**. Das prüfst du mit einem if. - -::: ludwig -Ein einziges vergessenes Anführungszeichen oder ein Komma zu viel, und kein Programm kann die Datei mehr lesen. JSON ist streng. Darum benutzen Profis zum Schreiben auch eine Bibliothek. -::: - -## Übung: Punkte als JSON speichern - -::: übung l26-java-schreiben -titel: Punkte als JSON speichern -code: java -auftrag: Speichere die Punkte aus dem Quiz-Turnier als Liste in \`punkte.json\`. Jede Zeile enthält ein Objekt mit **name** und **punkte**, zwischen den Objekten steht ein Komma. Gib am Ende **3 Einträge gespeichert** aus. -start: l26-java-schreiben-start.visustruct -test: - -> 3 Einträge gespeichert -datei: punkte.json -> [ | {"name": "Ludwig", "punkte": 12}, | {"name": "Zora", "punkte": 15}, | {"name": "Fuchs", "punkte": 9} | ] -braucht: forSchleife, verzweigung -tipp: Gehe mit \`for (int i = 0; i < namen.length; i++)\` durch die Arrays. Baue den Eintrag mit \`" {\\"name\\": \\"" + namen[i] + "\\", \\"punkte\\": " + punkte[i] + "}"\`. -tipp: Ist \`i < namen.length - 1\`, hängst du noch \`","\` an. Schreibe nach der Schleife \`"]"\` und schließe die Datei. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Stärken und Schwächen von JSON - -| Gut | Schwierig | -|---|---| -| Jeder Wert hat einen Namen. Man sieht sofort, was \`6\` bedeutet. | Die Datei ist länger als eine CSV-Datei, weil die Schlüssel in jeder Zeile stehen. | -| Daten lassen sich verschachteln, zum Beispiel eine Liste in einem Objekt. | Java kann JSON nicht von selbst lesen. Man braucht eine Bibliothek wie Gson. | -| JSON ist der Standard im Web. Fast jede Programmiersprache kennt es. | Ein Zeichen zu viel oder zu wenig macht die ganze Datei unlesbar. | - -## Spiel: Welcher Schlüssel? - -::: spiel frage -titel: Spiel: Welcher Schlüssel? -intro: Ludwig sucht Werte in JSON-Dateien. Lies die Datei und sag, welcher Wert gemeint ist. Fünf Runden. - -runde: - {"name": "Zora", "art": "Ziege", "alter": 6} -frage: Was ist der Wert von **alter**? -- "alter" -- [x] 6 -- "Ziege" -> Vor dem Doppelpunkt steht der Schlüssel, dahinter der Wert. Zahlen stehen ohne Anführungszeichen. - -runde: - { - "tiere": [ - {"name": "Ludwig", "alter": 7}, - {"name": "Zora", "alter": 6}, - {"name": "Fuchs", "alter": 4} - ] - } -frage: Was ist **tiere[1].name**? -- Ludwig -- [x] Zora -- Fuchs -> Die Liste zählt wie ein Array ab 0. \`tiere[1]\` ist das zweite Tier. - -runde: - { - "zoo": "Burgzoo", - "chef": {"name": "Ludwig", "art": "Geißbock"} - } -frage: Wie kommst du an **Geißbock**? -- [x] chef.art -- art -- zoo.chef.art -> \`art\` steckt im Objekt \`chef\`. Darum geht der Weg über \`chef\`. - -runde: - { - "name": "Hase", - "lieblingsessen": ["Karotten", "Klee", "Löwenzahn"] - } -frage: Was ist **lieblingsessen[2]**? -- Klee -- Karotten -- [x] Löwenzahn -> Index 0 ist Karotten, 1 ist Klee, 2 ist Löwenzahn. So etwas passt in CSV nur schwer in eine Zelle. - -runde: - {"name": "Fuchs", "alter": 4, "hungrig": true} -frage: Welcher Schlüssel hat einen Wahrheitswert? -- name -- alter -- [x] hungrig -> \`true\` und \`false\` stehen wie Zahlen ohne Anführungszeichen. In Java wäre das ein \`boolean\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Objekte, Schlüssel, Werte und Listen in JSON erkennen. → Daten mit Namen -- Ich kann mit indexOf und substring einen Wert aus einer JSON-Zeile holen. → Einen Wert herausholen -- Ich kann alle Werte zu einem Schlüssel aus einer JSON-Datei ausgeben. → Übung: Alle Namen aus dem Zoo -- Ich kann mit + und \\" einen JSON-Eintrag bauen und speichern. → JSON schreiben -- Ich kann Daten als JSON-Datei speichern. → Übung: Punkte als JSON speichern -- Ich kann eine Stärke und eine Schwäche von JSON nennen. → Stärken und Schwächen von JSON -::: - -## Wissenstest - -::: quiz -? Was steht in JSON vor jedem Wert? -→ Daten mit Namen -- [ ] seine Zeilennummer -- [x] sein Schlüssel, zum Beispiel "name" -- [ ] ein Semikolon -> Schlüssel und Wert werden durch einen Doppelpunkt getrennt. - -? Wie schreibst du ein Anführungszeichen in einem Java-Text? -→ JSON schreiben -- [ ] "" -- [x] \\" -- [ ] '"' - -? Was ist bei {"tiere": [{"name": "Ludwig"}, {"name": "Zora"}]} der Wert von tiere[0].name? -→ Daten mit Namen -- [x] Ludwig -- [ ] Zora -- [ ] tiere -> Die Liste zählt ab 0. - -? Ordne zu. -→ Stärken und Schwächen von JSON -= { } -> ein Objekt mit Schlüsseln und Werten -= [ ] -> eine Liste -= : -> trennt Schlüssel und Wert -::: -`,gw=`--- -titel: Formate im Vergleich -bereich: Dateien -kurz: Dieselben Daten als txt, CSV und JSON vergleichen, das passende Format wählen und eine Datei in ein anderes Format umwandeln. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- dieselben Daten als txt, CSV und JSON erkennen -- Stärken und Schwächen von txt, CSV und JSON vergleichen -- eine CSV-Datei in Sätze umwandeln -- eine CSV-Datei in JSON umwandeln -::: - -## Dreimal dieselben Tiere - -Ludwig hat die Tiere der Burg in drei Dateien gespeichert. In allen steht dasselbe, nur anders aufgeschrieben. - -Als Text, so wie man es einem Menschen schreiben würde: - -::: datei tiere.txt -Ludwig (Geißbock) ist 7 Jahre alt. -Zora (Ziege) ist 6 Jahre alt. -Fuchs (Rotfuchs) ist 4 Jahre alt. -::: - -Als Tabelle in CSV: - -::: datei tiere.csv -Name;Art;Alter -Ludwig;Geißbock;7 -Zora;Ziege;6 -Fuchs;Rotfuchs;4 -::: - -Als JSON: - -::: datei tiere.json -[ - {"name": "Ludwig", "art": "Geißbock", "alter": 7}, - {"name": "Zora", "art": "Ziege", "alter": 6}, - {"name": "Fuchs", "art": "Rotfuchs", "alter": 4} -] -::: - -## Was kann welches Format? - -| | txt | CSV | JSON | -|---|---|---|---| -| Für Menschen gut lesbar | ja | geht so | geht so | -| Feste Ordnung für Programme | nein | ja, Spalten | ja, Schlüssel | -| Was bedeutet ein Wert? | steht im Satz | steht in der Kopfzeile | steht direkt davor | -| Verschachteln (Liste in einem Eintrag) | nein | schwer | ja | -| Öffnen in Excel | nein | ja | nein | -| Lesen in Java | Scanner, BufferedReader | dazu \`split\` | String-Methoden oder eine Bibliothek | -| Größe | klein | am kleinsten | am größten | - -Kurz gesagt: - -- **txt** für Texte, die Menschen lesen: Briefe, Notizen, ein Logbuch. -- **CSV** für Tabellen: Noten, Messwerte, eine Bestenliste. Jede Zeile hat dieselben Spalten. -- **JSON** für Daten mit Struktur: Spielstände, Einstellungen, alles, was zwischen Programmen und im Web hin- und hergeht. - -::: ludwig -Wenn deine Wetter-App die Temperatur holt, kommt die Antwort fast immer als JSON. Und wenn die Lehrkraft die Noten aus dem Schulprogramm exportiert, bekommt sie oft eine CSV-Datei. -::: - -## Übung: Aus CSV werden Sätze - -::: übung l27-java-saetze -titel: Aus CSV werden Sätze -code: java -auftrag: Wandle \`tiere.csv\` in Text um. Schreibe für jedes Tier einen Satz wie **Ludwig (Geißbock) ist 7 Jahre alt.** in die Datei \`saetze.txt\`. Gib am Ende **3 Sätze gespeichert** aus. -start: l27-java-saetze-start.visustruct -test: - -> 3 Sätze gespeichert -datei: saetze.txt -> Ludwig (Geißbock) ist 7 Jahre alt. | Zora (Ziege) ist 6 Jahre alt. | Fuchs (Rotfuchs) ist 4 Jahre alt. -braucht: whileSchleife -tipp: Die Kopfzeile ist im Startcode schon übersprungen. Zerlege jede Zeile mit \`String[] teile = zeile.split(";");\`. -tipp: Der Satz ist \`teile[0] + " (" + teile[1] + ") ist " + teile[2] + " Jahre alt."\`. Zähle mit \`anzahl++\` mit und lies am Ende der Schleife die nächste Zeile. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Übung: Aus CSV wird JSON - -::: übung l27-java-json -titel: Aus CSV wird JSON -code: java -auftrag: Wandle \`tiere.csv\` in JSON um und speichere es in \`tiere-neu.json\`, genau wie \`tiere.json\` oben. Gib am Ende **3 Tiere gespeichert** aus. -start: l27-java-json-start.visustruct -test: - -> 3 Tiere gespeichert -datei: tiere-neu.json -> [ | {"name": "Ludwig", "art": "Geißbock", "alter": 7}, | {"name": "Zora", "art": "Ziege", "alter": 6}, | {"name": "Fuchs", "art": "Rotfuchs", "alter": 4} | ] -braucht: forSchleife, verzweigung -tipp: \`zeilen[0]\` ist die Kopfzeile. Beginne die for-Schleife deshalb bei \`i = 1\` und zerlege \`zeilen[i]\` mit \`split(";")\`. -tipp: Baue den Eintrag wie in Lektion 26 mit \`\\"\`. Das Alter ist eine Zahl und steht ohne Anführungszeichen. Ein Komma hängst du nur an, wenn \`i < zeilen.length - 1\` ist. -lösung: l27-java-json-loesung.visustruct -::: - -## Spiel: Welches Format passt? - -::: spiel frage -titel: Spiel: Welches Format passt? -intro: Ludwig will Daten speichern und weiß nicht, welches Format er nehmen soll. Hilf ihm. Sechs Runden. -antworten: txt | CSV | JSON - -runde: -frage: Die Noten der ganzen Klasse sollen in Excel geöffnet werden. -richtig: CSV -> Noten sind eine Tabelle, und Excel öffnet CSV-Dateien direkt. - -runde: -frage: Ludwig schreibt Zora einen Brief. -richtig: txt -> Ein Brief ist Text für Menschen. Er braucht keine Spalten und keine Schlüssel. - -runde: -frage: Ein Spielstand mit Name, Level und einer Liste aller gesammelten Schlüssel. -richtig: JSON -> Eine Liste in einem Eintrag lässt sich in JSON verschachteln. In CSV passt sie nur schwer in eine Zelle. - -runde: -frage: Ein Messgerät speichert jede Minute Uhrzeit und Temperatur, ein ganzes Jahr lang. -richtig: CSV -> Viele gleiche Zeilen mit denselben Spalten: CSV ist dafür am kleinsten. JSON würde die Schlüssel in jeder Zeile wiederholen. - -runde: -frage: Eine Wetter-App holt die Vorhersage von einem Server im Internet. -richtig: JSON -> JSON ist der Standard im Web. Fast jede App bekommt ihre Daten so. - -runde: -frage: Das Logbuch der Burg, in dem jeder Eintrag ein kurzer Satz ist. -richtig: txt -> Jede Zeile ist ein Satz für Menschen. Gelesen wird sie Zeile für Zeile, wie in Lektion 24. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann dieselben Daten als txt, CSV und JSON erkennen. → Dreimal dieselben Tiere -- Ich kann Stärken und Schwächen von txt, CSV und JSON vergleichen. → Was kann welches Format? -- Ich kann eine CSV-Datei in Sätze umwandeln. → Übung: Aus CSV werden Sätze -- Ich kann eine CSV-Datei in JSON umwandeln. → Übung: Aus CSV wird JSON -::: - -## Wissenstest - -::: quiz -? Welches Format öffnet Excel direkt als Tabelle? -→ Was kann welches Format? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Warum ist eine JSON-Datei meist größer als eine CSV-Datei mit denselben Daten? -→ Was kann welches Format? -- [ ] Weil JSON mehr Zahlen speichert. -- [x] Weil die Schlüssel in jedem Eintrag wiederholt werden. -- [ ] Weil JSON keine Kommas benutzt. - -? In welchem Format sagt jeder Wert selbst, was er bedeutet? -→ Was kann welches Format? -- [ ] txt -- [ ] CSV -- [x] JSON -> In JSON steht der Schlüssel direkt vor dem Wert, zum Beispiel "alter": 7. - -? Ordne jedem Zweck ein Format zu. -→ Was kann welches Format? -= ein Brief an Zora -> txt -= die Notenliste der Klasse -> CSV -= ein Spielstand mit Listen -> JSON -::: -`,pw=`--- -titel: Dein erstes Fenster -bereich: Fenster mit Swing -kurz: Mit Java und Swing ein eigenes Fenster öffnen, Zeile für Zeile erklärt. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- im Bereich Code „Echtes Java“ einschalten -- ein Fenster mit Titel und Größe öffnen -- erklären, was jede Zeile des Fensterprogramms macht -- erklären, warum setVisible(true) am Ende steht -::: - -## Programme mit Fenstern - -Bisher haben deine Programme Text in die Konsole geschrieben. Die meisten Programme, die du kennst, haben aber **Fenster** mit Knöpfen und Textfeldern. Das nennt man eine grafische Oberfläche. - -Java bringt dafür einen Werkzeugkasten mit: **Swing**. In VisuStruct schaltest du ihn so ein: - -1. Öffne rechts den Bereich [Code](#zeige-codelive). -2. Klicke oben rechts auf das [Zahnrad](#zeige-einstellungen) und wähle als **Programmiersprache** Java. -3. Setze darunter im Abschnitt Java den Haken bei **Echtes Java (mit Swing)**. - -Beim ersten [▶︎ Ausführen](#zeige-ausfuehren) lädt VisuStruct einmalig etwa 40 MB, das dauert einen Moment. Danach läuft dein Code als echtes Java, und deine Fenster erscheinen im Bereich Code im Feld **Java-Fenster** über der Konsole. - -> Mit „Echtes Java“ funktionieren Eingaben über die Konsole (\`Scanner\`) noch nicht. Für Programme mit Eingabe schaltest du den Haken wieder aus. - -::: angeberwissen -Swing gibt es seit 1998. Davor hatte Java nur das ältere AWT mit einer Klasse \`Frame\`. Damit man die neuen Bausteine nicht verwechselt, beginnen in Swing fast alle Namen mit **J**: \`JFrame\`, \`JButton\`, \`JLabel\`. Am J erkennst du Swing also sofort. -::: - -## Das ganze Programm - -So sieht das kleinste Programm aus, das ein Fenster öffnet. Tippe es im Bereich Code ab und klicke auf [▶︎ Ausführen](#zeige-ausfuehren): - -\`\`\`java -import javax.swing.*; - -public class MeinFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Mein erstes Fenster"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); - } -} -\`\`\` - -Ein leeres graues Fenster mit dem Titel „Mein erstes Fenster“ erscheint. Mehr nicht, aber es ist dein eigenes Fenster! - -Den Rahmen mit \`public class\` und \`main\` kennst du schon aus der Lektion Java. Neu sind die erste Zeile und die vier Zeilen in \`main\`. Die schauen wir uns jetzt einzeln an. - -::: ludwig -Schau dir das Struktogramm neben dem Code an: Jede Zeile in \`main\` ist ein Kasten, alle untereinander. Ein Fensterprogramm ist am Anfang also nur eine Sequenz. -::: - -## Zeile für Zeile - -| Zeile | Was sie macht | -|---|---| -| \`import javax.swing.*;\` | Holt den Werkzeugkasten Swing ins Programm. Ohne diese Zeile kennt Java \`JFrame\` nicht. | -| \`JFrame fenster = new JFrame("Mein erstes Fenster");\` | Baut ein neues Fenster. Der Text in Klammern ist der Titel oben im Fensterrahmen. Über die Variable \`fenster\` sprichst du es danach an. | -| \`fenster.setSize(300, 200);\` | Das Fenster wird 300 Pixel breit und 200 Pixel hoch. | -| \`fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\` | Schließt jemand das Fenster, endet auch das Programm. | -| \`fenster.setVisible(true);\` | Jetzt erst wird das Fenster sichtbar. | - -Der Punkt in \`fenster.setSize(…)\` bedeutet: „Fenster, mach das!“ Vor dem Punkt steht, wer etwas tun soll, dahinter, was es tun soll. - -> Ohne \`setDefaultCloseOperation\` versteckt Java das Fenster beim Schließen nur. Das Programm läuft dann unsichtbar weiter. - -::: quiz -? Was bedeutet der Punkt in \`fenster.setSize(300, 200)\`? -- [x] „Fenster, mach das!“: Vor dem Punkt steht, wer etwas tut, dahinter, was es tut. -- [ ] Er trennt die beiden Zahlen. -- [ ] Er beendet die Zeile. -> Vor dem Punkt steht das Objekt \`fenster\`, dahinter seine Methode \`setSize\`. -::: - -## Warum setVisible am Ende steht - -\`setVisible(true)\` zeigt das Fenster so, wie es in diesem Moment ist. Deshalb richtest du zuerst alles ein: Titel, Größe, später auch Knöpfe. Erst ganz am Ende machst du das Fenster sichtbar. - -Was du erst nach \`setVisible(true)\` ins Fenster legst, erscheint oft nicht. Java zeichnet das Fenster nämlich nicht von selbst neu. Manchmal taucht es erst auf, wenn man die Größe des Fensters ändert. Solche Fehler sind schwer zu finden. - -::: ludwig -Erst einrichten, dann zeigen. Wie im Theater: Der Vorhang geht erst auf, wenn die Bühne fertig aufgebaut ist. -::: - -Probier es aus: Lösche die Zeile mit \`setVisible(true)\` und führe das Programm noch einmal aus. Das Programm läuft, aber du siehst kein Fenster. - -## Klassen und Objekte - -Das steht nicht im Lehrplan, hilft dir aber, Swing zu verstehen. - -::: angeberwissen -Java ist eigentlich eine **objektorientierte** Sprache. \`JFrame\` ist eine Klasse, also ein Bauplan für Fenster. Mit \`new JFrame(…)\` baut Java nach diesem Bauplan ein echtes Fenster, ein **Objekt**. Du könntest mit zwei \`new\` auch zwei Fenster bauen, beide nach demselben Bauplan. - -\`setSize\` und \`setVisible\` sind Methoden, die jedes Fenster-Objekt kann. Deshalb schreibst du \`fenster.setSize(…)\`: Das Objekt \`fenster\` führt seine Methode aus. Auch dein Programm selbst ist eine Klasse, nämlich \`MeinFenster\`. -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Ludwig hat am Fensterprogramm herumgebastelt. Lies die Zeilen und sag vorher, was passiert. Sechs Runden. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: Was siehst du nach dem Ausführen? -- ein Fenster mit dem Titel „Hallo“ -- [x] gar kein Fenster -- eine Fehlermeldung -> \`setVisible(true)\` fehlt. Das Fenster ist gebaut, bleibt aber unsichtbar. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(200, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); -frage: Wie sieht das Fenster aus? -- breiter als hoch -- [x] höher als breit -- quadratisch -> Die erste Zahl ist die Breite, die zweite die Höhe: 200 breit, 300 hoch. - -runde: - JFrame fenster = new JFrame("Hallo"); - fenster.setSize(300, 200); - fenster.setVisible(true); -frage: Du schließt das Fenster. Was passiert mit dem Programm? -- Es endet. -- [x] Es läuft unsichtbar weiter. -- Das Fenster geht sofort wieder auf. -> Ohne \`setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)\` versteckt Java das Fenster nur. - -runde: - JFrame fenster = new JFrame(); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setVisible(true); -frage: Was steht oben im Fensterrahmen? -- fenster -- JFrame -- [x] nichts, der Titel ist leer -> Ohne Text in den Klammern hat das Fenster keinen Titel. Der Variablenname \`fenster\` erscheint nie im Fenster. - -runde: - public class Test { - public static void main(String[] args) { - JFrame fenster = new JFrame("Test"); - fenster.setVisible(true); - } - } -frage: Was meldet javac? -- [x] cannot find symbol: class JFrame -- nichts, das Fenster erscheint -- missing return statement -> Die Zeile \`import javax.swing.*;\` fehlt. Ohne sie kennt Java \`JFrame\` nicht. - -runde: - JFrame a = new JFrame("Eins"); - JFrame b = new JFrame("Zwei"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: Was siehst du? -- [x] ein Fenster mit dem Titel „Eins“ -- zwei Fenster -- ein Fenster mit dem Titel „Zwei“ -> Zwei Fenster nach demselben Bauplan, aber nur \`a\` wird sichtbar gemacht. -::: - -## Aufgabe: Dein eigenes Fenster - -Ändere das Programm so, dass - -- im Titel „Hallo“ und dein Name steht, -- das Fenster 400 Pixel breit und 300 Pixel hoch ist, -- das Programm endet, wenn du das Fenster schließt. - -Führe es aus und prüfe alle drei Punkte. - -::: ludwig -Du willst nicht so viel tippen? Mit „Echtes Java“ darfst du im Bereich Code auch nur die vier Zeilen aus \`main\` schreiben. VisuStruct packt sie dann selbst in eine Klasse und ergänzt die \`import\`-Zeilen. In einer richtigen Java-Entwicklungsumgebung brauchst du aber das ganze Programm. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann im Bereich Code „Echtes Java“ einschalten. → Programme mit Fenstern -- Ich kann ein Fenster mit Titel und Größe öffnen. → Das ganze Programm -- Ich kann erklären, was jede Zeile des Fensterprogramms macht. → Zeile für Zeile -- Ich kann erklären, warum setVisible(true) am Ende steht. → Warum setVisible am Ende steht -::: - -## Wissenstest - -::: quiz -? Welche Zeile macht das Fenster sichtbar? -→ Warum setVisible am Ende steht -- [ ] fenster.setSize(300, 200); -- [x] fenster.setVisible(true); -- [ ] new JFrame("Hallo"); -> Ohne setVisible(true) bleibt das Fenster unsichtbar. - -? Wo steht fenster.setVisible(true) am besten? -→ Warum setVisible am Ende steht -- [ ] Ganz am Anfang von main -- [x] Am Ende, wenn das Fenster fertig eingerichtet ist -- [ ] Das ist egal. -> Was nach setVisible(true) dazukommt, erscheint oft nicht. - -? Was bewirkt setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)? -→ Zeile für Zeile -- [ ] Das Fenster schließt sich sofort. -- [x] Schließt man das Fenster, endet das Programm. -- [ ] Das Fenster lässt sich nicht mehr schließen. - -? Woran erkennst du Bausteine aus Swing? -→ Zeile für Zeile -- [x] Ihr Name beginnt mit J, z. B. JFrame. -- [ ] Sie stehen in geschweiften Klammern. -- [ ] Sie enden mit Window. -::: -`,fw=`--- -titel: Ein Knopf im Fenster -bereich: Fenster mit Swing -kurz: Einen Knopf ins Fenster legen und mit einem Layout ordentlich anordnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Knopf bauen und mit add ins Fenster legen -- erklären, warum der Knopf vor setVisible(true) hineinkommt -- mit FlowLayout dafür sorgen, dass der Knopf normal groß ist -- ein Fenster mit einem Knopf bauen, den man drücken kann -::: - -## Einen Knopf bauen - -Ein leeres Fenster ist langweilig. Jetzt kommt ein Knopf hinein. In Swing heißt ein Knopf **JButton** (button ist englisch für Knopf). - -Zwei neue Zeilen brauchst du dafür: - -\`\`\`java -JButton knopf = new JButton("Klick mich"); -fenster.add(knopf); -\`\`\` - -- \`new JButton("Klick mich")\` baut einen Knopf. Der Text in Klammern steht später auf dem Knopf. -- \`fenster.add(knopf)\` legt den Knopf ins Fenster. Ohne \`add\` gibt es den Knopf zwar, aber er liegt nirgends und du siehst ihn nicht. - -::: quiz -? Was steht auf dem Knopf nach \`new JButton("Start")\`? -- [ ] knopf -- [x] Start -- [ ] JButton -> Der Text in Klammern ist die Beschriftung. \`knopf\` ist nur der Name der Variable. -::: - -## Das ganze Programm - -Beide Zeilen kommen **vor** \`setVisible(true)\`, denn das Fenster soll erst fertig eingerichtet und dann gezeigt werden: - -\`\`\`java -import javax.swing.*; - -public class KnopfFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton knopf = new JButton("Klick mich"); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -\`\`\` - -Führe das Programm aus. Überraschung: Der Knopf ist riesig und füllt das ganze Fenster! - -::: ludwig -Die Reihenfolge ist wie beim ersten Fenster: einrichten, Knopf bauen, Knopf hineinlegen und ganz zum Schluss zeigen. Die Leerzeilen im Code helfen dir, diese Teile zu sehen. -::: - -## Der Riesenknopf - -Wie ein Fenster seine Teile anordnet, bestimmt sein **Layout**. Ein \`JFrame\` legt ohne weitere Angabe alles in die Mitte und macht es so groß wie das ganze Fenster. - -Mit einem anderen Layout bekommt der Knopf seine normale Größe. Das **FlowLayout** stellt alles wie Wörter in einer Zeile nebeneinander, so groß wie nötig: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class KnopfFenster { - public static void main(String[] args) { - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new FlowLayout()); - - JButton knopf = new JButton("Klick mich"); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -\`\`\` - -\`FlowLayout\` gehört zum älteren Werkzeugkasten AWT. Deshalb steht oben eine zweite Zeile: \`import java.awt.*;\`. - -::: angeberwissen -Beim FlowLayout fließen die Bausteine wie Text: Passt ein Knopf nicht mehr in die Zeile, rutscht er in die nächste. Mach das Fenster schmal und schau zu, wie die Knöpfe umbrechen. Flow ist englisch für Fluss. -::: - -## Klicken geht, aber nichts passiert - -Klicke auf den Knopf. Er wird kurz dunkler, wenn du ihn drückst. Swing zeigt also, dass er gedrückt wurde. Sonst passiert aber nichts. - -Das ist kein Fehler: Du hast dem Programm noch nicht gesagt, **was** beim Klick passieren soll. Das lernst du in der nächsten Lektion. - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Fensterprogramm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was das Programm tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sechs Runden, je 25 Sekunden. -zeit: 25 - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - JButton knopf = new JButton(Klick mich); - fenster.add(knopf); - fenster.setVisible(true); -frage: Soll einen Knopf „Klick mich“ zeigen. -fehler: 3 -besser: JButton knopf = new JButton("Klick mich"); -> Die Beschriftung ist ein Text und braucht Anführungszeichen. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - JButton knopf = new JButton("OK"); - fenster.add(fenster); - fenster.setVisible(true); -frage: Soll den Knopf „OK“ ins Fenster legen. -fehler: 4 -besser: fenster.add(knopf); -> Ins Fenster gehört der Knopf, nicht das Fenster selbst. - -runde: - import java.swing.*; - import java.awt.*; -frage: Soll Swing und das FlowLayout ins Programm holen. -fehler: 1 -besser: import javax.swing.*; -> Swing liegt in \`javax.swing\`, mit x. Das FlowLayout kommt aus \`java.awt\`, ohne x. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout); - fenster.add(new JButton("OK")); - fenster.setVisible(true); -frage: Soll den Knopf in normaler Größe zeigen. -fehler: 3 -besser: fenster.setLayout(new FlowLayout()); -> Nach \`new\` und dem Namen kommen immer runde Klammern, auch wenn nichts darin steht. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout()); - JButton knopf = new Jbutton("OK"); - fenster.add(knopf); - fenster.setVisible(true); -frage: Soll einen Knopf „OK“ zeigen. -fehler: 4 -besser: JButton knopf = new JButton("OK"); -> Java unterscheidet Groß- und Kleinschreibung: \`JButton\` mit großem B. - -runde: - JFrame fenster = new JFrame("Knopf"); - fenster.setSize(300, 200); - fenster.setLayout(new FlowLayout()); - fenster.add(new JButton("OK")); - fenster.setVisible(false); -frage: Soll das Fenster mit dem Knopf zeigen. -fehler: 5 -besser: fenster.setVisible(true); -> Mit \`false\` bleibt das Fenster unsichtbar. -::: - -## Aufgabe: Klick mich - -Baue ein Fenster mit dem Titel „Knopf-Test“, 300 mal 200 Pixel groß, mit einem Knopf „Klick mich“ in normaler Größe. Der Knopf soll sich drücken lassen, auch wenn noch nichts passiert. - -Zusatz: Lege einen zweiten Knopf „Ich auch“ daneben. In welcher Reihenfolge stehen die Knöpfe? Tausche die beiden \`add\`-Zeilen und probiere es noch einmal. - -**Du bist fertig, wenn …** - -- oben im Fenster „Knopf-Test“ steht, -- das Fenster 300 Pixel breit und 200 Pixel hoch ist, -- der Knopf „Klick mich“ nur so groß ist wie seine Schrift und nicht das ganze Fenster füllt, -- sich der Knopf beim Klicken sichtbar eindrückt. - -::: ludwig -Jeder Knopf braucht eine eigene Variable, zum Beispiel \`knopf\` und \`knopf2\`, und ein eigenes \`add\`. Das FlowLayout stellt die Knöpfe in der Reihenfolge der \`add\`-Zeilen auf. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Knopf bauen und mit add ins Fenster legen. → Einen Knopf bauen -- Ich kann erklären, warum der Knopf vor setVisible(true) hineinkommt. → Das ganze Programm -- Ich kann mit FlowLayout dafür sorgen, dass der Knopf normal groß ist. → Der Riesenknopf -- Ich kann ein Fenster mit einem Knopf bauen, den man drücken kann. → Aufgabe: Klick mich -::: - -## Wissenstest - -::: quiz -? Was macht fenster.add(knopf)? -→ Einen Knopf bauen -- [ ] Es baut einen neuen Knopf. -- [x] Es legt den Knopf ins Fenster. -- [ ] Es macht den Knopf klickbar. -> Gebaut wird der Knopf mit new JButton(…). - -? Warum füllt der Knopf ohne FlowLayout das ganze Fenster? -→ Der Riesenknopf -- [ ] Weil der Text zu lang ist -- [x] Weil das Fenster ohne eigenes Layout alles so groß wie das Fenster macht -- [ ] Weil setSize fehlt - -? Welche import-Zeile brauchst du für FlowLayout? -→ Der Riesenknopf -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> FlowLayout stammt aus dem älteren AWT. - -? Was passiert beim Klick auf den Knopf in dieser Lektion? -→ Aufgabe: Klick mich -- [ ] Das Programm endet. -- [x] Der Knopf wird kurz dunkler, sonst passiert nichts. -- [ ] Auf dem Knopf steht „Ich wurde geklickt“. -> Was beim Klick passieren soll, lernst du in der nächsten Lektion. -::: -`,bw=`--- -titel: Der Knopf reagiert -bereich: Fenster mit Swing -kurz: Mit einem ActionListener festlegen, was beim Klick passiert. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Ereignis ist -- mit addActionListener festlegen, was beim Klick passiert -- beim Klick mehrere Anweisungen ausführen -- einen Knopf bauen, der nach dem Klick seinen Text ändert -::: - -## Ein Klick ist ein Ereignis - -In der letzten Lektion ließ sich der Knopf drücken, aber nichts geschah. Bei einem Fensterprogramm bestimmt nämlich der Mensch, wann etwas passiert: Er klickt, tippt oder schließt ein Fenster. Jede solche Aktion heißt **Ereignis**. - -Dein Programm muss deshalb auf den Klick warten und ihn bemerken. Dafür meldest du beim Knopf einen Zuhörer an, einen **ActionListener** (englisch für „Aktions-Zuhörer“). Bei jedem Klick ruft der Knopf seinen Zuhörer, und der führt deinen Code aus. - -::: ludwig -Bisher lief dein Programm von oben nach unten durch, wie im Struktogramm. Der Code im Zuhörer läuft dagegen erst später, nämlich immer dann, wenn jemand klickt. Wie oft das ist, weiß vorher niemand. -::: - -## Die Zeile mit dem Pfeil - -So meldest du den Zuhörer an: - -\`\`\`java -knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); -\`\`\` - -Von außen nach innen gelesen: - -- \`knopf.addActionListener( … )\` meldet beim Knopf einen Zuhörer an. -- \`e\` ist das Ereignis. Darin steht zum Beispiel, wann geklickt wurde. Hier brauchst du es nicht, aber der Name muss dastehen. -- \`->\` liest du als „dann mach“. -- \`knopf.setText("Ich wurde geklickt")\` ist das, was beim Klick passiert: Der Knopf bekommt eine neue Beschriftung. - -Die Zeile steht nach \`new JButton(…)\`, denn den Knopf muss es schon geben. Und wie alles andere kommt sie vor \`setVisible(true)\`. - -::: quiz -? Wofür steht das \`e\` in \`e -> knopf.setText(…)\`? -- [x] Für das Ereignis, also den Klick -- [ ] Für „Ende“ -- [ ] Für einen Fehler -> Hier brauchst du \`e\` nicht, aber der Name muss dastehen. -::: - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class KlickMich { - public static void main(String[] args) { - JFrame fenster = new JFrame("Klick mich"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new FlowLayout()); - - JButton knopf = new JButton("Klick mich"); - knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -\`\`\` - -Führe es aus und klicke auf den Knopf. Jetzt steht darauf „Ich wurde geklickt“. - -> Ist der neue Text länger als der alte, macht das FlowLayout den Knopf von selbst breiter. - -## Mehrere Anweisungen beim Klick - -Soll beim Klick mehr als eine Sache passieren, setzt du hinter den Pfeil geschweifte Klammern. Darin steht jede Anweisung mit Semikolon, wie in einem Schleifenrumpf: - -\`\`\`java -knopf.addActionListener(e -> { - knopf.setText("Ich wurde geklickt"); - fenster.setTitle("Danke!"); - System.out.println("Klick!"); -}); -\`\`\` - -\`System.out.println\` schreibt wie gewohnt in die [Konsole](#zeige-konsole). Klicke mehrmals: Bei jedem Klick erscheint eine neue Zeile „Klick!“. So siehst du, dass der Code im Zuhörer wirklich bei jedem Klick läuft. - -::: ludwig -Achte auf das Ende: Erst schließt \`}\` den Block, dann \`)\` die Klammer von \`addActionListener\`, dann kommt das Semikolon. Also \`});\`. Fehlt etwas davon, steht unter dem Code ein Hinweis. -::: - -## Spiel: Wer hört zu? - -::: spiel zuhoerer -titel: Spiel: Wer hört zu? -intro: Ludwig zeigt dir Code und das Fenster dazu. Darunter steht, welche Knöpfe nacheinander geklickt werden. Was passiert? Nach deiner Antwort spielt Ludwig die Klicks ab: Du siehst, welcher Zuhörer das Ereignis hört und welche Zeilen dann laufen. -frage: Was steht nach den Klicks in der Beschriftung? - -runde: - JFrame fenster = new JFrame("Gruß"); - JLabel text = new JLabel("Start"); - JButton knopf = new JButton("Hallo"); - knopf.addActionListener(e -> text.setText("Hallo Welt")); -klicks: Hallo -- Start -- [x] Hallo Welt -- Hallo -> Der Knopf hat einen Zuhörer. Beim Klick führt er \`text.setText("Hallo Welt")\` aus, und die Beschriftung zeigt „Hallo Welt“. - -runde: - JFrame fenster = new JFrame("Obst"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apfel")); - b.addActionListener(e -> text.setText("Birne")); -klicks: A, B -- Apfel -- [x] Birne -- Apfel Birne -> Jeder Knopf hat seinen eigenen Zuhörer. Erst schreibt A „Apfel“ hinein, dann überschreibt B das mit „Birne“. \`setText\` ersetzt den alten Text, es hängt nichts an. - -runde: - JFrame fenster = new JFrame("Obst"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apfel")); -klicks: B -- Apfel -- Birne -- [x] ? -> Beim Knopf B hat sich niemand angemeldet: Es gibt kein \`b.addActionListener\`. Der Klick ist ein Ereignis, aber niemand hört zu. Deshalb bleibt das Fragezeichen stehen. - -runde: - JFrame fenster = new JFrame("Farben"); - JLabel text = new JLabel("keine"); - JButton rot = new JButton("Rot"); - JButton blau = new JButton("Blau"); - rot.addActionListener(e -> text.setText("Blau")); - blau.addActionListener(e -> text.setText("Rot")); -klicks: Rot -- Rot -- [x] Blau -- keine -> Aufgepasst: Was passiert, entscheidet der Code im Zuhörer, nicht die Beschriftung des Knopfs. Der Zuhörer am Knopf „Rot“ schreibt „Blau“ hinein. - -runde: - JFrame fenster = new JFrame("Knopf"); - JLabel text = new JLabel("Bitte klicken"); - JButton knopf = new JButton("Klick mich"); - knopf.addActionListener(e -> { - knopf.setText("Danke!"); - text.setText("Geklickt"); - }); -klicks: Klick mich -frage: Was steht nach dem Klick auf dem Knopf? -- Klick mich -- [x] Danke! -- Geklickt -> Im Block hinter dem Pfeil stehen zwei Anweisungen. Beide laufen beim Klick nacheinander: Der Knopf bekommt „Danke!“, die Beschriftung „Geklickt“. - -runde: - JFrame fenster = new JFrame("Konsole"); - JButton knopf = new JButton("Los"); - knopf.addActionListener(e -> System.out.println("Klick!")); -klicks: Los, Los, Los -frage: Wie viele Zeilen „Klick!“ stehen danach in der Konsole? -- 1 -- [x] 3 -- 0 -> Der Zuhörer läuft bei **jedem** Klick, nicht nur beim ersten. Drei Klicks, drei Zeilen. -::: - -## ActionListener ist ein Interface - -Das steht nicht im Lehrplan. Wenn du wissen willst, was hinter dem Pfeil steckt, lies weiter. - -::: angeberwissen -\`ActionListener\` ist ein **Interface**, eine Art Vertrag. Darin steht: Wer ein ActionListener sein will, muss eine Methode \`actionPerformed(ActionEvent e)\` haben. Der Knopf ruft bei jedem Klick genau diese Methode auf. -::: - -Die Zeile mit dem Pfeil kennst du schon aus Schritt 3: - -\`\`\`java -knopf.addActionListener(e -> knopf.setText("Ich wurde geklickt")); -\`\`\` - -Ohne Pfeil schreibt man den Zuhörer als Klasse ohne Namen, die den Vertrag erfüllt (eine anonyme Klasse): - -\`\`\`java -knopf.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - knopf.setText("Ich wurde geklickt"); - } -}); -\`\`\` - -Dafür brauchst du oben noch \`import java.awt.event.*;\`. Beide Schreibweisen machen genau dasselbe. Der Pfeil \`e -> …\` ist die Kurzform (ein Lambda-Ausdruck, seit Java 8): \`e\` ist der Parameter von \`actionPerformed\`, und hinter dem Pfeil steht ihr Rumpf. Die Kurzform geht, weil das Interface nur eine einzige Methode hat. Java weiß also, welche Methode gemeint ist. - -## Spiel: Vertrag erfüllt? - -::: spiel vertrag -titel: Spiel: Vertrag erfüllt? -intro: Der Vertrag \`ActionListener\` verlangt genau eine Methode: \`public void actionPerformed(ActionEvent e)\`. Ludwig zeigt dir Code. Erfüllt er den Vertrag, sodass man ihn beim Knopf anmelden kann? Jede Antwort hat Ludwig mit dem echten Java-Compiler geprüft. -frage: Erfüllt das den Vertrag ActionListener? -antworten: erfüllt | nicht erfüllt - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Alles da: \`implements ActionListener\` unterschreibt den Vertrag, und die Methode heißt genau \`actionPerformed\`, ist \`public\`, gibt nichts zurück (\`void\`) und bekommt ein \`ActionEvent\`. - -runde: - class Gruss implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Schau genau hin: \`actionPerfomed\` statt \`actionPerformed\`, ein r fehlt. Für Java ist das eine ganz andere Methode. Der Compiler meldet, dass \`actionPerformed\` fehlt. - -runde: - class Gruss implements ActionListener { - public void actionPerformed() { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Der Parameter fehlt. Der Vertrag verlangt \`actionPerformed(ActionEvent e)\`, denn der Knopf übergibt beim Klick das Ereignis. - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent ereignis) { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Der Name des Parameters ist frei wählbar. Wichtig ist nur der Typ \`ActionEvent\`. Statt \`e\` darf er also auch \`ereignis\` heißen. - -runde: - class Gruss implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } -richtig: nicht erfüllt -> Das \`public\` fehlt. Alle Methoden eines Interfaces sind öffentlich, denn der Knopf muss sie von außen aufrufen können. Ohne \`public\` meldet der Compiler einen Fehler. - -runde: - class Gruss implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: nicht erfüllt -> Der Vertrag sagt \`void\`: Die Methode gibt nichts zurück. Mit \`int\` als Rückgabetyp passt sie nicht mehr. - -runde: - class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - sagHallo(); - } - void sagHallo() { - System.out.println("Hallo"); - } - } -richtig: erfüllt -> Zusätzliche Methoden sind erlaubt. Der Vertrag verlangt nur, dass \`actionPerformed\` da ist, nicht, dass sonst nichts da ist. - -runde: - class Gruss { - public void actionPerformed(ActionEvent e) { - System.out.println("Hallo"); - } - } - knopf.addActionListener(new Gruss()); -richtig: nicht erfüllt -> Die Methode stimmt, aber der Vertrag ist nicht unterschrieben: Es fehlt \`implements ActionListener\`. Java nimmt beim Knopf nur Objekte an, die den Vertrag offiziell erfüllen. - -runde: - knopf.addActionListener(e -> System.out.println("Hallo")); -richtig: erfüllt -> Der Pfeil ist die Kurzform. Weil der Vertrag nur eine Methode hat, weiß Java: \`e\` ist der Parameter von \`actionPerformed\`, und hinter dem Pfeil steht ihr Rumpf. - -runde: - knopf.addActionListener((a, b) -> System.out.println("Hallo")); -richtig: nicht erfüllt -> Hier hat der Pfeil zwei Parameter. \`actionPerformed\` bekommt aber genau einen, das Ereignis. Darum passt diese Kurzform nicht zum Vertrag. -::: - -## Aufgabe: Ich wurde geklickt - -Jetzt machst du die Aufgabe fertig: Baue ein Fenster mit einem Knopf „Klick mich“ in normaler Größe. Nach dem Klick steht auf dem Knopf „Ich wurde geklickt“. - -Zusatz: Baue zwei Knöpfe „Rot“ und „Blau“. Ein Klick auf einen Knopf schreibt seinen Namen in den Titel des Fensters, zum Beispiel „Du hast Rot gewählt“. - -**Du bist fertig, wenn …** - -- vor dem Klick „Klick mich“ auf dem Knopf steht, -- nach dem Klick „Ich wurde geklickt“ darauf steht, -- der Knopf nicht das ganze Fenster füllt. - -::: ludwig -Jeder Knopf bekommt seinen eigenen Zuhörer mit eigenem \`addActionListener\`. Den Titel änderst du mit \`fenster.setTitle(…)\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Ereignis ist. → Ein Klick ist ein Ereignis -- Ich kann mit addActionListener festlegen, was beim Klick passiert. → Die Zeile mit dem Pfeil -- Ich kann beim Klick mehrere Anweisungen ausführen. → Mehrere Anweisungen beim Klick -- Ich kann einen Knopf bauen, der nach dem Klick seinen Text ändert. → Aufgabe: Ich wurde geklickt -::: - -## Wissenstest - -::: quiz -? Was ist ein ActionListener? -→ Ein Klick ist ein Ereignis -- [ ] Ein Knopf mit Text -- [x] Ein Zuhörer, der bei jedem Klick deinen Code ausführt -- [ ] Ein Layout für Fenster -> Du meldest ihn mit addActionListener beim Knopf an. - -? Wann läuft knopf.setText("Ich wurde geklickt") im Zuhörer? -→ Die Zeile mit dem Pfeil -- [ ] Sofort beim Start des Programms -- [x] Jedes Mal, wenn jemand auf den Knopf klickt -- [ ] Nur beim ersten Klick -> Der Code im Zuhörer läuft erst, wenn das Ereignis eintritt. - -? Wie endet ein Zuhörer mit geschweiften Klammern? -→ Die Zeile mit dem Pfeil -- [ ] };) -- [x] }); -- [ ] );} -> Erst schließt der Block, dann die Klammer von addActionListener, dann das Semikolon. - -? Ordne jedem Teil seine Aufgabe zu. -→ Die Zeile mit dem Pfeil -= JButton -> ein Knopf -= addActionListener -> Zuhörer anmelden -= setText -> Beschriftung ändern -= setVisible(true) -> Fenster zeigen -::: -`,xw=`--- -titel: Zahl verdoppeln im Fenster -bereich: Fenster mit Swing -kurz: Beschriftung, Textfeld und Knopf mit einem GridLayout anordnen und mit dem Text im Textfeld rechnen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- JLabel und JTextField ins Fenster legen -- erklären, warum ohne Layout nur der letzte Baustein zu sehen ist -- mit GridLayout(0, 1) alles untereinander anordnen -- mit getText, Integer.parseInt und setText rechnen -- ein Programm schreiben, das bei „abc“ nicht abstürzt -::: - -## Das Ziel - -Wir bauen ein kleines Rechenprogramm mit Fenster: Oben steht eine Beschriftung, darunter ein Textfeld und ein Knopf **Berechne**. Gibst du 2 ein und klickst, steht im Textfeld 4. Klickst du noch einmal, wird daraus 8, dann 16 und so weiter. - -Dafür brauchst du zwei neue Bausteine: - -- **JLabel**: eine Beschriftung, also einfacher Text im Fenster. -- **JTextField**: ein Textfeld, in das man etwas tippen kann. - -Schalte wie in den letzten Lektionen im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Drei Bausteine ins Fenster - -Wir legen alle drei Bausteine ins Fenster, genau wie den Knopf in Lektion 29: - -\`\`\`java -import javax.swing.*; - -public class Verdoppeln { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahl verdoppeln"); - fenster.setSize(250, 150); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel beschriftung = new JLabel("Gib eine Zahl ein:"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Berechne"); - fenster.add(beschriftung); - fenster.add(feld); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -\`\`\` - -Führe das Programm aus. Hoppla: Man sieht nur den riesigen Knopf. Wo sind die Beschriftung und das Textfeld? - -## Warum nur der Knopf zu sehen ist - -::: ludwig -Erinnerst du dich an den Riesenknopf? Ein \`JFrame\` hat ohne eigenes Layout nur **einen** Platz: die Mitte. Jedes \`add\` legt den neuen Baustein genau dorthin, so groß wie das ganze Fenster. Die drei Bausteine landen also übereinander auf demselben Platz, und das Fenster zeigt nur den zuletzt hineingelegten: den Knopf. Benutzen kannst du das Programm so nicht. -::: - -Die Lösung ist wieder ein Layout. Diesmal nehmen wir das **GridLayout**. Es teilt das Fenster in gleich große Zellen wie ein Gitter: - -\`\`\`java -fenster.setLayout(new GridLayout(0, 1)); -\`\`\` - -Die beiden Zahlen sind Zeilen und Spalten. \`0\` Zeilen heißt „so viele wie nötig“, \`1\` Spalte heißt: alles untereinander. Für \`GridLayout\` brauchst du oben außerdem \`import java.awt.*;\`. - -| Layout | So ordnet es an | -|---|---| -| ohne Layout | alles in die Mitte, übereinander | -| \`FlowLayout\` | nebeneinander wie Wörter, Zeilenumbruch wenn nötig | -| \`GridLayout(0, 1)\` | untereinander, jede Zeile gleich hoch | - -## Das Fenster mit GridLayout - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Verdoppeln { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahl verdoppeln"); - fenster.setSize(250, 150); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel beschriftung = new JLabel("Gib eine Zahl ein:"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Berechne"); - fenster.add(beschriftung); - fenster.add(feld); - fenster.add(knopf); - - fenster.setVisible(true); - } -} -\`\`\` - -Jetzt stehen alle drei untereinander. Du kannst ins Textfeld tippen und den Knopf drücken. Rechnen tut das Programm aber noch nicht. - -::: angeberwissen -Mit \`new GridLayout(2, 3)\` bekommst du 2 Zeilen mit je 3 Spalten, also 6 gleich große Zellen. So baut man zum Beispiel die Tasten eines Taschenrechners. Die Bausteine füllen die Zellen in der Reihenfolge der \`add\`-Zeilen, Zeile für Zeile von links nach rechts. -::: - -## Mit dem Text im Textfeld rechnen - -Beim Klick soll das Programm die Zahl aus dem Textfeld holen, verdoppeln und wieder hineinschreiben. Das machen drei Methoden: - -- \`feld.getText()\` liefert den Text im Textfeld, zum Beispiel \`"2"\`. -- \`Integer.parseInt(…)\` macht daraus die Zahl 2. Das kennst du aus Lektion 22. -- \`feld.setText(…)\` schreibt einen neuen Text ins Textfeld. - -Zusammen mit dem ActionListener aus Lektion 30 sieht das so aus: - -\`\`\`java -knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); -}); -\`\`\` - -\`setText\` braucht einen Text. \`"" + zahl * 2\` hängt das Ergebnis an einen leeren Text und macht so aus der Zahl einen Text. - -## Keine Zahl? Kein Absturz! - -Tippe „abc“ ins Textfeld und klicke auf Berechne. Das Fenster bleibt, aber es passiert nichts, und in der [Konsole](#zeige-konsole) steht eine \`NumberFormatException\`. Aus Lektion 22 weißt du, was zu tun ist: - -\`\`\`java -knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } -}); -\`\`\` - -Statt eines Fehlers in der Konsole sieht man jetzt einen Hinweis im Fenster. - -::: ludwig -Der Pfeil hat schon ein \`e\` für das Ereignis. Deshalb braucht die Exception im \`catch\` einen anderen Namen, hier \`fehler\`. Zwei Variablen mit demselben Namen erlaubt Java nicht. -::: - -::: quiz -? Warum heißt die Exception im \`catch\` hier \`fehler\` und nicht \`e\`? -- [ ] \`catch\` verlangt immer den Namen \`fehler\`. -- [x] \`e\` ist schon das Ereignis des Pfeils, und zwei Variablen mit demselben Namen erlaubt Java nicht. -- [ ] \`e\` ist nur für Zahlen erlaubt. -> Den Namen darfst du frei wählen, nur nicht doppelt. -::: - -## Spiel: Was steht danach im Fenster? - -::: spiel frage -titel: Spiel: Was steht danach im Fenster? -intro: Ludwig tippt etwas ins Textfeld und klickt auf Berechne. Lies den Zuhörer und sag vorher, was danach im Fenster steht. Sechs Runden. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - }); -frage: Im Textfeld steht 21. Was steht nach dem Klick darin? -- 2121 -- [x] 42 -- 21 -> \`parseInt\` macht aus dem Text die Zahl 21. Das Doppelte ist 42. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - }); -frage: Im Textfeld steht 21. Du klickst zweimal. Was steht danach darin? -- 42 -- [x] 84 -- 4242 -> Jeder Klick verdoppelt, was gerade im Feld steht: 21, 42, 84. - -runde: - knopf.addActionListener(e -> { - feld.setText(feld.getText() + feld.getText()); - }); -frage: Im Textfeld steht 21. Was steht nach dem Klick darin? -- 42 -- [x] 2121 -- 21 -> Ohne \`parseInt\` bleibt es ein Text, und \`+\` hängt Texte aneinander. - -runde: - knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } - }); -frage: Im Textfeld steht abc. Was passiert beim Klick? -- [x] Die Beschriftung zeigt „Bitte eine ganze Zahl eingeben!“ -- Im Textfeld steht abcabc. -- Das Fenster stürzt ab. -> \`parseInt\` wirft eine NumberFormatException, und \`catch\` fängt sie ab. - -runde: - knopf.addActionListener(e -> { - try { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl * 2); - } catch (NumberFormatException fehler) { - beschriftung.setText("Bitte eine ganze Zahl eingeben!"); - } - }); -frage: Im Textfeld steht 2.5. Was passiert beim Klick? -- Im Textfeld steht 5.0. -- Im Textfeld steht 5. -- [x] Die Beschriftung zeigt „Bitte eine ganze Zahl eingeben!“ -> \`Integer.parseInt\` kennt nur ganze Zahlen. 2.5 ist für ihn keine Zahl. - -runde: - knopf.addActionListener(e -> { - int zahl = Integer.parseInt(feld.getText()); - feld.setText("" + zahl + zahl); - }); -frage: Im Textfeld steht 5. Was steht nach dem Klick darin? -- 10 -- [x] 55 -- 5 -> \`"" + zahl\` ist schon ein Text, und das zweite \`+\` hängt die 5 noch einmal an. Mit \`"" + (zahl + zahl)\` wird zuerst gerechnet. -::: - -## Aufgabe: Zahl verdoppeln - -Baue das ganze Programm zusammen: - -- ein Fenster mit dem Titel „Zahl verdoppeln“, 250 Pixel breit und 150 Pixel hoch, -- untereinander eine Beschriftung, ein Textfeld und ein Knopf „Berechne“, -- jeder Klick verdoppelt die Zahl im Textfeld: 2, 4, 8, 16, … -- bei „abc“ erscheint in der Beschriftung ein Hinweis statt eines Absturzes. - -Zusatz: Ein zweiter Knopf „Halbieren“ teilt die Zahl durch 2. Was passiert bei 5? - -**Du bist fertig, wenn …** - -- der Titel „Zahl verdoppeln“ heißt und Beschriftung, Textfeld und Knopf untereinander stehen, -- aus 2 nach drei Klicks nacheinander 4, 8 und 16 werden, -- bei „abc“ ein Hinweis in der Beschriftung steht und das Programm weiterläuft. - -::: ludwig -Der Knopf „Halbieren“ kommt als vierte Zeile ins GridLayout und bekommt seinen eigenen ActionListener. Bei \`int\` schneidet Java beim Teilen die Nachkommastellen ab. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann JLabel und JTextField ins Fenster legen. → Drei Bausteine ins Fenster -- Ich kann erklären, warum ohne Layout nur der letzte Baustein zu sehen ist. → Warum nur der Knopf zu sehen ist -- Ich kann mit GridLayout(0, 1) alles untereinander anordnen. → Das Fenster mit GridLayout -- Ich kann mit getText, Integer.parseInt und setText rechnen. → Mit dem Text im Textfeld rechnen -- Ich kann ein Programm schreiben, das bei „abc“ nicht abstürzt. → Keine Zahl? Kein Absturz! -::: - -## Wissenstest - -::: quiz -? Warum ist ohne Layout nur der Knopf zu sehen? -→ Warum nur der Knopf zu sehen ist -- [ ] Weil Beschriftung und Textfeld fehlen -- [x] Weil alle Bausteine in der Mitte übereinander liegen und der letzte die anderen verdeckt -- [ ] Weil das Fenster zu klein ist - -? Was bedeutet new GridLayout(0, 1)? -→ Das Fenster mit GridLayout -- [ ] Keine Zeile und eine Spalte, also ist das Fenster leer -- [x] So viele Zeilen wie nötig in einer Spalte, also alles untereinander -- [ ] Eine Zeile, alles nebeneinander - -? Was liefert feld.getText()? -→ Mit dem Text im Textfeld rechnen -- [ ] Immer eine Zahl -- [x] Den Text im Textfeld -- [ ] Den Titel des Fensters -> Für eine Zahl brauchst du noch Integer.parseInt. - -? Ordne jedem Baustein seine Aufgabe zu. -→ Drei Bausteine ins Fenster -= JLabel -> Beschriftung -= JTextField -> Textfeld zum Tippen -= JButton -> Knopf -= GridLayout -> Anordnung im Gitter -::: -`,vw=`--- -titel: Klickzähler mit Reset -bereich: Fenster mit Swing -kurz: Zwei Knöpfe ändern dieselbe Zahl: eine Variable für die ganze Klasse anlegen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, warum ein Zuhörer eine Variable aus main nicht ändern darf -- eine static-Variable in der Klasse anlegen -- zwei Knöpfe dieselbe Zahl ändern lassen -- einen Zähler mit −1 bauen, der nie unter 0 fällt -::: - -## Das Ziel - -Ein Fenster zählt Klicks: Oben steht die Zahl, darunter zwei Knöpfe. **+1** zählt eins hoch, **Reset** setzt die Zahl wieder auf 0. - -Neu ist diesmal: Zwei Knöpfe arbeiten mit **derselben** Zahl. Dafür brauchst du eine Variable, die beide Zuhörer kennen. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Der erste Versuch - -Naheliegend wäre, die Zahl wie gewohnt in \`main\` anzulegen und im Zuhörer hochzuzählen: - -\`\`\`java -int anzahl = 0; -knopf.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); -}); -\`\`\` - -Führst du das mit echtem Java aus, meldet der Compiler einen Fehler: - -\`\`\` -local variables referenced from a lambda expression must be final or effectively final -\`\`\` - -::: ludwig -Der Zuhörer läuft erst später, bei jedem Klick, wenn \`main\` längst fertig ist. Deshalb darf er die Variablen aus \`main\` zwar lesen, aber nicht verändern. Java besteht darauf: Eine Variable aus \`main\`, die im Pfeil vorkommt, muss gleich bleiben. Für eine Zahl, die sich bei jedem Klick ändert, brauchen wir einen anderen Platz. -::: - -## Eine Variable für die ganze Klasse - -Die Lösung: Die Variable kommt **nicht** in \`main\`, sondern direkt in die Klasse, über \`main\`. Dann gehört sie der ganzen Klasse, und jeder Zuhörer darf sie ändern: - -\`\`\`java -public class Zaehler { - static int anzahl = 0; - - public static void main(String[] args) { - // hier wie gewohnt das Fenster - } -} -\`\`\` - -- \`static\` heißt: Die Variable gehört zur Klasse selbst. Weil \`main\` auch \`static\` ist, braucht es das hier. -- Eine solche Variable nennt man **Attribut** der Klasse. Sie lebt so lange wie das Programm, nicht nur so lange wie \`main\`. - -::: quiz -? Wie nennt man eine Variable, die direkt in der Klasse steht und nicht in \`main\`? -- [ ] Parameter -- [x] Attribut -- [ ] Zuhörer -> Ein Attribut lebt so lange wie das Programm, nicht nur so lange wie \`main\`. -::: - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Zaehler { - static int anzahl = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - reset.addActionListener(e -> { - anzahl = 0; - zahl.setText("0"); - }); - - fenster.add(zahl); - fenster.add(plus); - fenster.add(reset); - fenster.setVisible(true); - } -} -\`\`\` - -\`SwingConstants.CENTER\` setzt den Text der Beschriftung in die Mitte. Klicke ein paarmal auf +1 und dann auf Reset. - -::: angeberwissen -Mit \`zahl.setFont(new Font("SansSerif", Font.BOLD, 48));\` wird die Zahl groß und fett. \`Font\` kommt aus \`java.awt\`, das du schon importiert hast. -::: - -## Spiel: Wer zählt mit? - -::: spiel zuhoerer -titel: Spiel: Wer zählt mit? -intro: Jetzt rechnen die Zuhörer mit der Variable aus der Klasse. Ludwig zeigt dir Code, Fenster und die Klicks. Welche Zahl steht am Ende da? Danach spielt Ludwig die Klicks ab. -frage: Was steht nach den Klicks in der Beschriftung? - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> Die Variable steht in der Klasse und behält ihren Wert zwischen den Klicks. Jeder Klick zählt eins hoch: 1, 2, 3. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - reset.addActionListener(e -> { - anzahl = 0; - zahl.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> Nach zwei Klicks steht 2 da. Reset setzt die Variable auf 0, und der letzte Klick zählt wieder auf 1. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> anzahl++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> Die Variable ist danach 2, aber niemand schreibt sie in die Beschriftung. Ohne \`zahl.setText(…)\` zeigt das Fenster weiter 0. Rechnen und Anzeigen sind zwei verschiedene Dinge. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("Klicks: 0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("Klicks: " + anzahl + 1); - }); -klicks: +1 -- Klicks: 2 -- Klicks: 1 -- [x] Klicks: 11 -> Die Klammerfalle: Java rechnet von links nach rechts. \`"Klicks: " + anzahl\` ergibt den Text „Klicks: 1“, und \`+ 1\` hängt eine weitere 1 an. - -runde: - static int anzahl = 0; - JLabel zahl = new JLabel("0"); - JButton eins = new JButton("+1"); - JButton zehn = new JButton("+10"); - eins.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); - zehn.addActionListener(e -> { - anzahl += 10; - zahl.setText("" + anzahl); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Beide Zuhörer ändern dieselbe Variable: 10, dann 11, dann 21. \`anzahl += 10\` ist die Kurzform von \`anzahl = anzahl + 10\`. -::: - -## Aufgabe: Klickzähler - -Baue den Klickzähler nach und erweitere ihn: - -- ein Knopf **−1**, der eins herunterzählt, -- die Zahl soll nie unter 0 fallen. Steht sie auf 0, passiert bei −1 nichts. - -**Du bist fertig, wenn …** - -- **+1** eins hochzählt und **−1** eins herunterzählt, -- bei 0 ein Klick auf **−1** nichts ändert, -- **Reset** die Zahl wieder auf 0 setzt. - -::: ludwig -Für „nie unter 0“ brauchst du im Zuhörer von −1 ein \`if\`: Nur wenn \`anzahl > 0\` ist, wird heruntergezählt. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, warum ein Zuhörer eine Variable aus main nicht ändern darf. → Der erste Versuch -- Ich kann eine static-Variable in der Klasse anlegen. → Eine Variable für die ganze Klasse -- Ich kann zwei Knöpfe dieselbe Zahl ändern lassen. → Das ganze Programm -- Ich kann einen Zähler mit −1 bauen, der nie unter 0 fällt. → Aufgabe: Klickzähler -::: - -## Wissenstest - -::: quiz -? Warum darf der Zuhörer die Variable anzahl aus main nicht verändern? -→ Der erste Versuch -- [ ] Weil Zahlen in Swing nicht erlaubt sind -- [x] Weil der Zuhörer erst später läuft und Java Variablen aus main im Pfeil nicht verändern lässt -- [ ] Weil anzahl zu groß wird - -? Wo steht static int anzahl = 0;? -→ Eine Variable für die ganze Klasse -- [ ] In main, ganz oben -- [x] In der Klasse, außerhalb von main -- [ ] Im Zuhörer - -? Was macht SwingConstants.CENTER bei new JLabel("0", SwingConstants.CENTER)? -→ Das ganze Programm -- [x] Es setzt den Text in die Mitte der Beschriftung. -- [ ] Es setzt das Fenster in die Mitte des Bildschirms. -- [ ] Es macht die Schrift größer. -::: -`,ww=`--- -titel: Eine Ampel mit Farben -bereich: Fenster mit Swing -kurz: Hintergrundfarben setzen und mit einer Zustandsvariable eine Ampel weiterschalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit setOpaque und setBackground eine Beschriftung färben -- mir mit einer static-Variable einen Zustand merken -- mit BorderLayout etwas groß in die Mitte und etwas schmal nach unten setzen -- erklären, wofür FlowLayout, GridLayout und BorderLayout gut sind -- eine Ampel mit vier Phasen programmieren -::: - -## Das Ziel - -Wir bauen eine Ampel: Eine große Fläche leuchtet rot. Bei jedem Klick auf **Weiter** schaltet sie um: Rot, Grün, Gelb und wieder Rot. - -Neu sind diesmal **Farben** und ein **Zustand**: Das Programm muss sich merken, welche Farbe gerade dran ist. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Farben in Swing - -Farben stehen in der Klasse \`Color\` aus \`java.awt\`. Fertige Farben sind zum Beispiel \`Color.RED\`, \`Color.GREEN\`, \`Color.YELLOW\`, \`Color.ORANGE\` und \`Color.WHITE\`. - -Eine Beschriftung färbst du so: - -\`\`\`java -JLabel licht = new JLabel("", SwingConstants.CENTER); -licht.setOpaque(true); -licht.setBackground(Color.RED); -\`\`\` - -- \`setBackground(…)\` legt die Hintergrundfarbe fest. -- \`setOpaque(true)\` braucht ein \`JLabel\` dazu. Ohne diese Zeile ist der Hintergrund durchsichtig, und die Farbe bleibt unsichtbar. - -::: ludwig -\`opaque\` ist englisch für „undurchsichtig“. Eine Beschriftung ist normalerweise durchsichtig wie Schrift auf einer Glasscheibe. Erst mit \`setOpaque(true)\` bekommt sie eine eigene Fläche, die man anmalen kann. -::: - -::: quiz -? Welche Zeile malt den Hintergrund der Beschriftung \`licht\` grün an? -- [ ] \`licht.setText(Color.GREEN);\` -- [x] \`licht.setBackground(Color.GREEN);\` -- [ ] \`licht.setColor("grün");\` -> Die Hintergrundfarbe setzt \`setBackground\`. Damit man sie sieht, braucht ein \`JLabel\` vorher \`setOpaque(true)\`. -::: - -## Sich die Farbe merken - -Damit die Ampel weiß, was als Nächstes kommt, merkt sie sich ihren Zustand in einer Zahl: 0 heißt Rot, 1 heißt Grün, 2 heißt Gelb. Wie beim Klickzähler aus Lektion 32 steht die Variable in der Klasse, damit der Zuhörer sie ändern darf: - -\`\`\`java -static int phase = 0; -\`\`\` - -Bei jedem Klick geht die Phase eins weiter. Nach Gelb (2) kommt wieder Rot (0): - -\`\`\`java -weiter.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - licht.setBackground(Color.RED); - } else if (phase == 1) { - licht.setBackground(Color.GREEN); - } else { - licht.setBackground(Color.YELLOW); - } -}); -\`\`\` - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Ampel { - static int phase = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Ampel"); - fenster.setSize(200, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new BorderLayout()); - - JLabel licht = new JLabel("", SwingConstants.CENTER); - licht.setOpaque(true); - licht.setBackground(Color.RED); - JButton weiter = new JButton("Weiter"); - - weiter.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - licht.setBackground(Color.RED); - } else if (phase == 1) { - licht.setBackground(Color.GREEN); - } else { - licht.setBackground(Color.YELLOW); - } - }); - - fenster.add(licht, BorderLayout.CENTER); - fenster.add(weiter, BorderLayout.SOUTH); - fenster.setVisible(true); - } -} -\`\`\` - -Hier kommt ein neues Layout zum Einsatz, das **BorderLayout**. Es hat fünf Plätze: \`NORTH\` (oben), \`SOUTH\` (unten), \`WEST\` (links), \`EAST\` (rechts) und \`CENTER\` (Mitte). Die Mitte bekommt allen Platz, der übrig bleibt. So wird das Licht groß, und der Knopf bleibt unten schmal. - -::: angeberwissen -Das BorderLayout ist das Layout, das ein \`JFrame\` ohne eigenes Layout benutzt. Ein \`add\` ohne Platzangabe landet in \`CENTER\`. Deshalb lagen in Lektion 31 alle Bausteine übereinander. -::: - -::: angeberwissen -Profis schreiben das Weiterschalten in einer Zeile: \`phase = (phase + 1) % 3;\`. Der Rest-Operator \`%\` liefert den Rest beim Teilen. 3 % 3 ist 0, also beginnt es nach 2 wieder bei 0. -::: - -## Welches Layout wofür? - -Du kennst jetzt drei Layouts. Jedes ordnet die Bausteine anders an: - -| Layout | So ordnet es an | Gut für | -|---|---|---| -| \`FlowLayout\` (Lektion 29) | nebeneinander wie Wörter in einer Zeile, jeder Baustein so groß wie nötig | ein paar Knöpfe in einer Reihe | -| \`GridLayout\` (Lektion 31) | ein Gitter aus gleich großen Zellen, Zeile für Zeile gefüllt | Formulare, Tastenfelder wie beim Taschenrechner | -| \`BorderLayout\` (diese Lektion) | fünf Plätze: oben, unten, links, rechts und die Mitte, die den Rest bekommt | etwas Großes in der Mitte, z. B. ein Bild oder eine Anzeige, mit Knöpfen am Rand | - -Ohne \`setLayout\` benutzt ein Fenster das BorderLayout. Darum landet dann alles in der Mitte. - -::: ludwig -Frag dich beim Planen: Sollen alle Teile gleich groß sein? Dann GridLayout. Soll ein Teil groß sein und der Rest schmal am Rand? Dann BorderLayout. Sollen die Teile einfach ihre normale Größe behalten? Dann FlowLayout. -::: - -## Spiel: Wo landet der Knopf? - -::: spiel layout -titel: Spiel: Wo landet der Knopf? -intro: Du kennst jetzt drei Layouts und das Fenster ganz ohne Layout. Ludwig zeigt dir Code mit den Knöpfen A, B, C … Wie sieht das Fenster aus? Tippe auf das richtige Bild. -frage: Wie sieht das Fenster aus? - -runde: - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Ohne eigenes Layout landet jedes \`add\` in der Mitte, so groß wie das Fenster. Die drei liegen übereinander, man sieht nur den letzten: C. - -runde: - fenster.setLayout(new FlowLayout()); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> FlowLayout setzt die Knöpfe in normaler Größe nebeneinander, wie Wörter in einer Zeile, und zentriert sie. - -runde: - fenster.setLayout(new GridLayout(0, 1)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> Eine Spalte, so viele Zeilen wie nötig: alles untereinander, jede Zeile gleich hoch und so breit wie das Fenster. - -runde: - fenster.setLayout(new GridLayout(0, 2)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); - fenster.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Zwei Spalten. Die Zellen werden Zeile für Zeile gefüllt, immer erst links, dann rechts: A B, dann C D. - -runde: - fenster.setLayout(new GridLayout(2, 3)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); - fenster.add(new JButton("D")); - fenster.add(new JButton("E")); - fenster.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Erst kommen die Zeilen, dann die Spalten: 2 Zeilen mit je 3 Spalten. - -runde: - fenster.setLayout(new GridLayout(0, 2)); - fenster.add(new JButton("A")); - fenster.add(new JButton("B")); - fenster.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Zwei Spalten, drei Knöpfe: Die erste Zeile ist voll, in der zweiten steht C links, und rechts bleibt die Zelle leer. - -runde: - fenster.setLayout(new BorderLayout()); - fenster.add(new JButton("A"), BorderLayout.NORTH); - fenster.add(new JButton("B"), BorderLayout.CENTER); - fenster.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH ist oben, SOUTH unten. Die Mitte bekommt den ganzen übrigen Platz, darum ist B groß. - -runde: - fenster.setLayout(new BorderLayout()); - fenster.add(new JButton("A"), BorderLayout.WEST); - fenster.add(new JButton("B")); - fenster.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST ist links, EAST rechts. B hat keine Platzangabe und landet deshalb in CENTER. -::: - -## Aufgabe: Ampel - -Baue die Ampel nach und mach sie echter. In Deutschland leuchten nach Rot kurz Rot und Gelb zusammen. Nimm deshalb eine vierte Phase dazu: - -- 0: Rot -- 1: Rot-Gelb (nimm dafür \`Color.ORANGE\`) -- 2: Grün -- 3: Gelb - -Zusatz: Schreib in jeder Phase zusätzlich mit \`licht.setText(…)\`, was die Ampel bedeutet, zum Beispiel „Stehen“ oder „Gehen“. - -**Du bist fertig, wenn …** - -- jeder Klick die nächste Phase zeigt: Rot, Rot-Gelb (orange), Grün, Gelb, -- nach Gelb wieder Rot kommt, -- im Zusatz zu jeder Phase der passende Text steht. - -::: ludwig -Mit vier Phasen kommt nach 3 wieder 0. Pass also die Zahl in \`if (phase > 2)\` an. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit setOpaque und setBackground eine Beschriftung färben. → Farben in Swing -- Ich kann mir mit einer static-Variable einen Zustand merken. → Sich die Farbe merken -- Ich kann mit BorderLayout etwas groß in die Mitte und etwas schmal nach unten setzen. → Das ganze Programm -- Ich kann erklären, wofür FlowLayout, GridLayout und BorderLayout gut sind. → Welches Layout wofür? -- Ich kann eine Ampel mit vier Phasen programmieren. → Aufgabe: Ampel -::: - -## Wissenstest - -::: quiz -? Warum bleibt die Farbe ohne setOpaque(true) unsichtbar? -→ Farben in Swing -- [ ] Weil Color.RED nicht existiert -- [x] Weil eine Beschriftung sonst durchsichtig ist -- [ ] Weil das Fenster zu klein ist - -? Welcher Platz im BorderLayout bekommt den übrigen Platz? -→ Das ganze Programm -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? phase ist 2 und der Zuhörer führt phase++ und dann if (phase > 2) { phase = 0; } aus. Welchen Wert hat phase danach? -→ Sich die Farbe merken -- [x] 0 -- [ ] 2 -- [ ] 3 -> Nach phase++ ist sie 3, das ist größer als 2, also wird sie 0. -::: -`,yw=`--- -titel: Notenrechner im Fenster -bereich: Fenster mit Swing -kurz: Punkte eingeben, mit else if die Note bestimmen und falsche Eingaben abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit else if Punkte in eine Note umrechnen -- die Note im Fenster anzeigen -- „abc“ und Punkte außerhalb von 0 bis 100 abfangen und melden -- ein Programm an den Grenzen testen, zum Beispiel mit 92 und 91 -::: - -## Das Ziel - -Ein Fenster rechnet Punkte in eine Note um: Du tippst die Punkte einer Klassenarbeit ein (0 bis 100), klickst auf **Note berechnen**, und unten steht die Note. - -Diesmal kommt kein neuer Swing-Baustein dazu. Neu ist, dass du alles Bisherige kombinierst: Textfeld, Knopf, \`else if\` aus Lektion 20 und try/catch aus Lektion 22. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Der Notenschlüssel - -Wir nehmen den Notenschlüssel der IHK-Prüfungen: - -| Punkte | Note | -|---|---| -| 92 bis 100 | 1 | -| 81 bis 91 | 2 | -| 67 bis 80 | 3 | -| 50 bis 66 | 4 | -| 30 bis 49 | 5 | -| 0 bis 29 | 6 | - -Mit \`else if\` prüfst du von oben nach unten. Die erste Bedingung, die zutrifft, gewinnt: - -\`\`\`java -int note; -if (punkte >= 92) { - note = 1; -} else if (punkte >= 81) { - note = 2; -} else if (punkte >= 67) { - note = 3; -} else if (punkte >= 50) { - note = 4; -} else if (punkte >= 30) { - note = 5; -} else { - note = 6; -} -\`\`\` - -::: ludwig -Bei 85 Punkten ist \`punkte >= 92\` falsch, aber \`punkte >= 81\` wahr. Also Note 2, und die restlichen Bedingungen werden gar nicht mehr geprüft. Deshalb reicht jeweils die untere Grenze. -::: - -Du kannst diesen Teil ohne Fenster im Bereich [Code](#zeige-codelive) ausprobieren. Schreib \`int punkte = 85;\` davor und \`System.out.println("Note: " + note);\` dahinter. Das Struktogramm zeigt dir die verschachtelten Verzweigungen. - -::: quiz -? Welche Note ergibt der Schlüssel bei 29 Punkten? -- [ ] 4 -- [ ] 5 -- [x] 6 -> Alle Bedingungen sind falsch, auch \`punkte >= 30\`. Dann gilt der \`else\`-Zweig. -::: - -## Das Fenster - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Notenrechner { - public static void main(String[] args) { - JFrame fenster = new JFrame("Notenrechner"); - fenster.setSize(260, 180); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel frage = new JLabel("Punkte (0 bis 100):"); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Note berechnen"); - JLabel ergebnis = new JLabel("Note: ?"); - - knopf.addActionListener(e -> { - int punkte = Integer.parseInt(feld.getText()); - int note; - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else if (punkte >= 50) { - note = 4; - } else if (punkte >= 30) { - note = 5; - } else { - note = 6; - } - ergebnis.setText("Note: " + note); - }); - - fenster.add(frage); - fenster.add(feld); - fenster.add(knopf); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -\`\`\` - -Probiere 100, 85, 50 und 12 aus. - -## Falsche Eingaben abfangen - -Zwei Dinge können noch schiefgehen: - -1. Jemand tippt „abc“. Dann wirft \`Integer.parseInt\` eine \`NumberFormatException\`. -2. Jemand tippt 150 oder −5. Das ist zwar eine Zahl, aber keine gültige Punktzahl. - -Den ersten Fall fängst du mit try/catch ab, den zweiten mit einem \`if\` vor der Notenberechnung: - -\`\`\`java -knopf.addActionListener(e -> { - try { - int punkte = Integer.parseInt(feld.getText()); - if (punkte < 0 || punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - // hier die Notenberechnung von oben - } - } catch (NumberFormatException fehler) { - ergebnis.setText("Das ist keine Zahl."); - } -}); -\`\`\` - -::: ludwig -\`||\` heißt „oder“ (Lektion 9). Die Bedingung ist wahr, wenn die Punkte zu klein **oder** zu groß sind. -::: - -## Spiel: Welche Note? - -::: spiel frage -titel: Spiel: Welche Note? -intro: Ludwig hat Notenrechner gebaut, aber nicht alle sind richtig. Rechne nach, welche Note herauskommt. Sechs Runden. - -runde: - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else { - note = 4; - } -frage: punkte ist 81. Welche Note kommt heraus? -- 1 -- [x] 2 -- 3 -> 81 >= 92 ist falsch, 81 >= 81 ist wahr. Die Grenze gehört noch zur 2. - -runde: - if (punkte >= 92) { - note = 1; - } else if (punkte >= 81) { - note = 2; - } else if (punkte >= 67) { - note = 3; - } else if (punkte >= 50) { - note = 4; - } else { - note = 5; - } -frage: punkte ist 66. Welche Note kommt heraus? -- 3 -- [x] 4 -- 5 -> 66 >= 67 ist knapp falsch, 66 >= 50 ist wahr. Also Note 4. - -runde: - if (punkte >= 50) { - note = 4; - } else if (punkte >= 92) { - note = 1; - } else { - note = 6; - } -frage: punkte ist 95. Welche Note kommt heraus? -- 1 -- [x] 4 -- 6 -> Die erste wahre Bedingung gewinnt. 95 >= 50 ist schon wahr, die 1 wird nie erreicht. Deshalb prüft man von den hohen Punkten nach unten. - -runde: - note = 6; - if (punkte >= 30) { - note = 5; - } - if (punkte >= 50) { - note = 4; - } -frage: punkte ist 70. Welche Note steht danach in note? -- 6 -- 5 -- [x] 4 -> Ohne \`else\` wird jedes \`if\` geprüft. Beide sind wahr, das letzte setzt die Note 4. - -runde: - // note ist schon berechnet - if (punkte < 0 || punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - ergebnis.setText("Note: " + note); - } -frage: Im Textfeld steht 100. Was zeigt das Fenster? -- [x] Note: 1 -- Bitte 0 bis 100 eingeben. -- Das ist keine Zahl. -> 100 > 100 ist falsch. 100 Punkte sind also gültig und ergeben die Note 1. - -runde: - // note ist schon berechnet - if (punkte < 0 && punkte > 100) { - ergebnis.setText("Bitte 0 bis 100 eingeben."); - } else { - ergebnis.setText("Note: " + note); - } -frage: Im Textfeld steht 150. Was zeigt das Fenster? -- Bitte 0 bis 100 eingeben. -- [x] eine Note, obwohl 150 ungültig ist -- Das ist keine Zahl. -> Mit \`&&\` müssten die Punkte gleichzeitig unter 0 und über 100 sein. Das geht nie, also warnt das Programm nie. Richtig ist \`||\`. -::: - -## Aufgabe: Notenrechner - -Baue den Notenrechner zusammen, mit beiden Prüfungen aus dem letzten Schritt. Teste ihn mit 92, 91, 0, 100, 101 und „zehn“. - -Zusatz: Schreib die Note zusätzlich als Wort hin, zum Beispiel „Note: 2 (gut)“. Die Wörter sind sehr gut, gut, befriedigend, ausreichend, mangelhaft und ungenügend. - -**Du bist fertig, wenn …** - -- 92 und 100 eine 1 ergeben, 91 eine 2 und 0 eine 6, -- bei 101 der Hinweis „Bitte 0 bis 100 eingeben.“ erscheint, -- bei „zehn“ ein Hinweis erscheint und das Programm weiterläuft. - -::: ludwig -Teste immer die Grenzen: 92 muss eine 1 sein, 91 eine 2. An den Grenzen stecken die meisten Fehler, zum Beispiel \`>\` statt \`>=\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit else if Punkte in eine Note umrechnen. → Der Notenschlüssel -- Ich kann die Note im Fenster anzeigen. → Das Fenster -- Ich kann „abc“ und Punkte außerhalb von 0 bis 100 abfangen und melden. → Falsche Eingaben abfangen -- Ich kann ein Programm an den Grenzen testen, zum Beispiel mit 92 und 91. → Aufgabe: Notenrechner -::: - -## Wissenstest - -::: quiz -? Welche Note ergibt der Schlüssel bei 67 Punkten? -→ Der Notenschlüssel -- [ ] 2 -- [x] 3 -- [ ] 4 -> 67 ist die untere Grenze für die 3. - -? Warum reicht in jeder Bedingung die untere Grenze? -→ Der Notenschlüssel -- [x] Weil else if die oberen Fälle schon vorher aussortiert hat -- [ ] Weil Java obere Grenzen nicht kennt -- [ ] Weil die Punkte immer 100 sind - -? Welche Bedingung erkennt ungültige Punkte? -→ Falsche Eingaben abfangen -- [ ] punkte < 0 && punkte > 100 -- [x] punkte < 0 || punkte > 100 -- [ ] punkte >= 0 || punkte <= 100 -> Eine Zahl kann nicht gleichzeitig kleiner als 0 und größer als 100 sein, deshalb braucht es oder. -::: -`,zw=`--- -titel: Ein Taschenrechner -bereich: Fenster mit Swing -kurz: Zwei Spalten mit GridLayout, vier Rechenknöpfe und die Division durch 0 abfangen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit GridLayout(0, 2) zwei Spalten bauen -- erklären, warum "" + (a + b) Klammern braucht -- alle vier Rechenknöpfe programmieren -- einen Rechner bauen, der bei 0 und bei „abc“ nicht abstürzt -::: - -## Das Ziel - -Wir bauen einen kleinen Taschenrechner: zwei Textfelder für die Zahlen, vier Knöpfe **+**, **-**, **\\*** und **/** und eine Zeile für das Ergebnis. - -Neu sind diesmal **zwei Spalten** im GridLayout und **mehrere Knöpfe**, die alle mit denselben Textfeldern rechnen. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Zwei Spalten - -Bisher stand mit \`GridLayout(0, 1)\` alles untereinander. Mit \`GridLayout(0, 2)\` gibt es zwei Spalten. Die Bausteine füllen die Zellen in der Reihenfolge der \`add\`-Zeilen, immer erst links, dann rechts: - -| links | rechts | -|---|---| -| Erste Zahl: | Textfeld | -| Zweite Zahl: | Textfeld | -| + | - | -| \\* | / | -| Ergebnis: | ? | - -Die Beschriftungen links brauchst du später nicht mehr. Du kannst sie deshalb direkt in \`add\` bauen, ohne Variable: - -\`\`\`java -fenster.add(new JLabel("Erste Zahl:")); -fenster.add(feld1); -\`\`\` - -## Das Fenster mit dem ersten Knopf - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Rechner { - public static void main(String[] args) { - JFrame fenster = new JFrame("Rechner"); - fenster.setSize(280, 220); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 2)); - - JTextField feld1 = new JTextField(); - JTextField feld2 = new JTextField(); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - JButton mal = new JButton("*"); - JButton geteilt = new JButton("/"); - JLabel ergebnis = new JLabel("?"); - - plus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a + b)); - }); - - fenster.add(new JLabel("Erste Zahl:")); - fenster.add(feld1); - fenster.add(new JLabel("Zweite Zahl:")); - fenster.add(feld2); - fenster.add(plus); - fenster.add(minus); - fenster.add(mal); - fenster.add(geteilt); - fenster.add(new JLabel("Ergebnis:")); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -\`\`\` - -Tippe 3 und 4 ein und klicke auf +. Unten steht 7. - -::: ludwig -Achte auf die Klammern in \`"" + (a + b)\`. Ohne sie rechnet Java von links nach rechts: \`"" + a\` ergibt den Text „3“, und \`"3" + b\` hängt die 4 hinten an. Heraus kommt „34“ statt 7. Die Klammer sorgt dafür, dass zuerst gerechnet wird. -::: - -## Die anderen drei Knöpfe - -Die Knöpfe -, \\* und / funktionieren genauso. Nur das Rechenzeichen in der letzten Zeile ändert sich: - -\`\`\`java -minus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a - b)); -}); -\`\`\` - -Bei / gibt es zwei Besonderheiten: - -- Mit \`int\` schneidet Java beim Teilen die Nachkommastellen ab. 7 / 2 ergibt 3. -- Bei 0 als zweiter Zahl wirft Java eine \`ArithmeticException\`, wie bei der Aufgabe „Sicher teilen“ in Lektion 22. - -## Division durch 0 abfangen - -\`\`\`java -geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } -}); -\`\`\` - -::: angeberwissen -Mit \`double\` und \`Double.parseDouble(…)\` gibt es Nachkommastellen: 7 / 2 ergibt 3.5. Dafür wirft Java bei Kommazahlen keine Exception beim Teilen durch 0. Das Ergebnis heißt dann \`Infinity\`, also „unendlich“. -::: - -::: quiz -? Was zeigt der Rechner bei 8 geteilt durch 0? -- [ ] 0 -- [ ] Infinity -- [x] Nicht durch 0! -> Mit \`int\` wirft \`a / b\` eine \`ArithmeticException\`. Das \`catch\` fängt sie ab und zeigt den Hinweis. -::: - -## Eine eigene Methode - -Das steht nicht im Lehrplan, macht den Code aber kürzer. - -::: angeberwissen -Jeder Zuhörer liest dieselben zwei Felder. Diese Wiederholung kannst du in eine eigene Methode auslagern, ein Unterprogramm wie in Lektion 18. Sie steht in der Klasse neben \`main\`: - -\`static int zahl(JTextField feld) { return Integer.parseInt(feld.getText()); }\` - -Im Zuhörer heißt es dann nur noch \`ergebnis.setText("" + (zahl(feld1) + zahl(feld2)));\`. -::: - -## Spiel: Was zeigt der Rechner? - -::: spiel frage -titel: Spiel: Was zeigt der Rechner? -intro: Ludwig hat am Taschenrechner geschraubt. Lies den Zuhörer und sag vorher, was im Ergebnis steht. Sechs Runden. - -runde: - plus.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + a + b); - }); -frage: Du tippst 3 und 4 und klickst auf +. Was steht im Ergebnis? -- 7 -- [x] 34 -- 12 -> Ohne Klammern rechnet Java von links nach rechts: \`"" + 3\` ist der Text „3“, und \`+ 4\` hängt die 4 an. - -runde: - mal.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + a * b); - }); -frage: Du tippst 3 und 4 und klickst auf *. Was steht im Ergebnis? -- 34 -- [x] 12 -- 7 -> Punkt vor Strich gilt auch hier: Erst rechnet Java 3 * 4, dann hängt es die 12 an den Text. Beim Plus fehlt dieser Vorrang, darum braucht es dort die Klammern. - -runde: - geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - ergebnis.setText("" + (a / b)); - }); -frage: Du tippst 7 und 2 und klickst auf /. Was steht im Ergebnis? -- 3.5 -- [x] 3 -- 4 -> Mit \`int\` schneidet Java die Nachkommastellen ab. Es wird nicht gerundet. - -runde: - geteilt.addActionListener(e -> { - int a = Integer.parseInt(feld1.getText()); - int b = Integer.parseInt(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } - }); -frage: Du tippst 8 und 0 und klickst auf /. Was steht im Ergebnis? -- 0 -- [x] Nicht durch 0! -- Infinity -> Bei \`int\` wirft das Teilen durch 0 eine ArithmeticException, und \`catch\` schreibt den Hinweis. - -runde: - geteilt.addActionListener(e -> { - double a = Double.parseDouble(feld1.getText()); - double b = Double.parseDouble(feld2.getText()); - ergebnis.setText("" + (a / b)); - }); -frage: Du tippst 7 und 2 und klickst auf /. Was steht im Ergebnis? -- 3 -- [x] 3.5 -- 3.0 -> Mit \`double\` bleiben die Nachkommastellen erhalten. - -runde: - geteilt.addActionListener(e -> { - double a = Double.parseDouble(feld1.getText()); - double b = Double.parseDouble(feld2.getText()); - try { - ergebnis.setText("" + (a / b)); - } catch (ArithmeticException fehler) { - ergebnis.setText("Nicht durch 0!"); - } - }); -frage: Du tippst 8 und 0 und klickst auf /. Was steht im Ergebnis? -- Nicht durch 0! -- [x] Infinity -- 0.0 -> Bei \`double\` gibt es keine Exception. Das Ergebnis ist \`Infinity\`, also „unendlich“, und das \`catch\` kommt nie dran. -::: - -## Aufgabe: Taschenrechner - -Baue den Rechner fertig: - -- alle vier Knöpfe rechnen, -- / meldet „Nicht durch 0!“ statt abzustürzen, -- steht in einem Feld „abc“ oder nichts, erscheint „Bitte zwei Zahlen eingeben“. - -Zusatz: Ein fünfter Knopf **C** leert beide Felder und setzt das Ergebnis wieder auf „?“. - -**Du bist fertig, wenn …** - -- 7 und 2 bei +, −, * und / die Ergebnisse 9, 5, 14 und 3 zeigen, -- 7 / 0 „Nicht durch 0!“ meldet, -- bei „abc“ oder einem leeren Feld „Bitte zwei Zahlen eingeben“ erscheint und nichts abstürzt. - -::: ludwig -Für „abc“ brauchst du in jedem Zuhörer ein try/catch mit \`NumberFormatException\`. Hinter einem \`try\` dürfen auch zwei \`catch\` stehen, einer für \`NumberFormatException\` und einer für \`ArithmeticException\`. Ein leeres Feld liefert übrigens den Text \`""\`, und auch der ist keine Zahl. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit GridLayout(0, 2) zwei Spalten bauen. → Zwei Spalten -- Ich kann erklären, warum "" + (a + b) Klammern braucht. → Das Fenster mit dem ersten Knopf -- Ich kann alle vier Rechenknöpfe programmieren. → Die anderen drei Knöpfe -- Ich kann einen Rechner bauen, der bei 0 und bei „abc“ nicht abstürzt. → Aufgabe: Taschenrechner -::: - -## Wissenstest - -::: quiz -? a ist 3 und b ist 4. Was ergibt "" + a + b? -→ Das Fenster mit dem ersten Knopf -- [ ] 7 -- [x] 34 -- [ ] Einen Fehler -> Java rechnet von links nach rechts: Erst entsteht der Text "3", dann wird die 4 angehängt. - -? Was ergibt 7 / 2 mit int? -→ Die anderen drei Knöpfe -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? In welcher Reihenfolge füllt GridLayout(0, 2) die Zellen? -→ Zwei Spalten -- [x] Zeile für Zeile, jeweils erst links, dann rechts -- [ ] Spalte für Spalte, erst ganz links von oben nach unten -- [ ] Zufällig -::: -`,Iw=`--- -titel: Zahlenraten -bereich: Fenster mit Swing -kurz: Ein Spiel mit Zufallszahl, Versuchszähler und einem Knopf, der sich am Ende abschaltet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit Math.random eine ganze Zufallszahl von 1 bis 100 erzeugen -- entscheiden, welche Variablen in die Klasse gehören -- einen Knopf mit setEnabled abschalten -- ein Ratespiel mit einem Knopf „Neues Spiel“ bauen -::: - -## Das Ziel - -Jetzt wird gespielt: Der Computer denkt sich eine Zahl von 1 bis 100 aus. Du rätst, und das Fenster sagt dir „zu klein“ oder „zu groß“. Hast du sie gefunden, verrät es dir, wie viele Versuche du gebraucht hast. - -Neu sind diesmal **Zufallszahlen** und ein Knopf, der sich am Ende **abschaltet**. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Eine Zufallszahl - -\`Math.random()\` liefert eine zufällige Kommazahl von 0 bis knapp unter 1, zum Beispiel 0.4173. Daraus wird in drei Schritten eine ganze Zahl von 1 bis 100: - -\`\`\`java -int geheim = (int) (Math.random() * 100) + 1; -\`\`\` - -| Schritt | Beispiel | -|---|---| -| \`Math.random()\` | 0.4173 | -| \`* 100\` | 41.73 | -| \`(int)\` schneidet die Nachkommastellen ab | 41 | -| \`+ 1\` | 42 | - -Ohne \`+ 1\` käme 0 bis 99 heraus. - -Probiere es im Bereich [Code](#zeige-codelive) aus: \`int geheim = (int) (Math.random() * 100) + 1;\` und \`System.out.println(geheim);\`. Jedes Ausführen liefert eine andere Zahl. - -::: angeberwissen -Computer können eigentlich gar nicht würfeln. \`Math.random()\` rechnet mit einer Formel eine lange Folge von Zahlen aus, die nur zufällig **aussehen**. Man nennt sie Pseudozufallszahlen. Für Spiele reicht das völlig. -::: - -## Was sich das Spiel merkt - -Zwei Dinge muss sich das Spiel über alle Klicks hinweg merken: die geheime Zahl und die Zahl der Versuche. Wie beim Klickzähler aus Lektion 32 stehen beide in der Klasse: - -\`\`\`java -static int geheim = (int) (Math.random() * 100) + 1; -static int versuche = 0; -\`\`\` - -::: quiz -? Warum stehen \`geheim\` und \`versuche\` in der Klasse und nicht in \`main\`? -- [ ] Weil \`Math.random()\` nur in der Klasse funktioniert -- [x] Weil sie über alle Klicks erhalten bleiben und der Zuhörer \`versuche\` ändern muss -- [ ] Weil \`main\` keine Zahlen speichern kann -> Wie beim Klickzähler: Variablen aus \`main\` darf der Zuhörer nicht verändern. -::: - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Zahlenraten { - static int geheim = (int) (Math.random() * 100) + 1; - static int versuche = 0; - - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahlenraten"); - fenster.setSize(320, 180); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JLabel hinweis = new JLabel("Ich denke mir eine Zahl von 1 bis 100."); - JTextField feld = new JTextField(); - JButton knopf = new JButton("Raten"); - - knopf.addActionListener(e -> { - int tipp = Integer.parseInt(feld.getText()); - versuche++; - if (tipp < geheim) { - hinweis.setText(tipp + " ist zu klein."); - } else if (tipp > geheim) { - hinweis.setText(tipp + " ist zu groß."); - } else { - hinweis.setText("Richtig! Du hast " + versuche + " Versuche gebraucht."); - knopf.setEnabled(false); - } - feld.setText(""); - }); - - fenster.add(hinweis); - fenster.add(feld); - fenster.add(knopf); - fenster.setVisible(true); - } -} -\`\`\` - -- \`feld.setText("")\` leert das Textfeld nach jedem Tipp, damit du gleich den nächsten eintippen kannst. -- \`knopf.setEnabled(false)\` schaltet den Knopf ab. Er wird grau und lässt sich nicht mehr klicken. Das Spiel ist vorbei. - -::: ludwig -Mit \`setEnabled(true)\` schaltest du einen Knopf wieder ein. So kannst du zum Beispiel einen Knopf erst freigeben, wenn etwas eingegeben wurde. -::: - -## Mit 7 Versuchen gewinnen - -::: angeberwissen -Wer geschickt rät, braucht nie mehr als 7 Versuche. Rate immer die Mitte des Bereichs, der noch übrig ist: erst 50, dann 25 oder 75 und so weiter. Jeder Tipp halbiert den Bereich: 100, 50, 25, 13, 7, 4, 2, 1. Das ist die **binäre Suche** aus Lektion 19. Weil 2 hoch 7 = 128 größer als 100 ist, reichen 7 Tipps. -::: - -## Aufgabe: Zahlenraten - -Baue das Spiel nach und mach es besser: - -- Eine Eingabe wie „abc“ zählt nicht als Versuch und bringt den Hinweis „Bitte eine Zahl eingeben“. -- Ein zweiter Knopf **Neues Spiel** denkt sich eine neue Zahl aus, setzt die Versuche auf 0, schaltet den Knopf Raten wieder ein und setzt den Hinweis zurück. - -Zusatz: Nach 7 Fehlversuchen ist das Spiel verloren. Das Fenster verrät dann die geheime Zahl. - -**Du bist fertig, wenn …** - -- du bei jedem falschen Tipp „zu klein“ oder „zu groß“ siehst, -- „abc“ den Hinweis „Bitte eine Zahl eingeben“ bringt und die Versuche dabei gleich bleiben, -- **Neues Spiel** die Versuche auf 0 setzt, den Knopf Raten wieder einschaltet und den Hinweis zurücksetzt. - -::: ludwig -Für „zählt nicht als Versuch“ muss \`versuche++\` **hinter** \`Integer.parseInt\` stehen, beides im \`try\`-Block. Wirft \`parseInt\` eine Exception, wird die Zeile mit \`versuche++\` übersprungen. -::: - -## Spiel: Zu klein oder zu groß? - -::: spiel frage -titel: Spiel: Zu klein oder zu groß? -intro: Ludwig spielt Zahlenraten. Lies den Code und sag vorher, was passiert. Sieben Runden. - -runde: - // geheim ist 42 - if (tipp < geheim) { - hinweis.setText(tipp + " ist zu klein."); - } else if (tipp > geheim) { - hinweis.setText(tipp + " ist zu groß."); - } else { - hinweis.setText("Richtig!"); - } -frage: Ludwig tippt 50. Was steht im Hinweis? -- 50 ist zu klein. -- [x] 50 ist zu groß. -- Richtig! -> 50 ist größer als 42. - -runde: -frage: Die Zahl liegt von 1 bis 100. 50 war zu groß, 25 war zu klein. Welche Zahl rät Ludwig klug als Nächstes? -- 26 -- [x] 37 -- 49 -> Übrig sind 26 bis 49. Die Mitte davon ist 37 (oder 38). So halbiert jeder Tipp den Bereich. - -runde: - int wurf = (int) (Math.random() * 6) + 1; -frage: Welche Zahlen kann \`wurf\` haben? -- 0 bis 6 -- [x] 1 bis 6 -- 1 bis 7 -> \`Math.random() * 6\` liegt von 0 bis knapp unter 6. \`(int)\` macht 0 bis 5 daraus, \`+ 1\` dann 1 bis 6. Ein Würfel! - -runde: - int zahl = (int) (Math.random() * 10); -frage: Welche Zahlen kann \`zahl\` haben? -- [x] 0 bis 9 -- 1 bis 10 -- 0 bis 10 -> Ohne \`+ 1\` fängt es bei 0 an, und die 10 wird nie erreicht. - -runde: - // geheim ist 42 - versuche++; - if (tipp == geheim) { - hinweis.setText("Richtig! Du hast " + versuche + " Versuche gebraucht."); - } -frage: Ludwig tippt 50, dann 25, dann 42. Was steht am Ende im Hinweis? -- Richtig! Du hast 2 Versuche gebraucht. -- [x] Richtig! Du hast 3 Versuche gebraucht. -- Richtig! Du hast 42 Versuche gebraucht. -> \`versuche++\` zählt bei jedem Klick hoch, auch beim Treffer. - -runde: - knopf.setEnabled(false); -frage: Ludwig hat die Zahl gefunden und klickt noch einmal auf **Raten**. Was passiert? -- Das Spiel beginnt von vorn. -- Es zählt einen Versuch mehr. -- [x] Nichts, der Knopf ist grau und lässt sich nicht klicken. -> \`setEnabled(false)\` schaltet den Knopf ab. - -runde: -frage: Die Zahl liegt jetzt von 1 bis 1000. Wie viele Tipps braucht man mit der binären Suche höchstens? -- 7 -- [x] 10 -- 500 -> 2 hoch 10 = 1024 ist größer als 1000. Zehn Halbierungen reichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit Math.random eine ganze Zufallszahl von 1 bis 100 erzeugen. → Eine Zufallszahl -- Ich kann entscheiden, welche Variablen in die Klasse gehören. → Was sich das Spiel merkt -- Ich kann einen Knopf mit setEnabled abschalten. → Das ganze Programm -- Ich kann ein Ratespiel mit einem Knopf „Neues Spiel“ bauen. → Aufgabe: Zahlenraten -::: - -## Wissenstest - -::: quiz -? Welche Zahlen liefert (int) (Math.random() * 100) + 1? -→ Eine Zufallszahl -- [ ] 0 bis 100 -- [x] 1 bis 100 -- [ ] 1 bis 101 - -? Was macht knopf.setEnabled(false)? -→ Das ganze Programm -- [ ] Es löscht den Knopf aus dem Fenster. -- [x] Der Knopf wird grau und lässt sich nicht mehr klicken. -- [ ] Es ändert die Beschriftung des Knopfs. - -? Wie viele Versuche braucht man mit der binären Suche höchstens bei 1 bis 100? -→ Aufgabe: Zahlenraten -- [ ] 50 -- [ ] 10 -- [x] 7 -> Jeder Tipp halbiert den Bereich, und 2 hoch 7 = 128 ist größer als 100. -::: -`,kw=`--- -titel: Viele Knöpfe, ein Zuhörer -bereich: Fenster mit Swing -kurz: Ein Zuhörer für viele Knöpfe, mit getActionCommand und getSource herausfinden, welcher Knopf geklickt wurde. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Zuhörer in einer Variable vom Typ ActionListener speichern und bei mehreren Knöpfen anmelden -- erklären, was getActionCommand und getSource liefern -- Knöpfe in einer Schleife auf ein JPanel setzen -- ein Zahlenschloss bauen, das sich nur mit dem richtigen Code öffnet -::: - -## Das Ziel - -Seit der Sache mit dem Kuchen will Ludwig seinen Stall abschließen. Er baut ein **Zahlenschloss**: neun Zifferntasten, eine Null, **C** zum Löschen und **OK** zum Prüfen. Nur wer 1234 eintippt, bekommt „Tor offen!“. - -Das sind zwölf Knöpfe. Bisher hatte jeder Knopf seinen eigenen Zuhörer, wie beim Taschenrechner in Lektion 35. Heute lernst du, wie **ein** Zuhörer für alle Knöpfe reicht. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Neun fast gleiche Zuhörer - -So sähe es mit einem Zuhörer pro Taste aus: - -\`\`\`java -eins.addActionListener(e -> anzeige.setText(anzeige.getText() + "1")); -zwei.addActionListener(e -> anzeige.setText(anzeige.getText() + "2")); -drei.addActionListener(e -> anzeige.setText(anzeige.getText() + "3")); -// ... und so weiter bis 9 -\`\`\` - -Die Zeilen unterscheiden sich nur in der Ziffer. Wer eine Kleinigkeit ändern will, muss es neunmal tun und vergisst bestimmt eine Zeile. - -## Ein Zuhörer als Variable - -Ein Zuhörer kann auch in einer **Variable** stehen. Ihr Datentyp ist \`ActionListener\`, der Vertrag aus Lektion 30. Danach meldest du denselben Zuhörer bei mehreren Knöpfen an: - -\`\`\`java -ActionListener ziffer = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - -eins.addActionListener(ziffer); -zwei.addActionListener(ziffer); -drei.addActionListener(ziffer); -\`\`\` - -\`ActionListener\` steht im Paket \`java.awt.event\`. Du brauchst deshalb oben \`import java.awt.event.*;\`. - -Jetzt gibt es den Code nur einmal. Aber woher weiß der Zuhörer, welche Ziffer er anhängen soll? - -## Wer hat geklickt? - -Das \`e\` vor dem Pfeil ist das **Ereignis**, ein Objekt vom Typ \`ActionEvent\`. Der Knopf gibt es bei jedem Klick mit, und darin steht, was passiert ist: - -| Aufruf | Liefert | Beispiel | -|---|---|---| -| \`e.getActionCommand()\` | die Beschriftung des geklickten Knopfs, als Text | \`"7"\` | -| \`e.getSource()\` | den geklickten Knopf selbst | der Knopf \`ok\` | - -- \`getActionCommand()\` ist praktisch, wenn die Beschriftung schon sagt, was zu tun ist, wie bei den Ziffern. -- \`getSource()\` vergleichst du mit \`==\` mit einem Knopf: \`if (e.getSource() == ok)\`. Hier ist \`==\` richtig, denn es geht um genau dieses eine Objekt, nicht um einen Text. - -::: ludwig -Texte vergleichst du mit \`equals\`, Knöpfe mit \`==\`. Willst du über die Beschriftung entscheiden, schreibst du \`e.getActionCommand().equals("OK")\`. Das geht auch, bricht aber, sobald jemand die Beschriftung in „Öffnen“ ändert. \`e.getSource() == ok\` bleibt richtig. -::: - -## Knöpfe in einer Schleife - -Mit einem gemeinsamen Zuhörer kannst du die Zifferntasten sogar in einer Schleife bauen. Damit sie ordentlich im Raster stehen, kommen sie auf ein **JPanel**. Ein JPanel ist eine Fläche im Fenster mit einem eigenen Layout: - -\`\`\`java -JPanel tasten = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton ziffer = new JButton("" + i); - ziffer.addActionListener(taste); - tasten.add(ziffer); -} -\`\`\` - -- \`"" + i\` macht aus der Zahl \`i\` den Text für die Beschriftung. -- In jedem Durchlauf entsteht ein neuer Knopf. Alle bekommen denselben Zuhörer \`taste\`. -- \`fenster.add(tasten, BorderLayout.CENTER)\` setzt die ganze Fläche mit allen Tasten ins Fenster. - -::: quiz -? Wie viele Knöpfe und wie viele Zuhörer entstehen in dieser Schleife? -- [ ] 9 Knöpfe und 9 Zuhörer -- [x] 9 Knöpfe und 1 Zuhörer -- [ ] 1 Knopf und 9 Zuhörer -> In jedem Durchlauf kommt ein neuer Knopf dazu. Alle bekommen denselben Zuhörer \`taste\`. -::: - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Zahlenschloss { - public static void main(String[] args) { - JFrame fenster = new JFrame("Zahlenschloss"); - fenster.setSize(240, 300); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new BorderLayout()); - - JLabel anzeige = new JLabel("", SwingConstants.CENTER); - JPanel tasten = new JPanel(new GridLayout(0, 3)); - JButton loeschen = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener taste = e -> { - if (e.getSource() == loeschen) { - anzeige.setText(""); - } else if (e.getSource() == ok) { - if (anzeige.getText().equals("1234")) { - anzeige.setText("Tor offen!"); - } else { - anzeige.setText("Falsch"); - } - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton ziffer = new JButton("" + i); - ziffer.addActionListener(taste); - tasten.add(ziffer); - } - JButton nullTaste = new JButton("0"); - nullTaste.addActionListener(taste); - loeschen.addActionListener(taste); - ok.addActionListener(taste); - tasten.add(loeschen); - tasten.add(nullTaste); - tasten.add(ok); - - fenster.add(anzeige, BorderLayout.NORTH); - fenster.add(tasten, BorderLayout.CENTER); - fenster.setVisible(true); - } -} -\`\`\` - -- \`loeschen\` und \`ok\` werden **vor** dem Zuhörer angelegt, denn der Zuhörer vergleicht mit ihnen. -- Der Zuhörer prüft erst die beiden besonderen Knöpfe. Alle anderen sind Ziffern und hängen ihre Beschriftung an. -- Die Null heißt \`nullTaste\`, weil \`null\` in Java schon ein Wort mit fester Bedeutung ist. - -::: angeberwissen -Das Zahlenfeld am Telefon hat die 1 oben links, das am Taschenrechner und an der Computertastatur hat die 7 oben links. Warum? Als die Tastentelefone kamen, gab es Tests mit Leuten, die damit wählen sollten. Mit der 1 oben ging es schneller, und Rechenmaschinen hatten ihre Anordnung da schon längst. Ludwigs Schloss macht es wie das Telefon. -::: - -## Spiel: Wer hat geklickt? - -::: spiel zuhoerer -titel: Spiel: Wer hat geklickt? -intro: In jeder Runde hört **ein** Zuhörer an mehreren Knöpfen. Ludwig zeigt dir Code, Fenster und die Klicks. Was steht am Ende in der Anzeige? Danach spielt Ludwig die Klicks ab. -frage: Was steht nach den Klicks in der Anzeige? - -runde: - JFrame fenster = new JFrame("Buchstaben"); - JLabel anzeige = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> Ein Zuhörer, zwei Knöpfe. \`e.getActionCommand()\` liefert jedes Mal die Beschriftung des geklickten Knopfs, und sie wird hinten angehängt. - -runde: - JFrame fenster = new JFrame("Zähler"); - static int zahl = 0; - JLabel anzeige = new JLabel("0"); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == plus) { - zahl++; - } else { - zahl--; - } - anzeige.setText("" + zahl); - }; - plus.addActionListener(z); - minus.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> \`e.getSource()\` sagt, welcher Knopf es war. Zweimal plus, einmal minus: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame fenster = new JFrame("Buchstaben"); - JLabel anzeige = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> anzeige.setText(anzeige.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> Der Zuhörer ist nur bei A und B angemeldet. Beim Klick auf C hört niemand zu. - -runde: - JFrame fenster = new JFrame("Tasten"); - JLabel anzeige = new JLabel(""); - JButton eins = new JButton("1"); - JButton zwei = new JButton("2"); - JButton weg = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - anzeige.setText(""); - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - eins.addActionListener(z); - zwei.addActionListener(z); - weg.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> Nach 1 und 2 steht „12“ da. C löscht alles, und die letzte 1 steht allein. - -runde: - JFrame fenster = new JFrame("Schloss"); - JLabel anzeige = new JLabel(""); - JButton vier = new JButton("4"); - JButton zwei = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (anzeige.getText().equals("42")) { - anzeige.setText("Tor offen!"); - } else { - anzeige.setText("Falsch"); - } - } else { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; - vier.addActionListener(z); - zwei.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Tor offen! -- [x] Falsch -- 24OK -> Eingetippt ist „24“, das Geheimnis ist aber „42“. Die Reihenfolge der Klicks zählt. -::: - -## Aufgabe: Zahlenschloss - -Baue das Zahlenschloss nach und probiere es aus. - -Dann mach es besser: - -- Nach drei falschen Versuchen schaltet sich **OK** ab, wie der Knopf beim Zahlenraten in Lektion 36. Zähle die Versuche in einer \`static int\`-Variable in der Klasse. -- Zusatz: Die Anzeige zeigt \`*\` statt der Ziffern. Merke dir die echte Eingabe in einer \`static String eingabe = "";\` und hänge an die Anzeige nur einen Stern an. - -**Du bist fertig, wenn …** - -- der richtige Code das Schloss öffnet und ein falscher nicht, -- nach drei falschen Versuchen **OK** ausgeschaltet ist, -- im Zusatz die Anzeige nur Sterne zeigt, der Code aber trotzdem geprüft wird. - -::: ludwig -Zähle im Zweig für **OK** bei jedem falschen Code \`versuche++\` und prüfe danach \`if (versuche >= 3)\`. Dann schaltet \`ok.setEnabled(false)\` den Knopf ab, wie beim Zahlenraten. Für die Sterne: \`eingabe = eingabe + e.getActionCommand();\` und \`anzeige.setText(anzeige.getText() + "*");\`. Verglichen wird dann \`eingabe\`, nicht der Text der Anzeige. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Zuhörer in einer Variable vom Typ ActionListener speichern und bei mehreren Knöpfen anmelden. → Ein Zuhörer als Variable -- Ich kann erklären, was getActionCommand und getSource liefern. → Wer hat geklickt? -- Ich kann Knöpfe in einer Schleife auf ein JPanel setzen. → Knöpfe in einer Schleife -- Ich kann ein Zahlenschloss bauen, das sich nur mit dem richtigen Code öffnet. → Aufgabe: Zahlenschloss -::: - -## Wissenstest - -::: quiz -? Was liefert e.getActionCommand() bei einem Klick auf den Knopf new JButton("7")? -→ Wer hat geklickt? -- [ ] die Zahl 7 -- [x] den Text "7" -- [ ] den Knopf selbst - -? Wie prüfst du, ob der Knopf ok geklickt wurde? -→ Wer hat geklickt? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource liefert den Knopf selbst, und den vergleichst du mit ==. - -? Warum steht ein Zuhörer in einer Variable? -→ Ein Zuhörer als Variable -- [ ] Damit er schneller läuft. -- [x] Damit man ihn bei mehreren Knöpfen anmelden kann und den Code nur einmal schreibt. -- [ ] Weil Lambdas sonst nicht erlaubt sind. - -? Ordne zu. -→ Wer hat geklickt? -= ActionListener -> Datentyp eines Zuhörers -= ActionEvent -> das Ereignis e mit den Infos zum Klick -= JPanel -> eine Fläche mit eigenem Layout -::: -`,Sw=`--- -titel: Innere Klassen -bereich: Fenster mit Swing -kurz: Einen Zuhörer als benannte innere Klasse, als anonyme Klasse und als Lambda schreiben und wissen, wer was sehen darf. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Zuhörer als benannte innere Klasse schreiben -- einen Zuhörer als anonyme Klasse schreiben -- erklären, was beim Lambda weggelassen wird -- erklären, welche Schreibweise welche Variablen sieht -- einen Zähler mit drei Knöpfen in drei Schreibweisen bauen -::: - -## Das Ziel - -Den Klickzähler aus Lektion 32 kennst du schon. Heute schreibst du seinen Zuhörer auf **drei Arten**, die alle genau dasselbe tun: - -1. als **benannte innere Klasse**, wie man es vor Java 8 meistens gemacht hat, -2. als **anonyme Klasse**, die du aus Lektion 30 („ActionListener ist ein Interface“) kennst, -3. als **Lambda** mit dem Pfeil, so wie bisher. - -In fremdem Code, in Büchern und im Internet triffst du alle drei Schreibweisen. Nach dieser Lektion erkennst du sie. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Erinnerung: der Vertrag - -Ein Zuhörer ist alles, was den Vertrag \`ActionListener\` erfüllt. Darin steht genau eine Methode: - -\`\`\` -public void actionPerformed(ActionEvent e) -\`\`\` - -Der Knopf ruft sie bei jedem Klick auf. Die drei Schreibweisen sind drei Wege, diese eine Methode zu liefern. - -## 1. Die benannte innere Klasse - -Eine **innere Klasse** steht innerhalb einer anderen Klasse. Sie hat einen Namen, hier \`Plus\`, und erfüllt den Vertrag mit \`implements ActionListener\`: - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Zaehler { - static int anzahl = 0; - static JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - static class Plus implements ActionListener { - public void actionPerformed(ActionEvent e) { - anzahl++; - zahl.setText("" + anzahl); - } - } - - public static void main(String[] args) { - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - JButton nochmal = new JButton("Auch +1"); - plus.addActionListener(new Plus()); - nochmal.addActionListener(new Plus()); - - fenster.add(zahl); - fenster.add(plus); - fenster.add(nochmal); - fenster.setVisible(true); - } -} -\`\`\` - -- \`new Plus()\` erzeugt ein Objekt der Klasse \`Plus\`. Das ist der Zuhörer, den der Knopf bekommt. -- Weil die Klasse einen Namen hat, kannst du sie bei mehreren Knöpfen benutzen, hier bei \`plus\` und \`nochmal\`. -- Die Klasse steht **außerhalb** von \`main\`. Darum sieht sie nur, was in der Klasse \`Zaehler\` steht: \`anzahl\` und \`zahl\`. Deshalb ist \`zahl\` diesmal eine Variable der Klasse und nicht von \`main\`. -- \`static class\` brauchst du, weil \`main\` static ist. Ohne \`static\` meldet javac „non-static variable this cannot be referenced from a static context“. - -## 2. Die anonyme Klasse - -Brauchst du den Zuhörer nur an einer Stelle, kannst du die Klasse direkt dort schreiben, wo das Objekt gebraucht wird. Sie bekommt dann keinen Namen: - -\`\`\`java -plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - anzahl++; - zahl.setText("" + anzahl); - } -}); -\`\`\` - -- \`new ActionListener() { … }\` heißt: „ein neues Objekt, das den Vertrag ActionListener erfüllt, und zwar so: …“. -- Am Ende steht \`});\`: die geschweifte Klammer schließt die Klasse, die runde den Aufruf von \`addActionListener\`. -- Die anonyme Klasse steht **in** \`main\`. Sie sieht deshalb auch die Variablen von \`main\`, solange diese sich nicht mehr ändern. - -::: quiz -? Was schließt \`});\` am Ende der anonymen Klasse? -- [x] \`}\` schließt die Klasse, \`)\` den Aufruf von \`addActionListener\`. -- [ ] \`}\` schließt \`main\`, \`)\` die Klasse. -- [ ] Beides schließt die Methode \`actionPerformed\`. -> Die Methode \`actionPerformed\` endet schon eine Zeile davor mit ihrer eigenen \`}\`. -::: - -## 3. Das Lambda - -Das Lambda ist die Kurzform der anonymen Klasse, seit Java 8: - -\`\`\`java -plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); -}); -\`\`\` - -Weggelassen wird alles, was Java sowieso weiß: \`new ActionListener()\`, der Name \`actionPerformed\`, \`public void\` und der Typ \`ActionEvent\`. Übrig bleiben der Parameter \`e\`, der Pfeil und der Rumpf. Das geht nur, weil der Vertrag genau **eine** Methode hat. - -## Die drei im Vergleich - -| | benannte innere Klasse | anonyme Klasse | Lambda | -|---|---|---|---| -| hat einen Namen | ja, z. B. \`Plus\` | nein | nein | -| steht | in der Klasse, außerhalb von \`main\` | direkt beim Knopf | direkt beim Knopf | -| für mehrere Knöpfe | ja, \`new Plus()\` | nein | ja, in einer Variable (Lektion 37) | -| sieht Variablen von \`main\` | nein | ja, wenn sie sich nicht ändern | ja, wenn sie sich nicht ändern | -| Länge | am längsten | mittel | am kürzesten | - -::: ludwig -Wenn sich eine Variable von \`main\` im Zuhörer ändern soll, wie \`anzahl++\`, muss sie in die Klasse, egal welche Schreibweise du nimmst. Das war das Problem aus Lektion 32. -::: - -::: angeberwissen -Schau nach dem Übersetzen in den Ordner mit den \`.class\`-Dateien. Für jede Klasse legt javac eine Datei an: \`Zaehler.class\`, für die innere Klasse \`Zaehler$Plus.class\` und für die anonyme Klasse \`Zaehler$1.class\`. Weil sie keinen Namen hat, bekommt sie eine Nummer. Für ein Lambda gibt es keine eigene Datei, Java baut es erst beim Start. -::: - -## Spiel: Welche Schreibweise? - -::: spiel frage -titel: Spiel: Welche Schreibweise? -intro: Ludwig zeigt dir Zuhörer aus fremden Programmen. Welche Schreibweise ist das? Sechs Runden. -frage: Welche Schreibweise ist das? -antworten: benannte innere Klasse | anonyme Klasse | Lambda - -runde: - knopf.addActionListener(e -> text.setText("Hallo")); -richtig: Lambda -> Der Pfeil \`->\` verrät das Lambda. - -runde: - knopf.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - text.setText("Hallo"); - } - }); -richtig: anonyme Klasse -> \`new ActionListener() {\` und dahinter eine ganze Methode: Hier wird eine Klasse ohne Namen geschrieben. - -runde: - static class Gruss implements ActionListener { - public void actionPerformed(ActionEvent e) { - text.setText("Hallo"); - } - } -richtig: benannte innere Klasse -> Die Klasse heißt \`Gruss\` und erfüllt den Vertrag mit \`implements ActionListener\`. - -runde: - knopf.addActionListener(new Gruss()); -richtig: benannte innere Klasse -> \`new Gruss()\` erzeugt ein Objekt der Klasse \`Gruss\`. Die Klasse selbst steht woanders, sie hat einen Namen. - -runde: - ActionListener taste = e -> { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - }; -richtig: Lambda -> Ein Lambda in einer Variable, wie beim Zahlenschloss in Lektion 37. Der Pfeil verrät es. - -runde: - ActionListener taste = new ActionListener() { - public void actionPerformed(ActionEvent e) { - anzeige.setText(anzeige.getText() + e.getActionCommand()); - } - }; -richtig: anonyme Klasse -> Auch eine anonyme Klasse kann in einer Variable stehen. Dann kann man sie wie das Lambda bei mehreren Knöpfen anmelden. -::: - -## Aufgabe: Ein Zähler, drei Schreibweisen - -Baue den Klickzähler mit drei Knöpfen: - -- **+1** bekommt die benannte innere Klasse \`Plus\`. -- **-1** bekommt eine anonyme Klasse. -- **Reset** bekommt ein Lambda. - -Probiere aus, ob alle drei Knöpfe wie erwartet zählen. Lass dann bei der Klasse \`Plus\` das \`static\` weg und lies die Fehlermeldung von javac. - -**Du bist fertig, wenn …** - -- **+1** hoch-, **-1** herunterzählt und **Reset** auf 0 setzt, -- im Code genau eine benannte innere Klasse, eine anonyme Klasse und ein Lambda stehen, -- du ohne \`static\` bei \`Plus\` die Fehlermeldung von javac gelesen hast und erklären kannst, was sie bedeutet. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Zuhörer als benannte innere Klasse schreiben. → 1. Die benannte innere Klasse -- Ich kann einen Zuhörer als anonyme Klasse schreiben. → 2. Die anonyme Klasse -- Ich kann erklären, was beim Lambda weggelassen wird. → 3. Das Lambda -- Ich kann erklären, welche Schreibweise welche Variablen sieht. → Die drei im Vergleich -- Ich kann einen Zähler mit drei Knöpfen in drei Schreibweisen bauen. → Aufgabe: Ein Zähler, drei Schreibweisen -::: - -## Wissenstest - -::: quiz -? Warum geht die Kurzform mit dem Pfeil bei ActionListener? -→ 3. Das Lambda -- [ ] Weil ActionListener eine Klasse ist. -- [x] Weil der Vertrag genau eine Methode hat. -- [ ] Weil der Knopf es so verlangt. - -? Welche Schreibweise kannst du mit new Plus() bei mehreren Knöpfen benutzen, ohne sie in eine Variable zu speichern? -→ 1. Die benannte innere Klasse -- [x] die benannte innere Klasse -- [ ] die anonyme Klasse -- [ ] das Lambda - -? Warum steht static vor class Plus? -→ 1. Die benannte innere Klasse -- [ ] Damit die Klasse schneller ist. -- [x] Weil main static ist und sonst kein Objekt von Plus erzeugen kann. -- [ ] Weil jede Klasse static sein muss. - -? Ordne zu. -→ Die drei im Vergleich -= class Plus implements ActionListener -> benannte innere Klasse -= new ActionListener() { … } -> anonyme Klasse -= der Pfeil hinter e -> Lambda -::: -`,Tw=`--- -titel: Zwei Reiter -bereich: Fenster mit Swing -kurz: Ein Fenster mit zwei Reitern bauen, Daten von einem Reiter zum anderen bringen und die Liste als Textdatei speichern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Fenster mit zwei Reitern bauen -- mit append Zeilen an eine JTextArea anhängen -- erklären, warum ein Zuhörer Bausteine auf beiden Reitern benutzen kann -- im Zuhörer mit try und catch eine Datei speichern -- eine Einkaufsliste bauen, die mitzählt, wie viele Dinge darauf stehen -::: - -## Das Ziel - -Ludwigs Einkaufsliste aus Lektion 23 bekommt ein Fenster. Es hat zwei **Reiter**, wie die Tabs im Browser: - -- Im Reiter **Eingabe** tippst du ein, was du brauchst, und klickst auf **Hinzufügen**. -- Im Reiter **Liste** steht alles, was du eingegeben hast. Ein Knopf **Speichern** schreibt die Liste in eine Textdatei. - -Neu sind die Reiter (\`JTabbedPane\`), ein Textbereich mit mehreren Zeilen (\`JTextArea\`) und das Speichern aus einem Zuhörer heraus. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Reiter mit JTabbedPane - -Jeder Reiter zeigt eine eigene Fläche. Die Flächen sind JPanels, wie das Tastenfeld beim Zahlenschloss in Lektion 37: - -\`\`\`java -JTabbedPane reiter = new JTabbedPane(); -JPanel eingabe = new JPanel(new GridLayout(0, 1)); -JPanel liste = new JPanel(new BorderLayout()); - -reiter.addTab("Eingabe", eingabe); -reiter.addTab("Liste", liste); -fenster.add(reiter); -\`\`\` - -| Aufruf | Bedeutung | -|---|---| -| \`addTab("Eingabe", eingabe)\` | neuer Reiter mit der Beschriftung „Eingabe“, er zeigt das JPanel \`eingabe\` | -| \`setSelectedIndex(1)\` | wechselt zum zweiten Reiter, gezählt wird ab 0 | -| \`setTitleAt(1, "Liste (3)")\` | ändert die Beschriftung des zweiten Reiters | - -Beim Start ist der Reiter offen, der zuerst hinzugefügt wurde. - -## Ein Textbereich mit mehreren Zeilen - -Ein \`JTextField\` hat nur eine Zeile. Für die Liste brauchst du eine \`JTextArea\`: - -\`\`\`java -JTextArea bereich = new JTextArea(); -bereich.setEditable(false); -bereich.append("Heu\\n"); -\`\`\` - -- \`append\` hängt Text hinten an. Das \`"\\n"\` beginnt danach eine neue Zeile, wie beim Zerlegen mit \`split("\\n")\` in Lektion 24. -- \`setEditable(false)\` verhindert, dass man in der Liste selbst herumtippt. -- \`getText()\` liefert den ganzen Text mit allen Zeilen. - -::: quiz -? Was macht \`bereich.append("Heu\\n")\`? -- [ ] Es ersetzt den ganzen Text durch „Heu“. -- [x] Es hängt „Heu“ hinten an und beginnt danach eine neue Zeile. -- [ ] Es schreibt „Heu\\n“ genau so in die Liste. -> \`append\` hängt an, \`"\\n"\` ist der Zeilenumbruch. -::: - -## Von einem Reiter zum anderen - -Der Knopf **Hinzufügen** steht im Reiter Eingabe, der Textbereich im Reiter Liste. Trotzdem kann der Zuhörer beide benutzen. Alle Bausteine sind Variablen in \`main\`, und das Lambda sieht sie alle, egal auf welchem Reiter sie später angezeigt werden: - -\`\`\`java -hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\\n"); - info.setText(feld.getText() + " steht auf der Liste."); - feld.setText(""); -}); -\`\`\` - -::: ludwig -Ein Baustein kann nur auf **einer** Fläche liegen. Fügst du denselben Knopf erst zu \`eingabe\` und dann zu \`liste\` hinzu, nimmt Swing ihn bei \`eingabe\` wieder weg. Er erscheint nur im Reiter Liste. -::: - -## Speichern im Zuhörer - -Zum Speichern nimmst du den \`PrintWriter\` aus Lektion 23. Im Zuhörer gibt es aber einen Haken: Den Ausweg \`throws Exception\` aus Lektion 23 gibt es hier nicht. Das Lambda ist eine eigene Methode, \`actionPerformed\`, und die darf laut Vertrag keine \`FileNotFoundException\` weiterwerfen. Darum steht das Speichern in try und catch: - -\`\`\`java -speichern.addActionListener(e -> { - try { - PrintWriter aus = new PrintWriter("einkaufsliste.txt"); - aus.print(bereich.getText()); - aus.close(); - info.setText("Gespeichert in einkaufsliste.txt"); - reiter.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Speichern ging nicht."); - } -}); -\`\`\` - -- Die Exception heißt hier \`ex\`, weil der Name \`e\` schon für das Ereignis vergeben ist. -- Nach dem Speichern springt das Fenster mit \`setSelectedIndex(0)\` zurück zum Reiter Eingabe, wo die Meldung steht. -- VisuStruct zeigt die gespeicherte Datei unter der Konsole, sobald du auf Speichern geklickt hast. - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class Einkaufsliste { - public static void main(String[] args) { - JFrame fenster = new JFrame("Einkaufsliste"); - fenster.setSize(300, 250); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane reiter = new JTabbedPane(); - - JPanel eingabe = new JPanel(new GridLayout(0, 1)); - JTextField feld = new JTextField(); - JButton hinzufuegen = new JButton("Hinzufügen"); - JLabel info = new JLabel("Noch nichts auf der Liste."); - eingabe.add(new JLabel("Was brauchst du?")); - eingabe.add(feld); - eingabe.add(hinzufuegen); - eingabe.add(info); - - JPanel liste = new JPanel(new BorderLayout()); - JTextArea bereich = new JTextArea(); - bereich.setEditable(false); - JButton speichern = new JButton("Speichern"); - liste.add(bereich, BorderLayout.CENTER); - liste.add(speichern, BorderLayout.SOUTH); - - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); - - hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\\n"); - info.setText(feld.getText() + " steht auf der Liste."); - feld.setText(""); - }); - - speichern.addActionListener(e -> { - try { - PrintWriter aus = new PrintWriter("einkaufsliste.txt"); - aus.print(bereich.getText()); - aus.close(); - info.setText("Gespeichert in einkaufsliste.txt"); - reiter.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Speichern ging nicht."); - } - }); - - fenster.add(reiter); - fenster.setVisible(true); - } -} -\`\`\` - -Tippe Heu ein, klicke auf Hinzufügen, dann Äpfel und noch einmal Hinzufügen. Wechsle zum Reiter Liste und klicke auf Speichern. - -## Spiel: Wo landet es? - -::: spiel frage -titel: Spiel: Wo landet es? -intro: Ludwig baut Fenster mit Reitern. Lies den Code und sag voraus, was du siehst. Sechs Runden. - -runde: - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); -frage: Welcher Reiter ist beim Start offen? -- [x] Eingabe -- Liste -- keiner -> Beim Start ist der Reiter offen, der zuerst hinzugefügt wurde. - -runde: - reiter.addTab("Liste", liste); - reiter.addTab("Eingabe", eingabe); -frage: Welcher Reiter ist beim Start offen? -- Eingabe -- [x] Liste -- beide -> Diesmal kommt Liste zuerst. Die Reihenfolge von \`addTab\` bestimmt die Reihenfolge der Reiter. - -runde: - reiter.addTab("Eingabe", eingabe); - reiter.addTab("Liste", liste); - reiter.addTab("Hilfe", hilfe); - reiter.setSelectedIndex(1); -frage: Welcher Reiter ist offen? -- Eingabe -- [x] Liste -- Hilfe -> Gezählt wird ab 0: 0 ist Eingabe, 1 ist Liste, 2 ist Hilfe. - -runde: - hinzufuegen.addActionListener(e -> { - bereich.append(feld.getText() + "\\n"); - feld.setText(""); - }); - // Du tippst Heu, klickst Hinzufügen, tippst Äpfel, klickst Hinzufügen. -frage: Was steht danach im Reiter Liste? -- Äpfel -- HeuÄpfel -- [x] Heu\\nÄpfel -> \`append\` hängt an, und jedes \`"\\n"\` beginnt eine neue Zeile. - -runde: - JButton knopf = new JButton("OK"); - eingabe.add(knopf); - liste.add(knopf); -frage: Wo erscheint der Knopf OK? -- in beiden Reitern -- nur im Reiter Eingabe -- [x] nur im Reiter Liste -> Ein Baustein kann nur auf einer Fläche liegen. Beim zweiten \`add\` nimmt Swing ihn von \`eingabe\` weg. - -runde: - speichern.addActionListener(e -> { - bereich.setText(""); - reiter.setTitleAt(1, "Liste (leer)"); - reiter.setSelectedIndex(0); - }); - // Der Knopf speichern liegt im Reiter Liste. -frage: Was siehst du nach dem Klick? -- den leeren Reiter Liste -- [x] den Reiter Eingabe, und der zweite Reiter heißt „Liste (leer)“ -- den Reiter Eingabe, der zweite Reiter heißt weiter „Liste“ -> Der Zuhörer leert den Bereich, benennt den Reiter um und wechselt zu Reiter 0. Alles passiert bei einem Klick. -::: - -## Aufgabe: Einkaufsliste mit Reitern - -Baue das Programm nach und probiere es aus. - -Dann mach es besser: - -- Leere Eingaben kommen nicht auf die Liste. Prüfe vorher \`feld.getText().isEmpty()\` (Lektion 17). -- Der Reiter Liste zeigt in seinem Titel, wie viele Dinge auf der Liste stehen: „Liste (3)“. Zähle in einer \`static int\`-Variable mit und benutze \`setTitleAt\`. -- Zusatz: Ein dritter Reiter **Hilfe** mit einem JLabel, das erklärt, wie das Programm geht. - -**Du bist fertig, wenn …** - -- ein eingegebener Eintrag nach dem Klick im Reiter Liste steht, -- ein leeres Feld nichts zur Liste hinzufügt, -- der Reiter nach drei Einträgen „Liste (3)“ heißt. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Fenster mit zwei Reitern bauen. → Reiter mit JTabbedPane -- Ich kann mit append Zeilen an eine JTextArea anhängen. → Ein Textbereich mit mehreren Zeilen -- Ich kann erklären, warum ein Zuhörer Bausteine auf beiden Reitern benutzen kann. → Von einem Reiter zum anderen -- Ich kann im Zuhörer mit try und catch eine Datei speichern. → Speichern im Zuhörer -- Ich kann eine Einkaufsliste bauen, die mitzählt, wie viele Dinge darauf stehen. → Aufgabe: Einkaufsliste mit Reitern -::: - -## Wissenstest - -::: quiz -? Welcher Reiter ist nach reiter.setSelectedIndex(0) offen? -→ Reiter mit JTabbedPane -- [x] der erste -- [ ] der zweite -- [ ] keiner - -? Warum steht das Speichern im Zuhörer in try und catch? -→ Speichern im Zuhörer -- [ ] Weil PrintWriter nur in try funktioniert. -- [x] Weil actionPerformed keine FileNotFoundException weiterwerfen darf. -- [ ] Weil main kein throws hat. - -? Was ist der Unterschied zwischen JTextField und JTextArea? -→ Ein Textbereich mit mehreren Zeilen -- [ ] Es gibt keinen. -- [x] JTextArea kann mehrere Zeilen zeigen. -- [ ] JTextField kann man nicht beschreiben. - -? Ordne zu. -→ Reiter mit JTabbedPane -= JTabbedPane -> eine Fläche mit Reitern -= JPanel -> die Fläche eines Reiters -= JTextArea -> ein Textbereich mit mehreren Zeilen -::: -`,_w=`--- -titel: Model View Controller -bereich: Fenster mit Swing -kurz: Den Klickzähler in Model, View und Controller zerlegen und verstehen, wer mit wem redet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- sagen, was Model, View und Controller jeweils tun -- ein Model ohne Swing schreiben und ohne Fenster testen -- erklären, was new Zaehler() baut und warum dort kein static steht -- die vier Nachrichten nach einem Klick nennen -- eine zweite View dazubauen, ohne das Model zu ändern -- einen Zähler mit −1, Reset und Grenzen im Model bauen -::: - -## Das Ziel - -Große Programme mit Fenstern haben fast immer denselben Aufbau. Er heißt **Model View Controller**, kurz **MVC**: - -- Das **Model** hält die Daten und kennt ihre Regeln. -- Die **View** zeigt die Daten nur an. -- Der **Controller** reagiert auf Klicks und gibt Bescheid. - -Du lernst MVC am Klickzähler aus Lektion 32. Er wird Schritt für Schritt in drei Teile zerlegt. Am Ende zeigt er denselben Zählerstand zweimal an: als Zahl und als Balken. - -Schalte im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** ein. - -## Alles in einer Klasse - -So sah der Klickzähler bisher aus, alles steckt in \`main\` und einer Variable der Klasse: - -\`\`\`java -static int anzahl = 0; - -public static void main(String[] args) { - // ... Fenster bauen ... - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - anzahl++; - zahl.setText("" + anzahl); - }); -} -\`\`\` - -Das läuft. Aber stell dir vor, der Zählerstand soll zusätzlich als Balken erscheinen, später noch in einem zweiten Fenster und in einer Datei. Dann muss jeder Zuhörer an alle Anzeigen denken. Rechnen und Anzeigen sind ineinander verknotet, und mit jeder Anzeige wird der Knoten größer. - -MVC löst den Knoten: Jede Aufgabe bekommt ihre eigene Klasse. - -## Das Model - -Das Model ist der Zähler selbst, ganz ohne Swing: - -\`\`\`java -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} -\`\`\` - -- Diesmal steht vor \`wert\` und den Methoden kein \`static\`. Dann gehören sie zu einem **Objekt**: \`new Zaehler()\` baut einen Zähler mit eigenem \`wert\`, so wie \`new JButton("+1")\` einen Knopf mit eigener Beschriftung baut. -- \`model.erhoehen()\` ruft die Methode dieses einen Zählers auf, genau wie \`knopf.setText(…)\`. -- Wer den Wert wissen will, fragt mit \`getWert()\`. Ändern geht nur über \`erhoehen()\`. - -Weil das Model kein Fenster braucht, kannst du es ganz ohne Fenster ausprobieren: - -\`\`\`java -public class ZaehlerTest { - public static void main(String[] args) { - Zaehler model = new Zaehler(); - model.erhoehen(); - model.erhoehen(); - System.out.println(model.getWert()); - } -} - -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} -\`\`\` - -Die Ausgabe ist 2. Mehrere Klassen dürfen in einer Datei stehen. Nur eine davon ist \`public\`, und die enthält \`main\`. - -## Die View - -Die View zeigt den Wert an und macht sonst nichts. Sie rechnet nicht und weiß nicht, woher die Zahl kommt: - -\`\`\`java -class ZahlAnzeige { - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - void aktualisieren(int wert) { - zahl.setText("" + wert); - } -} -\`\`\` - -- Die View bekommt den Wert mit \`aktualisieren(int wert)\` geliefert und schreibt ihn in ihr JLabel. -- Fenster und Knopf baust du wie immer in \`main\`. Mit \`fenster.add(view.zahl)\` kommt das JLabel der View ins Fenster. - -## Der Controller - -Der Controller ist der Zuhörer am Knopf. Er verbindet Model und View: - -\`\`\`java -Zaehler model = new Zaehler(); -ZahlAnzeige view = new ZahlAnzeige(); - -plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); -}); -\`\`\` - -- Erst ändert er das Model, dann holt er den neuen Wert und gibt ihn der View. -- Das Problem aus Lektion 32 ist weg: Die Variable \`model\` ändert sich nie, sie zeigt immer auf denselben Zähler. Nur der \`wert\` darin ändert sich. Deshalb darf das Lambda \`model\` benutzen, ganz ohne \`static\`. - -## Wer redet mit wem? - -Bei jedem Klick laufen vier Nachrichten, immer in derselben Reihenfolge: - -::: grafik mvc-klick.svg -Die Pfeile leuchten der Reihe nach auf. Das Model zählt beim zweiten Pfeil hoch, die Anzeige erst beim vierten. -::: - -1. Der Knopf meldet den Klick an den Controller (\`actionPerformed\`). -2. Der Controller sagt dem Model: \`erhoehen()\`. -3. Der Controller fragt das Model nach dem neuen Wert: \`getWert()\`. -4. Der Controller gibt den Wert an die View: \`aktualisieren(wert)\`. - -Das Model redet mit niemandem. Es wartet, bis es gefragt wird. - -::: ludwig -Merk dir: Das Model weiß nichts von Fenstern. In \`Zaehler\` steht kein einziges \`JLabel\` und kein \`import javax.swing\`. Wenn du dort Swing brauchst, gehört der Code in die View. -::: - -::: quiz -? Mit wem redet das Model von sich aus? -- [ ] Mit der View -- [ ] Mit dem Knopf -- [x] Mit niemandem -> Das Model wartet, bis der Controller es fragt. -::: - -## Der Gewinn: eine zweite View - -Jetzt soll der Zählerstand zusätzlich als Balken erscheinen. Dafür kommt eine zweite View dazu. Ein \`JProgressBar\` ist ein Balken, der sich füllt, hier von 0 bis 10: - -\`\`\`java -class BalkenAnzeige { - JProgressBar balken = new JProgressBar(0, 10); - - void aktualisieren(int wert) { - balken.setValue(wert); - } -} -\`\`\` - -Der Controller bekommt eine Zeile mehr. Das Model bleibt genau, wie es war: - -\`\`\`java -plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); -}); -\`\`\` - -Genau das ist der Gewinn von MVC: Du kannst Anzeigen dazubauen oder austauschen, ohne das Model anzufassen. - -## Das ganze Programm - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -class Zaehler { - int wert = 0; - - void erhoehen() { - wert++; - } - - int getWert() { - return wert; - } -} - -class ZahlAnzeige { - JLabel zahl = new JLabel("0", SwingConstants.CENTER); - - void aktualisieren(int wert) { - zahl.setText("" + wert); - } -} - -class BalkenAnzeige { - JProgressBar balken = new JProgressBar(0, 10); - - void aktualisieren(int wert) { - balken.setValue(wert); - } -} - -public class KlickZaehler { - public static void main(String[] args) { - Zaehler model = new Zaehler(); - ZahlAnzeige view = new ZahlAnzeige(); - BalkenAnzeige balken = new BalkenAnzeige(); - - JFrame fenster = new JFrame("Klickzähler"); - fenster.setSize(250, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); - }); - - fenster.add(view.zahl); - fenster.add(balken.balken); - fenster.add(plus); - fenster.setVisible(true); - } -} -\`\`\` - -Klick ein paar Mal auf +1 und sieh zu, wie Zahl und Balken gemeinsam wachsen. - -::: angeberwissen -MVC hat sich der Norweger Trygve Reenskaug 1979 ausgedacht, im Forschungszentrum Xerox PARC in Kalifornien. Dort standen damals schon Computer mit Fenstern und Maus. Im selben Jahr besuchte Steve Jobs das Labor und war so begeistert, dass Apple danach Computer mit Fenstern und Maus baute. -::: - -## Spiel: Wohin gehört der Code? - -::: spiel frage -titel: Spiel: Wohin gehört der Code? -intro: Ludwig hat Code aus einem MVC-Programm auf einen Haufen geworfen. Sortier jedes Stück ein. Acht Runden. -frage: Wohin gehört dieser Code? -antworten: Model | View | Controller - -runde: - int wert = 0; -richtig: Model -> Der Zählerstand sind die Daten. Daten wohnen im Model. - -runde: - zahl.setText("" + wert); -richtig: View -> Etwas auf dem Bildschirm anzeigen ist die Aufgabe der View. - -runde: - plus.addActionListener(e -> model.erhoehen()); -richtig: Controller -> Auf einen Klick reagieren und dem Model Bescheid geben: Das macht der Controller. - -runde: - void erhoehen() { - wert++; - } -richtig: Model -> Das Model weiß selbst, wie es seine Daten ändert. - -runde: - balken.setValue(wert); -richtig: View -> Ein Balken, der sich füllt, zeigt nur an. Also View. - -runde: - void erhoehen() { - if (wert < 10) { - wert++; - } - } -richtig: Model -> „Höchstens 10“ ist eine Regel für die Daten. Regeln gehören ins Model, dann gelten sie für jeden Knopf und jede View. - -runde: - view.aktualisieren(model.getWert()); -richtig: Controller -> Den Wert beim Model abholen und der View geben: Das ist der Controller in der Mitte. - -runde: - JLabel zahl = new JLabel("0", SwingConstants.CENTER); -richtig: View -> Ein JLabel ist ein Baustein der Oberfläche. Im Model steht kein Swing. -::: - -## Spiel: Wer ist dran? - -::: spiel reihenfolge -titel: Spiel: Wer ist dran? -intro: Nach einem Klick reden Model, View und Controller miteinander, aber nur in einer Reihenfolge funktioniert es. Bring die Schritte in die richtige Reihenfolge. Fünf Runden. - -runde: -frage: Du klickst im Klickzähler auf +1. Was passiert der Reihe nach? -- Du klickst auf +1. -- Der Controller bekommt \`actionPerformed\`. -- Der Controller ruft \`model.erhoehen()\` auf. -- Das Model zählt \`wert\` hoch. -- Der Controller holt \`model.getWert()\`. -- Die View zeigt die neue Zahl. -> Klick, Controller, Model ändern, Wert holen, anzeigen. Das sind die vier Pfeile aus der Grafik, mit dem Hochzählen in der Mitte. - -runde: - plus.addActionListener(e -> { - model.erhoehen(); - view.aktualisieren(model.getWert()); - balken.aktualisieren(model.getWert()); - }); -frage: Was passiert nach einem Klick auf +1 der Reihe nach? -- Der Klick auf +1 -- \`model.erhoehen()\` -- Die Zahl zeigt den neuen Wert. -- Der Balken wächst. -> Der Zuhörer arbeitet seine Zeilen von oben nach unten ab: erst das Model, dann die Zahl, dann der Balken. - -runde: -frage: Bring den Controller in die richtige Reihenfolge, damit die View die neue Zahl zeigt. -- \`plus.addActionListener(e -> {\` -- \`model.erhoehen();\` -- \`view.aktualisieren(model.getWert());\` -- \`});\` -> Erst ändern, dann anzeigen. Andersherum zeigt die View noch den alten Wert. - -runde: - reset.addActionListener(e -> { - model.zuruecksetzen(); - view.aktualisieren(model.getWert()); - }); -frage: Der Zähler steht auf 5. Du klickst auf Reset. Was passiert der Reihe nach? -- Du klickst auf Reset. -- Das Model setzt \`wert\` auf 0. -- \`model.getWert()\` liefert 0. -- Die View zeigt 0. -> Auch Reset läuft über den Controller ins Model. Die View erfährt den neuen Wert erst ganz am Schluss. - -runde: -frage: Ein Test ohne Fenster soll 1 ausgeben. In welcher Reihenfolge stehen die Zeilen in main? -- \`Zaehler model = new Zaehler();\` -- \`model.erhoehen();\` -- \`System.out.println(model.getWert());\` -> Erst das Objekt bauen, dann damit arbeiten, zum Schluss nachsehen. Das Model lässt sich ganz ohne Fenster testen. -::: - -## Aufgabe: Ein Zähler mit Grenzen - -Baue das ganze Programm und probier es aus. - -Dann mach es besser: - -- Ein Knopf **−1** zählt herunter. Das Model bekommt dafür eine Methode \`verringern()\`, der Controller einen zweiten Zuhörer. -- Ein Knopf **Reset** setzt den Zähler mit \`zuruecksetzen()\` auf 0. -- Das Model achtet selbst auf die Grenzen: nie unter 0 und nie über 10, denn dann ist der Balken voll. -- Extra: eine dritte View \`KonsolenAnzeige\`, deren \`aktualisieren\` mit \`System.out.println\` „Neuer Wert: 3“ ausgibt. Eine View muss kein Fenster sein. - -**Du bist fertig, wenn …** - -- **+1**, **−1** und **Reset** richtig zählen, -- der Zähler nie unter 0 und nie über 10 geht, egal wie oft du klickst, -- Zahl und Balken immer denselben Wert zeigen, -- in der Klasse des Model kein \`JButton\`, \`JLabel\` oder anderes Swing vorkommt. - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann sagen, was Model, View und Controller jeweils tun. → Das Ziel -- Ich kann ein Model ohne Swing schreiben und ohne Fenster testen. → Das Model -- Ich kann erklären, was new Zaehler() baut und warum dort kein static steht. → Das Model -- Ich kann die vier Nachrichten nach einem Klick nennen. → Wer redet mit wem? -- Ich kann eine zweite View dazubauen, ohne das Model zu ändern. → Der Gewinn: eine zweite View -- Ich kann einen Zähler mit −1, Reset und Grenzen im Model bauen. → Aufgabe: Ein Zähler mit Grenzen -::: - -## Wissenstest - -::: quiz -? Wo steht die Regel „Der Zähler geht nie über 10“? -→ Das Model -- [x] im Model -- [ ] in der View -- [ ] im Controller -> Regeln über die Daten gehören ins Model. Dann gelten sie, egal welcher Knopf drückt. - -? Was macht die View? -→ Das Ziel -- [ ] Sie rechnet den neuen Wert aus. -- [x] Sie zeigt den Wert an, den sie bekommt. -- [ ] Sie hört auf Klicks. - -? Warum darf das Lambda model benutzen, obwohl model eine Variable von main ist? -→ Wer redet mit wem? -- [ ] Weil Zaehler public ist. -- [x] Weil model immer auf denselben Zähler zeigt; nur der wert darin ändert sich. -- [ ] Weil Lambdas alle Variablen ändern dürfen. - -? Was musst du am Model ändern, wenn eine zweite View dazukommt? -→ Der Gewinn: eine zweite View -- [x] nichts -- [ ] eine zweite Methode erhoehen -- [ ] ein zweites JLabel - -? Ordne zu. -→ Das Ziel -= Zaehler mit wert und erhoehen() -> Model -= ZahlAnzeige mit aktualisieren(int wert) -> View -= der Zuhörer am Knopf -> Controller -::: -`,Aw=`Fachbegriffe für den Lernbereich. Kommt ein Begriff in einem Schritt vor, wird er dort einmal markiert und zeigt beim -Hovern oder Antippen seine Erklärung. Steht er im Schritt fett, wird er gerade erklärt und deshalb nicht markiert. -\`auch:\` nennt weitere Formen, die ebenfalls erkannt werden. Groß- und Kleinschreibung zählt. - -### Algorithmus -auch: Algorithmen -Eine genaue Schritt-für-Schritt-Anleitung, mit der man eine Aufgabe löst. Jeder Schritt ist eindeutig. - -### Struktogramm -auch: Struktogramme, Struktogramms, Struktogrammen -Ein Algorithmus als Bild aus Kästen. Es wird von oben nach unten gelesen. - -### Programmablaufplan -auch: PAP, Programmablaufpläne, Programmablaufplans -Ein Algorithmus als Bild aus Symbolen, die mit Pfeilen verbunden sind (DIN 66001). Eine Raute ist eine Verzweigung. - -### Pseudocode -Ein Algorithmus als Text in normaler Sprache mit festen Wörtern wie WENN und SOLANGE. Die Einrückung zeigt, was zusammengehört. - -### Anweisung -auch: Anweisungen -Ein einzelner Schritt im Struktogramm, zum Beispiel eine Rechnung, eine Eingabe oder eine Ausgabe. - -### Sequenz -auch: Sequenzen -Schritte, die nacheinander von oben nach unten ausgeführt werden, jeder genau einmal. - -### Verzweigung -auch: Verzweigungen, bedingte Anweisung, bedingte Anweisungen -Ein Baustein, der mit einer Bedingung entscheidet, welcher von zwei Wegen ausgeführt wird. Im Lehrplan heißt sie auch bedingte Anweisung. - -### Zweig -auch: Zweige, Zweigen -Einer der beiden Wege einer Verzweigung: links für true, rechts für false. - -### Schleife -auch: Schleifen, Wiederholung, Wiederholungen, Wiederholungsstruktur, Wiederholungsstrukturen -Ein Baustein, der Schritte wiederholt, solange eine Bedingung gilt. Im Lehrplan heißt sie auch Wiederholungsstruktur. - -### Rumpf -Der Inhalt einer Schleife, also die Schritte, die wiederholt werden. - -### Durchlauf -auch: Durchläufe, Durchlaufs, Durchläufen -Ein einmaliges Ausführen des Inhalts einer Schleife. - -### Bedingung -auch: Bedingungen -Eine Frage, die nur mit ja (true) oder nein (false) beantwortet wird, zum Beispiel alter >= 18. - -### Platzhalter -Ein leerer Kasten mit dem Zeichen ø. Ziehst du ein Element darauf, wird er ersetzt. - -### Objektinspektor -Der Bereich unter dem Diagramm. Hier bearbeitest du Text und Einstellungen des Kastens, den du gerade angeklickt hast. - -### Simulation -Führt dein Struktogramm im Editor aus, auf Wunsch Schritt für Schritt. So siehst du, was passiert. - -### Variable -auch: Variablen -Eine beschriftete Kiste für einen Wert. Sie hat einen Namen, einen Datentyp und einen Wert. - -### CamelCase -auch: camelCase -Schreibweise für Namen aus mehreren Wörtern: zusammengeschrieben, jedes weitere Wort beginnt groß, z. B. \`preisMitSteuer\`. - -### case-sensitive -auch: Groß- und Kleinschreibung -Java unterscheidet Groß- und Kleinbuchstaben: \`zahl\`, \`Zahl\` und \`ZAHL\` sind drei verschiedene Namen. \`system\` statt \`System\` ist ein Fehler. - -### Konstante -auch: Konstanten -Eine Variable, deren Wert nach dem Festlegen gleich bleibt. Man schreibt \`const\` davor, z. B. \`const double MWST = 0.19\`. - -### Datentyp -auch: Datentypen, Datentyps -Legt fest, welche Art von Wert in eine Variable passt: int (ganze Zahl), double (Kommazahl), String (Text), boolean (true oder false) oder char (ein einzelnes Zeichen). - -### Zuweisung -auch: Zuweisungen -Das Zeichen =. Es rechnet die rechte Seite aus und speichert das Ergebnis in der Variablen links. - -### Hilfsvariable -auch: Hilfsvariablen -Eine zusätzliche Variable, die einen Wert kurz aufbewahrt, zum Beispiel beim Tauschen. - -### EVA-Prinzip -Eingabe, Verarbeitung, Ausgabe: erst Werte abfragen, dann rechnen, dann das Ergebnis anzeigen. - -### Problemanalyse -Du liest eine Aufgabe genau und klärst, was gegeben ist, was gesucht ist und wie man vom einen zum anderen kommt. - -### Vergleichsoperator -auch: Vergleichsoperatoren -Ein Zeichen, das zwei Werte vergleicht, zum Beispiel == oder <. Das Ergebnis ist true oder false. - -### Fallauswahl -auch: Mehrfachauswahl -Ein Baustein, der je nach Wert einer Variablen genau einen von mehreren Fällen ausführt. Im Lehrplan heißt sie auch Mehrfachauswahl. - -### kopfgesteuert -auch: kopfgesteuerte, kopfgesteuerten -Die Schleife prüft ihre Bedingung vor jedem Durchlauf. Ist sie gleich zu Beginn falsch, läuft die Schleife kein einziges Mal. - -### fußgesteuert -auch: fußgesteuerte, fußgesteuerten -Die Schleife prüft ihre Bedingung erst nach jedem Durchlauf. Sie läuft also mindestens einmal. - -### Zählvariable -auch: Zählvariablen -Eine Variable, die in einer Schleife mitzählt, meistens i. - -### Endlosschleife -auch: Endlosschleifen -Eine Schleife, deren Bedingung nie falsch wird. Sie hört nie von selbst auf. - -### Merker -Eine Variable vom Typ boolean, die mit false anfängt und auf true gesetzt wird, sobald etwas passiert ist. - -### Zustandsautomat -auch: Zustandsautomaten, Zustandsvariable, Zustandsvariablen -Ein Programm, das immer in genau einem Zustand ist und nach festen Regeln in den nächsten Zustand wechselt, z. B. eine Ampel. - -### Array -auch: Arrays -Eine Reihe von Fächern mit einem gemeinsamen Namen und demselben Datentyp. Im Lehrplan heißt es auch Feld. - -### Index -Die Nummer eines Fachs in einem Array. Gezählt wird ab 0. - -### char -Der Datentyp für ein einzelnes Zeichen. Er steht in einfachen Anführungszeichen, z. B. 'C'. "C" mit doppelten Anführungszeichen ist dagegen ein String. - -### Zeichencode -auch: Zeichencodes, ASCII-Code, ASCII-Tabelle -Die Zahl, die hinter einem Zeichen steckt. In der ASCII-Tabelle hat A den Code 65, B 66 und so weiter. In Java wird mit (int) aus einem Zeichen sein Code und mit (char) aus einem Code das Zeichen. - -### Zeichenkette -auch: Zeichenketten -Ein Text, also eine Reihe von Zeichen. In Java heißt der Datentyp String. Die Zeichen werden ab 0 gezählt. - -### 2D-Array -auch: 2D-Arrays, zweidimensionales Array, zweidimensionale Arrays -Ein Array mit Zeilen und Spalten, wie eine Tabelle. Ein Fach braucht zwei Indizes, z. B. \`sitze[1][2]\`. - -### Unterprogramm -auch: Unterprogramme, Unterprogramms, Unterprogrammen, Methode, Methoden, Prozedur, Prozeduren, Funktion, Funktionen -Ein eigenes Struktogramm für eine Teilaufgabe. Es wird mit einem Aufruf ausgeführt. Im Lehrplan und in Java heißt es auch Methode, je nach Art auch Prozedur oder Funktion. - -### Parameter -auch: Parametern -Eine Variable in der Kopfzeile eines Unterprogramms. Beim Aufruf bekommt sie den Wert aus den Klammern. - -### Überladung -auch: überladen, überladene, überladenen, overloading -Mehrere Methoden mit demselben Namen, aber verschiedenen Parametern, z. B. \`mbot.forward(30)\` und \`mbot.forward(2, "Umdrehungen")\`. Java wählt beim Aufruf die passende nach Anzahl und Typ der Parameter. - -### Spaghetticode -Ein Programm, das so kreuz und quer springt, dass man den Ablauf kaum noch verfolgen kann. Struktogramme verhindern das. - -### Wertübergabe -auch: call by value -Englisch call by value. Ein Unterprogramm bekommt eine Kopie des Werts. Ändert es seinen Parameter, bleibt die Variable des Aufrufers unverändert. - -### Referenzübergabe -auch: call by reference -Englisch call by reference. Ein Unterprogramm bekommt einen Verweis auf dieselben Daten, z. B. auf ein Array. Ändert es ein Fach, sieht auch der Aufrufer die Änderung. - -### Rückgabewert -auch: Rückgabewerte -Das Ergebnis, das ein Unterprogramm mit return an den Aufruf zurückgibt. - -### Aussprung -Verlässt sofort die Schleife, in der er steht. Danach geht es unter der Schleife weiter. - -### Syntaxfehler -auch: Syntaxfehlern, Syntaxfehlers -Ein Fehler in der Schreibweise, zum Beispiel ein fehlendes \`;\`. Das Programm startet gar nicht. Englisch: syntax error. - -### Laufzeitfehler -auch: Laufzeitfehlern, Laufzeitfehlers -Ein Fehler, der erst beim Ausführen auftritt, zum Beispiel Teilen durch 0. Das Programm bricht an dieser Stelle ab. Englisch: runtime error. - -### Logikfehler -auch: Logikfehlern, Logikfehlers -Das Programm läuft ohne Meldung, liefert aber ein falsches Ergebnis. Du findest ihn mit dem Schreibtischtest. Englisch: logic error. - -### Schreibtischtest -auch: Schreibtischtests -Du führst ein Struktogramm auf Papier aus und notierst nach jedem Schritt die Werte der Variablen. - -### lineare Suche -auch: Lineare Suche -Ein Suchverfahren, das alle Werte der Reihe nach mit dem gesuchten Wert vergleicht. Es funktioniert auch bei unsortierten Werten. - -### binäre Suche -auch: Binäre Suche -Ein Suchverfahren für sortierte Werte. Es sieht sich die Mitte an und sucht nur in der passenden Hälfte weiter. - -### Bubblesort -Ein einfaches Sortierverfahren. Es vergleicht immer zwei Nachbarn und tauscht sie, wenn sie falsch herum stehen. - -### Ereignis -auch: Ereignisse, Ereignissen, Ereignisses -Etwas, das der Mensch an einem Fensterprogramm tut, zum Beispiel einen Knopf anklicken. Das Programm reagiert darauf mit einem Zuhörer (ActionListener). - -### Interface -auch: Interfaces -Eine Art Vertrag in Java: Er legt fest, welche Methoden eine Klasse haben muss. \`ActionListener\` ist ein Interface mit der Methode \`actionPerformed\`. - -### Exception -auch: Exceptions -Ein Fehler, der erst beim Ausführen auftritt, zum Beispiel ein Index außerhalb des Arrays. Fängt ihn kein \`catch\` auf, bricht das Programm ab. - -### Attribut -auch: Attribute, Attributs -Eine Variable, die direkt in der Klasse steht und nicht in einer Methode. Mit \`static\` gehört sie zur Klasse selbst und lebt so lange wie das Programm, zum Beispiel ein Klickzähler, den mehrere Zuhörer ändern. - -### Scanner -Ein fertiger Baustein von Java, der Eingaben über die Tastatur liest, z. B. mit \`scanner.nextInt()\` eine Zahl. Er braucht ganz oben \`import java.util.Scanner;\`. - -### Kommentar -auch: Kommentare, Kommentaren, Kommentars -Eine Notiz im Code für Menschen. Java überspringt sie beim Ausführen. \`//\` gilt bis zum Ende der Zeile, \`/* … */\` kann über mehrere Zeilen gehen. - -### import -Eine Zeile ganz oben im Java-Code, die einen fertigen Baustein aus einem Paket holt, z. B. \`import java.util.Scanner;\`. Ohne sie kennt Java den Namen nicht. - -### Quellcode -Der Programmtext, den Menschen schreiben und lesen, zum Beispiel in einer Datei \`Einmaleins.java\`. - -### Compiler -auch: Compilers -Ein Programm, das Quellcode übersetzt. In Java heißt er \`javac\` und macht aus einer \`.java\`-Datei Bytecode in einer \`.class\`-Datei. - -### Bytecode -Die übersetzte Form eines Java-Programms in einer \`.class\`-Datei. Ihn liest nicht der Mensch, sondern die JVM. - -### JVM -auch: Java Virtual Machine, virtuelle Maschine -Die Java Virtual Machine führt Bytecode aus. Sie ist ein Programm, das wie ein kleiner Computer aus Software auf Windows, macOS oder Linux läuft. Deshalb ist Java plattformunabhängig. - -### JRE -Java Runtime Environment: die JVM und fertige Bausteine. Damit kann man Java-Programme ausführen, aber nicht übersetzen. - -### JDK -Java Development Kit: die JRE und dazu den Compiler \`javac\` und weitere Werkzeuge. Das braucht man zum Programmieren. - -### plattformunabhängig -Ein Programm läuft ohne Änderung auf verschiedenen Betriebssystemen. Bei Java sorgt die JVM dafür: „Write once, run anywhere“. - -### Maschinencode -Befehle als Zahlen, die der Prozessor direkt ausführt. Jede Prozessorart hat ihren eigenen Maschinencode. - -### JIT-Compiler -auch: JIT -Ein Teil der JVM, der oft benutzten Bytecode während des Ausführens („just in time“) in Maschinencode übersetzt. Dadurch wird Java schneller. -`,jw=`## Grundbausteine - -### Anweisung - -Ein einzelner Schritt. Damit legst du Variablen an oder rechnest. - -\`\`\` -int zahl = 5 -double preis = 2.5 -String name = "Ada" -zahl = zahl + 1 -\`\`\` - -### Datentypen - -Der Datentyp steht vor dem Namen der Variablen. - -| Datentyp | Bedeutung | Beispiel | -|---|---|---| -| \`int\` | ganze Zahl | \`int anzahl = 3\` | -| \`double\` | Kommazahl (mit Punkt) | \`double preis = 2.5\` | -| \`String\` | Text in doppelten Anführungszeichen | \`String name = "Ada"\` | -| \`boolean\` | Wahrheitswert | \`boolean fertig = false\` | -| \`char\` | ein einzelnes Zeichen in einfachen Anführungszeichen | \`char note = 'C'\` | - -\`'C'\` ist ein \`char\`, \`"C"\` ein String. Ein Zeichen aus einem Text holst du mit \`charAt\`: \`char erster = name.charAt(0)\`. Jedes Zeichen hat einen Zeichencode: \`(int) 'A'\` ist 65, \`(char) 66\` ist \`'B'\`. - -### Eingabe - -Fragt beim Ausführen einen Wert ab und speichert ihn in einer Variablen. Der Text in Anführungszeichen ist die Frage. - -\`\`\` -input: int alter "Wie alt bist du?" -input: double preis "Preis eingeben" -input: String name "Wie heißt du?" -\`\`\` - -### Ausgabe - -Zeigt einen Text oder einen Wert an. Texte stehen in Anführungszeichen, mit \`+\` verbindest du Text und Variablen. - -\`\`\` -output: "Hallo" -output: summe -output: "Du bist " + alter + " Jahre alt." -\`\`\` - -> In der Ausgabe hängt \`+\` die Teile immer aneinander: \`output: a + b\` zeigt bei 3 und 4 also 34. Rechne deshalb vorher in einer Variablen, z. B. \`int summe = a + b\`, und gib dann \`summe\` aus. - -## Verzweigungen - -### Verzweigung - -Prüft eine Bedingung. Ist sie wahr, wird der linke Zweig (true) ausgeführt, sonst der rechte (false). - -\`\`\` -alter >= 18 -zahl % 2 == 0 -note < 1 || note > 6 -\`\`\` - -### Fallauswahl - -Wählt je nach Wert einer Variablen einen von mehreren Fällen. Passt kein Fall, gilt \`default\`. Fälle fügst du rechts mit **Weiteren Fall hinzufügen** hinzu. - -## Schleifen - -### While-Schleife - -Wiederholt den Inhalt, **solange** die Bedingung wahr ist. Die Bedingung wird vor jedem Durchlauf geprüft. - -\`\`\` -zahl != 1 -i < 10 -\`\`\` - -### Do-while-Schleife - -Führt den Inhalt **mindestens einmal** aus und prüft die Bedingung erst danach. - -### For-Schleife - -Zählt eine Variable von einem Startwert bis zu einer Grenze. Die drei Felder sind Start, Bedingung und Schritt. - -\`\`\` -int i = 0 -i < 5 -i++ -\`\`\` - -### Endlosschleife - -Wiederholt den Inhalt ohne Bedingung. Mit einem **Aussprung** im Inneren verlässt du sie. - -### Aussprung - -Verlässt die umgebende Schleife (\`break\`). - -## Unterprogramme - -### Aufruf - -Steht für ein Unterprogramm, z. B. \`berechneSumme()\`. Mit Parametern und Rückgabewert hat das Unterprogramm eine Kopfzeile als Titel: - -\`\`\` -int quadrat(int x) Kopfzeile des Unterprogramms -return x * x gibt das Ergebnis zurück -int q = quadrat(5) Aufruf, q bekommt 25 -zeigeSumme(3, 4) Aufruf eines void-Unterprogramms -\`\`\` - -## Rechnen und Vergleichen - -### Rechenzeichen und Vergleiche - -- \`+ - * /\` Grundrechenarten -- \`%\` Rest beim Teilen (\`7 % 2\` ist 1) -- \`==\` gleich, \`!=\` ungleich -- \`<\` \`<=\` \`>\` \`>=\` kleiner, kleiner gleich, größer, größer gleich -- \`&&\` und, \`||\` oder - -## Arrays - -### Arrays - -Mehrere Werte unter einem Namen. Gezählt wird ab 0. - -\`\`\` -int[] zahlen = {3, 7, 1} -output: zahlen[0] -int[] leer = new int[5] -\`\`\` - -Ein **2D-Array** ist eine Tabelle mit Zeilen und Spalten: - -\`\`\` -boolean[][] sitze = new boolean[3][5] -sitze[1][4] = true -output: sitze.length + " Reihen, " + sitze[0].length + " Plätze" -\`\`\` - -## Java - -### Vom Code zum Programm - -Der Compiler \`javac\` übersetzt \`Datei.java\` in Bytecode (\`Datei.class\`). Die JVM übersetzt den Bytecode beim Ausführen in Maschinencode, auf Windows, macOS und Linux gleich. C++ wird dagegen direkt zu Maschinencode und muss für jedes System neu kompiliert werden. - -| | enthält | wofür | -|---|---|---| -| JRE | JVM und Bibliotheken | ausführen | -| JDK | JRE, Compiler \`javac\`, Werkzeuge | programmieren | - -### Kommentare - -Notizen im Code. Java überspringt sie, das Struktogramm bleibt gleich. - -\`\`\`java -/* - * Aufgabe: die Zahlen von 1 bis 10 addieren - * (mehrzeiliger Kommentar) - */ -// Verarbeitung (einzeiliger Kommentar) -int summe = 0; -for (int i = 1; i <= 10; i++) { - summe = summe + i; // Kommentar hinter dem Code -} // Ende for -\`\`\` - -Gut für: die Aufgabenstellung, die Einteilung in Eingabe, Verarbeitung und Ausgabe, eine Erklärung, das Ende eines Blocks hinter \`}\`, die erwartete Ausgabe und Notizen fürs nächste Mal. -`,Cw=`--- -titel: What is a structure chart? -bereich: First steps -kurz: Show and read algorithms as a series of clear steps. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an algorithm is -- read a structure chart -- recognise the three basic building blocks: sequence, branch and loop -::: - -## An algorithm is an exact set of instructions - -An **algorithm** describes step by step how to solve a task. Every step must be so clear that someone can carry it out without asking, a person just as well as a computer. - -You know algorithms from everyday life: a recipe, directions to a place or the instructions for building a wardrobe. - -::: diagramm l1-tee.visustruct -Making tea as a structure chart: every box is one step. -::: - -::: angeberwissen -Baaah, who wrote the first program? A woman, back in 1843, long before the first computer! **Ada Lovelace** described step by step how Charles Babbage's *Analytical Engine* should calculate the so-called Bernoulli numbers. That was a real algorithm for a machine. The instructions were meant to be stored on punched cards (more on this in lesson 3). However, the machine was never built. - -Ada had another brilliant idea: a machine like this can do more than calculate. It could also work with letters, symbols or even music. That is exactly what computers do today. A programming language called **Ada** is named after her. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), mathematician (picture: watercolour by Alfred Edward Chalon, around 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), public domain). -::: - -## How to read a structure chart - -You read a structure chart **from top to bottom**. Every box is one step. Only when a step is finished does the next one start. - -This way of drawing was invented by **Isaac Nassi and Ben Shneiderman** (1973). That is why it is also called a Nassi-Shneiderman diagram. In Germany it is standardised as DIN 66261. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -Left: Isaac “Ike” Nassi (photo: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/), cropped), right: Ben Shneiderman (photo: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> A structure chart always has exactly one start (at the top) and one end (at the bottom). There are no arrows and no jumps all over the place. Programs that jump all over the place are called **spaghetti code**, because their flow gets tangled like noodles on a plate. More on this in the next lesson. - -::: angeberwissen -Baaah, listen up! In 1972 Nassi and Shneiderman were still PhD students at Stony Brook University near New York. Shneiderman was about 25 and Nassi about 23. So basically two students who wrote computer science history on the side. -::: - -## Building block 1: the sequence - -Every algorithm can be built from just **three basic building blocks**: **sequence**, [**branch**]{.blau} and [**loop**]{.rosa}. The colours help you match them: [statements]{.gelb} are yellow, branches blue, loops pink. The sequence has no colour of its own, it is simply the row of statements one below the other. You see the same colours in the editor and later in the code. You can switch them off in the settings under “Colored blocks”. In this step and the next two you get to know them one by one. - -The **sequence** is the simplest building block: several statements run **one after another**, each exactly once. - -This is what it looks like: - -- Every statement is a **simple rectangle**. -- The rectangles sit **on top of each other** without a gap. -- They run from top to bottom, one statement after the other. - -::: diagramm l1-sequenz.visustruct -Four statements as a sequence. First you get up, then you brush your teeth, and so on. -::: - -> The tea example above is a sequence, too. - -::: angeberwissen -15 minutes! That is how long Shneiderman needed for the first sketch with sequence, branch and loop. I need longer than that to choose between hay and clover. -::: - -## Building block 2: the branch - -Often the next step depends on a **condition**. A condition is a question you can only answer with **yes or no**, for example *Is it raining?* In VisuStruct yes is **true** and no is **false**. - -For decisions like this there is the **branch**. This is what it looks like: - -- At the top, the condition sits in a **triangle**. -- Below it, the box splits into **two columns**: on the left the path for **true**, on the right the path for **false**. -- **Only one** of the two paths ever runs. After that, both paths continue together below the branch. - -::: diagramm l1-verzweigung.visustruct -If it is raining, the left path runs, otherwise the right one. After that, both cases continue with “Leave”. -::: - -> If nothing should happen in one case, that path stays empty. The sign **ø** shows this. - -::: angeberwissen -Spelling alert! It is Shneiderman, with no c. Anyone who writes “Schneiderman” gets a gentle nudge from my horns. He himself points out that people keep sneaking a c into his name. -::: - -## Building block 3: the loop - -If something should happen **several times**, you do not write it down several times. Instead you use a **loop**, also called repetition. - -This is what it looks like: - -- At the top is the condition, for example *Plate not empty*. -- The steps that are repeated are **indented** below it. At the top and on the left, the box forms an **upside-down L** around them. -- Before every pass the condition is checked. As long as it is **true**, the steps inside the upside-down L run once more. If it is **false**, the program continues below the loop. - -::: diagramm l1-wiederholung.visustruct -As long as the plate is not empty, you take a bite. After that, the plate is cleared. -::: - -> There are more kinds of loops. You will meet them in later lessons. - -## All three together - -The building blocks become really useful when you combine them. This diagram contains all three: a sequence from top to bottom, a [branch]{.blau} and a [loop]{.rosa}. The yellow boxes are simple [statements]{.gelb}. The statements inside a branch or a loop are yellow too. Strictly speaking, the whole diagram is a sequence, because the branch and the loop also stand in the row from top to bottom. - -::: diagramm l1-grundbausteine.visustruct -You recognise the branch by its triangle and the loop by its upside-down L. Everything together sits in a sequence. -::: - -The boxes can also be **nested** inside each other: a loop or a path can contain sequences, branches and loops again. - -::: angeberwissen -Comfort for anyone who has messed up a test: the famous journal *Communications of the ACM* rejected the structure chart article. According to Nassi, the reviewer even said they should collect every copy and burn it. Ouch! It then appeared in 1973 in a smaller journal and today it is quoted thousands of times. Ha! -::: - -## Structure first, then code - -When you program, two things happen at once: you have to think about the steps and pay attention to how the programming language is written. A structure chart lets you keep the two apart. - -1. First you think about the **logic**: which steps, which conditions, which repetitions? -2. Then you translate the finished structure into **Java, Python or JavaScript**. VisuStruct can even generate the code for you. - -With the **simulation** you can also run your structure chart step by step and check whether it really does what it should. - -## Game: Which building block? - -::: spiel frage -titel: Game: Which building block? -intro: Ludwig describes things from his everyday life. Which of the three building blocks is hidden in each? Eight rounds. -antworten: sequence | branch | loop - -runde: -frage: "Toothpaste on the brush, brush your teeth, rinse your mouth." -richtig: sequence -> Three steps, each exactly once and always in this order. - -runde: -frage: "If it rains, take the umbrella, otherwise the sunglasses." -richtig: branch -> A condition decides which of the two ways is taken. - -runde: -frage: "Stir until the dough is smooth." -richtig: loop -> Stirring is repeated as long as the dough isn't smooth yet. - -runde: -frage: "Run ten laps around the sports field." -richtig: loop -> The same lap is run ten times. - -runde: -frage: "Shoes on, jacket on, door open, off you go." -richtig: sequence -> One step after the other, nothing is skipped or repeated. - -runde: -frage: "If the light is green, you cross the street." -richtig: branch -> Even without "otherwise" it is a branch. The otherwise branch is simply empty. - -runde: -frage: "Roll the dice until you get a six." -richtig: loop -> You don't know beforehand how often. You roll until the condition is met. - -runde: -frage: "If someone says 'Come in', you go in. Otherwise you wait at the door." -richtig: branch -> The answer decides between two ways: go in or wait. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an algorithm is. → An algorithm is an exact set of instructions -- I can read a structure chart from top to bottom. → How to read a structure chart -- I can name the three basic building blocks: sequence, branch and loop. → Building block 1: the sequence -- I can recognise every building block in a structure chart by its shape. → All three together -::: - -## Quiz - -::: quiz -? In which direction do you read a structure chart? -→ How to read a structure chart -- [ ] From left to right -- [x] From top to bottom -- [ ] From bottom to top -> Every box is one step, and the steps follow each other from top to bottom. - -? Which building block is NOT one of the three basic building blocks? -→ Building block 1: the sequence -- [ ] Sequence -- [ ] Branch -- [x] Jump -- [ ] Loop -> There are no jumps in a structure chart. Sequence, branch and loop are enough for every algorithm. - -? How do you recognise a branch in a structure chart? -→ All three together -- [ ] By an upside-down L -- [x] By a triangle with the condition and two columns below it -- [ ] By several rectangles on top of each other -> The upside-down L belongs to the loop, and rectangles on top of each other are a sequence. - -? When does the inside of a loop run once more? -→ All three together -- [x] As long as the condition is true -- [ ] Only if the condition is false -- [ ] Always exactly twice -> The condition is checked before every pass. If it is false, the program continues below the loop. - -? What is an algorithm? -→ An algorithm is an exact set of instructions -- [x] Clear step-by-step instructions for solving a task -- [ ] A particular program in Java -- [ ] A picture that explains a program -> An algorithm does not depend on a programming language. A structure chart is one way to write it down. -::: -`,Dw=`--- -titel: Flowcharts and pseudocode -bereich: First steps -kurz: Show algorithms as a flowchart and as pseudocode, and compare them with the structure chart. ---- - -## Skills I will gain now - -::: kompetenzen -- read a flowchart and build one myself -- explain why jumps in a flowchart lead to mistakes -- read a simple algorithm as pseudocode -::: - -## Other ways to draw it: the flowchart - -There are two more ways to show an algorithm: the **flowchart** and **pseudocode**. You will meet both in class. - -Here you see our structure chart for drinking tea from the last lesson once more. Next we look at the flowchart, using the same example. - -::: diagramm l1-grundbausteine.visustruct -The tea example as a structure chart: first the cup, then the branch for the sugar, then the loop. -::: - -### The same algorithm as a flowchart - -::: grafik l01-pap.svg -The tea example as a flowchart. The arrow from “Wait 1 minute” back up forms the loop. -::: - -### The symbols - -A flowchart is made of a few symbols joined by **arrows**. It is standardised as DIN 66001 (ISO 5807). - -- **Oval:** start or end. Every flowchart has exactly one start and one end. -- **Rectangle:** a statement, for example *Put a cup on the table*. -- **Diamond:** a decision. The diamond holds a question, and two arrows lead out of it: **yes** and **no**. -- **Parallelogram:** an input or output. The tea example does not have one. -- **Arrow:** shows which step comes next. - -A loop has no symbol of its own in a flowchart. You recognise it by an arrow that leads **back up**. - -### The advantages - -- **Very easy to read**, even for people who have never seen a flowchart. You simply follow the arrows with your finger. -- Flowcharts are not only used in programming but wherever a process is explained: in user manuals, for troubleshooting (“Is the lamp on? No: is it plugged in?”) or in companies for work processes. - -## Exercise: Baking a cake as a flowchart - -Now you build a flowchart yourself. Ludwig bakes a cake like this: - -He puts the ingredients in the bowl and stirs the batter. If the batter is not smooth yet, he keeps stirring. Once it is smooth, he pours the batter into the tin and bakes the cake for 30 minutes. Then he checks with a wooden skewer: if batter still sticks to it, he bakes for 5 more minutes and checks again. Once the skewer comes out clean, he takes the cake out and calls: “The cake is ready!” - -::: pap -titel: Exercise: Baking a cake as a flowchart -auftrag: The places in the chart are empty. Put each element in its place. The shape of the place helps you. -start: Start -anweisung: Put ingredients in the bowl -anweisung: Stir the batter -frage: Batter smooth? | nein -> Stir the batter -anweisung: Pour batter into the tin -anweisung: Bake for 30 minutes -frage: Skewer clean? | nein: Bake 5 more minutes -anweisung: Take the cake out -ausgabe: “The cake is ready!” -ende: End -erklärung: Two loops, two arrows back up: when stirring, the arrow leads back to “Stir the batter”; when baking, it leads from “Bake 5 more minutes” back to the question. The output sits in the parallelogram. -legende: ja -::: - -**Draw your own flowcharts:** With **VisuFlow** you can easily draw your own flowchart. The symbols are ready in a bar: tapping one adds the next symbol and connects it with an arrow right away. To get started you can load the tea example. You save your flowchart as a file and export it as an image or PDF, for example for your notebook or a presentation. [Open VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Drawbacks of the flowchart: jumps - -An arrow in a flowchart may lead from any symbol to any other. That sounds handy, but it is dangerous. - -In the old days, programs were written exactly like that. Every line had a **number**, and the command **GOTO** jumped to a line number. Every GOTO is an arrow from the flowchart. Our tea example would look like this: - -\`\`\` -10 Put a cup on the table -20 IF no sugar wanted, GOTO 40 -30 Add sugar -40 IF tea not too hot, GOTO 70 -50 Wait 1 minute -60 GOTO 40 -70 Drink the tea -\`\`\` - -### One more line, and the jumps go wrong - -Now you improve the algorithm: after the sugar, the tea bag should come out. You insert the new line 40, and all lines below move down one number. But the jumps still point to the old numbers: - -\`\`\` -10 Put a cup on the table -20 IF no sugar wanted, GOTO 40 -30 Add sugar -40 Take out the tea bag -50 IF tea not too hot, GOTO 70 -60 Wait 1 minute -70 GOTO 40 -80 Drink the tea -\`\`\` - -Play it through: line 70 no longer sends you to the check in line 50 but to the tea bag. And when the tea is cool enough, line 50 jumps to line 70 instead of *Drink the tea*. So you keep taking out the tea bag and never drink. With every change you have to check every GOTO. In a big program with hundreds of jumps, something easily goes wrong. - -### All over the place: spaghetti code - -With arrows you can also jump out of the middle of a loop, or from outside into the middle of a loop. Such jumps quickly make the chart confusing. With many jumps the arrows cross until the chart looks like a plate of spaghetti. That is why it is called **spaghetti code**. You can hardly follow the flow any more, and mistakes hide well. - -A structure chart has no arrows. If you insert a box, everything below simply moves down with it, and no jump can point to the wrong place. For leaving a loop early there is a separate, tidy box, the break. You will meet it in lesson 18. - -::: angeberwissen -In 1968 the computer scientist Edsger Dijkstra wrote a famous letter: “Go To Statement Considered Harmful”. Since then, programs are built only from sequence, branch and loop. In Java, \`goto\` is still a reserved word today, but you are not allowed to use it. Baah, I would rather eat spaghetti than program it. -::: - -## Pseudocode and comparison - -**Pseudocode** is text that looks like a program but is written in plain language. Fixed words such as IF and WHILE show the building blocks, and the **indentation** shows what belongs to them. There are no jumps here either. This is our tea example: - -\`\`\` -Put a cup on the table -IF sugar wanted THEN - Add sugar -END IF -WHILE tea still too hot - Wait 1 minute -END WHILE -Drink the tea -\`\`\` - -Pseudocode has **no fixed notation**. Some people write WHEN instead of IF, others use an arrow for assignment (\`number ← 5\`) or leave out the END lines. All of that is fine. What matters is that anyone can understand it without an explanation. So pseudocode is not another programming language you have to learn. - -::: angeberwissen -Does pseudocode look familiar? The programming language Python looks very similar, and that is on purpose: its inventor Guido van Rossum wanted programs to read almost like plain text. Python also uses indentation to show what belongs together. But careful: Python is a real programming language with strict rules. One space too many and the computer complains. Pseudocode is more relaxed, just like me at breakfast. -::: - -### Compared - -- **Structure chart:** boxes inside boxes, the loop is an upside-down L. You see the structure at a glance, and wild jumps are impossible. -- **Flowchart:** symbols with arrows, the loop is an arrow back up. Very easy to picture, but big charts quickly get confusing, and jumps all over the place are possible. -- **Pseudocode:** text with indentation, the loop sits between WHILE and END WHILE. Quick to write and already close to program code. - -> All three describe the same algorithm. Once you can read one form, you learn the others quickly. - -::: angeberwissen -Nassi and Shneiderman were out to get the arrows! Their 1973 article was called “Flowchart Techniques for Structured Programming”. With arrows you can jump all over the place until the chart looks like a plate of spaghetti. In a structure chart that is simply impossible. Baah, that is how I like it: tidy, like my stable. -::: - -## Game: Which way of drawing? - -::: spiel frage -titel: Game: Which way of drawing? -intro: Ludwig describes a way of drawing an algorithm without saying its name. Is it the structure chart, the flowchart or pseudocode? Eight rounds. -antworten: structure chart | flowchart | pseudocode - -runde: -frage: “When something is repeated, one of my arrows points back up.” -richtig: flowchart -> A flowchart has no special symbol for a loop. An arrow leads back up. - -runde: -frage: “My loop looks like an upside-down L.” -richtig: structure chart -> The box wraps around the repeated steps at the top and on the left. - -runde: -frage: “I am made of words like IF and WHILE, and the indentation shows what belongs together.” -richtig: pseudocode -> Pseudocode is text in plain language that is laid out like a program. - -runde: -frage: “My branch is a diamond with the exits yes and no.” -richtig: flowchart -> In a structure chart, the condition sits in a triangle instead. - -runde: -frage: “My arrows can get tangled all over the place like spaghetti.” -richtig: flowchart -> Because arrows may lead anywhere, spaghetti code is possible in a flowchart. - -runde: -frage: “I have no fixed way of writing. You may write WHEN instead of IF.” -richtig: pseudocode -> All that matters is that anyone can understand it without an explanation. - -runde: -frage: “The condition sits at the top in a triangle, and below it I split into two columns.” -richtig: structure chart -> This is how a branch looks in a structure chart: true on the left, false on the right. - -runde: -frage: “If you insert a box, everything below simply moves down with it. No jump can go wrong.” -richtig: structure chart -> A structure chart has no arrows and no jumps, so nothing can point to the wrong place. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the symbols of a flowchart and read a simple flowchart. → Other ways to draw it: the flowchart -- I can build a simple algorithm as a flowchart myself. → Exercise: Baking a cake as a flowchart -- I can explain why jumps with GOTO lead to mistakes and spaghetti code. → Drawbacks of the flowchart: jumps -- I can read a simple algorithm as pseudocode. → Pseudocode and comparison -::: - -## Quiz - -::: quiz -? How do you recognise a loop in a flowchart? -→ Other ways to draw it: the flowchart -- [ ] By an upside-down L -- [x] By an arrow that leads back up -- [ ] By the word WHILE -> The upside-down L belongs to the structure chart, and WHILE to pseudocode. - -? Match each flowchart symbol to its meaning. -→ Other ways to draw it: the flowchart -= Oval -> Start or end -= Rectangle -> Statement -= Diamond -> Decision -= Parallelogram -> Input or output -> The diamond has two exits: yes and no. - -? You insert a new line into a program with GOTO. What can happen? -→ Drawbacks of the flowchart: jumps -- [x] The jumps point to the wrong lines because the numbers have moved -- [ ] Nothing, the jumps adjust by themselves -- [ ] The program automatically gets faster -> Every GOTO points to a fixed line number. If the lines move down, the program jumps to the wrong place. - -? In pseudocode, what shows which lines belong to a loop? -→ Pseudocode and comparison -- [ ] The colour -- [x] The indentation and the END WHILE -- [ ] An arrow -> Everything between WHILE and END WHILE is indented and is repeated. -::: -`,Bw=`--- -titel: The editor -bereich: First steps -kurz: Insert elements, type text, move and delete. ---- - -## Skills I will gain now - -::: kompetenzen -- insert an element from the Elements area and replace a placeholder -- change the text of a box and switch between Statement, Input and Output -- move, copy and delete boxes and undo steps -::: - -## The screen - -VisuStruct has two editors: the [structure chart editor](#zeige-editor), which this lesson is about, and on the right the [Code](#zeige-codelive) area for Java code (from lesson 4). If the structure chart editor is collapsed, this lesson opens it for you. The blue words show you the areas, even when they are collapsed: move the mouse over one, and the area opens next to the learning area and lights up. Click it (or tap it), and it stays open. - -The structure chart editor has several areas. Move the mouse over a blue word or tap it, and that area lights up in the editor; collapsed areas such as Elements open. - -- **At the very top, the [toolbar](#zeige-leiste):** New, Open and Save. On the far right, the gear for [Settings](#zeige-einstellungen): text size and language. -- **Below it, the [tools](#zeige-werkzeuge)** above the elements and the structure chart: Undo, Cut, Copy, Paste, Delete, Export, Code and Simulation. The buttons only show icons. Hold the mouse over one and its name appears. -- **On the left, the [Elements](#zeige-elemente):** Here are all the building blocks you can drag into the structure chart. Some people also call this area the palette. -- **In the middle, the [structure chart](#zeige-struktogramm):** A new diagram contains only an empty placeholder, marked with **ø**. -- **At the bottom, the [Object Inspector](#zeige-objektinspektor):** Here you edit the text of the selected element. If it is too small or too big, drag the line with the grip above it up or down. - -> You can collapse this learning area at any time with the [sidebar icon](#zeige-lernen) at the top of this area. Then only the slim “🎓 Learn” strip stays on the left; click it to open the area again. It stays open next to the editor so you can try out right away what you read. In the same way there is the [📁 Files](#zeige-dateien) column on the left for your saved programs (lesson 3) and the [Code](#zeige-codelive) area on the right for Java (from lesson 4). - -## Insert an element - -Drag an element from the **Elements** area onto the structure chart. While you drag, a blue line shows where it will land: - -- **Onto the placeholder ø:** The element replaces the placeholder. -- **Onto the top half of a box:** The element goes in front of it. -- **Onto the bottom half of a box:** The element goes after it. - -::: grafik l02-einfuegen.svg -Where you drop it decides where the new element lands. -::: - -It also works without dragging: **click** an element in the Elements area (or tap it). Then it is inserted after the selected box. If the placeholder **ø** is selected, it replaces it. - -With the keyboard, press **Tab** to jump to an element and press **Enter**. The new element also goes after the selected box. - -::: angeberwissen -Grabbing boxes and dragging them around, just like you are doing now, has a fancy name: **direct manipulation**. Ben Shneiderman coined the term in 1982. Yes, the structure chart guy. That man worked harder than I do when I graze. -::: - -## Type text - -Click a box. It gets a blue outline, and its text appears in the Object Inspector below the diagram. Whatever you type there shows up in the diagram straight away. - -It is quicker to **double-click** the box, or **double-tap** it on a tablet. Then you type the text right in the structure chart. **Enter** takes it, **Esc** cancels. With the keyboard, select the box and press **Enter**. - -The box from the Elements area is called a **Statement**. At the top of the Object Inspector you choose what sort of statement it should be: - -- **Statement:** calculate or set something, e.g. \`int number = 5\`. You will learn how in lesson 4. -- **Input:** ask for a value while the chart runs. That comes in lesson 6. -- **Output:** show something. In the diagram it then has \`output:\` in front, e.g. \`output: "Hello"\`. - -To start with, you only need Output. Texts that should be shown exactly as they are go in **quotation marks**. - -::: diagramm l2-ausgabe.visustruct -Two outputs one after the other. Open the diagram in the editor and start the [simulation](#zeige-simulation) in the tools to see the output. You will learn more about it in lesson 7. -::: - -## Move, copy, delete - -- **Move:** Drag an existing box to a new place with the mouse. -- **Copy and paste:** Select a box, then press Ctrl+C and Ctrl+V (on a Mac ⌘C and ⌘V). The copy goes after the selected box. Without a keyboard, for example on a tablet, tap **Copy** and **Paste** in the [tools](#zeige-werkzeuge). -- **Delete:** Select a box and press Del, or click the [trash can](#zeige-loeschen) „Delete Selection“ in the tools. -- **Undo:** Ctrl+Z undoes the last step, Ctrl+Shift+Z brings it back (on a Mac ⌘Z and ⌘⇧Z). Without a keyboard, tap [Undo](#zeige-rueckgaengig) in the tools. - -## Exercise: Hello World - -::: übung l2-hallo -titel: Hello World -auftrag: Build a structure chart that outputs **Hello World**. -Drag a **Statement** from the Elements onto the placeholder **ø** and click the box. In the Object Inspector below the structure chart there are three buttons at the top: Statement, Input and Output. Click **Output**. Then type \`"Hello World"\` in the text field below, with quotation marks. -test: - -> Hello World -braucht: anweisung -tipp: You only need one single box. The text that should appear goes into the text field in the Object Inspector. -tipp: Watch out for the quotation marks. Without them, VisuStruct looks for a variable called Hello. -lösung: l2-hallo-loesung.visustruct -::: - -Click **Check** when you are done. VisuStruct runs your structure chart and compares the output. - -## Exercise: Two lines - -::: übung l2-zwei-zeilen -titel: Two lines -auftrag: First output **Hello** and then **I am learning structure charts**, each on its own line. -test: - -> Hello | I am learning structure charts -tipp: Two lines means two outputs. The boxes sit on top of each other and run from top to bottom. -tipp: Every output is a box of its own. Drag the second Statement onto the bottom half of the first one. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Game: Editor pro - -::: spiel frage -titel: Game: Editor pro -intro: Ludwig tests whether you have the editor under control. Seven rounds. - -runde: -frage: You drag a statement onto the placeholder **ø**. What happens? -- [x] The statement replaces the placeholder. -- The statement goes after the placeholder. -- Nothing, you can't drop anything on ø. -> The placeholder only keeps the place free. The new element takes it. - -runde: -frage: You drag an element onto the **top** half of a box. Where does it end up? -- [x] in front of it -- after it -- It replaces the box. -> Top half means in front, bottom half means after. The blue line shows you while dragging. - -runde: -frage: You drag an element onto the **bottom** half of a box. Where does it end up? -- in front of it -- [x] after it -- at the very end of the structure chart -> Bottom half means: directly after this box. - -runde: -frage: You deleted a box by accident. What brings it back? -- Ctrl+C / ⌘C -- [x] Ctrl+Z / ⌘Z -- Ctrl+V / ⌘V -> Ctrl+Z / ⌘Z undoes the last step. Ctrl+C / ⌘C copies, Ctrl+V / ⌘V pastes. - -runde: -frage: When it runs, exactly **Hello** should appear. What is in the box? -- [x] output: "Hello" -- output: Hello -- "Hello" -> Text that should be shown exactly like that goes in quotation marks. The editor puts \`output:\` in front when you choose the kind Output. - -runde: -frage: Where do you change the text of the selected box? -- in the Elements -- [x] in the Object Inspector -- in the toolbar at the very top -> The Object Inspector below the diagram shows the text of the selected box. - -runde: -frage: You click an element in the Elements area without dragging. Where does it end up? -- [x] after the selected box -- at the very beginning -- at the very end -> Without dragging (click, or Tab and Enter) the new element goes after the box that is selected right now. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can insert an element from the Elements area and replace a placeholder. → Insert an element -- I can change the text of a box and switch between Statement, Input and Output. → Type text -- I can move, copy and delete boxes and undo steps. → Move, copy, delete -::: - -## Quiz - -::: quiz -? What does the sign ø in a box mean? -→ Insert an element -- [ ] The box contains a mistake. -- [x] There is still an empty placeholder here. -- [ ] The program ends here. -> A placeholder is replaced when you drag an element onto it. - -? You drag a Statement onto the bottom half of a box. Where does it land? -→ Insert an element -- [ ] In front of it -- [x] After it -- [ ] The box is replaced. - -? How do you output the text Good morning exactly as it is? -→ Type text -- [ ] output: Good morning -- [x] output: "Good morning" -- [ ] input: "Good morning" -> Without quotation marks, Good morning is understood as the name of a variable. -::: -`,Pw=`--- -titel: Save, open, export -bereich: First steps -kurz: Keep your files safe, use your workspace, open several diagrams and share them as an image or PDF. ---- - -## Skills I will gain now - -::: kompetenzen -- give a diagram a title -- save a diagram as a .visustruct file and open it again -- export a diagram as an image (PNG) or PDF -- open a second diagram in a new tab -- create my workspace \`VisuStruct-Files\` -- save a program in my workspace -::: - -## The .visustruct file - -Structure charts are saved as **.visustruct** files. All versions of VisuStruct use the same format: in the browser, on Mac, iPad and iPhone, and the Java app on the computer. So you can start in the browser at school and carry on in the app at home. - -::: angeberwissen -When Nassi was a student, there were no files to save. There were punched cards. Nassi says a Saturday night date looked like this: his future wife sat with him in the computer centre and punched his cards. Romance, computer science style! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -This is what a punched card looked like. One card held one line of a program, here \`PRINT *, 'HELLO WORLD'\` in the Fortran language. Each column is one character, and the holes in it are its code. A whole program was a stack of these cards. If you dropped it, you had to sort everything again. -::: - -::: angeberwissen -A computer pioneer from my home region in Germany: **Konrad Zuse**. On 12 May 1941 he presented the **Z3** in Berlin, the world's first working program-controlled digital computer. It already calculated in the **binary system**, using only 0 and 1, with about 2,000 relays (electric switches). Its program was not on punched cards but punched as holes into used cinema film. Zuse then invented **Plankalkül**, one of the very first programming languages. Later he lived in **Hünfeld** near Fulda until his death in 1995. The Konrad Zuse Museum there shows replicas of the Z3. My tip for a school trip! The town of Hünfeld's website tells you when it is open. -::: - -::: grafik l03-zuse-lochstreifen.svg -Roughly what Zuse's program strip looked like: old cinema film with punched holes. Each column is one instruction with 8 positions. A hole means 1, no hole means 0. The machine read the strip one instruction after another. -::: - -## Save and open - -- **[Save](#zeige-speichern)** (Ctrl+S, on a Mac ⌘S): The diagram is saved as a file. If the **Files** column is set up (see below), it goes there. Otherwise the browser asks where to put it or puts the file in your downloads folder. -- **[Open](#zeige-oeffnen)** (Ctrl+O, on a Mac ⌘O): Load a saved file again. You can also simply drag a file into the browser window. - -> The browser remembers your exercises automatically. But that only works in this browser on this device. So always save important work as a file as well. - -## The Files column - -To the left of the learning area you find the [📁 Files](#zeige-dateien) column. There you keep all your programs in one place, your **workspace**. - -**Your turn:** Set up your workspace right now, before you read on. From today on you save all your programs there. - -1. Create a workspace: open the column and click **Choose folder**. In the window, go to your **Documents**, use **New Folder** to create a folder called \`VisuStruct-Files\` there and select it. Allow VisuStruct to use the folder. This works in Chrome and Edge. On the iPad and in other browsers, click **Save in the browser** instead. -2. Save as usual. In your workspace, VisuStruct automatically creates a folder with today's date, for example \`2026-10-23\`, and saves the program in it. The file name comes from the title. - -This is what your workspace looks like then: - -\`\`\`text -VisuStruct-Files ← workspace -└── 2026-10-23 ← day - └── Factorial.visustruct -\`\`\` - -Clicking a program in the column opens it again. That way you quickly find every lesson later. - -Your **learning progress** goes into the workspace too: the file \`learnProgress.json\` lists the lessons and exercises you have finished. If you open the workspace in another browser, your progress is back. - -> Why a real folder? Your learning progress and your programs are stored in it as files. You can take the folder with you, for example on a USB stick or in your cloud folder such as OneDrive. That way you carry on at home on another computer exactly where you stopped. If you only save in the browser, everything stays in this one browser on this device. If someone deletes the browser data, your learning progress is gone too. - -## Several diagrams in tabs - -Above the structure chart you see the [tabs](#zeige-reiter). With **+** or [New](#zeige-neu) you open another, empty diagram. A dot • in front of the name shows that there are unsaved changes. - -You type the [title](#zeige-titel) of a diagram into the field above the structure chart. It also appears when you export and is suggested as the file name. - -## Share: image, PDF or file - -With [Export](#zeige-exportieren) in the tools (icon with an arrow pointing up) you can share your structure chart: - -- **Image (PNG):** to put into a worksheet or a presentation -- **Vector graphic (SVG):** stays sharp at any size -- **PDF (A4):** one page to hand in or print -- **VisuStruct file (.visustruct):** to pass on to someone who should open and keep editing your structure chart in VisuStruct, for example by email or through the learning platform -- **Print** (Ctrl+P, on a Mac ⌘P): straight to the printer - -Under the [gear](#zeige-textgroesse) at the top right, Text size sets the font size. Larger text also shows when you export an image. - -## Try it out - -Open one of your exercises from lesson 2 and try the following: - -1. Give the diagram a name in the [title](#zeige-titel) field above the structure chart, for example \`Hello World\`. -2. [Save](#zeige-speichern) it in your workspace. Look in the [📁 Files](#zeige-dateien) column: is it in today's folder? -3. Open a second tab with **+** and click your program in the Files column there. It opens again. -4. [Export](#zeige-exportieren) it as an **image (PNG)** and as a **PDF (A4)**. Open both files and compare them. - -Did everything work? Then you know where your programs are and how to share them. - -## Game: How do you pass it on? - -::: spiel frage -titel: Game: How do you pass it on? -intro: Ludwig wants to pass on his structure chart. Which format fits best? Six rounds. -frage: Which format fits? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: You want to keep working on your structure chart tomorrow. -richtig: .visustruct -> Only the .visustruct file can be opened and edited in VisuStruct again. - -runde: -frage: The structure chart should go into a presentation as a picture. -richtig: PNG -> A PNG image can be put into any presentation and any worksheet. - -runde: -frage: Your teacher wants to print your work as one A4 page. -richtig: PDF -> The PDF is exactly one A4 page to hand in or print. - -runde: -frage: The structure chart goes on a poster and should stay sharp even when huge. -richtig: SVG -> A vector graphic stays sharp at any size. A PNG gets pixelated when you enlarge it. - -runde: -frage: Your friend should change the structure chart in her VisuStruct app on the iPad. -richtig: .visustruct -> All VisuStruct versions open the same .visustruct file: in the browser, on Mac, iPad and iPhone. - -runde: -frage: You want to send a picture into a chat quickly. -richtig: PNG -> Anyone can look at a PNG straight away, without VisuStruct. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can give a diagram a title. → Try it out -- I can save a diagram as a .visustruct file and open it again. → Save and open -- I can export a diagram as an image (PNG) or PDF. → Share: image, PDF or file -- I can open a second diagram in a new tab. → Several diagrams in tabs -- I can create my workspace \`VisuStruct-Files\`. → The Files column -- I can save a program in my workspace. → The Files column -::: - -## Quiz - -::: quiz -? Which file can you open again in the Mac app and in the Java app? -→ Save and open -- [x] The .visustruct file -- [ ] The exported PNG image -- [ ] The exported PDF -> Images and PDFs are only for looking at. You can only keep editing the .visustruct file. - -? The browser remembers your exercises. Why should you still save? -→ Save and open -- [ ] Because otherwise the exercises are not checked -- [x] Because the browser only keeps the data on this device and it can get lost -- [ ] Because you cannot export without saving - -? Today you save a program in your workspace. Where does it end up? -→ The Files column -- [ ] In the downloads folder -- [x] In the workspace, inside a folder with today's date -- [ ] Directly in the workspace without a subfolder -> VisuStruct automatically creates a date folder for each day. That keeps the programs sorted by lesson. -::: -`,Ow=`--- -titel: Variables and data types -bereich: Basics -kurz: Store values, get to know the data types int, double, String and boolean, and calculate. ---- - -## Skills I will gain now - -::: kompetenzen -- create a variable with a data type, a name and a value -- explain that Java is case-sensitive (\`number\` is not \`Number\`) -- tell the data types int, double, String and boolean apart -- explain that = is an assignment and not a comparison -- calculate with + - * / % and use whole-number division -- create and output variables in Java -::: - -## What is a variable? - -A **variable** is like a labelled box. It has a **name**, a **data type** and a **value**. - -\`\`\` -int age = 14 -\`\`\` - -Here the variable is called \`age\`, its data type is \`int\` (whole number), and its value is 14. You can use the value again later through its name. - -::: grafik l04-variable.svg -The variable as a box: the name on the label, the data type as a sticker, the value inside. -::: - -> Variable names start with a **lowercase letter** and contain no spaces. Good names say what is inside: \`price\` is better than \`p\`. - -::: angeberwissen -In September 1999 the Mars Climate Orbiter space probe was lost. One team calculated the thrust in American units (pounds), the other expected metric units (newtons). The data only contained the number, not the unit. The probe flew too low into the Martian atmosphere and burned up, a loss of about 125 million dollars. A meaningful name like \`impulseInNewtonSeconds\` would have said what is inside. By the way, I always label my hay box \`hayInKilos\`. -::: - -If a name is made of several words, you write them together and start every further word with a capital letter: \`priceWithTax\`, \`numberOfStudents\`. This way of writing is called **camelCase**. - -::: angeberwissen -camelCase got its name because the capital letters in the middle stick out of the word like the humps of a camel: \`priceWithTax\` has two humps. Me, I'm a billy goat with zero humps but two horns. Sadly, nobody has invented GoatCase yet. -::: - -::: grafik l04-camelcase.svg -The capitals W and T stick out like the camel's two humps. -::: - -## Upper and lower case - -Java tells **upper and lower case** apart. The English word for this is **case-sensitive**. For Java, \`number\`, \`Number\` and \`NUMBER\` are three different names, so three different boxes: - -\`\`\` -int number = 4 -int Number = 6 -\`\`\` - -There are two variables here. If you later write \`NUMBER\`, Java finds no variable with that name. The program doesn’t run and you get an error message. - -> This also applies to Java words: \`String\` and \`System\` start with a capital, \`int\` and \`double\` are small. In your own programs, never create two variables that differ only in upper and lower case. It confuses you and everyone who reads your code. - -## Exercise: Three names - -::: übung l04-gross-klein -titel: Three names -auftrag: Ludwig created two variables: \`number\` with the value 4 and \`Number\` with the value 6. The structure chart should multiply them and output **Result: 24**. But it doesn’t run. - Click “Open in editor”, then Check, and read the error message. Fix the statement and click Check again. -start: l04-gross-klein-start.visustruct -test: - -> Result: 24 -braucht: anweisung -tipp: How many different names are in the structure chart? \`number\`, \`Number\` and \`NUMBER\` are three different names for the computer. -tipp: \`NUMBER\` doesn’t exist. For 24 you need 4 times 6, so \`number\` times \`Number\`: \`int result = number * Number\`. \`number * number\` gives 16. -lösung: l04-gross-klein-loesung.visustruct -::: - -## The four data types - -The data type says what kind of value may be stored in the variable. - -- **int:** whole numbers, e.g. \`int count = 3\` or \`int temperature = -5\` -- **double:** decimal numbers, e.g. \`double price = 2.5\`. You write a **point**, never a comma. -- **String:** text, e.g. \`String name = "Mia"\`. Text goes in quotation marks. -- **boolean:** truth values, only \`true\` or \`false\` - -There are more data types. You will soon need one of them: \`char\` for a **single character**, e.g. \`char grade = 'C'\`. A \`char\` goes in **single** quotation marks. You will get to know it better in lesson 17. - -::: diagramm l05-datentypen.visustruct -Three variables with different data types. Open the diagram and start the [simulation](#zeige-simulation). -::: - -You mostly meet truth values in **conditions**: \`age >= 18\` is either \`true\` or \`false\`. That is what lesson 8 is about. A variable of type \`boolean\` simply gets \`true\` or \`false\`: - -\`\`\` -boolean adult = false -\`\`\` - -::: angeberwissen -On 4 June 1996 the Ariane 5 rocket exploded just 37 seconds after launch. The cause was a single line: a decimal number for the speed was put into a whole-number variable that was too small for such large values. So the data type is no small matter, it can cost half a billion euros. -::: - -## Assignment with = - -The sign \`=\` is called an **assignment**. It does not mean “equals” as in maths. It means: work out the right-hand side and store the result in the variable on the left. - -\`\`\` -int points = 10 -points = points + 5 -\`\`\` - -In maths the second line would be wrong, because no number equals itself plus 5. In programming you read it like this: take the old value 10, add 5 and store 15 in \`points\`. You only write the data type the first time, when the variable is created. - -::: grafik l04-zuweisung.svg -First the right-hand side is calculated, then the result lands in the variable on the left. -::: - -## Calculating - -You can calculate with numbers: - -- \`+\` plus, \`-\` minus, \`*\` times, \`/\` divided by -- \`%\` remainder of a division (called “modulo”), e.g. \`17 % 5\` is 2 - -You know **division with remainder** from primary school, before you learned about decimals: 17 ÷ 5 = 3 remainder 2. 5 fits into 17 three times, that makes 15. To get to 17, 2 are still missing: that is the remainder. The computer calculates with whole numbers in exactly this way: \`/\` gives the result without the remainder, \`%\` gives only the remainder. - -| Primary school | with \`/\` | with \`%\` | -|---|---|---| -| 17 ÷ 5 = 3 remainder 2 | \`17 / 5\` gives 3 | \`17 % 5\` gives 2 | -| 20 ÷ 5 = 4 remainder 0 | \`20 / 5\` gives 4 | \`20 % 5\` gives 0 | -| 3 ÷ 5 = 0 remainder 3 | \`3 / 5\` gives 0 | \`3 % 5\` gives 3 | - -If the remainder is 0, the division works out exactly. Later you use this to check whether a number is even: for even numbers, \`number % 2\` is 0. - -**Multiplication and division come before addition and subtraction**, and brackets are worked out first: \`2 * (3 + 4)\` is 14. - -### Output text and result together - -A result on its own says little. With \`+\` you join a text and the value of a variable: - -\`\`\` -int age = 14 -output: "Age: " + age -\`\`\` - -This shows **Age: 14**. The text in quotation marks appears exactly as written; for \`age\` the computer puts in the value. The space after the colon belongs to the text, otherwise you get \`Age:14\`. - -## Exercise: Rectangle - -::: übung l05-rechteck -titel: Work out a rectangle -auftrag: A rectangle is 7 long and 4 wide. Create the variables \`length\` and \`width\`. Calculate the area and the perimeter in variables of their own and output both, like this: -\`Area: 28\` and on the next line \`Perimeter: 22\`. -test: - -> Area: 28 | Perimeter: 22 -braucht: anweisung -tipp: First think about which variables you need: two for the measurements and two for the results. They are all whole numbers. -tipp: The area is length times width, the perimeter is 2 times (length plus width). For the output you write e.g. \`output: "Area: " + area\`. The \`+\` joins the text and the value of the variable together. -lösung: l05-rechteck-loesung.visustruct -::: - -## Whole-number division - -If you divide two whole numbers and store the result in an \`int\` variable, everything after the decimal point is dropped. It is **not rounded**. - -\`\`\` -int x = 7 / 2 gives 3 -int r = 7 % 2 gives 1 (the remainder) -double d = 7.0 / 2 gives 3.5 -\`\`\` - -With \`/\` and \`%\` together you can convert things nicely, for example minutes into hours and minutes: - -::: diagramm l05-division.visustruct -135 minutes are 2 hours (135 / 60) with 15 minutes left over (135 % 60). -::: - -## Exercise: Convert seconds - -::: übung l05-sekunden -titel: Convert seconds -auftrag: 200 seconds should be output as minutes and seconds. The first and the last statement are already there. Insert statements between them that calculate \`minutes\` and \`remainder\`. -start: l05-sekunden-start.visustruct -test: - -> 3 minutes and 20 seconds -braucht: anweisung -tipp: How many whole minutes fit into 200 seconds, and how many seconds are left over? Work it out in your head first. -tipp: A minute has 60 seconds. Use \`/\` for the whole minutes and \`%\` for the remainder. Both variables have the type \`int\`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Exercise: Shopping - -::: übung l05-einkauf -titel: Shopping -auftrag: You buy 3 exercise books at 2.5 euros each. Create \`count\` as an \`int\` and \`price\` as a \`double\`, calculate the total price in a variable \`totalPrice\` and output \`Total: 7.5 euros\`. -test: - -> Total: 7.5 euros -braucht: anweisung -tipp: The count times the price gives the total price. For each variable, think about whether it is a whole number or a decimal number. -tipp: The total price is a decimal number, so \`totalPrice\` needs the type \`double\`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Now in Java - -A structure chart is a plan. In **Java** it becomes a program that the computer runs. Your shopping from the exercise looks like this: - -\`\`\`java editor -int count = 3; -double price = 2.5; -double totalPrice = count * price; -System.out.println("Total: " + totalPrice + " euros"); -\`\`\` - -- First the data type, then the name, then the value, just like in the box. -- Every statement ends with a **semicolon** \`;\`. -- Output in Java is written \`System.out.println(…)\`. - -How the computer understands your Java code and why it runs the same on Windows, macOS and Linux is explained in the lesson “Structure chart and code”. - -::: angeberwissen -Java was first called **Oak**, after the tree outside James Gosling’s office window. From 1991 his team at Sun Microsystems wanted to program small devices such as TV set-top boxes. But another company already had the name Oak. So in 1995 it became **Java**, like the coffee from the island of Java. -::: - -## Text in Java - -Variables do not only store numbers. For text there is the data type \`String\`: - -\`\`\`java editor -String name = "Mia"; -int age = 14; -double height = 1.62; -System.out.println(name + " is " + age + " years old."); -System.out.println("Height: " + height + " m"); -\`\`\` - -- Text goes in quotation marks. -- You write \`String\` with a capital letter, \`int\`, \`double\` and \`boolean\` in lowercase. Java tells them apart. -- With \`+\` you join text and numbers together. - -You type Java in the [Code](#zeige-codelive) area. The structure chart builds itself as you type. “Open in editor” brings the example into the Code area. Click “Run” there, then change the name, age or height and try again. - -::: übung l04-java-steckbrief -titel: Profile in Java -bereich: beide -code: java -auftrag: Write a profile of Ludwig the billy goat. Create three variables: the name **Ludwig**, the age **7** and the height **1.2**. Then output these two lines: - **Ludwig is 7 years old.** and **Height: 1.2 m** - “Open in editor” also opens the Code area on the right. Type your code there; the structure chart builds itself. Then click Check. -test: - -> Ludwig is 7 years old. | Height: 1.2 m -braucht: anweisung -tipp: You need a \`String\`, an \`int\` and a \`double\`. Every line ends with a semicolon. -tipp: \`String name = "Ludwig";\`, \`int age = 7;\` and \`double height = 1.2;\`. You join the output with \`+\`: \`System.out.println(name + " is " + age + " years old.");\` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Fill in the gaps: data types - -::: lückentext -titel: Profile in Java -auftrag: Fill in the missing words. On the outside is the frame that every Java program in its own file needs. The variables go inside it. -\`\`\`java -[[public]] class Profile { - public static [[void]] main(String[] args) { - [[int]] age = 14; - [[double]] height = 1.62; - [[String]] name = "Mia"; - [[boolean]] hasPet = true; - System.out.[[println]](name + " is " + age + " years old."); - } -} -\`\`\` -erklärung: Whole numbers are \`int\`, decimal numbers \`double\`, texts \`String\` (with a capital S) and truth values \`boolean\`. \`public class\` and \`public static void main\` make up the frame. In the Code area you may leave it out, more on this in the lesson “Structure chart and code”. -::: - -## Find the bug: seconds - -::: übung l04-java-fehler -titel: Fix the seconds -code: java -auftrag: The program should convert 200 seconds into minutes and seconds and output **3 min 20 s**. But it outputs **20 min 3 s**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: \`/\` divides without a remainder, \`%\` gives the remainder. Which one do you need for the minutes, which one for the seconds left over? -tipp: The two operators are swapped: \`int minutes = seconds / 60;\` and \`int rest = seconds % 60;\` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l04-java-laeuft-nicht -titel: Fix the profile -code: java -auftrag: The program should output **Ludwig is 7 years old.** But it doesn’t run, because two words are spelled wrong. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig is 7 years old. -braucht: anweisung -tipp: Java is case-sensitive. Compare every word in the output carefully with what was created above. -tipp: \`System\` is written with a capital S. The variable is called \`age\` with a small a: \`System.out.println(name + " is " + age + " years old.");\` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which data type? - -::: spiel frage -titel: Game: Which data type? -intro: Ludwig has collected values. Which data type fits each value? Eight rounds. -antworten: int | double | String | boolean - -runde: -frage: Which data type fits \`17\`? -richtig: int -> A whole number without a decimal point is an \`int\`. - -runde: -frage: Which data type fits \`3.5\`? -richtig: double -> Numbers with a decimal point are \`double\`. - -runde: -frage: Which data type fits \`"Hello"\`? -richtig: String -> Text in quotation marks is a \`String\`. - -runde: -frage: Which data type fits \`true\`? -richtig: boolean -> \`true\` and \`false\` are the two truth values, data type \`boolean\`. - -runde: -frage: Which data type fits \`"17"\`? -richtig: String -> Careful, quotation marks! Here 17 is a text you can't calculate with. - -runde: -frage: Which data type fits \`-4\`? -richtig: int -> Negative whole numbers are \`int\` as well. - -runde: -frage: Which data type fits \`0.0\`? -richtig: double -> The point makes it a decimal number, even if there is only a 0 behind it. - -runde: -frage: Which data type fits \`"false"\`? -richtig: String -> In quotation marks, false is just a text too. The truth value has no quotation marks. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a variable with a data type, a name and a value. → What is a variable? -- I can explain that Java is case-sensitive (\`number\` is not \`Number\`). → Upper and lower case -- I can tell the data types int, double, String and boolean apart. → The four data types -- I can explain that = is an assignment and not a comparison. → Assignment with = -- I can calculate with + - * / % and use whole-number division. → Calculating -- I can create and output variables in Java. → Now in Java -::: - -## Quiz - -::: quiz -? Which data type fits the value 3.75? -→ The four data types -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Decimal numbers have the type double. In VisuStruct you write them with a point. - -? How many variables do \`int number = 1\`, \`int Number = 2\` and \`int NUMBER = 3\` create? -→ Upper and lower case -- [ ] one -- [ ] two -- [x] three -> Java is case-sensitive. The three names are different, so there are three variables. - -? What value does x have after \`int x = 17 / 5\`? -→ Calculating -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> With whole numbers, everything after the decimal point is dropped. It is not rounded. - -? What does \`17 % 5\` give? -→ Calculating -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 divided by 5 is 3, remainder 2. The operator % gives the remainder. - -? At the start, \`int points = 10\`. What is in points after \`points = points * 2\`? -→ Assignment with = -- [ ] 10 -- [ ] 12 -- [x] 20 -> The right-hand side is calculated with the old value 10, and the result 20 is stored in points. - -? Which line correctly creates a decimal number in Java? -→ Now in Java -- [ ] double price = 2,5; -- [x] double price = 2.5; -- [ ] Double price = 2.5 -> Decimal numbers use a point, and the line ends with a semicolon. -::: -`,Mw=`--- -titel: Sequence -bereich: Basics -kurz: Run steps one after another, watch the order and swap values. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that a sequence runs from top to bottom -- explain why the order of the steps matters -- swap two values with a temporary variable -- write a sequence in Java -::: - -## Step by step - -A **sequence** is a series of steps that run exactly once and in exactly this order. In the structure chart the boxes simply sit on top of each other. - -Each box only starts when the box above it is finished. Nothing is skipped and nothing is repeated. - -::: diagramm l05-sequenz.visustruct -A sequence of four boxes: first \`length\` and \`width\` get their values, then the area is calculated, and finally it is output. Each box runs exactly once, from top to bottom. Output: 15. -::: - -> The sequence is the simplest of the three basic building blocks. Almost every structure chart is mostly made of sequences. - -## Order matters - -When you make tea, you notice straight away if the order is wrong. With calculations it is just the same, only it is harder to spot. - -::: diagramm l04-rechnen.visustruct -First double, then add 1: the result is 7. If you swap the two middle boxes, you get 8. -::: - -The statement \`number = number * 2\` means: take the value of \`number\`, multiply it by 2 and store the result in \`number\` again. So what is in \`number\` depends on what happened before. - -::: angeberwissen -Structure charts are not just school stuff. According to Shneiderman, companies like IBM and Siemens planned their programs with them too. So you are learning pro stuff here. Baaa-mazing! -::: - -## Exercise: Put in order - -::: übung l04-reihenfolge -titel: Put in order -auftrag: The start diagram has the right boxes, but in the wrong order. Move them so that **20** is output at the end. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Read each box and ask yourself: which variable does it need, and where does that variable get its value? -tipp: A variable has to be created before you calculate with it. The output comes right at the end. Check in your head: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Swap two values - -Imagine two glasses: one with juice, one with water. If you want to swap what is in them, you need a third, empty glass. - -It is exactly the same with variables. If you simply write \`a = b\` and then \`b = a\`, the old value of \`a\` is already lost. In the end both variables have the same value. - -The solution is a **temporary variable** that keeps the old value for a short time: - -::: grafik l05-tauschen.svg -Three steps with an empty glass: first the juice into temp, then the water to the left, then the juice from temp to the right. -::: - -::: diagramm l04-tauschen.visustruct -First a is kept in temp. Then a gets the value of b, and b gets the kept value from temp. -::: - -## Exercise: Swap glasses - -::: übung l04-glaeser-tauschen -titel: Swap glasses -auftrag: The left glass holds juice, the right one water. Swap the contents of the variables \`left\` and \`right\` before they are output. -Do not change the first two boxes. Insert new statements between them instead. -start: l04-tauschen-start.visustruct -test: - -> Left: water | Right: juice -braucht: anweisung -tipp: How do you swap two full glasses in real life? You need a third, empty glass. -tipp: Create a temporary variable with \`String temp = left\`. After that you need two more assignments. -lösung: l04-tauschen-loesung.visustruct -::: - -## Now in Java - -As in lesson 4, you can also write a sequence directly in Java. The swap from above looks like this: - -\`\`\`java editor -int a = 5; -int b = 9; -int temp = a; -a = b; -b = temp; -System.out.println("a = " + a); -System.out.println("b = " + b); -\`\`\` - -- Every box becomes one line. The lines run from top to bottom, just like the boxes. -- Every statement ends with a **semicolon** \`;\`. -- An output is called \`System.out.println(…)\` in Java. Mind the capital S in \`System\`: Java tells upper and lower case apart. - -You type Java in the [Code](#zeige-codelive) area to the right of the structure chart. While you type, VisuStruct builds the structure chart along with it. So you see straight away which box belongs to which line. - -::: übung l05-java-kino -titel: Cinema tickets in Java -bereich: beide -code: java -auftrag: A cinema ticket costs 9 euros. Write a Java program that calculates the price of 3 tickets and outputs **Total: 27**. - “Open in editor” also opens the Code area on the right. Type your code there; the structure chart builds itself. Then click Check. -test: - -> Total: 27 -braucht: anweisung -tipp: You need three variables: the price, the number of tickets and the result. Every line ends with a semicolon. -tipp: \`int price = 9;\` and \`int count = 3;\` create the variables. \`int total = price * count;\` calculates, and \`System.out.println("Total: " + total);\` outputs the result. -lösung: l05-java-kino-loesung.visustruct -::: - -## Fill in the gaps: swapping glasses - -::: lückentext -titel: Swap two values -auftrag: The values of \`a\` and \`b\` should be swapped. Fill in the missing words. -\`\`\`java -int a = 3; -int b = 8; -[[int]] temp = [[a]]; -a = [[b]]; -b = [[temp]]; -System.out.println("a = " + a + ", b = " + b); -\`\`\` -erklärung: First you save \`a\` in the helper variable, then \`a\` gets the value of \`b\`, and finally \`b\` gets the saved value. Output: **a = 8, b = 3** -::: - -## Find the bug: credit - -::: übung l05-java-fehler -titel: Fix the credit -code: java -auftrag: The canteen card holds 20 euros. A meal costs 7 euros. The program should output the credit **after** paying, so **Left: 13**. But it outputs **Left: 20**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l05-java-fehler-start.visustruct -test: - -> Left: 13 -braucht: anweisung -tipp: All lines are right, only their order is not. When is the credit output, and when is the meal paid? -tipp: Swap the last two lines: first \`credit = credit - price;\`, then the output. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l05-java-laeuft-nicht -titel: Share a pizza -code: java -auftrag: A pizza has 8 slices and 4 friends share it. The program should output **Everyone gets 2 slices.** But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Everyone gets 2 slices. -braucht: anweisung -tipp: Compare the name in the output letter by letter with the name created in the third line. -tipp: The variable is called \`perPerson\` with a capital P (camelCase). Write it exactly like that in the output. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Sort the boxes - -::: spiel reihenfolge -titel: Game: Sort the boxes -intro: Ludwig has mixed up the boxes. Put them in the right order. Four rounds. - -runde: -frage: At the end, **7** should be output. -- \`int number = 3\` -- \`number = number * 2\` -- \`number = number + 1\` -- \`output: number\` -> First double, then add 1: 3 · 2 + 1 = 7. The other way round you'd get (3 + 1) · 2 = 8. - -runde: -frage: At the end, **8** should be output. -- \`int number = 3\` -- \`number = number + 1\` -- \`number = number * 2\` -- \`output: number\` -> The same boxes, a different order, a different result: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a and b should swap their values. -- \`int temp = a\` -- \`a = b\` -- \`b = temp\` -- \`output: a + " " + b\` -> First a is saved in temp. Otherwise the old value is gone as soon as a gets the value of b. - -runde: -frage: Ludwig makes tea. -- Fill the kettle with water -- Switch the kettle on -- Wait until the water boils -- Pour the water over the tea bag -- Let the tea brew -> Every step needs the one before. Just like in a structure chart. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that a sequence runs from top to bottom. → Step by step -- I can explain why the order of the steps matters. → Order matters -- I can swap two values with a temporary variable. → Swap two values -- I can write a sequence in Java. → Now in Java -::: - -## Quiz - -::: quiz -? How often does each box of a sequence run? -→ Step by step -- [ ] Until a condition is false -- [x] Exactly once -- [ ] Not at all if it is empty -> In a sequence nothing is repeated and nothing is skipped. Loops are there for repetition. - -? At the start a = 5 and b = 9. Then \`a = b\` runs and after that \`b = a\`. What is in the variables at the end? -→ Swap two values -- [ ] a = 9 and b = 5 -- [x] a = 9 and b = 9 -- [ ] a = 5 and b = 5 -> After \`a = b\` the old value 5 is lost. \`b = a\` then only copies the 9 back. - -? Why do you need a temporary variable when swapping? -→ Swap two values -- [x] So that a value is not overwritten before it is needed -- [ ] So that the structure chart gets longer -- [ ] Because otherwise you cannot output variables - -? What does the sequence \`int x = 4\`, \`x = x + 1\`, \`x = x * 3\`, \`output: x\` output? -→ Order matters -- [ ] 13 -- [x] 15 -- [ ] 4 -> First x becomes 5, then 15. - -? What does every statement end with in Java? -→ Now in Java -- [ ] With a full stop . -- [x] With a semicolon ; -- [ ] With a colon : -> If the semicolon is missing, VisuStruct shows a hint below the code. -::: -`,Zw=`--- -titel: Input and output -bereich: Basics -kurz: Ask for values, show results and use the IPO model. ---- - -## Skills I will gain now - -::: kompetenzen -- explain the IPO model -- write an input with a data type, a name and a question -- join text and variables in an output with + -- work out a result first and store it in a variable, then output that variable, e.g. \`int sum = a + b\`, then \`output: sum\` -- pick out input, process and output from a task -- read numbers in Java with the scanner -- write comments with \`//\` and \`/* */\` and use them to structure and explain my code -::: - -## The IPO model - -Many programs work in three parts: - -1. **Input:** The program asks for values, e.g. two numbers. -2. **Process:** It calculates with the values. -3. **Output:** It shows the result. - -The first letters give **IPO**. So far the values were fixed in the structure chart. With an input, a different value can be used every time the chart runs. - -::: diagramm l06-eva.visustruct -Input at the top, process in the middle, output at the bottom. Start the simulation and try different numbers. -::: - -## Input with input: - -An input asks for a value while the chart runs and stores it in a variable. You write the **data type**, the **name** and a **question** in quotation marks: - -\`\`\` -input: int age "How old are you?" -input: double price "Price in euros?" -input: String name "What is your name?" -\`\`\` - -In the editor you choose the kind **Input** at the top of a Statement. The word \`input:\` is then already in front, and you only type \`int age "How old are you?"\`. - -> The data type decides what may be typed in. With \`int\`, the simulation reports an error if someone types 2.5 or Hello. - -## Output with output: - -An output shows text and values. Text goes in quotation marks, variables do not. With \`+\` you **join** the parts together: - -\`\`\` -output: "Hello " + name + "!" -output: "You are " + age + " years old." -\`\`\` - -Watch out for the spaces inside the quotation marks. Without them everything sticks together, and you see \`HelloMia!\` instead of \`Hello Mia!\`. - -## Exercise: Greeting - -::: übung l06-begruessung -titel: Greeting -auftrag: Ask for a name and greet the person. When Ada is typed in, **Hello Ada!** should be output. -test: Ada -> Hello Ada! -test: Ben -> Hello Ben! -braucht: anweisung -tipp: First comes an input for the name, then an output that uses the name. -tipp: The name is text, so the input needs the type \`String\`. In the output you join \`"Hello "\`, the variable and \`"!"\` with \`+\`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Careful with + in an output - -In an output, \`+\` **always** joins the parts together, even with numbers. If \`a\` is 3 and \`b\` is 4, then - -\`\`\` -output: a + b -\`\`\` - -does not show 7 but **34**. So calculate in a variable first and then output the variable: - -\`\`\` -int sum = a + b -output: "Sum: " + sum -\`\`\` - -That also fits the IPO model: you calculate in the process part, not in the output. - -## From the task to the structure chart - -Before you drag the first box, read the task carefully and take it apart. This is called **problem analysis**. Three questions help you: - -1. **What is given?** These values become the **input**. Also think about the data type. -2. **What are you looking for?** That becomes the **output**. -3. **How do you get from what is given to what you are looking for?** That is the **process**, often a formula. - -An example: *A cinema ticket costs 9 euros. The program asks how many tickets someone buys and shows the total price.* - -- Given: the number of tickets → \`input: int count "How many tickets?"\` -- Looking for: the total price → \`output: "Total price: " + total\` -- Way: count times 9 → \`int total = count * 9\` - -Now you only have to put the three parts on top of each other in the order I, P, O. - -> Look out for signal words in the task: *asks*, *reads in* or *enters* point to an input. *Shows*, *outputs* or *reports* point to an output. - -## Exercise: Calculator - -::: übung l06-rechner -titel: Sum and product -auftrag: Ask for two whole numbers. Then output their sum and their product, each on its own line. -With 3 and 4 it looks like this: \`Sum: 7\` and \`Product: 12\`. -test: 3, 4 -> Sum: 7 | Product: 12 -test: 10, -2 -> Sum: 8 | Product: -20 -test: 0, 5 -> Sum: 5 | Product: 0 -braucht: anweisung -tipp: Plan the order: first read in both numbers, then calculate, then output. -tipp: You need two inputs, two statements for calculating and two outputs. Calculate the sum in a variable of its own, otherwise \`+\` joins the numbers together. -lösung: l06-rechner-loesung.visustruct -::: - -## Exercise: Mean - -::: übung l06-mittelwert -titel: Mean -auftrag: Ask for two numbers, which may also be decimal numbers. Output their mean, so for 2 and 3 that is \`Mean: 2.5\`. -test: 2, 3 -> Mean: 2.5 -test: 4, 6 -> Mean: 5.0 -test: 1.5, 2 -> Mean: 1.75 -braucht: anweisung -tipp: You get the mean of two numbers by adding them and halving the result. -tipp: For decimal numbers you use the type \`double\`. The mean is the sum divided by 2. Remember the brackets: \`(x + y) / 2\`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Now in Java - -This is the IPO example from the start in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("First number?"); -int a = scanner.nextInt(); -System.out.println("Second number?"); -int b = scanner.nextInt(); -int sum = a + b; -System.out.println("Sum: " + sum); -\`\`\` - -- For input, Java needs a **Scanner**. You write the first two lines once at the very top, then you can read as often as you like. -- \`scanner.nextInt()\` reads a whole number, \`scanner.nextDouble()\` a decimal number and \`scanner.next()\` a word. -- The question to the user is a normal output right before it. VisuStruct turns both lines together into one input box. - -::: angeberwissen -Java was originally meant to control small devices, and it still does: many SIM cards in phones and many bank cards run a mini version called **Java Card**. Quite possibly you have Java in your pocket right now. -::: - -::: übung l06-java-tage -titel: Age in days -code: java -auftrag: Write a Java program that asks for the age in years and outputs roughly how many days that is. Use 365 days per year. For 2 years it shows **That is about 730 days.** -test: 2 -> That is about 730 days. -test: 10 -> That is about 3650 days. -test: 1 -> That is about 365 days. -braucht: anweisung -tipp: First the two scanner lines, then the question and the input, then the calculation, then the output. -tipp: \`int years = scanner.nextInt();\` reads. \`int days = years * 365;\` calculates, and \`System.out.println("That is about " + days + " days.");\` outputs. -lösung: l06-java-tage-loesung.visustruct -::: - -## Comments in code - -You may write notes in your code. They are called **comments**. Java skips them when it runs, so the program and the structure chart stay the same. There are two kinds: - -- \`//\` turns the rest of the line into a comment. This is a **single-line comment**. -- \`/*\` starts a **multi-line comment** and \`*/\` ends it. Everything in between belongs to the comment, however many lines. Java does not need the stars at the start of the lines in between, they just look tidy. - -In finished programs you sometimes also see \`/** … */\` with two stars at the start. That is a **Javadoc comment**, from which Java can create a description of the program. For you, \`//\` and \`/* */\` are enough. - -This is the IPO example with comments: - -\`\`\`java editor -/* - * Task: The program asks for two numbers - * and outputs their sum. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Input -System.out.println("First number?"); -int a = scanner.nextInt(); -System.out.println("Second number?"); -int b = scanner.nextInt(); - -// Process -int sum = a + b; // add the two numbers - -// Output -System.out.println("Sum: " + sum); // for 3 and 4 I expect: Sum: 7 - -// Next time: allow decimal numbers too -\`\`\` - -What comments are good for: - -- **The task:** the multi-line comment at the top says what the program should do. -- **Structure:** \`// Input\`, \`// Process\` and \`// Output\` divide the code by the IPO model. -- **Explanation:** a comment after a line says what your algorithm does there. -- **Expectation:** write down which output you expect. When you test, you compare. -- **Matching brackets:** later you will meet blocks in curly brackets \`{ }\`. Then, for example, \`} // end for\` shows which bracket closes what. -- **Keeping your thoughts:** what do you need to remember when you carry on next time? - -Click **Open in editor**, change a comment or add a new one. The structure chart stays the same, and VisuStruct keeps your comments. - -> Don't put a comment after every line. Explain what your algorithm does and why, not every single character. - -::: angeberwissen -Both kinds are much older than Java: \`/* … */\` comes from the language PL/I from 1964, \`//\` from BCPL from 1967. Java inherited them via C and C++. -::: - -## Fill in the gaps: comments - -::: lückentext -titel: Add the comments -auftrag: Add the characters so that a multi-line comment with the task is at the top and the other two notes are single-line comments. -\`\`\`java -[[/*]] - * Task: The program converts euros to cents. - [[*/]] -int euros = 3; -[[//]] Process -int cents = euros * 100; -[[//]] Output, expected: 300 -System.out.println(cents); -\`\`\` -erklärung: \`/*\` opens the multi-line comment and \`*/\` closes it. \`//\` only lasts until the end of the line, so every note needs its own \`//\`. -::: - -## Fill in the gaps: input - -::: lückentext -titel: Read a name and an age -auftrag: Fill in the missing words so that the program reads a name and an age. -\`\`\`java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("What is your name?"); -[[String]] name = scanner.[[next]](); -System.out.println("How old are you?"); -int age = scanner.[[nextInt]](); -System.out.println("Hello " + name + ", next year you will be " + [[(age + 1)]] + "."); -\`\`\` -erklärung: \`scanner.next()\` reads a word, \`scanner.nextInt()\` a whole number. The brackets around \`age + 1\` matter: without them Java first appends \`age\` and then \`1\` to the text, and 14 would become **141**. -::: - -## Find the bug: sum - -::: übung l06-java-fehler -titel: Fix the sum -code: java -auftrag: The program reads two numbers and should output their sum. For 3 and 4, however, it shows **Sum: 34** instead of **Sum: 7**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Sum: 7 -test: 10, 5 -> Sum: 15 -test: 0, 9 -> Sum: 9 -braucht: anweisung -tipp: Read the output from left to right: what is \`"Sum: " + a\`? And what happens when \`+ b\` comes after it? -tipp: Add brackets so that Java calculates first: \`System.out.println("Sum: " + (a + b));\` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l06-java-laeuft-nicht -titel: Age next year -code: java -auftrag: The program asks for your age and should say how old you will be next year. For 14 that is **Next year you will be 15**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> Next year you will be 15 -test: 9 -> Next year you will be 10 -braucht: anweisung -tipp: The input ends up in a variable. What exactly is it called, and what is it called in the output? -tipp: The variable is called \`age\` with a small a: \`System.out.println("Next year you will be " + (age + 1));\` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Game: What is output? - -::: spiel frage -titel: Game: What is output? -intro: Ludwig has built inputs and outputs. Predict what happens. Seven rounds. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: What is output? -- 7 -- [x] 34 -- a + b -> In an output, \`+\` always joins the parts together, even numbers. - -runde: - int a = 3 - int b = 4 - int sum = a + b - output: "Sum: " + sum -frage: What is output? -- [x] Sum: 7 -- Sum: 34 -- Sum: sum -> The calculating happens before, in a variable. The output only joins text and result. - -runde: - input: String name "What is your name?" - output: "Hello" + name + "!" -frage: You type Mia. What is output? -- Hello Mia! -- [x] HelloMia! -- Hello name! -> The text \`"Hello"\` has no space at the end. That's why everything sticks together. - -runde: - input: int age "How old are you?" -frage: You type 2.5. What happens? -- age is 2. -- age is 2.5. -- [x] The simulation reports an error. -> The data type \`int\` only allows whole numbers. - -runde: - input: double price "Price in euros?" -frage: Which part of IPO does this box belong to? -- [x] Input -- Process -- Output -> \`input:\` asks for a value. That is the input. - -runde: - double mean = (a + b) / 2.0 -frage: Which part of IPO does this box belong to? -- Input -- [x] Process -- Output -> Here something is calculated. That is the process. - -runde: - output: "Mean: " + mean -frage: Which part of IPO does this box belong to? -- Input -- Process -- [x] Output -> \`output:\` shows the result. That is the output. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain the IPO model. → The IPO model -- I can write an input with a data type, a name and a question. → Input with input: -- I can join text and variables in an output with +. → Careful with + in an output -- I can work out a result first and store it in a variable, then output that variable, e.g. \`int sum = a + b\`, then \`output: sum\`. → Careful with + in an output -- I can pick out input, process and output from a task. → From the task to the structure chart -- I can read numbers in Java with the scanner. → Now in Java -- I can write comments with \`//\` and \`/* */\` and use them to structure and explain my code. → Comments in code -::: - -## Quiz - -::: quiz -? What does IPO stand for? -→ The IPO model -- [x] Input, process, output -- [ ] Input, program, order -- [ ] Insert, paste, open -> Many programs first ask for values, process them and then show the result. - -? The variables a and b have the values 3 and 4. What does \`output: a + b\` show? -→ Careful with + in an output -- [ ] 7 -- [x] 34 -- [ ] a + b -> In an output, + joins the parts together. Calculate the sum in a variable first. - -? Which input fits if a price such as 4.99 should be typed in? -→ Input with input: -- [ ] input: int price "Price?" -- [x] input: double price "Price?" -- [ ] input: String "Price?" -> 4.99 is a decimal number, so double. Also, every input needs a variable name. - -? What does \`output: "Age: " + age\` show if age has the value 12? -→ Careful with + in an output -- [x] Age: 12 -- [ ] "Age: " + 12 -- [ ] Age: age - -? Task: *The program asks for the radius of a circle and shows its circumference.* Match each part of the text to where it belongs in the IPO model. -→ From the task to the structure chart -= asks for the radius -> Input -= circumference = 2 * 3.14 * radius -> Process -= shows its circumference -> Output -> The radius is given, the circumference is what you are looking for. The formula connects the two. - -? What does \`scanner.nextInt()\` read? -→ Now in Java -- [x] A whole number -- [ ] A decimal number -- [ ] A whole sentence -> For decimal numbers there is \`scanner.nextDouble()\`, for a word \`scanner.next()\`. - -? What does Java do with a comment? -→ Comments in code -- [ ] It outputs it as text. -- [x] It skips it. -- [ ] It reports an error. -> Comments are notes for people. The program runs exactly the same with or without them. - -? Where does a comment that starts with \`/*\` end? -→ Comments in code -- [x] At \`*/\` -- [ ] At the end of the line -- [ ] At the next empty line -> That is why a comment with \`/*\` can span several lines. A comment with \`//\`, on the other hand, ends at the end of the line. -::: -`,Lw=`--- -titel: Simulation and desk check -bereich: Basics -kurz: Run a structure chart step by step, work it out by hand and find mistakes. ---- - -## Skills I will gain now - -::: kompetenzen -- start the simulation, pause it, run single steps and stop it -- read the variables, the output and the last step during the simulation -- do a desk check by hand as a table -- tell syntax errors, runtime errors and logic errors apart -- find a mistake by comparing the values with what I expect -- follow Java code with Step and breakpoints -::: - -## Start the simulation - -Before branches and loops come along, you get to know your most important tool. From now on you can use the simulation to check every exercise yourself. - -With the [▶︎ Start simulation](#zeige-simulation) button on the right of the tools, VisuStruct runs your structure chart. While the simulation is running, you cannot edit the diagram. The same button is then called **■ End simulation**; it takes you back to editing. - -On the right, the **Controls** appear: - -- **Play** runs the diagram automatically, step by step. You set how long it waits between two steps under **Pause per step**. -- **Pause** stops for a moment. Then you carry on with Play or Step. -- **Step** runs exactly one step. That way you can watch calmly. -- **Stop** cancels and resets everything to the start. - -Open the following diagram in the editor and go through it with **Step**: - -::: diagramm sim-tausch.visustruct -The values of a and b are swapped. The variable temp keeps one value meanwhile. -::: - -## What the simulation shows - -- **Highlight:** One box in the diagram is coloured. With **Last step** it is the box that has just run. With **Next step** it is the box that comes next. -- **Input:** If the diagram asks for a value, you type it in here and confirm with **OK**. -- **Last Step:** Here you see what has just happened, e.g. \`a = 5 -> 5\` after the box \`a = b\`. On the left, the variables have already been replaced by their values; to the right of the arrow is the result. -- **Variables:** All variables with their current values. -- **Output:** Everything that was output with \`output:\`. - -If the [Code](#zeige-codelive) area is open on the right, the [console](#zeige-konsole) runs along there as well. It is cleared at the start and then shows output and input just as they would appear in the real program. - -At the end, **Simulation finished.** appears. If something goes wrong, the simulation stops on the faulty box and shows a message, e.g. **Unknown variable: total**. - -## The desk check - -In a **desk check** you play the computer yourself. You go through the structure chart with pen and paper and write down the values of the variables after every step. It is best to make a table: one column for the step, one for each variable and one for the output. - -::: grafik l07-schreibtischtest-en.svg -A sheet of paper and a pen are all you need: after each step you write the new values in a new row. -::: - -For the diagram above, the table looks like this: - -| Step | a | b | temp | Output | -|---|---|---|---|---| -| \`int a = 3\` | 3 | – | – | | -| \`int b = 5\` | 3 | 5 | – | | -| \`int temp = a\` | 3 | 5 | 3 | | -| \`a = b\` | 5 | 5 | 3 | | -| \`b = temp\` | 5 | 3 | 3 | | -| \`output: "a = " + a\` | 5 | 3 | 3 | a = 5 | -| \`output: "b = " + b\` | 5 | 3 | 3 | b = 3 | - -Then compare your table with the simulation. Do the values under **Variables** match your row after every step? - -> A desk check is especially useful in tests. There you have no computer, but you can still show what your structure chart does. - -::: angeberwissen -“Computer” used to be a job: people who did calculations with pencil, paper and a calculating machine. One of the best was the mathematician Katherine Johnson at NASA. She calculated flight paths for space capsules, and later also for the Apollo 11 flight to the Moon. - -In 1962 John Glenn was to become the first American to orbit the Earth. A new IBM computer had calculated the flight path. Glenn did not fully trust the machine and said: “Get the girl to check the numbers. If she says they’re good, I’m ready to go.” Katherine Johnson checked everything by hand, a real desk check. Her numbers were right, and Glenn flew. - -Katherine Johnson was Black, and at that time laws in the US state of Virginia separated Black and white people. In the film “Hidden Figures” (2016) she has to run across the whole site every time she needs the toilet. The film exaggerates this: separate toilets really existed, but Katherine Johnson said she simply ignored the sign. - -Her colleague Dorothy Vaughan saw early on that the IBM computers would take over the calculating work. She taught herself and her whole team the programming language FORTRAN. That is how human computers became programmers. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), mathematician at NASA (photo: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), public domain). -::: - -## Three kinds of errors - -Errors are part of programming, even for professionals. There are three kinds, and you find each of them in a different way: - -- **Syntax error**: The text breaks the rules of the language, e.g. a \`;\` is missing at the end. The program does not start at all. -- **Runtime error**: The program starts but stops at one point, e.g. when it divides by 0. -- **Logic error**: The program runs to the end, but the result is wrong, e.g. because brackets are missing: \`a + b / 2\` instead of \`(a + b) / 2\`. - -This is how VisuStruct shows you the errors: - -- **Syntax error:** They happen less often in a structure chart, because the boxes give you the shape. In the [Code](#zeige-codelive) area the spot is underlined in red, the line number turns red, and you read e.g. **Line 2: Still missing here: ;**. -- **Runtime error:** The simulation stops on the box and reports e.g. **Division by zero.** In the Code area the line number turns red, and the [console](#zeige-konsole) names the line: **Line 3: Division by zero.** -- **Logic error:** Nobody reports anything here. The computer does not know what you actually wanted. Only you know the right result, so the desk check helps here. - -> A red line number shows an error. The round red dot in front of the line number is something else: a breakpoint. You will meet it below in “Now in Java”. - -## Find mistakes - -If a structure chart outputs something wrong, that is a logic error. Then do this: - -1. **Write down what you expect:** What should come out for a particular input? -2. **Run it step by step:** Start the simulation with **Step** and watch the variables. -3. **Find the spot:** Where does a value first differ from what you expected? That is exactly where the mistake is. - -Common mistakes in a sequence are: - -- The **order** is wrong: a variable is used before it has a value. -- A value is **overwritten** before it is needed. -- **Brackets** are missing: \`a + b / 2\` is not the same as \`(a + b) / 2\`. -- Two whole numbers are divided: \`7 / 2\` gives \`3\`, because with \`int\` everything after the decimal point is dropped. - -> Later, branches and loops come along. Then the simulation also shows you which path is taken and how often a loop runs. - -::: angeberwissen -On 9 September 1947 Grace Hopper’s team found a real moth stuck between two contacts in the Harvard Mark II computer. They taped it into the logbook and wrote: “First actual case of bug being found.” Today the moth is in a museum in Washington. - -Grace Hopper was one of the first programmers in the world. In 1952 she built one of the first **compilers**: a program that translates code into machine code. She believed people should be able to give computers instructions in almost normal English. This led to the programming language COBOL, which many banks still use today. She even became an admiral in the US Navy. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), computer scientist and admiral (photo: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), public domain). -::: - -## Exercise: Fix the swap - -::: übung sim-tausch -titel: Fix the swap -auftrag: The structure chart should read in two numbers and output them swapped. So with 3 and 5, first **5** and then **3** should appear. But it outputs 5 twice. - Do a desk check or use **Step** and fix the mistake. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Go through the diagram with **Step** and write down the values of a and b after every step. Where does the 3 disappear? -tipp: Look at a after the step \`a = b\`. Where did the old value of a go? Keep it in a temporary variable first, as in the example above. -lösung: sim-tausch-loesung.visustruct -::: - -## Exercise: Fix the cinema tickets - -::: übung sim-kino -titel: Fix the cinema tickets -auftrag: A cinema ticket costs 8.50 euros. The structure chart should ask for the number of tickets and output the total price, so for 2 tickets \`Total: 17.0\`. - Start the simulation. It stops with an error message. Find out why and fix the structure chart. -start: sim-kino-start.visustruct -test: 2 -> Total: 17.0 -test: 3 -> Total: 25.5 -test: 0 -> Total: 0.0 -braucht: anweisung -tipp: Read the error message of the simulation carefully. Which variable does it not know yet? -tipp: The simulation runs the boxes from top to bottom. Does total already have a value when the output comes? You can move boxes with the mouse. -lösung: sim-kino-loesung.visustruct -::: - -> If a simulation keeps running forever, click **Stop**. When you **Check** an exercise, VisuStruct stops by itself after a very large number of steps. - -## Now in Java - -You can run Java code step by step too. In the [Code](#zeige-codelive) area the tool for this is called the **debugger**: - -- Click a line number on the left. A red dot appears: that is a **breakpoint**. [▶︎ Run](#zeige-ausfuehren) pauses before this line. -- The [Step](#zeige-einzelschritt) button runs exactly one line, like **Step** in the simulation. -- Below the code you see the current value of every variable. That is your desk check, except that the computer writes it down for you. - -::: übung l07-java-pizza -titel: Fix the pizza order -code: java -auftrag: A pizza costs 8 euros, extra cheese 2 euros. For 3 pizzas with extra cheese the program should output **Total: 30**. But it outputs 14. - Go through the Java code with Step and watch \`total\`. Find the mistake and fix it in the code. -start: l07-java-pizza-start.visustruct -test: - -> Total: 30 -braucht: anweisung -tipp: What value is in \`total\` after the fourth line? Work out in your head what Java calculates first. -tipp: As in maths, multiplication comes before addition: \`price + cheese * count\` is \`8 + 6\`. Add brackets: \`(price + cheese) * count\`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Fill in the gaps: desk check - -::: lückentext -titel: What is in the variables? -auftrag: Do a desk check in your head. After each line, write down the value the variable has afterwards, and in the last line what is output. -\`\`\`java -int a = 5; -int b = 2; -a = a + b; // a is now [[7]] -b = a * 2; // b is now [[14]] -a = b - a; // a is now [[7]] -System.out.println(a + b); // output: [[21]] -\`\`\` -erklärung: Each line calculates with the values that are in the variables right now: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, output 7 + 14 = 21. You can check it with Step in the Code area. -::: - -## Find the bug: score - -::: übung l07-java-fehler -titel: Fix the score -code: java -auftrag: In a game Ludwig first gets 10 points, then 5 points, then he loses 3 points. At the end **Points: 12** should appear. But **Points: 3** appears. - Go through the code with Step and watch \`points\`. Fix the wrong line and click Check. -start: l07-java-fehler-start.visustruct -test: - -> Points: 12 -braucht: anweisung -tipp: After which line does \`points\` suddenly have the value 3? What should actually happen in that line? -tipp: Ludwig loses 3 points, so 3 is subtracted from the old value: \`points = points - 3;\` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l07-java-laeuft-nicht -titel: Fix the bonus -code: java -auftrag: Ludwig has 10 points and gets 5 bonus points. At the end **Points: 15** should appear. But the code doesn’t run. Also step through it with Single step: in which line does it stop? - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Points: 15 -braucht: anweisung -tipp: Java runs the code from top to bottom. Does \`bonus\` already exist when it is used in the calculation? -tipp: Create \`bonus\` before the calculation: first \`int bonus = 5;\`, then \`points = points + bonus;\` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Do the desk check in your head and tap the wrong line before time runs out. The programs are in Java, but every line is a box like in the structure chart. Six rounds, 30 seconds each. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int temp = a; - a = b; - b = a; -frage: Should swap the values of a and b. -fehler: 5 -besser: b = temp; -> a already holds the value of b. The old value of a is in temp. - -runde: - int a = 4; - int b = 6; - double mean = a + b / 2; - System.out.println(mean); -frage: Should output the mean 5.0. -fehler: 3 -besser: double mean = (a + b) / 2.0; -> Without brackets the division comes first: 4 + 6 / 2 = 7. - -runde: - int tickets = 3; - double price = 8.5; - double total = tickets + price; - System.out.println("Total: " + total); -frage: Should output the price for 3 cinema tickets at €8.50 each. -fehler: 3 -besser: double total = tickets * price; -> For the total price you multiply, you don't add. - -runde: - int minutes = 135; - int hours = minutes / 60; - int rest = minutes / 60; - System.out.println(hours + " h " + rest + " min"); -frage: Should output 2 h 15 min. -fehler: 3 -besser: int rest = minutes % 60; -> \`%\` gives the remainder of a division. With \`/\` rest would hold a 2 as well. - -runde: - String name = "Ada"; - System.out.println("Hello " + "name" + "!"); -frage: Should output "Hello Ada!". -fehler: 2 -besser: System.out.println("Hello " + name + "!"); -> In quotation marks, name is just a text. The variable has no quotation marks. - -runde: - double price = 100; - price = price * 0.1; - System.out.println(price); -frage: Should raise the price by 10 %, so output 110.0. -fehler: 2 -besser: price = price + price * 0.1; -> Times 0.1 only gives the 10 %, so 10.0. That has to be added to the old price. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can start the simulation, pause it, run single steps and stop it. → Start the simulation -- I can read the variables, the output and the last step during the simulation. → What the simulation shows -- I can do a desk check by hand as a table. → The desk check -- I can tell syntax errors, runtime errors and logic errors apart. → Three kinds of errors -- I can find a mistake by comparing the values with what I expect. → Find mistakes -- I can follow Java code with Step and breakpoints. → Now in Java -::: - -## Quiz - -::: quiz -? Which button runs exactly one step? -→ Start the simulation -- [ ] Play -- [x] Step -- [ ] Stop -> Play keeps going automatically, Step runs only one step. - -? What do you write down after every step in a desk check? -→ The desk check -- [ ] Only the output at the end -- [x] The current values of all variables -- [ ] The generated Java code -> That way you see exactly where a value is different from what you expected. - -? First \`int a = 3\`, then \`int b = 5\`, then \`a = b\`. What values do a and b have afterwards? -→ The desk check -- [ ] a = 5 and b = 3 -- [x] a = 5 and b = 5 -- [ ] a = 3 and b = 5 -> The old value of a has been overwritten. That is why you need a temporary variable when swapping. - -? The Last Step field shows \`price = 4 * 2.5 -> 10.0\`. What does that mean? -→ What the simulation shows -- [ ] The variable price now has the value 4. -- [x] 4 * 2.5 was worked out and the result 10.0 was stored in price. -- [ ] The simulation has found a mistake. - -? The simulation reports **Unknown variable: sum**. What is a likely reason? -→ Find mistakes -- [x] sum is used before it has been given a value. -- [ ] sum is too big. -- [ ] The structure chart has too many boxes. - -? What does a red dot in front of a line number in the Code area do? -→ Now in Java -- [ ] It marks an error. -- [x] The program pauses before this line. -- [ ] The line is skipped. -> That is a breakpoint. ▶︎ or Step carries on. -? Match each error to its kind. -→ Three kinds of errors -= The semicolon at the end of the line is missing. -> Syntax error -= The program divides by 0 and stops. -> Runtime error -= The average is too big because brackets are missing. -> Logic error - -? The program runs to the end without any message but prints 12 instead of 21. What kind of error is that? -→ Three kinds of errors -- [ ] Syntax error -- [ ] Runtime error -- [x] Logic error -> There is no message, but the result is still wrong. You only find an error like this with your expectation and a desk check. -::: -`,Ww=`--- -titel: Branch -bereich: Basics -kurz: Use a condition to decide which of two paths runs. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that exactly one path of a branch runs -- use the comparison operators == != < <= > >= -- tell == and = apart -- build a branch with an empty path -- write a branch with if and else in Java -::: - -## If, then, else - -So far every box always ran. But often a program should **decide**: if a condition is met, do one thing, otherwise do the other. - -::: diagramm l07-alter-pruefen.visustruct -Depending on the age typed in, one message or the other appears. Try 17 and 18 in the simulation. -::: - -That is what the **branch** is for. The condition sits in the triangle at the top. Below it, the structure chart splits into two paths: - -- **true** (left): runs if the condition is true -- **false** (right): runs if the condition is false - -Always **exactly one** of the two paths runs. After that, the program continues below the branch. - -## Insert a branch - -Drag the **Branch** from the Elements area into the structure chart. Click the triangle and type the condition in the Object Inspector, e.g. \`age >= 18\`. The word \`if\` is already in front. - -Each path starts with a placeholder ø. Drag statements into it. A path may also hold several boxes on top of each other. - -## Comparison operators - -A condition is usually a **comparison**. Its result is always \`true\` or \`false\`. - -- \`==\` equal, e.g. \`number == 0\` -- \`!=\` not equal, e.g. \`answer != 42\` -- \`<\` less than, \`<=\` less than or equal -- \`>\` greater than, \`>=\` greater than or equal - -> Do not mix up \`==\` and \`=\`. A single \`=\` is an assignment and stores a value. To compare, you need two equals signs. - -With \`%\` you check whether a number can be divided by another: \`number % 2 == 0\` is true if \`number\` is even. - -## Exercise: Even or odd - -::: übung l07-gerade -titel: Even or odd -auftrag: Ask for a whole number. Output **even** if it is even, otherwise **odd**. -test: 4 -> even -test: 7 -> odd -test: 0 -> even -braucht: verzweigung -tipp: There are two possibilities here, so you need a branch. How do you recognise an even number? -tipp: A number is even if there is no remainder when you divide it by 2. The condition is \`number % 2 == 0\`. -lösung: l07-gerade-loesung.visustruct -::: - -## Exercise: The larger number - -::: übung l07-maximum -titel: The larger number -auftrag: Ask for two whole numbers and output only the larger one. If both are the same, output that number. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: A branch compares the two numbers. Each path holds exactly one output. -tipp: Compare with \`a > b\`. In the true path you output \`a\`, in the false path \`b\`. -lösung: l07-maximum-loesung.visustruct -::: - -## An empty path - -Sometimes something should only happen if the condition is true, and otherwise nothing at all. Then the false path stays **empty**. In the structure chart it shows the placeholder **ø**. - -::: diagramm l07-rabatt.visustruct -From 50 euros on, you get 5 euros off. Otherwise nothing happens; the false path is empty. The output below runs in both cases. -::: - -::: angeberwissen -A single forgotten branch caused a stir across half the internet in 2014: the security hole **Heartbleed** in OpenSSL, a program that many websites use for encryption. A computer could tell the server: “Send me back the word *hat*, it has 500 letters.” The server did not check whether 500 matched the real length of 3 and simply sent back 500 characters from its memory. Sometimes they contained other people's passwords. All that was missing was one if: if the stated length is greater than the real one, then send nothing back. By the way, this had nothing to do with brackets. Baah, check your inputs before you believe them. -::: - -## Exercise: Absolute value - -::: übung l07-betrag -titel: Absolute value of a number -auftrag: Ask for a whole number. If it is negative, make it positive. Then output the number. So -5 becomes 5, and 7 stays 7. -Use a branch with an empty path and only **one** output below the branch. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Only negative numbers need to be changed. What happens to a number if you multiply it by -1? -tipp: The condition is \`number < 0\`. In the true path you flip the sign, e.g. with \`number = 0 - number\`. The false path stays empty. -lösung: l07-betrag-loesung.visustruct -::: - -## Now in Java - -This is the age check from the start of this lesson in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("How old are you?"); -int age = scanner.nextInt(); -if (age >= 18) { - System.out.println("You are an adult."); -} else { - System.out.println("You are not an adult yet."); -} -\`\`\` - -- The condition comes after \`if\` in **round brackets** \`( )\`. -- The true branch is in **curly brackets** \`{ }\`, the false branch follows after \`else\` in its own curly brackets. There is no semicolon after a curly bracket. -- If the false branch is empty, simply leave out \`else { }\`. -- Your program needs the first two lines once as soon as it reads input. After that, \`scanner.nextInt()\` reads a whole number. The question before it is a normal output. - -::: angeberwissen -Java has a mascot too: **Duke**, a little black-and-white fellow with a big red nose. He was drawn by the Java team in 1992 and has been waving from conferences and T-shirts ever since. Ludwig thinks a billy goat would have been a better fit. -::: - -::: übung l08-java-bestanden -titel: Passed in Java -code: java -auftrag: Write a Java program that asks for the number of points. From 50 points on it outputs **passed**, otherwise **not passed**. - Type the code in the Code area and then click Check. -test: 50 -> passed -test: 49 -> not passed -test: 100 -> passed -test: 0 -> not passed -braucht: verzweigung -tipp: Start with the two lines for the scanner. Then ask for the points, read them and compare them with \`if\`. -tipp: The condition is \`points >= 50\`. One branch has \`System.out.println("passed");\`, and after \`else\` comes \`System.out.println("not passed");\`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Fill in the gaps: even or odd - -::: lückentext -titel: Even or odd -auftrag: Fill in the missing words and symbols. The program should output whether a number is even. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which number?"); -int number = scanner.nextInt(); -[[if]] (number [[%]] 2 [[==]] 0) [[{]] - System.out.println("even"); -[[}]] [[else]] { - System.out.println("odd"); -} -\`\`\` -erklärung: \`number % 2\` is the remainder when dividing by 2. If it is 0, the number is even. To compare you need \`==\`, a single \`=\` is an assignment. Both branches are in curly brackets. -::: - -::: angeberwissen -If a branch holds only a single statement, Java even lets you leave out the curly brackets. We never do that, and here is why: - -\`\`\`java -if (age >= 18) - System.out.println("You are an adult."); - System.out.println("You may watch the film."); -\`\`\` - -Without brackets only the first line belongs to the if. The second one always runs, even for twelve-year-olds. The indentation is misleading, because Java ignores spaces. - -Exactly this kind of mistake was hidden in iPhones and Macs in 2014, known as **“goto fail”**. In the language C, the line \`goto fail;\` accidentally appeared twice under an if without brackets. The second one always ran and skipped the check of the security certificates. On an open Wi-Fi network, an attacker could have pretended to be your bank, for example. Baah, brackets cost two characters, a security hole costs much more. -::: - -## Find the bug: admission - -::: übung l08-java-fehler -titel: Fix the admission -code: java -auftrag: At the zoo, children **under** 6 get in for free, everyone else pays 4 euros. But the program also lets six-year-olds in for free. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l08-java-fehler-start.visustruct -test: 5 -> Free entry -test: 6 -> 4 euros -test: 10 -> 4 euros -test: 0 -> Free entry -braucht: verzweigung -tipp: Try the program with 6. Which comparison operator still lets 6 through? -tipp: “Under 6” means \`age < 6\`. \`<=\` means “less than or equal to”. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l08-java-laeuft-nicht -titel: Rated 16 -code: java -auftrag: The film is rated 16. Ludwig is 15, so the program should output **No entry**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> No entry -braucht: verzweigung -tipp: Look closely at the condition in the brackets after \`if\`. Does this variable exist? -tipp: The variable is called \`age\` with a small a: \`if (age >= 16) {\` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which way? - -::: spiel frage -titel: Game: Which way? -intro: Ludwig has written branches in Java. Work out the condition in your head and predict what is output. Seven rounds. - -runde: - int x = 5; - if (x > 5) { - System.out.println("big"); - } else { - System.out.println("small"); - } -frage: What is output? -- big -- [x] small -- nothing -> 5 > 5 is false, because 5 is not greater than 5. So the false branch. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("big"); - } else { - System.out.println("small"); - } -frage: What is output? -- [x] big -- small -- big and small -> \`>=\` means "greater than or equal". 5 >= 5 is true. - -runde: - int number = 7; - if (number % 2 == 0) { - System.out.println("even"); - } else { - System.out.println("odd"); - } -frage: What is output? -- even -- [x] odd -- 1 -> 7 % 2 is the remainder 1, so not 0. The number is odd. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: What is output? -- -4 -- [x] 4 -- nothing -> -4 < 0 is true, so the sign is turned around. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: What is output? -- -4 -- [x] 4 -- nothing -> 4 < 0 is false. The false branch is empty, the program simply carries on below. - -runde: - int grade = 3; - if (grade != 3) { - System.out.println("different"); - } else { - System.out.println("three"); - } -frage: What is output? -- different -- [x] three -- nothing -> \`!=\` means "not equal". 3 != 3 is false. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: What is output? -- 8 -- [x] 12 -- 20 -> The program outputs the bigger of the two numbers. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that exactly one path of a branch runs. → If, then, else -- I can use the comparison operators == != < <= > >=. → Comparison operators -- I can tell == and = apart. → Comparison operators -- I can build a branch with an empty path. → An empty path -- I can write a branch with if and else in Java. → Now in Java -::: - -## Quiz - -::: quiz -? Which path runs if the condition is false? -→ If, then, else -- [ ] The true path -- [x] The false path -- [ ] Both paths -- [ ] Neither, the program ends -> With false, the right path runs. After that, the program continues below the branch. - -? Which condition checks whether age is exactly 18? -→ Comparison operators -- [ ] age = 18 -- [x] age == 18 -- [ ] age >= 18 -> A single = is an assignment. To compare, you need ==. - -? What does ø in a path mean? -→ An empty path -- [x] Nothing happens in this case. -- [ ] The program ends here. -- [ ] The condition is false. - -? The condition is \`points >= 50\`. For which value is it true? -→ Comparison operators -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= means greater than or equal. So 50 is included. - -? In textbooks the branch often has another name. Which one? -→ If, then, else -- [ ] Multiple selection -- [x] Conditional statement -- [ ] Iteration -> The statements in a path only run under a condition. That is why it is also called a conditional statement. - -? Where does the condition of a branch go in Java? -→ Now in Java -- [x] In round brackets right after if -- [ ] In curly brackets after else -- [ ] After a semicolon -> \`if (age >= 18) { … }\`: the round brackets belong to the condition, the curly ones to the branch. -::: -`,Ew=`--- -titel: Combining conditions -bereich: Basics -kurz: Join several conditions with and, or and not, and check ranges of values. ---- - -## Skills I will gain now - -::: kompetenzen -- explain when a combination with && is true -- explain when a combination with || is true -- turn a condition round with ! -- check whether a value is in a range such as 1 to 6 -- read the truth table for &&, || and ! -- store the result of a condition in a boolean variable -- evaluate combined conditions confidently -- combine conditions in Java with && and || -::: - -## And: && - -Sometimes a single comparison is not enough. For example, you only get a youth ticket if you are **at least 12 and younger than 18**. - -With \`&&\` (and) you join two conditions. The result is only \`true\` if **both** conditions are true. - -::: diagramm l08-jugendticket.visustruct -Both comparisons must be right. Try 11, 12, 17 and 18 in the simulation. -::: - -## Or: || - -With \`||\` (or) the result is \`true\` if **at least one** of the conditions is true. Both may be true as well. - -\`\`\` -day == 6 || day == 7 -\`\`\` - -This condition is true if \`day\` has the value 6 or the value 7, so on Saturday or Sunday. - -> On many keyboards you type the vertical bar \`|\` with Shift and the backslash key. On a German keyboard it is AltGr and \`<\`, on a Mac Option and 7. - -## Exercise: Weekend - -::: übung l08-wochenende -titel: Weekend -auftrag: Ask for a day of the week as a number (1 is Monday, 7 is Sunday). Output **Weekend** if it is Saturday or Sunday, otherwise **Weekday**. -test: 6 -> Weekend -test: 7 -> Weekend -test: 1 -> Weekday -test: 5 -> Weekday -braucht: verzweigung -tipp: It is the weekend if the day is 6 **or** 7. For that you need two comparisons in one condition. -tipp: Saturday is 6, Sunday is 7. Join the two comparisons with \`||\`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Check ranges of values - -In German schools, grades go from 1 (best) to 6. You check whether a grade is **in the range** with two comparisons and \`&&\`: - -\`\`\` -grade >= 1 && grade <= 6 -\`\`\` - -::: grafik l08-zahlenstrahl.svg -The green range from 1 to 6 is valid; both ends are included. -::: - -The other way round, a grade is **outside** the range if it is too small **or** too big: - -::: diagramm l08-note.visustruct -There is no grade smaller than 1 or bigger than 6. -::: - -> The way you write it in maths, \`1 <= grade <= 6\`, does not work. You have to split it into two comparisons and join them with \`&&\`. - -## Exercise: Valid grade - -::: übung l08-note-gueltig -titel: Valid grade -auftrag: Ask for a grade as a whole number. Output **valid** if it is between 1 and 6 (1 and 6 are included), otherwise **invalid**. -test: 1 -> valid -test: 6 -> valid -test: 3 -> valid -test: 0 -> invalid -test: 7 -> invalid -braucht: verzweigung -tipp: A valid grade has to meet two things at once: not too small **and** not too big. -tipp: The grade must be greater than or equal to 1 and at the same time less than or equal to 6. -lösung: l08-gueltig-loesung.visustruct -::: - -## Not and brackets - -With \`!\` (not) you turn a condition round: \`true\` becomes \`false\` and the other way round. - -\`\`\` -!(grade >= 1 && grade <= 6) -\`\`\` - -This is true if the grade is **not** in the range, and it means the same as \`grade < 1 || grade > 6\`. - -If \`&&\` and \`||\` appear together, \`&&\` is worked out first, just like multiplication before addition. It is better to use **brackets**; then it is clear what belongs together: - -\`\`\` -(age >= 12 && age < 18) || student == 1 -\`\`\` - -## The truth table - -A **truth table** shows at a glance what \`&&\`, \`||\` and \`!\` produce. On the left are all four possibilities for two conditions \`a\` and \`b\`, on the right the result: - -::: grafik l09-wahrheitstabelle.svg -Green is true, red is false. With && there is only one green row, with || only one red one. -::: - -Rules of thumb: - -- \`a && b\` is only true if **both** are true. -- \`a || b\` is only false if **both** are false. -- \`!a\` flips the value. - -::: angeberwissen -The data type \`boolean\` is named after **George Boole**. As early as 1847 this Englishman showed that you can calculate with true and false just like with numbers, almost a hundred years before the first computer existed. Today his logic is in every chip. -::: - -## Truth values in variables - -The result of a condition is a **truth value** of type \`boolean\`. You can store it in a variable just like a number and use it later: - -\`\`\` -boolean weekend = day == 6 || day == 7 -\`\`\` - -The branch then only says \`weekend\`. That reads almost like a sentence: "If weekend, then …". Don't write \`weekend == true\`, because \`weekend\` already is true or false. - -With \`!weekend\` you ask for the opposite, a weekday. - -::: ludwig -Long conditions become clearer when you put parts of them into boolean variables with meaningful names, for example \`boolean teenager = age >= 12 && age < 18\`. -::: - -## Game: True or false? - -Now it's your turn. Work out each condition in your head. After every answer you see how VisuStruct evaluates it step by step. - -::: spiel -age: 8..20 -day: 1..7 -grade: 0..7 -student: boolean -weekend: boolean -? age >= 18 -? day == 6 -? grade != 3 -? age >= 12 && age < 18 -? day == 6 || day == 7 -? student && weekend -? !student -? grade >= 1 && grade <= 6 -? !(age < 18) -? student || age >= 18 -? (age >= 12 && age < 18) || student -? !(grade >= 1 && grade <= 6) -? !weekend && day != 3 -? age >= 12 && age < 18 && !student -::: - -## Exercise: Leap year - -::: übung l08-schaltjahr -titel: Leap year -auftrag: A year is a leap year if it can be divided by 4 but not by 100. Years that can be divided by 400 are leap years anyway. -Ask for a year and output **Leap year** or **Not a leap year**. -test: 2024 -> Leap year -test: 2023 -> Not a leap year -test: 1900 -> Not a leap year -test: 2000 -> Leap year -braucht: verzweigung -tipp: Check the rule with examples first: 2024 is a leap year, 1900 is not, 2000 is. “Can be divided” means the remainder is 0. -tipp: Divisible by 4 means \`year % 4 == 0\`, not divisible by 100 means \`year % 100 != 0\`. Join these two with \`&&\`, put brackets around them and add the case for 400 with \`||\`. -tipp: Almost the solution: one single branch with the condition \`(year % 4 == 0 && year % 100 != 0) || year % 400 == 0\`. The true branch has \`output: "Leap year"\`, the false branch \`output: "Not a leap year"\`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Now in Java - -The youth ticket from the start looks like this in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("How old are you?"); -int age = scanner.nextInt(); -if (age >= 12 && age < 18) { - System.out.println("Youth ticket"); -} else { - System.out.println("No youth ticket"); -} -\`\`\` - -The symbols \`&&\`, \`||\` and \`!\` are exactly the ones you already use in the structure chart. They come from Java. As always, the whole condition is in the round brackets after \`if\`. - -::: übung l09-java-schule -titel: School or free time -code: java -auftrag: Write a Java program that asks for the hour (0 to 23). From 8 up to and including 15 it outputs **School**, otherwise **Free time**. -test: 8 -> School -test: 15 -> School -test: 16 -> Free time -test: 7 -> Free time -braucht: verzweigung -tipp: The hour has to meet two conditions at the same time. Which operator means “and”? -tipp: \`if (hour >= 8 && hour <= 15) { … } else { … }\` -lösung: l09-java-schule-loesung.visustruct -::: - -## Fill in the gaps: conditions - -::: lückentext -titel: Grade and weekend -auftrag: Fill in the missing operators and words. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which grade?"); -int grade = scanner.nextInt(); -if (grade >= 1 [[&&]] grade [[<=]] 6) { - System.out.println("valid grade"); -} else { - System.out.println("invalid grade"); -} -System.out.println("Which day (1 to 7)?"); -int day = scanner.nextInt(); -[[boolean]] weekend = day == 6 [[||]] day == 7; -if ([[!]]weekend) { - System.out.println("school day"); -} -\`\`\` -erklärung: A grade is valid when **both** conditions are true, so \`&&\`. It is the weekend when **one** of the two is true, so \`||\`. The result fits into a \`boolean\` variable, and \`!\` turns it around. -::: - -## Find the bug: no school - -::: übung l09-java-fehler -titel: Fix the day off -code: java -auftrag: The program should output **no school** for day 6 (Saturday) and day 7 (Sunday), otherwise **school**. But it always outputs **school**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l09-java-fehler-start.visustruct -test: 6 -> no school -test: 7 -> no school -test: 3 -> school -test: 1 -> school -braucht: verzweigung -tipp: Can \`day\` be 6 **and** 7 at the same time? -tipp: It is enough if one of the two conditions is true: \`day == 6 || day == 7\`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l09-java-laeuft-nicht -titel: Fix the weekend -code: java -auftrag: Day 6 is Saturday, day 7 is Sunday. Today is day 7, so **no school** should appear. But the program doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> no school -braucht: verzweigung -tipp: The condition has two parts. Compare the variable name in both parts. -tipp: In the second part the variable is also called \`day\` with a small d: \`if (day == 6 || day == 7) {\` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain when a combination with && is true. → And: && -- I can explain when a combination with || is true. → Or: || -- I can turn a condition round with !. → Not and brackets -- I can check whether a value is in a range such as 1 to 6. → Check ranges of values -- I can read the truth table for &&, || and !. → The truth table -- I can store the result of a condition in a boolean variable. → Truth values in variables -- I can evaluate combined conditions confidently (at least 8 of 10 right in the game). → Game: True or false? -- I can combine conditions in Java with && and ||. → Now in Java -::: - -## Quiz - -::: quiz -? When is \`a && b\` true? -→ And: && -- [ ] If a or b is true -- [x] Only if a and b are both true -- [ ] If exactly one of them is true -> With && both parts must be right. If one is false, the whole thing is false. - -? The variable day has the value 6. What does \`day == 6 || day == 7\` give? -→ Or: || -- [x] true -- [ ] false -> With || it is enough if one side is true. - -? Which condition checks whether number is between 10 and 20 (both included)? -→ Check ranges of values -- [ ] 10 <= number <= 20 -- [ ] number >= 10 || number <= 20 -- [x] number >= 10 && number <= 20 -> With || every number would be allowed, because every number is greater than 10 or less than 20. - -? What does \`!(5 > 3)\` give? -→ Not and brackets -- [ ] true -- [x] false -> 5 > 3 is true, and ! turns that round. - -? How do you write “age is at least 12 and less than 18” in Java? -→ Now in Java -- [ ] if (12 <= age < 18) -- [x] if (age >= 12 && age < 18) -- [ ] if (age >= 12 || age < 18) -> Each comparison stands on its own, joined with \`&&\`. -::: -`,Nw=`--- -titel: Switch -bereich: Basics -kurz: Run one of many cases, depending on the value of a variable. ---- - -## Skills I will gain now - -::: kompetenzen -- insert a switch, add cases and change case names -- explain when the case default runs -- decide whether a switch or a branch fits better -- write a case selection in Java with switch -::: - -## Many possibilities, one variable - -A drinks machine has three buttons: 1 for water, 2 for juice, 3 for cocoa. With branches you need three boxes nested inside each other. That quickly gets wide and confusing. - -::: diagramm l09-verschachtelt.visustruct -The machine with three nested branches. Each branch checks the same variable choice again. -::: - -Whenever you compare **one variable** with **several fixed values**, there is an easier way: the **switch**. - -## How a switch is built - -At the top of the box is the variable that decides. Below it there is a column of its own for every case. Above each column is the value it belongs to. - -::: diagramm l09-automat.visustruct -The same machine as a switch. If choice has the value 2, only column 2 runs. -::: - -- The simulation looks for the case whose name matches the value exactly. -- If no case matches, **default** runs. This case is always on the far right. -- After that, the program continues below the switch. Only **one** case ever runs. - -> A new switch has the cases 1, 2 and default. Click a case to change its **Case name** in the Object Inspector. With **Add Case** you get a new column in front of default. - -## Cases with text - -The cases do not have to be numbers. You can also choose by the content of a \`String\` variable. Then you write the text in quotation marks as the case name, for example \`"red"\`. Just like in code, this shows that it is a text. - -::: diagramm l09-ampel.visustruct -A traffic light: depending on the colour typed in, a different message appears. In the simulation, also try a colour that does not exist. -::: - -Upper and lower case matter: \`Red\` does not match the case \`"red"\`. - -## Exercise: Grade in words - -::: übung l09-note -titel: Grade in words -auftrag: Ask for a grade as a whole number and output it in words: 1 very good, 2 good, 3 satisfactory, 4 sufficient, 5 poor, 6 fail. - For any other number, **Not a valid grade** should appear. -test: 1 -> very good -test: 4 -> sufficient -test: 6 -> fail -test: 9 -> Not a valid grade -braucht: fallauswahl -tipp: For many fixed values, a switch is clearer than lots of branches. Every grade gets a case of its own. -tipp: Keep using “Add Case” until you have the cases 1 to 6 and default. At the top of the switch you write \`grade\`. -lösung: l09-note-loesung.visustruct -::: - -## Exercise: Calculator - -::: übung l09-rechner -titel: Calculator -auftrag: Ask, one after another, for a whole number \`a\`, an operator as a \`String\` and a second whole number \`b\`. - For \`+\`, \`-\` and \`*\` you output the result. For any other sign, **Unknown operator** appears. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Unknown operator -braucht: fallauswahl -tipp: First read in all three values. After that, a switch on the operator decides what is calculated. -tipp: Before the switch, create \`int result = 0\`. In each case you calculate, for example, \`result = a + b\` and then output \`result\`. The case names are \`"+"\`, \`"-"\` and \`"*"\` in quotation marks, because the operator is a \`String\`. -tipp: The outline from top to bottom: three inputs (\`int a\`, \`String operator\`, \`int b\`), then \`int result = 0\`, then a switch over \`operator\`. Case \`"+"\` holds \`result = a + b\` and \`output: result\`, cases \`"-"\` and \`"*"\` the same. The default case holds \`output: "Unknown operator"\`. -lösung: l09-rechner-loesung.visustruct -::: - -## Switch or branch? - -The switch only checks whether a value is **exactly equal** to a case name. So: - -- **Switch:** one variable, several fixed values. Examples: choosing from a menu, day of the week as a number, grades. -- **Branch:** ranges and combined conditions. Examples: \`age >= 18\`, \`temperature < 0 || temperature > 30\`. - -So for “age 18 or older” you still need the branch. You cannot create a case of its own for every possible age. - -## Now in Java - -The drinks machine looks like this in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 water, 2 juice, 3 cocoa"); -int choice = scanner.nextInt(); -switch (choice) { - case 1 -> { - System.out.println("Here is your water."); - } - case 2 -> { - System.out.println("Here is your juice."); - } - case 3 -> { - System.out.println("Here is your cocoa."); - } - default -> { - System.out.println("This button does not exist."); - } -} -\`\`\` - -- After \`switch\`, the variable being compared is in round brackets. -- Every case starts with \`case\`, the value and an arrow \`->\`. After it, curly brackets hold what should happen. -- \`default\` is the otherwise case. It runs when no value matches. - -::: angeberwissen -The arrow \`case 1 ->\` has only existed in Java since 2020 (Java 14). Before that you wrote \`case 1:\` and had to end every case with \`break;\`. If you forgot it, the program simply ran on into the next case. So the arrow has saved a lot of programmers a lot of trouble. -::: - -::: übung l10-java-wuerfel -titel: Dice in words -code: java -auftrag: Write a Java program with \`switch\` that reads a number. For 1 it outputs **one**, for 2 **two**, for 3 **three** and otherwise **unknown**. -test: 1 -> one -test: 2 -> two -test: 3 -> three -test: 7 -> unknown -braucht: fallauswahl -tipp: You need three \`case\` branches and one \`default\` branch. -tipp: \`switch (number) { case 1 -> { System.out.println("one"); } … default -> { System.out.println("unknown"); } }\` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Fill in the gaps: switch - -::: lückentext -titel: Months in words -auftrag: Fill in the missing words and symbols of the case selection. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which month (1 to 3)?"); -int month = scanner.nextInt(); -[[switch]] (month) { - [[case]] 1 -> { - System.out.println("January"); - } - case 2 [[->]] { - System.out.println("February"); - } - [[case]] 3 -> { - System.out.println("March"); - } - [[default]] -> { - System.out.println("Unknown"); - } -} -\`\`\` -erklärung: After \`switch\` the variable is in round brackets. Each case starts with \`case\`, the value and the arrow \`->\`. \`default\` runs when no case matches. -::: - -## Find the bug: calculator - -::: übung l10-java-fehler -titel: Fix the calculator -code: java -auftrag: The calculator reads two numbers and a choice: 1 plus, 2 minus, 3 times. For 7, 3 and choice 2 the result should be **4**, but it calculates **-4**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Not an option -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Plus and times are right. Take a close look at \`case 2\`. -tipp: With minus the order matters: \`a - b\` instead of \`b - a\`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l10-java-laeuft-nicht -titel: Fix the grade -code: java -auftrag: Ludwig got grade 2. The program should output **good**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> good -braucht: fallauswahl -tipp: Which variable does the case selection check? Is it written the same way above? -tipp: The brackets after \`switch\` need \`grade\` with a small g: \`switch (grade) {\` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which case? - -::: spiel frage -titel: Game: Which case? -intro: Ludwig presses buttons on the drinks machine and other case selections. What happens? Six rounds. - -runde: - int choice = 2; - switch (choice) { - case 1 -> { System.out.println("water"); } - case 2 -> { System.out.println("juice"); } - case 3 -> { System.out.println("cocoa"); } - default -> { System.out.println("There is no such button."); } - } -frage: What is output? -- water -- [x] juice -- There is no such button. -> choice is 2, so exactly \`case 2\` runs. - -runde: - int choice = 4; - switch (choice) { - case 1 -> { System.out.println("water"); } - case 2 -> { System.out.println("juice"); } - case 3 -> { System.out.println("cocoa"); } - default -> { System.out.println("There is no such button."); } - } -frage: What is output? -- cocoa -- nothing -- [x] There is no such button. -> No case fits 4, so \`default\` runs. - -runde: - String day = "monday"; - switch (day) { - case "Monday" -> { System.out.println("maths"); } - case "Tuesday" -> { System.out.println("sports"); } - default -> { System.out.println("free"); } - } -frage: What is output? -- maths -- sports -- [x] free -> With texts every letter counts: "monday" with a small m is not "Monday". - -runde: - int grade = 0; - switch (grade) { - case 1 -> { System.out.println("very good"); } - case 2 -> { System.out.println("good"); } - case 3 -> { System.out.println("satisfactory"); } - } -frage: What is output? -- very good -- [x] nothing -- satisfactory -> No case fits, and there is no \`default\`. Then simply nothing happens. - -runde: -frage: You want to check whether \`age >= 18\`. What fits better? -- [x] a branch -- a case selection -> A case selection only compares with fixed values. For "greater than or equal" you need a branch. - -runde: -frage: A dice shows 1 to 6, and for every number a different word should appear. What fits better? -- a branch -- [x] a case selection -> One variable, six fixed values: that's exactly what the case selection is for. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can insert a switch, add cases and change case names. → How a switch is built -- I can explain when the case default runs. → How a switch is built -- I can decide whether a switch or a branch fits better. → Switch or branch? -- I can write a case selection in Java with switch. → Now in Java -::: - -## Quiz - -::: quiz -? The variable day has the value 5. There are the cases 1, 2, 3 and default. What runs? -→ How a switch is built -- [ ] Nothing at all -- [ ] The case 3 -- [x] The case default -> No case name matches 5, so default applies. - -? How many cases run each time the switch runs? -→ How a switch is built -- [x] Exactly one -- [ ] All cases from the matching one on -- [ ] All cases -> After the matching case, the program continues straight below the switch. - -? What is a switch NOT suitable for? -→ Switch or branch? -- [ ] Choosing menu item 1, 2 or 3 -- [x] Checking whether a temperature is above 30 degrees -- [ ] Handling a colour such as red, yellow or green -> “Above 30” is a range. For that you need a branch with a comparison. - -? At the traffic light you type \`Red\`, and the case is called \`"red"\`. What happens? -→ How a switch is built -- [ ] The case \`"red"\` runs. -- [x] The case default runs. -- [ ] The simulation reports an error. -> Upper and lower case matter. Red and red are different texts. - -? Under which name do you also find the switch in textbooks? -→ How a switch is built -- [ ] Conditional statement -- [ ] Sequence -- [x] Multiple selection -> One of many cases is selected. That is why it is also called multiple selection. - -? What is the otherwise case of a switch called in Java? -→ Now in Java -- [ ] else -- [x] default -- [ ] case 0 -::: -`,Fw=`--- -titel: While loop -bereich: Basics -kurz: Repeat steps as long as a condition is true. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the while loop checks its condition before every pass -- use a counter variable with a start value, a condition and a change -- notice when a loop never ends and fix the mistake -- follow a loop pass by pass in a desk check -- write a while loop in Java -::: - -## Repeat as long as something holds - -Often a program should do something several times. Instead of writing the same box five times, you use a **loop**. - -The **while loop** repeats its inside **as long as** its condition is true. The condition is at the top, in the head of the box. The inside is indented below it, framed by a bracket on the left. - -::: diagramm l10-countdown.visustruct -A countdown: as long as number is greater than 0, number is output and gets smaller by 1. After that, Start! appears. -::: - -## The condition comes first - -The while loop is a **pre-test loop**. Before **every** pass, the condition is checked: - -1. Check the condition. -2. If it is true, run the inside once and start again at 1. -3. If it is false, the program continues below the loop. - -::: grafik l10-kopfgesteuert.svg -After every pass it goes back to the condition. Only when it is false does the program continue below the loop. -::: - -In the countdown the condition is checked four times: with 3, 2 and 1 it is true, with 0 it is false. - -> If the condition is already false the first time it is checked, the inside runs **not even once**. Start the countdown with \`int number = 0\` and only Start! appears. - -## A counter variable - -Many loops count along. For that you need three things: - -- **A start value before the loop**, e.g. \`int i = 1\` -- **A condition in the head**, e.g. \`i <= 5\` -- **A change inside**, e.g. \`i = i + 1\` (short: \`i++\`) - -If one of these parts is missing, the loop counts wrongly or not at all. - -::: angeberwissen -Counting loops are ancient! In their 1973 article, Nassi and Shneiderman showed their idea using the **factorial**, among other things: 5! = 1 · 2 · 3 · 4 · 5 = 120. Here is my goat version of it. Try it with **Step** in the simulation and watch \`factorial\` grow. Baaah, that goes fast! -::: - -::: diagramm l10-fakultaet.visustruct -The factorial with a counter variable \`i\`, rebuilt from an example in the 1973 article. -::: - -## Exercise: Count to n - -::: übung l10-bis-n -titel: Count to n -auftrag: Ask for a whole number \`n\` and output all numbers from 1 to \`n\`, each on its own line. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: You need a counter variable that starts at 1 and grows by 1 in every pass. -tipp: Before the loop, create \`int i = 1\`. The condition is \`i <= n\`. Inside, you output \`i\` and then increase \`i\` by 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Watch out: endless loop - -If nothing inside the loop changes the condition, it stays true forever. The loop never stops. This is called an **endless loop**. - -::: diagramm l10-endlos.visustruct -Here number = number - 1 is missing. The loop outputs 3 again and again and never reaches Start! -::: - -Ask yourself for every loop: **What inside makes sure that the condition becomes false at some point?** When an exercise is checked, VisuStruct stops after a very large number of steps and reports a possible endless loop. - -## Exercise: Double up to 100 - -::: übung l10-verdoppeln -titel: Double up to 100 -auftrag: Ask for a whole number. Keep doubling it until it is at least 100. - At the end, output **how often** you doubled it. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: The loop runs as long as the number is still too small. Count along how often it has run. -tipp: You need a second variable \`int steps = 0\`. The condition is \`number < 100\`. Inside, you double the number with \`number = number * 2\` and increase \`steps\` by 1. If the number is already 100, the loop does not run at all. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Loops in a desk check - -With a loop, the **desk check** is especially valuable. You see exactly how often the loop runs and with which value it stops. Every check of the condition gets a row of its own. - -::: diagramm l16-schreibtisch.visustruct -A while loop adds up the numbers 1, 2 and 3. -::: - -| Step | sum | i | Output | -|---|---|---|---| -| \`int sum = 0\` | 0 | – | | -| \`int i = 1\` | 0 | 1 | | -| \`i <= 3\` → true | 0 | 1 | | -| \`sum = sum + i\` | 1 | 1 | | -| \`i = i + 1\` | 1 | 2 | | -| \`i <= 3\` → true | 1 | 2 | | -| \`sum = sum + i\` | 3 | 2 | | -| \`i = i + 1\` | 3 | 3 | | -| \`i <= 3\` → true | 3 | 3 | | -| \`sum = sum + i\` | 6 | 3 | | -| \`i = i + 1\` | 6 | 4 | | -| \`i <= 3\` → false | 6 | 4 | | -| \`output: sum\` | 6 | 4 | 6 | - -Common mistakes in loops are: - -- The loop runs **once too often or once too few**, e.g. \`<\` instead of \`<=\`. -- A variable is **not changed** in the loop, or changed in the wrong direction. Then the loop never ends. -- A variable gets its start value **inside** the loop instead of before it and is reset in every pass. - -## Exercise: Fix the countdown - -::: übung l16-countdown -titel: Fix the countdown -auftrag: The countdown should count down from the start value and output **Go!** at the end. So with 3: 3, 2, 1, Go! - The start diagram has two mistakes. Find and fix both. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Go! -test: 1 -> 1 | Go! -test: 0 -> Go! -tipp: There are two mistakes. One is about counting, the other about the output at the end. -tipp: Start the simulation with start value 3 and watch i under Variables. Does i get smaller? Also compare the last output carefully with the task. -lösung: l16-countdown-loesung.visustruct -::: - -## Now in Java - -The countdown from the start of this lesson looks like this in Java: - -\`\`\`java editor -int number = 3; -while (number > 0) { - System.out.println(number); - number = number - 1; -} -System.out.println("Start!"); -\`\`\` - -- After \`while\` the condition is in round brackets, as with \`if\`. -- Everything that is repeated is in curly brackets. That is the body of the loop. -- The body is indented. As in the structure chart, you can see what belongs to the loop. - -::: übung l11-java-gerade -titel: Even numbers in Java -code: java -auftrag: Write a Java program that reads a whole number \`n\` and uses a while loop to output all even numbers from 2 up to at most \`n\`. For 7 that is 2, 4 and 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: You need a counter variable that starts at 2. The loop runs as long as it is not greater than \`n\`. -tipp: \`int number = 2;\` before the loop, then \`while (number <= n) { … }\`. In the body you output \`number\` and increase it with \`number = number + 2;\`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Fill in the gaps: while - -::: lückentext -titel: Sum from 1 to 10 -auftrag: The program should add up the numbers from 1 to 10. Fill in the missing words and symbols. -\`\`\`java -int sum = 0; -int number = 1; -[[while]] (number [[<=]] 10) [[{]] - sum = sum + [[number]]; - [[number]] = number + 1; -} -System.out.println("Sum: " + sum); -\`\`\` -erklärung: The loop runs as long as \`number\` is at most 10. In each pass \`number\` is added to the sum, then \`number\` goes up by 1. Without that line it would be an endless loop. Output: **Sum: 55** -::: - -## Find the bug: saving - -::: übung l11-java-fehler -titel: Fix the savings plan -code: java -auftrag: Ludwig saves 5 euros every week. The program asks for the savings goal and should output after how many weeks he has reached it. For 50 euros that is 10 weeks, but the program says **Weeks: 11**. - Go through the code with Step and watch \`money\` and \`weeks\`. Fix the bug and click Check. -start: l11-java-fehler-start.visustruct -test: 50 -> Weeks: 10 -test: 12 -> Weeks: 3 -test: 5 -> Weeks: 1 -test: 1 -> Weeks: 1 -braucht: whileSchleife -tipp: How much money does Ludwig have when the loop runs for the last time? Does he really need to save for another week? -tipp: He only saves as long as the goal has **not been reached** yet: \`while (money < goal)\`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l11-java-laeuft-nicht -titel: Fix the countdown -code: java -auftrag: The countdown should output **3**, **2**, **1** and then **Go!** But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Go! -braucht: whileSchleife -tipp: The bug is inside the loop. Which variable should be output, and what is it really called? -tipp: Output \`number\` with a small n: \`System.out.println(number);\` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Game: How often? - -::: spiel frage -titel: Game: How often? -intro: Ludwig has written while loops. Do the desk check in your head. Six rounds. - -runde: - int number = 3; - while (number > 0) { - System.out.println(number); - number = number - 1; - } - System.out.println("Go!"); -frage: What is output? -- [x] 3\\n2\\n1\\nGo! -- 3\\n2\\n1\\n0\\nGo! -- Go! -> When number is 0, \`number > 0\` is false, so the 0 is not output any more. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: What is output? -- [x] nothing -- 10 -- 10 to 4 -> The condition is checked first. 10 < 5 is false, the body doesn't run even once. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: What happens? -- 1, 2 and 3 are output. -- [x] The loop never stops. -- Nothing is output. -> i never gets bigger in the body. The condition always stays true: an endless loop. - -runde: - int number = 1; - while (number < 100) { - number = number * 2; - } - System.out.println(number); -frage: What is output? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Only at 128 is \`number < 100\` false. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: What is output? -- 4 -- 5 -- [x] 6 -> i runs 0, 2, 4, 6. At 6, \`i < 5\` is false. The 5 is skipped. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: How often does the body run? -- 3 times -- [x] 4 times -- 10 times -> The body runs at i = 1, 4, 7 and 10. After that i is 13. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the while loop checks its condition before every pass. → The condition comes first -- I can use a counter variable with a start value, a condition and a change. → A counter variable -- I can notice when a loop never ends and fix the mistake. → Watch out: endless loop -- I can follow a loop pass by pass in a desk check. → Loops in a desk check -- I can write a while loop in Java. → Now in Java -::: - -## Quiz - -::: quiz -? What does pre-test loop mean? -→ The condition comes first -- [x] The condition is checked before every pass. -- [ ] The condition is checked after every pass. -- [ ] The loop always runs exactly once. -> The head with the condition is at the top and is worked out first. - -? \`int i = 5\`, then a while loop with the condition \`i < 3\`. How often does the inside run? -→ The condition comes first -- [x] Not at all -- [ ] Once -- [ ] Three times -> The very first time it is checked, 5 < 3 is false. - -? Why does a loop with the condition \`number > 0\` never end if the inside only contains \`output: number\`? -→ Watch out: endless loop -- [ ] Because output makes the number bigger -- [x] Because number never changes and the condition stays true -- [ ] Because the condition is written wrongly -> Something has to happen inside that makes the condition false at some point. - -? What else are loops called in textbooks? -→ The condition comes first -- [x] Iteration or repetition -- [ ] Branches -- [ ] Subprograms -> A loop repeats statements. That is why it is also called iteration or repetition. - -? A loop should count from 1 to 5, but it only goes up to 4. What is the likely mistake? -→ A counter variable -- [x] The condition says i < 5 instead of i <= 5. -- [ ] The loop is a while loop. -- [ ] The variable i has the wrong name. - -? Which line starts a while loop correctly in Java? -→ Now in Java -- [ ] while number > 0 { -- [x] while (number > 0) { -- [ ] while (number > 0); -> The condition is in round brackets, then \`{\` opens the body. -::: -`,Rw=`--- -titel: Do-while loop -bereich: Basics -kurz: Run first, check afterwards: loops with at least one pass. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the do-while loop only checks its condition after the inside -- explain why the inside runs at least once -- repeat an input until it is valid -- write a do-while loop in Java -::: - -## Do first, check afterwards - -The **do-while loop** is a **post-test loop**. The condition is at the bottom, in the foot of the box. This is how it runs: - -1. Run the inside once. -2. Check the condition. -3. If it is true, start again at 1. If it is false, the program continues below the loop. - -::: grafik l11-vergleich.svg -On the left, the while loop checks first; on the right, the do-while loop runs first. -::: - -At the top of the box is \`do\`, and the condition is in the foot. When you click the loop, the word \`while\` is in front of the text field in the Object Inspector: the condition means “repeat as long as …”. - -> Just like with the while loop, the condition says **when the loop repeats**. As long as it is true, the loop keeps running. - -::: angeberwissen -Python has no do-while loop at all. If you want “do first, check later” there, you write \`while True:\` and leave the loop with \`break\`. Java, C and JavaScript, on the other hand, have had \`do … while\` for decades. -::: - -## At least one pass - -Because the condition is only checked at the end, the inside **always runs at least once**. That is the most important difference from the while loop. - -::: diagramm l11-einmal.visustruct -i is 10 and so it is not less than 5. Still, 10 is output once, because the check only comes afterwards. -::: - -With a while loop and the same condition \`i < 5\`, nothing at all would be output here. - -## Typical use: repeat an input - -The do-while loop fits whenever something has to happen first before you can check it. The best example is an input: only after someone has typed something do you know whether it is valid. - -::: diagramm l11-eingabe-pruefen.visustruct -The question comes again until a number from 1 to 10 has been typed in. In the simulation, first try 0 and then 12. -::: - -Here the condition describes the **invalid** input: \`number < 1 || number > 10\`. As long as the input is invalid, the question is asked again. - -## Exercise: Valid grade - -::: übung l11-note -titel: Valid grade -auftrag: Ask for a grade. If it is not between 1 and 6, ask again. If it is valid, output it, for example **Your grade: 3**. -test: 3 -> Your grade: 3 -test: 0, 7, 2 -> Your grade: 2 -test: 9, 6 -> Your grade: 6 -test: -1, 1 -> Your grade: 1 -braucht: doUntilSchleife -tipp: The grade is asked for at least once. After that, the condition decides whether to ask again. -tipp: Before the loop, create \`int grade = 0\`. The input \`input: grade "Grade from 1 to 6"\` goes inside the loop. The condition is \`grade < 1 || grade > 6\`. -lösung: l11-note-loesung.visustruct -::: - -## Exercise: Sum until 0 - -::: übung l11-summe -titel: Sum until 0 -auftrag: Keep asking for numbers until a 0 is typed in. Then output the sum of all the numbers typed in. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: You need a variable for the sum that starts at 0 before the loop. In every pass the new number is added. -tipp: Before the loop, create \`int sum = 0\` and \`int number = 0\`. In the loop you ask for \`number\` and calculate \`sum = sum + number\`. Adding the 0 does no harm. The loop keeps running as long as \`number != 0\`. -tipp: The outline: \`int sum = 0\` and \`int number = 0\`, then a do-while loop with the condition \`number != 0\`. Inside it: the input of \`number\` and \`sum = sum + number\`. Below the loop: \`output: sum\`. -lösung: l11-summe-loesung.visustruct -::: - -## Now in Java - -The loop that still runs once looks like this in Java: - -\`\`\`java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -\`\`\` - -- The body is between \`do {\` and \`}\`, the condition only comes afterwards. -- Here there is a **semicolon** after \`while (…)\`. That is different from the while loop. - -::: übung l12-java-verdoppeln -titel: Double at least once -code: java -auftrag: Write a Java program that reads a start number. A do-while loop doubles it and outputs it as long as it is less than 100. A number of 100 or more is still doubled at least once: 200 becomes 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: In the body you double the number and output it. The condition comes at the end. -tipp: \`do { number = number * 2; System.out.println(number); } while (number < 100);\` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Fill in the gaps: do-while - -::: lückentext -titel: Tripling -auftrag: The number is tripled and output as long as it is less than 100. Fill in the missing words and symbols. -\`\`\`java -int number = 1; -[[do]] { - number = number * 3; - System.out.println(number); -} [[while]] (number [[<]] 100)[[;]] -\`\`\` -erklärung: The body is between \`do {\` and \`}\`, the condition only comes afterwards. In a do-while loop there is a semicolon after \`while (…)\`. Output: 3, 9, 27, 81 and 243. -::: - -## Find the bug: halving - -::: übung l12-java-fehler -titel: Fix the halving -code: java -auftrag: The program reads a start number and keeps dividing it by 2 (whole-number division) with a do-while loop. Every result is output until **0** is reached. For 10 that is 5, 2, 1 and 0. But the 0 is missing. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: What value does \`number\` have after the 1 has been output? Does the loop run once more then? -tipp: The loop should keep going as long as the number is greater than 0: \`while (number > 0);\` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l12-java-laeuft-nicht -titel: Fix the doubling -code: java -auftrag: Starting at 1, the program should keep doubling as long as the number is less than 20: **1**, **2**, **4**, **8**, **16**. It outputs only the 1 and then stops. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: The first round still runs. The bug appears when the condition at the bottom is checked for the first time. -tipp: The condition needs \`limit\` with a small l: \`} while (number < limit);\` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Game: At least once? - -::: spiel frage -titel: Game: At least once? -intro: While or do-while? Ludwig shows you loops. Predict what happens. Six rounds. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: What is output? -- nothing -- [x] 10 -- 10 to 4 -> First do, then check: the body runs once, after that 11 < 5 is false. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: What is output? -- [x] nothing -- 10 -- 11 -> In a while loop the condition comes first. 10 < 5 is false, so nothing runs at all. - -runde: - int number = 200; - do { - number = number * 2; - } while (number < 100); - System.out.println(number); -frage: What is output? -- 200 -- [x] 400 -- 800 -> It is doubled once no matter what. After that 400 < 100 is false. - -runde: - int number = 30; - do { - number = number * 2; - System.out.println(number); - } while (number < 100); -frage: What is output? -- 60 -- [x] 60\\n120 -- 60\\n120\\n240 -> After 60, 60 < 100 is true, so it goes on. After 120 it stops. - -runde: -frage: Which line ends a do-while loop in Java correctly? -- [x] \`} while (i < 5);\` -- \`} while (i < 5)\` -- \`while (i < 5) {\` -> After the \`while (…)\` of a do-while loop there is a semicolon. - -runde: -frage: What is a do-while loop typically used for? -- [x] repeating an input until it is valid -- counting from 1 to 10 -- swapping two values -> The question is asked at least once. Only after that do you know whether the input was valid. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the do-while loop only checks its condition after the inside. → Do first, check afterwards -- I can explain why the inside runs at least once. → At least one pass -- I can repeat an input until it is valid. → Typical use: repeat an input -- I can write a do-while loop in Java. → Now in Java -::: - -## Quiz - -::: quiz -? What does post-test loop mean? -→ Do first, check afterwards -- [ ] The condition is checked before every pass. -- [x] The condition is checked after every pass. -- [ ] The loop has no condition. -> The condition is in the foot, at the bottom, and is worked out after the inside. - -? The condition of a do-while loop is false from the start. How often does the inside run? -→ At least one pass -- [ ] Not at all -- [x] Exactly once -- [ ] Forever -> The inside runs first. Only then does the loop notice that it should stop. - -? You want to ask for a number from 1 to 10. Which condition goes in the foot? -→ Typical use: repeat an input -- [ ] \`number >= 1 && number <= 10\` -- [x] \`number < 1 || number > 10\` -- [ ] \`number == 10\` -> The loop repeats as long as the input is invalid, that is, less than 1 or greater than 10. - -? When is a do-while loop better than a while loop? -→ At least one pass -- [x] When the inside has to run at least once, e.g. for an input -- [ ] When the loop might not need to run at all -- [ ] When you want to count exactly to 10 - -? What comes at the end of a do-while loop in Java? -→ Now in Java -- [ ] } while (condition) { -- [x] } while (condition); -- [ ] } until (condition); -> A semicolon follows the condition. -::: -`,Jw=`--- -titel: For loop -bereich: Basics -kurz: Count a fixed number of passes with Init, Condition and Step. ---- - -## Skills I will gain now - -::: kompetenzen -- name the three parts Init, Condition and Step of the for loop -- explain in which order the three parts run -- count forwards, backwards and in bigger steps with a for loop -- write a for loop in Java and explain its three fields -::: - -## Counting with a fixed plan - -In the while loop you already counted along: a start value before the loop, a condition in the head, a change inside. The **for loop** puts these three parts together in the head. That way you see at a glance how it counts. - -::: grafik l12-while-zu-for.svg animiert -The while loop becomes a for loop: init, condition and step move together into the head. -::: - -When you click a for loop, it has three fields on the right: - -- **Init:** \`int i = 1\` creates the counter variable. -- **Condition:** \`i <= 5\` says how long to count. -- **Step:** \`i++\` increases \`i\` by 1 after every pass. - -::: diagramm l12-eins-bis-fuenf.visustruct -A for loop that outputs the numbers from 1 to 5. -::: - -::: angeberwissen -Why is the counting variable almost always called \`i\`? In maths \`i\` has long stood for an index. And in FORTRAN, one of the first programming languages from 1957, all variables starting with I to N were automatically whole numbers. Programmers have not broken the habit to this day. -::: - -## In which order? - -The three parts run at different times: - -1. **Init** runs exactly once, right at the start. -2. **Condition** is checked before every pass. If it is false, the loop is finished. -3. After the inside comes the **Step**, then the condition again. - -::: grafik l12-for-ablauf.svg -Init only runs once. After that, condition, inside and step repeat. -::: - -So the for loop is a pre-test loop, like the while loop. Go through the example in the simulation with **Step** and watch how \`i\` changes. - -> Use the for loop when you know beforehand how often something should happen. If the end depends on something that only happens during the loop, for example an input, the while or do-while loop is better. - -## Example: sum from 1 to n - -A for loop is good for adding up numbers. Before the loop there is \`int sum = 0\`. In every pass the current number \`i\` is added. - -::: diagramm l12-summe.visustruct -With n = 4 the loop calculates 0 + 1 + 2 + 3 + 4. At the end, 10 is output. -::: - -## Other steps - -The step does not always have to be \`i++\`: - -- \`i--\` counts backwards, for example with Init \`int i = 10\` and Condition \`i >= 1\`. -- \`i = i + 2\` jumps in steps of two, for example 0, 2, 4, 6. - -Make sure that Init, Condition and Step fit together. With Init \`int i = 1\`, Condition \`i >= 1\` and Step \`i++\`, the condition never becomes false. - -::: übung l12-countdown -titel: Countdown -auftrag: Ask for a whole number \`n\`. Use a for loop to count backwards from \`n\` to 1 and output each number. Then output **Go!**. -test: 3 -> 3 | 2 | 1 | Go! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Go! -test: 1 -> 1 | Go! -braucht: forSchleife -tipp: This time the for loop counts backwards. Think about where it starts, when it stops and how it counts. -tipp: Init is \`int i = n\`, the condition \`i >= 1\` and the step \`i--\`. The output Go! goes below the loop, not inside it. -lösung: l12-countdown-loesung.visustruct -::: - -## Exercise: Times table - -::: übung l12-einmaleins -titel: Times table -auftrag: Ask for a number and output its times table from 1 to 10. Each line looks like this: **3 x 1 = 3**, **3 x 2 = 6** and so on. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: The loop counts from 1 to 10. In every pass you output one line of the table. -tipp: Inside, first calculate \`int product = number * i\`. Then output \`number + " x " + i + " = " + product\`. Remember the spaces inside the quotation marks. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Exercise: Fix the sum - -::: übung l16-summe -titel: Fix the sum -auftrag: The structure chart should add up all numbers from 1 to n. So with n = 4 the result should be 1 + 2 + 3 + 4 = **10**. But it outputs 6. - Do a desk check or use **Step** and fix the mistake. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Go through the loop with n = 4 in your head or with **Step**. Which numbers are really added? -tipp: Look at the last value of i the loop runs with. With n = 4, i = 4 must be added too. -lösung: l16-summe-loesung.visustruct -::: - -## Now in Java - -The sum from 1 to n looks like this in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Up to which number?"); -int n = scanner.nextInt(); -int sum = 0; -for (int i = 1; i <= n; i++) { - sum = sum + i; -} -System.out.println(sum); -\`\`\` - -The round brackets after \`for\` hold the three fields of the for loop, separated by semicolons: - -| Field | In Java | Meaning | -|---|---|---| -| Init | \`int i = 1\` | \`i\` starts at 1. | -| Condition | \`i <= n\` | The loop runs as long as this is true. | -| Step | \`i++\` | After every pass \`i\` gets 1 bigger. | - -\`i++\` is short for \`i = i + 1\`. To count backwards, use \`i--\`. - -::: übung l13-java-quadrate -titel: Square numbers in Java -code: java -auftrag: Write a Java program that reads a whole number \`n\` and uses a for loop to output the square numbers from 1 to \`n\`, each on its own line. For 3 that is 1, 4 and 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: The loop counts \`i\` from 1 to \`n\`. The square of \`i\` is \`i * i\`. -tipp: \`for (int i = 1; i <= n; i++) { … }\` and in the body \`System.out.println(i * i);\`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Fill in the gaps: for - -::: lückentext -titel: Counting up and down -auftrag: The first loop adds up the numbers from 1 to 5, the second counts down from 10 to 0. Fill in the missing words and symbols. -\`\`\`java -int sum = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - sum = sum + i; -} -System.out.println("Sum: " + sum); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -\`\`\` -erklärung: The round brackets hold the start value, the condition and the step, separated by semicolons. \`i++\` counts up, \`i--\` counts down. When counting down to 0 the condition is \`i >= 0\`. -::: - -## Find the bug: times table - -::: übung l13-java-fehler -titel: Fix the times table -code: java -auftrag: The program asks for a times table and should output it from 1 times to 10 times, for 7 that is 7, 14, … up to 70. But the 70 is missing. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: How often does the loop run? Count which values \`i\` takes. -tipp: \`i\` should also take the value 10: \`i <= 10\` instead of \`i < 10\`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l13-java-laeuft-nicht -titel: Fix the stairs -code: java -auftrag: Ludwig climbs 5 steps, each 20 cm high. The program should output **Height: 100 cm**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Height: 100 cm -braucht: forSchleife -tipp: \`height\` is used in a calculation, but where is the variable created? And at what height does Ludwig start? -tipp: Create \`height\` before the loop: \`int height = 0;\` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program hides **exactly one mistake**, and the bomb is ticking. Read what the program should do and tap the wrong line before time runs out. If the parts of a line have a border, tap exactly the wrong part, for example just \`i++\`. Eight rounds, 25 seconds each. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Should output 1, 2 and 3. -fehler: 2 -besser: System.out.println(i); -> The semicolon at the end of the statement is missing. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Should output 1 to 5. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> The three fields of the for loop are separated by semicolons, not commas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Should count backwards from 10 to 1. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> With \`i++\`, \`i\` keeps growing and the condition \`i >= 1\` stays true: an endless loop. To count backwards, use \`i--\`. - -runde: - int sum = 0; - for (int i = 1; i <= 4; i++) { - sum = i; - } - System.out.println(sum); -frage: Should output the sum 1 + 2 + 3 + 4 = 10. -fehler: 3 -stelle: i -besser: sum = sum + i; -> \`sum = i\` overwrites the sum in every pass. In the end it only holds 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Baaa"); - } -frage: Should output Baaa exactly five times. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> From 0 up to and including 5 there are six passes. If you start at 0, use \`<\`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Should output 1, 2 and 3. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> Java tells upper and lower case apart: \`System\` is written with a capital S. - -runde: - int number = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(number + " x " + i + " = " + number * number); - } -frage: Should output the 7 times table: 7 x 1 = 7, 7 x 2 = 14 and so on. -fehler: 3 -stelle: number * number -besser: System.out.println(number + " x " + i + " = " + number * i); -> \`number * number\` is always 49. It has to be \`number * i\`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Should output the even numbers 2, 4, 6, 8 and 10. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> \`i + 2\` only calculates and stores nothing. For \`i\` to really grow, you need \`i = i + 2\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the three parts Init, Condition and Step of the for loop. → Counting with a fixed plan -- I can explain in which order the three parts run. → In which order? -- I can count forwards, backwards and in bigger steps with a for loop. → Other steps -- I can write a for loop in Java and explain its three fields. → Now in Java -::: - -## Quiz - -::: quiz -? How often does the Init of a for loop run? -→ In which order? -- [x] Exactly once -- [ ] Before every pass -- [ ] After every pass -> Init creates the counter variable once at the start. - -? Init \`int i = 0\`, Condition \`i < 3\`, Step \`i++\`. Which values does i have inside the loop? -→ Counting with a fixed plan -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> With i = 3 the condition is false, so the inside does not run any more. - -? Init \`int i = 2\`, Condition \`i <= 10\`, Step \`i = i + 2\`. How often does the inside run? -→ Other steps -- [ ] 4 times -- [x] 5 times -- [ ] 10 times -> i takes the values 2, 4, 6, 8 and 10. - -? When does a for loop fit best? -→ Counting with a fixed plan -- [x] When you know beforehand how often to repeat -- [ ] When you keep asking until an input is valid -- [ ] When only a single value is checked - -? What does \`i++\` mean in a for loop? -→ Counting with a fixed plan -- [ ] i is doubled. -- [x] i gets 1 bigger. -- [ ] i is output. -> \`i++\` is short for \`i = i + 1\`. -::: -`,Gw=`--- -titel: Nesting -bereich: Basics -kurz: Combine branches inside loops and loops inside loops. ---- - -## Skills I will gain now - -::: kompetenzen -- put a branch inside a loop -- put a loop inside another loop -- explain that the inner loop runs all the way through in every pass of the outer one -- write nested loops and branches in Java -::: - -## Building blocks inside each other - -You now know all the important building blocks: sequence, branch, switch and three kinds of loops. They only get really powerful together. - -Every path and every loop body is a sequence itself. You may put any element there, including more branches and loops. This is called **nesting**. - -> To do this, simply drag an element onto the placeholder ø inside a loop or a path. - -## A branch inside a loop - -The loop goes through all the numbers, and the branch decides for each single number whether something happens. That way you can count, for example, how many even numbers there are from 1 to n. - -::: diagramm l13-gerade-zaehlen.visustruct -In every pass the branch checks with i % 2 == 0 whether i is even. Only then is count increased by 1. The false path stays empty. -::: - -With n = 7 these are the numbers 2, 4 and 6, so 3 is output. - -## Exercise: Count even inputs - -::: übung l13-gerade-eingaben -titel: Count even inputs -auftrag: Ask for exactly **five** whole numbers, one after another. Then output how many of them were even. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Doing the same thing five times means: loop. In every pass a number is read in and checked. -tipp: A for loop from 1 to 5. Inside, first comes the input \`input: int number "Enter a number"\`, below it a branch with \`number % 2 == 0\`. In the true path you increase \`count\` by 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## A loop inside a loop - -If one loop sits inside another, the **inner loop runs all the way through in every pass of the outer one**. With this you can build patterns of rows and columns. - -::: diagramm l13-dreieck.visustruct -The outer loop counts the lines. The inner one adds as many stars to the text as the line number says. With n = 3 you get a triangle of 1, 2 and 3 stars. -::: - -With \`String text = ""\` every line starts empty. \`text = text + "*"\` adds a star. Only when the line is finished is it output. - -Experienced programmers give the counter variables the short names \`i\` and \`j\`: \`i\` outside, \`j\` inside. Then it looks like this, here with a small times table: - -::: grafik l13-verschachtelt-tabelle.svg animiert -The outer loop counts the lines with \`i\`, the inner one counts the numbers in a line with \`j\`. On every new line, \`j\` starts again at 1. -::: - -## Exercise: Rectangle - -::: übung l13-rechteck -titel: Rectangle -auftrag: Ask for a width and a height. Output a rectangle made of \`#\`: as many lines as the height, and in each line as many \`#\` as the width. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: You need two loops inside each other: one for the lines and inside it one for the characters of a line. -tipp: Build it like the triangle. The difference: the inner loop always counts up to \`width\`, not up to the line number. The outer loop counts up to \`height\`. -tipp: The outline: inputs \`width\` and \`height\`. Outer for loop \`int line = 1\`, \`line <= height\`, \`line++\`. Inside it first \`String text = ""\`, then the inner for loop \`int column = 1\`, \`column <= width\`, \`column++\` with \`text = text + "#"\`, and below the inner loop \`output: text\`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Keep track - -With nested diagrams, the drawing helps you: whatever is further inside is indented and surrounded by its loop or path. - -- Read from the outside in: what repeats? What is decided in every pass? -- Give counter variables meaningful names such as \`line\` and \`column\`. Two loops must not change the same counter variable. -- Go through an example in the simulation with **Step** and watch the variables. - -::: angeberwissen -This is exactly what the structure chart was invented for. Shneiderman asked himself: if we are supposed to do without GOTO, why not do without the arrows in the flowchart as well? The result: no arrow spaghetti, and every block stays nicely in its pen. -::: - -## Now in Java - -Counting the even numbers looks like this in Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Up to which number?"); -int n = scanner.nextInt(); -int count = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - count++; - } // end if -} // end for -System.out.println(count); -\`\`\` - -What is nested in the structure chart is nested in curly brackets in Java. Every level is indented four spaces further. If your code gets messy, the **Indent** button at the top of the Code area helps. - -With many closing brackets \`}\` it is easy to lose track: which bracket belongs to which block? A comment after it tells you, for example \`} // end if\` and \`} // end for\`. This also helps when you carry on later. - -\`count++\` is short for \`count = count + 1\`. - -::: übung l14-java-treppe -titel: Stairs of stars -code: java -auftrag: Write a Java program that reads the number of steps and outputs stairs made of stars. For 3 steps that is \`*\`, \`**\` and \`***\`, each step on its own line. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: An outer loop counts the steps. In each step an inner loop builds a text of stars. -tipp: In the outer loop: \`String line = "";\`, then \`for (int j = 1; j <= i; j++) { line = line + "*"; }\` and after that \`System.out.println(line);\`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Fill in the gaps: nesting - -::: lückentext -titel: Rectangle of stars -auftrag: The program outputs a rectangle of 3 lines with 4 stars each. Fill in the missing words and symbols. -\`\`\`java -for (int line = 1; line <= 3; line++) { - String text = [[""]]; - [[for]] (int column = 1; column [[<=]] 4; [[column++ | column += 1 | column = column + 1]]) { - text = text [[+]] "*"; - } - System.out.[[println]](text); -[[}]] -\`\`\` -erklärung: The inner loop builds one line of 4 stars, the outer loop repeats that three times. Each line starts with an empty text \`""\`. The last bracket closes the outer loop. -::: - -## Find the bug: divisible by 3 - -::: übung l14-java-fehler -titel: Fix the counting -code: java -auftrag: The program should count how many numbers from 1 to \`n\` are divisible by 3 and output the result **once**. For 10 that is just **Count: 3**. But it outputs far too many lines. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l14-java-fehler-start.visustruct -test: 10 -> Count: 3 -test: 3 -> Count: 1 -test: 2 -> Count: 0 -braucht: forSchleife, verzweigung -tipp: Look at the curly brackets: is the output inside the loop or after it? -tipp: Move the line \`System.out.println("Count: " + count);\` after the closing bracket of the loop. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l14-java-laeuft-nicht -titel: Fix the even numbers -code: java -auftrag: The program should output the even numbers from 1 to 10: **2**, **4**, **6**, **8**, **10**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: The bug is right inside, in the output. What is the loop’s counter variable called? -tipp: The counter variable is \`i\` with a small i: \`System.out.println(i);\` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Inside and outside - -::: spiel frage -titel: Game: Inside and outside -intro: Ludwig has put building blocks inside each other. Read from the outside in and predict what happens. Six rounds. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: How many stars are output? -- 3 -- 5 -- [x] 6 -> The inner loop runs completely in each of the 3 runs of the outer loop: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Which line is output fourth? -- 1 4 -- [x] 2 1 -- 2 2 -> First j runs through for i = 1: 1 1, 1 2, 1 3. Then j starts again at 1, now with i = 2. - -runde: - int count = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - count++; - } - } - System.out.println(count); -frage: What is output? -- [x] 3 -- 10 -- 4 -> The branch only counts 3, 6 and 9. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("even"); - } else { - System.out.println(i); - } - } -frage: What is output? -- 1\\n2\\n3\\n4 -- [x] 1\\neven\\n3\\neven -- even\\neven -> For every number the branch decides again: odd numbers themselves, even ones as a word. - -runde: - for (int i = 1; i <= 3; i++) { - String line = ""; - for (int j = 1; j <= i; j++) { - line = line + "*"; - } - System.out.println(line); - } -frage: What is output? -- ***\\n***\\n*** -- [x] *\\n**\\n*** -- ***\\n**\\n* -> The inner loop runs up to i. That's why every line gets one star longer. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: What happens? -- 1 to 3 is output three times. -- 1 to 3 is output once. -- [x] Java reports an error. -> Two loops inside each other need two different counter variables, for example \`row\` and \`column\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can put a branch inside a loop. → A branch inside a loop -- I can put a loop inside another loop. → A loop inside a loop -- I can explain that the inner loop runs all the way through in every pass of the outer one. → A loop inside a loop -- I can write nested loops and branches in Java. → Now in Java -::: - -## Quiz - -::: quiz -? The outer loop runs 3 times, the inner one 4 times. How often does the inside of the inner loop run in total? -→ A loop inside a loop -- [ ] 7 times -- [x] 12 times -- [ ] 4 times -> In each of the 3 outer passes, the inner loop runs 4 times, so 3 · 4 = 12. - -? How can you tell whether a number is even? -→ A branch inside a loop -- [ ] \`number / 2 == 0\` -- [x] \`number % 2 == 0\` -- [ ] \`number == 2\` -> % gives the remainder of a division. For even numbers, the remainder when dividing by 2 is zero. - -? There is a branch inside a for loop. How often is the condition of the branch checked? -→ A branch inside a loop -- [ ] Once -- [x] Once per pass of the loop -- [ ] Only if it is true -> The branch is part of the inside and runs in every pass. - -? How can you tell in Java what belongs to a loop? -→ Now in Java -- [ ] By the colour -- [x] By the curly brackets and the indentation -- [ ] By the semicolons -::: -`,Hw=`--- -titel: Arrays -bereich: Basics -kurz: Store many values under one name, reach them with the index and go through them with a loop. ---- - -## Skills I will gain now - -::: kompetenzen -- create an array with values or with \`new int[5]\` -- explain that the index starts at 0 and that the last index is \`length - 1\` -- go through and fill an array with a for loop -- calculate the sum and the maximum of an array -- create an array in Java and go through it with a loop -::: - -## Many values under one name - -Imagine you want to store the points from five tests. With five variables \`points1\` to \`points5\` that quickly gets confusing. An **array** is a row of slots that all have the same name and the same data type. - -You create an array with a statement. After the data type you write square brackets \`[]\`: - -\`\`\` -int[] numbers = {2, 4, 6, 8, 10} -String[] names = {"Ada", "Ben", "Cem"} -\`\`\` - -The values in the curly brackets go straight into the slots. You can also create an empty array with a fixed number of slots: - -\`\`\` -int[] points = new int[5] -String[] names = new String[3] -\`\`\` - -With \`new int[5]\` all five slots hold zeros at first, with \`new String[3]\` empty texts. - -## The index starts at 0 - -Every slot has a number, the **index**. Counting starts at **0**. So with \`int[] numbers = {2, 4, 6, 8, 10}\`: - -- \`numbers[0]\` is 2 -- \`numbers[1]\` is 4 -- \`numbers[4]\` is 10 - -::: grafik l14-array.svg -Five slots with the indices 0 to 4. The index is above the slot, the value inside it. -::: - -**Why start at 0?** In computer science the index is not “which slot” but the **distance from the start**. The first slot is right at the start, so 0 steps away. The second is 1 step away, the third 2 steps. You know this from a lift: the ground floor is **0**, and the first floor is one level above it. - -This gives you a rule worth remembering: with 5 slots the **last index is 4**, always one less than the number of slots. - -::: angeberwissen -Counting from 0 is pretty old. The programming language C from 1972 already works like this: the computer knows the address of the first slot, and for \`numbers[i]\` it simply calculates “start plus i slots further”. With i = 0 it stays at the start, which saves a calculation. Java, Python and JavaScript took this over. By the way, I count my horns from 0 as well: horn 0 on the left, horn 1 on the right. -::: - -With the index you read a value or write a new one into it: - -\`\`\` -output: numbers[0] -numbers[1] = 99 -\`\`\` - -> An array with 5 slots has the indices 0 to 4. \`numbers[5]\` does not exist. The simulation then reports an invalid expression. - -## The length of an array - -\`numbers.length\` tells you how many slots an array has. With five values it is 5. So the **last** index is always \`numbers.length - 1\`: - -\`\`\` -output: numbers.length -output: numbers[numbers.length - 1] -\`\`\` - -The second line outputs the last value, however long the array is. VisuStruct also understands \`numbers.length()\`. - -::: übung l14-erster-letzter -titel: First and last value -auftrag: Create the array \`int[] values = {4, 8, 15, 16, 23, 42}\`. Then output on three lines: the first value, the last value and the number of values. - For the last value, use the length and do not simply write the index 5. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Counting starts at 0, so the first slot has the index 0. And what is the index of the last slot? -tipp: The first value is \`values[0]\`. The last one is \`values[values.length - 1]\`. The number of values is \`values.length\`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Go through an array with a loop - -An array shows its strength together with a **for loop**. The counter variable \`i\` runs from 0 to the last index and is used as the index: - -\`\`\` -int i = 0 -i < numbers.length -i++ -\`\`\` - -In the first pass \`i\` is 0, in the last one 4. Because the condition is \`i < numbers.length\`, the loop fits any length. - -::: diagramm l14-array-durchlaufen.visustruct -The for loop outputs all five values one after another. Under Variables in the simulation, watch i count from 0 to 4. -::: - -This is also how you fill an empty array with inputs. As the target of the input you write the slot \`names[i]\`: - -::: diagramm l14-namen-eingeben.visustruct -Three names are asked for and written into the slots 0, 1 and 2. -::: - -::: übung l14-rueckwaerts -titel: Print backwards -auftrag: Create an array for 4 whole numbers and read in the 4 numbers with a loop. Then output them in **reverse** order, each on its own line. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: You need two loops: one for reading in from front to back and one for outputting from back to front. -tipp: You create the array with \`int[] numbers = new int[4]\`. The input is \`input: numbers[i] "Enter a number"\`. For the second loop you start at \`int i = numbers.length - 1\`, check \`i >= 0\` and count down with \`i--\`. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr** from Vienna was a famous Hollywood film star in the 1940s. In her spare time she liked to invent things. In 1942, together with the composer George Antheil, she received a patent for a radio link that an enemy should not be able to jam: sender and receiver jump together through a list of **88 frequencies**, as many as a piano has keys. The list was punched into a paper strip like the roll of a self-playing piano. Both go through the same list in the same order, just like your loop goes through an array with \`i\`. Anyone who listens to or jams only one frequency gets almost nothing. - -This method, **frequency hopping**, is used today in Bluetooth, for example. She did not invent it all by herself; there were earlier ideas. But her patent is seen as an important forerunner, and in 1997 she received an award for it. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), actress and inventor, 1944 (publicity photo: [unknown](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), public domain). -::: - -## Sum and maximum - -Two tasks come up with arrays again and again: - -- **Sum:** You start with \`sum = 0\` and add every value in the loop. -- **Maximum:** First you remember the value in slot 0 as the largest so far. In the loop you check every value. If it is larger, it becomes the new maximum. - -::: diagramm l14-summe-maximum.visustruct -Sum and maximum in one single pass. The maximum starts with points[0] and not with 0, so that it is also right for negative numbers. -::: - -::: übung l14-summe-max -titel: Sum and maximum -auftrag: Read 5 whole numbers into an array. Then output **Sum: …** and on the next line **Maximum: …**. -test: 3, 9, 2, 9, 1 -> Sum: 24 | Maximum: 9 -test: 10, 20, 30, 40, 50 -> Sum: 150 | Maximum: 50 -test: -5, -2, -9, -1, -3 -> Sum: -20 | Maximum: -1 -braucht: forSchleife, verzweigung -tipp: Go through the array with a loop. Add every value to the sum and remember the largest value seen so far. -tipp: Calculate the sum and the maximum in variables of their own and only output them at the end, e.g. with \`output: "Sum: " + sum\`. If your maximum starts at 0, the third test fails. -tipp: The outline: create the array and read it in with a loop. Then \`int sum = 0\` and \`int max = numbers[0]\`. A second loop calculates \`sum = sum + numbers[i]\`, and a branch \`numbers[i] > max\` sets \`max = numbers[i]\` in its true branch. Finally the two outputs. -lösung: l14-summe-max-loesung.visustruct -::: - -## Now in Java - -Going through an array looks like this in Java: - -\`\`\`java editor -int[] numbers = {2, 4, 6, 8, 10}; -for (int i = 0; i < numbers.length; i++) { - System.out.println(numbers[i]); -} -\`\`\` - -- \`int[]\` means “array of whole numbers”. The values are in curly brackets. -- You create an empty array with 5 slots with \`new int[5]\`. -- \`numbers[i]\` is the slot with index \`i\`, \`numbers.length\` is the length. - -If you output the whole array, that is \`System.out.println(numbers);\`, Java does not show the numbers. You only see a text like \`[I@1b6d3586\`. This is the **reference**, the place where the array is stored in memory. \`Arrays.toString(numbers)\` shows the content: \`[2, 4, 6, 8, 10]\`. For this you write \`import java.util.Arrays;\` at the very top. - -::: übung l15-java-noten -titel: Sum of grades in Java -code: java -auftrag: Create the array \`grades\` in Java with the values 2, 3, 1 and 4. Add up all grades with a for loop and output **Sum: 10**. -test: - -> Sum: 10 -braucht: forSchleife -tipp: Create \`int sum = 0;\` before the loop. The loop runs from 0 to \`grades.length - 1\`. -tipp: \`int[] grades = {2, 3, 1, 4};\`, then \`for (int i = 0; i < grades.length; i++) { sum = sum + grades[i]; }\` and finally \`System.out.println("Sum: " + sum);\`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Fill in the gaps: arrays - -::: lückentext -titel: Finding the maximum -auftrag: The program looks for the largest value in the array. Fill in the missing words and symbols. -\`\`\`java -[[int]][] values = {3, 7, 1, 9, 4}; -int max = values[0]; -[[for]] (int i = 1; i < values.[[length]]; i++) { - if (values[i] [[>]] max) { - max = values[i]; - } -} -System.out.println("Maximum: " + max); -\`\`\` -erklärung: \`int[]\` is an array of whole numbers, \`values.length\` is its length. \`max\` starts with the first value, and the loop compares all the others with it. Output: **Maximum: 9** -::: - -## Find the bug: temperatures - -::: übung l15-java-fehler -titel: Fix the highest temperature -code: java -auftrag: In January the temperatures -5, -2, -8 and -3 degrees were measured. The program should output the highest, so **Highest temperature: -2**. But it claims 0 degrees, although it was never that warm. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l15-java-fehler-start.visustruct -test: - -> Highest temperature: -2 -braucht: forSchleife, verzweigung -tipp: What value does \`max\` start with? Is any of the temperatures greater than this start value? -tipp: \`max\` has to start with a value from the array: \`int max = temperatures[0];\` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l15-java-laeuft-nicht -titel: Add up the points -code: java -auftrag: The array \`points\` holds 4, 7, 2 and 5. The program should add them all up and output **Sum: 18**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Sum: 18 -braucht: forSchleife -tipp: The Code area underlines the wrong word in red. How do you write the length of an array? -tipp: \`length\` is written all in small letters: \`i < points.length\` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake** to do with arrays, and the bomb is ticking. Tap the wrong line before time runs out. If the parts of a line have a border, tap exactly the wrong part. Six rounds, 30 seconds each. -zeit: 30 - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - for (int i = 0; i <= numbers.length; i++) { - System.out.println(numbers[i]); - } -frage: Should output all five values. -fehler: 2 -stelle: i <= numbers.length -besser: for (int i = 0; i < numbers.length; i++) { -> With \`<=\`, i runs up to 5. There is no slot \`numbers[5]\`. - -runde: - String[] names = {"Ada", "Ben", "Cem"}; - System.out.println(names[1]); -frage: Should output the first name, Ada. -fehler: 2 -stelle: names[1] -besser: System.out.println(names[0]); -> Counting starts at 0. \`names[1]\` is already Ben. - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - System.out.println(numbers[numbers.length]); -frage: Should output the last value, 10. -fehler: 2 -stelle: numbers[numbers.length] -besser: System.out.println(numbers[numbers.length - 1]); -> The last index is always one less than the length. - -runde: - int[] points = {3, 9, 2}; - int sum = 0; - for (int i = 0; i < points.length; i++) { - sum = points[i]; - } - System.out.println(sum); -frage: Should output the sum 14. -fehler: 4 -stelle: points[i] -besser: sum = sum + points[i]; -> Like this every value overwrites the one before, and at the end sum only holds 2. - -runde: - int[] points = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < points.length; i++) { - if (points[i] > max) { - max = points[i]; - } - } - System.out.println(max); -frage: Should output the maximum -2. -fehler: 2 -stelle: 0 -besser: int max = points[0]; -> None of the numbers is greater than 0, so max would stay 0. That's why the maximum starts with the first slot. - -runde: - int[] numbers = {2, 4, 6, 8, 10}; - for (int i = numbers.length; i >= 0; i--) { - System.out.println(numbers[i]); - } -frage: Should output the values backwards, from 10 to 2. -fehler: 2 -stelle: int i = numbers.length -besser: for (int i = numbers.length - 1; i >= 0; i--) { -> Backwards you start at the last index, and that is \`numbers.length - 1\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create an array with values or with \`new int[5]\`. → Many values under one name -- I can explain that the index starts at 0 and that the last index is \`length - 1\`. → The index starts at 0 -- I can go through and fill an array with a for loop. → Go through an array with a loop -- I can calculate the sum and the maximum of an array. → Sum and maximum -- I can create an array in Java and go through it with a loop. → Now in Java -::: - -## Quiz - -::: quiz -? What does \`output: numbers[2]\` output with \`int[] numbers = {5, 10, 15, 20}\`? -→ The index starts at 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Counting starts at 0: numbers[0] is 5, numbers[1] is 10, numbers[2] is 15. - -? An array has 8 slots. Which index is the last one? -→ The index starts at 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> The last index is always length - 1. - -? Which condition fits a for loop that goes through all values of \`numbers\` and starts at \`int i = 0\`? -→ Go through an array with a loop -- [x] i < numbers.length -- [ ] i <= numbers.length -- [ ] i > numbers.length -> With <= the loop would also read numbers[length], and that slot does not exist. - -? What should the maximum start with before the loop begins? -→ Sum and maximum -- [ ] Always with 0 -- [x] With the first value of the array -- [ ] With the length of the array -> If it starts with 0, then with only negative numbers the result would be 0, although 0 is not in the array at all. - -? What does \`output: numbers.length\` output with \`int[] numbers = {5, 10, 15, 20}\`? -→ Many values under one name -- [ ] 3 -- [x] 4 -- [ ] 20 -> length is the number of slots, not the last index and not the last value. - -? Which line creates an array with three numbers in Java? -→ Now in Java -- [x] int[] numbers = {1, 2, 3}; -- [ ] int numbers = [1, 2, 3]; -- [ ] array numbers = (1, 2, 3); -::: -`,Vw=`--- -titel: Two-dimensional arrays -bereich: Basics -kurz: Store values in rows and columns, like the seats in a cinema, and go through them with two nested loops. ---- - -## Skills I will gain now - -::: kompetenzen -- create a 2D array and explain what the two numbers mean -- read and write a slot with a row and a seat -- tell \`seats.length\` and \`seats[0].length\` apart -- explain in which order two nested loops visit the slots -- create and go through a two-dimensional array in Java -::: - -## A cinema as a table - -A cinema has several **rows**, and every row has several **seats**. For every seat we want to remember whether its ticket has already been sold. That is a yes-or-no question, so the data type \`boolean\` fits: \`true\` means sold, \`false\` means free. - -A normal array is one single row of slots. For the cinema we need a **table** with rows and columns. That is a **two-dimensional array**, 2D array for short. You write two pairs of square brackets for it: - -\`\`\` -boolean[][] seats = new boolean[3][5] -\`\`\` - -That gives 3 rows with 5 seats each, so 15 slots. At the start every slot holds \`false\`: all seats are free. - -::: grafik kino-saal.svg -The first index is the row, the second the seat. Both are counted from 0. -::: - -## Reach a slot - -For a slot you now need **two** indices: first the row, then the seat. - -\`\`\` -seats[1][2] = true -\`\`\` - -Now seat 2 in row 1 is sold. Reading works the same way: \`seats[1][2]\` is now \`true\`. - -You can also ask for the size: - -- \`seats.length\` is the number of **rows**, here 3. -- \`seats[0].length\` is the number of **seats** in row 0, here 5. - -::: diagramm kino-verkaufen.visustruct -Three tickets are sold. After that, the diagram outputs the size of the cinema and whether row 1, seat 2 is sold. Watch under Variables in the simulation: seats is shown row by row. -::: - -> Just like with a normal array, there is no slot \`seats[3][0]\`, because the rows are called 0, 1 and 2. The simulation then reports an invalid expression. - -## Two loops inside each other - -To look at **all** seats, you need two for loops inside each other: - -- The **outer loop** counts the row: \`row\` runs from 0 to 2. -- The **inner loop** counts the seat: \`seat\` runs from 0 to 4. - -::: grafik kino-schleifen.svg -Blue is the outer loop, orange the inner one. The numbers show the order in which the seats come up. -::: - -For every row, the inner loop runs **all the way** through once. Only when \`seat\` has visited all five seats does the outer loop move \`row\` on by one. Then \`seat\` starts at 0 again. That way all 3 · 5 = 15 seats come up exactly once. - -The conditions are \`row < seats.length\` and \`seat < seats[row].length\`. That way the loops fit a cinema of any size. - -::: diagramm kino-saalplan.visustruct -The seating plan: for every row, a line of X (sold) and O (free) is put together and output. Go through it with Step and watch how seat keeps starting again from the beginning while row only slowly counts on. -::: - -::: angeberwissen -Every phone photo is a 2D array! A photo with 4000 · 3000 pixels has 3000 rows with 4000 slots each, so 12 million pixels. Every slot holds the colour of one dot. When you make a photo brighter, the phone runs over all the pixels with two nested loops. My profile picture, by the way, has only one slot: brown. -::: - -## Exercise: Count free seats - -::: übung kino-frei -titel: Count free seats -auftrag: In the start diagram three tickets have already been sold. Use two nested loops to count how many seats are still **free**, and output e.g. \`Free: 12\`. -start: kino-frei-start.visustruct -test: - -> Free: 12 -braucht: forSchleife, verzweigung -tipp: Just like for the seating plan, you go over all seats with two loops. Instead of outputting something, you count. -tipp: Before the loops, create \`int free = 0\`. A seat is free if \`seats[row][seat]\` is false. The condition for that is \`!seats[row][seat]\`. -lösung: kino-frei-loesung.visustruct -::: - -## Exercise: Sell a ticket - -::: übung kino-karte -titel: Sell a ticket -auftrag: The start diagram asks for a row and a seat. If the seat is already sold, output \`Already sold\`. Otherwise mark it as sold and output e.g. \`Sold: row 0, seat 4\`. -start: kino-karte-start.visustruct -test: 1, 2 -> Already sold -test: 0, 4 -> Sold: row 0, seat 4 -test: 2, 0 -> Sold: row 2, seat 0 -braucht: verzweigung -tipp: First check whether the seat is sold. Depending on the answer, something different happens. -tipp: You do not need a loop here. A branch with the condition \`seats[row][seat]\` is enough. In the false path you set the slot to \`true\`. -lösung: kino-karte-loesung.visustruct -::: - -## Exercise: Times table grid - -::: übung kino-einmaleins -titel: Times table grid -auftrag: Create \`int[][] table = new int[10][10]\` and fill it with the times tables up to 10 using two loops. Then ask for two numbers from 1 to 10 and read the result from the table, e.g. \`3 · 4 = 12\`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: First two nested loops fill the table; after that you read only one single slot. -tipp: The indices run from 0 to 9, but the numbers from 1 to 10. That is why the slot \`table[i][j]\` holds the value \`(i + 1) * (j + 1)\`, and for 3 · 4 you read \`table[a - 1][b - 1]\`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Now in Java - -The cinema looks like this in Java: - -\`\`\`java editor -boolean[][] seats = new boolean[3][5]; -seats[0][2] = true; -seats[1][1] = true; -seats[1][2] = true; -System.out.println("Rows: " + seats.length); -System.out.println("Seats per row: " + seats[0].length); -System.out.println("Row 1, seat 2 sold? " + seats[1][2]); -\`\`\` - -- Two pairs of square brackets \`[][]\` stand for a table: first the row, then the seat. -- \`seats.length\` is the number of rows, \`seats[0].length\` the number of seats in one row. -- With fixed values you write every row in its own curly brackets: \`int[][] t = {{1, 2}, {3, 4}};\` - -::: übung l16-java-punkte -titel: Add up the points -code: java -auftrag: Three players have played two rounds each. Create the table \`points\` in Java with the rows {3, 5}, {4, 1} and {2, 2}. Add up all points with two nested for loops and output **Sum: 17**. -test: - -> Sum: 17 -braucht: forSchleife -tipp: The outer loop goes through the rows, the inner one through the seats of a row. -tipp: \`int[][] points = {{3, 5}, {4, 1}, {2, 2}};\` and in the inner loop \`sum = sum + points[i][j];\`. The inner loop runs up to \`points[i].length\`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Fill in the gaps: tables - -::: lückentext -titel: Adding up all fields -auftrag: The program adds up all numbers in the table. Fill in the missing words and symbols. -\`\`\`java -[[int]][][] grid = {{1, 2, 3}, {4, 5, 6}}; -int sum = 0; -for (int r = 0; r < grid.[[length]]; r++) { - for (int c = 0; c < grid[r].[[length]]; c++) { - sum = sum [[+]] grid[r][c]; - } -} -System.out.println("Sum: " + sum); -\`\`\` -erklärung: \`int[][]\` is a table. \`grid.length\` is the number of rows (2), \`grid[r].length\` the number of fields in row \`r\` (3). Output: **Sum: 21** -::: - -## Find the bug: points - -::: übung l16-java-fehler -titel: Fix player 2 -code: java -auftrag: In the table \`points\` each row stands for one player. The program should add up the points of player 2 (row 1), so **Player 2: 11**. Instead it reports an error. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l16-java-fehler-start.visustruct -test: - -> Player 2: 11 -braucht: forSchleife -tipp: In \`points[a][b]\`, \`a\` is the row and \`b\` the place in the row. Which number stands for the row of player 2? -tipp: The indices are swapped: \`points[1][j]\` instead of \`points[j][1]\`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l16-java-laeuft-nicht -titel: Read the score -code: java -auftrag: In the table \`points\` each row is a player and each column a round. The program should output player 2’s points in round 1, so **Player 2, round 1: 4**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Player 2, round 1: 4 -braucht: anweisung -tipp: The cells \`[1][0]\` are right. Look at the name of the table. -tipp: The table is called \`points\` with a small p: \`points[1][0]\` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Which seat? - -::: spiel frage -titel: Game: Which seat? -intro: Ludwig is selling cinema tickets. First the row, then the seat, both from 0. Seven rounds. - -runde: - boolean[][] seats = new boolean[3][5]; - System.out.println(seats.length); -frage: What is output? -- [x] 3 -- 5 -- 15 -> \`seats.length\` is the number of rows. - -runde: - boolean[][] seats = new boolean[3][5]; - System.out.println(seats[0].length); -frage: What is output? -- 3 -- [x] 5 -- 15 -> \`seats[0]\` is row 0. Its length is the number of seats in this row. - -runde: - int[][] table = new int[4][6]; -frage: How many slots does the table have? -- 10 -- [x] 24 -- 35 -> 4 rows with 6 seats each: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: What is output? -- 2 -- [x] 3 -- 4 -> Row 1 is \`{3, 4}\`, and seat 0 in it is the 3. - -runde: - boolean[][] seats = new boolean[3][5]; - seats[3][0] = true; -frage: What happens? -- The first seat in row 3 is sold. -- The first seat in the last row is sold. -- [x] There is an error, because the rows are called 0, 1 and 2. -> Just like with a normal array, the last index is one less than the length. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int sum = 0; - for (int row = 0; row < t.length; row++) { - for (int seat = 0; seat < t[row].length; seat++) { - sum = sum + t[row][seat]; - } - } - System.out.println(sum); -frage: What is output? -- 6 -- 15 -- [x] 21 -> The two loops visit all six slots: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Two loops go through the cinema \`new boolean[3][5]\`. Which slot comes right after \`seats[0][4]\`? -- \`seats[0][5]\` -- [x] \`seats[1][0]\` -- \`seats[1][4]\` -> Row 0 is done. The outer loop counts on, and the seat starts again at 0. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a 2D array and explain what the two numbers mean. → A cinema as a table -- I can read and write a slot with a row and a seat. → Reach a slot -- I can tell \`seats.length\` and \`seats[0].length\` apart. → Reach a slot -- I can explain in which order two nested loops visit the slots. → Two loops inside each other -- I can create and go through a two-dimensional array in Java. → Now in Java -::: - -## Quiz - -::: quiz -? How many slots does \`boolean[][] seats = new boolean[4][6]\` have? -→ A cinema as a table -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 rows with 6 seats each: 4 · 6 = 24. - -? What is in all slots after \`new boolean[3][5]\`? -→ A cinema as a table -- [ ] true -- [x] false -- [ ] Nothing, the slots are empty -> A new boolean array is filled with false. In the cinema that means: all seats are free. - -? Two nested loops run over 3 rows with 5 seats. How often does the inside of the **inner** loop run? -→ Two loops inside each other -- [ ] 3 times -- [ ] 8 times -- [x] 15 times -> For each of the 3 rows the inner loop runs 5 times: 3 · 5 = 15. - -? Match what each expression means in the cinema. -→ Reach a slot -= seats.length -> Number of rows -= seats[0].length -> Number of seats in row 0 -= seats[2][4] -> Row 2, seat 4 -= seats[1][0] = true -> Seat 0 in row 1 is sold - -? What does \`seats.length\` give for \`new boolean[3][5]\`? -→ Reach a slot -- [x] 3, the number of rows -- [ ] 5, the number of seats -- [ ] 15, all seats together -::: -`,qw=`--- -titel: Strings -bereich: Basics -kurz: Work with text: read single characters, cut out parts, search, compare and split. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the characters of a String are also counted from 0 -- work with \`length()\` and \`charAt(i)\` -- tell a \`char\` from a String and use the character code with \`(int)\` and \`(char)\` -- cut a part out of a text with \`substring\` -- use \`indexOf\`, \`contains\`, \`toUpperCase\` and \`equals\` -- go through a text character by character with a loop -- split a text into a String array with \`split\` -- read a text in Java and compare it with \`equals\` -- use more methods such as \`lastIndexOf\`, \`replace\`, \`compareTo\` and \`Integer.parseInt\` -- split a text from a file into lines with \`split("\\n")\` -- look up a method in the Java API and read its description -::: - -## Text is a row of characters - -You already know \`String\`: \`String name = "Ludwig"\`. In computer science such a text is called a **string**, because the characters hang one after another like beads on a string. Every character has a place with an index, just like in an array. Here, too, **counting starts at 0**: the first character is 0 steps away from the start. - -::: grafik zk-zeichen.svg -The nine characters of “Billygoat” with their indices 0 to 8. -::: - -A String comes with **methods**. You write the name of the variable, a dot and the method: - -- \`word.length()\` gives the number of characters, here 9. -- \`word.charAt(0)\` gives the character with the index 0, here \`'B'\`. - -> With Strings you write \`length()\` **with** brackets, with arrays \`length\` **without**. Sadly, that is just how Java is. - -Just like in an array, the last character has the index \`length() - 1\`. - -## A single character: char - -Java has its own data type for a single character: **char**, short for *character*. A \`char\` goes in **single** quotation marks, a String in double ones: - -- \`'C'\` is a \`char\`: exactly one character. -- \`"C"\` is a String: a text that is only one character long. -- A \`char\` always holds exactly one character. There is no empty \`char\` like \`''\`, but there is an empty String \`""\`. - -\`charAt\` gives you such a character from a text: - -\`\`\` -char first = word.charAt(0) -char last = word.charAt(word.length() - 1) -\`\`\` - -You compare two \`char\` values with \`==\`: \`word.charAt(0) == 'B'\` is \`true\`. - -Behind every character there is a number, its **character code**. Which number belongs to which character is listed in the ASCII table: \`'A'\` has the code 65, \`'B'\` 66, \`'C'\` 67 and so on up to \`'Z'\` with 90. With \`(int)\` you get the code of a character, with \`(char)\` a code turns back into a character: - -\`\`\` -int code = (int) 'C' -code = code + 1 -char next = (char) code -\`\`\` - -After that \`code\` is 68 and \`next\` is \`'D'\`. This is how you shift a letter in the alphabet, and that is exactly what you will need later for the Caesar cipher. - -## Cut out parts - -With \`substring\` you cut a part out of the text: - -- \`word.substring(0, 5)\` gives the characters from index 0 **up to just before** index 5, so \`"Billy"\`. -- \`word.substring(5)\` gives everything from index 5 to the end, so \`"goat"\`. - -The end is **not** included. Handy: \`substring(0, 5)\` gives exactly 5 characters. - -::: diagramm zk-zeichen.visustruct -All the methods from this lesson in one example. Go through it in the simulation and compare every output with the picture. -::: - -## Search and compare - -More methods you often need: - -| Method | Meaning | Example with \`"Billygoat"\` | -|---|---|---| -| \`indexOf("goat")\` | Where does the text start? | 5 | -| \`indexOf("x")\` | not found | -1 | -| \`contains("lly")\` | Does the text appear? | true | -| \`toUpperCase()\` | all capitals | BILLYGOAT | -| \`toLowerCase()\` | all small letters | billygoat | -| \`equals("Billygoat")\` | same text? | true | - -> In Java you compare two Strings with \`equals\`, not with \`==\`. Two single characters (\`char\`), on the other hand, you compare with \`==\`, e.g. \`word.charAt(i) == 'e'\`. - -::: angeberwissen -Try \`"Billygoat".equals("billygoat")\`. The answer is false! For a computer, B and b are completely different characters. Inside, every character is stored as a number: B is 66 and b is 98. This numbering is called ASCII and dates from 1963. Baah, so for a computer Ludwig and ludwig are two different goats. I am the one with the capital L. -::: - -## Character by character - -With a for loop you go through a text character by character. The index \`i\` runs from 0 to \`word.length() - 1\`, just like with an array: - -::: diagramm zk-zaehlen.visustruct -Counts how often the letter e appears. In the simulation, try “cheese” and watch i and count. -::: - -## Split with split - -Often several pieces of information sit in one single line, separated by a character, e.g. \`"Ludwig;billy goat;7"\`. With \`split\` you cut the line apart at this character. The result is a **String array**: - -\`\`\` -String[] parts = line.split(";") -\`\`\` - -After that, \`parts[0]\` is \`"Ludwig"\`, \`parts[1]\` is \`"billy goat"\` and \`parts[2]\` is \`"7"\`. With \`parts.length\` you find out how many parts there are. - -::: diagramm zk-split.visustruct -The line is split at the semicolon, and every part is output with its index. -::: - -> CSV files, which you know from spreadsheets, are built the same way: one line per record, the values separated by a comma or semicolon. - -## Exercise: Initials - -::: übung zk-initialen -titel: Initials -auftrag: Ask for a first name and a last name and output the initials. So for Ludwig Billygoat that is \`L. B.\` -test: Ludwig, Billygoat -> L. B. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: The initial is the first letter of a name. Which method gives the character at a particular place? -tipp: \`firstName.charAt(0)\` gives the first letter. The output is then \`firstName.charAt(0) + ". " + lastName.charAt(0) + "."\` -lösung: zk-initialen-loesung.visustruct -::: - -## Exercise: Backwards - -::: übung zk-rueckwaerts -titel: Backwards -auftrag: Ask for a word and output it backwards. Ludwig becomes \`giwduL\`. -test: Ludwig -> giwduL -test: Anna -> annA -test: a -> a -braucht: forSchleife -tipp: Go through the word character by character, but from back to front, and build a new text as you go. -tipp: Create \`String backwards = ""\`. The loop starts at the last index \`word.length() - 1\` and counts down to 0 with \`i--\`. In every pass you add \`word.charAt(i)\` at the end. -tipp: The outline: input \`word\`, then \`String backwards = ""\`. For loop with start \`int i = word.length() - 1\`, condition \`i >= 0\` and step \`i--\`. Inside it \`backwards = backwards + word.charAt(i)\`. Below the loop \`output: backwards\`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Exercise: Count words - -::: übung zk-woerter -titel: Count words -auftrag: Ask for a sentence and output how many words it has, e.g. \`Words: 4\` for “Ludwig is a goat”. The words are separated by exactly one space. -test: Ludwig is a goat -> Words: 4 -test: Hello -> Words: 1 -test: I like goats -> Words: 3 -braucht: anweisung -tipp: There is always a space between the words. Which method splits a text at a character? -tipp: Split the sentence into a String array with \`sentence.split(" ")\`. Its length is the number of words. -tipp: The whole program has three boxes: the input \`sentence\`, then \`String[] words = sentence.split(" ")\` and finally \`output: "Words: " + words.length\`. -lösung: zk-woerter-loesung.visustruct -::: - -## Now in Java - -The methods in this lesson already are Java. A whole program with a text looks like this: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("What is your name?"); -String name = scanner.next(); -System.out.println("Hello " + name.toUpperCase() + "!"); -System.out.println("Your name has " + name.length() + " letters."); -\`\`\` - -- \`scanner.next()\` reads one word, that is text up to the next space. For numbers you keep using \`scanner.nextInt()\`. -- Text goes in **double** quotes \`"Ludwig"\`, a single character in **single** quotes \`'L'\`. -- \`length()\`, \`toUpperCase()\` and the other methods always have round brackets after them, even when nothing is inside. -- You compare two texts with \`equals\`. Every letter counts: \`"Ludwig".equals("ludwig")\` is false. - -::: übung l17-java-passwort -titel: Check the password -code: java -auftrag: Write a Java program that asks for the password. If it is **Ludwig**, it outputs **Welcome!**, otherwise **Wrong password**. - Mind upper and lower case: \`ludwig\` is wrong. -test: Ludwig -> Welcome! -test: ludwig -> Wrong password -test: Geißbock -> Wrong password -braucht: verzweigung -tipp: Read the password with \`scanner.next()\` into a String variable. Then compare it in a selection. -tipp: The condition is \`input.equals("Ludwig")\`. In Java, \`==\` does not reliably compare texts. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Fill in the gaps: texts - -::: lückentext -titel: Examining a word -auftrag: Fill in the missing methods and words. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("A word?"); -[[String]] word = scanner.next(); -System.out.println("Length: " + word.[[length]]()); -System.out.println("Capitals: " + word.[[toUpperCase]]()); -System.out.println("First letter: " + word.[[charAt]](0)); -if (word.[[equals]]("Ludwig")) { - System.out.println("Hello Ludwig!"); -} -\`\`\` -erklärung: \`length()\` counts the characters, \`toUpperCase()\` makes everything upper case, \`charAt(0)\` gives the first character. You compare texts with \`equals\`, not with \`==\`. -::: - -## Find the bug: initials - -::: übung l17-java-fehler -titel: Fix the initials -code: java -auftrag: The program reads a first name and a last name and should output the initials. For Ludwig Geißbock that is **L. G.** But it outputs **u. e.** - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: At which index does Java start counting? -tipp: The first character has the index 0: \`first.charAt(0)\` and \`last.charAt(0)\`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l17-java-laeuft-nicht -titel: Count the letters -code: java -auftrag: The program should output the first letter and the length of **Ludwig**, so **L has 6 letters**. But it doesn’t run, because two methods are spelled wrong. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L has 6 letters -braucht: anweisung -tipp: Method names are camelCase too: they start small, and every further word starts with a capital. -tipp: Correct are \`name.charAt(0)\` with a capital A and \`name.length()\` all small. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Even more methods - -A String can do much more. You will often need these methods too: - -| Method | Meaning | Example | Result | -|---|---|---|---| -| \`lastIndexOf("l")\` | Where is the last l? | \`"Billygoat".lastIndexOf("l")\` | 3 | -| \`isEmpty()\` | Is the text empty? | \`"".isEmpty()\` | true | -| \`equalsIgnoreCase(…)\` | same text, whether upper or lower case? | \`"Billygoat".equalsIgnoreCase("BILLYGOAT")\` | true | -| \`replace(old, new)\` | replaces every occurrence | \`"Billygoat".replace("Billy", "Nanny")\` | Nannygoat | -| \`repeat(n)\` | the text n times in a row | \`"Baa".repeat(3)\` | BaaBaaBaa | -| \`concat(…)\` | adds text to the end, like \`+\` | \`"Billy".concat("goat")\` | Billygoat | -| \`compareTo(…)\` | Which comes first in the alphabet? | \`"Anna".compareTo("Ben")\` | -1 | - -\`compareTo\` returns a number: **negative** if the text comes earlier in the alphabet, **0** if both are the same, and **positive** if it comes later. You can use it to sort names. - -Two methods do not belong to a text but to a class. That is why the class name comes first: - -- \`Integer.parseInt("7")\` turns the text "7" into the number 7. You can calculate with it. -- \`String.valueOf(7)\` turns the number 7 into the text "7". - -::: diagramm zk-mehr.visustruct -All the new methods in one example. Go through it in the simulation and compare every output with the table. -::: - -## Text from a file - -Programs often get their text from a **file**. Princess Zora has written Ludwig a letter. It is ready as the file \`letter.txt\`: - -::: datei letter.txt -Dear Ludwig, -the fox has locked me in his tower. -The door has a lock with a secret word. -You can only find the word with Java. -Please help me! -Yours, Princess Zora -::: - -These lines read the whole file into a String: - -\`\`\` -import java.nio.file.*; - -String letter = Files.readString(Path.of("letter.txt")); -\`\`\` - -You will learn exactly how reading works later, in a lesson of its own about files. For now this is enough: afterwards the whole letter is in \`letter\`, with all its lines. - -At the end of every line there is an invisible character for the **line break**. In Java you write it as \`"\\n"\`. So \`letter.split("\\n")\` splits the letter into its lines. - -::: übung l17-java-brief -titel: Where is the fox? -code: java -auftrag: The start code already reads the letter. Split it into lines and output **Lines:** and the number of lines. Then find the line that contains **fox** and output **Fox in line** and its number. The first line has the number 1. -start: l17-java-brief-start.visustruct -test: - -> Lines: 6 | Fox in line 2 -braucht: forSchleife, verzweigung -tipp: \`String[] lines = letter.split("\\n");\` splits the letter. Then a for loop goes through all the lines. -tipp: In the loop you check \`lines[i].contains("fox")\`. The index starts at 0, so you output \`(i + 1)\`. You need the brackets so that Java calculates instead of adding 1 to the text. -lösung: l17-java-brief-loesung.visustruct -::: - -## The Java API - -Nobody knows all methods by heart, not even professionals. They look them up in the **Java API**. API stands for Application Programming Interface. It means the description of everything Java already brings along. - -You can find all methods of String here: [String in the Java API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Scroll down to the table **Method Summary**. Each row describes one method: - -| Column | Example | Meaning | -|---|---|---| -| Modifier and Type | \`int\` | What the method returns | -| Method | \`lastIndexOf(String str)\` | Name and parameters with their data type | -| Description | Returns the index within this string of the last occurrence … | What the method does | - -::: ludwig -The page is very long. Press Ctrl+F (on a Mac ⌘+F) and search for the name of the method to get there at once. -::: - -Look up these methods. They are not in this lesson: - -::: quiz -? Look up \`strip()\`. What does the method do? -- [ ] It removes all spaces in the text. -- [x] It removes the spaces at the beginning and the end. -- [ ] It cuts off the last character. -> Like \`trim()\`. But \`strip()\` also knows special spaces from other writing systems. - -? Look up \`isBlank()\`. What does \`" ".isBlank()\` return? -- [x] true -- [ ] false -- [ ] 3 -> isBlank checks whether the text is empty or only contains spaces. \`" ".isEmpty()\`, on the other hand, would be false. - -? Look up \`toCharArray()\`. Which data type does the method return? -- [ ] String -- [ ] String[] -- [x] char[] -> An array with all characters of the text, one element for each character. - -? Look up \`indexOf\` with two parameters. What does \`"Billygoat".indexOf("l", 3)\` return? -- [ ] 2 -- [x] 3 -- [ ] -1 -> The search only starts at index 3. The first l from there is at index 3. - -? Look up \`startsWith\`. Which data type does the method return? -- [x] boolean -- [ ] int -- [ ] String -> The column “Modifier and Type” says boolean: the answer is true or false. -::: - -## Game: Rescue the princess - -Now you help Ludwig. The fox has locked Princess Zora in his tower, and the gate only opens with a secret word. - -::: spiel prinzessin -titel: Game: Rescue the princess -intro: On the way to the tower Ludwig comes to **six stations**. Each one has a piece of Java code, and a number comes out. \`alphabet.charAt(number)\` turns the number into a letter that goes into the **crossword**. Solve the crossword, and the yellow squares spell the secret word for the gate. - -station: At the moat - String name = " Zora "; - int number = name.trim().length(); -frage: Which number is in \`number\`? -zahl: 4 -feld: 5 1 -tipp: \`trim()\` cuts off the spaces at the front and the back. Then count the characters. -> After \`trim()\` only \`"Zora"\` is left, and that is 4 characters. - -station: In the fox forest - String place = "Fox lair"; - int number = place.length(); -frage: Which number is in \`number\`? -zahl: 8 -feld: 4 4 -tipp: \`length()\` counts every character, the space too. -> F-o-x, the space and l-a-i-r make 8 characters. - -station: At the goat rock - String text = "Ludwig the goat"; - int number = text.indexOf(" "); -frage: Which number is in \`number\`? -zahl: 6 -feld: 4 6 -tipp: \`indexOf\` returns the position of the first space. The L has index 0. -> L u d w i g has the indexes 0 to 5, so the first space is at index 6. - -station: On the tower stairs - String word = "Princess"; - int number = word.indexOf("s", 7); -frage: Which number is in \`number\`? Look up \`indexOf(String str, int fromIndex)\` in the Java API. -zahl: 7 -feld: 6 6 -tipp: The search starts at index 7, and index 7 itself counts. Write the indexes under the letters. -> P r i n c e s s has the indexes 0 to 7. Searching from index 7, the first “s” is right at 7. - -station: In front of the fox den - int number = "Zora".compareTo("Zoe"); -frage: Which number is in \`number\`? -zahl: 13 -feld: 1 9 -tipp: \`compareTo\` looks for the first position where the texts differ and subtracts the character codes. \`'r'\` is 114, \`'e'\` is 101. -> “Zo” is the same, then \`'r'\` meets \`'e'\`: 114 − 101 = 13. - -station: At the tower gate - int number = Integer.parseInt("1" + "1") / 5; -frage: Which number is in \`number\`? -zahl: 2 -feld: 6 5 -tipp: First the texts are joined, then they become a number. With \`int\`, division drops the remainder. -> \`"1" + "1"\` is \`"11"\`, \`parseInt\` makes 11 of it, and 11 / 5 is 2 for whole numbers. - -kreuzwort: You already have the blue letters. Fill the other squares with Java terms. Click a clue to jump to its word. -- → 1 3 BOOLEAN: Data type for true or false -- → 4 1 STRING: Data type for text -- → 6 5 CHAR: Data type for a single character -- ↓ 1 6 LENGTH: Method that counts the characters of a string -- ↓ 2 1 ELSE: The “otherwise” part of a branch -- ↓ 3 3 BREAK: Jumps straight out of a loop -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: Ludwig stands in front of the gate. The lock compares your word with \`equals\`. Enter the secret word. - String word = "___"; - if (word.equals(secretWord)) { - gate.open(); - } -ende: Zora is free, and the fox has run off. The secret word was \`BREAK\`: with \`break\` you get out of any loop, and Zora gets out of the tower. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the characters of a String are also counted from 0. → Text is a row of characters -- I can work with \`length()\` and \`charAt(i)\`. → Text is a row of characters -- I can tell a \`char\` from a String and use the character code with \`(int)\` and \`(char)\`. → A single character: char -- I can cut a part out of a text with \`substring\`. → Cut out parts -- I can use \`indexOf\`, \`contains\`, \`toUpperCase\` and \`equals\`. → Search and compare -- I can go through a text character by character with a loop. → Character by character -- I can split a text into a String array with \`split\`. → Split with split -- I can read a text in Java and compare it with \`equals\`. → Now in Java -- I can use more methods such as \`lastIndexOf\`, \`replace\`, \`compareTo\` and \`Integer.parseInt\`. → Even more methods -- I can split a text from a file into lines with \`split("\\n")\`. → Text from a file -- I can look up a method in the Java API and read its description. → The Java API -::: - -## Quiz - -::: quiz -? What does \`"Ludwig".charAt(1)\` give? -→ Text is a row of characters -- [ ] L -- [x] u -- [ ] d -> Counting starts at 0: L has the index 0, u the index 1. - -? What is \`'C'\`? -→ A single character: char -- [x] a \`char\`, that is, exactly one character -- [ ] a String -- [ ] a variable called C -> Single quotation marks stand for a single character, a \`char\`. \`"C"\` in double ones would be a String. - -? \`'A'\` has the character code 65. What does \`(char) 67\` give? -→ A single character: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 is A, 66 is B, 67 is C. With \`(char)\` the code turns back into a character. - -? What does \`"Structure".substring(0, 6)\` give? -→ Cut out parts -- [x] Struct -- [ ] Structu -- [ ] tructu -> The characters with the indices 0 to 5. The end 6 is not included. - -? A word has 7 characters. Which index does the last character have? -→ Text is a row of characters -- [ ] 7 -- [x] 6 -- [ ] 8 -> Just like with an array: always one less than the length. - -? How do you compare two Strings for the same content in Java? -→ Search and compare -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Match each method to what it gives for \`"Billygoat"\`. -→ Search and compare -= length() -> 9 -= charAt(0) -> B -= indexOf("goat") -> 5 -= substring(5) -> goat - -? Which condition checks in Java whether \`input\` contains the text Ludwig? -→ Now in Java -- [ ] input = "Ludwig" -- [ ] input == 'Ludwig' -- [x] input.equals("Ludwig") -> You compare texts with equals. Single quotes are only for a single character. - -? What does \`Integer.parseInt("12") + 1\` return? -→ Even more methods -- [ ] "121" -- [x] 13 -- [ ] an error message -> parseInt turns the text into a number. After that Java calculates as usual. -::: -`,Uw=`--- -titel: Call and break -bereich: Basics -kurz: Split big tasks into subprograms and leave loops early. ---- - -## Skills I will gain now - -::: kompetenzen -- split a big task into smaller problems -- insert a call and build a subprogram in a tab of its own -- pass values to a subprogram with parameters -- hand back a return value with return and store it when calling -- explain why a subprogram cannot change a number but can change an array -- explain what a Break does and why it sits in a branch -- end a loop with a Break or through its condition -- recognise a method in Java and leave a loop with \`break\` -::: - -## Split big tasks - -Long structure charts quickly get confusing. That is why you split a big task into **smaller problems**. Every smaller problem gets a name of its own and a structure chart of its own, a **subprogram**. - -A quiz, for example, has three parts: greet, ask questions, show the result. The main program only names these three parts: - -::: diagramm l15-hauptprogramm.visustruct -The main program is made of just three calls. What happens in each part is in a structure chart of its own. -::: - -That way you see at a glance how the program is built. The details are somewhere else. - -## The call - -A **call** is a box with double vertical lines on the left and right. It means: run the subprogram with this name here, then carry on. - -- You drag **Call** from the Elements area and type the name into it, e.g. \`showGreeting()\`. -- The round brackets at the end show that it is a subprogram. That is how you write it in Java, Python and JavaScript, too. -- You build the subprogram itself as a structure chart of its own in a new tab. As the title you type the same name. - -::: diagramm l15-unterprogramm.visustruct -The subprogram showGreeting() as a structure chart of its own. -::: - -> **In the simulation:** When the simulation reaches a call, it looks among the open tabs for the structure chart with this title and runs it. Then it carries on in the main program. While the subprogram is running, the call box stays highlighted. A subprogram like this one, with only a name as its title, shares its variables with the main program. You will soon learn how a subprogram gets variables of its own. If there is no matching tab, the simulation skips the call and shows a note. - -::: angeberwissen -Why does your teacher always say “method” in Java and never “function” or “procedure”? Because in Java every subprogram has to belong to a class, and the subprograms of a class are called methods. The difference still exists, though: if a method hands back a result, it is really a function. If it has \`void\` in front, it hands back nothing and is really a procedure. Java just keeps it simple and calls both methods. So do I: my method is called Munching. -::: - -## Exercise: Add a call - -::: übung l15-titel -titel: Greeting with a call -auftrag: Imagine the subprogram \`showTitle()\` already exists. Build the main program: first it calls \`showTitle()\`. Then it asks for a name and outputs **Hello** and the name. - So when Ada is typed in, **Hello Ada** appears. -test: Ada -> Hello Ada -test: Ben -> Hello Ben -braucht: aufruf -tipp: The main program has three parts: the call, the input and the output. -tipp: Drag a Call from the Elements area right to the top and type \`showTitle()\` into it. Below it come the input \`input: String name "What is your name?"\` and the output. -lösung: l15-titel-loesung.visustruct -::: - -## Pass values: parameters - -Often a subprogram should work with values that are only known when it is called. Then it gets a **header line** as its title: - -\`\`\` -void showSum(int a, int b) -\`\`\` - -- \`void\` means: the subprogram does not hand back a result. -- \`showSum\` is the name. -- The brackets hold the **parameters**: variables with a data type that get their values when the subprogram is called. You separate several of them with commas. - -::: diagramm l15-zeige-summe.visustruct -The subprogram with the header line as its title. It calculates with a and b as if they were normal variables. -::: - -In the call box you write the values in the brackets, e.g. \`showSum(3, 4)\`. The first value goes into \`a\`, the second into \`b\`. Instead of numbers you can also use variables and calculations such as \`showSum(x, x + 1)\`. - -::: diagramm l15-zeige-summe-haupt.visustruct -The main program calls showSum twice with different values. Open both diagrams and start the simulation here. -::: - -> **Local variables:** The parameters and all variables created in the subprogram belong only to the subprogram. It gets **copies** of the values; more on that below under call by value. During the call, the simulation only shows the variables of the subprogram. - -## Hand back a result: return - -A subprogram can also hand back a result, the **return value**. Then the header line has the data type of the result instead of \`void\`: - -\`\`\` -int square(int x) -\`\`\` - -The statement \`return result\` hands back the value and ends the subprogram straight away. When you call it, you store the result in a variable: after \`int q = square(5)\`, \`q\` holds the value 25. - -::: diagramm l15-quadrat.visustruct -The subprogram square calculates and hands back the result with return. -::: - -::: diagramm l15-quadrat-haupt.visustruct -The main program stores the return value in q. Open both diagrams and try the simulation. -::: - -> A subprogram with a return value has to end with \`return\`. If the return is missing, the simulation reports an error. - -In the **code view**, the header line becomes a Java method (\`public static int square(int x)\`), a \`def\` in Python and a \`function\` in JavaScript. If the subprograms are open in other tabs, they appear in the code together with the main program. - -## Exercise: Calculate the area - -::: übung l15-flaeche -titel: Calculate the area -auftrag: Build two diagrams. The subprogram \`int area(int a, int b)\` hands back the area of a rectangle, that is a times b. The main program asks for the length and the width, calls \`area\` and outputs **Area:** and the result. - With 3 and 4, **Area: 12** appears. -test: 3, 4 -> Area: 12 -test: 5, 5 -> Area: 25 -test: 7, 0 -> Area: 0 -braucht: aufruf -tipp: You build two diagrams: a subprogram that only calculates and hands back the result, and a main program for input and output. -tipp: Open a new tab with **+** and give it the title \`int area(int a, int b)\`. One statement is enough inside: \`return a * b\`. In the main program you type \`int f = area(length, width)\` into the call box. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Copy or original: call by value and call by reference - -What happens if a subprogram changes its parameter? That depends on **what** is passed. - -**Call by value:** With \`int\`, \`double\`, \`boolean\`, \`char\` and \`String\`, the subprogram gets a **copy** of the value. If it changes its parameter, the caller's variable stays as it was. - -**Call by reference:** With an **array**, the subprogram does not get a copy but a reference to the same array. If it changes a slot, the slot is changed for the caller too. The header line for this looks like this: - -\`\`\` -void clear(boolean[] seats) -\`\`\` - -::: grafik uebergabe.svg -At the top, doubleIt gets a copy of number. At the bottom, row and seats point to the same array. -::: - -Try out both. Open the three diagrams and start the simulation in the main program: - -::: diagramm uebergabe-verdopple.visustruct -doubleIt only changes its copy x. -::: - -::: diagramm uebergabe-leeren.visustruct -clear sets all seats to false, for example when a showing is cancelled. -::: - -::: diagramm uebergabe-haupt.visustruct -number is still 5 after the call. The seats in row, on the other hand, are free after the call. -::: - -> If you want to get a number back from a subprogram, use \`return\`, e.g. \`number = doubleIt(number)\` with the header line \`int doubleIt(int x)\`. An array, on the other hand, can be changed by the subprogram directly. - -::: angeberwissen -Strictly speaking, Java only knows call by value. But with an array, the whole array is not copied, only its **address** in memory. With the copied address, the subprogram finds the same array. It is as if I gave you a copy of my locker key: the key is copied, but the hay in the locker only exists once. -::: - -## Exercise: Bonus points - -::: übung uebergabe-bonus -titel: Bonus points -auftrag: The main program calls \`bonus(points)\` and then outputs all the points. Build the subprogram \`void bonus(int[] points)\` that increases every score in the array by 1. Instead of 3, 5 and 8 you then see **4**, **6** and **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: The main program is already finished. You only build the subprogram in a new tab. -tipp: Open a new tab with **+** and the title \`void bonus(int[] points)\`. Inside, a for loop runs over all slots and calculates \`points[i] = points[i] + 1\`. You do not need a return, because the array is changed directly. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## The break - -Sometimes a loop should end **early**, in the middle of its body. That is what the **Break** is for. It leaves the loop it is in straight away. The program carries on with the first box after the loop. In Java, Python and JavaScript this is called \`break\`. - -You recognise the Break by the point on its left edge. You can write a short comment into the box, e.g. \`Leave the loop\`. It appears in the code as a comment after \`break\`. - -A Break almost always sits in a **branch**: the loop is only left if a condition is met. - -A typical case is the **endless loop with a Break**. The loop itself has no condition. It runs until the Break is reached: - -::: diagramm l15-endlos-aussprung.visustruct -The loop keeps asking for a number. With 0 it jumps out, otherwise it adds the number to the sum. -::: - -Without a Break, an endless loop would really never end. - -## Break in the simulation - -The simulation runs the Break like \`break\`: it leaves the innermost loop and carries on after it. If a Break is outside every loop, the subprogram ends; in the main program, the whole program ends. \`return\` does the same. - -Often you can do without a Break altogether: you write the stop condition straight into the loop. That is usually even clearer. - -## Exercise: Sum until 0 - -::: übung l15-bis-null -titel: Sum until 0 -auftrag: Rebuild the program “Sum until 0” **without** a Break. It keeps asking for whole numbers until someone types 0. Then it outputs **Sum:** and the sum of all the numbers typed in. -test: 3, 4, 0 -> Sum: 7 -test: 0 -> Sum: 0 -test: 10, -2, 5, 0 -> Sum: 13 -tipp: Without a Break, the condition of the loop itself has to check whether 0 was typed in. -tipp: Ask for the first number before the loop. The while loop runs with the condition \`number != 0\`. In the body you add the number to the sum and then ask for the next number. For the second input you only write \`input: number "…"\`, because \`number\` already exists. -lösung: l15-bis-null-loesung.visustruct -::: - -## Now in Java - -In Java a subprogram is called a **method**. The square from above looks like this: - -\`\`\`java -public static int square(int x) { - int result = x * x; - return result; -} -\`\`\` - -The main program calls the method by its name and passes the value in round brackets: - -\`\`\`java -int q = square(number); -System.out.println("The square is " + q); -\`\`\` - -- In the header, what the method returns (\`int\`) comes before the name. If it returns nothing, it says \`void\`. -- The parameters go in the round brackets with their data type: \`(int x)\`. You separate several with commas. -- In the Code area you write every method in its own tab, just as every subprogram has its own structure chart. -- In Java the exit is called \`break\`. With \`while (true)\` you build a loop that only ends through \`break\`. - -::: übung l18-java-kasse -titel: Till with break -code: java -auftrag: Write a till in Java. It keeps asking for a price in whole euros. At 0 it leaves the loop with \`break\`. Then it outputs how many items there were and what they cost altogether. - For 3, 4 and then 0 you see **Items: 2** and **Total: 7**. -test: 3, 4, 0 -> Items: 2 | Total: 7 -test: 0 -> Items: 0 | Total: 0 -test: 10, 2, 5, 0 -> Items: 3 | Total: 17 -braucht: endlosschleife, aussprung -tipp: You need two variables that start at 0 before the loop: one for the sum and one for the count. -tipp: Inside \`while (true) { … }\` you read the price. Right after that you check \`if (price == 0) { break; }\`. Only after that do you add the price to the sum and increase the count by 1. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Fill in the gaps: methods and break - -::: lückentext -titel: Method and exit -auftrag: Fill in the missing words. At the top is the method that calculates a square. Below is the main program that calls it. In the Code area both are in tabs of their own. -\`\`\`java -public static [[int]] square(int x) { - [[return]] x * x; -} -\`\`\` -\`\`\`java -int number = 1; -while ([[true]]) { - int q = [[square]](number); - if (q > 50) { - [[break]]; - } - System.out.println(q); - number = number + 1; -} -\`\`\` -erklärung: \`int\` before the method name means: the method returns a whole number, with \`return\`. If a method returns nothing, it says \`void\` there. \`while (true)\` runs endlessly until \`break\` leaves the loop. Output: 1, 4, 9, 16, 25, 36 and 49. -::: - -## Find the bug: guessing - -::: übung l18-java-fehler -titel: Fix the try counter -code: java -auftrag: In this guessing game the secret number is 7. The program keeps asking for a guess until 7 comes, then it outputs the number of tries. For 3, 5 and 7 that was 3 tries, but the program says **Tries: 2**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Tries: 3 -test: 7 -> Tries: 1 -test: 1, 2, 3, 7 -> Tries: 4 -braucht: endlosschleife, aussprung -tipp: What happens with the right guess? Is it still counted before \`break\` leaves the loop? -tipp: Count first, then check: \`tries = tries + 1;\` belongs before the \`if\` with the \`break\`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Game: What comes back? - -::: spiel frage -titel: Game: What comes back? -intro: Ludwig calls methods and jumps out of loops. Predict what is output. Seven rounds. - -runde: - public static int square(int x) { - return x * x; - } - - int q = square(5); - System.out.println(q); -frage: What is output? -- 5 -- 10 -- [x] 25 -> The call puts 5 into x, and return gives back 5 · 5. - -runde: - public static void showSum(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - showSum(x, x + 1); -frage: What is output? -- [x] 5 -- 23 -- 3 -> The first value 2 goes into a, the second value 3 into b. In Java, \`a + b\` calculates with numbers as usual. - -runde: -frage: What does a method return when \`void\` stands in front of its name? -- a whole number -- [x] nothing -- always 0 -> \`void\` means: no return value. With \`int\` there, a whole number comes back. - -runde: - public static void doubleIt(int x) { - x = x * 2; - } - - int number = 5; - doubleIt(number); - System.out.println(number); -frage: What is output? -- [x] 5 -- 10 -- 0 -> Call by value: doubleIt gets a copy. number stays 5. - -runde: - public static void clear(boolean[] seats) { - for (int i = 0; i < seats.length; i++) { - seats[i] = false; - } - } - - boolean[] row = {true, true}; - clear(row); - System.out.println(row[0]); -frage: What is output? -- true -- [x] false -> With an array, row and seats point to the same array. The caller sees the change too. - -runde: - int sum = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - sum = sum + i; - } - System.out.println(sum); -frage: What is output? -- [x] 6 -- 10 -- 55 -> At i = 4, \`break\` jumps out of the loop straight away. Only 1 + 2 + 3 have been added. - -runde: - int number = 1; - while (true) { - number = number * 3; - if (number > 20) { - break; - } - } - System.out.println(number); -frage: What is output? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Only 27 is greater than 20, then the endless loop ends with break. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can split a big task into smaller problems. → Split big tasks -- I can insert a call and build a subprogram in a tab of its own. → The call -- I can pass values to a subprogram with parameters. → Pass values: parameters -- I can hand back a return value with return and store it when calling. → Hand back a result: return -- I can explain why a subprogram cannot change a number but can change an array. → Copy or original: call by value and call by reference -- I can explain what a Break does and why it sits in a branch. → The break -- I can end a loop with a Break or through its condition. → Break in the simulation -- I can recognise a method in Java and leave a loop with \`break\`. → Now in Java -::: - -## Quiz - -::: quiz -? What does a call box with the text \`showResult()\` mean? -→ The call -- [ ] The program ends here. -- [x] The subprogram showResult() runs here, and then the program carries on. -- [ ] The word showResult() is output here. - -? Where does the program carry on after a Break? -→ The break -- [ ] At the start of the loop -- [x] At the first box after the loop -- [ ] Right at the start of the program -> The Break leaves the loop it is in. In Java, Python and JavaScript it is called break. - -? Why does a Break usually sit in a branch? -→ The break -- [x] So that the loop is only left if a condition is met -- [ ] Because otherwise a Break cannot be saved -- [ ] Because a branch is a loop itself -> Without a branch, the loop would be left in the very first pass. - -? Where does the simulation find the subprogram for a call \`showResult()\`? -→ The call -- [ ] It has to be in the same structure chart. -- [x] In an open tab with the title showResult() -- [ ] The simulation cannot run subprograms. -> Build every subprogram in a tab of its own and give it the name from the call as its title. - -? The subprogram has the header line \`void showSum(int a, int b)\`. Which call fits? -→ Pass values: parameters -- [ ] showSum() -- [x] showSum(3, 4) -- [ ] int s = showSum(3, 4) -> Two parameters need two values. And \`void\` means nothing comes back that you could store. - -? After \`int q = square(5)\`, q holds 25. What is at the end of the subprogram square? -→ Hand back a result: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> With return the subprogram hands the value back to the call. An output would only show it. - -? \`void doubleIt(int x)\` calculates \`x = x * 2\`. The main program has \`int number = 5\` and then the call \`doubleIt(number)\`. What value does number have afterwards? -→ Copy or original: call by value and call by reference -- [x] 5 -- [ ] 10 -- [ ] 0 -> Call by value: doubleIt only changed a copy of number. - -? \`void clear(boolean[] seats)\` sets all slots to false. What is true for the array row in the main program after \`clear(row)\`? -→ Copy or original: call by value and call by reference -- [ ] It is unchanged, because the subprogram gets a copy. -- [x] All slots are false, because seats and row are the same array. -- [ ] The array is deleted. -> Call by reference: an array is passed as a reference. The caller sees changes to the slots too. - -? What is a subprogram called in Java? -→ Now in Java -- [ ] Class -- [x] Method -- [ ] Loop -> In Java a subprogram is a method, for example the \`main\` method. Depending on its kind, it is also called a procedure or a function. - -? Many building blocks have a second name in textbooks. Match each term from VisuStruct to its other name. -→ Now in Java -= Branch -> Conditional statement -= Switch -> Multiple selection -= Loop -> Iteration -= Subprogram -> Method -> Both names mean the same thing. If you know one of them, you understand the other as well. - -? What is written in the header of a Java method that returns nothing? -→ Hand back a result: return -- [ ] int -- [x] void -- [ ] return -> void means “empty”: the method does something but returns no value. -::: -`,Qw=`--- -titel: Searching and sorting -bereich: Basics -kurz: Use playing cards to understand how linear search, binary search and bubble sort work, and why some methods are much faster. ---- - -## Skills I will gain now - -::: kompetenzen -- explain how linear search goes through an array -- build a linear search as a structure chart -- explain why binary search only works with sorted values -- explain why binary search is much faster with many values -- show one round of bubble sort with playing cards -- search an array in Java and count matches -::: - -## Look for a card - -A pile of playing cards lies face down in a row in front of you. You are looking for the 9. What do you do? You turn over one card after another until you find the 9 or have turned over all the cards. - -::: grafik ss-lineare-suche.svg -The cards are mixed up. Every card you turn over is one comparison: is this the 9? -::: - -This is exactly how **linear search** works: it goes through the array from index 0 to the end and compares every slot with the value it is looking for. It always works, even if the values are mixed up. - -In the array our cards are numbers. We leave out jack, queen and king. - -::: diagramm ss-lineare-suche.visustruct -The loop runs until the card is found or there is no slot left. The variable comparisons counts how often a comparison was made. In the simulation, look for the 2, the 27 and the 4. -::: - -> If the card is not there, \`position\` stays at **-1**. There is no index -1, so here -1 means “not found”. - -## Exercise: Find a card - -::: übung ss-karte-suchen -titel: Find a card -auftrag: Create the array \`int[] cards = {8, 4, 11, 4, 6}\` and ask for a card. Output the position of the **first** matching card, e.g. \`Position: 1\` for the 4. If the card is not there, output \`Not found\`. -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 8 -> Position: 0 -test: 9 -> Not found -braucht: whileSchleife, verzweigung -tipp: Go through the array from front to back and compare every card with the one you are looking for. -tipp: Rebuild the example above. You do not need the variable comparisons here. The loop has to stop as soon as \`position\` is no longer -1, otherwise for the 4 you find the second one instead of the first. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Binary search: sorted is faster - -Now the cards lie in front of you **sorted**, from small to large. You are looking for the 27. You no longer turn over the first card at the front, but the **middle** one: - -- If it is the one you are looking for, you are done. -- If it is smaller, the card you are looking for can only be on the right. The whole left half drops out. -- If it is larger, the right half drops out. - -You do the same again with the rest. With every comparison the pile is halved. - -::: grafik ss-binaere-suche.svg -Three comparisons instead of fourteen: with every middle, half of the remaining cards drop out. -::: - -In the structure chart, two variables remember the range that is still being searched: \`left\` and \`right\`. The middle is \`(left + right) / 2\`. Because this is whole-number division, the result is always a whole index. - -::: diagramm ss-binaere-suche.visustruct -The same array as for the linear search. In the simulation, look for the 27 again and compare the output Comparisons with the linear search. -::: - -> Binary search **only works with sorted values**. With mixed-up cards, after looking at the middle you do not know which half to keep searching in. - -::: angeberwissen -I think of a number from 1 to 100. You may guess, and I only say “higher” or “lower”. With binary search you need at most **7** guesses. For a number up to a million it is at most 20, for a billion 30. Every doubling costs only one single extra guess. This is how databases search millions of entries in a fraction of a second. I search linearly, by the way: I eat the meadow from left to right. -::: - -## How fast is it? - -How good a method is gets measured by the number of comparisons it needs **in the worst case**. For linear search, that is the case where the card is right at the back or missing. - -::: grafik ss-vergleiche.svg -With 100 cards, linear search needs up to 100 comparisons, binary search at most 7. -::: - -| Number of cards | Linear search | Binary search | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1,000 | 1,000 | 10 | -| 1,000,000 | 1,000,000 | 20 | - -Linear search grows at the same rate as the number of cards. With binary search, twice as many cards add only **one** comparison. - -## Sorting with bubble sort - -For binary search the values must be sorted. One of the simplest ways of sorting is **bubble sort**. You go through the row from left to right and always compare **two neighbours**. If the larger card is on the left, you swap the two. - -::: grafik ss-bubblesort.svg -After one round, the largest card is on the far right. -::: - -After the first round the largest card is on the far right, like an air bubble rising to the top. That is where the name comes from. Then you repeat the round for the remaining cards. With 5 cards you need 4 rounds. - -The structure chart has two for loops inside each other. The outer one counts the rounds, the inner one compares the neighbours \`cards[i]\` and \`cards[i + 1]\`. You already know how to swap: it needs a **temporary variable**. - -::: diagramm ss-bubblesort.visustruct -The inner loop gets shorter with every round, because the largest cards on the right are already in the right place. Go through it in the simulation with Step and watch the array under Variables. -::: - -Bubble sort is easy to understand, but slow. With n cards it needs up to n · (n − 1) / 2 comparisons: with 10 cards 45, with 100 cards already 4,950 and with 1,000 cards almost half a million. You may get to know faster methods such as quicksort later. - -## Exercise: Sort descending - -::: übung ss-absteigend -titel: Sort descending -auftrag: The start diagram reads in 5 cards and sorts them with bubble sort **ascending**, that is from small to large. Change it so that the cards are output **descending**, the largest first. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Look at the condition in the branch. It decides when two neighbours are swapped. -tipp: You only need to change one single character. When should two neighbours be swapped if the larger card is supposed to go to the left? -tipp: In the branch, change \`cards[i] > cards[i + 1]\` to \`cards[i] < cards[i + 1]\`. Then two neighbours swap when the bigger card is on the right, and the big card moves to the left. -lösung: ss-absteigend-loesung.visustruct -::: - -## Now in Java - -The linear search from the exercise looks like this in Java: - -\`\`\`java editor -int[] cards = {8, 4, 11, 4, 6}; -int wanted = 4; -int position = -1; -int i = 0; -while (position == -1 && i < cards.length) { - if (cards[i] == wanted) { - position = i; - } - i = i + 1; -} -System.out.println("Position: " + position); -\`\`\` - -- \`&&\` means “and”: the loop only runs while nothing has been found **and** there are still cards left. -- If the false branch is empty, you simply leave out \`else\`. -- The swap in bubble sort needs a helper variable in Java too: \`int temp = cards[i];\` - -::: übung l19-java-zaehlen -titel: Count cards -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. Write a Java program that asks for a card and counts how often it appears. It outputs **Count:** and the number. - For 4 you see **Count: 2**. -test: 4 -> Count: 2 -test: 8 -> Count: 1 -test: 9 -> Count: 0 -braucht: forSchleife, verzweigung -tipp: Unlike the search, you do not stop at the first match. A for loop goes through all cards. -tipp: \`for (int i = 0; i < cards.length; i++)\` and inside it \`if (cards[i] == wanted) { count = count + 1; }\`. \`count\` starts at 0 before the loop. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Fill in the gaps: bubble sort - -::: lückentext -titel: Sorting cards -auftrag: The program sorts the cards in ascending order with bubble sort. Fill in the missing words and symbols. -\`\`\`java -int[] cards = {5, 2, 8, 1}; -for (int round = 0; round < cards.[[length]] - 1; round++) { - for (int i = 0; i < cards.length - 1; i++) { - if (cards[i] [[>]] cards[i + 1]) { - [[int]] temp = cards[i]; - cards[i] = cards[i + 1]; - cards[i + 1] = [[temp]]; - } - } -} -for (int i = 0; i < cards.length; i++) { - System.out.println(cards[i]); -} -\`\`\` -erklärung: If a card is larger than its right neighbour, the two are swapped, with a helper variable as when swapping glasses. After \`length - 1\` rounds everything is sorted: 1, 2, 5, 8. -::: - -## Find the bug: search - -::: übung l19-java-fehler -titel: Fix the linear search -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. The program should output the **first** position of the card you are looking for, so for 4 **Position: 1**. But it outputs **Position: 3**. - “Open in editor” shows the code in the Code area. Find the bug, fix it and click Check. -start: l19-java-fehler-start.visustruct -test: 4 -> Position: 1 -test: 6 -> Position: 4 -test: 9 -> Position: -1 -test: 8 -> Position: 0 -braucht: whileSchleife, verzweigung -tipp: The loop always runs up to the last card. What happens when the 4 comes a second time? -tipp: The loop should stop as soon as something has been found: \`while (position == -1 && i < cards.length)\`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Why doesn’t it run? - -::: übung l19-java-laeuft-nicht -titel: Find a card -code: java -auftrag: The cards {8, 4, 11, 4, 6} are in an array. The program searches for the 11 and should output **Position: 2**. But it doesn’t run. - “Open in editor” shows the code in the Code area. Click “Run” and read the error message. Fix the bug and click Check. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Position: 2 -braucht: forSchleife, verzweigung -tipp: Compare the names in the condition with the names created above. -tipp: The card you are looking for is in \`wanted\` with a small w: \`if (cards[i] == wanted) {\` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Game: Search pro - -::: spiel frage -titel: Game: Search pro -intro: Ludwig searches and sorts cards. How fast is it, and what comes out? Seven rounds. - -runde: - int[] cards = {8, 4, 11, 4, 6}; - int wanted = 4; - int position = -1; - int i = 0; - while (position == -1 && i < cards.length) { - if (cards[i] == wanted) { - position = i; - } - i = i + 1; - } - System.out.println("Position: " + position); -frage: What is output? -- [x] Position: 1 -- Position: 3 -- Position: 2 -> The search stops at the first 4. It lies in slot 1, counting from 0. - -runde: - int[] cards = {8, 4, 11, 4, 6}; - int wanted = 7; -frage: With the same linear search: what is in position afterwards? -- 0 -- 5 -- [x] -1 -> The 7 isn't there. position stays -1, which means "not found". - -runde: -frage: For which cards does binary search work? -- for all of them -- [x] only for sorted ones -- only for fewer than 100 -> Only if the cards are sorted do you know, after looking at the middle, in which half to keep searching. - -runde: -frage: How many comparisons does binary search need at most for 100 cards? -- 50 -- [x] 7 -- 100 -> Every comparison halves the pile. Halving 7 times is enough for up to 128 cards, so for 100 as well. - -runde: -frage: Instead of 1,000 cards there are now 2,000. How many more comparisons does binary search need at most? -- [x] one -- twice as many -- 1,000 -> The first comparison halves 2,000 to 1,000. After that it goes on as before. - -runde: - int[] cards = {5, 3, 8, 1}; -frage: One pass of bubble sort compares two neighbours at a time, from left to right. What is the order of the cards afterwards? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 and 3 swap, 5 and 8 stay, 8 and 1 swap. The biggest card is on the far right. - -runde: -frage: Bubble sort swaps \`cards[i]\` and \`cards[i + 1]\`. What does it need for that? -- [x] a temporary variable -- a second array -- a binary search -> Just like swapping two variables: \`int temp = cards[i];\`, otherwise a value gets lost. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain how linear search goes through an array. → Look for a card -- I can build a linear search as a structure chart. → Exercise: Find a card -- I can explain why binary search only works with sorted values. → Binary search: sorted is faster -- I can explain why binary search is much faster with many values. → How fast is it? -- I can show one round of bubble sort with playing cards. → Sorting with bubble sort -- I can search an array in Java and count matches. → Now in Java -::: - -## Quiz - -::: quiz -? What does binary search need? -→ Binary search: sorted is faster -- [ ] The array must have exactly 100 slots. -- [x] The values must be sorted. -- [ ] The value you are looking for must be there. -> Only with sorted values do you know, after looking at the middle, which half to keep searching in. - -? A sorted array has 1,000 values. How many comparisons does binary search need at most? -→ How fast is it? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1,000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: after about 10 halvings, one value is left. - -? What does bubble sort compare in every step? -→ Sorting with bubble sort -- [ ] The first and the last card -- [x] Two neighbouring cards -- [ ] Every card with the middle -> If the larger card is on the left, the neighbours are swapped. - -? Where is the largest card after the first round of bubble sort? -→ Sorting with bubble sort -- [ ] On the far left -- [ ] In the middle -- [x] On the far right -> It is swapped on with every comparison and rises to the right like a bubble. - -? What does \`position = -1\` mean in linear search? -→ Look for a card -- [x] The card has not been found (yet). -- [ ] The card is right at the back. -- [ ] The array is empty. - -? Match each method to the right description. -→ Look for a card -= Linear search -> look at one card after another -= Binary search -> look at the middle and drop one half -= Bubble sort -> compare neighbours and swap them - -? Why do you use a for loop for counting and not a search that stops at the first match? -→ Now in Java -- [ ] A for loop is always faster. -- [x] You have to look at every card, otherwise later matches are missing. -- [ ] A while loop cannot go through arrays. -> The card can appear several times. That is why you go through all cards to the end. -::: -`,$w=`--- -titel: Structure chart and code -bereich: From structure chart to code -kurz: All elements in Java at a glance, generate code, write Java in the Code area, run it and find mistakes. ---- - -## Skills I will gain now - -::: kompetenzen -- open the code view and choose Java as the language -- recognise output, input, branches and loops in Java -- explain the difference between Snippet and Class -- explain how Java code runs on every operating system with the compiler, bytecode and the JVM -- tell the JDK and the JRE apart -- explain why a C++ program has to be compiled again for every operating system but a Java program does not -- rebuild a short Java program as a structure chart -- type Java in the Code area and read the structure chart that is built -- run a program with ▶︎ and use input and output in the console -- find a syntax error or a runtime error by its red line number -- set breakpoints and follow a program step by step -::: - -## The code view - -In lessons 4 to 19 you have already written every element in Java. Here you see everything at a glance and learn how VisuStruct turns a whole structure chart into a Java program. - -A structure chart describes a process. For a computer to run it, you need a program in a programming language. VisuStruct writes this code for you. - -Click [Code](#zeige-code) in the tools (icon with angle brackets). On the right, the **Code generator** appears: - -- At the top you choose the language: **Java**, **Python** or **JavaScript**. -- For Java you also choose under **Java output** between **Snippet** (only the statements) and **Class** (a complete Java file). -- **Diagram text as comments** writes the text of every box as a comment above the code. -- **Copy** puts the code on the clipboard, **Save Java…** saves a \`.java\` file. - -The code changes straight away whenever you change something in the structure chart. - -::: angeberwissen -Later on, Nassi worked on the programming language Ada and was in charge of software at Apple. So if you invent structure charts, you do not end up in a goat shed, you end up at Apple. -::: - -::: diagramm l17-beispiel.visustruct -A small program with an input, a for loop and a branch. Open it and look at the Java code for it. -::: - -## This is the example in Java - -For the diagram above, VisuStruct generates this code as a **Snippet**: - -\`\`\` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Which times table?"); -int number = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int result = number * i; - System.out.println(i + " times " + number + " = " + result); -} -if (number > 10) { - System.out.println("That was hard!"); -} else { - System.out.println("That was easy."); -} -\`\`\` - -In Java every statement ends with a **semicolon** \`;\`. Whatever is inside a box goes in **curly brackets** \`{ }\` in Java. The indentation shows you what belongs together. - -## The elements in Java - -| Element | Java | Lesson | -|---|---|---| -| Statement | \`int sum = 0;\` | 4, 5 | -| Output | \`System.out.println(sum);\` | 6 | -| Input | \`int number = scanner.nextInt();\` | 6 | -| Branch | \`if (condition) { … } else { … }\` | 8, 9 | -| Switch | \`switch (value) { case 1 -> { … } default -> { … } }\` | 10 | -| While loop | \`while (condition) { … }\` | 11 | -| Do-while loop | \`do { … } while (condition);\` | 12 | -| For loop | \`for (int i = 0; i < 5; i++) { … }\` | 13 | -| Endless loop | \`while (true) { … }\` | 11, 18 | -| Break | \`break;\` | 18 | -| Call | \`calculate();\` | 18 | -| Array | \`int[] numbers = new int[5];\` | 15, 16 | -| Text (String) | \`String name = scanner.next();\` | 17 | - -For input, \`import java.util.Scanner;\` and \`Scanner scanner = new Scanner(System.in);\` are written once at the very top. Before every input VisuStruct outputs the question with \`System.out.println(…)\`. - -If the false path of a branch is empty, VisuStruct still generates an \`else\` part that contains only the comment \`// empty element\`. If you switch off **Emit empty else branch in code**, the empty \`else\` part disappears. - -## Snippet or Class - -You can paste a **Snippet** into the \`main\` method of an existing Java program. With **Class** you get a complete file that you can compile and run straight away: - -\`\`\` -import java.util.Scanner; - -public class TimesTable { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Which times table?"); - int number = scanner.nextInt(); - ... - } // end main -} // end class -\`\`\` - -The comments \`// end main\` and \`// end class\` after the last two brackets are not needed, but very helpful: you see at once which bracket closes what, and you don't forget one. - -The name of the class comes from the **title** of the diagram. The file must have exactly the same name, so here \`TimesTable.java\`. **Save Java…** takes care of that for you. - -> The generated code is a good start, but check it. If you first create a variable and later read it in again with an input, VisuStruct still writes \`int pin = scanner.nextInt();\`. Java then reports that \`pin\` already exists. Simply delete the \`int\` in that line. - -## How Java runs on every computer - -Your file \`TimesTable.java\` is **source code**: text for people. The processor in your computer cannot understand it directly. That is why Java runs in two steps: - -1. The **compiler** \`javac\` translates the source code into **bytecode**. The result is the file \`TimesTable.class\`. -2. The **JVM** (Java Virtual Machine) reads the bytecode and, while running it, translates it into **machine code**: instructions for the processor in your computer. - -::: grafik l20-jvm.svg -Compile once: the same bytecode runs on every JVM. -::: - -There is only one bytecode. But there is a JVM for Windows, macOS, Linux and many other systems. That is why the same \`.class\` file runs on every computer that has a JVM installed. This is called **platform independent**. The slogan for it is **“Write once, run anywhere”**. - -::: angeberwissen -One of the first compilers was built in 1952 by **Grace Hopper**, whom you already know from lesson 7. Back then many people told her that computers could only do arithmetic and could not translate programs. She did it anyway. -::: - -Compare this with **C++**: there the compiler translates the source code directly into machine code for exactly one processor and one operating system. The finished program only runs there. For Windows, macOS and Linux you have to compile it again each time. - -::: grafik l20-cpp.svg -C++ is compiled to machine code separately for every system. -::: - -| | C++ | Java | -|---|---|---| -| The compiler creates | machine code | bytecode | -| How often do you compile? | again for every system | once | -| What does the computer need to run it? | nothing else | a JVM | - -Does that make Java slower? Hardly. Parts of a program that run often, loops for example, are translated completely into machine code once by the JVM’s **JIT compiler** (“just in time”). After that they run almost as fast as C++. - -> Think of the JVM as a small computer made of software, almost like an operating system of its own just for Java programs. Strictly speaking it is not an operating system, though, but a **virtual machine**: a program that itself runs on Windows, macOS or Linux and pretends to be a computer inside it. - -When you install Java you meet two abbreviations: - -| Abbreviation | What is inside? | What for? | -|---|---|---| -| **JRE** (Java Runtime Environment) | the JVM and ready-made building blocks such as \`Scanner\` | running Java programs | -| **JDK** (Java Development Kit) | the JRE plus the compiler \`javac\` and other tools | writing and compiling Java programs | - -So to program you need the JDK. Today you usually only download the JDK, the JRE is inside it. For VisuStruct in the browser you don't need to install any of this. - -::: angeberwissen -With a JDK you can do all of this yourself in the console, that is in the terminal or the command prompt: - -\`\`\` -java -version -javac TimesTable.java -java TimesTable -\`\`\` - -- \`java -version\` shows which Java version is installed. If you get an error message, Java is still missing. -- \`javac TimesTable.java\` starts the compiler. Afterwards there are two files in the folder: \`TimesTable.java\` with your source code and the new \`TimesTable.class\` with the bytecode. -- \`java TimesTable\` starts the JVM with your bytecode. Here you write the class name without \`.class\`. - -If \`java -version\` works but \`javac -version\` doesn't, only a JRE is installed. Then you are missing the JDK. -::: - -::: angeberwissen -Every compiled Java file (\`.class\`) starts with the same four bytes, written in hexadecimal: \`CA FE BA BE\`. That is how the computer recognises it as Java. James Gosling came up with this “cafe babe”, to go with the coffee in the name. -::: - -## The Java language mode - -Under the [gear](#zeige-einstellungen) at the top right you can switch on **Code keywords**. Then the building blocks in the Elements area have the same names as the commands in Java: \`if\`, \`switch\`, \`for\`, \`while\`, \`do\`, \`while(true)\`, \`break\` and \`call\`. The rest of the interface stays in your language. - -This helps you match the boxes to the Java commands. Your diagram itself stays unchanged. - -## Write Java directly - -So far you drew the structure chart first and then looked at the code. In the [Code](#zeige-codelive) area it also works the other way round. Click the slim **Code** strip on the right. Next to the structure chart a column appears where you type Java yourself. - -- **Structure chart to code:** When you change something in the structure chart, the Java code changes right away. -- **Code to structure chart:** When you type Java, VisuStruct builds the structure chart as you type. If something is still missing, for example a closing bracket \`}\`, a hint appears below the code. -- **Suggestions:** While you type, VisuStruct suggests templates, for example for \`if\`, \`for\` or \`while\`. Press Enter to accept a template. Simply replace the grey placeholders with your own text. -- **Indent:** If your code has got messy, click the **Indent** button at the top of the Code area. Every level then sits four spaces deeper, as in Eclipse or IntelliJ. If you have selected lines, VisuStruct only indents those. - -So you can try both ways. Drawing the structure chart first is good for planning a process. Writing code directly is faster when you already know what you want. The structure chart then helps you check your code. - -## Run the program - -At the top of the Code area, [▶︎ Run](#zeige-ausfuehren) starts your program and **■ Stop** ends it, for example in an endless loop. What your program outputs appears below in the [console](#zeige-konsole). If it asks for input, type it there and press Enter. - -You can drag the border between code and console up or down with the mouse. - -If something goes wrong, the line number turns red. With a syntax error the spot is also underlined in red, and below it you read e.g. **Line 2: Still missing here: ;**. Then ▶︎ does not even start the program. If the program stops while it runs, it is a runtime error, and the console names the line, e.g. **Line 3: Division by zero.** You know the three kinds of errors from the lesson “Simulation and desk check”. - -If VisuStruct knows exactly what is missing, there is a 💡 at the end of the line. Move the mouse onto it to see the suggestion, e.g. **Suggestion: insert import java.util.Scanner;**. A click applies it. Still read what was inserted, so that you know it yourself next time. - -> The Code area understands the Java commands that also exist as boxes in the structure chart. It does not know your own classes and objects yet. Windows with Swing work anyway: turn on **Real Java (with Swing)** under the gear (lesson “Your first window”). - -::: angeberwissen -Professionals call a program where you write code, run it and look for mistakes an **IDE** (Integrated Development Environment). With the Code area, VisuStruct is a small IDE with a built-in structure chart. -::: - -## Find mistakes with the debugger - -Sometimes a program runs but calculates something wrong. That is a logic error. Then the **debugger** helps: it runs your program slowly while you watch. - -- **Breakpoint:** Click a line number on the left. A red dot appears. [▶︎ Run](#zeige-ausfuehren) pauses before this line. Click again to remove the dot. -- **Yellow line:** This line runs next. The matching box is selected in the structure chart. -- **Variables:** Below the code you see the current value of every variable. Whatever changed since the last pause lights up yellow for a moment. -- **Step:** The [Step](#zeige-einzelschritt) button next to ▶︎ runs exactly one line and pauses again. Without a breakpoint it starts the program and pauses before the first line. -- **Continue:** ▶︎ runs to the next breakpoint or to the end. **■ Stop** ends it. - -Try it with this program. Set a breakpoint on line 3 and press ▶︎ several times. How do \`i\` and \`summe\` grow? - -\`\`\` -int summe = 0; -for (int i = 1; i <= 4; i++) { - summe = summe + i; -} -System.out.println("Summe: " + summe); -\`\`\` - -::: ludwig -This is a desk check, except the computer takes the notes for you. Before each step, think about which value the variable should have next. When the table doesn't match your guess, you have found the place with the mistake. -::: - -## Exercise: Code first, then the structure chart - -This time you don't draw. Open the Code area and type in this Java code: - -\`\`\` -System.out.println("Start number?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -\`\`\` - -::: übung l20-countdown -titel: Countdown -bereich: beide -auftrag: Type the Java code above into the Code area. Run it with ▶︎ and enter a start number in the console. - Then look at the structure chart that was built and click Check. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Open the Code area with the slim strip on the far right and just start typing. -tipp: Watch out for the semicolon at the end of every statement and the curly brackets of the while loop. A hint below the code tells you when something is still missing. -lösung: l20-countdown-loesung.visustruct -::: - -## Exercise: Java as a structure chart - -Now the other way round: here is a piece of Java code. Rebuild it as a structure chart. - -\`\`\` -System.out.println("Enter a number"); -int n = scanner.nextInt(); -int factorial = 1; -for (int i = 1; i <= n; i++) { - factorial = factorial * i; -} -System.out.println(factorial); -\`\`\` - -::: übung l17-fakultaet -titel: Factorial from Java -bereich: beide -auftrag: Rebuild the Java code above as a structure chart. The program reads in a number n and outputs the product 1 · 2 · … · n. - At the end, check in the code view whether your Java code looks the same. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Read the Java code line by line and think about which box fits each line. -tipp: The first two Java lines together are one single input: \`input: int n "Enter a number"\`. The for loop has the three fields \`int i = 1\`, \`i <= n\` and \`i++\`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Exercise: else if - -In Java, several cases are often written as \`else if\`: - -\`\`\` -if (age < 6) { - System.out.println("Free entry"); -} else if (age < 18) { - System.out.println("Child ticket: 4 euros"); -} else { - System.out.println("Adult ticket: 8 euros"); -} -\`\`\` - -There is no \`else if\` in a structure chart. There, a second branch sits in the false path of the first one. If the false path holds only this one branch, VisuStruct turns it back into \`else if\` when it generates the code, and into \`elif\` in Python. - -::: übung l17-ticket -titel: Ticket price -bereich: beide -auftrag: Ask for the age as a whole number and rebuild the Java code above as a structure chart. -test: 5 -> Free entry -test: 6 -> Child ticket: 4 euros -test: 17 -> Child ticket: 4 euros -test: 18 -> Adult ticket: 8 euros -braucht: verzweigung -tipp: In the Java code there is another \`if\` inside an \`else\`. In the structure chart that is a branch inside a path. -tipp: Drag a second branch with the condition \`age < 18\` into the false path of the first branch. -lösung: l17-ticket-loesung.visustruct -::: - -## Game: Which box? - -::: spiel frage -titel: Game: Which box? -intro: Ludwig shows you Java. Which box from the structure chart is behind it? Eight rounds. -frage: Which box is this? - -runde: - do { - number = number * 2; - } while (number < 100); -- While loop -- [x] Do-while loop -- For loop -> The condition is at the bottom. So the body runs at least once. - -runde: - switch (choice) { - case 1 -> { System.out.println("water"); } - default -> { System.out.println("There is no such button."); } - } -- Branch -- [x] Switch -- Call -> One variable is compared with fixed values: that is the switch. - -runde: - break; -- Call -- Endless loop -- [x] Break -> \`break\` leaves the loop it stands in straight away. - -runde: - calculate(); -- [x] Call -- Output -- Statement -> A name with round brackets: a subprogram is called here. - -runde: - int number = scanner.nextInt(); -- Output -- [x] Input -- Call -> The scanner reads in a value. That is an input. - -runde: - while (true) { - System.out.println("Hello"); - } -- While loop with a condition -- [x] Endless loop -- Do-while loop -> \`while (true)\` has no real condition. Only a break could end it. - -runde: -frage: You want a complete Java file that you can compile and run straight away. What do you choose in the code view? -- Snippet -- [x] Class -> A snippet is only the statements for the \`main\` method. Class creates the whole file. - -runde: -frage: In the debugger, a line is yellow. What does that mean? -- It has a mistake. -- [x] It runs next. -- There is a breakpoint there. -> Yellow is the next line. A breakpoint is a red dot at the line number. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can open the code view and choose Java as the language. → The code view -- I can recognise output, input, branches and loops in Java. → The elements in Java -- I can explain the difference between Snippet and Class. → Snippet or Class -- I can explain how Java code runs on every operating system with the compiler, bytecode and the JVM. → How Java runs on every computer -- I can tell the JDK and the JRE apart. → How Java runs on every computer -- I can explain why a C++ program has to be compiled again for every operating system but a Java program does not. → How Java runs on every computer -- I can rebuild a short Java program as a structure chart. → Exercise: Java as a structure chart -- I can type Java in the Code area and read the structure chart that is built. → Write Java directly -- I can run a program with ▶︎ and use input and output in the console. → Run the program -- I can find a syntax error or a runtime error by its red line number. → Run the program -- I can set breakpoints and follow a program step by step. → Find mistakes with the debugger -::: - -## Quiz - -::: quiz -? What does \`output: "Hello"\` look like in Java? -→ The elements in Java -- [ ] print("Hello") -- [x] System.out.println("Hello"); -- [ ] console.log("Hello"); -> print is Python, console.log is JavaScript. - -? Where does the class name come from with Class? -→ Snippet or Class -- [ ] It is always called Main. -- [x] From the title of the diagram -- [ ] From the first box -> Without a title, the class is called Main. - -? Which Java code belongs to a do-while loop? -→ The elements in Java -- [ ] while (condition) { … } -- [x] do { … } while (condition); -- [ ] for (;;) { … } -> With do-while the condition is at the end, so the body runs at least once. - -? What does a Break become in Java code? -→ The elements in Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? What happens at a red dot before the line number? -→ Find mistakes with the debugger -- [ ] The line is skipped. -- [x] The program pauses before this line. -- [ ] The line contains a mistake. -> That is a breakpoint. Continue with ▶︎ or Step. - -? Why does the same \`.class\` file run on Windows, macOS and Linux? -→ How Java runs on every computer -- [ ] The compiler creates a separate file for every operating system. -- [x] On every operating system a JVM runs the same bytecode. -- [ ] Java programs don't need an operating system. -> The bytecode stays the same. Only the JVM exists separately for each system. - -? What do you need to write and compile Java programs? -→ How Java runs on every computer -- [ ] only the JRE -- [x] the JDK -- [ ] only the JVM -> The JRE is enough to run programs. The compiler \`javac\` only comes with the JDK. - -? You want to use your program on Windows and on a Mac. What is true? -→ How Java runs on every computer -- [ ] You have to compile Java again for every system, but not C++. -- [x] You have to compile C++ again for every system, but not Java. -- [ ] You only compile both once. -> C++ becomes machine code for one system directly. Java becomes bytecode, and the JVM runs it on every system. - -? Line number 3 in the Code area is red. What does that mean? -→ Run the program -- [ ] There is a breakpoint in line 3. -- [x] There is an error in line 3. -- [ ] Line 3 runs next. -> A breakpoint is a round red dot, and the next line is yellow. A red line number shows a syntax error or a runtime error. -::: -`,Yw=`--- -titel: Python and JavaScript -bereich: From structure chart to code -kurz: Generate Python and JavaScript code from the structure chart and run JavaScript straight in the browser. ---- - -## Skills I will gain now - -::: kompetenzen -- choose and compare Python and JavaScript in the code view -- explain that Python forms blocks by indentation and needs no data types -- explain what VisuStruct adjusts when translating to Python -- run JavaScript with Test in browser -::: - -## The same diagram, three languages - -In the code view ([Code](#zeige-code) in the tools) you choose the language at the top. The structure chart stays the same; only the way it is written changes. That way you can compare the languages easily. - -::: diagramm l18-beispiel.visustruct -A number is made smaller by 3 again and again until it is no longer greater than 0. -::: - -## Python - -For the diagram above, VisuStruct generates this Python code: - -\`\`\` -number = int(input("Start value?")) -while number > 0: - print(number) - number = number - 3 -print("End") -\`\`\` - -What is different in Python compared with Java: - -- There are **no semicolons** and **no curly brackets**. Python recognises what belongs to a loop or a branch by the **indentation** alone. After \`while\`, \`if\` and \`else\` there is a colon. -- Variables get **no data type**: \`int number = 5\` becomes \`number = 5\`. -- Output is called \`print(…)\`, input \`input(…)\`. For a number, \`int(…)\` goes around it. -- In Python, arrays are called **lists**: \`int[] numbers = {2, 4, 6}\` becomes \`numbers = [2, 4, 6]\`, and \`numbers.length\` becomes \`len(numbers)\`. -- A simple for loop such as \`int i = 0\`, \`i < 5\`, \`i++\` becomes \`for i in range(5):\`. - -## What to watch out for in Python - -In some places Python is stricter than Java. That is why VisuStruct translates the Java way of writing things when it exports: - -- **Text and number with +:** \`"Sum: " + sum\` becomes \`"Sum: " + str(sum)\`. Without \`str(…)\`, Python stops with an error. -- **There is no i++:** it becomes \`i += 1\`. -- **&&, || and !** are called \`and\`, \`or\` and \`not\` in Python, and \`true\` and \`false\` become \`True\` and \`False\`. -- **Whole-number division:** In Python, \`7 / 2\` always gives 3.5. If the structure chart divides two \`int\` values, VisuStruct writes \`7 // 2\`, which gives 3, just like in Java. -- **For loops:** Counting loops become \`for … in range(…)\`, also with \`i <= n\`, counting backwards or steps such as \`i = i + 2\`. If a loop does not fit that pattern, it becomes a \`while\` loop. -- **There is no do-while:** it becomes \`while True:\` with \`if not (…): break\` at the end. - -If a diagram is marked as **pseudocode**, only a note appears above the code. Then the text is a sketch and not a runnable program. - -## JavaScript - -The same diagram in JavaScript: - -\`\`\` -"use strict"; - -let number = parseInt((prompt("Start value?") ?? "0"), 10); -while (number > 0) { - console.log(number); - number = number - 3; -} -console.log("End"); -\`\`\` - -JavaScript looks similar to Java: curly brackets, semicolons, \`while (…)\` and \`if (…)\`. The differences: - -- Variables are created with \`let\`, without a data type. Constants with \`const\`. -- Output is called \`console.log(…)\`, input \`prompt(…)\`. \`parseInt(…)\` turns the input into a whole number. -- Arrays go in square brackets: \`let numbers = [2, 4, 6];\`. -- \`"use strict";\` at the very top makes JavaScript complain about typos in variable names. -- JavaScript has only one number type. If the structure chart divides two \`int\` values, VisuStruct writes \`Math.trunc(a / b)\`. Then the digits after the decimal point are dropped, just like in Java. - -::: angeberwissen -JavaScript has almost nothing to do with Java. Brendan Eich built it in 1995 in just ten days for the Netscape browser. First it was called Mocha, then LiveScript. Because everyone was excited about Java at the time, it was renamed JavaScript shortly before launch, for advertising reasons. People have been mixing the two up ever since. Programmers say: Java is to JavaScript as car is to carpet. -::: - -## Run JavaScript in the browser - -JavaScript is the language of browsers. That is why VisuStruct can **run the code straight away**: - -::: angeberwissen -Why are links in the browser blue, anyway? In 1985 Ben Shneiderman's team tested which colour is easy to see but does not get in the way when you read. Light blue won. Red stood out more, but people remembered the text less well afterwards. -::: - -1. Choose **JavaScript** in the code view. -2. Click **Test in browser**. -3. Whatever \`console.log\` outputs appears under **Output**. If the program asks for a value with \`prompt\`, type it in and click **OK**. -4. **Stop** cancels the program. - -VisuStruct stops an endless loop \`while (true)\` by itself after 100,000 passes and reports **Execution stopped: possible infinite loop.** - -> In JavaScript code, a **Call** needs a subprogram with that name, otherwise JavaScript reports an error. Unlike the simulation, the browser does not look in the other tabs. - -## Exercise: Python as a structure chart - -This Python program outputs all even numbers from 0 up to a limit: - -\`\`\` -limit = int(input("Up to which number?")) -for i in range(0, limit + 1, 2): - print(i) -\`\`\` - -\`range(0, limit + 1, 2)\` means: start at 0, stop before \`limit + 1\`, count in steps of two. - -::: übung l18-python -titel: Even numbers -bereich: beide -auftrag: Rebuild the Python program above as a structure chart with a for loop. With the limit 7 it outputs 0, 2, 4 and 6. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: \`range(0, limit + 1, 2)\` counts from 0 up to the limit in steps of two. That is a for loop. -tipp: The three fields of the for loop are \`int i = 0\`, \`i <= limit\` and \`i = i + 2\`. -lösung: l18-python-loesung.visustruct -::: - -## Exercise: JavaScript as a structure chart - -\`\`\` -let age = parseInt((prompt("How old are you?") ?? "0"), 10); -if (age >= 16) { - console.log("You may vote."); -} else { - let years = 16 - age; - console.log("Wait " + years + " more years."); -} -\`\`\` - -::: übung l18-js -titel: Voting from 16 -bereich: beide -auftrag: Rebuild the JavaScript code above as a structure chart. Then test in the code view with **JavaScript** and **Test in browser** whether your code outputs the same as the simulation. -test: 12 -> Wait 4 more years. -test: 16 -> You may vote. -test: 13 -> Wait 3 more years. -test: 40 -> You may vote. -braucht: verzweigung -tipp: Read the code line by line. \`if\` and \`else\` become a branch. -tipp: \`prompt\` and \`parseInt\` together are one input: \`input: int age "How old are you?"\`. The false path holds two boxes: first \`int years = 16 - age\`, then the output. -lösung: l18-js-loesung.visustruct -::: - -## Game: Which language? - -::: spiel frage -titel: Game: Which language? -intro: Ludwig has mixed up lines from three languages. Can you tell where each comes from? Eight rounds. -frage: Which language is this written in? -antworten: Java | Python | JavaScript - -runde: - print(number) -richtig: Python -> In Python the output is called \`print(…)\`, without a semicolon. - -runde: - console.log(number); -richtig: JavaScript -> \`console.log\` is the output in JavaScript. - -runde: - System.out.println(number); -richtig: Java -> The long output with \`System.out\` only exists in Java. - -runde: - let numbers = [2, 4, 6]; -richtig: JavaScript -> \`let\` and square brackets: an array in JavaScript. - -runde: - for i in range(5): -richtig: Python -> A colon instead of curly brackets, and \`range\`: typical Python. - -runde: - int[] numbers = {2, 4, 6}; -richtig: Java -> Only Java writes the data type \`int[]\` in front. - -runde: - number = int(input("Start value?")) -richtig: Python -> No data type in front of the variable, no semicolon, and \`int(…)\` around the input. - -runde: - let number = parseInt(prompt("Start value?"), 10); -richtig: JavaScript -> \`prompt\` asks for a value in the browser, \`parseInt\` turns it into a number. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can choose and compare Python and JavaScript in the code view. → The same diagram, three languages -- I can explain that Python forms blocks by indentation and needs no data types. → Python -- I can explain what VisuStruct adjusts when translating to Python. → What to watch out for in Python -- I can run JavaScript with Test in browser. → Run JavaScript in the browser -::: - -## Quiz - -::: quiz -? How does Python recognise which lines belong to a loop? -→ Python -- [ ] By the curly brackets -- [x] By the indentation -- [ ] By the semicolon at the end of the line - -? What is \`output: sum\` in JavaScript? -→ The same diagram, three languages -- [ ] print(sum) -- [ ] System.out.println(sum); -- [x] console.log(sum); - -? Which language can VisuStruct run straight in the browser? -→ Run JavaScript in the browser -- [ ] Java -- [ ] Python -- [x] JavaScript -> Every browser can run JavaScript. For Java and Python you need a program of its own. - -? What happens in Python with \`print("Sum: " + sum)\` if sum is the number 7? -→ What to watch out for in Python -- [ ] Sum: 7 is output. -- [x] Python reports an error, because text and a number cannot be joined with +. -- [ ] Sum: is output. -> str(sum) turns the number into text. -::: -`,Kw=`--- -titel: When the program crashes: try and catch -bereich: Catching errors -kurz: Use real Java to read error messages and catch crashes with try and catch. ---- - -## Skills I will gain now - -::: kompetenzen -- switch on real Java in the Code area -- read the exception and the line from an error message -- catch an exception with try and catch -- turn text into a number with Integer.parseInt and catch the NumberFormatException -- write a program that divides safely, even by 0 -::: - -## Crashed! - -This program should print three grades. Type it into the [Code](#zeige-codelive) area and click [▶︎ Run](#zeige-ausfuehren): - -\`\`\`java -int[] grades = {2, 3, 1}; -System.out.println("First grade: " + grades[0]); -System.out.println("Fourth grade: " + grades[3]); -System.out.println("Done!"); -\`\`\` - -The first grade appears, then it stops. VisuStruct reports “Invalid expression: grades[3]”, and “Done!” never shows up. The array only has the slots 0, 1 and 2; there is no slot 3. - -::: ludwig -Has this ever happened to you? One small mistake, and the whole program is gone. It happened to me so often that I cried in front of the screen. But there is a solution, and it is called **try** and **catch**. -::: - -::: angeberwissen -In 1947 Grace Hopper's team found a moth in the Mark II computer at Harvard University that had caused a fault. They taped it into the logbook and wrote: “First actual case of bug being found”. Since then, program errors have been called **bugs**. -::: - -## Switching on real Java - -The Code area normally calculates with the same simulation as the structure chart. It simply stops when there is an error. To see what real Java does, switch on real Java: - -1. Click the [gear](#zeige-einstellungen) at the top right and choose Java as the **Programming language**. -2. In the Java section, tick **Real Java (with Swing)**. - -The first time you run a program, VisuStruct downloads about 40 MB once. After that your code is compiled and run just like in a real Java development environment. - -> With real Java, input from the console (\`Scanner\`) does not work yet. That is why our examples work without input. - -Now write the program as a whole class, so that the line numbers match, and run it: - -\`\`\`java -public class Grades { - public static void main(String[] args) { - int[] grades = {2, 3, 1}; - System.out.println("First grade: " + grades[0]); - System.out.println("Fourth grade: " + grades[3]); - System.out.println("Done!"); - } -} -\`\`\` - -The [console](#zeige-konsole) now shows: - -\`\`\` -First grade: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Grades.main(Grades.java:5) -\`\`\` - -## Reading the error message - -A message like this looks scary at first. But it tells you exactly what is going on: - -| Part | Meaning | -|---|---| -| \`ArrayIndexOutOfBoundsException\` | The kind of error: the index is outside the array. | -| \`3\` | The wrong index. | -| \`Grades.java:5\` | The line where it happened: line 5. | - -Java calls a runtime error like this an **exception**. We say: Java *throws* an exception. If nobody catches it, the program stops. That is why “Done!” does not appear. - -::: ludwig -Always read error messages from the top: first the name of the exception, then the line number. In the Code area the line numbers are on the left next to the code. -::: - -## try and catch - -With **try** and **catch** you catch the exception. Change the line with \`grades[3]\` like this: - -\`\`\`java -try { - System.out.println("Fourth grade: " + grades[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("This grade does not exist."); -} -System.out.println("Done!"); -\`\`\` - -- \`try\` means “give it a go”. Java runs the block in curly brackets as usual. -- If a line in it throws an exception, Java jumps straight into the \`catch\` block. The rest of the \`try\` block is skipped. -- The brackets after \`catch\` say which exception is caught, plus a name for it, usually \`e\`. -- After that the program carries on as normal. Now “Done!” appears too. - -If everything in the \`try\` block goes well, the \`catch\` block is simply skipped. - -::: ludwig -try and catch are like the net under the trapeze at the circus: as long as everything works, you don't notice it. If someone falls, the net catches them and the show goes on. -::: - -> The structure chart has no box for try and catch. So VisuStruct shows the whole block as one statement. You can only run it with real Java. - -::: angeberwissen -In July 1969, Apollo 11 landed on the Moon. Shortly before touchdown, the onboard computer suddenly reported the errors **1202** and **1201**: it had more work than it could handle. Still, it did not crash. **Margaret Hamilton**'s team had built the software so that it recognised such errors, put aside the unimportant jobs and kept only the most important ones running. The landing succeeded. - -Margaret Hamilton led the group at MIT that wrote the software for the Apollo spacecraft. She always planned for things going wrong, just like you do with \`try\` and \`catch\`. So that software would be taken as seriously as hardware, she called her work **software engineering**. Today a whole field of computer science has that name. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (born 1936) in 1969, next to the printed programs her team wrote for Apollo (photo: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), public domain). -::: - -## Turning text into a number - -Often a number is text at first, for example later in a text field in a window. \`Integer.parseInt\` turns text into a whole number: - -\`\`\`java -String input = "twelve"; -try { - int number = Integer.parseInt(input); - System.out.println("Double: " + number * 2); -} catch (NumberFormatException e) { - System.out.println("That is not a number: " + input); -} -\`\`\` - -Java cannot convert “twelve” and throws a \`NumberFormatException\`. The program prints “That is not a number: twelve”. Change the first line to \`String input = "12";\` and run it again: now “Double: 24” appears. - -## Exceptions are objects - -This is not in the curriculum, but it is useful to know. - -::: angeberwissen -Every exception is an object of its own class, for example \`ArithmeticException\` for dividing by 0. \`e.getMessage()\` gives you its message, such as \`/ by zero\`. - -A \`try\` may be followed by several \`catch\` blocks, one for each kind of error. \`catch (Exception e)\` catches all exceptions, but then tells you less about what happened. And a \`finally { … }\` block at the end always runs, whether there was an error or not. -::: - -## Game: Does the net catch it? - -::: spiel frage -titel: Game: Does the net catch it? -intro: Ludwig is swinging on the trapeze. Do try and catch save him, or does the program crash? Seven rounds. - -runde: - int[] grades = {2, 3, 1}; - System.out.println(grades[3]); - System.out.println("Done!"); -frage: What happens with real Java? -- 1 and Done! appear. -- Only Done! appears. -- [x] The program crashes with an ArrayIndexOutOfBoundsException. -> There is no slot 3. Nobody catches the exception, so Done! never arrives. - -runde: - int[] grades = {2, 3, 1}; - try { - System.out.println(grades[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("This grade does not exist."); - } - System.out.println("Done!"); -frage: What is output? -- Done! -- [x] This grade does not exist.\\nDone! -- This grade does not exist. -> The catch block catches the exception, and after that the program carries on as normal. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: What is output? -- A\\nB\\nC -- [x] A\\nC -- C -> At 10 / 0, Java jumps straight into the catch block. The rest of the try block, B, is skipped. - -runde: - String input = "12"; - try { - int number = Integer.parseInt(input); - System.out.println("Double: " + number * 2); - } catch (NumberFormatException e) { - System.out.println("That is not a number: " + input); - } -frage: What is output? -- [x] Double: 24 -- That is not a number: 12 -- Double: 1212 -> "12" can be converted. If everything goes well in the try block, catch is skipped. - -runde: -frage: Which exception does \`Integer.parseInt("2.5")\` throw? -- ArithmeticException -- [x] NumberFormatException -- none, the result is 2 -> parseInt only converts whole numbers. With a point it isn't a valid format. - -runde: -frage: Which exception does \`12 / 0\` throw with \`int\`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Dividing by 0 doesn't work with whole numbers. The message is \`/ by zero\`. - -runde: - try { - int pieces = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Error!"); - } - System.out.println("Done!"); -frage: What happens? -- Error! and Done! appear. -- Only Done! appears. -- [x] The program crashes. -> catch only catches the exception named in the brackets. An ArithmeticException falls through the net. -::: - -## Task: dividing safely - -This program crashes: - -\`\`\`java -int cake = 12; -int people = 0; -System.out.println("Everyone gets " + cake / people + " pieces."); -System.out.println("Enjoy!"); -\`\`\` - -1. Run it with real Java and read the error message. Which exception is it? -2. Catch it with try and catch. Instead of the crash, “You can't share without people.” should appear, and “Enjoy!” should still be printed. -3. Set \`people\` to 4 and check that “Everyone gets 3 pieces.” appears. - -::: ludwig -The exception is called \`ArithmeticException\`. Only put the line that can crash into the \`try\` block. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can switch on real Java in the Code area. → Switching on real Java -- I can read the exception and the line from an error message. → Reading the error message -- I can catch an exception with try and catch. → try and catch -- I can turn text into a number with Integer.parseInt and catch the NumberFormatException. → Turning text into a number -- I can write a program that divides safely, even by 0. → Task: dividing safely -::: - -## Quiz - -::: quiz -? What happens if an exception is not caught? -→ Reading the error message -- [ ] Java skips the line and carries on. -- [x] The program stops. -- [ ] The computer restarts. -> That is why “Done!” does not appear in the first example. - -? What happens in the try block after the line that throws an exception? -→ try and catch -- [ ] The remaining lines run anyway. -- [x] The rest is skipped and Java jumps into the catch block. -- [ ] Java starts the try block again. - -? Which exception does Integer.parseInt("twelve") throw? -→ Turning text into a number -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> “twelve” is not in a number format that Java understands. - -? Match each exception to its cause. -→ Reading the error message -= ArrayIndexOutOfBoundsException -> index outside the array -= ArithmeticException -> dividing by 0 -= NumberFormatException -> text is not a number -::: -`,Xw=`--- -titel: Reading and saving text -bereich: Files -kurz: Read a text file line by line with Scanner and save a file with PrintWriter. ---- - -## Skills I will gain now - -::: kompetenzen -- explain why programs save data in files -- read a text file line by line with Scanner, hasNextLine and nextLine -- explain why Java requires try and catch or throws for files -- write a file with PrintWriter and finish it with close -- read a file, add to it and save it again with numbers -::: - -## Why files? - -Everything stored in variables is gone as soon as the program ends. If a program should remember something, for example a game score, a list or grades, it saves the data in a **file**. The next time it starts, it reads the file again. - -Zora is free, and Ludwig is planning a party. His shopping list is ready in the file \`shopping.txt\`: - -::: datei shopping.txt -Hay -Apples -Carrots -Dandelions -::: - -A file ending in \`.txt\` contains only text, line by line. You can open it with any editor, for example Notepad on Windows or TextEdit on a Mac. - -## Reading a file with Scanner - -You already know the \`Scanner\`: \`new Scanner(System.in)\` reads from the keyboard. If you give it a file instead of \`System.in\`, it reads from the file: - -\`\`\`java -import java.io.*; -import java.util.Scanner; - -Scanner file = new Scanner(new File("shopping.txt")); -while (file.hasNextLine()) { - String line = file.nextLine(); - System.out.println(line); -} -file.close(); -\`\`\` - -| Method | Meaning | -|---|---| -| \`new File("shopping.txt")\` | the file with this name | -| \`hasNextLine()\` | Is there another line? \`true\` or \`false\` | -| \`nextLine()\` | returns the next line and moves on by one line | -| \`close()\` | closes the file when you are done | - -The loop runs as long as there is another line. In each pass, \`nextLine()\` fetches the next line. \`File\` belongs to the package \`java.io\`, so you need \`import java.io.*;\`. - -::: diagramm dt-lesen.visustruct -This is what reading looks like in a structure chart. Try it in the simulation. -::: - -::: ludwig -Imagine the Scanner as a finger pointing at a line. \`nextLine()\` reads the line under the finger and moves the finger down one line. Once the finger is below the last line, \`hasNextLine()\` returns \`false\`. -::: - -## What if the file is missing? - -If the file does not exist, Java cannot read it and throws a \`FileNotFoundException\`. Java insists that your program is prepared for this. Either you catch it with try and catch, as in lesson 22: - -\`\`\`java -try { - Scanner file = new Scanner(new File("shopping.txt")); - // ... read ... - file.close(); -} catch (FileNotFoundException e) { - System.out.println("The file shopping.txt is missing!"); -} -\`\`\` - -Or you write \`throws Exception\` after \`main\`. It means: "This method may throw an exception, I won't deal with it." Then the program stops if the file is missing. - -\`\`\`java -public static void main(String[] args) throws Exception { -\`\`\` - -> VisuStruct adds \`throws Exception\` after \`main\` automatically as soon as your program works with files. In the simulation, VisuStruct reports a missing file with "There is no file called …". - -## Exercise: Numbering the shopping list - -::: übung l23-java-nummern -titel: Numbering the shopping list -code: java -auftrag: Read \`shopping.txt\` line by line and print each line with its number: **1. Hay**, **2. Apples** and so on. -start: l23-java-nummern-start.visustruct -test: - -> 1. Hay | 2. Apples | 3. Carrots | 4. Dandelions -braucht: whileSchleife -tipp: Create \`int number = 1;\` before the loop and increase the number at the end of each pass. -tipp: The output is \`System.out.println(number + ". " + line);\` -lösung: l23-java-nummern-loesung.visustruct -::: - -## Writing to a file with PrintWriter - -For writing there is the \`PrintWriter\`. Its methods have the same names as those of \`System.out\`: - -\`\`\`java -PrintWriter out = new PrintWriter("greeting.txt"); -out.println("Hello Zora!"); -out.println("See you tomorrow, Ludwig"); -out.close(); -\`\`\` - -Afterwards \`greeting.txt\` contains: - -\`\`\` -Hello Zora! -See you tomorrow, Ludwig -\`\`\` - -- \`println\` writes the text and then starts a new line, \`print\` writes without a new line. -- If the file already exists, it is **overwritten**. The old content is gone. -- After the run, VisuStruct shows below the output what was saved in the file. - -::: ludwig -Don't forget \`close()\`! The PrintWriter first collects the text in a buffer and only writes it to the file when it is closed. Without \`close()\` the file stays empty. I once lost a whole story that way. -::: - -## Exercise: Adding to the shopping list - -::: übung l23-java-ergaenzen -titel: Adding to the shopping list -code: java -auftrag: Ludwig still needs cake. Read \`shopping.txt\` and write each line to the new file \`shopping-new.txt\`. Then add **Cake** as the last line. At the end, print how many lines were saved: **5 lines saved**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 lines saved -datei: shopping-new.txt -> Hay | Apples | Carrots | Dandelions | Cake -braucht: whileSchleife -tipp: In the loop, read a line with \`file.nextLine()\` and write it straight out again with \`out.println(line)\`. -tipp: After the loop comes \`out.println("Cake");\`. Close both files with \`close()\`, otherwise \`shopping-new.txt\` stays empty. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Game: What is in the file? - -::: spiel frage -titel: Game: What is in the file? -intro: Ludwig writes files with the PrintWriter. Read the code and predict what is in the file afterwards. Six rounds. -frage: What is in the file afterwards? - -runde: - PrintWriter out = new PrintWriter("greeting.txt"); - out.println("Hello"); - out.println("Zora"); - out.close(); -- [x] Hello\\nZora -- HelloZora -- Zora -> \`println\` starts a new line after each text. - -runde: - PrintWriter out = new PrintWriter("word.txt"); - out.print("He"); - out.print("llo"); - out.println("!"); - out.close(); -- He\\nllo\\n! -- [x] Hello! -- He llo ! -> \`print\` carries on without a new line, so the parts are joined directly. - -runde: - PrintWriter out = new PrintWriter("secret.txt"); - out.println("The treasure is in the tower"); -- The treasure is in the tower -- [x] Nothing, the file stays empty. -- Java reports an error. -> Without \`close()\` the text never reaches the file. It stays in the buffer. - -runde: - // Before, note.txt contains: Old - PrintWriter out = new PrintWriter("note.txt"); - out.println("New"); - out.close(); -- Old\\nNew -- [x] New -- New\\nOld -> A new PrintWriter overwrites the file. The old content is gone. - -runde: - PrintWriter out = new PrintWriter("numbers.txt"); - for (int i = 1; i <= 3; i++) { - out.println(i * 2); - } - out.close(); -- 1\\n2\\n3 -- 246 -- [x] 2\\n4\\n6 -> The loop writes 2, 4 and 6, each number on its own line. - -runde: - // shopping.txt contains: Hay, Apples (one per line) - Scanner file = new Scanner(new File("shopping.txt")); - PrintWriter out = new PrintWriter("big.txt"); - while (file.hasNextLine()) { - out.println(file.nextLine().toUpperCase()); - } - file.close(); - out.close(); -- Hay\\nApples -- [x] HAY\\nAPPLES -- HAYAPPLES -> Each line is read, turned into capital letters and written on its own line. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain why programs save data in files. → Why files? -- I can read a text file line by line with Scanner, hasNextLine and nextLine. → Reading a file with Scanner -- I can explain why Java requires try and catch or throws for files. → What if the file is missing? -- I can write a file with PrintWriter and finish it with close(). → Writing to a file with PrintWriter -- I can read a file, add to it and save it again with numbers. → Exercise: Adding to the shopping list -::: - -## Quiz - -::: quiz -? What does hasNextLine() return? -→ Reading a file with Scanner -- [ ] the next line -- [x] true if there is another line, otherwise false -- [ ] the number of lines -> With hasNextLine() the loop checks whether to carry on. - -? What happens if you call new PrintWriter("list.txt") for a file that already exists? -→ Writing to a file with PrintWriter -- [ ] The new text is added at the end. -- [x] The file is overwritten. -- [ ] Java reports an error. - -? Why does a file stay empty even though println was called? -→ Writing to a file with PrintWriter -- [ ] Because println only writes to the console. -- [x] Because close() is missing. -- [ ] Because the file name ends in .txt. -> Only close() writes the buffer into the file. - -? Match each method to its job. -→ Reading a file with Scanner -= nextLine() -> reads the next line -= println(text) -> writes a line -= close() -> closes the file -::: -`,ey=`--- -titel: Line by line with BufferedReader -bereich: Files -kurz: Read with BufferedReader and readLine until null comes, and read a whole file at once with Files. ---- - -## Skills I will gain now - -::: kompetenzen -- read a file with BufferedReader and readLine -- spot the end of a file, because readLine returns null there -- count how often a word appears in a file -- read a whole file with Files.readString and split it into lines with split -- find the longest entry in a file -::: - -## The cake is gone! - -The morning after the party, Ludwig's cake has disappeared. Luckily the castle keeps a logbook. Every line starts with the day and the time: - -::: datei logbook.txt -Mo 08:15 Ludwig feeds the goats -Mo 12:30 Zora reads in the tower -Mo 18:00 Fox sneaks past the gate -Tu 09:00 Ludwig bakes a cake -Tu 14:45 Hare brings carrots -Tu 16:20 Fox asks for the recipe -Tu 23:55 The cake is gone -We 07:00 Zora finds crumbs at Fox's den -::: - -You could already read it with the Scanner from lesson 23. For files, though, professionals often use the **BufferedReader**. - -## Reading with BufferedReader - -\`\`\`java -import java.io.*; - -BufferedReader reader = new BufferedReader(new FileReader("logbook.txt")); -String line = reader.readLine(); -while (line != null) { - System.out.println(line); - line = reader.readLine(); -} -reader.close(); -\`\`\` - -- \`new FileReader("logbook.txt")\` opens the file, and \`new BufferedReader(…)\` puts a buffer around it. It fetches the file in large chunks and is therefore faster for long files. -- \`readLine()\` returns the next line. When there are no more lines, it returns **null**. -- That is why the loop runs as long as \`line != null\`. At the end of each pass it reads the next line. - -::: diagramm dt-buffered.visustruct -First read a line, then check, then read the next one at the end of the pass. -::: - -**null** means "nothing at all". It is not the empty text \`""\`, but no text. A variable holding \`null\` has no methods: \`line.length()\` then stops with a \`NullPointerException\`. So always check \`line != null\` first. - -| | Scanner | BufferedReader | -|---|---|---| -| Create | \`new Scanner(new File(…))\` | \`new BufferedReader(new FileReader(…))\` | -| Is there another line? | \`hasNextLine()\` | \`readLine()\` returns \`null\` | -| Next line | \`nextLine()\` | \`readLine()\` | -| Strength | can also read numbers (\`nextInt()\`) | fast, even with large files | - -::: ludwig -The loop looks different from the Scanner one: the first line is read **before** the loop, every further one **at the end** of the pass. If you forget the read at the end, the program reads the same line again and again and never stops. -::: - -::: angeberwissen -Where a line ends is not the same everywhere: Windows writes two invisible characters, “carriage return” and “line feed”, Linux and macOS only the line feed. The names come from the typewriter, where the carriage moved back and the paper moved up one line. \`readLine()\` copes with both. -::: - -## Exercise: How often does the fox appear? - -::: übung l24-java-fuchs -titel: How often does the fox appear? -code: java -auftrag: Read the logbook with the BufferedReader and count the lines that contain **Fox**. At the end, print **Fox:** and the count. -start: l24-java-fuchs-start.visustruct -test: - -> Fox: 3 -braucht: whileSchleife, verzweigung -tipp: Create \`int count = 0;\` before the loop. In the loop, check \`line.contains("Fox")\`. -tipp: The \`line = reader.readLine();\` belongs at the end of the loop, after the if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Closing automatically - -So that \`close()\` isn't forgotten even after an exception, there is a short form of try: whatever is created in the round brackets after \`try\` is closed by Java on its own at the end. - -\`\`\`java -try (BufferedReader reader = new BufferedReader(new FileReader("logbook.txt"))) { - String line = reader.readLine(); - while (line != null) { - System.out.println(line); - line = reader.readLine(); - } -} catch (IOException e) { - System.out.println("The logbook is missing."); -} -\`\`\` - -This is called **try-with-resources**. \`IOException\` catches all errors while reading and writing, including a missing file. - -> Like try and catch, try-with-resources only runs with real Java (lesson 22). In the simulation, you write \`close()\` yourself. - -## The shortcut: Files - -Since Java 11 it is even shorter. \`Files.readString\` reads the whole file at once, as you know from lesson 17. With \`split("\\n")\` you split the text into an array of lines: - -\`\`\`java -import java.nio.file.*; - -String text = Files.readString(Path.of("logbook.txt")); -String[] lines = text.split("\\n"); -for (int i = 0; i < lines.length; i++) { - System.out.println(lines[i]); -} -\`\`\` - -Now all lines are in the array. You can look at them in any order, for example backwards or only every second one. - -| Way | Good for | -|---|---| -| \`Scanner\` | small files, reading numbers and words | -| \`BufferedReader\` | large files, line by line | -| \`Files.readString\` | small files that fit into the program as a whole | - -::: ludwig -A file with a million lines is better read line by line with the BufferedReader. With \`Files.readString\` the whole file would have to go into memory at once. -::: - -## Exercise: The longest entry - -::: übung l24-java-laengster -titel: The longest entry -code: java -auftrag: In the start code, the lines of the logbook are already in the array \`lines\`. Find the longest line and print it like this: **Longest entry:** followed by the line. -start: l24-java-laengster-start.visustruct -test: - -> Longest entry: We 07:00 Zora finds crumbs at Fox's den -braucht: forSchleife, verzweigung -tipp: Remember the longest line so far in \`String longest = lines[0];\` and go through the other lines with a for loop. -tipp: If \`lines[i].length() > longest.length()\`, then \`lines[i]\` becomes the new longest. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Game: File detective - -::: spiel frage -titel: Game: File detective -intro: Who took the cake? Each program searches the **logbook** from the top. Predict what it prints and collect the clues. Five rounds. -frage: What does the program print? - -runde: - String line = reader.readLine(); - line = reader.readLine(); - System.out.println(line.substring(9)); -- Ludwig feeds the goats -- [x] Zora reads in the tower -- Mo 12:30 Zora reads in the tower -> The second \`readLine()\` returns the second line. The text after the time starts at index 9. - -runde: - int n = 0; - String line = reader.readLine(); - while (line != null) { - if (line.startsWith("Tu")) n++; - line = reader.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Four entries are from Tuesday. - -runde: - String line = reader.readLine(); - while (line != null) { - if (line.contains("cake")) { - System.out.println(line.substring(3, 8)); - } - line = reader.readLine(); - } -- [x] 09:00\\n23:55 -- 09:00 -- Tu\\nTu -> Two lines contain "cake". \`substring(3, 8)\` cuts out the time. - -runde: - String line = reader.readLine(); - String last = ""; - while (line != null) { - if (line.contains("Fox")) last = line; - line = reader.readLine(); - } - System.out.println(last); -- Tu 16:20 Fox asks for the recipe -- [x] We 07:00 Zora finds crumbs at Fox's den -- Mo 18:00 Fox sneaks past the gate -> \`last\` is overwritten at every match. At the end it holds the last match. "Fox's den" contains "Fox" too. - -runde: - String line = reader.readLine(); - while (line != null) { - if (line.contains("crumbs")) { - int at = line.indexOf("at "); - System.out.println(line.substring(at + 3)); - } - line = reader.readLine(); - } -- [x] Fox's den -- at Fox's den -- crumbs -> The crumbs are at Fox's den. The fox asked for the recipe, and at 23:55 the cake was gone. Case solved! -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can read a file with BufferedReader and readLine. → Reading with BufferedReader -- I can spot the end of a file, because readLine returns null there (line != null). → Reading with BufferedReader -- I can count how often a word appears in a file. → Exercise: How often does the fox appear? -- I can read a whole file with Files.readString and split it into lines with split. → The shortcut: Files -- I can find the longest entry in a file. → Exercise: The longest entry -::: - -## Quiz - -::: quiz -? What does readLine() return when the file has ended? -→ Reading with BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null means: there is no more text. - -? Why is there another line = reader.readLine() at the end of the loop? -→ Reading with BufferedReader -- [ ] So that the file is closed. -- [x] Otherwise line never changes and the loop never stops. -- [ ] So that the first line is printed twice. - -? What does try-with-resources do? -→ Reading with BufferedReader -- [ ] It reads the file faster. -- [x] It closes the file on its own at the end. -- [ ] It prevents every exception. - -? Match each way to what it is good for. -→ The shortcut: Files -= BufferedReader -> large files, line by line -= Files.readString -> small files all at once -= Scanner -> reading numbers and words -::: -`,ty=`--- -titel: Tables as CSV -bereich: Files -kurz: Read a table as a CSV file, split each line with split and calculate with the numbers. ---- - -## Skills I will gain now - -::: kompetenzen -- explain how a CSV file is built: header, records, separator -- split a line with split and make a number out of it with Integer.parseInt -- skip the header before calculating -- calculate an average from a column of a CSV file -- find the best value in a column of a CSV file -- name one strength and one weakness of CSV -::: - -## A table in a text file - -Mrs Goat, the teacher at the castle school, has saved her class's grades as a table. As in Germany, 1 is the best grade and 6 the worst: - -| Name | Maths | German | English | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Fox | 4 | 3 | 5 | -| Hare | 2 | 1 | 3 | - -In the file, the table looks like this: - -::: datei class.csv -Name;Maths;German;English -Ludwig;2;3;1 -Zora;1;2;2 -Fox;4;3;5 -Hare;2;1;3 -::: - -This is a **CSV file**. CSV stands for "Comma-Separated Values". Each line is a row of the table, and the columns are separated by a separator character. In English-speaking countries this is usually a comma, in Germany and many other countries a **semicolon** \`;\`, because the comma is already used in numbers like \`2,5\`. We use the semicolon. - -- The first line is the **header**. It says what is in the columns. -- Every further line is a **record**, here one pupil. -- Excel, LibreOffice and Numbers open CSV files directly as a table. - -## Splitting a line with split - -You know \`split(";")\` from lesson 17. It splits a text at every semicolon and returns an array: - -\`\`\`java -String line = "Zora;1;2;2"; -String[] parts = line.split(";"); -// parts[0] is "Zora", parts[1] is "1", parts[2] is "2", parts[3] is "2" -\`\`\` - -Afterwards the grades are still **text**. You can only calculate with them once you have turned them into a number with \`Integer.parseInt\`: - -\`\`\`java -int maths = Integer.parseInt(parts[1]); -\`\`\` - -::: diagramm dt-csv.visustruct -Split a CSV line and read the maths grade as a number. -::: - -## Reading the whole file - -For the whole file, you combine the BufferedReader from lesson 24 with \`split\`. The header contains no grades, so you read it before the loop and skip it: - -\`\`\`java -BufferedReader reader = new BufferedReader(new FileReader("class.csv")); -String line = reader.readLine(); // header -line = reader.readLine(); // first record -while (line != null) { - String[] parts = line.split(";"); - System.out.println(parts[0] + ": Maths " + parts[1]); - line = reader.readLine(); -} -reader.close(); -\`\`\` - -::: ludwig -Without the second \`readLine()\` before the loop, the header ends up in the loop. Then \`Integer.parseInt("Maths")\` tries to make a number out of the word, and Java stops with a \`NumberFormatException\`. -::: - -## Exercise: Maths average - -::: übung l25-java-schnitt -titel: Maths average -code: java -auftrag: Calculate the average maths grade. Skip the header, add up the maths grades (column 1) and divide by the number of records. Print **Maths average:** and the average. -start: l25-java-schnitt-start.visustruct -test: - -> Maths average: 2.25 -braucht: whileSchleife -tipp: Create \`double sum = 0;\` and \`int count = 0;\` before the loop. With \`double\`, nothing is cut off when dividing. -tipp: In the loop: \`String[] parts = line.split(";");\`, then \`sum = sum + Integer.parseInt(parts[1]);\` and \`count++;\`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Exercise: Best grade in English - -::: übung l25-java-beste -titel: Best grade in English -code: java -auftrag: Who has the best grade in English (column 3)? Print **Best grade in English:**, the name and the grade in brackets. -start: l25-java-beste-start.visustruct -test: - -> Best grade in English: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Remember the best grade so far in \`int bestGrade = 7;\` and the name in \`String bestName = "";\`. Every real grade is better than 7. -tipp: If \`grade < bestGrade\`, remember the new grade and \`parts[0]\` as the name. -lösung: l25-java-beste-loesung.visustruct -::: - -## Strengths and weaknesses of CSV - -| Good | Tricky | -|---|---| -| Tables take up little space. | If the separator appears in the text, for example \`Hare;very good; hard-working\`, the line falls apart into too many parts. | -| Excel and other programs open the file directly. | Everything is a flat table. If an animal has several favourite foods, that doesn't fit well into one cell. | -| Every program can read it with \`split\`. | The file doesn't say whether \`1\` is a number or a text. | - -::: ludwig -Excel saves CSV files with semicolons in Germany and with commas in England and the USA. That is why someone opening a file from another country sometimes sees everything in one single column. -::: - -## Game: Broken line - -::: spiel bombe -titel: Game: Broken line -intro: In every CSV file **exactly one line is broken**, and the bomb is ticking. Compare each line with the header and tap the broken line before time runs out. Five rounds, 20 seconds each. -zeit: 20 -daten: csv - -runde: - Name;Maths;German;English - Ludwig;2;3;1 - Zora;1 - Fox;4;3;5 -frage: Every line needs four columns. -fehler: 3 -besser: Zora;1;2;2 -> Two grades are missing for Zora. Then there is no \`parts[3]\`, and Java stops with an \`ArrayIndexOutOfBoundsException\`. - -runde: - Name;Grade;Comment - Ludwig;2;works well with others - Hare;1;very good; hard-working - Zora;1;reads a lot -frage: Every line needs three columns. -fehler: 3 -besser: Hare;1;very good, hard-working -> The semicolon in the comment splits off a fourth column. The separator must not appear in the text. - -runde: - Name;Maths;German;English - Ludwig;2;3;1 - Zora;1;2;2 - Fox,4,3,5 -frage: The separator is a semicolon. -fehler: 4 -besser: Fox;4;3;5 -> Here there are commas instead of semicolons. \`split(";")\` finds no semicolon and returns the whole line as a single part. - -runde: - Name;Maths;German;English - Hare;2;1;3 - Fox;four;3;5 - Ludwig;2;3;1 -frage: Grades are numbers. -fehler: 3 -besser: Fox;4;3;5 -> \`Integer.parseInt("four")\` cannot make a number out of the word and throws a \`NumberFormatException\`. - -runde: - Name;Maths;German;English - Zora;1;2;2 - Hare;2;1;3 - Ludwig;2;;1 -frage: No field may be empty. -fehler: 4 -besser: Ludwig;2;3;1 -> There is nothing between the two semicolons. \`Integer.parseInt("")\` throws a \`NumberFormatException\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain how a CSV file is built: header, records, separator. → A table in a text file -- I can split a line with split and make a number out of it with Integer.parseInt. → Splitting a line with split -- I can skip the header before calculating. → Reading the whole file -- I can calculate an average from a column of a CSV file. → Exercise: Maths average -- I can find the best value in a column of a CSV file. → Exercise: Best grade in English -- I can name one strength and one weakness of CSV. → Strengths and weaknesses of CSV -::: - -## Quiz - -::: quiz -? What does "Hare;2;1;3".split(";")[2] return? -→ Splitting a line with split -- [ ] "2" -- [x] "1" -- [ ] 1 -> Counting starts at 0. And the result is a text, not a number. - -? Why does the program call readLine() twice before the loop? -→ Reading the whole file -- [ ] So that the file is read faster. -- [x] The first line is the header without grades. It is skipped. -- [ ] Because readLine always needs two lines. - -? What happens with Integer.parseInt("Maths")? -→ Splitting a line with split -- [ ] It returns 0. -- [x] Java throws a NumberFormatException. -- [ ] It returns the length of the word. - -? Match. -→ Strengths and weaknesses of CSV -= header -> says what is in the columns -= record -> a line with values -= separator -> separates the columns, often ; -::: -`,ny=`--- -titel: Data as JSON -bereich: Files -kurz: Understand JSON files, get values out with indexOf and substring, and write JSON yourself. ---- - -## Skills I will gain now - -::: kompetenzen -- recognise objects, keys, values and lists in JSON -- get a value out of a JSON line with indexOf and substring -- output all values of one key from a JSON file -- build and save a JSON entry with + and \\" -- save data as a JSON file -- name one strength and one weakness of JSON -::: - -## Data with names - -The castle zoo stores its animals in a file \`zoo.json\`: - -::: datei zoo.json -{ - "zoo": "Castle Zoo", - "animals": [ - {"name": "Ludwig", "species": "billy goat", "age": 7}, - {"name": "Zora", "species": "goat", "age": 6}, - {"name": "Fox", "species": "red fox", "age": 4}, - {"name": "Hare", "species": "brown hare", "age": 2} - ] -} -::: - -This is **JSON**, the "JavaScript Object Notation". Almost every web app and every game sends and stores data like this. Unlike in a CSV file, each value has its **name** in front of it, the **key**. - -| Character | Meaning | Example | -|---|---|---| -| \`{ … }\` | an **object**: pairs of key and value | \`{"name": "Zora", "age": 6}\` | -| \`"key": value\` | a pair, separated by a colon | \`"age": 6\` | -| \`[ … ]\` | a **list** of values, like an array | \`"animals": [ … ]\` | -| \`,\` | separates the pairs and the entries of a list | | -| \`"…"\` | a text. Numbers and \`true\`/\`false\` have no quotation marks. | \`"goat"\`, \`6\` | - -Objects and lists can be **nested**: the whole zoo object contains the list \`animals\`, and each animal is an object again. As with arrays, the list counts from 0: \`animals[1].name\` is \`"Zora"\`. - -::: angeberwissen -Douglas Crockford made JSON popular in the early 2000s. In the licence of his JSON program he wrote: “The Software shall be used for Good, not Evil.” So the company IBM asked whether it could use it anyway. He replied that IBM and its customers were allowed to use it for evil. -::: - -## Getting a value out - -Professionals read JSON with a library, for example **Gson**. It is not part of Java and has to be added first. For our zoo file, the string methods from lesson 17 are enough, because each animal is on its own line. - -In Java you write a quotation mark inside a text as \`\\"\`. So the text \`"\\"name\\": \\""\` is \`"name": "\`, exactly what comes before each name. - -\`\`\`java -String line = "{\\"name\\": \\"Zora\\", \\"species\\": \\"goat\\", \\"age\\": 6},"; -int start = line.indexOf("\\"name\\": \\"") + 9; -int end = line.indexOf("\\"", start); -String name = line.substring(start, end); -\`\`\` - -- \`indexOf("\\"name\\": \\"")\` finds the start of \`"name": "\`. That is 9 characters, so the name begins 9 characters further on. -- \`indexOf("\\"", start)\` searches for the next quotation mark **from** \`start\`. That is where the name ends. -- \`substring(start, end)\` cuts out the name: \`Zora\`. - -::: diagramm dt-json.visustruct -Getting the name out of a JSON line. -::: - -## Exercise: All the names in the zoo - -::: übung l26-java-namen -titel: All the names in the zoo -code: java -auftrag: Read \`zoo.json\` line by line. In every line that contains \`"name"\`, print only the name. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Fox | Hare -braucht: whileSchleife, verzweigung -tipp: Check with \`line.contains("\\"name\\"")\` whether there is a name in the line. Skip the other lines. -tipp: Inside the if: \`int start = line.indexOf("\\"name\\": \\"") + 9;\`, then \`int end = line.indexOf("\\"", start);\` and \`System.out.println(line.substring(start, end));\`. -lösung: l26-java-namen-loesung.visustruct -::: - -## Writing JSON - -To write, you put the text together with \`+\` and write it to the file with the PrintWriter from lesson 23. Texts in JSON need quotation marks, so \`\\"\` in the Java text, numbers don't: - -\`\`\`java -String name = "Zora"; -int points = 15; -String entry = "{\\"name\\": \\"" + name + "\\", \\"points\\": " + points + "}"; -// entry is {"name": "Zora", "points": 15} -\`\`\` - -In a list there is a comma between the entries, but **none** after the last one. You check that with an if. - -::: ludwig -A single forgotten quotation mark or one comma too many, and no program can read the file any more. JSON is strict. That is why professionals also use a library for writing. -::: - -## Exercise: Saving points as JSON - -::: übung l26-java-schreiben -titel: Saving points as JSON -code: java -auftrag: Save the points from the quiz tournament as a list in \`points.json\`. Each line contains an object with **name** and **points**, and there is a comma between the objects. At the end, print **3 entries saved**. -start: l26-java-schreiben-start.visustruct -test: - -> 3 entries saved -datei: points.json -> [ | {"name": "Ludwig", "points": 12}, | {"name": "Zora", "points": 15}, | {"name": "Fox", "points": 9} | ] -braucht: forSchleife, verzweigung -tipp: Go through the arrays with \`for (int i = 0; i < names.length; i++)\`. Build the entry with \`" {\\"name\\": \\"" + names[i] + "\\", \\"points\\": " + points[i] + "}"\`. -tipp: If \`i < names.length - 1\`, add \`","\`. After the loop, write \`"]"\` and close the file. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Strengths and weaknesses of JSON - -| Good | Tricky | -|---|---| -| Every value has a name. You can see at once what \`6\` means. | The file is longer than a CSV file, because the keys appear on every line. | -| Data can be nested, for example a list inside an object. | Java can't read JSON on its own. You need a library such as Gson. | -| JSON is the standard on the web. Almost every programming language knows it. | One character too many or too few makes the whole file unreadable. | - -## Game: Which key? - -::: spiel frage -titel: Game: Which key? -intro: Ludwig is looking for values in JSON files. Read the file and say which value is meant. Five rounds. - -runde: - {"name": "Zora", "species": "goat", "age": 6} -frage: What is the value of **age**? -- "age" -- [x] 6 -- "goat" -> The key comes before the colon, the value after it. Numbers have no quotation marks. - -runde: - { - "animals": [ - {"name": "Ludwig", "age": 7}, - {"name": "Zora", "age": 6}, - {"name": "Fox", "age": 4} - ] - } -frage: What is **animals[1].name**? -- Ludwig -- [x] Zora -- Fox -> The list counts from 0, like an array. \`animals[1]\` is the second animal. - -runde: - { - "zoo": "Castle Zoo", - "boss": {"name": "Ludwig", "species": "billy goat"} - } -frage: How do you get to **billy goat**? -- [x] boss.species -- species -- zoo.boss.species -> \`species\` is inside the object \`boss\`. So the way goes through \`boss\`. - -runde: - { - "name": "Hare", - "favouriteFood": ["carrots", "clover", "dandelions"] - } -frage: What is **favouriteFood[2]**? -- clover -- carrots -- [x] dandelions -> Index 0 is carrots, 1 is clover, 2 is dandelions. Something like this hardly fits into one cell in CSV. - -runde: - {"name": "Fox", "age": 4, "hungry": true} -frage: Which key has a truth value? -- name -- age -- [x] hungry -> \`true\` and \`false\` have no quotation marks, like numbers. In Java this would be a \`boolean\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can recognise objects, keys, values and lists in JSON. → Data with names -- I can get a value out of a JSON line with indexOf and substring. → Getting a value out -- I can output all values of one key from a JSON file. → Exercise: All the names in the zoo -- I can build and save a JSON entry with + and \\". → Writing JSON -- I can save data as a JSON file. → Exercise: Saving points as JSON -- I can name one strength and one weakness of JSON. → Strengths and weaknesses of JSON -::: - -## Quiz - -::: quiz -? What comes before each value in JSON? -→ Data with names -- [ ] its line number -- [x] its key, for example "name" -- [ ] a semicolon -> Key and value are separated by a colon. - -? How do you write a quotation mark inside a Java text? -→ Writing JSON -- [ ] "" -- [x] \\" -- [ ] '"' - -? In {"animals": [{"name": "Ludwig"}, {"name": "Zora"}]}, what is the value of animals[0].name? -→ Data with names -- [x] Ludwig -- [ ] Zora -- [ ] animals -> The list counts from 0. - -? Match. -→ Strengths and weaknesses of JSON -= { } -> an object with keys and values -= [ ] -> a list -= : -> separates key and value -::: -`,iy=`--- -titel: Comparing formats -bereich: Files -kurz: Compare the same data as txt, CSV and JSON, choose the right format and convert a file into another format. ---- - -## Skills I will gain now - -::: kompetenzen -- recognise the same data as txt, CSV and JSON -- compare strengths and weaknesses of txt, CSV and JSON -- convert a CSV file into sentences -- convert a CSV file into JSON -::: - -## The same animals three times - -Ludwig has saved the castle's animals in three files. They all contain the same, just written down differently. - -As text, the way you would write it to a person: - -::: datei animals.txt -Ludwig (billy goat) is 7 years old. -Zora (goat) is 6 years old. -Fox (red fox) is 4 years old. -::: - -As a table in CSV: - -::: datei animals.csv -Name;Species;Age -Ludwig;billy goat;7 -Zora;goat;6 -Fox;red fox;4 -::: - -As JSON: - -::: datei animals.json -[ - {"name": "Ludwig", "species": "billy goat", "age": 7}, - {"name": "Zora", "species": "goat", "age": 6}, - {"name": "Fox", "species": "red fox", "age": 4} -] -::: - -## What can each format do? - -| | txt | CSV | JSON | -|---|---|---|---| -| Easy for people to read | yes | so-so | so-so | -| Fixed order for programs | no | yes, columns | yes, keys | -| What does a value mean? | it's in the sentence | it's in the header | it's right in front of it | -| Nesting (a list inside an entry) | no | hard | yes | -| Opens in Excel | no | yes | no | -| Reading in Java | Scanner, BufferedReader | plus \`split\` | string methods or a library | -| Size | small | smallest | largest | - -In short: - -- **txt** for texts that people read: letters, notes, a logbook. -- **CSV** for tables: grades, measurements, a high-score list. Every line has the same columns. -- **JSON** for data with structure: game saves, settings, everything that goes back and forth between programs and on the web. - -::: ludwig -When your weather app fetches the temperature, the answer almost always comes as JSON. And when a teacher exports the grades from the school software, they often get a CSV file. -::: - -## Exercise: From CSV to sentences - -::: übung l27-java-saetze -titel: From CSV to sentences -code: java -auftrag: Convert \`animals.csv\` into text. For each animal, write a sentence like **Ludwig (billy goat) is 7 years old.** into the file \`sentences.txt\`. At the end, print **3 sentences saved**. -start: l27-java-saetze-start.visustruct -test: - -> 3 sentences saved -datei: sentences.txt -> Ludwig (billy goat) is 7 years old. | Zora (goat) is 6 years old. | Fox (red fox) is 4 years old. -braucht: whileSchleife -tipp: The start code already skips the header. Split each line with \`String[] parts = line.split(";");\`. -tipp: The sentence is \`parts[0] + " (" + parts[1] + ") is " + parts[2] + " years old."\`. Count along with \`count++\` and read the next line at the end of the loop. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Exercise: From CSV to JSON - -::: übung l27-java-json -titel: From CSV to JSON -code: java -auftrag: Convert \`animals.csv\` into JSON and save it in \`animals-new.json\`, exactly like \`animals.json\` above. At the end, print **3 animals saved**. -start: l27-java-json-start.visustruct -test: - -> 3 animals saved -datei: animals-new.json -> [ | {"name": "Ludwig", "species": "billy goat", "age": 7}, | {"name": "Zora", "species": "goat", "age": 6}, | {"name": "Fox", "species": "red fox", "age": 4} | ] -braucht: forSchleife, verzweigung -tipp: \`lines[0]\` is the header. So start the for loop at \`i = 1\` and split \`lines[i]\` with \`split(";")\`. -tipp: Build the entry with \`\\"\` as in lesson 26. The age is a number and has no quotation marks. Only add a comma if \`i < lines.length - 1\`. -lösung: l27-java-json-loesung.visustruct -::: - -## Game: Which format fits? - -::: spiel frage -titel: Game: Which format fits? -intro: Ludwig wants to save data and doesn't know which format to use. Help him. Six rounds. -antworten: txt | CSV | JSON - -runde: -frage: The grades of the whole class should be opened in Excel. -richtig: CSV -> Grades are a table, and Excel opens CSV files directly. - -runde: -frage: Ludwig writes a letter to Zora. -richtig: txt -> A letter is text for people. It needs no columns and no keys. - -runde: -frage: A game save with a name, a level and a list of all the keys collected. -richtig: JSON -> A list inside an entry can be nested in JSON. In CSV it hardly fits into one cell. - -runde: -frage: A measuring device saves the time and temperature every minute, for a whole year. -richtig: CSV -> Many identical lines with the same columns: CSV is the smallest for this. JSON would repeat the keys on every line. - -runde: -frage: A weather app fetches the forecast from a server on the internet. -richtig: JSON -> JSON is the standard on the web. Almost every app gets its data this way. - -runde: -frage: The castle logbook, where every entry is a short sentence. -richtig: txt -> Every line is a sentence for people. It is read line by line, as in lesson 24. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can recognise the same data as txt, CSV and JSON. → The same animals three times -- I can compare strengths and weaknesses of txt, CSV and JSON. → What can each format do? -- I can convert a CSV file into sentences. → Exercise: From CSV to sentences -- I can convert a CSV file into JSON. → Exercise: From CSV to JSON -::: - -## Quiz - -::: quiz -? Which format does Excel open directly as a table? -→ What can each format do? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Why is a JSON file usually larger than a CSV file with the same data? -→ What can each format do? -- [ ] Because JSON stores more numbers. -- [x] Because the keys are repeated in every entry. -- [ ] Because JSON uses no commas. - -? In which format does every value say what it means? -→ What can each format do? -- [ ] txt -- [ ] CSV -- [x] JSON -> In JSON the key comes right before the value, for example "age": 7. - -? Match each purpose with a format. -→ What can each format do? -= a letter to Zora -> txt -= the class grade list -> CSV -= a game save with lists -> JSON -::: -`,ay=`--- -titel: Your first window -bereich: Windows with Swing -kurz: Open your own window with Java and Swing, explained line by line. ---- - -## Skills I will gain now - -::: kompetenzen -- switch on “Real Java” in the Code area -- open a window with a title and a size -- explain what each line of the window program does -- explain why setVisible(true) comes last -::: - -## Programs with windows - -So far your programs have written text to the console. Most programs you know, however, have **windows** with buttons and text fields. This is called a graphical user interface. - -Java comes with a toolbox for this: **Swing**. In VisuStruct you switch it on like this: - -1. Open the [Code](#zeige-codelive) area on the right. -2. Click the [gear](#zeige-einstellungen) at the top right and choose Java as the **Programming language**. -3. Below that, in the Java section, tick **Real Java (with Swing)**. - -The first time you click [▶︎ Run](#zeige-ausfuehren), VisuStruct downloads about 40 MB once, which takes a moment. After that your code runs as real Java, and your windows appear in the Code area in the **Java windows** box above the console. - -> With “Real Java”, input from the console (\`Scanner\`) does not work yet. For programs with input, untick the box again. - -::: angeberwissen -Swing has been around since 1998. Before that, Java only had the older AWT with a class called \`Frame\`. So that nobody mixes up the new building blocks, almost all names in Swing start with **J**: \`JFrame\`, \`JButton\`, \`JLabel\`. The J tells you straight away that it is Swing. -::: - -## The whole program - -This is the smallest program that opens a window. Type it into the Code area and click [▶︎ Run](#zeige-ausfuehren): - -\`\`\`java -import javax.swing.*; - -public class MyWindow { - public static void main(String[] args) { - JFrame window = new JFrame("My first window"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); - } -} -\`\`\` - -An empty grey window with the title “My first window” appears. That's all, but it is your very own window! - -You already know the frame with \`public class\` and \`main\` from the Java lesson. The first line and the four lines inside \`main\` are new. Let's look at them one by one. - -::: ludwig -Look at the structure chart next to the code: every line in \`main\` is a box, one below the other. So at the start, a window program is just a sequence. -::: - -## Line by line - -| Line | What it does | -|---|---| -| \`import javax.swing.*;\` | Brings the Swing toolbox into the program. Without this line Java does not know \`JFrame\`. | -| \`JFrame window = new JFrame("My first window");\` | Builds a new window. The text in brackets is the title at the top of the window frame. After that you talk to it through the variable \`window\`. | -| \`window.setSize(300, 200);\` | The window becomes 300 pixels wide and 200 pixels high. | -| \`window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\` | When someone closes the window, the program ends too. | -| \`window.setVisible(true);\` | Only now does the window become visible. | - -The dot in \`window.setSize(…)\` means: “Window, do this!” Before the dot is who should do something, after it is what they should do. - -> Without \`setDefaultCloseOperation\`, Java only hides the window when it is closed. The program then keeps running invisibly. - -::: quiz -? What does the dot in \`window.setSize(300, 200)\` mean? -- [x] “Window, do this!”: before the dot is who does something, after it what they do. -- [ ] It separates the two numbers. -- [ ] It ends the line. -> Before the dot is the object \`window\`, after it its method \`setSize\`. -::: - -## Why setVisible comes last - -\`setVisible(true)\` shows the window exactly as it is at that moment. So you set everything up first: title, size, and later buttons too. Only at the very end do you make the window visible. - -Things you put into the window after \`setVisible(true)\` often do not appear. Java does not redraw the window on its own. Sometimes they only show up when you resize the window. Mistakes like that are hard to find. - -::: ludwig -Set up first, then show. Like in the theatre: the curtain only goes up once the stage is ready. -::: - -Try it: delete the line with \`setVisible(true)\` and run the program again. The program runs, but you do not see a window. - -## Classes and objects - -This is not in the curriculum, but it helps you understand Swing. - -::: angeberwissen -Java is really an **object-oriented** language. \`JFrame\` is a class, a blueprint for windows. With \`new JFrame(…)\` Java builds a real window from this blueprint, an **object**. With two \`new\` you could build two windows, both from the same blueprint. - -\`setSize\` and \`setVisible\` are methods that every window object has. That is why you write \`window.setSize(…)\`: the object \`window\` runs its method. Your program itself is a class too, namely \`MyWindow\`. -::: - -## Game: What happens? - -::: spiel frage -titel: Game: What happens? -intro: Ludwig has been tinkering with the window program. Read the lines and predict what happens. Six rounds. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: What do you see after running it? -- a window with the title "Hello" -- [x] no window at all -- an error message -> \`setVisible(true)\` is missing. The window is built, but it stays invisible. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(200, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); -frage: What does the window look like? -- wider than high -- [x] higher than wide -- square -> The first number is the width, the second the height: 200 wide, 300 high. - -runde: - JFrame window = new JFrame("Hello"); - window.setSize(300, 200); - window.setVisible(true); -frage: You close the window. What happens to the program? -- It ends. -- [x] It keeps running invisibly. -- The window opens again right away. -> Without \`setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)\` Java only hides the window. - -runde: - JFrame window = new JFrame(); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setVisible(true); -frage: What is written at the top of the window frame? -- window -- JFrame -- [x] nothing, the title is empty -> Without text in the brackets the window has no title. The variable name \`window\` never shows up in the window. - -runde: - public class Test { - public static void main(String[] args) { - JFrame window = new JFrame("Test"); - window.setVisible(true); - } - } -frage: What does javac report? -- [x] cannot find symbol: class JFrame -- nothing, the window appears -- missing return statement -> The line \`import javax.swing.*;\` is missing. Without it Java doesn't know \`JFrame\`. - -runde: - JFrame a = new JFrame("One"); - JFrame b = new JFrame("Two"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: What do you see? -- [x] one window with the title "One" -- two windows -- one window with the title "Two" -> Two windows from the same blueprint, but only \`a\` is made visible. -::: - -## Task: your own window - -Change the program so that - -- the title says “Hello” and your name, -- the window is 400 pixels wide and 300 pixels high, -- the program ends when you close the window. - -Run it and check all three points. - -::: ludwig -Don't feel like typing so much? With “Real Java” you may type just the four lines from \`main\` into the Code area. VisuStruct then wraps them in a class itself and adds the \`import\` lines. In a real Java development environment, though, you need the whole program. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can switch on “Real Java” in the Code area. → Programs with windows -- I can open a window with a title and a size. → The whole program -- I can explain what each line of the window program does. → Line by line -- I can explain why setVisible(true) comes last. → Why setVisible comes last -::: - -## Quiz - -::: quiz -? Which line makes the window visible? -→ Why setVisible comes last -- [ ] window.setSize(300, 200); -- [x] window.setVisible(true); -- [ ] new JFrame("Hello"); -> Without setVisible(true) the window stays invisible. - -? Where is the best place for window.setVisible(true)? -→ Why setVisible comes last -- [ ] At the very start of main -- [x] At the end, once the window is set up -- [ ] It does not matter. -> Things added after setVisible(true) often do not appear. - -? What does setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE) do? -→ Line by line -- [ ] The window closes immediately. -- [x] When you close the window, the program ends. -- [ ] The window can no longer be closed. - -? How do you recognise building blocks from Swing? -→ Line by line -- [x] Their name starts with J, e.g. JFrame. -- [ ] They are in curly brackets. -- [ ] They end in Window. -::: -`,sy=`--- -titel: A button in the window -bereich: Windows with Swing -kurz: Put a button into the window and arrange it neatly with a layout. ---- - -## Skills I will gain now - -::: kompetenzen -- build a button and put it into the window with add -- explain why the button goes in before setVisible -- use FlowLayout so that the button has its normal size -- build a window with a button you can press -::: - -## Building a button - -An empty window is boring. Now a button goes in. In Swing a button is called **JButton**. - -You need two new lines for it: - -\`\`\`java -JButton button = new JButton("Click me"); -window.add(button); -\`\`\` - -- \`new JButton("Click me")\` builds a button. The text in brackets will be written on the button. -- \`window.add(button)\` puts the button into the window. Without \`add\` the button exists, but it is not placed anywhere and you cannot see it. - -::: quiz -? What is written on the button after \`new JButton("Start")\`? -- [ ] button -- [x] Start -- [ ] JButton -> The text in brackets is the label. \`button\` is only the name of the variable. -::: - -## The whole program - -Both lines go **before** \`setVisible(true)\`, because the window should be set up completely first and then shown: - -\`\`\`java -import javax.swing.*; - -public class ButtonWindow { - public static void main(String[] args) { - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton button = new JButton("Click me"); - window.add(button); - - window.setVisible(true); - } -} -\`\`\` - -Run the program. Surprise: the button is huge and fills the whole window! - -::: ludwig -The order is the same as with your first window: set up, build the button, put the button in and show everything at the very end. The empty lines in the code help you see these parts. -::: - -## The giant button - -How a window arranges its parts is decided by its **layout**. Without further instructions, a \`JFrame\` puts everything in the middle and makes it as big as the whole window. - -With a different layout the button gets its normal size. The **FlowLayout** places everything side by side like words in a line, only as big as needed: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class ButtonWindow { - public static void main(String[] args) { - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new FlowLayout()); - - JButton button = new JButton("Click me"); - window.add(button); - - window.setVisible(true); - } -} -\`\`\` - -\`FlowLayout\` belongs to the older AWT toolbox. That is why there is a second line at the top: \`import java.awt.*;\`. - -::: angeberwissen -With FlowLayout the building blocks flow like text: if a button no longer fits into the line, it moves to the next one. Make the window narrow and watch the buttons wrap. That is where the name comes from. -::: - -## Clicking works, but nothing happens - -Click the button. It turns a little darker while you press it. So Swing shows that it was pressed. Apart from that, nothing happens. - -That is not a mistake: you have not told the program **what** should happen on a click yet. You will learn that in the next lesson. - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every window program contains **exactly one mistake**, and the bomb is ticking. Read what the program should do and tap the wrong line before time runs out. Six rounds, 25 seconds each. -zeit: 25 - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - JButton button = new JButton(Click me); - window.add(button); - window.setVisible(true); -frage: Should show a button "Click me". -fehler: 3 -besser: JButton button = new JButton("Click me"); -> The caption is a text and needs quotation marks. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - JButton button = new JButton("OK"); - window.add(window); - window.setVisible(true); -frage: Should put the button "OK" into the window. -fehler: 4 -besser: window.add(button); -> The button goes into the window, not the window itself. - -runde: - import java.swing.*; - import java.awt.*; -frage: Should bring Swing and the FlowLayout into the program. -fehler: 1 -besser: import javax.swing.*; -> Swing lives in \`javax.swing\`, with an x. The FlowLayout comes from \`java.awt\`, without an x. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout); - window.add(new JButton("OK")); - window.setVisible(true); -frage: Should show the button at its normal size. -fehler: 3 -besser: window.setLayout(new FlowLayout()); -> After \`new\` and the name there are always round brackets, even if nothing is inside. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout()); - JButton button = new Jbutton("OK"); - window.add(button); - window.setVisible(true); -frage: Should show a button "OK". -fehler: 4 -besser: JButton button = new JButton("OK"); -> Java tells capital and small letters apart: \`JButton\` with a capital B. - -runde: - JFrame window = new JFrame("Button"); - window.setSize(300, 200); - window.setLayout(new FlowLayout()); - window.add(new JButton("OK")); - window.setVisible(false); -frage: Should show the window with the button. -fehler: 5 -besser: window.setVisible(true); -> With \`false\` the window stays invisible. -::: - -## Task: click me - -Build a window with the title “Button test”, 300 by 200 pixels, with a button “Click me” in normal size. It should be possible to press the button, even though nothing happens yet. - -Extra: put a second button “Me too” next to it. In which order are the buttons? Swap the two \`add\` lines and try again. - -**You are done when …** - -- the window title says “Button test”, -- the window is 300 pixels wide and 200 pixels high, -- the “Click me” button is only as big as its text and does not fill the whole window, -- the button visibly presses down when you click it. - -::: ludwig -Every button needs its own variable, for example \`button\` and \`button2\`, and its own \`add\`. FlowLayout places the buttons in the order of the \`add\` lines. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a button and put it into the window with add. → Building a button -- I can explain why the button goes in before setVisible(true). → The whole program -- I can use FlowLayout so that the button has its normal size. → The giant button -- I can build a window with a button you can press. → Task: click me -::: - -## Quiz - -::: quiz -? What does window.add(button) do? -→ Building a button -- [ ] It builds a new button. -- [x] It puts the button into the window. -- [ ] It makes the button clickable. -> The button is built with new JButton(…). - -? Why does the button fill the whole window without FlowLayout? -→ The giant button -- [ ] Because the text is too long -- [x] Because without its own layout the window makes everything as big as the window -- [ ] Because setSize is missing - -? Which import line do you need for FlowLayout? -→ The giant button -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> FlowLayout comes from the older AWT. - -? What happens when you click the button in this lesson? -→ Task: click me -- [ ] The program ends. -- [x] The button turns darker for a moment, nothing else happens. -- [ ] The button says “I was clicked”. -> What should happen on a click is what you learn in the next lesson. -::: -`,ry=`--- -titel: The button reacts -bereich: Windows with Swing -kurz: Use an ActionListener to decide what happens on a click. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an event is -- use addActionListener to decide what happens on a click -- run several statements on a click -- build a button that changes its text after the click -::: - -## A click is an event - -In the last lesson you could press the button, but nothing happened. In a window program it is the person who decides when something happens: they click, type or close a window. Each such action is called an **event**. - -So your program has to wait for the click and notice it. For this you register a listener with the button, an **ActionListener**. On every click the button calls its listener, and the listener runs your code. - -::: ludwig -Until now your program ran from top to bottom, just like the structure chart. The code in the listener only runs later, whenever someone clicks. Nobody knows in advance how often that will be. -::: - -## The line with the arrow - -This is how you register the listener: - -\`\`\`java -button.addActionListener(e -> button.setText("I was clicked")); -\`\`\` - -Read from the outside in: - -- \`button.addActionListener( … )\` registers a listener with the button. -- \`e\` is the event. It contains, for example, when the click happened. You do not need it here, but the name has to be there. -- Read \`->\` as “then do”. -- \`button.setText("I was clicked")\` is what happens on a click: the button gets a new label. - -The line comes after \`new JButton(…)\`, because the button has to exist already. And like everything else it comes before \`setVisible(true)\`. - -::: quiz -? What does the \`e\` in \`e -> button.setText(…)\` stand for? -- [x] The event, that is the click -- [ ] “End” -- [ ] An error -> You do not need \`e\` here, but the name has to be there. -::: - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class ClickMe { - public static void main(String[] args) { - JFrame window = new JFrame("Click me"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new FlowLayout()); - - JButton button = new JButton("Click me"); - button.addActionListener(e -> button.setText("I was clicked")); - window.add(button); - - window.setVisible(true); - } -} -\`\`\` - -Run it and click the button. Now it says “I was clicked”. - -> If the new text is longer than the old one, FlowLayout makes the button wider on its own. - -## Several statements on a click - -If more than one thing should happen on a click, put curly brackets after the arrow. Inside, every statement has its semicolon, just like in a loop body: - -\`\`\`java -button.addActionListener(e -> { - button.setText("I was clicked"); - window.setTitle("Thank you!"); - System.out.println("Click!"); -}); -\`\`\` - -\`System.out.println\` writes to the [console](#zeige-konsole) as usual. Click several times: every click adds a new line “Click!”. That shows you that the code in the listener really runs on every click. - -::: ludwig -Watch the ending: first \`}\` closes the block, then \`)\` closes the bracket of \`addActionListener\`, then comes the semicolon. So \`});\`. If any of it is missing, a hint appears below the code. -::: - -## Game: Who is listening? - -::: spiel zuhoerer -titel: Game: Who is listening? -intro: Ludwig shows you code and the window that goes with it. Below it you see which buttons are clicked, one after the other. What happens? After your answer Ludwig plays the clicks: you see which listener hears the event and which lines then run. -frage: What does the label say after the clicks? - -runde: - JFrame window = new JFrame("Greeting"); - JLabel text = new JLabel("Start"); - JButton button = new JButton("Hello"); - button.addActionListener(e -> text.setText("Hello world")); -klicks: Hello -- Start -- [x] Hello world -- Hello -> The button has a listener. On a click it runs \`text.setText("Hello world")\`, and the label shows “Hello world”. - -runde: - JFrame window = new JFrame("Fruit"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apple")); - b.addActionListener(e -> text.setText("Pear")); -klicks: A, B -- Apple -- [x] Pear -- Apple Pear -> Each button has its own listener. First A writes “Apple”, then B overwrites it with “Pear”. \`setText\` replaces the old text, it doesn't add anything. - -runde: - JFrame window = new JFrame("Fruit"); - JLabel text = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> text.setText("Apple")); -klicks: B -- Apple -- Pear -- [x] ? -> Nobody signed up at button B: there is no \`b.addActionListener\`. The click is an event, but nobody is listening. So the question mark stays. - -runde: - JFrame window = new JFrame("Colours"); - JLabel text = new JLabel("none"); - JButton red = new JButton("Red"); - JButton blue = new JButton("Blue"); - red.addActionListener(e -> text.setText("Blue")); - blue.addActionListener(e -> text.setText("Red")); -klicks: Red -- Red -- [x] Blue -- none -> Careful: what happens is decided by the code in the listener, not by the caption of the button. The listener on the button “Red” writes “Blue”. - -runde: - JFrame window = new JFrame("Button"); - JLabel text = new JLabel("Please click"); - JButton button = new JButton("Click me"); - button.addActionListener(e -> { - button.setText("Thanks!"); - text.setText("Clicked"); - }); -klicks: Click me -frage: What does the button say after the click? -- Click me -- [x] Thanks! -- Clicked -> The block after the arrow has two statements. Both run on the click, one after the other: the button gets “Thanks!”, the label “Clicked”. - -runde: - JFrame window = new JFrame("Console"); - JButton button = new JButton("Go"); - button.addActionListener(e -> System.out.println("Click!")); -klicks: Go, Go, Go -frage: How many lines “Click!” are in the console afterwards? -- 1 -- [x] 3 -- 0 -> The listener runs on **every** click, not only on the first one. Three clicks, three lines. -::: - -## ActionListener is an interface - -This is not in the curriculum. If you want to know what is behind the arrow, read on. - -::: angeberwissen -\`ActionListener\` is an **interface**, a kind of contract. It says: whoever wants to be an ActionListener must have a method \`actionPerformed(ActionEvent e)\`. On every click the button calls exactly this method. -::: - -You already know the line with the arrow from step 3: - -\`\`\`java -button.addActionListener(e -> button.setText("I was clicked")); -\`\`\` - -Without the arrow you write the listener as a class without a name that fulfils the contract (an anonymous class): - -\`\`\`java -button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - button.setText("I was clicked"); - } -}); -\`\`\` - -For this you also need \`import java.awt.event.*;\` at the top. Both ways do exactly the same. The arrow \`e -> …\` is the short form (a lambda expression, since Java 8): \`e\` is the parameter of \`actionPerformed\`, and after the arrow comes its body. The short form works because the interface has only one method. So Java knows which method is meant. - -## Game: Contract fulfilled? - -::: spiel vertrag -titel: Game: Contract fulfilled? -intro: The contract \`ActionListener\` asks for exactly one method: \`public void actionPerformed(ActionEvent e)\`. Ludwig shows you code. Does it fulfil the contract, so that you can sign it up at a button? Ludwig checked every answer with the real Java compiler. -frage: Does this fulfil the contract ActionListener? -antworten: fulfilled | not fulfilled - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: fulfilled -> Everything is there: \`implements ActionListener\` signs the contract, and the method is called exactly \`actionPerformed\`, is \`public\`, returns nothing (\`void\`) and gets an \`ActionEvent\`. - -runde: - class Greeting implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> Look closely: \`actionPerfomed\` instead of \`actionPerformed\`, an r is missing. For Java that is a completely different method. The compiler reports that \`actionPerformed\` is missing. - -runde: - class Greeting implements ActionListener { - public void actionPerformed() { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> The parameter is missing. The contract asks for \`actionPerformed(ActionEvent e)\`, because on a click the button hands over the event. - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent event) { - System.out.println("Hello"); - } - } -richtig: fulfilled -> You can choose the name of the parameter freely. Only the type \`ActionEvent\` matters. So instead of \`e\` it may also be called \`event\`. - -runde: - class Greeting implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } -richtig: not fulfilled -> The \`public\` is missing. All methods of an interface are public, because the button has to be able to call them from outside. Without \`public\` the compiler reports an error. - -runde: - class Greeting implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: not fulfilled -> The contract says \`void\`: the method returns nothing. With \`int\` as the return type it no longer fits. - -runde: - class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - sayHello(); - } - void sayHello() { - System.out.println("Hello"); - } - } -richtig: fulfilled -> Extra methods are allowed. The contract only asks that \`actionPerformed\` is there, not that nothing else is there. - -runde: - class Greeting { - public void actionPerformed(ActionEvent e) { - System.out.println("Hello"); - } - } - button.addActionListener(new Greeting()); -richtig: not fulfilled -> The method is right, but the contract isn't signed: \`implements ActionListener\` is missing. At a button Java only accepts objects that officially fulfil the contract. - -runde: - button.addActionListener(e -> System.out.println("Hello")); -richtig: fulfilled -> The arrow is the short form. Because the contract has only one method, Java knows: \`e\` is the parameter of \`actionPerformed\`, and after the arrow comes its body. - -runde: - button.addActionListener((a, b) -> System.out.println("Hello")); -richtig: not fulfilled -> Here the arrow has two parameters. But \`actionPerformed\` gets exactly one, the event. So this short form doesn't fit the contract. -::: - -## Task: I was clicked - -Now you finish the task: build a window with a button “Click me” in normal size. After the click the button says “I was clicked”. - -Extra: build two buttons “Red” and “Blue”. Clicking a button writes its name into the window title, for example “You chose Red”. - -**You are done when …** - -- the button says “Click me” before the click, -- it says “I was clicked” after the click, -- the button does not fill the whole window. - -::: ludwig -Every button gets its own listener with its own \`addActionListener\`. You change the title with \`window.setTitle(…)\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an event is. → A click is an event -- I can use addActionListener to decide what happens on a click. → The line with the arrow -- I can run several statements on a click. → Several statements on a click -- I can build a button that changes its text after the click. → Task: I was clicked -::: - -## Quiz - -::: quiz -? What is an ActionListener? -→ A click is an event -- [ ] A button with text -- [x] A listener that runs your code on every click -- [ ] A layout for windows -> You register it with the button using addActionListener. - -? When does button.setText("I was clicked") in the listener run? -→ The line with the arrow -- [ ] Right when the program starts -- [x] Every time someone clicks the button -- [ ] Only on the first click -> The code in the listener only runs when the event happens. - -? How does a listener with curly brackets end? -→ The line with the arrow -- [ ] };) -- [x] }); -- [ ] );} -> First the block closes, then the bracket of addActionListener, then the semicolon. - -? Match each part to its job. -→ The line with the arrow -= JButton -> a button -= addActionListener -> register a listener -= setText -> change the label -= setVisible(true) -> show the window -::: -`,oy=`--- -titel: Doubling a number in a window -bereich: Windows with Swing -kurz: Arrange a label, a text field and a button with a GridLayout and calculate with the text in the text field. ---- - -## Skills I will gain now - -::: kompetenzen -- put a JLabel and a JTextField into the window -- explain why only the last building block shows without a layout -- arrange everything below each other with GridLayout -- calculate with getText, Integer.parseInt and setText -- write a program that doesn't crash with “abc” -::: - -## The goal - -We build a small calculator with a window: at the top there is a label, below it a text field and a button **Calculate**. If you type 2 and click, the text field shows 4. Click again and it becomes 8, then 16 and so on. - -For this you need two new building blocks: - -- **JLabel**: a label, simply text in the window. -- **JTextField**: a text field you can type into. - -As in the last lessons, tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Three building blocks in the window - -We put all three building blocks into the window, just like the button in lesson 29: - -\`\`\`java -import javax.swing.*; - -public class Doubling { - public static void main(String[] args) { - JFrame window = new JFrame("Double the number"); - window.setSize(250, 150); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel label = new JLabel("Enter a number:"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate"); - window.add(label); - window.add(field); - window.add(button); - - window.setVisible(true); - } -} -\`\`\` - -Run the program. Oops: all you can see is the giant button. Where are the label and the text field? - -## Why only the button shows - -::: ludwig -Remember the giant button? Without a layout of its own, a \`JFrame\` has only **one** place: the middle. Every \`add\` puts the new building block exactly there, as big as the whole window. So the three building blocks land on top of each other in the same place, and the window only shows the last one you put in: the button. You can't use the program like this. -::: - -The solution is a layout again. This time we use the **GridLayout**. It divides the window into cells of equal size, like a grid: - -\`\`\`java -window.setLayout(new GridLayout(0, 1)); -\`\`\` - -The two numbers are rows and columns. \`0\` rows means “as many as needed”, \`1\` column means: everything below each other. For \`GridLayout\` you also need \`import java.awt.*;\` at the top. - -| Layout | How it arranges things | -|---|---| -| no layout | everything in the middle, on top of each other | -| \`FlowLayout\` | side by side like words, with a line break when needed | -| \`GridLayout(0, 1)\` | below each other, every row the same height | - -## The window with GridLayout - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Doubling { - public static void main(String[] args) { - JFrame window = new JFrame("Double the number"); - window.setSize(250, 150); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel label = new JLabel("Enter a number:"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate"); - window.add(label); - window.add(field); - window.add(button); - - window.setVisible(true); - } -} -\`\`\` - -Now all three are below each other. You can type into the text field and press the button. But the program doesn't calculate anything yet. - -::: angeberwissen -With \`new GridLayout(2, 3)\` you get 2 rows with 3 columns each, so 6 cells of the same size. That is how you build the keys of a calculator, for example. The building blocks fill the cells in the order of the \`add\` lines, row by row from left to right. -::: - -## Calculating with the text in the text field - -On a click, the program should take the number from the text field, double it and write it back. Three methods do this: - -- \`field.getText()\` gives you the text in the text field, for example \`"2"\`. -- \`Integer.parseInt(…)\` turns it into the number 2. You know this from lesson 22. -- \`field.setText(…)\` writes a new text into the text field. - -Together with the ActionListener from lesson 30 it looks like this: - -\`\`\`java -button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); -}); -\`\`\` - -\`setText\` needs a text. \`"" + number * 2\` appends the result to an empty text and so turns the number into a text. - -## Not a number? No crash! - -Type “abc” into the text field and click Calculate. The window stays, but nothing happens, and the [console](#zeige-konsole) shows a \`NumberFormatException\`. From lesson 22 you know what to do: - -\`\`\`java -button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } -}); -\`\`\` - -Instead of an error in the console, you now see a hint in the window. - -::: ludwig -The arrow already has an \`e\` for the event. That is why the exception in \`catch\` needs a different name, here \`error\`. Java doesn't allow two variables with the same name. -::: - -::: quiz -? Why is the exception in \`catch\` called \`error\` here and not \`e\`? -- [ ] \`catch\` always needs the name \`error\`. -- [x] \`e\` is already the event of the arrow, and Java does not allow two variables with the same name. -- [ ] \`e\` is only allowed for numbers. -> You can choose the name freely, just not twice. -::: - -## Game: What is in the window afterwards? - -::: spiel frage -titel: Game: What is in the window afterwards? -intro: Ludwig types something into the text field and clicks Calculate. Read the listener and predict what the window shows afterwards. Six rounds. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - }); -frage: The text field says 21. What does it say after the click? -- 2121 -- [x] 42 -- 21 -> \`parseInt\` turns the text into the number 21. Double that is 42. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - }); -frage: The text field says 21. You click twice. What does it say afterwards? -- 42 -- [x] 84 -- 4242 -> Every click doubles whatever is in the field right now: 21, 42, 84. - -runde: - button.addActionListener(e -> { - field.setText(field.getText() + field.getText()); - }); -frage: The text field says 21. What does it say after the click? -- 42 -- [x] 2121 -- 21 -> Without \`parseInt\` it stays a text, and \`+\` joins texts together. - -runde: - button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } - }); -frage: The text field says abc. What happens on the click? -- [x] The label shows "Please enter a whole number!" -- The text field says abcabc. -- The window crashes. -> \`parseInt\` throws a NumberFormatException, and \`catch\` catches it. - -runde: - button.addActionListener(e -> { - try { - int number = Integer.parseInt(field.getText()); - field.setText("" + number * 2); - } catch (NumberFormatException error) { - label.setText("Please enter a whole number!"); - } - }); -frage: The text field says 2.5. What happens on the click? -- The text field says 5.0. -- The text field says 5. -- [x] The label shows "Please enter a whole number!" -> \`Integer.parseInt\` only knows whole numbers. For it, 2.5 is not a number. - -runde: - button.addActionListener(e -> { - int number = Integer.parseInt(field.getText()); - field.setText("" + number + number); - }); -frage: The text field says 5. What does it say after the click? -- 10 -- [x] 55 -- 5 -> \`"" + number\` is already a text, and the second \`+\` adds the 5 once more. With \`"" + (number + number)\` Java calculates first. -::: - -## Task: doubling a number - -Put the whole program together: - -- a window with the title “Double the number”, 250 pixels wide and 150 pixels high, -- below each other a label, a text field and a button “Calculate”, -- every click doubles the number in the text field: 2, 4, 8, 16, … -- with “abc” a hint appears in the label instead of a crash. - -Extra: a second button “Halve” divides the number by 2. What happens with 5? - -**You are done when …** - -- the title is “Double the number” and label, text field and button are stacked, -- 2 becomes 4, 8 and 16 after three clicks, -- with “abc” a hint appears in the label and the program keeps running. - -::: ludwig -The “Halve” button goes into the GridLayout as the fourth row and gets its own ActionListener. With \`int\`, Java cuts off the decimal places when dividing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can put a JLabel and a JTextField into the window. → Three building blocks in the window -- I can explain why only the last building block shows without a layout. → Why only the button shows -- I can arrange everything below each other with GridLayout(0, 1). → The window with GridLayout -- I can calculate with getText, Integer.parseInt and setText. → Calculating with the text in the text field -- I can write a program that doesn't crash with “abc”. → Not a number? No crash! -::: - -## Quiz - -::: quiz -? Why does only the button show without a layout? -→ Why only the button shows -- [ ] Because the label and the text field are missing -- [x] Because all building blocks lie on top of each other in the middle and the last one hides the others -- [ ] Because the window is too small - -? What does new GridLayout(0, 1) mean? -→ The window with GridLayout -- [ ] No row and one column, so the window is empty -- [x] As many rows as needed in one column, so everything below each other -- [ ] One row, everything side by side - -? What does field.getText() give you? -→ Calculating with the text in the text field -- [ ] Always a number -- [x] The text in the text field -- [ ] The title of the window -> For a number you still need Integer.parseInt. - -? Match each building block to its job. -→ Three building blocks in the window -= JLabel -> label -= JTextField -> text field for typing -= JButton -> button -= GridLayout -> arrangement in a grid -::: -`,ly=`--- -titel: Click counter with reset -bereich: Windows with Swing -kurz: Two buttons change the same number: create a variable for the whole class. ---- - -## Skills I will gain now - -::: kompetenzen -- explain why a listener may not change a variable from main -- create a static variable in the class -- let two buttons change the same number -- build a counter with −1 that never goes below 0 -::: - -## The goal - -A window counts clicks: the number is at the top, two buttons below it. **+1** counts up by one, **Reset** sets the number back to 0. - -What's new this time: two buttons work with the **same** number. For that you need a variable that both listeners know. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## The first attempt - -The obvious idea is to create the number in \`main\` as usual and count it up in the listener: - -\`\`\`java -int count = 0; -button.addActionListener(e -> { - count++; - number.setText("" + count); -}); -\`\`\` - -If you run this with real Java, the compiler reports an error: - -\`\`\` -local variables referenced from a lambda expression must be final or effectively final -\`\`\` - -::: ludwig -The listener only runs later, on every click, long after \`main\` has finished. That is why it may read the variables from \`main\`, but not change them. Java insists: a variable from \`main\` that appears in the arrow must stay the same. For a number that changes on every click, we need a different place. -::: - -## A variable for the whole class - -The solution: the variable does **not** go into \`main\`, but directly into the class, above \`main\`. Then it belongs to the whole class, and every listener may change it: - -\`\`\`java -public class Counter { - static int count = 0; - - public static void main(String[] args) { - // the window as usual - } -} -\`\`\` - -- \`static\` means: the variable belongs to the class itself. Because \`main\` is \`static\` too, it is needed here. -- A variable like this is called a **field** of the class. It lives as long as the program, not just as long as \`main\`. - -::: quiz -? What do you call a variable that sits directly in the class and not in \`main\`? -- [ ] A parameter -- [x] A field -- [ ] A listener -> A field lives as long as the program, not just as long as \`main\`. -::: - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Counter { - static int count = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel number = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); - reset.addActionListener(e -> { - count = 0; - number.setText("0"); - }); - - window.add(number); - window.add(plus); - window.add(reset); - window.setVisible(true); - } -} -\`\`\` - -\`SwingConstants.CENTER\` puts the text of the label in the middle. Click +1 a few times and then Reset. - -::: angeberwissen -With \`number.setFont(new Font("SansSerif", Font.BOLD, 48));\` the number becomes big and bold. \`Font\` comes from \`java.awt\`, which you have already imported. -::: - -## Game: Who is counting? - -::: spiel zuhoerer -titel: Game: Who is counting? -intro: Now the listeners calculate with the variable of the class. Ludwig shows you code, window and the clicks. Which number is shown at the end? Afterwards Ludwig plays the clicks. -frage: What does the label say after the clicks? - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> The variable is in the class and keeps its value between the clicks. Every click counts up by one: 1, 2, 3. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - JButton reset = new JButton("Reset"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); - reset.addActionListener(e -> { - count = 0; - number.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> After two clicks it shows 2. Reset sets the variable to 0, and the last click counts up to 1 again. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> count++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> Afterwards the variable is 2, but nobody writes it into the label. Without \`number.setText(…)\` the window keeps showing 0. Calculating and showing are two different things. - -runde: - static int count = 0; - JLabel number = new JLabel("Clicks: 0"); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("Clicks: " + count + 1); - }); -klicks: +1 -- Clicks: 2 -- Clicks: 1 -- [x] Clicks: 11 -> The bracket trap: Java works from left to right. \`"Clicks: " + count\` gives the text “Clicks: 1”, and \`+ 1\` adds another 1 to the end. - -runde: - static int count = 0; - JLabel number = new JLabel("0"); - JButton one = new JButton("+1"); - JButton ten = new JButton("+10"); - one.addActionListener(e -> { - count++; - number.setText("" + count); - }); - ten.addActionListener(e -> { - count += 10; - number.setText("" + count); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Both listeners change the same variable: 10, then 11, then 21. \`count += 10\` is the short form of \`count = count + 10\`. -::: - -## Task: click counter - -Rebuild the click counter and extend it: - -- a button **−1** that counts down by one, -- the number should never go below 0. If it is 0, −1 does nothing. - -**You are done when …** - -- **+1** counts up by one and **−1** counts down by one, -- at 0 a click on **−1** changes nothing, -- **Reset** sets the number back to 0. - -::: ludwig -For “never below 0” the listener of −1 needs an \`if\`: only if \`count > 0\` does it count down. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain why a listener may not change a variable from main. → The first attempt -- I can create a static variable in the class. → A variable for the whole class -- I can let two buttons change the same number. → The whole program -- I can build a counter with −1 that never goes below 0. → Task: click counter -::: - -## Quiz - -::: quiz -? Why may the listener not change the variable count from main? -→ The first attempt -- [ ] Because numbers are not allowed in Swing -- [x] Because the listener runs later and Java doesn't let the arrow change variables from main -- [ ] Because count gets too big - -? Where does static int count = 0; go? -→ A variable for the whole class -- [ ] In main, at the very top -- [x] In the class, outside main -- [ ] In the listener - -? What does SwingConstants.CENTER do in new JLabel("0", SwingConstants.CENTER)? -→ The whole program -- [x] It puts the text in the middle of the label. -- [ ] It puts the window in the middle of the screen. -- [ ] It makes the font bigger. -::: -`,uy=`--- -titel: A traffic light with colours -bereich: Windows with Swing -kurz: Set background colours and switch a traffic light on with a state variable. ---- - -## Skills I will gain now - -::: kompetenzen -- colour a label with setOpaque and setBackground -- remember a state with a static variable -- use BorderLayout to put something big in the middle and something narrow at the bottom -- explain what FlowLayout, GridLayout and BorderLayout are good for -- program a traffic light with four phases -::: - -## The goal - -We build a traffic light: a large area lights up red. Each click on **Next** switches it on: red, green, yellow and red again. - -New this time are **colours** and a **state**: the program has to remember which colour is currently showing. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Colours in Swing - -Colours live in the class \`Color\` from \`java.awt\`. Ready-made colours are, for example, \`Color.RED\`, \`Color.GREEN\`, \`Color.YELLOW\`, \`Color.ORANGE\` and \`Color.WHITE\`. - -This is how you colour a label: - -\`\`\`java -JLabel light = new JLabel("", SwingConstants.CENTER); -light.setOpaque(true); -light.setBackground(Color.RED); -\`\`\` - -- \`setBackground(…)\` sets the background colour. -- A \`JLabel\` also needs \`setOpaque(true)\`. Without this line the background is transparent and the colour stays invisible. - -::: ludwig -\`opaque\` means “not see-through”. A label is normally transparent, like writing on a pane of glass. Only with \`setOpaque(true)\` does it get its own area that can be painted. -::: - -::: quiz -? Which line paints the background of the label \`light\` green? -- [ ] \`light.setText(Color.GREEN);\` -- [x] \`light.setBackground(Color.GREEN);\` -- [ ] \`light.setColor("green");\` -> \`setBackground\` sets the background colour. To see it, a \`JLabel\` first needs \`setOpaque(true)\`. -::: - -## Remembering the colour - -So that the traffic light knows what comes next, it remembers its state in a number: 0 means red, 1 means green, 2 means yellow. As with the click counter from lesson 32, the variable is in the class so that the listener may change it: - -\`\`\`java -static int phase = 0; -\`\`\` - -On every click the phase moves on by one. After yellow (2) comes red (0) again: - -\`\`\`java -next.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - light.setBackground(Color.RED); - } else if (phase == 1) { - light.setBackground(Color.GREEN); - } else { - light.setBackground(Color.YELLOW); - } -}); -\`\`\` - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class TrafficLight { - static int phase = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Traffic light"); - window.setSize(200, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new BorderLayout()); - - JLabel light = new JLabel("", SwingConstants.CENTER); - light.setOpaque(true); - light.setBackground(Color.RED); - JButton next = new JButton("Next"); - - next.addActionListener(e -> { - phase++; - if (phase > 2) { - phase = 0; - } - if (phase == 0) { - light.setBackground(Color.RED); - } else if (phase == 1) { - light.setBackground(Color.GREEN); - } else { - light.setBackground(Color.YELLOW); - } - }); - - window.add(light, BorderLayout.CENTER); - window.add(next, BorderLayout.SOUTH); - window.setVisible(true); - } -} -\`\`\` - -This uses a new layout, the **BorderLayout**. It has five places: \`NORTH\` (top), \`SOUTH\` (bottom), \`WEST\` (left), \`EAST\` (right) and \`CENTER\` (middle). The middle gets all the space that is left. So the light becomes big, and the button stays narrow at the bottom. - -::: angeberwissen -BorderLayout is the layout a \`JFrame\` uses when you don't give it one. An \`add\` without a place lands in \`CENTER\`. That is why all the building blocks lay on top of each other in lesson 31. -::: - -::: angeberwissen -Professionals switch on in one line: \`phase = (phase + 1) % 3;\`. The remainder operator \`%\` gives the remainder of a division. 3 % 3 is 0, so after 2 it starts at 0 again. -::: - -## Which layout for what? - -You now know three layouts. Each one arranges the building blocks differently: - -| Layout | How it arranges | Good for | -|---|---|---| -| \`FlowLayout\` (lesson 29) | next to each other like words in a line, each block as big as needed | a few buttons in a row | -| \`GridLayout\` (lesson 31) | a grid of cells of the same size, filled row by row | forms, keypads like on a calculator | -| \`BorderLayout\` (this lesson) | five places: top, bottom, left, right and the middle, which gets the rest | something big in the middle, such as a picture or a display, with buttons at the edge | - -Without \`setLayout\` a window uses the BorderLayout. That is why everything then lands in the middle. - -::: ludwig -Ask yourself when planning: should all parts be the same size? Then GridLayout. Should one part be big and the rest narrow at the edge? Then BorderLayout. Should the parts simply keep their normal size? Then FlowLayout. -::: - -## Game: Where does the button go? - -::: spiel layout -titel: Game: Where does the button go? -intro: You now know three layouts and the window with no layout at all. Ludwig shows you code with the buttons A, B, C … What does the window look like? Tap the right picture. -frage: What does the window look like? - -runde: - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Without a layout of its own, every \`add\` lands in the centre, as big as the window. The three lie on top of each other, and you only see the last one: C. - -runde: - window.setLayout(new FlowLayout()); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> FlowLayout puts the buttons next to each other at their normal size, like words in a line, and centres them. - -runde: - window.setLayout(new GridLayout(0, 1)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> One column, as many rows as needed: everything below each other, every row the same height and as wide as the window. - -runde: - window.setLayout(new GridLayout(0, 2)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); - window.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Two columns. The cells are filled row by row, always left first, then right: A B, then C D. - -runde: - window.setLayout(new GridLayout(2, 3)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); - window.add(new JButton("D")); - window.add(new JButton("E")); - window.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Rows come first, then columns: 2 rows with 3 columns each. - -runde: - window.setLayout(new GridLayout(0, 2)); - window.add(new JButton("A")); - window.add(new JButton("B")); - window.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Two columns, three buttons: the first row is full, in the second C is on the left, and the cell on the right stays empty. - -runde: - window.setLayout(new BorderLayout()); - window.add(new JButton("A"), BorderLayout.NORTH); - window.add(new JButton("B"), BorderLayout.CENTER); - window.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH is at the top, SOUTH at the bottom. The centre gets all the remaining space, which is why B is big. - -runde: - window.setLayout(new BorderLayout()); - window.add(new JButton("A"), BorderLayout.WEST); - window.add(new JButton("B")); - window.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST is on the left, EAST on the right. B has no place given, so it lands in CENTER. -::: - -## Task: traffic light - -Rebuild the traffic light and make it more realistic. In the UK and Germany, red and amber light up together briefly after red. So add a fourth phase: - -- 0: red -- 1: red and amber (use \`Color.ORANGE\`) -- 2: green -- 3: amber (yellow) - -Extra: in each phase, also write with \`light.setText(…)\` what the light means, for example “Stop” or “Go”. - -**You are done when …** - -- every click shows the next phase: red, red-yellow (orange), green, yellow, -- red comes again after yellow, -- for the extra, every phase shows its matching text. - -::: ludwig -With four phases, 0 comes after 3. So change the number in \`if (phase > 2)\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can colour a label with setOpaque and setBackground. → Colours in Swing -- I can remember a state with a static variable. → Remembering the colour -- I can use BorderLayout to put something big in the middle and something narrow at the bottom. → The whole program -- I can explain what FlowLayout, GridLayout and BorderLayout are good for. → Which layout for what? -- I can program a traffic light with four phases. → Task: traffic light -::: - -## Quiz - -::: quiz -? Why does the colour stay invisible without setOpaque(true)? -→ Colours in Swing -- [ ] Because Color.RED doesn't exist -- [x] Because a label is transparent otherwise -- [ ] Because the window is too small - -? Which place in the BorderLayout gets the space that is left? -→ The whole program -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? phase is 2 and the listener runs phase++ and then if (phase > 2) { phase = 0; }. What is phase afterwards? -→ Remembering the colour -- [x] 0 -- [ ] 2 -- [ ] 3 -> After phase++ it is 3, which is greater than 2, so it becomes 0. -::: -`,my=`--- -titel: Grade calculator in a window -bereich: Windows with Swing -kurz: Enter points, work out the grade with else if and catch wrong input. ---- - -## Skills I will gain now - -::: kompetenzen -- turn points into a grade with else if -- show the grade in the window -- catch and report “abc” and points outside 0 to 100 -- test a program at the limits, for example with 90 and 89 -::: - -## The goal - -A window turns points into a grade: you type the points of a test (0 to 100), click **Calculate grade**, and the grade appears at the bottom. - -This time there is no new Swing building block. What's new is that you combine everything so far: text field, button, \`else if\` from lesson 20 and try/catch from lesson 22. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## The grading scale - -We use this grading scale: - -| Points | Grade | -|---|---| -| 90 to 100 | A | -| 80 to 89 | B | -| 70 to 79 | C | -| 60 to 69 | D | -| 0 to 59 | F | - -With \`else if\` you check from top to bottom. The first condition that is true wins: - -\`\`\`java -String grade; -if (points >= 90) { - grade = "A"; -} else if (points >= 80) { - grade = "B"; -} else if (points >= 70) { - grade = "C"; -} else if (points >= 60) { - grade = "D"; -} else { - grade = "F"; -} -\`\`\` - -::: ludwig -With 85 points, \`points >= 90\` is false, but \`points >= 80\` is true. So grade B, and the remaining conditions are not checked at all. That is why the lower limit is enough each time. -::: - -You can try this part without a window in the [Code](#zeige-codelive) area. Put \`int points = 85;\` in front of it and \`System.out.println("Grade: " + grade);\` after it. The structure chart shows you the nested branches. - -::: quiz -? Which grade does the key give for 29 points? -- [ ] 4 -- [ ] 5 -- [x] 6 -> All conditions are false, even \`points >= 30\`. Then the \`else\` branch applies. -::: - -## The window - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class GradeCalculator { - public static void main(String[] args) { - JFrame window = new JFrame("Grade calculator"); - window.setSize(260, 180); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel question = new JLabel("Points (0 to 100):"); - JTextField field = new JTextField(); - JButton button = new JButton("Calculate grade"); - JLabel result = new JLabel("Grade: ?"); - - button.addActionListener(e -> { - int points = Integer.parseInt(field.getText()); - String grade; - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else if (points >= 60) { - grade = "D"; - } else { - grade = "F"; - } - result.setText("Grade: " + grade); - }); - - window.add(question); - window.add(field); - window.add(button); - window.add(result); - window.setVisible(true); - } -} -\`\`\` - -Try 100, 85, 60 and 12. - -## Catching wrong input - -Two things can still go wrong: - -1. Someone types “abc”. Then \`Integer.parseInt\` throws a \`NumberFormatException\`. -2. Someone types 150 or −5. That is a number, but not a valid score. - -You catch the first case with try/catch, the second with an \`if\` before working out the grade: - -\`\`\`java -button.addActionListener(e -> { - try { - int points = Integer.parseInt(field.getText()); - if (points < 0 || points > 100) { - result.setText("Please enter 0 to 100."); - } else { - // the grade calculation from above goes here - } - } catch (NumberFormatException error) { - result.setText("That is not a number."); - } -}); -\`\`\` - -::: ludwig -\`||\` means “or” (lesson 9). The condition is true if the points are too small **or** too big. -::: - -## Game: Which grade? - -::: spiel frage -titel: Game: Which grade? -intro: Ludwig has built grade calculators, but not all of them are right. Work out which grade comes out. Six rounds. - -runde: - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else { - grade = "D"; - } -frage: points is 80. Which grade comes out? -- A -- [x] B -- C -> 80 >= 90 is false, 80 >= 80 is true. The limit still belongs to B. - -runde: - if (points >= 90) { - grade = "A"; - } else if (points >= 80) { - grade = "B"; - } else if (points >= 70) { - grade = "C"; - } else if (points >= 60) { - grade = "D"; - } else { - grade = "F"; - } -frage: points is 69. Which grade comes out? -- C -- [x] D -- F -> 69 >= 70 is only just false, 69 >= 60 is true. So grade D. - -runde: - if (points >= 60) { - grade = "D"; - } else if (points >= 90) { - grade = "A"; - } else { - grade = "F"; - } -frage: points is 95. Which grade comes out? -- A -- [x] D -- F -> The first true condition wins. 95 >= 60 is already true, so A is never reached. That's why you check from the high points downwards. - -runde: - grade = "F"; - if (points >= 60) { - grade = "D"; - } - if (points >= 70) { - grade = "C"; - } -frage: points is 85. What is in grade afterwards? -- F -- D -- [x] C -> Without \`else\` every \`if\` is checked. Both are true, and the last one sets grade C. - -runde: - // grade has already been worked out - if (points < 0 || points > 100) { - result.setText("Please enter 0 to 100."); - } else { - result.setText("Grade: " + grade); - } -frage: The text field says 100. What does the window show? -- [x] Grade: A -- Please enter 0 to 100. -- That is not a number. -> 100 > 100 is false. So 100 points are valid and give grade A. - -runde: - // grade has already been worked out - if (points < 0 && points > 100) { - result.setText("Please enter 0 to 100."); - } else { - result.setText("Grade: " + grade); - } -frage: The text field says 150. What does the window show? -- Please enter 0 to 100. -- [x] a grade, although 150 is not valid -- That is not a number. -> With \`&&\` the points would have to be below 0 and above 100 at the same time. That never happens, so the program never warns. The right one is \`||\`. -::: - -## Task: grade calculator - -Put the grade calculator together, with both checks from the last step. Test it with 90, 89, 0, 100, 101 and “ten”. - -Extra: also write a word for the grade, for example “Grade: B (good)”. The words are excellent, good, satisfactory, poor and fail. - -**You are done when …** - -- 92 and 100 give a 1, 91 a 2 and 0 a 6, -- 101 shows the hint to enter 0 to 100, -- “ten” shows a hint and the program keeps running. - -::: ludwig -Always test the limits: 90 must be an A, 89 a B. Most mistakes hide at the limits, for example \`>\` instead of \`>=\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can turn points into a grade with else if. → The grading scale -- I can show the grade in the window. → The window -- I can catch and report “abc” and points outside 0 to 100. → Catching wrong input -- I can test a program at the limits, for example with 90 and 89. → Task: grade calculator -::: - -## Quiz - -::: quiz -? Which grade does the scale give for 70 points? -→ The grading scale -- [ ] B -- [x] C -- [ ] D -> 70 is the lower limit for a C. - -? Why is the lower limit enough in each condition? -→ The grading scale -- [x] Because else if has already sorted out the higher cases -- [ ] Because Java doesn't know upper limits -- [ ] Because the points are always 100 - -? Which condition detects invalid points? -→ Catching wrong input -- [ ] points < 0 && points > 100 -- [x] points < 0 || points > 100 -- [ ] points >= 0 || points <= 100 -> A number can't be smaller than 0 and bigger than 100 at the same time, so you need or. -::: -`,cy=`--- -titel: A calculator -bereich: Windows with Swing -kurz: Two columns with GridLayout, four arithmetic buttons and catching division by 0. ---- - -## Skills I will gain now - -::: kompetenzen -- build two columns with GridLayout -- explain why "" + (a + b) needs brackets -- program all four arithmetic buttons -- build a calculator that doesn't crash with 0 or “abc” -::: - -## The goal - -We build a small calculator: two text fields for the numbers, four buttons **+**, **-**, **\\*** and **/** and a line for the result. - -New this time are **two columns** in the GridLayout and **several buttons** that all calculate with the same text fields. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Two columns - -So far \`GridLayout(0, 1)\` put everything below each other. \`GridLayout(0, 2)\` gives you two columns. The building blocks fill the cells in the order of the \`add\` lines, always left first, then right: - -| left | right | -|---|---| -| First number: | text field | -| Second number: | text field | -| + | - | -| \\* | / | -| Result: | ? | - -You won't need the labels on the left later. So you can build them directly inside \`add\`, without a variable: - -\`\`\`java -window.add(new JLabel("First number:")); -window.add(field1); -\`\`\` - -## The window with the first button - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Calculator { - public static void main(String[] args) { - JFrame window = new JFrame("Calculator"); - window.setSize(280, 220); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 2)); - - JTextField field1 = new JTextField(); - JTextField field2 = new JTextField(); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - JButton times = new JButton("*"); - JButton divide = new JButton("/"); - JLabel result = new JLabel("?"); - - plus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a + b)); - }); - - window.add(new JLabel("First number:")); - window.add(field1); - window.add(new JLabel("Second number:")); - window.add(field2); - window.add(plus); - window.add(minus); - window.add(times); - window.add(divide); - window.add(new JLabel("Result:")); - window.add(result); - window.setVisible(true); - } -} -\`\`\` - -Type 3 and 4 and click +. The result shows 7. - -::: ludwig -Watch the brackets in \`"" + (a + b)\`. Without them Java works from left to right: \`"" + a\` gives the text “3”, and \`"3" + b\` appends the 4. You get “34” instead of 7. The bracket makes sure the calculation happens first. -::: - -## The other three buttons - -The buttons -, \\* and / work the same way. Only the operator in the last line changes: - -\`\`\`java -minus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a - b)); -}); -\`\`\` - -/ has two special features: - -- With \`int\`, Java cuts off the decimal places when dividing. 7 / 2 gives 3. -- If the second number is 0, Java throws an \`ArithmeticException\`, as in the task “Dividing safely” in lesson 22. - -## Catching division by 0 - -\`\`\`java -divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } -}); -\`\`\` - -::: angeberwissen -With \`double\` and \`Double.parseDouble(…)\` you get decimal places: 7 / 2 gives 3.5. In return, Java throws no exception when dividing decimal numbers by 0. The result is then \`Infinity\`. -::: - -::: quiz -? What does the calculator show for 8 divided by 0? -- [ ] 0 -- [ ] Infinity -- [x] Not by 0! -> With \`int\`, \`a / b\` throws an \`ArithmeticException\`. The \`catch\` catches it and shows the message. -::: - -## A method of your own - -This is not in the curriculum, but it makes the code shorter. - -::: angeberwissen -Every listener reads the same two fields. You can move this repetition into a method of your own, a subprogram like in lesson 18. It goes in the class next to \`main\`: - -\`static int number(JTextField field) { return Integer.parseInt(field.getText()); }\` - -The listener then just says \`result.setText("" + (number(field1) + number(field2)));\`. -::: - -## Game: What does the calculator show? - -::: spiel frage -titel: Game: What does the calculator show? -intro: Ludwig has been tinkering with the calculator. Read the listener and predict what the result shows. Six rounds. - -runde: - plus.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + a + b); - }); -frage: You type 3 and 4 and click +. What does the result show? -- 7 -- [x] 34 -- 12 -> Without brackets Java works from left to right: \`"" + 3\` is the text "3", and \`+ 4\` adds the 4 to the end. - -runde: - times.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + a * b); - }); -frage: You type 3 and 4 and click *. What does the result show? -- 34 -- [x] 12 -- 7 -> Multiplication comes before addition here too: Java first works out 3 * 4, then adds the 12 to the text. Plus doesn't get this priority, which is why it needs the brackets. - -runde: - divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - result.setText("" + (a / b)); - }); -frage: You type 7 and 2 and click /. What does the result show? -- 3.5 -- [x] 3 -- 4 -> With \`int\` Java cuts off the decimal places. It doesn't round. - -runde: - divide.addActionListener(e -> { - int a = Integer.parseInt(field1.getText()); - int b = Integer.parseInt(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } - }); -frage: You type 8 and 0 and click /. What does the result show? -- 0 -- [x] Not by 0! -- Infinity -> With \`int\`, dividing by 0 throws an ArithmeticException, and \`catch\` writes the hint. - -runde: - divide.addActionListener(e -> { - double a = Double.parseDouble(field1.getText()); - double b = Double.parseDouble(field2.getText()); - result.setText("" + (a / b)); - }); -frage: You type 7 and 2 and click /. What does the result show? -- 3 -- [x] 3.5 -- 3.0 -> With \`double\` the decimal places are kept. - -runde: - divide.addActionListener(e -> { - double a = Double.parseDouble(field1.getText()); - double b = Double.parseDouble(field2.getText()); - try { - result.setText("" + (a / b)); - } catch (ArithmeticException error) { - result.setText("Not by 0!"); - } - }); -frage: You type 8 and 0 and click /. What does the result show? -- Not by 0! -- [x] Infinity -- 0.0 -> With \`double\` there is no exception. The result is \`Infinity\`, and the \`catch\` never gets its turn. -::: - -## Task: calculator - -Finish the calculator: - -- all four buttons calculate, -- / reports “Not by 0!” instead of crashing, -- if a field contains “abc” or nothing, “Please enter two numbers” appears. - -Extra: a fifth button **C** empties both fields and sets the result back to “?”. - -**You are done when …** - -- 7 and 2 give 9, 5, 14 and 3 for +, −, * and /, -- 7 / 0 shows the “not by 0” message, -- “abc” or an empty field shows the “enter two numbers” message and nothing crashes. - -::: ludwig -For “abc” every listener needs a try/catch with \`NumberFormatException\`. A \`try\` may also be followed by two \`catch\` blocks, one for \`NumberFormatException\` and one for \`ArithmeticException\`. By the way, an empty field gives the text \`""\`, and that isn't a number either. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build two columns with GridLayout(0, 2). → Two columns -- I can explain why "" + (a + b) needs brackets. → The window with the first button -- I can program all four arithmetic buttons. → The other three buttons -- I can build a calculator that doesn't crash with 0 or “abc”. → Task: calculator -::: - -## Quiz - -::: quiz -? a is 3 and b is 4. What does "" + a + b give? -→ The window with the first button -- [ ] 7 -- [x] 34 -- [ ] An error -> Java works from left to right: first the text "3" is created, then the 4 is appended. - -? What does 7 / 2 give with int? -→ The other three buttons -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? In which order does GridLayout(0, 2) fill the cells? -→ Two columns -- [x] Row by row, left first, then right -- [ ] Column by column, starting top to bottom on the far left -- [ ] Randomly -::: -`,dy=`--- -titel: Guess the number -bereich: Windows with Swing -kurz: A game with a random number, a counter for the guesses and a button that switches itself off at the end. ---- - -## Skills I will gain now - -::: kompetenzen -- create a random whole number from 1 to 100 with Math.random -- decide which variables belong in the class -- switch a button off with setEnabled -- build a guessing game with a “New game” button -::: - -## The goal - -Time for a game: the computer thinks of a number from 1 to 100. You guess, and the window tells you “too small” or “too big”. Once you have found it, it tells you how many guesses you needed. - -New this time are **random numbers** and a button that **switches itself off** at the end. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## A random number - -\`Math.random()\` gives a random decimal number from 0 to just under 1, for example 0.4173. Three steps turn it into a whole number from 1 to 100: - -\`\`\`java -int secret = (int) (Math.random() * 100) + 1; -\`\`\` - -| Step | Example | -|---|---| -| \`Math.random()\` | 0.4173 | -| \`* 100\` | 41.73 | -| \`(int)\` cuts off the decimal places | 41 | -| \`+ 1\` | 42 | - -Without \`+ 1\` you would get 0 to 99. - -Try it in the [Code](#zeige-codelive) area: \`int secret = (int) (Math.random() * 100) + 1;\` and \`System.out.println(secret);\`. Every run gives a different number. - -::: angeberwissen -Computers can't actually roll dice. \`Math.random()\` uses a formula to calculate a long sequence of numbers that only **look** random. They are called pseudo-random numbers. For games that is perfectly fine. -::: - -## What the game remembers - -The game has to remember two things across all clicks: the secret number and the number of guesses. As with the click counter from lesson 32, both go in the class: - -\`\`\`java -static int secret = (int) (Math.random() * 100) + 1; -static int guesses = 0; -\`\`\` - -::: quiz -? Why are \`secret\` and \`guesses\` in the class and not in \`main\`? -- [ ] Because \`Math.random()\` only works in the class -- [x] Because they must last over all clicks and the listener has to change \`guesses\` -- [ ] Because \`main\` cannot store numbers -> Like in the click counter: the listener must not change variables from \`main\`. -::: - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class GuessTheNumber { - static int secret = (int) (Math.random() * 100) + 1; - static int guesses = 0; - - public static void main(String[] args) { - JFrame window = new JFrame("Guess the number"); - window.setSize(320, 180); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JLabel hint = new JLabel("I'm thinking of a number from 1 to 100."); - JTextField field = new JTextField(); - JButton button = new JButton("Guess"); - - button.addActionListener(e -> { - int guess = Integer.parseInt(field.getText()); - guesses++; - if (guess < secret) { - hint.setText(guess + " is too small."); - } else if (guess > secret) { - hint.setText(guess + " is too big."); - } else { - hint.setText("Correct! You needed " + guesses + " guesses."); - button.setEnabled(false); - } - field.setText(""); - }); - - window.add(hint); - window.add(field); - window.add(button); - window.setVisible(true); - } -} -\`\`\` - -- \`field.setText("")\` empties the text field after each guess, so you can type the next one straight away. -- \`button.setEnabled(false)\` switches the button off. It turns grey and can't be clicked any more. The game is over. - -::: ludwig -\`setEnabled(true)\` switches a button back on. That way you can, for example, only allow a button once something has been entered. -::: - -## Winning in 7 guesses - -::: angeberwissen -If you guess cleverly, you never need more than 7 guesses. Always guess the middle of the range that is left: first 50, then 25 or 75 and so on. Each guess halves the range: 100, 50, 25, 13, 7, 4, 2, 1. That is the **binary search** from lesson 19. Because 2 to the power of 7 = 128 is bigger than 100, 7 guesses are enough. -::: - -## Task: guess the number - -Rebuild the game and improve it: - -- An input like “abc” doesn't count as a guess and shows the hint “Please enter a number”. -- A second button **New game** thinks of a new number, sets the guesses to 0, switches the Guess button back on and resets the hint. - -Extra: after 7 wrong guesses the game is lost. The window then reveals the secret number. - -**You are done when …** - -- every wrong guess shows “too small” or “too big”, -- “abc” shows the hint to enter a number and the number of guesses stays the same, -- **New game** sets the guesses to 0, turns the guess button back on and resets the hint. - -::: ludwig -For “doesn't count as a guess”, \`guesses++\` must come **after** \`Integer.parseInt\`, both in the \`try\` block. If \`parseInt\` throws an exception, the line with \`guesses++\` is skipped. -::: - -## Game: Too small or too big? - -::: spiel frage -titel: Game: Too small or too big? -intro: Ludwig is playing guess the number. Read the code and predict what happens. Seven rounds. - -runde: - // secret is 42 - if (guess < secret) { - hint.setText(guess + " is too small."); - } else if (guess > secret) { - hint.setText(guess + " is too big."); - } else { - hint.setText("Correct!"); - } -frage: Ludwig types 50. What does the hint say? -- 50 is too small. -- [x] 50 is too big. -- Correct! -> 50 is greater than 42. - -runde: -frage: The number is between 1 and 100. 50 was too big, 25 was too small. Which number does Ludwig cleverly guess next? -- 26 -- [x] 37 -- 49 -> 26 to 49 are left. The middle of that is 37 (or 38). That way every guess halves the range. - -runde: - int roll = (int) (Math.random() * 6) + 1; -frage: Which numbers can \`roll\` have? -- 0 to 6 -- [x] 1 to 6 -- 1 to 7 -> \`Math.random() * 6\` is from 0 to just under 6. \`(int)\` turns that into 0 to 5, and \`+ 1\` into 1 to 6. A dice! - -runde: - int number = (int) (Math.random() * 10); -frage: Which numbers can \`number\` have? -- [x] 0 to 9 -- 1 to 10 -- 0 to 10 -> Without \`+ 1\` it starts at 0, and 10 is never reached. - -runde: - // secret is 42 - guesses++; - if (guess == secret) { - hint.setText("Correct! You needed " + guesses + " guesses."); - } -frage: Ludwig types 50, then 25, then 42. What does the hint say at the end? -- Correct! You needed 2 guesses. -- [x] Correct! You needed 3 guesses. -- Correct! You needed 42 guesses. -> \`guesses++\` counts up with every click, including the hit. - -runde: - button.setEnabled(false); -frage: Ludwig has found the number and clicks **Guess** once more. What happens? -- The game starts again. -- It counts one more guess. -- [x] Nothing, the button is grey and can't be clicked. -> \`setEnabled(false)\` switches the button off. - -runde: -frage: The number is now between 1 and 1000. How many guesses do you need at most with binary search? -- 7 -- [x] 10 -- 500 -> 2 to the power of 10 = 1024 is greater than 1000. Ten halvings are enough. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a random whole number from 1 to 100 with Math.random. → A random number -- I can decide which variables belong in the class. → What the game remembers -- I can switch a button off with setEnabled. → The whole program -- I can build a guessing game with a “New game” button. → Task: guess the number -::: - -## Quiz - -::: quiz -? Which numbers does (int) (Math.random() * 100) + 1 give? -→ A random number -- [ ] 0 to 100 -- [x] 1 to 100 -- [ ] 1 to 101 - -? What does button.setEnabled(false) do? -→ The whole program -- [ ] It removes the button from the window. -- [x] The button turns grey and can't be clicked any more. -- [ ] It changes the text on the button. - -? With binary search, how many guesses do you need at most for 1 to 100? -→ Task: guess the number -- [ ] 50 -- [ ] 10 -- [x] 7 -> Each guess halves the range, and 2 to the power of 7 = 128 is bigger than 100. -::: -`,hy=`--- -titel: Many buttons, one listener -bereich: Windows with Swing -kurz: One listener for many buttons, and finding out with getActionCommand and getSource which button was clicked. ---- - -## Skills I will gain now - -::: kompetenzen -- store a listener in a variable of type ActionListener and sign it up at several buttons -- explain what getActionCommand and getSource return -- put buttons onto a JPanel in a loop -- build a code lock that only opens with the right code -::: - -## The goal - -Since the business with the cake, Ludwig wants to lock his stable. He builds a **code lock**: nine digit keys, a zero, **C** to clear and **OK** to check. Only someone who types 1234 gets "Gate open!". - -That's twelve buttons. Until now every button had its own listener, as with the calculator in lesson 35. Today you learn how **one** listener is enough for all buttons. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Nine almost identical listeners - -With one listener per key it would look like this: - -\`\`\`java -one.addActionListener(e -> display.setText(display.getText() + "1")); -two.addActionListener(e -> display.setText(display.getText() + "2")); -three.addActionListener(e -> display.setText(display.getText() + "3")); -// ... and so on up to 9 -\`\`\` - -The lines only differ in the digit. If you want to change a small detail, you have to do it nine times and are bound to forget one line. - -## A listener as a variable - -A listener can also be stored in a **variable**. Its data type is \`ActionListener\`, the contract from lesson 30. Then you sign up the same listener at several buttons: - -\`\`\`java -ActionListener digit = e -> display.setText(display.getText() + e.getActionCommand()); - -one.addActionListener(digit); -two.addActionListener(digit); -three.addActionListener(digit); -\`\`\` - -\`ActionListener\` is in the package \`java.awt.event\`. So you need \`import java.awt.event.*;\` at the top. - -Now the code exists only once. But how does the listener know which digit to add? - -## Who clicked? - -The \`e\` before the arrow is the **event**, an object of type \`ActionEvent\`. The button passes it along with every click, and it contains what happened: - -| Call | Returns | Example | -|---|---|---| -| \`e.getActionCommand()\` | the caption of the clicked button, as text | \`"7"\` | -| \`e.getSource()\` | the clicked button itself | the button \`ok\` | - -- \`getActionCommand()\` is handy when the caption already says what to do, as with the digits. -- You compare \`getSource()\` with a button using \`==\`: \`if (e.getSource() == ok)\`. Here \`==\` is right, because it is about exactly this one object, not about a text. - -::: ludwig -You compare texts with \`equals\` and buttons with \`==\`. If you want to decide by the caption, you write \`e.getActionCommand().equals("OK")\`. That works too, but breaks as soon as someone changes the caption to "Open". \`e.getSource() == ok\` stays correct. -::: - -## Buttons in a loop - -With a shared listener you can even build the digit keys in a loop. So that they line up neatly in a grid, they go onto a **JPanel**. A JPanel is an area in the window with its own layout: - -\`\`\`java -JPanel keys = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton digit = new JButton("" + i); - digit.addActionListener(key); - keys.add(digit); -} -\`\`\` - -- \`"" + i\` turns the number \`i\` into the text for the caption. -- Each pass creates a new button. All of them get the same listener \`key\`. -- \`window.add(keys, BorderLayout.CENTER)\` puts the whole area with all the keys into the window. - -::: quiz -? How many buttons and how many listeners does this loop create? -- [ ] 9 buttons and 9 listeners -- [x] 9 buttons and 1 listener -- [ ] 1 button and 9 listeners -> Each pass adds a new button. All of them get the same listener \`key\`. -::: - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class CodeLock { - public static void main(String[] args) { - JFrame window = new JFrame("Code lock"); - window.setSize(240, 300); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new BorderLayout()); - - JLabel display = new JLabel("", SwingConstants.CENTER); - JPanel keys = new JPanel(new GridLayout(0, 3)); - JButton clear = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener key = e -> { - if (e.getSource() == clear) { - display.setText(""); - } else if (e.getSource() == ok) { - if (display.getText().equals("1234")) { - display.setText("Gate open!"); - } else { - display.setText("Wrong"); - } - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton digit = new JButton("" + i); - digit.addActionListener(key); - keys.add(digit); - } - JButton zeroKey = new JButton("0"); - zeroKey.addActionListener(key); - clear.addActionListener(key); - ok.addActionListener(key); - keys.add(clear); - keys.add(zeroKey); - keys.add(ok); - - window.add(display, BorderLayout.NORTH); - window.add(keys, BorderLayout.CENTER); - window.setVisible(true); - } -} -\`\`\` - -- \`clear\` and \`ok\` are created **before** the listener, because the listener compares with them. -- The listener first checks the two special buttons. All the others are digits and add their caption. - -::: angeberwissen -The keypad on a phone has the 1 at the top left, the one on a calculator and a computer keyboard has the 7 at the top left. Why? When push-button phones came along, there were tests with people who had to dial with them. With the 1 at the top they were faster, and calculating machines had their layout long before that. Ludwig's lock does it like the phone. -::: - -## Game: Who clicked? - -::: spiel zuhoerer -titel: Game: Who clicked? -intro: In every round **one** listener listens at several buttons. Ludwig shows you code, the window and the clicks. What does the display say at the end? Afterwards Ludwig plays the clicks. -frage: What does the display say after the clicks? - -runde: - JFrame window = new JFrame("Letters"); - JLabel display = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> display.setText(display.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> One listener, two buttons. Each time, \`e.getActionCommand()\` returns the caption of the clicked button, and it is added at the end. - -runde: - JFrame window = new JFrame("Counter"); - static int number = 0; - JLabel display = new JLabel("0"); - JButton plus = new JButton("+"); - JButton minus = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == plus) { - number++; - } else { - number--; - } - display.setText("" + number); - }; - plus.addActionListener(z); - minus.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> \`e.getSource()\` tells you which button it was. Plus twice, minus once: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame window = new JFrame("Letters"); - JLabel display = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> display.setText(display.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> The listener is only signed up at A and B. Nobody is listening when C is clicked. - -runde: - JFrame window = new JFrame("Keys"); - JLabel display = new JLabel(""); - JButton one = new JButton("1"); - JButton two = new JButton("2"); - JButton del = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - display.setText(""); - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - one.addActionListener(z); - two.addActionListener(z); - del.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> After 1 and 2 the display says "12". C clears everything, and the last 1 stands alone. - -runde: - JFrame window = new JFrame("Lock"); - JLabel display = new JLabel(""); - JButton four = new JButton("4"); - JButton two = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (display.getText().equals("42")) { - display.setText("Gate open!"); - } else { - display.setText("Wrong"); - } - } else { - display.setText(display.getText() + e.getActionCommand()); - } - }; - four.addActionListener(z); - two.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Gate open! -- [x] Wrong -- 24OK -> "24" was typed, but the secret is "42". The order of the clicks matters. -::: - -## Task: Code lock - -Build the code lock and try it out. - -Then make it better: - -- After three wrong attempts, **OK** switches itself off, like the button in Guess the number in lesson 36. Count the attempts in a \`static int\` variable in the class. -- Extra: the display shows \`*\` instead of the digits. Remember the real input in a \`static String input = "";\` and only add a star to the display. - -**You are done when …** - -- the right code opens the lock and a wrong one doesn’t, -- after three wrong tries **OK** is switched off, -- for the extra, the display only shows stars but the code is still checked. - -::: ludwig -In the branch for **OK**, count \`attempts++\` for every wrong code and then check \`if (attempts >= 3)\`. Then \`ok.setEnabled(false)\` switches the button off, like in the guessing game. For the stars: \`input = input + e.getActionCommand();\` and \`display.setText(display.getText() + "*");\`. Then you compare \`input\`, not the text of the display. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can store a listener in a variable of type ActionListener and sign it up at several buttons. → A listener as a variable -- I can explain what getActionCommand and getSource return. → Who clicked? -- I can put buttons onto a JPanel in a loop. → Buttons in a loop -- I can build a code lock that only opens with the right code. → Task: Code lock -::: - -## Quiz - -::: quiz -? What does e.getActionCommand() return for a click on the button new JButton("7")? -→ Who clicked? -- [ ] the number 7 -- [x] the text "7" -- [ ] the button itself - -? How do you check whether the button ok was clicked? -→ Who clicked? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource returns the button itself, and you compare it with ==. - -? Why is a listener stored in a variable? -→ A listener as a variable -- [ ] So that it runs faster. -- [x] So that you can sign it up at several buttons and write the code only once. -- [ ] Because lambdas aren't allowed otherwise. - -? Match. -→ Who clicked? -= ActionListener -> data type of a listener -= ActionEvent -> the event e with the information about the click -= JPanel -> an area with its own layout -::: -`,gy=`--- -titel: Inner classes -bereich: Windows with Swing -kurz: Write a listener as a named inner class, as an anonymous class and as a lambda, and know who can see what. ---- - -## Skills I will gain now - -::: kompetenzen -- write a listener as a named inner class -- write a listener as an anonymous class -- explain what is left out in a lambda -- explain which way of writing sees which variables -- build a counter with three buttons written in three ways -::: - -## The goal - -You already know the click counter from lesson 32. Today you write its listener in **three ways**, all of which do exactly the same: - -1. as a **named inner class**, the way it was mostly done before Java 8, -2. as an **anonymous class**, which you know from lesson 30 (“ActionListener is an interface”), -3. as a **lambda** with the arrow, as before. - -In other people's code, in books and on the internet you will meet all three. After this lesson you will recognise them. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Reminder: the contract - -A listener is anything that fulfils the contract \`ActionListener\`. It contains exactly one method: - -\`\`\` -public void actionPerformed(ActionEvent e) -\`\`\` - -The button calls it with every click. The three ways of writing are three ways of providing this one method. - -## 1. The named inner class - -An **inner class** is a class inside another class. It has a name, here \`Plus\`, and fulfils the contract with \`implements ActionListener\`: - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Counter { - static int count = 0; - static JLabel number = new JLabel("0", SwingConstants.CENTER); - - static class Plus implements ActionListener { - public void actionPerformed(ActionEvent e) { - count++; - number.setText("" + count); - } - } - - public static void main(String[] args) { - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - JButton again = new JButton("Also +1"); - plus.addActionListener(new Plus()); - again.addActionListener(new Plus()); - - window.add(number); - window.add(plus); - window.add(again); - window.setVisible(true); - } -} -\`\`\` - -- \`new Plus()\` creates an object of the class \`Plus\`. That is the listener the button gets. -- Because the class has a name, you can use it for several buttons, here for \`plus\` and \`again\`. -- The class is **outside** \`main\`. So it only sees what is in the class \`Counter\`: \`count\` and \`number\`. That's why \`number\` is a variable of the class this time and not of \`main\`. -- You need \`static class\` because \`main\` is static. Without \`static\`, javac reports "non-static variable this cannot be referenced from a static context". - -## 2. The anonymous class - -If you only need the listener in one place, you can write the class right where the object is needed. Then it gets no name: - -\`\`\`java -plus.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - count++; - number.setText("" + count); - } -}); -\`\`\` - -- \`new ActionListener() { … }\` means: "a new object that fulfils the contract ActionListener, like this: …". -- At the end there is \`});\`: the curly bracket closes the class, the round one closes the call to \`addActionListener\`. -- The anonymous class is **inside** \`main\`. So it also sees the variables of \`main\`, as long as they don't change any more. - -::: quiz -? What does \`});\` close at the end of the anonymous class? -- [x] \`}\` closes the class, \`)\` closes the call of \`addActionListener\`. -- [ ] \`}\` closes \`main\`, \`)\` closes the class. -- [ ] Both close the method \`actionPerformed\`. -> The method \`actionPerformed\` already ends one line earlier with its own \`}\`. -::: - -## 3. The lambda - -The lambda is the short form of the anonymous class, since Java 8: - -\`\`\`java -plus.addActionListener(e -> { - count++; - number.setText("" + count); -}); -\`\`\` - -Everything Java knows anyway is left out: \`new ActionListener()\`, the name \`actionPerformed\`, \`public void\` and the type \`ActionEvent\`. What remains is the parameter \`e\`, the arrow and the body. This only works because the contract has exactly **one** method. - -## The three compared - -| | named inner class | anonymous class | lambda | -|---|---|---|---| -| has a name | yes, e.g. \`Plus\` | no | no | -| stands | in the class, outside \`main\` | right at the button | right at the button | -| for several buttons | yes, \`new Plus()\` | no | yes, in a variable (lesson 37) | -| sees variables of \`main\` | no | yes, if they don't change | yes, if they don't change | -| length | longest | medium | shortest | - -::: ludwig -If a variable of \`main\` should change in the listener, like \`count++\`, it has to go into the class, whichever way you write the listener. That was the problem from lesson 32. -::: - -::: angeberwissen -After compiling, look into the folder with the \`.class\` files. javac creates one file for every class: \`Counter.class\`, \`Counter$Plus.class\` for the inner class and \`Counter$1.class\` for the anonymous class. As it has no name, it gets a number. A lambda gets no file of its own, Java only builds it when the program starts. -::: - -## Game: Which way of writing? - -::: spiel frage -titel: Game: Which way of writing? -intro: Ludwig shows you listeners from other people's programs. Which way of writing is it? Six rounds. -frage: Which way of writing is this? -antworten: named inner class | anonymous class | lambda - -runde: - button.addActionListener(e -> text.setText("Hello")); -richtig: lambda -> The arrow \`->\` gives the lambda away. - -runde: - button.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - text.setText("Hello"); - } - }); -richtig: anonymous class -> \`new ActionListener() {\` followed by a whole method: a class without a name is being written here. - -runde: - static class Greeting implements ActionListener { - public void actionPerformed(ActionEvent e) { - text.setText("Hello"); - } - } -richtig: named inner class -> The class is called \`Greeting\` and fulfils the contract with \`implements ActionListener\`. - -runde: - button.addActionListener(new Greeting()); -richtig: named inner class -> \`new Greeting()\` creates an object of the class \`Greeting\`. The class itself is somewhere else and has a name. - -runde: - ActionListener key = e -> { - display.setText(display.getText() + e.getActionCommand()); - }; -richtig: lambda -> A lambda in a variable, as with the code lock in lesson 37. The arrow gives it away. - -runde: - ActionListener key = new ActionListener() { - public void actionPerformed(ActionEvent e) { - display.setText(display.getText() + e.getActionCommand()); - } - }; -richtig: anonymous class -> An anonymous class can be stored in a variable too. Then it can be signed up at several buttons, just like the lambda. -::: - -## Task: One counter, three ways - -Build the click counter with three buttons: - -- **+1** gets the named inner class \`Plus\`. -- **-1** gets an anonymous class. -- **Reset** gets a lambda. - -Check that all three buttons count as expected. Then remove the \`static\` from the class \`Plus\` and read javac's error message. - -**You are done when …** - -- **+1** counts up, **-1** counts down and **Reset** sets 0, -- the code contains exactly one named inner class, one anonymous class and one lambda, -- you have read javac’s error message without \`static\` on \`Plus\` and can explain what it means. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can write a listener as a named inner class. → 1. The named inner class -- I can write a listener as an anonymous class. → 2. The anonymous class -- I can explain what is left out in a lambda. → 3. The lambda -- I can explain which way of writing sees which variables. → The three compared -- I can build a counter with three buttons written in three ways. → Task: One counter, three ways -::: - -## Quiz - -::: quiz -? Why does the short form with the arrow work for ActionListener? -→ 3. The lambda -- [ ] Because ActionListener is a class. -- [x] Because the contract has exactly one method. -- [ ] Because the button demands it. - -? Which way of writing can you use with new Plus() for several buttons without storing it in a variable? -→ 1. The named inner class -- [x] the named inner class -- [ ] the anonymous class -- [ ] the lambda - -? Why is there static in front of class Plus? -→ 1. The named inner class -- [ ] So that the class is faster. -- [x] Because main is static and couldn't create an object of Plus otherwise. -- [ ] Because every class has to be static. - -? Match. -→ The three compared -= class Plus implements ActionListener -> named inner class -= new ActionListener() { … } -> anonymous class -= the arrow after e -> lambda -::: -`,py=`--- -titel: Two tabs -bereich: Windows with Swing -kurz: Build a window with two tabs, bring data from one tab to the other and save the list as a text file. ---- - -## Skills I will gain now - -::: kompetenzen -- build a window with two tabs -- add lines to a JTextArea with append -- explain why a listener can use building blocks on both tabs -- save a file inside a listener with try and catch -- build a shopping list that counts how many things are on it -::: - -## The goal - -Ludwig's shopping list from lesson 23 gets a window. It has two **tabs**, like the tabs in a browser: - -- In the tab **Input** you type what you need and click **Add**. -- The tab **List** shows everything you have entered. A button **Save** writes the list into a text file. - -New are the tabs (\`JTabbedPane\`), a text area with several lines (\`JTextArea\`) and saving from inside a listener. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Tabs with JTabbedPane - -Each tab shows its own area. The areas are JPanels, like the keypad of the code lock in lesson 37: - -\`\`\`java -JTabbedPane tabs = new JTabbedPane(); -JPanel input = new JPanel(new GridLayout(0, 1)); -JPanel list = new JPanel(new BorderLayout()); - -tabs.addTab("Input", input); -tabs.addTab("List", list); -window.add(tabs); -\`\`\` - -| Call | Meaning | -|---|---| -| \`addTab("Input", input)\` | a new tab with the caption "Input", it shows the JPanel \`input\` | -| \`setSelectedIndex(1)\` | switches to the second tab, counting starts at 0 | -| \`setTitleAt(1, "List (3)")\` | changes the caption of the second tab | - -At the start, the tab that was added first is open. - -## A text area with several lines - -A \`JTextField\` has only one line. For the list you need a \`JTextArea\`: - -\`\`\`java -JTextArea area = new JTextArea(); -area.setEditable(false); -area.append("Hay\\n"); -\`\`\` - -- \`append\` adds text at the end. The \`"\\n"\` then starts a new line, as when splitting with \`split("\\n")\` in lesson 24. -- \`setEditable(false)\` stops people from typing into the list itself. -- \`getText()\` returns the whole text with all lines. - -::: quiz -? What does \`area.append("Hay\\n")\` do? -- [ ] It replaces the whole text with “Hay”. -- [x] It adds “Hay” at the end and then starts a new line. -- [ ] It writes “Hay\\n” into the list exactly like that. -> \`append\` adds at the end, \`"\\n"\` is the line break. -::: - -## From one tab to the other - -The button **Add** is on the tab Input, the text area on the tab List. Still, the listener can use both. All building blocks are variables in \`main\`, and the lambda sees all of them, no matter on which tab they are shown later: - -\`\`\`java -add.addActionListener(e -> { - area.append(field.getText() + "\\n"); - info.setText(field.getText() + " is on the list."); - field.setText(""); -}); -\`\`\` - -::: ludwig -A building block can only lie on **one** area. If you add the same button first to \`input\` and then to \`list\`, Swing takes it away from \`input\`. It only shows up on the tab List. -::: - -## Saving inside the listener - -To save, you use the \`PrintWriter\` from lesson 23. Inside the listener there is a catch, though: the way out with \`throws Exception\` from lesson 23 doesn't exist here. The lambda is a method of its own, \`actionPerformed\`, and according to the contract it must not pass on a \`FileNotFoundException\`. That's why saving goes into try and catch: - -\`\`\`java -save.addActionListener(e -> { - try { - PrintWriter out = new PrintWriter("shoppinglist.txt"); - out.print(area.getText()); - out.close(); - info.setText("Saved in shoppinglist.txt"); - tabs.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Saving didn't work."); - } -}); -\`\`\` - -- The exception is called \`ex\` here, because the name \`e\` is already taken by the event. -- After saving, the window jumps back to the tab Input with \`setSelectedIndex(0)\`, where the message is. -- VisuStruct shows the saved file below the console as soon as you have clicked Save. - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class ShoppingList { - public static void main(String[] args) { - JFrame window = new JFrame("Shopping list"); - window.setSize(300, 250); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane tabs = new JTabbedPane(); - - JPanel input = new JPanel(new GridLayout(0, 1)); - JTextField field = new JTextField(); - JButton add = new JButton("Add"); - JLabel info = new JLabel("Nothing on the list yet."); - input.add(new JLabel("What do you need?")); - input.add(field); - input.add(add); - input.add(info); - - JPanel list = new JPanel(new BorderLayout()); - JTextArea area = new JTextArea(); - area.setEditable(false); - JButton save = new JButton("Save"); - list.add(area, BorderLayout.CENTER); - list.add(save, BorderLayout.SOUTH); - - tabs.addTab("Input", input); - tabs.addTab("List", list); - - add.addActionListener(e -> { - area.append(field.getText() + "\\n"); - info.setText(field.getText() + " is on the list."); - field.setText(""); - }); - - save.addActionListener(e -> { - try { - PrintWriter out = new PrintWriter("shoppinglist.txt"); - out.print(area.getText()); - out.close(); - info.setText("Saved in shoppinglist.txt"); - tabs.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Saving didn't work."); - } - }); - - window.add(tabs); - window.setVisible(true); - } -} -\`\`\` - -Type Hay, click Add, then Apples and Add again. Switch to the tab List and click Save. - -## Game: Where does it end up? - -::: spiel frage -titel: Game: Where does it end up? -intro: Ludwig builds windows with tabs. Read the code and predict what you see. Six rounds. - -runde: - tabs.addTab("Input", input); - tabs.addTab("List", list); -frage: Which tab is open at the start? -- [x] Input -- List -- none -> At the start, the tab that was added first is open. - -runde: - tabs.addTab("List", list); - tabs.addTab("Input", input); -frage: Which tab is open at the start? -- Input -- [x] List -- both -> This time List comes first. The order of \`addTab\` decides the order of the tabs. - -runde: - tabs.addTab("Input", input); - tabs.addTab("List", list); - tabs.addTab("Help", help); - tabs.setSelectedIndex(1); -frage: Which tab is open? -- Input -- [x] List -- Help -> Counting starts at 0: 0 is Input, 1 is List, 2 is Help. - -runde: - add.addActionListener(e -> { - area.append(field.getText() + "\\n"); - field.setText(""); - }); - // You type Hay, click Add, type Apples, click Add. -frage: What is on the tab List afterwards? -- Apples -- HayApples -- [x] Hay\\nApples -> \`append\` adds at the end, and each \`"\\n"\` starts a new line. - -runde: - JButton button = new JButton("OK"); - input.add(button); - list.add(button); -frage: Where does the button OK show up? -- on both tabs -- only on the tab Input -- [x] only on the tab List -> A building block can only lie on one area. With the second \`add\`, Swing takes it away from \`input\`. - -runde: - save.addActionListener(e -> { - area.setText(""); - tabs.setTitleAt(1, "List (empty)"); - tabs.setSelectedIndex(0); - }); - // The button save is on the tab List. -frage: What do you see after the click? -- the empty tab List -- [x] the tab Input, and the second tab is called "List (empty)" -- the tab Input, the second tab is still called "List" -> The listener empties the area, renames the tab and switches to tab 0. All of it happens with one click. -::: - -## Task: Shopping list with tabs - -Build the program and try it out. - -Then make it better: - -- Empty inputs don't go onto the list. Check \`field.getText().isEmpty()\` first (lesson 17). -- The tab List shows in its title how many things are on the list: "List (3)". Count along in a \`static int\` variable and use \`setTitleAt\`. -- Extra: a third tab **Help** with a JLabel that explains how the program works. - -**You are done when …** - -- an entry you type appears in the list tab after the click, -- an empty field adds nothing to the list, -- after three entries the tab title shows the count, e.g. “List (3)”. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a window with two tabs. → Tabs with JTabbedPane -- I can add lines to a JTextArea with append. → A text area with several lines -- I can explain why a listener can use building blocks on both tabs. → From one tab to the other -- I can save a file inside a listener with try and catch. → Saving inside the listener -- I can build a shopping list that counts how many things are on it. → Task: Shopping list with tabs -::: - -## Quiz - -::: quiz -? Which tab is open after tabs.setSelectedIndex(0)? -→ Tabs with JTabbedPane -- [x] the first one -- [ ] the second one -- [ ] none - -? Why is saving inside the listener in try and catch? -→ Saving inside the listener -- [ ] Because PrintWriter only works inside try. -- [x] Because actionPerformed must not pass on a FileNotFoundException. -- [ ] Because main has no throws. - -? What is the difference between JTextField and JTextArea? -→ A text area with several lines -- [ ] There is none. -- [x] JTextArea can show several lines. -- [ ] You can't write into a JTextField. - -? Match. -→ Tabs with JTabbedPane -= JTabbedPane -> an area with tabs -= JPanel -> the area of a tab -= JTextArea -> a text area with several lines -::: -`,fy=`--- -titel: Model View Controller -bereich: Windows with Swing -kurz: Split the click counter into model, view and controller and understand who talks to whom. ---- - -## Skills I will gain now - -::: kompetenzen -- say what model, view and controller each do -- write a model without Swing and test it without a window -- explain what new Counter() builds and why there is no static -- name the four messages after a click -- add a second view without changing the model -- build a counter with −1, Reset and limits in the model -::: - -## The goal - -Big programs with windows almost always share the same structure. It is called **Model View Controller**, **MVC** for short: - -- The **model** holds the data and knows its rules. -- The **view** only shows the data. -- The **controller** reacts to clicks and passes the word on. - -You learn MVC with the click counter from lesson 32. It is split into three parts, step by step. In the end it shows the same count twice: as a number and as a bar. - -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). - -## Everything in one class - -This is what the click counter looked like so far, everything sits in \`main\` and in one variable of the class: - -\`\`\`java -static int count = 0; - -public static void main(String[] args) { - // ... build the window ... - JLabel number = new JLabel("0", SwingConstants.CENTER); - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - count++; - number.setText("" + count); - }); -} -\`\`\` - -That works. But imagine the count should also show up as a bar, later in a second window and in a file as well. Then every listener has to think of every display. Counting and showing are tied up in a knot, and with every display the knot gets bigger. - -MVC unties the knot: every job gets its own class. - -## The model - -The model is the counter itself, without any Swing: - -\`\`\`java -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} -\`\`\` - -- This time there is no \`static\` in front of \`value\` and the methods. That makes them belong to an **object**: \`new Counter()\` builds a counter with its own \`value\`, just as \`new JButton("+1")\` builds a button with its own caption. -- \`model.increase()\` calls the method of this one counter, just like \`button.setText(…)\`. -- Whoever wants to know the value asks with \`getValue()\`. It can only be changed through \`increase()\`. - -As the model needs no window, you can try it out without a window at all: - -\`\`\`java -public class CounterTest { - public static void main(String[] args) { - Counter model = new Counter(); - model.increase(); - model.increase(); - System.out.println(model.getValue()); - } -} - -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} -\`\`\` - -The output is 2. Several classes may stand in one file. Only one of them is \`public\`, and that one contains \`main\`. - -## The view - -The view shows the value and does nothing else. It doesn't calculate, and it doesn't know where the number comes from: - -\`\`\`java -class NumberView { - JLabel number = new JLabel("0", SwingConstants.CENTER); - - void update(int value) { - number.setText("" + value); - } -} -\`\`\` - -- The view is handed the value with \`update(int value)\` and writes it into its JLabel. -- You build the window and the button in \`main\`, as always. \`window.add(view.number)\` puts the view's JLabel into the window. - -## The controller - -The controller is the listener at the button. It connects model and view: - -\`\`\`java -Counter model = new Counter(); -NumberView view = new NumberView(); - -plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); -}); -\`\`\` - -- First it changes the model, then it fetches the new value and hands it to the view. -- The problem from lesson 32 is gone: the variable \`model\` never changes, it always points to the same counter. Only the \`value\` inside it changes. That's why the lambda may use \`model\`, without any \`static\`. - -## Who talks to whom? - -With every click four messages run, always in the same order: - -::: grafik mvc-klick.svg -The arrows light up one after the other. The model counts up at the second arrow, the display only at the fourth. -::: - -1. The button reports the click to the controller (\`actionPerformed\`). -2. The controller tells the model: \`increase()\`. -3. The controller asks the model for the new value: \`getValue()\`. -4. The controller hands the value to the view: \`update(value)\`. - -The model talks to nobody. It waits until it is asked. - -::: ludwig -Remember: the model knows nothing about windows. In \`Counter\` there isn't a single \`JLabel\` and no \`import javax.swing\`. If you need Swing there, the code belongs in the view. -::: - -::: quiz -? Who does the model talk to on its own? -- [ ] The view -- [ ] The button -- [x] Nobody -> The model waits until the controller asks it. -::: - -## The pay-off: a second view - -Now the count should also show up as a bar. For that a second view joins in. A \`JProgressBar\` is a bar that fills up, here from 0 to 10: - -\`\`\`java -class BarView { - JProgressBar bar = new JProgressBar(0, 10); - - void update(int value) { - bar.setValue(value); - } -} -\`\`\` - -The controller gets one more line. The model stays exactly as it was: - -\`\`\`java -plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); -}); -\`\`\` - -That is exactly the pay-off of MVC: you can add or swap displays without touching the model. - -## The whole program - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -class Counter { - int value = 0; - - void increase() { - value++; - } - - int getValue() { - return value; - } -} - -class NumberView { - JLabel number = new JLabel("0", SwingConstants.CENTER); - - void update(int value) { - number.setText("" + value); - } -} - -class BarView { - JProgressBar bar = new JProgressBar(0, 10); - - void update(int value) { - bar.setValue(value); - } -} - -public class ClickCounter { - public static void main(String[] args) { - Counter model = new Counter(); - NumberView view = new NumberView(); - BarView bar = new BarView(); - - JFrame window = new JFrame("Click counter"); - window.setSize(250, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JButton plus = new JButton("+1"); - plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); - }); - - window.add(view.number); - window.add(bar.bar); - window.add(plus); - window.setVisible(true); - } -} -\`\`\` - -Click +1 a few times and watch the number and the bar grow together. - -::: angeberwissen -MVC was invented in 1979 by the Norwegian Trygve Reenskaug, at the research centre Xerox PARC in California. Back then there were already computers with windows and a mouse there. In the same year Steve Jobs visited the lab and was so excited that Apple went on to build computers with windows and a mouse. -::: - -## Game: Where does the code belong? - -::: spiel frage -titel: Game: Where does the code belong? -intro: Ludwig has thrown code from an MVC program onto a pile. Sort every piece. Eight rounds. -frage: Where does this code belong? -antworten: Model | View | Controller - -runde: - int value = 0; -richtig: Model -> The count is the data. Data lives in the model. - -runde: - number.setText("" + value); -richtig: View -> Showing something on the screen is the view's job. - -runde: - plus.addActionListener(e -> model.increase()); -richtig: Controller -> Reacting to a click and telling the model: that's what the controller does. - -runde: - void increase() { - value++; - } -richtig: Model -> The model itself knows how to change its data. - -runde: - bar.setValue(value); -richtig: View -> A bar that fills up only shows something. So: view. - -runde: - void increase() { - if (value < 10) { - value++; - } - } -richtig: Model -> "At most 10" is a rule for the data. Rules belong in the model, then they hold for every button and every view. - -runde: - view.update(model.getValue()); -richtig: Controller -> Fetching the value from the model and handing it to the view: that's the controller in the middle. - -runde: - JLabel number = new JLabel("0", SwingConstants.CENTER); -richtig: View -> A JLabel is a building block of the screen. There is no Swing in the model. -::: - -## Game: Whose turn is it? - -::: spiel reihenfolge -titel: Game: Whose turn is it? -intro: After a click, model, view and controller talk to each other, but it only works in one order. Put the steps in the right order. Five rounds. - -runde: -frage: You click +1 in the click counter. What happens, one after the other? -- You click +1. -- The controller gets \`actionPerformed\`. -- The controller calls \`model.increase()\`. -- The model counts \`value\` up. -- The controller fetches \`model.getValue()\`. -- The view shows the new number. -> Click, controller, change the model, fetch the value, show it. Those are the four arrows from the picture, with counting up in the middle. - -runde: - plus.addActionListener(e -> { - model.increase(); - view.update(model.getValue()); - bar.update(model.getValue()); - }); -frage: What happens after a click on +1, one after the other? -- The click on +1 -- \`model.increase()\` -- The number shows the new value. -- The bar grows. -> The listener works through its lines from top to bottom: first the model, then the number, then the bar. - -runde: -frage: Put the controller in the right order so that the view shows the new number. -- \`plus.addActionListener(e -> {\` -- \`model.increase();\` -- \`view.update(model.getValue());\` -- \`});\` -> First change, then show. The other way round, the view still shows the old value. - -runde: - reset.addActionListener(e -> { - model.reset(); - view.update(model.getValue()); - }); -frage: The counter is at 5. You click Reset. What happens, one after the other? -- You click Reset. -- The model sets \`value\` to 0. -- \`model.getValue()\` returns 0. -- The view shows 0. -> Reset also runs through the controller into the model. The view only learns about the new value at the very end. - -runde: -frage: A test without a window should print 1. In which order do the lines stand in main? -- \`Counter model = new Counter();\` -- \`model.increase();\` -- \`System.out.println(model.getValue());\` -> First build the object, then work with it, and look at the result last. The model can be tested without any window. -::: - -## Task: A counter with limits - -Build the whole program and try it out. - -Then make it better: - -- A button **−1** counts down. The model gets a method \`decrease()\` for it, the controller a second listener. -- A button **Reset** sets the counter back to 0 with \`reset()\`. -- The model itself takes care of the limits: never below 0 and never above 10, because then the bar is full. -- Extra: a third view \`ConsoleView\` whose \`update\` prints "New value: 3" with \`System.out.println\`. A view doesn't have to be a window. - -**You are done when …** - -- **+1**, **−1** and **Reset** count correctly, -- the counter never goes below 0 or above 10, however often you click, -- number and bar always show the same value, -- the model class contains no \`JButton\`, \`JLabel\` or other Swing. - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can say what model, view and controller each do. → The goal -- I can write a model without Swing and test it without a window. → The model -- I can explain what new Counter() builds and why there is no static. → The model -- I can name the four messages after a click. → Who talks to whom? -- I can add a second view without changing the model. → The pay-off: a second view -- I can build a counter with −1, Reset and limits in the model. → Task: A counter with limits -::: - -## Quiz - -::: quiz -? Where does the rule "The counter never goes above 10" belong? -→ The model -- [x] in the model -- [ ] in the view -- [ ] in the controller -> Rules about the data belong in the model. Then they hold no matter which button is pressed. - -? What does the view do? -→ The goal -- [ ] It calculates the new value. -- [x] It shows the value it is given. -- [ ] It listens for clicks. - -? Why may the lambda use model, even though model is a variable of main? -→ Who talks to whom? -- [ ] Because Counter is public. -- [x] Because model always points to the same counter; only the value inside it changes. -- [ ] Because lambdas may change any variable. - -? What do you have to change in the model when a second view joins in? -→ The pay-off: a second view -- [x] nothing -- [ ] a second method increase -- [ ] a second JLabel - -? Match. -→ The goal -= Counter with value and increase() -> model -= NumberView with update(int value) -> view -= the listener at the button -> controller -::: -`,by=`Technical terms for the learning area. When a term appears in a step, it is marked there once and shows its explanation -on hover or tap. If it is in bold in that step, it is being explained right there and is not marked. -\`also:\` lists other forms that are recognised too. Upper and lower case matter. - -### Algorithm -also: algorithm, algorithms, Algorithms -Exact step-by-step instructions for solving a task. Every step is clear. - -### Structure chart -also: structure chart, structure charts, Structure charts, Nassi-Shneiderman diagram, Nassi-Shneiderman diagrams -An algorithm drawn as a picture made of boxes. You read it from top to bottom. It is also called a Nassi-Shneiderman diagram. - -### Flowchart -also: flowchart, flowcharts, Flowcharts -An algorithm drawn as symbols joined by arrows (DIN 66001 / ISO 5807). A diamond is a decision. - -### Pseudocode -also: pseudocode -An algorithm written as text in plain language with fixed words such as IF and WHILE. The indentation shows what belongs together. - -### Statement -also: statement, statements, Statements -A single step in the structure chart, for example a calculation, an input or an output. - -### Sequence -also: sequence, sequences, Sequences -Steps that run one after another from top to bottom, each exactly once. - -### Branch -also: branch, branches, Branches, conditional statement, conditional statements -A block that uses a condition to decide which of two paths runs. It is also called a conditional statement. - -### Path -also: path, paths -One of the two ways through a branch: left for true, right for false. - -### Loop -also: loop, loops, Loops, iteration, repetition -A block that repeats steps as long as a condition holds. - -### Loop body -also: loop body -The inside of a loop, that is, the steps that are repeated. - -### Pass -also: passes -Running the inside of a loop once. - -### Condition -also: condition, conditions, Conditions -A question that can only be answered with yes (true) or no (false), for example age >= 18. - -### Placeholder -also: placeholder, placeholders -An empty box with the sign ø. If you drag an element onto it, the element replaces it. - -### Object Inspector -The area below the diagram. Here you edit the text and settings of the box you just clicked. - -### Simulation -also: simulation -Runs your structure chart in the editor, step by step if you like. That way you see what happens. - -### Variable -also: variable, variables, Variables -A labelled box for a value. It has a name, a data type and a value. - -### camelCase -also: CamelCase -A way of writing names made of several words: written together, every further word starts with a capital, e.g. \`priceWithTax\`. - -### case-sensitive -Java tells upper and lower case apart: \`number\`, \`Number\` and \`NUMBER\` are three different names. \`system\` instead of \`System\` is an error. - -### Constant -also: constant, constants, Constants -A variable whose value stays the same once it is set. You write \`const\` in front of it, e.g. \`const double VAT = 0.19\`. - -### Data type -also: data type, data types, Data types -Says what kind of value fits into a variable: int (whole number), double (decimal number), String (text), boolean (true or false) or char (a single character). - -### Assignment -also: assignment, assignments, Assignments -The sign =. It works out the right-hand side and stores the result in the variable on the left. - -### Temporary variable -also: temporary variable, temporary variables, helper variable -An extra variable that keeps a value for a short time, for example when swapping. - -### IPO model -also: input-process-output -Input, process, output: first ask for values, then calculate, then show the result. - -### Problem analysis -also: problem analysis -You read a task carefully and work out what is given, what you are looking for and how to get from one to the other. - -### Comparison operator -also: comparison operator, comparison operators, Comparison operators -A sign that compares two values, for example == or <. The result is true or false. - -### Switch -also: switch, multiple selection -A block that runs exactly one of several cases, depending on the value of a variable. It is also called multiple selection. - -### Pre-test loop -also: pre-test loop, pre-test loops, head-controlled -The loop checks its condition before every pass. If the condition is false right at the start, the loop does not run even once. - -### Post-test loop -also: post-test loop, post-test loops, foot-controlled -The loop checks its condition only after every pass. So it always runs at least once. - -### Counter variable -also: counter variable, counter variables, counter -A variable that counts along in a loop, usually i. - -### Endless loop -also: endless loop, endless loops, infinite loop, infinite loops -A loop whose condition never becomes false. It never stops by itself. - -### Flag -also: flag, flags -A boolean variable that starts with false and is set to true as soon as something has happened. - -### State machine -also: state machine, state machines, state variable, state variables -A program that is always in exactly one state and changes to the next state by fixed rules, e.g. a traffic light. - -### Array -also: array, arrays, Arrays -A row of slots with one shared name and the same data type. - -### Index -also: index, indices, indexes -The number of a slot in an array. Counting starts at 0. - -### char -The data type for a single character. It goes in single quotation marks, e.g. 'C'. "C" with double quotation marks, on the other hand, is a String. - -### Character code -also: character code, character codes, ASCII code, ASCII table -The number behind a character. In the ASCII table A has the code 65, B 66 and so on. In Java (int) turns a character into its code and (char) turns a code into the character. - -### String -also: strings, Strings -A text, that is, a row of characters. In Java the data type is called String. The characters are counted from 0. - -### 2D array -also: 2D arrays, two-dimensional array, two-dimensional arrays -An array with rows and columns, like a table. A slot needs two indices, e.g. \`seats[1][2]\`. - -### Subprogram -also: subprogram, subprograms, Subprograms, method, methods, Methods, procedure, procedures, function, functions, Functions -A structure chart of its own for part of a task. It runs when it is called. In Java it is called a method; depending on its kind also a procedure or a function. - -### Parameter -also: parameter, parameters, Parameters -A variable in the header line of a subprogram. When it is called, it gets the value from the parentheses. - -### Overloading -also: overloaded, overload -Several methods with the same name but different parameters, e.g. \`mbot.forward(30)\` and \`mbot.forward(2, "rotations")\`. When calling, Java picks the right one by the number and type of the parameters. - -### Spaghetti code -also: spaghetti code -A program that jumps around so wildly that you can hardly follow what happens. Structure charts prevent that. - -### Call by value -also: call by value -A subprogram gets a copy of the value. If it changes its parameter, the caller's variable stays the same. - -### Call by reference -also: call by reference -A subprogram gets a reference to the same data, e.g. to an array. If it changes a slot, the caller sees the change too. - -### Return value -also: return value, return values, Return values -The result that a subprogram hands back to the call with return. - -### Break -A block that leaves the loop it is in straight away. Then the program continues below the loop. - -### Syntax error -also: syntax error, syntax errors, Syntax errors -A mistake in how the code is written, for example a missing \`;\`. The program does not start at all. - -### Runtime error -also: runtime error, runtime errors, Runtime errors -An error that only happens while the program runs, for example dividing by 0. The program stops at that point. - -### Logic error -also: logic error, logic errors, Logic errors -The program runs without any message but gives a wrong result. You find it with a desk check. - -### Desk check -also: desk check, desk checks, Desk checks -You run a structure chart on paper and write down the values of the variables after every step. - -### Linear search -also: linear search -A way of searching that compares all values one after another with the value you are looking for. It also works with unsorted values. - -### Binary search -also: binary search -A way of searching sorted values. It looks at the middle and keeps searching only in the matching half. - -### Bubble sort -also: bubble sort, Bubblesort -A simple way of sorting. It always compares two neighbours and swaps them if they are the wrong way round. - -### Event -also: event, events, Events -Something a person does in a window program, for example clicking a button. The program reacts to it with a listener (ActionListener). - -### Interface -also: Interfaces -A kind of contract in Java: it states which methods a class must have. \`ActionListener\` is an interface with the method \`actionPerformed\`. - -### Exception -also: exception, exceptions, Exceptions -An error that only happens while the program runs, for example an index outside the array. If no \`catch\` catches it, the program stops. - -### Scanner -A ready-made Java building block that reads input from the keyboard, for example a number with \`scanner.nextInt()\`. It needs \`import java.util.Scanner;\` at the very top. - -### comment -auch: comments, Comment, Comments -A note in the code for people. Java skips it when it runs. \`//\` lasts until the end of the line, \`/* … */\` can span several lines. - -### import -A line at the very top of Java code that fetches a ready-made building block from a package, for example \`import java.util.Scanner;\`. Without it, Java doesn't know the name. - -### source code -auch: Source code -The program text that people write and read, for example in a file \`TimesTable.java\`. - -### compiler -auch: compilers, Compiler -A program that translates source code. In Java it is called \`javac\` and turns a \`.java\` file into bytecode in a \`.class\` file. - -### bytecode -auch: Bytecode -The translated form of a Java program in a \`.class\` file. It is read by the JVM, not by people. - -### JVM -auch: Java Virtual Machine, virtual machine -The Java Virtual Machine runs bytecode. It is a program that works like a small computer made of software on Windows, macOS or Linux. That is why Java is platform independent. - -### JRE -Java Runtime Environment: the JVM and ready-made building blocks. You can run Java programs with it, but not compile them. - -### JDK -Java Development Kit: the JRE plus the compiler \`javac\` and other tools. You need it to program. - -### platform independent -A program runs without changes on different operating systems. In Java the JVM makes this possible: “Write once, run anywhere”. - -### machine code -auch: Machine code -Instructions as numbers that the processor runs directly. Every kind of processor has its own machine code. - -### JIT compiler -auch: JIT -A part of the JVM that translates often-used bytecode into machine code while the program runs (“just in time”). This makes Java faster. -`,xy=`## Basics - -### Statement - -A single step. You use it to create variables or to calculate. - -\`\`\` -int number = 5 -double price = 2.5 -String name = "Ada" -number = number + 1 -\`\`\` - -### Data types - -The data type comes before the name of the variable. - -| Data type | Meaning | Example | -|---|---|---| -| \`int\` | whole number | \`int count = 3\` | -| \`double\` | decimal number (with a point) | \`double price = 2.5\` | -| \`String\` | text in double quotation marks | \`String name = "Ada"\` | -| \`boolean\` | truth value | \`boolean done = false\` | -| \`char\` | a single character in single quotation marks | \`char grade = 'C'\` | - -\`'C'\` is a \`char\`, \`"C"\` is a String. You get a character from a text with \`charAt\`: \`char first = name.charAt(0)\`. Every character has a character code: \`(int) 'A'\` is 65, \`(char) 66\` is \`'B'\`. - -### Input - -Asks for a value while the chart runs and stores it in a variable. The text in quotation marks is the question. - -\`\`\` -input: int age "How old are you?" -input: double price "Enter a price" -input: String name "What is your name?" -\`\`\` - -### Output - -Shows a text or a value. Texts go in quotation marks; you join text and variables with \`+\`. - -\`\`\` -output: "Hello" -output: sum -output: "You are " + age + " years old." -\`\`\` - -> In an output, \`+\` always joins the parts together: with 3 and 4, \`output: a + b\` shows 34. So calculate in a variable first, e.g. \`int sum = a + b\`, and then output \`sum\`. - -## Branches - -### Branch - -Checks a condition. If it is true, the left path (true) runs, otherwise the right one (false). - -\`\`\` -age >= 18 -number % 2 == 0 -grade < 1 || grade > 6 -\`\`\` - -### Switch - -Picks one of several cases, depending on the value of a variable. If no case fits, \`default\` applies. You add cases on the right with **Add Case**. - -## Loops - -### While loop - -Repeats its inside **as long as** the condition is true. The condition is checked before every pass. - -\`\`\` -number != 1 -i < 10 -\`\`\` - -### Do-while loop - -Runs its inside **at least once** and checks the condition only afterwards. - -### For loop - -Counts a variable from a start value up to a limit. The three fields are Init, Condition and Step. - -\`\`\` -int i = 0 -i < 5 -i++ -\`\`\` - -### Endless loop - -Repeats its inside without a condition. A **Break** inside it lets you leave it. - -### Break - -Leaves the loop around it (\`break\`). - -## Subprograms - -### Call - -Stands for a subprogram, e.g. \`calculateSum()\`. With parameters and a return value, the subprogram has a header line as its title: - -\`\`\` -int square(int x) header line of the subprogram -return x * x hands back the result -int q = square(5) call, q gets 25 -showSum(3, 4) call of a void subprogram -\`\`\` - -## Calculating and comparing - -### Operators and comparisons - -- \`+ - * /\` the four basic operations -- \`%\` remainder of a division (\`7 % 2\` is 1) -- \`==\` equal, \`!=\` not equal -- \`<\` \`<=\` \`>\` \`>=\` less than, less than or equal, greater than, greater than or equal -- \`&&\` and, \`||\` or - -## Arrays - -### Arrays - -Several values under one name. Counting starts at 0. - -\`\`\` -int[] numbers = {3, 7, 1} -output: numbers[0] -int[] empty = new int[5] -\`\`\` - -A **2D array** is a table with rows and columns: - -\`\`\` -boolean[][] seats = new boolean[3][5] -seats[1][4] = true -output: seats.length + " rows, " + seats[0].length + " seats" -\`\`\` - -## Java - -### From code to program - -The compiler \`javac\` translates \`File.java\` into bytecode (\`File.class\`). The JVM translates the bytecode into machine code while it runs, the same way on Windows, macOS and Linux. C++, on the other hand, becomes machine code directly and has to be compiled again for every system. - -| | contains | for | -|---|---|---| -| JRE | JVM and libraries | running | -| JDK | JRE, compiler \`javac\`, tools | programming | - -### Comments - -Notes in the code. Java skips them, the structure chart stays the same. - -\`\`\`java -/* - * Task: add the numbers from 1 to 10 - * (multi-line comment) - */ -// Process (single-line comment) -int sum = 0; -for (int i = 1; i <= 10; i++) { - sum = sum + i; // comment after the code -} // end for -\`\`\` - -Good for: the task, dividing the code into input, process and output, an explanation, the end of a block after \`}\`, the expected output and notes for next time. -`,vy=`--- -titel: O que é um estrutograma? -bereich: Primeiros passos -kurz: Mostrar e ler algoritmos como uma série de passos claros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o que é um algoritmo -- ler um estrutograma -- reconhecer os três blocos básicos: sequência, decisão e ciclo -::: - -## Um algoritmo é uma instrução exata - -Um **algoritmo** descreve passo a passo como resolver uma tarefa. Cada passo tem de ser tão claro que qualquer um o consiga executar sem perguntar, tanto uma pessoa como um computador. - -Conheces algoritmos do dia a dia: uma receita, o caminho para um sítio ou as instruções para montar um roupeiro. - -::: diagramm l1-tee.visustruct -Fazer chá como estrutograma: cada bloco é um passo. -::: - -::: angeberwissen -Méééé, quem escreveu o primeiro programa? Uma mulher, já em 1843, muito antes do primeiro computador! **Ada Lovelace** descreveu passo a passo como a *Analytical Engine* (Máquina Analítica) de Charles Babbage devia calcular os chamados números de Bernoulli. Era um verdadeiro algoritmo para uma máquina. As instruções deviam ficar guardadas em cartões perfurados (mais sobre isto na lição 3). Mas a máquina nunca chegou a ser construída. - -A Ada teve ainda outra ideia genial: uma máquina destas consegue fazer mais do que contas. Também podia trabalhar com letras, símbolos ou até música. É exatamente isso que os computadores fazem hoje. Em sua honra, há uma linguagem de programação chamada **Ada**. -::: - -::: bild ada-lovelace.jpg -Ada Lovelace (1815–1852), matemática (imagem: aguarela de Alfred Edward Chalon, por volta de 1840, [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Ada_Lovelace_portrait.jpg), domínio público). -::: - -## Como se lê um estrutograma - -Um estrutograma lê-se **de cima para baixo**. Cada bloco é um passo. Só quando um passo termina é que começa o seguinte. - -Esta forma de desenhar foi inventada por **Isaac Nassi e Ben Shneiderman** (1973). Por isso também se chama diagrama de Nassi-Shneiderman. Na Alemanha está normalizado como DIN 66261. - -::: bild ike-nassi.jpg ben-shneiderman.jpg -À esquerda: Isaac “Ike” Nassi (foto: [Ed Schipul](https://commons.wikimedia.org/wiki/File:Me_with_Ike_Nassi!!!!!_%286893328729%29.jpg), [CC BY 2.0](https://creativecommons.org/licenses/by/2.0/), recortada), à direita: Ben Shneiderman (foto: [Robert Kosara](https://commons.wikimedia.org/wiki/File:Ben_Shneiderman_at_UNCC_%283x4_cropped%29.jpg), CC0). -::: - -> Um estrutograma tem sempre exatamente um início (em cima) e um fim (em baixo). Não há setas nem saltos para todo o lado. Programas que saltam para todo o lado chamam-se **código esparguete**, porque o seu fluxo se enrola como massa no prato. Mais sobre isto na próxima lição. - -::: angeberwissen -Méééé, ouve bem! Em 1972, Nassi e Shneiderman ainda eram doutorandos na Universidade de Stony Brook, perto de Nova Iorque. Shneiderman tinha uns 25 anos e Nassi uns 23. Ou seja, dois estudantes que escreveram história da informática nas horas vagas. -::: - -## Bloco básico 1: a sequência - -Qualquer algoritmo pode ser construído com apenas **três blocos básicos**: **sequência**, [**decisão**]{.blau} e [**ciclo**]{.rosa}. As cores ajudam-te a associá-los: as [instruções]{.gelb} são amarelas, as decisões azuis, os ciclos cor-de-rosa. A sequência não tem cor própria, é simplesmente a fila de instruções umas por baixo das outras. Vês as mesmas cores no editor e mais tarde no código. Podes desligá-las nas definições em “Blocos coloridos”. Neste passo e nos dois seguintes vais conhecê-los um a um. - -A **sequência** é o bloco mais simples: várias instruções são executadas **uma a seguir à outra**, cada uma exatamente uma vez. - -É assim que se parece: - -- Cada instrução é um **retângulo simples**. -- Os retângulos ficam **uns por cima dos outros**, sem espaço entre eles. -- São executados de cima para baixo, uma instrução depois da outra. - -::: diagramm l1-sequenz.visustruct -Quatro instruções em sequência. Primeiro levantas-te, depois lavas os dentes, e assim por diante. -::: - -> O exemplo do chá lá em cima também é uma sequência. - -::: angeberwissen -15 minutos! Foi o tempo de que Shneiderman precisou para o primeiro esboço com sequência, decisão e ciclo. Eu preciso de mais tempo do que isso para escolher entre feno e trevo. -::: - -## Bloco básico 2: a decisão - -Muitas vezes o passo seguinte depende de uma **condição**. Uma condição é uma pergunta a que só se pode responder com **sim ou não**, por exemplo *Está a chover?* No VisuStruct, sim é **true** e não é **false**. - -Para decisões destas existe a **decisão**. É assim que se parece: - -- Em cima, a condição fica dentro de um **triângulo**. -- Por baixo, o bloco divide-se em **duas colunas**: à esquerda o ramo para **true**, à direita o ramo para **false**. -- Só é executado **um** dos dois ramos. Depois, os dois ramos continuam juntos por baixo da decisão. - -::: diagramm l1-verzweigung.visustruct -Se estiver a chover, é executado o ramo da esquerda, senão o da direita. Depois, os dois casos continuam com “Sair”. -::: - -> Se num dos casos não deve acontecer nada, esse ramo fica vazio. O sinal **ø** indica isso. - -::: angeberwissen -Alerta de ortografia! Escreve-se Shneiderman, sem c. Quem escrever “Schneiderman” leva um empurrãozinho dos meus chifres. Ele próprio conta que as pessoas estão sempre a meter um c no nome dele. -::: - -## Bloco básico 3: o ciclo - -Se uma coisa deve acontecer **várias vezes**, não a escreves várias vezes. Em vez disso usas um **ciclo**, também chamado repetição. - -É assim que se parece: - -- Em cima está a condição, por exemplo *Prato não vazio*. -- Os passos que se repetem ficam **recuados** por baixo dela. Em cima e à esquerda, o bloco forma um **L invertido** à volta deles. -- Antes de cada iteração, a condição é verificada. Enquanto for **true**, os passos dentro do L invertido são executados mais uma vez. Se for **false**, o programa continua por baixo do ciclo. - -::: diagramm l1-wiederholung.visustruct -Enquanto o prato não estiver vazio, dás uma garfada. Depois, o prato é levantado. -::: - -> Há mais tipos de ciclos. Vais conhecê-los nas próximas lições. - -## Os três juntos - -Os blocos básicos tornam-se realmente úteis quando os combinas. Este diagrama contém os três: uma sequência de cima para baixo, uma [decisão]{.blau} e um [ciclo]{.rosa}. As caixas amarelas são [instruções]{.gelb} simples. As instruções dentro de uma decisão ou de um ciclo também são amarelas. Em rigor, o diagrama todo é uma sequência, porque a decisão e o ciclo também estão na fila de cima para baixo. - -::: diagramm l1-grundbausteine.visustruct -Reconheces a decisão pelo triângulo e o ciclo pelo L invertido. Tudo junto forma uma sequência. -::: - -Os blocos também podem ser **encaixados** uns dentro dos outros: um ciclo ou um ramo pode voltar a conter sequências, decisões e ciclos. - -::: angeberwissen -Consolo para quem correu mal um teste: a famosa revista *Communications of the ACM* rejeitou o artigo sobre estrutogramas. Segundo Nassi, o revisor até disse que deviam juntar todas as cópias e queimá-las. Ai! O artigo saiu em 1973 numa revista mais pequena e hoje é citado milhares de vezes. Ha! -::: - -## Primeiro a estrutura, depois o código - -Quando programas, acontecem duas coisas ao mesmo tempo: tens de pensar nos passos e prestar atenção à forma como a linguagem de programação se escreve. Um estrutograma permite-te separar as duas coisas. - -1. Primeiro pensas na **lógica**: que passos, que condições, que repetições? -2. Depois traduzes a estrutura pronta para **Java, Python ou JavaScript**. O VisuStruct até consegue gerar o código por ti. - -Com a **simulação** também podes executar o teu estrutograma passo a passo e verificar se ele faz mesmo o que deve. - -## Jogo: Que bloco básico? - -::: spiel frage -titel: Jogo: Que bloco básico? -intro: O Ludwig descreve coisas do seu dia a dia. Que bloco básico se esconde em cada uma? Oito rondas. -antworten: sequência | decisão | ciclo - -runde: -frage: "Pasta na escova, lavar os dentes, bochechar." -richtig: sequência -> Três passos, cada um exatamente uma vez e sempre por esta ordem. - -runde: -frage: "Se chover, levo o guarda-chuva, senão os óculos de sol." -richtig: decisão -> Uma condição decide qual dos dois caminhos é seguido. - -runde: -frage: "Mexer até a massa ficar lisa." -richtig: ciclo -> Mexe-se repetidamente enquanto a massa ainda não estiver lisa. - -runde: -frage: "Correr dez voltas ao campo de desporto." -richtig: ciclo -> A mesma volta é corrida dez vezes. - -runde: -frage: "Calçar os sapatos, vestir o casaco, abrir a porta, e lá vamos." -richtig: sequência -> Um passo a seguir ao outro, nada é saltado nem repetido. - -runde: -frage: "Se o semáforo estiver verde, atravessas a rua." -richtig: decisão -> Mesmo sem "senão" é uma decisão. O ramo senão fica simplesmente vazio. - -runde: -frage: "Lançar o dado até sair um seis." -richtig: ciclo -> Não se sabe antes quantas vezes. Lança-se até a condição se cumprir. - -runde: -frage: "Se alguém disser 'Entra', entras. Senão, esperas à porta." -richtig: decisão -> A resposta decide entre dois caminhos: entrar ou esperar. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o que é um algoritmo. → Um algoritmo é uma instrução exata -- Consigo ler um estrutograma de cima para baixo. → Como se lê um estrutograma -- Consigo nomear os três blocos básicos: sequência, decisão e ciclo. → Bloco básico 1: a sequência -- Consigo reconhecer cada bloco básico num estrutograma pela sua forma. → Os três juntos -::: - -## Teste de conhecimentos - -::: quiz -? Em que direção se lê um estrutograma? -→ Como se lê um estrutograma -- [ ] Da esquerda para a direita -- [x] De cima para baixo -- [ ] De baixo para cima -> Cada bloco é um passo, e os passos seguem-se de cima para baixo. - -? Qual destes NÃO é um dos três blocos básicos? -→ Bloco básico 1: a sequência -- [ ] Sequência -- [ ] Decisão -- [x] Salto -- [ ] Ciclo -> Num estrutograma não há saltos. Sequência, decisão e ciclo chegam para qualquer algoritmo. - -? Como reconheces uma decisão num estrutograma? -→ Os três juntos -- [ ] Por um L invertido -- [x] Por um triângulo com a condição e duas colunas por baixo -- [ ] Por vários retângulos uns por cima dos outros -> O L invertido pertence ao ciclo, e retângulos uns por cima dos outros são uma sequência. - -? Quando é que o interior de um ciclo é executado mais uma vez? -→ Os três juntos -- [x] Enquanto a condição for verdadeira -- [ ] Só se a condição for falsa -- [ ] Sempre exatamente duas vezes -> A condição é verificada antes de cada iteração. Se for falsa, o programa continua por baixo do ciclo. - -? O que é um algoritmo? -→ Um algoritmo é uma instrução exata -- [x] Instruções claras, passo a passo, para resolver uma tarefa -- [ ] Um determinado programa em Java -- [ ] Uma imagem que explica um programa -> Um algoritmo não depende de nenhuma linguagem de programação. Um estrutograma é uma forma de o escrever. -::: -`,wy=`--- -titel: Fluxograma e pseudocódigo -bereich: Primeiros passos -kurz: Representar algoritmos como fluxograma e como pseudocódigo e compará-los com o estrutograma. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ler um fluxograma (em inglês *flowchart*) e construir um sozinho -- explicar porque é que os saltos num fluxograma levam a erros -- ler um algoritmo simples em pseudocódigo -::: - -## Outras formas de desenhar: o fluxograma - -Há mais duas formas de representar um algoritmo: o **fluxograma** e o **pseudocódigo**. Vais conhecer as duas nas aulas. Em inglês, o fluxograma chama-se **flowchart**. Esta palavra aparece muitas vezes, por exemplo em programas e na internet. - -Aqui vês outra vez o nosso estrutograma para beber chá da última lição. A seguir vamos ver o fluxograma, com o mesmo exemplo. - -::: diagramm l1-grundbausteine.visustruct -O exemplo do chá como estrutograma: primeiro a chávena, depois a decisão para o açúcar e depois o ciclo. -::: - -### O mesmo algoritmo como fluxograma - -::: grafik l01-pap.svg -O exemplo do chá como fluxograma. A seta de “Esperar 1 minuto” para cima forma o ciclo. -::: - -### Os símbolos - -Um fluxograma é feito de poucos símbolos ligados por **setas**. Está normalizado como DIN 66001 (ISO 5807). - -- **Oval:** início ou fim. Cada fluxograma tem exatamente um início e um fim. -- **Retângulo:** uma instrução, por exemplo *Pôr uma chávena na mesa*. -- **Losango:** uma decisão. No losango está uma pergunta, e dele saem duas setas: **sim** e **não**. -- **Paralelogramo:** uma entrada ou saída. No exemplo do chá não aparece. -- **Seta:** mostra qual é o passo seguinte. - -Um ciclo não tem símbolo próprio no fluxograma. Reconheces um ciclo por uma seta que volta **para cima**. - -### As vantagens - -- **Muito fácil de ler**, mesmo para quem nunca viu um fluxograma. Basta seguir as setas com o dedo. -- Os fluxogramas não se usam só na programação, mas em todo o lado onde se explica um processo: em manuais de instruções, na procura de avarias (“A lâmpada acende? Não: a ficha está ligada?”) ou nas empresas para os processos de trabalho. - -## Exercício: Fazer um bolo como fluxograma - -Agora és tu que constróis um fluxograma. O Ludwig faz um bolo assim: - -Põe os ingredientes na tigela e mexe a massa. Se a massa ainda não estiver lisa, continua a mexer. Quando está lisa, deita a massa na forma e coze o bolo durante 30 minutos. Depois testa com um palito: se ainda tiver massa agarrada, coze mais 5 minutos e testa outra vez. Quando o palito sai limpo, tira o bolo e diz: “O bolo está pronto!” - -::: pap -titel: Exercício: Fazer um bolo como fluxograma -auftrag: Os lugares no fluxograma estão vazios. Põe cada elemento no seu lugar. A forma do lugar ajuda-te. -start: Início -anweisung: Pôr os ingredientes na tigela -anweisung: Mexer a massa -frage: Massa lisa? | nein -> Mexer a massa -anweisung: Deitar a massa na forma -anweisung: Cozer 30 minutos -frage: Palito limpo? | nein: Cozer mais 5 minutos -anweisung: Tirar o bolo do forno -ausgabe: “O bolo está pronto!” -ende: Fim -erklärung: Dois ciclos, duas setas para cima: ao mexer, a seta volta a “Mexer a massa”; ao cozer, vai de “Cozer mais 5 minutos” de volta à pergunta. A saída fica no paralelogramo. -legende: ja -::: - -**Desenhar os teus próprios fluxogramas:** Com o **VisuFlow** desenhas o teu fluxograma com toda a facilidade. Os símbolos estão prontos numa barra: tocar num acrescenta o símbolo seguinte e liga-o logo com uma seta. Para começar, podes carregar o exemplo do chá. Guardas o teu fluxograma como ficheiro e exportas como imagem ou PDF, por exemplo para o teu caderno ou uma apresentação. [Abrir o VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Desvantagens do fluxograma: saltos - -Uma seta num fluxograma pode ir de qualquer símbolo para qualquer outro. Parece prático, mas é perigoso. - -Antigamente, os programas escreviam-se exatamente assim. Cada linha tinha um **número**, e o comando **GOTO**, em português “ir para”, saltava para um número de linha. Cada IR PARA é uma seta do fluxograma. O nosso exemplo do chá ficava assim: - -\`\`\` -10 Pôr uma chávena na mesa -20 SE não quer açúcar, IR PARA 40 -30 Juntar açúcar -40 SE chá não demasiado quente, IR PARA 70 -50 Esperar 1 minuto -60 IR PARA 40 -70 Beber o chá -\`\`\` - -### Mais uma linha, e os saltos ficam errados - -Agora melhoras o algoritmo: depois do açúcar, o saquinho de chá deve sair. Acrescentas a nova linha 40, e todas as linhas abaixo avançam um número. Mas os saltos continuam a apontar para os números antigos: - -\`\`\` -10 Pôr uma chávena na mesa -20 SE não quer açúcar, IR PARA 40 -30 Juntar açúcar -40 Tirar o saquinho de chá -50 SE chá não demasiado quente, IR PARA 70 -60 Esperar 1 minuto -70 IR PARA 40 -80 Beber o chá -\`\`\` - -Experimenta: a linha 70 já não te manda para a verificação da linha 50, mas para o saquinho. E quando o chá já está frio o suficiente, a linha 50 salta para a linha 70 em vez de *Beber o chá*. Ou seja, tiras o saquinho vezes sem conta e nunca bebes. A cada alteração tens de verificar todos os IR PARA. Num programa grande, com centenas de saltos, é fácil algo correr mal. - -### Para todo o lado: código esparguete - -Com setas também se pode saltar do meio de um ciclo para fora, ou de fora para o meio de um ciclo. Estes saltos tornam o fluxograma confuso muito depressa. Com muitos saltos, as setas cruzam-se até o fluxograma parecer um prato de esparguete. Por isso chama-se **código esparguete**. Mal se consegue seguir o fluxo, e os erros escondem-se bem. - -Um estrutograma não tem setas. Se acrescentares um bloco, tudo o que está abaixo desce com ele, e nenhum salto pode apontar para o sítio errado. Para sair de um ciclo mais cedo existe lá um bloco próprio e arrumado, a interrupção. Vais conhecê-la na lição 18. - -::: angeberwissen -Em 1968, o informático Edsger Dijkstra escreveu uma carta famosa: “Go To Statement Considered Harmful”, ou seja, “o GOTO é considerado prejudicial”. Desde então, os programas são construídos só com sequência, decisão e ciclo. Em Java, \`goto\` ainda hoje é uma palavra reservada, mas não a podes usar. Mééé, prefiro comer esparguete a programá-lo. -::: - -## Pseudocódigo e comparação - -**Pseudocódigo** é texto que parece um programa, mas está escrito em linguagem corrente. Palavras fixas como SE e ENQUANTO mostram os blocos, e a **indentação** mostra o que pertence a eles. Aqui também não há saltos. Este é o nosso exemplo do chá: - -\`\`\` -Pôr uma chávena na mesa -SE quer açúcar ENTÃO - Juntar açúcar -FIM SE -ENQUANTO chá ainda demasiado quente - Esperar 1 minuto -FIM ENQUANTO -Beber o chá -\`\`\` - -O pseudocódigo **não tem uma forma fixa**. Há quem escreva CASO em vez de SE, quem use uma seta para a atribuição (\`numero ← 5\`) ou quem não escreva as linhas FIM. Tudo isso é permitido. O importante é que qualquer pessoa o perceba sem explicação. Por isso, o pseudocódigo não é mais uma linguagem de programação que tens de aprender. - -::: angeberwissen -O pseudocódigo parece-te familiar? A linguagem de programação Python é muito parecida, e isso é de propósito: o seu criador, Guido van Rossum, queria que os programas se lessem quase como texto normal. O Python também usa a indentação para mostrar o que pertence junto. Mas atenção: o Python é uma linguagem de programação a sério, com regras fixas. Um espaço a mais e o computador reclama. O pseudocódigo é mais descontraído, tal como eu ao pequeno-almoço. -::: - -### Comparação - -- **Estrutograma:** blocos dentro de blocos, o ciclo é um L invertido. Vês a estrutura num relance, e saltos malucos são impossíveis. -- **Fluxograma:** símbolos com setas, o ciclo é uma seta que volta para cima. Muito fácil de imaginar, mas os fluxogramas grandes ficam depressa confusos, e são possíveis saltos para todo o lado. -- **Pseudocódigo:** texto com indentação, o ciclo fica entre ENQUANTO e FIM ENQUANTO. Rápido de escrever e já perto do código de um programa. - -> Os três descrevem o mesmo algoritmo. Quando sabes ler uma das formas, aprendes as outras depressa. - -::: angeberwissen -Nassi e Shneiderman estavam em guerra com as setas! O artigo deles de 1973 chamava-se “Flowchart Techniques for Structured Programming”. Com setas pode-se saltar para todo o lado até o fluxograma parecer um prato de esparguete. Num estrutograma isso é simplesmente impossível. Mééé, é assim que eu gosto: arrumado, como o meu estábulo. -::: - -## Jogo: Que representação? - -::: spiel frage -titel: Jogo: Que representação? -intro: O Ludwig descreve uma forma de representar um algoritmo sem dizer o nome. É o estrutograma, o fluxograma ou o pseudocódigo? Oito rondas. -antworten: estrutograma | fluxograma | pseudocódigo - -runde: -frage: “Quando algo se repete, uma das minhas setas aponta de volta para cima.” -richtig: fluxograma -> No fluxograma, o ciclo não tem símbolo próprio. Uma seta volta para cima. - -runde: -frage: “O meu ciclo parece um L invertido.” -richtig: estrutograma -> A caixa envolve os passos repetidos em cima e à esquerda. - -runde: -frage: “Sou feito de palavras como SE e ENQUANTO, e a indentação mostra o que pertence a quê.” -richtig: pseudocódigo -> O pseudocódigo é texto em linguagem normal, organizado como um programa. - -runde: -frage: “A minha decisão é um losango com as saídas sim e não.” -richtig: fluxograma -> No estrutograma, a condição fica num triângulo. - -runde: -frage: “As minhas setas podem enrolar-se para todo o lado como esparguete.” -richtig: fluxograma -> Como as setas podem ir para qualquer lado, no fluxograma é possível haver código esparguete. - -runde: -frage: “Não tenho uma forma fixa de escrever. Também podes escrever CASO em vez de SE.” -richtig: pseudocódigo -> O importante é que qualquer pessoa o perceba sem explicação. - -runde: -frage: “A condição fica em cima num triângulo, e por baixo divido-me em duas colunas.” -richtig: estrutograma -> É assim que a decisão aparece no estrutograma: true à esquerda, false à direita. - -runde: -frage: “Se inserires uma caixa, tudo o que está por baixo desce também. Nenhum salto pode falhar.” -richtig: estrutograma -> O estrutograma não tem setas nem saltos, por isso nada pode apontar para o sítio errado. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo nomear os símbolos de um fluxograma e ler um fluxograma simples. → Outras formas de desenhar: o fluxograma -- Consigo construir sozinho um algoritmo simples como fluxograma. → Exercício: Fazer um bolo como fluxograma -- Consigo explicar porque é que os saltos com GOTO levam a erros e a código esparguete. → Desvantagens do fluxograma: saltos -- Consigo ler um algoritmo simples em pseudocódigo. → Pseudocódigo e comparação -::: - -## Teste de conhecimentos - -::: quiz -? Como reconheces um ciclo num fluxograma? -→ Outras formas de desenhar: o fluxograma -- [ ] Por um L invertido -- [x] Por uma seta que volta para cima -- [ ] Pela palavra ENQUANTO -> O L invertido pertence ao estrutograma, e ENQUANTO ao pseudocódigo. - -? Associa cada símbolo do fluxograma ao seu significado. -→ Outras formas de desenhar: o fluxograma -= Oval -> Início ou fim -= Retângulo -> Instrução -= Losango -> Decisão -= Paralelogramo -> Entrada ou saída -> O losango tem duas saídas: sim e não. - -? Como se diz fluxograma em inglês? -→ Outras formas de desenhar: o fluxograma -- [x] Flowchart -- [ ] Structure chart -- [ ] Pseudocode -> Structure chart é o estrutograma. - -? Acrescentas uma nova linha a um programa com GOTO. O que pode acontecer? -→ Desvantagens do fluxograma: saltos -- [x] Os saltos apontam para as linhas erradas, porque os números mudaram -- [ ] Nada, os saltos ajustam-se sozinhos -- [ ] O programa fica automaticamente mais rápido -> Cada IR PARA aponta para um número de linha fixo. Se as linhas descem, o programa salta para o sítio errado. - -? No pseudocódigo, o que mostra que linhas pertencem a um ciclo? -→ Pseudocódigo e comparação -- [ ] A cor -- [x] A indentação e o FIM ENQUANTO -- [ ] Uma seta -> Tudo o que está entre ENQUANTO e FIM ENQUANTO está recuado e é repetido. -::: -`,yy=`--- -titel: O editor -bereich: Primeiros passos -kurz: Inserir elementos, escrever texto, mover e eliminar. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- inserir um elemento da área Elementos e substituir um marcador -- alterar o texto de um bloco e mudar entre Instrução, Entrada e Saída -- mover, copiar e eliminar blocos e anular passos -::: - -## O ecrã - -O VisuStruct tem dois editores: o [editor de estrutogramas](#zeige-editor), de que trata esta lição, e à direita a área [Código](#zeige-codelive) para código Java (a partir da lição 4). Se o editor de estrutogramas estiver recolhido, esta lição abre-o sozinha. As palavras azuis mostram-te as áreas, mesmo que estejam recolhidas: passa o rato por cima de uma, e a área abre-se ao lado da área de aprendizagem e acende-se. Se clicares nela (ou tocares nela), fica aberta. - -O editor de estrutogramas tem várias áreas. Passa o rato por cima de uma palavra azul ou toca nela, e essa área acende-se no editor; as áreas recolhidas, como os Elementos, abrem-se. - -- **Mesmo em cima, a [barra de ferramentas](#zeige-leiste):** Novo, Abrir e Guardar. À direita, a roda dentada das [Definições](#zeige-einstellungen): tamanho do texto e idioma. -- **Por baixo, as [ferramentas](#zeige-werkzeuge)** por cima dos elementos e do estrutograma: Anular, Cortar, Copiar, Colar, Eliminar, Exportar, Código e Simulação. Os botões só mostram ícones. Se puseres o rato por cima de um, aparece o nome. -- **À esquerda, os [Elementos](#zeige-elemente):** Aqui estão todos os blocos que podes arrastar para o estrutograma. Há quem chame a esta área a paleta. -- **Ao meio, o [estrutograma](#zeige-struktogramm):** Um diagrama novo contém apenas um marcador vazio, assinalado com **ø**. -- **Em baixo, o [Inspetor de objetos](#zeige-objektinspektor):** Aqui editas o texto do elemento selecionado. Se for pequeno ou grande demais, arrasta a linha com a pega por cima dele para cima ou para baixo. - -> Podes recolher esta área de aprendizagem a qualquer momento com o [ícone da barra lateral](#zeige-lernen) no topo desta área. Fica só a barra estreita “🎓 Aprender” à esquerda; um clique abre-a de novo. Ela fica aberta ao lado do editor, para poderes experimentar logo o que lês. Da mesma forma, há à esquerda a coluna [📁 Ficheiros](#zeige-dateien) para os teus programas guardados (lição 3) e à direita a área [Código](#zeige-codelive) para Java (a partir da lição 4). - -## Inserir um elemento - -Arrasta um elemento da área **Elementos** para o estrutograma. Enquanto arrastas, uma linha azul mostra onde ele vai ficar: - -- **Sobre o marcador ø:** O elemento substitui o marcador. -- **Sobre a metade de cima de um bloco:** O elemento fica antes dele. -- **Sobre a metade de baixo de um bloco:** O elemento fica depois dele. - -::: grafik l02-einfuegen.svg -O sítio onde largas decide onde fica o novo elemento. -::: - -Também funciona sem arrastar: **clica** num elemento da área Elementos (ou toca nele). Ele é então inserido depois do bloco selecionado. Se o marcador **ø** estiver selecionado, substitui-o. - -Com o teclado, usa a tecla **Tab** para saltar para um elemento e prime **Enter**. O novo elemento também fica depois do bloco selecionado. - -::: angeberwissen -Agarrar blocos e arrastá-los de um lado para o outro, tal como estás a fazer agora, tem um nome pomposo: **manipulação direta**. Foi Ben Shneiderman quem inventou o termo em 1982. Sim, o senhor dos estrutogramas. Aquele homem trabalhava mais do que eu quando estou a pastar. -::: - -## Escrever texto - -Clica num bloco. Ele fica com um contorno azul, e o seu texto aparece no Inspetor de objetos por baixo do diagrama. O que escreveres lá aparece logo no diagrama. - -Mais rápido é fazer **duplo clique** no bloco, ou **toque duplo** no tablet. Assim escreves o texto diretamente no estrutograma. **Enter** aceita, **Esc** cancela. Com o teclado, seleciona o bloco e carrega em **Enter**. - -O bloco da área Elementos chama-se **Instrução**. Em cima, no Inspetor de objetos, escolhes de que tipo deve ser: - -- **Instrução:** calcular ou definir alguma coisa, p. ex. \`int numero = 5\`. Vais aprender como na lição 4. -- **Entrada:** pedir um valor enquanto o estrutograma é executado. Isso vem na lição 6. -- **Saída:** mostrar alguma coisa. No diagrama fica com \`output:\` à frente, p. ex. \`output: "Olá"\`. - -Para começar, só precisas de Saída. Os textos que devem aparecer exatamente como estão ficam entre **aspas**. - -::: diagramm l2-ausgabe.visustruct -Duas saídas, uma a seguir à outra. Abre o diagrama no editor e inicia a [simulação](#zeige-simulation) nas ferramentas para veres a saída. Vais conhecê-la melhor na lição 7. -::: - -## Mover, copiar, eliminar - -- **Mover:** Arrasta um bloco existente com o rato para um novo sítio. -- **Copiar e colar:** Seleciona um bloco e prime Ctrl+C e Ctrl+V (no Mac ⌘C e ⌘V). A cópia fica depois do bloco selecionado. Sem teclado, por exemplo no tablet, toca em **Copiar** e **Colar** nas [ferramentas](#zeige-werkzeuge). -- **Eliminar:** Seleciona um bloco e prime Del, ou clica no [caixote do lixo](#zeige-loeschen) „Eliminar seleção“ nas ferramentas. -- **Anular:** Ctrl+Z anula o último passo, Ctrl+Shift+Z refá-lo (no Mac ⌘Z e ⌘⇧Z). Sem teclado, toca em [Anular](#zeige-rueckgaengig) nas ferramentas. - -## Exercício: Olá Mundo - -::: übung l2-hallo -titel: Olá Mundo -auftrag: Constrói um estrutograma que mostre **Olá Mundo**. -Arrasta uma **Instrução** dos Elementos para o marcador **ø** e clica no bloco. No Inspetor de objetos, por baixo do estrutograma, há três botões em cima: Instrução, Entrada e Saída. Clica em **Saída**. Depois escreve \`"Olá Mundo"\` no campo de texto por baixo, com aspas. -test: - -> Olá Mundo -braucht: anweisung -tipp: Só precisas de um único bloco. O texto que deve aparecer vai para o campo de texto do Inspetor de objetos. -tipp: Atenção às aspas. Sem elas, o VisuStruct procura uma variável chamada Olá. -lösung: l2-hallo-loesung.visustruct -::: - -Clica em **Verificar** quando terminares. O VisuStruct executa o teu estrutograma e compara a saída. - -## Exercício: Duas linhas - -::: übung l2-zwei-zeilen -titel: Duas linhas -auftrag: Mostra primeiro **Olá** e depois **Estou a aprender estrutogramas**, cada um na sua linha. -test: - -> Olá | Estou a aprender estrutogramas -tipp: Duas linhas significa duas saídas. Os blocos ficam uns por cima dos outros e são executados de cima para baixo. -tipp: Cada saída é um bloco próprio. Arrasta a segunda Instrução para a metade de baixo da primeira. -lösung: l2-zwei-zeilen-loesung.visustruct -::: - -## Jogo: Profissional do editor - -::: spiel frage -titel: Jogo: Profissional do editor -intro: O Ludwig testa se dominas o editor. Sete rondas. - -runde: -frage: Arrastas uma instrução para o marcador **ø**. O que acontece? -- [x] A instrução substitui o marcador. -- A instrução fica depois do marcador. -- Nada, não se pode largar nada sobre ø. -> O marcador só guarda o lugar. O novo elemento ocupa-o. - -runde: -frage: Arrastas um elemento para a metade **de cima** de um bloco. Onde fica? -- [x] antes dele -- depois dele -- Substitui o bloco. -> Metade de cima quer dizer antes, metade de baixo quer dizer depois. A linha azul mostra-te enquanto arrastas. - -runde: -frage: Arrastas um elemento para a metade **de baixo** de um bloco. Onde fica? -- antes dele -- [x] depois dele -- no fim do estrutograma -> Metade de baixo quer dizer: logo a seguir a este bloco. - -runde: -frage: Apagaste um bloco sem querer. O que o traz de volta? -- Ctrl+C / ⌘C -- [x] Ctrl+Z / ⌘Z -- Ctrl+V / ⌘V -> Ctrl+Z / ⌘Z anula o último passo. Ctrl+C / ⌘C copia, Ctrl+V / ⌘V cola. - -runde: -frage: Ao executar deve aparecer exatamente **Olá**. O que está no bloco? -- [x] output: "Olá" -- output: Olá -- "Olá" -> Um texto que deve aparecer tal e qual fica entre aspas. O editor põe \`output:\` à frente quando escolhes o tipo Saída. - -runde: -frage: Onde alteras o texto do bloco selecionado? -- na área Elementos -- [x] no Inspetor de objetos -- na barra de ferramentas lá em cima -> O Inspetor de objetos por baixo do diagrama mostra o texto do bloco selecionado. - -runde: -frage: Clicas num elemento da área Elementos, sem arrastar. Onde fica? -- [x] depois do bloco selecionado -- no início -- no fim -> Sem arrastar (clique, ou Tab e Enter), o novo elemento fica depois do bloco que está selecionado nesse momento. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo inserir um elemento da área Elementos e substituir um marcador. → Inserir um elemento -- Consigo alterar o texto de um bloco e mudar entre Instrução, Entrada e Saída. → Escrever texto -- Consigo mover, copiar e eliminar blocos e anular passos. → Mover, copiar, eliminar -::: - -## Teste de conhecimentos - -::: quiz -? O que significa o sinal ø num bloco? -→ Inserir um elemento -- [ ] O bloco contém um erro. -- [x] Aqui ainda há um marcador vazio. -- [ ] O programa termina aqui. -> Um marcador é substituído quando arrastas um elemento para cima dele. - -? Arrastas uma Instrução para a metade de baixo de um bloco. Onde é que ela fica? -→ Inserir um elemento -- [ ] Antes dele -- [x] Depois dele -- [ ] O bloco é substituído. - -? Como mostras o texto Bom dia exatamente como está? -→ Escrever texto -- [ ] output: Bom dia -- [x] output: "Bom dia" -- [ ] input: "Bom dia" -> Sem aspas, Bom dia é entendido como o nome de uma variável. -::: -`,zy=`--- -titel: Guardar, abrir, exportar -bereich: Primeiros passos -kurz: Guardar os teus ficheiros em segurança, usar o teu espaço de trabalho, abrir vários diagramas e partilhá-los como imagem ou PDF. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dar um título a um diagrama -- guardar um diagrama como ficheiro .visustruct e voltar a abri-lo -- exportar um diagrama como imagem (PNG) ou PDF -- abrir um segundo diagrama num novo separador -- criar o meu espaço de trabalho \`VisuStruct-Ficheiros\` -- guardar um programa no meu espaço de trabalho -::: - -## O ficheiro .visustruct - -Os estrutogramas são guardados como ficheiros **.visustruct**. Todas as versões do VisuStruct usam o mesmo formato: no navegador, no Mac, iPad e iPhone, e a app Java no computador. Assim podes começar no navegador na escola e continuar na app em casa. - -::: angeberwissen -Quando Nassi era estudante, não havia ficheiros para guardar. Havia cartões perfurados. Nassi conta que um encontro ao sábado à noite era assim: a futura mulher dele sentava-se com ele no centro de computação e perfurava-lhe os cartões. Romance à moda da informática! -::: - -::: grafik l03-lochkarte.svg l03-lochkarte-beschriftet.svg -Era assim um cartão perfurado. Cada cartão tinha uma linha de programa, aqui \`PRINT *, 'OLA MUNDO'\` na linguagem Fortran. Cada coluna é um carácter, e os furos são o seu código. Um programa inteiro era uma pilha destes cartões. Se caísse ao chão, era preciso voltar a ordenar tudo. -::: - -::: angeberwissen -Um pioneiro da informática da minha região na Alemanha: **Konrad Zuse**. A 12 de maio de 1941 apresentou em Berlim o **Z3**, o primeiro computador digital programável a funcionar no mundo. Já calculava no **sistema binário**, só com 0 e 1, com cerca de 2000 relés (interruptores elétricos). O programa não estava em cartões perfurados, mas sim em furos numa película de cinema usada. Depois, Zuse inventou o **Plankalkül**, uma das primeiras linguagens de programação. Mais tarde viveu em **Hünfeld**, perto de Fulda, até morrer em 1995. O Museu Konrad Zuse de lá mostra réplicas do Z3. A minha dica para uma visita de estudo! O horário está no site da cidade de Hünfeld. -::: - -::: grafik l03-zuse-lochstreifen.svg -Mais ou menos assim era a fita de programa de Zuse: uma película de cinema velha com furos. Cada coluna é uma instrução com 8 posições. Um furo significa 1, sem furo significa 0. A máquina lia a fita instrução a instrução. -::: - -## Guardar e abrir - -- **[Guardar](#zeige-speichern)** (Ctrl+S, no Mac ⌘S): O diagrama é guardado como ficheiro. Se a coluna **Ficheiros** estiver configurada (ver abaixo), fica lá. Caso contrário, o navegador pergunta onde o guardar ou põe o ficheiro na pasta de transferências. -- **[Abrir](#zeige-oeffnen)** (Ctrl+O, no Mac ⌘O): Voltar a carregar um ficheiro guardado. Também podes simplesmente arrastar um ficheiro para a janela do navegador. - -> O navegador memoriza os teus exercícios automaticamente. Mas isso só funciona neste navegador, neste dispositivo. Por isso, guarda sempre também os trabalhos importantes como ficheiro. - -## A coluna Ficheiros - -À esquerda da área de aprendizagem encontras a coluna [📁 Ficheiros](#zeige-dateien). Aí reúnes todos os teus programas num só sítio, o teu **espaço de trabalho**. - -**A tua vez:** Configura já o teu espaço de trabalho, antes de continuares a ler. A partir de hoje guardas lá todos os teus programas. - -1. Cria um espaço de trabalho: abre a coluna e clica em **Escolher pasta**. Na janela, vai aos teus **Documentos**, cria aí com **Nova pasta** uma pasta chamada \`VisuStruct-Ficheiros\` e seleciona-a. Permite que o VisuStruct use a pasta. Isto funciona no Chrome e no Edge. No iPad e noutros navegadores, clica antes em **Guardar no navegador**. -2. Guarda como de costume. No teu espaço de trabalho, o VisuStruct cria automaticamente uma pasta com a data de hoje, por exemplo \`2026-10-23\`, e guarda lá o programa. O nome do ficheiro vem do título. - -O teu espaço de trabalho fica assim: - -\`\`\`text -VisuStruct-Ficheiros ← espaço de trabalho -└── 2026-10-23 ← dia - └── Fatorial.visustruct -\`\`\` - -Um clique num programa da coluna volta a abri-lo. Assim encontras depressa cada aula mais tarde. - -Também o teu **progresso** fica no espaço de trabalho: o ficheiro \`learnProgress.json\` guarda as lições e os exercícios que já concluíste. Se abrires o espaço de trabalho noutro navegador, o teu progresso volta a estar lá. - -> Porquê uma pasta real? O teu progresso e os teus programas ficam lá guardados como ficheiros. Podes levar a pasta contigo, por exemplo numa pen USB ou na tua pasta na nuvem, como o OneDrive. Assim continuas em casa, noutro computador, exatamente onde paraste. Se guardares só no navegador, tudo fica apenas neste navegador, neste dispositivo. Se alguém apagar os dados do navegador, o teu progresso também desaparece. - -## Vários diagramas em separadores - -Por cima do estrutograma vês os [separadores](#zeige-reiter). Com **+** ou [Novo](#zeige-neu) abres mais um diagrama vazio. Um ponto • antes do nome mostra que há alterações não guardadas. - -O [título](#zeige-titel) de um diagrama escreve-se no campo por cima do estrutograma. Também aparece quando exportas e é sugerido como nome do ficheiro. - -## Partilhar: imagem, PDF ou ficheiro - -Com [Exportar](#zeige-exportieren) nas ferramentas (ícone com uma seta para cima) podes partilhar o teu estrutograma: - -- **Imagem (PNG):** para pôr numa ficha de trabalho ou numa apresentação -- **Gráfico vetorial (SVG):** fica nítido em qualquer tamanho -- **PDF (A4):** uma página para entregar ou imprimir -- **Ficheiro VisuStruct (.visustruct):** para passar a alguém que deva abrir e continuar a editar o teu estrutograma no VisuStruct, por exemplo por e-mail ou pela plataforma de aprendizagem -- **Imprimir** (Ctrl+P, no Mac ⌘P): diretamente para a impressora - -Na [roda dentada](#zeige-textgroesse) em cima à direita, em Tamanho do texto, defines o tamanho da letra. O texto maior também se nota quando exportas uma imagem. - -## Experimenta - -Abre um dos teus exercícios da lição 2 e experimenta o seguinte: - -1. Dá um nome ao diagrama no campo [título](#zeige-titel) por cima do estrutograma, por exemplo \`Olá Mundo\`. -2. [Guarda-o](#zeige-speichern) no teu espaço de trabalho. Vê na coluna [📁 Ficheiros](#zeige-dateien): está na pasta de hoje? -3. Abre um segundo separador com **+** e clica lá no teu programa na coluna Ficheiros. Volta a abrir-se. -4. [Exporta-o](#zeige-exportieren) como **imagem (PNG)** e como **PDF (A4)**. Abre os dois ficheiros e compara-os. - -Correu tudo bem? Então sabes onde estão os teus programas e como os partilhar. - -## Jogo: Como o partilhas? - -::: spiel frage -titel: Jogo: Como o partilhas? -intro: O Ludwig quer partilhar o seu estrutograma. Que formato serve melhor? Seis rondas. -frage: Que formato serve? -antworten: .visustruct | PNG | SVG | PDF - -runde: -frage: Amanhã queres continuar a trabalhar no teu estrutograma. -richtig: .visustruct -> Só o ficheiro .visustruct pode voltar a ser aberto e editado no VisuStruct. - -runde: -frage: O estrutograma deve entrar numa apresentação como imagem. -richtig: PNG -> Uma imagem PNG pode ser posta em qualquer apresentação e em qualquer ficha de trabalho. - -runde: -frage: O teu professor quer imprimir o teu trabalho numa página A4. -richtig: PDF -> O PDF é exatamente uma página A4 para entregar ou imprimir. - -runde: -frage: O estrutograma vai para um cartaz e deve ficar nítido mesmo em tamanho enorme. -richtig: SVG -> Um gráfico vetorial fica nítido em qualquer tamanho. Um PNG fica com pixéis quando o aumentas. - -runde: -frage: A tua amiga deve alterar o estrutograma na app VisuStruct do iPad dela. -richtig: .visustruct -> Todas as versões do VisuStruct abrem o mesmo ficheiro .visustruct: no browser, no Mac, no iPad e no iPhone. - -runde: -frage: Queres enviar depressa uma imagem para um chat. -richtig: PNG -> Um PNG pode ser visto logo por qualquer pessoa, sem VisuStruct. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dar um título a um diagrama. → Experimenta -- Consigo guardar um diagrama como ficheiro .visustruct e voltar a abri-lo. → Guardar e abrir -- Consigo exportar um diagrama como imagem (PNG) ou PDF. → Partilhar: imagem, PDF ou ficheiro -- Consigo abrir um segundo diagrama num novo separador. → Vários diagramas em separadores -- Consigo criar o meu espaço de trabalho \`VisuStruct-Ficheiros\`. → A coluna Ficheiros -- Consigo guardar um programa no meu espaço de trabalho. → A coluna Ficheiros -::: - -## Teste de conhecimentos - -::: quiz -? Que ficheiro podes voltar a abrir na app para Mac e na app Java? -→ Guardar e abrir -- [x] O ficheiro .visustruct -- [ ] A imagem PNG exportada -- [ ] O PDF exportado -> As imagens e os PDF servem só para ver. Só o ficheiro .visustruct pode continuar a ser editado. - -? O navegador memoriza os teus exercícios. Porque deves guardar mesmo assim? -→ Guardar e abrir -- [ ] Porque senão os exercícios não são verificados -- [x] Porque o navegador só guarda os dados neste dispositivo e eles podem perder-se -- [ ] Porque não se pode exportar sem guardar - -? Hoje guardas um programa no teu espaço de trabalho. Onde fica? -→ A coluna Ficheiros -- [ ] Na pasta de transferências -- [x] No espaço de trabalho, dentro de uma pasta com a data de hoje -- [ ] Diretamente no espaço de trabalho, sem subpasta -> O VisuStruct cria automaticamente uma pasta com a data de cada dia. Assim os programas ficam ordenados por aula. -::: -`,Iy=`--- -titel: Variáveis e tipos de dados -bereich: Fundamentos -kurz: Guardar valores, conhecer os tipos de dados int, double, String e boolean, e fazer contas. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar uma variável com tipo de dados, nome e valor -- explicar que o Java distingue maiúsculas de minúsculas (\`numero\` não é \`Numero\`) -- distinguir os tipos de dados int, double, String e boolean -- explicar que = é uma atribuição e não uma comparação -- fazer contas com + - * / % e usar a divisão inteira -- criar e mostrar variáveis em Java -::: - -## O que é uma variável? - -Uma **variável** é como uma caixa com etiqueta. Tem um **nome**, um **tipo de dados** e um **valor**. - -\`\`\` -int idade = 14 -\`\`\` - -Aqui a variável chama-se \`idade\`, o seu tipo de dados é \`int\` (número inteiro) e o seu valor é 14. Mais tarde podes voltar a usar o valor através do nome. - -::: grafik l04-variable.svg -A variável como caixa: o nome na etiqueta, o tipo de dados como autocolante, o valor lá dentro. -::: - -> Os nomes de variáveis começam por uma **letra minúscula** e não têm espaços. Bons nomes dizem o que está lá dentro: \`preco\` é melhor do que \`p\`. Evita acentos e cedilhas nos nomes: escreve \`preco\` e não \`preço\`. - -::: angeberwissen -Em setembro de 1999 perdeu-se a sonda Mars Climate Orbiter. Uma equipa calculava a força de impulso em unidades americanas (libras), a outra esperava unidades métricas (newtons). Nos dados só estava o número, não a unidade. A sonda voou demasiado baixo na atmosfera de Marte e ardeu, um prejuízo de cerca de 125 milhões de dólares. Um nome que diz o que guarda, como \`impulsoEmNewtonSegundos\`, teria evitado a confusão. Já agora, eu escrevo sempre \`fenoEmQuilos\` na minha caixa de feno. -::: - -Se um nome tiver várias palavras, escreves-as juntas e começas cada palavra seguinte com maiúscula: \`precoComIva\`, \`numeroDeAlunos\`. Esta forma de escrever chama-se **camelCase**. - -::: angeberwissen -O camelCase tem este nome porque as maiúsculas no meio saem da palavra como as bossas de um camelo: \`precoComIva\` tem duas bossas. Eu, como bode, tenho zero bossas, mas dois chifres. Infelizmente ainda ninguém inventou a escrita BodeCase. -::: - -::: grafik l04-camelcase.svg -As maiúsculas C e I sobressaem como as duas bossas do camelo. -::: - -## Maiúsculas e minúsculas - -O Java distingue **maiúsculas de minúsculas**. Em inglês diz-se **case-sensitive**. Para o Java, \`numero\`, \`Numero\` e \`NUMERO\` são três nomes diferentes, ou seja, três caixas diferentes: - -\`\`\` -int numero = 4 -int Numero = 6 -\`\`\` - -Aqui há duas variáveis. Se mais tarde escreveres \`NUMERO\`, o Java não encontra nenhuma variável com esse nome. O programa não corre e aparece uma mensagem de erro. - -> Isto também vale para as palavras do Java: \`String\` e \`System\` escrevem-se com maiúscula, \`int\` e \`double\` com minúscula. Nos teus programas, nunca cries duas variáveis que só se distinguem por maiúsculas e minúsculas. Isso confunde-te a ti e a quem lê o teu código. - -## Exercício: Três nomes - -::: übung l04-gross-klein -titel: Três nomes -auftrag: O Ludwig criou duas variáveis: \`numero\` com o valor 4 e \`Numero\` com o valor 6. O estrutograma deve multiplicá-las e mostrar **Resultado: 24**. Mas não corre. - Carrega em “Abrir no editor”, depois em Verificar, e lê a mensagem de erro. Corrige a instrução e carrega outra vez em Verificar. -start: l04-gross-klein-start.visustruct -test: - -> Resultado: 24 -braucht: anweisung -tipp: Quantos nomes diferentes há no estrutograma? \`numero\`, \`Numero\` e \`NUMERO\` são três nomes diferentes para o computador. -tipp: \`NUMERO\` não existe. Para 24 precisas de 4 vezes 6, ou seja, \`numero\` vezes \`Numero\`: \`int resultado = numero * Numero\`. Com \`numero * numero\` dá 16. -lösung: l04-gross-klein-loesung.visustruct -::: - -## Os quatro tipos de dados - -O tipo de dados diz que tipo de valor pode ser guardado na variável. - -- **int:** números inteiros, p. ex. \`int quantidade = 3\` ou \`int temperatura = -5\` -- **double:** números decimais, p. ex. \`double preco = 2.5\`. Escreves um **ponto**, nunca uma vírgula. -- **String:** texto, p. ex. \`String nome = "Inês"\`. O texto fica entre aspas. -- **boolean:** valores lógicos, só \`true\` ou \`false\` - -Há mais tipos de dados. Um deles vais precisar em breve: \`char\` para um **único caractere**, p. ex. \`char nota = 'C'\`. Um \`char\` fica entre aspas **simples**. Vais conhecê-lo melhor na lição 17. - -::: diagramm l05-datentypen.visustruct -Três variáveis com tipos de dados diferentes. Abre o diagrama e inicia a [simulação](#zeige-simulation). -::: - -Os valores lógicos aparecem sobretudo em **condições**: \`idade >= 18\` é \`true\` ou \`false\`. É disso que trata a lição 8. Uma variável do tipo \`boolean\` recebe simplesmente \`true\` ou \`false\`: - -\`\`\` -boolean adulto = false -\`\`\` - -::: angeberwissen -A 4 de junho de 1996, o foguetão Ariane 5 explodiu apenas 37 segundos depois da descolagem. A culpa foi de uma única linha: um número decimal para a velocidade foi metido numa variável inteira demasiado pequena para valores tão grandes. O tipo de dados não é um pormenor, pode custar meio milhar de milhões de euros. -::: - -## Atribuição com = - -O sinal \`=\` chama-se **atribuição**. Não quer dizer “é igual a” como na matemática. Quer dizer: calcula o lado direito e guarda o resultado na variável da esquerda. - -\`\`\` -int pontos = 10 -pontos = pontos + 5 -\`\`\` - -Na matemática, a segunda linha estaria errada, porque nenhum número é igual a si próprio mais 5. Na programação lê-se assim: pega no valor antigo 10, soma 5 e guarda 15 em \`pontos\`. O tipo de dados só se escreve da primeira vez, quando a variável é criada. - -::: grafik l04-zuweisung.svg -Primeiro calcula-se o lado direito, depois o resultado vai para a variável da esquerda. -::: - -## Fazer contas - -Podes fazer contas com números: - -- \`+\` mais, \`-\` menos, \`*\` vezes, \`/\` a dividir por -- \`%\` resto de uma divisão (diz-se “módulo”), p. ex. \`17 % 5\` dá 2 - -A **divisão com resto** conheces da escola primária, quando ainda não sabias números decimais: 17 : 5 = 3 e sobram 2. O 5 cabe três vezes no 17, o que dá 15. Até 17 faltam ainda 2: esse é o resto. O computador calcula com números inteiros exatamente assim: \`/\` dá o resultado sem o resto, \`%\` dá só o resto. - -| Escola primária | com \`/\` | com \`%\` | -|---|---|---| -| 17 : 5 = 3, resto 2 | \`17 / 5\` dá 3 | \`17 % 5\` dá 2 | -| 20 : 5 = 4, resto 0 | \`20 / 5\` dá 4 | \`20 % 5\` dá 0 | -| 3 : 5 = 0, resto 3 | \`3 / 5\` dá 0 | \`3 % 5\` dá 3 | - -Se o resto for 0, a divisão é exata. Mais tarde usas isto para ver se um número é par: nos números pares, \`numero % 2\` dá 0. - -**A multiplicação e a divisão vêm antes da adição e da subtração**, e os parênteses calculam-se primeiro: \`2 * (3 + 4)\` dá 14. - -### Mostrar texto e resultado juntos - -Um resultado sozinho diz pouco. Com \`+\` juntas um texto e o valor de uma variável: - -\`\`\` -int idade = 14 -output: "Idade: " + idade -\`\`\` - -Isto mostra **Idade: 14**. O texto entre aspas aparece tal e qual; em vez de \`idade\` o computador põe o valor. O espaço depois dos dois pontos faz parte do texto, senão aparece \`Idade:14\`. - -## Exercício: Retângulo - -::: übung l05-rechteck -titel: Calcular um retângulo -auftrag: Um retângulo tem 7 de comprimento e 4 de largura. Cria as variáveis \`comprimento\` e \`largura\`. Calcula a área e o perímetro em variáveis próprias e mostra os dois assim: -\`Área: 28\` e na linha seguinte \`Perímetro: 22\`. -test: - -> Área: 28 | Perímetro: 22 -braucht: anweisung -tipp: Pensa primeiro de que variáveis precisas: duas para as medidas e duas para os resultados. São todas números inteiros. -tipp: A área é o comprimento vezes a largura, o perímetro é 2 vezes (comprimento mais largura). Para a saída escreves p. ex. \`output: "Área: " + area\`. O \`+\` junta o texto e o valor da variável. -lösung: l05-rechteck-loesung.visustruct -::: - -## Divisão inteira - -Se dividires dois números inteiros e guardares o resultado numa variável \`int\`, tudo o que vem depois da vírgula desaparece. **Não é arredondado.** - -\`\`\` -int x = 7 / 2 dá 3 -int r = 7 % 2 dá 1 (o resto) -double d = 7.0 / 2 dá 3.5 -\`\`\` - -Com \`/\` e \`%\` juntos podes fazer boas conversões, por exemplo de minutos para horas e minutos: - -::: diagramm l05-division.visustruct -135 minutos são 2 horas (135 / 60) e sobram 15 minutos (135 % 60). -::: - -## Exercício: Converter segundos - -::: übung l05-sekunden -titel: Converter segundos -auftrag: 200 segundos devem ser mostrados em minutos e segundos. A primeira e a última instrução já lá estão. Insere entre elas instruções que calculem \`minutos\` e \`resto\`. -start: l05-sekunden-start.visustruct -test: - -> 3 minutos e 20 segundos -braucht: anweisung -tipp: Quantos minutos inteiros cabem em 200 segundos, e quantos segundos sobram? Calcula primeiro de cabeça. -tipp: Um minuto tem 60 segundos. Usa \`/\` para os minutos inteiros e \`%\` para o resto. As duas variáveis são do tipo \`int\`. -lösung: l05-sekunden-loesung.visustruct -::: - -## Exercício: Compras - -::: übung l05-einkauf -titel: Compras -auftrag: Compras 3 cadernos a 2.5 euros cada. Cria \`quantidade\` como \`int\` e \`preco\` como \`double\`, calcula o preço total numa variável \`precoTotal\` e mostra \`Total: 7.5 euros\`. -test: - -> Total: 7.5 euros -braucht: anweisung -tipp: A quantidade vezes o preço dá o preço total. Para cada variável, pensa se é um número inteiro ou um número decimal. -tipp: O preço total é um número decimal, por isso \`precoTotal\` precisa do tipo \`double\`. -lösung: l05-einkauf-loesung.visustruct -::: - -## Agora em Java - -Um estrutograma é um plano. Em **Java** torna-se um programa que o computador executa. As tuas compras do exercício ficam assim: - -\`\`\`java editor -int quantidade = 3; -double preco = 2.5; -double precoTotal = quantidade * preco; -System.out.println("Total: " + precoTotal + " euros"); -\`\`\` - -- Primeiro o tipo de dados, depois o nome, depois o valor, tal como no bloco. -- Cada instrução termina com um **ponto e vírgula** \`;\`. -- Em Java, uma saída escreve-se \`System.out.println(…)\`. - -Como é que o computador entende o teu código Java e porque é que ele corre igual no Windows, no macOS e no Linux, vais ver na lição “Estrutograma e código”. - -::: angeberwissen -O Java chamava-se primeiro **Oak** (carvalho), por causa da árvore em frente à janela do escritório de James Gosling. A partir de 1991, a sua equipa na Sun Microsystems queria programar pequenos aparelhos, como boxes de televisão. Mas o nome Oak já pertencia a outra empresa. Assim, em 1995 passou a chamar-se **Java**, como o café da ilha de Java. -::: - -## Texto em Java - -As variáveis não guardam só números. Para texto existe o tipo de dados \`String\`: - -\`\`\`java editor -String nome = "Inês"; -int idade = 14; -double altura = 1.62; -System.out.println(nome + " tem " + idade + " anos."); -System.out.println("Altura: " + altura + " m"); -\`\`\` - -- O texto fica entre aspas. -- \`String\` escreve-se com maiúscula, \`int\`, \`double\` e \`boolean\` com minúscula. O Java distingue-os. -- Com \`+\` juntas texto e números. - -Escreves Java na área [Código](#zeige-codelive). O estrutograma constrói-se sozinho enquanto escreves. “Abrir no editor” leva o exemplo para a área Código. Clica aí em “Executar”, depois muda o nome, a idade ou a altura e experimenta outra vez. - -::: übung l04-java-steckbrief -titel: Ficha em Java -bereich: beide -code: java -auftrag: Escreve a ficha do Ludwig, o bode. Cria três variáveis: o nome **Ludwig**, a idade **7** e a altura **1.2**. Depois mostra estas duas linhas: - **Ludwig tem 7 anos.** e **Altura: 1.2 m** - “Abrir no editor” abre também a área Código à direita. Escreve lá o teu código; o estrutograma constrói-se sozinho. Depois clica em Verificar. -test: - -> Ludwig tem 7 anos. | Altura: 1.2 m -braucht: anweisung -tipp: Precisas de uma \`String\`, um \`int\` e um \`double\`. Cada linha termina com ponto e vírgula. -tipp: \`String nome = "Ludwig";\`, \`int idade = 7;\` e \`double altura = 1.2;\`. Juntas a saída com \`+\`: \`System.out.println(nome + " tem " + idade + " anos.");\` -lösung: l04-java-steckbrief-loesung.visustruct -::: - -## Preenche os espaços: tipos de dados - -::: lückentext -titel: Ficha em Java -auftrag: Preenche as palavras que faltam. Por fora está a moldura de que qualquer programa Java precisa num ficheiro próprio. As variáveis ficam lá dentro. -\`\`\`java -[[public]] class Ficha { - public static [[void]] main(String[] args) { - [[int]] idade = 14; - [[double]] altura = 1.62; - [[String]] nome = "Mia"; - [[boolean]] temAnimal = true; - System.out.[[println]](nome + " tem " + idade + " anos."); - } -} -\`\`\` -erklärung: Números inteiros são \`int\`, números decimais \`double\`, textos \`String\` (com S maiúsculo) e valores lógicos \`boolean\`. \`public class\` e \`public static void main\` formam a moldura. Na área Código podes deixá-la de fora, há mais sobre isto na lição “Estrutograma e código”. -::: - -## Encontra o erro: segundos - -::: übung l04-java-fehler -titel: Corrigir os segundos -code: java -auftrag: O programa deve converter 200 segundos em minutos e segundos e mostrar **3 min 20 s**. Mas mostra **20 min 3 s**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l04-java-fehler-start.visustruct -test: - -> 3 min 20 s -braucht: anweisung -tipp: \`/\` divide sem resto, \`%\` dá o resto. Qual dos dois precisas para os minutos e qual para os segundos que sobram? -tipp: Os dois operadores estão trocados: \`int minutos = segundos / 60;\` e \`int resto = segundos % 60;\` -lösung: l04-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l04-java-laeuft-nicht -titel: Corrigir a ficha -code: java -auftrag: O programa deve mostrar **Ludwig tem 7 anos.** Mas não corre, porque há duas palavras mal escritas. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l04-java-laeuft-nicht-start.visustruct -test: - -> Ludwig tem 7 anos. -braucht: anweisung -tipp: O Java distingue maiúsculas de minúsculas. Compara cada palavra da saída com o que foi criado em cima. -tipp: \`System\` escreve-se com S maiúsculo. A variável chama-se \`idade\` com i minúsculo: \`System.out.println(nome + " tem " + idade + " anos.");\` -lösung: l04-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que tipo de dados? - -::: spiel frage -titel: Jogo: Que tipo de dados? -intro: O Ludwig juntou valores. Que tipo de dados serve para cada valor? Oito rondas. -antworten: int | double | String | boolean - -runde: -frage: Que tipo de dados serve para \`17\`? -richtig: int -> Um número inteiro sem ponto decimal é um \`int\`. - -runde: -frage: Que tipo de dados serve para \`3.5\`? -richtig: double -> Números com ponto decimal são \`double\`. - -runde: -frage: Que tipo de dados serve para \`"Olá"\`? -richtig: String -> Texto entre aspas é uma \`String\`. - -runde: -frage: Que tipo de dados serve para \`true\`? -richtig: boolean -> \`true\` e \`false\` são os dois valores lógicos, tipo de dados \`boolean\`. - -runde: -frage: Que tipo de dados serve para \`"17"\`? -richtig: String -> Atenção às aspas! Aqui 17 é um texto com o qual não se pode calcular. - -runde: -frage: Que tipo de dados serve para \`-4\`? -richtig: int -> Os números inteiros negativos também são \`int\`. - -runde: -frage: Que tipo de dados serve para \`0.0\`? -richtig: double -> O ponto torna-o um número decimal, mesmo que a seguir só esteja um 0. - -runde: -frage: Que tipo de dados serve para \`"false"\`? -richtig: String -> Entre aspas, false também é só um texto. O valor lógico não tem aspas. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar uma variável com tipo de dados, nome e valor. → O que é uma variável? -- Consigo explicar que o Java distingue maiúsculas de minúsculas (\`numero\` não é \`Numero\`). → Maiúsculas e minúsculas -- Consigo distinguir os tipos de dados int, double, String e boolean. → Os quatro tipos de dados -- Consigo explicar que = é uma atribuição e não uma comparação. → Atribuição com = -- Consigo fazer contas com + - * / % e usar a divisão inteira. → Fazer contas -- Consigo criar e mostrar variáveis em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que tipo de dados serve para o valor 3.75? -→ Os quatro tipos de dados -- [ ] int -- [x] double -- [ ] String -- [ ] boolean -> Os números decimais são do tipo double. No VisuStruct escrevem-se com ponto. - -? Quantas variáveis criam \`int numero = 1\`, \`int Numero = 2\` e \`int NUMERO = 3\`? -→ Maiúsculas e minúsculas -- [ ] uma -- [ ] duas -- [x] três -> O Java distingue maiúsculas de minúsculas. Os três nomes são diferentes, por isso há três variáveis. - -? Que valor tem x depois de \`int x = 17 / 5\`? -→ Fazer contas -- [ ] 3.4 -- [x] 3 -- [ ] 4 -> Com números inteiros, tudo o que vem depois da vírgula desaparece. Não é arredondado. - -? Quanto dá \`17 % 5\`? -→ Fazer contas -- [x] 2 -- [ ] 3 -- [ ] 3.4 -> 17 a dividir por 5 dá 3, resto 2. O operador % dá o resto. - -? No início, \`int pontos = 10\`. O que fica em pontos depois de \`pontos = pontos * 2\`? -→ Atribuição com = -- [ ] 10 -- [ ] 12 -- [x] 20 -> O lado direito é calculado com o valor antigo 10, e o resultado 20 é guardado em pontos. - -? Que linha cria corretamente um número decimal em Java? -→ Agora em Java -- [ ] double preco = 2,5; -- [x] double preco = 2.5; -- [ ] Double preco = 2.5 -> Os números decimais escrevem-se com ponto, e no fim há um ponto e vírgula. -::: -`,ky=`--- -titel: Sequência -bereich: Fundamentos -kurz: Executar passos um a seguir ao outro, prestar atenção à ordem e trocar valores. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que uma sequência é executada de cima para baixo -- explicar porque é que a ordem dos passos conta -- trocar dois valores com uma variável auxiliar -- escrever uma sequência em Java -::: - -## Passo a passo - -Uma **sequência** é uma série de passos que são executados exatamente uma vez e exatamente por esta ordem. No estrutograma, os blocos ficam simplesmente uns por cima dos outros. - -Cada bloco só começa quando o bloco de cima terminou. Nada é saltado e nada é repetido. - -::: diagramm l05-sequenz.visustruct -Uma sequência de quatro blocos: primeiro \`comprimento\` e \`largura\` recebem os seus valores, depois a área é calculada e, no fim, é apresentada. Cada bloco é executado exatamente uma vez, de cima para baixo. Saída: 15. -::: - -> A sequência é o mais simples dos três blocos básicos. Quase todos os estrutogramas são feitos sobretudo de sequências. - -## A ordem conta - -Quando fazes chá, notas logo se a ordem estiver errada. Com contas é igual, só que é mais difícil de ver. - -::: diagramm l04-rechnen.visustruct -Primeiro duplicar, depois somar 1: o resultado é 7. Se trocares os dois blocos do meio, dá 8. -::: - -A instrução \`numero = numero * 2\` quer dizer: pega no valor de \`numero\`, multiplica-o por 2 e volta a guardar o resultado em \`numero\`. Por isso, o que está em \`numero\` depende do que aconteceu antes. - -::: angeberwissen -Os estrutogramas não são só coisa da escola. Segundo Shneiderman, empresas como a IBM e a Siemens também planeavam os seus programas com eles. Ou seja, aqui estás a aprender coisas de profissionais. Mééé-ravilha! -::: - -## Exercício: Pôr por ordem - -::: übung l04-reihenfolge -titel: Pôr por ordem -auftrag: O diagrama inicial tem os blocos certos, mas pela ordem errada. Move-os de modo que no fim seja mostrado **20**. -start: l04-reihenfolge-start.visustruct -test: - -> 20 -braucht: anweisung -tipp: Lê cada bloco e pergunta-te: de que variável precisa, e onde é que essa variável recebe o seu valor? -tipp: Uma variável tem de ser criada antes de fazeres contas com ela. A saída vem mesmo no fim. Confirma de cabeça: (2 + 3) · 4 = 20. -lösung: l04-reihenfolge-loesung.visustruct -::: - -## Trocar dois valores - -Imagina dois copos: um com sumo, outro com água. Se quiseres trocar o que está lá dentro, precisas de um terceiro copo, vazio. - -Com variáveis é exatamente igual. Se escreveres simplesmente \`a = b\` e depois \`b = a\`, o valor antigo de \`a\` já se perdeu. No fim, as duas variáveis têm o mesmo valor. - -A solução é uma **variável auxiliar** que guarda o valor antigo durante pouco tempo: - -::: grafik l05-tauschen.svg -Três passos com um copo vazio: primeiro o sumo para aux, depois a água para a esquerda, depois o sumo de aux para a direita. -::: - -::: diagramm l04-tauschen.visustruct -Primeiro guarda-se a em aux. Depois a recebe o valor de b, e b recebe o valor guardado em aux. -::: - -## Exercício: Trocar copos - -::: übung l04-glaeser-tauschen -titel: Trocar copos -auftrag: O copo da esquerda tem sumo, o da direita tem água. Troca o conteúdo das variáveis \`esquerda\` e \`direita\` antes de serem mostradas. -Não alteres os dois primeiros blocos. Insere novas instruções entre eles. -start: l04-tauschen-start.visustruct -test: - -> Esquerda: água | Direita: sumo -braucht: anweisung -tipp: Como trocas dois copos cheios na vida real? Precisas de um terceiro copo, vazio. -tipp: Cria uma variável auxiliar com \`String aux = esquerda\`. Depois disso precisas de mais duas atribuições. -lösung: l04-tauschen-loesung.visustruct -::: - -## Agora em Java - -Tal como na lição 4, também podes escrever uma sequência diretamente em Java. A troca de cima fica assim: - -\`\`\`java editor -int a = 5; -int b = 9; -int aux = a; -a = b; -b = aux; -System.out.println("a = " + a); -System.out.println("b = " + b); -\`\`\` - -- Cada bloco passa a ser uma linha. As linhas correm de cima para baixo, tal como os blocos. -- No fim de cada instrução há um **ponto e vírgula** \`;\`. -- Uma saída chama-se \`System.out.println(…)\` em Java. Atenção ao S maiúsculo de \`System\`: o Java distingue maiúsculas de minúsculas. - -Escreves Java na área [Código](#zeige-codelive), à direita do estrutograma. Enquanto escreves, o VisuStruct constrói o estrutograma ao mesmo tempo. Assim vês logo que bloco pertence a que linha. - -::: übung l05-java-kino -titel: Bilhetes de cinema em Java -bereich: beide -code: java -auftrag: Um bilhete de cinema custa 9 euros. Escreve um programa em Java que calcula o preço de 3 bilhetes e mostra **Total: 27**. - “Abrir no editor” abre também a área Código à direita. Escreve lá o teu código; o estrutograma constrói-se sozinho. Depois clica em Verificar. -test: - -> Total: 27 -braucht: anweisung -tipp: Precisas de três variáveis: o preço, a quantidade e o resultado. Cada linha termina com ponto e vírgula. -tipp: \`int preco = 9;\` e \`int quantidade = 3;\` criam as variáveis. \`int total = preco * quantidade;\` calcula, e \`System.out.println("Total: " + total);\` mostra o resultado. -lösung: l05-java-kino-loesung.visustruct -::: - -## Preenche os espaços: trocar os copos - -::: lückentext -titel: Trocar dois valores -auftrag: Os valores de \`a\` e \`b\` devem ser trocados. Preenche as palavras que faltam. -\`\`\`java -int a = 3; -int b = 8; -[[int]] aux = [[a]]; -a = [[b]]; -b = [[aux]]; -System.out.println("a = " + a + ", b = " + b); -\`\`\` -erklärung: Primeiro guardas \`a\` na variável auxiliar, depois \`a\` recebe o valor de \`b\` e, por fim, \`b\` recebe o valor guardado. Saída: **a = 8, b = 3** -::: - -## Encontra o erro: saldo - -::: übung l05-java-fehler -titel: Corrigir o saldo -code: java -auftrag: O cartão da cantina tem 20 euros. Uma refeição custa 7 euros. O programa deve mostrar o saldo **depois** de pagar, ou seja, **Resta: 13**. Mas mostra **Resta: 20**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l05-java-fehler-start.visustruct -test: - -> Resta: 13 -braucht: anweisung -tipp: Todas as linhas estão certas, só a ordem não. Quando é mostrado o saldo e quando se paga? -tipp: Troca as duas últimas linhas: primeiro \`saldo = saldo - preco;\`, depois a saída. -lösung: l05-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l05-java-laeuft-nicht -titel: Dividir uma piza -code: java -auftrag: Uma piza tem 8 fatias e 4 amigos dividem-na. O programa deve mostrar **Cada um recebe 2 fatias.** Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l05-java-laeuft-nicht-start.visustruct -test: - -> Cada um recebe 2 fatias. -braucht: anweisung -tipp: Compara o nome na saída, letra a letra, com o nome criado na terceira linha. -tipp: A variável chama-se \`porPessoa\` com P maiúsculo (camelCase). Escreve-a exatamente assim na saída. -lösung: l05-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Ordenar os blocos - -::: spiel reihenfolge -titel: Jogo: Ordenar os blocos -intro: O Ludwig baralhou os blocos. Põe-nos pela ordem certa. Quatro rondas. - -runde: -frage: No fim deve aparecer **7**. -- \`int numero = 3\` -- \`numero = numero * 2\` -- \`numero = numero + 1\` -- \`output: numero\` -> Primeiro duplicar, depois somar 1: 3 · 2 + 1 = 7. Ao contrário dava (3 + 1) · 2 = 8. - -runde: -frage: No fim deve aparecer **8**. -- \`int numero = 3\` -- \`numero = numero + 1\` -- \`numero = numero * 2\` -- \`output: numero\` -> Os mesmos blocos, outra ordem, outro resultado: (3 + 1) · 2 = 8. - -runde: - int a = 3 - int b = 5 -frage: a e b devem trocar os seus valores. -- \`int aux = a\` -- \`a = b\` -- \`b = aux\` -- \`output: a + " " + b\` -> Primeiro guarda-se a em aux. Senão o valor antigo perde-se assim que a recebe o valor de b. - -runde: -frage: O Ludwig faz chá. -- Encher a chaleira com água -- Ligar a chaleira -- Esperar até a água ferver -- Deitar a água sobre o saquinho de chá -- Deixar o chá em infusão -> Cada passo precisa do anterior. Tal como num estrutograma. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que uma sequência é executada de cima para baixo. → Passo a passo -- Consigo explicar porque é que a ordem dos passos conta. → A ordem conta -- Consigo trocar dois valores com uma variável auxiliar. → Trocar dois valores -- Consigo escrever uma sequência em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas vezes é executado cada bloco de uma sequência? -→ Passo a passo -- [ ] Até uma condição ser falsa -- [x] Exatamente uma vez -- [ ] Nenhuma, se estiver vazio -> Numa sequência nada é repetido e nada é saltado. Para repetir existem os ciclos. - -? No início a = 5 e b = 9. Depois é executado \`a = b\` e a seguir \`b = a\`. O que está nas variáveis no fim? -→ Trocar dois valores -- [ ] a = 9 e b = 5 -- [x] a = 9 e b = 9 -- [ ] a = 5 e b = 5 -> Depois de \`a = b\`, o valor antigo 5 perdeu-se. \`b = a\` só copia o 9 de volta. - -? Porque precisas de uma variável auxiliar para trocar valores? -→ Trocar dois valores -- [x] Para um valor não ser escrito por cima antes de ser preciso -- [ ] Para o estrutograma ficar mais comprido -- [ ] Porque senão não se podem mostrar variáveis - -? O que mostra a sequência \`int x = 4\`, \`x = x + 1\`, \`x = x * 3\`, \`output: x\`? -→ A ordem conta -- [ ] 13 -- [x] 15 -- [ ] 4 -> Primeiro x passa a 5, depois a 15. - -? Com que termina cada instrução em Java? -→ Agora em Java -- [ ] Com um ponto . -- [x] Com um ponto e vírgula ; -- [ ] Com dois pontos : -> Se faltar o ponto e vírgula, o VisuStruct mostra uma indicação por baixo do código. -::: -`,Sy=`--- -titel: Entrada e saída -bereich: Fundamentos -kurz: Pedir valores, mostrar resultados e usar o modelo EPS. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o modelo EPS -- escrever uma entrada com tipo de dados, nome e pergunta -- juntar texto e variáveis numa saída com + -- calcular primeiro um resultado e guardá-lo numa variável, depois mostrar essa variável, p. ex. \`int soma = a + b\`, depois \`output: soma\` -- identificar a entrada, o processamento e a saída numa tarefa -- ler números em Java com o scanner -- escrever comentários com \`//\` e \`/* */\` e usá-los para organizar e explicar o meu código -::: - -## O modelo EPS - -Muitos programas funcionam em três partes: - -1. **Entrada:** O programa pede valores, p. ex. dois números. -2. **Processamento:** Faz contas com os valores. -3. **Saída:** Mostra o resultado. - -As primeiras letras dão **EPS**. Até agora, os valores estavam fixos no estrutograma. Com uma entrada, pode ser usado um valor diferente de cada vez que o estrutograma é executado. - -::: diagramm l06-eva.visustruct -Entrada em cima, processamento ao meio, saída em baixo. Inicia a simulação e experimenta números diferentes. -::: - -## Entrada com input: - -Uma entrada pede um valor enquanto o estrutograma é executado e guarda-o numa variável. Escreves o **tipo de dados**, o **nome** e uma **pergunta** entre aspas: - -\`\`\` -input: int idade "Quantos anos tens?" -input: double preco "Preço em euros?" -input: String nome "Como te chamas?" -\`\`\` - -No editor escolhes o tipo **Entrada** em cima, numa Instrução. A palavra \`input:\` já fica à frente, e só escreves \`int idade "Quantos anos tens?"\`. - -> O tipo de dados decide o que pode ser introduzido. Com \`int\`, a simulação dá erro se alguém escrever 2.5 ou Olá. - -## Saída com output: - -Uma saída mostra texto e valores. O texto fica entre aspas, as variáveis não. Com \`+\` **juntas** as partes: - -\`\`\` -output: "Olá " + nome + "!" -output: "Tens " + idade + " anos." -\`\`\` - -Atenção aos espaços dentro das aspas. Sem eles fica tudo colado, e vês \`OláInês!\` em vez de \`Olá Inês!\`. - -## Exercício: Cumprimento - -::: übung l06-begruessung -titel: Cumprimento -auftrag: Pede um nome e cumprimenta a pessoa. Quando se escreve Ada, deve ser mostrado **Olá Ada!**. -test: Ada -> Olá Ada! -test: Rui -> Olá Rui! -braucht: anweisung -tipp: Primeiro vem uma entrada para o nome, depois uma saída que usa o nome. -tipp: O nome é texto, por isso a entrada precisa do tipo \`String\`. Na saída juntas \`"Olá "\`, a variável e \`"!"\` com \`+\`. -lösung: l06-begruessung-loesung.visustruct -::: - -## Cuidado com o + numa saída - -Numa saída, o \`+\` junta **sempre** as partes, mesmo com números. Se \`a\` for 3 e \`b\` for 4, então - -\`\`\` -output: a + b -\`\`\` - -não mostra 7, mas **34**. Por isso calcula primeiro numa variável e depois mostra a variável: - -\`\`\` -int soma = a + b -output: "Soma: " + soma -\`\`\` - -Isso também encaixa no modelo EPS: calculas na parte do processamento, não na saída. - -## Da tarefa ao estrutograma - -Antes de arrastares o primeiro bloco, lê a tarefa com atenção e desmonta-a. A isto chama-se **análise do problema**. Três perguntas ajudam-te: - -1. **O que é dado?** Esses valores tornam-se a **entrada**. Pensa também no tipo de dados. -2. **O que procuras?** Isso torna-se a **saída**. -3. **Como chegas do que é dado ao que procuras?** Isso é o **processamento**, muitas vezes uma fórmula. - -Um exemplo: *Um bilhete de cinema custa 9 euros. O programa pergunta quantos bilhetes alguém compra e mostra o preço total.* - -- Dado: o número de bilhetes → \`input: int quantidade "Quantos bilhetes?"\` -- Procurado: o preço total → \`output: "Preço total: " + total\` -- Caminho: quantidade vezes 9 → \`int total = quantidade * 9\` - -Agora só tens de pôr as três partes umas por cima das outras, pela ordem E, P, S. - -> Procura palavras-sinal na tarefa: *pergunta*, *lê* ou *introduz* apontam para uma entrada. *Mostra*, *escreve* ou *indica* apontam para uma saída. - -## Exercício: Calculadora - -::: übung l06-rechner -titel: Soma e produto -auftrag: Pede dois números inteiros. Depois mostra a soma e o produto deles, cada um na sua linha. -Com 3 e 4 fica assim: \`Soma: 7\` e \`Produto: 12\`. -test: 3, 4 -> Soma: 7 | Produto: 12 -test: 10, -2 -> Soma: 8 | Produto: -20 -test: 0, 5 -> Soma: 5 | Produto: 0 -braucht: anweisung -tipp: Planeia a ordem: primeiro ler os dois números, depois calcular, depois mostrar. -tipp: Precisas de duas entradas, duas instruções para calcular e duas saídas. Calcula a soma numa variável própria, senão o \`+\` junta os números. -lösung: l06-rechner-loesung.visustruct -::: - -## Exercício: Média - -::: übung l06-mittelwert -titel: Média -auftrag: Pede dois números, que também podem ser números decimais. Mostra a média deles, ou seja, para 2 e 3 é \`Média: 2.5\`. -test: 2, 3 -> Média: 2.5 -test: 4, 6 -> Média: 5.0 -test: 1.5, 2 -> Média: 1.75 -braucht: anweisung -tipp: A média de dois números obtém-se somando-os e dividindo o resultado ao meio. -tipp: Para números decimais usas o tipo \`double\`. A média é a soma a dividir por 2. Não te esqueças dos parênteses: \`(x + y) / 2\`. -lösung: l06-mittelwert-loesung.visustruct -::: - -## Agora em Java - -O exemplo EPS do início fica assim em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Primeiro número?"); -int a = scanner.nextInt(); -System.out.println("Segundo número?"); -int b = scanner.nextInt(); -int soma = a + b; -System.out.println("Soma: " + soma); -\`\`\` - -- Para entradas, o Java precisa de um **Scanner**. Escreves as duas primeiras linhas uma vez lá em cima e depois podes ler quantas vezes quiseres. -- \`scanner.nextInt()\` lê um número inteiro, \`scanner.nextDouble()\` um número decimal e \`scanner.next()\` uma palavra. -- A pergunta ao utilizador é uma saída normal logo antes. O VisuStruct junta as duas linhas num bloco de entrada. - -::: angeberwissen -O Java foi pensado para controlar pequenos aparelhos, e ainda o faz: em muitos cartões SIM de telemóvel e cartões bancários corre uma mini versão chamada **Java Card**. É bem possível que tenhas Java no bolso neste momento. -::: - -::: übung l06-java-tage -titel: Idade em dias -code: java -auftrag: Escreve um programa em Java que pergunta a idade em anos e mostra quantos dias são, mais ou menos. Conta 365 dias por ano. Para 2 anos aparece **São cerca de 730 dias.** -test: 2 -> São cerca de 730 dias. -test: 10 -> São cerca de 3650 dias. -test: 1 -> São cerca de 365 dias. -braucht: anweisung -tipp: Primeiro as duas linhas do scanner, depois a pergunta e a entrada, depois o cálculo, depois a saída. -tipp: \`int anos = scanner.nextInt();\` lê. \`int dias = anos * 365;\` calcula, e \`System.out.println("São cerca de " + dias + " dias.");\` mostra. -lösung: l06-java-tage-loesung.visustruct -::: - -## Comentários no código - -Podes escrever notas no teu código. Chamam-se **comentários** (em inglês *comments*). O Java salta-os ao executar, por isso o programa e o estrutograma ficam iguais. Há dois tipos: - -- \`//\` transforma o resto da linha num comentário. É um **comentário de uma linha**. -- \`/*\` começa um **comentário de várias linhas** e \`*/\` termina-o. Tudo o que está entre os dois faz parte do comentário, seja quantas linhas forem. O Java não precisa dos asteriscos no início das linhas do meio, só ficam arrumados. - -Em programas acabados às vezes também vês \`/** … */\` com dois asteriscos no início. É um **comentário Javadoc**, a partir do qual o Java consegue criar uma descrição do programa. Para ti, chegam \`//\` e \`/* */\`. - -O exemplo EPS com comentários fica assim: - -\`\`\`java editor -/* - * Tarefa: O programa pede dois números - * e mostra a sua soma. - */ -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -// Entrada -System.out.println("Primeiro número?"); -int a = scanner.nextInt(); -System.out.println("Segundo número?"); -int b = scanner.nextInt(); - -// Processamento -int soma = a + b; // somar os dois números - -// Saída -System.out.println("Soma: " + soma); // com 3 e 4 espero: Soma: 7 - -// Da próxima vez: permitir também números decimais -\`\`\` - -Para que servem os comentários: - -- **A tarefa:** o comentário de várias linhas lá em cima diz o que o programa deve fazer. -- **Organização:** \`// Entrada\`, \`// Processamento\` e \`// Saída\` dividem o código segundo o modelo EPS. -- **Explicação:** um comentário depois de uma linha diz o que o teu algoritmo faz ali. -- **Expectativa:** escreve que saída esperas. Ao testar, comparas. -- **Associar chavetas:** mais tarde vais ter blocos entre chavetas \`{ }\`. Aí, p. ex. \`} // fim do for\` mostra que chaveta fecha o quê. -- **Guardar ideias:** de que te tens de lembrar quando continuares da próxima vez? - -Clica em **Abrir no editor**, muda um comentário ou acrescenta um novo. O estrutograma fica igual e o VisuStruct guarda os teus comentários. - -> Não escrevas um comentário depois de cada linha. Explica o que o teu algoritmo faz e porquê, não cada carácter. - -::: angeberwissen -Os dois tipos são muito mais antigos do que o Java: \`/* … */\` vem da linguagem PL/I, de 1964, e \`//\` da BCPL, de 1967. O Java herdou-os através do C e do C++. -::: - -## Preenche os espaços: comentários - -::: lückentext -titel: Completar os comentários -auftrag: Completa os caracteres para que lá em cima fique um comentário de várias linhas com a tarefa e as outras duas notas sejam comentários de uma linha. -\`\`\`java -[[/*]] - * Tarefa: O programa converte euros em cêntimos. - [[*/]] -int euros = 3; -[[//]] Processamento -int centimos = euros * 100; -[[//]] Saída, esperado: 300 -System.out.println(centimos); -\`\`\` -erklärung: \`/*\` abre o comentário de várias linhas e \`*/\` fecha-o. \`//\` só vale até ao fim da linha, por isso cada nota precisa do seu próprio \`//\`. -::: - -## Preenche os espaços: entrada - -::: lückentext -titel: Ler um nome e uma idade -auftrag: Preenche as palavras que faltam para que o programa leia um nome e uma idade. -\`\`\`java -import java.util.[[Scanner]]; -Scanner scanner = [[new]] Scanner(System.[[in]]); - -System.out.println("Como te chamas?"); -[[String]] nome = scanner.[[next]](); -System.out.println("Que idade tens?"); -int idade = scanner.[[nextInt]](); -System.out.println("Olá " + nome + ", para o ano vais ter " + [[(idade + 1)]] + " anos."); -\`\`\` -erklärung: \`scanner.next()\` lê uma palavra, \`scanner.nextInt()\` um número inteiro. Os parênteses à volta de \`idade + 1\` são importantes: sem eles, o Java junta primeiro \`idade\` e depois \`1\` ao texto, e 14 passaria a **141**. -::: - -## Encontra o erro: soma - -::: übung l06-java-fehler -titel: Corrigir a soma -code: java -auftrag: O programa lê dois números e deve mostrar a soma. Mas com 3 e 4 aparece **Soma: 34** em vez de **Soma: 7**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l06-java-fehler-start.visustruct -test: 3, 4 -> Soma: 7 -test: 10, 5 -> Soma: 15 -test: 0, 9 -> Soma: 9 -braucht: anweisung -tipp: Lê a saída da esquerda para a direita: o que dá \`"Soma: " + a\`? E o que acontece quando ainda vem \`+ b\` a seguir? -tipp: Põe parênteses para o Java calcular primeiro: \`System.out.println("Soma: " + (a + b));\` -lösung: l06-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l06-java-laeuft-nicht -titel: Idade no próximo ano -code: java -auftrag: O programa pergunta a tua idade e deve dizer quantos anos terás no próximo ano. Com 14, **No próximo ano tens 15**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l06-java-laeuft-nicht-start.visustruct -test: 14 -> No próximo ano tens 15 -test: 9 -> No próximo ano tens 10 -braucht: anweisung -tipp: A entrada fica guardada numa variável. Como se chama exatamente, e como se chama na saída? -tipp: A variável chama-se \`idade\` com i minúsculo: \`System.out.println("No próximo ano tens " + (idade + 1));\` -lösung: l06-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: O que aparece? - -::: spiel frage -titel: Jogo: O que aparece? -intro: O Ludwig construiu entradas e saídas. Diz antes o que acontece. Sete rondas. - -runde: - int a = 3 - int b = 4 - output: a + b -frage: O que aparece? -- 7 -- [x] 34 -- a + b -> Numa saída, o \`+\` junta sempre as partes, mesmo números. - -runde: - int a = 3 - int b = 4 - int soma = a + b - output: "Soma: " + soma -frage: O que aparece? -- [x] Soma: 7 -- Soma: 34 -- Soma: soma -> O cálculo acontece antes, numa variável. A saída só junta o texto e o resultado. - -runde: - input: String nome "Como te chamas?" - output: "Olá" + nome + "!" -frage: Escreves Mia. O que aparece? -- Olá Mia! -- [x] OláMia! -- Olá nome! -> O texto \`"Olá"\` não tem espaço no fim. Por isso fica tudo colado. - -runde: - input: int idade "Quantos anos tens?" -frage: Escreves 2.5. O que acontece? -- idade fica 2. -- idade fica 2.5. -- [x] A simulação mostra um erro. -> O tipo de dados \`int\` só aceita números inteiros. - -runde: - input: double preco "Preço em euros?" -frage: A que parte do modelo EPS pertence este bloco? -- [x] Entrada -- Processamento -- Saída -> \`input:\` pede um valor. Isso é a entrada. - -runde: - double media = (a + b) / 2.0 -frage: A que parte do modelo EPS pertence este bloco? -- Entrada -- [x] Processamento -- Saída -> Aqui calcula-se alguma coisa. Isso é o processamento. - -runde: - output: "Média: " + media -frage: A que parte do modelo EPS pertence este bloco? -- Entrada -- Processamento -- [x] Saída -> \`output:\` mostra o resultado. Isso é a saída. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o modelo EPS. → O modelo EPS -- Consigo escrever uma entrada com tipo de dados, nome e pergunta. → Entrada com input: -- Consigo juntar texto e variáveis numa saída com +. → Cuidado com o + numa saída -- Consigo calcular primeiro um resultado e guardá-lo numa variável, depois mostrar essa variável, p. ex. \`int soma = a + b\`, depois \`output: soma\`. → Cuidado com o + numa saída -- Consigo identificar a entrada, o processamento e a saída numa tarefa. → Da tarefa ao estrutograma -- Consigo ler números em Java com o scanner. → Agora em Java -- Consigo escrever comentários com \`//\` e \`/* */\` e usá-los para organizar e explicar o meu código. → Comentários no código -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer EPS? -→ O modelo EPS -- [x] Entrada, processamento, saída -- [ ] Entrada, programa, sequência -- [ ] Editar, partilhar, sair -> Muitos programas pedem primeiro valores, processam-nos e depois mostram o resultado. - -? As variáveis a e b têm os valores 3 e 4. O que mostra \`output: a + b\`? -→ Cuidado com o + numa saída -- [ ] 7 -- [x] 34 -- [ ] a + b -> Numa saída, o + junta as partes. Calcula primeiro a soma numa variável. - -? Que entrada serve se for introduzido um preço como 4.99? -→ Entrada com input: -- [ ] input: int preco "Preço?" -- [x] input: double preco "Preço?" -- [ ] input: String "Preço?" -> 4.99 é um número decimal, portanto double. Além disso, cada entrada precisa de um nome de variável. - -? O que mostra \`output: "Idade: " + idade\` se idade tiver o valor 12? -→ Cuidado com o + numa saída -- [x] Idade: 12 -- [ ] "Idade: " + 12 -- [ ] Idade: idade - -? Tarefa: *O programa pede o raio de um círculo e mostra o seu perímetro.* Associa cada parte do texto ao seu lugar no modelo EPS. -→ Da tarefa ao estrutograma -= pede o raio -> Entrada -= perímetro = 2 * 3.14 * raio -> Processamento -= mostra o seu perímetro -> Saída -> O raio é dado, o perímetro é o que procuras. A fórmula liga os dois. - -? O que lê \`scanner.nextInt()\`? -→ Agora em Java -- [x] Um número inteiro -- [ ] Um número decimal -- [ ] Uma frase inteira -> Para números decimais há \`scanner.nextDouble()\`, para uma palavra \`scanner.next()\`. - -? O que faz o Java com um comentário? -→ Comentários no código -- [ ] Mostra-o como texto. -- [x] Salta-o. -- [ ] Indica um erro. -> Os comentários são notas para pessoas. O programa corre exatamente igual com ou sem eles. - -? Onde termina um comentário que começa com \`/*\`? -→ Comentários no código -- [x] Em \`*/\` -- [ ] No fim da linha -- [ ] Na próxima linha vazia -> Por isso um comentário com \`/*\` pode ocupar várias linhas. Já um comentário com \`//\` termina no fim da linha. -::: -`,Ty=`--- -titel: Simulação e teste de secretária -bereich: Fundamentos -kurz: Executar um estrutograma passo a passo, segui-lo à mão e encontrar erros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- iniciar a simulação, fazer pausa, executar passos individuais e pará-la -- ler as variáveis, a saída e o último passo durante a simulação -- fazer um teste de secretária à mão, em forma de tabela -- distinguir erros de sintaxe, erros de execução e erros de lógica -- encontrar um erro comparando os valores com o que espero -- acompanhar código Java com Passo a passo e pontos de paragem -::: - -## Iniciar a simulação - -Antes de chegarem as decisões e os ciclos, vais conhecer a tua ferramenta mais importante. A partir de agora podes usar a simulação para verificar sozinho cada exercício. - -Com o botão [▶︎ Iniciar simulação](#zeige-simulation) à direita nas ferramentas, o VisuStruct executa o teu estrutograma. Enquanto a simulação está a correr, não podes editar o diagrama. O mesmo botão chama-se então **■ Terminar simulação** e leva-te de volta à edição. - -À direita aparecem os **Controlos**: - -- **Reproduzir** executa o diagrama automaticamente, passo a passo. Quanto tempo espera entre dois passos defines em **Pausa por passo**. -- **Pausa** para por um momento. Depois continuas com Reproduzir ou Passo. -- **Passo** executa exatamente um passo. Assim podes observar com calma. -- **Parar** cancela e põe tudo de volta ao início. - -Abre o diagrama seguinte no editor e percorre-o com **Passo**: - -::: diagramm sim-tausch.visustruct -Os valores de a e b são trocados. A variável aux guarda um dos valores entretanto. -::: - -## O que a simulação mostra - -- **Destaque:** Um bloco do diagrama fica colorido. Com **Último passo** é o bloco que acabou de ser executado. Com **Próximo passo** é o bloco que vem a seguir. -- **Entrada:** Se o diagrama pedir um valor, escreves aqui e confirmas com **OK**. -- **Último passo:** Aqui vês o que acabou de acontecer, p. ex. \`a = 5 -> 5\` depois do bloco \`a = b\`. À esquerda, as variáveis já foram substituídas pelos seus valores; à direita da seta está o resultado. -- **Variáveis:** Todas as variáveis com os seus valores atuais. -- **Saída:** Tudo o que foi mostrado com \`output:\`. - -Se a área [Código](#zeige-codelive) estiver aberta à direita, a [consola](#zeige-konsole) também acompanha lá. É limpa no início e depois mostra as saídas e entradas tal como apareceriam no programa real. - -No fim aparece **Simulação terminada.** Se algo correr mal, a simulação para no bloco com erro e mostra uma mensagem, p. ex. **Variável desconhecida: total**. - -## O teste de secretária - -Num **teste de secretária** és tu o computador. Percorres o estrutograma com papel e caneta e anotas os valores das variáveis depois de cada passo. O melhor é fazer uma tabela: uma coluna para o passo, uma para cada variável e uma para a saída. - -::: grafik l07-schreibtischtest-pt.svg -Basta uma folha de papel e uma caneta: depois de cada passo escreves os novos valores numa nova linha. -::: - -Para o diagrama de cima, a tabela fica assim: - -| Passo | a | b | aux | Saída | -|---|---|---|---|---| -| \`int a = 3\` | 3 | – | – | | -| \`int b = 5\` | 3 | 5 | – | | -| \`int aux = a\` | 3 | 5 | 3 | | -| \`a = b\` | 5 | 5 | 3 | | -| \`b = aux\` | 5 | 3 | 3 | | -| \`output: "a = " + a\` | 5 | 3 | 3 | a = 5 | -| \`output: "b = " + b\` | 5 | 3 | 3 | b = 3 | - -Depois compara a tua tabela com a simulação. Os valores em **Variáveis** coincidem com a tua linha depois de cada passo? - -> Um teste de secretária é especialmente útil nos testes escritos. Aí não tens computador, mas mesmo assim podes mostrar o que o teu estrutograma faz. - -::: angeberwissen -Antigamente, “computador” era uma profissão: pessoas que faziam contas com lápis, papel e máquina de calcular. Uma das melhores foi a matemática Katherine Johnson, na NASA. Ela calculava trajetórias de cápsulas espaciais e, mais tarde, também do voo da Apollo 11 até à Lua. - -Em 1962, John Glenn ia ser o primeiro americano a dar a volta à Terra em órbita. A trajetória tinha sido calculada por um novo computador da IBM. Glenn não confiava totalmente na máquina e disse: “Chamem a rapariga para verificar os números. Se ela disser que estão certos, eu vou.” Katherine Johnson refez todas as contas à mão, um verdadeiro teste de secretária. Os números estavam certos, e Glenn voou. - -Katherine Johnson era negra e, nessa altura, no estado norte-americano da Virgínia, havia leis que separavam pessoas negras e brancas. No filme “Elementos Secretos” (2016), ela tem de atravessar o recinto todo a correr sempre que precisa de ir à casa de banho. O filme exagera: as casas de banho separadas existiam mesmo, mas Katherine Johnson contou que simplesmente ignorava a placa. - -A sua colega Dorothy Vaughan percebeu cedo que os computadores da IBM iam ficar com o trabalho de cálculo. Aprendeu sozinha a linguagem de programação FORTRAN e ensinou-a a toda a sua equipa. Foi assim que os computadores humanos passaram a ser programadoras. -::: - -::: bild katherine-johnson.jpg -Katherine Johnson (1918–2020), matemática na NASA (foto: [NASA](https://commons.wikimedia.org/wiki/File:Katherine_Johnson_1983.jpg), domínio público). -::: - -## Três tipos de erros - -Os erros fazem parte da programação, até para profissionais. Há três tipos, e cada um encontra-se de outra maneira: - -- **Erro de sintaxe** (syntax error): O texto não cumpre as regras da linguagem, p. ex. falta um \`;\` no fim. O programa nem sequer arranca. -- **Erro de execução** (runtime error): O programa arranca, mas para num certo ponto, p. ex. ao dividir por 0. -- **Erro de lógica** (logic error): O programa corre até ao fim, mas o resultado está errado, p. ex. porque faltam parênteses: \`a + b / 2\` em vez de \`(a + b) / 2\`. - -É assim que o VisuStruct te mostra os erros: - -- **Erro de sintaxe:** No estrutograma acontecem menos, porque os blocos dão a forma. Na área [Código](#zeige-codelive) o sítio fica sublinhado a vermelho, o número da linha fica vermelho e lês, por exemplo, **Linha 2: Ainda falta aqui: ;**. -- **Erro de execução:** A simulação para no bloco e indica, por exemplo, **Divisão por zero.** Na área Código o número da linha fica vermelho e a [consola](#zeige-konsole) diz a linha: **Linha 3: Divisão por zero.** -- **Erro de lógica:** Aqui ninguém avisa nada. O computador não sabe o que querias. Só tu conheces o resultado certo, por isso aqui ajuda o teste de secretária. - -> Um número de linha vermelho mostra um erro. O ponto vermelho redondo antes do número da linha é outra coisa: um ponto de paragem. Vais conhecê-lo mais abaixo em “Agora em Java”. - -## Encontrar erros - -Se um estrutograma mostrar algo errado, é um erro de lógica. Então faz assim: - -1. **Escreve o que esperas:** O que deve sair para uma determinada entrada? -2. **Executa passo a passo:** Inicia a simulação com **Passo** e observa as variáveis. -3. **Encontra o sítio:** Onde é que um valor fica pela primeira vez diferente do que esperavas? É exatamente aí que está o erro. - -Erros frequentes numa sequência são: - -- A **ordem** está errada: uma variável é usada antes de ter um valor. -- Um valor é **escrito por cima** antes de ser preciso. -- Faltam **parênteses**: \`a + b / 2\` não é o mesmo que \`(a + b) / 2\`. -- São divididos dois números inteiros: \`7 / 2\` dá \`3\`, porque com \`int\` tudo o que vem depois da vírgula desaparece. - -> Mais tarde chegam as decisões e os ciclos. Aí a simulação também te mostra que ramo é seguido e quantas vezes um ciclo é executado. - -::: angeberwissen -A 9 de setembro de 1947, a equipa de Grace Hopper encontrou uma traça verdadeira presa entre dois contactos do computador Harvard Mark II. Colaram-na no diário de bordo e escreveram: “Primeiro caso real de um bug encontrado.” Hoje a traça está num museu em Washington. - -Grace Hopper foi uma das primeiras programadoras do mundo. Em 1952 construiu um dos primeiros **compiladores**: um programa que traduz código para código máquina. Achava que devia ser possível dar ordens aos computadores em inglês quase normal. Daí nasceu a linguagem de programação COBOL, que muitos bancos ainda hoje usam. Na Marinha dos EUA chegou mesmo a almirante. -::: - -::: bild grace-hopper.jpg -Grace Hopper (1906–1992), informática e almirante (foto: [U.S. Navy](https://commons.wikimedia.org/wiki/File:Commodore_Grace_M._Hopper,_USN_(covered).jpg), domínio público). -::: - -## Exercício: Corrigir a troca - -::: übung sim-tausch -titel: Corrigir a troca -auftrag: O estrutograma deve ler dois números e mostrá-los trocados. Ou seja, com 3 e 5 deve aparecer primeiro **5** e depois **3**. Mas mostra 5 duas vezes. - Faz um teste de secretária ou usa **Passo** e corrige o erro. -start: sim-tausch-start.visustruct -test: 3, 5 -> 5 | 3 -test: 1, 2 -> 2 | 1 -test: 7, 7 -> 7 | 7 -braucht: anweisung -tipp: Percorre o diagrama com **Passo** e anota os valores de a e b depois de cada passo. Onde é que o 3 desaparece? -tipp: Olha para a depois do passo \`a = b\`. Para onde foi o valor antigo de a? Guarda-o primeiro numa variável auxiliar, como no exemplo de cima. -lösung: sim-tausch-loesung.visustruct -::: - -## Exercício: Corrigir os bilhetes de cinema - -::: übung sim-kino -titel: Corrigir os bilhetes de cinema -auftrag: Um bilhete de cinema custa 8.50 euros. O estrutograma deve pedir o número de bilhetes e mostrar o preço total, ou seja, para 2 bilhetes \`Total: 17.0\`. - Inicia a simulação. Ela para com uma mensagem de erro. Descobre porquê e corrige o estrutograma. -start: sim-kino-start.visustruct -test: 2 -> Total: 17.0 -test: 3 -> Total: 25.5 -test: 0 -> Total: 0.0 -braucht: anweisung -tipp: Lê com atenção a mensagem de erro da simulação. Que variável é que ela ainda não conhece? -tipp: A simulação executa os blocos de cima para baixo. O total já tem um valor quando chega a saída? Podes mover os blocos com o rato. -lösung: sim-kino-loesung.visustruct -::: - -> Se uma simulação nunca mais acabar, clica em **Parar**. Quando **Verificas** um exercício, o VisuStruct para sozinho depois de um número muito grande de passos. - -## Agora em Java - -Também podes executar código Java passo a passo. Na área [Código](#zeige-codelive), a ferramenta para isso chama-se **depurador** (debugger): - -- Clica num número de linha à esquerda. Aparece um ponto vermelho: é um **ponto de paragem**. [▶︎ Executar](#zeige-ausfuehren) para antes dessa linha. -- O botão [Passo a passo](#zeige-einzelschritt) executa exatamente uma linha, como **Passo** na simulação. -- Por baixo do código vês o valor atual de cada variável. É o teu teste de secretária, só que é o computador que escreve. - -::: übung l07-java-pizza -titel: Corrigir a encomenda de pizzas -code: java -auftrag: Uma pizza custa 8 euros, o queijo extra 2 euros. Para 3 pizzas com queijo extra, o programa deve mostrar **Total: 30**. Mas mostra 14. - Percorre o código Java com Passo a passo e observa \`total\`. Encontra o erro e corrige-o no código. -start: l07-java-pizza-start.visustruct -test: - -> Total: 30 -braucht: anweisung -tipp: Que valor está em \`total\` depois da quarta linha? Faz as contas de cabeça: o que é que o Java calcula primeiro? -tipp: Como na matemática, a multiplicação vem antes da soma: \`preco + queijo * quantidade\` é \`8 + 6\`. Põe parênteses: \`(preco + queijo) * quantidade\`. -lösung: l07-java-pizza-loesung.visustruct -::: - -## Preenche os espaços: teste de secretária - -::: lückentext -titel: O que está nas variáveis? -auftrag: Faz um teste de secretária de cabeça. Escreve depois de cada linha o valor que a variável tem a seguir e, na última linha, o que é mostrado. -\`\`\`java -int a = 5; -int b = 2; -a = a + b; // a vale agora [[7]] -b = a * 2; // b vale agora [[14]] -a = b - a; // a vale agora [[7]] -System.out.println(a + b); // saída: [[21]] -\`\`\` -erklärung: Cada linha calcula com os valores que estão nesse momento nas variáveis: a = 5 + 2 = 7, b = 7 · 2 = 14, a = 14 − 7 = 7, saída 7 + 14 = 21. Podes confirmar com Passo a passo na área Código. -::: - -## Encontra o erro: pontuação - -::: übung l07-java-fehler -titel: Corrigir a pontuação -code: java -auftrag: Num jogo, o Ludwig ganha primeiro 10 pontos, depois 5 pontos e depois perde 3 pontos. No fim deve aparecer **Pontos: 12**. Mas aparece **Pontos: 3**. - Percorre o código com Passo a passo e observa \`pontos\`. Corrige a linha errada e carrega em Verificar. -start: l07-java-fehler-start.visustruct -test: - -> Pontos: 12 -braucht: anweisung -tipp: Depois de que linha é que \`pontos\` passa de repente a valer 3? O que devia acontecer nessa linha? -tipp: O Ludwig perde 3 pontos, por isso tira-se 3 ao valor antigo: \`pontos = pontos - 3;\` -lösung: l07-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l07-java-laeuft-nicht -titel: Corrigir o bónus -code: java -auftrag: O Ludwig tem 10 pontos e recebe 5 pontos de bónus. No fim deve aparecer **Pontos: 15**. Mas o código não corre. Percorre-o também com o passo a passo: em que linha para? - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l07-java-laeuft-nicht-start.visustruct -test: - -> Pontos: 15 -braucht: anweisung -tipp: O Java executa o código de cima para baixo. A variável \`bonus\` já existe quando é usada no cálculo? -tipp: Cria \`bonus\` antes do cálculo: primeiro \`int bonus = 5;\`, depois \`pontos = pontos + bonus;\` -lösung: l07-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa tem **exatamente um erro**, e a bomba está a fazer tique-taque. Faz o teste de secretária de cabeça e toca na linha errada antes de o tempo acabar. Os programas estão em Java, mas cada linha é um bloco como no estrutograma. Seis rondas, 30 segundos cada. -zeit: 30 - -runde: - int a = 3; - int b = 5; - int aux = a; - a = b; - b = a; -frage: Deve trocar os valores de a e b. -fehler: 5 -besser: b = aux; -> a já tem o valor de b. O valor antigo de a está em aux. - -runde: - int a = 4; - int b = 6; - double media = a + b / 2; - System.out.println(media); -frage: Deve mostrar a média 5.0. -fehler: 3 -besser: double media = (a + b) / 2.0; -> Sem parênteses, a divisão vem primeiro: 4 + 6 / 2 = 7. - -runde: - int bilhetes = 3; - double preco = 8.5; - double total = bilhetes + preco; - System.out.println("Total: " + total); -frage: Deve mostrar o preço de 3 bilhetes de cinema a 8,50 € cada. -fehler: 3 -besser: double total = bilhetes * preco; -> Para o preço total multiplica-se, não se soma. - -runde: - int minutos = 135; - int horas = minutos / 60; - int resto = minutos / 60; - System.out.println(horas + " h " + resto + " min"); -frage: Deve mostrar 2 h 15 min. -fehler: 3 -besser: int resto = minutos % 60; -> \`%\` dá o resto de uma divisão. Com \`/\`, resto também ficava com 2. - -runde: - String nome = "Ada"; - System.out.println("Olá " + "nome" + "!"); -frage: Deve mostrar "Olá Ada!". -fehler: 2 -besser: System.out.println("Olá " + nome + "!"); -> Entre aspas, nome é só um texto. A variável não tem aspas. - -runde: - double preco = 100; - preco = preco * 0.1; - System.out.println(preco); -frage: Deve aumentar o preço em 10 %, ou seja, mostrar 110.0. -fehler: 2 -besser: preco = preco + preco * 0.1; -> Vezes 0.1 dá só os 10 %, ou seja, 10.0. Isso ainda tem de ser somado ao preço antigo. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo iniciar a simulação, fazer pausa, executar passos individuais e pará-la. → Iniciar a simulação -- Consigo ler as variáveis, a saída e o último passo durante a simulação. → O que a simulação mostra -- Consigo fazer um teste de secretária à mão, em forma de tabela. → O teste de secretária -- Consigo distinguir erros de sintaxe, erros de execução e erros de lógica. → Três tipos de erros -- Consigo encontrar um erro comparando os valores com o que espero. → Encontrar erros -- Consigo acompanhar código Java com Passo a passo e pontos de paragem. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que botão executa exatamente um passo? -→ Iniciar a simulação -- [ ] Reproduzir -- [x] Passo -- [ ] Parar -> Reproduzir continua automaticamente, Passo executa só um passo. - -? O que anotas depois de cada passo num teste de secretária? -→ O teste de secretária -- [ ] Só a saída no fim -- [x] Os valores atuais de todas as variáveis -- [ ] O código Java gerado -> Assim vês exatamente onde um valor é diferente do que esperavas. - -? Primeiro \`int a = 3\`, depois \`int b = 5\`, depois \`a = b\`. Que valores têm a e b a seguir? -→ O teste de secretária -- [ ] a = 5 e b = 3 -- [x] a = 5 e b = 5 -- [ ] a = 3 e b = 5 -> O valor antigo de a foi escrito por cima. É por isso que precisas de uma variável auxiliar para trocar valores. - -? O campo Último passo mostra \`preco = 4 * 2.5 -> 10.0\`. O que quer isso dizer? -→ O que a simulação mostra -- [ ] A variável preco tem agora o valor 4. -- [x] Foi calculado 4 * 2.5 e o resultado 10.0 foi guardado em preco. -- [ ] A simulação encontrou um erro. - -? A simulação indica **Variável desconhecida: soma**. Qual é uma razão provável? -→ Encontrar erros -- [x] soma é usada antes de lhe ter sido dado um valor. -- [ ] soma é demasiado grande. -- [ ] O estrutograma tem demasiados blocos. - -? O que faz um ponto vermelho antes de um número de linha na área Código? -→ Agora em Java -- [ ] Marca um erro. -- [x] O programa para antes dessa linha. -- [ ] A linha é saltada. -> É um ponto de paragem. Continuas com ▶︎ ou com Passo a passo. -? Associa cada erro ao seu tipo. -→ Três tipos de erros -= Falta o ponto e vírgula no fim da linha. -> Erro de sintaxe -= O programa divide por 0 e para. -> Erro de execução -= A média dá demasiado alta porque faltam parênteses. -> Erro de lógica - -? O programa corre até ao fim sem nenhuma mensagem, mas mostra 12 em vez de 21. Que tipo de erro é? -→ Três tipos de erros -- [ ] Erro de sintaxe -- [ ] Erro de execução -- [x] Erro de lógica -> Não há mensagem, mas o resultado está errado. Um erro destes só se encontra com o que esperas e com o teste de secretária. -::: -`,_y=`--- -titel: Decisão -bereich: Fundamentos -kurz: Usar uma condição para decidir qual de dois ramos é executado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que é executado exatamente um ramo de uma decisão -- usar os operadores de comparação == != < <= > >= -- distinguir == de = -- construir uma decisão com um ramo vazio -- escrever uma decisão com if e else em Java -::: - -## Se, então, senão - -Até agora, todos os blocos eram sempre executados. Mas muitas vezes um programa deve **decidir**: se uma condição se verificar, faz uma coisa, senão faz a outra. - -::: diagramm l07-alter-pruefen.visustruct -Consoante a idade introduzida, aparece uma mensagem ou a outra. Experimenta 17 e 18 na simulação. -::: - -É para isso que serve a **decisão**. A condição fica no triângulo em cima. Por baixo, o estrutograma divide-se em dois ramos: - -- **true** (esquerda): é executado se a condição for verdadeira -- **false** (direita): é executado se a condição for falsa - -É sempre executado **exatamente um** dos dois ramos. Depois, o programa continua por baixo da decisão. - -## Inserir uma decisão - -Arrasta a **Decisão** da área Elementos para o estrutograma. Clica no triângulo e escreve a condição no Inspetor de objetos, p. ex. \`idade >= 18\`. A palavra \`if\` já está à frente. - -Cada ramo começa com um marcador ø. Arrasta instruções para lá. Um ramo também pode ter vários blocos uns por cima dos outros. - -## Operadores de comparação - -Uma condição é normalmente uma **comparação**. O seu resultado é sempre \`true\` ou \`false\`. - -- \`==\` igual, p. ex. \`numero == 0\` -- \`!=\` diferente, p. ex. \`resposta != 42\` -- \`<\` menor, \`<=\` menor ou igual -- \`>\` maior, \`>=\` maior ou igual - -> Não confundas \`==\` com \`=\`. Um só \`=\` é uma atribuição e guarda um valor. Para comparar precisas de dois sinais de igual. - -Com \`%\` verificas se um número é divisível por outro: \`numero % 2 == 0\` é verdadeiro se \`numero\` for par. - -## Exercício: Par ou ímpar - -::: übung l07-gerade -titel: Par ou ímpar -auftrag: Pede um número inteiro. Mostra **par** se for par, senão **ímpar**. -test: 4 -> par -test: 7 -> ímpar -test: 0 -> par -braucht: verzweigung -tipp: Aqui há duas possibilidades, por isso precisas de uma decisão. Como reconheces um número par? -tipp: Um número é par se não sobrar resto quando o divides por 2. A condição é \`numero % 2 == 0\`. -lösung: l07-gerade-loesung.visustruct -::: - -## Exercício: O número maior - -::: übung l07-maximum -titel: O número maior -auftrag: Pede dois números inteiros e mostra só o maior. Se os dois forem iguais, mostra esse número. -test: 3, 8 -> 8 -test: 9, 2 -> 9 -test: 5, 5 -> 5 -braucht: verzweigung -tipp: Uma decisão compara os dois números. Cada ramo tem exatamente uma saída. -tipp: Compara com \`a > b\`. No ramo true mostras \`a\`, no ramo false \`b\`. -lösung: l07-maximum-loesung.visustruct -::: - -## Um ramo vazio - -Às vezes uma coisa só deve acontecer se a condição for verdadeira e, caso contrário, não acontece nada. Então o ramo false fica **vazio**. No estrutograma mostra o marcador **ø**. - -::: diagramm l07-rabatt.visustruct -A partir de 50 euros tens 5 euros de desconto. Caso contrário não acontece nada; o ramo false está vazio. A saída em baixo é executada nos dois casos. -::: - -::: angeberwissen -Uma única decisão esquecida deixou meia Internet em alvoroço em 2014: a falha de segurança **Heartbleed** no OpenSSL, um programa que muitos sites usam para a encriptação. Um computador podia dizer ao servidor: “Devolve-me a palavra *pão*, tem 500 letras.” O servidor não verificava se 500 correspondia ao comprimento real de 3 e devolvia simplesmente 500 caracteres da sua memória. Às vezes lá dentro estavam palavras-passe de outras pessoas. Só faltava um if: se o comprimento indicado for maior do que o real, então não devolver nada. Já agora, isto não tinha nada a ver com chavetas. Mééé, verifica as tuas entradas antes de acreditares nelas. -::: - -## Exercício: Valor absoluto - -::: übung l07-betrag -titel: Valor absoluto de um número -auftrag: Pede um número inteiro. Se for negativo, torna-o positivo. Depois mostra o número. Assim, -5 passa a 5, e 7 fica 7. -Usa uma decisão com um ramo vazio e só **uma** saída por baixo da decisão. -test: -5 -> 5 -test: 7 -> 7 -test: 0 -> 0 -test: -12 -> 12 -braucht: verzweigung -tipp: Só os números negativos têm de ser alterados. O que acontece a um número se o multiplicares por -1? -tipp: A condição é \`numero < 0\`. No ramo true trocas o sinal, p. ex. com \`numero = 0 - numero\`. O ramo false fica vazio. -lösung: l07-betrag-loesung.visustruct -::: - -## Agora em Java - -Esta é a verificação da idade do início desta lição em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Quantos anos tens?"); -int idade = scanner.nextInt(); -if (idade >= 18) { - System.out.println("És maior de idade."); -} else { - System.out.println("Ainda não és maior de idade."); -} -\`\`\` - -- A condição fica depois de \`if\`, entre **parênteses curvos** \`( )\`. -- O ramo true fica entre **chavetas** \`{ }\`, o ramo false vem depois de \`else\`, nas suas próprias chavetas. Depois de uma chaveta não há ponto e vírgula. -- Se o ramo false estiver vazio, deixas simplesmente de fora \`else { }\`. -- O teu programa precisa das duas primeiras linhas uma vez, assim que lê alguma coisa. Depois, \`scanner.nextInt()\` lê um número inteiro. A pergunta antes disso é uma saída normal. - -::: angeberwissen -O Java também tem uma mascote: o **Duke**, um bonequinho preto e branco com um grande nariz vermelho. Foi desenhado pela equipa do Java em 1992 e desde então acena em conferências e t-shirts. O Ludwig acha que um bode teria ficado melhor. -::: - -::: übung l08-java-bestanden -titel: Aprovado em Java -code: java -auftrag: Escreve um programa em Java que pergunta pela pontuação. A partir de 50 pontos mostra **aprovado**, senão **reprovado**. - Escreve o código na área Código e depois clica em Verificar. -test: 50 -> aprovado -test: 49 -> reprovado -test: 100 -> aprovado -test: 0 -> reprovado -braucht: verzweigung -tipp: Começa pelas duas linhas do scanner. Depois perguntas pelos pontos, lês o valor e comparas com \`if\`. -tipp: A condição é \`pontos >= 50\`. Num ramo fica \`System.out.println("aprovado");\` e depois de \`else\` vem \`System.out.println("reprovado");\`. -lösung: l08-java-bestanden-loesung.visustruct -::: - -## Preenche os espaços: par ou ímpar - -::: lückentext -titel: Par ou ímpar -auftrag: Preenche as palavras e os sinais que faltam. O programa deve mostrar se um número é par. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que número?"); -int numero = scanner.nextInt(); -[[if]] (numero [[%]] 2 [[==]] 0) [[{]] - System.out.println("par"); -[[}]] [[else]] { - System.out.println("ímpar"); -} -\`\`\` -erklärung: \`numero % 2\` é o resto da divisão por 2. Se for 0, o número é par. Para comparar precisas de \`==\`, um \`=\` sozinho é uma atribuição. Os dois ramos ficam entre chavetas. -::: - -::: angeberwissen -Se um ramo tiver só uma instrução, o Java até deixa tirar as chavetas. Nós nunca fazemos isso, e é por esta razão: - -\`\`\`java -if (idade >= 18) - System.out.println("És maior de idade."); - System.out.println("Podes ver o filme."); -\`\`\` - -Sem chavetas, só a primeira linha pertence ao if. A segunda é sempre executada, até para quem tem doze anos. A indentação engana, porque o Java não liga aos espaços. - -Foi exatamente um erro destes que estava escondido em iPhones e Macs em 2014, conhecido como **“goto fail”**. Na linguagem C, a linha \`goto fail;\` aparecia por engano duas vezes debaixo de um if sem chavetas. A segunda era sempre executada e saltava a verificação dos certificados de segurança. Numa rede Wi-Fi aberta, um atacante podia, por exemplo, fazer-se passar pelo teu banco. Mééé, as chavetas custam dois caracteres, uma falha de segurança custa muito mais. -::: - -## Encontra o erro: entrada - -::: übung l08-java-fehler -titel: Corrigir a entrada -code: java -auftrag: No jardim zoológico, as crianças com **menos** de 6 anos entram de graça, todos os outros pagam 4 euros. Mas o programa também deixa entrar de graça as crianças de seis anos. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l08-java-fehler-start.visustruct -test: 5 -> Entrada gratuita -test: 6 -> 4 euros -test: 10 -> 4 euros -test: 0 -> Entrada gratuita -braucht: verzweigung -tipp: Experimenta o programa com 6. Que operador de comparação ainda deixa passar o 6? -tipp: “Menos de 6” é \`idade < 6\`. \`<=\` significa “menor ou igual”. -lösung: l08-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l08-java-laeuft-nicht -titel: Filme para maiores de 16 -code: java -auftrag: O filme é para maiores de 16. O Ludwig tem 15, por isso o programa deve mostrar **Não pode entrar**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l08-java-laeuft-nicht-start.visustruct -test: - -> Não pode entrar -braucht: verzweigung -tipp: Olha com atenção para a condição entre parênteses depois de \`if\`. Esta variável existe? -tipp: A variável chama-se \`idade\` com i minúsculo: \`if (idade >= 16) {\` -lösung: l08-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que caminho? - -::: spiel frage -titel: Jogo: Que caminho? -intro: O Ludwig escreveu decisões em Java. Avalia a condição de cabeça e diz o que aparece. Sete rondas. - -runde: - int x = 5; - if (x > 5) { - System.out.println("grande"); - } else { - System.out.println("pequeno"); - } -frage: O que aparece? -- grande -- [x] pequeno -- nada -> 5 > 5 é falso, porque 5 não é maior do que 5. Portanto, o ramo falso. - -runde: - int x = 5; - if (x >= 5) { - System.out.println("grande"); - } else { - System.out.println("pequeno"); - } -frage: O que aparece? -- [x] grande -- pequeno -- grande e pequeno -> \`>=\` quer dizer "maior ou igual". 5 >= 5 é verdadeiro. - -runde: - int numero = 7; - if (numero % 2 == 0) { - System.out.println("par"); - } else { - System.out.println("ímpar"); - } -frage: O que aparece? -- par -- [x] ímpar -- 1 -> 7 % 2 dá resto 1, portanto não 0. O número é ímpar. - -runde: - int x = -4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: O que aparece? -- -4 -- [x] 4 -- nada -> -4 < 0 é verdadeiro, por isso o sinal é invertido. - -runde: - int x = 4; - if (x < 0) { - x = -x; - } - System.out.println(x); -frage: O que aparece? -- -4 -- [x] 4 -- nada -> 4 < 0 é falso. O ramo falso está vazio, o programa continua simplesmente por baixo. - -runde: - int nota = 3; - if (nota != 3) { - System.out.println("diferente"); - } else { - System.out.println("três"); - } -frage: O que aparece? -- diferente -- [x] três -- nada -> \`!=\` quer dizer "diferente de". 3 != 3 é falso. - -runde: - int a = 8; - int b = 12; - if (a > b) { - System.out.println(a); - } else { - System.out.println(b); - } -frage: O que aparece? -- 8 -- [x] 12 -- 20 -> O programa mostra o maior dos dois números. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que é executado exatamente um ramo de uma decisão. → Se, então, senão -- Consigo usar os operadores de comparação == != < <= > >=. → Operadores de comparação -- Consigo distinguir == de =. → Operadores de comparação -- Consigo construir uma decisão com um ramo vazio. → Um ramo vazio -- Consigo escrever uma decisão com if e else em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Que ramo é executado se a condição for falsa? -→ Se, então, senão -- [ ] O ramo true -- [x] O ramo false -- [ ] Os dois ramos -- [ ] Nenhum, o programa termina -> Com false, é executado o ramo da direita. Depois, o programa continua por baixo da decisão. - -? Que condição verifica se idade é exatamente 18? -→ Operadores de comparação -- [ ] idade = 18 -- [x] idade == 18 -- [ ] idade >= 18 -> Um só = é uma atribuição. Para comparar precisas de ==. - -? O que significa ø num ramo? -→ Um ramo vazio -- [x] Neste caso não acontece nada. -- [ ] O programa termina aqui. -- [ ] A condição é falsa. - -? A condição é \`pontos >= 50\`. Para que valor é verdadeira? -→ Operadores de comparação -- [ ] 49 -- [x] 50 -- [ ] 0 -> >= quer dizer maior ou igual. Portanto o 50 está incluído. - -? Nos manuais, a decisão tem muitas vezes outro nome. Qual? -→ Se, então, senão -- [ ] Seleção múltipla -- [x] Instrução condicional -- [ ] Iteração -> As instruções de um ramo só são executadas sob uma condição. Por isso também se chama instrução condicional. - -? Onde fica em Java a condição de uma decisão? -→ Agora em Java -- [x] Entre parênteses curvos logo a seguir a if -- [ ] Entre chavetas depois de else -- [ ] Depois de um ponto e vírgula -> \`if (idade >= 18) { … }\`: os parênteses curvos pertencem à condição, as chavetas ao ramo. -::: -`,Ay=`--- -titel: Combinar condições -bereich: Fundamentos -kurz: Juntar várias condições com e, ou e não, e verificar intervalos de valores. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar quando é que uma combinação com && é verdadeira -- explicar quando é que uma combinação com || é verdadeira -- inverter uma condição com ! -- verificar se um valor está num intervalo como de 1 a 5 -- ler a tabela de verdade de &&, || e ! -- guardar o resultado de uma condição numa variável boolean -- avaliar condições compostas com segurança -- ligar condições em Java com && e || -::: - -## E: && - -Às vezes uma só comparação não chega. Por exemplo, só tens direito a um bilhete jovem se tiveres **pelo menos 12 anos e menos de 18**. - -Com \`&&\` (e) juntas duas condições. O resultado só é \`true\` se **as duas** condições forem verdadeiras. - -::: diagramm l08-jugendticket.visustruct -As duas comparações têm de estar certas. Experimenta 11, 12, 17 e 18 na simulação. -::: - -## Ou: || - -Com \`||\` (ou), o resultado é \`true\` se **pelo menos uma** das condições for verdadeira. Também podem ser as duas. - -\`\`\` -dia == 6 || dia == 7 -\`\`\` - -Esta condição é verdadeira se \`dia\` tiver o valor 6 ou o valor 7, ou seja, ao sábado ou ao domingo. - -> Num teclado português, a barra vertical \`|\` escreve-se com Shift e a tecla à esquerda do 1. - -## Exercício: Fim de semana - -::: übung l08-wochenende -titel: Fim de semana -auftrag: Pede um dia da semana como número (1 é segunda-feira, 7 é domingo). Mostra **Fim de semana** se for sábado ou domingo, senão **Dia útil**. -test: 6 -> Fim de semana -test: 7 -> Fim de semana -test: 1 -> Dia útil -test: 5 -> Dia útil -braucht: verzweigung -tipp: É fim de semana se o dia for 6 **ou** 7. Para isso precisas de duas comparações numa só condição. -tipp: Sábado é 6, domingo é 7. Junta as duas comparações com \`||\`. -lösung: l08-wochenende-loesung.visustruct -::: - -## Verificar intervalos de valores - -No ensino básico em Portugal, as notas vão de 1 a 5 (5 é a melhor). Verificas se uma nota está **dentro do intervalo** com duas comparações e \`&&\`: - -\`\`\` -nota >= 1 && nota <= 5 -\`\`\` - -::: grafik l08-zahlenstrahl.svg -O intervalo verde de 1 a 5 é válido; as duas pontas estão incluídas. -::: - -Ao contrário, uma nota está **fora** do intervalo se for demasiado pequena **ou** demasiado grande: - -::: diagramm l08-note.visustruct -Não há notas menores do que 1 nem maiores do que 5. -::: - -> A forma como se escreve na matemática, \`1 <= nota <= 5\`, não funciona. Tens de a dividir em duas comparações e juntá-las com \`&&\`. - -## Exercício: Nota válida - -::: übung l08-note-gueltig -titel: Nota válida -auftrag: Pede uma nota como número inteiro. Mostra **válida** se estiver entre 1 e 5 (1 e 5 incluídos), senão **inválida**. -test: 1 -> válida -test: 5 -> válida -test: 3 -> válida -test: 0 -> inválida -test: 6 -> inválida -braucht: verzweigung -tipp: Uma nota válida tem de cumprir duas coisas ao mesmo tempo: não ser demasiado pequena **e** não ser demasiado grande. -tipp: A nota tem de ser maior ou igual a 1 e, ao mesmo tempo, menor ou igual a 5. -lösung: l08-gueltig-loesung.visustruct -::: - -## Não e parênteses - -Com \`!\` (não) inverte-se uma condição: \`true\` passa a \`false\` e vice-versa. - -\`\`\` -!(nota >= 1 && nota <= 5) -\`\`\` - -Isto é verdadeiro se a nota **não** estiver no intervalo, e quer dizer o mesmo que \`nota < 1 || nota > 5\`. - -Se \`&&\` e \`||\` aparecerem juntos, o \`&&\` é calculado primeiro, tal como a multiplicação antes da adição. É melhor usar **parênteses**; assim fica claro o que pertence ao quê: - -\`\`\` -(idade >= 12 && idade < 18) || estudante == 1 -\`\`\` - -## A tabela de verdade - -Uma **tabela de verdade** mostra de relance o que resulta de \`&&\`, \`||\` e \`!\`. À esquerda estão as quatro possibilidades para duas condições \`a\` e \`b\`, à direita o resultado: - -::: grafik l09-wahrheitstabelle.svg -Verde é true, vermelho é false. Com && só há uma linha verde, com || só uma vermelha. -::: - -Para fixar: - -- \`a && b\` só é verdadeiro se **ambas** forem verdadeiras. -- \`a || b\` só é falso se **ambas** forem falsas. -- \`!a\` inverte o valor. - -::: angeberwissen -O tipo de dados \`boolean\` tem o nome de **George Boole**. Este inglês mostrou já em 1847 que se pode calcular com verdadeiro e falso como com números, quase cem anos antes de existir o primeiro computador. Hoje a sua lógica está em todos os chips. -::: - -## Valores lógicos em variáveis - -O resultado de uma condição é um **valor lógico** do tipo \`boolean\`. Podes guardá-lo numa variável, tal como um número, e usá-lo mais tarde: - -\`\`\` -boolean fimDeSemana = dia == 6 || dia == 7 -\`\`\` - -Na decisão fica então só \`fimDeSemana\`. Lê-se quase como uma frase: "Se for fim de semana, então …". Não escrevas \`fimDeSemana == true\`, porque \`fimDeSemana\` já é true ou false. - -Com \`!fimDeSemana\` perguntas pelo contrário, ou seja, um dia útil. - -::: ludwig -Condições longas ficam mais claras se puseres partes delas em variáveis boolean com nomes claros, por exemplo \`boolean jovem = idade >= 12 && idade < 18\`. -::: - -## Jogo: Verdadeiro ou falso? - -Agora é a tua vez. Calcula cada condição de cabeça. Depois de cada resposta vês como o VisuStruct a avalia passo a passo. - -::: spiel -idade: 8..20 -dia: 1..7 -nota: 0..6 -estudante: boolean -fimDeSemana: boolean -? idade >= 18 -? dia == 6 -? nota != 3 -? idade >= 12 && idade < 18 -? dia == 6 || dia == 7 -? estudante && fimDeSemana -? !estudante -? nota >= 1 && nota <= 5 -? !(idade < 18) -? estudante || idade >= 18 -? (idade >= 12 && idade < 18) || estudante -? !(nota >= 1 && nota <= 5) -? !fimDeSemana && dia != 3 -? idade >= 12 && idade < 18 && !estudante -::: - -## Exercício: Ano bissexto - -::: übung l08-schaltjahr -titel: Ano bissexto -auftrag: Um ano é bissexto se for divisível por 4, mas não por 100. Os anos divisíveis por 400 são bissextos de qualquer forma. -Pede um ano e mostra **Ano bissexto** ou **Não é ano bissexto**. -test: 2024 -> Ano bissexto -test: 2023 -> Não é ano bissexto -test: 1900 -> Não é ano bissexto -test: 2000 -> Ano bissexto -braucht: verzweigung -tipp: Confirma primeiro a regra com exemplos: 2024 é bissexto, 1900 não é, 2000 é. “Divisível” quer dizer que o resto é 0. -tipp: Divisível por 4 é \`ano % 4 == 0\`, não divisível por 100 é \`ano % 100 != 0\`. Junta estas duas com \`&&\`, põe parênteses à volta e acrescenta o caso do 400 com \`||\`. -tipp: Quase a solução: uma única decisão com a condição \`(ano % 4 == 0 && ano % 100 != 0) || ano % 400 == 0\`. No ramo true fica \`output: "Ano bissexto"\`, no ramo false \`output: "Não é ano bissexto"\`. -lösung: l08-schaltjahr-loesung.visustruct -::: - -## Agora em Java - -O bilhete jovem do início fica assim em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Quantos anos tens?"); -int idade = scanner.nextInt(); -if (idade >= 12 && idade < 18) { - System.out.println("Bilhete jovem"); -} else { - System.out.println("Sem bilhete jovem"); -} -\`\`\` - -Os sinais \`&&\`, \`||\` e \`!\` são exatamente os que já usas no estrutograma. Vêm do Java. Como sempre, a condição inteira fica entre os parênteses curvos depois de \`if\`. - -::: übung l09-java-schule -titel: Escola ou tempo livre -code: java -auftrag: Escreve um programa em Java que pergunta a hora (0 a 23). Das 8 até às 15, inclusive, mostra **Escola**, senão **Tempo livre**. -test: 8 -> Escola -test: 15 -> Escola -test: 16 -> Tempo livre -test: 7 -> Tempo livre -braucht: verzweigung -tipp: A hora tem de cumprir duas condições ao mesmo tempo. Que operador quer dizer “e”? -tipp: \`if (hora >= 8 && hora <= 15) { … } else { … }\` -lösung: l09-java-schule-loesung.visustruct -::: - -## Preenche os espaços: condições - -::: lückentext -titel: Nota e fim de semana -auftrag: Preenche os operadores e as palavras que faltam. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que nota?"); -int nota = scanner.nextInt(); -if (nota >= 1 [[&&]] nota [[<=]] 5) { - System.out.println("nota válida"); -} else { - System.out.println("nota inválida"); -} -System.out.println("Que dia (1 a 7)?"); -int dia = scanner.nextInt(); -[[boolean]] fimDeSemana = dia == 6 [[||]] dia == 7; -if ([[!]]fimDeSemana) { - System.out.println("dia de escola"); -} -\`\`\` -erklärung: Uma nota é válida quando **as duas** condições são verdadeiras, por isso \`&&\`. É fim de semana quando **uma** das duas é verdadeira, por isso \`||\`. O resultado cabe numa variável \`boolean\`, e \`!\` inverte-o. -::: - -## Encontra o erro: sem escola - -::: übung l09-java-fehler -titel: Corrigir o dia sem escola -code: java -auftrag: O programa deve mostrar **sem escola** para o dia 6 (sábado) e o dia 7 (domingo), e **escola** nos outros dias. Mas mostra sempre **escola**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l09-java-fehler-start.visustruct -test: 6 -> sem escola -test: 7 -> sem escola -test: 3 -> escola -test: 1 -> escola -braucht: verzweigung -tipp: \`dia\` pode ser 6 **e** 7 ao mesmo tempo? -tipp: Basta que uma das duas condições seja verdadeira: \`dia == 6 || dia == 7\`. -lösung: l09-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l09-java-laeuft-nicht -titel: Corrigir o fim de semana -code: java -auftrag: O dia 6 é sábado, o dia 7 é domingo. Hoje é o dia 7, por isso deve aparecer **sem escola**. Mas o programa não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l09-java-laeuft-nicht-start.visustruct -test: - -> sem escola -braucht: verzweigung -tipp: A condição tem duas partes. Compara o nome da variável nas duas partes. -tipp: Também na segunda parte a variável chama-se \`dia\` com d minúsculo: \`if (dia == 6 || dia == 7) {\` -lösung: l09-java-laeuft-nicht-loesung.visustruct -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar quando é que uma combinação com && é verdadeira. → E: && -- Consigo explicar quando é que uma combinação com || é verdadeira. → Ou: || -- Consigo inverter uma condição com !. → Não e parênteses -- Consigo verificar se um valor está num intervalo como de 1 a 5. → Verificar intervalos de valores -- Consigo ler a tabela de verdade de &&, || e !. → A tabela de verdade -- Consigo guardar o resultado de uma condição numa variável boolean. → Valores lógicos em variáveis -- Consigo avaliar condições compostas com segurança (pelo menos 8 de 10 certas no jogo). → Jogo: Verdadeiro ou falso? -- Consigo ligar condições em Java com && e ||. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quando é que \`a && b\` é verdadeiro? -→ E: && -- [ ] Se a ou b for verdadeiro -- [x] Só se a e b forem ambos verdadeiros -- [ ] Se exatamente um deles for verdadeiro -> Com && as duas partes têm de estar certas. Se uma for falsa, o todo é falso. - -? A variável dia tem o valor 6. Quanto dá \`dia == 6 || dia == 7\`? -→ Ou: || -- [x] true -- [ ] false -> Com || basta que um dos lados seja verdadeiro. - -? Que condição verifica se numero está entre 10 e 20 (ambos incluídos)? -→ Verificar intervalos de valores -- [ ] 10 <= numero <= 20 -- [ ] numero >= 10 || numero <= 20 -- [x] numero >= 10 && numero <= 20 -> Com || todos os números seriam permitidos, porque qualquer número é maior do que 10 ou menor do que 20. - -? Quanto dá \`!(5 > 3)\`? -→ Não e parênteses -- [ ] true -- [x] false -> 5 > 3 é verdadeiro, e o ! inverte isso. - -? Como escreves em Java “idade é pelo menos 12 e menor do que 18”? -→ Agora em Java -- [ ] if (12 <= idade < 18) -- [x] if (idade >= 12 && idade < 18) -- [ ] if (idade >= 12 || idade < 18) -> Cada comparação fica sozinha, ligada com \`&&\`. -::: -`,jy=`--- -titel: Seleção -bereich: Fundamentos -kurz: Executar um de vários casos, consoante o valor de uma variável. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- inserir uma seleção, acrescentar casos e alterar os nomes dos casos -- explicar quando é executado o caso default -- decidir se serve melhor uma seleção ou uma decisão -- escrever uma seleção múltipla em Java com switch -::: - -## Muitas possibilidades, uma variável - -Uma máquina de bebidas tem três botões: 1 para água, 2 para sumo, 3 para chocolate quente. Com decisões precisas de três blocos encaixados uns nos outros. Isso fica depressa largo e confuso. - -::: diagramm l09-verschachtelt.visustruct -A máquina com três decisões encaixadas. Cada decisão volta a verificar a mesma variável escolha. -::: - -Sempre que comparas **uma variável** com **vários valores fixos**, há uma forma mais simples: a **seleção**. - -## Como se constrói uma seleção - -Em cima, no bloco, está a variável que decide. Por baixo há uma coluna própria para cada caso. Por cima de cada coluna está o valor a que ela pertence. - -::: diagramm l09-automat.visustruct -A mesma máquina como seleção. Se escolha tiver o valor 2, só é executada a coluna 2. -::: - -- A simulação procura o caso cujo nome corresponde exatamente ao valor. -- Se nenhum caso corresponder, é executado o **default**. Este caso fica sempre na ponta da direita. -- Depois, o programa continua por baixo da seleção. Só é executado **um** caso. - -> Uma seleção nova tem os casos 1, 2 e default. Clica num caso para alterar o seu **Nome do caso** no Inspetor de objetos. Com **Adicionar caso** ganhas uma nova coluna antes do default. - -## Casos com texto - -Os casos não têm de ser números. Também podes escolher pelo conteúdo de uma variável \`String\`. Então escreves o texto entre aspas como nome do caso, por exemplo \`"vermelho"\`. Tal como no código, isto mostra que é um texto. - -::: diagramm l09-ampel.visustruct -Um semáforo: consoante a cor introduzida, aparece uma mensagem diferente. Na simulação, experimenta também uma cor que não existe. -::: - -Maiúsculas e minúsculas contam: \`Vermelho\` não corresponde ao caso \`"vermelho"\`. - -## Exercício: Nota por extenso - -::: übung l09-note -titel: Nota por extenso -auftrag: Pede uma nota como número inteiro e mostra-a por extenso: 1 muito insuficiente, 2 insuficiente, 3 suficiente, 4 bom, 5 muito bom. - Para qualquer outro número deve aparecer **Nota inválida**. -test: 5 -> muito bom -test: 3 -> suficiente -test: 1 -> muito insuficiente -test: 9 -> Nota inválida -braucht: fallauswahl -tipp: Para muitos valores fixos, uma seleção é mais clara do que muitas decisões. Cada nota tem um caso próprio. -tipp: Usa “Adicionar caso” até teres os casos 1 a 5 e default. Em cima, na seleção, escreves \`nota\`. -lösung: l09-note-loesung.visustruct -::: - -## Exercício: Calculadora - -::: übung l09-rechner -titel: Calculadora -auftrag: Pede, um a seguir ao outro, um número inteiro \`a\`, um operador como \`String\` e um segundo número inteiro \`b\`. - Para \`+\`, \`-\` e \`*\` mostras o resultado. Para qualquer outro sinal aparece **Operador desconhecido**. -test: 3, +, 4 -> 7 -test: 10, -, 4 -> 6 -test: 6, *, 7 -> 42 -test: 1, x, 2 -> Operador desconhecido -braucht: fallauswahl -tipp: Primeiro lê os três valores. Depois, uma seleção sobre o operador decide o que é calculado. -tipp: Antes da seleção, cria \`int resultado = 0\`. Em cada caso calculas, por exemplo, \`resultado = a + b\` e depois mostras \`resultado\`. Os nomes dos casos são \`"+"\`, \`"-"\` e \`"*"\` entre aspas, porque o operador é uma \`String\`. -tipp: O esqueleto de cima para baixo: três entradas (\`int a\`, \`String operador\`, \`int b\`), depois \`int resultado = 0\`, depois uma seleção múltipla sobre \`operador\`. No caso \`"+"\` ficam \`resultado = a + b\` e \`output: resultado\`, em \`"-"\` e \`"*"\` igual. No caso senão fica \`output: "Operador desconhecido"\`. -lösung: l09-rechner-loesung.visustruct -::: - -## Seleção ou decisão? - -A seleção só verifica se um valor é **exatamente igual** a um nome de caso. Portanto: - -- **Seleção:** uma variável, vários valores fixos. Exemplos: escolher num menu, dia da semana como número, notas. -- **Decisão:** intervalos e condições combinadas. Exemplos: \`idade >= 18\`, \`temperatura < 0 || temperatura > 30\`. - -Por isso, para “idade 18 ou mais” continuas a precisar da decisão. Não podes criar um caso próprio para cada idade possível. - -## Agora em Java - -A máquina de bebidas fica assim em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("1 água, 2 sumo, 3 chocolate quente"); -int escolha = scanner.nextInt(); -switch (escolha) { - case 1 -> { - System.out.println("Aqui está a tua água."); - } - case 2 -> { - System.out.println("Aqui está o teu sumo."); - } - case 3 -> { - System.out.println("Aqui está o teu chocolate quente."); - } - default -> { - System.out.println("Este botão não existe."); - } -} -\`\`\` - -- Depois de \`switch\`, a variável que é comparada fica entre parênteses curvos. -- Cada caso começa com \`case\`, o valor e uma seta \`->\`. Depois, entre chavetas, fica o que deve acontecer. -- \`default\` é o caso “senão”. Corre quando nenhum valor serve. - -::: angeberwissen -A seta \`case 1 ->\` só existe em Java desde 2020 (Java 14). Antes escrevia-se \`case 1:\` e era preciso terminar cada caso com \`break;\`. Quem se esquecia, continuava simplesmente para o caso seguinte. A seta poupou muitos problemas a muitos programadores. -::: - -::: übung l10-java-wuerfel -titel: Dado por extenso -code: java -auftrag: Escreve um programa em Java com \`switch\` que lê um número. Para 1 mostra **um**, para 2 **dois**, para 3 **três** e senão **não conheço**. -test: 1 -> um -test: 2 -> dois -test: 3 -> três -test: 7 -> não conheço -braucht: fallauswahl -tipp: Precisas de três casos \`case\` e um caso \`default\`. -tipp: \`switch (numero) { case 1 -> { System.out.println("um"); } … default -> { System.out.println("não conheço"); } }\` -lösung: l10-java-wuerfel-loesung.visustruct -::: - -## Preenche os espaços: switch - -::: lückentext -titel: Meses por extenso -auftrag: Preenche as palavras e os sinais que faltam na seleção múltipla. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que mês (1 a 3)?"); -int mes = scanner.nextInt(); -[[switch]] (mes) { - [[case]] 1 -> { - System.out.println("janeiro"); - } - case 2 [[->]] { - System.out.println("fevereiro"); - } - [[case]] 3 -> { - System.out.println("março"); - } - [[default]] -> { - System.out.println("Não conheço"); - } -} -\`\`\` -erklärung: Depois de \`switch\` vem a variável entre parênteses. Cada caso começa com \`case\`, o valor e a seta \`->\`. \`default\` é executado quando nenhum caso serve. -::: - -## Encontra o erro: calculadora - -::: übung l10-java-fehler -titel: Corrigir a calculadora -code: java -auftrag: A calculadora lê dois números e uma opção: 1 mais, 2 menos, 3 vezes. Com 7, 3 e a opção 2 devia dar **4**, mas dá **-4**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l10-java-fehler-start.visustruct -test: 7, 3, 2 -> 4 -test: 7, 3, 1 -> 10 -test: 7, 3, 3 -> 21 -test: 7, 3, 5 -> Não existe -test: 10, 4, 2 -> 6 -braucht: fallauswahl -tipp: Mais e vezes estão certos. Olha com atenção para o \`case 2\`. -tipp: No menos, a ordem conta: \`a - b\` em vez de \`b - a\`. -lösung: l10-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l10-java-laeuft-nicht -titel: Corrigir a nota -code: java -auftrag: O Ludwig teve nota 2. O programa deve mostrar **bom**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l10-java-laeuft-nicht-start.visustruct -test: - -> bom -braucht: fallauswahl -tipp: Que variável verifica a seleção múltipla? Está escrita da mesma forma em cima? -tipp: Entre parênteses depois de \`switch\` tem de estar \`nota\` com n minúsculo: \`switch (nota) {\` -lösung: l10-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que caso? - -::: spiel frage -titel: Jogo: Que caso? -intro: O Ludwig carrega em botões da máquina de bebidas e de outras seleções. O que acontece? Seis rondas. - -runde: - int escolha = 2; - switch (escolha) { - case 1 -> { System.out.println("água"); } - case 2 -> { System.out.println("sumo"); } - case 3 -> { System.out.println("chocolate quente"); } - default -> { System.out.println("Esse botão não existe."); } - } -frage: O que aparece? -- água -- [x] sumo -- Esse botão não existe. -> escolha é 2, por isso corre exatamente o \`case 2\`. - -runde: - int escolha = 4; - switch (escolha) { - case 1 -> { System.out.println("água"); } - case 2 -> { System.out.println("sumo"); } - case 3 -> { System.out.println("chocolate quente"); } - default -> { System.out.println("Esse botão não existe."); } - } -frage: O que aparece? -- chocolate quente -- nada -- [x] Esse botão não existe. -> Nenhum caso serve para 4, por isso corre o \`default\`. - -runde: - String dia = "segunda"; - switch (dia) { - case "Segunda" -> { System.out.println("Matemática"); } - case "Terça" -> { System.out.println("Educação Física"); } - default -> { System.out.println("livre"); } - } -frage: O que aparece? -- Matemática -- Educação Física -- [x] livre -> Nos textos conta cada letra: "segunda" com s minúsculo não é "Segunda". - -runde: - int nota = 0; - switch (nota) { - case 1 -> { System.out.println("muito bom"); } - case 2 -> { System.out.println("bom"); } - case 3 -> { System.out.println("suficiente"); } - } -frage: O que aparece? -- muito bom -- [x] nada -- suficiente -> Nenhum caso serve e não há \`default\`. Então simplesmente não acontece nada. - -runde: -frage: Queres verificar se \`idade >= 18\`. O que serve melhor? -- [x] uma decisão -- uma seleção -> Uma seleção só compara com valores fixos. Para "maior ou igual" precisas de uma decisão. - -runde: -frage: Um dado mostra de 1 a 6, e para cada número deve aparecer uma palavra diferente. O que serve melhor? -- uma decisão -- [x] uma seleção -> Uma variável, seis valores fixos: é exatamente para isso que serve a seleção. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo inserir uma seleção, acrescentar casos e alterar os nomes dos casos. → Como se constrói uma seleção -- Consigo explicar quando é executado o caso default. → Como se constrói uma seleção -- Consigo decidir se serve melhor uma seleção ou uma decisão. → Seleção ou decisão? -- Consigo escrever uma seleção múltipla em Java com switch. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? A variável dia tem o valor 5. Existem os casos 1, 2, 3 e default. O que é executado? -→ Como se constrói uma seleção -- [ ] Nada -- [ ] O caso 3 -- [x] O caso default -> Nenhum nome de caso corresponde a 5, por isso aplica-se o default. - -? Quantos casos são executados de cada vez que a seleção é executada? -→ Como se constrói uma seleção -- [x] Exatamente um -- [ ] Todos os casos a partir do que corresponde -- [ ] Todos os casos -> Depois do caso que corresponde, o programa continua logo por baixo da seleção. - -? Para que é que uma seleção NÃO serve? -→ Seleção ou decisão? -- [ ] Escolher a opção 1, 2 ou 3 de um menu -- [x] Verificar se uma temperatura está acima dos 30 graus -- [ ] Tratar uma cor como vermelho, amarelo ou verde -> “Acima dos 30” é um intervalo. Para isso precisas de uma decisão com uma comparação. - -? No semáforo escreves \`Vermelho\`, e o caso chama-se \`"vermelho"\`. O que acontece? -→ Como se constrói uma seleção -- [ ] É executado o caso \`"vermelho"\`. -- [x] É executado o caso default. -- [ ] A simulação indica um erro. -> Maiúsculas e minúsculas contam. Vermelho e vermelho são textos diferentes. - -? Com que nome também encontras a seleção nos manuais? -→ Como se constrói uma seleção -- [ ] Instrução condicional -- [ ] Sequência -- [x] Seleção múltipla -> É escolhido um de muitos casos. Por isso também se chama seleção múltipla. - -? Como se chama em Java o caso “senão” de um switch? -→ Agora em Java -- [ ] else -- [x] default -- [ ] case 0 -::: -`,Cy=`--- -titel: Ciclo while -bereich: Fundamentos -kurz: Repetir passos enquanto uma condição for verdadeira. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que o ciclo while verifica a sua condição antes de cada iteração -- usar uma variável contadora com valor inicial, condição e alteração -- notar quando um ciclo nunca acaba e corrigir o erro -- seguir um ciclo iteração a iteração num teste de secretária -- escrever um ciclo while em Java -::: - -## Repetir enquanto algo se verificar - -Muitas vezes um programa deve fazer uma coisa várias vezes. Em vez de escreveres o mesmo bloco cinco vezes, usas um **ciclo**. - -O **ciclo while** repete o seu interior **enquanto** a sua condição for verdadeira. A condição fica em cima, na cabeça do bloco. O interior fica recuado por baixo, enquadrado por um parêntese à esquerda. - -::: diagramm l10-countdown.visustruct -Uma contagem decrescente: enquanto numero for maior do que 0, numero é mostrado e diminui 1. Depois aparece Partida! -::: - -## Primeiro a condição - -O ciclo while é um **ciclo pré-teste**. Antes de **cada** iteração, a condição é verificada: - -1. Verificar a condição. -2. Se for verdadeira, executar o interior uma vez e recomeçar no 1. -3. Se for falsa, o programa continua por baixo do ciclo. - -::: grafik l10-kopfgesteuert.svg -Depois de cada iteração volta-se à condição. Só quando ela é falsa é que o programa continua por baixo do ciclo. -::: - -Na contagem decrescente, a condição é verificada quatro vezes: com 3, 2 e 1 é verdadeira, com 0 é falsa. - -> Se a condição já for falsa da primeira vez que é verificada, o interior **não é executado nem uma vez**. Começa a contagem com \`int numero = 0\` e só aparece Partida! - -## Uma variável contadora - -Muitos ciclos vão contando. Para isso precisas de três coisas: - -- **Um valor inicial antes do ciclo**, p. ex. \`int i = 1\` -- **Uma condição na cabeça**, p. ex. \`i <= 5\` -- **Uma alteração no interior**, p. ex. \`i = i + 1\` (abreviado: \`i++\`) - -Se faltar uma destas partes, o ciclo conta mal ou nem sequer conta. - -::: angeberwissen -Os ciclos de contagem são antiquíssimos! No artigo de 1973, Nassi e Shneiderman mostraram a sua ideia, entre outras coisas, com o **fatorial**: 5! = 1 · 2 · 3 · 4 · 5 = 120. Aqui está a minha versão caprina. Experimenta-a com **Passo** na simulação e vê o \`fatorial\` a crescer. Mééé, isto vai depressa! -::: - -::: diagramm l10-fakultaet.visustruct -O fatorial com uma variável contadora \`i\`, reconstruído a partir de um exemplo do artigo de 1973. -::: - -## Exercício: Contar até n - -::: übung l10-bis-n -titel: Contar até n -auftrag: Pede um número inteiro \`n\` e mostra todos os números de 1 a \`n\`, cada um na sua linha. -test: 3 -> 1 | 2 | 3 -test: 5 -> 1 | 2 | 3 | 4 | 5 -test: 1 -> 1 -braucht: whileSchleife -tipp: Precisas de uma variável contadora que começa em 1 e aumenta 1 em cada iteração. -tipp: Antes do ciclo, cria \`int i = 1\`. A condição é \`i <= n\`. No interior mostras \`i\` e depois aumentas \`i\` em 1. -lösung: l10-bis-n-loesung.visustruct -::: - -## Atenção: ciclo infinito - -Se nada no interior do ciclo alterar a condição, ela fica verdadeira para sempre. O ciclo nunca para. A isto chama-se **ciclo infinito**. - -::: diagramm l10-endlos.visustruct -Aqui falta numero = numero - 1. O ciclo mostra 3 uma e outra vez e nunca chega a Partida! -::: - -Pergunta-te em cada ciclo: **O que é que no interior garante que a condição acaba por ficar falsa?** Quando um exercício é verificado, o VisuStruct para depois de um número muito grande de passos e indica um possível ciclo infinito. - -## Exercício: Duplicar até 100 - -::: übung l10-verdoppeln -titel: Duplicar até 100 -auftrag: Pede um número inteiro. Duplica-o até ele ser pelo menos 100. - No fim, mostra **quantas vezes** o duplicaste. -test: 10 -> 4 -test: 30 -> 2 -test: 1 -> 7 -test: 100 -> 0 -braucht: whileSchleife -tipp: O ciclo é executado enquanto o número ainda for demasiado pequeno. Vai contando quantas vezes foi executado. -tipp: Precisas de uma segunda variável \`int passos = 0\`. A condição é \`numero < 100\`. No interior duplicas o número com \`numero = numero * 2\` e aumentas \`passos\` em 1. Se o número já for 100, o ciclo nem chega a ser executado. -lösung: l10-verdoppeln-loesung.visustruct -::: - -## Ciclos num teste de secretária - -Com um ciclo, o **teste de secretária** é especialmente valioso. Vês exatamente quantas vezes o ciclo é executado e com que valor para. Cada verificação da condição tem uma linha própria. - -::: diagramm l16-schreibtisch.visustruct -Um ciclo while soma os números 1, 2 e 3. -::: - -| Passo | soma | i | Saída | -|---|---|---|---| -| \`int soma = 0\` | 0 | – | | -| \`int i = 1\` | 0 | 1 | | -| \`i <= 3\` → true | 0 | 1 | | -| \`soma = soma + i\` | 1 | 1 | | -| \`i = i + 1\` | 1 | 2 | | -| \`i <= 3\` → true | 1 | 2 | | -| \`soma = soma + i\` | 3 | 2 | | -| \`i = i + 1\` | 3 | 3 | | -| \`i <= 3\` → true | 3 | 3 | | -| \`soma = soma + i\` | 6 | 3 | | -| \`i = i + 1\` | 6 | 4 | | -| \`i <= 3\` → false | 6 | 4 | | -| \`output: soma\` | 6 | 4 | 6 | - -Erros frequentes em ciclos são: - -- O ciclo é executado **uma vez a mais ou uma vez a menos**, p. ex. \`<\` em vez de \`<=\`. -- Uma variável **não é alterada** no ciclo, ou é alterada no sentido errado. Então o ciclo nunca acaba. -- Uma variável recebe o seu valor inicial **dentro** do ciclo em vez de antes dele e é reposta em cada iteração. - -## Exercício: Corrigir a contagem decrescente - -::: übung l16-countdown -titel: Corrigir a contagem decrescente -auftrag: A contagem decrescente deve contar para trás a partir do valor inicial e mostrar **Já!** no fim. Ou seja, com 3: 3, 2, 1, Já! - O diagrama inicial tem dois erros. Encontra e corrige os dois. -start: l16-countdown-start.visustruct -test: 3 -> 3 | 2 | 1 | Já! -test: 1 -> 1 | Já! -test: 0 -> Já! -tipp: Há dois erros. Um tem a ver com a contagem, o outro com a saída no fim. -tipp: Inicia a simulação com o valor inicial 3 e observa i em Variáveis. O i diminui? Compara também com atenção a última saída com a tarefa. -lösung: l16-countdown-loesung.visustruct -::: - -## Agora em Java - -A contagem decrescente do início desta lição fica assim em Java: - -\`\`\`java editor -int numero = 3; -while (numero > 0) { - System.out.println(numero); - numero = numero - 1; -} -System.out.println("Partida!"); -\`\`\` - -- Depois de \`while\`, a condição fica entre parênteses curvos, como em \`if\`. -- Tudo o que se repete fica entre chavetas. É o corpo do ciclo. -- O corpo está recuado. Assim vês, como no estrutograma, o que pertence ao ciclo. - -::: übung l11-java-gerade -titel: Números pares em Java -code: java -auftrag: Escreve um programa em Java que lê um número inteiro \`n\` e, com um ciclo while, mostra todos os números pares de 2 até no máximo \`n\`. Para 7, são 2, 4 e 6. -test: 7 -> 2 | 4 | 6 -test: 8 -> 2 | 4 | 6 | 8 -test: 2 -> 2 -braucht: whileSchleife -tipp: Precisas de uma variável contadora que começa em 2. O ciclo corre enquanto ela não for maior do que \`n\`. -tipp: \`int numero = 2;\` antes do ciclo, depois \`while (numero <= n) { … }\`. No corpo mostras \`numero\` e aumentas com \`numero = numero + 2;\`. -lösung: l11-java-gerade-loesung.visustruct -::: - -## Preenche os espaços: while - -::: lückentext -titel: Soma de 1 a 10 -auftrag: O programa deve somar os números de 1 a 10. Preenche as palavras e os sinais que faltam. -\`\`\`java -int soma = 0; -int numero = 1; -[[while]] (numero [[<=]] 10) [[{]] - soma = soma + [[numero]]; - [[numero]] = numero + 1; -} -System.out.println("Soma: " + soma); -\`\`\` -erklärung: O ciclo repete-se enquanto \`numero\` for no máximo 10. Em cada volta, \`numero\` é somado à soma e depois \`numero\` aumenta 1. Sem essa linha seria um ciclo infinito. Saída: **Soma: 55** -::: - -## Encontra o erro: poupar - -::: übung l11-java-fehler -titel: Corrigir o plano de poupança -code: java -auftrag: O Ludwig poupa 5 euros por semana. O programa pergunta o objetivo e deve mostrar ao fim de quantas semanas ele o atinge. Para 50 euros são 10 semanas, mas o programa diz **Semanas: 11**. - Percorre o código com Passo a passo e observa \`dinheiro\` e \`semanas\`. Corrige o erro e carrega em Verificar. -start: l11-java-fehler-start.visustruct -test: 50 -> Semanas: 10 -test: 12 -> Semanas: 3 -test: 5 -> Semanas: 1 -test: 1 -> Semanas: 1 -braucht: whileSchleife -tipp: Quanto dinheiro tem o Ludwig quando o ciclo se repete pela última vez? Precisa mesmo de poupar mais uma semana? -tipp: Só se poupa enquanto o objetivo **ainda não foi atingido**: \`while (dinheiro < objetivo)\`. -lösung: l11-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l11-java-laeuft-nicht -titel: Corrigir a contagem -code: java -auftrag: A contagem decrescente deve mostrar **3**, **2**, **1** e depois **Partida!** Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l11-java-laeuft-nicht-start.visustruct -test: - -> 3 | 2 | 1 | Partida! -braucht: whileSchleife -tipp: O erro está dentro do ciclo. Que variável deve ser mostrada e como se chama de facto? -tipp: Mostra \`numero\` com n minúsculo: \`System.out.println(numero);\` -lösung: l11-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Quantas vezes? - -::: spiel frage -titel: Jogo: Quantas vezes? -intro: O Ludwig escreveu ciclos while. Faz o teste de secretária de cabeça. Seis rondas. - -runde: - int numero = 3; - while (numero > 0) { - System.out.println(numero); - numero = numero - 1; - } - System.out.println("Já!"); -frage: O que aparece? -- [x] 3\\n2\\n1\\nJá! -- 3\\n2\\n1\\n0\\nJá! -- Já! -> Quando numero é 0, \`numero > 0\` é falso, por isso o 0 já não aparece. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: O que aparece? -- [x] nada -- 10 -- de 10 a 4 -> A condição é verificada primeiro. 10 < 5 é falso, o corpo não corre nem uma vez. - -runde: - int i = 1; - while (i <= 3) { - System.out.println(i); - } -frage: O que acontece? -- Aparecem 1, 2 e 3. -- [x] O ciclo nunca para. -- Não aparece nada. -> i nunca aumenta no corpo. A condição fica sempre verdadeira: um ciclo infinito. - -runde: - int numero = 1; - while (numero < 100) { - numero = numero * 2; - } - System.out.println(numero); -frage: O que aparece? -- 64 -- 100 -- [x] 128 -> 1, 2, 4, 8, 16, 32, 64, 128. Só com 128 é que \`numero < 100\` é falso. - -runde: - int i = 0; - while (i < 5) { - i = i + 2; - } - System.out.println(i); -frage: O que aparece? -- 4 -- 5 -- [x] 6 -> i passa por 0, 2, 4, 6. Com 6, \`i < 5\` é falso. O 5 é saltado. - -runde: - int i = 1; - while (i <= 10) { - i = i + 3; - } -frage: Quantas vezes corre o corpo? -- 3 vezes -- [x] 4 vezes -- 10 vezes -> O corpo corre com i = 1, 4, 7 e 10. Depois i fica 13. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que o ciclo while verifica a sua condição antes de cada iteração. → Primeiro a condição -- Consigo usar uma variável contadora com valor inicial, condição e alteração. → Uma variável contadora -- Consigo notar quando um ciclo nunca acaba e corrigir o erro. → Atenção: ciclo infinito -- Consigo seguir um ciclo iteração a iteração num teste de secretária. → Ciclos num teste de secretária -- Consigo escrever um ciclo while em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer ciclo pré-teste? -→ Primeiro a condição -- [x] A condição é verificada antes de cada iteração. -- [ ] A condição é verificada depois de cada iteração. -- [ ] O ciclo é sempre executado exatamente uma vez. -> A cabeça com a condição está em cima e é avaliada primeiro. - -? \`int i = 5\`, depois um ciclo while com a condição \`i < 3\`. Quantas vezes é executado o interior? -→ Primeiro a condição -- [x] Nenhuma -- [ ] Uma vez -- [ ] Três vezes -> Logo da primeira vez que é verificado, 5 < 3 é falso. - -? Porque é que um ciclo com a condição \`numero > 0\` nunca acaba se o interior só tiver \`output: numero\`? -→ Atenção: ciclo infinito -- [ ] Porque a saída aumenta o número -- [x] Porque numero nunca muda e a condição fica sempre verdadeira -- [ ] Porque a condição está mal escrita -> No interior tem de acontecer algo que acabe por tornar a condição falsa. - -? Que outros nomes têm os ciclos nos manuais? -→ Primeiro a condição -- [x] Iteração ou repetição -- [ ] Decisões -- [ ] Subprogramas -> Um ciclo repete instruções. Por isso também se chama iteração ou estrutura de repetição. - -? Um ciclo deve contar de 1 a 5, mas só vai até 4. Qual é o erro provável? -→ Uma variável contadora -- [x] A condição diz i < 5 em vez de i <= 5. -- [ ] O ciclo é um ciclo while. -- [ ] A variável i tem o nome errado. - -? Que linha começa corretamente um ciclo while em Java? -→ Agora em Java -- [ ] while numero > 0 { -- [x] while (numero > 0) { -- [ ] while (numero > 0); -> A condição fica entre parênteses curvos, depois \`{\` abre o corpo. -::: -`,Dy=`--- -titel: Ciclo do-while -bereich: Fundamentos -kurz: Primeiro executar, depois verificar: ciclos com pelo menos uma iteração. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que o ciclo do-while só verifica a sua condição depois do interior -- explicar porque é que o interior é executado pelo menos uma vez -- repetir uma entrada até ela ser válida -- escrever um ciclo do-while em Java -::: - -## Primeiro fazer, depois verificar - -O **ciclo do-while** é um **ciclo pós-teste**. A condição fica em baixo, no pé do bloco. É executado assim: - -1. Executar o interior uma vez. -2. Verificar a condição. -3. Se for verdadeira, recomeçar no 1. Se for falsa, o programa continua por baixo do ciclo. - -::: grafik l11-vergleich.svg -À esquerda, o ciclo while verifica primeiro; à direita, o ciclo do-while executa primeiro. -::: - -Em cima, no bloco, está \`do\`, e a condição fica no pé. Quando clicas no ciclo, a palavra \`while\` está à frente do campo de texto no Inspetor de objetos: a condição quer dizer “repete enquanto …”. - -> Tal como no ciclo while, a condição diz **quando é que o ciclo se repete**. Enquanto for verdadeira, o ciclo continua. - -::: angeberwissen -O Python não tem ciclo do-while. Quem lá quer “primeiro fazer, depois verificar” escreve \`while True:\` e sai do ciclo com \`break\`. Java, C e JavaScript, pelo contrário, têm \`do … while\` há décadas. -::: - -## Pelo menos uma iteração - -Como a condição só é verificada no fim, o interior **é sempre executado pelo menos uma vez**. Essa é a diferença mais importante em relação ao ciclo while. - -::: diagramm l11-einmal.visustruct -i vale 10 e, portanto, não é menor do que 5. Mesmo assim, 10 é mostrado uma vez, porque a verificação só vem depois. -::: - -Com um ciclo while e a mesma condição \`i < 5\`, aqui não seria mostrado nada. - -## Uso típico: repetir uma entrada - -O ciclo do-while serve sempre que alguma coisa tem de acontecer primeiro para a poderes verificar. O melhor exemplo é uma entrada: só depois de alguém ter escrito alguma coisa é que sabes se é válida. - -::: diagramm l11-eingabe-pruefen.visustruct -A pergunta repete-se até ser introduzido um número de 1 a 10. Na simulação, experimenta primeiro 0 e depois 12. -::: - -Aqui a condição descreve a entrada **inválida**: \`numero < 1 || numero > 10\`. Enquanto a entrada for inválida, a pergunta é feita outra vez. - -## Exercício: Nota válida - -::: übung l11-note -titel: Nota válida -auftrag: Pede uma nota. Se não estiver entre 1 e 5, pergunta outra vez. Se for válida, mostra-a, por exemplo **A tua nota: 3**. -test: 3 -> A tua nota: 3 -test: 0, 6, 2 -> A tua nota: 2 -test: 9, 5 -> A tua nota: 5 -test: -1, 1 -> A tua nota: 1 -braucht: doUntilSchleife -tipp: A nota é pedida pelo menos uma vez. Depois, a condição decide se se pergunta outra vez. -tipp: Antes do ciclo, cria \`int nota = 0\`. A entrada \`input: nota "Nota de 1 a 5"\` fica dentro do ciclo. A condição é \`nota < 1 || nota > 5\`. -lösung: l11-note-loesung.visustruct -::: - -## Exercício: Somar até 0 - -::: übung l11-summe -titel: Somar até 0 -auftrag: Pede números até ser introduzido um 0. Depois mostra a soma de todos os números introduzidos. -test: 3, 4, 0 -> 7 -test: 10, 0 -> 10 -test: 5, 5, 5, 0 -> 15 -test: 0 -> 0 -braucht: doUntilSchleife -tipp: Precisas de uma variável para a soma que começa em 0 antes do ciclo. Em cada iteração, o novo número é somado. -tipp: Antes do ciclo, cria \`int soma = 0\` e \`int numero = 0\`. No ciclo pedes \`numero\` e calculas \`soma = soma + numero\`. Somar o 0 não faz mal. O ciclo continua enquanto \`numero != 0\`. -tipp: O esqueleto: \`int soma = 0\` e \`int numero = 0\`, depois um ciclo do-while com a condição \`numero != 0\`. Dentro: a entrada de \`numero\` e \`soma = soma + numero\`. Por baixo do ciclo: \`output: soma\`. -lösung: l11-summe-loesung.visustruct -::: - -## Agora em Java - -O ciclo que corre mesmo assim uma vez fica assim em Java: - -\`\`\`java editor -int i = 10; -do { - System.out.println(i); - i = i + 1; -} while (i < 5); -\`\`\` - -- O corpo fica entre \`do {\` e \`}\`, a condição só vem depois. -- Aqui, depois de \`while (…)\`, há um **ponto e vírgula**. É diferente do ciclo while. - -::: übung l12-java-verdoppeln -titel: Duplicar pelo menos uma vez -code: java -auftrag: Escreve um programa em Java que lê um número inicial. Um ciclo do-while duplica-o e mostra-o enquanto for menor do que 100. Um número a partir de 100 também é duplicado pelo menos uma vez: 200 passa a 400. -test: 30 -> 60 | 120 -test: 200 -> 400 -test: 13 -> 26 | 52 | 104 -braucht: doUntilSchleife -tipp: No corpo duplicas o número e mostras. A condição fica no fim. -tipp: \`do { numero = numero * 2; System.out.println(numero); } while (numero < 100);\` -lösung: l12-java-verdoppeln-loesung.visustruct -::: - -## Preenche os espaços: do-while - -::: lückentext -titel: Triplicar -auftrag: O número é triplicado e mostrado enquanto for menor do que 100. Preenche as palavras e os sinais que faltam. -\`\`\`java -int numero = 1; -[[do]] { - numero = numero * 3; - System.out.println(numero); -} [[while]] (numero [[<]] 100)[[;]] -\`\`\` -erklärung: O corpo fica entre \`do {\` e \`}\`, a condição só vem depois. No ciclo do-while há um ponto e vírgula depois de \`while (…)\`. Saída: 3, 9, 27, 81 e 243. -::: - -## Encontra o erro: dividir ao meio - -::: übung l12-java-fehler -titel: Corrigir a divisão ao meio -code: java -auftrag: O programa lê um número inicial e, com um ciclo do-while, divide-o repetidamente por 2 (divisão inteira). Cada resultado é mostrado até chegar a **0**. Para 10 são 5, 2, 1 e 0. Mas falta o 0. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l12-java-fehler-start.visustruct -test: 10 -> 5 | 2 | 1 | 0 -test: 1 -> 0 -test: 7 -> 3 | 1 | 0 -test: 20 -> 10 | 5 | 2 | 1 | 0 -braucht: doUntilSchleife -tipp: Que valor tem \`numero\` depois de o 1 ser mostrado? O ciclo ainda se repete mais uma vez? -tipp: O ciclo deve continuar enquanto o número for maior do que 0: \`while (numero > 0);\` -lösung: l12-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l12-java-laeuft-nicht -titel: Corrigir a duplicação -code: java -auftrag: A partir de 1, o programa deve duplicar enquanto o número for menor do que 20: **1**, **2**, **4**, **8**, **16**. Só mostra o 1 e depois para. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l12-java-laeuft-nicht-start.visustruct -test: - -> 1 | 2 | 4 | 8 | 16 -braucht: doUntilSchleife -tipp: A primeira volta ainda corre. O erro aparece quando a condição em baixo é verificada pela primeira vez. -tipp: Na condição tem de estar \`limite\` com l minúsculo: \`} while (numero < limite);\` -lösung: l12-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Pelo menos uma vez? - -::: spiel frage -titel: Jogo: Pelo menos uma vez? -intro: While ou do-while? O Ludwig mostra-te ciclos. Diz antes o que acontece. Seis rondas. - -runde: - int i = 10; - do { - System.out.println(i); - i = i + 1; - } while (i < 5); -frage: O que aparece? -- nada -- [x] 10 -- de 10 a 4 -> Primeiro fazer, depois verificar: o corpo corre uma vez, depois 11 < 5 é falso. - -runde: - int i = 10; - while (i < 5) { - System.out.println(i); - i = i + 1; - } -frage: O que aparece? -- [x] nada -- 10 -- 11 -> No ciclo while a condição vem primeiro. 10 < 5 é falso, por isso não corre nada. - -runde: - int numero = 200; - do { - numero = numero * 2; - } while (numero < 100); - System.out.println(numero); -frage: O que aparece? -- 200 -- [x] 400 -- 800 -> Duplica-se uma vez de certeza. Depois 400 < 100 é falso. - -runde: - int numero = 30; - do { - numero = numero * 2; - System.out.println(numero); - } while (numero < 100); -frage: O que aparece? -- 60 -- [x] 60\\n120 -- 60\\n120\\n240 -> Depois de 60, 60 < 100 é verdadeiro, continua. Depois de 120 acaba. - -runde: -frage: Que linha termina corretamente um ciclo do-while em Java? -- [x] \`} while (i < 5);\` -- \`} while (i < 5)\` -- \`while (i < 5) {\` -> Depois do \`while (…)\` do ciclo do-while há um ponto e vírgula. - -runde: -frage: Para que se usa tipicamente um ciclo do-while? -- [x] repetir uma entrada até ser válida -- contar de 1 a 10 -- trocar dois valores -> A pergunta é feita pelo menos uma vez. Só depois se sabe se a entrada era válida. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que o ciclo do-while só verifica a sua condição depois do interior. → Primeiro fazer, depois verificar -- Consigo explicar porque é que o interior é executado pelo menos uma vez. → Pelo menos uma iteração -- Consigo repetir uma entrada até ela ser válida. → Uso típico: repetir uma entrada -- Consigo escrever um ciclo do-while em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que quer dizer ciclo pós-teste? -→ Primeiro fazer, depois verificar -- [ ] A condição é verificada antes de cada iteração. -- [x] A condição é verificada depois de cada iteração. -- [ ] O ciclo não tem condição. -> A condição fica no pé, em baixo, e é avaliada depois do interior. - -? A condição de um ciclo do-while é falsa desde o início. Quantas vezes é executado o interior? -→ Pelo menos uma iteração -- [ ] Nenhuma -- [x] Exatamente uma vez -- [ ] Para sempre -> O interior é executado primeiro. Só depois o ciclo nota que deve parar. - -? Queres pedir um número de 1 a 10. Que condição fica no pé? -→ Uso típico: repetir uma entrada -- [ ] \`numero >= 1 && numero <= 10\` -- [x] \`numero < 1 || numero > 10\` -- [ ] \`numero == 10\` -> O ciclo repete-se enquanto a entrada for inválida, ou seja, menor do que 1 ou maior do que 10. - -? Quando é que um ciclo do-while é melhor do que um ciclo while? -→ Pelo menos uma iteração -- [x] Quando o interior tem de ser executado pelo menos uma vez, p. ex. numa entrada -- [ ] Quando o ciclo talvez nem precise de ser executado -- [ ] Quando queres contar exatamente até 10 - -? O que fica no fim de um ciclo do-while em Java? -→ Agora em Java -- [ ] } while (condicao) { -- [x] } while (condicao); -- [ ] } until (condicao); -> Depois da condição vem um ponto e vírgula. -::: -`,By=`--- -titel: Ciclo for -bereich: Fundamentos -kurz: Contar um número fixo de iterações com Início, Condição e Passo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- nomear as três partes Início, Condição e Passo do ciclo for -- explicar por que ordem são executadas as três partes -- contar para a frente, para trás e em passos maiores com um ciclo for -- escrever um ciclo for em Java e explicar os seus três campos -::: - -## Contar com um plano fixo - -No ciclo while já foste contando: um valor inicial antes do ciclo, uma condição na cabeça, uma alteração no interior. O **ciclo for** junta estas três partes na cabeça. Assim vês num relance como ele conta. - -::: grafik l12-while-zu-for.svg animiert -O ciclo while torna-se um ciclo for: o início, a condição e o passo juntam-se na cabeça. -::: - -Quando clicas num ciclo for, ele tem três campos à direita: - -- **Início:** \`int i = 1\` cria a variável contadora. -- **Condição:** \`i <= 5\` diz até quando se conta. -- **Passo:** \`i++\` aumenta \`i\` em 1 depois de cada iteração. - -::: diagramm l12-eins-bis-fuenf.visustruct -Um ciclo for que mostra os números de 1 a 5. -::: - -::: angeberwissen -Porque é que a variável de contagem se chama quase sempre \`i\`? Na matemática, \`i\` representa há muito tempo um índice. E em FORTRAN, uma das primeiras linguagens de programação, de 1957, todas as variáveis começadas por I a N eram automaticamente números inteiros. Os programadores ainda não perderam o hábito. -::: - -## Por que ordem? - -As três partes são executadas em momentos diferentes: - -1. O **Início** é executado exatamente uma vez, logo no começo. -2. A **Condição** é verificada antes de cada iteração. Se for falsa, o ciclo termina. -3. Depois do interior vem o **Passo**, depois outra vez a condição. - -::: grafik l12-for-ablauf.svg -O Início só é executado uma vez. Depois, a condição, o interior e o passo repetem-se. -::: - -Portanto, o ciclo for é um ciclo pré-teste, como o ciclo while. Percorre o exemplo na simulação com **Passo** e observa como \`i\` muda. - -> Usa o ciclo for quando sabes de antemão quantas vezes uma coisa deve acontecer. Se o fim depender de algo que só acontece durante o ciclo, por exemplo uma entrada, é melhor o ciclo while ou do-while. - -## Exemplo: soma de 1 a n - -Um ciclo for é bom para somar números. Antes do ciclo está \`int soma = 0\`. Em cada iteração soma-se o número atual \`i\`. - -::: diagramm l12-summe.visustruct -Com n = 4, o ciclo calcula 0 + 1 + 2 + 3 + 4. No fim é mostrado 10. -::: - -## Outros passos - -O passo nem sempre tem de ser \`i++\`: - -- \`i--\` conta para trás, por exemplo com Início \`int i = 10\` e Condição \`i >= 1\`. -- \`i = i + 2\` salta de dois em dois, por exemplo 0, 2, 4, 6. - -Garante que o Início, a Condição e o Passo combinam. Com Início \`int i = 1\`, Condição \`i >= 1\` e Passo \`i++\`, a condição nunca fica falsa. - -::: übung l12-countdown -titel: Contagem decrescente -auftrag: Pede um número inteiro \`n\`. Usa um ciclo for para contar para trás de \`n\` até 1 e mostra cada número. Depois mostra **Já!**. -test: 3 -> 3 | 2 | 1 | Já! -test: 5 -> 5 | 4 | 3 | 2 | 1 | Já! -test: 1 -> 1 | Já! -braucht: forSchleife -tipp: Desta vez o ciclo for conta para trás. Pensa onde começa, quando para e como conta. -tipp: O Início é \`int i = n\`, a condição \`i >= 1\` e o passo \`i--\`. A saída Já! fica por baixo do ciclo, não dentro dele. -lösung: l12-countdown-loesung.visustruct -::: - -## Exercício: Tabuada - -::: übung l12-einmaleins -titel: Tabuada -auftrag: Pede um número e mostra a sua tabuada de 1 a 10. Cada linha fica assim: **3 x 1 = 3**, **3 x 2 = 6** e assim por diante. -test: 3 -> 3 x 1 = 3 | 3 x 2 = 6 | 3 x 3 = 9 | 3 x 4 = 12 | 3 x 5 = 15 | 3 x 6 = 18 | 3 x 7 = 21 | 3 x 8 = 24 | 3 x 9 = 27 | 3 x 10 = 30 -test: 7 -> 7 x 1 = 7 | 7 x 2 = 14 | 7 x 3 = 21 | 7 x 4 = 28 | 7 x 5 = 35 | 7 x 6 = 42 | 7 x 7 = 49 | 7 x 8 = 56 | 7 x 9 = 63 | 7 x 10 = 70 -braucht: forSchleife -tipp: O ciclo conta de 1 a 10. Em cada iteração mostras uma linha da tabuada. -tipp: No interior, calcula primeiro \`int produto = numero * i\`. Depois mostra \`numero + " x " + i + " = " + produto\`. Não te esqueças dos espaços dentro das aspas. -lösung: l12-einmaleins-loesung.visustruct -::: - -## Exercício: Corrigir a soma - -::: übung l16-summe -titel: Corrigir a soma -auftrag: O estrutograma deve somar todos os números de 1 a n. Ou seja, com n = 4 o resultado deve ser 1 + 2 + 3 + 4 = **10**. Mas mostra 6. - Faz um teste de secretária ou usa **Passo** e corrige o erro. -start: l16-summe-start.visustruct -test: 4 -> 10 -test: 1 -> 1 -test: 5 -> 15 -braucht: forSchleife -tipp: Percorre o ciclo com n = 4 de cabeça ou com **Passo**. Que números são realmente somados? -tipp: Olha para o último valor de i com que o ciclo é executado. Com n = 4, também tem de ser somado i = 4. -lösung: l16-summe-loesung.visustruct -::: - -## Agora em Java - -A soma de 1 até n fica assim em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Até que número?"); -int n = scanner.nextInt(); -int soma = 0; -for (int i = 1; i <= n; i++) { - soma = soma + i; -} -System.out.println(soma); -\`\`\` - -Nos parênteses curvos depois de \`for\` estão os três campos do ciclo for, separados por ponto e vírgula: - -| Campo | Em Java | Significado | -|---|---|---| -| Início | \`int i = 1\` | \`i\` começa em 1. | -| Condição | \`i <= n\` | O ciclo corre enquanto isto for verdade. | -| Passo | \`i++\` | Depois de cada iteração, \`i\` aumenta 1. | - -\`i++\` é a forma curta de \`i = i + 1\`. Para contar para trás usas \`i--\`. - -::: übung l13-java-quadrate -titel: Quadrados em Java -code: java -auftrag: Escreve um programa em Java que lê um número inteiro \`n\` e, com um ciclo for, mostra os quadrados de 1 até \`n\`, cada um na sua linha. Para 3, são 1, 4 e 9. -test: 3 -> 1 | 4 | 9 -test: 5 -> 1 | 4 | 9 | 16 | 25 -test: 1 -> 1 -braucht: forSchleife -tipp: O ciclo conta \`i\` de 1 até \`n\`. O quadrado de \`i\` é \`i * i\`. -tipp: \`for (int i = 1; i <= n; i++) { … }\` e no corpo \`System.out.println(i * i);\`. -lösung: l13-java-quadrate-loesung.visustruct -::: - -## Preenche os espaços: for - -::: lückentext -titel: Contar para cima e para baixo -auftrag: O primeiro ciclo soma os números de 1 a 5, o segundo conta de 10 até 0 para trás. Preenche as palavras e os sinais que faltam. -\`\`\`java -int soma = 0; -[[for]] ([[int]] i = 1; i [[<=]] 5; [[i++ | i += 1 | i = i + 1]]) { - soma = soma + i; -} -System.out.println("Soma: " + soma); -for (int i = 10; i [[>=]] 0; [[i-- | i -= 1 | i = i - 1]]) { - System.out.println(i); -} -\`\`\` -erklärung: Entre os parênteses ficam o valor inicial, a condição e o passo, separados por ponto e vírgula. \`i++\` conta para cima, \`i--\` conta para baixo. Ao contar para trás até 0, a condição é \`i >= 0\`. -::: - -## Encontra o erro: tabuada - -::: übung l13-java-fehler -titel: Corrigir a tabuada -code: java -auftrag: O programa pergunta que tabuada queres e deve mostrá-la de 1 vez até 10 vezes, para o 7 são 7, 14, … até 70. Mas falta o 70. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l13-java-fehler-start.visustruct -test: 7 -> 7 | 14 | 21 | 28 | 35 | 42 | 49 | 56 | 63 | 70 -test: 3 -> 3 | 6 | 9 | 12 | 15 | 18 | 21 | 24 | 27 | 30 -braucht: forSchleife -tipp: Quantas vezes se repete o ciclo? Conta os valores que \`i\` toma. -tipp: \`i\` também deve tomar o valor 10: \`i <= 10\` em vez de \`i < 10\`. -lösung: l13-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l13-java-laeuft-nicht -titel: Corrigir a escada -code: java -auftrag: O Ludwig sobe 5 degraus, cada um com 20 cm. O programa deve mostrar **Altura: 100 cm**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l13-java-laeuft-nicht-start.visustruct -test: - -> Altura: 100 cm -braucht: forSchleife -tipp: A variável \`altura\` é usada num cálculo, mas onde é criada? E com que altura começa o Ludwig? -tipp: Cria \`altura\` antes do ciclo: \`int altura = 0;\` -lösung: l13-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa esconde **exatamente um erro**, e a bomba está a fazer tiquetaque. Lê o que o programa deve fazer e toca na linha errada antes de o tempo acabar. Se as partes de uma linha tiverem moldura, toca exatamente na parte errada, por exemplo só em \`i++\`. Oito rondas, 25 segundos cada. -zeit: 25 - -runde: - for (int i = 1; i <= 3; i++) { - System.out.println(i) - } -frage: Deve mostrar 1, 2 e 3. -fehler: 2 -besser: System.out.println(i); -> Falta o ponto e vírgula no fim da instrução. - -runde: - for (int i = 1, i <= 5, i++) { - System.out.println(i); - } -frage: Deve mostrar de 1 a 5. -fehler: 1 -besser: for (int i = 1; i <= 5; i++) { -> Os três campos do ciclo for separam-se com ponto e vírgula, não com vírgulas. - -runde: - for (int i = 10; i >= 1; i++) { - System.out.println(i); - } -frage: Deve contar para trás de 10 até 1. -fehler: 1 -stelle: i++ -besser: for (int i = 10; i >= 1; i--) { -> Com \`i++\`, \`i\` fica sempre maior e a condição \`i >= 1\` continua verdadeira: um ciclo infinito. Para contar para trás usas \`i--\`. - -runde: - int soma = 0; - for (int i = 1; i <= 4; i++) { - soma = i; - } - System.out.println(soma); -frage: Deve mostrar a soma 1 + 2 + 3 + 4 = 10. -fehler: 3 -stelle: i -besser: soma = soma + i; -> \`soma = i\` substitui a soma em cada iteração. No fim fica lá só 4. - -runde: - for (int i = 0; i <= 5; i++) { - System.out.println("Mééé"); - } -frage: Deve mostrar Mééé exatamente cinco vezes. -fehler: 1 -stelle: i <= 5 -besser: for (int i = 0; i < 5; i++) { -> De 0 até 5, inclusive, são seis iterações. Quem começa em 0 usa \`<\`. - -runde: - for (int i = 1; i <= 3; i++) { - system.out.println(i); - } -frage: Deve mostrar 1, 2 e 3. -fehler: 2 -stelle: system.out.println -besser: System.out.println(i); -> O Java distingue maiúsculas de minúsculas: \`System\` escreve-se com S maiúsculo. - -runde: - int numero = 7; - for (int i = 1; i <= 10; i++) { - System.out.println(numero + " x " + i + " = " + numero * numero); - } -frage: Deve mostrar a tabuada do 7: 7 x 1 = 7, 7 x 2 = 14 e assim por diante. -fehler: 3 -stelle: numero * numero -besser: System.out.println(numero + " x " + i + " = " + numero * i); -> \`numero * numero\` dá sempre 49. Tem de ser \`numero * i\`. - -runde: - for (int i = 2; i <= 10; i + 2) { - System.out.println(i); - } -frage: Deve mostrar os números pares 2, 4, 6, 8 e 10. -fehler: 1 -stelle: i + 2 -besser: for (int i = 2; i <= 10; i = i + 2) { -> \`i + 2\` só calcula e não guarda nada. Para \`i\` crescer mesmo, é preciso \`i = i + 2\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo nomear as três partes Início, Condição e Passo do ciclo for. → Contar com um plano fixo -- Consigo explicar por que ordem são executadas as três partes. → Por que ordem? -- Consigo contar para a frente, para trás e em passos maiores com um ciclo for. → Outros passos -- Consigo escrever um ciclo for em Java e explicar os seus três campos. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas vezes é executado o Início de um ciclo for? -→ Por que ordem? -- [x] Exatamente uma vez -- [ ] Antes de cada iteração -- [ ] Depois de cada iteração -> O Início cria a variável contadora uma vez, no começo. - -? Início \`int i = 0\`, Condição \`i < 3\`, Passo \`i++\`. Que valores tem i dentro do ciclo? -→ Contar com um plano fixo -- [ ] 1, 2, 3 -- [x] 0, 1, 2 -- [ ] 0, 1, 2, 3 -> Com i = 3 a condição é falsa, por isso o interior já não é executado. - -? Início \`int i = 2\`, Condição \`i <= 10\`, Passo \`i = i + 2\`. Quantas vezes é executado o interior? -→ Outros passos -- [ ] 4 vezes -- [x] 5 vezes -- [ ] 10 vezes -> i toma os valores 2, 4, 6, 8 e 10. - -? Quando é que um ciclo for serve melhor? -→ Contar com um plano fixo -- [x] Quando sabes de antemão quantas vezes repetir -- [ ] Quando perguntas até uma entrada ser válida -- [ ] Quando só é verificado um único valor - -? O que significa \`i++\` num ciclo for? -→ Contar com um plano fixo -- [ ] i é duplicado. -- [x] i aumenta 1. -- [ ] i é mostrado. -> \`i++\` é a forma curta de \`i = i + 1\`. -::: -`,Py=`--- -titel: Encaixar -bereich: Fundamentos -kurz: Combinar decisões dentro de ciclos e ciclos dentro de ciclos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pôr uma decisão dentro de um ciclo -- pôr um ciclo dentro de outro ciclo -- explicar que o ciclo interior é executado do princípio ao fim em cada iteração do exterior -- escrever ciclos e decisões encaixados em Java -::: - -## Blocos uns dentro dos outros - -Já conheces todos os blocos importantes: sequência, decisão, seleção e três tipos de ciclos. Só juntos é que eles se tornam realmente poderosos. - -Cada ramo e cada corpo do ciclo é, ele próprio, uma sequência. Podes pôr lá qualquer elemento, incluindo mais decisões e ciclos. A isto chama-se **encaixar**. - -> Para isso, basta arrastares um elemento para o marcador ø dentro de um ciclo ou de um ramo. - -## Uma decisão dentro de um ciclo - -O ciclo percorre todos os números, e a decisão decide para cada número se acontece alguma coisa. Assim podes contar, por exemplo, quantos números pares há de 1 a n. - -::: diagramm l13-gerade-zaehlen.visustruct -Em cada iteração, a decisão verifica com i % 2 == 0 se i é par. Só nesse caso quantidade aumenta 1. O ramo false fica vazio. -::: - -Com n = 7 são os números 2, 4 e 6, portanto é mostrado 3. - -## Exercício: Contar entradas pares - -::: übung l13-gerade-eingaben -titel: Contar entradas pares -auftrag: Pede exatamente **cinco** números inteiros, um a seguir ao outro. Depois mostra quantos deles eram pares. -test: 1, 2, 3, 4, 5 -> 2 -test: 2, 4, 6, 8, 10 -> 5 -test: 1, 3, 5, 7, 9 -> 0 -test: 10, 11, 0, 7, 8 -> 3 -braucht: forSchleife, verzweigung -tipp: Fazer a mesma coisa cinco vezes significa: ciclo. Em cada iteração é lido e verificado um número. -tipp: Um ciclo for de 1 a 5. No interior vem primeiro a entrada \`input: int numero "Introduz um número"\`, por baixo uma decisão com \`numero % 2 == 0\`. No ramo true aumentas \`quantidade\` em 1. -lösung: l13-gerade-eingaben-loesung.visustruct -::: - -## Um ciclo dentro de um ciclo - -Se um ciclo estiver dentro de outro, o **ciclo interior é executado do princípio ao fim em cada iteração do exterior**. Com isto podes construir padrões de linhas e colunas. - -::: diagramm l13-dreieck.visustruct -O ciclo exterior conta as linhas. O interior acrescenta ao texto tantas estrelas quantas o número da linha indica. Com n = 3 obténs um triângulo de 1, 2 e 3 estrelas. -::: - -Com \`String texto = ""\` cada linha começa vazia. \`texto = texto + "*"\` acrescenta uma estrela. Só quando a linha está pronta é que é mostrada. - -Programadores experientes dão às variáveis contadoras os nomes curtos \`i\` e \`j\`: \`i\` por fora, \`j\` por dentro. Fica assim, aqui com uma pequena tabuada: - -::: grafik l13-verschachtelt-tabelle.svg animiert -O ciclo exterior conta as linhas com \`i\`, o interior conta os números de uma linha com \`j\`. Em cada nova linha, \`j\` começa outra vez em 1. -::: - -## Exercício: Retângulo - -::: übung l13-rechteck -titel: Retângulo -auftrag: Pede uma largura e uma altura. Mostra um retângulo feito de \`#\`: tantas linhas como a altura, e em cada linha tantos \`#\` como a largura. -test: 3, 2 -> ### | ### -test: 1, 3 -> # | # | # -test: 4, 1 -> #### -test: 2, 2 -> ## | ## -braucht: forSchleife -tipp: Precisas de dois ciclos, um dentro do outro: um para as linhas e, lá dentro, um para os caracteres de uma linha. -tipp: Constrói-o como o triângulo. A diferença: o ciclo interior conta sempre até \`largura\`, não até ao número da linha. O ciclo exterior conta até \`altura\`. -tipp: O esqueleto: entradas \`largura\` e \`altura\`. Ciclo for exterior \`int linha = 1\`, \`linha <= altura\`, \`linha++\`. Dentro, primeiro \`String texto = ""\`, depois o ciclo for interior \`int coluna = 1\`, \`coluna <= largura\`, \`coluna++\` com \`texto = texto + "#"\`, e por baixo do ciclo interior \`output: texto\`. -lösung: l13-rechteck-loesung.visustruct -::: - -## Não perder o fio - -Nos diagramas encaixados, o desenho ajuda-te: o que está mais para dentro fica recuado e rodeado pelo seu ciclo ou ramo. - -- Lê de fora para dentro: o que se repete? O que é decidido em cada iteração? -- Dá nomes com significado às variáveis contadoras, como \`linha\` e \`coluna\`. Dois ciclos não podem alterar a mesma variável contadora. -- Percorre um exemplo na simulação com **Passo** e observa as variáveis. - -::: angeberwissen -Foi exatamente para isto que o estrutograma foi inventado. Shneiderman perguntou-se: se devemos passar sem GOTO, porque não passar também sem as setas do fluxograma? O resultado: nada de esparguete de setas, e cada bloco fica bem arrumado no seu cercado. -::: - -## Agora em Java - -Contar os números pares fica assim em Java: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Até que número?"); -int n = scanner.nextInt(); -int quantidade = 0; -for (int i = 1; i <= n; i++) { - if (i % 2 == 0) { - quantidade++; - } // fim do if -} // fim do for -System.out.println(quantidade); -\`\`\` - -O que no estrutograma está encaixado, em Java está encaixado entre chavetas. Cada nível fica quatro espaços mais para dentro. Se o teu código ficar desarrumado, ajuda o botão **Indentar** em cima na área Código. - -Com muitas chavetas de fecho \`}\` é fácil perder a noção: que chaveta pertence a que bloco? Um comentário a seguir diz-te, p. ex. \`} // fim do if\` e \`} // fim do for\`. Isto também ajuda quando continuares mais tarde. - -\`quantidade++\` é a forma curta de \`quantidade = quantidade + 1\`. - -::: übung l14-java-treppe -titel: Escada de estrelas -code: java -auftrag: Escreve um programa em Java que lê o número de degraus e mostra uma escada de asteriscos. Para 3 degraus são \`*\`, \`**\` e \`***\`, cada degrau na sua linha. -test: 3 -> * | ** | *** -test: 1 -> * -test: 4 -> * | ** | *** | **** -braucht: forSchleife -tipp: Um ciclo exterior conta os degraus. Em cada degrau, um ciclo interior junta um texto de asteriscos. -tipp: No ciclo exterior: \`String linha = "";\`, depois \`for (int j = 1; j <= i; j++) { linha = linha + "*"; }\` e a seguir \`System.out.println(linha);\`. -lösung: l14-java-treppe-loesung.visustruct -::: - -## Preenche os espaços: encaixar - -::: lückentext -titel: Retângulo de estrelas -auftrag: O programa mostra um retângulo de 3 linhas com 4 estrelas cada. Preenche as palavras e os sinais que faltam. -\`\`\`java -for (int linha = 1; linha <= 3; linha++) { - String texto = [[""]]; - [[for]] (int coluna = 1; coluna [[<=]] 4; [[coluna++ | coluna += 1 | coluna = coluna + 1]]) { - texto = texto [[+]] "*"; - } - System.out.[[println]](texto); -[[}]] -\`\`\` -erklärung: O ciclo de dentro constrói uma linha de 4 estrelas, o de fora repete isso três vezes. Cada linha começa com um texto vazio \`""\`. A última chaveta fecha o ciclo de fora. -::: - -## Encontra o erro: divisíveis por 3 - -::: übung l14-java-fehler -titel: Corrigir a contagem -code: java -auftrag: O programa deve contar quantos números de 1 a \`n\` são divisíveis por 3 e mostrar o resultado **uma vez**. Para 10 é só **Quantidade: 3**. Mas mostra linhas a mais. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l14-java-fehler-start.visustruct -test: 10 -> Quantidade: 3 -test: 3 -> Quantidade: 1 -test: 2 -> Quantidade: 0 -braucht: forSchleife, verzweigung -tipp: Repara nas chavetas: a saída está dentro do ciclo ou depois dele? -tipp: Passa a linha \`System.out.println("Quantidade: " + quantidade);\` para depois da chaveta que fecha o ciclo. -lösung: l14-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l14-java-laeuft-nicht -titel: Corrigir os números pares -code: java -auftrag: O programa deve mostrar os números pares de 1 a 10: **2**, **4**, **6**, **8**, **10**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l14-java-laeuft-nicht-start.visustruct -test: - -> 2 | 4 | 6 | 8 | 10 -braucht: forSchleife, verzweigung -tipp: O erro está lá dentro, na saída. Como se chama a variável contadora do ciclo? -tipp: A variável contadora é \`i\` com i minúsculo: \`System.out.println(i);\` -lösung: l14-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Dentro e fora - -::: spiel frage -titel: Jogo: Dentro e fora -intro: O Ludwig encaixou blocos uns nos outros. Lê de fora para dentro e diz antes o que acontece. Seis rondas. - -runde: - for (int i = 1; i <= 3; i++) { - for (int j = 1; j <= 2; j++) { - System.out.println("*"); - } - } -frage: Quantas estrelas aparecem? -- 3 -- 5 -- [x] 6 -> O ciclo interior corre por completo em cada uma das 3 voltas do ciclo exterior: 3 · 2 = 6. - -runde: - for (int i = 1; i <= 2; i++) { - for (int j = 1; j <= 3; j++) { - System.out.println(i + " " + j); - } - } -frage: Que linha aparece em quarto lugar? -- 1 4 -- [x] 2 1 -- 2 2 -> Primeiro j percorre tudo com i = 1: 1 1, 1 2, 1 3. Depois j recomeça em 1, agora com i = 2. - -runde: - int contagem = 0; - for (int i = 1; i <= 10; i++) { - if (i % 3 == 0) { - contagem++; - } - } - System.out.println(contagem); -frage: O que aparece? -- [x] 3 -- 10 -- 4 -> A decisão só conta o 3, o 6 e o 9. - -runde: - for (int i = 1; i <= 4; i++) { - if (i % 2 == 0) { - System.out.println("par"); - } else { - System.out.println(i); - } - } -frage: O que aparece? -- 1\\n2\\n3\\n4 -- [x] 1\\npar\\n3\\npar -- par\\npar -> Em cada número a decisão volta a decidir: os ímpares aparecem como número, os pares como palavra. - -runde: - for (int i = 1; i <= 3; i++) { - String linha = ""; - for (int j = 1; j <= i; j++) { - linha = linha + "*"; - } - System.out.println(linha); - } -frage: O que aparece? -- ***\\n***\\n*** -- [x] *\\n**\\n*** -- ***\\n**\\n* -> O ciclo interior vai até i. Por isso cada linha fica com mais uma estrela. - -runde: - for (int i = 1; i <= 3; i++) { - for (int i = 1; i <= 3; i++) { - System.out.println(i); - } - } -frage: O que acontece? -- Aparece 1 a 3 três vezes. -- Aparece 1 a 3 uma vez. -- [x] O Java indica um erro. -> Dois ciclos um dentro do outro precisam de duas variáveis de contagem diferentes, por exemplo \`linha\` e \`coluna\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pôr uma decisão dentro de um ciclo. → Uma decisão dentro de um ciclo -- Consigo pôr um ciclo dentro de outro ciclo. → Um ciclo dentro de um ciclo -- Consigo explicar que o ciclo interior é executado do princípio ao fim em cada iteração do exterior. → Um ciclo dentro de um ciclo -- Consigo escrever ciclos e decisões encaixados em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O ciclo exterior é executado 3 vezes, o interior 4 vezes. Quantas vezes é executado ao todo o interior do ciclo interior? -→ Um ciclo dentro de um ciclo -- [ ] 7 vezes -- [x] 12 vezes -- [ ] 4 vezes -> Em cada uma das 3 iterações exteriores, o ciclo interior é executado 4 vezes, portanto 3 · 4 = 12. - -? Como sabes se um número é par? -→ Uma decisão dentro de um ciclo -- [ ] \`numero / 2 == 0\` -- [x] \`numero % 2 == 0\` -- [ ] \`numero == 2\` -> O % dá o resto de uma divisão. Nos números pares, o resto da divisão por 2 é zero. - -? Há uma decisão dentro de um ciclo for. Quantas vezes é verificada a condição da decisão? -→ Uma decisão dentro de um ciclo -- [ ] Uma vez -- [x] Uma vez por iteração do ciclo -- [ ] Só se for verdadeira -> A decisão faz parte do interior e é executada em cada iteração. - -? Como reconheces em Java o que pertence a um ciclo? -→ Agora em Java -- [ ] Pela cor -- [x] Pelas chavetas e pela indentação -- [ ] Pelos pontos e vírgulas -::: -`,Oy=`--- -titel: Arrays -bereich: Fundamentos -kurz: Guardar muitos valores com um só nome, chegar a eles com o índice e percorrê-los com um ciclo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar um array com valores ou com \`new int[5]\` -- explicar que o índice começa em 0 e que o último índice é \`length - 1\` -- percorrer e encher um array com um ciclo for -- calcular a soma e o máximo de um array -- criar um array em Java e percorrê-lo com um ciclo -::: - -## Muitos valores com um só nome - -Imagina que queres guardar os pontos de cinco testes. Com cinco variáveis \`pontos1\` a \`pontos5\` isso fica depressa confuso. Um **array** é uma fila de posições que têm todas o mesmo nome e o mesmo tipo de dados. - -Crias um array com uma instrução. Depois do tipo de dados escreves parênteses retos \`[]\`: - -\`\`\` -int[] numeros = {2, 4, 6, 8, 10} -String[] nomes = {"Ada", "Rui", "Inês"} -\`\`\` - -Os valores entre chavetas vão logo para as posições. Também podes criar um array vazio com um número fixo de posições: - -\`\`\` -int[] pontos = new int[5] -String[] nomes = new String[3] -\`\`\` - -Com \`new int[5]\`, as cinco posições têm zeros no início; com \`new String[3]\`, textos vazios. - -## O índice começa em 0 - -Cada posição tem um número, o **índice**. Começa-se a contar em **0**. Portanto, com \`int[] numeros = {2, 4, 6, 8, 10}\`: - -- \`numeros[0]\` é 2 -- \`numeros[1]\` é 4 -- \`numeros[4]\` é 10 - -::: grafik l14-array.svg -Cinco posições com os índices 0 a 4. O índice está por cima da posição, o valor lá dentro. -::: - -**Porquê começar em 0?** Na informática, o índice não é “que posição”, mas sim a **distância ao início**. A primeira posição está logo no início, portanto a 0 passos. A segunda está a 1 passo, a terceira a 2 passos. Conheces isto do elevador: o rés-do-chão é o **0**, e o primeiro andar fica um nível acima. - -Daqui sai uma regra que vale a pena decorar: com 5 posições, o **último índice é 4**, sempre um a menos do que o número de posições. - -::: angeberwissen -Contar a partir de 0 já é bem antigo. A linguagem de programação C, de 1972, já funciona assim: o computador sabe o endereço da primeira posição e, para \`numeros[i]\`, calcula simplesmente “início mais i posições”. Com i = 0 fica no início, o que poupa uma conta. Java, Python e JavaScript copiaram isto. Já agora, eu também conto os meus chifres a partir de 0: chifre 0 à esquerda, chifre 1 à direita. -::: - -Com o índice lês um valor ou escreves lá um novo: - -\`\`\` -output: numeros[0] -numeros[1] = 99 -\`\`\` - -> Um array com 5 posições tem os índices 0 a 4. \`numeros[5]\` não existe. A simulação indica então uma expressão inválida. - -## O comprimento de um array - -\`numeros.length\` diz-te quantas posições tem um array. Com cinco valores é 5. Por isso, o **último** índice é sempre \`numeros.length - 1\`: - -\`\`\` -output: numeros.length -output: numeros[numeros.length - 1] -\`\`\` - -A segunda linha mostra o último valor, seja qual for o comprimento do array. O VisuStruct também percebe \`numeros.length()\`. - -::: übung l14-erster-letzter -titel: Primeiro e último valor -auftrag: Cria o array \`int[] valores = {4, 8, 15, 16, 23, 42}\`. Depois mostra em três linhas: o primeiro valor, o último valor e o número de valores. - Para o último valor, usa o comprimento e não escrevas simplesmente o índice 5. -test: - -> 4 | 42 | 6 -braucht: anweisung -tipp: Começa-se a contar em 0, por isso a primeira posição tem o índice 0. E qual é o índice da última posição? -tipp: O primeiro valor é \`valores[0]\`. O último é \`valores[valores.length - 1]\`. O número de valores é \`valores.length\`. -lösung: l14-erster-letzter-loesung.visustruct -::: - -## Percorrer um array com um ciclo - -Um array mostra a sua força junto com um **ciclo for**. A variável contadora \`i\` vai de 0 até ao último índice e é usada como índice: - -\`\`\` -int i = 0 -i < numeros.length -i++ -\`\`\` - -Na primeira iteração \`i\` é 0, na última é 4. Como a condição é \`i < numeros.length\`, o ciclo serve para qualquer comprimento. - -::: diagramm l14-array-durchlaufen.visustruct -O ciclo for mostra os cinco valores um a seguir ao outro. Em Variáveis, na simulação, vê o i a contar de 0 a 4. -::: - -É também assim que enches um array vazio com entradas. Como destino da entrada escreves a posição \`nomes[i]\`: - -::: diagramm l14-namen-eingeben.visustruct -São pedidos três nomes, que são escritos nas posições 0, 1 e 2. -::: - -::: übung l14-rueckwaerts -titel: Mostrar ao contrário -auftrag: Cria um array para 4 números inteiros e lê os 4 números com um ciclo. Depois mostra-os pela ordem **inversa**, cada um na sua linha. -test: 1, 2, 3, 4 -> 4 | 3 | 2 | 1 -test: 7, 0, 5, 9 -> 9 | 5 | 0 | 7 -braucht: forSchleife -tipp: Precisas de dois ciclos: um para ler da frente para trás e outro para mostrar de trás para a frente. -tipp: Crias o array com \`int[] numeros = new int[4]\`. A entrada é \`input: numeros[i] "Introduz um número"\`. No segundo ciclo começas em \`int i = numeros.length - 1\`, verificas \`i >= 0\` e contas para trás com \`i--\`. -lösung: l14-rueckwaerts-loesung.visustruct -::: - -::: angeberwissen -**Hedy Lamarr**, nascida em Viena, era uma estrela de cinema famosa de Hollywood nos anos 40. Nos tempos livres gostava de inventar. Em 1942, juntamente com o compositor George Antheil, recebeu uma patente para uma ligação de rádio que o inimigo não conseguisse perturbar: o emissor e o recetor saltam juntos por uma lista de **88 frequências**, tantas como as teclas de um piano. A lista estava perfurada numa fita de papel, como o rolo de um piano automático. Os dois percorrem a mesma lista pela mesma ordem, tal como o teu ciclo percorre um array com \`i\`. Quem escuta ou perturba só uma frequência quase não apanha nada. - -Este método, o **salto de frequência** (em inglês *frequency hopping*), é usado hoje, por exemplo, no Bluetooth. Ela não o inventou sozinha, já havia ideias anteriores. Mas a sua patente é vista como uma precursora importante, e em 1997 recebeu um prémio por isso. -::: - -::: bild hedy-lamarr.jpg -Hedy Lamarr (1914–2000), atriz e inventora, 1944 (foto promocional: [desconhecido](https://commons.wikimedia.org/wiki/File:Hedy_Lamarr_Publicity_Photo_for_The_Heavenly_Body_1944.jpg), domínio público). -::: - -## Soma e máximo - -Há duas tarefas que aparecem sempre com arrays: - -- **Soma:** Começas com \`soma = 0\` e somas cada valor no ciclo. -- **Máximo:** Primeiro guardas o valor da posição 0 como o maior até agora. No ciclo verificas cada valor. Se for maior, passa a ser o novo máximo. - -::: diagramm l14-summe-maximum.visustruct -Soma e máximo numa única passagem. O máximo começa com pontos[0] e não com 0, para também estar certo com números negativos. -::: - -::: übung l14-summe-max -titel: Soma e máximo -auftrag: Lê 5 números inteiros para um array. Depois mostra **Soma: …** e na linha seguinte **Máximo: …**. -test: 3, 9, 2, 9, 1 -> Soma: 24 | Máximo: 9 -test: 10, 20, 30, 40, 50 -> Soma: 150 | Máximo: 50 -test: -5, -2, -9, -1, -3 -> Soma: -20 | Máximo: -1 -braucht: forSchleife, verzweigung -tipp: Percorre o array com um ciclo. Soma cada valor à soma e guarda o maior valor visto até agora. -tipp: Calcula a soma e o máximo em variáveis próprias e só os mostra no fim, p. ex. com \`output: "Soma: " + soma\`. Se o teu máximo começar em 0, o terceiro teste falha. -tipp: O esqueleto: criar o array e lê-lo com um ciclo. Depois \`int soma = 0\` e \`int max = numeros[0]\`. Um segundo ciclo calcula \`soma = soma + numeros[i]\`, e uma decisão \`numeros[i] > max\` põe \`max = numeros[i]\` no ramo true. No fim, as duas saídas. -lösung: l14-summe-max-loesung.visustruct -::: - -## Agora em Java - -Percorrer um array fica assim em Java: - -\`\`\`java editor -int[] numeros = {2, 4, 6, 8, 10}; -for (int i = 0; i < numeros.length; i++) { - System.out.println(numeros[i]); -} -\`\`\` - -- \`int[]\` quer dizer “array de números inteiros”. Os valores ficam entre chavetas. -- Um array vazio com 5 posições cria-se com \`new int[5]\`. -- \`numeros[i]\` é a posição com o índice \`i\`, \`numeros.length\` é o comprimento. - -Se mostrares o array inteiro, ou seja \`System.out.println(numeros);\`, o Java não mostra os números. Só vês um texto como \`[I@1b6d3586\`. É a **referência**, o sítio onde o array está guardado na memória. O conteúdo só aparece com \`Arrays.toString(numeros)\`, que dá \`[2, 4, 6, 8, 10]\`. Para isso escreves \`import java.util.Arrays;\` logo no início. - -::: übung l15-java-noten -titel: Soma das notas em Java -code: java -auftrag: Cria em Java o array \`notas\` com os valores 2, 3, 1 e 4. Soma todas as notas com um ciclo for e mostra **Soma: 10**. -test: - -> Soma: 10 -braucht: forSchleife -tipp: Cria \`int soma = 0;\` antes do ciclo. O ciclo vai de 0 até \`notas.length - 1\`. -tipp: \`int[] notas = {2, 3, 1, 4};\`, depois \`for (int i = 0; i < notas.length; i++) { soma = soma + notas[i]; }\` e no fim \`System.out.println("Soma: " + soma);\`. -lösung: l15-java-noten-loesung.visustruct -::: - -## Preenche os espaços: arrays - -::: lückentext -titel: Procurar o máximo -auftrag: O programa procura o maior valor do array. Preenche as palavras e os sinais que faltam. -\`\`\`java -[[int]][] valores = {3, 7, 1, 9, 4}; -int max = valores[0]; -[[for]] (int i = 1; i < valores.[[length]]; i++) { - if (valores[i] [[>]] max) { - max = valores[i]; - } -} -System.out.println("Máximo: " + max); -\`\`\` -erklärung: \`int[]\` é um array de números inteiros, \`valores.length\` é o seu comprimento. \`max\` começa com o primeiro valor e o ciclo compara todos os outros com ele. Saída: **Máximo: 9** -::: - -## Encontra o erro: temperaturas - -::: übung l15-java-fehler -titel: Corrigir a temperatura mais alta -code: java -auftrag: Em janeiro mediram-se as temperaturas -5, -2, -8 e -3 graus. O programa deve mostrar a mais alta, ou seja, **Temperatura mais alta: -2**. Mas diz 0 graus, embora nunca tenha estado tão quente. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l15-java-fehler-start.visustruct -test: - -> Temperatura mais alta: -2 -braucht: forSchleife, verzweigung -tipp: Com que valor começa \`max\`? Alguma das temperaturas é maior do que esse valor inicial? -tipp: \`max\` tem de começar com um valor do array: \`int max = temperaturas[0];\` -lösung: l15-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l15-java-laeuft-nicht -titel: Somar os pontos -code: java -auftrag: O array \`pontos\` contém 4, 7, 2 e 5. O programa deve somá-los todos e mostrar **Soma: 18**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l15-java-laeuft-nicht-start.visustruct -test: - -> Soma: 18 -braucht: forSchleife -tipp: A área Código sublinha a palavra errada a vermelho. Como se escreve o comprimento de um array? -tipp: \`length\` escreve-se todo em minúsculas: \`i < pontos.length\` -lösung: l15-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Cada programa tem **exatamente um erro** com arrays, e a bomba está a fazer tique-taque. Toca na linha errada antes de o tempo acabar. Se as partes de uma linha tiverem moldura, toca exatamente na parte errada. Seis rondas, 30 segundos cada. -zeit: 30 - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - for (int i = 0; i <= numeros.length; i++) { - System.out.println(numeros[i]); - } -frage: Deve mostrar os cinco valores. -fehler: 2 -stelle: i <= numeros.length -besser: for (int i = 0; i < numeros.length; i++) { -> Com \`<=\`, i vai até 5. A posição \`numeros[5]\` não existe. - -runde: - String[] nomes = {"Ada", "Ben", "Cem"}; - System.out.println(nomes[1]); -frage: Deve mostrar o primeiro nome, ou seja, Ada. -fehler: 2 -stelle: nomes[1] -besser: System.out.println(nomes[0]); -> Conta-se a partir de 0. \`nomes[1]\` já é o Ben. - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - System.out.println(numeros[numeros.length]); -frage: Deve mostrar o último valor, ou seja, 10. -fehler: 2 -stelle: numeros[numeros.length] -besser: System.out.println(numeros[numeros.length - 1]); -> O último índice é sempre um a menos do que o comprimento. - -runde: - int[] pontos = {3, 9, 2}; - int soma = 0; - for (int i = 0; i < pontos.length; i++) { - soma = pontos[i]; - } - System.out.println(soma); -frage: Deve mostrar a soma 14. -fehler: 4 -stelle: pontos[i] -besser: soma = soma + pontos[i]; -> Assim cada valor apaga o anterior, e no fim soma só tem 2. - -runde: - int[] pontos = {-5, -2, -9}; - int max = 0; - for (int i = 0; i < pontos.length; i++) { - if (pontos[i] > max) { - max = pontos[i]; - } - } - System.out.println(max); -frage: Deve mostrar o máximo -2. -fehler: 2 -stelle: 0 -besser: int max = pontos[0]; -> Nenhum dos números é maior do que 0, por isso max ficaria em 0. É por isso que o máximo começa com a primeira posição. - -runde: - int[] numeros = {2, 4, 6, 8, 10}; - for (int i = numeros.length; i >= 0; i--) { - System.out.println(numeros[i]); - } -frage: Deve mostrar os valores de trás para a frente, de 10 a 2. -fehler: 2 -stelle: int i = numeros.length -besser: for (int i = numeros.length - 1; i >= 0; i--) { -> De trás para a frente começa-se no último índice, que é \`numeros.length - 1\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar um array com valores ou com \`new int[5]\`. → Muitos valores com um só nome -- Consigo explicar que o índice começa em 0 e que o último índice é \`length - 1\`. → O índice começa em 0 -- Consigo percorrer e encher um array com um ciclo for. → Percorrer um array com um ciclo -- Consigo calcular a soma e o máximo de um array. → Soma e máximo -- Consigo criar um array em Java e percorrê-lo com um ciclo. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que mostra \`output: numeros[2]\` com \`int[] numeros = {5, 10, 15, 20}\`? -→ O índice começa em 0 -- [ ] 10 -- [x] 15 -- [ ] 20 -> Começa-se a contar em 0: numeros[0] é 5, numeros[1] é 10, numeros[2] é 15. - -? Um array tem 8 posições. Qual é o último índice? -→ O índice começa em 0 -- [ ] 8 -- [x] 7 -- [ ] 9 -> O último índice é sempre length - 1. - -? Que condição serve para um ciclo for que percorre todos os valores de \`numeros\` e começa em \`int i = 0\`? -→ Percorrer um array com um ciclo -- [x] i < numeros.length -- [ ] i <= numeros.length -- [ ] i > numeros.length -> Com <= o ciclo também leria numeros[length], e essa posição não existe. - -? Com que valor deve começar o máximo antes de o ciclo começar? -→ Soma e máximo -- [ ] Sempre com 0 -- [x] Com o primeiro valor do array -- [ ] Com o comprimento do array -> Se começar com 0, então só com números negativos o resultado seria 0, embora o 0 nem esteja no array. - -? O que mostra \`output: numeros.length\` com \`int[] numeros = {5, 10, 15, 20}\`? -→ Muitos valores com um só nome -- [ ] 3 -- [x] 4 -- [ ] 20 -> length é o número de posições, não o último índice nem o último valor. - -? Que linha cria em Java um array com três números? -→ Agora em Java -- [x] int[] numeros = {1, 2, 3}; -- [ ] int numeros = [1, 2, 3]; -- [ ] array numeros = (1, 2, 3); -::: -`,My=`--- -titel: Arrays bidimensionais -bereich: Fundamentos -kurz: Guardar valores em linhas e colunas, como os lugares de um cinema, e percorrê-los com dois ciclos encaixados. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar um array 2D e explicar o que significam os dois números -- ler e escrever uma posição com fila e lugar -- distinguir \`lugares.length\` de \`lugares[0].length\` -- explicar por que ordem dois ciclos encaixados visitam as posições -- criar e percorrer um array bidimensional em Java -::: - -## Um cinema como tabela - -Um cinema tem várias **filas**, e cada fila tem vários **lugares**. Para cada lugar queremos saber se o bilhete já foi vendido. É uma pergunta de sim ou não, por isso serve o tipo de dados \`boolean\`: \`true\` quer dizer vendido, \`false\` quer dizer livre. - -Um array normal é uma só fila de posições. Para o cinema precisamos de uma **tabela** com linhas e colunas. Isso é um **array bidimensional**, abreviado array 2D. Para isso escreves dois pares de parênteses retos: - -\`\`\` -boolean[][] lugares = new boolean[3][5] -\`\`\` - -Isto dá 3 filas com 5 lugares cada, ou seja, 15 posições. No início, todas as posições têm \`false\`: todos os lugares estão livres. - -::: grafik kino-saal.svg -O primeiro índice é a fila, o segundo é o lugar. Os dois contam-se a partir de 0. -::: - -## Chegar a uma posição - -Para uma posição precisas agora de **dois** índices: primeiro a fila, depois o lugar. - -\`\`\` -lugares[1][2] = true -\`\`\` - -Agora o lugar 2 da fila 1 está vendido. Ler funciona da mesma maneira: \`lugares[1][2]\` é agora \`true\`. - -Também podes perguntar o tamanho: - -- \`lugares.length\` é o número de **filas**, aqui 3. -- \`lugares[0].length\` é o número de **lugares** da fila 0, aqui 5. - -::: diagramm kino-verkaufen.visustruct -São vendidos três bilhetes. Depois, o diagrama mostra o tamanho do cinema e se a fila 1, lugar 2, está vendido. Repara em Variáveis, na simulação: lugares é mostrado fila a fila. -::: - -> Tal como num array normal, não existe a posição \`lugares[3][0]\`, porque as filas se chamam 0, 1 e 2. A simulação indica então uma expressão inválida. - -## Dois ciclos um dentro do outro - -Para olhar para **todos** os lugares, precisas de dois ciclos for, um dentro do outro: - -- O **ciclo exterior** conta a fila: \`fila\` vai de 0 a 2. -- O **ciclo interior** conta o lugar: \`lugar\` vai de 0 a 4. - -::: grafik kino-schleifen.svg -Azul é o ciclo exterior, laranja o interior. Os números mostram a ordem pela qual os lugares aparecem. -::: - -Para cada fila, o ciclo interior é executado **do princípio ao fim** uma vez. Só quando \`lugar\` passou pelos cinco lugares é que o ciclo exterior avança \`fila\` em um. Depois, \`lugar\` recomeça em 0. Assim, todos os 3 · 5 = 15 lugares aparecem exatamente uma vez. - -As condições são \`fila < lugares.length\` e \`lugar < lugares[fila].length\`. Assim os ciclos servem para um cinema de qualquer tamanho. - -::: diagramm kino-saalplan.visustruct -A planta da sala: para cada fila é montada e mostrada uma linha de X (vendido) e O (livre). Percorre-a com Passo e vê como lugar recomeça sempre do início, enquanto fila só vai contando devagar. -::: - -::: angeberwissen -Cada foto do telemóvel é um array 2D! Uma foto com 4000 · 3000 píxeis tem 3000 linhas com 4000 posições cada, ou seja, 12 milhões de píxeis. Cada posição guarda a cor de um ponto. Quando pões uma foto mais clara, o telemóvel percorre todos os píxeis com dois ciclos encaixados. A minha foto de perfil, já agora, só tem uma posição: castanho. -::: - -## Exercício: Contar lugares livres - -::: übung kino-frei -titel: Contar lugares livres -auftrag: No diagrama inicial já foram vendidos três bilhetes. Usa dois ciclos encaixados para contar quantos lugares ainda estão **livres** e mostra p. ex. \`Livres: 12\`. -start: kino-frei-start.visustruct -test: - -> Livres: 12 -braucht: forSchleife, verzweigung -tipp: Tal como na planta da sala, percorres todos os lugares com dois ciclos. Em vez de mostrares alguma coisa, contas. -tipp: Antes dos ciclos, cria \`int livres = 0\`. Um lugar está livre se \`lugares[fila][lugar]\` for false. A condição para isso é \`!lugares[fila][lugar]\`. -lösung: kino-frei-loesung.visustruct -::: - -## Exercício: Vender um bilhete - -::: übung kino-karte -titel: Vender um bilhete -auftrag: O diagrama inicial pede uma fila e um lugar. Se o lugar já estiver vendido, mostra \`Já vendido\`. Caso contrário, marca-o como vendido e mostra p. ex. \`Vendido: fila 0, lugar 4\`. -start: kino-karte-start.visustruct -test: 1, 2 -> Já vendido -test: 0, 4 -> Vendido: fila 0, lugar 4 -test: 2, 0 -> Vendido: fila 2, lugar 0 -braucht: verzweigung -tipp: Verifica primeiro se o lugar está vendido. Consoante a resposta, acontece uma coisa diferente. -tipp: Aqui não precisas de ciclo. Basta uma decisão com a condição \`lugares[fila][lugar]\`. No ramo false pões a posição a \`true\`. -lösung: kino-karte-loesung.visustruct -::: - -## Exercício: Tabela da tabuada - -::: übung kino-einmaleins -titel: Tabela da tabuada -auftrag: Cria \`int[][] tabela = new int[10][10]\` e enche-a com a tabuada até 10 usando dois ciclos. Depois pede dois números de 1 a 10 e lê o resultado da tabela, p. ex. \`3 · 4 = 12\`. -test: 3, 4 -> 3 · 4 = 12 -test: 7, 8 -> 7 · 8 = 56 -test: 10, 10 -> 10 · 10 = 100 -braucht: forSchleife -tipp: Primeiro, dois ciclos encaixados enchem a tabela; depois lês só uma única posição. -tipp: Os índices vão de 0 a 9, mas os números de 1 a 10. Por isso, a posição \`tabela[i][j]\` tem o valor \`(i + 1) * (j + 1)\`, e para 3 · 4 lês \`tabela[a - 1][b - 1]\`. -lösung: kino-einmaleins-loesung.visustruct -::: - -## Agora em Java - -A sala de cinema fica assim em Java: - -\`\`\`java editor -boolean[][] lugares = new boolean[3][5]; -lugares[0][2] = true; -lugares[1][1] = true; -lugares[1][2] = true; -System.out.println("Filas: " + lugares.length); -System.out.println("Lugares por fila: " + lugares[0].length); -System.out.println("Fila 1, lugar 2 vendido? " + lugares[1][2]); -\`\`\` - -- Dois pares de parênteses retos \`[][]\` representam uma tabela: primeiro a fila, depois o lugar. -- \`lugares.length\` é o número de filas, \`lugares[0].length\` o número de lugares numa fila. -- Com valores fixos, escreves cada fila nas suas próprias chavetas: \`int[][] t = {{1, 2}, {3, 4}};\` - -::: übung l16-java-punkte -titel: Somar os pontos -code: java -auftrag: Três jogadores jogaram duas rondas cada um. Cria em Java a tabela \`pontos\` com as filas {3, 5}, {4, 1} e {2, 2}. Soma todos os pontos com dois ciclos for encaixados e mostra **Soma: 17**. -test: - -> Soma: 17 -braucht: forSchleife -tipp: O ciclo exterior percorre as filas, o interior os lugares de uma fila. -tipp: \`int[][] pontos = {{3, 5}, {4, 1}, {2, 2}};\` e no ciclo interior \`soma = soma + pontos[i][j];\`. O ciclo interior vai até \`pontos[i].length\`. -lösung: l16-java-punkte-loesung.visustruct -::: - -## Preenche os espaços: tabelas - -::: lückentext -titel: Somar todos os campos -auftrag: O programa soma todos os números da tabela. Preenche as palavras e os sinais que faltam. -\`\`\`java -[[int]][][] campo = {{1, 2, 3}, {4, 5, 6}}; -int soma = 0; -for (int l = 0; l < campo.[[length]]; l++) { - for (int c = 0; c < campo[l].[[length]]; c++) { - soma = soma [[+]] campo[l][c]; - } -} -System.out.println("Soma: " + soma); -\`\`\` -erklärung: \`int[][]\` é uma tabela. \`campo.length\` é o número de linhas (2), \`campo[l].length\` o número de campos na linha \`l\` (3). Saída: **Soma: 21** -::: - -## Encontra o erro: pontos - -::: übung l16-java-fehler -titel: Corrigir o jogador 2 -code: java -auftrag: Na tabela \`pontos\`, cada linha corresponde a um jogador. O programa deve somar os pontos do jogador 2 (linha 1), ou seja, **Jogador 2: 11**. Em vez disso, dá um erro. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l16-java-fehler-start.visustruct -test: - -> Jogador 2: 11 -braucht: forSchleife -tipp: Em \`pontos[a][b]\`, \`a\` é a linha e \`b\` o lugar na linha. Que número corresponde à linha do jogador 2? -tipp: Os índices estão trocados: \`pontos[1][j]\` em vez de \`pontos[j][1]\`. -lösung: l16-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l16-java-laeuft-nicht -titel: Ler a pontuação -code: java -auftrag: Na tabela \`pontos\` cada linha é um jogador e cada coluna uma ronda. O programa deve mostrar os pontos do jogador 2 na ronda 1, ou seja, **Jogador 2, ronda 1: 4**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l16-java-laeuft-nicht-start.visustruct -test: - -> Jogador 2, ronda 1: 4 -braucht: anweisung -tipp: As posições \`[1][0]\` estão certas. Olha para o nome da tabela. -tipp: A tabela chama-se \`pontos\` com p minúsculo: \`pontos[1][0]\` -lösung: l16-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Que lugar? - -::: spiel frage -titel: Jogo: Que lugar? -intro: O Ludwig vende bilhetes de cinema. Primeiro a fila, depois o lugar, ambos a partir de 0. Sete rondas. - -runde: - boolean[][] lugares = new boolean[3][5]; - System.out.println(lugares.length); -frage: O que aparece? -- [x] 3 -- 5 -- 15 -> \`lugares.length\` é o número de filas. - -runde: - boolean[][] lugares = new boolean[3][5]; - System.out.println(lugares[0].length); -frage: O que aparece? -- 3 -- [x] 5 -- 15 -> \`lugares[0]\` é a fila 0. O seu comprimento é o número de lugares nessa fila. - -runde: - int[][] tabela = new int[4][6]; -frage: Quantas posições tem a tabela? -- 10 -- [x] 24 -- 35 -> 4 filas com 6 lugares cada: 4 · 6 = 24. - -runde: - int[][] t = {{1, 2}, {3, 4}}; - System.out.println(t[1][0]); -frage: O que aparece? -- 2 -- [x] 3 -- 4 -> A fila 1 é \`{3, 4}\`, e o lugar 0 dela é o 3. - -runde: - boolean[][] lugares = new boolean[3][5]; - lugares[3][0] = true; -frage: O que acontece? -- O primeiro lugar da fila 3 fica vendido. -- O primeiro lugar da última fila fica vendido. -- [x] Há um erro, porque as filas são 0, 1 e 2. -> Tal como num array normal, o último índice é um a menos do que o comprimento. - -runde: - int[][] t = {{1, 2, 3}, {4, 5, 6}}; - int soma = 0; - for (int fila = 0; fila < t.length; fila++) { - for (int lugar = 0; lugar < t[fila].length; lugar++) { - soma = soma + t[fila][lugar]; - } - } - System.out.println(soma); -frage: O que aparece? -- 6 -- 15 -- [x] 21 -> Os dois ciclos passam pelas seis posições: 1 + 2 + 3 + 4 + 5 + 6 = 21. - -runde: -frage: Dois ciclos percorrem a sala \`new boolean[3][5]\`. Que posição vem logo a seguir a \`lugares[0][4]\`? -- \`lugares[0][5]\` -- [x] \`lugares[1][0]\` -- \`lugares[1][4]\` -> A fila 0 terminou. O ciclo exterior avança, e o lugar recomeça em 0. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar um array 2D e explicar o que significam os dois números. → Um cinema como tabela -- Consigo ler e escrever uma posição com fila e lugar. → Chegar a uma posição -- Consigo distinguir \`lugares.length\` de \`lugares[0].length\`. → Chegar a uma posição -- Consigo explicar por que ordem dois ciclos encaixados visitam as posições. → Dois ciclos um dentro do outro -- Consigo criar e percorrer um array bidimensional em Java. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? Quantas posições tem \`boolean[][] lugares = new boolean[4][6]\`? -→ Um cinema como tabela -- [ ] 10 -- [x] 24 -- [ ] 46 -> 4 filas com 6 lugares cada: 4 · 6 = 24. - -? O que está em todas as posições depois de \`new boolean[3][5]\`? -→ Um cinema como tabela -- [ ] true -- [x] false -- [ ] Nada, as posições estão vazias -> Um array boolean novo é enchido com false. No cinema isso quer dizer: todos os lugares estão livres. - -? Dois ciclos encaixados percorrem 3 filas com 5 lugares. Quantas vezes é executado o interior do ciclo **interior**? -→ Dois ciclos um dentro do outro -- [ ] 3 vezes -- [ ] 8 vezes -- [x] 15 vezes -> Para cada uma das 3 filas, o ciclo interior é executado 5 vezes: 3 · 5 = 15. - -? Associa o que cada expressão significa no cinema. -→ Chegar a uma posição -= lugares.length -> Número de filas -= lugares[0].length -> Número de lugares da fila 0 -= lugares[2][4] -> Fila 2, lugar 4 -= lugares[1][0] = true -> O lugar 0 da fila 1 está vendido - -? O que dá \`lugares.length\` para \`new boolean[3][5]\`? -→ Chegar a uma posição -- [x] 3, o número de filas -- [ ] 5, o número de lugares -- [ ] 15, todos os lugares juntos -::: -`,Zy=`--- -titel: Strings -bereich: Fundamentos -kurz: Trabalhar com texto: ler caracteres, recortar partes, pesquisar, comparar e dividir. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar que os caracteres de uma String também se contam a partir de 0 -- trabalhar com \`length()\` e \`charAt(i)\` -- distinguir um \`char\` de uma String e usar o código de caractere com \`(int)\` e \`(char)\` -- recortar uma parte de um texto com \`substring\` -- usar \`indexOf\`, \`contains\`, \`toUpperCase\` e \`equals\` -- percorrer um texto caractere a caractere com um ciclo -- dividir um texto num array de Strings com \`split\` -- ler um texto em Java e compará-lo com \`equals\` -- usar mais métodos, como \`lastIndexOf\`, \`replace\`, \`compareTo\` e \`Integer.parseInt\` -- dividir o texto de um ficheiro em linhas com \`split("\\n")\` -- consultar um método na API do Java e ler a sua descrição -::: - -## Um texto é uma fila de caracteres - -Já conheces \`String\`: \`String nome = "Ludwig"\`. Na informática, um texto destes chama-se **string** (em inglês, “fio”), porque os caracteres ficam pendurados uns a seguir aos outros como contas num fio. Também se diz **cadeia de caracteres**. Cada caractere tem um lugar com um índice, tal como num array. Também aqui **se começa a contar em 0**: o primeiro caractere está a 0 passos do início. - -::: grafik zk-zeichen.svg -Os dez caracteres de “Passatempo” com os seus índices de 0 a 9. -::: - -Uma String traz consigo **métodos**. Escreves o nome da variável, um ponto e o método: - -- \`palavra.length()\` dá o número de caracteres, aqui 10. -- \`palavra.charAt(0)\` dá o caractere com o índice 0, aqui \`'P'\`. - -> Nas Strings escreves \`length()\` **com** parênteses, nos arrays \`length\` **sem**. Infelizmente, o Java é mesmo assim. - -Tal como num array, o último caractere tem o índice \`length() - 1\`. - -## Um único caractere: char - -Para um único caractere, o Java tem um tipo de dados próprio: **char**, abreviatura do inglês *character* (caractere). Um \`char\` fica entre aspas **simples**, uma String entre aspas duplas: - -- \`'C'\` é um \`char\`: exatamente um caractere. -- \`"C"\` é uma String: um texto que só tem um caractere. -- Um \`char\` tem sempre exatamente um caractere. Não existe um \`char\` vazio como \`''\`, mas uma String vazia \`""\` existe. - -\`charAt\` dá-te um caractere destes a partir de um texto: - -\`\`\` -char primeiro = palavra.charAt(0) -char ultimo = palavra.charAt(palavra.length() - 1) -\`\`\` - -Dois \`char\` comparas com \`==\`: \`palavra.charAt(0) == 'P'\` é \`true\`. - -Por trás de cada caractere há um número, o seu **código de caractere**. Que número pertence a que caractere está na tabela ASCII: \`'A'\` tem o código 65, \`'B'\` 66, \`'C'\` 67 e assim por diante até \`'Z'\` com 90. Com \`(int)\` obténs o código de um caractere, com \`(char)\` um código volta a ser um caractere: - -\`\`\` -int codigo = (int) 'C' -codigo = codigo + 1 -char seguinte = (char) codigo -\`\`\` - -Depois disto, \`codigo\` vale 68 e \`seguinte\` é \`'D'\`. Assim deslocas uma letra no alfabeto, e é exatamente isso que vais precisar mais tarde na cifra de César. - -## Recortar partes - -Com \`substring\` recortas uma parte do texto: - -- \`palavra.substring(0, 5)\` dá os caracteres desde o índice 0 **até mesmo antes** do índice 5, ou seja, \`"Passa"\`. -- \`palavra.substring(5)\` dá tudo desde o índice 5 até ao fim, ou seja, \`"tempo"\`. - -O fim **não** está incluído. Dá jeito: \`substring(0, 5)\` dá exatamente 5 caracteres. - -::: diagramm zk-zeichen.visustruct -Todos os métodos desta lição num exemplo. Percorre-o na simulação e compara cada saída com a imagem. -::: - -## Pesquisar e comparar - -Mais métodos de que precisas muitas vezes: - -| Método | Significado | Exemplo com \`"Passatempo"\` | -|---|---|---| -| \`indexOf("tempo")\` | Onde começa o texto? | 5 | -| \`indexOf("x")\` | não encontrado | -1 | -| \`contains("sat")\` | O texto aparece? | true | -| \`toUpperCase()\` | tudo em maiúsculas | PASSATEMPO | -| \`toLowerCase()\` | tudo em minúsculas | passatempo | -| \`equals("Passatempo")\` | o mesmo texto? | true | - -> Em Java comparas duas Strings com \`equals\`, não com \`==\`. Já dois caracteres (\`char\`) comparas com \`==\`, p. ex. \`palavra.charAt(i) == 'a'\`. - -::: angeberwissen -Experimenta \`"Ludwig".equals("ludwig")\`. A resposta é false! Para um computador, L e l são caracteres completamente diferentes. Lá dentro, cada caractere é guardado como um número: L é 76 e l é 108. Esta numeração chama-se ASCII e é de 1963. Mééé, portanto, para um computador, Ludwig e ludwig são dois bodes diferentes. Eu sou o do L maiúsculo. -::: - -## Caractere a caractere - -Com um ciclo for percorres um texto caractere a caractere. O índice \`i\` vai de 0 a \`palavra.length() - 1\`, tal como num array: - -::: diagramm zk-zaehlen.visustruct -Conta quantas vezes aparece a letra a. Na simulação, experimenta “banana” e observa i e quantidade. -::: - -## Dividir com split - -Muitas vezes há várias informações numa só linha, separadas por um caractere, p. ex. \`"Ludwig;bode;7"\`. Com \`split\` cortas a linha nesse caractere. O resultado é um **array de Strings**: - -\`\`\` -String[] partes = linha.split(";") -\`\`\` - -Depois disso, \`partes[0]\` é \`"Ludwig"\`, \`partes[1]\` é \`"bode"\` e \`partes[2]\` é \`"7"\`. Com \`partes.length\` descobres quantas partes há. - -::: diagramm zk-split.visustruct -A linha é dividida no ponto e vírgula, e cada parte é mostrada com o seu índice. -::: - -> Os ficheiros CSV, que conheces das folhas de cálculo, são feitos da mesma maneira: uma linha por registo, com os valores separados por vírgula ou ponto e vírgula. - -## Exercício: Iniciais - -::: übung zk-initialen -titel: Iniciais -auftrag: Pede um nome próprio e um apelido e mostra as iniciais. Ou seja, para Ludwig Bode fica \`L. B.\` -test: Ludwig, Bode -> L. B. -test: Ada, Lovelace -> A. L. -test: Ben, Shneiderman -> B. S. -braucht: anweisung -tipp: A inicial é a primeira letra de um nome. Que método dá o caractere numa determinada posição? -tipp: \`nome.charAt(0)\` dá a primeira letra. A saída fica então \`nome.charAt(0) + ". " + apelido.charAt(0) + "."\` -lösung: zk-initialen-loesung.visustruct -::: - -## Exercício: Ao contrário - -::: übung zk-rueckwaerts -titel: Ao contrário -auftrag: Pede uma palavra e mostra-a ao contrário. Ludwig passa a \`giwduL\`. -test: Ludwig -> giwduL -test: Ana -> anA -test: a -> a -braucht: forSchleife -tipp: Percorre a palavra caractere a caractere, mas de trás para a frente, e vai construindo um novo texto. -tipp: Cria \`String invertida = ""\`. O ciclo começa no último índice \`palavra.length() - 1\` e conta para trás até 0 com \`i--\`. Em cada iteração acrescentas \`palavra.charAt(i)\` no fim. -tipp: O esqueleto: entrada \`palavra\`, depois \`String invertida = ""\`. Ciclo for com início \`int i = palavra.length() - 1\`, condição \`i >= 0\` e passo \`i--\`. Dentro, \`invertida = invertida + palavra.charAt(i)\`. Por baixo do ciclo, \`output: invertida\`. -lösung: zk-rueckwaerts-loesung.visustruct -::: - -## Exercício: Contar palavras - -::: übung zk-woerter -titel: Contar palavras -auftrag: Pede uma frase e mostra quantas palavras tem, p. ex. \`Palavras: 5\` para “O Ludwig é um bode”. As palavras estão separadas por exatamente um espaço. -test: O Ludwig é um bode -> Palavras: 5 -test: Olá -> Palavras: 1 -test: Eu gosto de cabras -> Palavras: 4 -braucht: anweisung -tipp: Entre as palavras há sempre um espaço. Que método divide um texto num caractere? -tipp: Divide a frase num array de Strings com \`frase.split(" ")\`. O seu comprimento é o número de palavras. -tipp: O programa todo tem três blocos: a entrada \`frase\`, depois \`String[] palavras = frase.split(" ")\` e no fim \`output: "Palavras: " + palavras.length\`. -lösung: zk-woerter-loesung.visustruct -::: - -## Agora em Java - -Os métodos desta lição já são Java. Um programa completo com um texto fica assim: - -\`\`\`java editor -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Como te chamas?"); -String nome = scanner.next(); -System.out.println("Olá " + nome.toUpperCase() + "!"); -System.out.println("O teu nome tem " + nome.length() + " letras."); -\`\`\` - -- \`scanner.next()\` lê uma palavra, ou seja, texto até ao próximo espaço. Para números continuas a usar \`scanner.nextInt()\`. -- O texto fica entre aspas **duplas** \`"Ludwig"\`, um único carácter entre aspas **simples** \`'L'\`. -- Depois de \`length()\`, \`toUpperCase()\` e dos outros métodos há sempre parênteses curvos, mesmo quando não há nada lá dentro. -- Comparas dois textos com \`equals\`. Cada letra conta: \`"Ludwig".equals("ludwig")\` é false. - -::: übung l17-java-passwort -titel: Verificar a palavra-passe -code: java -auftrag: Escreve em Java um programa que pergunta pela palavra-passe. Se for **Ludwig**, mostra **Bem-vindo!**, senão **Palavra-passe errada**. - Atenção às maiúsculas e minúsculas: \`ludwig\` está errado. -test: Ludwig -> Bem-vindo! -test: ludwig -> Palavra-passe errada -test: Geißbock -> Palavra-passe errada -braucht: verzweigung -tipp: Lê a palavra-passe com \`scanner.next()\` para uma variável String. Depois comparas numa decisão. -tipp: A condição é \`entrada.equals("Ludwig")\`. Em Java, \`==\` não compara textos de forma fiável. -lösung: l17-java-passwort-loesung.visustruct -::: - -## Preenche os espaços: textos - -::: lückentext -titel: Analisar uma palavra -auftrag: Preenche os métodos e as palavras que faltam. -\`\`\`java -import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Uma palavra?"); -[[String]] palavra = scanner.next(); -System.out.println("Comprimento: " + palavra.[[length]]()); -System.out.println("Maiúsculas: " + palavra.[[toUpperCase]]()); -System.out.println("Primeira letra: " + palavra.[[charAt]](0)); -if (palavra.[[equals]]("Ludwig")) { - System.out.println("Olá Ludwig!"); -} -\`\`\` -erklärung: \`length()\` conta os carateres, \`toUpperCase()\` põe tudo em maiúsculas, \`charAt(0)\` dá o primeiro caráter. Textos comparas com \`equals\`, não com \`==\`. -::: - -## Encontra o erro: iniciais - -::: übung l17-java-fehler -titel: Corrigir as iniciais -code: java -auftrag: O programa lê o nome próprio e o apelido e deve mostrar as iniciais. Para Ludwig Geißbock é **L. G.** Mas mostra **u. e.** - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l17-java-fehler-start.visustruct -test: Ludwig, Geißbock -> L. G. -test: Mia, Meyer -> M. M. -test: Ada, Lovelace -> A. L. -braucht: anweisung -tipp: Em que índice começa o Java a contar? -tipp: O primeiro caráter tem o índice 0: \`nome.charAt(0)\` e \`apelido.charAt(0)\`. -lösung: l17-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l17-java-laeuft-nicht -titel: Contar as letras -code: java -auftrag: O programa deve mostrar a primeira letra e o comprimento de **Ludwig**, ou seja, **L tem 6 letras**. Mas não corre, porque há dois métodos mal escritos. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l17-java-laeuft-nicht-start.visustruct -test: - -> L tem 6 letras -braucht: anweisung -tipp: Os nomes dos métodos também são camelCase: começam com minúscula e cada palavra seguinte começa com maiúscula. -tipp: O correto é \`nome.charAt(0)\` com A maiúsculo e \`nome.length()\` todo em minúsculas. -lösung: l17-java-laeuft-nicht-loesung.visustruct -::: - -## Ainda mais métodos - -Uma String sabe fazer muito mais. Também vais precisar muitas vezes destes métodos: - -| Método | Significado | Exemplo | Resultado | -|---|---|---|---| -| \`lastIndexOf("a")\` | Onde está o último a? | \`"Passatempo".lastIndexOf("a")\` | 4 | -| \`isEmpty()\` | O texto está vazio? | \`"".isEmpty()\` | true | -| \`equalsIgnoreCase(…)\` | o mesmo texto, sem contar maiúsculas e minúsculas? | \`"Passatempo".equalsIgnoreCase("PASSATEMPO")\` | true | -| \`replace(antigo, novo)\` | substitui todas as ocorrências | \`"Passatempo".replace("tempo", "porte")\` | Passaporte | -| \`repeat(n)\` | o texto n vezes seguidas | \`"Mé".repeat(3)\` | MéMéMé | -| \`concat(…)\` | junta texto no fim, como \`+\` | \`"Passa".concat("tempo")\` | Passatempo | -| \`compareTo(…)\` | Qual vem primeiro no alfabeto? | \`"Anna".compareTo("Ben")\` | -1 | - -\`compareTo\` devolve um número: **negativo** se o texto vem antes no alfabeto, **0** se os dois são iguais e **positivo** se vem depois. Assim podes ordenar nomes. - -Dois métodos não pertencem a um texto, mas a uma classe. Por isso, à frente fica o nome da classe: - -- \`Integer.parseInt("7")\` transforma o texto "7" no número 7. Com ele já podes fazer contas. -- \`String.valueOf(7)\` transforma o número 7 no texto "7". - -::: diagramm zk-mehr.visustruct -Todos os métodos novos num só exemplo. Percorre-o na simulação e compara cada saída com a tabela. -::: - -## Texto de um ficheiro - -Os programas recebem muitas vezes o texto de um **ficheiro**. A Princesa Zora escreveu uma carta ao Ludwig. Ela está pronta no ficheiro \`carta.txt\`: - -::: datei carta.txt -Querido Ludwig, -a raposa fechou-me na sua torre. -A porta tem um cadeado com uma palavra secreta. -Só encontras a palavra com Java. -Ajuda-me, por favor! -A tua Princesa Zora -::: - -Estas linhas leem o ficheiro inteiro para uma String: - -\`\`\` -import java.nio.file.*; - -String carta = Files.readString(Path.of("carta.txt")); -\`\`\` - -Como a leitura funciona ao certo, vais aprender mais tarde, numa lição só sobre ficheiros. Por agora basta: depois disto, a carta inteira está em \`carta\`, com todas as linhas. - -No fim de cada linha há um carácter invisível para a **mudança de linha**. Em Java escreve-se \`"\\n"\`. Por isso, \`carta.split("\\n")\` divide a carta nas suas linhas. - -::: übung l17-java-brief -titel: Onde está a raposa? -code: java -auftrag: O código inicial já lê a carta. Divide-a em linhas e mostra **Linhas:** e o número de linhas. Depois procura a linha onde está **raposa** e mostra **Raposa na linha** e o seu número. A primeira linha tem o número 1. -start: l17-java-brief-start.visustruct -test: - -> Linhas: 6 | Raposa na linha 2 -braucht: forSchleife, verzweigung -tipp: \`String[] linhas = carta.split("\\n");\` divide a carta. Depois, um ciclo for percorre todas as linhas. -tipp: No ciclo verificas \`linhas[i].contains("raposa")\`. O índice começa em 0, por isso mostras \`(i + 1)\`. Os parênteses são precisos para o Java fazer a conta em vez de juntar 1 ao texto. -lösung: l17-java-brief-loesung.visustruct -::: - -## A API do Java - -Ninguém sabe todos os métodos de cor, nem os profissionais. Eles consultam a **API do Java**. API significa Application Programming Interface. É a descrição de tudo o que o Java já traz consigo. - -Todos os métodos de String estão aqui: [String na API do Java](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/String.html) - -Desce até à tabela **Method Summary**. Cada linha descreve um método: - -| Coluna | Exemplo | Significado | -|---|---|---| -| Modifier and Type | \`int\` | O que o método devolve | -| Method | \`lastIndexOf(String str)\` | Nome e parâmetros com o seu tipo de dados | -| Description | Returns the index within this string of the last occurrence … | O que o método faz | - -A API está em inglês. O teu browser pode traduzir a página. Os nomes dos métodos ficam em inglês, porque é assim que os escreves em Java. - -::: ludwig -A página é muito comprida. Com Ctrl+F (no Mac ⌘+F) procuras o nome do método e chegas lá logo. -::: - -Consulta estes métodos. Não estão nesta lição: - -::: quiz -? Consulta \`strip()\`. O que faz o método? -- [ ] Remove todos os espaços do texto. -- [x] Remove os espaços no início e no fim. -- [ ] Corta o último carácter. -> Como \`trim()\`. Mas \`strip()\` também conhece espaços especiais de outras escritas. - -? Consulta \`isBlank()\`. O que devolve \`" ".isBlank()\`? -- [x] true -- [ ] false -- [ ] 3 -> isBlank verifica se o texto está vazio ou só tem espaços. Já \`" ".isEmpty()\` seria false. - -? Consulta \`toCharArray()\`. Que tipo de dados devolve o método? -- [ ] String -- [ ] String[] -- [x] char[] -> Um array com todos os caracteres do texto, uma posição para cada carácter. - -? Consulta \`indexOf\` com dois parâmetros. O que devolve \`"Passatempo".indexOf("a", 2)\`? -- [ ] 1 -- [x] 4 -- [ ] -1 -> A pesquisa só começa no índice 2. O primeiro a a partir daí está no índice 4. - -? Consulta \`startsWith\`. Que tipo de dados devolve o método? -- [x] boolean -- [ ] int -- [ ] String -> Na coluna “Modifier and Type” está boolean: a resposta é true ou false. -::: - -## Jogo: Salvar a princesa - -Agora ajudas o Ludwig. A raposa fechou a princesa Zora na sua torre, e o portão só abre com uma palavra secreta. - -::: spiel prinzessin -titel: Jogo: Salvar a princesa -intro: No caminho para a torre, o Ludwig passa por **seis estações**. Em cada uma há um pedaço de código Java, e sai um número. Com \`alphabet.charAt(numero)\` o número transforma-se numa letra, que vai para as **palavras cruzadas**. Resolve-as, e os quadrados amarelos formam a palavra secreta para o portão. - -station: No fosso - String nome = " Zora "; - int numero = nome.trim().length(); -frage: Que número fica em \`numero\`? -zahl: 4 -feld: 5 1 -tipp: \`trim()\` corta os espaços no início e no fim. Depois conta os caracteres. -> Depois do \`trim()\` fica só \`"Zora"\`, que tem 4 caracteres. - -station: Na floresta da raposa - String lugar = "A raposa"; - int numero = lugar.length(); -frage: Que número fica em \`numero\`? -zahl: 8 -feld: 4 4 -tipp: \`length()\` conta todos os caracteres, também o espaço. -> O A, o espaço e r-a-p-o-s-a dão 8 caracteres. - -station: Na rocha das cabras - String texto = "Ludwig o bode"; - int numero = texto.indexOf(" "); -frage: Que número fica em \`numero\`? -zahl: 6 -feld: 4 6 -tipp: \`indexOf\` devolve a posição do primeiro espaço. O L tem o índice 0. -> L u d w i g tem os índices 0 a 5, por isso o primeiro espaço está no índice 6. - -station: Na escada da torre - String palavra = "Princesa"; - int numero = palavra.indexOf("a", 5); -frage: Que número fica em \`numero\`? Procura \`indexOf(String str, int fromIndex)\` na API do Java. -zahl: 7 -feld: 6 6 -tipp: A pesquisa começa no índice 5. Escreve os índices por baixo das letras. -> P r i n c e s a tem os índices 0 a 7. A partir do índice 5, o primeiro “a” está no 7. - -station: Diante da toca da raposa - int numero = "Zora".compareTo("Zoe"); -frage: Que número fica em \`numero\`? -zahl: 13 -feld: 1 9 -tipp: \`compareTo\` procura a primeira posição em que os textos são diferentes e subtrai os códigos dos caracteres. \`'r'\` é 114, \`'e'\` é 101. -> “Zo” é igual, depois vem \`'r'\` contra \`'e'\`: 114 − 101 = 13. - -station: No portão da torre - int numero = Integer.parseInt("1" + "1") / 5; -frage: Que número fica em \`numero\`? -zahl: 2 -feld: 6 5 -tipp: Primeiro os textos são juntados, depois passam a número. Com \`int\`, a divisão deita fora o resto. -> \`"1" + "1"\` é \`"11"\`, o \`parseInt\` faz disso 11, e 11 / 5 dá 2 com números inteiros. - -kreuzwort: As letras azuis já as tens. Preenche os outros quadrados com termos do Java. Clica numa pista para saltar para a palavra. -- → 1 3 BOOLEAN: Tipo de dados para verdadeiro ou falso -- → 4 1 STRING: Tipo de dados para texto -- → 6 5 CHAR: Tipo de dados para um único carácter -- ↓ 1 6 LENGTH: Método que conta os caracteres de uma string -- ↓ 2 1 ELSE: A parte “senão” de uma decisão -- ↓ 3 3 BREAK: Sai imediatamente de um ciclo -lösung: 1 3, 4 3, 2 6, 6 7, 7 3 - -tor: O Ludwig está diante do portão. A fechadura compara a tua palavra com \`equals\`. Escreve a palavra secreta. - String palavra = "___"; - if (palavra.equals(palavraSecreta)) { - portao.abrir(); - } -ende: A Zora está livre, e a raposa fugiu a correr. A palavra secreta era \`BREAK\`: com \`break\` sai-se de qualquer ciclo, e a Zora sai da torre. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar que os caracteres de uma String também se contam a partir de 0. → Um texto é uma fila de caracteres -- Consigo trabalhar com \`length()\` e \`charAt(i)\`. → Um texto é uma fila de caracteres -- Consigo distinguir um \`char\` de uma String e usar o código de caractere com \`(int)\` e \`(char)\`. → Um único caractere: char -- Consigo recortar uma parte de um texto com \`substring\`. → Recortar partes -- Consigo usar \`indexOf\`, \`contains\`, \`toUpperCase\` e \`equals\`. → Pesquisar e comparar -- Consigo percorrer um texto caractere a caractere com um ciclo. → Caractere a caractere -- Consigo dividir um texto num array de Strings com \`split\`. → Dividir com split -- Consigo ler um texto em Java e compará-lo com \`equals\`. → Agora em Java -- Consigo usar mais métodos, como \`lastIndexOf\`, \`replace\`, \`compareTo\` e \`Integer.parseInt\`. → Ainda mais métodos -- Consigo dividir o texto de um ficheiro em linhas com \`split("\\n")\`. → Texto de um ficheiro -- Consigo consultar um método na API do Java e ler a sua descrição. → A API do Java -::: - -## Teste de conhecimentos - -::: quiz -? Quanto dá \`"Ludwig".charAt(1)\`? -→ Um texto é uma fila de caracteres -- [ ] L -- [x] u -- [ ] d -> Começa-se a contar em 0: L tem o índice 0, u o índice 1. - -? O que é \`'C'\`? -→ Um único caractere: char -- [x] um \`char\`, ou seja, exatamente um caractere -- [ ] uma String -- [ ] uma variável chamada C -> Aspas simples indicam um único caractere, um \`char\`. \`"C"\` entre aspas duplas seria uma String. - -? \`'A'\` tem o código de caractere 65. Quanto dá \`(char) 67\`? -→ Um único caractere: char -- [ ] 'B' -- [x] 'C' -- [ ] 67 -> 65 é A, 66 é B, 67 é C. Com \`(char)\` o código volta a ser um caractere. - -? Quanto dá \`"Estrutura".substring(0, 6)\`? -→ Recortar partes -- [x] Estrut -- [ ] Estrutu -- [ ] strutu -> Os caracteres com os índices 0 a 5. O fim 6 não está incluído. - -? Uma palavra tem 7 caracteres. Que índice tem o último caractere? -→ Um texto é uma fila de caracteres -- [ ] 7 -- [x] 6 -- [ ] 8 -> Tal como num array: sempre um a menos do que o comprimento. - -? Como comparas duas Strings quanto ao conteúdo em Java? -→ Pesquisar e comparar -- [ ] a == b -- [x] a.equals(b) -- [ ] a = b - -? Associa cada método ao que dá para \`"Passatempo"\`. -→ Pesquisar e comparar -= length() -> 10 -= charAt(0) -> P -= indexOf("tempo") -> 5 -= substring(5) -> tempo - -? Que condição verifica em Java se \`entrada\` contém o texto Ludwig? -→ Agora em Java -- [ ] entrada = "Ludwig" -- [ ] entrada == 'Ludwig' -- [x] entrada.equals("Ludwig") -> Comparas textos com equals. As aspas simples são só para um único carácter. - -? O que devolve \`Integer.parseInt("12") + 1\`? -→ Ainda mais métodos -- [ ] "121" -- [x] 13 -- [ ] uma mensagem de erro -> parseInt transforma o texto num número. Depois o Java faz a conta normalmente. -::: -`,Ly=`--- -titel: Chamada e interrupção -bereich: Fundamentos -kurz: Dividir tarefas grandes em subprogramas e sair mais cedo dos ciclos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dividir uma tarefa grande em problemas mais pequenos -- inserir uma chamada e construir um subprograma num separador próprio -- passar valores a um subprograma com parâmetros -- devolver um valor de retorno com return e guardá-lo ao chamar -- explicar porque é que um subprograma não consegue alterar um número, mas consegue alterar um array -- explicar o que faz uma Interrupção e porque está numa decisão -- terminar um ciclo com uma Interrupção ou através da sua condição -- reconhecer um método em Java e sair de um ciclo com \`break\` -::: - -## Dividir tarefas grandes - -Os estrutogramas compridos ficam depressa confusos. Por isso divides uma tarefa grande em **problemas mais pequenos**. Cada problema mais pequeno tem um nome próprio e um estrutograma próprio, um **subprograma**. - -Um quiz, por exemplo, tem três partes: dar as boas-vindas, fazer as perguntas, mostrar o resultado. O programa principal só nomeia estas três partes: - -::: diagramm l15-hauptprogramm.visustruct -O programa principal é feito só de três chamadas. O que acontece em cada parte está num estrutograma próprio. -::: - -Assim vês num relance como o programa está construído. Os pormenores estão noutro sítio. - -## A chamada - -Uma **chamada** é um bloco com linhas verticais duplas à esquerda e à direita. Quer dizer: executa aqui o subprograma com este nome e depois continua. - -- Arrastas a **Chamada** da área Elementos e escreves lá o nome, p. ex. \`mostrarBoasVindas()\`. -- Os parênteses curvos no fim mostram que é um subprograma. É assim que também se escreve em Java, Python e JavaScript. -- O subprograma em si constróis como estrutograma próprio num novo separador. Como título escreves o mesmo nome. - -::: diagramm l15-unterprogramm.visustruct -O subprograma mostrarBoasVindas() como estrutograma próprio. -::: - -> **Na simulação:** Quando a simulação chega a uma chamada, procura entre os separadores abertos o estrutograma com este título e executa-o. Depois continua no programa principal. Enquanto o subprograma é executado, o bloco da chamada fica destacado. Um subprograma como este, só com um nome como título, partilha as suas variáveis com o programa principal. Daqui a pouco vais aprender como um subprograma tem variáveis próprias. Se não houver nenhum separador que corresponda, a simulação salta a chamada e mostra um aviso. - -::: angeberwissen -Porque é que o teu professor diz sempre “método” em Java e nunca “função” ou “procedimento”? Porque em Java cada subprograma tem de pertencer a uma classe, e os subprogramas de uma classe chamam-se métodos. Mas a diferença continua a existir: se um método devolve um resultado, na verdade é uma função. Se tiver \`void\` à frente, não devolve nada e na verdade é um procedimento. O Java simplifica e chama métodos aos dois. Eu também: o meu método chama-se Ruminar. -::: - -## Exercício: Acrescentar uma chamada - -::: übung l15-titel -titel: Cumprimento com chamada -auftrag: Imagina que o subprograma \`mostrarTitulo()\` já existe. Constrói o programa principal: primeiro chama \`mostrarTitulo()\`. Depois pede um nome e mostra **Olá** e o nome. - Ou seja, quando se escreve Ada, aparece **Olá Ada**. -test: Ada -> Olá Ada -test: Rui -> Olá Rui -braucht: aufruf -tipp: O programa principal tem três partes: a chamada, a entrada e a saída. -tipp: Arrasta uma Chamada da área Elementos lá para cima e escreve \`mostrarTitulo()\`. Por baixo vêm a entrada \`input: String nome "Como te chamas?"\` e a saída. -lösung: l15-titel-loesung.visustruct -::: - -## Passar valores: parâmetros - -Muitas vezes um subprograma deve trabalhar com valores que só se conhecem quando é chamado. Nesse caso tem uma **linha de cabeçalho** como título: - -\`\`\` -void mostrarSoma(int a, int b) -\`\`\` - -- \`void\` quer dizer: o subprograma não devolve nenhum resultado. -- \`mostrarSoma\` é o nome. -- Entre os parênteses estão os **parâmetros**: variáveis com tipo de dados que recebem os seus valores quando o subprograma é chamado. Vários separam-se com vírgulas. - -::: diagramm l15-zeige-summe.visustruct -O subprograma com a linha de cabeçalho como título. Faz contas com a e b como se fossem variáveis normais. -::: - -No bloco da chamada escreves os valores entre os parênteses, p. ex. \`mostrarSoma(3, 4)\`. O primeiro valor vai para \`a\`, o segundo para \`b\`. Em vez de números também podes usar variáveis e contas, como \`mostrarSoma(x, x + 1)\`. - -::: diagramm l15-zeige-summe-haupt.visustruct -O programa principal chama mostrarSoma duas vezes com valores diferentes. Abre os dois diagramas e inicia aqui a simulação. -::: - -> **Variáveis locais:** Os parâmetros e todas as variáveis criadas no subprograma pertencem só ao subprograma. Ele recebe **cópias** dos valores; mais sobre isso em baixo, em passagem por valor. Durante a chamada, a simulação só mostra as variáveis do subprograma. - -## Devolver um resultado: return - -Um subprograma também pode devolver um resultado, o **valor de retorno**. Nesse caso, a linha de cabeçalho tem o tipo de dados do resultado em vez de \`void\`: - -\`\`\` -int quadrado(int x) -\`\`\` - -A instrução \`return resultado\` devolve o valor e termina logo o subprograma. Quando o chamas, guardas o resultado numa variável: depois de \`int q = quadrado(5)\`, \`q\` tem o valor 25. - -::: diagramm l15-quadrat.visustruct -O subprograma quadrado calcula e devolve o resultado com return. -::: - -::: diagramm l15-quadrat-haupt.visustruct -O programa principal guarda o valor de retorno em q. Abre os dois diagramas e experimenta a simulação. -::: - -> Um subprograma com valor de retorno tem de terminar com \`return\`. Se faltar o return, a simulação indica um erro. - -Na **vista de código**, a linha de cabeçalho passa a ser um método Java (\`public static int quadrado(int x)\`), um \`def\` em Python e uma \`function\` em JavaScript. Se os subprogramas estiverem abertos noutros separadores, aparecem no código juntamente com o programa principal. - -## Exercício: Calcular a área - -::: übung l15-flaeche -titel: Calcular a área -auftrag: Constrói dois diagramas. O subprograma \`int area(int a, int b)\` devolve a área de um retângulo, ou seja, a vezes b. O programa principal pede o comprimento e a largura, chama \`area\` e mostra **Área:** e o resultado. - Com 3 e 4 aparece **Área: 12**. -test: 3, 4 -> Área: 12 -test: 5, 5 -> Área: 25 -test: 7, 0 -> Área: 0 -braucht: aufruf -tipp: Constróis dois diagramas: um subprograma que só calcula e devolve o resultado, e um programa principal para a entrada e a saída. -tipp: Abre um novo separador com **+** e dá-lhe o título \`int area(int a, int b)\`. Lá dentro basta uma instrução: \`return a * b\`. No programa principal escreves \`int f = area(comprimento, largura)\` no bloco da chamada. -lösung: l15-flaeche-loesung.visustruct -unterprogramm: l15-flaeche-unterprogramm-loesung.visustruct -::: - -## Cópia ou original: passagem por valor e passagem por referência - -O que acontece se um subprograma alterar o seu parâmetro? Depende do **que** é passado. - -**Passagem por valor:** Com \`int\`, \`double\`, \`boolean\`, \`char\` e \`String\`, o subprograma recebe uma **cópia** do valor. Se alterar o seu parâmetro, a variável de quem chamou fica como estava. - -**Passagem por referência:** Com um **array**, o subprograma não recebe uma cópia, mas uma referência para o mesmo array. Se alterar uma posição, a posição também fica alterada para quem chamou. A linha de cabeçalho para isso fica assim: - -\`\`\` -void limpar(boolean[] lugares) -\`\`\` - -::: grafik uebergabe.svg -Em cima, duplicar recebe uma cópia de numero. Em baixo, fila e lugares apontam para o mesmo array. -::: - -Experimenta as duas. Abre os três diagramas e inicia a simulação no programa principal: - -::: diagramm uebergabe-verdopple.visustruct -duplicar só altera a sua cópia x. -::: - -::: diagramm uebergabe-leeren.visustruct -limpar põe todos os lugares a false, por exemplo quando uma sessão é cancelada. -::: - -::: diagramm uebergabe-haupt.visustruct -numero continua a ser 5 depois da chamada. Já os lugares em fila ficam livres depois da chamada. -::: - -> Se quiseres receber um número de volta de um subprograma, usa \`return\`, p. ex. \`numero = duplicar(numero)\` com a linha de cabeçalho \`int duplicar(int x)\`. Um array, pelo contrário, pode ser alterado diretamente pelo subprograma. - -::: angeberwissen -Em rigor, o Java só conhece a passagem por valor. Mas, com um array, não é copiado o array inteiro, só o seu **endereço** na memória. Com o endereço copiado, o subprograma encontra o mesmo array. É como se eu te desse uma cópia da chave do meu cacifo: a chave é copiada, mas o feno no cacifo só existe uma vez. -::: - -## Exercício: Pontos de bónus - -::: übung uebergabe-bonus -titel: Pontos de bónus -auftrag: O programa principal chama \`bonus(pontos)\` e depois mostra todos os pontos. Constrói o subprograma \`void bonus(int[] pontos)\`, que aumenta em 1 cada pontuação do array. Em vez de 3, 5 e 8 vês então **4**, **6** e **9**. -start: uebergabe-bonus-start.visustruct -test: - -> 4 | 6 | 9 -braucht: aufruf -tipp: O programa principal já está pronto. Só constróis o subprograma num novo separador. -tipp: Abre um novo separador com **+** e o título \`void bonus(int[] pontos)\`. Lá dentro, um ciclo for percorre todas as posições e calcula \`pontos[i] = pontos[i] + 1\`. Não precisas de return, porque o array é alterado diretamente. -lösung: uebergabe-bonus-loesung.visustruct -unterprogramm: uebergabe-bonus-unterprogramm-loesung.visustruct -::: - -## A interrupção - -Às vezes um ciclo deve terminar **mais cedo**, a meio do seu corpo. É para isso que serve a **Interrupção**. Ela sai imediatamente do ciclo em que está. O programa continua no primeiro bloco depois do ciclo. Em Java, Python e JavaScript chama-se \`break\`. - -Reconheces a Interrupção pela ponta no seu lado esquerdo. Podes escrever um pequeno comentário no bloco, p. ex. \`Sair do ciclo\`. Ele aparece no código como comentário depois de \`break\`. - -Uma Interrupção está quase sempre numa **decisão**: só se sai do ciclo se uma condição se verificar. - -Um caso típico é o **ciclo infinito com Interrupção**. O ciclo em si não tem condição. É executado até se chegar à Interrupção: - -::: diagramm l15-endlos-aussprung.visustruct -O ciclo pede sempre um número. Com 0 salta para fora, senão soma o número à soma. -::: - -Sem Interrupção, um ciclo infinito nunca acabaria mesmo. - -## Interrupção na simulação - -A simulação executa a Interrupção como \`break\`: sai do ciclo mais interior e continua depois dele. Se uma Interrupção estiver fora de qualquer ciclo, o subprograma termina; no programa principal termina o programa todo. O \`return\` faz o mesmo. - -Muitas vezes podes passar completamente sem Interrupção: escreves a condição de paragem diretamente no ciclo. Normalmente até fica mais claro. - -## Exercício: Somar até 0 - -::: übung l15-bis-null -titel: Somar até 0 -auftrag: Reconstrói o programa “Somar até 0” **sem** Interrupção. Ele pede números inteiros até alguém escrever 0. Depois mostra **Soma:** e a soma de todos os números introduzidos. -test: 3, 4, 0 -> Soma: 7 -test: 0 -> Soma: 0 -test: 10, -2, 5, 0 -> Soma: 13 -tipp: Sem Interrupção, é a própria condição do ciclo que tem de verificar se foi escrito 0. -tipp: Pede o primeiro número antes do ciclo. O ciclo while é executado com a condição \`numero != 0\`. No corpo somas o número à soma e depois pedes o número seguinte. Na segunda entrada só escreves \`input: numero "…"\`, porque \`numero\` já existe. -lösung: l15-bis-null-loesung.visustruct -::: - -## Agora em Java - -Em Java, um subprograma chama-se **método**. O quadrado de cima fica assim: - -\`\`\`java -public static int quadrado(int x) { - int resultado = x * x; - return resultado; -} -\`\`\` - -O programa principal chama o método pelo nome e passa-lhe o valor entre parênteses curvos: - -\`\`\`java -int q = quadrado(numero); -System.out.println("O quadrado é " + q); -\`\`\` - -- No cabeçalho, antes do nome, fica o que o método devolve (\`int\`). Se não devolver nada, fica \`void\`. -- Os parâmetros ficam entre parênteses curvos, com o tipo de dados: \`(int x)\`. Separas vários com vírgulas. -- Na área Código escreves cada método no seu próprio separador, tal como cada subprograma tem o seu próprio estrutograma. -- Em Java, a saída chama-se \`break\`. Com \`while (true)\` constróis um ciclo que só termina com \`break\`. - -::: übung l18-java-kasse -titel: Caixa com break -code: java -auftrag: Escreve uma caixa registadora em Java. Pergunta repetidamente por um preço em euros inteiros. Com 0 sai do ciclo com \`break\`. Depois mostra quantos artigos foram e quanto custam ao todo. - Com 3, 4 e depois 0 aparece **Artigos: 2** e **Total: 7**. -test: 3, 4, 0 -> Artigos: 2 | Total: 7 -test: 0 -> Artigos: 0 | Total: 0 -test: 10, 2, 5, 0 -> Artigos: 3 | Total: 17 -braucht: endlosschleife, aussprung -tipp: Precisas de duas variáveis que começam em 0 antes do ciclo: uma para a soma e outra para a quantidade de artigos. -tipp: Dentro de \`while (true) { … }\` lês o preço. Logo a seguir verificas \`if (preco == 0) { break; }\`. Só depois somas o preço e aumentas os artigos em 1. -lösung: l18-java-kasse-loesung.visustruct -::: - -## Preenche os espaços: métodos e break - -::: lückentext -titel: Método e saída -auftrag: Preenche as palavras que faltam. Em cima está o método que calcula um quadrado. Por baixo está o programa principal, que o chama. Na área Código, cada um fica no seu separador. -\`\`\`java -public static [[int]] quadrado(int x) { - [[return]] x * x; -} -\`\`\` -\`\`\`java -int numero = 1; -while ([[true]]) { - int q = [[quadrado]](numero); - if (q > 50) { - [[break]]; - } - System.out.println(q); - numero = numero + 1; -} -\`\`\` -erklärung: \`int\` antes do nome do método quer dizer que o método devolve um número inteiro, com \`return\`. Se um método não devolver nada, aparece \`void\`. \`while (true)\` repete-se sem fim até \`break\` sair do ciclo. Saída: 1, 4, 9, 16, 25, 36 e 49. -::: - -## Encontra o erro: adivinhar - -::: übung l18-java-fehler -titel: Corrigir a contagem de tentativas -code: java -auftrag: Neste jogo de adivinhar, o número secreto é 7. O programa pede um palpite até aparecer o 7 e depois mostra o número de tentativas. Com 3, 5 e 7 foram 3 tentativas, mas o programa diz **Tentativas: 2**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l18-java-fehler-start.visustruct -test: 3, 5, 7 -> Tentativas: 3 -test: 7 -> Tentativas: 1 -test: 1, 2, 3, 7 -> Tentativas: 4 -braucht: endlosschleife, aussprung -tipp: O que acontece com o palpite certo? Ainda é contado antes de \`break\` sair do ciclo? -tipp: Primeiro conta, depois verifica: \`tentativas = tentativas + 1;\` fica antes do \`if\` com o \`break\`. -lösung: l18-java-fehler-loesung.visustruct -::: - -## Jogo: O que é devolvido? - -::: spiel frage -titel: Jogo: O que é devolvido? -intro: O Ludwig chama métodos e sai de ciclos a meio. Diz antes o que aparece. Sete rondas. - -runde: - public static int quadrado(int x) { - return x * x; - } - - int q = quadrado(5); - System.out.println(q); -frage: O que aparece? -- 5 -- 10 -- [x] 25 -> A chamada põe 5 em x, e o return devolve 5 · 5. - -runde: - public static void mostrarSoma(int a, int b) { - System.out.println(a + b); - } - - int x = 2; - mostrarSoma(x, x + 1); -frage: O que aparece? -- [x] 5 -- 23 -- 3 -> O primeiro valor 2 vai para a, o segundo valor 3 para b. Em Java, \`a + b\` calcula com números normalmente. - -runde: -frage: O que devolve um método que tem \`void\` antes do nome? -- um número inteiro -- [x] nada -- sempre 0 -> \`void\` quer dizer: sem valor de retorno. Com \`int\` nesse lugar, é devolvido um número inteiro. - -runde: - public static void duplicar(int x) { - x = x * 2; - } - - int numero = 5; - duplicar(numero); - System.out.println(numero); -frage: O que aparece? -- [x] 5 -- 10 -- 0 -> Passagem por valor (call by value): duplicar recebe uma cópia. numero continua a ser 5. - -runde: - public static void limpar(boolean[] lugares) { - for (int i = 0; i < lugares.length; i++) { - lugares[i] = false; - } - } - - boolean[] fila = {true, true}; - limpar(fila); - System.out.println(fila[0]); -frage: O que aparece? -- true -- [x] false -> Num array, fila e lugares apontam para o mesmo array. Quem chama também vê a alteração. - -runde: - int soma = 0; - for (int i = 1; i <= 10; i++) { - if (i == 4) { - break; - } - soma = soma + i; - } - System.out.println(soma); -frage: O que aparece? -- [x] 6 -- 10 -- 55 -> Com i = 4, o \`break\` sai logo do ciclo. Só foram somados 1 + 2 + 3. - -runde: - int numero = 1; - while (true) { - numero = numero * 3; - if (numero > 20) { - break; - } - } - System.out.println(numero); -frage: O que aparece? -- 9 -- 20 -- [x] 27 -> 3, 9, 27. Só 27 é maior do que 20, e então o ciclo infinito termina com break. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dividir uma tarefa grande em problemas mais pequenos. → Dividir tarefas grandes -- Consigo inserir uma chamada e construir um subprograma num separador próprio. → A chamada -- Consigo passar valores a um subprograma com parâmetros. → Passar valores: parâmetros -- Consigo devolver um valor de retorno com return e guardá-lo ao chamar. → Devolver um resultado: return -- Consigo explicar porque é que um subprograma não consegue alterar um número, mas consegue alterar um array. → Cópia ou original: passagem por valor e passagem por referência -- Consigo explicar o que faz uma Interrupção e porque está numa decisão. → A interrupção -- Consigo terminar um ciclo com uma Interrupção ou através da sua condição. → Interrupção na simulação -- Consigo reconhecer um método em Java e sair de um ciclo com \`break\`. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? O que significa um bloco de chamada com o texto \`mostrarResultado()\`? -→ A chamada -- [ ] O programa termina aqui. -- [x] O subprograma mostrarResultado() é executado aqui, e depois o programa continua. -- [ ] A palavra mostrarResultado() é mostrada aqui. - -? Onde continua o programa depois de uma Interrupção? -→ A interrupção -- [ ] No início do ciclo -- [x] No primeiro bloco depois do ciclo -- [ ] Logo no início do programa -> A Interrupção sai do ciclo em que está. Em Java, Python e JavaScript chama-se break. - -? Porque é que uma Interrupção costuma estar numa decisão? -→ A interrupção -- [x] Para só se sair do ciclo se uma condição se verificar -- [ ] Porque senão uma Interrupção não pode ser guardada -- [ ] Porque uma decisão é ela própria um ciclo -> Sem decisão, sair-se-ia do ciclo logo na primeira iteração. - -? Onde encontra a simulação o subprograma de uma chamada \`mostrarResultado()\`? -→ A chamada -- [ ] Tem de estar no mesmo estrutograma. -- [x] Num separador aberto com o título mostrarResultado() -- [ ] A simulação não consegue executar subprogramas. -> Constrói cada subprograma num separador próprio e dá-lhe como título o nome da chamada. - -? O subprograma tem a linha de cabeçalho \`void mostrarSoma(int a, int b)\`. Que chamada serve? -→ Passar valores: parâmetros -- [ ] mostrarSoma() -- [x] mostrarSoma(3, 4) -- [ ] int s = mostrarSoma(3, 4) -> Dois parâmetros precisam de dois valores. E \`void\` quer dizer que não volta nada que pudesses guardar. - -? Depois de \`int q = quadrado(5)\`, q tem 25. O que está no fim do subprograma quadrado? -→ Devolver um resultado: return -- [ ] output: x * x -- [x] return x * x -- [ ] q = x * x -> Com return, o subprograma devolve o valor à chamada. Uma saída só o mostraria. - -? \`void duplicar(int x)\` calcula \`x = x * 2\`. O programa principal tem \`int numero = 5\` e depois a chamada \`duplicar(numero)\`. Que valor tem numero a seguir? -→ Cópia ou original: passagem por valor e passagem por referência -- [x] 5 -- [ ] 10 -- [ ] 0 -> Passagem por valor: duplicar só alterou uma cópia de numero. - -? \`void limpar(boolean[] lugares)\` põe todas as posições a false. O que é verdade para o array fila no programa principal depois de \`limpar(fila)\`? -→ Cópia ou original: passagem por valor e passagem por referência -- [ ] Não mudou, porque o subprograma recebe uma cópia. -- [x] Todas as posições são false, porque lugares e fila são o mesmo array. -- [ ] O array é apagado. -> Passagem por referência: um array é passado como referência. Quem chamou também vê as alterações nas posições. - -? Como se chama um subprograma em Java? -→ Agora em Java -- [ ] Classe -- [x] Método -- [ ] Ciclo -> Em Java, um subprograma é um método, por exemplo o método \`main\`. Consoante o tipo, também se chama procedimento ou função. - -? Muitos blocos têm um segundo nome nos manuais. Associa cada termo do VisuStruct ao seu outro nome. -→ Agora em Java -= Decisão -> Instrução condicional -= Seleção -> Seleção múltipla -= Ciclo -> Iteração -= Subprograma -> Método -> Os dois nomes querem dizer o mesmo. Se conheces um deles, também percebes o outro. - -? O que fica no cabeçalho de um método Java que não devolve nada? -→ Devolver um resultado: return -- [ ] int -- [x] void -- [ ] return -> void significa “vazio”: o método faz alguma coisa, mas não devolve nenhum valor. -::: -`,Wy=`--- -titel: Pesquisar e ordenar -bereich: Fundamentos -kurz: Perceber com cartas de jogar como funcionam a pesquisa linear, a pesquisa binária e o bubble sort, e porque é que alguns métodos são muito mais rápidos. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar como a pesquisa linear percorre um array -- construir uma pesquisa linear como estrutograma -- explicar porque é que a pesquisa binária só funciona com valores ordenados -- explicar porque é que a pesquisa binária é muito mais rápida com muitos valores -- mostrar uma ronda de bubble sort com cartas de jogar -- percorrer um array em Java e contar resultados -::: - -## Procurar uma carta - -À tua frente está uma fila de cartas de jogar viradas para baixo. Procuras o 9. O que fazes? Viras uma carta a seguir à outra até encontrares o 9 ou teres virado todas as cartas. - -::: grafik ss-lineare-suche.svg -As cartas estão baralhadas. Cada carta que viras é uma comparação: é esta o 9? -::: - -É exatamente assim que funciona a **pesquisa linear**: percorre o array do índice 0 até ao fim e compara cada posição com o valor procurado. Funciona sempre, mesmo que os valores estejam baralhados. - -No array, as nossas cartas são números. Deixamos de fora o valete, a dama e o rei. - -::: diagramm ss-lineare-suche.visustruct -O ciclo é executado até a carta ser encontrada ou já não haver posições. A variável comparacoes conta quantas vezes se comparou. Na simulação, procura o 2, o 27 e o 4. -::: - -> Se a carta não estiver lá, \`posicao\` fica em **-1**. Não existe o índice -1, por isso aqui -1 quer dizer “não encontrado”. - -## Exercício: Encontrar uma carta - -::: übung ss-karte-suchen -titel: Encontrar uma carta -auftrag: Cria o array \`int[] cartas = {8, 4, 11, 4, 6}\` e pede uma carta. Mostra a posição da **primeira** carta que corresponde, p. ex. \`Posição: 1\` para o 4. Se a carta não estiver lá, mostra \`Não encontrado\`. -test: 4 -> Posição: 1 -test: 6 -> Posição: 4 -test: 8 -> Posição: 0 -test: 9 -> Não encontrado -braucht: whileSchleife, verzweigung -tipp: Percorre o array da frente para trás e compara cada carta com a que procuras. -tipp: Reconstrói o exemplo de cima. Aqui não precisas da variável comparacoes. O ciclo tem de parar assim que \`posicao\` deixar de ser -1, senão, para o 4, encontras o segundo em vez do primeiro. -lösung: ss-karte-suchen-loesung.visustruct -::: - -## Pesquisa binária: ordenado é mais rápido - -Agora as cartas estão à tua frente **ordenadas**, da mais pequena para a maior. Procuras o 27. Já não viras a primeira carta da frente, mas a do **meio**: - -- Se for a que procuras, terminaste. -- Se for mais pequena, a carta procurada só pode estar à direita. A metade esquerda inteira fica de fora. -- Se for maior, fica de fora a metade direita. - -Fazes o mesmo outra vez com o resto. Com cada comparação, o monte fica reduzido a metade. - -::: grafik ss-binaere-suche.svg -Três comparações em vez de catorze: com cada meio, metade das cartas que restam fica de fora. -::: - -No estrutograma, duas variáveis guardam a parte em que ainda se procura: \`esquerda\` e \`direita\`. O meio é \`(esquerda + direita) / 2\`. Como é uma divisão inteira, o resultado é sempre um índice inteiro. - -::: diagramm ss-binaere-suche.visustruct -O mesmo array da pesquisa linear. Na simulação, procura outra vez o 27 e compara a saída Comparações com a pesquisa linear. -::: - -> A pesquisa binária **só funciona com valores ordenados**. Com cartas baralhadas, depois de olhares para o meio não sabes em que metade continuar a procurar. - -::: angeberwissen -Penso num número de 1 a 100. Podes adivinhar, e eu só digo “maior” ou “menor”. Com a pesquisa binária precisas no máximo de **7** tentativas. Para um número até um milhão são no máximo 20, para mil milhões 30. Cada duplicação custa só mais uma tentativa. É assim que as bases de dados pesquisam milhões de registos numa fração de segundo. Eu, já agora, pesquiso de forma linear: como o prado da esquerda para a direita. -::: - -## Quão rápido é? - -A qualidade de um método mede-se pelo número de comparações de que precisa **no pior caso**. Na pesquisa linear, é o caso em que a carta está mesmo lá atrás ou não existe. - -::: grafik ss-vergleiche.svg -Com 100 cartas, a pesquisa linear precisa de até 100 comparações, a pesquisa binária no máximo de 7. -::: - -| Número de cartas | Pesquisa linear | Pesquisa binária | -|---|---|---| -| 10 | 10 | 4 | -| 100 | 100 | 7 | -| 1000 | 1000 | 10 | -| 1 000 000 | 1 000 000 | 20 | - -A pesquisa linear cresce ao mesmo ritmo que o número de cartas. Na pesquisa binária, o dobro das cartas acrescenta só **uma** comparação. - -## Ordenar com bubble sort - -Para a pesquisa binária, os valores têm de estar ordenados. Uma das formas mais simples de ordenar é o **bubble sort**. Percorres a fila da esquerda para a direita e comparas sempre **dois vizinhos**. Se a carta maior estiver à esquerda, trocas as duas. - -::: grafik ss-bubblesort.svg -Depois de uma ronda, a carta maior está na ponta da direita. -::: - -Depois da primeira ronda, a carta maior está na ponta da direita, como uma bolha de ar que sobe. Daí vem o nome (*bubble* quer dizer bolha). Depois repetes a ronda para as cartas que restam. Com 5 cartas precisas de 4 rondas. - -O estrutograma tem dois ciclos for, um dentro do outro. O exterior conta as rondas, o interior compara os vizinhos \`cartas[i]\` e \`cartas[i + 1]\`. Já sabes como trocar: precisas de uma **variável auxiliar**. - -::: diagramm ss-bubblesort.visustruct -O ciclo interior fica mais curto em cada ronda, porque as cartas maiores à direita já estão no sítio certo. Percorre-o na simulação com Passo e observa o array em Variáveis. -::: - -O bubble sort é fácil de perceber, mas lento. Com n cartas precisa de até n · (n − 1) / 2 comparações: com 10 cartas 45, com 100 cartas já 4950 e com 1000 cartas quase meio milhão. Mais tarde talvez conheças métodos mais rápidos, como o quicksort. - -## Exercício: Ordenar por ordem decrescente - -::: übung ss-absteigend -titel: Ordenar por ordem decrescente -auftrag: O diagrama inicial lê 5 cartas e ordena-as com bubble sort por ordem **crescente**, ou seja, da mais pequena para a maior. Altera-o de modo que as cartas sejam mostradas por ordem **decrescente**, a maior primeiro. -start: ss-absteigend-start.visustruct -test: 3, 1, 4, 1, 5 -> 5 | 4 | 3 | 1 | 1 -test: 10, 20, 30, 40, 50 -> 50 | 40 | 30 | 20 | 10 -test: 2, 9, 2, 7, 6 -> 9 | 7 | 6 | 2 | 2 -braucht: forSchleife, verzweigung -tipp: Olha para a condição na decisão. É ela que decide quando dois vizinhos são trocados. -tipp: Só precisas de alterar um único caractere. Quando devem ser trocados dois vizinhos, se a carta maior deve ir para a esquerda? -tipp: Na decisão, troca \`cartas[i] > cartas[i + 1]\` por \`cartas[i] < cartas[i + 1]\`. Assim os vizinhos trocam quando a carta maior está à direita, e a carta grande vai para a esquerda. -lösung: ss-absteigend-loesung.visustruct -::: - -## Agora em Java - -A pesquisa linear do exercício fica assim em Java: - -\`\`\`java editor -int[] cartas = {8, 4, 11, 4, 6}; -int procurada = 4; -int posicao = -1; -int i = 0; -while (posicao == -1 && i < cartas.length) { - if (cartas[i] == procurada) { - posicao = i; - } - i = i + 1; -} -System.out.println("Posição: " + posicao); -\`\`\` - -- \`&&\` significa “e”: o ciclo só corre enquanto não se encontrou nada **e** ainda há cartas. -- Se o ramo false estiver vazio, deixas simplesmente de fora \`else\`. -- A troca no bubble sort também precisa de uma variável auxiliar em Java: \`int aux = cartas[i];\` - -::: übung l19-java-zaehlen -titel: Contar cartas -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. Escreve em Java um programa que pergunta por uma carta e conta quantas vezes aparece. Mostra **Quantidade:** e o número. - Com 4 aparece **Quantidade: 2**. -test: 4 -> Quantidade: 2 -test: 8 -> Quantidade: 1 -test: 9 -> Quantidade: 0 -braucht: forSchleife, verzweigung -tipp: Ao contrário da pesquisa, não paras no primeiro resultado. Um ciclo for percorre todas as cartas. -tipp: \`for (int i = 0; i < cartas.length; i++)\` e lá dentro \`if (cartas[i] == procurada) { quantidade = quantidade + 1; }\`. \`quantidade\` começa em 0 antes do ciclo. -lösung: l19-java-zaehlen-loesung.visustruct -::: - -## Preenche os espaços: bubble sort - -::: lückentext -titel: Ordenar cartas -auftrag: O programa ordena as cartas por ordem crescente com o bubble sort. Preenche as palavras e os sinais que faltam. -\`\`\`java -int[] cartas = {5, 2, 8, 1}; -for (int ronda = 0; ronda < cartas.[[length]] - 1; ronda++) { - for (int i = 0; i < cartas.length - 1; i++) { - if (cartas[i] [[>]] cartas[i + 1]) { - [[int]] aux = cartas[i]; - cartas[i] = cartas[i + 1]; - cartas[i + 1] = [[aux]]; - } - } -} -for (int i = 0; i < cartas.length; i++) { - System.out.println(cartas[i]); -} -\`\`\` -erklärung: Se uma carta for maior do que a vizinha da direita, as duas trocam de lugar, com uma variável auxiliar como ao trocar os copos. Depois de \`length - 1\` rondas está tudo ordenado: 1, 2, 5, 8. -::: - -## Encontra o erro: pesquisa - -::: übung l19-java-fehler -titel: Corrigir a pesquisa linear -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. O programa deve mostrar a **primeira** posição da carta procurada, para o 4 é **Posição: 1**. Mas mostra **Posição: 3**. - “Abrir no editor” mostra o código na área Código. Encontra o erro, corrige-o e carrega em Verificar. -start: l19-java-fehler-start.visustruct -test: 4 -> Posição: 1 -test: 6 -> Posição: 4 -test: 9 -> Posição: -1 -test: 8 -> Posição: 0 -braucht: whileSchleife, verzweigung -tipp: O ciclo vai sempre até à última carta. O que acontece quando o 4 aparece uma segunda vez? -tipp: O ciclo deve parar assim que encontrar algo: \`while (posicao == -1 && i < cartas.length)\`. -lösung: l19-java-fehler-loesung.visustruct -::: - -## Porque é que não corre? - -::: übung l19-java-laeuft-nicht -titel: Procurar uma carta -code: java -auftrag: As cartas {8, 4, 11, 4, 6} estão num array. O programa procura o 11 e deve mostrar **Posição: 2**. Mas não corre. - “Abrir no editor” mostra o código na área Código. Clica em “Executar” e lê a mensagem de erro. Corrige o erro e carrega em Verificar. -start: l19-java-laeuft-nicht-start.visustruct -test: - -> Posição: 2 -braucht: forSchleife, verzweigung -tipp: Compara os nomes na condição com os nomes criados em cima. -tipp: A carta procurada está em \`procurada\` com p minúsculo: \`if (cartas[i] == procurada) {\` -lösung: l19-java-laeuft-nicht-loesung.visustruct -::: - -## Jogo: Profissional da pesquisa - -::: spiel frage -titel: Jogo: Profissional da pesquisa -intro: O Ludwig procura e ordena cartas. Quão rápido é, e o que sai? Sete rondas. - -runde: - int[] cartas = {8, 4, 11, 4, 6}; - int procurada = 4; - int posicao = -1; - int i = 0; - while (posicao == -1 && i < cartas.length) { - if (cartas[i] == procurada) { - posicao = i; - } - i = i + 1; - } - System.out.println("Posição: " + posicao); -frage: O que aparece? -- [x] Posição: 1 -- Posição: 3 -- Posição: 2 -> A pesquisa para no primeiro 4. Está na posição 1, contando a partir de 0. - -runde: - int[] cartas = {8, 4, 11, 4, 6}; - int procurada = 7; -frage: Com a mesma pesquisa linear: o que fica em posicao? -- 0 -- 5 -- [x] -1 -> O 7 não está lá. posicao fica em -1, que quer dizer "não encontrado". - -runde: -frage: Com que cartas funciona a pesquisa binária? -- com todas -- [x] só com cartas ordenadas -- só com menos de 100 -> Só com as cartas ordenadas é que, depois de olhares para o meio, sabes em que metade continuar a procurar. - -runde: -frage: De quantas comparações precisa no máximo a pesquisa binária com 100 cartas? -- 50 -- [x] 7 -- 100 -> Cada comparação corta o monte a meio. Cortar 7 vezes chega para até 128 cartas, portanto também para 100. - -runde: -frage: Em vez de 1000 cartas agora são 2000. Quantas comparações a mais precisa no máximo a pesquisa binária? -- [x] uma -- o dobro -- 1000 -> A primeira comparação reduz 2000 a 1000. Depois continua como antes. - -runde: - int[] cartas = {5, 3, 8, 1}; -frage: Uma passagem do bubble sort compara sempre dois vizinhos, da esquerda para a direita. Como ficam as cartas depois? -- 1, 3, 5, 8 -- [x] 3, 5, 1, 8 -- 3, 5, 8, 1 -> 5 e 3 trocam, 5 e 8 ficam, 8 e 1 trocam. A carta maior fica na ponta da direita. - -runde: -frage: O bubble sort troca \`cartas[i]\` e \`cartas[i + 1]\`. De que precisa para isso? -- [x] de uma variável auxiliar -- de um segundo array -- de uma pesquisa binária -> Tal como ao trocar duas variáveis: \`int aux = cartas[i];\`, senão perde-se um valor. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar como a pesquisa linear percorre um array. → Procurar uma carta -- Consigo construir uma pesquisa linear como estrutograma. → Exercício: Encontrar uma carta -- Consigo explicar porque é que a pesquisa binária só funciona com valores ordenados. → Pesquisa binária: ordenado é mais rápido -- Consigo explicar porque é que a pesquisa binária é muito mais rápida com muitos valores. → Quão rápido é? -- Consigo mostrar uma ronda de bubble sort com cartas de jogar. → Ordenar com bubble sort -- Consigo percorrer um array em Java e contar resultados. → Agora em Java -::: - -## Teste de conhecimentos - -::: quiz -? De que precisa a pesquisa binária? -→ Pesquisa binária: ordenado é mais rápido -- [ ] O array tem de ter exatamente 100 posições. -- [x] Os valores têm de estar ordenados. -- [ ] O valor procurado tem de existir. -> Só com valores ordenados é que sabes, depois de olhares para o meio, em que metade continuar a procurar. - -? Um array ordenado tem 1000 valores. De quantas comparações precisa a pesquisa binária no máximo? -→ Quão rápido é? -- [ ] 500 -- [ ] 100 -- [x] 10 -> 1000 → 500 → 250 → 125 → 63 → 32 → 16 → 8 → 4 → 2 → 1: depois de cerca de 10 divisões a meio, resta um valor. - -? O que compara o bubble sort em cada passo? -→ Ordenar com bubble sort -- [ ] A primeira e a última carta -- [x] Duas cartas vizinhas -- [ ] Cada carta com a do meio -> Se a carta maior estiver à esquerda, os vizinhos são trocados. - -? Onde está a carta maior depois da primeira ronda do bubble sort? -→ Ordenar com bubble sort -- [ ] Na ponta da esquerda -- [ ] No meio -- [x] Na ponta da direita -> Vai sendo trocada em cada comparação e sobe para a direita como uma bolha. - -? O que quer dizer \`posicao = -1\` na pesquisa linear? -→ Procurar uma carta -- [x] A carta (ainda) não foi encontrada. -- [ ] A carta está mesmo lá atrás. -- [ ] O array está vazio. - -? Associa cada método à descrição certa. -→ Procurar uma carta -= Pesquisa linear -> olhar para uma carta a seguir à outra -= Pesquisa binária -> olhar para o meio e pôr de lado uma metade -= Bubble sort -> comparar vizinhos e trocá-los - -? Porque usas um ciclo for para contar e não uma pesquisa que para no primeiro resultado? -→ Agora em Java -- [ ] Um ciclo for é sempre mais rápido. -- [x] Tens de ver todas as cartas, senão faltam resultados mais à frente. -- [ ] Um ciclo while não consegue percorrer arrays. -> A carta pode aparecer várias vezes. Por isso percorres todas as cartas até ao fim. -::: -`,Ey=`--- -titel: Estrutograma e código -bereich: Do estrutograma ao código -kurz: Todos os elementos em Java num relance, gerar código, escrever Java na área Código, executá-lo e procurar erros. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- abrir a vista de código e escolher Java como linguagem -- reconhecer em Java a saída, a entrada, as decisões e os ciclos -- explicar a diferença entre Excerto e Classe -- explicar como o código Java corre em qualquer sistema operativo com o compilador, o bytecode e a JVM -- distinguir o JDK do JRE -- explicar porque é que um programa em C++ tem de ser compilado de novo para cada sistema operativo e um programa Java não -- reconstruir um programa Java curto como estrutograma -- escrever Java na área Código e ler o estrutograma que é construído -- executar um programa com ▶︎ e usar entradas e saídas na consola -- encontrar um erro de sintaxe ou de execução pelo número de linha vermelho -- pôr pontos de paragem e seguir um programa passo a passo -::: - -## A vista de código - -Nas lições 4 a 19 já escreveste cada elemento em Java. Aqui vês tudo num relance e aprendes como o VisuStruct transforma um estrutograma inteiro num programa Java. - -Um estrutograma descreve um processo. Para um computador o executar, precisas de um programa numa linguagem de programação. O VisuStruct escreve esse código por ti. - -Clica em [Código](#zeige-code) nas ferramentas (ícone com parênteses angulares). À direita aparece o **Gerador de código**: - -- Em cima escolhes a linguagem: **Java**, **Python** ou **JavaScript**. -- Para Java escolhes ainda em **Saída Java** entre **Excerto** (só as instruções) e **Classe** (um ficheiro Java completo). -- **Texto do diagrama como comentários** escreve o texto de cada bloco como comentário por cima do código. -- **Copiar** põe o código na área de transferência, **Guardar Java…** guarda um ficheiro \`.java\`. - -O código muda logo sempre que alteras alguma coisa no estrutograma. - -::: angeberwissen -Mais tarde, Nassi trabalhou na linguagem de programação Ada e foi responsável pelo software na Apple. Portanto, quem inventa estrutogramas não acaba num curral de cabras, acaba na Apple. -::: - -::: diagramm l17-beispiel.visustruct -Um pequeno programa com uma entrada, um ciclo for e uma decisão. Abre-o e olha para o código Java correspondente. -::: - -## O exemplo em Java - -Para o diagrama de cima, o VisuStruct gera este código como **Excerto**: - -\`\`\` -// import java.util.Scanner; -Scanner scanner = new Scanner(System.in); - -System.out.println("Que tabuada?"); -int numero = scanner.nextInt(); -for (int i = 1; i <= 3; i++) { - int resultado = numero * i; - System.out.println(i + " vezes " + numero + " = " + resultado); -} -if (numero > 10) { - System.out.println("Esta foi difícil!"); -} else { - System.out.println("Esta foi fácil."); -} -\`\`\` - -Em Java, cada instrução termina com um **ponto e vírgula** \`;\`. O que está dentro de um bloco fica entre **chavetas** \`{ }\` em Java. A indentação mostra-te o que pertence ao quê. - -## Os elementos em Java - -| Elemento | Java | Lição | -|---|---|---| -| Instrução | \`int soma = 0;\` | 4, 5 | -| Saída | \`System.out.println(soma);\` | 6 | -| Entrada | \`int numero = scanner.nextInt();\` | 6 | -| Decisão | \`if (condição) { … } else { … }\` | 8, 9 | -| Seleção | \`switch (valor) { case 1 -> { … } default -> { … } }\` | 10 | -| Ciclo while | \`while (condição) { … }\` | 11 | -| Ciclo do-while | \`do { … } while (condição);\` | 12 | -| Ciclo for | \`for (int i = 0; i < 5; i++) { … }\` | 13 | -| Ciclo infinito | \`while (true) { … }\` | 11, 18 | -| Interrupção | \`break;\` | 18 | -| Chamada | \`calcular();\` | 18 | -| Array | \`int[] numeros = new int[5];\` | 15, 16 | -| Texto (String) | \`String nome = scanner.next();\` | 17 | - -Para entradas, \`import java.util.Scanner;\` e \`Scanner scanner = new Scanner(System.in);\` são escritos uma vez lá em cima. Antes de cada entrada, o VisuStruct mostra a pergunta com \`System.out.println(…)\`. - -Se o ramo false de uma decisão estiver vazio, o VisuStruct gera mesmo assim uma parte \`else\` que só contém o comentário \`// empty element\`. Se desligares **Gerar ramo else vazio no código**, a parte \`else\` vazia desaparece. - -## Excerto ou Classe - -Podes colar um **Excerto** no método \`main\` de um programa Java que já exista. Com **Classe** obténs um ficheiro completo que podes compilar e executar logo: - -\`\`\` -import java.util.Scanner; - -public class Tabuada { - public static void main(String[] args) { - Scanner scanner = new Scanner(System.in); - - System.out.println("Que tabuada?"); - int numero = scanner.nextInt(); - ... - } // fim do main -} // fim da classe -\`\`\` - -Os comentários \`// fim do main\` e \`// fim da classe\` depois das duas últimas chavetas não são necessários, mas ajudam muito: vês logo que chaveta fecha o quê e não te esqueces de nenhuma. - -O nome da classe vem do **título** do diagrama. O ficheiro tem de ter exatamente o mesmo nome, aqui \`Tabuada.java\`. O **Guardar Java…** trata disso por ti. - -> O código gerado é um bom começo, mas confirma-o. Se primeiro criares uma variável e mais tarde a voltares a ler com uma entrada, o VisuStruct escreve mesmo assim \`int pin = scanner.nextInt();\`. O Java indica então que \`pin\` já existe. Basta apagares o \`int\` nessa linha. - -## Como o Java corre em qualquer computador - -O teu ficheiro \`Tabuada.java\` é **código-fonte**: texto para pessoas. O processador do teu computador não o entende diretamente. Por isso, o Java corre em dois passos: - -1. O **compilador** \`javac\` traduz o código-fonte em **bytecode**. O resultado é o ficheiro \`Tabuada.class\`. -2. A **JVM** (Java Virtual Machine, máquina virtual Java) lê o bytecode e, ao executá-lo, traduz-o em **código máquina**: instruções para o processador do teu computador. - -::: grafik l20-jvm.svg -Compilar uma vez: o mesmo bytecode corre em qualquer JVM. -::: - -O bytecode existe só uma vez. Já a JVM existe para Windows, macOS, Linux e muitos outros sistemas. Por isso, o mesmo ficheiro \`.class\` corre em qualquer computador que tenha uma JVM instalada. A isto chama-se **independente da plataforma**. O lema é **“Write once, run anywhere”**: escrever uma vez, executar em qualquer lado. - -::: angeberwissen -Um dos primeiros compiladores foi construído em 1952 por **Grace Hopper**, que já conheces da lição 7. Na altura, muitos diziam-lhe que os computadores só sabiam fazer contas e não traduzir programas. Ela fê-lo na mesma. -::: - -Compara com o **C++**: aí o compilador traduz o código-fonte diretamente em código máquina para um só processador e um só sistema operativo. O programa pronto só corre aí. Para Windows, macOS e Linux tens de o compilar de novo de cada vez. - -::: grafik l20-cpp.svg -O C++ é compilado para código máquina em separado para cada sistema. -::: - -| | C++ | Java | -|---|---|---| -| O compilador gera | código máquina | bytecode | -| Quantas vezes compilar? | de novo para cada sistema | uma vez | -| De que precisa o computador para o executar? | de mais nada | de uma JVM | - -Isto torna o Java mais lento? Quase nada. As partes do programa que correm muitas vezes, por exemplo ciclos, são traduzidas de uma vez para código máquina pelo **compilador JIT** da JVM (“just in time”, mesmo a tempo). Depois correm quase tão depressa como em C++. - -> Imagina a JVM como um pequeno computador feito de software, quase como um sistema operativo só para programas Java. Mas, rigorosamente, não é um sistema operativo, é uma **máquina virtual**: um programa que corre ele próprio no Windows, no macOS ou no Linux e que, lá dentro, faz de conta que é um computador. - -Ao instalar o Java, encontras duas siglas: - -| Sigla | O que tem lá dentro? | Para quê? | -|---|---|---| -| **JRE** (Java Runtime Environment) | a JVM e blocos prontos como o \`Scanner\` | executar programas Java | -| **JDK** (Java Development Kit) | o JRE e ainda o compilador \`javac\` e outras ferramentas | escrever e compilar programas Java | - -Para programar precisas, portanto, do JDK. Hoje em dia normalmente só se descarrega o JDK, o JRE vem lá dentro. Para usar o VisuStruct no browser não precisas de instalar nada disto. - -::: angeberwissen -Com um JDK podes fazer tudo isto tu próprio na consola, ou seja, no terminal ou na linha de comandos: - -\`\`\` -java -version -javac Tabuada.java -java Tabuada -\`\`\` - -- \`java -version\` mostra que versão do Java está instalada. Se aparecer uma mensagem de erro, ainda falta o Java. -- \`javac Tabuada.java\` inicia o compilador. Depois tens dois ficheiros na pasta: o \`Tabuada.java\` com o teu código-fonte e o novo \`Tabuada.class\` com o bytecode. -- \`java Tabuada\` inicia a JVM com o teu bytecode. Aqui escreves o nome da classe sem \`.class\`. - -Se \`java -version\` funcionar mas \`javac -version\` não, só tens um JRE instalado. Nesse caso falta-te o JDK. -::: - -::: angeberwissen -Todos os ficheiros Java compilados (\`.class\`) começam com os mesmos quatro bytes, escritos em hexadecimal: \`CA FE BA BE\`. É assim que o computador reconhece que é Java. Foi James Gosling que inventou este “cafe babe”, a condizer com o café do nome. -::: - -## O modo de linguagem Java - -Na [roda dentada](#zeige-einstellungen) em cima à direita podes ativar as **Palavras-chave do código**. Então os blocos da área Elementos têm os mesmos nomes que os comandos em Java: \`if\`, \`switch\`, \`for\`, \`while\`, \`do\`, \`while(true)\`, \`break\` e \`call\`. O resto da interface fica no teu idioma. - -Isto ajuda-te a associar os blocos aos comandos Java. O teu diagrama em si fica igual. - -## Escrever Java diretamente - -Até agora desenhaste primeiro o estrutograma e depois viste o código. Na área [Código](#zeige-codelive) também funciona ao contrário. Clica na barra estreita **Código** à direita. Ao lado do estrutograma aparece uma coluna onde escreves tu mesmo o Java. - -- **Do estrutograma para o código:** Quando alteras alguma coisa no estrutograma, o código Java muda logo. -- **Do código para o estrutograma:** Quando escreves Java, o VisuStruct constrói o estrutograma enquanto escreves. Se ainda faltar alguma coisa, por exemplo uma chaveta de fecho \`}\`, aparece uma indicação por baixo do código. -- **Sugestões:** Enquanto escreves, o VisuStruct sugere modelos, por exemplo para \`if\`, \`for\` ou \`while\`. Com Enter aceitas um modelo. Substitui simplesmente os marcadores cinzentos pelo teu texto. -- **Indentar:** Se o teu código ficou desarrumado, clica no botão **Indentar** em cima na área Código. Cada nível fica então quatro espaços mais para dentro, como no Eclipse ou no IntelliJ. Se tiveres linhas selecionadas, o VisuStruct só indenta essas. - -Assim podes experimentar os dois caminhos. Desenhar primeiro o estrutograma é bom para planear um processo. Escrever o código diretamente é mais rápido quando já sabes o que queres. O estrutograma ajuda-te depois a verificar o teu código. - -## Executar o programa - -Em cima, na área Código, [▶︎ Executar](#zeige-ausfuehren) inicia o teu programa e **■ Parar** termina-o, por exemplo num ciclo infinito. O que o teu programa mostra aparece em baixo na [consola](#zeige-konsole). Se pedir uma entrada, escreve-a lá e carrega em Enter. - -Podes arrastar com o rato a fronteira entre o código e a consola para cima ou para baixo. - -Se algo correr mal, o número da linha fica vermelho. Num erro de sintaxe, o sítio também fica sublinhado a vermelho e por baixo lês, por exemplo, **Linha 2: Ainda falta aqui: ;**. Aí o ▶︎ nem sequer arranca o programa. Se o programa parar enquanto corre, é um erro de execução e a consola diz a linha, por exemplo **Linha 3: Divisão por zero.** Os três tipos de erros já os conheces da lição “Simulação e teste de secretária”. - -Se o VisuStruct souber exatamente o que falta, aparece um 💡 no fim da linha. Passa o rato por cima para veres a sugestão, por exemplo **Sugestão: inserir import java.util.Scanner;**. Um clique aplica-a. Mesmo assim, lê o que foi inserido, para saberes fazê-lo sozinho da próxima vez. - -> A área Código entende os comandos Java que também existem como blocos no estrutograma. Ainda não conhece classes próprias nem objetos. Janelas com Swing funcionam na mesma: liga o **Java verdadeiro (com Swing)** na roda dentada (lição “A tua primeira janela”). - -::: angeberwissen -Os profissionais chamam **IDE** (Integrated Development Environment) a um programa onde se escreve código, se executa e se procuram erros. Com a área Código, o VisuStruct é um pequeno IDE com estrutograma incluído. -::: - -## Encontrar erros com o depurador - -Às vezes um programa corre, mas calcula mal. É um erro de lógica. Aí ajuda o **depurador** (debugger): executa o teu programa devagar e tu vês o que acontece. - -- **Ponto de paragem:** Clica num número de linha à esquerda. Aparece um ponto vermelho. [▶︎ Executar](#zeige-ausfuehren) para antes dessa linha. Outro clique tira o ponto. -- **Linha amarela:** É a próxima a ser executada. No estrutograma, o bloco correspondente fica selecionado. -- **Variáveis:** Por baixo do código vês o valor atual de cada variável. O que mudou desde a última paragem acende-se a amarelo por um momento. -- **Passo a passo:** O botão [Passo a passo](#zeige-einzelschritt) ao lado de ▶︎ executa exatamente uma linha e para outra vez. Sem ponto de paragem, inicia o programa e para antes da primeira linha. -- **Continuar:** ▶︎ corre até ao próximo ponto de paragem ou até ao fim. **■ Parar** termina. - -Experimenta com este programa. Põe um ponto de paragem na linha 3 e carrega várias vezes em ▶︎. Como crescem \`i\` e \`soma\`? - -\`\`\` -int soma = 0; -for (int i = 1; i <= 4; i++) { - soma = soma + i; -} -System.out.println("Soma: " + soma); -\`\`\` - -::: ludwig -É um teste de secretária, só que o computador toma notas por ti. Antes de cada passo, pensa no valor que a variável devia ter a seguir. Quando a tabela não bate com o que esperavas, encontraste o sítio do erro. -::: - -## Exercício: Primeiro o código, depois o estrutograma - -Desta vez não desenhas. Abre a área Código e escreve este código Java: - -\`\`\` -System.out.println("Número inicial?"); -int zahl = scanner.nextInt(); -while (zahl > 0) { - System.out.println(zahl); - zahl = zahl - 1; -} -System.out.println("Start!"); -\`\`\` - -::: übung l20-countdown -titel: Contagem decrescente -bereich: beide -auftrag: Escreve o código Java acima na área Código. Executa-o com ▶︎ e introduz um número inicial na consola. - Depois observa o estrutograma que foi construído e clica em Verificar. -test: 3 -> 3 | 2 | 1 | Start! -test: 1 -> 1 | Start! -test: 0 -> Start! -braucht: whileSchleife -tipp: Abre a área Código com a barra estreita mais à direita e começa simplesmente a escrever. -tipp: Atenção ao ponto e vírgula no fim de cada instrução e às chavetas do ciclo while. Por baixo do código aparece uma indicação quando ainda falta alguma coisa. -lösung: l20-countdown-loesung.visustruct -::: - -## Exercício: Java como estrutograma - -Agora ao contrário: aqui está um pedaço de código Java. Reconstrói-o como estrutograma. - -\`\`\` -System.out.println("Introduz um número"); -int n = scanner.nextInt(); -int fatorial = 1; -for (int i = 1; i <= n; i++) { - fatorial = fatorial * i; -} -System.out.println(fatorial); -\`\`\` - -::: übung l17-fakultaet -titel: Fatorial a partir de Java -bereich: beide -auftrag: Reconstrói o código Java de cima como estrutograma. O programa lê um número n e mostra o produto 1 · 2 · … · n. - No fim, confirma na vista de código se o teu código Java fica igual. -test: 3 -> 6 -test: 5 -> 120 -test: 1 -> 1 -braucht: forSchleife -tipp: Lê o código Java linha a linha e pensa que bloco serve para cada linha. -tipp: As duas primeiras linhas Java juntas são uma única entrada: \`input: int n "Introduz um número"\`. O ciclo for tem os três campos \`int i = 1\`, \`i <= n\` e \`i++\`. -lösung: l17-fakultaet-loesung.visustruct -::: - -## Exercício: else if - -Em Java, vários casos escrevem-se muitas vezes com \`else if\`: - -\`\`\` -if (idade < 6) { - System.out.println("Entrada gratuita"); -} else if (idade < 18) { - System.out.println("Bilhete de criança: 4 euros"); -} else { - System.out.println("Bilhete de adulto: 8 euros"); -} -\`\`\` - -Num estrutograma não existe \`else if\`. Aí, uma segunda decisão fica no ramo false da primeira. Se o ramo false tiver só essa decisão, o VisuStruct volta a transformá-la em \`else if\` quando gera o código, e em \`elif\` em Python. - -::: übung l17-ticket -titel: Preço do bilhete -bereich: beide -auftrag: Pede a idade como número inteiro e reconstrói o código Java de cima como estrutograma. -test: 5 -> Entrada gratuita -test: 6 -> Bilhete de criança: 4 euros -test: 17 -> Bilhete de criança: 4 euros -test: 18 -> Bilhete de adulto: 8 euros -braucht: verzweigung -tipp: No código Java há outro \`if\` dentro de um \`else\`. No estrutograma isso é uma decisão dentro de um ramo. -tipp: Arrasta uma segunda decisão com a condição \`idade < 18\` para o ramo false da primeira decisão. -lösung: l17-ticket-loesung.visustruct -::: - -## Jogo: Que bloco é? - -::: spiel frage -titel: Jogo: Que bloco é? -intro: O Ludwig mostra-te Java. Que bloco do estrutograma está por trás? Oito rondas. -frage: Que bloco é este? - -runde: - do { - numero = numero * 2; - } while (numero < 100); -- Ciclo while -- [x] Ciclo do-while -- Ciclo for -> A condição está em baixo. Por isso o corpo corre pelo menos uma vez. - -runde: - switch (escolha) { - case 1 -> { System.out.println("água"); } - default -> { System.out.println("Esse botão não existe."); } - } -- Decisão -- [x] Seleção -- Chamada -> Uma variável é comparada com valores fixos: isso é a seleção. - -runde: - break; -- Chamada -- Ciclo infinito -- [x] Interrupção -> O \`break\` sai logo do ciclo onde está. - -runde: - calcular(); -- [x] Chamada -- Saída -- Instrução -> Um nome com parênteses: aqui é chamado um subprograma. - -runde: - int numero = scanner.nextInt(); -- Saída -- [x] Entrada -- Chamada -> O scanner lê um valor. Isso é uma entrada. - -runde: - while (true) { - System.out.println("Olá"); - } -- Ciclo while com condição -- [x] Ciclo infinito -- Ciclo do-while -> \`while (true)\` não tem uma condição verdadeira. Só um break o poderia terminar. - -runde: -frage: Queres um ficheiro Java completo que possas compilar e executar logo. O que escolhes na vista de código? -- Excerto -- [x] Classe -> Um excerto são só as instruções para o método \`main\`. Classe cria o ficheiro inteiro. - -runde: -frage: No depurador, uma linha está amarela. O que quer dizer? -- Tem um erro. -- [x] É a próxima a ser executada. -- Tem um ponto de paragem. -> Amarela é a próxima linha. Um ponto de paragem é um ponto vermelho no número da linha. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo abrir a vista de código e escolher Java como linguagem. → A vista de código -- Consigo reconhecer em Java a saída, a entrada, as decisões e os ciclos. → Os elementos em Java -- Consigo explicar a diferença entre Excerto e Classe. → Excerto ou Classe -- Consigo explicar como o código Java corre em qualquer sistema operativo com o compilador, o bytecode e a JVM. → Como o Java corre em qualquer computador -- Consigo distinguir o JDK do JRE. → Como o Java corre em qualquer computador -- Consigo explicar porque é que um programa em C++ tem de ser compilado de novo para cada sistema operativo e um programa Java não. → Como o Java corre em qualquer computador -- Consigo reconstruir um programa Java curto como estrutograma. → Exercício: Java como estrutograma -- Consigo escrever Java na área Código e ler o estrutograma que é construído. → Escrever Java diretamente -- Consigo executar um programa com ▶︎ e usar entradas e saídas na consola. → Executar o programa -- Consigo encontrar um erro de sintaxe ou de execução pelo número de linha vermelho. → Executar o programa -- Consigo pôr pontos de paragem e seguir um programa passo a passo. → Encontrar erros com o depurador -::: - -## Teste de conhecimentos - -::: quiz -? Como fica \`output: "Olá"\` em Java? -→ Os elementos em Java -- [ ] print("Olá") -- [x] System.out.println("Olá"); -- [ ] console.log("Olá"); -> print é Python, console.log é JavaScript. - -? De onde vem o nome da classe em Classe? -→ Excerto ou Classe -- [ ] Chama-se sempre Main. -- [x] Do título do diagrama -- [ ] Do primeiro bloco -> Sem título, a classe chama-se Main. - -? Que código Java pertence a um ciclo do-while? -→ Os elementos em Java -- [ ] while (condição) { … } -- [x] do { … } while (condição); -- [ ] for (;;) { … } -> No do-while a condição está no fim, por isso o corpo é executado pelo menos uma vez. - -? Em que se transforma uma Interrupção no código Java? -→ Os elementos em Java -- [x] break; -- [ ] return; -- [ ] exit(); - -? O que acontece num ponto vermelho antes do número da linha? -→ Encontrar erros com o depurador -- [ ] A linha é ignorada. -- [x] O programa para antes dessa linha. -- [ ] A linha tem um erro. -> É um ponto de paragem. Continua-se com ▶︎ ou com Passo a passo. - -? Porque é que o mesmo ficheiro \`.class\` corre no Windows, no macOS e no Linux? -→ Como o Java corre em qualquer computador -- [ ] O compilador cria um ficheiro diferente para cada sistema operativo. -- [x] Em cada sistema operativo, uma JVM executa o mesmo bytecode. -- [ ] Os programas Java não precisam de sistema operativo. -> O bytecode fica igual. Só a JVM existe em separado para cada sistema. - -? De que precisas para escrever e compilar programas Java? -→ Como o Java corre em qualquer computador -- [ ] só do JRE -- [x] do JDK -- [ ] só da JVM -> O JRE chega para executar. O compilador \`javac\` só vem com o JDK. - -? Queres usar o teu programa no Windows e no Mac. O que é verdade? -→ Como o Java corre em qualquer computador -- [ ] O Java tem de ser compilado de novo para cada sistema, o C++ não. -- [x] O C++ tem de ser compilado de novo para cada sistema, o Java não. -- [ ] Os dois só se compilam uma vez. -> O C++ passa diretamente a código máquina para um sistema. O Java passa a bytecode, e é a JVM que o executa em cada sistema. - -? O número de linha 3 na área Código está vermelho. O que quer isso dizer? -→ Executar o programa -- [ ] Há um ponto de paragem na linha 3. -- [x] Há um erro na linha 3. -- [ ] A linha 3 é a próxima a ser executada. -> Um ponto de paragem é um ponto vermelho redondo, e a próxima linha está a amarelo. Um número de linha vermelho mostra um erro de sintaxe ou de execução. -::: -`,Ny=`--- -titel: Python e JavaScript -bereich: Do estrutograma ao código -kurz: Gerar código Python e JavaScript a partir do estrutograma e executar JavaScript diretamente no navegador. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- escolher e comparar Python e JavaScript na vista de código -- explicar que o Python forma blocos pela indentação e não precisa de tipos de dados -- explicar o que o VisuStruct ajusta ao traduzir para Python -- executar JavaScript com Testar no navegador -::: - -## O mesmo diagrama, três linguagens - -Na vista de código ([Código](#zeige-code) nas ferramentas) escolhes a linguagem em cima. O estrutograma fica igual; só muda a forma de escrever. Assim podes comparar as linguagens facilmente. - -::: diagramm l18-beispiel.visustruct -Um número é diminuído em 3 uma e outra vez até deixar de ser maior do que 0. -::: - -## Python - -Para o diagrama de cima, o VisuStruct gera este código Python: - -\`\`\` -numero = int(input("Valor inicial?")) -while numero > 0: - print(numero) - numero = numero - 3 -print("Fim") -\`\`\` - -O que é diferente em Python em relação ao Java: - -- **Não há ponto e vírgula** nem **chavetas**. O Python reconhece o que pertence a um ciclo ou a uma decisão só pela **indentação**. Depois de \`while\`, \`if\` e \`else\` vêm dois pontos. -- As variáveis **não têm tipo de dados**: \`int numero = 5\` passa a \`numero = 5\`. -- A saída chama-se \`print(…)\`, a entrada \`input(…)\`. Para um número, fica \`int(…)\` à volta. -- Em Python, os arrays chamam-se **listas**: \`int[] numeros = {2, 4, 6}\` passa a \`numeros = [2, 4, 6]\`, e \`numeros.length\` passa a \`len(numeros)\`. -- Um ciclo for simples como \`int i = 0\`, \`i < 5\`, \`i++\` passa a \`for i in range(5):\`. - -## Cuidados com o Python - -Em alguns sítios, o Python é mais rigoroso do que o Java. Por isso, o VisuStruct traduz a forma de escrever do Java quando exporta: - -- **Texto e número com +:** \`"Soma: " + soma\` passa a \`"Soma: " + str(soma)\`. Sem \`str(…)\`, o Python para com um erro. -- **Não existe i++:** passa a \`i += 1\`. -- **&&, || e !** chamam-se \`and\`, \`or\` e \`not\` em Python, e \`true\` e \`false\` passam a \`True\` e \`False\`. -- **Divisão inteira:** Em Python, \`7 / 2\` dá sempre 3.5. Se o estrutograma dividir dois valores \`int\`, o VisuStruct escreve \`7 // 2\`, que dá 3, tal como em Java. -- **Ciclos for:** Os ciclos de contagem passam a \`for … in range(…)\`, também com \`i <= n\`, a contar para trás ou com passos como \`i = i + 2\`. Se um ciclo não encaixar nesse padrão, passa a ciclo \`while\`. -- **Não existe do-while:** passa a \`while True:\` com \`if not (…): break\` no fim. - -Se um diagrama estiver marcado como **pseudocódigo**, só aparece um aviso por cima do código. Nesse caso o texto é um esboço e não um programa executável. - -## JavaScript - -O mesmo diagrama em JavaScript: - -\`\`\` -"use strict"; - -let numero = parseInt((prompt("Valor inicial?") ?? "0"), 10); -while (numero > 0) { - console.log(numero); - numero = numero - 3; -} -console.log("Fim"); -\`\`\` - -O JavaScript parece-se com o Java: chavetas, ponto e vírgula, \`while (…)\` e \`if (…)\`. As diferenças: - -- As variáveis criam-se com \`let\`, sem tipo de dados. As constantes com \`const\`. -- A saída chama-se \`console.log(…)\`, a entrada \`prompt(…)\`. O \`parseInt(…)\` transforma a entrada num número inteiro. -- Os arrays ficam entre parênteses retos: \`let numeros = [2, 4, 6];\`. -- \`"use strict";\` mesmo lá em cima faz o JavaScript queixar-se de erros de escrita nos nomes das variáveis. -- O JavaScript só tem um tipo de número. Se o estrutograma dividir dois valores \`int\`, o VisuStruct escreve \`Math.trunc(a / b)\`. Assim desaparecem os algarismos depois da vírgula, tal como em Java. - -::: angeberwissen -O JavaScript não tem quase nada a ver com o Java. Brendan Eich criou-o em 1995 em apenas dez dias para o navegador Netscape. Primeiro chamava-se Mocha, depois LiveScript. Como toda a gente andava entusiasmada com o Java, pouco antes do lançamento mudaram-lhe o nome para JavaScript, por razões de publicidade. Desde então, as duas linguagens são constantemente confundidas. Os programadores dizem que o Java e o JavaScript têm tanto em comum como um carro e uma carpete. -::: - -## Executar JavaScript no navegador - -O JavaScript é a linguagem dos navegadores. Por isso o VisuStruct consegue **executar o código logo**: - -::: angeberwissen -Já agora, porque é que as ligações no navegador são azuis? Em 1985, a equipa de Ben Shneiderman testou que cor se vê bem mas não atrapalha a leitura. Ganhou o azul-claro. O vermelho destacava-se mais, mas as pessoas lembravam-se pior do texto a seguir. -::: - -1. Escolhe **JavaScript** na vista de código. -2. Clica em **Testar no navegador**. -3. O que o \`console.log\` mostra aparece em **Saída**. Se o programa pedir um valor com \`prompt\`, escreve-o e clica em **OK**. -4. **Parar** cancela o programa. - -O VisuStruct para sozinho um ciclo infinito \`while (true)\` depois de 100 000 iterações e indica **Execução interrompida: possível ciclo infinito.** - -> No código JavaScript, uma **Chamada** precisa de um subprograma com esse nome, senão o JavaScript indica um erro. Ao contrário da simulação, o navegador não procura nos outros separadores. - -## Exercício: Python como estrutograma - -Este programa Python mostra todos os números pares de 0 até um limite: - -\`\`\` -limite = int(input("Até que número?")) -for i in range(0, limite + 1, 2): - print(i) -\`\`\` - -\`range(0, limite + 1, 2)\` quer dizer: começar em 0, parar antes de \`limite + 1\`, contar de dois em dois. - -::: übung l18-python -titel: Números pares -bereich: beide -auftrag: Reconstrói o programa Python de cima como estrutograma com um ciclo for. Com o limite 7, mostra 0, 2, 4 e 6. -test: 7 -> 0 | 2 | 4 | 6 -test: 4 -> 0 | 2 | 4 -test: 0 -> 0 -braucht: forSchleife -tipp: \`range(0, limite + 1, 2)\` conta de 0 até ao limite de dois em dois. Isso é um ciclo for. -tipp: Os três campos do ciclo for são \`int i = 0\`, \`i <= limite\` e \`i = i + 2\`. -lösung: l18-python-loesung.visustruct -::: - -## Exercício: JavaScript como estrutograma - -\`\`\` -let idade = parseInt((prompt("Quantos anos tens?") ?? "0"), 10); -if (idade >= 18) { - console.log("Podes votar."); -} else { - let anos = 18 - idade; - console.log("Espera mais " + anos + " anos."); -} -\`\`\` - -::: übung l18-js -titel: Votar a partir dos 18 -bereich: beide -auftrag: Reconstrói o código JavaScript de cima como estrutograma. Depois testa na vista de código, com **JavaScript** e **Testar no navegador**, se o teu código mostra o mesmo que a simulação. -test: 12 -> Espera mais 6 anos. -test: 18 -> Podes votar. -test: 15 -> Espera mais 3 anos. -test: 40 -> Podes votar. -braucht: verzweigung -tipp: Lê o código linha a linha. \`if\` e \`else\` passam a uma decisão. -tipp: \`prompt\` e \`parseInt\` juntos são uma entrada: \`input: int idade "Quantos anos tens?"\`. O ramo false tem dois blocos: primeiro \`int anos = 18 - idade\`, depois a saída. -lösung: l18-js-loesung.visustruct -::: - -## Jogo: Que linguagem? - -::: spiel frage -titel: Jogo: Que linguagem? -intro: O Ludwig misturou linhas de três linguagens. Reconheces de onde vem cada uma? Oito rondas. -frage: Em que linguagem está isto escrito? -antworten: Java | Python | JavaScript - -runde: - print(numero) -richtig: Python -> Em Python, a saída chama-se \`print(…)\`, sem ponto e vírgula. - -runde: - console.log(numero); -richtig: JavaScript -> \`console.log\` é a saída em JavaScript. - -runde: - System.out.println(numero); -richtig: Java -> A saída comprida com \`System.out\` só existe em Java. - -runde: - let numeros = [2, 4, 6]; -richtig: JavaScript -> \`let\` e parênteses retos: um array em JavaScript. - -runde: - for i in range(5): -richtig: Python -> Dois pontos em vez de chavetas, e \`range\`: típico de Python. - -runde: - int[] numeros = {2, 4, 6}; -richtig: Java -> Só o Java escreve o tipo de dados \`int[]\` à frente. - -runde: - numero = int(input("Valor inicial?")) -richtig: Python -> Sem tipo de dados antes da variável, sem ponto e vírgula, e \`int(…)\` à volta da entrada. - -runde: - let numero = parseInt(prompt("Valor inicial?"), 10); -richtig: JavaScript -> \`prompt\` pede um valor no browser, \`parseInt\` transforma-o num número. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo escolher e comparar Python e JavaScript na vista de código. → O mesmo diagrama, três linguagens -- Consigo explicar que o Python forma blocos pela indentação e não precisa de tipos de dados. → Python -- Consigo explicar o que o VisuStruct ajusta ao traduzir para Python. → Cuidados com o Python -- Consigo executar JavaScript com Testar no navegador. → Executar JavaScript no navegador -::: - -## Teste de conhecimentos - -::: quiz -? Como reconhece o Python que linhas pertencem a um ciclo? -→ Python -- [ ] Pelas chavetas -- [x] Pela indentação -- [ ] Pelo ponto e vírgula no fim da linha - -? Como fica \`output: soma\` em JavaScript? -→ O mesmo diagrama, três linguagens -- [ ] print(soma) -- [ ] System.out.println(soma); -- [x] console.log(soma); - -? Que linguagem consegue o VisuStruct executar diretamente no navegador? -→ Executar JavaScript no navegador -- [ ] Java -- [ ] Python -- [x] JavaScript -> Qualquer navegador consegue executar JavaScript. Para Java e Python precisas de um programa próprio. - -? O que acontece em Python com \`print("Soma: " + soma)\` se soma for o número 7? -→ Cuidados com o Python -- [ ] É mostrado Soma: 7. -- [x] O Python indica um erro, porque texto e número não se podem juntar com +. -- [ ] É mostrado Soma:. -> str(soma) transforma o número em texto. -::: -`,Fy=`--- -titel: Quando o programa falha: try e catch -bereich: Apanhar erros -kurz: Ler mensagens de erro com Java verdadeiro e apanhar falhas com try e catch. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ligar o Java verdadeiro na área Código -- ler numa mensagem de erro a exceção e a linha -- apanhar uma exceção com try e catch -- transformar texto num número com Integer.parseInt e apanhar a NumberFormatException -- escrever um programa que divide em segurança, mesmo por 0 -::: - -## Falhou! - -Este programa deve escrever três notas. Escreve-o na área [Código](#zeige-codelive) e clica em [▶︎ Executar](#zeige-ausfuehren): - -\`\`\`java -int[] notas = {2, 3, 1}; -System.out.println("Primeira nota: " + notas[0]); -System.out.println("Quarta nota: " + notas[3]); -System.out.println("Pronto!"); -\`\`\` - -A primeira nota aparece e depois acabou. O VisuStruct indica “Expressão inválida: notas[3]”, e “Pronto!” nunca chega a aparecer. O array só tem as posições 0, 1 e 2; a posição 3 não existe. - -::: ludwig -Já te aconteceu? Um pequeno erro e o programa inteiro vai-se. A mim aconteceu-me tantas vezes que cheguei a chorar em frente ao ecrã. Mas há uma solução, e chama-se **try** e **catch**. -::: - -::: angeberwissen -Em 1947, a equipa de Grace Hopper encontrou no computador Mark II da Universidade de Harvard uma traça que tinha causado uma avaria. Colaram-na no diário de bordo e escreveram: “First actual case of bug being found”. Desde então, os erros de programa chamam-se **bugs** (inglês para insetos). -::: - -## Ligar o Java verdadeiro - -Normalmente a área Código calcula com a mesma simulação que o estrutograma. Quando há um erro, ela simplesmente para. Para veres o que faz o Java verdadeiro, liga o Java verdadeiro: - -1. Clica na [roda dentada](#zeige-einstellungen) em cima à direita e escolhe Java como **Linguagem de programação**. -2. Na secção Java, marca **Java verdadeiro (com Swing)**. - -Na primeira execução, o VisuStruct descarrega uma única vez cerca de 40 MB. Depois disso, o teu código é compilado e executado como num ambiente de desenvolvimento Java a sério. - -> Com o Java verdadeiro, as entradas pela consola (\`Scanner\`) ainda não funcionam. Por isso os nossos exemplos não precisam de entradas. - -Escreve agora o programa como uma classe completa, para que os números das linhas batam certo, e executa-o: - -\`\`\`java -public class Notas { - public static void main(String[] args) { - int[] notas = {2, 3, 1}; - System.out.println("Primeira nota: " + notas[0]); - System.out.println("Quarta nota: " + notas[3]); - System.out.println("Pronto!"); - } -} -\`\`\` - -Na [consola](#zeige-konsole) aparece agora: - -\`\`\` -Primeira nota: 2 -java.lang.ArrayIndexOutOfBoundsException: 3 - at Notas.main(Notas.java:5) -\`\`\` - -## Ler a mensagem de erro - -Uma mensagem destas assusta à primeira vista. Mas diz-te exatamente o que se passa: - -| Parte | Significado | -|---|---| -| \`ArrayIndexOutOfBoundsException\` | O tipo de erro: o índice está fora do array. | -| \`3\` | O índice errado. | -| \`Notas.java:5\` | A linha onde aconteceu: a linha 5. | - -O Java chama a um erro de execução destes uma **exceção** (em inglês *exception*). Diz-se: o Java *lança* uma exceção. Se ninguém a apanhar, o programa para. É por isso que “Pronto!” não aparece. - -::: ludwig -Lê as mensagens de erro sempre de cima: primeiro o nome da exceção, depois o número da linha. Na área Código, os números das linhas estão à esquerda do código. -::: - -## try e catch - -Com **try** e **catch** apanhas a exceção. Altera a linha com \`notas[3]\` assim: - -\`\`\`java -try { - System.out.println("Quarta nota: " + notas[3]); -} catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Esta nota não existe."); -} -System.out.println("Pronto!"); -\`\`\` - -- \`try\` quer dizer “tenta”. O Java executa o bloco entre chavetas normalmente. -- Se uma linha lá dentro lançar uma exceção, o Java salta logo para o bloco \`catch\`. O resto do bloco \`try\` é saltado. -- Entre parênteses, depois de \`catch\`, está a exceção que é apanhada e um nome para ela, normalmente \`e\`. -- Depois, o programa continua normalmente. Agora também aparece “Pronto!”. - -Se tudo correr bem no bloco \`try\`, o bloco \`catch\` é simplesmente saltado. - -::: ludwig -try e catch são como a rede por baixo do trapézio no circo: enquanto tudo corre bem, nem dás por ela. Se alguém cair, a rede apanha-o e o espetáculo continua. -::: - -> No estrutograma não há nenhuma caixa para try e catch. Por isso o VisuStruct mostra o bloco inteiro como uma instrução. Só o podes executar com o Java verdadeiro. - -::: angeberwissen -Em julho de 1969, a Apollo 11 aterrou na Lua. Pouco antes da aterragem, o computador de bordo mostrou de repente os erros **1202** e **1201**: tinha mais trabalho do que conseguia fazer. Mesmo assim, não falhou. A equipa de **Margaret Hamilton** tinha construído o software de forma a reconhecer esses erros, deixar de lado as tarefas pouco importantes e continuar só com as mais importantes. A aterragem correu bem. - -Margaret Hamilton dirigia no MIT o grupo que escreveu o software das naves Apollo. Ela contava sempre com a possibilidade de algo correr mal, tal como tu fazes com \`try\` e \`catch\`. Para que o software fosse levado tão a sério como o hardware, chamou ao seu trabalho **software engineering** (engenharia de software). Hoje, uma área inteira da informática tem esse nome. -::: - -::: bild margaret-hamilton.jpg -Margaret Hamilton (nascida em 1936) em 1969, ao lado dos programas impressos que a sua equipa escreveu para a Apollo (foto: [Draper Laboratory](https://commons.wikimedia.org/wiki/File:Margaret_Hamilton_-_restoration.jpg), domínio público). -::: - -## Transformar texto num número - -Muitas vezes um número começa por ser texto, por exemplo mais tarde num campo de texto numa janela. \`Integer.parseInt\` transforma texto num número inteiro: - -\`\`\`java -String entrada = "doze"; -try { - int numero = Integer.parseInt(entrada); - System.out.println("Dobro: " + numero * 2); -} catch (NumberFormatException e) { - System.out.println("Isto não é um número: " + entrada); -} -\`\`\` - -O Java não consegue converter “doze” e lança uma \`NumberFormatException\`. O programa escreve “Isto não é um número: doze”. Muda a primeira linha para \`String entrada = "12";\` e executa outra vez: agora aparece “Dobro: 24”. - -## As exceções são objetos - -Isto não está no programa da disciplina, mas é útil saber. - -::: angeberwissen -Cada exceção é um objeto de uma classe própria, por exemplo \`ArithmeticException\` para a divisão por 0. \`e.getMessage()\` dá-te a sua mensagem, por exemplo \`/ by zero\`. - -Depois de um \`try\` podem vir vários blocos \`catch\`, um para cada tipo de erro. \`catch (Exception e)\` apanha todas as exceções, mas diz-te menos sobre o que aconteceu. E um bloco \`finally { … }\` no fim corre sempre, quer tenha havido erro quer não. -::: - -## Jogo: A rede apanha? - -::: spiel frage -titel: Jogo: A rede apanha? -intro: O Ludwig anda no trapézio. O try e o catch apanham-no, ou o programa vai abaixo? Sete rondas. - -runde: - int[] notas = {2, 3, 1}; - System.out.println(notas[3]); - System.out.println("Pronto!"); -frage: O que acontece com Java a sério? -- Aparece 1 e Pronto! -- Só aparece Pronto! -- [x] O programa vai abaixo com uma ArrayIndexOutOfBoundsException. -> A posição 3 não existe. Ninguém apanha a exceção, por isso Pronto! nunca chega a aparecer. - -runde: - int[] notas = {2, 3, 1}; - try { - System.out.println(notas[3]); - } catch (ArrayIndexOutOfBoundsException e) { - System.out.println("Esta nota não existe."); - } - System.out.println("Pronto!"); -frage: O que aparece? -- Pronto! -- [x] Esta nota não existe.\\nPronto! -- Esta nota não existe. -> O bloco catch apanha a exceção, e depois o programa continua normalmente. - -runde: - try { - System.out.println("A"); - System.out.println(10 / 0); - System.out.println("B"); - } catch (ArithmeticException e) { - System.out.println("C"); - } -frage: O que aparece? -- A\\nB\\nC -- [x] A\\nC -- C -> Em 10 / 0, o Java salta logo para o bloco catch. O resto do bloco try, o B, é saltado. - -runde: - String entrada = "12"; - try { - int numero = Integer.parseInt(entrada); - System.out.println("Dobro: " + numero * 2); - } catch (NumberFormatException e) { - System.out.println("Isto não é um número: " + entrada); - } -frage: O que aparece? -- [x] Dobro: 24 -- Isto não é um número: 12 -- Dobro: 1212 -> "12" pode ser convertido. Se tudo correr bem no bloco try, o catch é saltado. - -runde: -frage: Que exceção lança \`Integer.parseInt("2.5")\`? -- ArithmeticException -- [x] NumberFormatException -- nenhuma, o resultado é 2 -> parseInt só converte números inteiros. Com um ponto, o formato não é válido. - -runde: -frage: Que exceção lança \`12 / 0\` com \`int\`? -- [x] ArithmeticException -- NumberFormatException -- ArrayIndexOutOfBoundsException -> Dividir por 0 não funciona com números inteiros. A mensagem é \`/ by zero\`. - -runde: - try { - int fatias = 12 / 0; - } catch (NumberFormatException e) { - System.out.println("Erro!"); - } - System.out.println("Pronto!"); -frage: O que acontece? -- Aparece Erro! e Pronto! -- Só aparece Pronto! -- [x] O programa vai abaixo. -> O catch só apanha a exceção que está entre parênteses. Uma ArithmeticException cai pela rede. -::: - -## Tarefa: dividir em segurança - -Este programa falha: - -\`\`\`java -int bolo = 12; -int pessoas = 0; -System.out.println("Cada um recebe " + bolo / pessoas + " fatias."); -System.out.println("Bom apetite!"); -\`\`\` - -1. Executa-o com o Java verdadeiro e lê a mensagem de erro. Que exceção é? -2. Apanha-a com try e catch. Em vez da falha deve aparecer “Sem pessoas não se pode dividir.”, e “Bom apetite!” deve aparecer na mesma. -3. Põe \`pessoas\` a 4 e verifica que aparece “Cada um recebe 3 fatias.”. - -::: ludwig -A exceção chama-se \`ArithmeticException\`. Põe no bloco \`try\` só a linha que pode falhar. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ligar o Java verdadeiro na área Código. → Ligar o Java verdadeiro -- Consigo ler numa mensagem de erro a exceção e a linha. → Ler a mensagem de erro -- Consigo apanhar uma exceção com try e catch. → try e catch -- Consigo transformar texto num número com Integer.parseInt e apanhar a NumberFormatException. → Transformar texto num número -- Consigo escrever um programa que divide em segurança, mesmo por 0. → Tarefa: dividir em segurança -::: - -## Teste de conhecimentos - -::: quiz -? O que acontece quando uma exceção não é apanhada? -→ Ler a mensagem de erro -- [ ] O Java salta a linha e continua. -- [x] O programa para. -- [ ] O computador reinicia. -> É por isso que no primeiro exemplo “Pronto!” não aparece. - -? O que acontece no bloco try depois da linha que lança uma exceção? -→ try e catch -- [ ] As restantes linhas correm na mesma. -- [x] O resto é saltado e o Java salta para o bloco catch. -- [ ] O Java recomeça o bloco try. - -? Que exceção lança Integer.parseInt("doze")? -→ Transformar texto num número -- [ ] ArithmeticException -- [ ] ArrayIndexOutOfBoundsException -- [x] NumberFormatException -> “doze” não tem um formato de número que o Java entenda. - -? Associa cada exceção à sua causa. -→ Ler a mensagem de erro -= ArrayIndexOutOfBoundsException -> índice fora do array -= ArithmeticException -> divisão por 0 -= NumberFormatException -> o texto não é um número -::: -`,Ry=`--- -titel: Ler e guardar texto -bereich: Ficheiros -kurz: Ler um ficheiro de texto linha a linha com Scanner e guardar um ficheiro com PrintWriter. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar porque é que os programas guardam dados em ficheiros -- ler um ficheiro de texto linha a linha com Scanner, hasNextLine e nextLine -- explicar porque é que o Java exige try e catch ou throws com ficheiros -- escrever um ficheiro com PrintWriter e terminá-lo com close -- ler um ficheiro, completá-lo e voltar a guardá-lo numerado -::: - -## Porquê ficheiros? - -Tudo o que está em variáveis desaparece assim que o programa termina. Se um programa deve lembrar-se de alguma coisa, por exemplo uma pontuação, uma lista ou notas, guarda os dados num **ficheiro**. Na próxima vez que arranca, volta a ler o ficheiro. - -A Zora está livre, e o Ludwig está a preparar uma festa. A lista de compras dele está pronta no ficheiro \`compras.txt\`: - -::: datei compras.txt -Feno -Maçãs -Cenouras -Dentes-de-leão -::: - -Um ficheiro com a extensão \`.txt\` contém só texto, linha a linha. Podes abri-lo com qualquer editor, por exemplo o Bloco de Notas no Windows ou o TextEdit no Mac. - -## Ler um ficheiro com Scanner - -Já conheces o \`Scanner\`: \`new Scanner(System.in)\` lê do teclado. Se lhe deres um ficheiro em vez de \`System.in\`, ele lê do ficheiro: - -\`\`\`java -import java.io.*; -import java.util.Scanner; - -Scanner ficheiro = new Scanner(new File("compras.txt")); -while (ficheiro.hasNextLine()) { - String linha = ficheiro.nextLine(); - System.out.println(linha); -} -ficheiro.close(); -\`\`\` - -| Método | Significado | -|---|---| -| \`new File("compras.txt")\` | o ficheiro com este nome | -| \`hasNextLine()\` | Ainda há mais uma linha? \`true\` ou \`false\` | -| \`nextLine()\` | devolve a linha seguinte e avança uma linha | -| \`close()\` | fecha o ficheiro quando terminas | - -O ciclo repete-se enquanto houver mais uma linha. Em cada volta, \`nextLine()\` vai buscar a linha seguinte. \`File\` pertence ao pacote \`java.io\`, por isso precisas de \`import java.io.*;\`. - -::: diagramm dt-lesen.visustruct -É assim que a leitura fica num estrutograma. Experimenta na simulação. -::: - -::: ludwig -Imagina o Scanner como um dedo a apontar para uma linha. \`nextLine()\` lê a linha debaixo do dedo e desce o dedo uma linha. Quando o dedo já está abaixo da última linha, \`hasNextLine()\` devolve \`false\`. -::: - -## E se o ficheiro não existir? - -Se o ficheiro não existir, o Java não o consegue ler e lança uma \`FileNotFoundException\`. O Java exige que o teu programa conte com isso. Ou apanhas a exceção com try e catch, como na lição 22: - -\`\`\`java -try { - Scanner ficheiro = new Scanner(new File("compras.txt")); - // ... ler ... - ficheiro.close(); -} catch (FileNotFoundException e) { - System.out.println("Falta o ficheiro compras.txt!"); -} -\`\`\` - -Ou escreves \`throws Exception\` a seguir a \`main\`. Isso quer dizer: "Este método pode lançar uma exceção, eu não trato dela." Nesse caso, o programa para se o ficheiro não existir. - -\`\`\`java -public static void main(String[] args) throws Exception { -\`\`\` - -> O VisuStruct escreve \`throws Exception\` a seguir a \`main\` automaticamente assim que o teu programa trabalha com ficheiros. Na simulação, o VisuStruct indica um ficheiro em falta com "Não existe nenhum ficheiro chamado …". - -## Exercício: Numerar a lista de compras - -::: übung l23-java-nummern -titel: Numerar a lista de compras -code: java -auftrag: Lê \`compras.txt\` linha a linha e mostra cada linha com o seu número: **1. Feno**, **2. Maçãs** e assim por diante. -start: l23-java-nummern-start.visustruct -test: - -> 1. Feno | 2. Maçãs | 3. Cenouras | 4. Dentes-de-leão -braucht: whileSchleife -tipp: Antes do ciclo, cria \`int numero = 1;\` e aumenta o número no fim de cada volta. -tipp: A saída é \`System.out.println(numero + ". " + linha);\` -lösung: l23-java-nummern-loesung.visustruct -::: - -## Escrever num ficheiro com PrintWriter - -Para escrever existe o \`PrintWriter\`. Os seus métodos têm os mesmos nomes que os de \`System.out\`: - -\`\`\`java -PrintWriter saida = new PrintWriter("saudacao.txt"); -saida.println("Olá Zora!"); -saida.println("Até amanhã, Ludwig"); -saida.close(); -\`\`\` - -Depois disto, \`saudacao.txt\` contém: - -\`\`\` -Olá Zora! -Até amanhã, Ludwig -\`\`\` - -- \`println\` escreve o texto e começa depois uma nova linha, \`print\` escreve sem nova linha. -- Se o ficheiro já existir, é **substituído**. O conteúdo antigo desaparece. -- Depois da execução, o VisuStruct mostra por baixo da saída o que foi guardado no ficheiro. - -::: ludwig -Não te esqueças do \`close()\`! O PrintWriter junta primeiro o texto numa memória intermédia e só o escreve no ficheiro quando é fechado. Sem \`close()\` o ficheiro fica vazio. Já perdi assim uma história inteira. -::: - -## Exercício: Completar a lista de compras - -::: übung l23-java-ergaenzen -titel: Completar a lista de compras -code: java -auftrag: O Ludwig ainda precisa de bolo. Lê \`compras.txt\` e escreve cada linha no novo ficheiro \`compras-nova.txt\`. Depois acrescenta **Bolo** como última linha. No fim, mostra quantas linhas foram guardadas: **5 linhas guardadas**. -start: l23-java-ergaenzen-start.visustruct -test: - -> 5 linhas guardadas -datei: compras-nova.txt -> Feno | Maçãs | Cenouras | Dentes-de-leão | Bolo -braucht: whileSchleife -tipp: No ciclo, lês uma linha com \`ficheiro.nextLine()\` e escreve-la logo a seguir com \`saida.println(linha)\`. -tipp: Depois do ciclo vem \`saida.println("Bolo");\`. Fecha os dois ficheiros com \`close()\`, senão \`compras-nova.txt\` fica vazio. -lösung: l23-java-ergaenzen-loesung.visustruct -::: - -## Jogo: O que fica no ficheiro? - -::: spiel frage -titel: Jogo: O que fica no ficheiro? -intro: O Ludwig escreve ficheiros com o PrintWriter. Lê o código e prevê o que fica no ficheiro. Seis rondas. -frage: O que fica depois no ficheiro? - -runde: - PrintWriter saida = new PrintWriter("saudacao.txt"); - saida.println("Olá"); - saida.println("Zora"); - saida.close(); -- [x] Olá\\nZora -- OláZora -- Zora -> \`println\` começa uma nova linha depois de cada texto. - -runde: - PrintWriter saida = new PrintWriter("palavra.txt"); - saida.print("Bo"); - saida.print("la"); - saida.println("!"); - saida.close(); -- Bo\\nla\\n! -- [x] Bola! -- Bo la ! -> \`print\` continua a escrever sem nova linha, por isso as partes ficam coladas. - -runde: - PrintWriter saida = new PrintWriter("segredo.txt"); - saida.println("O tesouro está na torre"); -- O tesouro está na torre -- [x] Nada, o ficheiro fica vazio. -- O Java indica um erro. -> Sem \`close()\` o texto nunca chega ao ficheiro. Fica na memória intermédia. - -runde: - // Antes, nota.txt contém: Antigo - PrintWriter saida = new PrintWriter("nota.txt"); - saida.println("Novo"); - saida.close(); -- Antigo\\nNovo -- [x] Novo -- Novo\\nAntigo -> Um novo PrintWriter substitui o ficheiro. O conteúdo antigo desaparece. - -runde: - PrintWriter saida = new PrintWriter("numeros.txt"); - for (int i = 1; i <= 3; i++) { - saida.println(i * 2); - } - saida.close(); -- 1\\n2\\n3 -- 246 -- [x] 2\\n4\\n6 -> O ciclo escreve 2, 4 e 6, cada número na sua linha. - -runde: - // compras.txt contém: Feno, Maçãs (uma por linha) - Scanner ficheiro = new Scanner(new File("compras.txt")); - PrintWriter saida = new PrintWriter("grande.txt"); - while (ficheiro.hasNextLine()) { - saida.println(ficheiro.nextLine().toUpperCase()); - } - ficheiro.close(); - saida.close(); -- Feno\\nMaçãs -- [x] FENO\\nMAÇÃS -- FENOMAÇÃS -> Cada linha é lida, passada para maiúsculas e escrita na sua própria linha. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar porque é que os programas guardam dados em ficheiros. → Porquê ficheiros? -- Consigo ler um ficheiro de texto linha a linha com Scanner, hasNextLine e nextLine. → Ler um ficheiro com Scanner -- Consigo explicar porque é que o Java exige try e catch ou throws com ficheiros. → E se o ficheiro não existir? -- Consigo escrever um ficheiro com PrintWriter e terminá-lo com close(). → Escrever num ficheiro com PrintWriter -- Consigo ler um ficheiro, completá-lo e voltar a guardá-lo numerado. → Exercício: Completar a lista de compras -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve hasNextLine()? -→ Ler um ficheiro com Scanner -- [ ] a linha seguinte -- [x] true se ainda houver uma linha, senão false -- [ ] o número de linhas -> Com hasNextLine() o ciclo verifica se continua. - -? O que acontece se chamares new PrintWriter("lista.txt") para um ficheiro que já existe? -→ Escrever num ficheiro com PrintWriter -- [ ] O texto novo é acrescentado no fim. -- [x] O ficheiro é substituído. -- [ ] O Java indica um erro. - -? Porque é que um ficheiro fica vazio, apesar de println ter sido chamado? -→ Escrever num ficheiro com PrintWriter -- [ ] Porque println só escreve na consola. -- [x] Porque falta o close(). -- [ ] Porque o nome do ficheiro termina em .txt. -> Só o close() escreve a memória intermédia no ficheiro. - -? Liga cada método à sua tarefa. -→ Ler um ficheiro com Scanner -= nextLine() -> lê a linha seguinte -= println(texto) -> escreve uma linha -= close() -> fecha o ficheiro -::: -`,Jy=`--- -titel: Linha a linha com BufferedReader -bereich: Ficheiros -kurz: Ler com BufferedReader e readLine até chegar null, e ler um ficheiro inteiro de uma vez com Files. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ler um ficheiro com BufferedReader e readLine -- reconhecer o fim de um ficheiro, porque readLine aí devolve null -- contar quantas vezes uma palavra aparece num ficheiro -- ler um ficheiro inteiro com Files.readString e dividi-lo em linhas com split -- encontrar a entrada mais longa de um ficheiro -::: - -## O bolo desapareceu! - -Na manhã a seguir à festa, o bolo do Ludwig desapareceu. Felizmente, o castelo tem um diário. Cada linha começa com o dia e a hora: - -::: datei diario.txt -Seg 08:15 Ludwig alimenta as cabras -Seg 12:30 Zora lê na torre -Seg 18:00 Raposa passa às escondidas pelo portão -Ter 09:00 Ludwig faz um bolo -Ter 14:45 Lebre traz cenouras -Ter 16:20 Raposa pergunta pela receita -Ter 23:55 O bolo desapareceu -Qua 07:00 Zora encontra migalhas junto à toca da Raposa -::: - -Já o conseguias ler com o Scanner da lição 23. Mas, para ficheiros, os profissionais usam muitas vezes o **BufferedReader**. - -## Ler com BufferedReader - -\`\`\`java -import java.io.*; - -BufferedReader leitor = new BufferedReader(new FileReader("diario.txt")); -String linha = leitor.readLine(); -while (linha != null) { - System.out.println(linha); - linha = leitor.readLine(); -} -leitor.close(); -\`\`\` - -- \`new FileReader("diario.txt")\` abre o ficheiro, e \`new BufferedReader(…)\` põe-lhe uma memória intermédia à volta. Vai buscar o ficheiro em pedaços grandes e por isso é mais rápido com ficheiros longos. -- \`readLine()\` devolve a linha seguinte. Quando já não há linhas, devolve **null**. -- Por isso, o ciclo repete-se enquanto \`linha != null\`. No fim de cada volta lê a linha seguinte. - -::: diagramm dt-buffered.visustruct -Primeiro ler uma linha, depois verificar, e no fim da volta ler a seguinte. -::: - -**null** quer dizer "nada". Não é o texto vazio \`""\`, mas nenhum texto. Uma variável com \`null\` não tem métodos: \`linha.length()\` para então com uma \`NullPointerException\`. Por isso, verifica sempre primeiro \`linha != null\`. - -| | Scanner | BufferedReader | -|---|---|---| -| Criar | \`new Scanner(new File(…))\` | \`new BufferedReader(new FileReader(…))\` | -| Ainda há mais uma linha? | \`hasNextLine()\` | \`readLine()\` devolve \`null\` | -| Linha seguinte | \`nextLine()\` | \`readLine()\` | -| Ponto forte | também lê números (\`nextInt()\`) | rápido, mesmo com ficheiros grandes | - -::: ludwig -O ciclo é diferente do ciclo com o Scanner: a primeira linha é lida **antes** do ciclo, e cada uma das seguintes **no fim** da volta. Se te esqueceres da leitura no fim, o programa lê sempre a mesma linha e nunca para. -::: - -::: angeberwissen -O fim de uma linha não é igual em todo o lado: o Windows escreve dois carateres invisíveis, “retorno do carro” e “avanço de linha”, o Linux e o macOS só o avanço de linha. Os nomes vêm da máquina de escrever, em que o carro voltava atrás e o papel subia uma linha. O \`readLine()\` funciona com os dois. -::: - -## Exercício: Quantas vezes aparece a Raposa? - -::: übung l24-java-fuchs -titel: Quantas vezes aparece a Raposa? -code: java -auftrag: Lê o diário com o BufferedReader e conta as linhas onde aparece **Raposa**. No fim, mostra **Raposa:** e o número. -start: l24-java-fuchs-start.visustruct -test: - -> Raposa: 3 -braucht: whileSchleife, verzweigung -tipp: Antes do ciclo, cria \`int quantidade = 0;\`. No ciclo verificas \`linha.contains("Raposa")\`. -tipp: O \`linha = leitor.readLine();\` fica no fim do ciclo, depois do if. -lösung: l24-java-fuchs-loesung.visustruct -::: - -## Fechar automaticamente - -Para que o \`close()\` não seja esquecido, mesmo depois de uma exceção, existe uma forma curta do try: o que é criado entre os parênteses a seguir a \`try\` é fechado pelo Java sozinho no fim. - -\`\`\`java -try (BufferedReader leitor = new BufferedReader(new FileReader("diario.txt"))) { - String linha = leitor.readLine(); - while (linha != null) { - System.out.println(linha); - linha = leitor.readLine(); - } -} catch (IOException e) { - System.out.println("Falta o diário."); -} -\`\`\` - -Isto chama-se **try-with-resources**. \`IOException\` apanha todos os erros ao ler e escrever, incluindo um ficheiro em falta. - -> Tal como try e catch, o try-with-resources só funciona com Java verdadeiro (lição 22). Na simulação, escreves tu o \`close()\`. - -## O atalho: Files - -Desde o Java 11 é ainda mais curto. \`Files.readString\` lê o ficheiro inteiro de uma vez, como já conheces da lição 17. Com \`split("\\n")\` divides o texto num array de linhas: - -\`\`\`java -import java.nio.file.*; - -String texto = Files.readString(Path.of("diario.txt")); -String[] linhas = texto.split("\\n"); -for (int i = 0; i < linhas.length; i++) { - System.out.println(linhas[i]); -} -\`\`\` - -Agora todas as linhas estão no array. Podes vê-las pela ordem que quiseres, por exemplo de trás para a frente ou só uma sim, uma não. - -| Forma | Boa para | -|---|---| -| \`Scanner\` | ficheiros pequenos, ler números e palavras | -| \`BufferedReader\` | ficheiros grandes, linha a linha | -| \`Files.readString\` | ficheiros pequenos que cabem inteiros no programa | - -::: ludwig -Um ficheiro com um milhão de linhas é melhor lido linha a linha com o BufferedReader. Com \`Files.readString\`, o ficheiro inteiro teria de ir de uma vez para a memória. -::: - -## Exercício: A entrada mais longa - -::: übung l24-java-laengster -titel: A entrada mais longa -code: java -auftrag: No código inicial, as linhas do diário já estão no array \`linhas\`. Encontra a linha mais longa e mostra-a assim: **Entrada mais longa:** seguido da linha. -start: l24-java-laengster-start.visustruct -test: - -> Entrada mais longa: Qua 07:00 Zora encontra migalhas junto à toca da Raposa -braucht: forSchleife, verzweigung -tipp: Guarda a linha mais longa até agora em \`String maisLonga = linhas[0];\` e percorre as outras linhas com um ciclo for. -tipp: Se \`linhas[i].length() > maisLonga.length()\`, então \`linhas[i]\` passa a ser a mais longa. -lösung: l24-java-laengster-loesung.visustruct -::: - -## Jogo: Detetive de ficheiros - -::: spiel frage -titel: Jogo: Detetive de ficheiros -intro: Quem levou o bolo? Cada programa procura no **diário** a partir do início. Prevê o que mostra e junta as pistas. Cinco rondas. -frage: O que mostra o programa? - -runde: - String linha = leitor.readLine(); - linha = leitor.readLine(); - System.out.println(linha.substring(10)); -- Ludwig alimenta as cabras -- [x] Zora lê na torre -- Seg 12:30 Zora lê na torre -> O segundo \`readLine()\` devolve a segunda linha. O texto depois da hora começa no índice 10. - -runde: - int n = 0; - String linha = leitor.readLine(); - while (linha != null) { - if (linha.startsWith("Ter")) n++; - linha = leitor.readLine(); - } - System.out.println(n); -- 3 -- [x] 4 -- 8 -> Quatro entradas são de terça-feira. - -runde: - String linha = leitor.readLine(); - while (linha != null) { - if (linha.contains("bolo")) { - System.out.println(linha.substring(4, 9)); - } - linha = leitor.readLine(); - } -- [x] 09:00\\n23:55 -- 09:00 -- Ter\\nTer -> Há duas linhas com "bolo". \`substring(4, 9)\` recorta a hora. - -runde: - String linha = leitor.readLine(); - String ultima = ""; - while (linha != null) { - if (linha.contains("Raposa")) ultima = linha; - linha = leitor.readLine(); - } - System.out.println(ultima); -- Ter 16:20 Raposa pergunta pela receita -- [x] Qua 07:00 Zora encontra migalhas junto à toca da Raposa -- Seg 18:00 Raposa passa às escondidas pelo portão -> \`ultima\` é substituída a cada resultado. No fim fica lá o último. "toca da Raposa" também contém "Raposa". - -runde: - String linha = leitor.readLine(); - while (linha != null) { - if (linha.contains("migalhas")) { - int junto = linha.indexOf("junto à "); - System.out.println(linha.substring(junto + 8)); - } - linha = leitor.readLine(); - } -- [x] toca da Raposa -- junto à toca da Raposa -- migalhas -> As migalhas estão junto à toca da Raposa. A Raposa perguntou pela receita, e às 23:55 o bolo tinha desaparecido. Caso resolvido! -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ler um ficheiro com BufferedReader e readLine. → Ler com BufferedReader -- Consigo reconhecer o fim de um ficheiro, porque readLine aí devolve null (linha != null). → Ler com BufferedReader -- Consigo contar quantas vezes uma palavra aparece num ficheiro. → Exercício: Quantas vezes aparece a Raposa? -- Consigo ler um ficheiro inteiro com Files.readString e dividi-lo em linhas com split. → O atalho: Files -- Consigo encontrar a entrada mais longa de um ficheiro. → Exercício: A entrada mais longa -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve readLine() quando o ficheiro chegou ao fim? -→ Ler com BufferedReader -- [ ] "" -- [x] null -- [ ] -1 -> null quer dizer: já não há mais texto. - -? Porque é que no fim do ciclo há outra vez linha = leitor.readLine()? -→ Ler com BufferedReader -- [ ] Para fechar o ficheiro. -- [x] Senão linha fica sempre igual e o ciclo nunca para. -- [ ] Para mostrar a primeira linha duas vezes. - -? O que faz o try-with-resources? -→ Ler com BufferedReader -- [ ] Lê o ficheiro mais depressa. -- [x] Fecha o ficheiro sozinho no fim. -- [ ] Impede qualquer exceção. - -? Liga cada forma àquilo para que é boa. -→ O atalho: Files -= BufferedReader -> ficheiros grandes, linha a linha -= Files.readString -> ficheiros pequenos de uma vez -= Scanner -> ler números e palavras -::: -`,Gy=`--- -titel: Tabelas em CSV -bereich: Ficheiros -kurz: Ler uma tabela num ficheiro CSV, dividir cada linha com split e fazer contas com os números. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar como é feito um ficheiro CSV: cabeçalho, registos, separador -- dividir uma linha com split e fazer dela um número com Integer.parseInt -- saltar o cabeçalho antes de fazer contas -- calcular uma média a partir de uma coluna de um ficheiro CSV -- encontrar o melhor valor numa coluna de um ficheiro CSV -- nomear um ponto forte e um ponto fraco do CSV -::: - -## Uma tabela num ficheiro de texto - -A professora Cabra, da escola do castelo, guardou as notas da turma numa tabela. Como na Alemanha, 1 é a melhor nota e 6 a pior: - -| Nome | Matemática | Alemão | Inglês | -|---|---|---|---| -| Ludwig | 2 | 3 | 1 | -| Zora | 1 | 2 | 2 | -| Raposa | 4 | 3 | 5 | -| Lebre | 2 | 1 | 3 | - -No ficheiro, a tabela fica assim: - -::: datei turma.csv -Nome;Matemática;Alemão;Inglês -Ludwig;2;3;1 -Zora;1;2;2 -Raposa;4;3;5 -Lebre;2;1;3 -::: - -Isto é um **ficheiro CSV**. CSV quer dizer "Comma-Separated Values", ou seja, "valores separados por vírgulas". Cada linha é uma linha da tabela, e as colunas estão separadas por um carácter separador. Em Portugal e na Alemanha usa-se normalmente o **ponto e vírgula** \`;\`, porque a vírgula já aparece em números como \`2,5\`. - -- A primeira linha é o **cabeçalho**. Diz o que está em cada coluna. -- Cada uma das linhas seguintes é um **registo**, aqui um aluno ou uma aluna. -- O Excel, o LibreOffice e o Numbers abrem ficheiros CSV diretamente como tabela. - -## Dividir uma linha com split - -Já conheces o \`split(";")\` da lição 17. Divide um texto em cada ponto e vírgula e devolve um array: - -\`\`\`java -String linha = "Zora;1;2;2"; -String[] partes = linha.split(";"); -// partes[0] é "Zora", partes[1] é "1", partes[2] é "2", partes[3] é "2" -\`\`\` - -Depois disto, as notas ainda são **texto**. Só podes fazer contas com elas depois de as transformares num número com \`Integer.parseInt\`: - -\`\`\`java -int matematica = Integer.parseInt(partes[1]); -\`\`\` - -::: diagramm dt-csv.visustruct -Dividir uma linha CSV e ler a nota de Matemática como número. -::: - -## Ler o ficheiro inteiro - -Para o ficheiro inteiro, combinas o BufferedReader da lição 24 com \`split\`. O cabeçalho não tem notas, por isso lês essa linha antes do ciclo e saltas por cima dela: - -\`\`\`java -BufferedReader leitor = new BufferedReader(new FileReader("turma.csv")); -String linha = leitor.readLine(); // cabeçalho -linha = leitor.readLine(); // primeiro registo -while (linha != null) { - String[] partes = linha.split(";"); - System.out.println(partes[0] + ": Matemática " + partes[1]); - linha = leitor.readLine(); -} -leitor.close(); -\`\`\` - -::: ludwig -Sem o segundo \`readLine()\` antes do ciclo, o cabeçalho vai parar ao ciclo. Aí, \`Integer.parseInt("Matemática")\` tenta fazer um número a partir da palavra, e o Java para com uma \`NumberFormatException\`. -::: - -## Exercício: Média de Matemática - -::: übung l25-java-schnitt -titel: Média de Matemática -code: java -auftrag: Calcula a média das notas de Matemática. Salta o cabeçalho, soma as notas de Matemática (coluna 1) e divide pelo número de registos. Mostra **Média de Matemática:** e a média. -start: l25-java-schnitt-start.visustruct -test: - -> Média de Matemática: 2.25 -braucht: whileSchleife -tipp: Antes do ciclo, cria \`double soma = 0;\` e \`int quantidade = 0;\`. Com \`double\`, nada é cortado na divisão. -tipp: No ciclo: \`String[] partes = linha.split(";");\`, depois \`soma = soma + Integer.parseInt(partes[1]);\` e \`quantidade++;\`. -lösung: l25-java-schnitt-loesung.visustruct -::: - -## Exercício: Melhor nota a Inglês - -::: übung l25-java-beste -titel: Melhor nota a Inglês -code: java -auftrag: Quem tem a melhor nota a Inglês (coluna 3)? Mostra **Melhor nota a Inglês:**, o nome e, entre parênteses, a nota. -start: l25-java-beste-start.visustruct -test: - -> Melhor nota a Inglês: Ludwig (1) -braucht: whileSchleife, verzweigung -tipp: Guarda a melhor nota até agora em \`int melhorNota = 7;\` e o nome em \`String melhorNome = "";\`. Qualquer nota verdadeira é melhor do que 7. -tipp: Se \`nota < melhorNota\`, guardas a nova nota e \`partes[0]\` como nome. -lösung: l25-java-beste-loesung.visustruct -::: - -## Pontos fortes e fracos do CSV - -| Bom | Difícil | -|---|---| -| As tabelas ocupam pouco espaço. | Se o separador aparecer no texto, por exemplo \`Lebre;muito bem; esforçada\`, a linha parte-se em partes a mais. | -| O Excel e outros programas abrem o ficheiro diretamente. | Tudo é uma tabela plana. Se um animal tiver várias comidas preferidas, isso não cabe bem numa célula. | -| Qualquer programa o consegue ler com \`split\`. | O ficheiro não diz se \`1\` é um número ou um texto. | - -::: ludwig -O Excel guarda ficheiros CSV com ponto e vírgula em Portugal e na Alemanha, mas com vírgula em Inglaterra e nos EUA. Por isso, quem abre um ficheiro de outro país às vezes vê tudo numa única coluna. -::: - -## Jogo: Linha estragada - -::: spiel bombe -titel: Jogo: Linha estragada -intro: Em cada ficheiro CSV há **exatamente uma linha estragada**, e a bomba está a contar. Compara cada linha com o cabeçalho e toca na linha estragada antes que o tempo acabe. Cinco rondas, 20 segundos cada. -zeit: 20 -daten: csv - -runde: - Nome;Matemática;Alemão;Inglês - Ludwig;2;3;1 - Zora;1 - Raposa;4;3;5 -frage: Cada linha precisa de quatro colunas. -fehler: 3 -besser: Zora;1;2;2 -> Faltam duas notas à Zora. Então \`partes[3]\` não existe, e o Java para com uma \`ArrayIndexOutOfBoundsException\`. - -runde: - Nome;Nota;Comentário - Ludwig;2;trabalha bem em equipa - Lebre;1;muito bem; esforçada - Zora;1;lê muito -frage: Cada linha precisa de três colunas. -fehler: 3 -besser: Lebre;1;muito bem, esforçada -> O ponto e vírgula no comentário separa uma quarta coluna. O separador não pode aparecer no texto. - -runde: - Nome;Matemática;Alemão;Inglês - Ludwig;2;3;1 - Zora;1;2;2 - Raposa,4,3,5 -frage: O separador é o ponto e vírgula. -fehler: 4 -besser: Raposa;4;3;5 -> Aqui há vírgulas em vez de pontos e vírgulas. \`split(";")\` não encontra nenhum ponto e vírgula e devolve a linha inteira como uma única parte. - -runde: - Nome;Matemática;Alemão;Inglês - Lebre;2;1;3 - Raposa;quatro;3;5 - Ludwig;2;3;1 -frage: As notas são números. -fehler: 3 -besser: Raposa;4;3;5 -> \`Integer.parseInt("quatro")\` não consegue fazer um número a partir da palavra e lança uma \`NumberFormatException\`. - -runde: - Nome;Matemática;Alemão;Inglês - Zora;1;2;2 - Lebre;2;1;3 - Ludwig;2;;1 -frage: Nenhum campo pode estar vazio. -fehler: 4 -besser: Ludwig;2;3;1 -> Entre os dois pontos e vírgulas não há nada. \`Integer.parseInt("")\` lança uma \`NumberFormatException\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar como é feito um ficheiro CSV: cabeçalho, registos, separador. → Uma tabela num ficheiro de texto -- Consigo dividir uma linha com split e fazer dela um número com Integer.parseInt. → Dividir uma linha com split -- Consigo saltar o cabeçalho antes de fazer contas. → Ler o ficheiro inteiro -- Consigo calcular uma média a partir de uma coluna de um ficheiro CSV. → Exercício: Média de Matemática -- Consigo encontrar o melhor valor numa coluna de um ficheiro CSV. → Exercício: Melhor nota a Inglês -- Consigo nomear um ponto forte e um ponto fraco do CSV. → Pontos fortes e fracos do CSV -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve "Lebre;2;1;3".split(";")[2]? -→ Dividir uma linha com split -- [ ] "2" -- [x] "1" -- [ ] 1 -> A contagem começa em 0. E o resultado é um texto, não um número. - -? Porque é que o programa chama readLine() duas vezes antes do ciclo? -→ Ler o ficheiro inteiro -- [ ] Para ler o ficheiro mais depressa. -- [x] A primeira linha é o cabeçalho, sem notas. É saltada. -- [ ] Porque readLine precisa sempre de duas linhas. - -? O que acontece com Integer.parseInt("Matemática")? -→ Dividir uma linha com split -- [ ] Devolve 0. -- [x] O Java lança uma NumberFormatException. -- [ ] Devolve o comprimento da palavra. - -? Liga. -→ Pontos fortes e fracos do CSV -= cabeçalho -> diz o que está nas colunas -= registo -> uma linha com valores -= separador -> separa as colunas, muitas vezes ; -::: -`,Hy=`--- -titel: Dados em JSON -bereich: Ficheiros -kurz: Perceber ficheiros JSON, tirar valores com indexOf e substring e escrever JSON tu mesmo. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- reconhecer objetos, chaves, valores e listas em JSON -- tirar um valor de uma linha JSON com indexOf e substring -- mostrar todos os valores de uma chave de um ficheiro JSON -- construir e guardar uma entrada JSON com + e \\" -- guardar dados num ficheiro JSON -- nomear um ponto forte e um ponto fraco do JSON -::: - -## Dados com nomes - -O zoo do castelo guarda os seus animais num ficheiro \`zoo.json\`: - -::: datei zoo.json -{ - "zoo": "Zoo do Castelo", - "animais": [ - {"nome": "Ludwig", "especie": "bode", "idade": 7}, - {"nome": "Zora", "especie": "cabra", "idade": 6}, - {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4}, - {"nome": "Lebre", "especie": "lebre", "idade": 2} - ] -} -::: - -Isto é **JSON**, a "JavaScript Object Notation". Quase todas as aplicações web e todos os jogos enviam e guardam dados assim. Ao contrário de um ficheiro CSV, antes de cada valor está o seu **nome**, a **chave**. - -| Carácter | Significado | Exemplo | -|---|---|---| -| \`{ … }\` | um **objeto**: pares de chave e valor | \`{"nome": "Zora", "idade": 6}\` | -| \`"chave": valor\` | um par, separado por dois pontos | \`"idade": 6\` | -| \`[ … ]\` | uma **lista** de valores, como um array | \`"animais": [ … ]\` | -| \`,\` | separa os pares e as entradas de uma lista | | -| \`"…"\` | um texto. Números e \`true\`/\`false\` ficam sem aspas. | \`"cabra"\`, \`6\` | - -Objetos e listas podem ser **encaixados** uns nos outros: o objeto do zoo contém a lista \`animais\`, e cada animal é outra vez um objeto. Tal como nos arrays, a lista conta a partir de 0: \`animais[1].nome\` é \`"Zora"\`. - -::: angeberwissen -Foi Douglas Crockford quem tornou o JSON conhecido no início dos anos 2000. Na licença do seu programa de JSON escreveu: “O software deve ser usado para o Bem, não para o Mal.” Por isso, a empresa IBM perguntou se o podia usar na mesma. Ele respondeu que a IBM e os seus clientes também o podiam usar para o Mal. -::: - -## Tirar um valor - -Os profissionais leem JSON com uma biblioteca, por exemplo o **Gson**. Ela não faz parte do Java e tem de ser adicionada primeiro. Para o nosso ficheiro do zoo chegam os métodos de String da lição 17, porque cada animal está na sua própria linha. - -Em Java, escreves umas aspas dentro de um texto como \`\\"\`. Assim, o texto \`"\\"nome\\": \\""\` é \`"nome": "\`, exatamente o que está antes de cada nome. - -\`\`\`java -String linha = "{\\"nome\\": \\"Zora\\", \\"especie\\": \\"cabra\\", \\"idade\\": 6},"; -int inicio = linha.indexOf("\\"nome\\": \\"") + 9; -int fim = linha.indexOf("\\"", inicio); -String nome = linha.substring(inicio, fim); -\`\`\` - -- \`indexOf("\\"nome\\": \\"")\` encontra o início de \`"nome": "\`. São 9 caracteres, por isso o nome começa 9 caracteres mais à frente. -- \`indexOf("\\"", inicio)\` procura as aspas seguintes **a partir de** \`inicio\`. É aí que o nome acaba. -- \`substring(inicio, fim)\` recorta o nome: \`Zora\`. - -::: diagramm dt-json.visustruct -Tirar o nome de uma linha JSON. -::: - -## Exercício: Todos os nomes do zoo - -::: übung l26-java-namen -titel: Todos os nomes do zoo -code: java -auftrag: Lê \`zoo.json\` linha a linha. Em cada linha que contém \`"nome"\`, mostra só o nome. -start: l26-java-namen-start.visustruct -test: - -> Ludwig | Zora | Raposa | Lebre -braucht: whileSchleife, verzweigung -tipp: Verifica com \`linha.contains("\\"nome\\"")\` se há um nome na linha. As outras linhas são saltadas. -tipp: Dentro do if: \`int inicio = linha.indexOf("\\"nome\\": \\"") + 9;\`, depois \`int fim = linha.indexOf("\\"", inicio);\` e \`System.out.println(linha.substring(inicio, fim));\`. -lösung: l26-java-namen-loesung.visustruct -::: - -## Escrever JSON - -Para escrever, juntas o texto com \`+\` e escreves-o no ficheiro com o PrintWriter da lição 23. Os textos em JSON precisam de aspas, portanto \`\\"\` no texto Java, os números não: - -\`\`\`java -String nome = "Zora"; -int pontos = 15; -String entrada = "{\\"nome\\": \\"" + nome + "\\", \\"pontos\\": " + pontos + "}"; -// entrada é {"nome": "Zora", "pontos": 15} -\`\`\` - -Numa lista há uma vírgula entre as entradas, mas **nenhuma** depois da última. Isso verificas com um if. - -::: ludwig -Umas aspas esquecidas ou uma vírgula a mais, e nenhum programa consegue ler o ficheiro. O JSON é rigoroso. Por isso, os profissionais também usam uma biblioteca para escrever. -::: - -## Exercício: Guardar pontos em JSON - -::: übung l26-java-schreiben -titel: Guardar pontos em JSON -code: java -auftrag: Guarda os pontos do torneio de perguntas como lista em \`pontos.json\`. Cada linha contém um objeto com **nome** e **pontos**, e entre os objetos há uma vírgula. No fim, mostra **3 entradas guardadas**. -start: l26-java-schreiben-start.visustruct -test: - -> 3 entradas guardadas -datei: pontos.json -> [ | {"nome": "Ludwig", "pontos": 12}, | {"nome": "Zora", "pontos": 15}, | {"nome": "Raposa", "pontos": 9} | ] -braucht: forSchleife, verzweigung -tipp: Percorre os arrays com \`for (int i = 0; i < nomes.length; i++)\`. Constrói a entrada com \`" {\\"nome\\": \\"" + nomes[i] + "\\", \\"pontos\\": " + pontos[i] + "}"\`. -tipp: Se \`i < nomes.length - 1\`, acrescentas ainda \`","\`. Depois do ciclo, escreve \`"]"\` e fecha o ficheiro. -lösung: l26-java-schreiben-loesung.visustruct -::: - -## Pontos fortes e fracos do JSON - -| Bom | Difícil | -|---|---| -| Cada valor tem um nome. Vê-se logo o que \`6\` quer dizer. | O ficheiro é mais longo do que um CSV, porque as chaves aparecem em todas as linhas. | -| Os dados podem ser encaixados, por exemplo uma lista dentro de um objeto. | O Java não lê JSON sozinho. É precisa uma biblioteca como o Gson. | -| O JSON é o padrão na web. Quase todas as linguagens de programação o conhecem. | Um carácter a mais ou a menos torna o ficheiro inteiro ilegível. | - -## Jogo: Qual é a chave? - -::: spiel frage -titel: Jogo: Qual é a chave? -intro: O Ludwig procura valores em ficheiros JSON. Lê o ficheiro e diz qual é o valor pedido. Cinco rondas. - -runde: - {"nome": "Zora", "especie": "cabra", "idade": 6} -frage: Qual é o valor de **idade**? -- "idade" -- [x] 6 -- "cabra" -> Antes dos dois pontos está a chave, depois está o valor. Os números ficam sem aspas. - -runde: - { - "animais": [ - {"nome": "Ludwig", "idade": 7}, - {"nome": "Zora", "idade": 6}, - {"nome": "Raposa", "idade": 4} - ] - } -frage: O que é **animais[1].nome**? -- Ludwig -- [x] Zora -- Raposa -> A lista conta a partir de 0, como um array. \`animais[1]\` é o segundo animal. - -runde: - { - "zoo": "Zoo do Castelo", - "chefe": {"nome": "Ludwig", "especie": "bode"} - } -frage: Como chegas a **bode**? -- [x] chefe.especie -- especie -- zoo.chefe.especie -> \`especie\` está dentro do objeto \`chefe\`. Por isso o caminho passa por \`chefe\`. - -runde: - { - "nome": "Lebre", - "comidaPreferida": ["cenouras", "trevo", "dentes-de-leão"] - } -frage: O que é **comidaPreferida[2]**? -- trevo -- cenouras -- [x] dentes-de-leão -> O índice 0 é cenouras, o 1 é trevo e o 2 é dentes-de-leão. Algo assim dificilmente cabe numa célula em CSV. - -runde: - {"nome": "Raposa", "idade": 4, "comFome": true} -frage: Que chave tem um valor lógico? -- nome -- idade -- [x] comFome -> \`true\` e \`false\` ficam sem aspas, como os números. Em Java isto seria um \`boolean\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo reconhecer objetos, chaves, valores e listas em JSON. → Dados com nomes -- Consigo tirar um valor de uma linha JSON com indexOf e substring. → Tirar um valor -- Consigo mostrar todos os valores de uma chave de um ficheiro JSON. → Exercício: Todos os nomes do zoo -- Consigo construir e guardar uma entrada JSON com + e \\". → Escrever JSON -- Consigo guardar dados num ficheiro JSON. → Exercício: Guardar pontos em JSON -- Consigo nomear um ponto forte e um ponto fraco do JSON. → Pontos fortes e fracos do JSON -::: - -## Teste de conhecimentos - -::: quiz -? O que está em JSON antes de cada valor? -→ Dados com nomes -- [ ] o número da linha -- [x] a sua chave, por exemplo "nome" -- [ ] um ponto e vírgula -> A chave e o valor estão separados por dois pontos. - -? Como escreves umas aspas dentro de um texto Java? -→ Escrever JSON -- [ ] "" -- [x] \\" -- [ ] '"' - -? Em {"animais": [{"nome": "Ludwig"}, {"nome": "Zora"}]}, qual é o valor de animais[0].nome? -→ Dados com nomes -- [x] Ludwig -- [ ] Zora -- [ ] animais -> A lista conta a partir de 0. - -? Liga. -→ Pontos fortes e fracos do JSON -= { } -> um objeto com chaves e valores -= [ ] -> uma lista -= : -> separa a chave do valor -::: -`,Vy=`--- -titel: Comparar formatos -bereich: Ficheiros -kurz: Comparar os mesmos dados em txt, CSV e JSON, escolher o formato certo e converter um ficheiro noutro formato. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- reconhecer os mesmos dados em txt, CSV e JSON -- comparar pontos fortes e fracos de txt, CSV e JSON -- converter um ficheiro CSV em frases -- converter um ficheiro CSV em JSON -::: - -## Os mesmos animais três vezes - -O Ludwig guardou os animais do castelo em três ficheiros. Todos dizem o mesmo, só que escrito de outra maneira. - -Como texto, tal como se escreveria a uma pessoa: - -::: datei animais.txt -Ludwig (bode) tem 7 anos. -Zora (cabra) tem 6 anos. -Raposa (raposa-vermelha) tem 4 anos. -::: - -Como tabela em CSV: - -::: datei animais.csv -Nome;Espécie;Idade -Ludwig;bode;7 -Zora;cabra;6 -Raposa;raposa-vermelha;4 -::: - -Como JSON: - -::: datei animais.json -[ - {"nome": "Ludwig", "especie": "bode", "idade": 7}, - {"nome": "Zora", "especie": "cabra", "idade": 6}, - {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4} -] -::: - -## O que sabe fazer cada formato? - -| | txt | CSV | JSON | -|---|---|---|---| -| Fácil de ler para pessoas | sim | mais ou menos | mais ou menos | -| Ordem fixa para programas | não | sim, colunas | sim, chaves | -| O que quer dizer um valor? | está na frase | está no cabeçalho | está mesmo antes dele | -| Encaixar (uma lista dentro de uma entrada) | não | difícil | sim | -| Abrir no Excel | não | sim | não | -| Ler em Java | Scanner, BufferedReader | mais \`split\` | métodos de String ou uma biblioteca | -| Tamanho | pequeno | o mais pequeno | o maior | - -Em resumo: - -- **txt** para textos que pessoas leem: cartas, notas, um diário. -- **CSV** para tabelas: notas, medições, uma lista de recordes. Todas as linhas têm as mesmas colunas. -- **JSON** para dados com estrutura: jogos guardados, definições, tudo o que circula entre programas e na web. - -::: ludwig -Quando a tua aplicação de meteorologia vai buscar a temperatura, a resposta chega quase sempre em JSON. E quando um professor exporta as notas do programa da escola, recebe muitas vezes um ficheiro CSV. -::: - -## Exercício: De CSV para frases - -::: übung l27-java-saetze -titel: De CSV para frases -code: java -auftrag: Converte \`animais.csv\` em texto. Para cada animal, escreve uma frase como **Ludwig (bode) tem 7 anos.** no ficheiro \`frases.txt\`. No fim, mostra **3 frases guardadas**. -start: l27-java-saetze-start.visustruct -test: - -> 3 frases guardadas -datei: frases.txt -> Ludwig (bode) tem 7 anos. | Zora (cabra) tem 6 anos. | Raposa (raposa-vermelha) tem 4 anos. -braucht: whileSchleife -tipp: O código inicial já salta o cabeçalho. Divide cada linha com \`String[] partes = linha.split(";");\`. -tipp: A frase é \`partes[0] + " (" + partes[1] + ") tem " + partes[2] + " anos."\`. Conta com \`quantidade++\` e lê a linha seguinte no fim do ciclo. -lösung: l27-java-saetze-loesung.visustruct -::: - -## Exercício: De CSV para JSON - -::: übung l27-java-json -titel: De CSV para JSON -code: java -auftrag: Converte \`animais.csv\` em JSON e guarda-o em \`animais-novos.json\`, exatamente como \`animais.json\` acima. No fim, mostra **3 animais guardados**. -start: l27-java-json-start.visustruct -test: - -> 3 animais guardados -datei: animais-novos.json -> [ | {"nome": "Ludwig", "especie": "bode", "idade": 7}, | {"nome": "Zora", "especie": "cabra", "idade": 6}, | {"nome": "Raposa", "especie": "raposa-vermelha", "idade": 4} | ] -braucht: forSchleife, verzweigung -tipp: \`linhas[0]\` é o cabeçalho. Por isso, começa o ciclo for em \`i = 1\` e divide \`linhas[i]\` com \`split(";")\`. -tipp: Constrói a entrada com \`\\"\`, como na lição 26. A idade é um número e fica sem aspas. Só acrescentas uma vírgula se \`i < linhas.length - 1\`. -lösung: l27-java-json-loesung.visustruct -::: - -## Jogo: Que formato serve? - -::: spiel frage -titel: Jogo: Que formato serve? -intro: O Ludwig quer guardar dados e não sabe que formato usar. Ajuda-o. Seis rondas. -antworten: txt | CSV | JSON - -runde: -frage: As notas da turma inteira devem ser abertas no Excel. -richtig: CSV -> As notas são uma tabela, e o Excel abre ficheiros CSV diretamente. - -runde: -frage: O Ludwig escreve uma carta à Zora. -richtig: txt -> Uma carta é texto para pessoas. Não precisa de colunas nem de chaves. - -runde: -frage: Um jogo guardado com nome, nível e uma lista de todas as chaves apanhadas. -richtig: JSON -> Em JSON, uma lista pode ficar encaixada numa entrada. Em CSV dificilmente cabe numa célula. - -runde: -frage: Um aparelho de medição guarda a hora e a temperatura a cada minuto, durante um ano inteiro. -richtig: CSV -> Muitas linhas iguais com as mesmas colunas: para isto, o CSV é o mais pequeno. O JSON repetiria as chaves em todas as linhas. - -runde: -frage: Uma aplicação de meteorologia vai buscar a previsão a um servidor na internet. -richtig: JSON -> O JSON é o padrão na web. Quase todas as aplicações recebem os dados assim. - -runde: -frage: O diário do castelo, em que cada entrada é uma frase curta. -richtig: txt -> Cada linha é uma frase para pessoas. É lida linha a linha, como na lição 24. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo reconhecer os mesmos dados em txt, CSV e JSON. → Os mesmos animais três vezes -- Consigo comparar pontos fortes e fracos de txt, CSV e JSON. → O que sabe fazer cada formato? -- Consigo converter um ficheiro CSV em frases. → Exercício: De CSV para frases -- Consigo converter um ficheiro CSV em JSON. → Exercício: De CSV para JSON -::: - -## Teste de conhecimentos - -::: quiz -? Que formato o Excel abre diretamente como tabela? -→ O que sabe fazer cada formato? -- [ ] txt -- [x] CSV -- [ ] JSON - -? Porque é que um ficheiro JSON é normalmente maior do que um CSV com os mesmos dados? -→ O que sabe fazer cada formato? -- [ ] Porque o JSON guarda mais números. -- [x] Porque as chaves se repetem em cada entrada. -- [ ] Porque o JSON não usa vírgulas. - -? Em que formato cada valor diz o que significa? -→ O que sabe fazer cada formato? -- [ ] txt -- [ ] CSV -- [x] JSON -> Em JSON a chave está mesmo antes do valor, por exemplo "idade": 7. - -? Liga cada objetivo a um formato. -→ O que sabe fazer cada formato? -= uma carta à Zora -> txt -= a pauta de notas da turma -> CSV -= um jogo guardado com listas -> JSON -::: -`,qy=`--- -titel: A tua primeira janela -bereich: Janelas com Swing -kurz: Abrir uma janela tua com Java e Swing, explicado linha a linha. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- ligar o “Java verdadeiro” na área Código -- abrir uma janela com título e tamanho -- explicar o que faz cada linha do programa com janela -- explicar porque é que setVisible(true) fica no fim -::: - -## Programas com janelas - -Até agora os teus programas escreviam texto na consola. Mas a maioria dos programas que conheces tem **janelas** com botões e campos de texto. A isto chama-se uma interface gráfica. - -O Java traz uma caixa de ferramentas para isso: o **Swing**. No VisuStruct ligas o Swing assim: - -1. Abre à direita a área [Código](#zeige-codelive). -2. Clica na [roda dentada](#zeige-einstellungen) em cima à direita e escolhe Java como **Linguagem de programação**. -3. Por baixo, na secção Java, marca **Java verdadeiro (com Swing)**. - -Na primeira vez que clicas em [▶︎ Executar](#zeige-ausfuehren), o VisuStruct descarrega uma única vez cerca de 40 MB, o que demora um pouco. Depois disso o teu código corre como Java verdadeiro, e as tuas janelas aparecem na área Código na área **Janelas Java**, por cima da consola. - -> Com o “Java verdadeiro”, as entradas pela consola (\`Scanner\`) ainda não funcionam. Para programas com entradas, desliga outra vez a opção. - -::: angeberwissen -O Swing existe desde 1998. Antes disso, o Java só tinha o AWT, mais antigo, com uma classe \`Frame\`. Para ninguém confundir os blocos novos, quase todos os nomes no Swing começam por **J**: \`JFrame\`, \`JButton\`, \`JLabel\`. Pelo J reconheces logo o Swing. -::: - -## O programa completo - -Este é o programa mais pequeno que abre uma janela. Escreve-o na área Código e clica em [▶︎ Executar](#zeige-ausfuehren): - -\`\`\`java -import javax.swing.*; - -public class MinhaJanela { - public static void main(String[] args) { - JFrame janela = new JFrame("A minha primeira janela"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); - } -} -\`\`\` - -Aparece uma janela cinzenta vazia com o título “A minha primeira janela”. Mais nada, mas é a tua própria janela! - -A moldura com \`public class\` e \`main\` já conheces da lição Java. Novas são a primeira linha e as quatro linhas dentro de \`main\`. Vamos vê-las uma a uma. - -::: ludwig -Olha para o estrutograma ao lado do código: cada linha em \`main\` é um bloco, todos uns por baixo dos outros. No início, um programa com janelas é então só uma sequência. -::: - -## Linha a linha - -| Linha | O que faz | -|---|---| -| \`import javax.swing.*;\` | Traz a caixa de ferramentas Swing para o programa. Sem esta linha o Java não conhece \`JFrame\`. | -| \`JFrame janela = new JFrame("A minha primeira janela");\` | Constrói uma janela nova. O texto entre parênteses é o título em cima, na moldura da janela. Depois falas com ela através da variável \`janela\`. | -| \`janela.setSize(300, 200);\` | A janela fica com 300 píxeis de largura e 200 píxeis de altura. | -| \`janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);\` | Quando alguém fecha a janela, o programa também termina. | -| \`janela.setVisible(true);\` | Só agora a janela fica visível. | - -O ponto em \`janela.setSize(…)\` quer dizer: “Janela, faz isto!” Antes do ponto está quem deve fazer alguma coisa, depois do ponto o que deve fazer. - -> Sem \`setDefaultCloseOperation\`, o Java só esconde a janela quando ela é fechada. O programa continua a correr sem se ver. - -::: quiz -? O que quer dizer o ponto em \`janela.setSize(300, 200)\`? -- [x] “Janela, faz isto!”: antes do ponto está quem faz, depois o que faz. -- [ ] Separa os dois números. -- [ ] Termina a linha. -> Antes do ponto está o objeto \`janela\`, depois o seu método \`setSize\`. -::: - -## Porque é que setVisible fica no fim - -\`setVisible(true)\` mostra a janela tal como ela está nesse momento. Por isso preparas tudo primeiro: título, tamanho e, mais tarde, também botões. Só mesmo no fim tornas a janela visível. - -O que só pões na janela depois de \`setVisible(true)\` muitas vezes não aparece. É que o Java não volta a desenhar a janela sozinho. Às vezes só aparece quando se muda o tamanho da janela. Erros destes são difíceis de encontrar. - -::: ludwig -Primeiro preparar, depois mostrar. Como no teatro: a cortina só sobe quando o palco está pronto. -::: - -Experimenta: apaga a linha com \`setVisible(true)\` e executa o programa outra vez. O programa corre, mas não vês nenhuma janela. - -## Classes e objetos - -Isto não está no programa da disciplina, mas ajuda-te a perceber o Swing. - -::: angeberwissen -O Java é, na verdade, uma linguagem **orientada a objetos**. \`JFrame\` é uma classe, ou seja, uma planta para janelas. Com \`new JFrame(…)\` o Java constrói, segundo essa planta, uma janela a sério: um **objeto**. Com dois \`new\` podias construir duas janelas, ambas com a mesma planta. - -\`setSize\` e \`setVisible\` são métodos que qualquer objeto janela sabe fazer. Por isso escreves \`janela.setSize(…)\`: o objeto \`janela\` executa o seu método. O teu próprio programa também é uma classe, neste caso \`MinhaJanela\`. -::: - -## Jogo: O que acontece? - -::: spiel frage -titel: Jogo: O que acontece? -intro: O Ludwig andou a mexer no programa da janela. Lê as linhas e prevê o que acontece. Seis rondas. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); -frage: O que vês depois de executar? -- uma janela com o título "Olá" -- [x] nenhuma janela -- uma mensagem de erro -> Falta \`setVisible(true)\`. A janela está construída, mas fica invisível. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(200, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); -frage: Como é a janela? -- mais larga do que alta -- [x] mais alta do que larga -- quadrada -> O primeiro número é a largura, o segundo a altura: 200 de largura, 300 de altura. - -runde: - JFrame janela = new JFrame("Olá"); - janela.setSize(300, 200); - janela.setVisible(true); -frage: Fechas a janela. O que acontece ao programa? -- Termina. -- [x] Continua a correr, invisível. -- A janela volta logo a abrir. -> Sem \`setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)\`, o Java só esconde a janela. - -runde: - JFrame janela = new JFrame(); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setVisible(true); -frage: O que aparece no topo da moldura da janela? -- janela -- JFrame -- [x] nada, o título está vazio -> Sem texto entre os parênteses, a janela não tem título. O nome da variável \`janela\` nunca aparece na janela. - -runde: - public class Teste { - public static void main(String[] args) { - JFrame janela = new JFrame("Teste"); - janela.setVisible(true); - } - } -frage: O que diz o javac? -- [x] cannot find symbol: class JFrame -- nada, a janela aparece -- missing return statement -> Falta a linha \`import javax.swing.*;\`. Sem ela, o Java não conhece \`JFrame\`. - -runde: - JFrame a = new JFrame("Uma"); - JFrame b = new JFrame("Duas"); - a.setSize(300, 200); - b.setSize(300, 200); - a.setVisible(true); -frage: O que vês? -- [x] uma janela com o título "Uma" -- duas janelas -- uma janela com o título "Duas" -> Duas janelas feitas com a mesma planta, mas só a \`a\` fica visível. -::: - -## Tarefa: a tua própria janela - -Altera o programa de forma que - -- o título diga “Olá” e o teu nome, -- a janela tenha 400 píxeis de largura e 300 píxeis de altura, -- o programa termine quando fechas a janela. - -Executa-o e verifica os três pontos. - -::: ludwig -Não te apetece escrever tanto? Com o “Java verdadeiro” podes escrever na área Código só as quatro linhas de \`main\`. O VisuStruct põe-nas sozinho dentro de uma classe e acrescenta as linhas \`import\`. Num ambiente de desenvolvimento Java a sério precisas, no entanto, do programa completo. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo ligar o “Java verdadeiro” na área Código. → Programas com janelas -- Consigo abrir uma janela com título e tamanho. → O programa completo -- Consigo explicar o que faz cada linha do programa com janela. → Linha a linha -- Consigo explicar porque é que setVisible(true) fica no fim. → Porque é que setVisible fica no fim -::: - -## Teste de conhecimentos - -::: quiz -? Que linha torna a janela visível? -→ Porque é que setVisible fica no fim -- [ ] janela.setSize(300, 200); -- [x] janela.setVisible(true); -- [ ] new JFrame("Olá"); -> Sem setVisible(true) a janela fica invisível. - -? Onde fica melhor janela.setVisible(true)? -→ Porque é que setVisible fica no fim -- [ ] Mesmo no início de main -- [x] No fim, quando a janela já está preparada -- [ ] Tanto faz. -> O que vem depois de setVisible(true) muitas vezes não aparece. - -? O que faz setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE)? -→ Linha a linha -- [ ] A janela fecha-se logo. -- [x] Quando se fecha a janela, o programa termina. -- [ ] A janela já não se pode fechar. - -? Como reconheces os blocos do Swing? -→ Linha a linha -- [x] O nome começa por J, p. ex. JFrame. -- [ ] Estão entre chavetas. -- [ ] Terminam em Window. -::: -`,Uy=`--- -titel: Um botão na janela -bereich: Janelas com Swing -kurz: Pôr um botão na janela e arrumá-lo bem com um layout. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir um botão e pô-lo na janela com add -- explicar porque é que o botão entra antes de setVisible -- usar o FlowLayout para o botão ter o tamanho normal -- construir uma janela com um botão em que se pode carregar -::: - -## Construir um botão - -Uma janela vazia é aborrecida. Agora vai lá para dentro um botão. No Swing, um botão chama-se **JButton** (button é inglês para botão). - -Precisas de duas linhas novas: - -\`\`\`java -JButton botao = new JButton("Clica-me"); -janela.add(botao); -\`\`\` - -- \`new JButton("Clica-me")\` constrói um botão. O texto entre parênteses fica escrito no botão. -- \`janela.add(botao)\` põe o botão na janela. Sem \`add\` o botão existe, mas não está em lado nenhum e não o vês. - -::: quiz -? O que está escrito no botão depois de \`new JButton("Iniciar")\`? -- [ ] botao -- [x] Iniciar -- [ ] JButton -> O texto entre parênteses é a legenda. \`botao\` é só o nome da variável. -::: - -## O programa completo - -As duas linhas ficam **antes** de \`setVisible(true)\`, porque a janela deve primeiro ficar pronta e só depois ser mostrada: - -\`\`\`java -import javax.swing.*; - -public class JanelaBotao { - public static void main(String[] args) { - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JButton botao = new JButton("Clica-me"); - janela.add(botao); - - janela.setVisible(true); - } -} -\`\`\` - -Executa o programa. Surpresa: o botão é enorme e enche a janela toda! - -::: ludwig -A ordem é a mesma da primeira janela: preparar, construir o botão, pô-lo na janela e só no fim mostrar. As linhas em branco no código ajudam-te a ver estas partes. -::: - -## O botão gigante - -A forma como uma janela arruma as suas partes é decidida pelo seu **layout**. Sem mais indicações, um \`JFrame\` põe tudo no meio e torna-o tão grande como a janela inteira. - -Com outro layout o botão fica com o tamanho normal. O **FlowLayout** põe tudo lado a lado, como palavras numa linha, só do tamanho necessário: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class JanelaBotao { - public static void main(String[] args) { - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new FlowLayout()); - - JButton botao = new JButton("Clica-me"); - janela.add(botao); - - janela.setVisible(true); - } -} -\`\`\` - -O \`FlowLayout\` pertence à caixa de ferramentas AWT, mais antiga. Por isso há uma segunda linha em cima: \`import java.awt.*;\`. - -::: angeberwissen -No FlowLayout os blocos correm como texto: se um botão já não cabe na linha, passa para a seguinte. Torna a janela estreita e vê como os botões mudam de linha. Flow é inglês para fluxo. -::: - -## Clicar funciona, mas nada acontece - -Clica no botão. Ele fica um pouco mais escuro enquanto o carregas. O Swing mostra assim que foi carregado. Fora isso, não acontece nada. - -Isto não é um erro: ainda não disseste ao programa **o que** deve acontecer com um clique. Isso aprendes na próxima lição. - -## Jogo: Desativar a bomba - -::: spiel bombe -titel: Jogo: Desativar a bomba -intro: Em cada programa de janela há **exatamente um erro**, e a bomba está a contar. Lê o que o programa deve fazer e toca na linha errada antes de o tempo acabar. Seis rondas, 25 segundos cada. -zeit: 25 - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - JButton botao = new JButton(Clica-me); - janela.add(botao); - janela.setVisible(true); -frage: Deve mostrar um botão "Clica-me". -fehler: 3 -besser: JButton botao = new JButton("Clica-me"); -> A legenda é um texto e precisa de aspas. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - JButton botao = new JButton("OK"); - janela.add(janela); - janela.setVisible(true); -frage: Deve pôr o botão "OK" na janela. -fehler: 4 -besser: janela.add(botao); -> Na janela entra o botão, não a própria janela. - -runde: - import java.swing.*; - import java.awt.*; -frage: Deve trazer o Swing e o FlowLayout para o programa. -fehler: 1 -besser: import javax.swing.*; -> O Swing está em \`javax.swing\`, com x. O FlowLayout vem de \`java.awt\`, sem x. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout); - janela.add(new JButton("OK")); - janela.setVisible(true); -frage: Deve mostrar o botão no tamanho normal. -fehler: 3 -besser: janela.setLayout(new FlowLayout()); -> Depois de \`new\` e do nome vêm sempre parênteses, mesmo que não haja nada lá dentro. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout()); - JButton botao = new Jbutton("OK"); - janela.add(botao); - janela.setVisible(true); -frage: Deve mostrar um botão "OK". -fehler: 4 -besser: JButton botao = new JButton("OK"); -> O Java distingue maiúsculas de minúsculas: \`JButton\` com B maiúsculo. - -runde: - JFrame janela = new JFrame("Botão"); - janela.setSize(300, 200); - janela.setLayout(new FlowLayout()); - janela.add(new JButton("OK")); - janela.setVisible(false); -frage: Deve mostrar a janela com o botão. -fehler: 5 -besser: janela.setVisible(true); -> Com \`false\`, a janela fica invisível. -::: - -## Tarefa: clica-me - -Constrói uma janela com o título “Teste do botão”, com 300 por 200 píxeis e um botão “Clica-me” de tamanho normal. Deve ser possível carregar no botão, mesmo que ainda não aconteça nada. - -Extra: põe um segundo botão “Eu também” ao lado. Por que ordem aparecem os botões? Troca as duas linhas \`add\` e experimenta outra vez. - -**Terminaste quando …** - -- o título da janela diz “Teste do botão”, -- a janela tem 300 píxeis de largura e 200 de altura, -- o botão “Clica-me” só é do tamanho do texto e não enche a janela toda, -- o botão afunda visivelmente quando clicas nele. - -::: ludwig -Cada botão precisa da sua própria variável, por exemplo \`botao\` e \`botao2\`, e do seu próprio \`add\`. O FlowLayout põe os botões pela ordem das linhas \`add\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir um botão e pô-lo na janela com add. → Construir um botão -- Consigo explicar porque é que o botão entra antes de setVisible(true). → O programa completo -- Consigo usar o FlowLayout para o botão ter o tamanho normal. → O botão gigante -- Consigo construir uma janela com um botão em que se pode carregar. → Tarefa: clica-me -::: - -## Teste de conhecimentos - -::: quiz -? O que faz janela.add(botao)? -→ Construir um botão -- [ ] Constrói um botão novo. -- [x] Põe o botão na janela. -- [ ] Torna o botão clicável. -> O botão é construído com new JButton(…). - -? Porque é que, sem FlowLayout, o botão enche a janela toda? -→ O botão gigante -- [ ] Porque o texto é demasiado comprido -- [x] Porque sem layout próprio a janela torna tudo tão grande como ela -- [ ] Porque falta o setSize - -? Que linha import precisas para o FlowLayout? -→ O botão gigante -- [ ] import javax.swing.*; -- [x] import java.awt.*; -- [ ] import java.util.*; -> O FlowLayout vem do AWT, mais antigo. - -? O que acontece nesta lição quando clicas no botão? -→ Tarefa: clica-me -- [ ] O programa termina. -- [x] O botão fica mais escuro por um momento, e mais nada. -- [ ] No botão aparece “Fui clicado”. -> O que deve acontecer com um clique aprendes na próxima lição. -::: -`,Qy=`--- -titel: O botão reage -bereich: Janelas com Swing -kurz: Com um ActionListener decides o que acontece com um clique. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar o que é um evento -- usar addActionListener para decidir o que acontece com um clique -- executar várias instruções com um clique -- construir um botão que muda o seu texto depois do clique -::: - -## Um clique é um evento - -Na lição anterior podias carregar no botão, mas nada acontecia. Num programa com janelas é a pessoa que decide quando acontece alguma coisa: clica, escreve ou fecha uma janela. Cada ação destas chama-se **evento**. - -Por isso o teu programa tem de esperar pelo clique e dar por ele. Para isso registas no botão um ouvinte, um **ActionListener** (inglês para “ouvinte de ações”). A cada clique o botão chama o seu ouvinte, e o ouvinte executa o teu código. - -::: ludwig -Até agora o teu programa corria de cima para baixo, como no estrutograma. O código do ouvinte, pelo contrário, só corre mais tarde, sempre que alguém clica. Quantas vezes isso acontece, ninguém sabe de antemão. -::: - -## A linha com a seta - -É assim que registas o ouvinte: - -\`\`\`java -botao.addActionListener(e -> botao.setText("Fui clicado")); -\`\`\` - -Lido de fora para dentro: - -- \`botao.addActionListener( … )\` regista um ouvinte no botão. -- \`e\` é o evento. Contém, por exemplo, quando foi o clique. Aqui não precisas dele, mas o nome tem de estar lá. -- \`->\` lê-se “então faz”. -- \`botao.setText("Fui clicado")\` é o que acontece com o clique: o botão recebe um texto novo. - -A linha vem depois de \`new JButton(…)\`, porque o botão já tem de existir. E, como tudo o resto, vem antes de \`setVisible(true)\`. - -::: quiz -? O que representa o \`e\` em \`e -> botao.setText(…)\`? -- [x] O evento, ou seja, o clique -- [ ] “Fim” -- [ ] Um erro -> Aqui não precisas de \`e\`, mas o nome tem de estar lá. -::: - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class ClicaMe { - public static void main(String[] args) { - JFrame janela = new JFrame("Clica-me"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new FlowLayout()); - - JButton botao = new JButton("Clica-me"); - botao.addActionListener(e -> botao.setText("Fui clicado")); - janela.add(botao); - - janela.setVisible(true); - } -} -\`\`\` - -Executa-o e clica no botão. Agora diz “Fui clicado”. - -> Se o texto novo for mais comprido do que o antigo, o FlowLayout alarga o botão sozinho. - -## Várias instruções com um clique - -Se com um clique deve acontecer mais do que uma coisa, pões chavetas depois da seta. Lá dentro, cada instrução tem o seu ponto e vírgula, como no corpo de um ciclo: - -\`\`\`java -botao.addActionListener(e -> { - botao.setText("Fui clicado"); - janela.setTitle("Obrigado!"); - System.out.println("Clique!"); -}); -\`\`\` - -\`System.out.println\` escreve na [consola](#zeige-konsole), como de costume. Clica várias vezes: a cada clique aparece uma nova linha “Clique!”. Assim vês que o código do ouvinte corre mesmo a cada clique. - -::: ludwig -Atenção ao fim: primeiro \`}\` fecha o bloco, depois \`)\` fecha o parêntese de \`addActionListener\`, e só então vem o ponto e vírgula. Ou seja, \`});\`. Se faltar alguma coisa, aparece uma indicação por baixo do código. -::: - -## Jogo: Quem está a ouvir? - -::: spiel zuhoerer -titel: Jogo: Quem está a ouvir? -intro: O Ludwig mostra-te código e a janela correspondente. Por baixo vês que botões são clicados, um a seguir ao outro. O que acontece? Depois da tua resposta, o Ludwig reproduz os cliques: vês que ouvinte ouve o evento e que linhas correm a seguir. -frage: O que diz a etiqueta depois dos cliques? - -runde: - JFrame janela = new JFrame("Saudação"); - JLabel texto = new JLabel("Início"); - JButton botao = new JButton("Olá"); - botao.addActionListener(e -> texto.setText("Olá mundo")); -klicks: Olá -- Início -- [x] Olá mundo -- Olá -> O botão tem um ouvinte. Com o clique executa \`texto.setText("Olá mundo")\`, e a etiqueta mostra “Olá mundo”. - -runde: - JFrame janela = new JFrame("Fruta"); - JLabel texto = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> texto.setText("Maçã")); - b.addActionListener(e -> texto.setText("Pera")); -klicks: A, B -- Maçã -- [x] Pera -- Maçã Pera -> Cada botão tem o seu próprio ouvinte. Primeiro o A escreve “Maçã”, depois o B substitui-o por “Pera”. O \`setText\` troca o texto antigo, não acrescenta nada. - -runde: - JFrame janela = new JFrame("Fruta"); - JLabel texto = new JLabel("?"); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - a.addActionListener(e -> texto.setText("Maçã")); -klicks: B -- Maçã -- Pera -- [x] ? -> Ninguém se inscreveu no botão B: não há \`b.addActionListener\`. O clique é um evento, mas ninguém está a ouvir. Por isso o ponto de interrogação fica. - -runde: - JFrame janela = new JFrame("Cores"); - JLabel texto = new JLabel("nenhuma"); - JButton vermelho = new JButton("Vermelho"); - JButton azul = new JButton("Azul"); - vermelho.addActionListener(e -> texto.setText("Azul")); - azul.addActionListener(e -> texto.setText("Vermelho")); -klicks: Vermelho -- Vermelho -- [x] Azul -- nenhuma -> Atenção: o que acontece é decidido pelo código no ouvinte, não pela legenda do botão. O ouvinte do botão “Vermelho” escreve “Azul”. - -runde: - JFrame janela = new JFrame("Botão"); - JLabel texto = new JLabel("Clica, por favor"); - JButton botao = new JButton("Clica-me"); - botao.addActionListener(e -> { - botao.setText("Obrigado!"); - texto.setText("Clicado"); - }); -klicks: Clica-me -frage: O que diz o botão depois do clique? -- Clica-me -- [x] Obrigado! -- Clicado -> O bloco depois da seta tem duas instruções. As duas correm com o clique, uma a seguir à outra: o botão fica com “Obrigado!”, a etiqueta com “Clicado”. - -runde: - JFrame janela = new JFrame("Consola"); - JButton botao = new JButton("Vai"); - botao.addActionListener(e -> System.out.println("Clique!")); -klicks: Vai, Vai, Vai -frage: Quantas linhas “Clique!” há depois na consola? -- 1 -- [x] 3 -- 0 -> O ouvinte corre a **cada** clique, não só no primeiro. Três cliques, três linhas. -::: - -## ActionListener é uma interface - -Isto não está no programa da disciplina. Se quiseres saber o que está por trás da seta, continua a ler. - -::: angeberwissen -\`ActionListener\` é uma **interface**, uma espécie de contrato. Diz: quem quiser ser um ActionListener tem de ter um método \`actionPerformed(ActionEvent e)\`. A cada clique o botão chama exatamente esse método. -::: - -A linha com a seta já conheces do passo 3: - -\`\`\`java -botao.addActionListener(e -> botao.setText("Fui clicado")); -\`\`\` - -Sem a seta escreve-se o ouvinte como uma classe sem nome que cumpre o contrato (uma classe anónima): - -\`\`\`java -botao.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - botao.setText("Fui clicado"); - } -}); -\`\`\` - -Para isso precisas ainda de \`import java.awt.event.*;\` em cima. As duas formas fazem exatamente o mesmo. A seta \`e -> …\` é a forma curta (uma expressão lambda, desde o Java 8): \`e\` é o parâmetro de \`actionPerformed\`, e depois da seta vem o seu corpo. A forma curta funciona porque a interface tem um único método. Assim o Java sabe de que método se trata. - -## Jogo: Contrato cumprido? - -::: spiel vertrag -titel: Jogo: Contrato cumprido? -intro: O contrato \`ActionListener\` exige exatamente um método: \`public void actionPerformed(ActionEvent e)\`. O Ludwig mostra-te código. Cumpre o contrato, de forma a poder ser inscrito num botão? O Ludwig verificou cada resposta com o compilador de Java verdadeiro. -frage: Isto cumpre o contrato ActionListener? -antworten: cumprido | não cumprido - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: cumprido -> Está tudo: \`implements ActionListener\` assina o contrato, e o método chama-se exatamente \`actionPerformed\`, é \`public\`, não devolve nada (\`void\`) e recebe um \`ActionEvent\`. - -runde: - class Saudacao implements ActionListener { - public void actionPerfomed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Olha bem: \`actionPerfomed\` em vez de \`actionPerformed\`, falta um r. Para o Java é um método completamente diferente. O compilador avisa que falta \`actionPerformed\`. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed() { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Falta o parâmetro. O contrato exige \`actionPerformed(ActionEvent e)\`, porque com o clique o botão entrega o evento. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent evento) { - System.out.println("Olá"); - } - } -richtig: cumprido -> O nome do parâmetro escolhe-se livremente. Só conta o tipo \`ActionEvent\`. Em vez de \`e\` também se pode chamar \`evento\`. - -runde: - class Saudacao implements ActionListener { - void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } -richtig: não cumprido -> Falta o \`public\`. Todos os métodos de uma interface são públicos, porque o botão tem de os poder chamar de fora. Sem \`public\` o compilador indica um erro. - -runde: - class Saudacao implements ActionListener { - public int actionPerformed(ActionEvent e) { - return 0; - } - } -richtig: não cumprido -> O contrato diz \`void\`: o método não devolve nada. Com \`int\` como tipo de retorno já não serve. - -runde: - class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - dizOla(); - } - void dizOla() { - System.out.println("Olá"); - } - } -richtig: cumprido -> Métodos a mais são permitidos. O contrato só exige que o \`actionPerformed\` exista, não que não haja mais nada. - -runde: - class Saudacao { - public void actionPerformed(ActionEvent e) { - System.out.println("Olá"); - } - } - botao.addActionListener(new Saudacao()); -richtig: não cumprido -> O método está certo, mas o contrato não foi assinado: falta \`implements ActionListener\`. Num botão, o Java só aceita objetos que cumprem oficialmente o contrato. - -runde: - botao.addActionListener(e -> System.out.println("Olá")); -richtig: cumprido -> A seta é a forma curta. Como o contrato só tem um método, o Java sabe: \`e\` é o parâmetro de \`actionPerformed\`, e depois da seta vem o seu corpo. - -runde: - botao.addActionListener((a, b) -> System.out.println("Olá")); -richtig: não cumprido -> Aqui a seta tem dois parâmetros. Mas o \`actionPerformed\` recebe exatamente um, o evento. Por isso esta forma curta não serve para o contrato. -::: - -## Tarefa: fui clicado - -Agora terminas a tarefa: constrói uma janela com um botão “Clica-me” de tamanho normal. Depois do clique, o botão diz “Fui clicado”. - -Extra: constrói dois botões “Vermelho” e “Azul”. Um clique num botão escreve o seu nome no título da janela, por exemplo “Escolheste Vermelho”. - -**Terminaste quando …** - -- o botão diz “Clica-me” antes do clique, -- depois do clique diz “Fui clicado”, -- o botão não enche a janela toda. - -::: ludwig -Cada botão recebe o seu próprio ouvinte, com o seu próprio \`addActionListener\`. O título mudas com \`janela.setTitle(…)\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar o que é um evento. → Um clique é um evento -- Consigo usar addActionListener para decidir o que acontece com um clique. → A linha com a seta -- Consigo executar várias instruções com um clique. → Várias instruções com um clique -- Consigo construir um botão que muda o seu texto depois do clique. → Tarefa: fui clicado -::: - -## Teste de conhecimentos - -::: quiz -? O que é um ActionListener? -→ Um clique é um evento -- [ ] Um botão com texto -- [x] Um ouvinte que executa o teu código a cada clique -- [ ] Um layout para janelas -> Registas o ouvinte no botão com addActionListener. - -? Quando corre botao.setText("Fui clicado") dentro do ouvinte? -→ A linha com a seta -- [ ] Logo no início do programa -- [x] Sempre que alguém clica no botão -- [ ] Só no primeiro clique -> O código do ouvinte só corre quando o evento acontece. - -? Como termina um ouvinte com chavetas? -→ A linha com a seta -- [ ] };) -- [x] }); -- [ ] );} -> Primeiro fecha o bloco, depois o parêntese de addActionListener, depois o ponto e vírgula. - -? Associa cada parte à sua função. -→ A linha com a seta -= JButton -> um botão -= addActionListener -> registar um ouvinte -= setText -> mudar o texto -= setVisible(true) -> mostrar a janela -::: -`,$y=`--- -titel: Duplicar um número na janela -bereich: Janelas com Swing -kurz: Arrumar uma etiqueta, um campo de texto e um botão com um GridLayout e calcular com o texto do campo de texto. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pôr um JLabel e um JTextField na janela -- explicar porque é que sem layout só se vê a última peça -- arrumar tudo umas por baixo das outras com GridLayout -- calcular com getText, Integer.parseInt e setText -- escrever um programa que não falha com “abc” -::: - -## O objetivo - -Vamos construir uma pequena calculadora com janela: em cima está uma etiqueta, por baixo um campo de texto e um botão **Calcular**. Se escreveres 2 e clicares, o campo de texto mostra 4. Clicas outra vez e passa a 8, depois 16 e assim por diante. - -Para isso precisas de duas peças novas: - -- **JLabel**: uma etiqueta, ou seja, texto simples na janela. -- **JTextField**: um campo de texto onde se pode escrever. - -Como nas últimas lições, marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Três peças na janela - -Pomos as três peças na janela, tal como o botão na lição 29: - -\`\`\`java -import javax.swing.*; - -public class Duplicar { - public static void main(String[] args) { - JFrame janela = new JFrame("Duplicar número"); - janela.setSize(250, 150); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JLabel etiqueta = new JLabel("Escreve um número:"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular"); - janela.add(etiqueta); - janela.add(campo); - janela.add(botao); - - janela.setVisible(true); - } -} -\`\`\` - -Executa o programa. Ups: só se vê o botão gigante. Onde estão a etiqueta e o campo de texto? - -## Porque só se vê o botão - -::: ludwig -Lembras-te do botão gigante? Sem um layout próprio, um \`JFrame\` só tem **um** lugar: o meio. Cada \`add\` põe a peça nova exatamente aí, do tamanho da janela inteira. As três peças ficam por isso umas por cima das outras no mesmo lugar, e a janela só mostra a última que lá puseste: o botão. Assim não consegues usar o programa. -::: - -A solução é outra vez um layout. Desta vez usamos o **GridLayout**. Divide a janela em células do mesmo tamanho, como uma grelha: - -\`\`\`java -janela.setLayout(new GridLayout(0, 1)); -\`\`\` - -Os dois números são linhas e colunas. \`0\` linhas quer dizer “tantas quantas forem precisas”, \`1\` coluna quer dizer: tudo umas por baixo das outras. Para o \`GridLayout\` precisas ainda de \`import java.awt.*;\` lá em cima. - -| Layout | Como arruma | -|---|---| -| sem layout | tudo no meio, umas por cima das outras | -| \`FlowLayout\` | lado a lado como palavras, com mudança de linha quando é preciso | -| \`GridLayout(0, 1)\` | umas por baixo das outras, todas as linhas da mesma altura | - -## A janela com GridLayout - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Duplicar { - public static void main(String[] args) { - JFrame janela = new JFrame("Duplicar número"); - janela.setSize(250, 150); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel etiqueta = new JLabel("Escreve um número:"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular"); - janela.add(etiqueta); - janela.add(campo); - janela.add(botao); - - janela.setVisible(true); - } -} -\`\`\` - -Agora as três estão umas por baixo das outras. Podes escrever no campo de texto e carregar no botão. Mas o programa ainda não calcula nada. - -::: angeberwissen -Com \`new GridLayout(2, 3)\` obténs 2 linhas com 3 colunas cada, ou seja, 6 células do mesmo tamanho. É assim que se constroem, por exemplo, as teclas de uma calculadora. As peças preenchem as células pela ordem das linhas \`add\`, linha a linha da esquerda para a direita. -::: - -## Calcular com o texto do campo de texto - -Com um clique, o programa deve ir buscar o número ao campo de texto, duplicá-lo e voltar a escrevê-lo lá. Isso faz-se com três métodos: - -- \`campo.getText()\` dá-te o texto do campo de texto, por exemplo \`"2"\`. -- \`Integer.parseInt(…)\` transforma-o no número 2. Já conheces isto da lição 22. -- \`campo.setText(…)\` escreve um texto novo no campo de texto. - -Juntamente com o ActionListener da lição 30 fica assim: - -\`\`\`java -botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); -}); -\`\`\` - -\`setText\` precisa de um texto. \`"" + numero * 2\` junta o resultado a um texto vazio e assim transforma o número num texto. - -## Não é um número? Sem falhas! - -Escreve “abc” no campo de texto e clica em Calcular. A janela fica, mas não acontece nada, e na [consola](#zeige-konsole) aparece uma \`NumberFormatException\`. Da lição 22 já sabes o que fazer: - -\`\`\`java -botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } -}); -\`\`\` - -Em vez de um erro na consola, vês agora um aviso na janela. - -::: ludwig -A seta já tem um \`e\` para o evento. Por isso a exceção no \`catch\` precisa de outro nome, aqui \`erro\`. O Java não permite duas variáveis com o mesmo nome. -::: - -::: quiz -? Porque é que a exceção no \`catch\` se chama aqui \`erro\` e não \`e\`? -- [ ] O \`catch\` exige sempre o nome \`erro\`. -- [x] \`e\` já é o evento da seta, e o Java não permite duas variáveis com o mesmo nome. -- [ ] \`e\` só é permitido para números. -> Podes escolher o nome à vontade, só não pode repetir-se. -::: - -## Jogo: O que fica na janela? - -::: spiel frage -titel: Jogo: O que fica na janela? -intro: O Ludwig escreve algo no campo de texto e clica em Calcular. Lê o ouvinte e prevê o que a janela mostra depois. Seis rondas. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - }); -frage: No campo de texto está 21. O que fica lá depois do clique? -- 2121 -- [x] 42 -- 21 -> \`parseInt\` transforma o texto no número 21. O dobro é 42. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - }); -frage: No campo de texto está 21. Clicas duas vezes. O que fica lá depois? -- 42 -- [x] 84 -- 4242 -> Cada clique duplica o que está no campo nesse momento: 21, 42, 84. - -runde: - botao.addActionListener(e -> { - campo.setText(campo.getText() + campo.getText()); - }); -frage: No campo de texto está 21. O que fica lá depois do clique? -- 42 -- [x] 2121 -- 21 -> Sem \`parseInt\` continua a ser texto, e \`+\` junta textos. - -runde: - botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } - }); -frage: No campo de texto está abc. O que acontece com o clique? -- [x] A etiqueta mostra "Escreve um número inteiro!" -- No campo de texto fica abcabc. -- A janela vai abaixo. -> \`parseInt\` lança uma NumberFormatException, e o \`catch\` apanha-a. - -runde: - botao.addActionListener(e -> { - try { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero * 2); - } catch (NumberFormatException erro) { - etiqueta.setText("Escreve um número inteiro!"); - } - }); -frage: No campo de texto está 2.5. O que acontece com o clique? -- No campo de texto fica 5.0. -- No campo de texto fica 5. -- [x] A etiqueta mostra "Escreve um número inteiro!" -> \`Integer.parseInt\` só conhece números inteiros. Para ele, 2.5 não é um número. - -runde: - botao.addActionListener(e -> { - int numero = Integer.parseInt(campo.getText()); - campo.setText("" + numero + numero); - }); -frage: No campo de texto está 5. O que fica lá depois do clique? -- 10 -- [x] 55 -- 5 -> \`"" + numero\` já é um texto, e o segundo \`+\` junta outra vez o 5. Com \`"" + (numero + numero)\` o Java calcula primeiro. -::: - -## Tarefa: duplicar um número - -Junta o programa todo: - -- uma janela com o título “Duplicar número”, com 250 píxeis de largura e 150 píxeis de altura, -- umas por baixo das outras, uma etiqueta, um campo de texto e um botão “Calcular”, -- cada clique duplica o número no campo de texto: 2, 4, 8, 16, … -- com “abc” aparece um aviso na etiqueta em vez de uma falha. - -Extra: um segundo botão “Dividir por 2” divide o número por 2. O que acontece com 5? - -**Terminaste quando …** - -- o título é “Duplicar número” e etiqueta, campo de texto e botão estão uns por baixo dos outros, -- o 2 passa a 4, 8 e 16 com três cliques, -- com “abc” aparece um aviso na etiqueta e o programa continua a funcionar. - -::: ludwig -O botão “Dividir por 2” entra no GridLayout como quarta linha e recebe o seu próprio ActionListener. Com \`int\`, o Java corta as casas decimais ao dividir. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pôr um JLabel e um JTextField na janela. → Três peças na janela -- Consigo explicar porque é que sem layout só se vê a última peça. → Porque só se vê o botão -- Consigo arrumar tudo umas por baixo das outras com GridLayout(0, 1). → A janela com GridLayout -- Consigo calcular com getText, Integer.parseInt e setText. → Calcular com o texto do campo de texto -- Consigo escrever um programa que não falha com “abc”. → Não é um número? Sem falhas! -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que sem layout só se vê o botão? -→ Porque só se vê o botão -- [ ] Porque faltam a etiqueta e o campo de texto -- [x] Porque todas as peças estão no meio umas por cima das outras e a última tapa as outras -- [ ] Porque a janela é demasiado pequena - -? O que significa new GridLayout(0, 1)? -→ A janela com GridLayout -- [ ] Nenhuma linha e uma coluna, por isso a janela fica vazia -- [x] Tantas linhas quantas forem precisas numa coluna, ou seja, tudo umas por baixo das outras -- [ ] Uma linha, tudo lado a lado - -? O que te dá campo.getText()? -→ Calcular com o texto do campo de texto -- [ ] Sempre um número -- [x] O texto do campo de texto -- [ ] O título da janela -> Para um número precisas ainda de Integer.parseInt. - -? Associa cada peça à sua função. -→ Três peças na janela -= JLabel -> etiqueta -= JTextField -> campo de texto para escrever -= JButton -> botão -= GridLayout -> arrumação em grelha -::: -`,Yy=`--- -titel: Contador de cliques com reset -bereich: Janelas com Swing -kurz: Dois botões alteram o mesmo número: criar uma variável para a classe toda. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- explicar porque é que um ouvinte não pode alterar uma variável do main -- criar uma variável static na classe -- fazer com que dois botões alterem o mesmo número -- construir um contador com −1 que nunca fica abaixo de 0 -::: - -## O objetivo - -Uma janela conta cliques: em cima está o número, por baixo dois botões. **+1** soma um, **Reset** volta a pôr o número a 0. - -A novidade desta vez: dois botões trabalham com o **mesmo** número. Para isso precisas de uma variável que os dois ouvintes conheçam. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## A primeira tentativa - -A ideia mais óbvia é criar o número no \`main\`, como de costume, e somá-lo no ouvinte: - -\`\`\`java -int contagem = 0; -botao.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); -}); -\`\`\` - -Se executares isto com o Java verdadeiro, o compilador indica um erro: - -\`\`\` -local variables referenced from a lambda expression must be final or effectively final -\`\`\` - -::: ludwig -O ouvinte só corre mais tarde, a cada clique, muito depois de o \`main\` ter terminado. Por isso pode ler as variáveis do \`main\`, mas não alterá-las. O Java insiste: uma variável do \`main\` que aparece na seta tem de ficar igual. Para um número que muda a cada clique, precisamos de outro lugar. -::: - -## Uma variável para a classe toda - -A solução: a variável **não** vai para o \`main\`, mas diretamente para a classe, por cima do \`main\`. Assim pertence à classe toda, e qualquer ouvinte a pode alterar: - -\`\`\`java -public class Contador { - static int contagem = 0; - - public static void main(String[] args) { - // aqui a janela, como de costume - } -} -\`\`\` - -- \`static\` quer dizer: a variável pertence à própria classe. Como o \`main\` também é \`static\`, aqui é preciso. -- Uma variável destas chama-se **atributo** da classe. Vive tanto tempo como o programa, e não só enquanto o \`main\` corre. - -::: quiz -? Como se chama uma variável que está diretamente na classe e não no \`main\`? -- [ ] Parâmetro -- [x] Atributo -- [ ] Ouvinte -> Um atributo vive tanto tempo como o programa, e não só enquanto o \`main\` corre. -::: - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Contador { - static int contagem = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel numero = new JLabel("0", SwingConstants.CENTER); - JButton mais = new JButton("+1"); - JButton reset = new JButton("Reset"); - - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - reset.addActionListener(e -> { - contagem = 0; - numero.setText("0"); - }); - - janela.add(numero); - janela.add(mais); - janela.add(reset); - janela.setVisible(true); - } -} -\`\`\` - -\`SwingConstants.CENTER\` põe o texto da etiqueta no meio. Clica algumas vezes em +1 e depois em Reset. - -::: angeberwissen -Com \`numero.setFont(new Font("SansSerif", Font.BOLD, 48));\` o número fica grande e a negrito. \`Font\` vem de \`java.awt\`, que já importaste. -::: - -## Jogo: Quem está a contar? - -::: spiel zuhoerer -titel: Jogo: Quem está a contar? -intro: Agora os ouvintes fazem contas com a variável da classe. O Ludwig mostra-te o código, a janela e os cliques. Que número aparece no fim? A seguir o Ludwig reproduz os cliques. -frage: O que diz a etiqueta depois dos cliques? - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); -klicks: +1, +1, +1 -- 1 -- [x] 3 -- 0 -> A variável está na classe e guarda o valor entre os cliques. Cada clique soma um: 1, 2, 3. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - JButton reset = new JButton("Reset"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - reset.addActionListener(e -> { - contagem = 0; - numero.setText("0"); - }); -klicks: +1, +1, Reset, +1 -- 3 -- 0 -- [x] 1 -> Depois de dois cliques aparece 2. O Reset põe a variável a 0, e o último clique volta a contar até 1. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> contagem++); -klicks: +1, +1 -- 2 -- [x] 0 -- 1 -> A variável fica a 2, mas ninguém a escreve na etiqueta. Sem \`numero.setText(…)\` a janela continua a mostrar 0. Calcular e mostrar são duas coisas diferentes. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("Cliques: 0"); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("Cliques: " + contagem + 1); - }); -klicks: +1 -- Cliques: 2 -- Cliques: 1 -- [x] Cliques: 11 -> A armadilha dos parênteses: o Java calcula da esquerda para a direita. \`"Cliques: " + contagem\` dá o texto “Cliques: 1”, e \`+ 1\` junta mais um 1 no fim. - -runde: - static int contagem = 0; - JLabel numero = new JLabel("0"); - JButton um = new JButton("+1"); - JButton dez = new JButton("+10"); - um.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); - dez.addActionListener(e -> { - contagem += 10; - numero.setText("" + contagem); - }); -klicks: +10, +1, +10 -- 12 -- [x] 21 -- 111 -> Os dois ouvintes alteram a mesma variável: 10, depois 11, depois 21. \`contagem += 10\` é a forma curta de \`contagem = contagem + 10\`. -::: - -## Tarefa: contador de cliques - -Reconstrói o contador de cliques e acrescenta: - -- um botão **−1** que subtrai um, -- o número nunca deve ficar abaixo de 0. Se estiver em 0, o −1 não faz nada. - -**Terminaste quando …** - -- **+1** conta mais um e **−1** conta menos um, -- no 0 um clique em **−1** não muda nada, -- **Reset** volta a pôr o número em 0. - -::: ludwig -Para “nunca abaixo de 0”, o ouvinte do −1 precisa de um \`if\`: só se \`contagem > 0\` é que subtrai. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo explicar porque é que um ouvinte não pode alterar uma variável do main. → A primeira tentativa -- Consigo criar uma variável static na classe. → Uma variável para a classe toda -- Consigo fazer com que dois botões alterem o mesmo número. → O programa completo -- Consigo construir um contador com −1 que nunca fica abaixo de 0. → Tarefa: contador de cliques -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que o ouvinte não pode alterar a variável contagem do main? -→ A primeira tentativa -- [ ] Porque no Swing não são permitidos números -- [x] Porque o ouvinte só corre mais tarde e o Java não deixa a seta alterar variáveis do main -- [ ] Porque a contagem fica demasiado grande - -? Onde fica static int contagem = 0;? -→ Uma variável para a classe toda -- [ ] No main, bem em cima -- [x] Na classe, fora do main -- [ ] No ouvinte - -? O que faz SwingConstants.CENTER em new JLabel("0", SwingConstants.CENTER)? -→ O programa completo -- [x] Põe o texto no meio da etiqueta. -- [ ] Põe a janela no meio do ecrã. -- [ ] Aumenta a letra. -::: -`,Ky=`--- -titel: Um semáforo com cores -bereich: Janelas com Swing -kurz: Definir cores de fundo e mudar um semáforo com uma variável de estado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- pintar uma etiqueta com setOpaque e setBackground -- guardar um estado numa variável static -- usar o BorderLayout para pôr algo grande no meio e algo estreito em baixo -- explicar para que servem o FlowLayout, o GridLayout e o BorderLayout -- programar um semáforo com quatro fases -::: - -## O objetivo - -Vamos construir um semáforo: uma área grande acende a vermelho. A cada clique em **Seguinte** muda: vermelho, verde, amarelo e outra vez vermelho. - -As novidades desta vez são as **cores** e um **estado**: o programa tem de se lembrar de que cor está acesa. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Cores no Swing - -As cores estão na classe \`Color\` de \`java.awt\`. Cores já prontas são, por exemplo, \`Color.RED\`, \`Color.GREEN\`, \`Color.YELLOW\`, \`Color.ORANGE\` e \`Color.WHITE\`. - -É assim que pintas uma etiqueta: - -\`\`\`java -JLabel luz = new JLabel("", SwingConstants.CENTER); -luz.setOpaque(true); -luz.setBackground(Color.RED); -\`\`\` - -- \`setBackground(…)\` define a cor de fundo. -- Um \`JLabel\` precisa ainda de \`setOpaque(true)\`. Sem esta linha o fundo é transparente e a cor fica invisível. - -::: ludwig -\`opaque\` é inglês para “opaco”, ou seja, não transparente. Normalmente uma etiqueta é transparente, como letras escritas num vidro. Só com \`setOpaque(true)\` ganha uma área própria que se pode pintar. -::: - -::: quiz -? Que linha pinta de verde o fundo da etiqueta \`luz\`? -- [ ] \`luz.setText(Color.GREEN);\` -- [x] \`luz.setBackground(Color.GREEN);\` -- [ ] \`luz.setColor("verde");\` -> A cor de fundo define-se com \`setBackground\`. Para se ver, um \`JLabel\` precisa antes de \`setOpaque(true)\`. -::: - -## Lembrar a cor - -Para o semáforo saber o que vem a seguir, guarda o seu estado num número: 0 é vermelho, 1 é verde, 2 é amarelo. Tal como no contador de cliques da lição 32, a variável fica na classe, para o ouvinte a poder alterar: - -\`\`\`java -static int fase = 0; -\`\`\` - -A cada clique a fase avança um. Depois do amarelo (2) vem outra vez o vermelho (0): - -\`\`\`java -seguinte.addActionListener(e -> { - fase++; - if (fase > 2) { - fase = 0; - } - if (fase == 0) { - luz.setBackground(Color.RED); - } else if (fase == 1) { - luz.setBackground(Color.GREEN); - } else { - luz.setBackground(Color.YELLOW); - } -}); -\`\`\` - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Semaforo { - static int fase = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Semáforo"); - janela.setSize(200, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new BorderLayout()); - - JLabel luz = new JLabel("", SwingConstants.CENTER); - luz.setOpaque(true); - luz.setBackground(Color.RED); - JButton seguinte = new JButton("Seguinte"); - - seguinte.addActionListener(e -> { - fase++; - if (fase > 2) { - fase = 0; - } - if (fase == 0) { - luz.setBackground(Color.RED); - } else if (fase == 1) { - luz.setBackground(Color.GREEN); - } else { - luz.setBackground(Color.YELLOW); - } - }); - - janela.add(luz, BorderLayout.CENTER); - janela.add(seguinte, BorderLayout.SOUTH); - janela.setVisible(true); - } -} -\`\`\` - -Aqui entra um layout novo, o **BorderLayout**. Tem cinco lugares: \`NORTH\` (em cima), \`SOUTH\` (em baixo), \`WEST\` (à esquerda), \`EAST\` (à direita) e \`CENTER\` (no meio). O meio fica com todo o espaço que sobra. Assim a luz fica grande e o botão fica estreito em baixo. - -::: angeberwissen -O BorderLayout é o layout que um \`JFrame\` usa quando não lhe dás nenhum. Um \`add\` sem indicação de lugar vai para o \`CENTER\`. É por isso que na lição 31 as peças ficaram todas umas por cima das outras. -::: - -::: angeberwissen -Os profissionais avançam a fase numa linha: \`fase = (fase + 1) % 3;\`. O operador de resto \`%\` dá o resto da divisão. 3 % 3 é 0, por isso depois do 2 recomeça no 0. -::: - -## Que layout para quê? - -Já conheces três layouts. Cada um arruma os blocos de outra maneira: - -| Layout | Como arruma | Bom para | -|---|---|---| -| \`FlowLayout\` (lição 29) | lado a lado, como palavras numa linha, cada bloco do tamanho necessário | alguns botões numa fila | -| \`GridLayout\` (lição 31) | uma grelha de células do mesmo tamanho, preenchida linha a linha | formulários, teclados como o da calculadora | -| \`BorderLayout\` (esta lição) | cinco lugares: em cima, em baixo, à esquerda, à direita e o meio, que fica com o resto | algo grande no meio, por exemplo uma imagem ou um mostrador, com botões na margem | - -Sem \`setLayout\`, uma janela usa o BorderLayout. É por isso que, nesse caso, tudo vai para o meio. - -::: ludwig -Ao planear, pergunta-te: todas as partes devem ter o mesmo tamanho? Então GridLayout. Uma parte deve ser grande e o resto estreito na margem? Então BorderLayout. As partes devem simplesmente manter o tamanho normal? Então FlowLayout. -::: - -## Jogo: Onde fica o botão? - -::: spiel layout -titel: Jogo: Onde fica o botão? -intro: Já conheces três layouts e a janela sem layout nenhum. O Ludwig mostra-te código com os botões A, B, C … Como fica a janela? Toca na imagem certa. -frage: Como fica a janela? - -runde: - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- flow: A B C -- grid: A / B / C -- [x] border: C=C -> Sem layout próprio, cada \`add\` vai para o meio, do tamanho da janela. Os três ficam uns por cima dos outros e só se vê o último: C. - -runde: - janela.setLayout(new FlowLayout()); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- [x] flow: A B C -- grid: A B C -- border: C=C -> O FlowLayout põe os botões no tamanho normal lado a lado, como palavras numa linha, e centra-os. - -runde: - janela.setLayout(new GridLayout(0, 1)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- flow: A B C -- [x] grid: A / B / C -- grid: A B C -> Uma coluna e tantas linhas quantas forem precisas: tudo uns por baixo dos outros, cada linha com a mesma altura e tão larga como a janela. - -runde: - janela.setLayout(new GridLayout(0, 2)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); - janela.add(new JButton("D")); -- grid: A C / B D -- [x] grid: A B / C D -- grid: A / B / C / D -> Duas colunas. As células são preenchidas linha a linha, sempre primeiro à esquerda e depois à direita: A B, depois C D. - -runde: - janela.setLayout(new GridLayout(2, 3)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); - janela.add(new JButton("D")); - janela.add(new JButton("E")); - janela.add(new JButton("F")); -- grid: A B / C D / E F -- [x] grid: A B C / D E F -- flow: A B C D E F -> Primeiro vêm as linhas, depois as colunas: 2 linhas com 3 colunas cada. - -runde: - janela.setLayout(new GridLayout(0, 2)); - janela.add(new JButton("A")); - janela.add(new JButton("B")); - janela.add(new JButton("C")); -- [x] grid: A B / C -- grid: A B C -- grid: A / B C -> Duas colunas, três botões: a primeira linha fica cheia, na segunda o C fica à esquerda e a célula da direita fica vazia. - -runde: - janela.setLayout(new BorderLayout()); - janela.add(new JButton("A"), BorderLayout.NORTH); - janela.add(new JButton("B"), BorderLayout.CENTER); - janela.add(new JButton("C"), BorderLayout.SOUTH); -- grid: A / B / C -- [x] border: N=A C=B S=C -- border: N=C C=B S=A -> NORTH é em cima, SOUTH em baixo. O meio fica com todo o espaço que sobra, por isso o B é grande. - -runde: - janela.setLayout(new BorderLayout()); - janela.add(new JButton("A"), BorderLayout.WEST); - janela.add(new JButton("B")); - janela.add(new JButton("C"), BorderLayout.EAST); -- flow: A B C -- border: N=A C=B S=C -- [x] border: W=A C=B E=C -> WEST é à esquerda, EAST à direita. O B não tem lugar indicado e por isso vai para o CENTER. -::: - -## Tarefa: semáforo - -Reconstrói o semáforo e torna-o mais realista. Em alguns países, como a Alemanha, depois do vermelho acendem durante pouco tempo o vermelho e o amarelo juntos. Acrescenta por isso uma quarta fase: - -- 0: vermelho -- 1: vermelho e amarelo (usa \`Color.ORANGE\`) -- 2: verde -- 3: amarelo - -Extra: em cada fase, escreve também com \`luz.setText(…)\` o que o semáforo quer dizer, por exemplo “Parar” ou “Avançar”. - -**Terminaste quando …** - -- cada clique mostra a fase seguinte: vermelho, vermelho-amarelo (laranja), verde, amarelo, -- depois do amarelo volta o vermelho, -- no extra, cada fase mostra o texto certo. - -::: ludwig -Com quatro fases, depois do 3 vem o 0. Ajusta por isso o número em \`if (fase > 2)\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo pintar uma etiqueta com setOpaque e setBackground. → Cores no Swing -- Consigo guardar um estado numa variável static. → Lembrar a cor -- Consigo usar o BorderLayout para pôr algo grande no meio e algo estreito em baixo. → O programa completo -- Consigo explicar para que servem o FlowLayout, o GridLayout e o BorderLayout. → Que layout para quê? -- Consigo programar um semáforo com quatro fases. → Tarefa: semáforo -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que a cor fica invisível sem setOpaque(true)? -→ Cores no Swing -- [ ] Porque Color.RED não existe -- [x] Porque, sem isso, uma etiqueta é transparente -- [ ] Porque a janela é demasiado pequena - -? Que lugar do BorderLayout fica com o espaço que sobra? -→ O programa completo -- [ ] NORTH -- [ ] SOUTH -- [x] CENTER - -? fase é 2 e o ouvinte executa fase++ e depois if (fase > 2) { fase = 0; }. Que valor tem fase a seguir? -→ Lembrar a cor -- [x] 0 -- [ ] 2 -- [ ] 3 -> Depois de fase++ é 3, que é maior do que 2, por isso passa a 0. -::: -`,Xy=`--- -titel: Calculadora de notas na janela -bereich: Janelas com Swing -kurz: Escrever a percentagem, determinar o nível com else if e apanhar entradas erradas. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- transformar uma percentagem num nível com else if -- mostrar o nível na janela -- apanhar e avisar “abc” e valores fora de 0 a 100 -- testar um programa nos limites, por exemplo com 90 e 89 -::: - -## O objetivo - -Uma janela transforma a percentagem de um teste num nível: escreves a percentagem (0 a 100), clicas em **Calcular nível** e em baixo aparece o nível. - -Desta vez não há nenhuma peça nova do Swing. A novidade é que combinas tudo o que já sabes: campo de texto, botão, \`else if\` da lição 20 e try/catch da lição 22. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## A escala de níveis - -Usamos a escala de níveis do ensino básico: - -| Percentagem | Nível | -|---|---| -| 90 a 100 | 5 | -| 70 a 89 | 4 | -| 50 a 69 | 3 | -| 20 a 49 | 2 | -| 0 a 19 | 1 | - -Com \`else if\` verificas de cima para baixo. Ganha a primeira condição verdadeira: - -\`\`\`java -int nivel; -if (percentagem >= 90) { - nivel = 5; -} else if (percentagem >= 70) { - nivel = 4; -} else if (percentagem >= 50) { - nivel = 3; -} else if (percentagem >= 20) { - nivel = 2; -} else { - nivel = 1; -} -\`\`\` - -::: ludwig -Com 75 %, \`percentagem >= 90\` é falso, mas \`percentagem >= 70\` é verdadeiro. Portanto nível 4, e as restantes condições já nem são verificadas. É por isso que basta sempre o limite de baixo. -::: - -Podes experimentar esta parte sem janela na área [Código](#zeige-codelive). Escreve \`int percentagem = 75;\` antes e \`System.out.println("Nível: " + nivel);\` depois. O estrutograma mostra-te as decisões encaixadas. - -::: quiz -? Que nota dá a tabela com 29 pontos? -- [ ] 4 -- [ ] 5 -- [x] 6 -> Todas as condições são falsas, também \`pontos >= 30\`. Então vale o ramo \`else\`. -::: - -## A janela - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class CalculadoraNiveis { - public static void main(String[] args) { - JFrame janela = new JFrame("Calculadora de níveis"); - janela.setSize(260, 180); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel pergunta = new JLabel("Percentagem (0 a 100):"); - JTextField campo = new JTextField(); - JButton botao = new JButton("Calcular nível"); - JLabel resultado = new JLabel("Nível: ?"); - - botao.addActionListener(e -> { - int percentagem = Integer.parseInt(campo.getText()); - int nivel; - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 20) { - nivel = 2; - } else { - nivel = 1; - } - resultado.setText("Nível: " + nivel); - }); - - janela.add(pergunta); - janela.add(campo); - janela.add(botao); - janela.add(resultado); - janela.setVisible(true); - } -} -\`\`\` - -Experimenta 100, 75, 50 e 12. - -## Apanhar entradas erradas - -Ainda há duas coisas que podem correr mal: - -1. Alguém escreve “abc”. Então \`Integer.parseInt\` lança uma \`NumberFormatException\`. -2. Alguém escreve 150 ou −5. É um número, mas não é uma percentagem válida. - -O primeiro caso apanhas com try/catch, o segundo com um \`if\` antes de calcular o nível: - -\`\`\`java -botao.addActionListener(e -> { - try { - int percentagem = Integer.parseInt(campo.getText()); - if (percentagem < 0 || percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - // aqui o cálculo do nível de cima - } - } catch (NumberFormatException erro) { - resultado.setText("Isso não é um número."); - } -}); -\`\`\` - -::: ludwig -\`||\` quer dizer “ou” (lição 9). A condição é verdadeira se a percentagem for demasiado pequena **ou** demasiado grande. -::: - -## Jogo: Que nível? - -::: spiel frage -titel: Jogo: Que nível? -intro: O Ludwig construiu calculadoras de níveis, mas nem todas estão certas. Calcula que nível sai. Seis rondas. - -runde: - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else { - nivel = 2; - } -frage: percentagem é 70. Que nível sai? -- 5 -- [x] 4 -- 3 -> 70 >= 90 é falso, 70 >= 70 é verdadeiro. O limite ainda pertence ao 4. - -runde: - if (percentagem >= 90) { - nivel = 5; - } else if (percentagem >= 70) { - nivel = 4; - } else if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 20) { - nivel = 2; - } else { - nivel = 1; - } -frage: percentagem é 49. Que nível sai? -- 3 -- [x] 2 -- 1 -> 49 >= 50 é falso por pouco, 49 >= 20 é verdadeiro. Portanto nível 2. - -runde: - if (percentagem >= 50) { - nivel = 3; - } else if (percentagem >= 90) { - nivel = 5; - } else { - nivel = 1; - } -frage: percentagem é 95. Que nível sai? -- 5 -- [x] 3 -- 1 -> Ganha a primeira condição verdadeira. 95 >= 50 já é verdadeiro, o 5 nunca é alcançado. Por isso verifica-se das percentagens altas para baixo. - -runde: - nivel = 1; - if (percentagem >= 20) { - nivel = 2; - } - if (percentagem >= 50) { - nivel = 3; - } -frage: percentagem é 80. Que valor tem nivel depois? -- 1 -- 2 -- [x] 3 -> Sem \`else\`, todos os \`if\` são verificados. Os dois são verdadeiros, e o último dá o nível 3. - -runde: - // nivel já está calculado - if (percentagem < 0 || percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - resultado.setText("Nível: " + nivel); - } -frage: No campo de texto está 100. O que mostra a janela? -- [x] Nível: 5 -- Escreve um valor de 0 a 100. -- Isso não é um número. -> 100 > 100 é falso. Portanto 100 é válido e dá o nível 5. - -runde: - // nivel já está calculado - if (percentagem < 0 && percentagem > 100) { - resultado.setText("Escreve um valor de 0 a 100."); - } else { - resultado.setText("Nível: " + nivel); - } -frage: No campo de texto está 150. O que mostra a janela? -- Escreve um valor de 0 a 100. -- [x] um nível, embora 150 não seja válido -- Isso não é um número. -> Com \`&&\`, a percentagem teria de ser ao mesmo tempo menor que 0 e maior que 100. Isso nunca acontece, por isso o programa nunca avisa. O certo é \`||\`. -::: - -## Tarefa: calculadora de níveis - -Junta a calculadora de níveis, com as duas verificações do último passo. Testa com 90, 89, 0, 100, 101 e “dez”. - -Extra: escreve também a menção do nível, por exemplo “Nível: 4 (Bom)”. As menções são Muito bom, Bom, Suficiente, Insuficiente e Fraco. - -**Terminaste quando …** - -- 92 e 100 dão 1, 91 dá 2 e 0 dá 6, -- 101 mostra o aviso para escrever 0 a 100, -- um texto como “dez” mostra um aviso e o programa continua a funcionar. - -::: ludwig -Testa sempre os limites: 90 tem de dar 5, 89 tem de dar 4. É nos limites que se escondem a maioria dos erros, por exemplo \`>\` em vez de \`>=\`. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo transformar uma percentagem num nível com else if. → A escala de níveis -- Consigo mostrar o nível na janela. → A janela -- Consigo apanhar e avisar “abc” e valores fora de 0 a 100. → Apanhar entradas erradas -- Consigo testar um programa nos limites, por exemplo com 90 e 89. → Tarefa: calculadora de níveis -::: - -## Teste de conhecimentos - -::: quiz -? Que nível dá a escala com 70 %? -→ A escala de níveis -- [ ] 3 -- [x] 4 -- [ ] 5 -> 70 é o limite de baixo do nível 4. - -? Porque é que em cada condição basta o limite de baixo? -→ A escala de níveis -- [x] Porque o else if já separou antes os casos mais altos -- [ ] Porque o Java não conhece limites de cima -- [ ] Porque a percentagem é sempre 100 - -? Que condição deteta percentagens inválidas? -→ Apanhar entradas erradas -- [ ] percentagem < 0 && percentagem > 100 -- [x] percentagem < 0 || percentagem > 100 -- [ ] percentagem >= 0 || percentagem <= 100 -> Um número não pode ser ao mesmo tempo menor do que 0 e maior do que 100, por isso é preciso o ou. -::: -`,e9=`--- -titel: Uma calculadora -bereich: Janelas com Swing -kurz: Duas colunas com GridLayout, quatro botões de cálculo e apanhar a divisão por 0. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir duas colunas com GridLayout -- explicar porque é que "" + (a + b) precisa de parênteses -- programar os quatro botões de cálculo -- construir uma calculadora que não falha com 0 nem com “abc” -::: - -## O objetivo - -Vamos construir uma pequena calculadora: dois campos de texto para os números, quatro botões **+**, **-**, **\\*** e **/** e uma linha para o resultado. - -As novidades desta vez são **duas colunas** no GridLayout e **vários botões** que calculam todos com os mesmos campos de texto. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Duas colunas - -Até agora, com \`GridLayout(0, 1)\`, ficava tudo umas por baixo das outras. Com \`GridLayout(0, 2)\` há duas colunas. As peças preenchem as células pela ordem das linhas \`add\`, sempre primeiro à esquerda e depois à direita: - -| esquerda | direita | -|---|---| -| Primeiro número: | campo de texto | -| Segundo número: | campo de texto | -| + | - | -| \\* | / | -| Resultado: | ? | - -As etiquetas da esquerda não vais precisar delas mais tarde. Por isso podes construí-las logo dentro do \`add\`, sem variável: - -\`\`\`java -janela.add(new JLabel("Primeiro número:")); -janela.add(campo1); -\`\`\` - -## A janela com o primeiro botão - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Calculadora { - public static void main(String[] args) { - JFrame janela = new JFrame("Calculadora"); - janela.setSize(280, 220); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 2)); - - JTextField campo1 = new JTextField(); - JTextField campo2 = new JTextField(); - JButton mais = new JButton("+"); - JButton menos = new JButton("-"); - JButton vezes = new JButton("*"); - JButton dividir = new JButton("/"); - JLabel resultado = new JLabel("?"); - - mais.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a + b)); - }); - - janela.add(new JLabel("Primeiro número:")); - janela.add(campo1); - janela.add(new JLabel("Segundo número:")); - janela.add(campo2); - janela.add(mais); - janela.add(menos); - janela.add(vezes); - janela.add(dividir); - janela.add(new JLabel("Resultado:")); - janela.add(resultado); - janela.setVisible(true); - } -} -\`\`\` - -Escreve 3 e 4 e clica em +. Em baixo aparece 7. - -::: ludwig -Atenção aos parênteses em \`"" + (a + b)\`. Sem eles, o Java calcula da esquerda para a direita: \`"" + a\` dá o texto “3”, e \`"3" + b\` junta o 4 no fim. Sai “34” em vez de 7. O parêntese garante que primeiro se faz a conta. -::: - -## Os outros três botões - -Os botões -, \\* e / funcionam da mesma maneira. Só muda o sinal na última linha: - -\`\`\`java -menos.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a - b)); -}); -\`\`\` - -O / tem duas particularidades: - -- Com \`int\`, o Java corta as casas decimais ao dividir. 7 / 2 dá 3. -- Se o segundo número for 0, o Java lança uma \`ArithmeticException\`, como na tarefa “Dividir em segurança” da lição 22. - -## Apanhar a divisão por 0 - -\`\`\`java -dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } -}); -\`\`\` - -::: angeberwissen -Com \`double\` e \`Double.parseDouble(…)\` há casas decimais: 7 / 2 dá 3.5. Em contrapartida, com números decimais o Java não lança nenhuma exceção ao dividir por 0. O resultado é então \`Infinity\`, ou seja, “infinito”. -::: - -::: quiz -? O que mostra a calculadora com 8 a dividir por 0? -- [ ] 0 -- [ ] Infinity -- [x] Não se divide por 0! -> Com \`int\`, \`a / b\` lança uma \`ArithmeticException\`. O \`catch\` apanha-a e mostra o aviso. -::: - -## Um método próprio - -Isto não está no programa da disciplina, mas torna o código mais curto. - -::: angeberwissen -Cada ouvinte lê os mesmos dois campos. Podes passar esta repetição para um método próprio, um subprograma como na lição 18. Fica na classe, ao lado do \`main\`: - -\`static int numero(JTextField campo) { return Integer.parseInt(campo.getText()); }\` - -No ouvinte basta então escrever \`resultado.setText("" + (numero(campo1) + numero(campo2)));\`. -::: - -## Jogo: O que mostra a calculadora? - -::: spiel frage -titel: Jogo: O que mostra a calculadora? -intro: O Ludwig andou a mexer na calculadora. Lê o ouvinte e prevê o que aparece no resultado. Seis rondas. - -runde: - mais.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + a + b); - }); -frage: Escreves 3 e 4 e clicas em +. O que aparece no resultado? -- 7 -- [x] 34 -- 12 -> Sem parênteses, o Java trabalha da esquerda para a direita: \`"" + 3\` é o texto "3", e \`+ 4\` junta o 4 no fim. - -runde: - vezes.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + a * b); - }); -frage: Escreves 3 e 4 e clicas em *. O que aparece no resultado? -- 34 -- [x] 12 -- 7 -> A multiplicação vem antes da soma também aqui: o Java calcula primeiro 3 * 4 e depois junta o 12 ao texto. O mais não tem esta prioridade, por isso precisa dos parênteses. - -runde: - dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - resultado.setText("" + (a / b)); - }); -frage: Escreves 7 e 2 e clicas em /. O que aparece no resultado? -- 3.5 -- [x] 3 -- 4 -> Com \`int\`, o Java corta as casas decimais. Não arredonda. - -runde: - dividir.addActionListener(e -> { - int a = Integer.parseInt(campo1.getText()); - int b = Integer.parseInt(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } - }); -frage: Escreves 8 e 0 e clicas em /. O que aparece no resultado? -- 0 -- [x] Não se divide por 0! -- Infinity -> Com \`int\`, dividir por 0 lança uma ArithmeticException, e o \`catch\` escreve o aviso. - -runde: - dividir.addActionListener(e -> { - double a = Double.parseDouble(campo1.getText()); - double b = Double.parseDouble(campo2.getText()); - resultado.setText("" + (a / b)); - }); -frage: Escreves 7 e 2 e clicas em /. O que aparece no resultado? -- 3 -- [x] 3.5 -- 3.0 -> Com \`double\`, as casas decimais ficam. - -runde: - dividir.addActionListener(e -> { - double a = Double.parseDouble(campo1.getText()); - double b = Double.parseDouble(campo2.getText()); - try { - resultado.setText("" + (a / b)); - } catch (ArithmeticException erro) { - resultado.setText("Não se divide por 0!"); - } - }); -frage: Escreves 8 e 0 e clicas em /. O que aparece no resultado? -- Não se divide por 0! -- [x] Infinity -- 0.0 -> Com \`double\` não há exceção. O resultado é \`Infinity\`, ou seja "infinito", e o \`catch\` nunca chega a entrar. -::: - -## Tarefa: calculadora - -Acaba a calculadora: - -- os quatro botões calculam, -- o / avisa “Não se divide por 0!” em vez de falhar, -- se um campo tiver “abc” ou estiver vazio, aparece “Escreve dois números”. - -Extra: um quinto botão **C** esvazia os dois campos e volta a pôr o resultado em “?”. - -**Terminaste quando …** - -- 7 e 2 dão 9, 5, 14 e 3 em +, −, * e /, -- 7 / 0 mostra a mensagem de não dividir por 0, -- “abc” ou um campo vazio mostra a mensagem para escrever dois números e nada falha. - -::: ludwig -Para o “abc” cada ouvinte precisa de um try/catch com \`NumberFormatException\`. Depois de um \`try\` também podem vir dois \`catch\`, um para \`NumberFormatException\` e outro para \`ArithmeticException\`. Já agora: um campo vazio dá o texto \`""\`, e isso também não é um número. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir duas colunas com GridLayout(0, 2). → Duas colunas -- Consigo explicar porque é que "" + (a + b) precisa de parênteses. → A janela com o primeiro botão -- Consigo programar os quatro botões de cálculo. → Os outros três botões -- Consigo construir uma calculadora que não falha com 0 nem com “abc”. → Tarefa: calculadora -::: - -## Teste de conhecimentos - -::: quiz -? a é 3 e b é 4. Quanto dá "" + a + b? -→ A janela com o primeiro botão -- [ ] 7 -- [x] 34 -- [ ] Um erro -> O Java calcula da esquerda para a direita: primeiro surge o texto "3", depois junta-se o 4. - -? Quanto dá 7 / 2 com int? -→ Os outros três botões -- [ ] 3.5 -- [x] 3 -- [ ] 4 - -? Por que ordem preenche o GridLayout(0, 2) as células? -→ Duas colunas -- [x] Linha a linha, primeiro à esquerda e depois à direita -- [ ] Coluna a coluna, começando de cima para baixo na coluna da esquerda -- [ ] Ao acaso -::: -`,t9=`--- -titel: Adivinha o número -bereich: Janelas com Swing -kurz: Um jogo com número aleatório, contagem de tentativas e um botão que se desliga no fim. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- criar com Math.random um número inteiro aleatório de 1 a 100 -- decidir que variáveis pertencem à classe -- desligar um botão com setEnabled -- construir um jogo de adivinhar com um botão “Novo jogo” -::: - -## O objetivo - -Agora um jogo: o computador pensa num número de 1 a 100. Tu adivinhas, e a janela diz-te “demasiado pequeno” ou “demasiado grande”. Quando o encontrares, diz-te quantas tentativas precisaste. - -As novidades desta vez são os **números aleatórios** e um botão que se **desliga** no fim. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Um número aleatório - -\`Math.random()\` dá um número decimal aleatório de 0 até quase 1, por exemplo 0.4173. Em três passos transforma-se num número inteiro de 1 a 100: - -\`\`\`java -int secreto = (int) (Math.random() * 100) + 1; -\`\`\` - -| Passo | Exemplo | -|---|---| -| \`Math.random()\` | 0.4173 | -| \`* 100\` | 41.73 | -| \`(int)\` corta as casas decimais | 41 | -| \`+ 1\` | 42 | - -Sem o \`+ 1\` sairia de 0 a 99. - -Experimenta na área [Código](#zeige-codelive): \`int secreto = (int) (Math.random() * 100) + 1;\` e \`System.out.println(secreto);\`. Cada execução dá um número diferente. - -::: angeberwissen -Na verdade, os computadores não sabem lançar dados. O \`Math.random()\` calcula com uma fórmula uma longa sequência de números que só **parecem** aleatórios. Chamam-se números pseudoaleatórios. Para jogos chega perfeitamente. -::: - -## O que o jogo guarda - -O jogo tem de se lembrar de duas coisas ao longo de todos os cliques: o número secreto e o número de tentativas. Tal como no contador de cliques da lição 32, as duas ficam na classe: - -\`\`\`java -static int secreto = (int) (Math.random() * 100) + 1; -static int tentativas = 0; -\`\`\` - -::: quiz -? Porque é que \`secreto\` e \`tentativas\` estão na classe e não no \`main\`? -- [ ] Porque \`Math.random()\` só funciona na classe -- [x] Porque têm de durar ao longo de todos os cliques e o ouvinte tem de alterar \`tentativas\` -- [ ] Porque o \`main\` não consegue guardar números -> Como no contador de cliques: o ouvinte não pode alterar variáveis do \`main\`. -::: - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class AdivinhaNumero { - static int secreto = (int) (Math.random() * 100) + 1; - static int tentativas = 0; - - public static void main(String[] args) { - JFrame janela = new JFrame("Adivinha o número"); - janela.setSize(320, 180); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JLabel dica = new JLabel("Estou a pensar num número de 1 a 100."); - JTextField campo = new JTextField(); - JButton botao = new JButton("Adivinhar"); - - botao.addActionListener(e -> { - int palpite = Integer.parseInt(campo.getText()); - tentativas++; - if (palpite < secreto) { - dica.setText(palpite + " é demasiado pequeno."); - } else if (palpite > secreto) { - dica.setText(palpite + " é demasiado grande."); - } else { - dica.setText("Certo! Precisaste de " + tentativas + " tentativas."); - botao.setEnabled(false); - } - campo.setText(""); - }); - - janela.add(dica); - janela.add(campo); - janela.add(botao); - janela.setVisible(true); - } -} -\`\`\` - -- \`campo.setText("")\` esvazia o campo de texto depois de cada palpite, para escreveres logo o seguinte. -- \`botao.setEnabled(false)\` desliga o botão. Fica cinzento e já não se pode clicar. O jogo acabou. - -::: ludwig -Com \`setEnabled(true)\` voltas a ligar um botão. Assim podes, por exemplo, só ativar um botão depois de alguém ter escrito alguma coisa. -::: - -## Ganhar com 7 tentativas - -::: angeberwissen -Quem adivinha com jeito nunca precisa de mais de 7 tentativas. Escolhe sempre o meio do intervalo que ainda resta: primeiro 50, depois 25 ou 75 e assim por diante. Cada palpite corta o intervalo a meio: 100, 50, 25, 13, 7, 4, 2, 1. É a **pesquisa binária** da lição 19. Como 2 elevado a 7 = 128 é maior do que 100, chegam 7 palpites. -::: - -## Tarefa: adivinha o número - -Reconstrói o jogo e melhora-o: - -- Uma entrada como “abc” não conta como tentativa e mostra a dica “Escreve um número”. -- Um segundo botão **Novo jogo** pensa num número novo, põe as tentativas a 0, volta a ligar o botão Adivinhar e repõe a dica. - -Extra: depois de 7 tentativas falhadas o jogo está perdido. A janela revela então o número secreto. - -**Terminaste quando …** - -- cada palpite errado mostra “demasiado pequeno” ou “demasiado grande”, -- “abc” mostra o aviso para escrever um número e as tentativas não mudam, -- **Novo jogo** põe as tentativas a 0, volta a ligar o botão de adivinhar e repõe o aviso. - -::: ludwig -Para “não conta como tentativa”, o \`tentativas++\` tem de vir **depois** de \`Integer.parseInt\`, os dois dentro do bloco \`try\`. Se o \`parseInt\` lançar uma exceção, a linha com \`tentativas++\` é saltada. -::: - -## Jogo: Demasiado pequeno ou demasiado grande? - -::: spiel frage -titel: Jogo: Demasiado pequeno ou demasiado grande? -intro: O Ludwig está a jogar ao adivinha o número. Lê o código e prevê o que acontece. Sete rondas. - -runde: - // secreto é 42 - if (palpite < secreto) { - dica.setText(palpite + " é demasiado pequeno."); - } else if (palpite > secreto) { - dica.setText(palpite + " é demasiado grande."); - } else { - dica.setText("Certo!"); - } -frage: O Ludwig escreve 50. O que diz a dica? -- 50 é demasiado pequeno. -- [x] 50 é demasiado grande. -- Certo! -> 50 é maior do que 42. - -runde: -frage: O número está entre 1 e 100. 50 era demasiado grande, 25 era demasiado pequeno. Que número é que o Ludwig, esperto, tenta a seguir? -- 26 -- [x] 37 -- 49 -> Sobram 26 a 49. O meio é 37 (ou 38). Assim, cada palpite corta o intervalo a meio. - -runde: - int dado = (int) (Math.random() * 6) + 1; -frage: Que números pode ter \`dado\`? -- 0 a 6 -- [x] 1 a 6 -- 1 a 7 -> \`Math.random() * 6\` vai de 0 até um pouco menos de 6. \`(int)\` faz disso 0 a 5, e \`+ 1\` faz 1 a 6. Um dado! - -runde: - int numero = (int) (Math.random() * 10); -frage: Que números pode ter \`numero\`? -- [x] 0 a 9 -- 1 a 10 -- 0 a 10 -> Sem \`+ 1\` começa em 0, e o 10 nunca é alcançado. - -runde: - // secreto é 42 - tentativas++; - if (palpite == secreto) { - dica.setText("Certo! Precisaste de " + tentativas + " tentativas."); - } -frage: O Ludwig escreve 50, depois 25, depois 42. O que diz a dica no fim? -- Certo! Precisaste de 2 tentativas. -- [x] Certo! Precisaste de 3 tentativas. -- Certo! Precisaste de 42 tentativas. -> \`tentativas++\` conta a cada clique, também no acerto. - -runde: - botao.setEnabled(false); -frage: O Ludwig encontrou o número e clica mais uma vez em **Adivinhar**. O que acontece? -- O jogo recomeça. -- Conta mais uma tentativa. -- [x] Nada, o botão fica cinzento e não se pode clicar. -> \`setEnabled(false)\` desliga o botão. - -runde: -frage: Agora o número está entre 1 e 1000. Quantos palpites são precisos, no máximo, com a pesquisa binária? -- 7 -- [x] 10 -- 500 -> 2 elevado a 10 = 1024 é maior do que 1000. Dez divisões a meio chegam. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo criar com Math.random um número inteiro aleatório de 1 a 100. → Um número aleatório -- Consigo decidir que variáveis pertencem à classe. → O que o jogo guarda -- Consigo desligar um botão com setEnabled. → O programa completo -- Consigo construir um jogo de adivinhar com um botão “Novo jogo”. → Tarefa: adivinha o número -::: - -## Teste de conhecimentos - -::: quiz -? Que números dá (int) (Math.random() * 100) + 1? -→ Um número aleatório -- [ ] 0 a 100 -- [x] 1 a 100 -- [ ] 1 a 101 - -? O que faz botao.setEnabled(false)? -→ O programa completo -- [ ] Tira o botão da janela. -- [x] O botão fica cinzento e já não se pode clicar. -- [ ] Muda o texto do botão. - -? Com a pesquisa binária, quantas tentativas precisas no máximo para 1 a 100? -→ Tarefa: adivinha o número -- [ ] 50 -- [ ] 10 -- [x] 7 -> Cada palpite corta o intervalo a meio, e 2 elevado a 7 = 128 é maior do que 100. -::: -`,n9=`--- -titel: Muitos botões, um ouvinte -bereich: Janelas com Swing -kurz: Um ouvinte para muitos botões e descobrir com getActionCommand e getSource qual foi o botão clicado. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- guardar um ouvinte numa variável do tipo ActionListener e registá-lo em vários botões -- explicar o que devolvem getActionCommand e getSource -- pôr botões num JPanel com um ciclo -- construir um cadeado que só abre com o código certo -::: - -## O objetivo - -Desde a história do bolo, o Ludwig quer trancar o seu estábulo. Ele constrói um **cadeado de código**: nove teclas com algarismos, um zero, **C** para apagar e **OK** para verificar. Só quem escrever 1234 recebe "Portão aberto!". - -São doze botões. Até agora, cada botão tinha o seu próprio ouvinte, como na calculadora da lição 35. Hoje aprendes como **um** ouvinte chega para todos os botões. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Nove ouvintes quase iguais - -Com um ouvinte por tecla ficaria assim: - -\`\`\`java -um.addActionListener(e -> visor.setText(visor.getText() + "1")); -dois.addActionListener(e -> visor.setText(visor.getText() + "2")); -tres.addActionListener(e -> visor.setText(visor.getText() + "3")); -// ... e assim por diante até 9 -\`\`\` - -As linhas só se distinguem pelo algarismo. Quem quiser mudar um pormenor tem de o fazer nove vezes e de certeza que se esquece de uma linha. - -## Um ouvinte numa variável - -Um ouvinte também pode ficar numa **variável**. O seu tipo de dados é \`ActionListener\`, o contrato da lição 30. Depois registas o mesmo ouvinte em vários botões: - -\`\`\`java -ActionListener algarismo = e -> visor.setText(visor.getText() + e.getActionCommand()); - -um.addActionListener(algarismo); -dois.addActionListener(algarismo); -tres.addActionListener(algarismo); -\`\`\` - -\`ActionListener\` está no pacote \`java.awt.event\`. Por isso precisas de \`import java.awt.event.*;\` lá em cima. - -Agora o código só existe uma vez. Mas como sabe o ouvinte que algarismo deve acrescentar? - -## Quem clicou? - -O \`e\` antes da seta é o **evento**, um objeto do tipo \`ActionEvent\`. O botão entrega-o a cada clique, e lá dentro está o que aconteceu: - -| Chamada | Devolve | Exemplo | -|---|---|---| -| \`e.getActionCommand()\` | a legenda do botão clicado, como texto | \`"7"\` | -| \`e.getSource()\` | o próprio botão clicado | o botão \`ok\` | - -- \`getActionCommand()\` dá jeito quando a legenda já diz o que fazer, como nos algarismos. -- \`getSource()\` compara-se com um botão usando \`==\`: \`if (e.getSource() == ok)\`. Aqui \`==\` está certo, porque se trata exatamente deste objeto, não de um texto. - -::: ludwig -Textos comparas com \`equals\`, botões com \`==\`. Se quiseres decidir pela legenda, escreves \`e.getActionCommand().equals("OK")\`. Também funciona, mas deixa de funcionar assim que alguém mudar a legenda para "Abrir". \`e.getSource() == ok\` continua certo. -::: - -## Botões num ciclo - -Com um ouvinte partilhado, podes até criar as teclas dos algarismos num ciclo. Para ficarem bem arrumadas numa grelha, vão para um **JPanel**. Um JPanel é uma área na janela com o seu próprio layout: - -\`\`\`java -JPanel teclas = new JPanel(new GridLayout(0, 3)); -for (int i = 1; i <= 9; i++) { - JButton algarismo = new JButton("" + i); - algarismo.addActionListener(tecla); - teclas.add(algarismo); -} -\`\`\` - -- \`"" + i\` transforma o número \`i\` no texto da legenda. -- Em cada volta nasce um botão novo. Todos recebem o mesmo ouvinte \`tecla\`. -- \`janela.add(teclas, BorderLayout.CENTER)\` põe a área inteira, com todas as teclas, na janela. - -::: quiz -? Quantos botões e quantos ouvintes cria este ciclo? -- [ ] 9 botões e 9 ouvintes -- [x] 9 botões e 1 ouvinte -- [ ] 1 botão e 9 ouvintes -> Em cada passagem nasce um botão novo. Todos recebem o mesmo ouvinte \`tecla\`. -::: - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Cadeado { - public static void main(String[] args) { - JFrame janela = new JFrame("Cadeado de código"); - janela.setSize(240, 300); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new BorderLayout()); - - JLabel visor = new JLabel("", SwingConstants.CENTER); - JPanel teclas = new JPanel(new GridLayout(0, 3)); - JButton apagar = new JButton("C"); - JButton ok = new JButton("OK"); - - ActionListener tecla = e -> { - if (e.getSource() == apagar) { - visor.setText(""); - } else if (e.getSource() == ok) { - if (visor.getText().equals("1234")) { - visor.setText("Portão aberto!"); - } else { - visor.setText("Errado"); - } - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - - for (int i = 1; i <= 9; i++) { - JButton algarismo = new JButton("" + i); - algarismo.addActionListener(tecla); - teclas.add(algarismo); - } - JButton zero = new JButton("0"); - zero.addActionListener(tecla); - apagar.addActionListener(tecla); - ok.addActionListener(tecla); - teclas.add(apagar); - teclas.add(zero); - teclas.add(ok); - - janela.add(visor, BorderLayout.NORTH); - janela.add(teclas, BorderLayout.CENTER); - janela.setVisible(true); - } -} -\`\`\` - -- \`apagar\` e \`ok\` são criados **antes** do ouvinte, porque o ouvinte compara com eles. -- O ouvinte verifica primeiro os dois botões especiais. Todos os outros são algarismos e acrescentam a sua legenda. - -::: angeberwissen -O teclado do telefone tem o 1 em cima à esquerda, o da calculadora e do teclado do computador tem o 7 em cima à esquerda. Porquê? Quando chegaram os telefones de teclas, fizeram-se testes com pessoas que tinham de marcar números com eles. Com o 1 em cima eram mais rápidas, e as máquinas de calcular já tinham a sua disposição há muito tempo. O cadeado do Ludwig faz como o telefone. -::: - -## Jogo: Quem clicou? - -::: spiel zuhoerer -titel: Jogo: Quem clicou? -intro: Em cada ronda, **um** ouvinte está registado em vários botões. O Ludwig mostra-te o código, a janela e os cliques. O que fica no visor no fim? Depois o Ludwig reproduz os cliques. -frage: O que fica no visor depois dos cliques? - -runde: - JFrame janela = new JFrame("Letras"); - JLabel visor = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - ActionListener z = e -> visor.setText(visor.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, B, A -- A -- AB -- [x] ABA -> Um ouvinte, dois botões. \`e.getActionCommand()\` devolve de cada vez a legenda do botão clicado, e ela é acrescentada no fim. - -runde: - JFrame janela = new JFrame("Contador"); - static int numero = 0; - JLabel visor = new JLabel("0"); - JButton mais = new JButton("+"); - JButton menos = new JButton("-"); - ActionListener z = e -> { - if (e.getSource() == mais) { - numero++; - } else { - numero--; - } - visor.setText("" + numero); - }; - mais.addActionListener(z); - menos.addActionListener(z); -klicks: +, +, - -- 3 -- [x] 1 -- -1 -> \`e.getSource()\` diz qual foi o botão. Duas vezes mais, uma vez menos: 0 + 1 + 1 - 1 = 1. - -runde: - JFrame janela = new JFrame("Letras"); - JLabel visor = new JLabel(""); - JButton a = new JButton("A"); - JButton b = new JButton("B"); - JButton c = new JButton("C"); - ActionListener z = e -> visor.setText(visor.getText() + e.getActionCommand()); - a.addActionListener(z); - b.addActionListener(z); -klicks: A, C, B -- ACB -- [x] AB -- A -> O ouvinte só está registado em A e B. Quando se clica em C, ninguém está a ouvir. - -runde: - JFrame janela = new JFrame("Teclas"); - JLabel visor = new JLabel(""); - JButton um = new JButton("1"); - JButton dois = new JButton("2"); - JButton apagar = new JButton("C"); - ActionListener z = e -> { - if (e.getActionCommand().equals("C")) { - visor.setText(""); - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - um.addActionListener(z); - dois.addActionListener(z); - apagar.addActionListener(z); -klicks: 1, 2, C, 1 -- 121 -- 12 -- [x] 1 -> Depois de 1 e 2 o visor mostra "12". C apaga tudo, e o último 1 fica sozinho. - -runde: - JFrame janela = new JFrame("Cadeado"); - JLabel visor = new JLabel(""); - JButton quatro = new JButton("4"); - JButton dois = new JButton("2"); - JButton ok = new JButton("OK"); - ActionListener z = e -> { - if (e.getSource() == ok) { - if (visor.getText().equals("42")) { - visor.setText("Portão aberto!"); - } else { - visor.setText("Errado"); - } - } else { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; - quatro.addActionListener(z); - dois.addActionListener(z); - ok.addActionListener(z); -klicks: 2, 4, OK -- Portão aberto! -- [x] Errado -- 24OK -> Foi escrito "24", mas o segredo é "42". A ordem dos cliques conta. -::: - -## Tarefa: Cadeado de código - -Constrói o cadeado de código e experimenta-o. - -Depois melhora-o: - -- Depois de três tentativas erradas, o **OK** desliga-se, como o botão do Adivinha o número na lição 36. Conta as tentativas numa variável \`static int\` na classe. -- Extra: o visor mostra \`*\` em vez dos algarismos. Guarda a entrada verdadeira numa \`static String entrada = "";\` e acrescenta ao visor só uma estrela. - -**Terminaste quando …** - -- o código certo abre o cadeado e um errado não, -- depois de três tentativas erradas o **OK** fica desligado, -- no extra, o visor só mostra estrelas mas o código continua a ser verificado. - -::: ludwig -No ramo do **OK**, conta \`tentativas++\` a cada código errado e depois verifica \`if (tentativas >= 3)\`. Aí \`ok.setEnabled(false)\` desliga o botão, como no Adivinha o número. Para as estrelas: \`entrada = entrada + e.getActionCommand();\` e \`visor.setText(visor.getText() + "*");\`. Depois comparas \`entrada\`, não o texto do visor. -::: - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo guardar um ouvinte numa variável do tipo ActionListener e registá-lo em vários botões. → Um ouvinte numa variável -- Consigo explicar o que devolvem getActionCommand e getSource. → Quem clicou? -- Consigo pôr botões num JPanel com um ciclo. → Botões num ciclo -- Consigo construir um cadeado que só abre com o código certo. → Tarefa: Cadeado de código -::: - -## Teste de conhecimentos - -::: quiz -? O que devolve e.getActionCommand() num clique no botão new JButton("7")? -→ Quem clicou? -- [ ] o número 7 -- [x] o texto "7" -- [ ] o próprio botão - -? Como verificas se foi clicado o botão ok? -→ Quem clicou? -- [x] e.getSource() == ok -- [ ] e.getSource().equals("ok") -- [ ] ok.getActionCommand() -> getSource devolve o próprio botão, e esse comparas com ==. - -? Porque é que um ouvinte fica numa variável? -→ Um ouvinte numa variável -- [ ] Para correr mais depressa. -- [x] Para o poder registar em vários botões e escrever o código só uma vez. -- [ ] Porque senão os lambdas não são permitidos. - -? Liga. -→ Quem clicou? -= ActionListener -> tipo de dados de um ouvinte -= ActionEvent -> o evento e com a informação sobre o clique -= JPanel -> uma área com o seu próprio layout -::: -`,i9=`--- -titel: Classes internas -bereich: Janelas com Swing -kurz: Escrever um ouvinte como classe interna com nome, como classe anónima e como lambda, e saber quem vê o quê. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- escrever um ouvinte como classe interna com nome -- escrever um ouvinte como classe anónima -- explicar o que fica de fora no lambda -- explicar que maneira de escrever vê que variáveis -- construir um contador com três botões escritos de três maneiras -::: - -## O objetivo - -O contador de cliques da lição 32 já conheces. Hoje escreves o seu ouvinte de **três maneiras**, que fazem todas exatamente o mesmo: - -1. como **classe interna com nome**, como se fazia quase sempre antes do Java 8, -2. como **classe anónima**, que conheces da lição 30 (“ActionListener é uma interface”), -3. como **lambda** com a seta, como até agora. - -Em código de outras pessoas, em livros e na internet vais encontrar as três. Depois desta lição vais reconhecê-las. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Lembrete: o contrato - -Um ouvinte é tudo o que cumpre o contrato \`ActionListener\`. Nele está exatamente um método: - -\`\`\` -public void actionPerformed(ActionEvent e) -\`\`\` - -O botão chama-o a cada clique. As três maneiras de escrever são três caminhos para fornecer este único método. - -## 1. A classe interna com nome - -Uma **classe interna** fica dentro de outra classe. Tem um nome, aqui \`Mais\`, e cumpre o contrato com \`implements ActionListener\`: - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; - -public class Contador { - static int contagem = 0; - static JLabel numero = new JLabel("0", SwingConstants.CENTER); - - static class Mais implements ActionListener { - public void actionPerformed(ActionEvent e) { - contagem++; - numero.setText("" + contagem); - } - } - - public static void main(String[] args) { - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JButton mais = new JButton("+1"); - JButton outro = new JButton("Também +1"); - mais.addActionListener(new Mais()); - outro.addActionListener(new Mais()); - - janela.add(numero); - janela.add(mais); - janela.add(outro); - janela.setVisible(true); - } -} -\`\`\` - -- \`new Mais()\` cria um objeto da classe \`Mais\`. É esse o ouvinte que o botão recebe. -- Como a classe tem nome, podes usá-la em vários botões, aqui em \`mais\` e \`outro\`. -- A classe está **fora** de \`main\`. Por isso só vê o que está na classe \`Contador\`: \`contagem\` e \`numero\`. É por isso que \`numero\` desta vez é uma variável da classe e não de \`main\`. -- Precisas de \`static class\` porque \`main\` é static. Sem \`static\`, o javac indica "non-static variable this cannot be referenced from a static context". - -## 2. A classe anónima - -Se só precisas do ouvinte num sítio, podes escrever a classe mesmo onde o objeto é preciso. Assim ela não recebe nome: - -\`\`\`java -mais.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - contagem++; - numero.setText("" + contagem); - } -}); -\`\`\` - -- \`new ActionListener() { … }\` quer dizer: "um objeto novo que cumpre o contrato ActionListener, e é assim: …". -- No fim está \`});\`: a chaveta fecha a classe, o parêntese fecha a chamada de \`addActionListener\`. -- A classe anónima está **dentro** de \`main\`. Por isso também vê as variáveis de \`main\`, desde que já não mudem. - -::: quiz -? O que fecha \`});\` no fim da classe anónima? -- [x] \`}\` fecha a classe, \`)\` fecha a chamada de \`addActionListener\`. -- [ ] \`}\` fecha o \`main\`, \`)\` fecha a classe. -- [ ] Os dois fecham o método \`actionPerformed\`. -> O método \`actionPerformed\` já termina uma linha antes com a sua própria \`}\`. -::: - -## 3. O lambda - -O lambda é a forma curta da classe anónima, desde o Java 8: - -\`\`\`java -mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); -}); -\`\`\` - -Fica de fora tudo o que o Java já sabe: \`new ActionListener()\`, o nome \`actionPerformed\`, \`public void\` e o tipo \`ActionEvent\`. Sobram o parâmetro \`e\`, a seta e o corpo. Isto só funciona porque o contrato tem exatamente **um** método. - -## As três comparadas - -| | classe interna com nome | classe anónima | lambda | -|---|---|---|---| -| tem nome | sim, p. ex. \`Mais\` | não | não | -| fica | na classe, fora de \`main\` | mesmo junto ao botão | mesmo junto ao botão | -| para vários botões | sim, \`new Mais()\` | não | sim, numa variável (lição 37) | -| vê variáveis de \`main\` | não | sim, se não mudarem | sim, se não mudarem | -| comprimento | a mais longa | média | a mais curta | - -::: ludwig -Se uma variável de \`main\` tiver de mudar no ouvinte, como \`contagem++\`, tem de ir para a classe, seja qual for a maneira de escrever. Esse era o problema da lição 32. -::: - -::: angeberwissen -Depois de compilar, espreita a pasta com os ficheiros \`.class\`. O javac cria um ficheiro para cada classe: \`Contador.class\`, \`Contador$Mais.class\` para a classe interna e \`Contador$1.class\` para a classe anónima. Como não tem nome, recebe um número. Um lambda não tem ficheiro próprio, o Java só o constrói quando o programa arranca. -::: - -## Jogo: Que maneira de escrever? - -::: spiel frage -titel: Jogo: Que maneira de escrever? -intro: O Ludwig mostra-te ouvintes de programas de outras pessoas. Que maneira de escrever é esta? Seis rondas. -frage: Que maneira de escrever é esta? -antworten: classe interna com nome | classe anónima | lambda - -runde: - botao.addActionListener(e -> texto.setText("Olá")); -richtig: lambda -> A seta \`->\` denuncia o lambda. - -runde: - botao.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - texto.setText("Olá"); - } - }); -richtig: classe anónima -> \`new ActionListener() {\` e a seguir um método inteiro: aqui está a ser escrita uma classe sem nome. - -runde: - static class Saudacao implements ActionListener { - public void actionPerformed(ActionEvent e) { - texto.setText("Olá"); - } - } -richtig: classe interna com nome -> A classe chama-se \`Saudacao\` e cumpre o contrato com \`implements ActionListener\`. - -runde: - botao.addActionListener(new Saudacao()); -richtig: classe interna com nome -> \`new Saudacao()\` cria um objeto da classe \`Saudacao\`. A própria classe está noutro sítio e tem nome. - -runde: - ActionListener tecla = e -> { - visor.setText(visor.getText() + e.getActionCommand()); - }; -richtig: lambda -> Um lambda numa variável, como no cadeado da lição 37. A seta denuncia-o. - -runde: - ActionListener tecla = new ActionListener() { - public void actionPerformed(ActionEvent e) { - visor.setText(visor.getText() + e.getActionCommand()); - } - }; -richtig: classe anónima -> Uma classe anónima também pode ficar numa variável. Assim pode ser registada em vários botões, tal como o lambda. -::: - -## Tarefa: Um contador, três maneiras - -Constrói o contador de cliques com três botões: - -- **+1** recebe a classe interna com nome \`Mais\`. -- **-1** recebe uma classe anónima. -- **Reset** recebe um lambda. - -Experimenta se os três botões contam como esperado. Depois tira o \`static\` da classe \`Mais\` e lê a mensagem de erro do javac. - -**Terminaste quando …** - -- **+1** conta para cima, **-1** para baixo e **Reset** põe 0, -- o código tem exatamente uma classe interna com nome, uma classe anónima e uma lambda, -- leste a mensagem de erro do javac sem \`static\` em \`Plus\` e consegues explicar o que significa. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo escrever um ouvinte como classe interna com nome. → 1. A classe interna com nome -- Consigo escrever um ouvinte como classe anónima. → 2. A classe anónima -- Consigo explicar o que fica de fora no lambda. → 3. O lambda -- Consigo explicar que maneira de escrever vê que variáveis. → As três comparadas -- Consigo construir um contador com três botões escritos de três maneiras. → Tarefa: Um contador, três maneiras -::: - -## Teste de conhecimentos - -::: quiz -? Porque é que a forma curta com a seta funciona com ActionListener? -→ 3. O lambda -- [ ] Porque ActionListener é uma classe. -- [x] Porque o contrato tem exatamente um método. -- [ ] Porque o botão o exige. - -? Que maneira de escrever podes usar com new Mais() em vários botões sem a guardar numa variável? -→ 1. A classe interna com nome -- [x] a classe interna com nome -- [ ] a classe anónima -- [ ] o lambda - -? Porque é que está static antes de class Mais? -→ 1. A classe interna com nome -- [ ] Para a classe ser mais rápida. -- [x] Porque main é static e senão não consegue criar um objeto de Mais. -- [ ] Porque todas as classes têm de ser static. - -? Liga. -→ As três comparadas -= class Mais implements ActionListener -> classe interna com nome -= new ActionListener() { … } -> classe anónima -= a seta depois de e -> lambda -::: -`,a9=`--- -titel: Dois separadores -bereich: Janelas com Swing -kurz: Construir uma janela com dois separadores, levar dados de um separador para o outro e guardar a lista num ficheiro de texto. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- construir uma janela com dois separadores -- acrescentar linhas a uma JTextArea com append -- explicar porque é que um ouvinte pode usar peças dos dois separadores -- guardar um ficheiro dentro de um ouvinte com try e catch -- construir uma lista de compras que conta quantas coisas tem -::: - -## O objetivo - -A lista de compras do Ludwig da lição 23 ganha uma janela. Tem dois **separadores**, como os do browser: - -- No separador **Entrada** escreves o que precisas e clicas em **Adicionar**. -- No separador **Lista** está tudo o que escreveste. Um botão **Guardar** escreve a lista num ficheiro de texto. - -Novos são os separadores (\`JTabbedPane\`), uma área de texto com várias linhas (\`JTextArea\`) e guardar a partir de um ouvinte. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Separadores com JTabbedPane - -Cada separador mostra a sua própria área. As áreas são JPanels, como o teclado do cadeado na lição 37: - -\`\`\`java -JTabbedPane separadores = new JTabbedPane(); -JPanel entrada = new JPanel(new GridLayout(0, 1)); -JPanel lista = new JPanel(new BorderLayout()); - -separadores.addTab("Entrada", entrada); -separadores.addTab("Lista", lista); -janela.add(separadores); -\`\`\` - -| Chamada | Significado | -|---|---| -| \`addTab("Entrada", entrada)\` | um separador novo com a legenda "Entrada", mostra o JPanel \`entrada\` | -| \`setSelectedIndex(1)\` | muda para o segundo separador, conta-se a partir de 0 | -| \`setTitleAt(1, "Lista (3)")\` | muda a legenda do segundo separador | - -No início está aberto o separador que foi adicionado primeiro. - -## Uma área de texto com várias linhas - -Um \`JTextField\` só tem uma linha. Para a lista precisas de uma \`JTextArea\`: - -\`\`\`java -JTextArea area = new JTextArea(); -area.setEditable(false); -area.append("Feno\\n"); -\`\`\` - -- \`append\` acrescenta texto no fim. O \`"\\n"\` começa depois uma nova linha, como ao dividir com \`split("\\n")\` na lição 24. -- \`setEditable(false)\` impede que se escreva diretamente na lista. -- \`getText()\` devolve o texto inteiro com todas as linhas. - -::: quiz -? O que faz \`area.append("Feno\\n")\`? -- [ ] Substitui o texto todo por “Feno”. -- [x] Acrescenta “Feno” no fim e começa depois uma linha nova. -- [ ] Escreve “Feno\\n” tal e qual na lista. -> \`append\` acrescenta no fim, \`"\\n"\` é a mudança de linha. -::: - -## De um separador para o outro - -O botão **Adicionar** está no separador Entrada, a área de texto no separador Lista. Mesmo assim, o ouvinte pode usar os dois. Todas as peças são variáveis em \`main\`, e o lambda vê-as todas, seja qual for o separador em que aparecem depois: - -\`\`\`java -adicionar.addActionListener(e -> { - area.append(campo.getText() + "\\n"); - info.setText(campo.getText() + " está na lista."); - campo.setText(""); -}); -\`\`\` - -::: ludwig -Uma peça só pode estar numa **única** área. Se juntares o mesmo botão primeiro a \`entrada\` e depois a \`lista\`, o Swing tira-o de \`entrada\`. Só aparece no separador Lista. -::: - -## Guardar dentro do ouvinte - -Para guardar usas o \`PrintWriter\` da lição 23. Dentro do ouvinte há um senão: a saída com \`throws Exception\` da lição 23 aqui não existe. O lambda é um método próprio, \`actionPerformed\`, e segundo o contrato não pode passar adiante uma \`FileNotFoundException\`. Por isso, guardar fica dentro de try e catch: - -\`\`\`java -guardar.addActionListener(e -> { - try { - PrintWriter saida = new PrintWriter("listacompras.txt"); - saida.print(area.getText()); - saida.close(); - info.setText("Guardado em listacompras.txt"); - separadores.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Não foi possível guardar."); - } -}); -\`\`\` - -- A exceção chama-se aqui \`ex\`, porque o nome \`e\` já está ocupado pelo evento. -- Depois de guardar, a janela volta com \`setSelectedIndex(0)\` ao separador Entrada, onde está a mensagem. -- O VisuStruct mostra o ficheiro guardado por baixo da consola, assim que clicares em Guardar. - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; -import java.io.*; - -public class ListaCompras { - public static void main(String[] args) { - JFrame janela = new JFrame("Lista de compras"); - janela.setSize(300, 250); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JTabbedPane separadores = new JTabbedPane(); - - JPanel entrada = new JPanel(new GridLayout(0, 1)); - JTextField campo = new JTextField(); - JButton adicionar = new JButton("Adicionar"); - JLabel info = new JLabel("Ainda não há nada na lista."); - entrada.add(new JLabel("Do que precisas?")); - entrada.add(campo); - entrada.add(adicionar); - entrada.add(info); - - JPanel lista = new JPanel(new BorderLayout()); - JTextArea area = new JTextArea(); - area.setEditable(false); - JButton guardar = new JButton("Guardar"); - lista.add(area, BorderLayout.CENTER); - lista.add(guardar, BorderLayout.SOUTH); - - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); - - adicionar.addActionListener(e -> { - area.append(campo.getText() + "\\n"); - info.setText(campo.getText() + " está na lista."); - campo.setText(""); - }); - - guardar.addActionListener(e -> { - try { - PrintWriter saida = new PrintWriter("listacompras.txt"); - saida.print(area.getText()); - saida.close(); - info.setText("Guardado em listacompras.txt"); - separadores.setSelectedIndex(0); - } catch (FileNotFoundException ex) { - info.setText("Não foi possível guardar."); - } - }); - - janela.add(separadores); - janela.setVisible(true); - } -} -\`\`\` - -Escreve Feno, clica em Adicionar, depois Maçãs e outra vez Adicionar. Muda para o separador Lista e clica em Guardar. - -## Jogo: Onde vai parar? - -::: spiel frage -titel: Jogo: Onde vai parar? -intro: O Ludwig constrói janelas com separadores. Lê o código e prevê o que vês. Seis rondas. - -runde: - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); -frage: Que separador está aberto no início? -- [x] Entrada -- Lista -- nenhum -> No início está aberto o separador que foi adicionado primeiro. - -runde: - separadores.addTab("Lista", lista); - separadores.addTab("Entrada", entrada); -frage: Que separador está aberto no início? -- Entrada -- [x] Lista -- os dois -> Desta vez a Lista vem primeiro. A ordem dos \`addTab\` decide a ordem dos separadores. - -runde: - separadores.addTab("Entrada", entrada); - separadores.addTab("Lista", lista); - separadores.addTab("Ajuda", ajuda); - separadores.setSelectedIndex(1); -frage: Que separador está aberto? -- Entrada -- [x] Lista -- Ajuda -> Conta-se a partir de 0: 0 é Entrada, 1 é Lista, 2 é Ajuda. - -runde: - adicionar.addActionListener(e -> { - area.append(campo.getText() + "\\n"); - campo.setText(""); - }); - // Escreves Feno, clicas em Adicionar, escreves Maçãs, clicas em Adicionar. -frage: O que está depois no separador Lista? -- Maçãs -- FenoMaçãs -- [x] Feno\\nMaçãs -> \`append\` acrescenta no fim, e cada \`"\\n"\` começa uma nova linha. - -runde: - JButton botao = new JButton("OK"); - entrada.add(botao); - lista.add(botao); -frage: Onde aparece o botão OK? -- nos dois separadores -- só no separador Entrada -- [x] só no separador Lista -> Uma peça só pode estar numa área. No segundo \`add\`, o Swing tira-a de \`entrada\`. - -runde: - guardar.addActionListener(e -> { - area.setText(""); - separadores.setTitleAt(1, "Lista (vazia)"); - separadores.setSelectedIndex(0); - }); - // O botão guardar está no separador Lista. -frage: O que vês depois do clique? -- o separador Lista vazio -- [x] o separador Entrada, e o segundo separador chama-se "Lista (vazia)" -- o separador Entrada, e o segundo separador continua a chamar-se "Lista" -> O ouvinte esvazia a área, muda o nome do separador e muda para o separador 0. Tudo acontece com um clique. -::: - -## Tarefa: Lista de compras com separadores - -Constrói o programa e experimenta-o. - -Depois melhora-o: - -- Entradas vazias não vão para a lista. Verifica antes \`campo.getText().isEmpty()\` (lição 17). -- O separador Lista mostra no título quantas coisas estão na lista: "Lista (3)". Conta numa variável \`static int\` e usa \`setTitleAt\`. -- Extra: um terceiro separador **Ajuda** com um JLabel que explica como funciona o programa. - -**Terminaste quando …** - -- um item escrito aparece no separador da lista depois do clique, -- um campo vazio não acrescenta nada à lista, -- depois de três itens o título do separador mostra o número, p. ex. “Lista (3)”. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo construir uma janela com dois separadores. → Separadores com JTabbedPane -- Consigo acrescentar linhas a uma JTextArea com append. → Uma área de texto com várias linhas -- Consigo explicar porque é que um ouvinte pode usar peças dos dois separadores. → De um separador para o outro -- Consigo guardar um ficheiro dentro de um ouvinte com try e catch. → Guardar dentro do ouvinte -- Consigo construir uma lista de compras que conta quantas coisas tem. → Tarefa: Lista de compras com separadores -::: - -## Teste de conhecimentos - -::: quiz -? Que separador está aberto depois de separadores.setSelectedIndex(0)? -→ Separadores com JTabbedPane -- [x] o primeiro -- [ ] o segundo -- [ ] nenhum - -? Porque é que guardar dentro do ouvinte fica em try e catch? -→ Guardar dentro do ouvinte -- [ ] Porque o PrintWriter só funciona dentro de try. -- [x] Porque actionPerformed não pode passar adiante uma FileNotFoundException. -- [ ] Porque o main não tem throws. - -? Qual é a diferença entre JTextField e JTextArea? -→ Uma área de texto com várias linhas -- [ ] Não há nenhuma. -- [x] A JTextArea pode mostrar várias linhas. -- [ ] Num JTextField não se pode escrever. - -? Liga. -→ Separadores com JTabbedPane -= JTabbedPane -> uma área com separadores -= JPanel -> a área de um separador -= JTextArea -> uma área de texto com várias linhas -::: -`,s9=`--- -titel: Model View Controller -bereich: Janelas com Swing -kurz: Dividir o contador de cliques em model, view e controller e perceber quem fala com quem. ---- - -## Competências que vou adquirir agora - -::: kompetenzen -- dizer o que fazem o model, a view e o controller -- escrever um model sem Swing e testá-lo sem janela -- explicar o que constrói new Contador() e porque é que ali não há static -- nomear as quatro mensagens depois de um clique -- acrescentar uma segunda view sem mudar o model -- construir um contador com −1, Reset e limites no model -::: - -## O objetivo - -Os programas grandes com janelas têm quase sempre a mesma estrutura. Chama-se **Model View Controller**, ou **MVC**, em português modelo, vista e controlador: - -- O **model** guarda os dados e conhece as suas regras. -- A **view** só mostra os dados. -- O **controller** reage aos cliques e dá o recado. - -Aprendes MVC com o contador de cliques da lição 32. Ele é dividido em três partes, passo a passo. No fim mostra a mesma contagem duas vezes: como número e como barra. - -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). - -## Tudo numa classe - -Até agora o contador de cliques era assim, tudo está em \`main\` e numa variável da classe: - -\`\`\`java -static int contagem = 0; - -public static void main(String[] args) { - // ... construir a janela ... - JLabel numero = new JLabel("0", SwingConstants.CENTER); - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - contagem++; - numero.setText("" + contagem); - }); -} -\`\`\` - -Funciona. Mas imagina que a contagem também deve aparecer como barra, mais tarde numa segunda janela e num ficheiro. Então cada ouvinte tem de se lembrar de todos os visores. Contar e mostrar ficam atados num nó, e com cada visor o nó fica maior. - -O MVC desata o nó: cada tarefa recebe a sua própria classe. - -## O model - -O model é o próprio contador, sem nada de Swing: - -\`\`\`java -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} -\`\`\` - -- Desta vez não há \`static\` antes de \`valor\` e dos métodos. Assim pertencem a um **objeto**: \`new Contador()\` constrói um contador com o seu próprio \`valor\`, tal como \`new JButton("+1")\` constrói um botão com a sua própria legenda. -- \`model.aumentar()\` chama o método deste contador, tal como \`botao.setText(…)\`. -- Quem quer saber o valor pergunta com \`getValor()\`. Mudá-lo só é possível através de \`aumentar()\`. - -Como o model não precisa de janela, podes experimentá-lo sem janela nenhuma: - -\`\`\`java -public class ContadorTeste { - public static void main(String[] args) { - Contador model = new Contador(); - model.aumentar(); - model.aumentar(); - System.out.println(model.getValor()); - } -} - -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} -\`\`\` - -A saída é 2. Num ficheiro podem estar várias classes. Só uma delas é \`public\`, e essa tem o \`main\`. - -## A view - -A view mostra o valor e mais nada. Não faz contas e não sabe de onde vem o número: - -\`\`\`java -class VistaNumero { - JLabel numero = new JLabel("0", SwingConstants.CENTER); - - void atualizar(int valor) { - numero.setText("" + valor); - } -} -\`\`\` - -- A view recebe o valor com \`atualizar(int valor)\` e escreve-o no seu JLabel. -- A janela e o botão constróis em \`main\`, como sempre. Com \`janela.add(view.numero)\` o JLabel da view entra na janela. - -## O controller - -O controller é o ouvinte do botão. Liga o model à view: - -\`\`\`java -Contador model = new Contador(); -VistaNumero view = new VistaNumero(); - -mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); -}); -\`\`\` - -- Primeiro muda o model, depois vai buscar o novo valor e entrega-o à view. -- O problema da lição 32 desapareceu: a variável \`model\` nunca muda, aponta sempre para o mesmo contador. Só o \`valor\` lá dentro muda. Por isso o lambda pode usar \`model\`, sem nenhum \`static\`. - -## Quem fala com quem? - -A cada clique correm quatro mensagens, sempre pela mesma ordem: - -::: grafik mvc-klick.svg -As setas acendem-se uma após a outra. O model conta na segunda seta, o visor só na quarta. -::: - -1. O botão comunica o clique ao controller (\`actionPerformed\`). -2. O controller diz ao model: \`aumentar()\`. -3. O controller pergunta ao model o novo valor: \`getValor()\`. -4. O controller entrega o valor à view: \`atualizar(valor)\`. - -O model não fala com ninguém. Espera até lhe perguntarem. - -::: ludwig -Lembra-te: o model não sabe nada de janelas. Em \`Contador\` não há nenhum \`JLabel\` nem nenhum \`import javax.swing\`. Se precisares de Swing ali, o código pertence à view. -::: - -::: quiz -? Com quem fala o model por iniciativa própria? -- [ ] Com a view -- [ ] Com o botão -- [x] Com ninguém -> O model espera até o controller lhe perguntar. -::: - -## A vantagem: uma segunda view - -Agora a contagem também deve aparecer como barra. Para isso junta-se uma segunda view. Uma \`JProgressBar\` é uma barra que se enche, aqui de 0 a 10: - -\`\`\`java -class VistaBarra { - JProgressBar barra = new JProgressBar(0, 10); - - void atualizar(int valor) { - barra.setValue(valor); - } -} -\`\`\` - -O controller ganha mais uma linha. O model fica exatamente como estava: - -\`\`\`java -mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); -}); -\`\`\` - -É exatamente esta a vantagem do MVC: podes acrescentar ou trocar visores sem mexer no model. - -## O programa completo - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -class Contador { - int valor = 0; - - void aumentar() { - valor++; - } - - int getValor() { - return valor; - } -} - -class VistaNumero { - JLabel numero = new JLabel("0", SwingConstants.CENTER); - - void atualizar(int valor) { - numero.setText("" + valor); - } -} - -class VistaBarra { - JProgressBar barra = new JProgressBar(0, 10); - - void atualizar(int valor) { - barra.setValue(valor); - } -} - -public class ContadorCliques { - public static void main(String[] args) { - Contador model = new Contador(); - VistaNumero view = new VistaNumero(); - VistaBarra barra = new VistaBarra(); - - JFrame janela = new JFrame("Contador de cliques"); - janela.setSize(250, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JButton mais = new JButton("+1"); - mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); - }); - - janela.add(view.numero); - janela.add(barra.barra); - janela.add(mais); - janela.setVisible(true); - } -} -\`\`\` - -Clica algumas vezes em +1 e vê o número e a barra a crescer juntos. - -::: angeberwissen -O MVC foi inventado em 1979 pelo norueguês Trygve Reenskaug, no centro de investigação Xerox PARC, na Califórnia. Já nessa altura havia lá computadores com janelas e rato. No mesmo ano, Steve Jobs visitou o laboratório e ficou tão entusiasmado que a Apple passou a construir computadores com janelas e rato. -::: - -## Jogo: Onde pertence o código? - -::: spiel frage -titel: Jogo: Onde pertence o código? -intro: O Ludwig atirou código de um programa MVC para um monte. Arruma cada peça. Oito rondas. -frage: Onde pertence este código? -antworten: Model | View | Controller - -runde: - int valor = 0; -richtig: Model -> A contagem são os dados. Os dados moram no model. - -runde: - numero.setText("" + valor); -richtig: View -> Mostrar algo no ecrã é a tarefa da view. - -runde: - mais.addActionListener(e -> model.aumentar()); -richtig: Controller -> Reagir a um clique e avisar o model: é isso que o controller faz. - -runde: - void aumentar() { - valor++; - } -richtig: Model -> O próprio model sabe como mudar os seus dados. - -runde: - barra.setValue(valor); -richtig: View -> Uma barra que se enche só mostra. Portanto: view. - -runde: - void aumentar() { - if (valor < 10) { - valor++; - } - } -richtig: Model -> "No máximo 10" é uma regra para os dados. As regras pertencem ao model, assim valem para todos os botões e todas as views. - -runde: - view.atualizar(model.getValor()); -richtig: Controller -> Ir buscar o valor ao model e entregá-lo à view: é o controller no meio. - -runde: - JLabel numero = new JLabel("0", SwingConstants.CENTER); -richtig: View -> Um JLabel é uma peça do ecrã. No model não há Swing. -::: - -## Jogo: Quem é o próximo? - -::: spiel reihenfolge -titel: Jogo: Quem é o próximo? -intro: Depois de um clique, o model, a view e o controller falam uns com os outros, mas só funciona por uma ordem. Põe os passos pela ordem certa. Cinco rondas. - -runde: -frage: Clicas em +1 no contador de cliques. O que acontece, um passo após o outro? -- Clicas em +1. -- O controller recebe \`actionPerformed\`. -- O controller chama \`model.aumentar()\`. -- O model aumenta o \`valor\`. -- O controller vai buscar \`model.getValor()\`. -- A view mostra o novo número. -> Clique, controller, mudar o model, ir buscar o valor, mostrar. São as quatro setas da imagem, com a contagem no meio. - -runde: - mais.addActionListener(e -> { - model.aumentar(); - view.atualizar(model.getValor()); - barra.atualizar(model.getValor()); - }); -frage: O que acontece depois de um clique em +1, um passo após o outro? -- O clique em +1 -- \`model.aumentar()\` -- O número mostra o novo valor. -- A barra cresce. -> O ouvinte trata as suas linhas de cima para baixo: primeiro o model, depois o número, depois a barra. - -runde: -frage: Põe o controller pela ordem certa, para que a view mostre o novo número. -- \`mais.addActionListener(e -> {\` -- \`model.aumentar();\` -- \`view.atualizar(model.getValor());\` -- \`});\` -> Primeiro mudar, depois mostrar. Ao contrário, a view ainda mostra o valor antigo. - -runde: - reset.addActionListener(e -> { - model.reiniciar(); - view.atualizar(model.getValor()); - }); -frage: O contador está em 5. Clicas em Reset. O que acontece, um passo após o outro? -- Clicas em Reset. -- O model põe o \`valor\` a 0. -- \`model.getValor()\` devolve 0. -- A view mostra 0. -> Também o Reset passa pelo controller até ao model. A view só sabe do novo valor no fim. - -runde: -frage: Um teste sem janela deve escrever 1. Por que ordem estão as linhas no main? -- \`Contador model = new Contador();\` -- \`model.aumentar();\` -- \`System.out.println(model.getValor());\` -> Primeiro construir o objeto, depois trabalhar com ele e no fim ver o resultado. O model pode ser testado sem janela nenhuma. -::: - -## Tarefa: Um contador com limites - -Constrói o programa completo e experimenta-o. - -Depois melhora-o: - -- Um botão **−1** conta para baixo. O model recebe para isso um método \`diminuir()\`, o controller um segundo ouvinte. -- Um botão **Reset** põe o contador a 0 com \`reiniciar()\`. -- O próprio model trata dos limites: nunca abaixo de 0 e nunca acima de 10, porque aí a barra está cheia. -- Extra: uma terceira view \`VistaConsola\`, cujo \`atualizar\` escreve "Novo valor: 3" com \`System.out.println\`. Uma view não tem de ser uma janela. - -**Terminaste quando …** - -- **+1**, **−1** e **Reset** contam bem, -- o contador nunca fica abaixo de 0 nem acima de 10, por mais que cliques, -- o número e a barra mostram sempre o mesmo valor, -- a classe do modelo não tem \`JButton\`, \`JLabel\` nem outro Swing. - -## Lista de verificação - -Avalia-te com honestidade em cada ponto: ☂️ ainda não, ⛅ em parte, ☀️ consigo com segurança. Com ⛅ e ☂️, **Rever** ajuda-te. Depois vem o teste de conhecimentos. Mostra-te se a tua autoavaliação está certa. - -::: checkliste -- Consigo dizer o que fazem o model, a view e o controller. → O objetivo -- Consigo escrever um model sem Swing e testá-lo sem janela. → O model -- Consigo explicar o que constrói new Contador() e porque é que ali não há static. → O model -- Consigo nomear as quatro mensagens depois de um clique. → Quem fala com quem? -- Consigo acrescentar uma segunda view sem mudar o model. → A vantagem: uma segunda view -- Consigo construir um contador com −1, Reset e limites no model. → Tarefa: Um contador com limites -::: - -## Teste de conhecimentos - -::: quiz -? Onde fica a regra "O contador nunca passa de 10"? -→ O model -- [x] no model -- [ ] na view -- [ ] no controller -> As regras sobre os dados pertencem ao model. Assim valem seja qual for o botão. - -? O que faz a view? -→ O objetivo -- [ ] Calcula o novo valor. -- [x] Mostra o valor que recebe. -- [ ] Ouve os cliques. - -? Porque é que o lambda pode usar model, se model é uma variável do main? -→ Quem fala com quem? -- [ ] Porque Contador é public. -- [x] Porque model aponta sempre para o mesmo contador; só o valor lá dentro muda. -- [ ] Porque os lambdas podem mudar qualquer variável. - -? O que tens de mudar no model quando se junta uma segunda view? -→ A vantagem: uma segunda view -- [x] nada -- [ ] um segundo método aumentar -- [ ] um segundo JLabel - -? Liga. -→ O objetivo -= Contador com valor e aumentar() -> model -= VistaNumero com atualizar(int valor) -> view -= o ouvinte do botão -> controller -::: -`,r9=`Termos técnicos da área de aprendizagem. Quando um termo aparece num passo, fica marcado uma vez e mostra a sua -explicação ao passar o rato ou ao tocar. Se estiver a negrito nesse passo, é explicado ali mesmo e não é marcado. -\`auch:\` indica outras formas que também são reconhecidas. Maiúsculas e minúsculas contam. - -### Algoritmo -auch: algoritmo, algoritmos, Algoritmos -Instruções exatas, passo a passo, para resolver uma tarefa. Cada passo é claro. - -### Estrutograma -auch: estrutograma, estrutogramas, Estrutogramas, diagrama de Nassi-Shneiderman, diagramas de Nassi-Shneiderman, diagrama estruturado -Um algoritmo desenhado como imagem feita de blocos. Lê-se de cima para baixo. Também se chama diagrama de Nassi-Shneiderman. - -### Fluxograma -auch: fluxograma, fluxogramas, Fluxogramas -Um algoritmo desenhado com símbolos ligados por setas (DIN 66001 / ISO 5807). Um losango é uma decisão. - -### Pseudocódigo -auch: pseudocódigo -Um algoritmo escrito como texto em linguagem corrente com palavras fixas como SE e ENQUANTO. A indentação mostra o que pertence ao quê. - -### Instrução -auch: instrução, instruções, Instruções -Um único passo no estrutograma, por exemplo um cálculo, uma entrada ou uma saída. - -### Sequência -auch: sequência, sequências, Sequências -Passos que são executados uns a seguir aos outros, de cima para baixo, cada um exatamente uma vez. - -### Decisão -auch: decisão, decisões, Decisões, instrução condicional, instruções condicionais, estrutura de decisão -Um bloco que usa uma condição para decidir qual de dois ramos é executado. Também se chama instrução condicional. - -### Ramo -auch: ramo, ramos -Um dos dois caminhos de uma decisão: à esquerda para verdadeiro (true), à direita para falso (false). - -### Ciclo -auch: ciclo, ciclos, Ciclos, repetição, estrutura de repetição -Um bloco que repete passos enquanto uma condição se verificar. - -### Corpo do ciclo -auch: corpo do ciclo -O interior de um ciclo, ou seja, os passos que são repetidos. - -### Iteração -auch: iteração, iterações -Executar uma vez o interior de um ciclo. - -### Condição -auch: condição, condições, Condições -Uma pergunta que só pode ter como resposta sim (true) ou não (false), por exemplo idade >= 18. - -### Marcador -auch: marcador, marcadores -Um bloco vazio com o sinal ø. Se arrastares um elemento para cima dele, o elemento substitui-o. - -### Inspetor de objetos -A área por baixo do diagrama. Aqui editas o texto e as definições do bloco em que acabaste de clicar. - -### Simulação -auch: simulação -Executa o teu estrutograma no editor, se quiseres passo a passo. Assim vês o que acontece. - -### Variável -auch: variável, variáveis, Variáveis -Uma caixa com etiqueta para um valor. Tem um nome, um tipo de dados e um valor. - -### camelCase -auch: CamelCase -Forma de escrever nomes com várias palavras: escritas juntas, cada palavra seguinte começa com maiúscula, p. ex. \`precoComIva\`. - -### case-sensitive -O Java distingue maiúsculas de minúsculas: \`numero\`, \`Numero\` e \`NUMERO\` são três nomes diferentes. \`system\` em vez de \`System\` é um erro. - -### Constante -auch: constante, constantes, Constantes -Uma variável cujo valor não muda depois de definido. Escreve-se \`const\` à frente, p. ex. \`const double IVA = 0.23\`. - -### Tipo de dados -auch: tipo de dados, tipos de dados, Tipos de dados -Diz que tipo de valor cabe numa variável: int (número inteiro), double (número decimal), String (texto), boolean (verdadeiro ou falso) ou char (um único caractere). - -### Atribuição -auch: atribuição, atribuições, Atribuições -O sinal =. Calcula o lado direito e guarda o resultado na variável à esquerda. - -### Variável auxiliar -auch: variável auxiliar, variáveis auxiliares -Uma variável extra que guarda um valor durante pouco tempo, por exemplo ao trocar valores. - -### Modelo EPS -auch: entrada-processamento-saída -Entrada, processamento, saída: primeiro pedir os valores, depois calcular, depois mostrar o resultado. - -### Análise do problema -auch: análise do problema -Lês uma tarefa com atenção e descobres o que é dado, o que procuras e como chegar de um ao outro. - -### Operador de comparação -auch: operador de comparação, operadores de comparação, Operadores de comparação -Um sinal que compara dois valores, por exemplo == ou <. O resultado é true ou false. - -### Seleção -auch: seleção, seleção múltipla, escolha múltipla -Um bloco que executa exatamente um de vários casos, consoante o valor de uma variável. Também se chama seleção múltipla. - -### Ciclo pré-teste -auch: ciclo pré-teste, ciclos pré-teste, controlado no início -O ciclo verifica a sua condição antes de cada iteração. Se a condição for falsa logo no início, o ciclo não é executado nem uma vez. - -### Ciclo pós-teste -auch: ciclo pós-teste, ciclos pós-teste, controlado no fim -O ciclo só verifica a sua condição depois de cada iteração. Por isso é sempre executado pelo menos uma vez. - -### Variável contadora -auch: variável contadora, variáveis contadoras, contador -Uma variável que vai contando num ciclo, normalmente i. - -### Ciclo infinito -auch: ciclo infinito, ciclos infinitos, ciclo sem fim -Um ciclo cuja condição nunca se torna falsa. Nunca para sozinho. - -### Array -auch: array, arrays, Arrays, vetor, vetores -Uma fila de posições com um nome comum e o mesmo tipo de dados. - -### Índice -auch: índice, índices -O número de uma posição num array. Começa-se a contar em 0. - -### char -O tipo de dados para um único caractere. Fica entre aspas simples, p. ex. 'C'. Já "C" com aspas duplas é uma String. - -### Código de caractere -auch: código de caractere, códigos de caractere, código ASCII, tabela ASCII -O número que está por trás de um caractere. Na tabela ASCII, o A tem o código 65, o B 66 e assim por diante. Em Java, (int) transforma um caractere no seu código e (char) transforma um código no caractere. - -### String -auch: strings, Strings, cadeia de caracteres, cadeias de caracteres -Um texto, ou seja, uma fila de caracteres. Em Java o tipo de dados chama-se String. Os caracteres contam-se a partir de 0. - -### Array 2D -auch: arrays 2D, array bidimensional, arrays bidimensionais, matriz, matrizes -Um array com linhas e colunas, como uma tabela. Uma posição precisa de dois índices, p. ex. \`lugares[1][2]\`. - -### Subprograma -auch: subprograma, subprogramas, Subprogramas, método, métodos, Métodos, procedimento, procedimentos, função, funções, Funções -Um estrutograma próprio para uma parte da tarefa. É executado quando é chamado. Em Java chama-se método; consoante o tipo, também procedimento ou função. - -### Parâmetro -auch: parâmetro, parâmetros, Parâmetros -Uma variável na linha de cabeçalho de um subprograma. Quando é chamado, recebe o valor que está entre parênteses. - -### Código esparguete -auch: código esparguete -Um programa que salta de um lado para o outro de forma tão confusa que mal se consegue seguir o que acontece. Os estrutogramas evitam isso. - -### Passagem por valor -auch: passagem por valor -Um subprograma recebe uma cópia do valor. Se alterar o seu parâmetro, a variável de quem chamou fica igual. - -### Passagem por referência -auch: passagem por referência -Um subprograma recebe uma referência para os mesmos dados, p. ex. para um array. Se alterar uma posição, quem chamou também vê a alteração. - -### Valor de retorno -auch: valor de retorno, valores de retorno, Valores de retorno -O resultado que um subprograma devolve à chamada com return. - -### Interrupção -Um bloco que sai imediatamente do ciclo em que está. Depois o programa continua por baixo do ciclo. - -### Erro de sintaxe -auch: erro de sintaxe, erros de sintaxe, Erros de sintaxe -Um erro na forma como o código está escrito, por exemplo um \`;\` em falta. O programa nem sequer arranca. Em inglês: syntax error. - -### Erro de execução -auch: erro de execução, erros de execução, Erros de execução -Um erro que só acontece enquanto o programa corre, por exemplo dividir por 0. O programa para nesse ponto. Em inglês: runtime error. - -### Erro de lógica -auch: erro de lógica, erros de lógica, Erros de lógica -O programa corre sem nenhuma mensagem, mas dá um resultado errado. Encontra-se com o teste de secretária. Em inglês: logic error. - -### Teste de secretária -auch: teste de secretária, testes de secretária, Testes de secretária, teste de mesa -Executas um estrutograma no papel e anotas os valores das variáveis depois de cada passo. - -### Pesquisa linear -auch: pesquisa linear -Uma forma de pesquisar que compara todos os valores, um a seguir ao outro, com o valor procurado. Também funciona com valores desordenados. - -### Pesquisa binária -auch: pesquisa binária -Uma forma de pesquisar em valores ordenados. Olha para o meio e continua a pesquisar só na metade certa. - -### Bubble sort -auch: bubble sort, Bubblesort, ordenação por bolha -Uma forma simples de ordenar. Compara sempre dois vizinhos e troca-os se estiverem na ordem errada. - -### Evento -auch: evento, eventos, Eventos -Algo que uma pessoa faz num programa com janelas, por exemplo clicar num botão. O programa reage com um ouvinte (ActionListener). - -### Interface -auch: Interfaces -Uma espécie de contrato em Java: diz que métodos uma classe tem de ter. \`ActionListener\` é uma interface com o método \`actionPerformed\`. - -### Exceção -auch: exceção, exceções, Exceções, Exception, Exceptions -Um erro que só acontece durante a execução, por exemplo um índice fora do array. Se nenhum \`catch\` a apanhar, o programa termina. - -### Atributo -auch: atributo, atributos, Atributos -Uma variável que está diretamente na classe e não dentro de um método. Com \`static\` pertence à própria classe e vive tanto tempo como o programa, por exemplo um contador de cliques que vários ouvintes alteram. - -### Scanner -Um bloco pronto do Java que lê entradas do teclado, por exemplo um número com \`scanner.nextInt()\`. Precisa de \`import java.util.Scanner;\` mesmo no início. - -### Comentário -auch: Comentários, comentário, comentários -Uma nota no código para pessoas. O Java salta-a ao executar. \`//\` vale até ao fim da linha, \`/* … */\` pode ocupar várias linhas. - -### import -Uma linha mesmo no início do código Java que vai buscar um bloco pronto a um pacote, por exemplo \`import java.util.Scanner;\`. Sem ela, o Java não conhece o nome. - -### código-fonte -auch: Código-fonte -O texto do programa que as pessoas escrevem e leem, por exemplo num ficheiro \`Tabuada.java\`. - -### compilador -auch: Compilador, compiladores -Um programa que traduz código-fonte. Em Java chama-se \`javac\` e transforma um ficheiro \`.java\` em bytecode num ficheiro \`.class\`. - -### bytecode -auch: Bytecode -A forma traduzida de um programa Java num ficheiro \`.class\`. Quem o lê é a JVM, não as pessoas. - -### JVM -auch: Java Virtual Machine, máquina virtual -A Java Virtual Machine executa bytecode. É um programa que funciona como um pequeno computador feito de software no Windows, no macOS ou no Linux. Por isso o Java é independente da plataforma. - -### JRE -Java Runtime Environment: a JVM e blocos prontos. Com ele podes executar programas Java, mas não compilá-los. - -### JDK -Java Development Kit: o JRE e ainda o compilador \`javac\` e outras ferramentas. É preciso para programar. - -### independente da plataforma -Um programa corre sem alterações em sistemas operativos diferentes. Em Java é a JVM que o permite: “Write once, run anywhere”. - -### código máquina -auch: Código máquina -Instruções em forma de números que o processador executa diretamente. Cada tipo de processador tem o seu próprio código máquina. - -### compilador JIT -auch: JIT -Uma parte da JVM que, durante a execução (“just in time”), traduz o bytecode muito usado em código máquina. Assim o Java fica mais rápido. -`,o9=`## Elementos básicos - -### Instrução - -Um único passo. Serve para criar variáveis ou para calcular. - -\`\`\` -int numero = 5 -double preco = 2.5 -String nome = "Ada" -numero = numero + 1 -\`\`\` - -### Tipos de dados - -O tipo de dados vem antes do nome da variável. - -| Tipo de dados | Significado | Exemplo | -|---|---|---| -| \`int\` | número inteiro | \`int quantidade = 3\` | -| \`double\` | número decimal (com ponto) | \`double preco = 2.5\` | -| \`String\` | texto entre aspas duplas | \`String nome = "Ada"\` | -| \`boolean\` | valor lógico | \`boolean pronto = false\` | -| \`char\` | um único caractere entre aspas simples | \`char nota = 'C'\` | - -\`'C'\` é um \`char\`, \`"C"\` é uma String. Um caractere de um texto obténs com \`charAt\`: \`char primeiro = nome.charAt(0)\`. Cada caractere tem um código de caractere: \`(int) 'A'\` é 65, \`(char) 66\` é \`'B'\`. - -### Entrada - -Pede um valor enquanto o estrutograma é executado e guarda-o numa variável. O texto entre aspas é a pergunta. - -\`\`\` -input: int idade "Quantos anos tens?" -input: double preco "Introduz um preço" -input: String nome "Como te chamas?" -\`\`\` - -### Saída - -Mostra um texto ou um valor. Os textos ficam entre aspas; juntas texto e variáveis com \`+\`. - -\`\`\` -output: "Olá" -output: soma -output: "Tens " + idade + " anos." -\`\`\` - -> Numa saída, \`+\` junta sempre as partes: com 3 e 4, \`output: a + b\` mostra 34. Por isso calcula primeiro numa variável, p. ex. \`int soma = a + b\`, e depois mostra \`soma\`. - -## Decisões - -### Decisão - -Verifica uma condição. Se for verdadeira, é executado o ramo da esquerda (true), senão o da direita (false). - -\`\`\` -idade >= 18 -numero % 2 == 0 -nota < 1 || nota > 5 -\`\`\` - -### Seleção - -Escolhe um de vários casos, consoante o valor de uma variável. Se nenhum caso servir, aplica-se \`default\`. Acrescentas casos à direita com **Adicionar caso**. - -## Ciclos - -### Ciclo while - -Repete o seu interior **enquanto** a condição for verdadeira. A condição é verificada antes de cada iteração. - -\`\`\` -numero != 1 -i < 10 -\`\`\` - -### Ciclo do-while - -Executa o seu interior **pelo menos uma vez** e só depois verifica a condição. - -### Ciclo for - -Conta uma variável desde um valor inicial até um limite. Os três campos são Início, Condição e Passo. - -\`\`\` -int i = 0 -i < 5 -i++ -\`\`\` - -### Ciclo infinito - -Repete o seu interior sem condição. Uma **Interrupção** lá dentro permite sair dele. - -### Interrupção - -Sai do ciclo que a rodeia (\`break\`). - -## Subprogramas - -### Chamada - -Representa um subprograma, p. ex. \`calcularSoma()\`. Com parâmetros e valor de retorno, o subprograma tem uma linha de cabeçalho como título: - -\`\`\` -int quadrado(int x) linha de cabeçalho do subprograma -return x * x devolve o resultado -int q = quadrado(5) chamada, q fica com 25 -mostrarSoma(3, 4) chamada de um subprograma void -\`\`\` - -## Cálculos e comparações - -### Operadores e comparações - -- \`+ - * /\` as quatro operações básicas -- \`%\` resto de uma divisão (\`7 % 2\` dá 1) -- \`==\` igual, \`!=\` diferente -- \`<\` \`<=\` \`>\` \`>=\` menor, menor ou igual, maior, maior ou igual -- \`&&\` e, \`||\` ou - -## Arrays - -### Arrays - -Vários valores com um só nome. Começa-se a contar em 0. - -\`\`\` -int[] numeros = {3, 7, 1} -output: numeros[0] -int[] vazio = new int[5] -\`\`\` - -Um **array 2D** é uma tabela com linhas e colunas: - -\`\`\` -boolean[][] lugares = new boolean[3][5] -lugares[1][4] = true -output: lugares.length + " filas, " + lugares[0].length + " lugares" -\`\`\` - -## Java - -### Do código ao programa - -O compilador \`javac\` traduz \`Ficheiro.java\` em bytecode (\`Ficheiro.class\`). A JVM traduz o bytecode em código máquina ao executá-lo, da mesma forma no Windows, no macOS e no Linux. Já o C++ passa diretamente a código máquina e tem de ser compilado de novo para cada sistema. - -| | contém | para | -|---|---|---| -| JRE | JVM e bibliotecas | executar | -| JDK | JRE, compilador \`javac\`, ferramentas | programar | - -### Comentários - -Notas no código. O Java salta-as, o estrutograma fica igual. - -\`\`\`java -/* - * Tarefa: somar os números de 1 a 10 - * (comentário de várias linhas) - */ -// Processamento (comentário de uma linha) -int soma = 0; -for (int i = 1; i <= 10; i++) { - soma = soma + i; // comentário depois do código -} // fim do for -\`\`\` - -Serve para: a tarefa, dividir o código em entrada, processamento e saída, uma explicação, o fim de um bloco depois de \`}\`, a saída esperada e notas para a próxima vez. -`,l9=`--- -titel: Alter im Unterprogramm -nach: 18 -stufe: 2 ---- - -Die Altersprüfung aus „Volljährig?“ wird jetzt ein eigenes Unterprogramm. Das Hauptprogramm ruft es nur noch auf. - -::: übung aufgabe-alter-unterprogramm -titel: Alter im Unterprogramm -auftrag: Baue zwei Diagramme. Das Hauptprogramm gibt **Willkommen** aus, ruft dann \`pruefeAlter()\` auf und gibt zum Schluss **Auf Wiedersehen** aus. - Das Unterprogramm \`pruefeAlter\` steht in einem eigenen Reiter: Es fragt nach dem Alter und gibt **volljährig** oder **minderjährig** aus. -test: 20 -> Willkommen | volljährig | Auf Wiedersehen -test: 18 -> Willkommen | volljährig | Auf Wiedersehen -test: 12 -> Willkommen | minderjährig | Auf Wiedersehen -braucht: aufruf -tipp: Das Hauptprogramm hat drei Kästen. Die eigentliche Arbeit macht das Unterprogramm. -tipp: Öffne die Übung im Editor und baue dort das Hauptprogramm mit dem Aufruf \`pruefeAlter()\`. Lege dann mit **+** einen neuen Reiter an und gib ihm den Titel \`pruefeAlter\`. Darin baust du die Eingabe und die Verzweigung. Prüfen findet das Unterprogramm über den Titel des Reiters. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: -`,u9=`--- -titel: Ballwurf auswerten -nach: 25 -stufe: 2 ---- - -Beim Sportfest haben alle ihre Weite beim Ballwurf in eine Tabelle eingetragen: - -::: datei ballwurf.csv -Name;Weite -Ludwig;28 -Zora;42 -Fuchs;35 -Hase;21 -::: - -::: übung aufgabe-ballwurf -titel: Ballwurf auswerten -code: java -auftrag: Lies die Datei ein und überspringe die Kopfzeile. Gib aus, wie viele mitgemacht haben (**Teilnehmer: 4**) und wie weit im Schnitt geworfen wurde (**Schnitt: 31.5 m**). - Speichere den weitesten Wurf in der Datei **sieger.txt**, zum Beispiel **Sieger: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Teilnehmer: 4 | Schnitt: 31.5 m -datei: sieger.txt -> Sieger: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: Du brauchst vier Variablen vor der Schleife: die Anzahl, die Summe als \`double\`, die beste Weite und den besten Namen. -tipp: In der Schleife: \`String[] teile = zeile.split(";");\` und \`int weite = Integer.parseInt(teile[1]);\`. Ist \`weite > besteWeite\`, merkst du dir Weite und Namen. Zum Speichern: \`PrintWriter aus = new PrintWriter("sieger.txt");\`, \`aus.println(…)\` und \`aus.close();\`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: -`,m9=`--- -titel: Bestenliste -nach: 19 -stufe: 2 ---- - -Fünf Ziegen sind beim Hindernislauf gestartet. Jetzt soll die Bestenliste an die Stalltür: die meisten Punkte oben, die wenigsten unten. - -::: übung aufgabe-bestenliste -titel: Bestenliste -auftrag: Lies 5 Punktzahlen in ein Array ein. Sortiere das Array mit Bubblesort **absteigend**, also die größte Zahl zuerst. Gib danach die Liste mit Platz aus, zum Beispiel **1. 9**, in der nächsten Zeile **2. 9** und so weiter. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Bubblesort aus Lektion 19 sortiert aufsteigend. Für absteigend drehst du nur den Vergleich der Nachbarn um. -tipp: Getauscht wird, wenn \`punkte[i] < punkte[i + 1]\` ist, mit einer Hilfsvariablen. Für die Ausgabe rechnest du zuerst \`int platz = i + 1\` aus und gibst dann \`platz + ". " + punkte[i]\` aus. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-bestenliste-java -titel: Bestenliste in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Lies jede Zahl erst in eine Variable ein und schreibe sie dann ins Array: \`int zahl = scanner.nextInt();\` und \`punkte[i] = zahl;\`. -tipp: Der Tausch braucht auch in Java eine Hilfsvariable: \`int hilf = punkte[i]; punkte[i] = punkte[i + 1]; punkte[i + 1] = hilf;\`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: -`,c9=`--- -titel: Binärzahl -nach: 11 -stufe: 3 ---- - -Computer rechnen mit nur zwei Ziffern: 0 und 1. So wandelst du eine Zahl ins **Binärsystem** um: - -1. Teile die Zahl durch 2 und merke dir den Rest (0 oder 1). -2. Rechne mit dem Ergebnis ohne Rest weiter. -3. Wiederhole das, bis die Zahl 0 ist. - -Die Reste von **unten nach oben** gelesen ergeben die Binärzahl. Bei 6 sind die Reste 0, 1, 1, also lautet die Binärzahl 110. - -::: übung aufgabe-binaer -titel: Binärzahl -auftrag: Frage nach einer ganzen Zahl größer als 0 und gib sie als Binärzahl aus. Bei 6 lautet die Ausgabe **110**, bei 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Teile die Zahl immer wieder durch 2 und merke dir jeden Rest. Die Reste rückwärts gelesen ergeben die Binärzahl. -tipp: Sammle die Ziffern in einem Text: \`String binaer = ""\`. In der Schleife berechnest du \`int rest = zahl % 2\` und hängst den Rest **vorne** an: \`binaer = rest + binaer\`. Danach \`zahl = zahl / 2\`. Weil zahl eine ganze Zahl ist, fällt der Rest dabei weg. -lösung: aufgabe-binaer-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-binaer-java -titel: Binärzahl in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Fang mit \`String binaer = "";\` an. Die Schleife läuft mit \`while (zahl > 0)\`. -tipp: Im Rumpf: \`int rest = zahl % 2;\`, dann \`binaer = rest + binaer;\` und \`zahl = zahl / 2;\`. Weil \`binaer\` ein String ist, hängt \`+\` den Rest vorne an. -lösung: aufgabe-binaer-loesung.visustruct -::: -`,d9=`--- -titel: Caesar-Verschlüsselung -nach: 17 -stufe: 3 ---- - -Schon vor über 2000 Jahren schickte der römische Feldherr Julius Caesar geheime Nachrichten an seine Truppen. Sein Trick war einfach: Er ersetzte jeden Buchstaben durch den Buchstaben, der **drei Stellen weiter** im Alphabet steht. Aus A wird D, aus B wird E und so weiter. Die lesbare Nachricht heißt **Klartext**, die verschlüsselte **Geheimtext**. Um wie viele Stellen verschoben wird, ist der **Schlüssel**, oft kurz \`k\` genannt. - -::: grafik caesar-verschiebung.svg -Verschlüsselung mit dem Schlüssel 3: Jeder Buchstabe oben wird zum Buchstaben darunter. Am Ende geht es von vorn weiter, aus X, Y und Z werden A, B und C. -::: - -Aus \`HALLO\` wird so \`KDOOR\`. Wer den Schlüssel kennt, schiebt einfach drei Stellen zurück und kann die Nachricht wieder lesen. - -### Die Chiffrierscheibe - -Damit man nicht jedes Mal abzählen muss, baute man sich eine **Chiffrierscheibe**: zwei Scheiben mit dem Alphabet, eine kleine auf einer großen, in der Mitte drehbar befestigt. Außen steht der Klartext, innen der Geheimtext. Dreht man die innere Scheibe um drei Buchstaben, steht unter jedem Buchstaben außen sein Geheimbuchstabe. Solche Scheiben gibt es seit dem 15. Jahrhundert. Im Amerikanischen Bürgerkrieg (1861–1865) trugen Soldaten kleine Scheiben aus Messing bei sich. - -::: grafik caesar-scheibe.svg -Eine Chiffrierscheibe nach dem Vorbild historischer Messingscheiben. Das blaue Fenster zeigt: Mit dem Schlüssel 3 steht unter A das D. -::: - -::: angeberwissen -Caesar selbst hatte noch gar keine Scheibe! Das wissen wir vom römischen Geschichtsschreiber Sueton: Caesar verschob in seinen Briefen jeden Buchstaben um drei Stellen und zählte das einfach im Kopf ab. Die Scheibe erfand erst Leon Battista Alberti um 1467, also rund 1500 Jahre später. Ich zähle übrigens auch im Kopf, ich habe ja keine Finger, nur Hufe. -::: - -Probiere es selbst aus. Drehe die innere Scheibe, tippe einen Text ein und schau zu, wie der Geheimtext entsteht. Mit **Entschlüsseln** geht es rückwärts. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -Eine echte Scheibe kannst du ganz leicht selbst basteln: zwei Kreise aus Pappe, einer etwas kleiner, je 26 gleich große Felder mit dem Alphabet und in der Mitte eine Musterklammer. Fertig ist dein Geheimdienst-Werkzeug! -::: - -::: quiz -? Was wird mit dem Schlüssel 3 aus dem Buchstaben Y? -- [ ] A -- [x] B -- [ ] C -> Nach Y kommen Z, dann geht es von vorn los mit A und B. Drei Stellen weiter ist also B. - -? Ein Geheimtext wurde mit dem Schlüssel 5 verschlüsselt. Wie entschlüsselst du ihn? -- [ ] Noch einmal 5 Stellen weiter schieben -- [x] 5 Stellen zurück schieben -- [ ] Die Buchstaben rückwärts lesen -> Entschlüsseln ist das Gegenteil vom Verschlüsseln. Fünf Stellen zurück ist übrigens dasselbe wie 21 Stellen weiter, denn 5 + 21 = 26. -::: - -### Das Programm: Es gibt mehr als einen Weg - -Wie bringst du dem Computer bei, einen Buchstaben um \`k\` Stellen zu verschieben? Dafür gibt es nicht *die eine* richtige Lösung. Wir schauen uns zwei Wege an. Beide gehen den Text mit einer For-Schleife Zeichen für Zeichen durch, so wie in der Lektion Zeichenketten. Der Unterschied ist, wie sie einen einzelnen Buchstaben verschieben. - -In beiden Wegen hilft der Rest-Operator \`%\`. Er sorgt dafür, dass es nach Z wieder bei A weitergeht: Die Positionen 0 bis 25 bleiben, wie sie sind, aus 26 wird 0, aus 27 wird 1 und so weiter. - -### Weg 1: Das Alphabet in einem Array - -Du legst das Alphabet in ein Array \`char[] alphabet = {'A', 'B', …, 'Z'}\`. Für jeden Buchstaben des Wortes suchst du im Array seine Position \`pos\`, also seinen Index. Der Geheimbuchstabe steht dann im selben Array an der Stelle \`(pos + k) % 26\`. - -Ein Beispiel mit dem Schlüssel 3: X steht an der Position 23. Dann ist \`(23 + 3) % 26 = 0\`, und an der Position 0 steht A. - -::: übung aufgabe-caesar-array -titel: Caesar mit Array -auftrag: Das Startdiagramm enthält schon das Array \`alphabet\`. Frage nach einem Wort aus Großbuchstaben und nach dem Schlüssel \`k\`. Gib dann das verschlüsselte Wort aus. Bei HALLO und 3 also \`KDOOR\`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Lege einen leeren Text \`String geheim = ""\` an. Gehe das Wort mit einer For-Schleife Zeichen für Zeichen durch. In jedem Durchlauf hängst du einen Geheimbuchstaben hinten an \`geheim\` an. -tipp: Um die Position eines Buchstabens zu finden, brauchst du eine zweite, innere Schleife über das Array: Ist \`alphabet[j] == zeichen\`, merkst du dir \`pos = j\`. -tipp: Nach der inneren Schleife hängst du \`alphabet[(pos + k) % 26]\` an: \`geheim = geheim + alphabet[(pos + k) % 26]\`. Nach der äußeren Schleife gibst du \`geheim\` aus. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Weg 2: Rechnen mit der ASCII-Tabelle - -Für den Computer ist jedes Zeichen eigentlich eine Zahl. Welche Zahl zu welchem Zeichen gehört, steht in der **ASCII-Tabelle**. Dort hat A den Code 65, B den Code 66 und so weiter bis Z mit 90. In Java wandelst du ein Zeichen mit \`(int)\` in seinen Code um und einen Code mit \`(char)\` zurück in ein Zeichen: - -\`\`\` -int code = (int) 'A' -char zeichen = (char) 68 -\`\`\` - -Danach ist \`code\` gleich 65 und \`zeichen\` gleich \`'D'\`. So eine Umwandlung von einem Datentyp in einen anderen heißt **Typumwandlung** (englisch *cast*). - -Jetzt kannst du mit dem Buchstaben rechnen. Damit der Sprung von Z zurück zu A klappt, ziehst du erst 65 ab. Dann hat A die Position 0 und Z die Position 25, genau wie in Weg 1. Du verschiebst mit \`% 26\` und zählst am Ende die 65 wieder dazu: - -::: grafik caesar-ascii.svg -Oben die Großbuchstaben mit ihren ASCII-Codes. Unten der Rechenweg für X mit dem Schlüssel 3: Heraus kommt A. -::: - -::: übung aufgabe-caesar-ascii -titel: Caesar mit ASCII -code: java -auftrag: Schreibe ein Java-Programm, das ein Wort aus Großbuchstaben und den Schlüssel \`k\` einliest und das verschlüsselte Wort ausgibt. Diesmal ohne Array: Rechne mit \`(int)\` und \`(char)\`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Gehe das Wort mit einer For-Schleife durch. Den Code des Zeichens an der Stelle \`i\` bekommst du mit \`int code = (int) wort.charAt(i);\`. -tipp: Rechne \`code = (code - 65 + k) % 26 + 65;\` und hänge das Zeichen an: \`geheim = geheim + (char) code;\`. Vor der Schleife steht \`String geheim = "";\`, nach der Schleife \`System.out.println(geheim);\`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Welcher Weg ist besser? - -**Weg 1: Array.** Die Idee ist, die Position im Array zu suchen. -- Vorteil: leicht zu verstehen, und auch eigene Alphabete gehen, z. B. mit Ä, Ö und Ü. -- Nachteil: Für jeden Buchstaben muss gesucht werden. - -**Weg 2: ASCII.** Die Idee ist, mit dem Zeichencode zu rechnen. -- Vorteil: kurz und ohne Suche, darum schneller. -- Nachteil: Man muss die ASCII-Codes kennen. - -Beide Programme liefern dasselbe Ergebnis. Genau so ist es oft beim Programmieren: Es gibt mehrere richtige Lösungen, und du wählst die, die am besten zu deiner Aufgabe passt. - -::: übung aufgabe-caesar-entschluesseln -titel: Caesar entschlüsseln -code: java -auftrag: Jetzt drehst du den Spieß um. Lies einen Geheimtext aus Großbuchstaben und den Schlüssel \`k\` ein und gib den Klartext aus. Bei KDOOR und 3 also \`HALLO\`. Welchen Weg du nimmst, entscheidest du. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Entschlüsseln heißt: \`k\` Stellen zurück statt vor. Aus dem \`+ k\` wird also ein \`- k\`. -tipp: Vorsicht bei A: \`0 - 3\` ergibt -3, und \`%\` macht daraus in Java keine positive Zahl. Zähle darum noch 26 dazu: \`code = (code - 65 - k + 26) % 26 + 65;\` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Sicher ist Caesar leider nicht. Es gibt nur 25 Schlüssel, die kann man in ein paar Minuten alle ausprobieren. Noch schneller geht es mit einem Trick: Im Deutschen ist E der häufigste Buchstabe. Kommt im Geheimtext H am häufigsten vor, war der Schlüssel wohl 3. Im Internet gibt es übrigens ROT13, das ist Caesar mit dem Schlüssel 13. Zweimal ROT13 ergibt wieder den Klartext, weil 13 + 13 = 26. -::: -`,h9=`--- -titel: Collatz-Folge -nach: 11 -stufe: 2 ---- - -Die Collatz-Folge ist ein berühmtes Rätsel der Mathematik. Du beginnst mit einer Zahl und wendest immer wieder dieselbe Regel an: - -- Ist die Zahl **gerade**, teilst du sie durch 2. -- Ist sie **ungerade**, rechnest du mal 3 plus 1. - -Bisher endet die Folge bei jeder ausprobierten Startzahl irgendwann bei 1. Ob das für alle Zahlen gilt, weiß bis heute niemand. - -::: übung aufgabe-collatz -titel: Collatz-Folge -auftrag: Frage nach einer Startzahl. Wende die Regel so lange an, bis die Zahl 1 ist, und gib nach jedem Schritt die neue Zahl aus. Zähle dabei die Schritte mit und gib am Ende **Schritte:** und die Anzahl aus. - Bei der Startzahl 6 lautet die Folge 3, 10, 5, 16, 8, 4, 2, 1. Das sind 8 Schritte. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 7 -test: 1 -> Schritte: 0 -braucht: whileSchleife, verzweigung -tipp: Du weißt nicht, wie viele Schritte es werden. Darum passt eine While-Schleife besser als eine For-Schleife. -tipp: Die While-Schleife läuft, solange \`zahl != 1\` gilt. Ob eine Zahl gerade ist, prüfst du mit \`zahl % 2 == 0\`. Die Ausgabe und \`schritte++\` kommen nach der Verzweigung, aber noch in die Schleife. -lösung: aufgabe-collatz-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-collatz-java -titel: Collatz-Folge in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Schritte: 7 -test: 1 -> Schritte: 0 -braucht: whileSchleife, verzweigung -tipp: Eine \`while\`-Schleife läuft, solange \`zahl != 1\` ist. Darin entscheidet ein \`if\` mit \`zahl % 2 == 0\`. -tipp: Im Rumpf: halbieren oder \`zahl = zahl * 3 + 1;\`, dann \`System.out.println(zahl);\` und \`schritte++;\`. Nach der Schleife kommt \`"Schritte: " + schritte\`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -Mit dieser Aufgabe kann man reich werden! Der Mathematiker Paul Erdős setzte für einen Beweis 500 Dollar aus. 2021 legte die japanische Firma Bakuage noch einiges drauf: 120 Millionen Yen, damals umgerechnet rund eine Million Dollar, für einen Beweis oder ein Gegenbeispiel. Abgeholt hat das Geld bis heute niemand. Erfunden hat das Rätsel übrigens der deutsche Mathematiker Lothar Collatz im Jahr 1937. Ich rechne gleich mal los, für so viel Geld gibt es eine Menge Heu. -::: -`,g9=`--- -titel: Einheitenrechner -nach: 35 -stufe: 2 ---- - -Baue ein Fenster, das Längen umrechnet. Oben tippst du eine Länge in Metern ein, darunter stehen drei Knöpfe: - -- **in cm** zeigt die Länge in Zentimetern, aus 1.5 wird „150.0 cm“, -- **in mm** zeigt sie in Millimetern, aus 1.5 wird „1500.0 mm“, -- **in km** zeigt sie in Kilometern, aus 1500 wird „1.5 km“. - -Das Ergebnis steht in einem JLabel unter den Knöpfen. Steht im Feld keine Zahl, erscheint dort „Bitte eine Zahl eingeben“, und das Programm stürzt nicht ab. - -So kannst du anfangen: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Einheiten { - public static void main(String[] args) { - JFrame fenster = new JFrame("Einheitenrechner"); - fenster.setSize(300, 200); - fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - fenster.setLayout(new GridLayout(0, 1)); - - JTextField meter = new JTextField(); - JButton inCm = new JButton("in cm"); - JLabel ergebnis = new JLabel("?", SwingConstants.CENTER); - - inCm.addActionListener(e -> { - double m = Double.parseDouble(meter.getText()); - ergebnis.setText((m * 100) + " cm"); - }); - - fenster.add(meter); - fenster.add(inCm); - fenster.add(ergebnis); - fenster.setVisible(true); - } -} -\`\`\` - -**Du bist fertig, wenn …** - -- 1.5 nach **in cm** „150.0 cm“ zeigt, nach **in mm** „1500.0 mm“, -- 1500 nach **in km** „1.5 km“ zeigt, -- „abc“ im Feld „Bitte eine Zahl eingeben“ zeigt und das Fenster offen bleibt. - -Zusatz: Ein vierter Knopf **in Fuß** rechnet in die englische Einheit um. Ein Fuß sind 0.3048 m. - -::: ludwig -Tick im [Zahnrad](#zeige-einstellungen) **Echtes Java (mit Swing)** an. Für „abc“ im Feld brauchst du in jedem Zuhörer ein try/catch mit \`NumberFormatException\`, wie beim Taschenrechner. -::: -`,p9=`--- -titel: Fibonacci-Zahlen -nach: 13 -stufe: 2 ---- - -Bei den Fibonacci-Zahlen ist jede Zahl die Summe der beiden Zahlen davor. Die Folge beginnt mit 1 und 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -Diese Zahlen findest du erstaunlich oft in der Natur, zum Beispiel bei der Anordnung von Sonnenblumenkernen. - -::: ludwig -Erinnerst du dich ans **Tauschen** aus Lektion 5? Dort brauchtest du eine Hilfsvariable, weil \`a = b\` den alten Wert von \`a\` überschreibt. Hier ist es genauso: Bevor \`zahl2\` die neue Zahl bekommt, muss ihr alter Wert in \`zahl1\` gerettet sein. Achte also auf die **Reihenfolge**: erst \`zahl1 = zahl2\`, dann \`zahl2 = zahl3\`. Dieses Weiterschieben und Tauschen brauchst du später wieder, zum Beispiel beim Sortieren in Lektion 19. -::: - -::: angeberwissen -Leonardo von Pisa, genannt Fibonacci, stellte 1202 in seinem Rechenbuch eine Aufgabe über Kaninchen: Jedes Kaninchenpaar bekommt ab dem zweiten Monat jeden Monat ein neues Paar. Wie viele Paare gibt es nach einem Jahr? Die Antwort ist diese Zahlenfolge. Ziegen kamen in dem Buch leider nicht vor. -::: - -::: übung aufgabe-fibonacci -titel: Fibonacci-Zahlen -auftrag: Frage, wie viele Fibonacci-Zahlen ausgegeben werden sollen. Gib dann genau so viele aus, jede in einer eigenen Zeile. Du kannst davon ausgehen, dass mindestens 2 eingegeben wird. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Jede neue Zahl ist die Summe der beiden vorherigen. Du musst dir also immer die letzten zwei Zahlen merken. -tipp: Merke dir die beiden letzten Zahlen in \`zahl1\` und \`zahl2\` und gib beide vor der Schleife aus. Die Schleife beginnt bei \`int i = 2\`. Darin berechnest du \`int zahl3 = zahl1 + zahl2\`, gibst sie aus und schiebst dann weiter: \`zahl1 = zahl2\` und \`zahl2 = zahl3\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-fibonacci-java -titel: Fibonacci-Zahlen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Die ersten beiden Zahlen gibst du vor der Schleife aus. Die \`for\`-Schleife beginnt dann bei \`int i = 2\`. -tipp: Im Rumpf: \`int zahl3 = zahl1 + zahl2;\`, ausgeben, dann \`zahl1 = zahl2;\` und \`zahl2 = zahl3;\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Warum muss in der Schleife \`zahl1 = zahl2\` vor \`zahl2 = zahl3\` stehen? -- [ ] Weil Java die Zuweisungen sonst nicht erlaubt -- [x] Weil sonst der alte Wert von zahl2 überschrieben ist, bevor zahl1 ihn bekommt -- [ ] Die Reihenfolge ist egal -> Andersherum hätten zahl1 und zahl2 danach denselben Wert. Wie beim Tauschen gilt: Erst retten, dann überschreiben. -::: -`,f9=`--- -titel: Gerade oder ungerade -nach: 8 -stufe: 1 ---- - -Eine Zahl ist gerade, wenn beim Teilen durch 2 kein Rest bleibt. Den Rest berechnest du mit \`%\`: \`8 % 2\` ist 0, \`7 % 2\` ist 1. - -::: übung aufgabe-gerade -titel: Gerade oder ungerade -auftrag: Frage nach einer ganzen Zahl. Gib aus, ob sie gerade oder ungerade ist. Bei 4 lautet die Ausgabe **4 ist gerade**, bei 7 **7 ist ungerade**. -test: 4 -> 4 ist gerade -test: 7 -> 7 ist ungerade -test: 0 -> 0 ist gerade -test: 15 -> 15 ist ungerade -braucht: verzweigung -tipp: Eine Verzweigung entscheidet zwischen gerade und ungerade. Gerade heißt: durch 2 teilbar ohne Rest. -tipp: Die Bedingung lautet \`zahl % 2 == 0\`. In der Ausgabe verbindest du die Zahl und den Text mit \`+\`, z. B. \`zahl + " ist gerade"\`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-gerade-java -titel: Gerade oder ungerade in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 4 -> 4 ist gerade -test: 7 -> 7 ist ungerade -test: 0 -> 0 ist gerade -test: 15 -> 15 ist ungerade -braucht: verzweigung -tipp: Eine Zahl liest du mit \`int zahl = scanner.nextInt();\` ein. Vorher fragst du mit \`System.out.println(…)\`. -tipp: \`if (zahl % 2 == 0) { … } else { … }\`. Die Ausgabe ist \`System.out.println(zahl + " ist gerade");\`. -lösung: aufgabe-gerade-loesung.visustruct -::: -`,b9=`--- -titel: Mehrwertsteuer -nach: 6 -stufe: 1 ---- - -Auf fast alles, was du kaufst, kommen 19 % **Mehrwertsteuer**. Der Laden rechnet mit dem Nettopreis (ohne Steuer) und schlägt die Steuer drauf. So entsteht der Bruttopreis, den du an der Kasse zahlst. - -Ein Beispiel: Du kaufst ein Falt-Handy, das aufgeklappt zum Tablet wird. Auf dem Preisschild des Händlers stehen 1000 € netto. Dazu kommen 19 %, also 190 €. An der Kasse zahlst du 1190 €. Die 190 € darf der Händler nicht behalten: Er führt sie an das Finanzamt ab. - -::: grafik aufgabe-mehrwertsteuer.svg -Nettopreis plus Mehrwertsteuer ergibt den Bruttopreis. Die Mehrwertsteuer geht an das Finanzamt. -::: - -::: angeberwissen -Mäh, der Händler hat das Handy ja selbst eingekauft: beim Hersteller für 800 € plus 152 € Mehrwertsteuer. Diese 152 € heißen **Vorsteuer**, und die darf er von den 190 € abziehen. Ans Finanzamt überweist er also nur 38 €. Die ganze Steuer zahlt am Ende nur einer: der Kunde an der Kasse. Also du. Ich zahle übrigens gar keine, ich fresse nur Gras. -::: - -Der Steuersatz ändert sich beim Rechnen nie. Darum legst du ihn als **Konstante** an: Das ist eine Variable, deren Wert nach dem Festlegen gleich bleibt. Du schreibst \`const\` davor, zum Beispiel \`const double MWST = 0.19\`. Namen von Konstanten schreibt man in Großbuchstaben, dann erkennt man sie sofort. Im Java-Code wird daraus \`final double MWST = 0.19;\`. - -Wenn sich der Steuersatz einmal ändert, musst du nur diese eine Zeile anpassen. - -::: übung aufgabe-mehrwertsteuer -titel: Mehrwertsteuer -auftrag: Lege die Konstante \`MWST\` mit dem Wert 0.19 an. Frage dann nach dem Nettopreis. Gib zuerst die Steuer und danach den Bruttopreis aus, jeweils nur die Zahl. - Bei einem Nettopreis von 100 lauten die Ausgaben **19** und **119**. -test: 100 -> 19 | 119 -test: 50 -> 9.5 | 59.5 -test: 0 -> 0 | 0 -tipp: Die Steuer ist 19 Prozent vom Nettopreis. Der Bruttopreis ist das, was man am Ende bezahlt. -tipp: Die Steuer ist \`nettopreis * MWST\`. Den Bruttopreis bekommst du, wenn du die Steuer zum Nettopreis addierst. Beide Werte sind Kommazahlen, also \`double\`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Warum legst du den Steuersatz als Konstante an? -- [ ] Weil Konstanten schneller gerechnet werden -- [x] Weil sich sein Wert im Programm nicht ändern soll -- [ ] Weil man Kommazahlen nur als Konstante speichern kann -> Eine Konstante behält ihren Wert. Ändert sich der Steuersatz einmal, passt du nur die eine Zeile mit \`const\` an. -::: -`,x9=`--- -titel: Notendurchschnitt -nach: 13 -stufe: 2 ---- - -Wie gut war die letzte Zeit? Der Durchschnitt ist die Summe aller Noten geteilt durch ihre Anzahl. - -::: übung aufgabe-durchschnitt -titel: Notendurchschnitt -auftrag: Frage zuerst, wie viele Noten eingegeben werden. Lies dann genau so viele Noten ein und gib den Durchschnitt aus, nur die Zahl. - Bei den Noten 1 und 2 ist der Durchschnitt **1.5**. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: Du brauchst die Summe aller Noten und ihre Anzahl. Der Durchschnitt ist die Summe geteilt durch die Anzahl. -tipp: Die For-Schleife läuft von \`int i = 0\` bis \`i < anzahl\`. Darin liest du eine Note ein und zählst sie zur Summe. Den Durchschnitt speicherst du als Kommazahl: \`double schnitt = summe * 1.0 / anzahl\`. Das \`* 1.0\` sorgt dafür, dass auch in Java nicht ganzzahlig geteilt wird. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-durchschnitt-java -titel: Notendurchschnitt in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: \`for (int i = 0; i < anzahl; i++)\` liest genau so viele Noten ein, wie vorher angegeben. -tipp: Teile mit \`double schnitt = summe * 1.0 / anzahl;\`. Ohne \`* 1.0\` teilt Java zwei \`int\` ganzzahlig, und aus 1.5 wird 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: -`,v9=`--- -titel: PIN prüfen -nach: 12 -stufe: 2 ---- - -Am Geldautomaten hast du drei Versuche für deine PIN. Gibst du dreimal eine falsche PIN ein, wird die Karte gesperrt. - -::: angeberwissen -John Shepherd-Barron, der 1967 den ersten Geldautomaten in London aufstellte, wollte eigentlich sechsstellige PINs. Seine Frau sagte aber, sie könne sich höchstens vier Ziffern merken. Deshalb hat deine PIN heute vier Stellen. -::: - -::: übung aufgabe-pin -titel: PIN prüfen -auftrag: Die richtige PIN ist 1234. Frage so lange nach der PIN, bis sie richtig ist oder drei Versuche verbraucht sind. Gib danach **PIN richtig** oder **Karte gesperrt** aus. -test: 1234 -> PIN richtig -test: 1111, 1234 -> PIN richtig -test: 1, 2, 1234 -> PIN richtig -test: 1, 2, 3 -> Karte gesperrt -braucht: doUntilSchleife, verzweigung -tipp: Die Schleife endet in zwei Fällen: wenn die PIN stimmt oder wenn drei Versuche verbraucht sind. Zähle die Versuche mit. -tipp: Die PIN wird mindestens einmal abgefragt, deshalb passt eine Do-while-Schleife. Lege vorher \`int pin = 0\` und \`int versuche = 0\` an. Die Schleife wiederholt sich, solange \`pin != 1234 && versuche < 3\` gilt. Nach der Schleife entscheidet eine Verzweigung, was ausgegeben wird. -lösung: aufgabe-pin-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-pin-java -titel: PIN prüfen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 1234 -> PIN richtig -test: 1111, 1234 -> PIN richtig -test: 1, 2, 1234 -> PIN richtig -test: 1, 2, 3 -> Karte gesperrt -braucht: doUntilSchleife, verzweigung -tipp: \`do { … } while (…);\` fragt mindestens einmal. Hinter \`while (…)\` steht ein Semikolon. -tipp: Lege vorher \`int pin = 0;\` und \`int versuche = 0;\` an. In der Schleife: \`int eingabe = scanner.nextInt();\`, \`pin = eingabe;\` und \`versuche++;\`. Die Bedingung ist \`pin != 1234 && versuche < 3\`. -lösung: aufgabe-pin-java-loesung.visustruct -::: -`,w9=`--- -titel: Schiffe versenken -nach: 16 -stufe: 2 ---- - -Beim Spiel Schiffe versenken ist das Meer ein Gitter. Die Spalten heißen A bis E, die Reihen 1 bis 5. Im Startdiagramm steht das Meer als 2D-Array \`meer\`: \`true\` heißt, auf diesem Feld liegt ein Teil eines Schiffs. - -::: grafik schiffe-versenken.svg -Grau heißt \`true\`: Hier liegt ein Schiff. Die kleinen grauen Zahlen sind die Indizes im Array, sie beginnen bei 0. Im Spiel heißen die Felder dagegen A bis E und 1 bis 5. -::: - -::: übung aufgabe-schiffe -titel: Schiffe versenken -auftrag: Gib alle Felder aus, auf denen ein Schiff liegt, jedes Feld in einer eigenen Zeile, z. B. **B1**. Gehe dafür mit zwei verschachtelten Schleifen Reihe für Reihe durch das Meer. - Für das Startdiagramm erscheinen **B1**, **C1**, **D1**, **E3**, **E4** und **A5**. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Gehe das Meer wie den Kinosaal mit zwei verschachtelten Schleifen durch. Gib nur die Felder aus, auf denen \`true\` steht. -tipp: Die äußere Schleife zählt \`reihe\`, die innere \`spalte\`, beide ab 0. Liegt auf \`meer[reihe][spalte]\` ein Schiff, gibst du den Buchstaben aus \`spalten[spalte]\` und die Reihennummer aus: \`output: spalten[spalte] + (reihe + 1)\`. Das \`+ 1\` braucht es, weil das Spiel bei 1 zu zählen beginnt. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-schiffe-java -titel: Schiffe versenken in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Zwei \`for\`-Schleifen ineinander: außen die Reihe, innen die Spalte, beide bis \`.length\`. -tipp: \`if (meer[reihe][spalte]) { System.out.println(spalten[spalte] + (reihe + 1)); }\`. Die Klammer um \`reihe + 1\` sorgt dafür, dass Java erst rechnet. -lösung: aufgabe-schiffe-loesung.visustruct -::: -`,y9=`--- -titel: Speisekarte -nach: 10 -stufe: 1 ---- - -In der Mensa bestellst du über eine Nummer. Das Programm sagt dir, welches Gericht zu der Nummer gehört. - -::: übung aufgabe-speisekarte -titel: Speisekarte -auftrag: Frage nach einer Nummer. Gib bei 1 **Griechischer Salat**, bei 2 **Reis-Bowl** und bei 3 **Falafel-Wrap** aus. Bei jeder anderen Nummer gib **Bitte eine gültige Nummer wählen** aus. -test: 1 -> Griechischer Salat -test: 2 -> Reis-Bowl -test: 3 -> Falafel-Wrap -test: 7 -> Bitte eine gültige Nummer wählen -braucht: fallauswahl -tipp: Für mehrere feste Nummern passt eine Fallauswahl. Was passiert mit allen anderen Nummern? -tipp: Lies die Nummer mit \`input: int nummer "Welches Gericht?"\` ein. Die Fallauswahl prüft \`nummer\`, die Fälle heißen \`1\`, \`2\` und \`3\`. Alle anderen Nummern landen im Fall \`default\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-speisekarte-java -titel: Speisekarte in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 1 -> Griechischer Salat -test: 2 -> Reis-Bowl -test: 3 -> Falafel-Wrap -test: 7 -> Bitte eine gültige Nummer wählen -braucht: fallauswahl -tipp: In Java ist das eine Fallauswahl mit \`switch (nummer) { … }\`. -tipp: Jeder Fall sieht so aus: \`case 1 -> { System.out.println("…"); }\`. Für alle anderen Nummern gibt es \`default -> { … }\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: -`,z9=`--- -titel: Summe bilden -nach: 2 -stufe: 1 ---- - -Lege zwei Zahlen fest, addiere sie und gib das Ergebnis aus. - -::: übung aufgabe-summe -titel: Summe bilden -auftrag: Lege \`zahl1\` mit dem Wert 4 und \`zahl2\` mit dem Wert 3 fest. Berechne die Summe in einer dritten Variablen und gib nur die Summe aus. -test: - -> 7 -tipp: Du brauchst drei Variablen: zwei für die Zahlen und eine für das Ergebnis. -tipp: Eine Variable legst du mit einer Anweisung wie \`int zahl1 = 4\` fest. Für die Ausgabe schreibst du den Namen der Variablen ohne Anführungszeichen. -lösung: aufgabe-summe-loesung.visustruct -::: -`,I9=`--- -titel: Tafeldienst auslosen -nach: 15 -stufe: 2 ---- - -Wer hat diese Woche Tafeldienst? Fünf Namen kommen in ein Array, dann wird eine Losnummer gezogen. - -::: übung aufgabe-tafeldienst -titel: Tafeldienst auslosen -auftrag: Lies fünf Namen in ein String-Array ein. Frage danach nach einer Losnummer von 1 bis 5 und gib **Tafeldienst:** und den passenden Namen aus. - Die Losnummer 1 gehört zum ersten Namen, die 5 zum letzten. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Tafeldienst: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Tafeldienst: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Tafeldienst: Pia -braucht: forSchleife -tipp: Erst liest eine Schleife die fünf Namen ein. Danach brauchst du keine Schleife mehr, nur noch einen Zugriff auf ein Fach. -tipp: Mit \`String[] namen = new String[5]\` legst du das Array an. Achtung: Die Losnummer beginnt bei 1, der Index bei 0. Der Name steht also in \`namen[nummer - 1]\`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-tafeldienst-java -titel: Tafeldienst auslosen in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Tafeldienst: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Tafeldienst: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Tafeldienst: Pia -braucht: forSchleife -tipp: Ein Array für fünf Namen legst du mit \`String[] namen = new String[5];\` an. -tipp: In der Schleife: \`namen[i] = scanner.next();\`. Die Losnummer 1 gehört zum Fach 0, darum gibst du \`namen[nummer - 1]\` aus. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> Mit \`int nummer = (int) (Math.random() * 5) + 1\` kannst du die Losnummer auch vom Zufall ziehen lassen. Für die automatische Prüfung brauchen wir aber eine feste Eingabe. -`,k9=`--- -titel: Taschenrechner -nach: 10 -stufe: 2 ---- - -Ein kleiner Taschenrechner: zwei Zahlen und ein Rechenzeichen eingeben, das Ergebnis erscheint. - -::: übung aufgabe-rechner -titel: Taschenrechner -auftrag: Frage nach zwei ganzen Zahlen und danach nach einem Rechenzeichen als Text: \`+\`, \`-\` oder \`*\`. Gib das Ergebnis aus. Bei einem anderen Zeichen gib **Unbekanntes Rechenzeichen** aus. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Lies alle drei Werte ein. Eine Fallauswahl über das Rechenzeichen entscheidet dann, was gerechnet wird. -tipp: Das Rechenzeichen liest du mit \`input: String zeichen "Rechenzeichen"\` ein. Die Fallauswahl prüft \`zeichen\`, die Fälle heißen \`"+"\`, \`"-"\` und \`"*"\`, der letzte Fall ist \`default\`. Rechne in jedem Fall erst in einer Variablen und gib sie dann aus, sonst hängt \`+\` die Zahlen nur aneinander. -lösung: aufgabe-rechner-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-rechner-java -titel: Taschenrechner in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unbekanntes Rechenzeichen -braucht: fallauswahl -tipp: Das Rechenzeichen ist ein Text. Du liest es mit \`String zeichen = scanner.next();\` ein. -tipp: \`switch (zeichen) { case "+" -> { … } … default -> { … } }\`. Bei Texten stehen die Fälle in Anführungszeichen. -lösung: aufgabe-rechner-loesung.visustruct -::: -`,S9=`--- -titel: Verleih-Menü -nach: 18 -stufe: 2 ---- - -Am Verleih-Automaten kannst du nacheinander mehrere Fahrzeuge buchen. Das Menü erscheint immer wieder, bis du **0** für Ende wählst. - -::: übung aufgabe-verleih -titel: Verleih-Menü -auftrag: Frage immer wieder nach einer Auswahl: 1 = E-Bike, 2 = E-Scooter, 0 = Ende. Gib bei 1 **E-Bike gebucht** und bei 2 **E-Scooter gebucht** aus, bei jeder anderen Zahl **Ungültige Auswahl**. - Bei 0 gibt das Programm **Tschüss** aus und verlässt die Schleife mit einem Aussprung. -test: 0 -> Tschüss -test: 1, 2, 0 -> E-Bike gebucht | E-Scooter gebucht | Tschüss -test: 2, 5, 1, 0 -> E-Scooter gebucht | Ungültige Auswahl | E-Bike gebucht | Tschüss -braucht: endlosschleife, aussprung -tipp: Das Menü läuft immer wieder, bis jemand 0 wählt. Dafür eignet sich eine Endlosschleife mit einem Aussprung. -tipp: Nimm eine Endlosschleife. Darin steht zuerst die Eingabe \`input: int wahl "1 = E-Bike, 2 = E-Scooter, 0 = Ende"\`, danach eine Fallauswahl über \`wahl\` mit den Fällen \`0\`, \`1\`, \`2\` und \`default\`. Im Fall \`0\` stehen die Ausgabe und darunter der Aussprung. -lösung: aufgabe-verleih-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-verleih-java -titel: Verleih-Menü in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 0 -> Tschüss -test: 1, 2, 0 -> E-Bike gebucht | E-Scooter gebucht | Tschüss -test: 2, 5, 1, 0 -> E-Scooter gebucht | Ungültige Auswahl | E-Bike gebucht | Tschüss -braucht: endlosschleife, aussprung -tipp: \`while (true) { … }\` läuft endlos, bis ein \`break;\` kommt. -tipp: Nimm in Java hier \`if\`/\`else if\` statt \`switch\`: Ein \`break;\` in einem \`switch\` verlässt nur den \`switch\`, nicht die Schleife. \`if (wahl == 0) { System.out.println("Tschüss"); break; } else if (wahl == 1) { … }\`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: -`,T9=`--- -titel: Vokabeltrainer -nach: 27 -stufe: 3 ---- - -Ludwig lernt Englisch. Seine Vokabeln stehen in einer CSV-Datei, links Deutsch, rechts Englisch: - -::: datei vokabeln.csv -Deutsch;Englisch -Ziege;goat -Heu;hay -Wiese;meadow -::: - -Weil nur die Datei die Wörter kennt, kannst du später einfach neue Zeilen anhängen, ohne das Programm zu ändern. - -::: angeberwissen -Die Lernkartei mit mehreren Fächern hat Sebastian Leitner 1972 bekannt gemacht: Gewusste Karten wandern ein Fach weiter und kommen seltener dran, falsche wandern zurück ins erste Fach. Viele Vokabel-Apps arbeiten bis heute nach diesem Prinzip. -::: - -::: übung aufgabe-vokabeln -titel: Vokabeltrainer -code: java -auftrag: Frage jede Vokabel aus der Datei ab: Gib **Was heißt Ziege?** aus und lies die Antwort mit \`scanner.next()\` ein. Ist sie richtig, gib **Richtig!** aus, sonst zum Beispiel **Falsch, richtig ist goat.** - Am Ende steht da, wie viele richtig waren, zum Beispiel **2 von 3 richtig**. -start: aufgabe-vokabeln-start.visustruct -test: goat, hay, meadow -> Was heißt Ziege? | Richtig! | Was heißt Heu? | Richtig! | Was heißt Wiese? | Richtig! | 3 von 3 richtig -test: cow, hay, meadow -> Was heißt Ziege? | Falsch, richtig ist goat. | Was heißt Heu? | Richtig! | Was heißt Wiese? | Richtig! | 2 von 3 richtig -test: goat, heu, wiese -> Was heißt Ziege? | Richtig! | Was heißt Heu? | Falsch, richtig ist hay. | Was heißt Wiese? | Falsch, richtig ist meadow. | 1 von 3 richtig -braucht: whileSchleife, verzweigung -tipp: Überspringe die Kopfzeile. In der Schleife zerlegst du jede Zeile mit \`split(";")\`: \`teile[0]\` ist das deutsche Wort, \`teile[1]\` das englische. -tipp: Texte vergleichst du mit \`antwort.equals(teile[1])\`, nicht mit \`==\`. Zähle vor der Schleife mit \`int richtig = 0;\` und \`int anzahl = 0;\` mit. -lösung: aufgabe-vokabeln-loesung.visustruct -::: -`,_9=`--- -titel: Volljährig? -nach: 8 -stufe: 1 ---- - -Im Kino, beim Führerschein oder bei der Wahl: Oft kommt es darauf an, ob jemand schon 18 ist. - -::: übung aufgabe-volljaehrig -titel: Volljährig? -auftrag: Frage nach dem Alter. Ist die Person 18 oder älter, gib **volljährig** aus, sonst **minderjährig**. -test: 18 -> volljährig -test: 35 -> volljährig -test: 17 -> minderjährig -test: 6 -> minderjährig -braucht: verzweigung -tipp: Es gibt zwei mögliche Ausgaben, also brauchst du eine Verzweigung. -tipp: Lies das Alter mit \`input: int alter "Wie alt bist du?"\` ein. Die Bedingung der Verzweigung lautet \`alter >= 18\`. Achte darauf, dass 18 schon volljährig ist. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -Und jetzt dasselbe in Java: - -::: übung aufgabe-volljaehrig-java -titel: Volljährig? in Java -code: java -auftrag: Löse die Aufgabe von oben jetzt in Java. Die Ausgaben bleiben genau dieselben. -test: 18 -> volljährig -test: 35 -> volljährig -test: 17 -> minderjährig -test: 6 -> minderjährig -braucht: verzweigung -tipp: Lies das Alter mit \`int alter = scanner.nextInt();\` ein. -tipp: \`if (alter >= 18) { … } else { … }\`, in jedem Zweig ein \`System.out.println(…)\`. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: -`,A9=`--- -titel: Würfeln mit MVC -nach: 40 -stufe: 3 ---- - -Baue ein Würfelspiel nach Model View Controller. Ein Knopf **Würfeln** würfelt, und zwei Views zeigen das Ergebnis. - -Das Model ist schon fertig. Es würfelt und zählt mit, wie oft gewürfelt wurde und wie viele Sechsen dabei waren: - -\`\`\`java -class Wuerfel { - int augen = 1; - int wuerfe = 0; - int sechsen = 0; - - void wuerfeln() { - augen = (int) (Math.random() * 6) + 1; - wuerfe++; - if (augen == 6) { - sechsen++; - } - } -} -\`\`\` - -Deine Aufgabe: - -1. Teste das Model zuerst ohne Fenster: Würfle in \`main\` zehnmal und gib danach \`wuerfe\` und \`sechsen\` aus. -2. Schreibe die View \`AugenAnzeige\`. Sie zeigt in einem großen JLabel die gewürfelte Zahl, zum Beispiel „4“. -3. Schreibe die View \`StatistikAnzeige\`. Sie zeigt zum Beispiel „Sechsen: 2 von 10 Würfen“. -4. Der Controller ist der Zuhörer am Knopf: Er lässt das Model würfeln und gibt beiden Views die neuen Werte. - -**Du bist fertig, wenn …** - -- in der Konsole nach zehn Würfen \`wuerfe\` gleich 10 ist und \`sechsen\` zwischen 0 und 10 liegt, -- jeder Klick auf **Würfeln** eine neue Zahl von 1 bis 6 in der \`AugenAnzeige\` zeigt, -- die \`StatistikAnzeige\` nach jedem Klick einen Wurf mehr zählt und Sechsen richtig mitzählt, -- im Model kein \`JLabel\` und in den Views kein \`Math.random\` steht. - -Zusatz: Ein Knopf **Neues Spiel** setzt alles auf Anfang. Die Methode dafür gehört ins Model. - -::: ludwig -Denk an die Regel aus Lektion 40: Im Model steht kein einziges \`JLabel\`. Wenn du beim Würfeln die Schriftgröße änderst, gehört das in die View. -::: -`,j9=`--- -titel: Zahl verdoppeln -nach: 2 -stufe: 1 ---- - -Eine Variable kann ihren Wert ändern. Hier wird eine Zahl verdoppelt. - -::: übung aufgabe-verdoppeln -titel: Zahl verdoppeln -auftrag: Lege die Variable \`zahl\` mit dem Wert 4 fest. Verdopple sie in einer zweiten Anweisung, ohne eine neue Variable anzulegen. Gib danach **Verdoppelte Zahl: 8** aus, wobei die 8 aus der Variablen kommt. -test: - -> Verdoppelte Zahl: 8 -tipp: Verdoppeln heißt: mal 2. Das Ergebnis speicherst du wieder in derselben Variablen. -tipp: \`zahl = zahl * 2\` rechnet mit dem alten Wert und speichert das Ergebnis wieder in \`zahl\`. Text und Variable verbindest du mit \`+\`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: -`,C9=`--- -titel: Age in a subprogram -nach: 18 -stufe: 2 ---- - -The age check from “Adult?” now becomes a subprogram of its own. The main program only calls it. - -::: übung aufgabe-alter-unterprogramm -titel: Age in a subprogram -auftrag: Build two diagrams. The main program outputs **Welcome**, then calls \`checkAge()\` and finally outputs **Goodbye**. - The subprogram \`checkAge\` is in a tab of its own: it asks for the age and outputs **adult** or **minor**. -test: 20 -> Welcome | adult | Goodbye -test: 18 -> Welcome | adult | Goodbye -test: 12 -> Welcome | minor | Goodbye -braucht: aufruf -tipp: The main program has three boxes. The subprogram does the real work. -tipp: Open the exercise in the editor and build the main program there with the call \`checkAge()\`. Then open a new tab with **+** and give it the title \`checkAge\`. Build the input and the branch in it. Check finds the subprogram by the title of the tab. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: -`,D9=`--- -titel: Ball throw results -nach: 25 -stufe: 2 ---- - -At sports day everyone wrote their ball throw distance into a table: - -::: datei throw.csv -Name;Distance -Ludwig;28 -Zora;42 -Fox;35 -Hare;21 -::: - -::: übung aufgabe-ballwurf -titel: Ball throw results -code: java -auftrag: Read the file and skip the header line. Output how many took part (**Participants: 4**) and how far they threw on average (**Average: 31.5 m**). - Save the longest throw in the file **winner.txt**, for example **Winner: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Participants: 4 | Average: 31.5 m -datei: winner.txt -> Winner: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: You need four variables before the loop: the count, the sum as a \`double\`, the best distance and the best name. -tipp: In the loop: \`String[] parts = line.split(";");\` and \`int distance = Integer.parseInt(parts[1]);\`. If \`distance > bestDistance\`, remember the distance and the name. To save: \`PrintWriter out = new PrintWriter("winner.txt");\`, \`out.println(…)\` and \`out.close();\`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: -`,B9=`--- -titel: Leaderboard -nach: 19 -stufe: 2 ---- - -Five goats have run the obstacle race. Now the leaderboard goes on the stable door: most points at the top, fewest at the bottom. - -::: übung aufgabe-bestenliste -titel: Leaderboard -auftrag: Read 5 scores into an array. Sort the array with bubble sort in **descending** order, so the biggest number comes first. Then output the list with places, for example **1. 9**, on the next line **2. 9** and so on. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Bubble sort from lesson 19 sorts in ascending order. For descending order you only turn round the comparison of the neighbours. -tipp: Swap when \`points[i] < points[i + 1]\`, using a temporary variable. For the output, first work out \`int place = i + 1\` and then output \`place + ". " + points[i]\`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-bestenliste-java -titel: Leaderboard in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Read each number into a variable first and then put it into the array: \`int number = scanner.nextInt();\` and \`points[i] = number;\`. -tipp: The swap needs a temporary variable in Java too: \`int temp = points[i]; points[i] = points[i + 1]; points[i + 1] = temp;\`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: -`,P9=`--- -titel: Binary number -nach: 11 -stufe: 3 ---- - -Computers calculate with only two digits: 0 and 1. This is how you convert a number into the **binary system**: - -1. Divide the number by 2 and remember the remainder (0 or 1). -2. Carry on with the result without the remainder. -3. Repeat this until the number is 0. - -The remainders read **from bottom to top** give the binary number. For 6 the remainders are 0, 1, 1, so the binary number is 110. - -::: übung aufgabe-binaer -titel: Binary number -auftrag: Ask for a whole number greater than 0 and output it as a binary number. For 6 the output is **110**, for 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Keep dividing the number by 2 and remember every remainder. The remainders read backwards give the binary number. -tipp: Collect the digits in a text: \`String binary = ""\`. In the loop you calculate \`int remainder = number % 2\` and add the remainder at the **front**: \`binary = remainder + binary\`. After that, \`number = number / 2\`. Because number is a whole number, the remainder is dropped. -lösung: aufgabe-binaer-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-binaer-java -titel: Binary number in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Start with \`String binary = "";\`. The loop runs with \`while (number > 0)\`. -tipp: In the body: \`int remainder = number % 2;\`, then \`binary = remainder + binary;\` and \`number = number / 2;\`. Because \`binary\` is a String, \`+\` puts the remainder at the front. -lösung: aufgabe-binaer-loesung.visustruct -::: -`,O9=`--- -titel: Caesar cipher -nach: 17 -stufe: 3 ---- - -More than 2000 years ago, the Roman general Julius Caesar sent secret messages to his troops. His trick was simple: he replaced every letter with the letter **three places further on** in the alphabet. A becomes D, B becomes E and so on. The readable message is called the **plain text**, the encrypted one the **secret text**. The number of places you shift by is the **key**, often just called \`k\`. - -::: grafik caesar-verschiebung.svg -Encryption with the key 3: each letter at the top becomes the letter below it. At the end you start again from the beginning, so X, Y and Z become A, B and C. -::: - -This turns \`HALLO\` into \`KDOOR\`. Anyone who knows the key simply shifts three places back and can read the message again. - -### The cipher wheel - -To avoid counting every time, people built a **cipher wheel**: two discs with the alphabet, a small one on top of a large one, fixed in the middle so it can turn. The outer ring shows the plain text, the inner ring the secret text. Turn the inner disc by three letters, and below every outer letter you find its secret letter. Wheels like this have existed since the 15th century. In the American Civil War (1861–1865), soldiers carried small brass wheels. - -::: grafik caesar-scheibe.svg -A cipher wheel modelled on historical brass wheels. The blue window shows: with the key 3, D is below A. -::: - -::: angeberwissen -Caesar himself didn’t even have a wheel! We know this from the Roman historian Suetonius: in his letters, Caesar shifted every letter by three places and simply counted it out in his head. The wheel was only invented by Leon Battista Alberti around 1467, about 1500 years later. By the way, I count in my head too. I don’t have fingers, only hooves. -::: - -Try it yourself. Turn the inner wheel, type a text and watch the secret text appear. **Decrypt** goes the other way. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -You can easily make a real wheel yourself: two circles of cardboard, one a bit smaller, each with 26 equal fields for the alphabet, and a paper fastener in the middle. Your secret agent tool is ready! -::: - -::: quiz -? What does the letter Y become with the key 3? -- [ ] A -- [x] B -- [ ] C -> After Y comes Z, then you start again with A and B. Three places further on is therefore B. - -? A secret text was encrypted with the key 5. How do you decrypt it? -- [ ] Shift it 5 places further on again -- [x] Shift it 5 places back -- [ ] Read the letters backwards -> Decrypting is the opposite of encrypting. By the way, 5 places back is the same as 21 places forward, because 5 + 21 = 26. -::: - -### The program: there is more than one way - -How do you teach the computer to shift a letter by \`k\` places? There is no *single* right solution. We look at two ways. Both go through the text character by character with a for loop, just like in the lesson on strings. The difference is how they shift a single letter. - -In both ways the remainder operator \`%\` helps. It makes sure that after Z you continue with A: positions 0 to 25 stay as they are, 26 becomes 0, 27 becomes 1 and so on. - -### Way 1: the alphabet in an array - -You put the alphabet into an array \`char[] alphabet = {'A', 'B', …, 'Z'}\`. For every letter of the word you search the array for its position \`pos\`, that is its index. The secret letter is then in the same array at position \`(pos + k) % 26\`. - -An example with the key 3: X is at position 23. Then \`(23 + 3) % 26 = 0\`, and position 0 holds A. - -::: übung aufgabe-caesar-array -titel: Caesar with an array -auftrag: The start diagram already contains the array \`alphabet\`. Ask for a word in capital letters and for the key \`k\`. Then output the encrypted word. For HALLO and 3 that is \`KDOOR\`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Create an empty text \`String secret = ""\`. Go through the word character by character with a for loop. In every pass you append one secret letter to \`secret\`. -tipp: To find the position of a letter you need a second, inner loop over the array: if \`alphabet[j] == letter\`, remember \`pos = j\`. -tipp: After the inner loop, append \`alphabet[(pos + k) % 26]\`: \`secret = secret + alphabet[(pos + k) % 26]\`. After the outer loop, output \`secret\`. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Way 2: calculating with the ASCII table - -For the computer, every character is really a number. Which number belongs to which character is listed in the **ASCII table**. There A has the code 65, B the code 66 and so on up to Z with 90. In Java you turn a character into its code with \`(int)\` and a code back into a character with \`(char)\`: - -\`\`\` -int code = (int) 'A' -char letter = (char) 68 -\`\`\` - -After that, \`code\` is 65 and \`letter\` is \`'D'\`. Changing a value from one data type into another like this is called a **type cast**. - -Now you can calculate with the letter. So that the jump from Z back to A works, first subtract 65. Then A has position 0 and Z position 25, exactly as in way 1. Shift with \`% 26\` and add the 65 back at the end: - -::: grafik caesar-ascii.svg -At the top the capital letters with their ASCII codes. Below, the calculation for X with the key 3: the result is A. -::: - -::: übung aufgabe-caesar-ascii -titel: Caesar with ASCII -code: java -auftrag: Write a Java program that reads a word in capital letters and the key \`k\` and outputs the encrypted word. This time without an array: calculate with \`(int)\` and \`(char)\`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Go through the word with a for loop. You get the code of the character at position \`i\` with \`int code = (int) word.charAt(i);\`. -tipp: Calculate \`code = (code - 65 + k) % 26 + 65;\` and append the character: \`secret = secret + (char) code;\`. Before the loop write \`String secret = "";\`, after the loop \`System.out.println(secret);\`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Which way is better? - -**Way 1: array.** The idea is to search for the position in the array. -- Advantage: easy to understand, and your own alphabets work too, e.g. with Ä, Ö and Ü. -- Disadvantage: every letter has to be searched for. - -**Way 2: ASCII.** The idea is to calculate with the character code. -- Advantage: short and without searching, therefore faster. -- Disadvantage: you need to know the ASCII codes. - -Both programs give the same result. That is often how programming works: there are several correct solutions, and you choose the one that fits your task best. - -::: übung aufgabe-caesar-entschluesseln -titel: Decrypt Caesar -code: java -auftrag: Now turn the tables. Read a secret text in capital letters and the key \`k\` and output the plain text. For KDOOR and 3 that is \`HALLO\`. You decide which way to use. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Decrypting means shifting \`k\` places back instead of forward. So \`+ k\` becomes \`- k\`. -tipp: Careful with A: \`0 - 3\` gives -3, and in Java \`%\` does not turn that into a positive number. So add another 26: \`code = (code - 65 - k + 26) % 26 + 65;\` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Unfortunately, Caesar is not secure. There are only 25 keys, and you can try them all in a few minutes. It is even quicker with a trick: in English, E is the most common letter. If H is the most common letter in the secret text, the key was probably 3. On the internet there is also ROT13, which is Caesar with the key 13. Applying ROT13 twice gives you the plain text again, because 13 + 13 = 26. -::: -`,M9=`--- -titel: Collatz sequence -nach: 11 -stufe: 2 ---- - -The Collatz sequence is a famous puzzle in mathematics. You start with a number and apply the same rule again and again: - -- If the number is **even**, you divide it by 2. -- If it is **odd**, you multiply it by 3 and add 1. - -So far, for every start number anyone has tried, the sequence ends at 1 at some point. Whether this is true for all numbers, nobody knows to this day. - -::: übung aufgabe-collatz -titel: Collatz sequence -auftrag: Ask for a start number. Apply the rule until the number is 1, and output the new number after every step. Count the steps as you go and output **Steps:** and the count at the end. - With the start number 6 the sequence is 3, 10, 5, 16, 8, 4, 2, 1. That is 8 steps. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 7 -test: 1 -> Steps: 0 -braucht: whileSchleife, verzweigung -tipp: You do not know how many steps there will be. That is why a while loop fits better than a for loop. -tipp: The while loop runs as long as \`number != 1\`. You check whether a number is even with \`number % 2 == 0\`. The output and \`steps++\` come after the branch, but still inside the loop. -lösung: aufgabe-collatz-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-collatz-java -titel: Collatz sequence in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Steps: 7 -test: 1 -> Steps: 0 -braucht: whileSchleife, verzweigung -tipp: A \`while\` loop runs as long as \`number != 1\`. Inside it, an \`if\` with \`number % 2 == 0\` decides. -tipp: In the body: halve it or \`number = number * 3 + 1;\`, then \`System.out.println(number);\` and \`steps++;\`. After the loop comes \`"Steps: " + steps\`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -This task could make you rich! The mathematician Paul Erdős offered 500 dollars for a proof. In 2021 the Japanese company Bakuage added quite a bit more: 120 million yen, about a million dollars at the time, for a proof or a counterexample. Nobody has collected the money yet. By the way, the puzzle was invented by the German mathematician Lothar Collatz in 1937. I am going to start calculating right now; that much money buys a lot of hay. -::: -`,Z9=`--- -titel: Unit converter -nach: 35 -stufe: 2 ---- - -Build a window that converts lengths. At the top you type a length in metres, below it there are three buttons: - -- **in cm** shows the length in centimetres, 1.5 becomes "150.0 cm", -- **in mm** shows it in millimetres, 1.5 becomes "1500.0 mm", -- **in km** shows it in kilometres, 1500 becomes "1.5 km". - -The result is shown in a JLabel below the buttons. If the field doesn't contain a number, it says "Please enter a number", and the program doesn't crash. - -You can start like this: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Units { - public static void main(String[] args) { - JFrame window = new JFrame("Unit converter"); - window.setSize(300, 200); - window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - window.setLayout(new GridLayout(0, 1)); - - JTextField metres = new JTextField(); - JButton inCm = new JButton("in cm"); - JLabel result = new JLabel("?", SwingConstants.CENTER); - - inCm.addActionListener(e -> { - double m = Double.parseDouble(metres.getText()); - result.setText((m * 100) + " cm"); - }); - - window.add(metres); - window.add(inCm); - window.add(result); - window.setVisible(true); - } -} -\`\`\` - -**You are done when …** - -- 1.5 shows "150.0 cm" after **in cm** and "1500.0 mm" after **in mm**, -- 1500 shows "1.5 km" after **in km**, -- "abc" in the field shows "Please enter a number" and the window stays open. - -Extra: a fourth button **in feet** converts to the imperial unit. One foot is 0.3048 m. - -::: ludwig -Tick **Real Java (with Swing)** in the [gear](#zeige-einstellungen). For "abc" in the field, every listener needs a try/catch with \`NumberFormatException\`, just like the calculator. -::: -`,L9=`--- -titel: Fibonacci numbers -nach: 13 -stufe: 2 ---- - -With the Fibonacci numbers, every number is the sum of the two numbers before it. The sequence starts with 1 and 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -You find these numbers surprisingly often in nature, for example in the way sunflower seeds are arranged. - -::: ludwig -Do you remember **swapping** from lesson 5? There you needed a helper variable, because \`a = b\` overwrites the old value of \`a\`. It is the same here: before \`number2\` gets the new number, its old value must be saved in \`number1\`. So watch the **order**: first \`number1 = number2\`, then \`number2 = number3\`. You will need this moving on and swapping again later, for example when sorting in lesson 19. -::: - -::: angeberwissen -In 1202 Leonardo of Pisa, known as Fibonacci, set a puzzle about rabbits in his arithmetic book: from its second month on, every pair of rabbits has a new pair every month. How many pairs are there after a year? The answer is this sequence. Sadly, there were no goats in the book. -::: - -::: übung aufgabe-fibonacci -titel: Fibonacci numbers -auftrag: Ask how many Fibonacci numbers should be output. Then output exactly that many, each on its own line. You can assume that at least 2 is typed in. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Every new number is the sum of the two before it. So you always have to remember the last two numbers. -tipp: Keep the last two numbers in \`number1\` and \`number2\` and output both before the loop. The loop starts at \`int i = 2\`. Inside it you calculate \`int number3 = number1 + number2\`, output it and then move on: \`number1 = number2\` and \`number2 = number3\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-fibonacci-java -titel: Fibonacci numbers in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Output the first two numbers before the loop. The \`for\` loop then starts at \`int i = 2\`. -tipp: In the body: \`int number3 = number1 + number2;\`, output it, then \`number1 = number2;\` and \`number2 = number3;\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Why must \`number1 = number2\` come before \`number2 = number3\` in the loop? -- [ ] Because otherwise Java does not allow the assignments -- [x] Because otherwise the old value of number2 is overwritten before number1 gets it -- [ ] The order does not matter -> The other way round, number1 and number2 would have the same value afterwards. Just like swapping: first save, then overwrite. -::: -`,W9=`--- -titel: Even or odd -nach: 8 -stufe: 1 ---- - -A number is even if there is no remainder when you divide it by 2. You calculate the remainder with \`%\`: \`8 % 2\` is 0, \`7 % 2\` is 1. - -::: übung aufgabe-gerade -titel: Even or odd -auftrag: Ask for a whole number. Output whether it is even or odd. For 4 the output is **4 is even**, for 7 **7 is odd**. -test: 4 -> 4 is even -test: 7 -> 7 is odd -test: 0 -> 0 is even -test: 15 -> 15 is odd -braucht: verzweigung -tipp: A branch decides between even and odd. Even means: can be divided by 2 without a remainder. -tipp: The condition is \`number % 2 == 0\`. In the output you join the number and the text with \`+\`, e.g. \`number + " is even"\`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-gerade-java -titel: Even or odd in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 4 -> 4 is even -test: 7 -> 7 is odd -test: 0 -> 0 is even -test: 15 -> 15 is odd -braucht: verzweigung -tipp: Read a number with \`int number = scanner.nextInt();\`. Ask with \`System.out.println(…)\` before that. -tipp: \`if (number % 2 == 0) { … } else { … }\`. The output is \`System.out.println(number + " is even");\`. -lösung: aufgabe-gerade-loesung.visustruct -::: -`,E9=`--- -titel: Sales tax -nach: 6 -stufe: 1 ---- - -In Germany, 19 % **VAT** (value added tax, a kind of sales tax) is added to almost everything you buy. The shop calculates with the net price (without tax) and adds the tax on top. That gives the gross price you pay at the till. - -An example: you buy a folding phone that opens up into a tablet. The shop's price tag says 1000 euros net. On top of that come 19 %, so 190 euros. At the till you pay 1190 euros. The shop is not allowed to keep the 190 euros: it passes them on to the tax office. - -::: grafik aufgabe-mehrwertsteuer.svg -Net price plus VAT gives the gross price. The VAT goes to the tax office. -::: - -::: angeberwissen -Baah, the shop bought the phone itself, of course: from the manufacturer for 800 euros plus 152 euros VAT. These 152 euros are called **input tax**, and the shop may subtract them from the 190 euros. So it only transfers 38 euros to the tax office. In the end only one person pays the whole tax: the customer at the till. That is you. I pay no tax at all, by the way. I just eat grass. -::: - -The tax rate never changes while you calculate. That is why you create it as a **constant**: a variable whose value stays the same once it is set. You write \`const\` in front of it, for example \`const double VAT = 0.19\`. Names of constants are written in capital letters so you recognise them straight away. In Java code this becomes \`final double VAT = 0.19;\`. - -If the tax rate ever changes, you only have to change this one line. - -::: übung aufgabe-mehrwertsteuer -titel: Sales tax -auftrag: Create the constant \`VAT\` with the value 0.19. Then ask for the net price. First output the tax and then the gross price, only the number each time. - For a net price of 100 the outputs are **19** and **119**. -test: 100 -> 19 | 119 -test: 50 -> 9.5 | 59.5 -test: 0 -> 0 | 0 -tipp: The tax is 19 percent of the net price. The gross price is what you pay in the end. -tipp: The tax is \`netPrice * VAT\`. You get the gross price by adding the tax to the net price. Both values are decimal numbers, so \`double\`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Why do you create the tax rate as a constant? -- [ ] Because constants are calculated faster -- [x] Because its value should not change in the program -- [ ] Because decimal numbers can only be stored as constants -> A constant keeps its value. If the tax rate ever changes, you only change the one line with \`const\`. -::: -`,N9=`--- -titel: Grade average -nach: 13 -stufe: 2 ---- - -How did you do lately? The average is the sum of all grades divided by how many there are. - -::: übung aufgabe-durchschnitt -titel: Grade average -auftrag: First ask how many grades will be typed in. Then read in exactly that many grades and output the average, only the number. - For the grades 1 and 2 the average is **1.5**. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: You need the sum of all grades and how many there are. The average is the sum divided by the count. -tipp: The for loop runs from \`int i = 0\` while \`i < count\`. Inside it you read in a grade and add it to the sum. You store the average as a decimal number: \`double average = sum * 1.0 / count\`. The \`* 1.0\` makes sure that Java does not do whole-number division either. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-durchschnitt-java -titel: Grade average in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 2, 1, 2 -> 1.5 -test: 3, 1, 2, 3 -> 2 -test: 4, 2, 2, 3, 4 -> 2.75 -braucht: forSchleife -tipp: \`for (int i = 0; i < count; i++)\` reads exactly as many grades as were given before. -tipp: Divide with \`double average = sum * 1.0 / count;\`. Without \`* 1.0\`, Java divides two \`int\` values as whole numbers, and 1.5 becomes 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: -`,F9=`--- -titel: Check the PIN -nach: 12 -stufe: 2 ---- - -At a cash machine you have three tries for your PIN. If you type a wrong PIN three times, the card is blocked. - -::: angeberwissen -John Shepherd-Barron, who set up the first cash machine in London in 1967, actually wanted six-digit PINs. But his wife said she could remember four digits at most. That is why your PIN has four digits today. -::: - -::: übung aufgabe-pin -titel: Check the PIN -auftrag: The right PIN is 1234. Keep asking for the PIN until it is right or three tries have been used. Then output **PIN correct** or **Card blocked**. -test: 1234 -> PIN correct -test: 1111, 1234 -> PIN correct -test: 1, 2, 1234 -> PIN correct -test: 1, 2, 3 -> Card blocked -braucht: doUntilSchleife, verzweigung -tipp: The loop ends in two cases: when the PIN is right or when three tries have been used. Count the tries. -tipp: The PIN is asked for at least once, so a do-while loop fits. Create \`int pin = 0\` and \`int attempts = 0\` before it. The loop repeats as long as \`pin != 1234 && attempts < 3\`. After the loop, a branch decides what is output. -lösung: aufgabe-pin-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-pin-java -titel: Check the PIN in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 1234 -> PIN correct -test: 1111, 1234 -> PIN correct -test: 1, 2, 1234 -> PIN correct -test: 1, 2, 3 -> Card blocked -braucht: doUntilSchleife, verzweigung -tipp: \`do { … } while (…);\` asks at least once. There is a semicolon after \`while (…)\`. -tipp: Create \`int pin = 0;\` and \`int attempts = 0;\` first. In the loop: \`int entered = scanner.nextInt();\`, \`pin = entered;\` and \`attempts++;\`. The condition is \`pin != 1234 && attempts < 3\`. -lösung: aufgabe-pin-java-loesung.visustruct -::: -`,R9=`--- -titel: Battleship -nach: 16 -stufe: 2 ---- - -In the game Battleship the sea is a grid. The columns are called A to E, the rows 1 to 5. In the start diagram the sea is the 2D array \`sea\`: \`true\` means that part of a ship is on this square. - -::: grafik schiffe-versenken.svg -Grey means \`true\`: there is a ship here. The small grey numbers are the indices in the array; they start at 0. In the game, on the other hand, the squares are called A to E and 1 to 5. -::: - -::: übung aufgabe-schiffe -titel: Battleship -auftrag: Output all squares that have a ship on them, each square on its own line, e.g. **B1**. Go through the sea row by row with two nested loops. - For the start diagram, **B1**, **C1**, **D1**, **E3**, **E4** and **A5** appear. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Go through the sea with two nested loops, just like the cinema. Only output the squares that hold \`true\`. -tipp: The outer loop counts \`row\`, the inner one \`column\`, both from 0. If there is a ship on \`sea[row][column]\`, you output the letter from \`columns[column]\` and the row number: \`output: columns[column] + (row + 1)\`. You need the \`+ 1\` because the game starts counting at 1. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-schiffe-java -titel: Battleship in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Two \`for\` loops inside each other: the row outside, the column inside, both up to \`.length\`. -tipp: \`if (sea[row][column]) { System.out.println(columns[column] + (row + 1)); }\`. The brackets around \`row + 1\` make Java calculate first. -lösung: aufgabe-schiffe-loesung.visustruct -::: -`,J9=`--- -titel: Menu -nach: 10 -stufe: 1 ---- - -In the school canteen you order by number. The program tells you which dish belongs to the number. - -::: übung aufgabe-speisekarte -titel: Menu -auftrag: Ask for a number. For 1 output **Greek salad**, for 2 **Rice bowl** and for 3 **Falafel wrap**. For any other number output **Please choose a valid number**. -test: 1 -> Greek salad -test: 2 -> Rice bowl -test: 3 -> Falafel wrap -test: 7 -> Please choose a valid number -braucht: fallauswahl -tipp: A switch fits several fixed numbers. What happens with all the other numbers? -tipp: Read in the number with \`input: int pick "Which dish?"\`. The switch checks \`pick\`, and the cases are called \`1\`, \`2\` and \`3\`. All other numbers end up in the case \`default\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-speisekarte-java -titel: Menu in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 1 -> Greek salad -test: 2 -> Rice bowl -test: 3 -> Falafel wrap -test: 7 -> Please choose a valid number -braucht: fallauswahl -tipp: In Java this is a switch: \`switch (pick) { … }\`. -tipp: Every case looks like this: \`case 1 -> { System.out.println("…"); }\`. All other numbers go to \`default -> { … }\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: -`,G9=`--- -titel: Add up -nach: 2 -stufe: 1 ---- - -Set two numbers, add them and output the result. - -::: übung aufgabe-summe -titel: Add up -auftrag: Set \`number1\` to the value 4 and \`number2\` to the value 3. Calculate the sum in a third variable and output only the sum. -test: - -> 7 -tipp: You need three variables: two for the numbers and one for the result. -tipp: You set a variable with a statement such as \`int number1 = 4\`. For the output you write the name of the variable without quotation marks. -lösung: aufgabe-summe-loesung.visustruct -::: -`,H9=`--- -titel: Draw for board duty -nach: 15 -stufe: 2 ---- - -Who is on board duty this week, cleaning the board? Five names go into an array, then a number is drawn. - -::: übung aufgabe-tafeldienst -titel: Draw for board duty -auftrag: Read five names into a String array. Then ask for a draw number from 1 to 5 and output **Board duty:** and the matching name. - Draw number 1 belongs to the first name, 5 to the last one. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Board duty: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Board duty: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Board duty: Pia -braucht: forSchleife -tipp: First a loop reads in the five names. After that you do not need a loop any more, just one look into a slot. -tipp: You create the array with \`String[] names = new String[5]\`. Careful: the draw number starts at 1, the index at 0. So the name is in \`names[pick - 1]\`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-tafeldienst-java -titel: Draw for board duty in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: Ada, Ben, Cem, Dana, Emil, 3 -> Board duty: Cem -test: Ada, Ben, Cem, Dana, Emil, 1 -> Board duty: Ada -test: Lea, Max, Nia, Ole, Pia, 5 -> Board duty: Pia -braucht: forSchleife -tipp: Create an array for five names with \`String[] names = new String[5];\`. -tipp: In the loop: \`names[i] = scanner.next();\`. Ticket number 1 belongs to slot 0, so you output \`names[pick - 1]\`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> With \`int pick = (int) (Math.random() * 5) + 1\` you can also let chance draw the number. But for the automatic check we need a fixed input. -`,V9=`--- -titel: Calculator -nach: 10 -stufe: 2 ---- - -A small calculator: type in two numbers and an operator, and the result appears. - -::: übung aufgabe-rechner -titel: Calculator -auftrag: Ask for two whole numbers and then for an operator as text: \`+\`, \`-\` or \`*\`. Output the result. For any other sign output **Unknown operator**. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unknown operator -braucht: fallauswahl -tipp: Read in all three values. A switch on the operator then decides what is calculated. -tipp: You read in the operator with \`input: String operator "Operator"\`. The switch checks \`operator\`, the cases are called \`"+"\`, \`"-"\` and \`"*"\`, and the last case is \`default\`. In every case, first calculate in a variable and then output it, otherwise \`+\` only joins the numbers together. -lösung: aufgabe-rechner-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-rechner-java -titel: Calculator in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Unknown operator -braucht: fallauswahl -tipp: The operator is a text. Read it with \`String operator = scanner.next();\`. -tipp: \`switch (operator) { case "+" -> { … } … default -> { … } }\`. With texts, the cases are in quotation marks. -lösung: aufgabe-rechner-loesung.visustruct -::: -`,q9=`--- -titel: Rental menu -nach: 18 -stufe: 2 ---- - -At the rental machine you can book several vehicles one after another. The menu keeps coming back until you choose **0** to quit. - -::: übung aufgabe-verleih -titel: Rental menu -auftrag: Keep asking for a choice: 1 = e-bike, 2 = e-scooter, 0 = quit. For 1 output **E-bike booked**, for 2 **E-scooter booked**, and for any other number **Invalid choice**. - For 0 the program outputs **Bye** and leaves the loop with a Break. -test: 0 -> Bye -test: 1, 2, 0 -> E-bike booked | E-scooter booked | Bye -test: 2, 5, 1, 0 -> E-scooter booked | Invalid choice | E-bike booked | Bye -braucht: endlosschleife, aussprung -tipp: The menu runs again and again until someone chooses 0. An endless loop with a Break is good for that. -tipp: Use an endless loop. Inside it, first comes the input \`input: int choice "1 = e-bike, 2 = e-scooter, 0 = quit"\`, then a switch on \`choice\` with the cases \`0\`, \`1\`, \`2\` and \`default\`. The case \`0\` holds the output and, below it, the Break. -lösung: aufgabe-verleih-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-verleih-java -titel: Rental menu in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 0 -> Bye -test: 1, 2, 0 -> E-bike booked | E-scooter booked | Bye -test: 2, 5, 1, 0 -> E-scooter booked | Invalid choice | E-bike booked | Bye -braucht: endlosschleife, aussprung -tipp: \`while (true) { … }\` runs forever until a \`break;\` comes. -tipp: In Java, use \`if\`/\`else if\` here instead of \`switch\`: a \`break;\` inside a \`switch\` only leaves the \`switch\`, not the loop. \`if (choice == 0) { System.out.println("Bye"); break; } else if (choice == 1) { … }\`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: -`,U9=`--- -titel: Vocabulary trainer -nach: 27 -stufe: 3 ---- - -Ludwig is learning German. His words are in a CSV file, English on the left, German on the right: - -::: datei words.csv -English;German -goat;Ziege -hay;Heu -meadow;Wiese -::: - -Because only the file knows the words, you can simply add new lines later without changing the program. - -::: angeberwissen -Sebastian Leitner made the flashcard box with several compartments popular in 1972: cards you know move one compartment further and come up less often, wrong ones go back to the first compartment. Many vocabulary apps still work on this principle today. -::: - -::: übung aufgabe-vokabeln -titel: Vocabulary trainer -code: java -auftrag: Ask for every word in the file: output **What is goat in German?** and read the answer with \`scanner.next()\`. If it is right, output **Right!**, otherwise for example **Wrong, it is Ziege.** - At the end, show how many were right, for example **2 of 3 right**. -start: aufgabe-vokabeln-start.visustruct -test: Ziege, Heu, Wiese -> What is goat in German? | Right! | What is hay in German? | Right! | What is meadow in German? | Right! | 3 of 3 right -test: Kuh, Heu, Wiese -> What is goat in German? | Wrong, it is Ziege. | What is hay in German? | Right! | What is meadow in German? | Right! | 2 of 3 right -test: Ziege, hay, wiese -> What is goat in German? | Right! | What is hay in German? | Wrong, it is Heu. | What is meadow in German? | Wrong, it is Wiese. | 1 of 3 right -braucht: whileSchleife, verzweigung -tipp: Skip the header line. In the loop, split every line with \`split(";")\`: \`parts[0]\` is the English word, \`parts[1]\` the German one. -tipp: Compare texts with \`answer.equals(parts[1])\`, not with \`==\`. Count along with \`int right = 0;\` and \`int count = 0;\` before the loop. -lösung: aufgabe-vokabeln-loesung.visustruct -::: -`,Q9=`--- -titel: Adult? -nach: 8 -stufe: 1 ---- - -At the cinema, for a driving licence or for voting: it often matters whether someone is already 18. - -::: übung aufgabe-volljaehrig -titel: Adult? -auftrag: Ask for the age. If the person is 18 or older, output **adult**, otherwise **minor**. -test: 18 -> adult -test: 35 -> adult -test: 17 -> minor -test: 6 -> minor -braucht: verzweigung -tipp: There are two possible outputs, so you need a branch. -tipp: Read in the age with \`input: int age "How old are you?"\`. The condition of the branch is \`age >= 18\`. Make sure that 18 already counts as adult. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -And now the same in Java: - -::: übung aufgabe-volljaehrig-java -titel: Adult? in Java -code: java -auftrag: Now solve the task above in Java. The outputs stay exactly the same. -test: 18 -> adult -test: 35 -> adult -test: 17 -> minor -test: 6 -> minor -braucht: verzweigung -tipp: Read the age with \`int age = scanner.nextInt();\`. -tipp: \`if (age >= 18) { … } else { … }\`, with a \`System.out.println(…)\` in each branch. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: -`,$9=`--- -titel: Rolling dice with MVC -nach: 40 -stufe: 3 ---- - -Build a dice game with Model View Controller. A button **Roll** rolls the dice, and two views show the result. - -The model is already finished. It rolls and counts how often it was rolled and how many sixes came up: - -\`\`\`java -class Dice { - int pips = 1; - int rolls = 0; - int sixes = 0; - - void roll() { - pips = (int) (Math.random() * 6) + 1; - rolls++; - if (pips == 6) { - sixes++; - } - } -} -\`\`\` - -Your task: - -1. Test the model without a window first: roll ten times in \`main\` and then output \`rolls\` and \`sixes\`. -2. Write the view \`PipsView\`. It shows the number rolled in a big JLabel, for example "4". -3. Write the view \`StatsView\`. It shows for example "Sixes: 2 of 10 rolls". -4. The controller is the listener at the button: it lets the model roll and hands the new values to both views. - -**You are done when …** - -- after ten rolls in the console \`rolls\` is 10 and \`sixes\` is between 0 and 10, -- every click on **Roll** shows a new number from 1 to 6 in the \`PipsView\`, -- the \`StatsView\` counts one more roll after every click and counts the sixes correctly, -- there is no \`JLabel\` in the model and no \`Math.random\` in the views. - -Extra: a button **New game** resets everything. The method for that belongs in the model. - -::: ludwig -Remember the rule from lesson 40: there isn't a single \`JLabel\` in the model. If you change the font size when rolling, that belongs in the view. -::: -`,Y9=`--- -titel: Double a number -nach: 2 -stufe: 1 ---- - -A variable can change its value. Here a number is doubled. - -::: übung aufgabe-verdoppeln -titel: Double a number -auftrag: Set the variable \`number\` to the value 4. Double it in a second statement without creating a new variable. Then output **Doubled number: 8**, where the 8 comes from the variable. -test: - -> Doubled number: 8 -tipp: Doubling means: times 2. You store the result in the same variable again. -tipp: \`number = number * 2\` calculates with the old value and stores the result in \`number\` again. You join text and a variable with \`+\`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: -`,K9=`--- -titel: Idade num subprograma -nach: 18 -stufe: 2 ---- - -A verificação da idade de “Maior de idade?” passa agora a ser um subprograma próprio. O programa principal só o chama. - -::: übung aufgabe-alter-unterprogramm -titel: Idade num subprograma -auftrag: Constrói dois diagramas. O programa principal mostra **Bem-vindo**, depois chama \`verificarIdade()\` e no fim mostra **Adeus**. - O subprograma \`verificarIdade\` fica num separador próprio: pede a idade e mostra **maior de idade** ou **menor de idade**. -test: 20 -> Bem-vindo | maior de idade | Adeus -test: 18 -> Bem-vindo | maior de idade | Adeus -test: 12 -> Bem-vindo | menor de idade | Adeus -braucht: aufruf -tipp: O programa principal tem três blocos. O subprograma faz o trabalho a sério. -tipp: Abre o exercício no editor e constrói lá o programa principal com a chamada \`verificarIdade()\`. Depois abre um novo separador com **+** e dá-lhe o título \`verificarIdade\`. Constrói nele a entrada e a decisão. O Verificar encontra o subprograma pelo título do separador. -lösung: aufgabe-alter-haupt-loesung.visustruct -unterprogramm: aufgabe-alter-unterprogramm-loesung.visustruct -::: -`,X9=`--- -titel: Avaliar o lançamento da bola -nach: 25 -stufe: 2 ---- - -No dia do desporto, todos escreveram numa tabela a distância a que lançaram a bola: - -::: datei lancamento.csv -Nome;Distancia -Ludwig;28 -Zora;42 -Raposa;35 -Lebre;21 -::: - -::: übung aufgabe-ballwurf -titel: Avaliar o lançamento da bola -code: java -auftrag: Lê o ficheiro e salta a linha de cabeçalho. Mostra quantos participaram (**Participantes: 4**) e a que distância lançaram em média (**Média: 31.5 m**). - Guarda o lançamento mais longo no ficheiro **vencedor.txt**, por exemplo **Vencedor: Zora (42 m)**. -start: aufgabe-ballwurf-start.visustruct -test: - -> Participantes: 4 | Média: 31.5 m -datei: vencedor.txt -> Vencedor: Zora (42 m) -braucht: whileSchleife, verzweigung -tipp: Precisas de quatro variáveis antes do ciclo: a contagem, a soma como \`double\`, a melhor distância e o melhor nome. -tipp: No ciclo: \`String[] partes = linha.split(";");\` e \`int distancia = Integer.parseInt(partes[1]);\`. Se \`distancia > melhorDistancia\`, guarda a distância e o nome. Para guardar: \`PrintWriter saida = new PrintWriter("vencedor.txt");\`, \`saida.println(…)\` e \`saida.close();\`. -lösung: aufgabe-ballwurf-loesung.visustruct -::: -`,e5=`--- -titel: Classificação -nach: 19 -stufe: 2 ---- - -Cinco cabras fizeram a corrida de obstáculos. Agora a classificação vai para a porta do estábulo: mais pontos em cima, menos pontos em baixo. - -::: übung aufgabe-bestenliste -titel: Classificação -auftrag: Lê 5 pontuações para um array. Ordena o array com o bubble sort por ordem **decrescente**, ou seja, o maior número primeiro. Depois mostra a lista com o lugar, por exemplo **1. 9**, na linha seguinte **2. 9** e assim por diante. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: O bubble sort da lição 19 ordena por ordem crescente. Para decrescente basta inverter a comparação dos vizinhos. -tipp: Troca-se quando \`pontos[i] < pontos[i + 1]\`, com uma variável auxiliar. Para a saída, calcula primeiro \`int lugar = i + 1\` e depois mostra \`lugar + ". " + pontos[i]\`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-bestenliste-java -titel: Classificação em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 3, 9, 2, 9, 1 -> 1. 9 | 2. 9 | 3. 3 | 4. 2 | 5. 1 -test: 10, 20, 30, 40, 50 -> 1. 50 | 2. 40 | 3. 30 | 4. 20 | 5. 10 -test: 5, 4, 3, 2, 1 -> 1. 5 | 2. 4 | 3. 3 | 4. 2 | 5. 1 -braucht: forSchleife, verzweigung -tipp: Lê cada número primeiro para uma variável e depois põe-no no array: \`int numero = scanner.nextInt();\` e \`pontos[i] = numero;\`. -tipp: A troca também precisa de uma variável auxiliar em Java: \`int aux = pontos[i]; pontos[i] = pontos[i + 1]; pontos[i + 1] = aux;\`. -lösung: aufgabe-bestenliste-loesung.visustruct -::: -`,t5=`--- -titel: Número binário -nach: 11 -stufe: 3 ---- - -Os computadores fazem contas só com dois algarismos: 0 e 1. É assim que se converte um número para o **sistema binário**: - -1. Divide o número por 2 e guarda o resto (0 ou 1). -2. Continua com o resultado sem o resto. -3. Repete isto até o número ser 0. - -Os restos lidos **de baixo para cima** dão o número binário. Para 6, os restos são 0, 1, 1, portanto o número binário é 110. - -::: übung aufgabe-binaer -titel: Número binário -auftrag: Pede um número inteiro maior do que 0 e mostra-o como número binário. Para 6 a saída é **110**, para 13 **1101**. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Vai dividindo o número por 2 e guarda cada resto. Os restos lidos ao contrário dão o número binário. -tipp: Junta os algarismos num texto: \`String binario = ""\`. No ciclo calculas \`int resto = numero % 2\` e acrescentas o resto à **frente**: \`binario = resto + binario\`. Depois, \`numero = numero / 2\`. Como numero é um número inteiro, o resto desaparece. -lösung: aufgabe-binaer-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-binaer-java -titel: Número binário em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 6 -> 110 -test: 13 -> 1101 -test: 1 -> 1 -test: 8 -> 1000 -braucht: whileSchleife -tipp: Começa com \`String binario = "";\`. O ciclo corre com \`while (numero > 0)\`. -tipp: No corpo: \`int resto = numero % 2;\`, depois \`binario = resto + binario;\` e \`numero = numero / 2;\`. Como \`binario\` é uma String, o \`+\` põe o resto à frente. -lösung: aufgabe-binaer-loesung.visustruct -::: -`,n5=`--- -titel: Cifra de César -nach: 17 -stufe: 3 ---- - -Há mais de 2000 anos, o general romano Júlio César enviava mensagens secretas às suas tropas. O truque era simples: substituía cada letra pela letra que está **três posições à frente** no alfabeto. O A passa a D, o B passa a E e assim por diante. A mensagem legível chama-se **texto simples**, a cifrada **texto cifrado**. O número de posições que se avança é a **chave**, muitas vezes chamada apenas \`k\`. - -::: grafik caesar-verschiebung.svg -Cifrar com a chave 3: cada letra de cima passa a ser a letra de baixo. No fim recomeça-se do início, por isso X, Y e Z passam a A, B e C. -::: - -Assim, \`HALLO\` passa a \`KDOOR\`. Quem conhece a chave recua simplesmente três posições e consegue voltar a ler a mensagem. - -### O disco de cifra - -Para não ter de contar sempre, construiu-se um **disco de cifra**: dois discos com o alfabeto, um pequeno sobre um grande, presos no meio para poderem rodar. Por fora está o texto simples, por dentro o texto cifrado. Rodando o disco interior três letras, por baixo de cada letra exterior aparece a sua letra cifrada. Discos assim existem desde o século XV. Na Guerra Civil Americana (1861–1865), os soldados levavam pequenos discos de latão. - -::: grafik caesar-scheibe.svg -Um disco de cifra inspirado nos discos históricos de latão. A janela azul mostra: com a chave 3, por baixo do A está o D. -::: - -::: angeberwissen -O próprio César nem sequer tinha um disco! Sabemo-lo pelo historiador romano Suetónio: nas suas cartas, César avançava cada letra três posições e contava isso simplesmente de cabeça. O disco só foi inventado por Leon Battista Alberti por volta de 1467, cerca de 1500 anos mais tarde. Aliás, eu também conto de cabeça. Não tenho dedos, só cascos. -::: - -Experimenta tu. Roda o disco interior, escreve um texto e vê como surge o texto cifrado. Com **Decifrar** faz-se o caminho inverso. - -::: caesar 3 -LUDWIG -::: - -::: ludwig -Um disco verdadeiro é muito fácil de fazer: dois círculos de cartão, um um pouco mais pequeno, cada um com 26 campos iguais para o alfabeto, e um colchete de latão no meio. Está pronta a tua ferramenta de agente secreto! -::: - -::: quiz -? Em que letra se transforma o Y com a chave 3? -- [ ] A -- [x] B -- [ ] C -> A seguir ao Y vem o Z, depois recomeça-se com A e B. Três posições à frente é, portanto, o B. - -? Um texto cifrado foi cifrado com a chave 5. Como o decifras? -- [ ] Avançar mais 5 posições -- [x] Recuar 5 posições -- [ ] Ler as letras de trás para a frente -> Decifrar é o contrário de cifrar. Recuar 5 posições é, aliás, o mesmo que avançar 21, porque 5 + 21 = 26. -::: - -### O programa: há mais do que um caminho - -Como ensinas o computador a avançar uma letra \`k\` posições? Não existe *a* única solução certa. Vamos ver dois caminhos. Ambos percorrem o texto carácter a carácter com um ciclo for, tal como na lição sobre cadeias de caracteres. A diferença está na forma como avançam uma única letra. - -Nos dois caminhos ajuda o operador de resto \`%\`. Ele garante que depois do Z se continua no A: as posições 0 a 25 ficam como estão, 26 passa a 0, 27 passa a 1 e assim por diante. - -### Caminho 1: o alfabeto num array - -Colocas o alfabeto num array \`char[] alfabeto = {'A', 'B', …, 'Z'}\`. Para cada letra da palavra procuras no array a sua posição \`pos\`, ou seja, o seu índice. A letra cifrada está então no mesmo array na posição \`(pos + k) % 26\`. - -Um exemplo com a chave 3: o X está na posição 23. Então \`(23 + 3) % 26 = 0\`, e na posição 0 está o A. - -::: übung aufgabe-caesar-array -titel: César com array -auftrag: O diagrama inicial já contém o array \`alfabeto\`. Pede uma palavra em maiúsculas e a chave \`k\`. Depois mostra a palavra cifrada. Para HALLO e 3, isso dá \`KDOOR\`. -start: aufgabe-caesar-array-start.visustruct -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife, verzweigung -tipp: Cria um texto vazio \`String segredo = ""\`. Percorre a palavra carácter a carácter com um ciclo for. Em cada passagem acrescentas uma letra cifrada ao fim de \`segredo\`. -tipp: Para encontrar a posição de uma letra precisas de um segundo ciclo, interior, sobre o array: se \`alfabeto[j] == letra\`, guardas \`pos = j\`. -tipp: Depois do ciclo interior acrescentas \`alfabeto[(pos + k) % 26]\`: \`segredo = segredo + alfabeto[(pos + k) % 26]\`. Depois do ciclo exterior mostras \`segredo\`. -lösung: aufgabe-caesar-array-loesung.visustruct -::: - -### Caminho 2: calcular com a tabela ASCII - -Para o computador, cada carácter é na verdade um número. Que número pertence a que carácter está na **tabela ASCII**. Aí o A tem o código 65, o B o código 66 e assim por diante até ao Z com 90. Em Java transformas um carácter no seu código com \`(int)\` e um código de volta num carácter com \`(char)\`: - -\`\`\` -int codigo = (int) 'A' -char letra = (char) 68 -\`\`\` - -Depois disso, \`codigo\` vale 65 e \`letra\` vale \`'D'\`. Uma conversão destas de um tipo de dados para outro chama-se **conversão de tipo** (em inglês *cast*). - -Agora já podes fazer contas com a letra. Para que o salto do Z de volta ao A funcione, subtrais primeiro 65. Assim o A fica na posição 0 e o Z na posição 25, tal como no caminho 1. Avanças com \`% 26\` e no fim voltas a somar os 65: - -::: grafik caesar-ascii.svg -Em cima, as letras maiúsculas com os seus códigos ASCII. Em baixo, o cálculo para o X com a chave 3: o resultado é A. -::: - -::: übung aufgabe-caesar-ascii -titel: César com ASCII -code: java -auftrag: Escreve um programa em Java que lê uma palavra em maiúsculas e a chave \`k\` e mostra a palavra cifrada. Desta vez sem array: calcula com \`(int)\` e \`(char)\`. -test: HALLO, 3 -> KDOOR -test: ZEBRA, 1 -> AFCSB -test: LUDWIG, 13 -> YHQJVT -braucht: forSchleife -tipp: Percorre a palavra com um ciclo for. O código do carácter na posição \`i\` obténs com \`int codigo = (int) palavra.charAt(i);\`. -tipp: Calcula \`codigo = (codigo - 65 + k) % 26 + 65;\` e acrescenta o carácter: \`segredo = segredo + (char) codigo;\`. Antes do ciclo escreves \`String segredo = "";\`, depois do ciclo \`System.out.println(segredo);\`. -lösung: aufgabe-caesar-ascii-loesung.visustruct -::: - -### Qual é o melhor caminho? - -**Caminho 1: array.** A ideia é procurar a posição no array. -- Vantagem: fácil de perceber, e também funciona com alfabetos próprios, p. ex. com Ç. -- Desvantagem: é preciso procurar cada letra. - -**Caminho 2: ASCII.** A ideia é calcular com o código do carácter. -- Vantagem: curto e sem procura, por isso mais rápido. -- Desvantagem: é preciso conhecer os códigos ASCII. - -Os dois programas dão o mesmo resultado. Na programação é muitas vezes assim: há várias soluções certas, e escolhes a que melhor se adapta à tua tarefa. - -::: übung aufgabe-caesar-entschluesseln -titel: Decifrar César -code: java -auftrag: Agora inverte o jogo. Lê um texto cifrado em maiúsculas e a chave \`k\` e mostra o texto simples. Para KDOOR e 3, isso dá \`HALLO\`. Escolhes tu o caminho. -test: KDOOR, 3 -> HALLO -test: AFCSB, 1 -> ZEBRA -test: YHQJVT, 13 -> LUDWIG -braucht: forSchleife -tipp: Decifrar significa recuar \`k\` posições em vez de avançar. Assim, o \`+ k\` passa a \`- k\`. -tipp: Cuidado com o A: \`0 - 3\` dá -3, e em Java o \`%\` não transforma isso num número positivo. Por isso soma ainda 26: \`codigo = (codigo - 65 - k + 26) % 26 + 65;\` -lösung: aufgabe-caesar-entschluesseln-loesung.visustruct -::: - -::: angeberwissen -Infelizmente, a cifra de César não é segura. Há apenas 25 chaves, e é possível experimentá-las todas em poucos minutos. Com um truque é ainda mais rápido: em português, as letras mais frequentes são o A e o E. Se no texto cifrado o H aparecer muitas vezes, a chave foi provavelmente 3. Na internet existe ainda o ROT13, que é a cifra de César com a chave 13. Aplicar ROT13 duas vezes devolve o texto simples, porque 13 + 13 = 26. -::: -`,i5=`--- -titel: Sequência de Collatz -nach: 11 -stufe: 2 ---- - -A sequência de Collatz é um enigma famoso da matemática. Começas com um número e aplicas sempre a mesma regra: - -- Se o número for **par**, divides por 2. -- Se for **ímpar**, multiplicas por 3 e somas 1. - -Até agora, para todos os números iniciais que alguém experimentou, a sequência acaba por chegar a 1. Se isto é verdade para todos os números, ninguém sabe até hoje. - -::: übung aufgabe-collatz -titel: Sequência de Collatz -auftrag: Pede um número inicial. Aplica a regra até o número ser 1 e mostra o novo número depois de cada passo. Vai contando os passos e mostra no fim **Passos:** e o número de passos. - Com o número inicial 6, a sequência é 3, 10, 5, 16, 8, 4, 2, 1. São 8 passos. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 7 -test: 1 -> Passos: 0 -braucht: whileSchleife, verzweigung -tipp: Não sabes quantos passos vão ser. Por isso um ciclo while serve melhor do que um ciclo for. -tipp: O ciclo while é executado enquanto \`numero != 1\`. Verificas se um número é par com \`numero % 2 == 0\`. A saída e \`passos++\` vêm depois da decisão, mas ainda dentro do ciclo. -lösung: aufgabe-collatz-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-collatz-java -titel: Sequência de Collatz em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 6 -> 3 | 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 8 -test: 3 -> 10 | 5 | 16 | 8 | 4 | 2 | 1 | Passos: 7 -test: 1 -> Passos: 0 -braucht: whileSchleife, verzweigung -tipp: Um ciclo \`while\` corre enquanto \`numero != 1\`. Lá dentro, um \`if\` com \`numero % 2 == 0\` decide. -tipp: No corpo: dividir por 2 ou \`numero = numero * 3 + 1;\`, depois \`System.out.println(numero);\` e \`passos++;\`. Depois do ciclo vem \`"Passos: " + passos\`. -lösung: aufgabe-collatz-loesung.visustruct -::: - -::: angeberwissen -Esta tarefa pode tornar-te rico! O matemático Paul Erdős ofereceu 500 dólares por uma demonstração. Em 2021, a empresa japonesa Bakuage juntou bastante mais: 120 milhões de ienes, na altura cerca de um milhão de dólares, por uma demonstração ou um contraexemplo. Ainda ninguém foi buscar o dinheiro. Já agora, o enigma foi inventado pelo matemático alemão Lothar Collatz em 1937. Vou já começar a fazer contas; com tanto dinheiro compra-se muito feno. -::: -`,a5=`--- -titel: Conversor de unidades -nach: 35 -stufe: 2 ---- - -Constrói uma janela que converte comprimentos. Em cima escreves um comprimento em metros, por baixo há três botões: - -- **em cm** mostra o comprimento em centímetros, 1.5 passa a "150.0 cm", -- **em mm** mostra-o em milímetros, 1.5 passa a "1500.0 mm", -- **em km** mostra-o em quilómetros, 1500 passa a "1.5 km". - -O resultado aparece num JLabel por baixo dos botões. Se no campo não estiver um número, aparece "Escreve um número", e o programa não vai abaixo. - -Podes começar assim: - -\`\`\`java -import javax.swing.*; -import java.awt.*; - -public class Unidades { - public static void main(String[] args) { - JFrame janela = new JFrame("Conversor de unidades"); - janela.setSize(300, 200); - janela.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - janela.setLayout(new GridLayout(0, 1)); - - JTextField metros = new JTextField(); - JButton emCm = new JButton("em cm"); - JLabel resultado = new JLabel("?", SwingConstants.CENTER); - - emCm.addActionListener(e -> { - double m = Double.parseDouble(metros.getText()); - resultado.setText((m * 100) + " cm"); - }); - - janela.add(metros); - janela.add(emCm); - janela.add(resultado); - janela.setVisible(true); - } -} -\`\`\` - -**Terminaste quando …** - -- 1.5 mostra "150.0 cm" depois de **em cm** e "1500.0 mm" depois de **em mm**, -- 1500 mostra "1.5 km" depois de **em km**, -- "abc" no campo mostra "Escreve um número" e a janela continua aberta. - -Extra: um quarto botão **em pés** converte para a unidade inglesa. Um pé são 0.3048 m. - -::: ludwig -Marca **Java verdadeiro (com Swing)** na [roda dentada](#zeige-einstellungen). Para "abc" no campo, cada ouvinte precisa de um try/catch com \`NumberFormatException\`, tal como na calculadora. -::: -`,s5=`--- -titel: Números de Fibonacci -nach: 13 -stufe: 2 ---- - -Nos números de Fibonacci, cada número é a soma dos dois anteriores. A sequência começa com 1 e 1: - -1, 1, 2, 3, 5, 8, 13, 21, … - -Encontras estes números surpreendentemente vezes na natureza, por exemplo na forma como as sementes do girassol estão dispostas. - -::: ludwig -Lembras-te da **troca** da lição 5? Aí precisavas de uma variável auxiliar, porque \`a = b\` substitui o valor antigo de \`a\`. Aqui é igual: antes de \`numero2\` receber o número novo, o seu valor antigo tem de estar guardado em \`numero1\`. Atenção à **ordem**: primeiro \`numero1 = numero2\`, depois \`numero2 = numero3\`. Vais precisar outra vez de avançar e trocar valores mais tarde, por exemplo ao ordenar na lição 19. -::: - -::: angeberwissen -Em 1202, Leonardo de Pisa, conhecido como Fibonacci, pôs no seu livro de cálculo um problema sobre coelhos: a partir do segundo mês, cada casal de coelhos tem um novo casal todos os meses. Quantos casais há ao fim de um ano? A resposta é esta sequência. Infelizmente, o livro não tinha cabras. -::: - -::: übung aufgabe-fibonacci -titel: Números de Fibonacci -auftrag: Pergunta quantos números de Fibonacci devem ser mostrados. Depois mostra exatamente esses, cada um na sua linha. Podes partir do princípio de que é introduzido pelo menos 2. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Cada número novo é a soma dos dois anteriores. Por isso tens sempre de guardar os dois últimos números. -tipp: Guarda os dois últimos números em \`numero1\` e \`numero2\` e mostra os dois antes do ciclo. O ciclo começa em \`int i = 2\`. Lá dentro calculas \`int numero3 = numero1 + numero2\`, mostras e depois avanças: \`numero1 = numero2\` e \`numero2 = numero3\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-fibonacci-java -titel: Números de Fibonacci em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 5 -> 1 | 1 | 2 | 3 | 5 -test: 8 -> 1 | 1 | 2 | 3 | 5 | 8 | 13 | 21 -test: 2 -> 1 | 1 -braucht: forSchleife -tipp: Os dois primeiros números mostras antes do ciclo. O ciclo \`for\` começa então em \`int i = 2\`. -tipp: No corpo: \`int numero3 = numero1 + numero2;\`, mostrar, depois \`numero1 = numero2;\` e \`numero2 = numero3;\`. -lösung: aufgabe-fibonacci-loesung.visustruct -::: - -::: quiz -? Porque é que no ciclo \`numero1 = numero2\` tem de vir antes de \`numero2 = numero3\`? -- [ ] Porque senão o Java não permite as atribuições -- [x] Porque senão o valor antigo de numero2 é substituído antes de numero1 o receber -- [ ] A ordem não importa -> Ao contrário, numero1 e numero2 ficariam depois com o mesmo valor. Tal como na troca: primeiro guardar, depois substituir. -::: -`,r5=`--- -titel: Par ou ímpar -nach: 8 -stufe: 1 ---- - -Um número é par se não sobrar resto quando o divides por 2. O resto calcula-se com \`%\`: \`8 % 2\` dá 0, \`7 % 2\` dá 1. - -::: übung aufgabe-gerade -titel: Par ou ímpar -auftrag: Pede um número inteiro. Mostra se é par ou ímpar. Para 4 a saída é **4 é par**, para 7 **7 é ímpar**. -test: 4 -> 4 é par -test: 7 -> 7 é ímpar -test: 0 -> 0 é par -test: 15 -> 15 é ímpar -braucht: verzweigung -tipp: Uma decisão escolhe entre par e ímpar. Par quer dizer: divisível por 2 sem resto. -tipp: A condição é \`numero % 2 == 0\`. Na saída juntas o número e o texto com \`+\`, p. ex. \`numero + " é par"\`. -lösung: aufgabe-gerade-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-gerade-java -titel: Par ou ímpar em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 4 -> 4 é par -test: 7 -> 7 é ímpar -test: 0 -> 0 é par -test: 15 -> 15 é ímpar -braucht: verzweigung -tipp: Lês um número com \`int numero = scanner.nextInt();\`. Antes disso perguntas com \`System.out.println(…)\`. -tipp: \`if (numero % 2 == 0) { … } else { … }\`. A saída é \`System.out.println(numero + " é par");\`. -lösung: aufgabe-gerade-loesung.visustruct -::: -`,o5=`--- -titel: IVA -nach: 6 -stufe: 1 ---- - -Em Portugal continental, a quase tudo o que compras é somado 23 % de **IVA** (imposto sobre o valor acrescentado). A loja calcula com o preço sem IVA e soma o imposto por cima. Isso dá o preço com IVA que pagas na caixa. - -Um exemplo: compras um telemóvel dobrável que se abre e fica um tablet. Na etiqueta da loja estão 1000 euros sem IVA. Por cima vêm 23 %, ou seja, 230 euros. Na caixa pagas 1230 euros. A loja não pode ficar com os 230 euros: entrega-os às Finanças. - -::: grafik aufgabe-mehrwertsteuer.svg -Preço sem IVA mais IVA dá o preço com IVA. O IVA vai para as Finanças. -::: - -::: angeberwissen -Mééé, claro que a própria loja comprou o telemóvel: ao fabricante, por 800 euros mais 184 euros de IVA. Estes 184 euros chamam-se **IVA dedutível**, e a loja pode descontá-los dos 230 euros. Por isso só entrega 46 euros às Finanças. No fim, só uma pessoa paga o imposto todo: o cliente na caixa. Ou seja, tu. Eu, já agora, não pago imposto nenhum. Só como erva. -::: - -A taxa do imposto nunca muda enquanto fazes as contas. Por isso crias-a como **constante**: uma variável cujo valor não muda depois de definido. Escreves \`const\` à frente, por exemplo \`const double IVA = 0.23\`. Os nomes das constantes escrevem-se em maiúsculas para as reconheceres logo. No código Java isto passa a \`final double IVA = 0.23;\`. - -Se a taxa alguma vez mudar, só tens de alterar esta linha. - -::: übung aufgabe-mehrwertsteuer -titel: IVA -auftrag: Cria a constante \`IVA\` com o valor 0.23. Depois pede o preço sem IVA. Mostra primeiro o imposto e depois o preço com IVA, de cada vez só o número. - Para um preço sem IVA de 100, as saídas são **23** e **123**. -test: 100 -> 23 | 123 -test: 50 -> 11.5 | 61.5 -test: 0 -> 0 | 0 -tipp: O imposto é 23 por cento do preço sem IVA. O preço com IVA é o que pagas no fim. -tipp: O imposto é \`precoSemIva * IVA\`. O preço com IVA obténs somando o imposto ao preço sem IVA. Os dois valores são números decimais, portanto \`double\`. -lösung: aufgabe-mehrwertsteuer-loesung.visustruct -::: - -::: quiz -? Porque crias a taxa do imposto como constante? -- [ ] Porque as constantes são calculadas mais depressa -- [x] Porque o seu valor não deve mudar no programa -- [ ] Porque os números decimais só podem ser guardados como constantes -> Uma constante mantém o seu valor. Se a taxa alguma vez mudar, só alteras a linha com \`const\`. -::: -`,l5=`--- -titel: Média das notas -nach: 13 -stufe: 2 ---- - -Como te correu ultimamente? A média é a soma de todas as notas a dividir pelo número de notas. - -::: übung aufgabe-durchschnitt -titel: Média das notas -auftrag: Pergunta primeiro quantas notas vão ser introduzidas. Depois lê exatamente essas notas e mostra a média, só o número. - Para as notas 4 e 5, a média é **4.5**. -test: 2, 4, 5 -> 4.5 -test: 3, 3, 4, 5 -> 4 -test: 4, 3, 3, 4, 5 -> 3.75 -braucht: forSchleife -tipp: Precisas da soma de todas as notas e de quantas são. A média é a soma a dividir pela quantidade. -tipp: O ciclo for vai de \`int i = 0\` enquanto \`i < quantidade\`. Lá dentro lês uma nota e somas à soma. Guardas a média como número decimal: \`double media = soma * 1.0 / quantidade\`. O \`* 1.0\` garante que também o Java não faz uma divisão inteira. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-durchschnitt-java -titel: Média das notas em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 2, 4, 5 -> 4.5 -test: 3, 3, 4, 5 -> 4 -test: 4, 3, 3, 4, 5 -> 3.75 -braucht: forSchleife -tipp: \`for (int i = 0; i < quantidade; i++)\` lê exatamente as notas que foram indicadas antes. -tipp: Divide com \`double media = soma * 1.0 / quantidade;\`. Sem \`* 1.0\`, o Java divide dois \`int\` como inteiros, e 1.5 passa a 1. -lösung: aufgabe-durchschnitt-loesung.visustruct -::: -`,u5=`--- -titel: Verificar o PIN -nach: 12 -stufe: 2 ---- - -Numa caixa multibanco tens três tentativas para o teu PIN. Se escreveres um PIN errado três vezes, o cartão fica bloqueado. - -::: angeberwissen -John Shepherd-Barron, que instalou o primeiro multibanco em Londres em 1967, queria PINs de seis algarismos. Mas a mulher dele disse que só conseguia decorar quatro. É por isso que o teu PIN tem hoje quatro algarismos. -::: - -::: übung aufgabe-pin -titel: Verificar o PIN -auftrag: O PIN certo é 1234. Pede o PIN até estar certo ou até se terem gastado três tentativas. Depois mostra **PIN correto** ou **Cartão bloqueado**. -test: 1234 -> PIN correto -test: 1111, 1234 -> PIN correto -test: 1, 2, 1234 -> PIN correto -test: 1, 2, 3 -> Cartão bloqueado -braucht: doUntilSchleife, verzweigung -tipp: O ciclo termina em dois casos: quando o PIN está certo ou quando se gastaram três tentativas. Conta as tentativas. -tipp: O PIN é pedido pelo menos uma vez, por isso serve um ciclo do-while. Antes dele, cria \`int pin = 0\` e \`int tentativas = 0\`. O ciclo repete-se enquanto \`pin != 1234 && tentativas < 3\`. Depois do ciclo, uma decisão decide o que é mostrado. -lösung: aufgabe-pin-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-pin-java -titel: Verificar o PIN em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 1234 -> PIN correto -test: 1111, 1234 -> PIN correto -test: 1, 2, 1234 -> PIN correto -test: 1, 2, 3 -> Cartão bloqueado -braucht: doUntilSchleife, verzweigung -tipp: \`do { … } while (…);\` pergunta pelo menos uma vez. Depois do \`while (…)\` há um ponto e vírgula. -tipp: Cria antes \`int pin = 0;\` e \`int tentativas = 0;\`. No ciclo: \`int entrada = scanner.nextInt();\`, \`pin = entrada;\` e \`tentativas++;\`. A condição é \`pin != 1234 && tentativas < 3\`. -lösung: aufgabe-pin-java-loesung.visustruct -::: -`,m5=`--- -titel: Batalha naval -nach: 16 -stufe: 2 ---- - -No jogo da batalha naval, o mar é uma grelha. As colunas chamam-se A a E, as filas 1 a 5. No diagrama inicial, o mar é o array 2D \`mar\`: \`true\` quer dizer que há parte de um navio nesse quadrado. - -::: grafik schiffe-versenken.svg -Cinzento quer dizer \`true\`: aqui há um navio. Os pequenos números cinzentos são os índices no array; começam em 0. No jogo, pelo contrário, os quadrados chamam-se A a E e 1 a 5. -::: - -::: übung aufgabe-schiffe -titel: Batalha naval -auftrag: Mostra todos os quadrados que têm um navio, cada quadrado na sua linha, p. ex. **B1**. Percorre o mar fila a fila com dois ciclos encaixados. - Para o diagrama inicial aparecem **B1**, **C1**, **D1**, **E3**, **E4** e **A5**. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Percorre o mar com dois ciclos encaixados, tal como no cinema. Só mostras os quadrados que têm \`true\`. -tipp: O ciclo exterior conta \`fila\`, o interior \`coluna\`, os dois a partir de 0. Se houver um navio em \`mar[fila][coluna]\`, mostras a letra de \`colunas[coluna]\` e o número da fila: \`output: colunas[coluna] + (fila + 1)\`. Precisas do \`+ 1\` porque o jogo começa a contar em 1. -lösung: aufgabe-schiffe-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-schiffe-java -titel: Batalha naval em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -start: aufgabe-schiffe-start.visustruct -test: - -> B1 | C1 | D1 | E3 | E4 | A5 -braucht: forSchleife, verzweigung -tipp: Dois ciclos \`for\` um dentro do outro: fora a fila, dentro a coluna, ambos até \`.length\`. -tipp: \`if (mar[fila][coluna]) { System.out.println(colunas[coluna] + (fila + 1)); }\`. Os parênteses à volta de \`fila + 1\` fazem o Java calcular primeiro. -lösung: aufgabe-schiffe-loesung.visustruct -::: -`,c5=`--- -titel: Ementa -nach: 10 -stufe: 1 ---- - -Na cantina da escola pedes por número. O programa diz-te que prato corresponde ao número. - -::: übung aufgabe-speisekarte -titel: Ementa -auftrag: Pede um número. Para 1 mostra **Salada grega**, para 2 **Bacalhau à Brás** e para 3 **Wrap de falafel**. Para qualquer outro número mostra **Escolhe um número válido**. -test: 1 -> Salada grega -test: 2 -> Bacalhau à Brás -test: 3 -> Wrap de falafel -test: 7 -> Escolhe um número válido -braucht: fallauswahl -tipp: Para vários números fixos serve uma seleção. O que acontece com todos os outros números? -tipp: Lê o número com \`input: int prato "Que prato?"\`. A seleção verifica \`prato\`, e os casos chamam-se \`1\`, \`2\` e \`3\`. Todos os outros números vão parar ao caso \`default\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-speisekarte-java -titel: Ementa em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 1 -> Salada grega -test: 2 -> Bacalhau à Brás -test: 3 -> Wrap de falafel -test: 7 -> Escolhe um número válido -braucht: fallauswahl -tipp: Em Java isto é uma seleção com \`switch (prato) { … }\`. -tipp: Cada caso é assim: \`case 1 -> { System.out.println("…"); }\`. Para todos os outros números há \`default -> { … }\`. -lösung: aufgabe-speisekarte-loesung.visustruct -::: -`,d5=`--- -titel: Somar -nach: 2 -stufe: 1 ---- - -Define dois números, soma-os e mostra o resultado. - -::: übung aufgabe-summe -titel: Somar -auftrag: Dá a \`numero1\` o valor 4 e a \`numero2\` o valor 3. Calcula a soma numa terceira variável e mostra só a soma. -test: - -> 7 -tipp: Precisas de três variáveis: duas para os números e uma para o resultado. -tipp: Defines uma variável com uma instrução como \`int numero1 = 4\`. Na saída escreves o nome da variável sem aspas. -lösung: aufgabe-summe-loesung.visustruct -::: -`,h5=`--- -titel: Sortear quem limpa o quadro -nach: 15 -stufe: 2 ---- - -Quem é que limpa o quadro esta semana? Cinco nomes vão para um array, depois é sorteado um número. - -::: übung aufgabe-tafeldienst -titel: Sortear quem limpa o quadro -auftrag: Lê cinco nomes para um array de Strings. Depois pede um número sorteado de 1 a 5 e mostra **Limpa o quadro:** e o nome correspondente. - O número 1 corresponde ao primeiro nome, o 5 ao último. -test: Ada, Rui, Inês, Duarte, Eva, 3 -> Limpa o quadro: Inês -test: Ada, Rui, Inês, Duarte, Eva, 1 -> Limpa o quadro: Ada -test: Lara, Mário, Nuno, Olga, Pedro, 5 -> Limpa o quadro: Pedro -braucht: forSchleife -tipp: Primeiro, um ciclo lê os cinco nomes. Depois já não precisas de ciclo, só de olhar para uma posição. -tipp: Crias o array com \`String[] nomes = new String[5]\`. Atenção: o número sorteado começa em 1, o índice em 0. Por isso o nome está em \`nomes[sorteado - 1]\`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-tafeldienst-java -titel: Sortear quem limpa o quadro em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: Ada, Rui, Inês, Duarte, Eva, 3 -> Limpa o quadro: Inês -test: Ada, Rui, Inês, Duarte, Eva, 1 -> Limpa o quadro: Ada -test: Lara, Mário, Nuno, Olga, Pedro, 5 -> Limpa o quadro: Pedro -braucht: forSchleife -tipp: Um array para cinco nomes crias com \`String[] nomes = new String[5];\`. -tipp: No ciclo: \`nomes[i] = scanner.next();\`. O número 1 corresponde à posição 0, por isso mostras \`nomes[sorteado - 1]\`. -lösung: aufgabe-tafeldienst-loesung.visustruct -::: - -> Com \`int sorteado = (int) (Math.random() * 5) + 1\` também podes deixar o acaso sortear o número. Mas para a verificação automática precisamos de uma entrada fixa. -`,g5=`--- -titel: Calculadora -nach: 10 -stufe: 2 ---- - -Uma pequena calculadora: escreves dois números e um operador, e aparece o resultado. - -::: übung aufgabe-rechner -titel: Calculadora -auftrag: Pede dois números inteiros e depois um operador como texto: \`+\`, \`-\` ou \`*\`. Mostra o resultado. Para qualquer outro sinal mostra **Operador desconhecido**. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Operador desconhecido -braucht: fallauswahl -tipp: Lê os três valores. Depois, uma seleção sobre o operador decide o que é calculado. -tipp: Lês o operador com \`input: String operador "Operador"\`. A seleção verifica \`operador\`, os casos chamam-se \`"+"\`, \`"-"\` e \`"*"\`, e o último caso é \`default\`. Em cada caso, calcula primeiro numa variável e depois mostra-a, senão o \`+\` só junta os números. -lösung: aufgabe-rechner-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-rechner-java -titel: Calculadora em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 3, 4, + -> 7 -test: 10, 4, - -> 6 -test: 6, 7, * -> 42 -test: 1, 2, x -> Operador desconhecido -braucht: fallauswahl -tipp: O operador é um texto. Lês com \`String operador = scanner.next();\`. -tipp: \`switch (operador) { case "+" -> { … } … default -> { … } }\`. Com textos, os casos ficam entre aspas. -lösung: aufgabe-rechner-loesung.visustruct -::: -`,p5=`--- -titel: Menu de aluguer -nach: 18 -stufe: 2 ---- - -Na máquina de aluguer podes reservar vários veículos, um a seguir ao outro. O menu volta sempre até escolheres **0** para sair. - -::: übung aufgabe-verleih -titel: Menu de aluguer -auftrag: Pede sempre uma escolha: 1 = bicicleta elétrica, 2 = trotinete elétrica, 0 = sair. Para 1 mostra **Bicicleta reservada**, para 2 **Trotinete reservada** e para qualquer outro número **Escolha inválida**. - Para 0, o programa mostra **Adeus** e sai do ciclo com uma Interrupção. -test: 0 -> Adeus -test: 1, 2, 0 -> Bicicleta reservada | Trotinete reservada | Adeus -test: 2, 5, 1, 0 -> Trotinete reservada | Escolha inválida | Bicicleta reservada | Adeus -braucht: endlosschleife, aussprung -tipp: O menu é executado uma e outra vez até alguém escolher 0. Para isso serve bem um ciclo infinito com uma Interrupção. -tipp: Usa um ciclo infinito. Lá dentro vem primeiro a entrada \`input: int escolha "1 = bicicleta elétrica, 2 = trotinete elétrica, 0 = sair"\`, depois uma seleção sobre \`escolha\` com os casos \`0\`, \`1\`, \`2\` e \`default\`. O caso \`0\` tem a saída e, por baixo, a Interrupção. -lösung: aufgabe-verleih-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-verleih-java -titel: Menu de aluguer em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 0 -> Adeus -test: 1, 2, 0 -> Bicicleta reservada | Trotinete reservada | Adeus -test: 2, 5, 1, 0 -> Trotinete reservada | Escolha inválida | Bicicleta reservada | Adeus -braucht: endlosschleife, aussprung -tipp: \`while (true) { … }\` corre sem fim até aparecer um \`break;\`. -tipp: Em Java usa aqui \`if\`/\`else if\` em vez de \`switch\`: um \`break;\` dentro de um \`switch\` só sai do \`switch\`, não do ciclo. \`if (escolha == 0) { System.out.println("Adeus"); break; } else if (escolha == 1) { … }\`. -lösung: aufgabe-verleih-java-loesung.visustruct -::: -`,f5=`--- -titel: Treino de vocabulário -nach: 27 -stufe: 3 ---- - -O Ludwig está a aprender inglês. As palavras dele estão num ficheiro CSV, à esquerda em português, à direita em inglês: - -::: datei vocabulario.csv -Português;Inglês -cabra;goat -feno;hay -prado;meadow -::: - -Como só o ficheiro conhece as palavras, mais tarde podes simplesmente acrescentar linhas novas sem alterar o programa. - -::: angeberwissen -Foi Sebastian Leitner quem, em 1972, tornou conhecida a caixa de fichas com várias divisões: as fichas que sabes passam para a divisão seguinte e aparecem menos vezes, as erradas voltam para a primeira. Muitas apps de vocabulário ainda funcionam assim. -::: - -::: übung aufgabe-vokabeln -titel: Treino de vocabulário -code: java -auftrag: Pergunta cada palavra do ficheiro: mostra **Como se diz cabra em inglês?** e lê a resposta com \`scanner.next()\`. Se estiver certa, mostra **Certo!**, senão por exemplo **Errado, é goat.** - No fim aparece quantas estavam certas, por exemplo **2 de 3 certas**. -start: aufgabe-vokabeln-start.visustruct -test: goat, hay, meadow -> Como se diz cabra em inglês? | Certo! | Como se diz feno em inglês? | Certo! | Como se diz prado em inglês? | Certo! | 3 de 3 certas -test: cow, hay, meadow -> Como se diz cabra em inglês? | Errado, é goat. | Como se diz feno em inglês? | Certo! | Como se diz prado em inglês? | Certo! | 2 de 3 certas -test: goat, feno, prado -> Como se diz cabra em inglês? | Certo! | Como se diz feno em inglês? | Errado, é hay. | Como se diz prado em inglês? | Errado, é meadow. | 1 de 3 certas -braucht: whileSchleife, verzweigung -tipp: Salta a linha de cabeçalho. No ciclo, divide cada linha com \`split(";")\`: \`partes[0]\` é a palavra em português, \`partes[1]\` a palavra em inglês. -tipp: Os textos comparam-se com \`resposta.equals(partes[1])\`, não com \`==\`. Conta com \`int certas = 0;\` e \`int contagem = 0;\` antes do ciclo. -lösung: aufgabe-vokabeln-loesung.visustruct -::: -`,b5=`--- -titel: Maior de idade? -nach: 8 -stufe: 1 ---- - -No cinema, na carta de condução ou para votar: muitas vezes interessa saber se alguém já tem 18 anos. - -::: übung aufgabe-volljaehrig -titel: Maior de idade? -auftrag: Pede a idade. Se a pessoa tiver 18 anos ou mais, mostra **maior de idade**, senão **menor de idade**. -test: 18 -> maior de idade -test: 35 -> maior de idade -test: 17 -> menor de idade -test: 6 -> menor de idade -braucht: verzweigung -tipp: Há duas saídas possíveis, por isso precisas de uma decisão. -tipp: Lê a idade com \`input: int idade "Quantos anos tens?"\`. A condição da decisão é \`idade >= 18\`. Garante que 18 já conta como maior de idade. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: - -E agora o mesmo em Java: - -::: übung aufgabe-volljaehrig-java -titel: Maior de idade? em Java -code: java -auftrag: Resolve agora em Java a tarefa de cima. As saídas ficam exatamente iguais. -test: 18 -> maior de idade -test: 35 -> maior de idade -test: 17 -> menor de idade -test: 6 -> menor de idade -braucht: verzweigung -tipp: Lê a idade com \`int idade = scanner.nextInt();\`. -tipp: \`if (idade >= 18) { … } else { … }\`, com um \`System.out.println(…)\` em cada ramo. -lösung: aufgabe-volljaehrig-loesung.visustruct -::: -`,x5=`--- -titel: Lançar o dado com MVC -nach: 40 -stufe: 3 ---- - -Constrói um jogo de dados com Model View Controller. Um botão **Lançar** lança o dado, e duas views mostram o resultado. - -O model já está pronto. Lança o dado e conta quantas vezes foi lançado e quantos seis saíram: - -\`\`\`java -class Dado { - int pontos = 1; - int lancamentos = 0; - int seis = 0; - - void lancar() { - pontos = (int) (Math.random() * 6) + 1; - lancamentos++; - if (pontos == 6) { - seis++; - } - } -} -\`\`\` - -A tua tarefa: - -1. Testa primeiro o model sem janela: lança dez vezes no \`main\` e depois mostra \`lancamentos\` e \`seis\`. -2. Escreve a view \`VistaPontos\`. Mostra o número que saiu num JLabel grande, por exemplo "4". -3. Escreve a view \`VistaEstatistica\`. Mostra por exemplo "Seis: 2 em 10 lançamentos". -4. O controller é o ouvinte do botão: manda o model lançar e passa os valores novos às duas views. - -**Terminaste quando …** - -- na consola, depois de dez lançamentos, \`lancamentos\` é 10 e \`seis\` está entre 0 e 10, -- cada clique em **Lançar** mostra um número novo de 1 a 6 na \`VistaPontos\`, -- a \`VistaEstatistica\` conta mais um lançamento depois de cada clique e conta bem os seis, -- no model não há nenhum \`JLabel\` e nas views não há nenhum \`Math.random\`. - -Extra: um botão **Novo jogo** põe tudo no início. O método para isso pertence ao model. - -::: ludwig -Lembra-te da regra da lição 40: no model não há um único \`JLabel\`. Se mudares o tamanho da letra ao lançar, isso pertence à view. -::: -`,v5=`--- -titel: Duplicar um número -nach: 2 -stufe: 1 ---- - -Uma variável pode mudar de valor. Aqui um número é duplicado. - -::: übung aufgabe-verdoppeln -titel: Duplicar um número -auftrag: Dá à variável \`numero\` o valor 4. Duplica-a numa segunda instrução, sem criar uma variável nova. Depois mostra **Número duplicado: 8**, em que o 8 vem da variável. -test: - -> Número duplicado: 8 -tipp: Duplicar quer dizer: vezes 2. Guardas o resultado outra vez na mesma variável. -tipp: \`numero = numero * 2\` calcula com o valor antigo e volta a guardar o resultado em \`numero\`. Juntas texto e uma variável com \`+\`. -lösung: aufgabe-verdoppeln-loesung.visustruct -::: -`,w5=`--- -titel: Struktogramm und Editor -bereich: Losfahren -kurz: Was ein Struktogramm ist, wie du den Editor bedienst und deine Programme speicherst. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Algorithmus und ein Struktogramm ist -- die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen -- im Editor Elemente einfügen, verschieben und löschen -- ein Struktogramm speichern und wieder öffnen -::: - -## Ein Plan für den Roboter - -Ein Roboter denkt nicht mit. Er tut genau das, was in seinem Programm steht, nicht mehr und nicht weniger. Bevor du ihn programmierst, brauchst du deshalb einen genauen Plan. So eine genaue Schritt-für-Schritt-Anleitung heißt **Algorithmus**. - -Ein **Struktogramm** zeichnet den Plan als Kästen. Du liest es von oben nach unten. Jeder Kasten ist ein Schritt. Erfunden haben es 1973 Isaac Nassi und Ben Shneiderman, deshalb heißt es auch **Nassi-Shneiderman-Diagramm**. - -::: diagramm vb-l00-sequenz-loesung.visustruct -Fahre vor, drehe nach rechts, fahre vor: drei Kästen, von oben nach unten. -::: - -::: angeberwissen -Das erste Programm für eine Maschine schrieb Ada Lovelace schon 1843, für eine Rechenmaschine, die nie fertig gebaut wurde. Sie ahnte damals schon, dass solche Maschinen einmal mehr können als rechnen, zum Beispiel Musik machen. Dein mBot2 kann beides. -::: - -## Drei Bausteine - -Jedes Programm, auch das für einen Roboter, besteht aus nur drei Bausteinen: - -| Baustein | Was er tut | Beispiel mit dem mBot2 | -|---|---|---| -| **Sequenz** | Schritte nacheinander | vorfahren, drehen, vorfahren | -| **Verzweigung** | je nach Bedingung das eine oder das andere | Ist die Wand nah? Dann drehen, sonst vorfahren. | -| **Wiederholung** | Schritte mehrmals | viermal das Gleiche tun | - -Im Struktogramm sieht man die Bausteine sofort: Die Verzweigung hat oben ein Dreieck und zwei Spalten: links **true** (wahr), rechts **false** (falsch). Die Wiederholung (Schleife) umschließt ihre Kästen wie ein gekipptes L. - -::: diagramm vb-l00-bausteine-loesung.visustruct -Alle drei Bausteine: erst ein Kasten, dann eine Schleife, darin eine Verzweigung. -::: - -## Der Editor - -Der [Struktogramm-Editor](#zeige-editor) besteht aus mehreren Bereichen. Fahre mit der Maus über ein blaues Wort oder tippe es an, dann leuchtet der Bereich auf: - -- **Links die [Elemente](#zeige-elemente):** alle Bausteine zum Hineinziehen. In der Gruppe **mBot2-Befehle** liegen die Befehle für den mBot2 wie **fahre vor** und **drehe rechts**. -- **In der Mitte das [Struktogramm](#zeige-struktogramm):** Ein neues Diagramm enthält nur einen leeren Platzhalter, erkennbar an **ø**. -- **Unten der [Objektinspektor](#zeige-objektinspektor):** Hier änderst du den Text des ausgewählten Kastens, zum Beispiel \`mbot.forward(30)\` statt \`mbot.forward()\`. Die Linie mit dem Griff darüber kannst du nach oben oder unten ziehen. -- **Darüber die [Werkzeuge](#zeige-werkzeuge):** Rückgängig, Kopieren, Löschen, Exportieren und rechts die **Simulation**. - -So baust du: - -- **Einfügen:** Zieh ein Element aus den Elementen auf das Struktogramm. Auf den Platzhalter **ø** ersetzt es ihn. Auf die obere Hälfte eines Kastens kommt es davor, auf die untere Hälfte dahinter. Eine blaue Linie zeigt dir, wo es landet. -- **Text ändern:** Doppelklick auf einen Kasten, auf dem Tablet Doppeltipp. Dann tippst du direkt im Struktogramm, **Enter** übernimmt. -- **Verschieben:** einen Kasten an eine andere Stelle ziehen. -- **Löschen:** Kasten anklicken und Entf drücken oder auf den [Mülleimer](#zeige-loeschen) klicken. -- **Rückgängig:** Strg+Z, am Mac ⌘Z. - -> 🧩 **Lieber Blöcke wie in Scratch?** Klicke in den Werkzeugen auf [Blöcke](#zeige-bloecke). Dann siehst du dasselbe Programm als Blöcke, die du zusammensteckst. Zahlen tippst du direkt in die weißen Felder, Sensoren ziehst du hinein. Mit **Struktogramm** kommst du zurück. Es bleibt dasselbe Programm, du kannst jederzeit wechseln. - -::: übung vb-erster-plan -titel: Um die Ecke -auftrag: Baue ein Struktogramm: Der mBot2 fährt vor, dreht nach rechts, fährt vor, dreht nach links und fährt noch einmal vor. Starte danach die Simulation und schau zu. -fahrt: forward, turnRight, forward, turnLeft, forward -braucht: anweisung -tipp: Zieh **fahre vor** auf den Platzhalter ø. Die nächsten Befehle ziehst du jeweils auf die untere Hälfte des letzten Kastens. -tipp: Von oben nach unten: **fahre vor**, **drehe rechts**, **fahre vor**, **drehe links**, **fahre vor**. -lösung: vb-erster-plan-loesung.visustruct -::: - -> Bist du fertig, klickst du in der Übung auf **Prüfen**. VisuStruct lässt dein Struktogramm dann laufen und schaut nach, ob der mBot2 genau so fährt wie verlangt. Später prüft es dein Programm oft in mehreren Welten, zum Beispiel einmal mit Wand und einmal ohne. Klappt es nicht, helfen dir die **Tipps**. - -## Speichern und Öffnen - -- **[Speichern](#zeige-speichern)** (Strg+S, am Mac ⌘S): Das Struktogramm wird als Datei mit der Endung \`.visustruct\` gespeichert. Den Dateinamen schlägt der [Titel](#zeige-titel) über dem Struktogramm vor. -- **[Öffnen](#zeige-oeffnen)** (Strg+O, am Mac ⌘O): eine gespeicherte Datei wieder laden. - -In der Spalte [📁 Dateien](#zeige-dateien) richtest du einmal einen Ordner ein, deinen **Workspace**. Dort landen alle deine Programme und auch dein Lernstand. Den Ordner kannst du mitnehmen und zu Hause weitermachen. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektionen [„Was ist ein Struktogramm?“](?visustruct&lektion=01-was-ist-ein-struktogramm), [„Der Editor“](?visustruct&lektion=02-der-editor) und [„Speichern, Öffnen, Exportieren“](?visustruct&lektion=03-speichern-oeffnen-exportieren). Sie öffnen sich in einem neuen Fenster. - -## Spiel: Welcher Baustein? - -::: spiel frage -titel: Spiel: Welcher Baustein? -intro: Ludwig beschreibt, was sein mBot2 tun soll. Ist das eine Sequenz, eine Verzweigung oder eine Wiederholung? Sechs Runden. -antworten: Sequenz | Verzweigung | Wiederholung - -runde: -frage: Der mBot2 fährt vor, dreht sich nach rechts und fährt wieder vor. -richtig: Sequenz -> Drei Schritte nacheinander, jeder genau einmal. - -runde: -frage: Ist die Wand nah, dreht er sich. Sonst fährt er vor. -richtig: Verzweigung -> Je nach Bedingung passiert das eine oder das andere. - -runde: -frage: Er fährt viermal 10 cm vor. -richtig: Wiederholung -> Derselbe Schritt kommt mehrmals dran. - -runde: -frage: Solange keine Linie kommt, fährt er weiter. -richtig: Wiederholung -> „Solange“ heißt: immer wieder, bis die Bedingung nicht mehr stimmt. - -runde: -frage: Steht er auf Rot, piept er. -richtig: Verzweigung -> Nur wenn die Bedingung stimmt, piept er. - -runde: -frage: Er zeigt „Hallo“ auf dem Bildschirm und spielt danach einen Ton. -richtig: Sequenz -> Zwei Schritte nacheinander, ohne Bedingung und ohne Wiederholung. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Algorithmus und ein Struktogramm ist. → Ein Plan für den Roboter -- Ich kann die drei Grundbausteine Sequenz, Verzweigung und Wiederholung erkennen. → Drei Bausteine -- Ich kann im Editor Elemente einfügen, verschieben und löschen. → Der Editor -- Ich kann ein Struktogramm speichern und wieder öffnen. → Speichern und Öffnen -::: - -## Wissenstest - -::: quiz -? Was ist ein Algorithmus? -→ Ein Plan für den Roboter -- [ ] ein Bauteil des mBot2 -- [x] eine genaue Schritt-für-Schritt-Anleitung -- [ ] eine Programmiersprache -> Ein Algorithmus sagt genau, was in welcher Reihenfolge passiert. Das Struktogramm zeichnet ihn. -? Welcher Baustein entscheidet je nach Bedingung? -→ Drei Bausteine -- [ ] die Sequenz -- [x] die Verzweigung -- [ ] die Wiederholung -> Die Verzweigung hat oben ein Dreieck und zwei Spalten für true und false. -? Du ziehst **fahre vor** auf die untere Hälfte eines Kastens. Wo landet es? -→ Der Editor -- [ ] davor -- [x] dahinter -- [ ] es ersetzt den Kasten -> Obere Hälfte heißt davor, untere Hälfte dahinter. -? Welche Endung hat eine gespeicherte Struktogramm-Datei? -→ Speichern und Öffnen -- [ ] \`.java\` -- [x] \`.visustruct\` -- [ ] \`.png\` -> \`.png\` ist ein Bild zum Weitergeben. Weiterbearbeiten kannst du nur die \`.visustruct\`-Datei. -::: -`,y5=`--- -titel: Der mBot2 -bereich: Losfahren -kurz: Lerne den Roboter kennen, den du gleich programmierst. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Teile des mBot2 benennen -- erklären, wie der mBot2 Abstände misst -- erklären, wie der mBot2 eine Linie erkennt -- erklären, was Sensoren und Aktoren sind -- den mBot2 mit Elementen aus der Gruppe mBot2-Befehle steuern -- den mBot2 im Simulator fahren lassen -- den echten mBot2 per USB-C verbinden und wieder trennen -- prüfen, ob alle Sensoren und Aktoren funktionieren -::: - -## Hallo mBot2 - -::: mbot2 -fokus: -drehen: ja -::: - -Der **mBot2** ist ein kleiner Roboter der Firma Makeblock. In dieser Lektion machst du eine Tour durch alle seine Teile: - -- den [CyberPi]{teil=cyberpi}, den kleinen Computer hinten oben -- das durchsichtige [Shield mit Akku]{teil=shield} in der Mitte -- zwei [Motoren]{teil=motors} mit [Rädern]{teil=wheels} -- den [Ultraschallsensor]{teil=ultrasonic} vorn, der wie zwei Augen aussieht -- den [Liniensensor]{teil=lineSensor} unten an der Nase -- das türkise [Chassis]{teil=chassis}, das alles zusammenhält - -In VisuBot programmierst du ihn mit Struktogrammen. Jedes Programm kannst du zuerst im Simulator ausprobieren, auch ganz ohne echten Roboter. - -> Fahr mit der Maus über ein gelb unterstrichenes Wort, dann leuchtet das Teil im Modell auf. Tippst du darauf, fährt die Kamera hin. Das Modell kannst du mit der Maus oder dem Finger drehen und mit dem Mausrad heranzoomen. - -## Der CyberPi - -::: mbot2 -fokus: cyberpi, screen, joystick, buttons, leds, lightSensor, microphone -ansicht: hinten -::: - -Der [CyberPi]{teil=cyberpi,screen,joystick,buttons,leds,lightSensor,microphone} ist das Gehirn des mBot2: ein **Mikrocontroller**, also ein kleiner Computer ohne Tastatur. Er führt dein Programm aus. Er liegt quer hinten oben auf dem Roboter und lässt sich auch abnehmen. - -Auf ihm ist schon eine Menge eingebaut: - -- ein farbiger [Bildschirm]{teil=screen}, auf dem Text und Bilder erscheinen -- ein [Joystick]{teil=joystick}, den du in vier Richtungen drücken kannst -- zwei [Tasten]{teil=buttons}, eine mit Dreieck und eine mit Quadrat, und an der Seite neben dem USB-Anschluss die Home-Taste -- fünf bunte [LEDs]{teil=leds} als Lichtleiste an der Rückseite -- ein [Lichtsensor]{teil=lightSensor} und ein [Mikrofon]{teil=microphone} - -Dazu kommen ein Lautsprecher und ein Lagesensor, die du von außen nicht siehst. Der Lagesensor merkt, ob der CyberPi gekippt oder gedreht wird. - -> Das Modell ist nach Fotos nachgebaut. Schau dir deinen eigenen mBot2 an und vergleiche. - -## Bildschirm, LEDs und Lautsprecher - -::: mbot2 -fokus: screen, leds -ansicht: hinten -::: - -Mit dem [Bildschirm]{teil=screen} und den fünf [LEDs]{teil=leds} zeigt dir der mBot2, was er gerade tut. Jede LED kann in fast jeder Farbe leuchten, weil sie Rot, Grün und Blau mischt. Darum heißen sie **RGB-LEDs**. - -Später lässt du die LEDs mit \`mbot.setLed(255, 0, 0)\` rot leuchten. Die drei Zahlen sind Rot, Grün und Blau, jeweils von 0 bis 255. - -Im [CyberPi]{teil=cyberpi} steckt außerdem ein kleiner **Lautsprecher**. Mit \`mbot.playTone(440, 300)\` spielt er einen Ton: 440 Schwingungen pro Sekunde (Hertz), 300 Millisekunden lang. - -## Licht und Geräusche messen - -::: mbot2 -fokus: lightSensor, microphone -ansicht: oben -::: - -Der [Lichtsensor]{teil=lightSensor} misst, wie hell es ist. Das [Mikrofon]{teil=microphone} misst, wie laut es ist. So kann der mBot2 zum Beispiel losfahren, wenn du in die Hände klatschst. - -## Tasten und Joystick - -::: mbot2 -fokus: joystick, buttons -ansicht: oben -::: - -Die [Tasten]{teil=buttons} A und B und der [Joystick]{teil=joystick} sind auch Sensoren: Sie merken, ob du sie drückst. Dein Programm kann zum Beispiel warten, bis du A drückst, und erst dann losfahren. Mit dem Joystick lenkst du den mBot2 wie mit einer Fernbedienung. - -## Der Lagesensor - -::: mbot2 -fokus: cyberpi -drehen: ja -::: - -Im [CyberPi]{teil=cyberpi} steckt ein Sensor, den du nicht siehst: der **Lagesensor** (ein Gyroskop mit Beschleunigungssensor). Er merkt, ob der mBot2 nach vorn, nach hinten oder zur Seite geneigt ist, wie weit er sich gedreht hat und ob ihn jemand schüttelt. Dein Handy hat so einen Sensor auch: Darum dreht sich das Bild, wenn du es quer hältst. - -## Strom: das Shield mit Akku - -::: mbot2 -fokus: shield, powerSwitch -ansicht: seite -::: - -Das große durchsichtige Gehäuse ist das [mBot2-Shield]{teil=shield}. Durch den Deckel siehst du den grauen **Akku**, der den Roboter mit Strom versorgt. Hinten am Shield stecken die Kabel der Motoren (M1 und M2) und des Ultraschallsensors. - -Links an der Seite ist der [Ein-Aus-Schalter]{teil=powerSwitch} mit der Aufschrift ON/OFF. Daneben liegen zwei Buchsen S1 und S2 für kleine Servomotoren. Schalte den mBot2 aus, wenn du ihn nicht brauchst, dann hält der Akku länger. Geladen wird er über das USB-Kabel am CyberPi. - -## Motoren und Räder - -::: mbot2 -fokus: motors, wheels -ansicht: seite -::: - -Zwei [Motoren]{teil=motors} treiben die beiden [Räder]{teil=wheels} an, jeder sein eigenes. Du siehst sie von unten: silberne Motoren in hellblauen Getrieben. - -- Drehen sich beide Räder gleich schnell vorwärts, fährt der mBot2 **geradeaus**. -- Dreht sich ein Rad vorwärts und das andere rückwärts, dreht er sich **auf der Stelle**. - -Die Motoren sind **Encoder-Motoren**: Sie zählen mit, wie weit sich das Rad gedreht hat. Darum weiß der mBot2, wann er genau 10 cm gefahren ist oder sich genau um 90° gedreht hat. - -## Der Ultraschallsensor - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Vorn sitzt der schwarze [Ultraschallsensor]{teil=ultrasonic}. Er sieht aus wie zwei Augen, ist aber eher ein Ohr: Der eine Kreis schickt einen sehr hohen Ton los, den wir nicht hören. Der andere wartet auf das Echo. Je länger das Echo braucht, desto weiter ist die Wand weg. - -So misst der mBot2 Abstände in Zentimetern. Die Ringe um die Kreise können außerdem blau leuchten. - -::: angeberwissen -Fledermäuse machen es genau wie der Ultraschallsensor: Sie rufen so hoch, dass wir es nicht hören, und lauschen auf das Echo. Schall ist etwa 343 Meter pro Sekunde schnell. Steht eine Wand einen Meter vor dem mBot2, ist das Echo schon nach knapp 6 Tausendstel Sekunden zurück. -::: - -## Unter dem Roboter - -::: mbot2 -fokus: lineSensor, caster -ansicht: unten -::: - -Jetzt schaust du von unten. Ganz vorn schaut die schwarze Platine des [Liniensensors]{teil=lineSensor} heraus. Er hat **vier** kleine Fühler nebeneinander. Jeder leuchtet den Boden an und misst, wie viel Licht zurückkommt. Auf einer schwarzen Linie kommt wenig zurück, auf hellem Boden viel. So erkennt der mBot2, ob er auf einer Linie fährt. - -Der Sensor heißt **Quad-RGB-Sensor**, weil er noch mehr kann: Er erkennt auch **Farben**, zum Beispiel ein rotes oder grünes Feld auf dem Boden. - -Die [Kugelrolle]{teil=caster} dahinter ist das dritte Bein. Sie hat keinen Motor und rollt einfach in jede Richtung mit. - -## Das Chassis - -::: mbot2 -fokus: chassis -::: - -Das [Chassis]{teil=chassis} ist der türkise Rahmen aus Aluminium. Er hält alle Teile zusammen. Vorn hat er zwei runde „Ohren“, hinten ein großes **M** für Makeblock. Die vielen Löcher sind kein Zufall: Dort kannst du weitere Teile anschrauben. - -## Sensoren und Aktoren - -Ein Roboter hat zwei Arten von Teilen: - -- **Sensoren** messen etwas: den Abstand, eine Linie, eine Farbe, die Helligkeit, die Lautstärke, die Neigung oder ob eine Taste gedrückt ist. -- **Aktoren** tun etwas: Die Motoren bewegen die Räder, die LEDs leuchten, der Bildschirm zeigt etwas an, der Lautsprecher piept. - -Das Wort kommt vom lateinischen *agere*, „handeln“: Aktoren handeln, Sensoren (von *sentire*, „fühlen“) fühlen. - -Dein Programm liest die Sensoren und steuert die Aktoren. Das kennst du schon als Eingabe, Verarbeitung und Ausgabe. - -## Finde die Bauteile - -Jetzt bist du dran: Klicke im Modell auf das gesuchte Teil. Liegst du richtig, kommt die nächste Frage. - -::: mbot2quiz -- ultrasonic: Womit misst der mBot2, wie weit die Wand weg ist? -- screen: Wo erscheint Text, den dein Programm ausgibt? -- wheels: Was bewegt den mBot2 vorwärts? -- lineSensor: Was erkennt eine schwarze Linie auf dem Boden? (Tipp: Dreh das Modell.) -- powerSwitch: Wo schaltest du den mBot2 ein und aus? -- leds: Was leuchtet bei \`mbot.setLed(255, 0, 0)\` rot? -fertig: Super, du kennst dich am mBot2 aus! -::: - -## Befehle für den mBot2 - -Unter den [Elementen](#zeige-elemente) gibt es in VisuBot eine eigene Gruppe **mBot2-Befehle**. Jeder Kasten darin ist eine Anweisung an den Roboter: - -| Element | Text im Kasten | Was passiert | -|---|---|---| -| fahre vor | \`mbot.forward()\` | 10 cm vorwärts | -| fahre zurück | \`mbot.backward()\` | 10 cm rückwärts | -| drehe rechts | \`mbot.turnRight()\` | 90° nach rechts drehen | -| drehe links | \`mbot.turnLeft()\` | 90° nach links drehen | -| LEDs an | \`mbot.setLed(255, 0, 0)\` | die LEDs leuchten rot | - -In die Klammern darfst du auch eine Zahl schreiben: \`mbot.forward(30)\` fährt 30 cm, \`mbot.turnRight(45)\` dreht nur 45°. - -> Die Anweisungen sind echtes Java. \`mbot\` ist der Roboter, hinter dem Punkt steht, was er tun soll. - -## Erste Fahrt - -Starte nach dem Bauen die [Simulation](#zeige-simulation). Rechts erscheint die VisuBot-Welt mit dem mBot2. Das Gitter auf dem Boden hat Kästchen von 10 cm, also genau ein **fahre vor**. - -::: übung vb-gerade -titel: Geradeaus -auftrag: Ziehe fünfmal **fahre vor** in das Struktogramm. Starte dann die Simulation und schau zu, wie der mBot2 50 cm geradeaus fährt. -fahrt: 5 x (forward) -tipp: Die Gruppe mBot2-Befehle findest du unter den normalen Elementen. -tipp: Fünf Kästen \`mbot.forward()\` untereinander. Jeder fährt 10 cm. -lösung: vb-gerade-loesung.visustruct -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - mbot.forward(60); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,0 -> Zwei Felder nach vorn, nach rechts drehen, ein Feld weiter. - -runde: - mbot.forward(30); - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,1 -> Ein Feld nach vorn, dann schaut er nach links und fährt zwei Felder. - -runde: - mbot.turnRight(); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Zweimal rechts drehen heißt: Er schaut nach hinten. Dann fährt er ein Feld zurück, nach unten. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: .... | .... | .^.. -ziel: 2,2 -> Hoch, nach rechts, und nach der zweiten Drehung schaut er nach unten. -::: - -## Den echten mBot2 verbinden - -::: mbot2 -fokus: cyberpi, powerSwitch, joystick, buttons -ansicht: seite -::: - -Alles, was du im Simulator baust, kann auch der echte mBot2 fahren. So verbindest du ihn: - -1. Steck ein **USB-C-Kabel** in deinen Computer (zum Beispiel ein MacBook) und das andere Ende in den USB-C-Anschluss rechts am [CyberPi]{teil=cyberpi}, neben der Home-Taste. -2. Schieb den [Schiebeschalter ON/OFF]{teil=powerSwitch} links am Shield auf ON. Der Bildschirm des CyberPi leuchtet. -3. Auf dem Bildschirm erscheint ein Menü, in dem **neu starten** markiert ist. Drück den [Joystick]{teil=joystick} wie eine Taste nach unten, um das zu bestätigen. -4. Nach dem Neustart steht dort „Press B to start“. Drück **Taste B**, das ist die [Taste mit dem Dreieck]{teil=buttons}. -5. Öffne VisuStruct in **Chrome** oder **Edge**. Safari und das iPad können keine USB-Geräte ansprechen. -6. Sobald der CyberPi hochgefahren ist, klick oben auf **🔌 mBot2** (oder im Zahnrad auf „Mit mBot2 verbinden“). -7. Chrome zeigt eine Liste mit Anschlüssen. Wähle den Eintrag **USB Serial** (zum Beispiel „USB Serial (cu.usbserial-110)“). Er steht oft ganz unten, eventuell musst du nach unten scrollen. Dann klick auf **Verbinden**. -8. Der CyberPi startet kurz neu (⏳). Dann steht „Hallo VisuBot!“ auf seinem Bildschirm und der Knopf zeigt 🟢. - -Jetzt fährt der mBot2 bei jedem Schritt der Simulation mit. Stell ihn auf den Boden und lass ihm Platz! - -> Fertig? Klick noch einmal auf **🟢 mBot2**. Dann hält er an, zeigt „Bis bald!“ und gibt den Anschluss frei. Erst danach das Kabel ziehen. - -> Ganz ohne Kabel fahren? Das lernst du in Lektion 2 bei **Ohne Kabel fahren**. - -## Den mBot2 testen - -Ist der mBot2 verbunden, erscheint unter der Welt **🔧 mBot2 testen**. Klapp es auf: - -- Mit den Knöpfen probierst du jeden Aktor einmal aus: LEDs, Ton, Text auf dem Bildschirm und die Räder. Heb den mBot2 dafür hoch, damit er nicht vom Tisch fährt. -- Darunter stehen alle Sensoren mit ihren Werten, und die laufen live mit. Halte die Hand vor den Ultraschallsensor, drück Taste A, neige ihn, klatsch in die Hände oder stell ihn auf ein rotes Blatt. - -Ändert sich ein Wert nicht, obwohl du etwas tust, stimmt etwas nicht. Dann sag deiner Lehrkraft Bescheid, bevor du programmierst. - -## Spiel: Sensor oder Aktor? - -::: spiel frage -titel: Spiel: Sensor oder Aktor? -intro: Misst das Bauteil etwas, oder tut es etwas? Zehn Runden. -antworten: Sensor | Aktor - -runde: -frage: Der Ultraschallsensor misst, wie weit die Wand weg ist. -richtig: Sensor -> Er misst etwas: den Abstand. - -runde: -frage: Die Motoren drehen die Räder. -richtig: Aktor -> Sie tun etwas: Sie bewegen den mBot2. - -runde: -frage: Die LEDs leuchten rot. -richtig: Aktor -> Leuchten ist etwas tun, nicht messen. - -runde: -frage: Der Liniensensor erkennt das schwarze Klebeband. -richtig: Sensor -> Er misst, wie viel Licht vom Boden zurückkommt. - -runde: -frage: Der Lautsprecher piept. -richtig: Aktor -> Er gibt einen Ton aus. - -runde: -frage: Das Mikrofon hört dein Klatschen. -richtig: Sensor -> Es misst die Lautstärke. - -runde: -frage: Der Bildschirm zeigt „Hallo VisuBot!“. -richtig: Aktor -> Anzeigen ist eine Ausgabe. - -runde: -frage: Taste A merkt, dass du drückst. -richtig: Sensor -> Tasten sind Sensoren: Sie melden, ob sie gedrückt sind. - -runde: -frage: Der Lagesensor merkt, dass der mBot2 schief steht. -richtig: Sensor -> Er misst die Neigung. - -runde: -frage: Die Ringe am Ultraschallsensor leuchten blau. -richtig: Aktor -> Gemein! Das Bauteil ist ein Sensor, aber das Leuchten der Ringe ist eine Ausgabe. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Teile des mBot2 benennen. → Hallo mBot2 -- Ich kann erklären, wie der mBot2 Abstände misst. → Der Ultraschallsensor -- Ich kann erklären, wie der mBot2 eine Linie erkennt. → Unter dem Roboter -- Ich kann erklären, was Sensoren und Aktoren sind. → Sensoren und Aktoren -- Ich kann den mBot2 mit Elementen aus der Gruppe mBot2-Befehle steuern. → Befehle für den mBot2 -- Ich kann den mBot2 im Simulator fahren lassen. → Erste Fahrt -- Ich kann den echten mBot2 per USB-C verbinden und wieder trennen. → Den echten mBot2 verbinden -- Ich kann prüfen, ob alle Sensoren und Aktoren funktionieren. → Den mBot2 testen -::: - -## Wissenstest - -::: quiz -? Ordne jedes Teil seiner Aufgabe zu. -→ Hallo mBot2 -= Ultraschallsensor -> misst den Abstand -= Motoren -> bewegen die Räder -= CyberPi -> führt das Programm aus -= Liniensensor -> erkennt eine dunkle Linie -? Welche Teile sind Aktoren? -→ Sensoren und Aktoren -- [x] Motoren -- [ ] Ultraschallsensor -- [x] LEDs -- [ ] Liniensensor -> Aktoren tun etwas, Sensoren messen etwas. -? Wie misst der Ultraschallsensor den Abstand? -→ Der Ultraschallsensor -- [ ] Er fotografiert die Wand. -- [x] Er schickt einen sehr hohen Ton los und wartet auf das Echo. -- [ ] Er tastet die Wand mit seinen blauen Ringen ab. -> Je länger das Echo braucht, desto weiter ist die Wand weg. -? Woran erkennt der Liniensensor eine schwarze Linie? -→ Unter dem Roboter -- [x] Von der schwarzen Linie kommt wenig Licht zurück. -- [ ] Die Linie ist wärmer als der Boden. -- [ ] Er hört, wie die Räder über die Linie rollen. -> Jeder Fühler leuchtet den Boden an. Heller Boden wirft viel Licht zurück, eine schwarze Linie wenig. -? Was tut \`mbot.turnRight()\`? -→ Befehle für den mBot2 -- [ ] Der mBot2 fährt 10 cm nach rechts. -- [x] Der mBot2 dreht sich um 90° nach rechts. -- [ ] Die rechte LED geht an. -> Ohne Zahl in den Klammern dreht er sich um 90°. \`mbot.turnRight(45)\` dreht nur 45°. -? Im Struktogramm stehen fünf Kästen \`mbot.forward()\` untereinander. Wie weit fährt der mBot2 in der Simulation? -→ Erste Fahrt -- [ ] 5 cm -- [x] 50 cm -- [ ] 10 cm -> Jedes \`mbot.forward()\` fährt 10 cm, also ein Kästchen im Gitter. -? Du bist mit dem echten mBot2 fertig. Was machst du, bevor du das Kabel ziehst? -→ Den echten mBot2 verbinden -- [x] Noch einmal auf **🟢 mBot2** klicken und warten, bis „Bis bald!“ dasteht -- [ ] Den Schiebeschalter auf OFF schieben -- [ ] Nichts, das Kabel darf sofort raus -> Erst nach „Bis bald!“ ist der Anschluss frei. -? Du hältst die Hand vor den Ultraschallsensor, aber unter **🔧 mBot2 testen** ändert sich der Wert nicht. Was tust du? -→ Den mBot2 testen -- [ ] Trotzdem losprogrammieren -- [x] Der Lehrkraft Bescheid sagen, bevor du programmierst -- [ ] Den Wert in der Simulation von Hand ändern -> Ändert sich ein Wert nicht, obwohl du etwas tust, stimmt am Roboter etwas nicht. -::: -`,z5=`--- -titel: Das Quadrat -bereich: Losfahren -kurz: Vom langen Struktogramm zur dreifach verschachtelten Schleife. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen Ablauf erst ohne Schleife bauen -- Wiederholungen erkennen und durch eine For-Schleife ersetzen -- zwei Schleifen ineinander schachteln -- drei Schleifen ineinander schachteln -- mein Programm auf den mBot2 übertragen und ohne Kabel mit Taste A starten -::: - -## Ein Quadrat fahren - -Der mBot2 soll ein Quadrat mit 50 cm Seitenlänge fahren. Eine Seite kennst du schon: fünfmal **fahre vor**. Danach dreht er sich nach rechts, und die nächste Seite beginnt. - -::: übung vb-quadrat -titel: Das Quadrat -auftrag: Baue ein Struktogramm, mit dem der mBot2 ein Quadrat fährt: fünfmal **fahre vor**, dann **drehe rechts**. Und das viermal. -fahrt: 4 x (5 x (forward), turnRight) -tipp: Eine Seite besteht aus fünfmal fahre vor und einmal drehe rechts. -tipp: Insgesamt sind es 24 Kästen: viermal fünf \`mbot.forward()\` und danach jeweils \`mbot.turnRight()\`. -lösung: vb-quadrat-loesung.visustruct -::: - -Ganz schön viele Kästen, oder? Wenn du dich beim Zählen vertust, fährt der mBot2 kein Quadrat mehr. - -## Immer das Gleiche - -Schau dein Struktogramm genau an: Fünfmal **fahre vor** steht viermal da. Wenn sich etwas wiederholt, nimmt man eine **Schleife**. Wir wissen genau, wie oft gefahren wird, nämlich fünfmal. Dafür passt die For-Schleife. - -Ziehe eine For-Schleife ins Struktogramm und gib ihr diese drei Teile: - -- Start: \`int i = 0\` -- Bedingung: \`i < 5\` -- Schritt: \`i++\` - -In den Inhalt der Schleife kommt ein einziges **fahre vor**. Die Schleife zählt \`i\` von 0 bis 4, das sind fünf Durchläufe. - -\`i\` ist ein **Zähler**. Der Computer merkt sich darin, im wievielten Durchlauf er gerade ist. So einen Speicher mit Namen nennt man **Variable**, genauer lernst du sie in Lektion 3 kennen. Die drei Teile bedeuten: - -| Teil | Bedeutung | -|---|---| -| \`int i = 0\` | Der Zähler \`i\` ist eine ganze Zahl (\`int\`) und fängt bei 0 an. | -| \`i < 5\` | Solange \`i\` kleiner als 5 ist, läuft die Schleife noch einmal. | -| \`i++\` | Nach jedem Durchlauf wird \`i\` um 1 größer. | - -So zählt die Schleife: - -| Durchlauf | 1 | 2 | 3 | 4 | 5 | danach | -|---|---|---|---|---|---|---| -| \`i\` | 0 | 1 | 2 | 3 | 4 | 5 | -| \`i < 5\`? | true | true | true | true | true | false, fertig | - -::: übung vb-quadrat-schleife -titel: Seiten mit Schleife -auftrag: Ersetze in deinem Quadrat jede Reihe aus fünf **fahre vor** durch eine For-Schleife, die fünfmal fährt. Der mBot2 fährt danach genau dasselbe Quadrat. -fahrt: 4 x (5 x (forward), turnRight) -braucht: forSchleife -tipp: Start \`int i = 0\`, Bedingung \`i < 5\`, Schritt \`i++\`. Im Inhalt steht nur \`mbot.forward()\`. -tipp: Viermal untereinander: die For-Schleife und danach \`mbot.turnRight()\`. -lösung: vb-quadrat-schleife-loesung.visustruct -::: - -## Schleife in der Schleife - -Jetzt steht viermal dasselbe Paar da: eine For-Schleife und **drehe rechts**. Auch das ist eine Wiederholung! Packe beides in eine zweite For-Schleife, die viermal läuft. Eine Schleife in einer Schleife heißt **verschachtelte Schleife**. - -Jede Schleife braucht ihre eigene Zählvariable, sonst kommen sich die beiden in die Quere. Üblich ist: Die äußere Schleife zählt mit \`i\`, die innere mit \`j\`. Benenne deine innere Schleife also in \`j\` um. - -::: übung vb-quadrat-verschachtelt -titel: Quadrat verschachtelt -auftrag: Baue das Quadrat mit zwei ineinander geschachtelten For-Schleifen. Die äußere läuft viermal, einmal für jede Seite. Die innere läuft fünfmal, einmal für jeden Schritt. -fahrt: 4 x (5 x (forward), turnRight) -schleifentiefe: 2 -tipp: Außen: Start \`int i = 0\`, Bedingung \`i < 4\`, Schritt \`i++\`. Innen: \`int j = 0\`, \`j < 5\`, \`j++\`. -tipp: In die äußere Schleife kommen die innere Schleife mit \`mbot.forward()\` und darunter \`mbot.turnRight()\`. -lösung: vb-quadrat-verschachtelt-loesung.visustruct -::: - -Aus 24 Kästen sind vier geworden. Gehe das Programm in der Simulation mit **Schritt** durch und beobachte die Variablen \`i\` und \`j\`. - -::: quiz -? Wie oft führt der mBot2 im verschachtelten Quadrat \`mbot.forward()\` aus? -- [ ] 9-mal -- [x] 20-mal -- [ ] 5-mal -> Die innere Schleife läuft fünfmal, und das in jedem der vier Durchläufe der äußeren Schleife: 4 · 5 = 20. -::: - -## Vier Quadrate - -Jetzt wird es richtig schön: Der mBot2 soll vier Quadrate fahren. Nach jedem Quadrat dreht er sich einmal nach rechts. So entsteht ein großes Fenster mit Fensterkreuz. - -Du könntest dein Quadrat viermal untereinander kopieren und nach jedem Quadrat ein **drehe rechts** einfügen. Probier es ruhig aus! Merkst du es? Schon wieder steht dasselbe viermal da. - -::: ludwig -Immer wenn du Kästen kopierst, frag dich: Wiederholt sich hier etwas? Dann passt eine Schleife. -::: - -::: übung vb-vier-quadrate -titel: Vier Quadrate -auftrag: Lass den mBot2 vier Quadrate fahren. Nach jedem Quadrat dreht er sich einmal nach rechts. Benutze drei ineinander geschachtelte Schleifen. -fahrt: 4 x (4 x (5 x (forward), turnRight), turnRight) -schleifentiefe: 3 -tipp: Lege um dein verschachteltes Quadrat noch eine dritte For-Schleife, die viermal läuft. -tipp: Ganz außen zählt jetzt \`i\` (\`int i = 0\`, \`i < 4\`, \`i++\`). Die Schleifen darin heißen dann \`j\` und \`k\`. In die äußere Schleife kommen dein Quadrat und danach \`mbot.turnRight()\`. -lösung: vb-vier-quadrate-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektionen [„For-Schleife“](?visustruct&lektion=13-for-schleife) und [„Verschachteln“](?visustruct&lektion=14-verschachteln). Sie öffnen sich in einem neuen Fenster. - -::: quiz -? Wie oft fährt der mBot2 bei den vier Quadraten 10 cm vor? -→ Vier Quadrate -- [ ] 20-mal -- [ ] 60-mal -- [x] 80-mal -> 4 Quadrate · 4 Seiten · 5 Schritte = 80. Ohne Schleifen wären das 80 Kästen. -::: - -## Ohne Kabel fahren - -::: mbot2 -fokus: cyberpi, buttons -ansicht: oben -::: - -Bisher hängt der mBot2 am Kabel, solange er fährt. Du kannst dein Programm aber auch **auf den mBot2 übertragen**. Dann merkt er es sich und fährt ganz allein, zum Beispiel durch ein Labyrinth auf dem Boden. - -1. Schließ den mBot2 mit dem USB-Kabel an und schalte ihn ein, wie in Lektion 1. Das geht nur in **Chrome** oder **Edge**. -2. Öffne dein Programm, zum Beispiel die vier Quadrate. -3. Klick oben auf **⬆ Übertragen** und dann auf **Jetzt übertragen**. Wähle wieder **USB Serial**. -4. Warte, bis „Fertig!“ dasteht. Der mBot2 startet neu und zeigt **A = Start**. -5. Zieh das Kabel ab und setz den mBot2 an den Start. -6. Drück **Taste A**, das ist die [Taste mit dem Quadrat]{teil=buttons}. Jetzt fährt dein Programm los. - -Das Programm bleibt gespeichert, auch wenn du den mBot2 aus- und wieder einschaltest. Erst das nächste Übertragen ersetzt es. - -> Fährt er los, bevor du bereit bist? Keine Sorge: Er wartet immer auf Taste A. Drückst du A noch einmal, während er fährt, passiert nichts. Ist er fertig, startet A das Programm von vorn. - -## Spiel: Wie oft fährt er? - -::: spiel frage -titel: Spiel: Wie oft fährt er? -intro: Zähle mit, ohne die Simulation zu starten. Wie oft fährt oder dreht sich der mBot2? Sechs Runden. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(); - mbot.turnRight(); - } -frage: Wie oft fährt der mBot2 vor? -- 3-mal -- [x] 4-mal -- 5-mal -> \`i\` zählt 0, 1, 2 und 3. Das sind vier Durchläufe. - -runde: - for (int i = 1; i <= 4; i++) { - mbot.forward(); - } -frage: Wie oft fährt der mBot2 vor? -- 3-mal -- [x] 4-mal -- 5-mal -> Diesmal zählt \`i\` von 1 bis 4, wegen \`<=\`. Auch das sind vier Durchläufe. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Wie oft fährt der mBot2 vor? -- 8-mal -- [x] 15-mal -- 5-mal -> Die innere Schleife läuft in jedem der drei äußeren Durchläufe fünfmal: 3 · 5 = 15. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Wie oft dreht sich der mBot2 nach rechts? -- [x] 3-mal -- 15-mal -- 5-mal -> \`mbot.turnRight()\` steht in der äußeren Schleife, nicht in der inneren. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - } - mbot.turnRight(); -frage: Fährt der mBot2 ein Quadrat? -- Ja, mit 50 cm Seitenlänge. -- [x] Nein, er fährt 200 cm geradeaus und dreht sich erst am Ende. -- Ja, aber nur ein halbes. -> \`mbot.turnRight()\` steht unter der Schleife. Es läuft erst, wenn die Schleife fertig ist. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - mbot.forward(30); - mbot.turnRight(); - } - mbot.turnRight(); - } -frage: Wie viele Quadrate fährt der mBot2? -- [x] 2 -- 4 -- 8 -> Die innere Schleife fährt ein Quadrat. Die äußere wiederholt das zweimal. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 fahren soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(50); - mbot.turnRight(); - } -frage: Soll ein Quadrat mit 50 cm Seitenlänge fahren. -fehler: 1 -besser: for (int i = 0; i < 4; i++) { -> Ein Quadrat hat vier Seiten. Mit \`i < 3\` fährt er nur drei. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(180); - } -frage: Soll ein Quadrat mit 50 cm Seitenlänge fahren. -fehler: 3 -besser: mbot.turnRight(); -> Mit 180° kehrt er um und fährt immer hin und her. Ein Quadrat braucht 90°. - -runde: - for (int i = 0; i < 4; i++) { - for (int i = 0; i < 5; i++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Soll ein Quadrat aus viermal fünf Schritten fahren. -fehler: 2 -besser: for (int j = 0; j < 5; j++) { -> Die innere Schleife braucht ihre eigene Zählvariable \`j\`. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnLeft(45); - } -frage: Soll ein Quadrat mit 30 cm Seitenlänge fahren. -fehler: 3 -besser: mbot.turnLeft(); -> Ein Quadrat braucht rechte Winkel, also 90°. Ohne Zahl dreht er sich 90°. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..... | ..^.. -ziel: 2,0 -> Dreimal ein Feld nach vorn. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 1,2 -> Ein ganzes Quadrat: Er kommt genau dort an, wo er losgefahren ist. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 2,2 -> Nur drei Seiten: Die vierte fehlt, also steht er rechts neben dem Start. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - mbot.forward(30); - } - mbot.turnRight(); - } -karte: .... | .... | .... | ^... -ziel: 2,1 -> Die innere Schleife fährt zwei Felder, dann dreht er sich. Das Ganze zweimal: zwei Felder hoch, zwei nach rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann einen Ablauf erst ohne Schleife bauen. → Ein Quadrat fahren -- Ich kann Wiederholungen erkennen und durch eine For-Schleife ersetzen. → Immer das Gleiche -- Ich kann zwei Schleifen ineinander schachteln. → Schleife in der Schleife -- Ich kann drei Schleifen ineinander schachteln. → Vier Quadrate -- Ich kann mein Programm auf den mBot2 übertragen und ohne Kabel mit Taste A starten. → Ohne Kabel fahren -::: - -## Wissenstest - -::: quiz -? Der mBot2 soll noch ohne Schleife eine Seite von 50 cm fahren und sich dann drehen. Welche Kästen brauchst du? -→ Ein Quadrat fahren -- [ ] einmal \`mbot.forward()\` und einmal \`mbot.turnRight()\` -- [x] fünfmal \`mbot.forward()\` und einmal \`mbot.turnRight()\` -- [ ] fünfmal \`mbot.turnRight()\` -> Jedes \`mbot.forward()\` fährt 10 cm, fünf davon sind 50 cm. -? Eine For-Schleife hat den Start \`int i = 0\`, die Bedingung \`i < 5\` und den Schritt \`i++\`. Wie oft läuft sie? -→ Immer das Gleiche -- [ ] 4-mal -- [x] 5-mal -- [ ] 6-mal -> \`i\` zählt 0, 1, 2, 3, 4. Das sind fünf Durchläufe. -? Warum heißt die Zählvariable der inneren Schleife \`j\` und nicht auch \`i\`? -→ Schleife in der Schleife -- [ ] In einem Programm darf jeder Buchstabe nur einmal vorkommen. -- [x] Jede Schleife braucht ihre eigene Zählvariable, sonst kommen sich die beiden in die Quere. -- [ ] \`j\` zählt schneller als \`i\`. -> Würden beide mit \`i\` zählen, würde die innere Schleife die äußere durcheinanderbringen. -? Wie oft führt der mBot2 bei den vier Quadraten \`mbot.turnRight()\` aus? -→ Vier Quadrate -- [ ] 4-mal -- [ ] 16-mal -- [x] 20-mal -> In jedem Quadrat dreht er sich viermal, das sind 4 · 4 = 16. Dazu kommt nach jedem Quadrat noch eine Drehung: 16 + 4 = 20. -? Dein Programm ist übertragen und das Kabel ist ab. Wie startest du es? -→ Ohne Kabel fahren -- [ ] mit Taste B -- [x] mit Taste A -- [ ] mit **⬆ Übertragen** -> Nach dem Übertragen zeigt der mBot2 **A = Start** und wartet auf Taste A. -::: -`,I5=`--- -titel: PAP und Pseudocode -bereich: Losfahren -kurz: Den Plan für das Quadrat als Programmablaufplan und als Pseudocode aufschreiben und mit dem Struktogramm vergleichen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- einen PAP (englisch *flowchart*) für den mBot2 lesen und selbst zusammenbauen -- einen Plan als Pseudocode lesen -- Struktogramm, PAP und Pseudocode vergleichen -::: - -## Drei Arten, einen Plan aufzuschreiben - -Den Plan für das Quadrat kennst du als **Struktogramm**: eine Schleife, die viermal läuft, darin eine Seite fahren und rechts drehen. - -Es gibt noch zwei weitere Arten, denselben Plan aufzuschreiben. Beide wirst du im Unterricht und im Internet oft treffen: - -- den **Programmablaufplan**, kurz **PAP**, auf Englisch **flowchart**, auf Deutsch auch Flussdiagramm, -- den **Pseudocode**, einen Plan aus Text in normaler Sprache. - -Den mBot2 kannst du damit nicht direkt steuern. Aber mit allen drei kannst du einen Plan aufschreiben, bevor du ihn als Struktogramm oder in Java baust. - -## Der PAP - -Ein PAP besteht aus wenigen Symbolen, die mit **Pfeilen** verbunden sind: - -- **Oval:** Start oder Ende. -- **Rechteck:** eine Anweisung, zum Beispiel *50 cm vorwärts fahren*. -- **Raute:** eine Frage mit zwei Ausgängen: **ja** und **nein**. -- **Parallelogramm:** eine Ein- oder Ausgabe, zum Beispiel ein Text auf dem Bildschirm. -- **Pfeil:** zeigt, welcher Schritt als Nächstes kommt. - -Eine Schleife hat im PAP kein eigenes Symbol. Du erkennst sie an einem Pfeil, der **zurück nach oben** führt. So lange die Antwort in der Raute **nein** ist, geht es zurück, und die Schritte laufen noch einmal. - -> Vorsicht: Ein Pfeil darf im PAP überallhin zeigen, auch kreuz und quer. Bei großen Plänen wird das schnell unübersichtlich. Das nennt man **Spaghetticode**. Im Struktogramm kann das nicht passieren, weil es keine Pfeile gibt. - -## Übung: Das Quadrat als PAP - -Der mBot2 fährt eine Seite, dreht sich rechts und zählt die Seite mit. Nach vier Seiten zeigt er auf dem Bildschirm „Quadrat fertig!“. - -::: pap -titel: Übung: Das Quadrat als PAP -auftrag: Die Plätze im Plan sind leer. Lege jedes Element an seinen Platz. Die Form des Platzes hilft dir. -start: Start -anweisung: 50 cm vorwärts fahren -anweisung: rechts drehen -anweisung: Seite mitzählen -frage: 4 Seiten gefahren? | nein -> 50 cm vorwärts fahren -ausgabe: „Quadrat fertig!“ anzeigen -ende: Ende -erklärung: Solange noch keine vier Seiten gefahren sind, führt der Pfeil von der Raute zurück zu „50 cm vorwärts fahren“. Das ist die Schleife. Die Anzeige auf dem Bildschirm ist eine Ausgabe und steht deshalb im Parallelogramm. -legende: ja -::: - -**Eigene PAPs zeichnen:** Mit **VisuFlow** zeichnest du deinen PAP ganz leicht selbst, zum Beispiel für deine nächste Roboterfahrt. Die Symbole liegen fertig in einer Leiste: Antippen hängt das nächste Symbol an und verbindet es gleich mit einem Pfeil. Deinen PAP speicherst du als Datei und exportierst ihn als Bild oder PDF, zum Beispiel für dein Heft oder eine Präsentation. [VisuFlow öffnen](https://visustruct.visucode.de/app/?visuflow) - -## Pseudocode - -**Pseudocode** ist Text, der wie ein Programm aufgebaut ist, aber in normaler Sprache geschrieben wird. Feste Wörter wie WIEDERHOLE, WENN und SOLANGE zeigen die Bausteine, die **Einrückung** zeigt, was dazugehört. Das Quadrat sieht so aus: - -\`\`\` -WIEDERHOLE 4-mal - 50 cm vorwärts fahren - rechts drehen -ENDE WIEDERHOLE -"Quadrat fertig!" anzeigen -\`\`\` - -Und so sieht derselbe Plan in Java aus: - -\`\`\`java -for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(); -} -mbot.showText("Quadrat fertig!"); -\`\`\` - -Pseudocode hat **keine feste Schreibweise**. Manche schreiben FÜR i VON 1 BIS 4, andere WIEDERHOLE 4-mal. Wichtig ist nur, dass jeder Mensch ihn ohne Erklärung versteht. Den Computer kannst du damit nicht steuern, dafür brauchst du Java. - -### Im Vergleich - -- **Struktogramm:** Kästen ineinander, die Schleife ist ein gekipptes L. Die Struktur siehst du auf einen Blick. -- **PAP:** Symbole mit Pfeilen, die Schleife ist ein Pfeil zurück nach oben. Sehr anschaulich, aber bei großen Plänen unübersichtlich. -- **Pseudocode:** Text mit Einrückung, die Schleife steht zwischen WIEDERHOLE und ENDE WIEDERHOLE. Schnell geschrieben und schon nah an Java. - -::: angeberwissen -Määäh, wer hat die Ablaufpläne erfunden? Eine Frau und ihr Mann: **Lillian und Frank Gilbreth** stellten 1921 ihre „Process Charts“ vor, lange vor dem ersten Computer. Damit zeichneten sie Arbeitsabläufe in Fabriken auf, um sie einfacher zu machen. Lillian Gilbreth hatte einen Doktortitel in Psychologie und wurde 1965 als erste Frau in die amerikanische Akademie der Ingenieure gewählt. Programmierer haben die Idee später für ihre Pläne übernommen. -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„PAP und Pseudocode“](?visustruct&lektion=01-weitere-darstellungen) mit Kuchenbacken als PAP und dem Befehl GOTO. Sie öffnet sich in einem neuen Fenster. - -## Spiel: Welche Darstellung? - -::: spiel frage -titel: Spiel: Welche Darstellung? -intro: Ludwig beschreibt einen Plan für den mBot2, ohne zu sagen, wie er aufgeschrieben ist. Ist es das Struktogramm, der PAP oder der Pseudocode? Sechs Runden. -antworten: Struktogramm | PAP | Pseudocode - -runde: -frage: „Ein Pfeil führt von der Raute zurück zu ‚50 cm vorwärts fahren‘.“ -richtig: PAP -> Eine Schleife im PAP erkennst du am Pfeil zurück nach oben. - -runde: -frage: „Die Schleife sieht aus wie ein gekipptes L, darin stehen fahre vor und drehe rechts.“ -richtig: Struktogramm -> Das gekippte L ist die Schleife im Struktogramm. - -runde: -frage: „WIEDERHOLE 4-mal, darunter eingerückt zwei Zeilen.“ -richtig: Pseudocode -> Feste Wörter und Einrückung: Das ist Pseudocode. - -runde: -frage: „‚Quadrat fertig!‘ steht in einem schrägen Kasten.“ -richtig: PAP -> Der schräge Kasten ist das Parallelogramm für Ein- und Ausgabe. - -runde: -frage: „Es gibt keine Pfeile. Fügst du einen Kasten ein, rutscht alles darunter einfach mit.“ -richtig: Struktogramm -> Ohne Pfeile kann im Struktogramm kein Sprung auf die falsche Stelle zeigen. - -runde: -frage: „Manche schreiben FÜR i VON 1 BIS 4, andere WIEDERHOLE 4-mal. Beides ist richtig.“ -richtig: Pseudocode -> Pseudocode hat keine feste Schreibweise. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Elemente eines PAP benennen. → Der PAP -- Ich kann das Quadrat selbst als PAP zusammenbauen. → Übung: Das Quadrat als PAP -- Ich kann einen Plan als Pseudocode lesen. → Pseudocode -- Ich kann Struktogramm, PAP und Pseudocode vergleichen. → Drei Arten, einen Plan aufzuschreiben -::: - -## Wissenstest - -::: quiz -? Woran erkennst du im PAP eine Schleife? -→ Der PAP -- [ ] An einem gekippten L -- [x] An einem Pfeil, der zurück nach oben führt -- [ ] Am Wort WIEDERHOLE -> Das gekippte L gehört zum Struktogramm, WIEDERHOLE zum Pseudocode. - -? Ordne jedem Symbol im PAP seine Bedeutung zu. -→ Der PAP -= Oval -> Start oder Ende -= Rechteck -> Anweisung -= Raute -> Frage mit ja und nein -= Parallelogramm -> Ein- oder Ausgabe -> „50 cm vorwärts fahren“ ist eine Anweisung, „Quadrat fertig!“ anzeigen eine Ausgabe. - -? Im Quadrat-PAP steht „4 Seiten gefahren?“ in der Raute. Wohin führt der Pfeil bei **nein**? -→ Übung: Das Quadrat als PAP -- [x] Zurück zu „50 cm vorwärts fahren“ -- [ ] Zum Ende -- [ ] Zu „Quadrat fertig!“ anzeigen -> Bei nein ist das Quadrat noch nicht fertig, also fährt der mBot2 die nächste Seite. - -? Was zeigt im Pseudocode, welche Zeilen zur Schleife gehören? -→ Pseudocode -- [ ] Ein Pfeil -- [x] Die Einrückung und das ENDE WIEDERHOLE -- [ ] Die Farbe -> Alles zwischen WIEDERHOLE und ENDE WIEDERHOLE ist eingerückt und wird wiederholt. - -? Kann der mBot2 Pseudocode direkt ausführen? -→ Drei Arten, einen Plan aufzuschreiben -- [ ] Ja, wie Java -- [x] Nein, Pseudocode ist nur ein Plan für Menschen -> Ausführen kann der mBot2 das Struktogramm oder Java. Pseudocode hilft beim Planen. -::: -`,k5=`--- -titel: Variablen und Ausgabe -bereich: Losfahren -kurz: Werte in Variablen speichern, den mBot2 damit fahren lassen und Ergebnisse auf dem Bildschirm zeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable anlegen und benutzen -- ein kurzes Java-Programm lesen und im Bereich Code tippen -- die Datentypen \`int\`, \`double\`, \`String\` und \`boolean\` unterscheiden -- mit zwei Variablen ein Rechteck in Java fahren -- mit \`mbot.showText\` ein Ergebnis auf dem Bildschirm zeigen -::: - -## Eine Variable für die Seite - -Mit \`mbot.forward(30)\` fährt der mBot2 30 cm am Stück. Fährst du damit ein Quadrat ohne Schleife, steht die 30 aber viermal im Programm. Soll das Quadrat größer werden, musst du sie an vier Stellen ändern. Besser ist es, die Seitenlänge **einmal** zu speichern und dann überall zu benutzen. - -Dafür gibt es **Variablen**. Eine Variable ist ein Kästchen mit einem Namen, in dem ein Wert liegt: - -\`\`\`java -int seite = 30; -mbot.forward(seite); -\`\`\` - -- \`int\` ist der **Datentyp**: eine ganze Zahl. -- \`seite\` ist der **Name**. Du darfst ihn selbst wählen. -- \`30\` ist der **Wert**. Das \`=\` legt ihn in das Kästchen. - -Wo danach \`seite\` steht, setzt der Computer den Wert ein. \`mbot.forward(seite)\` fährt also 30 cm. - -::: übung vb-variable-seite -titel: Quadrat mit Variable -auftrag: Lege in einer Anweisung die Variable \`int seite = 30\` an. Danach fährt der mBot2 ein Quadrat: viermal \`seite\` Zentimeter vor und nach rechts drehen. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: Die erste Anweisung ist \`int seite = 30\`. Darunter kommt eine For-Schleife, die viermal läuft. -tipp: In der Schleife stehen \`mbot.forward(seite)\` und \`mbot.turnRight()\`. -lösung: vb-variable-seite-loesung.visustruct -::: - -> Ändere jetzt nur die 30 in eine 60 und starte die Simulation. Das ganze Quadrat wird doppelt so groß. - -## Welche Datentypen gibt es? - -Jede Variable hat einen Datentyp. Er legt fest, welche Werte hineinpassen: - -| Datentyp | Was passt hinein? | Beispiel | -|---|---|---| -| \`int\` | ganze Zahlen | \`int seite = 30;\` | -| \`double\` | Kommazahlen, mit Punkt geschrieben | \`double radumfang = 20.4;\` | -| \`String\` | Text in Anführungszeichen | \`String name = "Ludwig";\` | -| \`boolean\` | wahr oder falsch | \`boolean wandNah = mbot.getDistance() < 20;\` | - -Ein Rad des mBot2 kommt mit einer Umdrehung etwa 20.4 cm weit, das ist eine Kommazahl. In \`wandNah\` steht \`true\`, wenn die Wand näher als 20 cm ist. Den Abstandssensor lernst du in Lektion 4 kennen. - -Mit Variablen kannst du auch rechnen: \`int strecke = 4 * seite;\` rechnet die ganze Strecke des Quadrats aus. - -## So sieht Java aus - -Bisher hast du Kästen gezogen. Ab jetzt schreibst du manche Programme auch in **Java**. Die Kästen kennst du schon, denn ihr Text ist bereits echtes Java. Rechts neben dem Struktogramm liegt der Bereich [Code](#zeige-codelive). Dort steht dein Struktogramm als Java-Code, und was du dort tippst, wird sofort zum Struktogramm. - -Das Quadrat mit Variable sieht in Java so aus: - -\`\`\`java -int seite = 30; -for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); -} -\`\`\` - -- Jeder Kasten ist eine Zeile. Am Ende jeder Anweisung steht ein **Semikolon** \`;\`. -- Die For-Schleife heißt \`for\`. In den runden Klammern stehen ihre drei Teile Start, Bedingung und Schritt, getrennt durch \`;\`. -- Was in der Schleife wiederholt wird, steht in **geschweiften Klammern** \`{ }\`. Rücke es ein, dann siehst du sofort, was dazugehört. Hinter \`{\` und \`}\` kommt kein Semikolon. -- Groß- und Kleinschreibung zählt: \`mbot.turnRight()\` geht, \`mbot.turnright()\` nicht. - -> So löst du eine Java-Übung: Klicke auf **Im Editor öffnen**. Dann öffnet sich rechts der Bereich Code. Tippe dort dein Programm, sieh in der Simulation zu und klicke zum Schluss auf **Prüfen**. - -## Jetzt in Java: Rechteck - -Ein Rechteck hat zwei verschiedene Seiten. Also brauchst du zwei Variablen. - -::: übung vb-java-rechteck -titel: Rechteck mit Variablen -code: java -auftrag: Schreibe in Java: Lege \`int laenge = 60\` und \`int breite = 30\` an. Der mBot2 fährt ein Rechteck: Länge, rechts drehen, Breite, rechts drehen, und das zweimal. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -braucht: forSchleife -tipp: Zwei Variablen oben: \`int laenge = 60;\` und \`int breite = 30;\`. Darunter eine For-Schleife, die zweimal läuft. -tipp: In der Schleife: \`mbot.forward(laenge);\`, \`mbot.turnRight();\`, \`mbot.forward(breite);\`, \`mbot.turnRight();\` -lösung: vb-java-rechteck-loesung.visustruct -::: - -## Ergebnis auf dem Bildschirm - -Der mBot2 kann nicht nur fahren, er kann dir auch etwas zeigen. Mit \`mbot.showText(…)\` steht ein Text auf dem [Bildschirm]{teil=screen} des CyberPi. Mit \`+\` hängst du Text und Werte aneinander: - -\`\`\`java -int seite = 30; -int strecke = 4 * seite; -mbot.showText("Strecke: " + strecke + " cm"); -\`\`\` - -Der Bildschirm zeigt dann **Strecke: 120 cm**. Den Bildschirm lernst du in Lektion 18 noch genauer kennen. - -::: übung vb-strecke-anzeigen -titel: Wie weit fährt er? -code: java -auftrag: Lege \`int laenge = 60\` und \`int breite = 30\` an und fahre das Rechteck wie eben. Berechne danach in einer Variablen \`strecke\`, wie weit der mBot2 einmal ganz herum gefahren ist, und zeige **Strecke: 180 cm** auf dem Bildschirm. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -befehle: showText "Strecke: 180 cm" -braucht: forSchleife -tipp: Einmal herum sind zweimal die Länge und zweimal die Breite: \`int strecke = 2 * laenge + 2 * breite;\` -tipp: Nach der Schleife: \`mbot.showText("Strecke: " + strecke + " cm");\` -lösung: vb-strecke-anzeigen-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Eingabe und Ausgabe“](?visustruct&lektion=06-eingabe-und-ausgabe). Sie öffnet sich in einem neuen Fenster. Dort gibst du Ergebnisse mit \`System.out.println\` in der Konsole aus. - -## Spiel: Welcher Datentyp? - -::: spiel frage -titel: Spiel: Welcher Datentyp? -intro: Ludwig hat die Datentypen weggewischt. Welcher passt in die Lücke? Sieben Runden. -antworten: int | double | String | boolean - -runde: - ___ seite = 30; -frage: Welcher Datentyp passt? -richtig: int -> 30 ist eine ganze Zahl. - -runde: - ___ radumfang = 20.4; -frage: Welcher Datentyp passt? -richtig: double -> 20.4 ist eine Kommazahl, in Java mit Punkt geschrieben. - -runde: - ___ name = "Ludwig"; -frage: Welcher Datentyp passt? -richtig: String -> Text in Anführungszeichen ist ein \`String\`. - -runde: - ___ wandNah = mbot.getDistance() < 20; -frage: Welcher Datentyp passt? -richtig: boolean -> Ein Vergleich ist wahr oder falsch, also \`boolean\`. - -runde: - ___ abstand = mbot.getDistance(); -frage: Welcher Datentyp passt? -richtig: int -> Der Abstandssensor liefert ganze Zentimeter, zum Beispiel 38. - -runde: - ___ gruss = "Hallo " + "Klasse"; -frage: Welcher Datentyp passt? -richtig: String -> Zwei Texte mit \`+\` ergeben wieder einen Text. - -runde: - ___ mittelwert = 120 / 5.0; -frage: Welcher Datentyp passt? -richtig: double -> Mit \`5.0\` rechnet Java mit Kommazahlen. Das Ergebnis ist 24.0. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Lies das Programm und tippe auf das Feld, auf dem der mBot2 am Ende steht. Achte auf die Werte in den Variablen. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - int seite = 60; - mbot.forward(seite); - mbot.turnRight(); - mbot.forward(seite); -karte: ... | ... | ^.. -ziel: 2,0 -> \`seite\` ist 60, also zwei Felder hoch und zwei nach rechts. - -runde: - int a = 30; - int b = a * 3; - mbot.forward(b); -karte: ... | ... | ... | ^.. -ziel: 0,0 -> \`b\` ist 3 · 30 = 90 cm, also drei Felder. - -runde: - int strecke = 30 * 2; - mbot.backward(strecke); -karte: ..^.. | ..... | ..... -ziel: 2,2 -> \`backward\` fährt rückwärts: Er schaut nach oben, fährt aber 60 cm nach unten. - -runde: - int seite = 30; - for (int i = 0; i < 2; i++) { - mbot.forward(seite); - mbot.turnRight(); - seite = seite + 30; - } -karte: .... | .... | .... | ^... -ziel: 2,2 -> Erst 30 cm hoch, dann wächst \`seite\` auf 60: zwei Felder nach rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable anlegen und benutzen. → Eine Variable für die Seite -- Ich kann ein kurzes Java-Programm lesen und im Bereich Code tippen. → So sieht Java aus -- Ich kann die Datentypen \`int\`, \`double\`, \`String\` und \`boolean\` unterscheiden. → Welche Datentypen gibt es? -- Ich kann mit zwei Variablen ein Rechteck in Java fahren. → Jetzt in Java: Rechteck -- Ich kann mit \`mbot.showText\` ein Ergebnis auf dem Bildschirm zeigen. → Ergebnis auf dem Bildschirm -::: - -## Wissenstest - -::: quiz -? Was fährt \`mbot.forward(seite)\`, wenn vorher \`int seite = 40;\` steht? -→ Eine Variable für die Seite -- [ ] 4 cm -- [x] 40 cm -- [ ] gar nichts, \`seite\` ist kein Befehl -> Wo \`seite\` steht, setzt Java den Wert 40 ein. -? Welcher Datentyp passt zu \`"Ludwig"\`? -→ Welche Datentypen gibt es? -- [ ] \`int\` -- [ ] \`double\` -- [x] \`String\` -> Text in Anführungszeichen ist ein \`String\`. -? Was zeigt \`mbot.showText("Seite: " + 30);\` auf dem Bildschirm? -→ Ergebnis auf dem Bildschirm -- [x] Seite: 30 -- [ ] "Seite: " + 30 -- [ ] 30 -> Das \`+\` hängt den Text und die Zahl aneinander. -::: -`,S5=`--- -titel: Schritt für Schritt testen -bereich: Losfahren -kurz: Mit der Simulation und dem Schreibtischtest herausfinden, was dein Programm wirklich tut. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Simulation mit Start, Schritt und Stopp steuern -- in der Simulation die Variablen beobachten -- einen Schreibtischtest mit einer Tabelle machen -- mit der Simulation einen Fehler finden und reparieren -::: - -## Die Simulation steuern - -Mit dem Knopf [▶︎ Simulation starten](#zeige-simulation) rechts bei den Werkzeugen führt VisuStruct dein Struktogramm aus. In der VisuBot-Welt fährt der mBot2 dabei mit. Rechts erscheint die **Steuerung**: - -- **Start** führt das Programm automatisch Schritt für Schritt aus. Wie schnell, stellst du bei **Pause pro Schritt** ein. -- **Pause** hält an. Danach geht es mit Start oder Schritt weiter. -- **Schritt** führt genau einen Kasten aus. So kannst du in Ruhe zuschauen. -- **Stopp** bricht ab und stellt den mBot2 zurück an den Start. - -Während der Simulation ist ein Kasten farbig markiert. Unter **Letzter Schritt** steht, was gerade passiert ist, unter **Variablen** stehen alle Variablen mit ihren Werten. - -::: diagramm vb-l03-wachsend-loesung.visustruct -Jede Seite wird 10 cm länger. Öffne das Diagramm im Editor und gehe es mit **Schritt** durch. Beobachte dabei \`seite\` und \`i\`. -::: - -## Der Schreibtischtest - -Beim **Schreibtischtest** spielst du selbst den Computer. Du gehst das Struktogramm mit Stift und Papier durch und schreibst nach jedem Schritt die Werte der Variablen auf. Für das Diagramm oben sieht die Tabelle so aus: - -| Schritt | \`i\` | \`seite\` | Der mBot2 … | -|---|---|---|---| -| \`int seite = 20\` | – | 20 | | -| \`mbot.forward(seite)\` | 0 | 20 | fährt 20 cm | -| \`mbot.turnRight()\` | 0 | 20 | dreht rechts | -| \`seite = seite + 10\` | 0 | 30 | | -| \`mbot.forward(seite)\` | 1 | 30 | fährt 30 cm | -| … | … | … | … | - -Vergleiche deine Tabelle danach mit der Simulation. Stimmen die Werte unter **Variablen** nach jedem Schritt? - -::: angeberwissen -Früher war „Computer“ ein Beruf: Menschen, die mit Stift und Papier rechneten. Eine der besten war Katherine Johnson bei der NASA. 1962 wollte der Astronaut John Glenn erst losfliegen, als sie die Flugbahn des neuen IBM-Rechners von Hand nachgerechnet hatte: „Wenn sie sagt, die Zahlen stimmen, fliege ich.“ -::: - -## Fehler finden mit der Simulation - -Ludwig wollte die Spirale von oben bauen: 20 cm, 30 cm, 40 cm. Sein mBot2 fährt aber 30, 40 und 50 cm. Das Programm läuft ohne Fehlermeldung, es tut nur das Falsche. So einen Fehler nennt man **logischen Fehler**. Ihn findest du am besten mit **Schritt** und den Variablen. - -::: übung vb-spirale-falsch -titel: Die Spirale reparieren -auftrag: „Im Editor öffnen“ lädt Ludwigs Struktogramm. Gehe es mit **Schritt** durch und beobachte \`seite\`. Finde heraus, warum die erste Seite 30 cm lang ist, und repariere es. Der mBot2 soll 20, 30 und 40 cm fahren und nach jeder Seite rechts drehen. -start: vb-spirale-falsch-start.visustruct -fahrt: forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -braucht: forSchleife -tipp: Achte darauf, welchen Wert \`seite\` hat, wenn \`mbot.forward(seite)\` drankommt. Ist die Reihenfolge in der Schleife richtig? -tipp: \`seite = seite + 10\` muss **nach** \`mbot.forward(seite)\` und \`mbot.turnRight()\` stehen. Zieh den Kasten ans Ende der Schleife. -lösung: vb-spirale-falsch-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Simulation und Schreibtischtest“](?visustruct&lektion=07-simulation-und-schreibtischtest). Sie öffnet sich in einem neuen Fenster. - -## Spiel: Spiel den Computer - -::: spiel frage -titel: Spiel: Spiel den Computer -intro: Mach in deinem Kopf den Schreibtischtest. Was tut der mBot2, und welche Werte haben die Variablen? Fünf Runden. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite + 10; - } -frage: Welchen Wert hat \`seite\` nach der Schleife? -- 40 -- [x] 50 -- 30 -> 20, dann 30, 40 und nach dem dritten Durchgang 50. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite + 10; - } -frage: Wie weit fährt der mBot2 beim dritten Mal? -- [x] 40 cm -- 50 cm -- 30 cm -> Er fährt 20, 30 und 40 cm. Die 50 steht erst danach in \`seite\`. - -runde: - int seite = 20; - for (int i = 0; i < 3; i++) { - seite = seite + 10; - mbot.forward(seite); - } -frage: Wie weit fährt der mBot2 beim ersten Mal? -- 20 cm -- [x] 30 cm -- 10 cm -> \`seite\` wächst schon vor dem Fahren. Genau das war Ludwigs logischer Fehler. - -runde: - int strecke = 0; - for (int i = 0; i < 4; i++) { - mbot.forward(25); - strecke = strecke + 25; - } - mbot.showText("Strecke: " + strecke); -frage: Was zeigt der Bildschirm am Ende? -- Strecke: 25 -- [x] Strecke: 100 -- Strecke: 0 -> Viermal kommen 25 dazu: 25, 50, 75, 100. - -runde: - int seite = 10; - for (int i = 0; i < 3; i++) { - mbot.forward(seite); - seite = seite * 2; - } -frage: Wie weit fährt der mBot2 insgesamt? -- 60 cm -- [x] 70 cm -- 80 cm -> Er fährt 10, 20 und 40 cm. Zusammen sind das 70 cm. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 8 4 SCHLEIFE: wiederholt Schritte, im Struktogramm wie ein gekipptes L -- ↓ 1 6 ALGORITHMUS: genaue Schritt-für-Schritt-Anleitung -- ↓ 4 8 SEQUENZ: Schritte, die nacheinander kommen -- → 1 1 SIMULATION: führt dein Programm am Bildschirm aus, auch ohne Roboter -- ↓ 8 4 STRUKTOGRAMM: Plan aus Kästen, den du von oben nach unten liest -- → 16 2 VARIABLE: Kästchen mit Namen, in dem ein Wert liegt -- → 13 2 DATENTYP: legt fest, welche Werte in eine Variable passen, z. B. int -- ↓ 4 11 SCHREIBTISCHTEST: du spielst mit Stift und Papier den Computer -lösung: 10 4, 4 6, 16 7, 14 4, 13 4, 13 5, 7 11 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Simulation mit Start, Schritt und Stopp steuern. → Die Simulation steuern -- Ich kann in der Simulation die Variablen beobachten. → Die Simulation steuern -- Ich kann einen Schreibtischtest mit einer Tabelle machen. → Der Schreibtischtest -- Ich kann mit der Simulation einen Fehler finden und reparieren. → Fehler finden mit der Simulation -::: - -## Wissenstest - -::: quiz -? Welcher Knopf führt genau einen Kasten aus? -→ Die Simulation steuern -- [ ] Start -- [x] Schritt -- [ ] Stopp -> Mit Schritt geht es Kasten für Kasten. Start läuft von allein weiter. -? Wie lang fährt der mBot2 im zweiten Durchgang von „Wachsende Seiten“? -→ Der Schreibtischtest -- [ ] 20 cm -- [x] 30 cm -- [ ] 40 cm -> Nach dem ersten Durchgang ist \`seite\` um 10 gewachsen, also 30. -? Das Programm läuft ohne Meldung, der mBot2 fährt aber falsch. Was für ein Fehler ist das? -→ Fehler finden mit der Simulation -- [ ] ein Tippfehler -- [x] ein logischer Fehler -- [ ] ein Fehler im Roboter -> Das Programm tut genau, was dasteht, nur nicht das, was gemeint war. -::: -`,T5=`--- -titel: Sensoren abfragen -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 misst etwas, und dein Programm entscheidet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- den Unterschied zwischen Befehl und Sensor erklären -- einen Sensor in eine Bedingung einbauen -::: - -## Befehle und Sensoren - -Bisher hast du dem mBot2 nur **Befehle** gegeben: fahre vor, drehe rechts. Er hat sie ausgeführt, ohne auf seine Umgebung zu achten. - -Jetzt kommen die **Sensoren** dazu. Unter den [Elementen](#zeige-elemente) gibt es dafür die Gruppe **mBot2-Sensoren**: - -| Sensor | Text | Was du bekommst | -|---|---|---| -| Abstand | \`mbot.getDistance()\` | Abstand bis zur nächsten Wand in cm, z. B. \`38\` | -| Linie | \`mbot.getLine()\` | \`0\`, wenn unter dem Liniensensor alles hell ist, sonst eine Zahl größer als 0 | - -Der mBot2 hat noch mehr Sensoren: Farben, Tasten und Joystick, Licht, Lautstärke und Neigung. Jeden lernst du in den Lektionen 6 bis 13 einzeln kennen, die Motoren in Lektion 20 und Stoppuhr und Akku in Lektion 21. - -Ein Sensor ist **kein eigener Kasten**. Er liefert eine Zahl, und mit der baust du eine **Bedingung**: - -\`\`\`java -mbot.getDistance() < 20 -\`\`\` - -Das heißt: „Ist die Wand näher als 20 cm?“ Die Antwort ist wahr oder falsch. - -> So baust du es: Zieh zuerst eine **Verzweigung** ins Struktogramm. Dann ziehst du den Sensor **Abstand** auf die Verzweigung. In der Bedingung steht jetzt \`mbot.getDistance()\`. Den Vergleich \`< 20\` schreibst du selbst dazu. - -## Wand vorn? - -Der mBot2 soll selbst entscheiden: Ist vorn eine Wand, dreht er sich nach rechts. Ist frei, fährt er ein Stück vor. - -Diese Übung hat **zwei Welten**: In der einen steht die Wand direkt vor ihm, in der anderen ist viel Platz. Dein Programm muss in beiden das Richtige tun. - -::: übung vb-wand-vorn -titel: Wand vorn? -auftrag: Baue eine Verzweigung mit der Bedingung \`mbot.getDistance() < 20\`. Bei **true** dreht sich der mBot2 nach rechts, bei **false** fährt er vor. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und dann den Sensor **Abstand** auf die Verzweigung. Schreib dahinter \`< 20\`. -tipp: Im true-Zweig steht \`mbot.turnRight()\`, im false-Zweig \`mbot.forward()\`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -Im Bereich [Code](#zeige-codelive) siehst du dein Struktogramm auch als Java. Eine Verzweigung heißt dort **if**, der false-Zweig **else**. Die Bedingung steht in runden Klammern, jeder Zweig in geschweiften Klammern: - -\`\`\`java -if (mbot.getDistance() < 20) { - mbot.turnRight(); -} else { - mbot.forward(); -} -\`\`\` - -> Selbst tippen musst du \`if\` noch nicht. Mehr dazu kommt in der Lektion über den Ultraschallsensor. - -## Was liefert der Sensor? - -Ein Sensor liefert einen **Wert**, so wie eine Variable. Fährt der mBot2 weiter, ändert sich der Wert: Der Abstand wird kleiner, je näher die Wand kommt. Erst ein **Vergleich** macht aus dem Wert eine Frage mit der Antwort \`true\` (wahr) oder \`false\` (falsch). - -| Bedingung | Frage | Antwort bei 38 cm Abstand | -|---|---|---| -| \`mbot.getDistance() < 20\` | Ist die Wand näher als 20 cm? | \`false\` | -| \`mbot.getDistance() > 30\` | Ist mehr als 30 cm Platz? | \`true\` | -| \`mbot.getLine() == 0\` | Ist unter dem mBot2 alles hell? | hängt vom Boden ab | - -Vergleichen kannst du mit \`<\` (kleiner), \`>\` (größer), \`<=\` (kleiner oder gleich), \`>=\` (größer oder gleich), \`==\` (gleich) und \`!=\` (ungleich). - -> Für „gleich“ schreibst du **zwei** Gleichheitszeichen: \`==\`. Ein einzelnes \`=\` legt einen Wert in eine Variable, das ist etwas ganz anderes. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Verzweigung“](?visustruct&lektion=08-verzweigung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-04-sensoren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wand vorn?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-04-sensoren-start.visustruct -welt: wand-nah -> turnRight -welt: wand-frei -> forward -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.turnRight()\`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -## Spiel: Befehl oder Sensor? - -::: spiel frage -titel: Spiel: Befehl oder Sensor? -intro: Tut die Zeile etwas, oder liefert sie einen Wert? Acht Runden. -antworten: Befehl | Sensor - -runde: - mbot.forward(30) -frage: Befehl oder Sensor? -richtig: Befehl -> Er fährt 30 cm. Das ist etwas tun. - -runde: - mbot.getDistance() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert den Abstand in cm, zum Beispiel 38. - -runde: - mbot.turnRight() -frage: Befehl oder Sensor? -richtig: Befehl -> Drehen ist etwas tun. - -runde: - mbot.getLine() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert 0 oder eine Zahl größer als 0. - -runde: - mbot.playTone(440, 300) -frage: Befehl oder Sensor? -richtig: Befehl -> Ein Ton ist eine Ausgabe. - -runde: - mbot.isButtonA() -frage: Befehl oder Sensor? -richtig: Sensor -> Er liefert wahr oder falsch: Wird Taste A gedrückt? - -runde: - mbot.showText("Hallo") -frage: Befehl oder Sensor? -richtig: Befehl -> Etwas anzeigen ist etwas tun. - -runde: - mbot.getBrightness() -frage: Befehl oder Sensor? -richtig: Sensor -> Er misst, wie hell es ist, von 0 bis 100. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann den Unterschied zwischen Befehl und Sensor erklären. → Befehle und Sensoren -- Ich kann einen Sensor in eine Bedingung einbauen. → Wand vorn? -::: - -## Wissenstest - -::: quiz -? Die Wand ist 12 cm entfernt. Ist \`mbot.getDistance() < 20\` wahr oder falsch? -→ Wand vorn? -- [x] wahr -- [ ] falsch -> 12 ist kleiner als 20, also ist die Bedingung wahr. -? Der mBot2 steht auf hellem Boden. Was liefert \`mbot.getLine()\`? -→ Befehle und Sensoren -- [x] 0 -- [ ] 15 -- [ ] den Abstand in cm -> 0 heißt: Keiner der vier Fühler sieht etwas Dunkles. -? Warum ist der Sensor kein eigener Kasten im Struktogramm? -→ Wand vorn? -- [x] Er tut nichts, er liefert nur einen Wert. Den braucht man in einer Bedingung. -- [ ] Weil der mBot2 sonst zu langsam wird. -- [ ] Weil Sensoren nur in Java gehen. -> Befehle sind Anweisungen. Sensoren sind Werte, so wie eine Variable. -::: -`,_5=`--- -titel: Bis zur Wand -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 misst den Abstand und hält rechtzeitig an. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit \`mbot.getDistance()\` den Abstand abfragen -- entscheiden, wann ich eine While-Schleife statt einer For-Schleife nehme -- mit dem Liniensensor ein Ziel finden -::: - -## Wie weit ist die Wand? - -Bisher hast du genau gesagt, wie weit der mBot2 fahren soll. Jetzt soll er selbst merken, wann er anhalten muss. - -Dafür gibt es den Ultraschallsensor. \`mbot.getDistance()\` liefert den Abstand bis zur nächsten Wand in Zentimetern, zum Beispiel \`38\`. Du kannst ihn wie eine Zahl benutzen: - -\`\`\`java -mbot.getDistance() > 15 -\`\`\` - -Das ist eine **Bedingung**: Sie ist wahr, solange die Wand mehr als 15 cm entfernt ist. - -> Starte die [Simulation](#zeige-simulation): Unter der Welt steht immer der aktuelle Abstand. Fährt der mBot2 näher an die Wand, wird die Zahl kleiner. - -## Drei Welten, ein Programm - -Diese Übung hat **drei Welten**. In jeder steht die Wand woanders, und der mBot2 soll jedes Mal im Ziel mit der grünen Fahne anhalten, ohne anzustoßen. - -Mit einer festen Anzahl **fahre vor** klappt das nur in einer Welt. Du brauchst eine Schleife, die so lange fährt, wie die Bedingung wahr ist: die **While-Schleife**. - -::: übung vb-bis-wand -titel: Bis zur Wand -auftrag: Der mBot2 soll vorwärts fahren, bis die Wand höchstens 15 cm entfernt ist. Dein Programm muss in allen drei Welten funktionieren. Über der Simulation wählst du, welche Welt du siehst. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Abstand** auf die Schleife. Dann fehlt nur noch der Vergleich. -tipp: Eine While-Schleife mit der Bedingung \`mbot.getDistance() > 15\`, darin ein \`mbot.forward()\`. -lösung: vb-bis-wand-loesung.visustruct -::: - -::: ludwig -Eine Schleife, die eine Bedingung prüft, bevor sie fährt, ist wie ich am Futtertrog: Ich frage zuerst, ob noch etwas drin ist, und erst dann fresse ich weiter. -::: - -## Bis zum Ziel - -Jetzt kommt der zweite Sensor dran. Das Ziel ist ein schwarzes Feld mit grüner Fahne. Fährt der mBot2 darauf, sieht es sein Liniensensor: \`mbot.getLine()\` ist dann nicht mehr \`0\`. - -::: übung vb-bis-ziel -titel: Fahre bis zum Ziel -auftrag: Der mBot2 soll so lange vorwärts fahren, bis er auf dem Ziel steht. Auch hier gibt es zwei Welten. -welt: ziel-kurz, ziel-lang -braucht: whileSchleife -tipp: Die Schleife soll laufen, solange der Liniensensor **nichts** sieht. Zieh den Sensor **Linie** auf die While-Schleife. -tipp: Die Bedingung heißt \`mbot.getLine() == 0\`. In der Schleife steht \`mbot.forward()\`. -lösung: vb-bis-ziel-loesung.visustruct -::: - -## Was prüft die Schleife? - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„While-Schleife“](?visustruct&lektion=11-while-schleife). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-05-bis-zur-wand -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Wand“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-bis-zur-wand-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.forward()\`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Spiel: Wie oft fährt er? - -::: spiel frage -titel: Spiel: Wie oft fährt er? -intro: Jede Fahrt ist 10 cm lang. Rechne aus, wie oft der mBot2 vorfährt, bevor die Schleife aufhört. Sechs Runden. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 40 cm entfernt. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- 4-mal -> 40, 30 und 20 sind größer als 15: drei Fahrten. Bei 10 hört die Schleife auf. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 15 cm entfernt. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- 2-mal -> 15 ist nicht größer als 15. Die Bedingung ist gleich am Anfang falsch. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand ist am Anfang 50 cm entfernt. Wo hält er an? -- 15 cm vor der Wand -- [x] 10 cm vor der Wand -- 0 cm, er stößt an -> 50, 40, 30, 20: viermal vor. Dann ist die Wand 10 cm weg, und die Schleife hört auf. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } -frage: Der mBot2 steht schon auf dem schwarzen Ziel. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- immer weiter -> Auf Schwarz ist \`mbot.getLine()\` nicht 0. Die Bedingung ist sofort falsch. - -runde: -frage: Der mBot2 soll genau fünfmal vorfahren. Welche Schleife passt am besten? -- [x] eine For-Schleife -- eine While-Schleife mit \`mbot.getDistance()\` -- gar keine Schleife -> Die Anzahl steht vorher fest. Dafür ist die For-Schleife da. - -runde: -frage: Der mBot2 soll fahren, bis er die Wand fast berührt. Welche Schleife passt? -- eine For-Schleife, die zehnmal läuft -- [x] eine While-Schleife mit \`mbot.getDistance()\` -- gar keine Schleife -> Wie weit die Wand weg ist, weiß man vorher nicht. Die While-Schleife fragt unterwegs nach. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Grau ist eine Wand. Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß, der Sensor sitzt vorn am mBot2. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, solange vorn mehr als 15 cm frei sind. Direkt vor der Wand hält er an. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,1 -> Egal wie weit die Wand weg ist: Die While-Schleife hält ihn direkt davor an. - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(60); -karte: ##### | ..... | ..... | ..... | ^.... -ziel: 2,2 -> Bei mehr als 45 cm hört er schon ein Feld früher auf, dreht sich und fährt zwei Felder nach rechts. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,0 -> Er fährt, bis sein Liniensensor Schwarz sieht. Dann steht er auf dem schwarzen Feld. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit \`mbot.getDistance()\` den Abstand abfragen. → Wie weit ist die Wand? -- Ich kann entscheiden, wann ich eine While-Schleife statt einer For-Schleife nehme. → Drei Welten, ein Programm -- Ich kann mit dem Liniensensor ein Ziel finden. → Bis zum Ziel -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht 40 cm vor der Wand. Wie oft fährt er mit \`while (mbot.getDistance() > 15) mbot.forward();\` vor, wenn jede Fahrt 10 cm ist? -→ Wie weit ist die Wand? -- [ ] gar nicht -- [ ] einmal -- [x] so oft, bis die Wand höchstens 15 cm weg ist -- [ ] immer genau fünfmal -> Die While-Schleife prüft vor jeder Fahrt die Bedingung. Ist die Wand nah genug, hört sie auf. -? Warum reicht hier keine For-Schleife mit fester Anzahl? -→ Drei Welten, ein Programm -- [x] Weil die Wand in jeder Welt anders weit weg ist. -- [ ] Weil eine For-Schleife keine Anweisungen enthalten darf. -- [ ] Weil der mBot2 For-Schleifen nicht versteht. -> Die For-Schleife weiß vorher, wie oft sie läuft. Die While-Schleife entscheidet unterwegs. -::: -`,A5=`--- -titel: Erst fahren, dann prüfen -bereich: Sensoren und Entscheidungen -kurz: Die Do-while-Schleife fährt immer mindestens einmal und prüft erst danach. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft -- eine While- und eine Do-while-Schleife vergleichen -- eine Do-while-Schleife in Java schreiben -::: - -## Erst fahren, dann prüfen - -Die While-Schleife aus Lektion 5 prüft **zuerst** und fährt dann. Steht die Wand schon direkt vor dem mBot2, fährt er gar nicht los. Sie ist **kopfgesteuert**: Die Bedingung steht oben im Kopf. - -Die **Do-while-Schleife** ist **fußgesteuert**. Die Bedingung steht unten im Fuß des Kastens: - -1. Den Inhalt einmal ausführen. -2. Die Bedingung prüfen. -3. Ist sie wahr, wieder bei 1 anfangen. Ist sie falsch, geht es unter der Schleife weiter. - -Deshalb läuft der Inhalt **immer mindestens einmal**. Wenn du die Schleife anklickst, steht im Objektinspektor vor dem Textfeld \`while\`: Die Bedingung heißt „wiederhole, solange …“. - -| Abstand am Anfang | While-Schleife | Do-while-Schleife | -|---|---|---| -| 50 cm | fährt bis 15 cm | fährt bis 15 cm | -| 10 cm | fährt **gar nicht** | fährt **einmal** 10 cm | - -::: übung vb-do-while -titel: Mindestens ein Schritt -auftrag: Baue ein Struktogramm mit einer Do-while-Schleife: Der mBot2 fährt vor, solange der Abstand größer als 15 cm ist, aber **mindestens einmal**. Danach spielt er einen Ton. -messwerte: getDistance=10 -> forward, tone -welt: wand-kurz -> tone -braucht: doUntilSchleife -tipp: Zieh die **Do-while-Schleife** aus den Elementen ins Struktogramm. In den Kasten kommt **fahre vor**, in den Fuß die Bedingung \`mbot.getDistance() > 15\`. -tipp: Unter die Schleife (nicht hinein) kommt \`mbot.playTone(440, 300)\`. -lösung: vb-do-while-loesung.visustruct -::: - -> Beim Prüfen steht die Wand einmal nur 10 cm vor dem mBot2. Eine While-Schleife würde dann gar nicht fahren. - -## Jetzt in Java: Schritte zählen - -In Java steht \`do\` oben und \`while (…)\` mit Semikolon unten: - -\`\`\`java -do { - mbot.forward(); -} while (mbot.getDistance() > 15); -\`\`\` - -::: übung vb-java-schritte-zaehlen -titel: Schritte zählen -code: java -auftrag: Schreibe in Java: Lege \`int schritte = 0\` an. In einer Do-while-Schleife fährt der mBot2 vor und zählt \`schritte\` um eins hoch, solange der Abstand größer als 15 cm ist. Danach zeigt der Bildschirm zum Beispiel **Schritte: 3**. -welt: wand-kurz -> screen "Schritte: 3" -messwerte: getDistance=10 -> forward, screen "Schritte: 1" -braucht: doUntilSchleife -tipp: \`do { mbot.forward(); schritte = schritte + 1; } while (mbot.getDistance() > 15);\` -tipp: Nach der Schleife \`mbot.showText("Schritte: " + schritte);\`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Do-while-Schleife“](?visustruct&lektion=12-do-while-schleife). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-05-erst-fahren-dann-pruefen -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Schritte zählen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct -welt: wand-kurz -> screen "Schritte: 3" -messwerte: getDistance=10 -> forward, screen "Schritte: 1" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.showText("Schritte: " + schritte)\`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -## Spiel: Kopf oder Fuß? - -::: spiel frage -titel: Spiel: Kopf oder Fuß? -intro: While oder Do-while: Wie oft fährt der mBot2 vor? Jede Fahrt ist 10 cm lang. Sechs Runden. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: Die Wand steht 10 cm vor dem mBot2. Wie oft fährt er vor? -- [x] gar nicht -- 1-mal -- 2-mal -> Die While-Schleife prüft zuerst. 10 ist nicht größer als 15. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Die Wand steht 10 cm vor dem mBot2. Wie oft fährt er vor? -- gar nicht -- [x] 1-mal -- 2-mal -> Die Do-while-Schleife fährt erst und prüft dann. Einmal fährt sie immer. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Die Wand steht 40 cm vor dem mBot2. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- 4-mal -> Nach der ersten Fahrt sind es 30 cm, dann 20, dann 10. Bei 10 ist Schluss. Hier fahren beide Schleifen gleich oft. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte = schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + schritte); -frage: Die Wand steht 10 cm vor dem mBot2. Was zeigt der Bildschirm? -- Schritte: 0 -- [x] Schritte: 1 -- Schritte: 2 -> Der Inhalt läuft einmal, also zählt \`schritte\` einmal hoch. - -runde: -frage: Welche Schleife ist fußgesteuert? -- die While-Schleife -- [x] die Do-while-Schleife -- die For-Schleife -> Ihre Bedingung steht unten im Fuß. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15) -frage: Warum läuft dieser Code nicht? -- \`do\` muss großgeschrieben werden. -- [x] Hinter \`while (…)\` fehlt das Semikolon. -- Die Bedingung muss oben stehen. -> Die Do-while-Schleife endet mit \`} while (Bedingung);\`. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() < 15); -frage: Soll mindestens einmal vorfahren und weiter, solange die Wand mehr als 15 cm weg ist. -fehler: 3 -besser: } while (mbot.getDistance() > 15); -> „Mehr als 15 cm“ heißt \`> 15\`. - -runde: - do - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Soll mindestens einmal vorfahren und weiter, solange die Wand mehr als 15 cm weg ist. -fehler: 1 -besser: do { -> Hinter \`do\` fehlt die öffnende geschweifte Klammer. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + schritte); -frage: Soll die Schritte zählen und am Ende anzeigen. -fehler: 4 -besser: schritte = schritte + 1; -> \`schritte + 1\` rechnet nur, speichert aber nichts. Das Ergebnis muss mit \`=\` zurück in \`schritte\`. - -runde: - int schritte = 0; - do { - mbot.forward(); - schritte = schritte + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Schritte: " + Schritte); -frage: Soll die Schritte zählen und am Ende anzeigen. -fehler: 6 -besser: mbot.showText("Schritte: " + schritte); -> Java unterscheidet Groß- und Kleinschreibung. \`Schritte\` gibt es nicht. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Grau ist eine Wand. While oder Do-while: Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,2 -> Die Wand ist schon nah. Die While-Schleife prüft zuerst und fährt gar nicht los. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 45); -karte: ##### | ..... | ..^.. -ziel: 2,1 -> Die Do-while-Schleife fährt erst einmal und prüft dann. Ein Feld weiter hört sie auf. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); - mbot.turnLeft(); - mbot.forward(30); -karte: ##### | ..... | ..^.. -ziel: 1,1 -> Ein Feld vor, dann ist die Wand direkt davor. Danach links und ein Feld weiter. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> Ist die Wand weit weg, fahren While und Do-while gleich weit: bis direkt vor die Wand. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, dass die Do-while-Schleife ihre Bedingung erst nach dem Inhalt prüft. → Erst fahren, dann prüfen -- Ich kann eine While- und eine Do-while-Schleife vergleichen. → Erst fahren, dann prüfen -- Ich kann eine Do-while-Schleife in Java schreiben. → Jetzt in Java: Schritte zählen -::: - -## Wissenstest - -::: quiz -? Wo steht bei der Do-while-Schleife die Bedingung? -→ Erst fahren, dann prüfen -- [ ] oben im Kopf -- [x] unten im Fuß -- [ ] gar nicht -> Fußgesteuert heißt: erst der Inhalt, dann die Bedingung. -? Die Wand steht 10 cm vor dem mBot2. Wie oft fährt \`do { mbot.forward(); } while (mbot.getDistance() > 15);\`? -→ Erst fahren, dann prüfen -- [ ] gar nicht -- [x] einmal -- [ ] bis er anstößt -> Der Inhalt läuft immer mindestens einmal. Danach ist die Bedingung falsch. -? Was gehört in Java hinter \`while (…)\` am Ende der Do-while-Schleife? -→ Jetzt in Java: Schritte zählen -- [x] ein Semikolon \`;\` -- [ ] eine geschweifte Klammer \`{\` -- [ ] nichts -> \`} while (Bedingung);\` endet mit einem Semikolon. -::: -`,j5=`--- -titel: Zählen und Summieren -bereich: Sensoren und Entscheidungen -kurz: Mit einer Variablen mitzählen und Werte zusammenzählen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Zählvariable anlegen und in einer Schleife hochzählen -- in einer Schleife eine Summe bilden -- das Ergebnis nach der Schleife auf dem Bildschirm zeigen -::: - -## Mitzählen - -Wie viele Schritte fährt der mBot2 bis zur Wand? Das weißt du vorher nicht, die Welt ist jedes Mal anders. Der mBot2 soll deshalb **mitzählen**. Dafür brauchst du drei Kästen: - -1. **Vor** der Schleife: \`int schritte = 0\`. Am Anfang ist noch nichts gezählt. -2. **In** der Schleife: \`schritte = schritte + 1\`. Nach jedem Schritt eins mehr. -3. **Nach** der Schleife: das Ergebnis zeigen, zum Beispiel mit \`mbot.showText("Schritte: " + schritte)\`. - -Lies \`schritte = schritte + 1\` von rechts nach links: Nimm den alten Wert von \`schritte\`, zähle 1 dazu und speichere das Ergebnis wieder in \`schritte\`. So eine Variable heißt **Zählvariable** oder kurz Zähler. - -> Achtung: Steht \`int schritte = 0\` **in** der Schleife, fängt der Zähler bei jedem Durchgang wieder bei 0 an. Am Ende steht dann immer 1 da. - -::: angeberwissen -Dein Handy zählt deine Schritte genau so: Bei jedem Schritt rechnet es \`schritte = schritte + 1\`. Ich zähle lieber meine Grashalme. Ich bin bei 4 Millionen. -::: - -::: übung vb-schritte-welt -titel: Schritte bis zur Wand -auftrag: Baue ein Struktogramm: Der mBot2 fährt vor, solange der Abstand größer als 15 ist. Dabei zählt er seine Schritte mit. Danach zeigt er zum Beispiel **Schritte: 3** auf dem Bildschirm. Es gibt drei Welten. -welt: wand-kurz -> screen "Schritte: 3" -welt: wand-mittel -> screen "Schritte: 7" -welt: wand-lang -> screen "Schritte: 12" -braucht: whileSchleife -tipp: Der erste Kasten ist \`int schritte = 0\`. Danach kommt die While-Schleife mit \`mbot.getDistance() > 15\`. -tipp: In der Schleife stehen **fahre vor** und \`schritte = schritte + 1\`. Nach der Schleife steht \`mbot.showText("Schritte: " + schritte)\`. -lösung: vb-schritte-welt-loesung.visustruct -::: - -## Zusammenzählen - -Ein Zähler wächst immer um 1. Eine **Summe** wächst um verschiedene Werte. Der mBot2 fährt eine Spirale: erst 10 cm, dann 20, dann 30, dann 40. Wie weit ist er insgesamt gefahren? - -| Durchgang | \`seite\` | \`gesamt\` danach | -|---|---|---| -| vorher | 10 | 0 | -| 1 | 10 | 0 + 10 = 10 | -| 2 | 20 | 10 + 20 = 30 | -| 3 | 30 | 30 + 30 = 60 | -| 4 | 40 | 60 + 40 = 100 | - -Das Muster ist dasselbe wie beim Zählen. Nur steht statt der 1 jetzt der Wert, der dazukommt: \`gesamt = gesamt + seite\`. - -## Jetzt in Java: Spirale mit Strecke - -::: übung vb-java-spirale-strecke -titel: Spirale mit Strecke -code: java -auftrag: Schreibe in Java: Lege \`int seite = 10\` und \`int gesamt = 0\` an. Fahre viermal: \`seite\` cm vor, rechts drehen, \`seite\` zu \`gesamt\` dazuzählen, \`seite\` um 10 größer machen. Zeige danach **Strecke: 100 cm** auf dem Bildschirm. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Strecke: 100 cm" -braucht: forSchleife -tipp: In der For-Schleife stehen vier Zeilen: \`mbot.forward(seite);\`, \`mbot.turnRight();\`, \`gesamt = gesamt + seite;\` und \`seite = seite + 10;\`. -tipp: Nach der Schleife steht \`mbot.showText("Strecke: " + gesamt + " cm");\`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -::: übung vb-fehler-05-zaehlen-und-summieren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Spirale mit Strecke“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-05-zaehlen-und-summieren-start.visustruct -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Strecke: 100 cm" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`gesamt = gesamt + seite\`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -## Spiel: Was steht am Ende in der Variablen? - -::: spiel frage -titel: Spiel: Was steht am Ende in der Variablen? -intro: Ludwig hat kleine Programme geschrieben. Rechne im Kopf mit. Fünf Runden. - -runde: -frage: \`int z = 0\`, dann dreimal \`z = z + 1\`. Was ist \`z\`? -- 1 -- [x] 3 -- 0 -> Jeder Durchgang zählt 1 dazu: 1, 2, 3. - -runde: -frage: \`int summe = 0\`, dann \`summe = summe + 5\` und \`summe = summe + 7\`. Was ist \`summe\`? -- 7 -- [x] 12 -- 5 -> 0 + 5 = 5, dann 5 + 7 = 12. - -runde: -frage: Eine Schleife läuft viermal. In ihr stehen \`int z = 0\` und \`z = z + 1\`. Was ist \`z\` am Ende? -- 4 -- [x] 1 -- 0 -> Der Zähler wird in jedem Durchgang wieder auf 0 gesetzt. Er gehört vor die Schleife. - -runde: -frage: \`int gesamt = 0\`. Der mBot2 fährt 20, 20 und 30 cm und zählt jedes Mal dazu. Was ist \`gesamt\`? -- 30 -- 20 -- [x] 70 -> 20 + 20 + 30 = 70. - -runde: -frage: \`int schritte = 10\`, dann zweimal \`schritte = schritte + 1\`. Was ist \`schritte\`? -- 2 -- [x] 12 -- 11 -> Der Zähler fängt hier bei 10 an, nicht bei 0. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Zählvariable anlegen und in einer Schleife hochzählen. → Mitzählen -- Ich kann in einer Schleife eine Summe bilden. → Zusammenzählen -- Ich kann das Ergebnis nach der Schleife auf dem Bildschirm zeigen. → Jetzt in Java: Spirale mit Strecke -::: - -## Wissenstest - -::: quiz -? Wo legst du die Zählvariable \`int schritte = 0\` an? -→ Mitzählen -- [x] Vor der Schleife -- [ ] In der Schleife -- [ ] Nach der Schleife -> In der Schleife würde sie bei jedem Durchgang wieder 0. -? Was macht \`schritte = schritte + 1\`? -→ Mitzählen -- [ ] Es vergleicht schritte mit schritte + 1. -- [x] Es nimmt den alten Wert, zählt 1 dazu und speichert das Ergebnis. -- [ ] Es setzt schritte auf 1. -? Wie zählst du die Seitenlänge \`seite\` zur Summe \`gesamt\` dazu? -→ Zusammenzählen -- [ ] \`gesamt = seite\` -- [ ] \`seite = gesamt + seite\` -- [x] \`gesamt = gesamt + seite\` -::: -`,C5=`--- -titel: Der Ultraschallsensor -bereich: Sensoren und Entscheidungen -kurz: Abstand messen, Abstand halten und das Ganze in Java. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was \`mbot.getDistance()\` misst -- zwei Verzweigungen ineinander bauen -- eine While-Schleife mit dem Abstand in Java schreiben -::: - -## Wie misst der mBot2 den Abstand? - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Der [Ultraschallsensor]{teil=ultrasonic} schickt einen sehr hohen Ton los und wartet auf das Echo. Je länger das Echo braucht, desto weiter ist die Wand weg. \`mbot.getDistance()\` liefert den Abstand in **Zentimetern**, von etwa 3 bis 300. - -| Sensor | Text | Wert | -|---|---|---| -| Abstand | \`mbot.getDistance()\` | ganze Zahl in cm, z. B. \`38\` | - -> In der [Simulation](#zeige-simulation) steht unter der Welt immer der aktuelle Abstand. Mit einem echten mBot2 kannst du die Hand vor den Sensor halten und zusehen, wie sich die Zahl ändert. - -## Abstand halten im Struktogramm - -Der mBot2 soll wie ein Hund an der Leine sein: Ist deine Hand zu nah (unter 10 cm), weicht er zurück. Ist sie zu weit weg (über 20 cm), kommt er näher. Dazwischen bleibt er stehen. - -Dafür brauchst du zwei Verzweigungen ineinander. Die äußere prüft „zu nah?“, die innere im false-Zweig prüft „zu weit?“. - -::: übung vb-abstand-halten -titel: Abstand halten -auftrag: Baue ein Struktogramm: Ist der Abstand kleiner als 10, fährt der mBot2 zurück. Sonst: Ist der Abstand größer als 20, fährt er vor. Sonst tut er nichts. -messwerte: getDistance=5 -> backward -messwerte: getDistance=35 -> forward -messwerte: getDistance=15 -> none -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und den Sensor **Abstand** darauf. Schreib \`< 10\` dahinter. Im true-Zweig steht **fahre zurück**. -tipp: In den false-Zweig kommt eine zweite Verzweigung mit \`mbot.getDistance() > 20\` und **fahre vor** im true-Zweig. Ihr false-Zweig bleibt leer. -lösung: vb-abstand-halten-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand nacheinander auf 5, 35 und 15 cm und schaut, was dein mBot2 tut. - -## Jetzt in Java - -Im Bereich [Code](#zeige-codelive) siehst du dein Struktogramm schon als Java-Code. Eine Verzweigung heißt dort \`if\`, der false-Zweig \`else\`: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.backward(); -} else { - ... -} -\`\`\` - -Den Sensor tippst du wie einen Befehl: \`mbot.\` und dann \`getDistance()\`. Nach dem Punkt schlägt dir der Editor alle Befehle und Sensoren vor. - -::: übung vb-java-wand-piep -titel: Bis zur Wand und piepen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange die Wand mehr als 15 cm entfernt ist. Danach spielt er einen Ton. Es gibt drei Welten. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: Eine While-Schleife heißt in Java \`while (Bedingung) { … }\`. Die Bedingung ist \`mbot.getDistance() > 15\`. -tipp: In der Schleife steht \`mbot.forward();\`, nach der Schleife \`mbot.playTone(440, 300);\`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -::: übung vb-fehler-06-abstand -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Wand und piepen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-abstand-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es \`mbot.playTone(440, 300)\`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Sechs Runden, je 30 Sekunden. -zeit: 30 - -runde: - if (mbot.getDistance() < 10) { - mbot.forward(); - } - if (mbot.getDistance() > 20) { - mbot.forward(); - } -frage: Unter 10 cm soll er zurückfahren, über 20 cm vorfahren. -fehler: 2 -besser: mbot.backward(); -> Ist die Hand zu nah, muss er zurück, nicht vor. - -runde: - while (mbot.getDistance() < 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll vorfahren, solange die Wand mehr als 15 cm weg ist, und dann piepen. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> „Mehr als 15 cm weg“ heißt \`> 15\`. - -runde: - while (mbot.getDistance > 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll bis zur Wand fahren und dann piepen. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> Ein Sensor ist eine Methode. Ohne Klammern findet Java ihn nicht. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } els { - mbot.forward(); - } -frage: Unter 10 cm soll er zurückfahren, sonst vorfahren. -fehler: 3 -besser: } else { -> Der false-Zweig heißt \`else\`. - -runde: - while (mbot.getDistance() > 15) { - mbot.turnRight(); - } - mbot.playTone(440, 300); -frage: Soll bis zur Wand fahren und dann piepen. -fehler: 2 -besser: mbot.forward(); -> Er soll vorfahren, nicht sich drehen. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } else { - if (mbot.getDistance() < 20) { - mbot.forward(); - } - } -frage: Unter 10 cm zurück, über 20 cm vor, dazwischen stehen bleiben. -fehler: 4 -besser: if (mbot.getDistance() > 20) { -> Vorfahren soll er nur, wenn die Hand weiter als 20 cm weg ist. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was \`mbot.getDistance()\` misst. → Wie misst der mBot2 den Abstand? -- Ich kann zwei Verzweigungen ineinander bauen. → Abstand halten im Struktogramm -- Ich kann eine While-Schleife mit dem Abstand in Java schreiben. → Jetzt in Java -::: - -## Wissenstest - -::: quiz -? \`mbot.getDistance()\` liefert \`8\`. Was tut das Programm aus „Abstand halten“? -→ Abstand halten im Struktogramm -- [x] Es fährt zurück. -- [ ] Es fährt vor. -- [ ] Es bleibt stehen. -> 8 ist kleiner als 10, also ist schon die erste Bedingung wahr. -? Wie heißt der false-Zweig einer Verzweigung in Java? -→ Jetzt in Java -- [ ] \`then\` -- [x] \`else\` -- [ ] \`otherwise\` -> \`if\` ist der Anfang, \`else\` der false-Zweig. -::: -`,D5=`--- -titel: Feste Werte mit final -bereich: Sensoren und Entscheidungen -kurz: Eine Konstante anlegen, damit eine wichtige Zahl nur an einer Stelle steht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was eine Konstante ist -- eine Konstante im Struktogramm und in Java anlegen -- eine Konstante in einer Bedingung und in einem Befehl benutzen -::: - -## Eine Zahl an vielen Stellen - -Ludwig hat ein Programm für den Flur geschrieben: Der mBot2 fährt bis 20 cm vor die Wand, dreht sich um und fährt bis 20 cm vor die andere Wand. - -::: diagramm vb-l06k-ohne.visustruct -Hin und zurück. Die Zahl 20 steht an zwei Stellen. -::: - -Jetzt soll der mBot2 schon bei 30 cm anhalten. Ludwig muss jede 20 suchen und ändern. Vergisst er eine, hält der mBot2 einmal bei 30 und einmal bei 20 cm. In langen Programmen passiert so etwas schnell. - -::: angeberwissen -Profis nennen eine Zahl, die einfach so im Programm steht, eine **magische Zahl**. Magisch, weil niemand mehr weiß, was sie bedeutet. Ich mag nur magische Grashalme. -::: - -## Eine Konstante anlegen - -Besser ist eine **Konstante**. Das ist eine Variable, deren Wert nach dem Festlegen gleich bleibt. Du legst sie einmal ganz oben an und benutzt danach nur noch ihren Namen. - -| | Struktogramm | Java | -|---|---|---| -| anlegen | \`const int ABSTAND = 20\` | \`final int ABSTAND = 20;\` | -| benutzen | \`mbot.getDistance() > ABSTAND\` | \`mbot.getDistance() > ABSTAND\` | - -Im Struktogramm schreibst du \`const\` davor, in Java \`final\`. Beides heißt: Dieser Wert ändert sich nicht mehr. Namen von Konstanten schreibt man in **Großbuchstaben**, dann erkennt man sie sofort. - -::: diagramm vb-l06k-mit.visustruct -Dasselbe Programm mit der Konstanten ABSTAND. Für 30 cm änderst du nur die erste Zeile. -::: - -::: übung vb-konstante-wand -titel: Wand mit Konstante -auftrag: Lege die Konstante \`ABSTAND\` mit dem Wert 20 an. Der mBot2 fährt vor, solange der Abstand größer als \`ABSTAND\` ist. Danach spielt er einen Ton. Es gibt drei Welten. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: Der erste Kasten ist eine Anweisung mit \`const int ABSTAND = 20\`. Darunter kommt eine While-Schleife. -tipp: Die Bedingung der Schleife ist \`mbot.getDistance() > ABSTAND\`. In der Schleife steht **fahre vor**, nach der Schleife **spiele Ton**. -lösung: vb-konstante-wand-loesung.visustruct -::: - -## Jetzt in Java: Quadrat mit Konstante - -Eine Konstante passt auch in einen Befehl. Mit \`final int SEITE = 30;\` fährt \`mbot.forward(SEITE);\` genau 30 cm. Soll das Quadrat größer werden, änderst du nur die eine Zeile. - -::: übung vb-java-konstante-seite -titel: Quadrat mit Konstante -code: java -auftrag: Schreibe in Java: Lege die Konstante \`SEITE\` mit dem Wert 30 an. Fahre dann mit einer For-Schleife ein Quadrat mit der Seitenlänge \`SEITE\`. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: Die erste Zeile ist \`final int SEITE = 30;\`. Danach kommt \`for (int i = 0; i < 4; i++) { … }\`. -tipp: In der Schleife stehen \`mbot.forward(SEITE);\` und \`mbot.turnRight();\`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -::: übung vb-fehler-06-konstanten-final -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Quadrat mit Konstante“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-konstanten-final-start.visustruct -fahrt: 4 x (forward 30, turnRight) -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Konstante dort, wo sie angelegt wird? Konstanten schreibt man in Großbuchstaben. -tipp: Richtig heißt es \`mbot.forward(SEITE)\`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -## Spiel: Konstante oder Variable? - -::: spiel frage -titel: Spiel: Konstante oder Variable? -intro: Ludwig sortiert Werte. Bleibt der Wert im ganzen Programm gleich, ist er eine Konstante. Ändert er sich, ist er eine Variable. Sechs Runden. -frage: Konstante oder Variable? -antworten: Konstante | Variable - -runde: -frage: Die Anzahl der Ecken eines Quadrats -richtig: Konstante -> Ein Quadrat hat immer vier Ecken. - -runde: -frage: Der Abstand, den der Ultraschallsensor gerade misst -richtig: Variable -> Fährt der mBot2, ändert sich der Abstand ständig. - -runde: -frage: Der Mindestabstand, bei dem der mBot2 immer anhalten soll -richtig: Konstante -> Du legst ihn einmal fest, zum Beispiel \`final int ABSTAND = 20;\`. - -runde: -frage: Die Zahl der Schritte, die der mBot2 schon gefahren ist -richtig: Variable -> Nach jedem Schritt wird sie um 1 größer. - -runde: -frage: Die Tonhöhe des Warntons, immer 440 Hz -richtig: Konstante -> Der Warnton klingt immer gleich. - -runde: -frage: Der Akkustand in Prozent -richtig: Variable -> Der Akku wird beim Fahren langsam leer. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was eine Konstante ist. → Eine Konstante anlegen -- Ich kann eine Konstante im Struktogramm und in Java anlegen. → Eine Konstante anlegen -- Ich kann eine Konstante in einer Bedingung und in einem Befehl benutzen. → Jetzt in Java: Quadrat mit Konstante -::: - -## Wissenstest - -::: quiz -? Was ist eine Konstante? -→ Eine Konstante anlegen -- [ ] Eine Variable, die sich in jedem Durchgang ändert -- [x] Eine Variable, deren Wert nach dem Festlegen gleich bleibt -- [ ] Ein Sensor, der immer denselben Wert misst -> Eine Konstante legst du einmal fest. Danach benutzt du nur ihren Namen. -? Wie legst du in Java die Konstante \`ABSTAND\` mit dem Wert 20 an? -→ Eine Konstante anlegen -- [ ] \`const ABSTAND = 20;\` -- [x] \`final int ABSTAND = 20;\` -- [ ] \`int final = 20;\` -> In Java steht \`final\` vor dem Datentyp. Im Struktogramm schreibst du \`const\`. -? Warum ist eine Konstante besser als die Zahl 20 an vielen Stellen? -→ Eine Konstante anlegen -- [ ] Weil das Programm dann schneller fährt -- [x] Weil du den Wert nur an einer Stelle ändern musst -- [ ] Weil der Sensor dann genauer misst -::: -`,B5=`--- -titel: Nah, mittel oder weit? -bereich: Sensoren und Entscheidungen -kurz: Eine Verzweigung mit drei Fällen bauen und in Java mit else if schreiben. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Verzweigung mit drei Fällen bauen -- \`else if\` in Java lesen und schreiben -- die Bedingungen in der richtigen Reihenfolge prüfen -::: - -## Drei Fälle statt zwei - -Viele Autos haben eine **Einparkhilfe**. Ist die Wand weit weg, ist alles frei. Kommt sie näher, heißt es: langsam. Ist sie ganz nah: Stopp! - -Eine Verzweigung hat nur zwei Zweige, Ja und Nein. Für drei Fälle setzt du eine zweite Verzweigung in den Nein-Zweig der ersten: - -1. Ist der Abstand kleiner als 10? Dann **Stopp**. -2. Sonst: Ist der Abstand kleiner als 30? Dann **Langsam**. -3. Sonst: **Frei**. - -::: diagramm vb-l06n-einparkhilfe.visustruct -Die Einparkhilfe. Die zweite Frage wird nur gestellt, wenn die Antwort auf die erste Nein war. -::: - -Probiere es in der [Simulation](#zeige-simulation) aus: Öffne das Diagramm im Editor und stelle den Abstand auf 5, 20 und 50 cm. - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Verzweigung“](?visustruct&lektion=08-verzweigung). Sie öffnet sich in einem neuen Fenster. - -## Tempo nach Abstand im Struktogramm - -::: übung vb-tempo-abstand -titel: Tempo nach Abstand -auftrag: Baue ein Struktogramm mit drei Fällen: Ist der Abstand kleiner als 10, fährt der mBot2 5 cm zurück. Sonst: Ist der Abstand kleiner als 30, fährt er 5 cm vor. Sonst fährt er 20 cm vor. -messwerte: getDistance=5 -> backward 5 -messwerte: getDistance=20 -> forward 5 -messwerte: getDistance=50 -> forward 20 -braucht: verzweigung -tipp: Baue es wie die Einparkhilfe: eine Verzweigung mit \`mbot.getDistance() < 10\` und im Nein-Zweig eine zweite mit \`mbot.getDistance() < 30\`. -tipp: Die Befehle sind \`mbot.backward(5)\`, \`mbot.forward(5)\` und \`mbot.forward(20)\`. Der letzte steht im Nein-Zweig der zweiten Verzweigung. -lösung: vb-tempo-abstand-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand nacheinander auf 5, 20 und 50 cm. - -## Jetzt in Java: else if - -Schau in den Bereich [Code](#zeige-codelive). Die Verzweigung im Nein-Zweig sieht so aus: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} else { - if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); - } else { - mbot.showText("Frei"); - } -} -\`\`\` - -In Java darfst du das kürzer schreiben. Das \`if\` rutscht direkt hinter das \`else\`: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} else if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); -} else { - mbot.showText("Frei"); -} -\`\`\` - -Beides bedeutet genau dasselbe. Mit \`else if\` ist es aber besser zu lesen. Die geschweiften Klammern \`{ }\` schreibst du trotzdem immer. - -::: übung vb-java-tageszeit -titel: Tag oder Nacht? -code: java -auftrag: Schreibe in Java mit \`else if\`: Ist die Helligkeit kleiner als 20, zeigt der mBot2 **Nacht**. Sonst: Ist sie kleiner als 60, zeigt er **Dämmerung**. Sonst zeigt er **Tag**. -messwerte: getBrightness=10 -> none, screen "Nacht" -messwerte: getBrightness=40 -> none, screen "Dämmerung" -messwerte: getBrightness=80 -> none, screen "Tag" -braucht: verzweigung -tipp: Die Helligkeit liest du mit \`mbot.getBrightness()\`. Die erste Bedingung ist \`mbot.getBrightness() < 20\`. -tipp: \`if (…) { mbot.showText("Nacht"); } else if (mbot.getBrightness() < 60) { mbot.showText("Dämmerung"); } else { mbot.showText("Tag"); }\` -lösung: vb-java-tageszeit-loesung.visustruct -::: - -::: übung vb-fehler-06-nah-mittel-weit -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Tag oder Nacht?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-06-nah-mittel-weit-start.visustruct -messwerte: getBrightness=10 -> none, screen "Nacht" -messwerte: getBrightness=40 -> none, screen "Dämmerung" -messwerte: getBrightness=80 -> none, screen "Tag" -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es \`mbot.showText("Nacht")\`. -lösung: vb-java-tageszeit-loesung.visustruct -::: - -## Auf die Reihenfolge kommt es an - -Ludwig hat die Einparkhilfe umgebaut und die Fragen getauscht: - -\`\`\`java -if (mbot.getDistance() < 30) { - mbot.showText("Langsam"); -} else if (mbot.getDistance() < 10) { - mbot.showText("Stopp"); -} -\`\`\` - -Bei 5 cm zeigt sein mBot2 **Langsam** statt **Stopp**. Denn 5 ist auch kleiner als 30. Die erste Bedingung ist schon wahr, die zweite wird gar nicht mehr geprüft. **Stopp** erscheint nie. - -> Merke: Bei \`<\` prüfst du zuerst den kleinsten Wert, dann den nächstgrößeren. - -::: spiel frage -titel: Spiel: Was zeigt die Einparkhilfe? -intro: Ludwigs Einparkhilfe von oben: unter 10 cm **Stopp**, unter 30 cm **Langsam**, sonst **Frei**. Was zeigt sie? Sechs Runden. -frage: Was zeigt die Einparkhilfe? -antworten: Stopp | Langsam | Frei - -runde: -frage: Der Abstand ist **5 cm**. -richtig: Stopp -> 5 ist kleiner als 10. Schon die erste Frage ist wahr. - -runde: -frage: Der Abstand ist **25 cm**. -richtig: Langsam -> 25 ist nicht kleiner als 10, aber kleiner als 30. - -runde: -frage: Der Abstand ist **80 cm**. -richtig: Frei -> Beide Fragen sind falsch, also bleibt der letzte Nein-Zweig. - -runde: -frage: Der Abstand ist genau **10 cm**. -richtig: Langsam -> 10 ist nicht kleiner als 10. Die erste Frage ist falsch, die zweite wahr. - -runde: -frage: Der Abstand ist genau **30 cm**. -richtig: Frei -> 30 ist nicht kleiner als 30. Beide Fragen sind falsch. - -runde: -frage: Der Abstand ist **9 cm**. -richtig: Stopp -> 9 ist kleiner als 10. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Verzweigung mit drei Fällen bauen. → Drei Fälle statt zwei -- Ich kann \`else if\` in Java lesen und schreiben. → Jetzt in Java: else if -- Ich kann die Bedingungen in der richtigen Reihenfolge prüfen. → Auf die Reihenfolge kommt es an -::: - -## Wissenstest - -::: quiz -? Wie viele Verzweigungen brauchst du im Struktogramm für drei Fälle? -→ Drei Fälle statt zwei -- [ ] Eine -- [x] Zwei, die zweite im Nein-Zweig der ersten -- [ ] Drei nebeneinander -> Jede Verzweigung teilt in zwei Zweige. Die zweite teilt den Nein-Zweig noch einmal. -? Was bedeutet \`else if\` in Java? -→ Jetzt in Java: else if -- [x] Sonst prüfe noch eine Bedingung -- [ ] Wiederhole die Bedingung -- [ ] Prüfe beide Bedingungen gleichzeitig -> \`else if\` ist die kurze Schreibweise für ein \`if\` im \`else\`-Zweig. -? Ludwig prüft zuerst \`< 30\` und danach \`< 10\`. Was passiert bei 5 cm? -→ Auf die Reihenfolge kommt es an -- [ ] Es erscheint Stopp. -- [x] Es erscheint Langsam. -- [ ] Es erscheint gar nichts. -> 5 ist auch kleiner als 30. Die zweite Bedingung wird nicht mehr geprüft. -::: -`,P5=`--- -titel: Der Liniensensor -bereich: Sensoren und Entscheidungen -kurz: Dunkle Linien auf dem Boden erkennen und nicht vom Tisch fallen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was die Zahl von \`mbot.getLine()\` bedeutet -- bis zu einer Linie fahren -- in Java mit einer Variable Schritte zählen -::: - -## Vier Fühler unter der Nase - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der [Liniensensor]{teil=lineSensor} hat vier Fühler nebeneinander. Jeder leuchtet den Boden an und misst, wie viel Licht zurückkommt. Schwarz schluckt das Licht, Weiß wirft es zurück. - -| Sensor | Text | Wert | -|---|---|---| -| Linie | \`mbot.getLine()\` | \`0\` = alles hell, größer als \`0\` = mindestens ein Fühler sieht Schwarz | - -Für den Anfang reicht dir: **\`mbot.getLine() == 0\` heißt „keine Linie“**, \`mbot.getLine() != 0\` heißt „Linie gefunden“. - -::: angeberwissen -\`mbot.getLine()\` ist eigentlich eine Binärzahl mit vier Stellen, eine für jeden Fühler: \`1111\` (also 15) heißt „alle vier sehen Schwarz“, \`0110\` (6) „nur die mittleren“. Damit kann ein mBot2 sogar einer Linie folgen. -::: - -## Am Rand umkehren im Struktogramm - -Stell dir einen Tisch mit schwarzem Klebeband am Rand vor. Der mBot2 soll vorfahren, bis er das Band sieht, und dann umkehren, damit er nicht herunterfällt. - -::: übung vb-rand -titel: Am Rand umkehren -auftrag: Der mBot2 fährt vor, solange \`mbot.getLine() == 0\` ist. Danach dreht er sich um 180°. Es gibt zwei Welten mit unterschiedlich weitem Weg. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Linie** darauf. Schreib \`== 0\` dahinter. In die Schleife kommt **fahre vor**. -tipp: Nach der Schleife (darunter, nicht darin) steht \`mbot.turnRight(180)\`. -lösung: vb-rand-loesung.visustruct -::: - -## Jetzt in Java: hin und zurück - -Jetzt soll der mBot2 nach dem Umkehren genau bis zum Start zurückfahren. Dafür zählt er mit einer **Variable**, wie oft er vorgefahren ist. - -\`\`\`java -int schritte = 0; -while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; -} -\`\`\` - -::: übung vb-java-rand -titel: Bis zur Linie und zurück -code: java -auftrag: Schreibe in Java: Der mBot2 fährt bis zur Linie und zählt dabei seine Schritte. Dann dreht er sich um 180° und fährt mit einer For-Schleife genauso viele Schritte zurück. -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -braucht: whileSchleife, forSchleife -tipp: Nach der While-Schleife: \`mbot.turnRight(180);\` und dann \`for (int i = 0; i < schritte; i++) { … }\`. -tipp: In der For-Schleife steht nur \`mbot.forward();\`. -lösung: vb-java-rand-loesung.visustruct -::: - -::: übung vb-fehler-07-linie -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis zur Linie und zurück“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-07-linie-start.visustruct -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`schritte = schritte + 1\`. -lösung: vb-java-rand-loesung.visustruct -::: - -## Spiel: Linie oder nicht? - -::: spiel frage -titel: Spiel: Linie oder nicht? -intro: Wo steht der mBot2, und ist die Bedingung wahr oder falsch? Sechs Runden. -antworten: wahr | falsch - -runde: - mbot.getLine() == 0 -frage: Der mBot2 steht auf weißem Papier. Ist die Bedingung wahr? -richtig: wahr -> Kein Fühler sieht Schwarz, also liefert \`mbot.getLine()\` 0. - -runde: - mbot.getLine() == 0 -frage: Die beiden mittleren Fühler stehen auf dem schwarzen Klebeband. Ist die Bedingung wahr? -richtig: falsch -> Zwei Fühler sehen Schwarz, also ist \`mbot.getLine()\` größer als 0. - -runde: - mbot.getLine() != 0 -frage: Nur der äußere linke Fühler sieht Schwarz. Ist die Bedingung wahr? -richtig: wahr -> Ein Fühler reicht schon, dann ist der Wert nicht mehr 0. - -runde: - mbot.getLine() != 0 -frage: Der mBot2 steht auf weißem Boden. Ist die Bedingung wahr? -richtig: falsch -> Auf Weiß ist der Wert 0, und \`0 != 0\` ist falsch. - -runde: - mbot.getLine() == 15 -frage: Eine Querlinie: Alle vier Fühler sehen Schwarz. Ist die Bedingung wahr? -richtig: wahr -> Alle vier Fühler ergeben die Binärzahl \`1111\`, also 15. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; - } -frage: Die Linie ist 30 cm entfernt, jede Fahrt ist 10 cm lang. Ist danach \`schritte == 3\`? -richtig: wahr -> Nach drei Fahrten steht er auf der Linie, und die Schleife hört auf. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getLine() != 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Soll bis zum schwarzen Rand fahren und dann umkehren. -fehler: 1 -besser: while (mbot.getLine() == 0) { -> Er soll fahren, solange **keine** Linie da ist, also \`== 0\`. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(); -frage: Soll bis zum schwarzen Rand fahren und dann umkehren. -fehler: 4 -besser: mbot.turnRight(180); -> Umkehren heißt 180°. Ohne Zahl dreht er sich nur 90°. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte - 1; - } -frage: Soll bis zur Linie fahren und die Schritte zählen. -fehler: 4 -besser: schritte = schritte + 1; -> Zählen heißt eins dazu, nicht eins weg. - -runde: - int schritte = "0"; - while (mbot.getLine() == 0) { - mbot.forward(); - schritte = schritte + 1; - } -frage: Soll bis zur Linie fahren und die Schritte zählen. -fehler: 1 -besser: int schritte = 0; -> In Anführungszeichen ist die 0 ein Text. In ein \`int\` passt nur eine Zahl. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schwarz ist Klebeband. Tippe auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ..=.. | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, bis er auf dem schwarzen Feld steht. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } - mbot.turnRight(180); - mbot.forward(60); -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> Bis zum Rand, umkehren und zwei Felder zurück. - -runde: - int schritte = 0; - while (mbot.getLine() == 0) { - mbot.forward(30); - schritte = schritte + 1; - } - mbot.turnRight(180); - for (int i = 0; i < schritte; i++) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..^.. -ziel: 2,3 -> Er zählt drei Schritte bis zur Linie und fährt genau drei zurück: wieder am Start. - -runde: - mbot.turnRight(); - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ^...= | ..... -ziel: 4,1 -> Erst nach rechts drehen, dann bis zum schwarzen Feld ganz rechts. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was die Zahl von \`mbot.getLine()\` bedeutet. → Vier Fühler unter der Nase -- Ich kann bis zu einer Linie fahren. → Am Rand umkehren im Struktogramm -- Ich kann in Java mit einer Variable Schritte zählen. → Jetzt in Java: hin und zurück -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf weißem Papier. Was liefert \`mbot.getLine()\`? -→ Vier Fühler unter der Nase -- [x] 0 -- [ ] 15 -- [ ] 1 -> Kein Fühler sieht Schwarz, also 0. -? Welche Bedingung heißt „Linie gefunden“? -→ Am Rand umkehren im Struktogramm -- [ ] \`mbot.getLine() == 0\` -- [x] \`mbot.getLine() != 0\` -- [ ] \`mbot.getLine() < 0\` -> \`!=\` heißt „ungleich“. -::: -`,O5=`--- -titel: Und, oder, nicht -bereich: Sensoren und Entscheidungen -kurz: Zwei Sensoren in einer Bedingung prüfen: und, oder, nicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- zwei Bedingungen mit \`&&\` (und) verknüpfen -- zwei Bedingungen mit \`||\` (oder) verknüpfen -- eine Bedingung mit \`!\` (nicht) umdrehen -- eine verknüpfte Bedingung in einer Java-Schleife schreiben -::: - -## Wand und Linie: und - -Der mBot2 soll nur vorfahren, wenn vorn frei ist **und** unter ihm keine Linie ist. Zwei Sensoren, eine Bedingung. In Java heißt **und** \`&&\`: - -\`\`\`java -mbot.getDistance() > 15 && mbot.getLine() == 0 -\`\`\` - -Die ganze Bedingung ist nur wahr, wenn **beide** Teile wahr sind: - -| vorn frei? | keine Linie? | frei \`&&\` keine Linie | -|---|---|---| -| wahr | wahr | **wahr** | -| wahr | falsch | falsch | -| falsch | wahr | falsch | -| falsch | falsch | falsch | - -So eine Tabelle heißt **Wahrheitstabelle** (englisch *truth table*). - -::: übung vb-und -titel: Frei und keine Linie -auftrag: Baue ein Struktogramm mit einer Verzweigung: Ist der Abstand größer als 15 **und** \`mbot.getLine() == 0\`, fährt der mBot2 vor. Sonst tut er nichts. -messwerte: getDistance=50, getLine=0 -> forward -messwerte: getDistance=10, getLine=0 -> none -messwerte: getDistance=50, getLine=6 -> none -braucht: verzweigung -tipp: Zieh den Sensor **Abstand** auf die Verzweigung und schreib \`> 15\` dahinter. Dann tippst du \` && \` und den zweiten Teil \`mbot.getLine() == 0\`. -tipp: Im true-Zweig steht **fahre vor**, der false-Zweig bleibt leer. -lösung: vb-und-loesung.visustruct -::: - -> Beim Prüfen setzt VisuStruct den Abstand und die Linie auf verschiedene Werte. Nur wenn beides passt, darf der mBot2 fahren. - -## Taste A oder Taste B: oder - -Jetzt soll der mBot2 piepen, wenn du Taste A **oder** Taste B drückst. Es reicht, wenn eine der beiden gedrückt ist. In Java heißt **oder** \`||\`. Die beiden Striche findest du auf der Tastatur meist mit **Alt Gr** und **<** (Windows) oder **Option** und **7** (Mac). - -| Taste A? | Taste B? | A oder B | -|---|---|---| -| wahr | wahr | **wahr** | -| wahr | falsch | **wahr** | -| falsch | wahr | **wahr** | -| falsch | falsch | falsch | - -::: übung vb-oder -titel: Taste A oder B -auftrag: Baue ein Struktogramm: Ist Taste A **oder** Taste B gedrückt, spielt der mBot2 einen Ton. Sonst tut er nichts. -messwerte: isButtonA=true, isButtonB=false -> tone -messwerte: isButtonA=false, isButtonB=true -> tone -messwerte: isButtonA=false, isButtonB=false -> noTone -braucht: verzweigung -tipp: Die Bedingung der Verzweigung ist \`mbot.isButtonA() || mbot.isButtonB()\`. -tipp: Im true-Zweig steht \`mbot.playTone(440, 300)\`, der false-Zweig bleibt leer. -lösung: vb-oder-loesung.visustruct -::: - -## Nicht: das Gegenteil - -Manchmal willst du das Gegenteil einer Bedingung. Dafür gibt es \`!\`, gesprochen **nicht**. \`mbot.isButtonA()\` ist wahr, wenn Taste A gedrückt ist. \`!mbot.isButtonA()\` ist wahr, wenn sie **nicht** gedrückt ist. - -\`\`\`java -while (!mbot.isButtonA()) { - mbot.pause(100); -} -mbot.forward(30); -\`\`\` - -Der mBot2 wartet, solange Taste A nicht gedrückt ist. Danach fährt er los. - -## Jetzt in Java: Sicher fahren - -Jetzt kommt alles zusammen. Der mBot2 fährt, solange vorn frei ist **und** keine Linie kommt. Egal, was zuerst kommt, die Wand oder der Rand: Er hält an und piept. - -::: übung vb-java-sicher-fahren -titel: Sicher fahren -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange der Abstand größer als 15 ist **und** \`mbot.getLine() == 0\` ist. Danach spielt er einen Ton. In zwei Welten wartet eine Wand, in zwei Welten ein schwarzer Rand. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -braucht: whileSchleife -tipp: \`while (mbot.getDistance() > 15 && mbot.getLine() == 0) { … }\` -tipp: In der Schleife steht \`mbot.forward();\`, nach der Schleife \`mbot.playTone(440, 300);\`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Bedingungen verknüpfen“](?visustruct&lektion=09-bedingungen-verknuepfen). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-07-und-oder -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Sicher fahren“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-07-und-oder-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.playTone(440, 300)\`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -## Spiel: Wahr oder falsch? - -::: spiel frage -titel: Spiel: Wahr oder falsch? -intro: Ludwig stellt den mBot2 in eine Lage. Ist die Bedingung wahr oder falsch? Acht Runden. -antworten: wahr | falsch - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: Die Wand ist 50 cm weg, unter ihm ist keine Linie. -richtig: wahr -> Beide Teile sind wahr, also ist auch das Ganze wahr. - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: Die Wand ist 10 cm weg, unter ihm ist keine Linie. -richtig: falsch -> Bei \`&&\` müssen beide Teile wahr sein. Der Abstand stimmt nicht. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Nur Taste B ist gedrückt. -richtig: wahr -> Bei \`||\` reicht ein wahrer Teil. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Keine Taste ist gedrückt. -richtig: falsch -> Beide Teile sind falsch, dann ist auch \`||\` falsch. - -runde: - !mbot.isButtonA() -frage: Taste A ist gedrückt. -richtig: falsch -> \`mbot.isButtonA()\` ist wahr, das \`!\` macht daraus falsch. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: Die Wand ist 10 cm weg, und er steht auf einer Linie. -richtig: falsch -> Kein Teil ist wahr, also ist auch \`||\` falsch. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: Die Wand ist 50 cm weg, und er steht auf einer Linie. -richtig: wahr -> Der erste Teil ist wahr, das reicht bei \`||\`. - -runde: - !mbot.isButtonA() && mbot.getDistance() > 15 -frage: Niemand drückt eine Taste, die Wand ist 30 cm weg. -richtig: wahr -> A ist nicht gedrückt, also ist \`!mbot.isButtonA()\` wahr. Der Abstand stimmt auch. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getDistance() > 15 || mbot.getLine() == 0) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Soll fahren, solange vorn frei ist **und** keine Linie kommt. -fehler: 1 -besser: while (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> Mit \`||\` fährt er weiter, wenn nur einer der Teile stimmt, zum Beispiel über den Rand. - -runde: - if (mbot.isButtonA() && mbot.isButtonB()) { - mbot.playTone(440, 300); - } -frage: Soll piepen, wenn Taste A **oder** Taste B gedrückt ist. -fehler: 1 -besser: if (mbot.isButtonA() || mbot.isButtonB()) { -> „Oder“ heißt in Java \`||\`. Mit \`&&\` piept er nur, wenn beide gedrückt sind. - -runde: - while (mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Soll warten, bis Taste A gedrückt wird, und dann losfahren. -fehler: 1 -besser: while (!mbot.isButtonA()) { -> Warten heißt: Schleife, solange A **nicht** gedrückt ist. Das \`!\` fehlt. - -runde: - if (mbot.getDistance() > 15 & mbot.getLine() == 0) { - mbot.forward(); - } -frage: Soll vorfahren, wenn vorn frei ist und keine Linie da ist. -fehler: 1 -besser: if (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> „Und“ schreibt man mit zwei Zeichen: \`&&\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann zwei Bedingungen mit \`&&\` (und) verknüpfen. → Wand und Linie: und -- Ich kann zwei Bedingungen mit \`||\` (oder) verknüpfen. → Taste A oder Taste B: oder -- Ich kann eine Bedingung mit \`!\` (nicht) umdrehen. → Nicht: das Gegenteil -- Ich kann eine verknüpfte Bedingung in einer Java-Schleife schreiben. → Jetzt in Java: Sicher fahren -::: - -## Wissenstest - -::: quiz -? Der Abstand ist 50 und \`mbot.getLine()\` liefert 6. Fährt der mBot2 bei „Frei und keine Linie“? -→ Wand und Linie: und -- [ ] Ja, vorn ist ja frei. -- [x] Nein, denn bei \`&&\` müssen beide Teile wahr sein. -- [ ] Nur halb so weit. -> Er steht auf einer Linie. Damit ist ein Teil falsch, also die ganze Bedingung. -? Nur Taste B ist gedrückt. Ist \`mbot.isButtonA() || mbot.isButtonB()\` wahr? -→ Taste A oder Taste B: oder -- [x] Ja -- [ ] Nein -> Bei \`||\` reicht es, wenn ein Teil wahr ist. -? Taste A ist gedrückt. Was ist \`!mbot.isButtonA()\`? -→ Nicht: das Gegenteil -- [ ] wahr -- [x] falsch -> \`!\` dreht den Wert um: aus wahr wird falsch. -? Welche Bedingung hält den mBot2 an der Wand **und** am Rand an? -→ Jetzt in Java: Sicher fahren -- [x] \`while (mbot.getDistance() > 15 && mbot.getLine() == 0)\` -- [ ] \`while (mbot.getDistance() > 15 || mbot.getLine() == 0)\` -- [ ] \`while (mbot.getDistance() > 15)\` -> Mit \`||\` würde er weiterfahren, solange nur einer der Teile stimmt, also zum Beispiel über den Rand hinaus. -::: -`,M5=`--- -titel: Farben erkennen -bereich: Sensoren und Entscheidungen -kurz: Der Quad-RGB-Sensor sagt, auf welcher Farbe der mBot2 steht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit \`mbot.getColor().equals(…)\` eine Farbe abfragen -- mit \`!\` eine Bedingung umdrehen -- eine Verzweigung in eine Schleife schreiben -::: - -## Ein Sensor, der Farben sieht - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der [Liniensensor]{teil=lineSensor} heißt eigentlich **Quad-RGB-Sensor**. „Quad“ heißt vier (vier Fühler), „RGB“ steht für Rot, Grün und Blau. Er leuchtet den Boden in diesen drei Farben an und misst, welche Farbe zurückkommt. So erkennt er farbige Felder. - -\`mbot.getColor()\` liefert den Namen der Farbe unter dem mBot2, und zwar als **Text**: - -| Boden | \`mbot.getColor()\` | -|---|---| -| rotes Feld | \`"rot"\` | -| grünes Feld | \`"grün"\` | -| blaues Feld | \`"blau"\` | -| gelbes Feld | \`"gelb"\` | -| schwarzes Feld oder Linie | \`"schwarz"\` | -| heller Boden | \`"weiß"\` | - -Ob es eine bestimmte Farbe ist, fragst du mit \`.equals(…)\`: \`mbot.getColor().equals("rot")\` ist **wahr**, wenn der mBot2 auf Rot steht, sonst **falsch**. Das ist also schon eine fertige Bedingung. - -> Texte vergleichst du in Java immer mit \`.equals(…)\`, **nicht** mit \`==\`. Das \`==\` sieht richtig aus, klappt bei Texten aber oft nicht. - -> Mit dem \`!\` davor drehst du eine Bedingung um: \`!mbot.getColor().equals("rot")\` heißt „**nicht** rot“. - -## Fahre bis Rot im Struktogramm - -::: übung vb-bis-rot -titel: Fahre bis Rot -auftrag: Der mBot2 fährt vor, solange er **nicht** auf einem roten Feld steht. In der zweiten Welt liegt vorher ein grünes Feld, das soll ihn nicht stören. -welt: farbe-1 -> forward 20 -welt: farbe-2 -> forward 50 -braucht: whileSchleife -tipp: Zieh eine While-Schleife ins Struktogramm und den Sensor **Farbe ist rot?** darauf. Schreib ein \`!\` vor \`mbot.getColor().equals("rot")\`. -tipp: Die Bedingung heißt \`!mbot.getColor().equals("rot")\`, in der Schleife steht **fahre vor**. -lösung: vb-bis-rot-loesung.visustruct -::: - -## Jetzt in Java: bei Grün piepen - -Jetzt kombinierst du eine Schleife mit einer Verzweigung: Der mBot2 fährt bis Blau. Kommt er unterwegs über ein grünes Feld, piept er. - -::: übung vb-java-blau-gruen -titel: Bis Blau, bei Grün piepen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt vor, solange er nicht auf Blau steht. Nach jedem Schritt prüft er: Steht er auf Grün, spielt er einen Ton. In der zweiten Welt gibt es kein grünes Feld, da darf er nicht piepen. -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -braucht: whileSchleife, verzweigung -tipp: Außen steht \`while (!mbot.getColor().equals("blau")) { … }\`. Darin zuerst \`mbot.forward();\`. -tipp: Danach, noch in der Schleife: \`if (mbot.getColor().equals("grün")) { mbot.playTone(880, 200); }\`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -::: übung vb-fehler-08-farben -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Bis Blau, bei Grün piepen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-08-farben-start.visustruct -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es \`equals("grün")\`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 tun soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Fünf Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (mbot.getColor().equals("rot")) { - mbot.forward(); - } -frage: Soll vorfahren, bis er auf Rot steht. -fehler: 1 -besser: while (!mbot.getColor().equals("rot")) { -> Er soll fahren, solange er **nicht** auf Rot steht. Dafür fehlt das \`!\`. - -runde: - if (mbot.getColor() == "grün") { - mbot.playTone(440, 300); - } -frage: Soll piepen, wenn er auf Grün steht. -fehler: 1 -besser: if (mbot.getColor().equals("grün")) { -> Texte vergleichst du mit \`.equals(…)\`, nicht mit \`==\`. - -runde: - while (!mbot.getColor().equals("blau")) { - mbot.forward(); - if (mbot.getColor().equals("grün")) { - mbot.turnRight(); - } - } -frage: Soll bis Blau fahren und bei Grün piepen. -fehler: 4 -besser: mbot.playTone(440, 300); -> Bei Grün soll er piepen, nicht abbiegen. - -runde: - if (mbot.getColor().equals(rot)) { - mbot.turnRight(); - } -frage: Soll sich auf Rot nach rechts drehen. -fehler: 1 -besser: if (mbot.getColor().equals("rot")) { -> Ohne Anführungszeichen sucht Java eine Variable \`rot\`. Der Farbname ist ein Text. - -runde: - while (!mbot.getColor().equals("rot")) { - mbot.forward(); - } - mbot.playTone(440, 300) -frage: Soll bis Rot fahren und dann piepen. -fehler: 4 -besser: mbot.playTone(440, 300); -> Am Ende der Anweisung fehlt das Semikolon. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit \`mbot.getColor().equals(…)\` eine Farbe abfragen. → Ein Sensor, der Farben sieht -- Ich kann mit \`!\` eine Bedingung umdrehen. → Fahre bis Rot im Struktogramm -- Ich kann eine Verzweigung in eine Schleife schreiben. → Jetzt in Java: bei Grün piepen -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf einem roten Feld. Was ist \`!mbot.getColor().equals("rot")\`? -→ Fahre bis Rot im Struktogramm -- [ ] wahr -- [x] falsch -> \`mbot.getColor().equals("rot")\` ist wahr, das \`!\` macht daraus falsch. Die Schleife hört also auf. -? Womit vergleichst du die Farbe mit \`"grün"\`? -→ Ein Sensor, der Farben sieht -- [x] \`mbot.getColor().equals("grün")\` -- [ ] \`mbot.getColor() == "grün"\` -- [ ] \`mbot.getColor() = "grün"\` -> \`mbot.getColor()\` liefert einen Text, und Texte vergleicht man mit \`.equals(…)\`. Ein einzelnes \`=\` ist eine Zuweisung, gar kein Vergleich. -::: -`,Z5=`--- -titel: Der größte Wert -bereich: Sensoren und Entscheidungen -kurz: Rundum messen und sich den größten Wert merken: Wo ist am meisten Platz? ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit einer Variablen den bisher größten Wert merken -- in einer Schleife jeden neuen Messwert mit dem bisher größten vergleichen -- mit dem mBot2 herausfinden, in welcher Richtung am meisten Platz ist -::: - -## Den Rekord merken - -Beim Weitsprung gibt es eine Tafel mit dem **Rekord**. Nach jedem Sprung schaut die Schiedsrichterin: Ist der neue Sprung weiter als der Rekord? Dann kommt er an die Tafel. Sonst bleibt der alte Rekord stehen. - -Genau so findet ein Programm den größten Wert. Die Tafel ist eine Variable \`max\`: - -1. **Vor** der Schleife: \`int max = 0\`. Noch steht nichts an der Tafel. -2. **In** der Schleife: Ist der neue Wert größer als \`max\`? Dann \`max = wert\`. -3. **Nach** der Schleife steht in \`max\` der größte Wert. - -Der mBot2 dreht sich viermal und misst jedes Mal den Abstand. So sieht das im Schreibtischtest aus: - -| Richtung | gemessen | größer als \`max\`? | \`max\` danach | -|---|---|---|---| -| vorher | | | 0 | -| vorn | 16 | ja | 16 | -| rechts | 36 | ja | 36 | -| hinten | 6 | nein | 36 | -| links | 26 | nein | 36 | - -> Wichtig: Die Verzweigung hat nur einen Ja-Zweig. Ist der Wert nicht größer, passiert nichts. Der alte Rekord bleibt. - -## Wo ist am meisten Platz? - -::: übung vb-meister-platz -titel: Wo ist am meisten Platz? -auftrag: Baue ein Struktogramm: Lege \`int max = 0\` an. Der mBot2 misst viermal den Abstand und dreht sich danach jeweils nach rechts. Ist ein Abstand größer als \`max\`, merkt er ihn sich in \`max\`. Zum Schluss zeigt er zum Beispiel **Am weitesten: 36 cm**. Es gibt zwei Welten. -welt: platz-1 -> screen "Am weitesten: 36 cm" -welt: platz-2 -> screen "Am weitesten: 46 cm" -braucht: forSchleife, verzweigung -tipp: Die For-Schleife läuft viermal. In ihr stehen drei Kästen: \`int abstand = mbot.getDistance()\`, eine Verzweigung und **drehe rechts**. -tipp: Die Verzweigung prüft \`abstand > max\`. Im Ja-Zweig steht \`max = abstand\`, der Nein-Zweig bleibt leer. Nach der Schleife: \`mbot.showText("Am weitesten: " + max + " cm")\`. -lösung: vb-meister-platz-loesung.visustruct -::: - -::: angeberwissen -Viele Saugroboter haben oben einen kleinen Turm mit einem Laser. Er dreht sich und misst ringsum die Abstände. So zeichnet der Roboter eine Karte der Wohnung. Bei mir zu Hause misst niemand. Ich fresse mir einfach den Weg frei. -::: - -## Jetzt in Java: Die Richtung merken - -Jetzt weiß der mBot2, **wie viel** Platz es höchstens gibt. Aber **wo**? Dafür merkt er sich in einer zweiten Variablen auch die Richtung. Die Zählvariable \`i\` sagt dir, die wievielte Messung es war: 0 ist vorn, 1 ist rechts, 2 ist hinten und 3 ist links. - -\`\`\`java -if (abstand > max) { - max = abstand; - richtung = i; -} -\`\`\` - -Beide Zeilen stehen im Ja-Zweig. Nur wenn es einen neuen Rekord gibt, ändert sich auch die Richtung. - -::: übung vb-java-richtung -titel: Die Richtung merken -code: java -auftrag: Schreibe „Wo ist am meisten Platz?“ in Java. Merke dir zusätzlich in \`int richtung = 0\` die Nummer der Messung mit dem größten Abstand. Zeige zum Schluss zum Beispiel **Richtung: 1**. -welt: platz-1 -> screen "Richtung: 1" -welt: platz-2 -> screen "Richtung: 0" -braucht: forSchleife, verzweigung -tipp: Vor der Schleife stehen \`int max = 0;\` und \`int richtung = 0;\`. In der Schleife: \`int abstand = mbot.getDistance();\`, die Verzweigung und \`mbot.turnRight();\`. -tipp: Im Ja-Zweig stehen \`max = abstand;\` und \`richtung = i;\`. Nach der Schleife: \`mbot.showText("Richtung: " + richtung);\` -lösung: vb-java-richtung-loesung.visustruct -::: - -::: übung vb-fehler-08-groesster-wert -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Die Richtung merken“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-08-groesster-wert-start.visustruct -welt: platz-1 -> screen "Richtung: 1" -welt: platz-2 -> screen "Richtung: 0" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`richtung = i\`. -lösung: vb-java-richtung-loesung.visustruct -::: - -## Spiel: Neuer Rekord? - -::: spiel frage -titel: Spiel: Neuer Rekord? -intro: Der mBot2 misst nacheinander mehrere Abstände. Was steht am Ende in \`max\`? Fünf Runden. - -runde: -frage: Gemessen: 20, 50, 30 -- 30 -- [x] 50 -- 100 -> 50 ist der größte Wert. Die 30 danach ist kein neuer Rekord. - -runde: -frage: Gemessen: 10, 20, 30, 40 -- 10 -- [x] 40 -- 100 -> Jeder Wert ist ein neuer Rekord. Am Ende steht der letzte da. - -runde: -frage: Gemessen: 90, 10, 20, 30 -- 30 -- [x] 90 -- 10 -> Der Rekord kommt gleich am Anfang. Danach ist kein Wert größer. - -runde: -frage: Gemessen: 15, 15, 15 -- 45 -- 0 -- [x] 15 -> 15 ist nicht größer als 15. \`max\` bleibt 15. - -runde: -frage: Gemessen: 25, 60, 60, 5. In welcher Richtung (\`i\`) ist der Rekord? -- 0 -- [x] 1 -- 2 -> Bei \`i = 1\` kommt die 60. Die zweite 60 ist nicht **größer**, also bleibt \`richtung\` bei 1. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit einer Variablen den bisher größten Wert merken. → Den Rekord merken -- Ich kann in einer Schleife jeden neuen Messwert mit dem bisher größten vergleichen. → Wo ist am meisten Platz? -- Ich kann mit dem mBot2 herausfinden, in welcher Richtung am meisten Platz ist. → Jetzt in Java: Die Richtung merken -::: - -## Wissenstest - -::: quiz -? Womit fängt \`max\` vor der Schleife an? -→ Den Rekord merken -- [x] Mit 0, weil noch nichts gemessen ist -- [ ] Mit dem größten Wert -- [ ] Mit 100 -> Jeder Abstand ist größer als 0, also wird schon der erste Messwert der Rekord. -? Was steht im Nein-Zweig der Verzweigung \`abstand > max\`? -→ Den Rekord merken -- [ ] \`max = 0\` -- [ ] \`max = abstand\` -- [x] Nichts, der alte Rekord bleibt. -? Wann ändert sich \`richtung\`? -→ Jetzt in Java: Die Richtung merken -- [ ] In jedem Durchgang -- [x] Nur wenn es einen neuen Rekord gibt -- [ ] Nur in der letzten Runde -::: -`,L5=`--- -titel: Fallauswahl mit Farben -bereich: Sensoren und Entscheidungen -kurz: Je nach Farbe oder Linie etwas anderes tun, mit einer Fallauswahl statt vieler Verzweigungen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Fallauswahl mit Texten bauen -- eine Fallauswahl mit Zahlen in Java schreiben -- je nach Zahl einen anderen Text auf dem Bildschirm zeigen -::: - -## Viele Farben, viele Fälle - -Der mBot2 soll auf jede Farbe anders reagieren: bei Rot rechts abbiegen, bei Grün ein Stück vorfahren, bei Blau piepen. Mit Verzweigungen bräuchtest du drei ineinander. Übersichtlicher ist die **Fallauswahl**, in Java \`switch\`: - -\`\`\`java -switch (mbot.getColor()) { - case "rot" -> { - mbot.turnRight(); - } - case "grün" -> { - mbot.forward(20); - } - default -> { - mbot.forward(10); - } -} -\`\`\` - -- Oben in den Klammern steht der Wert, der geprüft wird, hier die Farbe. -- Jeder \`case\` ist ein **Fall**. Passt der Wert, läuft genau dieser Fall. -- \`default\` ist der Fall für **alles andere**. - -Im Struktogramm ist die Fallauswahl ein Kasten mit mehreren Spalten, für jeden Fall eine. - -::: übung vb-farbweiche -titel: Farbweiche -auftrag: Baue eine Fallauswahl über \`mbot.getColor()\`. Bei \`"rot"\` dreht sich der mBot2 nach rechts. Bei \`"grün"\` fährt er 20 cm vor. Bei \`"blau"\` spielt er einen Ton mit 880 Hertz, 200 Millisekunden lang. In allen anderen Fällen fährt er 10 cm vor. -messwerte: getColor=rot -> turnRight -messwerte: getColor=grün -> forward 20 -messwerte: getColor=blau -> none, tone -messwerte: getColor=weiß -> forward 10 -braucht: fallauswahl -tipp: Zieh eine Fallauswahl ins Struktogramm. Oben steht \`mbot.getColor()\`, die Fälle heißen \`"rot"\`, \`"grün"\` und \`"blau"\`, dazu der Fall für alles andere. -tipp: Bei Rot \`mbot.turnRight()\`, bei Grün \`mbot.forward(20)\`, bei Blau \`mbot.playTone(880, 200)\`, sonst \`mbot.forward(10)\`. -lösung: vb-farbweiche-loesung.visustruct -::: - -## Jetzt in Java: Linienmuster - -Auch eine Zahl kann der Wert einer Fallauswahl sein. \`mbot.getLine()\` aus Lektion 7 liefert zum Beispiel: - -| Wert | Bedeutung | -|---|---| -| \`0\` | keine Linie | -| \`6\` | die beiden mittleren Fühler sehen die Linie: Er steht genau drauf | -| \`15\` | alle vier sehen Schwarz: eine Querlinie | - -Bei Zahlen schreibst du den Fall ohne Anführungszeichen: \`case 6 -> { … }\`. - -::: übung vb-linienmuster -titel: Linienmuster -code: java -auftrag: Schreibe in Java eine Fallauswahl über \`mbot.getLine()\`. Bei 6 fährt der mBot2 10 cm vor. Bei 0 dreht er sich nach rechts. Bei 15 zeigt er **Querlinie!** auf dem Bildschirm. In allen anderen Fällen fährt er 5 cm vor. -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Querlinie!" -messwerte: getLine=4 -> forward 5 -braucht: fallauswahl -tipp: \`switch (mbot.getLine()) { case 6 -> { … } case 0 -> { … } case 15 -> { … } default -> { … } }\` -tipp: Bei 6 \`mbot.forward(10);\`, bei 0 \`mbot.turnRight();\`, bei 15 \`mbot.showText("Querlinie!");\`, sonst \`mbot.forward(5);\`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Jetzt in Java: Startsignal - -Vor einem Rennen zeigt der mBot2 nacheinander drei Texte und fährt dann los. Eine For-Schleife zählt die Runden 1, 2 und 3. Die Fallauswahl sucht zu jeder Runde den passenden Text: - -\`\`\`java -for (int runde = 1; runde <= 3; runde++) { - switch (runde) { - case 1 -> { mbot.showText("Auf die Plätze"); } - ... - } - mbot.pause(1000); -} -\`\`\` - -::: übung vb-startsignal -titel: Startsignal -code: java -auftrag: Schreibe in Java: Eine Schleife zählt \`runde\` von 1 bis 3. In Runde 1 zeigt der Bildschirm **Auf die Plätze**, in Runde 2 **Fertig**, sonst **Los!**. Nach jedem Text wartet der mBot2 eine Sekunde. Nach der Schleife fährt er 50 cm vor. -befehle: showText "Auf die Plätze", pause 1000, showText "Fertig", pause 1000, showText "Los!", pause 1000 -fahrt: forward 50 -braucht: fallauswahl -tipp: Die Schleife heißt \`for (int runde = 1; runde <= 3; runde++) { … }\`. Darin steht \`switch (runde) { … }\` und danach \`mbot.pause(1000);\`. -tipp: \`case 1 -> { mbot.showText("Auf die Plätze"); }\`, \`case 2 -> { … }\` und \`default -> { mbot.showText("Los!"); }\`. Nach der Schleife \`mbot.forward(50);\`. -lösung: vb-startsignal-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Fallauswahl“](?visustruct&lektion=10-fallauswahl). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-09-fallauswahl-mbot2 -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Linienmuster“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-09-fallauswahl-mbot2-start.visustruct -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Querlinie!" -messwerte: getLine=4 -> forward 5 -tipp: Lies die Fehlermeldung genau. Ist der Befehl richtig geschrieben? Nach \`mbot.\` schlägt dir der Editor alle Befehle vor. -tipp: Richtig heißt es \`mbot.turnRight()\`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Spiel: Welcher Fall läuft? - -::: spiel frage -titel: Spiel: Welcher Fall läuft? -intro: Lies die Fallauswahl und sag vorher, was der mBot2 tut. Sechs Runden. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Grün. Was tut er? -- Er dreht sich nach rechts. -- [x] Er fährt 20 cm vor. -- Er fährt 10 cm vor. -> Der Fall \`"grün"\` passt, also läuft genau dieser. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Gelb. Was tut er? -- Gar nichts. -- Er piept. -- [x] Er fährt 10 cm vor. -> Für Gelb gibt es keinen Fall, also läuft \`default\`. - -runde: - switch (mbot.getColor()) { - case "rot" -> { mbot.turnRight(); } - case "grün" -> { mbot.forward(20); } - case "blau" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: Der mBot2 steht auf Blau. Was tut er? -- [x] Er piept. -- Er piept und fährt dann 10 cm vor. -- Er dreht sich nach rechts. -> Es läuft nur der passende Fall. \`default\` kommt nur dran, wenn kein anderer Fall passt. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Querlinie!"); } - default -> { mbot.stop(); } - } -frage: Alle vier Fühler sehen Schwarz. Was tut der mBot2? -- Er fährt 10 cm vor. -- [x] Er zeigt **Querlinie!** -- Er hält an. -> Vier Fühler auf Schwarz ergeben 15. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Querlinie!"); } - default -> { mbot.stop(); } - } -frage: Unter dem mBot2 ist alles weiß. Was tut er? -- Er fährt 10 cm vor. -- [x] Er dreht sich nach rechts. -- Er hält an. -> Kein Fühler sieht Schwarz, also liefert \`mbot.getLine()\` 0. - -runde: - for (int runde = 1; runde <= 3; runde++) { - switch (runde) { - case 1 -> { mbot.showText("Auf die Plätze"); } - case 2 -> { mbot.showText("Fertig"); } - default -> { mbot.showText("Los!"); } - } - } -frage: Was zeigt der Bildschirm in Runde 3? -- Fertig -- [x] Los! -- Auf die Plätze -> Für 3 gibt es keinen eigenen Fall, also läuft \`default\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Fallauswahl mit Texten bauen. → Viele Farben, viele Fälle -- Ich kann eine Fallauswahl mit Zahlen in Java schreiben. → Jetzt in Java: Linienmuster -- Ich kann je nach Zahl einen anderen Text auf dem Bildschirm zeigen. → Jetzt in Java: Startsignal -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht auf Gelb. Welcher Fall der Farbweiche läuft? -→ Viele Farben, viele Fälle -- [ ] der Fall \`"rot"\` -- [ ] gar keiner -- [x] \`default\` -> Für Gelb gibt es keinen eigenen Fall. Dann läuft der Fall für alles andere. -? Wie schreibst du den Fall für die Zahl 6? -→ Jetzt in Java: Linienmuster -- [x] \`case 6 -> { … }\` -- [ ] \`case "6" -> { … }\` -- [ ] \`if 6 -> { … }\` -> Zahlen stehen ohne Anführungszeichen, Texte wie \`"rot"\` mit. -::: -`,W5=`--- -titel: Tasten und Joystick -bereich: Sensoren und Entscheidungen -kurz: Der mBot2 wartet auf dich und lässt sich fernsteuern. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Sensoren für Tasten und Joystick abfragen -- auf eine Taste warten -- den mBot2 mit dem Joystick steuern -::: - -## Drücken als Sensor - -::: mbot2 -fokus: joystick, buttons -ansicht: oben -::: - -Auf dem CyberPi sitzen zwei [Tasten]{teil=buttons} (A und B) und ein [Joystick]{teil=joystick}. Für dein Programm sind das Sensoren: Sie liefern **wahr**, solange du drückst. - -| Sensor | Text | -|---|---| -| Taste A / B gedrückt? | \`mbot.isButtonA()\`, \`mbot.isButtonB()\` | -| Joystick nach oben / unten | \`mbot.isJoystickUp()\`, \`mbot.isJoystickDown()\` | -| Joystick nach links / rechts | \`mbot.isJoystickLeft()\`, \`mbot.isJoystickRight()\` | - -> Ohne echten mBot2 klappst du in der [Simulation](#zeige-simulation) unter der Welt **🎛 Sensoren ausprobieren** auf. Dort gibt es die Knöpfe A, B und die Pfeile für den Joystick. Ein Klick hält sie eine Sekunde lang gedrückt. - -## Warten auf Taste A im Struktogramm - -Ein Roboter soll nicht sofort losfahren, wenn das Programm startet. Besser: Er wartet, bis du A drückst. - -Warten heißt: eine Schleife, die so lange läuft, wie A **nicht** gedrückt ist. Damit sie nicht ganz leer ist, wartet der mBot2 darin kurz mit **kurz warten** (\`mbot.pause(100)\`, also 100 Millisekunden). - -::: übung vb-start-a -titel: Start mit Taste A -auftrag: Der mBot2 wartet, bis Taste A gedrückt ist. Dann fährt er 30 cm vor. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: Eine While-Schleife mit der Bedingung \`!mbot.isButtonA()\`. Darin steht **kurz warten**. -tipp: Nach der Schleife steht \`mbot.forward(30)\`. -lösung: vb-start-a-loesung.visustruct -::: - -> Beim Prüfen ist Taste A erst eine Weile nicht gedrückt und dann gedrückt. Fährt dein mBot2 zu früh los, fällt das auf. - -## Jetzt in Java: Fernsteuerung - -Mit dem Joystick lenkst du den mBot2. Das Programm fragt fünfmal nacheinander den Joystick ab: nach oben heißt vorfahren, nach unten zurückfahren. - -::: übung vb-java-joystick -titel: Fernsteuerung -code: java -auftrag: Schreibe in Java eine For-Schleife, die fünfmal läuft. Darin: Ist der Joystick oben, fährt der mBot2 vor. Sonst: Ist er unten, fährt er zurück. -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -braucht: forSchleife, verzweigung -tipp: \`for (int i = 0; i < 5; i++) { … }\` und darin \`if (mbot.isJoystickUp()) { … } else { … }\`. -tipp: Im \`else\` steht noch eine Verzweigung: \`if (mbot.isJoystickDown()) { mbot.backward(); }\`. -lösung: vb-java-joystick-loesung.visustruct -::: - -::: übung vb-fehler-10-tasten-joystick -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Fernsteuerung“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-10-tasten-joystick-start.visustruct -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.backward()\`. -lösung: vb-java-joystick-loesung.visustruct -::: - -## Spiel: Was tut der mBot2? - -::: spiel frage -titel: Spiel: Was tut der mBot2? -intro: Du drückst eine Taste oder den Joystick. Was tut der mBot2? Sechs Runden. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Niemand drückt eine Taste. Was tut der mBot2? -- Er fährt 30 cm vor. -- [x] Er wartet weiter. -- Er piept. -> Solange A nicht gedrückt ist, bleibt er in der Warteschleife. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du drückst Taste B. Was tut der mBot2? -- Er fährt 30 cm vor. -- [x] Er wartet weiter. -- Er fährt 30 cm zurück. -> Die Schleife fragt nur nach Taste A. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du drückst Taste A. Was tut der mBot2? -- [x] Er fährt 30 cm vor. -- Er wartet weiter. -- Er wartet 100 ms und hört dann auf. -> Jetzt ist \`!mbot.isButtonA()\` falsch, die Schleife endet, und er fährt los. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: Du drückst den Joystick nach unten. Was tut der mBot2? -- Er fährt vor. -- [x] Er fährt zurück. -- Gar nichts. -> Die erste Bedingung ist falsch, die zweite wahr. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: Du drückst den Joystick nach links. Was tut der mBot2? -- Er dreht sich nach links. -- Er fährt zurück. -- [x] Gar nichts. -> Für links gibt es keinen Zweig. Beide Bedingungen sind falsch. - -runde: -frage: Du hältst Taste A gedrückt. Was liefert \`mbot.isButtonA()\`? -- [x] true -- false -- 1 -> Tasten liefern wahr oder falsch, und gedrückt heißt wahr. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Sensoren für Tasten und Joystick abfragen. → Drücken als Sensor -- Ich kann auf eine Taste warten. → Warten auf Taste A im Struktogramm -- Ich kann den mBot2 mit dem Joystick steuern. → Jetzt in Java: Fernsteuerung -::: - -## Wissenstest - -::: quiz -? Was liefert \`mbot.isButtonB()\`, wenn niemand drückt? -→ Drücken als Sensor -- [ ] 0 -- [x] false -- [ ] B -> Tasten liefern wahr oder falsch. -? Warum steht in der Warteschleife **kurz warten**? -→ Warten auf Taste A im Struktogramm -- [x] Damit die Schleife nicht leer ist und der CyberPi nicht unnötig schnell im Kreis rennt. -- [ ] Damit die Taste länger leuchtet. -- [ ] Ohne Pause fährt der mBot2 rückwärts. -> Die Schleife fragt alle 100 Millisekunden nach, das reicht völlig. -::: -`,E5=`--- -titel: Notbremse mit break -bereich: Sensoren und Entscheidungen -kurz: Eine Endlosschleife mit Aussprung: Der mBot2 fährt immer weiter, bis die Notbremse greift. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Endlosschleife mit einem Aussprung beenden -- erklären, warum der Aussprung in einer Verzweigung steht -- in Java eine Schleife mit \`while (true)\` und \`break\` schreiben -::: - -## Endlosschleife mit Aussprung - -Viele Roboter-Programme laufen „für immer“: Der mBot2 fährt, misst, fährt, misst … Dafür gibt es die **Endlosschleife**. Sie hat keine Bedingung und hört von selbst nie auf. - -Damit der mBot2 trotzdem anhalten kann, brauchst du eine **Notbremse**: den **Aussprung**. Er verlässt sofort die Schleife, in der er steht. Das Programm macht mit dem ersten Kasten **nach** der Schleife weiter. Du erkennst ihn an der Spitze am linken Rand. In den Kasten schreibst du einen kurzen Kommentar, zum Beispiel \`Schleife verlassen\`. - -Ein Aussprung steht fast immer in einer **Verzweigung**. Sonst würde die Schleife schon beim ersten Durchgang enden. - -::: übung vb-notbremse -titel: Notbremse an der Wand -auftrag: Baue eine Endlosschleife. Darin prüft eine Verzweigung: Ist der Abstand kleiner als 15 cm, kommt ein Aussprung. Danach (unter der Verzweigung, noch in der Schleife) fährt der mBot2 vor. Nach der Schleife spielt er einen Ton. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -braucht: endlosschleife, aussprung -tipp: Zieh die **Endlosschleife** ins Struktogramm. In die Schleife kommt zuerst eine Verzweigung mit \`mbot.getDistance() < 15\`, im true-Zweig der **Aussprung**, der false-Zweig bleibt leer. -tipp: Unter die Verzweigung (in der Schleife) kommt **fahre vor**, unter die Schleife \`mbot.playTone(440, 300)\`. -lösung: vb-notbremse-loesung.visustruct -::: - -## Jetzt in Java: Stopp mit Taste A - -In Java ist die Endlosschleife \`while (true)\`. Der Aussprung heißt \`break\`: - -\`\`\`java -while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); -} -\`\`\` - -::: übung vb-java-taste-stopp -titel: Stopp mit Taste A -code: java -auftrag: Schreibe in Java: Der mBot2 fährt in einer Endlosschleife immer weiter vor. Wird Taste A gedrückt, verlässt er die Schleife mit \`break\`. Danach zeigt der Bildschirm **Gestoppt**. -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -braucht: endlosschleife, aussprung -tipp: \`while (true) { … }\` mit \`if (mbot.isButtonA()) { break; }\` und danach \`mbot.forward();\`. -tipp: Nach der Schleife \`mbot.showText("Gestoppt");\`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Aufruf und Aussprung“](?visustruct&lektion=18-aufruf-und-aussprung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-10-unterbrechen-mit-break -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Stopp mit Taste A“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-10-unterbrechen-mit-break-start.visustruct -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es \`mbot.showText("Gestoppt")\`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -## Spiel: Notbremse - -::: spiel frage -titel: Spiel: Notbremse -intro: Wann greift der Aussprung, und was passiert danach? Jede Fahrt ist 10 cm lang. Fünf Runden. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Die Wand ist am Anfang 40 cm entfernt. Wie oft fährt er vor? -- 2-mal -- [x] 3-mal -- immer weiter -> Bei 40, 30 und 20 fährt er. Bei 10 greift die Notbremse. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Was passiert direkt nach \`break\`? -- Die Schleife beginnt von vorn. -- Er fährt noch einmal vor. -- [x] Er piept. -> \`break\` verlässt die Schleife. Weiter geht es mit dem ersten Befehl danach. - -runde: - while (true) { - mbot.forward(); - break; - } -frage: Wie oft fährt der mBot2 vor? -- [x] 1-mal -- gar nicht -- immer weiter -> Der Aussprung steht nicht in einer Verzweigung. Die Schleife endet schon im ersten Durchgang. - -runde: - while (true) { - mbot.forward(); - } -frage: Wann hört diese Schleife auf? -- nach 10 Fahrten -- an der Wand -- [x] nie -> Ohne \`break\` ist \`while (true)\` wirklich endlos. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Du drückst Taste A, nachdem er zweimal vorgefahren ist. Wie oft ist er vorgefahren? -- [x] 2-mal -- 3-mal -- gar nicht -> Beim nächsten Durchgang merkt er die Taste und springt aus der Schleife. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - while (true) { - if (mbot.getDistance() > 15) { - break; - } - mbot.forward(); - } -frage: Soll vorfahren und an der Wand mit der Notbremse anhalten. -fehler: 2 -besser: if (mbot.getDistance() < 15) { -> Die Notbremse soll greifen, wenn die Wand **nah** ist. - -runde: - while (true) { - if (mbot.isButtonB()) { - break; - } - mbot.forward(); - } - mbot.showText("Gestoppt"); -frage: Soll vorfahren, bis Taste A gedrückt wird, und dann „Gestoppt“ zeigen. -fehler: 2 -besser: if (mbot.isButtonA()) { -> Gebremst werden soll mit Taste A, nicht mit B. - -runde: - while (true) { - if (mbot.isButtonA()) { - brake; - } - mbot.forward(); - } -frage: Soll vorfahren, bis Taste A gedrückt wird. -fehler: 3 -besser: break; -> Der Aussprung heißt \`break\`, nicht \`brake\`. - -runde: - while (false) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Soll in einer Endlosschleife fahren, bis Taste A gedrückt wird. -fehler: 1 -besser: while (true) { -> Die Endlosschleife heißt \`while (true)\`. Mit \`false\` läuft sie kein einziges Mal. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Endlosschleife mit einem Aussprung beenden. → Endlosschleife mit Aussprung -- Ich kann erklären, warum der Aussprung in einer Verzweigung steht. → Endlosschleife mit Aussprung -- Ich kann in Java eine Schleife mit \`while (true)\` und \`break\` schreiben. → Jetzt in Java: Stopp mit Taste A -::: - -## Wissenstest - -::: quiz -? Wo macht das Programm nach einem Aussprung weiter? -→ Endlosschleife mit Aussprung -- [ ] am Anfang der Schleife -- [x] beim ersten Kasten nach der Schleife -- [ ] ganz am Anfang des Programms -> Der Aussprung verlässt die Schleife sofort. -? Warum steht der Aussprung in einer Verzweigung? -→ Endlosschleife mit Aussprung -- [x] Sonst endet die Schleife schon im ersten Durchgang. -- [ ] Sonst fährt der mBot2 rückwärts. -- [ ] Das ist nur schöner. -> Nur wenn die Bedingung zutrifft, soll die Notbremse greifen. -? Wie heißt die Endlosschleife in Java? -→ Jetzt in Java: Stopp mit Taste A -- [ ] \`while (false)\` -- [x] \`while (true)\` -- [ ] \`for (;;) break;\` -> \`true\` ist immer wahr, die Schleife hört nur mit \`break\` auf. -::: -`,N5=`--- -titel: Der Lichtsensor -bereich: Sensoren und Entscheidungen -kurz: Hell oder dunkel? Der mBot2 wird zum Nachtlicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Helligkeit abfragen -- mit einer Verzweigung die LEDs schalten -- mehrere Befehle in einen Zweig schreiben -::: - -## Wie hell ist es? - -::: mbot2 -fokus: lightSensor -ansicht: oben -::: - -Der [Lichtsensor]{teil=lightSensor} sitzt auf dem CyberPi. \`mbot.getBrightness()\` liefert eine Zahl von **0** (stockdunkel) bis **100** (sehr hell). - -| Sensor | Text | Wert | -|---|---|---| -| Helligkeit | \`mbot.getBrightness()\` | 0 bis 100 | - -> In der Simulation stellst du die Helligkeit mit dem Regler unter **🎛 Sensoren ausprobieren** ein. Mit einem echten mBot2 hältst du einfach die Hand über den CyberPi. - -## Nachtlicht im Struktogramm - -::: übung vb-nachtlicht -titel: Nachtlicht -auftrag: Ist die Helligkeit kleiner als 30, leuchten die LEDs weiß (\`mbot.setLed(255, 255, 255)\`). Sonst gehen die LEDs aus (\`mbot.ledOff()\`). -messwerte: getBrightness=10 -> ledOn -messwerte: getBrightness=80 -> ledOff -braucht: verzweigung -tipp: Zieh eine Verzweigung ins Struktogramm und den Sensor **Helligkeit** darauf. Schreib \`< 30\` dahinter. -tipp: Im true-Zweig steht **LEDs an** mit \`mbot.setLed(255, 255, 255)\`, im false-Zweig **LEDs aus**. -lösung: vb-nachtlicht-loesung.visustruct -::: - -::: angeberwissen -Straßenlaternen machen es genauso: Ein Lichtsensor merkt, wann es dämmert, und schaltet sie ein. Darum gehen sie im Winter früher an als im Sommer, ohne dass jemand eine Uhr umstellt. -::: - -## Jetzt in Java: Nachtlicht mit Ton - -::: übung vb-java-nachtlicht-ton -titel: Nachtlicht mit Ton -code: java -auftrag: Schreibe das Nachtlicht in Java, aber mit zwei Änderungen: Im Dunkeln leuchten die LEDs gelb (\`mbot.setLed(255, 200, 0)\`) **und** der mBot2 spielt einen Ton. Im Hellen gehen die LEDs aus, ohne Ton. -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -braucht: verzweigung -tipp: \`if (mbot.getBrightness() < 30) { … } else { … }\` -tipp: Im \`if\`-Teil stehen zwei Zeilen: \`mbot.setLed(255, 200, 0);\` und \`mbot.playTone(440, 300);\`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -::: übung vb-fehler-11-licht -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Nachtlicht mit Ton“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-11-licht-start.visustruct -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es \`mbot.setLed(255, 200, 0)\`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -## Spiel: Licht an oder aus? - -::: spiel frage -titel: Spiel: Licht an oder aus? -intro: Wie hell ist es, und was tun die LEDs? Sechs Runden. -antworten: LEDs an | LEDs aus - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Es ist fast dunkel, die Helligkeit ist 10. -richtig: LEDs an -> 10 ist kleiner als 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist 45. -richtig: LEDs aus -> 45 ist nicht kleiner als 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist genau 30. -richtig: LEDs aus -> 30 ist nicht kleiner als 30. Für „bis einschließlich 30“ bräuchtest du \`<=\`. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: Die Helligkeit ist 29. -richtig: LEDs an -> 29 ist gerade noch kleiner als 30. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: Die Helligkeit ist 50. -richtig: LEDs an -> Achtung, hier ist es umgekehrt: 50 ist nicht größer als 70, also läuft der false-Zweig. Die LEDs leuchten gelb. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: Die Sonne scheint, die Helligkeit ist 100. -richtig: LEDs aus -> 100 ist größer als 70, also läuft der true-Zweig. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Helligkeit abfragen. → Wie hell ist es? -- Ich kann mit einer Verzweigung die LEDs schalten. → Nachtlicht im Struktogramm -- Ich kann mehrere Befehle in einen Zweig schreiben. → Jetzt in Java: Nachtlicht mit Ton -::: - -## Wissenstest - -::: quiz -? \`mbot.getBrightness()\` liefert 45. Was tut das Nachtlicht? -→ Nachtlicht im Struktogramm -- [ ] Die LEDs leuchten. -- [x] Die LEDs gehen aus. -> 45 ist nicht kleiner als 30. -? In welchem Bereich liegen die Werte des Lichtsensors? -→ Wie hell ist es? -- [ ] 0 bis 255 -- [x] 0 bis 100 -- [ ] -100 bis 100 -::: -`,F5=`--- -titel: Ein Merker mit boolean -bereich: Sensoren und Entscheidungen -kurz: Mit einer Variablen vom Typ boolean festhalten, ob etwas passiert ist. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Variable vom Typ \`boolean\` anlegen -- mit einem Merker festhalten, ob etwas passiert ist -- einen Merker nach der Schleife in einer Bedingung abfragen -::: - -## Wahr oder falsch - -Eine Variable vom Typ \`boolean\` kennt nur zwei Werte: \`true\` (wahr) und \`false\` (falsch). Du kennst das schon von den Sensoren: \`mbot.isButtonA()\` liefert \`true\` oder \`false\`. Auch ein Vergleich wie \`mbot.getDistance() < 10\` ist am Ende \`true\` oder \`false\`. - -| Anlegen | Bedeutung | -|---|---| -| \`boolean dunkel = false\` | Es war noch nicht dunkel. | -| \`dunkel = true\` | Jetzt war es dunkel. | -| \`if (dunkel)\` | Ist \`dunkel\` wahr? | - -Bei \`if (dunkel)\` brauchst du kein \`== true\`. Die Variable ist ja selbst schon wahr oder falsch. - -## Der Merker - -Der mBot2 soll drei Sekunden lang Wache halten. Er misst immer wieder die Helligkeit. Danach soll er sagen, ob es **irgendwann** dunkel war. - -Dafür nimmst du einen **Merker**: eine \`boolean\`-Variable, die mit \`false\` anfängt. Wird es dunkel, setzt du sie auf \`true\`. Danach setzt sie niemand mehr zurück. Nach der Schleife weißt du also: War es auch nur ein einziges Mal dunkel? - -::: übung vb-wurde-es-dunkel -titel: Wurde es dunkel? -auftrag: Baue ein Struktogramm: Lege \`boolean dunkel = false\` an. Prüfe in einer For-Schleife 30-mal: Ist die Helligkeit kleiner als 20, wird \`dunkel\` wahr. Warte in jedem Durchgang 100 ms. Zeige danach **Es war dunkel** oder **Immer hell**. -messwerte: getBrightness=80/10/80 -> none, screen "Es war dunkel" -messwerte: getBrightness=80 -> none, screen "Immer hell" -braucht: forSchleife, verzweigung -tipp: In der Schleife steht eine Verzweigung mit \`mbot.getBrightness() < 20\`. Im Ja-Zweig steht \`dunkel = true\`, der Nein-Zweig bleibt leer. Danach kommt \`mbot.pause(100)\`. -tipp: Nach der Schleife kommt eine zweite Verzweigung mit der Bedingung \`dunkel\`. Ja: \`mbot.showText("Es war dunkel")\`, Nein: \`mbot.showText("Immer hell")\`. -lösung: vb-wurde-es-dunkel-loesung.visustruct -::: - -> Beim Prüfen wird es in der Mitte der Wache kurz dunkel und dann wieder hell. Zeigt dein mBot2 trotzdem **Es war dunkel**? - -## Jetzt in Java: Wurde geklatscht? - -In Java sieht der Merker so aus: - -\`\`\`java -boolean laut = false; -for (int i = 0; i < 30; i++) { - if (mbot.getLoudness() > 60) { - laut = true; - } - mbot.pause(100); -} -\`\`\` - -\`true\` und \`false\` schreibst du in Java immer klein. - -::: übung vb-java-klatsch-merker -titel: Wurde geklatscht? -code: java -auftrag: Schreibe in Java: Der mBot2 hört drei Sekunden lang zu (30-mal mit 100 ms Pause). Ist die Lautstärke einmal größer als 60, merkt er sich das in \`boolean laut\`. Danach zeigt er **Geklatscht!** oder **Alles still**. -messwerte: getLoudness=0/90/0 -> none, screen "Geklatscht!" -messwerte: getLoudness=0 -> none, screen "Alles still" -braucht: forSchleife, verzweigung -tipp: Nimm den Code oben und schreibe danach \`if (laut) { … } else { … }\`. -tipp: In die Zweige kommen \`mbot.showText("Geklatscht!");\` und \`mbot.showText("Alles still");\`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -::: übung vb-fehler-11-merker-boolean -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wurde geklatscht?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-11-merker-boolean-start.visustruct -messwerte: getLoudness=0/90/0 -> none, screen "Geklatscht!" -messwerte: getLoudness=0 -> none, screen "Alles still" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie schreibt man wahr und falsch in Java? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`laut = true\`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -## Spiel: true oder false? - -::: spiel frage -titel: Spiel: true oder false? -intro: Ludwig fragt, was in der Bedingung herauskommt. Sechs Runden. -frage: Ist es true oder false? -antworten: true | false - -runde: -frage: \`mbot.getDistance() < 10\`, der Abstand ist 25 cm. -richtig: false -> 25 ist nicht kleiner als 10. - -runde: -frage: \`mbot.isButtonA()\`, und du drückst gerade Taste A. -richtig: true -> Der Sensor meldet: gedrückt. - -runde: -frage: \`boolean dunkel = false\`. In der Schleife wird es nie dunkel. Was ist \`dunkel\` danach? -richtig: false -> Niemand hat den Merker auf \`true\` gesetzt. - -runde: -frage: \`boolean dunkel = false\`. In der Schleife wird es einmal dunkel, danach wieder hell. Was ist \`dunkel\` danach? -richtig: true -> Ein Merker bleibt \`true\`, wenn er einmal gesetzt wurde. - -runde: -frage: \`mbot.getBrightness() > 50\`, die Helligkeit ist 50. -richtig: false -> 50 ist nicht größer als 50. - -runde: -frage: \`!mbot.isButtonA()\`, und du drückst Taste A nicht. -richtig: true -> Taste A ist nicht gedrückt, also \`false\`. Das \`!\` dreht es um: \`true\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Variable vom Typ \`boolean\` anlegen. → Wahr oder falsch -- Ich kann mit einem Merker festhalten, ob etwas passiert ist. → Der Merker -- Ich kann einen Merker nach der Schleife in einer Bedingung abfragen. → Jetzt in Java: Wurde geklatscht? -::: - -## Wissenstest - -::: quiz -? Welche Werte kann eine Variable vom Typ \`boolean\` haben? -→ Wahr oder falsch -- [ ] Jede ganze Zahl -- [x] Nur \`true\` und \`false\` -- [ ] Nur 0 und 1 -? Womit fängt ein Merker an? -→ Der Merker -- [x] Mit \`false\`, weil noch nichts passiert ist -- [ ] Mit \`true\` -- [ ] Mit dem ersten Messwert -> Erst wenn das Ereignis passiert, wird er \`true\`. -? Wie fragst du in Java ab, ob der Merker \`laut\` wahr ist? -→ Jetzt in Java: Wurde geklatscht? -- [x] \`if (laut)\` -- [ ] \`if (laut = true)\` -- [ ] \`if laut\` -> \`laut\` ist selbst schon wahr oder falsch. Ein einzelnes \`=\` wäre eine Zuweisung. -::: -`,R5=`--- -titel: Das Mikrofon -bereich: Sensoren und Entscheidungen -kurz: Klatschen zum Losfahren und eine Lärmampel für das Klassenzimmer. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Lautstärke abfragen -- warten, bis etwas passiert -- eine Lärmampel in Java schreiben -::: - -## Wie laut ist es? - -::: mbot2 -fokus: microphone -ansicht: oben -::: - -Das [Mikrofon]{teil=microphone} des CyberPi misst die Lautstärke. \`mbot.getLoudness()\` liefert **0** (still) bis **100** (sehr laut). Ein Klatschen in der Nähe ergibt ungefähr 60 bis 90. - -| Sensor | Text | Wert | -|---|---|---| -| Lautstärke | \`mbot.getLoudness()\` | 0 bis 100 | - -## Klatschen zum Losfahren im Struktogramm - -Wie beim Warten auf Taste A: Eine Schleife wartet, solange es leise ist. Erst dann fährt der mBot2 los. - -::: übung vb-klatschen -titel: Klatschen zum Losfahren -auftrag: Der mBot2 wartet, solange die Lautstärke kleiner als 50 ist. Danach fährt er 30 cm vor. -messwerte: getLoudness=0/80 -> forward 30 -messwerte: getLoudness=90 -> forward 30 -braucht: whileSchleife -tipp: Eine While-Schleife mit der Bedingung \`mbot.getLoudness() < 50\`, darin **kurz warten**. -tipp: Nach der Schleife: \`mbot.forward(30)\`. -lösung: vb-klatschen-loesung.visustruct -::: - -> In der Simulation schiebst du den Regler **Lautstärke** nach rechts, während das Programm wartet. - -## Jetzt in Java: Lärmampel - -::: übung vb-java-laermampel -titel: Lärmampel -code: java -auftrag: Schreibe in Java: Ist die Lautstärke größer als 60, leuchten die LEDs rot. Sonst sind sie aus. -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -braucht: verzweigung -tipp: \`if (mbot.getLoudness() > 60) { … } else { … }\` -tipp: Rot ist \`mbot.setLed(255, 0, 0);\`, aus ist \`mbot.ledOff();\`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -::: angeberwissen -Lautstärke misst man eigentlich in **Dezibel** (dB). Flüstern hat etwa 30 dB, ein Klassenzimmer 60 bis 70 dB, ein Presslufthammer 100 dB. Der CyberPi rechnet das in eine einfache Zahl von 0 bis 100 um. -::: - -::: übung vb-fehler-12-lautstaerke -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Lärmampel“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-12-lautstaerke-start.visustruct -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.ledOff()\`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -## Spiel: Laut oder leise? - -::: spiel frage -titel: Spiel: Laut oder leise? -intro: Wie laut ist es, und was tut der mBot2? Sechs Runden. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: In der Klasse ist es ruhig, die Lautstärke ist 30. -- [x] Er wartet weiter. -- Er fährt 30 cm vor. -> 30 ist kleiner als 50, die Warteschleife läuft weiter. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: Du klatschst, die Lautstärke ist 70. -- Er wartet weiter. -- [x] Er fährt 30 cm vor. -> 70 ist nicht kleiner als 50. Die Schleife endet, und er fährt los. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: Die Lautstärke ist genau 50. -- Er wartet weiter. -- [x] Er fährt 30 cm vor. -> 50 ist nicht kleiner als 50. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Die Lautstärke ist 65. -- [x] Die LEDs leuchten rot. -- Die LEDs sind aus. -> 65 ist größer als 60: zu laut. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Die Lautstärke ist genau 60. -- Die LEDs leuchten rot. -- [x] Die LEDs sind aus. -> 60 ist nicht größer als 60. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Jemand flüstert, die Lautstärke ist 10. -- Die LEDs leuchten rot. -- [x] Die LEDs sind aus. -> Flüstern ist leise. Die Lärmampel bleibt aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Lautstärke abfragen. → Wie laut ist es? -- Ich kann warten, bis etwas passiert. → Klatschen zum Losfahren im Struktogramm -- Ich kann eine Lärmampel in Java schreiben. → Jetzt in Java: Lärmampel -::: - -## Wissenstest - -::: quiz -? Die Klatsch-Schleife lautet \`while (mbot.getLoudness() < 50)\`. Es ist 30 laut. Was passiert? -→ Klatschen zum Losfahren im Struktogramm -- [x] Der mBot2 wartet weiter. -- [ ] Der mBot2 fährt los. -> 30 ist kleiner als 50, die Bedingung ist wahr, die Schleife läuft weiter. -::: -`,J5=`--- -titel: Der Lagesensor -bereich: Sensoren und Entscheidungen -kurz: Neigung messen und Schütteln erkennen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was der Lagesensor misst -- zwei Bedingungen mit „oder“ verbinden -- mehrere Bedingungen mit „und“ verbinden -::: - -## Schief oder gerade? - -::: mbot2 -fokus: cyberpi -drehen: ja -::: - -Im [CyberPi]{teil=cyberpi} steckt ein **Lagesensor** (Gyroskop). Er misst, wie schief der mBot2 steht, in Grad: - -| Sensor | Text | Wert | -|---|---|---| -| Neigung vorn/hinten | \`mbot.getPitch()\` | Nase nach unten: positiv, nach oben: negativ (−90 bis 90) | -| Neigung seitlich | \`mbot.getRoll()\` | zur einen Seite positiv, zur anderen negativ | -| Geschüttelt? | \`mbot.isShaken()\` | \`true\` oder \`false\` | - -Steht der mBot2 flach auf dem Tisch, sind beide Neigungen ungefähr **0**. - -> In der Simulation gibt es dafür zwei Regler unter **🎛 Sensoren ausprobieren** und den Knopf **schütteln**. Mit einem echten mBot2 kippst du ihn vorsichtig in der Hand. - -## Schräglage-Warnung im Struktogramm - -Fährt der mBot2 eine zu steile Rampe hoch oder runter, soll er warnen. Zu steil heißt: mehr als 20° nach vorn **oder** mehr als 20° nach hinten. - -::: übung vb-schraeglage -titel: Schräglage-Warnung -auftrag: Ist die Neigung vorn/hinten größer als 20 **oder** kleiner als −20, spielt der mBot2 einen Ton. Sonst tut er nichts. -messwerte: getPitch=30 -> tone -messwerte: getPitch=-30 -> tone -messwerte: getPitch=5 -> noTone -braucht: verzweigung -tipp: „oder“ heißt in einer Bedingung \`||\`. Zieh den Sensor **Neigung vorn/hinten** zweimal auf die Verzweigung. -tipp: Die Bedingung heißt \`mbot.getPitch() > 20 || mbot.getPitch() < -20\`, im true-Zweig steht **Ton spielen**. -lösung: vb-schraeglage-loesung.visustruct -::: - -## Jetzt in Java: Wasserwaage - -Eine Wasserwaage zeigt, ob etwas genau gerade ist. Der mBot2 leuchtet grün, wenn beide Neigungen zwischen −5 und 5 liegen. - -::: übung vb-java-wasserwaage -titel: Wasserwaage -code: java -auftrag: Schreibe in Java: Liegen \`mbot.getPitch()\` und \`mbot.getRoll()\` beide zwischen −5 und 5, leuchten die LEDs grün. Sonst sind sie aus. -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -braucht: verzweigung -tipp: „und“ heißt \`&&\`. Du brauchst vier Vergleiche: \`mbot.getPitch() > -5 && mbot.getPitch() < 5 && …\` -tipp: Grün ist \`mbot.setLed(0, 255, 0);\`, im \`else\` steht \`mbot.ledOff();\`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -::: übung vb-fehler-13-neigung -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wasserwaage“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-13-neigung-start.visustruct -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.setLed(0, 255, 0)\`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -## Spiel: Zu schief? - -::: spiel frage -titel: Spiel: Zu schief? -intro: Ludwig kippt den mBot2. Was tut er? Sechs Runden. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Die Nase zeigt 30° nach unten, \`mbot.getPitch()\` liefert 30. -- [x] Er piept. -- Er piept nicht. -> 30 ist größer als 20. Ein wahrer Teil reicht bei \`||\`. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Die Nase zeigt nach oben, \`mbot.getPitch()\` liefert −25. -- [x] Er piept. -- Er piept nicht. -> −25 ist kleiner als −20, also ist der zweite Teil wahr. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: Eine flache Rampe, \`mbot.getPitch()\` liefert 10. -- Er piept. -- [x] Er piept nicht. -> 10 liegt zwischen −20 und 20. Beide Teile sind falsch. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: \`mbot.getPitch()\` liefert genau 20. -- Er piept. -- [x] Er piept nicht. -> 20 ist nicht größer als 20. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: \`mbot.getPitch()\` liefert 0, \`mbot.getRoll()\` liefert 3. -- [x] Die LEDs leuchten grün. -- Die LEDs sind aus. -> Alle vier Teile sind wahr. Der mBot2 liegt fast gerade. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: \`mbot.getPitch()\` liefert 2, \`mbot.getRoll()\` liefert −8. -- Die LEDs leuchten grün. -- [x] Die LEDs sind aus. -> −8 ist nicht größer als −5. Bei \`&&\` macht ein falscher Teil alles falsch. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- ↓ 1 7 JOYSTICK: kleiner Hebel auf dem CyberPi -- → 4 1 FALLAUSWAHL: switch: für jeden Wert ein eigener Fall -- → 2 3 SENSOR: Bauteil, das etwas misst -- → 6 1 VERZWEIGUNG: je nach Bedingung das eine oder das andere -- ↓ 6 9 ULTRASCHALL: damit misst der mBot2 den Abstand: ein sehr hoher Ton und sein Echo -- → 8 3 BREAK: Notbremse: verlässt sofort die Schleife -- ↓ 8 3 BEDINGUNG: ist wahr oder falsch, z. B. mbot.getDistance() < 20 -- ↓ 1 11 HELLIGKEIT: mbot.getBrightness() misst sie -lösung: 4 7, 6 6, 12 3, 2 6, 2 7, 8 4 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was der Lagesensor misst. → Schief oder gerade? -- Ich kann zwei Bedingungen mit „oder“ verbinden. → Schräglage-Warnung im Struktogramm -- Ich kann mehrere Bedingungen mit „und“ verbinden. → Jetzt in Java: Wasserwaage -::: - -## Wissenstest - -::: quiz -? Der mBot2 steht flach auf dem Tisch. Was liefert \`mbot.getPitch()\` ungefähr? -→ Schief oder gerade? -- [x] 0 -- [ ] 90 -- [ ] 100 -? Was macht \`mbot.isShaken()\`? -→ Schief oder gerade? -- [ ] Es schüttelt den mBot2. -- [x] Es meldet, ob jemand den mBot2 schüttelt. -> Sensoren tun nichts, sie messen nur. -::: -`,G5=`--- -titel: Die LEDs -bereich: Licht, Ton und Bildschirm -kurz: Farben mischen, eine Ampel bauen und blinken lassen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- eine Farbe aus Rot, Grün und Blau mischen -- Farben mit Pausen nacheinander zeigen -- die LEDs in Java blinken lassen -::: - -## Rot, Grün und Blau mischen - -::: mbot2 -fokus: leds -ansicht: hinten -::: - -Die fünf [LEDs]{teil=leds} sind ein **Aktor**: Sie tun etwas, sie leuchten. Jede LED mischt ihre Farbe aus Rot, Grün und Blau, jeweils von 0 (aus) bis 255 (ganz hell). - -| Befehl | Kasten | Was passiert | -|---|---|---| -| \`mbot.setLed(255, 0, 0)\` | **LEDs an** | alle LEDs rot | -| \`mbot.ledOff()\` | **LEDs aus** | alle LEDs aus | -| \`mbot.pause(1000)\` | **kurz warten** | 1000 Millisekunden (1 Sekunde) warten | - -| Farbe | Rot | Grün | Blau | -|---|---|---|---| -| Rot | 255 | 0 | 0 | -| Gelb | 255 | 200 | 0 | -| Grün | 0 | 255 | 0 | -| Blau | 0 | 0 | 255 | -| Weiß | 255 | 255 | 255 | -| Lila | 160 | 0 | 255 | - -> Ohne **kurz warten** schaltet der mBot2 die Farben so schnell um, dass du nur die letzte siehst. In der Simulation siehst du die Farbe an den LEDs des 3D-Modells. - -## Ampel im Struktogramm - -::: übung vb-ampel -titel: Ampel -auftrag: Die LEDs zeigen nacheinander Rot, Gelb und Grün wie eine Ampel. Zwischen den Farben wartet der mBot2 jeweils 1000 Millisekunden. Gelb ist \`255, 200, 0\`. -befehle: setLed 255 0 0, pause 1000, setLed 255 200 0, pause 1000, setLed 0 255 0 -tipp: Zieh dreimal **LEDs an** und zweimal **kurz warten** ins Struktogramm, abwechselnd. -tipp: Ändere die Zahlen: \`mbot.setLed(255, 0, 0)\`, \`mbot.pause(1000)\`, \`mbot.setLed(255, 200, 0)\`, \`mbot.pause(1000)\`, \`mbot.setLed(0, 255, 0)\`. -lösung: vb-ampel-loesung.visustruct -::: - -## Jetzt in Java: Blaulicht - -Blinken heißt: an, warten, aus, warten, und das immer wieder. Das „immer wieder“ ist eine Schleife. - -::: übung vb-java-blinken -titel: Blaulicht -code: java -auftrag: Schreibe in Java eine For-Schleife, die dreimal läuft. Darin gehen die LEDs blau an, der mBot2 wartet 500 Millisekunden, die LEDs gehen aus, und er wartet wieder 500 Millisekunden. -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: \`for (int i = 0; i < 3; i++) { … }\` -tipp: In der Schleife: \`mbot.setLed(0, 0, 255);\`, \`mbot.pause(500);\`, \`mbot.ledOff();\`, \`mbot.pause(500);\`. -lösung: vb-java-blinken-loesung.visustruct -::: - -::: übung vb-fehler-14-leds -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Blaulicht“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-14-leds-start.visustruct -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.pause(500)\`. -lösung: vb-java-blinken-loesung.visustruct -::: - -## Spiel: Welche Farbe? - -::: spiel frage -titel: Spiel: Welche Farbe? -intro: Rot, Grün und Blau gemischt: Welche Farbe zeigen die LEDs? Sieben Runden. - -runde: - mbot.setLed(255, 0, 0); -frage: Welche Farbe leuchtet? -- [x] Rot -- Grün -- Blau -> Nur der erste Wert, Rot, ist an. - -runde: - mbot.setLed(0, 0, 255); -frage: Welche Farbe leuchtet? -- Rot -- Grün -- [x] Blau -> Die Reihenfolge ist immer Rot, Grün, Blau. Nur Blau ist an. - -runde: - mbot.setLed(255, 255, 255); -frage: Welche Farbe leuchtet? -- Gelb -- [x] Weiß -- aus -> Alle drei Farben ganz hell ergeben Weiß. - -runde: - mbot.setLed(255, 200, 0); -frage: Welche Farbe leuchtet? -- [x] Gelb -- Lila -- Weiß -> Rot und Grün zusammen ergeben Gelb. - -runde: - mbot.setLed(160, 0, 255); -frage: Welche Farbe leuchtet? -- Gelb -- [x] Lila -- Grün -> Rot und Blau zusammen ergeben Lila. - -runde: - mbot.setLed(0, 0, 0); -frage: Welche Farbe leuchtet? -- Weiß -- Schwarz -- [x] keine, die LEDs sind aus -> Alle drei Werte sind 0. Eine LED kann nicht schwarz leuchten. - -runde: - mbot.setLed(255, 0, 0); - mbot.setLed(0, 255, 0); - mbot.pause(1000); -frage: Welche Farbe siehst du eine Sekunde lang? -- Rot -- [x] Grün -- erst Rot, dann Grün -> Ohne **kurz warten** zwischen den Farben ist Rot sofort wieder weg. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.setLed(255, 0, 0); - mbot.pause(1000); - mbot.setLed(255, 200, 0); - mbot.pause(1000); - mbot.setLed(0, 0, 255); -frage: Soll wie eine Ampel Rot, Gelb und Grün zeigen. -fehler: 5 -besser: mbot.setLed(0, 255, 0); -> Grün ist \`0, 255, 0\`. \`0, 0, 255\` ist Blau. - -runde: - for (int i = 0; i < 3; i++) { - mbot.setLed(0, 0, 255); - mbot.pause(500); - mbot.ledOff; - mbot.pause(500); - } -frage: Soll dreimal blau blinken. -fehler: 4 -besser: mbot.ledOff(); -> Auch ein Befehl ohne Zahl braucht seine Klammern. - -runde: - mbot.setLed(0, 0, 300); - mbot.pause(1000); - mbot.ledOff(); -frage: Soll eine Sekunde ganz hell blau leuchten. -fehler: 1 -besser: mbot.setLed(0, 0, 255); -> Jede Farbe geht nur von 0 bis 255. - -runde: - mbot.setLed(255, 255, 255); - mbot.pause(1000); - mbot.setLed(0, 0, 0, 0); -frage: Soll eine Sekunde weiß leuchten und dann ausgehen. -fehler: 3 -besser: mbot.ledOff(); -> \`setLed\` hat genau drei Zahlen: Rot, Grün, Blau. Zum Ausschalten gibt es \`mbot.ledOff()\`. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann eine Farbe aus Rot, Grün und Blau mischen. → Rot, Grün und Blau mischen -- Ich kann Farben mit Pausen nacheinander zeigen. → Ampel im Struktogramm -- Ich kann die LEDs in Java blinken lassen. → Jetzt in Java: Blaulicht -::: - -## Wissenstest - -::: quiz -? Welche Farbe ergibt \`mbot.setLed(0, 255, 0)\`? -→ Rot, Grün und Blau mischen -- [ ] Rot -- [x] Grün -- [ ] Blau -> Die Zahlen sind Rot, Grün, Blau. Nur Grün ist an. -? Warum braucht das Blaulicht **kurz warten**? -→ Jetzt in Java: Blaulicht -- [x] Sonst wechselt es so schnell, dass man kein Blinken sieht. -- [ ] Sonst gehen die LEDs kaputt. -- [ ] Weil \`ledOff()\` sonst nicht geht. -::: -`,H5=`--- -titel: Einzelne LEDs -bereich: Licht, Ton und Bildschirm -kurz: Jede der fünf LEDs bekommt ihre eigene Farbe, bis zum Lauflicht. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die LEDs über ihre Nummern 0 bis 4 ansprechen -- einzelne LEDs in verschiedenen Farben leuchten lassen -- mit einer Schleife ein Lauflicht bauen -::: - -## Jede LED hat eine Nummer - -::: mbot2 -fokus: leds -ansicht: hinten -::: - -Bisher haben alle fünf [LEDs]{teil=leds} dieselbe Farbe gezeigt. Du kannst aber auch jede LED einzeln ansprechen. Dafür hat jede LED eine **Nummer**. Wie bei Arrays fängt das Zählen bei **0** an: Die fünf LEDs heißen 0, 1, 2, 3 und 4. - -| Befehl | Kasten | Was passiert | -|---|---|---| -| \`mbot.led(0).setColor("grün")\` | **LED-Farbe setzen** | LED 0 leuchtet grün | -| \`mbot.led(3).setColor("aus")\` | **LED-Farbe setzen** | LED 3 geht aus | - -Die Farbe schreibst du als Namen in Anführungszeichen: - -| Farbname | Farbname | Farbname | -|---|---|---| -| \`"rot"\` | \`"orange"\` | \`"gelb"\` | -| \`"grün"\` | \`"türkis"\` | \`"blau"\` | -| \`"lila"\` | \`"pink"\` | \`"weiß"\` | - -Mit \`"aus"\` schaltest du eine LED aus. - -> Die Nummer darf auch eine Variable sein: \`mbot.led(i).setColor("rot")\`. So kann eine Schleife eine LED nach der anderen einschalten. - -## Drei Lichter im Struktogramm - -::: übung vb-drei-lichter -titel: Drei Lichter -auftrag: Schalte LED 0 rot, LED 2 gelb und LED 4 grün. Die LEDs 1 und 3 bleiben aus. -befehle: ledColor 0 "rot", ledColor 2 "gelb", ledColor 4 "grün" -tipp: Zieh dreimal **LED-Farbe setzen** ins Struktogramm. -tipp: Ändere Nummer und Farbe: \`mbot.led(0).setColor("rot")\`, \`mbot.led(2).setColor("gelb")\`, \`mbot.led(4).setColor("grün")\`. -lösung: vb-drei-lichter-loesung.visustruct -::: - -## Jetzt in Java: Lauflicht - -Bei einem Lauflicht wandert ein Licht von einer LED zur nächsten. Die Schleifenvariable \`i\` ist dabei die Nummer der LED. - -::: übung vb-java-lauflicht -titel: Lauflicht -code: java -auftrag: Schreibe in Java eine For-Schleife, in der \`i\` von 0 bis 4 läuft. Darin geht LED \`i\` blau an, der mBot2 wartet 200 Millisekunden, und LED \`i\` geht wieder aus. -befehle: ledColor 0 "blau", pause 200, ledColor 0 "aus", ledColor 1 "blau", pause 200, ledColor 1 "aus", ledColor 2 "blau", pause 200, ledColor 2 "aus", ledColor 3 "blau", pause 200, ledColor 3 "aus", ledColor 4 "blau", pause 200, ledColor 4 "aus" -braucht: forSchleife -tipp: \`for (int i = 0; i < 5; i++) { … }\` -tipp: In der Schleife: \`mbot.led(i).setColor("blau");\`, \`mbot.pause(200);\`, \`mbot.led(i).setColor("aus");\`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -::: übung vb-fehler-15-einzelne-leds -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Lauflicht“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-15-einzelne-leds-start.visustruct -befehle: ledColor 0 "blau", pause 200, ledColor 0 "aus", ledColor 1 "blau", pause 200, ledColor 1 "aus", ledColor 2 "blau", pause 200, ledColor 2 "aus", ledColor 3 "blau", pause 200, ledColor 3 "aus", ledColor 4 "blau", pause 200, ledColor 4 "aus" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.led(i)\`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -## Spiel: Welche LED leuchtet? - -::: spiel frage -titel: Spiel: Welche LED leuchtet? -intro: Fünf LEDs mit den Nummern 0 bis 4. Welche leuchten? Sechs Runden. - -runde: - mbot.led(0).setColor("rot"); -frage: Welche LED leuchtet rot? -- [x] die erste -- die zweite -- alle -> Die Nummern fangen bei 0 an. LED 0 ist die erste. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blau"); - } -frage: Welche LEDs leuchten blau? -- LED 0 bis 3 -- [x] alle fünf -- nur LED 4 -> \`i\` läuft von 0 bis 4, das sind alle fünf. - -runde: - for (int i = 0; i < 5; i = i + 2) { - mbot.led(i).setColor("grün"); - } -frage: Welche LEDs leuchten grün? -- LED 1 und 3 -- [x] LED 0, 2 und 4 -- LED 2 und 4 -> \`i\` springt in Zweierschritten: 0, 2, 4. - -runde: - mbot.led(2).setColor("gelb"); - mbot.led(2).setColor("aus"); -frage: Welche LED leuchtet am Ende? -- LED 2 gelb -- [x] keine -- LED 3 gelb -> LED 2 geht an und gleich wieder aus. - -runde: - for (int i = 4; i >= 0; i--) { - mbot.led(i).setColor("rot"); - mbot.pause(200); - mbot.led(i).setColor("aus"); - } -frage: Bei welcher LED beginnt dieses Lauflicht? -- LED 0 -- [x] LED 4 -- LED 5 -> \`i\` beginnt bei 4 und zählt rückwärts. Das Licht läuft andersherum. - -runde: -frage: Ludwig schreibt \`mbot.led(5).setColor("rot");\`. Welche LED leuchtet? -- die fünfte -- die sechste -- [x] keine, eine LED 5 gibt es nicht -> Die fünf LEDs haben die Nummern 0 bis 4. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.led(1).setColor("rot"); - mbot.led(2).setColor("gelb"); - mbot.led(4).setColor("grün"); -frage: Soll LED 0 rot, LED 2 gelb und LED 4 grün schalten. -fehler: 1 -besser: mbot.led(0).setColor("rot"); -> Die erste LED hat die Nummer 0. - -runde: - for (int i = 1; i <= 5; i++) { - mbot.led(i).setColor("blau"); - } -frage: Soll alle fünf LEDs blau schalten. -fehler: 1 -besser: for (int i = 0; i < 5; i++) { -> Die LEDs haben die Nummern 0 bis 4. Eine LED 5 gibt es nicht. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blau"); - mbot.pause(200); - mbot.led(0).setColor("aus"); - } -frage: Lauflicht: Jede LED geht kurz blau an und wieder aus. -fehler: 4 -besser: mbot.led(i).setColor("aus"); -> Ausgehen soll dieselbe LED \`i\`, nicht immer LED 0. - -runde: - mbot.led(3).setColor(rot); -frage: Soll LED 3 rot schalten. -fehler: 1 -besser: mbot.led(3).setColor("rot"); -> Der Farbname ist ein Text und braucht Anführungszeichen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die LEDs über ihre Nummern 0 bis 4 ansprechen. → Jede LED hat eine Nummer -- Ich kann einzelne LEDs in verschiedenen Farben leuchten lassen. → Drei Lichter im Struktogramm -- Ich kann mit einer Schleife ein Lauflicht bauen. → Jetzt in Java: Lauflicht -::: - -## Wissenstest - -::: quiz -? Welche Nummer hat die erste LED? -→ Jede LED hat eine Nummer -- [x] 0 -- [ ] 1 -- [ ] 5 -> Wie bei Arrays zählt man ab 0. Die fünf LEDs haben die Nummern 0 bis 4. -? Was macht \`mbot.led(4).setColor("aus")\`? -→ Drei Lichter im Struktogramm -- [ ] Alle LEDs gehen aus. -- [x] Nur LED 4 geht aus. -- [ ] Vier LEDs gehen aus. -::: -`,V5=`--- -titel: Der Lautsprecher -bereich: Licht, Ton und Bildschirm -kurz: Töne, eine Tonleiter und eine Sirene. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was die beiden Zahlen bei \`playTone\` bedeuten -- eine Tonfolge bauen -- in Java mit der Zählvariable rechnen -- den mBot2 ein Gefühl zeigen lassen -::: - -## Wie hoch, wie lang? - -::: mbot2 -fokus: cyberpi -ansicht: oben -::: - -Im [CyberPi]{teil=cyberpi} steckt ein kleiner Lautsprecher. \`mbot.playTone(440, 300)\` spielt einen Ton: - -- Die erste Zahl ist die **Tonhöhe** in Hertz (Schwingungen pro Sekunde). Je größer, desto höher. -- Die zweite Zahl ist die **Länge** in Millisekunden. - -| Ton | c | d | e | f | g | a | h | c' | -|---|---|---|---|---|---|---|---|---| -| Hertz | 262 | 294 | 330 | 349 | 392 | 440 | 494 | 523 | - -> In der Simulation hörst du die Töne über die Lautsprecher deines Computers. Dreh ruhig etwas leiser. - -## Tonleiter im Struktogramm - -::: übung vb-tonleiter -titel: Tonleiter -auftrag: Der mBot2 spielt die Töne c, d, e, f, g nacheinander, jeden 300 Millisekunden lang. Die Hertz-Zahlen stehen in der Tabelle. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -tipp: Zieh fünfmal **Ton spielen** ins Struktogramm. -tipp: Die Kästen heißen \`mbot.playTone(262, 300)\`, \`mbot.playTone(294, 300)\` und so weiter. -lösung: vb-tonleiter-loesung.visustruct -::: - -::: angeberwissen -Der Ton a mit 440 Hertz heißt **Kammerton**. Nach ihm stimmen Orchester ihre Instrumente. Eine Oktave höher hat genau doppelt so viele Schwingungen: 880 Hertz. -::: - -## Jetzt in Java: immer höher - -In einer For-Schleife kannst du mit der Zählvariable rechnen. So wird jeder Ton höher als der letzte: - -\`\`\`java -mbot.playTone(i * 200, 200); -\`\`\` - -::: übung vb-java-hoeher -titel: Immer höher -code: java -auftrag: Schreibe in Java eine For-Schleife, in der \`i\` von 1 bis 5 zählt. In jedem Durchgang spielt der mBot2 einen Ton mit \`i * 200\` Hertz, 200 Millisekunden lang. -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -braucht: forSchleife -tipp: \`for (int i = 1; i <= 5; i++) { … }\` -tipp: In der Schleife steht nur \`mbot.playTone(i * 200, 200);\`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Gefühle zeigen - -Der mBot2 kann auch fröhlich, traurig oder müde sein. Mit **Gefühl zeigen** (\`mbot.emotion("smile")\`) spielt er eine kleine Vorführung: Die LEDs leuchten, er macht ein Geräusch und wackelt ein bisschen. - -| Text | Gefühl | -|---|---| -| \`mbot.emotion("smile")\` | lacht | -| \`mbot.emotion("hello")\` | grüßt | -| \`mbot.emotion("sad")\` | ist traurig | -| \`mbot.emotion("angry")\` | ist wütend | -| \`mbot.emotion("dizzy")\` | ist schwindelig | -| \`mbot.emotion("zzz")\` | schläft | - -> Probier es mit dem echten mBot2 aus. Im Simulator siehst du davon nichts, er zeigt nur den Befehl an. Weitere Gefühle stehen unter **Nachschlagen**. - -::: übung vb-fehler-16-lautsprecher -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Immer höher“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-16-lautsprecher-start.visustruct -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es \`mbot.playTone(i * 200, 200)\`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Spiel: Von tief nach hoch - -::: spiel reihenfolge -titel: Spiel: Von tief nach hoch -intro: Tippe die Antworten in der richtigen Reihenfolge an. Ein falscher Tipp beendet die Runde. Fünf Runden. - -runde: -frage: Tippe die Töne von tief nach hoch an. -- \`mbot.playTone(262, 300)\` -- \`mbot.playTone(330, 300)\` -- \`mbot.playTone(440, 300)\` -- \`mbot.playTone(880, 300)\` -> Je mehr Hertz, desto höher der Ton. - -runde: -frage: Tippe die Töne von kurz nach lang an. -- \`mbot.playTone(440, 100)\` -- \`mbot.playTone(440, 300)\` -- \`mbot.playTone(440, 1000)\` -- \`mbot.playTone(440, 2000)\` -> Die zweite Zahl ist die Länge in Millisekunden. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.playTone(i * 200, 200); - } -frage: Tippe die Töne in der Reihenfolge an, in der sie klingen. -- 600 Hertz -- 400 Hertz -- 200 Hertz -> \`i\` zählt rückwärts: 3, 2, 1. Die Töne werden immer tiefer. - -runde: -frage: Tippe die Töne von tief nach hoch an. -- c mit 262 Hertz -- der Kammerton a -- a eine Oktave höher -> Der Kammerton a hat 440 Hertz, eine Oktave höher sind es doppelt so viele: 880 Hertz. - -runde: -frage: Tippe die Zeiten von kurz nach lang an. -- 300 Millisekunden -- 1 Sekunde -- 1500 Millisekunden -- 2 Sekunden -> 1 Sekunde sind 1000 Millisekunden, 2 Sekunden sind 2000. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was die beiden Zahlen bei \`playTone\` bedeuten. → Wie hoch, wie lang? -- Ich kann eine Tonfolge bauen. → Tonleiter im Struktogramm -- Ich kann in Java mit der Zählvariable rechnen. → Jetzt in Java: immer höher -- Ich kann den mBot2 ein Gefühl zeigen lassen. → Gefühle zeigen -::: - -## Wissenstest - -::: quiz -? Welcher Ton ist höher? -→ Wie hoch, wie lang? -- [x] \`mbot.playTone(880, 300)\` -- [ ] \`mbot.playTone(440, 300)\` -> Mehr Hertz heißt höher. -? Wie lange klingt \`mbot.playTone(440, 1000)\`? -→ Wie hoch, wie lang? -- [ ] 440 Sekunden -- [x] 1 Sekunde -- [ ] 1000 Sekunden -> 1000 Millisekunden sind eine Sekunde. -::: -`,q5=`--- -titel: Arrays und Melodien -bereich: Licht, Ton und Bildschirm -kurz: Viele Werte in einem Array speichern und mit einer Schleife abarbeiten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Array anlegen und mit einer Schleife abspielen -- ein Array mit Texten in Java benutzen -- Messwerte in einem Array sammeln und den Mittelwert ausrechnen -::: - -## Eine Melodie als Array - -Eine Melodie ist eine Reihe von Tönen. Statt fünf Variablen \`ton1\`, \`ton2\`, … nimmst du ein **Array**. Ein Array ist eine Reihe von Fächern unter **einem** Namen: - -\`\`\`java -int[] toene = {262, 294, 330, 349, 392}; -\`\`\` - -- \`int[]\` heißt: ein Array aus ganzen Zahlen. -- Die Fächer sind von **0** an nummeriert. \`toene[0]\` ist 262, \`toene[4]\` ist 392. -- \`toene.length\` sagt, wie viele Fächer es gibt, hier 5. - -Eine For-Schleife mit \`i\` von 0 bis \`toene.length - 1\` kommt so an jedes Fach. - -::: übung vb-melodie -titel: Melodie -auftrag: Lege ein Array \`toene\` mit den Tonhöhen 262, 294, 330, 349 und 392 an. Eine For-Schleife spielt sie nacheinander, jeden 300 Millisekunden lang. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -braucht: forSchleife -tipp: \`int[] toene = {262, 294, 330, 349, 392}\` legt das Array an. -tipp: Die Schleife läuft von \`i = 0\` bis \`i < toene.length\`. Darin steht \`mbot.playTone(toene[i], 300)\`. -lösung: vb-melodie-loesung.visustruct -::: - -> Willst du eine andere Melodie, änderst du nur das Array. Die Schleife bleibt gleich. - -## Jetzt in Java: Regenbogen - -Die fünf LEDs haben die Nummern 0 bis 4, genau wie die Fächer eines Arrays mit fünf Werten. Ein Array kann auch Texte enthalten: \`String[] farben = {"rot", "gelb"};\`. LED \`i\` bekommt dann die Farbe aus Fach \`i\`: \`mbot.led(i).setColor(farben[i])\`. - -::: übung vb-farben -titel: Regenbogen -code: java -auftrag: Schreibe in Java: Lege ein Array \`farben\` mit den Texten \`"rot"\`, \`"gelb"\`, \`"grün"\`, \`"blau"\` und \`"lila"\` an. Eine For-Schleife gibt jeder LED \`i\` die Farbe aus \`farben[i]\`. -befehle: ledColor 0 "rot", ledColor 1 "gelb", ledColor 2 "grün", ledColor 3 "blau", ledColor 4 "lila" -braucht: forSchleife -tipp: \`String[] farben = {"rot", "gelb", "grün", "blau", "lila"};\` legt das Array an. -tipp: \`for (int i = 0; i < farben.length; i++) { mbot.led(i).setColor(farben[i]); }\` -lösung: vb-farben-loesung.visustruct -::: - -## Jetzt in Java: Messreihe - -Sensoren messen nie ganz genau. Halte die Hand vor den Ultraschallsensor: Die Zahl springt immer ein bisschen hin und her. Profis messen deshalb mehrmals und rechnen den **Mittelwert** aus. - -Die Messwerte kommen in ein Array. \`new int[5]\` legt ein Array mit fünf leeren Fächern an: - -\`\`\`java -int[] werte = new int[5]; -for (int i = 0; i < werte.length; i++) { - werte[i] = mbot.getDistance(); - mbot.pause(100); -} -\`\`\` - -Danach zählst du alle Werte zusammen. Dafür brauchst du eine Variable, die mit 0 anfängt und in jedem Durchgang wächst: - -\`\`\`java -int summe = 0; -for (int i = 0; i < werte.length; i++) { - summe = summe + werte[i]; -} -\`\`\` - -Der Mittelwert ist die Summe geteilt durch die Anzahl. Schreib \`5.0\` statt \`5\`, dann rechnet Java mit Kommazahlen: \`double mittelwert = summe / 5.0;\` - -::: übung vb-messreihe -titel: Messreihe -code: java -auftrag: Miss den Abstand fünfmal in ein Array \`int[] werte = new int[5]\`, mit 100 ms Pause dazwischen. Zähle danach alle Werte zusammen, rechne den Mittelwert aus und zeige zum Beispiel **Mittelwert: 40.0** auf dem Bildschirm. -messwerte: getDistance=40 -> screen "Mittelwert: 40.0" -messwerte: getDistance=25 -> screen "Mittelwert: 25.0" -braucht: forSchleife -tipp: Zwei For-Schleifen: Die erste füllt das Array mit \`werte[i] = mbot.getDistance();\`, die zweite rechnet \`summe = summe + werte[i];\`. -tipp: Danach \`double mittelwert = summe / 5.0;\` und \`mbot.showText("Mittelwert: " + mittelwert);\`. -lösung: vb-messreihe-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Arrays“](?visustruct&lektion=15-arrays). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-17-arrays-melodie -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Messreihe“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-17-arrays-melodie-start.visustruct -messwerte: getDistance=40 -> screen "Mittelwert: 40.0" -messwerte: getDistance=25 -> screen "Mittelwert: 25.0" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`summe = summe + werte[i]\`. -lösung: vb-messreihe-loesung.visustruct -::: - -## Spiel: Welches Fach? - -::: spiel frage -titel: Spiel: Welches Fach? -intro: Fächer zählen ab 0. Was steht wo im Array? Sechs Runden. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist \`toene[2]\`? -- 294 -- [x] 330 -- 349 -> Fach 0 ist 262, Fach 1 ist 294, Fach 2 ist 330. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist \`toene.length\`? -- 4 -- [x] 5 -- 392 -> \`length\` zählt die Fächer. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was ist \`toene[toene.length - 1]\`? -- 349 -- [x] 392 -- Fehler -> \`toene.length - 1\` ist 4, das letzte Fach. - -runde: - int[] toene = {262, 294, 330, 349, 392}; -frage: Was passiert bei \`mbot.playTone(toene[5], 300)\`? -- Er spielt 392 Hertz. -- Er spielt gar nichts und macht weiter. -- [x] Fehler: Fach 5 gibt es nicht. -> Die Fächer haben die Nummern 0 bis 4. Java meldet \`ArrayIndexOutOfBoundsException\`. - -runde: - String[] farben = {"rot", "gelb", "grün", "blau", "lila"}; - for (int i = 0; i < farben.length; i++) { - mbot.led(i).setColor(farben[i]); - } -frage: Welche Farbe hat LED 3? -- grün -- [x] blau -- lila -> LED 3 bekommt \`farben[3]\`, das vierte Wort. - -runde: - int[] werte = {20, 22, 18, 20, 20}; - int summe = 0; - for (int i = 0; i < werte.length; i++) { - summe = summe + werte[i]; - } - double mittelwert = summe / 5.0; -frage: Welchen Wert hat \`mittelwert\`? -- 18.0 -- [x] 20.0 -- 100.0 -> Die Summe ist 100, und 100 / 5.0 ist 20.0. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - int[] toene = {262, 294, 330, 349, 392}; - for (int i = 0; i <= toene.length; i++) { - mbot.playTone(toene[i], 300); - } -frage: Soll alle fünf Töne spielen. -fehler: 2 -besser: for (int i = 0; i < toene.length; i++) { -> Mit \`<=\` will die Schleife auch \`toene[5]\` spielen. Das Fach gibt es nicht. - -runde: - int[] toene = {262, 294, 330, 349, 392}; - for (int i = 0; i < toene.length; i++) { - mbot.playTone(toene, 300); - } -frage: Soll alle fünf Töne spielen. -fehler: 3 -besser: mbot.playTone(toene[i], 300); -> Gespielt wird ein Fach, \`toene[i]\`, nicht das ganze Array. - -runde: - String[] farben = {"rot", "gelb", "grün", "blau", "lila"}; - for (int i = 0; i < farben.length(); i++) { - mbot.led(i).setColor(farben[i]); - } -frage: Soll jeder LED eine Farbe aus dem Array geben. -fehler: 2 -besser: for (int i = 0; i < farben.length; i++) { -> Bei Arrays steht \`length\` ohne Klammern. Klammern braucht nur \`length()\` bei Strings. - -runde: - int[] werte = new int[5]; - int summe = 0; - for (int i = 0; i < werte.length; i++) { - summe = werte[i]; - } -frage: Soll alle Messwerte zusammenzählen. -fehler: 4 -besser: summe = summe + werte[i]; -> So steht am Ende nur der letzte Wert in \`summe\`. Der neue Wert muss dazukommen. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Array anlegen und mit einer Schleife abspielen. → Eine Melodie als Array -- Ich kann ein Array mit Texten in Java benutzen. → Jetzt in Java: Regenbogen -- Ich kann Messwerte in einem Array sammeln und den Mittelwert ausrechnen. → Jetzt in Java: Messreihe -::: - -## Wissenstest - -::: quiz -? Was ist \`toene[1]\`, wenn \`int[] toene = {262, 294, 330};\`? -→ Eine Melodie als Array -- [ ] 262 -- [x] 294 -- [ ] 1 -> Die Fächer zählen ab 0. Fach 1 ist also das zweite. -? Wie viele Fächer hat \`{262, 294, 330}\`, also was ist \`toene.length\`? -→ Eine Melodie als Array -- [ ] 2 -- [x] 3 -- [ ] 330 -> \`length\` zählt die Fächer, hier drei. Das letzte Fach hat deshalb die Nummer 2. -::: -`,U5=`--- -titel: Der Bildschirm -bereich: Licht, Ton und Bildschirm -kurz: Text, Zahlen und Messwerte auf dem CyberPi anzeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- Text auf dem Bildschirm anzeigen -- mehrere Texte nacheinander zeigen -- Zahlen und Messwerte anzeigen -- einen Countdown in Java schreiben -::: - -## Text anzeigen - -::: mbot2 -fokus: screen -ansicht: hinten -::: - -Der farbige [Bildschirm]{teil=screen} des CyberPi ist auch ein Aktor. Mit **Text anzeigen** schreibst du etwas darauf: - -\`\`\`java -mbot.showText("Hallo!") -\`\`\` - -Der Text steht in Anführungszeichen, wie bei \`System.out.println\`. Ein neuer Text ersetzt den alten. - -> In der Simulation erscheint der Text auf dem Bildschirm des 3D-Modells und unter der Welt hinter 📺. - -## Begrüßung im Struktogramm - -::: übung vb-begruessung -titel: Begrüßung -auftrag: Der mBot2 zeigt zuerst „Hallo!“, wartet 1000 Millisekunden und zeigt dann „Ich bin VisuBot“. -befehle: showText "Hallo!", pause 1000, showText "Ich bin VisuBot" -tipp: Zieh zweimal **Text anzeigen** und einmal **kurz warten** ins Struktogramm. -tipp: \`mbot.showText("Hallo!")\`, \`mbot.pause(1000)\`, \`mbot.showText("Ich bin VisuBot")\`. -lösung: vb-begruessung-loesung.visustruct -::: - -## Zahlen und Messwerte - -Statt Text kannst du auch Zahlen und Variablen anzeigen, und mit \`+\` setzt du alles zusammen: - -\`\`\`java -mbot.showText(i) -mbot.showText("Abstand: " + mbot.getDistance()) -\`\`\` - -So zeigt der mBot2 dir, was seine Sensoren gerade messen. - -## Jetzt in Java: Countdown - -::: übung vb-java-countdown -titel: Countdown -code: java -auftrag: Schreibe in Java einen Countdown: Eine For-Schleife zählt \`i\` von 3 rückwärts bis 1. In jedem Durchgang zeigt der mBot2 \`i\` an und wartet 1000 Millisekunden. Danach zeigt er „Los!“. -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Los!" -braucht: forSchleife -tipp: Rückwärts zählen: \`for (int i = 3; i >= 1; i--) { … }\` -tipp: In der Schleife: \`mbot.showText(i);\` und \`mbot.pause(1000);\`. Nach der Schleife \`mbot.showText("Los!");\`. -lösung: vb-java-countdown-loesung.visustruct -::: - -::: übung vb-fehler-18-bildschirm -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Countdown“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-18-bildschirm-start.visustruct -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Los!" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.showText(i)\`. -lösung: vb-java-countdown-loesung.visustruct -::: - -## Spiel: Was steht auf dem Bildschirm? - -::: spiel frage -titel: Spiel: Was steht auf dem Bildschirm? -intro: Lies den Code und sag vorher, was der Bildschirm am Ende zeigt. Sechs Runden. - -runde: - mbot.showText("Hallo!"); - mbot.pause(1000); - mbot.showText("Ich bin VisuBot"); -frage: Was steht am Ende auf dem Bildschirm? -- Hallo! Ich bin VisuBot -- [x] Ich bin VisuBot -- Hallo! -> Ein neuer Text ersetzt den alten. - -runde: - mbot.showText("Abstand: " + 25); -frage: Was steht auf dem Bildschirm? -- [x] Abstand: 25 -- "Abstand: " + 25 -- Abstand: -> Das \`+\` hängt die Zahl an den Text. - -runde: - int i = 3; - mbot.showText("i"); -frage: Was steht auf dem Bildschirm? -- 3 -- [x] i -- "i" -> In Anführungszeichen ist \`i\` ein Text, keine Variable. Die Anführungszeichen selbst erscheinen nicht. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.showText(i); - mbot.pause(1000); - } - mbot.showText("Start!"); -frage: Welche Zahl steht direkt vor „Start!“ da? -- 0 -- [x] 1 -- 3 -> Der Countdown zeigt 3, 2, 1. Bei 0 ist \`i >= 1\` falsch. - -runde: - mbot.showText("Summe: " + 2 + 3); -frage: Was steht auf dem Bildschirm? -- Summe: 5 -- [x] Summe: 23 -- Summe: 2 + 3 -> Java rechnet von links nach rechts: Erst kommt die 2 an den Text, dann die 3. - -runde: - mbot.showText("Summe: " + (2 + 3)); -frage: Was steht auf dem Bildschirm? -- [x] Summe: 5 -- Summe: 23 -- Summe: (2 + 3) -> Die Klammern rechnet Java zuerst aus. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - mbot.showText("Hallo!"); - mbot.pause(1); - mbot.showText("Ich bin VisuBot"); -frage: Soll „Hallo!“ zeigen, eine Sekunde warten und dann „Ich bin VisuBot“ zeigen. -fehler: 2 -besser: mbot.pause(1000); -> \`pause\` zählt in Millisekunden. Eine Sekunde sind 1000. - -runde: - for (int i = 3; i >= 1; i++) { - mbot.showText(i); - mbot.pause(1000); - } -frage: Countdown: soll 3, 2, 1 zeigen. -fehler: 1 -besser: for (int i = 3; i >= 1; i--) { -> Rückwärts zählen heißt \`i--\`. Mit \`i++\` hört die Schleife nie auf. - -runde: - mbot.showText(Hallo!); -frage: Soll „Hallo!“ zeigen. -fehler: 1 -besser: mbot.showText("Hallo!"); -> Text steht in Anführungszeichen. - -runde: - mbot.showText("Abstand: " - mbot.getDistance()); -frage: Soll zum Beispiel „Abstand: 25“ zeigen. -fehler: 1 -besser: mbot.showText("Abstand: " + mbot.getDistance()); -> Text und Zahl hängst du mit \`+\` aneinander. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann Text auf dem Bildschirm anzeigen. → Text anzeigen -- Ich kann mehrere Texte nacheinander zeigen. → Begrüßung im Struktogramm -- Ich kann Zahlen und Messwerte anzeigen. → Zahlen und Messwerte -- Ich kann einen Countdown in Java schreiben. → Jetzt in Java: Countdown -::: - -## Wissenstest - -::: quiz -? Was zeigt \`mbot.showText("Abstand: " + 25)\`? -→ Zahlen und Messwerte -- [x] Abstand: 25 -- [ ] "Abstand: " + 25 -- [ ] 25 -> Das \`+\` hängt die Zahl an den Text. -? Was passiert mit dem alten Text, wenn du einen neuen anzeigst? -→ Text anzeigen -- [x] Er wird ersetzt. -- [ ] Der neue kommt darunter. -::: -`,Q5=`--- -titel: Zeichenketten -bereich: Licht, Ton und Bildschirm -kurz: Mit Texten rechnen, Buchstaben herausholen und auf dem Bildschirm zeigen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit \`length()\` und \`substring(…)\` arbeiten -- Texte und Zahlen mit \`+\` verbinden -- mit \`charAt(…)\` einzelne Zeichen holen -::: - -## Buchstabe für Buchstabe - -Ein Text heißt in Java **Zeichenkette** oder \`String\`. Er besteht aus einzelnen Zeichen, die wie bei einem Array ab **0** nummeriert sind: - -| Zeichen | V | i | s | u | B | o | t | -|---|---|---|---|---|---|---|---| -| Nummer | 0 | 1 | 2 | 3 | 4 | 5 | 6 | - -- \`wort.length()\` liefert die Anzahl der Zeichen, hier 7. -- \`wort.substring(0, 3)\` liefert die ersten 3 Zeichen: \`"Vis"\`. - -Der mBot2 soll seinen Namen auf dem [Bildschirm]{teil=screen} aufbauen: erst **V**, dann **Vi**, dann **Vis** und so weiter bis **VisuBot**. - -::: übung vb-buchstaben -titel: Name aufbauen -auftrag: Lege \`String wort = "VisuBot"\` an. Eine For-Schleife zählt \`i\` von 1 bis \`wort.length()\`. Darin zeigt der mBot2 \`wort.substring(0, i)\` und wartet 300 Millisekunden. -befehle: showText "V", pause 300, showText "Vi", pause 300, showText "Vis", pause 300, showText "Visu", pause 300, showText "VisuB", pause 300, showText "VisuBo", pause 300, showText "VisuBot", pause 300 -braucht: forSchleife -tipp: Die Schleife beginnt bei \`int i = 1\` und läuft, solange \`i <= wort.length()\`. -tipp: In der Schleife: \`mbot.showText(wort.substring(0, i))\` und \`mbot.pause(300)\`. -lösung: vb-buchstaben-loesung.visustruct -::: - -## Jetzt in Java: Text und Länge - -Mit \`+\` hängst du Texte und Zahlen aneinander. So zeigt der mBot2 einen Text und dahinter seine Länge. - -::: übung vb-java-textlaenge -titel: Wie lang ist der Text? -code: java -auftrag: Schreibe in Java: Lege \`String text = "Hallo Klasse"\` an. Der mBot2 zeigt den Text und in Klammern seine Länge, also **Hallo Klasse (12)**. -befehle: showText "Hallo Klasse (12)" -tipp: Die Länge liefert \`text.length()\`. Auch das Leerzeichen zählt mit. -tipp: \`mbot.showText(text + " (" + text.length() + ")");\` -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Jetzt in Java: Laufschrift - -\`wort.charAt(i)\` liefert das Zeichen mit der Nummer \`i\`. Zeigst du die Zeichen nacheinander mit einer Pause, läuft das Wort Buchstabe für Buchstabe über den Bildschirm: - -\`\`\`java -String wort = "Ludwig"; -for (int i = 0; i < wort.length(); i++) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); -} -\`\`\` - -| Durchgang | \`i\` | Bildschirm | -|---|---|---| -| 1 | 0 | L | -| 2 | 1 | u | -| 3 | 2 | d | - -::: übung vb-laufschrift -titel: Ludwig rückwärts -code: java -auftrag: Lege \`String wort = "Ludwig"\` an. Zeige das Wort **rückwärts** Buchstabe für Buchstabe auf dem Bildschirm, also g, i, w, d, u, L, mit 500 ms Pause nach jedem Buchstaben. -befehle: showText "g", pause 500, showText "i", pause 500, showText "w", pause 500, showText "d", pause 500, showText "u", pause 500, showText "L", pause 500 -braucht: forSchleife -tipp: Das letzte Zeichen hat die Nummer \`wort.length() - 1\`. Die Schleife zählt rückwärts: \`for (int i = wort.length() - 1; i >= 0; i--)\`. -tipp: In der Schleife: \`mbot.showText(wort.charAt(i));\` und \`mbot.pause(500);\`. -lösung: vb-laufschrift-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Zeichenketten“](?visustruct&lektion=17-zeichenketten). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-19-zeichenketten -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Wie lang ist der Text?“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-19-zeichenketten-start.visustruct -befehle: showText "Hallo Klasse (12)" -tipp: Die Fehlermeldung nennt eine unbekannte Variable. Wie heißt die Variable dort, wo sie angelegt wird? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.showText(text +\`. -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Lies, was der mBot2 zeigen soll, und tippe auf die falsche Zeile, bevor die Zeit abläuft. Fünf Runden, je 30 Sekunden. -zeit: 30 - -runde: - String wort = "VisuBot"; - for (int i = 1; i <= wort.length(); i++) { - mbot.showText(wort.substring(1, i)); - mbot.pause(300); - } -frage: Soll V, Vi, Vis und so weiter bis VisuBot zeigen. -fehler: 3 -besser: mbot.showText(wort.substring(0, i)); -> Das erste Zeichen hat die Nummer 0. Mit \`substring(1, i)\` fehlt das V. - -runde: - String text = "Hallo Klasse"; - mbot.showText(text + " (" + text.length + ")"); -frage: Soll **Hallo Klasse (12)** zeigen. -fehler: 2 -besser: mbot.showText(text + " (" + text.length() + ")"); -> Bei einem String ist \`length()\` eine Methode und braucht Klammern. Nur bei Arrays steht \`length\` ohne Klammern. - -runde: - String wort = "Ludwig"; - for (int i = 0; i <= wort.length(); i++) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); - } -frage: Soll L, u, d, w, i, g zeigen. -fehler: 2 -besser: for (int i = 0; i < wort.length(); i++) { -> Das letzte Zeichen hat die Nummer 5. Bei \`i = 6\` gibt es kein Zeichen mehr. - -runde: - String wort = "Ludwig"; - for (int i = wort.length(); i >= 0; i--) { - mbot.showText(wort.charAt(i)); - mbot.pause(500); - } -frage: Soll das Wort rückwärts zeigen: g, i, w, d, u, L. -fehler: 2 -besser: for (int i = wort.length() - 1; i >= 0; i--) { -> Rückwärts beginnt man beim letzten Zeichen, und das hat die Nummer \`wort.length() - 1\`. - -runde: - String name = 'Ludwig'; - mbot.showText("Hallo " + name); -frage: Soll **Hallo Ludwig** zeigen. -fehler: 1 -besser: String name = "Ludwig"; -> Texte stehen in doppelten Anführungszeichen. Einfache gibt es nur für ein einzelnes Zeichen (\`char\`). -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 6 1 LAUFSCHRIFT: ein Wort Buchstabe für Buchstabe über den Bildschirm -- ↓ 4 1 BILDSCHIRM: zeigt mit mbot.showText einen Text -- ↓ 6 7 HERTZ: Einheit der Tonhöhe: Schwingungen pro Sekunde -- ↓ 4 11 AKTOR: Bauteil, das etwas tut, z. B. leuchten -- ↓ 4 3 LAUTSPRECHER: spielt mit mbot.playTone einen Ton -- ↓ 4 9 ZEICHENKETTE: anderes Wort für String -- ↓ 1 5 MILLISEKUNDE: ein Tausendstel einer Sekunde -- → 15 1 ARRAY: Reihe von Fächern unter einem Namen -lösung: 6 2, 8 5, 9 7, 7 11, 10 3 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit \`length()\` und \`substring(…)\` arbeiten. → Buchstabe für Buchstabe -- Ich kann Texte und Zahlen mit \`+\` verbinden. → Jetzt in Java: Text und Länge -- Ich kann mit \`charAt(…)\` einzelne Zeichen holen. → Jetzt in Java: Laufschrift -::: - -## Wissenstest - -::: quiz -? Was liefert \`"VisuBot".substring(0, 4)\`? -→ Buchstabe für Buchstabe -- [ ] \`"VisuB"\` -- [x] \`"Visu"\` -- [ ] \`"uBot"\` -> \`substring(0, 4)\` nimmt die Zeichen 0, 1, 2 und 3, also vier Stück. -? Was liefert \`"Ludwig".charAt(0)\`? -→ Jetzt in Java: Laufschrift -- [x] \`L\` -- [ ] \`u\` -- [ ] \`g\` -> Die Zeichen werden ab 0 gezählt. Zeichen 0 ist das erste. -::: -`,$5=`--- -titel: Motoren und Geschwindigkeit -bereich: Motoren und Methoden -kurz: Gas geben, lenken und einer Linie folgen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- mit Räder einzeln Gas geben und anhalten -- fahren, bis ein Sensor etwas meldet -- einen Linienfolger in Java schreiben -::: - -## Zwei Motoren, zwei Räder - -::: mbot2 -fokus: motors, wheels -ansicht: unten -::: - -Der mBot2 hat zwei [Motoren]{teil=motors}, jeder treibt ein [Rad]{teil=wheels} an. Bisher hast du **fahre vor** und **drehe rechts** benutzt. Diese Befehle fahren eine feste Strecke und warten, bis der mBot2 angekommen ist. - -Mit **Räder einzeln** (\`mbot.drive(links, rechts)\`) gibst du stattdessen Gas: Du sagst, wie stark sich jedes Rad dreht, in Prozent von −100 bis 100. Der mBot2 fährt dann **weiter**, bis du ihm etwas anderes sagst. Mit **anhalten** (\`mbot.stop()\`) bleibt er stehen. - -| Befehl | Was passiert? | -|---|---| -| \`mbot.drive(40, 40)\` | beide Räder gleich schnell: geradeaus | -| \`mbot.drive(-40, -40)\` | rückwärts | -| \`mbot.drive(10, 40)\` | rechts schneller: Kurve nach links | -| \`mbot.drive(40, -40)\` | Räder gegeneinander: dreht auf der Stelle nach rechts | -| \`mbot.stop()\` | anhalten | - -> Im Simulator fährt \`mbot.drive(100, 100)\` 20 cm pro Sekunde. Probier es in der [Simulation](#zeige-simulation) aus. - -## Gas geben bis zur Wand im Struktogramm - -In Lektion 5 ist der mBot2 in 10-cm-Schritten bis zur Wand gefahren. Mit **Räder einzeln** geht das ohne Ruckeln: einmal Gas geben, dann warten, bis die Wand nah ist, dann anhalten. - -::: übung vb-gas-bis-wand -titel: Gas geben bis zur Wand -auftrag: Der mBot2 gibt mit 50 % Gas. Solange die Wand mehr als 15 cm entfernt ist, wartet er kurz. Danach hält er an. Es gibt drei Welten. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Ganz oben steht \`mbot.drive(50, 50)\`. Darunter eine While-Schleife mit der Bedingung \`mbot.getDistance() > 15\`. -tipp: In der Schleife steht nur **kurz warten**. Der mBot2 fährt dabei weiter! Nach der Schleife steht **anhalten**. -lösung: vb-gas-bis-wand-loesung.visustruct -::: - -## Der Linienfolger - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Ein Klassiker: Der mBot2 folgt einer schwarzen Linie auf dem Boden. Dafür schaut er auf die beiden mittleren Fühler des [Liniensensors]{teil=lineSensor}: - -| Sensor | Text | -|---|---| -| linker mittlerer Fühler sieht Schwarz | \`mbot.isLineLeft()\` | -| rechter mittlerer Fühler sieht Schwarz | \`mbot.isLineRight()\` | - -Die Idee: Sehen **beide** die Linie, fährt er geradeaus. Sieht nur der **linke** sie, ist die Linie nach links gewandert: Er lenkt nach links, also dreht sich das rechte Rad schneller. Sieht nur der **rechte** sie, lenkt er nach rechts. - -## Jetzt in Java: Linienfolger - -::: übung vb-java-linienfolger -titel: Linienfolger -code: java -auftrag: Schreibe in Java einen Linienfolger. Solange die Wand mehr als 15 cm entfernt ist, prüft der mBot2 seine beiden Fühler und lenkt. Danach hält er an. In der ersten Welt biegt die Linie nach rechts ab, in der zweiten nach links. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: \`while (mbot.getDistance() > 15) { … }\` und danach \`mbot.stop();\`. -tipp: In der Schleife: \`if (mbot.isLineLeft() && mbot.isLineRight()) { mbot.drive(40, 40); } else if (mbot.isLineLeft()) { mbot.drive(10, 40); } else if (mbot.isLineRight()) { mbot.drive(40, 10); }\` -lösung: vb-java-linienfolger-loesung.visustruct -::: - -> Am echten mBot2 klebst du die Linie mit schwarzem Isolierband auf einen hellen Boden. Fahr anfangs langsam, sonst schießt er in der Kurve über die Linie hinaus. - -::: übung vb-fehler-20-motoren -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Linienfolger“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-20-motoren-start.visustruct -welt: linie-1, linie-2 -tipp: Lies die Fehlermeldung genau. In welchem Kasten bleibt die Simulation stehen? Java unterscheidet Groß- und Kleinschreibung. -tipp: Richtig heißt es \`mbot.stop()\`. -lösung: vb-java-linienfolger-loesung.visustruct -::: - -## Spiel: Wohin fährt er? - -::: spiel frage -titel: Spiel: Wohin fährt er? -intro: Links und rechts Gas in Prozent: Wohin fährt der mBot2? Sieben Runden. -antworten: geradeaus | rückwärts | Kurve nach links | Kurve nach rechts | dreht sich auf der Stelle - -runde: - mbot.drive(40, 40); -frage: Wohin fährt er? -richtig: geradeaus -> Beide Räder drehen sich gleich schnell. - -runde: - mbot.drive(-40, -40); -frage: Wohin fährt er? -richtig: rückwärts -> Minus heißt rückwärts, und beide Räder sind gleich. - -runde: - mbot.drive(10, 40); -frage: Wohin fährt er? -richtig: Kurve nach links -> Das rechte Rad ist schneller, also lenkt er nach links. - -runde: - mbot.drive(40, 10); -frage: Wohin fährt er? -richtig: Kurve nach rechts -> Das linke Rad ist schneller, also lenkt er nach rechts. - -runde: - mbot.drive(40, -40); -frage: Wohin fährt er? -richtig: dreht sich auf der Stelle -> Ein Rad vor, eins zurück: Er dreht sich nach rechts, ohne vom Fleck zu kommen. - -runde: - mbot.drive(-40, 40); -frage: Wohin fährt er? -richtig: dreht sich auf der Stelle -> Diesmal dreht er sich nach links. - -runde: - if (mbot.isLineLeft() && !mbot.isLineRight()) { - ... - } -frage: Linienfolger: Nur der linke Fühler sieht die Linie. Was gehört in die Lücke? -- \`mbot.drive(40, 10);\` -- [x] \`mbot.drive(10, 40);\` -- \`mbot.stop();\` -> Die Linie ist nach links gewandert. Er lenkt nach links, also dreht sich das rechte Rad schneller. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann mit Räder einzeln Gas geben und anhalten. → Zwei Motoren, zwei Räder -- Ich kann fahren, bis ein Sensor etwas meldet. → Gas geben bis zur Wand im Struktogramm -- Ich kann einen Linienfolger in Java schreiben. → Jetzt in Java: Linienfolger -::: - -## Wissenstest - -::: quiz -? Was macht \`mbot.drive(30, 30)\`? -→ Zwei Motoren, zwei Räder -- [ ] Er fährt 30 cm und hält an. -- [x] Er fährt geradeaus und hört nicht auf, bis ein anderer Fahrbefehl kommt. -- [ ] Er dreht sich um 30°. -> **Räder einzeln** gibt nur Gas. Anhalten musst du selbst. -? Der mBot2 soll nach rechts lenken. Welcher Befehl passt? -→ Zwei Motoren, zwei Räder -- [ ] \`mbot.drive(10, 40)\` -- [x] \`mbot.drive(40, 10)\` -- [ ] \`mbot.drive(-40, -40)\` -> Dreht sich das linke Rad schneller, fährt er eine Rechtskurve. -::: -`,Y5=`--- -titel: Stoppuhr und Akku -bereich: Motoren und Methoden -kurz: Zeit messen und den Akku im Blick behalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- die Stoppuhr auf 0 setzen und ablesen -- eine bestimmte Zeit lang fahren -- den Akkustand anzeigen -::: - -## Die Stoppuhr - -::: mbot2 -fokus: cyberpi -ansicht: oben -::: - -Im [CyberPi]{teil=cyberpi} läuft eine Stoppuhr. Sie zählt die Sekunden, seit das Programm läuft. Mit **Stoppuhr auf 0** (\`mbot.resetTimer()\`) fängt sie wieder bei 0 an. - -| Sensor oder Befehl | Text | Wert | -|---|---|---| -| Stoppuhr auf 0 | \`mbot.resetTimer()\` | | -| Stoppuhr | \`mbot.getTimer()\` | Sekunden mit Nachkommastelle, z. B. \`1.5\` | - -So fährst du eine bestimmte Zeit lang: Stoppuhr auf 0, Gas geben, warten, solange die Stoppuhr noch nicht weit genug ist, anhalten. - -## Zwei Sekunden fahren im Struktogramm - -::: übung vb-zwei-sekunden -titel: Zwei Sekunden -auftrag: Der mBot2 setzt die Stoppuhr auf 0 und gibt mit 50 % Gas. Solange die Stoppuhr unter 2 Sekunden ist, wartet er kurz. Danach hält er an. Mit 50 % schafft er 10 cm pro Sekunde, er muss also 20 cm weiter stehen bleiben. -welt: zeit-1 -braucht: whileSchleife -tipp: Oben stehen **Stoppuhr auf 0** und \`mbot.drive(50, 50)\`. Darunter eine While-Schleife mit \`mbot.getTimer() < 2\`. -tipp: In der Schleife steht **kurz warten**, nach der Schleife **anhalten**. -lösung: vb-zwei-sekunden-loesung.visustruct -::: - -## Der Akku - -::: mbot2 -fokus: shield -ansicht: oben -::: - -Unter dem durchsichtigen [Shield]{teil=shield} siehst du den Akku. Wie voll er ist, liefert \`mbot.getBattery()\` in Prozent: \`100\` ist ganz voll. Ist er fast leer, fahren die Motoren schwächer. Dann wird es Zeit zum Laden. - -::: angeberwissen -Auch dein Handy misst seinen Akku nicht direkt in Prozent. Es misst die Spannung: Ein voller Lithium-Akku hat etwas über 4 Volt, ein leerer gut 3. Daraus rechnet es die Prozente aus, genau wie der CyberPi. -::: - -## Jetzt in Java: Akku-Anzeige - -::: übung vb-java-akku -titel: Akku-Anzeige -code: java -auftrag: Schreibe in Java: Der mBot2 zeigt auf dem Bildschirm zum Beispiel **Akku: 80 %**. Ist der Akku unter 20 %, leuchten zusätzlich die LEDs rot. -messwerte: getBattery=80 -> none, ledOff, screen "Akku: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Akku: 15 %" -braucht: verzweigung -tipp: \`mbot.showText("Akku: " + mbot.getBattery() + " %");\` setzt den Text aus drei Teilen zusammen. -tipp: Danach: \`if (mbot.getBattery() < 20) { mbot.setLed(255, 0, 0); }\` -lösung: vb-java-akku-loesung.visustruct -::: - -::: übung vb-fehler-21-stoppuhr-akku -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Akku-Anzeige“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-21-stoppuhr-akku-start.visustruct -messwerte: getBattery=80 -> none, ledOff, screen "Akku: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Akku: 15 %" -tipp: Zähle die Zahlen in der Klammer. Wie viele braucht der Befehl? Das steht unter **Nachschlagen**. -tipp: Richtig heißt es \`mbot.setLed(255, 0, 0)\`. -lösung: vb-java-akku-loesung.visustruct -::: - -## Spiel: Stoppuhr und Akku - -::: spiel frage -titel: Spiel: Stoppuhr und Akku -intro: Lies den Code und sag vorher, was passiert. Fünf Runden. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: Wie lange fährt der mBot2? -- 0,1 Sekunden -- [x] 2 Sekunden -- bis zur Wand -> Er wartet, bis die Stoppuhr 2 Sekunden zeigt, dann hält er an. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: Mit 50 % schafft er 10 cm pro Sekunde. Wie weit fährt er? -- 10 cm -- [x] 20 cm -- 50 cm -> 2 Sekunden mal 10 cm pro Sekunde sind 20 cm. - -runde: - mbot.pause(3000); - mbot.resetTimer(); - mbot.pause(1000); - mbot.showText(mbot.getTimer()); -frage: Welche Zahl zeigt der Bildschirm ungefähr? -- 0 -- [x] 1 -- 4 -> Die Stoppuhr fängt nach \`mbot.resetTimer()\` wieder bei 0 an. Danach vergeht nur noch 1 Sekunde. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Akku: " + mbot.getBattery() + " %"); -frage: Der Akku hat noch 15 %. Was passiert? -- Nur der Bildschirm zeigt **Akku: 15 %**. -- Nur die LEDs leuchten rot. -- [x] Der Bildschirm zeigt **Akku: 15 %**, und die LEDs leuchten rot. -> 15 ist kleiner als 20. Die Anzeige steht unter der Verzweigung und kommt immer. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Akku: " + mbot.getBattery() + " %"); -frage: Der Akku hat 80 %. Was passiert? -- [x] Nur der Bildschirm zeigt **Akku: 80 %**. -- Nur die LEDs leuchten rot. -- Gar nichts. -> 80 ist nicht kleiner als 20, die LEDs bleiben aus. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann die Stoppuhr auf 0 setzen und ablesen. → Die Stoppuhr -- Ich kann eine bestimmte Zeit lang fahren. → Zwei Sekunden fahren im Struktogramm -- Ich kann den Akkustand anzeigen. → Jetzt in Java: Akku-Anzeige -::: - -## Wissenstest - -::: quiz -? Was liefert \`mbot.getTimer()\` direkt nach \`mbot.resetTimer()\`? -→ Die Stoppuhr -- [x] 0 -- [ ] 100 -- [ ] Die Uhrzeit -> Die Stoppuhr fängt wieder bei 0 an. -? Warum wartet der mBot2 in der Schleife nur **kurz** und hält nicht an? -→ Zwei Sekunden fahren im Struktogramm -- [ ] Weil die Schleife sonst nicht startet. -- [x] Weil er weiterfahren soll, bis die Zeit um ist. -- [ ] Weil die Stoppuhr sonst stehen bleibt. -> Gas geben gilt weiter, auch während er wartet. -::: -`,K5=`--- -titel: Zustände: Was mache ich gerade? -bereich: Motoren und Methoden -kurz: Mit einer Zustandsvariablen und einer Fallauswahl eine Robo-Ampel weiterschalten. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- erklären, was ein Zustand ist -- mit einer Fallauswahl je nach Zustand handeln und in den nächsten Zustand wechseln -- Zustände in Java mit Namen und \`.equals\` unterscheiden -::: - -## Was ist ein Zustand? - -Eine Ampel ist immer in genau einem **Zustand**: rot, grün oder gelb. In jedem Zustand weiß sie zwei Dinge: Was zeige ich? Und was kommt danach? - -Unsere Robo-Ampel hat drei Zustände. Jeder bekommt eine Nummer: - -| Zustand | LEDs | danach | -|---|---|---| -| 0 | rot | 1 | -| 1 | grün | 2 | -| 2 | gelb | 0 | - -Den aktuellen Zustand speicherst du in einer Variablen: \`int zustand = 0\`. So eine Variable heißt **Zustandsvariable**. Ein Programm, das so von Zustand zu Zustand springt, heißt **Zustandsautomat**. - -> Eine echte Ampel in Deutschland hat vier Zustände: rot, rot-gelb, grün, gelb. Unsere Robo-Ampel lässt rot-gelb weg. - -::: angeberwissen -Auch ein Saugroboter ist ein Zustandsautomat: saugen, ausweichen, zur Ladestation fahren. Ich habe nur zwei Zustände: fressen und schlafen. -::: - -## Die Robo-Ampel im Struktogramm - -Die Fallauswahl passt perfekt: Für jeden Zustand gibt es einen Fall. In jedem Fall stehen zwei Kästen: Was tue ich? Und welcher Zustand kommt als Nächstes? - -::: übung vb-ampel-zustand -titel: Robo-Ampel -auftrag: Baue ein Struktogramm: Lege \`int zustand = 0\` an. Eine For-Schleife läuft sechsmal. In ihr steht eine Fallauswahl über \`zustand\`. Fall 0: LEDs rot, \`zustand = 1\`. Fall 1: LEDs grün, \`zustand = 2\`. Sonst: LEDs gelb, \`zustand = 0\`. Nach der Fallauswahl wartet der mBot2 eine Sekunde. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, fallauswahl -tipp: Die Fallauswahl findest du bei den Elementen. Schreib \`zustand\` hinein. Die Fälle heißen 0, 1 und sonst (default). -tipp: Die Farben sind \`mbot.setLed(255, 0, 0)\` für rot, \`mbot.setLed(0, 255, 0)\` für grün und \`mbot.setLed(255, 200, 0)\` für gelb. Unter der Fallauswahl, noch in der Schleife, steht \`mbot.pause(1000)\`. -lösung: vb-ampel-zustand-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Fallauswahl“](?visustruct&lektion=10-fallauswahl). Sie öffnet sich in einem neuen Fenster. - -## Jetzt in Java: Zustände mit Namen - -Nummern sind schnell vergessen. Was war noch mal Zustand 2? Lesbarer sind Namen. Dann ist der Zustand ein Text: - -\`\`\`java -String zustand = "rot"; -... -if (zustand.equals("rot")) { - mbot.setLed(255, 0, 0); - zustand = "grün"; -} else if (zustand.equals("grün")) { - ... -\`\`\` - -Texte vergleichst du in Java mit \`.equals("…")\`, nicht mit \`==\`. - -::: übung vb-java-ampel-text -titel: Ampel mit Namen -code: java -auftrag: Schreibe die Robo-Ampel in Java mit \`String zustand = "rot";\`. Prüfe mit \`if\` und \`else if\`, in welchem Zustand die Ampel ist: rot, dann grün, dann gelb, dann wieder rot. Die Schleife läuft sechsmal, nach jedem Durchgang 1 Sekunde Pause. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, verzweigung -tipp: In der Schleife: \`if (zustand.equals("rot")) { … } else if (zustand.equals("grün")) { … } else { … }\` und danach \`mbot.pause(1000);\`. -tipp: Im Fall rot stehen \`mbot.setLed(255, 0, 0);\` und \`zustand = "grün";\`. Grün schaltet auf \`"gelb"\`, gelb wieder auf \`"rot"\`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -::: übung vb-fehler-21-zustaende -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Ampel mit Namen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-21-zustaende-start.visustruct -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es \`zustand = "grün"\`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -## Zustände beim Fahren - -Mit Zuständen kann der mBot2 auch fahren. Im Zustand **fahren** fährt er vor. Sieht er eine Wand, wechselt er in den Zustand **drehen**. Dort dreht er sich einmal und wechselt zurück zu **fahren**. - -::: diagramm vb-l21z-ausweichen.visustruct -Fahren und Ausweichen mit zwei Zuständen. Öffne das Diagramm im Editor und probiere es in einer Welt aus. -::: - -## Spiel: Welcher Zustand kommt jetzt? - -::: spiel frage -titel: Spiel: Welcher Zustand kommt jetzt? -intro: Die Robo-Ampel schaltet: 0 rot → 1 grün → 2 gelb → 0 rot. Welcher Zustand kommt als Nächstes? Fünf Runden. -frage: Welcher Zustand kommt danach? -antworten: 0 | 1 | 2 - -runde: -frage: Die Ampel ist gerade in Zustand **0** (rot). -richtig: 1 -> Nach rot kommt grün. - -runde: -frage: Die Ampel ist gerade in Zustand **2** (gelb). -richtig: 0 -> Nach gelb geht es wieder von vorn los. - -runde: -frage: Die Ampel ist gerade in Zustand **1** (grün). -richtig: 2 -> Nach grün kommt gelb. - -runde: -frage: Die Ampel startet in Zustand 0 und schaltet **zweimal** weiter. -richtig: 2 -> 0 → 1 → 2. - -runde: -frage: Die Ampel startet in Zustand 0 und schaltet **dreimal** weiter. -richtig: 0 -> 0 → 1 → 2 → 0. Nach drei Schritten ist sie wieder am Anfang. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann erklären, was ein Zustand ist. → Was ist ein Zustand? -- Ich kann mit einer Fallauswahl je nach Zustand handeln und in den nächsten Zustand wechseln. → Die Robo-Ampel im Struktogramm -- Ich kann Zustände in Java mit Namen und \`.equals\` unterscheiden. → Jetzt in Java: Zustände mit Namen -::: - -## Wissenstest - -::: quiz -? Was speichert eine Zustandsvariable? -→ Was ist ein Zustand? -- [ ] Wie oft die Schleife gelaufen ist -- [x] Was das Programm gerade macht, zum Beispiel rot oder grün -- [ ] Den größten Messwert -? Was steht in jedem Fall der Fallauswahl? -→ Die Robo-Ampel im Struktogramm -- [x] Was zu tun ist und welcher Zustand als Nächstes kommt -- [ ] Nur eine Pause -- [ ] Eine neue Schleife -? Wie prüfst du in Java, ob \`zustand\` den Text \`"rot"\` enthält? -→ Jetzt in Java: Zustände mit Namen -- [ ] \`zustand == rot\` -- [x] \`zustand.equals("rot")\` -- [ ] \`zustand = "rot"\` -> Texte vergleichst du mit \`.equals\`. Ein einzelnes \`=\` würde \`"rot"\` zuweisen. -::: -`,X5=`--- -titel: Eigene Methoden -bereich: Motoren und Methoden -kurz: Eigene Befehle mit Parametern und Rückgabewert schreiben und aufrufen. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- ein Unterprogramm mit Parameter bauen und aufrufen -- Methoden in Java erkennen -- eine überladene Methode erkennen und aufrufen -- eine Methode mit Rückgabewert schreiben -::: - -## Ein Unterprogramm für das Quadrat - -Ein Quadrat fahren kann der mBot2 schon. Jetzt soll er zwei verschieden große Quadrate fahren. Statt die Schleife zweimal zu bauen, schreibst du ein **Unterprogramm**. In Java heißt es **Methode**. Es ist ein eigener Befehl, den du so oft aufrufen kannst, wie du willst. - -Die Kopfzeile \`void quadrat(int seite)\` sagt: - -- \`void\`: Die Methode liefert kein Ergebnis zurück, sie **tut** nur etwas. -- \`quadrat\`: So heißt dein neuer Befehl. -- \`int seite\`: Beim Aufruf gibst du eine Zahl mit, den **Parameter**. - -::: übung vb-quadrat-methode -titel: Zwei Quadrate -auftrag: Baue ein Unterprogramm \`void quadrat(int seite)\`, das ein Quadrat mit der Seitenlänge \`seite\` fährt. Das Hauptprogramm ruft es zweimal auf: erst mit 30, dann mit 50. -fahrt: 4 x (forward 30, turnRight), 4 x (forward 50, turnRight) -braucht: aufruf -tipp: Lege mit **+** einen neuen Reiter an und gib ihm den Titel \`void quadrat(int seite)\`. Darin steht eine For-Schleife, die viermal \`mbot.forward(seite)\` und \`mbot.turnRight()\` ausführt. -tipp: Im Hauptprogramm stehen zwei Aufruf-Kästen: \`quadrat(30)\` und \`quadrat(50)\`. -lösung: vb-quadrat-methode-loesung.visustruct -unterprogramm: vb-quadrat-unterprogramm-loesung.visustruct -::: - -## Methoden in Java - -In Java steht jede Methode in einer Klasse. Das Programm beginnt immer in der Methode \`main\`. Von dort wird \`quadrat\` aufgerufen: - -\`\`\`java -public class Quadrate { - public static void main(String[] args) { - quadrat(30); - quadrat(50); - } - - static void quadrat(int seite) { - for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); - } - } -} -\`\`\` - -Im Bereich [Code](#zeige-codelive) siehst du jede Methode in ihrem eigenen Reiter, so wie im Struktogramm. - -::: quiz -? Was bedeutet \`void\` in \`void quadrat(int seite)\`? -→ Methoden in Java -- [ ] Die Methode hat keinen Parameter. -- [x] Die Methode liefert kein Ergebnis zurück. -- [ ] Die Methode darf nur einmal aufgerufen werden. -> \`void\` heißt „leer“: Es kommt kein Wert zurück. Der Parameter steht in den Klammern. -? Mit welchem Wert für \`seite\` läuft \`quadrat(50)\`? -→ Ein Unterprogramm für das Quadrat -- [ ] 30 -- [x] 50 -- [ ] mit keinem -> Der Wert in den Klammern des Aufrufs wird zum Parameter \`seite\`. -::: - -## Gleicher Name, andere Parameter: Überladung - -Du kennst \`mbot.forward()\` und \`mbot.forward(30)\`. Das sind **zwei verschiedene Methoden mit demselben Namen**. Java erkennt am Aufruf, welche gemeint ist: an der **Anzahl** und am **Typ** der Parameter. Das nennt man **Überladung** (englisch *overloading*). - -Es gibt noch eine dritte \`forward\`-Methode. Sie hat zwei Parameter: eine Zahl und eine **Einheit** als Text. - -\`\`\`java -mbot.forward(); // 10 cm (Standardwert) -mbot.forward(30); // 30 cm -mbot.forward(30, "cm"); // 30 cm, Einheit ausdrücklich -mbot.forward(2, "Umdrehungen"); // die Räder drehen sich zweimal -\`\`\` - -Eine Radumdrehung ist so weit wie der Umfang des Rads: π · 6,5 cm ≈ 20,4 cm. Zwei Umdrehungen sind also etwa 40,8 cm. Für \`mbot.backward\` gibt es dieselben drei Varianten. - -So sehen die drei Kopfzeilen aus. Der Methodenname ist gleich, die Parameterlisten sind verschieden: - -\`\`\`java -void forward() -void forward(int cm) -void forward(int wert, String einheit) -\`\`\` - -Überladung kennst du schon: \`System.out.println\` gibt es für \`int\`, für \`double\`, für \`String\` und noch mehr. Deshalb kannst du jeden dieser Werte einfach in die Klammern schreiben. - -::: angeberwissen -Stimmen die 20,4 cm bei **deinem** mBot2 wirklich? Profis glauben keiner Zahl, die sie nicht nachgemessen haben. Miss den Raddurchmesser und lass \`mbot.forward(5, "Umdrehungen")\` fahren: Soll sind 102,1 cm. So testet man Softwaresysteme: Jeder **Testfall** hat ein erwartetes Ergebnis (**Soll**) und ein gemessenes (**Ist**). Man testet mehrmals und schreibt alles in ein **Protokoll**. Hier ist dein [Messprotokoll zum Ausdrucken (PDF)](https://visubot.visucode.de/messprotokoll-radumdrehung.pdf). Gib es deiner Lehrkraft, dann sieht sie, ob alle Roboter gleich weit fahren. -::: - -::: übung vb-ueberladung-umdrehung -titel: Quadrat mit Umdrehungen -auftrag: Fahre ein Quadrat, bei dem jede Seite genau **eine Radumdrehung** lang ist. Nimm dafür die überladene Methode \`mbot.forward(1, "Umdrehungen")\`. -fahrt: 4 x (forward 20.42, turnRight) -braucht: forSchleife -tipp: Eine For-Schleife mit \`int i = 0\`, \`i < 4\`, \`i++\`. -tipp: Im Inhalt stehen \`mbot.forward(1, "Umdrehungen")\` und \`mbot.turnRight()\`. -lösung: vb-ueberladung-umdrehung-loesung.visustruct -::: - -::: übung vb-java-rechteck-umdrehungen -titel: Rechteck mit Umdrehungen -code: java -auftrag: Schreibe in Java: Der mBot2 fährt ein Rechteck. Die langen Seiten sind **zwei** Radumdrehungen lang, die kurzen **eine**. Benutze \`mbot.forward(2, "Umdrehungen")\` und \`mbot.forward(1, "Umdrehungen")\`. -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -braucht: forSchleife -tipp: Eine For-Schleife, die zweimal läuft: \`for (int i = 0; i < 2; i++) { … }\`. -tipp: In der Schleife: \`mbot.forward(2, "Umdrehungen");\`, \`mbot.turnRight();\`, \`mbot.forward(1, "Umdrehungen");\`, \`mbot.turnRight();\` -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -::: quiz -? Welche Methode ruft \`mbot.forward(3, "Umdrehungen")\` auf? -→ Gleicher Name, andere Parameter: Überladung -- [ ] \`forward()\` -- [ ] \`forward(int cm)\` -- [x] \`forward(int wert, String einheit)\` -> Java wählt die Variante, deren Parameter passen: zwei Parameter, eine Zahl und ein Text. -::: - -## Methode mit Rückgabe - -Eine Methode kann auch etwas **ausrechnen** und das Ergebnis zurückgeben. Statt \`void\` steht dann der Datentyp des Ergebnisses in der Kopfzeile, und \`return\` gibt den Wert zurück: - -\`\`\`java -static int zentimeter(int umdrehungen) { - return umdrehungen * 20; -} -\`\`\` - -Eine Radumdrehung sind rund 20 cm. Der Aufruf \`int strecke = zentimeter(3);\` legt also 60 in \`strecke\`. - -::: übung vb-zentimeter -titel: Umdrehungen in Zentimeter -auftrag: Baue ein Unterprogramm \`int zentimeter(int umdrehungen)\`, das \`umdrehungen * 20\` zurückgibt. Das Hauptprogramm ruft es mit 3 auf, fährt die Strecke und zeigt **Strecke: 60 cm** auf dem Bildschirm. -fahrt: forward 60 -befehle: showText "Strecke: 60 cm" -braucht: aufruf -tipp: Neuer Reiter mit dem Titel \`int zentimeter(int umdrehungen)\`. Darin reicht eine Anweisung: \`return umdrehungen * 20\`. -tipp: Im Hauptprogramm: Aufruf \`int strecke = zentimeter(3)\`, danach \`mbot.forward(strecke)\` und \`mbot.showText("Strecke: " + strecke + " cm")\`. -lösung: vb-zentimeter-loesung.visustruct -unterprogramm: vb-zentimeter-unterprogramm-loesung.visustruct -::: - -> 📘 **Mehr dazu ohne Roboter:** In VisuStruct gibt es die Lektion [„Aufruf und Aussprung“](?visustruct&lektion=18-aufruf-und-aussprung). Sie öffnet sich in einem neuen Fenster. - -::: übung vb-fehler-22-eigene-methoden -titel: Warum läuft das nicht? -code: java -auftrag: Ludwig hat „Rechteck mit Umdrehungen“ abgetippt, aber das Programm läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-22-eigene-methoden-start.visustruct -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -tipp: Ein Text gehört in Anführungszeichen. Ohne sie sucht Java eine Variable mit diesem Namen. -tipp: Richtig heißt es \`mbot.forward(1, "Umdrehungen")\`. -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -## Spiel: Welche Methode? - -::: spiel frage -titel: Spiel: Welche Methode? -intro: Gleicher Name, andere Parameter: Welche Methode ruft Java auf, und was kommt heraus? Sechs Runden. - -runde: - mbot.forward(30, "cm"); -frage: Welche \`forward\`-Methode läuft? -- \`forward()\` -- \`forward(int cm)\` -- [x] \`forward(int wert, String einheit)\` -> Zwei Parameter, eine Zahl und ein Text: Das passt nur zur dritten. - -runde: - mbot.forward(); -frage: Wie weit fährt der mBot2? -- [x] 10 cm -- gar nicht, die Zahl fehlt -- eine Radumdrehung -> Ohne Parameter fährt \`forward\` 10 cm. Das ist die Variante ohne Parameter. - -runde: - mbot.forward(2, "Umdrehungen"); -frage: Wie weit fährt der mBot2 ungefähr? -- 2 cm -- 20 cm -- [x] 41 cm -> Eine Radumdrehung sind etwa 20,4 cm, zwei also etwa 40,8 cm. - -runde: - static int zentimeter(int umdrehungen) { - return umdrehungen * 20; - } -frage: Was liefert \`zentimeter(4)\`? -- 4 -- 24 -- [x] 80 -> Der Parameter \`umdrehungen\` ist 4, und \`return\` gibt 4 · 20 zurück. - -runde: - public static void main(String[] args) { - quadrat(30); - quadrat(50); - } -frage: Wie viele Quadrate fährt der mBot2, und wie groß ist das zweite? -- eins mit 80 cm -- [x] zwei, das zweite mit 50 cm Seitenlänge -- zwei, beide mit 30 cm Seitenlänge -> Jeder Aufruf fährt ein Quadrat. Der Wert in den Klammern wird zu \`seite\`. - -runde: -frage: Welche Kopfzeile passt zu einer Methode, die eine Zahl zurückgibt? -- \`void zentimeter(int umdrehungen)\` -- [x] \`int zentimeter(int umdrehungen)\` -- \`zentimeter(int umdrehungen)\` -> Vor dem Namen steht der Datentyp des Ergebnisses. \`void\` heißt: kein Ergebnis. -::: - -## Spiel: Bombe entschärfen - -::: spiel bombe -titel: Spiel: Bombe entschärfen -intro: In jedem Programm steckt **genau ein Fehler**, und die Bombe tickt. Tippe auf die falsche Zeile, bevor die Zeit abläuft. Vier Runden, je 30 Sekunden. -zeit: 30 - -runde: - static void quadrat(int seite) { - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } - } -frage: Soll ein Quadrat mit der Seitenlänge \`seite\` fahren. -fehler: 3 -besser: mbot.forward(seite); -> Mit \`30\` ist jedes Quadrat gleich groß, egal was man übergibt. - -runde: - static void zentimeter(int umdrehungen) { - return umdrehungen * 20; - } -frage: Soll die Strecke in cm zurückgeben. -fehler: 1 -besser: static int zentimeter(int umdrehungen) { -> Wer etwas zurückgibt, braucht den Datentyp des Ergebnisses statt \`void\`. - -runde: - static int zentimeter(int umdrehungen) { - int cm = umdrehungen * 20; - } -frage: Soll die Strecke in cm zurückgeben. -fehler: 2 -besser: return umdrehungen * 20; -> Die Methode rechnet, gibt aber nichts zurück. Es fehlt \`return\`. - -runde: - mbot.forward(2, Umdrehungen); -frage: Soll zwei Radumdrehungen weit fahren. -fehler: 1 -besser: mbot.forward(2, "Umdrehungen"); -> Die Einheit ist ein Text und braucht Anführungszeichen. -::: - -## Spiel: Begriffe-Rätsel - -::: spiel kreuzwort -titel: Spiel: Begriffe-Rätsel -intro: Fülle das Rätsel mit den Fachbegriffen aus diesem Teil. Klick auf einen Hinweis, um zum Wort zu springen. Die gelben Felder ergeben das Lösungswort. -- → 5 3 PARAMETER: Wert, den du beim Aufruf in die Klammern schreibst -- ↓ 1 3 STOPPUHR: mbot.getTimer() liest sie ab -- ↓ 5 5 RÜCKGABE: mit return liefert eine Methode ein Ergebnis zurück -- ↓ 3 8 ÜBERLADUNG: gleicher Methodenname, andere Parameter -- → 2 1 METHODE: eigener Befehl mit Namen, z. B. quadrat(30) -- → 10 5 AKKU: mbot.getBattery() sagt, wie voll er ist -- ↓ 2 1 MOTOR: jedes Rad hat einen eigenen -- ↓ 1 10 LINIENFOLGER: fährt einer schwarzen Linie nach -lösung: 5 7, 5 10, 4 1, 7 3, 3 3, 9 8, 5 8 -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann ein Unterprogramm mit Parameter bauen und aufrufen. → Ein Unterprogramm für das Quadrat -- Ich kann Methoden in Java erkennen. → Methoden in Java -- Ich kann eine überladene Methode erkennen und aufrufen. → Gleicher Name, andere Parameter: Überladung -- Ich kann eine Methode mit Rückgabewert schreiben. → Methode mit Rückgabe -::: - -## Wissenstest - -::: quiz -? Das Unterprogramm \`void quadrat(int seite)\` soll ein Quadrat mit 30 cm Seitenlänge fahren. Wie sieht der Aufruf aus? -→ Ein Unterprogramm für das Quadrat -- [ ] \`quadrat()\` -- [x] \`quadrat(30)\` -- [ ] \`int seite = quadrat(30)\` -> Der Wert in den Klammern wird zum Parameter \`seite\`. Weil \`quadrat\` nichts zurückgibt, gibt es keine Zuweisung. -? In welcher Methode beginnt ein Java-Programm? -→ Methoden in Java -- [ ] in der ersten Methode der Klasse -- [x] in der Methode \`main\` -- [ ] in der Methode \`quadrat\` -> Das Programm beginnt immer in \`main\`. Von dort werden die anderen Methoden aufgerufen. -? Woran unterscheidet Java die überladenen Methoden \`forward()\`, \`forward(int cm)\` und \`forward(int wert, String einheit)\`? -→ Gleicher Name, andere Parameter: Überladung -- [ ] am Namen -- [x] an der Anzahl und am Typ der Parameter -- [ ] an der Reihenfolge im Programm -> Der Name ist bei allen gleich. Java wählt die Variante, deren Parameter zum Aufruf passen. -? \`static int zentimeter(int umdrehungen) { return umdrehungen * 20; }\`: Was liegt nach \`int strecke = zentimeter(2);\` in \`strecke\`? -→ Methode mit Rückgabe -- [ ] 2 -- [ ] 20 -- [x] 40 -> \`return\` gibt 2 · 20 = 40 zurück, und dieser Wert landet in \`strecke\`. -? Was steht in der Kopfzeile einer Methode, die eine ganze Zahl zurückgibt, statt \`void\`? -→ Methode mit Rückgabe -- [ ] \`return\` -- [x] \`int\` -- [ ] \`static\` -> Statt \`void\` steht der Datentyp des Ergebnisses in der Kopfzeile. -::: -`,ez=`--- -titel: Wie geht es weiter? -bereich: Ausblick -kurz: Zurückblicken, was du mit dem mBot2 gelernt hast, und sehen, was in VisuStruct noch auf dich wartet. ---- - -## Diese Kompetenzen erwerbe ich jetzt - -::: kompetenzen -- sagen, welche Grundlagen der Programmierung ich mit dem mBot2 gelernt habe -- einen Fehler in einem Roboterprogramm allein finden und reparieren -- die Themen nennen, die in VisuStruct noch auf mich warten -::: - -## Das kannst du jetzt - -Glückwunsch, du hast alle Roboter-Lektionen geschafft! Mit dem mBot2 hast du die **Grundlagen der strukturierten Programmierung** gelernt: - -- ein **Struktogramm** lesen und bauen und es mit dem **Schreibtischtest** prüfen, einen Plan als **PAP** und als **Pseudocode** aufschreiben, -- **Sequenz**, **Verzweigung** und **Fallauswahl**, Bedingungen mit **und**, **oder** und **nicht**, -- die Schleifen **for**, **while** und **do-while**, auch ineinander verschachtelt, und den Aussprung mit **break**, -- **Variablen**, **Datentypen** und **Konstanten** mit \`final\`, Zählen, Summieren und den größten Wert finden, -- **Arrays** und **Zeichenketten**, -- eigene **Methoden** mit Parametern und Rückgabewert. - -Das alles gilt nicht nur für Roboter. Jedes Java-Programm ist aus genau diesen Bausteinen gebaut, auch ein Spiel oder eine App. - -::: angeberwissen -Määäh, weißt du, was du mit dem mBot2 eigentlich gebaut hast? Ein **eingebettetes System**: einen kleinen Computer, der in einem Gerät steckt und mit Sensoren seine Umwelt misst. Solche Systeme stecken in Waschmaschinen, Autos und Herzschrittmachern. In einem modernen Auto sind es über hundert. Und programmiert werden sie mit genau den Bausteinen, die du jetzt kennst. -::: - -## Noch einmal: Warum läuft das nicht? - -Zum Schluss eine letzte Fehlersuche. Diesmal ist es ein Fehler, den auch Profis oft machen. - -::: übung vb-fehler-23-wie-geht-es-weiter -titel: Warum läuft das nicht? -code: java -auftrag: Das Programm soll bis kurz vor die Wand fahren, aber es läuft nicht. „Im Editor öffnen“ zeigt den Code im Bereich Code. Klicke auf „Ausführen“ und lies die Fehlermeldung. Repariere den Fehler und klicke auf Prüfen. -start: vb-fehler-23-wie-geht-es-weiter-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Lies die Fehlermeldung genau. Vergleiche den Sensor mit den anderen Befehlen: Was steht hinter \`mbot.forward\`? -tipp: Hinter einem Befehl stehen immer runde Klammern, auch wenn nichts darin steht. Richtig heißt es \`mbot.getDistance() > 15\`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Was noch fehlt - -Einige Themen der Programmierung haben mit dem Roboter wenig zu tun. Du findest sie in **VisuStruct**, der Welt ohne Roboter. Weil du die Grundlagen schon kennst, kommst du dort schnell voran. Die Links öffnen sich in einem neuen Fenster. - -- **Eingabe mit der Tastatur:** Werte beim Ausführen abfragen, in [„Eingabe und Ausgabe“](?visustruct&lektion=06-eingabe-und-ausgabe). -- **Tabellen im Speicher:** Arrays mit Zeilen und Spalten, zum Beispiel für einen Kinosaal, in [„Zweidimensionale Arrays“](?visustruct&lektion=16-zweidimensionale-arrays). -- **Suchen und Sortieren:** einen Wert schnell finden und eine Liste der Größe nach ordnen, in [„Suchen und Sortieren“](?visustruct&lektion=19-suchen-und-sortieren). -- **Andere Sprachen:** dasselbe Programm in Python und JavaScript, in [„Python und JavaScript“](?visustruct&lektion=21-python-und-javascript). -- **Abstürze abfangen:** was passiert, wenn jemand „abc“ statt einer Zahl eintippt, in [„Wenn das Programm abstürzt: try und catch“](?visustruct&lektion=22-try-und-catch). -- **Dateien:** Text, Tabellen und Daten lesen und speichern, ab [„Text einlesen und speichern“](?visustruct&lektion=23-text-einlesen-und-speichern) über [„Tabellen als CSV“](?visustruct&lektion=25-tabellen-als-csv) bis [„Daten als JSON“](?visustruct&lektion=26-json). -- **Fenster mit Knöpfen:** eigene Programme mit Fenster, Textfeld und Knopf, ab [„Dein erstes Fenster“](?visustruct&lektion=28-erstes-fenster) bis [„Model View Controller“](?visustruct&lektion=40-model-view-controller). - -> Den mBot2 brauchst du dafür nicht. Du kannst aber jederzeit zurückkommen und ihm neue Kunststücke beibringen. - -## Spiel: Welche Welt? - -::: spiel frage -titel: Spiel: Welche Welt? -intro: Ludwig hat Ideen für neue Programme. Lernst du das mit dem mBot2 in VisuBot oder ohne Roboter in VisuStruct? Acht Runden. -antworten: VisuBot | VisuStruct - -runde: -frage: „Fahre bis zur Wand und piepe dann.“ -richtig: VisuBot -> Abstandssensor, Motoren und Lautsprecher: Das ist ein Fall für den mBot2. - -runde: -frage: „Ein Fenster mit einem Knopf, der bis 10 zählt.“ -richtig: VisuStruct -> Fenster mit Knöpfen baust du mit Swing in VisuStruct. - -runde: -frage: „Spiele eine Melodie, die in einem Array steht.“ -richtig: VisuBot -> Arrays und Melodien hast du mit dem Lautsprecher des mBot2 gelernt. - -runde: -frage: „Lies die Noten der Klasse aus einer CSV-Datei.“ -richtig: VisuStruct -> Dateien lesen und speichern lernst du in VisuStruct. - -runde: -frage: „Tippt jemand ‚abc‘ statt einer Zahl ein, soll das Programm nicht abstürzen.“ -richtig: VisuStruct -> Abstürze fängst du mit try und catch ab. Das steht in VisuStruct. - -runde: -frage: „Folge mit dem Liniensensor einer schwarzen Linie.“ -richtig: VisuBot -> Der Liniensensor sitzt unter dem mBot2. - -runde: -frage: „Sortiere zehn Zahlen der Größe nach.“ -richtig: VisuStruct -> Suchen und Sortieren steht in VisuStruct. Den größten Wert finden kannst du aber schon. - -runde: -frage: „Speichere die Bestenliste in einer Datei.“ -richtig: VisuStruct -> Dateien schreiben lernst du in VisuStruct in „Text einlesen und speichern“. -::: - -## Checkliste - -Schätze dich bei jedem Punkt ehrlich ein: ☂️ noch nicht, ⛅ teilweise, ☀️ kann ich sicher. Bei ⛅ und ☂️ hilft dir **Nachlesen**. Danach kommt der Wissenstest. Er zeigt dir, ob deine Einschätzung stimmt. - -::: checkliste -- Ich kann sagen, welche Grundlagen der Programmierung ich mit dem mBot2 gelernt habe. → Das kannst du jetzt -- Ich kann einen Fehler in einem Roboterprogramm allein finden und reparieren. → Noch einmal: Warum läuft das nicht? -- Ich kann die Themen nennen, die in VisuStruct noch auf mich warten. → Was noch fehlt -::: - -## Wissenstest - -::: quiz -? Gelten die Bausteine aus den Roboter-Lektionen auch für Programme ohne Roboter? -→ Das kannst du jetzt -- [x] Ja, jedes Java-Programm ist aus Sequenz, Verzweigung, Schleife, Variablen und Methoden gebaut -- [ ] Nein, ohne Roboter braucht man ganz andere Befehle -- [ ] Nur die Schleifen -> Nur die \`mbot.\`-Befehle gehören zum Roboter. Alles andere ist ganz normales Java. - -? Warum läuft \`mbot.getDistance > 15\` nicht? -→ Noch einmal: Warum läuft das nicht? -- [ ] Weil 15 zu klein ist -- [x] Weil hinter dem Befehl die runden Klammern fehlen -- [ ] Weil man \`<\` schreiben muss -> Ohne Klammern sucht Java eine Variable \`getDistance\` und findet keine. - -? Wo lernst du, wie ein Programm Abstürze mit try und catch abfängt? -→ Was noch fehlt -- [ ] In VisuBot -- [x] In VisuStruct -> Dort steht die Lektion „Wenn das Programm abstürzt: try und catch“. - -? Welche Themen findest du in VisuStruct? Wähle die passende Liste. -→ Was noch fehlt -- [x] Sortieren, Dateien und Fenster mit Knöpfen -- [ ] Liniensensor, LEDs und Motoren -- [ ] Nur die for-Schleife -> Liniensensor, LEDs und Motoren gehören zum mBot2, die for-Schleife kennst du schon. -::: -`,tz=`## 🤖 mBot2-Befehle - -Ein Befehl sagt dem mBot2, was er **tun** soll. Jeder Befehl beginnt mit \`mbot.\` und endet mit Klammern. In die Klammern schreibst du oft eine Zahl. - -### 🚗 Fahren und Drehen {.bot-fahren} - -Der mBot2 fährt geradeaus oder dreht sich auf der Stelle. - -\`\`\` -mbot.forward() fährt 10 cm nach vorn -mbot.forward(30) fährt 30 cm nach vorn -mbot.forward(2, "Umdrehungen") die Räder drehen sich 2-mal (≈ 41 cm) -mbot.backward(1, "Umdrehungen") eine Radumdrehung zurück (≈ 20 cm) -mbot.backward(20) fährt 20 cm zurück -mbot.turnRight() dreht sich nach rechts (90°) -mbot.turnLeft() dreht sich nach links (90°) -mbot.turnLeft(45) dreht sich nur ein bisschen nach links (45°) -mbot.turnRight(180) dreht sich ganz um -\`\`\` - -> \`forward\` und \`backward\` sind **überladen**: gleicher Name, verschiedene Parameter. Als Einheit gehen \`"cm"\` und \`"Umdrehungen"\`. Eine Radumdrehung sind etwa 20,4 cm. - -> Ohne Zahl fährt er 10 cm oder dreht sich 90°. 90° ist ein rechter Winkel, wie die Ecke von einem Blatt Papier. - -> Für ein Vieleck dreht er sich insgesamt einmal ganz herum: Dreieck 3 × 120°, Quadrat 4 × 90°, Sechseck 6 × 60°. - -### ⚙️ Räder einzeln {.bot-fahren} - -Hier gibst du jedem Rad selbst Gas. Die Zahlen sind Prozent: 100 ist volles Gas, 0 ist Stillstand, eine Zahl mit Minus fährt rückwärts. Der mBot2 fährt so lange weiter, bis ein anderer Fahrbefehl kommt. - -\`\`\` -mbot.drive(40, 40) beide Räder gleich: geradeaus -mbot.drive(10, 40) links langsamer: Kurve nach links -mbot.drive(40, -40) ein Rad vor, eins zurück: dreht sich -mbot.stop() hält an -\`\`\` - -### 💡 Licht {.bot-licht} - -Der mBot2 hat fünf LEDs mit den Nummern 0 bis 4. - -\`\`\` -mbot.setLed(255, 0, 0) alle LEDs leuchten rot -mbot.ledOff() alle LEDs aus -mbot.led(0).setColor("grün") nur LED 0 leuchtet grün -mbot.led(0).setColor("aus") nur LED 0 aus -\`\`\` - -Bei \`setLed\` mischst du Rot, Grün und Blau, jedes von 0 (aus) bis 255 (ganz hell). - -### 🔊 Ton {.bot-ton} - -\`\`\` -mbot.playTone(440, 300) spielt einen Ton -\`\`\` - -Die erste Zahl ist die Tonhöhe. Je größer, desto höher. Die zweite Zahl ist die Länge in Millisekunden (1000 Millisekunden sind 1 Sekunde). - -### 🖥️ Bildschirm {.bot-anzeige} - -\`\`\` -mbot.showText("Hallo") zeigt Hallo auf dem Bildschirm -\`\`\` - -Mit \`+\` hängst du Text und Zahlen aneinander: \`mbot.showText("Abstand: " + mbot.getDistance())\`. - -### 😊 Gefühle {.bot-anzeige} - -Der mBot2 zeigt ein Gefühl mit Licht, Ton und Wackeln. Das siehst du nur am echten mBot2. - -\`\`\` -mbot.emotion("smile") lacht -mbot.emotion("sad") ist traurig -mbot.emotion("wow") staunt -\`\`\` - -Weitere Gefühle: hello, yeah, proud, yummy, naughty, uh_oh, hurt, angry, startle, shiver, dizzy, sleepy, zzz, wake_up, blink, yes, no, look_up, look_down, look_left, look_right, look_around - -### ⏱️ Warten und Stoppuhr {.bot-warten} - -\`\`\` -mbot.pause(1000) wartet 1 Sekunde -mbot.resetTimer() stellt die Stoppuhr auf 0 -\`\`\` - -Die Zeit der Stoppuhr liest du mit dem Sensor \`mbot.getTimer()\` (unten bei **Stoppuhr und Akku**). - -## 📡 mBot2-Sensoren - -Ein Sensor **misst** etwas und gibt dir einen Wert zurück. Den Wert benutzt du in einer Bedingung, zum Beispiel in einer Schleife oder Verzweigung. Manche Sensoren liefern eine **Zahl**, manche **wahr oder falsch**, einer einen **Text**. - -### 📏 Abstand {.bot-sensor} - -Der Ultraschallsensor vorn misst, wie weit die nächste Wand weg ist, in Zentimetern. - -\`\`\` -mbot.getDistance() Abstand in cm, z. B. 25 -mbot.getDistance() > 15 ist vorn mehr als 15 cm frei? -int abstand = mbot.getDistance() Abstand in einer Variablen merken -\`\`\` - -### ➖ Linie {.bot-sensor} - -Unter dem mBot2 sitzen vier Fühler. Sie sehen, ob der Boden schwarz ist. - -\`\`\` -mbot.getLine() Zahl: 0 heißt, kein Fühler sieht Schwarz -mbot.getLine() == 0 kein Schwarz, weiterfahren -mbot.getLine() != 0 ein Fühler sieht Schwarz -mbot.isLineLeft() sieht der linke mittlere Fühler Schwarz? -mbot.isLineRight() sieht der rechte mittlere Fühler Schwarz? -\`\`\` - -### 🎨 Farben {.bot-sensor} - -Derselbe Sensor unten erkennt auch Farben. \`mbot.getColor()\` gibt dir den Namen der Farbe als Text: \`"rot"\`, \`"grün"\`, \`"blau"\`, \`"gelb"\`, \`"weiß"\` oder \`"schwarz"\`. - -\`\`\` -mbot.getColor().equals("rot") steht er auf Rot? -!mbot.getColor().equals("rot") steht er nicht auf Rot? -\`\`\` - -> Texte vergleichst du mit \`.equals(…)\`, nicht mit \`==\`. Das \`!\` davor heißt **nicht**. - -### 🕹️ Tasten und Joystick {.bot-sensor} - -Auf dem CyberPi gibt es die Tasten A und B und einen kleinen Joystick. Jeder Sensor sagt **wahr**, solange gedrückt wird. - -\`\`\` -mbot.isButtonA() wird Taste A gedrückt? -mbot.isButtonB() wird Taste B gedrückt? -mbot.isJoystickUp() Joystick nach oben gedrückt? -mbot.isJoystickDown() Joystick nach unten gedrückt? -mbot.isJoystickLeft() Joystick nach links gedrückt? -mbot.isJoystickRight() Joystick nach rechts gedrückt? -\`\`\` - -### 🔆 Licht und Lautstärke {.bot-sensor} - -\`\`\` -mbot.getBrightness() wie hell? 0 ist dunkel, 100 ist hell -mbot.getLoudness() wie laut? 0 ist still, 100 ist laut -\`\`\` - -### 📐 Neigung und Schütteln {.bot-sensor} - -\`\`\` -mbot.getPitch() nach vorn oder hinten gekippt? (in Grad) -mbot.getRoll() zur Seite gekippt? (in Grad) -mbot.isShaken() wird er geschüttelt? -\`\`\` - -> Liegt der mBot2 flach, sind beide Neigungen 0. In der Simulation stellst du diese Werte unter **🎛 Sensoren ausprobieren** ein. - -### 🔋 Stoppuhr und Akku {.bot-sensor} - -\`\`\` -mbot.getTimer() Sekunden seit dem Start oder seit resetTimer() -mbot.getBattery() wie voll ist der Akku? (in Prozent) -\`\`\` - -### Echter mBot2 - -Mit **🔌 mBot2** neben „Simulation starten“ verbindest du den echten Roboter mit dem USB-Kabel. Das geht nur in Chrome oder Edge. Dann fährt er jeden Schritt der Simulation mit, und die Sensoren messen echte Werte. Ein zweiter Klick trennt ihn wieder. - -### Ohne Kabel fahren - -Mit **⬆ Übertragen** speicherst du dein Programm fest auf dem mBot2. Danach Kabel ab, mBot2 an den Start und **Taste A** drücken: Er fährt dein Programm allein. Es bleibt gespeichert, bis du ein neues überträgst. Eingaben mit der Tastatur gehen am mBot2 nicht. -`,nz=`--- -titel: Structure chart and editor -bereich: Getting started -kurz: What a structure chart is, how to use the editor and how to save your programs. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what an algorithm and a structure chart are -- recognise the three building blocks sequence, branch and repetition -- insert, move and delete elements in the editor -- save a structure chart and open it again -::: - -## A plan for the robot - -A robot does not think for itself. It does exactly what is in its program, no more and no less. So before you program it, you need an exact plan. Such an exact step-by-step instruction is called an **algorithm**. - -A **structure chart** draws the plan as boxes. You read it from top to bottom. Each box is one step. It was invented in 1973 by Isaac Nassi and Ben Shneiderman, which is why it is also called a **Nassi-Shneiderman diagram**. - -::: diagramm vb-l00-sequenz-loesung.visustruct -Drive forward, turn right, drive forward: three boxes, from top to bottom. -::: - -::: angeberwissen -Ada Lovelace wrote the first program for a machine back in 1843, for a calculating machine that was never finished. Even then she guessed that such machines would one day do more than calculate, for example make music. Your mBot2 can do both. -::: - -## Three building blocks - -Every program, including one for a robot, is made of only three building blocks: - -| Building block | What it does | Example with the mBot2 | -|---|---|---| -| **Sequence** | steps one after the other | drive forward, turn, drive forward | -| **Branch** | one thing or the other, depending on a condition | Is the wall close? Then turn, otherwise drive forward. | -| **Repetition** | steps several times | do the same thing four times | - -In a structure chart you see the building blocks straight away: the branch has a triangle and two columns: **true** on the left, **false** on the right. The repetition (loop) wraps around its boxes like a tilted L. - -::: diagramm vb-l00-bausteine-loesung.visustruct -All three building blocks: first a box, then a loop, inside it a branch. -::: - -## The editor - -The [structure chart editor](#zeige-editor) has several areas. Move the mouse over a blue word or tap it, and the area lights up: - -- **On the left the [Elements](#zeige-elemente):** all building blocks to drag in. The group **mBot2 commands** has the commands for the mBot2 such as **drive forward** and **turn right**. -- **In the middle the [structure chart](#zeige-struktogramm):** a new diagram only contains an empty placeholder, shown as **ø**. -- **Below it the [Object Inspector](#zeige-objektinspektor):** here you change the text of the selected box, for example \`mbot.forward(30)\` instead of \`mbot.forward()\`. You can drag the line with the grip above it up or down. -- **Above it the [tools](#zeige-werkzeuge):** undo, copy, delete, export and, on the right, the **simulation**. - -This is how you build: - -- **Insert:** drag an element from the Elements onto the structure chart. On the placeholder **ø** it replaces it. On the top half of a box it goes before it, on the bottom half after it. A blue line shows you where it lands. -- **Change text:** double-click a box, or double-tap it on a tablet. Then you type right in the structure chart, **Enter** takes it. -- **Move:** drag a box to another place. -- **Delete:** click the box and press Delete, or click the [trash can](#zeige-loeschen). -- **Undo:** Ctrl+Z, on a Mac ⌘Z. - -> 🧩 **Prefer blocks like in Scratch?** Click [Blocks](#zeige-bloecke) in the tools. Then you see the same program as blocks that snap together. You type numbers right into the white fields and drag sensors in. **Structure chart** takes you back. It stays the same program, so you can switch at any time. - -::: übung vb-erster-plan -titel: Round the corner -auftrag: Build a structure chart: the mBot2 drives forward, turns right, drives forward, turns left and drives forward once more. Then start the simulation and watch. -fahrt: forward, turnRight, forward, turnLeft, forward -braucht: anweisung -tipp: Drag **drive forward** onto the placeholder ø. Drag each next command onto the bottom half of the last box. -tipp: From top to bottom: **drive forward**, **turn right**, **drive forward**, **turn left**, **drive forward**. -lösung: vb-erster-plan-loesung.visustruct -::: - -> When you are done, click **Check** in the exercise. VisuStruct then runs your structure chart and checks whether the mBot2 drives exactly as required. Later it often checks your program in several worlds, for example once with a wall and once without. If it doesn't work, the **hints** help you. - -## Saving and opening - -- **[Save](#zeige-speichern)** (Ctrl+S, on a Mac ⌘S): the structure chart is saved as a file ending in \`.visustruct\`. The [title](#zeige-titel) above the structure chart suggests the file name. -- **[Open](#zeige-oeffnen)** (Ctrl+O, on a Mac ⌘O): load a saved file again. - -In the column [📁 Files](#zeige-dateien) you set up a folder once, your **workspace**. All your programs and your learning progress go there. You can take the folder with you and carry on at home. - -> 📘 **More without the robot:** VisuStruct has the lessons ["What is a structure chart?"](?visustruct&lektion=01-was-ist-ein-struktogramm), ["The editor"](?visustruct&lektion=02-der-editor) and ["Save, open, export"](?visustruct&lektion=03-speichern-oeffnen-exportieren). They open in a new window. - -## Game: Which building block? - -::: spiel frage -titel: Game: Which building block? -intro: Ludwig describes what his mBot2 should do. Is it a sequence, a branch or a loop? Six rounds. -antworten: sequence | branch | loop - -runde: -frage: The mBot2 drives forward, turns right and drives forward again. -richtig: sequence -> Three steps one after another, each exactly once. - -runde: -frage: If the wall is close, it turns. Otherwise it drives forward. -richtig: branch -> Depending on the condition, one thing or the other happens. - -runde: -frage: It drives forward 10 cm four times. -richtig: loop -> The same step happens several times. - -runde: -frage: As long as no line comes, it keeps driving. -richtig: loop -> "As long as" means: again and again until the condition is no longer true. - -runde: -frage: If it is standing on red, it beeps. -richtig: branch -> It only beeps if the condition is true. - -runde: -frage: It shows "Hello" on the screen and then plays a tone. -richtig: sequence -> Two steps one after another, with no condition and no repetition. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what an algorithm and a structure chart are. → A plan for the robot -- I can recognise the three building blocks sequence, branch and repetition. → Three building blocks -- I can insert, move and delete elements in the editor. → The editor -- I can save a structure chart and open it again. → Saving and opening -::: - -## Quiz - -::: quiz -? What is an algorithm? -→ A plan for the robot -- [ ] a part of the mBot2 -- [x] an exact step-by-step instruction -- [ ] a programming language -> An algorithm says exactly what happens in which order. The structure chart draws it. -? Which building block decides depending on a condition? -→ Three building blocks -- [ ] the sequence -- [x] the branch -- [ ] the repetition -> The branch has a triangle and two columns for true and false. -? You drag **drive forward** onto the bottom half of a box. Where does it land? -→ The editor -- [ ] before it -- [x] after it -- [ ] it replaces the box -> Top half means before, bottom half means after. -? Which ending does a saved structure chart file have? -→ Saving and opening -- [ ] \`.java\` -- [x] \`.visustruct\` -- [ ] \`.png\` -> \`.png\` is a picture to pass on. You can only keep editing the \`.visustruct\` file. -::: -`,iz=`--- -titel: The mBot2 -bereich: Getting started -kurz: Get to know the robot you are about to program. ---- - -## Skills I will gain now - -::: kompetenzen -- name the parts of the mBot2 -- explain how the mBot2 measures distances -- explain how the mBot2 detects a line -- explain what sensors and actuators are -- control the mBot2 with elements from the group mBot2 commands -- let the mBot2 drive in the simulator -- connect the real mBot2 with USB-C and disconnect it again -- check that all sensors and actuators work -::: - -## Hello mBot2 - -::: mbot2 -focus: -spin: yes -::: - -The **mBot2** is a small robot made by Makeblock. In this lesson you take a tour through all its parts: - -- the [CyberPi]{part=cyberpi}, the small computer at the back on top -- the see-through [shield with battery]{part=shield} in the middle -- two [motors]{part=motors} with [wheels]{part=wheels} -- the [ultrasonic sensor]{part=ultrasonic} at the front, which looks like two eyes -- the [line sensor]{part=lineSensor} under the nose -- the turquoise [chassis]{part=chassis} that holds everything together - -In VisuBot you program it with structure charts. You can try every program in the simulator first, even without a real robot. - -> Move the mouse over a word underlined in yellow and the part lights up in the model. Tap the word and the camera flies there. You can rotate the model with the mouse or your finger and zoom with the mouse wheel. - -## The CyberPi - -::: mbot2 -focus: cyberpi, screen, joystick, buttons, leds, lightSensor, microphone -view: back -::: - -The [CyberPi]{part=cyberpi,screen,joystick,buttons,leds,lightSensor,microphone} is the brain of the mBot2: a **microcontroller**, a small computer without a keyboard. It runs your program. It lies crosswise at the back on top of the robot and can be taken off. - -A lot is already built in: - -- a colour [screen]{part=screen} that shows text and pictures -- a [joystick]{part=joystick} you can push in four directions -- two [buttons]{part=buttons}, one with a triangle and one with a square, and the Home button on the side next to the USB port -- five colourful [LEDs]{part=leds} as a light bar along the back -- a [light sensor]{part=lightSensor} and a [microphone]{part=microphone} - -There is also a speaker and a motion sensor that you cannot see from outside. The motion sensor notices when the CyberPi is tilted or turned. - -> The model was built from photos. Look at your own mBot2 and compare. - -## Screen, LEDs and speaker - -::: mbot2 -focus: screen, leds -view: back -::: - -With the [screen]{part=screen} and the five [LEDs]{part=leds} the mBot2 shows you what it is doing. Each LED can glow in almost any colour because it mixes red, green and blue. That is why they are called **RGB LEDs**. - -Later you make the LEDs glow red with \`mbot.setLed(255, 0, 0)\`. The three numbers are red, green and blue, each from 0 to 255. - -The [CyberPi]{part=cyberpi} also has a small **speaker**. With \`mbot.playTone(440, 300)\` it plays a tone: 440 vibrations per second (hertz), 300 milliseconds long. - -## Measuring light and sound - -::: mbot2 -focus: lightSensor, microphone -view: top -::: - -The [light sensor]{part=lightSensor} measures how bright it is. The [microphone]{part=microphone} measures how loud it is. For example, the mBot2 can start driving when you clap your hands. - -## Buttons and joystick - -::: mbot2 -focus: joystick, buttons -view: top -::: - -The [buttons]{part=buttons} A and B and the [joystick]{part=joystick} are sensors too: they notice when you press them. Your program can, for example, wait until you press A and only then start driving. With the joystick you steer the mBot2 like with a remote control. - -## The motion sensor - -::: mbot2 -focus: cyberpi -spin: yes -::: - -Inside the [CyberPi]{part=cyberpi} there is a sensor you cannot see: the **motion sensor** (a gyroscope with an accelerometer). It notices whether the mBot2 is tilted forward, backward or to the side, how far it has turned and whether someone shakes it. Your phone has such a sensor too: that is why the picture turns when you hold it sideways. - -## Power: the shield with battery - -::: mbot2 -focus: shield, powerSwitch -view: side -::: - -The big see-through case is the [mBot2 shield]{part=shield}. Through the lid you see the grey **battery** that powers the robot. At the back of the shield the cables of the motors (M1 and M2) and of the ultrasonic sensor are plugged in. - -On the left side is the [power switch]{part=powerSwitch} labelled ON/OFF. Next to it are two sockets S1 and S2 for small servo motors. Switch the mBot2 off when you do not need it, so the battery lasts longer. You charge it with the USB cable on the CyberPi. - -## Motors and wheels - -::: mbot2 -focus: motors, wheels -view: side -::: - -Two [motors]{part=motors} drive the two [wheels]{part=wheels}, each its own. You see them from below: silver motors in light blue gearboxes. - -- If both wheels turn forward at the same speed, the mBot2 drives **straight**. -- If one wheel turns forward and the other backward, it turns **on the spot**. - -The motors are **encoder motors**: they count how far the wheel has turned. That is how the mBot2 knows when it has driven exactly 10 cm or turned exactly 90°. - -## The ultrasonic sensor - -::: mbot2 -focus: ultrasonic -view: front -::: - -At the front sits the black [ultrasonic sensor]{part=ultrasonic}. It looks like two eyes but is more like an ear: one circle sends out a very high sound we cannot hear. The other waits for the echo. The longer the echo takes, the farther away the wall is. - -That is how the mBot2 measures distances in centimetres. The rings around the circles can also glow blue. - -::: angeberwissen -Bats do exactly what the ultrasonic sensor does: they call so high that we cannot hear it and listen for the echo. Sound travels about 343 metres per second. If a wall is one metre in front of the mBot2, the echo is back after just under 6 thousandths of a second. -::: - -## Under the robot - -::: mbot2 -focus: lineSensor, caster -view: bottom -::: - -Now you look from below. Right at the front the black board of the [line sensor]{part=lineSensor} sticks out. It has **four** small probes side by side. Each one shines light on the floor and measures how much comes back. A black line reflects little, a bright floor a lot. That is how the mBot2 knows whether it is on a line. - -The sensor is called a **quad RGB sensor** because it can do even more: it also recognises **colours**, for example a red or green field on the floor. - -The [ball caster]{part=caster} behind it is the third leg. It has no motor and simply rolls along in any direction. - -## The chassis - -::: mbot2 -focus: chassis -::: - -The [chassis]{part=chassis} is the turquoise aluminium frame. It holds all the parts together. At the front it has two round "ears", at the back a big **M** for Makeblock. The many holes are there on purpose: you can screw more parts onto them. - -## Sensors and actuators - -A robot has two kinds of parts: - -- **Sensors** measure something: the distance, a line, a colour, the brightness, the loudness, the tilt or whether a button is pressed. -- **Actuators** do something: the motors turn the wheels, the LEDs light up, the screen shows something, the speaker beeps. - -The word comes from the Latin *agere*, “to act”: actuators act, sensors (from *sentire*, “to feel”) feel. - -Your program reads the sensors and controls the actuators. You already know this as input, processing and output. - -## Find the parts - -Your turn: click the part you are looking for in the model. If you are right, the next question appears. - -::: mbot2quiz -- ultrasonic: What does the mBot2 use to measure how far away the wall is? -- screen: Where does text from your program appear? -- wheels: What moves the mBot2 forward? -- lineSensor: What detects a black line on the floor? (Hint: rotate the model.) -- powerSwitch: Where do you switch the mBot2 on and off? -- leds: What glows red with \`mbot.setLed(255, 0, 0)\`? -done: Great, you know your way around the mBot2! -::: - -## Commands for the mBot2 - -In VisuBot the [Elements](#zeige-elemente) have their own group **mBot2 commands**. Each box in it is a command for the robot: - -| Element | Text in the box | What happens | -|---|---|---| -| move forward | \`mbot.forward()\` | 10 cm forward | -| move back | \`mbot.backward()\` | 10 cm back | -| turn right | \`mbot.turnRight()\` | turn 90° to the right | -| turn left | \`mbot.turnLeft()\` | turn 90° to the left | -| LEDs on | \`mbot.setLed(255, 0, 0)\` | the LEDs light up red | - -You may also put a number in the brackets: \`mbot.forward(30)\` drives 30 cm, \`mbot.turnRight(45)\` turns only 45°. - -> The commands are real Java. \`mbot\` is the robot, and after the dot comes what it should do. - -## First drive - -After building, start the [simulation](#zeige-simulation). The VisuBot world with the mBot2 appears on the right. The grid on the floor has squares of 10 cm, exactly one **move forward**. - -::: übung vb-gerade -titel: Straight ahead -auftrag: Drag **move forward** into the structure chart five times. Then start the simulation and watch the mBot2 drive 50 cm straight ahead. -fahrt: 5 x (forward) -tipp: You find the group mBot2 commands below the normal elements. -tipp: Five boxes \`mbot.forward()\` one below the other. Each drives 10 cm. -lösung: vb-gerade-loesung.visustruct -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Then it drives the route for you. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - mbot.forward(60); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,0 -> Two fields forward, turn right, one more field. - -runde: - mbot.forward(30); - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,1 -> One field forward, then it faces left and drives two fields. - -runde: - mbot.turnRight(); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Turning right twice means it faces backwards. Then it drives one field down. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: .... | .... | .^.. -ziel: 2,2 -> Up, to the right, and after the second turn it faces down. -::: - -## Connecting the real mBot2 - -::: mbot2 -focus: cyberpi, powerSwitch, joystick, buttons -view: side -::: - -Everything you build in the simulator can also be driven by the real mBot2. This is how you connect it: - -1. Plug a **USB-C cable** into your computer (for example a MacBook) and the other end into the USB-C port on the right of the [CyberPi]{part=cyberpi}, next to the Home button. -2. Push the [ON/OFF slide switch]{part=powerSwitch} on the left of the shield to ON. The CyberPi's screen lights up. -3. A menu appears on the screen with **restart** highlighted (on a German CyberPi: „neu starten“). Press the [joystick]{part=joystick} down like a button to confirm it. -4. After the restart it says “Press B to start”. Press **button B**, that is the [button with the triangle]{part=buttons}. -5. Open VisuStruct in **Chrome** or **Edge**. Safari and the iPad cannot talk to USB devices. -6. As soon as the CyberPi has started up, click **🔌 mBot2** at the top (or “Connect mBot2” in the gear menu). -7. Chrome shows a list of ports. Choose the entry **USB Serial** (for example “USB Serial (cu.usbserial-110)”). It is often at the very bottom, so you may have to scroll down. Then click **Connect**. -8. The CyberPi restarts briefly (⏳). Then “Hallo VisuBot!” appears on its screen and the button shows 🟢. - -Now the mBot2 drives along with every step of the simulation. Put it on the floor and give it room! - -> Finished? Click **🟢 mBot2** once more. It stops, shows “Bis bald!” and releases the port. Only then pull out the cable. - -> Want to drive with no cable at all? You learn that in lesson 2 under **Driving without a cable**. - -## Testing the mBot2 - -When the mBot2 is connected, **🔧 Test the mBot2** appears below the world. Open it: - -- With the buttons you try each actuator once: LEDs, tone, text on the screen and the wheels. Lift the mBot2 up for this so it does not drive off the table. -- Below are all sensors with their values, and they update live. Hold your hand in front of the ultrasonic sensor, press button A, tilt it, clap your hands or put it on a red sheet of paper. - -If a value does not change although you do something, something is wrong. Then tell your teacher before you start programming. - -## Game: Sensor or actuator? - -::: spiel frage -titel: Game: sensor or actuator? -intro: Does the part measure something, or does it do something? Ten rounds. -antworten: Sensor | Actuator - -runde: -frage: The ultrasonic sensor measures how far away the wall is. -richtig: Sensor -> It measures something: the distance. - -runde: -frage: The motors turn the wheels. -richtig: Actuator -> They do something: they move the mBot2. - -runde: -frage: The LEDs glow red. -richtig: Actuator -> Glowing is doing something, not measuring. - -runde: -frage: The line sensor recognises the black tape. -richtig: Sensor -> It measures how much light comes back from the floor. - -runde: -frage: The speaker beeps. -richtig: Actuator -> It outputs a sound. - -runde: -frage: The microphone hears you clap. -richtig: Sensor -> It measures the loudness. - -runde: -frage: The screen shows “Hello VisuBot!”. -richtig: Actuator -> Showing something is an output. - -runde: -frage: Button A notices that you press it. -richtig: Sensor -> Buttons are sensors: they report whether they are pressed. - -runde: -frage: The motion sensor notices that the mBot2 is tilted. -richtig: Sensor -> It measures the tilt. - -runde: -frage: The rings on the ultrasonic sensor glow blue. -richtig: Actuator -> Tricky! The part is a sensor, but the glowing rings are an output. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the parts of the mBot2. → Hello mBot2 -- I can explain how the mBot2 measures distances. → The ultrasonic sensor -- I can explain how the mBot2 detects a line. → Under the robot -- I can explain what sensors and actuators are. → Sensors and actuators -- I can control the mBot2 with elements from the group mBot2 commands. → Commands for the mBot2 -- I can let the mBot2 drive in the simulator. → First drive -- I can connect the real mBot2 with USB-C and disconnect it again. → Connecting the real mBot2 -- I can check that all sensors and actuators work. → Testing the mBot2 -::: - -## Quiz - -::: quiz -? Match each part to its job. -→ Hello mBot2 -= Ultrasonic sensor -> measures the distance -= Motors -> move the wheels -= CyberPi -> runs the program -= Line sensor -> detects a dark line -? Which parts are actuators? -→ Sensors and actuators -- [x] Motors -- [ ] Ultrasonic sensor -- [x] LEDs -- [ ] Line sensor -> Actuators do something, sensors measure something. -? How does the ultrasonic sensor measure the distance? -→ The ultrasonic sensor -- [ ] It takes a photo of the wall. -- [x] It sends out a very high sound and waits for the echo. -- [ ] It feels the wall with its blue rings. -> The longer the echo takes, the further away the wall is. -? How does the line sensor detect a black line? -→ Under the robot -- [x] Little light comes back from the black line. -- [ ] The line is warmer than the floor. -- [ ] It hears the wheels rolling over the line. -> Each feeler shines light on the floor. A light floor reflects a lot of light, a black line only a little. -? What does \`mbot.turnRight()\` do? -→ Commands for the mBot2 -- [ ] The mBot2 drives 10 cm to the right. -- [x] The mBot2 turns 90° to the right. -- [ ] The right LED switches on. -> With no number in the brackets it turns 90°. \`mbot.turnRight(45)\` turns only 45°. -? The structure chart has five boxes \`mbot.forward()\` one below the other. How far does the mBot2 drive in the simulation? -→ First drive -- [ ] 5 cm -- [x] 50 cm -- [ ] 10 cm -> Each \`mbot.forward()\` drives 10 cm, which is one square of the grid. -? You have finished with the real mBot2. What do you do before you pull out the cable? -→ Connecting the real mBot2 -- [x] Click **🟢 mBot2** once more and wait until “Bis bald!” appears -- [ ] Push the slide switch to OFF -- [ ] Nothing, the cable can come out right away -> Only after “Bis bald!” is the port free. -? You hold your hand in front of the ultrasonic sensor, but the value under **🔧 Test the mBot2** does not change. What do you do? -→ Testing the mBot2 -- [ ] Start programming anyway -- [x] Tell your teacher before you start programming -- [ ] Change the value in the simulation by hand -> If a value does not change although you do something, something is wrong with the robot. -::: -`,az=`--- -titel: The square -bereich: Getting started -kurz: From a long structure chart to a triple nested loop. ---- - -## Skills I will gain now - -::: kompetenzen -- build a sequence without a loop first -- spot repetitions and replace them with a for loop -- nest two loops -- nest three loops -- upload my program to the mBot2 and start it with button A without a cable -::: - -## Driving a square - -The mBot2 should drive a square with sides of 50 cm. You already know one side: **move forward** five times. Then it turns right, and the next side begins. - -::: übung vb-quadrat -titel: The square -auftrag: Build a structure chart that makes the mBot2 drive a square: **move forward** five times, then **turn right**. And all of that four times. -fahrt: 4 x (5 x (forward), turnRight) -tipp: One side is five times move forward and once turn right. -tipp: That makes 24 boxes: four times five \`mbot.forward()\`, each followed by \`mbot.turnRight()\`. -lösung: vb-quadrat-loesung.visustruct -::: - -Quite a lot of boxes, right? If you miscount, the mBot2 no longer drives a square. - -## Always the same - -Look closely at your structure chart: five times **move forward** appears four times. When something repeats, you use a **loop**. We know exactly how often to drive, five times. The for loop fits that. - -Drag a for loop into the structure chart and give it these three parts: - -- Start: \`int i = 0\` -- Condition: \`i < 5\` -- Step: \`i++\` - -The body of the loop gets a single **move forward**. The loop counts \`i\` from 0 to 4, that is five runs. - -\`i\` is a **counter**. The computer uses it to remember which run it is in. Such a named store is called a **variable**; you will get to know it properly in lesson 3. The three parts mean: - -| Part | Meaning | -|---|---| -| \`int i = 0\` | The counter \`i\` is a whole number (\`int\`) and starts at 0. | -| \`i < 5\` | As long as \`i\` is less than 5, the loop runs once more. | -| \`i++\` | After every run \`i\` gets 1 bigger. | - -This is how the loop counts: - -| Run | 1 | 2 | 3 | 4 | 5 | after that | -|---|---|---|---|---|---|---| -| \`i\` | 0 | 1 | 2 | 3 | 4 | 5 | -| \`i < 5\`? | true | true | true | true | true | false, done | - -::: übung vb-quadrat-schleife -titel: Sides with a loop -auftrag: In your square, replace each row of five **move forward** with a for loop that drives five times. The mBot2 then drives exactly the same square. -fahrt: 4 x (5 x (forward), turnRight) -braucht: forSchleife -tipp: Start \`int i = 0\`, condition \`i < 5\`, step \`i++\`. The body only contains \`mbot.forward()\`. -tipp: Four times one below the other: the for loop and then \`mbot.turnRight()\`. -lösung: vb-quadrat-schleife-loesung.visustruct -::: - -## A loop in a loop - -Now the same pair appears four times: a for loop and **turn right**. That is a repetition too! Put both into a second for loop that runs four times. A loop inside a loop is called a **nested loop**. - -Each loop needs its own counter variable, otherwise the two get in each other's way. The usual way: the outer loop counts with \`i\`, the inner one with \`j\`. So rename your inner loop to \`j\`. - -::: übung vb-quadrat-verschachtelt -titel: Nested square -auftrag: Build the square with two nested for loops. The outer one runs four times, once for each side. The inner one runs five times, once for each step. -fahrt: 4 x (5 x (forward), turnRight) -schleifentiefe: 2 -tipp: Outside: start \`int i = 0\`, condition \`i < 4\`, step \`i++\`. Inside: \`int j = 0\`, \`j < 5\`, \`j++\`. -tipp: The outer loop contains the inner loop with \`mbot.forward()\` and below it \`mbot.turnRight()\`. -lösung: vb-quadrat-verschachtelt-loesung.visustruct -::: - -24 boxes have become four. Step through the program in the simulation with **Step** and watch the variables. - -::: quiz -? How often does the mBot2 run \`mbot.forward()\` in the nested square? -- [ ] 9 times -- [x] 20 times -- [ ] 5 times -> The inner loop runs five times, in each of the four runs of the outer loop: 4 · 5 = 20. -::: - -## Four squares - -Now it gets really nice: the mBot2 should drive four squares. After each square it turns right once. That makes a big window with a cross. - -You could copy your square four times and add a **turn right** after each square. Go ahead and try it! Notice something? Again the same thing appears four times. - -::: ludwig -Whenever you copy boxes, ask yourself: does something repeat here? Then a loop fits. -::: - -::: übung vb-vier-quadrate -titel: Four squares -auftrag: Let the mBot2 drive four squares. After each square it turns right once. Use three nested loops. -fahrt: 4 x (4 x (5 x (forward), turnRight), turnRight) -schleifentiefe: 3 -tipp: Put a third for loop that runs four times around your nested square. -tipp: Now \`i\` counts on the very outside (\`int i = 0\`, \`i < 4\`, \`i++\`). The loops inside are then called \`j\` and \`k\`. Inside the outer loop goes your square and then \`mbot.turnRight()\`. -lösung: vb-vier-quadrate-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lessons ["For loop"](?visustruct&lektion=13-for-schleife) and ["Nesting"](?visustruct&lektion=14-verschachteln). They open in a new window. - -::: quiz -? How often does the mBot2 drive 10 cm forward for the four squares? -→ Four squares -- [ ] 20 times -- [ ] 60 times -- [x] 80 times -> 4 squares · 4 sides · 5 steps = 80. Without loops that would be 80 boxes. -::: - -## Driving without a cable - -::: mbot2 -fokus: cyberpi, buttons -ansicht: oben -::: - -So far the mBot2 hangs on the cable while it drives. But you can also **upload your program to the mBot2**. Then it remembers it and drives all by itself, for example through a maze on the floor. - -1. Connect the mBot2 with the USB cable and switch it on, as in lesson 1. This only works in **Chrome** or **Edge**. -2. Open your program, for example the four squares. -3. Click **⬆ Upload** at the top and then **Upload now**. Choose **USB Serial** again. -4. Wait until it says “Done!”. The mBot2 restarts and shows **A = Start**. -5. Unplug the cable and put the mBot2 at the start. -6. Press **button A**, that is the [button with the square]{teil=buttons}. Now your program starts. - -The program stays saved, even when you switch the mBot2 off and on again. Only the next upload replaces it. - -> Afraid it drives off before you are ready? Don't worry: it always waits for button A. If you press A again while it drives, nothing happens. When it has finished, A starts the program again from the beginning. - -## Game: How often does it drive? - -::: spiel frage -titel: Game: How often does it drive? -intro: Count along without starting the simulation. How often does the mBot2 drive or turn? Six rounds. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(); - mbot.turnRight(); - } -frage: How often does the mBot2 drive forward? -- 3 times -- [x] 4 times -- 5 times -> \`i\` counts 0, 1, 2 and 3. That is four runs. - -runde: - for (int i = 1; i <= 4; i++) { - mbot.forward(); - } -frage: How often does the mBot2 drive forward? -- 3 times -- [x] 4 times -- 5 times -> This time \`i\` counts from 1 to 4 because of \`<=\`. That is four runs too. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: How often does the mBot2 drive forward? -- 8 times -- [x] 15 times -- 5 times -> The inner loop runs five times in each of the three outer runs: 3 · 5 = 15. - -runde: - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 5; j++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: How often does the mBot2 turn right? -- [x] 3 times -- 15 times -- 5 times -> \`mbot.turnRight()\` is in the outer loop, not in the inner one. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - } - mbot.turnRight(); -frage: Does the mBot2 drive a square? -- Yes, with 50 cm sides. -- [x] No, it drives 200 cm straight ahead and only turns at the end. -- Yes, but only half of one. -> \`mbot.turnRight()\` is below the loop. It only runs when the loop has finished. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 4; j++) { - mbot.forward(30); - mbot.turnRight(); - } - mbot.turnRight(); - } -frage: How many squares does the mBot2 drive? -- [x] 2 -- 4 -- 8 -> The inner loop drives one square. The outer loop repeats that twice. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should drive and tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(50); - mbot.turnRight(); - } -frage: Should drive a square with 50 cm sides. -fehler: 1 -besser: for (int i = 0; i < 4; i++) { -> A square has four sides. With \`i < 3\` it only drives three. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(180); - } -frage: Should drive a square with 50 cm sides. -fehler: 3 -besser: mbot.turnRight(); -> With 180° it turns round and just drives back and forth. A square needs 90°. - -runde: - for (int i = 0; i < 4; i++) { - for (int i = 0; i < 5; i++) { - mbot.forward(); - } - mbot.turnRight(); - } -frage: Should drive a square made of four times five steps. -fehler: 2 -besser: for (int j = 0; j < 5; j++) { -> The inner loop needs its own counter \`j\`. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnLeft(45); - } -frage: Should drive a square with 30 cm sides. -fehler: 3 -besser: mbot.turnLeft(); -> A square needs right angles, so 90°. Without a number it turns 90°. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Then it drives the route for you. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..... | ..^.. -ziel: 2,0 -> One field forward, three times. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 1,2 -> A whole square: it arrives exactly where it started. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: .... | .... | .^.. -ziel: 2,2 -> Only three sides: the fourth is missing, so it ends up to the right of the start. - -runde: - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - mbot.forward(30); - } - mbot.turnRight(); - } -karte: .... | .... | .... | ^... -ziel: 2,1 -> The inner loop drives two fields, then it turns. All that twice: two fields up, two to the right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a sequence without a loop first. → Driving a square -- I can spot repetitions and replace them with a for loop. → Always the same -- I can nest two loops. → A loop in a loop -- I can nest three loops. → Four squares -- I can upload my program to the mBot2 and start it with button A without a cable. → Driving without a cable -::: - -## Quiz - -::: quiz -? Still without a loop, the mBot2 should drive one side of 50 cm and then turn. Which boxes do you need? -→ Driving a square -- [ ] one \`mbot.forward()\` and one \`mbot.turnRight()\` -- [x] five \`mbot.forward()\` and one \`mbot.turnRight()\` -- [ ] five \`mbot.turnRight()\` -> Each \`mbot.forward()\` drives 10 cm, five of them are 50 cm. -? A for loop has the start \`int i = 0\`, the condition \`i < 5\` and the step \`i++\`. How often does it run? -→ Always the same -- [ ] 4 times -- [x] 5 times -- [ ] 6 times -> \`i\` counts 0, 1, 2, 3, 4. That is five runs. -? Why is the counter of the inner loop called \`j\` and not \`i\` as well? -→ A loop in a loop -- [ ] Each letter may only appear once in a program. -- [x] Each loop needs its own counter, otherwise the two get in each other's way. -- [ ] \`j\` counts faster than \`i\`. -> If both counted with \`i\`, the inner loop would mix up the outer one. -? How often does the mBot2 run \`mbot.turnRight()\` for the four squares? -→ Four squares -- [ ] 4 times -- [ ] 16 times -- [x] 20 times -> In each square it turns four times, that is 4 · 4 = 16. After each square there is one more turn: 16 + 4 = 20. -? Your program has been transferred and the cable is out. How do you start it? -→ Driving without a cable -- [ ] with button B -- [x] with button A -- [ ] with **⬆ Transfer** -> After the transfer the mBot2 shows **A = Start** and waits for button A. -::: -`,sz=`--- -titel: Flowcharts and pseudocode -bereich: Getting started -kurz: Write the plan for the square as a flowchart and as pseudocode and compare them with the structure chart. ---- - -## Skills I will gain now - -::: kompetenzen -- read a flowchart for the mBot2 and build one myself -- read a plan written as pseudocode -- compare structure chart, flowchart and pseudocode -::: - -## Three ways to write down a plan - -You know the plan for the square as a **structure chart**: a loop that runs four times, with driving one side and turning right inside it. - -There are two more ways to write down the same plan. You will meet both often, in class and on the internet: - -- the **flowchart**, -- **pseudocode**, a plan made of text in plain language. - -You can't control the mBot2 with them directly. But with all three you can write down a plan before you build it as a structure chart or in Java. - -## The flowchart - -A flowchart is made of a few symbols connected by **arrows**: - -- **Oval:** start or end. -- **Rectangle:** a statement, for example *drive forward 50 cm*. -- **Diamond:** a question with two exits: **yes** and **no**. -- **Parallelogram:** an input or output, for example a text on the screen. -- **Arrow:** shows which step comes next. - -A loop has no symbol of its own in a flowchart. You recognise it by an arrow that leads **back up**. As long as the answer in the diamond is **no**, it goes back and the steps run again. - -> Careful: in a flowchart an arrow may point anywhere, even all over the place. In big charts this quickly becomes confusing. This is called **spaghetti code**. It can't happen in a structure chart, because there are no arrows. - -## Exercise: The square as a flowchart - -The mBot2 drives one side, turns right and counts the side. After four sides it shows "Square done!" on the screen. - -::: pap -titel: Exercise: The square as a flowchart -auftrag: The places in the chart are empty. Put each element in its place. The shape of the place helps you. -start: Start -anweisung: Drive forward 50 cm -anweisung: Turn right -anweisung: Count the side -frage: 4 sides driven? | nein -> Drive forward 50 cm -ausgabe: Show “Square done!” -ende: End -erklärung: As long as four sides have not been driven yet, the arrow leads from the diamond back to “Drive forward 50 cm”. That is the loop. Showing a text on the screen is an output, so it sits in the parallelogram. -legende: ja -::: - -**Draw your own flowcharts:** With **VisuFlow** you can easily draw your own flowchart, for example for your robot's next drive. The symbols are ready in a bar: tapping one adds the next symbol and connects it with an arrow right away. You save your flowchart as a file and export it as an image or PDF, for example for your notebook or a presentation. [Open VisuFlow](https://visustruct.visucode.de/app/?visuflow) - -## Pseudocode - -**Pseudocode** is text that is laid out like a program but written in plain language. Fixed words like REPEAT, IF and WHILE show the building blocks, and the **indentation** shows what belongs to them. The square looks like this: - -\`\`\` -REPEAT 4 times - drive forward 50 cm - turn right -END REPEAT -show "Square done!" -\`\`\` - -And this is the same plan in Java: - -\`\`\`java -for (int i = 0; i < 4; i++) { - mbot.forward(50); - mbot.turnRight(); -} -mbot.showText("Square done!"); -\`\`\` - -Pseudocode has **no fixed way of writing**. Some people write FOR i FROM 1 TO 4, others REPEAT 4 times. All that matters is that anyone can understand it without an explanation. You can't control the computer with it, for that you need Java. - -### Comparison - -- **Structure chart:** boxes inside each other, the loop is an upside-down L. You see the structure at a glance. -- **Flowchart:** symbols with arrows, the loop is an arrow back up. Very clear, but big charts get confusing. -- **Pseudocode:** text with indentation, the loop sits between REPEAT and END REPEAT. Quick to write and already close to Java. - -::: angeberwissen -Baah, who invented flowcharts? A woman and her husband: **Lillian and Frank Gilbreth** presented their "process charts" in 1921, long before the first computer. They used them to draw work steps in factories so they could make them simpler. Lillian Gilbreth had a doctorate in psychology and in 1965 became the first woman elected to the American National Academy of Engineering. Programmers later took over the idea for their plans. -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Flowcharts and pseudocode"](?visustruct&lektion=01-weitere-darstellungen) with baking a cake as a flowchart and the GOTO command. It opens in a new window. - -## Game: Which way of drawing? - -::: spiel frage -titel: Game: Which way of drawing? -intro: Ludwig describes a plan for the mBot2 without saying how it is written down. Is it the structure chart, the flowchart or pseudocode? Six rounds. -antworten: structure chart | flowchart | pseudocode - -runde: -frage: "An arrow leads from the diamond back to 'Drive forward 50 cm'." -richtig: flowchart -> You recognise a loop in a flowchart by the arrow back up. - -runde: -frage: "The loop looks like an upside-down L, with drive forward and turn right inside." -richtig: structure chart -> The upside-down L is the loop in a structure chart. - -runde: -frage: "REPEAT 4 times, with two indented lines below." -richtig: pseudocode -> Fixed words and indentation: that is pseudocode. - -runde: -frage: "'Square done!' sits in a slanted box." -richtig: flowchart -> The slanted box is the parallelogram for input and output. - -runde: -frage: "There are no arrows. If you insert a box, everything below simply moves down." -richtig: structure chart -> Without arrows, no jump in a structure chart can point to the wrong place. - -runde: -frage: "Some write FOR i FROM 1 TO 4, others REPEAT 4 times. Both are right." -richtig: pseudocode -> Pseudocode has no fixed way of writing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can name the elements of a flowchart. → The flowchart -- I can build the square as a flowchart myself. → Exercise: The square as a flowchart -- I can read a plan written as pseudocode. → Pseudocode -- I can compare structure chart, flowchart and pseudocode. → Three ways to write down a plan -::: - -## Quiz - -::: quiz -? How do you recognise a loop in a flowchart? -→ The flowchart -- [ ] By an upside-down L -- [x] By an arrow that leads back up -- [ ] By the word REPEAT -> The upside-down L belongs to the structure chart, REPEAT to pseudocode. - -? Match each flowchart symbol to its meaning. -→ The flowchart -= Oval -> Start or end -= Rectangle -> Statement -= Diamond -> Question with yes and no -= Parallelogram -> Input or output -> "Drive forward 50 cm" is a statement, showing "Square done!" is an output. - -? In the square flowchart, the diamond says "4 sides driven?". Where does the arrow lead for **no**? -→ Exercise: The square as a flowchart -- [x] Back to "Drive forward 50 cm" -- [ ] To the end -- [ ] To showing "Square done!" -> With no, the square isn't finished yet, so the mBot2 drives the next side. - -? In pseudocode, what shows which lines belong to the loop? -→ Pseudocode -- [ ] An arrow -- [x] The indentation and the END REPEAT -- [ ] The colour -> Everything between REPEAT and END REPEAT is indented and repeated. - -? Can the mBot2 run pseudocode directly? -→ Three ways to write down a plan -- [ ] Yes, like Java -- [x] No, pseudocode is only a plan for people -> The mBot2 can run the structure chart or Java. Pseudocode helps with planning. -::: -`,rz=`--- -titel: Variables and output -bereich: Getting started -kurz: Store values in variables, drive the mBot2 with them and show results on the screen. ---- - -## Skills I will gain now - -::: kompetenzen -- create and use a variable -- read a short Java program and type it in the Code area -- tell the data types \`int\`, \`double\`, \`String\` and \`boolean\` apart -- drive a rectangle in Java with two variables -- show a result on the screen with \`mbot.showText\` -::: - -## A variable for the side - -With \`mbot.forward(30)\` the mBot2 drives 30 cm in one go. If you drive a square with it and no loop, the 30 appears four times in your program. To make the square bigger, you have to change it in four places. It is better to store the side length **once** and then use it everywhere. - -That is what **variables** are for. A variable is a box with a name that holds a value: - -\`\`\`java -int side = 30; -mbot.forward(side); -\`\`\` - -- \`int\` is the **data type**: a whole number. -- \`side\` is the **name**. You may choose it yourself. -- \`30\` is the **value**. The \`=\` puts it into the box. - -Wherever \`side\` appears afterwards, the computer uses its value. So \`mbot.forward(side)\` drives 30 cm. - -::: übung vb-variable-seite -titel: Square with a variable -auftrag: In one statement, create the variable \`int side = 30\`. After that the mBot2 drives a square: four times \`side\` centimetres forward and turn right. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: The first statement is \`int side = 30\`. Below it comes a for loop that runs four times. -tipp: Inside the loop are \`mbot.forward(side)\` and \`mbot.turnRight()\`. -lösung: vb-variable-seite-loesung.visustruct -::: - -> Now change only the 30 to 60 and start the simulation. The whole square becomes twice as big. - -## Which data types are there? - -Every variable has a data type. It decides which values fit inside: - -| Data type | What fits inside? | Example | -|---|---|---| -| \`int\` | whole numbers | \`int side = 30;\` | -| \`double\` | decimal numbers, written with a point | \`double wheelCircumference = 20.4;\` | -| \`String\` | text in quotation marks | \`String name = "Ludwig";\` | -| \`boolean\` | true or false | \`boolean wallNear = mbot.getDistance() < 20;\` | - -One wheel rotation takes the mBot2 about 20.4 cm, which is a decimal number. \`wallNear\` holds \`true\` if the wall is closer than 20 cm. You will meet the distance sensor in Lesson 4. - -You can also calculate with variables: \`int distance = 4 * side;\` works out the whole distance around the square. - -## This is what Java looks like - -So far you have dragged boxes. From now on you will also write some programs in **Java**. You already know the boxes, because their text is real Java already. To the right of the structure chart is the [Code](#zeige-codelive) area. It shows your structure chart as Java code, and whatever you type there turns into the structure chart straight away. - -The square with a variable looks like this in Java: - -\`\`\`java -int seite = 30; -for (int i = 0; i < 4; i++) { - mbot.forward(seite); - mbot.turnRight(); -} -\`\`\` - -- Every box is one line. Every statement ends with a **semicolon** \`;\`. -- The for loop is called \`for\`. Its three parts start, condition and step stand in round brackets, separated by \`;\`. -- Whatever the loop repeats stands in **curly brackets** \`{ }\`. Indent it so you can see at once what belongs to the loop. There is no semicolon after \`{\` and \`}\`. -- Upper and lower case matter: \`mbot.turnRight()\` works, \`mbot.turnright()\` doesn't. - -> How to solve a Java exercise: click **Open in editor**. The Code area opens on the right. Type your program there, watch the simulation and finally click **Check**. - -## Now in Java: rectangle - -A rectangle has two different sides. So you need two variables. - -::: übung vb-java-rechteck -titel: Rectangle with variables -code: java -auftrag: Write in Java: create \`int length = 60\` and \`int width = 30\`. The mBot2 drives a rectangle: length, turn right, width, turn right, and all that twice. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -braucht: forSchleife -tipp: Two variables at the top: \`int length = 60;\` and \`int width = 30;\`. Below them a for loop that runs twice. -tipp: Inside the loop: \`mbot.forward(length);\`, \`mbot.turnRight();\`, \`mbot.forward(width);\`, \`mbot.turnRight();\` -lösung: vb-java-rechteck-loesung.visustruct -::: - -## Result on the screen - -The mBot2 can do more than drive, it can also show you something. With \`mbot.showText(…)\` a text appears on the CyberPi's [screen]{teil=screen}. With \`+\` you join text and values: - -\`\`\`java -int side = 30; -int distance = 4 * side; -mbot.showText("Distance: " + distance + " cm"); -\`\`\` - -The screen then shows **Distance: 120 cm**. You will learn more about the screen in Lesson 18. - -::: übung vb-strecke-anzeigen -titel: How far does it drive? -code: java -auftrag: Create \`int length = 60\` and \`int width = 30\` and drive the rectangle as before. Then work out in a variable \`distance\` how far the mBot2 drove all the way round, and show **Distance: 180 cm** on the screen. -fahrt: 2 x (forward 60, turnRight, forward 30, turnRight) -befehle: showText "Distance: 180 cm" -braucht: forSchleife -tipp: All the way round is twice the length and twice the width: \`int distance = 2 * length + 2 * width;\` -tipp: After the loop: \`mbot.showText("Distance: " + distance + " cm");\` -lösung: vb-strecke-anzeigen-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Input and output"](?visustruct&lektion=06-eingabe-und-ausgabe). It opens in a new window. There you print results to the console with \`System.out.println\`. - -## Game: Which data type? - -::: spiel frage -titel: Game: Which data type? -intro: Ludwig has wiped out the data types. Which one fits the gap? Seven rounds. -antworten: int | double | String | boolean - -runde: - ___ side = 30; -frage: Which data type fits? -richtig: int -> 30 is a whole number. - -runde: - ___ wheelCircumference = 20.4; -frage: Which data type fits? -richtig: double -> 20.4 is a decimal number, written with a point in Java. - -runde: - ___ name = "Ludwig"; -frage: Which data type fits? -richtig: String -> Text in quotation marks is a \`String\`. - -runde: - ___ wallNear = mbot.getDistance() < 20; -frage: Which data type fits? -richtig: boolean -> A comparison is true or false, so \`boolean\`. - -runde: - ___ distance = mbot.getDistance(); -frage: Which data type fits? -richtig: int -> The distance sensor gives whole centimetres, for example 38. - -runde: - ___ greeting = "Hello " + "class"; -frage: Which data type fits? -richtig: String -> Two texts joined with \`+\` give a text again. - -runde: - ___ average = 120 / 5.0; -frage: Which data type fits? -richtig: double -> With \`5.0\`, Java calculates with decimal numbers. The result is 24.0. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Read the program and tap the field where the mBot2 is at the end. Watch the values in the variables. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - int side = 60; - mbot.forward(side); - mbot.turnRight(); - mbot.forward(side); -karte: ... | ... | ^.. -ziel: 2,0 -> \`side\` is 60, so two fields up and two to the right. - -runde: - int a = 30; - int b = a * 3; - mbot.forward(b); -karte: ... | ... | ... | ^.. -ziel: 0,0 -> \`b\` is 3 · 30 = 90 cm, so three fields. - -runde: - int distance = 30 * 2; - mbot.backward(distance); -karte: ..^.. | ..... | ..... -ziel: 2,2 -> \`backward\` drives in reverse: it faces up but drives 60 cm down. - -runde: - int side = 30; - for (int i = 0; i < 2; i++) { - mbot.forward(side); - mbot.turnRight(); - side = side + 30; - } -karte: .... | .... | .... | ^... -ziel: 2,2 -> First 30 cm up, then \`side\` grows to 60: two fields to the right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create and use a variable. → A variable for the side -- I can read a short Java program and type it in the Code area. → This is what Java looks like -- I can tell the data types \`int\`, \`double\`, \`String\` and \`boolean\` apart. → Which data types are there? -- I can drive a rectangle in Java with two variables. → Now in Java: rectangle -- I can show a result on the screen with \`mbot.showText\`. → Result on the screen -::: - -## Quiz - -::: quiz -? What does \`mbot.forward(side)\` drive if \`int side = 40;\` comes first? -→ A variable for the side -- [ ] 4 cm -- [x] 40 cm -- [ ] nothing, \`side\` is not a command -> Wherever \`side\` appears, Java uses the value 40. -? Which data type fits \`"Ludwig"\`? -→ Which data types are there? -- [ ] \`int\` -- [ ] \`double\` -- [x] \`String\` -> Text in quotation marks is a \`String\`. -? What does \`mbot.showText("Side: " + 30);\` show on the screen? -→ Result on the screen -- [x] Side: 30 -- [ ] "Side: " + 30 -- [ ] 30 -> The \`+\` joins the text and the number. -::: -`,oz=`--- -titel: Testing step by step -bereich: Getting started -kurz: Use the simulation and a desk check to find out what your program really does. ---- - -## Skills I will gain now - -::: kompetenzen -- control the simulation with Play, Step and Stop -- watch the variables in the simulation -- do a desk check with a table -- find and fix a mistake with the simulation -::: - -## Controlling the simulation - -With the [▶︎ Start simulation](#zeige-simulation) button on the right of the tools, VisuStruct runs your structogram. In the VisuBot world the mBot2 drives along. On the right, the **Controls** appear: - -- **Play** runs the program automatically, step by step. You set how fast under **Pause per step**. -- **Pause** stops for a moment. Then you carry on with Play or Step. -- **Step** runs exactly one box. That way you can watch calmly. -- **Stop** cancels and puts the mBot2 back at the start. - -During the simulation one box is highlighted. **Last step** shows what has just happened, **Variables** shows all variables with their values. - -::: diagramm vb-l03-wachsend-loesung.visustruct -Each side gets 10 cm longer. Open the diagram in the editor and go through it with **Step**. Watch \`side\` and \`i\`. -::: - -## The desk check - -In a **desk check** you play the computer yourself. You go through the structogram with pen and paper and write down the values of the variables after every step. For the diagram above the table looks like this: - -| Step | \`i\` | \`side\` | The mBot2 … | -|---|---|---|---| -| \`int side = 20\` | – | 20 | | -| \`mbot.forward(side)\` | 0 | 20 | drives 20 cm | -| \`mbot.turnRight()\` | 0 | 20 | turns right | -| \`side = side + 10\` | 0 | 30 | | -| \`mbot.forward(side)\` | 1 | 30 | drives 30 cm | -| … | … | … | … | - -Then compare your table with the simulation. Do the values under **Variables** match after every step? - -::: angeberwissen -"Computer" used to be a job: people who calculated with pen and paper. One of the best was Katherine Johnson at NASA. In 1962 the astronaut John Glenn only wanted to fly once she had checked the new IBM computer's orbit by hand: "If she says they're good, I'm ready to go." -::: - -## Finding mistakes with the simulation - -Ludwig wanted to build the spiral from above: 20 cm, 30 cm, 40 cm. But his mBot2 drives 30, 40 and 50 cm. The program runs without an error message, it just does the wrong thing. This kind of mistake is called a **logic error**. You find it best with **Step** and the variables. - -::: übung vb-spirale-falsch -titel: Fix the spiral -auftrag: "Open in editor" loads Ludwig's structogram. Go through it with **Step** and watch \`side\`. Find out why the first side is 30 cm long and fix it. The mBot2 should drive 20, 30 and 40 cm and turn right after each side. -start: vb-spirale-falsch-start.visustruct -fahrt: forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -braucht: forSchleife -tipp: Look at the value of \`side\` when \`mbot.forward(side)\` comes up. Is the order inside the loop right? -tipp: \`side = side + 10\` must come **after** \`mbot.forward(side)\` and \`mbot.turnRight()\`. Drag the box to the end of the loop. -lösung: vb-spirale-falsch-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Simulation and desk check"](?visustruct&lektion=07-simulation-und-schreibtischtest). It opens in a new window. - -## Game: Be the computer - -::: spiel frage -titel: Game: Be the computer -intro: Do the desk check in your head. What does the mBot2 do, and what values do the variables have? Five rounds. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side + 10; - } -frage: What value does \`side\` have after the loop? -- 40 -- [x] 50 -- 30 -> 20, then 30, 40 and after the third run 50. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side + 10; - } -frage: How far does the mBot2 drive the third time? -- [x] 40 cm -- 50 cm -- 30 cm -> It drives 20, 30 and 40 cm. The 50 only ends up in \`side\` afterwards. - -runde: - int side = 20; - for (int i = 0; i < 3; i++) { - side = side + 10; - mbot.forward(side); - } -frage: How far does the mBot2 drive the first time? -- 20 cm -- [x] 30 cm -- 10 cm -> \`side\` grows before driving. That was exactly Ludwig's logic error. - -runde: - int distance = 0; - for (int i = 0; i < 4; i++) { - mbot.forward(25); - distance = distance + 25; - } - mbot.showText("Distance: " + distance); -frage: What does the screen show at the end? -- Distance: 25 -- [x] Distance: 100 -- Distance: 0 -> 25 is added four times: 25, 50, 75, 100. - -runde: - int side = 10; - for (int i = 0; i < 3; i++) { - mbot.forward(side); - side = side * 2; - } -frage: How far does the mBot2 drive in total? -- 60 cm -- [x] 70 cm -- 80 cm -> It drives 10, 20 and 40 cm. Together that is 70 cm. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 6 1 SIMULATION: runs your program on the screen, even without a robot -- ↓ 1 10 SEQUENCE: steps that come one after another -- ↓ 3 4 STRUCTOGRAM: another name for a Nassi-Shneiderman diagram -- ↓ 3 6 DATATYPE: decides which values fit into a variable, e.g. int -- ↓ 1 8 ALGORITHM: exact step-by-step instructions -- → 9 3 LOOP: repeats steps, looks like a tilted L in the structure chart -- → 12 3 VARIABLE: a little box with a name that holds a value -- ↓ 4 1 DESKCHECK: you play the computer with pen and paper -lösung: 11 4, 9 4, 12 8, 6 9, 7 6 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can control the simulation with Play, Step and Stop. → Controlling the simulation -- I can watch the variables in the simulation. → Controlling the simulation -- I can do a desk check with a table. → The desk check -- I can find and fix a mistake with the simulation. → Finding mistakes with the simulation -::: - -## Quiz - -::: quiz -? Which button runs exactly one box? -→ Controlling the simulation -- [ ] Play -- [x] Step -- [ ] Stop -> With Step you go box by box. Play carries on by itself. -? How far does the mBot2 drive in the second pass of "Growing sides"? -→ The desk check -- [ ] 20 cm -- [x] 30 cm -- [ ] 40 cm -> After the first pass \`side\` has grown by 10, so it is 30. -? The program runs without a message, but the mBot2 drives the wrong way. What kind of mistake is it? -→ Finding mistakes with the simulation -- [ ] a typo -- [x] a logic error -- [ ] a fault in the robot -> The program does exactly what it says, just not what was meant. -::: -`,lz=`--- -titel: Reading sensors -bereich: Sensors and decisions -kurz: The mBot2 measures something, and your program decides. ---- - -## Skills I will gain now - -::: kompetenzen -- explain the difference between a command and a sensor -- build a sensor into a condition -::: - -## Commands and sensors - -So far you have only given the mBot2 **commands**: move forward, turn right. It carried them out without paying attention to its surroundings. - -Now the **sensors** join in. Among the [elements](#zeige-elemente) there is a group **mBot2 sensors** for them: - -| Sensor | Text | What you get | -|---|---|---| -| Distance | \`mbot.getDistance()\` | distance to the next wall in cm, e.g. \`38\` | -| Line | \`mbot.getLine()\` | \`0\` if everything under the line sensor is bright, otherwise a number greater than 0 | - -The mBot2 has even more sensors: colours, buttons and joystick, light, loudness and tilt. You get to know each of them in lessons 6 to 13, the motors in lesson 20 and the stopwatch and battery in lesson 21. - -A sensor is **not a box of its own**. It gives you a number, and with that number you build a **condition**: - -\`\`\`java -mbot.getDistance() < 20 -\`\`\` - -This means: "Is the wall closer than 20 cm?" The answer is true or false. - -> How to build it: first drag a **branch** into the structure chart. Then drag the sensor **Distance** onto the branch. The condition now says \`mbot.getDistance()\`. You add the comparison \`< 20\` yourself. - -## Wall ahead? - -The mBot2 should decide by itself: if there is a wall ahead, it turns right. If the way is free, it drives forward a bit. - -This exercise has **two worlds**: in one the wall is right in front of it, in the other there is plenty of space. Your program must do the right thing in both. - -::: übung vb-wand-vorn -titel: Wall ahead? -auftrag: Build a branch with the condition \`mbot.getDistance() < 20\`. If **true**, the mBot2 turns right; if **false**, it moves forward. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Drag a branch into the structure chart and then the sensor **Distance** onto the branch. Write \`< 20\` after it. -tipp: The true branch contains \`mbot.turnRight()\`, the false branch \`mbot.forward()\`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -In the [Code](#zeige-codelive) area you can also see your structure chart as Java. A branch is called **if** there, and the false branch **else**. The condition goes in round brackets, each branch in curly brackets: - -\`\`\`java -if (mbot.getDistance() < 20) { - mbot.turnRight(); -} else { - mbot.forward(); -} -\`\`\` - -> You don't have to type \`if\` yourself yet. There is more about it in the lesson on the ultrasonic sensor. - -## What does the sensor give you? - -A sensor gives you a **value**, just like a variable. When the mBot2 drives on, the value changes: the distance gets smaller as the wall comes closer. Only a **comparison** turns the value into a question with the answer \`true\` or \`false\`. - -| Condition | Question | Answer at a distance of 38 cm | -|---|---|---| -| \`mbot.getDistance() < 20\` | Is the wall closer than 20 cm? | \`false\` | -| \`mbot.getDistance() > 30\` | Is there more than 30 cm of space? | \`true\` | -| \`mbot.getLine() == 0\` | Is everything bright under the mBot2? | depends on the floor | - -You can compare with \`<\` (less than), \`>\` (greater than), \`<=\` (less than or equal), \`>=\` (greater than or equal), \`==\` (equal) and \`!=\` (not equal). - -> For "equal" you write **two** equals signs: \`==\`. A single \`=\` puts a value into a variable, which is something completely different. - -> 📘 **More without the robot:** VisuStruct has the lesson ["Branch"](?visustruct&lektion=08-verzweigung). It opens in a new window. - -::: übung vb-fehler-04-sensoren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Wall ahead?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-04-sensoren-start.visustruct -welt: wand-nah -> turnRight -welt: wand-frei -> forward -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.turnRight()\`. -lösung: vb-wand-vorn-loesung.visustruct -::: - -## Game: Command or sensor? - -::: spiel frage -titel: Game: Command or sensor? -intro: Does the line do something, or does it give a value? Eight rounds. -antworten: command | sensor - -runde: - mbot.forward(30) -frage: Command or sensor? -richtig: command -> It drives 30 cm. That is doing something. - -runde: - mbot.getDistance() -frage: Command or sensor? -richtig: sensor -> It gives the distance in cm, for example 38. - -runde: - mbot.turnRight() -frage: Command or sensor? -richtig: command -> Turning is doing something. - -runde: - mbot.getLine() -frage: Command or sensor? -richtig: sensor -> It gives 0 or a number greater than 0. - -runde: - mbot.playTone(440, 300) -frage: Command or sensor? -richtig: command -> A tone is an output. - -runde: - mbot.isButtonA() -frage: Command or sensor? -richtig: sensor -> It gives true or false: is button A pressed? - -runde: - mbot.showText("Hello") -frage: Command or sensor? -richtig: command -> Showing something is doing something. - -runde: - mbot.getBrightness() -frage: Command or sensor? -richtig: sensor -> It measures how bright it is, from 0 to 100. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain the difference between a command and a sensor. → Commands and sensors -- I can build a sensor into a condition. → Wall ahead? -::: - -## Quiz - -::: quiz -? The wall is 12 cm away. Is \`mbot.getDistance() < 20\` true or false? -→ Wall ahead? -- [x] true -- [ ] false -> 12 is less than 20, so the condition is true. -? The mBot2 is standing on a bright floor. What does \`mbot.getLine()\` give you? -→ Commands and sensors -- [x] 0 -- [ ] 15 -- [ ] the distance in cm -> 0 means: none of the four probes sees anything dark. -? Why is the sensor not a box of its own in the structure chart? -→ Wall ahead? -- [x] It does nothing, it only gives a value. You need that value in a condition. -- [ ] Because the mBot2 would get too slow otherwise. -- [ ] Because sensors only work in Java. -> Commands are statements. Sensors are values, just like a variable. -::: -`,uz=`--- -titel: Up to the wall -bereich: Sensors and decisions -kurz: The mBot2 measures the distance and stops in time. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the distance with \`mbot.getDistance()\` -- decide when to use a while loop instead of a for loop -- find a goal with the line sensor -::: - -## How far away is the wall? - -So far you told the mBot2 exactly how far to drive. Now it should notice by itself when to stop. - -That is what the ultrasonic sensor is for. \`mbot.getDistance()\` gives the distance to the next wall in centimetres, for example \`38\`. You can use it like a number: - -\`\`\`java -mbot.getDistance() > 15 -\`\`\` - -This is a **condition**: it is true as long as the wall is more than 15 cm away. - -> Start the [simulation](#zeige-simulation): below the world you always see the current distance. When the mBot2 gets closer to the wall, the number gets smaller. - -## Three worlds, one program - -This exercise has **three worlds**. In each one the wall is somewhere else, and the mBot2 should stop at the goal with the green flag every time, without bumping into it. - -A fixed number of **move forward** only works in one world. You need a loop that keeps driving as long as the condition is true: the **while loop**. - -::: übung vb-bis-wand -titel: Up to the wall -auftrag: The mBot2 should drive forward until the wall is at most 15 cm away. Your program must work in all three worlds. Above the simulation you choose which world you see. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Distance** onto the loop. Then only the comparison is missing. -tipp: A while loop with the condition \`mbot.getDistance() > 15\`, with \`mbot.forward()\` inside. -lösung: vb-bis-wand-loesung.visustruct -::: - -::: ludwig -A loop that checks a condition before it drives is like me at the feeding trough: first I check whether there is anything left, and only then do I keep eating. -::: - -## To the goal - -Now it is the second sensor's turn. The goal is a black square with a green flag. When the mBot2 drives onto it, its line sensor sees it: \`mbot.getLine()\` is then no longer \`0\`. - -::: übung vb-bis-ziel -titel: Drive to the goal -auftrag: The mBot2 should keep driving forward until it is on the goal. Again there are two worlds. -welt: ziel-kurz, ziel-lang -braucht: whileSchleife -tipp: The loop should run as long as the line sensor sees **nothing**. Drag the sensor **Line** onto the while loop. -tipp: The condition is \`mbot.getLine() == 0\`. Inside the loop is \`mbot.forward()\`. -lösung: vb-bis-ziel-loesung.visustruct -::: - -## What does the loop check? - -> 📘 **More without the robot:** VisuStruct has the lesson ["While loop"](?visustruct&lektion=11-while-schleife). It opens in a new window. - -::: übung vb-fehler-05-bis-zur-wand -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Up to the wall", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-bis-zur-wand-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.forward()\`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## Game: How often does it drive? - -::: spiel frage -titel: Game: How often does it drive? -intro: Each drive is 10 cm long. Work out how often the mBot2 drives forward before the loop stops. Six rounds. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 40 cm away. How often does it drive forward? -- 2 times -- [x] 3 times -- 4 times -> 40, 30 and 20 are greater than 15: three drives. At 10 the loop stops. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 15 cm away. How often does it drive forward? -- [x] not at all -- once -- 2 times -> 15 is not greater than 15. The condition is false right at the start. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall starts 50 cm away. Where does it stop? -- 15 cm from the wall -- [x] 10 cm from the wall -- 0 cm, it bumps into it -> 50, 40, 30, 20: four drives. Then the wall is 10 cm away and the loop stops. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } -frage: The mBot2 is already standing on the black goal. How often does it drive forward? -- [x] not at all -- once -- forever -> On black, \`mbot.getLine()\` is not 0. The condition is false straight away. - -runde: -frage: The mBot2 should drive forward exactly five times. Which loop fits best? -- [x] a for loop -- a while loop with \`mbot.getDistance()\` -- no loop at all -> The number is known in advance. That is what the for loop is for. - -runde: -frage: The mBot2 should drive until it almost touches the wall. Which loop fits? -- a for loop that runs ten times -- [x] a while loop with \`mbot.getDistance()\` -- no loop at all -> You don't know in advance how far away the wall is. The while loop checks on the way. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Grey is a wall. Tap the field where the mBot2 is at the end. Each field is 30 cm, and the sensor is at the front of the mBot2. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> It drives as long as more than 15 cm ahead are free. It stops right in front of the wall. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,1 -> However far away the wall is, the while loop stops it right in front of it. - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(60); -karte: ##### | ..... | ..... | ..... | ^.... -ziel: 2,2 -> With more than 45 cm it stops one field earlier, turns and drives two fields to the right. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,0 -> It drives until its line sensor sees black. Then it is standing on the black field. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the distance with \`mbot.getDistance()\`. → How far away is the wall? -- I can decide when to use a while loop instead of a for loop. → Three worlds, one program -- I can find a goal with the line sensor. → To the goal -::: - -## Quiz - -::: quiz -? The mBot2 is 40 cm in front of the wall. How often does it drive forward with \`while (mbot.getDistance() > 15) mbot.forward();\` if each move is 10 cm? -→ How far away is the wall? -- [ ] not at all -- [ ] once -- [x] until the wall is at most 15 cm away -- [ ] always exactly five times -> The while loop checks the condition before every move. When the wall is close enough, it stops. -? Why is a for loop with a fixed count not enough here? -→ Three worlds, one program -- [x] Because the wall is at a different distance in every world. -- [ ] Because a for loop must not contain statements. -- [ ] Because the mBot2 does not understand for loops. -> The for loop knows beforehand how often it runs. The while loop decides on the way. -::: -`,mz=`--- -titel: Drive first, then check -bereich: Sensors and decisions -kurz: The do-while loop always drives at least once and only checks afterwards. ---- - -## Skills I will gain now - -::: kompetenzen -- explain that the do-while loop checks its condition only after the body -- compare a while loop and a do-while loop -- write a do-while loop in Java -::: - -## Drive first, then check - -The while loop from Lesson 5 checks **first** and then drives. If the wall is already right in front of the mBot2, it does not drive off at all. It is a **pre-test loop**: the condition is at the top, in the head. - -The **do-while loop** is a **post-test loop**. The condition is at the bottom, in the foot of the box: - -1. Run the body once. -2. Check the condition. -3. If it is true, start again at 1. If it is false, carry on below the loop. - -That is why the body **always runs at least once**. When you click the loop, the word \`while\` is in front of the text field in the Object Inspector: the condition means "repeat as long as …". - -| Distance at the start | While loop | Do-while loop | -|---|---|---| -| 50 cm | drives until 15 cm | drives until 15 cm | -| 10 cm | does **not drive at all** | drives 10 cm **once** | - -::: übung vb-do-while -titel: At least one step -auftrag: Build a structogram with a do-while loop: the mBot2 drives forward as long as the distance is greater than 15 cm, but **at least once**. Then it plays a tone. -messwerte: getDistance=10 -> forward, tone -welt: wand-kurz -> tone -braucht: doUntilSchleife -tipp: Drag the **do-while loop** from the Elements into the structogram. **Drive forward** goes into the box, the condition \`mbot.getDistance() > 15\` into the foot. -tipp: Below the loop (not inside it) goes \`mbot.playTone(440, 300)\`. -lösung: vb-do-while-loesung.visustruct -::: - -> When checking, the wall is once only 10 cm in front of the mBot2. A while loop would not drive at all then. - -## Now in Java: counting steps - -In Java, \`do\` is at the top and \`while (…)\` with a semicolon at the bottom: - -\`\`\`java -do { - mbot.forward(); -} while (mbot.getDistance() > 15); -\`\`\` - -::: übung vb-java-schritte-zaehlen -titel: Count the steps -code: java -auftrag: Write in Java: create \`int steps = 0\`. In a do-while loop the mBot2 drives forward and counts \`steps\` up by one, as long as the distance is greater than 15 cm. Then the screen shows for example **Steps: 3**. -welt: wand-kurz -> screen "Steps: 3" -messwerte: getDistance=10 -> forward, screen "Steps: 1" -braucht: doUntilSchleife -tipp: \`do { mbot.forward(); steps = steps + 1; } while (mbot.getDistance() > 15);\` -tipp: After the loop \`mbot.showText("Steps: " + steps);\`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Do-while loop"](?visustruct&lektion=12-do-while-schleife). It opens in a new window. - -::: übung vb-fehler-05-erst-fahren-dann-pruefen -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Count the steps", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct -welt: wand-kurz -> screen "Steps: 3" -messwerte: getDistance=10 -> forward, screen "Steps: 1" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`mbot.showText("Steps: " + steps)\`. -lösung: vb-java-schritte-zaehlen-loesung.visustruct -::: - -## Game: Head or foot? - -::: spiel frage -titel: Game: Head or foot? -intro: While or do-while: how often does the mBot2 drive forward? Each drive is 10 cm long. Six rounds. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(); - } -frage: The wall is 10 cm in front of the mBot2. How often does it drive forward? -- [x] not at all -- once -- 2 times -> The while loop checks first. 10 is not greater than 15. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: The wall is 10 cm in front of the mBot2. How often does it drive forward? -- not at all -- [x] once -- 2 times -> The do-while loop drives first and checks afterwards. It always drives once. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: The wall is 40 cm in front of the mBot2. How often does it drive forward? -- 2 times -- [x] 3 times -- 4 times -> After the first drive it is 30 cm, then 20, then 10. At 10 it stops. Here both loops drive the same number of times. - -runde: - int steps = 0; - do { - mbot.forward(); - steps = steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + steps); -frage: The wall is 10 cm in front of the mBot2. What does the screen show? -- Steps: 0 -- [x] Steps: 1 -- Steps: 2 -> The body runs once, so \`steps\` counts up once. - -runde: -frage: Which loop is foot-controlled? -- the while loop -- [x] the do-while loop -- the for loop -> Its condition is at the bottom, in the foot. - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() > 15) -frage: Why doesn't this code run? -- \`do\` must be written with a capital letter. -- [x] The semicolon after \`while (…)\` is missing. -- The condition must be at the top. -> The do-while loop ends with \`} while (condition);\`. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - do { - mbot.forward(); - } while (mbot.getDistance() < 15); -frage: Should drive forward at least once, and keep going as long as the wall is more than 15 cm away. -fehler: 3 -besser: } while (mbot.getDistance() > 15); -> "More than 15 cm" means \`> 15\`. - -runde: - do - mbot.forward(); - } while (mbot.getDistance() > 15); -frage: Should drive forward at least once, and keep going as long as the wall is more than 15 cm away. -fehler: 1 -besser: do { -> The opening curly bracket after \`do\` is missing. - -runde: - int steps = 0; - do { - mbot.forward(); - steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + steps); -frage: Should count the steps and show them at the end. -fehler: 4 -besser: steps = steps + 1; -> \`steps + 1\` only calculates, it doesn't store anything. The result has to go back into \`steps\` with \`=\`. - -runde: - int steps = 0; - do { - mbot.forward(); - steps = steps + 1; - } while (mbot.getDistance() > 15); - mbot.showText("Steps: " + Steps); -frage: Should count the steps and show them at the end. -fehler: 6 -besser: mbot.showText("Steps: " + steps); -> Java tells capital and small letters apart. There is no \`Steps\`. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Grey is a wall. While or do-while: tap the field where the mBot2 is at the end. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getDistance() > 45) { - mbot.forward(30); - } -karte: ##### | ..... | ..^.. -ziel: 2,2 -> The wall is already close. The while loop checks first and doesn't set off at all. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 45); -karte: ##### | ..... | ..^.. -ziel: 2,1 -> The do-while loop drives once first and checks afterwards. One field further on it stops. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); - mbot.turnLeft(); - mbot.forward(30); -karte: ##### | ..... | ..^.. -ziel: 1,1 -> One field forward, then the wall is right in front. Then left and one more field. - -runde: - do { - mbot.forward(30); - } while (mbot.getDistance() > 15); -karte: ##### | ..... | ..... | ..... | ..^.. -ziel: 2,1 -> If the wall is far away, while and do-while drive the same distance: right up to the wall. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain that the do-while loop checks its condition only after the body. → Drive first, then check -- I can compare a while loop and a do-while loop. → Drive first, then check -- I can write a do-while loop in Java. → Now in Java: counting steps -::: - -## Quiz - -::: quiz -? Where is the condition of a do-while loop? -→ Drive first, then check -- [ ] at the top, in the head -- [x] at the bottom, in the foot -- [ ] nowhere -> Post-test means: first the body, then the condition. -? The wall is 10 cm in front of the mBot2. How often does \`do { mbot.forward(); } while (mbot.getDistance() > 15);\` drive? -→ Drive first, then check -- [ ] not at all -- [x] once -- [ ] until it bumps -> The body always runs at least once. After that the condition is false. -? What comes after \`while (…)\` at the end of a do-while loop in Java? -→ Now in Java: counting steps -- [x] a semicolon \`;\` -- [ ] a curly bracket \`{\` -- [ ] nothing -> \`} while (condition);\` ends with a semicolon. -::: -`,cz=`--- -titel: Counting and adding up -bereich: Sensors and decisions -kurz: Count along with a variable and add values together. ---- - -## Skills I will gain now - -::: kompetenzen -- create a counter variable and count it up in a loop -- build a sum in a loop -- show the result on the screen after the loop -::: - -## Counting along - -How many steps does the mBot2 drive to the wall? You don't know in advance, the world is different every time. So the mBot2 should **count along**. You need three boxes for that: - -1. **Before** the loop: \`int steps = 0\`. At the start nothing has been counted yet. -2. **Inside** the loop: \`steps = steps + 1\`. One more after every step. -3. **After** the loop: show the result, for example with \`mbot.showText("Steps: " + steps)\`. - -Read \`steps = steps + 1\` from right to left: take the old value of \`steps\`, add 1 and store the result in \`steps\` again. A variable like this is called a **counter variable**, or counter for short. - -> Careful: if \`int steps = 0\` is **inside** the loop, the counter starts again at 0 in every pass. Then the result is always 1. - -::: angeberwissen -Your phone counts your steps exactly like this: with every step it calculates \`steps = steps + 1\`. I prefer counting my blades of grass. I'm at 4 million. -::: - -::: übung vb-schritte-welt -titel: Steps to the wall -auftrag: Build a structure chart: the mBot2 drives forward as long as the distance is greater than 15. It counts its steps along the way. Then it shows, for example, **Steps: 3** on the screen. There are three worlds. -welt: wand-kurz -> screen "Steps: 3" -welt: wand-mittel -> screen "Steps: 7" -welt: wand-lang -> screen "Steps: 12" -braucht: whileSchleife -tipp: The first box is \`int steps = 0\`. Then comes the while loop with \`mbot.getDistance() > 15\`. -tipp: Inside the loop are **drive forward** and \`steps = steps + 1\`. After the loop is \`mbot.showText("Steps: " + steps)\`. -lösung: vb-schritte-welt-loesung.visustruct -::: - -## Adding up - -A counter always grows by 1. A **sum** grows by different values. The mBot2 drives a spiral: first 10 cm, then 20, then 30, then 40. How far has it driven in total? - -| Pass | \`side\` | \`total\` afterwards | -|---|---|---| -| before | 10 | 0 | -| 1 | 10 | 0 + 10 = 10 | -| 2 | 20 | 10 + 20 = 30 | -| 3 | 30 | 30 + 30 = 60 | -| 4 | 40 | 60 + 40 = 100 | - -The pattern is the same as for counting. Only instead of 1 you now add the value that comes in: \`total = total + side\`. - -## Now in Java: spiral with distance - -::: übung vb-java-spirale-strecke -titel: Spiral with distance -code: java -auftrag: Write in Java: create \`int side = 10\` and \`int total = 0\`. Four times: drive \`side\` cm forward, turn right, add \`side\` to \`total\`, make \`side\` 10 bigger. Then show **Distance: 100 cm** on the screen. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Distance: 100 cm" -braucht: forSchleife -tipp: The for loop has four lines: \`mbot.forward(side);\`, \`mbot.turnRight();\`, \`total = total + side;\` and \`side = side + 10;\`. -tipp: After the loop comes \`mbot.showText("Distance: " + total + " cm");\`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -::: übung vb-fehler-05-zaehlen-und-summieren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Spiral with distance", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-05-zaehlen-und-summieren-start.visustruct -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight -befehle: showText "Distance: 100 cm" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`total = total + side\`. -lösung: vb-java-spirale-strecke-loesung.visustruct -::: - -## Game: what is in the variable at the end? - -::: spiel frage -titel: Game: what is in the variable at the end? -intro: Ludwig has written small programs. Work them out in your head. Five rounds. - -runde: -frage: \`int z = 0\`, then three times \`z = z + 1\`. What is \`z\`? -- 1 -- [x] 3 -- 0 -> Each pass adds 1: 1, 2, 3. - -runde: -frage: \`int sum = 0\`, then \`sum = sum + 5\` and \`sum = sum + 7\`. What is \`sum\`? -- 7 -- [x] 12 -- 5 -> 0 + 5 = 5, then 5 + 7 = 12. - -runde: -frage: A loop runs four times. Inside it are \`int z = 0\` and \`z = z + 1\`. What is \`z\` at the end? -- 4 -- [x] 1 -- 0 -> The counter is set back to 0 in every pass. It belongs before the loop. - -runde: -frage: \`int total = 0\`. The mBot2 drives 20, 20 and 30 cm and adds each one. What is \`total\`? -- 30 -- 20 -- [x] 70 -> 20 + 20 + 30 = 70. - -runde: -frage: \`int steps = 10\`, then twice \`steps = steps + 1\`. What is \`steps\`? -- 2 -- [x] 12 -- 11 -> Here the counter starts at 10, not at 0. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a counter variable and count it up in a loop. → Counting along -- I can build a sum in a loop. → Adding up -- I can show the result on the screen after the loop. → Now in Java: spiral with distance -::: - -## Quiz - -::: quiz -? Where do you create the counter variable \`int steps = 0\`? -→ Counting along -- [x] Before the loop -- [ ] Inside the loop -- [ ] After the loop -> Inside the loop it would become 0 again in every pass. -? What does \`steps = steps + 1\` do? -→ Counting along -- [ ] It compares steps with steps + 1. -- [x] It takes the old value, adds 1 and stores the result. -- [ ] It sets steps to 1. -? How do you add the side length \`side\` to the sum \`total\`? -→ Adding up -- [ ] \`total = side\` -- [ ] \`side = total + side\` -- [x] \`total = total + side\` -::: -`,dz=`--- -titel: The ultrasonic sensor -bereich: Sensors and decisions -kurz: Measure the distance, keep your distance, and do it all in Java. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what \`mbot.getDistance()\` measures -- put one branch inside another -- write a while loop with the distance in Java -::: - -## How does the mBot2 measure distance? - -::: mbot2 -focus: ultrasonic -view: front -::: - -The [ultrasonic sensor]{part=ultrasonic} sends out a very high sound and waits for the echo. The longer the echo takes, the further away the wall is. \`mbot.getDistance()\` gives you the distance in **centimetres**, from about 3 to 300. - -| Sensor | Text | Value | -|---|---|---| -| Distance | \`mbot.getDistance()\` | whole number in cm, e.g. \`38\` | - -> In the [simulation](#zeige-simulation) the current distance is always shown below the world. With a real mBot2 you can hold your hand in front of the sensor and watch the number change. - -## Keep your distance in the structure chart - -The mBot2 should behave like a dog on a lead: if your hand is too close (under 10 cm), it backs away. If it is too far away (over 20 cm), it comes closer. In between, it stays where it is. - -For this you need two branches, one inside the other. The outer one checks “too close?”, the inner one in the false branch checks “too far?”. - -::: übung vb-abstand-halten -titel: Keep your distance -auftrag: Build a structure chart: if the distance is less than 10, the mBot2 drives back. Otherwise: if the distance is greater than 20, it drives forward. Otherwise it does nothing. -messwerte: getDistance=5 -> backward -messwerte: getDistance=35 -> forward -messwerte: getDistance=15 -> none -braucht: verzweigung -tipp: Drag a branch into the structure chart and the sensor **Distance** onto it. Write \`< 10\` after it. The true branch gets **move back**. -tipp: The false branch gets a second branch with \`mbot.getDistance() > 20\` and **move forward** in its true branch. Its false branch stays empty. -lösung: vb-abstand-halten-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance to 5, 35 and 15 cm one after the other and looks at what your mBot2 does. - -## Now in Java - -In the [Code](#zeige-codelive) area you already see your structure chart as Java code. A branch is called \`if\` there, the false branch is \`else\`: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.backward(); -} else { - ... -} -\`\`\` - -You type a sensor like a command: \`mbot.\` and then \`getDistance()\`. After the dot the editor suggests all commands and sensors. - -::: übung vb-java-wand-piep -titel: To the wall and beep -code: java -auftrag: Write in Java: the mBot2 drives forward as long as the wall is more than 15 cm away. Then it plays a tone. There are three worlds. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: A while loop in Java is \`while (condition) { … }\`. The condition is \`mbot.getDistance() > 15\`. -tipp: Inside the loop is \`mbot.forward();\`, after the loop \`mbot.playTone(440, 300);\`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -::: übung vb-fehler-06-abstand -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To the wall and beep", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-abstand-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be \`mbot.playTone(440, 300)\`. -lösung: vb-java-wand-piep-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should do and tap the wrong line before time runs out. Six rounds, 30 seconds each. -zeit: 30 - -runde: - if (mbot.getDistance() < 10) { - mbot.forward(); - } - if (mbot.getDistance() > 20) { - mbot.forward(); - } -frage: Under 10 cm it should drive back, over 20 cm forward. -fehler: 2 -besser: mbot.backward(); -> If the hand is too close, it has to go back, not forward. - -runde: - while (mbot.getDistance() < 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive forward as long as the wall is more than 15 cm away, then beep. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> "More than 15 cm away" means \`> 15\`. - -runde: - while (mbot.getDistance > 15) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive to the wall and then beep. -fehler: 1 -besser: while (mbot.getDistance() > 15) { -> A sensor is a method. Without brackets, Java can't find it. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } els { - mbot.forward(); - } -frage: Under 10 cm it should drive back, otherwise forward. -fehler: 3 -besser: } else { -> The false branch is called \`else\`. - -runde: - while (mbot.getDistance() > 15) { - mbot.turnRight(); - } - mbot.playTone(440, 300); -frage: Should drive to the wall and then beep. -fehler: 2 -besser: mbot.forward(); -> It should drive forward, not turn. - -runde: - if (mbot.getDistance() < 10) { - mbot.backward(); - } else { - if (mbot.getDistance() < 20) { - mbot.forward(); - } - } -frage: Under 10 cm back, over 20 cm forward, in between stand still. -fehler: 4 -besser: if (mbot.getDistance() > 20) { -> It should only drive forward if the hand is more than 20 cm away. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what \`mbot.getDistance()\` measures. → How does the mBot2 measure distance? -- I can put one branch inside another. → Keep your distance in the structure chart -- I can write a while loop with the distance in Java. → Now in Java -::: - -## Quiz - -::: quiz -? \`mbot.getDistance()\` gives \`8\`. What does the program from “Keep your distance” do? -→ Keep your distance in the structure chart -- [x] It drives back. -- [ ] It drives forward. -- [ ] It stays where it is. -> 8 is less than 10, so the first condition is already true. -? What is the false branch of a branch called in Java? -→ Now in Java -- [ ] \`then\` -- [x] \`else\` -- [ ] \`otherwise\` -> \`if\` is the start, \`else\` the false branch. -::: -`,hz=`--- -titel: Fixed values with final -bereich: Sensors and decisions -kurz: Create a constant so that an important number appears in only one place. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what a constant is -- create a constant in the structure chart and in Java -- use a constant in a condition and in a command -::: - -## One number in many places - -Ludwig has written a program for the hallway: the mBot2 drives up to 20 cm from the wall, turns around and drives up to 20 cm from the other wall. - -::: diagramm vb-l06k-ohne.visustruct -There and back. The number 20 appears in two places. -::: - -Now the mBot2 should stop at 30 cm instead. Ludwig has to find and change every 20. If he forgets one, the mBot2 stops once at 30 and once at 20 cm. In long programs this happens quickly. - -::: angeberwissen -Professionals call a number that just sits in the program a **magic number**. Magic, because nobody remembers what it means. The only magic I like is magic grass. -::: - -## Creating a constant - -A **constant** is better. It is a variable whose value stays the same once it is set. You create it once at the very top and after that you only use its name. - -| | Structure chart | Java | -|---|---|---| -| create | \`const int DISTANCE = 20\` | \`final int DISTANCE = 20;\` | -| use | \`mbot.getDistance() > DISTANCE\` | \`mbot.getDistance() > DISTANCE\` | - -In the structure chart you write \`const\` in front, in Java \`final\`. Both mean: this value does not change any more. Names of constants are written in **capital letters**, so you can spot them right away. - -::: diagramm vb-l06k-mit.visustruct -The same program with the constant DISTANCE. For 30 cm you only change the first line. -::: - -::: übung vb-konstante-wand -titel: Wall with a constant -auftrag: Create the constant \`DISTANCE\` with the value 20. The mBot2 drives forward as long as the distance is greater than \`DISTANCE\`. Then it plays a tone. There are three worlds. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: wand-lang -> tone -braucht: whileSchleife -tipp: The first box is a statement with \`const int DISTANCE = 20\`. Below it comes a while loop. -tipp: The loop condition is \`mbot.getDistance() > DISTANCE\`. Inside the loop is **drive forward**, after the loop **play tone**. -lösung: vb-konstante-wand-loesung.visustruct -::: - -## Now in Java: square with a constant - -A constant also fits into a command. With \`final int SIDE = 30;\`, \`mbot.forward(SIDE);\` drives exactly 30 cm. If the square should get bigger, you only change that one line. - -::: übung vb-java-konstante-seite -titel: Square with a constant -code: java -auftrag: Write in Java: create the constant \`SIDE\` with the value 30. Then use a for loop to drive a square with side length \`SIDE\`. -fahrt: 4 x (forward 30, turnRight) -braucht: forSchleife -tipp: The first line is \`final int SIDE = 30;\`. Then comes \`for (int i = 0; i < 4; i++) { … }\`. -tipp: Inside the loop are \`mbot.forward(SIDE);\` and \`mbot.turnRight();\`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -::: übung vb-fehler-06-konstanten-final -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Square with a constant", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-konstanten-final-start.visustruct -fahrt: 4 x (forward 30, turnRight) -tipp: The error message names an unknown variable. What is the constant called where it is created? Constants are written in capital letters. -tipp: It should be \`mbot.forward(SIDE)\`. -lösung: vb-java-konstante-seite-loesung.visustruct -::: - -## Game: constant or variable? - -::: spiel frage -titel: Game: constant or variable? -intro: Ludwig is sorting values. If the value stays the same in the whole program, it is a constant. If it changes, it is a variable. Six rounds. -frage: Constant or variable? -antworten: Constant | Variable - -runde: -frage: The number of corners of a square -richtig: Constant -> A square always has four corners. - -runde: -frage: The distance the ultrasonic sensor is measuring right now -richtig: Variable -> When the mBot2 drives, the distance changes all the time. - -runde: -frage: The minimum distance at which the mBot2 should always stop -richtig: Constant -> You set it once, for example \`final int DISTANCE = 20;\`. - -runde: -frage: The number of steps the mBot2 has already driven -richtig: Variable -> It grows by 1 after every step. - -runde: -frage: The pitch of the warning tone, always 440 Hz -richtig: Constant -> The warning tone always sounds the same. - -runde: -frage: The battery level in percent -richtig: Variable -> The battery slowly runs down while driving. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what a constant is. → Creating a constant -- I can create a constant in the structure chart and in Java. → Creating a constant -- I can use a constant in a condition and in a command. → Now in Java: square with a constant -::: - -## Quiz - -::: quiz -? What is a constant? -→ Creating a constant -- [ ] A variable that changes in every pass -- [x] A variable whose value stays the same once it is set -- [ ] A sensor that always measures the same value -> You set a constant once. After that you only use its name. -? How do you create the constant \`DISTANCE\` with the value 20 in Java? -→ Creating a constant -- [ ] \`const DISTANCE = 20;\` -- [x] \`final int DISTANCE = 20;\` -- [ ] \`int final = 20;\` -> In Java, \`final\` comes before the data type. In the structure chart you write \`const\`. -? Why is a constant better than the number 20 in many places? -→ Creating a constant -- [ ] Because the program drives faster -- [x] Because you only have to change the value in one place -- [ ] Because the sensor measures more precisely -::: -`,gz=`--- -titel: Near, medium or far? -bereich: Sensors and decisions -kurz: Build a branch with three cases and write it in Java with else if. ---- - -## Skills I will gain now - -::: kompetenzen -- build a branch with three cases -- read and write \`else if\` in Java -- check the conditions in the right order -::: - -## Three cases instead of two - -Many cars have a **parking aid**. If the wall is far away, everything is free. When it gets closer, it says: slow. When it is very close: stop! - -A branch has only two branches, yes and no. For three cases you put a second branch into the no branch of the first one: - -1. Is the distance less than 10? Then **Stop**. -2. Otherwise: is the distance less than 30? Then **Slow**. -3. Otherwise: **Free**. - -::: diagramm vb-l06n-einparkhilfe.visustruct -The parking aid. The second question is only asked if the answer to the first one was no. -::: - -Try it in the [simulation](#zeige-simulation): open the diagram in the editor and set the distance to 5, 20 and 50 cm. - -> 📘 **More without the robot:** VisuStruct has the lesson ["Branch"](?visustruct&lektion=08-verzweigung). It opens in a new window. - -## Speed by distance in the structure chart - -::: übung vb-tempo-abstand -titel: Speed by distance -auftrag: Build a structure chart with three cases: if the distance is less than 10, the mBot2 drives 5 cm backward. Otherwise: if the distance is less than 30, it drives 5 cm forward. Otherwise it drives 20 cm forward. -messwerte: getDistance=5 -> backward 5 -messwerte: getDistance=20 -> forward 5 -messwerte: getDistance=50 -> forward 20 -braucht: verzweigung -tipp: Build it like the parking aid: a branch with \`mbot.getDistance() < 10\` and in its no branch a second one with \`mbot.getDistance() < 30\`. -tipp: The commands are \`mbot.backward(5)\`, \`mbot.forward(5)\` and \`mbot.forward(20)\`. The last one goes into the no branch of the second branch. -lösung: vb-tempo-abstand-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance to 5, 20 and 50 cm one after the other. - -## Now in Java: else if - -Look at the [Code](#zeige-codelive) area. The branch inside the no branch looks like this: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} else { - if (mbot.getDistance() < 30) { - mbot.showText("Slow"); - } else { - mbot.showText("Free"); - } -} -\`\`\` - -In Java you may write this more briefly. The \`if\` moves right behind the \`else\`: - -\`\`\`java -if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} else if (mbot.getDistance() < 30) { - mbot.showText("Slow"); -} else { - mbot.showText("Free"); -} -\`\`\` - -Both mean exactly the same. With \`else if\` it is easier to read, though. You still always write the curly brackets \`{ }\`. - -::: übung vb-java-tageszeit -titel: Day or night? -code: java -auftrag: Write in Java with \`else if\`: if the brightness is less than 20, the mBot2 shows **Night**. Otherwise: if it is less than 60, it shows **Dusk**. Otherwise it shows **Day**. -messwerte: getBrightness=10 -> none, screen "Night" -messwerte: getBrightness=40 -> none, screen "Dusk" -messwerte: getBrightness=80 -> none, screen "Day" -braucht: verzweigung -tipp: You read the brightness with \`mbot.getBrightness()\`. The first condition is \`mbot.getBrightness() < 20\`. -tipp: \`if (…) { mbot.showText("Night"); } else if (mbot.getBrightness() < 60) { mbot.showText("Dusk"); } else { mbot.showText("Day"); }\` -lösung: vb-java-tageszeit-loesung.visustruct -::: - -::: übung vb-fehler-06-nah-mittel-weit -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Day or night?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-06-nah-mittel-weit-start.visustruct -messwerte: getBrightness=10 -> none, screen "Night" -messwerte: getBrightness=40 -> none, screen "Dusk" -messwerte: getBrightness=80 -> none, screen "Day" -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be \`mbot.showText("Night")\`. -lösung: vb-java-tageszeit-loesung.visustruct -::: - -## The order matters - -Ludwig rebuilt the parking aid and swapped the questions: - -\`\`\`java -if (mbot.getDistance() < 30) { - mbot.showText("Slow"); -} else if (mbot.getDistance() < 10) { - mbot.showText("Stop"); -} -\`\`\` - -At 5 cm his mBot2 shows **Slow** instead of **Stop**. That is because 5 is also less than 30. The first condition is already true, so the second one is never checked. **Stop** never appears. - -> Remember: with \`<\`, check the smallest value first, then the next bigger one. - -::: spiel frage -titel: Game: what does the parking aid show? -intro: Ludwig's parking aid from above: under 10 cm **Stop**, under 30 cm **Slow**, otherwise **Free**. What does it show? Six rounds. -frage: What does the parking aid show? -antworten: Stop | Slow | Free - -runde: -frage: The distance is **5 cm**. -richtig: Stop -> 5 is less than 10. The very first question is true. - -runde: -frage: The distance is **25 cm**. -richtig: Slow -> 25 is not less than 10, but it is less than 30. - -runde: -frage: The distance is **80 cm**. -richtig: Free -> Both questions are false, so the last no branch is left. - -runde: -frage: The distance is exactly **10 cm**. -richtig: Slow -> 10 is not less than 10. The first question is false, the second true. - -runde: -frage: The distance is exactly **30 cm**. -richtig: Free -> 30 is not less than 30. Both questions are false. - -runde: -frage: The distance is **9 cm**. -richtig: Stop -> 9 is less than 10. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a branch with three cases. → Three cases instead of two -- I can read and write \`else if\` in Java. → Now in Java: else if -- I can check the conditions in the right order. → The order matters -::: - -## Quiz - -::: quiz -? How many branches do you need in the structure chart for three cases? -→ Three cases instead of two -- [ ] One -- [x] Two, the second in the no branch of the first -- [ ] Three side by side -> Each branch splits into two. The second one splits the no branch again. -? What does \`else if\` mean in Java? -→ Now in Java: else if -- [x] Otherwise, check another condition -- [ ] Repeat the condition -- [ ] Check both conditions at the same time -> \`else if\` is the short way of writing an \`if\` inside the \`else\` branch. -? Ludwig checks \`< 30\` first and then \`< 10\`. What happens at 5 cm? -→ The order matters -- [ ] Stop appears. -- [x] Slow appears. -- [ ] Nothing appears. -> 5 is also less than 30. The second condition is no longer checked. -::: -`,pz=`--- -titel: The line sensor -bereich: Sensors and decisions -kurz: Detect dark lines on the floor and don't fall off the table. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the number from \`mbot.getLine()\` means -- drive up to a line -- count steps with a variable in Java -::: - -## Four probes under the nose - -::: mbot2 -focus: lineSensor -view: bottom -::: - -The [line sensor]{part=lineSensor} has four probes side by side. Each one shines light on the floor and measures how much comes back. Black swallows the light, white reflects it. - -| Sensor | Text | Value | -|---|---|---| -| Line | \`mbot.getLine()\` | \`0\` = all bright, greater than \`0\` = at least one probe sees black | - -To start with, this is all you need: **\`mbot.getLine() == 0\` means “no line”**, \`mbot.getLine() != 0\` means “line found”. - -::: angeberwissen -\`mbot.getLine()\` is really a binary number with four digits, one for each probe: \`1111\` (that is 15) means “all four see black”, \`0110\` (6) “only the middle two”. With that an mBot2 can even follow a line. -::: - -## Turn round at the edge in the structure chart - -Imagine a table with black tape along the edge. The mBot2 should drive forward until it sees the tape and then turn round so it doesn't fall off. - -::: übung vb-rand -titel: Turn round at the edge -auftrag: The mBot2 drives forward as long as \`mbot.getLine() == 0\`. Then it turns 180°. There are two worlds with different distances. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Line** onto it. Write \`== 0\` after it. **move forward** goes inside the loop. -tipp: After the loop (below it, not inside) is \`mbot.turnRight(180)\`. -lösung: vb-rand-loesung.visustruct -::: - -## Now in Java: there and back - -Now the mBot2 should drive exactly back to the start after turning round. To do that it counts with a **variable** how often it drove forward. - -\`\`\`java -int steps = 0; -while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; -} -\`\`\` - -::: übung vb-java-rand -titel: To the line and back -code: java -auftrag: Write in Java: the mBot2 drives to the line and counts its steps. Then it turns 180° and uses a for loop to drive the same number of steps back. -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -braucht: whileSchleife, forSchleife -tipp: After the while loop: \`mbot.turnRight(180);\` and then \`for (int i = 0; i < steps; i++) { … }\`. -tipp: The for loop only contains \`mbot.forward();\`. -lösung: vb-java-rand-loesung.visustruct -::: - -::: übung vb-fehler-07-linie -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To the line and back", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-07-linie-start.visustruct -welt: rand-1 -> forward 20, turnRight 180, forward 20 -welt: rand-2 -> forward 50, turnRight 180, forward 50 -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`steps = steps + 1\`. -lösung: vb-java-rand-loesung.visustruct -::: - -## Game: Line or no line? - -::: spiel frage -titel: Game: Line or no line? -intro: Where is the mBot2 standing, and is the condition true or false? Six rounds. -antworten: true | false - -runde: - mbot.getLine() == 0 -frage: The mBot2 is standing on white paper. Is the condition true? -richtig: true -> No sensor sees black, so \`mbot.getLine()\` gives 0. - -runde: - mbot.getLine() == 0 -frage: The two middle sensors are on the black tape. Is the condition true? -richtig: false -> Two sensors see black, so \`mbot.getLine()\` is greater than 0. - -runde: - mbot.getLine() != 0 -frage: Only the outer left sensor sees black. Is the condition true? -richtig: true -> One sensor is enough, then the value is no longer 0. - -runde: - mbot.getLine() != 0 -frage: The mBot2 is standing on a white floor. Is the condition true? -richtig: false -> On white the value is 0, and \`0 != 0\` is false. - -runde: - mbot.getLine() == 15 -frage: A cross line: all four sensors see black. Is the condition true? -richtig: true -> All four sensors give the binary number \`1111\`, which is 15. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; - } -frage: The line is 30 cm away, each drive is 10 cm long. Is \`steps == 3\` afterwards? -richtig: true -> After three drives it is on the line, and the loop stops. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getLine() != 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Should drive to the black edge and then turn round. -fehler: 1 -besser: while (mbot.getLine() == 0) { -> It should drive as long as there is **no** line, so \`== 0\`. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(); -frage: Should drive to the black edge and then turn round. -fehler: 4 -besser: mbot.turnRight(180); -> Turning round means 180°. Without a number it only turns 90°. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps - 1; - } -frage: Should drive to the line and count the steps. -fehler: 4 -besser: steps = steps + 1; -> Counting means adding one, not taking one away. - -runde: - int steps = "0"; - while (mbot.getLine() == 0) { - mbot.forward(); - steps = steps + 1; - } -frage: Should drive to the line and count the steps. -fehler: 1 -besser: int steps = 0; -> In quotation marks, the 0 is a text. Only a number fits into an \`int\`. -::: - -## Game: Where does the mBot2 end up? - -::: spiel fahrt -titel: Game: Where does the mBot2 end up? -intro: Black is tape. Tap the field where the mBot2 is at the end. Each field is 30 cm. Four rounds. -frage: On which field is the mBot2 at the end? - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ..=.. | ..... | ..... | ..^.. -ziel: 2,1 -> It drives until it is standing on the black field. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(30); - } - mbot.turnRight(180); - mbot.forward(60); -karte: ..=.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> To the edge, turn round and two fields back. - -runde: - int steps = 0; - while (mbot.getLine() == 0) { - mbot.forward(30); - steps = steps + 1; - } - mbot.turnRight(180); - for (int i = 0; i < steps; i++) { - mbot.forward(30); - } -karte: ..=.. | ..... | ..... | ..^.. -ziel: 2,3 -> It counts three steps to the line and drives exactly three back: at the start again. - -runde: - mbot.turnRight(); - while (mbot.getLine() == 0) { - mbot.forward(30); - } -karte: ..... | ^...= | ..... -ziel: 4,1 -> First turn right, then on to the black field on the far right. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the number from \`mbot.getLine()\` means. → Four probes under the nose -- I can drive up to a line. → Turn round at the edge in the structure chart -- I can count steps with a variable in Java. → Now in Java: there and back -::: - -## Quiz - -::: quiz -? The mBot2 is on white paper. What does \`mbot.getLine()\` give? -→ Four probes under the nose -- [x] 0 -- [ ] 15 -- [ ] 1 -> No probe sees black, so 0. -? Which condition means “line found”? -→ Turn round at the edge in the structure chart -- [ ] \`mbot.getLine() == 0\` -- [x] \`mbot.getLine() != 0\` -- [ ] \`mbot.getLine() < 0\` -> \`!=\` means “not equal”. -::: -`,fz=`--- -titel: And, or, not -bereich: Sensors and decisions -kurz: Check two sensors in one condition: and, or, not. ---- - -## Skills I will gain now - -::: kompetenzen -- combine two conditions with \`&&\` (and) -- combine two conditions with \`||\` (or) -- turn a condition around with \`!\` (not) -- write a combined condition in a Java loop -::: - -## Wall and line: and - -The mBot2 should only drive forward if the way ahead is clear **and** there is no line under it. Two sensors, one condition. In Java, **and** is written \`&&\`: - -\`\`\`java -mbot.getDistance() > 15 && mbot.getLine() == 0 -\`\`\` - -The whole condition is only true if **both** parts are true: - -| clear ahead? | no line? | clear \`&&\` no line | -|---|---|---| -| true | true | **true** | -| true | false | false | -| false | true | false | -| false | false | false | - -A table like this is called a **truth table**. - -::: übung vb-und -titel: Clear and no line -auftrag: Build a structogram with a branch: if the distance is greater than 15 **and** \`mbot.getLine() == 0\`, the mBot2 drives forward. Otherwise it does nothing. -messwerte: getDistance=50, getLine=0 -> forward -messwerte: getDistance=10, getLine=0 -> none -messwerte: getDistance=50, getLine=6 -> none -braucht: verzweigung -tipp: Drag the **distance** sensor onto the branch and write \`> 15\` after it. Then type \` && \` and the second part \`mbot.getLine() == 0\`. -tipp: The true branch contains **drive forward**, the false branch stays empty. -lösung: vb-und-loesung.visustruct -::: - -> When checking, VisuStruct sets the distance and the line to different values. Only if both fit may the mBot2 drive. - -## Button A or button B: or - -Now the mBot2 should beep when you press button A **or** button B. It is enough if one of them is pressed. In Java, **or** is written \`||\`. - -| Button A? | Button B? | A or B | -|---|---|---| -| true | true | **true** | -| true | false | **true** | -| false | true | **true** | -| false | false | false | - -::: übung vb-oder -titel: Button A or B -auftrag: Build a structogram: if button A **or** button B is pressed, the mBot2 plays a tone. Otherwise it does nothing. -messwerte: isButtonA=true, isButtonB=false -> tone -messwerte: isButtonA=false, isButtonB=true -> tone -messwerte: isButtonA=false, isButtonB=false -> noTone -braucht: verzweigung -tipp: The condition of the branch is \`mbot.isButtonA() || mbot.isButtonB()\`. -tipp: The true branch contains \`mbot.playTone(440, 300)\`, the false branch stays empty. -lösung: vb-oder-loesung.visustruct -::: - -## Not: the opposite - -Sometimes you want the opposite of a condition. That is what \`!\` is for, read as **not**. \`mbot.isButtonA()\` is true when button A is pressed. \`!mbot.isButtonA()\` is true when it is **not** pressed. - -\`\`\`java -while (!mbot.isButtonA()) { - mbot.pause(100); -} -mbot.forward(30); -\`\`\` - -The mBot2 waits as long as button A is not pressed. Then it drives off. - -## Now in Java: drive safely - -Now everything comes together. The mBot2 drives as long as the way is clear **and** no line comes. Whatever comes first, the wall or the edge: it stops and beeps. - -::: übung vb-java-sicher-fahren -titel: Drive safely -code: java -auftrag: Write in Java: the mBot2 drives forward as long as the distance is greater than 15 **and** \`mbot.getLine() == 0\`. Then it plays a tone. In two worlds a wall is waiting, in two worlds a black edge. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -braucht: whileSchleife -tipp: \`while (mbot.getDistance() > 15 && mbot.getLine() == 0) { … }\` -tipp: Inside the loop is \`mbot.forward();\`, after the loop \`mbot.playTone(440, 300);\`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Combining conditions"](?visustruct&lektion=09-bedingungen-verknuepfen). It opens in a new window. - -::: übung vb-fehler-07-und-oder -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Drive safely", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-07-und-oder-start.visustruct -welt: wand-kurz -> tone -welt: wand-mittel -> tone -welt: rand-1 -> forward 20, tone -welt: rand-2 -> forward 50, tone -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.playTone(440, 300)\`. -lösung: vb-java-sicher-fahren-loesung.visustruct -::: - -## Game: True or false? - -::: spiel frage -titel: Game: True or false? -intro: Ludwig puts the mBot2 into a situation. Is the condition true or false? Eight rounds. -antworten: true | false - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: The wall is 50 cm away, there is no line underneath. -richtig: true -> Both parts are true, so the whole thing is true. - -runde: - mbot.getDistance() > 15 && mbot.getLine() == 0 -frage: The wall is 10 cm away, there is no line underneath. -richtig: false -> With \`&&\`, both parts must be true. The distance part isn't. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: Only button B is pressed. -richtig: true -> With \`||\`, one true part is enough. - -runde: - mbot.isButtonA() || mbot.isButtonB() -frage: No button is pressed. -richtig: false -> Both parts are false, so \`||\` is false too. - -runde: - !mbot.isButtonA() -frage: Button A is pressed. -richtig: false -> \`mbot.isButtonA()\` is true, and the \`!\` turns it into false. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: The wall is 10 cm away, and it is standing on a line. -richtig: false -> No part is true, so \`||\` is false too. - -runde: - mbot.getDistance() > 15 || mbot.getLine() == 0 -frage: The wall is 50 cm away, and it is standing on a line. -richtig: true -> The first part is true, and that is enough for \`||\`. - -runde: - !mbot.isButtonA() && mbot.getDistance() > 15 -frage: Nobody is pressing a button, the wall is 30 cm away. -richtig: true -> A isn't pressed, so \`!mbot.isButtonA()\` is true. The distance part is true as well. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getDistance() > 15 || mbot.getLine() == 0) { - mbot.forward(); - } - mbot.playTone(440, 300); -frage: Should drive as long as the way ahead is clear **and** no line comes. -fehler: 1 -besser: while (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> With \`||\` it keeps going if only one part is true, for example over the edge. - -runde: - if (mbot.isButtonA() && mbot.isButtonB()) { - mbot.playTone(440, 300); - } -frage: Should beep when button A **or** button B is pressed. -fehler: 1 -besser: if (mbot.isButtonA() || mbot.isButtonB()) { -> "Or" is \`||\` in Java. With \`&&\` it only beeps when both are pressed. - -runde: - while (mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Should wait until button A is pressed and then set off. -fehler: 1 -besser: while (!mbot.isButtonA()) { -> Waiting means: loop as long as A is **not** pressed. The \`!\` is missing. - -runde: - if (mbot.getDistance() > 15 & mbot.getLine() == 0) { - mbot.forward(); - } -frage: Should drive forward if the way ahead is clear and there is no line. -fehler: 1 -besser: if (mbot.getDistance() > 15 && mbot.getLine() == 0) { -> "And" is written with two characters: \`&&\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can combine two conditions with \`&&\` (and). → Wall and line: and -- I can combine two conditions with \`||\` (or). → Button A or button B: or -- I can turn a condition around with \`!\` (not). → Not: the opposite -- I can write a combined condition in a Java loop. → Now in Java: drive safely -::: - -## Quiz - -::: quiz -? The distance is 50 and \`mbot.getLine()\` gives 6. Does the mBot2 drive in "Clear and no line"? -→ Wall and line: and -- [ ] Yes, the way ahead is clear. -- [x] No, because with \`&&\` both parts must be true. -- [ ] Only half as far. -> It is standing on a line. So one part is false, and so is the whole condition. -? Only button B is pressed. Is \`mbot.isButtonA() || mbot.isButtonB()\` true? -→ Button A or button B: or -- [x] Yes -- [ ] No -> With \`||\` it is enough if one part is true. -? Button A is pressed. What is \`!mbot.isButtonA()\`? -→ Not: the opposite -- [ ] true -- [x] false -> \`!\` turns the value around: true becomes false. -? Which condition stops the mBot2 at the wall **and** at the edge? -→ Now in Java: drive safely -- [x] \`while (mbot.getDistance() > 15 && mbot.getLine() == 0)\` -- [ ] \`while (mbot.getDistance() > 15 || mbot.getLine() == 0)\` -- [ ] \`while (mbot.getDistance() > 15)\` -> With \`||\` it would keep driving as long as just one part is true, for example over the edge. -::: -`,bz=`--- -titel: Recognising colours -bereich: Sensors and decisions -kurz: The quad RGB sensor tells you which colour the mBot2 is standing on. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for a colour with \`mbot.getColor().equals(…)\` -- turn a condition round with \`!\` -- put a branch inside a loop -::: - -## A sensor that sees colours - -::: mbot2 -focus: lineSensor -view: bottom -::: - -The [line sensor]{part=lineSensor} is really called a **quad RGB sensor**. “Quad” means four (four probes), “RGB” stands for red, green and blue. It shines these three colours on the floor and measures which colour comes back. That is how it recognises coloured fields. - -\`mbot.getColor()\` gives the name of the colour under the mBot2, as **text**: - -| Floor | \`mbot.getColor()\` | -|---|---| -| red field | \`"red"\` | -| green field | \`"green"\` | -| blue field | \`"blue"\` | -| yellow field | \`"yellow"\` | -| black field or line | \`"black"\` | -| light floor | \`"white"\` | - -You ask whether it is a certain colour with \`.equals(…)\`: \`mbot.getColor().equals("red")\` is **true** when the mBot2 stands on red, otherwise **false**. So it is already a complete condition. - -> In Java you always compare texts with \`.equals(…)\`, **not** with \`==\`. The \`==\` looks right but often doesn't work with texts. - -> With a \`!\` in front you turn a condition round: \`!mbot.getColor().equals("red")\` means “**not** red”. - -## Drive to red in the structure chart - -::: übung vb-bis-rot -titel: Drive to red -auftrag: The mBot2 drives forward as long as it is **not** on a red field. In the second world there is a green field first, which should not bother it. -welt: farbe-1 -> forward 20 -welt: farbe-2 -> forward 50 -braucht: whileSchleife -tipp: Drag a while loop into the structure chart and the sensor **Colour is red?** onto it. Write a \`!\` in front of \`mbot.getColor().equals("red")\`. -tipp: The condition is \`!mbot.getColor().equals("red")\`, inside the loop is **move forward**. -lösung: vb-bis-rot-loesung.visustruct -::: - -## Now in Java: beep on green - -Now you combine a loop with a branch: the mBot2 drives to blue. If it passes over a green field on the way, it beeps. - -::: übung vb-java-blau-gruen -titel: To blue, beep on green -code: java -auftrag: Write in Java: the mBot2 drives forward as long as it is not on blue. After each step it checks: if it is on green, it plays a tone. In the second world there is no green field, so it must not beep there. -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -braucht: whileSchleife, verzweigung -tipp: Outside is \`while (!mbot.getColor().equals("blue")) { … }\`. Inside, first \`mbot.forward();\`. -tipp: After that, still inside the loop: \`if (mbot.getColor().equals("green")) { mbot.playTone(880, 200); }\`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -::: übung vb-fehler-08-farben -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "To blue, beep on green", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-08-farben-start.visustruct -welt: farbe-3 -> forward 30, tone -welt: farbe-4 -> forward 30, noTone -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be \`equals("green")\`. -lösung: vb-java-blau-gruen-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should do and tap the wrong line before time runs out. Five rounds, 30 seconds each. -zeit: 30 - -runde: - while (mbot.getColor().equals("red")) { - mbot.forward(); - } -frage: Should drive forward until it is standing on red. -fehler: 1 -besser: while (!mbot.getColor().equals("red")) { -> It should drive as long as it is **not** on red. The \`!\` is missing. - -runde: - if (mbot.getColor() == "green") { - mbot.playTone(440, 300); - } -frage: Should beep when it is standing on green. -fehler: 1 -besser: if (mbot.getColor().equals("green")) { -> You compare texts with \`.equals(…)\`, not with \`==\`. - -runde: - while (!mbot.getColor().equals("blue")) { - mbot.forward(); - if (mbot.getColor().equals("green")) { - mbot.turnRight(); - } - } -frage: Should drive to blue and beep on green. -fehler: 4 -besser: mbot.playTone(440, 300); -> On green it should beep, not turn. - -runde: - if (mbot.getColor().equals(red)) { - mbot.turnRight(); - } -frage: Should turn right on red. -fehler: 1 -besser: if (mbot.getColor().equals("red")) { -> Without quotation marks, Java looks for a variable called \`red\`. The colour name is a text. - -runde: - while (!mbot.getColor().equals("red")) { - mbot.forward(); - } - mbot.playTone(440, 300) -frage: Should drive to red and then beep. -fehler: 4 -besser: mbot.playTone(440, 300); -> The semicolon at the end of the statement is missing. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for a colour with \`mbot.getColor().equals(…)\`. → A sensor that sees colours -- I can turn a condition round with \`!\`. → Drive to red in the structure chart -- I can put a branch inside a loop. → Now in Java: beep on green -::: - -## Quiz - -::: quiz -? The mBot2 is on a red field. What is \`!mbot.getColor().equals("red")\`? -→ Drive to red in the structure chart -- [ ] true -- [x] false -> \`mbot.getColor().equals("red")\` is true, the \`!\` turns it into false. So the loop stops. -? How do you compare the colour with \`"green"\`? -→ A sensor that sees colours -- [x] \`mbot.getColor().equals("green")\` -- [ ] \`mbot.getColor() == "green"\` -- [ ] \`mbot.getColor() = "green"\` -> \`mbot.getColor()\` gives a text, and texts are compared with \`.equals(…)\`. A single \`=\` is an assignment, not a comparison at all. -::: -`,xz=`--- -titel: The biggest value -bereich: Sensors and decisions -kurz: Measure all around and remember the biggest value: where is the most space? ---- - -## Skills I will gain now - -::: kompetenzen -- remember the biggest value so far in a variable -- compare every new reading with the biggest so far in a loop -- find out with the mBot2 in which direction there is the most space -::: - -## Remembering the record - -In the long jump there is a board showing the **record**. After every jump the referee checks: is the new jump longer than the record? Then it goes on the board. Otherwise the old record stays. - -A program finds the biggest value in exactly the same way. The board is a variable \`max\`: - -1. **Before** the loop: \`int max = 0\`. Nothing is on the board yet. -2. **Inside** the loop: is the new value bigger than \`max\`? Then \`max = value\`. -3. **After** the loop, \`max\` holds the biggest value. - -The mBot2 turns four times and measures the distance each time. This is how it looks in a desk check: - -| Direction | measured | bigger than \`max\`? | \`max\` afterwards | -|---|---|---|---| -| before | | | 0 | -| ahead | 16 | yes | 16 | -| right | 36 | yes | 36 | -| behind | 6 | no | 36 | -| left | 26 | no | 36 | - -> Important: the branch only has a yes branch. If the value is not bigger, nothing happens. The old record stays. - -## Where is the most space? - -::: übung vb-meister-platz -titel: Where is the most space? -auftrag: Build a structure chart: create \`int max = 0\`. The mBot2 measures the distance four times and turns right after each one. If a distance is bigger than \`max\`, it remembers it in \`max\`. At the end it shows, for example, **Farthest: 36 cm**. There are two worlds. -welt: platz-1 -> screen "Farthest: 36 cm" -welt: platz-2 -> screen "Farthest: 46 cm" -braucht: forSchleife, verzweigung -tipp: The for loop runs four times. It contains three boxes: \`int distance = mbot.getDistance()\`, a branch and **turn right**. -tipp: The branch checks \`distance > max\`. The yes branch has \`max = distance\`, the no branch stays empty. After the loop: \`mbot.showText("Farthest: " + max + " cm")\`. -lösung: vb-meister-platz-loesung.visustruct -::: - -::: angeberwissen -Many robot vacuums have a small tower with a laser on top. It spins and measures the distances all around. That way the robot draws a map of the home. Nobody measures anything at my place. I just eat my way through. -::: - -## Now in Java: remembering the direction - -Now the mBot2 knows **how much** space there is at most. But **where**? For that it also remembers the direction in a second variable. The counter variable \`i\` tells you which measurement it was: 0 is ahead, 1 is right, 2 is behind and 3 is left. - -\`\`\`java -if (distance > max) { - max = distance; - direction = i; -} -\`\`\` - -Both lines are in the yes branch. The direction only changes when there is a new record. - -::: übung vb-java-richtung -titel: Remembering the direction -code: java -auftrag: Write "Where is the most space?" in Java. Also remember in \`int direction = 0\` the number of the measurement with the biggest distance. At the end show, for example, **Direction: 1**. -welt: platz-1 -> screen "Direction: 1" -welt: platz-2 -> screen "Direction: 0" -braucht: forSchleife, verzweigung -tipp: Before the loop are \`int max = 0;\` and \`int direction = 0;\`. Inside the loop: \`int distance = mbot.getDistance();\`, the branch and \`mbot.turnRight();\`. -tipp: The yes branch has \`max = distance;\` and \`direction = i;\`. After the loop: \`mbot.showText("Direction: " + direction);\` -lösung: vb-java-richtung-loesung.visustruct -::: - -::: übung vb-fehler-08-groesster-wert -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Remembering the direction", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-08-groesster-wert-start.visustruct -welt: platz-1 -> screen "Direction: 1" -welt: platz-2 -> screen "Direction: 0" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`direction = i\`. -lösung: vb-java-richtung-loesung.visustruct -::: - -## Game: new record? - -::: spiel frage -titel: Game: new record? -intro: The mBot2 measures several distances one after the other. What is in \`max\` at the end? Five rounds. - -runde: -frage: Measured: 20, 50, 30 -- 30 -- [x] 50 -- 100 -> 50 is the biggest value. The 30 after it is not a new record. - -runde: -frage: Measured: 10, 20, 30, 40 -- 10 -- [x] 40 -- 100 -> Every value is a new record. At the end the last one is there. - -runde: -frage: Measured: 90, 10, 20, 30 -- 30 -- [x] 90 -- 10 -> The record comes right at the start. After that no value is bigger. - -runde: -frage: Measured: 15, 15, 15 -- 45 -- 0 -- [x] 15 -> 15 is not bigger than 15. \`max\` stays 15. - -runde: -frage: Measured: 25, 60, 60, 5. In which direction (\`i\`) is the record? -- 0 -- [x] 1 -- 2 -> At \`i = 1\` the 60 comes. The second 60 is not **bigger**, so \`direction\` stays at 1. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can remember the biggest value so far in a variable. → Remembering the record -- I can compare every new reading with the biggest so far in a loop. → Where is the most space? -- I can find out with the mBot2 in which direction there is the most space. → Now in Java: remembering the direction -::: - -## Quiz - -::: quiz -? What does \`max\` start with before the loop? -→ Remembering the record -- [x] With 0, because nothing has been measured yet -- [ ] With the biggest value -- [ ] With 100 -> Every distance is bigger than 0, so the very first reading becomes the record. -? What is in the no branch of the branch \`distance > max\`? -→ Remembering the record -- [ ] \`max = 0\` -- [ ] \`max = distance\` -- [x] Nothing, the old record stays. -? When does \`direction\` change? -→ Now in Java: remembering the direction -- [ ] In every pass -- [x] Only when there is a new record -- [ ] Only in the last round -::: -`,vz=`--- -titel: Case selection with colours -bereich: Sensors and decisions -kurz: Do something different for each colour or line, with one case selection instead of many branches. ---- - -## Skills I will gain now - -::: kompetenzen -- build a case selection with texts -- write a case selection with numbers in Java -- show a different text on the screen depending on a number -::: - -## Many colours, many cases - -The mBot2 should react differently to each colour: turn right on red, drive forward a bit on green, beep on blue. With branches you would need three inside each other. A **case selection** is clearer, in Java \`switch\`: - -\`\`\`java -switch (mbot.getColor()) { - case "red" -> { - mbot.turnRight(); - } - case "green" -> { - mbot.forward(20); - } - default -> { - mbot.forward(10); - } -} -\`\`\` - -- The value being checked goes in the brackets at the top, here the colour. -- Each \`case\` is one **case**. If the value matches, exactly this case runs. -- \`default\` is the case for **everything else**. - -In the structogram the case selection is a box with several columns, one for each case. - -::: übung vb-farbweiche -titel: Colour switch -auftrag: Build a case selection on \`mbot.getColor()\`. For \`"red"\` the mBot2 turns right. For \`"green"\` it drives forward 20 cm. For \`"blue"\` it plays a tone of 880 hertz for 200 milliseconds. In all other cases it drives forward 10 cm. -messwerte: getColor=red -> turnRight -messwerte: getColor=green -> forward 20 -messwerte: getColor=blue -> none, tone -messwerte: getColor=white -> forward 10 -braucht: fallauswahl -tipp: Drag a case selection into the structogram. At the top goes \`mbot.getColor()\`, the cases are \`"red"\`, \`"green"\` and \`"blue"\`, plus the case for everything else. -tipp: For red \`mbot.turnRight()\`, for green \`mbot.forward(20)\`, for blue \`mbot.playTone(880, 200)\`, otherwise \`mbot.forward(10)\`. -lösung: vb-farbweiche-loesung.visustruct -::: - -## Now in Java: line patterns - -A number can also be the value of a case selection. \`mbot.getLine()\` from lesson 7 gives for example: - -| Value | Meaning | -|---|---| -| \`0\` | no line | -| \`6\` | the two middle sensors see the line: it is right on it | -| \`15\` | all four see black: a cross line | - -For numbers you write the case without quotation marks: \`case 6 -> { … }\`. - -::: übung vb-linienmuster -titel: Line patterns -code: java -auftrag: Write a case selection on \`mbot.getLine()\` in Java. For 6 the mBot2 drives forward 10 cm. For 0 it turns right. For 15 it shows **Cross line!** on its screen. In all other cases it drives forward 5 cm. -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Cross line!" -messwerte: getLine=4 -> forward 5 -braucht: fallauswahl -tipp: \`switch (mbot.getLine()) { case 6 -> { … } case 0 -> { … } case 15 -> { … } default -> { … } }\` -tipp: For 6 \`mbot.forward(10);\`, for 0 \`mbot.turnRight();\`, for 15 \`mbot.showText("Cross line!");\`, otherwise \`mbot.forward(5);\`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Now in Java: start signal - -Before a race the mBot2 shows three texts one after the other and then drives off. A for loop counts the rounds 1, 2 and 3. The case selection finds the right text for each round: - -\`\`\`java -for (int round = 1; round <= 3; round++) { - switch (round) { - case 1 -> { mbot.showText("On your marks"); } - ... - } - mbot.pause(1000); -} -\`\`\` - -::: übung vb-startsignal -titel: Start signal -code: java -auftrag: Write in Java: a loop counts \`round\` from 1 to 3. In round 1 the screen shows **On your marks**, in round 2 **Get set**, otherwise **Go!**. After each text the mBot2 waits one second. After the loop it drives 50 cm forward. -befehle: showText "On your marks", pause 1000, showText "Get set", pause 1000, showText "Go!", pause 1000 -fahrt: forward 50 -braucht: fallauswahl -tipp: The loop is \`for (int round = 1; round <= 3; round++) { … }\`. Inside it is \`switch (round) { … }\` and after that \`mbot.pause(1000);\`. -tipp: \`case 1 -> { mbot.showText("On your marks"); }\`, \`case 2 -> { … }\` and \`default -> { mbot.showText("Go!"); }\`. After the loop \`mbot.forward(50);\`. -lösung: vb-startsignal-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Switch"](?visustruct&lektion=10-fallauswahl). It opens in a new window. - -::: übung vb-fehler-09-fallauswahl-mbot2 -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Line patterns", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-09-fallauswahl-mbot2-start.visustruct -messwerte: getLine=6 -> forward 10 -messwerte: getLine=0 -> turnRight -messwerte: getLine=15 -> none, screen "Cross line!" -messwerte: getLine=4 -> forward 5 -tipp: Read the error message carefully. Is the command spelled correctly? After \`mbot.\` the editor suggests all commands. -tipp: It should be \`mbot.turnRight()\`. -lösung: vb-linienmuster-loesung.visustruct -::: - -## Game: Which case runs? - -::: spiel frage -titel: Game: Which case runs? -intro: Read the switch and predict what the mBot2 does. Six rounds. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on green. What does it do? -- It turns right. -- [x] It drives forward 20 cm. -- It drives forward 10 cm. -> The case \`"green"\` matches, so exactly that one runs. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on yellow. What does it do? -- Nothing at all. -- It beeps. -- [x] It drives forward 10 cm. -> There is no case for yellow, so \`default\` runs. - -runde: - switch (mbot.getColor()) { - case "red" -> { mbot.turnRight(); } - case "green" -> { mbot.forward(20); } - case "blue" -> { mbot.playTone(880, 300); } - default -> { mbot.forward(10); } - } -frage: The mBot2 is standing on blue. What does it do? -- [x] It beeps. -- It beeps and then drives forward 10 cm. -- It turns right. -> Only the matching case runs. \`default\` only runs when no other case matches. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Cross line!"); } - default -> { mbot.stop(); } - } -frage: All four sensors see black. What does the mBot2 do? -- It drives forward 10 cm. -- [x] It shows **Cross line!** -- It stops. -> Four sensors on black give 15. - -runde: - switch (mbot.getLine()) { - case 6 -> { mbot.forward(10); } - case 0 -> { mbot.turnRight(); } - case 15 -> { mbot.showText("Cross line!"); } - default -> { mbot.stop(); } - } -frage: Everything under the mBot2 is white. What does it do? -- It drives forward 10 cm. -- [x] It turns right. -- It stops. -> No sensor sees black, so \`mbot.getLine()\` gives 0. - -runde: - for (int round = 1; round <= 3; round++) { - switch (round) { - case 1 -> { mbot.showText("On your marks"); } - case 2 -> { mbot.showText("Get set"); } - default -> { mbot.showText("Go!"); } - } - } -frage: What does the screen show in round 3? -- Get set -- [x] Go! -- On your marks -> There is no case of its own for 3, so \`default\` runs. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build a case selection with texts. → Many colours, many cases -- I can write a case selection with numbers in Java. → Now in Java: line patterns -- I can show a different text on the screen depending on a number. → Now in Java: start signal -::: - -## Quiz - -::: quiz -? The mBot2 is on yellow. Which case of the colour switch runs? -→ Many colours, many cases -- [ ] the case \`"red"\` -- [ ] none at all -- [x] \`default\` -> There is no case of its own for yellow. So the case for everything else runs. -? How do you write the case for the number 6? -→ Now in Java: line patterns -- [x] \`case 6 -> { … }\` -- [ ] \`case "6" -> { … }\` -- [ ] \`if 6 -> { … }\` -> Numbers have no quotation marks, texts like \`"red"\` do. -::: -`,wz=`--- -titel: Buttons and joystick -bereich: Sensors and decisions -kurz: The mBot2 waits for you and can be remote controlled. ---- - -## Skills I will gain now - -::: kompetenzen -- ask the sensors for buttons and joystick -- wait for a button -- steer the mBot2 with the joystick -::: - -## Pressing as a sensor - -::: mbot2 -focus: joystick, buttons -view: top -::: - -On the CyberPi there are two [buttons]{part=buttons} (A and B) and a [joystick]{part=joystick}. For your program these are sensors: they give **true** as long as you press them. - -| Sensor | Text | -|---|---| -| Button A / B pressed? | \`mbot.isButtonA()\`, \`mbot.isButtonB()\` | -| Joystick up / down | \`mbot.isJoystickUp()\`, \`mbot.isJoystickDown()\` | -| Joystick left / right | \`mbot.isJoystickLeft()\`, \`mbot.isJoystickRight()\` | - -> Without a real mBot2, open **🎛 Try the sensors** below the world in the [simulation](#zeige-simulation). There you find the buttons A, B and the arrows for the joystick. A click keeps them pressed for one second. - -## Waiting for button A in the structure chart - -A robot should not drive off as soon as the program starts. Better: it waits until you press A. - -Waiting means: a loop that runs as long as A is **not** pressed. So that it is not completely empty, the mBot2 waits briefly inside with **wait a moment** (\`mbot.pause(100)\`, that is 100 milliseconds). - -::: übung vb-start-a -titel: Start with button A -auftrag: The mBot2 waits until button A is pressed. Then it drives forward 30 cm. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: A while loop with the condition \`!mbot.isButtonA()\`. Inside is **wait a moment**. -tipp: After the loop is \`mbot.forward(30)\`. -lösung: vb-start-a-loesung.visustruct -::: - -> When checking, button A is not pressed for a while and then pressed. If your mBot2 drives off too early, it shows. - -## Now in Java: remote control - -With the joystick you steer the mBot2. The program asks the joystick five times in a row: up means drive forward, down means drive back. - -::: übung vb-java-joystick -titel: Remote control -code: java -auftrag: Write a for loop in Java that runs five times. Inside: if the joystick is up, the mBot2 drives forward. Otherwise: if it is down, it drives back. -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -braucht: forSchleife, verzweigung -tipp: \`for (int i = 0; i < 5; i++) { … }\` and inside \`if (mbot.isJoystickUp()) { … } else { … }\`. -tipp: The \`else\` contains another branch: \`if (mbot.isJoystickDown()) { mbot.backward(); }\`. -lösung: vb-java-joystick-loesung.visustruct -::: - -::: übung vb-fehler-10-tasten-joystick -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Remote control", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-10-tasten-joystick-start.visustruct -messwerte: isJoystickUp=true -> forward 50 -messwerte: isJoystickDown=true -> backward 50 -messwerte: isJoystickUp=false -> none -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.backward()\`. -lösung: vb-java-joystick-loesung.visustruct -::: - -## Game: What does the mBot2 do? - -::: spiel frage -titel: Game: What does the mBot2 do? -intro: You press a button or the joystick. What does the mBot2 do? Six rounds. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: Nobody presses a button. What does the mBot2 do? -- It drives forward 30 cm. -- [x] It keeps waiting. -- It beeps. -> As long as A isn't pressed, it stays in the waiting loop. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: You press button B. What does the mBot2 do? -- It drives forward 30 cm. -- [x] It keeps waiting. -- It drives back 30 cm. -> The loop only asks about button A. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(30); -frage: You press button A. What does the mBot2 do? -- [x] It drives forward 30 cm. -- It keeps waiting. -- It waits 100 ms and then stops. -> Now \`!mbot.isButtonA()\` is false, the loop ends, and it sets off. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: You push the joystick down. What does the mBot2 do? -- It drives forward. -- [x] It drives back. -- Nothing at all. -> The first condition is false, the second is true. - -runde: - if (mbot.isJoystickUp()) { - mbot.forward(); - } else if (mbot.isJoystickDown()) { - mbot.backward(); - } -frage: You push the joystick to the left. What does the mBot2 do? -- It turns left. -- It drives back. -- [x] Nothing at all. -> There is no branch for left. Both conditions are false. - -runde: -frage: You hold button A down. What does \`mbot.isButtonA()\` give? -- [x] true -- false -- 1 -> Buttons give true or false, and pressed means true. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask the sensors for buttons and joystick. → Pressing as a sensor -- I can wait for a button. → Waiting for button A in the structure chart -- I can steer the mBot2 with the joystick. → Now in Java: remote control -::: - -## Quiz - -::: quiz -? What does \`mbot.isButtonB()\` give when nobody presses it? -→ Pressing as a sensor -- [ ] 0 -- [x] false -- [ ] B -> Buttons give true or false. -? Why is there **wait a moment** in the waiting loop? -→ Waiting for button A in the structure chart -- [x] So the loop is not empty and the CyberPi doesn't race round in circles for nothing. -- [ ] So the button glows longer. -- [ ] Without a pause the mBot2 drives backwards. -> The loop checks every 100 milliseconds, that is plenty. -::: -`,yz=`--- -titel: Emergency brake with break -bereich: Sensors and decisions -kurz: An endless loop with a Break: the mBot2 keeps driving until the emergency brake kicks in. ---- - -## Skills I will gain now - -::: kompetenzen -- end an endless loop with a Break -- explain why the Break sits inside a branch -- write a loop with \`while (true)\` and \`break\` in Java -::: - -## Endless loop with a Break - -Many robot programs run "forever": the mBot2 drives, measures, drives, measures … That is what the **endless loop** is for. It has no condition and never stops by itself. - -So that the mBot2 can still stop, you need an **emergency brake**: the **Break**. It leaves the loop it is in straight away. The program carries on with the first box **after** the loop. You recognise it by the point on its left edge. In the box you write a short comment, for example \`leave the loop\`. - -A Break almost always sits inside a **branch**. Otherwise the loop would end in the very first pass. - -::: übung vb-notbremse -titel: Emergency brake at the wall -auftrag: Build an endless loop. Inside it a branch checks: if the distance is less than 15 cm, there is a Break. After that (below the branch, still inside the loop) the mBot2 drives forward. After the loop it plays a tone. -welt: wand-kurz -> tone -welt: wand-mittel -> tone -braucht: endlosschleife, aussprung -tipp: Drag the **endless loop** into the structogram. First a branch with \`mbot.getDistance() < 15\` goes into the loop, the **Break** into its true branch, the false branch stays empty. -tipp: Below the branch (inside the loop) goes **drive forward**, below the loop \`mbot.playTone(440, 300)\`. -lösung: vb-notbremse-loesung.visustruct -::: - -## Now in Java: stop with button A - -In Java the endless loop is \`while (true)\`. The Break is called \`break\`: - -\`\`\`java -while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); -} -\`\`\` - -::: übung vb-java-taste-stopp -titel: Stop with button A -code: java -auftrag: Write in Java: the mBot2 keeps driving forward in an endless loop. When button A is pressed, it leaves the loop with \`break\`. Then the screen shows **Stopped**. -messwerte: isButtonA=true -> none, screen "Stopped" -messwerte: isButtonA=false/true -> drives, screen "Stopped" -braucht: endlosschleife, aussprung -tipp: \`while (true) { … }\` with \`if (mbot.isButtonA()) { break; }\` and then \`mbot.forward();\`. -tipp: After the loop \`mbot.showText("Stopped");\`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Call and break"](?visustruct&lektion=18-aufruf-und-aussprung). It opens in a new window. - -::: übung vb-fehler-10-unterbrechen-mit-break -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Stop with button A", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-10-unterbrechen-mit-break-start.visustruct -messwerte: isButtonA=true -> none, screen "Stopped" -messwerte: isButtonA=false/true -> drives, screen "Stopped" -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be \`mbot.showText("Stopped")\`. -lösung: vb-java-taste-stopp-loesung.visustruct -::: - -## Game: Emergency brake - -::: spiel frage -titel: Game: Emergency brake -intro: When does the break kick in, and what happens afterwards? Each drive is 10 cm long. Five rounds. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: The wall starts 40 cm away. How often does it drive forward? -- 2 times -- [x] 3 times -- forever -> At 40, 30 and 20 it drives. At 10 the emergency brake kicks in. - -runde: - while (true) { - if (mbot.getDistance() < 15) { - break; - } - mbot.forward(); - } - mbot.playTone(440, 300); -frage: What happens straight after \`break\`? -- The loop starts again. -- It drives forward once more. -- [x] It beeps. -> \`break\` leaves the loop. The program carries on with the first command after it. - -runde: - while (true) { - mbot.forward(); - break; - } -frage: How often does the mBot2 drive forward? -- [x] once -- not at all -- forever -> The break isn't inside a branch. The loop ends in its first run. - -runde: - while (true) { - mbot.forward(); - } -frage: When does this loop stop? -- after 10 drives -- at the wall -- [x] never -> Without \`break\`, \`while (true)\` really is endless. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: You press button A after it has driven forward twice. How often has it driven forward? -- [x] 2 times -- 3 times -- not at all -> In the next run it notices the button and jumps out of the loop. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - while (true) { - if (mbot.getDistance() > 15) { - break; - } - mbot.forward(); - } -frage: Should drive forward and stop at the wall with the emergency brake. -fehler: 2 -besser: if (mbot.getDistance() < 15) { -> The emergency brake should kick in when the wall is **close**. - -runde: - while (true) { - if (mbot.isButtonB()) { - break; - } - mbot.forward(); - } - mbot.showText("Stopped"); -frage: Should drive forward until button A is pressed, then show "Stopped". -fehler: 2 -besser: if (mbot.isButtonA()) { -> The brake should be button A, not B. - -runde: - while (true) { - if (mbot.isButtonA()) { - brake; - } - mbot.forward(); - } -frage: Should drive forward until button A is pressed. -fehler: 3 -besser: break; -> The exit is called \`break\`, not \`brake\`. - -runde: - while (false) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } -frage: Should drive in an endless loop until button A is pressed. -fehler: 1 -besser: while (true) { -> The endless loop is \`while (true)\`. With \`false\` it doesn't run even once. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can end an endless loop with a Break. → Endless loop with a Break -- I can explain why the Break sits inside a branch. → Endless loop with a Break -- I can write a loop with \`while (true)\` and \`break\` in Java. → Now in Java: stop with button A -::: - -## Quiz - -::: quiz -? Where does the program carry on after a Break? -→ Endless loop with a Break -- [ ] at the start of the loop -- [x] at the first box after the loop -- [ ] at the very start of the program -> The Break leaves the loop straight away. -? Why does the Break sit inside a branch? -→ Endless loop with a Break -- [x] Otherwise the loop ends in the first pass. -- [ ] Otherwise the mBot2 drives backwards. -- [ ] It just looks nicer. -> The emergency brake should only kick in when the condition is true. -? What is the endless loop called in Java? -→ Now in Java: stop with button A -- [ ] \`while (false)\` -- [x] \`while (true)\` -- [ ] \`for (;;) break;\` -> \`true\` is always true, the loop only stops with \`break\`. -::: -`,zz=`--- -titel: The light sensor -bereich: Sensors and decisions -kurz: Bright or dark? The mBot2 becomes a night light. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the brightness -- switch the LEDs with a branch -- write several commands in one branch -::: - -## How bright is it? - -::: mbot2 -focus: lightSensor -view: top -::: - -The [light sensor]{part=lightSensor} is on the CyberPi. \`mbot.getBrightness()\` gives a number from **0** (pitch dark) to **100** (very bright). - -| Sensor | Text | Value | -|---|---|---| -| Brightness | \`mbot.getBrightness()\` | 0 to 100 | - -> In the simulation you set the brightness with the slider under **🎛 Try the sensors**. With a real mBot2 you simply hold your hand over the CyberPi. - -## Night light in the structure chart - -::: übung vb-nachtlicht -titel: Night light -auftrag: If the brightness is less than 30, the LEDs glow white (\`mbot.setLed(255, 255, 255)\`). Otherwise the LEDs go off (\`mbot.ledOff()\`). -messwerte: getBrightness=10 -> ledOn -messwerte: getBrightness=80 -> ledOff -braucht: verzweigung -tipp: Drag a branch into the structure chart and the sensor **Brightness** onto it. Write \`< 30\` after it. -tipp: The true branch gets **LEDs on** with \`mbot.setLed(255, 255, 255)\`, the false branch **LEDs off**. -lösung: vb-nachtlicht-loesung.visustruct -::: - -::: angeberwissen -Street lights do the same: a light sensor notices when it gets dark and switches them on. That is why they come on earlier in winter than in summer, without anyone changing a clock. -::: - -## Now in Java: night light with sound - -::: übung vb-java-nachtlicht-ton -titel: Night light with sound -code: java -auftrag: Write the night light in Java, but with two changes: in the dark the LEDs glow yellow (\`mbot.setLed(255, 200, 0)\`) **and** the mBot2 plays a tone. In the light the LEDs go off, without a tone. -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -braucht: verzweigung -tipp: \`if (mbot.getBrightness() < 30) { … } else { … }\` -tipp: The \`if\` part has two lines: \`mbot.setLed(255, 200, 0);\` and \`mbot.playTone(440, 300);\`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -::: übung vb-fehler-11-licht -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Night light with sound", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-11-licht-start.visustruct -messwerte: getBrightness=10 -> ledOn, tone -messwerte: getBrightness=80 -> ledOff, noTone -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be \`mbot.setLed(255, 200, 0)\`. -lösung: vb-java-nachtlicht-ton-loesung.visustruct -::: - -## Game: Lights on or off? - -::: spiel frage -titel: Game: Lights on or off? -intro: How bright is it, and what do the LEDs do? Six rounds. -antworten: LEDs on | LEDs off - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: It is almost dark, the brightness is 10. -richtig: LEDs on -> 10 is less than 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is 45. -richtig: LEDs off -> 45 is not less than 30. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is exactly 30. -richtig: LEDs off -> 30 is not less than 30. For "up to and including 30" you would need \`<=\`. - -runde: - if (mbot.getBrightness() < 30) { - mbot.setLed(255, 255, 255); - } else { - mbot.ledOff(); - } -frage: The brightness is 29. -richtig: LEDs on -> 29 is just less than 30. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: The brightness is 50. -richtig: LEDs on -> Careful, it's the other way round here: 50 is not greater than 70, so the false branch runs. The LEDs light up yellow. - -runde: - if (mbot.getBrightness() > 70) { - mbot.ledOff(); - } else { - mbot.setLed(255, 200, 0); - } -frage: The sun is shining, the brightness is 100. -richtig: LEDs off -> 100 is greater than 70, so the true branch runs. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the brightness. → How bright is it? -- I can switch the LEDs with a branch. → Night light in the structure chart -- I can write several commands in one branch. → Now in Java: night light with sound -::: - -## Quiz - -::: quiz -? \`mbot.getBrightness()\` gives 45. What does the night light do? -→ Night light in the structure chart -- [ ] The LEDs glow. -- [x] The LEDs go off. -> 45 is not less than 30. -? What range do the light sensor's values have? -→ How bright is it? -- [ ] 0 to 255 -- [x] 0 to 100 -- [ ] -100 to 100 -::: -`,Iz=`--- -titel: A flag with boolean -bereich: Sensors and decisions -kurz: Use a variable of type boolean to remember whether something happened. ---- - -## Skills I will gain now - -::: kompetenzen -- create a variable of type \`boolean\` -- use a flag to remember whether something happened -- check a flag in a condition after the loop -::: - -## True or false - -A variable of type \`boolean\` knows only two values: \`true\` and \`false\`. You already know this from the sensors: \`mbot.isButtonA()\` gives you \`true\` or \`false\`. A comparison like \`mbot.getDistance() < 10\` is also \`true\` or \`false\` in the end. - -| Code | Meaning | -|---|---| -| \`boolean dark = false\` | It has not been dark yet. | -| \`dark = true\` | Now it has been dark. | -| \`if (dark)\` | Is \`dark\` true? | - -With \`if (dark)\` you don't need \`== true\`. The variable itself is already true or false. - -## The flag - -The mBot2 should keep watch for three seconds. It measures the brightness again and again. Afterwards it should say whether it was dark **at any point**. - -For this you use a **flag**: a \`boolean\` variable that starts with \`false\`. When it gets dark, you set it to \`true\`. After that nobody sets it back. So after the loop you know: was it dark even just once? - -::: übung vb-wurde-es-dunkel -titel: Did it get dark? -auftrag: Build a structure chart: create \`boolean dark = false\`. In a for loop, check 30 times: if the brightness is less than 20, \`dark\` becomes true. Wait 100 ms in every pass. Then show **It was dark** or **Always bright**. -messwerte: getBrightness=80/10/80 -> none, screen "It was dark" -messwerte: getBrightness=80 -> none, screen "Always bright" -braucht: forSchleife, verzweigung -tipp: Inside the loop is a branch with \`mbot.getBrightness() < 20\`. The yes branch has \`dark = true\`, the no branch stays empty. Then comes \`mbot.pause(100)\`. -tipp: After the loop comes a second branch with the condition \`dark\`. Yes: \`mbot.showText("It was dark")\`, no: \`mbot.showText("Always bright")\`. -lösung: vb-wurde-es-dunkel-loesung.visustruct -::: - -> When checking, it gets dark for a moment in the middle of the watch and then bright again. Does your mBot2 still show **It was dark**? - -## Now in Java: was there a clap? - -In Java the flag looks like this: - -\`\`\`java -boolean loud = false; -for (int i = 0; i < 30; i++) { - if (mbot.getLoudness() > 60) { - loud = true; - } - mbot.pause(100); -} -\`\`\` - -In Java you always write \`true\` and \`false\` in lower case. - -::: übung vb-java-klatsch-merker -titel: Was there a clap? -code: java -auftrag: Write in Java: the mBot2 listens for three seconds (30 times with a 100 ms pause). If the loudness is ever greater than 60, it remembers this in \`boolean loud\`. Then it shows **Clap!** or **All quiet**. -messwerte: getLoudness=0/90/0 -> none, screen "Clap!" -messwerte: getLoudness=0 -> none, screen "All quiet" -braucht: forSchleife, verzweigung -tipp: Take the code above and then write \`if (loud) { … } else { … }\`. -tipp: The branches get \`mbot.showText("Clap!");\` and \`mbot.showText("All quiet");\`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -::: übung vb-fehler-11-merker-boolean -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Was there a clap?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-11-merker-boolean-start.visustruct -messwerte: getLoudness=0/90/0 -> none, screen "Clap!" -messwerte: getLoudness=0 -> none, screen "All quiet" -tipp: The error message names an unknown variable. How do you write true and false in Java? Java is case-sensitive. -tipp: It should be \`loud = true\`. -lösung: vb-java-klatsch-merker-loesung.visustruct -::: - -## Game: true or false? - -::: spiel frage -titel: Game: true or false? -intro: Ludwig asks what the condition comes out as. Six rounds. -frage: Is it true or false? -antworten: true | false - -runde: -frage: \`mbot.getDistance() < 10\`, the distance is 25 cm. -richtig: false -> 25 is not less than 10. - -runde: -frage: \`mbot.isButtonA()\`, and you are pressing button A right now. -richtig: true -> The sensor reports: pressed. - -runde: -frage: \`boolean dark = false\`. It never gets dark in the loop. What is \`dark\` afterwards? -richtig: false -> Nobody set the flag to \`true\`. - -runde: -frage: \`boolean dark = false\`. It gets dark once in the loop, then bright again. What is \`dark\` afterwards? -richtig: true -> A flag stays \`true\` once it has been set. - -runde: -frage: \`mbot.getBrightness() > 50\`, the brightness is 50. -richtig: false -> 50 is not greater than 50. - -runde: -frage: \`!mbot.isButtonA()\`, and you are not pressing button A. -richtig: true -> Button A is not pressed, so \`false\`. The \`!\` turns it around: \`true\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create a variable of type \`boolean\`. → True or false -- I can use a flag to remember whether something happened. → The flag -- I can check a flag in a condition after the loop. → Now in Java: was there a clap? -::: - -## Quiz - -::: quiz -? Which values can a variable of type \`boolean\` have? -→ True or false -- [ ] Any whole number -- [x] Only \`true\` and \`false\` -- [ ] Only 0 and 1 -? What does a flag start with? -→ The flag -- [x] With \`false\`, because nothing has happened yet -- [ ] With \`true\` -- [ ] With the first reading -> Only when the event happens does it become \`true\`. -? How do you check in Java whether the flag \`loud\` is true? -→ Now in Java: was there a clap? -- [x] \`if (loud)\` -- [ ] \`if (loud = true)\` -- [ ] \`if loud\` -> \`loud\` itself is already true or false. A single \`=\` would be an assignment. -::: -`,kz=`--- -titel: The microphone -bereich: Sensors and decisions -kurz: Clap to start, and a noise light for the classroom. ---- - -## Skills I will gain now - -::: kompetenzen -- ask for the loudness -- wait until something happens -- write a noise light in Java -::: - -## How loud is it? - -::: mbot2 -focus: microphone -view: top -::: - -The CyberPi's [microphone]{part=microphone} measures the loudness. \`mbot.getLoudness()\` gives **0** (silent) to **100** (very loud). A clap nearby gives about 60 to 90. - -| Sensor | Text | Value | -|---|---|---| -| Loudness | \`mbot.getLoudness()\` | 0 to 100 | - -## Clap to start in the structure chart - -Just like waiting for button A: a loop waits as long as it is quiet. Only then does the mBot2 drive off. - -::: übung vb-klatschen -titel: Clap to start -auftrag: The mBot2 waits as long as the loudness is less than 50. Then it drives forward 30 cm. -messwerte: getLoudness=0/80 -> forward 30 -messwerte: getLoudness=90 -> forward 30 -braucht: whileSchleife -tipp: A while loop with the condition \`mbot.getLoudness() < 50\`, with **wait a moment** inside. -tipp: After the loop: \`mbot.forward(30)\`. -lösung: vb-klatschen-loesung.visustruct -::: - -> In the simulation you push the **Loudness** slider to the right while the program is waiting. - -## Now in Java: noise light - -::: übung vb-java-laermampel -titel: Noise light -code: java -auftrag: Write in Java: if the loudness is greater than 60, the LEDs glow red. Otherwise they are off. -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -braucht: verzweigung -tipp: \`if (mbot.getLoudness() > 60) { … } else { … }\` -tipp: Red is \`mbot.setLed(255, 0, 0);\`, off is \`mbot.ledOff();\`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -::: angeberwissen -Loudness is really measured in **decibels** (dB). Whispering is about 30 dB, a classroom 60 to 70 dB, a pneumatic drill 100 dB. The CyberPi turns that into a simple number from 0 to 100. -::: - -::: übung vb-fehler-12-lautstaerke -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Noise light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-12-lautstaerke-start.visustruct -messwerte: getLoudness=80 -> ledOn -messwerte: getLoudness=20 -> ledOff -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.ledOff()\`. -lösung: vb-java-laermampel-loesung.visustruct -::: - -## Game: Loud or quiet? - -::: spiel frage -titel: Game: Loud or quiet? -intro: How loud is it, and what does the mBot2 do? Six rounds. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: The class is quiet, the loudness is 30. -- [x] It keeps waiting. -- It drives forward 30 cm. -> 30 is less than 50, the waiting loop keeps running. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: You clap, the loudness is 70. -- It keeps waiting. -- [x] It drives forward 30 cm. -> 70 is not less than 50. The loop ends and it sets off. - -runde: - while (mbot.getLoudness() < 50) { - mbot.pause(100); - } - mbot.forward(30); -frage: The loudness is exactly 50. -- It keeps waiting. -- [x] It drives forward 30 cm. -> 50 is not less than 50. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: The loudness is 65. -- [x] The LEDs light up red. -- The LEDs are off. -> 65 is greater than 60: too loud. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: The loudness is exactly 60. -- The LEDs light up red. -- [x] The LEDs are off. -> 60 is not greater than 60. - -runde: - if (mbot.getLoudness() > 60) { - mbot.setLed(255, 0, 0); - } else { - mbot.ledOff(); - } -frage: Someone whispers, the loudness is 10. -- The LEDs light up red. -- [x] The LEDs are off. -> Whispering is quiet. The noise light stays off. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can ask for the loudness. → How loud is it? -- I can wait until something happens. → Clap to start in the structure chart -- I can write a noise light in Java. → Now in Java: noise light -::: - -## Quiz - -::: quiz -? The clap loop is \`while (mbot.getLoudness() < 50)\`. The loudness is 30. What happens? -→ Clap to start in the structure chart -- [x] The mBot2 keeps waiting. -- [ ] The mBot2 drives off. -> 30 is less than 50, the condition is true, the loop keeps running. -::: -`,Sz=`--- -titel: The motion sensor -bereich: Sensors and decisions -kurz: Measure tilt and detect shaking. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the motion sensor measures -- join two conditions with “or” -- join several conditions with “and” -::: - -## Tilted or level? - -::: mbot2 -focus: cyberpi -spin: yes -::: - -Inside the [CyberPi]{part=cyberpi} there is a **motion sensor** (gyroscope). It measures how tilted the mBot2 is, in degrees: - -| Sensor | Text | Value | -|---|---|---| -| Tilt forward/back | \`mbot.getPitch()\` | nose down: positive, nose up: negative (−90 to 90) | -| Tilt sideways | \`mbot.getRoll()\` | positive to one side, negative to the other | -| Shaken? | \`mbot.isShaken()\` | \`true\` or \`false\` | - -When the mBot2 is lying flat on the table, both tilts are about **0**. - -> In the simulation there are two sliders for this under **🎛 Try the sensors** and the button **shake**. With a real mBot2 you tilt it carefully in your hand. - -## Tilt warning in the structure chart - -If the mBot2 drives up or down a ramp that is too steep, it should warn you. Too steep means: more than 20° forward **or** more than 20° back. - -::: übung vb-schraeglage -titel: Tilt warning -auftrag: If the forward/back tilt is greater than 20 **or** less than −20, the mBot2 plays a tone. Otherwise it does nothing. -messwerte: getPitch=30 -> tone -messwerte: getPitch=-30 -> tone -messwerte: getPitch=5 -> noTone -braucht: verzweigung -tipp: “or” in a condition is \`||\`. Drag the sensor **Tilt forward/back** onto the branch twice. -tipp: The condition is \`mbot.getPitch() > 20 || mbot.getPitch() < -20\`, the true branch gets **play a tone**. -lösung: vb-schraeglage-loesung.visustruct -::: - -## Now in Java: spirit level - -A spirit level shows whether something is exactly level. The mBot2 glows green when both tilts are between −5 and 5. - -::: übung vb-java-wasserwaage -titel: Spirit level -code: java -auftrag: Write in Java: if \`mbot.getPitch()\` and \`mbot.getRoll()\` are both between −5 and 5, the LEDs glow green. Otherwise they are off. -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -braucht: verzweigung -tipp: “and” is \`&&\`. You need four comparisons: \`mbot.getPitch() > -5 && mbot.getPitch() < 5 && …\` -tipp: Green is \`mbot.setLed(0, 255, 0);\`, the \`else\` has \`mbot.ledOff();\`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -::: übung vb-fehler-13-neigung -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Spirit level", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-13-neigung-start.visustruct -messwerte: getPitch=0, getRoll=0 -> ledOn -messwerte: getPitch=0, getRoll=20 -> ledOff -messwerte: getPitch=-10, getRoll=0 -> ledOff -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.setLed(0, 255, 0)\`. -lösung: vb-java-wasserwaage-loesung.visustruct -::: - -## Game: Too steep? - -::: spiel frage -titel: Game: Too steep? -intro: Ludwig tilts the mBot2. What does it do? Six rounds. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: The nose points 30° down, \`mbot.getPitch()\` gives 30. -- [x] It beeps. -- It doesn't beep. -> 30 is greater than 20. One true part is enough for \`||\`. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: The nose points up, \`mbot.getPitch()\` gives −25. -- [x] It beeps. -- It doesn't beep. -> −25 is less than −20, so the second part is true. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: A gentle ramp, \`mbot.getPitch()\` gives 10. -- It beeps. -- [x] It doesn't beep. -> 10 is between −20 and 20. Both parts are false. - -runde: - if (mbot.getPitch() > 20 || mbot.getPitch() < -20) { - mbot.playTone(440, 300); - } -frage: \`mbot.getPitch()\` gives exactly 20. -- It beeps. -- [x] It doesn't beep. -> 20 is not greater than 20. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: \`mbot.getPitch()\` gives 0, \`mbot.getRoll()\` gives 3. -- [x] The LEDs light up green. -- The LEDs are off. -> All four parts are true. The mBot2 is almost level. - -runde: - if (mbot.getPitch() > -5 && mbot.getPitch() < 5 && mbot.getRoll() > -5 && mbot.getRoll() < 5) { - mbot.setLed(0, 255, 0); - } else { - mbot.ledOff(); - } -frage: \`mbot.getPitch()\` gives 2, \`mbot.getRoll()\` gives −8. -- The LEDs light up green. -- [x] The LEDs are off. -> −8 is not greater than −5. With \`&&\`, one false part makes everything false. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 6 2 CONDITION: is true or false, e.g. mbot.getDistance() < 20 -- ↓ 4 4 SENSOR: a part that measures something -- ↓ 3 10 BRANCH: does one thing or the other, depending on a condition -- ↓ 4 6 BRIGHTNESS: mbot.getBrightness() measures it -- → 11 4 BREAK: emergency brake: leaves the loop straight away -- → 13 1 ULTRASOUND: how the mBot2 measures distance: a very high sound and its echo -- ↓ 1 8 JOYSTICK: small lever on the CyberPi -- ↓ 2 2 SWITCH: one case of its own for each value -lösung: 7 4, 11 6, 10 6, 4 8, 6 9, 9 4 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the motion sensor measures. → Tilted or level? -- I can join two conditions with “or”. → Tilt warning in the structure chart -- I can join several conditions with “and”. → Now in Java: spirit level -::: - -## Quiz - -::: quiz -? The mBot2 is lying flat on the table. What does \`mbot.getPitch()\` give, roughly? -→ Tilted or level? -- [x] 0 -- [ ] 90 -- [ ] 100 -? What does \`mbot.isShaken()\` do? -→ Tilted or level? -- [ ] It shakes the mBot2. -- [x] It reports whether someone is shaking the mBot2. -> Sensors don't do anything, they only measure. -::: -`,Tz=`--- -titel: The LEDs -bereich: Light, sound and screen -kurz: Mix colours, build a traffic light and make it blink. ---- - -## Skills I will gain now - -::: kompetenzen -- mix a colour from red, green and blue -- show colours one after the other with pauses -- make the LEDs blink in Java -::: - -## Mixing red, green and blue - -::: mbot2 -focus: leds -view: back -::: - -The five [LEDs]{part=leds} are an **actuator**: they do something, they light up. Each LED mixes its colour from red, green and blue, each from 0 (off) to 255 (fully bright). - -| Command | Box | What happens | -|---|---|---| -| \`mbot.setLed(255, 0, 0)\` | **LEDs on** | all LEDs red | -| \`mbot.ledOff()\` | **LEDs off** | all LEDs off | -| \`mbot.pause(1000)\` | **wait a moment** | wait 1000 milliseconds (1 second) | - -| Colour | Red | Green | Blue | -|---|---|---|---| -| Red | 255 | 0 | 0 | -| Yellow | 255 | 200 | 0 | -| Green | 0 | 255 | 0 | -| Blue | 0 | 0 | 255 | -| White | 255 | 255 | 255 | -| Purple | 160 | 0 | 255 | - -> Without **wait a moment** the mBot2 switches the colours so fast that you only see the last one. In the simulation you see the colour on the LEDs of the 3D model. - -## Traffic light in the structure chart - -::: übung vb-ampel -titel: Traffic light -auftrag: The LEDs show red, yellow and green one after the other like a traffic light. Between the colours the mBot2 waits 1000 milliseconds each time. Yellow is \`255, 200, 0\`. -befehle: setLed 255 0 0, pause 1000, setLed 255 200 0, pause 1000, setLed 0 255 0 -tipp: Drag **LEDs on** three times and **wait a moment** twice into the structure chart, taking turns. -tipp: Change the numbers: \`mbot.setLed(255, 0, 0)\`, \`mbot.pause(1000)\`, \`mbot.setLed(255, 200, 0)\`, \`mbot.pause(1000)\`, \`mbot.setLed(0, 255, 0)\`. -lösung: vb-ampel-loesung.visustruct -::: - -## Now in Java: blue light - -Blinking means: on, wait, off, wait, again and again. “Again and again” is a loop. - -::: übung vb-java-blinken -titel: Blue light -code: java -auftrag: Write a for loop in Java that runs three times. Inside, the LEDs go blue, the mBot2 waits 500 milliseconds, the LEDs go off, and it waits 500 milliseconds again. -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: \`for (int i = 0; i < 3; i++) { … }\` -tipp: Inside the loop: \`mbot.setLed(0, 0, 255);\`, \`mbot.pause(500);\`, \`mbot.ledOff();\`, \`mbot.pause(500);\`. -lösung: vb-java-blinken-loesung.visustruct -::: - -::: übung vb-fehler-14-leds -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Blue light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-14-leds-start.visustruct -befehle: 3 x (setLed 0 0 255, pause 500, ledOff, pause 500) -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.pause(500)\`. -lösung: vb-java-blinken-loesung.visustruct -::: - -## Game: Which colour? - -::: spiel frage -titel: Game: Which colour? -intro: Red, green and blue mixed together: which colour do the LEDs show? Seven rounds. - -runde: - mbot.setLed(255, 0, 0); -frage: Which colour lights up? -- [x] red -- green -- blue -> Only the first value, red, is on. - -runde: - mbot.setLed(0, 0, 255); -frage: Which colour lights up? -- red -- green -- [x] blue -> The order is always red, green, blue. Only blue is on. - -runde: - mbot.setLed(255, 255, 255); -frage: Which colour lights up? -- yellow -- [x] white -- off -> All three colours at full brightness make white. - -runde: - mbot.setLed(255, 200, 0); -frage: Which colour lights up? -- [x] yellow -- purple -- white -> Red and green together make yellow. - -runde: - mbot.setLed(160, 0, 255); -frage: Which colour lights up? -- yellow -- [x] purple -- green -> Red and blue together make purple. - -runde: - mbot.setLed(0, 0, 0); -frage: Which colour lights up? -- white -- black -- [x] none, the LEDs are off -> All three values are 0. An LED can't shine black. - -runde: - mbot.setLed(255, 0, 0); - mbot.setLed(0, 255, 0); - mbot.pause(1000); -frage: Which colour do you see for one second? -- red -- [x] green -- first red, then green -> Without **wait a moment** between the colours, red is gone immediately. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.setLed(255, 0, 0); - mbot.pause(1000); - mbot.setLed(255, 200, 0); - mbot.pause(1000); - mbot.setLed(0, 0, 255); -frage: Should show red, yellow and green like a traffic light. -fehler: 5 -besser: mbot.setLed(0, 255, 0); -> Green is \`0, 255, 0\`. \`0, 0, 255\` is blue. - -runde: - for (int i = 0; i < 3; i++) { - mbot.setLed(0, 0, 255); - mbot.pause(500); - mbot.ledOff; - mbot.pause(500); - } -frage: Should flash blue three times. -fehler: 4 -besser: mbot.ledOff(); -> A command without a number still needs its brackets. - -runde: - mbot.setLed(0, 0, 300); - mbot.pause(1000); - mbot.ledOff(); -frage: Should shine bright blue for one second. -fehler: 1 -besser: mbot.setLed(0, 0, 255); -> Each colour only goes from 0 to 255. - -runde: - mbot.setLed(255, 255, 255); - mbot.pause(1000); - mbot.setLed(0, 0, 0, 0); -frage: Should shine white for one second and then go off. -fehler: 3 -besser: mbot.ledOff(); -> \`setLed\` has exactly three numbers: red, green, blue. To switch off there is \`mbot.ledOff()\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can mix a colour from red, green and blue. → Mixing red, green and blue -- I can show colours one after the other with pauses. → Traffic light in the structure chart -- I can make the LEDs blink in Java. → Now in Java: blue light -::: - -## Quiz - -::: quiz -? Which colour does \`mbot.setLed(0, 255, 0)\` give? -→ Mixing red, green and blue -- [ ] Red -- [x] Green -- [ ] Blue -> The numbers are red, green, blue. Only green is on. -? Why does the blue light need **wait a moment**? -→ Now in Java: blue light -- [x] Otherwise it changes so fast that you can't see any blinking. -- [ ] Otherwise the LEDs break. -- [ ] Because \`ledOff()\` doesn't work otherwise. -::: -`,_z=`--- -titel: Single LEDs -bereich: Light, sound and screen -kurz: Each of the five LEDs gets its own colour, all the way to a running light. ---- - -## Skills I will gain now - -::: kompetenzen -- address the LEDs by their numbers 0 to 4 -- make single LEDs light up in different colours -- build a running light with a loop -::: - -## Every LED has a number - -::: mbot2 -focus: leds -view: back -::: - -So far all five [LEDs]{part=leds} showed the same colour. But you can also address each LED on its own. For that, every LED has a **number**. As with arrays, counting starts at **0**: the five LEDs are called 0, 1, 2, 3 and 4. - -| Command | Box | What happens | -|---|---|---| -| \`mbot.led(0).setColor("green")\` | **set LED colour** | LED 0 lights up green | -| \`mbot.led(3).setColor("off")\` | **set LED colour** | LED 3 goes off | - -You write the colour as a name in quotes: - -| Colour name | Colour name | Colour name | -|---|---|---| -| \`"red"\` | \`"orange"\` | \`"yellow"\` | -| \`"green"\` | \`"cyan"\` | \`"blue"\` | -| \`"purple"\` | \`"pink"\` | \`"white"\` | - -With \`"off"\` you switch an LED off. - -> The number can also be a variable: \`mbot.led(i).setColor("red")\`. That way a loop can switch on one LED after the other. - -## Three lights in the structogram - -::: übung vb-drei-lichter -titel: Three lights -auftrag: Switch LED 0 red, LED 2 yellow and LED 4 green. LEDs 1 and 3 stay off. -befehle: ledColor 0 "red", ledColor 2 "yellow", ledColor 4 "green" -tipp: Drag **set LED colour** into the structogram three times. -tipp: Change number and colour: \`mbot.led(0).setColor("red")\`, \`mbot.led(2).setColor("yellow")\`, \`mbot.led(4).setColor("green")\`. -lösung: vb-drei-lichter-loesung.visustruct -::: - -## Now in Java: running light - -In a running light, a light moves from one LED to the next. The loop variable \`i\` is the number of the LED. - -::: übung vb-java-lauflicht -titel: Running light -code: java -auftrag: Write a for loop in Java in which \`i\` runs from 0 to 4. Inside, LED \`i\` turns blue, the mBot2 waits 200 milliseconds, and LED \`i\` goes off again. -befehle: ledColor 0 "blue", pause 200, ledColor 0 "off", ledColor 1 "blue", pause 200, ledColor 1 "off", ledColor 2 "blue", pause 200, ledColor 2 "off", ledColor 3 "blue", pause 200, ledColor 3 "off", ledColor 4 "blue", pause 200, ledColor 4 "off" -braucht: forSchleife -tipp: \`for (int i = 0; i < 5; i++) { … }\` -tipp: Inside the loop: \`mbot.led(i).setColor("blue");\`, \`mbot.pause(200);\`, \`mbot.led(i).setColor("off");\`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -::: übung vb-fehler-15-einzelne-leds -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Running light", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-15-einzelne-leds-start.visustruct -befehle: ledColor 0 "blue", pause 200, ledColor 0 "off", ledColor 1 "blue", pause 200, ledColor 1 "off", ledColor 2 "blue", pause 200, ledColor 2 "off", ledColor 3 "blue", pause 200, ledColor 3 "off", ledColor 4 "blue", pause 200, ledColor 4 "off" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`mbot.led(i)\`. -lösung: vb-java-lauflicht-loesung.visustruct -::: - -## Game: Which LED is on? - -::: spiel frage -titel: Game: Which LED is on? -intro: Five LEDs numbered 0 to 4. Which ones light up? Six rounds. - -runde: - mbot.led(0).setColor("red"); -frage: Which LED lights up red? -- [x] the first one -- the second one -- all of them -> The numbers start at 0. LED 0 is the first one. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blue"); - } -frage: Which LEDs light up blue? -- LEDs 0 to 3 -- [x] all five -- only LED 4 -> \`i\` runs from 0 to 4, that is all five. - -runde: - for (int i = 0; i < 5; i = i + 2) { - mbot.led(i).setColor("green"); - } -frage: Which LEDs light up green? -- LEDs 1 and 3 -- [x] LEDs 0, 2 and 4 -- LEDs 2 and 4 -> \`i\` jumps in steps of two: 0, 2, 4. - -runde: - mbot.led(2).setColor("yellow"); - mbot.led(2).setColor("off"); -frage: Which LED is on at the end? -- LED 2 yellow -- [x] none -- LED 3 yellow -> LED 2 goes on and straight off again. - -runde: - for (int i = 4; i >= 0; i--) { - mbot.led(i).setColor("red"); - mbot.pause(200); - mbot.led(i).setColor("off"); - } -frage: At which LED does this running light start? -- LED 0 -- [x] LED 4 -- LED 5 -> \`i\` starts at 4 and counts down. The light runs the other way round. - -runde: -frage: Ludwig writes \`mbot.led(5).setColor("red");\`. Which LED lights up? -- the fifth one -- the sixth one -- [x] none, there is no LED 5 -> The five LEDs are numbered 0 to 4. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.led(1).setColor("red"); - mbot.led(2).setColor("yellow"); - mbot.led(4).setColor("green"); -frage: Should switch LED 0 red, LED 2 yellow and LED 4 green. -fehler: 1 -besser: mbot.led(0).setColor("red"); -> The first LED has the number 0. - -runde: - for (int i = 1; i <= 5; i++) { - mbot.led(i).setColor("blue"); - } -frage: Should switch all five LEDs blue. -fehler: 1 -besser: for (int i = 0; i < 5; i++) { -> The LEDs are numbered 0 to 4. There is no LED 5. - -runde: - for (int i = 0; i < 5; i++) { - mbot.led(i).setColor("blue"); - mbot.pause(200); - mbot.led(0).setColor("off"); - } -frage: Running light: each LED goes blue for a moment and then off again. -fehler: 4 -besser: mbot.led(i).setColor("off"); -> The same LED \`i\` should go off, not always LED 0. - -runde: - mbot.led(3).setColor(red); -frage: Should switch LED 3 red. -fehler: 1 -besser: mbot.led(3).setColor("red"); -> The colour name is a text and needs quotation marks. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can address the LEDs by their numbers 0 to 4. → Every LED has a number -- I can make single LEDs light up in different colours. → Three lights in the structogram -- I can build a running light with a loop. → Now in Java: running light -::: - -## Quiz - -::: quiz -? Which number does the first LED have? -→ Every LED has a number -- [x] 0 -- [ ] 1 -- [ ] 5 -> As with arrays, you count from 0. The five LEDs have the numbers 0 to 4. -? What does \`mbot.led(4).setColor("off")\` do? -→ Three lights in the structogram -- [ ] All LEDs go off. -- [x] Only LED 4 goes off. -- [ ] Four LEDs go off. -::: -`,Az=`--- -titel: The speaker -bereich: Light, sound and screen -kurz: Tones, a scale and a siren. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what the two numbers in \`playTone\` mean -- build a sequence of tones -- calculate with the counting variable in Java -- make the mBot2 show a feeling -::: - -## How high, how long? - -::: mbot2 -focus: cyberpi -view: top -::: - -The [CyberPi]{part=cyberpi} has a small speaker. \`mbot.playTone(440, 300)\` plays a tone: - -- The first number is the **pitch** in hertz (vibrations per second). The bigger, the higher. -- The second number is the **length** in milliseconds. - -| Note | C | D | E | F | G | A | B | C' | -|---|---|---|---|---|---|---|---|---| -| Hertz | 262 | 294 | 330 | 349 | 392 | 440 | 494 | 523 | - -> In the simulation you hear the tones through your computer's speakers. Feel free to turn it down a bit. - -## Scale in the structure chart - -::: übung vb-tonleiter -titel: Scale -auftrag: The mBot2 plays the notes C, D, E, F, G one after the other, each 300 milliseconds long. The hertz numbers are in the table. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -tipp: Drag **play a tone** into the structure chart five times. -tipp: The boxes are \`mbot.playTone(262, 300)\`, \`mbot.playTone(294, 300)\` and so on. -lösung: vb-tonleiter-loesung.visustruct -::: - -::: angeberwissen -The note A at 440 hertz is called **concert pitch**. Orchestras tune their instruments to it. One octave higher has exactly twice as many vibrations: 880 hertz. -::: - -## Now in Java: higher and higher - -In a for loop you can calculate with the counting variable. That way each tone is higher than the last: - -\`\`\`java -mbot.playTone(i * 200, 200); -\`\`\` - -::: übung vb-java-hoeher -titel: Higher and higher -code: java -auftrag: Write a for loop in Java in which \`i\` counts from 1 to 5. In each round the mBot2 plays a tone with \`i * 200\` hertz, 200 milliseconds long. -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -braucht: forSchleife -tipp: \`for (int i = 1; i <= 5; i++) { … }\` -tipp: The loop only contains \`mbot.playTone(i * 200, 200);\`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Showing feelings - -The mBot2 can also be happy, sad or sleepy. With **show a feeling** (\`mbot.emotion("smile")\`) it plays a little show: the LEDs light up, it makes a sound and wiggles a bit. - -| Text | Feeling | -|---|---| -| \`mbot.emotion("smile")\` | laughs | -| \`mbot.emotion("hello")\` | says hello | -| \`mbot.emotion("sad")\` | is sad | -| \`mbot.emotion("angry")\` | is angry | -| \`mbot.emotion("dizzy")\` | is dizzy | -| \`mbot.emotion("zzz")\` | sleeps | - -> Try it with the real mBot2. The simulator only shows the command. More feelings are listed under **Reference**. - -::: übung vb-fehler-16-lautsprecher -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Higher and higher", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-16-lautsprecher-start.visustruct -befehle: playTone 200 200, playTone 400 200, playTone 600 200, playTone 800 200, playTone 1000 200 -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be \`mbot.playTone(i * 200, 200)\`. -lösung: vb-java-hoeher-loesung.visustruct -::: - -## Game: From low to high - -::: spiel reihenfolge -titel: Game: From low to high -intro: Tap the answers in the right order. One wrong tap ends the round. Five rounds. - -runde: -frage: Tap the tones from low to high. -- \`mbot.playTone(262, 300)\` -- \`mbot.playTone(330, 300)\` -- \`mbot.playTone(440, 300)\` -- \`mbot.playTone(880, 300)\` -> The more hertz, the higher the tone. - -runde: -frage: Tap the tones from short to long. -- \`mbot.playTone(440, 100)\` -- \`mbot.playTone(440, 300)\` -- \`mbot.playTone(440, 1000)\` -- \`mbot.playTone(440, 2000)\` -> The second number is the length in milliseconds. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.playTone(i * 200, 200); - } -frage: Tap the tones in the order in which they sound. -- 600 hertz -- 400 hertz -- 200 hertz -> \`i\` counts down: 3, 2, 1. The tones get lower and lower. - -runde: -frage: Tap the tones from low to high. -- C at 262 hertz -- concert pitch A -- A one octave higher -> Concert pitch A has 440 hertz; one octave higher has twice as many: 880 hertz. - -runde: -frage: Tap the times from short to long. -- 300 milliseconds -- 1 second -- 1500 milliseconds -- 2 seconds -> 1 second is 1000 milliseconds, 2 seconds are 2000. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what the two numbers in \`playTone\` mean. → How high, how long? -- I can build a sequence of tones. → Scale in the structure chart -- I can calculate with the counting variable in Java. → Now in Java: higher and higher -- I can make the mBot2 show a feeling. → Showing feelings -::: - -## Quiz - -::: quiz -? Which tone is higher? -→ How high, how long? -- [x] \`mbot.playTone(880, 300)\` -- [ ] \`mbot.playTone(440, 300)\` -> More hertz means higher. -? How long does \`mbot.playTone(440, 1000)\` sound? -→ How high, how long? -- [ ] 440 seconds -- [x] 1 second -- [ ] 1000 seconds -> 1000 milliseconds are one second. -::: -`,jz=`--- -titel: Arrays and melodies -bereich: Light, sound and screen -kurz: Store many values in one array and work through them with a loop. ---- - -## Skills I will gain now - -::: kompetenzen -- create an array and play it with a loop -- use an array of texts in Java -- collect measurements in an array and work out the average -::: - -## A melody as an array - -A melody is a row of tones. Instead of five variables \`tone1\`, \`tone2\`, … you use an **array**. An array is a row of slots under **one** name: - -\`\`\`java -int[] tones = {262, 294, 330, 349, 392}; -\`\`\` - -- \`int[]\` means: an array of whole numbers. -- The slots are numbered from **0**. \`tones[0]\` is 262, \`tones[4]\` is 392. -- \`tones.length\` tells you how many slots there are, here 5. - -A for loop with \`i\` from 0 to \`tones.length - 1\` reaches every slot. - -::: übung vb-melodie -titel: Melody -auftrag: Create an array \`tones\` with the pitches 262, 294, 330, 349 and 392. A for loop plays them one after the other, each for 300 milliseconds. -befehle: playTone 262 300, playTone 294 300, playTone 330 300, playTone 349 300, playTone 392 300 -braucht: forSchleife -tipp: \`int[] tones = {262, 294, 330, 349, 392}\` creates the array. -tipp: The loop runs from \`i = 0\` while \`i < tones.length\`. Inside is \`mbot.playTone(tones[i], 300)\`. -lösung: vb-melodie-loesung.visustruct -::: - -> If you want a different melody, you only change the array. The loop stays the same. - -## Now in Java: rainbow - -The five LEDs have the numbers 0 to 4, just like the slots of an array with five values. An array can hold texts too: \`String[] colours = {"red", "yellow"};\`. LED \`i\` then gets the colour from slot \`i\`: \`mbot.led(i).setColor(colours[i])\`. - -::: übung vb-farben -titel: Rainbow -code: java -auftrag: Write in Java: create an array \`colours\` with the texts \`"red"\`, \`"yellow"\`, \`"green"\`, \`"blue"\` and \`"purple"\`. A for loop gives every LED \`i\` the colour from \`colours[i]\`. -befehle: ledColor 0 "red", ledColor 1 "yellow", ledColor 2 "green", ledColor 3 "blue", ledColor 4 "purple" -braucht: forSchleife -tipp: \`String[] colours = {"red", "yellow", "green", "blue", "purple"};\` creates the array. -tipp: \`for (int i = 0; i < colours.length; i++) { mbot.led(i).setColor(colours[i]); }\` -lösung: vb-farben-loesung.visustruct -::: - -## Now in Java: measurement series - -Sensors never measure exactly. Hold your hand in front of the ultrasonic sensor: the number always jumps around a little. That is why professionals measure several times and work out the **average**. - -The measurements go into an array. \`new int[5]\` creates an array with five empty slots: - -\`\`\`java -int[] values = new int[5]; -for (int i = 0; i < values.length; i++) { - values[i] = mbot.getDistance(); - mbot.pause(100); -} -\`\`\` - -Then you add up all the values. For that you need a variable that starts at 0 and grows in every pass: - -\`\`\`java -int sum = 0; -for (int i = 0; i < values.length; i++) { - sum = sum + values[i]; -} -\`\`\` - -The average is the sum divided by the number of values. Write \`5.0\` instead of \`5\`, then Java calculates with decimal numbers: \`double average = sum / 5.0;\` - -::: übung vb-messreihe -titel: Measurement series -code: java -auftrag: Measure the distance five times into an array \`int[] values = new int[5]\`, with a 100 ms pause in between. Then add up all values, work out the average and show for example **Average: 40.0** on the screen. -messwerte: getDistance=40 -> screen "Average: 40.0" -messwerte: getDistance=25 -> screen "Average: 25.0" -braucht: forSchleife -tipp: Two for loops: the first fills the array with \`values[i] = mbot.getDistance();\`, the second calculates \`sum = sum + values[i];\`. -tipp: Then \`double average = sum / 5.0;\` and \`mbot.showText("Average: " + average);\`. -lösung: vb-messreihe-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Arrays"](?visustruct&lektion=15-arrays). It opens in a new window. - -::: übung vb-fehler-17-arrays-melodie -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Measurement series", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-17-arrays-melodie-start.visustruct -messwerte: getDistance=40 -> screen "Average: 40.0" -messwerte: getDistance=25 -> screen "Average: 25.0" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`sum = sum + values[i]\`. -lösung: vb-messreihe-loesung.visustruct -::: - -## Game: Which slot? - -::: spiel frage -titel: Game: Which slot? -intro: Slots are counted from 0. What is where in the array? Six rounds. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is \`tones[2]\`? -- 294 -- [x] 330 -- 349 -> Slot 0 is 262, slot 1 is 294, slot 2 is 330. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is \`tones.length\`? -- 4 -- [x] 5 -- 392 -> \`length\` counts the slots. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What is \`tones[tones.length - 1]\`? -- 349 -- [x] 392 -- an error -> \`tones.length - 1\` is 4, the last slot. - -runde: - int[] tones = {262, 294, 330, 349, 392}; -frage: What happens with \`mbot.playTone(tones[5], 300)\`? -- It plays 392 hertz. -- It plays nothing and carries on. -- [x] An error: there is no slot 5. -> The slots are numbered 0 to 4. Java reports an \`ArrayIndexOutOfBoundsException\`. - -runde: - String[] colours = {"red", "yellow", "green", "blue", "purple"}; - for (int i = 0; i < colours.length; i++) { - mbot.led(i).setColor(colours[i]); - } -frage: Which colour does LED 3 have? -- green -- [x] blue -- purple -> LED 3 gets \`colours[3]\`, the fourth word. - -runde: - int[] values = {20, 22, 18, 20, 20}; - int sum = 0; - for (int i = 0; i < values.length; i++) { - sum = sum + values[i]; - } - double average = sum / 5.0; -frage: What value does \`average\` have? -- 18.0 -- [x] 20.0 -- 100.0 -> The sum is 100, and 100 / 5.0 is 20.0. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - int[] tones = {262, 294, 330, 349, 392}; - for (int i = 0; i <= tones.length; i++) { - mbot.playTone(tones[i], 300); - } -frage: Should play all five tones. -fehler: 2 -besser: for (int i = 0; i < tones.length; i++) { -> With \`<=\` the loop also tries to play \`tones[5]\`. That slot doesn't exist. - -runde: - int[] tones = {262, 294, 330, 349, 392}; - for (int i = 0; i < tones.length; i++) { - mbot.playTone(tones, 300); - } -frage: Should play all five tones. -fehler: 3 -besser: mbot.playTone(tones[i], 300); -> One slot is played, \`tones[i]\`, not the whole array. - -runde: - String[] colours = {"red", "yellow", "green", "blue", "purple"}; - for (int i = 0; i < colours.length(); i++) { - mbot.led(i).setColor(colours[i]); - } -frage: Should give each LED a colour from the array. -fehler: 2 -besser: for (int i = 0; i < colours.length; i++) { -> Arrays have \`length\` without brackets. Only \`length()\` for Strings needs brackets. - -runde: - int[] values = new int[5]; - int sum = 0; - for (int i = 0; i < values.length; i++) { - sum = values[i]; - } -frage: Should add up all the measurements. -fehler: 4 -besser: sum = sum + values[i]; -> Like this, only the last value ends up in \`sum\`. The new value has to be added on. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can create an array and play it with a loop. → A melody as an array -- I can use an array of texts in Java. → Now in Java: rainbow -- I can collect measurements in an array and work out the average. → Now in Java: measurement series -::: - -## Quiz - -::: quiz -? What is \`tones[1]\` if \`int[] tones = {262, 294, 330};\`? -→ A melody as an array -- [ ] 262 -- [x] 294 -- [ ] 1 -> The slots count from 0. So slot 1 is the second one. -? How many slots does \`{262, 294, 330}\` have, so what is \`tones.length\`? -→ A melody as an array -- [ ] 2 -- [x] 3 -- [ ] 330 -> \`length\` counts the slots, here three. That is why the last slot has the number 2. -::: -`,Cz=`--- -titel: The screen -bereich: Light, sound and screen -kurz: Show text, numbers and sensor values on the CyberPi. ---- - -## Skills I will gain now - -::: kompetenzen -- show text on the screen -- show several texts one after the other -- show numbers and sensor values -- write a countdown in Java -::: - -## Showing text - -::: mbot2 -focus: screen -view: back -::: - -The CyberPi's colour [screen]{part=screen} is an actuator too. With **show text** you write something on it: - -\`\`\`java -mbot.showText("Hello!") -\`\`\` - -The text is in quotation marks, as with \`System.out.println\`. A new text replaces the old one. - -> In the simulation the text appears on the screen of the 3D model and below the world after 📺. - -## Greeting in the structure chart - -::: übung vb-begruessung -titel: Greeting -auftrag: The mBot2 first shows “Hello!”, waits 1000 milliseconds and then shows “I am VisuBot”. -befehle: showText "Hello!", pause 1000, showText "I am VisuBot" -tipp: Drag **show text** twice and **wait a moment** once into the structure chart. -tipp: \`mbot.showText("Hello!")\`, \`mbot.pause(1000)\`, \`mbot.showText("I am VisuBot")\`. -lösung: vb-begruessung-loesung.visustruct -::: - -## Numbers and sensor values - -Instead of text you can also show numbers and variables, and with \`+\` you join everything together: - -\`\`\`java -mbot.showText(i) -mbot.showText("Distance: " + mbot.getDistance()) -\`\`\` - -That way the mBot2 shows you what its sensors are measuring right now. - -## Now in Java: countdown - -::: übung vb-java-countdown -titel: Countdown -code: java -auftrag: Write a countdown in Java: a for loop counts \`i\` backwards from 3 to 1. In each round the mBot2 shows \`i\` and waits 1000 milliseconds. After that it shows “Go!”. -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Go!" -braucht: forSchleife -tipp: Counting backwards: \`for (int i = 3; i >= 1; i--) { … }\` -tipp: Inside the loop: \`mbot.showText(i);\` and \`mbot.pause(1000);\`. After the loop \`mbot.showText("Go!");\`. -lösung: vb-java-countdown-loesung.visustruct -::: - -::: übung vb-fehler-18-bildschirm -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Countdown", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-18-bildschirm-start.visustruct -befehle: showText "3", pause 1000, showText "2", pause 1000, showText "1", pause 1000, showText "Go!" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`mbot.showText(i)\`. -lösung: vb-java-countdown-loesung.visustruct -::: - -## Game: What's on the screen? - -::: spiel frage -titel: Game: What's on the screen? -intro: Read the code and predict what the screen shows at the end. Six rounds. - -runde: - mbot.showText("Hello!"); - mbot.pause(1000); - mbot.showText("I am VisuBot"); -frage: What is on the screen at the end? -- Hello! I am VisuBot -- [x] I am VisuBot -- Hello! -> A new text replaces the old one. - -runde: - mbot.showText("Distance: " + 25); -frage: What is on the screen? -- [x] Distance: 25 -- "Distance: " + 25 -- Distance: -> The \`+\` attaches the number to the text. - -runde: - int i = 3; - mbot.showText("i"); -frage: What is on the screen? -- 3 -- [x] i -- "i" -> In quotation marks, \`i\` is a text, not a variable. The quotation marks themselves don't appear. - -runde: - for (int i = 3; i >= 1; i--) { - mbot.showText(i); - mbot.pause(1000); - } - mbot.showText("Start!"); -frage: Which number is shown just before "Start!"? -- 0 -- [x] 1 -- 3 -> The countdown shows 3, 2, 1. At 0, \`i >= 1\` is false. - -runde: - mbot.showText("Sum: " + 2 + 3); -frage: What is on the screen? -- Sum: 5 -- [x] Sum: 23 -- Sum: 2 + 3 -> Java works from left to right: first the 2 is attached to the text, then the 3. - -runde: - mbot.showText("Sum: " + (2 + 3)); -frage: What is on the screen? -- [x] Sum: 5 -- Sum: 23 -- Sum: (2 + 3) -> Java works out the brackets first. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - mbot.showText("Hello!"); - mbot.pause(1); - mbot.showText("I am VisuBot"); -frage: Should show "Hello!", wait one second and then show "I am VisuBot". -fehler: 2 -besser: mbot.pause(1000); -> \`pause\` counts in milliseconds. One second is 1000. - -runde: - for (int i = 3; i >= 1; i++) { - mbot.showText(i); - mbot.pause(1000); - } -frage: Countdown: should show 3, 2, 1. -fehler: 1 -besser: for (int i = 3; i >= 1; i--) { -> Counting down means \`i--\`. With \`i++\` the loop never stops. - -runde: - mbot.showText(Hello!); -frage: Should show "Hello!". -fehler: 1 -besser: mbot.showText("Hello!"); -> Text goes in quotation marks. - -runde: - mbot.showText("Distance: " - mbot.getDistance()); -frage: Should show for example "Distance: 25". -fehler: 1 -besser: mbot.showText("Distance: " + mbot.getDistance()); -> You join text and number with \`+\`. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can show text on the screen. → Showing text -- I can show several texts one after the other. → Greeting in the structure chart -- I can show numbers and sensor values. → Numbers and sensor values -- I can write a countdown in Java. → Now in Java: countdown -::: - -## Quiz - -::: quiz -? What does \`mbot.showText("Distance: " + 25)\` show? -→ Numbers and sensor values -- [x] Distance: 25 -- [ ] "Distance: " + 25 -- [ ] 25 -> The \`+\` adds the number to the text. -? What happens to the old text when you show a new one? -→ Showing text -- [x] It is replaced. -- [ ] The new one appears below it. -::: -`,Dz=`--- -titel: Strings -bereich: Light, sound and screen -kurz: Work with texts, pick out letters and show them on the screen. ---- - -## Skills I will gain now - -::: kompetenzen -- work with \`length()\` and \`substring(…)\` -- join texts and numbers with \`+\` -- get single characters with \`charAt(…)\` -::: - -## Letter by letter - -In Java a text is called a **string** or \`String\`. It consists of single characters, numbered from **0** just like an array: - -| Character | V | i | s | u | B | o | t | -|---|---|---|---|---|---|---|---| -| Number | 0 | 1 | 2 | 3 | 4 | 5 | 6 | - -- \`word.length()\` gives the number of characters, here 7. -- \`word.substring(0, 3)\` gives the first 3 characters: \`"Vis"\`. - -The mBot2 should build its name on the [screen]{part=screen}: first **V**, then **Vi**, then **Vis** and so on up to **VisuBot**. - -::: übung vb-buchstaben -titel: Build the name -auftrag: Create \`String word = "VisuBot"\`. A for loop counts \`i\` from 1 to \`word.length()\`. Inside, the mBot2 shows \`word.substring(0, i)\` and waits 300 milliseconds. -befehle: showText "V", pause 300, showText "Vi", pause 300, showText "Vis", pause 300, showText "Visu", pause 300, showText "VisuB", pause 300, showText "VisuBo", pause 300, showText "VisuBot", pause 300 -braucht: forSchleife -tipp: The loop starts at \`int i = 1\` and runs as long as \`i <= word.length()\`. -tipp: Inside the loop: \`mbot.showText(word.substring(0, i))\` and \`mbot.pause(300)\`. -lösung: vb-buchstaben-loesung.visustruct -::: - -## Now in Java: text and length - -With \`+\` you join texts and numbers. This way the mBot2 shows a text followed by its length. - -::: übung vb-java-textlaenge -titel: How long is the text? -code: java -auftrag: Write in Java: create \`String text = "Hello class"\`. The mBot2 shows the text and its length in brackets, so **Hello class (11)**. -befehle: showText "Hello class (11)" -tipp: \`text.length()\` gives the length. The space counts too. -tipp: \`mbot.showText(text + " (" + text.length() + ")");\` -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Now in Java: scrolling text - -\`word.charAt(i)\` gives the character with the number \`i\`. If you show the characters one after the other with a pause, the word runs across the screen letter by letter: - -\`\`\`java -String word = "Ludwig"; -for (int i = 0; i < word.length(); i++) { - mbot.showText(word.charAt(i)); - mbot.pause(500); -} -\`\`\` - -| Pass | \`i\` | Screen | -|---|---|---| -| 1 | 0 | L | -| 2 | 1 | u | -| 3 | 2 | d | - -::: übung vb-laufschrift -titel: Ludwig backwards -code: java -auftrag: Create \`String word = "Ludwig"\`. Show the word **backwards** letter by letter on the screen, so g, i, w, d, u, L, with a 500 ms pause after each letter. -befehle: showText "g", pause 500, showText "i", pause 500, showText "w", pause 500, showText "d", pause 500, showText "u", pause 500, showText "L", pause 500 -braucht: forSchleife -tipp: The last character has the number \`word.length() - 1\`. The loop counts backwards: \`for (int i = word.length() - 1; i >= 0; i--)\`. -tipp: Inside the loop: \`mbot.showText(word.charAt(i));\` and \`mbot.pause(500);\`. -lösung: vb-laufschrift-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Strings"](?visustruct&lektion=17-zeichenketten). It opens in a new window. - -::: übung vb-fehler-19-zeichenketten -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "How long is the text?", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-19-zeichenketten-start.visustruct -befehle: showText "Hello class (11)" -tipp: The error message names an unknown variable. What is the variable called where it is created? Java is case-sensitive. -tipp: It should be \`mbot.showText(text +\`. -lösung: vb-java-textlaenge-loesung.visustruct -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Read what the mBot2 should show and tap the wrong line before time runs out. Five rounds, 30 seconds each. -zeit: 30 - -runde: - String word = "VisuBot"; - for (int i = 1; i <= word.length(); i++) { - mbot.showText(word.substring(1, i)); - mbot.pause(300); - } -frage: Should show V, Vi, Vis and so on up to VisuBot. -fehler: 3 -besser: mbot.showText(word.substring(0, i)); -> The first character has the number 0. With \`substring(1, i)\` the V is missing. - -runde: - String text = "Hello class"; - mbot.showText(text + " (" + text.length + ")"); -frage: Should show **Hello class (11)**. -fehler: 2 -besser: mbot.showText(text + " (" + text.length() + ")"); -> For a String, \`length()\` is a method and needs brackets. Only arrays have \`length\` without brackets. - -runde: - String word = "Ludwig"; - for (int i = 0; i <= word.length(); i++) { - mbot.showText(word.charAt(i)); - mbot.pause(500); - } -frage: Should show L, u, d, w, i, g. -fehler: 2 -besser: for (int i = 0; i < word.length(); i++) { -> The last character has the number 5. At \`i = 6\` there is no character left. - -runde: - String word = "Ludwig"; - for (int i = word.length(); i >= 0; i--) { - mbot.showText(word.charAt(i)); - mbot.pause(500); - } -frage: Should show the word backwards: g, i, w, d, u, L. -fehler: 2 -besser: for (int i = word.length() - 1; i >= 0; i--) { -> Backwards you start at the last character, and that has the number \`word.length() - 1\`. - -runde: - String name = 'Ludwig'; - mbot.showText("Hello " + name); -frage: Should show **Hello Ludwig**. -fehler: 1 -besser: String name = "Ludwig"; -> Texts go in double quotation marks. Single ones are only for a single character (\`char\`). -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 8 6 LED: the mBot2 has five of these little lights -- ↓ 2 7 MILLISECOND: a thousandth of a second -- → 11 2 SCREEN: mbot.showText shows a text on it -- ↓ 4 4 ACTUATOR: a part that does something, e.g. light up -- → 4 1 ARRAY: a row of slots under one name -- → 9 1 HERTZ: unit of pitch: vibrations per second -- ↓ 1 1 SPEAKER: mbot.playTone plays a tone through it -- → 1 1 STRING: a text in Java -lösung: 7 7, 10 7, 7 4, 11 7, 12 7 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can work with \`length()\` and \`substring(…)\`. → Letter by letter -- I can join texts and numbers with \`+\`. → Now in Java: text and length -- I can get single characters with \`charAt(…)\`. → Now in Java: scrolling text -::: - -## Quiz - -::: quiz -? What does \`"VisuBot".substring(0, 4)\` give? -→ Letter by letter -- [ ] \`"VisuB"\` -- [x] \`"Visu"\` -- [ ] \`"uBot"\` -> \`substring(0, 4)\` takes the characters 0, 1, 2 and 3, so four of them. -? What does \`"Ludwig".charAt(0)\` give? -→ Now in Java: scrolling text -- [x] \`L\` -- [ ] \`u\` -- [ ] \`g\` -> Characters are counted from 0. Character 0 is the first one. -::: -`,Bz=`--- -titel: Motors and speed -bereich: Motors and methods -kurz: Step on the gas, steer and follow a line. ---- - -## Skills I will gain now - -::: kompetenzen -- step on the gas with wheels separately and stop -- drive until a sensor reports something -- write a line follower in Java -::: - -## Two motors, two wheels - -::: mbot2 -focus: motors, wheels -view: bottom -::: - -The mBot2 has two [motors]{part=motors}, each drives one [wheel]{part=wheels}. So far you have used **move forward** and **turn right**. These commands drive a fixed distance and wait until the mBot2 has arrived. - -With **wheels separately** (\`mbot.drive(left, right)\`) you step on the gas instead: you say how strongly each wheel turns, in percent from −100 to 100. The mBot2 then **keeps going** until you tell it something else. With **stop** (\`mbot.stop()\`) it stops. - -| Command | What happens? | -|---|---| -| \`mbot.drive(40, 40)\` | both wheels equally fast: straight ahead | -| \`mbot.drive(-40, -40)\` | backwards | -| \`mbot.drive(10, 40)\` | right faster: curve to the left | -| \`mbot.drive(40, -40)\` | wheels against each other: turns right on the spot | -| \`mbot.stop()\` | stop | - -> In the simulator \`mbot.drive(100, 100)\` drives 20 cm per second. Try it in the [simulation](#zeige-simulation). - -## Full speed to the wall in the structure chart - -In lesson 5 the mBot2 drove to the wall in steps of 10 cm. With **wheels separately** it works without jerking: step on the gas once, wait until the wall is close, then stop. - -::: übung vb-gas-bis-wand -titel: Full speed to the wall -auftrag: The mBot2 steps on the gas with 50 %. As long as the wall is more than 15 cm away, it waits a moment. Then it stops. There are three worlds. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: At the top is \`mbot.drive(50, 50)\`. Below it a while loop with the condition \`mbot.getDistance() > 15\`. -tipp: Inside the loop is only **wait a moment**. The mBot2 keeps driving meanwhile! After the loop is **stop**. -lösung: vb-gas-bis-wand-loesung.visustruct -::: - -## The line follower - -::: mbot2 -focus: lineSensor -view: bottom -::: - -A classic: the mBot2 follows a black line on the floor. For this it looks at the two middle probes of the [line sensor]{part=lineSensor}: - -| Sensor | Text | -|---|---| -| left middle probe sees black | \`mbot.isLineLeft()\` | -| right middle probe sees black | \`mbot.isLineRight()\` | - -The idea: if **both** see the line, it drives straight ahead. If only the **left** one sees it, the line has moved to the left: it steers left, so the right wheel turns faster. If only the **right** one sees it, it steers right. - -## Now in Java: line follower - -::: übung vb-java-linienfolger -titel: Line follower -code: java -auftrag: Write a line follower in Java. As long as the wall is more than 15 cm away, the mBot2 checks its two probes and steers. Then it stops. In the first world the line bends to the right, in the second to the left. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: \`while (mbot.getDistance() > 15) { … }\` and after it \`mbot.stop();\`. -tipp: Inside the loop: \`if (mbot.isLineLeft() && mbot.isLineRight()) { mbot.drive(40, 40); } else if (mbot.isLineLeft()) { mbot.drive(10, 40); } else if (mbot.isLineRight()) { mbot.drive(40, 10); }\` -lösung: vb-java-linienfolger-loesung.visustruct -::: - -> For the real mBot2, stick the line with black insulating tape on a light floor. Drive slowly at first, otherwise it shoots past the line in the curve. - -::: übung vb-fehler-20-motoren -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Line follower", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-20-motoren-start.visustruct -welt: linie-1, linie-2 -tipp: Read the error message carefully. In which box does the simulation stop? Java is case-sensitive. -tipp: It should be \`mbot.stop()\`. -lösung: vb-java-linienfolger-loesung.visustruct -::: - -## Game: Where does it go? - -::: spiel frage -titel: Game: Where does it go? -intro: Power for left and right in percent: where does the mBot2 go? Seven rounds. -antworten: straight ahead | backwards | curve to the left | curve to the right | turns on the spot - -runde: - mbot.drive(40, 40); -frage: Where does it go? -richtig: straight ahead -> Both wheels turn at the same speed. - -runde: - mbot.drive(-40, -40); -frage: Where does it go? -richtig: backwards -> Minus means backwards, and both wheels are the same. - -runde: - mbot.drive(10, 40); -frage: Where does it go? -richtig: curve to the left -> The right wheel is faster, so it steers to the left. - -runde: - mbot.drive(40, 10); -frage: Where does it go? -richtig: curve to the right -> The left wheel is faster, so it steers to the right. - -runde: - mbot.drive(40, -40); -frage: Where does it go? -richtig: turns on the spot -> One wheel forward, one back: it turns right without moving from the spot. - -runde: - mbot.drive(-40, 40); -frage: Where does it go? -richtig: turns on the spot -> This time it turns to the left. - -runde: - if (mbot.isLineLeft() && !mbot.isLineRight()) { - ... - } -frage: Line follower: only the left sensor sees the line. What goes in the gap? -- \`mbot.drive(40, 10);\` -- [x] \`mbot.drive(10, 40);\` -- \`mbot.stop();\` -> The line has moved to the left. It steers to the left, so the right wheel turns faster. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can step on the gas with wheels separately and stop. → Two motors, two wheels -- I can drive until a sensor reports something. → Full speed to the wall in the structure chart -- I can write a line follower in Java. → Now in Java: line follower -::: - -## Quiz - -::: quiz -? What does \`mbot.drive(30, 30)\` do? -→ Two motors, two wheels -- [ ] It drives 30 cm and stops. -- [x] It drives straight ahead and does not stop until another driving command comes. -- [ ] It turns by 30°. -> **wheels separately** only steps on the gas. You have to stop it yourself. -? The mBot2 should steer to the right. Which command fits? -→ Two motors, two wheels -- [ ] \`mbot.drive(10, 40)\` -- [x] \`mbot.drive(40, 10)\` -- [ ] \`mbot.drive(-40, -40)\` -> If the left wheel turns faster, it drives a right curve. -::: -`,Pz=`--- -titel: Stopwatch and battery -bereich: Motors and methods -kurz: Measure time and keep an eye on the battery. ---- - -## Skills I will gain now - -::: kompetenzen -- reset the stopwatch and read it -- drive for a certain time -- show the battery level -::: - -## The stopwatch - -::: mbot2 -focus: cyberpi -view: top -::: - -A stopwatch runs inside the [CyberPi]{part=cyberpi}. It counts the seconds since the program started. With **reset stopwatch** (\`mbot.resetTimer()\`) it starts again at 0. - -| Sensor or command | Text | Value | -|---|---|---| -| reset stopwatch | \`mbot.resetTimer()\` | | -| stopwatch | \`mbot.getTimer()\` | seconds with a decimal place, e.g. \`1.5\` | - -This is how you drive for a certain time: reset the stopwatch, step on the gas, wait as long as the stopwatch is not far enough yet, stop. - -## Driving two seconds in the structure chart - -::: übung vb-zwei-sekunden -titel: Two seconds -auftrag: The mBot2 resets the stopwatch and steps on the gas with 50 %. As long as the stopwatch is below 2 seconds, it waits a moment. Then it stops. With 50 % it manages 10 cm per second, so it must stop 20 cm further on. -welt: zeit-1 -braucht: whileSchleife -tipp: At the top are **reset stopwatch** and \`mbot.drive(50, 50)\`. Below it a while loop with \`mbot.getTimer() < 2\`. -tipp: Inside the loop is **wait a moment**, after the loop **stop**. -lösung: vb-zwei-sekunden-loesung.visustruct -::: - -## The battery - -::: mbot2 -focus: shield -view: top -::: - -Under the transparent [shield]{part=shield} you see the battery. How full it is comes from \`mbot.getBattery()\` in percent: \`100\` is completely full. When it is almost empty, the motors get weaker. Then it is time to charge. - -::: angeberwissen -Your phone does not measure its battery directly in percent either. It measures the voltage: a full lithium battery has a little over 4 volts, an empty one just over 3. From that it calculates the percentage, just like the CyberPi. -::: - -## Now in Java: battery display - -::: übung vb-java-akku -titel: Battery display -code: java -auftrag: Write in Java: the mBot2 shows for example **Battery: 80 %** on the screen. If the battery is below 20 %, the LEDs also light up red. -messwerte: getBattery=80 -> none, ledOff, screen "Battery: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Battery: 15 %" -braucht: verzweigung -tipp: \`mbot.showText("Battery: " + mbot.getBattery() + " %");\` puts the text together from three parts. -tipp: After it: \`if (mbot.getBattery() < 20) { mbot.setLed(255, 0, 0); }\` -lösung: vb-java-akku-loesung.visustruct -::: - -::: übung vb-fehler-21-stoppuhr-akku -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Battery display", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-21-stoppuhr-akku-start.visustruct -messwerte: getBattery=80 -> none, ledOff, screen "Battery: 80 %" -messwerte: getBattery=15 -> none, ledOn, screen "Battery: 15 %" -tipp: Count the numbers in the brackets. How many does the command need? You can find it under **Reference**. -tipp: It should be \`mbot.setLed(255, 0, 0)\`. -lösung: vb-java-akku-loesung.visustruct -::: - -## Game: Stopwatch and battery - -::: spiel frage -titel: Game: Stopwatch and battery -intro: Read the code and predict what happens. Five rounds. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: How long does the mBot2 drive? -- 0.1 seconds -- [x] 2 seconds -- until it reaches the wall -> It waits until the stopwatch shows 2 seconds, then it stops. - -runde: - mbot.resetTimer(); - mbot.drive(50, 50); - while (mbot.getTimer() < 2) { - mbot.pause(100); - } - mbot.stop(); -frage: At 50 % it manages 10 cm per second. How far does it drive? -- 10 cm -- [x] 20 cm -- 50 cm -> 2 seconds times 10 cm per second is 20 cm. - -runde: - mbot.pause(3000); - mbot.resetTimer(); - mbot.pause(1000); - mbot.showText(mbot.getTimer()); -frage: Roughly which number does the screen show? -- 0 -- [x] 1 -- 4 -> After \`mbot.resetTimer()\` the stopwatch starts again at 0. After that only 1 second passes. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Battery: " + mbot.getBattery() + " %"); -frage: The battery has 15 % left. What happens? -- Only the screen shows **Battery: 15 %**. -- Only the LEDs light up red. -- [x] The screen shows **Battery: 15 %**, and the LEDs light up red. -> 15 is less than 20. The display is below the branch and always comes. - -runde: - if (mbot.getBattery() < 20) { - mbot.setLed(255, 0, 0); - } - mbot.showText("Battery: " + mbot.getBattery() + " %"); -frage: The battery has 80 %. What happens? -- [x] Only the screen shows **Battery: 80 %**. -- Only the LEDs light up red. -- Nothing at all. -> 80 is not less than 20, so the LEDs stay off. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can reset the stopwatch and read it. → The stopwatch -- I can drive for a certain time. → Driving two seconds in the structure chart -- I can show the battery level. → Now in Java: battery display -::: - -## Quiz - -::: quiz -? What does \`mbot.getTimer()\` give right after \`mbot.resetTimer()\`? -→ The stopwatch -- [x] 0 -- [ ] 100 -- [ ] The time of day -> The stopwatch starts again at 0. -? Why does the mBot2 only wait **a moment** in the loop instead of stopping? -→ Driving two seconds in the structure chart -- [ ] Because otherwise the loop does not start. -- [x] Because it should keep driving until the time is up. -- [ ] Because otherwise the stopwatch stops. -> Stepping on the gas still applies while it waits. -::: -`,Oz=`--- -titel: States: what am I doing right now? -bereich: Motors and methods -kurz: Switch a robo traffic light on with a state variable and a switch. ---- - -## Skills I will gain now - -::: kompetenzen -- explain what a state is -- act depending on the state with a switch and change to the next state -- tell states apart in Java with names and \`.equals\` -::: - -## What is a state? - -A traffic light is always in exactly one **state**: red, green or yellow. In each state it knows two things: what do I show? And what comes next? - -Our robo traffic light has three states. Each gets a number: - -| State | LEDs | next | -|---|---|---| -| 0 | red | 1 | -| 1 | green | 2 | -| 2 | yellow | 0 | - -You store the current state in a variable: \`int state = 0\`. A variable like this is called a **state variable**. A program that jumps from state to state like this is called a **state machine**. - -> A real traffic light in Germany has four states: red, red-yellow, green, yellow. Our robo traffic light leaves out red-yellow. - -::: angeberwissen -A robot vacuum is a state machine too: vacuuming, avoiding, driving back to the charging station. I only have two states: eating and sleeping. -::: - -## The robo traffic light in the structure chart - -The switch fits perfectly: there is one case for each state. Each case has two boxes: what do I do? And which state comes next? - -::: übung vb-ampel-zustand -titel: Robo traffic light -auftrag: Build a structure chart: create \`int state = 0\`. A for loop runs six times. Inside it is a switch on \`state\`. Case 0: LEDs red, \`state = 1\`. Case 1: LEDs green, \`state = 2\`. Otherwise: LEDs yellow, \`state = 0\`. After the switch the mBot2 waits one second. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, fallauswahl -tipp: You will find the switch among the Elements. Write \`state\` into it. The cases are called 0, 1 and default. -tipp: The colours are \`mbot.setLed(255, 0, 0)\` for red, \`mbot.setLed(0, 255, 0)\` for green and \`mbot.setLed(255, 200, 0)\` for yellow. Below the switch, still inside the loop, is \`mbot.pause(1000)\`. -lösung: vb-ampel-zustand-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Switch"](?visustruct&lektion=10-fallauswahl). It opens in a new window. - -## Now in Java: states with names - -Numbers are easy to forget. What was state 2 again? Names are easier to read. Then the state is a text: - -\`\`\`java -String state = "red"; -... -if (state.equals("red")) { - mbot.setLed(255, 0, 0); - state = "green"; -} else if (state.equals("green")) { - ... -\`\`\` - -In Java you compare texts with \`.equals("…")\`, not with \`==\`. - -::: übung vb-java-ampel-text -titel: Traffic light with names -code: java -auftrag: Write the robo traffic light in Java with \`String state = "red";\`. Use \`if\` and \`else if\` to check which state the light is in: red, then green, then yellow, then red again. The loop runs six times, with a 1 second pause after each pass. -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -braucht: forSchleife, verzweigung -tipp: Inside the loop: \`if (state.equals("red")) { … } else if (state.equals("green")) { … } else { … }\` and then \`mbot.pause(1000);\`. -tipp: In the red case are \`mbot.setLed(255, 0, 0);\` and \`state = "green";\`. Green switches to \`"yellow"\`, yellow back to \`"red"\`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -::: übung vb-fehler-21-zustaende -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Traffic light with names", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-21-zustaende-start.visustruct -befehle: 2 x (setLed 255 0 0, pause 1000, setLed 0 255 0, pause 1000, setLed 255 200 0, pause 1000) -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be \`state = "green"\`. -lösung: vb-java-ampel-text-loesung.visustruct -::: - -## States while driving - -The mBot2 can also drive with states. In the state **drive** it drives forward. When it sees a wall, it changes to the state **turn**. There it turns once and changes back to **drive**. - -::: diagramm vb-l21z-ausweichen.visustruct -Driving and avoiding with two states. Open the diagram in the editor and try it out in a world. -::: - -## Game: which state comes now? - -::: spiel frage -titel: Game: which state comes now? -intro: The robo traffic light switches: 0 red → 1 green → 2 yellow → 0 red. Which state comes next? Five rounds. -frage: Which state comes next? -antworten: 0 | 1 | 2 - -runde: -frage: The light is in state **0** (red) right now. -richtig: 1 -> After red comes green. - -runde: -frage: The light is in state **2** (yellow) right now. -richtig: 0 -> After yellow it starts all over again. - -runde: -frage: The light is in state **1** (green) right now. -richtig: 2 -> After green comes yellow. - -runde: -frage: The light starts in state 0 and switches on **twice**. -richtig: 2 -> 0 → 1 → 2. - -runde: -frage: The light starts in state 0 and switches on **three times**. -richtig: 0 -> 0 → 1 → 2 → 0. After three steps it is back at the start. -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can explain what a state is. → What is a state? -- I can act depending on the state with a switch and change to the next state. → The robo traffic light in the structure chart -- I can tell states apart in Java with names and \`.equals\`. → Now in Java: states with names -::: - -## Quiz - -::: quiz -? What does a state variable store? -→ What is a state? -- [ ] How often the loop has run -- [x] What the program is doing right now, for example red or green -- [ ] The biggest reading -? What is in each case of the switch? -→ The robo traffic light in the structure chart -- [x] What to do and which state comes next -- [ ] Only a pause -- [ ] A new loop -? How do you check in Java whether \`state\` contains the text \`"red"\`? -→ Now in Java: states with names -- [ ] \`state == red\` -- [x] \`state.equals("red")\` -- [ ] \`state = "red"\` -> You compare texts with \`.equals\`. A single \`=\` would assign \`"red"\`. -::: -`,Mz=`--- -titel: Your own methods -bereich: Motors and methods -kurz: Write and call your own commands with parameters and return values. ---- - -## Skills I will gain now - -::: kompetenzen -- build and call a subprogram with a parameter -- recognise methods in Java -- recognise and call an overloaded method -- write a method with a return value -::: - -## A subprogram for the square - -The mBot2 can already drive a square. Now it should drive two squares of different sizes. Instead of building the loop twice, you write a **subprogram**. In Java it is called a **method**. It is a command of your own that you can call as often as you like. - -The header \`void square(int side)\` says: - -- \`void\`: the method gives no result back, it only **does** something. -- \`square\`: that is the name of your new command. -- \`int side\`: when you call it, you pass a number, the **parameter**. - -::: übung vb-quadrat-methode -titel: Two squares -auftrag: Build a subprogram \`void square(int side)\` that drives a square with side length \`side\`. The main program calls it twice: first with 30, then with 50. -fahrt: 4 x (forward 30, turnRight), 4 x (forward 50, turnRight) -braucht: aufruf -tipp: Create a new tab with **+** and give it the title \`void square(int side)\`. Inside is a for loop that runs \`mbot.forward(side)\` and \`mbot.turnRight()\` four times. -tipp: The main program contains two call boxes: \`square(30)\` and \`square(50)\`. -lösung: vb-quadrat-methode-loesung.visustruct -unterprogramm: vb-quadrat-unterprogramm-loesung.visustruct -::: - -## Methods in Java - -In Java every method lives in a class. The program always starts in the method \`main\`. From there \`square\` is called: - -\`\`\`java -public class Squares { - public static void main(String[] args) { - square(30); - square(50); - } - - static void square(int side) { - for (int i = 0; i < 4; i++) { - mbot.forward(side); - mbot.turnRight(); - } - } -} -\`\`\` - -In [Code](#zeige-codelive) you see each method in its own tab, just like in the structogram. - -::: quiz -? What does \`void\` mean in \`void square(int side)\`? -→ Methods in Java -- [ ] The method has no parameter. -- [x] The method gives no result back. -- [ ] The method may only be called once. -> \`void\` means "empty": no value comes back. The parameter is in the brackets. -? With which value for \`side\` does \`square(50)\` run? -→ A subprogram for the square -- [ ] 30 -- [x] 50 -- [ ] with none -> The value in the brackets of the call becomes the parameter \`side\`. -::: - -## Same name, different parameters: overloading - -You know \`mbot.forward()\` and \`mbot.forward(30)\`. These are **two different methods with the same name**. Java can tell from the call which one is meant: by the **number** and the **type** of the parameters. This is called **overloading**. - -There is a third \`forward\` method. It has two parameters: a number and a **unit** as text. - -\`\`\`java -mbot.forward(); // 10 cm (default) -mbot.forward(30); // 30 cm -mbot.forward(30, "cm"); // 30 cm, unit written out -mbot.forward(2, "rotations"); // the wheels turn twice -\`\`\` - -One wheel rotation is as far as the wheel's circumference: π · 6.5 cm ≈ 20.4 cm. So two rotations are about 40.8 cm. \`mbot.backward\` has the same three versions. - -These are the three headers. The method name is the same, the parameter lists are different: - -\`\`\`java -void forward() -void forward(int cm) -void forward(int value, String unit) -\`\`\` - -You already know overloading: \`System.out.println\` exists for \`int\`, for \`double\`, for \`String\` and more. That is why you can simply put any of these values in the brackets. - -::: angeberwissen -Are the 20.4 cm really true for **your** mBot2? Professionals don't trust a number they haven't measured. Measure the wheel diameter and run \`mbot.forward(5, "rotations")\`: the expected distance is 102.1 cm. This is how software systems are tested: every **test case** has an expected result and an actual (measured) result. You test several times and write everything into a **test record**. Here is your [test record to print (PDF)](https://visubot.visucode.de/test-record-wheel-rotation.pdf). Give it to your teacher, so they can see whether all robots drive the same distance. -::: - -::: übung vb-ueberladung-umdrehung -titel: Square with rotations -auftrag: Drive a square in which every side is exactly **one wheel rotation** long. Use the overloaded method \`mbot.forward(1, "rotations")\`. -fahrt: 4 x (forward 20.42, turnRight) -braucht: forSchleife -tipp: A for loop with \`int i = 0\`, \`i < 4\`, \`i++\`. -tipp: Inside are \`mbot.forward(1, "rotations")\` and \`mbot.turnRight()\`. -lösung: vb-ueberladung-umdrehung-loesung.visustruct -::: - -::: übung vb-java-rechteck-umdrehungen -titel: Rectangle with rotations -code: java -auftrag: Write in Java: the mBot2 drives a rectangle. The long sides are **two** wheel rotations long, the short ones **one**. Use \`mbot.forward(2, "rotations")\` and \`mbot.forward(1, "rotations")\`. -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -braucht: forSchleife -tipp: A for loop that runs twice: \`for (int i = 0; i < 2; i++) { … }\`. -tipp: Inside the loop: \`mbot.forward(2, "rotations");\`, \`mbot.turnRight();\`, \`mbot.forward(1, "rotations");\`, \`mbot.turnRight();\` -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -::: quiz -? Which method does \`mbot.forward(3, "rotations")\` call? -→ Same name, different parameters: overloading -- [ ] \`forward()\` -- [ ] \`forward(int cm)\` -- [x] \`forward(int value, String unit)\` -> Java picks the version whose parameters fit: two parameters, a number and a text. -::: - -## A method with a return value - -A method can also **work something out** and give the result back. Instead of \`void\`, the header then has the data type of the result, and \`return\` gives the value back: - -\`\`\`java -static int centimetres(int rotations) { - return rotations * 20; -} -\`\`\` - -One wheel rotation is about 20 cm. So the call \`int distance = centimetres(3);\` puts 60 into \`distance\`. - -::: übung vb-zentimeter -titel: Rotations into centimetres -auftrag: Build a subprogram \`int centimetres(int rotations)\` that returns \`rotations * 20\`. The main program calls it with 3, drives the distance and shows **Distance: 60 cm** on the screen. -fahrt: forward 60 -befehle: showText "Distance: 60 cm" -braucht: aufruf -tipp: A new tab with the title \`int centimetres(int rotations)\`. One statement is enough inside: \`return rotations * 20\`. -tipp: In the main program: call \`int distance = centimetres(3)\`, then \`mbot.forward(distance)\` and \`mbot.showText("Distance: " + distance + " cm")\`. -lösung: vb-zentimeter-loesung.visustruct -unterprogramm: vb-zentimeter-unterprogramm-loesung.visustruct -::: - -> 📘 **More without the robot:** VisuStruct has the lesson ["Call and break"](?visustruct&lektion=18-aufruf-und-aussprung). It opens in a new window. - -::: übung vb-fehler-22-eigene-methoden -titel: Why doesn't it run? -code: java -auftrag: Ludwig typed in "Rectangle with rotations", but the program does not run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-22-eigene-methoden-start.visustruct -fahrt: 2 x (forward 40.84, turnRight, forward 20.42, turnRight) -tipp: A text belongs in quotation marks. Without them Java looks for a variable with that name. -tipp: It should be \`mbot.forward(1, "rotations")\`. -lösung: vb-java-rechteck-umdrehungen-loesung.visustruct -::: - -## Game: Which method? - -::: spiel frage -titel: Game: Which method? -intro: Same name, different parameters: which method does Java call, and what comes out? Six rounds. - -runde: - mbot.forward(30, "cm"); -frage: Which \`forward\` method runs? -- \`forward()\` -- \`forward(int cm)\` -- [x] \`forward(int value, String unit)\` -> Two parameters, a number and a text: that only fits the third one. - -runde: - mbot.forward(); -frage: How far does the mBot2 drive? -- [x] 10 cm -- not at all, the number is missing -- one wheel rotation -> Without a parameter, \`forward\` drives 10 cm. That is the version with no parameters. - -runde: - mbot.forward(2, "rotations"); -frage: Roughly how far does the mBot2 drive? -- 2 cm -- 20 cm -- [x] 41 cm -> One wheel rotation is about 20.4 cm, so two are about 40.8 cm. - -runde: - static int centimetres(int rotations) { - return rotations * 20; - } -frage: What does \`centimetres(4)\` give? -- 4 -- 24 -- [x] 80 -> The parameter \`rotations\` is 4, and \`return\` gives back 4 · 20. - -runde: - public static void main(String[] args) { - square(30); - square(50); - } -frage: How many squares does the mBot2 drive, and how big is the second one? -- one with 80 cm -- [x] two, the second with 50 cm sides -- two, both with 30 cm sides -> Each call drives one square. The value in the brackets becomes \`side\`. - -runde: -frage: Which header fits a method that gives back a number? -- \`void centimetres(int rotations)\` -- [x] \`int centimetres(int rotations)\` -- \`centimetres(int rotations)\` -> The data type of the result comes before the name. \`void\` means: no result. -::: - -## Game: Defuse the bomb - -::: spiel bombe -titel: Game: Defuse the bomb -intro: Every program contains **exactly one mistake**, and the bomb is ticking. Tap the wrong line before time runs out. Four rounds, 30 seconds each. -zeit: 30 - -runde: - static void square(int side) { - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } - } -frage: Should drive a square with sides of length \`side\`. -fehler: 3 -besser: mbot.forward(side); -> With \`30\` every square is the same size, whatever you pass in. - -runde: - static void centimetres(int rotations) { - return rotations * 20; - } -frage: Should give back the distance in cm. -fehler: 1 -besser: static int centimetres(int rotations) { -> A method that gives something back needs the data type of the result instead of \`void\`. - -runde: - static int centimetres(int rotations) { - int cm = rotations * 20; - } -frage: Should give back the distance in cm. -fehler: 2 -besser: return rotations * 20; -> The method calculates but gives nothing back. \`return\` is missing. - -runde: - mbot.forward(2, rotations); -frage: Should drive two wheel rotations. -fehler: 1 -besser: mbot.forward(2, "rotations"); -> The unit is a text and needs quotation marks. -::: - -## Game: Terms crossword - -::: spiel kreuzwort -titel: Game: Terms crossword -intro: Fill in the crossword with the technical terms from this part. Click a clue to jump to its word. The yellow fields spell the answer word. -- → 7 1 PARAMETER: value you write in the brackets when you call a method -- ↓ 5 7 METHOD: your own command with a name, e.g. square(30) -- ↓ 1 4 OVERLOADING: same method name, different parameters -- ↓ 2 9 BATTERY: mbot.getBattery() tells you how full it is -- → 2 4 VOID: in the header: the method gives nothing back -- ↓ 2 2 STOPWATCH: mbot.getTimer() reads it -- → 5 7 MOTOR: each wheel has its own -- ↓ 1 11 RETURN: gives a result back from a method -lösung: 7 5, 6 9, 5 9, 10 2, 5 8, 8 4 -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can build and call a subprogram with a parameter. → A subprogram for the square -- I can recognise methods in Java. → Methods in Java -- I can recognise and call an overloaded method. → Same name, different parameters: overloading -- I can write a method with a return value. → A method with a return value -::: - -## Quiz - -::: quiz -? The subprogram \`void quadrat(int seite)\` should drive a square with sides of 30 cm. What does the call look like? -→ A subprogram for the square -- [ ] \`quadrat()\` -- [x] \`quadrat(30)\` -- [ ] \`int seite = quadrat(30)\` -> The value in the brackets becomes the parameter \`seite\`. Because \`quadrat\` returns nothing, there is no assignment. -? In which method does a Java program start? -→ Methods in Java -- [ ] in the first method of the class -- [x] in the method \`main\` -- [ ] in the method \`quadrat\` -> The program always starts in \`main\`. The other methods are called from there. -? How does Java tell the overloaded methods \`forward()\`, \`forward(int cm)\` and \`forward(int wert, String einheit)\` apart? -→ Same name, different parameters: overloading -- [ ] by their name -- [x] by the number and type of the parameters -- [ ] by their order in the program -> The name is the same for all of them. Java chooses the variant whose parameters fit the call. -? \`static int zentimeter(int umdrehungen) { return umdrehungen * 20; }\`: What is in \`strecke\` after \`int strecke = zentimeter(2);\`? -→ A method with a return value -- [ ] 2 -- [ ] 20 -- [x] 40 -> \`return\` gives back 2 · 20 = 40, and this value ends up in \`strecke\`. -? A method returns a whole number. What is written in its header instead of \`void\`? -→ A method with a return value -- [ ] \`return\` -- [x] \`int\` -- [ ] \`static\` -> Instead of \`void\`, the header names the data type of the result. -::: -`,Zz=`--- -titel: What's next? -bereich: Outlook -kurz: Look back at what you learned with the mBot2 and see what is still waiting for you in VisuStruct. ---- - -## Skills I will gain now - -::: kompetenzen -- say which programming basics I learned with the mBot2 -- find and fix a mistake in a robot program on my own -- name the topics that are still waiting for me in VisuStruct -::: - -## What you can do now - -Congratulations, you have finished all the robot lessons! With the mBot2 you learned the **basics of structured programming**: - -- reading and building a **structure chart** and checking it with a **desk check**, writing a plan as a **flowchart** and as **pseudocode**, -- **sequence**, **branch** and **switch**, conditions with **and**, **or** and **not**, -- the loops **for**, **while** and **do-while**, also nested, and leaving a loop with **break**, -- **variables**, **data types** and **constants** with \`final\`, counting, adding up and finding the largest value, -- **arrays** and **strings**, -- your own **methods** with parameters and a return value. - -All of this is not just for robots. Every Java program is built from exactly these building blocks, even a game or an app. - -::: angeberwissen -Baah, do you know what you actually built with the mBot2? An **embedded system**: a small computer inside a device that measures its surroundings with sensors. Such systems are in washing machines, cars and pacemakers. A modern car has more than a hundred of them. And they are programmed with exactly the building blocks you know now. -::: - -## Once more: Why doesn't it run? - -One last bug hunt to finish. This time it is a mistake that even professionals often make. - -::: übung vb-fehler-23-wie-geht-es-weiter -titel: Why doesn't it run? -code: java -auftrag: The program should drive up to just before the wall, but it doesn't run. "Open in editor" shows the code in the Code area. Click "Run" and read the error message. Fix the mistake and click Check. -start: vb-fehler-23-wie-geht-es-weiter-start.visustruct -welt: wand-kurz, wand-mittel, wand-lang -tipp: Read the error message carefully. Compare the sensor with the other commands: what comes after \`mbot.forward\`? -tipp: A command is always followed by round brackets, even if there is nothing inside them. It should be \`mbot.getDistance() > 15\`. -lösung: vb-bis-wand-loesung.visustruct -::: - -## What is still missing - -Some programming topics have little to do with the robot. You will find them in **VisuStruct**, the world without the robot. Because you already know the basics, you will make quick progress there. The links open in a new window. - -- **Keyboard input:** asking for values while the program runs, in ["Input and output"](?visustruct&lektion=06-eingabe-und-ausgabe). -- **Tables in memory:** arrays with rows and columns, for example for a cinema, in ["Two-dimensional arrays"](?visustruct&lektion=16-zweidimensionale-arrays). -- **Searching and sorting:** finding a value quickly and putting a list in order, in ["Searching and sorting"](?visustruct&lektion=19-suchen-und-sortieren). -- **Other languages:** the same program in Python and JavaScript, in ["Python and JavaScript"](?visustruct&lektion=21-python-und-javascript). -- **Catching crashes:** what happens when someone types "abc" instead of a number, in ["When the program crashes: try and catch"](?visustruct&lektion=22-try-und-catch). -- **Files:** reading and saving text, tables and data, from ["Reading and saving text"](?visustruct&lektion=23-text-einlesen-und-speichern) via ["Tables as CSV"](?visustruct&lektion=25-tabellen-als-csv) to ["Data as JSON"](?visustruct&lektion=26-json). -- **Windows with buttons:** your own programs with a window, a text field and a button, from ["Your first window"](?visustruct&lektion=28-erstes-fenster) to ["Model View Controller"](?visustruct&lektion=40-model-view-controller). - -> You don't need the mBot2 for this. But you can come back at any time and teach it new tricks. - -## Game: Which world? - -::: spiel frage -titel: Game: Which world? -intro: Ludwig has ideas for new programs. Do you learn this with the mBot2 in VisuBot or without the robot in VisuStruct? Eight rounds. -antworten: VisuBot | VisuStruct - -runde: -frage: "Drive up to the wall and then beep." -richtig: VisuBot -> Distance sensor, motors and speaker: that is a job for the mBot2. - -runde: -frage: "A window with a button that counts to 10." -richtig: VisuStruct -> You build windows with buttons with Swing in VisuStruct. - -runde: -frage: "Play a melody that is stored in an array." -richtig: VisuBot -> You learned arrays and melodies with the mBot2's speaker. - -runde: -frage: "Read the class's marks from a CSV file." -richtig: VisuStruct -> You learn to read and save files in VisuStruct. - -runde: -frage: "If someone types 'abc' instead of a number, the program should not crash." -richtig: VisuStruct -> You catch crashes with try and catch. That is in VisuStruct. - -runde: -frage: "Follow a black line with the line sensor." -richtig: VisuBot -> The line sensor sits under the mBot2. - -runde: -frage: "Sort ten numbers by size." -richtig: VisuStruct -> Searching and sorting is in VisuStruct. But you can already find the largest value. - -runde: -frage: "Save the high score list in a file." -richtig: VisuStruct -> You learn to write files in VisuStruct in "Reading and saving text". -::: - -## Checklist - -Rate yourself honestly for each point: ☂️ not yet, ⛅ partly, ☀️ I can do this confidently. For ⛅ and ☂️, **Review** helps you. Then comes the quiz. It shows you whether your self-assessment is right. - -::: checkliste -- I can say which programming basics I learned with the mBot2. → What you can do now -- I can find and fix a mistake in a robot program on my own. → Once more: Why doesn't it run? -- I can name the topics that are still waiting for me in VisuStruct. → What is still missing -::: - -## Quiz - -::: quiz -? Do the building blocks from the robot lessons also work for programs without a robot? -→ What you can do now -- [x] Yes, every Java program is built from sequence, branch, loop, variables and methods -- [ ] No, without a robot you need completely different commands -- [ ] Only the loops -> Only the \`mbot.\` commands belong to the robot. Everything else is ordinary Java. - -? Why doesn't \`mbot.getDistance > 15\` run? -→ Once more: Why doesn't it run? -- [ ] Because 15 is too small -- [x] Because the round brackets after the command are missing -- [ ] Because you have to write \`<\` -> Without brackets, Java looks for a variable \`getDistance\` and finds none. - -? Where do you learn how a program catches crashes with try and catch? -→ What is still missing -- [ ] In VisuBot -- [x] In VisuStruct -> That is where the lesson "When the program crashes: try and catch" is. - -? Which topics do you find in VisuStruct? Choose the right list. -→ What is still missing -- [x] Sorting, files and windows with buttons -- [ ] Line sensor, LEDs and motors -- [ ] Only the for loop -> Line sensor, LEDs and motors belong to the mBot2, and you already know the for loop. -::: -`,Lz=`## 🤖 mBot2 commands - -A command tells the mBot2 what to **do**. Every command starts with \`mbot.\` and ends with brackets. Often you write a number in the brackets. - -### 🚗 Driving and turning {.bot-fahren} - -The mBot2 drives straight or turns on the spot. - -\`\`\` -mbot.forward() drives 10 cm forward -mbot.forward(30) drives 30 cm forward -mbot.forward(2, "rotations") the wheels turn twice (≈ 41 cm) -mbot.backward(1, "rotations") one wheel rotation back (≈ 20 cm) -mbot.backward(20) drives 20 cm back -mbot.turnRight() turns to the right (90°) -mbot.turnLeft() turns to the left (90°) -mbot.turnLeft(45) turns just a little to the left (45°) -mbot.turnRight(180) turns all the way round -\`\`\` - -> \`forward\` and \`backward\` are **overloaded**: same name, different parameters. Units are \`"cm"\` and \`"rotations"\`. One wheel rotation is about 20.4 cm. - -> Without a number it drives 10 cm or turns 90°. 90° is a right angle, like the corner of a sheet of paper. - -> For a polygon it turns all the way round once in total: triangle 3 × 120°, square 4 × 90°, hexagon 6 × 60°. - -### ⚙️ Wheels separately {.bot-fahren} - -Here you give each wheel gas yourself. The numbers are percent: 100 is full gas, 0 is standing still, a number with a minus drives backwards. The mBot2 keeps going until another driving command comes. - -\`\`\` -mbot.drive(40, 40) both wheels the same: straight ahead -mbot.drive(10, 40) left slower: curve to the left -mbot.drive(40, -40) one wheel forward, one back: it turns -mbot.stop() stops -\`\`\` - -### 💡 Light {.bot-licht} - -The mBot2 has five LEDs with the numbers 0 to 4. - -\`\`\` -mbot.setLed(255, 0, 0) all LEDs light up red -mbot.ledOff() all LEDs off -mbot.led(0).setColor("green") only LED 0 lights up green -mbot.led(0).setColor("off") only LED 0 off -\`\`\` - -With \`setLed\` you mix red, green and blue, each from 0 (off) to 255 (very bright). - -### 🔊 Sound {.bot-ton} - -\`\`\` -mbot.playTone(440, 300) plays a tone -\`\`\` - -The first number is the pitch. The bigger, the higher. The second number is the length in milliseconds (1000 milliseconds are 1 second). - -### 🖥️ Screen {.bot-anzeige} - -\`\`\` -mbot.showText("Hello") shows Hello on the screen -\`\`\` - -With \`+\` you join text and numbers: \`mbot.showText("Distance: " + mbot.getDistance())\`. - -### 😊 Emotions {.bot-anzeige} - -The mBot2 shows a feeling with light, sound and wiggling. You only see it on the real mBot2. - -\`\`\` -mbot.emotion("smile") laughs -mbot.emotion("sad") is sad -mbot.emotion("wow") is amazed -\`\`\` - -More feelings: hello, yeah, proud, yummy, naughty, uh_oh, hurt, angry, startle, shiver, dizzy, sleepy, zzz, wake_up, blink, yes, no, look_up, look_down, look_left, look_right, look_around - -### ⏱️ Waiting and stopwatch {.bot-warten} - -\`\`\` -mbot.pause(1000) waits 1 second -mbot.resetTimer() sets the stopwatch to 0 -\`\`\` - -You read the stopwatch time with the sensor \`mbot.getTimer()\` (below under **Stopwatch and battery**). - -## 📡 mBot2 sensors - -A sensor **measures** something and gives you a value back. You use the value in a condition, for example in a loop or a branch. Some sensors give a **number**, some **true or false**, one gives a **text**. - -### 📏 Distance {.bot-sensor} - -The ultrasonic sensor at the front measures how far away the next wall is, in centimetres. - -\`\`\` -mbot.getDistance() distance in cm, e.g. 25 -mbot.getDistance() > 15 is there more than 15 cm free ahead? -int distance = mbot.getDistance() remember the distance in a variable -\`\`\` - -### ➖ Line {.bot-sensor} - -Under the mBot2 there are four probes. They see whether the floor is black. - -\`\`\` -mbot.getLine() number: 0 means no probe sees black -mbot.getLine() == 0 no black, keep driving -mbot.getLine() != 0 a probe sees black -mbot.isLineLeft() does the left middle probe see black? -mbot.isLineRight() does the right middle probe see black? -\`\`\` - -### 🎨 Colours {.bot-sensor} - -The same sensor underneath also recognises colours. \`mbot.getColor()\` gives you the name of the colour as text: \`"red"\`, \`"green"\`, \`"blue"\`, \`"yellow"\`, \`"white"\` or \`"black"\`. - -\`\`\` -mbot.getColor().equals("red") is it standing on red? -!mbot.getColor().equals("red") is it not standing on red? -\`\`\` - -> You compare texts with \`.equals(…)\`, not with \`==\`. The \`!\` in front means **not**. - -### 🕹️ Buttons and joystick {.bot-sensor} - -On the CyberPi there are the buttons A and B and a small joystick. Each sensor says **true** while it is being pressed. - -\`\`\` -mbot.isButtonA() is button A pressed? -mbot.isButtonB() is button B pressed? -mbot.isJoystickUp() joystick pushed up? -mbot.isJoystickDown() joystick pushed down? -mbot.isJoystickLeft() joystick pushed left? -mbot.isJoystickRight() joystick pushed right? -\`\`\` - -### 🔆 Light and loudness {.bot-sensor} - -\`\`\` -mbot.getBrightness() how bright? 0 is dark, 100 is bright -mbot.getLoudness() how loud? 0 is silent, 100 is loud -\`\`\` - -### 📐 Tilt and shaking {.bot-sensor} - -\`\`\` -mbot.getPitch() tipped forward or back? (in degrees) -mbot.getRoll() tipped to the side? (in degrees) -mbot.isShaken() is it being shaken? -\`\`\` - -> When the mBot2 lies flat, both tilts are 0. In the simulation you set these values under **🎛 Try the sensors**. - -### 🔋 Stopwatch and battery {.bot-sensor} - -\`\`\` -mbot.getTimer() seconds since the start or since resetTimer() -mbot.getBattery() how full is the battery? (in percent) -\`\`\` - -### Real mBot2 - -With **🔌 mBot2** next to "Start simulation" you connect the real robot with the USB cable. This only works in Chrome or Edge. Then it drives every step of the simulation along, and the sensors measure real values. A second click disconnects it again. - -### Driving without a cable - -With **⬆ Upload** you save your program on the mBot2. Then unplug the cable, put the mBot2 at the start and press **button A**: it drives your program on its own. It stays saved until you upload a new one. Keyboard inputs do not work on the mBot2. -`,Wz=`--- -titel: Losfahren -bereich: Erste Schritte -kurz: Den mBot2 mit Blöcken fahren und drehen lassen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 mit Blöcken fahren lassen -- die Zahl in einem Block ändern -- den mBot2 drehen lassen -::: - -Du baust dein Programm aus **Blöcken**, wie aus Bausteinen. Probier ruhig alles aus. Kaputt machen kannst du nichts. - -## Dein erster Block - -Links unter **Elemente** findest du die Gruppe **🚗 Fahren und Drehen**. Zieh den Block **fahre vor** nach rechts und lass ihn direkt unter **🏁 Wenn das Programm startet** los. - -Im Block steht jetzt \`mbot.forward( )\`. Das ist Englisch: *forward* heißt vorwärts. Ganz rechts unter **mBot2 Live** siehst du sofort, wohin dein mBot2 fährt. - -::: übung vbb-erster-block -titel: Der erste Block -auftrag: Zieh einen Block **fahre vor** unter **🏁 Wenn das Programm startet**. Klick dann auf **Simulation starten** und schau zu. -fahrt: forward -tipp: Die Blöcke liegen links unter **Elemente**, in der Gruppe **🚗 Fahren und Drehen**. -tipp: Lass den Block los, wenn er genau unter dem Block mit der Zielflagge 🏁 hängt. -lösung: vbb-erster-block-loesung.visustruct -::: - -## Weiter fahren - -Ist das weiße Feld im Block leer, fährt der mBot2 nur 10 cm. Das ist eine Kästchenbreite im Gitter. Klick auf das weiße Feld und tipp eine Zahl ein, zum Beispiel **50**. Jetzt fährt er 50 cm. - -::: übung vbb-weiter -titel: 50 cm -auftrag: Lass den mBot2 genau 50 cm vorwärts fahren. Benutze dafür nur **einen** Block. -fahrt: forward 50 -tipp: Klick in das weiße Feld im Block **fahre vor**. -tipp: Tipp **50** ein und drück Enter. Im Block steht dann \`mbot.forward(50)\`. -lösung: vbb-weiter-loesung.visustruct -::: - -Probier aus: Was passiert bei **200**? Und was bei **fahre zurück**? - -## Um die Ecke - -Mit **drehe rechts** dreht sich der mBot2 auf der Stelle. Ist das Feld leer, dreht er sich um 90 Grad, also eine Ecke wie bei einem Blatt Papier. Die Blöcke werden von oben nach unten ausgeführt, einer nach dem anderen. - -::: übung vbb-ecke -titel: Um die Ecke -auftrag: Der mBot2 fährt 50 cm vor, dreht sich nach rechts und fährt dann noch 30 cm. -fahrt: forward 50, turnRight 90, forward 30 -tipp: Du brauchst drei Blöcke: **fahre vor**, **drehe rechts** und noch einmal **fahre vor**. -tipp: Im ersten Block steht 50, im letzten 30. Das Feld von **drehe rechts** bleibt leer. -lösung: vbb-ecke-loesung.visustruct -::: - -Jetzt du: Tipp bei **drehe rechts** mal **180** ein. Wohin schaut der mBot2 danach? - -::: übung vbb-hin-und-zurueck -titel: Hin und zurück -auftrag: Der mBot2 fährt 50 cm vor, dreht sich um und fährt zurück zum Start. -fahrt: forward 50, turnRight 180, forward 50 -tipp: Umdrehen heißt: einmal halb herum, das sind 180 Grad. -tipp: **fahre vor** mit 50, **drehe rechts** mit 180, **fahre vor** mit 50. -lösung: vbb-hin-und-zurueck-loesung.visustruct -::: - -::: ludwig -Zwei Blöcke **drehe rechts** mit leerem Feld hintereinander gehen auch. 90 und 90 sind zusammen 180. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Danach fährt er die Strecke vor. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 2,0 -> 60 cm sind zwei Felder. - -runde: - mbot.forward(30); - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..^.. -ziel: 3,1 -> Ein Feld nach vorn, nach rechts drehen, ein Feld weiter. - -runde: - mbot.turnLeft(); - mbot.forward(60); -karte: ..... | ..... | ..^.. -ziel: 0,2 -> Erst dreht er sich nach links, dann fährt er zwei Felder. - -runde: - mbot.forward(30); - mbot.turnRight(180); - mbot.forward(60); -karte: ..... | ..^.. | ..... -ziel: 2,2 -> Ein Feld vor, umdrehen, und dann zwei Felder zurück. Er landet unter dem Start. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann einen Block ins Programm ziehen und starten. → Dein erster Block -- Ich kann die Zahl in einem Block ändern. → Weiter fahren -- Ich kann den mBot2 um die Ecke fahren lassen. → Um die Ecke -::: -`,Ez=`--- -titel: Immer wieder -bereich: Erste Schritte -kurz: Ein Quadrat fahren und mit „wiederhole“ Blöcke sparen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 ein Quadrat fahren lassen -- mit dem Block „wiederhole“ Blöcke sparen -- eigene Formen ausprobieren -::: - -## Ein Quadrat - -Ein Quadrat hat vier gleich lange Seiten und vier Ecken. Für jede Seite fährt der mBot2 ein Stück vor und dreht sich dann nach rechts. - -::: übung vbb-quadrat -titel: Das Quadrat -auftrag: Lass den mBot2 ein Quadrat mit 50 cm langen Seiten fahren. Am Ende steht er wieder am Start. -fahrt: 4 x (forward 50, turnRight 90) -tipp: Eine Seite sind zwei Blöcke: **fahre vor** mit 50 und **drehe rechts**. -tipp: Diese zwei Blöcke brauchst du viermal untereinander, zusammen also acht Blöcke. -lösung: vbb-quadrat-loesung.visustruct -::: - -Acht Blöcke, und immer wieder dieselben zwei. Das geht auch kürzer! - -## Wiederhole - -In der Gruppe **Steuerung** liegt der Block **wiederhole 10 mal**. Er sieht aus wie ein Maul. Alles, was du hineinlegst, macht der mBot2 so oft, wie die Zahl sagt. So eine Wiederholung heißt beim Programmieren **Schleife**. - -::: übung vbb-quadrat-wiederhole -titel: Quadrat mit wiederhole -auftrag: Baue das Quadrat noch einmal, aber nur mit **wiederhole** und zwei Blöcken darin. -fahrt: 4 x (forward 50, turnRight 90) -braucht: forSchleife -tipp: Zieh **wiederhole 10 mal** unter 🏁. Leg **fahre vor** und **drehe rechts** in das Maul. -tipp: Klick auf die 10 und tipp **4** ein, denn ein Quadrat hat vier Seiten. -lösung: vbb-quadrat-wiederhole-loesung.visustruct -::: - -Aus acht Blöcken sind drei geworden. Starte die Simulation und schau zu, wie der mBot2 viermal dasselbe macht. - -## Neue Formen - -Jetzt bist du dran. Ändere nur die Zahlen in deinem Programm und schau, was passiert. - -::: übung vbb-dreieck -titel: Das Dreieck -auftrag: Lass den mBot2 ein Dreieck mit 50 cm langen Seiten fahren. -fahrt: 3 x (forward 50, turnRight 120) -braucht: forSchleife -tipp: Ein Dreieck hat drei Seiten. Also **wiederhole 3 mal**. -tipp: Bei drei Ecken muss er sich jedes Mal um 120 Grad drehen. Tipp **120** in **drehe rechts**. -lösung: vbb-dreieck-loesung.visustruct -::: - -Noch mehr zum Ausprobieren: - -- **wiederhole 5 mal** und **drehe rechts** mit **72** ergibt ein Fünfeck. -- **wiederhole 6 mal** mit **60** ergibt ein Sechseck. -- Was passiert mit **wiederhole 36 mal**, **fahre vor** mit 10 und **drehe rechts** mit 10? - -::: ludwig -Fällt dir etwas auf? Die Zahl beim Wiederholen mal die Zahl beim Drehen ergibt immer 360. Einmal ganz herum! -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - for (int i = 0; i < 2; i++) { - mbot.forward(30); - } -karte: ..... | ..... | ..^.. -ziel: 2,0 -> Zweimal ein Feld vor, das sind zwei Felder. - -runde: - for (int i = 0; i < 4; i++) { - mbot.forward(30); - mbot.turnRight(); - } -karte: ..... | ..... | ..^.. -ziel: 2,2 -> Das ist ein Quadrat. Am Ende steht er wieder am Start. - -runde: - for (int i = 0; i < 3; i++) { - mbot.forward(30); - } - mbot.turnRight(); - mbot.forward(30); -karte: ..... | ..... | ..... | ..^.. -ziel: 3,0 -> Erst dreimal vor, dann nach rechts drehen und noch ein Feld. Der letzte Block steht nicht mehr im Maul von wiederhole. - -runde: - for (int i = 0; i < 2; i++) { - mbot.forward(30); - mbot.turnLeft(); - } -karte: ..... | ..... | ..^.. -ziel: 1,1 -> Vor, links drehen, vor, links drehen. Er steht links neben dem Feld über dem Start. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 ein Quadrat fahren lassen. → Ein Quadrat -- Ich kann mit „wiederhole“ Blöcke sparen. → Wiederhole -- Ich kann neue Formen ausprobieren. → Neue Formen -::: -`,Nz=`--- -titel: Der mBot2 misst -bereich: Sensoren und Entscheidungen -kurz: Den Abstand zur Wand messen und rechtzeitig anhalten. ---- - -## Das schaffst du heute - -::: kompetenzen -- den Abstand messen und auf dem Bildschirm zeigen -- fahren lassen, solange vorn frei ist -::: - -## Die Augen vorn - -Vorn am mBot2 sitzen zwei runde „Augen“. Das ist der **Ultraschallsensor**. Er schickt ein Geräusch los, das du nicht hören kannst, und wartet auf das Echo. So weiß er, wie weit die nächste Wand weg ist, in Zentimetern. - -::: mbot2 -fokus: ultrasonic -ansicht: vorn -::: - -Links unter **Elemente** findest du weiter unten die **mBot2-Sensoren**. Ein Sensor ist kein eigener Block. Er ist ein runder Baustein, den du in ein weißes Feld legst. - -::: übung vbb-abstand-zeigen -titel: Abstand anzeigen -auftrag: Der mBot2 soll den Abstand zur Wand auf seinem Bildschirm zeigen. Nimm dafür den Block **Text anzeigen** und leg den Sensor **Abstand** in sein weißes Feld. -messwerte: getDistance=35 -> screen "35" -messwerte: getDistance=12 -> screen "12" -tipp: **Text anzeigen** liegt in der Gruppe **🖥️ Bildschirm**, **Abstand** in der Gruppe **📏 Abstand**. -tipp: Zieh **Abstand** genau auf das weiße Feld mit „Hallo“. Dann steht im Block \`mbot.showText(mbot.getDistance())\`. -lösung: vbb-abstand-zeigen-loesung.visustruct -::: - -## Fahren, solange frei ist - -Der mBot2 soll auf eine Wand zufahren und kurz davor anhalten. Wie weit die Wand weg ist, weiß er vorher nicht. Er muss also immer wieder messen. - -Dafür gibt es die **While-Schleife**. Im Block steht oben **solange**. Sie wiederholt ihren Inhalt, solange die Frage oben drin mit **ja** beantwortet wird. So baust du die Frage „Ist mehr als 15 cm frei?“: - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **größer als** auf die Frage hinter **solange**. -3. Leg den Sensor **Abstand** in das linke Feld und tipp rechts **15** ein. -4. Leg **fahre vor** in die Schleife. - -::: übung vbb-bis-wand -titel: Bis zur Wand -auftrag: Der mBot2 fährt vor, solange mehr als 15 cm frei sind. Dein Programm muss in drei Welten klappen, die Wand steht jedes Mal woanders. Über der Simulation kannst du die Welt wechseln. -welt: wand-kurz, wand-mittel, wand-lang -braucht: whileSchleife -tipp: Die Frage der Schleife heißt \`mbot.getDistance() > 15\`. -tipp: In der Schleife liegt nur **fahre vor** mit leerem Feld. So fährt er immer 10 cm und misst dann neu. -lösung: vbb-bis-wand-loesung.visustruct -::: - -Probier aus: Was passiert, wenn du statt 15 eine **5** eintippst? Und was bei **100**? - -::: ludwig -Mit fünfmal **fahre vor** klappt es nur in einer Welt. Mit dem Sensor findet der mBot2 die Wand überall. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..#.. | ..... | ..... | ..^.. -ziel: 2,1 -> Er fährt, solange mehr als 15 cm frei sind. Direkt vor der Wand bleibt er stehen. - -runde: - while (mbot.getDistance() > 40) { - mbot.forward(30); - } -karte: ..#.. | ..... | ..... | ..... | ..^.. -ziel: 2,2 -> Zwei Felder vor der Wand sind schon weniger als 40 cm frei. Dort hält er an. - -runde: - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..... | ..^.# | ..... -ziel: 3,1 -> Erst dreht er sich zur Wand, dann fährt er bis kurz davor. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnLeft(); - mbot.forward(60); -karte: ..#.. | ..... | ..... | ..^.. -ziel: 0,1 -> Bis vor die Wand, dann nach links drehen und zwei Felder weiter. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den Abstand auf dem Bildschirm zeigen. → Die Augen vorn -- Ich kann den mBot2 fahren lassen, solange vorn frei ist. → Fahren, solange frei ist -::: -`,Fz=`--- -titel: Entscheidungen -bereich: Sensoren und Entscheidungen -kurz: Mit „wenn“ und „sonst“ entscheidet der mBot2 selbst, was er tut. ---- - -## Das schaffst du heute - -::: kompetenzen -- mit **wenn** und **sonst** eine Entscheidung bauen -- den mBot2 auf eine Wand reagieren lassen -- den mBot2 auf eine Farbe reagieren lassen -::: - -## Wenn … sonst … - -Du entscheidest jeden Tag: **Wenn** es regnet, nimmst du einen Schirm mit, **sonst** nicht. Der mBot2 kann das auch. - -Zieh eine **Verzweigung** unter 🏁. Oben steht **wenn** und dahinter eine Frage. Darunter kommt alles, was passiert, wenn die Antwort **ja** ist. Bei **sonst** steht, was bei **nein** passiert. Der mBot2 macht immer nur einen der beiden Teile. In Java heißen die beiden übrigens \`if\` und \`else\`. - -::: übung vbb-wand-vorn -titel: Wand vorn? -auftrag: Wenn die Wand näher als 20 cm ist, dreht sich der mBot2 nach rechts. Sonst fährt er vor. Dein Programm wird in zwei Welten geprüft, einmal mit Wand direkt vor ihm und einmal mit viel Platz. -welt: wand-nah -> turnRight -welt: wand-frei -> forward -braucht: verzweigung -tipp: Zieh **kleiner als** aus **Operatoren** auf die Frage bei **wenn**. Leg **Abstand** in das linke Feld und tipp rechts **20** ein. -tipp: Unter **wenn** liegt **drehe rechts**, unter **sonst** liegt **fahre vor**. -lösung: vbb-wand-vorn-loesung.visustruct -::: - -## Rot heißt Stopp - -Unten am mBot2 sitzt ein Farbsensor. Der Baustein **Farbe ist rot?** liefert schon ja oder nein. Du kannst ihn darum direkt auf die Frage bei **wenn** ziehen, ohne **kleiner als**. - -::: übung vbb-ampel -titel: Rote Ampel -auftrag: Steht der mBot2 auf Rot, hält er an. Sonst fährt er ein Stück vor. -messwerte: getColor=rot -> none -messwerte: getColor=grün -> forward -braucht: verzweigung -tipp: **Farbe ist rot?** liegt in der Gruppe **🎨 Farben** bei den Sensoren. -tipp: Unter **wenn** liegt **anhalten**, unter **sonst** liegt **fahre vor**. -lösung: vbb-ampel-loesung.visustruct -::: - -Probier aus: Klick im Block auf „rot“ und tipp **grün** ein. Was macht der mBot2 jetzt? - -::: ludwig -Ich entscheide auch so: Wenn Löwenzahn da ist, fresse ich. Sonst suche ich weiter. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Überleg, ob der mBot2 den wenn-Teil oder den sonst-Teil nimmt, und tipp auf das Feld, auf dem er am Ende steht. Jedes Feld ist 30 cm groß. Vier Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } -karte: ..#.. | ..^.. | ..... -ziel: 2,1 -> Die Wand ist direkt vor ihm. Er nimmt den wenn-Teil, dreht sich nur und bleibt auf seinem Feld. - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } -karte: ..#.. | ..... | ..^.. -ziel: 2,1 -> Die Wand ist noch weit weg. Er nimmt den sonst-Teil und fährt ein Feld vor. - -runde: - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } - mbot.forward(30); -karte: ..#.. | ..^.. | ..... -ziel: 3,1 -> Die Wand ist nah, also dreht er sich nach rechts. Der Block unter der Verzweigung läuft immer, also fährt er danach ein Feld nach rechts. - -runde: - if (mbot.getDistance() > 50) { - mbot.forward(60); - } else { - mbot.turnLeft(); - mbot.forward(30); - } -karte: ..#.. | ..... | ..^.. -ziel: 1,2 -> Bis zur Wand sind es weniger als 50 cm. Also nimmt er den sonst-Teil: links drehen und ein Feld fahren. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann mit wenn und sonst eine Entscheidung bauen. → Wenn … sonst … -- Ich kann den mBot2 auf eine Wand reagieren lassen. → Wenn … sonst … -- Ich kann den mBot2 auf eine Farbe reagieren lassen. → Rot heißt Stopp -::: -`,Rz=`--- -titel: Lichter und Töne -bereich: Licht und Ton -kurz: LEDs in allen Farben leuchten lassen, blinken und Töne spielen. ---- - -## Das schaffst du heute - -::: kompetenzen -- die LEDs in einer Farbe leuchten lassen -- die LEDs blinken lassen -- Töne spielen -::: - -## Licht an - -Hinten auf dem mBot2 sitzt eine Leiste mit fünf bunten **LEDs**. Das sind kleine Lampen. - -::: mbot2 -fokus: leds -ansicht: oben -::: - -Zieh aus der Gruppe **💡 Licht** den Block **LEDs an** unter 🏁. Im Block stehen drei Zahlen: wie viel **Rot**, wie viel **Grün** und wie viel **Blau** die LEDs mischen. Jede Zahl geht von **0** (gar nicht) bis **255** (ganz hell). \`mbot.setLed(255, 0, 0)\` ist also ganz rot. - -::: übung vbb-licht-gruen -titel: Grünes Licht -auftrag: Lass die LEDs grün leuchten. -befehle: setLed 0 255 0 -tipp: Rot und Blau brauchst du nicht, sie bekommen eine 0. -tipp: Im Block steht dann \`mbot.setLed(0, 255, 0)\`. -lösung: vbb-licht-gruen-loesung.visustruct -::: - -Probier aus: Welche Farbe gibt \`255, 255, 0\`? Und \`255, 0, 255\`? Findest du Orange? - -## Blinken - -Ein Blinklicht geht an, wartet, geht aus und wartet wieder. Und das immer wieder. Den Block **kurz warten** findest du in der Gruppe **⏱️ Warten und Stoppuhr**. Die Zahl darin sind **Millisekunden**: 1000 Millisekunden sind eine Sekunde, 500 also eine halbe. - -::: übung vbb-blinken -titel: Blinklicht -auftrag: Die LEDs blinken dreimal rot: eine halbe Sekunde an, eine halbe Sekunde aus. -befehle: 3 x (setLed 255 0 0, pause 500, ledOff, pause 500) -braucht: forSchleife -tipp: Du brauchst **wiederhole 3 mal** und darin vier Blöcke: **LEDs an**, **kurz warten**, **LEDs aus**, **kurz warten**. -tipp: In beiden Blöcken **kurz warten** steht **500**. -lösung: vbb-blinken-loesung.visustruct -::: - -::: ludwig -Ohne **kurz warten** geht das Licht so schnell an und aus, dass du gar nichts siehst. Der mBot2 ist viel schneller als deine Augen! -::: - -## Töne - -Mit **Ton spielen** aus der Gruppe **🔊 Ton** piept der mBot2. Die erste Zahl sagt, wie **hoch** der Ton ist: 200 ist tief, 1000 ist hoch. Die zweite Zahl sagt, wie **lange** er klingt, wieder in Millisekunden. - -::: übung vbb-sirene -titel: Sirene -auftrag: Baue eine Sirene: dreimal hintereinander ein hoher Ton mit 800 und ein tiefer mit 600, jeder 300 Millisekunden lang. -befehle: 3 x (playTone 800 300, playTone 600 300) -braucht: forSchleife -tipp: Leg zwei Blöcke **Ton spielen** in **wiederhole 3 mal**. -tipp: Im ersten steht \`mbot.playTone(800, 300)\`, im zweiten \`mbot.playTone(600, 300)\`. -lösung: vbb-sirene-loesung.visustruct -::: - -Jetzt du: Bau eine **Lichtshow**. Lass die LEDs abwechselnd in verschiedenen Farben leuchten und spiel bei jeder Farbe einen anderen Ton. Je mehr Farben, desto besser! - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Fünf Runden. - -runde: - mbot.setLed(0, 0, 255); -frage: Welche Farbe leuchtet? -- rot -- grün -- [x] blau -> Rot 0, Grün 0, Blau 255: nur Blau leuchtet. - -runde: - mbot.setLed(255, 255, 0); -frage: Welche Farbe leuchtet? -- [x] gelb -- lila -- weiß -> Rot und Grün zusammen ergeben bei Licht Gelb. - -runde: - for (int i = 0; i < 4; i++) { - mbot.playTone(440, 300); - } -frage: Wie oft piept der mBot2? -- 1-mal -- 3-mal -- [x] 4-mal -> Der Block **Ton spielen** liegt in **wiederhole 4 mal**. - -runde: - for (int i = 0; i < 2; i++) { - mbot.setLed(255, 0, 0); - mbot.pause(500); - mbot.ledOff(); - mbot.pause(500); - } -frage: Wie lange dauert das Programm? -- eine Sekunde -- [x] zwei Sekunden -- vier Sekunden -> Jede Runde dauert 500 + 500 Millisekunden, also eine Sekunde. Zwei Runden sind zwei Sekunden. - -runde: - mbot.playTone(1000, 200); - mbot.playTone(200, 200); -frage: Wie klingt das? -- erst tief, dann hoch -- [x] erst hoch, dann tief -- zweimal gleich -> 1000 ist ein hoher Ton, 200 ein tiefer. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann die LEDs in einer Farbe leuchten lassen. → Licht an -- Ich kann die LEDs blinken lassen. → Blinken -- Ich kann Töne spielen. → Töne -::: -`,Jz=`--- -titel: Immer wieder prüfen -bereich: Sensoren und Entscheidungen -kurz: Auf Taste A warten, immer wieder prüfen und Hindernissen ausweichen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 auf Taste A warten lassen -- eine Schleife mit „wiederhole immer“ bauen und wieder verlassen -- den mBot2 Hindernissen ausweichen lassen -::: - -## Start mit Taste A - -Oben auf dem mBot2 sitzt die Taste **A**, das ist die [Taste mit dem Quadrat]{teil=buttons}. Der mBot2 soll erst losfahren, wenn du sie drückst. Bis dahin wartet er. - -Warten heißt: **solange** die Taste **nicht** gedrückt ist, kurz warten. So baust du das: - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **nicht** auf die Frage hinter **solange**. -3. Zieh den Sensor **Taste A gedrückt?** in den Baustein **nicht**. -4. Leg **kurz warten** in die Schleife und **fahre vor** darunter. - -::: übung vbb-start-a -titel: Start mit Taste A -auftrag: Der mBot2 wartet, bis Taste A gedrückt ist. Dann fährt er 30 cm vor. -messwerte: isButtonA=false/true -> forward 30 -messwerte: isButtonA=true -> forward 30 -braucht: whileSchleife -tipp: Im Kopf der Schleife steht **solange nicht** und dann \`mbot.isButtonA()\`. -tipp: In der Schleife liegt **kurz warten**, unter der Schleife **fahre vor** mit 30. -lösung: vbb-start-a-loesung.visustruct -::: - -> Beim Prüfen drückt VisuBot die Taste für dich: erst eine Weile nicht, dann schon. Fährt dein mBot2 zu früh los, fällt das auf. - -## Wiederhole immer - -Manche Programme sollen nie aufhören: Der mBot2 fährt, schaut, fährt, schaut … Dafür gibt es die **Endlosschleife**. Im Block steht **wiederhole immer**. - -Damit du ihn trotzdem anhalten kannst, baust du eine **Notbremse** ein: **Wenn** Taste A gedrückt ist, **verlasse die Schleife**. Den Block dafür findest du unter **Elemente**, er heißt **Aussprung**. Danach macht das Programm unter der Schleife weiter. - -::: übung vbb-notbremse -titel: Notbremse -auftrag: Der mBot2 fährt immer weiter vor. Wird Taste A gedrückt, verlässt er die Schleife und zeigt **Gestoppt** auf dem Bildschirm. -messwerte: isButtonA=true -> none, screen "Gestoppt" -messwerte: isButtonA=false/true -> drives, screen "Gestoppt" -braucht: endlosschleife, aussprung -tipp: In **wiederhole immer** liegt zuerst eine Verzweigung mit **Taste A gedrückt?**. Unter **wenn** kommt der **Aussprung**, **sonst** bleibt leer. -tipp: Unter der Verzweigung, noch in der Schleife, liegt **fahre vor**. Unter der Schleife liegt **Text anzeigen** mit „Gestoppt“. -lösung: vbb-notbremse-loesung.visustruct -::: - -## Ausweichen - -Jetzt wird es spannend! Der mBot2 soll durch einen Raum fahren, ohne irgendwo anzustoßen. Baue dafür: - -- **wiederhole immer** -- darin: **wenn** der Abstand kleiner als 20 ist, **drehe rechts**, **sonst** **fahre vor** - -Starte die Simulation und schau zu, wie er sich seinen Weg sucht. Mit **Simulation beenden** hältst du ihn an. - -Probier aus: - -- Was passiert, wenn du statt **drehe rechts** mal **drehe links** nimmst? -- Was passiert bei **drehe rechts** mit **45**? -- Kannst du ihn bei jeder Drehung piepen lassen? - -::: ludwig -So einen Roboter gibt es wirklich: Viele Staubsauger-Roboter fahren genau so durch die Wohnung. Wand vorn? Drehen! Frei? Weiter! -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Vier Runden. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(50); -frage: Niemand drückt eine Taste. Was tut der mBot2? -- Er fährt 50 cm. -- [x] Er wartet und wartet. -- Er fährt immer weiter. -> Solange Taste A nicht gedrückt ist, wartet er in der Schleife. - -runde: - while (!mbot.isButtonA()) { - mbot.pause(100); - } - mbot.forward(50); -frage: Du drückst Taste A. Was tut der mBot2? -- [x] Er fährt 50 cm und hört dann auf. -- Er fährt immer weiter. -- Er wartet weiter. -> Jetzt ist die Schleife zu Ende, und der Block darunter läuft einmal. - -runde: - while (true) { - mbot.playTone(440, 300); - } -frage: Wie oft piept der mBot2? -- einmal -- zehnmal -- [x] immer weiter, ohne Ende -> **wiederhole immer** hört nie von selbst auf. - -runde: - while (true) { - if (mbot.isButtonA()) { - break; - } - mbot.forward(); - } - mbot.setLed(255, 0, 0); -frage: Wann leuchten die LEDs rot? -- sofort -- [x] wenn jemand Taste A drückt -- nie -> Erst die Notbremse verlässt die Schleife. Dann geht es unter der Schleife weiter. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 auf Taste A warten lassen. → Start mit Taste A -- Ich kann eine Schleife mit „wiederhole immer“ bauen und wieder verlassen. → Wiederhole immer -- Ich kann den mBot2 Hindernissen ausweichen lassen. → Ausweichen -::: -`,Gz=`--- -titel: Der Linie folgen -bereich: Sensoren und Entscheidungen -kurz: Mit dem Liniensensor am Rand umkehren und einer schwarzen Linie folgen. ---- - -## Das schaffst du heute - -::: kompetenzen -- mit dem Liniensensor eine schwarze Linie erkennen -- den mBot2 am Rand umkehren lassen -- den mBot2 einer Linie folgen lassen -::: - -## Augen nach unten - -Unter dem mBot2 sitzt vorn der **Liniensensor**. Er leuchtet auf den Boden und sieht, ob es dort hell oder dunkel ist. - -::: mbot2 -fokus: lineSensor -ansicht: unten -::: - -Der Sensor **Linie** aus der Gruppe **➖ Linie** liefert **0**, wenn unter dem mBot2 alles hell ist. Sieht er etwas Schwarzes, ist die Zahl größer als 0. - -## Am Rand umkehren - -Stell dir einen Tisch vor, um den schwarzes Klebeband klebt. Der mBot2 soll vorfahren, bis er das Band sieht, und dann umkehren. So fällt er nicht herunter. - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh aus **Operatoren** den Baustein **gleich** auf die Frage hinter **solange**. -3. Leg den Sensor **Linie** in das linke Feld und tipp rechts **0** ein. -4. Leg **fahre vor** in die Schleife und **drehe rechts** mit **180** darunter. - -::: übung vbb-rand -titel: Am Rand umkehren -auftrag: Der mBot2 fährt vor, solange er keine Linie sieht. Dann dreht er sich um. Es gibt zwei Welten, der Rand ist mal näher und mal weiter weg. -welt: rand-1 -> forward 20, turnRight 180 -welt: rand-2 -> forward 50, turnRight 180 -braucht: whileSchleife -tipp: Die Frage der Schleife heißt \`mbot.getLine() == 0\`. -tipp: **drehe rechts** mit 180 liegt unter der Schleife, nicht darin. -lösung: vbb-rand-loesung.visustruct -::: - -## Der Linie folgen - -Jetzt kommt ein Klassiker: Der mBot2 fährt eine schwarze Linie entlang, auch durch Kurven. Dafür brauchst du zwei neue Dinge: - -- **Linie links?** aus der Gruppe **➖ Linie** sagt ja, wenn der linke Fühler Schwarz sieht. -- **Räder einzeln** aus der Gruppe **⚙️ Räder einzeln** lässt jedes Rad anders schnell drehen. Die erste Zahl ist das linke Rad, die zweite das rechte. Dreht das rechte schneller, fährt er eine Linkskurve. - -Die Idee ist ganz einfach: **Wenn** links Schwarz ist, lenkt er nach links. **Sonst** lenkt er nach rechts. So wackelt er an der Linie entlang. - -::: übung vbb-linienfolger -titel: Linienfolger -auftrag: Solange die Wand mehr als 15 cm weg ist, folgt der mBot2 der Linie. Danach hält er an. In der einen Welt biegt die Linie nach rechts ab, in der anderen nach links. -welt: linie-1, linie-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit \`mbot.getDistance() > 15\`. Darin eine Verzweigung mit **Linie links?**. Unter der Schleife liegt **anhalten**. -tipp: Unter **wenn** liegt **Räder einzeln** mit \`10, 40\`, unter **sonst** **Räder einzeln** mit \`40, 10\`. -lösung: vbb-linienfolger-loesung.visustruct -::: - -Probier aus: Was passiert mit \`20, 60\` und \`60, 20\`? Wird er schneller oder fliegt er aus der Kurve? - -::: ludwig -Am echten mBot2 klebst du die Linie mit schwarzem Isolierband auf einen hellen Boden. Fang langsam an, sonst schießt er in der Kurve über die Linie hinaus. -::: - -## Spiel: Was passiert? - -::: spiel frage -titel: Spiel: Was passiert? -intro: Schau dir die Blöcke an. Was macht der mBot2? Vier Runden. - -runde: - mbot.drive(40, 40); -frage: Wie fährt der mBot2? -- [x] geradeaus -- Linkskurve -- Rechtskurve -> Beide Räder drehen gleich schnell. - -runde: - mbot.drive(10, 40); -frage: Wie fährt der mBot2? -- geradeaus -- [x] Linkskurve -- Rechtskurve -> Das rechte Rad ist schneller, also schiebt es ihn nach links. - -runde: - if (mbot.isLineLeft()) { - mbot.drive(10, 40); - } else { - mbot.drive(40, 10); - } -frage: Nur der rechte Fühler sieht die Linie. Wie fährt er? -- Linkskurve -- [x] Rechtskurve -- Er hält an. -> Links ist kein Schwarz, also nimmt er den sonst-Teil: das linke Rad ist schneller. - -runde: - while (mbot.getLine() == 0) { - mbot.forward(); - } - mbot.turnRight(180); -frage: Der mBot2 steht schon auf der schwarzen Linie. Was tut er? -- Er fährt erst ein Stück vor. -- [x] Er dreht sich sofort um. -- Er fährt immer weiter. -> Die Frage der Schleife ist gleich am Anfang nein, also läuft sie gar nicht. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann mit dem Liniensensor eine Linie erkennen. → Augen nach unten -- Ich kann den mBot2 am Rand umkehren lassen. → Am Rand umkehren -- Ich kann den mBot2 einer Linie folgen lassen. → Der Linie folgen -::: -`,Hz=`--- -titel: Parcours -bereich: Sensoren und Entscheidungen -kurz: Alles zusammen: der Linie bis Rot folgen, Klötzen ausweichen und einen eigenen Parcours bauen. ---- - -## Das schaffst du heute - -::: kompetenzen -- den mBot2 einer Linie bis zum roten Feld folgen lassen -- den mBot2 vor einem Klotz warnen und ausweichen lassen -- einen eigenen Parcours planen und Stück für Stück bauen -::: - -## Station 1: Bis zum roten Feld - -Ein Parcours besteht aus Stationen. Bei der ersten folgt der mBot2 einer schwarzen Linie. Am Ende der Linie liegt ein rotes Feld, dort soll er anhalten. - -Du kennst schon alles dafür. Den Linienfolger hast du in der letzten Lektion gebaut. Neu ist nur die Frage hinter **solange**: Er fährt, solange er **nicht** auf Rot steht. - -1. Zieh eine **While-Schleife** unter 🏁. -2. Zieh **nicht** aus **Operatoren** auf die Frage und leg **Farbe ist rot?** hinein. -3. Leg in die Schleife eine **Verzweigung** mit **Linie links?**. -4. Unter **wenn** kommt **Räder einzeln** mit \`10, 40\`, unter **sonst** mit \`40, 10\`. -5. Unter die Schleife legst du **anhalten**. - -::: übung vbb-linie-bis-rot -titel: Bis zum roten Feld -auftrag: Der mBot2 folgt der Linie, solange er nicht auf Rot steht. Dann hält er an. In der zweiten Welt schlängelt sich die Linie. -welt: linie-rot-1, linie-rot-2 -braucht: whileSchleife, verzweigung -tipp: Die Frage der Schleife heißt \`!mbot.getColor().equals("rot")\`. Im Block steht dort **nicht Farbe ist rot?**. -tipp: In der Schleife liegt dieselbe Verzweigung wie beim Linienfolger aus Lektion 7. -lösung: vbb-linie-bis-rot-loesung.visustruct -::: - -## Station 2: Achtung, Klotz! - -Bei der zweiten Station stehen Klötze im Weg. Der mBot2 fährt in kleinen Schritten vor. Ist ein Klotz näher als 20 cm, warnt er mit oranger Lampe und Piepton und fährt außen herum. Danach geht die Lampe aus. - -Außen herum heißt: **drehe rechts**, **fahre vor** mit 30, **drehe links**. Dann steht er neben dem Klotz und schaut wieder nach vorn. - -::: übung vbb-klotz -titel: Achtung, Klotz! -auftrag: Solange der mBot2 nicht auf Rot steht: Ist ein Klotz näher als 20 cm, leuchtet er orange, piept und weicht aus. Sonst fährt er ein Stück vor. Es gibt zwei Welten, in der zweiten stehen zwei Klötze. -welt: hindernis-1 -> tone -welt: hindernis-2 -> tone -braucht: whileSchleife, verzweigung -tipp: Außen dieselbe Schleife wie bei Station 1. Darin eine Verzweigung mit **Abstand** **kleiner als** 20. Unter **sonst** liegt **fahre vor**. -tipp: Unter **wenn**: **LEDs an** mit \`255, 128, 0\`, **Ton spielen** mit \`880, 200\`, **drehe rechts**, **fahre vor** mit 30, **drehe links**, **LEDs aus**. -lösung: vbb-klotz-loesung.visustruct -::: - -## Dein eigener Parcours - -Jetzt bist du dran! Bau mit Klebeband und Kartons einen Parcours auf dem Boden. Plane erst, dann programmier Station für Station: - -- **Start:** Der mBot2 wartet, bis du Taste A drückst. -- **Linie:** Er folgt einer schwarzen Linie bis zu einem roten Blatt Papier. -- **Klotz:** Danach weicht er einem Karton aus. -- **Ziel:** Am Ende blinkt er grün und spielt eine Melodie. - -Probier jede Station erst allein aus. Klappt sie, hängst du die nächste unten an. So findest du Fehler viel schneller. - -::: ludwig -Profis machen das genauso: Sie testen jedes kleine Stück, bevor sie alles zusammenstecken. Bei Robotik-Wettbewerben gewinnt oft nicht der schnellste Roboter, sondern der, der zuverlässig ins Ziel kommt. -::: - -## Spiel: Wo landet der mBot2? - -::: spiel fahrt -titel: Spiel: Wo landet der mBot2? -intro: Die grauen Felder sind Wände. Schau dir die Blöcke an und tipp auf das Feld, auf dem der mBot2 am Ende steht. Jedes Feld ist 30 cm groß. Drei Runden. -frage: Auf welchem Feld steht der mBot2 am Ende? - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: #.... | ..... | ^...# -ziel: 4,1 -> Er fährt bis vor die Wand, dreht sich nach rechts und fährt wieder, bis der Rand kommt. - -runde: - for (int i = 0; i < 3; i++) { - if (mbot.getDistance() < 20) { - mbot.turnRight(); - } else { - mbot.forward(30); - } - } -karte: ..#.. | ..... | ..^.. -ziel: 3,1 -> Erst ist frei, er fährt ein Feld. Dann ist die Wand direkt vor ihm, er dreht sich. Beim dritten Mal ist wieder frei, er fährt nach rechts. - -runde: - while (mbot.getDistance() > 15) { - mbot.forward(30); - } - mbot.turnLeft(); - mbot.forward(30); - mbot.turnRight(); - while (mbot.getDistance() > 15) { - mbot.forward(30); - } -karte: ..... | ..#.. | ..... | ..^.. -ziel: 1,0 -> Er fährt bis vor den Klotz, weicht nach links aus und fährt dann geradeaus weiter bis zum Rand. -::: - -## Das hab ich geschafft - -Wie gut klappt es schon? ☂️ noch nicht, ⛅ ein bisschen, ☀️ klappt prima. Bei ☂️ und ⛅ bringt dich **Nachlesen** zum passenden Schritt. - -::: checkliste -- Ich kann den mBot2 einer Linie bis zum roten Feld folgen lassen. → Station 1: Bis zum roten Feld -- Ich kann den mBot2 vor einem Klotz warnen und ausweichen lassen. → Station 2: Achtung, Klotz! -- Ich kann einen eigenen Parcours Station für Station bauen. → Dein eigener Parcours -::: -`,Vz=`--- -titel: Ausgang finden -nach: 8 -stufe: 3 ---- - -Der mBot2 steht unten links und schaut nach rechts. Über ihm liegen Wände. Jede Wand hat genau eine Lücke, und jede Lücke liegt weiter rechts als die darunter. Ganz oben ist das schwarze Ziel. - -Der mBot2 fährt nach rechts und schaut in jedem Feld nach oben. Ist dort eine Lücke, fährt er hindurch in den nächsten Gang. - -::: übung vbb-ausgang -titel: Ausgang finden -auftrag: Der mBot2 fährt in beiden Welten durch alle Lücken bis zum Ziel, ohne anzustoßen. Ein Feld ist 30 cm groß. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit **Linie** **gleich** 0. Darin dreht sich der mBot2 zuerst nach links und schaut: Ist oben frei, misst er mehr als 20 cm. -tipp: **Wenn** frei: 60 vor (durch die Lücke in den nächsten Gang), dann rechts drehen. **Sonst**: rechts drehen und 30 vor. -lösung: vbb-ausgang-loesung.visustruct -::: -`,qz=`--- -titel: Disco -nach: 5 -stufe: 1 ---- - -Der mBot2 macht Party! Seine LEDs wechseln die Farbe: rot, grün, blau, immer wieder. - -::: übung vbb-disco -titel: Disco -auftrag: Die LEDs leuchten nacheinander rot, grün und blau, jede Farbe eine halbe Sekunde lang. Das Ganze dreimal. Danach gehen die LEDs aus. -befehle: 3 x (setLed 255 0 0, pause 500, setLed 0 255 0, pause 500, setLed 0 0 255, pause 500), ledOff -braucht: forSchleife -tipp: In **wiederhole 3 mal** liegen drei Blöcke **LEDs an**, nach jedem ein **kurz warten** mit 500. -tipp: Rot ist \`255, 0, 0\`, grün \`0, 255, 0\`, blau \`0, 0, 255\`. **LEDs aus** liegt unter der Schleife. -lösung: vbb-disco-loesung.visustruct -::: - -Probier aus: Mach eine eigene Farbe, zum Beispiel lila mit \`255, 0, 255\`. -`,Uz=`--- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Vor dem mBot2 stehen vier Pylonen in einer Reihe. Er fährt Slalom: am ersten Pylon oben vorbei, am zweiten unten, dann wieder oben und unten. Hinter dem letzten Pylon ist das Ziel. - -Ein Feld ist 30 cm groß, so breit wie der mBot2. Ein Schritt von Feld zu Feld ist also **fahre vor** mit 30. - -::: übung vbb-slalom -titel: Slalom -auftrag: Fahre den Slalom bis ins Ziel, ohne einen Pylon zu berühren. Benutze **wiederhole**. -welt: slalom -braucht: forSchleife -tipp: Fahr erst nur oben um den ersten Pylon herum: links drehen, ein Feld vor, rechts drehen, zwei Felder vor, rechts drehen, ein Feld vor, links drehen. -tipp: Unten herum geht genauso, nur mit links und rechts vertauscht. „Oben herum, dann unten herum“ kommt in **wiederhole 2 mal**. -lösung: vbb-slalom-loesung.visustruct -::: -`,Qz=`--- -titel: Kleeblatt im Wald -nach: 7 -stufe: 3 ---- - -Ganz hinten im Wald liegt ein Kleeblatt auf einem schwarzen Feld. Auf dem Weg dorthin stehen Bäume, aber nie zwei direkt hintereinander. Steht ein Baum vor dem mBot2, fährt er außen herum und dann weiter geradeaus. - -Ein Feld ist 30 cm groß. Steht ein Baum im nächsten Feld, misst der Abstandssensor weniger als 20 cm. - -::: übung vbb-wald -titel: Kleeblatt im Wald -auftrag: Der mBot2 fährt in beiden Welten bis zum schwarzen Feld und umfährt dabei jeden Baum, ohne anzustoßen. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit **Linie** **gleich** 0, so wie beim Rand in Lektion 7. Darin eine Verzweigung: **wenn** **Abstand** **kleiner als** 20, **sonst** **fahre vor** mit 30. -tipp: Um einen Baum herum: links drehen, 30 vor, rechts drehen, 60 vor, rechts drehen, 30 vor, links drehen. -lösung: vbb-wald-loesung.visustruct -::: -`,$z=`--- -titel: Die Alarmanlage -nach: 16 -stufe: 2 ---- - -Der mBot2 bewacht dein Zimmer. Kommt jemand näher als 20 cm **oder** wird es laut (Lautstärke über 60), schlägt er Alarm: Die LEDs leuchten rot und er piept. Sonst sind die LEDs aus. Mit Taste B schaltest du die Alarmanlage ab. - -::: übung vb-alarmanlage -titel: Die Alarmanlage -auftrag: Solange Taste B nicht gedrückt ist, prüft der mBot2 immer wieder: Ist der Abstand unter 20 cm oder die Lautstärke über 60, leuchten die LEDs rot und er spielt einen Ton. Sonst gehen die LEDs aus. Danach wartet er kurz. -messwerte: getDistance=10, isButtonB=false/true -> none, tone -messwerte: getLoudness=80, isButtonB=false/true -> none, tone -messwerte: getDistance=100, getLoudness=0, isButtonB=false/true -> none, noTone, ledOff -braucht: whileSchleife, verzweigung -tipp: Außen steht eine While-Schleife mit \`!mbot.isButtonB()\`. Darin eine Verzweigung und danach **kurz warten**. -tipp: Die Bedingung der Verzweigung: \`mbot.getDistance() < 20 || mbot.getLoudness() > 60\`. Im true-Zweig \`mbot.setLed(255, 0, 0)\` und \`mbot.playTone(880, 200)\`, im false-Zweig \`mbot.ledOff()\`. -lösung: vb-alarmanlage-loesung.visustruct -::: -`,Yz=`--- -titel: Labyrinth mit Ausgängen -nach: 5 -stufe: 3 ---- - -Der mBot2 steht unten links und schaut nach rechts. Über ihm liegen Wandreihen. Jede hat genau eine Lücke, und jede Lücke liegt weiter rechts als die darunter. Ganz oben ist das Ziel. - -Der mBot2 fährt nach rechts und schaut in jedem Feld nach oben. Ist dort eine Lücke, fährt er hindurch in den nächsten Gang. - -::: übung vb-ausgang -titel: Labyrinth mit Ausgängen -auftrag: Programmiere den mBot2 so, dass er in beiden Welten durch alle Lücken bis zum Ziel fährt, ohne anzustoßen. Ein Feld ist 30 cm groß. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: In jedem Durchlauf dreht sich der mBot2 zuerst nach links und misst. Ist oben frei, sind es mehr als 20 cm. -tipp: Durch die Lücke sind es zwei Felder bis in den nächsten Gang. Danach soll er wieder nach rechts schauen. Ist oben eine Wand, dreht er sich zurück und fährt ein Feld weiter. -lösung: vb-ausgang-loesung.visustruct -::: -`,Kz=`--- -titel: Binär zählen -nach: 15 -stufe: 3 ---- - -Computer rechnen mit nur zwei Ziffern: 0 und 1. Das heißt **binär**. Mit drei LEDs kannst du die Zahlen 0 bis 7 binär zeigen: LED 0 zählt 1, LED 1 zählt 2 und LED 2 zählt 4. Die Zahl 5 ist 4 + 1, also leuchten LED 2 und LED 0. - -| Zahl | LED 2 (4) | LED 1 (2) | LED 0 (1) | -|---|---|---|---| -| 0 | aus | aus | aus | -| 1 | aus | aus | an | -| 2 | aus | an | aus | -| 3 | aus | an | an | -| 5 | an | aus | an | - -::: übung vb-binaer-zaehlen -titel: Binär zählen -auftrag: Eine For-Schleife zählt \`zahl\` von 0 bis 7. Für jede Zahl leuchtet jede der LEDs 0, 1 und 2 grün oder ist aus, genau wie in der Tabelle. Danach wartet der mBot2 1000 Millisekunden. Schalte die LEDs immer in der Reihenfolge 0, 1, 2. -befehle: ledColor 0 "aus", ledColor 1 "aus", ledColor 2 "aus", pause 1000, ledColor 0 "grün", ledColor 1 "aus", ledColor 2 "aus", pause 1000, ledColor 0 "aus", ledColor 1 "grün", ledColor 2 "aus", pause 1000, ledColor 0 "grün", ledColor 1 "grün", ledColor 2 "aus", pause 1000, ledColor 0 "aus", ledColor 1 "aus", ledColor 2 "grün", pause 1000, ledColor 0 "grün", ledColor 1 "aus", ledColor 2 "grün", pause 1000, ledColor 0 "aus", ledColor 1 "grün", ledColor 2 "grün", pause 1000, ledColor 0 "grün", ledColor 1 "grün", ledColor 2 "grün", pause 1000 -braucht: forSchleife, verzweigung -tipp: Ob LED 0 an ist, verrät der Rest beim Teilen durch 2: \`zahl % 2 == 1\`. Für LED 1 teilst du erst durch 2: \`zahl / 2 % 2 == 1\`. Für LED 2 durch 4: \`zahl / 4 % 2 == 1\`. -tipp: In der Schleife stehen drei Verzweigungen und danach **kurz warten**. Im true-Zweig zum Beispiel \`mbot.led(0).setColor("grün")\`, im false-Zweig \`mbot.led(0).setColor("aus")\`. -lösung: vb-binaer-zaehlen-loesung.visustruct -::: -`,Xz=`--- -titel: Das Dreieck -nach: 2 -stufe: 1 ---- - -Beim Quadrat dreht sich der mBot2 viermal um 90°. Zusammen sind das 360°, eine ganze Runde. Bei einem Dreieck dreht er sich auch einmal ganz herum, aber nur in drei Ecken. - -In die Klammer von **drehe rechts** kannst du schreiben, wie weit er sich drehen soll: \`mbot.turnRight(45)\` dreht nur 45°. - -::: übung vb-dreieck -titel: Das Dreieck -auftrag: Lass den mBot2 ein Dreieck mit drei gleich langen Seiten fahren. Jede Seite ist 30 cm lang. Benutze eine Schleife. -fahrt: 3 x (forward 30, turnRight 120) -braucht: forSchleife -tipp: Wie viel Grad muss eine Ecke sein, damit drei Ecken zusammen eine ganze Runde (360°) ergeben? -tipp: In der Schleife fährt der mBot2 eine Seite und dreht sich dann um 120°. -lösung: vb-dreieck-loesung.visustruct -::: - -::: angeberwissen -Die Idee, mit einem fahrenden Roboter Figuren zu zeichnen, ist über 50 Jahre alt: Ende der 1960er-Jahre entstand die Sprache **Logo**. Kinder steuerten damit eine „Schildkröte“, die mit einem Stift über das Papier fuhr. -::: -`,e6=`--- -titel: Rückwärts einparken -nach: 16 -stufe: 2 ---- - -Rechts von der Straße parken Autos (die Klötze). Irgendwo ist eine Lücke, und genau davor klebt eine blaue Markierung auf der Straße. Der mBot2 sucht die Lücke und parkt rückwärts ein, wie ein Auto. - -Hinten hat er keinen Sensor. Er schaut deshalb nach vorn auf die Wand gegenüber: Je weiter er rückwärts in die Lücke fährt, desto größer wird der Abstand dorthin. - -::: übung vb-einparken -titel: Rückwärts einparken -auftrag: Der mBot2 fährt in 10-cm-Schritten vor, bis er auf Blau steht. Dann dreht er sich nach links, sodass sein Heck zur Lücke zeigt. Er fährt rückwärts, solange der Abstand zur Wand vor ihm unter 45 cm ist. Zum Schluss leuchten die LEDs grün und er zeigt **Geparkt!**. Es gibt zwei Straßen mit verschiedenen Lücken. -welt: parken-1 -> ledOn, screen "Geparkt!" -welt: parken-2 -> ledOn, screen "Geparkt!" -braucht: whileSchleife -tipp: Zwei While-Schleifen hintereinander: erst \`!mbot.getColor().equals("blau")\` mit \`mbot.forward()\` darin, dann \`mbot.getDistance() < 45\` mit \`mbot.backward()\` darin. Dazwischen steht \`mbot.turnLeft()\`. -tipp: Am Ende: \`mbot.setLed(0, 255, 0)\` und \`mbot.showText("Geparkt!")\`. -lösung: vb-einparken-loesung.visustruct -::: -`,t6='---\ntitel: Hindernis ausweichen\nnach: 16\nstufe: 2\n---\n\nAuf dem Weg zum roten Feld stehen Klötze im Weg. Der mBot2 fährt in 10-cm-Schritten vor. Taucht ein Klotz auf (näher als 20 cm), warnt er mit Licht und Ton und fährt außen herum: nach rechts drehen, 30 cm vor, nach links drehen. Dann geht es weiter geradeaus.\n\n::: übung vb-hindernis\ntitel: Hindernis ausweichen\nauftrag: Der mBot2 fährt, solange er nicht auf Rot steht. Ist ein Klotz näher als 20 cm, leuchten die LEDs orange (`mbot.setLed(255, 128, 0)`), er piept und weicht aus. Danach gehen die LEDs aus. Sonst fährt er 10 cm vor. Es gibt zwei Welten.\nwelt: hindernis-1 -> tone\nwelt: hindernis-2 -> tone\nbraucht: whileSchleife, verzweigung\ntipp: Außen eine While-Schleife mit `!mbot.getColor().equals("rot")`, darin eine Verzweigung mit `mbot.getDistance() < 20`.\ntipp: Im true-Zweig: `mbot.setLed(255, 128, 0)`, `mbot.playTone(880, 200)`, `mbot.turnRight()`, `mbot.forward(30)`, `mbot.turnLeft()`, `mbot.ledOff()`. Im false-Zweig: `mbot.forward()`.\nlösung: vb-hindernis-loesung.visustruct\n:::\n',n6=`--- -titel: Knight Rider -nach: 15 -stufe: 2 ---- - -In der Fernsehserie „Knight Rider“ hat das sprechende Auto KITT vorn ein rotes Licht, das immer hin und her läuft. Das baust du mit den fünf LEDs nach. - -::: übung vb-knight-rider -titel: Knight Rider -auftrag: Ein rotes Licht läuft von LED 0 bis LED 4 und wieder zurück bis LED 1. Jede LED leuchtet 100 Millisekunden und geht dann aus. Benutze zwei For-Schleifen: eine zählt hoch, eine zählt runter. -befehle: ledColor 0 "rot", pause 100, ledColor 0 "aus", ledColor 1 "rot", pause 100, ledColor 1 "aus", ledColor 2 "rot", pause 100, ledColor 2 "aus", ledColor 3 "rot", pause 100, ledColor 3 "aus", ledColor 4 "rot", pause 100, ledColor 4 "aus", ledColor 3 "rot", pause 100, ledColor 3 "aus", ledColor 2 "rot", pause 100, ledColor 2 "aus", ledColor 1 "rot", pause 100, ledColor 1 "aus" -braucht: forSchleife -tipp: Die erste Schleife zählt \`i\` von 0 bis 4 hoch. Die zweite startet bei 3 und zählt mit \`i--\` runter, solange \`i > 0\`. -tipp: In beiden Schleifen: \`mbot.led(i).setColor("rot")\`, \`mbot.pause(100)\`, \`mbot.led(i).setColor("aus")\`. -lösung: vb-knight-rider-loesung.visustruct -::: -`,i6=`--- -titel: Das Labyrinth -nach: 5 -stufe: 2 ---- - -Der mBot2 steht in einem Labyrinth. Das Ziel ist ein schwarzes Feld mit grüner Fahne. Die Gänge sind 30 cm breit. - -Eine einfache Regel bringt ihn hindurch: **Ist vorn frei, fahr vor. Sonst dreh dich nach rechts. Ist dort auch eine Wand, dreh dich ganz um.** - -Woran merkt der mBot2, dass er im Ziel ist? Der Liniensensor unten sieht das schwarze Feld: \`mbot.getLine()\` ist dann nicht mehr \`0\`. - -::: übung vb-labyrinth -titel: Das Labyrinth -auftrag: Programmiere den mBot2 so, dass er in beiden Labyrinthen ins Ziel findet, ohne anzustoßen. -welt: labyrinth-1, labyrinth-2 -braucht: whileSchleife, verzweigung -tipp: Außen eine While-Schleife mit der Bedingung \`mbot.getLine() == 0\`. Sie läuft, bis der mBot2 auf dem Ziel steht. -tipp: In der Schleife eine Verzweigung \`mbot.getDistance() > 15\`: Ja → \`mbot.forward()\`. Nein → \`mbot.turnRight()\`, danach noch eine Verzweigung \`mbot.getDistance() < 15\` mit \`mbot.turnRight(180)\`. -lösung: vb-labyrinth-loesung.visustruct -::: - -::: angeberwissen -Mit der Regel „Hand immer an der Wand“ findet man aus jedem Labyrinth, dessen Wände alle zusammenhängen. Schon die Griechen erzählten von Theseus, der sich im Labyrinth des Minotaurus mit einem Wollfaden half. -::: -`,a6='---\ntitel: Linienfolger mit Stopp\nnach: 20\nstufe: 3\n---\n\nDer mBot2 folgt einer schwarzen Linie und hält am roten Feld an ihrem Ende an.\n\nDafür gibt er mit **Räder einzeln** Gas: `mbot.drive(40, 40)` lässt beide Räder gleich schnell drehen, er fährt geradeaus weiter, bis ein anderer Fahrbefehl kommt. `mbot.drive(10, 40)` macht eine Linkskurve, `mbot.drive(40, 10)` eine Rechtskurve. `mbot.stop()` hält ihn an.\n\nDie beiden mittleren Fühler des Liniensensors fragst du mit `mbot.isLineLeft()` und `mbot.isLineRight()` ab. Sehen beide die Linie, fährt er geradeaus. Sieht nur der linke sie, lenkt er nach links, sieht nur der rechte sie, nach rechts.\n\n::: übung vb-linienfolger-stopp\ntitel: Linienfolger mit Stopp\nauftrag: Solange der mBot2 nicht auf Rot steht, folgt er der Linie. Danach hält er an. In der zweiten Welt schlängelt sich die Linie.\nwelt: linie-rot-1, linie-rot-2\nbraucht: whileSchleife, verzweigung\ntipp: Außen eine While-Schleife mit `!mbot.getColor().equals("rot")`, danach **anhalten**.\ntipp: In der Schleife: Sind `mbot.isLineLeft() && mbot.isLineRight()`, dann `mbot.drive(40, 40)`. Sonst, wenn `mbot.isLineLeft()`, dann `mbot.drive(10, 40)`. Sonst, wenn `mbot.isLineRight()`, dann `mbot.drive(40, 10)`.\nlösung: vb-linienfolger-stopp-loesung.visustruct\n:::\n',s6=`--- -titel: Morsen -nach: 15 -stufe: 2 ---- - -Früher haben Schiffe mit Licht Nachrichten geschickt, im **Morsecode**: Jeder Buchstabe besteht aus kurzen und langen Signalen. Der bekannteste Notruf ist **SOS**: S ist dreimal kurz, O ist dreimal lang. - -::: übung vb-morsen -titel: SOS morsen -auftrag: LED 2 morst SOS in Weiß. Kurz heißt: 200 Millisekunden an, dann 200 aus. Lang heißt: 600 Millisekunden an, dann 200 aus. Zwischen den Buchstaben wartet der mBot2 zusätzlich 400 Millisekunden. Benutze für jeden Buchstaben eine Schleife. -befehle: 3 x (ledColor 2 "weiß", pause 200, ledColor 2 "aus", pause 200), pause 400, 3 x (ledColor 2 "weiß", pause 600, ledColor 2 "aus", pause 200), pause 400, 3 x (ledColor 2 "weiß", pause 200, ledColor 2 "aus", pause 200) -braucht: forSchleife -tipp: Drei For-Schleifen hintereinander, jede läuft dreimal. Dazwischen steht jeweils **kurz warten** mit 400. -tipp: In der ersten und dritten Schleife: \`mbot.led(2).setColor("weiß")\`, \`mbot.pause(200)\`, \`mbot.led(2).setColor("aus")\`, \`mbot.pause(200)\`. In der mittleren wartet er nach dem Einschalten 600. -lösung: vb-morsen-loesung.visustruct -::: -`,r6=`--- -titel: Der Nachtwächter -nach: 18 -stufe: 1 ---- - -Der mBot2 ist ein Nachtwächter. Solange es dunkel ist (Helligkeit unter 20), läuft er Streife: 30 cm vor und dann umdrehen. Sobald es hell wird, ist seine Schicht vorbei: Er zeigt **Zzz** auf dem Bildschirm und schläft. - -::: übung vb-nachtwaechter -titel: Der Nachtwächter -auftrag: Solange die Helligkeit unter 20 ist, fährt der mBot2 30 cm vor und dreht sich um 180°. Danach zeigt er **Zzz** auf dem Bildschirm. -messwerte: getBrightness=5/80 -> drives, screen "Zzz" -messwerte: getBrightness=80 -> none, screen "Zzz" -braucht: whileSchleife -tipp: Eine While-Schleife mit \`mbot.getBrightness() < 20\`. Darin \`mbot.forward(30)\` und \`mbot.turnRight(180)\`. -tipp: Nach der Schleife steht \`mbot.showText("Zzz")\`. -lösung: vb-nachtwaechter-loesung.visustruct -::: -`,o6=`--- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Vor dem mBot2 stehen vier Pylonen in einer Reihe. Er fährt Slalom: am ersten Pylon oben vorbei, am zweiten unten, dann wieder oben und unten. Hinter dem letzten Pylon ist das Ziel. - -In dieser Welt ist ein Feld 30 cm groß, so breit wie der mBot2. Ein Schritt von Feld zu Feld ist also \`mbot.forward(30)\`. - -::: übung vb-slalom -titel: Slalom -auftrag: Fahre den Slalom bis ins Ziel, ohne einen Pylon zu berühren. Benutze eine Schleife. -welt: slalom -braucht: forSchleife -tipp: Überlege zuerst, wie der mBot2 oben um einen einzigen Pylon herumfährt: Er steht danach zwei Felder weiter in derselben Reihe und schaut wieder nach rechts. -tipp: Unten herum geht genauso, nur mit links und rechts vertauscht. „Oben herum, dann unten herum“ wiederholt sich zweimal. -lösung: vb-slalom-loesung.visustruct -::: -`,l6=`--- -titel: Die Spirale -nach: 2 -stufe: 2 ---- - -Der mBot2 soll eine eckige Spirale fahren: Die erste Seite ist 10 cm lang, die zweite 20 cm, die dritte 30 cm und so weiter. Nach jeder Seite dreht er sich nach rechts. - -Die Zählvariable einer For-Schleife kannst du auch in der Schleife benutzen, zum Beispiel in der Klammer von **fahre vor**. Dort darf auch eine Rechnung stehen, etwa \`mbot.forward(i * 5)\`. - -::: übung vb-spirale -titel: Die Spirale -auftrag: Lass den mBot2 eine Spirale mit 8 Seiten fahren: 10 cm, 20 cm, 30 cm … bis 80 cm. Nach jeder Seite dreht er sich nach rechts. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight, forward 50, turnRight, forward 60, turnRight, forward 70, turnRight, forward 80, turnRight -braucht: forSchleife -tipp: Lass die Zählvariable von 1 bis 8 laufen. Wie lang ist dann die Seite in jedem Durchlauf? -tipp: In der Klammer von **fahre vor** steht eine Rechnung mit der Zählvariable. -lösung: vb-spirale-loesung.visustruct -::: -`,u6=`--- -titel: Die Treppe -nach: 2 -stufe: 1 ---- - -Der mBot2 steigt eine Treppe hinauf, von oben gesehen: Jede Stufe ist 20 cm tief und 20 cm breit. - -::: übung vb-treppe -titel: Die Treppe -auftrag: Lass den mBot2 drei Stufen fahren. Eine Stufe: 20 cm vor, nach links drehen, 20 cm vor, nach rechts drehen. Benutze eine Schleife. -fahrt: 3 x (forward 20, turnLeft, forward 20, turnRight) -braucht: forSchleife -tipp: Schreib zuerst eine Stufe auf. Dann packst du sie in eine For-Schleife, die dreimal läuft. -tipp: In der Schleife: \`mbot.forward(20)\`, \`mbot.turnLeft()\`, \`mbot.forward(20)\`, \`mbot.turnRight()\`. -lösung: vb-treppe-loesung.visustruct -::: -`,m6=`--- -titel: Der Tunnelsucher -nach: 5 -stufe: 3 ---- - -Der mBot2 soll im ersten **Tunnelfeld** anhalten: Das ist ein Feld mit einer Wand links **und** einer Wand rechts. Felder mit nur einer Wand zählen nicht. In der Simulation ist das richtige Feld hellgrün, der Liniensensor sieht es aber nicht. - -Der Ultraschallsensor schaut nur nach vorn. Um nach links zu schauen, dreht sich der mBot2 nach links, misst und dreht sich wieder zurück. - -Den Messwert merkst du dir in einer **Variable**, zum Beispiel in einer Anweisung \`links = mbot.getDistance()\`. Am Anfang legst du sie an: \`int links = 99\`. - -::: übung vb-tunnel -titel: Der Tunnelsucher -auftrag: Programmiere den mBot2 so, dass er in beiden Welten im ersten Tunnelfeld anhält. Ein Feld ist 30 cm groß. -welt: tunnel-1, tunnel-2 -braucht: doUntilSchleife -tipp: In jedem Durchlauf fährt der mBot2 ein Feld vor und schaut sich dann nach links und nach rechts um. Merk dir beide Abstände in Variablen. Am Ende schaut er wieder nach vorn. -tipp: Hier passt eine Schleife, die erst fährt und danach prüft. Sie läuft weiter, solange links **oder** rechts mindestens 20 cm frei ist: \`links >= 20 || rechts >= 20\`. -lösung: vb-tunnel-loesung.visustruct -::: -`,c6=`--- -titel: Kleeblatt im Wald -nach: 5 -stufe: 2 ---- - -Das Ziel liegt geradeaus. Auf dem Weg stehen Bäume, aber nie zwei direkt hintereinander. Steht ein Baum vor dem mBot2, fährt er außen herum und dann weiter geradeaus. - -Ein Feld ist 30 cm groß. Steht ein Baum im nächsten Feld, misst der Ultraschallsensor weniger als 20 cm. - -::: übung vb-wald -titel: Kleeblatt im Wald -auftrag: Der mBot2 soll in beiden Welten bis zum Ziel fahren und dabei jeden Baum umfahren, ohne anzustoßen. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: Wie bei „Fahre bis zum Ziel“ läuft außen eine Schleife, bis der Liniensensor das Ziel sieht. In jedem Durchlauf gibt es zwei Möglichkeiten: Baum vorn oder frei. -tipp: Um einen Baum herum: nach links drehen, ein Feld vor, nach rechts drehen, zwei Felder vor, nach rechts drehen, ein Feld vor, nach links drehen. -lösung: vb-wald-loesung.visustruct -::: -`,d6=`--- -titel: The alarm system -nach: 16 -stufe: 2 ---- - -The mBot2 guards your room. If someone comes closer than 20 cm **or** it gets loud (loudness above 60), it raises the alarm: the LEDs light up red and it beeps. Otherwise the LEDs are off. With button B you switch the alarm system off. - -::: übung vb-alarmanlage -titel: The alarm system -auftrag: As long as button B is not pressed, the mBot2 checks again and again: if the distance is below 20 cm or the loudness above 60, the LEDs light up red and it plays a tone. Otherwise the LEDs go off. Then it waits a moment. -messwerte: getDistance=10, isButtonB=false/true -> none, tone -messwerte: getLoudness=80, isButtonB=false/true -> none, tone -messwerte: getDistance=100, getLoudness=0, isButtonB=false/true -> none, noTone, ledOff -braucht: whileSchleife, verzweigung -tipp: On the outside is a while loop with \`!mbot.isButtonB()\`. Inside a branch and after it **wait a moment**. -tipp: The condition of the branch: \`mbot.getDistance() < 20 || mbot.getLoudness() > 60\`. In the true branch \`mbot.setLed(255, 0, 0)\` and \`mbot.playTone(880, 200)\`, in the false branch \`mbot.ledOff()\`. -lösung: vb-alarmanlage-loesung.visustruct -::: -`,h6=`--- -titel: Maze with exits -nach: 5 -stufe: 3 ---- - -The mBot2 stands at the bottom left and faces right. Above it are rows of walls. Each has exactly one gap, and each gap lies further right than the one below. The goal is at the very top. - -The mBot2 drives to the right and looks up in every square. If there is a gap, it drives through into the next corridor. - -::: übung vb-ausgang -titel: Maze with exits -auftrag: Program the mBot2 so that in both worlds it drives through all gaps to the goal without bumping into anything. One square is 30 cm. -welt: ausgang-1, ausgang-2 -braucht: whileSchleife, verzweigung -tipp: In each pass the mBot2 first turns left and measures. If the way up is free, it is more than 20 cm. -tipp: Through the gap it is two squares into the next corridor. Afterwards it should face right again. If there is a wall above, it turns back and drives one square further. -lösung: vb-ausgang-loesung.visustruct -::: -`,g6=`--- -titel: Counting in binary -nach: 15 -stufe: 3 ---- - -Computers calculate with only two digits: 0 and 1. That is called **binary**. With three LEDs you can show the numbers 0 to 7 in binary: LED 0 counts 1, LED 1 counts 2 and LED 2 counts 4. The number 5 is 4 + 1, so LED 2 and LED 0 light up. - -| Number | LED 2 (4) | LED 1 (2) | LED 0 (1) | -|---|---|---|---| -| 0 | off | off | off | -| 1 | off | off | on | -| 2 | off | on | off | -| 3 | off | on | on | -| 5 | on | off | on | - -::: übung vb-binaer-zaehlen -titel: Counting in binary -auftrag: A for loop counts \`number\` from 0 to 7. For each number, each of the LEDs 0, 1 and 2 lights up green or is off, exactly as in the table. Then the mBot2 waits 1000 milliseconds. Always switch the LEDs in the order 0, 1, 2. -befehle: ledColor 0 "off", ledColor 1 "off", ledColor 2 "off", pause 1000, ledColor 0 "green", ledColor 1 "off", ledColor 2 "off", pause 1000, ledColor 0 "off", ledColor 1 "green", ledColor 2 "off", pause 1000, ledColor 0 "green", ledColor 1 "green", ledColor 2 "off", pause 1000, ledColor 0 "off", ledColor 1 "off", ledColor 2 "green", pause 1000, ledColor 0 "green", ledColor 1 "off", ledColor 2 "green", pause 1000, ledColor 0 "off", ledColor 1 "green", ledColor 2 "green", pause 1000, ledColor 0 "green", ledColor 1 "green", ledColor 2 "green", pause 1000 -braucht: forSchleife, verzweigung -tipp: Whether LED 0 is on is given by the remainder when dividing by 2: \`number % 2 == 1\`. For LED 1 you first divide by 2: \`number / 2 % 2 == 1\`. For LED 2 by 4: \`number / 4 % 2 == 1\`. -tipp: The loop contains three branches and then **wait a moment**. In the true branch, for example, \`mbot.led(0).setColor("green")\`, in the false branch \`mbot.led(0).setColor("off")\`. -lösung: vb-binaer-zaehlen-loesung.visustruct -::: -`,p6=`--- -titel: The triangle -nach: 2 -stufe: 1 ---- - -For the square the mBot2 turns four times by 90°. Together that is 360°, one full turn. For a triangle it also turns all the way around once, but in only three corners. - -Inside the brackets of **turn right** you can write how far it should turn: \`mbot.turnRight(45)\` turns only 45°. - -::: übung vb-dreieck -titel: The triangle -auftrag: Let the mBot2 drive a triangle with three sides of equal length. Each side is 30 cm long. Use a loop. -fahrt: 3 x (forward 30, turnRight 120) -braucht: forSchleife -tipp: How many degrees must one corner be so that three corners add up to a full turn (360°)? -tipp: In the loop the mBot2 drives one side and then turns by 120°. -lösung: vb-dreieck-loesung.visustruct -::: - -::: angeberwissen -The idea of drawing shapes with a driving robot is more than 50 years old: the language **Logo** was created in the late 1960s. Children used it to steer a "turtle" that rolled across paper with a pen. -::: -`,f6=`--- -titel: Reverse parking -nach: 16 -stufe: 2 ---- - -To the right of the road there are parked cars (the blocks). Somewhere there is a gap, and right in front of it a blue marking is stuck on the road. The mBot2 looks for the gap and reverses into it, like a car. - -It has no sensor at the back. So it looks forward at the wall opposite: the further it reverses into the gap, the bigger the distance to that wall gets. - -::: übung vb-einparken -titel: Reverse parking -auftrag: The mBot2 drives forward in steps of 10 cm until it is on blue. Then it turns left, so its rear points to the gap. It reverses as long as the distance to the wall in front of it is below 45 cm. Finally the LEDs light up green and it shows **Parked!**. There are two roads with different gaps. -welt: parken-1 -> ledOn, screen "Parked!" -welt: parken-2 -> ledOn, screen "Parked!" -braucht: whileSchleife -tipp: Two while loops one after the other: first \`!mbot.getColor().equals("blue")\` with \`mbot.forward()\` inside, then \`mbot.getDistance() < 45\` with \`mbot.backward()\` inside. In between is \`mbot.turnLeft()\`. -tipp: At the end: \`mbot.setLed(0, 255, 0)\` and \`mbot.showText("Parked!")\`. -lösung: vb-einparken-loesung.visustruct -::: -`,b6='---\ntitel: Avoid obstacles\nnach: 16\nstufe: 2\n---\n\nOn the way to the red field there are blocks in the way. The mBot2 drives forward in steps of 10 cm. If a block shows up (closer than 20 cm), it warns with light and sound and drives around it: turn right, 30 cm forward, turn left. Then it carries on straight ahead.\n\n::: übung vb-hindernis\ntitel: Avoid obstacles\nauftrag: The mBot2 drives as long as it is not on red. If a block is closer than 20 cm, the LEDs light up orange (`mbot.setLed(255, 128, 0)`), it beeps and drives around it. Then the LEDs go off. Otherwise it drives forward 10 cm. There are two worlds.\nwelt: hindernis-1 -> tone\nwelt: hindernis-2 -> tone\nbraucht: whileSchleife, verzweigung\ntipp: On the outside a while loop with `!mbot.getColor().equals("red")`, inside a branch with `mbot.getDistance() < 20`.\ntipp: In the true branch: `mbot.setLed(255, 128, 0)`, `mbot.playTone(880, 200)`, `mbot.turnRight()`, `mbot.forward(30)`, `mbot.turnLeft()`, `mbot.ledOff()`. In the false branch: `mbot.forward()`.\nlösung: vb-hindernis-loesung.visustruct\n:::\n',x6=`--- -titel: Knight Rider -nach: 15 -stufe: 2 ---- - -In the TV series “Knight Rider”, the talking car KITT has a red light at the front that keeps running back and forth. You rebuild it with the five LEDs. - -::: übung vb-knight-rider -titel: Knight Rider -auftrag: A red light runs from LED 0 to LED 4 and back to LED 1. Each LED lights up for 100 milliseconds and then goes off. Use two for loops: one counts up, one counts down. -befehle: ledColor 0 "red", pause 100, ledColor 0 "off", ledColor 1 "red", pause 100, ledColor 1 "off", ledColor 2 "red", pause 100, ledColor 2 "off", ledColor 3 "red", pause 100, ledColor 3 "off", ledColor 4 "red", pause 100, ledColor 4 "off", ledColor 3 "red", pause 100, ledColor 3 "off", ledColor 2 "red", pause 100, ledColor 2 "off", ledColor 1 "red", pause 100, ledColor 1 "off" -braucht: forSchleife -tipp: The first loop counts \`i\` up from 0 to 4. The second starts at 3 and counts down with \`i--\` as long as \`i > 0\`. -tipp: In both loops: \`mbot.led(i).setColor("red")\`, \`mbot.pause(100)\`, \`mbot.led(i).setColor("off")\`. -lösung: vb-knight-rider-loesung.visustruct -::: -`,v6=`--- -titel: The maze -nach: 5 -stufe: 2 ---- - -The mBot2 is standing in a maze. The goal is a black square with a green flag. The corridors are 30 cm wide. - -A simple rule gets it through: **If the way ahead is free, drive forward. Otherwise turn right. If there is a wall there too, turn all the way around.** - -How does the mBot2 notice that it has reached the goal? The line sensor underneath sees the black square: \`mbot.getLine()\` is then no longer \`0\`. - -::: übung vb-labyrinth -titel: The maze -auftrag: Program the mBot2 so that it finds the goal in both mazes without bumping into a wall. -welt: labyrinth-1, labyrinth-2 -braucht: whileSchleife, verzweigung -tipp: On the outside a while loop with the condition \`mbot.getLine() == 0\`. It runs until the mBot2 is on the goal. -tipp: Inside the loop a branch \`mbot.getDistance() > 15\`: yes → \`mbot.forward()\`. No → \`mbot.turnRight()\`, then another branch \`mbot.getDistance() < 15\` with \`mbot.turnRight(180)\`. -lösung: vb-labyrinth-loesung.visustruct -::: - -::: angeberwissen -With the rule "always keep one hand on the wall" you get out of any maze whose walls are all connected. The ancient Greeks already told of Theseus, who found his way through the Minotaur's labyrinth with a ball of thread. -::: -`,w6='---\ntitel: Line follower with stop\nnach: 20\nstufe: 3\n---\n\nThe mBot2 follows a black line and stops at the red field at its end.\n\nFor this it steps on the gas with **wheels separately**: `mbot.drive(40, 40)` turns both wheels equally fast, it keeps driving straight ahead until another driving command comes. `mbot.drive(10, 40)` makes a left curve, `mbot.drive(40, 10)` a right curve. `mbot.stop()` stops it.\n\nYou ask the two middle probes of the line sensor with `mbot.isLineLeft()` and `mbot.isLineRight()`. If both see the line, it drives straight ahead. If only the left one sees it, it steers left, if only the right one sees it, right.\n\n::: übung vb-linienfolger-stopp\ntitel: Line follower with stop\nauftrag: As long as the mBot2 is not on red, it follows the line. Then it stops. In the second world the line winds about.\nwelt: linie-rot-1, linie-rot-2\nbraucht: whileSchleife, verzweigung\ntipp: On the outside a while loop with `!mbot.getColor().equals("red")`, after it **stop**.\ntipp: Inside the loop: if `mbot.isLineLeft() && mbot.isLineRight()`, then `mbot.drive(40, 40)`. Otherwise, if `mbot.isLineLeft()`, then `mbot.drive(10, 40)`. Otherwise, if `mbot.isLineRight()`, then `mbot.drive(40, 10)`.\nlösung: vb-linienfolger-stopp-loesung.visustruct\n:::\n',y6=`--- -titel: Morse code -nach: 15 -stufe: 2 ---- - -Ships used to send messages with light, in **Morse code**: every letter is made of short and long signals. The best-known distress call is **SOS**: S is three short, O is three long. - -::: übung vb-morsen -titel: Morse SOS -auftrag: LED 2 sends SOS in white. Short means: on for 200 milliseconds, then off for 200. Long means: on for 600 milliseconds, then off for 200. Between the letters the mBot2 waits another 400 milliseconds. Use a loop for each letter. -befehle: 3 x (ledColor 2 "white", pause 200, ledColor 2 "off", pause 200), pause 400, 3 x (ledColor 2 "white", pause 600, ledColor 2 "off", pause 200), pause 400, 3 x (ledColor 2 "white", pause 200, ledColor 2 "off", pause 200) -braucht: forSchleife -tipp: Three for loops one after the other, each runs three times. Between them there is **wait a moment** with 400. -tipp: In the first and third loop: \`mbot.led(2).setColor("white")\`, \`mbot.pause(200)\`, \`mbot.led(2).setColor("off")\`, \`mbot.pause(200)\`. In the middle one it waits 600 after switching on. -lösung: vb-morsen-loesung.visustruct -::: -`,z6=`--- -titel: The night watchman -nach: 18 -stufe: 1 ---- - -The mBot2 is a night watchman. As long as it is dark (brightness below 20), it goes on patrol: 30 cm forward and then turn around. As soon as it gets light, its shift is over: it shows **Zzz** on its screen and sleeps. - -::: übung vb-nachtwaechter -titel: The night watchman -auftrag: As long as the brightness is below 20, the mBot2 drives forward 30 cm and turns by 180°. Then it shows **Zzz** on its screen. -messwerte: getBrightness=5/80 -> drives, screen "Zzz" -messwerte: getBrightness=80 -> none, screen "Zzz" -braucht: whileSchleife -tipp: A while loop with \`mbot.getBrightness() < 20\`. Inside \`mbot.forward(30)\` and \`mbot.turnRight(180)\`. -tipp: After the loop is \`mbot.showText("Zzz")\`. -lösung: vb-nachtwaechter-loesung.visustruct -::: -`,I6=`--- -titel: Slalom -nach: 2 -stufe: 2 ---- - -Four cones stand in a row in front of the mBot2. It drives a slalom: past the first cone on the top side, past the second on the bottom side, then top and bottom again. Behind the last cone is the goal. - -In this world one square is 30 cm, as wide as the mBot2. One step from square to square is therefore \`mbot.forward(30)\`. - -::: übung vb-slalom -titel: Slalom -auftrag: Drive the slalom to the goal without touching a cone. Use a loop. -welt: slalom -braucht: forSchleife -tipp: First think about how the mBot2 drives around a single cone on the top side: afterwards it is two squares further along in the same row and faces right again. -tipp: Around the bottom works the same way, only with left and right swapped. "Top, then bottom" repeats twice. -lösung: vb-slalom-loesung.visustruct -::: -`,k6=`--- -titel: The spiral -nach: 2 -stufe: 2 ---- - -The mBot2 should drive a square spiral: the first side is 10 cm long, the second 20 cm, the third 30 cm and so on. After each side it turns right. - -You can also use the counter variable of a for loop inside the loop, for example in the brackets of **drive forward**. A calculation is allowed there too, such as \`mbot.forward(i * 5)\`. - -::: übung vb-spirale -titel: The spiral -auftrag: Let the mBot2 drive a spiral with 8 sides: 10 cm, 20 cm, 30 cm … up to 80 cm. After each side it turns right. -fahrt: forward 10, turnRight, forward 20, turnRight, forward 30, turnRight, forward 40, turnRight, forward 50, turnRight, forward 60, turnRight, forward 70, turnRight, forward 80, turnRight -braucht: forSchleife -tipp: Let the counter variable run from 1 to 8. How long is the side in each pass then? -tipp: The brackets of **drive forward** contain a calculation with the counter variable. -lösung: vb-spirale-loesung.visustruct -::: -`,S6=`--- -titel: The staircase -nach: 2 -stufe: 1 ---- - -The mBot2 climbs a staircase, seen from above: each step is 20 cm deep and 20 cm wide. - -::: übung vb-treppe -titel: The staircase -auftrag: Let the mBot2 drive three steps. One step: 20 cm forward, turn left, 20 cm forward, turn right. Use a loop. -fahrt: 3 x (forward 20, turnLeft, forward 20, turnRight) -braucht: forSchleife -tipp: Write down one step first. Then put it into a for loop that runs three times. -tipp: Inside the loop: \`mbot.forward(20)\`, \`mbot.turnLeft()\`, \`mbot.forward(20)\`, \`mbot.turnRight()\`. -lösung: vb-treppe-loesung.visustruct -::: -`,T6=`--- -titel: The tunnel finder -nach: 5 -stufe: 3 ---- - -The mBot2 should stop in the first **tunnel square**: a square with a wall on the left **and** a wall on the right. Squares with only one wall don't count. In the simulation the right square is light green, but the line sensor can't see it. - -The ultrasonic sensor only looks forward. To look left, the mBot2 turns left, measures and turns back again. - -You remember the measured value in a **variable**, for example with a statement \`left = mbot.getDistance()\`. At the start you create it: \`int left = 99\`. - -::: übung vb-tunnel -titel: The tunnel finder -auftrag: Program the mBot2 so that it stops in the first tunnel square in both worlds. One square is 30 cm. -welt: tunnel-1, tunnel-2 -braucht: doUntilSchleife -tipp: In each pass the mBot2 drives one square forward and then looks left and right. Remember both distances in variables. At the end it faces forward again. -tipp: A loop that drives first and checks afterwards fits here. It keeps going as long as at least 20 cm are free on the left **or** on the right: \`left >= 20 || right >= 20\`. -lösung: vb-tunnel-loesung.visustruct -::: -`,_6=`--- -titel: Clover in the forest -nach: 5 -stufe: 2 ---- - -The goal is straight ahead. There are trees on the way, but never two directly behind each other. If a tree stands in front of the mBot2, it drives around it and then continues straight ahead. - -One square is 30 cm. If there is a tree in the next square, the ultrasonic sensor measures less than 20 cm. - -::: übung vb-wald -titel: Clover in the forest -auftrag: In both worlds the mBot2 should drive to the goal and go around every tree without bumping into it. -welt: wald-1, wald-2 -braucht: whileSchleife, verzweigung -tipp: As in "Drive to the goal", a loop on the outside runs until the line sensor sees the goal. In each pass there are two possibilities: tree ahead or free. -tipp: Around a tree: turn left, one square forward, turn right, two squares forward, turn right, one square forward, turn left. -lösung: vb-wald-loesung.visustruct -::: -`,A6=` - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;34; - - - 112;114;117;101;102;101;65;108;116;101;114;40;41; - - - 111;117;116;112;117;116;58;32;34;65;117;102;32;87;105;101;100;101;114;115;101;104;101;110;34; - - -`,j6=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;118;111;108;108;106;228;104;114;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;100;101;114;106;228;104;114;105;103;34; - - - - -`,C6=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;98;97;108;108;119;117;114;102;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 100;111;117;98;108;101;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;98;101;115;116;101;87;101;105;116;101;32;61;32;48; - - - 83;116;114;105;110;103;32;98;101;115;116;101;114;78;97;109;101;32;61;32;34;34; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;119;101;105;116;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 97;110;122;97;104;108;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;119;101;105;116;101; - - - 119;101;105;116;101;32;62;32;98;101;115;116;101;87;101;105;116;101; - - - 98;101;115;116;101;87;101;105;116;101;32;61;32;119;101;105;116;101; - - - 98;101;115;116;101;114;78;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;115;99;104;110;105;116;116;32;61;32;115;117;109;109;101;32;47;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;84;101;105;108;110;101;104;109;101;114;58;32;34;32;43;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;83;99;104;110;105;116;116;58;32;34;32;43;32;115;99;104;110;105;116;116;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;105;101;103;101;114;46;116;120;116;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;83;105;101;103;101;114;58;32;34;32;43;32;98;101;115;116;101;114;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;101;87;101;105;116;101;32;43;32;34;32;109;41;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - -`,D6=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;98;97;108;108;119;117;114;102;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,B6=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;80;117;110;107;116;101;63;34; - - - 112;117;110;107;116;101;91;105;93;32;61;32;122;97;104;108; - - - - - 105;110;116;32;100;117;114;99;104;103;97;110;103;32;61;32;49; - 100;117;114;99;104;103;97;110;103;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 100;117;114;99;104;103;97;110;103;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104;32;45;32;100;117;114;99;104;103;97;110;103; - 105;43;43; - - - 112;117;110;107;116;101;91;105;93;32;60;32;112;117;110;107;116;101;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;112;117;110;107;116;101;91;105;93; - - - 112;117;110;107;116;101;91;105;93;32;61;32;112;117;110;107;116;101;91;105;32;43;32;49;93; - - - 112;117;110;107;116;101;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;112;108;97;116;122;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;112;108;97;116;122;32;43;32;34;46;32;34;32;43;32;112;117;110;107;116;101;91;105;93; - - - - -`,P6=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;71;97;110;122;101;32;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;32;98;105;110;97;101;114;32;61;32;34;34; - - - 122;97;104;108;32;62;32;48; - - - 105;110;116;32;114;101;115;116;32;61;32;122;97;104;108;32;37;32;50; - - - 98;105;110;97;101;114;32;61;32;114;101;115;116;32;43;32;98;105;110;97;101;114; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;101;114; - - -`,O6=` - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;101;108;99;104;101;115;32;87;111;114;116;32;40;110;117;114;32;71;114;111;223;98;117;99;104;115;116;97;98;101;110;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;103;101;104;101;105;109;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;112;104;97;98;101;116;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;112;104;97;98;101;116;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 103;101;104;101;105;109;32;61;32;103;101;104;101;105;109;32;43;32;97;108;112;104;97;98;101;116;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;103;101;104;101;105;109; - - -`,M6=` - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - -`,Z6=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;101;108;99;104;101;115;32;87;111;114;116;32;40;110;117;114;32;71;114;111;223;98;117;99;104;115;116;97;98;101;110;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;103;101;104;101;105;109;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 103;101;104;101;105;109;32;61;32;103;101;104;101;105;109;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;103;101;104;101;105;109; - - -`,L6=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;103;101;104;101;105;109;32;34;87;101;108;99;104;101;114;32;71;101;104;101;105;109;116;101;120;116;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;85;109;32;119;105;101;32;118;105;101;108;101;32;83;116;101;108;108;101;110;32;118;101;114;115;99;104;105;101;98;101;110;63;34; - - - 83;116;114;105;110;103;32;107;108;97;114;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;103;101;104;101;105;109;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;103;101;104;101;105;109;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 107;108;97;114;116;101;120;116;32;61;32;107;108;97;114;116;101;120;116;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;107;108;97;114;116;101;120;116; - - -`,W6=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 122;97;104;108;32;33;61;32;49; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 115;99;104;114;105;116;116;101;43;43; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101; - - -`,E6=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;78;111;116;101;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;97;110;122;97;104;108; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;32;101;105;110;103;101;98;101;110;34; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;110;111;116;101; - - - - - 100;111;117;98;108;101;32;115;99;104;110;105;116;116;32;61;32;115;117;109;109;101;32;42;32;49;46;48;32;47;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;115;99;104;110;105;116;116; - - -`,N6=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;90;97;104;108;101;110;63;34; - - - 105;110;116;32;122;97;104;108;49;32;61;32;49; - - - 105;110;116;32;122;97;104;108;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;97;110;122;97;104;108; - 105;43;43; - - - 105;110;116;32;122;97;104;108;51;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108;51; - - - 122;97;104;108;49;32;61;32;122;97;104;108;50; - - - 122;97;104;108;50;32;61;32;122;97;104;108;51; - - - - -`,F6=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;105;115;116;32;103;101;114;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;105;115;116;32;117;110;103;101;114;97;100;101;34; - - - - -`,R6=` - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;77;87;83;84;32;61;32;48;46;49;57; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;110;101;116;116;111;112;114;101;105;115;32;34;78;101;116;116;111;112;114;101;105;115;63;34; - - - 100;111;117;98;108;101;32;115;116;101;117;101;114;32;61;32;110;101;116;116;111;112;114;101;105;115;32;42;32;77;87;83;84; - - - 100;111;117;98;108;101;32;98;114;117;116;116;111;112;114;101;105;115;32;61;32;110;101;116;116;111;112;114;101;105;115;32;43;32;115;116;101;117;101;114; - - - 111;117;116;112;117;116;58;32;115;116;101;117;101;114; - - - 111;117;116;112;117;116;58;32;98;114;117;116;116;111;112;114;101;105;115; - - -`,J6=` - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;118;101;114;115;117;99;104;101;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;105;110;103;97;98;101;32;34;80;73;78;32;101;105;110;103;101;98;101;110;34; - - - 112;105;110;32;61;32;101;105;110;103;97;98;101; - - - 118;101;114;115;117;99;104;101;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;114;105;99;104;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;75;97;114;116;101;32;103;101;115;112;101;114;114;116;34; - - - - -`,G6=` - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;118;101;114;115;117;99;104;101;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;80;73;78;32;101;105;110;103;101;98;101;110;34; - - - 118;101;114;115;117;99;104;101;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;114;105;99;104;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;75;97;114;116;101;32;103;101;115;112;101;114;114;116;34; - - - - -`,H6=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;49;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;50;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;122;101;105;99;104;101;110;32;34;82;101;99;104;101;110;122;101;105;99;104;101;110;32;40;43;44;32;45;32;111;100;101;114;32;42;41;34; - - - 122;101;105;99;104;101;110; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;45;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;49;32;42;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 111;117;116;112;117;116;58;32;34;85;110;98;101;107;97;110;110;116;101;115;32;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - - -`,V6=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;101;101;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;101;101;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;115;112;97;108;116;101;110;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;109;101;101;114;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 105;110;116;32;115;112;97;108;116;101;32;61;32;48; - 115;112;97;108;116;101;32;60;32;109;101;101;114;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 115;112;97;108;116;101;43;43; - - - 109;101;101;114;91;114;101;105;104;101;93;91;115;112;97;108;116;101;93; - - - 111;117;116;112;117;116;58;32;115;112;97;108;116;101;110;91;115;112;97;108;116;101;93;32;43;32;40;114;101;105;104;101;32;43;32;49;41; - - - - - 248; - - - - - - - - -`,q6=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;101;101;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;101;101;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;101;101;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;115;112;97;108;116;101;110;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - -`,U6=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;109;101;114;32;34;87;101;108;99;104;101;115;32;71;101;114;105;99;104;116;63;32;40;49;44;32;50;32;111;100;101;114;32;51;41;34; - - - 110;117;109;109;101;114; - - - 111;117;116;112;117;116;58;32;34;71;114;105;101;99;104;105;115;99;104;101;114;32;83;97;108;97;116;34; - - - - - 111;117;116;112;117;116;58;32;34;82;101;105;115;45;66;111;119;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;97;102;101;108;45;87;114;97;112;34; - - - - - 111;117;116;112;117;116;58;32;34;66;105;116;116;101;32;101;105;110;101;32;103;252;108;116;105;103;101;32;78;117;109;109;101;114;32;119;228;104;108;101;110;34; - - - - -`,Q6=` - - - 105;110;116;32;122;97;104;108;49;32;61;32;52; - - - 105;110;116;32;122;97;104;108;50;32;61;32;51; - - - 105;110;116;32;115;117;109;109;101;32;61;32;122;97;104;108;49;32;43;32;122;97;104;108;50; - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,$6=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;110;91;105;93;32;34;78;97;109;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;109;101;114;32;34;76;111;115;110;117;109;109;101;114;32;40;49;32;98;105;115;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;84;97;102;101;108;100;105;101;110;115;116;58;32;34;32;43;32;110;97;109;101;110;91;110;117;109;109;101;114;32;45;32;49;93; - - -`,Y6=` - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;86;101;114;100;111;112;112;101;108;116;101;32;90;97;104;108;58;32;34;32;43;32;122;97;104;108; - - -`,K6=` - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;61;32;69;45;66;105;107;101;44;32;50;32;61;32;69;45;83;99;111;111;116;101;114;44;32;48;32;61;32;69;110;100;101;34; - - - 119;97;104;108;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;84;115;99;104;252;115;115;34; - - - -1; - - - - - 119;97;104;108;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;69;45;66;105;107;101;32;103;101;98;117;99;104;116;34; - - - - - 119;97;104;108;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;69;45;83;99;111;111;116;101;114;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;85;110;103;252;108;116;105;103;101;32;65;117;115;119;97;104;108;34; - - - - - - - - - - -`,X6=` - - - - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;61;32;69;45;66;105;107;101;44;32;50;32;61;32;69;45;83;99;111;111;116;101;114;44;32;48;32;61;32;69;110;100;101;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;34;84;115;99;104;252;115;115;34; - - - 77;101;110;252;32;118;101;114;108;97;115;115;101;110; - - - - - 111;117;116;112;117;116;58;32;34;69;45;66;105;107;101;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;69;45;83;99;111;111;116;101;114;32;103;101;98;117;99;104;116;34; - - - - - 111;117;116;112;117;116;58;32;34;85;110;103;252;108;116;105;103;101;32;65;117;115;119;97;104;108;34; - - - - - - -`,eI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;107;97;98;101;108;110;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;114;105;99;104;116;105;103;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;87;97;115;32;104;101;105;223;116;32;34;32;43;32;116;101;105;108;101;91;48;93;32;43;32;34;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;110;116;119;111;114;116; - - - 97;110;122;97;104;108;43;43; - - - 97;110;116;119;111;114;116;46;101;113;117;97;108;115;40;116;101;105;108;101;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;82;105;99;104;116;105;103;33;34; - - - 114;105;99;104;116;105;103;43;43; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;115;99;104;44;32;114;105;99;104;116;105;103;32;105;115;116;32;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;46;34; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;114;105;99;104;116;105;103;32;43;32;34;32;118;111;110;32;34;32;43;32;97;110;122;97;104;108;32;43;32;34;32;114;105;99;104;116;105;103;34; - - -`,tI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;107;97;98;101;108;110;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,nI=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;118;111;108;108;106;228;104;114;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;100;101;114;106;228;104;114;105;103;34; - - - - -`,iI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - -`,aI=` - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - 105;110;116;32;109;97;116;104;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;104;97;116;32;105;110;32;77;97;116;104;101;32;101;105;110;101;32;34;32;43;32;109;97;116;104;101; - - -`,sI=` - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;123;92;34;110;97;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;97;114;116;92;34;58;32;92;34;90;105;101;103;101;92;34;44;32;92;34;97;108;116;101;114;92;34;58;32;54;125;44;34; - - - 105;110;116;32;115;116;97;114;116;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;101;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;122;101;105;108;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;101;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101; - - -`,rI=` - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - -`,oI=` - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;34; - - - 99;104;101;99;107;65;103;101;40;41; - - - 111;117;116;112;117;116;58;32;34;71;111;111;100;98;121;101;34; - - -`,lI=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;97;100;117;108;116;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;111;114;34; - - - - -`,uI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;104;114;111;119;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 100;111;117;98;108;101;32;115;117;109;32;61;32;48; - - - 105;110;116;32;98;101;115;116;68;105;115;116;97;110;99;101;32;61;32;48; - - - 83;116;114;105;110;103;32;98;101;115;116;78;97;109;101;32;61;32;34;34; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 99;111;117;110;116;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;100;105;115;116;97;110;99;101; - - - 100;105;115;116;97;110;99;101;32;62;32;98;101;115;116;68;105;115;116;97;110;99;101; - - - 98;101;115;116;68;105;115;116;97;110;99;101;32;61;32;100;105;115;116;97;110;99;101; - - - 98;101;115;116;78;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;99;105;112;97;110;116;115;58;32;34;32;43;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;119;105;110;110;101;114;46;116;120;116;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;87;105;110;110;101;114;58;32;34;32;43;32;98;101;115;116;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;68;105;115;116;97;110;99;101;32;43;32;34;32;109;41;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - -`,mI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;104;114;111;119;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,cI=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;80;111;105;110;116;115;63;34; - - - 112;111;105;110;116;115;91;105;93;32;61;32;110;117;109;98;101;114; - - - - - 105;110;116;32;114;117;110;32;61;32;49; - 114;117;110;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 114;117;110;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104;32;45;32;114;117;110; - 105;43;43; - - - 112;111;105;110;116;115;91;105;93;32;60;32;112;111;105;110;116;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;112;111;105;110;116;115;91;105;93; - - - 112;111;105;110;116;115;91;105;93;32;61;32;112;111;105;110;116;115;91;105;32;43;32;49;93; - - - 112;111;105;110;116;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;112;108;97;99;101;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;112;108;97;99;101;32;43;32;34;46;32;34;32;43;32;112;111;105;110;116;115;91;105;93; - - - - -`,dI=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;119;104;111;108;101;32;110;117;109;98;101;114;34; - - - 83;116;114;105;110;103;32;98;105;110;97;114;121;32;61;32;34;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;110;117;109;98;101;114;32;37;32;50; - - - 98;105;110;97;114;121;32;61;32;114;101;109;97;105;110;100;101;114;32;43;32;98;105;110;97;114;121; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;114;121; - - -`,hI=` - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;87;104;105;99;104;32;119;111;114;100;32;40;99;97;112;105;116;97;108;32;108;101;116;116;101;114;115;32;111;110;108;121;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;99;114;101;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;112;104;97;98;101;116;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;112;104;97;98;101;116;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 115;101;99;114;101;116;32;61;32;115;101;99;114;101;116;32;43;32;97;108;112;104;97;98;101;116;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;115;101;99;114;101;116; - - -`,gI=` - - - 99;104;97;114;91;93;32;97;108;112;104;97;98;101;116;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - -`,pI=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;87;104;105;99;104;32;119;111;114;100;32;40;99;97;112;105;116;97;108;32;108;101;116;116;101;114;115;32;111;110;108;121;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;99;114;101;116;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 115;101;99;114;101;116;32;61;32;115;101;99;114;101;116;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;115;101;99;114;101;116; - - -`,fI=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;99;114;101;116;32;34;87;104;105;99;104;32;115;101;99;114;101;116;32;116;101;120;116;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;83;104;105;102;116;32;98;121;32;104;111;119;32;109;97;110;121;32;112;108;97;99;101;115;63;34; - - - 83;116;114;105;110;103;32;112;108;97;105;110;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;99;114;101;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;101;32;61;32;40;105;110;116;41;32;115;101;99;114;101;116;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;101;32;61;32;40;99;111;100;101;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 112;108;97;105;110;32;61;32;112;108;97;105;110;32;43;32;40;99;104;97;114;41;32;99;111;100;101; - - - - - 111;117;116;112;117;116;58;32;112;108;97;105;110; - - -`,bI=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;115;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 110;117;109;98;101;114;32;33;61;32;49; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 115;116;101;112;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115; - - -`,xI=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;103;114;97;100;101;115;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;111;117;110;116; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;69;110;116;101;114;32;97;32;103;114;97;100;101;34; - - - 115;117;109;32;61;32;115;117;109;32;43;32;103;114;97;100;101; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;42;32;49;46;48;32;47;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;97;118;101;114;97;103;101; - - -`,vI=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;110;117;109;98;101;114;115;63;34; - - - 105;110;116;32;110;117;109;98;101;114;49;32;61;32;49; - - - 105;110;116;32;110;117;109;98;101;114;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;99;111;117;110;116; - 105;43;43; - - - 105;110;116;32;110;117;109;98;101;114;51;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;51; - - - 110;117;109;98;101;114;49;32;61;32;110;117;109;98;101;114;50; - - - 110;117;109;98;101;114;50;32;61;32;110;117;109;98;101;114;51; - - - - -`,wI=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;105;115;32;101;118;101;110;34; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;105;115;32;111;100;100;34; - - - - -`,yI=` - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;86;65;84;32;61;32;48;46;49;57; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;110;101;116;80;114;105;99;101;32;34;78;101;116;32;112;114;105;99;101;63;34; - - - 100;111;117;98;108;101;32;116;97;120;32;61;32;110;101;116;80;114;105;99;101;32;42;32;86;65;84; - - - 100;111;117;98;108;101;32;103;114;111;115;115;80;114;105;99;101;32;61;32;110;101;116;80;114;105;99;101;32;43;32;116;97;120; - - - 111;117;116;112;117;116;58;32;116;97;120; - - - 111;117;116;112;117;116;58;32;103;114;111;115;115;80;114;105;99;101; - - -`,zI=` - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;97;116;116;101;109;112;116;115;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;97;116;116;101;109;112;116;115;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;110;116;101;114;101;100;32;34;69;110;116;101;114;32;80;73;78;34; - - - 112;105;110;32;61;32;101;110;116;101;114;101;100; - - - 97;116;116;101;109;112;116;115;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;99;116;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;100;32;98;108;111;99;107;101;100;34; - - - - -`,II=` - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;97;116;116;101;109;112;116;115;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;97;116;116;101;109;112;116;115;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;69;110;116;101;114;32;80;73;78;34; - - - 97;116;116;101;109;112;116;115;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;99;116;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;100;32;98;108;111;99;107;101;100;34; - - - - -`,kI=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;49;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;50;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;116;111;114;32;34;79;112;101;114;97;116;111;114;32;40;43;44;32;45;32;111;114;32;42;41;34; - - - 111;112;101;114;97;116;111;114; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;45;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;49;32;42;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 111;117;116;112;117;116;58;32;34;85;110;107;110;111;119;110;32;111;112;101;114;97;116;111;114;34; - - - - -`,SI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 115;101;97;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 115;101;97;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;109;110;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 105;110;116;32;99;111;108;117;109;110;32;61;32;48; - 99;111;108;117;109;110;32;60;32;115;101;97;91;114;111;119;93;46;108;101;110;103;116;104; - 99;111;108;117;109;110;43;43; - - - 115;101;97;91;114;111;119;93;91;99;111;108;117;109;110;93; - - - 111;117;116;112;117;116;58;32;99;111;108;117;109;110;115;91;99;111;108;117;109;110;93;32;43;32;40;114;111;119;32;43;32;49;41; - - - - - 248; - - - - - - - - -`,TI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 115;101;97;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 115;101;97;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 115;101;97;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;109;110;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - -`,_I=` - - - 105;110;112;117;116;58;32;105;110;116;32;112;105;99;107;32;34;87;104;105;99;104;32;100;105;115;104;63;32;40;49;44;32;50;32;111;114;32;51;41;34; - - - 112;105;99;107; - - - 111;117;116;112;117;116;58;32;34;71;114;101;101;107;32;115;97;108;97;100;34; - - - - - 111;117;116;112;117;116;58;32;34;82;105;99;101;32;98;111;119;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;97;102;101;108;32;119;114;97;112;34; - - - - - 111;117;116;112;117;116;58;32;34;80;108;101;97;115;101;32;99;104;111;111;115;101;32;97;32;118;97;108;105;100;32;110;117;109;98;101;114;34; - - - - -`,AI=` - - - 105;110;116;32;110;117;109;98;101;114;49;32;61;32;52; - - - 105;110;116;32;110;117;109;98;101;114;50;32;61;32;51; - - - 105;110;116;32;115;117;109;32;61;32;110;117;109;98;101;114;49;32;43;32;110;117;109;98;101;114;50; - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,jI=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;97;109;101;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;112;105;99;107;32;34;68;114;97;119;32;110;117;109;98;101;114;32;40;49;32;116;111;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;66;111;97;114;100;32;100;117;116;121;58;32;34;32;43;32;110;97;109;101;115;91;112;105;99;107;32;45;32;49;93; - - -`,CI=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;68;111;117;98;108;101;100;32;110;117;109;98;101;114;58;32;34;32;43;32;110;117;109;98;101;114; - - -`,DI=` - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;61;32;101;45;98;105;107;101;44;32;50;32;61;32;101;45;115;99;111;111;116;101;114;44;32;48;32;61;32;113;117;105;116;34; - - - 99;104;111;105;99;101;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;66;121;101;34; - - - -1; - - - - - 99;104;111;105;99;101;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;69;45;98;105;107;101;32;98;111;111;107;101;100;34; - - - - - 99;104;111;105;99;101;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;69;45;115;99;111;111;116;101;114;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;73;110;118;97;108;105;100;32;99;104;111;105;99;101;34; - - - - - - - - - - -`,BI=` - - - - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;61;32;101;45;98;105;107;101;44;32;50;32;61;32;101;45;115;99;111;111;116;101;114;44;32;48;32;61;32;113;117;105;116;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;34;66;121;101;34; - - - 76;101;97;118;101;32;116;104;101;32;109;101;110;117; - - - - - 111;117;116;112;117;116;58;32;34;69;45;98;105;107;101;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;69;45;115;99;111;111;116;101;114;32;98;111;111;107;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;73;110;118;97;108;105;100;32;99;104;111;105;99;101;34; - - - - - - -`,PI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;119;111;114;100;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;114;105;103;104;116;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;87;104;97;116;32;105;115;32;34;32;43;32;112;97;114;116;115;91;48;93;32;43;32;34;32;105;110;32;71;101;114;109;97;110;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;110;115;119;101;114; - - - 99;111;117;110;116;43;43; - - - 97;110;115;119;101;114;46;101;113;117;97;108;115;40;112;97;114;116;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;33;34; - - - 114;105;103;104;116;43;43; - - - - - 111;117;116;112;117;116;58;32;34;87;114;111;110;103;44;32;105;116;32;105;115;32;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;46;34; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;114;105;103;104;116;32;43;32;34;32;111;102;32;34;32;43;32;99;111;117;110;116;32;43;32;34;32;114;105;103;104;116;34; - - -`,OI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;119;111;114;100;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,MI=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;97;100;117;108;116;34; - - - - - 111;117;116;112;117;116;58;32;34;109;105;110;111;114;34; - - - - -`,ZI=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - -`,LI=` - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - 105;110;116;32;109;97;116;104;115;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;103;111;116;32;97;32;34;32;43;32;109;97;116;104;115;32;43;32;34;32;105;110;32;109;97;116;104;115;34; - - -`,WI=` - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;123;92;34;110;97;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;115;112;101;99;105;101;115;92;34;58;32;92;34;103;111;97;116;92;34;44;32;92;34;97;103;101;92;34;58;32;54;125;44;34; - - - 105;110;116;32;115;116;97;114;116;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;108;105;110;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101; - - -`,EI=` - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - -`,NI=` - - - 105;110;116;91;93;91;93;32;116;97;98;108;101;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;108;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;108;101;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;32;40;49;32;116;111;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;32;40;49;32;116;111;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;108;101;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - -`,FI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;114;101;101;32;61;32;48; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 105;110;116;32;115;101;97;116;32;61;32;48; - 115;101;97;116;32;60;32;115;101;97;116;115;91;114;111;119;93;46;108;101;110;103;116;104; - 115;101;97;116;43;43; - - - 33;115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 102;114;101;101;32;61;32;102;114;101;101;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;58;32;34;32;43;32;102;114;101;101; - - -`,RI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - -`,JI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;82;111;119;34; - - - 105;110;112;117;116;58;32;105;110;116;32;115;101;97;116;32;34;83;101;97;116;34; - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 111;117;116;112;117;116;58;32;34;65;108;114;101;97;100;121;32;115;111;108;100;34; - - - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;83;111;108;100;58;32;114;111;119;32;34;32;43;32;114;111;119;32;43;32;34;44;32;115;101;97;116;32;34;32;43;32;115;101;97;116; - - - - -`,GI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;82;111;119;34; - - - 105;110;112;117;116;58;32;105;110;116;32;115;101;97;116;32;34;83;101;97;116;34; - - -`,HI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;114;111;119;32;61;32;48; - 114;111;119;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 114;111;119;43;43; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;34; - - - 105;110;116;32;115;101;97;116;32;61;32;48; - 115;101;97;116;32;60;32;115;101;97;116;115;91;114;111;119;93;46;108;101;110;103;116;104; - 115;101;97;116;43;43; - - - 115;101;97;116;115;91;114;111;119;93;91;115;101;97;116;93; - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;88;34; - - - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - -`,VI=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;101;97;116;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;101;97;116;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;101;97;116;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;82;111;119;115;58;32;34;32;43;32;115;101;97;116;115;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;83;101;97;116;115;32;112;101;114;32;114;111;119;58;32;34;32;43;32;115;101;97;116;115;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;82;111;119;32;49;44;32;115;101;97;116;32;50;32;115;111;108;100;63;32;34;32;43;32;115;101;97;116;115;91;49;93;91;50;93; - - -`,qI=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 105;110;116;32;78;117;109;98;101;114;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;78;117;109;98;101;114; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;58;32;34;32;43;32;114;101;115;117;108;116; - - -`,UI=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;52; - - - 105;110;116;32;78;117;109;98;101;114;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;78;85;77;66;69;82; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;58;32;34;32;43;32;114;101;115;117;108;116; - - -`,QI=` - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - -`,$I=` - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - -`,YI=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - -`,KI=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;65;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34;41; - - -`,XI=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;103;101;32;61;32;55; - - - 100;111;117;98;108;101;32;104;101;105;103;104;116;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;109;34; - - -`,ek=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - -`,tk=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;51; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;52; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - -`,nk=` - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;52; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;51; - - -`,ik=` - - - 83;116;114;105;110;103;32;108;101;102;116;32;61;32;34;106;117;105;99;101;34; - - - 83;116;114;105;110;103;32;114;105;103;104;116;32;61;32;34;119;97;116;101;114;34; - - - 83;116;114;105;110;103;32;116;101;109;112;32;61;32;108;101;102;116; - - - 108;101;102;116;32;61;32;114;105;103;104;116; - - - 114;105;103;104;116;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;108;101;102;116; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;58;32;34;32;43;32;114;105;103;104;116; - - -`,ak=` - - - 83;116;114;105;110;103;32;108;101;102;116;32;61;32;34;106;117;105;99;101;34; - - - 83;116;114;105;110;103;32;114;105;103;104;116;32;61;32;34;119;97;116;101;114;34; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;108;101;102;116; - - - 111;117;116;112;117;116;58;32;34;82;105;103;104;116;58;32;34;32;43;32;114;105;103;104;116; - - -`,sk=` - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,rk=` - - - 105;110;116;32;97;103;101;32;61;32;49;52; - - - 100;111;117;98;108;101;32;104;101;105;103;104;116;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;77;105;97;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;32;34;32;43;32;97;103;101;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34; - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;109;34; - - -`,ok=` - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;49;51;53; - - - 105;110;116;32;104;111;117;114;115;32;61;32;109;105;110;117;116;101;115;32;47;32;54;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;109;105;110;117;116;101;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;104;111;117;114;115;32;43;32;34;32;104;111;117;114;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;109;105;110;117;116;101;115;34; - - -`,lk=` - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;105;99;101;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;116;111;116;97;108;80;114;105;99;101;32;61;32;99;111;117;110;116;32;42;32;112;114;105;99;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108;80;114;105;99;101;32;43;32;34;32;101;117;114;111;115;34; - - -`,uk=` - - - 105;110;116;32;99;114;101;100;105;116;32;61;32;50;48; - - - 105;110;116;32;112;114;105;99;101;32;61;32;55; - - - 99;114;101;100;105;116;32;61;32;99;114;101;100;105;116;32;45;32;112;114;105;99;101; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;99;114;101;100;105;116; - - -`,mk=` - - - 105;110;116;32;99;114;101;100;105;116;32;61;32;50;48; - - - 105;110;116;32;112;114;105;99;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;76;101;102;116;58;32;34;32;43;32;99;114;101;100;105;116; - - - 99;114;101;100;105;116;32;61;32;99;114;101;100;105;116;32;45;32;112;114;105;99;101; - - -`,ck=` - - - 105;110;116;32;112;114;105;99;101;32;61;32;57; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;105;99;101;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,dk=` - - - 105;110;116;32;115;108;105;99;101;115;32;61;32;56; - - - 105;110;116;32;112;101;111;112;108;101;32;61;32;52; - - - 105;110;116;32;112;101;114;80;101;114;115;111;110;32;61;32;115;108;105;99;101;115;32;47;32;112;101;111;112;108;101; - - - 111;117;116;112;117;116;58;32;34;69;118;101;114;121;111;110;101;32;103;101;116;115;32;34;32;43;32;112;101;114;80;101;114;115;111;110;32;43;32;34;32;115;108;105;99;101;115;46;34; - - -`,hk=` - - - 105;110;116;32;115;108;105;99;101;115;32;61;32;56; - - - 105;110;116;32;112;101;111;112;108;101;32;61;32;52; - - - 105;110;116;32;112;101;114;80;101;114;115;111;110;32;61;32;115;108;105;99;101;115;32;47;32;112;101;111;112;108;101; - - - 111;117;116;112;117;116;58;32;34;69;118;101;114;121;111;110;101;32;103;101;116;115;32;34;32;43;32;112;101;114;112;101;114;115;111;110;32;43;32;34;32;115;108;105;99;101;115;46;34; - - -`,gk=` - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;55; - - - 105;110;116;32;119;105;100;116;104;32;61;32;52; - - - 105;110;116;32;97;114;101;97;32;61;32;108;101;110;103;116;104;32;42;32;119;105;100;116;104; - - - 105;110;116;32;112;101;114;105;109;101;116;101;114;32;61;32;50;32;42;32;40;108;101;110;103;116;104;32;43;32;119;105;100;116;104;41; - - - 111;117;116;112;117;116;58;32;34;65;114;101;97;58;32;34;32;43;32;97;114;101;97; - - - 111;117;116;112;117;116;58;32;34;80;101;114;105;109;101;116;101;114;58;32;34;32;43;32;112;101;114;105;109;101;116;101;114; - - -`,pk=` - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;115;32;61;32;115;101;99;111;110;100;115;32;47;32;54;48; - - - 105;110;116;32;114;101;109;97;105;110;100;101;114;32;61;32;115;101;99;111;110;100;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;117;116;101;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;115;101;99;111;110;100;115;34; - - -`,fk=` - - - 105;110;116;32;115;101;99;111;110;100;115;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;115;32;43;32;34;32;109;105;110;117;116;101;115;32;97;110;100;32;34;32;43;32;114;101;109;97;105;110;100;101;114;32;43;32;34;32;115;101;99;111;110;100;115;34; - - -`,bk=` - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;53; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51; - - - 105;110;116;32;97;114;101;97;32;61;32;108;101;110;103;116;104;32;42;32;119;105;100;116;104; - - - 111;117;116;112;117;116;58;32;97;114;101;97; - - -`,xk=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;104;97;116;32;105;115;32;121;111;117;114;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;34;32;43;32;110;97;109;101;32;43;32;34;33;34; - - -`,vk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,wk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;40;97;32;43;32;98;41; - - -`,yk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;97;32;43;32;98; - - -`,zk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;101;120;116;32;121;101;97;114;32;121;111;117;32;119;105;108;108;32;98;101;32;34;32;43;32;40;97;103;101;32;43;32;49;41; - - -`,Ik=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;101;120;116;32;121;101;97;114;32;121;111;117;32;119;105;108;108;32;98;101;32;34;32;43;32;40;65;103;101;32;43;32;49;41; - - -`,kk=` - - - 105;110;112;117;116;58;32;105;110;116;32;121;101;97;114;115;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 105;110;116;32;100;97;121;115;32;61;32;121;101;97;114;115;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;105;115;32;97;98;111;117;116;32;34;32;43;32;100;97;121;115;32;43;32;34;32;100;97;121;115;46;34; - - -`,Sk=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 100;111;117;98;108;101;32;109;101;97;110;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;101;97;110;58;32;34;32;43;32;109;101;97;110; - - -`,Tk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;99;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;99;116;58;32;34;32;43;32;112;114;111;100;117;99;116; - - -`,_k=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;97;114;101;32;97;110;32;97;100;117;108;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;97;114;101;32;110;111;116;32;97;110;32;97;100;117;108;116;32;121;101;116;46;34; - - - - -`,Ak=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;60;32;48; - - - 110;117;109;98;101;114;32;61;32;48;32;45;32;110;117;109;98;101;114; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - -`,jk=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;101;118;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;111;100;100;34; - - - - -`,Ck=` - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;53; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - -`,Dk=` - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;53; - - - 112;111;105;110;116;115;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - -`,Bk=` - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - -`,Pk=` - - - 105;110;116;32;112;111;105;110;116;115;32;61;32;49;48; - - - 112;111;105;110;116;115;32;61;32;112;111;105;110;116;115;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;111;105;110;116;115;58;32;34;32;43;32;112;111;105;110;116;115; - - -`,Ok=` - - - 105;110;116;32;112;114;105;99;101;32;61;32;56; - - - 105;110;116;32;99;104;101;101;115;101;32;61;32;50; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;40;112;114;105;99;101;32;43;32;99;104;101;101;115;101;41;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,Mk=` - - - 105;110;116;32;112;114;105;99;101;32;61;32;56; - - - 105;110;116;32;99;104;101;101;115;101;32;61;32;50; - - - 105;110;116;32;99;111;117;110;116;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;105;99;101;32;43;32;99;104;101;101;115;101;32;42;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,Zk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - -`,Lk=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;105;99;101;32;34;80;114;105;99;101;32;105;110;32;101;117;114;111;115;63;34; - - - 112;114;105;99;101;32;62;32;53;48; - - - 112;114;105;99;101;32;61;32;112;114;105;99;101;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;84;111;32;112;97;121;58;32;34;32;43;32;112;114;105;99;101;32;43;32;34;32;101;117;114;111;115;34; - - -`,Wk=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;71;114;97;100;101;63;34; - - - 103;114;97;100;101;32;62;61;32;49;32;38;38;32;103;114;97;100;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;118;97;108;105;100;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;118;97;108;105;100;34; - - - - -`,Ek=` - - - 105;110;112;117;116;58;32;105;110;116;32;112;111;105;110;116;115;32;34;72;111;119;32;109;97;110;121;32;112;111;105;110;116;115;63;34; - - - 112;111;105;110;116;115;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;112;97;115;115;101;100;34; - - - - - 111;117;116;112;117;116;58;32;34;110;111;116;32;112;97;115;115;101;100;34; - - - - -`,Nk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - -`,Fk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - -`,Rk=` - - - 105;110;116;32;97;103;101;32;61;32;49;53; - - - 97;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;101;110;116;114;121;34; - - - - -`,Jk=` - - - 105;110;116;32;97;103;101;32;61;32;49;53; - - - 65;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;101;110;116;114;121;34; - - - - -`,Gk=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;50;32;38;38;32;97;103;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;89;111;117;116;104;32;116;105;99;107;101;116;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;32;121;111;117;116;104;32;116;105;99;107;101;116;34; - - - - -`,Hk=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;71;114;97;100;101;63;34; - - - 103;114;97;100;101;32;60;32;49;32;124;124;32;103;114;97;100;101;32;62;32;54; - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;103;114;97;100;101;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;71;114;97;100;101;32;34;32;43;32;103;114;97;100;101;32;43;32;34;32;105;115;32;118;97;108;105;100;46;34; - - - - -`,Vk=` - - - 105;110;112;117;116;58;32;105;110;116;32;121;101;97;114;32;34;89;101;97;114;63;34; - - - 40;121;101;97;114;32;37;32;52;32;61;61;32;48;32;38;38;32;121;101;97;114;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;121;101;97;114;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;76;101;97;112;32;121;101;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;32;108;101;97;112;32;121;101;97;114;34; - - - - -`,qk=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;68;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;100;97;121;34; - - - - -`,Uk=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;99;111;108;111;114;32;34;87;104;97;116;32;99;111;108;111;114;32;105;115;32;116;104;101;32;116;114;97;102;102;105;99;32;108;105;103;104;116;63;34; - - - 99;111;108;111;114; - - - 111;117;116;112;117;116;58;32;34;83;116;111;112;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;105;116;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;108;107;34; - - - - - 111;117;116;112;117;116;58;32;34;73;32;100;111;32;110;111;116;32;107;110;111;119;32;116;104;105;115;32;99;111;108;111;114;46;34; - - - - -`,Qk=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;119;97;116;101;114;44;32;50;32;106;117;105;99;101;44;32;51;32;99;111;99;111;97;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;119;97;116;101;114;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;106;117;105;99;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;99;111;99;111;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;98;117;116;116;111;110;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - -`,$k=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;87;104;105;99;104;32;100;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - -`,Yk=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;97;121;32;34;87;104;105;99;104;32;100;97;121;32;40;49;32;116;111;32;55;41;63;34; - - - 100;97;121;32;61;61;32;54;32;38;38;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - -`,Kk=` - - - 105;110;116;32;100;97;121;32;61;32;55; - - - 100;97;121;32;61;61;32;54;32;124;124;32;100;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - -`,Xk=` - - - 105;110;116;32;100;97;121;32;61;32;55; - - - 100;97;121;32;61;61;32;54;32;124;124;32;68;97;121;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;110;111;32;115;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;115;99;104;111;111;108;34; - - - - -`,e7=` - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;117;114;32;34;87;104;97;116;32;116;105;109;101;32;105;115;32;105;116;32;40;106;117;115;116;32;116;104;101;32;104;111;117;114;41;63;34; - - - 104;111;117;114;32;62;61;32;56;32;38;38;32;104;111;117;114;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;83;99;104;111;111;108;34; - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;116;105;109;101;34; - - - - -`,t7=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;97;100;101;32;34;87;104;105;99;104;32;103;114;97;100;101;63;34; - - - 103;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;115;97;116;105;115;102;97;99;116;111;114;121;34; - - - - - 111;117;116;112;117;116;58;32;34;115;117;102;102;105;99;105;101;110;116;34; - - - - - 111;117;116;112;117;116;58;32;34;112;111;111;114;34; - - - - - 111;117;116;112;117;116;58;32;34;102;97;105;108;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;32;118;97;108;105;100;32;103;114;97;100;101;34; - - - - -`,n7=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;116;111;114;32;34;79;112;101;114;97;116;111;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;48; - - - 111;112;101;114;97;116;111;114; - - - 114;101;115;117;108;116;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 114;101;115;117;108;116;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 114;101;115;117;108;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116; - - - - - 111;117;116;112;117;116;58;32;34;85;110;107;110;111;119;110;32;111;112;101;114;97;116;111;114;34; - - - - -`,i7=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;119;97;116;101;114;44;32;50;32;106;117;105;99;101;44;32;51;32;99;111;99;111;97;34; - - - 99;104;111;105;99;101;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;119;97;116;101;114;46;34; - - - - - 99;104;111;105;99;101;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;106;117;105;99;101;46;34; - - - - - 99;104;111;105;99;101;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;72;101;114;101;32;105;115;32;121;111;117;114;32;99;111;99;111;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;105;115;32;98;117;116;116;111;110;32;100;111;101;115;32;110;111;116;32;101;120;105;115;116;46;34; - - - - - - - - -`,a7=` - - - 80;117;116;32;97;32;99;117;112;32;111;110;32;116;104;101;32;116;97;98;108;101; - - - 83;117;103;97;114;32;119;97;110;116;101;100;63; - - - 65;100;100;32;115;117;103;97;114; - - - - - 248; - - - - - 84;101;97;32;115;116;105;108;108;32;116;111;111;32;104;111;116; - - - 87;97;105;116;32;49;32;109;105;110;117;116;101; - - - - - 68;114;105;110;107;32;116;104;101;32;116;101;97; - - -`,s7=` - - - 71;101;116;32;117;112; - - - 66;114;117;115;104;32;116;101;101;116;104; - - - 71;101;116;32;100;114;101;115;115;101;100; - - - 72;97;118;101;32;98;114;101;97;107;102;97;115;116; - - -`,r7=` - - - 70;105;108;108;32;116;104;101;32;107;101;116;116;108;101;32;119;105;116;104;32;119;97;116;101;114; - - - 83;119;105;116;99;104;32;111;110;32;116;104;101;32;107;101;116;116;108;101; - - - 80;117;116;32;97;32;116;101;97;32;98;97;103;32;105;110;32;116;104;101;32;99;117;112; - - - 80;111;117;114;32;104;111;116;32;119;97;116;101;114;32;105;110;116;111;32;116;104;101;32;99;117;112; - - - 87;97;105;116;32;51;32;109;105;110;117;116;101;115; - - - 84;97;107;101;32;111;117;116;32;116;104;101;32;116;101;97;32;98;97;103; - - -`,o7=` - - - 73;115;32;105;116;32;114;97;105;110;105;110;103;63; - - - 80;97;99;107;32;97;110;32;117;109;98;114;101;108;108;97; - - - - - 80;97;99;107;32;115;117;110;103;108;97;115;115;101;115; - - - - - 76;101;97;118;101; - - -`,l7=` - - - 80;108;97;116;101;32;110;111;116;32;101;109;112;116;121; - - - 84;97;107;101;32;97;32;98;105;116;101; - - - - - 67;108;101;97;114;32;116;104;101;32;112;108;97;116;101; - - -`,u7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;67;111;117;110;116;32;117;112;32;116;111;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,m7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,c7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,d7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;99;116;111;114;105;97;108;32;111;102;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;102;97;99;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;99;116;111;114;105;97;108;32;61;32;102;97;99;116;111;114;105;97;108;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;99;116;111;114;105;97;108; - - -`,h7=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;116;105;109;101;115;63;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;110;32;111;112;116;105;111;110;34; - - - - -`,g7=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;99;104;111;105;99;101;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;116;105;109;101;115;63;34; - - - 99;104;111;105;99;101; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;97;110;32;111;112;116;105;111;110;34; - - - - -`,p7=` - - - 105;110;116;32;103;114;97;100;101;32;61;32;50; - - - 103;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;101;112;32;112;114;97;99;116;105;115;105;110;103;34; - - - - -`,f7=` - - - 105;110;116;32;103;114;97;100;101;32;61;32;50; - - - 71;114;97;100;101; - - - 111;117;116;112;117;116;58;32;34;118;101;114;121;32;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;103;111;111;100;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;101;112;32;112;114;97;99;116;105;115;105;110;103;34; - - - - -`,b7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114; - - - 111;117;116;112;117;116;58;32;34;111;110;101;34; - - - - - 111;117;116;112;117;116;58;32;34;116;119;111;34; - - - - - 111;117;116;112;117;116;58;32;34;116;104;114;101;101;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;107;110;111;119;110;34; - - - - -`,x7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 110;117;109;98;101;114;32;60;32;49;48;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;116;101;112;115; - - -`,v7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - - - 110;117;109;98;101;114;32;60;32;49;32;124;124;32;110;117;109;98;101;114;32;62;32;49;48; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;32;102;114;111;109;32;49;32;116;111;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;97;110;107;32;121;111;117;33;34; - - -`,w7=` - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,y7=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;111;97;108;32;34;83;97;118;105;110;103;115;32;103;111;97;108;63;34; - - - 105;110;116;32;109;111;110;101;121;32;61;32;48; - - - 105;110;116;32;119;101;101;107;115;32;61;32;48; - - - 109;111;110;101;121;32;60;32;103;111;97;108; - - - 109;111;110;101;121;32;61;32;109;111;110;101;121;32;43;32;53; - - - 119;101;101;107;115;32;61;32;119;101;101;107;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;115;58;32;34;32;43;32;119;101;101;107;115; - - -`,z7=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;111;97;108;32;34;83;97;118;105;110;103;115;32;103;111;97;108;63;34; - - - 105;110;116;32;109;111;110;101;121;32;61;32;48; - - - 105;110;116;32;119;101;101;107;115;32;61;32;48; - - - 109;111;110;101;121;32;60;61;32;103;111;97;108; - - - 109;111;110;101;121;32;61;32;109;111;110;101;121;32;43;32;53; - - - 119;101;101;107;115;32;61;32;119;101;101;107;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;101;101;107;115;58;32;34;32;43;32;119;101;101;107;115; - - -`,I7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;101;114;101;63;34; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;50; - - - 110;117;109;98;101;114;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;43;32;50; - - - - -`,k7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - -`,S7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;51; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;78;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - -`,T7=` - - - 105;110;116;32;103;114;97;100;101;32;61;32;48; - - - 103;114;97;100;101;32;60;32;49;32;124;124;32;103;114;97;100;101;32;62;32;54; - - - 105;110;112;117;116;58;32;103;114;97;100;101;32;34;71;114;97;100;101;32;102;114;111;109;32;49;32;116;111;32;54;34; - - - - - 111;117;116;112;117;116;58;32;34;89;111;117;114;32;103;114;97;100;101;58;32;34;32;43;32;103;114;97;100;101; - - -`,_7=` - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - - - 110;117;109;98;101;114;32;33;61;32;48; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;78;117;109;98;101;114;32;40;48;32;115;116;111;112;115;41;34; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,A7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;83;116;97;114;116;32;110;117;109;98;101;114;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - -`,j7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;99;116;32;61;32;110;117;109;98;101;114;32;42;32;105; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;99;116; - - - - -`,C7=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,D7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - -`,B7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;62;32;49; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - -`,P7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;32;61;32;50;48; - - - 110;117;109;98;101;114;32;60;32;108;105;109;105;116; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - - -`,O7=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;32;61;32;50;48; - - - 110;117;109;98;101;114;32;60;32;76;105;109;105;116; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - - -`,M7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;110;117;109;98;101;114;63;34; - - - 110;117;109;98;101;114;32;60;32;49;48;48; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;42;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - - -`,Z7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,L7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;72;111;119;32;109;97;110;121;32;108;105;110;101;115;63;34; - - - 105;110;116;32;108;105;110;101;32;61;32;49; - 108;105;110;101;32;60;61;32;110; - 108;105;110;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;116;97;114;32;61;32;49; - 115;116;97;114;32;60;61;32;108;105;110;101; - 115;116;97;114;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - -`,W7=` - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;48; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;99;111;117;110;116; - - -`,E7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;99;111;117;110;116; - - -`,N7=` - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;111;119;32;42;32;105; - - - - -`,F7=` - - - 105;110;112;117;116;58;32;105;110;116;32;114;111;119;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;111;119;32;42;32;105; - - - - -`,R7=` - - - 105;110;116;32;104;101;105;103;104;116;32;61;32;48; - - - 105;110;116;32;115;116;101;112;32;61;32;49; - 115;116;101;112;32;60;61;32;53; - 115;116;101;112;43;43; - - - 104;101;105;103;104;116;32;61;32;104;101;105;103;104;116;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;99;109;34; - - -`,J7=` - - - 105;110;116;32;115;116;101;112;32;61;32;49; - 115;116;101;112;32;60;61;32;53; - 115;116;101;112;43;43; - - - 104;101;105;103;104;116;32;61;32;104;101;105;103;104;116;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;101;105;103;104;116;58;32;34;32;43;32;104;101;105;103;104;116;32;43;32;34;32;99;109;34; - - -`,G7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - -`,H7=` - - - 105;110;112;117;116;58;32;105;110;116;32;119;105;100;116;104;32;34;87;105;100;116;104;34; - - - 105;110;112;117;116;58;32;105;110;116;32;104;101;105;103;104;116;32;34;72;101;105;103;104;116;34; - - - 105;110;116;32;108;105;110;101;32;61;32;49; - 108;105;110;101;32;60;61;32;104;101;105;103;104;116; - 108;105;110;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;99;111;108;117;109;110;32;61;32;49; - 99;111;108;117;109;110;32;60;61;32;119;105;100;116;104; - 99;111;108;117;109;110;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - -`,V7=` - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93; - - - - -`,q7=` - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;91;48;93; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;91;118;97;108;117;101;115;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - - -`,U7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - -`,Q7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - - - -`,$7=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - -`,Y7=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - -`,K7=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;72;111;119;32;109;97;110;121;32;115;116;101;112;115;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 108;105;110;101;32;61;32;108;105;110;101;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;108;105;110;101; - - - - -`,X7=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;97;109;101;34; - - - - - 111;117;116;112;117;116;58;32;34;70;105;114;115;116;32;110;97;109;101;58;32;34;32;43;32;110;97;109;101;115;91;48;93; - - -`,e8=` - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - - - 105;110;116;32;105;32;61;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93; - - - - -`,t8=` - - - 105;110;116;91;93;32;110;117;109;98;101;114;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;115;91;105;93;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;110;117;109;98;101;114;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;98;101;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114;115;91;105;93; - - - 110;117;109;98;101;114;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;110;117;109;98;101;114;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - -`,n8=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;111;105;110;116;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - 112;111;105;110;116;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;111;105;110;116;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - -`,i8=` - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - 110;117;109;98;101;114;32;33;61;32;48; - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - 105;110;112;117;116;58;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,a8=` - - - 105;110;116;32;115;117;109;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;32;40;48;32;61;32;115;116;111;112;41;34; - - - 110;117;109;98;101;114;32;61;61;32;48; - - - 76;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 115;117;109;32;61;32;115;117;109;32;43;32;110;117;109;98;101;114; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,s8=` - - - 105;110;112;117;116;58;32;105;110;116;32;108;101;110;103;116;104;32;34;76;101;110;103;116;104;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;105;100;116;104;32;34;87;105;100;116;104;63;34; - - - 105;110;116;32;102;32;61;32;97;114;101;97;40;108;101;110;103;116;104;44;32;119;105;100;116;104;41; - - - 111;117;116;112;117;116;58;32;34;65;114;101;97;58;32;34;32;43;32;102; - - -`,r8=` - - - 114;101;116;117;114;110;32;97;32;42;32;98; - - -`,o8=` - - - 115;104;111;119;71;114;101;101;116;105;110;103;40;41; - - - 97;115;107;81;117;101;115;116;105;111;110;115;40;41; - - - 115;104;111;119;82;101;115;117;108;116;40;41; - - -`,l8=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;105;103;104;101;115;116;32;116;101;109;112;101;114;97;116;117;114;101;58;32;34;32;43;32;109;97;120; - - -`,u8=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;105;103;104;101;115;116;32;116;101;109;112;101;114;97;116;117;114;101;58;32;34;32;43;32;109;97;120; - - -`,m8=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,c8=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;76;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,d8=` - - - 105;110;116;91;93;32;103;114;97;100;101;115;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;103;114;97;100;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;103;114;97;100;101;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,h8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;113;32;61;32;115;113;117;97;114;101;40;110;117;109;98;101;114;41; - - - 111;117;116;112;117;116;58;32;34;84;104;101;32;115;113;117;97;114;101;32;105;115;32;34;32;43;32;113; - - -`,g8=` - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;114;101;115;117;108;116; - - -`,p8=` - - - 115;104;111;119;84;105;116;108;101;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;104;97;116;32;105;115;32;121;111;117;114;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;34;32;43;32;110;97;109;101; - - -`,f8=` - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;32;116;111;32;116;104;101;32;113;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;103;101;116;32;116;104;114;101;101;32;113;117;101;115;116;105;111;110;115;46;34; - - -`,b8=` - - - 115;104;111;119;83;117;109;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 115;104;111;119;83;117;109;40;120;44;32;120;32;43;32;49;41; - - -`,x8=` - - - 105;110;116;32;115;117;109;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;117;109; - - -`,v8=` - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;32;118;97;108;117;101;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;33;34; - - -`,w8=` - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;32;118;97;108;117;101;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;71;111;34; - - -`,y8=` - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;112;111;105;110;116;115;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;58;32;34;32;43;32;116;111;116;97;108; - - -`,z8=` - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;112;111;105;110;116;115;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;58;32;34;32;43;32;116;111;116;97;108; - - -`,I8=` - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;44;32;114;111;117;110;100;32;49;58;32;34;32;43;32;112;111;105;110;116;115;91;49;93;91;48;93; - - -`,k8=` - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;80;108;97;121;101;114;32;50;44;32;114;111;117;110;100;32;49;58;32;34;32;43;32;80;111;105;110;116;115;91;49;93;91;48;93; - - -`,S8=` - - - 105;110;116;91;93;91;93;32;112;111;105;110;116;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;105;110;116;115;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;111;105;110;116;115;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;58;32;34;32;43;32;115;117;109; - - -`,T8=` - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,_8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,A8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109; - - -`,j8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;87;104;105;99;104;32;116;105;109;101;115;32;116;97;98;108;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;114;101;115;117;108;116;32;61;32;110;117;109;98;101;114;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;116;105;109;101;115;32;34;32;43;32;110;117;109;98;101;114;32;43;32;34;32;61;32;34;32;43;32;114;101;115;117;108;116; - - - - - 110;117;109;98;101;114;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;119;97;115;32;104;97;114;100;33;34; - - - - - 111;117;116;112;117;116;58;32;34;84;104;97;116;32;119;97;115;32;101;97;115;121;46;34; - - - - -`,C8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;69;110;116;101;114;32;97;32;110;117;109;98;101;114;34; - - - 105;110;116;32;102;97;99;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;99;116;111;114;105;97;108;32;61;32;102;97;99;116;111;114;105;97;108;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;99;116;111;114;105;97;108; - - -`,D8=` - - - 83;116;114;105;110;103;32;108;101;116;116;101;114;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;101;116;116;101;114;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;108;101;116;116;101;114;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;76;105;110;101;115;58;32;34;32;43;32;108;105;110;101;115;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;101;115;91;105;93;46;99;111;110;116;97;105;110;115;40;34;102;111;120;34;41; - - - 111;117;116;112;117;116;58;32;34;70;111;120;32;105;110;32;108;105;110;101;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - -`,B8=` - - - 83;116;114;105;110;103;32;108;101;116;116;101;114;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;101;116;116;101;114;46;116;120;116;34;41;41; - - -`,P8=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;32;34;70;105;114;115;116;32;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;32;34;76;97;115;116;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,O8=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;32;34;70;105;114;115;116;32;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;32;34;76;97;115;116;32;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - -`,M8=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;104;97;115;32;34;32;43;32;110;97;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;116;101;114;115;34; - - -`,Z8=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;104;97;115;32;34;32;43;32;110;97;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;116;101;114;115;34; - - -`,L8=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;105;110;112;117;116;32;34;80;97;115;115;119;111;114;100;63;34; - - - 105;110;112;117;116;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;87;101;108;99;111;109;101;33;34; - - - - - 111;117;116;112;117;116;58;32;34;87;114;111;110;103;32;112;97;115;115;119;111;114;100;34; - - - - -`,W8=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;70;114;101;101;32;101;110;116;114;121;34; - - - - - 97;103;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;67;104;105;108;100;32;116;105;99;107;101;116;58;32;52;32;101;117;114;111;115;34; - - - - - 111;117;116;112;117;116;58;32;34;65;100;117;108;116;32;116;105;99;107;101;116;58;32;56;32;101;117;114;111;115;34; - - - - - - -`,E8=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;98;101;114;32;34;83;116;97;114;116;32;118;97;108;117;101;63;34; - - - 110;117;109;98;101;114;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114; - - - 110;117;109;98;101;114;32;61;32;110;117;109;98;101;114;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;69;110;100;34; - - -`,N8=` - - - 105;110;116;32;116;114;105;101;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;103;117;101;115;115;32;34;89;111;117;114;32;103;117;101;115;115;63;34; - - - 116;114;105;101;115;32;61;32;116;114;105;101;115;32;43;32;49; - - - 103;117;101;115;115;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;114;105;101;115;58;32;34;32;43;32;116;114;105;101;115; - - -`,F8=` - - - 105;110;116;32;116;114;105;101;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;103;117;101;115;115;32;34;89;111;117;114;32;103;117;101;115;115;63;34; - - - 103;117;101;115;115;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 116;114;105;101;115;32;61;32;116;114;105;101;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;84;114;105;101;115;58;32;34;32;43;32;116;114;105;101;115; - - -`,R8=` - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;105;99;101;32;34;80;114;105;99;101;63;32;40;48;32;61;32;101;110;100;41;34; - - - 112;114;105;99;101;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;117;109;32;61;32;115;117;109;32;43;32;112;114;105;99;101; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;73;116;101;109;115;58;32;34;32;43;32;99;111;117;110;116; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;115;117;109; - - -`,J8=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;103;101;32;34;72;111;119;32;111;108;100;32;97;114;101;32;121;111;117;63;34; - - - 97;103;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;89;111;117;32;109;97;121;32;118;111;116;101;46;34; - - - - - 105;110;116;32;121;101;97;114;115;32;61;32;49;54;32;45;32;97;103;101; - - - 111;117;116;112;117;116;58;32;34;87;97;105;116;32;34;32;43;32;121;101;97;114;115;32;43;32;34;32;109;111;114;101;32;121;101;97;114;115;46;34; - - - - -`,G8=` - - - 105;110;112;117;116;58;32;105;110;116;32;108;105;109;105;116;32;34;85;112;32;116;111;32;119;104;105;99;104;32;110;117;109;98;101;114;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;108;105;109;105;116; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,H8=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,V8=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,q8=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;119;97;110;116;101;100;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,U8=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;119;97;110;116;101;100;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;87;97;110;116;101;100; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,Q8=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;110;116;101;100;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;61;61;32;119;97;110;116;101;100; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;67;111;117;110;116;58;32;34;32;43;32;99;111;117;110;116; - - -`,$8=` - - - 111;117;116;112;117;116;58;32;34;71;111;111;100;32;109;111;114;110;105;110;103;34; - - - 111;117;116;112;117;116;58;32;34;84;111;100;97;121;32;119;101;32;108;101;97;114;110;32;115;116;114;117;99;116;117;114;101;32;99;104;97;114;116;115;46;34; - - -`,Y8=` - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;32;87;111;114;108;100;34; - - -`,K8=` - - - 111;117;116;112;117;116;58;32;34;72;101;108;108;111;34; - - - 111;117;116;112;117;116;58;32;34;73;32;97;109;32;108;101;97;114;110;105;110;103;32;115;116;114;117;99;116;117;114;101;32;99;104;97;114;116;115;34; - - -`,X8=` - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;104;111;112;112;105;110;103;45;110;101;119;46;116;120;116;34;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;108;105;110;101;41; - - - 99;111;117;110;116;43;43; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;67;97;107;101;34;41; - - - 99;111;117;110;116;43;43; - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;111;117;110;116;32;43;32;34;32;108;105;110;101;115;32;115;97;118;101;100;34; - - -`,eS=` - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;104;111;112;112;105;110;103;45;110;101;119;46;116;120;116;34;41; - - -`,tS=` - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;49; - - - 102;105;108;101;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;102;105;108;101;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;98;101;114;32;43;32;34;46;32;34;32;43;32;108;105;110;101; - - - 110;117;109;98;101;114;43;43; - - - - - 102;105;108;101;46;99;108;111;115;101;40;41; - - -`,nS=` - - - 83;99;97;110;110;101;114;32;102;105;108;101;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;115;104;111;112;112;105;110;103;46;116;120;116;34;41;41; - - -`,iS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 108;105;110;101;46;99;111;110;116;97;105;110;115;40;34;70;111;120;34;41; - - - 99;111;117;110;116;43;43; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;70;111;120;58;32;34;32;43;32;99;111;117;110;116; - - -`,aS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,sS=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;108;111;110;103;101;115;116;32;61;32;108;105;110;101;115;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;101;115;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;108;111;110;103;101;115;116;46;108;101;110;103;116;104;40;41; - - - 108;111;110;103;101;115;116;32;61;32;108;105;110;101;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;76;111;110;103;101;115;116;32;101;110;116;114;121;58;32;34;32;43;32;108;111;110;103;101;115;116; - - -`,rS=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;111;111;107;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - -`,oS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;98;101;115;116;78;97;109;101;32;61;32;34;34; - - - 105;110;116;32;98;101;115;116;71;114;97;100;101;32;61;32;55; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;103;114;97;100;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;51;93;41; - - - 103;114;97;100;101;32;60;32;98;101;115;116;71;114;97;100;101; - - - 98;101;115;116;71;114;97;100;101;32;61;32;103;114;97;100;101; - - - 98;101;115;116;78;97;109;101;32;61;32;112;97;114;116;115;91;48;93; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;66;101;115;116;32;103;114;97;100;101;32;105;110;32;69;110;103;108;105;115;104;58;32;34;32;43;32;98;101;115;116;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;71;114;97;100;101;32;43;32;34;41;34; - - -`,lS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,uS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;117;109;32;61;32;48; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 115;117;109;32;61;32;115;117;109;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;115;91;49;93;41; - - - 99;111;117;110;116;43;43; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;97;116;104;115;32;97;118;101;114;97;103;101;58;32;34;32;43;32;115;117;109;32;47;32;99;111;117;110;116; - - -`,mS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;99;108;97;115;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,cS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 108;105;110;101;46;99;111;110;116;97;105;110;115;40;34;92;34;110;97;109;101;92;34;34;41; - - - 105;110;116;32;115;116;97;114;116;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;32;61;32;108;105;110;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 111;117;116;112;117;116;58;32;108;105;110;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;41; - - - - - 248; - - - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - -`,dS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,hS=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;111;120;34;125; - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;105;110;116;115;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;110;116;114;121;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;110;97;109;101;115;91;105;93;32;43;32;34;92;34;44;32;92;34;112;111;105;110;116;115;92;34;58;32;34;32;43;32;112;111;105;110;116;115;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;97;109;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;121;32;61;32;101;110;116;114;121;32;43;32;34;44;34; - - - - - 248; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;101;110;116;114;121;41; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;115;46;108;101;110;103;116;104;32;43;32;34;32;101;110;116;114;105;101;115;32;115;97;118;101;100;34; - - -`,gS=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;111;120;34;125; - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;105;110;116;115;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - -`,pS=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;108;115;45;110;101;119;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;115;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;110;116;114;121;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;115;91;48;93;32;43;32;34;92;34;44;32;92;34;115;112;101;99;105;101;115;92;34;58;32;92;34;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;92;34;44;32;92;34;97;103;101;92;34;58;32;34;32;43;32;112;97;114;116;115;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;108;105;110;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;121;32;61;32;101;110;116;114;121;32;43;32;34;44;34; - - - - - 248; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;101;110;116;114;121;41; - - - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;108;105;110;101;115;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;97;110;105;109;97;108;115;32;115;97;118;101;100;34; - - -`,fS=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;101;115;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;108;115;45;110;101;119;46;106;115;111;110;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - -`,bS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;101;110;116;101;110;99;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 108;105;110;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;46;112;114;105;110;116;108;110;40;112;97;114;116;115;91;48;93;32;43;32;34;32;40;34;32;43;32;112;97;114;116;115;91;49;93;32;43;32;34;41;32;105;115;32;34;32;43;32;112;97;114;116;115;91;50;93;32;43;32;34;32;121;101;97;114;115;32;111;108;100;46;34;41; - - - 99;111;117;110;116;43;43; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;111;117;110;116;32;43;32;34;32;115;101;110;116;101;110;99;101;115;32;115;97;118;101;100;34; - - -`,xS=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;114;101;97;100;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;108;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;111;117;116;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;101;110;116;101;110;99;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;101;32;61;32;114;101;97;100;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 114;101;97;100;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;46;99;108;111;115;101;40;41; - - -`,vS=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;116;105;99;107;101;116;115;63;34; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;99;111;117;110;116;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,wS=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;117;110;116;32;34;72;111;119;32;109;97;110;121;32;116;105;99;107;101;116;115;63;34; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;99;111;117;110;116;32;42;32;56;46;53; - - -`,yS=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,zS=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;70;105;114;115;116;32;110;117;109;98;101;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;99;111;110;100;32;110;117;109;98;101;114;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,IS=` - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;116;101;109;112;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;116;101;109;112; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,kS=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;100;115;91;105;93;32;34;69;110;116;101;114;32;97;32;99;97;114;100;34; - - - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;60;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - -`,SS=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;100;115;91;105;93;32;34;69;110;116;101;114;32;97;32;99;97;114;100;34; - - - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;62;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - -`,TS=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;32;97;114;101;32;121;111;117;32;108;111;111;107;105;110;103;32;102;111;114;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;105;115;111;110;115;32;61;32;48; - - - 105;110;116;32;108;101;102;116;32;61;32;48; - - - 105;110;116;32;114;105;103;104;116;32;61;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;105;100;100;108;101;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;108;101;102;116;32;60;61;32;114;105;103;104;116; - - - 109;105;100;100;108;101;32;61;32;40;108;101;102;116;32;43;32;114;105;103;104;116;41;32;47;32;50; - - - 99;111;109;112;97;114;105;115;111;110;115;32;61;32;99;111;109;112;97;114;105;115;111;110;115;32;43;32;49; - - - 99;97;114;100;115;91;109;105;100;100;108;101;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;109;105;100;100;108;101; - - - - - 99;97;114;100;115;91;109;105;100;100;108;101;93;32;60;32;116;97;114;103;101;116; - - - 108;101;102;116;32;61;32;109;105;100;100;108;101;32;43;32;49; - - - - - 114;105;103;104;116;32;61;32;109;105;100;100;108;101;32;45;32;49; - - - - - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;70;111;117;110;100;32;97;116;32;112;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;105;115;111;110;115;58;32;34;32;43;32;99;111;109;112;97;114;105;115;111;110;115; - - -`,_S=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;111;117;110;100;32;61;32;49; - 114;111;117;110;100;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 114;111;117;110;100;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104;32;45;32;114;111;117;110;100; - 105;43;43; - - - 99;97;114;100;115;91;105;93;32;62;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 105;110;116;32;116;101;109;112;32;61;32;99;97;114;100;115;91;105;93; - - - 99;97;114;100;115;91;105;93;32;61;32;99;97;114;100;115;91;105;32;43;32;49;93; - - - 99;97;114;100;115;91;105;32;43;32;49;93;32;61;32;116;101;109;112; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;100;115;91;105;93; - - - - -`,AS=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;97;114;100;115;91;105;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - -`,jS=` - - - 105;110;116;91;93;32;99;97;114;100;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;114;103;101;116;32;34;87;104;105;99;104;32;99;97;114;100;32;97;114;101;32;121;111;117;32;108;111;111;107;105;110;103;32;102;111;114;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;105;115;111;110;115;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;100;115;46;108;101;110;103;116;104; - - - 99;111;109;112;97;114;105;115;111;110;115;32;61;32;99;111;109;112;97;114;105;115;111;110;115;32;43;32;49; - - - 99;97;114;100;115;91;105;93;32;61;61;32;116;97;114;103;101;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;70;111;117;110;100;32;97;116;32;112;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;32;102;111;117;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;105;115;111;110;115;58;32;34;32;43;32;99;111;109;112;97;114;105;115;111;110;115; - - -`,CS=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;105;110;116;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;105;110;116;115;91;105;93; - - - - -`,DS=` - - - 105;110;116;91;93;32;112;111;105;110;116;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;105;110;116;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;105;110;116;115;91;105;93; - - - - -`,BS=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;105;110;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 112;111;105;110;116;115;91;105;93;32;61;32;112;111;105;110;116;115;91;105;93;32;43;32;49; - - - - -`,PS=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;53; - - - 100;111;117;98;108;101;73;116;40;110;117;109;98;101;114;41; - - - 111;117;116;112;117;116;58;32;34;73;110;32;116;104;101;32;109;97;105;110;32;112;114;111;103;114;97;109;58;32;110;117;109;98;101;114;32;61;32;34;32;43;32;110;117;109;98;101;114; - - - 98;111;111;108;101;97;110;91;93;32;114;111;119;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;66;101;102;111;114;101;58;32;34;32;43;32;114;111;119;91;48;93;32;43;32;34;44;32;34;32;43;32;114;111;119;91;49;93; - - - 99;108;101;97;114;40;114;111;119;41; - - - 111;117;116;112;117;116;58;32;34;65;102;116;101;114;58;32;34;32;43;32;114;111;119;91;48;93;32;43;32;34;44;32;34;32;43;32;114;111;119;91;49;93; - - -`,OS=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;97;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;101;97;116;115;91;105;93;32;61;32;102;97;108;115;101; - - - - -`,MS=` - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;73;110;32;116;104;101;32;115;117;98;112;114;111;103;114;97;109;58;32;120;32;61;32;34;32;43;32;120; - - -`,ZS=` - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - -`,LS=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - - - -`,WS=` - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - -`,ES=` - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - -`,NS=` - - - 105;110;116;32;115;116;97;116;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;49; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;50; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;48; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,FS=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,RS=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;72;101;108;108;111;33;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;32;97;109;32;86;105;115;117;66;111;116;34;41; - - -`,JS=` - - - 105;110;116;32;110;117;109;98;101;114;32;61;32;48; - 110;117;109;98;101;114;32;60;32;56; - 110;117;109;98;101;114;43;43; - - - 110;117;109;98;101;114;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 110;117;109;98;101;114;32;47;32;50;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 110;117;109;98;101;114;32;47;32;52;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,GS=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,HS=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,VS=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,qS=` - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;86;105;115;117;66;111;116;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;48;44;32;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;51;48;48;41; - - - - -`,US=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,QS=` - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;121;101;108;108;111;119;34;41; - - - 109;98;111;116;46;108;101;100;40;52;41;46;115;101;116;67;111;108;111;114;40;34;103;114;101;101;110;34;41; - - -`,$S=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - -`,YS=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;52;53; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;80;97;114;107;101;100;33;34;41; - - -`,KS=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,XS=` - - - 83;116;114;105;110;103;91;93;32;99;111;108;111;117;114;115;32;61;32;123;34;114;101;100;34;44;32;34;121;101;108;108;111;119;34;44;32;34;103;114;101;101;110;34;44;32;34;98;108;117;101;34;44;32;34;112;117;114;112;108;101;34;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;111;108;111;117;114;115;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;99;111;108;111;117;114;115;91;105;93;41; - - - - -`,eT=` - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - -`,tT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;114;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,nT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;70;111;114;119;97;114;100;40;41; - - - - -`,iT=` - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;83;116;101;112;115;41; - - -`,aT=` - - - 105;110;116;32;115;105;100;101;32;61;32;49;48; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 116;111;116;97;108;32;61;32;84;111;116;97;108;32;43;32;115;105;100;101; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;116;111;116;97;108;32;43;32;34;32;99;109;34;41; - - -`,sT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;41; - - -`,rT=` - - - 99;111;110;115;116;32;105;110;116;32;83;73;68;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,oT=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;78;105;103;104;116;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;117;115;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;97;121;34;41; - - - - - - -`,lT=` - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;83;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;116;101;112;115; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,uT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;116;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,mT=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;103;114;101;101;110;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - -`,cT=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;100;105;114;101;99;116;105;111;110;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - 100;105;114;101;99;116;105;111;110;32;61;32;73; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;114;101;99;116;105;111;110;58;32;34;32;43;32;100;105;114;101;99;116;105;111;110;41; - - -`,dT=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;116;104;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;114;111;115;115;32;108;105;110;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - -`,hT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;66;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - -`,gT=` - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;83;116;111;112;112;101;100;41; - - -`,pT=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,fT=` - - - 98;111;111;108;101;97;110;32;108;111;117;100;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;111;117;100;32;61;32;84;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;111;117;100; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;108;97;112;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;32;113;117;105;101;116;34;41; - - - - -`,bT=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;111;102;102;40;41; - - - - -`,xT=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;108;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,vT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;80;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,wT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;73;41;46;115;101;116;67;111;108;111;114;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - -`,yT=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;41; - - - - -`,zT=` - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 118;97;108;117;101;115;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;86;97;108;117;101;115;91;105;93; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;41; - - -`,IT=` - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;73;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - -`,kT=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;101;108;108;111;32;99;108;97;115;115;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;84;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - -`,ST=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;83;116;111;112;40;41; - - -`,TT=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;66;97;116;116;101;114;121;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;41; - - - - - 248; - - - - -`,_T=` - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;114;101;100;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;103;114;101;101;110; - - - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;121;101;108;108;111;119;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;114;101;100;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,AT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;114;111;116;97;116;105;111;110;115;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,jT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,CT=` - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,DT=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,BT=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - -`,PT=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;66;97;116;116;101;114;121;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 248; - - - - -`,OT=` - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;114;101;100;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;103;114;101;101;110;34; - - - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;121;101;108;108;111;119;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 115;116;97;116;101;32;61;32;34;114;101;100;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,MT=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;103;114;101;101;110;34;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - -`,ZT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,LT=` - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;105;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - -`,WT=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;44;32;50;48;48;41; - - - - -`,ET=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - -`,NT=` - - - 98;111;111;108;101;97;110;32;108;111;117;100;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;111;117;100;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;111;117;100; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;108;97;112;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;32;113;117;105;101;116;34;41; - - - - -`,FT=` - - - 99;111;110;115;116;32;105;110;116;32;83;73;68;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;83;73;68;69;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,RT=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,JT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;98;108;117;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - -`,GT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,HT=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,VT=` - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;116;101;112;115; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,qT=` - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;54;48; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;101;110;103;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;119;105;100;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,UT=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,QT=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;100;105;114;101;99;116;105;111;110;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - 100;105;114;101;99;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;114;101;99;116;105;111;110;58;32;34;32;43;32;100;105;114;101;99;116;105;111;110;41; - - -`,$T=` - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115;41; - - -`,YT=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,KT=` - - - 105;110;116;32;115;105;100;101;32;61;32;49;48; - - - 105;110;116;32;116;111;116;97;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 116;111;116;97;108;32;61;32;116;111;116;97;108;32;43;32;115;105;100;101; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;116;111;116;97;108;32;43;32;34;32;99;109;34;41; - - -`,XT=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;78;105;103;104;116;34;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;117;115;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;97;121;34;41; - - - - - - -`,e_=` - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;112;101;100;34;41; - - -`,t_=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;101;108;108;111;32;99;108;97;115;115;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;116;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - -`,n_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,i_=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,a_=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;60;32;53;48; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,s_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;32;48; - 105;45;45; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;101;100;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - - -`,r_=` - - - 99;111;110;115;116;32;105;110;116;32;68;73;83;84;65;78;67;69;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,o_=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - -`,l_=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,u_=` - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - -`,m_=` - - - 99;111;110;115;116;32;105;110;116;32;68;73;83;84;65;78;67;69;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;68;73;83;84;65;78;67;69; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,c_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,d_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;34;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;108;111;119;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;114;101;101;34;41; - - - - - - -`,h_=` - - - 83;116;114;105;110;103;32;115;116;97;116;101;32;61;32;34;100;114;105;118;101;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50;48; - 105;43;43; - - - 115;116;97;116;101;46;101;113;117;97;108;115;40;34;100;114;105;118;101;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 115;116;97;116;101;32;61;32;34;116;117;114;110;34; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;116;97;116;101;32;61;32;34;100;114;105;118;101;34; - - - - - - -`,g_=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 248; - - - - - - - - -`,p_=` - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;32;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;100;46;99;104;97;114;65;116;40;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,f_=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;101;100;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,b_=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;67;114;111;115;115;32;108;105;110;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - -`,x_=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 100;105;115;116;97;110;99;101;32;62;32;109;97;120; - - - 109;97;120;32;61;32;100;105;115;116;97;110;99;101; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;97;114;116;104;101;115;116;58;32;34;32;43;32;109;97;120;32;43;32;34;32;99;109;34;41; - - -`,v_=` - - - 105;110;116;91;93;32;116;111;110;101;115;32;61;32;123;50;54;50;44;32;50;57;52;44;32;51;51;48;44;32;51;52;57;44;32;51;57;50;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;111;110;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;116;111;110;101;115;91;105;93;44;32;51;48;48;41; - - - - -`,w_=` - - - 105;110;116;91;93;32;118;97;108;117;101;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 118;97;108;117;101;115;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;118;97;108;117;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;32;61;32;115;117;109;32;43;32;118;97;108;117;101;115;91;105;93; - - - - - 100;111;117;98;108;101;32;97;118;101;114;97;103;101;32;61;32;115;117;109;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;118;101;114;97;103;101;58;32;34;32;43;32;97;118;101;114;97;103;101;41; - - -`,y_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;54;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;104;105;116;101;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;111;102;102;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - -`,z_=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;53;53;44;32;50;53;53;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,I_=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;90;122;122;34;41; - - -`,k_=` - - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 108;101;97;118;101;32;116;104;101;32;108;111;111;112; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,S_=` - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41;32;124;124;32;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 248; - - - - -`,T_=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - -`,__=` - - - 115;113;117;97;114;101;40;51;48;41; - - - 115;113;117;97;114;101;40;53;48;41; - - -`,A_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - -`,j_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,C_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;53; - 106;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,D_=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - -`,B_=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;45;50;48; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;51;48;48;41; - - - - - 248; - - - - -`,P_=` - - - 105;110;116;32;115;116;101;112;115;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;116;101;112;115;32;61;32;115;116;101;112;115;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;101;112;115;58;32;34;32;43;32;115;116;101;112;115;41; - - -`,O_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,M_=` - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - - -`,Z_=` - - - 105;110;116;32;115;105;100;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 115;105;100;101;32;61;32;115;105;100;101;32;43;32;49;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,L_=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;56; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;105;32;42;32;49;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,W_=` - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,E_=` - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;61;32;51; - 114;117;110;100;101;43;43; - - - 114;117;110;100;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;79;110;32;121;111;117;114;32;109;97;114;107;115;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;116;32;115;101;116;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;111;33;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - -`,N_=` - - - 105;110;116;32;108;101;110;103;116;104;32;61;32;54;48; - - - 105;110;116;32;119;105;100;116;104;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;101;110;103;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;119;105;100;116;104;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;50;32;42;32;108;101;110;103;116;104;32;43;32;50;32;42;32;119;105;100;116;104; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - -`,F_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - - -`,R_=` - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;54;50;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;57;52;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;51;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;52;57;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;57;50;44;32;51;48;48;41; - - -`,J_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,G_=` - - - 105;110;116;32;108;101;102;116;32;61;32;57;57; - - - 105;110;116;32;114;105;103;104;116;32;61;32;57;57; - - - 108;101;102;116;32;62;61;32;50;48;32;124;124;32;114;105;103;104;116;32;62;61;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 108;101;102;116;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 114;105;103;104;116;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - -`,H_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;114;111;116;97;116;105;111;110;115;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,V_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - -`,q_=` - - - 105;110;116;32;115;105;100;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;105;100;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,U_=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;52; - 106;43;43; - - - 105;110;116;32;107;32;61;32;48; - 107;32;60;32;53; - 107;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,Q_=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,$_=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,Y_=` - - - 98;111;111;108;101;97;110;32;100;97;114;107;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 100;97;114;107;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 100;97;114;107; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;116;32;119;97;115;32;100;97;114;107;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;119;97;121;115;32;98;114;105;103;104;116;34;41; - - - - -`,K_=` - - - 105;110;116;32;100;105;115;116;97;110;99;101;32;61;32;99;101;110;116;105;109;101;116;114;101;115;40;51;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;100;105;115;116;97;110;99;101;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;105;115;116;97;110;99;101;58;32;34;32;43;32;100;105;115;116;97;110;99;101;32;43;32;34;32;99;109;34;41; - - -`,X_=` - - - 114;101;116;117;114;110;32;114;111;116;97;116;105;111;110;115;32;42;32;50;48; - - -`,eA=` - - - 109;98;111;116;46;114;101;115;101;116;84;105;109;101;114;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;84;105;109;101;114;40;41;32;60;32;50; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,tA=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;105;114;115;116;78;97;109;101;32;34;70;105;114;115;116;32;110;97;109;101;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;108;97;115;116;78;97;109;101;32;34;76;97;115;116;32;110;97;109;101;34; - - - 111;117;116;112;117;116;58;32;102;105;114;115;116;78;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;108;97;115;116;78;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,nA=` - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;66;105;108;108;121;103;111;97;116;34; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;110;100;101;120;79;102;40;34;108;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;108;97;115;116;73;110;100;101;120;79;102;40;34;108;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;66;73;76;76;89;71;79;65;84;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;114;101;112;108;97;99;101;40;34;66;105;108;108;121;34;44;32;34;78;97;110;110;121;34;41; - - - 111;117;116;112;117;116;58;32;34;66;97;97;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;97;103;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;97;103;101;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;97;103;101;41;32;43;32;34;32;121;101;97;114;115;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - -`,iA=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;69;110;116;101;114;32;97;32;119;111;114;100;34; - - - 83;116;114;105;110;103;32;98;97;99;107;119;97;114;100;115;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;119;111;114;100;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 98;97;99;107;119;97;114;100;115;32;61;32;98;97;99;107;119;97;114;100;115;32;43;32;119;111;114;100;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;98;97;99;107;119;97;114;100;115; - - -`,aA=` - - - 83;116;114;105;110;103;32;108;105;110;101;32;61;32;34;76;117;100;119;105;103;59;98;105;108;108;121;32;103;111;97;116;59;55;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;115;32;61;32;108;105;110;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;114;116;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;112;97;114;116;115;91;105;93; - - - - -`,sA=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;110;116;101;110;99;101;32;34;69;110;116;101;114;32;97;32;115;101;110;116;101;110;99;101;34; - - - 83;116;114;105;110;103;91;93;32;119;111;114;100;115;32;61;32;115;101;110;116;101;110;99;101;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;87;111;114;100;115;58;32;34;32;43;32;119;111;114;100;115;46;108;101;110;103;116;104; - - -`,rA=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;100;32;34;69;110;116;101;114;32;97;32;119;111;114;100;34; - - - 105;110;116;32;99;111;117;110;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 119;111;114;100;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;101;39; - - - 99;111;117;110;116;32;61;32;99;111;117;110;116;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;78;117;109;98;101;114;32;111;102;32;101;58;32;34;32;43;32;99;111;117;110;116; - - -`,oA=` - - - 83;116;114;105;110;103;32;119;111;114;100;32;61;32;34;66;105;108;108;121;103;111;97;116;34; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;48;44;32;53;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;115;117;98;115;116;114;105;110;103;40;53;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;105;110;100;101;120;79;102;40;34;103;111;97;116;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;100;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - -`,lA=` - - - 105;110;116;91;93;91;93;32;116;97;98;101;108;108;101;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;101;108;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;101;108;108;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;101;108;108;101;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;32;40;49;32;98;105;115;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;32;40;49;32;98;105;115;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;101;108;108;101;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - -`,uA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;114;101;105;32;61;32;48; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;115;105;116;122;101;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 105;110;116;32;112;108;97;116;122;32;61;32;48; - 112;108;97;116;122;32;60;32;115;105;116;122;101;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 112;108;97;116;122;43;43; - - - 33;115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 102;114;101;105;32;61;32;102;114;101;105;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;105;58;32;34;32;43;32;102;114;101;105; - - -`,mA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;50;93;91;51;93;32;61;32;116;114;117;101; - - -`,cA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;82;101;105;104;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;112;108;97;116;122;32;34;80;108;97;116;122;34; - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 111;117;116;112;117;116;58;32;34;83;99;104;111;110;32;118;101;114;107;97;117;102;116;34; - - - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;86;101;114;107;97;117;102;116;58;32;82;101;105;104;101;32;34;32;43;32;114;101;105;104;101;32;43;32;34;44;32;80;108;97;116;122;32;34;32;43;32;112;108;97;116;122; - - - - -`,dA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;82;101;105;104;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;112;108;97;116;122;32;34;80;108;97;116;122;34; - - -`,hA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;114;101;105;104;101;32;61;32;48; - 114;101;105;104;101;32;60;32;115;105;116;122;101;46;108;101;110;103;116;104; - 114;101;105;104;101;43;43; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;34; - - - 105;110;116;32;112;108;97;116;122;32;61;32;48; - 112;108;97;116;122;32;60;32;115;105;116;122;101;91;114;101;105;104;101;93;46;108;101;110;103;116;104; - 112;108;97;116;122;43;43; - - - 115;105;116;122;101;91;114;101;105;104;101;93;91;112;108;97;116;122;93; - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;88;34; - - - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - -`,gA=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;115;105;116;122;101;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 115;105;116;122;101;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 115;105;116;122;101;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;82;101;105;104;101;110;58;32;34;32;43;32;115;105;116;122;101;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;80;108;228;116;122;101;32;112;114;111;32;82;101;105;104;101;58;32;34;32;43;32;115;105;116;122;101;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;82;101;105;104;101;32;49;44;32;80;108;97;116;122;32;50;32;118;101;114;107;97;117;102;116;63;32;34;32;43;32;115;105;116;122;101;91;49;93;91;50;93; - - -`,pA=` - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 105;110;116;32;90;97;104;108;32;61;32;54; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;90;97;104;108; - - - 111;117;116;112;117;116;58;32;34;69;114;103;101;98;110;105;115;58;32;34;32;43;32;101;114;103;101;98;110;105;115; - - -`,fA=` - - - 105;110;116;32;122;97;104;108;32;61;32;52; - - - 105;110;116;32;90;97;104;108;32;61;32;54; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;90;65;72;76; - - - 111;117;116;112;117;116;58;32;34;69;114;103;101;98;110;105;115;58;32;34;32;43;32;101;114;103;101;98;110;105;115; - - -`,bA=` - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - -`,xA=` - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;32;43;32;34;32;115;34; - - -`,vA=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - -`,wA=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;65;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34;41; - - -`,yA=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;97;108;116;101;114;32;61;32;55; - - - 100;111;117;98;108;101;32;103;114;111;101;115;115;101;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - - 111;117;116;112;117;116;58;32;34;71;114;246;223;101;58;32;34;32;43;32;103;114;111;101;115;115;101;32;43;32;34;32;109;34; - - -`,zA=` - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;49; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - -`,IA=` - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;51; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;52; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - -`,kA=` - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;52; - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;51; - - -`,SA=` - - - 83;116;114;105;110;103;32;108;105;110;107;115;32;61;32;34;83;97;102;116;34; - - - 83;116;114;105;110;103;32;114;101;99;104;116;115;32;61;32;34;87;97;115;115;101;114;34; - - - 83;116;114;105;110;103;32;104;105;108;102;32;61;32;108;105;110;107;115; - - - 108;105;110;107;115;32;61;32;114;101;99;104;116;115; - - - 114;101;99;104;116;115;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;76;105;110;107;115;58;32;34;32;43;32;108;105;110;107;115; - - - 111;117;116;112;117;116;58;32;34;82;101;99;104;116;115;58;32;34;32;43;32;114;101;99;104;116;115; - - -`,TA=` - - - 83;116;114;105;110;103;32;108;105;110;107;115;32;61;32;34;83;97;102;116;34; - - - 83;116;114;105;110;103;32;114;101;99;104;116;115;32;61;32;34;87;97;115;115;101;114;34; - - - 111;117;116;112;117;116;58;32;34;76;105;110;107;115;58;32;34;32;43;32;108;105;110;107;115; - - - 111;117;116;112;117;116;58;32;34;82;101;99;104;116;115;58;32;34;32;43;32;114;101;99;104;116;115; - - -`,_A=` - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,AA=` - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;52; - - - 100;111;117;98;108;101;32;103;114;111;101;115;115;101;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;77;105;97;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;32;43;32;34;32;105;115;116;32;34;32;43;32;97;108;116;101;114;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34; - - - 111;117;116;112;117;116;58;32;34;71;114;246;223;101;58;32;34;32;43;32;103;114;111;101;115;115;101;32;43;32;34;32;109;34; - - -`,jA=` - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;49;51;53; - - - 105;110;116;32;115;116;117;110;100;101;110;32;61;32;109;105;110;117;116;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;109;105;110;117;116;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;115;116;117;110;100;101;110;32;43;32;34;32;83;116;117;110;100;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;77;105;110;117;116;101;110;34; - - -`,CA=` - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;101;105;115;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;80;114;101;105;115;32;61;32;97;110;122;97;104;108;32;42;32;112;114;101;105;115; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116;80;114;101;105;115;32;43;32;34;32;69;117;114;111;34; - - -`,DA=` - - - 105;110;116;32;103;117;116;104;97;98;101;110;32;61;32;50;48; - - - 105;110;116;32;112;114;101;105;115;32;61;32;55; - - - 103;117;116;104;97;98;101;110;32;61;32;103;117;116;104;97;98;101;110;32;45;32;112;114;101;105;115; - - - 111;117;116;112;117;116;58;32;34;220;98;114;105;103;58;32;34;32;43;32;103;117;116;104;97;98;101;110; - - -`,BA=` - - - 105;110;116;32;103;117;116;104;97;98;101;110;32;61;32;50;48; - - - 105;110;116;32;112;114;101;105;115;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;220;98;114;105;103;58;32;34;32;43;32;103;117;116;104;97;98;101;110; - - - 103;117;116;104;97;98;101;110;32;61;32;103;117;116;104;97;98;101;110;32;45;32;112;114;101;105;115; - - -`,PA=` - - - 105;110;116;32;112;114;101;105;115;32;61;32;57; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;112;114;101;105;115;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - -`,OA=` - - - 105;110;116;32;115;116;117;101;99;107;101;32;61;32;56; - - - 105;110;116;32;112;101;114;115;111;110;101;110;32;61;32;52; - - - 105;110;116;32;112;114;111;80;101;114;115;111;110;32;61;32;115;116;117;101;99;107;101;32;47;32;112;101;114;115;111;110;101;110; - - - 111;117;116;112;117;116;58;32;34;74;101;100;101;114;32;98;101;107;111;109;109;116;32;34;32;43;32;112;114;111;80;101;114;115;111;110;32;43;32;34;32;83;116;252;99;107;101;46;34; - - -`,MA=` - - - 105;110;116;32;115;116;117;101;99;107;101;32;61;32;56; - - - 105;110;116;32;112;101;114;115;111;110;101;110;32;61;32;52; - - - 105;110;116;32;112;114;111;80;101;114;115;111;110;32;61;32;115;116;117;101;99;107;101;32;47;32;112;101;114;115;111;110;101;110; - - - 111;117;116;112;117;116;58;32;34;74;101;100;101;114;32;98;101;107;111;109;109;116;32;34;32;43;32;112;114;111;112;101;114;115;111;110;32;43;32;34;32;83;116;252;99;107;101;46;34; - - -`,ZA=` - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;55; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;52; - - - 105;110;116;32;102;108;97;101;99;104;101;32;61;32;108;97;101;110;103;101;32;42;32;98;114;101;105;116;101; - - - 105;110;116;32;117;109;102;97;110;103;32;61;32;50;32;42;32;40;108;97;101;110;103;101;32;43;32;98;114;101;105;116;101;41; - - - 111;117;116;112;117;116;58;32;34;70;108;228;99;104;101;58;32;34;32;43;32;102;108;97;101;99;104;101; - - - 111;117;116;112;117;116;58;32;34;85;109;102;97;110;103;58;32;34;32;43;32;117;109;102;97;110;103; - - -`,LA=` - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;101;110;32;61;32;115;101;107;117;110;100;101;110;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;32;61;32;115;101;107;117;110;100;101;110;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;77;105;110;117;116;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;83;101;107;117;110;100;101;110;34; - - -`,WA=` - - - 105;110;116;32;115;101;107;117;110;100;101;110;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;101;110;32;43;32;34;32;77;105;110;117;116;101;110;32;117;110;100;32;34;32;43;32;114;101;115;116;32;43;32;34;32;83;101;107;117;110;100;101;110;34; - - -`,EA=` - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;53; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51; - - - 105;110;116;32;102;108;97;101;99;104;101;32;61;32;108;97;101;110;103;101;32;42;32;98;114;101;105;116;101; - - - 111;117;116;112;117;116;58;32;102;108;97;101;99;104;101; - - -`,NA=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;105;101;32;104;101;105;223;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;34;32;43;32;110;97;109;101;32;43;32;34;33;34; - - -`,FA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,RA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;40;97;32;43;32;98;41; - - -`,JA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;97;32;43;32;98; - - -`,GA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;228;99;104;115;116;101;115;32;74;97;104;114;32;98;105;115;116;32;100;117;32;34;32;43;32;40;97;108;116;101;114;32;43;32;49;41; - - -`,HA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;78;228;99;104;115;116;101;115;32;74;97;104;114;32;98;105;115;116;32;100;117;32;34;32;43;32;40;65;108;116;101;114;32;43;32;49;41; - - -`,VA=` - - - 105;110;112;117;116;58;32;105;110;116;32;106;97;104;114;101;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 105;110;116;32;116;97;103;101;32;61;32;106;97;104;114;101;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;115;105;110;100;32;101;116;119;97;32;34;32;43;32;116;97;103;101;32;43;32;34;32;84;97;103;101;46;34; - - -`,qA=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108; - - -`,UA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;107;116;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;107;116;58;32;34;32;43;32;112;114;111;100;117;107;116; - - -`,QA=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;105;115;116;32;118;111;108;108;106;228;104;114;105;103;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;105;115;116;32;110;111;99;104;32;110;105;99;104;116;32;118;111;108;108;106;228;104;114;105;103;46;34; - - - - -`,$A=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;63;34; - - - 122;97;104;108;32;60;32;48; - - - 122;97;104;108;32;61;32;48;32;45;32;122;97;104;108; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - -`,YA=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;63;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;103;101;114;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;101;114;97;100;101;34; - - - - -`,KA=` - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;53; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - -`,XA=` - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;53; - - - 112;117;110;107;116;101;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - -`,ej=` - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - -`,tj=` - - - 105;110;116;32;112;117;110;107;116;101;32;61;32;49;48; - - - 112;117;110;107;116;101;32;61;32;112;117;110;107;116;101;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;117;110;107;116;101;58;32;34;32;43;32;112;117;110;107;116;101; - - -`,nj=` - - - 105;110;116;32;112;114;101;105;115;32;61;32;56; - - - 105;110;116;32;107;97;101;115;101;32;61;32;50; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;40;112;114;101;105;115;32;43;32;107;97;101;115;101;41;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - -`,ij=` - - - 105;110;116;32;112;114;101;105;115;32;61;32;56; - - - 105;110;116;32;107;97;101;115;101;32;61;32;50; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;51; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;112;114;101;105;115;32;43;32;107;97;101;115;101;32;42;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - -`,aj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - -`,sj=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;105;115;32;34;80;114;101;105;115;32;105;110;32;69;117;114;111;63;34; - - - 112;114;101;105;115;32;62;32;53;48; - - - 112;114;101;105;115;32;61;32;112;114;101;105;115;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;90;117;32;122;97;104;108;101;110;58;32;34;32;43;32;112;114;101;105;115;32;43;32;34;32;69;117;114;111;34; - - -`,rj=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;63;34; - - - 110;111;116;101;32;62;61;32;49;32;38;38;32;110;111;116;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;103;252;108;116;105;103;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;252;108;116;105;103;34; - - - - -`,oj=` - - - 105;110;112;117;116;58;32;105;110;116;32;112;117;110;107;116;101;32;34;87;105;101;32;118;105;101;108;101;32;80;117;110;107;116;101;63;34; - - - 112;117;110;107;116;101;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;98;101;115;116;97;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;110;105;99;104;116;32;98;101;115;116;97;110;100;101;110;34; - - - - -`,lj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;69;117;114;111;34; - - - - -`,uj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;69;117;114;111;34; - - - - -`,mj=` - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;53; - - - 97;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;108;97;115;115;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;69;105;110;108;97;115;115;34; - - - - -`,cj=` - - - 105;110;116;32;97;108;116;101;114;32;61;32;49;53; - - - 65;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;108;97;115;115;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;69;105;110;108;97;115;115;34; - - - - -`,dj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;50;32;38;38;32;97;108;116;101;114;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;74;117;103;101;110;100;116;105;99;107;101;116;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;32;74;117;103;101;110;100;116;105;99;107;101;116;34; - - - - -`,hj=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;78;111;116;101;63;34; - - - 110;111;116;101;32;60;32;49;32;124;124;32;110;111;116;101;32;62;32;54; - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;78;111;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;101;32;34;32;43;32;110;111;116;101;32;43;32;34;32;105;115;116;32;103;252;108;116;105;103;46;34; - - - - -`,gj=` - - - 105;110;112;117;116;58;32;105;110;116;32;106;97;104;114;32;34;74;97;104;114;63;34; - - - 40;106;97;104;114;32;37;32;52;32;61;61;32;48;32;38;38;32;106;97;104;114;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;106;97;104;114;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;83;99;104;97;108;116;106;97;104;114;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;105;110;32;83;99;104;97;108;116;106;97;104;114;34; - - - - -`,pj=` - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;101;110;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;87;101;114;107;116;97;103;34; - - - - -`,fj=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;97;114;98;101;32;34;87;101;108;99;104;101;32;70;97;114;98;101;32;104;97;116;32;100;105;101;32;65;109;112;101;108;63;34; - - - 102;97;114;98;101; - - - 111;117;116;112;117;116;58;32;34;83;116;101;104;101;110;32;98;108;101;105;98;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;87;97;114;116;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;71;101;104;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;70;97;114;98;101;32;107;101;110;110;101;32;105;99;104;32;110;105;99;104;116;46;34; - - - - -`,bj=` - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;87;97;115;115;101;114;44;32;50;32;83;97;102;116;44;32;51;32;75;97;107;97;111;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;87;97;115;115;101;114;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;83;97;102;116;46;34; - - - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;75;97;107;97;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;84;97;115;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - -`,xj=` - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;87;101;108;99;104;101;114;32;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - -`,vj=` - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;103;32;34;87;101;108;99;104;101;114;32;84;97;103;32;40;49;32;98;105;115;32;55;41;63;34; - - - 116;97;103;32;61;61;32;54;32;38;38;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - -`,wj=` - - - 105;110;116;32;116;97;103;32;61;32;55; - - - 116;97;103;32;61;61;32;54;32;124;124;32;116;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - -`,yj=` - - - 105;110;116;32;116;97;103;32;61;32;55; - - - 116;97;103;32;61;61;32;54;32;124;124;32;84;97;103;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;99;104;117;108;102;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - -`,zj=` - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;117;110;100;101;32;34;87;105;101;32;115;112;228;116;32;105;115;116;32;101;115;32;40;110;117;114;32;100;105;101;32;83;116;117;110;100;101;41;63;34; - - - 115;116;117;110;100;101;32;62;61;32;56;32;38;38;32;115;116;117;110;100;101;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;83;99;104;117;108;101;34; - - - - - 111;117;116;112;117;116;58;32;34;70;114;101;105;122;101;105;116;34; - - - - -`,Ij=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;101;32;34;87;101;108;99;104;101;32;78;111;116;101;63;34; - - - 110;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;98;101;102;114;105;101;100;105;103;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;97;117;115;114;101;105;99;104;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;109;97;110;103;101;108;104;97;102;116;34; - - - - - 111;117;116;112;117;116;58;32;34;117;110;103;101;110;252;103;101;110;100;34; - - - - - 111;117;116;112;117;116;58;32;34;75;101;105;110;101;32;103;252;108;116;105;103;101;32;78;111;116;101;34; - - - - -`,kj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;122;101;105;99;104;101;110;32;34;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;48; - - - 122;101;105;99;104;101;110; - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 101;114;103;101;98;110;105;115;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;101;114;103;101;98;110;105;115; - - - - - 111;117;116;112;117;116;58;32;34;85;110;98;101;107;97;110;110;116;101;115;32;82;101;99;104;101;110;122;101;105;99;104;101;110;34; - - - - -`,Sj=` - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;87;97;115;115;101;114;44;32;50;32;83;97;102;116;44;32;51;32;75;97;107;97;111;34; - - - 119;97;104;108;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;87;97;115;115;101;114;46;34; - - - - - 119;97;104;108;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;83;97;102;116;46;34; - - - - - 119;97;104;108;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;72;105;101;114;32;105;115;116;32;100;101;105;110;32;75;97;107;97;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;101;115;101;32;84;97;115;116;101;32;103;105;98;116;32;101;115;32;110;105;99;104;116;46;34; - - - - - - - - -`,Tj=` - - - 84;97;115;115;101;32;97;117;102;32;100;101;110;32;84;105;115;99;104;32;115;116;101;108;108;101;110; - - - 90;117;99;107;101;114;32;103;101;119;252;110;115;99;104;116;63; - - - 90;117;99;107;101;114;32;104;105;110;101;105;110;103;101;98;101;110; - - - - - 248; - - - - - 84;101;101;32;110;111;99;104;32;122;117;32;104;101;105;223; - - - 49;32;77;105;110;117;116;101;32;119;97;114;116;101;110; - - - - - 84;101;101;32;116;114;105;110;107;101;110; - - -`,_j=` - - - 65;117;102;115;116;101;104;101;110; - - - 90;228;104;110;101;32;112;117;116;122;101;110; - - - 65;110;122;105;101;104;101;110; - - - 70;114;252;104;115;116;252;99;107;101;110; - - -`,Aj=` - - - 87;97;115;115;101;114;32;105;110;32;100;101;110;32;87;97;115;115;101;114;107;111;99;104;101;114;32;102;252;108;108;101;110; - - - 87;97;115;115;101;114;107;111;99;104;101;114;32;101;105;110;115;99;104;97;108;116;101;110; - - - 84;101;101;98;101;117;116;101;108;32;105;110;32;100;105;101;32;84;97;115;115;101;32;108;101;103;101;110; - - - 72;101;105;223;101;115;32;87;97;115;115;101;114;32;105;110;32;100;105;101;32;84;97;115;115;101;32;103;105;101;223;101;110; - - - 51;32;77;105;110;117;116;101;110;32;119;97;114;116;101;110; - - - 84;101;101;98;101;117;116;101;108;32;104;101;114;97;117;115;110;101;104;109;101;110; - - -`,jj=` - - - 82;101;103;110;101;116;32;101;115;63; - - - 82;101;103;101;110;115;99;104;105;114;109;32;101;105;110;112;97;99;107;101;110; - - - - - 83;111;110;110;101;110;98;114;105;108;108;101;32;101;105;110;112;97;99;107;101;110; - - - - - 76;111;115;103;101;104;101;110; - - -`,Cj=` - - - 84;101;108;108;101;114;32;110;105;99;104;116;32;108;101;101;114; - - - 69;105;110;101;110;32;66;105;115;115;101;110;32;101;115;115;101;110; - - - - - 84;101;108;108;101;114;32;97;98;114;228;117;109;101;110; - - -`,Dj=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;32;122;228;104;108;101;110;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,Bj=` - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,Pj=` - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,Oj=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;107;117;108;116;228;116;32;118;111;110;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;102;97;107;117;108;116;97;101;116;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;107;117;108;116;97;101;116;32;61;32;102;97;107;117;108;116;97;101;116;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;107;117;108;116;97;101;116; - - -`,Mj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;109;97;108;63;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;71;105;98;116;32;101;115;32;110;105;99;104;116;34; - - - - -`,Zj=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;119;97;104;108;32;34;49;32;112;108;117;115;44;32;50;32;109;105;110;117;115;44;32;51;32;109;97;108;63;34; - - - 119;97;104;108; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;71;105;98;116;32;101;115;32;110;105;99;104;116;34; - - - - -`,Lj=` - - - 105;110;116;32;110;111;116;101;32;61;32;50; - - - 110;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;119;101;105;116;101;114;32;252;98;101;110;34; - - - - -`,Wj=` - - - 105;110;116;32;110;111;116;101;32;61;32;50; - - - 78;111;116;101; - - - 111;117;116;112;117;116;58;32;34;115;101;104;114;32;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;103;117;116;34; - - - - - 111;117;116;112;117;116;58;32;34;119;101;105;116;101;114;32;252;98;101;110;34; - - - - -`,Ej=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;90;97;104;108;63;34; - - - 122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;101;105;110;115;34; - - - - - 111;117;116;112;117;116;58;32;34;122;119;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;100;114;101;105;34; - - - - - 111;117;116;112;117;116;58;32;34;107;101;110;110;101;32;105;99;104;32;110;105;99;104;116;34; - - - - -`,Nj=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;34; - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 122;97;104;108;32;60;32;49;48;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;99;104;114;105;116;116;101; - - -`,Fj=` - - - 105;110;116;32;122;97;104;108;32;61;32;48; - - - 122;97;104;108;32;60;32;49;32;124;124;32;122;97;104;108;32;62;32;49;48; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;118;111;110;32;49;32;98;105;115;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;68;97;110;107;101;33;34; - - -`,Rj=` - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,Jj=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;105;101;108;32;34;83;112;97;114;122;105;101;108;63;34; - - - 105;110;116;32;103;101;108;100;32;61;32;48; - - - 105;110;116;32;119;111;99;104;101;110;32;61;32;48; - - - 103;101;108;100;32;60;32;122;105;101;108; - - - 103;101;108;100;32;61;32;103;101;108;100;32;43;32;53; - - - 119;111;99;104;101;110;32;61;32;119;111;99;104;101;110;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;58;32;34;32;43;32;119;111;99;104;101;110; - - -`,Gj=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;105;101;108;32;34;83;112;97;114;122;105;101;108;63;34; - - - 105;110;116;32;103;101;108;100;32;61;32;48; - - - 105;110;116;32;119;111;99;104;101;110;32;61;32;48; - - - 103;101;108;100;32;60;61;32;122;105;101;108; - - - 103;101;108;100;32;61;32;103;101;108;100;32;43;32;53; - - - 119;111;99;104;101;110;32;61;32;119;111;99;104;101;110;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;87;111;99;104;101;110;58;32;34;32;43;32;119;111;99;104;101;110; - - -`,Hj=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;122;97;104;108;32;61;32;50; - - - 122;97;104;108;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;43;32;50; - - - - -`,Vj=` - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,qj=` - - - 105;110;116;32;122;97;104;108;32;61;32;51; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;90;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,Uj=` - - - 105;110;116;32;110;111;116;101;32;61;32;48; - - - 110;111;116;101;32;60;32;49;32;124;124;32;110;111;116;101;32;62;32;54; - - - 105;110;112;117;116;58;32;110;111;116;101;32;34;78;111;116;101;32;118;111;110;32;49;32;98;105;115;32;54;34; - - - - - 111;117;116;112;117;116;58;32;34;68;101;105;110;101;32;78;111;116;101;58;32;34;32;43;32;110;111;116;101; - - -`,Qj=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;122;97;104;108;32;61;32;48; - - - 122;97;104;108;32;33;61;32;48; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;40;48;32;98;101;101;110;100;101;116;41;34; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,$j=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;83;116;97;114;116;122;97;104;108;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;33;34; - - -`,Yj=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;107;116;32;61;32;122;97;104;108;32;42;32;105; - - - 111;117;116;112;117;116;58;32;122;97;104;108;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;107;116; - - - - -`,Kj=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,Xj=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - -`,eC=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;49; - - - 122;97;104;108;32;61;32;122;97;104;108;32;47;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - -`,tC=` - - - 105;110;116;32;122;97;104;108;32;61;32;49; - - - 105;110;116;32;103;114;101;110;122;101;32;61;32;50;48; - - - 122;97;104;108;32;60;32;103;114;101;110;122;101; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - - -`,nC=` - - - 105;110;116;32;122;97;104;108;32;61;32;49; - - - 105;110;116;32;103;114;101;110;122;101;32;61;32;50;48; - - - 122;97;104;108;32;60;32;71;114;101;110;122;101; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - - -`,iC=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;60;32;49;48;48; - - - 122;97;104;108;32;61;32;122;97;104;108;32;42;32;50; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - - -`,aC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,sC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;87;105;101;32;118;105;101;108;101;32;90;101;105;108;101;110;63;34; - - - 105;110;116;32;122;101;105;108;101;32;61;32;49; - 122;101;105;108;101;32;60;61;32;110; - 122;101;105;108;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;116;101;114;110;32;61;32;49; - 115;116;101;114;110;32;60;61;32;122;101;105;108;101; - 115;116;101;114;110;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - -`,rC=` - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 122;97;104;108;32;37;32;50;32;61;61;32;48; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108; - - -`,oC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108; - - -`,lC=` - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;101;105;104;101;32;42;32;105; - - - - -`,uC=` - - - 105;110;112;117;116;58;32;105;110;116;32;114;101;105;104;101;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;114;101;105;104;101;32;42;32;105; - - - - -`,mC=` - - - 105;110;116;32;104;111;101;104;101;32;61;32;48; - - - 105;110;116;32;115;116;117;102;101;32;61;32;49; - 115;116;117;102;101;32;60;61;32;53; - 115;116;117;102;101;43;43; - - - 104;111;101;104;101;32;61;32;104;111;101;104;101;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;246;104;101;58;32;34;32;43;32;104;111;101;104;101;32;43;32;34;32;99;109;34; - - -`,cC=` - - - 105;110;116;32;115;116;117;102;101;32;61;32;49; - 115;116;117;102;101;32;60;61;32;53; - 115;116;117;102;101;43;43; - - - 104;111;101;104;101;32;61;32;104;111;101;104;101;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;72;246;104;101;58;32;34;32;43;32;104;111;101;104;101;32;43;32;34;32;99;109;34; - - -`,dC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - -`,hC=` - - - 105;110;112;117;116;58;32;105;110;116;32;98;114;101;105;116;101;32;34;66;114;101;105;116;101;34; - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;101;104;101;32;34;72;246;104;101;34; - - - 105;110;116;32;122;101;105;108;101;32;61;32;49; - 122;101;105;108;101;32;60;61;32;104;111;101;104;101; - 122;101;105;108;101;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;34; - - - 105;110;116;32;115;112;97;108;116;101;32;61;32;49; - 115;112;97;108;116;101;32;60;61;32;98;114;101;105;116;101; - 115;112;97;108;116;101;43;43; - - - 116;101;120;116;32;61;32;116;101;120;116;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - - - -`,gC=` - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;122;97;104;108;101;110;91;105;93; - - - - -`,pC=` - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;91;48;93; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;91;119;101;114;116;101;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;119;101;114;116;101;46;108;101;110;103;116;104; - - -`,fC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - -`,bC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;122;117;32;119;101;108;99;104;101;114;32;90;97;104;108;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - - - -`,xC=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - -`,vC=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - -`,wC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;87;105;101;32;118;105;101;108;101;32;83;116;117;102;101;110;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 122;101;105;108;101;32;61;32;122;101;105;108;101;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;122;101;105;108;101; - - - - -`,yC=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;97;109;101;110;91;105;93;32;34;78;97;109;101;32;101;105;110;103;101;98;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;69;114;115;116;101;114;32;78;97;109;101;58;32;34;32;43;32;110;97;109;101;110;91;48;93; - - -`,zC=` - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;122;97;104;108;101;110;91;105;93;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;105;32;61;32;122;97;104;108;101;110;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;122;97;104;108;101;110;91;105;93; - - - - -`,IC=` - - - 105;110;116;91;93;32;122;97;104;108;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;122;97;104;108;101;110;91;105;93;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;122;97;104;108;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;97;104;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108;101;110;91;105;93; - - - 122;97;104;108;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;122;97;104;108;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - -`,kC=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;117;110;107;116;101;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - 112;117;110;107;116;101;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;117;110;107;116;101;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - - 111;117;116;112;117;116;58;32;34;77;97;120;105;109;117;109;58;32;34;32;43;32;109;97;120; - - -`,SC=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - 122;97;104;108;32;33;61;32;48; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - 105;110;112;117;116;58;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,TC=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;32;40;48;32;61;32;69;110;100;101;41;34; - - - 122;97;104;108;32;61;61;32;48; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;122;97;104;108; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,_C=` - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;101;110;103;101;32;34;76;228;110;103;101;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;114;101;105;116;101;32;34;66;114;101;105;116;101;63;34; - - - 105;110;116;32;102;32;61;32;102;108;97;101;99;104;101;40;108;97;101;110;103;101;44;32;98;114;101;105;116;101;41; - - - 111;117;116;112;117;116;58;32;34;70;108;228;99;104;101;58;32;34;32;43;32;102; - - -`,AC=` - - - 114;101;116;117;114;110;32;97;32;42;32;98; - - -`,jC=` - - - 122;101;105;103;101;66;101;103;114;117;101;115;115;117;110;103;40;41; - - - 115;116;101;108;108;101;70;114;97;103;101;110;40;41; - - - 122;101;105;103;101;69;114;103;101;98;110;105;115;40;41; - - -`,CC=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;110;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;246;99;104;115;116;101;32;84;101;109;112;101;114;97;116;117;114;58;32;34;32;43;32;109;97;120; - - -`,DC=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;101;110;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;101;110;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;72;246;99;104;115;116;101;32;84;101;109;112;101;114;97;116;117;114;58;32;34;32;43;32;109;97;120; - - -`,BC=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,PC=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;76;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,OC=` - - - 105;110;116;91;93;32;110;111;116;101;110;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;110;111;116;101;110;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,MC=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;90;97;104;108;63;34; - - - 105;110;116;32;113;32;61;32;113;117;97;100;114;97;116;40;122;97;104;108;41; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;81;117;97;100;114;97;116;32;105;115;116;32;34;32;43;32;113; - - -`,ZC=` - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;101;114;103;101;98;110;105;115; - - -`,LC=` - - - 122;101;105;103;101;84;105;116;101;108;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;109;101;32;34;87;105;101;32;104;101;105;223;116;32;100;117;63;34; - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;34;32;43;32;110;97;109;101; - - -`,WC=` - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;32;122;117;109;32;81;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;68;117;32;98;101;107;111;109;109;115;116;32;100;114;101;105;32;70;114;97;103;101;110;46;34; - - -`,EC=` - - - 122;101;105;103;101;83;117;109;109;101;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 122;101;105;103;101;83;117;109;109;101;40;120;44;32;120;32;43;32;49;41; - - -`,NC=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;117;109;109;101; - - -`,FC=` - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;119;101;114;116;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;33;34; - - -`,RC=` - - - 105;110;112;117;116;58;32;105;110;116;32;115;116;97;114;116;32;34;83;116;97;114;116;119;101;114;116;34; - - - 105;110;116;32;105;32;61;32;115;116;97;114;116; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;76;111;115;34; - - -`,JC=` - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;58;32;34;32;43;32;115;117;109;109;101; - - -`,GC=` - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;58;32;34;32;43;32;115;117;109;109;101; - - -`,HC=` - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;44;32;82;117;110;100;101;32;49;58;32;34;32;43;32;112;117;110;107;116;101;91;49;93;91;48;93; - - -`,VC=` - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;83;112;105;101;108;101;114;32;50;44;32;82;117;110;100;101;32;49;58;32;34;32;43;32;80;117;110;107;116;101;91;49;93;91;48;93; - - -`,qC=` - - - 105;110;116;91;93;91;93;32;112;117;110;107;116;101;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;117;110;107;116;101;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;117;110;107;116;101;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;117;109;109;101;58;32;34;32;43;32;115;117;109;109;101; - - -`,UC=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,QC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,$C=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;117;109;109;101; - - -`,YC=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;87;101;108;99;104;101;32;82;101;105;104;101;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;101;114;103;101;98;110;105;115;32;61;32;122;97;104;108;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;109;97;108;32;34;32;43;32;122;97;104;108;32;43;32;34;32;61;32;34;32;43;32;101;114;103;101;98;110;105;115; - - - - - 122;97;104;108;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;119;97;114;32;115;99;104;119;101;114;33;34; - - - - - 111;117;116;112;117;116;58;32;34;68;97;115;32;119;97;114;32;108;101;105;99;104;116;46;34; - - - - -`,KC=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;90;97;104;108;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;102;97;107;117;108;116;97;101;116;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;107;117;108;116;97;101;116;32;61;32;102;97;107;117;108;116;97;101;116;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;107;117;108;116;97;101;116; - - -`,XC=` - - - 83;116;114;105;110;103;32;98;114;105;101;102;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;98;114;105;101;102;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;98;114;105;101;102;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;90;101;105;108;101;110;58;32;34;32;43;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 122;101;105;108;101;110;91;105;93;46;99;111;110;116;97;105;110;115;40;34;70;117;99;104;115;34;41; - - - 111;117;116;112;117;116;58;32;34;70;117;99;104;115;32;105;110;32;90;101;105;108;101;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - -`,eD=` - - - 83;116;114;105;110;103;32;98;114;105;101;102;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;98;114;105;101;102;46;116;120;116;34;41;41; - - -`,tD=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,nD=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;63;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - -`,iD=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;104;97;116;32;34;32;43;32;110;97;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;66;117;99;104;115;116;97;98;101;110;34; - - -`,aD=` - - - 83;116;114;105;110;103;32;110;97;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;97;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;104;97;116;32;34;32;43;32;110;97;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;66;117;99;104;115;116;97;98;101;110;34; - - -`,sD=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;101;105;110;103;97;98;101;32;34;80;97;115;115;119;111;114;116;63;34; - - - 101;105;110;103;97;98;101;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;87;105;108;108;107;111;109;109;101;110;33;34; - - - - - 111;117;116;112;117;116;58;32;34;70;97;108;115;99;104;101;115;32;80;97;115;115;119;111;114;116;34; - - - - -`,rD=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;105;110;116;114;105;116;116;32;102;114;101;105;34; - - - - - 97;108;116;101;114;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;75;105;110;100;101;114;116;105;99;107;101;116;58;32;52;32;69;117;114;111;34; - - - - - 111;117;116;112;117;116;58;32;34;69;114;119;97;99;104;115;101;110;101;110;116;105;99;107;101;116;58;32;56;32;69;117;114;111;34; - - - - - - -`,oD=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;119;101;114;116;63;34; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;69;110;100;101;34; - - -`,lD=` - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;116;105;112;112;32;34;68;101;105;110;32;84;105;112;112;63;34; - - - 118;101;114;115;117;99;104;101;32;61;32;118;101;114;115;117;99;104;101;32;43;32;49; - - - 116;105;112;112;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;115;117;99;104;101;58;32;34;32;43;32;118;101;114;115;117;99;104;101; - - -`,uD=` - - - 105;110;116;32;118;101;114;115;117;99;104;101;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;116;105;112;112;32;34;68;101;105;110;32;84;105;112;112;63;34; - - - 116;105;112;112;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 118;101;114;115;117;99;104;101;32;61;32;118;101;114;115;117;99;104;101;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;115;117;99;104;101;58;32;34;32;43;32;118;101;114;115;117;99;104;101; - - -`,mD=` - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;101;105;115;32;34;80;114;101;105;115;63;32;40;48;32;61;32;69;110;100;101;41;34; - - - 112;114;101;105;115;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;112;114;101;105;115; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;65;114;116;105;107;101;108;58;32;34;32;43;32;97;110;122;97;104;108; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;115;117;109;109;101; - - -`,cD=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;101;114;32;34;87;105;101;32;97;108;116;32;98;105;115;116;32;100;117;63;34; - - - 97;108;116;101;114;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;68;117;32;100;97;114;102;115;116;32;119;228;104;108;101;110;46;34; - - - - - 105;110;116;32;106;97;104;114;101;32;61;32;49;54;32;45;32;97;108;116;101;114; - - - 111;117;116;112;117;116;58;32;34;78;111;99;104;32;34;32;43;32;106;97;104;114;101;32;43;32;34;32;74;97;104;114;101;32;119;97;114;116;101;110;46;34; - - - - -`,dD=` - - - 105;110;112;117;116;58;32;105;110;116;32;103;114;101;110;122;101;32;34;66;105;115;32;119;111;104;105;110;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;103;114;101;110;122;101; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,hD=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,gD=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,pD=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;103;101;115;117;99;104;116;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,fD=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;103;101;115;117;99;104;116;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;71;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - -`,bD=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;58;32;34;32;43;32;97;110;122;97;104;108; - - -`,xD=` - - - 111;117;116;112;117;116;58;32;34;71;117;116;101;110;32;77;111;114;103;101;110;34; - - - 111;117;116;112;117;116;58;32;34;72;101;117;116;101;32;108;101;114;110;101;110;32;119;105;114;32;83;116;114;117;107;116;111;103;114;97;109;109;101;46;34; - - -`,vD=` - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;32;87;101;108;116;34; - - -`,wD=` - - - 111;117;116;112;117;116;58;32;34;72;97;108;108;111;34; - - - 111;117;116;112;117;116;58;32;34;73;99;104;32;108;101;114;110;101;32;83;116;114;117;107;116;111;103;114;97;109;109;101;34; - - -`,yD=` - - - 105;110;112;117;116;58;32;105;110;116;32;122;97;104;108;32;34;83;116;97;114;116;122;97;104;108;63;34; - - - 122;97;104;108;32;62;32;48; - - - 111;117;116;112;117;116;58;32;122;97;104;108; - - - 122;97;104;108;32;61;32;122;97;104;108;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;116;97;114;116;33;34; - - -`,zD=` - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;101;105;110;107;97;117;102;45;110;101;117;46;116;120;116;34;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;122;101;105;108;101;41; - - - 97;110;122;97;104;108;43;43; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;75;117;99;104;101;110;34;41; - - - 97;110;122;97;104;108;43;43; - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108;32;43;32;34;32;90;101;105;108;101;110;32;103;101;115;112;101;105;99;104;101;114;116;34; - - -`,ID=` - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;101;105;110;107;97;117;102;45;110;101;117;46;116;120;116;34;41; - - -`,kD=` - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;109;101;114;32;61;32;49; - - - 100;97;116;101;105;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;100;97;116;101;105;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;109;101;114;32;43;32;34;46;32;34;32;43;32;122;101;105;108;101; - - - 110;117;109;109;101;114;43;43; - - - - - 100;97;116;101;105;46;99;108;111;115;101;40;41; - - -`,SD=` - - - 83;99;97;110;110;101;114;32;100;97;116;101;105;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;101;105;110;107;97;117;102;46;116;120;116;34;41;41; - - -`,TD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 122;101;105;108;101;46;99;111;110;116;97;105;110;115;40;34;70;117;99;104;115;34;41; - - - 97;110;122;97;104;108;43;43; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;70;117;99;104;115;58;32;34;32;43;32;97;110;122;97;104;108; - - -`,_D=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,AD=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;108;97;101;110;103;115;116;101;114;32;61;32;122;101;105;108;101;110;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 122;101;105;108;101;110;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;108;97;101;110;103;115;116;101;114;46;108;101;110;103;116;104;40;41; - - - 108;97;101;110;103;115;116;101;114;32;61;32;122;101;105;108;101;110;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;76;228;110;103;115;116;101;114;32;69;105;110;116;114;97;103;58;32;34;32;43;32;108;97;101;110;103;115;116;101;114; - - -`,jD=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;108;111;103;98;117;99;104;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - -`,CD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;98;101;115;116;101;114;78;97;109;101;32;61;32;34;34; - - - 105;110;116;32;98;101;115;116;101;78;111;116;101;32;61;32;55; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;110;111;116;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;51;93;41; - - - 110;111;116;101;32;60;32;98;101;115;116;101;78;111;116;101; - - - 98;101;115;116;101;78;111;116;101;32;61;32;110;111;116;101; - - - 98;101;115;116;101;114;78;97;109;101;32;61;32;116;101;105;108;101;91;48;93; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;66;101;115;116;101;32;78;111;116;101;32;105;110;32;69;110;103;108;105;115;99;104;58;32;34;32;43;32;98;101;115;116;101;114;78;97;109;101;32;43;32;34;32;40;34;32;43;32;98;101;115;116;101;78;111;116;101;32;43;32;34;41;34; - - -`,DD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,BD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;116;101;105;108;101;91;49;93;41; - - - 97;110;122;97;104;108;43;43; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;83;99;104;110;105;116;116;32;77;97;116;104;101;58;32;34;32;43;32;115;117;109;109;101;32;47;32;97;110;122;97;104;108; - - -`,PD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;107;108;97;115;115;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,OD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 122;101;105;108;101;46;99;111;110;116;97;105;110;115;40;34;92;34;110;97;109;101;92;34;34;41; - - - 105;110;116;32;115;116;97;114;116;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;110;97;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;101;110;100;101;32;61;32;122;101;105;108;101;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;115;116;97;114;116;41; - - - 111;117;116;112;117;116;58;32;122;101;105;108;101;46;115;117;98;115;116;114;105;110;103;40;115;116;97;114;116;44;32;101;110;100;101;41; - - - - - 248; - - - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - -`,MD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,ZD=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;117;99;104;115;34;125; - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;117;110;107;116;101;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;105;110;116;114;97;103;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;110;97;109;101;110;91;105;93;32;43;32;34;92;34;44;32;92;34;112;117;110;107;116;101;92;34;58;32;34;32;43;32;112;117;110;107;116;101;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;97;109;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 101;105;110;116;114;97;103;32;61;32;101;105;110;116;114;97;103;32;43;32;34;44;34; - - - - - 248; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;101;105;110;116;114;97;103;41; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;97;109;101;110;46;108;101;110;103;116;104;32;43;32;34;32;69;105;110;116;114;228;103;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - -`,LD=` - - - 83;116;114;105;110;103;91;93;32;110;97;109;101;110;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;70;117;99;104;115;34;125; - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;117;110;107;116;101;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - -`,WD=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;116;105;101;114;101;45;110;101;117;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;110;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;105;110;116;114;97;103;32;61;32;34;32;32;123;92;34;110;97;109;101;92;34;58;32;92;34;34;32;43;32;116;101;105;108;101;91;48;93;32;43;32;34;92;34;44;32;92;34;97;114;116;92;34;58;32;92;34;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;92;34;44;32;92;34;97;108;116;101;114;92;34;58;32;34;32;43;32;116;101;105;108;101;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;122;101;105;108;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 101;105;110;116;114;97;103;32;61;32;101;105;110;116;114;97;103;32;43;32;34;44;34; - - - - - 248; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;101;105;110;116;114;97;103;41; - - - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;122;101;105;108;101;110;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;84;105;101;114;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - -`,ED=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;122;101;105;108;101;110;32;61;32;116;101;120;116;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;116;105;101;114;101;45;110;101;117;46;106;115;111;110;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - -`,ND=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;97;101;116;122;101;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 122;101;105;108;101;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 97;117;115;46;112;114;105;110;116;108;110;40;116;101;105;108;101;91;48;93;32;43;32;34;32;40;34;32;43;32;116;101;105;108;101;91;49;93;32;43;32;34;41;32;105;115;116;32;34;32;43;32;116;101;105;108;101;91;50;93;32;43;32;34;32;74;97;104;114;101;32;97;108;116;46;34;41; - - - 97;110;122;97;104;108;43;43; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;97;110;122;97;104;108;32;43;32;34;32;83;228;116;122;101;32;103;101;115;112;101;105;99;104;101;114;116;34; - - -`,FD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;115;101;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;105;101;114;101;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;97;117;115;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;115;97;101;116;122;101;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 122;101;105;108;101;32;61;32;108;101;115;101;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;101;115;101;114;46;99;108;111;115;101;40;41; - - - 97;117;115;46;99;108;111;115;101;40;41; - - -`,RD=` - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;34; - - - 118;101;114;105;102;105;99;97;114;73;100;97;100;101;40;41; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - -`,JD=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;109;97;105;111;114;32;100;101;32;105;100;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;109;101;110;111;114;32;100;101;32;105;100;97;100;101;34; - - - - -`,GD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;97;110;99;97;109;101;110;116;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;111;110;116;97;103;101;109;32;61;32;48; - - - 100;111;117;98;108;101;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;61;32;48; - - - 83;116;114;105;110;103;32;109;101;108;104;111;114;78;111;109;101;32;61;32;34;34; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;100;105;115;116;97;110;99;105;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 99;111;110;116;97;103;101;109;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;100;105;115;116;97;110;99;105;97; - - - 100;105;115;116;97;110;99;105;97;32;62;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97; - - - 109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;61;32;100;105;115;116;97;110;99;105;97; - - - 109;101;108;104;111;114;78;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;115;111;109;97;32;47;32;99;111;110;116;97;103;101;109; - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;99;105;112;97;110;116;101;115;58;32;34;32;43;32;99;111;110;116;97;103;101;109; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;58;32;34;32;43;32;109;101;100;105;97;32;43;32;34;32;109;34; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;118;101;110;99;101;100;111;114;46;116;120;116;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;86;101;110;99;101;100;111;114;58;32;34;32;43;32;109;101;108;104;111;114;78;111;109;101;32;43;32;34;32;40;34;32;43;32;109;101;108;104;111;114;68;105;115;116;97;110;99;105;97;32;43;32;34;32;109;41;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - -`,HD=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;108;97;110;99;97;109;101;110;116;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,VD=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;80;111;110;116;111;115;63;34; - - - 112;111;110;116;111;115;91;105;93;32;61;32;110;117;109;101;114;111; - - - - - 105;110;116;32;112;97;115;115;97;103;101;109;32;61;32;49; - 112;97;115;115;97;103;101;109;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 112;97;115;115;97;103;101;109;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104;32;45;32;112;97;115;115;97;103;101;109; - 105;43;43; - - - 112;111;110;116;111;115;91;105;93;32;60;32;112;111;110;116;111;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;112;111;110;116;111;115;91;105;93; - - - 112;111;110;116;111;115;91;105;93;32;61;32;112;111;110;116;111;115;91;105;32;43;32;49;93; - - - 112;111;110;116;111;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;108;117;103;97;114;32;61;32;105;32;43;32;49; - - - 111;117;116;112;117;116;58;32;108;117;103;97;114;32;43;32;34;46;32;34;32;43;32;112;111;110;116;111;115;91;105;93; - - - - -`,qD=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;105;110;116;101;105;114;111;34; - - - 83;116;114;105;110;103;32;98;105;110;97;114;105;111;32;61;32;34;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;110;117;109;101;114;111;32;37;32;50; - - - 98;105;110;97;114;105;111;32;61;32;114;101;115;116;111;32;43;32;98;105;110;97;114;105;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - - - 111;117;116;112;117;116;58;32;98;105;110;97;114;105;111; - - -`,UD=` - - - 99;104;97;114;91;93;32;97;108;102;97;98;101;116;111;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;81;117;101;32;112;97;108;97;118;114;97;32;40;115;243;32;109;97;105;250;115;99;117;108;97;115;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 99;104;97;114;32;90;32;61;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - 105;110;116;32;112;111;115;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;97;108;102;97;98;101;116;111;46;108;101;110;103;116;104; - 106;43;43; - - - 97;108;102;97;98;101;116;111;91;106;93;32;61;61;32;90; - - - 112;111;115;32;61;32;106; - - - - - 248; - - - - - - - 115;101;103;114;101;100;111;32;61;32;115;101;103;114;101;100;111;32;43;32;97;108;102;97;98;101;116;111;91;40;112;111;115;32;43;32;75;41;32;37;32;50;54;93; - - - - - 111;117;116;112;117;116;58;32;115;101;103;114;101;100;111; - - -`,QD=` - - - 99;104;97;114;91;93;32;97;108;102;97;98;101;116;111;32;61;32;123;39;65;39;44;32;39;66;39;44;32;39;67;39;44;32;39;68;39;44;32;39;69;39;44;32;39;70;39;44;32;39;71;39;44;32;39;72;39;44;32;39;73;39;44;32;39;74;39;44;32;39;75;39;44;32;39;76;39;44;32;39;77;39;44;32;39;78;39;44;32;39;79;39;44;32;39;80;39;44;32;39;81;39;44;32;39;82;39;44;32;39;83;39;44;32;39;84;39;44;32;39;85;39;44;32;39;86;39;44;32;39;87;39;44;32;39;88;39;44;32;39;89;39;44;32;39;90;39;125; - - -`,$D=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;81;117;101;32;112;97;108;97;118;114;97;32;40;115;243;32;109;97;105;250;115;99;117;108;97;115;41;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;105;103;111;32;61;32;40;105;110;116;41;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;105;103;111;32;61;32;40;99;111;100;105;103;111;32;45;32;54;53;32;43;32;75;41;32;37;32;50;54;32;43;32;54;53; - - - 115;101;103;114;101;100;111;32;61;32;115;101;103;114;101;100;111;32;43;32;40;99;104;97;114;41;32;99;111;100;105;103;111; - - - - - 111;117;116;112;117;116;58;32;115;101;103;114;101;100;111; - - -`,YD=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;101;103;114;101;100;111;32;34;81;117;97;108;32;233;32;111;32;116;101;120;116;111;32;99;105;102;114;97;100;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;75;32;34;68;101;115;108;111;99;97;114;32;113;117;97;110;116;97;115;32;112;111;115;105;231;245;101;115;63;34; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;101;103;114;101;100;111;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 105;110;116;32;99;111;100;105;103;111;32;61;32;40;105;110;116;41;32;115;101;103;114;101;100;111;46;99;104;97;114;65;116;40;105;41; - - - 99;111;100;105;103;111;32;61;32;40;99;111;100;105;103;111;32;45;32;54;53;32;45;32;75;32;43;32;50;54;41;32;37;32;50;54;32;43;32;54;53; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;40;99;104;97;114;41;32;99;111;100;105;103;111; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - -`,KD=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;112;97;115;115;111;115;32;61;32;48; - - - 110;117;109;101;114;111;32;33;61;32;49; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;51;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 112;97;115;115;111;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;80;97;115;115;111;115;58;32;34;32;43;32;112;97;115;115;111;115; - - -`,XD=` - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;97;115;32;110;111;116;97;115;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;113;117;97;110;116;105;100;97;100;101; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;110;111;116;97;34; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;111;116;97; - - - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;115;111;109;97;32;42;32;49;46;48;32;47;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;109;101;100;105;97; - - -`,eB=` - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;110;250;109;101;114;111;115;63;34; - - - 105;110;116;32;110;117;109;101;114;111;49;32;61;32;49; - - - 105;110;116;32;110;117;109;101;114;111;50;32;61;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;50; - - - 105;110;116;32;105;32;61;32;50; - 105;32;60;32;113;117;97;110;116;105;100;97;100;101; - 105;43;43; - - - 105;110;116;32;110;117;109;101;114;111;51;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;51; - - - 110;117;109;101;114;111;49;32;61;32;110;117;109;101;114;111;50; - - - 110;117;109;101;114;111;50;32;61;32;110;117;109;101;114;111;51; - - - - -`,tB=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;233;32;112;97;114;34; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;233;32;237;109;112;97;114;34; - - - - -`,nB=` - - - 99;111;110;115;116;32;100;111;117;98;108;101;32;73;86;65;32;61;32;48;46;50;51; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;99;111;83;101;109;73;118;97;32;34;80;114;101;231;111;32;115;101;109;32;73;86;65;63;34; - - - 100;111;117;98;108;101;32;105;109;112;111;115;116;111;32;61;32;112;114;101;99;111;83;101;109;73;118;97;32;42;32;73;86;65; - - - 100;111;117;98;108;101;32;112;114;101;99;111;67;111;109;73;118;97;32;61;32;112;114;101;99;111;83;101;109;73;118;97;32;43;32;105;109;112;111;115;116;111; - - - 111;117;116;112;117;116;58;32;105;109;112;111;115;116;111; - - - 111;117;116;112;117;116;58;32;112;114;101;99;111;67;111;109;73;118;97; - - -`,iB=` - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - 112;105;110;32;33;61;32;49;50;51;52;32;38;38;32;116;101;110;116;97;116;105;118;97;115;32;60;32;51; - - - 105;110;112;117;116;58;32;105;110;116;32;101;110;116;114;97;100;97;32;34;73;110;116;114;111;100;117;122;32;111;32;80;73;78;34; - - - 112;105;110;32;61;32;101;110;116;114;97;100;97; - - - 116;101;110;116;97;116;105;118;97;115;43;43; - - - - - 112;105;110;32;61;61;32;49;50;51;52; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;116;227;111;32;98;108;111;113;117;101;97;100;111;34; - - - - -`,aB=` - - - 99;111;110;115;116;32;105;110;116;32;80;73;78;32;61;32;49;50;51;52; - - - 105;110;116;32;112;105;110;32;61;32;48; - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - 112;105;110;32;33;61;32;80;73;78;32;38;38;32;116;101;110;116;97;116;105;118;97;115;32;60;32;51; - - - 105;110;112;117;116;58;32;112;105;110;32;34;73;110;116;114;111;100;117;122;32;111;32;80;73;78;34; - - - 116;101;110;116;97;116;105;118;97;115;43;43; - - - - - 112;105;110;32;61;61;32;80;73;78; - - - 111;117;116;112;117;116;58;32;34;80;73;78;32;99;111;114;114;101;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;97;114;116;227;111;32;98;108;111;113;117;101;97;100;111;34; - - - - -`,sB=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;49;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;50;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;100;111;114;32;34;79;112;101;114;97;100;111;114;32;40;43;44;32;45;32;111;117;32;42;41;34; - - - 111;112;101;114;97;100;111;114; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;45;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;49;32;42;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 111;117;116;112;117;116;58;32;34;79;112;101;114;97;100;111;114;32;100;101;115;99;111;110;104;101;99;105;100;111;34; - - - - -`,rB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;97;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;97;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;97;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;110;97;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;109;97;114;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 105;110;116;32;99;111;108;117;110;97;32;61;32;48; - 99;111;108;117;110;97;32;60;32;109;97;114;91;102;105;108;97;93;46;108;101;110;103;116;104; - 99;111;108;117;110;97;43;43; - - - 109;97;114;91;102;105;108;97;93;91;99;111;108;117;110;97;93; - - - 111;117;116;112;117;116;58;32;99;111;108;117;110;97;115;91;99;111;108;117;110;97;93;32;43;32;40;102;105;108;97;32;43;32;49;41; - - - - - 248; - - - - - - - - -`,oB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;109;97;114;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;53;93;91;53;93; - - - 109;97;114;91;48;93;91;49;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 109;97;114;91;48;93;91;51;93;32;61;32;116;114;117;101; - - - 109;97;114;91;50;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;51;93;91;52;93;32;61;32;116;114;117;101; - - - 109;97;114;91;52;93;91;48;93;32;61;32;116;114;117;101; - - - 83;116;114;105;110;103;91;93;32;99;111;108;117;110;97;115;32;61;32;123;34;65;34;44;32;34;66;34;44;32;34;67;34;44;32;34;68;34;44;32;34;69;34;125; - - -`,lB=` - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;97;116;111;32;34;81;117;101;32;112;114;97;116;111;63;32;40;49;44;32;50;32;111;117;32;51;41;34; - - - 112;114;97;116;111; - - - 111;117;116;112;117;116;58;32;34;83;97;108;97;100;97;32;103;114;101;103;97;34; - - - - - 111;117;116;112;117;116;58;32;34;66;97;99;97;108;104;97;117;32;224;32;66;114;225;115;34; - - - - - 111;117;116;112;117;116;58;32;34;87;114;97;112;32;100;101;32;102;97;108;97;102;101;108;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;101;32;117;109;32;110;250;109;101;114;111;32;118;225;108;105;100;111;34; - - - - -`,uB=` - - - 105;110;116;32;110;117;109;101;114;111;49;32;61;32;52; - - - 105;110;116;32;110;117;109;101;114;111;50;32;61;32;51; - - - 105;110;116;32;115;111;109;97;32;61;32;110;117;109;101;114;111;49;32;43;32;110;117;109;101;114;111;50; - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,mB=` - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;111;109;101;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;111;109;101;34; - - - - - 105;110;112;117;116;58;32;105;110;116;32;115;111;114;116;101;97;100;111;32;34;78;250;109;101;114;111;32;115;111;114;116;101;97;100;111;32;40;49;32;97;32;53;41;34; - - - 111;117;116;112;117;116;58;32;34;76;105;109;112;97;32;111;32;113;117;97;100;114;111;58;32;34;32;43;32;110;111;109;101;115;91;115;111;114;116;101;97;100;111;32;45;32;49;93; - - -`,cB=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;78;250;109;101;114;111;32;100;117;112;108;105;99;97;100;111;58;32;34;32;43;32;110;117;109;101;114;111; - - -`,dB=` - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;61;32;98;105;99;105;99;108;101;116;97;32;101;108;233;116;114;105;99;97;44;32;50;32;61;32;116;114;111;116;105;110;101;116;101;32;101;108;233;116;114;105;99;97;44;32;48;32;61;32;115;97;105;114;34; - - - 101;115;99;111;108;104;97;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - - -1; - - - - - 101;115;99;111;108;104;97;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;66;105;99;105;99;108;101;116;97;32;114;101;115;101;114;118;97;100;97;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;84;114;111;116;105;110;101;116;101;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;97;32;105;110;118;225;108;105;100;97;34; - - - - - - - - - - -`,hB=` - - - - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;61;32;98;105;99;105;99;108;101;116;97;32;101;108;233;116;114;105;99;97;44;32;50;32;61;32;116;114;111;116;105;110;101;116;101;32;101;108;233;116;114;105;99;97;44;32;48;32;61;32;115;97;105;114;34; - - - 101;115;99;111;108;104;97; - - - 111;117;116;112;117;116;58;32;34;65;100;101;117;115;34; - - - 83;97;105;114;32;100;111;32;109;101;110;117; - - - - - 111;117;116;112;117;116;58;32;34;66;105;99;105;99;108;101;116;97;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;84;114;111;116;105;110;101;116;101;32;114;101;115;101;114;118;97;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;104;97;32;105;110;118;225;108;105;100;97;34; - - - - - - -`,gB=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;99;97;98;117;108;97;114;105;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;99;101;114;116;97;115;32;61;32;48; - - - 105;110;116;32;99;111;110;116;97;103;101;109;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 111;117;116;112;117;116;58;32;34;67;111;109;111;32;115;101;32;100;105;122;32;34;32;43;32;112;97;114;116;101;115;91;48;93;32;43;32;34;32;101;109;32;105;110;103;108;234;115;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;114;101;115;112;111;115;116;97; - - - 99;111;110;116;97;103;101;109;43;43; - - - 114;101;115;112;111;115;116;97;46;101;113;117;97;108;115;40;112;97;114;116;101;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;34;67;101;114;116;111;33;34; - - - 99;101;114;116;97;115;43;43; - - - - - 111;117;116;112;117;116;58;32;34;69;114;114;97;100;111;44;32;233;32;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;46;34; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;99;101;114;116;97;115;32;43;32;34;32;100;101;32;34;32;43;32;99;111;110;116;97;103;101;109;32;43;32;34;32;99;101;114;116;97;115;34; - - -`,pB=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;118;111;99;97;98;117;108;97;114;105;111;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,fB=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;109;97;105;111;114;32;100;101;32;105;100;97;100;101;34; - - - - - 111;117;116;112;117;116;58;32;34;109;101;110;111;114;32;100;101;32;105;100;97;100;101;34; - - - - -`,bB=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - -`,xB=` - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;90;111;114;97;59;49;59;50;59;50;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - 105;110;116;32;109;97;116;101;109;97;116;105;99;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;109;97;116;101;109;97;116;105;99;97;32;43;32;34;32;97;32;77;97;116;101;109;225;116;105;99;97;34; - - -`,vB=` - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;123;92;34;110;111;109;101;92;34;58;32;92;34;90;111;114;97;92;34;44;32;92;34;101;115;112;101;99;105;101;92;34;58;32;92;34;99;97;98;114;97;92;34;44;32;92;34;105;100;97;100;101;92;34;58;32;54;125;44;34; - - - 105;110;116;32;105;110;105;99;105;111;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;110;111;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;102;105;109;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;105;110;105;99;105;111;41; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;108;105;110;104;97;46;115;117;98;115;116;114;105;110;103;40;105;110;105;99;105;111;44;32;102;105;109;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101; - - -`,wB=` - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - -`,yB=` - - - 105;110;116;91;93;91;93;32;116;97;98;101;108;97;32;61;32;110;101;119;32;105;110;116;91;49;48;93;91;49;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;97;98;101;108;97;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;116;97;98;101;108;97;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 116;97;98;101;108;97;91;105;93;91;106;93;32;61;32;40;105;32;43;32;49;41;32;42;32;40;106;32;43;32;49;41; - - - - - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;32;40;49;32;97;32;49;48;41;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;32;40;49;32;97;32;49;48;41;34; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;183;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;116;97;98;101;108;97;91;97;32;45;32;49;93;91;98;32;45;32;49;93; - - -`,zB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;116;32;108;105;118;114;101;115;32;61;32;48; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 105;110;116;32;108;117;103;97;114;32;61;32;48; - 108;117;103;97;114;32;60;32;108;117;103;97;114;101;115;91;102;105;108;97;93;46;108;101;110;103;116;104; - 108;117;103;97;114;43;43; - - - 33;108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 108;105;118;114;101;115;32;61;32;108;105;118;114;101;115;32;43;32;49; - - - - - 248; - - - - - - - - - 111;117;116;112;117;116;58;32;34;76;105;118;114;101;115;58;32;34;32;43;32;108;105;118;114;101;115; - - -`,IB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;48;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;50;93;91;51;93;32;61;32;116;114;117;101; - - -`,kB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;102;105;108;97;32;34;70;105;108;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;117;103;97;114;32;34;76;117;103;97;114;34; - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 111;117;116;112;117;116;58;32;34;74;225;32;118;101;110;100;105;100;111;34; - - - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;86;101;110;100;105;100;111;58;32;102;105;108;97;32;34;32;43;32;102;105;108;97;32;43;32;34;44;32;108;117;103;97;114;32;34;32;43;32;108;117;103;97;114; - - - - -`,SB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;51;93;32;61;32;116;114;117;101; - - - 105;110;112;117;116;58;32;105;110;116;32;102;105;108;97;32;34;70;105;108;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;117;103;97;114;32;34;76;117;103;97;114;34; - - -`,TB=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 105;110;116;32;102;105;108;97;32;61;32;48; - 102;105;108;97;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 102;105;108;97;43;43; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;34; - - - 105;110;116;32;108;117;103;97;114;32;61;32;48; - 108;117;103;97;114;32;60;32;108;117;103;97;114;101;115;91;102;105;108;97;93;46;108;101;110;103;116;104; - 108;117;103;97;114;43;43; - - - 108;117;103;97;114;101;115;91;102;105;108;97;93;91;108;117;103;97;114;93; - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;88;34; - - - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;79;34; - - - - - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - -`,_B=` - - - 98;111;111;108;101;97;110;91;93;91;93;32;108;117;103;97;114;101;115;32;61;32;110;101;119;32;98;111;111;108;101;97;110;91;51;93;91;53;93; - - - 108;117;103;97;114;101;115;91;48;93;91;50;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;49;93;32;61;32;116;114;117;101; - - - 108;117;103;97;114;101;115;91;49;93;91;50;93;32;61;32;116;114;117;101; - - - 111;117;116;112;117;116;58;32;34;70;105;108;97;115;58;32;34;32;43;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;76;117;103;97;114;101;115;32;112;111;114;32;102;105;108;97;58;32;34;32;43;32;108;117;103;97;114;101;115;91;48;93;46;108;101;110;103;116;104; - - - 111;117;116;112;117;116;58;32;34;70;105;108;97;32;49;44;32;108;117;103;97;114;32;50;32;118;101;110;100;105;100;111;63;32;34;32;43;32;108;117;103;97;114;101;115;91;49;93;91;50;93; - - -`,AB=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 105;110;116;32;78;117;109;101;114;111;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;78;117;109;101;114;111; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;97;100;111;58;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - -`,jB=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;52; - - - 105;110;116;32;78;117;109;101;114;111;32;61;32;54; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;78;85;77;69;82;79; - - - 111;117;116;112;117;116;58;32;34;82;101;115;117;108;116;97;100;111;58;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - -`,CB=` - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;34; - - -`,DB=` - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;34; - - -`,BB=` - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - -`,PB=` - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 115;121;115;116;101;109;46;111;117;116;46;112;114;105;110;116;108;110;40;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;73;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34;41; - - -`,OB=` - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;100;97;100;101;32;61;32;55; - - - 100;111;117;98;108;101;32;97;108;116;117;114;97;32;61;32;49;46;50; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;109;34; - - -`,MB=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;49; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - -`,ZB=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;51; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;52; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - -`,LB=` - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;52; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;51; - - -`,WB=` - - - 83;116;114;105;110;103;32;101;115;113;117;101;114;100;97;32;61;32;34;115;117;109;111;34; - - - 83;116;114;105;110;103;32;100;105;114;101;105;116;97;32;61;32;34;225;103;117;97;34; - - - 83;116;114;105;110;103;32;97;117;120;32;61;32;101;115;113;117;101;114;100;97; - - - 101;115;113;117;101;114;100;97;32;61;32;100;105;114;101;105;116;97; - - - 100;105;114;101;105;116;97;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;69;115;113;117;101;114;100;97;58;32;34;32;43;32;101;115;113;117;101;114;100;97; - - - 111;117;116;112;117;116;58;32;34;68;105;114;101;105;116;97;58;32;34;32;43;32;100;105;114;101;105;116;97; - - -`,EB=` - - - 83;116;114;105;110;103;32;101;115;113;117;101;114;100;97;32;61;32;34;115;117;109;111;34; - - - 83;116;114;105;110;103;32;100;105;114;101;105;116;97;32;61;32;34;225;103;117;97;34; - - - 111;117;116;112;117;116;58;32;34;69;115;113;117;101;114;100;97;58;32;34;32;43;32;101;115;113;117;101;114;100;97; - - - 111;117;116;112;117;116;58;32;34;68;105;114;101;105;116;97;58;32;34;32;43;32;100;105;114;101;105;116;97; - - -`,NB=` - - - 105;110;116;32;97;32;61;32;53; - - - 105;110;116;32;98;32;61;32;57; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,FB=` - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;52; - - - 100;111;117;98;108;101;32;97;108;116;117;114;97;32;61;32;49;46;54;50; - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;73;110;234;115;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;32;43;32;34;32;116;101;109;32;34;32;43;32;105;100;97;100;101;32;43;32;34;32;97;110;111;115;46;34; - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;109;34; - - -`,RB=` - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;49;51;53; - - - 105;110;116;32;104;111;114;97;115;32;61;32;109;105;110;117;116;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;109;105;110;117;116;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;104;111;114;97;115;32;43;32;34;32;104;111;114;97;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;109;105;110;117;116;111;115;34; - - -`,JB=` - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 100;111;117;98;108;101;32;112;114;101;99;111;32;61;32;50;46;53; - - - 100;111;117;98;108;101;32;112;114;101;99;111;84;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;112;114;101;99;111; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;112;114;101;99;111;84;111;116;97;108;32;43;32;34;32;101;117;114;111;115;34; - - -`,GB=` - - - 105;110;116;32;115;97;108;100;111;32;61;32;50;48; - - - 105;110;116;32;112;114;101;99;111;32;61;32;55; - - - 115;97;108;100;111;32;61;32;115;97;108;100;111;32;45;32;112;114;101;99;111; - - - 111;117;116;112;117;116;58;32;34;82;101;115;116;97;58;32;34;32;43;32;115;97;108;100;111; - - -`,HB=` - - - 105;110;116;32;115;97;108;100;111;32;61;32;50;48; - - - 105;110;116;32;112;114;101;99;111;32;61;32;55; - - - 111;117;116;112;117;116;58;32;34;82;101;115;116;97;58;32;34;32;43;32;115;97;108;100;111; - - - 115;97;108;100;111;32;61;32;115;97;108;100;111;32;45;32;112;114;101;99;111; - - -`,VB=` - - - 105;110;116;32;112;114;101;99;111;32;61;32;57; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;101;99;111;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,qB=` - - - 105;110;116;32;102;97;116;105;97;115;32;61;32;56; - - - 105;110;116;32;112;101;115;115;111;97;115;32;61;32;52; - - - 105;110;116;32;112;111;114;80;101;115;115;111;97;32;61;32;102;97;116;105;97;115;32;47;32;112;101;115;115;111;97;115; - - - 111;117;116;112;117;116;58;32;34;67;97;100;97;32;117;109;32;114;101;99;101;98;101;32;34;32;43;32;112;111;114;80;101;115;115;111;97;32;43;32;34;32;102;97;116;105;97;115;46;34; - - -`,UB=` - - - 105;110;116;32;102;97;116;105;97;115;32;61;32;56; - - - 105;110;116;32;112;101;115;115;111;97;115;32;61;32;52; - - - 105;110;116;32;112;111;114;80;101;115;115;111;97;32;61;32;102;97;116;105;97;115;32;47;32;112;101;115;115;111;97;115; - - - 111;117;116;112;117;116;58;32;34;67;97;100;97;32;117;109;32;114;101;99;101;98;101;32;34;32;43;32;112;111;114;112;101;115;115;111;97;32;43;32;34;32;102;97;116;105;97;115;46;34; - - -`,QB=` - - - 105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;61;32;55; - - - 105;110;116;32;108;97;114;103;117;114;97;32;61;32;52; - - - 105;110;116;32;97;114;101;97;32;61;32;99;111;109;112;114;105;109;101;110;116;111;32;42;32;108;97;114;103;117;114;97; - - - 105;110;116;32;112;101;114;105;109;101;116;114;111;32;61;32;50;32;42;32;40;99;111;109;112;114;105;109;101;110;116;111;32;43;32;108;97;114;103;117;114;97;41; - - - 111;117;116;112;117;116;58;32;34;193;114;101;97;58;32;34;32;43;32;97;114;101;97; - - - 111;117;116;112;117;116;58;32;34;80;101;114;237;109;101;116;114;111;58;32;34;32;43;32;112;101;114;105;109;101;116;114;111; - - -`,$B=` - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 105;110;116;32;109;105;110;117;116;111;115;32;61;32;115;101;103;117;110;100;111;115;32;47;32;54;48; - - - 105;110;116;32;114;101;115;116;111;32;61;32;115;101;103;117;110;100;111;115;32;37;32;54;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;117;116;111;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;101;103;117;110;100;111;115;34; - - -`,YB=` - - - 105;110;116;32;115;101;103;117;110;100;111;115;32;61;32;50;48;48; - - - 111;117;116;112;117;116;58;32;109;105;110;117;116;111;115;32;43;32;34;32;109;105;110;117;116;111;115;32;101;32;34;32;43;32;114;101;115;116;111;32;43;32;34;32;115;101;103;117;110;100;111;115;34; - - -`,KB=` - - - 105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;61;32;53; - - - 105;110;116;32;108;97;114;103;117;114;97;32;61;32;51; - - - 105;110;116;32;97;114;101;97;32;61;32;99;111;109;112;114;105;109;101;110;116;111;32;42;32;108;97;114;103;117;114;97; - - - 111;117;116;112;117;116;58;32;97;114;101;97; - - -`,XB=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;67;111;109;111;32;116;101;32;99;104;97;109;97;115;63;34; - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;34;32;43;32;110;111;109;101;32;43;32;34;33;34; - - -`,eP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,tP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;40;97;32;43;32;98;41; - - -`,nP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;97;32;43;32;98; - - -`,iP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;243;120;105;109;111;32;97;110;111;32;116;101;110;115;32;34;32;43;32;40;105;100;97;100;101;32;43;32;49;41; - - -`,aP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;243;120;105;109;111;32;97;110;111;32;116;101;110;115;32;34;32;43;32;40;73;100;97;100;101;32;43;32;49;41; - - -`,sP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;111;115;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;110;116;32;100;105;97;115;32;61;32;97;110;111;115;32;42;32;51;54;53; - - - 111;117;116;112;117;116;58;32;34;83;227;111;32;99;101;114;99;97;32;100;101;32;34;32;43;32;100;105;97;115;32;43;32;34;32;100;105;97;115;46;34; - - -`,rP=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;120;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;121;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 100;111;117;98;108;101;32;109;101;100;105;97;32;61;32;40;120;32;43;32;121;41;32;47;32;50; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;58;32;34;32;43;32;109;101;100;105;97; - - -`,oP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 105;110;116;32;112;114;111;100;117;116;111;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;80;114;111;100;117;116;111;58;32;34;32;43;32;112;114;111;100;117;116;111; - - -`,lP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;201;115;32;109;97;105;111;114;32;100;101;32;105;100;97;100;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;105;110;100;97;32;110;227;111;32;233;115;32;109;97;105;111;114;32;100;101;32;105;100;97;100;101;46;34; - - - - -`,uP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111;32;60;32;48; - - - 110;117;109;101;114;111;32;61;32;48;32;45;32;110;117;109;101;114;111; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - -`,mP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;112;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;237;109;112;97;114;34; - - - - -`,cP=` - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;53; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;45;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - -`,dP=` - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;53; - - - 112;111;110;116;111;115;32;61;32;51; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - -`,hP=` - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;49;48; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;98;111;110;117;115; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - -`,gP=` - - - 105;110;116;32;112;111;110;116;111;115;32;61;32;49;48; - - - 112;111;110;116;111;115;32;61;32;112;111;110;116;111;115;32;43;32;98;111;110;117;115; - - - 105;110;116;32;98;111;110;117;115;32;61;32;53; - - - 111;117;116;112;117;116;58;32;34;80;111;110;116;111;115;58;32;34;32;43;32;112;111;110;116;111;115; - - -`,pP=` - - - 105;110;116;32;112;114;101;99;111;32;61;32;56; - - - 105;110;116;32;113;117;101;105;106;111;32;61;32;50; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;40;112;114;101;99;111;32;43;32;113;117;101;105;106;111;41;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,fP=` - - - 105;110;116;32;112;114;101;99;111;32;61;32;56; - - - 105;110;116;32;113;117;101;105;106;111;32;61;32;50; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;51; - - - 105;110;116;32;116;111;116;97;108;32;61;32;112;114;101;99;111;32;43;32;113;117;101;105;106;111;32;42;32;113;117;97;110;116;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,bP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 97;32;62;32;98; - - - 111;117;116;112;117;116;58;32;97; - - - - - 111;117;116;112;117;116;58;32;98; - - - - -`,xP=` - - - 105;110;112;117;116;58;32;100;111;117;98;108;101;32;112;114;101;99;111;32;34;80;114;101;231;111;32;101;109;32;101;117;114;111;115;63;34; - - - 112;114;101;99;111;32;62;32;53;48; - - - 112;114;101;99;111;32;61;32;112;114;101;99;111;32;45;32;53; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;65;32;112;97;103;97;114;58;32;34;32;43;32;112;114;101;99;111;32;43;32;34;32;101;117;114;111;115;34; - - -`,vP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;78;111;116;97;63;34; - - - 110;111;116;97;32;62;61;32;49;32;38;38;32;110;111;116;97;32;60;61;32;53; - - - 111;117;116;112;117;116;58;32;34;118;225;108;105;100;97;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;118;225;108;105;100;97;34; - - - - -`,wP=` - - - 105;110;112;117;116;58;32;105;110;116;32;112;111;110;116;111;115;32;34;81;117;97;110;116;111;115;32;112;111;110;116;111;115;63;34; - - - 112;111;110;116;111;115;32;62;61;32;53;48; - - - 111;117;116;112;117;116;58;32;34;97;112;114;111;118;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;114;101;112;114;111;118;97;100;111;34; - - - - -`,yP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;101;32;105;100;97;100;101;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - -`,zP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;101;32;105;100;97;100;101;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;61;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 111;117;116;112;117;116;58;32;34;52;32;101;117;114;111;115;34; - - - - -`,IP=` - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;53; - - - 105;100;97;100;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;32;101;110;116;114;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;112;111;100;101;32;101;110;116;114;97;114;34; - - - - -`,kP=` - - - 105;110;116;32;105;100;97;100;101;32;61;32;49;53; - - - 73;100;97;100;101;32;62;61;32;49;54; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;32;101;110;116;114;97;114;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;112;111;100;101;32;101;110;116;114;97;114;34; - - - - -`,SP=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;50;32;38;38;32;105;100;97;100;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;106;111;118;101;109;34; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;32;98;105;108;104;101;116;101;32;106;111;118;101;109;34; - - - - -`,TP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;78;111;116;97;63;34; - - - 110;111;116;97;32;60;32;49;32;124;124;32;110;111;116;97;32;62;32;53; - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;110;111;116;97;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;32;110;111;116;97;32;34;32;43;32;110;111;116;97;32;43;32;34;32;233;32;118;225;108;105;100;97;46;34; - - - - -`,_P=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;111;32;34;65;110;111;63;34; - - - 40;97;110;111;32;37;32;52;32;61;61;32;48;32;38;38;32;97;110;111;32;37;32;49;48;48;32;33;61;32;48;41;32;124;124;32;97;110;111;32;37;32;52;48;48;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;34;65;110;111;32;98;105;115;115;101;120;116;111;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;233;32;97;110;111;32;98;105;115;115;101;120;116;111;34; - - - - -`,AP=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;68;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;70;105;109;32;100;101;32;115;101;109;97;110;97;34; - - - - - 111;117;116;112;117;116;58;32;34;68;105;97;32;250;116;105;108;34; - - - - -`,jP=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;99;111;114;32;34;68;101;32;113;117;101;32;99;111;114;32;101;115;116;225;32;111;32;115;101;109;225;102;111;114;111;63;34; - - - 99;111;114; - - - 111;117;116;112;117;116;58;32;34;80;97;114;97;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;112;101;114;97;34; - - - - - 111;117;116;112;117;116;58;32;34;65;118;97;110;231;97;34; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;99;111;110;104;101;231;111;32;101;115;116;97;32;99;111;114;46;34; - - - - -`,CP=` - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;225;103;117;97;44;32;50;32;115;117;109;111;44;32;51;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;34; - - - 101;115;99;111;108;104;97; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;97;32;116;117;97;32;225;103;117;97;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;115;117;109;111;46;34; - - - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;101;32;98;111;116;227;111;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - -`,DP=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;81;117;101;32;100;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - -`,BP=` - - - 105;110;112;117;116;58;32;105;110;116;32;100;105;97;32;34;81;117;101;32;100;105;97;32;40;49;32;97;32;55;41;63;34; - - - 100;105;97;32;61;61;32;54;32;38;38;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - -`,PP=` - - - 105;110;116;32;100;105;97;32;61;32;55; - - - 100;105;97;32;61;61;32;54;32;124;124;32;100;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - -`,OP=` - - - 105;110;116;32;100;105;97;32;61;32;55; - - - 100;105;97;32;61;61;32;54;32;124;124;32;68;105;97;32;61;61;32;55; - - - 111;117;116;112;117;116;58;32;34;115;101;109;32;101;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;101;115;99;111;108;97;34; - - - - -`,MP=` - - - 105;110;112;117;116;58;32;105;110;116;32;104;111;114;97;32;34;81;117;101;32;104;111;114;97;115;32;115;227;111;32;40;115;243;32;97;32;104;111;114;97;41;63;34; - - - 104;111;114;97;32;62;61;32;56;32;38;38;32;104;111;114;97;32;60;61;32;49;53; - - - 111;117;116;112;117;116;58;32;34;69;115;99;111;108;97;34; - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;111;32;108;105;118;114;101;34; - - - - -`,ZP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;111;116;97;32;34;81;117;101;32;110;111;116;97;63;34; - - - 110;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;105;110;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;105;110;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;115;117;102;105;99;105;101;110;116;101;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;78;111;116;97;32;105;110;118;225;108;105;100;97;34; - - - - -`,LP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;111;112;101;114;97;100;111;114;32;34;79;112;101;114;97;100;111;114;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;48; - - - 111;112;101;114;97;100;111;114; - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;45;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 114;101;115;117;108;116;97;100;111;32;61;32;97;32;42;32;98; - - - 111;117;116;112;117;116;58;32;114;101;115;117;108;116;97;100;111; - - - - - 111;117;116;112;117;116;58;32;34;79;112;101;114;97;100;111;114;32;100;101;115;99;111;110;104;101;99;105;100;111;34; - - - - -`,WP=` - - - 105;110;112;117;116;58;32;105;110;116;32;101;115;99;111;108;104;97;32;34;49;32;225;103;117;97;44;32;50;32;115;117;109;111;44;32;51;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;34; - - - 101;115;99;111;108;104;97;32;61;61;32;49; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;97;32;116;117;97;32;225;103;117;97;46;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;50; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;115;117;109;111;46;34; - - - - - 101;115;99;111;108;104;97;32;61;61;32;51; - - - 111;117;116;112;117;116;58;32;34;65;113;117;105;32;101;115;116;225;32;111;32;116;101;117;32;99;104;111;99;111;108;97;116;101;32;113;117;101;110;116;101;46;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;101;32;98;111;116;227;111;32;110;227;111;32;101;120;105;115;116;101;46;34; - - - - - - - - -`,EP=` - - - 80;244;114;32;117;109;97;32;99;104;225;118;101;110;97;32;110;97;32;109;101;115;97; - - - 81;117;101;114;32;97;231;250;99;97;114;63; - - - 74;117;110;116;97;114;32;97;231;250;99;97;114; - - - - - 248; - - - - - 67;104;225;32;97;105;110;100;97;32;100;101;109;97;115;105;97;100;111;32;113;117;101;110;116;101; - - - 69;115;112;101;114;97;114;32;49;32;109;105;110;117;116;111; - - - - - 66;101;98;101;114;32;111;32;99;104;225; - - -`,NP=` - - - 76;101;118;97;110;116;97;114; - - - 76;97;118;97;114;32;111;115;32;100;101;110;116;101;115; - - - 86;101;115;116;105;114; - - - 84;111;109;97;114;32;111;32;112;101;113;117;101;110;111;45;97;108;109;111;231;111; - - -`,FP=` - - - 69;110;99;104;101;114;32;97;32;99;104;97;108;101;105;114;97;32;99;111;109;32;225;103;117;97; - - - 76;105;103;97;114;32;97;32;99;104;97;108;101;105;114;97; - - - 80;244;114;32;117;109;97;32;115;97;113;117;101;116;97;32;100;101;32;99;104;225;32;110;97;32;99;104;225;118;101;110;97; - - - 68;101;105;116;97;114;32;225;103;117;97;32;113;117;101;110;116;101;32;110;97;32;99;104;225;118;101;110;97; - - - 69;115;112;101;114;97;114;32;51;32;109;105;110;117;116;111;115; - - - 84;105;114;97;114;32;97;32;115;97;113;117;101;116;97;32;100;101;32;99;104;225; - - -`,RP=` - - - 69;115;116;225;32;97;32;99;104;111;118;101;114;63; - - - 76;101;118;97;114;32;103;117;97;114;100;97;45;99;104;117;118;97; - - - - - 76;101;118;97;114;32;243;99;117;108;111;115;32;100;101;32;115;111;108; - - - - - 83;97;105;114; - - -`,JP=` - - - 80;114;97;116;111;32;110;227;111;32;118;97;122;105;111; - - - 68;97;114;32;117;109;97;32;103;97;114;102;97;100;97; - - - - - 76;101;118;97;110;116;97;114;32;111;32;112;114;97;116;111; - - -`,GP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;67;111;110;116;97;114;32;97;116;233;63;34; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,HP=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - -`,VP=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - -`,qP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;70;97;116;111;114;105;97;108;32;100;101;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;102;97;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;110; - - - 102;97;116;111;114;105;97;108;32;61;32;102;97;116;111;114;105;97;108;32;42;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;110;32;43;32;34;33;32;61;32;34;32;43;32;102;97;116;111;114;105;97;108; - - -`,UP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;111;112;99;97;111;32;34;49;32;109;97;105;115;44;32;50;32;109;101;110;111;115;44;32;51;32;118;101;122;101;115;63;34; - - - 111;112;99;97;111; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;45;32;98; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;120;105;115;116;101;34; - - - - -`,QP=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;111;112;99;97;111;32;34;49;32;109;97;105;115;44;32;50;32;109;101;110;111;115;44;32;51;32;118;101;122;101;115;63;34; - - - 111;112;99;97;111; - - - 111;117;116;112;117;116;58;32;97;32;43;32;98; - - - - - 111;117;116;112;117;116;58;32;98;32;45;32;97; - - - - - 111;117;116;112;117;116;58;32;97;32;42;32;98; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;120;105;115;116;101;34; - - - - -`,$P=` - - - 105;110;116;32;110;111;116;97;32;61;32;50; - - - 110;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;99;111;110;116;105;110;117;97;32;97;32;112;114;97;116;105;99;97;114;34; - - - - -`,YP=` - - - 105;110;116;32;110;111;116;97;32;61;32;50; - - - 78;111;116;97; - - - 111;117;116;112;117;116;58;32;34;109;117;105;116;111;32;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;98;111;109;34; - - - - - 111;117;116;112;117;116;58;32;34;99;111;110;116;105;110;117;97;32;97;32;112;114;97;116;105;99;97;114;34; - - - - -`,KP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;110;250;109;101;114;111;63;34; - - - 110;117;109;101;114;111; - - - 111;117;116;112;117;116;58;32;34;117;109;34; - - - - - 111;117;116;112;117;116;58;32;34;100;111;105;115;34; - - - - - 111;117;116;112;117;116;58;32;34;116;114;234;115;34; - - - - - 111;117;116;112;117;116;58;32;34;110;227;111;32;99;111;110;104;101;231;111;34; - - - - -`,XP=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;112;97;115;115;111;115;32;61;32;48; - - - 110;117;109;101;114;111;32;60;32;49;48;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 112;97;115;115;111;115;32;61;32;112;97;115;115;111;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;112;97;115;115;111;115; - - -`,eO=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;48; - - - 110;117;109;101;114;111;32;60;32;49;32;124;124;32;110;117;109;101;114;111;32;62;32;49;48; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;100;101;32;49;32;97;32;49;48;34; - - - - - 111;117;116;112;117;116;58;32;34;79;98;114;105;103;97;100;111;33;34; - - -`,tO=` - - - 105;110;116;32;105;32;61;32;49;48; - - - 105;32;60;32;53; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - -`,nO=` - - - 105;110;112;117;116;58;32;105;110;116;32;111;98;106;101;116;105;118;111;32;34;79;98;106;101;116;105;118;111;63;34; - - - 105;110;116;32;100;105;110;104;101;105;114;111;32;61;32;48; - - - 105;110;116;32;115;101;109;97;110;97;115;32;61;32;48; - - - 100;105;110;104;101;105;114;111;32;60;32;111;98;106;101;116;105;118;111; - - - 100;105;110;104;101;105;114;111;32;61;32;100;105;110;104;101;105;114;111;32;43;32;53; - - - 115;101;109;97;110;97;115;32;61;32;115;101;109;97;110;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;97;110;97;115;58;32;34;32;43;32;115;101;109;97;110;97;115; - - -`,iO=` - - - 105;110;112;117;116;58;32;105;110;116;32;111;98;106;101;116;105;118;111;32;34;79;98;106;101;116;105;118;111;63;34; - - - 105;110;116;32;100;105;110;104;101;105;114;111;32;61;32;48; - - - 105;110;116;32;115;101;109;97;110;97;115;32;61;32;48; - - - 100;105;110;104;101;105;114;111;32;60;61;32;111;98;106;101;116;105;118;111; - - - 100;105;110;104;101;105;114;111;32;61;32;100;105;110;104;101;105;114;111;32;43;32;53; - - - 115;101;109;97;110;97;115;32;61;32;115;101;109;97;110;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;83;101;109;97;110;97;115;58;32;34;32;43;32;115;101;109;97;110;97;115; - - -`,aO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;111;110;100;101;63;34; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;50; - - - 110;117;109;101;114;111;32;60;61;32;110; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;43;32;50; - - - - -`,sO=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - -`,rO=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;51; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;78;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;97;114;116;105;100;97;33;34; - - -`,oO=` - - - 105;110;116;32;110;111;116;97;32;61;32;48; - - - 110;111;116;97;32;60;32;49;32;124;124;32;110;111;116;97;32;62;32;53; - - - 105;110;112;117;116;58;32;110;111;116;97;32;34;78;111;116;97;32;100;101;32;49;32;97;32;53;34; - - - - - 111;117;116;112;117;116;58;32;34;65;32;116;117;97;32;110;111;116;97;58;32;34;32;43;32;110;111;116;97; - - -`,lO=` - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;48; - - - 110;117;109;101;114;111;32;33;61;32;48; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;40;48;32;116;101;114;109;105;110;97;41;34; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,uO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;110; - 105;32;62;61;32;49; - 105;45;45; - - - 111;117;116;112;117;116;58;32;105; - - - - - 111;117;116;112;117;116;58;32;34;74;225;33;34; - - -`,mO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;110;116;32;112;114;111;100;117;116;111;32;61;32;110;117;109;101;114;111;32;42;32;105; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;32;120;32;34;32;43;32;105;32;43;32;34;32;61;32;34;32;43;32;112;114;111;100;117;116;111; - - - - -`,cO=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,dO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - -`,hO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;49; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;47;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - -`,gO=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;101;32;61;32;50;48; - - - 110;117;109;101;114;111;32;60;32;108;105;109;105;116;101; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - - -`,pO=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 105;110;116;32;108;105;109;105;116;101;32;61;32;50;48; - - - 110;117;109;101;114;111;32;60;32;76;105;109;105;116;101; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - - -`,fO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;78;250;109;101;114;111;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;60;32;49;48;48; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;42;32;50; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - - -`,bO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,xO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;81;117;97;110;116;97;115;32;108;105;110;104;97;115;63;34; - - - 105;110;116;32;108;105;110;104;97;32;61;32;49; - 108;105;110;104;97;32;60;61;32;110; - 108;105;110;104;97;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;101;115;116;114;101;108;97;32;61;32;49; - 101;115;116;114;101;108;97;32;60;61;32;108;105;110;104;97; - 101;115;116;114;101;108;97;43;43; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - - - -`,vO=` - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 110;117;109;101;114;111;32;37;32;50;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101; - - -`,wO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101; - - -`,yO=` - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;98;117;97;100;97;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;116;97;98;117;97;100;97;32;42;32;105; - - - - -`,zO=` - - - 105;110;112;117;116;58;32;105;110;116;32;116;97;98;117;97;100;97;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;49;48; - 105;43;43; - - - 111;117;116;112;117;116;58;32;116;97;98;117;97;100;97;32;42;32;105; - - - - -`,IO=` - - - 105;110;116;32;97;108;116;117;114;97;32;61;32;48; - - - 105;110;116;32;100;101;103;114;97;117;32;61;32;49; - 100;101;103;114;97;117;32;60;61;32;53; - 100;101;103;114;97;117;43;43; - - - 97;108;116;117;114;97;32;61;32;97;108;116;117;114;97;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;99;109;34; - - -`,kO=` - - - 105;110;116;32;100;101;103;114;97;117;32;61;32;49; - 100;101;103;114;97;117;32;60;61;32;53; - 100;101;103;114;97;117;43;43; - - - 97;108;116;117;114;97;32;61;32;97;108;116;117;114;97;32;43;32;50;48; - - - - - 111;117;116;112;117;116;58;32;34;65;108;116;117;114;97;58;32;34;32;43;32;97;108;116;117;114;97;32;43;32;34;32;99;109;34; - - -`,SO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;42;32;105; - - - - -`,TO=` - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;114;103;117;114;97;32;34;76;97;114;103;117;114;97;34; - - - 105;110;112;117;116;58;32;105;110;116;32;97;108;116;117;114;97;32;34;65;108;116;117;114;97;34; - - - 105;110;116;32;108;105;110;104;97;32;61;32;49; - 108;105;110;104;97;32;60;61;32;97;108;116;117;114;97; - 108;105;110;104;97;43;43; - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;34;34; - - - 105;110;116;32;99;111;108;117;110;97;32;61;32;49; - 99;111;108;117;110;97;32;60;61;32;108;97;114;103;117;114;97; - 99;111;108;117;110;97;43;43; - - - 116;101;120;116;111;32;61;32;116;101;120;116;111;32;43;32;34;35;34; - - - - - 111;117;116;112;117;116;58;32;116;101;120;116;111; - - - - -`,_O=` - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;123;50;44;32;52;44;32;54;44;32;56;44;32;49;48;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93; - - - - -`,AO=` - - - 105;110;116;91;93;32;118;97;108;111;114;101;115;32;61;32;123;52;44;32;56;44;32;49;53;44;32;49;54;44;32;50;51;44;32;52;50;125; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;91;48;93; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;91;118;97;108;111;114;101;115;46;108;101;110;103;116;104;32;45;32;49;93; - - - 111;117;116;112;117;116;58;32;118;97;108;111;114;101;115;46;108;101;110;103;116;104; - - -`,jO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - -`,CO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 105;32;37;32;51;32;61;61;32;48; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - - - -`,DO=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - - - 248; - - - - - - -`,BO=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;49;48; - 105;43;43; - - - 105;32;37;32;50;32;61;61;32;48; - - - 111;117;116;112;117;116;58;32;73; - - - - - 248; - - - - - - -`,PO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;81;117;97;110;116;111;115;32;100;101;103;114;97;117;115;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;34; - - - 105;110;116;32;106;32;61;32;49; - 106;32;60;61;32;105; - 106;43;43; - - - 108;105;110;104;97;32;61;32;108;105;110;104;97;32;43;32;34;42;34; - - - - - 111;117;116;112;117;116;58;32;108;105;110;104;97; - - - - -`,OO=` - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;110;101;119;32;83;116;114;105;110;103;91;51;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;111;109;101;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;111;109;101;34; - - - - - 111;117;116;112;117;116;58;32;34;80;114;105;109;101;105;114;111;32;110;111;109;101;58;32;34;32;43;32;110;111;109;101;115;91;48;93; - - -`,MO=` - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;110;101;119;32;105;110;116;91;52;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - - - 105;110;116;32;105;32;61;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93; - - - - -`,ZO=` - - - 105;110;116;91;93;32;110;117;109;101;114;111;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;110;117;109;101;114;111;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;117;109;101;114;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111;115;91;105;93; - - - 110;117;109;101;114;111;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;110;117;109;101;114;111;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;77;225;120;105;109;111;58;32;34;32;43;32;109;97;120; - - -`,LO=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;55;44;32;49;57;44;32;52;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;109;97;120;32;61;32;112;111;110;116;111;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - 112;111;110;116;111;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;112;111;110;116;111;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - - 111;117;116;112;117;116;58;32;34;77;225;120;105;109;111;58;32;34;32;43;32;109;97;120; - - -`,WO=` - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - 110;117;109;101;114;111;32;33;61;32;48; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - 105;110;112;117;116;58;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,EO=` - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;32;40;48;32;61;32;112;97;114;97;114;41;34; - - - 110;117;109;101;114;111;32;61;61;32;48; - - - 83;97;105;114;32;100;111;32;99;105;99;108;111; - - - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;117;109;101;114;111; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,NO=` - - - 105;110;112;117;116;58;32;105;110;116;32;99;111;109;112;114;105;109;101;110;116;111;32;34;67;111;109;112;114;105;109;101;110;116;111;63;34; - - - 105;110;112;117;116;58;32;105;110;116;32;108;97;114;103;117;114;97;32;34;76;97;114;103;117;114;97;63;34; - - - 105;110;116;32;102;32;61;32;97;114;101;97;40;99;111;109;112;114;105;109;101;110;116;111;44;32;108;97;114;103;117;114;97;41; - - - 111;117;116;112;117;116;58;32;34;193;114;101;97;58;32;34;32;43;32;102; - - -`,FO=` - - - 109;111;115;116;114;97;114;66;111;97;115;86;105;110;100;97;115;40;41; - - - 102;97;122;101;114;80;101;114;103;117;110;116;97;115;40;41; - - - 109;111;115;116;114;97;114;82;101;115;117;108;116;97;100;111;40;41; - - -`,RO=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;97;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;48;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;97;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;101;114;97;116;117;114;97;32;109;97;105;115;32;97;108;116;97;58;32;34;32;43;32;109;97;120; - - -`,JO=` - - - 105;110;116;91;93;32;116;101;109;112;101;114;97;116;117;114;97;115;32;61;32;123;45;53;44;32;45;50;44;32;45;56;44;32;45;51;125; - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;109;112;101;114;97;116;117;114;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 116;101;109;112;101;114;97;116;117;114;97;115;91;105;93;32;62;32;109;97;120; - - - 109;97;120;32;61;32;116;101;109;112;101;114;97;116;117;114;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;109;112;101;114;97;116;117;114;97;32;109;97;105;115;32;97;108;116;97;58;32;34;32;43;32;109;97;120; - - -`,GO=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,HO=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;52;44;32;55;44;32;50;44;32;53;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;76;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,VO=` - - - 105;110;116;91;93;32;110;111;116;97;115;32;61;32;123;50;44;32;51;44;32;49;44;32;52;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;110;111;116;97;115;91;105;93; - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,qO=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;113;32;61;32;113;117;97;100;114;97;100;111;40;110;117;109;101;114;111;41; - - - 111;117;116;112;117;116;58;32;34;79;32;113;117;97;100;114;97;100;111;32;233;32;34;32;43;32;113; - - -`,UO=` - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;120;32;42;32;120; - - - 114;101;116;117;114;110;32;114;101;115;117;108;116;97;100;111; - - -`,QO=` - - - 109;111;115;116;114;97;114;84;105;116;117;108;111;40;41; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;67;111;109;111;32;116;101;32;99;104;97;109;97;115;63;34; - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;34;32;43;32;110;111;109;101; - - -`,$O=` - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;32;97;111;32;113;117;105;122;33;34; - - - 111;117;116;112;117;116;58;32;34;86;97;105;115;32;116;101;114;32;116;114;234;115;32;112;101;114;103;117;110;116;97;115;46;34; - - -`,YO=` - - - 109;111;115;116;114;97;114;83;111;109;97;40;51;44;32;52;41; - - - 105;110;116;32;120;32;61;32;49;48; - - - 109;111;115;116;114;97;114;83;111;109;97;40;120;44;32;120;32;43;32;49;41; - - -`,KO=` - - - 105;110;116;32;115;111;109;97;32;61;32;97;32;43;32;98; - - - 111;117;116;112;117;116;58;32;97;32;43;32;34;32;43;32;34;32;43;32;98;32;43;32;34;32;61;32;34;32;43;32;115;111;109;97; - - -`,XO=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;110;105;99;105;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;105;110;105;99;105;111; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;45;32;49; - - - - - 111;117;116;112;117;116;58;32;34;74;225;33;34; - - -`,eM=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;110;105;99;105;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;34; - - - 105;110;116;32;105;32;61;32;105;110;105;99;105;111; - - - 105;32;62;32;48; - - - 111;117;116;112;117;116;58;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;74;225;34; - - -`,tM=` - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;49;93;91;106;93; - - - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;58;32;34;32;43;32;115;111;109;97; - - -`,nM=` - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;44;32;50;125;44;32;123;52;44;32;49;44;32;54;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;49;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;106;93;91;49;93; - - - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;58;32;34;32;43;32;115;111;109;97; - - -`,iM=` - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;44;32;114;111;110;100;97;32;49;58;32;34;32;43;32;112;111;110;116;111;115;91;49;93;91;48;93; - - -`,aM=` - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;50;125;125; - - - 111;117;116;112;117;116;58;32;34;74;111;103;97;100;111;114;32;50;44;32;114;111;110;100;97;32;49;58;32;34;32;43;32;80;111;110;116;111;115;91;49;93;91;48;93; - - -`,sM=` - - - 105;110;116;91;93;91;93;32;112;111;110;116;111;115;32;61;32;123;123;51;44;32;53;125;44;32;123;52;44;32;49;125;44;32;123;50;44;32;50;125;125; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;112;111;110;116;111;115;91;105;93;46;108;101;110;103;116;104; - 106;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;111;110;116;111;115;91;105;93;91;106;93; - - - - - - - 111;117;116;112;117;116;58;32;34;83;111;109;97;58;32;34;32;43;32;115;111;109;97; - - -`,rM=` - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - - - 105;32;60;61;32;51; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,oM=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,lM=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;110; - 105;43;43; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;105; - - - - - 111;117;116;112;117;116;58;32;115;111;109;97; - - -`,uM=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;81;117;101;32;116;97;98;117;97;100;97;63;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;51; - 105;43;43; - - - 105;110;116;32;114;101;115;117;108;116;97;100;111;32;61;32;110;117;109;101;114;111;32;42;32;105; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;32;118;101;122;101;115;32;34;32;43;32;110;117;109;101;114;111;32;43;32;34;32;61;32;34;32;43;32;114;101;115;117;108;116;97;100;111; - - - - - 110;117;109;101;114;111;32;62;32;49;48; - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;102;111;105;32;100;105;102;237;99;105;108;33;34; - - - - - 111;117;116;112;117;116;58;32;34;69;115;116;97;32;102;111;105;32;102;225;99;105;108;46;34; - - - - -`,mM=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;32;34;73;110;116;114;111;100;117;122;32;117;109;32;110;250;109;101;114;111;34; - - - 105;110;116;32;102;97;116;111;114;105;97;108;32;61;32;49; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;110; - 105;43;43; - - - 102;97;116;111;114;105;97;108;32;61;32;102;97;116;111;114;105;97;108;32;42;32;105; - - - - - 111;117;116;112;117;116;58;32;102;97;116;111;114;105;97;108; - - -`,cM=` - - - 83;116;114;105;110;103;32;99;97;114;116;97;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;99;97;114;116;97;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;99;97;114;116;97;46;115;112;108;105;116;40;34;92;110;34;41; - - - 111;117;116;112;117;116;58;32;34;76;105;110;104;97;115;58;32;34;32;43;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;104;97;115;91;105;93;46;99;111;110;116;97;105;110;115;40;34;114;97;112;111;115;97;34;41; - - - 111;117;116;112;117;116;58;32;34;82;97;112;111;115;97;32;110;97;32;108;105;110;104;97;32;34;32;43;32;40;105;32;43;32;49;41; - - - - - 248; - - - - - - -`,dM=` - - - 83;116;114;105;110;103;32;99;97;114;116;97;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;99;97;114;116;97;46;116;120;116;34;41;41; - - -`,hM=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;63;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,gM=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;63;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;63;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;49;41;32;43;32;34;46;34; - - -`,pM=` - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;32;116;101;109;32;34;32;43;32;110;111;109;101;46;108;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;114;97;115;34; - - -`,fM=` - - - 83;116;114;105;110;103;32;110;111;109;101;32;61;32;34;76;117;100;119;105;103;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;97;116;40;48;41;32;43;32;34;32;116;101;109;32;34;32;43;32;110;111;109;101;46;76;101;110;103;116;104;40;41;32;43;32;34;32;108;101;116;114;97;115;34; - - -`,bM=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;34;80;97;108;97;118;114;97;45;112;97;115;115;101;63;34; - - - 101;110;116;114;97;100;97;46;101;113;117;97;108;115;40;34;76;117;100;119;105;103;34;41; - - - 111;117;116;112;117;116;58;32;34;66;101;109;45;118;105;110;100;111;33;34; - - - - - 111;117;116;112;117;116;58;32;34;80;97;108;97;118;114;97;45;112;97;115;115;101;32;101;114;114;97;100;97;34; - - - - -`,xM=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;60;32;54; - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;103;114;97;116;117;105;116;97;34; - - - - - 105;100;97;100;101;32;60;32;49;56; - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;100;101;32;99;114;105;97;110;231;97;58;32;52;32;101;117;114;111;115;34; - - - - - 111;117;116;112;117;116;58;32;34;66;105;108;104;101;116;101;32;100;101;32;97;100;117;108;116;111;58;32;56;32;101;117;114;111;115;34; - - - - - - -`,vM=` - - - 105;110;112;117;116;58;32;105;110;116;32;110;117;109;101;114;111;32;34;86;97;108;111;114;32;105;110;105;99;105;97;108;63;34; - - - 110;117;109;101;114;111;32;62;32;48; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111; - - - 110;117;109;101;114;111;32;61;32;110;117;109;101;114;111;32;45;32;51; - - - - - 111;117;116;112;117;116;58;32;34;70;105;109;34; - - -`,wM=` - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;97;108;112;105;116;101;32;34;79;32;116;101;117;32;112;97;108;112;105;116;101;63;34; - - - 116;101;110;116;97;116;105;118;97;115;32;61;32;116;101;110;116;97;116;105;118;97;115;32;43;32;49; - - - 112;97;108;112;105;116;101;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;84;101;110;116;97;116;105;118;97;115;58;32;34;32;43;32;116;101;110;116;97;116;105;118;97;115; - - -`,yM=` - - - 105;110;116;32;116;101;110;116;97;116;105;118;97;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;97;108;112;105;116;101;32;34;79;32;116;101;117;32;112;97;108;112;105;116;101;63;34; - - - 112;97;108;112;105;116;101;32;61;61;32;55; - - - -1; - - - - - 248; - - - - - 116;101;110;116;97;116;105;118;97;115;32;61;32;116;101;110;116;97;116;105;118;97;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;84;101;110;116;97;116;105;118;97;115;58;32;34;32;43;32;116;101;110;116;97;116;105;118;97;115; - - -`,zM=` - - - 105;110;116;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;97;114;116;105;103;111;115;32;61;32;48; - - - -1; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;101;99;111;32;34;80;114;101;231;111;63;32;40;48;32;61;32;102;105;109;41;34; - - - 112;114;101;99;111;32;61;61;32;48; - - - -1; - - - - - 248; - - - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;112;114;101;99;111; - - - 97;114;116;105;103;111;115;32;61;32;97;114;116;105;103;111;115;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;65;114;116;105;103;111;115;58;32;34;32;43;32;97;114;116;105;103;111;115; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;115;111;109;97; - - -`,IM=` - - - 105;110;112;117;116;58;32;105;110;116;32;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;97;110;111;115;32;116;101;110;115;63;34; - - - 105;100;97;100;101;32;62;61;32;49;56; - - - 111;117;116;112;117;116;58;32;34;80;111;100;101;115;32;118;111;116;97;114;46;34; - - - - - 105;110;116;32;97;110;111;115;32;61;32;49;56;32;45;32;105;100;97;100;101; - - - 111;117;116;112;117;116;58;32;34;69;115;112;101;114;97;32;109;97;105;115;32;34;32;43;32;97;110;111;115;32;43;32;34;32;97;110;111;115;46;34; - - - - -`,kM=` - - - 105;110;112;117;116;58;32;105;110;116;32;108;105;109;105;116;101;32;34;65;116;233;32;113;117;101;32;110;250;109;101;114;111;63;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;61;32;108;105;109;105;116;101; - 105;32;61;32;105;32;43;32;50; - - - 111;117;116;112;117;116;58;32;105; - - - - -`,SM=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - -`,TM=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - -`,_M=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;112;114;111;99;117;114;97;100;97;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - -`,AM=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;116;32;112;114;111;99;117;114;97;100;97;32;61;32;49;49; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;80;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - -`,jM=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;81;117;97;110;116;105;100;97;100;101;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - -`,CM=` - - - 111;117;116;112;117;116;58;32;34;66;111;109;32;100;105;97;34; - - - 111;117;116;112;117;116;58;32;34;72;111;106;101;32;97;112;114;101;110;100;101;109;111;115;32;101;115;116;114;117;116;111;103;114;97;109;97;115;46;34; - - -`,DM=` - - - 111;117;116;112;117;116;58;32;34;79;108;225;32;77;117;110;100;111;34; - - -`,BM=` - - - 111;117;116;112;117;116;58;32;34;79;108;225;34; - - - 111;117;116;112;117;116;58;32;34;69;115;116;111;117;32;97;32;97;112;114;101;110;100;101;114;32;101;115;116;114;117;116;111;103;114;97;109;97;115;34; - - -`,PM=` - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;99;111;109;112;114;97;115;45;110;111;118;97;46;116;120;116;34;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;108;105;110;104;97;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;66;111;108;111;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101;32;43;32;34;32;108;105;110;104;97;115;32;103;117;97;114;100;97;100;97;115;34; - - -`,OM=` - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;99;111;109;112;114;97;115;45;110;111;118;97;46;116;120;116;34;41; - - -`,MM=` - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;49; - - - 102;105;99;104;101;105;114;111;46;104;97;115;78;101;120;116;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;102;105;99;104;101;105;114;111;46;110;101;120;116;76;105;110;101;40;41; - - - 111;117;116;112;117;116;58;32;110;117;109;101;114;111;32;43;32;34;46;32;34;32;43;32;108;105;110;104;97; - - - 110;117;109;101;114;111;43;43; - - - - - 102;105;99;104;101;105;114;111;46;99;108;111;115;101;40;41; - - -`,ZM=` - - - 83;99;97;110;110;101;114;32;102;105;99;104;101;105;114;111;32;61;32;110;101;119;32;83;99;97;110;110;101;114;40;110;101;119;32;70;105;108;101;40;34;99;111;109;112;114;97;115;46;116;120;116;34;41;41; - - -`,LM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 108;105;110;104;97;46;99;111;110;116;97;105;110;115;40;34;82;97;112;111;115;97;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;82;97;112;111;115;97;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - -`,WM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,EM=` - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 83;116;114;105;110;103;32;109;97;105;115;76;111;110;103;97;32;61;32;108;105;110;104;97;115;91;48;93; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;105;110;104;97;115;91;105;93;46;108;101;110;103;116;104;40;41;32;62;32;109;97;105;115;76;111;110;103;97;46;108;101;110;103;116;104;40;41; - - - 109;97;105;115;76;111;110;103;97;32;61;32;108;105;110;104;97;115;91;105;93; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;69;110;116;114;97;100;97;32;109;97;105;115;32;108;111;110;103;97;58;32;34;32;43;32;109;97;105;115;76;111;110;103;97; - - -`,NM=` - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;100;105;97;114;105;111;46;116;120;116;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - -`,FM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 83;116;114;105;110;103;32;109;101;108;104;111;114;78;111;109;101;32;61;32;34;34; - - - 105;110;116;32;109;101;108;104;111;114;78;111;116;97;32;61;32;55; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;110;111;116;97;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;51;93;41; - - - 110;111;116;97;32;60;32;109;101;108;104;111;114;78;111;116;97; - - - 109;101;108;104;111;114;78;111;116;97;32;61;32;110;111;116;97; - - - 109;101;108;104;111;114;78;111;109;101;32;61;32;112;97;114;116;101;115;91;48;93; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;101;108;104;111;114;32;110;111;116;97;32;97;32;73;110;103;108;234;115;58;32;34;32;43;32;109;101;108;104;111;114;78;111;109;101;32;43;32;34;32;40;34;32;43;32;109;101;108;104;111;114;78;111;116;97;32;43;32;34;41;34; - - -`,RM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,JM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 100;111;117;98;108;101;32;115;111;109;97;32;61;32;48; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 115;111;109;97;32;61;32;115;111;109;97;32;43;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;112;97;114;116;101;115;91;49;93;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;34;77;233;100;105;97;32;100;101;32;77;97;116;101;109;225;116;105;99;97;58;32;34;32;43;32;115;111;109;97;32;47;32;113;117;97;110;116;105;100;97;100;101; - - -`,GM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;116;117;114;109;97;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,HM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 108;105;110;104;97;46;99;111;110;116;97;105;110;115;40;34;92;34;110;111;109;101;92;34;34;41; - - - 105;110;116;32;105;110;105;99;105;111;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;110;111;109;101;92;34;58;32;92;34;34;41;32;43;32;57; - - - 105;110;116;32;102;105;109;32;61;32;108;105;110;104;97;46;105;110;100;101;120;79;102;40;34;92;34;34;44;32;105;110;105;99;105;111;41; - - - 111;117;116;112;117;116;58;32;108;105;110;104;97;46;115;117;98;115;116;114;105;110;103;40;105;110;105;99;105;111;44;32;102;105;109;41; - - - - - 248; - - - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - -`,VM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;122;111;111;46;106;115;111;110;34;41;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - -`,qM=` - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;82;97;112;111;115;97;34;125; - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;110;116;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;61;32;34;32;32;123;92;34;110;111;109;101;92;34;58;32;92;34;34;32;43;32;110;111;109;101;115;91;105;93;32;43;32;34;92;34;44;32;92;34;112;111;110;116;111;115;92;34;58;32;34;32;43;32;112;111;110;116;111;115;91;105;93;32;43;32;34;125;34; - - - 105;32;60;32;110;111;109;101;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;97;100;97;32;61;32;101;110;116;114;97;100;97;32;43;32;34;44;34; - - - - - 248; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;101;110;116;114;97;100;97;41; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;110;111;109;101;115;46;108;101;110;103;116;104;32;43;32;34;32;101;110;116;114;97;100;97;115;32;103;117;97;114;100;97;100;97;115;34; - - -`,UM=` - - - 83;116;114;105;110;103;91;93;32;110;111;109;101;115;32;61;32;123;34;76;117;100;119;105;103;34;44;32;34;90;111;114;97;34;44;32;34;82;97;112;111;115;97;34;125; - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;49;50;44;32;49;53;44;32;57;125; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;112;111;110;116;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - -`,QM=` - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;105;115;45;110;111;118;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;115;91;105;93;46;115;112;108;105;116;40;34;59;34;41; - - - 83;116;114;105;110;103;32;101;110;116;114;97;100;97;32;61;32;34;32;32;123;92;34;110;111;109;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;101;115;91;48;93;32;43;32;34;92;34;44;32;92;34;101;115;112;101;99;105;101;92;34;58;32;92;34;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;92;34;44;32;92;34;105;100;97;100;101;92;34;58;32;34;32;43;32;112;97;114;116;101;115;91;50;93;32;43;32;34;125;34; - - - 105;32;60;32;108;105;110;104;97;115;46;108;101;110;103;116;104;32;45;32;49; - - - 101;110;116;114;97;100;97;32;61;32;101;110;116;114;97;100;97;32;43;32;34;44;34; - - - - - 248; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;101;110;116;114;97;100;97;41; - - - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;40;108;105;110;104;97;115;46;108;101;110;103;116;104;32;45;32;49;41;32;43;32;34;32;97;110;105;109;97;105;115;32;103;117;97;114;100;97;100;111;115;34; - - -`,$M=` - - - 83;116;114;105;110;103;32;116;101;120;116;111;32;61;32;70;105;108;101;115;46;114;101;97;100;83;116;114;105;110;103;40;80;97;116;104;46;111;102;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 83;116;114;105;110;103;91;93;32;108;105;110;104;97;115;32;61;32;116;101;120;116;111;46;115;112;108;105;116;40;34;92;110;34;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;97;110;105;109;97;105;115;45;110;111;118;111;115;46;106;115;111;110;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;91;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;34;93;34;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - -`,YM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;102;114;97;115;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 108;105;110;104;97;32;33;61;32;110;117;108;108; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 115;97;105;100;97;46;112;114;105;110;116;108;110;40;112;97;114;116;101;115;91;48;93;32;43;32;34;32;40;34;32;43;32;112;97;114;116;101;115;91;49;93;32;43;32;34;41;32;116;101;109;32;34;32;43;32;112;97;114;116;101;115;91;50;93;32;43;32;34;32;97;110;111;115;46;34;41; - - - 113;117;97;110;116;105;100;97;100;101;43;43; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - - 111;117;116;112;117;116;58;32;113;117;97;110;116;105;100;97;100;101;32;43;32;34;32;102;114;97;115;101;115;32;103;117;97;114;100;97;100;97;115;34; - - -`,KM=` - - - 66;117;102;102;101;114;101;100;82;101;97;100;101;114;32;108;101;105;116;111;114;32;61;32;110;101;119;32;66;117;102;102;101;114;101;100;82;101;97;100;101;114;40;110;101;119;32;70;105;108;101;82;101;97;100;101;114;40;34;97;110;105;109;97;105;115;46;99;115;118;34;41;41; - - - 80;114;105;110;116;87;114;105;116;101;114;32;115;97;105;100;97;32;61;32;110;101;119;32;80;114;105;110;116;87;114;105;116;101;114;40;34;102;114;97;115;101;115;46;116;120;116;34;41; - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;105;110;104;97;32;61;32;108;101;105;116;111;114;46;114;101;97;100;76;105;110;101;40;41; - - - 108;101;105;116;111;114;46;99;108;111;115;101;40;41; - - - 115;97;105;100;97;46;99;108;111;115;101;40;41; - - -`,XM=` - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;98;105;108;104;101;116;101;115;63;34; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - -`,eZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;34;81;117;97;110;116;111;115;32;98;105;108;104;101;116;101;115;63;34; - - - 111;117;116;112;117;116;58;32;34;84;111;116;97;108;58;32;34;32;43;32;116;111;116;97;108; - - - 100;111;117;98;108;101;32;116;111;116;97;108;32;61;32;113;117;97;110;116;105;100;97;100;101;32;42;32;56;46;53; - - -`,tZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,nZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;80;114;105;109;101;105;114;111;32;110;250;109;101;114;111;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;83;101;103;117;110;100;111;32;110;250;109;101;114;111;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,iZ=` - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;97;117;120;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;97;117;120; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,aZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;116;97;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;99;97;114;116;97;34; - - - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;60;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - -`,sZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;99;97;114;116;97;115;91;105;93;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;99;97;114;116;97;34; - - - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;62;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - -`,rZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;32;112;114;111;99;117;114;97;115;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;97;99;111;101;115;32;61;32;48; - - - 105;110;116;32;101;115;113;117;101;114;100;97;32;61;32;48; - - - 105;110;116;32;100;105;114;101;105;116;97;32;61;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;101;105;111;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;101;115;113;117;101;114;100;97;32;60;61;32;100;105;114;101;105;116;97; - - - 109;101;105;111;32;61;32;40;101;115;113;117;101;114;100;97;32;43;32;100;105;114;101;105;116;97;41;32;47;32;50; - - - 99;111;109;112;97;114;97;99;111;101;115;32;61;32;99;111;109;112;97;114;97;99;111;101;115;32;43;32;49; - - - 99;97;114;116;97;115;91;109;101;105;111;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;109;101;105;111; - - - - - 99;97;114;116;97;115;91;109;101;105;111;93;32;60;32;112;114;111;99;117;114;97;100;97; - - - 101;115;113;117;101;114;100;97;32;61;32;109;101;105;111;32;43;32;49; - - - - - 100;105;114;101;105;116;97;32;61;32;109;101;105;111;32;45;32;49; - - - - - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;69;110;99;111;110;116;114;97;100;97;32;110;97;32;112;111;115;105;231;227;111;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;97;231;245;101;115;58;32;34;32;43;32;99;111;109;112;97;114;97;99;111;101;115; - - -`,oZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;111;110;100;97;32;61;32;49; - 114;111;110;100;97;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 114;111;110;100;97;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104;32;45;32;114;111;110;100;97; - 105;43;43; - - - 99;97;114;116;97;115;91;105;93;32;62;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 105;110;116;32;97;117;120;32;61;32;99;97;114;116;97;115;91;105;93; - - - 99;97;114;116;97;115;91;105;93;32;61;32;99;97;114;116;97;115;91;105;32;43;32;49;93; - - - 99;97;114;116;97;115;91;105;32;43;32;49;93;32;61;32;97;117;120; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;99;97;114;116;97;115;91;105;93; - - - - -`,lZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;231;227;111;58;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - -`,uZ=` - - - 105;110;116;91;93;32;99;97;114;116;97;115;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;112;114;111;99;117;114;97;100;97;32;34;81;117;101;32;99;97;114;116;97;32;112;114;111;99;117;114;97;115;63;34; - - - 105;110;116;32;112;111;115;105;99;97;111;32;61;32;45;49; - - - 105;110;116;32;99;111;109;112;97;114;97;99;111;101;115;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;99;97;111;32;61;61;32;45;49;32;38;38;32;105;32;60;32;99;97;114;116;97;115;46;108;101;110;103;116;104; - - - 99;111;109;112;97;114;97;99;111;101;115;32;61;32;99;111;109;112;97;114;97;99;111;101;115;32;43;32;49; - - - 99;97;114;116;97;115;91;105;93;32;61;61;32;112;114;111;99;117;114;97;100;97; - - - 112;111;115;105;99;97;111;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;99;97;111;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;69;110;99;111;110;116;114;97;100;97;32;110;97;32;112;111;115;105;231;227;111;32;34;32;43;32;112;111;115;105;99;97;111; - - - - - 111;117;116;112;117;116;58;32;34;78;227;111;32;101;110;99;111;110;116;114;97;100;111;34; - - - - - 111;117;116;112;117;116;58;32;34;67;111;109;112;97;114;97;231;245;101;115;58;32;34;32;43;32;99;111;109;112;97;114;97;99;111;101;115; - - -`,mZ=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;110;116;111;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;110;116;111;115;91;105;93; - - - - -`,cZ=` - - - 105;110;116;91;93;32;112;111;110;116;111;115;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;111;110;116;111;115;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;111;110;116;111;115;91;105;93; - - - - -`,dZ=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;111;110;116;111;115;46;108;101;110;103;116;104; - 105;43;43; - - - 112;111;110;116;111;115;91;105;93;32;61;32;112;111;110;116;111;115;91;105;93;32;43;32;49; - - - - -`,hZ=` - - - 105;110;116;32;110;117;109;101;114;111;32;61;32;53; - - - 100;117;112;108;105;99;97;114;40;110;117;109;101;114;111;41; - - - 111;117;116;112;117;116;58;32;34;78;111;32;112;114;111;103;114;97;109;97;32;112;114;105;110;99;105;112;97;108;58;32;110;117;109;101;114;111;32;61;32;34;32;43;32;110;117;109;101;114;111; - - - 98;111;111;108;101;97;110;91;93;32;102;105;108;97;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;65;110;116;101;115;58;32;34;32;43;32;102;105;108;97;91;48;93;32;43;32;34;44;32;34;32;43;32;102;105;108;97;91;49;93; - - - 108;105;109;112;97;114;40;102;105;108;97;41; - - - 111;117;116;112;117;116;58;32;34;68;101;112;111;105;115;58;32;34;32;43;32;102;105;108;97;91;48;93;32;43;32;34;44;32;34;32;43;32;102;105;108;97;91;49;93; - - -`,gZ=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;108;117;103;97;114;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 108;117;103;97;114;101;115;91;105;93;32;61;32;102;97;108;115;101; - - - - -`,pZ=` - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;78;111;32;115;117;98;112;114;111;103;114;97;109;97;58;32;120;32;61;32;34;32;43;32;120; - - -`,fZ=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;111;109;101;32;34;78;111;109;101;32;112;114;243;112;114;105;111;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;97;112;101;108;105;100;111;32;34;65;112;101;108;105;100;111;34; - - - 111;117;116;112;117;116;58;32;110;111;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;97;112;101;108;105;100;111;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,bZ=` - - - 83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;61;32;34;80;97;115;115;97;116;101;109;112;111;34; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;110;100;101;120;79;102;40;34;97;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;108;97;115;116;73;110;100;101;120;79;102;40;34;97;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;80;65;83;83;65;84;69;77;80;79;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;114;101;112;108;97;99;101;40;34;116;101;109;112;111;34;44;32;34;112;111;114;116;101;34;41; - - - 111;117;116;112;117;116;58;32;34;77;233;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;105;100;97;100;101;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;105;100;97;100;101;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;105;100;97;100;101;41;32;43;32;34;32;97;110;111;115;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - -`,xZ=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;112;97;108;97;118;114;97;34; - - - 83;116;114;105;110;103;32;105;110;118;101;114;116;105;100;97;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 105;110;118;101;114;116;105;100;97;32;61;32;105;110;118;101;114;116;105;100;97;32;43;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;105;110;118;101;114;116;105;100;97; - - -`,vZ=` - - - 83;116;114;105;110;103;32;108;105;110;104;97;32;61;32;34;76;117;100;119;105;103;59;98;111;100;101;59;55;34; - - - 83;116;114;105;110;103;91;93;32;112;97;114;116;101;115;32;61;32;108;105;110;104;97;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;114;116;101;115;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;112;97;114;116;101;115;91;105;93; - - - - -`,wZ=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;102;114;97;115;101;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;102;114;97;115;101;34; - - - 83;116;114;105;110;103;91;93;32;112;97;108;97;118;114;97;115;32;61;32;102;114;97;115;101;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;80;97;108;97;118;114;97;115;58;32;34;32;43;32;112;97;108;97;118;114;97;115;46;108;101;110;103;116;104; - - -`,yZ=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;34;73;110;116;114;111;100;117;122;32;117;109;97;32;112;97;108;97;118;114;97;34; - - - 105;110;116;32;113;117;97;110;116;105;100;97;100;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;97;39; - - - 113;117;97;110;116;105;100;97;100;101;32;61;32;113;117;97;110;116;105;100;97;100;101;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;78;250;109;101;114;111;32;100;101;32;97;58;32;34;32;43;32;113;117;97;110;116;105;100;97;100;101; - - -`,zZ=` - - - 83;116;114;105;110;103;32;112;97;108;97;118;114;97;32;61;32;34;80;97;115;115;97;116;101;109;112;111;34; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;115;117;98;115;116;114;105;110;103;40;48;44;32;53;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;115;117;98;115;116;114;105;110;103;40;53;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;105;110;100;101;120;79;102;40;34;116;101;109;112;111;34;41; - - - 111;117;116;112;117;116;58;32;112;97;108;97;118;114;97;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - -`,IZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;75;97;114;116;101;110;63;34; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;32;61;32;97;110;122;97;104;108;32;42;32;56;46;53; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - -`,kZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;110;122;97;104;108;32;34;87;105;101;32;118;105;101;108;101;32;75;97;114;116;101;110;63;34; - - - 111;117;116;112;117;116;58;32;34;71;101;115;97;109;116;58;32;34;32;43;32;103;101;115;97;109;116; - - - 100;111;117;98;108;101;32;103;101;115;97;109;116;32;61;32;97;110;122;97;104;108;32;42;32;56;46;53; - - -`,SZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,TZ=` - - - 105;110;112;117;116;58;32;105;110;116;32;97;32;34;69;114;115;116;101;32;90;97;104;108;34; - - - 105;110;112;117;116;58;32;105;110;116;32;98;32;34;90;119;101;105;116;101;32;90;97;104;108;34; - - - 97;32;61;32;98; - - - 98;32;61;32;97; - - - 111;117;116;112;117;116;58;32;97; - - - 111;117;116;112;117;116;58;32;98; - - -`,_Z=` - - - 105;110;116;32;97;32;61;32;51; - - - 105;110;116;32;98;32;61;32;53; - - - 105;110;116;32;104;105;108;102;32;61;32;97; - - - 97;32;61;32;98; - - - 98;32;61;32;104;105;108;102; - - - 111;117;116;112;117;116;58;32;34;97;32;61;32;34;32;43;32;97; - - - 111;117;116;112;117;116;58;32;34;98;32;61;32;34;32;43;32;98; - - -`,AZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;107;97;114;116;101;110;91;105;93;32;34;75;97;114;116;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;60;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - -`,jZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 105;110;112;117;116;58;32;107;97;114;116;101;110;91;105;93;32;34;75;97;114;116;101;32;101;105;110;103;101;98;101;110;34; - - - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;62;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - -`,CZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;32;115;117;99;104;115;116;32;100;117;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;118;101;114;103;108;101;105;99;104;101;32;61;32;48; - - - 105;110;116;32;108;105;110;107;115;32;61;32;48; - - - 105;110;116;32;114;101;99;104;116;115;32;61;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;49; - - - 105;110;116;32;109;105;116;116;101;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;108;105;110;107;115;32;60;61;32;114;101;99;104;116;115; - - - 109;105;116;116;101;32;61;32;40;108;105;110;107;115;32;43;32;114;101;99;104;116;115;41;32;47;32;50; - - - 118;101;114;103;108;101;105;99;104;101;32;61;32;118;101;114;103;108;101;105;99;104;101;32;43;32;49; - - - 107;97;114;116;101;110;91;109;105;116;116;101;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;109;105;116;116;101; - - - - - 107;97;114;116;101;110;91;109;105;116;116;101;93;32;60;32;103;101;115;117;99;104;116; - - - 108;105;110;107;115;32;61;32;109;105;116;116;101;32;43;32;49; - - - - - 114;101;99;104;116;115;32;61;32;109;105;116;116;101;32;45;32;49; - - - - - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;71;101;102;117;110;100;101;110;32;97;110;32;80;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;103;108;101;105;99;104;101;58;32;34;32;43;32;118;101;114;103;108;101;105;99;104;101; - - -`,DZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;55;44;32;51;44;32;49;48;44;32;53;44;32;50;125; - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 114;117;110;100;101;43;43; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104;32;45;32;114;117;110;100;101; - 105;43;43; - - - 107;97;114;116;101;110;91;105;93;32;62;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 105;110;116;32;104;105;108;102;32;61;32;107;97;114;116;101;110;91;105;93; - - - 107;97;114;116;101;110;91;105;93;32;61;32;107;97;114;116;101;110;91;105;32;43;32;49;93; - - - 107;97;114;116;101;110;91;105;32;43;32;49;93;32;61;32;104;105;108;102; - - - - - 248; - - - - - - - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;107;97;114;116;101;110;91;105;93; - - - - -`,BZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;56;44;32;52;44;32;49;49;44;32;52;44;32;54;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;80;111;115;105;116;105;111;110;58;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - -`,PZ=` - - - 105;110;116;91;93;32;107;97;114;116;101;110;32;61;32;123;50;44;32;51;44;32;53;44;32;55;44;32;56;44;32;49;48;44;32;49;50;44;32;49;51;44;32;49;53;44;32;49;56;44;32;50;48;44;32;50;49;44;32;50;52;44;32;50;55;44;32;51;48;125; - - - 105;110;112;117;116;58;32;105;110;116;32;103;101;115;117;99;104;116;32;34;87;101;108;99;104;101;32;75;97;114;116;101;32;115;117;99;104;115;116;32;100;117;63;34; - - - 105;110;116;32;112;111;115;105;116;105;111;110;32;61;32;45;49; - - - 105;110;116;32;118;101;114;103;108;101;105;99;104;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - - - 112;111;115;105;116;105;111;110;32;61;61;32;45;49;32;38;38;32;105;32;60;32;107;97;114;116;101;110;46;108;101;110;103;116;104; - - - 118;101;114;103;108;101;105;99;104;101;32;61;32;118;101;114;103;108;101;105;99;104;101;32;43;32;49; - - - 107;97;114;116;101;110;91;105;93;32;61;61;32;103;101;115;117;99;104;116; - - - 112;111;115;105;116;105;111;110;32;61;32;105; - - - - - 248; - - - - - 105;32;61;32;105;32;43;32;49; - - - - - 112;111;115;105;116;105;111;110;32;62;61;32;48; - - - 111;117;116;112;117;116;58;32;34;71;101;102;117;110;100;101;110;32;97;110;32;80;111;115;105;116;105;111;110;32;34;32;43;32;112;111;115;105;116;105;111;110; - - - - - 111;117;116;112;117;116;58;32;34;78;105;99;104;116;32;103;101;102;117;110;100;101;110;34; - - - - - 111;117;116;112;117;116;58;32;34;86;101;114;103;108;101;105;99;104;101;58;32;34;32;43;32;118;101;114;103;108;101;105;99;104;101; - - -`,OZ=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;117;110;107;116;101;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;117;110;107;116;101;91;105;93; - - - - -`,MZ=` - - - 105;110;116;91;93;32;112;117;110;107;116;101;32;61;32;123;51;44;32;53;44;32;56;125; - - - 98;111;110;117;115;40;112;117;110;107;116;101;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;112;117;110;107;116;101;91;105;93; - - - - -`,ZZ=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;117;110;107;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 112;117;110;107;116;101;91;105;93;32;61;32;112;117;110;107;116;101;91;105;93;32;43;32;49; - - - - -`,LZ=` - - - 105;110;116;32;122;97;104;108;32;61;32;53; - - - 118;101;114;100;111;112;112;108;101;40;122;97;104;108;41; - - - 111;117;116;112;117;116;58;32;34;73;109;32;72;97;117;112;116;112;114;111;103;114;97;109;109;58;32;122;97;104;108;32;61;32;34;32;43;32;122;97;104;108; - - - 98;111;111;108;101;97;110;91;93;32;114;101;105;104;101;32;61;32;123;116;114;117;101;44;32;116;114;117;101;44;32;102;97;108;115;101;44;32;116;114;117;101;125; - - - 111;117;116;112;117;116;58;32;34;86;111;114;104;101;114;58;32;34;32;43;32;114;101;105;104;101;91;48;93;32;43;32;34;44;32;34;32;43;32;114;101;105;104;101;91;49;93; - - - 108;101;101;114;101;110;40;114;101;105;104;101;41; - - - 111;117;116;112;117;116;58;32;34;78;97;99;104;104;101;114;58;32;34;32;43;32;114;101;105;104;101;91;48;93;32;43;32;34;44;32;34;32;43;32;114;101;105;104;101;91;49;93; - - -`,WZ=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;112;108;97;101;116;122;101;46;108;101;110;103;116;104; - 105;43;43; - - - 112;108;97;101;116;122;101;91;105;93;32;61;32;102;97;108;115;101; - - - - -`,EZ=` - - - 120;32;61;32;120;32;42;32;50; - - - 111;117;116;112;117;116;58;32;34;73;109;32;85;110;116;101;114;112;114;111;103;114;97;109;109;58;32;120;32;61;32;34;32;43;32;120; - - -`,NZ=` - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;98;115;116;97;110;100;58;32;34;32;43;32;97;98;115;116;97;110;100;32;43;32;34;32;99;109;34;41; - - -`,FZ=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - - - -`,RZ=` - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - -`,JZ=` - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - -`,GZ=` - - - 105;110;116;32;122;117;115;116;97;110;100;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;49; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;50; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;48; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,HZ=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,VZ=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;72;97;108;108;111;33;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;99;104;32;98;105;110;32;86;105;115;117;66;111;116;34;41; - - -`,qZ=` - - - 105;110;116;32;122;97;104;108;32;61;32;48; - 122;97;104;108;32;60;32;56; - 122;97;104;108;43;43; - - - 122;97;104;108;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 122;97;104;108;32;47;32;50;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;49;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 122;97;104;108;32;47;32;52;32;37;32;50;32;61;61;32;49; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,UZ=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,QZ=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,$Z=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,YZ=` - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;86;105;115;117;66;111;116;34; - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;48;44;32;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;51;48;48;41; - - - - -`,KZ=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,XZ=` - - - 109;98;111;116;46;108;101;100;40;48;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;103;101;108;98;34;41; - - - 109;98;111;116;46;108;101;100;40;52;41;46;115;101;116;67;111;108;111;114;40;34;103;114;252;110;34;41; - - -`,eL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - -`,tL=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;52;53; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;112;97;114;107;116;33;34;41; - - -`,nL=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,iL=` - - - 83;116;114;105;110;103;91;93;32;102;97;114;98;101;110;32;61;32;123;34;114;111;116;34;44;32;34;103;101;108;98;34;44;32;34;103;114;252;110;34;44;32;34;98;108;97;117;34;44;32;34;108;105;108;97;34;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;102;97;114;98;101;110;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;102;97;114;98;101;110;91;105;93;41; - - - - -`,aL=` - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - -`,sL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;114;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,rL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;70;111;114;119;97;114;100;40;41; - - - - -`,oL=` - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;83;99;104;114;105;116;116;101;41; - - -`,lL=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;49;48; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 103;101;115;97;109;116;32;61;32;71;101;115;97;109;116;32;43;32;115;101;105;116;101; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;103;101;115;97;109;116;32;43;32;34;32;99;109;34;41; - - -`,uL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;41; - - -`,mL=` - - - 99;111;110;115;116;32;105;110;116;32;83;69;73;84;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,cL=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;78;97;99;104;116;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;228;109;109;101;114;117;110;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;84;97;103;34;41; - - - - - - -`,dL=` - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;83;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;99;104;114;105;116;116;101; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,hL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;116;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,gL=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;103;114;252;110;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - -`,pL=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;114;105;99;104;116;117;110;103;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - 114;105;99;104;116;117;110;103;32;61;32;73; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;82;105;99;104;116;117;110;103;58;32;34;32;43;32;114;105;99;104;116;117;110;103;41; - - -`,fL=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;116;104;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;81;117;101;114;108;105;110;105;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - -`,bL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;66;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - -`,xL=` - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;71;101;115;116;111;112;112;116;41; - - -`,vL=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,wL=` - - - 98;111;111;108;101;97;110;32;108;97;117;116;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;97;117;116;32;61;32;84;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;97;117;116; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;107;108;97;116;115;99;104;116;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;101;115;32;115;116;105;108;108;34;41; - - - - -`,yL=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;111;102;102;40;41; - - - - -`,zL=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;108;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,IL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;80;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,kL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;73;41;46;115;101;116;67;111;108;111;114;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - -`,SL=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;41; - - - - -`,TL=` - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 119;101;114;116;101;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;87;101;114;116;101;91;105;93; - - - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;119;101;114;116;32;61;32;115;117;109;109;101;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108;119;101;114;116;41; - - -`,_L=` - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;73;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - -`,AL=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;97;108;108;111;32;75;108;97;115;115;101;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;84;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - -`,jL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;83;116;111;112;40;41; - - -`,CL=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;107;107;117;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;41; - - - - - 248; - - - - -`,DL=` - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;103;114;252;110; - - - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;101;108;98;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,BL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;85;109;100;114;101;104;117;110;103;101;110;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,PL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,OL=` - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,ML=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,ZL=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - -`,LL=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;107;107;117;58;32;34;32;43;32;109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;43;32;34;32;37;34;41; - - - 109;98;111;116;46;103;101;116;66;97;116;116;101;114;121;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 248; - - - - -`,WL=` - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;54; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;114;252;110;34; - - - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;103;101;108;98;34; - - - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 122;117;115;116;97;110;100;32;61;32;34;114;111;116;34; - - - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - -`,EL=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;103;114;252;110;34;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - - - 248; - - - - - - -`,NL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,FL=` - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;61;32;49; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;105;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - -`,RL=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;53; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;105;32;42;32;50;48;48;44;32;50;48;48;41; - - - - -`,JL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;85;112;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;105;115;74;111;121;115;116;105;99;107;68;111;119;110;40;41; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;41; - - - - - 248; - - - - - - - - -`,GL=` - - - 98;111;111;108;101;97;110;32;108;97;117;116;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 108;97;117;116;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 108;97;117;116; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;107;108;97;116;115;99;104;116;33;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;108;108;101;115;32;115;116;105;108;108;34;41; - - - - -`,HL=` - - - 99;111;110;115;116;32;105;110;116;32;83;69;73;84;69;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;83;69;73;84;69;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,VL=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;62;32;54;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,qL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;98;108;97;117;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - -`,UL=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,QL=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;48;48;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,$L=` - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;115;99;104;114;105;116;116;101; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,YL=` - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;54;48; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;97;101;110;103;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;98;114;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,KL=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,XL=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;114;105;99;104;116;117;110;103;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - 114;105;99;104;116;117;110;103;32;61;32;105; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;82;105;99;104;116;117;110;103;58;32;34;32;43;32;114;105;99;104;116;117;110;103;41; - - -`,eW=` - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101;41; - - -`,tW=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,nW=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;49;48; - - - 105;110;116;32;103;101;115;97;109;116;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 103;101;115;97;109;116;32;61;32;103;101;115;97;109;116;32;43;32;115;101;105;116;101; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;103;101;115;97;109;116;32;43;32;34;32;99;109;34;41; - - -`,iW=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;78;97;99;104;116;34;41; - - - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;54;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;68;228;109;109;101;114;117;110;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;84;97;103;34;41; - - - - - - -`,aW=` - - - - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;115;116;111;112;112;116;34;41; - - -`,sW=` - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;34;72;97;108;108;111;32;75;108;97;115;115;101;34; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;116;101;120;116;32;43;32;34;32;40;34;32;43;32;116;101;120;116;46;108;101;110;103;116;104;40;41;32;43;32;34;41;34;41; - - -`,rW=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,oW=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;62;32;45;53;32;38;38;32;109;98;111;116;46;103;101;116;82;111;108;108;40;41;32;60;32;53; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,lW=` - - - 109;98;111;116;46;103;101;116;76;111;117;100;110;101;115;115;40;41;32;60;32;53;48; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,uW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - - 105;110;116;32;105;32;61;32;51; - 105;32;62;32;48; - 105;45;45; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;114;111;116;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - 109;98;111;116;46;108;101;100;40;105;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - - -`,mW=` - - - 99;111;110;115;116;32;105;110;116;32;65;66;83;84;65;78;68;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,cW=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - -`,dW=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,hW=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - -`,gW=` - - - 99;111;110;115;116;32;105;110;116;32;65;66;83;84;65;78;68;32;61;32;50;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;65;66;83;84;65;78;68; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,pW=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,fW=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;111;112;112;34;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;97;110;103;115;97;109;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;114;101;105;34;41; - - - - - - -`,bW=` - - - 83;116;114;105;110;103;32;122;117;115;116;97;110;100;32;61;32;34;102;97;104;114;101;110;34; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50;48; - 105;43;43; - - - 122;117;115;116;97;110;100;46;101;113;117;97;108;115;40;34;102;97;104;114;101;110;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 122;117;115;116;97;110;100;32;61;32;34;100;114;101;104;101;110;34; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 122;117;115;116;97;110;100;32;61;32;34;102;97;104;114;101;110;34; - - - - - - -`,xW=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 248; - - - - - - - - -`,vW=` - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;76;117;100;119;105;103;34; - - - 105;110;116;32;105;32;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;119;111;114;116;46;99;104;97;114;65;116;40;105;41;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,wW=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41;32;38;38;32;109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;105;115;76;105;110;101;82;105;103;104;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - 248; - - - - - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,yW=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;48;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;81;117;101;114;108;105;110;105;101;33;34;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - -`,zW=` - - - 105;110;116;32;109;97;120;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;97;98;115;116;97;110;100;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 97;98;115;116;97;110;100;32;62;32;109;97;120; - - - 109;97;120;32;61;32;97;98;115;116;97;110;100; - - - - - 248; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;109;32;119;101;105;116;101;115;116;101;110;58;32;34;32;43;32;109;97;120;32;43;32;34;32;99;109;34;41; - - -`,IW=` - - - 105;110;116;91;93;32;116;111;101;110;101;32;61;32;123;50;54;50;44;32;50;57;52;44;32;51;51;48;44;32;51;52;57;44;32;51;57;50;125; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;111;101;110;101;46;108;101;110;103;116;104; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;116;111;101;110;101;91;105;93;44;32;51;48;48;41; - - - - -`,kW=` - - - 105;110;116;91;93;32;119;101;114;116;101;32;61;32;110;101;119;32;105;110;116;91;53;93; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 119;101;114;116;101;91;105;93;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 105;110;116;32;115;117;109;109;101;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;101;114;116;101;46;108;101;110;103;116;104; - 105;43;43; - - - 115;117;109;109;101;32;61;32;115;117;109;109;101;32;43;32;119;101;114;116;101;91;105;93; - - - - - 100;111;117;98;108;101;32;109;105;116;116;101;108;119;101;114;116;32;61;32;115;117;109;109;101;32;47;32;53;46;48; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;77;105;116;116;101;108;119;101;114;116;58;32;34;32;43;32;109;105;116;116;101;108;119;101;114;116;41; - - -`,SW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;54;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;52;48;48;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;119;101;105;223;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - 109;98;111;116;46;108;101;100;40;50;41;46;115;101;116;67;111;108;111;114;40;34;97;117;115;34;41; - - - 109;98;111;116;46;112;97;117;115;101;40;50;48;48;41; - - - - -`,TW=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;51;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;50;53;53;44;32;50;53;53;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - -`,_W=` - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;90;122;122;34;41; - - -`,AW=` - - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;53; - - - 83;99;104;108;101;105;102;101;32;118;101;114;108;97;115;115;101;110; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - -`,jW=` - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41;32;124;124;32;109;98;111;116;46;105;115;66;117;116;116;111;110;66;40;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;52;52;48;44;32;51;48;48;41; - - - - - 248; - - - - -`,CW=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - -`,DW=` - - - 113;117;97;100;114;97;116;40;51;48;41; - - - 113;117;97;100;114;97;116;40;53;48;41; - - -`,BW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;53; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - -`,PW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,OW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;53; - 106;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,MW=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - -`,ZW=` - - - 109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;62;32;50;48;32;124;124;32;109;98;111;116;46;103;101;116;80;105;116;99;104;40;41;32;60;32;45;50;48; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;51;48;48;41; - - - - - 248; - - - - -`,LW=` - - - 105;110;116;32;115;99;104;114;105;116;116;101;32;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - 115;99;104;114;105;116;116;101;32;61;32;115;99;104;114;105;116;116;101;32;43;32;49; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;99;104;114;105;116;116;101;58;32;34;32;43;32;115;99;104;114;105;116;116;101;41; - - -`,WW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,EW=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - - -`,NW=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;50;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 115;101;105;116;101;32;61;32;115;101;105;116;101;32;43;32;49;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,FW=` - - - 105;110;116;32;105;32;61;32;49; - 105;32;60;61;32;56; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;105;32;42;32;49;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,RW=` - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,JW=` - - - 105;110;116;32;114;117;110;100;101;32;61;32;49; - 114;117;110;100;101;32;60;61;32;51; - 114;117;110;100;101;43;43; - - - 114;117;110;100;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;65;117;102;32;100;105;101;32;80;108;228;116;122;101;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;70;101;114;116;105;103;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;76;111;115;33;34;41; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - -`,GW=` - - - 105;110;116;32;108;97;101;110;103;101;32;61;32;54;48; - - - 105;110;116;32;98;114;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;108;97;101;110;103;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;98;114;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 105;110;116;32;115;116;114;101;99;107;101;32;61;32;50;32;42;32;108;97;101;110;103;101;32;43;32;50;32;42;32;98;114;101;105;116;101; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;115;116;114;101;99;107;101;32;43;32;34;32;99;109;34;41; - - -`,HW=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;49;48; - - - 109;98;111;116;46;98;97;99;107;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;51;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - - - - -`,VW=` - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;54;50;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;50;57;52;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;51;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;52;57;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;51;57;50;44;32;51;48;48;41; - - -`,qW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;50;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,UW=` - - - 105;110;116;32;108;105;110;107;115;32;61;32;57;57; - - - 105;110;116;32;114;101;99;104;116;115;32;61;32;57;57; - - - 108;105;110;107;115;32;62;61;32;50;48;32;124;124;32;114;101;99;104;116;115;32;62;61;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 108;105;110;107;115;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 114;101;99;104;116;115;32;61;32;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - -`,QW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;49;44;32;34;85;109;100;114;101;104;117;110;103;101;110;34;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,$W=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53;32;38;38;32;109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 248; - - - - -`,YW=` - - - 105;110;116;32;115;101;105;116;101;32;61;32;51;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;101;105;116;101;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,KW=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 105;110;116;32;106;32;61;32;48; - 106;32;60;32;52; - 106;43;43; - - - 105;110;116;32;107;32;61;32;48; - 107;32;60;32;53; - 107;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,XW=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,eE=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,tE=` - - - 98;111;111;108;101;97;110;32;100;117;110;107;101;108;32;61;32;102;97;108;115;101; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51;48; - 105;43;43; - - - 109;98;111;116;46;103;101;116;66;114;105;103;104;116;110;101;115;115;40;41;32;60;32;50;48; - - - 100;117;110;107;101;108;32;61;32;116;114;117;101; - - - - - 248; - - - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 100;117;110;107;101;108; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;69;115;32;119;97;114;32;100;117;110;107;101;108;34;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;73;109;109;101;114;32;104;101;108;108;34;41; - - - - -`,nE=` - - - 105;110;116;32;115;116;114;101;99;107;101;32;61;32;122;101;110;116;105;109;101;116;101;114;40;51;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;115;116;114;101;99;107;101;41; - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;83;116;114;101;99;107;101;58;32;34;32;43;32;115;116;114;101;99;107;101;32;43;32;34;32;99;109;34;41; - - -`,iE=` - - - 114;101;116;117;114;110;32;117;109;100;114;101;104;117;110;103;101;110;32;42;32;50;48; - - -`,aE=` - - - 109;98;111;116;46;114;101;115;101;116;84;105;109;101;114;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;53;48;44;32;53;48;41; - - - 109;98;111;116;46;103;101;116;84;105;109;101;114;40;41;32;60;32;50; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,sE=` - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;41; - - -`,rE=` - - - 109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;115;116;111;112;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,oE=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;50;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,lE=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,uE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - -`,mE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;48;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;48;44;32;50;53;53;41; - - - 109;98;111;116;46;112;97;117;115;101;40;53;48;48;41; - - - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - -`,cE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;50;48;41; - - - - -`,dE=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,hE=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - -`,gE=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - -`,pE=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;115;101;116;76;101;100;40;50;53;53;44;32;49;50;56;44;32;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;56;48;44;32;50;48;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;108;101;100;79;102;102;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - - -`,fE=` - - - 109;98;111;116;46;115;101;116;76;101;100;40;48;44;32;50;53;53;44;32;48;41; - - -`,bE=` - - - 33;109;98;111;116;46;103;101;116;67;111;108;111;114;40;41;46;101;113;117;97;108;115;40;34;114;111;116;34;41; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,xE=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;62;32;49;53; - - - 109;98;111;116;46;105;115;76;105;110;101;76;101;102;116;40;41; - - - 109;98;111;116;46;100;114;105;118;101;40;49;48;44;32;52;48;41; - - - - - 109;98;111;116;46;100;114;105;118;101;40;52;48;44;32;49;48;41; - - - - - - - 109;98;111;116;46;115;116;111;112;40;41; - - -`,vE=` - - - -1; - - - 109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - -1; - - - - - 248; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;115;104;111;119;84;101;120;116;40;34;71;101;115;116;111;112;112;116;34;41; - - -`,wE=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - -`,yE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;52; - 105;43;43; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,zE=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;49;56;48;41; - - -`,IE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;51; - 105;43;43; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;56;48;48;44;32;51;48;48;41; - - - 109;98;111;116;46;112;108;97;121;84;111;110;101;40;54;48;48;44;32;51;48;48;41; - - - - -`,kE=` - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;50; - 105;43;43; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - -`,SE=` - - - 33;109;98;111;116;46;105;115;66;117;116;116;111;110;65;40;41; - - - 109;98;111;116;46;112;97;117;115;101;40;49;48;48;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - -`,TE=` - - - 109;98;111;116;46;103;101;116;76;105;110;101;40;41;32;61;61;32;48; - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;54;48;41; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - 109;98;111;116;46;116;117;114;110;76;101;102;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;51;48;41; - - - - - - -`,_E=` - - - 109;98;111;116;46;103;101;116;68;105;115;116;97;110;99;101;40;41;32;60;32;50;48; - - - 109;98;111;116;46;116;117;114;110;82;105;103;104;116;40;41; - - - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;41; - - - - -`,AE=` - - - 109;98;111;116;46;102;111;114;119;97;114;100;40;53;48;41; - - -`,jE=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;118;111;114;110;97;109;101;32;34;86;111;114;110;97;109;101;34; - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;110;97;99;104;110;97;109;101;32;34;78;97;99;104;110;97;109;101;34; - - - 111;117;116;112;117;116;58;32;118;111;114;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;32;34;32;43;32;110;97;99;104;110;97;109;101;46;99;104;97;114;65;116;40;48;41;32;43;32;34;46;34; - - -`,CE=` - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;90;105;101;103;101;110;98;111;99;107;34; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;110;100;101;120;79;102;40;34;101;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;108;97;115;116;73;110;100;101;120;79;102;40;34;101;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;115;69;109;112;116;121;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;101;113;117;97;108;115;73;103;110;111;114;101;67;97;115;101;40;34;90;73;69;71;69;78;66;79;67;75;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;114;101;112;108;97;99;101;40;34;98;111;99;107;34;44;32;34;107;228;115;101;34;41; - - - 111;117;116;112;117;116;58;32;34;77;228;104;34;46;114;101;112;101;97;116;40;51;41; - - - 111;117;116;112;117;116;58;32;34;65;110;110;97;34;46;99;111;109;112;97;114;101;84;111;40;34;66;101;110;34;41; - - - 105;110;116;32;97;108;116;101;114;32;61;32;73;110;116;101;103;101;114;46;112;97;114;115;101;73;110;116;40;34;55;34;41; - - - 111;117;116;112;117;116;58;32;97;108;116;101;114;32;43;32;49; - - - 83;116;114;105;110;103;32;116;101;120;116;32;61;32;83;116;114;105;110;103;46;118;97;108;117;101;79;102;40;97;108;116;101;114;41;32;43;32;34;32;74;97;104;114;101;34; - - - 111;117;116;112;117;116;58;32;116;101;120;116; - - -`,DE=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;111;114;116;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;32;114;117;101;99;107;119;97;101;114;116;115;32;61;32;34;34; - - - 105;110;116;32;105;32;61;32;119;111;114;116;46;108;101;110;103;116;104;40;41;32;45;32;49; - 105;32;62;61;32;48; - 105;45;45; - - - 114;117;101;99;107;119;97;101;114;116;115;32;61;32;114;117;101;99;107;119;97;101;114;116;115;32;43;32;119;111;114;116;46;99;104;97;114;65;116;40;105;41; - - - - - 111;117;116;112;117;116;58;32;114;117;101;99;107;119;97;101;114;116;115; - - -`,BE=` - - - 83;116;114;105;110;103;32;122;101;105;108;101;32;61;32;34;76;117;100;119;105;103;59;71;101;105;223;98;111;99;107;59;55;34; - - - 83;116;114;105;110;103;91;93;32;116;101;105;108;101;32;61;32;122;101;105;108;101;46;115;112;108;105;116;40;34;59;34;41; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;116;101;105;108;101;46;108;101;110;103;116;104; - 105;43;43; - - - 111;117;116;112;117;116;58;32;105;32;43;32;34;58;32;34;32;43;32;116;101;105;108;101;91;105;93; - - - - -`,PE=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;115;97;116;122;32;34;83;97;116;122;32;101;105;110;103;101;98;101;110;34; - - - 83;116;114;105;110;103;91;93;32;119;111;101;114;116;101;114;32;61;32;115;97;116;122;46;115;112;108;105;116;40;34;32;34;41; - - - 111;117;116;112;117;116;58;32;34;87;246;114;116;101;114;58;32;34;32;43;32;119;111;101;114;116;101;114;46;108;101;110;103;116;104; - - -`,OE=` - - - 105;110;112;117;116;58;32;83;116;114;105;110;103;32;119;111;114;116;32;34;87;111;114;116;32;101;105;110;103;101;98;101;110;34; - - - 105;110;116;32;97;110;122;97;104;108;32;61;32;48; - - - 105;110;116;32;105;32;61;32;48; - 105;32;60;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - 105;43;43; - - - 119;111;114;116;46;99;104;97;114;65;116;40;105;41;32;61;61;32;39;101;39; - - - 97;110;122;97;104;108;32;61;32;97;110;122;97;104;108;32;43;32;49; - - - - - 248; - - - - - - - 111;117;116;112;117;116;58;32;34;65;110;122;97;104;108;32;101;58;32;34;32;43;32;97;110;122;97;104;108; - - -`,ME=` - - - 83;116;114;105;110;103;32;119;111;114;116;32;61;32;34;71;101;105;223;98;111;99;107;34; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;108;101;110;103;116;104;40;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;99;104;97;114;65;116;40;48;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;48;44;32;52;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;115;117;98;115;116;114;105;110;103;40;52;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;105;110;100;101;120;79;102;40;34;98;111;99;107;34;41; - - - 111;117;116;112;117;116;58;32;119;111;114;116;46;116;111;85;112;112;101;114;67;97;115;101;40;41; - - -`,ZE=`// Labyrinth mit Ausgängen (nach Kara): jede Wandreihe hat genau eine Lücke, immer weiter rechts. Ein Zeichen = 30 cm. -raster: 3 -.......Z -#####.## -........ -##.##### ->....... -`,LE=`// Labyrinth mit Ausgängen, drei Reihen. Ein Zeichen = 30 cm. -raster: 3 -........Z -######.## -......... -####.#### -......... -.######## ->........ -`,WE=`// Um die Ecke (Sequenz im Kurs, Lektion 5): ein Zeichen = 30 cm. -raster: 3 -Z.... -###.. -..... -..### -^.... -`,EE=`// Fahre bis Rot: kurzer Weg -..... -..r.. -..... -..... -..^.. -`,NE=`// Fahre bis Rot: erst kommt ein grünes Feld -..... -..r.. -..... -..... -..g.. -..... -..... -..^.. -`,FE=`// Bis Blau, bei Grün piepen -..... -..b.. -..... -..g.. -..... -..^.. -`,RE=`// Bis Blau, ohne grünes Feld -..... -..b.. -..... -..... -..... -..^.. -`,JE=`// Hindernis ausweichen: ein Klotz im Weg, das rote Feld liegt dahinter. -......... -.....r... -.....o... -.....o... -......... -..#...... -......... -......... -......... -..^...... -`,GE=`// Hindernis ausweichen: zwei Klötze im Weg. -......... -.......r. -.......o. -.......o. -......... -....#.... -......... -......... -......... -.#....... -......... -......... -......... -.^....... -`,HE=`// Labyrinth: Gänge 30 cm breit, Wände 10 cm dick. Ziel oben links. -........... -.Z......... -........... -########... -........... -........... -........... -...######## -........... -.........<. -........... -`,VE=`// Labyrinth: Gänge 30 cm breit, Wände 10 cm dick. Ziel oben rechts. -........... -.........Z. -........... -...######## -........... -........... -........... -########... -........... -.>......... -........... -`,qE=`// Linienfolger: Die Linie biegt nach rechts ab und endet vor der Wand. -######### -......... -......... -....o.... -....o.... -......... -......... -......... -......... -......... -......... -......... -..^...... -linie: 2,12 2,8 4,5 4,0.6 -`,UE=`// Linienfolger: Die Linie biegt nach links ab und endet vor der Wand. -######### -......... -......... -....o.... -....o.... -......... -......... -......... -......... -......... -......... -......... -......^.. -linie: 6,12 6,8 4,5 4,0.6 -`,QE=`// Linienfolger mit Stopp: Am Ende der Linie liegt ein rotes Feld. -......... -.....r... -.....o... -.....o... -......... -......... -......... -......... -......... -......... -......... -..^...... -linie: 2,11 2,8 5,4 5,1.5 -`,$E=`// Linienfolger mit Stopp: Die Linie schlängelt sich zum roten Feld. -........... -...r....... -...o....... -...o....... -........... -........... -........... -........... -........... -........... -........... -........... -........... -.......^... -linie: 7,13 7,10 5,8 5,7 3,5 3,1.5 -`,YE=`// Einparken: rechts parken Autos (Klötze), die blaue Markierung zeigt die Lücke. -#....#### -#....#### -#....#### -#..b....# -#.....o.# -#.......# -#....#### -#....#### -#....#### -#....#### -#....#### -#..^.#### -`,KE=`// Einparken: Die Lücke liegt weiter vorn. -#....#### -#..b....# -#.....o.# -#.......# -#....#### -#....#### -#....#### -#....#### -#....#### -#....#### -#....#### -#..^.#### -`,XE=`// Wo ist am meisten Platz? Rechts ist es am weitesten. -........... -.....#..... -........... -........... -.#...^....# -........... -.....#..... -`,eN=`// Wo ist am meisten Platz? Vorn ist es am weitesten. -.....#..... -........... -........... -........... -........... -........... -....#^.#... -........... -........... -.....#..... -`,tN=`// Bis zur Linie: der schwarze Rand ist nah -..... -===== -..... -..... -..^.. -..... -`,nN=`// Bis zur Linie: der schwarze Rand ist weiter weg -..... -===== -..... -..... -..... -..... -..... -..^.. -..... -`,iN=`// Slalom: ein Zeichen = 30 cm. Vier Pylonen, abwechselnd oben und unten herum, Ziel am Ende. -raster: 3 -......... ->#.#.#.#Z -......... -`,aN=`// Tunnelsucher (nach Kara): Halt im ersten Feld mit Wand links UND rechts (hellgrün). Ein Zeichen = 30 cm. -raster: 3 -..#..###. ->....o... -....##### -`,sN=`// Tunnelsucher, nach oben. Ein Zeichen = 30 cm. -raster: 3 -.#.#. -.#o#. -...#. -.#... -..... -..^.. -`,rN=`// Kleeblatt im Wald (nach Kara): Bäume liegen auf dem Weg zum Ziel, nie zwei nebeneinander. Ein Zeichen = 30 cm. -raster: 3 -.......... ->..#.#..#Z -.......... -`,oN=`// Kleeblatt im Wald, nach oben. Ein Zeichen = 30 cm. -raster: 3 -.Z. -.#. -... -.#. -... -... -.^. -`,lN=`// Wand vorn? Vor dem mBot2 ist viel Platz. -##### -..... -..... -..... -..... -..... -..... -..^.. -..... -`,uN=`// Bis zur Wand: kurzer Weg -##### -..... -..Z.. -..... -..... -..^.. -`,mN=`// Bis zur Wand: langer Weg -##### -..... -..Z.. -..... -..... -..... -..... -..... -..... -..... -..... -..... -..... -..... -..^.. -`,cN=`// Bis zur Wand: mittlerer Weg -##### -..... -..Z.. -..... -..... -..... -..... -..... -..... -..^.. -`,dN=`// Wand vorn? Die Wand steht direkt vor dem mBot2. -##### -..... -..^.. -..... -`,hN=`// Zwei Sekunden mit halber Kraft: Wo bleibt er stehen? -..... -..... -..... -..o.. -..... -..^.. -`,gN=`// Fahre bis zum Ziel: kurzer Weg, keine Wand -..... -..Z.. -..... -..... -..^.. -..... -`,pN=`// Fahre bis zum Ziel: langer Weg, keine Wand -..... -..Z.. -..... -..... -..... -..... -..... -..... -..... -..... -..^.. -..... -`,fN=""+new URL("ada-lovelace-DS_988PF.jpg",import.meta.url).href,bN=""+new URL("aufgabe-mehrwertsteuer-C-TFTrWz.svg",import.meta.url).href,xN=""+new URL("ben-shneiderman-Cev5hrqS.jpg",import.meta.url).href,vN=""+new URL("caesar-ascii-Bix93F5i.svg",import.meta.url).href,wN=""+new URL("caesar-scheibe-CMRPZI6Y.svg",import.meta.url).href,yN=""+new URL("caesar-verschiebung-DIR-lDbs.svg",import.meta.url).href,zN=""+new URL("aufgabe-mehrwertsteuer-CnrZ00ny.svg",import.meta.url).href,IN=""+new URL("kino-saal-C8zaJk4X.svg",import.meta.url).href,kN=""+new URL("kino-schleifen-D7sfo9iK.svg",import.meta.url).href,SN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDkyIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iNDkyIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiPgogICAgPCEtLSBTdGFydCAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMTAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMjgiIHJ4PSIxNCIgZmlsbD0iI2QxZjJkOSIvPgogICAgPGxpbmUgeDE9IjE1MCIgeTE9IjM4IiB4Mj0iMTUwIiB5Mj0iNTQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gQW53ZWlzdW5nIC0tPgogICAgPHJlY3QgeD0iNTAiIHk9IjU2IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iODYiIHgyPSIxNTAiIHkyPSI5OCIgbWFya2VyLWVuZD0idXJsKCNhKSIvPgogICAgPCEtLSBSYXV0ZSAxIC0tPgogICAgPHBhdGggZD0iTTE1MCAxMDAgTDIzNSAxMjggTDE1MCAxNTYgTDY1IDEyOCBaIiBmaWxsPSIjZmZmNGQ2Ii8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMTU2IiB4Mj0iMTUwIiB5Mj0iMTcyIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8cmVjdCB4PSI1MCIgeT0iMTc0IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMjA0IiB4Mj0iMTUwIiB5Mj0iMjUwIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIG5laW4tV2VnIHVtIGRlbiBLYXN0ZW4gaGVydW0gLS0+CiAgICA8cG9seWxpbmUgcG9pbnRzPSIyMzUsMTI4IDI4NSwxMjggMjg1LDIyMiAxNTAsMjIyIiBmaWxsPSJub25lIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIFJhdXRlIDIgLS0+CiAgICA8cGF0aCBkPSJNMTUwIDI1MiBMMjM1IDI4MCBMMTUwIDMwOCBMNjUgMjgwIFoiIGZpbGw9IiNmZmY0ZDYiLz4KICAgIDxsaW5lIHgxPSIyMzUiIHkxPSIyODAiIHgyPSIyNjgiIHkyPSIyODAiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjI3MCIgeT0iMjY1IiB3aWR0aD0iMTE1IiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8IS0tIHp1csO8Y2sgenVyIFByw7xmdW5nIC0tPgogICAgPHBvbHlsaW5lIHBvaW50cz0iMzI3LDI2NSAzMjcsMjM4IDE1MCwyMzgiIGZpbGw9Im5vbmUiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzMDgiIHgyPSIxNTAiIHkyPSIzMjYiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjUwIiB5PSIzMjgiIHdpZHRoPSIyMDAiIGhlaWdodD0iMzAiIGZpbGw9IiNlNWYwZmYiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzNTgiIHgyPSIxNTAiIHkyPSIzNzQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gRW5kZSAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMzc2IiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjI4IiByeD0iMTQiIGZpbGw9IiNkMWYyZDkiLz4KICA8L2c+CiAgPGcgdGV4dC1hbmNob3I9Im1pZGRsZSI+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjI5Ij5TdGFydDwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iNzYiPlB1dCBhIGN1cCBvbiB0aGUgdGFibGU8L3RleHQ+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjEzMyI+U3VnYXIgd2FudGVkPzwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMTk0Ij5BZGQgc3VnYXI8L3RleHQ+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjI4NSI+VGVhIHN0aWxsIHRvbyBob3Q/PC90ZXh0PgogICAgPHRleHQgeD0iMzI3LjUiIHk9IjI4NSI+V2FpdCAxIG1pbnV0ZTwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMzQ4Ij5EcmluayB0aGUgdGVhPC90ZXh0PgogICAgPHRleHQgeD0iMTUwIiB5PSIzOTUiPkVuZDwvdGV4dD4KICA8L2c+CiAgPGcgZm9udC1zaXplPSIxMiIgZm9udC13ZWlnaHQ9IjYwMCI+CiAgICA8dGV4dCB4PSIxNTYiIHk9IjE2OCIgZmlsbD0iIzI0OGEzZCI+eWVzPC90ZXh0PgogICAgPHRleHQgeD0iMjQyIiB5PSIxMjIiIGZpbGw9IiNkNzAwMTUiPm5vPC90ZXh0PgogICAgPHRleHQgeD0iMjQwIiB5PSIyNzQiIGZpbGw9IiMyNDhhM2QiPnllczwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1NiIgeT0iMzIwIiBmaWxsPSIjZDcwMDE1Ij5ubzwvdGV4dD4KICA8L2c+CiAgPCEtLSBMZWdlbmRlIC0tPgogIDxsaW5lIHgxPSIxMCIgeTE9IjQxOCIgeDI9IjM5MCIgeTI9IjQxOCIgc3Ryb2tlPSIjZDFkMWQ2Ii8+CiAgPGcgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuNSI+CiAgICA8cmVjdCB4PSI0MCIgeT0iNDMwIiB3aWR0aD0iMzYiIGhlaWdodD0iMTgiIHJ4PSI5IiBmaWxsPSIjZDFmMmQ5Ii8+CiAgICA8cmVjdCB4PSIyMjAiIHk9IjQzMCIgd2lkdGg9IjM2IiBoZWlnaHQ9IjE4IiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8cGF0aCBkPSJNNTggNDU4IEw3NiA0NjkgTDU4IDQ4MCBMNDAgNDY5IFoiIGZpbGw9IiNmZmY0ZDYiLz4KICAgIDxwYXRoIGQ9Ik0yMjYgNDYwIEwyNTYgNDYwIEwyNTAgNDc4IEwyMjAgNDc4IFoiIGZpbGw9IiNmMmU2ZmYiLz4KICA8L2c+CiAgPGcgZm9udC1zaXplPSIxMiIgZmlsbD0iIzNhM2EzYyI+CiAgICA8dGV4dCB4PSI4NiIgeT0iNDQzIj5TdGFydCBvciBlbmQ8L3RleHQ+CiAgICA8dGV4dCB4PSIyNjYiIHk9IjQ0MyI+U3RhdGVtZW50PC90ZXh0PgogICAgPHRleHQgeD0iODYiIHk9IjQ3MyI+RGVjaXNpb248L3RleHQ+CiAgICA8dGV4dCB4PSIyNjYiIHk9IjQ3MyI+SW5wdXQgb3Igb3V0cHV0PC90ZXh0PgogIDwvZz4KPC9zdmc+Cg==",TN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE5MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwIiB5PSIyNCIgZm9udC13ZWlnaHQ9IjYwMCI+V2hlcmUgZG9lcyB0aGUgZWxlbWVudCBsYW5kPzwvdGV4dD4KICA8cmVjdCB4PSIyMCIgeT0iNDAiIHdpZHRoPSIxOTAiIGhlaWdodD0iMzUiIGZpbGw9IiNkYmVhZmUiLz4KICA8cmVjdCB4PSIyMCIgeT0iNzUiIHdpZHRoPSIxOTAiIGhlaWdodD0iMzUiIGZpbGw9IiNkY2ZjZTciLz4KICA8bGluZSB4MT0iMjAiIHkxPSI3NSIgeDI9IjIxMCIgeTI9Ijc1IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS1kYXNoYXJyYXk9IjQgMyIvPgogIDxyZWN0IHg9IjIwIiB5PSI0MCIgd2lkdGg9IjE5MCIgaGVpZ2h0PSI3MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIxMTUiIHk9IjYyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMWQ0ZWQ4Ij50b3AgaGFsZjwvdGV4dD4KICA8dGV4dCB4PSIxMTUiIHk9Ijk3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMTU4MDNkIj5ib3R0b20gaGFsZjwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9IjYyIj7ihpIgZ29lcyA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+aW4gZnJvbnQ8L3RzcGFuPjwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9Ijk3Ij7ihpIgZ29lcyA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+YWZ0ZXI8L3RzcGFuPjwvdGV4dD4KICA8cmVjdCB4PSIyMCIgeT0iMTMyIiB3aWR0aD0iMTkwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjExNSIgeT0iMTU3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2Ij7DuDwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9IjE1NyI+4oaSIDx0c3BhbiBmb250LXdlaWdodD0iNjAwIj5yZXBsYWNlczwvdHNwYW4+IGl0PC90ZXh0Pgo8L3N2Zz4K",_N=""+new URL("l03-lochkarte-beschriftet-BjBFZjzb.svg",import.meta.url).href,AN=""+new URL("l03-lochkarte-BB3rWQOm.svg",import.meta.url).href,jN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NjAgMjkwIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSI+CjxyZWN0IHdpZHRoPSI0NjAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+Cjx0ZXh0IHg9IjUwIiB5PSI0NCIgZm9udC1zaXplPSI0MCIgZmlsbD0iIzZlNmU3MyIgdGV4dExlbmd0aD0iMjg4IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiPnByaWNlPHRzcGFuIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPlc8L3RzcGFuPml0aDx0c3BhbiBmaWxsPSIjMDA3YWZmIiBmb250LXdlaWdodD0iNzAwIj5UPC90c3Bhbj5heDwvdGV4dD4KPGxpbmUgeDE9IjE4MiIgeTE9IjU0IiB4Mj0iMTgyIiB5Mj0iODIiIHN0cm9rZT0iIzAwN2FmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIHN0cm9rZS1kYXNoYXJyYXk9IjUgNCIvPgo8bGluZSB4MT0iMjc4IiB5MT0iNTQiIHgyPSIyNzgiIHkyPSI4MiIgc3Ryb2tlPSIjMDA3YWZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWRhc2hhcnJheT0iNSA0Ii8+CjxwYXRoIGQ9Ik0yMCAyNzIgSDQ0MCIgc3Ryb2tlPSIjZTVkM2IwIiBzdHJva2Utd2lkdGg9IjQiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTU2IDE3MiBMMTUwIDIyNiIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTE1MCAyMjYgTDE1NiAyNjIiIHN0cm9rZT0iI2I4OGE1MiIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMTUwIiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIxNjEiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTMyOCAxNzIgTDMzMiAyMjYiIHN0cm9rZT0iI2I4OGE1MiIgc3Ryb2tlLXdpZHRoPSIxMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMzIgMjI2IEwzMjggMjYyIiBzdHJva2U9IiNiODhhNTIiIHN0cm9rZS13aWR0aD0iOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxjaXJjbGUgY3g9IjMzMiIgY3k9IjIyNiIgcj0iNiIgZmlsbD0iIzdhNTUzMCIgb3BhY2l0eT0iMC41NSIvPgo8ZWxsaXBzZSBjeD0iMzMzIiBjeT0iMjY3IiByeD0iMTEiIHJ5PSI1IiBmaWxsPSIjN2E1NTMwIi8+CjxwYXRoIGQ9Ik0xMjAgMTQwIEMxMDQgMTUwIDEwNCAxNzYgMTEwIDE5NiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8ZWxsaXBzZSBjeD0iMTEwIiBjeT0iMjAwIiByeD0iNSIgcnk9IjkiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTEyMCAxNzIgQzEwOCAxNTIgMTE0IDEyOCAxMzQgMTIyIEMxNDggMTIwIDE1NCA4NCAxODIgODQgQzIxMCA4NCAyMDggMTI2IDIzMCAxMjQgQzI1MiAxMjYgMjUwIDg0IDI3OCA4NCBDMzA2IDg0IDMwOCAxMjIgMzE4IDEyOCBDMzQwIDEzMiAzNDggMTU4IDM2NCAxNTggQzM3NiAxNTggMzc4IDEyNCAzODIgMTEwIEMzODYgOTggNDAwIDk2IDQxMiA5OSBDNDI0IDEwMiA0MzAgMTA4IDQyOCAxMTYgQzQyNiAxMjMgNDE0IDEyNCA0MDQgMTIyIEMzOTYgMTIyIDM5NCAxMjggMzk0IDEzNiBDMzk0IDE2MiAzODggMTg0IDM2NiAxODYgQzM1MCAxODggMzQwIDE4MiAzMzAgMTc4IEMyNjAgMTg4IDIwMCAxODggMTQwIDE4MiBDMTMwIDE4MCAxMjQgMTc3IDEyMCAxNzIgWiIgZmlsbD0iI2QyYTU2YSIgc3Ryb2tlPSIjN2E1NTMwIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTM0IDE3MiBMMTI4IDIyNiIgc3Ryb2tlPSIjZDJhNTZhIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTEyOCAyMjYgTDEzNCAyNjIiIHN0cm9rZT0iI2QyYTU2YSIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMTI4IiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIxMzkiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTMwOCAxNzIgTDMxMiAyMjYiIHN0cm9rZT0iI2QyYTU2YSIgc3Ryb2tlLXdpZHRoPSIxMyIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zMTIgMjI2IEwzMDggMjYyIiBzdHJva2U9IiNkMmE1NmEiIHN0cm9rZS13aWR0aD0iOCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxjaXJjbGUgY3g9IjMxMiIgY3k9IjIyNiIgcj0iNiIgZmlsbD0iIzdhNTUzMCIgb3BhY2l0eT0iMC41NSIvPgo8ZWxsaXBzZSBjeD0iMzEzIiBjeT0iMjY3IiByeD0iMTEiIHJ5PSI1IiBmaWxsPSIjN2E1NTMwIi8+CjxwYXRoIGQ9Ik0zOTAgMTAzIEwzODYgOTIgTDM5OCAxMDAgWiIgZmlsbD0iI2QyYTU2YSIgc3Ryb2tlPSIjN2E1NTMwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KPGNpcmNsZSBjeD0iNDA2IiBjeT0iMTA3IiByPSIzLjIiIGZpbGw9IiMxYzFjMWUiLz4KPHBhdGggZD0iTTQwMSAxMDQgUTQwNiAxMDEgNDExIDEwNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjN2E1NTMwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNNDIyIDEwNyBxNCAxIDUgNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjN2E1NTMwIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNNDE2IDExOSBRNDIyIDExOCA0MjcgMTE3IiBmaWxsPSJub25lIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+Cjwvc3ZnPgo=",CN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjEwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjIxMCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsIE1lbmxvLCBDb25zb2xhcywgbW9ub3NwYWNlIiBmb250LXNpemU9IjE2Ij5pbnQgYWdlID0gMTQ8L3RleHQ+CiAgPHBhdGggZD0iTTEzMCA3MCBMMTcwIDUwIEwzMDAgNTAgTDI2MCA3MCBaIiBmaWxsPSIjYzk4YTNjIiBzdHJva2U9IiM4YTVhMWYiIHN0cm9rZS13aWR0aD0iMiIvPgogIDxyZWN0IHg9IjE4MCIgeT0iMzQiIHdpZHRoPSI0NCIgaGVpZ2h0PSI1MCIgcng9IjMiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIyMDIiIHk9IjYwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjIwIiBmb250LXdlaWdodD0iNzAwIj4xNDwvdGV4dD4KICA8cmVjdCB4PSIxMzAiIHk9IjcwIiB3aWR0aD0iMTMwIiBoZWlnaHQ9IjEwMCIgZmlsbD0iI2U5Yjg3MiIgc3Ryb2tlPSIjOGE1YTFmIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cGF0aCBkPSJNMjYwIDcwIEwzMDAgNTAgTDMwMCAxNTAgTDI2MCAxNzAgWiIgZmlsbD0iI2Q0OWE0YyIgc3Ryb2tlPSIjOGE1YTFmIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8cmVjdCB4PSIxNTAiIHk9IjEwMCIgd2lkdGg9IjkwIiBoZWlnaHQ9IjMwIiByeD0iNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjOGE1YTFmIi8+CiAgPHRleHQgeD0iMTk1IiB5PSIxMjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPmFnZTwvdGV4dD4KICA8cmVjdCB4PSIxMzgiIHk9IjE0MiIgd2lkdGg9IjM0IiBoZWlnaHQ9IjIwIiByeD0iMTAiIGZpbGw9IiMwMDdhZmYiLz4KICA8dGV4dCB4PSIxNTUiIHk9IjE1NiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMiIgZmlsbD0iI2ZmZmZmZiIgZm9udC13ZWlnaHQ9IjYwMCI+aW50PC90ZXh0PgogIDxnIHN0cm9rZT0iIzZlNmU3MyI+CiAgICA8bGluZSB4MT0iMzEwIiB5MT0iNDgiIHgyPSIyMjYiIHkyPSI1MiIvPgogICAgPGxpbmUgeDE9IjMxMCIgeTE9IjExNSIgeDI9IjI0MiIgeTI9IjExNSIvPgogICAgPGxpbmUgeDE9IjgwIiB5MT0iMTUyIiB4Mj0iMTM0IiB5Mj0iMTUyIi8+CiAgPC9nPgogIDx0ZXh0IHg9IjMxNiIgeT0iNTIiPlZhbHVlPC90ZXh0PgogIDx0ZXh0IHg9IjMxNiIgeT0iMTE5Ij5OYW1lPC90ZXh0PgogIDx0ZXh0IHg9IjgiIHk9IjE1NiI+RGF0YSB0eXBlPC90ZXh0PgogIDx0ZXh0IHg9IjIwMCIgeT0iMTk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5UaGUgbmFtZSBnZXRzIHlvdSB0byB0aGUgdmFsdWUuPC90ZXh0Pgo8L3N2Zz4K",DN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjAwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjAiPgogICAgPHRleHQgeD0iNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+cG9pbnRzPC90ZXh0PgogICAgPHRleHQgeD0iMTI1IiB5PSIxMTAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPj08L3RleHQ+CiAgICA8dGV4dCB4PSIyNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+cG9pbnRzICsgNTwvdGV4dD4KICA8L2c+CiAgPHJlY3QgeD0iMTgwIiB5PSI4NiIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjYwIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPuKRoCB3b3JrIG91dCB0aGUgcmlnaHQ6IDEwICsgNSA9IDE1PC90ZXh0PgogIDxwYXRoIGQ9Ik0yNTAgODIgQzIzMCAzMCwgOTAgMzAsIDY0IDg0IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDdhZmYiIHN0cm9rZS13aWR0aD0iMi41IiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgPHRleHQgeD0iMTYwIiB5PSIzNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiI+4pGhIHN0b3JlIHRoZSByZXN1bHQgb24gdGhlIGxlZnQ8L3RleHQ+CiAgPHJlY3QgeD0iMjAiIHk9IjE2NCIgd2lkdGg9IjM2MCIgaGVpZ2h0PSIyNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciLz4KICA8dGV4dCB4PSIyMDAiIHk9IjE4MiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+cG9pbnRzIGJlZm9yZTogPHRzcGFuIGZvbnQtd2VpZ2h0PSI3MDAiPjEwPC90c3Bhbj4gICDihpIgICBwb2ludHMgYWZ0ZXI6IDx0c3BhbiBmb250LXdlaWdodD0iNzAwIj4xNTwvdHNwYW4+PC90ZXh0Pgo8L3N2Zz4K",BN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjYwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjYwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiPgogICAgPHBhdGggZD0iTTQwIDExMCBMNDggMTgwIEwxMDIgMTgwIEwxMTAgMTEwIi8+CiAgICA8cGF0aCBkPSJNMTY1IDExMCBMMTczIDE4MCBMMjI3IDE4MCBMMjM1IDExMCIvPgogICAgPHBhdGggZD0iTTI5MCAxMTAgTDI5OCAxODAgTDM1MiAxODAgTDM2MCAxMTAiLz4KICA8L2c+CiAgPHBhdGggZD0iTTQ0IDEzNSBMNDggMTgwIEwxMDIgMTgwIEwxMDYgMTM1IFoiIGZpbGw9IiNmZGJhNzQiLz4KICA8cGF0aCBkPSJNMjk0IDEzNSBMMjk4IDE4MCBMMzUyIDE4MCBMMzU2IDEzNSBaIiBmaWxsPSIjOTNjNWZkIi8+CiAgPHRleHQgeD0iNzUiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+bGVmdDwvdGV4dD4KICA8dGV4dCB4PSI3NSIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5qdWljZTwvdGV4dD4KICA8dGV4dCB4PSIyMDAiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+dGVtcDwvdGV4dD4KICA8dGV4dCB4PSIyMDAiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+ZW1wdHk8L3RleHQ+CiAgPHRleHQgeD0iMzI1IiB5PSIyMDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPnJpZ2h0PC90ZXh0PgogIDx0ZXh0IHg9IjMyNSIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj53YXRlcjwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxwYXRoIGQ9Ik04OCAxMDQgQzExMCA3MCwgMTYwIDcwLCAxODYgMTA0Ii8+CiAgICA8cGF0aCBkPSJNMzE4IDEwNCBDMjcwIDE1LCAxMzAgMTUsIDgyIDEwNCIvPgogICAgPHBhdGggZD0iTTIxNCAxMDQgQzI0MCA3MCwgMjkwIDcwLCAzMTIgMTA0Ii8+CiAgPC9nPgogIDxnIGZvbnQtd2VpZ2h0PSI3MDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPgogICAgPGNpcmNsZSBjeD0iMTM3IiBjeT0iODAiIHI9IjExIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz48dGV4dCB4PSIxMzciIHk9Ijg1Ij4xPC90ZXh0PgogICAgPGNpcmNsZSBjeD0iMjAwIiBjeT0iMzgiIHI9IjExIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz48dGV4dCB4PSIyMDAiIHk9IjQzIj4yPC90ZXh0PgogICAgPGNpcmNsZSBjeD0iMjYzIiBjeT0iODAiIHI9IjExIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz48dGV4dCB4PSIyNjMiIHk9Ijg1Ij4zPC90ZXh0PgogIDwvZz4KICA8ZyB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMiI+CiAgICA8dGV4dCB4PSI3MCIgeT0iMjQ3Ij4xOiB0ZW1wID0gbGVmdDwvdGV4dD4KICAgIDx0ZXh0IHg9IjIwMCIgeT0iMjQ3Ij4yOiBsZWZ0ID0gcmlnaHQ8L3RleHQ+CiAgICA8dGV4dCB4PSIzMzAiIHk9IjI0NyI+MzogcmlnaHQgPSB0ZW1wPC90ZXh0PgogIDwvZz4KPC9zdmc+Cg==",PN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE1MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDxyZWN0IHg9IjIwIiB5PSI1MCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+PHJlY3QgeD0iMzEwIiB5PSI1MCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+CiAgPHJlY3QgeD0iOTAiIHk9IjUwIiB3aWR0aD0iMjIwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZGNmY2U3Ii8+CiAgPGxpbmUgeDE9IjIwIiB5MT0iNzQiIHgyPSIzODAiIHkyPSI3NCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8bGluZSB4MT0iNDYiIHkxPSI2OCIgeDI9IjQ2IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iNDYiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4wPC90ZXh0PgogIDxsaW5lIHgxPSI5MCIgeTE9IjY4IiB4Mj0iOTAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSI5MCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjE8L3RleHQ+CiAgPGxpbmUgeDE9IjEzNCIgeTE9IjY4IiB4Mj0iMTM0IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMTM0IiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MjwvdGV4dD4KICA8bGluZSB4MT0iMTc4IiB5MT0iNjgiIHgyPSIxNzgiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIxNzgiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4zPC90ZXh0PgogIDxsaW5lIHgxPSIyMjIiIHkxPSI2OCIgeDI9IjIyMiIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjIyMiIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjQ8L3RleHQ+CiAgPGxpbmUgeDE9IjI2NiIgeTE9IjY4IiB4Mj0iMjY2IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMjY2IiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+NTwvdGV4dD4KICA8bGluZSB4MT0iMzEwIiB5MT0iNjgiIHgyPSIzMTAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIzMTAiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj42PC90ZXh0PgogIDxsaW5lIHgxPSIzNTQiIHkxPSI2OCIgeDI9IjM1NCIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjM1NCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjc8L3RleHQ+CiAgPGNpcmNsZSBjeD0iOTAiIGN5PSI3NCIgcj0iNiIgZmlsbD0iIzE1ODAzZCIvPjxjaXJjbGUgY3g9IjMxMCIgY3k9Ijc0IiByPSI2IiBmaWxsPSIjMTU4MDNkIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI0MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+dmFsaWQ8L3RleHQ+CiAgPHRleHQgeD0iNDAiIHk9IjQwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj5pbnZhbGlkPC90ZXh0Pjx0ZXh0IHg9IjM2MCIgeT0iNDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNiOTFjMWMiIGZvbnQtd2VpZ2h0PSI2MDAiPmludmFsaWQ8L3RleHQ+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPgogICAgPHRleHQgeD0iMjAwIiB5PSIxMjQiIGZpbGw9IiMxNTgwM2QiPmdyYWRlICZndDs9IDEgJmFtcDsmYW1wOyBncmFkZSAmbHQ7PSA2PC90ZXh0PgogICAgPHRleHQgeD0iMjAwIiB5PSIxNDIiIGZpbGw9IiNiOTFjMWMiPmdyYWRlICZsdDsgMSB8fCBncmFkZSAmZ3Q7IDY8L3RleHQ+CiAgPC9nPgo8L3N2Zz4K",ON="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIj5Ib3cgdGhlIHdoaWxlIGxvb3AgcnVuczwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9IjQ0IiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjQwIiByeD0iMjAiIGZpbGw9IiNmZWYzYzciIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI2OSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIGNoZWNrIHRoZSBjb25kaXRpb248L3RleHQ+CiAgPHJlY3QgeD0iMTIwIiB5PSIxMjgiIHdpZHRoPSIxNjAiIGhlaWdodD0iNDAiIHJ4PSI2IiBmaWxsPSIjZGJlYWZlIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMTUzIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgcnVuIHRoZSBpbnNpZGU8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iODQiIHgyPSIyMDAiIHkyPSIxMjYiLz4KICAgIDxwYXRoIGQ9Ik0xMjAgMTQ4IEM4MCAxNDgsIDgwIDY0LCAxMTggNjQiLz4KICAgIDxwYXRoIGQ9Ik0yODAgNjQgTDM0MCA2NCBMMzQwIDIwMCBMMjgyIDIwMCIvPgogIDwvZz4KICA8dGV4dCB4PSIyMDgiIHk9IjExMCIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+dHJ1ZTwvdGV4dD4KICA8dGV4dCB4PSIzOCIgeT0iMTEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5iYWNrPC90ZXh0PgogIDx0ZXh0IHg9IjM4IiB5PSIxMjYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPnRvIHRoZSB0b3A8L3RleHQ+CiAgPHRleHQgeD0iMzQ4IiB5PSIxMzQiIGZpbGw9IiNiOTFjMWMiIGZvbnQtd2VpZ2h0PSI2MDAiPmZhbHNlPC90ZXh0PgogIDx0ZXh0IHg9IjI3NiIgeT0iMjA1IiB0ZXh0LWFuY2hvcj0iZW5kIj5jb250aW51ZSBiZWxvdyB0aGUgbG9vcDwvdGV4dD4KPC9zdmc+Cg==",MN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMTAwIiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+cHJlLXRlc3QgKHdoaWxlKTwvdGV4dD4KICA8dGV4dCB4PSIzMDAiIHk9IjIyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5wb3N0LXRlc3QgKGRvLXdoaWxlKTwvdGV4dD4KICA8bGluZSB4MT0iMjAwIiB5MT0iMTAiIHgyPSIyMDAiIHkyPSIyMjAiIHN0cm9rZT0iI2QxZDFkNiIvPgogIDxyZWN0IHg9IjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjE4IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjEwMCIgeT0iNjciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoCBjb25kaXRpb248L3RleHQ+CiAgPHJlY3QgeD0iMzUiIHk9IjEyNCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMTAwIiB5PSIxNDciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoSBpbnNpZGU8L3RleHQ+CiAgPHJlY3QgeD0iMjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzAwIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIGluc2lkZTwvdGV4dD4KICA8cmVjdCB4PSIyMzUiIHk9IjEyNCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjE4IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjMwMCIgeT0iMTQ3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgY29uZGl0aW9uPC90ZXh0PgogIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjYiIG1hcmtlci1lbmQ9InVybCgjYSkiPgogICAgPGxpbmUgeDE9IjEwMCIgeTE9IjgwIiB4Mj0iMTAwIiB5Mj0iMTIyIi8+CiAgICA8cGF0aCBkPSJNMzUgMTQyIEM4IDE0MiwgOCA2MiwgMzMgNjIiLz4KICAgIDxsaW5lIHgxPSIzMDAiIHkxPSI4MCIgeDI9IjMwMCIgeTI9IjEyMiIvPgogICAgPHBhdGggZD0iTTM2NSAxNDIgQzM5MiAxNDIsIDM5MiA2MiwgMzY3IDYyIi8+CiAgPC9nPgogIDx0ZXh0IHg9IjEwNiIgeT0iMTA2IiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjM4MCIgeT0iMTA2IiB0ZXh0LWFuY2hvcj0iZW5kIiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMTkwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5JZiB0aGUgY29uZGl0aW9uIGlzIGZhbHNlIGF0IG9uY2UsPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMjA2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj50aGUgaW5zaWRlIHJ1bnMgPHRzcGFuIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPm5ldmVyPC90c3Bhbj4uPC90ZXh0PgogIDx0ZXh0IHg9IjMwMCIgeT0iMTkwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5UaGUgaW5zaWRlIGFsd2F5cyBydW5zPC90ZXh0PgogIDx0ZXh0IHg9IjMwMCIgeT0iMjA2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj48dHNwYW4gZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+YXQgbGVhc3Qgb25jZTwvdHNwYW4+LjwvdGV4dD4KPC9zdmc+Cg==",ZN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjUwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTIwIiB5PSIxNCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIzNiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIFN0YXJ0IDx0c3BhbiBmaWxsPSIjNmU2ZTczIj4ob25seSBvbmNlKTwvdHNwYW4+PC90ZXh0PgogIDxyZWN0IHg9IjEyMCIgeT0iNzYiIHdpZHRoPSIxNjAiIGhlaWdodD0iMzQiIHJ4PSIxNyIgZmlsbD0iI2ZlZjNjNyIgc3Ryb2tlPSIjYjQ1MzA5IiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMDAiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgY2hlY2sgdGhlIGNvbmRpdGlvbjwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9IjEzOCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIxNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoiBydW4gdGhlIGluc2lkZTwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9IjIwMCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIyMjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoyBzdGVwLCBlLmcuIGkrKzwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMDAiIHkxPSI0OCIgeDI9IjIwMCIgeTI9Ijc0Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTEwIiB4Mj0iMjAwIiB5Mj0iMTM2Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTcyIiB4Mj0iMjAwIiB5Mj0iMTk4Ii8+CiAgICA8cGF0aCBkPSJNMTIwIDIxNyBDNjAgMjE3LCA2MCA5MywgMTE4IDkzIi8+CiAgICA8cGF0aCBkPSJNMjgwIDkzIEwzMzAgOTMiLz4KICA8L2c+CiAgPHRleHQgeD0iMjA4IiB5PSIxMjkiIGZpbGw9IiMxNTgwM2QiIGZvbnQtd2VpZ2h0PSI2MDAiPnRydWU8L3RleHQ+CiAgPHRleHQgeD0iNDgiIHk9IjE2MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+YmFjazwvdGV4dD4KICA8dGV4dCB4PSI0OCIgeT0iMTc2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj50byDikaE8L3RleHQ+CiAgPHRleHQgeD0iMzAwIiB5PSI4NCIgZmlsbD0iI2I5MWMxYyIgZm9udC13ZWlnaHQ9IjYwMCI+ZmFsc2U8L3RleHQ+CiAgPHRleHQgeD0iMzM2IiB5PSI5OCI+ZG9uZTwvdGV4dD4KPC9zdmc+Cg==",LN=""+new URL("l12-while-zu-for-DddKE_8R.svg",import.meta.url).href,WN=""+new URL("l13-verschachtelt-tabelle-jipYA7Ld.svg",import.meta.url).href,EN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMTkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxNCI+aW50W10gbnVtYmVycyA9IHsyLCA0LCA2LCA4LCAxMH08L3RleHQ+CiAgPHRleHQgeD0iMjAiIHk9IjU0IiBmaWxsPSIjNmU2ZTczIiBmb250LXNpemU9IjEyIj5JbmRleDwvdGV4dD4KICA8dGV4dCB4PSI5MCIgeT0iNTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPjA8L3RleHQ+PHJlY3QgeD0iNjAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iOTAiIHk9IjkyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNjAwIj4yPC90ZXh0PgogIDx0ZXh0IHg9IjE1MCIgeT0iNTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPjE8L3RleHQ+PHJlY3QgeD0iMTIwIiB5PSI2MiIgd2lkdGg9IjYwIiBoZWlnaHQ9IjQ2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjE1MCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjQ8L3RleHQ+CiAgPHRleHQgeD0iMjEwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+MjwvdGV4dD48cmVjdCB4PSIxODAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMjEwIiB5PSI5MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjYwMCI+NjwvdGV4dD4KICA8dGV4dCB4PSIyNzAiIHk9IjU0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDA3YWZmIiBmb250LXdlaWdodD0iNzAwIj4zPC90ZXh0PjxyZWN0IHg9IjI0MCIgeT0iNjIiIHdpZHRoPSI2MCIgaGVpZ2h0PSI0NiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIyNzAiIHk9IjkyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNjAwIj44PC90ZXh0PgogIDx0ZXh0IHg9IjMzMCIgeT0iNTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPjQ8L3RleHQ+PHJlY3QgeD0iMzAwIiB5PSI2MiIgd2lkdGg9IjYwIiBoZWlnaHQ9IjQ2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjMzMCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjEwPC90ZXh0PgogIDx0ZXh0IHg9IjIwIiB5PSI5MCIgZmlsbD0iIzZlNmU3MyIgZm9udC1zaXplPSIxMiI+VmFsdWU8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDA3YWZmIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+PGxpbmUgeDE9IjkwIiB5MT0iMTQwIiB4Mj0iOTAiIHkyPSIxMTIiLz48bGluZSB4MT0iMzMwIiB5MT0iMTQwIiB4Mj0iMzMwIiB5Mj0iMTEyIi8+PC9nPgogIDxnIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsIE1lbmxvLCBDb25zb2xhcywgbW9ub3NwYWNlIiBmb250LXNpemU9IjEzIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj48dGV4dCB4PSI5MCIgeT0iMTU2Ij5udW1iZXJzWzBdIGlzIDI8L3RleHQ+PHRleHQgeD0iMzMwIiB5PSIxNTYiPm51bWJlcnNbNF0gaXMgMTA8L3RleHQ+PC9nPgogIDx0ZXh0IHg9IjIwMCIgeT0iMTgyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj41IHNsb3RzLCBjb3VudGluZyBzdGFydHMgYXQgMDogdGhlIGxhc3QgaW5kZXggaXMgNC48L3RleHQ+Cjwvc3ZnPgo=",NN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMjkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPmNhbGN1bGF0b3IuY3BwPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iNTEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMzYTNhM2MiPnNvdXJjZSBjb2RlIGluIEMrKzwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSI2MCIgeDI9IjgwIiB5Mj0iOTQiLz4KICAgIDxsaW5lIHgxPSI4MCIgeTE9IjEyNiIgeDI9IjgwIiB5Mj0iMTU2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iNjAiIHgyPSIyMjAiIHkyPSI5NCIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjEyNiIgeDI9IjIyMCIgeTI9IjE1NiIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjYwIiB4Mj0iMzYwIiB5Mj0iOTQiLz4KICAgIDxsaW5lIHgxPSIzNjAiIHkxPSIxMjYiIHgyPSIzNjAiIHkyPSIxNTYiLz4KICA8L2c+CiAgPHJlY3QgeD0iMTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSI4MCIgeT0iMTE2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5jb21waWxlcjwvdGV4dD4KICA8cmVjdCB4PSIxOCIgeT0iMTU4IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjYjkxYzFjIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSI4MCIgeT0iMTgxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5tYWNoaW5lIGNvZGU8L3RleHQ+CiAgPHJlY3QgeD0iMTgiIHk9IjE5NCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iODAiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+V2luZG93czwvdGV4dD4KICA8cmVjdCB4PSIxNTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIyMjAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Y29tcGlsZXI8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxNTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZmVlMmUyIiBzdHJva2U9IiNiOTFjMWMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMTgxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5tYWNoaW5lIGNvZGU8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxOTQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5tYWNPUzwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIzNjAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Y29tcGlsZXI8L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIxNTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZmVlMmUyIiBzdHJva2U9IiNiOTFjMWMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMTgxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5tYWNoaW5lIGNvZGU8L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIxOTQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5MaW51eDwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjI1OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zdHlsZT0iaXRhbGljIj5Db21waWxlIGFnYWluPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMjc4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPmZvciBldmVyeSBzeXN0ZW08L3RleHQ+Cjwvc3ZnPgo=",FN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMzMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPlRpbWVzVGFibGUuamF2YTwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjUxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjM2EzYTNjIj5zb3VyY2UgY29kZSwgZm9yIHBlb3BsZTwvdGV4dD4KICA8cmVjdCB4PSIxMTAiIHk9IjEwNCIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNmZWYzYzciIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIxMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsIE1lbmxvLCBDb25zb2xhcywgbW9ub3NwYWNlIiBmb250LXdlaWdodD0iNjAwIj5UaW1lc1RhYmxlLmNsYXNzPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMTQzIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjM2EzYTNjIj5ieXRlY29kZTogQ0EgRkUgQkEgQkUg4oCmPC90ZXh0PgogIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjgiIG1hcmtlci1lbmQ9InVybCgjYSkiPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjYwIiB4Mj0iMjIwIiB5Mj0iMTAyIi8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iMTUyIiB4Mj0iODAiIHkyPSIxOTYiLz4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSIxNTIiIHgyPSIyMjAiIHkyPSIxOTYiLz4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSIxNTIiIHgyPSIzNjAiIHkyPSIxOTYiLz4KICA8L2c+CiAgPHRleHQgeD0iMjMwIiB5PSI4NiI+Y29tcGlsZXIgamF2YWM8L3RleHQ+CiAgPHJlY3QgeD0iMTgiIHk9IjE5OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iODAiIHk9IjIyMSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+SlZNPC90ZXh0PgogIDxyZWN0IHg9IjE4IiB5PSIyMzQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjgwIiB5PSIyNTciIHRleHQtYW5jaG9yPSJtaWRkbGUiPldpbmRvd3M8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxOTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiMxNTgwM2QiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjIxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5KVk08L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIyMzQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjU3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5tYWNPUzwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9IjE5OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzYwIiB5PSIyMjEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPkpWTTwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9IjIzNCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzYwIiB5PSIyNTciIHRleHQtYW5jaG9yPSJtaWRkbGUiPkxpbnV4PC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMjk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPldyaXRlIG9uY2UsPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMzE4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPnJ1biBhbnl3aGVyZTwvdGV4dD4KPC9zdmc+Cg==",RN=""+new URL("mvc-klick-BIA_bYzu.svg",import.meta.url).href,JN=""+new URL("ss-binaere-suche-D02W1TH_.svg",import.meta.url).href,GN=""+new URL("ss-bubblesort-CmKEoNfO.svg",import.meta.url).href,HN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDQgMTk2IiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KPHJlY3Qgd2lkdGg9IjQwNCIgaGVpZ2h0PSIxOTYiIGZpbGw9IiNmZmZmZmYiLz4KPHRleHQgeD0iMTQiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIiBmb250LXNpemU9IjE1Ij5Mb29raW5nIGZvciB0aGUgOS4gVGhlIGNhcmRzIGFyZSBtaXhlZCB1cC48L3RleHQ+CjxnPjxyZWN0IHg9IjE0IiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSIzNC4wIiB5PSI3NS4yIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjZGMyNjI2Ij43PC90ZXh0Pjx0ZXh0IHg9IjM0LjAiIHk9Ijk1LjkxOTk5OTk5OTk5OTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE1IiBmaWxsPSIjZGMyNjI2Ij7imaU8L3RleHQ+PC9nPgo8dGV4dCB4PSIzNC4wIiB5PSIxMzAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiNkYzI2MjYiPuKclzwvdGV4dD4KPHRleHQgeD0iMzQuMCIgeT0iMTQ4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj4xLjwvdGV4dD4KPGc+PHJlY3QgeD0iNjIiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHg9IjgyLjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPjM8L3RleHQ+PHRleHQgeD0iODIuMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiMxYzFjMWUiPuKZoDwvdGV4dD48L2c+Cjx0ZXh0IHg9IjgyLjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iI2RjMjYyNiI+4pyXPC90ZXh0Pgo8dGV4dCB4PSI4Mi4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjIuPC90ZXh0Pgo8Zz48cmVjdCB4PSIxMTAiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHg9IjEzMC4wIiB5PSI3NS4yIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjZGMyNjI2Ij4xMDwvdGV4dD48dGV4dCB4PSIxMzAuMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNkYzI2MjYiPuKZpjwvdGV4dD48L2c+Cjx0ZXh0IHg9IjEzMC4wIiB5PSIxMzAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiNkYzI2MjYiPuKclzwvdGV4dD4KPHRleHQgeD0iMTMwLjAiIHk9IjE0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+My48L3RleHQ+CjxnPjxyZWN0IHg9IjE1OCIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iMTc4LjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPjU8L3RleHQ+PHRleHQgeD0iMTc4LjAiIHk9Ijk1LjkxOTk5OTk5OTk5OTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE1IiBmaWxsPSIjMWMxYzFlIj7imaM8L3RleHQ+PC9nPgo8dGV4dCB4PSIxNzguMCIgeT0iMTMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjZGMyNjI2Ij7inJc8L3RleHQ+Cjx0ZXh0IHg9IjE3OC4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjQuPC90ZXh0Pgo8Zz48cmVjdCB4PSIyMDYiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiMxNTgwM2QiIHN0cm9rZS13aWR0aD0iMyIvPjx0ZXh0IHg9IjIyNi4wIiB5PSI3NS4yIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWMxYzFlIj45PC90ZXh0Pjx0ZXh0IHg9IjIyNi4wIiB5PSI5NS45MTk5OTk5OTk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzFjMWMxZSI+4pmgPC90ZXh0PjwvZz4KPHRleHQgeD0iMjI2LjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iIzE1ODAzZCI+4pyTPC90ZXh0Pgo8dGV4dCB4PSIyMjYuMCIgeT0iMTQ4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj41LjwvdGV4dD4KPGc+PHJlY3QgeD0iMjU0IiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iIzFkNGVkOCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48cmVjdCB4PSIyNTgiIHk9IjU0IiB3aWR0aD0iMzIiIGhlaWdodD0iNDgiIHJ4PSIzIiBmaWxsPSJub25lIiBzdHJva2U9IiM5M2M1ZmQiIHN0cm9rZS13aWR0aD0iMS4yIiBzdHJva2UtZGFzaGFycmF5PSIzIDIiLz48L2c+CjxnPjxyZWN0IHg9IjMwMiIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiMxZDRlZDgiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHJlY3QgeD0iMzA2IiB5PSI1NCIgd2lkdGg9IjMyIiBoZWlnaHQ9IjQ4IiByeD0iMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTNjNWZkIiBzdHJva2Utd2lkdGg9IjEuMiIgc3Ryb2tlLWRhc2hhcnJheT0iMyAyIi8+PC9nPgo8Zz48cmVjdCB4PSIzNTAiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjMWQ0ZWQ4IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjxyZWN0IHg9IjM1NCIgeT0iNTQiIHdpZHRoPSIzMiIgaGVpZ2h0PSI0OCIgcng9IjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzkzYzVmZCIgc3Ryb2tlLXdpZHRoPSIxLjIiIHN0cm9rZS1kYXNoYXJyYXk9IjMgMiIvPjwvZz4KPHRleHQgeD0iMTQiIHk9IjE2OCIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzZlNmU3MyI+VHVybiB0aGVtIG92ZXIgZnJvbSBsZWZ0IHRvIHJpZ2h0OjwvdGV4dD48dGV4dCB4PSIxNCIgeT0iMTg4IiBmb250LXNpemU9IjE1IiBmaWxsPSIjNmU2ZTczIj5BZnRlciA1IGNvbXBhcmlzb25zIHRoZSA5IGlzIGZvdW5kLjwvdGV4dD4KPC9zdmc+Cg==",VN=""+new URL("ss-vergleiche-UczNldcC.svg",import.meta.url).href,qN="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjAgNDUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KPGRlZnM+PG1hcmtlciBpZD0iayIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSI5IiByZWZZPSI1IiBtYXJrZXJXaWR0aD0iNiIgbWFya2VySGVpZ2h0PSI2IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0wIDAgTDEwIDUgTDAgMTAgWiIgZmlsbD0iIzFjMWMxZSIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9ImIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iOSIgcmVmWT0iNSIgbWFya2VyV2lkdGg9IjYiIG1hcmtlckhlaWdodD0iNiIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNMCAwIEwxMCA1IEwwIDEwIFoiIGZpbGw9IiMxZDRlZDgiLz48L21hcmtlcj48bWFya2VyIGlkPSJvIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI2IiBtYXJrZXJIZWlnaHQ9IjYiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjYjQ1MzA5Ii8+PC9tYXJrZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNDIwIiBoZWlnaHQ9IjQ1MCIgZmlsbD0iI2ZmZmZmZiIvPgo8dGV4dCB4PSIxMCIgeT0iMjIiIGZvbnQtc2l6ZT0iMTUiIGZvbnQtd2VpZ2h0PSI3MDAiPkNhbGwgYnkgdmFsdWU6IGEgY29weTwvdGV4dD4KPHJlY3QgeD0iMTAiIHk9IjM4IiB3aWR0aD0iMTYwIiBoZWlnaHQ9Ijg2IiByeD0iOCIgZmlsbD0iI2VmZjZmZiIgc3Ryb2tlPSIjMWQ0ZWQ4IiBzdHJva2Utd2lkdGg9IjEuNSIvPgo8dGV4dCB4PSIyMCIgeT0iNTgiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZDRlZDgiPk1haW4gcHJvZ3JhbTwvdGV4dD4KPHRleHQgeD0iMTYiIHk9Ijk4IiBmb250LXNpemU9IjEyIj5udW1iZXI8L3RleHQ+CjxyZWN0IHg9IjcwIiB5PSI3OCIgd2lkdGg9IjU2IiBoZWlnaHQ9IjMwIiByeD0iNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+Cjx0ZXh0IHg9Ijk4IiB5PSI5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+NTwvdGV4dD4KPHJlY3QgeD0iMjUwIiB5PSIzOCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSI4NiIgcng9IjgiIGZpbGw9IiNmZmY3ZWQiIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHRleHQgeD0iMjYwIiB5PSI1OCIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2I0NTMwOSI+ZG91YmxlSXQ8L3RleHQ+Cjx0ZXh0IHg9IjI2MCIgeT0iOTgiPng8L3RleHQ+CjxyZWN0IHg9IjI4MCIgeT0iNzgiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMCIgcng9IjQiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFjMWMxZSIvPgo8dGV4dCB4PSIzMjAiIHk9Ijk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj48dHNwYW4gZmlsbD0iIzZlNmU3MyIgdGV4dC1kZWNvcmF0aW9uPSJsaW5lLXRocm91Z2giPjU8L3RzcGFuPiA8dHNwYW4gZm9udC13ZWlnaHQ9IjcwMCI+MTA8L3RzcGFuPjwvdGV4dD4KPGxpbmUgeDE9IjEyOCIgeTE9IjkzIiB4Mj0iMjQ4IiB5Mj0iOTMiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1kYXNoYXJyYXk9IjUgNCIgbWFya2VyLWVuZD0idXJsKCNrKSIvPgo8dGV4dCB4PSIyMTAiIHk9Ijg0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5jb3B5PC90ZXh0Pgo8dGV4dCB4PSIxMCIgeT0iMTUwIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj54IGJlY29tZXMgMTAsIGJ1dCBudW1iZXIgc3RheXMgNS48L3RleHQ+Cjx0ZXh0IHg9IjEwIiB5PSIxNjgiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPkxpa2UgYSBwaG90b2NvcHk6IHdyaXRpbmcgb24gdGhlIGNvcHk8L3RleHQ+Cjx0ZXh0IHg9IjEwIiB5PSIxODYiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPmRvZXMgbm90IGNoYW5nZSB0aGUgb3JpZ2luYWwuPC90ZXh0Pgo8bGluZSB4MT0iMTAiIHkxPSIyMDgiIHgyPSI0MTAiIHkyPSIyMDgiIHN0cm9rZT0iI2QxZDFkNiIvPgo8dGV4dCB4PSIxMCIgeT0iMjM2IiBmb250LXNpemU9IjE1IiBmb250LXdlaWdodD0iNzAwIj5DYWxsIGJ5IHJlZmVyZW5jZTogdGhlIHNhbWUgYXJyYXk8L3RleHQ+CjxyZWN0IHg9IjEwIiB5PSIyNTIiIHdpZHRoPSIxNjAiIGhlaWdodD0iNjIiIHJ4PSI4IiBmaWxsPSIjZWZmNmZmIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjx0ZXh0IHg9IjIwIiB5PSIyNzIiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZDRlZDgiPk1haW4gcHJvZ3JhbTwvdGV4dD4KPHRleHQgeD0iMjAiIHk9IjMwMCI+cm93PC90ZXh0Pgo8Y2lyY2xlIGN4PSIxNTAiIGN5PSIyOTUiIHI9IjUiIGZpbGw9IiMxZDRlZDgiLz4KPHJlY3QgeD0iMjUwIiB5PSIyNTIiIHdpZHRoPSIxNjAiIGhlaWdodD0iNjIiIHJ4PSI4IiBmaWxsPSIjZmZmN2VkIiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjx0ZXh0IHg9IjI2MCIgeT0iMjcyIiBmb250LXNpemU9IjEzIiBmaWxsPSIjYjQ1MzA5Ij5jbGVhcjwvdGV4dD4KPHRleHQgeD0iMjYwIiB5PSIzMDAiPnNlYXRzPC90ZXh0Pgo8Y2lyY2xlIGN4PSIzOTAiIGN5PSIyOTUiIHI9IjUiIGZpbGw9IiNiNDUzMDkiLz4KPHBhdGggZD0iTTE1MCAzMDAgTDE1MCAzNDAgTDE0MCAzNTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjYikiLz4KPHBhdGggZD0iTTM5MCAzMDAgTDM5MCAzNDAgTDMyMiAzNTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjbykiLz4KPHJlY3QgeD0iOTAiIHk9IjM1OCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHJlY3QgeD0iMTUwIiB5PSIzNTgiIHdpZHRoPSI2MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+CjxyZWN0IHg9IjIxMCIgeT0iMzU4IiB3aWR0aD0iNjAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFjMWMxZSIvPgo8cmVjdCB4PSIyNzAiIHk9IjM1OCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHRleHQgeD0iMTIwIiB5PSIzNzkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiPmZhbHNlPC90ZXh0Pgo8dGV4dCB4PSIxODAiIHk9IjM3OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+ZmFsc2U8L3RleHQ+Cjx0ZXh0IHg9IjI0MCIgeT0iMzc5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5mYWxzZTwvdGV4dD4KPHRleHQgeD0iMzAwIiB5PSIzNzkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiPmZhbHNlPC90ZXh0Pgo8dGV4dCB4PSIxMCIgeT0iNDE2IiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj5UaGVyZSBpcyBvbmx5IG9uZSBhcnJheS4gV2hhdCBjbGVhciBjaGFuZ2VzIHRocm91Z2ggc2VhdHMsPC90ZXh0Pgo8dGV4dCB4PSIxMCIgeT0iNDM0IiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj50aGUgbWFpbiBwcm9ncmFtIHNlZXMgdW5kZXIgcm93LjwvdGV4dD4KPC9zdmc+Cg==",UN=""+new URL("zk-zeichen-CgUEhNoK.svg",import.meta.url).href,QN=""+new URL("grace-hopper-DltOT1I5.jpg",import.meta.url).href,$N=""+new URL("hedy-lamarr-MFZMAlPQ.jpg",import.meta.url).href,YN=""+new URL("ike-nassi-D2w47gvX.jpg",import.meta.url).href,KN=""+new URL("katherine-johnson-k17EjVTs.jpg",import.meta.url).href,XN=""+new URL("kino-saal-CYMOok4_.svg",import.meta.url).href,eF=""+new URL("kino-schleifen-DrTQV7dq.svg",import.meta.url).href,tF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDkyIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iNDkyIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiPgogICAgPCEtLSBTdGFydCAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMTAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMjgiIHJ4PSIxNCIgZmlsbD0iI2QxZjJkOSIvPgogICAgPGxpbmUgeDE9IjE1MCIgeTE9IjM4IiB4Mj0iMTUwIiB5Mj0iNTQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gQW53ZWlzdW5nIC0tPgogICAgPHJlY3QgeD0iNTAiIHk9IjU2IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iODYiIHgyPSIxNTAiIHkyPSI5OCIgbWFya2VyLWVuZD0idXJsKCNhKSIvPgogICAgPCEtLSBSYXV0ZSAxIC0tPgogICAgPHBhdGggZD0iTTE1MCAxMDAgTDIzNSAxMjggTDE1MCAxNTYgTDY1IDEyOCBaIiBmaWxsPSIjZmZmNGQ2Ii8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMTU2IiB4Mj0iMTUwIiB5Mj0iMTcyIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8cmVjdCB4PSI1MCIgeT0iMTc0IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMjA0IiB4Mj0iMTUwIiB5Mj0iMjUwIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIG5laW4tV2VnIHVtIGRlbiBLYXN0ZW4gaGVydW0gLS0+CiAgICA8cG9seWxpbmUgcG9pbnRzPSIyMzUsMTI4IDI4NSwxMjggMjg1LDIyMiAxNTAsMjIyIiBmaWxsPSJub25lIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIFJhdXRlIDIgLS0+CiAgICA8cGF0aCBkPSJNMTUwIDI1MiBMMjM1IDI4MCBMMTUwIDMwOCBMNjUgMjgwIFoiIGZpbGw9IiNmZmY0ZDYiLz4KICAgIDxsaW5lIHgxPSIyMzUiIHkxPSIyODAiIHgyPSIyNjgiIHkyPSIyODAiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjI3MCIgeT0iMjY1IiB3aWR0aD0iMTE1IiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8IS0tIHp1csO8Y2sgenVyIFByw7xmdW5nIC0tPgogICAgPHBvbHlsaW5lIHBvaW50cz0iMzI3LDI2NSAzMjcsMjM4IDE1MCwyMzgiIGZpbGw9Im5vbmUiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzMDgiIHgyPSIxNTAiIHkyPSIzMjYiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjUwIiB5PSIzMjgiIHdpZHRoPSIyMDAiIGhlaWdodD0iMzAiIGZpbGw9IiNlNWYwZmYiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzNTgiIHgyPSIxNTAiIHkyPSIzNzQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gRW5kZSAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMzc2IiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjI4IiByeD0iMTQiIGZpbGw9IiNkMWYyZDkiLz4KICA8L2c+CiAgPGcgdGV4dC1hbmNob3I9Im1pZGRsZSI+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjI5Ij5TdGFydDwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iNzYiPlRhc3NlIGF1ZiBkZW4gVGlzY2ggc3RlbGxlbjwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMTMzIj5adWNrZXIgZ2V3w7xuc2NodD88L3RleHQ+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjE5NCI+WnVja2VyIGhpbmVpbmdlYmVuPC90ZXh0PgogICAgPHRleHQgeD0iMTUwIiB5PSIyODUiPlRlZSBub2NoIHp1IGhlacOfPzwvdGV4dD4KICAgIDx0ZXh0IHg9IjMyNy41IiB5PSIyODUiPjEgTWludXRlIHdhcnRlbjwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMzQ4Ij5UZWUgdHJpbmtlbjwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMzk1Ij5FbmRlPC90ZXh0PgogIDwvZz4KICA8ZyBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0iNjAwIj4KICAgIDx0ZXh0IHg9IjE1NiIgeT0iMTY4IiBmaWxsPSIjMjQ4YTNkIj5qYTwvdGV4dD4KICAgIDx0ZXh0IHg9IjI0MiIgeT0iMTIyIiBmaWxsPSIjZDcwMDE1Ij5uZWluPC90ZXh0PgogICAgPHRleHQgeD0iMjQwIiB5PSIyNzQiIGZpbGw9IiMyNDhhM2QiPmphPC90ZXh0PgogICAgPHRleHQgeD0iMTU2IiB5PSIzMjAiIGZpbGw9IiNkNzAwMTUiPm5laW48L3RleHQ+CiAgPC9nPgogIDwhLS0gTGVnZW5kZSAtLT4KICA8bGluZSB4MT0iMTAiIHkxPSI0MTgiIHgyPSIzOTAiIHkyPSI0MTgiIHN0cm9rZT0iI2QxZDFkNiIvPgogIDxnIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjUiPgogICAgPHJlY3QgeD0iNDAiIHk9IjQzMCIgd2lkdGg9IjM2IiBoZWlnaHQ9IjE4IiByeD0iOSIgZmlsbD0iI2QxZjJkOSIvPgogICAgPHJlY3QgeD0iMjIwIiB5PSI0MzAiIHdpZHRoPSIzNiIgaGVpZ2h0PSIxOCIgZmlsbD0iI2U1ZjBmZiIvPgogICAgPHBhdGggZD0iTTU4IDQ1OCBMNzYgNDY5IEw1OCA0ODAgTDQwIDQ2OSBaIiBmaWxsPSIjZmZmNGQ2Ii8+CiAgICA8cGF0aCBkPSJNMjI2IDQ2MCBMMjU2IDQ2MCBMMjUwIDQ3OCBMMjIwIDQ3OCBaIiBmaWxsPSIjZjJlNmZmIi8+CiAgPC9nPgogIDxnIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiMzYTNhM2MiPgogICAgPHRleHQgeD0iODYiIHk9IjQ0MyI+U3RhcnQgb2RlciBFbmRlPC90ZXh0PgogICAgPHRleHQgeD0iMjY2IiB5PSI0NDMiPkFud2Vpc3VuZzwvdGV4dD4KICAgIDx0ZXh0IHg9Ijg2IiB5PSI0NzMiPlZlcnp3ZWlndW5nPC90ZXh0PgogICAgPHRleHQgeD0iMjY2IiB5PSI0NzMiPkVpbi0gb2RlciBBdXNnYWJlPC90ZXh0PgogIDwvZz4KPC9zdmc+Cg==",nF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE5MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwIiB5PSIyNCIgZm9udC13ZWlnaHQ9IjYwMCI+V29oaW4gbGFuZGV0IGRhcyBFbGVtZW50PzwvdGV4dD4KICA8cmVjdCB4PSIyMCIgeT0iNDAiIHdpZHRoPSIxOTAiIGhlaWdodD0iMzUiIGZpbGw9IiNkYmVhZmUiLz4KICA8cmVjdCB4PSIyMCIgeT0iNzUiIHdpZHRoPSIxOTAiIGhlaWdodD0iMzUiIGZpbGw9IiNkY2ZjZTciLz4KICA8bGluZSB4MT0iMjAiIHkxPSI3NSIgeDI9IjIxMCIgeTI9Ijc1IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS1kYXNoYXJyYXk9IjQgMyIvPgogIDxyZWN0IHg9IjIwIiB5PSI0MCIgd2lkdGg9IjE5MCIgaGVpZ2h0PSI3MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIxMTUiIHk9IjYyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMWQ0ZWQ4Ij5vYmVyZSBIw6RsZnRlPC90ZXh0PgogIDx0ZXh0IHg9IjExNSIgeT0iOTciIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPnVudGVyZSBIw6RsZnRlPC90ZXh0PgogIDx0ZXh0IHg9IjIyNCIgeT0iNjIiPuKGkiBrb21tdCA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+ZGF2b3I8L3RzcGFuPjwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9Ijk3Ij7ihpIga29tbXQgPHRzcGFuIGZvbnQtd2VpZ2h0PSI2MDAiPmRhaGludGVyPC90c3Bhbj48L3RleHQ+CiAgPHJlY3QgeD0iMjAiIHk9IjEzMiIgd2lkdGg9IjE5MCIgaGVpZ2h0PSI0MCIgZmlsbD0iI2ZlZjNjNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIxMTUiIHk9IjE1NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiI+w7g8L3RleHQ+CiAgPHRleHQgeD0iMjI0IiB5PSIxNTciPuKGkiA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+ZXJzZXR6dDwvdHNwYW4+IGRlbiBQbGF0emhhbHRlcjwvdGV4dD4KPC9zdmc+Cg==",iF=""+new URL("l03-lochkarte-beschriftet-7hmU_7ct.svg",import.meta.url).href,aF=""+new URL("l03-lochkarte-gzfFEatc.svg",import.meta.url).href,sF=""+new URL("l03-zuse-lochstreifen-B87kqGex.svg",import.meta.url).href,rF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NjAgMjkwIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSI+CjxyZWN0IHdpZHRoPSI0NjAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+Cjx0ZXh0IHg9IjUwIiB5PSI0NCIgZm9udC1zaXplPSI0MCIgZmlsbD0iIzZlNmU3MyIgdGV4dExlbmd0aD0iMzM2IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiPnByZWlzPHRzcGFuIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPk08L3RzcGFuPml0PHRzcGFuIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPlM8L3RzcGFuPnRldWVyPC90ZXh0Pgo8bGluZSB4MT0iMTgyIiB5MT0iNTQiIHgyPSIxODIiIHkyPSI4MiIgc3Ryb2tlPSIjMDA3YWZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWRhc2hhcnJheT0iNSA0Ii8+CjxsaW5lIHgxPSIyNTQiIHkxPSI1NCIgeDI9IjI1NCIgeTI9IjgyIiBzdHJva2U9IiMwMDdhZmYiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtZGFzaGFycmF5PSI1IDQiLz4KPHBhdGggZD0iTTIwIDI3MiBINDQwIiBzdHJva2U9IiNlNWQzYjAiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0xNTYgMTcyIEwxNTAgMjI2IiBzdHJva2U9IiNiODhhNTIiIHN0cm9rZS13aWR0aD0iMTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTUwIDIyNiBMMTU2IDI2MiIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIxNTAiIGN5PSIyMjYiIHI9IjYiIGZpbGw9IiM3YTU1MzAiIG9wYWNpdHk9IjAuNTUiLz4KPGVsbGlwc2UgY3g9IjE2MSIgY3k9IjI2NyIgcng9IjExIiByeT0iNSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMzA0IDE3MiBMMzA4IDIyNiIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTMwOCAyMjYgTDMwNCAyNjIiIHN0cm9rZT0iI2I4OGE1MiIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMzA4IiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIzMDkiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTEyMCAxNDAgQzEwNCAxNTAgMTA0IDE3NiAxMTAgMTk2IiBmaWxsPSJub25lIiBzdHJva2U9IiNiODhhNTIiIHN0cm9rZS13aWR0aD0iNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxlbGxpcHNlIGN4PSIxMTAiIGN5PSIyMDAiIHJ4PSI1IiByeT0iOSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMTIwIDE3MiBDMTA4IDE1MiAxMTQgMTI4IDEzNCAxMjIgQzE0OCAxMjAgMTU0IDg0IDE4MiA4NCBDMjEwIDg0IDIwOCAxMjYgMjE4IDEyNCBDMjI4IDEyNiAyMjYgODQgMjU0IDg0IEMyODIgODQgMjg0IDEyMiAyOTQgMTI4IEMzMTYgMTMyIDMyNCAxNTggMzQwIDE1OCBDMzUyIDE1OCAzNTQgMTI0IDM1OCAxMTAgQzM2MiA5OCAzNzYgOTYgMzg4IDk5IEM0MDAgMTAyIDQwNiAxMDggNDA0IDExNiBDNDAyIDEyMyAzOTAgMTI0IDM4MCAxMjIgQzM3MiAxMjIgMzcwIDEyOCAzNzAgMTM2IEMzNzAgMTYyIDM2NCAxODQgMzQyIDE4NiBDMzI2IDE4OCAzMTYgMTgyIDMwNiAxNzggQzI0OCAxODggMTg4IDE4OCAxNDAgMTgyIEMxMzAgMTgwIDEyNCAxNzcgMTIwIDE3MiBaIiBmaWxsPSIjZDJhNTZhIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMzQgMTcyIEwxMjggMjI2IiBzdHJva2U9IiNkMmE1NmEiIHN0cm9rZS13aWR0aD0iMTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTI4IDIyNiBMMTM0IDI2MiIgc3Ryb2tlPSIjZDJhNTZhIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIxMjgiIGN5PSIyMjYiIHI9IjYiIGZpbGw9IiM3YTU1MzAiIG9wYWNpdHk9IjAuNTUiLz4KPGVsbGlwc2UgY3g9IjEzOSIgY3k9IjI2NyIgcng9IjExIiByeT0iNSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMjg0IDE3MiBMMjg4IDIyNiIgc3Ryb2tlPSIjZDJhNTZhIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTI4OCAyMjYgTDI4NCAyNjIiIHN0cm9rZT0iI2QyYTU2YSIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMjg4IiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIyODkiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTM2NiAxMDMgTDM2MiA5MiBMMzc0IDEwMCBaIiBmaWxsPSIjZDJhNTZhIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIzODIiIGN5PSIxMDciIHI9IjMuMiIgZmlsbD0iIzFjMWMxZSIvPgo8cGF0aCBkPSJNMzc3IDEwNCBRMzgyIDEwMSAzODcgMTA0IiBmaWxsPSJub25lIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zOTggMTA3IHE0IDEgNSA0IiBmaWxsPSJub25lIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zOTIgMTE5IFEzOTggMTE4IDQwMyAxMTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzdhNTUzMCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+Cg==",oF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjEwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjIxMCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsIE1lbmxvLCBDb25zb2xhcywgbW9ub3NwYWNlIiBmb250LXNpemU9IjE2Ij5pbnQgYWx0ZXIgPSAxNDwvdGV4dD4KICA8cGF0aCBkPSJNMTMwIDcwIEwxNzAgNTAgTDMwMCA1MCBMMjYwIDcwIFoiIGZpbGw9IiNjOThhM2MiIHN0cm9rZT0iIzhhNWExZiIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHJlY3QgeD0iMTgwIiB5PSIzNCIgd2lkdGg9IjQ0IiBoZWlnaHQ9IjUwIiByeD0iMyIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjEuNSIvPgogIDx0ZXh0IHg9IjIwMiIgeT0iNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMjAiIGZvbnQtd2VpZ2h0PSI3MDAiPjE0PC90ZXh0PgogIDxyZWN0IHg9IjEzMCIgeT0iNzAiIHdpZHRoPSIxMzAiIGhlaWdodD0iMTAwIiBmaWxsPSIjZTliODcyIiBzdHJva2U9IiM4YTVhMWYiIHN0cm9rZS13aWR0aD0iMiIvPgogIDxwYXRoIGQ9Ik0yNjAgNzAgTDMwMCA1MCBMMzAwIDE1MCBMMjYwIDE3MCBaIiBmaWxsPSIjZDQ5YTRjIiBzdHJva2U9IiM4YTVhMWYiIHN0cm9rZS13aWR0aD0iMiIvPgogIDxyZWN0IHg9IjE1MCIgeT0iMTAwIiB3aWR0aD0iOTAiIGhlaWdodD0iMzAiIHJ4PSI0IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM4YTVhMWYiLz4KICA8dGV4dCB4PSIxOTUiIHk9IjEyMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+YWx0ZXI8L3RleHQ+CiAgPHJlY3QgeD0iMTM4IiB5PSIxNDIiIHdpZHRoPSIzNCIgaGVpZ2h0PSIyMCIgcng9IjEwIiBmaWxsPSIjMDA3YWZmIi8+CiAgPHRleHQgeD0iMTU1IiB5PSIxNTYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiNmZmZmZmYiIGZvbnQtd2VpZ2h0PSI2MDAiPmludDwvdGV4dD4KICA8ZyBzdHJva2U9IiM2ZTZlNzMiPgogICAgPGxpbmUgeDE9IjMxMCIgeTE9IjQ4IiB4Mj0iMjI2IiB5Mj0iNTIiLz4KICAgIDxsaW5lIHgxPSIzMTAiIHkxPSIxMTUiIHgyPSIyNDIiIHkyPSIxMTUiLz4KICAgIDxsaW5lIHgxPSI4MCIgeTE9IjE1MiIgeDI9IjEzNCIgeTI9IjE1MiIvPgogIDwvZz4KICA8dGV4dCB4PSIzMTYiIHk9IjUyIj5XZXJ0PC90ZXh0PgogIDx0ZXh0IHg9IjMxNiIgeT0iMTE5Ij5OYW1lPC90ZXh0PgogIDx0ZXh0IHg9IjgiIHk9IjE1NiI+RGF0ZW50eXA8L3RleHQ+CiAgPHRleHQgeD0iMjAwIiB5PSIxOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPsOcYmVyIGRlbiBOYW1lbiBrb21tc3QgZHUgYW4gZGVuIFdlcnQuPC90ZXh0Pgo8L3N2Zz4K",lF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjAwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjAiPgogICAgPHRleHQgeD0iNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+cHVua3RlPC90ZXh0PgogICAgPHRleHQgeD0iMTI1IiB5PSIxMTAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPj08L3RleHQ+CiAgICA8dGV4dCB4PSIyNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+cHVua3RlICsgNTwvdGV4dD4KICA8L2c+CiAgPHJlY3QgeD0iMTgwIiB5PSI4NiIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjYwIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPuKRoCByZWNodHMgYXVzcmVjaG5lbjogMTAgKyA1ID0gMTU8L3RleHQ+CiAgPHBhdGggZD0iTTI1MCA4MiBDMjMwIDMwLCA5MCAzMCwgNjQgODQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwN2FmZiIgc3Ryb2tlLXdpZHRoPSIyLjUiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICA8dGV4dCB4PSIxNjAiIHk9IjM2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDA3YWZmIj7ikaEgRXJnZWJuaXMgbGlua3Mgc3BlaWNoZXJuPC90ZXh0PgogIDxyZWN0IHg9IjIwIiB5PSIxNjQiIHdpZHRoPSIzNjAiIGhlaWdodD0iMjYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3Ii8+CiAgPHRleHQgeD0iMjAwIiB5PSIxODIiIHRleHQtYW5jaG9yPSJtaWRkbGUiPnB1bmt0ZSB2b3JoZXI6IDx0c3BhbiBmb250LXdlaWdodD0iNzAwIj4xMDwvdHNwYW4+ICAg4oaSICAgcHVua3RlIG5hY2hoZXI6IDx0c3BhbiBmb250LXdlaWdodD0iNzAwIj4xNTwvdHNwYW4+PC90ZXh0Pgo8L3N2Zz4K",uF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjYwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjYwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiPgogICAgPHBhdGggZD0iTTQwIDExMCBMNDggMTgwIEwxMDIgMTgwIEwxMTAgMTEwIi8+CiAgICA8cGF0aCBkPSJNMTY1IDExMCBMMTczIDE4MCBMMjI3IDE4MCBMMjM1IDExMCIvPgogICAgPHBhdGggZD0iTTI5MCAxMTAgTDI5OCAxODAgTDM1MiAxODAgTDM2MCAxMTAiLz4KICA8L2c+CiAgPHBhdGggZD0iTTQ0IDEzNSBMNDggMTgwIEwxMDIgMTgwIEwxMDYgMTM1IFoiIGZpbGw9IiNmZGJhNzQiLz4KICA8cGF0aCBkPSJNMjk0IDEzNSBMMjk4IDE4MCBMMzUyIDE4MCBMMzU2IDEzNSBaIiBmaWxsPSIjOTNjNWZkIi8+CiAgPHRleHQgeD0iNzUiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+bGlua3M8L3RleHQ+CiAgPHRleHQgeD0iNzUiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+U2FmdDwvdGV4dD4KICA8dGV4dCB4PSIyMDAiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+aGlsZjwvdGV4dD4KICA8dGV4dCB4PSIyMDAiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+bGVlcjwvdGV4dD4KICA8dGV4dCB4PSIzMjUiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+cmVjaHRzPC90ZXh0PgogIDx0ZXh0IHg9IjMyNSIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5XYXNzZXI8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8cGF0aCBkPSJNODggMTA0IEMxMTAgNzAsIDE2MCA3MCwgMTg2IDEwNCIvPgogICAgPHBhdGggZD0iTTMxOCAxMDQgQzI3MCAxNSwgMTMwIDE1LCA4MiAxMDQiLz4KICAgIDxwYXRoIGQ9Ik0yMTQgMTA0IEMyNDAgNzAsIDI5MCA3MCwgMzEyIDEwNCIvPgogIDwvZz4KICA8ZyBmb250LXdlaWdodD0iNzAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4KICAgIDxjaXJjbGUgY3g9IjEzNyIgY3k9IjgwIiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMTM3IiB5PSI4NSI+MTwvdGV4dD4KICAgIDxjaXJjbGUgY3g9IjIwMCIgY3k9IjM4IiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMjAwIiB5PSI0MyI+MjwvdGV4dD4KICAgIDxjaXJjbGUgY3g9IjI2MyIgY3k9IjgwIiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMjYzIiB5PSI4NSI+MzwvdGV4dD4KICA8L2c+CiAgPGcgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIiPgogICAgPHRleHQgeD0iNzAiIHk9IjI0NyI+MTogaGlsZiA9IGxpbmtzPC90ZXh0PgogICAgPHRleHQgeD0iMjAwIiB5PSIyNDciPjI6IGxpbmtzID0gcmVjaHRzPC90ZXh0PgogICAgPHRleHQgeD0iMzMwIiB5PSIyNDciPjM6IHJlY2h0cyA9IGhpbGY8L3RleHQ+CiAgPC9nPgo8L3N2Zz4K",mF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzAwIj4KPHJlY3Qgd2lkdGg9IjQ0MCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiNmZmZmZmYiLz4KPGcgdHJhbnNmb3JtPSJyb3RhdGUoLTMgMjAwIDE1MCkiPgo8cmVjdCB4PSI0NCIgeT0iMjQiIHdpZHRoPSIyOTAiIGhlaWdodD0iMjU2IiBmaWxsPSIjMDAwIiBvcGFjaXR5PSIwLjEwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1IDYpIi8+CjxyZWN0IHg9IjQ0IiB5PSIyNCIgd2lkdGg9IjI5MCIgaGVpZ2h0PSIyNTYiIGZpbGw9IiNmZmZkZjYiIHN0cm9rZT0iI2M5YzJhZCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iNTgiIHgyPSIzMzQiIHkyPSI1OCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iODAiIHgyPSIzMzQiIHkyPSI4MCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTAyIiB4Mj0iMzM0IiB5Mj0iMTAyIiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxMjQiIHgyPSIzMzQiIHkyPSIxMjQiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjE0NiIgeDI9IjMzNCIgeTI9IjE0NiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTY4IiB4Mj0iMzM0IiB5Mj0iMTY4IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxOTAiIHgyPSIzMzQiIHkyPSIxOTAiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjIxMiIgeDI9IjMzNCIgeTI9IjIxMiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMjM0IiB4Mj0iMzM0IiB5Mj0iMjM0IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIyNTYiIHgyPSIzMzQiIHkyPSIyNTYiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI3OCIgeTE9IjI0IiB4Mj0iNzgiIHkyPSIyODAiIHN0cm9rZT0iI2U4YTBhMCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGcgZm9udC1mYW1pbHk9IidTZWdvZSBQcmludCcsICdCcmFkbGV5IEhhbmQnLCAnQ2hhbGtib2FyZCBTRScsICdDb21pYyBTYW5zIE1TJywgY3Vyc2l2ZSIgZmlsbD0iIzFmM2E4YSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+Cjx0ZXh0IHg9IjEzMCIgeT0iNzUiIGZvbnQtc2l6ZT0iMTkiIGZvbnQtd2VpZ2h0PSJib2xkIj5hPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9Ijc1IiBmb250LXNpemU9IjE5IiBmb250LXdlaWdodD0iYm9sZCI+YjwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSI3NSIgZm9udC1zaXplPSIxOSIgZm9udC13ZWlnaHQ9ImJvbGQiPmhpbGY8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iOTciIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjE5NSIgeT0iOTciIGZvbnQtc2l6ZT0iMTgiPuKAkzwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSI5NyIgZm9udC1zaXplPSIxOCI+4oCTPC90ZXh0Pgo8dGV4dCB4PSIxMzAiIHk9IjExOSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMTk1IiB5PSIxMTkiIGZvbnQtc2l6ZT0iMTgiPjU8L3RleHQ+Cjx0ZXh0IHg9IjI2MiIgeT0iMTE5IiBmb250LXNpemU9IjE4Ij7igJM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTQxIiBmb250LXNpemU9IjE4Ij4zPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE0MSIgZm9udC1zaXplPSIxOCI+NTwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxNDEiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTYzIiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE2MyIgZm9udC1zaXplPSIxOCI+NTwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxNjMiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTg1IiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE4NSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxODUiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+CjwvZz4KPGcgc3Ryb2tlPSIjMWYzYThhIiBzdHJva2Utd2lkdGg9IjEuNiIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KPHBhdGggZD0iTTEwMCA4MyBRMTkwIDgxIDMwMCA4NCIvPgo8cGF0aCBkPSJNMTYyIDU2IFExNjAgMTUwIDE2MyAxOTQiLz4KPHBhdGggZD0iTTIyOCA1NiBRMjMwIDE1MCAyMjcgMTk0Ii8+CjwvZz4KPC9nPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMDAgMjMyKSByb3RhdGUoLTM4KSI+CjxyZWN0IHg9IjAiIHk9Ii05IiB3aWR0aD0iMTIwIiBoZWlnaHQ9IjE4IiBmaWxsPSIjZjVjMDAwIiBzdHJva2U9IiM4YTZkMDAiIHN0cm9rZS13aWR0aD0iMS4yIi8+CjxsaW5lIHgxPSIwIiB5MT0iLTMiIHgyPSIxMjAiIHkyPSItMyIgc3Ryb2tlPSIjZDlhODAwIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjAiIHkxPSIzIiB4Mj0iMTIwIiB5Mj0iMyIgc3Ryb2tlPSIjZDlhODAwIiBzdHJva2Utd2lkdGg9IjEiLz4KPHJlY3QgeD0iMTIwIiB5PSItOSIgd2lkdGg9IjEyIiBoZWlnaHQ9IjE4IiBmaWxsPSIjYjhiOGI4IiBzdHJva2U9IiM3NzciIHN0cm9rZS13aWR0aD0iMS4yIi8+CjxyZWN0IHg9IjEzMiIgeT0iLTkiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxOCIgcng9IjUiIGZpbGw9IiNmMDhhOWEiIHN0cm9rZT0iI2EyNGE1OCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPHBhdGggZD0iTTAgLTkgTC0yNiAwIEwwIDkgWiIgZmlsbD0iI2U5YzlhMCIgc3Ryb2tlPSIjOGE2ZDRhIiBzdHJva2Utd2lkdGg9IjEuMiIvPgo8cGF0aCBkPSJNLTE3IC0zIEwtMjYgMCBMLTE3IDMgWiIgZmlsbD0iIzMzMyIvPgo8L2c+Cjwvc3ZnPgo=",cF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzAwIj4KPHJlY3Qgd2lkdGg9IjQ0MCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiNmZmZmZmYiLz4KPGcgdHJhbnNmb3JtPSJyb3RhdGUoLTMgMjAwIDE1MCkiPgo8cmVjdCB4PSI0NCIgeT0iMjQiIHdpZHRoPSIyOTAiIGhlaWdodD0iMjU2IiBmaWxsPSIjMDAwIiBvcGFjaXR5PSIwLjEwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1IDYpIi8+CjxyZWN0IHg9IjQ0IiB5PSIyNCIgd2lkdGg9IjI5MCIgaGVpZ2h0PSIyNTYiIGZpbGw9IiNmZmZkZjYiIHN0cm9rZT0iI2M5YzJhZCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iNTgiIHgyPSIzMzQiIHkyPSI1OCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iODAiIHgyPSIzMzQiIHkyPSI4MCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTAyIiB4Mj0iMzM0IiB5Mj0iMTAyIiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxMjQiIHgyPSIzMzQiIHkyPSIxMjQiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjE0NiIgeDI9IjMzNCIgeTI9IjE0NiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTY4IiB4Mj0iMzM0IiB5Mj0iMTY4IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxOTAiIHgyPSIzMzQiIHkyPSIxOTAiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjIxMiIgeDI9IjMzNCIgeTI9IjIxMiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMjM0IiB4Mj0iMzM0IiB5Mj0iMjM0IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIyNTYiIHgyPSIzMzQiIHkyPSIyNTYiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI3OCIgeTE9IjI0IiB4Mj0iNzgiIHkyPSIyODAiIHN0cm9rZT0iI2U4YTBhMCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGcgZm9udC1mYW1pbHk9IidTZWdvZSBQcmludCcsICdCcmFkbGV5IEhhbmQnLCAnQ2hhbGtib2FyZCBTRScsICdDb21pYyBTYW5zIE1TJywgY3Vyc2l2ZSIgZmlsbD0iIzFmM2E4YSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+Cjx0ZXh0IHg9IjEzMCIgeT0iNzUiIGZvbnQtc2l6ZT0iMTkiIGZvbnQtd2VpZ2h0PSJib2xkIj5hPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9Ijc1IiBmb250LXNpemU9IjE5IiBmb250LXdlaWdodD0iYm9sZCI+YjwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSI3NSIgZm9udC1zaXplPSIxOSIgZm9udC13ZWlnaHQ9ImJvbGQiPnRlbXA8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iOTciIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjE5NSIgeT0iOTciIGZvbnQtc2l6ZT0iMTgiPuKAkzwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSI5NyIgZm9udC1zaXplPSIxOCI+4oCTPC90ZXh0Pgo8dGV4dCB4PSIxMzAiIHk9IjExOSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMTk1IiB5PSIxMTkiIGZvbnQtc2l6ZT0iMTgiPjU8L3RleHQ+Cjx0ZXh0IHg9IjI2MiIgeT0iMTE5IiBmb250LXNpemU9IjE4Ij7igJM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTQxIiBmb250LXNpemU9IjE4Ij4zPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE0MSIgZm9udC1zaXplPSIxOCI+NTwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxNDEiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTYzIiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE2MyIgZm9udC1zaXplPSIxOCI+NTwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxNjMiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTg1IiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjE4NSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxODUiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+CjwvZz4KPGcgc3Ryb2tlPSIjMWYzYThhIiBzdHJva2Utd2lkdGg9IjEuNiIgZmlsbD0ibm9uZSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIj4KPHBhdGggZD0iTTEwMCA4MyBRMTkwIDgxIDMwMCA4NCIvPgo8cGF0aCBkPSJNMTYyIDU2IFExNjAgMTUwIDE2MyAxOTQiLz4KPHBhdGggZD0iTTIyOCA1NiBRMjMwIDE1MCAyMjcgMTk0Ii8+CjwvZz4KPC9nPgo8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgzMDAgMjMyKSByb3RhdGUoLTM4KSI+CjxyZWN0IHg9IjAiIHk9Ii05IiB3aWR0aD0iMTIwIiBoZWlnaHQ9IjE4IiBmaWxsPSIjZjVjMDAwIiBzdHJva2U9IiM4YTZkMDAiIHN0cm9rZS13aWR0aD0iMS4yIi8+CjxsaW5lIHgxPSIwIiB5MT0iLTMiIHgyPSIxMjAiIHkyPSItMyIgc3Ryb2tlPSIjZDlhODAwIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjAiIHkxPSIzIiB4Mj0iMTIwIiB5Mj0iMyIgc3Ryb2tlPSIjZDlhODAwIiBzdHJva2Utd2lkdGg9IjEiLz4KPHJlY3QgeD0iMTIwIiB5PSItOSIgd2lkdGg9IjEyIiBoZWlnaHQ9IjE4IiBmaWxsPSIjYjhiOGI4IiBzdHJva2U9IiM3NzciIHN0cm9rZS13aWR0aD0iMS4yIi8+CjxyZWN0IHg9IjEzMiIgeT0iLTkiIHdpZHRoPSIxNiIgaGVpZ2h0PSIxOCIgcng9IjUiIGZpbGw9IiNmMDhhOWEiIHN0cm9rZT0iI2EyNGE1OCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPHBhdGggZD0iTTAgLTkgTC0yNiAwIEwwIDkgWiIgZmlsbD0iI2U5YzlhMCIgc3Ryb2tlPSIjOGE2ZDRhIiBzdHJva2Utd2lkdGg9IjEuMiIvPgo8cGF0aCBkPSJNLTE3IC0zIEwtMjYgMCBMLTE3IDMgWiIgZmlsbD0iIzMzMyIvPgo8L2c+Cjwvc3ZnPgo=",dF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzAwIj4KPHJlY3Qgd2lkdGg9IjQ0MCIgaGVpZ2h0PSIzMDAiIGZpbGw9IiNmZmZmZmYiLz4KPGcgdHJhbnNmb3JtPSJyb3RhdGUoLTMgMjAwIDE1MCkiPgo8cmVjdCB4PSI0NCIgeT0iMjQiIHdpZHRoPSIyOTAiIGhlaWdodD0iMjU2IiBmaWxsPSIjMDAwIiBvcGFjaXR5PSIwLjEwIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1IDYpIi8+CjxyZWN0IHg9IjQ0IiB5PSIyNCIgd2lkdGg9IjI5MCIgaGVpZ2h0PSIyNTYiIGZpbGw9IiNmZmZkZjYiIHN0cm9rZT0iI2M5YzJhZCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iNTgiIHgyPSIzMzQiIHkyPSI1OCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iODAiIHgyPSIzMzQiIHkyPSI4MCIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTAyIiB4Mj0iMzM0IiB5Mj0iMTAyIiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxMjQiIHgyPSIzMzQiIHkyPSIxMjQiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjE0NiIgeDI9IjMzNCIgeTI9IjE0NiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMTY4IiB4Mj0iMzM0IiB5Mj0iMTY4IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIxOTAiIHgyPSIzMzQiIHkyPSIxOTAiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI0NCIgeTE9IjIxMiIgeDI9IjMzNCIgeTI9IjIxMiIgc3Ryb2tlPSIjYmNkNGVlIiBzdHJva2Utd2lkdGg9IjEiLz4KPGxpbmUgeDE9IjQ0IiB5MT0iMjM0IiB4Mj0iMzM0IiB5Mj0iMjM0IiBzdHJva2U9IiNiY2Q0ZWUiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iNDQiIHkxPSIyNTYiIHgyPSIzMzQiIHkyPSIyNTYiIHN0cm9rZT0iI2JjZDRlZSIgc3Ryb2tlLXdpZHRoPSIxIi8+CjxsaW5lIHgxPSI3OCIgeTE9IjI0IiB4Mj0iNzgiIHkyPSIyODAiIHN0cm9rZT0iI2U4YTBhMCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGcgZm9udC1mYW1pbHk9IidTZWdvZSBQcmludCcsICdCcmFkbGV5IEhhbmQnLCAnQ2hhbGtib2FyZCBTRScsICdDb21pYyBTYW5zIE1TJywgY3Vyc2l2ZSIgZmlsbD0iIzFmM2E4YSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+Cjx0ZXh0IHg9IjEzMCIgeT0iNzUiIGZvbnQtc2l6ZT0iMTkiIGZvbnQtd2VpZ2h0PSJib2xkIj5hPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9Ijc1IiBmb250LXNpemU9IjE5IiBmb250LXdlaWdodD0iYm9sZCI+YjwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSI3NSIgZm9udC1zaXplPSIxOSIgZm9udC13ZWlnaHQ9ImJvbGQiPmF1eDwvdGV4dD4KPHRleHQgeD0iMTMwIiB5PSI5NyIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMTk1IiB5PSI5NyIgZm9udC1zaXplPSIxOCI+4oCTPC90ZXh0Pgo8dGV4dCB4PSIyNjIiIHk9Ijk3IiBmb250LXNpemU9IjE4Ij7igJM8L3RleHQ+Cjx0ZXh0IHg9IjEzMCIgeT0iMTE5IiBmb250LXNpemU9IjE4Ij4zPC90ZXh0Pgo8dGV4dCB4PSIxOTUiIHk9IjExOSIgZm9udC1zaXplPSIxOCI+NTwvdGV4dD4KPHRleHQgeD0iMjYyIiB5PSIxMTkiIGZvbnQtc2l6ZT0iMTgiPuKAkzwvdGV4dD4KPHRleHQgeD0iMTMwIiB5PSIxNDEiIGZvbnQtc2l6ZT0iMTgiPjM8L3RleHQ+Cjx0ZXh0IHg9IjE5NSIgeT0iMTQxIiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIyNjIiIHk9IjE0MSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMTMwIiB5PSIxNjMiIGZvbnQtc2l6ZT0iMTgiPjU8L3RleHQ+Cjx0ZXh0IHg9IjE5NSIgeT0iMTYzIiBmb250LXNpemU9IjE4Ij41PC90ZXh0Pgo8dGV4dCB4PSIyNjIiIHk9IjE2MyIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPHRleHQgeD0iMTMwIiB5PSIxODUiIGZvbnQtc2l6ZT0iMTgiPjU8L3RleHQ+Cjx0ZXh0IHg9IjE5NSIgeT0iMTg1IiBmb250LXNpemU9IjE4Ij4zPC90ZXh0Pgo8dGV4dCB4PSIyNjIiIHk9IjE4NSIgZm9udC1zaXplPSIxOCI+MzwvdGV4dD4KPC9nPgo8ZyBzdHJva2U9IiMxZjNhOGEiIHN0cm9rZS13aWR0aD0iMS42IiBmaWxsPSJub25lIiBzdHJva2UtbGluZWNhcD0icm91bmQiPgo8cGF0aCBkPSJNMTAwIDgzIFExOTAgODEgMzAwIDg0Ii8+CjxwYXRoIGQ9Ik0xNjIgNTYgUTE2MCAxNTAgMTYzIDE5NCIvPgo8cGF0aCBkPSJNMjI4IDU2IFEyMzAgMTUwIDIyNyAxOTQiLz4KPC9nPgo8L2c+CjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDMwMCAyMzIpIHJvdGF0ZSgtMzgpIj4KPHJlY3QgeD0iMCIgeT0iLTkiIHdpZHRoPSIxMjAiIGhlaWdodD0iMTgiIGZpbGw9IiNmNWMwMDAiIHN0cm9rZT0iIzhhNmQwMCIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPGxpbmUgeDE9IjAiIHkxPSItMyIgeDI9IjEyMCIgeTI9Ii0zIiBzdHJva2U9IiNkOWE4MDAiIHN0cm9rZS13aWR0aD0iMSIvPgo8bGluZSB4MT0iMCIgeTE9IjMiIHgyPSIxMjAiIHkyPSIzIiBzdHJva2U9IiNkOWE4MDAiIHN0cm9rZS13aWR0aD0iMSIvPgo8cmVjdCB4PSIxMjAiIHk9Ii05IiB3aWR0aD0iMTIiIGhlaWdodD0iMTgiIGZpbGw9IiNiOGI4YjgiIHN0cm9rZT0iIzc3NyIgc3Ryb2tlLXdpZHRoPSIxLjIiLz4KPHJlY3QgeD0iMTMyIiB5PSItOSIgd2lkdGg9IjE2IiBoZWlnaHQ9IjE4IiByeD0iNSIgZmlsbD0iI2YwOGE5YSIgc3Ryb2tlPSIjYTI0YTU4IiBzdHJva2Utd2lkdGg9IjEuMiIvPgo8cGF0aCBkPSJNMCAtOSBMLTI2IDAgTDAgOSBaIiBmaWxsPSIjZTljOWEwIiBzdHJva2U9IiM4YTZkNGEiIHN0cm9rZS13aWR0aD0iMS4yIi8+CjxwYXRoIGQ9Ik0tMTcgLTMgTC0yNiAwIEwtMTcgMyBaIiBmaWxsPSIjMzMzIi8+CjwvZz4KPC9zdmc+Cg==",hF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE1MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDxyZWN0IHg9IjIwIiB5PSI1MCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+PHJlY3QgeD0iMzEwIiB5PSI1MCIgd2lkdGg9IjcwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+CiAgPHJlY3QgeD0iOTAiIHk9IjUwIiB3aWR0aD0iMjIwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZGNmY2U3Ii8+CiAgPGxpbmUgeDE9IjIwIiB5MT0iNzQiIHgyPSIzODAiIHkyPSI3NCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8bGluZSB4MT0iNDYiIHkxPSI2OCIgeDI9IjQ2IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iNDYiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4wPC90ZXh0PgogIDxsaW5lIHgxPSI5MCIgeTE9IjY4IiB4Mj0iOTAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSI5MCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjE8L3RleHQ+CiAgPGxpbmUgeDE9IjEzNCIgeTE9IjY4IiB4Mj0iMTM0IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMTM0IiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MjwvdGV4dD4KICA8bGluZSB4MT0iMTc4IiB5MT0iNjgiIHgyPSIxNzgiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIxNzgiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4zPC90ZXh0PgogIDxsaW5lIHgxPSIyMjIiIHkxPSI2OCIgeDI9IjIyMiIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjIyMiIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjQ8L3RleHQ+CiAgPGxpbmUgeDE9IjI2NiIgeTE9IjY4IiB4Mj0iMjY2IiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMjY2IiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+NTwvdGV4dD4KICA8bGluZSB4MT0iMzEwIiB5MT0iNjgiIHgyPSIzMTAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIzMTAiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj42PC90ZXh0PgogIDxsaW5lIHgxPSIzNTQiIHkxPSI2OCIgeDI9IjM1NCIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjM1NCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjc8L3RleHQ+CiAgPGNpcmNsZSBjeD0iOTAiIGN5PSI3NCIgcj0iNiIgZmlsbD0iIzE1ODAzZCIvPjxjaXJjbGUgY3g9IjMxMCIgY3k9Ijc0IiByPSI2IiBmaWxsPSIjMTU4MDNkIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI0MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+Z8O8bHRpZzwvdGV4dD4KICA8dGV4dCB4PSI0MCIgeT0iNDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNiOTFjMWMiIGZvbnQtd2VpZ2h0PSI2MDAiPnVuZ8O8bHRpZzwvdGV4dD48dGV4dCB4PSIzNjAiIHk9IjQwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj51bmfDvGx0aWc8L3RleHQ+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPgogICAgPHRleHQgeD0iMjAwIiB5PSIxMjQiIGZpbGw9IiMxNTgwM2QiPm5vdGUgJmd0Oz0gMSAmYW1wOyZhbXA7IG5vdGUgJmx0Oz0gNjwvdGV4dD4KICAgIDx0ZXh0IHg9IjIwMCIgeT0iMTQyIiBmaWxsPSIjYjkxYzFjIj5ub3RlICZsdDsgMSB8fCBub3RlICZndDsgNjwvdGV4dD4KICA8L2c+Cjwvc3ZnPgo=",gF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMTkwIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzFjMWMxZSI+CiAgPHJlY3Qgd2lkdGg9IjQ0MCIgaGVpZ2h0PSIxOTAiIGZpbGw9IiNmZmZmZmYiLz4KICA8cmVjdCB4PSIyMCIgeT0iMTQiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iNjAuMCIgeT0iMzUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI3MDAiPmE8L3RleHQ+CiAgPHJlY3QgeD0iMTAwIiB5PSIxNCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIxNDAuMCIgeT0iMzUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI3MDAiPmI8L3RleHQ+CiAgPHJlY3QgeD0iMTgwIiB5PSIxNCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIyMjAuMCIgeT0iMzUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI3MDAiPmEgJmFtcDsmYW1wOyBiPC90ZXh0PgogIDxyZWN0IHg9IjI2MCIgeT0iMTQiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMzAwLjAiIHk9IjM1IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNzAwIj5hIHx8IGI8L3RleHQ+CiAgPHJlY3QgeD0iMzQwIiB5PSIxNCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIzODAuMCIgeT0iMzUiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI3MDAiPiFhPC90ZXh0PgogIDxyZWN0IHg9IjIwIiB5PSI0NiIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSI2MC4wIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8cmVjdCB4PSIxMDAiIHk9IjQ2IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjE0MC4wIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8cmVjdCB4PSIxODAiIHk9IjQ2IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjIyMC4wIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8cmVjdCB4PSIyNjAiIHk9IjQ2IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjMwMC4wIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8cmVjdCB4PSIzNDAiIHk9IjQ2IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjM4MC4wIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMjAiIHk9Ijc4IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjYwLjAiIHk9Ijk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMTU4MDNkIj50cnVlPC90ZXh0PgogIDxyZWN0IHg9IjEwMCIgeT0iNzgiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMTQwLjAiIHk9Ijk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjYjkxYzFjIj5mYWxzZTwvdGV4dD4KICA8cmVjdCB4PSIxODAiIHk9Ijc4IiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjIyMC4wIiB5PSI5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMjYwIiB5PSI3OCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIzMDAuMCIgeT0iOTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPnRydWU8L3RleHQ+CiAgPHJlY3QgeD0iMzQwIiB5PSI3OCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmVlMmUyIiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIzODAuMCIgeT0iOTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNiOTFjMWMiPmZhbHNlPC90ZXh0PgogIDxyZWN0IHg9IjIwIiB5PSIxMTAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iNjAuMCIgeT0iMTMxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjYjkxYzFjIj5mYWxzZTwvdGV4dD4KICA8cmVjdCB4PSIxMDAiIHk9IjExMCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiM4ZThlOTMiLz48dGV4dCB4PSIxNDAuMCIgeT0iMTMxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMTU4MDNkIj50cnVlPC90ZXh0PgogIDxyZWN0IHg9IjE4MCIgeT0iMTEwIiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjIyMC4wIiB5PSIxMzEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNiOTFjMWMiPmZhbHNlPC90ZXh0PgogIDxyZWN0IHg9IjI2MCIgeT0iMTEwIiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjMwMC4wIiB5PSIxMzEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPnRydWU8L3RleHQ+CiAgPHJlY3QgeD0iMzQwIiB5PSIxMTAiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2RjZmNlNyIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMzgwLjAiIHk9IjEzMSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8cmVjdCB4PSIyMCIgeT0iMTQyIiB3aWR0aD0iODAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iIzhlOGU5MyIvPjx0ZXh0IHg9IjYwLjAiIHk9IjE2MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMTAwIiB5PSIxNDIiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMTQwLjAiIHk9IjE2MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMTgwIiB5PSIxNDIiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMjIwLjAiIHk9IjE2MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMjYwIiB5PSIxNDIiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZlZTJlMiIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMzAwLjAiIHk9IjE2MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iI2I5MWMxYyI+ZmFsc2U8L3RleHQ+CiAgPHJlY3QgeD0iMzQwIiB5PSIxNDIiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2RjZmNlNyIgc3Ryb2tlPSIjOGU4ZTkzIi8+PHRleHQgeD0iMzgwLjAiIHk9IjE2MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCI+dHJ1ZTwvdGV4dD4KICA8bGluZSB4MT0iMTgwIiB5MT0iMTQiIHgyPSIxODAiIHkyPSIxNzQiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIzIi8+Cjwvc3ZnPgo=",pF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIj5BYmxhdWYgZGVyIFdoaWxlLVNjaGxlaWZlPC90ZXh0PgogIDxyZWN0IHg9IjEyMCIgeT0iNDQiIHdpZHRoPSIxNjAiIGhlaWdodD0iNDAiIHJ4PSIyMCIgZmlsbD0iI2ZlZjNjNyIgc3Ryb2tlPSIjYjQ1MzA5IiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMDAiIHk9IjY5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaAgQmVkaW5ndW5nIHByw7xmZW48L3RleHQ+CiAgPHJlY3QgeD0iMTIwIiB5PSIxMjgiIHdpZHRoPSIxNjAiIGhlaWdodD0iNDAiIHJ4PSI2IiBmaWxsPSIjZGJlYWZlIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMTUzIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgSW5oYWx0IGF1c2bDvGhyZW48L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iODQiIHgyPSIyMDAiIHkyPSIxMjYiLz4KICAgIDxwYXRoIGQ9Ik0xMjAgMTQ4IEM4MCAxNDgsIDgwIDY0LCAxMTggNjQiLz4KICAgIDxwYXRoIGQ9Ik0yODAgNjQgTDM0MCA2NCBMMzQwIDIwMCBMMjgyIDIwMCIvPgogIDwvZz4KICA8dGV4dCB4PSIyMDgiIHk9IjExMCIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+dHJ1ZTwvdGV4dD4KICA8dGV4dCB4PSIzOCIgeT0iMTEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj53aWVkZXI8L3RleHQ+CiAgPHRleHQgeD0iMzgiIHk9IjEyNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+bmFjaCBvYmVuPC90ZXh0PgogIDx0ZXh0IHg9IjM0OCIgeT0iMTM0IiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj5mYWxzZTwvdGV4dD4KICA8dGV4dCB4PSIyNzYiIHk9IjIwNSIgdGV4dC1hbmNob3I9ImVuZCI+d2VpdGVyIHVudGVyIGRlciBTY2hsZWlmZTwvdGV4dD4KPC9zdmc+Cg==",fF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMTAwIiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+a29wZmdlc3RldWVydCAoV2hpbGUpPC90ZXh0PgogIDx0ZXh0IHg9IjMwMCIgeT0iMjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPmZ1w59nZXN0ZXVlcnQgKERvLXdoaWxlKTwvdGV4dD4KICA8bGluZSB4MT0iMjAwIiB5MT0iMTAiIHgyPSIyMDAiIHkyPSIyMjAiIHN0cm9rZT0iI2QxZDFkNiIvPgogIDxyZWN0IHg9IjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjE4IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjEwMCIgeT0iNjciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoCBCZWRpbmd1bmc8L3RleHQ+CiAgPHJlY3QgeD0iMzUiIHk9IjEyNCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMTAwIiB5PSIxNDciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoSBJbmhhbHQ8L3RleHQ+CiAgPHJlY3QgeD0iMjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzAwIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIEluaGFsdDwvdGV4dD4KICA8cmVjdCB4PSIyMzUiIHk9IjEyNCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjE4IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjMwMCIgeT0iMTQ3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgQmVkaW5ndW5nPC90ZXh0PgogIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjYiIG1hcmtlci1lbmQ9InVybCgjYSkiPgogICAgPGxpbmUgeDE9IjEwMCIgeTE9IjgwIiB4Mj0iMTAwIiB5Mj0iMTIyIi8+CiAgICA8cGF0aCBkPSJNMzUgMTQyIEM4IDE0MiwgOCA2MiwgMzMgNjIiLz4KICAgIDxsaW5lIHgxPSIzMDAiIHkxPSI4MCIgeDI9IjMwMCIgeTI9IjEyMiIvPgogICAgPHBhdGggZD0iTTM2NSAxNDIgQzM5MiAxNDIsIDM5MiA2MiwgMzY3IDYyIi8+CiAgPC9nPgogIDx0ZXh0IHg9IjEwNiIgeT0iMTA2IiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjM4MCIgeT0iMTA2IiB0ZXh0LWFuY2hvcj0iZW5kIiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMTkwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5Jc3QgZGllIEJlZGluZ3VuZyBnbGVpY2ggZmFsc2UsPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMjA2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5sw6R1ZnQgZGVyIEluaGFsdCA8dHNwYW4gZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+bmllPC90c3Bhbj4uPC90ZXh0PgogIDx0ZXh0IHg9IjMwMCIgeT0iMTkwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5EZXIgSW5oYWx0IGzDpHVmdCBpbW1lcjwvdGV4dD4KICA8dGV4dCB4PSIzMDAiIHk9IjIwNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+PHRzcGFuIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPm1pbmRlc3RlbnMgZWlubWFsPC90c3Bhbj4uPC90ZXh0Pgo8L3N2Zz4K",bF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjUwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTIwIiB5PSIxNCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIzNiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIFN0YXJ0IDx0c3BhbiBmaWxsPSIjNmU2ZTczIj4obnVyIGVpbm1hbCk8L3RzcGFuPjwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9Ijc2IiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjM0IiByeD0iMTciIGZpbGw9IiNmZWYzYzciIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGhIEJlZGluZ3VuZyBwcsO8ZmVuPC90ZXh0PgogIDxyZWN0IHg9IjEyMCIgeT0iMTM4IiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjM0IiByeD0iNiIgZmlsbD0iI2RiZWFmZSIgc3Ryb2tlPSIjMWQ0ZWQ4IiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMDAiIHk9IjE2MCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGiIEluaGFsdCBhdXNmw7xocmVuPC90ZXh0PgogIDxyZWN0IHg9IjEyMCIgeT0iMjAwIiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjM0IiByeD0iNiIgZmlsbD0iI2RjZmNlNyIgc3Ryb2tlPSIjMTU4MDNkIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMDAiIHk9IjIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGjIFNjaHJpdHQsIHouIEIuIGkrKzwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMDAiIHkxPSI0OCIgeDI9IjIwMCIgeTI9Ijc0Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTEwIiB4Mj0iMjAwIiB5Mj0iMTM2Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTcyIiB4Mj0iMjAwIiB5Mj0iMTk4Ii8+CiAgICA8cGF0aCBkPSJNMTIwIDIxNyBDNjAgMjE3LCA2MCA5MywgMTE4IDkzIi8+CiAgICA8cGF0aCBkPSJNMjgwIDkzIEwzMzAgOTMiLz4KICA8L2c+CiAgPHRleHQgeD0iMjA4IiB5PSIxMjkiIGZpbGw9IiMxNTgwM2QiIGZvbnQtd2VpZ2h0PSI2MDAiPnRydWU8L3RleHQ+CiAgPHRleHQgeD0iNDgiIHk9IjE2MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+enVyw7xjazwvdGV4dD4KICA8dGV4dCB4PSI0OCIgeT0iMTc2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj56dSDikaE8L3RleHQ+CiAgPHRleHQgeD0iMzAwIiB5PSI4NCIgZmlsbD0iI2I5MWMxYyIgZm9udC13ZWlnaHQ9IjYwMCI+ZmFsc2U8L3RleHQ+CiAgPHRleHQgeD0iMzM2IiB5PSI5OCI+ZmVydGlnPC90ZXh0Pgo8L3N2Zz4K",xF=""+new URL("l12-while-zu-for-Cu-8sS5S.svg",import.meta.url).href,vF=""+new URL("l13-verschachtelt-tabelle-CmsFvxQ5.svg",import.meta.url).href,wF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMTkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxNCI+aW50W10gemFobGVuID0gezIsIDQsIDYsIDgsIDEwfTwvdGV4dD4KICA8dGV4dCB4PSIyMCIgeT0iNTQiIGZpbGw9IiM2ZTZlNzMiIGZvbnQtc2l6ZT0iMTIiPkluZGV4PC90ZXh0PgogIDx0ZXh0IHg9IjkwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+MDwvdGV4dD48cmVjdCB4PSI2MCIgeT0iNjIiIHdpZHRoPSI2MCIgaGVpZ2h0PSI0NiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSI5MCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjI8L3RleHQ+CiAgPHRleHQgeD0iMTUwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+MTwvdGV4dD48cmVjdCB4PSIxMjAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMTUwIiB5PSI5MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjYwMCI+NDwvdGV4dD4KICA8dGV4dCB4PSIyMTAiIHk9IjU0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDA3YWZmIiBmb250LXdlaWdodD0iNzAwIj4yPC90ZXh0PjxyZWN0IHg9IjE4MCIgeT0iNjIiIHdpZHRoPSI2MCIgaGVpZ2h0PSI0NiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIyMTAiIHk9IjkyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNjAwIj42PC90ZXh0PgogIDx0ZXh0IHg9IjI3MCIgeT0iNTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPjM8L3RleHQ+PHJlY3QgeD0iMjQwIiB5PSI2MiIgd2lkdGg9IjYwIiBoZWlnaHQ9IjQ2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjI3MCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjg8L3RleHQ+CiAgPHRleHQgeD0iMzMwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+NDwvdGV4dD48cmVjdCB4PSIzMDAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMzMwIiB5PSI5MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjYwMCI+MTA8L3RleHQ+CiAgPHRleHQgeD0iMjAiIHk9IjkwIiBmaWxsPSIjNmU2ZTczIiBmb250LXNpemU9IjEyIj5XZXJ0PC90ZXh0PgogIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzAwN2FmZiIgc3Ryb2tlLXdpZHRoPSIxLjgiIG1hcmtlci1lbmQ9InVybCgjYSkiPjxsaW5lIHgxPSI5MCIgeTE9IjE0MCIgeDI9IjkwIiB5Mj0iMTEyIi8+PGxpbmUgeDE9IjMzMCIgeTE9IjE0MCIgeDI9IjMzMCIgeTI9IjExMiIvPjwvZz4KICA8ZyBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxMyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+PHRleHQgeD0iOTAiIHk9IjE1NiI+emFobGVuWzBdIGlzdCAyPC90ZXh0Pjx0ZXh0IHg9IjMzMCIgeT0iMTU2Ij56YWhsZW5bNF0gaXN0IDEwPC90ZXh0PjwvZz4KICA8dGV4dCB4PSIyMDAiIHk9IjE4MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+NSBGw6RjaGVyLCBnZXrDpGhsdCB3aXJkIGFiIDA6IGRlciBsZXR6dGUgSW5kZXggaXN0IDQuPC90ZXh0Pgo8L3N2Zz4K",yF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMjkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPnJlY2huZXIuY3BwPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iNTEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMzYTNhM2MiPlF1ZWxsY29kZSBpbiBDKys8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iNjAiIHgyPSI4MCIgeTI9Ijk0Ii8+CiAgICA8bGluZSB4MT0iODAiIHkxPSIxMjYiIHgyPSI4MCIgeTI9IjE1NiIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjYwIiB4Mj0iMjIwIiB5Mj0iOTQiLz4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSIxMjYiIHgyPSIyMjAiIHkyPSIxNTYiLz4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSI2MCIgeDI9IjM2MCIgeTI9Ijk0Ii8+CiAgICA8bGluZSB4MT0iMzYwIiB5MT0iMTI2IiB4Mj0iMzYwIiB5Mj0iMTU2Ii8+CiAgPC9nPgogIDxyZWN0IHg9IjE4IiB5PSI5NiIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzMCIgcng9IjE1IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMS41Ii8+CiAgPHRleHQgeD0iODAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Q29tcGlsZXI8L3RleHQ+CiAgPHJlY3QgeD0iMTgiIHk9IjE1OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iI2I5MWMxYyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iODAiIHk9IjE4MSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+TWFzY2hpbmVuY29kZTwvdGV4dD4KICA8cmVjdCB4PSIxOCIgeT0iMTk0IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSI4MCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5XaW5kb3dzPC90ZXh0PgogIDxyZWN0IHg9IjE1OCIgeT0iOTYiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzAiIHJ4PSIxNSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjEuNSIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMTE2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5Db21waWxlcjwvdGV4dD4KICA8cmVjdCB4PSIxNTgiIHk9IjE1OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iI2I5MWMxYyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIxODEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPk1hc2NoaW5lbmNvZGU8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxOTQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5tYWNPUzwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIzNjAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Q29tcGlsZXI8L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIxNTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZmVlMmUyIiBzdHJva2U9IiNiOTFjMWMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMTgxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5NYXNjaGluZW5jb2RlPC90ZXh0PgogIDxyZWN0IHg9IjI5OCIgeT0iMTk0IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIzNjAiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+TGludXg8L3RleHQ+CiAgPHRleHQgeD0iMjIwIiB5PSIyNTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc3R5bGU9Iml0YWxpYyI+RsO8ciBqZWRlcyBTeXN0ZW08L3RleHQ+CiAgPHRleHQgeD0iMjIwIiB5PSIyNzgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc3R5bGU9Iml0YWxpYyI+bmV1IMO8YmVyc2V0emVuPC90ZXh0Pgo8L3N2Zz4K",zF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMzMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPkVpbm1hbGVpbnMuamF2YTwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjUxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjM2EzYTNjIj5RdWVsbGNvZGUsIGbDvHIgTWVuc2NoZW48L3RleHQ+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMDQiIHdpZHRoPSIyMjAiIGhlaWdodD0iNDgiIHJ4PSI2IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMTI0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC13ZWlnaHQ9IjYwMCI+RWlubWFsZWlucy5jbGFzczwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjE0MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzNhM2EzYyI+Qnl0ZWNvZGU6IENBIEZFIEJBIEJFIOKApjwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSI2MCIgeDI9IjIyMCIgeTI9IjEwMiIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjE1MiIgeDI9IjgwIiB5Mj0iMTk2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iMTUyIiB4Mj0iMjIwIiB5Mj0iMTk2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iMTUyIiB4Mj0iMzYwIiB5Mj0iMTk2Ii8+CiAgPC9nPgogIDx0ZXh0IHg9IjIzMCIgeT0iODYiPkNvbXBpbGVyIGphdmFjPC90ZXh0PgogIDxyZWN0IHg9IjE4IiB5PSIxOTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiMxNTgwM2QiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjgwIiB5PSIyMjEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPkpWTTwvdGV4dD4KICA8cmVjdCB4PSIxOCIgeT0iMjM0IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSI4MCIgeT0iMjU3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5XaW5kb3dzPC90ZXh0PgogIDxyZWN0IHg9IjE1OCIgeT0iMTk4IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2RjZmNlNyIgc3Ryb2tlPSIjMTU4MDNkIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMjAiIHk9IjIyMSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+SlZNPC90ZXh0PgogIDxyZWN0IHg9IjE1OCIgeT0iMjM0IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjM2IiByeD0iNiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMjAiIHk9IjI1NyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+bWFjT1M8L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIxOTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiMxNTgwM2QiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMjIxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5KVk08L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIyMzQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMjU3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5MaW51eDwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zdHlsZT0iaXRhbGljIj5FaW5tYWwgc2NocmVpYmVuLDwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjMxOCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zdHlsZT0iaXRhbGljIj7DvGJlcmFsbCBhdXNmw7xocmVuPC90ZXh0Pgo8L3N2Zz4K",IF=""+new URL("margaret-hamilton-pYPKs1sU.jpg",import.meta.url).href,kF=""+new URL("mvc-klick-KmFKqxH-.svg",import.meta.url).href,SF=""+new URL("aufgabe-mehrwertsteuer-KUkerRBd.svg",import.meta.url).href,TF=""+new URL("kino-saal-BOvcmdzb.svg",import.meta.url).href,_F=""+new URL("kino-schleifen-Ci9yPnbo.svg",import.meta.url).href,AF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDkyIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iNDkyIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiPgogICAgPCEtLSBTdGFydCAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMTAiIHdpZHRoPSIxMDAiIGhlaWdodD0iMjgiIHJ4PSIxNCIgZmlsbD0iI2QxZjJkOSIvPgogICAgPGxpbmUgeDE9IjE1MCIgeTE9IjM4IiB4Mj0iMTUwIiB5Mj0iNTQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gQW53ZWlzdW5nIC0tPgogICAgPHJlY3QgeD0iNTAiIHk9IjU2IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iODYiIHgyPSIxNTAiIHkyPSI5OCIgbWFya2VyLWVuZD0idXJsKCNhKSIvPgogICAgPCEtLSBSYXV0ZSAxIC0tPgogICAgPHBhdGggZD0iTTE1MCAxMDAgTDIzNSAxMjggTDE1MCAxNTYgTDY1IDEyOCBaIiBmaWxsPSIjZmZmNGQ2Ii8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMTU2IiB4Mj0iMTUwIiB5Mj0iMTcyIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8cmVjdCB4PSI1MCIgeT0iMTc0IiB3aWR0aD0iMjAwIiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8bGluZSB4MT0iMTUwIiB5MT0iMjA0IiB4Mj0iMTUwIiB5Mj0iMjUwIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIG5laW4tV2VnIHVtIGRlbiBLYXN0ZW4gaGVydW0gLS0+CiAgICA8cG9seWxpbmUgcG9pbnRzPSIyMzUsMTI4IDI4NSwxMjggMjg1LDIyMiAxNTAsMjIyIiBmaWxsPSJub25lIiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgICA8IS0tIFJhdXRlIDIgLS0+CiAgICA8cGF0aCBkPSJNMTUwIDI1MiBMMjM1IDI4MCBMMTUwIDMwOCBMNjUgMjgwIFoiIGZpbGw9IiNmZmY0ZDYiLz4KICAgIDxsaW5lIHgxPSIyMzUiIHkxPSIyODAiIHgyPSIyNjgiIHkyPSIyODAiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjI3MCIgeT0iMjY1IiB3aWR0aD0iMTE1IiBoZWlnaHQ9IjMwIiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8IS0tIHp1csO8Y2sgenVyIFByw7xmdW5nIC0tPgogICAgPHBvbHlsaW5lIHBvaW50cz0iMzI3LDI2NSAzMjcsMjM4IDE1MCwyMzgiIGZpbGw9Im5vbmUiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzMDgiIHgyPSIxNTAiIHkyPSIzMjYiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDxyZWN0IHg9IjUwIiB5PSIzMjgiIHdpZHRoPSIyMDAiIGhlaWdodD0iMzAiIGZpbGw9IiNlNWYwZmYiLz4KICAgIDxsaW5lIHgxPSIxNTAiIHkxPSIzNTgiIHgyPSIxNTAiIHkyPSIzNzQiIG1hcmtlci1lbmQ9InVybCgjYSkiLz4KICAgIDwhLS0gRW5kZSAtLT4KICAgIDxyZWN0IHg9IjEwMCIgeT0iMzc2IiB3aWR0aD0iMTAwIiBoZWlnaHQ9IjI4IiByeD0iMTQiIGZpbGw9IiNkMWYyZDkiLz4KICA8L2c+CiAgPGcgdGV4dC1hbmNob3I9Im1pZGRsZSI+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjI5Ij5JbsOtY2lvPC90ZXh0PgogICAgPHRleHQgeD0iMTUwIiB5PSI3NiI+UMO0ciB1bWEgY2jDoXZlbmEgbmEgbWVzYTwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMTMzIj5RdWVyIGHDp8O6Y2FyPzwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMTk0Ij5KdW50YXIgYcOnw7pjYXI8L3RleHQ+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjI4NSI+Q2jDoSBtdWl0byBxdWVudGU/PC90ZXh0PgogICAgPHRleHQgeD0iMzI3LjUiIHk9IjI4NSI+RXNwZXJhciAxIG1pbnV0bzwvdGV4dD4KICAgIDx0ZXh0IHg9IjE1MCIgeT0iMzQ4Ij5CZWJlciBvIGNow6E8L3RleHQ+CiAgICA8dGV4dCB4PSIxNTAiIHk9IjM5NSI+RmltPC90ZXh0PgogIDwvZz4KICA8ZyBmb250LXNpemU9IjEyIiBmb250LXdlaWdodD0iNjAwIj4KICAgIDx0ZXh0IHg9IjE1NiIgeT0iMTY4IiBmaWxsPSIjMjQ4YTNkIj5zaW08L3RleHQ+CiAgICA8dGV4dCB4PSIyNDIiIHk9IjEyMiIgZmlsbD0iI2Q3MDAxNSI+bsOjbzwvdGV4dD4KICAgIDx0ZXh0IHg9IjI0MCIgeT0iMjc0IiBmaWxsPSIjMjQ4YTNkIj5zaW08L3RleHQ+CiAgICA8dGV4dCB4PSIxNTYiIHk9IjMyMCIgZmlsbD0iI2Q3MDAxNSI+bsOjbzwvdGV4dD4KICA8L2c+CiAgPCEtLSBMZWdlbmRlIC0tPgogIDxsaW5lIHgxPSIxMCIgeTE9IjQxOCIgeDI9IjM5MCIgeTI9IjQxOCIgc3Ryb2tlPSIjZDFkMWQ2Ii8+CiAgPGcgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuNSI+CiAgICA8cmVjdCB4PSI0MCIgeT0iNDMwIiB3aWR0aD0iMzYiIGhlaWdodD0iMTgiIHJ4PSI5IiBmaWxsPSIjZDFmMmQ5Ii8+CiAgICA8cmVjdCB4PSIyMjAiIHk9IjQzMCIgd2lkdGg9IjM2IiBoZWlnaHQ9IjE4IiBmaWxsPSIjZTVmMGZmIi8+CiAgICA8cGF0aCBkPSJNNTggNDU4IEw3NiA0NjkgTDU4IDQ4MCBMNDAgNDY5IFoiIGZpbGw9IiNmZmY0ZDYiLz4KICAgIDxwYXRoIGQ9Ik0yMjYgNDYwIEwyNTYgNDYwIEwyNTAgNDc4IEwyMjAgNDc4IFoiIGZpbGw9IiNmMmU2ZmYiLz4KICA8L2c+CiAgPGcgZm9udC1zaXplPSIxMiIgZmlsbD0iIzNhM2EzYyI+CiAgICA8dGV4dCB4PSI4NiIgeT0iNDQzIj5JbsOtY2lvIG91IGZpbTwvdGV4dD4KICAgIDx0ZXh0IHg9IjI2NiIgeT0iNDQzIj5JbnN0cnXDp8OjbzwvdGV4dD4KICAgIDx0ZXh0IHg9Ijg2IiB5PSI0NzMiPkRlY2lzw6NvPC90ZXh0PgogICAgPHRleHQgeD0iMjY2IiB5PSI0NzMiPkVudHJhZGEgb3Ugc2HDrWRhPC90ZXh0PgogIDwvZz4KPC9zdmc+Cg==",jF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE5MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwIiB5PSIyNCIgZm9udC13ZWlnaHQ9IjYwMCI+T25kZSBmaWNhIG8gZWxlbWVudG8/PC90ZXh0PgogIDxyZWN0IHg9IjIwIiB5PSI0MCIgd2lkdGg9IjE5MCIgaGVpZ2h0PSIzNSIgZmlsbD0iI2RiZWFmZSIvPgogIDxyZWN0IHg9IjIwIiB5PSI3NSIgd2lkdGg9IjE5MCIgaGVpZ2h0PSIzNSIgZmlsbD0iI2RjZmNlNyIvPgogIDxsaW5lIHgxPSIyMCIgeTE9Ijc1IiB4Mj0iMjEwIiB5Mj0iNzUiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLWRhc2hhcnJheT0iNCAzIi8+CiAgPHJlY3QgeD0iMjAiIHk9IjQwIiB3aWR0aD0iMTkwIiBoZWlnaHQ9IjcwIiBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjExNSIgeT0iNjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxZDRlZDgiPm1ldGFkZSBkZSBjaW1hPC90ZXh0PgogIDx0ZXh0IHg9IjExNSIgeT0iOTciIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPm1ldGFkZSBkZSBiYWl4bzwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9IjYyIj7ihpIgZmljYSA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+YW50ZXM8L3RzcGFuPjwvdGV4dD4KICA8dGV4dCB4PSIyMjQiIHk9Ijk3Ij7ihpIgZmljYSA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+ZGVwb2lzPC90c3Bhbj48L3RleHQ+CiAgPHJlY3QgeD0iMjAiIHk9IjEzMiIgd2lkdGg9IjE5MCIgaGVpZ2h0PSI0MCIgZmlsbD0iI2ZlZjNjNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIxMTUiIHk9IjE1NyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiI+w7g8L3RleHQ+CiAgPHRleHQgeD0iMjI0IiB5PSIxNTciPuKGkiA8dHNwYW4gZm9udC13ZWlnaHQ9IjYwMCI+c3Vic3RpdHVpLW88L3RzcGFuPjwvdGV4dD4KPC9zdmc+Cg==",CF=""+new URL("l03-lochkarte-beschriftet-D1QjqGFo.svg",import.meta.url).href,DF=""+new URL("l03-lochkarte-BSn6oWaS.svg",import.meta.url).href,BF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NjAgMjkwIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSI+CjxyZWN0IHdpZHRoPSI0NjAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+Cjx0ZXh0IHg9IjUwIiB5PSI0NCIgZm9udC1zaXplPSI0MCIgZmlsbD0iIzZlNmU3MyIgdGV4dExlbmd0aD0iMjY0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmdBbmRHbHlwaHMiPnByZWNvPHRzcGFuIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPkM8L3RzcGFuPm9tPHRzcGFuIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPkk8L3RzcGFuPnZhPC90ZXh0Pgo8bGluZSB4MT0iMTgyIiB5MT0iNTQiIHgyPSIxODIiIHkyPSI4MiIgc3Ryb2tlPSIjMDA3YWZmIiBzdHJva2Utd2lkdGg9IjIuNSIgc3Ryb2tlLWRhc2hhcnJheT0iNSA0Ii8+CjxsaW5lIHgxPSIyNTQiIHkxPSI1NCIgeDI9IjI1NCIgeTI9IjgyIiBzdHJva2U9IiMwMDdhZmYiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtZGFzaGFycmF5PSI1IDQiLz4KPHBhdGggZD0iTTIwIDI3MiBINDQwIiBzdHJva2U9IiNlNWQzYjAiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0xNTYgMTcyIEwxNTAgMjI2IiBzdHJva2U9IiNiODhhNTIiIHN0cm9rZS13aWR0aD0iMTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTUwIDIyNiBMMTU2IDI2MiIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIxNTAiIGN5PSIyMjYiIHI9IjYiIGZpbGw9IiM3YTU1MzAiIG9wYWNpdHk9IjAuNTUiLz4KPGVsbGlwc2UgY3g9IjE2MSIgY3k9IjI2NyIgcng9IjExIiByeT0iNSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMzA0IDE3MiBMMzA4IDIyNiIgc3Ryb2tlPSIjYjg4YTUyIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTMwOCAyMjYgTDMwNCAyNjIiIHN0cm9rZT0iI2I4OGE1MiIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMzA4IiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIzMDkiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTEyMCAxNDAgQzEwNCAxNTAgMTA0IDE3NiAxMTAgMTk2IiBmaWxsPSJub25lIiBzdHJva2U9IiNiODhhNTIiIHN0cm9rZS13aWR0aD0iNSIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxlbGxpcHNlIGN4PSIxMTAiIGN5PSIyMDAiIHJ4PSI1IiByeT0iOSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMTIwIDE3MiBDMTA4IDE1MiAxMTQgMTI4IDEzNCAxMjIgQzE0OCAxMjAgMTU0IDg0IDE4MiA4NCBDMjEwIDg0IDIwOCAxMjYgMjE4IDEyNCBDMjI4IDEyNiAyMjYgODQgMjU0IDg0IEMyODIgODQgMjg0IDEyMiAyOTQgMTI4IEMzMTYgMTMyIDMyNCAxNTggMzQwIDE1OCBDMzUyIDE1OCAzNTQgMTI0IDM1OCAxMTAgQzM2MiA5OCAzNzYgOTYgMzg4IDk5IEM0MDAgMTAyIDQwNiAxMDggNDA0IDExNiBDNDAyIDEyMyAzOTAgMTI0IDM4MCAxMjIgQzM3MiAxMjIgMzcwIDEyOCAzNzAgMTM2IEMzNzAgMTYyIDM2NCAxODQgMzQyIDE4NiBDMzI2IDE4OCAzMTYgMTgyIDMwNiAxNzggQzI0OCAxODggMTg4IDE4OCAxNDAgMTgyIEMxMzAgMTgwIDEyNCAxNzcgMTIwIDE3MiBaIiBmaWxsPSIjZDJhNTZhIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMi41IiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik0xMzQgMTcyIEwxMjggMjI2IiBzdHJva2U9IiNkMmE1NmEiIHN0cm9rZS13aWR0aD0iMTMiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8cGF0aCBkPSJNMTI4IDIyNiBMMTM0IDI2MiIgc3Ryb2tlPSIjZDJhNTZhIiBzdHJva2Utd2lkdGg9IjgiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIxMjgiIGN5PSIyMjYiIHI9IjYiIGZpbGw9IiM3YTU1MzAiIG9wYWNpdHk9IjAuNTUiLz4KPGVsbGlwc2UgY3g9IjEzOSIgY3k9IjI2NyIgcng9IjExIiByeT0iNSIgZmlsbD0iIzdhNTUzMCIvPgo8cGF0aCBkPSJNMjg0IDE3MiBMMjg4IDIyNiIgc3Ryb2tlPSIjZDJhNTZhIiBzdHJva2Utd2lkdGg9IjEzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPHBhdGggZD0iTTI4OCAyMjYgTDI4NCAyNjIiIHN0cm9rZT0iI2QyYTU2YSIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPGNpcmNsZSBjeD0iMjg4IiBjeT0iMjI2IiByPSI2IiBmaWxsPSIjN2E1NTMwIiBvcGFjaXR5PSIwLjU1Ii8+CjxlbGxpcHNlIGN4PSIyODkiIGN5PSIyNjciIHJ4PSIxMSIgcnk9IjUiIGZpbGw9IiM3YTU1MzAiLz4KPHBhdGggZD0iTTM2NiAxMDMgTDM2MiA5MiBMMzc0IDEwMCBaIiBmaWxsPSIjZDJhNTZhIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgo8Y2lyY2xlIGN4PSIzODIiIGN5PSIxMDciIHI9IjMuMiIgZmlsbD0iIzFjMWMxZSIvPgo8cGF0aCBkPSJNMzc3IDEwNCBRMzgyIDEwMSAzODcgMTA0IiBmaWxsPSJub25lIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zOTggMTA3IHE0IDEgNSA0IiBmaWxsPSJub25lIiBzdHJva2U9IiM3YTU1MzAiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIi8+CjxwYXRoIGQ9Ik0zOTIgMTE5IFEzOTggMTE4IDQwMyAxMTciIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzdhNTUzMCIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+Cg==",PF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjEwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjIxMCIgZmlsbD0iI2ZmZmZmZiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMjQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtZmFtaWx5PSJ1aS1tb25vc3BhY2UsIE1lbmxvLCBDb25zb2xhcywgbW9ub3NwYWNlIiBmb250LXNpemU9IjE2Ij5pbnQgaWRhZGUgPSAxNDwvdGV4dD4KICA8cGF0aCBkPSJNMTMwIDcwIEwxNzAgNTAgTDMwMCA1MCBMMjYwIDcwIFoiIGZpbGw9IiNjOThhM2MiIHN0cm9rZT0iIzhhNWExZiIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHJlY3QgeD0iMTgwIiB5PSIzNCIgd2lkdGg9IjQ0IiBoZWlnaHQ9IjUwIiByeD0iMyIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjEuNSIvPgogIDx0ZXh0IHg9IjIwMiIgeT0iNjAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMjAiIGZvbnQtd2VpZ2h0PSI3MDAiPjE0PC90ZXh0PgogIDxyZWN0IHg9IjEzMCIgeT0iNzAiIHdpZHRoPSIxMzAiIGhlaWdodD0iMTAwIiBmaWxsPSIjZTliODcyIiBzdHJva2U9IiM4YTVhMWYiIHN0cm9rZS13aWR0aD0iMiIvPgogIDxwYXRoIGQ9Ik0yNjAgNzAgTDMwMCA1MCBMMzAwIDE1MCBMMjYwIDE3MCBaIiBmaWxsPSIjZDQ5YTRjIiBzdHJva2U9IiM4YTVhMWYiIHN0cm9rZS13aWR0aD0iMiIvPgogIDxyZWN0IHg9IjE1MCIgeT0iMTAwIiB3aWR0aD0iOTAiIGhlaWdodD0iMzAiIHJ4PSI0IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiM4YTVhMWYiLz4KICA8dGV4dCB4PSIxOTUiIHk9IjEyMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+aWRhZGU8L3RleHQ+CiAgPHJlY3QgeD0iMTM4IiB5PSIxNDIiIHdpZHRoPSIzNCIgaGVpZ2h0PSIyMCIgcng9IjEwIiBmaWxsPSIjMDA3YWZmIi8+CiAgPHRleHQgeD0iMTU1IiB5PSIxNTYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9IiNmZmZmZmYiIGZvbnQtd2VpZ2h0PSI2MDAiPmludDwvdGV4dD4KICA8ZyBzdHJva2U9IiM2ZTZlNzMiPgogICAgPGxpbmUgeDE9IjMxMCIgeTE9IjQ4IiB4Mj0iMjI2IiB5Mj0iNTIiLz4KICAgIDxsaW5lIHgxPSIzMTAiIHkxPSIxMTUiIHgyPSIyNDIiIHkyPSIxMTUiLz4KICAgIDxsaW5lIHgxPSI4MCIgeTE9IjE1MiIgeDI9IjEzNCIgeTI9IjE1MiIvPgogIDwvZz4KICA8dGV4dCB4PSIzMTYiIHk9IjUyIj5WYWxvcjwvdGV4dD4KICA8dGV4dCB4PSIzMTYiIHk9IjExOSI+Tm9tZTwvdGV4dD4KICA8dGV4dCB4PSI4IiB5PSIxNTYiPlRpcG8gZGUgZGFkb3M8L3RleHQ+CiAgPHRleHQgeD0iMjAwIiB5PSIxOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPlBlbG8gbm9tZSBjaGVnYXMgYW8gdmFsb3IuPC90ZXh0Pgo8L3N2Zz4K",OF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjAwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjAwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMjAiPgogICAgPHRleHQgeD0iNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+cG9udG9zPC90ZXh0PgogICAgPHRleHQgeD0iMTI1IiB5PSIxMTAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPj08L3RleHQ+CiAgICA8dGV4dCB4PSIyNjAiIHk9IjExMCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+cG9udG9zICsgNTwvdGV4dD4KICA8L2c+CiAgPHJlY3QgeD0iMTgwIiB5PSI4NiIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjYwIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMxNTgwM2QiPuKRoCBjYWxjdWxhciBhIGRpcmVpdGE6IDEwICsgNSA9IDE1PC90ZXh0PgogIDxwYXRoIGQ9Ik0yNTAgODIgQzIzMCAzMCwgOTAgMzAsIDY0IDg0IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDdhZmYiIHN0cm9rZS13aWR0aD0iMi41IiBtYXJrZXItZW5kPSJ1cmwoI2EpIi8+CiAgPHRleHQgeD0iMTYwIiB5PSIzNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiI+4pGhIGd1YXJkYXIgbyByZXN1bHRhZG8gw6AgZXNxdWVyZGE8L3RleHQ+CiAgPHJlY3QgeD0iMjAiIHk9IjE2NCIgd2lkdGg9IjM2MCIgaGVpZ2h0PSIyNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciLz4KICA8dGV4dCB4PSIyMDAiIHk9IjE4MiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+cG9udG9zIGFudGVzOiA8dHNwYW4gZm9udC13ZWlnaHQ9IjcwMCI+MTA8L3RzcGFuPiAgIOKGkiAgIHBvbnRvcyBkZXBvaXM6IDx0c3BhbiBmb250LXdlaWdodD0iNzAwIj4xNTwvdHNwYW4+PC90ZXh0Pgo8L3N2Zz4K",MF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMzAwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMzAwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPGcgc3Ryb2tlPSIjNmU2ZTczIiBzdHJva2Utd2lkdGg9IjIiIGZpbGw9Im5vbmUiPgogICAgPHBhdGggZD0iTTQwIDExMCBMNDggMTgwIEwxMDIgMTgwIEwxMTAgMTEwIi8+CiAgICA8cGF0aCBkPSJNMTY1IDExMCBMMTczIDE4MCBMMjI3IDE4MCBMMjM1IDExMCIvPgogICAgPHBhdGggZD0iTTI5MCAxMTAgTDI5OCAxODAgTDM1MiAxODAgTDM2MCAxMTAiLz4KICA8L2c+CiAgPHBhdGggZD0iTTQ0IDEzNSBMNDggMTgwIEwxMDIgMTgwIEwxMDYgMTM1IFoiIGZpbGw9IiNmZGJhNzQiLz4KICA8cGF0aCBkPSJNMjk0IDEzNSBMMjk4IDE4MCBMMzUyIDE4MCBMMzU2IDEzNSBaIiBmaWxsPSIjOTNjNWZkIi8+CiAgPHRleHQgeD0iNzUiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+ZXNxdWVyZGE8L3RleHQ+CiAgPHRleHQgeD0iNzUiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+c3VtbzwvdGV4dD4KICA8dGV4dCB4PSIyMDAiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+YXV4PC90ZXh0PgogIDx0ZXh0IHg9IjIwMCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj52YXppbzwvdGV4dD4KICA8dGV4dCB4PSIzMjUiIHk9IjIwMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+ZGlyZWl0YTwvdGV4dD4KICA8dGV4dCB4PSIzMjUiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+w6FndWE8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8cGF0aCBkPSJNODggMTA0IEMxMTAgNzAsIDE2MCA3MCwgMTg2IDEwNCIvPgogICAgPHBhdGggZD0iTTMxOCAxMDQgQzI3MCAxNSwgMTMwIDE1LCA4MiAxMDQiLz4KICAgIDxwYXRoIGQ9Ik0yMTQgMTA0IEMyNDAgNzAsIDI5MCA3MCwgMzEyIDEwNCIvPgogIDwvZz4KICA8ZyBmb250LXdlaWdodD0iNzAwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4KICAgIDxjaXJjbGUgY3g9IjEzNyIgY3k9IjgwIiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMTM3IiB5PSI4NSI+MTwvdGV4dD4KICAgIDxjaXJjbGUgY3g9IjIwMCIgY3k9IjM4IiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMjAwIiB5PSI0MyI+MjwvdGV4dD4KICAgIDxjaXJjbGUgY3g9IjI2MyIgY3k9IjgwIiByPSIxMSIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+PHRleHQgeD0iMjYzIiB5PSI4NSI+MzwvdGV4dD4KICA8L2c+CiAgPGcgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTIiPgogICAgPHRleHQgeD0iMjAwIiB5PSIyNTAiPjE6IGF1eCA9IGVzcXVlcmRhPC90ZXh0PgogICAgPHRleHQgeD0iMjAwIiB5PSIyNjgiPjI6IGVzcXVlcmRhID0gZGlyZWl0YTwvdGV4dD4KICAgIDx0ZXh0IHg9IjIwMCIgeT0iMjg2Ij4zOiBkaXJlaXRhID0gYXV4PC90ZXh0PgogIDwvZz4KPC9zdmc+Cg==",ZF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8cmVjdCB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE1MCIgZmlsbD0iI2ZmZmZmZiIvPgogIDxyZWN0IHg9IjIwIiB5PSI1MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+PHJlY3QgeD0iMzAwIiB5PSI1MCIgd2lkdGg9IjgwIiBoZWlnaHQ9IjI0IiBmaWxsPSIjZmVlMmUyIi8+CiAgPHJlY3QgeD0iMTAwIiB5PSI1MCIgd2lkdGg9IjIwMCIgaGVpZ2h0PSIyNCIgZmlsbD0iI2RjZmNlNyIvPgogIDxsaW5lIHgxPSIyMCIgeTE9Ijc0IiB4Mj0iMzgwIiB5Mj0iNzQiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPGxpbmUgeDE9IjUwIiB5MT0iNjgiIHgyPSI1MCIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjUwIiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MDwvdGV4dD4KICA8bGluZSB4MT0iMTAwIiB5MT0iNjgiIHgyPSIxMDAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIxMDAiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj4xPC90ZXh0PgogIDxsaW5lIHgxPSIxNTAiIHkxPSI2OCIgeDI9IjE1MCIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjE1MCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjI8L3RleHQ+CiAgPGxpbmUgeDE9IjIwMCIgeTE9IjY4IiB4Mj0iMjAwIiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMjAwIiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+MzwvdGV4dD4KICA8bGluZSB4MT0iMjUwIiB5MT0iNjgiIHgyPSIyNTAiIHkyPSI4MCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIyNTAiIHk9Ijk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj40PC90ZXh0PgogIDxsaW5lIHgxPSIzMDAiIHkxPSI2OCIgeDI9IjMwMCIgeTI9IjgwIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjMwMCIgeT0iOTgiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjU8L3RleHQ+CiAgPGxpbmUgeDE9IjM1MCIgeTE9IjY4IiB4Mj0iMzUwIiB5Mj0iODAiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMzUwIiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+NjwvdGV4dD4KICA8Y2lyY2xlIGN4PSIxMDAiIGN5PSI3NCIgcj0iNiIgZmlsbD0iIzE1ODAzZCIvPjxjaXJjbGUgY3g9IjMwMCIgY3k9Ijc0IiByPSI2IiBmaWxsPSIjMTU4MDNkIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI0MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+dsOhbGlkYTwvdGV4dD4KICA8dGV4dCB4PSI1MCIgeT0iNDAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiNiOTFjMWMiIGZvbnQtd2VpZ2h0PSI2MDAiPmludsOhbGlkYTwvdGV4dD48dGV4dCB4PSIzNTAiIHk9IjQwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj5pbnbDoWxpZGE8L3RleHQ+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPgogICAgPHRleHQgeD0iMjAwIiB5PSIxMjQiIGZpbGw9IiMxNTgwM2QiPm5vdGEgJmd0Oz0gMSAmYW1wOyZhbXA7IG5vdGEgJmx0Oz0gNTwvdGV4dD4KICAgIDx0ZXh0IHg9IjIwMCIgeT0iMTQyIiBmaWxsPSIjYjkxYzFjIj5ub3RhICZsdDsgMSB8fCBub3RhICZndDsgNTwvdGV4dD4KICA8L2c+Cjwvc3ZnPgo=",LF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIj5Db21vIGZ1bmNpb25hIG8gY2ljbG8gd2hpbGU8L3RleHQ+CiAgPHJlY3QgeD0iMTIwIiB5PSI0NCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSI0MCIgcng9IjIwIiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iNjkiIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoCB2ZXJpZmljYXIgYSBjb25kacOnw6NvPC90ZXh0PgogIDxyZWN0IHg9IjEyMCIgeT0iMTI4IiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjQwIiByeD0iNiIgZmlsbD0iI2RiZWFmZSIgc3Ryb2tlPSIjMWQ0ZWQ4IiBzdHJva2Utd2lkdGg9IjIiLz4KICA8dGV4dCB4PSIyMDAiIHk9IjE1MyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGhIGV4ZWN1dGFyIG8gaW50ZXJpb3I8L3RleHQ+CiAgPGcgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEuOCIgbWFya2VyLWVuZD0idXJsKCNhKSI+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iODQiIHgyPSIyMDAiIHkyPSIxMjYiLz4KICAgIDxwYXRoIGQ9Ik0xMjAgMTQ4IEM4MCAxNDgsIDgwIDY0LCAxMTggNjQiLz4KICAgIDxwYXRoIGQ9Ik0yODAgNjQgTDM0MCA2NCBMMzQwIDIwMCBMMjgyIDIwMCIvPgogIDwvZz4KICA8dGV4dCB4PSIyMDgiIHk9IjExMCIgZmlsbD0iIzE1ODAzZCIgZm9udC13ZWlnaHQ9IjYwMCI+dHJ1ZTwvdGV4dD4KICA8dGV4dCB4PSIzOCIgeT0iMTEwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj52b2x0YXI8L3RleHQ+CiAgPHRleHQgeD0iMzgiIHk9IjEyNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+cGFyYSBjaW1hPC90ZXh0PgogIDx0ZXh0IHg9IjM0OCIgeT0iMTM0IiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj5mYWxzZTwvdGV4dD4KICA8dGV4dCB4PSIyNzYiIHk9IjIwNSIgdGV4dC1hbmNob3I9ImVuZCI+Y29udGludWFyIHBvciBiYWl4byBkbyBjaWNsbzwvdGV4dD4KPC9zdmc+Cg==",WF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMTAwIiB5PSIyMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+cHLDqS10ZXN0ZSAod2hpbGUpPC90ZXh0PgogIDx0ZXh0IHg9IjMwMCIgeT0iMjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPnDDs3MtdGVzdGUgKGRvLXdoaWxlKTwvdGV4dD4KICA8bGluZSB4MT0iMjAwIiB5MT0iMTAiIHgyPSIyMDAiIHkyPSIyMjAiIHN0cm9rZT0iI2QxZDFkNiIvPgogIDxyZWN0IHg9IjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjE4IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjEwMCIgeT0iNjciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoCBjb25kacOnw6NvPC90ZXh0PgogIDxyZWN0IHg9IjM1IiB5PSIxMjQiIHdpZHRoPSIxMzAiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZGJlYWZlIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjEwMCIgeT0iMTQ3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaEgaW50ZXJpb3I8L3RleHQ+CiAgPHJlY3QgeD0iMjM1IiB5PSI0NCIgd2lkdGg9IjEzMCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzAwIiB5PSI2NyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIGludGVyaW9yPC90ZXh0PgogIDxyZWN0IHg9IjIzNSIgeT0iMTI0IiB3aWR0aD0iMTMwIiBoZWlnaHQ9IjM2IiByeD0iMTgiIGZpbGw9IiNmZWYzYzciIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzAwIiB5PSIxNDciIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoSBjb25kacOnw6NvPC90ZXh0PgogIDxnIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjYiIG1hcmtlci1lbmQ9InVybCgjYSkiPgogICAgPGxpbmUgeDE9IjEwMCIgeTE9IjgwIiB4Mj0iMTAwIiB5Mj0iMTIyIi8+CiAgICA8cGF0aCBkPSJNMzUgMTQyIEM4IDE0MiwgOCA2MiwgMzMgNjIiLz4KICAgIDxsaW5lIHgxPSIzMDAiIHkxPSI4MCIgeDI9IjMwMCIgeTI9IjEyMiIvPgogICAgPHBhdGggZD0iTTM2NSAxNDIgQzM5MiAxNDIsIDM5MiA2MiwgMzY3IDYyIi8+CiAgPC9nPgogIDx0ZXh0IHg9IjEwNiIgeT0iMTA2IiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjM4MCIgeT0iMTA2IiB0ZXh0LWFuY2hvcj0iZW5kIiBmaWxsPSIjMTU4MDNkIiBmb250LXdlaWdodD0iNjAwIj50cnVlPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMTkwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5TZSBhIGNvbmRpw6fDo28gZm9yIGxvZ28gZmFsc2EsPC90ZXh0PgogIDx0ZXh0IHg9IjEwMCIgeT0iMjA2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjNmU2ZTczIj5vIGludGVyaW9yIDx0c3BhbiBmb250LXdlaWdodD0iNzAwIiBmaWxsPSIjMWMxYzFlIj5udW5jYTwvdHNwYW4+IMOpIGV4ZWN1dGFkby48L3RleHQ+CiAgPHRleHQgeD0iMzAwIiB5PSIxOTAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPk8gaW50ZXJpb3Igw6kgc2VtcHJlIGV4ZWN1dGFkbzwvdGV4dD4KICA8dGV4dCB4PSIzMDAiIHk9IjIwNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+PHRzcGFuIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPnBlbG8gbWVub3MgdW1hIHZlejwvdHNwYW4+LjwvdGV4dD4KPC9zdmc+Cg==",EF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMjUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMjUwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTIwIiB5PSIxNCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIzNiIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGgIEluw61jaW8gPHRzcGFuIGZpbGw9IiM2ZTZlNzMiPihzw7MgdW1hIHZleik8L3RzcGFuPjwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9Ijc2IiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjM0IiByeD0iMTciIGZpbGw9IiNmZWYzYzciIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSI5OCIgdGV4dC1hbmNob3I9Im1pZGRsZSI+4pGhIHZlcmlmaWNhciBhIGNvbmRpw6fDo288L3RleHQ+CiAgPHJlY3QgeD0iMTIwIiB5PSIxMzgiIHdpZHRoPSIxNjAiIGhlaWdodD0iMzQiIHJ4PSI2IiBmaWxsPSIjZGJlYWZlIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIwMCIgeT0iMTYwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj7ikaIgZXhlY3V0YXIgbyBpbnRlcmlvcjwvdGV4dD4KICA8cmVjdCB4PSIxMjAiIHk9IjIwMCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSIzNCIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjAwIiB5PSIyMjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiPuKRoyBwYXNzbywgcC4gZXguIGkrKzwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMDAiIHkxPSI0OCIgeDI9IjIwMCIgeTI9Ijc0Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTEwIiB4Mj0iMjAwIiB5Mj0iMTM2Ii8+CiAgICA8bGluZSB4MT0iMjAwIiB5MT0iMTcyIiB4Mj0iMjAwIiB5Mj0iMTk4Ii8+CiAgICA8cGF0aCBkPSJNMTIwIDIxNyBDNjAgMjE3LCA2MCA5MywgMTE4IDkzIi8+CiAgICA8cGF0aCBkPSJNMjgwIDkzIEwzMzAgOTMiLz4KICA8L2c+CiAgPHRleHQgeD0iMjA4IiB5PSIxMjkiIGZpbGw9IiMxNTgwM2QiIGZvbnQtd2VpZ2h0PSI2MDAiPnRydWU8L3RleHQ+CiAgPHRleHQgeD0iNDgiIHk9IjE2MCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzZlNmU3MyI+dm9sdGFyPC90ZXh0PgogIDx0ZXh0IHg9IjQ4IiB5PSIxNzYiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPmFvIOKRoTwvdGV4dD4KICA8dGV4dCB4PSIzMDAiIHk9Ijg0IiBmaWxsPSIjYjkxYzFjIiBmb250LXdlaWdodD0iNjAwIj5mYWxzZTwvdGV4dD4KICA8dGV4dCB4PSIzMzYiIHk9Ijk4Ij5maW08L3RleHQ+Cjwvc3ZnPgo=",NF=""+new URL("l12-while-zu-for-CM2--y57.svg",import.meta.url).href,FF=""+new URL("l13-verschachtelt-tabelle-CR2WySqU.svg",import.meta.url).href,RF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgMTkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMDA3YWZmIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0MDAiIGhlaWdodD0iMTkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHRleHQgeD0iMjAiIHk9IjI0IiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC1zaXplPSIxNCI+aW50W10gbnVtZXJvcyA9IHsyLCA0LCA2LCA4LCAxMH08L3RleHQ+CiAgPHRleHQgeD0iMjAiIHk9IjU0IiBmaWxsPSIjNmU2ZTczIiBmb250LXNpemU9IjEyIj7DjW5kaWNlPC90ZXh0PgogIDx0ZXh0IHg9IjkwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+MDwvdGV4dD48cmVjdCB4PSI2MCIgeT0iNjIiIHdpZHRoPSI2MCIgaGVpZ2h0PSI0NiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSI5MCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjI8L3RleHQ+CiAgPHRleHQgeD0iMTUwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+MTwvdGV4dD48cmVjdCB4PSIxMjAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMTUwIiB5PSI5MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjYwMCI+NDwvdGV4dD4KICA8dGV4dCB4PSIyMTAiIHk9IjU0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmaWxsPSIjMDA3YWZmIiBmb250LXdlaWdodD0iNzAwIj4yPC90ZXh0PjxyZWN0IHg9IjE4MCIgeT0iNjIiIHdpZHRoPSI2MCIgaGVpZ2h0PSI0NiIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjIiLz48dGV4dCB4PSIyMTAiIHk9IjkyIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE4IiBmb250LXdlaWdodD0iNjAwIj42PC90ZXh0PgogIDx0ZXh0IHg9IjI3MCIgeT0iNTQiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiMwMDdhZmYiIGZvbnQtd2VpZ2h0PSI3MDAiPjM8L3RleHQ+PHJlY3QgeD0iMjQwIiB5PSI2MiIgd2lkdGg9IjYwIiBoZWlnaHQ9IjQ2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjI3MCIgeT0iOTIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI2MDAiPjg8L3RleHQ+CiAgPHRleHQgeD0iMzMwIiB5PSI1NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZmlsbD0iIzAwN2FmZiIgZm9udC13ZWlnaHQ9IjcwMCI+NDwvdGV4dD48cmVjdCB4PSIzMDAiIHk9IjYyIiB3aWR0aD0iNjAiIGhlaWdodD0iNDYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIyIi8+PHRleHQgeD0iMzMwIiB5PSI5MiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjYwMCI+MTA8L3RleHQ+CiAgPHRleHQgeD0iMjAiIHk9IjkwIiBmaWxsPSIjNmU2ZTczIiBmb250LXNpemU9IjEyIj5WYWxvcjwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMwMDdhZmYiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj48bGluZSB4MT0iOTAiIHkxPSIxNDAiIHgyPSI5MCIgeTI9IjExMiIvPjxsaW5lIHgxPSIzMzAiIHkxPSIxNDAiIHgyPSIzMzAiIHkyPSIxMTIiLz48L2c+CiAgPGcgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtc2l6ZT0iMTMiIHRleHQtYW5jaG9yPSJtaWRkbGUiPjx0ZXh0IHg9IjkwIiB5PSIxNTYiPm51bWVyb3NbMF0gw6kgMjwvdGV4dD48dGV4dCB4PSIzMzAiIHk9IjE1NiI+bnVtZXJvc1s0XSDDqSAxMDwvdGV4dD48L2c+CiAgPHRleHQgeD0iMjAwIiB5PSIxODIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZpbGw9IiM2ZTZlNzMiPjUgcG9zacOnw7VlcywgY29udGEtc2UgYSBwYXJ0aXIgZGUgMDogbyDDumx0aW1vIMOtbmRpY2Ugw6kgNC48L3RleHQ+Cjwvc3ZnPgo=",JF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMjkwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMjkwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPmNhbGN1bGFkb3JhLmNwcDwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjUxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjM2EzYTNjIj5jw7NkaWdvLWZvbnRlIGVtIEMrKzwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSI2MCIgeDI9IjgwIiB5Mj0iOTQiLz4KICAgIDxsaW5lIHgxPSI4MCIgeTE9IjEyNiIgeDI9IjgwIiB5Mj0iMTU2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iNjAiIHgyPSIyMjAiIHkyPSI5NCIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjEyNiIgeDI9IjIyMCIgeTI9IjE1NiIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjYwIiB4Mj0iMzYwIiB5Mj0iOTQiLz4KICAgIDxsaW5lIHgxPSIzNjAiIHkxPSIxMjYiIHgyPSIzNjAiIHkyPSIxNTYiLz4KICA8L2c+CiAgPHJlY3QgeD0iMTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSI4MCIgeT0iMTE2IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5jb21waWxhZG9yPC90ZXh0PgogIDxyZWN0IHg9IjE4IiB5PSIxNTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZmVlMmUyIiBzdHJva2U9IiNiOTFjMWMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjgwIiB5PSIxODEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPmPDs2RpZ28gbcOhcXVpbmE8L3RleHQ+CiAgPHJlY3QgeD0iMTgiIHk9IjE5NCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iODAiIHk9IjIxNyIgdGV4dC1hbmNob3I9Im1pZGRsZSI+V2luZG93czwvdGV4dD4KICA8cmVjdCB4PSIxNTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIyMjAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Y29tcGlsYWRvcjwvdGV4dD4KICA8cmVjdCB4PSIxNTgiIHk9IjE1OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iI2I5MWMxYyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIxODEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPmPDs2RpZ28gbcOhcXVpbmE8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxOTQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5tYWNPUzwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9Ijk2IiB3aWR0aD0iMTI0IiBoZWlnaHQ9IjMwIiByeD0iMTUiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KICA8dGV4dCB4PSIzNjAiIHk9IjExNiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+Y29tcGlsYWRvcjwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9IjE1OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmZWUyZTIiIHN0cm9rZT0iI2I5MWMxYyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzYwIiB5PSIxODEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPmPDs2RpZ28gbcOhcXVpbmE8L3RleHQ+CiAgPHJlY3QgeD0iMjk4IiB5PSIxOTQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjM2MCIgeT0iMjE3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5MaW51eDwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjI1OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zdHlsZT0iaXRhbGljIj5Db21waWxhciBkZSBub3ZvPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMjc4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPnBhcmEgY2FkYSBzaXN0ZW1hPC90ZXh0Pgo8L3N2Zz4K",GF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NDAgMzMwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KICA8ZGVmcz48bWFya2VyIGlkPSJhIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI3IiBtYXJrZXJIZWlnaHQ9IjciIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjMWMxYzFlIi8+PC9tYXJrZXI+PC9kZWZzPgogIDxyZWN0IHdpZHRoPSI0NDAiIGhlaWdodD0iMzMwIiBmaWxsPSIjZmZmZmZmIi8+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMiIgd2lkdGg9IjIyMCIgaGVpZ2h0PSI0OCIgcng9IjYiIGZpbGw9IiNkYmVhZmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMjIwIiB5PSIzMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1mYW1pbHk9InVpLW1vbm9zcGFjZSwgTWVubG8sIENvbnNvbGFzLCBtb25vc3BhY2UiIGZvbnQtd2VpZ2h0PSI2MDAiPlRhYnVhZGEuamF2YTwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjUxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjM2EzYTNjIj5jw7NkaWdvLWZvbnRlLCBwYXJhIHBlc3NvYXM8L3RleHQ+CiAgPHJlY3QgeD0iMTEwIiB5PSIxMDQiIHdpZHRoPSIyMjAiIGhlaWdodD0iNDgiIHJ4PSI2IiBmaWxsPSIjZmVmM2M3IiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMTI0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LWZhbWlseT0idWktbW9ub3NwYWNlLCBNZW5sbywgQ29uc29sYXMsIG1vbm9zcGFjZSIgZm9udC13ZWlnaHQ9IjYwMCI+VGFidWFkYS5jbGFzczwvdGV4dD4KICA8dGV4dCB4PSIyMjAiIHk9IjE0MyIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzNhM2EzYyI+Ynl0ZWNvZGU6IENBIEZFIEJBIEJFIOKApjwvdGV4dD4KICA8ZyBmaWxsPSJub25lIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS44IiBtYXJrZXItZW5kPSJ1cmwoI2EpIj4KICAgIDxsaW5lIHgxPSIyMjAiIHkxPSI2MCIgeDI9IjIyMCIgeTI9IjEwMiIvPgogICAgPGxpbmUgeDE9IjIyMCIgeTE9IjE1MiIgeDI9IjgwIiB5Mj0iMTk2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iMTUyIiB4Mj0iMjIwIiB5Mj0iMTk2Ii8+CiAgICA8bGluZSB4MT0iMjIwIiB5MT0iMTUyIiB4Mj0iMzYwIiB5Mj0iMTk2Ii8+CiAgPC9nPgogIDx0ZXh0IHg9IjIzMCIgeT0iODYiPmNvbXBpbGFkb3IgamF2YWM8L3RleHQ+CiAgPHJlY3QgeD0iMTgiIHk9IjE5OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iODAiIHk9IjIyMSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjYwMCI+SlZNPC90ZXh0PgogIDxyZWN0IHg9IjE4IiB5PSIyMzQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjgwIiB5PSIyNTciIHRleHQtYW5jaG9yPSJtaWRkbGUiPldpbmRvd3M8L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIxOTgiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZGNmY2U3IiBzdHJva2U9IiMxNTgwM2QiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjIxIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXdlaWdodD0iNjAwIj5KVk08L3RleHQ+CiAgPHJlY3QgeD0iMTU4IiB5PSIyMzQiIHdpZHRoPSIxMjQiIGhlaWdodD0iMzYiIHJ4PSI2IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiM2ZTZlNzMiIHN0cm9rZS13aWR0aD0iMiIvPgogIDx0ZXh0IHg9IjIyMCIgeT0iMjU3IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj5tYWNPUzwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9IjE5OCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzYwIiB5PSIyMjEiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtd2VpZ2h0PSI2MDAiPkpWTTwvdGV4dD4KICA8cmVjdCB4PSIyOTgiIHk9IjIzNCIgd2lkdGg9IjEyNCIgaGVpZ2h0PSIzNiIgcng9IjYiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzZlNmU3MyIgc3Ryb2tlLXdpZHRoPSIyIi8+CiAgPHRleHQgeD0iMzYwIiB5PSIyNTciIHRleHQtYW5jaG9yPSJtaWRkbGUiPkxpbnV4PC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMjk4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPkVzY3JldmVyIHVtYSB2ZXosPC90ZXh0PgogIDx0ZXh0IHg9IjIyMCIgeT0iMzE4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXN0eWxlPSJpdGFsaWMiPmV4ZWN1dGFyIGVtIHF1YWxxdWVyIGxhZG88L3RleHQ+Cjwvc3ZnPgo=",HF=""+new URL("mvc-klick-DKonBpbs.svg",import.meta.url).href,VF=""+new URL("schiffe-versenken-iEBDHIxD.svg",import.meta.url).href,qF=""+new URL("ss-binaere-suche-DVVS2SNc.svg",import.meta.url).href,UF=""+new URL("ss-bubblesort-AFDcFDah.svg",import.meta.url).href,QF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDQgMTk2IiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KPHJlY3Qgd2lkdGg9IjQwNCIgaGVpZ2h0PSIxOTYiIGZpbGw9IiNmZmZmZmYiLz4KPHRleHQgeD0iMTQiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIiBmb250LXNpemU9IjE1Ij5Qcm9jdXJhLXNlIG8gOS4gQXMgY2FydGFzIGVzdMOjbyBiYXJhbGhhZGFzLjwvdGV4dD4KPGc+PHJlY3QgeD0iMTQiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHg9IjM0LjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNkYzI2MjYiPjc8L3RleHQ+PHRleHQgeD0iMzQuMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNkYzI2MjYiPuKZpTwvdGV4dD48L2c+Cjx0ZXh0IHg9IjM0LjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iI2RjMjYyNiI+4pyXPC90ZXh0Pgo8dGV4dCB4PSIzNC4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjEuPC90ZXh0Pgo8Zz48cmVjdCB4PSI2MiIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iODIuMCIgeT0iNzUuMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+MzwvdGV4dD48dGV4dCB4PSI4Mi4wIiB5PSI5NS45MTk5OTk5OTk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzFjMWMxZSI+4pmgPC90ZXh0PjwvZz4KPHRleHQgeD0iODIuMCIgeT0iMTMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjZGMyNjI2Ij7inJc8L3RleHQ+Cjx0ZXh0IHg9IjgyLjAiIHk9IjE0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+Mi48L3RleHQ+CjxnPjxyZWN0IHg9IjExMCIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iMTMwLjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNkYzI2MjYiPjEwPC90ZXh0Pjx0ZXh0IHg9IjEzMC4wIiB5PSI5NS45MTk5OTk5OTk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iI2RjMjYyNiI+4pmmPC90ZXh0PjwvZz4KPHRleHQgeD0iMTMwLjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iI2RjMjYyNiI+4pyXPC90ZXh0Pgo8dGV4dCB4PSIxMzAuMCIgeT0iMTQ4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj4zLjwvdGV4dD4KPGc+PHJlY3QgeD0iMTU4IiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSIxNzguMCIgeT0iNzUuMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+NTwvdGV4dD48dGV4dCB4PSIxNzguMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiMxYzFjMWUiPuKZozwvdGV4dD48L2c+Cjx0ZXh0IHg9IjE3OC4wIiB5PSIxMzAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiNkYzI2MjYiPuKclzwvdGV4dD4KPHRleHQgeD0iMTc4LjAiIHk9IjE0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+NC48L3RleHQ+CjxnPjxyZWN0IHg9IjIwNiIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIzIi8+PHRleHQgeD0iMjI2LjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPjk8L3RleHQ+PHRleHQgeD0iMjI2LjAiIHk9Ijk1LjkxOTk5OTk5OTk5OTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE1IiBmaWxsPSIjMWMxYzFlIj7imaA8L3RleHQ+PC9nPgo8dGV4dCB4PSIyMjYuMCIgeT0iMTMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjMTU4MDNkIj7inJM8L3RleHQ+Cjx0ZXh0IHg9IjIyNi4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjUuPC90ZXh0Pgo8Zz48cmVjdCB4PSIyNTQiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjMWQ0ZWQ4IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjxyZWN0IHg9IjI1OCIgeT0iNTQiIHdpZHRoPSIzMiIgaGVpZ2h0PSI0OCIgcng9IjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzkzYzVmZCIgc3Ryb2tlLXdpZHRoPSIxLjIiIHN0cm9rZS1kYXNoYXJyYXk9IjMgMiIvPjwvZz4KPGc+PHJlY3QgeD0iMzAyIiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iIzFkNGVkOCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48cmVjdCB4PSIzMDYiIHk9IjU0IiB3aWR0aD0iMzIiIGhlaWdodD0iNDgiIHJ4PSIzIiBmaWxsPSJub25lIiBzdHJva2U9IiM5M2M1ZmQiIHN0cm9rZS13aWR0aD0iMS4yIiBzdHJva2UtZGFzaGFycmF5PSIzIDIiLz48L2c+CjxnPjxyZWN0IHg9IjM1MCIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiMxZDRlZDgiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHJlY3QgeD0iMzU0IiB5PSI1NCIgd2lkdGg9IjMyIiBoZWlnaHQ9IjQ4IiByeD0iMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTNjNWZkIiBzdHJva2Utd2lkdGg9IjEuMiIgc3Ryb2tlLWRhc2hhcnJheT0iMyAyIi8+PC9nPgo8dGV4dCB4PSIxNCIgeT0iMTY4IiBmb250LXNpemU9IjE1IiBmaWxsPSIjNmU2ZTczIj5WaXJhciBkYSBlc3F1ZXJkYSBwYXJhIGEgZGlyZWl0YTo8L3RleHQ+PHRleHQgeD0iMTQiIHk9IjE4OCIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzZlNmU3MyI+QXDDs3MgNSBjb21wYXJhw6fDtWVzLCBvIDkgw6kgZW5jb250cmFkby48L3RleHQ+Cjwvc3ZnPgo=",$F=""+new URL("ss-vergleiche-DWgkIvLr.svg",import.meta.url).href,YF="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjAgNDUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KPGRlZnM+PG1hcmtlciBpZD0iayIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSI5IiByZWZZPSI1IiBtYXJrZXJXaWR0aD0iNiIgbWFya2VySGVpZ2h0PSI2IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0wIDAgTDEwIDUgTDAgMTAgWiIgZmlsbD0iIzFjMWMxZSIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9ImIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iOSIgcmVmWT0iNSIgbWFya2VyV2lkdGg9IjYiIG1hcmtlckhlaWdodD0iNiIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNMCAwIEwxMCA1IEwwIDEwIFoiIGZpbGw9IiMxZDRlZDgiLz48L21hcmtlcj48bWFya2VyIGlkPSJvIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI2IiBtYXJrZXJIZWlnaHQ9IjYiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjYjQ1MzA5Ii8+PC9tYXJrZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNDIwIiBoZWlnaHQ9IjQ1MCIgZmlsbD0iI2ZmZmZmZiIvPgo8dGV4dCB4PSIxMCIgeT0iMjIiIGZvbnQtc2l6ZT0iMTUiIGZvbnQtd2VpZ2h0PSI3MDAiPlBhc3NhZ2VtIHBvciB2YWxvcjogdW1hIGPDs3BpYTwvdGV4dD4KPHJlY3QgeD0iMTAiIHk9IjM4IiB3aWR0aD0iMTYwIiBoZWlnaHQ9Ijg2IiByeD0iOCIgZmlsbD0iI2VmZjZmZiIgc3Ryb2tlPSIjMWQ0ZWQ4IiBzdHJva2Utd2lkdGg9IjEuNSIvPgo8dGV4dCB4PSIyMCIgeT0iNTgiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZDRlZDgiPlByb2dyYW1hIHByaW5jaXBhbDwvdGV4dD4KPHRleHQgeD0iMTYiIHk9Ijk4IiBmb250LXNpemU9IjEyIj5udW1lcm88L3RleHQ+CjxyZWN0IHg9IjcwIiB5PSI3OCIgd2lkdGg9IjU2IiBoZWlnaHQ9IjMwIiByeD0iNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+Cjx0ZXh0IHg9Ijk4IiB5PSI5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+NTwvdGV4dD4KPHJlY3QgeD0iMjUwIiB5PSIzOCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSI4NiIgcng9IjgiIGZpbGw9IiNmZmY3ZWQiIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHRleHQgeD0iMjYwIiB5PSI1OCIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2I0NTMwOSI+ZHVwbGljYXI8L3RleHQ+Cjx0ZXh0IHg9IjI2MCIgeT0iOTgiPng8L3RleHQ+CjxyZWN0IHg9IjI4MCIgeT0iNzgiIHdpZHRoPSI4MCIgaGVpZ2h0PSIzMCIgcng9IjQiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFjMWMxZSIvPgo8dGV4dCB4PSIzMjAiIHk9Ijk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIj48dHNwYW4gZmlsbD0iIzZlNmU3MyIgdGV4dC1kZWNvcmF0aW9uPSJsaW5lLXRocm91Z2giPjU8L3RzcGFuPiA8dHNwYW4gZm9udC13ZWlnaHQ9IjcwMCI+MTA8L3RzcGFuPjwvdGV4dD4KPGxpbmUgeDE9IjEyOCIgeTE9IjkzIiB4Mj0iMjQ4IiB5Mj0iOTMiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1kYXNoYXJyYXk9IjUgNCIgbWFya2VyLWVuZD0idXJsKCNrKSIvPgo8dGV4dCB4PSIyMTAiIHk9Ijg0IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5jw7NwaWE8L3RleHQ+Cjx0ZXh0IHg9IjEwIiB5PSIxNTAiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPnggcGFzc2EgYSAxMCwgbWFzIG51bWVybyBmaWNhIDUuPC90ZXh0Pgo8dGV4dCB4PSIxMCIgeT0iMTY4IiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj5Db21vIHVtYSBmb3RvY8OzcGlhOiBlc2NyZXZlciBuYSBjw7NwaWE8L3RleHQ+Cjx0ZXh0IHg9IjEwIiB5PSIxODYiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPm7Do28gYWx0ZXJhIG8gb3JpZ2luYWwuPC90ZXh0Pgo8bGluZSB4MT0iMTAiIHkxPSIyMDgiIHgyPSI0MTAiIHkyPSIyMDgiIHN0cm9rZT0iI2QxZDFkNiIvPgo8dGV4dCB4PSIxMCIgeT0iMjM2IiBmb250LXNpemU9IjE1IiBmb250LXdlaWdodD0iNzAwIj5QYXNzYWdlbSBwb3IgcmVmZXLDqm5jaWE6IG8gbWVzbW8gYXJyYXk8L3RleHQ+CjxyZWN0IHg9IjEwIiB5PSIyNTIiIHdpZHRoPSIxNjAiIGhlaWdodD0iNjIiIHJ4PSI4IiBmaWxsPSIjZWZmNmZmIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjx0ZXh0IHg9IjIwIiB5PSIyNzIiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZDRlZDgiPlByb2dyYW1hIHByaW5jaXBhbDwvdGV4dD4KPHRleHQgeD0iMjAiIHk9IjMwMCI+ZmlsYTwvdGV4dD4KPGNpcmNsZSBjeD0iMTUwIiBjeT0iMjk1IiByPSI1IiBmaWxsPSIjMWQ0ZWQ4Ii8+CjxyZWN0IHg9IjI1MCIgeT0iMjUyIiB3aWR0aD0iMTYwIiBoZWlnaHQ9IjYyIiByeD0iOCIgZmlsbD0iI2ZmZjdlZCIgc3Ryb2tlPSIjYjQ1MzA5IiBzdHJva2Utd2lkdGg9IjEuNSIvPgo8dGV4dCB4PSIyNjAiIHk9IjI3MiIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2I0NTMwOSI+bGltcGFyPC90ZXh0Pgo8dGV4dCB4PSIyNjAiIHk9IjMwMCI+bHVnYXJlczwvdGV4dD4KPGNpcmNsZSBjeD0iMzkwIiBjeT0iMjk1IiByPSI1IiBmaWxsPSIjYjQ1MzA5Ii8+CjxwYXRoIGQ9Ik0xNTAgMzAwIEwxNTAgMzQwIEwxNDAgMzUyIiBmaWxsPSJub25lIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMS41IiBtYXJrZXItZW5kPSJ1cmwoI2IpIi8+CjxwYXRoIGQ9Ik0zOTAgMzAwIEwzOTAgMzQwIEwzMjIgMzU0IiBmaWxsPSJub25lIiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMS41IiBtYXJrZXItZW5kPSJ1cmwoI28pIi8+CjxyZWN0IHg9IjkwIiB5PSIzNTgiIHdpZHRoPSI2MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+CjxyZWN0IHg9IjE1MCIgeT0iMzU4IiB3aWR0aD0iNjAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFjMWMxZSIvPgo8cmVjdCB4PSIyMTAiIHk9IjM1OCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHJlY3QgeD0iMjcwIiB5PSIzNTgiIHdpZHRoPSI2MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+Cjx0ZXh0IHg9IjEyMCIgeT0iMzc5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5mYWxzZTwvdGV4dD4KPHRleHQgeD0iMTgwIiB5PSIzNzkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiPmZhbHNlPC90ZXh0Pgo8dGV4dCB4PSIyNDAiIHk9IjM3OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+ZmFsc2U8L3RleHQ+Cjx0ZXh0IHg9IjMwMCIgeT0iMzc5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5mYWxzZTwvdGV4dD4KPHRleHQgeD0iMTAiIHk9IjQxNiIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+U8OzIGjDoSB1bSBhcnJheS4gTyBxdWUgbGltcGFyIGFsdGVyYSBhdHJhdsOpcyBkZSBsdWdhcmVzLDwvdGV4dD4KPHRleHQgeD0iMTAiIHk9IjQzNCIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+byBwcm9ncmFtYSBwcmluY2lwYWwgdsOqIGVtIGZpbGEuPC90ZXh0Pgo8L3N2Zz4K",KF=""+new URL("zk-zeichen-8efNmpGx.svg",import.meta.url).href,XF=""+new URL("schiffe-versenken-CXt1SF0S.svg",import.meta.url).href,eR=""+new URL("ss-binaere-suche-CPsLGldq.svg",import.meta.url).href,tR=""+new URL("ss-bubblesort-Dic2ZfaT.svg",import.meta.url).href,nR="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDQgMTk2IiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWMxYzFlIj4KPHJlY3Qgd2lkdGg9IjQwNCIgaGVpZ2h0PSIxOTYiIGZpbGw9IiNmZmZmZmYiLz4KPHRleHQgeD0iMTQiIHk9IjI0IiBmb250LXdlaWdodD0iNjAwIiBmb250LXNpemU9IjE1Ij5HZXN1Y2h0OiBkaWUgOS4gRGllIEthcnRlbiBzaW5kIGdlbWlzY2h0LjwvdGV4dD4KPGc+PHJlY3QgeD0iMTQiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjZjJmMmY3IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjx0ZXh0IHg9IjM0LjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNkYzI2MjYiPjc8L3RleHQ+PHRleHQgeD0iMzQuMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiNkYzI2MjYiPuKZpTwvdGV4dD48L2c+Cjx0ZXh0IHg9IjM0LjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iI2RjMjYyNiI+4pyXPC90ZXh0Pgo8dGV4dCB4PSIzNC4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjEuPC90ZXh0Pgo8Zz48cmVjdCB4PSI2MiIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iODIuMCIgeT0iNzUuMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+MzwvdGV4dD48dGV4dCB4PSI4Mi4wIiB5PSI5NS45MTk5OTk5OTk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iIzFjMWMxZSI+4pmgPC90ZXh0PjwvZz4KPHRleHQgeD0iODIuMCIgeT0iMTMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjZGMyNjI2Ij7inJc8L3RleHQ+Cjx0ZXh0IHg9IjgyLjAiIHk9IjE0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+Mi48L3RleHQ+CjxnPjxyZWN0IHg9IjExMCIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNmMmYyZjciIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHRleHQgeD0iMTMwLjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiNkYzI2MjYiPjEwPC90ZXh0Pjx0ZXh0IHg9IjEzMC4wIiB5PSI5NS45MTk5OTk5OTk5OTk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNSIgZmlsbD0iI2RjMjYyNiI+4pmmPC90ZXh0PjwvZz4KPHRleHQgeD0iMTMwLjAiIHk9IjEzMCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxNiIgZm9udC13ZWlnaHQ9IjYwMCIgZmlsbD0iI2RjMjYyNiI+4pyXPC90ZXh0Pgo8dGV4dCB4PSIxMzAuMCIgeT0iMTQ4IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj4zLjwvdGV4dD4KPGc+PHJlY3QgeD0iMTU4IiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iI2YyZjJmNyIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48dGV4dCB4PSIxNzguMCIgeT0iNzUuMiIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxOCIgZm9udC13ZWlnaHQ9IjcwMCIgZmlsbD0iIzFjMWMxZSI+NTwvdGV4dD48dGV4dCB4PSIxNzguMCIgeT0iOTUuOTE5OTk5OTk5OTk5OTkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiMxYzFjMWUiPuKZozwvdGV4dD48L2c+Cjx0ZXh0IHg9IjE3OC4wIiB5PSIxMzAiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTYiIGZvbnQtd2VpZ2h0PSI2MDAiIGZpbGw9IiNkYzI2MjYiPuKclzwvdGV4dD4KPHRleHQgeD0iMTc4LjAiIHk9IjE0OCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+NC48L3RleHQ+CjxnPjxyZWN0IHg9IjIwNiIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiNkY2ZjZTciIHN0cm9rZT0iIzE1ODAzZCIgc3Ryb2tlLXdpZHRoPSIzIi8+PHRleHQgeD0iMjI2LjAiIHk9Ijc1LjIiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTgiIGZvbnQtd2VpZ2h0PSI3MDAiIGZpbGw9IiMxYzFjMWUiPjk8L3RleHQ+PHRleHQgeD0iMjI2LjAiIHk9Ijk1LjkxOTk5OTk5OTk5OTk5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE1IiBmaWxsPSIjMWMxYzFlIj7imaA8L3RleHQ+PC9nPgo8dGV4dCB4PSIyMjYuMCIgeT0iMTMwIiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjE2IiBmb250LXdlaWdodD0iNjAwIiBmaWxsPSIjMTU4MDNkIj7inJM8L3RleHQ+Cjx0ZXh0IHg9IjIyNi4wIiB5PSIxNDgiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPjUuPC90ZXh0Pgo8Zz48cmVjdCB4PSIyNTQiIHk9IjUwIiB3aWR0aD0iNDAiIGhlaWdodD0iNTYiIHJ4PSI1IiBmaWxsPSIjMWQ0ZWQ4IiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMSIvPjxyZWN0IHg9IjI1OCIgeT0iNTQiIHdpZHRoPSIzMiIgaGVpZ2h0PSI0OCIgcng9IjMiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzkzYzVmZCIgc3Ryb2tlLXdpZHRoPSIxLjIiIHN0cm9rZS1kYXNoYXJyYXk9IjMgMiIvPjwvZz4KPGc+PHJlY3QgeD0iMzAyIiB5PSI1MCIgd2lkdGg9IjQwIiBoZWlnaHQ9IjU2IiByeD0iNSIgZmlsbD0iIzFkNGVkOCIgc3Ryb2tlPSIjMWMxYzFlIiBzdHJva2Utd2lkdGg9IjEiLz48cmVjdCB4PSIzMDYiIHk9IjU0IiB3aWR0aD0iMzIiIGhlaWdodD0iNDgiIHJ4PSIzIiBmaWxsPSJub25lIiBzdHJva2U9IiM5M2M1ZmQiIHN0cm9rZS13aWR0aD0iMS4yIiBzdHJva2UtZGFzaGFycmF5PSIzIDIiLz48L2c+CjxnPjxyZWN0IHg9IjM1MCIgeT0iNTAiIHdpZHRoPSI0MCIgaGVpZ2h0PSI1NiIgcng9IjUiIGZpbGw9IiMxZDRlZDgiIHN0cm9rZT0iIzFjMWMxZSIgc3Ryb2tlLXdpZHRoPSIxIi8+PHJlY3QgeD0iMzU0IiB5PSI1NCIgd2lkdGg9IjMyIiBoZWlnaHQ9IjQ4IiByeD0iMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTNjNWZkIiBzdHJva2Utd2lkdGg9IjEuMiIgc3Ryb2tlLWRhc2hhcnJheT0iMyAyIi8+PC9nPgo8dGV4dCB4PSIxNCIgeT0iMTY4IiBmb250LXNpemU9IjE1IiBmaWxsPSIjNmU2ZTczIj5Wb24gbGlua3MgbmFjaCByZWNodHMgdW1kcmVoZW46PC90ZXh0Pjx0ZXh0IHg9IjE0IiB5PSIxODgiIGZvbnQtc2l6ZT0iMTUiIGZpbGw9IiM2ZTZlNzMiPk5hY2ggNSBWZXJnbGVpY2hlbiBpc3QgZGllIDkgZ2VmdW5kZW4uPC90ZXh0Pgo8L3N2Zz4K",iR=""+new URL("ss-vergleiche-DRqzU2TZ.svg",import.meta.url).href,aR="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MjAgNDUwIiBmb250LWZhbWlseT0ic3lzdGVtLXVpLCAtYXBwbGUtc3lzdGVtLCBTZWdvZSBVSSwgUm9ib3RvLCBzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmaWxsPSIjMWMxYzFlIj4KPGRlZnM+PG1hcmtlciBpZD0iayIgdmlld0JveD0iMCAwIDEwIDEwIiByZWZYPSI5IiByZWZZPSI1IiBtYXJrZXJXaWR0aD0iNiIgbWFya2VySGVpZ2h0PSI2IiBvcmllbnQ9ImF1dG8iPjxwYXRoIGQ9Ik0wIDAgTDEwIDUgTDAgMTAgWiIgZmlsbD0iIzFjMWMxZSIvPjwvbWFya2VyPjxtYXJrZXIgaWQ9ImIiIHZpZXdCb3g9IjAgMCAxMCAxMCIgcmVmWD0iOSIgcmVmWT0iNSIgbWFya2VyV2lkdGg9IjYiIG1hcmtlckhlaWdodD0iNiIgb3JpZW50PSJhdXRvIj48cGF0aCBkPSJNMCAwIEwxMCA1IEwwIDEwIFoiIGZpbGw9IiMxZDRlZDgiLz48L21hcmtlcj48bWFya2VyIGlkPSJvIiB2aWV3Qm94PSIwIDAgMTAgMTAiIHJlZlg9IjkiIHJlZlk9IjUiIG1hcmtlcldpZHRoPSI2IiBtYXJrZXJIZWlnaHQ9IjYiIG9yaWVudD0iYXV0byI+PHBhdGggZD0iTTAgMCBMMTAgNSBMMCAxMCBaIiBmaWxsPSIjYjQ1MzA5Ii8+PC9tYXJrZXI+PC9kZWZzPgo8cmVjdCB3aWR0aD0iNDIwIiBoZWlnaHQ9IjQ1MCIgZmlsbD0iI2ZmZmZmZiIvPgo8dGV4dCB4PSIxMCIgeT0iMjIiIGZvbnQtc2l6ZT0iMTUiIGZvbnQtd2VpZ2h0PSI3MDAiPldlcnTDvGJlcmdhYmU6IGVpbmUgS29waWU8L3RleHQ+CjxyZWN0IHg9IjEwIiB5PSIzOCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSI4NiIgcng9IjgiIGZpbGw9IiNlZmY2ZmYiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHRleHQgeD0iMjAiIHk9IjU4IiBmb250LXNpemU9IjEzIiBmaWxsPSIjMWQ0ZWQ4Ij5IYXVwdHByb2dyYW1tPC90ZXh0Pgo8dGV4dCB4PSIyMCIgeT0iOTgiPnphaGw8L3RleHQ+CjxyZWN0IHg9IjcwIiB5PSI3OCIgd2lkdGg9IjU2IiBoZWlnaHQ9IjMwIiByeD0iNCIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+Cjx0ZXh0IHg9Ijk4IiB5PSI5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC13ZWlnaHQ9IjcwMCI+NTwvdGV4dD4KPHJlY3QgeD0iMjUwIiB5PSIzOCIgd2lkdGg9IjE2MCIgaGVpZ2h0PSI4NiIgcng9IjgiIGZpbGw9IiNmZmY3ZWQiIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIxLjUiLz4KPHRleHQgeD0iMjYwIiB5PSI1OCIgZm9udC1zaXplPSIxMyIgZmlsbD0iI2I0NTMwOSI+dmVyZG9wcGxlPC90ZXh0Pgo8dGV4dCB4PSIyNjAiIHk9Ijk4Ij54PC90ZXh0Pgo8cmVjdCB4PSIyODAiIHk9Ijc4IiB3aWR0aD0iODAiIGhlaWdodD0iMzAiIHJ4PSI0IiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHRleHQgeD0iMzIwIiB5PSI5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSI+PHRzcGFuIGZpbGw9IiM2ZTZlNzMiIHRleHQtZGVjb3JhdGlvbj0ibGluZS10aHJvdWdoIj41PC90c3Bhbj4gPHRzcGFuIGZvbnQtd2VpZ2h0PSI3MDAiPjEwPC90c3Bhbj48L3RleHQ+CjxsaW5lIHgxPSIxMjgiIHkxPSI5MyIgeDI9IjI0OCIgeTI9IjkzIiBzdHJva2U9IiMxYzFjMWUiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtZGFzaGFycmF5PSI1IDQiIG1hcmtlci1lbmQ9InVybCgjaykiLz4KPHRleHQgeD0iMjEwIiB5PSI4NCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+S29waWU8L3RleHQ+Cjx0ZXh0IHg9IjEwIiB5PSIxNTAiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiM2ZTZlNzMiPnggd2lyZCAxMCwgYWJlciB6YWhsIGJsZWlidCA1LjwvdGV4dD4KPHRleHQgeD0iMTAiIHk9IjE2OCIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+V2llIGJlaSBlaW5lciBGb3Rva29waWU6IFdlciBhdWYgZGVyIEtvcGllIHNjaHJlaWJ0LDwvdGV4dD4KPHRleHQgeD0iMTAiIHk9IjE4NiIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+w6RuZGVydCBkYXMgT3JpZ2luYWwgbmljaHQuPC90ZXh0Pgo8bGluZSB4MT0iMTAiIHkxPSIyMDgiIHgyPSI0MTAiIHkyPSIyMDgiIHN0cm9rZT0iI2QxZDFkNiIvPgo8dGV4dCB4PSIxMCIgeT0iMjM2IiBmb250LXNpemU9IjE1IiBmb250LXdlaWdodD0iNzAwIj5SZWZlcmVuesO8YmVyZ2FiZTogZGFzc2VsYmUgQXJyYXk8L3RleHQ+CjxyZWN0IHg9IjEwIiB5PSIyNTIiIHdpZHRoPSIxNjAiIGhlaWdodD0iNjIiIHJ4PSI4IiBmaWxsPSIjZWZmNmZmIiBzdHJva2U9IiMxZDRlZDgiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjx0ZXh0IHg9IjIwIiB5PSIyNzIiIGZvbnQtc2l6ZT0iMTMiIGZpbGw9IiMxZDRlZDgiPkhhdXB0cHJvZ3JhbW08L3RleHQ+Cjx0ZXh0IHg9IjIwIiB5PSIzMDAiPnJlaWhlPC90ZXh0Pgo8Y2lyY2xlIGN4PSIxNTAiIGN5PSIyOTUiIHI9IjUiIGZpbGw9IiMxZDRlZDgiLz4KPHJlY3QgeD0iMjUwIiB5PSIyNTIiIHdpZHRoPSIxNjAiIGhlaWdodD0iNjIiIHJ4PSI4IiBmaWxsPSIjZmZmN2VkIiBzdHJva2U9IiNiNDUzMDkiIHN0cm9rZS13aWR0aD0iMS41Ii8+Cjx0ZXh0IHg9IjI2MCIgeT0iMjcyIiBmb250LXNpemU9IjEzIiBmaWxsPSIjYjQ1MzA5Ij5sZWVyZW48L3RleHQ+Cjx0ZXh0IHg9IjI2MCIgeT0iMzAwIj5wbGFldHplPC90ZXh0Pgo8Y2lyY2xlIGN4PSIzOTAiIGN5PSIyOTUiIHI9IjUiIGZpbGw9IiNiNDUzMDkiLz4KPHBhdGggZD0iTTE1MCAzMDAgTDE1MCAzNDAgTDE0MCAzNTIiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzFkNGVkOCIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjYikiLz4KPHBhdGggZD0iTTM5MCAzMDAgTDM5MCAzNDAgTDMyMiAzNTQiIGZpbGw9Im5vbmUiIHN0cm9rZT0iI2I0NTMwOSIgc3Ryb2tlLXdpZHRoPSIxLjUiIG1hcmtlci1lbmQ9InVybCgjbykiLz4KPHJlY3QgeD0iOTAiIHk9IjM1OCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHJlY3QgeD0iMTUwIiB5PSIzNTgiIHdpZHRoPSI2MCIgaGVpZ2h0PSIzMiIgZmlsbD0iI2ZmZmZmZiIgc3Ryb2tlPSIjMWMxYzFlIi8+CjxyZWN0IHg9IjIxMCIgeT0iMzU4IiB3aWR0aD0iNjAiIGhlaWdodD0iMzIiIGZpbGw9IiNmZmZmZmYiIHN0cm9rZT0iIzFjMWMxZSIvPgo8cmVjdCB4PSIyNzAiIHk9IjM1OCIgd2lkdGg9IjYwIiBoZWlnaHQ9IjMyIiBmaWxsPSIjZmZmZmZmIiBzdHJva2U9IiMxYzFjMWUiLz4KPHRleHQgeD0iMTIwIiB5PSIzNzkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiPmZhbHNlPC90ZXh0Pgo8dGV4dCB4PSIxODAiIHk9IjM3OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZm9udC1zaXplPSIxMyI+ZmFsc2U8L3RleHQ+Cjx0ZXh0IHg9IjI0MCIgeT0iMzc5IiB0ZXh0LWFuY2hvcj0ibWlkZGxlIiBmb250LXNpemU9IjEzIj5mYWxzZTwvdGV4dD4KPHRleHQgeD0iMzAwIiB5PSIzNzkiIHRleHQtYW5jaG9yPSJtaWRkbGUiIGZvbnQtc2l6ZT0iMTMiPmZhbHNlPC90ZXh0Pgo8dGV4dCB4PSIxMCIgeT0iNDE2IiBmb250LXNpemU9IjEzIiBmaWxsPSIjNmU2ZTczIj5FcyBnaWJ0IG51ciBlaW4gQXJyYXkuIFdhcyBsZWVyZW4gw7xiZXIgcGxhZXR6ZSDDpG5kZXJ0LDwvdGV4dD4KPHRleHQgeD0iMTAiIHk9IjQzNCIgZm9udC1zaXplPSIxMyIgZmlsbD0iIzZlNmU3MyI+c2llaHQgZGFzIEhhdXB0cHJvZ3JhbW0gdW50ZXIgcmVpaGUuPC90ZXh0Pgo8L3N2Zz4K",sR=""+new URL("zk-zeichen-vYvc3iJg.svg",import.meta.url).href;function rR(i){const e=[];for(const t of i.replace(/\r\n/g,` -`).split(` -`)){const n=/^###\s+(.+)$/.exec(t),a=e.at(-1);if(n){const s=n[1].trim();e.push({term:s,forms:[s],text:""})}else a&&/^(auch|also):/i.test(t)?a.forms.push(...t.replace(/^(auch|also):/i,"").split(",").map(s=>s.trim()).filter(Boolean)):a&&t.trim()!==""&&(a.text=(a.text+" "+t.trim()).trim())}return e.filter(t=>t.text!=="")}const oR=i=>i.replace(/[.*+?^${}()|[\]\\]/g,"\\$&");function Lm(i){const e=i.flatMap(t=>t.forms).sort((t,n)=>n.length-t.length);if(e.length!==0)return new RegExp(`(?t.forms.includes(e))}class yl extends Error{}function lR(i){return i==="zuhoerer"||i==="vertrag"||i==="layout"||i==="bombe"||i==="frage"||i==="reihenfolge"}function uR(i,e,t){const n=h=>{throw new yl(`${t}: ${h}`)};let a="",s="",r="",o=[],l,u;const m=[];let c,d=!1;for(const h of e){const g=h.trim();if(c&&d&&(/^ {4}|^\t/.test(h)||g==="")){c.code+=(c.code?` -`:"")+h.replace(/^( {4}|\t)/,"");continue}if(d=!1,!g)continue;const f=/^(titel|intro|antworten|frage|runde|klicks|richtig|zeit|fehler|besser|daten|stelle):\s*(.*)$/.exec(g);if(f){const[,v,w]=f;v==="runde"?(c={code:"",question:r,options:[],explanation:"",clicks:[]},m.push(c),d=!0):c?v==="frage"?c.question=w:v==="fehler"?c.wrongLine=Number(w):v==="besser"?c.fix=w:v==="stelle"?c.wrongPart=w.trim():v==="klicks"?c.clicks=w.split(",").map(z=>z.trim()).filter(Boolean):v==="richtig"?c.rightText=w:n(`„${v}:“ gehört vor die erste Runde.`):v==="titel"?a=w:v==="intro"?s=w:v==="antworten"?o=w.split("|").map(z=>z.trim()):v==="frage"?r=w:v==="zeit"?l=Number(w):v==="daten"?u=w==="csv"?w:n("„daten:“ kennt nur „csv“."):n(`„${v}:“ gehört in eine Runde.`);continue}c||n(`Unbekannte Zeile vor der ersten Runde: „${g}“`);const b=c,x=/^-\s+(\[x\]\s+)?(.+)$/.exec(g);x?b.options.push({text:x[2].trim().replaceAll("\\n",` -`),right:!!x[1]}):g.startsWith(">")?b.explanation+=(b.explanation?" ":"")+g.slice(1).trim():n(`Unbekannte Zeile im Spiel: „${g}“`)}return a||n("Das Spiel braucht eine Zeile „titel:“."),m.length||n("Das Spiel braucht mindestens eine „runde:“."),m.forEach((h,g)=>{if(h.code=h.code.replace(/\n+$/,""),i==="bombe"){const f=h.code.split(` -`),b=h.wrongLine;if((!b||!Number.isInteger(b)||b<1||b>f.length||!f[b-1].trim())&&n(`Runde ${g+1}: „fehler:“ muss eine Zeile des Codes sein.`),h.fix||n(`Runde ${g+1}: „besser:“ fehlt.`),h.options.length&&n(`Runde ${g+1}: Bei „bombe“ gibt es keine Antworten, man tippt auf die Zeile.`),h.question||n(`Runde ${g+1}: keine Frage.`),h.wrongPart!==void 0){u&&n(`Runde ${g+1}: „stelle:“ geht nur bei Java, nicht bei CSV.`);const x=xp(f[b-1]).filter(v=>v.part).map(v=>v.text);x.filter(v=>v===h.wrongPart).length!==1&&n(`Runde ${g+1}: „stelle: ${h.wrongPart}“ ist kein Teil der Zeile ${b}. Teile: ${x.map(v=>`„${v}“`).join(", ")}`)}return}if(i==="reihenfolge"){h.options.length<3&&n(`Runde ${g+1}: „reihenfolge“ braucht mindestens drei Schritte.`),h.options.some(f=>f.right)&&n(`Runde ${g+1}: Bei „reihenfolge“ gibt es kein [x], die Schritte stehen der Reihe nach da.`),new Set(h.options.map(f=>f.text)).size!==h.options.length&&n(`Runde ${g+1}: zwei Schritte sind gleich.`),h.question||n(`Runde ${g+1}: keine Frage.`);return}if(h.options.length||(o.length||n(`Runde ${g+1}: keine Antworten.`),(!h.rightText||!o.includes(h.rightText))&&n(`Runde ${g+1}: „richtig:“ muss eine der Antworten sein.`),h.options=o.map(f=>({text:f,right:f===h.rightText}))),h.options.filter(f=>f.right).length!==1&&n(`Runde ${g+1}: genau eine Antwort muss richtig sein.`),h.question||n(`Runde ${g+1}: keine Frage.`),i==="layout"&&h.options.forEach(f=>vp(f.text,`Runde ${g+1}`,n)),i==="zuhoerer")try{const f=wp(h.code);for(const b of h.clicks)f.buttons.some(x=>x.caption===b)||n(`Runde ${g+1}: Knopf „${b}“ gibt es nicht.`);dR(f,h.clicks)}catch(f){if(f instanceof yl)throw f;n(`Runde ${g+1}: ${f.message}`)}delete h.rightText}),i==="bombe"&&!(l&&l>0)&&n("„bombe“ braucht „zeit:“ mit den Sekunden je Runde."),{kind:i,title:a,intro:s,...i==="bombe"?{seconds:l,...u?{data:u}:{}}:{},rounds:m}}function Da(i,e){const t=[];let n=0,a="";for(let s=0;so&&/\s/.test(i[l-1]);)l--;l>o&&a.push([e+o,e+l]),s=r+n}return a}const mR=(i,e)=>i[e]==="+"&&i[e-1]!=="+"&&i[e+1]!=="+"&&i[e+1]!=="=",cR=(i,e)=>i[e]==="="&&!"=<>!+-*/%".includes(i[e-1]??"")&&i[e+1]!=="=";function xp(i){const e=/^\s*/.exec(i)[0],t=i.slice(e.length).replace(/\s+$/,""),n=(()=>{const o=/^(?:\}\s*)?(?:else\s+)?(for|if|while)\s*\(/.exec(t);if(o){const g=o[0].length-1,f=Em(t,g);if(f<0)return[];const b=t.slice(g+1,f);if(o[1]!=="for")return I1(b,g+1,[],0);const x=Da(b,(v,w)=>v[w]===";");return x.length===2?I1(b,g+1,x,1):[]}if(!t.endsWith(";"))return[];const l=t.slice(0,-1),u=Da(l,cR);if(u.length===1)return I1(l,0,u,1);if(u.length)return[];const m=l.indexOf("(");if(m<=0||Em(l,m)!==l.length-1||/^(new|return)\b/.test(l))return[];const c=I1(l.slice(0,m),0,[],0),d=l.slice(m+1,-1),h=I1(d,m+1,Da(d,(g,f)=>g[f]===","),1).flatMap(([g,f])=>{const b=l.slice(g,f);return I1(b,g,Da(b,mR),1)});return[...c,...h]})(),a=n.length?n:t?[[0,t.length]]:[],s=e?[{text:e,part:!1}]:[];let r=0;for(const[o,l]of a)o>r&&s.push({text:t.slice(r,o),part:!1}),s.push({text:t.slice(o,l),part:!0}),r=l;return r{throw new yl(n)}){const n=/^(grid|flow|border):\s*(.+)$/.exec(i);if(!n)return t(`${e}: Antwort „${i}“ ist kein Fensterbild (grid:, flow: oder border:).`);if(n[1]==="border"){const s={};for(const r of n[2].split(/\s+/)){const o=/^([NSWEC])=(.+)$/.exec(r);o||t(`${e}: „${r}“ im BorderLayout-Bild verstehe ich nicht (z. B. N=A).`),s[o[1]]=o[2]}return{kind:"border",areas:s}}const a=n[2].split("/").map(s=>s.trim().split(/\s+/).filter(Boolean));return{kind:n[1],rows:a}}const gs='"((?:[^"\\\\]|\\\\.)*)"';function wp(i){const e=i.split(` -`),t={title:"",buttons:[],labels:[],ints:{},listeners:[]},n=new Map,a=(r,o,l)=>{if(o.startsWith("{")){let m=r+1;const c=[];for(;m=e.length)throw new Error(`Dem Zuhörer fehlt „${l}“.`);return{from:r,to:m,statements:zl(c.join(" "))}}const u=l==="});"?o.replace(/\)\s*;\s*$/,""):o.replace(/;\s*$/,"");return{from:r,to:r,statements:zl(u)}},s=r=>{if(!t.buttons.some(o=>o.name===r))throw new Error(`Knopf ${r} ist nicht angelegt.`);return r};for(let r=0;r\s*(.*)$/.exec(o)){const u=a(r,l[2],"};");n.set(l[1],u),r=u.to}else if(l=/^(\w+)\.addActionListener\(\s*(\w+)\s*\)\s*;$/.exec(o)){const u=n.get(l[2]);if(!u)throw new Error(`Zuhörer ${l[2]} ist nicht angelegt.`);t.listeners.push({button:s(l[1]),...u})}else if(l=/^(\w+)\.addActionListener\(\s*\w+\s*->\s*(.*)$/.exec(o)){const u=a(r,l[2],"});");t.listeners.push({button:s(l[1]),...u}),r=u.to}}return t}function zl(i){const e=[];let t=0;const n=()=>{for(;t{let u=0,m=!1;const c=t+1;for(;t{let o=0,l=!1;const u=t;for(;t{if(n(),i[t]==="{")return zl(a("{","}"));if(/^if\s*\(/.test(i.slice(t))){t=i.indexOf("(",t);const l=a("(",")").trim(),u=r();n();let m=[];return/^else\b/.test(i.slice(t))&&(t+=4,m=r()),[{condition:l,then:u,otherwise:m}]}const o=s();return o?[o]:[]};for(n();ta.caption===t)??i.buttons.find(a=>e.texts[a.name]===t);if(!n)throw new Error(`Knopf „${t}“ gibt es nicht.`);return Ip(i,e,n.name)}function Ip(i,e,t){const n=i.listeners.find(r=>r.button===t),a=[],s={source:t,command:e.texts[t]};for(const r of(n==null?void 0:n.statements)??[])kp(r,e,a,s);return{button:t,listener:n,texts:{...e.texts},console:a}}function dR(i,e){const t=yp(i);return e.map(n=>zp(i,t,n))}function kp(i,e,t,n){if(typeof i!="string"){for(const s of hR(i.condition,e,n)?i.then:i.otherwise)kp(s,e,t,n);return}let a;if(a=/^(\w+)\.setText\((.*)\)$/.exec(i)){if(!(a[1]in e.texts))throw new Error(`${a[1]} ist keine Beschriftung und kein Knopf.`);e.texts[a[1]]=String(n1(a[2],e,n))}else if(a=/^System\.out\.println\((.*)\)$/.exec(i))t.push(String(n1(a[1],e,n)));else if(a=/^(\w+)\s*(\+\+|--)$/.exec(i))e.ints[Nm(a[1],e)]+=a[2]==="++"?1:-1;else if(a=/^(\w+)\s*([+-]?=)\s*(.+)$/.exec(i)){const s=Nm(a[1],e),r=Number(n1(a[3],e,n));e.ints[s]=a[2]==="="?r:a[2]==="+="?e.ints[s]+r:e.ints[s]-r}else throw new Error(`Diese Anweisung kann das Spiel nicht nachspielen: „${i}“`)}function hR(i,e,t){let n;if(n=/^e\.getSource\(\)\s*(==|!=)\s*(\w+)$/.exec(i)){if(!(n[2]in e.texts))throw new Error(`${n[2]} ist kein Knopf.`);return t.source===n[2]==(n[1]==="==")}if(n=/^(!?)(.+)\.equals\((.+)\)$/.exec(i))return String(n1(n[2],e,t))===String(n1(n[3],e,t))!=(n[1]==="!");if(n=/^(.+?)\s*(==|!=|<=|>=|<|>)\s*(.+)$/.exec(i)){const a=Number(n1(n[1],e,t)),s=Number(n1(n[3],e,t));return{"==":a===s,"!=":a!==s,"<=":a<=s,">=":a>=s,"<":a":a>s}[n[2]]}throw new Error(`Diese Bedingung kann das Spiel nicht nachspielen: „${i}“`)}function Nm(i,e){if(!(i in e.ints))throw new Error(`Variable ${i} ist nicht angelegt.`);return i}function n1(i,e,t){const n=i.match(new RegExp(`${gs}|\\d+|e\\.getActionCommand\\(\\)|\\w+(?:\\.getText\\(\\))?|[-+*/()]`,"g"))??[];let a=0;const s=()=>{const l=n[a++];if(l===void 0)throw new Error(`Unvollständiger Ausdruck „${i}“`);if(l==="("){const u=o();return a++,u}if(l.startsWith('"'))return JSON.parse(l);if(/^\d+$/.test(l))return Number(l);if(l==="e.getActionCommand()"){if(!t)throw new Error("e.getActionCommand() gibt es nur im Zuhörer.");return t.command}if(l.endsWith(".getText()"))return e.texts[l.slice(0,-10)]??"";if(l in e.ints)return e.ints[l];throw new Error(`Unbekannt: „${l}“`)},r=()=>{let l=s();for(;n[a]==="*"||n[a]==="/";){const u=n[a++],m=Number(s());l=u==="*"?Number(l)*m:Math.trunc(Number(l)/m)}return l},o=()=>{let l=r();for(;n[a]==="+"||n[a]==="-";){const u=n[a++],m=r();u==="+"?l=typeof l=="string"||typeof m=="string"?`${l}${m}`:l+m:l=Number(l)-Number(m)}return l};return o()}class Sp extends Error{}const Vs="___";function gR(i){const e=Array.from({length:i.rows},()=>Array(i.cols).fill(""));for(const t of i.words)[...t.word].forEach((n,a)=>e[t.row+(t.across?0:a)][t.col+(t.across?a:0)]=n);return e}function pR(i,e){const t=k=>{throw new Sp(`${e}: ${k}`)},n=(k,C)=>{const P=/^(\d+)\s+(\d+)$/.exec(k.trim());return!P||Number(P[1])<1||Number(P[2])<1?t(`${C}: „${k}“ ist kein Feld (Zeile Spalte, ab 1).`):{row:Number(P[1])-1,col:Number(P[2])-1}};let a="",s="",r="ABCDEFGHIJKLMNOPQRSTUVWXYZ",o="",l="",u="",m="";const c=[],d=[];let h=[],g,f="start";for(const k of i){const C=k.trim();if(g&&(/^( {4}|\t)/.test(k)||C==="")){const D=k.replace(/^( {4}|\t)/,"");if(g==="station"){const B=c[c.length-1];B.code+=(B.code?` -`:"")+D}else u+=(u?` -`:"")+D;continue}if(g=void 0,!C)continue;const P=/^(titel|intro|alphabet|station|frage|zahl|feld|tipp|kreuzwort|lösung|loesung|tor|ende):\s*(.*)$/.exec(C);if(P){const[,D,B]=P;if(D==="station")c.push({name:B,code:"",question:"",hint:"",explanation:""}),f="station",g="station";else if(D==="kreuzwort")o=B,f="crossword";else if(D==="tor")l=B,f="gate",g="gate";else if(D==="titel")a=B;else if(D==="intro")s=B;else if(D==="alphabet")r=B;else if(D==="ende")m=B;else if(D==="lösung"||D==="loesung")f!=="crossword"&&t("„lösung:“ gehört zum Kreuzworträtsel."),h=B.split(",").map(j=>n(j,"lösung"));else{f!=="station"&&t(`„${D}:“ gehört in eine Station.`);const j=c[c.length-1];D==="frage"?j.question=B:D==="tipp"?j.hint=B:D==="zahl"?j.answer=/^-?\d+$/.test(B)?Number(B):t(`Station „${j.name}“: „zahl:“ muss eine ganze Zahl sein.`):Object.assign(j,n(B,`Station „${j.name}“`))}continue}if(f==="crossword"){d.push(Tp(C,t));continue}if(f==="station"&&C.startsWith(">")){const D=c[c.length-1];D.explanation+=(D.explanation?" ":"")+C.slice(1).trim();continue}t(`Unbekannte Zeile im Spiel: „${C}“`)}a||t("Das Spiel braucht eine Zeile „titel:“."),c.length||t("Das Spiel braucht mindestens eine „station:“."),d.length||t("Das Spiel braucht ein „kreuzwort:“ mit Wörtern."),h.length||t("Das Kreuzworträtsel braucht eine Zeile „lösung:“."),u.includes(Vs)||t(`Der Code bei „tor:“ braucht ${Vs} für das Eingabefeld.`),new Set(r).size!==r.length&&t("Im Alphabet kommt ein Zeichen doppelt vor.");const{rows:b,cols:x,words:v,keyword:w,grid:z}=_p(d,h,t),I=(k,C)=>{var P;return((P=z[k])==null?void 0:P[C])??""},T=c.map(k=>{k.code||t(`Station „${k.name}“: Code fehlt.`),k.question||t(`Station „${k.name}“: „frage:“ fehlt.`),k.answer===void 0&&t(`Station „${k.name}“: „zahl:“ fehlt.`),k.row===void 0&&t(`Station „${k.name}“: „feld:“ fehlt.`);const C=r.charAt(k.answer);C||t(`Station „${k.name}“: alphabet.charAt(${k.answer}) gibt es nicht.`);const P=I(k.row,k.col);return P!==C&&t(`Station „${k.name}“: alphabet.charAt(${k.answer}) ist ${C}, im Feld ${k.row+1} ${k.col+1} steht aber „${P}“.`),k.code=k.code.replace(/\n+$/,""),k});return{title:a,intro:s,alphabet:r,stations:T,crosswordText:o,rows:b,cols:x,words:v,solution:h,keyword:w,gateText:l,gateCode:u.replace(/\n+$/,""),end:m}}const fR=/^-\s+([→↓])\s+(\d+)\s+(\d+)\s+([A-ZÄÖÜ]+):\s*(.+)$/u;function Tp(i,e){const t=fR.exec(i);return t||e(`Kreuzworträtsel: „${i}“ verstehe ich nicht (z. B. „- → 1 3 STRING: Hinweis“).`),{across:t[1]==="→",row:Number(t[2])-1,col:Number(t[3])-1,word:t[4],clue:t[5]}}function _p(i,e,t){const n=Math.max(...i.map(m=>m.row+(m.across?1:m.word.length))),a=Math.max(...i.map(m=>m.col+(m.across?m.word.length:1))),s=Array.from({length:n},()=>Array(a).fill(""));for(const m of i)[...m.word].forEach((c,d)=>{const h=m.row+(m.across?0:d),g=m.col+(m.across?d:0);s[h][g]&&s[h][g]!==c&&t(`Kreuzworträtsel: ${m.word} passt in Zeile ${h+1}, Spalte ${g+1} nicht (dort steht ${s[h][g]}).`),s[h][g]=c});const r=[...new Set(i.map(m=>m.row*a+m.col))].sort((m,c)=>m-c),o=i.map(m=>({...m,number:r.indexOf(m.row*a+m.col)+1})),l=(m,c)=>{var d;return((d=s[m])==null?void 0:d[c])??""},u=e.map(({row:m,col:c})=>l(m,c)||t(`lösung: In Zeile ${m+1}, Spalte ${c+1} ist kein Feld.`)).join("");return{rows:n,cols:a,words:o,keyword:u,grid:s}}function bR(i,e){const t=d=>{throw new Sp(`${e}: ${d}`)};let n="",a="",s="",r=[];const o=[];for(const d of i){const h=d.trim();if(!h)continue;const g=/^(titel|intro|lösung|loesung|ende):\s*(.*)$/.exec(h);if(g){const[,f,b]=g;f==="titel"?n=b:f==="intro"?a=b:f==="ende"?s=b:r=b.split(",").map(x=>{const v=/^(\d+)\s+(\d+)$/.exec(x.trim());return!v||Number(v[1])<1||Number(v[2])<1?t(`lösung: „${x.trim()}“ ist kein Feld (Zeile Spalte, ab 1).`):{row:Number(v[1])-1,col:Number(v[2])-1}});continue}h.startsWith("-")?o.push(Tp(h,t)):t(`Unbekannte Zeile im Spiel: „${h}“`)}n||t("Das Spiel braucht eine Zeile „titel:“."),o.length<2&&t("Das Rätsel braucht mindestens zwei Wörter."),r.length||t("Das Rätsel braucht eine Zeile „lösung:“.");const{rows:l,cols:u,words:m,keyword:c}=_p(o,r,t);return{title:n,intro:a,rows:l,cols:u,words:m,solution:r,keyword:c,end:s}}const xR=5e3;function Ap(i,e,t,n={}){const a=new Wi(i,e),s=new Js(t),r=m=>({pose:{...s.pose},trail:s.trail.map(c=>({...c})),leds:[...s.leds],screen:s.screen,tone:m}),o=[r()];let l=0;const u=(m,c)=>({frames:o,stop:m,notice:c,bumps:s.bumps,distance:Math.round(s.trail.slice(1).reduce((d,h,g)=>d+Math.hypot(h.x-s.trail[g].x,h.y-s.trail[g].y),0))});for(let m=0;mh!==0);s.tick(G0),d&&o.push(r())}return u("limit")}class vR extends Error{}function wR(i,e){const t=m=>{throw new vR(`${e}: ${m}`)};let n="",a="",s="",r=30;const o=[];let l,u=!1;for(const m of i){const c=m.trim();if(l&&u&&(/^ {4}|^\t/.test(m)||c==="")){l.code+=(l.code?` -`:"")+m.replace(/^( {4}|\t)/,"");continue}if(u=!1,!c)continue;const d=/^(titel|intro|frage|feld|runde|karte|ziel):\s*(.*)$/.exec(c);if(d){const[,h,g]=d;if(h==="runde")l={code:"",map:[],question:s,explanation:""},o.push(l),u=!0;else if(!l)h==="titel"?n=g:h==="intro"?a=g:h==="frage"?s=g:h==="feld"?r=[10,20,30].includes(Number(g))?Number(g):t("„feld:“ ist 10, 20 oder 30 (Zentimeter)."):t(`„${h}:“ gehört in eine Runde.`);else if(h==="frage")l.question=g;else if(h==="karte")l.map=g.split("|").map(f=>f.trim());else if(h==="ziel"){const f=/^(\d+)\s*,\s*(\d+)$/.exec(g);f||t(`„ziel: ${g}“ muss Spalte,Zeile sein, zum Beispiel „ziel: 2,0“.`),l.target={col:Number(f[1]),row:Number(f[2])}}else t(`„${h}:“ gehört vor die erste Runde.`);continue}l&&c.startsWith(">")?l.explanation+=(l.explanation?" ":"")+c.slice(1).trim():t(`Unbekannte Zeile im Spiel: „${c}“`)}return n||t("Das Spiel braucht eine Zeile „titel:“."),o.length||t("Das Spiel braucht mindestens eine „runde:“."),o.forEach((m,c)=>{m.code=m.code.replace(/\n+$/,""),m.code.trim()||t(`Runde ${c+1}: Code fehlt.`),(!m.map.length||m.map.some(d=>d.length!==m.map[0].length))&&t(`Runde ${c+1}: „karte:“ fehlt oder die Zeilen sind verschieden lang.`),m.map.join("").replace(/[^\^> v <).`),/[^.#=rgby^>=m.map.length||m.target.col>=m.map[0].length)&&t(`Runde ${c+1}: „ziel:“ liegt außerhalb der Karte.`),m.question||t(`Runde ${c+1}: keine Frage.`)}),{title:n,intro:a,field:r,rounds:o}}function jp(i,e){return Xg([`raster: ${e/10}`,...i.map].join(` -`))}function yR(i,e,t){const n=e.map[0].length*t,a=e.map.length*t,s=Math.floor((i.x+n/2)/t),r=Math.floor((a/2-i.y)/t);if(!(s<0||r<0||s>=e.map[0].length||r>=e.map.length))return{col:s,row:r}}const zR={condition:"Bedingung",selector:"Wert",code:"Dein Code",start:"Startwert",step:"Schritt",end:"Ende"};function IR(i,e){const t=Ji(i.code,zR),n=t.problems.map(o=>typeof o=="string"?o:JSON.stringify(o)),a=Gi({caption:"",elements:[]},t),s=Ap(a,void 0,jp(i,e)),r=s.frames[s.frames.length-1].pose;return{run:s,end:r,cell:yR(r,i,e),problems:n}}const kR=/\[\[(.+?)\]\]/g;function SR(i,e){const t={title:"",task:"",blocks:[]},n=[];let a=!1;for(const s of i){if(/^\s*```/.test(s)){a||n.push([]),a=!a;continue}if(a){n[n.length-1].push(s.replace(/\s+$/,""));continue}const r=s.trim();if(!r)continue;const o=/^([a-zäöü]+):\s*(.*)$/i.exec(r);if(!o)throw new Error(`${e}: Unbekannte Zeile im Lückentext: „${r}“.`);const l=o[2].trim();switch(o[1].toLowerCase()){case"titel":t.title=l;break;case"auftrag":t.task=l;break;case"erklärung":case"erklaerung":t.explanation=l;break;default:throw new Error(`${e}: Unbekannter Schlüssel „${o[1]}“ im Lückentext.`)}}if(a)throw new Error(`${e}: Der Codeblock im Lückentext wird nicht mit \`\`\` geschlossen.`);if(!t.title)throw new Error(`${e}: Ein Lückentext braucht einen Titel.`);if(!n.some(s=>s.some(r=>r.trim())))throw new Error(`${e}: Lückentext „${t.title}“ braucht einen Codeblock.`);if(t.blocks=n.map(s=>{const r=Math.min(...s.filter(o=>o.trim()).map(o=>/^ */.exec(o)[0].length));return s.map(o=>TR(o.slice(r),e,t.title))}),!_R(t).length)throw new Error(`${e}: Lückentext „${t.title}“ braucht mindestens eine Lücke [[…]].`);return t}function TR(i,e,t){const n=[];let a=0;for(const s of i.matchAll(kR)){s.index>a&&n.push(i.slice(a,s.index));const r=s[1].split(/\s+\|\s+/).map(o=>o.trim()).filter(Boolean);if(!r.length)throw new Error(`${e}: Leere Lücke im Lückentext „${t}“.`);n.push({answers:r}),a=s.index+s[0].length}if(atypeof s=="string"&&(s.includes("[[")||s.includes("]]"))))throw new Error(`${e}: Unvollständige Lücke im Lückentext „${t}“: „${i}“.`);return n}function _R(i){return i.blocks.flat().flatMap(e=>e.flatMap(t=>typeof t=="string"?[]:[t.answers]))}function AR(i,e){const t=n=>n.replace(/\s+/g,"");return t(e)!==""&&i.some(n=>t(n)===t(e))}const Fm={start:"oval",ende:"oval",anweisung:"rechteck",eingabe:"parallelogramm",ausgabe:"parallelogramm",frage:"raute"};function jR(i,e){const t={title:"",task:"",nodes:[]},n=[];for(const a of i){const s=a.trim();if(!s)continue;const r=/^([a-zäöü]+):\s*(.*)$/i.exec(s);if(!r)throw new Error(`${e}: Unbekannte Zeile im PAP: „${s}“.`);const o=r[1].toLowerCase(),l=r[2].trim();if(o==="titel")t.title=l;else if(o==="auftrag")t.task=l;else if(o==="erklärung"||o==="erklaerung")t.explanation=l;else if(o==="legende")t.legend=/^(ja|yes|true|sim)$/i.test(l);else if(o in Fm){const[u,m]=l.split(/\s+\|\s+/);if(!u)throw new Error(`${e}: Leeres Element im PAP: „${s}“.`);const c={shape:Fm[o],text:u.trim()};if(t.nodes.push(c),o!=="frage"){if(m)throw new Error(`${e}: Nur eine Frage hat einen zweiten Ausgang: „${s}“.`);continue}const d=/^(ja|nein)\s*(->|:)\s*(.+)$/i.exec(m??"");if(!d)throw new Error(`${e}: Eine Frage braucht „| nein -> Text“ oder „| nein: Text“: „${s}“.`);c.sideExit=d[1].toLowerCase(),d[2]===":"?(c.side=t.nodes.length,t.nodes.push({shape:"rechteck",text:d[3].trim(),isSide:!0})):n.push({node:t.nodes.length-1,target:d[3].trim(),line:s})}else throw new Error(`${e}: Unbekannter Schlüssel „${r[1]}“ im PAP.`)}for(const a of n){const s=t.nodes.findIndex((r,o)=>o{if(n.isSide)return;const s=MR(n.shape);(i.nodes.some(r=>r.backTo===a)||n.side!==void 0)&&(t+=12),e[a]={x:DR,y:t+s/2,w:n.shape==="raute"?170:n.shape==="oval"?110:BR,h:s},n.side!==void 0&&(e[n.side]={x:PR,y:t+s/2,w:OR,h:40}),t+=s+Rm}),{boxes:e,height:t-Rm+10}}function Cp(i,e){if((r=>r.length*6.6<=e)(i))return[i];const n=i.split(" ");let a=[i],s=1/0;for(let r=1;ru.length));l|→)\s*/);if(t.length!==2)throw new se(`${e}: Test „${i}“ braucht genau ein „->“.`);const n=(a,s)=>a.trim()===""||a.trim()==="-"?[]:a.split(s).map(r=>r.trim());return{inputs:n(t[0],/\s*,\s*/),expected:n(t[1],/\s*\|\s*/)}}function WR(i){const e=/^(.*?)\s*(?:->|→)\s*(.*)$/.exec(i.trim());if(!e)throw new Error(`„messwerte: ${i}“ braucht ein „->“.`);const t={};for(const n of e[1].split(/\s*,\s*/).filter(Boolean)){const a=/^(\w+)\s*=\s*(.+)$/.exec(n);if(!a||!(a[1]in Bt))throw new Error(`Unbekannter Sensor in „${n}“.`);t[a[1]]=a[2].split("/").map(s=>{const r=s.trim();if(r==="true"||r==="false")return r==="true";if(/^-?\d+(\.\d+)?$/.test(r))return Number(r);if(typeof Bt[a[1]].simulated=="string")return r;throw new Error(`Messwert „${r}“ ist keine Zahl und nicht true/false.`)})}return Pp({inputs:[],expected:[],sensors:t},e[2])}function Pp(i,e){const t=[];e=e.replace(/\bscreen\s+("(?:[^"\\]|\\.)*")/u,(n,a)=>(i.screen=JSON.parse(a),""));for(const n of e.split(/\s*,\s*/).filter(Boolean))n==="ledOn"||n==="ledOff"?i.led=n==="ledOn":n==="tone"||n==="noTone"?i.tone=n==="tone":n==="none"?i.drive=[]:n==="drives"?i.drives=!0:t.push(n);return t.length&&(i.drive=Bs(fg(t.join(", ")))),i}function ER(i,e,t){const n={id:i,title:"",task:"",tests:[],requires:[],hints:[],subprograms:[]};let a;for(const s of e){const r=/^(titel|auftrag|start|test|datei|fahrt|befehle|welt|messwerte|schleifentiefe|braucht|tipp|lösung|loesung|unterprogramm|code|bereich):\s*(.*)$/i.exec(s.trim());if(r){const[,o,l]=r;switch(a=void 0,o.toLowerCase()){case"titel":n.title=l;break;case"auftrag":n.task=l,a="task";break;case"start":n.start=l;break;case"test":n.tests.push(LR(l,t));break;case"datei":{const u=/^([\w.-]+)\s*(?:->|→)\s*(.*)$/.exec(l.trim());if(!u||!n.tests.length)throw new se(`${t}: Übung „${i}“: „datei:“ braucht vorher einen Test und die Form „name.txt -> Zeile | Zeile“.`);const m=n.tests[n.tests.length-1];m.files=[...m.files??[],{name:u[1],lines:u[2].trim()===""?[]:u[2].split(/\s*\|\s*/).map(c=>c.trim())}];break}case"fahrt":try{n.tests.push({inputs:[],expected:[],drive:Bs(fg(l))})}catch(u){throw new se(`${t}: Übung „${i}“: ${u.message}`)}break;case"welt":{const u=/^([\w-]+)\s*(?:->|→)\s*(.+)$/.exec(l.trim());if(u){try{n.tests.push(Pp({inputs:[],expected:[],world:u[1]},u[2]))}catch(m){throw new se(`${t}: Übung „${i}“: ${m.message}`)}break}for(const m of l.split(/\s*,\s*/).filter(Boolean)){if(!/^[\w-]+$/.test(m))throw new se(`${t}: Übung „${i}“: Weltname „${m}“ (nur Buchstaben, Ziffern, -).`);n.tests.push({inputs:[],expected:[],world:m})}break}case"befehle":try{n.tests.push({inputs:[],expected:[],actions:tx(l)})}catch(u){throw new se(`${t}: Übung „${i}“: ${u.message}`)}break;case"messwerte":try{n.tests.push(WR(l))}catch(u){throw new se(`${t}: Übung „${i}“: ${u.message}`)}break;case"schleifentiefe":if(n.loopDepth=Number(l),!Number.isInteger(n.loopDepth)||n.loopDepth<1)throw new se(`${t}: Übung „${i}“: „schleifentiefe:“ braucht eine Zahl ab 1.`);break;case"braucht":n.requires.push(...l.split(/\s*,\s*/).filter(Boolean));break;case"tipp":n.hints.push(l),a="hint";break;case"lösung":case"loesung":n.solution=l;break;case"unterprogramm":n.subprograms.push(...l.split(/\s*,\s*/).filter(Boolean));break;case"code":if(l.trim().toLowerCase()!=="java")throw new se(`${t}: Übung „${i}“: „code:“ kennt nur „java“.`);n.code="java";break;case"bereich":{const u=l.trim().toLowerCase();if(u!=="struktogramm"&&u!=="code"&&u!=="beide")throw new se(`${t}: Übung „${i}“: „bereich:“ kennt nur „struktogramm“, „code“ oder „beide“.`);n.area=u;break}}}else a&&s.trim()!==""?a==="task"?n.task+=` -`+s.trim():n.hints[n.hints.length-1]+=` -`+s.trim():s.trim()===""&&a==="task"&&(n.task+=` -`)}if(!n.title)throw new se(`${t}: Übung „${i}“ hat keinen Titel.`);if(!n.task.trim())throw new se(`${t}: Übung „${i}“ hat keinen Auftrag.`);return n.task=n.task.trim(),n}function NR(i,e){const t=[];for(const n of i){const a=n.trim();if(a.startsWith("?"))t.push({question:a.slice(1).trim(),options:[]});else if(/^- \[[ xX]\]/.test(a)){const s=t.at(-1);if(!s)throw new se(`${e}: Antwort ohne Frage im Quiz.`);s.options.push({text:a.slice(5).trim(),correct:a[3].toLowerCase()==="x"})}else if(a.startsWith("= ")){const s=t.at(-1),r=/^=\s+(.+?)\s+->\s+(.+)$/.exec(a);if(!s||!r)throw new se(`${e}: Zuordnung „${a}“ braucht die Form „= links -> rechts“ nach einer Frage.`);(s.pairs??(s.pairs=[])).push({left:r[1].trim(),right:r[2].trim()})}else if(a.startsWith("→")){const s=t.at(-1);if(!s)throw new se(`${e}: „→ Schritt“ ohne Frage im Quiz.`);s.ref=a.slice(1).trim()}else if(a.startsWith(">")){const s=t.at(-1);s&&(s.explanation=((s.explanation?s.explanation+" ":"")+a.slice(1).trim()).trim())}}for(const n of t){if(n.pairs){const a=new Set(n.pairs.map(s=>s.right));if(n.options.length>0||n.pairs.length<2||a.size!==n.pairs.length)throw new se(`${e}: Zuordnung „${n.question}“ braucht mindestens zwei Paare mit verschiedenen rechten Seiten und keine Antworten.`);continue}if(n.options.length<2||!n.options.some(a=>a.correct))throw new se(`${e}: Quizfrage „${n.question}“ braucht mindestens zwei Antworten, davon eine richtige.`)}return t}function Op(i,e){const t=[],n=i.split(` -`);let a=[];const s=()=>{const r=a.join(` -`).trim();r&&t.push({kind:"markdown",text:r}),a=[]};for(let r=0;r=n.length)throw new se(`${e}: Block „::: ${o[1]}“ wird nicht mit „:::“ geschlossen.`);const m=o[1].toLowerCase(),c=o[2].trim();switch(m){case"diagramm":if(!c)throw new se(`${e}: „::: diagramm“ braucht einen Dateinamen.`);t.push({kind:"diagram",file:c,caption:l.join(" ").trim()});break;case"übung":case"uebung":if(!c)throw new se(`${e}: „::: übung“ braucht eine Kennung.`);t.push({kind:"exercise",exercise:ER(c,l,e)});break;case"checkliste":t.push({kind:"checklist",items:l.map(d=>d.trim()).filter(d=>d.startsWith("- ")).map(d=>FR(d.slice(2).trim()))});break;case"kompetenzen":t.push({kind:"competences",items:l.map(d=>d.trim()).filter(d=>d.startsWith("- ")).map(d=>d.slice(2).trim())});break;case"quiz":t.push({kind:"quiz",questions:NR(l,e)});break;case"bild":if(!c)throw new se(`${e}: „::: bild“ braucht einen Dateinamen.`);t.push({kind:"images",files:c.split(/\s+/),caption:l.join(" ").trim()});break;case"grafik":if(!c)throw new se(`${e}: „::: grafik“ braucht einen Dateinamen.`);{const[d,h]=c.split(/\s+/),g=h==="animiert"?{animated:!0}:h?{labeled:h}:{};t.push({kind:"graphic",file:d,...g,caption:l.join(" ").trim()})}break;case"angeberwissen":t.push({kind:"brag",text:l.join(` -`).trim()});break;case"spiel":try{if(lR(c))t.push({kind:"roundGame",game:uR(c,l,e)});else if(c==="prinzessin")t.push({kind:"rescueGame",game:pR(l,e)});else if(c==="fahrt")t.push({kind:"driveGame",game:wR(l,e)});else if(c==="kreuzwort")t.push({kind:"crosswordGame",game:bR(l,e)});else{if(c)throw new se(`${e}: Unbekanntes Spiel „${c}“ (zuhoerer, vertrag, layout, bombe, frage, reihenfolge, prinzessin, fahrt oder kreuzwort).`);t.push({kind:"game",game:Ov(l,e)})}}catch(d){throw new se(d.message)}break;case"lückentext":case"lueckentext":try{t.push({kind:"cloze",cloze:SR(l,e)})}catch(d){throw new se(d.message)}break;case"pap":try{t.push({kind:"pap",pap:jR(l,e)})}catch(d){throw new se(d.message)}break;case"ludwig":t.push({kind:"brag",text:l.join(` -`).trim(),tip:!0});break;case"caesar":{const d=c?Number(c):3;if(!Number.isInteger(d)||d<0||d>25)throw new se(`${e}: „::: caesar“ braucht einen Schlüssel von 0 bis 25.`);t.push({kind:"caesar",key:d,text:l.join(" ").trim()});break}case"mbot2":{const d={kind:"robotModel",focus:[]};for(const h of l.filter(g=>g.trim())){const g=/^\s*(fokus|focus|ansicht|view|drehen|spin):\s*(.*)$/i.exec(h);if(!g)throw new se(`${e}: „::: mbot2“ kennt nur Zeilen „fokus:“, „ansicht:“ und „drehen:“.`);const f=g[1].toLowerCase();f==="fokus"||f==="focus"?d.focus=Jm(g[2],e):f==="ansicht"||f==="view"?d.view=g[2].trim().toLowerCase():d.spin=/^(ja|yes|true|sim)$/i.test(g[2].trim())}t.push(d);break}case"mbot2quiz":{const d={kind:"robotQuiz",tasks:[]};for(const h of l.filter(g=>g.trim())){const g=/^\s*-\s*(\w+)\s*:\s*(.+)$/.exec(h),f=/^\s*(fertig|done):\s*(.+)$/i.exec(h);if(g)d.tasks.push({part:Jm(g[1],e)[0],question:g[2].trim()});else if(f)d.done=f[2].trim();else throw new se(`${e}: „::: mbot2quiz“ braucht Zeilen „- teil: Frage“ und optional „fertig: Text“.`)}if(!d.tasks.length)throw new se(`${e}: „::: mbot2quiz“ hat keine Fragen.`);t.push(d);break}case"datei":{if(!/^[\w.-]+\.(txt|csv|json)$/u.test(c))throw new se(`${e}: „::: datei“ braucht einen Dateinamen wie brief.txt, liste.csv oder daten.json.`);for(;l.length&&l[0].trim()==="";)l.shift();for(;l.length&&l[l.length-1].trim()==="";)l.pop();t.push({kind:"textFile",name:c,content:l.join(` -`)});break}default:throw new se(`${e}: Unbekannter Block „::: ${o[1]}“.`)}r=u}return s(),t}function FR(i){const e=/^(.*?)\s+→\s+(.+)$/.exec(i);return e?{text:e[1],ref:e[2].trim()}:{text:i}}function RR(i,e){const t=new Map(i.map((a,s)=>[a.title.toLowerCase(),s]));for(const a of i.flatMap(s=>s.blocks))if(a.kind==="checklist"){for(const s of a.items)if(s.ref!==void 0&&(s.step=t.get(s.ref.toLowerCase()),s.step===void 0))throw new se(`${e}: Checkliste verweist auf unbekannten Schritt „${s.ref}“.`)}const n=new Set(i.flatMap(a=>a.blocks).flatMap(a=>a.kind==="checklist"?a.items.map(s=>s.step):[]));for(const a of i.flatMap(s=>s.blocks))if(a.kind==="quiz"){for(const s of a.questions)if(s.ref!==void 0){if(s.step=t.get(s.ref.toLowerCase()),s.step===void 0)throw new se(`${e}: Quizfrage „${s.question}“ verweist auf unbekannten Schritt „${s.ref}“.`);if(!n.has(s.step))throw new se(`${e}: Zu „${s.ref}“ (Quizfrage „${s.question}“) gibt es keinen Checklistenpunkt.`)}}}function JR(i,e){var c;const{meta:t,body:n}=Bp(i,e),a=e.replace(/^.*\//,"").replace(/\.md$/,""),s=Number((c=/^(\d+)/.exec(a))==null?void 0:c[1]);if(!Number.isFinite(s))throw new se(`${e}: Dateiname muss mit der Nummer beginnen (z. B. 01-…).`);const r=[];let o=[],l;const u=()=>{l&&r.push({title:l.title,blocks:Op(l.lines.join(` -`),e)})};for(const d of n.split(` -`)){const h=/^##\s+(.*)$/.exec(d);h?(u(),l={title:h[1].trim(),lines:[]}):l?l.lines.push(d):o.push(d)}if(u(),r.length===0)throw new se(`${e}: Keine Schritte (## Überschrift) gefunden.`);RR(r,e);const m=t.get("titel");if(!m)throw new se(`${e}: „titel:“ fehlt.`);return{id:a,number:s,title:m,section:t.get("bereich")??"",summary:t.get("kurz")??o.join(" ").trim(),steps:r}}function GR(i,e){const{meta:t,body:n}=Bp(i,e),a=t.get("titel");if(!a)throw new se(`${e}: „titel:“ fehlt.`);const s=t.get("nach");return{id:e.replace(/^.*\//,"").replace(/\.md$/,""),title:a,after:s?Number(s):void 0,level:Number(t.get("stufe")??"1")||1,blocks:Op(n,e)}}function Jm(i,e){const t=i.split(/\s*,\s*/).map(n=>n.trim()).filter(Boolean);for(const n of t)if(!ax(n))throw new se(`${e}: Unbekanntes Teil „${n}“. Bekannt: ${bg.join(", ")}.`);return t}const HR=Object.assign({"../../lessons/de/01-was-ist-ein-struktogramm.md":Fv,"../../lessons/de/01-weitere-darstellungen.md":Rv,"../../lessons/de/02-der-editor.md":Jv,"../../lessons/de/03-speichern-oeffnen-exportieren.md":Gv,"../../lessons/de/04-variablen-und-datentypen.md":Hv,"../../lessons/de/05-sequenz.md":Vv,"../../lessons/de/06-eingabe-und-ausgabe.md":qv,"../../lessons/de/07-simulation-und-schreibtischtest.md":Uv,"../../lessons/de/08-verzweigung.md":Qv,"../../lessons/de/09-bedingungen-verknuepfen.md":$v,"../../lessons/de/10-fallauswahl.md":Yv,"../../lessons/de/11-while-schleife.md":Kv,"../../lessons/de/12-do-while-schleife.md":Xv,"../../lessons/de/13-for-schleife.md":ew,"../../lessons/de/14-verschachteln.md":tw,"../../lessons/de/15-arrays.md":nw,"../../lessons/de/16-zweidimensionale-arrays.md":iw,"../../lessons/de/17-zeichenketten.md":aw,"../../lessons/de/18-aufruf-und-aussprung.md":sw,"../../lessons/de/19-suchen-und-sortieren.md":rw,"../../lessons/de/20-java.md":ow,"../../lessons/de/21-python-und-javascript.md":lw,"../../lessons/de/22-try-und-catch.md":uw,"../../lessons/de/23-text-einlesen-und-speichern.md":mw,"../../lessons/de/24-zeile-fuer-zeile.md":cw,"../../lessons/de/25-tabellen-als-csv.md":dw,"../../lessons/de/26-json.md":hw,"../../lessons/de/27-formate-im-vergleich.md":gw,"../../lessons/de/28-erstes-fenster.md":pw,"../../lessons/de/29-knopf-im-fenster.md":fw,"../../lessons/de/30-der-knopf-reagiert.md":bw,"../../lessons/de/31-zahl-verdoppeln.md":xw,"../../lessons/de/32-klickzaehler.md":vw,"../../lessons/de/33-ampel.md":ww,"../../lessons/de/34-notenrechner.md":yw,"../../lessons/de/35-taschenrechner.md":zw,"../../lessons/de/36-zahlenraten.md":Iw,"../../lessons/de/37-viele-knoepfe-ein-zuhoerer.md":kw,"../../lessons/de/38-innere-klassen.md":Sw,"../../lessons/de/39-zwei-reiter.md":Tw,"../../lessons/de/40-model-view-controller.md":_w,"../../lessons/de/begriffe.md":Aw,"../../lessons/de/nachschlagen.md":jw,"../../lessons/en/01-was-ist-ein-struktogramm.md":Cw,"../../lessons/en/01-weitere-darstellungen.md":Dw,"../../lessons/en/02-der-editor.md":Bw,"../../lessons/en/03-speichern-oeffnen-exportieren.md":Pw,"../../lessons/en/04-variablen-und-datentypen.md":Ow,"../../lessons/en/05-sequenz.md":Mw,"../../lessons/en/06-eingabe-und-ausgabe.md":Zw,"../../lessons/en/07-simulation-und-schreibtischtest.md":Lw,"../../lessons/en/08-verzweigung.md":Ww,"../../lessons/en/09-bedingungen-verknuepfen.md":Ew,"../../lessons/en/10-fallauswahl.md":Nw,"../../lessons/en/11-while-schleife.md":Fw,"../../lessons/en/12-do-while-schleife.md":Rw,"../../lessons/en/13-for-schleife.md":Jw,"../../lessons/en/14-verschachteln.md":Gw,"../../lessons/en/15-arrays.md":Hw,"../../lessons/en/16-zweidimensionale-arrays.md":Vw,"../../lessons/en/17-zeichenketten.md":qw,"../../lessons/en/18-aufruf-und-aussprung.md":Uw,"../../lessons/en/19-suchen-und-sortieren.md":Qw,"../../lessons/en/20-java.md":$w,"../../lessons/en/21-python-und-javascript.md":Yw,"../../lessons/en/22-try-und-catch.md":Kw,"../../lessons/en/23-text-einlesen-und-speichern.md":Xw,"../../lessons/en/24-zeile-fuer-zeile.md":ey,"../../lessons/en/25-tabellen-als-csv.md":ty,"../../lessons/en/26-json.md":ny,"../../lessons/en/27-formate-im-vergleich.md":iy,"../../lessons/en/28-erstes-fenster.md":ay,"../../lessons/en/29-knopf-im-fenster.md":sy,"../../lessons/en/30-der-knopf-reagiert.md":ry,"../../lessons/en/31-zahl-verdoppeln.md":oy,"../../lessons/en/32-klickzaehler.md":ly,"../../lessons/en/33-ampel.md":uy,"../../lessons/en/34-notenrechner.md":my,"../../lessons/en/35-taschenrechner.md":cy,"../../lessons/en/36-zahlenraten.md":dy,"../../lessons/en/37-viele-knoepfe-ein-zuhoerer.md":hy,"../../lessons/en/38-innere-klassen.md":gy,"../../lessons/en/39-zwei-reiter.md":py,"../../lessons/en/40-model-view-controller.md":fy,"../../lessons/en/begriffe.md":by,"../../lessons/en/nachschlagen.md":xy,"../../lessons/pt/01-was-ist-ein-struktogramm.md":vy,"../../lessons/pt/01-weitere-darstellungen.md":wy,"../../lessons/pt/02-der-editor.md":yy,"../../lessons/pt/03-speichern-oeffnen-exportieren.md":zy,"../../lessons/pt/04-variablen-und-datentypen.md":Iy,"../../lessons/pt/05-sequenz.md":ky,"../../lessons/pt/06-eingabe-und-ausgabe.md":Sy,"../../lessons/pt/07-simulation-und-schreibtischtest.md":Ty,"../../lessons/pt/08-verzweigung.md":_y,"../../lessons/pt/09-bedingungen-verknuepfen.md":Ay,"../../lessons/pt/10-fallauswahl.md":jy,"../../lessons/pt/11-while-schleife.md":Cy,"../../lessons/pt/12-do-while-schleife.md":Dy,"../../lessons/pt/13-for-schleife.md":By,"../../lessons/pt/14-verschachteln.md":Py,"../../lessons/pt/15-arrays.md":Oy,"../../lessons/pt/16-zweidimensionale-arrays.md":My,"../../lessons/pt/17-zeichenketten.md":Zy,"../../lessons/pt/18-aufruf-und-aussprung.md":Ly,"../../lessons/pt/19-suchen-und-sortieren.md":Wy,"../../lessons/pt/20-java.md":Ey,"../../lessons/pt/21-python-und-javascript.md":Ny,"../../lessons/pt/22-try-und-catch.md":Fy,"../../lessons/pt/23-text-einlesen-und-speichern.md":Ry,"../../lessons/pt/24-zeile-fuer-zeile.md":Jy,"../../lessons/pt/25-tabellen-als-csv.md":Gy,"../../lessons/pt/26-json.md":Hy,"../../lessons/pt/27-formate-im-vergleich.md":Vy,"../../lessons/pt/28-erstes-fenster.md":qy,"../../lessons/pt/29-knopf-im-fenster.md":Uy,"../../lessons/pt/30-der-knopf-reagiert.md":Qy,"../../lessons/pt/31-zahl-verdoppeln.md":$y,"../../lessons/pt/32-klickzaehler.md":Yy,"../../lessons/pt/33-ampel.md":Ky,"../../lessons/pt/34-notenrechner.md":Xy,"../../lessons/pt/35-taschenrechner.md":e9,"../../lessons/pt/36-zahlenraten.md":t9,"../../lessons/pt/37-viele-knoepfe-ein-zuhoerer.md":n9,"../../lessons/pt/38-innere-klassen.md":i9,"../../lessons/pt/39-zwei-reiter.md":a9,"../../lessons/pt/40-model-view-controller.md":s9,"../../lessons/pt/begriffe.md":r9,"../../lessons/pt/nachschlagen.md":o9}),VR=Object.assign({"../../lessons/de/aufgaben/alter-unterprogramm.md":l9,"../../lessons/de/aufgaben/ballwurf.md":u9,"../../lessons/de/aufgaben/bestenliste.md":m9,"../../lessons/de/aufgaben/binaerzahl.md":c9,"../../lessons/de/aufgaben/caesar.md":d9,"../../lessons/de/aufgaben/collatz.md":h9,"../../lessons/de/aufgaben/einheitenrechner.md":g9,"../../lessons/de/aufgaben/fibonacci.md":p9,"../../lessons/de/aufgaben/gerade-ungerade.md":f9,"../../lessons/de/aufgaben/mehrwertsteuer.md":b9,"../../lessons/de/aufgaben/notendurchschnitt.md":x9,"../../lessons/de/aufgaben/pin-pruefen.md":v9,"../../lessons/de/aufgaben/schiffe-versenken.md":w9,"../../lessons/de/aufgaben/speisekarte.md":y9,"../../lessons/de/aufgaben/summe-bilden.md":z9,"../../lessons/de/aufgaben/tafeldienst.md":I9,"../../lessons/de/aufgaben/taschenrechner.md":k9,"../../lessons/de/aufgaben/verleih-menue.md":S9,"../../lessons/de/aufgaben/vokabeltrainer.md":T9,"../../lessons/de/aufgaben/volljaehrig.md":_9,"../../lessons/de/aufgaben/wuerfel-mvc.md":A9,"../../lessons/de/aufgaben/zahl-verdoppeln.md":j9,"../../lessons/en/aufgaben/alter-unterprogramm.md":C9,"../../lessons/en/aufgaben/ballwurf.md":D9,"../../lessons/en/aufgaben/bestenliste.md":B9,"../../lessons/en/aufgaben/binaerzahl.md":P9,"../../lessons/en/aufgaben/caesar.md":O9,"../../lessons/en/aufgaben/collatz.md":M9,"../../lessons/en/aufgaben/einheitenrechner.md":Z9,"../../lessons/en/aufgaben/fibonacci.md":L9,"../../lessons/en/aufgaben/gerade-ungerade.md":W9,"../../lessons/en/aufgaben/mehrwertsteuer.md":E9,"../../lessons/en/aufgaben/notendurchschnitt.md":N9,"../../lessons/en/aufgaben/pin-pruefen.md":F9,"../../lessons/en/aufgaben/schiffe-versenken.md":R9,"../../lessons/en/aufgaben/speisekarte.md":J9,"../../lessons/en/aufgaben/summe-bilden.md":G9,"../../lessons/en/aufgaben/tafeldienst.md":H9,"../../lessons/en/aufgaben/taschenrechner.md":V9,"../../lessons/en/aufgaben/verleih-menue.md":q9,"../../lessons/en/aufgaben/vokabeltrainer.md":U9,"../../lessons/en/aufgaben/volljaehrig.md":Q9,"../../lessons/en/aufgaben/wuerfel-mvc.md":$9,"../../lessons/en/aufgaben/zahl-verdoppeln.md":Y9,"../../lessons/pt/aufgaben/alter-unterprogramm.md":K9,"../../lessons/pt/aufgaben/ballwurf.md":X9,"../../lessons/pt/aufgaben/bestenliste.md":e5,"../../lessons/pt/aufgaben/binaerzahl.md":t5,"../../lessons/pt/aufgaben/caesar.md":n5,"../../lessons/pt/aufgaben/collatz.md":i5,"../../lessons/pt/aufgaben/einheitenrechner.md":a5,"../../lessons/pt/aufgaben/fibonacci.md":s5,"../../lessons/pt/aufgaben/gerade-ungerade.md":r5,"../../lessons/pt/aufgaben/mehrwertsteuer.md":o5,"../../lessons/pt/aufgaben/notendurchschnitt.md":l5,"../../lessons/pt/aufgaben/pin-pruefen.md":u5,"../../lessons/pt/aufgaben/schiffe-versenken.md":m5,"../../lessons/pt/aufgaben/speisekarte.md":c5,"../../lessons/pt/aufgaben/summe-bilden.md":d5,"../../lessons/pt/aufgaben/tafeldienst.md":h5,"../../lessons/pt/aufgaben/taschenrechner.md":g5,"../../lessons/pt/aufgaben/verleih-menue.md":p5,"../../lessons/pt/aufgaben/vokabeltrainer.md":f5,"../../lessons/pt/aufgaben/volljaehrig.md":b5,"../../lessons/pt/aufgaben/wuerfel-mvc.md":x5,"../../lessons/pt/aufgaben/zahl-verdoppeln.md":v5}),Gm=Object.assign({"../../lessons/de/visubot/00-struktogramm-und-editor.md":w5,"../../lessons/de/visubot/01-der-mbot2.md":y5,"../../lessons/de/visubot/02-das-quadrat.md":z5,"../../lessons/de/visubot/02-quadrat-als-pap.md":I5,"../../lessons/de/visubot/03-variablen-und-ausgabe.md":k5,"../../lessons/de/visubot/03-zeile-fuer-zeile-testen.md":S5,"../../lessons/de/visubot/04-sensoren.md":T5,"../../lessons/de/visubot/05-bis-zur-wand.md":_5,"../../lessons/de/visubot/05-erst-fahren-dann-pruefen.md":A5,"../../lessons/de/visubot/05-zaehlen-und-summieren.md":j5,"../../lessons/de/visubot/06-abstand.md":C5,"../../lessons/de/visubot/06-konstanten-final.md":D5,"../../lessons/de/visubot/06-nah-mittel-weit.md":B5,"../../lessons/de/visubot/07-linie.md":P5,"../../lessons/de/visubot/07-und-oder.md":O5,"../../lessons/de/visubot/08-farben.md":M5,"../../lessons/de/visubot/08-groesster-wert.md":Z5,"../../lessons/de/visubot/09-fallauswahl-mbot2.md":L5,"../../lessons/de/visubot/10-tasten-joystick.md":W5,"../../lessons/de/visubot/10-unterbrechen-mit-break.md":E5,"../../lessons/de/visubot/11-licht.md":N5,"../../lessons/de/visubot/11-merker-boolean.md":F5,"../../lessons/de/visubot/12-lautstaerke.md":R5,"../../lessons/de/visubot/13-neigung.md":J5,"../../lessons/de/visubot/14-leds.md":G5,"../../lessons/de/visubot/15-einzelne-leds.md":H5,"../../lessons/de/visubot/16-lautsprecher.md":V5,"../../lessons/de/visubot/17-arrays-melodie.md":q5,"../../lessons/de/visubot/18-bildschirm.md":U5,"../../lessons/de/visubot/19-zeichenketten.md":Q5,"../../lessons/de/visubot/20-motoren.md":$5,"../../lessons/de/visubot/21-stoppuhr-akku.md":Y5,"../../lessons/de/visubot/21-zustaende.md":K5,"../../lessons/de/visubot/22-eigene-methoden.md":X5,"../../lessons/de/visubot/23-wie-geht-es-weiter.md":ez,"../../lessons/de/visubot/nachschlagen.md":tz,"../../lessons/en/visubot/00-struktogramm-und-editor.md":nz,"../../lessons/en/visubot/01-der-mbot2.md":iz,"../../lessons/en/visubot/02-das-quadrat.md":az,"../../lessons/en/visubot/02-quadrat-als-pap.md":sz,"../../lessons/en/visubot/03-variablen-und-ausgabe.md":rz,"../../lessons/en/visubot/03-zeile-fuer-zeile-testen.md":oz,"../../lessons/en/visubot/04-sensoren.md":lz,"../../lessons/en/visubot/05-bis-zur-wand.md":uz,"../../lessons/en/visubot/05-erst-fahren-dann-pruefen.md":mz,"../../lessons/en/visubot/05-zaehlen-und-summieren.md":cz,"../../lessons/en/visubot/06-abstand.md":dz,"../../lessons/en/visubot/06-konstanten-final.md":hz,"../../lessons/en/visubot/06-nah-mittel-weit.md":gz,"../../lessons/en/visubot/07-linie.md":pz,"../../lessons/en/visubot/07-und-oder.md":fz,"../../lessons/en/visubot/08-farben.md":bz,"../../lessons/en/visubot/08-groesster-wert.md":xz,"../../lessons/en/visubot/09-fallauswahl-mbot2.md":vz,"../../lessons/en/visubot/10-tasten-joystick.md":wz,"../../lessons/en/visubot/10-unterbrechen-mit-break.md":yz,"../../lessons/en/visubot/11-licht.md":zz,"../../lessons/en/visubot/11-merker-boolean.md":Iz,"../../lessons/en/visubot/12-lautstaerke.md":kz,"../../lessons/en/visubot/13-neigung.md":Sz,"../../lessons/en/visubot/14-leds.md":Tz,"../../lessons/en/visubot/15-einzelne-leds.md":_z,"../../lessons/en/visubot/16-lautsprecher.md":Az,"../../lessons/en/visubot/17-arrays-melodie.md":jz,"../../lessons/en/visubot/18-bildschirm.md":Cz,"../../lessons/en/visubot/19-zeichenketten.md":Dz,"../../lessons/en/visubot/20-motoren.md":Bz,"../../lessons/en/visubot/21-stoppuhr-akku.md":Pz,"../../lessons/en/visubot/21-zustaende.md":Oz,"../../lessons/en/visubot/22-eigene-methoden.md":Mz,"../../lessons/en/visubot/23-wie-geht-es-weiter.md":Zz,"../../lessons/en/visubot/nachschlagen.md":Lz}),qR=Object.assign({"../../lessons/de/visubot/bloecke/01-losfahren.md":Wz,"../../lessons/de/visubot/bloecke/02-immer-wieder.md":Ez,"../../lessons/de/visubot/bloecke/03-der-mbot2-misst.md":Nz,"../../lessons/de/visubot/bloecke/04-entscheidungen.md":Fz,"../../lessons/de/visubot/bloecke/05-lichter-und-toene.md":Rz,"../../lessons/de/visubot/bloecke/06-immer-wieder-pruefen.md":Jz,"../../lessons/de/visubot/bloecke/07-der-linie-folgen.md":Gz,"../../lessons/de/visubot/bloecke/08-parcours.md":Hz}),UR=Object.assign({"../../lessons/de/visubot/bloecke/aufgaben/block-ausgang.md":Vz,"../../lessons/de/visubot/bloecke/aufgaben/block-disco.md":qz,"../../lessons/de/visubot/bloecke/aufgaben/block-slalom.md":Uz,"../../lessons/de/visubot/bloecke/aufgaben/block-wald.md":Qz}),QR=Object.assign({"../../lessons/de/visubot/aufgaben/alarmanlage.md":$z,"../../lessons/de/visubot/aufgaben/ausgang.md":Yz,"../../lessons/de/visubot/aufgaben/binaer-zaehlen.md":Kz,"../../lessons/de/visubot/aufgaben/dreieck.md":Xz,"../../lessons/de/visubot/aufgaben/einparken.md":e6,"../../lessons/de/visubot/aufgaben/hindernis.md":t6,"../../lessons/de/visubot/aufgaben/knight-rider.md":n6,"../../lessons/de/visubot/aufgaben/labyrinth.md":i6,"../../lessons/de/visubot/aufgaben/linienfolger-stopp.md":a6,"../../lessons/de/visubot/aufgaben/morsen.md":s6,"../../lessons/de/visubot/aufgaben/nachtwaechter.md":r6,"../../lessons/de/visubot/aufgaben/slalom.md":o6,"../../lessons/de/visubot/aufgaben/spirale.md":l6,"../../lessons/de/visubot/aufgaben/treppe.md":u6,"../../lessons/de/visubot/aufgaben/tunnel.md":m6,"../../lessons/de/visubot/aufgaben/wald.md":c6,"../../lessons/en/visubot/aufgaben/alarmanlage.md":d6,"../../lessons/en/visubot/aufgaben/ausgang.md":h6,"../../lessons/en/visubot/aufgaben/binaer-zaehlen.md":g6,"../../lessons/en/visubot/aufgaben/dreieck.md":p6,"../../lessons/en/visubot/aufgaben/einparken.md":f6,"../../lessons/en/visubot/aufgaben/hindernis.md":b6,"../../lessons/en/visubot/aufgaben/knight-rider.md":x6,"../../lessons/en/visubot/aufgaben/labyrinth.md":v6,"../../lessons/en/visubot/aufgaben/linienfolger-stopp.md":w6,"../../lessons/en/visubot/aufgaben/morsen.md":y6,"../../lessons/en/visubot/aufgaben/nachtwaechter.md":z6,"../../lessons/en/visubot/aufgaben/slalom.md":I6,"../../lessons/en/visubot/aufgaben/spirale.md":k6,"../../lessons/en/visubot/aufgaben/treppe.md":S6,"../../lessons/en/visubot/aufgaben/tunnel.md":T6,"../../lessons/en/visubot/aufgaben/wald.md":_6}),$R=Object.assign({"../../lessons/diagramme/aufgabe-alter-haupt-loesung.visustruct":A6,"../../lessons/diagramme/aufgabe-alter-unterprogramm-loesung.visustruct":j6,"../../lessons/diagramme/aufgabe-ballwurf-loesung.visustruct":C6,"../../lessons/diagramme/aufgabe-ballwurf-start.visustruct":D6,"../../lessons/diagramme/aufgabe-bestenliste-loesung.visustruct":B6,"../../lessons/diagramme/aufgabe-binaer-loesung.visustruct":P6,"../../lessons/diagramme/aufgabe-caesar-array-loesung.visustruct":O6,"../../lessons/diagramme/aufgabe-caesar-array-start.visustruct":M6,"../../lessons/diagramme/aufgabe-caesar-ascii-loesung.visustruct":Z6,"../../lessons/diagramme/aufgabe-caesar-entschluesseln-loesung.visustruct":L6,"../../lessons/diagramme/aufgabe-collatz-loesung.visustruct":W6,"../../lessons/diagramme/aufgabe-durchschnitt-loesung.visustruct":E6,"../../lessons/diagramme/aufgabe-fibonacci-loesung.visustruct":N6,"../../lessons/diagramme/aufgabe-gerade-loesung.visustruct":F6,"../../lessons/diagramme/aufgabe-mehrwertsteuer-loesung.visustruct":R6,"../../lessons/diagramme/aufgabe-pin-java-loesung.visustruct":J6,"../../lessons/diagramme/aufgabe-pin-loesung.visustruct":G6,"../../lessons/diagramme/aufgabe-rechner-loesung.visustruct":H6,"../../lessons/diagramme/aufgabe-schiffe-loesung.visustruct":V6,"../../lessons/diagramme/aufgabe-schiffe-start.visustruct":q6,"../../lessons/diagramme/aufgabe-speisekarte-loesung.visustruct":U6,"../../lessons/diagramme/aufgabe-summe-loesung.visustruct":Q6,"../../lessons/diagramme/aufgabe-tafeldienst-loesung.visustruct":$6,"../../lessons/diagramme/aufgabe-verdoppeln-loesung.visustruct":Y6,"../../lessons/diagramme/aufgabe-verleih-java-loesung.visustruct":K6,"../../lessons/diagramme/aufgabe-verleih-loesung.visustruct":X6,"../../lessons/diagramme/aufgabe-vokabeln-loesung.visustruct":eI,"../../lessons/diagramme/aufgabe-vokabeln-start.visustruct":tI,"../../lessons/diagramme/aufgabe-volljaehrig-loesung.visustruct":nI,"../../lessons/diagramme/dt-buffered.visustruct":iI,"../../lessons/diagramme/dt-csv.visustruct":aI,"../../lessons/diagramme/dt-json.visustruct":sI,"../../lessons/diagramme/dt-lesen.visustruct":rI,"../../lessons/diagramme/en/aufgabe-alter-haupt-loesung.visustruct":oI,"../../lessons/diagramme/en/aufgabe-alter-unterprogramm-loesung.visustruct":lI,"../../lessons/diagramme/en/aufgabe-ballwurf-loesung.visustruct":uI,"../../lessons/diagramme/en/aufgabe-ballwurf-start.visustruct":mI,"../../lessons/diagramme/en/aufgabe-bestenliste-loesung.visustruct":cI,"../../lessons/diagramme/en/aufgabe-binaer-loesung.visustruct":dI,"../../lessons/diagramme/en/aufgabe-caesar-array-loesung.visustruct":hI,"../../lessons/diagramme/en/aufgabe-caesar-array-start.visustruct":gI,"../../lessons/diagramme/en/aufgabe-caesar-ascii-loesung.visustruct":pI,"../../lessons/diagramme/en/aufgabe-caesar-entschluesseln-loesung.visustruct":fI,"../../lessons/diagramme/en/aufgabe-collatz-loesung.visustruct":bI,"../../lessons/diagramme/en/aufgabe-durchschnitt-loesung.visustruct":xI,"../../lessons/diagramme/en/aufgabe-fibonacci-loesung.visustruct":vI,"../../lessons/diagramme/en/aufgabe-gerade-loesung.visustruct":wI,"../../lessons/diagramme/en/aufgabe-mehrwertsteuer-loesung.visustruct":yI,"../../lessons/diagramme/en/aufgabe-pin-java-loesung.visustruct":zI,"../../lessons/diagramme/en/aufgabe-pin-loesung.visustruct":II,"../../lessons/diagramme/en/aufgabe-rechner-loesung.visustruct":kI,"../../lessons/diagramme/en/aufgabe-schiffe-loesung.visustruct":SI,"../../lessons/diagramme/en/aufgabe-schiffe-start.visustruct":TI,"../../lessons/diagramme/en/aufgabe-speisekarte-loesung.visustruct":_I,"../../lessons/diagramme/en/aufgabe-summe-loesung.visustruct":AI,"../../lessons/diagramme/en/aufgabe-tafeldienst-loesung.visustruct":jI,"../../lessons/diagramme/en/aufgabe-verdoppeln-loesung.visustruct":CI,"../../lessons/diagramme/en/aufgabe-verleih-java-loesung.visustruct":DI,"../../lessons/diagramme/en/aufgabe-verleih-loesung.visustruct":BI,"../../lessons/diagramme/en/aufgabe-vokabeln-loesung.visustruct":PI,"../../lessons/diagramme/en/aufgabe-vokabeln-start.visustruct":OI,"../../lessons/diagramme/en/aufgabe-volljaehrig-loesung.visustruct":MI,"../../lessons/diagramme/en/dt-buffered.visustruct":ZI,"../../lessons/diagramme/en/dt-csv.visustruct":LI,"../../lessons/diagramme/en/dt-json.visustruct":WI,"../../lessons/diagramme/en/dt-lesen.visustruct":EI,"../../lessons/diagramme/en/kino-einmaleins-loesung.visustruct":NI,"../../lessons/diagramme/en/kino-frei-loesung.visustruct":FI,"../../lessons/diagramme/en/kino-frei-start.visustruct":RI,"../../lessons/diagramme/en/kino-karte-loesung.visustruct":JI,"../../lessons/diagramme/en/kino-karte-start.visustruct":GI,"../../lessons/diagramme/en/kino-saalplan.visustruct":HI,"../../lessons/diagramme/en/kino-verkaufen.visustruct":VI,"../../lessons/diagramme/en/l04-gross-klein-loesung.visustruct":qI,"../../lessons/diagramme/en/l04-gross-klein-start.visustruct":UI,"../../lessons/diagramme/en/l04-java-fehler-loesung.visustruct":QI,"../../lessons/diagramme/en/l04-java-fehler-start.visustruct":$I,"../../lessons/diagramme/en/l04-java-laeuft-nicht-loesung.visustruct":YI,"../../lessons/diagramme/en/l04-java-laeuft-nicht-start.visustruct":KI,"../../lessons/diagramme/en/l04-java-steckbrief-loesung.visustruct":XI,"../../lessons/diagramme/en/l04-rechnen.visustruct":ek,"../../lessons/diagramme/en/l04-reihenfolge-loesung.visustruct":tk,"../../lessons/diagramme/en/l04-reihenfolge-start.visustruct":nk,"../../lessons/diagramme/en/l04-tauschen-loesung.visustruct":ik,"../../lessons/diagramme/en/l04-tauschen-start.visustruct":ak,"../../lessons/diagramme/en/l04-tauschen.visustruct":sk,"../../lessons/diagramme/en/l05-datentypen.visustruct":rk,"../../lessons/diagramme/en/l05-division.visustruct":ok,"../../lessons/diagramme/en/l05-einkauf-loesung.visustruct":lk,"../../lessons/diagramme/en/l05-java-fehler-loesung.visustruct":uk,"../../lessons/diagramme/en/l05-java-fehler-start.visustruct":mk,"../../lessons/diagramme/en/l05-java-kino-loesung.visustruct":ck,"../../lessons/diagramme/en/l05-java-laeuft-nicht-loesung.visustruct":dk,"../../lessons/diagramme/en/l05-java-laeuft-nicht-start.visustruct":hk,"../../lessons/diagramme/en/l05-rechteck-loesung.visustruct":gk,"../../lessons/diagramme/en/l05-sekunden-loesung.visustruct":pk,"../../lessons/diagramme/en/l05-sekunden-start.visustruct":fk,"../../lessons/diagramme/en/l05-sequenz.visustruct":bk,"../../lessons/diagramme/en/l06-begruessung-loesung.visustruct":xk,"../../lessons/diagramme/en/l06-eva.visustruct":vk,"../../lessons/diagramme/en/l06-java-fehler-loesung.visustruct":wk,"../../lessons/diagramme/en/l06-java-fehler-start.visustruct":yk,"../../lessons/diagramme/en/l06-java-laeuft-nicht-loesung.visustruct":zk,"../../lessons/diagramme/en/l06-java-laeuft-nicht-start.visustruct":Ik,"../../lessons/diagramme/en/l06-java-tage-loesung.visustruct":kk,"../../lessons/diagramme/en/l06-mittelwert-loesung.visustruct":Sk,"../../lessons/diagramme/en/l06-rechner-loesung.visustruct":Tk,"../../lessons/diagramme/en/l07-alter-pruefen.visustruct":_k,"../../lessons/diagramme/en/l07-betrag-loesung.visustruct":Ak,"../../lessons/diagramme/en/l07-gerade-loesung.visustruct":jk,"../../lessons/diagramme/en/l07-java-fehler-loesung.visustruct":Ck,"../../lessons/diagramme/en/l07-java-fehler-start.visustruct":Dk,"../../lessons/diagramme/en/l07-java-laeuft-nicht-loesung.visustruct":Bk,"../../lessons/diagramme/en/l07-java-laeuft-nicht-start.visustruct":Pk,"../../lessons/diagramme/en/l07-java-pizza-loesung.visustruct":Ok,"../../lessons/diagramme/en/l07-java-pizza-start.visustruct":Mk,"../../lessons/diagramme/en/l07-maximum-loesung.visustruct":Zk,"../../lessons/diagramme/en/l07-rabatt.visustruct":Lk,"../../lessons/diagramme/en/l08-gueltig-loesung.visustruct":Wk,"../../lessons/diagramme/en/l08-java-bestanden-loesung.visustruct":Ek,"../../lessons/diagramme/en/l08-java-fehler-loesung.visustruct":Nk,"../../lessons/diagramme/en/l08-java-fehler-start.visustruct":Fk,"../../lessons/diagramme/en/l08-java-laeuft-nicht-loesung.visustruct":Rk,"../../lessons/diagramme/en/l08-java-laeuft-nicht-start.visustruct":Jk,"../../lessons/diagramme/en/l08-jugendticket.visustruct":Gk,"../../lessons/diagramme/en/l08-note.visustruct":Hk,"../../lessons/diagramme/en/l08-schaltjahr-loesung.visustruct":Vk,"../../lessons/diagramme/en/l08-wochenende-loesung.visustruct":qk,"../../lessons/diagramme/en/l09-ampel.visustruct":Uk,"../../lessons/diagramme/en/l09-automat.visustruct":Qk,"../../lessons/diagramme/en/l09-java-fehler-loesung.visustruct":$k,"../../lessons/diagramme/en/l09-java-fehler-start.visustruct":Yk,"../../lessons/diagramme/en/l09-java-laeuft-nicht-loesung.visustruct":Kk,"../../lessons/diagramme/en/l09-java-laeuft-nicht-start.visustruct":Xk,"../../lessons/diagramme/en/l09-java-schule-loesung.visustruct":e7,"../../lessons/diagramme/en/l09-note-loesung.visustruct":t7,"../../lessons/diagramme/en/l09-rechner-loesung.visustruct":n7,"../../lessons/diagramme/en/l09-verschachtelt.visustruct":i7,"../../lessons/diagramme/en/l1-grundbausteine.visustruct":a7,"../../lessons/diagramme/en/l1-sequenz.visustruct":s7,"../../lessons/diagramme/en/l1-tee.visustruct":r7,"../../lessons/diagramme/en/l1-verzweigung.visustruct":o7,"../../lessons/diagramme/en/l1-wiederholung.visustruct":l7,"../../lessons/diagramme/en/l10-bis-n-loesung.visustruct":u7,"../../lessons/diagramme/en/l10-countdown.visustruct":m7,"../../lessons/diagramme/en/l10-endlos.visustruct":c7,"../../lessons/diagramme/en/l10-fakultaet.visustruct":d7,"../../lessons/diagramme/en/l10-java-fehler-loesung.visustruct":h7,"../../lessons/diagramme/en/l10-java-fehler-start.visustruct":g7,"../../lessons/diagramme/en/l10-java-laeuft-nicht-loesung.visustruct":p7,"../../lessons/diagramme/en/l10-java-laeuft-nicht-start.visustruct":f7,"../../lessons/diagramme/en/l10-java-wuerfel-loesung.visustruct":b7,"../../lessons/diagramme/en/l10-verdoppeln-loesung.visustruct":x7,"../../lessons/diagramme/en/l11-eingabe-pruefen.visustruct":v7,"../../lessons/diagramme/en/l11-einmal.visustruct":w7,"../../lessons/diagramme/en/l11-java-fehler-loesung.visustruct":y7,"../../lessons/diagramme/en/l11-java-fehler-start.visustruct":z7,"../../lessons/diagramme/en/l11-java-gerade-loesung.visustruct":I7,"../../lessons/diagramme/en/l11-java-laeuft-nicht-loesung.visustruct":k7,"../../lessons/diagramme/en/l11-java-laeuft-nicht-start.visustruct":S7,"../../lessons/diagramme/en/l11-note-loesung.visustruct":T7,"../../lessons/diagramme/en/l11-summe-loesung.visustruct":_7,"../../lessons/diagramme/en/l12-countdown-loesung.visustruct":A7,"../../lessons/diagramme/en/l12-einmaleins-loesung.visustruct":j7,"../../lessons/diagramme/en/l12-eins-bis-fuenf.visustruct":C7,"../../lessons/diagramme/en/l12-java-fehler-loesung.visustruct":D7,"../../lessons/diagramme/en/l12-java-fehler-start.visustruct":B7,"../../lessons/diagramme/en/l12-java-laeuft-nicht-loesung.visustruct":P7,"../../lessons/diagramme/en/l12-java-laeuft-nicht-start.visustruct":O7,"../../lessons/diagramme/en/l12-java-verdoppeln-loesung.visustruct":M7,"../../lessons/diagramme/en/l12-summe.visustruct":Z7,"../../lessons/diagramme/en/l13-dreieck.visustruct":L7,"../../lessons/diagramme/en/l13-gerade-eingaben-loesung.visustruct":W7,"../../lessons/diagramme/en/l13-gerade-zaehlen.visustruct":E7,"../../lessons/diagramme/en/l13-java-fehler-loesung.visustruct":N7,"../../lessons/diagramme/en/l13-java-fehler-start.visustruct":F7,"../../lessons/diagramme/en/l13-java-laeuft-nicht-loesung.visustruct":R7,"../../lessons/diagramme/en/l13-java-laeuft-nicht-start.visustruct":J7,"../../lessons/diagramme/en/l13-java-quadrate-loesung.visustruct":G7,"../../lessons/diagramme/en/l13-rechteck-loesung.visustruct":H7,"../../lessons/diagramme/en/l14-array-durchlaufen.visustruct":V7,"../../lessons/diagramme/en/l14-erster-letzter-loesung.visustruct":q7,"../../lessons/diagramme/en/l14-java-fehler-loesung.visustruct":U7,"../../lessons/diagramme/en/l14-java-fehler-start.visustruct":Q7,"../../lessons/diagramme/en/l14-java-laeuft-nicht-loesung.visustruct":$7,"../../lessons/diagramme/en/l14-java-laeuft-nicht-start.visustruct":Y7,"../../lessons/diagramme/en/l14-java-treppe-loesung.visustruct":K7,"../../lessons/diagramme/en/l14-namen-eingeben.visustruct":X7,"../../lessons/diagramme/en/l14-rueckwaerts-loesung.visustruct":e8,"../../lessons/diagramme/en/l14-summe-max-loesung.visustruct":t8,"../../lessons/diagramme/en/l14-summe-maximum.visustruct":n8,"../../lessons/diagramme/en/l15-bis-null-loesung.visustruct":i8,"../../lessons/diagramme/en/l15-endlos-aussprung.visustruct":a8,"../../lessons/diagramme/en/l15-flaeche-loesung.visustruct":s8,"../../lessons/diagramme/en/l15-flaeche-unterprogramm-loesung.visustruct":r8,"../../lessons/diagramme/en/l15-hauptprogramm.visustruct":o8,"../../lessons/diagramme/en/l15-java-fehler-loesung.visustruct":l8,"../../lessons/diagramme/en/l15-java-fehler-start.visustruct":u8,"../../lessons/diagramme/en/l15-java-laeuft-nicht-loesung.visustruct":m8,"../../lessons/diagramme/en/l15-java-laeuft-nicht-start.visustruct":c8,"../../lessons/diagramme/en/l15-java-noten-loesung.visustruct":d8,"../../lessons/diagramme/en/l15-quadrat-haupt.visustruct":h8,"../../lessons/diagramme/en/l15-quadrat.visustruct":g8,"../../lessons/diagramme/en/l15-titel-loesung.visustruct":p8,"../../lessons/diagramme/en/l15-unterprogramm.visustruct":f8,"../../lessons/diagramme/en/l15-zeige-summe-haupt.visustruct":b8,"../../lessons/diagramme/en/l15-zeige-summe.visustruct":x8,"../../lessons/diagramme/en/l16-countdown-loesung.visustruct":v8,"../../lessons/diagramme/en/l16-countdown-start.visustruct":w8,"../../lessons/diagramme/en/l16-java-fehler-loesung.visustruct":y8,"../../lessons/diagramme/en/l16-java-fehler-start.visustruct":z8,"../../lessons/diagramme/en/l16-java-laeuft-nicht-loesung.visustruct":I8,"../../lessons/diagramme/en/l16-java-laeuft-nicht-start.visustruct":k8,"../../lessons/diagramme/en/l16-java-punkte-loesung.visustruct":S8,"../../lessons/diagramme/en/l16-schreibtisch.visustruct":T8,"../../lessons/diagramme/en/l16-summe-loesung.visustruct":_8,"../../lessons/diagramme/en/l16-summe-start.visustruct":A8,"../../lessons/diagramme/en/l17-beispiel.visustruct":j8,"../../lessons/diagramme/en/l17-fakultaet-loesung.visustruct":C8,"../../lessons/diagramme/en/l17-java-brief-loesung.visustruct":D8,"../../lessons/diagramme/en/l17-java-brief-start.visustruct":B8,"../../lessons/diagramme/en/l17-java-fehler-loesung.visustruct":P8,"../../lessons/diagramme/en/l17-java-fehler-start.visustruct":O8,"../../lessons/diagramme/en/l17-java-laeuft-nicht-loesung.visustruct":M8,"../../lessons/diagramme/en/l17-java-laeuft-nicht-start.visustruct":Z8,"../../lessons/diagramme/en/l17-java-passwort-loesung.visustruct":L8,"../../lessons/diagramme/en/l17-ticket-loesung.visustruct":W8,"../../lessons/diagramme/en/l18-beispiel.visustruct":E8,"../../lessons/diagramme/en/l18-java-fehler-loesung.visustruct":N8,"../../lessons/diagramme/en/l18-java-fehler-start.visustruct":F8,"../../lessons/diagramme/en/l18-java-kasse-loesung.visustruct":R8,"../../lessons/diagramme/en/l18-js-loesung.visustruct":J8,"../../lessons/diagramme/en/l18-python-loesung.visustruct":G8,"../../lessons/diagramme/en/l19-java-fehler-loesung.visustruct":H8,"../../lessons/diagramme/en/l19-java-fehler-start.visustruct":V8,"../../lessons/diagramme/en/l19-java-laeuft-nicht-loesung.visustruct":q8,"../../lessons/diagramme/en/l19-java-laeuft-nicht-start.visustruct":U8,"../../lessons/diagramme/en/l19-java-zaehlen-loesung.visustruct":Q8,"../../lessons/diagramme/en/l2-ausgabe.visustruct":$8,"../../lessons/diagramme/en/l2-hallo-loesung.visustruct":Y8,"../../lessons/diagramme/en/l2-zwei-zeilen-loesung.visustruct":K8,"../../lessons/diagramme/en/l23-java-ergaenzen-loesung.visustruct":X8,"../../lessons/diagramme/en/l23-java-ergaenzen-start.visustruct":eS,"../../lessons/diagramme/en/l23-java-nummern-loesung.visustruct":tS,"../../lessons/diagramme/en/l23-java-nummern-start.visustruct":nS,"../../lessons/diagramme/en/l24-java-fuchs-loesung.visustruct":iS,"../../lessons/diagramme/en/l24-java-fuchs-start.visustruct":aS,"../../lessons/diagramme/en/l24-java-laengster-loesung.visustruct":sS,"../../lessons/diagramme/en/l24-java-laengster-start.visustruct":rS,"../../lessons/diagramme/en/l25-java-beste-loesung.visustruct":oS,"../../lessons/diagramme/en/l25-java-beste-start.visustruct":lS,"../../lessons/diagramme/en/l25-java-schnitt-loesung.visustruct":uS,"../../lessons/diagramme/en/l25-java-schnitt-start.visustruct":mS,"../../lessons/diagramme/en/l26-java-namen-loesung.visustruct":cS,"../../lessons/diagramme/en/l26-java-namen-start.visustruct":dS,"../../lessons/diagramme/en/l26-java-schreiben-loesung.visustruct":hS,"../../lessons/diagramme/en/l26-java-schreiben-start.visustruct":gS,"../../lessons/diagramme/en/l27-java-json-loesung.visustruct":pS,"../../lessons/diagramme/en/l27-java-json-start.visustruct":fS,"../../lessons/diagramme/en/l27-java-saetze-loesung.visustruct":bS,"../../lessons/diagramme/en/l27-java-saetze-start.visustruct":xS,"../../lessons/diagramme/en/sim-kino-loesung.visustruct":vS,"../../lessons/diagramme/en/sim-kino-start.visustruct":wS,"../../lessons/diagramme/en/sim-tausch-loesung.visustruct":yS,"../../lessons/diagramme/en/sim-tausch-start.visustruct":zS,"../../lessons/diagramme/en/sim-tausch.visustruct":IS,"../../lessons/diagramme/en/ss-absteigend-loesung.visustruct":kS,"../../lessons/diagramme/en/ss-absteigend-start.visustruct":SS,"../../lessons/diagramme/en/ss-binaere-suche.visustruct":TS,"../../lessons/diagramme/en/ss-bubblesort.visustruct":_S,"../../lessons/diagramme/en/ss-karte-suchen-loesung.visustruct":AS,"../../lessons/diagramme/en/ss-lineare-suche.visustruct":jS,"../../lessons/diagramme/en/uebergabe-bonus-loesung.visustruct":CS,"../../lessons/diagramme/en/uebergabe-bonus-start.visustruct":DS,"../../lessons/diagramme/en/uebergabe-bonus-unterprogramm-loesung.visustruct":BS,"../../lessons/diagramme/en/uebergabe-haupt.visustruct":PS,"../../lessons/diagramme/en/uebergabe-leeren.visustruct":OS,"../../lessons/diagramme/en/uebergabe-verdopple.visustruct":MS,"../../lessons/diagramme/en/vb-abstand-anzeigen-loesung.visustruct":ZS,"../../lessons/diagramme/en/vb-abstand-halten-loesung.visustruct":LS,"../../lessons/diagramme/en/vb-alarmanlage-loesung.visustruct":WS,"../../lessons/diagramme/en/vb-ampel-loesung.visustruct":ES,"../../lessons/diagramme/en/vb-ampel-zustand-loesung.visustruct":NS,"../../lessons/diagramme/en/vb-ausgang-loesung.visustruct":FS,"../../lessons/diagramme/en/vb-begruessung-loesung.visustruct":RS,"../../lessons/diagramme/en/vb-binaer-zaehlen-loesung.visustruct":JS,"../../lessons/diagramme/en/vb-bis-rot-loesung.visustruct":GS,"../../lessons/diagramme/en/vb-bis-wand-loesung.visustruct":HS,"../../lessons/diagramme/en/vb-bis-ziel-loesung.visustruct":VS,"../../lessons/diagramme/en/vb-buchstaben-loesung.visustruct":qS,"../../lessons/diagramme/en/vb-do-while-loesung.visustruct":US,"../../lessons/diagramme/en/vb-drei-lichter-loesung.visustruct":QS,"../../lessons/diagramme/en/vb-dreieck-loesung.visustruct":$S,"../../lessons/diagramme/en/vb-einparken-loesung.visustruct":YS,"../../lessons/diagramme/en/vb-erster-plan-loesung.visustruct":KS,"../../lessons/diagramme/en/vb-farben-loesung.visustruct":XS,"../../lessons/diagramme/en/vb-farbweiche-loesung.visustruct":eT,"../../lessons/diagramme/en/vb-fehler-04-sensoren-start.visustruct":tT,"../../lessons/diagramme/en/vb-fehler-05-bis-zur-wand-start.visustruct":nT,"../../lessons/diagramme/en/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct":iT,"../../lessons/diagramme/en/vb-fehler-05-zaehlen-und-summieren-start.visustruct":aT,"../../lessons/diagramme/en/vb-fehler-06-abstand-start.visustruct":sT,"../../lessons/diagramme/en/vb-fehler-06-konstanten-final-start.visustruct":rT,"../../lessons/diagramme/en/vb-fehler-06-nah-mittel-weit-start.visustruct":oT,"../../lessons/diagramme/en/vb-fehler-07-linie-start.visustruct":lT,"../../lessons/diagramme/en/vb-fehler-07-und-oder-start.visustruct":uT,"../../lessons/diagramme/en/vb-fehler-08-farben-start.visustruct":mT,"../../lessons/diagramme/en/vb-fehler-08-groesster-wert-start.visustruct":cT,"../../lessons/diagramme/en/vb-fehler-09-fallauswahl-mbot2-start.visustruct":dT,"../../lessons/diagramme/en/vb-fehler-10-tasten-joystick-start.visustruct":hT,"../../lessons/diagramme/en/vb-fehler-10-unterbrechen-mit-break-start.visustruct":gT,"../../lessons/diagramme/en/vb-fehler-11-licht-start.visustruct":pT,"../../lessons/diagramme/en/vb-fehler-11-merker-boolean-start.visustruct":fT,"../../lessons/diagramme/en/vb-fehler-12-lautstaerke-start.visustruct":bT,"../../lessons/diagramme/en/vb-fehler-13-neigung-start.visustruct":xT,"../../lessons/diagramme/en/vb-fehler-14-leds-start.visustruct":vT,"../../lessons/diagramme/en/vb-fehler-15-einzelne-leds-start.visustruct":wT,"../../lessons/diagramme/en/vb-fehler-16-lautsprecher-start.visustruct":yT,"../../lessons/diagramme/en/vb-fehler-17-arrays-melodie-start.visustruct":zT,"../../lessons/diagramme/en/vb-fehler-18-bildschirm-start.visustruct":IT,"../../lessons/diagramme/en/vb-fehler-19-zeichenketten-start.visustruct":kT,"../../lessons/diagramme/en/vb-fehler-20-motoren-start.visustruct":ST,"../../lessons/diagramme/en/vb-fehler-21-stoppuhr-akku-start.visustruct":TT,"../../lessons/diagramme/en/vb-fehler-21-zustaende-start.visustruct":_T,"../../lessons/diagramme/en/vb-fehler-22-eigene-methoden-start.visustruct":AT,"../../lessons/diagramme/en/vb-fehler-23-wie-geht-es-weiter-start.visustruct":jT,"../../lessons/diagramme/en/vb-gas-bis-wand-loesung.visustruct":CT,"../../lessons/diagramme/en/vb-gerade-loesung.visustruct":DT,"../../lessons/diagramme/en/vb-hindernis-loesung.visustruct":BT,"../../lessons/diagramme/en/vb-java-akku-loesung.visustruct":PT,"../../lessons/diagramme/en/vb-java-ampel-text-loesung.visustruct":OT,"../../lessons/diagramme/en/vb-java-blau-gruen-loesung.visustruct":MT,"../../lessons/diagramme/en/vb-java-blinken-loesung.visustruct":ZT,"../../lessons/diagramme/en/vb-java-countdown-loesung.visustruct":LT,"../../lessons/diagramme/en/vb-java-hoeher-loesung.visustruct":WT,"../../lessons/diagramme/en/vb-java-joystick-loesung.visustruct":ET,"../../lessons/diagramme/en/vb-java-klatsch-merker-loesung.visustruct":NT,"../../lessons/diagramme/en/vb-java-konstante-seite-loesung.visustruct":FT,"../../lessons/diagramme/en/vb-java-laermampel-loesung.visustruct":RT,"../../lessons/diagramme/en/vb-java-lauflicht-loesung.visustruct":JT,"../../lessons/diagramme/en/vb-java-linienfolger-loesung.visustruct":GT,"../../lessons/diagramme/en/vb-java-nachtlicht-ton-loesung.visustruct":HT,"../../lessons/diagramme/en/vb-java-rand-loesung.visustruct":VT,"../../lessons/diagramme/en/vb-java-rechteck-loesung.visustruct":qT,"../../lessons/diagramme/en/vb-java-rechteck-umdrehungen-loesung.visustruct":UT,"../../lessons/diagramme/en/vb-java-richtung-loesung.visustruct":QT,"../../lessons/diagramme/en/vb-java-schritte-zaehlen-loesung.visustruct":$T,"../../lessons/diagramme/en/vb-java-sicher-fahren-loesung.visustruct":YT,"../../lessons/diagramme/en/vb-java-spirale-strecke-loesung.visustruct":KT,"../../lessons/diagramme/en/vb-java-tageszeit-loesung.visustruct":XT,"../../lessons/diagramme/en/vb-java-taste-stopp-loesung.visustruct":e_,"../../lessons/diagramme/en/vb-java-textlaenge-loesung.visustruct":t_,"../../lessons/diagramme/en/vb-java-wand-piep-loesung.visustruct":n_,"../../lessons/diagramme/en/vb-java-wasserwaage-loesung.visustruct":i_,"../../lessons/diagramme/en/vb-klatschen-loesung.visustruct":a_,"../../lessons/diagramme/en/vb-knight-rider-loesung.visustruct":s_,"../../lessons/diagramme/en/vb-konstante-wand-loesung.visustruct":r_,"../../lessons/diagramme/en/vb-l00-bausteine-loesung.visustruct":o_,"../../lessons/diagramme/en/vb-l00-sequenz-loesung.visustruct":l_,"../../lessons/diagramme/en/vb-l03-wachsend-loesung.visustruct":u_,"../../lessons/diagramme/en/vb-l06k-mit.visustruct":m_,"../../lessons/diagramme/en/vb-l06k-ohne.visustruct":c_,"../../lessons/diagramme/en/vb-l06n-einparkhilfe.visustruct":d_,"../../lessons/diagramme/en/vb-l21z-ausweichen.visustruct":h_,"../../lessons/diagramme/en/vb-labyrinth-loesung.visustruct":g_,"../../lessons/diagramme/en/vb-laufschrift-loesung.visustruct":p_,"../../lessons/diagramme/en/vb-linienfolger-stopp-loesung.visustruct":f_,"../../lessons/diagramme/en/vb-linienmuster-loesung.visustruct":b_,"../../lessons/diagramme/en/vb-meister-platz-loesung.visustruct":x_,"../../lessons/diagramme/en/vb-melodie-loesung.visustruct":v_,"../../lessons/diagramme/en/vb-messreihe-loesung.visustruct":w_,"../../lessons/diagramme/en/vb-morsen-loesung.visustruct":y_,"../../lessons/diagramme/en/vb-nachtlicht-loesung.visustruct":z_,"../../lessons/diagramme/en/vb-nachtwaechter-loesung.visustruct":I_,"../../lessons/diagramme/en/vb-notbremse-loesung.visustruct":k_,"../../lessons/diagramme/en/vb-oder-loesung.visustruct":S_,"../../lessons/diagramme/en/vb-quadrat-loesung.visustruct":T_,"../../lessons/diagramme/en/vb-quadrat-methode-loesung.visustruct":__,"../../lessons/diagramme/en/vb-quadrat-schleife-loesung.visustruct":A_,"../../lessons/diagramme/en/vb-quadrat-unterprogramm-loesung.visustruct":j_,"../../lessons/diagramme/en/vb-quadrat-verschachtelt-loesung.visustruct":C_,"../../lessons/diagramme/en/vb-rand-loesung.visustruct":D_,"../../lessons/diagramme/en/vb-schraeglage-loesung.visustruct":B_,"../../lessons/diagramme/en/vb-schritte-welt-loesung.visustruct":P_,"../../lessons/diagramme/en/vb-slalom-loesung.visustruct":O_,"../../lessons/diagramme/en/vb-spirale-falsch-loesung.visustruct":M_,"../../lessons/diagramme/en/vb-spirale-falsch-start.visustruct":Z_,"../../lessons/diagramme/en/vb-spirale-loesung.visustruct":L_,"../../lessons/diagramme/en/vb-start-a-loesung.visustruct":W_,"../../lessons/diagramme/en/vb-startsignal-loesung.visustruct":E_,"../../lessons/diagramme/en/vb-strecke-anzeigen-loesung.visustruct":N_,"../../lessons/diagramme/en/vb-tempo-abstand-loesung.visustruct":F_,"../../lessons/diagramme/en/vb-tonleiter-loesung.visustruct":R_,"../../lessons/diagramme/en/vb-treppe-loesung.visustruct":J_,"../../lessons/diagramme/en/vb-tunnel-loesung.visustruct":G_,"../../lessons/diagramme/en/vb-ueberladung-umdrehung-loesung.visustruct":H_,"../../lessons/diagramme/en/vb-und-loesung.visustruct":V_,"../../lessons/diagramme/en/vb-variable-seite-loesung.visustruct":q_,"../../lessons/diagramme/en/vb-vier-quadrate-loesung.visustruct":U_,"../../lessons/diagramme/en/vb-wald-loesung.visustruct":Q_,"../../lessons/diagramme/en/vb-wand-vorn-loesung.visustruct":$_,"../../lessons/diagramme/en/vb-wurde-es-dunkel-loesung.visustruct":Y_,"../../lessons/diagramme/en/vb-zentimeter-loesung.visustruct":K_,"../../lessons/diagramme/en/vb-zentimeter-unterprogramm-loesung.visustruct":X_,"../../lessons/diagramme/en/vb-zwei-sekunden-loesung.visustruct":eA,"../../lessons/diagramme/en/zk-initialen-loesung.visustruct":tA,"../../lessons/diagramme/en/zk-mehr.visustruct":nA,"../../lessons/diagramme/en/zk-rueckwaerts-loesung.visustruct":iA,"../../lessons/diagramme/en/zk-split.visustruct":aA,"../../lessons/diagramme/en/zk-woerter-loesung.visustruct":sA,"../../lessons/diagramme/en/zk-zaehlen.visustruct":rA,"../../lessons/diagramme/en/zk-zeichen.visustruct":oA,"../../lessons/diagramme/kino-einmaleins-loesung.visustruct":lA,"../../lessons/diagramme/kino-frei-loesung.visustruct":uA,"../../lessons/diagramme/kino-frei-start.visustruct":mA,"../../lessons/diagramme/kino-karte-loesung.visustruct":cA,"../../lessons/diagramme/kino-karte-start.visustruct":dA,"../../lessons/diagramme/kino-saalplan.visustruct":hA,"../../lessons/diagramme/kino-verkaufen.visustruct":gA,"../../lessons/diagramme/l04-gross-klein-loesung.visustruct":pA,"../../lessons/diagramme/l04-gross-klein-start.visustruct":fA,"../../lessons/diagramme/l04-java-fehler-loesung.visustruct":bA,"../../lessons/diagramme/l04-java-fehler-start.visustruct":xA,"../../lessons/diagramme/l04-java-laeuft-nicht-loesung.visustruct":vA,"../../lessons/diagramme/l04-java-laeuft-nicht-start.visustruct":wA,"../../lessons/diagramme/l04-java-steckbrief-loesung.visustruct":yA,"../../lessons/diagramme/l04-rechnen.visustruct":zA,"../../lessons/diagramme/l04-reihenfolge-loesung.visustruct":IA,"../../lessons/diagramme/l04-reihenfolge-start.visustruct":kA,"../../lessons/diagramme/l04-tauschen-loesung.visustruct":SA,"../../lessons/diagramme/l04-tauschen-start.visustruct":TA,"../../lessons/diagramme/l04-tauschen.visustruct":_A,"../../lessons/diagramme/l05-datentypen.visustruct":AA,"../../lessons/diagramme/l05-division.visustruct":jA,"../../lessons/diagramme/l05-einkauf-loesung.visustruct":CA,"../../lessons/diagramme/l05-java-fehler-loesung.visustruct":DA,"../../lessons/diagramme/l05-java-fehler-start.visustruct":BA,"../../lessons/diagramme/l05-java-kino-loesung.visustruct":PA,"../../lessons/diagramme/l05-java-laeuft-nicht-loesung.visustruct":OA,"../../lessons/diagramme/l05-java-laeuft-nicht-start.visustruct":MA,"../../lessons/diagramme/l05-rechteck-loesung.visustruct":ZA,"../../lessons/diagramme/l05-sekunden-loesung.visustruct":LA,"../../lessons/diagramme/l05-sekunden-start.visustruct":WA,"../../lessons/diagramme/l05-sequenz.visustruct":EA,"../../lessons/diagramme/l06-begruessung-loesung.visustruct":NA,"../../lessons/diagramme/l06-eva.visustruct":FA,"../../lessons/diagramme/l06-java-fehler-loesung.visustruct":RA,"../../lessons/diagramme/l06-java-fehler-start.visustruct":JA,"../../lessons/diagramme/l06-java-laeuft-nicht-loesung.visustruct":GA,"../../lessons/diagramme/l06-java-laeuft-nicht-start.visustruct":HA,"../../lessons/diagramme/l06-java-tage-loesung.visustruct":VA,"../../lessons/diagramme/l06-mittelwert-loesung.visustruct":qA,"../../lessons/diagramme/l06-rechner-loesung.visustruct":UA,"../../lessons/diagramme/l07-alter-pruefen.visustruct":QA,"../../lessons/diagramme/l07-betrag-loesung.visustruct":$A,"../../lessons/diagramme/l07-gerade-loesung.visustruct":YA,"../../lessons/diagramme/l07-java-fehler-loesung.visustruct":KA,"../../lessons/diagramme/l07-java-fehler-start.visustruct":XA,"../../lessons/diagramme/l07-java-laeuft-nicht-loesung.visustruct":ej,"../../lessons/diagramme/l07-java-laeuft-nicht-start.visustruct":tj,"../../lessons/diagramme/l07-java-pizza-loesung.visustruct":nj,"../../lessons/diagramme/l07-java-pizza-start.visustruct":ij,"../../lessons/diagramme/l07-maximum-loesung.visustruct":aj,"../../lessons/diagramme/l07-rabatt.visustruct":sj,"../../lessons/diagramme/l08-gueltig-loesung.visustruct":rj,"../../lessons/diagramme/l08-java-bestanden-loesung.visustruct":oj,"../../lessons/diagramme/l08-java-fehler-loesung.visustruct":lj,"../../lessons/diagramme/l08-java-fehler-start.visustruct":uj,"../../lessons/diagramme/l08-java-laeuft-nicht-loesung.visustruct":mj,"../../lessons/diagramme/l08-java-laeuft-nicht-start.visustruct":cj,"../../lessons/diagramme/l08-jugendticket.visustruct":dj,"../../lessons/diagramme/l08-note.visustruct":hj,"../../lessons/diagramme/l08-schaltjahr-loesung.visustruct":gj,"../../lessons/diagramme/l08-wochenende-loesung.visustruct":pj,"../../lessons/diagramme/l09-ampel.visustruct":fj,"../../lessons/diagramme/l09-automat.visustruct":bj,"../../lessons/diagramme/l09-java-fehler-loesung.visustruct":xj,"../../lessons/diagramme/l09-java-fehler-start.visustruct":vj,"../../lessons/diagramme/l09-java-laeuft-nicht-loesung.visustruct":wj,"../../lessons/diagramme/l09-java-laeuft-nicht-start.visustruct":yj,"../../lessons/diagramme/l09-java-schule-loesung.visustruct":zj,"../../lessons/diagramme/l09-note-loesung.visustruct":Ij,"../../lessons/diagramme/l09-rechner-loesung.visustruct":kj,"../../lessons/diagramme/l09-verschachtelt.visustruct":Sj,"../../lessons/diagramme/l1-grundbausteine.visustruct":Tj,"../../lessons/diagramme/l1-sequenz.visustruct":_j,"../../lessons/diagramme/l1-tee.visustruct":Aj,"../../lessons/diagramme/l1-verzweigung.visustruct":jj,"../../lessons/diagramme/l1-wiederholung.visustruct":Cj,"../../lessons/diagramme/l10-bis-n-loesung.visustruct":Dj,"../../lessons/diagramme/l10-countdown.visustruct":Bj,"../../lessons/diagramme/l10-endlos.visustruct":Pj,"../../lessons/diagramme/l10-fakultaet.visustruct":Oj,"../../lessons/diagramme/l10-java-fehler-loesung.visustruct":Mj,"../../lessons/diagramme/l10-java-fehler-start.visustruct":Zj,"../../lessons/diagramme/l10-java-laeuft-nicht-loesung.visustruct":Lj,"../../lessons/diagramme/l10-java-laeuft-nicht-start.visustruct":Wj,"../../lessons/diagramme/l10-java-wuerfel-loesung.visustruct":Ej,"../../lessons/diagramme/l10-verdoppeln-loesung.visustruct":Nj,"../../lessons/diagramme/l11-eingabe-pruefen.visustruct":Fj,"../../lessons/diagramme/l11-einmal.visustruct":Rj,"../../lessons/diagramme/l11-java-fehler-loesung.visustruct":Jj,"../../lessons/diagramme/l11-java-fehler-start.visustruct":Gj,"../../lessons/diagramme/l11-java-gerade-loesung.visustruct":Hj,"../../lessons/diagramme/l11-java-laeuft-nicht-loesung.visustruct":Vj,"../../lessons/diagramme/l11-java-laeuft-nicht-start.visustruct":qj,"../../lessons/diagramme/l11-note-loesung.visustruct":Uj,"../../lessons/diagramme/l11-summe-loesung.visustruct":Qj,"../../lessons/diagramme/l12-countdown-loesung.visustruct":$j,"../../lessons/diagramme/l12-einmaleins-loesung.visustruct":Yj,"../../lessons/diagramme/l12-eins-bis-fuenf.visustruct":Kj,"../../lessons/diagramme/l12-java-fehler-loesung.visustruct":Xj,"../../lessons/diagramme/l12-java-fehler-start.visustruct":eC,"../../lessons/diagramme/l12-java-laeuft-nicht-loesung.visustruct":tC,"../../lessons/diagramme/l12-java-laeuft-nicht-start.visustruct":nC,"../../lessons/diagramme/l12-java-verdoppeln-loesung.visustruct":iC,"../../lessons/diagramme/l12-summe.visustruct":aC,"../../lessons/diagramme/l13-dreieck.visustruct":sC,"../../lessons/diagramme/l13-gerade-eingaben-loesung.visustruct":rC,"../../lessons/diagramme/l13-gerade-zaehlen.visustruct":oC,"../../lessons/diagramme/l13-java-fehler-loesung.visustruct":lC,"../../lessons/diagramme/l13-java-fehler-start.visustruct":uC,"../../lessons/diagramme/l13-java-laeuft-nicht-loesung.visustruct":mC,"../../lessons/diagramme/l13-java-laeuft-nicht-start.visustruct":cC,"../../lessons/diagramme/l13-java-quadrate-loesung.visustruct":dC,"../../lessons/diagramme/l13-rechteck-loesung.visustruct":hC,"../../lessons/diagramme/l14-array-durchlaufen.visustruct":gC,"../../lessons/diagramme/l14-erster-letzter-loesung.visustruct":pC,"../../lessons/diagramme/l14-java-fehler-loesung.visustruct":fC,"../../lessons/diagramme/l14-java-fehler-start.visustruct":bC,"../../lessons/diagramme/l14-java-laeuft-nicht-loesung.visustruct":xC,"../../lessons/diagramme/l14-java-laeuft-nicht-start.visustruct":vC,"../../lessons/diagramme/l14-java-treppe-loesung.visustruct":wC,"../../lessons/diagramme/l14-namen-eingeben.visustruct":yC,"../../lessons/diagramme/l14-rueckwaerts-loesung.visustruct":zC,"../../lessons/diagramme/l14-summe-max-loesung.visustruct":IC,"../../lessons/diagramme/l14-summe-maximum.visustruct":kC,"../../lessons/diagramme/l15-bis-null-loesung.visustruct":SC,"../../lessons/diagramme/l15-endlos-aussprung.visustruct":TC,"../../lessons/diagramme/l15-flaeche-loesung.visustruct":_C,"../../lessons/diagramme/l15-flaeche-unterprogramm-loesung.visustruct":AC,"../../lessons/diagramme/l15-hauptprogramm.visustruct":jC,"../../lessons/diagramme/l15-java-fehler-loesung.visustruct":CC,"../../lessons/diagramme/l15-java-fehler-start.visustruct":DC,"../../lessons/diagramme/l15-java-laeuft-nicht-loesung.visustruct":BC,"../../lessons/diagramme/l15-java-laeuft-nicht-start.visustruct":PC,"../../lessons/diagramme/l15-java-noten-loesung.visustruct":OC,"../../lessons/diagramme/l15-quadrat-haupt.visustruct":MC,"../../lessons/diagramme/l15-quadrat.visustruct":ZC,"../../lessons/diagramme/l15-titel-loesung.visustruct":LC,"../../lessons/diagramme/l15-unterprogramm.visustruct":WC,"../../lessons/diagramme/l15-zeige-summe-haupt.visustruct":EC,"../../lessons/diagramme/l15-zeige-summe.visustruct":NC,"../../lessons/diagramme/l16-countdown-loesung.visustruct":FC,"../../lessons/diagramme/l16-countdown-start.visustruct":RC,"../../lessons/diagramme/l16-java-fehler-loesung.visustruct":JC,"../../lessons/diagramme/l16-java-fehler-start.visustruct":GC,"../../lessons/diagramme/l16-java-laeuft-nicht-loesung.visustruct":HC,"../../lessons/diagramme/l16-java-laeuft-nicht-start.visustruct":VC,"../../lessons/diagramme/l16-java-punkte-loesung.visustruct":qC,"../../lessons/diagramme/l16-schreibtisch.visustruct":UC,"../../lessons/diagramme/l16-summe-loesung.visustruct":QC,"../../lessons/diagramme/l16-summe-start.visustruct":$C,"../../lessons/diagramme/l17-beispiel.visustruct":YC,"../../lessons/diagramme/l17-fakultaet-loesung.visustruct":KC,"../../lessons/diagramme/l17-java-brief-loesung.visustruct":XC,"../../lessons/diagramme/l17-java-brief-start.visustruct":eD,"../../lessons/diagramme/l17-java-fehler-loesung.visustruct":tD,"../../lessons/diagramme/l17-java-fehler-start.visustruct":nD,"../../lessons/diagramme/l17-java-laeuft-nicht-loesung.visustruct":iD,"../../lessons/diagramme/l17-java-laeuft-nicht-start.visustruct":aD,"../../lessons/diagramme/l17-java-passwort-loesung.visustruct":sD,"../../lessons/diagramme/l17-ticket-loesung.visustruct":rD,"../../lessons/diagramme/l18-beispiel.visustruct":oD,"../../lessons/diagramme/l18-java-fehler-loesung.visustruct":lD,"../../lessons/diagramme/l18-java-fehler-start.visustruct":uD,"../../lessons/diagramme/l18-java-kasse-loesung.visustruct":mD,"../../lessons/diagramme/l18-js-loesung.visustruct":cD,"../../lessons/diagramme/l18-python-loesung.visustruct":dD,"../../lessons/diagramme/l19-java-fehler-loesung.visustruct":hD,"../../lessons/diagramme/l19-java-fehler-start.visustruct":gD,"../../lessons/diagramme/l19-java-laeuft-nicht-loesung.visustruct":pD,"../../lessons/diagramme/l19-java-laeuft-nicht-start.visustruct":fD,"../../lessons/diagramme/l19-java-zaehlen-loesung.visustruct":bD,"../../lessons/diagramme/l2-ausgabe.visustruct":xD,"../../lessons/diagramme/l2-hallo-loesung.visustruct":vD,"../../lessons/diagramme/l2-zwei-zeilen-loesung.visustruct":wD,"../../lessons/diagramme/l20-countdown-loesung.visustruct":yD,"../../lessons/diagramme/l23-java-ergaenzen-loesung.visustruct":zD,"../../lessons/diagramme/l23-java-ergaenzen-start.visustruct":ID,"../../lessons/diagramme/l23-java-nummern-loesung.visustruct":kD,"../../lessons/diagramme/l23-java-nummern-start.visustruct":SD,"../../lessons/diagramme/l24-java-fuchs-loesung.visustruct":TD,"../../lessons/diagramme/l24-java-fuchs-start.visustruct":_D,"../../lessons/diagramme/l24-java-laengster-loesung.visustruct":AD,"../../lessons/diagramme/l24-java-laengster-start.visustruct":jD,"../../lessons/diagramme/l25-java-beste-loesung.visustruct":CD,"../../lessons/diagramme/l25-java-beste-start.visustruct":DD,"../../lessons/diagramme/l25-java-schnitt-loesung.visustruct":BD,"../../lessons/diagramme/l25-java-schnitt-start.visustruct":PD,"../../lessons/diagramme/l26-java-namen-loesung.visustruct":OD,"../../lessons/diagramme/l26-java-namen-start.visustruct":MD,"../../lessons/diagramme/l26-java-schreiben-loesung.visustruct":ZD,"../../lessons/diagramme/l26-java-schreiben-start.visustruct":LD,"../../lessons/diagramme/l27-java-json-loesung.visustruct":WD,"../../lessons/diagramme/l27-java-json-start.visustruct":ED,"../../lessons/diagramme/l27-java-saetze-loesung.visustruct":ND,"../../lessons/diagramme/l27-java-saetze-start.visustruct":FD,"../../lessons/diagramme/pt/aufgabe-alter-haupt-loesung.visustruct":RD,"../../lessons/diagramme/pt/aufgabe-alter-unterprogramm-loesung.visustruct":JD,"../../lessons/diagramme/pt/aufgabe-ballwurf-loesung.visustruct":GD,"../../lessons/diagramme/pt/aufgabe-ballwurf-start.visustruct":HD,"../../lessons/diagramme/pt/aufgabe-bestenliste-loesung.visustruct":VD,"../../lessons/diagramme/pt/aufgabe-binaer-loesung.visustruct":qD,"../../lessons/diagramme/pt/aufgabe-caesar-array-loesung.visustruct":UD,"../../lessons/diagramme/pt/aufgabe-caesar-array-start.visustruct":QD,"../../lessons/diagramme/pt/aufgabe-caesar-ascii-loesung.visustruct":$D,"../../lessons/diagramme/pt/aufgabe-caesar-entschluesseln-loesung.visustruct":YD,"../../lessons/diagramme/pt/aufgabe-collatz-loesung.visustruct":KD,"../../lessons/diagramme/pt/aufgabe-durchschnitt-loesung.visustruct":XD,"../../lessons/diagramme/pt/aufgabe-fibonacci-loesung.visustruct":eB,"../../lessons/diagramme/pt/aufgabe-gerade-loesung.visustruct":tB,"../../lessons/diagramme/pt/aufgabe-mehrwertsteuer-loesung.visustruct":nB,"../../lessons/diagramme/pt/aufgabe-pin-java-loesung.visustruct":iB,"../../lessons/diagramme/pt/aufgabe-pin-loesung.visustruct":aB,"../../lessons/diagramme/pt/aufgabe-rechner-loesung.visustruct":sB,"../../lessons/diagramme/pt/aufgabe-schiffe-loesung.visustruct":rB,"../../lessons/diagramme/pt/aufgabe-schiffe-start.visustruct":oB,"../../lessons/diagramme/pt/aufgabe-speisekarte-loesung.visustruct":lB,"../../lessons/diagramme/pt/aufgabe-summe-loesung.visustruct":uB,"../../lessons/diagramme/pt/aufgabe-tafeldienst-loesung.visustruct":mB,"../../lessons/diagramme/pt/aufgabe-verdoppeln-loesung.visustruct":cB,"../../lessons/diagramme/pt/aufgabe-verleih-java-loesung.visustruct":dB,"../../lessons/diagramme/pt/aufgabe-verleih-loesung.visustruct":hB,"../../lessons/diagramme/pt/aufgabe-vokabeln-loesung.visustruct":gB,"../../lessons/diagramme/pt/aufgabe-vokabeln-start.visustruct":pB,"../../lessons/diagramme/pt/aufgabe-volljaehrig-loesung.visustruct":fB,"../../lessons/diagramme/pt/dt-buffered.visustruct":bB,"../../lessons/diagramme/pt/dt-csv.visustruct":xB,"../../lessons/diagramme/pt/dt-json.visustruct":vB,"../../lessons/diagramme/pt/dt-lesen.visustruct":wB,"../../lessons/diagramme/pt/kino-einmaleins-loesung.visustruct":yB,"../../lessons/diagramme/pt/kino-frei-loesung.visustruct":zB,"../../lessons/diagramme/pt/kino-frei-start.visustruct":IB,"../../lessons/diagramme/pt/kino-karte-loesung.visustruct":kB,"../../lessons/diagramme/pt/kino-karte-start.visustruct":SB,"../../lessons/diagramme/pt/kino-saalplan.visustruct":TB,"../../lessons/diagramme/pt/kino-verkaufen.visustruct":_B,"../../lessons/diagramme/pt/l04-gross-klein-loesung.visustruct":AB,"../../lessons/diagramme/pt/l04-gross-klein-start.visustruct":jB,"../../lessons/diagramme/pt/l04-java-fehler-loesung.visustruct":CB,"../../lessons/diagramme/pt/l04-java-fehler-start.visustruct":DB,"../../lessons/diagramme/pt/l04-java-laeuft-nicht-loesung.visustruct":BB,"../../lessons/diagramme/pt/l04-java-laeuft-nicht-start.visustruct":PB,"../../lessons/diagramme/pt/l04-java-steckbrief-loesung.visustruct":OB,"../../lessons/diagramme/pt/l04-rechnen.visustruct":MB,"../../lessons/diagramme/pt/l04-reihenfolge-loesung.visustruct":ZB,"../../lessons/diagramme/pt/l04-reihenfolge-start.visustruct":LB,"../../lessons/diagramme/pt/l04-tauschen-loesung.visustruct":WB,"../../lessons/diagramme/pt/l04-tauschen-start.visustruct":EB,"../../lessons/diagramme/pt/l04-tauschen.visustruct":NB,"../../lessons/diagramme/pt/l05-datentypen.visustruct":FB,"../../lessons/diagramme/pt/l05-division.visustruct":RB,"../../lessons/diagramme/pt/l05-einkauf-loesung.visustruct":JB,"../../lessons/diagramme/pt/l05-java-fehler-loesung.visustruct":GB,"../../lessons/diagramme/pt/l05-java-fehler-start.visustruct":HB,"../../lessons/diagramme/pt/l05-java-kino-loesung.visustruct":VB,"../../lessons/diagramme/pt/l05-java-laeuft-nicht-loesung.visustruct":qB,"../../lessons/diagramme/pt/l05-java-laeuft-nicht-start.visustruct":UB,"../../lessons/diagramme/pt/l05-rechteck-loesung.visustruct":QB,"../../lessons/diagramme/pt/l05-sekunden-loesung.visustruct":$B,"../../lessons/diagramme/pt/l05-sekunden-start.visustruct":YB,"../../lessons/diagramme/pt/l05-sequenz.visustruct":KB,"../../lessons/diagramme/pt/l06-begruessung-loesung.visustruct":XB,"../../lessons/diagramme/pt/l06-eva.visustruct":eP,"../../lessons/diagramme/pt/l06-java-fehler-loesung.visustruct":tP,"../../lessons/diagramme/pt/l06-java-fehler-start.visustruct":nP,"../../lessons/diagramme/pt/l06-java-laeuft-nicht-loesung.visustruct":iP,"../../lessons/diagramme/pt/l06-java-laeuft-nicht-start.visustruct":aP,"../../lessons/diagramme/pt/l06-java-tage-loesung.visustruct":sP,"../../lessons/diagramme/pt/l06-mittelwert-loesung.visustruct":rP,"../../lessons/diagramme/pt/l06-rechner-loesung.visustruct":oP,"../../lessons/diagramme/pt/l07-alter-pruefen.visustruct":lP,"../../lessons/diagramme/pt/l07-betrag-loesung.visustruct":uP,"../../lessons/diagramme/pt/l07-gerade-loesung.visustruct":mP,"../../lessons/diagramme/pt/l07-java-fehler-loesung.visustruct":cP,"../../lessons/diagramme/pt/l07-java-fehler-start.visustruct":dP,"../../lessons/diagramme/pt/l07-java-laeuft-nicht-loesung.visustruct":hP,"../../lessons/diagramme/pt/l07-java-laeuft-nicht-start.visustruct":gP,"../../lessons/diagramme/pt/l07-java-pizza-loesung.visustruct":pP,"../../lessons/diagramme/pt/l07-java-pizza-start.visustruct":fP,"../../lessons/diagramme/pt/l07-maximum-loesung.visustruct":bP,"../../lessons/diagramme/pt/l07-rabatt.visustruct":xP,"../../lessons/diagramme/pt/l08-gueltig-loesung.visustruct":vP,"../../lessons/diagramme/pt/l08-java-bestanden-loesung.visustruct":wP,"../../lessons/diagramme/pt/l08-java-fehler-loesung.visustruct":yP,"../../lessons/diagramme/pt/l08-java-fehler-start.visustruct":zP,"../../lessons/diagramme/pt/l08-java-laeuft-nicht-loesung.visustruct":IP,"../../lessons/diagramme/pt/l08-java-laeuft-nicht-start.visustruct":kP,"../../lessons/diagramme/pt/l08-jugendticket.visustruct":SP,"../../lessons/diagramme/pt/l08-note.visustruct":TP,"../../lessons/diagramme/pt/l08-schaltjahr-loesung.visustruct":_P,"../../lessons/diagramme/pt/l08-wochenende-loesung.visustruct":AP,"../../lessons/diagramme/pt/l09-ampel.visustruct":jP,"../../lessons/diagramme/pt/l09-automat.visustruct":CP,"../../lessons/diagramme/pt/l09-java-fehler-loesung.visustruct":DP,"../../lessons/diagramme/pt/l09-java-fehler-start.visustruct":BP,"../../lessons/diagramme/pt/l09-java-laeuft-nicht-loesung.visustruct":PP,"../../lessons/diagramme/pt/l09-java-laeuft-nicht-start.visustruct":OP,"../../lessons/diagramme/pt/l09-java-schule-loesung.visustruct":MP,"../../lessons/diagramme/pt/l09-note-loesung.visustruct":ZP,"../../lessons/diagramme/pt/l09-rechner-loesung.visustruct":LP,"../../lessons/diagramme/pt/l09-verschachtelt.visustruct":WP,"../../lessons/diagramme/pt/l1-grundbausteine.visustruct":EP,"../../lessons/diagramme/pt/l1-sequenz.visustruct":NP,"../../lessons/diagramme/pt/l1-tee.visustruct":FP,"../../lessons/diagramme/pt/l1-verzweigung.visustruct":RP,"../../lessons/diagramme/pt/l1-wiederholung.visustruct":JP,"../../lessons/diagramme/pt/l10-bis-n-loesung.visustruct":GP,"../../lessons/diagramme/pt/l10-countdown.visustruct":HP,"../../lessons/diagramme/pt/l10-endlos.visustruct":VP,"../../lessons/diagramme/pt/l10-fakultaet.visustruct":qP,"../../lessons/diagramme/pt/l10-java-fehler-loesung.visustruct":UP,"../../lessons/diagramme/pt/l10-java-fehler-start.visustruct":QP,"../../lessons/diagramme/pt/l10-java-laeuft-nicht-loesung.visustruct":$P,"../../lessons/diagramme/pt/l10-java-laeuft-nicht-start.visustruct":YP,"../../lessons/diagramme/pt/l10-java-wuerfel-loesung.visustruct":KP,"../../lessons/diagramme/pt/l10-verdoppeln-loesung.visustruct":XP,"../../lessons/diagramme/pt/l11-eingabe-pruefen.visustruct":eO,"../../lessons/diagramme/pt/l11-einmal.visustruct":tO,"../../lessons/diagramme/pt/l11-java-fehler-loesung.visustruct":nO,"../../lessons/diagramme/pt/l11-java-fehler-start.visustruct":iO,"../../lessons/diagramme/pt/l11-java-gerade-loesung.visustruct":aO,"../../lessons/diagramme/pt/l11-java-laeuft-nicht-loesung.visustruct":sO,"../../lessons/diagramme/pt/l11-java-laeuft-nicht-start.visustruct":rO,"../../lessons/diagramme/pt/l11-note-loesung.visustruct":oO,"../../lessons/diagramme/pt/l11-summe-loesung.visustruct":lO,"../../lessons/diagramme/pt/l12-countdown-loesung.visustruct":uO,"../../lessons/diagramme/pt/l12-einmaleins-loesung.visustruct":mO,"../../lessons/diagramme/pt/l12-eins-bis-fuenf.visustruct":cO,"../../lessons/diagramme/pt/l12-java-fehler-loesung.visustruct":dO,"../../lessons/diagramme/pt/l12-java-fehler-start.visustruct":hO,"../../lessons/diagramme/pt/l12-java-laeuft-nicht-loesung.visustruct":gO,"../../lessons/diagramme/pt/l12-java-laeuft-nicht-start.visustruct":pO,"../../lessons/diagramme/pt/l12-java-verdoppeln-loesung.visustruct":fO,"../../lessons/diagramme/pt/l12-summe.visustruct":bO,"../../lessons/diagramme/pt/l13-dreieck.visustruct":xO,"../../lessons/diagramme/pt/l13-gerade-eingaben-loesung.visustruct":vO,"../../lessons/diagramme/pt/l13-gerade-zaehlen.visustruct":wO,"../../lessons/diagramme/pt/l13-java-fehler-loesung.visustruct":yO,"../../lessons/diagramme/pt/l13-java-fehler-start.visustruct":zO,"../../lessons/diagramme/pt/l13-java-laeuft-nicht-loesung.visustruct":IO,"../../lessons/diagramme/pt/l13-java-laeuft-nicht-start.visustruct":kO,"../../lessons/diagramme/pt/l13-java-quadrate-loesung.visustruct":SO,"../../lessons/diagramme/pt/l13-rechteck-loesung.visustruct":TO,"../../lessons/diagramme/pt/l14-array-durchlaufen.visustruct":_O,"../../lessons/diagramme/pt/l14-erster-letzter-loesung.visustruct":AO,"../../lessons/diagramme/pt/l14-java-fehler-loesung.visustruct":jO,"../../lessons/diagramme/pt/l14-java-fehler-start.visustruct":CO,"../../lessons/diagramme/pt/l14-java-laeuft-nicht-loesung.visustruct":DO,"../../lessons/diagramme/pt/l14-java-laeuft-nicht-start.visustruct":BO,"../../lessons/diagramme/pt/l14-java-treppe-loesung.visustruct":PO,"../../lessons/diagramme/pt/l14-namen-eingeben.visustruct":OO,"../../lessons/diagramme/pt/l14-rueckwaerts-loesung.visustruct":MO,"../../lessons/diagramme/pt/l14-summe-max-loesung.visustruct":ZO,"../../lessons/diagramme/pt/l14-summe-maximum.visustruct":LO,"../../lessons/diagramme/pt/l15-bis-null-loesung.visustruct":WO,"../../lessons/diagramme/pt/l15-endlos-aussprung.visustruct":EO,"../../lessons/diagramme/pt/l15-flaeche-loesung.visustruct":NO,"../../lessons/diagramme/pt/l15-hauptprogramm.visustruct":FO,"../../lessons/diagramme/pt/l15-java-fehler-loesung.visustruct":RO,"../../lessons/diagramme/pt/l15-java-fehler-start.visustruct":JO,"../../lessons/diagramme/pt/l15-java-laeuft-nicht-loesung.visustruct":GO,"../../lessons/diagramme/pt/l15-java-laeuft-nicht-start.visustruct":HO,"../../lessons/diagramme/pt/l15-java-noten-loesung.visustruct":VO,"../../lessons/diagramme/pt/l15-quadrat-haupt.visustruct":qO,"../../lessons/diagramme/pt/l15-quadrat.visustruct":UO,"../../lessons/diagramme/pt/l15-titel-loesung.visustruct":QO,"../../lessons/diagramme/pt/l15-unterprogramm.visustruct":$O,"../../lessons/diagramme/pt/l15-zeige-summe-haupt.visustruct":YO,"../../lessons/diagramme/pt/l15-zeige-summe.visustruct":KO,"../../lessons/diagramme/pt/l16-countdown-loesung.visustruct":XO,"../../lessons/diagramme/pt/l16-countdown-start.visustruct":eM,"../../lessons/diagramme/pt/l16-java-fehler-loesung.visustruct":tM,"../../lessons/diagramme/pt/l16-java-fehler-start.visustruct":nM,"../../lessons/diagramme/pt/l16-java-laeuft-nicht-loesung.visustruct":iM,"../../lessons/diagramme/pt/l16-java-laeuft-nicht-start.visustruct":aM,"../../lessons/diagramme/pt/l16-java-punkte-loesung.visustruct":sM,"../../lessons/diagramme/pt/l16-schreibtisch.visustruct":rM,"../../lessons/diagramme/pt/l16-summe-loesung.visustruct":oM,"../../lessons/diagramme/pt/l16-summe-start.visustruct":lM,"../../lessons/diagramme/pt/l17-beispiel.visustruct":uM,"../../lessons/diagramme/pt/l17-fakultaet-loesung.visustruct":mM,"../../lessons/diagramme/pt/l17-java-brief-loesung.visustruct":cM,"../../lessons/diagramme/pt/l17-java-brief-start.visustruct":dM,"../../lessons/diagramme/pt/l17-java-fehler-loesung.visustruct":hM,"../../lessons/diagramme/pt/l17-java-fehler-start.visustruct":gM,"../../lessons/diagramme/pt/l17-java-laeuft-nicht-loesung.visustruct":pM,"../../lessons/diagramme/pt/l17-java-laeuft-nicht-start.visustruct":fM,"../../lessons/diagramme/pt/l17-java-passwort-loesung.visustruct":bM,"../../lessons/diagramme/pt/l17-ticket-loesung.visustruct":xM,"../../lessons/diagramme/pt/l18-beispiel.visustruct":vM,"../../lessons/diagramme/pt/l18-java-fehler-loesung.visustruct":wM,"../../lessons/diagramme/pt/l18-java-fehler-start.visustruct":yM,"../../lessons/diagramme/pt/l18-java-kasse-loesung.visustruct":zM,"../../lessons/diagramme/pt/l18-js-loesung.visustruct":IM,"../../lessons/diagramme/pt/l18-python-loesung.visustruct":kM,"../../lessons/diagramme/pt/l19-java-fehler-loesung.visustruct":SM,"../../lessons/diagramme/pt/l19-java-fehler-start.visustruct":TM,"../../lessons/diagramme/pt/l19-java-laeuft-nicht-loesung.visustruct":_M,"../../lessons/diagramme/pt/l19-java-laeuft-nicht-start.visustruct":AM,"../../lessons/diagramme/pt/l19-java-zaehlen-loesung.visustruct":jM,"../../lessons/diagramme/pt/l2-ausgabe.visustruct":CM,"../../lessons/diagramme/pt/l2-hallo-loesung.visustruct":DM,"../../lessons/diagramme/pt/l2-zwei-zeilen-loesung.visustruct":BM,"../../lessons/diagramme/pt/l23-java-ergaenzen-loesung.visustruct":PM,"../../lessons/diagramme/pt/l23-java-ergaenzen-start.visustruct":OM,"../../lessons/diagramme/pt/l23-java-nummern-loesung.visustruct":MM,"../../lessons/diagramme/pt/l23-java-nummern-start.visustruct":ZM,"../../lessons/diagramme/pt/l24-java-fuchs-loesung.visustruct":LM,"../../lessons/diagramme/pt/l24-java-fuchs-start.visustruct":WM,"../../lessons/diagramme/pt/l24-java-laengster-loesung.visustruct":EM,"../../lessons/diagramme/pt/l24-java-laengster-start.visustruct":NM,"../../lessons/diagramme/pt/l25-java-beste-loesung.visustruct":FM,"../../lessons/diagramme/pt/l25-java-beste-start.visustruct":RM,"../../lessons/diagramme/pt/l25-java-schnitt-loesung.visustruct":JM,"../../lessons/diagramme/pt/l25-java-schnitt-start.visustruct":GM,"../../lessons/diagramme/pt/l26-java-namen-loesung.visustruct":HM,"../../lessons/diagramme/pt/l26-java-namen-start.visustruct":VM,"../../lessons/diagramme/pt/l26-java-schreiben-loesung.visustruct":qM,"../../lessons/diagramme/pt/l26-java-schreiben-start.visustruct":UM,"../../lessons/diagramme/pt/l27-java-json-loesung.visustruct":QM,"../../lessons/diagramme/pt/l27-java-json-start.visustruct":$M,"../../lessons/diagramme/pt/l27-java-saetze-loesung.visustruct":YM,"../../lessons/diagramme/pt/l27-java-saetze-start.visustruct":KM,"../../lessons/diagramme/pt/sim-kino-loesung.visustruct":XM,"../../lessons/diagramme/pt/sim-kino-start.visustruct":eZ,"../../lessons/diagramme/pt/sim-tausch-loesung.visustruct":tZ,"../../lessons/diagramme/pt/sim-tausch-start.visustruct":nZ,"../../lessons/diagramme/pt/sim-tausch.visustruct":iZ,"../../lessons/diagramme/pt/ss-absteigend-loesung.visustruct":aZ,"../../lessons/diagramme/pt/ss-absteigend-start.visustruct":sZ,"../../lessons/diagramme/pt/ss-binaere-suche.visustruct":rZ,"../../lessons/diagramme/pt/ss-bubblesort.visustruct":oZ,"../../lessons/diagramme/pt/ss-karte-suchen-loesung.visustruct":lZ,"../../lessons/diagramme/pt/ss-lineare-suche.visustruct":uZ,"../../lessons/diagramme/pt/uebergabe-bonus-loesung.visustruct":mZ,"../../lessons/diagramme/pt/uebergabe-bonus-start.visustruct":cZ,"../../lessons/diagramme/pt/uebergabe-bonus-unterprogramm-loesung.visustruct":dZ,"../../lessons/diagramme/pt/uebergabe-haupt.visustruct":hZ,"../../lessons/diagramme/pt/uebergabe-leeren.visustruct":gZ,"../../lessons/diagramme/pt/uebergabe-verdopple.visustruct":pZ,"../../lessons/diagramme/pt/zk-initialen-loesung.visustruct":fZ,"../../lessons/diagramme/pt/zk-mehr.visustruct":bZ,"../../lessons/diagramme/pt/zk-rueckwaerts-loesung.visustruct":xZ,"../../lessons/diagramme/pt/zk-split.visustruct":vZ,"../../lessons/diagramme/pt/zk-woerter-loesung.visustruct":wZ,"../../lessons/diagramme/pt/zk-zaehlen.visustruct":yZ,"../../lessons/diagramme/pt/zk-zeichen.visustruct":zZ,"../../lessons/diagramme/sim-kino-loesung.visustruct":IZ,"../../lessons/diagramme/sim-kino-start.visustruct":kZ,"../../lessons/diagramme/sim-tausch-loesung.visustruct":SZ,"../../lessons/diagramme/sim-tausch-start.visustruct":TZ,"../../lessons/diagramme/sim-tausch.visustruct":_Z,"../../lessons/diagramme/ss-absteigend-loesung.visustruct":AZ,"../../lessons/diagramme/ss-absteigend-start.visustruct":jZ,"../../lessons/diagramme/ss-binaere-suche.visustruct":CZ,"../../lessons/diagramme/ss-bubblesort.visustruct":DZ,"../../lessons/diagramme/ss-karte-suchen-loesung.visustruct":BZ,"../../lessons/diagramme/ss-lineare-suche.visustruct":PZ,"../../lessons/diagramme/uebergabe-bonus-loesung.visustruct":OZ,"../../lessons/diagramme/uebergabe-bonus-start.visustruct":MZ,"../../lessons/diagramme/uebergabe-bonus-unterprogramm-loesung.visustruct":ZZ,"../../lessons/diagramme/uebergabe-haupt.visustruct":LZ,"../../lessons/diagramme/uebergabe-leeren.visustruct":WZ,"../../lessons/diagramme/uebergabe-verdopple.visustruct":EZ,"../../lessons/diagramme/vb-abstand-anzeigen-loesung.visustruct":NZ,"../../lessons/diagramme/vb-abstand-halten-loesung.visustruct":FZ,"../../lessons/diagramme/vb-alarmanlage-loesung.visustruct":RZ,"../../lessons/diagramme/vb-ampel-loesung.visustruct":JZ,"../../lessons/diagramme/vb-ampel-zustand-loesung.visustruct":GZ,"../../lessons/diagramme/vb-ausgang-loesung.visustruct":HZ,"../../lessons/diagramme/vb-begruessung-loesung.visustruct":VZ,"../../lessons/diagramme/vb-binaer-zaehlen-loesung.visustruct":qZ,"../../lessons/diagramme/vb-bis-rot-loesung.visustruct":UZ,"../../lessons/diagramme/vb-bis-wand-loesung.visustruct":QZ,"../../lessons/diagramme/vb-bis-ziel-loesung.visustruct":$Z,"../../lessons/diagramme/vb-buchstaben-loesung.visustruct":YZ,"../../lessons/diagramme/vb-do-while-loesung.visustruct":KZ,"../../lessons/diagramme/vb-drei-lichter-loesung.visustruct":XZ,"../../lessons/diagramme/vb-dreieck-loesung.visustruct":eL,"../../lessons/diagramme/vb-einparken-loesung.visustruct":tL,"../../lessons/diagramme/vb-erster-plan-loesung.visustruct":nL,"../../lessons/diagramme/vb-farben-loesung.visustruct":iL,"../../lessons/diagramme/vb-farbweiche-loesung.visustruct":aL,"../../lessons/diagramme/vb-fehler-04-sensoren-start.visustruct":sL,"../../lessons/diagramme/vb-fehler-05-bis-zur-wand-start.visustruct":rL,"../../lessons/diagramme/vb-fehler-05-erst-fahren-dann-pruefen-start.visustruct":oL,"../../lessons/diagramme/vb-fehler-05-zaehlen-und-summieren-start.visustruct":lL,"../../lessons/diagramme/vb-fehler-06-abstand-start.visustruct":uL,"../../lessons/diagramme/vb-fehler-06-konstanten-final-start.visustruct":mL,"../../lessons/diagramme/vb-fehler-06-nah-mittel-weit-start.visustruct":cL,"../../lessons/diagramme/vb-fehler-07-linie-start.visustruct":dL,"../../lessons/diagramme/vb-fehler-07-und-oder-start.visustruct":hL,"../../lessons/diagramme/vb-fehler-08-farben-start.visustruct":gL,"../../lessons/diagramme/vb-fehler-08-groesster-wert-start.visustruct":pL,"../../lessons/diagramme/vb-fehler-09-fallauswahl-mbot2-start.visustruct":fL,"../../lessons/diagramme/vb-fehler-10-tasten-joystick-start.visustruct":bL,"../../lessons/diagramme/vb-fehler-10-unterbrechen-mit-break-start.visustruct":xL,"../../lessons/diagramme/vb-fehler-11-licht-start.visustruct":vL,"../../lessons/diagramme/vb-fehler-11-merker-boolean-start.visustruct":wL,"../../lessons/diagramme/vb-fehler-12-lautstaerke-start.visustruct":yL,"../../lessons/diagramme/vb-fehler-13-neigung-start.visustruct":zL,"../../lessons/diagramme/vb-fehler-14-leds-start.visustruct":IL,"../../lessons/diagramme/vb-fehler-15-einzelne-leds-start.visustruct":kL,"../../lessons/diagramme/vb-fehler-16-lautsprecher-start.visustruct":SL,"../../lessons/diagramme/vb-fehler-17-arrays-melodie-start.visustruct":TL,"../../lessons/diagramme/vb-fehler-18-bildschirm-start.visustruct":_L,"../../lessons/diagramme/vb-fehler-19-zeichenketten-start.visustruct":AL,"../../lessons/diagramme/vb-fehler-20-motoren-start.visustruct":jL,"../../lessons/diagramme/vb-fehler-21-stoppuhr-akku-start.visustruct":CL,"../../lessons/diagramme/vb-fehler-21-zustaende-start.visustruct":DL,"../../lessons/diagramme/vb-fehler-22-eigene-methoden-start.visustruct":BL,"../../lessons/diagramme/vb-fehler-23-wie-geht-es-weiter-start.visustruct":PL,"../../lessons/diagramme/vb-gas-bis-wand-loesung.visustruct":OL,"../../lessons/diagramme/vb-gerade-loesung.visustruct":ML,"../../lessons/diagramme/vb-hindernis-loesung.visustruct":ZL,"../../lessons/diagramme/vb-java-akku-loesung.visustruct":LL,"../../lessons/diagramme/vb-java-ampel-text-loesung.visustruct":WL,"../../lessons/diagramme/vb-java-blau-gruen-loesung.visustruct":EL,"../../lessons/diagramme/vb-java-blinken-loesung.visustruct":NL,"../../lessons/diagramme/vb-java-countdown-loesung.visustruct":FL,"../../lessons/diagramme/vb-java-hoeher-loesung.visustruct":RL,"../../lessons/diagramme/vb-java-joystick-loesung.visustruct":JL,"../../lessons/diagramme/vb-java-klatsch-merker-loesung.visustruct":GL,"../../lessons/diagramme/vb-java-konstante-seite-loesung.visustruct":HL,"../../lessons/diagramme/vb-java-laermampel-loesung.visustruct":VL,"../../lessons/diagramme/vb-java-lauflicht-loesung.visustruct":qL,"../../lessons/diagramme/vb-java-linienfolger-loesung.visustruct":UL,"../../lessons/diagramme/vb-java-nachtlicht-ton-loesung.visustruct":QL,"../../lessons/diagramme/vb-java-rand-loesung.visustruct":$L,"../../lessons/diagramme/vb-java-rechteck-loesung.visustruct":YL,"../../lessons/diagramme/vb-java-rechteck-umdrehungen-loesung.visustruct":KL,"../../lessons/diagramme/vb-java-richtung-loesung.visustruct":XL,"../../lessons/diagramme/vb-java-schritte-zaehlen-loesung.visustruct":eW,"../../lessons/diagramme/vb-java-sicher-fahren-loesung.visustruct":tW,"../../lessons/diagramme/vb-java-spirale-strecke-loesung.visustruct":nW,"../../lessons/diagramme/vb-java-tageszeit-loesung.visustruct":iW,"../../lessons/diagramme/vb-java-taste-stopp-loesung.visustruct":aW,"../../lessons/diagramme/vb-java-textlaenge-loesung.visustruct":sW,"../../lessons/diagramme/vb-java-wand-piep-loesung.visustruct":rW,"../../lessons/diagramme/vb-java-wasserwaage-loesung.visustruct":oW,"../../lessons/diagramme/vb-klatschen-loesung.visustruct":lW,"../../lessons/diagramme/vb-knight-rider-loesung.visustruct":uW,"../../lessons/diagramme/vb-konstante-wand-loesung.visustruct":mW,"../../lessons/diagramme/vb-l00-bausteine-loesung.visustruct":cW,"../../lessons/diagramme/vb-l00-sequenz-loesung.visustruct":dW,"../../lessons/diagramme/vb-l03-wachsend-loesung.visustruct":hW,"../../lessons/diagramme/vb-l06k-mit.visustruct":gW,"../../lessons/diagramme/vb-l06k-ohne.visustruct":pW,"../../lessons/diagramme/vb-l06n-einparkhilfe.visustruct":fW,"../../lessons/diagramme/vb-l21z-ausweichen.visustruct":bW,"../../lessons/diagramme/vb-labyrinth-loesung.visustruct":xW,"../../lessons/diagramme/vb-laufschrift-loesung.visustruct":vW,"../../lessons/diagramme/vb-linienfolger-stopp-loesung.visustruct":wW,"../../lessons/diagramme/vb-linienmuster-loesung.visustruct":yW,"../../lessons/diagramme/vb-meister-platz-loesung.visustruct":zW,"../../lessons/diagramme/vb-melodie-loesung.visustruct":IW,"../../lessons/diagramme/vb-messreihe-loesung.visustruct":kW,"../../lessons/diagramme/vb-morsen-loesung.visustruct":SW,"../../lessons/diagramme/vb-nachtlicht-loesung.visustruct":TW,"../../lessons/diagramme/vb-nachtwaechter-loesung.visustruct":_W,"../../lessons/diagramme/vb-notbremse-loesung.visustruct":AW,"../../lessons/diagramme/vb-oder-loesung.visustruct":jW,"../../lessons/diagramme/vb-quadrat-loesung.visustruct":CW,"../../lessons/diagramme/vb-quadrat-methode-loesung.visustruct":DW,"../../lessons/diagramme/vb-quadrat-schleife-loesung.visustruct":BW,"../../lessons/diagramme/vb-quadrat-unterprogramm-loesung.visustruct":PW,"../../lessons/diagramme/vb-quadrat-verschachtelt-loesung.visustruct":OW,"../../lessons/diagramme/vb-rand-loesung.visustruct":MW,"../../lessons/diagramme/vb-schraeglage-loesung.visustruct":ZW,"../../lessons/diagramme/vb-schritte-welt-loesung.visustruct":LW,"../../lessons/diagramme/vb-slalom-loesung.visustruct":WW,"../../lessons/diagramme/vb-spirale-falsch-loesung.visustruct":EW,"../../lessons/diagramme/vb-spirale-falsch-start.visustruct":NW,"../../lessons/diagramme/vb-spirale-loesung.visustruct":FW,"../../lessons/diagramme/vb-start-a-loesung.visustruct":RW,"../../lessons/diagramme/vb-startsignal-loesung.visustruct":JW,"../../lessons/diagramme/vb-strecke-anzeigen-loesung.visustruct":GW,"../../lessons/diagramme/vb-tempo-abstand-loesung.visustruct":HW,"../../lessons/diagramme/vb-tonleiter-loesung.visustruct":VW,"../../lessons/diagramme/vb-treppe-loesung.visustruct":qW,"../../lessons/diagramme/vb-tunnel-loesung.visustruct":UW,"../../lessons/diagramme/vb-ueberladung-umdrehung-loesung.visustruct":QW,"../../lessons/diagramme/vb-und-loesung.visustruct":$W,"../../lessons/diagramme/vb-variable-seite-loesung.visustruct":YW,"../../lessons/diagramme/vb-vier-quadrate-loesung.visustruct":KW,"../../lessons/diagramme/vb-wald-loesung.visustruct":XW,"../../lessons/diagramme/vb-wand-vorn-loesung.visustruct":eE,"../../lessons/diagramme/vb-wurde-es-dunkel-loesung.visustruct":tE,"../../lessons/diagramme/vb-zentimeter-loesung.visustruct":nE,"../../lessons/diagramme/vb-zentimeter-unterprogramm-loesung.visustruct":iE,"../../lessons/diagramme/vb-zwei-sekunden-loesung.visustruct":aE,"../../lessons/diagramme/vbb-abstand-zeigen-loesung.visustruct":sE,"../../lessons/diagramme/vbb-ampel-loesung.visustruct":rE,"../../lessons/diagramme/vbb-ausgang-loesung.visustruct":oE,"../../lessons/diagramme/vbb-bis-wand-loesung.visustruct":lE,"../../lessons/diagramme/vbb-blinken-loesung.visustruct":uE,"../../lessons/diagramme/vbb-disco-loesung.visustruct":mE,"../../lessons/diagramme/vbb-dreieck-loesung.visustruct":cE,"../../lessons/diagramme/vbb-ecke-loesung.visustruct":dE,"../../lessons/diagramme/vbb-erster-block-loesung.visustruct":hE,"../../lessons/diagramme/vbb-hin-und-zurueck-loesung.visustruct":gE,"../../lessons/diagramme/vbb-klotz-loesung.visustruct":pE,"../../lessons/diagramme/vbb-licht-gruen-loesung.visustruct":fE,"../../lessons/diagramme/vbb-linie-bis-rot-loesung.visustruct":bE,"../../lessons/diagramme/vbb-linienfolger-loesung.visustruct":xE,"../../lessons/diagramme/vbb-notbremse-loesung.visustruct":vE,"../../lessons/diagramme/vbb-quadrat-loesung.visustruct":wE,"../../lessons/diagramme/vbb-quadrat-wiederhole-loesung.visustruct":yE,"../../lessons/diagramme/vbb-rand-loesung.visustruct":zE,"../../lessons/diagramme/vbb-sirene-loesung.visustruct":IE,"../../lessons/diagramme/vbb-slalom-loesung.visustruct":kE,"../../lessons/diagramme/vbb-start-a-loesung.visustruct":SE,"../../lessons/diagramme/vbb-wald-loesung.visustruct":TE,"../../lessons/diagramme/vbb-wand-vorn-loesung.visustruct":_E,"../../lessons/diagramme/vbb-weiter-loesung.visustruct":AE,"../../lessons/diagramme/zk-initialen-loesung.visustruct":jE,"../../lessons/diagramme/zk-mehr.visustruct":CE,"../../lessons/diagramme/zk-rueckwaerts-loesung.visustruct":DE,"../../lessons/diagramme/zk-split.visustruct":BE,"../../lessons/diagramme/zk-woerter-loesung.visustruct":PE,"../../lessons/diagramme/zk-zaehlen.visustruct":OE,"../../lessons/diagramme/zk-zeichen.visustruct":ME}),YR=Object.assign({"../../lessons/welten/ausgang-1.txt":ZE,"../../lessons/welten/ausgang-2.txt":LE,"../../lessons/welten/ecke.txt":WE,"../../lessons/welten/farbe-1.txt":EE,"../../lessons/welten/farbe-2.txt":NE,"../../lessons/welten/farbe-3.txt":FE,"../../lessons/welten/farbe-4.txt":RE,"../../lessons/welten/hindernis-1.txt":JE,"../../lessons/welten/hindernis-2.txt":GE,"../../lessons/welten/labyrinth-1.txt":HE,"../../lessons/welten/labyrinth-2.txt":VE,"../../lessons/welten/linie-1.txt":qE,"../../lessons/welten/linie-2.txt":UE,"../../lessons/welten/linie-rot-1.txt":QE,"../../lessons/welten/linie-rot-2.txt":$E,"../../lessons/welten/parken-1.txt":YE,"../../lessons/welten/parken-2.txt":KE,"../../lessons/welten/platz-1.txt":XE,"../../lessons/welten/platz-2.txt":eN,"../../lessons/welten/rand-1.txt":tN,"../../lessons/welten/rand-2.txt":nN,"../../lessons/welten/slalom.txt":iN,"../../lessons/welten/tunnel-1.txt":aN,"../../lessons/welten/tunnel-2.txt":sN,"../../lessons/welten/wald-1.txt":rN,"../../lessons/welten/wald-2.txt":oN,"../../lessons/welten/wand-frei.txt":lN,"../../lessons/welten/wand-kurz.txt":uN,"../../lessons/welten/wand-lang.txt":mN,"../../lessons/welten/wand-mittel.txt":cN,"../../lessons/welten/wand-nah.txt":dN,"../../lessons/welten/zeit-1.txt":hN,"../../lessons/welten/ziel-kurz.txt":gN,"../../lessons/welten/ziel-lang.txt":pN}),KR=Object.assign({"../../lessons/bilder/ada-lovelace.jpg":fN,"../../lessons/bilder/aufgabe-mehrwertsteuer.svg":bN,"../../lessons/bilder/ben-shneiderman.jpg":xN,"../../lessons/bilder/caesar-ascii.svg":vN,"../../lessons/bilder/caesar-scheibe.svg":wN,"../../lessons/bilder/caesar-verschiebung.svg":yN,"../../lessons/bilder/en/aufgabe-mehrwertsteuer.svg":zN,"../../lessons/bilder/en/kino-saal.svg":IN,"../../lessons/bilder/en/kino-schleifen.svg":kN,"../../lessons/bilder/en/l01-pap.svg":SN,"../../lessons/bilder/en/l02-einfuegen.svg":TN,"../../lessons/bilder/en/l03-lochkarte-beschriftet.svg":_N,"../../lessons/bilder/en/l03-lochkarte.svg":AN,"../../lessons/bilder/en/l04-camelcase.svg":jN,"../../lessons/bilder/en/l04-variable.svg":CN,"../../lessons/bilder/en/l04-zuweisung.svg":DN,"../../lessons/bilder/en/l05-tauschen.svg":BN,"../../lessons/bilder/en/l08-zahlenstrahl.svg":PN,"../../lessons/bilder/en/l10-kopfgesteuert.svg":ON,"../../lessons/bilder/en/l11-vergleich.svg":MN,"../../lessons/bilder/en/l12-for-ablauf.svg":ZN,"../../lessons/bilder/en/l12-while-zu-for.svg":LN,"../../lessons/bilder/en/l13-verschachtelt-tabelle.svg":WN,"../../lessons/bilder/en/l14-array.svg":EN,"../../lessons/bilder/en/l20-cpp.svg":NN,"../../lessons/bilder/en/l20-jvm.svg":FN,"../../lessons/bilder/en/mvc-klick.svg":RN,"../../lessons/bilder/en/ss-binaere-suche.svg":JN,"../../lessons/bilder/en/ss-bubblesort.svg":GN,"../../lessons/bilder/en/ss-lineare-suche.svg":HN,"../../lessons/bilder/en/ss-vergleiche.svg":VN,"../../lessons/bilder/en/uebergabe.svg":qN,"../../lessons/bilder/en/zk-zeichen.svg":UN,"../../lessons/bilder/grace-hopper.jpg":QN,"../../lessons/bilder/hedy-lamarr.jpg":$N,"../../lessons/bilder/ike-nassi.jpg":YN,"../../lessons/bilder/katherine-johnson.jpg":KN,"../../lessons/bilder/kino-saal.svg":XN,"../../lessons/bilder/kino-schleifen.svg":eF,"../../lessons/bilder/l01-pap.svg":tF,"../../lessons/bilder/l02-einfuegen.svg":nF,"../../lessons/bilder/l03-lochkarte-beschriftet.svg":iF,"../../lessons/bilder/l03-lochkarte.svg":aF,"../../lessons/bilder/l03-zuse-lochstreifen.svg":sF,"../../lessons/bilder/l04-camelcase.svg":rF,"../../lessons/bilder/l04-variable.svg":oF,"../../lessons/bilder/l04-zuweisung.svg":lF,"../../lessons/bilder/l05-tauschen.svg":uF,"../../lessons/bilder/l07-schreibtischtest-de.svg":mF,"../../lessons/bilder/l07-schreibtischtest-en.svg":cF,"../../lessons/bilder/l07-schreibtischtest-pt.svg":dF,"../../lessons/bilder/l08-zahlenstrahl.svg":hF,"../../lessons/bilder/l09-wahrheitstabelle.svg":gF,"../../lessons/bilder/l10-kopfgesteuert.svg":pF,"../../lessons/bilder/l11-vergleich.svg":fF,"../../lessons/bilder/l12-for-ablauf.svg":bF,"../../lessons/bilder/l12-while-zu-for.svg":xF,"../../lessons/bilder/l13-verschachtelt-tabelle.svg":vF,"../../lessons/bilder/l14-array.svg":wF,"../../lessons/bilder/l20-cpp.svg":yF,"../../lessons/bilder/l20-jvm.svg":zF,"../../lessons/bilder/margaret-hamilton.jpg":IF,"../../lessons/bilder/mvc-klick.svg":kF,"../../lessons/bilder/pt/aufgabe-mehrwertsteuer.svg":SF,"../../lessons/bilder/pt/kino-saal.svg":TF,"../../lessons/bilder/pt/kino-schleifen.svg":_F,"../../lessons/bilder/pt/l01-pap.svg":AF,"../../lessons/bilder/pt/l02-einfuegen.svg":jF,"../../lessons/bilder/pt/l03-lochkarte-beschriftet.svg":CF,"../../lessons/bilder/pt/l03-lochkarte.svg":DF,"../../lessons/bilder/pt/l04-camelcase.svg":BF,"../../lessons/bilder/pt/l04-variable.svg":PF,"../../lessons/bilder/pt/l04-zuweisung.svg":OF,"../../lessons/bilder/pt/l05-tauschen.svg":MF,"../../lessons/bilder/pt/l08-zahlenstrahl.svg":ZF,"../../lessons/bilder/pt/l10-kopfgesteuert.svg":LF,"../../lessons/bilder/pt/l11-vergleich.svg":WF,"../../lessons/bilder/pt/l12-for-ablauf.svg":EF,"../../lessons/bilder/pt/l12-while-zu-for.svg":NF,"../../lessons/bilder/pt/l13-verschachtelt-tabelle.svg":FF,"../../lessons/bilder/pt/l14-array.svg":RF,"../../lessons/bilder/pt/l20-cpp.svg":JF,"../../lessons/bilder/pt/l20-jvm.svg":GF,"../../lessons/bilder/pt/mvc-klick.svg":HF,"../../lessons/bilder/pt/schiffe-versenken.svg":VF,"../../lessons/bilder/pt/ss-binaere-suche.svg":qF,"../../lessons/bilder/pt/ss-bubblesort.svg":UF,"../../lessons/bilder/pt/ss-lineare-suche.svg":QF,"../../lessons/bilder/pt/ss-vergleiche.svg":$F,"../../lessons/bilder/pt/uebergabe.svg":YF,"../../lessons/bilder/pt/zk-zeichen.svg":KF,"../../lessons/bilder/schiffe-versenken.svg":XF,"../../lessons/bilder/ss-binaere-suche.svg":eR,"../../lessons/bilder/ss-bubblesort.svg":tR,"../../lessons/bilder/ss-lineare-suche.svg":nR,"../../lessons/bilder/ss-vergleiche.svg":iR,"../../lessons/bilder/uebergabe.svg":aR,"../../lessons/bilder/zk-zeichen.svg":sR}),Mp=i=>i.replace(/^.*\//,"");function Qn(i,e,t){const n=s=>new Map(Object.entries(i).filter(([r])=>{var o;return((o=e.exec(r))==null?void 0:o[1])===s}).map(([r,o])=>[Mp(r),o]));return[...new Map([...n("en"),...t==="en"?[]:n(t)])]}function Hm(i){return i==="de"?"de":i==="pt-PT"?"pt":"en"}let l1="en",ot=[],bn=[],ps="",fs=[],u1="off",qs=!1;function Vm(i){return i===qs?!1:(qs=i,ot=[],Vi(l1),u1==="on")}const qm=()=>u1==="on"&&qs&&ot.some(i=>i.blocks);function Um(i){i!==u1&&(u1=i,ot=[],Vi(l1))}function Vi(i){var r,o,l;if(Yb(i),i===l1&&ot.length>0)return;l1=i;const e=Qn(HR,/\/lessons\/([^/]+)\/[^/]+\.md$/,i),t=(u,m)=>u.filter(([c])=>/^\d+/.test(c)).map(([c,d])=>JR(Qm(d,m),`${i}/${c}`)).sort((c,d)=>c.number-d.number),n=(u,m,c)=>u.map(([d,h])=>{const g=GR(Qm(h,c),`${i}/${m}${d}`);return c&&g.after!==void 0?{...g,after:c.get(g.after)??g.after}:g}),a=Qn(VR,/\/lessons\/([^/]+)\/aufgaben\/[^/]+\.md$/,i),s=u1==="on"&&qs?Qn(qR,/\/lessons\/([^/]+)\/visubot\/bloecke\/[^/]+\.md$/,i):[];if(s.some(([u])=>/^\d+/.test(u))){const u=io(s);ot=t(s,u.renumber).map(c=>({...c,number:u.number.get(c.id),robot:!0,blocks:!0})).sort((c,d)=>c.number-d.number);const m=Qn(UR,/\/lessons\/([^/]+)\/visubot\/bloecke\/aufgaben\/[^/]+\.md$/,i);bn=n(m,"visubot/bloecke/aufgaben/",u.renumber).map(c=>({...c,robot:!0}))}else if(u1==="on"){const u=Qn(Gm,/\/lessons\/([^/]+)\/visubot\/[^/]+\.md$/,i),m=io(u);ot=t(u,m.renumber).map(d=>({...d,number:m.number.get(d.id),robot:!0})).sort((d,h)=>d.number-h.number);const c=Qn(QR,/\/lessons\/([^/]+)\/visubot\/aufgaben\/[^/]+\.md$/,i);bn=n(c,"visubot/aufgaben/",m.renumber).map(d=>({...d,robot:!0}))}else{const u=e.filter(([c])=>/^\d+/.test(c)),m=io(u);ot=t(u,m.renumber).map(c=>({...c,number:m.number.get(c.id)})).sort((c,d)=>c.number-d.number),bn=n(a,"aufgaben/",m.renumber)}bn.sort((u,m)=>(u.after??0)-(m.after??0)||u.level-m.level||u.title.localeCompare(m.title,i));for(const u of[...ot.flatMap(m=>m.steps.flatMap(c=>c.blocks)),...bn.flatMap(m=>m.blocks)])u.kind==="textFile"&&ox(u.name,u.content);if(ps=((r=e.find(([u])=>u==="nachschlagen.md"))==null?void 0:r[1])??"",u1!=="off"){const u=(o=Qn(Gm,/\/lessons\/([^/]+)\/visubot\/[^/]+\.md$/,i).find(([m])=>m==="nachschlagen.md"))==null?void 0:o[1];u&&(ps=`${ps.trimEnd()} - -${u}`)}fs=rR(((l=e.find(([u])=>u==="begriffe.md"))==null?void 0:l[1])??"")}function io(i){const e=i.map(([a])=>a).filter(a=>/^\d+/.test(a)).sort((a,s)=>Number(/^\d+/.exec(a)[0])-Number(/^\d+/.exec(s)[0])||a.localeCompare(s)),t=new Map,n=new Map;return e.forEach((a,s)=>{t.set(a.replace(/\.md$/,""),s+1);const r=Number(/^\d+/.exec(a)[0]);n.has(r)||n.set(r,s+1)}),{number:t,renumber:n}}function Qm(i,e){return e?i.replace(/\b(Lektion(?:en)?|Lessons?|Li[cç](?:ão|ões))(\s+\d+(?:\s*(?:,|bis|und|and|to|a|e|–|-)\s*\d+)*)/gi,(t,n,a)=>n+a.replace(/\d+/g,s=>String(e.get(Number(s))??s))):i}Vi("en");function Zp(i,e){return l1==="de"?i.get(e)??i.get(`en/${e}`):(l1==="en"?void 0:i.get(`${l1}/${e}`))??i.get(`en/${e}`)??i.get(e)}const Lp=(i,e)=>i.slice(i.indexOf(e)+e.length),XR=new Map(Object.entries($R).map(([i,e])=>[Lp(i,"/diagramme/"),e]));function bi(i){const e=Zp(XR,i);return e===void 0?void 0:z0(e)}const eJ=new Map(Object.entries(KR).map(([i,e])=>[Lp(i,"/bilder/"),e]));function Ba(i){return Zp(eJ,i)}const tJ=new Map(Object.entries(YR).map(([i,e])=>[Mp(i).replace(/\.txt$/,""),e])),$m=new Map;function bs(i){const e=tJ.get(i);if(e===void 0)return;let t=$m.get(i);return t||$m.set(i,t=Xg(e)),t}const Wp=i=>i.replace(/&/g,"&").replace(//g,">").replace(/"/g,""");function Ae(i){const e=[];let t=Wp(i).replace(/`([^`]+)`/g,(n,a)=>(e.push(a),`\0${e.length-1}\0`));return t=t.replace(/\*\*(.+?)\*\*/g,"$1").replace(/(^|[^*])\*([^*\s][^*]*?)\*/g,"$1$2").replace(/\[([^\]]+)\]\(([^)\s]+)\)/g,(n,a,s)=>{const r=/^\?(visustruct|visubot)(&lektion=[\w-]+)?$/.test(s),o=r||/^(https?:|mailto:|#|\.{0,2}\/)/.test(s)?s:"#",l=r||/^https?:/.test(o)?' target="_blank" rel="noopener noreferrer"':"";return`${a}`}).replace(/\[([^\]]+)\]\{\.(blau|orange|gruen|gelb|rosa)\}/g,'$1').replace(/\[([^\]]+)\]\{(?:teil|part)=([\w,]+)\}/g,'$1'),t.replace(/\u0000(\d+)\u0000/g,(n,a)=>`${e[Number(a)]}`)}function Bn(i){const e=[],t=i.replace(/\r\n/g,` -`).split(` -`);let n=0;for(;n${Wp(c.join(` -`))}`);continue}const s=/^(#{2,4})\s+(.*)$/.exec(a);if(s){const o=s[1].length,l=/\s*\{\.([a-z][a-z-]*)\}$/.exec(s[2]),u=l?s[2].slice(0,l.index):s[2];e.push(`${Ae(u)}`),n++;continue}if(/^\s*([-*]|\d+\.)\s+/.test(a)){const o=/^\s*\d+\./.test(a),l=[];for(;n${Ae(u)}`)}e.push(o?`
      ${l.join("")}
    `:`
      ${l.join("")}
    `);continue}if(a.startsWith("|")&&/^\|?[\s:|-]*-{3,}[\s:|-]*$/.test(t[n+1]??"")){const o=m=>m.trim().replace(/^\|/,"").replace(/\|$/,"").split("|").map(c=>Ae(c.trim())),l=o(a);n+=2;const u=[];for(;n${l.map(m=>``).join("")}${u.map(m=>`${m.map(c=>``).join("")}`).join("")}
    ${m}
    ${c}
    `);continue}if(a.startsWith(">")){const o=[];for(;n");)o.push(t[n++].replace(/^>\s?/,""));e.push(``);continue}const r=[];for(;n|\||\s*([-*]|\d+\.)\s+)/.test(t[n]);)r.push(t[n++].trim());e.push(`

    ${Ae(r.join(" "))}

    `)}return e.join(` -`)}const k1=""+new URL("ludwig-Cus1i402.webp",import.meta.url).href,Il="f4ecc8e489ae7865308288c7d0fa313333cf2be3ff529b23bc214bee010310e0",Y0="visustruct.learn.teacher";let K0=!1;function Ym(){if(K0)return!0;try{return localStorage.getItem(Y0)===Il}catch{return!1}}async function nJ(i){const e=await crypto.subtle.digest("SHA-256",new TextEncoder().encode(i));return[...new Uint8Array(e)].map(t=>t.toString(16).padStart(2,"0")).join("")}async function iJ(i){let e=!1;try{e=await nJ(i.trim())===Il}catch{return!1}if(e){K0=!0;try{localStorage.setItem(Y0,Il)}catch{}}return e}function aJ(){K0=!1;try{localStorage.removeItem(Y0)}catch{}}function Ep(i,e,t=!1){const n=document.createElement("figure");n.className=t?"learn-file written":"learn-file";const a=document.createElement("figcaption"),s=document.createElement("span");s.className="learn-file-icon",s.setAttribute("aria-hidden","true"),s.textContent="📄",a.append(s,t?p("web_sim_written_file_fmt",i):i);const r=document.createElement("pre");return r.className="learn-file-text",r.dir="ltr",e.replace(/\n$/,"").split(` -`).forEach((l,u)=>{const m=document.createElement("span");m.className="ln",m.textContent=String(u+1),r.append(m,l+` -`)}),n.append(a,r),n}function Np(i){return[...i].map(([e,t])=>Ep(e,t,!0))}function m1(i){switch(i.kind){case"finished":return p("sim_notice_finished");case"infiniteLoopWithoutBody":return p("sim_notice_infinite_loop_empty");case"unsupportedStatement":return p("sim_notice_unsupported_statement_fmt",i.text);case"invalidExpression":return p("sim_notice_invalid_expression_fmt",i.text);case"unknownVariable":return i.similar?p("web_sim_unknown_variable_case_fmt",i.text,i.similar):p("sim_notice_unknown_variable_fmt",i.text);case"divisionByZero":return p("sim_notice_division_by_zero");case"wholeNumberExpected":return p("sim_notice_whole_number_expected");case"numberExpected":return p("sim_notice_number_expected");case"booleanExpected":return p("sim_notice_boolean_expected");case"subprogramNotFound":return p("sim_notice_subprogram_not_found_fmt",i.text);case"callTooDeep":return p("sim_notice_call_too_deep");case"callArguments":return p("web_sim_call_arguments_fmt",i.text);case"missingReturn":return p("web_sim_missing_return_fmt",i.text);case"fileNotFound":return p("web_sim_file_not_found_fmt",i.text);case"endOfFile":return p("web_sim_end_of_file_fmt",i.text);case"nullValue":return p("web_sim_null_value_fmt",i.text);case"other":return i.text}}const Oe=i=>i.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Km=["0.2","0.5","1.0","1.5","2.0"],sJ=i=>Number(i).toLocaleString(In()==="java"?"en":In(),{minimumFractionDigits:1}),ao="visustruct.simulation.stepDelay",Xm="visustruct.simulation.highlightMode";function so(i){try{return localStorage.getItem(i)}catch{return null}}function ec(i,e){try{localStorage.setItem(i,e)}catch{}}class rJ{constructor(e,t,n=()=>{},a=()=>{},s=()=>{}){y(this,"element");y(this,"engine");y(this,"isPlaying",!1);y(this,"playTimer");y(this,"wasPlayingBeforeInput",!1);y(this,"delay",Km.includes(so(ao)??"")?so(ao):"0.5");y(this,"highlightMode",so(Xm)==="nextToExecute"?"nextToExecute":"lastExecuted");y(this,"buttons");y(this,"inputSection");y(this,"trace");y(this,"variables");y(this,"output");y(this,"transcript",[]);y(this,"printed",0);y(this,"notice");y(this,"editButton");y(this,"robot");y(this,"liveBusy",!1);this.currentDocument=e,this.onHighlight=t,this.findSubprogram=n,this.onEdit=a,this.onTranscript=s,this.engine=new Wi(e(),n),this.element=document.createElement("aside"),this.element.className="simpanel",this.build()}setRobot(e){var t;this.robot!==e&&((t=this.robot)==null||t.element.remove(),this.robot=e,e&&(this.element.querySelector("section").after(e.element),e.onWorldChange=()=>this.restartEngine(),e.reset()))}showRobot(){var e;(e=this.robot)==null||e.show()}begin(){this.pause(),this.restartEngine(),this.showRobot()}end(){this.pause()}step(){var n,a,s;if((n=this.robot)!=null&&n.live){this.stepLive();return}if(!this.engine.canStep){this.pause();return}const e=this.isPlaying,t=this.engine.currentStepPath;(a=this.robot)==null||a.prepare(this.engine),this.engine.step(),(s=this.robot)==null||s.follow(this.engine.state.robotActions,e?Number(this.delay)*850:450),this.afterStep(e,t)}async stepLive(){if(this.liveBusy)return;if(!this.engine.canStep){this.pause();return}this.liveBusy=!0;const e=this.robot,t=this.isPlaying,n=this.engine.currentStepPath;try{await e.prepareLive(this.engine,Xb(JSON.stringify(this.currentDocument()))),this.engine.step(),e.follow(this.engine.state.robotActions,450),await e.drive(this.engine.state.robotActions)}catch(a){e.liveFailed(a),this.isPlaying=!1}finally{this.liveBusy=!1}this.afterStep(t,n)}afterStep(e,t){this.collectOutput(),this.highlightMode==="nextToExecute"&&this.engine.canStep?this.onHighlight(this.engine.currentStepPath):this.onHighlight(t??this.engine.currentStepPath),this.engine.state.inputRequest&&e&&(this.wasPlayingBeforeInput=!0),this.engine.canStep||this.pause(),this.update()}reset(){this.pause(),this.restartEngine()}play(){if(this.isPlaying||!this.engine.canStep)return;this.isPlaying=!0;const e=async()=>{var t;this.playTimer=void 0,this.isPlaying&&((t=this.robot)!=null&&t.live?await this.stepLive():this.step(),this.isPlaying&&this.engine.canStep?this.playTimer=window.setTimeout(()=>void e(),Number(this.delay)*1e3):(this.isPlaying=!1,this.update()))};e()}pause(){this.playTimer!==void 0&&window.clearTimeout(this.playTimer),this.playTimer=void 0,this.isPlaying=!1,this.update()}submitInput(e){var s,r;const t=this.isPlaying||this.wasPlayingBeforeInput;this.pause();const n=((s=this.engine.state.inputRequest)==null?void 0:s.prompt)??"",a=(r=this.engine.state.inputRequest)==null?void 0:r.continuesLine;if(this.engine.provideInput(e.value),!this.engine.state.inputRequest){const o=this.transcript[this.transcript.length-1];a&&o&&o.typed===void 0?o.typed=e.value:this.transcript.push({text:n?`${n} `:"",typed:e.value}),e.value=""}this.onHighlight(this.engine.currentStepPath),t&&this.engine.canStep&&(this.wasPlayingBeforeInput=!1,this.play()),this.update()}restartEngine(){var e;this.engine=new Wi(this.currentDocument(),this.findSubprogram),this.transcript=[],this.printed=0,this.wasPlayingBeforeInput=!1,(e=this.robot)==null||e.reset(),this.onHighlight(this.engine.currentStepPath),this.update()}build(){this.element.innerHTML=` -
    -

    ${Oe(p("simulation_controls"))}

    -
    - - - - -
    - - -
    -

    - -

    ${Oe(p("simulation_input"))}

    -

    ${Oe(p("simulation_trace"))}

    -

    ${Oe(p("simulation_variables"))}

    -

    ${Oe(p("simulation_output"))}

    - `;const e=a=>this.element.querySelector(a);this.buttons={play:e("[data-sim=play]"),pause:e("[data-sim=pause]"),step:e("[data-sim=step]"),stop:e("[data-sim=stop]")},this.buttons.play.addEventListener("click",()=>this.play()),this.buttons.pause.addEventListener("click",()=>this.pause()),this.buttons.step.addEventListener("click",()=>this.step()),this.buttons.stop.addEventListener("click",()=>this.reset()),this.editButton=e(".sim-edit"),this.editButton.addEventListener("click",()=>this.onEdit());const t=e("[data-sim=highlight]");t.value=this.highlightMode,t.addEventListener("change",()=>{this.highlightMode=t.value,ec(Xm,this.highlightMode)});const n=e("[data-sim=delay]");n.value=this.delay,n.addEventListener("change",()=>{this.delay=n.value,ec(ao,this.delay)}),this.inputSection=e(".sim-input > div"),this.trace=e(".sim-trace"),this.variables=e(".sim-vars"),this.output=e(".sim-output"),this.notice=e(".sim-notice")}collectOutput(){const e=this.engine.state.outputLines;for(;this.printedr.localeCompare(o));if(n.length===0)this.variables.innerHTML=`

    ${Oe(p("simulation_variables_placeholder"))}

    `;else{const r=document.createElement("table");for(const o of n){const l=r.insertRow();l.insertCell().textContent=o;const u=e.variables.get(o),m=l.insertCell();u.kind==="grid"?m.append(Fp(u)):m.textContent=_e(u)}this.variables.replaceChildren(r)}e.outputLines.length===0&&e.files.size===0?this.output.innerHTML=`

    ${Oe(p("simulation_output_placeholder"))}

    `:(this.output.replaceChildren(...e.outputLines.map(r=>{const o=document.createElement("div");return o.textContent=r,o}),...Np(e.files)),this.output.scrollTop=this.output.scrollHeight)}updateInput(){const e=this.engine.state.inputRequest,t=this.inputSection;if(t.parentElement.classList.toggle("active",!!e),!e){t.innerHTML=`

    ${Oe(p("simulation_input_placeholder"))}

    `;return}let n=t.querySelector("form");if(!n){n=document.createElement("form"),n.innerHTML=`

    `;const l=n.querySelector("input");n.addEventListener("submit",u=>{u.preventDefault(),this.submitInput(l)}),t.replaceChildren(n),requestAnimationFrame(()=>l.focus())}const a=this.engine.state.outputLines;n.querySelector(".sim-prompt").textContent=e.prompt||(a[a.length-1]??"");const s=n.querySelector("input");s.inputMode=e.valueType==="string"||e.valueType==="boolean"?"text":"decimal";const r=this.engine.state.inputErrorNotice,o=n.querySelector(".sim-input-error");o.textContent=r?m1(r):"",o.hidden=!r}}function Fp(i){const e=document.createElement("table");e.className="sim-grid";for(const t of i.value){const n=e.insertRow();for(const a of t){const s=n.insertCell();s.textContent=String(a),a===!0&&(s.className="sim-grid-true")}}return e}const B1=Array.from("ABCDEFGHIJKLMNOPQRSTUVWXYZ"),Kn=360/B1.length,oJ="http://www.w3.org/2000/svg";function Ge(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}function zt(i,e={},t){const n=document.createElementNS(oJ,i);for(const[a,s]of Object.entries(e))n.setAttribute(a,String(s));return t!==void 0&&(n.textContent=t),n}function lJ(i,e){const t=(e%26+26)%26;return Array.from(i).map(n=>{const a=n.charCodeAt(0);return a>=65&&a<=90?String.fromCharCode((a-65+t)%26+65):a>=97&&a<=122?String.fromCharCode((a-97+t)%26+97):n}).join("")}function tc(i,e){const t=zt("g",{class:e}),n=B1.map((a,s)=>{const r=zt("g",{transform:`rotate(${s*Kn})`});return r.append(zt("text",{x:0,y:-i,"text-anchor":"middle","dominant-baseline":"central"},a)),t.append(r),r});return{group:t,cells:n}}function uJ(i,e){let t=i,n=!1,a=-t*Kn;const s=Ge("section",{class:"learn-game learn-caesar"});s.append(Ge("div",{class:"learn-game-head"},Ge("strong",{},p("web_learn_caesar_title")))),s.append(Ge("p",{class:"learn-game-status"},p("web_learn_caesar_hint")));const r=zt("svg",{viewBox:"-200 -200 400 400",class:"learn-caesar-disc",role:"img"});r.append(zt("circle",{r:196,class:"outer-disc"})),r.append(zt("path",{d:mJ(196,92,Kn/2),class:"window"}));const o=tc(168,"outer-letters");r.append(o.group);for(let j=0;j{o.cells.forEach((Z,F)=>Z.classList.toggle("marked",F===j)),u.cells.forEach((Z,F)=>Z.classList.toggle("marked",j!==void 0&&F===(j+t)%26))},k=()=>{m.textContent=String(t),l.setAttribute("transform",`rotate(${a})`),r.setAttribute("aria-label",p("web_learn_caesar_key_fmt",t)),x.textContent=p(n?"web_learn_caesar_secret":"web_learn_caesar_plain"),w.textContent=p(n?"web_learn_caesar_plain":"web_learn_caesar_secret"),f.classList.toggle("active",!n),b.classList.toggle("active",n),z.textContent=lJ(v.value,n?-t:t)||" ";const j=Array.from(v.value.toUpperCase()).reverse().find(F=>B1.includes(F));if(j===void 0)return T();const Z=B1.indexOf(j);T(n?(Z-t+26)%26:Z)},C=j=>{t=(j%26+26)%26,a=-t*Kn,k()};c.addEventListener("click",()=>C(t-1)),d.addEventListener("click",()=>C(t+1)),f.addEventListener("click",()=>{var j;n&&(v.value=((j=z.textContent)==null?void 0:j.trim())??""),n=!1,k()}),b.addEventListener("click",()=>{var j;n||(v.value=((j=z.textContent)==null?void 0:j.trim())??""),n=!0,k()}),v.addEventListener("input",k),o.cells.forEach((j,Z)=>j.addEventListener("click",()=>T(Z)));let P;const D=j=>{const Z=r.getBoundingClientRect();return Math.atan2(j.clientY-(Z.top+Z.height/2),j.clientX-(Z.left+Z.width/2))*180/Math.PI};l.addEventListener("pointerdown",j=>{const Z=j;P=D(Z)-a,l.classList.add("dragging"),r.setPointerCapture(Z.pointerId),Z.preventDefault()}),r.addEventListener("pointermove",j=>{if(P===void 0)return;a=D(j)-P,l.setAttribute("transform",`rotate(${a})`);const Z=Math.round(-a/Kn);if((Z%26+26)%26!==t){t=(Z%26+26)%26;const F=a;k(),a=F,l.setAttribute("transform",`rotate(${a})`)}});const B=()=>{P!==void 0&&(P=void 0,l.classList.remove("dragging"),C(t))};return r.addEventListener("pointerup",B),r.addEventListener("pointercancel",B),k(),s}function mJ(i,e,t){const n=(a,s)=>{const r=(s-90)*Math.PI/180;return`${(a*Math.cos(r)).toFixed(2)} ${(a*Math.sin(r)).toFixed(2)}`};return`M ${n(i,-t)} A ${i} ${i} 0 0 1 ${n(i,t)} L ${n(e,t)} A ${e} ${e} 0 0 0 ${n(e,-t)} Z`}const X0=i=>p(`web_visubot_part_${i}`),Rp=new WeakMap;function Jp(i,e,t,n){const a=document.createElement("div");a.className="visubot-model";const s=document.createElement("div");s.className="visubot-stage";const r=document.createElement("div");r.className="visubot-part-label",r.hidden=!0,s.append(r);const o=(c,d,h)=>{r.hidden=!1,r.textContent=X0(c),r.style.left=`${Math.min(d+12,s.clientWidth-140)}px`,r.style.top=`${h+12}px`},l=document.createElement("p");l.className="visubot-model-hint hint",l.textContent=p("web_visubot_tap_part"),l.hidden=!!n,a.append(s,l);const u=ee(async()=>{const{Robot3D:c}=await import("./robot3d-CJE1N58D.js");return{Robot3D:c}},[],import.meta.url).then(({Robot3D:c})=>{const d=new c(s);d.showcase(),d.highlight(i),i.includes("leds")&&d.setLed([40,90,255]),d.focusOn([],e==="unten"||e==="bottom"?"":e,!1,1),d.focusOn(i,e,t);let h;return s.addEventListener("pointerdown",g=>h={x:g.clientX,y:g.clientY}),s.addEventListener("pointerup",g=>{if(!h||Math.hypot(g.clientX-h.x,g.clientY-h.y)>5)return;const f=d.partAt(g.clientX,g.clientY);if(!f)return;if(n)return n(f);d.highlight(f);const b=s.getBoundingClientRect();o(f,g.clientX-b.left,g.clientY-b.top)}),s.addEventListener("pointermove",g=>{if(g.pointerType!=="mouse"||n)return;const f=d.partAt(g.clientX,g.clientY);if(r.hidden=!f,!f)return;const b=s.getBoundingClientRect();o(f,g.clientX-b.left,g.clientY-b.top)}),s.addEventListener("pointerleave",()=>r.hidden=!0),d}).catch(c=>{console.error(c),s.textContent=p("web_visubot_no_3d")}),m={box:a,ready:u,focus:i,view:e};return Rp.set(a,m),m}function cJ(i,e,t=!1){return Jp(i,e,t).box}function dJ(i){const e=i.querySelector(".visubot-model"),t=e&&Rp.get(e);for(const n of i.querySelectorAll("[data-robot-part]")){const a=(n.dataset.robotPart??"").split(",").filter(Boolean);if(n.title=a.map(X0).join(", "),!t)continue;const s=()=>void t.ready.then(o=>o==null?void 0:o.highlight(a)),r=()=>void t.ready.then(o=>o==null?void 0:o.highlight(t.focus));n.addEventListener("mouseenter",s),n.addEventListener("focus",s),n.addEventListener("mouseleave",r),n.addEventListener("blur",r),n.addEventListener("click",()=>{t.ready.then(o=>{o==null||o.highlight(a),o==null||o.focusOn(a,t.view)}),t.box.scrollIntoView({block:"nearest",behavior:"smooth"})})}}function hJ(i,e){let t=0;const n=document.createElement("div");n.className="visubot-quiz-question";const a=document.createElement("p");a.className="visubot-quiz-feedback",a.setAttribute("aria-live","polite");const s=()=>{n.innerHTML=Bn(`**${t+1}/${i.length}** ${i[t].question}`)},r=Jp([],void 0,!1,l=>{if(!(t>=i.length)){if(r.ready.then(u=>u==null?void 0:u.highlight(l)),l!==i[t].part){a.className="visubot-quiz-feedback wrong",a.textContent=p("web_visubot_quiz_wrong_fmt",X0(l));return}t++,a.className="visubot-quiz-feedback right",t{m.forEach(w=>{window.clearTimeout(w),window.clearInterval(w)}),m=[]},d=()=>{s.textContent=`${p("web_game_round_fmt",l+1,o)} · ${p("web_game_score_fmt",u)}`},h=()=>{const w=K("button",{class:"primary"},l+1{l++,l{const w=i.rounds[l],z=i.seconds,I=K("div",{class:"learn-bomb-fuse"},K("span",{})),T=K("span",{class:"learn-bomb-time"}),k=K("div",{class:"learn-bomb","aria-live":"polite"},K("span",{class:"learn-bomb-icon","aria-hidden":"true"},"💣"),T,I),C=K("div",{class:"learn-game-feedback",role:"status"}),P=w.wrongPart!==void 0,D=[],B=w.code.split(` -`).map((ie,ue)=>{if(!P){const _=K("button",{class:"ln",type:"button"},ie||" ");return _.disabled=!ie.trim(),D.push({button:_,line:ue+1,right:ue+1===w.wrongLine}),_}const N=K("div",{class:"ln parts"});ie.trim()||N.append(" ");for(const _ of xp(ie)){if(!_.part){N.append(_.text);continue}const M=K("button",{class:"part",type:"button"},_.text);D.push({button:M,line:ue+1,right:ue+1===w.wrongLine&&_.text===w.wrongPart}),N.append(M)}return N});for(const ie of D)ie.button.addEventListener("click",()=>U(ie.right?"right":ie.line===w.wrongLine?"part":"wrong",ie.button));const j=K("div",{class:`learn-game-code learn-bomb-code mono${P?" by-parts":""}`,dir:"ltr"},...B),Z=Date.now(),F=()=>{if(!n.isConnected&&Date.now()-Z>1e3)return c();const ie=Math.max(0,z-(Date.now()-Z)/1e3);T.textContent=p("web_game_time_fmt",Math.ceil(ie)),I.firstChild.style.width=`${ie/z*100}%`,k.classList.toggle("hurry",ie<=5),ie<=0&&U("time")},U=(ie,ue)=>{c();for(const M of D)M.button.disabled=!0,M.right&&M.button.classList.add("right");P&&B[w.wrongLine-1].classList.add("right"),ue&&ie!=="right"&&ue.classList.add("wrong");const N=ie==="right";N&&u++,d(),k.classList.add(N?"defused":"boom"),k.firstChild.textContent=N?"✂️":"💥",C.className=`learn-game-feedback ${N?"right":"wrong"}`;const _=h();C.replaceChildren(K("p",{},p(N?"web_game_defused":ie==="time"?"web_game_boom_time":ie==="part"?"web_game_boom_part":P?"web_game_boom_wrong_part":"web_game_boom_wrong")),$n("div",{class:"learn-game-explain"},w.explanation),K("p",{class:"learn-bomb-fix"},`${p("web_game_fix")} `,K("code",{},w.fix.trim())),_),_.focus({preventScroll:!0})},ne=$n("p",{class:"learn-game-question"},w.question);P&&ne.append(" ",K("span",{class:"learn-bomb-hint"},p("web_game_bomb_parts_hint"))),r.replaceChildren(k,ne,j,C),F();const ge=window.setInterval(F,200);m.push(ge)},f=()=>{const w=i.rounds[l],z=[];w.code.trim()&&z.push(ic(w.code).element),z.push($n("p",{class:"learn-game-question"},w.question),K("p",{class:"learn-order-hint"},p("web_game_order_hint")));const I=K("div",{class:"learn-game-options learn-order"}),T=K("div",{class:"learn-game-feedback",role:"status"});let k=0;const C=pJ(w.options.length).map(D=>{const B=K("span",{class:"learn-order-badge","aria-hidden":"true"}),j=K("span",{});j.innerHTML=Ae(w.options[D].text);const Z=K("button",{},B,j);return Z.addEventListener("click",()=>{if(D!==k)return Z.classList.add("wrong"),P(!1);Z.classList.add("right"),B.textContent=String(++k),Z.disabled=!0,k===w.options.length&&P(!0)}),I.append(Z),{k:D,b:Z,badge:B}}),P=D=>{for(const{k:j,b:Z,badge:F}of[...C].sort((U,ne)=>U.k-ne.k))Z.disabled=!0,F.textContent=String(j+1),I.append(Z);D&&u++,d(),T.className=`learn-game-feedback ${D?"right":"wrong"}`;const B=h();T.replaceChildren(K("p",{},p(D?"web_game_right":"web_game_wrong_short")),$n("div",{class:"learn-game-explain"},w.explanation),B),B.focus({preventScroll:!0})};r.replaceChildren(...z,I,T)},b=()=>{if(c(),i.kind==="bombe"||i.kind==="reihenfolge"){d(),i.kind==="bombe"?g():f();return}const w=i.rounds[l];d();const z=ic(w.code),I=[];let T;i.kind==="zuhoerer"?(T=fJ(wp(w.code),z),I.push(K("div",{class:"learn-swing-row"},z.element,T.element)),w.clicks.length&&I.push(K("p",{class:"learn-swing-clicks"},`${p("web_game_clicks")} `,...w.clicks.flatMap((B,j)=>[...j?[" → "]:[],K("code",{},B)])))):w.code.trim()&&I.push(t&&i.kind==="frage"&&gp(w.code)||z.element),I.push($n("p",{class:"learn-game-question"},w.question));const k=K("div",{class:`learn-game-options ${i.kind==="layout"?"pictures":""}`}),C=K("div",{class:"learn-game-feedback",role:"status"}),P=w.options.map(B=>{const j=K("button",{});return i.kind==="layout"?j.append(xJ(B.text)):B.text.includes(` -`)?j.append(bJ(B.text)):j.innerHTML=Ae(B.text),j.addEventListener("click",()=>D(B.right,j)),k.append(j),j}),D=(B,j)=>{P.forEach((F,U)=>{F.disabled=!0,w.options[U].right&&F.classList.add("right")}),B||j.classList.add("wrong"),B&&u++,d(),C.className=`learn-game-feedback ${B?"right":"wrong"}`;const Z=h();C.replaceChildren(K("p",{},p(B?"web_game_right":"web_game_wrong_short")),$n("div",{class:"learn-game-explain"},w.explanation)),C.append(Z),T&&(m=T.replay(w.clicks,()=>Z.before(K("p",{class:"learn-swing-try"},p("web_game_try"))))),Z.focus({preventScroll:!0})};r.replaceChildren(...I,k,C)},x=()=>{c(),s.textContent="";const w=K("button",{class:"primary"},p("web_game_again"));w.addEventListener("click",()=>{l=u=0,b()}),r.replaceChildren(K("p",{class:"learn-game-result"},p("web_game_done_fmt",u,o)),K("p",{},p(u===o?"web_game_perfect":u>=o*.7?"web_game_good":"web_game_practice_short")),w)},v=K("button",{class:"primary"},p("web_game_start"));return v.addEventListener("click",b),r.append($n("p",{},i.intro),v),n}function pJ(i){const e=[...Array(i).keys()];for(;e.every((t,n)=>t===n);)for(let t=i-1;t>0;t--){const n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}return e}function ic(i){const e=i.split(` -`).map(n=>K("div",{class:"ln"},n||" "));return{element:K("pre",{class:"learn-game-code mono",dir:"ltr"},...e),highlight(n,a){e.forEach((s,r)=>s.classList.toggle("hit",n!==void 0&&r>=n&&r<=(a??n)))}}}function fJ(i,e){const t=yp(i),n=K("div",{class:"learn-swing-window",dir:"ltr"});n.append(K("div",{class:"learn-swing-title"},i.title||" "));const a=K("div",{class:"learn-swing-body"}),s=new Map;for(const c of i.labels){const d=K("div",{class:"learn-swing-label"},t.texts[c.name]);s.set(c.name,d),a.append(d)}const r=i.buttons.map(c=>{const d=K("button",{class:"learn-swing-button",disabled:""},t.texts[c.name]);return s.set(c.name,d),a.append(d),d}),o=K("p",{class:"learn-swing-event",role:"status"}),l=K("pre",{class:"learn-swing-console mono",hidden:""});n.append(a,o,l);const u=c=>{var h,g;const d=s.get(c.button);d.classList.add("pressed"),window.setTimeout(()=>d.classList.remove("pressed"),350),e.highlight((h=c.listener)==null?void 0:h.from,(g=c.listener)==null?void 0:g.to),o.textContent=c.listener?p("web_game_event_fmt",d.textContent??""):p("web_game_nobody"),o.className=`learn-swing-event ${c.listener?"heard":"nobody"}`;for(const[f,b]of Object.entries(c.texts)){const x=s.get(f);x&&x.textContent!==b&&(x.textContent=b,x.classList.remove("changed"),x.offsetWidth,x.classList.add("changed"))}c.console.length&&(l.hidden=!1,l.textContent+=c.console.map(f=>`${f} -`).join(""))},m=c=>u(zp(i,t,c));return{element:n,replay(c,d){const h=c.map((g,f)=>window.setTimeout(()=>m(g),(f+.3)*nc));return h.push(window.setTimeout(()=>{i.buttons.forEach((g,f)=>{r[f].disabled=!1,r[f].addEventListener("click",()=>u(Ip(i,t,g.name)))}),d()},(c.length+.3)*nc)),h}}}function bJ(i){return K("pre",{class:"learn-game-file mono",dir:"ltr"},i)}function xJ(i){const e=vp(i),t=K("div",{class:"learn-layout-window",dir:"ltr"},K("div",{class:"learn-layout-title"})),n=K("div",{class:`learn-layout-body ${e.kind}`}),a=(s,r)=>K("span",{class:"learn-layout-cell",...r?{style:`grid-area:${r}`}:{}},s);if(e.kind==="border")for(const[s,r]of Object.entries(e.areas))n.append(a(r,s));else if(e.kind==="grid"){const s=Math.max(...e.rows.map(r=>r.length));n.style.gridTemplateColumns=`repeat(${s}, 1fr)`;for(const r of e.rows)for(let o=0;oa(r))));return t.append(n),t}function H(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}function an(i,e,t){const n=H(i,e);return n.innerHTML=Ae(t),n}function ac(i){return H("pre",{class:"learn-game-code mono",dir:"ltr"},...i.split(` -`).map(e=>H("div",{class:"ln"},e||" ")))}function vJ(i,e){const t=H("section",{class:"learn-game learn-rescue"}),n=H("div",{class:"learn-game-head"});n.append(H("img",{src:e,alt:"",width:"40",height:"40"}),H("strong",{},i.title));const a=i.stations.length,s=i.stations.map(()=>H("span",{class:"learn-rescue-dot"})),r=H("img",{class:"learn-rescue-ludwig",src:e,alt:"",width:"32",height:"32"}),o=H("span",{class:"learn-rescue-tower","aria-hidden":"true"},"🏰",H("span",{class:"learn-rescue-zora"},"🐐"),H("span",{class:"learn-rescue-fox"},"🦊")),l=H("span",{class:"learn-rescue-free"},"🐐"),u=H("div",{class:"learn-rescue-path","aria-hidden":"true"},r,...s,o),m=H("p",{class:"learn-game-status"}),c=H("div",{class:"learn-game-stage"}),d=Gp(i);t.append(n,u,m,c);let h=0;const g=()=>{s.forEach((I,T)=>I.classList.toggle("done",T{const I=[...i.alphabet].map((T,k)=>H("span",{class:"learn-rescue-letter"},H("b",{},T),H("small",{},String(k))));return{element:H("div",{class:"learn-rescue-alphabet mono",dir:"ltr"},...I),cells:I}},b=I=>{const T=i.stations[I];m.textContent=p("web_learn_rescue_station_fmt",I+1,a,T.name);const k=f(),C=H("input",{type:"text",inputmode:"numeric",autocomplete:"off",class:"learn-rescue-number mono","aria-label":p("web_learn_rescue_number")}),P=H("button",{class:"primary"},p("web_learn_check")),D=H("div",{class:"learn-game-feedback",role:"status"}),B=()=>{const j=C.value.trim();if(!/^-?\d+$/.test(j)){D.className="learn-game-feedback wrong",D.replaceChildren(H("p",{},p("web_learn_rescue_not_number")));return}if(Number(j)!==T.answer){D.className="learn-game-feedback wrong",D.replaceChildren(H("p",{},p("web_learn_rescue_wrong")),...T.hint?[an("div",{class:"learn-game-explain"},`**${p("web_learn_hint")}:** ${T.hint}`)]:[]),C.select();return}C.disabled=P.disabled=!0;const Z=i.alphabet.charAt(T.answer);k.cells[T.answer].classList.add("hit"),d.give(T.row,T.col,Z),h++,g();const F=I+1===a,U=H("button",{class:"primary"},p(F?"web_learn_rescue_to_crossword":"web_game_next"));U.addEventListener("click",()=>F?x():b(I+1)),D.className="learn-game-feedback right",D.replaceChildren(H("p",{},p("web_game_right")),H("p",{class:"learn-rescue-charat mono",dir:"ltr"},`alphabet.charAt(${T.answer})`,H("span",{"aria-hidden":"true"}," → "),H("code",{},`'${Z}'`)),an("div",{class:"learn-game-explain"},T.explanation),H("p",{class:"learn-rescue-moved"},p("web_learn_rescue_letter_moved")),U),U.focus({preventScroll:!0})};P.addEventListener("click",B),C.addEventListener("keydown",j=>{j.key==="Enter"&&B()}),c.replaceChildren(ac(T.code),an("p",{class:"learn-game-question"},T.question),H("div",{class:"learn-rescue-answer"},C,P),D,H("p",{class:"learn-rescue-label"},p("web_learn_rescue_alphabet")),ac(`String alphabet = "${i.alphabet}";`),k.element,d.element),C.focus({preventScroll:!0})},x=()=>{m.textContent=p("web_learn_rescue_crossword");const I=H("div",{class:"learn-game-feedback",role:"status"}),T=H("button",{class:"primary"},p("web_learn_check"));T.addEventListener("click",()=>{if(!d.check()){I.className="learn-game-feedback wrong",I.replaceChildren(H("p",{},p("web_learn_rescue_cw_wrong")));return}T.disabled=!0,d.lock();const k=H("button",{class:"primary"},p("web_learn_rescue_to_gate"));k.addEventListener("click",v),I.className="learn-game-feedback right",I.replaceChildren(H("p",{},p("web_learn_rescue_cw_done")),k),k.focus({preventScroll:!0})}),d.unlock(),c.replaceChildren(an("p",{},i.crosswordText),d.element,T,I),d.focusFirst()},v=()=>{m.textContent=p("web_learn_rescue_gate");const I=H("input",{type:"text",autocomplete:"off",autocapitalize:"off",spellcheck:"false",class:"learn-rescue-word mono",size:String(i.keyword.length+2),"aria-label":p("web_learn_rescue_word")}),T=i.gateCode.split(` -`).map(D=>{const B=D.indexOf(Vs);return B<0?H("div",{class:"ln"},D||" "):H("div",{class:"ln"},D.slice(0,B),I,D.slice(B+Vs.length))}),k=H("button",{class:"primary"},p("web_learn_rescue_open")),C=H("div",{class:"learn-game-feedback",role:"status"}),P=()=>{const D=I.value.trim();if(D!==i.keyword){const j=D.toUpperCase()===i.keyword.toUpperCase();C.className="learn-game-feedback wrong",C.replaceChildren(H("p",{},p(j?"web_learn_rescue_gate_case":"web_learn_rescue_gate_wrong"))),I.select();return}I.disabled=k.disabled=!0,t.classList.add("freed"),r.after(l),m.textContent="";const B=H("button",{},p("web_game_again"));B.addEventListener("click",w),C.className="learn-game-feedback right",C.replaceChildren(H("p",{},p("web_learn_rescue_freed")),an("div",{class:"learn-game-explain"},i.end),B)};k.addEventListener("click",P),I.addEventListener("keydown",D=>{D.key==="Enter"&&P()}),c.replaceChildren(an("p",{},i.gateText),H("div",{class:"learn-game-code mono learn-rescue-gate",dir:"ltr"},...T),k,C),I.focus({preventScroll:!0})},w=()=>{h=0,t.classList.remove("freed"),l.remove(),d.reset(),g(),b(0)},z=H("button",{class:"primary"},p("web_game_start"));return z.addEventListener("click",w),c.append(an("p",{},i.intro),z),t}function wJ(i,e){const t=H("section",{class:"learn-game learn-rescue learn-crossword-game"}),n=H("div",{class:"learn-game-head"});n.append(H("img",{src:e,alt:"",width:"40",height:"40"}),H("strong",{},i.title));const a=Gp(i),s=H("div",{class:"learn-game-feedback",role:"status"}),r=H("button",{class:"primary"},p("web_learn_check"));return r.addEventListener("click",()=>{if(!a.check()){s.className="learn-game-feedback wrong",s.replaceChildren(H("p",{},p("web_learn_rescue_cw_wrong")));return}r.disabled=!0,a.lock();const o=H("button",{},p("web_game_again"));o.addEventListener("click",()=>{a.reset(),a.unlock(),r.disabled=!1,s.className="learn-game-feedback",s.replaceChildren(),a.focusFirst()}),s.className="learn-game-feedback right",s.replaceChildren(H("p",{},p("web_learn_rescue_cw_done")),H("p",{class:"learn-game-result"},H("span",{class:"mono",dir:"ltr"},i.keyword)),...i.end?[an("p",{},i.end)]:[],o)}),a.unlock(),t.append(n,an("p",{},i.intro),a.element,r,s),t}function Gp(i){const e=gR(i),t=new Map(i.solution.map((g,f)=>[`${g.row} ${g.col}`,f])),n=new Map;for(const g of i.words)n.set(`${g.row} ${g.col}`,g.number);const a=new Map,s=H("div",{class:"learn-cw-grid",dir:"ltr"});s.style.gridTemplateColumns=`repeat(${i.cols}, var(--cw-cell))`;let r=!0;const o=(g,f)=>i.words.filter(b=>b.across?b.row===g&&f>=b.col&&f=b.row&&g{var v;const x=r?`${g} ${f+b}`:`${g+b} ${f}`;(v=a.get(x))==null||v.focus()};e.forEach((g,f)=>g.forEach((b,x)=>{if(!b)return s.append(H("span",{class:"learn-cw-empty"}));const v=`${f} ${x}`,w=H("input",{type:"text",maxlength:"1",autocomplete:"off",autocapitalize:"characters",spellcheck:"false","aria-label":p("web_learn_rescue_cell_fmt",f+1,x+1)});w.disabled=!0,w.addEventListener("focus",()=>{const k=o(f,x);k.some(C=>C.across===r)||(r=k[0].across),w.select()}),w.addEventListener("input",()=>{w.value=w.value.slice(-1).toUpperCase(),w.classList.remove("wrong"),c(),w.value&&l(f,x,1)}),w.addEventListener("keydown",k=>{if(k.key==="Backspace"&&!w.value)l(f,x,-1);else if(k.key==="ArrowRight"||k.key==="ArrowLeft")r=!0,l(f,x,k.key==="ArrowRight"?1:-1);else if(k.key==="ArrowDown"||k.key==="ArrowUp")r=!1,l(f,x,k.key==="ArrowDown"?1:-1);else return;k.preventDefault()}),a.set(v,w);const z=t.get(v),I=H("span",{class:`learn-cw-cell${z!==void 0?" key":""}`}),T=n.get(v);T&&I.append(H("small",{class:"learn-cw-number"},String(T))),z!==void 0&&I.append(H("small",{class:"learn-cw-keyindex"},String(z+1))),I.append(w),s.append(I)}));const u=g=>{const f=H("ol",{class:"learn-cw-clues"});for(const b of i.words.filter(x=>x.across===g).sort((x,v)=>x.number-v.number)){const x=an("li",{value:String(b.number)},`${b.clue} (${b.word.length})`);x.addEventListener("click",()=>{var v;r=b.across,(v=a.get(`${b.row} ${b.col}`))==null||v.focus()}),f.append(x)}return H("div",{},H("strong",{},p(g?"web_learn_rescue_across":"web_learn_rescue_down")),f)},m=i.solution.map(()=>H("span",{class:"learn-cw-keybox"})),c=()=>i.solution.forEach((g,f)=>m[f].textContent=a.get(`${g.row} ${g.col}`).value||" "),d=H("div",{class:"learn-cw"},H("div",{class:"learn-cw-board"},s,H("p",{class:"learn-cw-keyword"},`${p("web_learn_rescue_keyword")} `,H("span",{class:"mono",dir:"ltr"},...m))),H("div",{class:"learn-cw-cluebox"},u(!0),u(!1))),h=new Set;return{element:d,give(g,f,b){const x=a.get(`${g} ${f}`);x.value=b,x.parentElement.classList.add("given","arrived"),h.add(`${g} ${f}`),c()},unlock(){for(const[g,f]of a)f.disabled=h.has(g)},lock(){for(const g of a.values())g.disabled=!0},focusFirst(){var g;(g=[...a].find(([f])=>!h.has(f)))==null||g[1].focus({preventScroll:!0})},check(){let g=!0;for(const[f,b]of a){const[x,v]=f.split(" ").map(Number),w=b.value!==e[x][v];b.classList.toggle("wrong",w),w&&(g=!1)}return g},reset(){h.clear();for(const g of a.values())g.value="",g.disabled=!0,g.classList.remove("wrong"),g.parentElement.classList.remove("given","arrived");c()}}}const yJ="http://www.w3.org/2000/svg";function it(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}function S1(i,e={}){const t=document.createElementNS(yJ,i);for(const[n,a]of Object.entries(e))t.setAttribute(n,String(a));return t}function ro(i,e,t){const n=it(i,e);return n.innerHTML=Ae(t),n}const zJ={"#":"#6b7280","=":"#111827",r:"#ef4444",g:"#22c55e",b:"#3b82f6",y:"#facc15"},IJ=450;function kJ(i,e,t=!1){const n=it("section",{class:"learn-game learn-round-game learn-drive-game"}),a=it("div",{class:"learn-game-head"});a.append(it("img",{src:e,alt:"",width:"40",height:"40"}),it("strong",{},i.title));const s=it("p",{class:"learn-game-status"}),r=it("div",{class:"learn-game-stage"});n.append(a,s,r);const o=i.rounds.length,l=i.field;let u=0,m=0,c=0;const d=()=>{s.textContent=`${p("web_game_round_fmt",u+1,o)} · ${p("web_game_score_fmt",m)}`},h=()=>{const x=it("button",{class:"primary"},u+1{u++,u{window.clearInterval(c),d();const x=i.rounds[u],v=x.map[0].length,w=x.map.length,z=v*l,I=w*l,T=_=>_+z/2,k=_=>I/2-_,C=jp(x,l),P=IR(x,l),D=S1("svg",{viewBox:`-2 -2 ${z+4} ${I+4}`,class:"learn-drive-map",role:"group","aria-label":x.question}),B=[];x.map.forEach((_,M)=>{B.push([]),Array.from(_).forEach((J,q)=>{const X=S1("rect",{x:q*l,y:M*l,width:l,height:l,class:"cell",fill:zJ[J]??"#ffffff",tabindex:0,role:"button","aria-label":`${q+1}, ${M+1}`}),Y=()=>N(q,M);X.addEventListener("click",Y),X.addEventListener("keydown",ae=>{(ae.key==="Enter"||ae.key===" ")&&(ae.preventDefault(),Y())}),D.append(X),B[M].push(X)})});const j=S1("polyline",{class:"trail",points:"",fill:"none"}),Z=S1("g",{class:"robot"}),F=Math.min(l*.7,20);Z.append(S1("rect",{x:-F/2,y:-F/2,width:F,height:F,rx:F/5,class:"body"}),S1("path",{d:`M ${-F/3} ${F/6} L 0 ${-F/2.4} L ${F/3} ${F/6} Z`,class:"nose"}));let U=C.start.heading;const ne=_=>{U+=((_.heading-U)%360+540)%360-180,Z.style.transform=`translate(${T(_.x)}px, ${k(_.y)}px) rotate(${U}deg)`};ne(C.start),D.append(j,Z);const ge=it("div",{class:"learn-game-feedback",role:"status"}),ie=t&&gp(x.code)||it("pre",{class:"learn-game-code mono",dir:"ltr"},...x.code.split(` -`).map(_=>it("div",{class:"ln"},_||" ")));let ue=!1;const N=(_,M)=>{if(ue)return;ue=!0,D.classList.add("done");const J=_===x.target.col&&M===x.target.row;B[M][_].classList.add(J?"right":"wrong"),J&&m++,d();const q=P.run.frames;let X=0;const Y=[`${T(C.start.x)},${k(C.start.y)}`],ae=()=>{if(X++,X>=q.length){window.clearInterval(c),B[x.target.row][x.target.col].classList.add("right");const xe=h();ge.className=`learn-game-feedback ${J?"right":"wrong"}`,ge.replaceChildren(it("p",{},p(J?"web_game_right":"web_game_wrong_short")),ro("div",{class:"learn-game-explain"},x.explanation),xe),xe.focus({preventScroll:!0});return}const re=q[X].pose;ne(re),Y.push(`${T(re.x)},${k(re.y)}`),j.setAttribute("points",Y.join(" "))};c=window.setInterval(()=>{if(!n.isConnected)return window.clearInterval(c);ae()},IJ)};r.replaceChildren(ie,ro("p",{class:"learn-game-question"},x.question),D,ge)},f=()=>{window.clearInterval(c),s.textContent="";const x=it("button",{class:"primary"},p("web_game_again"));x.addEventListener("click",()=>{u=m=0,g()}),r.replaceChildren(it("p",{class:"learn-game-result"},p("web_game_done_fmt",m,o)),it("p",{},p(m===o?"web_game_perfect":m>=o*.7?"web_game_good":"web_game_practice_short")),x)},b=it("button",{class:"primary"},p("web_game_start"));return b.addEventListener("click",g),r.append(ro("p",{},i.intro),b),n}function jt(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}function sc(i,e,t){const n=jt(i,e);return n.innerHTML=Ae(t),n}function SJ(i){const e=jt("section",{class:"learn-cloze"});e.append(jt("strong",{class:"learn-cloze-title"},i.title)),i.task&&e.append(sc("p",{},i.task)),e.append(jt("p",{class:"learn-cloze-hint"},p("web_learn_cloze_hint")));const t=[];for(const m of i.blocks){const c=jt("pre",{class:"learn-cloze-code mono",dir:"ltr"});for(const d of m){const h=jt("div",{class:"ln"});for(const g of d){if(typeof g=="string"){h.append(g);continue}const f=jt("input",{type:"text",class:"learn-cloze-gap mono",autocomplete:"off",autocapitalize:"off",autocorrect:"off",spellcheck:"false",enterkeyhint:"done","aria-label":`${t.length+1}`});f.style.width=`${Math.max(3,...g.answers.map(b=>b.length))+1}ch`,t.push({input:f,answers:g.answers}),h.append(f)}d.length||h.append(` -`),c.append(h)}e.append(c)}const n=jt("p",{class:"learn-cloze-status",role:"status"}),a=i.explanation?sc("p",{class:"learn-explanation"},i.explanation):void 0;a&&(a.hidden=!0);const s=jt("button",{class:"learn-cloze-check"},p("web_learn_check")),r=jt("button",{},p("web_learn_cloze_solution")),o=jt("button",{},p("web_learn_cloze_again"));r.hidden=!0,o.hidden=!0;const l=jt("div",{class:"learn-cloze-actions"},s,r,o);e.append(l,n),a&&e.append(a);const u=()=>{var d;let m=0;for(const h of t){const g=AR(h.answers,h.input.value);h.input.classList.toggle("right",g),h.input.classList.toggle("wrong",!g),g&&m++}const c=m===t.length;n.textContent=c?p("web_learn_cloze_all_right"):p("web_learn_cloze_score_fmt",m,t.length),n.className=`learn-cloze-status ${c?"right":"wrong"}`,a&&(a.hidden=!c),c?(t.forEach(h=>h.input.disabled=!0),s.hidden=!0,o.hidden=!1):(r.hidden=!1,(d=t.find(h=>h.input.classList.contains("wrong")))==null||d.input.focus())};return s.addEventListener("click",u),r.addEventListener("click",()=>{for(const m of t)m.input.value=m.answers[0],m.input.classList.remove("right","wrong"),m.input.classList.add("shown"),m.input.disabled=!0;n.textContent="",n.className="learn-cloze-status",a&&(a.hidden=!1),s.hidden=!0,r.hidden=!0,o.hidden=!1}),o.addEventListener("click",()=>{var m;for(const c of t)c.input.value="",c.input.classList.remove("right","wrong","shown"),c.input.disabled=!1;n.textContent="",n.className="learn-cloze-status",a&&(a.hidden=!0),s.hidden=!1,r.hidden=!0,o.hidden=!0,(m=t[0])==null||m.input.focus()}),t.forEach((m,c)=>{m.input.addEventListener("input",()=>m.input.classList.remove("right","wrong")),m.input.addEventListener("keydown",d=>{d.key==="Enter"&&(d.preventDefault(),c1?12:13});return n.forEach((s,r)=>{const o=kt("tspan",{x:e.x,y:e.y+4.5+(r-(n.length-1)/2)*14});o.textContent=s,a.append(o)}),a}function Pa(i,e){return kt("polyline",{points:i.map(t=>t.join(",")).join(" "),fill:"none","marker-end":`url(#${e})`})}function rc(i,e,t,n){const a=kt("text",{x:e,y:t,"font-size":12,"font-weight":600,fill:n?"#248a3d":"#d70015"});return a.textContent=i,a}function AJ(i,e){const t=Cp(e,110),n=Math.max(...t.map(u=>u.length)),a=Math.max(70,n*6.6+(i==="raute"?56:26)),s=i==="raute"?48:t.length>1?40:30,r=kt("svg",{viewBox:`0 0 ${a+4} ${s+4}`,width:a+4,height:s+4,"aria-hidden":"true"}),o={x:(a+4)/2,y:(s+4)/2,w:a,h:s},l=tu(i,o);return l.setAttribute("fill",eu[i]),l.setAttribute("stroke","#1c1c1e"),l.setAttribute("stroke-width","1.5"),r.append(l,Hp(e,{...o,w:t.length>1?124:a},i)),r}function jJ(i){const e=[...Array(i).keys()];for(let t=i-1;t>0;t--){const n=Math.floor(Math.random()*(t+1));[e[t],e[n]]=[e[n],e[t]]}return i>1&&e.every((t,n)=>t===n)&&e.reverse(),e}function CJ(){const i=Qe("dl",{class:"learn-pap-legend"}),e=[["oval","web_learn_pap_legend_oval"],["rechteck","web_learn_pap_legend_rect"],["raute","web_learn_pap_legend_diamond"],["parallelogramm","web_learn_pap_legend_para"]];for(const[t,n]of e){const a=t==="raute"?44:40,s=kt("svg",{viewBox:`0 0 ${a+4} 28`,width:a+4,height:28,"aria-hidden":"true"}),r=tu(t,{x:(a+4)/2,y:14,w:a,h:t==="raute"?24:18});r.setAttribute("fill",eu[t]),r.setAttribute("stroke","#1c1c1e"),r.setAttribute("stroke-width","1.5"),s.append(r),i.append(Qe("dt",{},s),Qe("dd",{},p(n)))}return i}function DJ(i){const e=Qe("section",{class:"learn-pap"});if(e.append(Qe("strong",{class:"learn-pap-title"},i.title)),i.task){const N=Qe("p");N.innerHTML=Ae(i.task),e.append(N)}e.append(Qe("p",{class:"learn-pap-hint"},p("web_learn_pap_hint")));const{boxes:t,height:n}=ZR(i),a=t.filter(Boolean),s=Math.min(...a.map(N=>N.x-N.w/2))-4,r=i.nodes.some(N=>N.shape==="raute"),o=Math.max(...a.map(N=>N.x+N.w/2),r?Math.max(...a.map(N=>N.x))+85+40:0)+4,l=kt("svg",{viewBox:`${s} 0 ${o-s} ${n}`,class:"learn-pap-plan",role:"group","aria-label":i.title}),u=`pap-arrow-${++_J}`;l.innerHTML=``;const m=kt("g",{stroke:"#1c1c1e","stroke-width":2}),c=kt("g"),d=p("web_learn_pap_yes"),h=p("web_learn_pap_no"),g=N=>t[N].y-t[N].h/2,f=N=>t[N].y+t[N].h/2,b=i.nodes.map((N,_)=>_).filter(N=>!i.nodes[N].isSide);b.forEach((N,_)=>{const M=i.nodes[N],J=t[N],q=b[_+1];if(q!==void 0&&(m.append(Pa([[J.x,f(N)],[J.x,g(q)-2]],u)),M.shape==="raute"&&c.append(rc(M.sideExit==="nein"?d:h,J.x+6,f(N)+13,M.sideExit==="nein"))),M.shape!=="raute")return;const X=J.x+J.w/2;if(c.append(rc(M.sideExit==="ja"?d:h,X+5,J.y-6,M.sideExit==="ja")),M.backTo!==void 0){const Y=g(M.backTo)-14;m.append(Pa([[X,J.y],[X+22,J.y],[X+22,Y],[J.x,Y]],u))}else if(M.side!==void 0){const Y=t[M.side],ae=g(N)-14;m.append(Pa([[X,J.y],[Y.x-Y.w/2-2,J.y]],u)),m.append(Pa([[Y.x,Y.y-Y.h/2],[Y.x,ae],[J.x,ae]],u))}}),l.append(m,c);const x=i.nodes.map(()=>{}),v=[],w=[];let z;const I=Qe("p",{class:"learn-pap-status",role:"status"}),T=i.explanation?Qe("p",{class:"learn-explanation"}):void 0;T&&(T.innerHTML=Ae(i.explanation),T.hidden=!0);let k=!1;const C=()=>{v.forEach(N=>N.classList.remove("right","wrong")),I.textContent="",I.className="learn-pap-status"},P=N=>{const _=v[N];_.replaceChildren();const M=i.nodes[N],J=x[N],q=tu(M.shape,t[N]);if(q.setAttribute("class","learn-pap-shape"),J===void 0)q.setAttribute("fill","#f7f7f9"),q.setAttribute("stroke-dasharray","6 4"),_.append(q),_.setAttribute("aria-label",p("web_learn_pap_empty"));else{const X=i.nodes[J];q.setAttribute("fill",eu[X.shape]),_.append(q,Hp(X.text,t[N],M.shape)),_.setAttribute("aria-label",X.text)}},D=()=>{i.nodes.forEach((N,_)=>P(_)),w.forEach((N,_)=>{N.hidden=x.includes(_),N.classList.toggle("selected",z===_)})},B=(N,_)=>{if(k)return;const M=x.indexOf(_);M>=0&&(x[M]=void 0),x[N]=_,z=void 0,C(),D()};i.nodes.forEach((N,_)=>{const M=kt("g",{class:"learn-pap-slot","data-slot":_,tabindex:0,role:"button"}),J=()=>{k||(z!==void 0?B(_,z):x[_]!==void 0&&(x[_]=void 0,C(),D()))};M.addEventListener("click",J),M.addEventListener("keydown",q=>{q.key!=="Enter"&&q.key!==" "||(q.preventDefault(),J())}),v.push(M),l.append(M)});const j=Qe("div",{class:"learn-pap-tray"});let Z=!1;for(const N of jJ(i.nodes.length)){const _=i.nodes[N],M=Qe("button",{class:"learn-pap-card",type:"button","aria-label":_.text},AJ(_.shape,_.text));w[N]=M,M.addEventListener("click",()=>{Z||k||(z=z===N?void 0:N,D())}),M.addEventListener("pointerdown",J=>{if(k)return;Z=!1;let q;const X=re=>{var xe;!q&&Math.hypot(re.clientX-J.clientX,re.clientY-J.clientY)<6||(q||(Z=!0,q=M.cloneNode(!0),q.classList.add("learn-pap-ghost"),document.body.append(q),M.classList.add("dragging")),q.style.left=`${re.clientX}px`,q.style.top=`${re.clientY}px`,v.forEach(Je=>Je.classList.remove("over")),(xe=Y(re))==null||xe.classList.add("over"))},Y=re=>{var Je;q&&(q.style.display="none");const xe=(Je=document.elementFromPoint(re.clientX,re.clientY))==null?void 0:Je.closest("[data-slot]");return q&&(q.style.display=""),xe&&v.includes(xe)?xe:void 0},ae=re=>{if(M.removeEventListener("pointermove",X),M.removeEventListener("pointerup",ae),M.removeEventListener("pointercancel",ae),v.forEach(Je=>Je.classList.remove("over")),M.classList.remove("dragging"),!q)return;const xe=re.type==="pointerup"?Y(re):void 0;q.remove(),xe&&B(Number(xe.getAttribute("data-slot")),N),setTimeout(()=>Z=!1)};M.setPointerCapture(J.pointerId),M.addEventListener("pointermove",X),M.addEventListener("pointerup",ae),M.addEventListener("pointercancel",ae)}),j.append(M)}const F=Qe("button",{class:"learn-pap-check"},p("web_learn_check")),U=Qe("button",{},p("web_learn_cloze_solution")),ne=Qe("button",{},p("web_learn_cloze_again"));U.hidden=!0,ne.hidden=!0;const ge=()=>{k=!0,F.hidden=!0,U.hidden=!0,ne.hidden=!1,j.hidden=!0,ue.classList.add("done"),T&&(T.hidden=!1)};F.addEventListener("click",()=>{const N=x.filter(J=>J===void 0).length;let _=0;x.forEach((J,q)=>{const X=J!==void 0&&CR(i,q,J);v[q].classList.toggle("right",X),v[q].classList.toggle("wrong",J!==void 0&&!X),X&&_++});const M=_===i.nodes.length;I.textContent=M?p("web_learn_pap_all_right"):N?p("web_learn_pap_missing_fmt",N):p("web_learn_pap_score_fmt",_,i.nodes.length),I.className=`learn-pap-status ${M?"right":"wrong"}`,M?ge():N||(U.hidden=!1)}),U.addEventListener("click",()=>{i.nodes.forEach((N,_)=>x[_]=_),z=void 0,D(),C(),v.forEach(N=>N.classList.add("shown")),ge()}),ne.addEventListener("click",()=>{i.nodes.forEach((N,_)=>x[_]=void 0),k=!1,z=void 0,v.forEach(N=>N.classList.remove("shown")),C(),D(),F.hidden=!1,ne.hidden=!0,j.hidden=!1,ue.classList.remove("done"),T&&(T.hidden=!0)}),D();const ie=Qe("div",{class:"learn-pap-left"});ie.append(l),i.legend&&ie.append(CJ());const ue=Qe("div",{class:"learn-pap-work"},ie,j);return e.append(ue,Qe("div",{class:"learn-pap-actions"},F,U,ne),I),T&&e.append(T),e}const BJ="code, pre, a, button, h1, h2, h3, h4, strong, .term, .ui-pointer";function oc(i,e){var s;const t=[...i.querySelectorAll(".learn-text")],n=new Set;for(const r of t.flatMap(o=>[...o.querySelectorAll("strong")])){const o=Lm(e),l=o==null?void 0:o.exec(r.textContent??""),u=l?Wm(e,l[0]):void 0;u&&n.add(u)}let a=e.filter(r=>!n.has(r));for(const r of t){const o=document.createTreeWalker(r,NodeFilter.SHOW_TEXT),l=[];for(;o.nextNode();){const u=o.currentNode;(s=u.parentElement)!=null&&s.closest(BJ)||l.push(u)}for(let u of l)for(;;){const m=Lm(a),c=m==null?void 0:m.exec(u.data),d=c?Wm(a,c[0]):void 0;if(!c||!d)break;const h=u.splitText(c.index);u=h.splitText(c[0].length);const g=document.createElement("span");g.className="term",g.tabIndex=0,g.setAttribute("role","button"),g.setAttribute("aria-describedby","term-tip"),g.dataset.term=d.term,g.dataset.text=d.text,h.replaceWith(g),g.append(h),a=a.filter(f=>f!==d)}}}function PJ(i){const e=document.createElement("div");e.id="term-tip",e.className="term-tip",e.setAttribute("role","tooltip"),e.hidden=!0,document.body.append(e);let t,n=!1;const a=o=>{t=o,e.replaceChildren();const l=document.createElement("strong");l.textContent=o.dataset.term??"",e.append(l,document.createTextNode(" "+(o.dataset.text??""))),e.hidden=!1;const u=o.getBoundingClientRect(),m=e.offsetWidth,c=e.offsetHeight,d=Math.max(8,Math.min(u.left,window.innerWidth-m-8)),h=u.bottom+6;e.style.left=`${d}px`,e.style.top=`${h+c>window.innerHeight-8?Math.max(8,u.top-c-6):h}px`,o.setAttribute("aria-expanded","true")},s=()=>{t==null||t.setAttribute("aria-expanded","false"),t=void 0,n=!1,e.hidden=!0},r=o=>{var l,u;return((u=(l=o.target)==null?void 0:l.closest)==null?void 0:u.call(l,".term"))??void 0};return i.addEventListener("pointerover",o=>{const l=r(o);l&&o.pointerType==="mouse"&&!n&&a(l)}),i.addEventListener("pointerout",o=>{r(o)&&o.pointerType==="mouse"&&!n&&s()}),i.addEventListener("click",o=>{const l=r(o);if(l){if(n&&t===l)return s();a(l),n=!0}}),i.addEventListener("focusin",o=>{const l=r(o);l&&!n&&a(l)}),i.addEventListener("focusout",o=>{r(o)&&!n&&s()}),i.addEventListener("keydown",o=>{const l=r(o);o.key==="Escape"&&t?s():l&&(o.key==="Enter"||o.key===" ")&&(o.preventDefault(),l.click())}),i.addEventListener("scroll",s,!0),document.addEventListener("pointerdown",o=>{t&&!r(o)&&!e.contains(o.target)&&s()}),{hide:s}}const Vp={leiste:".topbar > .toolbar",editor:".editor-rail, .columns > .app",werkzeuge:".diagram-tools",dateien:".files-rail, .files",codelive:".live-rail, .livecode",ausfuehren:"[data-run=play]",konsole:".livecode .console",einzelschritt:"[data-run=step]",elemente:".palette",struktogramm:".canvas",objektinspektor:".inspector",reiter:".tabs",titel:".caption",neu:"[data-action=new]",oeffnen:"[data-action=open]",speichern:"[data-action=save]",exportieren:"[data-action=export]",rueckgaengig:"[data-action=undo]",loeschen:"[data-action=delete]",textgroesse:"[data-action=settings]",einstellungen:"[data-action=settings]",lernen:".learn .learn-hide",code:"[data-action=code]",simulation:"[data-action=simulate]",bloecke:"[data-action=blocks]"},qp=new Set(["editor","werkzeuge","elemente","struktogramm","objektinspektor","reiter","titel","rueckgaengig","loeschen","exportieren","code","simulation","bloecke"]),Up=new Set(["ausfuehren","konsole","einzelschritt"]);function Cr(i,e){return[...i.querySelectorAll(".ui-pointer")].some(t=>e.has(t.dataset.target??""))}function lc(i){return i==="elemente"?["editor","palette"]:qp.has(i)?["editor"]:i==="codelive"||Up.has(i)?["livecode"]:i==="dateien"?["files"]:[]}function OJ(i){return Cr(i,new Set(["elemente"]))}function MJ(i){return Cr(i,new Set(["dateien"]))}function ZJ(i){return Cr(i,qp)}function LJ(i){return Cr(i,Up)}function uc(i){for(const e of i.querySelectorAll('a[href^="#zeige-"]')){const t=e.getAttribute("href").slice(7);if(!(t in Vp))continue;const n=document.createElement("span");n.className="ui-pointer",n.tabIndex=0,n.setAttribute("role","button"),n.dataset.target=t,n.append(...e.childNodes),e.replaceWith(n)}}function WJ(i,e=()=>()=>{}){let t,n,a,s,r;const o=()=>{t==null||t.classList.remove("ui-spot"),t=void 0,window.clearTimeout(n)},l=(h,g)=>{o();const f=[...document.querySelectorAll(Vp[h.dataset.target??""]??":not(*)")].find(b=>b.getClientRects().length>0);f&&(t=f,f.classList.add("ui-spot"),g&&(n=window.setTimeout(o,2500)))},u=(h,g)=>requestAnimationFrame(()=>{l(h,g),t==null||t.scrollIntoView({block:"nearest",inline:"nearest"})}),m=()=>{window.clearTimeout(s),window.clearTimeout(r),a==null||a(),a=void 0},c=h=>{window.clearTimeout(s),window.clearTimeout(r),a=void 0,e(lc(h.dataset.target??"")),l(h,!0),u(h,!0)},d=h=>{var g,f;return((f=(g=h.target)==null?void 0:g.closest)==null?void 0:f.call(g,".ui-pointer"))??void 0};return i.addEventListener("pointerover",h=>{const g=d(h);!g||h.pointerType!=="mouse"||(window.clearTimeout(r),l(g,!1),window.clearTimeout(s),s=window.setTimeout(()=>{if(a)return;const f=lc(g.dataset.target??"");f.length!==0&&(a=e(f),u(g,!1))},250))}),i.addEventListener("pointerout",h=>{!d(h)||h.pointerType!=="mouse"||(o(),window.clearTimeout(s),window.clearTimeout(r),r=window.setTimeout(m,400))}),i.addEventListener("click",h=>{const g=d(h);g&&c(g)}),i.addEventListener("focusin",h=>{const g=d(h);g&&l(g,!1)}),i.addEventListener("focusout",h=>{d(h)&&o()}),i.addEventListener("keydown",h=>{const g=d(h);g&&(h.key==="Enter"||h.key===" ")&&(h.preventDefault(),c(g))}),{off:()=>{m(),o()}}}const Dt='',EJ=()=>{const i=[];for(const e of ot){const t=i.at(-1);t&&t.name===e.section?t.lessons.push(e):i.push({name:e.section,lessons:[e]})}return i};function A(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}function Ue(i,e,t){const n=A(i,e);return n.innerHTML=t,n}const NJ=i=>p(`element_${S[i]}`);function FJ(i){return{caption:i,elements:[je(S.anweisung,{textLines:["ø"]})]}}function mc(i){const e=i.start?bi(i.start):void 0;return e?{...e,caption:e.caption||i.title}:FJ(i.title)}class RJ{constructor(e){y(this,"element");y(this,"tab","lessons");y(this,"returnTo");y(this,"lessonId");y(this,"taskId");y(this,"body");y(this,"tips");y(this,"pointers");this.host=e,this.element=A("aside",{class:"learn","aria-label":p("web_learn_title")}),this.tips=PJ(this.element),this.pointers=WJ(this.element,t=>this.host.reveal(t)),Vi(Hm(In())),this.restore()}restore(){const e=ve.view();e&&(this.tab=["lessons","tasks","reference"].find(t=>t===e.tab)??"lessons",this.lessonId=ot.some(t=>t.id===e.lesson)?e.lesson:void 0,this.taskId=bn.some(t=>t.id===e.task)?e.task:void 0),this.render()}render(){this.tips.hide(),this.pointers.off(),Vi(Hm(In()));const e=A("div",{class:"segmented learn-tabs",role:"tablist"});for(const[a,s]of[["lessons",p("web_learn_lessons")],["tasks",p("web_learn_tasks")],["reference",p("web_learn_reference")]]){const r=A("button",{role:"tab","aria-pressed":String(this.tab===a),"data-learn-tab":a},s);r.addEventListener("click",()=>{this.tab===a&&(a==="lessons"&&(this.lessonId=void 0),a==="tasks"&&(this.taskId=void 0)),this.tab=a,this.persist(),this.render()}),e.append(r)}const t=Ue("button",{class:"learn-hide",title:p("web_learn_hide"),"aria-label":p("web_learn_hide")},Dt);t.addEventListener("click",()=>this.host.collapse());const n=A("div",{class:"learn-head"},e,t);if(this.body=A("div",{class:"learn-body"}),this.element.replaceChildren(n,this.body),this.tab==="lessons"){const a=ot.find(s=>s.id===this.lessonId);a?this.renderLesson(a):this.renderLessonList()}else if(this.tab==="tasks"){const a=bn.find(s=>s.id===this.taskId);a?this.renderTask(a):this.renderTaskList()}else{const a=fs.map(r=>`- **${r.term}:** ${r.text}`).join(` -`),s=Ue("div",{class:"learn-text learn-reference"},Bn(`${ps} - -## ${p("web_learn_terms")} - -${a}`));this.body.append(s),this.foldReference(s)}}foldReference(e){const t="visustruct.reference.open";let n=[];try{n=JSON.parse(localStorage.getItem(t)??"[]")}catch{}for(const a of[...e.querySelectorAll("h2")]){const s=a.textContent??"",r=A("details",{class:"reference-group"});r.open=n.includes(s);const o=A("summary",{},s);for(a.replaceWith(r),r.append(o);r.nextSibling&&r.nextSibling.nodeName!=="H2";)r.append(r.nextSibling);r.addEventListener("toggle",()=>{n=r.open?[...new Set([...n,s])]:n.filter(l=>l!==s);try{localStorage.setItem(t,JSON.stringify(n))}catch{}})}}showReference(){this.tab="reference",this.persist(),this.render()}persist(){ve.setView({tab:this.tab,lesson:this.lessonId,task:this.taskId})}showLesson(e){ot.some(t=>t.id===e)&&this.openLesson(e)}openLesson(e){this.tab="lessons",this.lessonId=e,this.returnTo=void 0,this.persist(),this.render(),this.body.scrollTop=0}renderLessonList(){this.body.append(A("p",{class:"hint"},p("web_learn_intro")));for(const e of EJ()){this.body.append(A("h3",{class:"learn-section"},e.name));const t=A("ol",{class:"learn-list"});for(const n of e.lessons){const a=ve.lesson(n.id),s=a.done?"✓":a.step>0?p("web_learn_step_of_fmt",a.step+1,n.steps.length):"",r=A("button",{class:"learn-item"+(a.done?" done":""),"data-lesson":n.id},A("span",{class:"learn-num"},String(n.number)),A("span",{class:"learn-item-text"},A("strong",{},n.robot?`🤖 ${n.title}`:n.title),A("small",{},n.summary)),A("span",{class:"learn-status"},s));r.addEventListener("click",()=>this.openLesson(n.id)),t.append(A("li",{},r))}this.body.append(t)}this.body.append(this.renderTeacherBox())}renderLesson(e){var u;const t=ve.lesson(e.id),n=Math.min(t.step,e.steps.length-1),a=e.steps[n],s=A("button",{class:"learn-back"},`← ${p("web_learn_all_lessons")}`);s.addEventListener("click",()=>this.openLesson(void 0));const r=A("nav",{class:"learn-progress","aria-label":p("web_learn_step_of_fmt",n+1,e.steps.length)});r.append(...e.steps.map((m,c)=>{const d=p("web_learn_step_fmt",c+1,m.title),h=cthis.goToStep(e,c)),g})),this.body.append(A("div",{class:"learn-lesson-head"},A("div",{class:"learn-head-row"},s,this.stepNav(e,n,t.done,"learn-nav learn-nav-top")),A("p",{class:"learn-kicker"},`${p("web_learn_lesson_fmt",e.number)} · ${e.section}${e.robot?" · 🤖 VisuBot":""}`),A("h2",{class:"learn-title"},A("span",{class:"learn-num","aria-hidden":"true"},String(e.number)),e.title),r,A("h3",{class:"learn-step-title"},`${n+1}. ${a.title}`)));const o=((u=this.returnTo)==null?void 0:u.lesson)===e.id&&this.returnTo.step!==n?this.returnTo:void 0,l=()=>{const m=A("button",{class:"learn-return"},`↩ ${p("web_learn_back_to_checklist")}`);return m.addEventListener("click",()=>{this.returnTo=void 0,this.goToStep(e,o.step),this.body.scrollTop=o.scroll}),m};o&&this.body.append(l()),a.blocks.forEach((m,c)=>this.body.append(this.renderBlock(m,`${n}-${c}`,e))),uc(this.body),dJ(this.body),this.openNeededColumns(a.blocks),oc(this.body,fs),this.body.append(this.stepNav(e,n,t.done,"learn-nav")),o&&this.body.append(l())}stepNav(e,t,n,a){const s=A("div",{class:a}),r=A("button",{},`← ${p("web_learn_back")}`);if(r.disabled=t===0,r.addEventListener("click",()=>this.goToStep(e,t-1)),s.append(r),tthis.goToStep(e,t+1)),s.append(o)}else{const o=A("button",{class:"primary"},n?`✓ ${p("web_learn_done")}`:p("web_learn_finish"));o.addEventListener("click",()=>{ve.updateLesson(e.id,{done:!0});const l=ot.find(u=>u.number===e.number+1);this.openLesson(l==null?void 0:l.id)}),s.append(o)}return s}goToStep(e,t){ve.updateLesson(e.id,{step:t}),this.render(),this.body.scrollTop=0}openNeededColumns(e){const t=e.flatMap(n=>n.kind==="exercise"?[n.exercise]:[]);ZJ(this.body)?(this.host.showEditor(),OJ(this.body)&&this.host.reveal(["palette"])):MJ(this.body)?this.host.showFiles():t.some(n=>n.code!=="java")?this.host.showEditor():(t.length>0||this.body.querySelector(".lesson-code[data-lang=java]"))&&this.host.showJava(),LJ(this.body)&&this.host.showLiveCode()}renderTaskList(){this.body.append(A("p",{class:"hint"},p("web_learn_tasks_intro")));const e=A("ul",{class:"learn-list"});for(const t of bn){const n=this.taskExercises(t),a=n.length>0&&n.every(o=>ve.exercise(o.id).done),s=ot.find(o=>o.number===t.after),r=A("button",{class:"learn-item"+(a?" done":""),"data-task":t.id},A("span",{class:"learn-num","aria-label":p("web_learn_level_fmt",t.level)},"★".repeat(t.level)),A("span",{class:"learn-item-text"},A("strong",{},t.title),A("small",{},s?p("web_learn_after_fmt",s.number,s.title):"")),A("span",{class:"learn-status"},a?"✓":""));r.addEventListener("click",()=>{this.taskId=t.id,this.persist(),this.render(),this.body.scrollTop=0}),e.append(A("li",{},r))}this.body.append(e,this.renderTeacherBox())}renderTeacherBox(){const e=A("details",{class:"learn-teacher"},A("summary",{},p("web_learn_teacher")));if(Ym()){e.setAttribute("open","");const s=A("button",{},p("web_learn_teacher_off"));return s.addEventListener("click",()=>{aJ(),this.render()}),e.append(A("p",{},p("web_learn_teacher_on")),s),e}const t=A("input",{type:"password",autocomplete:"off","aria-label":p("web_learn_teacher_code"),placeholder:p("web_learn_teacher_code")}),n=A("p",{class:"learn-result",role:"status"}),a=A("form",{class:"group"},t,A("button",{type:"submit"},p("web_learn_teacher_unlock")));return a.addEventListener("submit",async s=>{s.preventDefault(),await iJ(t.value)?this.render():(n.className="learn-result info",n.textContent=p("web_learn_teacher_wrong"))}),e.append(A("p",{class:"hint"},p("web_learn_teacher_intro")),a,n),e}taskExercises(e){return e.blocks.flatMap(t=>t.kind==="exercise"?[t.exercise]:[])}renderTask(e){const t=A("button",{class:"learn-back"},`← ${p("web_learn_all_tasks")}`);t.addEventListener("click",()=>{this.taskId=void 0,this.persist(),this.render()});const n=ot.find(a=>a.number===e.after);if(this.body.append(t,A("h2",{class:"learn-title"},e.title)),n){const a=A("button",{class:"link-button"},p("web_learn_after_fmt",n.number,n.title));a.addEventListener("click",()=>this.openLesson(n.id)),this.body.append(A("p",{class:"learn-kicker"},a))}e.blocks.forEach((a,s)=>this.body.append(this.renderBlock(a,`task-${s}`))),uc(this.body),this.openNeededColumns(e.blocks),oc(this.body,fs)}renderBlock(e,t,n){switch(e.kind){case"markdown":return this.withOpenButtons(Ue("div",{class:"learn-text"},Bn(e.text)));case"diagram":return this.renderDiagram(e.file,e.caption);case"exercise":return this.renderExercise(e.exercise);case"competences":return this.renderCompetences(e.items);case"checklist":return this.renderChecklist(e.items,t,n);case"quiz":return this.renderQuiz(e.questions,t,n);case"brag":return this.renderBrag(e.text,e.tip);case"images":return this.renderImages(e.files,e.caption);case"graphic":return e.animated?this.renderAnimation(e.file,e.caption):this.renderGraphic(e.file,e.caption,e.labeled);case"game":return this.renderGame(e.game);case"roundGame":return gJ(e.game,k1,qm());case"rescueGame":return vJ(e.game,k1);case"driveGame":return kJ(e.game,k1,qm());case"crosswordGame":return wJ(e.game,k1);case"caesar":return uJ(e.key,e.text);case"cloze":return SJ(e.cloze);case"pap":return DJ(e.pap);case"textFile":return Ep(e.name,e.content);case"robotModel":return cJ(e.focus,e.view,e.spin);case"robotQuiz":return hJ(e.tasks,e.done)}}renderBrag(e,t=!1){const n=A("aside",{class:"learn-brag"});n.append(A("img",{src:k1,alt:"",width:"48",height:"48"}));const a=A("div",{},A("p",{class:"learn-brag-title"},p(t?"web_learn_ludwig_tip":"web_learn_brag")));return a.append(Ue("div",{class:"learn-text"},Bn(e))),n.append(a),n}renderGame(e){const n=A("section",{class:"learn-game"}),a=A("div",{class:"learn-game-head"});a.append(A("img",{src:k1,alt:"",width:"40",height:"40"}),A("strong",{},p("web_game_title")));const s=A("p",{class:"learn-game-status"}),r=A("div",{class:"learn-game-stage"});n.append(a,s,r);let o=0,l=0;const u=()=>{const d=Ev(e,o,10);s.textContent=`${p("web_game_round_fmt",o+1,10)} · ${p("web_game_score_fmt",l)}`;const h=A("div",{class:"learn-game-values mono"});for(const[w,z]of Nv(d))h.append(A("code",{},`${w} = ${z}`));const g=A("div",{class:"group learn-game-answers"}),f=A("button",{class:"learn-game-true"},`✓ ${p("web_game_true")}`),b=A("button",{class:"learn-game-false"},`✗ ${p("web_game_false")}`);g.append(f,b);const x=A("div",{class:"learn-game-feedback",role:"status"}),v=w=>{f.disabled=b.disabled=!0;const z=w===d.answer;z&&l++,s.textContent=`${p("web_game_round_fmt",o+1,10)} · ${p("web_game_score_fmt",l)}`,x.className=`learn-game-feedback ${z?"right":"wrong"}`;const I=A("div",{class:"learn-game-steps mono"});Lv(d.condition,d.values).forEach((k,C)=>I.append(A("div",{},`${C?"→ ":""}${k}`)));const T=A("button",{class:"primary"},o+1<10?p("web_game_next"):p("web_game_finish"));T.addEventListener("click",()=>{o++,o<10?u():m()}),x.replaceChildren(A("p",{},p(z?"web_game_right":"web_game_wrong")),I,T),T.focus()};f.addEventListener("click",()=>v(!0)),b.addEventListener("click",()=>v(!1)),r.replaceChildren(h,A("div",{class:"learn-game-condition mono"},d.condition),g,x)},m=()=>{s.textContent="";const d=A("button",{class:"primary"},p("web_game_again"));d.addEventListener("click",()=>{o=l=0,u()}),r.replaceChildren(A("p",{class:"learn-game-result"},p("web_game_done_fmt",l,10)),A("p",{},p(l===10?"web_game_perfect":l>=10*.7?"web_game_good":"web_game_practice")),d)},c=A("button",{class:"primary"},p("web_game_start"));return c.addEventListener("click",u),r.append(A("p",{},p("web_game_intro")),c),n}renderImages(e,t){const n=A("figure",{class:"learn-figure"}),a=A("div",{class:"learn-images"});for(const s of e){const r=Ba(s);r&&a.append(A("img",{src:r,alt:"",loading:"lazy"}))}return n.append(a),t&&n.append(Ue("figcaption",{},Ae(t))),n}renderGraphic(e,t,n){const a=A("figure",{class:"learn-figure"}),s=Ba(e),r=n?Ba(n):void 0;if(s&&r){const o=A("img",{class:"learn-graphic",src:s,alt:"",loading:"lazy"}),l=A("button",{class:"learn-graphic-toggle",type:"button",title:p("web_learn_graphic_labels"),"aria-pressed":"false"},o);l.addEventListener("click",()=>{const u=l.getAttribute("aria-pressed")!=="true";l.setAttribute("aria-pressed",String(u)),o.src=u?r:s}),a.append(l,A("p",{class:"learn-graphic-hint"},p("web_learn_graphic_labels")))}else s&&a.append(A("img",{class:"learn-graphic",src:s,alt:"",loading:"lazy"}));return t&&a.append(Ue("figcaption",{},Ae(t))),a}renderAnimation(e,t){const n=A("figure",{class:"learn-figure"}),a=Ba(e);if(!a)return n;const s=A("div",{class:"learn-graphic learn-animation"}),r=A("button",{class:"learn-replay",type:"button"},`↻ ${p("web_learn_replay")}`);let o=()=>{};return fetch(a).then(l=>l.text()).then(l=>{var d,h;s.innerHTML=l;const u=s.querySelector("svg"),m=(h=(d=u==null?void 0:u.getAttribute("class"))==null?void 0:d.match(/(\S+-)svg\b/))==null?void 0:h[1];if(!u||!m)return;if(o=()=>{u.classList.remove(`${m}play`),u.getBoundingClientRect(),u.classList.add(`${m}play`)},typeof IntersectionObserver>"u")return o();const c=new IntersectionObserver(g=>{g.some(f=>f.isIntersecting)&&(c.disconnect(),o())},{threshold:.6});c.observe(s)}).catch(()=>s.append(A("img",{src:a,alt:""}))),r.addEventListener("click",()=>o()),n.append(s,r),t&&n.append(Ue("figcaption",{},Ae(t))),n}withOpenButtons(e){for(const t of e.querySelectorAll("pre.lesson-code[data-open=editor]")){const n=t.textContent??"",a=A("button",{class:"learn-open-code"},p("web_learn_open_in_editor"));a.addEventListener("click",()=>this.host.openJavaCode(n)),t.after(a)}return e}renderDiagram(e,t){const n=bi(e),a=A("figure",{class:"learn-figure"});if(!n)return a;const s=this.host.preview(n);s&&a.append(A("div",{class:"learn-preview",dir:"ltr"},s)),t&&a.append(Ue("figcaption",{},Ae(t)));const r=A("button",{},p("web_learn_open_in_editor"));return r.addEventListener("click",()=>this.host.openDiagram(n,e)),a.append(r),a}renderExercise(e){const t=A("section",{class:"learn-exercise","data-exercise":e.id}),n=()=>ve.exercise(e.id),a=A("span",{class:"learn-badge"},`✓ ${p("web_learn_solved")}`);a.hidden=!n().done,t.append(A("h4",{},`${p("web_learn_exercise")}: ${e.title}`,a),JJ(e),Ue("div",{class:"learn-text"},Bn(e.task)));const s=A("div",{class:"learn-result",role:"status"}),r=A("div",{class:"learn-hints"}),o=A("div",{class:"group"}),l=A("button",{class:"primary"},p("web_learn_open_exercise"));l.addEventListener("click",()=>{const c=n().xml;let d=mc(e);if(c)try{d=z0(c)}catch{}this.host.openExercise(e,d)});const u=A("button",{},p("web_learn_check"));if(u.addEventListener("click",()=>{const c=this.host.exerciseDocument(e.id);if(!c){s.className="learn-result info",s.textContent=p("web_learn_open_first");return}if(c.elements.every(wr)){s.className="learn-result info",s.textContent=p("web_learn_empty");return}this.host.stopSimulation();const d=Bv(c,e.tests,this.host.subprogramsFor(e.id),bs),h=Pv(c),g=e.requires.map(x=>x==="forZaehlSchleife"?"forSchleife":x).filter(x=>x in S&&!h.has(S[x])),f=e.loopDepth!==void 0&&wl(c.elements)x.passed)&&g.length===0&&!f;this.showResult(s,d,g,b,f),b&&(ve.updateExercise(e.id,{done:!0}),a.hidden=!1)}),o.append(l,u),e.hints.length>0){const c=A("button",{},p("web_learn_hint"));c.addEventListener("click",()=>{const d=r.children.length;if(d>=e.hints.length)return;const h=e.hints.length>1?`**${p("web_learn_hint_fmt",d+1,e.hints.length)}:** `:"";r.append(Ue("aside",{class:"lesson-note"},Bn(h+e.hints[d]))),c.textContent=p("web_learn_hint_more"),c.toggleAttribute("disabled",d+1>=e.hints.length)}),o.append(c)}const m=A("button",{class:"danger-text"},p("web_learn_reset"));if(m.addEventListener("click",()=>{if(m.dataset.confirm!=="1"){m.dataset.confirm="1",m.textContent=p("web_learn_reset_confirm"),setTimeout(()=>{m.dataset.confirm="",m.textContent=p("web_learn_reset")},4e3);return}ve.resetExercise(e.id),this.host.resetExercise(e.id,mc(e)),a.hidden=!0,s.replaceChildren(),s.className="learn-result",m.dataset.confirm="",m.textContent=p("web_learn_reset")}),o.append(m),t.append(o,r,s),Ym()&&e.solution){const c=bi(e.solution);if(c){const d=A("details",{class:"learn-solution"},A("summary",{},p("web_learn_solution")));d.addEventListener("toggle",()=>{if(!d.open||d.querySelector(".learn-preview"))return;const h=this.host.preview(c);h&&d.append(A("div",{class:"learn-preview",dir:"ltr"},h));const g=A("button",{},p("web_learn_open_in_editor"));g.addEventListener("click",()=>{this.host.openDiagram(c,e.solution);for(const f of e.subprograms){const b=bi(f);b&&this.host.openDiagram(b,f)}});for(const f of e.subprograms){const b=bi(f),x=b&&this.host.preview(b);x&&d.append(A("div",{class:"learn-preview",dir:"ltr"},x))}d.append(g)}),t.append(d)}}return t}showResult(e,t,n,a,s){var o,l,u,m,c;e.className="learn-result "+(a?"ok":"fail"),e.replaceChildren(A("p",{class:"learn-result-head"},a?`🎉 ${p("web_learn_passed")}`:p("web_learn_failed")));for(const d of n)e.append(A("p",{},`✗ ${p("web_learn_missing_fmt",NJ(S[d]))}`));s&&e.append(A("p",{},`✗ ${p("web_visubot_depth_fmt",s)}`));const r=A("ul",{class:"learn-tests",dir:"auto"});for(const d of t){const h=d.test.world?t.filter(x=>x.test.world).indexOf(d)+1:0,g=d.test.world?`🗺 ${p("web_visubot_world_fmt",h)}`:d.test.actions?`🤖 ${p("web_visubot_actions")}`:d.test.sensors?`🎛 ${Object.entries(d.test.sensors).map(([x,v])=>`mbot.${x}() = ${v.join(" → ")}`).join(", ")}`:d.test.drive?`🤖 ${p("web_visubot_drive")}`:d.test.inputs.length?p("web_learn_input_fmt",d.test.inputs.join(", ")):p("web_learn_no_input"),f=[`${d.passed?"✓":"✗"} ${g}`],b=A("div",{class:"learn-test-detail"});if(d.test.drive&&b.append(A("span",{},`${p("web_learn_expected")}: `,A("code",{},pi(d.test.drive)||"–")),A("span",{},`${p("web_learn_actual")}: `,A("code",{},pi(d.result.drive??[])||"–"))),d.test.actions&&b.append(A("span",{},`${p("web_learn_expected")}: `,A("code",{},pi(d.test.actions)||"–")),A("span",{},`${p("web_learn_actual")}: `,A("code",{},pi(d.result.actions??[])||"–"))),d.test.led!==void 0){const x=v=>p(v?"web_visubot_led_on":"web_visubot_led_off");b.append(A("span",{},`💡 ${p("web_learn_expected")}: ${x(d.test.led)} · ${p("web_learn_actual")}: ${x(d.result.led)}`))}if(d.test.tone!==void 0){const x=v=>p(v?"web_visubot_tone_yes":"web_visubot_tone_no");b.append(A("span",{},`🔊 ${p("web_learn_expected")}: ${x(d.test.tone)} · ${p("web_learn_actual")}: ${x(d.result.tone)}`))}d.test.screen!==void 0&&b.append(A("span",{},`📺 ${p("web_learn_expected")}: `,A("code",{},d.test.screen),` · ${p("web_learn_actual")}: `,A("code",{},d.result.screen??"–"))),d.test.world&&(((l=(o=bs(d.test.world))==null?void 0:o.goals)!=null&&l.length||(m=(u=bs(d.test.world))==null?void 0:u.targets)!=null&&m.length)&&b.append(A("span",{},d.result.atGoal?`✓ ${p("web_visubot_goal_reached")}`:`✗ ${p("web_visubot_goal_missed")}`)),d.result.bumps&&b.append(A("span",{class:"learn-error"},`⚠︎ ${p("web_visubot_bumps")}: ${d.result.bumps}`))),(!d.test.drive&&!d.test.world&&!d.test.sensors&&!d.test.actions||d.result.outputs.length)&&b.append(A("span",{},`${p("web_learn_expected")}: `,A("code",{},d.test.expected.join(" | ")||"–")),A("span",{},`${p("web_learn_actual")}: `,A("code",{},d.result.outputs.join(" | ")||"–")));for(const x of d.test.files??[])b.append(A("span",{},`📄 ${x.name}, ${p("web_learn_expected")}: `,A("code",{},x.lines.join(" | ")||"–")),A("span",{},`📄 ${x.name}, ${p("web_learn_actual")}: `,A("code",{},bp((c=d.result.files)==null?void 0:c.get(x.name)).join(" | ")||"–")));!d.passed&&!d.result.error&&Dv(d.result.outputs,d.test.expected)&&b.append(A("span",{class:"learn-error"},p("web_learn_missing_line_break"))),d.result.error&&b.append(A("span",{class:"learn-error"},m1(d.result.error))),d.result.missingInput&&b.append(A("span",{class:"learn-error"},p("web_learn_missing_input"))),d.result.tooManySteps&&b.append(A("span",{class:"learn-error"},p("web_learn_too_many_steps"))),!d.passed&&d.result.unusedInputs&&b.append(A("span",{class:"learn-error"},p("web_learn_unused_input"))),r.append(A("li",{class:d.passed?"ok":"fail"},...f,b))}e.append(r)}renderCompetences(e){const t=A("aside",{class:"learn-competences"},A("p",{class:"learn-competences-title"},p("web_learn_competences_intro"))),n=A("ul",{});for(const a of e)n.append(Ue("li",{},Ae(a)));return t.append(n),t}renderChecklist(e,t,n){const a=[{value:"umbrella",icon:"☂️",label:p("web_learn_rating_umbrella")},{value:"cloud",icon:"⛅",label:p("web_learn_rating_cloud")},{value:"sun",icon:"☀️",label:p("web_learn_rating_sun")}],s=A("div",{class:"learn-checklist-wrap"}),r=A("p",{class:"learn-rating-legend"});for(const l of a)r.append(A("span",{},`${l.icon} ${l.label}`));const o=A("ul",{class:"learn-checklist"});return s.append(r,o),e.forEach((l,u)=>{var v;const m=`${t}-${u}`,c=n?(v=ve.lesson(n.id).checks)==null?void 0:v[m]:void 0;let d=c===!0?"sun":c||void 0;const h=Ue("span",{class:"learn-check-text"},Ae(l.text)),g=A("span",{class:"learn-rating",role:"group","aria-label":h.textContent??""}),f=a.map(w=>{const z=A("button",{class:"learn-rating-button",title:w.label,"aria-label":w.label},w.icon);return z.addEventListener("click",()=>{if(d=d===w.value?void 0:w.value,b(),!n)return;const I={...ve.lesson(n.id).checks??{}};d?I[m]=d:delete I[m],ve.updateLesson(n.id,{checks:Object.keys(I).length?I:void 0})}),z}),b=()=>{f.forEach((w,z)=>w.setAttribute("aria-pressed",String(a[z].value===d))),x.dataset.rating=d??""};g.append(...f);const x=A("li",{},h,g);if(n&&l.step!==void 0){const w=l.step,z=p("web_learn_step_fmt",w+1,n.steps[w].title),I=A("button",{class:"link-button learn-check-link",title:z,"aria-label":`${p("web_learn_review")}: ${z}`},p("web_learn_review"));I.addEventListener("click",()=>{this.returnTo={lesson:n.id,step:ve.lesson(n.id).step,scroll:this.body.scrollTop},this.goToStep(n,w)}),x.append(I)}b(),o.append(x)}),s}renderQuiz(e,t,n){const a=A("div",{class:"learn-quiz"}),s=A("p",{class:"learn-score",role:"status"}),r=()=>n?ve.lesson(n.id).quiz??{}:{},o=A("div",{class:"learn-review"}),l=()=>{const u=r(),m=(f,b)=>f.pairs?f.pairs.every((x,v)=>u[`${t}-${b}-${v}`]!==void 0):u[`${t}-${b}`]!==void 0,c=(f,b)=>{var x;return!!(f.pairs?f.pairs.every((v,w)=>cc(f)[u[`${t}-${b}-${w}`]]===f.pairs[w].right):(x=f.options[u[`${t}-${b}`]])!=null&&x.correct)},d=e.filter(m).length,h=e.filter(c).length,g=d===e.length;s.textContent=g?p("web_learn_score_fmt",h,e.length):"",o.replaceChildren(),g&&n&&this.fillChecklistReview(o,n,e,f=>c(e[f],f))};return e.forEach((u,m)=>{const c=`${t}-${m}`,d=A("fieldset",{},Ue("legend",{},Ae(u.question))),h=Ue("p",{class:"learn-explanation"},Ae(u.explanation??""));if(u.pairs){d.append(this.renderMatch(u,c,r,n,h,l),h),a.append(d);return}const g=f=>{d.querySelectorAll("button").forEach((b,x)=>{b.classList.toggle("right",f!==void 0&&u.options[x].correct),b.classList.toggle("wrong",f===x&&!u.options[x].correct),b.setAttribute("aria-pressed",String(f===x))}),h.hidden=f===void 0||!u.explanation};u.options.forEach((f,b)=>{const x=Ue("button",{class:"learn-option"},Ae(f.text));x.addEventListener("click",()=>{n&&ve.updateLesson(n.id,{quiz:{...r(),[c]:b}}),g(b),l()}),d.append(x)}),d.append(h),g(r()[c]),a.append(d)}),a.append(s,o),l(),a}fillChecklistReview(e,t,n,a){const s={sun:"☀️",cloud:"⛅",umbrella:"☂️"},r=ve.lesson(t.id).checks??{},o=[];let l=0;t.steps.forEach((u,m)=>u.blocks.forEach((c,d)=>{c.kind==="checklist"&&c.items.forEach((h,g)=>{const f=r[`${m}-${d}-${g}`],b=f===!0?"sun":f||void 0,x=h.step===void 0?[]:n.map((T,k)=>T.step===h.step?k:-1).filter(T=>T>=0),v=x.filter(T=>!a(T));let w="";b==="sun"&&v.length?w=p("web_learn_review_overrated_fmt",v.map(T=>T+1).join(", ")):b&&b!=="sun"&&x.length&&!v.length&&(w=p("web_learn_review_underrated")),w&&l++;const z=A("li",{class:w?"learn-review-deviation":""}),I=Ue("span",{class:"learn-check-text"},Ae(h.text));if(w&&I.append(A("br",{}),A("em",{},w)),z.append(I,A("span",{class:"learn-review-icon","aria-label":p(b?`web_learn_rating_${b}`:"web_learn_review_unrated")},b?s[b]:"–")),w&&b==="sun"&&h.step!==void 0){const T=h.step,k=A("button",{class:"link-button learn-check-link"},p("web_learn_review"));k.addEventListener("click",()=>{this.returnTo={lesson:t.id,step:ve.lesson(t.id).step,scroll:this.body.scrollTop},this.goToStep(t,T)}),z.append(k)}o.push(z)})})),o.length&&e.append(A("p",{class:"learn-review-title"},`📋 ${p("web_learn_review_title")}`),A("p",{},p(l?"web_learn_review_hint":"web_learn_review_match")),A("ul",{class:"learn-checklist"},...o))}renderMatch(e,t,n,a,s,r){const o=e.pairs,l=cc(e),u=A("div",{class:"learn-match"});let m={};const c=()=>a?n():m,d=b=>a?ve.updateLesson(a.id,{quiz:b}):m=b,h=b=>c()[`${t}-${b}`],g=o.map(b=>{const x=A("select",{class:"learn-match-select"});x.append(A("option",{value:""},"–")),l.forEach((w,z)=>x.append(A("option",{value:String(z)},w)));const v=Ue("label",{class:"learn-match-left"},Ae(b.left));return v.append(x),u.append(v),x}),f=()=>{const b=o.every((x,v)=>h(v)!==void 0);g.forEach((x,v)=>{const w=h(v);x.value=w===void 0?"":String(w),x.classList.toggle("right",b&&l[w]===o[v].right),x.classList.toggle("wrong",b&&l[w]!==o[v].right)}),s.hidden=!b||!e.explanation};return g.forEach((b,x)=>b.addEventListener("change",()=>{const v={...c()};b.value===""?delete v[`${t}-${x}`]:v[`${t}-${x}`]=Number(b.value),d(v),f(),r()})),f(),u}}function cc(i){return(i.pairs??[]).map(e=>e.right).sort((e,t)=>e.localeCompare(t,"de"))}function JJ(i){const e=Dp(i),t=e==="beide"?"web_learn_solve_in_both":e==="code"?"web_learn_solve_in_code":"web_learn_solve_in_editor",n={editor:p("web_editor_title"),code:p("web_live_title")},a=e==="beide"?["editor","code"]:[e==="code"?"code":"editor"],s=A("p",{class:`learn-where learn-where-${e}`});for(const r of p(t,...a).split(/(\u0001(?:editor|code))/))if(r==="editor"||r==="code"){const o=r.slice(1);s.append(A("span",{class:`area-name area-${o}`},n[o]))}else r&&s.append(r);return s}const Oa=()=>typeof window.showDirectoryPicker=="function";class Dr{constructor(e){this.handle=e}get name(){return this.handle.name}async list(){const e=[];for await(const t of this.handle.values())t.name.startsWith(".")||e.push({kind:t.kind,name:t.name});return e}async dir(e,t=!1){return new Dr(await this.handle.getDirectoryHandle(e,{create:t}))}async read(e){return(await(await this.handle.getFileHandle(e)).getFile()).text()}async write(e,t){const a=await(await this.handle.getFileHandle(e,{create:!0})).createWritable();await a.write(t),await a.close()}async has(e){try{return await this.handle.getFileHandle(e),!0}catch{try{return await this.handle.getDirectoryHandle(e),!0}catch{return!1}}}async remove(e){await this.handle.removeEntry(e,{recursive:!0})}}const GJ="visustruct-workspace";function HJ(){return new Promise((i,e)=>{const t=indexedDB.open(GJ,1);t.onupgradeneeded=()=>{t.result.createObjectStore("files"),t.result.createObjectStore("meta")},t.onsuccess=()=>i(t.result),t.onerror=()=>e(t.error)})}async function Ct(i,e,t){const n=await HJ();return new Promise((a,s)=>{const r=t(n.transaction(i,e).objectStore(i));r.onsuccess=()=>a(r.result),r.onerror=()=>s(r.error)})}class nu{constructor(e,t){this.name=e,this.path=t}key(e){return this.path+e}async list(){const e=await Ct("files","readonly",n=>n.getAllKeys()),t=new Map;for(const n of e){if(!n.startsWith(this.path)||n===this.path)continue;const a=n.slice(this.path.length),s=a.indexOf("/"),r=s<0?a:a.slice(0,s);t.has(r)||t.set(r,{kind:s<0?"file":"directory",name:r})}return[...t.values()]}async dir(e,t=!1){const n=this.key(e)+"/";if(!(await Ct("files","readonly",s=>s.count(n))>0)){if(!t)throw new DOMException(e,"NotFoundError");await Ct("files","readwrite",s=>s.put("",n))}return new nu(e,n)}async read(e){const t=await Ct("files","readonly",n=>n.get(this.key(e)));if(typeof t!="string")throw new DOMException(e,"NotFoundError");return t}async write(e,t){await Ct("files","readwrite",n=>n.put(t,this.key(e)))}async has(e){const t=await Ct("files","readonly",a=>a.count(this.key(e))),n=await Ct("files","readonly",a=>a.count(this.key(e)+"/"));return t+n>0}async remove(e){const t=this.key(e);await Ct("files","readwrite",n=>n.delete(t)),await Ct("files","readwrite",n=>n.delete(IDBKeyRange.bound(t+"/",t+"/￿")))}}async function VJ(i,e="folder"){const t=await window.showDirectoryPicker({id:e==="folder"?"visustruct-workspace":e,mode:"readwrite"}),n=i&&t.name!==i?await t.getDirectoryHandle(i,{create:!0}):t;return await Ct("meta","readwrite",a=>a.put(n,e)),{kind:"folder",root:new Dr(n)}}function dc(){return{kind:"browser",root:new nu("VisuStruct","")}}async function qJ(i="folder"){var e;try{const t=await Ct("meta","readonly",a=>a.get(i));if(!t)return;const n=await((e=t.queryPermission)==null?void 0:e.call(t,{mode:"readwrite"}))??"granted";return{handle:t,ready:n==="granted"}}catch{return}}async function hc(i){var t;return(await((t=i.requestPermission)==null?void 0:t.call(i,{mode:"readwrite"}))??"granted")==="granted"?{kind:"folder",root:new Dr(i)}:void 0}async function UJ(i="folder"){try{await Ct("meta","readwrite",e=>e.delete(i))}catch{}}const Ma="learnProgress.json",Za="lernstand.json",La="visustruct.files.mode",gc="visustruct.files.expanded";function QJ(i){try{const e=JSON.parse(Qp(i)??"[]");return Array.isArray(e)?e.filter(t=>typeof t=="string"):[]}catch{return[]}}function Qp(i){try{return localStorage.getItem(i)}catch{return null}}function Wa(i,e){try{e===null?localStorage.removeItem(i):localStorage.setItem(i,e)}catch{}}const te=i=>i.replace(/&/g,"&").replace(//g,">").replace(/"/g,""");function pc(i){return new Promise(e=>{const t=document.createElement("div");t.className="confirm";const n=(i.choices??[]).map(o=>``).join("");t.innerHTML=``;const a=t.querySelector("form"),s=a.querySelector("input"),r=o=>{t.remove(),e(o)};a.addEventListener("submit",o=>{if(o.preventDefault(),!s)return r("");s.value.trim()?r(s.value):s.focus()}),t.addEventListener("click",o=>{var m;const l=o.target,u=(m=l.closest("[data-choice]"))==null?void 0:m.dataset.choice;u!==void 0?r(u):(l===t||l.closest("[data-cancel]"))&&r(void 0)}),t.addEventListener("keydown",o=>o.key==="Escape"&&r(void 0)),document.body.append(t),(s??a.querySelector("[data-cancel]")).focus()})}class $J{constructor(e){y(this,"element");y(this,"body");y(this,"workspace");y(this,"pending");y(this,"expanded",new Set(QJ(gc)));y(this,"rendering",0);y(this,"progressWorkspace");y(this,"hadWorkspace",!1);y(this,"progressTimer");this.host=e,this.element=document.createElement("aside"),this.element.className="files",this.build(),ve.onChange(()=>this.progressChanged()),this.restore()}rememberExpanded(){Wa(gc,JSON.stringify([...this.expanded]))}get ready(){return!!this.workspace}build(){this.element.innerHTML=` -
    -

    ${te(p("web_files_title"))}

    - - - -
    -
    - `,this.body=this.element.querySelector(".files-body");const e=(t,n)=>this.element.querySelector(`[data-files=${t}]`).addEventListener("click",n);e("hide",()=>this.host.hide()),e("new-folder",()=>void this.newFolder()),e("switch",()=>void this.switchWorkspace()),this.body.addEventListener("click",t=>void this.onTreeClick(t)),this.render()}async restore(){if(Qp(La)==="browser")this.workspace=dc();else if(Oa()){const e=await qJ();e!=null&&e.ready?this.workspace=await hc(e.handle):this.pending=e==null?void 0:e.handle}await this.render(),this.workspace||this.startDialog()}async startDialog(){var r;const e=p("web_learn_start_folder"),t=(o,l,u=!1,m)=>`${m?`

    ${te(m)}

    `:""}`,n=Oa()?[this.pending?t("reconnect",`${me.open} ${te(p("web_learn_start_continue_fmt",this.pending.name))}`,!0):"",t("pick",`${me.open} ${te(p("web_learn_start_open"))}`,!this.pending),t("create",`${me.newFolder} ${te(p("web_learn_start_create_fmt",e))}`,!1,p("web_learn_start_create_hint"))]:[t("browser",te(p("web_files_browser")),!0,p("web_files_browser_hint"))],a=document.createElement("div");a.className="confirm",a.innerHTML=``;const s=()=>a.remove();a.addEventListener("click",o=>{var m,c;const l=(m=o.target.closest("[data-world]"))==null?void 0:m.dataset.world;if(l){this.host.chooseWorld(l).then(()=>a.querySelector(".start-world").innerHTML=pl());return}const u=(c=o.target.closest("[data-start]"))==null?void 0:c.dataset.start;u&&(s(),u==="reconnect"?this.reconnect():u==="pick"?this.connectFolder():u==="create"?this.connectFolder(e):u==="browser"&&this.useBrowser())}),a.addEventListener("keydown",o=>o.key==="Escape"&&s()),document.body.append(a),(r=a.querySelector(".files-start"))==null||r.focus()}async connectFolder(e){try{this.workspace=await VJ(e),this.pending=void 0,Wa(La,"folder")}catch{}await this.render()}async reconnect(){this.pending&&(this.workspace=await hc(this.pending),this.workspace&&(this.pending=void 0),await this.render())}async useBrowser(){this.workspace=dc(),Wa(La,"browser"),await this.render()}async switchWorkspace(){if(Oa())return this.connectFolder();this.workspace=void 0,Wa(La,null),await UJ(),await this.render()}async syncProgress(e){if(this.progressWorkspace=e,!e)return;const t=!this.hadWorkspace;this.hadWorkspace=!0;try{const n=await e.root.has(Ma)?Ma:await e.root.has(Za)?Za:void 0;(!n||!ve.loadFile(await e.root.read(n),t))&&(t||ve.clear()),this.host.progressLoaded(),await this.writeProgress(e),await e.root.has(Za)&&(await e.root.remove(Za),await this.render())}catch{}}async writeProgress(e){if(e===this.progressWorkspace)try{const t=!await e.root.has(Ma);await e.root.write(Ma,ve.toFile()),t&&await this.render()}catch{}}progressChanged(){const e=this.progressWorkspace;e&&(window.clearTimeout(this.progressTimer),this.progressTimer=window.setTimeout(()=>void this.writeProgress(e),1e3))}async render(){const e=++this.rendering,t=this.workspace;if(t!==this.progressWorkspace&&this.syncProgress(t),this.element.querySelector("[data-files=new-folder]").hidden=!t,this.element.querySelector("[data-files=switch]").hidden=!t,!t){this.body.innerHTML=this.startHTML();return}const n=[],a=async(o,l,u)=>{const m=(await o.list()).sort(zv);for(const c of m){const d=l?`${l}/${c.name}`:c.name;if(c.kind==="directory"){const h=this.expanded.has(d);n.push(`
    - - -
    `),h&&await a(await o.dir(c.name),d,u+1)}else if(/\.(visustruct|java|json)$/i.test(c.name)){if(/\.java$/i.test(c.name)&&m.some(g=>g.name===c.name.replace(/\.java$/i,".visustruct")))continue;const h=/\.(visustruct|java)$/i.test(c.name);n.push(`
    - - ${h?``:""} -
    `)}}};try{await a(t.root,"",0)}catch(o){n.push(`

    ${te(o.message)}

    `)}if(e!==this.rendering)return;const s=t.kind==="folder"?`📁 ${te(t.root.name)}`:te(p("web_files_in_browser")),r=p("web_files_save_hint_fmt",Om());this.body.innerHTML=` -

    ${s}

    -
    ${n.join("")||`

    ${te(p("web_files_empty"))}

    `}
    -

    ${te(r)}

    - `}startHTML(){return this.pending?`

    ${te(p("web_files_reconnect_hint"))}

    - - `:Oa()?`

    ${te(p("web_files_connect_hint"))}

    - `:`

    ${te(p("web_files_browser_hint"))}

    - `}async onTreeClick(e){var r;const t=e.target,n=(r=t.closest("[data-start]"))==null?void 0:r.dataset.start;if(n==="pick")return this.connectFolder();if(n==="reconnect")return this.reconnect();if(n==="browser")return this.useBrowser();const a=t.closest(".files-row");if(!a||!this.workspace)return;const s=a.dataset.path;if(t.closest(".files-delete"))return this.remove(a);if(a.dataset.kind==="dir")return this.expanded.has(s)?this.expanded.delete(s):this.expanded.add(s),this.rememberExpanded(),this.render();if(a.dataset.kind==="program")return this.openProgram(s)}async locate(e){const t=e.split("/");let n=this.workspace.root;for(const a of t.slice(0,-1))n=await n.dir(a);return{dir:n,name:t.at(-1)}}async openProgram(e){try{const{dir:t,name:n}=await this.locate(e),a=n.replace(/\.(visustruct|java)$/i,""),s=await t.has(`${a}.visustruct`)?await t.read(`${a}.visustruct`):void 0,r=await t.has(`${a}.java`)?await t.read(`${a}.java`):void 0;if(s===void 0&&r===void 0)return;const o={dir:t,base:a,path:e.replace(/\.(visustruct|java)$/i,"")};this.host.open(s??"",o,r)}catch(t){this.host.message(t.message)}}async remove(e){const t=e.dataset.path,n=e.dataset.kind==="program",a=n?t.replace(/\.(visustruct|java)$/i,""):t;if(await pc({title:p("web_files_delete_confirm_fmt",a),ok:p("web_files_delete"),danger:!0})!==void 0){try{const{dir:s,name:r}=await this.locate(t);if(n){const o=r.replace(/\.(visustruct|java)$/i,"");for(const l of[".visustruct",".java"])await s.has(o+l)&&await s.remove(o+l)}else await s.remove(r)}catch(s){this.host.message(s.message)}await this.render()}}async newFolder(){if(!this.workspace)return;const e=Sv(await pc({title:p("web_files_folder_prompt"),input:"",ok:p("web_files_create")})??"");if(e){try{await this.workspace.root.dir(e,!0)}catch(t){this.host.message(t.message)}await this.render()}}async save(e,t,n,a){const s=this.workspace;if(!s)return;let r=a;if(!r){const o=Om(),l=await s.root.dir(o,!0),u=kv(Iv(e,p("web_files_default_name")),(await l.list()).map(m=>m.name));r={dir:l,base:u,path:`${o}/${u}`},this.expanded.add(o),this.rememberExpanded()}return await r.dir.write(`${r.base}.visustruct`,t),n!==void 0&&await r.dir.write(`${r.base}.java`,n),await this.render(),r}}let kl=[],$p=[];(()=>{let i="lc,34,7n,7,7b,19,,,,2,,2,,,20,b,1c,l,g,,2t,7,2,6,2,2,,4,z,,u,r,2j,b,1m,9,9,,o,4,,9,,3,,5,17,3,1n,9,16,o,,x,1i,3,,i,,7,a,2,t,3,1k,,,7,2,2,2,3,9,,a,2,q,,2,3,1k,,,5,4,2,2,3,3,,u,2,3,,b,3,1k,,,8,,3,,3,k,2,m,6,,3,1k,,,7,2,2,2,3,7,3,a,2,u,,1n,5,3,3,,4,9,,14,5,1j,,,7,,3,,4,7,2,b,2,t,3,1k,,,7,,3,,4,7,2,b,2,f,,c,4,1j,2,,7,,3,,4,9,,a,2,t,3,1y,,4,6,,,,8,i,2,1p,,,8,c,8,2q,,,a,b,7,21,2,r,,,,,,4,2,1d,k,,2,5,b,,10,9,,2u,b,,6,n,4,4,3,g,4,d,,,3,6,,f,,jj,3,qa,4,s,3,t,2,u,2,1s,w,9,,19,3,,,39,2,y,,3a,c,4,c,63,5,1l,a,,,,,2,o,2,,1c,1a,2,c,k,5,1b,h,12,9,c,3,u,d,1k,e,1c,k,48,3,,l,4,,6,,2,3,5i,1s,ek,,5f,x,2da,3,3x,,2o,w,fe,6,2x,2,n9w,4,,a,w,2,28,2,7k,,3,,4,,n,5,4,,2b,2,1e,i,q,i,d,,12,8,p,d,18,4,1b,e,10,,1v,e,c,,8,2,1a,,1f,,,3,2,2,5,2,,,15,5,5,2,6k,8,,2,fn4,,kh,g,g,g,a6,2,gt,,6a,,45,5,1ae,3,,2,5,4,14,3,4,,4l,2,fx,4,1t,5,8t,2,25,6,1y,b,1d,4,3e,3,1h,f,15,,2,2,a,4,19,b,7,,1p,3,10,e,g,2,18,,c,3,1c,e,8,4,,2,2k,c,6,,2,,4d,c,l,4,1j,2,,7,2,2,2,3,9,,a,2,2,7,3,5,1v,9,,,2,,,4,,5,,,e,2,2a,i,n,,29,k,6j,7,2,9,r,2,2a,h,2y,d,2t,3,2,a,74,f,6t,6,,2,2,4,,,,2,3x,7,2,7,3,,s,a,14,7,,4,8,,9,b,1a,g,5i,8,5j,8,,8,2a,m,,e,3e,6,3,,,2,,7,,,1u,5,,2,,5,9n,4,9,2,,,1c,7,3,5,n,,44l,,6,f,8ug,i,1xc,5,1n,7,t4,,,1j,7,4,29,,b,2,f57,2,3mp,1a,2,n,f2,5,3,6,8,8,2,7,u,4,44,3,1iz,1j,4,1e,8,,e,,m,5,,f,11s,7,,h,2,7,,2,,5,2s,,4g,7,af,,1p,4,e4,4,72,2,6r,,2,,7,2,5,,d6,7,31,7,240,5".split(",").map(e=>e?parseInt(e,36):1);for(let e=0,t=0;e>1;if(i=$p[n])e=n+1;else return!0;if(e==t)return!1}}function fc(i){return i>=127462&&i<=127487}const bc=8205;function KJ(i,e,t=!0,n=!0){return(t?Yp:XJ)(i,e,n)}function Yp(i,e,t){if(e==i.length)return e;e&&Kp(i.charCodeAt(e))&&Xp(i.charCodeAt(e-1))&&e--;let n=oo(i,e);for(e+=xc(n);e=0&&fc(oo(i,r));)s++,r-=2;if(s%2==0)break;e+=2}else break}return e}function XJ(i,e,t){for(;e>1;){let n=Yp(i,e-2,t);if(n=56320&&i<57344}function Xp(i){return i>=55296&&i<56320}function xc(i){return i<65536?1:2}class he{lineAt(e){if(e<0||e>this.length)throw new RangeError(`Invalid position ${e} in document of length ${this.length}`);return this.lineInner(e,!1,1,0)}line(e){if(e<1||e>this.lines)throw new RangeError(`Invalid line number ${e} in ${this.lines}-line document`);return this.lineInner(e,!0,1,0)}replace(e,t,n){[e,t]=G1(this,e,t);let a=[];return this.decompose(0,e,a,2),n.length&&n.decompose(0,n.length,a,3),this.decompose(t,this.length,a,1),rn.from(a,this.length-(t-e)+n.length)}append(e){return this.replace(this.length,this.length,e)}slice(e,t=this.length){[e,t]=G1(this,e,t);let n=[];return this.decompose(e,t,n,0),rn.from(n,t-e)}eq(e){if(e==this)return!0;if(e.length!=this.length||e.lines!=this.lines)return!1;let t=this.scanIdentical(e,1),n=this.length-this.scanIdentical(e,-1),a=new Ai(this),s=new Ai(e);for(let r=t,o=t;;){if(a.next(r),s.next(r),r=0,a.lineBreak!=s.lineBreak||a.done!=s.done||a.value!=s.value)return!1;if(o+=a.value.length,a.done||o>=n)return!0}}iter(e=1){return new Ai(this,e)}iterRange(e,t=this.length){return new ef(this,e,t)}iterLines(e,t){let n;if(e==null)n=this.iter();else{t==null&&(t=this.lines+1);let a=this.line(e).from;n=this.iterRange(a,Math.max(a,t==this.lines+1?this.length:t<=1?0:this.line(t-1).to))}return new tf(n)}toString(){return this.sliceString(0)}toJSON(){let e=[];return this.flatten(e),e}constructor(){}static of(e){if(e.length==0)throw new RangeError("A document must have at least one line");return e.length==1&&!e[0]?he.empty:e.length<=32?new Be(e):rn.from(Be.split(e,[]))}}class Be extends he{constructor(e,t=eG(e)){super(),this.text=e,this.length=t}get lines(){return this.text.length}get children(){return null}lineInner(e,t,n,a){for(let s=0;;s++){let r=this.text[s],o=a+r.length;if((t?n:o)>=e)return new tG(a,o,n,r);a=o+1,n++}}decompose(e,t,n,a){let s=e<=0&&t>=this.length?this:new Be(vc(this.text,e,t),Math.min(t,this.length)-Math.max(0,e));if(a&1){let r=n.pop(),o=xs(s.text,r.text.slice(),0,s.length);if(o.length<=32)n.push(new Be(o,r.length+s.length));else{let l=o.length>>1;n.push(new Be(o.slice(0,l)),new Be(o.slice(l)))}}else n.push(s)}replace(e,t,n){if(!(n instanceof Be))return super.replace(e,t,n);[e,t]=G1(this,e,t);let a=xs(this.text,xs(n.text,vc(this.text,0,e)),t),s=this.length+n.length-(t-e);return a.length<=32?new Be(a,s):rn.from(Be.split(a,[]),s)}sliceString(e,t=this.length,n=` -`){[e,t]=G1(this,e,t);let a="";for(let s=0,r=0;s<=t&&re&&r&&(a+=n),es&&(a+=o.slice(Math.max(0,e-s),t-s)),s=l+1}return a}flatten(e){for(let t of this.text)e.push(t)}scanIdentical(){return 0}static split(e,t){let n=[],a=-1;for(let s of e)n.push(s),a+=s.length+1,n.length==32&&(t.push(new Be(n,a)),n=[],a=-1);return a>-1&&t.push(new Be(n,a)),t}}class rn extends he{constructor(e,t){super(),this.children=e,this.length=t,this.lines=0;for(let n of e)this.lines+=n.lines}lineInner(e,t,n,a){for(let s=0;;s++){let r=this.children[s],o=a+r.length,l=n+r.lines-1;if((t?l:o)>=e)return r.lineInner(e,t,n,a);a=o+1,n=l+1}}decompose(e,t,n,a){for(let s=0,r=0;r<=t&&s=r){let u=a&((r<=e?1:0)|(l>=t?2:0));r>=e&&l<=t&&!u?n.push(o):o.decompose(e-r,t-r,n,u)}r=l+1}}replace(e,t,n){if([e,t]=G1(this,e,t),n.lines=s&&t<=o){let l=r.replace(e-s,t-s,n),u=this.lines-r.lines+l.lines;if(l.lines>4&&l.lines>u>>6){let m=this.children.slice();return m[a]=l,new rn(m,this.length-(t-e)+n.length)}return super.replace(s,o,l)}s=o+1}return super.replace(e,t,n)}sliceString(e,t=this.length,n=` -`){[e,t]=G1(this,e,t);let a="";for(let s=0,r=0;se&&s&&(a+=n),er&&(a+=o.sliceString(e-r,t-r,n)),r=l+1}return a}flatten(e){for(let t of this.children)t.flatten(e)}scanIdentical(e,t){if(!(e instanceof rn))return 0;let n=0,[a,s,r,o]=t>0?[0,0,this.children.length,e.children.length]:[this.children.length-1,e.children.length-1,-1,-1];for(;;a+=t,s+=t){if(a==r||s==o)return n;let l=this.children[a],u=e.children[s];if(l!=u)return n+l.scanIdentical(u,t);n+=l.length+1}}static from(e,t=e.reduce((n,a)=>n+a.length+1,-1)){let n=0;for(let h of e)n+=h.lines;if(n<32){let h=[];for(let g of e)g.flatten(h);return new Be(h,t)}let a=Math.max(32,n>>5),s=a<<1,r=a>>1,o=[],l=0,u=-1,m=[];function c(h){let g;if(h.lines>s&&h instanceof rn)for(let f of h.children)c(f);else h.lines>r&&(l>r||!l)?(d(),o.push(h)):h instanceof Be&&l&&(g=m[m.length-1])instanceof Be&&h.lines+g.lines<=32?(l+=h.lines,u+=h.length+1,m[m.length-1]=new Be(g.text.concat(h.text),g.length+1+h.length)):(l+h.lines>a&&d(),l+=h.lines,u+=h.length+1,m.push(h))}function d(){l!=0&&(o.push(m.length==1?m[0]:rn.from(m,u)),u=-1,l=m.length=0)}for(let h of e)c(h);return d(),o.length==1?o[0]:new rn(o,t)}}he.empty=new Be([""],0);function eG(i){let e=-1;for(let t of i)e+=t.length+1;return e}function xs(i,e,t=0,n=1e9){for(let a=0,s=0,r=!0;s=t&&(l>n&&(o=o.slice(0,n-a)),a0?1:(e instanceof Be?e.text.length:e.children.length)<<1]}nextInner(e,t){for(this.done=this.lineBreak=!1;;){let n=this.nodes.length-1,a=this.nodes[n],s=this.offsets[n],r=s>>1,o=a instanceof Be?a.text.length:a.children.length;if(r==(t>0?o:0)){if(n==0)return this.done=!0,this.value="",this;t>0&&this.offsets[n-1]++,this.nodes.pop(),this.offsets.pop()}else if((s&1)==(t>0?0:1)){if(this.offsets[n]+=t,e==0)return this.lineBreak=!0,this.value=` -`,this;e--}else if(a instanceof Be){let l=a.text[r+(t<0?-1:0)];if(this.offsets[n]+=t,l.length>Math.max(0,e))return this.value=e==0?l:t>0?l.slice(e):l.slice(0,l.length-e),this;e-=l.length}else{let l=a.children[r+(t<0?-1:0)];e>l.length?(e-=l.length,this.offsets[n]+=t):(t<0&&this.offsets[n]--,this.nodes.push(l),this.offsets.push(t>0?1:(l instanceof Be?l.text.length:l.children.length)<<1))}}}next(e=0){return e<0&&(this.nextInner(-e,-this.dir),e=this.value.length),this.nextInner(e,this.dir)}}class ef{constructor(e,t,n){this.value="",this.done=!1,this.cursor=new Ai(e,t>n?-1:1),this.pos=t>n?e.length:0,this.from=Math.min(t,n),this.to=Math.max(t,n)}nextInner(e,t){if(t<0?this.pos<=this.from:this.pos>=this.to)return this.value="",this.done=!0,this;e+=Math.max(0,t<0?this.pos-this.to:this.from-this.pos);let n=t<0?this.pos-this.from:this.to-this.pos;e>n&&(e=n),n-=e;let{value:a}=this.cursor.next(e);return this.pos+=(a.length+e)*t,this.value=a.length<=n?a:t<0?a.slice(a.length-n):a.slice(0,n),this.done=!this.value,this}next(e=0){return e<0?e=Math.max(e,this.from-this.pos):e>0&&(e=Math.min(e,this.to-this.pos)),this.nextInner(e,this.cursor.dir)}get lineBreak(){return this.cursor.lineBreak&&this.value!=""}}class tf{constructor(e){this.inner=e,this.afterBreak=!0,this.value="",this.done=!1}next(e=0){let{done:t,lineBreak:n,value:a}=this.inner.next(e);return t&&this.afterBreak?(this.value="",this.afterBreak=!1):t?(this.done=!0,this.value=""):n?this.afterBreak?this.value="":(this.afterBreak=!0,this.next()):(this.value=a,this.afterBreak=!1),this}get lineBreak(){return!1}}typeof Symbol<"u"&&(he.prototype[Symbol.iterator]=function(){return this.iter()},Ai.prototype[Symbol.iterator]=ef.prototype[Symbol.iterator]=tf.prototype[Symbol.iterator]=function(){return this});class tG{constructor(e,t,n,a){this.from=e,this.to=t,this.number=n,this.text=a}get length(){return this.to-this.from}}function G1(i,e,t){return e=Math.max(0,Math.min(i.length,e)),[e,Math.max(e,Math.min(i.length,t))]}function ut(i,e,t=!0,n=!0){return KJ(i,e,t,n)}function nG(i){return i>=56320&&i<57344}function iG(i){return i>=55296&&i<56320}function Pt(i,e){let t=i.charCodeAt(e);if(!iG(t)||e+1==i.length)return t;let n=i.charCodeAt(e+1);return nG(n)?(t-55296<<10)+(n-56320)+65536:t}function nf(i){return i<=65535?String.fromCharCode(i):(i-=65536,String.fromCharCode((i>>10)+55296,(i&1023)+56320))}function xn(i){return i<65536?1:2}const Sl=/\r\n?|\n/;var lt=(function(i){return i[i.Simple=0]="Simple",i[i.TrackDel=1]="TrackDel",i[i.TrackBefore=2]="TrackBefore",i[i.TrackAfter=3]="TrackAfter",i})(lt||(lt={}));class wn{constructor(e){this.sections=e}get length(){let e=0;for(let t=0;te)return s+(e-a);s+=o}else{if(n!=lt.Simple&&u>=e&&(n==lt.TrackDel&&ae||n==lt.TrackBefore&&ae))return null;if(u>e||u==e&&t<0&&!o)return e==a||t<0?s:s+l;s+=l}a=u}if(e>a)throw new RangeError(`Position ${e} is out of range for changeset of length ${a}`);return s}touchesRange(e,t=e){for(let n=0,a=0;n=0&&a<=t&&o>=e)return at?"cover":!0;a=o}return!1}toString(){let e="";for(let t=0;t=0?":"+a:"")}return e}toJSON(){return this.sections}static fromJSON(e){if(!Array.isArray(e)||e.length%2||e.some(t=>typeof t!="number"))throw new RangeError("Invalid JSON representation of ChangeDesc");return new wn(e)}static create(e){return new wn(e)}}class Le extends wn{constructor(e,t){super(e),this.inserted=t}apply(e){if(this.length!=e.length)throw new RangeError("Applying change set to a document with the wrong length");return Tl(this,(t,n,a,s,r)=>e=e.replace(a,a+(n-t),r),!1),e}mapDesc(e,t=!1){return _l(this,e,t,!0)}invert(e){let t=this.sections.slice(),n=[];for(let a=0,s=0;a=0){t[a]=o,t[a+1]=r;let l=a>>1;for(;n.length0&&Zn(n,t,s.text),s.forward(m),o+=m}let u=e[r++];for(;o>1].toJSON()))}return e}static of(e,t,n){let a=[],s=[],r=0,o=null;function l(m=!1){if(!m&&!a.length)return;rd||c<0||d>t)throw new RangeError(`Invalid change range ${c} to ${d} (in doc of length ${t})`);let g=h?typeof h=="string"?he.of(h.split(n||Sl)):h:he.empty,f=g.length;if(c==d&&f==0)return;cr&&rt(a,c-r,-1),rt(a,d-c,f),Zn(s,a,g),r=d}}return u(e),l(!o),o}static empty(e){return new Le(e?[e,-1]:[],[])}static fromJSON(e){if(!Array.isArray(e))throw new RangeError("Invalid JSON representation of ChangeSet");let t=[],n=[];for(let a=0;ao&&typeof r!="string"))throw new RangeError("Invalid JSON representation of ChangeSet");if(s.length==1)t.push(s[0],0);else{for(;n.length=0&&t<=0&&t==i[a+1]?i[a]+=e:a>=0&&e==0&&i[a]==0?i[a+1]+=t:n?(i[a]+=e,i[a+1]+=t):i.push(e,t)}function Zn(i,e,t){if(t.length==0)return;let n=e.length-2>>1;if(n>1])),!(t||r==i.sections.length||i.sections[r+1]<0);)o=i.sections[r++],l=i.sections[r++];e(a,u,s,m,c),a=u,s=m}}}function _l(i,e,t,n=!1){let a=[],s=n?[]:null,r=new qi(i),o=new qi(e);for(let l=-1;;){if(r.done&&o.len||o.done&&r.len)throw new Error("Mismatched change set lengths");if(r.ins==-1&&o.ins==-1){let u=Math.min(r.len,o.len);rt(a,u,-1),r.forward(u),o.forward(u)}else if(o.ins>=0&&(r.ins<0||l==r.i||r.off==0&&(o.len=0&&l=0){let u=0,m=r.len;for(;m;)if(o.ins==-1){let c=Math.min(m,o.len);u+=c,m-=c,o.forward(c)}else if(o.ins==0&&o.lenl||r.ins>=0&&r.len>l)&&(o||n.length>u),s.forward2(l),r.forward(l)}}}}class qi{constructor(e){this.set=e,this.i=0,this.next()}next(){let{sections:e}=this.set;this.i>1;return t>=e.length?he.empty:e[t]}textBit(e){let{inserted:t}=this.set,n=this.i-2>>1;return n>=t.length&&!e?he.empty:t[n].slice(this.off,e==null?void 0:this.off+e)}forward(e){e==this.len?this.next():(this.len-=e,this.off+=e)}forward2(e){this.ins==-1?this.forward(e):e==this.ins?this.next():(this.ins-=e,this.off+=e)}}class Pn{constructor(e,t,n,a){this.from=e,this.to=t,this.flags=n,this.goalColumn=a}get anchor(){return this.flags&32?this.to:this.from}get head(){return this.flags&32?this.from:this.to}get empty(){return this.from==this.to}get assoc(){return this.flags&8?-1:this.flags&16?1:0}get undirectional(){return(this.flags&64)>0}get bidiLevel(){let e=this.flags&7;return e==7?null:e}map(e,t=-1){let n,a;return this.empty?n=a=e.mapPos(this.from,t):(n=e.mapPos(this.from,1),a=e.mapPos(this.to,-1)),n==this.from&&a==this.to?this:new Pn(n,a,this.flags,this.goalColumn)}extend(e,t=e,n=0){if(e<=this.anchor&&t>=this.anchor)return O.range(e,t,void 0,void 0,n);let a=Math.abs(e-this.anchor)>Math.abs(t-this.anchor)?e:t;return O.range(this.anchor,a,void 0,void 0,n)}eq(e,t=!1){return this.anchor==e.anchor&&this.head==e.head&&this.goalColumn==e.goalColumn&&(!t||!this.empty||this.assoc==e.assoc)}toJSON(){return{anchor:this.anchor,head:this.head}}static fromJSON(e){if(!e||typeof e.anchor!="number"||typeof e.head!="number")throw new RangeError("Invalid JSON representation for SelectionRange");return O.range(e.anchor,e.head)}static create(e,t,n,a){return new Pn(e,t,n,a)}}class O{constructor(e,t){this.ranges=e,this.mainIndex=t}map(e,t=-1){return e.empty?this:O.create(this.ranges.map(n=>n.map(e,t)),this.mainIndex)}eq(e,t=!1){if(this.ranges.length!=e.ranges.length||this.mainIndex!=e.mainIndex)return!1;for(let n=0;ne.toJSON()),main:this.mainIndex}}static fromJSON(e){if(!e||!Array.isArray(e.ranges)||typeof e.main!="number"||e.main>=e.ranges.length)throw new RangeError("Invalid JSON representation for EditorSelection");return new O(e.ranges.map(t=>Pn.fromJSON(t)),e.main)}static single(e,t=e){return new O([O.range(e,t)],0)}static create(e,t=0){if(e.length==0)throw new RangeError("A selection needs at least one range");for(let n=0,a=0;aa.from-s.from),t=e.indexOf(n);for(let a=1;as.head?O.range(l,o):O.range(o,l))}}return new O(e,t)}}function sf(i,e){for(let t of i.ranges)if(t.to>e)throw new RangeError("Selection points outside of document")}let iu=0;class Q{constructor(e,t,n,a,s){this.combine=e,this.compareInput=t,this.compare=n,this.isStatic=a,this.id=iu++,this.default=e([]),this.extensions=typeof s=="function"?s(this):s}get reader(){return this}static define(e={}){return new Q(e.combine||(t=>t),e.compareInput||((t,n)=>t===n),e.compare||(e.combine?(t,n)=>t===n:au),!!e.static,e.enables)}of(e){return new vs([],this,0,e)}compute(e,t){if(this.isStatic)throw new Error("Can't compute a static facet");return new vs(e,this,1,t)}computeN(e,t){if(this.isStatic)throw new Error("Can't compute a static facet");return new vs(e,this,2,t)}from(e,t){return t||(t=n=>n),this.compute([e],n=>t(n.field(e)))}}function au(i,e){return i==e||i.length==e.length&&i.every((t,n)=>t===e[n])}class vs{constructor(e,t,n,a){this.dependencies=e,this.facet=t,this.type=n,this.value=a,this.id=iu++}dynamicSlot(e){var t;let n=this.value,a=this.facet.compareInput,s=this.id,r=e[s]>>1,o=this.type==2,l=!1,u=!1,m=[];for(let c of this.dependencies)c=="doc"?l=!0:c=="selection"?u=!0:(((t=e[c.id])!==null&&t!==void 0?t:1)&1)==0&&m.push(e[c.id]);return{create(c){return c.values[r]=n(c),1},update(c,d){if(l&&d.docChanged||u&&(d.docChanged||d.selection)||Al(c,m)){let h=n(c);if(o?!wc(h,c.values[r],a):!a(h,c.values[r]))return c.values[r]=h,1}return 0},reconfigure:(c,d)=>{let h,g=d.config.address[s];if(g!=null){let f=Qs(d,g);if(this.dependencies.every(b=>b instanceof Q?d.facet(b)===c.facet(b):b instanceof Nt?d.field(b,!1)==c.field(b,!1):!0)||(o?wc(h=n(c),f,a):a(h=n(c),f)))return c.values[r]=f,0}else h=n(c);return c.values[r]=h,1}}}get extension(){return this}}function wc(i,e,t){if(i.length!=e.length)return!1;for(let n=0;ni[l.id]),a=t.map(l=>l.type),s=n.filter(l=>!(l&1)),r=i[e.id]>>1;function o(l){let u=[];for(let m=0;mn===a),e);return e.provide&&(t.provides=e.provide(t)),t}create(e){let t=e.facet(Ea).find(n=>n.field==this);return((t==null?void 0:t.create)||this.createF)(e)}slot(e){let t=e[this.id]>>1;return{create:n=>(n.values[t]=this.create(n),1),update:(n,a)=>{let s=n.values[t],r=this.updateF(s,a);return this.compareF(s,r)?0:(n.values[t]=r,1)},reconfigure:(n,a)=>{let s=n.facet(Ea),r=a.facet(Ea),o;return(o=s.find(l=>l.field==this))&&o!=r.find(l=>l.field==this)?(n.values[t]=o.create(n),1):a.config.address[this.id]!=null?(n.values[t]=a.field(this),0):(n.values[t]=this.create(n),1)}}}init(e){return[this,Ea.of({field:this,create:e})]}get extension(){return this}}const e1={lowest:4,low:3,default:2,high:1,highest:0};function li(i){return e=>new rf(e,i)}const f1={highest:li(e1.highest),high:li(e1.high),default:li(e1.default),low:li(e1.low),lowest:li(e1.lowest)};class rf{constructor(e,t){this.inner=e,this.prec=t}get extension(){return this}}class ma{of(e){return new jl(this,e)}reconfigure(e){return ma.reconfigure.of({compartment:this,extension:e})}get(e){return e.config.compartments.get(this)}}class jl{constructor(e,t){this.compartment=e,this.inner=t}get extension(){return this}}class Us{constructor(e,t,n,a,s,r){for(this.base=e,this.compartments=t,this.dynamicSlots=n,this.address=a,this.staticValues=s,this.facets=r,this.statusTemplate=[];this.statusTemplate.length>1]}static resolve(e,t,n){let a=[],s=Object.create(null),r=new Map;for(let d of sG(e,t,r))d instanceof Nt?a.push(d):(s[d.facet.id]||(s[d.facet.id]=[])).push(d);let o=Object.create(null),l=[],u=[];for(let d of a)o[d.id]=u.length<<1,u.push(h=>d.slot(h));let m=n==null?void 0:n.config.facets;for(let d in s){let h=s[d],g=h[0].facet,f=m&&m[d]||[];if(h.every(b=>b.type==0))if(o[g.id]=l.length<<1|1,au(f,h))l.push(n.facet(g));else{let b=g.combine(h.map(x=>x.value));l.push(n&&g.compare(b,n.facet(g))?n.facet(g):b)}else{for(let b of h)b.type==0?(o[b.id]=l.length<<1|1,l.push(b.value)):(o[b.id]=u.length<<1,u.push(x=>b.dynamicSlot(x)));o[g.id]=u.length<<1,u.push(b=>aG(b,g,h))}}let c=u.map(d=>d(o));return new Us(e,r,c,o,l,s)}}function sG(i,e,t){let n=[[],[],[],[],[]],a=new Map;function s(r,o){let l=a.get(r);if(l!=null){if(l<=o)return;let u=n[l].indexOf(r);u>-1&&n[l].splice(u,1),r instanceof jl&&t.delete(r.compartment)}if(a.set(r,o),Array.isArray(r))for(let u of r)s(u,o);else if(r instanceof jl){if(t.has(r.compartment))throw new RangeError("Duplicate use of compartment in extensions");let u=e.get(r.compartment)||r.inner;t.set(r.compartment,u),s(u,o)}else if(r instanceof rf)s(r.inner,r.prec);else if(r instanceof Nt)n[o].push(r),r.provides&&s(r.provides,o);else if(r instanceof vs)n[o].push(r),r.facet.extensions&&s(r.facet.extensions,e1.default);else{let u=r.extension;if(!u)throw new Error(`Unrecognized extension value in extension set (${r}).`);if(u==r)throw new Error(`Unrecognized extension value in extension set (${r}). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks.`);s(u,o)}}return s(i,e1.default),n.reduce((r,o)=>r.concat(o))}function ji(i,e){if(e&1)return 2;let t=e>>1,n=i.status[t];if(n==4)throw new Error("Cyclic dependency between fields and/or facets");if(n&2)return n;i.status[t]=4;let a=i.computeSlot(i,i.config.dynamicSlots[t]);return i.status[t]=2|a}function Qs(i,e){return e&1?i.config.staticValues[e>>1]:i.values[e>>1]}const of=Q.define(),Cl=Q.define({combine:i=>i.some(e=>e),static:!0}),lf=Q.define({combine:i=>i.length?i[0]:void 0,static:!0}),uf=Q.define(),mf=Q.define(),cf=Q.define(),df=Q.define({combine:i=>i.length?i[0]:!1});class Vn{constructor(e,t){this.type=e,this.value=t}static define(){return new rG}}class rG{of(e){return new Vn(this,e)}}class oG{constructor(e){this.map=e}of(e){return new pe(this,e)}}class pe{constructor(e,t){this.type=e,this.value=t}map(e){let t=this.type.map(this.value,e);return t===void 0?void 0:t==this.value?this:new pe(this.type,t)}is(e){return this.type==e}static define(e={}){return new oG(e.map||(t=>t))}static mapEffects(e,t){if(!e.length)return e;let n=[];for(let a of e){let s=a.map(t);s&&n.push(s)}return n}}pe.reconfigure=pe.define();pe.appendConfig=pe.define();class Ke{constructor(e,t,n,a,s,r){this.startState=e,this.changes=t,this.selection=n,this.effects=a,this.annotations=s,this.scrollIntoView=r,this._doc=null,this._state=null,n&&sf(n,t.newLength),s.some(o=>o.type==Ke.time)||(this.annotations=s.concat(Ke.time.of(Date.now())))}static create(e,t,n,a,s,r){return new Ke(e,t,n,a,s,r)}get newDoc(){return this._doc||(this._doc=this.changes.apply(this.startState.doc))}get newSelection(){return this.selection||this.startState.selection.map(this.changes)}get state(){return this._state||this.startState.applyTransaction(this),this._state}annotation(e){for(let t of this.annotations)if(t.type==e)return t.value}get docChanged(){return!this.changes.empty}get reconfigured(){return this.startState.config!=this.state.config}isUserEvent(e){let t=this.annotation(Ke.userEvent);return!!(t&&(t==e||t.length>e.length&&t.slice(0,e.length)==e&&t[e.length]=="."))}}Ke.time=Vn.define();Ke.userEvent=Vn.define();Ke.addToHistory=Vn.define();Ke.remote=Vn.define();function lG(i,e){let t=[];for(let n=0,a=0;;){let s,r;if(n=i[n]))s=i[n++],r=i[n++];else if(a=0;a--){let s=n[a](i);s instanceof Ke?i=s:Array.isArray(s)&&s.length==1&&s[0]instanceof Ke?i=s[0]:i=gf(e,W1(s),!1)}return i}function mG(i){let e=i.startState,t=e.facet(cf),n=i;for(let a=t.length-1;a>=0;a--){let s=t[a](i);s&&Object.keys(s).length&&(n=hf(n,Dl(e,s,i.changes.newLength),!0))}return n==i?i:Ke.create(e,i.changes,i.selection,n.effects,n.annotations,n.scrollIntoView)}const cG=[];function W1(i){return i==null?cG:Array.isArray(i)?i:[i]}var Mt=(function(i){return i[i.Word=0]="Word",i[i.Space=1]="Space",i[i.Other=2]="Other",i})(Mt||(Mt={}));const dG=/[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/;let Bl;try{Bl=new RegExp("[\\p{Alphabetic}\\p{Number}_]","u")}catch{}function hG(i){if(Bl)return Bl.test(i);for(let e=0;e"€"&&(t.toUpperCase()!=t.toLowerCase()||dG.test(t)))return!0}return!1}function gG(i){return e=>{if(!/\S/.test(e))return Mt.Space;if(hG(e))return Mt.Word;for(let t=0;t-1)return Mt.Word;return Mt.Other}}class ce{constructor(e,t,n,a,s,r){this.config=e,this.doc=t,this.selection=n,this.values=a,this.status=e.statusTemplate.slice(),this.computeSlot=s,r&&(r._state=this);for(let o=0;oa.set(u,l)),t=null),a.set(o.value.compartment,o.value.extension)):o.is(pe.reconfigure)?(t=null,n=o.value):o.is(pe.appendConfig)&&(t=null,n=W1(n).concat(o.value));let s;t?s=e.startState.values.slice():(t=Us.resolve(n,a,this),s=new ce(t,this.doc,this.selection,t.dynamicSlots.map(()=>null),(l,u)=>u.reconfigure(l,this),null).values);let r=e.startState.facet(Cl)?e.newSelection:e.newSelection.asSingle();new ce(t,e.newDoc,r,s,(o,l)=>l.update(o,e),e)}replaceSelection(e){return typeof e=="string"&&(e=this.toText(e)),this.changeByRange(t=>({changes:{from:t.from,to:t.to,insert:e},range:O.cursor(t.from+e.length,-1)}))}changeByRange(e){let t=this.selection,n=e(t.ranges[0]),a=this.changes(n.changes),s=[n.range],r=W1(n.effects);for(let o=1;or.spec.fromJSON(o,l)))}}return ce.create({doc:e.doc,selection:O.fromJSON(e.selection),extensions:t.extensions?a.concat([t.extensions]):a})}static create(e={}){let t=Us.resolve(e.extensions||[],new Map),n=e.doc instanceof he?e.doc:he.of((e.doc||"").split(t.staticFacet(ce.lineSeparator)||Sl)),a=e.selection?e.selection instanceof O?e.selection:O.single(e.selection.anchor,e.selection.head):O.single(0);return sf(a,n.length),t.staticFacet(Cl)||(a=a.asSingle()),new ce(t,n,a,t.dynamicSlots.map(()=>null),(s,r)=>r.create(s),null)}get tabSize(){return this.facet(ce.tabSize)}get lineBreak(){return this.facet(ce.lineSeparator)||` -`}get readOnly(){return this.facet(df)}phrase(e,...t){for(let n of this.facet(ce.phrases))if(Object.prototype.hasOwnProperty.call(n,e)){e=n[e];break}return t.length&&(e=e.replace(/\$(\$|\d*)/g,(n,a)=>{if(a=="$")return"$";let s=+(a||1);return!s||s>t.length?n:t[s-1]})),e}languageDataAt(e,t,n=-1){let a=[];for(let s of this.facet(of))for(let r of s(this,t,n))Object.prototype.hasOwnProperty.call(r,e)&&a.push(r[e]);return a}charCategorizer(e){let t=this.languageDataAt("wordChars",e);return gG(t.length?t[0]:"")}wordAt(e){let{text:t,from:n,length:a}=this.doc.lineAt(e),s=this.charCategorizer(e),r=e-n,o=e-n;for(;r>0;){let l=ut(t,r,!1);if(s(t.slice(l,r))!=Mt.Word)break;r=l}for(;oi.length?i[0]:4});ce.lineSeparator=lf;ce.readOnly=df;ce.phrases=Q.define({compare(i,e){let t=Object.keys(i),n=Object.keys(e);return t.length==n.length&&t.every(a=>i[a]==e[a])}});ce.languageData=of;ce.changeFilter=uf;ce.transactionFilter=mf;ce.transactionExtender=cf;ma.reconfigure=pe.define();function su(i,e,t={}){let n={};for(let a of i)for(let s of Object.keys(a)){let r=a[s],o=n[s];if(o===void 0)n[s]=r;else if(!(o===r||r===void 0))if(Object.hasOwnProperty.call(t,s))n[s]=t[s](o,r);else throw new Error("Config merge conflict for field "+s)}for(let a in e)n[a]===void 0&&(n[a]=e[a]);return n}class Fn{eq(e){return this==e}range(e,t=e){return Pl.create(e,t,this)}}Fn.prototype.startSide=Fn.prototype.endSide=0;Fn.prototype.point=!1;Fn.prototype.mapMode=lt.TrackDel;function ru(i,e){return i==e||i.constructor==e.constructor&&i.eq(e)}let Pl=class pf{constructor(e,t,n){this.from=e,this.to=t,this.value=n}static create(e,t,n){return new pf(e,t,n)}};function Ol(i,e){return i.from-e.from||i.value.startSide-e.value.startSide}class ou{constructor(e,t,n,a){this.from=e,this.to=t,this.value=n,this.maxPoint=a}get length(){return j1(this.to)}findIndex(e,t,n,a=0){let s=n?this.to:this.from;for(let r=a,o=s.length;;){if(r==o)return r;let l=r+o>>1,u=s[l]-e||(n?this.value[l].endSide:this.value[l].startSide)-t;if(l==r)return u>=0?r:o;u>=0?o=l:r=l+1}}between(e,t,n,a){for(let s=this.findIndex(t,-1e9,!0),r=this.findIndex(n,1e9,!1,s);sb||f==b&&d.startSide>0&&d.endSide<=0)continue;if(!((b-f||d.endSide-d.startSide)<0))if(u<0&&(u=f),d.point&&(m=Math.max(m,b-f)),(f-n||d.startSide-a)>=0)r.push(d),o.push(f-u),l.push(b-u),n=b,a=d.endSide;else{if(f==b)for(let x=r.length;x>0;x--){if((f-(l[x-1]+u)||d.startSide-r[x-1].endSide)>=0){r.splice(x,0,d),o.splice(x,0,f-u),l.splice(x,0,b-u);continue e}if((f-(o[x-1]+u)||d.endSide-r[x-1].startSide)>0)break}s(f,b,d)}}return{mapped:r.length?new ou(o,l,r,m):null,pos:u}}}class oe{constructor(e,t,n,a){this.chunkPos=e,this.chunk=t,this.nextLayer=n,this.maxPoint=a}static create(e,t,n,a){return new oe(e,t,n,a)}get length(){let e=this.chunk.length-1;return e<0?0:Math.max(this.chunkEnd(e),this.nextLayer.length)}get size(){if(this.isEmpty)return 0;let e=this.nextLayer.size;for(let t of this.chunk)e+=t.value.length;return e}chunkEnd(e){return this.chunkPos[e]+this.chunk[e].length}update(e){let{add:t=[],sort:n=!1,filterFrom:a=0,filterTo:s=this.length}=e,r=e.filter;if(t.length==0&&!r)return this;if(n&&(t=t.slice().sort(Ol)),this.isEmpty)return t.length?oe.of(t):this;let o=new ff(this,null,-1).goto(0),l=0,u=[],m=new c1;for(;o.value||l=0){let c=t[l++];m.addInner(c.from,c.to,c.value,!1)||u.push(c)}else o.rangeIndex==1&&o.chunkIndexthis.chunkEnd(o.chunkIndex)||so.to||s{s||(s=new c1),s.addRange(l,u,m,!1)};for(let l=0;l=s&&e<=s+r.length&&r.between(s,e-s,t-s,n)===!1)return}this.nextLayer.between(e,t,n)}}iter(e=0){return Ui.from([this]).goto(e)}get isEmpty(){return this.nextLayer==this}static iter(e,t=0){return Ui.from(e).goto(t)}static compare(e,t,n,a,s=-1){let r=e.filter(c=>c.maxPoint>0||!c.isEmpty&&c.maxPoint>=s),o=t.filter(c=>c.maxPoint>0||!c.isEmpty&&c.maxPoint>=s),l=yc(r,o,n),u=new ui(r,l,s),m=new ui(o,l,s);n.iterGaps((c,d,h)=>zc(u,c,m,d,h,a)),n.empty&&n.length==0&&zc(u,0,m,0,0,a)}static eq(e,t,n=0,a){a==null&&(a=999999999);let s=e.filter(m=>!m.isEmpty&&t.indexOf(m)<0),r=t.filter(m=>!m.isEmpty&&e.indexOf(m)<0);if(s.length!=r.length)return!1;if(!s.length)return!0;let o=yc(s,r),l=new ui(s,o,0).goto(n),u=new ui(r,o,0).goto(n);for(;;){if(l.to!=u.to||!Ml(l.active,u.active)||l.point&&(!u.point||!ru(l.point,u.point)))return!1;if(l.to>a)return!0;l.next(),u.next()}}static spans(e,t,n,a,s=-1){let r=new ui(e,null,s).goto(t),o=t,l=r.openStart;for(;;){let u=Math.min(r.to,n);if(r.point){let m=r.activeForPoint(r.to),c=r.pointFromo&&(a.span(o,u,r.active,l),l=r.openEnd(u));if(r.to>n)return l+(r.point&&r.to>n?1:0);o=r.to,r.next()}}static of(e,t=!1){let n=new c1;for(let a of e instanceof Pl?[e]:t?pG(e):e)n.add(a.from,a.to,a.value);return n.finish()}static join(e){if(!e.length)return oe.empty;let t=j1(e);for(let n=e.length-2;n>=0;n--)for(let a=e[n];a!=oe.empty;a=a.nextLayer)t=new oe(a.chunkPos,a.chunk,t,Math.max(a.maxPoint,t.maxPoint));return t}}oe.empty=new oe([],[],null,-1);function j1(i){return i[i.length-1]}function pG(i){if(i.length>1)for(let e=i[0],t=1;t0)return i.slice().sort(Ol);e=n}return i}oe.empty.nextLayer=oe.empty;class c1{finishChunk(e){this.chunks.push(new ou(this.from,this.to,this.value,this.maxPoint)),this.chunkPos.push(this.chunkStart),this.chunkStart=-1,this.setMaxPoint=Math.max(this.setMaxPoint,this.maxPoint),this.maxPoint=-1,e&&(this.from=[],this.to=[],this.value=[])}constructor(){this.chunks=[],this.chunkPos=[],this.chunkStart=-1,this.last=null,this.lastFrom=-1e9,this.lastTo=-1e9,this.from=[],this.to=[],this.value=[],this.maxPoint=-1,this.setMaxPoint=-1,this.nextLayer=null}add(e,t,n){this.addRange(e,t,n,!0)}addRange(e,t,n,a){this.addInner(e,t,n,a)||(this.nextLayer||(this.nextLayer=new c1)).addRange(e,t,n,a)}addInner(e,t,n,a){let s=e-this.lastTo||n.startSide-this.last.endSide;if(a&&s<=0&&(e-this.lastFrom||n.startSide-this.last.startSide)<0)throw new Error("Ranges must be added sorted by `from` position and `startSide`");return s<0?!1:(this.from.length==250&&this.finishChunk(!0),this.chunkStart<0&&(this.chunkStart=e),this.from.push(e-this.chunkStart),this.to.push(t-this.chunkStart),this.last=n,this.lastFrom=e,this.lastTo=t,this.value.push(n),n.point&&(this.maxPoint=Math.max(this.maxPoint,t-e)),!0)}addChunk(e,t){if((e-this.lastTo||t.value[0].startSide-this.last.endSide)<0)return!1;this.from.length&&this.finishChunk(!0),this.setMaxPoint=Math.max(this.setMaxPoint,t.maxPoint),this.chunks.push(t),this.chunkPos.push(e);let n=t.value.length-1;return this.last=t.value[n],this.lastFrom=t.from[n]+e,this.lastTo=t.to[n]+e,!0}finish(){return this.finishInner(oe.empty)}finishInner(e){if(this.from.length&&this.finishChunk(!1),this.chunks.length==0)return e;let t=oe.create(this.chunkPos,this.chunks,this.nextLayer?this.nextLayer.finishInner(e):e,this.setMaxPoint);return this.from=null,t}}function yc(i,e,t){let n=new Map;for(let s of i)for(let r=0;r=this.minPoint)break}}setRangeIndex(e){if(e==this.layer.chunk[this.chunkIndex].value.length){if(this.chunkIndex++,this.skip)for(;this.chunkIndex=n&&a.push(new ff(r,t,n,s));return a.length==1?a[0]:new Ui(a)}get startSide(){return this.value?this.value.startSide:0}goto(e,t=-1e9){for(let n of this.heap)n.goto(e,t);for(let n=this.heap.length>>1;n>=0;n--)lo(this.heap,n);return this.next(),this}forward(e,t){for(let n of this.heap)n.forward(e,t);for(let n=this.heap.length>>1;n>=0;n--)lo(this.heap,n);(this.to-e||this.value.endSide-t)<0&&this.next()}next(){if(this.heap.length==0)this.from=this.to=1e9,this.value=null,this.rank=-1;else{let e=this.heap[0];this.from=e.from,this.to=e.to,this.value=e.value,this.rank=e.rank,e.value&&e.next(),lo(this.heap,0)}}}function lo(i,e){for(let t=i[e];;){let n=(e<<1)+1;if(n>=i.length)break;let a=i[n];if(n+1=0&&(a=i[n+1],n++),t.compare(a)<0)break;i[n]=t,i[e]=a,e=n}}class ui{constructor(e,t,n){this.minPoint=n,this.active=[],this.activeTo=[],this.activeRank=[],this.minActive=-1,this.point=null,this.pointFrom=0,this.pointRank=0,this.to=-1e9,this.endSide=0,this.openStart=-1,this.cursor=Ui.from(e,t,n)}goto(e,t=-1e9){return this.cursor.goto(e,t),this.active.length=this.activeTo.length=this.activeRank.length=0,this.minActive=-1,this.to=e,this.endSide=t,this.openStart=-1,this.next(),this}forward(e,t){for(;this.minActive>-1&&(this.activeTo[this.minActive]-e||this.active[this.minActive].endSide-t)<0;)this.removeActive(this.minActive);this.cursor.forward(e,t)}removeActive(e){Na(this.active,e),Na(this.activeTo,e),Na(this.activeRank,e),this.minActive=Ic(this.active,this.activeTo)}addActive(e){let t=0,{value:n,to:a,rank:s}=this.cursor;for(;t0;)t++;Fa(this.active,t,n),Fa(this.activeTo,t,a),Fa(this.activeRank,t,s),e&&Fa(e,t,this.cursor.from),this.minActive=Ic(this.active,this.activeTo)}next(){let e=this.to,t=this.point;this.point=null;let n=this.openStart<0?[]:null;for(;;){let a=this.minActive;if(a>-1&&(this.activeTo[a]-this.cursor.from||this.active[a].endSide-this.cursor.startSide)<0){if(this.activeTo[a]>e){this.to=this.activeTo[a],this.endSide=this.active[a].endSide;break}this.removeActive(a),n&&Na(n,a)}else if(this.cursor.value)if(this.cursor.from>e){this.to=this.cursor.from,this.endSide=this.cursor.startSide;break}else{let s=this.cursor.value;if(!s.point)this.addActive(n),this.cursor.next();else if(t&&this.cursor.to==this.to&&this.cursor.from=0&&n[a]=0&&!(this.activeRank[n]e||this.activeTo[n]==e&&this.active[n].endSide>=this.point.endSide)&&t.push(this.active[n]);return t.reverse()}openEnd(e){let t=0;for(let n=this.activeTo.length-1;n>=0&&this.activeTo[n]>e;n--)t++;return t}}function zc(i,e,t,n,a,s){i.goto(e),t.goto(n);let r=n+a,o=n,l=n-e,u=!!s.boundChange;for(let m=!1;;){let c=i.to+l-t.to,d=c||i.endSide-t.endSide,h=d<0?i.to+l:t.to,g=Math.min(h,r);if(i.point||t.point?(i.point&&t.point&&ru(i.point,t.point)&&Ml(i.activeForPoint(i.to),t.activeForPoint(t.to))||s.comparePoint(o,g,i.point,t.point),m=!1):(m&&(s.boundChange(o),m=!1),g>o&&!Ml(i.active,t.active)&&s.compareRange(o,g,i.active,t.active),u&&gr)break;o=h,d<=0&&i.next(),d>=0&&t.next()}}function Ml(i,e){if(i.length!=e.length)return!1;for(let t=0;t=e;n--)i[n+1]=i[n];i[e]=t}function Ic(i,e){let t=-1,n=1e9;for(let a=0;a=e)return a;if(a==i.length)break;s+=i.charCodeAt(a)==9?t-s%t:1,a=ut(i,a)}return i.length}const Zl="ͼ",kc=typeof Symbol>"u"?"__"+Zl:Symbol.for(Zl),Ll=typeof Symbol>"u"?"__styleSet"+Math.floor(Math.random()*1e8):Symbol("styleSet"),Sc=typeof globalThis<"u"?globalThis:typeof window<"u"?window:{};class Rn{constructor(e,t){this.rules=[];let{finish:n}=t||{};function a(r){return/^@/.test(r)?[r]:r.split(/,\s*/)}function s(r,o,l,u){let m=[],c=/^@(\w+)\b/.exec(r[0]),d=c&&c[1]=="keyframes";if(c&&o==null)return l.push(r[0]+";");for(let h in o){let g=o[h];if(/&/.test(h))s(h.split(/,\s*/).map(f=>r.map(b=>f.replace(/&/,b))).reduce((f,b)=>f.concat(b)),g,l);else if(g&&typeof g=="object"){if(!c)throw new RangeError("The value of a property ("+h+") should be a primitive value.");s(a(h),g,m,d)}else g!=null&&m.push(h.replace(/_.*/,"").replace(/[A-Z]/g,f=>"-"+f.toLowerCase())+": "+g+";")}(m.length||d)&&l.push((n&&!c&&!u?r.map(n):r).join(", ")+" {"+m.join(" ")+"}")}for(let r in e)s(a(r),e[r],this.rules)}getRules(){return this.rules.join(` -`)}static newName(){let e=Sc[kc]||1;return Sc[kc]=e+1,Zl+e.toString(36)}static mount(e,t,n){let a=e[Ll],s=n&&n.nonce;a?s&&a.setNonce(s):a=new bG(e,s),a.mount(Array.isArray(t)?t:[t],e)}}let Tc=new Map;class bG{constructor(e,t){let n=e.ownerDocument||e,a=n.defaultView;if(!e.head&&e.adoptedStyleSheets&&a.CSSStyleSheet){let s=Tc.get(n);if(s)return e[Ll]=s;this.sheet=new a.CSSStyleSheet,Tc.set(n,this)}else this.styleTag=n.createElement("style"),t&&this.styleTag.setAttribute("nonce",t);this.modules=[],e[Ll]=this}mount(e,t){let n=this.sheet,a=0,s=0,r=!1;for(let o=0;o-1&&(this.modules.splice(u,1),r=!0,s--,u=-1),u==-1){if(this.modules.splice(s++,0,l),r=!0,n)for(let m=0;m",191:"?",192:"~",219:"{",220:"|",221:"}",222:'"'},xG=typeof navigator<"u"&&/Mac/.test(navigator.platform),vG=typeof navigator<"u"&&/MSIE \d|Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(navigator.userAgent);for(var Ye=0;Ye<10;Ye++)Jn[48+Ye]=Jn[96+Ye]=String(Ye);for(var Ye=1;Ye<=24;Ye++)Jn[Ye+111]="F"+Ye;for(var Ye=65;Ye<=90;Ye++)Jn[Ye]=String.fromCharCode(Ye+32),Qi[Ye]=String.fromCharCode(Ye);for(var uo in Jn)Qi.hasOwnProperty(uo)||(Qi[uo]=Jn[uo]);function wG(i){var e=xG&&i.metaKey&&i.shiftKey&&!i.ctrlKey&&!i.altKey||vG&&i.shiftKey&&i.key&&i.key.length==1||i.key=="Unidentified",t=!e&&i.key||(i.shiftKey?Qi:Jn)[i.keyCode]||i.key||"Unidentified";return t=="Esc"&&(t="Escape"),t=="Del"&&(t="Delete"),t=="Left"&&(t="ArrowLeft"),t=="Up"&&(t="ArrowUp"),t=="Right"&&(t="ArrowRight"),t=="Down"&&(t="ArrowDown"),t}let dt=typeof navigator<"u"?navigator:{userAgent:"",vendor:"",platform:""},Wl=typeof document<"u"?document:{documentElement:{style:{}}};const El=/Edge\/(\d+)/.exec(dt.userAgent),bf=/MSIE \d/.test(dt.userAgent),Nl=/Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(dt.userAgent),Br=!!(bf||Nl||El),_c=!Br&&/gecko\/(\d+)/i.test(dt.userAgent),mo=!Br&&/Chrome\/(\d+)/.exec(dt.userAgent),Ac="webkitFontSmoothing"in Wl.documentElement.style,Fl=!Br&&/Apple Computer/.test(dt.vendor),jc=Fl&&(/Mobile\/\w+/.test(dt.userAgent)||dt.maxTouchPoints>2);var V={mac:jc||/Mac/.test(dt.platform),windows:/Win/.test(dt.platform),linux:/Linux|X11/.test(dt.platform),ie:Br,ie_version:bf?Wl.documentMode||6:Nl?+Nl[1]:El?+El[1]:0,gecko:_c,gecko_version:_c?+(/Firefox\/(\d+)/.exec(dt.userAgent)||[0,0])[1]:0,chrome:!!mo,chrome_version:mo?+mo[1]:0,ios:jc,android:/Android\b/.test(dt.userAgent),webkit:Ac,webkit_version:Ac?+(/\bAppleWebKit\/(\d+)/.exec(dt.userAgent)||[0,0])[1]:0,safari:Fl,safari_version:Fl?+(/\bVersion\/(\d+(\.\d+)?)/.exec(dt.userAgent)||[0,0])[1]:0,tabSize:Wl.documentElement.style.tabSize!=null?"tab-size":"-moz-tab-size"};function lu(i,e){for(let t in i)t=="class"&&e.class?e.class+=" "+i.class:t=="style"&&e.style?e.style+=";"+i.style:e[t]=i[t];return e}const $s=Object.create(null);function uu(i,e,t){if(i==e)return!0;i||(i=$s),e||(e=$s);let n=Object.keys(i),a=Object.keys(e);if(n.length-0!=a.length-0)return!1;for(let s of n)if(s!=t&&(a.indexOf(s)==-1||i[s]!==e[s]))return!1;return!0}function yG(i,e){for(let t=i.attributes.length-1;t>=0;t--){let n=i.attributes[t].name;e[n]==null&&i.removeAttribute(n)}for(let t in e){let n=e[t];t=="style"?i.style.cssText=n:i.getAttribute(t)!=n&&i.setAttribute(t,n)}}function Cc(i,e,t){let n=!1;if(e)for(let a in e)t&&a in t||(n=!0,a=="style"?i.style.cssText="":i.removeAttribute(a));if(t)for(let a in t)e&&e[a]==t[a]||(n=!0,a=="style"?i.style.cssText=t[a]:i.setAttribute(a,t[a]));return n}function zG(i){let e=Object.create(null);for(let t=0;t0?3e8:-4e8:t>0?1e8:-1e8,new g1(e,t,t,n,e.widget||null,!1)}static replace(e){let t=!!e.block,n,a;if(e.isBlockGap)n=-5e8,a=4e8;else{let{start:s,end:r}=xf(e,t);n=(s?t?-3e8:-1:5e8)-1,a=(r?t?2e8:1:-6e8)+1}return new g1(e,n,a,t,e.widget||null,!0)}static line(e){return new ha(e)}static set(e,t=!1){return oe.of(e,t)}hasHeight(){return this.widget?this.widget.estimatedHeight>-1:!1}}fe.none=oe.empty;class da extends fe{constructor(e){let{start:t,end:n}=xf(e);super(t?-1:5e8,n?1:-6e8,null,e),this.tagName=e.tagName||"span",this.attrs=e.class&&e.attributes?lu(e.attributes,{class:e.class}):e.class?{class:e.class}:e.attributes||$s}eq(e){return this==e||e instanceof da&&this.tagName==e.tagName&&uu(this.attrs,e.attrs)}range(e,t=e){if(e>=t)throw new RangeError("Mark decorations may not be empty");return super.range(e,t)}}da.prototype.point=!1;class ha extends fe{constructor(e){super(-2e8,-2e8,null,e)}eq(e){return e instanceof ha&&this.spec.class==e.spec.class&&uu(this.spec.attributes,e.spec.attributes)}range(e,t=e){if(t!=e)throw new RangeError("Line decoration ranges must be zero-length");return super.range(e,t)}}ha.prototype.mapMode=lt.TrackBefore;ha.prototype.point=!0;class g1 extends fe{constructor(e,t,n,a,s,r){super(t,n,s,e),this.block=a,this.isReplace=r,this.mapMode=a?t<=0?lt.TrackBefore:lt.TrackAfter:lt.TrackDel}get type(){return this.startSide!=this.endSide?Xe.WidgetRange:this.startSide<=0?Xe.WidgetBefore:Xe.WidgetAfter}get heightRelevant(){return this.block||!!this.widget&&(this.widget.estimatedHeight>=5||this.widget.lineBreaks>0)}eq(e){return e instanceof g1&&IG(this.widget,e.widget)&&this.block==e.block&&this.startSide==e.startSide&&this.endSide==e.endSide}range(e,t=e){if(this.isReplace&&(e>t||e==t&&this.startSide>0&&this.endSide<=0))throw new RangeError("Invalid range for replacement decoration");if(!this.isReplace&&t!=e)throw new RangeError("Widget decorations can only have zero-length ranges");return super.range(e,t)}}g1.prototype.point=!0;function xf(i,e=!1){let{inclusiveStart:t,inclusiveEnd:n}=i;return t==null&&(t=i.inclusive),n==null&&(n=i.inclusive),{start:t??e,end:n??e}}function IG(i,e){return i==e||!!(i&&e&&i.compare(e))}function E1(i,e,t,n=0){let a=t.length-1;a>=0&&t[a]+n>=i?t[a]=Math.max(t[a],e):t.push(i,e)}class $i extends Fn{constructor(e,t,n){super(),this.tagName=e,this.attributes=t,this.rank=n}eq(e){return e==this||e instanceof $i&&this.tagName==e.tagName&&uu(this.attributes,e.attributes)}static create(e){return new $i(e.tagName,e.attributes||$s,e.rank==null?50:Math.max(0,Math.min(e.rank,100)))}static set(e,t=!1){return oe.of(e,t)}}$i.prototype.startSide=$i.prototype.endSide=-1;const qe=i=>i.nodeType==1,H1=i=>i.nodeType==3;function Yi(i){let e;return i.nodeType==11?e=i.getSelection?i:i.ownerDocument:e=i,e.getSelection()}function Rl(i,e){return e?i==e||i.contains(e.nodeType!=1?e.parentNode:e):!1}function Ci(i,e){if(!e.anchorNode)return!1;try{return Rl(i,e.anchorNode)}catch{return!1}}function ws(i){return H1(i)?Ki(i,0,i.nodeValue.length).getClientRects():qe(i)?i.getClientRects():[]}function Di(i,e,t,n){return t?Dc(i,e,t,n,-1)||Dc(i,e,t,n,1):!1}function Gn(i){for(var e=0;;e++)if(i=i.previousSibling,!i)return e}function Ys(i){return qe(i)&&/^(DIV|P|LI|UL|OL|BLOCKQUOTE|DD|DT|H\d|SECTION|PRE)$/.test(i.nodeName)}function Dc(i,e,t,n,a){for(;;){if(i==t&&e==n)return!0;if(e==(a<0?0:Sn(i))){if(i.nodeName=="DIV")return!1;let s=i.parentNode;if(!s||!qe(s))return!1;e=Gn(i)+(a<0?0:1),i=s}else if(qe(i)){if(i=i.childNodes[e+(a<0?-1:0)],qe(i)&&(i.nodeName=="IMG"||i.nodeName=="BR"||i.contentEditable=="false"))return!1;e=a<0?Sn(i):0}else return!1}}function Sn(i){return H1(i)?i.nodeValue.length:i.childNodes.length}function Ks(i,e){let{left:t,right:n}=i;if(t==n)return i;let a=e?t:n;return{left:a,right:a,top:i.top,bottom:i.bottom}}function kG(i){let e=i.visualViewport;return e?{left:0,right:e.width,top:0,bottom:e.height}:{left:0,right:i.innerWidth,top:0,bottom:i.innerHeight}}function vf(i,e){let t=e.width/i.offsetWidth,n=e.height/i.offsetHeight;return(t>.995&&t<1.005||!isFinite(t)||Math.abs(e.width-i.offsetWidth)<1)&&(t=1),(n>.995&&n<1.005||!isFinite(n)||Math.abs(e.height-i.offsetHeight)<1)&&(n=1),{scaleX:t,scaleY:n}}function SG(i,e,t,n,a,s,r,o){let l=i.ownerDocument,u=l.defaultView||window;for(let m=i,c=!1;m&&!c;)if(qe(m)){let d,h=m==l.body,g=1,f=1,b=!0,x=!0;if(h)d=kG(u);else{let z=getComputedStyle(m);if(/^(fixed|sticky)$/.test(z.position)&&(c=!0),(m.scrollHeight<=m.clientHeight||!/auto|scroll/.test(z.overflowY))&&(x=!1),(m.scrollWidth<=m.clientWidth||!/auto|scroll/.test(z.overflowX))&&(b=!1),!b&&!x){m=m.assignedSlot||m.parentNode;continue}let I=m.getBoundingClientRect();({scaleX:g,scaleY:f}=vf(m,I)),d={left:I.left,right:I.left+m.clientWidth*g,top:I.top,bottom:I.top+m.clientHeight*f}}let v=0,w=0;if(x)if(a=="nearest")e.top0&&e.bottom>d.bottom+w&&(w=e.bottom-d.bottom+r)):e.bottom>d.bottom-r&&(w=e.bottom-d.bottom+r,t<0&&e.top-w0&&e.right>d.right+v&&(v=e.right-d.right+s)):e.right>d.right-s&&(v=e.right-d.right+s,t<0&&e.leftd.bottom||e.leftd.right)&&(e={left:Math.max(e.left,d.left),right:Math.min(e.right,d.right),top:Math.max(e.top,d.top),bottom:Math.min(e.bottom,d.bottom)}),m=m.assignedSlot||m.parentNode}else if(m.nodeType==11)m=m.host;else break}function wf(i,e=!0){let t=i.ownerDocument,n=null,a=null;for(let s=i.parentNode;s&&!(s==t.body||(!e||n)&&a);)if(qe(s))!a&&s.scrollHeight>s.clientHeight&&/scroll|auto/.test(getComputedStyle(s).overflowY)&&(a=s),e&&!n&&s.scrollWidth>s.clientWidth&&/scroll|auto/.test(getComputedStyle(s).overflowX)&&(n=s),s=s.assignedSlot||s.parentNode;else if(s.nodeType==11)s=s.host;else break;return{x:n,y:a}}class TG{constructor(){this.anchorNode=null,this.anchorOffset=0,this.focusNode=null,this.focusOffset=0}eq(e){return this.anchorNode==e.anchorNode&&this.anchorOffset==e.anchorOffset&&this.focusNode==e.focusNode&&this.focusOffset==e.focusOffset}setRange(e){let{anchorNode:t,focusNode:n}=e;this.set(t,Math.min(e.anchorOffset,t?Sn(t):0),n,Math.min(e.focusOffset,n?Sn(n):0))}set(e,t,n,a){this.anchorNode=e,this.anchorOffset=t,this.focusNode=n,this.focusOffset=a}}function yf(i){let e=[];for(let t=i;t;t=t.nodeType==11?t.host:t.parentNode)qe(t)&&e.push({node:t,left:t.scrollLeft,top:t.scrollTop});return e}function zf(i,e=!0){for(let{node:t,left:n,top:a}of i)e&&t.scrollTop!=a&&(t.scrollTop=a),t.scrollLeft!=n&&(t.scrollLeft=n)}let Xn=null;V.safari&&V.safari_version>=26&&(Xn=!1);function If(i){if(i.setActive)return i.setActive();if(Xn)return i.focus(Xn);let e=yf(i);i.focus(Xn==null?{get preventScroll(){return Xn={preventScroll:!0},!0}}:void 0),Xn||(Xn=!1,zf(e))}let Bc;function Ki(i,e,t=e){let n=Bc||(Bc=document.createRange());return n.setEnd(i,t),n.setStart(i,e),n}function N1(i,e,t,n){let a={key:e,code:e,keyCode:t,which:t,cancelable:!0};n&&({altKey:a.altKey,ctrlKey:a.ctrlKey,shiftKey:a.shiftKey,metaKey:a.metaKey}=n);let s=new KeyboardEvent("keydown",a);s.synthetic=!0,i.dispatchEvent(s);let r=new KeyboardEvent("keyup",a);return r.synthetic=!0,i.dispatchEvent(r),s.defaultPrevented||r.defaultPrevented}function _G(i){for(;i;){if(i&&(i.nodeType==9||i.nodeType==11&&i.host))return i;i=i.assignedSlot||i.parentNode}return null}function AG(i,e){let t=e.focusNode,n=e.focusOffset;if(!t||e.anchorNode!=t||e.anchorOffset!=n)return!1;for(n=Math.min(n,Sn(t));;)if(n){if(!qe(t))return!1;let a=t.childNodes[n-1];a.contentEditable=="false"?n--:(t=a,n=Sn(t))}else{if(t==i)return!0;n=Gn(t),t=t.parentNode}}function kf(i){return i instanceof Window?i.pageYOffset>Math.max(0,i.document.documentElement.scrollHeight-i.innerHeight-4):i.scrollTop>Math.max(1,i.scrollHeight-i.clientHeight-4)}function Sf(i,e){for(let t=i,n=e;;){if(H1(t)&&n>0)return{node:t,offset:n};if(qe(t)&&n>0){if(t.contentEditable=="false")return null;t=t.childNodes[n-1],n=Sn(t)}else if(t.parentNode&&!Ys(t))n=Gn(t),t=t.parentNode;else return null}}function Tf(i,e){for(let t=i,n=e;;){if(H1(t)&&n=t){if(o.level==n)return r;(s<0||(a!=0?a<0?o.fromt:e[s].level>o.level))&&(s=r)}}if(s<0)throw new RangeError("Index out of range");return s}}function jf(i,e){if(i.length!=e.length)return!1;for(let t=0;t=0;f-=3)if(Kt[f+1]==-h){let b=Kt[f+2],x=b&2?a:b&4?b&1?s:a:0;x&&(we[c]=we[Kt[f]]=x),o=f;break}}else{if(Kt.length==189)break;Kt[o++]=c,Kt[o++]=d,Kt[o++]=l}else if((g=we[c])==2||g==1){let f=g==a;l=f?0:1;for(let b=o-3;b>=0;b-=3){let x=Kt[b+2];if(x&2)break;if(f)Kt[b+2]|=2;else{if(x&4)break;Kt[b+2]|=4}}}}}function OG(i,e,t,n){for(let a=0,s=n;a<=t.length;a++){let r=a?t[a-1].to:i,o=al;)g==b&&(g=t[--f].from,b=f?t[f-1].to:i),we[--g]=h;l=m}else s=u,l++}}}function Gl(i,e,t,n,a,s,r){let o=n%2?2:1;if(n%2==a%2)for(let l=e,u=0;ll&&r.push(new ln(l,f.from,h));let b=f.direction==V1!=!(h%2);Hl(i,b?n+1:n,a,f.inner,f.from,f.to,r),l=f.to}g=f.to}else{if(g==t||(m?we[g]!=o:we[g]==o))break;g++}d?Gl(i,l,g,n+1,a,d,r):le;){let m=!0,c=!1;if(!u||l>s[u-1].to){let f=we[l-1];f!=o&&(m=!1,c=f==16)}let d=!m&&o==1?[]:null,h=m?n:n+1,g=l;e:for(;;)if(u&&g==s[u-1].to){if(c)break e;let f=s[--u];if(!m)for(let b=f.from,x=u;;){if(b==e)break e;if(x&&s[x-1].to==b)b=s[--x].from;else{if(we[b-1]==o)break e;break}}if(d)d.push(f);else{f.towe.length;)we[we.length]=256;let n=[],a=e==V1?0:1;return Hl(i,a,a,t,0,i.length,n),n}function Cf(i){return[new ln(0,i,0)]}let Df="";function ZG(i,e,t,n,a){var s;if(!i.length)return null;let r=n.head-i.from,o;if(n.head==i.from&&n.assoc<0){if(!a)return null;r=e[o=0].side(!1,t)}else if(n.head==i.to&&n.assoc>0){if(a)return null;r=e[o=e.length-1].side(!0,t)}else o=ln.find(e,r,(s=n.bidiLevel)!==null&&s!==void 0?s:-1,n.assoc);let l=e[o],u=l.side(a,t);if(r==u){let d=o+=a?1:-1;if(d<0||d>=e.length)return null;l=e[o=d],r=l.side(!a,t),u=l.side(a,t)}let m=ut(i.text,r,l.forward(a,t));(ml.to)&&(m=u),Df=i.text.slice(Math.min(r,m),Math.max(r,m));let c=o==(a?e.length-1:0)?null:e[o+(a?1:-1)];if(m==u){if(!c)return a?O.cursor(i.to,1):O.cursor(i.from,-1);if(c.level+(a?0:1)i.some(e=>e)}),WG=Q.define({combine:i=>i.some(e=>e)}),Ef=Q.define();class F1{constructor(e,t,n,a,s,r=!1){this.range=e,this.y=t,this.x=n,this.yMargin=a,this.xMargin=s,this.isSnapshot=r}map(e){return e.empty?this:new F1(this.range.map(e),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}clip(e){return this.range.to<=e.doc.length?this:new F1(O.cursor(e.doc.length),this.y,this.x,this.yMargin,this.xMargin,this.isSnapshot)}}const Ra=pe.define({map:(i,e)=>i.map(e)}),Nf=pe.define();function St(i,e,t){let n=i.facet(Mf);n.length?n[0](e):window.onerror&&window.onerror(String(e),t,void 0,void 0,e)||(t?console.error(t+":",e):console.error(e))}const vn=Q.define({combine:i=>i.length?i[0]:!0});let EG=0;const P1=Q.define({combine(i){return i.filter((e,t)=>{for(let n=0;n{let l=[];return r&&l.push(Pr.of(u=>{let m=u.plugin(o);return m?r(m):fe.none})),s&&l.push(s(o)),l})}static fromClass(e,t){return Et.define((n,a)=>new e(n,a),t)}}class co{constructor(e){this.spec=e,this.mustUpdate=null,this.value=null}get plugin(){return this.spec&&this.spec.plugin}update(e){if(this.value){if(this.mustUpdate){let t=this.mustUpdate;if(this.mustUpdate=null,this.value.update)try{this.value.update(t)}catch(n){if(St(t.state,n,"CodeMirror plugin crashed"),this.value.destroy)try{this.value.destroy()}catch{}this.deactivate()}}}else if(this.spec)try{this.value=this.spec.plugin.create(e,this.spec.arg)}catch(t){St(e.state,t,"CodeMirror plugin crashed"),this.deactivate()}return this}destroy(e){var t;if(!((t=this.value)===null||t===void 0)&&t.destroy)try{this.value.destroy()}catch(n){St(e.state,n,"CodeMirror plugin crashed")}}deactivate(){this.spec=this.value=null}}const Ff=Q.define(),hu=Q.define(),Pr=Q.define(),Rf=Q.define(),gu=Q.define(),ga=Q.define(),Jf=Q.define();function Pc(i,e){let t=i.state.facet(Jf);if(!t.length)return t;let n=t.map(s=>s instanceof Function?s(i):s),a=[];return oe.spans(n,e.from,e.to,{point(){},span(s,r,o,l){let u=s-e.from,m=r-e.from,c=a;for(let d=o.length-1;d>=0;d--,l--){let h=o[d].spec.bidiIsolate,g;if(l>0&&c.length&&(g=c[c.length-1]).to==u&&g.direction==h)g.to=m,c=g.inner;else{let f={from:u,to:m,direction:h,inner:[]};c.push(f),c=f.inner}}}}),Gf(e.text,a)}function Gf(i,e){for(let t of e){if(t.direction==null){for(let n=t.from,a=0;n0;t--){let a=e[t-1];if(!(a.fromA>n.toA)){if(a.toAa.push(new Zt(s,r,o,l))),this.changedRanges=a}static create(e,t,n){return new Xs(e,t,n)}get viewportChanged(){return(this.flags&4)>0}get viewportMoved(){return(this.flags&8)>0}get heightChanged(){return(this.flags&2)>0}get geometryChanged(){return this.docChanged||(this.flags&18)>0}get focusChanged(){return(this.flags&1)>0}get docChanged(){return!this.changes.empty}get selectionSet(){return this.transactions.some(e=>e.selection)}get empty(){return this.flags==0&&this.transactions.length==0}}const NG=[];class De{constructor(e,t,n=0){this.dom=e,this.length=t,this.flags=n,this.parent=null,e.cmTile=this}get breakAfter(){return this.flags&1}get children(){return NG}isWidget(){return!1}get isHidden(){return!1}isComposite(){return!1}isLine(){return!1}isText(){return!1}isBlock(){return!1}get domAttrs(){return null}sync(e){if(this.flags|=2,this.flags&4){this.flags&=-5;let t=this.domAttrs;t&&yG(this.dom,t)}}toString(){return this.constructor.name+(this.children.length?`(${this.children})`:"")+(this.breakAfter?"#":"")}destroy(){this.parent=null}setDOM(e){this.dom=e,e.cmTile=this}get posAtStart(){return this.parent?this.parent.posBefore(this):0}get posAtEnd(){return this.posAtStart+this.length}posBefore(e,t=this.posAtStart){let n=t;for(let a of this.children){if(a==e)return n;n+=a.length+a.breakAfter}throw new RangeError("Invalid child in posBefore")}posAfter(e){return this.posBefore(e)+e.length}covers(e){return!0}coordsIn(e,t,n){return null}domPosFor(e,t){let n=Gn(this.dom),a=this.length?e>0:t>0;return new Ht(this.parent.dom,n+(a?1:0),e==0||e==this.length)}markDirty(e){this.flags&=-3,e&&(this.flags|=4),this.parent&&this.parent.flags&2&&this.parent.markDirty(!1)}get overrideDOMText(){return null}get root(){for(let e=this;e;e=e.parent)if(e instanceof Mr)return e;return null}static get(e){return e.cmTile}}class Or extends De{constructor(e){super(e,0),this._children=[]}isComposite(){return!0}get children(){return this._children}get lastChild(){return this.children.length?this.children[this.children.length-1]:null}append(e){this.children.push(e),e.parent=this}sync(e){if(this.flags&2)return;super.sync(e);let t=this.dom,n=null,a,s=(e==null?void 0:e.node)==t?e:null,r=0;for(let o of this.children){if(o.sync(e),r+=o.length+o.breakAfter,a=n?n.nextSibling:t.firstChild,s&&a!=o.dom&&(s.written=!0),o.dom.parentNode==t)for(;a&&a!=o.dom;)a=Oc(a);else t.insertBefore(o.dom,a);n=o.dom}for(a=n?n.nextSibling:t.firstChild,s&&a&&(s.written=!0);a;)a=Oc(a);this.length=r}}function Oc(i){let e=i.nextSibling;return i.parentNode.removeChild(i),e}class Mr extends Or{constructor(e,t){super(t),this.view=e}owns(e){for(;e;e=e.parent)if(e==this)return!0;return!1}isBlock(){return!0}nearest(e){for(;;){if(!e)return null;let t=De.get(e);if(t&&this.owns(t))return t;e=e.parentNode}}blockTiles(e){for(let t=[],n=this,a=0,s=0;;)if(a==n.children.length){if(!t.length)return;n=n.parent,n.breakAfter&&s++,a=t.pop()}else{let r=n.children[a++];if(r instanceof yn)t.push(a),n=r,a=0;else{let o=s+r.length,l=e(r,s);if(l!==void 0)return l;s=o+r.breakAfter}}}resolveBlock(e,t){let n,a=-1,s,r=-1;if(this.blockTiles((o,l)=>{let u=l+o.length;if(e>=l&&e<=u){if(o.isWidget()&&t>=-1&&t<=1){if(o.flags&32)return!0;o.flags&16&&(n=void 0)}(le||e==l&&(t>1?o.length:o.covers(-1)))&&(!s||!o.isWidget()&&s.isWidget())&&(s=o,r=e-l)}}),!n&&!s)throw new Error("No tile at position "+e);return n&&t<0||!s?{tile:n,offset:a}:{tile:s,offset:r}}}class yn extends Or{constructor(e,t){super(e),this.wrapper=t}isBlock(){return!0}covers(e){return this.children.length?e<0?this.children[0].covers(-1):this.lastChild.covers(1):!1}get domAttrs(){return this.wrapper.attributes}static of(e,t){let n=new yn(t||document.createElement(e.tagName),e);return t||(n.flags|=4),n}}class q1 extends Or{constructor(e,t){super(e),this.attrs=t}isLine(){return!0}static start(e,t,n){let a=new q1(t||document.createElement("div"),e);return(!t||!n)&&(a.flags|=4),a}get domAttrs(){return this.attrs}resolveInline(e,t,n=!1){let a=null,s=-1,r=null,o=-1;function l(m,c){for(let d=0,h=0;d=c&&(g.isComposite()?l(g,c-h):(!r||n&&r.isHidden&&(t>0&&!(r.flags&32)||n&&RG(r,g)))&&(f>c||g.flags&32&&t<=1)?(r=g,o=c-h):(h=-1)&&(a=g,s=c-h)),h=f}}l(this,e);let u=(t<0?a:r)||a||r;return u?{tile:u,offset:u==a?s:o}:null}coordsIn(e,t,n){let a=this.resolveInline(e,t,!0);return a?a.tile.coordsIn(Math.max(0,a.offset),t,n):FG(this)}domIn(e,t){let n=this.resolveInline(e,t);if(n){let{tile:a,offset:s}=n;if(this.dom.contains(a.dom))return a.isText()?new Ht(a.dom,Math.min(a.dom.nodeValue.length,s)):a.domPosFor(s,a.flags&16?1:a.flags&32?-1:t);let r=n.tile.parent,o=!1;for(let l of r.children){if(o)return new Ht(l.dom,0);l==n.tile&&(o=!0)}}return new Ht(this.dom,0)}}function FG(i){let e=i.dom.lastChild;if(!e)return i.dom.getBoundingClientRect();let t=ws(e);return t[t.length-1]||null}function RG(i,e){let t=i.coordsIn(0,1),n=e.coordsIn(0,1);return t&&n&&n.topa&&(e=a);let s=e,r=e,o=0;e==0&&t<0||e==a&&t>=0?V.chrome||V.gecko||(e?(s--,o=1):r=0)?0:l.length-1];return V.safari&&!o&&u.width==0&&(u=Array.prototype.find.call(l,m=>m.width)||u),n==null?u:Ks(u,(o?o>0:t<0)==n)}static of(e,t){let n=new i1(t||document.createTextNode(e),e);return t||(n.flags|=2),n}}class p1 extends De{constructor(e,t,n,a){super(e,t,a),this.widget=n}isWidget(){return!0}get isHidden(){return this.widget.isHidden}covers(e){return this.flags&48?!1:(this.flags&(e<0?64:128))>0}coordsIn(e,t){return this.coordsInWidget(e,t,!1)}coordsInWidget(e,t,n){let a=this.widget.coordsAt(this.dom,e,t);if(a)return a;if(n)return Ks(this.dom.getBoundingClientRect(),this.length?e==0:t<=0);{let s=this.dom.getClientRects(),r=null;if(!s.length)return null;let o=this.flags&16?!0:this.flags&32?!1:e>0;for(let l=o?s.length-1:0;r=s[l],!(e>0?l==0:l==s.length-1||r.top0==n)}}class JG{constructor(e){this.index=0,this.beforeBreak=!1,this.parents=[],this.tile=e}advance(e,t,n){let{tile:a,index:s,beforeBreak:r,parents:o}=this;for(;e||t>0;)if(a.isComposite())if(r){if(!e)break;n&&n.break(),e--,r=!1}else if(s==a.children.length){if(!e&&!o.length)break;n&&n.leave(a),r=!!a.breakAfter,{tile:a,index:s}=o.pop(),s++}else{let l=a.children[s],u=l.breakAfter;(t>0?l.length<=e:l.length=0;o--){let l=t.marks[o],u=a.lastChild;if(u instanceof vt&&u.mark.eq(l.mark))u.dom!=l.dom&&u.setDOM(ho(l.dom)),a=u;else{let{dom:m}=l;this.cache.reused.get(l)&&De.get(l.dom)&&(m=ho(l.dom));let c=vt.of(l.mark,m);a.append(c),a=c}this.cache.reused.set(l,2)}let s=De.get(e.text);s&&this.cache.reused.set(s,2);let r=new i1(e.text,e.text.nodeValue);r.flags|=8,this.pos=e.range.toB,a.append(r)}addInlineWidget(e,t,n){let a=this.afterWidget&&e.flags&48&&(this.afterWidget.flags&48)==(e.flags&48);a||this.flushBuffer();let s=this.ensureMarks(t,n);!a&&!(e.flags&16)&&s.append(this.getBuffer(1)),s.append(e),this.pos+=e.length,this.afterWidget=e}addMark(e,t,n){this.flushBuffer(),this.ensureMarks(t,n).append(e),this.pos+=e.length,this.afterWidget=null}addBlockWidget(e){this.getBlockPos().append(e),this.pos+=e.length,this.lastBlock=e,this.endLine()}continueWidget(e){let t=this.afterWidget||this.lastBlock;t.length+=e,this.pos+=e}addLineStart(e,t){var n;e||(e=Vf);let a=q1.start(e,t||((n=this.cache.find(q1))===null||n===void 0?void 0:n.dom),!!t);this.getBlockPos().append(this.lastBlock=this.curLine=a)}addLine(e){this.getBlockPos().append(e),this.pos+=e.length,this.lastBlock=e,this.endLine()}addBreak(){this.lastBlock.flags|=1,this.endLine(),this.pos++}addLineStartIfNotCovered(e){this.blockPosCovered()||this.addLineStart(e)}ensureLine(e){this.curLine||this.addLineStart(e)}ensureMarks(e,t){var n;let a=this.curLine;for(let s=e.length-1;s>=0;s--){let r=e[s],o;if(t>0&&(o=a.lastChild)&&o instanceof vt&&o.mark.eq(r))a=o,t--;else{let l=vt.of(r,(n=this.cache.find(vt,u=>u.mark.eq(r)))===null||n===void 0?void 0:n.dom);a.append(l),a=l,t=0}}return a}endLine(){if(this.curLine){this.flushBuffer();let e=this.curLine.lastChild;(!e||!Mc(this.curLine,!1)||e.dom.nodeName!="BR"&&e.isWidget()&&!(V.ios&&Mc(this.curLine,!0)))&&this.curLine.append(this.cache.findWidget(go,0,32)||new p1(go.toDOM(),0,go,32)),this.curLine=this.afterWidget=null}}updateBlockWrappers(){this.wrapperPos>this.pos+1e4&&(this.blockWrappers.goto(this.pos),this.wrappers.length=0);for(let e=this.wrappers.length-1;e>=0;e--)this.wrappers[e].to=this.pos){let t=e.rank*102+e.value.rank,n=new GG(e.from,e.to,e.value,t),a=this.wrappers.length;for(;a>0&&(this.wrappers[a-1].rank-n.rank||this.wrappers[a-1].to-n.to)<0;)a--;this.wrappers.splice(a,0,n)}this.wrapperPos=this.pos}getBlockPos(){var e;this.updateBlockWrappers();let t=this.root;for(let n of this.wrappers){let a=t.lastChild;if(n.fromr.wrapper.eq(n.wrapper)))===null||e===void 0?void 0:e.dom);t.append(s),t=s}}return t}blockPosCovered(){let e=this.lastBlock;return e!=null&&!e.breakAfter&&(!e.isWidget()||(e.flags&160)>0)}getBuffer(e){let t=2|(e<0?16:32),n=this.cache.find(er,void 0,1);return n&&(n.flags=t),n||new er(t)}flushBuffer(){this.afterWidget&&!(this.afterWidget.flags&32)&&(this.afterWidget.parent.append(this.getBuffer(-1)),this.afterWidget=null)}}class VG{constructor(e){this.skipCount=0,this.text="",this.textOff=0,this.cursor=e.iter()}skip(e){this.textOff+e<=this.text.length?this.textOff+=e:(this.skipCount+=e-(this.text.length-this.textOff),this.text="",this.textOff=0)}next(e){if(this.textOff==this.text.length){let{value:a,lineBreak:s,done:r}=this.cursor.next(this.skipCount);if(this.skipCount=0,r)throw new Error("Ran out of text content when drawing inline views");this.text=a;let o=this.textOff=Math.min(e,a.length);return s?null:a.slice(0,o)}let t=Math.min(this.text.length,this.textOff+e),n=this.text.slice(this.textOff,t);return this.textOff=t,n}}const tr=[p1,q1,i1,vt,er,yn,Mr];for(let i=0;i[]),this.index=tr.map(()=>0),this.reused=new Map}add(e){let t=e.constructor.bucket,n=this.buckets[t];n.length<6?n.push(e):n[this.index[t]=(this.index[t]+1)%6]=e}find(e,t,n=2){let a=e.bucket,s=this.buckets[a],r=this.index[a];for(let o=0;o{if(this.cache.add(r),r.isComposite())return!1},enter:r=>this.cache.add(r),leave:()=>{},break:()=>{}}}run(e,t){let n=t&&this.getCompositionContext(t.text);for(let a=0,s=0,r=0;;){let o=ra){let u=l-a;this.preserve(u,!r,!o),a=l,s+=u}if(!o)break;t&&o.fromA<=t.range.fromA&&o.toA>=t.range.toA?(this.forward(o.fromA,t.range.fromA,t.range.fromA{if(r.isWidget())if(this.openWidget)this.builder.continueWidget(l-o);else{let u=l>0||o{r.isLine()?this.builder.addLineStart(r.attrs,this.cache.maybeReuse(r)):(this.cache.add(r),r instanceof vt&&a.unshift(r.mark)),this.openWidget=!1},leave:r=>{r.isLine()?a.length&&(a.length=s=0):r instanceof vt&&(a.shift(),s=Math.min(s,a.length))},break:()=>{this.builder.addBreak(),this.openWidget=!1}}),this.text.skip(e)}emit(e,t){let n=null,a=this.builder,s=-1,r=oe.spans(this.decorations,e,t,{point:(o,l,u,m,c,d)=>{if(u instanceof g1){if(this.disallowBlockEffectsFor[d]){if(u.block)throw new RangeError("Block decorations may not be specified via plugins");if(l>this.view.state.doc.lineAt(o).to)throw new RangeError("Decorations that replace line breaks may not be specified via plugins")}if(s=m.length,c>m.length)a.continueWidget(l-o);else{let h=u.widget||(u.block?U1.block:U1.inline),g=QG(u),f=this.cache.findWidget(h,l-o,g)||p1.of(h,this.view,l-o,g);u.block?(u.startSide>0&&a.addLineStartIfNotCovered(n),a.addBlockWidget(f)):(a.ensureLine(n),a.addInlineWidget(f,m,c))}n=null}else n=$G(n,u);l>o&&this.text.skip(l-o)},span:(o,l,u,m)=>{for(let c=o;c-1&&(this.openWidget=r>s),this.openWidget||a.addLineStartIfNotCovered(n),this.openMarks=r}forward(e,t,n=1){t-e<=10?this.old.advance(t-e,n,this.reuseWalker):(this.old.advance(5,-1,this.reuseWalker),this.old.advance(t-e-10,-1),this.old.advance(5,n,this.reuseWalker))}getCompositionContext(e){let t=[],n=null;for(let a=e.parentNode;;a=a.parentNode){let s=De.get(a);if(a==this.view.contentDOM)break;s instanceof vt?t.push(s):s!=null&&s.isLine()?n=s:s instanceof yn||(a.nodeName=="DIV"&&!n?n=new q1(a,Vf):n||t.push(vt.of(new da({tagName:a.nodeName.toLowerCase(),attributes:zG(a)}),a)))}return n?{line:n,marks:t}:null}}function Mc(i,e){let t=n=>{for(let a of n.children)if((e?a.isText():a.length)||t(a))return!0;return!1};return t(i)}function QG(i){let e=i.isReplace?(i.startSide<0?64:0)|(i.endSide>0?128:0):i.startSide>0?32:16;return i.block&&(e|=256),e}const Vf={class:"cm-line"};function $G(i,e){let t=e.spec.attributes,n=e.spec.class;return!t&&!n||(i||(i={class:"cm-line"}),t&&lu(t,i),n&&(i.class+=" "+n)),i}function YG(i){let e=[];for(let t=i.parents.length;t>1;t--){let n=t==i.parents.length?i.tile:i.parents[t].tile;n instanceof vt&&e.push(n.mark)}return e}function ho(i){let e=De.get(i);return e&&e.setDOM(i.cloneNode()),i}class U1 extends X1{constructor(e){super(),this.tag=e}eq(e){return e.tag==this.tag}toDOM(){return document.createElement(this.tag)}updateDOM(e){return e.nodeName.toLowerCase()==this.tag}get isHidden(){return!0}}U1.inline=new U1("span");U1.block=new U1("div");const go=new class extends X1{toDOM(){return document.createElement("br")}get isHidden(){return!0}get editable(){return!0}};class Zc{constructor(e){this.view=e,this.decorations=[],this.blockWrappers=[],this.dynamicDecorationMap=[!1],this.domChanged=null,this.hasComposition=null,this.editContextFormatting=fe.none,this.lastCompositionAfterCursor=!1,this.minWidth=0,this.minWidthFrom=0,this.minWidthTo=0,this.impreciseAnchor=null,this.impreciseHead=null,this.forceSelection=!1,this.lastUpdate=Date.now(),this.updateDeco(),this.tile=new Mr(e,e.contentDOM),this.updateInner([new Zt(0,0,0,e.state.doc.length)],null)}update(e){var t;let n=e.changedRanges;this.minWidth>0&&n.length&&(n.every(({fromA:m,toA:c})=>cthis.minWidthTo)?(this.minWidthFrom=e.changes.mapPos(this.minWidthFrom,1),this.minWidthTo=e.changes.mapPos(this.minWidthTo,1)):this.minWidth=this.minWidthFrom=this.minWidthTo=0),this.updateEditContextFormatting(e);let a=-1;this.view.inputState.composing>=0&&!this.view.observer.editContext&&(!((t=this.domChanged)===null||t===void 0)&&t.newSel?a=this.domChanged.newSel.head:!rH(e.changes,this.hasComposition)&&!e.selectionSet&&(a=e.state.selection.main.head));let s=a>-1?XG(this.view,e.changes,a):null;if(this.domChanged=null,this.hasComposition){let{from:m,to:c}=this.hasComposition;n=new Zt(m,c,e.changes.mapPos(m,-1),e.changes.mapPos(c,1)).addToSet(n.slice())}this.hasComposition=s?{from:s.range.fromB,to:s.range.toB}:null,(V.ie||V.chrome)&&!s&&e&&e.state.doc.lines!=e.startState.doc.lines&&(this.forceSelection=!0);let r=this.decorations,o=this.blockWrappers;this.updateDeco();let l=nH(r,this.decorations,e.changes);l.length&&(n=Zt.extendWithRanges(n,l));let u=aH(o,this.blockWrappers,e.changes);return u.length&&(n=Zt.extendWithRanges(n,u)),s&&!n.some(m=>m.fromA<=s.range.fromA&&m.toA>=s.range.toA)&&(n=s.range.addToSet(n.slice())),this.tile.flags&2&&n.length==0?!1:(this.updateInner(n,s),e.transactions.length&&(this.lastUpdate=Date.now()),!0)}updateInner(e,t){this.view.viewState.mustMeasureContent=!0;let{observer:n}=this.view;n.ignore(()=>{if(t||e.length){let r=this.tile,o=new UG(this.view,r,this.blockWrappers,this.decorations,this.dynamicDecorationMap);t&&De.get(t.text)&&o.cache.reused.set(De.get(t.text),2),this.tile=o.run(e,t),ql(r,o.cache.reused)}this.tile.dom.style.height=this.view.viewState.contentHeight/this.view.scaleY+"px",this.tile.dom.style.flexBasis=this.minWidth?this.minWidth+"px":"";let s=V.chrome||V.ios?{node:n.selectionRange.focusNode,written:!1}:void 0;this.tile.sync(s),s&&(s.written||n.selectionRange.focusNode!=s.node||!this.tile.dom.contains(s.node))&&(this.forceSelection=!0),this.tile.dom.style.height=""});let a=[];if(this.view.viewport.from||this.view.viewport.to-1)&&Ci(n,this.view.observer.selectionRange)&&!(a&&n.contains(a));if(!(s||t||r))return;let o=this.forceSelection;this.forceSelection=!1;let l=this.view.state.selection.main,u,m;if(l.empty?m=u=this.inlineDOMNearPos(l.anchor,l.assoc||1):(m=this.inlineDOMNearPos(l.head,l.head==l.from?1:-1),u=this.inlineDOMNearPos(l.anchor,l.anchor==l.from?1:-1)),V.gecko&&l.empty&&!this.hasComposition&&KG(u)){let d=document.createTextNode("");this.view.observer.ignore(()=>u.node.insertBefore(d,u.node.childNodes[u.offset]||null)),u=m=new Ht(d,0),o=!0}let c=this.view.observer.selectionRange;(o||!c.focusNode||(!Di(u.node,u.offset,c.anchorNode,c.anchorOffset)||!Di(m.node,m.offset,c.focusNode,c.focusOffset))&&!this.suppressWidgetCursorChange(c,l))&&(this.view.observer.ignore(()=>{V.android&&V.chrome&&n.contains(c.focusNode)&&sH(c.focusNode,n)&&(n.blur(),n.focus({preventScroll:!0}));let d=Yi(this.view.root);if(d)if(l.empty){if(V.gecko){let h=eH(u.node,u.offset);if(h&&h!=3){let g=(h==1?Sf:Tf)(u.node,u.offset);g&&(u=new Ht(g.node,g.offset))}}d.collapse(u.node,u.offset),l.bidiLevel!=null&&d.caretBidiLevel!==void 0&&(d.caretBidiLevel=l.bidiLevel)}else if(d.extend){d.collapse(u.node,u.offset);try{d.extend(m.node,m.offset)}catch{}}else{let h=document.createRange();l.anchor>l.head&&([u,m]=[m,u]),h.setEnd(m.node,m.offset),h.setStart(u.node,u.offset),d.removeAllRanges(),d.addRange(h)}r&&this.view.root.activeElement==n&&(n.blur(),a&&a.focus())}),this.view.observer.setSelectionRange(u,m)),this.impreciseAnchor=u.precise?null:new Ht(c.anchorNode,c.anchorOffset),this.impreciseHead=m.precise?null:new Ht(c.focusNode,c.focusOffset)}suppressWidgetCursorChange(e,t){return this.hasComposition&&t.empty&&Di(e.focusNode,e.focusOffset,e.anchorNode,e.anchorOffset)&&this.posFromDOM(e.focusNode,e.focusOffset)==t.head}enforceCursorAssoc(){if(this.hasComposition)return;let{view:e}=this,t=e.state.selection.main,n=Yi(e.root),{anchorNode:a,anchorOffset:s}=e.observer.selectionRange;if(!n||!t.empty||!t.assoc||!n.modify)return;let r=this.lineAt(t.head,t.assoc);if(!r)return;let o=r.posAtStart;if(t.head==o||t.head==o+r.length)return;let l=this.coordsAt(t.head,-1),u=this.coordsAt(t.head,1);if(!l||!u||l.bottom>u.top)return;let m=this.domAtPos(t.head+t.assoc,t.assoc);n.collapse(m.node,m.offset),n.modify("move",t.assoc<0?"forward":"backward","lineboundary"),e.observer.readSelectionRange();let c=e.observer.selectionRange;e.docView.posFromDOM(c.anchorNode,c.anchorOffset)!=t.from&&n.collapse(a,s)}posFromDOM(e,t){let n=this.tile.nearest(e);if(!n)return this.tile.dom.compareDocumentPosition(e)&2?0:this.view.state.doc.length;let a=n.posAtStart;if(n.isComposite()){let s;if(e==n.dom)s=n.dom.childNodes[t];else{let r=Sn(e)==0?0:t==0?-1:1;for(;;){let o=e.parentNode;if(o==n.dom)break;r==0&&o.firstChild!=o.lastChild&&(e==o.firstChild?r=-1:r=1),e=o}r<0?s=e:s=e.nextSibling}if(s==n.dom.firstChild)return a;for(;s&&!De.get(s);)s=s.nextSibling;if(!s)return a+n.length;for(let r=0,o=a;;r++){let l=n.children[r];if(l.dom==s)return o;o+=l.length+l.breakAfter}}else return n.isText()?e==n.dom?a+t:a+(t?n.length:0):a}domAtPos(e,t){let{tile:n,offset:a}=this.tile.resolveBlock(e,t);return n.isWidget()?n.domPosFor(a,t):n.domIn(a,t)}inlineDOMNearPos(e,t){let n,a=-1,s=!1,r,o=-1,l=!1;return this.tile.blockTiles((u,m)=>{if(u.isWidget()){if(u.flags&32&&m>=e)return!0;u.flags&16&&(s=!0)}else{let c=m+u.length;if(m<=e&&(n=u,a=e-m,s=c=e&&!r&&(r=u,o=e-m,l=m>e),m>e&&r)return!0}}),!n&&!r?this.domAtPos(e,t):(s&&r?n=null:l&&n&&(r=null),n&&t<0||!r?n.domIn(a,t):r.domIn(o,t))}coordsAt(e,t,n){let{tile:a,offset:s}=this.tile.resolveBlock(e,t);return a.isWidget()?a.widget instanceof po?null:a.coordsInWidget(s,t,!0):a.coordsIn(s,t,n)}lineAt(e,t){let{tile:n}=this.tile.resolveBlock(e,t);return n.isLine()?n:null}coordsForChar(e){let{tile:t,offset:n}=this.tile.resolveBlock(e,1);if(!t.isLine())return null;function a(s,r){if(s.isComposite())for(let o of s.children){if(o.length>=r){let l=a(o,r);if(l)return l}if(r-=o.length,r<0)break}else if(s.isText()&&rMath.max(this.view.scrollDOM.clientWidth,this.minWidth)+1,o=-1,l=this.view.textDirection==ze.LTR,u=0,m=(c,d,h)=>{for(let g=0;ga);g++){let f=c.children[g],b=d+f.length,x=f.dom.getBoundingClientRect(),{height:v}=x;if(h&&!g&&(u+=x.top-h.top),f instanceof yn)b>n&&m(f,d,x);else if(d>=n&&(u>0&&t.push(-u),t.push(v+u),u=0,r)){let w=f.dom.lastChild,z=w?ws(w):[];if(z.length){let I=z[z.length-1],T=l?I.right-x.left:x.right-I.left;T>o&&(o=T,this.minWidth=s,this.minWidthFrom=d,this.minWidthTo=b)}}h&&g==c.children.length-1&&(u+=h.bottom-x.bottom),d=b+f.breakAfter}};return m(this.tile,0,null),t}textDirectionAt(e){let{tile:t}=this.tile.resolveBlock(e,1);return getComputedStyle(t.dom).direction=="rtl"?ze.RTL:ze.LTR}measureTextSize(){let e=this.tile.blockTiles(r=>{if(r.isLine()&&r.children.length&&r.length<=20){let o=0,l;for(let u of r.children){if(!u.isText()||/[^ -~]/.test(u.text))return;let m=ws(u.dom);if(m.length!=1)return;o+=m[0].width,l=m[0].height}if(o)return{lineHeight:r.dom.getBoundingClientRect().height,charWidth:o/r.length,textHeight:l}}});if(e)return e;let t=document.createElement("div"),n,a,s;return t.className="cm-line",t.style.width="99999px",t.style.position="absolute",t.textContent="abc def ghi jkl mno pqr stu",this.view.observer.ignore(()=>{this.tile.dom.appendChild(t);let r=ws(t.firstChild)[0];n=t.getBoundingClientRect().height,a=r&&r.width?r.width/27:7,s=r&&r.height?r.height:n,t.remove()}),{lineHeight:n,charWidth:a,textHeight:s}}computeBlockGapDeco(){let e=[],t=this.view.viewState;for(let n=0,a=0;;a++){let s=a==t.viewports.length?null:t.viewports[a],r=s?s.from-1:this.view.state.doc.length;if(r>n){let o=(t.lineBlockAt(r).bottom-t.lineBlockAt(n).top)/this.view.scaleY;e.push(fe.replace({widget:new po(o),block:!0,inclusive:!0,isBlockGap:!0}).range(n,r))}if(!s)break;n=s.to+1}return fe.set(e)}updateDeco(){let e=1,t=this.view.state.facet(Pr).map(s=>(this.dynamicDecorationMap[e++]=typeof s=="function")?s(this.view):s),n=!1,a=this.view.state.facet(gu).map((s,r)=>{let o=typeof s=="function";return o&&(n=!0),o?s(this.view):s});for(a.length&&(this.dynamicDecorationMap[e++]=n,t.push(oe.join(a))),this.decorations=[this.editContextFormatting,...t,this.computeBlockGapDeco(),this.view.viewState.lineGapDeco];etypeof s=="function"?s(this.view):s)}scrollIntoView(e){if(e.isSnapshot){let u=this.view.viewState.lineBlockAt(e.range.head);this.view.scrollDOM.scrollTop=u.top-e.yMargin,this.view.scrollDOM.scrollLeft=e.xMargin;return}for(let u of this.view.state.facet(Ef))try{if(u(this.view,e.range,e))return!0}catch(m){St(this.view.state,m,"scroll handler")}let{range:t}=e,n=this.coordsAt(t.head,t.assoc||(t.head>t.anchor?-1:1)),a;if(!n)return;!t.empty&&(a=this.coordsAt(t.anchor,t.anchor>t.head?-1:1))&&(n={left:Math.min(n.left,a.left),top:Math.min(n.top,a.top),right:Math.max(n.right,a.right),bottom:Math.max(n.bottom,a.bottom)});let s=pu(this.view),r={left:n.left-s.left,top:n.top-s.top,right:n.right+s.right,bottom:n.bottom+s.bottom},{offsetWidth:o,offsetHeight:l}=this.view.scrollDOM;if(SG(this.view.scrollDOM,r,t.head1&&(n.top>window.visualViewport.offsetTop+window.visualViewport.height||n.bottomn.isWidget()||n.children.some(t);return t(this.tile.resolveBlock(e,1).tile)}destroy(){ql(this.tile)}}function ql(i,e){let t=e==null?void 0:e.get(i);if(t!=1){t==null&&i.destroy();for(let n of i.children)ql(n,e)}}function KG(i){return qe(i.node)&&i.node.firstChild&&(i.offset==0||i.node.childNodes[i.offset-1].contentEditable=="false")&&(i.offset==i.node.childNodes.length||i.node.childNodes[i.offset].contentEditable=="false")}function qf(i,e){let t=i.observer.selectionRange;if(!t.focusNode)return null;let n=Sf(t.focusNode,t.focusOffset),a=Tf(t.focusNode,t.focusOffset),s=n||a;if(a&&n&&a.node!=n.node){let o=De.get(a.node);if(!o||o.isText()&&o.text!=a.node.nodeValue)s=a;else if(i.docView.lastCompositionAfterCursor){let l=De.get(n.node);!l||l.isText()&&l.text!=n.node.nodeValue||(s=a)}}if(i.docView.lastCompositionAfterCursor=s!=n,!s)return null;let r=e-s.offset;return{from:r,to:r+s.node.nodeValue.length,node:s.node}}function XG(i,e,t){let n=qf(i,t);if(!n)return null;let{node:a,from:s,to:r}=n,o=a.nodeValue;if(/[\n\r]/.test(o)||i.state.doc.sliceString(n.from,n.to)!=o)return null;let l=e.invertedDesc;return{range:new Zt(l.mapPos(s),l.mapPos(r),s,r),text:a}}function eH(i,e){return qe(i)?(e&&i.childNodes[e-1].contentEditable=="false"?1:0)|(e{ne.from&&(t=!0)}),t}class po extends X1{constructor(e){super(),this.height=e}toDOM(){let e=document.createElement("div");return e.className="cm-gap",this.updateDOM(e),e}eq(e){return e.height==this.height}updateDOM(e){return e.style.height=this.height+"px",!0}get editable(){return!0}get estimatedHeight(){return this.height}ignoreEvent(){return!1}}function oH(i,e,t=1){let n=i.charCategorizer(e),a=i.doc.lineAt(e),s=e-a.from;if(a.length==0)return O.cursor(e);s==0?t=1:s==a.length&&(t=-1);let r=s,o=s;t<0?r=ut(a.text,s,!1):o=ut(a.text,s);let l=n(a.text.slice(r,o));for(;r>0;){let u=ut(a.text,r,!1);if(n(a.text.slice(u,r))!=l)break;r=u}for(;oi.defaultLineHeight*1.5){let o=i.viewState.heightOracle.textHeight,l=Math.floor((a-t.top-(i.defaultLineHeight-o)*.5)/o);s+=l*i.viewState.heightOracle.lineLength}let r=i.state.sliceDoc(t.from,t.to);return t.from+fG(r,s,i.state.tabSize)}function Ul(i,e,t){let n=i.lineBlockAt(e);if(Array.isArray(n.type)){let a;for(let s of n.type){if(s.from>e)break;if(!(s.toe)return s;(!a||s.type==Xe.Text&&(a.type!=s.type||(t<0?s.frome)))&&(a=s)}}return a||n}return n}function uH(i,e,t,n){let a=Ul(i,e.head,e.assoc||-1),s=!n||a.type!=Xe.Text||!(i.lineWrapping||a.widgetLineBreaks)?null:i.coordsAtPos(e.assoc<0&&e.head>a.from?e.head-1:e.head);if(s){let r=i.dom.getBoundingClientRect(),o=i.textDirectionAt(a.from),l=i.posAndSideAtCoords({x:t==(o==ze.LTR)?r.right-1:r.left+1,y:(s.top+s.bottom)/2});if(l)return O.cursor(l.pos,l.assoc)}return O.cursor(t?a.to:a.from,t?1:-1)}function Lc(i,e,t,n){let a=i.state.doc.lineAt(e.head),s=i.bidiSpans(a),r=i.textDirectionAt(a.from);for(let o=e,l=null;;){let u=ZG(a,s,r,o,t),m=Df;if(!u){if(a.number==(t?i.state.doc.lines:1))return o;m=` -`,a=i.state.doc.line(a.number+(t?1:-1)),s=i.bidiSpans(a),u=t?O.cursor(a.from,-1):O.cursor(a.to,1)}if(l){if(!l(m))return o}else{if(!n)return u;l=n(m)}o=u}}function mH(i,e,t){let n=i.state.charCategorizer(e),a=n(t);return s=>{let r=n(s);return a==Mt.Space&&(a=r),a==r}}function cH(i,e,t,n){let a=e.head,s=t?1:-1;if(a==(t?i.state.doc.length:0))return O.cursor(a,e.assoc);let r=e.goalColumn,o,l=i.contentDOM.getBoundingClientRect(),u=i.coordsAtPos(a,e.assoc||((e.empty?t:e.head==e.from)?1:-1)),m=i.documentTop;if(u)r==null&&(r=u.left-l.left),o=s<0?u.top:u.bottom;else{let g=i.viewState.lineBlockAt(a);r==null&&(r=Math.min(l.right-l.left,i.defaultCharacterWidth*(a-g.from))),o=(s<0?g.top:g.bottom)+m}let c=l.left+r,d=i.viewState.heightOracle.textHeight>>1,h=n??d;for(let g=0;;g+=d){let f=o+(h+g)*s,b=Ql(i,{x:c,y:f},!1,s);if(t?f>l.bottom:fo:v{if(e>s&&ea(i)),t.from,e.head>t.from?-1:1);return n==t.from?t:O.cursor(n,ni.viewState.docHeight)return new on(i.state.doc.length,-1);if(u=i.elementAtHeight(l),n==null)break;if(u.type==Xe.Text){if(n<0?u.toi.viewport.to)break;let d=i.docView.coordsAt(n<0?u.from:u.to,n>0?-1:1);if(d&&(n<0?d.top<=l+s:d.bottom>=l+s))break}let c=i.viewState.heightOracle.textHeight/2;l=n>0?u.bottom+c:u.top-c}if(i.viewport.from>=u.to||i.viewport.to<=u.from){if(t)return null;if(u.type==Xe.Text){let c=lH(i,a,u,r,o);return new on(c,c==u.from?1:-1)}}if(u.type!=Xe.Text)return l<(u.top+u.bottom)/2?new on(u.from,1):new on(u.to,-1);let m=i.docView.lineAt(u.from,2);return(!m||m.length!=u.length)&&(m=i.docView.lineAt(u.from,-2)),new dH(i,r,o,i.textDirectionAt(u.from)).scanTile(m,u.from)}class dH{constructor(e,t,n,a){this.view=e,this.x=t,this.y=n,this.baseDir=a,this.line=null,this.spans=null}bidiSpansAt(e){return(!this.line||this.line.from>e||this.line.to1||n.length&&(n[0].level!=this.baseDir||n[0].to+a.from>1;t:if(r.has(f)){for(let v=1;v=s&&(w-=g),!r.has(w)){f=w;break t}}break e}r.add(f);let b=t(f),x=0;if(b)for(let v=0;v1))if(w.bottomthis.y)(!u||u.top>w.top)&&(u=w),x=-1;else{let z=w.left>this.x?this.x-w.left:w.right(g+g+f)/3)return this.y=l.bottom-1,this.scan(e,t,!0);if(u&&u.top<(g+f+f)/3)return this.y=u.top+1,this.scan(e,t,!0)}let h=(o?this.dirAt(e[m],1):this.baseDir)==ze.LTR;return{i:m,after:this.x>(d.left+d.right)/2==h}}scanText(e,t){let n=[];for(let s=0;s{let r=n[s]-t,o=n[s+1]-t;return Ki(e.dom,r,o).getClientRects()});return a.after?new on(n[a.i+1],-1):new on(n[a.i],1)}scanTile(e,t){if(!e.length)return new on(t,1);if(e.children.length==1){let o=e.children[0];if(o.isText())return this.scanText(o,t);if(o.isComposite())return this.scanTile(o,t)}let n=[t];for(let o=0,l=t;o{let l=e.children[o];return l.flags&48?null:(qe(l.dom)?l.dom:Ki(l.dom,0,l.length)).getClientRects()}),s=e.children[a.i],r=n[a.i];return s.isText()?this.scanText(s,r):s.isComposite()?this.scanTile(s,r):a.after?new on(n[a.i+1],-1):new on(r,1)}}const C1="￿";class hH{constructor(e,t){this.points=e,this.view=t,this.text="",this.lineSeparator=t.state.facet(ce.lineSeparator)}append(e){this.text+=e}lineBreak(){this.text+=C1}readRange(e,t){if(!e)return this;let n=e.parentNode;for(let a=e;;){this.findPointBefore(n,a);let s=this.text.length;this.readNode(a);let r=De.get(a),o=a.nextSibling;if(o==t){r!=null&&r.breakAfter&&!o&&n!=this.view.contentDOM&&this.lineBreak();break}let l=De.get(o);(r&&l?r.breakAfter:(r?r.breakAfter:Ys(a))||Ys(o)&&(a.nodeName!="BR"||r!=null&&r.isWidget())&&this.text.length>s)&&!pH(o,t)&&this.lineBreak(),a=o}return this.findPointBefore(n,t),this}readTextNode(e){let t=e.nodeValue;for(let n of this.points)n.node==e&&(n.pos=this.text.length+Math.min(n.offset,t.length));for(let n=0,a=this.lineSeparator?null:/\r\n?|\n/g;;){let s=-1,r=1,o;if(this.lineSeparator?(s=t.indexOf(this.lineSeparator,n),r=this.lineSeparator.length):(o=a.exec(t))&&(s=o.index,r=o[0].length),this.append(t.slice(n,s<0?t.length:s)),s<0)break;if(this.lineBreak(),r>1)for(let l of this.points)l.node==e&&l.pos>this.text.length&&(l.pos-=r-1);n=s+r}}readNode(e){let t=De.get(e),n=t&&t.overrideDOMText;if(n!=null){this.findPointInside(e,n.length);for(let a=n.iter();!a.next().done;)a.lineBreak?this.lineBreak():this.append(a.value)}else H1(e)?this.readTextNode(e):e.nodeName=="BR"?e.nextSibling&&this.lineBreak():qe(e)&&this.readRange(e.firstChild,null)}findPointBefore(e,t){for(let n of this.points)n.node==e&&e.childNodes[n.offset]==t&&(n.pos=this.text.length)}findPointInside(e,t){for(let n of this.points)(H1(e)?n.node==e:e.contains(n.node))&&(n.pos=this.text.length+(gH(e,n.node,n.offset)?t:0))}}function gH(i,e,t){for(;;){if(!e||t-1;let{impreciseHead:s,impreciseAnchor:r}=e.docView,o=e.state.selection;if(e.state.readOnly&&t>-1)this.newSel=null;else if(t>-1&&(this.bounds=Qf(e.docView.tile,t,n,0))){let l=s||r?[]:xH(e),u=new hH(l,e);u.readRange(this.bounds.startDOM,this.bounds.endDOM),this.text=u.text,this.newSel=vH(l,this.bounds.from)}else{let l=e.observer.selectionRange,u=s&&s.node==l.focusNode&&s.offset==l.focusOffset||!Rl(e.contentDOM,l.focusNode)?o.main.head:e.docView.posFromDOM(l.focusNode,l.focusOffset),m=r&&r.node==l.anchorNode&&r.offset==l.anchorOffset||!Rl(e.contentDOM,l.anchorNode)?o.main.anchor:e.docView.posFromDOM(l.anchorNode,l.anchorOffset),c=e.viewport;if((V.ios||V.chrome)&&u!=m&&Math.min(u,m)<=o.main.from&&Math.max(u,m)>=o.main.to&&(c.from>0||c.to-1&&o.ranges.length>1)this.newSel=o.replaceRange(O.range(m,u));else if(e.lineWrapping&&m==u&&!(o.main.empty&&o.main.head==u)&&e.inputState.lastTouchTime>Date.now()-100){let d=e.coordsAtPos(u,-1),h=0;d&&(h=e.inputState.lastTouchY<=d.bottom?-1:1),this.newSel=O.create([O.cursor(u,h)])}else this.newSel=O.single(m,u)}}}function Qf(i,e,t,n){if(i.isComposite()){let a=-1,s=-1,r=-1,o=-1;for(let l=0,u=n,m=n;lt)return Qf(c,e,t,u);if(d>=e&&a==-1&&(a=l,s=u),u>t&&c.dom.parentNode==i.dom){r=l,o=m;break}m=d,u=d+c.breakAfter}return{from:s,to:o<0?n+i.length:o,startDOM:(a?i.children[a-1].dom.nextSibling:null)||i.dom.firstChild,endDOM:r=0?i.children[r].dom:null}}else return i.isText()?{from:n,to:n+i.length,startDOM:i.dom,endDOM:i.dom.nextSibling}:null}function $f(i,e){let t,{newSel:n}=e,{state:a}=i,s=a.selection.main,r=i.inputState.lastKeyTime>Date.now()-100?i.inputState.lastKeyCode:-1;if(e.bounds){let{from:o,to:l}=e.bounds,u=s.from,m=null;(r===8||V.android&&e.text.length=o&&s.to<=l&&(e.typeOver||c!=e.text)&&c.slice(0,s.from-o)==e.text.slice(0,s.from-o)&&c.slice(s.to-o)==e.text.slice(d=e.text.length-(c.length-(s.to-o)))?t={from:s.from,to:s.to,insert:he.of(e.text.slice(s.from-o,d).split(C1))}:(h=Yf(c,e.text,u-o,m))&&(V.chrome&&r==13&&h.toB==h.from+2&&e.text.slice(h.from,h.toB)==C1+C1&&h.toB--,t={from:o+h.from,to:o+h.toA,insert:he.of(e.text.slice(h.from,h.toB).split(C1))})}else n&&(!i.hasFocus&&a.facet(vn)||$l(n,s))&&(n=null);if(!t&&!n)return!1;if((V.mac||V.android)&&t&&t.from==t.to&&t.from==s.head-1&&/^\. ?$/.test(t.insert.toString())&&i.contentDOM.getAttribute("autocorrect")=="off"?(n&&t.insert.length==2&&(n=O.single(n.main.anchor-1,n.main.head-1)),t={from:t.from,to:t.to,insert:he.of([t.insert.toString().replace("."," ")])}):a.doc.lineAt(s.from).toDate.now()-50?t={from:s.from,to:s.to,insert:a.toText(i.inputState.insertingText)}:V.chrome&&t&&t.from==t.to&&t.from==s.head&&t.insert.toString()==` - `&&i.lineWrapping&&(n&&(n=O.single(n.main.anchor-1,n.main.head-1)),t={from:s.from,to:s.to,insert:he.of([" "])}),t)return fu(i,t,n,r);if(n&&!$l(n,s)){let o=!1,l="select";return i.inputState.lastSelectionTime>Date.now()-50&&(i.inputState.lastSelectionOrigin=="select"&&(o=!0),l=i.inputState.lastSelectionOrigin,l=="select.pointer"&&(n=Uf(a.facet(ga).map(u=>u(i)),n))),i.dispatch({selection:n,scrollIntoView:o,userEvent:l}),!0}else return!1}function fu(i,e,t,n=-1){if(V.ios&&i.inputState.flushIOSKey(e))return!0;let a=i.state.selection.main;if(V.android&&(e.to==a.to&&(e.from==a.from||e.from==a.from-1&&i.state.sliceDoc(e.from,a.from)==" ")&&e.insert.length==1&&e.insert.lines==2&&N1(i.contentDOM,"Enter",13)||(e.from==a.from-1&&e.to==a.to&&e.insert.length==0||n==8&&e.insert.lengtha.head)&&N1(i.contentDOM,"Backspace",8)||e.from==a.from&&e.to==a.to+1&&e.insert.length==0&&N1(i.contentDOM,"Delete",46)))return!0;let s=e.insert.toString();i.inputState.composing>=0&&i.inputState.composing++;let r,o=()=>r||(r=bH(i,e,t));return i.state.facet(Zf).some(l=>l(i,e.from,e.to,s,o))||i.dispatch(o()),!0}function bH(i,e,t){let n,a=i.state,s=a.selection.main,r=-1;if(e.from==e.to&&e.froms.to){let l=e.fromc(i)),u,l);e.from==m&&(r=m)}if(r>-1)n={changes:e,selection:O.cursor(e.from+e.insert.length,-1)};else if(e.from>=s.from&&e.to<=s.to&&e.to-e.from>=(s.to-s.from)/3&&(!t||t.main.empty&&t.main.from==e.from+e.insert.length)&&i.inputState.composing<0){let l=s.frome.to?a.sliceDoc(e.to,s.to):"";n=a.replaceSelection(i.state.toText(l+e.insert.sliceString(0,void 0,i.state.lineBreak)+u))}else{let l=a.changes(e),u=t&&t.main.to<=l.newLength?t.main:void 0;if(a.selection.ranges.length>1&&(i.inputState.composing>=0||i.inputState.compositionPendingChange)&&e.to<=s.to+10&&e.to>=s.to-10){let m=i.state.sliceDoc(e.from,e.to),c,d=t&&qf(i,t.main.head);if(d){let g=e.insert.length-(e.to-e.from);c={from:d.from,to:d.to-g}}else c=i.state.doc.lineAt(s.head);let h=s.to-e.to;n=a.changeByRange(g=>{if(g.from==s.from&&g.to==s.to)return{changes:l,range:u||g.map(l)};let f=g.to-h,b=f-m.length;if(i.state.sliceDoc(b,f)!=m||f>=c.from&&b<=c.to)return{range:g};let x=a.changes({from:b,to:f,insert:e.insert}),v=g.to-s.to;return{changes:x,range:u?O.range(Math.max(0,u.anchor+v),Math.max(0,u.head+v)):g.map(x)}})}else n={changes:l,selection:u&&a.selection.replaceRange(u)}}let o="input.type";return(i.composing||i.inputState.compositionPendingChange&&i.inputState.compositionEndedAt>Date.now()-50)&&(i.inputState.compositionPendingChange=!1,o+=".compose",i.inputState.compositionFirstChange&&(o+=".start",i.inputState.compositionFirstChange=!1)),a.update(n,{userEvent:o,scrollIntoView:!0})}function Yf(i,e,t,n){let a=Math.min(i.length,e.length),s=0;for(;s0&&o>0&&i.charCodeAt(r-1)==e.charCodeAt(o-1);)r--,o--;if(n=="end"){let l=Math.max(0,s-Math.min(r,o));t-=r+l-s}if(r=r?s-t:0;s-=l,o=s+(o-r),r=s}else if(o=o?s-t:0;s-=l,r=s+(r-o),o=s}return{from:s,toA:r,toB:o}}function xH(i){let e=[];if(i.root.activeElement!=i.contentDOM)return e;let{anchorNode:t,anchorOffset:n,focusNode:a,focusOffset:s}=i.observer.selectionRange;return t&&(e.push(new Wc(t,n)),(a!=t||s!=n)&&e.push(new Wc(a,s))),e}function vH(i,e){if(i.length==0)return null;let t=i[0].pos,n=i.length==2?i[1].pos:t;return t<0||n<0?null:t==n?O.create([O.cursor(n+e,-1)]):O.single(t+e,n+e)}function $l(i,e){return e.head==i.main.head&&e.anchor==i.main.anchor}class wH{setSelectionOrigin(e){this.lastSelectionOrigin=e,this.lastSelectionTime=Date.now()}constructor(e){this.view=e,this.lastKeyCode=0,this.lastKeyTime=0,this.touchActive=!1,this.lastTouchTime=0,this.lastTouchX=0,this.lastTouchY=0,this.lastFocusTime=0,this.lastScrollTop=0,this.lastScrollLeft=0,this.lastWheelEvent=0,this.pendingIOSKey=void 0,this.lastIOSMomentumScroll=0,this.tabFocusMode=-1,this.lastSelectionOrigin=null,this.lastSelectionTime=0,this.lastContextMenu=0,this.scrollHandlers=[],this.handlers=Object.create(null),this.composing=-1,this.compositionFirstChange=null,this.compositionEndedAt=0,this.compositionPendingKey=!1,this.compositionPendingChange=!1,this.insertingText="",this.insertingTextAt=0,this.mouseSelection=null,this.draggedContent=null,this.handleEvent=this.handleEvent.bind(this),this.notifiedFocused=e.hasFocus,V.safari&&e.contentDOM.addEventListener("input",()=>null),V.gecko&&ZH(e.contentDOM.ownerDocument)}handleEvent(e){!jH(this.view,e)||this.ignoreDuringComposition(e)||e.type=="keydown"&&this.keydown(e)||(this.view.updateState!=0?Promise.resolve().then(()=>this.runHandlers(e.type,e)):this.runHandlers(e.type,e))}runHandlers(e,t){let n=this.handlers[e];if(n){for(let a of n.observers)a(this.view,t);for(let a of n.handlers){if(t.defaultPrevented)break;if(a(this.view,t)){t.preventDefault();break}}}}ensureHandlers(e){let t=zH(e),n=this.handlers,a=this.view.contentDOM;for(let s in t)if(s!="scroll"){let r=!t[s].handlers.length,o=n[s];o&&r!=!o.handlers.length&&(a.removeEventListener(s,this.handleEvent),o=null),o||a.addEventListener(s,this.handleEvent,{passive:r})}for(let s in n)s!="scroll"&&!t[s]&&a.removeEventListener(s,this.handleEvent);this.handlers=t}keydown(e){if(this.lastKeyCode=e.keyCode,this.lastKeyTime=Date.now(),e.keyCode==9&&this.tabFocusMode>-1&&(!this.tabFocusMode||Date.now()<=this.tabFocusMode))return!0;if(this.tabFocusMode>0&&e.keyCode!=27&&Xf.indexOf(e.keyCode)<0&&(this.tabFocusMode=-1),V.android&&V.chrome&&!e.synthetic&&(e.keyCode==13||e.keyCode==8))return this.view.observer.delayAndroidKey(e.key,e.keyCode),!0;if(V.ios&&!e.synthetic&&!e.altKey&&!e.metaKey&&(Kf.some(t=>t.keyCode==e.keyCode)&&!e.ctrlKey||IH.indexOf(e.key)>-1&&e.ctrlKey)){let t={ctrlKey:e.ctrlKey,altKey:e.altKey,metaKey:e.metaKey,shiftKey:e.shiftKey};t.shiftKey&&V.ios&&!/^(off|none)$/.test(this.view.contentDOM.autocapitalize)&&yH(this.view.win)&&(t.shiftKey=!1);let n=this.pendingIOSKey={key:e.key,keyCode:e.keyCode,mods:t};return setTimeout(()=>{this.pendingIOSKey==n&&this.flushIOSKey()},50),!0}return e.keyCode!=229&&this.view.observer.forceFlush(),!1}flushIOSKey(e){let t=this.pendingIOSKey;return!t||this.view.observer.pendingRecords().length||t.key=="Enter"&&e&&e.from0?!0:V.safari&&!V.ios&&this.compositionPendingKey&&Date.now()-this.compositionEndedAt<100?(this.compositionPendingKey=!1,!0):!1}startMouseSelection(e){this.mouseSelection&&this.mouseSelection.destroy(),this.mouseSelection=e}update(e){this.view.observer.update(e),this.mouseSelection&&this.mouseSelection.update(e),this.draggedContent&&e.docChanged&&(this.draggedContent=this.draggedContent.map(e.changes)),e.transactions.length&&(this.lastKeyCode=this.lastSelectionTime=0)}destroy(){this.mouseSelection&&this.mouseSelection.destroy()}}function yH(i){return i.visualViewport?i.visualViewport.height*i.visualViewport.scale/i.document.documentElement.clientHeight<.85:!1}function Ec(i,e){return(t,n)=>{try{return e.call(i,n,t)}catch(a){St(t.state,a)}}}function zH(i){let e=Object.create(null);function t(n){return e[n]||(e[n]={observers:[],handlers:[]})}for(let n of i){let a=n.spec,s=a&&a.plugin.domEventHandlers,r=a&&a.plugin.domEventObservers;if(s)for(let o in s){let l=s[o];l&&t(o).handlers.push(Ec(n.value,l))}if(r)for(let o in r){let l=r[o];l&&t(o).observers.push(Ec(n.value,l))}}for(let n in qt)t(n).handlers.push(qt[n]);for(let n in gt)t(n).observers.push(gt[n]);return e}const Kf=[{key:"Backspace",keyCode:8,inputType:"deleteContentBackward"},{key:"Enter",keyCode:13,inputType:"insertParagraph"},{key:"Enter",keyCode:13,inputType:"insertLineBreak"},{key:"Delete",keyCode:46,inputType:"deleteContentForward"}],IH="dthko",Xf=[16,17,18,20,91,92,224,225],Ja=6;function Ga(i){return Math.max(0,i)*.7+8}function kH(i,e){return Math.max(Math.abs(i.clientX-e.clientX),Math.abs(i.clientY-e.clientY))}class SH{constructor(e,t,n,a){this.view=e,this.startEvent=t,this.style=n,this.mustSelect=a,this.scrollSpeed={x:0,y:0},this.scrolling=-1,this.lastEvent=t,this.scrollParents=wf(e.contentDOM),this.atoms=e.state.facet(ga).map(r=>r(e));let s=e.contentDOM.ownerDocument;s.addEventListener("mousemove",this.move=this.move.bind(this)),s.addEventListener("mouseup",this.up=this.up.bind(this)),this.extend=t.shiftKey,this.multiple=e.state.facet(ce.allowMultipleSelections)&&TH(e,t),this.dragging=AH(e,t)&&n2(t)==1?null:!1}start(e){this.dragging===!1&&this.select(e)}move(e){if(e.buttons==0)return this.destroy();if(this.dragging||this.dragging==null&&kH(this.startEvent,e)<10)return;this.select(this.lastEvent=e);let t=0,n=0,a=0,s=0,r=this.view.win.innerWidth,o=this.view.win.innerHeight;this.scrollParents.x&&({left:a,right:r}=this.scrollParents.x.getBoundingClientRect()),this.scrollParents.y&&({top:s,bottom:o}=this.scrollParents.y.getBoundingClientRect());let l=pu(this.view);e.clientX-l.left<=a+Ja?t=-Ga(a-e.clientX):e.clientX+l.right>=r-Ja&&(t=Ga(e.clientX-r)),e.clientY-l.top<=s+Ja?n=-Ga(s-e.clientY):e.clientY+l.bottom>=o-Ja&&(n=Ga(e.clientY-o)),this.setScrollSpeed(t,n)}up(e){this.dragging==null&&this.select(this.lastEvent),this.dragging||e.preventDefault(),this.destroy()}destroy(){this.setScrollSpeed(0,0);let e=this.view.contentDOM.ownerDocument;e.removeEventListener("mousemove",this.move),e.removeEventListener("mouseup",this.up),this.view.inputState.mouseSelection=this.view.inputState.draggedContent=null}setScrollSpeed(e,t){this.scrollSpeed={x:e,y:t},e||t?this.scrolling<0&&(this.scrolling=setInterval(()=>this.scroll(),50)):this.scrolling>-1&&(clearInterval(this.scrolling),this.scrolling=-1)}scroll(){let{x:e,y:t}=this.scrollSpeed;e&&this.scrollParents.x&&(this.scrollParents.x.scrollLeft+=e,e=0),t&&this.scrollParents.y&&(this.scrollParents.y.scrollTop+=t,t=0),(e||t)&&this.view.win.scrollBy(e,t),this.dragging===!1&&this.select(this.lastEvent)}select(e){let{view:t}=this,n=Uf(this.atoms,this.style.get(e,this.extend,this.multiple));(this.mustSelect||!n.eq(t.state.selection,this.dragging===!1))&&this.view.dispatch({selection:n,userEvent:"select.pointer"}),this.mustSelect=!1}update(e){e.transactions.some(t=>t.isUserEvent("input.type"))?this.destroy():this.style.update(e)&&setTimeout(()=>this.select(this.lastEvent),20)}}function TH(i,e){let t=i.state.facet(Bf);return t.length?t[0](e):V.mac?e.metaKey:e.ctrlKey}function _H(i,e){let t=i.state.facet(Pf);return t.length?t[0](e):V.mac?!e.altKey:!e.ctrlKey}function AH(i,e){let{main:t}=i.state.selection;if(t.empty)return!1;let n=Yi(i.root);if(!n||n.rangeCount==0)return!0;let a=n.getRangeAt(0).getClientRects();for(let s=0;s=e.clientX&&r.top<=e.clientY&&r.bottom>=e.clientY)return!0}return!1}function jH(i,e){if(!e.bubbles)return!0;if(e.defaultPrevented)return!1;for(let t=e.target,n;t!=i.contentDOM;t=t.parentNode)if(!t||t.nodeType==11||(n=De.get(t))&&n.isWidget()&&!n.isHidden&&n.widget.ignoreEvent(e))return!1;return!0}const qt=Object.create(null),gt=Object.create(null),e2=V.ie&&V.ie_version<15||V.ios&&V.webkit_version<604;function CH(i){let e=i.dom.parentNode;if(!e)return;let t=e.appendChild(document.createElement("textarea"));t.style.cssText="position: fixed; left: -10000px; top: 10px",t.focus(),setTimeout(()=>{i.focus(),t.remove(),t2(i,t.value)},50)}function Zr(i,e,t){for(let n of i.facet(e))t=n(t,i);return t}function t2(i,e){e=Zr(i.state,cu,e);let{state:t}=i,n,a=1,s=t.toText(e),r=s.lines==t.selection.ranges.length;if(Yl!=null&&t.selection.ranges.every(l=>l.empty)&&Yl==s.toString()){let l=-1;n=t.changeByRange(u=>{let m=t.doc.lineAt(u.from);if(m.from==l)return{range:u};l=m.from;let c=t.toText((r?s.line(a++).text:e)+t.lineBreak);return{changes:{from:m.from,insert:c},range:O.cursor(u.from+c.length,-1)}})}else r?n=t.changeByRange(l=>{let u=s.line(a++);return{changes:{from:l.from,to:l.to,insert:u.text},range:O.cursor(l.from+u.length,-1)}}):n=t.replaceSelection(s);i.dispatch(n,{userEvent:"input.paste",scrollIntoView:!0})}gt.scroll=i=>{let e=i.inputState;e.lastScrollTop=i.scrollDOM.scrollTop,e.lastScrollLeft=i.scrollDOM.scrollLeft,V.ios&&!e.touchActive&&(e.lastIOSMomentumScroll=Date.now())};gt.wheel=gt.mousewheel=i=>{i.inputState.lastWheelEvent=Date.now()};qt.keydown=(i,e)=>(i.inputState.setSelectionOrigin("select"),e.keyCode==27&&i.inputState.tabFocusMode!=0&&(i.inputState.tabFocusMode=Date.now()+2e3),!1);gt.touchstart=(i,e)=>{let t=i.inputState,n=e.targetTouches[0];t.touchActive=!0,t.lastTouchTime=Date.now(),n&&(t.lastTouchX=n.clientX,t.lastTouchY=n.clientY),t.setSelectionOrigin("select.pointer")};gt.touchmove=i=>{i.inputState.setSelectionOrigin("select.pointer")};gt.touchend=(i,e)=>{i.inputState.touchActive=!1};qt.mousedown=(i,e)=>{if(i.observer.flush(),i.inputState.lastTouchTime>Date.now()-2e3)return!1;let t=null;for(let n of i.state.facet(Of))if(t=n(i,e),t)break;if(!t&&e.button==0&&(t=BH(i,e)),t){let n=!i.hasFocus;i.inputState.startMouseSelection(new SH(i,e,t,n)),n&&i.observer.ignore(()=>{If(i.contentDOM);let s=i.root.activeElement;s&&!s.contains(i.contentDOM)&&s.blur()});let a=i.inputState.mouseSelection;if(a)return a.start(e),a.dragging===!1}else i.inputState.setSelectionOrigin("select.pointer");return!1};function Nc(i,e,t,n){if(n==1)return O.cursor(e,t);if(n==2)return oH(i.state,e,t);{let a=i.docView.lineAt(e,t),s=i.state.doc.lineAt(a?a.posAtEnd:e),r=a?a.posAtStart:s.from,o=a?a.posAtEnd:s.to;return oDate.now()-400&&Math.abs(e.clientX-i.clientX)<2&&Math.abs(e.clientY-i.clientY)<2?(Rc+1)%3:1}function BH(i,e){let t=i.posAndSideAtCoords({x:e.clientX,y:e.clientY},!1),n=n2(e),a=i.state.selection;return{update(s){s.docChanged&&(t.pos=s.changes.mapPos(t.pos),a=a.map(s.changes))},get(s,r,o){let l=i.posAndSideAtCoords({x:s.clientX,y:s.clientY},!1),u,m=Nc(i,l.pos,l.assoc,n);if(t.pos!=l.pos&&!r){let c=Nc(i,t.pos,t.assoc,n),d=Math.min(c.from,m.from),h=Math.max(c.to,m.to);m=d1&&(u=PH(a,l.pos))?u:o?a.addRange(m):O.create([m])}}}function PH(i,e){for(let t=0;t=e)return O.create(i.ranges.slice(0,t).concat(i.ranges.slice(t+1)),i.mainIndex==t?0:i.mainIndex-(i.mainIndex>t?1:0))}return null}qt.dragstart=(i,e)=>{let{selection:{main:t}}=i.state;if(e.target.draggable){let a=i.docView.tile.nearest(e.target);if(a&&a.isWidget()){let s=a.posAtStart,r=s+a.length;(s>=t.to||r<=t.from)&&(t=O.undirectionalRange(s,r))}}let{inputState:n}=i;return n.mouseSelection&&(n.mouseSelection.dragging=!0),n.draggedContent=t,e.dataTransfer&&(e.dataTransfer.setData("Text",Zr(i.state,du,i.state.sliceDoc(t.from,t.to))),e.dataTransfer.effectAllowed="copyMove"),!1};qt.dragend=i=>(i.inputState.draggedContent=null,!1);function Gc(i,e,t,n){if(t=Zr(i.state,cu,t),!t)return;let a=i.posAtCoords({x:e.clientX,y:e.clientY},!1),{draggedContent:s}=i.inputState,r=n&&s&&_H(i,e)?{from:s.from,to:s.to}:null,o={from:a,insert:t},l=i.state.changes(r?[r,o]:o);i.focus(),i.dispatch({changes:l,selection:{anchor:l.mapPos(a,-1),head:l.mapPos(a,1)},userEvent:r?"move.drop":"input.drop"}),i.inputState.draggedContent=null}qt.drop=(i,e)=>{if(!e.dataTransfer)return!1;if(i.state.readOnly)return!0;let t=e.dataTransfer.files;if(t&&t.length){let n=Array(t.length),a=0,s=()=>{++a==t.length&&Gc(i,e,n.filter(r=>r!=null).join(i.state.lineBreak),!1)};for(let r=0;r{/[\x00-\x08\x0e-\x1f]{2}/.test(o.result)||(n[r]=o.result),s()},o.readAsText(t[r])}return!0}else{let n=e.dataTransfer.getData("Text");if(n)return Gc(i,e,n,!0),!0}return!1};qt.paste=(i,e)=>{if(i.state.readOnly)return!0;i.observer.flush();let t=e2?null:e.clipboardData;return t?(t2(i,t.getData("text/plain")||t.getData("text/uri-list")),!0):(CH(i),!1)};function OH(i,e){let t=i.dom.parentNode;if(!t)return;let n=t.appendChild(document.createElement("textarea"));n.style.cssText="position: fixed; left: -10000px; top: 10px",n.value=e,n.focus(),n.selectionEnd=e.length,n.selectionStart=0,setTimeout(()=>{n.remove(),i.focus()},50)}function MH(i){let e=[],t=[],n=!1;for(let a of i.selection.ranges)a.empty||(e.push(i.sliceDoc(a.from,a.to)),t.push(a));if(!e.length){let a=-1;for(let{from:s}of i.selection.ranges){let r=i.doc.lineAt(s);r.number>a&&(e.push(r.text),t.push({from:r.from,to:Math.min(i.doc.length,r.to+1)})),a=r.number}n=!0}return{text:Zr(i,du,e.join(i.lineBreak)),ranges:t,linewise:n}}let Yl=null;qt.copy=qt.cut=(i,e)=>{if(!Ci(i.contentDOM,i.observer.selectionRange))return!1;let{text:t,ranges:n,linewise:a}=MH(i.state);if(!t&&!a)return!1;Yl=a?t:null,e.type=="cut"&&!i.state.readOnly&&i.dispatch({changes:n,scrollIntoView:!0,userEvent:"delete.cut"});let s=e2?null:e.clipboardData;return s?(s.clearData(),s.setData("text/plain",t),!0):(OH(i,t),!1)};const i2=Vn.define();function a2(i,e){let t=[];for(let n of i.facet(Lf)){let a=n(i,e);a&&t.push(a)}return t.length?i.update({effects:t,annotations:i2.of(!0)}):null}function s2(i){setTimeout(()=>{let e=i.hasFocus;if(e!=i.inputState.notifiedFocused){let t=a2(i.state,e);t?i.dispatch(t):i.update([])}},10)}gt.focus=i=>{i.inputState.lastFocusTime=Date.now(),!i.scrollDOM.scrollTop&&(i.inputState.lastScrollTop||i.inputState.lastScrollLeft)&&(i.scrollDOM.scrollTop=i.inputState.lastScrollTop,i.scrollDOM.scrollLeft=i.inputState.lastScrollLeft),s2(i)};gt.blur=i=>{i.observer.clearSelectionRange(),s2(i)};gt.compositionstart=gt.compositionupdate=(i,e)=>{if(!i.observer.editContext&&(i.inputState.compositionFirstChange==null&&(i.inputState.compositionFirstChange=!0),i.inputState.composing<0)){let{main:t}=i.state.selection;!t.empty&&i.lineBlockAt(t.from).from!=i.lineBlockAt(t.to).from&&!i.state.readOnly&&i.dispatch({changes:i.state.selection.ranges.filter(n=>!n.empty).map(n=>({from:n.from,to:n.to})),userEvent:"input"}),i.inputState.composing=0}};gt.compositionend=i=>{i.observer.editContext||(i.inputState.composing=-1,i.inputState.compositionEndedAt=Date.now(),i.inputState.compositionPendingKey=!0,i.inputState.compositionPendingChange=i.observer.pendingRecords().length>0,i.inputState.compositionFirstChange=null,V.chrome&&V.android?i.observer.flushSoon():i.inputState.compositionPendingChange?Promise.resolve().then(()=>i.observer.flush()):setTimeout(()=>{i.inputState.composing<0&&i.docView.hasComposition&&i.update([])},50))};gt.contextmenu=i=>{i.inputState.lastContextMenu=Date.now()};qt.beforeinput=(i,e)=>{var t,n;if((e.inputType=="insertText"||e.inputType=="insertCompositionText")&&(i.inputState.insertingText=e.data,i.inputState.insertingTextAt=Date.now()),e.inputType=="insertReplacementText"&&i.observer.editContext){let s=(t=e.dataTransfer)===null||t===void 0?void 0:t.getData("text/plain"),r=e.getTargetRanges();if(s&&r.length){let o=r[0],l=i.posAtDOM(o.startContainer,o.startOffset),u=i.posAtDOM(o.endContainer,o.endOffset);return fu(i,{from:l,to:u,insert:i.state.toText(s)},null),!0}}let a;if(V.chrome&&V.android&&(a=Kf.find(s=>s.inputType==e.inputType))&&(i.observer.delayAndroidKey(a.key,a.keyCode),a.key=="Backspace"||a.key=="Delete")){let s=((n=window.visualViewport)===null||n===void 0?void 0:n.height)||0;setTimeout(()=>{var r;(((r=window.visualViewport)===null||r===void 0?void 0:r.height)||0)>s+10&&i.hasFocus&&(i.contentDOM.blur(),i.focus())},100)}return V.ios&&e.inputType=="deleteContentForward"&&i.observer.flushSoon(),V.safari&&e.inputType=="insertText"&&i.inputState.composing>=0&&setTimeout(()=>gt.compositionend(i,e),20),!1};const Hc=new Set;function ZH(i){Hc.has(i)||(Hc.add(i),i.addEventListener("copy",()=>{}),i.addEventListener("cut",()=>{}))}const Vc=["pre-wrap","normal","pre-line","break-spaces"];let Q1=!1;function qc(){Q1=!1}class LH{constructor(e){this.lineWrapping=e,this.doc=he.empty,this.heightSamples={},this.lineHeight=14,this.charWidth=7,this.textHeight=14,this.lineLength=30}heightForGap(e,t){let n=this.doc.lineAt(t).number-this.doc.lineAt(e).number+1;return this.lineWrapping&&(n+=Math.max(0,Math.ceil((t-e-n*this.lineLength*.5)/this.lineLength))),this.lineHeight*n}heightForLine(e){return this.lineWrapping?(1+Math.max(0,Math.ceil((e-this.lineLength)/Math.max(1,this.lineLength-5))))*this.lineHeight:this.lineHeight}setDoc(e){return this.doc=e,this}mustRefreshForWrapping(e){return Vc.indexOf(e)>-1!=this.lineWrapping}mustRefreshForHeights(e){let t=!1;for(let n=0;n-1,l=Math.abs(t-this.lineHeight)>.3||this.lineWrapping!=o;if(this.lineWrapping=o,this.lineHeight=t,this.charWidth=n,this.textHeight=a,this.lineLength=s,l){this.heightSamples={};for(let u=0;u0}set outdated(e){this.flags=(e?2:0)|this.flags&-3}setHeight(e){this.height!=e&&(Math.abs(this.height-e)>ys&&(Q1=!0),this.height=e)}replace(e,t,n){return ht.of(n)}decomposeLeft(e,t){t.push(this)}decomposeRight(e,t){t.push(this)}applyChanges(e,t,n,a){let s=this,r=n.doc;for(let o=a.length-1;o>=0;o--){let{fromA:l,toA:u,fromB:m,toB:c}=a[o],d=s.lineAt(l,ke.ByPosNoHeight,n.setDoc(t),0,0),h=d.to>=u?d:s.lineAt(u,ke.ByPosNoHeight,n,0,0);for(c+=h.to-u,u=h.to;o>0&&d.from<=a[o-1].toA;)l=a[o-1].fromA,m=a[o-1].fromB,o--,ls*2){let o=e[t-1];o.break?e.splice(--t,1,o.left,null,o.right):e.splice(--t,1,o.left,o.right),n+=1+o.break,a-=o.size}else if(s>a*2){let o=e[n];o.break?e.splice(n,1,o.left,null,o.right):e.splice(n,1,o.left,o.right),n+=2+o.break,s-=o.size}else break;else if(a=s&&r(this.lineAt(0,ke.ByPos,n,a,s))}setMeasuredHeight(e){let t=e.heights[e.index++];t<0?(this.spaceAbove=-t,t=e.heights[e.index++]):this.spaceAbove=0,this.setHeight(t)}updateHeight(e,t=0,n=!1,a){return a&&a.from<=t&&a.more&&this.setMeasuredHeight(a),this.outdated=!1,this}toString(){return`block(${this.length})`}}class It extends r2{constructor(e,t,n){super(e,t,null),this.collapsed=0,this.widgetHeight=0,this.breaks=0,this.spaceAbove=n}mainBlock(e,t){return new Gt(t,this.length,e+this.spaceAbove,this.height-this.spaceAbove,this.breaks)}replace(e,t,n){let a=n[0];return n.length==1&&(a instanceof It||a instanceof $e&&a.flags&4)&&Math.abs(this.length-a.length)<10?(a instanceof $e?a=new It(a.length,this.height,this.spaceAbove):a.height=this.height,this.outdated||(a.outdated=!1),a):ht.of(n)}updateHeight(e,t=0,n=!1,a){return a&&a.from<=t&&a.more?this.setMeasuredHeight(a):(n||this.outdated)&&(this.spaceAbove=0,this.setHeight(Math.max(this.widgetHeight,e.heightForLine(this.length-this.collapsed))+this.breaks*e.lineHeight)),this.outdated=!1,this}toString(){return`line(${this.length}${this.collapsed?-this.collapsed:""}${this.widgetHeight?":"+this.widgetHeight:""})`}}class $e extends ht{constructor(e){super(e,0)}heightMetrics(e,t){let n=e.doc.lineAt(t).number,a=e.doc.lineAt(t+this.length).number,s=a-n+1,r,o=0;if(e.lineWrapping){let l=Math.min(this.height,e.lineHeight*s);r=l/s,this.length>s+1&&(o=(this.height-l)/(this.length-s-1))}else r=this.height/s;return{firstLine:n,lastLine:a,perLine:r,perChar:o}}blockAt(e,t,n,a){let{firstLine:s,lastLine:r,perLine:o,perChar:l}=this.heightMetrics(t,a);if(t.lineWrapping){let u=a+(e0){let s=n[n.length-1];s instanceof $e?n[n.length-1]=new $e(s.length+a):n.push(null,new $e(a-1))}if(e>0){let s=n[0];s instanceof $e?n[0]=new $e(e+s.length):n.unshift(new $e(e-1),null)}return ht.of(n)}decomposeLeft(e,t){t.push(new $e(e-1),null)}decomposeRight(e,t){t.push(null,new $e(this.length-e-1))}updateHeight(e,t=0,n=!1,a){let s=t+this.length;if(a&&a.from<=t+this.length&&a.more){let r=[],o=Math.max(t,a.from),l=-1;for(a.from>t&&r.push(new $e(a.from-t-1).updateHeight(e,t));o<=s&&a.more;){let m=e.doc.lineAt(o).length;r.length&&r.push(null);let c=a.heights[a.index++],d=0;c<0&&(d=-c,c=a.heights[a.index++]),l==-1?l=c:Math.abs(c-l)>=ys&&(l=-2);let h=new It(m,c,d);h.outdated=!1,r.push(h),o+=m+1}o<=s&&r.push(null,new $e(s-o).updateHeight(e,o));let u=ht.of(r);return(l<0||Math.abs(u.height-this.height)>=ys||Math.abs(l-this.heightMetrics(e,t).perLine)>=ys)&&(Q1=!0),nr(this,u)}else(n||this.outdated)&&(this.setHeight(e.heightForGap(t,t+this.length)),this.outdated=!1);return this}toString(){return`gap(${this.length})`}}class NH extends ht{constructor(e,t,n){super(e.length+(t?1:0)+n.length,e.height+n.height,(t?1:0)|(e.outdated||n.outdated?2:0)),this.left=e,this.right=n,this.size=e.size+n.size}get break(){return this.flags&1}blockAt(e,t,n,a){let s=n+this.left.height;return eo))return u;let m=t==ke.ByPosNoHeight?ke.ByPosNoHeight:ke.ByPos;return l?u.join(this.right.lineAt(o,m,n,r,o)):this.left.lineAt(o,m,n,a,s).join(u)}forEachLine(e,t,n,a,s,r){let o=a+this.left.height,l=s+this.left.length+this.break;if(this.break)e=l&&this.right.forEachLine(e,t,n,o,l,r);else{let u=this.lineAt(l,ke.ByPos,n,a,s);e=e&&u.from<=t&&r(u),t>u.to&&this.right.forEachLine(Math.max(e,u.to+1),t,n,o,l,r)}}replace(e,t,n){let a=this.left.length+this.break;if(tthis.left.length)return this.balanced(this.left,this.right.replace(e-a,t-a,n));let s=[];e>0&&this.decomposeLeft(e,s);let r=s.length;for(let o of n)s.push(o);if(e>0&&Uc(s,r-1),t=n&&t.push(null)),e>n&&this.right.decomposeLeft(e-n,t)}decomposeRight(e,t){let n=this.left.length,a=n+this.break;if(e>=a)return this.right.decomposeRight(e-a,t);e2*t.size||t.size>2*e.size?ht.of(this.break?[e,null,t]:[e,t]):(this.left=nr(this.left,e),this.right=nr(this.right,t),this.setHeight(e.height+t.height),this.outdated=e.outdated||t.outdated,this.size=e.size+t.size,this.length=e.length+this.break+t.length,this)}updateHeight(e,t=0,n=!1,a){let{left:s,right:r}=this,o=t+s.length+this.break,l=null;return a&&a.from<=t+s.length&&a.more?l=s=s.updateHeight(e,t,n,a):s.updateHeight(e,t,n),a&&a.from<=o+r.length&&a.more?l=r=r.updateHeight(e,o,n,a):r.updateHeight(e,o,n),l?this.balanced(s,r):(this.height=this.left.height+this.right.height,this.outdated=!1,this)}toString(){return this.left+(this.break?" ":"-")+this.right}}function Uc(i,e){let t,n;i[e]==null&&(t=i[e-1])instanceof $e&&(n=i[e+1])instanceof $e&&i.splice(e-1,3,new $e(t.length+1+n.length))}const FH=5;class bu{constructor(e,t){this.pos=e,this.oracle=t,this.nodes=[],this.lineStart=-1,this.lineEnd=-1,this.covering=null,this.writtenTo=e}get isCovered(){return this.covering&&this.nodes[this.nodes.length-1]==this.covering}span(e,t){if(this.lineStart>-1){let n=Math.min(t,this.lineEnd),a=this.nodes[this.nodes.length-1];a instanceof It?a.length+=n-this.pos:(n>this.pos||!this.isCovered)&&this.nodes.push(new It(n-this.pos,-1,0)),this.writtenTo=n,t>n&&(this.nodes.push(null),this.writtenTo++,this.lineStart=-1)}this.pos=t}point(e,t,n){if(e=FH)&&this.addLineDeco(a,s,r)}else t>e&&this.span(e,t);this.lineEnd>-1&&this.lineEnd-1)return;let{from:e,to:t}=this.oracle.doc.lineAt(this.pos);this.lineStart=e,this.lineEnd=t,this.writtenToe&&this.nodes.push(new It(this.pos-e,-1,0)),this.writtenTo=this.pos}blankContent(e,t){let n=new $e(t-e);return this.oracle.doc.lineAt(e).to==t&&(n.flags|=4),n}ensureLine(){this.enterLine();let e=this.nodes.length?this.nodes[this.nodes.length-1]:null;if(e instanceof It)return e;let t=new It(0,-1,0);return this.nodes.push(t),t}addBlock(e){this.enterLine();let t=e.deco;t&&t.startSide>0&&!this.isCovered&&this.ensureLine(),this.nodes.push(e),this.writtenTo=this.pos=this.pos+e.length,t&&t.endSide>0&&(this.covering=e)}addLineDeco(e,t,n){let a=this.ensureLine();a.length+=n,a.collapsed+=n,a.widgetHeight=Math.max(a.widgetHeight,e),a.breaks+=t,this.writtenTo=this.pos=this.pos+n}finish(e){let t=this.nodes.length==0?null:this.nodes[this.nodes.length-1];this.lineStart>-1&&!(t instanceof It)&&!this.isCovered?this.nodes.push(new It(0,-1,0)):(this.writtenTou.clientHeight||u.scrollWidth>u.clientWidth)&&m.overflow!="visible"){let c=u.getBoundingClientRect();s=Math.max(s,c.left),r=Math.min(r,c.right),o=Math.max(o,c.top),l=Math.min(u==i.parentNode?a.innerHeight:l,c.bottom)}u=m.position=="absolute"||m.position=="fixed"?u.offsetParent:u.parentNode}else if(u.nodeType==11)u=u.host;else break;return{left:s-t.left,right:Math.max(s,r)-t.left,top:o-(t.top+e),bottom:Math.max(o,l)-(t.top+e)}}function HH(i){let e=i.getBoundingClientRect(),t=i.ownerDocument.defaultView||window;return e.left0&&e.top0}function VH(i,e){let t=i.getBoundingClientRect();return{left:0,right:t.right-t.left,top:e,bottom:t.bottom-(t.top+e)}}class bo{constructor(e,t,n,a){this.from=e,this.to=t,this.size=n,this.displaySize=a}static same(e,t){if(e.length!=t.length)return!1;for(let n=0;ntypeof a!="function"&&a.class=="cm-lineWrapping");this.heightOracle=new LH(n),this.stateDeco=$c(t),this.heightMap=ht.empty().applyChanges(this.stateDeco,he.empty,this.heightOracle.setDoc(t.doc),[new Zt(0,0,0,t.doc.length)]);for(let a=0;a<2&&(this.viewport=this.getViewport(0,null),!!this.updateForViewport());a++);this.updateViewportLines(),this.lineGaps=this.ensureLineGaps([]),this.lineGapDeco=fe.set(this.lineGaps.map(a=>a.draw(this,!1))),this.scrollParent=e.scrollDOM,this.computeVisibleRanges()}updateForViewport(){let e=[this.viewport],{main:t}=this.state.selection;for(let n=0;n<=1;n++){let a=n?t.head:t.anchor;if(!e.some(({from:s,to:r})=>a>=s&&a<=r)){let{from:s,to:r}=this.lineBlockAt(a);e.push(new Ha(s,r))}}return this.viewports=e.sort((n,a)=>n.from-a.from),this.updateScaler()}updateScaler(){let e=this.scaler;return this.scaler=this.heightMap.height<=7e6?Kl:ir.get(this.heightOracle,this.heightMap,this.viewports),e.eq(this.scaler)?0:2}updateViewportLines(){this.viewportLines=[],this.heightMap.forEachLine(this.viewport.from,this.viewport.to,this.heightOracle.setDoc(this.state.doc),0,0,e=>{this.viewportLines.push(vi(e,this.scaler))})}update(e,t=null){this.state=e.state;let n=this.stateDeco;this.stateDeco=$c(this.state);let a=e.changedRanges,s=Zt.extendWithRanges(a,RH(n,this.stateDeco,e?e.changes:Le.empty(this.state.doc.length))),r=this.heightMap.height,o=this.scrolledToBottom?null:this.scrollAnchorAt(this.scrollOffset);qc(),this.heightMap=this.heightMap.applyChanges(this.stateDeco,e.startState.doc,this.heightOracle.setDoc(this.state.doc),s),(this.heightMap.height!=r||Q1)&&(e.flags|=2),o?(this.scrollAnchorPos=e.changes.mapPos(o.from,-1),this.scrollAnchorHeight=o.top):(this.scrollAnchorPos=-1,this.scrollAnchorHeight=r);let l=s.length?this.mapViewport(this.viewport,e.changes):this.viewport;(t&&(t.range.headl.to)||!this.viewportIsAppropriate(l))&&(l=this.getViewport(0,t));let u=l.from!=this.viewport.from||l.to!=this.viewport.to;this.viewport=l,e.flags|=this.updateForViewport(),(u||!e.changes.empty||e.flags&2)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(this.mapLineGaps(this.lineGaps,e.changes))),e.flags|=this.computeVisibleRanges(e.changes),t&&(this.scrollTarget=t),!this.mustEnforceCursorAssoc&&(e.selectionSet||e.focusChanged)&&e.view.lineWrapping&&e.state.selection.main.empty&&e.state.selection.main.assoc&&!e.state.facet(WG)&&(this.mustEnforceCursorAssoc=!0)}measure(){let{view:e}=this,t=e.contentDOM,n=window.getComputedStyle(t),a=this.heightOracle,s=n.whiteSpace;this.defaultTextDirection=n.direction=="rtl"?ze.RTL:ze.LTR;let r=this.heightOracle.mustRefreshForWrapping(s)||this.mustMeasureContent==="refresh",o=t.getBoundingClientRect(),l=r||this.mustMeasureContent||this.contentDOMHeight!=o.height;this.contentDOMHeight=o.height,this.mustMeasureContent=!1;let u=0,m=0;if(o.width&&o.height){let{scaleX:I,scaleY:T}=vf(t,o);(I>.005&&Math.abs(this.scaleX-I)>.005||T>.005&&Math.abs(this.scaleY-T)>.005)&&(this.scaleX=I,this.scaleY=T,u|=16,r=l=!0)}let c=(parseInt(n.paddingTop)||0)*this.scaleY,d=(parseInt(n.paddingBottom)||0)*this.scaleY;(this.paddingTop!=c||this.paddingBottom!=d)&&(this.paddingTop=c,this.paddingBottom=d,u|=18),this.editorWidth!=e.scrollDOM.clientWidth&&(a.lineWrapping&&(l=!0),this.editorWidth=e.scrollDOM.clientWidth,u|=16);let h=wf(this.view.contentDOM,!1).y;h!=this.scrollParent&&(this.scrollParent=h,this.scrollAnchorHeight=-1,this.scrollOffset=0);let g=this.getScrollOffset();this.scrollOffset!=g&&(this.scrollAnchorHeight=-1,this.scrollOffset=g),this.scrolledToBottom=kf(this.scrollParent||e.win);let f=(this.printing?VH:GH)(t,this.paddingTop),b=f.top-this.pixelViewport.top,x=f.bottom-this.pixelViewport.bottom;this.pixelViewport=f;let v=this.pixelViewport.bottom>this.pixelViewport.top&&this.pixelViewport.right>this.pixelViewport.left;if(v!=this.inView&&(this.inView=v,v&&(l=!0)),!this.inView&&!this.scrollTarget&&!HH(e.dom))return 0;let w=o.width;if((this.contentDOMWidth!=w||this.editorHeight!=e.scrollDOM.clientHeight)&&(this.contentDOMWidth=o.width,this.editorHeight=e.scrollDOM.clientHeight,u|=16),l){let I=e.docView.measureVisibleLineHeights(this.viewport);if(a.mustRefreshForHeights(I)&&(r=!0),r||a.lineWrapping&&Math.abs(w-this.contentDOMWidth)>a.charWidth){let{lineHeight:T,charWidth:k,textHeight:C}=e.docView.measureTextSize();r=T>0&&a.refresh(s,T,k,C,Math.max(5,w/k),I),r&&(e.docView.minWidth=0,u|=16)}b>0&&x>0?m=Math.max(b,x):b<0&&x<0&&(m=Math.min(b,x)),qc();for(let T of this.viewports){let k=T.from==this.viewport.from?I:e.docView.measureVisibleLineHeights(T);this.heightMap=(r?ht.empty().applyChanges(this.stateDeco,he.empty,this.heightOracle,[new Zt(0,0,0,e.state.doc.length)]):this.heightMap).updateHeight(a,0,r,new WH(T.from,k))}Q1&&(u|=2)}let z=!this.viewportIsAppropriate(this.viewport,m)||this.scrollTarget&&(this.scrollTarget.range.headthis.viewport.to);return z&&(u&2&&(u|=this.updateScaler()),this.viewport=this.getViewport(m,this.scrollTarget),u|=this.updateForViewport()),(u&2||z)&&this.updateViewportLines(),(this.lineGaps.length||this.viewport.to-this.viewport.from>4e3)&&this.updateLineGaps(this.ensureLineGaps(r?[]:this.lineGaps,e)),u|=this.computeVisibleRanges(),this.mustEnforceCursorAssoc&&(this.mustEnforceCursorAssoc=!1,e.docView.enforceCursorAssoc()),u}get visibleTop(){return this.scaler.fromDOM(this.pixelViewport.top)}get visibleBottom(){return this.scaler.fromDOM(this.pixelViewport.bottom)}getViewport(e,t){let n=.5-Math.max(-.5,Math.min(.5,e/1e3/2)),a=this.heightMap,s=this.heightOracle,{visibleTop:r,visibleBottom:o}=this,l=new Ha(a.lineAt(r-n*1e3,ke.ByHeight,s,0,0).from,a.lineAt(o+(1-n)*1e3,ke.ByHeight,s,0,0).to);if(t){let{head:u}=t.range;if(ul.to){let m=Math.min(this.editorHeight,this.pixelViewport.bottom-this.pixelViewport.top),c=a.lineAt(u,ke.ByPos,s,0,0),d;t.y=="center"?d=(c.top+c.bottom)/2-m/2:t.y=="start"||t.y=="nearest"&&u=o+Math.max(10,Math.min(n,250)))&&a>r-2*1e3&&s>1,r=a<<1;if(this.defaultTextDirection!=ze.LTR&&!n)return[];let o=[],l=(m,c,d,h)=>{if(c-mm&&xx.from>=d.from&&x.to<=d.to&&Math.abs(x.from-m)x.fromv));if(!b){if(cw.from<=c&&w.to>=c)){let w=t.moveToLineBoundary(O.cursor(c),!1,!0).head;w>m&&(c=w)}let x=this.gapSize(d,m,c,h),v=n||x<2e6?x:2e6;b=new bo(m,c,x,v)}o.push(b)},u=m=>{if(m.length2e6)for(let T of e)T.from>=m.from&&T.fromm.from&&l(m.from,h,m,c),gt.draw(this,this.heightOracle.lineWrapping))))}computeVisibleRanges(e){let t=this.stateDeco;this.lineGaps.length&&(t=t.concat(this.lineGapDeco));let n=[];oe.spans(t,this.viewport.from,this.viewport.to,{span(s,r){n.push({from:s,to:r})},point(){}},20);let a=0;if(n.length!=this.visibleRanges.length)a=12;else for(let s=0;s=this.viewport.from&&e<=this.viewport.to&&this.viewportLines.find(t=>t.from<=e&&t.to>=e)||vi(this.heightMap.lineAt(e,ke.ByPos,this.heightOracle,0,0),this.scaler)}lineBlockAtHeight(e){return e>=this.viewportLines[0].top&&e<=this.viewportLines[this.viewportLines.length-1].bottom&&this.viewportLines.find(t=>t.top<=e&&t.bottom>=e)||vi(this.heightMap.lineAt(this.scaler.fromDOM(e),ke.ByHeight,this.heightOracle,0,0),this.scaler)}getScrollOffset(){return this.scrollParent==this.view.scrollDOM?this.scrollParent.scrollTop*this.scaleY:(this.scrollParent?this.scrollParent.getBoundingClientRect().top:0)-this.view.contentDOM.getBoundingClientRect().top}scrollAnchorAt(e){let t=this.lineBlockAtHeight(e+12-this.paddingTop);return t.from>=this.viewport.from||this.viewportLines[0].top-e>200?t:this.viewportLines[0]}elementAtHeight(e){return vi(this.heightMap.blockAt(this.scaler.fromDOM(e),this.heightOracle,0,0),this.scaler)}get docHeight(){return this.scaler.toDOM(this.heightMap.height)}get contentHeight(){return this.docHeight+this.paddingTop+this.paddingBottom}}class Ha{constructor(e,t){this.from=e,this.to=t}}function UH(i,e,t){let n=[],a=i,s=0;return oe.spans(t,i,e,{span(){},point(r,o){r>a&&(n.push({from:a,to:r}),s+=r-a),a=o}},20),a=1)return e[e.length-1].to;let n=Math.floor(i*t);for(let a=0;;a++){let{from:s,to:r}=e[a],o=r-s;if(n<=o)return s+n;n-=o}}function qa(i,e){let t=0;for(let{from:n,to:a}of i.ranges){if(e<=a){t+=e-n;break}t+=a-n}return t/i.total}function QH(i,e){for(let t of i)if(e(t))return t}const Kl={toDOM(i){return i},fromDOM(i){return i},scale:1,eq(i){return i==this}};function $c(i){let e=i.facet(Pr).filter(n=>typeof n!="function"),t=i.facet(gu).filter(n=>typeof n!="function");return t.length&&e.push(oe.join(t)),e}class ir{constructor(e,t){this.scale=e,this.viewports=t}static get(e,t,n){let a=0,s=0,r=0,o=n.map(({from:u,to:m})=>{let c=t.lineAt(u,ke.ByPos,e,0,0).top,d=t.lineAt(m,ke.ByPos,e,0,0).bottom;return a+=d-c,{from:u,to:m,top:c,bottom:d,domTop:0,domBottom:0}});if(t.height-a<10)return Kl;let l=(7e6-a)/(t.height-a);for(let u of o)u.domTop=r+(u.top-s)*l,r=u.domBottom=u.domTop+(u.bottom-u.top),s=u.bottom;return new ir(l,o)}toDOM(e){for(let t=0,n=0,a=0;;t++){let s=tt.from==e.viewports[n].from&&t.to==e.viewports[n].to):!1}}function vi(i,e){if(e.scale==1)return i;let t=e.toDOM(i.top),n=e.toDOM(i.bottom);return new Gt(i.from,i.length,t,n-t,Array.isArray(i._content)?i._content.map(a=>vi(a,e)):i._content)}const Ua=Q.define({combine:i=>i.join(" ")}),Xl=Q.define({combine:i=>i.indexOf(!0)>-1}),e0=Rn.newName(),o2=Rn.newName(),l2=Rn.newName(),u2={"&light":"."+o2,"&dark":"."+l2};function t0(i,e,t){return new Rn(e,{finish(n){return/&/.test(n)?n.replace(/&\w*/,a=>{if(a=="&")return i;if(!t||!t[a])throw new RangeError(`Unsupported selector: ${a}`);return t[a]}):i+" "+n}})}const $H=t0("."+e0,{"&":{position:"relative !important",boxSizing:"border-box","&.cm-focused":{outline:"1px dotted #212121"},display:"flex !important",flexDirection:"column"},".cm-scroller":{display:"flex !important",alignItems:"flex-start !important",fontFamily:"monospace",lineHeight:1.4,height:"100%",overflowX:"auto",position:"relative",zIndex:0,overflowAnchor:"none"},".cm-content":{margin:0,flexGrow:2,flexShrink:0,display:"block",whiteSpace:"pre",wordWrap:"normal",boxSizing:"border-box",minHeight:"100%",padding:"4px 0",outline:"none","&[contenteditable=true]":{WebkitUserModify:"read-write-plaintext-only"}},".cm-lineWrapping":{whiteSpace_fallback:"pre-wrap",whiteSpace:"break-spaces",wordBreak:"break-word",overflowWrap:"anywhere",flexShrink:1},"&light .cm-content":{caretColor:"black"},"&dark .cm-content":{caretColor:"white"},".cm-line":{display:"block",padding:"0 2px 0 6px"},".cm-layer":{userSelect:"none",position:"absolute",left:0,top:0,contain:"size style","& > *":{position:"absolute"}},"&light .cm-selectionBackground":{background:"#d9d9d9"},"&dark .cm-selectionBackground":{background:"#222"},"&light.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#d7d4f0"},"&dark.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground":{background:"#233"},".cm-cursorLayer":{pointerEvents:"none"},"&.cm-focused > .cm-scroller > .cm-cursorLayer":{animation:"steps(1) cm-blink 1.2s infinite"},"@keyframes cm-blink":{"0%":{},"50%":{opacity:0},"100%":{}},"@keyframes cm-blink2":{"0%":{},"50%":{opacity:0},"100%":{}},".cm-cursor, .cm-dropCursor":{borderLeft:"1.2px solid black",marginLeft:"-0.6px",pointerEvents:"none"},".cm-cursor":{display:"none"},"&dark .cm-cursor":{borderLeftColor:"#ddd"},".cm-selectionHandle":{backgroundColor:"currentColor",width:"1.5px"},".cm-selectionHandle-start::before, .cm-selectionHandle-end::before":{content:'""',backgroundColor:"inherit",borderRadius:"50%",width:"8px",height:"8px",position:"absolute",left:"-3.25px"},".cm-selectionHandle-start::before":{top:"-8px"},".cm-selectionHandle-end::before":{bottom:"-8px"},".cm-dropCursor":{position:"absolute"},"&.cm-focused > .cm-scroller > .cm-cursorLayer .cm-cursor":{display:"block"},".cm-iso":{unicodeBidi:"isolate"},".cm-announced":{position:"fixed",top:"-10000px"},"@media print":{".cm-announced":{display:"none"}},"&light .cm-activeLine":{backgroundColor:"#cceeff44"},"&dark .cm-activeLine":{backgroundColor:"#99eeff33"},"&light .cm-specialChar":{color:"red"},"&dark .cm-specialChar":{color:"#f78"},".cm-gutters":{flexShrink:0,display:"flex",height:"100%",boxSizing:"border-box",zIndex:200},".cm-gutters-before":{insetInlineStart:0},".cm-gutters-after":{insetInlineEnd:0},"&light .cm-gutters":{backgroundColor:"#f5f5f5",color:"#6c6c6c",border:"0px solid #ddd","&.cm-gutters-before":{borderRightWidth:"1px"},"&.cm-gutters-after":{borderLeftWidth:"1px"}},"&dark .cm-gutters":{backgroundColor:"#333338",color:"#ccc"},".cm-gutter":{display:"flex !important",flexDirection:"column",flexShrink:0,boxSizing:"border-box",minHeight:"100%",overflow:"hidden"},".cm-gutterElement":{boxSizing:"border-box"},".cm-lineNumbers .cm-gutterElement":{padding:"0 3px 0 5px",minWidth:"20px",textAlign:"right",whiteSpace:"nowrap"},"&light .cm-activeLineGutter":{backgroundColor:"#e2f2ff"},"&dark .cm-activeLineGutter":{backgroundColor:"#222227"},".cm-panels":{boxSizing:"border-box",position:"sticky",left:0,right:0,zIndex:300},"&light .cm-panels":{backgroundColor:"#f5f5f5",color:"black"},".cm-panels-top":{top:"0"},".cm-panels-bottom":{bottom:"0"},"&light .cm-panels-top":{borderBottom:"1px solid #ddd"},"&light .cm-panels-bottom":{borderTop:"1px solid #ddd"},"&dark .cm-panels":{backgroundColor:"#333338",color:"white"},".cm-dialog":{padding:"2px 19px 4px 6px",position:"relative","& label":{fontSize:"80%"}},".cm-dialog-close":{position:"absolute",top:"3px",right:"4px",backgroundColor:"inherit",border:"none",font:"inherit",fontSize:"14px",padding:"0"},".cm-tab":{display:"inline-block",overflow:"hidden",verticalAlign:"bottom"},".cm-widgetBuffer":{verticalAlign:"baseline",height:"1em",width:0,display:"inline"},".cm-placeholder":{color:"#888",display:"inline-block",verticalAlign:"top",userSelect:"none"},".cm-highlightSpace":{background:"radial-gradient(circle at 50% 55%, #aaa 20%, transparent 0) no-repeat",backgroundSize:".4em",backgroundPosition:"calc(min(50%, 0px)) center"},".cm-highlightTab":{backgroundImage:`url('data:image/svg+xml,')`,backgroundSize:"auto 100%",backgroundPosition:"right 90%",backgroundRepeat:"no-repeat"},".cm-trailingSpace":{backgroundColor:"#ff332255"},".cm-button":{verticalAlign:"middle",color:"inherit",fontSize:"70%",padding:".2em 1em",borderRadius:"1px"},"&light .cm-button":{backgroundImage:"linear-gradient(#eff1f5, #d9d9df)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#b4b4b4, #d0d3d6)"}},"&dark .cm-button":{backgroundImage:"linear-gradient(#393939, #111)",border:"1px solid #888","&:active":{backgroundImage:"linear-gradient(#111, #333)"}},".cm-textfield":{verticalAlign:"middle",color:"inherit",fontSize:"70%",border:"1px solid silver",padding:".2em .5em"},"&light .cm-textfield":{backgroundColor:"white"},"&dark .cm-textfield":{border:"1px solid #555",backgroundColor:"inherit"}},u2),YH={childList:!0,characterData:!0,subtree:!0,attributes:!0,characterDataOldValue:!0},xo=V.ie&&V.ie_version<=11;class KH{constructor(e){this.view=e,this.active=!1,this.editContext=null,this.selectionRange=new TG,this.selectionChanged=!1,this.delayedFlush=-1,this.resizeTimeout=-1,this.queue=[],this.delayedAndroidKey=null,this.flushingAndroidKey=-1,this.lastChange=0,this.scrollTargets=[],this.intersection=null,this.resizeScroll=null,this.intersecting=!1,this.gapIntersection=null,this.gaps=[],this.printQuery=null,this.parentCheck=-1,this.dom=e.contentDOM,this.observer=new MutationObserver(t=>{for(let n of t)this.queue.push(n);(V.ie&&V.ie_version<=11||V.ios&&e.composing)&&t.some(n=>n.type=="childList"&&n.removedNodes.length||n.type=="characterData"&&n.oldValue.length>n.target.nodeValue.length)?this.flushSoon():this.flush()}),window.EditContext&&V.android&&e.constructor.EDIT_CONTEXT!==!1&&!(V.chrome&&V.chrome_version<126)&&(this.editContext=new eV(e),e.state.facet(vn)&&(e.contentDOM.editContext=this.editContext.editContext)),xo&&(this.onCharData=t=>{this.queue.push({target:t.target,type:"characterData",oldValue:t.prevValue}),this.flushSoon()}),this.onSelectionChange=this.onSelectionChange.bind(this),this.onResize=this.onResize.bind(this),this.onPrint=this.onPrint.bind(this),this.onScroll=this.onScroll.bind(this),window.matchMedia&&(this.printQuery=window.matchMedia("print")),typeof ResizeObserver=="function"&&(this.resizeScroll=new ResizeObserver(()=>{var t;((t=this.view.docView)===null||t===void 0?void 0:t.lastUpdate){this.parentCheck<0&&(this.parentCheck=setTimeout(this.listenForScroll.bind(this),1e3)),t.length>0&&t[t.length-1].intersectionRatio>0!=this.intersecting&&(this.intersecting=!this.intersecting,this.intersecting!=this.view.inView&&this.onScrollChanged(document.createEvent("Event")))},{threshold:[0,.001]}),this.intersection.observe(this.dom),this.gapIntersection=new IntersectionObserver(t=>{t.length>0&&t[t.length-1].intersectionRatio>0&&this.onScrollChanged(document.createEvent("Event"))},{})),this.listenForScroll(),this.readSelectionRange()}onScrollChanged(e){this.view.inputState.runHandlers("scroll",e),this.intersecting&&this.view.measure()}onScroll(e){this.intersecting&&this.flush(this.pendingRecords().length>0),this.editContext&&this.view.requestMeasure(this.editContext.measureReq),this.onScrollChanged(e)}onResize(){this.resizeTimeout<0&&(this.resizeTimeout=setTimeout(()=>{this.resizeTimeout=-1,this.view.requestMeasure()},50))}onPrint(e){(e.type=="change"||!e.type)&&!e.matches||(this.view.viewState.printing=!0,this.view.measure(),setTimeout(()=>{this.view.viewState.printing=!1,this.view.requestMeasure()},500))}updateGaps(e){if(this.gapIntersection&&(e.length!=this.gaps.length||this.gaps.some((t,n)=>t!=e[n]))){this.gapIntersection.disconnect();for(let t of e)this.gapIntersection.observe(t);this.gaps=e}}onSelectionChange(e){let t=this.selectionChanged;if(!this.readSelectionRange()||this.delayedAndroidKey)return;let{view:n}=this,a=this.selectionRange;if(n.state.facet(vn)?n.root.activeElement!=this.dom:!Ci(this.dom,a))return;let s=a.anchorNode&&n.docView.tile.nearest(a.anchorNode);if(s&&s.isWidget()&&s.widget.ignoreEvent(e)){t||(this.selectionChanged=!1);return}(V.ie&&V.ie_version<=11||V.android&&V.chrome)&&!n.state.selection.main.empty&&a.focusNode&&Di(a.focusNode,a.focusOffset,a.anchorNode,a.anchorOffset)?this.flushSoon():this.flush(!1)}readSelectionRange(){let{view:e}=this,t=Yi(e.root);if(!t)return!1;let n=V.safari&&e.root.nodeType==11&&e.root.activeElement==this.dom&&XH(this.view,t)||t;if(!n||this.selectionRange.eq(n))return!1;let a=Ci(this.dom,n);return a&&!this.selectionChanged&&e.inputState.lastFocusTime>Date.now()-200&&e.inputState.lastTouchTime{let s=this.delayedAndroidKey;s&&(this.clearDelayedAndroidKey(),this.view.inputState.lastKeyCode=s.keyCode,this.view.inputState.lastKeyTime=Date.now(),!this.flush()&&s.force&&N1(this.dom,s.key,s.keyCode))};this.flushingAndroidKey=this.view.win.requestAnimationFrame(a)}(!this.delayedAndroidKey||e=="Enter")&&(this.delayedAndroidKey={key:e,keyCode:t,force:this.lastChange{this.delayedFlush=-1,this.flush()}))}forceFlush(){this.delayedFlush>=0&&(this.view.win.cancelAnimationFrame(this.delayedFlush),this.delayedFlush=-1),this.flush()}pendingRecords(){for(let e of this.observer.takeRecords())this.queue.push(e);return this.queue}processRecords(){let e=this.pendingRecords();e.length&&(this.queue=[]);let t=-1,n=-1,a=!1;for(let s of e){let r=this.readMutation(s);r&&(r.typeOver&&(a=!0),t==-1?{from:t,to:n}=r:(t=Math.min(r.from,t),n=Math.max(r.to,n)))}return{from:t,to:n,typeOver:a}}readChange(){let{from:e,to:t,typeOver:n}=this.processRecords(),a=this.selectionChanged&&Ci(this.dom,this.selectionRange);if(e<0&&!a)return null;e>-1&&(this.lastChange=Date.now()),this.view.inputState.lastFocusTime=0,this.selectionChanged=!1;let s=new fH(this.view,e,t,n);return this.view.docView.domChanged={newSel:s.newSel?s.newSel.main:null},s}flush(e=!0){if(this.delayedFlush>=0||this.delayedAndroidKey)return!1;e&&this.readSelectionRange();let t=this.selectionChanged,n=this.readChange();if(!n)return this.view.requestMeasure(),!1;let a=this.view.state,s=$f(this.view,n);return this.view.state==a&&(n.domChanged||t)&&this.view.update([]),s}readMutation(e){let t=this.view.docView.tile.nearest(e.target);if(!t||t.isWidget())return null;if(t.markDirty(e.type=="attributes"),e.type=="childList"){let n=Yc(t,e.previousSibling||e.target.previousSibling,-1),a=Yc(t,e.nextSibling||e.target.nextSibling,1);return{from:n?t.posAfter(n):t.posAtStart,to:a?t.posBefore(a):t.posAtEnd,typeOver:!1}}else return e.type=="characterData"?{from:t.posAtStart,to:t.posAtEnd,typeOver:e.target.nodeValue==e.oldValue}:null}setWindow(e){e!=this.win&&(this.removeWindowListeners(this.win),this.win=e,this.addWindowListeners(this.win))}addWindowListeners(e){e.addEventListener("resize",this.onResize),this.printQuery?this.printQuery.addEventListener?this.printQuery.addEventListener("change",this.onPrint):this.printQuery.addListener(this.onPrint):e.addEventListener("beforeprint",this.onPrint),e.addEventListener("scroll",this.onScroll),e.document.addEventListener("selectionchange",this.onSelectionChange)}removeWindowListeners(e){e.removeEventListener("scroll",this.onScroll),e.removeEventListener("resize",this.onResize),this.printQuery?this.printQuery.removeEventListener?this.printQuery.removeEventListener("change",this.onPrint):this.printQuery.removeListener(this.onPrint):e.removeEventListener("beforeprint",this.onPrint),e.document.removeEventListener("selectionchange",this.onSelectionChange)}update(e){this.editContext&&(this.editContext.update(e),e.startState.facet(vn)!=e.state.facet(vn)&&(e.view.contentDOM.editContext=e.state.facet(vn)?this.editContext.editContext:null))}destroy(){var e,t,n;this.stop(),(e=this.intersection)===null||e===void 0||e.disconnect(),(t=this.gapIntersection)===null||t===void 0||t.disconnect(),(n=this.resizeScroll)===null||n===void 0||n.disconnect();for(let a of this.scrollTargets)a.removeEventListener("scroll",this.onScroll);this.removeWindowListeners(this.win),clearTimeout(this.parentCheck),clearTimeout(this.resizeTimeout),this.win.cancelAnimationFrame(this.delayedFlush),this.win.cancelAnimationFrame(this.flushingAndroidKey),this.editContext&&(this.view.contentDOM.editContext=null,this.editContext.destroy())}}function Yc(i,e,t){for(;e;){let n=De.get(e);if(n&&n.parent==i)return n;let a=e.parentNode;e=a!=i.dom?a:t>0?e.nextSibling:e.previousSibling}return null}function Kc(i,e){let t=e.startContainer,n=e.startOffset,a=e.endContainer,s=e.endOffset,r=i.docView.domAtPos(i.state.selection.main.anchor,1);return Di(r.node,r.offset,a,s)&&([t,n,a,s]=[a,s,t,n]),{anchorNode:t,anchorOffset:n,focusNode:a,focusOffset:s}}function XH(i,e){if(e.getComposedRanges){let a=e.getComposedRanges(i.root)[0];if(a)return Kc(i,a)}let t=null;function n(a){a.preventDefault(),a.stopImmediatePropagation(),t=a.getTargetRanges()[0]}return i.contentDOM.addEventListener("beforeinput",n,!0),i.dom.ownerDocument.execCommand("indent"),i.contentDOM.removeEventListener("beforeinput",n,!0),t?Kc(i,t):null}class eV{constructor(e){this.from=0,this.to=0,this.pendingContextChange=null,this.handlers=Object.create(null),this.composing=null,this.resetRange(e.state);let t=this.editContext=new window.EditContext({text:e.state.doc.sliceString(this.from,this.to),selectionStart:this.toContextPos(Math.max(this.from,Math.min(this.to,e.state.selection.main.anchor))),selectionEnd:this.toContextPos(e.state.selection.main.head)});this.handlers.textupdate=n=>{let a=e.state.selection.main,{anchor:s,head:r}=a,o=this.toEditorPos(n.updateRangeStart),l=this.toEditorPos(n.updateRangeEnd);e.inputState.composing>=0&&!this.composing&&(this.composing={contextBase:n.updateRangeStart,editorBase:o,drifted:!1});let u=l-o>n.text.length;o==this.from&&sthis.to&&(l=s);let m=Yf(e.state.sliceDoc(o,l),n.text,(u?a.from:a.to)-o,u?"end":null);if(!m){let d=O.single(this.toEditorPos(n.selectionStart),this.toEditorPos(n.selectionEnd));$l(d,a)||e.dispatch({selection:d,userEvent:"select"});return}let c={from:m.from+o,to:m.toA+o,insert:he.of(n.text.slice(m.from,m.toB).split(` -`))};if((V.mac||V.android)&&c.from==r-1&&/^\. ?$/.test(n.text)&&e.contentDOM.getAttribute("autocorrect")=="off"&&(c={from:o,to:l,insert:he.of([n.text.replace("."," ")])}),this.pendingContextChange=c,!e.state.readOnly){let d=this.to-this.from+(c.to-c.from+c.insert.length);fu(e,c,O.single(this.toEditorPos(n.selectionStart,d),this.toEditorPos(n.selectionEnd,d)))}this.pendingContextChange&&(this.revertPending(e.state),this.setSelection(e.state)),c.from=0&&!/[\\p{Alphabetic}\\p{Number}_]/.test(t.text.slice(Math.max(0,n.updateRangeStart-1),Math.min(t.text.length,n.updateRangeStart+1)))&&this.handlers.compositionend(n)},this.handlers.characterboundsupdate=n=>{let a=[],s=null;for(let r=this.toEditorPos(n.rangeStart),o=this.toEditorPos(n.rangeEnd);r{let a=[];for(let s of n.getTextFormats()){let r=s.underlineStyle,o=s.underlineThickness;if(!/none/i.test(r)&&!/none/i.test(o)){let l=this.toEditorPos(s.rangeStart),u=this.toEditorPos(s.rangeEnd);if(l{e.inputState.composing<0&&(e.inputState.composing=0,e.inputState.compositionFirstChange=!0)},this.handlers.compositionend=()=>{if(e.inputState.composing=-1,e.inputState.compositionFirstChange=null,this.composing){let{drifted:n}=this.composing;this.composing=null,n&&this.reset(e.state)}};for(let n in this.handlers)t.addEventListener(n,this.handlers[n]);this.measureReq={read:n=>{let a=Yi(n.root);a&&a.rangeCount&&this.editContext.updateSelectionBounds(a.getRangeAt(0).getBoundingClientRect())}}}applyEdits(e){let t=0,n=!1,a=this.pendingContextChange;return e.changes.iterChanges((s,r,o,l,u)=>{if(n)return;let m=u.length-(r-s);if(a&&r>=a.to)if(a.from==s&&a.to==r&&a.insert.eq(u)){a=this.pendingContextChange=null,t+=m,this.to+=m;return}else a=null,this.revertPending(e.state);if(s+=t,r+=t,r<=this.from)this.from+=m,this.to+=m;else if(sthis.to||this.to-this.from+u.length>3e4){n=!0;return}this.editContext.updateText(this.toContextPos(s),this.toContextPos(r),u.toString()),this.to+=m}t+=m}),a&&!n&&this.revertPending(e.state),!n}update(e){let t=this.pendingContextChange,n=e.startState.selection.main;this.composing&&(this.composing.drifted||!e.changes.touchesRange(n.from,n.to)&&e.transactions.some(a=>!a.isUserEvent("input.type")&&a.changes.touchesRange(this.from,this.to)))?(this.composing.drifted=!0,this.composing.editorBase=e.changes.mapPos(this.composing.editorBase)):!this.applyEdits(e)||!this.rangeIsValid(e.state)?(this.pendingContextChange=null,this.reset(e.state)):(e.docChanged||e.selectionSet||t)&&this.setSelection(e.state),(e.geometryChanged||e.docChanged||e.selectionSet)&&e.view.requestMeasure(this.measureReq)}resetRange(e){let{head:t}=e.selection.main;this.from=Math.max(0,t-1e4),this.to=Math.min(e.doc.length,t+1e4)}reset(e){this.resetRange(e),this.editContext.updateText(0,this.editContext.text.length,e.doc.sliceString(this.from,this.to)),this.setSelection(e)}revertPending(e){let t=this.pendingContextChange;this.pendingContextChange=null,this.editContext.updateText(this.toContextPos(t.from),this.toContextPos(t.from+t.insert.length),e.doc.sliceString(t.from,t.to))}setSelection(e){let{main:t}=e.selection,n=this.toContextPos(Math.max(this.from,Math.min(this.to,t.anchor))),a=this.toContextPos(t.head);(this.editContext.selectionStart!=n||this.editContext.selectionEnd!=a)&&this.editContext.updateSelection(n,a)}rangeIsValid(e){let{head:t}=e.selection.main;return!(this.from>0&&t-this.from<500||this.to1e4*3)}toEditorPos(e,t=this.to-this.from){e=Math.min(e,t);let n=this.composing;return n&&n.drifted?n.editorBase+(e-n.contextBase):e+this.from}toContextPos(e){let t=this.composing;return t&&t.drifted?t.contextBase+(e-t.editorBase):e-this.from}destroy(){for(let e in this.handlers)this.editContext.removeEventListener(e,this.handlers[e])}}class ${get state(){return this.viewState.state}get viewport(){return this.viewState.viewport}get visibleRanges(){return this.viewState.visibleRanges}get inView(){return this.viewState.inView}get composing(){return!!this.inputState&&this.inputState.composing>0}get compositionStarted(){return!!this.inputState&&this.inputState.composing>=0}get root(){return this._root}get win(){return this.dom.ownerDocument.defaultView||window}constructor(e={}){var t;this.plugins=[],this.pluginMap=new Map,this.editorAttrs={},this.contentAttrs={},this.bidiCache=[],this.destroyed=!1,this.updateState=2,this.measureScheduled=-1,this.measureRequests=[],this.clearAnnouncement=-1,this.contentDOM=document.createElement("div"),this.scrollDOM=document.createElement("div"),this.scrollDOM.tabIndex=-1,this.scrollDOM.className="cm-scroller",this.scrollDOM.appendChild(this.contentDOM),this.announceDOM=document.createElement("div"),this.announceDOM.className="cm-announced",this.announceDOM.setAttribute("aria-live","polite"),this.dom=document.createElement("div"),this.dom.appendChild(this.announceDOM),this.dom.appendChild(this.scrollDOM),e.parent&&e.parent.appendChild(this.dom);let{dispatch:n}=e;this.dispatchTransactions=e.dispatchTransactions||n&&(a=>a.forEach(s=>n(s,this)))||(a=>this.update(a)),this.dispatch=this.dispatch.bind(this),this._root=e.root||_G(e.parent)||document,this.viewState=new Qc(this,e.state||ce.create(e)),e.scrollTo&&e.scrollTo.is(Ra)&&(this.viewState.scrollTarget=e.scrollTo.value.clip(this.viewState.state)),this.plugins=this.state.facet(P1).map(a=>new co(a));for(let a of this.plugins)a.update(this);this.observer=new KH(this),this.inputState=new wH(this),this.inputState.ensureHandlers(this.plugins),this.docView=new Zc(this),this.mountStyles(),this.updateAttrs(),this.updateState=0,this.requestMeasure(),!((t=document.fonts)===null||t===void 0)&&t.ready&&document.fonts.ready.then(()=>{this.viewState.mustMeasureContent="refresh",this.requestMeasure()})}dispatch(...e){let t=e.length==1&&e[0]instanceof Ke?e:e.length==1&&Array.isArray(e[0])?e[0]:[this.state.update(...e)];this.dispatchTransactions(t,this)}update(e){if(this.updateState!=0)throw new Error("Calls to EditorView.update are not allowed while an update is in progress");let t=!1,n=!1,a,s=this.state;for(let d of e){if(d.startState!=s)throw new RangeError("Trying to update state with a transaction that doesn't start from the previous state.");s=d.state}if(this.destroyed){this.viewState.state=s;return}let r=this.hasFocus,o=0,l=null;e.some(d=>d.annotation(i2))?(this.inputState.notifiedFocused=r,o=1):r!=this.inputState.notifiedFocused&&(this.inputState.notifiedFocused=r,l=a2(s,r),l||(o=1));let u=this.observer.delayedAndroidKey,m=null;if(u?(this.observer.clearDelayedAndroidKey(),m=this.observer.readChange(),(m&&!this.state.doc.eq(s.doc)||!this.state.selection.eq(s.selection))&&(m=null)):this.observer.clear(),s.facet(ce.phrases)!=this.state.facet(ce.phrases))return this.setState(s);a=Xs.create(this,s,e),a.flags|=o;let c=this.viewState.scrollTarget;try{this.updateState=2;for(let d of e){if(c&&(c=c.map(d.changes)),d.scrollIntoView){let{main:h}=d.state.selection,{x:g,y:f}=this.state.facet($.cursorScrollMargin);c=new F1(h.empty?h:O.cursor(h.head,h.head>h.anchor?-1:1),"nearest","nearest",f,g)}for(let h of d.effects)h.is(Ra)&&(c=h.value.clip(this.state))}this.viewState.update(a,c),this.bidiCache=ar.update(this.bidiCache,a.changes),a.empty||(this.updatePlugins(a),this.inputState.update(a)),t=this.docView.update(a),this.state.facet(xi)!=this.styleModules&&this.mountStyles(),n=this.updateAttrs(),this.showAnnouncements(e),this.docView.updateSelection(t,e.some(d=>d.isUserEvent("select.pointer")))}finally{this.updateState=0}if(a.startState.facet(Ua)!=a.state.facet(Ua)&&(this.viewState.mustMeasureContent=!0),(t||n||c||this.viewState.mustEnforceCursorAssoc||this.viewState.mustMeasureContent)&&this.requestMeasure(),t&&this.docViewUpdate(),!a.empty)for(let d of this.state.facet(Vl))try{d(a)}catch(h){St(this.state,h,"update listener")}(l||m)&&Promise.resolve().then(()=>{l&&this.state==l.startState&&this.dispatch(l),m&&!$f(this,m)&&u.force&&N1(this.contentDOM,u.key,u.keyCode)})}setState(e){if(this.updateState!=0)throw new Error("Calls to EditorView.setState are not allowed while an update is in progress");if(this.destroyed){this.viewState.state=e;return}this.updateState=2;let t=this.hasFocus;try{for(let n of this.plugins)n.destroy(this);this.viewState=new Qc(this,e),this.plugins=e.facet(P1).map(n=>new co(n)),this.pluginMap.clear();for(let n of this.plugins)n.update(this);this.docView.destroy(),this.docView=new Zc(this),this.inputState.ensureHandlers(this.plugins),this.mountStyles(),this.updateAttrs(),this.bidiCache=[]}finally{this.updateState=0}t&&this.focus(),this.requestMeasure()}updatePlugins(e){let t=e.startState.facet(P1),n=e.state.facet(P1);if(t!=n){let a=[];for(let s of n){let r=t.indexOf(s);if(r<0)a.push(new co(s));else{let o=this.plugins[r];o.mustUpdate=e,a.push(o)}}for(let s of this.plugins)s.mustUpdate!=e&&s.destroy(this);this.plugins=a,this.pluginMap.clear()}else for(let a of this.plugins)a.mustUpdate=e;for(let a=0;a-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.observer.delayedAndroidKey){this.measureScheduled=-1,this.requestMeasure();return}this.measureScheduled=0,e&&this.observer.forceFlush();let t=null,n=this.viewState.scrollParent,a=this.viewState.getScrollOffset(),{scrollAnchorPos:s,scrollAnchorHeight:r,scaleY:o}=this.viewState;Math.abs(a-this.viewState.scrollOffset)>1&&(r=-1),this.viewState.scrollAnchorHeight=-1;try{for(let l=0;;l++){if(r<0){if(kf(n||this.win))s=-1,r=this.viewState.heightMap.height/this.viewState.scaleY;else{let g=this.viewState.scrollAnchorAt(a);s=g.from,r=g.top}o=this.viewState.scaleY}this.updateState=1;let u=this.viewState.measure();if(!u&&!this.measureRequests.length&&this.viewState.scrollTarget==null)break;if(l>5){console.warn(this.measureRequests.length?"Measure loop restarted more than 5 times":"Viewport failed to stabilize");break}let m=[];u&4||([this.measureRequests,m]=[m,this.measureRequests]);let c=m.map(g=>{try{return g.read(this)}catch(f){return St(this.state,f),Xc}}),d=Xs.create(this,this.state,[]),h=!1;d.flags|=u,t?t.flags|=u:t=d,this.updateState=2,d.empty||(this.updatePlugins(d),this.inputState.update(d),this.updateAttrs(),h=this.docView.update(d),h&&this.docViewUpdate());for(let g=0;g1||f<-1)&&!(V.ios&&this.inputState.lastIOSMomentumScroll>Date.now()-100)&&(n==this.scrollDOM||this.hasFocus||Math.max(this.inputState.lastWheelEvent,this.inputState.lastTouchTime)>Date.now()-100)){a=a+f,n?s<0?n.scrollTop=n.scrollHeight:n.scrollTop+=f:this.win.scrollBy(0,f),r=-1;continue}}break}}}finally{this.updateState=0,this.measureScheduled=-1}if(t&&!t.empty)for(let l of this.state.facet(Vl))l(t)}get themeClasses(){return e0+" "+(this.state.facet(Xl)?l2:o2)+" "+this.state.facet(Ua)}updateAttrs(){let e=ed(this,Ff,{class:"cm-editor"+(this.hasFocus?" cm-focused ":" ")+this.themeClasses}),t={spellcheck:"false",autocorrect:"off",autocapitalize:"off",writingsuggestions:"false",translate:"no",contenteditable:this.state.facet(vn)?"true":"false",class:"cm-content",style:`${V.tabSize}: ${this.state.tabSize}`,role:"textbox","aria-multiline":"true"};this.state.readOnly&&(t["aria-readonly"]="true"),ed(this,hu,t);let n=this.observer.ignore(()=>{let a=Cc(this.contentDOM,this.contentAttrs,t),s=Cc(this.dom,this.editorAttrs,e);return a||s});return this.editorAttrs=e,this.contentAttrs=t,n}showAnnouncements(e){let t=!0;for(let n of e)for(let a of n.effects)if(a.is($.announce)){t&&(this.announceDOM.textContent="",this.win.clearTimeout(this.clearAnnouncement),this.clearAnnouncement=this.win.setTimeout(()=>{this.announceDOM.textContent=" "},200),t=!1);let s=this.announceDOM.appendChild(document.createElement("div"));s.textContent=a.value}}mountStyles(){this.styleModules=this.state.facet(xi);let e=this.state.facet($.cspNonce);Rn.mount(this.root,this.styleModules.concat($H).reverse(),e?{nonce:e}:void 0)}readMeasured(){if(this.updateState==2)throw new Error("Reading the editor layout isn't allowed during an update");this.updateState==0&&this.measureScheduled>-1&&this.measure(!1)}requestMeasure(e){if(this.measureScheduled<0&&(this.measureScheduled=this.win.requestAnimationFrame(()=>this.measure())),e){if(this.measureRequests.indexOf(e)>-1)return;if(e.key!=null){for(let t=0;tn.plugin==e)||null),t&&t.update(this).value}get documentTop(){return this.contentDOM.getBoundingClientRect().top+this.viewState.paddingTop}get documentPadding(){return{top:this.viewState.paddingTop,bottom:this.viewState.paddingBottom}}get scaleX(){return this.viewState.scaleX}get scaleY(){return this.viewState.scaleY}elementAtHeight(e){return this.readMeasured(),this.viewState.elementAtHeight(e)}lineBlockAtHeight(e){return this.readMeasured(),this.viewState.lineBlockAtHeight(e)}get viewportLineBlocks(){return this.viewState.viewportLines}lineBlockAt(e){return this.viewState.lineBlockAt(e)}get contentHeight(){return this.viewState.contentHeight}moveByChar(e,t,n){return fo(this,e,Lc(this,e,t,n))}moveByGroup(e,t){return fo(this,e,Lc(this,e,t,n=>mH(this,e.head,n)))}visualLineSide(e,t){return t?O.cursor(e.to,1):O.cursor(e.from,-1)}moveToLineBoundary(e,t,n=!0){return uH(this,e,t,n)}moveVertically(e,t,n){return fo(this,e,cH(this,e,t,n))}domAtPos(e,t=1){return this.docView.domAtPos(e,t)}posAtDOM(e,t=0){return this.docView.posFromDOM(e,t)}posAtCoords(e,t=!0){this.readMeasured();let n=Ql(this,e,t);return n&&n.pos}posAndSideAtCoords(e,t=!0){return this.readMeasured(),Ql(this,e,t)}coordsAtPos(e,t=1){this.readMeasured();let n=this.state.doc.lineAt(e),a=this.bidiSpans(n),s=a[ln.find(a,e-n.from,-1,t)];return n.length&&(e==n.from&&t<0||e==n.to&&t>0)&&s.dir!=this.textDirectionAt(n.from)&&(e==n.to?(e=n.from+s.from,t=1):(e=n.from+s.to,t=-1)),this.docView.coordsAt(e,t,s.dir==ze.RTL)}coordsForChar(e){return this.readMeasured(),this.docView.coordsForChar(e)}get defaultCharacterWidth(){return this.viewState.heightOracle.charWidth}get defaultLineHeight(){return this.viewState.heightOracle.lineHeight}get textDirection(){return this.viewState.defaultTextDirection}textDirectionAt(e){return!this.state.facet(Wf)||ethis.viewport.to?this.textDirection:(this.readMeasured(),this.docView.textDirectionAt(e))}get lineWrapping(){return this.viewState.heightOracle.lineWrapping}bidiSpans(e){if(e.length>tV)return Cf(e.length);let t=this.textDirectionAt(e.from),n;for(let s of this.bidiCache)if(s.from==e.from&&s.dir==t&&(s.fresh||jf(s.isolates,n=Pc(this,e))))return s.order;n||(n=Pc(this,e));let a=MG(e.text,t,n);return this.bidiCache.push(new ar(e.from,e.to,t,n,!0,a)),a}get hasFocus(){var e;return(this.dom.ownerDocument.hasFocus()||V.safari&&((e=this.inputState)===null||e===void 0?void 0:e.lastContextMenu)>Date.now()-3e4)&&this.root.activeElement==this.contentDOM}focus(){this.observer.ignore(()=>{If(this.contentDOM),this.docView.updateSelection()})}setRoot(e){this._root!=e&&(this._root=e,this.observer.setWindow((e.nodeType==9?e:e.ownerDocument).defaultView||window),this.mountStyles())}destroy(){this.root.activeElement==this.contentDOM&&this.contentDOM.blur();for(let e of this.plugins)e.destroy(this);this.plugins=[],this.inputState.destroy(),this.docView.destroy(),this.dom.remove(),this.observer.destroy(),this.win.clearTimeout(this.clearAnnouncement),this.measureScheduled>-1&&this.win.cancelAnimationFrame(this.measureScheduled),this.destroyed=!0}static scrollIntoView(e,t={}){var n,a,s,r;return Ra.of(new F1(typeof e=="number"?O.cursor(e):e,(n=t.y)!==null&&n!==void 0?n:"nearest",(a=t.x)!==null&&a!==void 0?a:"nearest",(s=t.yMargin)!==null&&s!==void 0?s:5,(r=t.xMargin)!==null&&r!==void 0?r:5))}scrollSnapshot(){let{scrollTop:e,scrollLeft:t}=this.scrollDOM,n=this.viewState.scrollAnchorAt(e);return Ra.of(new F1(O.cursor(n.from),"start","start",n.top-e,t,!0))}setTabFocusMode(e){e==null?this.inputState.tabFocusMode=this.inputState.tabFocusMode<0?0:-1:typeof e=="boolean"?this.inputState.tabFocusMode=e?0:-1:this.inputState.tabFocusMode!=0&&(this.inputState.tabFocusMode=Date.now()+e)}static domEventHandlers(e){return Et.define(()=>({}),{eventHandlers:e})}static domEventObservers(e){return Et.define(()=>({}),{eventObservers:e})}static theme(e,t){let n=Rn.newName(),a=[Ua.of(n),xi.of(t0(`.${n}`,e))];return t&&t.dark&&a.push(Xl.of(!0)),a}static baseTheme(e){return f1.lowest(xi.of(t0("."+e0,e,u2)))}static findFromDOM(e){var t;let n=e.querySelector(".cm-content"),a=n&&De.get(n)||De.get(e);return((t=a==null?void 0:a.root)===null||t===void 0?void 0:t.view)||null}}$.styleModule=xi;$.inputHandler=Zf;$.clipboardInputFilter=cu;$.clipboardOutputFilter=du;$.scrollHandler=Ef;$.focusChangeEffect=Lf;$.perLineTextDirection=Wf;$.exceptionSink=Mf;$.updateListener=Vl;$.editable=vn;$.mouseSelectionStyle=Of;$.dragMovesSelection=Pf;$.clickAddsSelectionRange=Bf;$.decorations=Pr;$.blockWrappers=Rf;$.outerDecorations=gu;$.atomicRanges=ga;$.bidiIsolatedRanges=Jf;$.cursorScrollMargin=Q.define({combine:i=>{let e=5,t=5;for(let n of i)typeof n=="number"?e=t=n:{x:e,y:t}=n;return{x:e,y:t}}});$.scrollMargins=Hf;$.darkTheme=Xl;$.cspNonce=Q.define({combine:i=>i.length?i[0]:""});$.contentAttributes=hu;$.editorAttributes=Ff;$.lineWrapping=$.contentAttributes.of({class:"cm-lineWrapping"});$.announce=pe.define();const tV=4096,Xc={};class ar{constructor(e,t,n,a,s,r){this.from=e,this.to=t,this.dir=n,this.isolates=a,this.fresh=s,this.order=r}static update(e,t){if(t.empty&&!e.some(s=>s.fresh))return e;let n=[],a=e.length?e[e.length-1].dir:ze.LTR;for(let s=Math.max(0,e.length-10);s=0;a--){let s=n[a],r=typeof s=="function"?s(i):s;r&&lu(r,t)}return t}const nV=V.mac?"mac":V.windows?"win":V.linux?"linux":"key";function iV(i,e){const t=i.split(/-(?!$)/);let n=t[t.length-1];n=="Space"&&(n=" ");let a,s,r,o;for(let l=0;ln.concat(a),[]))),t}let On=null;const rV=4e3;function oV(i,e=nV){let t=Object.create(null),n=Object.create(null),a=(r,o)=>{let l=n[r];if(l==null)n[r]=o;else if(l!=o)throw new Error("Key binding "+r+" is used both as a regular binding and as a multi-stroke prefix")},s=(r,o,l,u,m)=>{var c,d;let h=t[r]||(t[r]=Object.create(null)),g=o.split(/ (?!$)/).map(x=>iV(x,e));for(let x=1;x{let z=On={view:w,prefix:v,scope:r};return setTimeout(()=>{On==z&&(On=null)},rV),!0}]})}let f=g.join(" ");a(f,!1);let b=h[f]||(h[f]={preventDefault:!1,stopPropagation:!1,run:((d=(c=h._any)===null||c===void 0?void 0:c.run)===null||d===void 0?void 0:d.slice())||[]});l&&b.run.push(l),u&&(b.preventDefault=!0),m&&(b.stopPropagation=!0)};for(let r of i){let o=r.scope?r.scope.split(" "):["editor"];if(r.any)for(let u of o){let m=t[u]||(t[u]=Object.create(null));m._any||(m._any={preventDefault:!1,stopPropagation:!1,run:[]});let{any:c}=r;for(let d in m)m[d].run.push(h=>c(h,n0))}let l=r[e]||r.key;if(l)for(let u of o)s(u,l,r.run,r.preventDefault,r.stopPropagation),r.shift&&s(u,"Shift-"+l,r.shift,r.preventDefault,r.stopPropagation)}return t}let n0=null;function lV(i,e,t,n){n0=e;let a=wG(e),s=Pt(a,0),r=xn(s)==a.length&&a!=" ",o="",l=!1,u=!1,m=!1;On&&On.view==t&&On.scope==n&&(o=On.prefix+" ",Xf.indexOf(e.keyCode)<0&&(u=!0,On=null));let c=new Set,d=b=>{if(b){for(let x of b.run)if(!c.has(x)&&(c.add(x),x(t)))return b.stopPropagation&&(m=!0),!0;b.preventDefault&&(b.stopPropagation&&(m=!0),u=!0)}return!1},h=i[n],g,f;return h&&(d(h[o+Qa(a,e,!r)])?l=!0:r&&(e.altKey||e.metaKey||e.ctrlKey)&&!(V.windows&&e.ctrlKey&&e.altKey)&&!(V.mac&&e.altKey&&!(e.ctrlKey||e.metaKey))&&(g=Jn[e.keyCode])&&g!=a?(d(h[o+Qa(g,e,!0)])||e.shiftKey&&(f=Qi[e.keyCode])!=a&&f!=g&&d(h[o+Qa(f,e,!1)]))&&(l=!0):r&&e.shiftKey&&d(h[o+Qa(a,e,!0)])&&(l=!0),!l&&d(h._any)&&(l=!0)),u&&(l=!0),l&&m&&e.stopPropagation(),n0=null,l}class Lr{constructor(e,t,n,a,s){this.className=e,this.left=t,this.top=n,this.width=a,this.height=s}draw(){let e=document.createElement("div");return e.className=this.className,this.adjust(e),e}update(e,t){return t.className!=this.className?!1:(this.adjust(e),!0)}adjust(e){e.style.left=this.left+"px",e.style.top=this.top+"px",this.width!=null&&(e.style.width=this.width+"px"),e.style.height=this.height+"px"}eq(e){return this.left==e.left&&this.top==e.top&&this.width==e.width&&this.height==e.height&&this.className==e.className}static forRange(e,t,n){if(n.empty){let a=e.coordsAtPos(n.head,n.assoc||1);if(!a)return[];let s=m2(e);return[new Lr(t,a.left-s.left,a.top-s.top,null,a.bottom-a.top)]}else return uV(e,t,n)}}function m2(i){let e=i.scrollDOM.getBoundingClientRect();return{left:(i.textDirection==ze.LTR?e.left:e.right-i.scrollDOM.clientWidth*i.scaleX)-i.scrollDOM.scrollLeft*i.scaleX,top:e.top-i.scrollDOM.scrollTop*i.scaleY}}function nd(i,e,t,n){let a=i.coordsAtPos(e,t*2);if(!a)return n;let s=i.dom.getBoundingClientRect(),r=(a.top+a.bottom)/2,o=i.posAtCoords({x:s.left+1,y:r}),l=i.posAtCoords({x:s.right-1,y:r});return o==null||l==null?n:{from:Math.max(n.from,Math.min(o,l)),to:Math.min(n.to,Math.max(o,l))}}function uV(i,e,t){if(t.to<=i.viewport.from||t.from>=i.viewport.to)return[];let n=Math.max(t.from,i.viewport.from),a=Math.min(t.to,i.viewport.to),s=i.textDirection==ze.LTR,r=i.contentDOM,o=r.getBoundingClientRect(),l=m2(i),u=r.querySelector(".cm-line"),m=u&&window.getComputedStyle(u),c=o.left+(m?parseInt(m.paddingLeft)+Math.min(0,parseInt(m.textIndent)):0),d=o.right-(m?parseInt(m.paddingRight):0),h=Ul(i,n,1),g=Ul(i,a,-1),f=h.type==Xe.Text?h:null,b=g.type==Xe.Text?g:null;if(f&&(i.lineWrapping||h.widgetLineBreaks)&&(f=nd(i,n,1,f)),b&&(i.lineWrapping||g.widgetLineBreaks)&&(b=nd(i,a,-1,b)),f&&b&&f.from==b.from&&f.to==b.to)return v(w(t.from,t.to,f));{let I=f?w(t.from,null,f):z(h,!1),T=b?w(null,t.to,b):z(g,!0),k=[];return(f||h).to<(b||g).from-(f&&b?1:0)||h.widgetLineBreaks>1&&I.bottom+i.defaultLineHeight/2j&&F.from=ne)break;N>U&&B(Math.max(ue,U),I==null&&ue<=j,Math.min(N,ne),T==null&&N>=Z,ie.dir)}if(U=ge.to+1,U>=ne)break}return D.length==0&&B(j,I==null,Z,T==null,i.textDirection),{top:C,bottom:P,horizontal:D}}function z(I,T){let k=o.top+(T?I.top:I.bottom);return{top:k,bottom:k,horizontal:[]}}}function mV(i,e){return i.constructor==e.constructor&&i.eq(e)}class cV{constructor(e,t){this.view=e,this.layer=t,this.drawn=[],this.scaleX=1,this.scaleY=1,this.measureReq={read:this.measure.bind(this),write:this.draw.bind(this)},this.dom=e.scrollDOM.appendChild(document.createElement("div")),this.dom.classList.add("cm-layer"),t.above&&this.dom.classList.add("cm-layer-above"),t.class&&this.dom.classList.add(t.class),this.scale(),this.dom.setAttribute("aria-hidden","true"),this.setOrder(e.state),e.requestMeasure(this.measureReq),t.mount&&t.mount(this.dom,e)}update(e){e.startState.facet(zs)!=e.state.facet(zs)&&this.setOrder(e.state),(this.layer.update(e,this.dom)||e.geometryChanged)&&(this.scale(),e.view.requestMeasure(this.measureReq))}docViewUpdate(e){this.layer.updateOnDocViewUpdate!==!1&&e.requestMeasure(this.measureReq)}setOrder(e){let t=0,n=e.facet(zs);for(;t!mV(t,this.drawn[n]))){let t=this.dom.firstChild,n=0;for(let a of e)a.update&&t&&a.constructor&&this.drawn[n].constructor&&a.update(t,this.drawn[n])?(t=t.nextSibling,n++):this.dom.insertBefore(a.draw(),t);for(;t;){let a=t.nextSibling;t.remove(),t=a}this.drawn=e,V.webkit&&(this.dom.style.display=this.dom.firstChild?"":"none")}}destroy(){this.layer.destroy&&this.layer.destroy(this.dom,this.view),this.dom.remove()}}const zs=Q.define();function dV(i){return[Et.define(e=>new cV(e,i)),zs.of(i)]}const $a="-10000px";class hV{constructor(e,t,n,a){this.facet=t,this.createTooltipView=n,this.removeTooltipView=a,this.input=e.state.facet(t),this.tooltips=this.input.filter(r=>r);let s=null;this.tooltipViews=this.tooltips.map(r=>s=n(r,s))}update(e,t){var n;let a=e.state.facet(this.facet),s=a.filter(l=>l);if(a===this.input){for(let l of this.tooltipViews)l.update&&l.update(e);return!1}let r=[],o=t?[]:null;for(let l=0;lt[u]=l),t.length=o.length),this.input=a,this.tooltips=s,this.tooltipViews=r,!0}}function c2(i){let e=i.dom.ownerDocument.documentElement,t=e.clientWidth,n=getComputedStyle(e);if(!/stable/.test(n.scrollbarGutter||""))return{left:0,right:t};let a=e.getBoundingClientRect(),s=i.win.scrollX;return{left:Math.max(0,a.left+s-parseFloat(n.marginLeft)),right:Math.min(t,a.right+s+parseFloat(n.marginRight))}}function gV(i){let{left:e,right:t}=c2(i);return{top:0,left:e,right:t,bottom:i.dom.ownerDocument.documentElement.clientHeight}}const vo=Q.define({combine:i=>{var e,t,n;return{position:V.ios?"absolute":((e=i.find(a=>a.position))===null||e===void 0?void 0:e.position)||"fixed",parent:((t=i.find(a=>a.parent))===null||t===void 0?void 0:t.parent)||null,tooltipSpace:((n=i.find(a=>a.tooltipSpace))===null||n===void 0?void 0:n.tooltipSpace)||gV}}}),id=new WeakMap,d2=Et.fromClass(class{constructor(i){this.view=i,this.above=[],this.inView=!0,this.madeAbsolute=!1,this.lastTransaction=0,this.measureTimeout=-1;let e=i.state.facet(vo);this.position=e.position,this.parent=e.parent,this.classes=i.themeClasses,this.createContainer(),this.measureReq={read:this.readMeasure.bind(this),write:this.writeMeasure.bind(this),key:this},this.resizeObserver=typeof ResizeObserver=="function"?new ResizeObserver(()=>this.measureSoon()):null,this.manager=new hV(i,h2,(t,n)=>this.createTooltip(t,n),t=>{this.resizeObserver&&this.resizeObserver.unobserve(t.dom),t.dom.remove()}),this.above=this.manager.tooltips.map(t=>!!t.above),this.intersectionObserver=typeof IntersectionObserver=="function"?new IntersectionObserver(t=>{Date.now()>this.lastTransaction-50&&t.length>0&&t[t.length-1].intersectionRatio<1&&this.measureSoon()},{threshold:[1]}):null,this.observeIntersection(),i.win.addEventListener("resize",this.measureSoon=this.measureSoon.bind(this)),this.maybeMeasure()}createContainer(){this.parent?(this.container=document.createElement("div"),this.container.style.position="relative",this.container.className=this.view.themeClasses,this.parent.appendChild(this.container)):this.container=this.view.dom}observeIntersection(){if(this.intersectionObserver){this.intersectionObserver.disconnect();for(let i of this.manager.tooltipViews)this.intersectionObserver.observe(i.dom)}}measureSoon(){this.measureTimeout<0&&(this.measureTimeout=setTimeout(()=>{this.measureTimeout=-1,this.maybeMeasure()},50))}update(i){i.transactions.length&&(this.lastTransaction=Date.now());let e=this.manager.update(i,this.above);e&&this.observeIntersection();let t=e||i.geometryChanged,n=i.state.facet(vo);if(n.position!=this.position&&!this.madeAbsolute){this.position=n.position;for(let a of this.manager.tooltipViews)a.dom.style.position=this.position;t=!0}if(n.parent!=this.parent){this.parent&&this.container.remove(),this.parent=n.parent,this.createContainer();for(let a of this.manager.tooltipViews)this.container.appendChild(a.dom);t=!0}else this.parent&&this.view.themeClasses!=this.classes&&(this.classes=this.container.className=this.view.themeClasses);t&&this.maybeMeasure()}createTooltip(i,e){let t=i.create(this.view),n=e?e.dom:null;if(t.dom.classList.add("cm-tooltip"),i.arrow&&!t.dom.querySelector(".cm-tooltip > .cm-tooltip-arrow")){let a=document.createElement("div");a.className="cm-tooltip-arrow",t.dom.appendChild(a)}return t.dom.style.position=this.position,t.dom.style.top=$a,t.dom.style.left="0px",this.container.insertBefore(t.dom,n),t.mount&&t.mount(this.view),this.resizeObserver&&this.resizeObserver.observe(t.dom),t}destroy(){var i,e,t;this.view.win.removeEventListener("resize",this.measureSoon);for(let n of this.manager.tooltipViews)n.dom.remove(),(i=n.destroy)===null||i===void 0||i.call(n);this.parent&&this.container.remove(),(e=this.resizeObserver)===null||e===void 0||e.disconnect(),(t=this.intersectionObserver)===null||t===void 0||t.disconnect(),clearTimeout(this.measureTimeout)}readMeasure(){let i=1,e=1,t=!1,n=0;if(this.position=="fixed"&&this.manager.tooltipViews.length){let{dom:r}=this.manager.tooltipViews[0];if(V.safari){let o=r.getBoundingClientRect();t=Math.abs(o.top+1e4)>1||Math.abs(o.left)>1}else t=!!r.offsetParent&&r.offsetParent!=this.container.ownerDocument.body}if(t||this.position=="absolute")if(this.parent){let r=this.parent.getBoundingClientRect();r.width&&r.height&&(i=r.width/this.parent.offsetWidth,e=r.height/this.parent.offsetHeight)}else({scaleX:i,scaleY:e}=this.view.viewState);else n=c2(this.view).left;let a=this.view.scrollDOM.getBoundingClientRect(),s=pu(this.view);return{visible:{left:a.left+s.left,top:a.top+s.top,right:a.right-s.right,bottom:a.bottom-s.bottom},parent:this.parent?this.container.getBoundingClientRect():this.view.dom.getBoundingClientRect(),pos:this.manager.tooltips.map((r,o)=>{let l=this.manager.tooltipViews[o];return l.getCoords?l.getCoords(r.pos):this.view.coordsAtPos(r.pos)}),size:this.manager.tooltipViews.map(({dom:r})=>r.getBoundingClientRect()),space:this.view.state.facet(vo).tooltipSpace(this.view),scaleX:i,scaleY:e,makeAbsolute:t,fixedLeft:n}}writeMeasure(i){var e;if(i.makeAbsolute){this.madeAbsolute=!0,this.position="absolute";for(let o of this.manager.tooltipViews)o.dom.style.position="absolute"}let{visible:t,space:n,scaleX:a,scaleY:s}=i,r=[];for(let o=0;o=Math.min(t.bottom,n.bottom)||c.rightMath.min(t.right,n.right)+.1)){m.style.top=$a;continue}let h=l.arrow?u.dom.querySelector(".cm-tooltip-arrow"):null,g=h?7:0,f=d.right-d.left,b=(e=id.get(u))!==null&&e!==void 0?e:d.bottom-d.top,x=u.offset||fV,v=this.view.textDirection==ze.LTR,w=d.width>n.right-n.left?v?n.left:n.right-d.width:v?Math.max(n.left,Math.min(c.left-(h?14:0)+x.x,n.right-f)):Math.min(Math.max(n.left,c.left-f+(h?14:0)-x.x),n.right-f),z=this.above[o];!l.strictSide&&(z?c.top-b-g-x.yn.bottom)&&z==n.bottom-c.bottom>c.top-n.top&&(z=this.above[o]=!z);let I=(z?c.top-n.top:n.bottom-c.bottom)-g;if(Iw&&C.topT&&(T=z?C.top-b-2-g:C.bottom+g+2);if(this.position=="absolute"?(m.style.top=(T-i.parent.top)/s+"px",ad(m,(w-i.parent.left)/a)):(m.style.top=T/s+"px",ad(m,(w-i.fixedLeft)/a)),h){let C=c.left+(v?x.x:-x.x)-(w+14-7);h.style.left=C/a+"px"}u.overlap!==!0&&r.push({left:w,top:T,right:k,bottom:T+b}),m.classList.toggle("cm-tooltip-above",z),m.classList.toggle("cm-tooltip-below",!z),u.positioned&&u.positioned(i.space)}}maybeMeasure(){if(this.manager.tooltips.length&&(this.view.inView&&this.view.requestMeasure(this.measureReq),this.inView!=this.view.inView&&(this.inView=this.view.inView,!this.inView)))for(let i of this.manager.tooltipViews)i.dom.style.top=$a}},{eventObservers:{scroll(){this.maybeMeasure()}}});function ad(i,e){let t=parseInt(i.style.left,10);(isNaN(t)||Math.abs(e-t)>1)&&(i.style.left=e+"px")}const pV=$.baseTheme({".cm-tooltip":{zIndex:500,boxSizing:"border-box"},"&light .cm-tooltip":{border:"1px solid #bbb",backgroundColor:"#f5f5f5"},"&light .cm-tooltip-section:not(:first-child)":{borderTop:"1px solid #bbb"},"&dark .cm-tooltip":{backgroundColor:"#333338",color:"white"},".cm-tooltip-arrow":{height:"7px",width:"14px",position:"absolute",zIndex:-1,overflow:"hidden","&:before, &:after":{content:"''",position:"absolute",width:0,height:0,borderLeft:"7px solid transparent",borderRight:"7px solid transparent"},".cm-tooltip-above &":{bottom:"-7px","&:before":{borderTop:"7px solid #bbb"},"&:after":{borderTop:"7px solid #f5f5f5",bottom:"1px"}},".cm-tooltip-below &":{top:"-7px","&:before":{borderBottom:"7px solid #bbb"},"&:after":{borderBottom:"7px solid #f5f5f5",top:"1px"}}},"&dark .cm-tooltip .cm-tooltip-arrow":{"&:before":{borderTopColor:"#333338",borderBottomColor:"#333338"},"&:after":{borderTopColor:"transparent",borderBottomColor:"transparent"}}}),fV={x:0,y:0},h2=Q.define({enables:[d2,pV]});function g2(i,e){let t=i.plugin(d2);if(!t)return null;let n=t.manager.tooltips.indexOf(e);return n<0?null:t.manager.tooltipViews[n]}class cn extends Fn{compare(e){return this==e||this.constructor==e.constructor&&this.eq(e)}eq(e){return!1}destroy(e){}}cn.prototype.elementClass="";cn.prototype.toDOM=void 0;cn.prototype.mapMode=lt.TrackBefore;cn.prototype.startSide=cn.prototype.endSide=-1;cn.prototype.point=!0;const Pi=Q.define(),bV=Q.define(),xV={class:"",renderEmptyElements:!1,elementStyle:"",markers:()=>oe.empty,lineMarker:()=>null,widgetMarker:()=>null,lineMarkerChange:null,initialSpacer:null,updateSpacer:null,domEventHandlers:{},side:"before"},Oi=Q.define();function vV(i){return[p2(),Oi.of({...xV,...i})]}const sd=Q.define({combine:i=>i.some(e=>e)});function p2(i){return[wV]}const wV=Et.fromClass(class{constructor(i){this.view=i,this.domAfter=null,this.prevViewport=i.viewport,this.dom=document.createElement("div"),this.dom.className="cm-gutters cm-gutters-before",this.dom.setAttribute("aria-hidden","true"),this.dom.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.gutters=i.state.facet(Oi).map(e=>new od(i,e)),this.fixed=!i.state.facet(sd);for(let e of this.gutters)e.config.side=="after"?this.getDOMAfter().appendChild(e.dom):this.dom.appendChild(e.dom);this.fixed&&(this.dom.style.position="sticky"),this.syncGutters(!1),i.scrollDOM.insertBefore(this.dom,i.contentDOM)}getDOMAfter(){return this.domAfter||(this.domAfter=document.createElement("div"),this.domAfter.className="cm-gutters cm-gutters-after",this.domAfter.setAttribute("aria-hidden","true"),this.domAfter.style.minHeight=this.view.contentHeight/this.view.scaleY+"px",this.domAfter.style.position=this.fixed?"sticky":"",this.view.scrollDOM.appendChild(this.domAfter)),this.domAfter}update(i){if(this.updateGutters(i)){let e=this.prevViewport,t=i.view.viewport,n=Math.min(e.to,t.to)-Math.max(e.from,t.from);this.syncGutters(n<(t.to-t.from)*.8)}if(i.geometryChanged){let e=this.view.contentHeight/this.view.scaleY+"px";this.dom.style.minHeight=e,this.domAfter&&(this.domAfter.style.minHeight=e)}this.view.state.facet(sd)!=!this.fixed&&(this.fixed=!this.fixed,this.dom.style.position=this.fixed?"sticky":"",this.domAfter&&(this.domAfter.style.position=this.fixed?"sticky":"")),this.prevViewport=i.view.viewport}syncGutters(i){let e=this.dom.nextSibling;i&&(this.dom.remove(),this.domAfter&&this.domAfter.remove());let t=oe.iter(this.view.state.facet(Pi),this.view.viewport.from),n=[],a=this.gutters.map(s=>new yV(s,this.view.viewport,-this.view.documentPadding.top));for(let s of this.view.viewportLineBlocks)if(n.length&&(n=[]),Array.isArray(s.type)){let r=!0;for(let o of s.type)if(o.type==Xe.Text&&r){i0(t,n,o.from);for(let l of a)l.line(this.view,o,n);r=!1}else if(o.widget)for(let l of a)l.widget(this.view,o)}else if(s.type==Xe.Text){i0(t,n,s.from);for(let r of a)r.line(this.view,s,n)}else if(s.widget)for(let r of a)r.widget(this.view,s);for(let s of a)s.finish();i&&(this.view.scrollDOM.insertBefore(this.dom,e),this.domAfter&&this.view.scrollDOM.appendChild(this.domAfter))}updateGutters(i){let e=i.startState.facet(Oi),t=i.state.facet(Oi),n=i.docChanged||i.heightChanged||i.viewportChanged||!oe.eq(i.startState.facet(Pi),i.state.facet(Pi),i.view.viewport.from,i.view.viewport.to);if(e==t)for(let a of this.gutters)a.update(i)&&(n=!0);else{n=!0;let a=[];for(let s of t){let r=e.indexOf(s);r<0?a.push(new od(this.view,s)):(this.gutters[r].update(i),a.push(this.gutters[r]))}for(let s of this.gutters)s.dom.remove(),a.indexOf(s)<0&&s.destroy();for(let s of a)s.config.side=="after"?this.getDOMAfter().appendChild(s.dom):this.dom.appendChild(s.dom);this.gutters=a}return n}destroy(){for(let i of this.gutters)i.destroy();this.dom.remove(),this.domAfter&&this.domAfter.remove()}},{provide:i=>$.scrollMargins.of(e=>{let t=e.plugin(i);if(!t||t.gutters.length==0||!t.fixed)return null;let n=t.dom.offsetWidth*e.scaleX,a=t.domAfter?t.domAfter.offsetWidth*e.scaleX:0;return e.textDirection==ze.LTR?{left:n,right:a}:{right:n,left:a}})});function rd(i){return Array.isArray(i)?i:[i]}function i0(i,e,t){for(;i.value&&i.from<=t;)i.from==t&&e.push(i.value),i.next()}class yV{constructor(e,t,n){this.gutter=e,this.height=n,this.i=0,this.cursor=oe.iter(e.markers,t.from)}addElement(e,t,n){let{gutter:a}=this,s=(t.top-this.height)/e.scaleY,r=t.height/e.scaleY;if(this.i==a.elements.length){let o=new f2(e,r,s,n);a.elements.push(o),a.dom.appendChild(o.dom)}else a.elements[this.i].update(e,r,s,n);this.height=t.bottom,this.i++}line(e,t,n){let a=[];i0(this.cursor,a,t.from),n.length&&(a=a.concat(n));let s=this.gutter.config.lineMarker(e,t,a);s&&a.unshift(s);let r=this.gutter;a.length==0&&!r.config.renderEmptyElements||this.addElement(e,t,a)}widget(e,t){let n=this.gutter.config.widgetMarker(e,t.widget,t),a=n?[n]:null;for(let s of e.state.facet(bV)){let r=s(e,t.widget,t);r&&(a||(a=[])).push(r)}a&&this.addElement(e,t,a)}finish(){let e=this.gutter;for(;e.elements.length>this.i;){let t=e.elements.pop();e.dom.removeChild(t.dom),t.destroy()}}}class od{constructor(e,t){this.view=e,this.config=t,this.elements=[],this.spacer=null,this.dom=document.createElement("div"),this.dom.className="cm-gutter"+(this.config.class?" "+this.config.class:"");for(let n in t.domEventHandlers)this.dom.addEventListener(n,a=>{let s=a.target,r;if(s!=this.dom&&this.dom.contains(s)){for(;s.parentNode!=this.dom;)s=s.parentNode;let l=s.getBoundingClientRect();r=(l.top+l.bottom)/2}else r=a.clientY;let o=e.lineBlockAtHeight(r-e.documentTop);t.domEventHandlers[n](e,o,a)&&a.preventDefault()});this.markers=rd(t.markers(e)),t.initialSpacer&&(this.spacer=new f2(e,0,0,[t.initialSpacer(e)]),this.dom.appendChild(this.spacer.dom),this.spacer.dom.style.cssText+="visibility: hidden; pointer-events: none")}update(e){let t=this.markers;if(this.markers=rd(this.config.markers(e.view)),this.spacer&&this.config.updateSpacer){let a=this.config.updateSpacer(this.spacer.markers[0],e);a!=this.spacer.markers[0]&&this.spacer.update(e.view,0,0,[a])}let n=e.view.viewport;return!oe.eq(this.markers,t,n.from,n.to)||(this.config.lineMarkerChange?this.config.lineMarkerChange(e):!1)}destroy(){for(let e of this.elements)e.destroy()}}class f2{constructor(e,t,n,a){this.height=-1,this.above=0,this.markers=[],this.dom=document.createElement("div"),this.dom.className="cm-gutterElement",this.update(e,t,n,a)}update(e,t,n,a){this.height!=t&&(this.height=t,this.dom.style.height=t+"px"),this.above!=n&&(this.dom.style.marginTop=(this.above=n)?n+"px":""),zV(this.markers,a)||this.setMarkers(e,a)}setMarkers(e,t){let n="cm-gutterElement",a=this.dom.firstChild;for(let s=0,r=0;;){let o=r,l=ss(o,l,u)||r(o,l,u):r}return n}})}});class wo extends cn{constructor(e){super(),this.number=e}eq(e){return this.number==e.number}toDOM(){return document.createTextNode(this.number)}}function yo(i,e){return i.state.facet(O1).formatNumber(e,i.state)}const SV=Oi.compute([O1],i=>({class:"cm-lineNumbers",renderEmptyElements:!1,markers(e){return e.state.facet(IV)},lineMarker(e,t,n){return n.some(a=>a.toDOM)?null:new wo(yo(e,e.state.doc.lineAt(t.from).number))},widgetMarker:(e,t,n)=>{for(let a of e.state.facet(kV)){let s=a(e,t,n);if(s)return s}return null},lineMarkerChange:e=>e.startState.facet(O1)!=e.state.facet(O1),initialSpacer(e){return new wo(yo(e,ld(e.state.doc.lines)))},updateSpacer(e,t){let n=yo(t.view,ld(t.view.state.doc.lines));return n==e.number?e:new wo(n)},domEventHandlers:i.facet(O1).domEventHandlers,side:"before"}));function TV(i={}){return[O1.of(i),p2(),SV]}function ld(i){let e=9;for(;e{let e=[],t=-1;for(let n of i.selection.ranges){let a=i.doc.lineAt(n.head).from;a>t&&(t=a,e.push(_V.range(a)))}return oe.of(e)});function jV(){return AV}const b2=1024;let CV=0;class zo{constructor(e,t){this.from=e,this.to=t}}class le{constructor(e={}){this.id=CV++,this.perNode=!!e.perNode,this.deserialize=e.deserialize||(()=>{throw new Error("This node type doesn't define a deserialize function")}),this.combine=e.combine||null}add(e){if(this.perNode)throw new RangeError("Can't add per-node props to node types");return typeof e!="function"&&(e=wt.match(e)),t=>{let n=e(t);return n===void 0?null:[this,n]}}}le.closedBy=new le({deserialize:i=>i.split(" ")});le.openedBy=new le({deserialize:i=>i.split(" ")});le.group=new le({deserialize:i=>i.split(" ")});le.isolate=new le({deserialize:i=>{if(i&&i!="rtl"&&i!="ltr"&&i!="auto")throw new RangeError("Invalid value for isolate: "+i);return i||"auto"}});le.contextHash=new le({perNode:!0});le.lookAhead=new le({perNode:!0});le.mounted=new le({perNode:!0});class Mi{constructor(e,t,n,a=!1){this.tree=e,this.overlay=t,this.parser=n,this.bracketed=a}static get(e){return e&&e.props&&e.props[le.mounted.id]}}const DV=Object.create(null);class wt{constructor(e,t,n,a=0){this.name=e,this.props=t,this.id=n,this.flags=a}static define(e){let t=e.props&&e.props.length?Object.create(null):DV,n=(e.top?1:0)|(e.skipped?2:0)|(e.error?4:0)|(e.name==null?8:0),a=new wt(e.name||"",t,e.id,n);if(e.props){for(let s of e.props)if(Array.isArray(s)||(s=s(a)),s){if(s[0].perNode)throw new RangeError("Can't store a per-node prop on a node type");t[s[0].id]=s[1]}}return a}prop(e){return this.props[e.id]}get isTop(){return(this.flags&1)>0}get isSkipped(){return(this.flags&2)>0}get isError(){return(this.flags&4)>0}get isAnonymous(){return(this.flags&8)>0}is(e){if(typeof e=="string"){if(this.name==e)return!0;let t=this.prop(le.group);return t?t.indexOf(e)>-1:!1}return this.id==e}static match(e){let t=Object.create(null);for(let n in e)for(let a of n.split(" "))t[a]=e[n];return n=>{for(let a=n.prop(le.group),s=-1;s<(a?a.length:0);s++){let r=t[s<0?n.name:a[s]];if(r)return r}}}}wt.none=new wt("",Object.create(null),0,8);class xu{constructor(e){this.types=e;for(let t=0;t0;for(let l=this.cursor(r|Ce.IncludeAnonymous);;){let u=!1;if(l.from<=s&&l.to>=a&&(!o&&l.type.isAnonymous||t(l)!==!1)){if(l.firstChild())continue;u=!0}for(;u&&n&&(o||!l.type.isAnonymous)&&n(l),!l.nextSibling();){if(!l.parent())return;u=!0}}}prop(e){return e.perNode?this.props?this.props[e.id]:void 0:this.type.prop(e)}get propValues(){let e=[];if(this.props)for(let t in this.props)e.push([+t,this.props[t]]);return e}balance(e={}){return this.children.length<=8?this:yu(wt.none,this.children,this.positions,0,this.children.length,0,this.length,(t,n,a)=>new Ee(this.type,t,n,a,this.propValues),e.makeTree||((t,n,a)=>new Ee(wt.none,t,n,a)))}static build(e){return MV(e)}}Ee.empty=new Ee(wt.none,[],[],0);class vu{constructor(e,t){this.buffer=e,this.index=t}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}get pos(){return this.index}next(){this.index-=4}fork(){return new vu(this.buffer,this.index)}}class Hn{constructor(e,t,n){this.buffer=e,this.length=t,this.set=n}get type(){return wt.none}toString(){let e=[];for(let t=0;t0));l=r[l+3]);return o}slice(e,t,n){let a=this.buffer,s=new Uint16Array(t-e),r=0;for(let o=e,l=0;o=e&&te;case 1:return t<=e&&n>e;case 2:return n>e;case 4:return!0}}function ea(i,e,t,n){for(var a;i.from==i.to||(t<1?i.from>=e:i.from>e)||(t>-1?i.to<=e:i.to0?o.length:-1;e!=u;e+=t){let m=o[e],c=l[e]+r.from,d;if(!(!(s&Ce.EnterBracketed&&m instanceof Ee&&(d=Mi.get(m))&&!d.overlay&&d.bracketed&&n>=c&&n<=c+m.length)&&!x2(a,n,c,c+m.length))){if(m instanceof Hn){if(s&Ce.ExcludeBuffers)continue;let h=m.findChild(0,m.buffer.length,t,n-c,a);if(h>-1)return new Ln(new BV(r,m,e,c),null,h)}else if(s&Ce.IncludeAnonymous||!m.type.isAnonymous||wu(m)){let h;if(!(s&Ce.IgnoreMounts)&&(h=Mi.get(m))&&!h.overlay)return new Wt(h.tree,c,e,r);let g=new Wt(m,c,e,r);return s&Ce.IncludeAnonymous||!g.type.isAnonymous?g:g.nextChild(t<0?m.children.length-1:0,t,n,a,s)}}}if(s&Ce.IncludeAnonymous||!r.type.isAnonymous||(r.index>=0?e=r.index+t:e=t<0?-1:r._parent._tree.children.length,r=r._parent,!r))return null}}get firstChild(){return this.nextChild(0,1,0,4)}get lastChild(){return this.nextChild(this._tree.children.length-1,-1,0,4)}childAfter(e){return this.nextChild(0,1,e,2)}childBefore(e){return this.nextChild(this._tree.children.length-1,-1,e,-2)}prop(e){return this._tree.prop(e)}enter(e,t,n=0){let a;if(!(n&Ce.IgnoreOverlays)&&(a=Mi.get(this._tree))&&a.overlay){let s=e-this.from,r=n&Ce.EnterBracketed&&a.bracketed;for(let{from:o,to:l}of a.overlay)if((t>0||r?o<=s:o=s:l>s))return new Wt(a.tree,a.overlay[0].from+this.from,-1,this)}return this.nextChild(0,1,e,t,n)}nextSignificantParent(){let e=this;for(;e.type.isAnonymous&&e._parent;)e=e._parent;return e}get parent(){return this._parent?this._parent.nextSignificantParent():null}get nextSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index+1,1,0,4):null}get prevSibling(){return this._parent&&this.index>=0?this._parent.nextChild(this.index-1,-1,0,4):null}get tree(){return this._tree}toTree(){return this._tree}toString(){return this._tree.toString()}}function md(i,e,t,n){let a=i.cursor(),s=[];if(!a.firstChild())return s;if(t!=null){for(let r=!1;!r;)if(r=a.type.is(t),!a.nextSibling())return s}for(;;){if(n!=null&&a.type.is(n))return s;if(a.type.is(e)&&s.push(a.node),!a.nextSibling())return n==null?s:[]}}function a0(i,e,t=e.length-1){for(let n=i;t>=0;n=n.parent){if(!n)return!1;if(!n.type.isAnonymous){if(e[t]&&e[t]!=n.name)return!1;t--}}return!0}class BV{constructor(e,t,n,a){this.parent=e,this.buffer=t,this.index=n,this.start=a}}class Ln extends v2{get name(){return this.type.name}get from(){return this.context.start+this.context.buffer.buffer[this.index+1]}get to(){return this.context.start+this.context.buffer.buffer[this.index+2]}constructor(e,t,n){super(),this.context=e,this._parent=t,this.index=n,this.type=e.buffer.set.types[e.buffer.buffer[n]]}child(e,t,n){let{buffer:a}=this.context,s=a.findChild(this.index+4,a.buffer[this.index+3],e,t-this.context.start,n);return s<0?null:new Ln(this.context,this,s)}get firstChild(){return this.child(1,0,4)}get lastChild(){return this.child(-1,0,4)}childAfter(e){return this.child(1,e,2)}childBefore(e){return this.child(-1,e,-2)}prop(e){return this.type.prop(e)}enter(e,t,n=0){if(n&Ce.ExcludeBuffers)return null;let{buffer:a}=this.context,s=a.findChild(this.index+4,a.buffer[this.index+3],t>0?1:-1,e-this.context.start,t);return s<0?null:new Ln(this.context,this,s)}get parent(){return this._parent||this.context.parent.nextSignificantParent()}externalSibling(e){return this._parent?null:this.context.parent.nextChild(this.context.index+e,e,0,4)}get nextSibling(){let{buffer:e}=this.context,t=e.buffer[this.index+3];return t<(this._parent?e.buffer[this._parent.index+3]:e.buffer.length)?new Ln(this.context,this._parent,t):this.externalSibling(1)}get prevSibling(){let{buffer:e}=this.context,t=this._parent?this._parent.index+4:0;return this.index==t?this.externalSibling(-1):new Ln(this.context,this._parent,e.findChild(t,this.index,-1,0,4))}get tree(){return null}toTree(){let e=[],t=[],{buffer:n}=this.context,a=this.index+4,s=n.buffer[this.index+3];if(s>a){let r=n.buffer[this.index+1];e.push(n.slice(a,s,r)),t.push(0)}return new Ee(this.type,e,t,this.to-this.from)}toString(){return this.context.buffer.childString(this.index)}}function w2(i){if(!i.length)return null;let e=0,t=i[0];for(let s=1;st.from||r.to=e){let o=new Wt(r.tree,r.overlay[0].from+s.from,-1,s);(a||(a=[n])).push(ea(o,e,t,!1))}}return a?w2(a):n}class s0{get name(){return this.type.name}constructor(e,t=0){if(this.buffer=null,this.stack=[],this.index=0,this.bufferNode=null,this.mode=t&~Ce.EnterBracketed,e instanceof Wt)this.yieldNode(e);else{this._tree=e.context.parent,this.buffer=e.context;for(let n=e._parent;n;n=n._parent)this.stack.unshift(n.index);this.bufferNode=e,this.yieldBuf(e.index)}}yieldNode(e){return e?(this._tree=e,this.type=e.type,this.from=e.from,this.to=e.to,!0):!1}yieldBuf(e,t){this.index=e;let{start:n,buffer:a}=this.buffer;return this.type=t||a.set.types[a.buffer[e]],this.from=n+a.buffer[e+1],this.to=n+a.buffer[e+2],!0}yield(e){return e?e instanceof Wt?(this.buffer=null,this.yieldNode(e)):(this.buffer=e.context,this.yieldBuf(e.index,e.type)):!1}toString(){return this.buffer?this.buffer.buffer.childString(this.index):this._tree.toString()}enterChild(e,t,n){if(!this.buffer)return this.yield(this._tree.nextChild(e<0?this._tree._tree.children.length-1:0,e,t,n,this.mode));let{buffer:a}=this.buffer,s=a.findChild(this.index+4,a.buffer[this.index+3],e,t-this.buffer.start,n);return s<0?!1:(this.stack.push(this.index),this.yieldBuf(s))}firstChild(){return this.enterChild(1,0,4)}lastChild(){return this.enterChild(-1,0,4)}childAfter(e){return this.enterChild(1,e,2)}childBefore(e){return this.enterChild(-1,e,-2)}enter(e,t,n=this.mode){return this.buffer?n&Ce.ExcludeBuffers?!1:this.enterChild(1,e,t):this.yield(this._tree.enter(e,t,n))}parent(){if(!this.buffer)return this.yieldNode(this.mode&Ce.IncludeAnonymous?this._tree._parent:this._tree.parent);if(this.stack.length)return this.yieldBuf(this.stack.pop());let e=this.mode&Ce.IncludeAnonymous?this.buffer.parent:this.buffer.parent.nextSignificantParent();return this.buffer=null,this.yieldNode(e)}sibling(e){if(!this.buffer)return this._tree._parent?this.yield(this._tree.index<0?null:this._tree._parent.nextChild(this._tree.index+e,e,0,4,this.mode)):!1;let{buffer:t}=this.buffer,n=this.stack.length-1;if(e<0){let a=n<0?0:this.stack[n]+4;if(this.index!=a)return this.yieldBuf(t.findChild(a,this.index,-1,0,4))}else{let a=t.buffer[this.index+3];if(a<(n<0?t.buffer.length:t.buffer[this.stack[n]+3]))return this.yieldBuf(a)}return n<0?this.yield(this.buffer.parent.nextChild(this.buffer.index+e,e,0,4,this.mode)):!1}nextSibling(){return this.sibling(1)}prevSibling(){return this.sibling(-1)}atLastNode(e){let t,n,{buffer:a}=this;if(a){if(e>0){if(this.index-1)for(let s=t+e,r=e<0?-1:n._tree.children.length;s!=r;s+=e){let o=n._tree.children[s];if(this.mode&Ce.IncludeAnonymous||o instanceof Hn||!o.type.isAnonymous||wu(o))return!1}return!0}move(e,t){if(t&&this.enterChild(e,0,4))return!0;for(;;){if(this.sibling(e))return!0;if(this.atLastNode(e)||!this.parent())return!1}}next(e=!0){return this.move(1,e)}prev(e=!0){return this.move(-1,e)}moveTo(e,t=0){for(;(this.from==this.to||(t<1?this.from>=e:this.from>e)||(t>-1?this.to<=e:this.to=0;){for(let r=e;r;r=r._parent)if(r.index==a){if(a==this.index)return r;t=r,n=s+1;break e}a=this.stack[--s]}for(let a=n;a=0;s--){if(s<0)return a0(this._tree,e,a);let r=n[t.buffer[this.stack[s]]];if(!r.isAnonymous){if(e[a]&&e[a]!=r.name)return!1;a--}}return!0}}function wu(i){return i.children.some(e=>e instanceof Hn||!e.type.isAnonymous||wu(e))}function MV(i){var e;let{buffer:t,nodeSet:n,maxBufferLength:a=b2,reused:s=[],minRepeatType:r=n.types.length}=i,o=Array.isArray(t)?new vu(t,t.length):t,l=n.types,u=0,m=0;function c(I,T,k,C,P,D){let{id:B,start:j,end:Z,size:F}=o,U=m,ne=u;if(F<0)if(o.next(),F==-1){let _=s[B];k.push(_),C.push(j-I);return}else if(F==-3){u=B;return}else if(F==-4){m=B;return}else throw new RangeError(`Unrecognized record size: ${F}`);let ge=l[B],ie,ue,N=j-I;if(Z-j<=a&&(ue=b(o.pos-T,P))){let _=new Uint16Array(ue.size-ue.skip),M=o.pos-ue.size,J=_.length;for(;o.pos>M;)J=x(ue.start,_,J);ie=new Hn(_,Z-ue.start,n),N=ue.start-I}else{let _=o.pos-F;o.next();let M=[],J=[],q=B>=r?B:-1,X=0,Y=Z,ae=U;for(;o.pos>_;)q>=0&&o.id==q&&o.size>=0?(o.end<=Y-a&&(g(M,J,j,X,o.end,Y,q,ae,ne),X=M.length,Y=o.end,ae=m),o.next()):D>2500?d(j,_,M,J):c(j,_,M,J,q,D+1);if(q>=0&&X>0&&X-1&&X>0){let re=h(ge,ne);ie=yu(ge,M,J,0,M.length,0,Z-j,re,re)}else ie=f(ge,M,J,Z-j,U-Z,ne)}k.push(ie),C.push(N)}function d(I,T,k,C){let P=[],D=0,B=-1;for(;o.pos>T;){let{id:j,start:Z,end:F,size:U}=o;if(U>4)o.next();else{if(B>-1&&Z=0;F-=3)j[U++]=P[F],j[U++]=P[F+1]-Z,j[U++]=P[F+2]-Z,j[U++]=U;k.push(new Hn(j,P[2]-Z,n)),C.push(Z-I)}}function h(I,T){return(k,C,P)=>{let D=0,B=k.length-1,j,Z;if(B>=0&&(j=k[B])instanceof Ee){if(!B&&j.type==I&&j.length==P)return j;(Z=j.prop(le.lookAhead))&&(D=C[B]+j.length+Z-P)}return f(I,k,C,P,D,T)}}function g(I,T,k,C,P,D,B,j,Z){let F=[],U=[];for(;I.length>C;)F.push(I.pop()),U.push(T.pop()+k-P);I.push(f(n.types[B],F,U,D-P,j-D,Z)),T.push(P-k)}function f(I,T,k,C,P,D,B){if(D){let j=[le.contextHash,D];B=B?[j].concat(B):[j]}if(P>25){let j=[le.lookAhead,P];B=B?[j].concat(B):[j]}return new Ee(I,T,k,C,B)}function b(I,T){let k=o.fork(),C=0,P=0,D=0,B=k.end-a,j={size:0,start:0,skip:0};e:for(let Z=k.pos-I;k.pos>Z;){let F=k.size;if(k.id==T&&F>=0){j.size=C,j.start=P,j.skip=D,D+=4,C+=4,k.next();continue}let U=k.pos-F;if(F<0||U=r?4:0,ge=k.start;for(k.next();k.pos>U;){if(k.size<0)if(k.size==-3||k.size==-4)ne+=4;else break e;else k.id>=r&&(ne+=4);k.next()}P=ge,C+=F,D+=ne}return(T<0||C==I)&&(j.size=C,j.start=P,j.skip=D),j.size>4?j:void 0}function x(I,T,k){let{id:C,start:P,end:D,size:B}=o;if(o.next(),B>=0&&C4){let Z=o.pos-(B-4);for(;o.pos>Z;)k=x(I,T,k)}T[--k]=j,T[--k]=D-I,T[--k]=P-I,T[--k]=C}else B==-3?u=C:B==-4&&(m=C);return k}let v=[],w=[];for(;o.pos>0;)c(i.start||0,i.bufferStart||0,v,w,-1,0);let z=(e=i.length)!==null&&e!==void 0?e:v.length?w[0]+v[0].length:0;return new Ee(l[i.topID],v.reverse(),w.reverse(),z)}const cd=new WeakMap;function Is(i,e){if(!i.isAnonymous||e instanceof Hn||e.type!=i)return 1;let t=cd.get(e);if(t==null){t=1;for(let n of e.children){if(n.type!=i||!(n instanceof Ee)){t=1;break}t+=Is(i,n)}cd.set(e,t)}return t}function yu(i,e,t,n,a,s,r,o,l){let u=0;for(let g=n;g=m)break;T+=k}if(w==z+1){if(T>m){let k=g[z];h(k.children,k.positions,0,k.children.length,f[z]+v);continue}c.push(g[z])}else{let k=f[w-1]+g[w-1].length-I;c.push(yu(i,g,f,z,w,I,k,null,l))}d.push(I+v-s)}}return h(e,t,n,a,0),(o||l)(c,d,r)}class d1{constructor(e,t,n,a,s=!1,r=!1){this.from=e,this.to=t,this.tree=n,this.offset=a,this.open=(s?1:0)|(r?2:0)}get openStart(){return(this.open&1)>0}get openEnd(){return(this.open&2)>0}static addTree(e,t=[],n=!1){let a=[new d1(0,e.length,e,0,!1,n)];for(let s of t)s.to>e.length&&a.push(s);return a}static applyChanges(e,t,n=128){if(!t.length)return e;let a=[],s=1,r=e.length?e[0]:null;for(let o=0,l=0,u=0;;o++){let m=o=n)for(;r&&r.from=d.from||c<=d.to||u){let h=Math.max(d.from,l)-u,g=Math.min(d.to,c)-u;d=h>=g?null:new d1(h,g,d.tree,d.offset+u,o>0,!!m)}if(d&&a.push(d),r.to>c)break;r=snew zo(a.from,a.to)):[new zo(0,0)]:[new zo(0,e.length)],this.createParse(e,t||[],n)}parse(e,t,n){let a=this.startParse(e,t,n);for(;;){let s=a.advance();if(s)return s}}}class ZV{constructor(e){this.string=e}get length(){return this.string.length}chunk(e){return this.string.slice(e)}get lineChunks(){return!1}read(e,t){return this.string.slice(e,t)}}new le({perNode:!0});let LV=0;class Ot{constructor(e,t,n,a){this.name=e,this.set=t,this.base=n,this.modified=a,this.id=LV++}toString(){let{name:e}=this;for(let t of this.modified)t.name&&(e=`${t.name}(${e})`);return e}static define(e,t){let n=typeof e=="string"?e:"?";if(e instanceof Ot&&(t=e),t!=null&&t.base)throw new Error("Can not derive from a modified tag");let a=new Ot(n,[],null,[]);if(a.set.push(a),t)for(let s of t.set)a.set.push(s);return a}static defineModifier(e){let t=new sr(e);return n=>n.modified.indexOf(t)>-1?n:sr.get(n.base||n,n.modified.concat(t).sort((a,s)=>a.id-s.id))}}let WV=0;class sr{constructor(e){this.name=e,this.instances=[],this.id=WV++}static get(e,t){if(!t.length)return e;let n=t[0].instances.find(o=>o.base==e&&EV(t,o.modified));if(n)return n;let a=[],s=new Ot(e.name,a,e,t);for(let o of t)o.instances.push(s);let r=NV(t);for(let o of e.set)if(!o.modified.length)for(let l of r)a.push(sr.get(o,l));return s}}function EV(i,e){return i.length==e.length&&i.every((t,n)=>t==e[n])}function NV(i){let e=[[]];for(let t=0;tn.length-t.length)}function z2(i){let e=Object.create(null);for(let t in i){let n=i[t];Array.isArray(n)||(n=[n]);for(let a of t.split(" "))if(a){let s=[],r=2,o=a;for(let c=0;;){if(o=="..."&&c>0&&c+3==a.length){r=1;break}let d=/^"(?:[^"\\]|\\.)*?"|[^\/!]+/.exec(o);if(!d)throw new RangeError("Invalid path: "+a);if(s.push(d[0]=="*"?"":d[0][0]=='"'?JSON.parse(d[0]):d[0]),c+=d[0].length,c==a.length)break;let h=a[c++];if(c==a.length&&h=="!"){r=0;break}if(h!="/")throw new RangeError("Invalid path: "+a);o=a.slice(c)}let l=s.length-1,u=s[l];if(!u)throw new RangeError("Invalid path: "+a);let m=new ta(n,r,l>0?s.slice(0,l):null);e[u]=m.sort(e[u])}}return I2.add(e)}const I2=new le({combine(i,e){let t,n,a;for(;i||e;){if(!i||e&&i.depth<=e.depth?(a=e,e=e.next):(a=i,i=i.next),t&&t.mode==a.mode&&!a.context&&!t.context)continue;let s=new ta(a.tags,a.mode,a.context);t?t.next=s:n=s,t=s}return n}});class ta{constructor(e,t,n,a){this.tags=e,this.mode=t,this.context=n,this.next=a}get opaque(){return this.mode==0}get inherit(){return this.mode==1}sort(e){return!e||e.depth{let r=a;for(let o of s)for(let l of o.set){let u=t[l.id];if(u){r=r?r+" "+u:u;break}}return r},scope:n}}function FV(i,e){let t=null;for(let n of i){let a=n.style(e);a&&(t=t?t+" "+a:a)}return t}function RV(i,e,t,n=0,a=i.length){let s=new JV(n,Array.isArray(e)?e:[e],t);s.highlightRange(i.cursor(),n,a,"",s.highlighters),s.flush(a)}class JV{constructor(e,t,n){this.at=e,this.highlighters=t,this.span=n,this.class=""}startSpan(e,t){t!=this.class&&(this.flush(e),e>this.at&&(this.at=e),this.class=t)}flush(e){e>this.at&&this.class&&this.span(this.at,e,this.class)}highlightRange(e,t,n,a,s){let{type:r,from:o,to:l}=e;if(o>=n||l<=t)return;r.isTop&&(s=this.highlighters.filter(h=>!h.scope||h.scope(r)));let u=a,m=GV(e)||ta.empty,c=FV(s,m.tags);if(c&&(u&&(u+=" "),u+=c,m.mode==1&&(a+=(a?" ":"")+c)),this.startSpan(Math.max(t,o),u),m.opaque)return;let d=e.tree&&e.tree.prop(le.mounted);if(d&&d.overlay){let h=e.node.enter(d.overlay[0].from+o,1),g=this.highlighters.filter(b=>!b.scope||b.scope(d.tree.type)),f=e.firstChild();for(let b=0,x=o;;b++){let v=b=w||!e.nextSibling())););if(!v||w>n)break;x=v.to+o,x>t&&(this.highlightRange(h.cursor(),Math.max(t,v.from+o),Math.min(n,x),"",g),this.startSpan(Math.min(n,x),u))}f&&e.parent()}else if(e.firstChild()){d&&(a="");do if(!(e.to<=t)){if(e.from>=n)break;this.highlightRange(e,t,n,a,s),this.startSpan(Math.min(n,e.to),u)}while(e.nextSibling());e.parent()}}}function GV(i){let e=i.type.prop(I2);for(;e&&e.context&&!i.matchContext(e.context);)e=e.next;return e||null}const R=Ot.define,Ka=R(),Cn=R(),dd=R(Cn),hd=R(Cn),Dn=R(),Xa=R(Dn),Io=R(Dn),tn=R(),Yn=R(tn),Xt=R(),en=R(),r0=R(),mi=R(r0),es=R(),E={comment:Ka,lineComment:R(Ka),blockComment:R(Ka),docComment:R(Ka),name:Cn,variableName:R(Cn),typeName:dd,tagName:R(dd),propertyName:hd,attributeName:R(hd),className:R(Cn),labelName:R(Cn),namespace:R(Cn),macroName:R(Cn),literal:Dn,string:Xa,docString:R(Xa),character:R(Xa),attributeValue:R(Xa),number:Io,integer:R(Io),float:R(Io),bool:R(Dn),regexp:R(Dn),escape:R(Dn),color:R(Dn),url:R(Dn),keyword:Xt,self:R(Xt),null:R(Xt),atom:R(Xt),unit:R(Xt),modifier:R(Xt),operatorKeyword:R(Xt),controlKeyword:R(Xt),definitionKeyword:R(Xt),moduleKeyword:R(Xt),operator:en,derefOperator:R(en),arithmeticOperator:R(en),logicOperator:R(en),bitwiseOperator:R(en),compareOperator:R(en),updateOperator:R(en),definitionOperator:R(en),typeOperator:R(en),controlOperator:R(en),punctuation:r0,separator:R(r0),bracket:mi,angleBracket:R(mi),squareBracket:R(mi),paren:R(mi),brace:R(mi),content:tn,heading:Yn,heading1:R(Yn),heading2:R(Yn),heading3:R(Yn),heading4:R(Yn),heading5:R(Yn),heading6:R(Yn),contentSeparator:R(tn),list:R(tn),quote:R(tn),emphasis:R(tn),strong:R(tn),link:R(tn),monospace:R(tn),strikethrough:R(tn),inserted:R(),deleted:R(),changed:R(),invalid:R(),meta:es,documentMeta:R(es),annotation:R(es),processingInstruction:R(es),definition:Ot.defineModifier("definition"),constant:Ot.defineModifier("constant"),function:Ot.defineModifier("function"),standard:Ot.defineModifier("standard"),local:Ot.defineModifier("local"),special:Ot.defineModifier("special")};for(let i in E){let e=E[i];e instanceof Ot&&(e.name=i)}k2([{tag:E.link,class:"tok-link"},{tag:E.heading,class:"tok-heading"},{tag:E.emphasis,class:"tok-emphasis"},{tag:E.strong,class:"tok-strong"},{tag:E.keyword,class:"tok-keyword"},{tag:E.atom,class:"tok-atom"},{tag:E.bool,class:"tok-bool"},{tag:E.url,class:"tok-url"},{tag:E.labelName,class:"tok-labelName"},{tag:E.inserted,class:"tok-inserted"},{tag:E.deleted,class:"tok-deleted"},{tag:E.literal,class:"tok-literal"},{tag:E.string,class:"tok-string"},{tag:E.number,class:"tok-number"},{tag:[E.regexp,E.escape,E.special(E.string)],class:"tok-string2"},{tag:E.variableName,class:"tok-variableName"},{tag:E.local(E.variableName),class:"tok-variableName tok-local"},{tag:E.definition(E.variableName),class:"tok-variableName tok-definition"},{tag:E.special(E.variableName),class:"tok-variableName2"},{tag:E.definition(E.propertyName),class:"tok-propertyName tok-definition"},{tag:E.typeName,class:"tok-typeName"},{tag:E.namespace,class:"tok-namespace"},{tag:E.className,class:"tok-className"},{tag:E.macroName,class:"tok-macroName"},{tag:E.propertyName,class:"tok-propertyName"},{tag:E.operator,class:"tok-operator"},{tag:E.comment,class:"tok-comment"},{tag:E.meta,class:"tok-meta"},{tag:E.invalid,class:"tok-invalid"},{tag:E.punctuation,class:"tok-punctuation"}]);var ko;const M1=new le;function HV(i){return Q.define({combine:i?e=>e.concat(i):void 0})}const VV=new le;class Vt{constructor(e,t,n=[],a=""){this.data=e,this.name=a,ce.prototype.hasOwnProperty("tree")||Object.defineProperty(ce.prototype,"tree",{get(){return et(this)}}),this.parser=t,this.extension=[Y1.of(this),ce.languageData.of((s,r,o)=>{let l=gd(s,r,o),u=l.type.prop(M1);if(!u)return[];let m=s.facet(u),c=l.type.prop(VV);if(c){let d=l.resolve(r-l.from,o);for(let h of c)if(h.test(d,s)){let g=s.facet(h.facet);return h.type=="replace"?g:g.concat(m)}}return m})].concat(n)}isActiveAt(e,t,n=-1){return gd(e,t,n).type.prop(M1)==this.data}findRegions(e){let t=e.facet(Y1);if((t==null?void 0:t.data)==this.data)return[{from:0,to:e.doc.length}];if(!t||!t.allowsNesting)return[];let n=[],a=(s,r)=>{if(s.prop(M1)==this.data){n.push({from:r,to:r+s.length});return}let o=s.prop(le.mounted);if(o){if(o.tree.prop(M1)==this.data){if(o.overlay)for(let l of o.overlay)n.push({from:l.from+r,to:l.to+r});else n.push({from:r,to:r+s.length});return}else if(o.overlay){let l=n.length;if(a(o.tree,o.overlay[0].from+r),n.length>l)return}}for(let l=0;ln.isTop?t:void 0)]}),e.name)}configure(e,t){return new rr(this.data,this.parser.configure(e),t||this.name)}get allowsNesting(){return this.parser.hasWrappers()}}function et(i){let e=i.field(Vt.state,!1);return e?e.tree:Ee.empty}class qV{constructor(e){this.doc=e,this.cursorPos=0,this.string="",this.cursor=e.iter()}get length(){return this.doc.length}syncTo(e){return this.string=this.cursor.next(e-this.cursorPos).value,this.cursorPos=e+this.string.length,this.cursorPos-this.string.length}chunk(e){return this.syncTo(e),this.string}get lineChunks(){return!0}read(e,t){let n=this.cursorPos-this.string.length;return e=this.cursorPos?this.doc.sliceString(e,t):this.string.slice(e-n,t-n)}}let ci=null;class or{constructor(e,t,n=[],a,s,r,o,l){this.parser=e,this.state=t,this.fragments=n,this.tree=a,this.treeLen=s,this.viewport=r,this.skipped=o,this.scheduleOn=l,this.parse=null,this.tempSkipped=[]}static create(e,t,n){return new or(e,t,[],Ee.empty,0,n,[],null)}startParse(){return this.parser.startParse(new qV(this.state.doc),this.fragments)}work(e,t){return t!=null&&t>=this.state.doc.length&&(t=void 0),this.tree!=Ee.empty&&this.isDone(t??this.state.doc.length)?(this.takeTree(),!0):this.withContext(()=>{var n;if(typeof e=="number"){let a=Date.now()+e;e=()=>Date.now()>a}for(this.parse||(this.parse=this.startParse()),t!=null&&(this.parse.stoppedAt==null||this.parse.stoppedAt>t)&&t=this.treeLen&&((this.parse.stoppedAt==null||this.parse.stoppedAt>e)&&this.parse.stopAt(e),this.withContext(()=>{for(;!(t=this.parse.advance()););}),this.treeLen=e,this.tree=t,this.fragments=this.withoutTempSkipped(d1.addTree(this.tree,this.fragments,!0)),this.parse=null)}withContext(e){let t=ci;ci=this;try{return e()}finally{ci=t}}withoutTempSkipped(e){for(let t;t=this.tempSkipped.pop();)e=pd(e,t.from,t.to);return e}changes(e,t){let{fragments:n,tree:a,treeLen:s,viewport:r,skipped:o}=this;if(this.takeTree(),!e.empty){let l=[];if(e.iterChangedRanges((u,m,c,d)=>l.push({fromA:u,toA:m,fromB:c,toB:d})),n=d1.applyChanges(n,l),a=Ee.empty,s=0,r={from:e.mapPos(r.from,-1),to:e.mapPos(r.to,1)},this.skipped.length){o=[];for(let u of this.skipped){let m=e.mapPos(u.from,1),c=e.mapPos(u.to,-1);me.from&&(this.fragments=pd(this.fragments,a,s),this.skipped.splice(n--,1))}return this.skipped.length>=t?!1:(this.reset(),!0)}reset(){this.parse&&(this.takeTree(),this.parse=null)}skipUntilInView(e,t){this.skipped.push({from:e,to:t})}static getSkippingParser(e){return new class extends y2{createParse(t,n,a){let s=a[0].from,r=a[a.length-1].to;return{parsedPos:s,advance(){let l=ci;if(l){for(let u of a)l.tempSkipped.push(u);e&&(l.scheduleOn=l.scheduleOn?Promise.all([l.scheduleOn,e]):e)}return this.parsedPos=r,new Ee(wt.none,[],[],r-s)},stoppedAt:null,stopAt(){}}}}}isDone(e){e=Math.min(e,this.state.doc.length);let t=this.fragments;return this.treeLen>=e&&t.length&&t[0].from==0&&t[0].to>=e}static get(){return ci}}function pd(i,e,t){return d1.applyChanges(i,[{fromA:e,toA:t,fromB:e,toB:t}])}class $1{constructor(e){this.context=e,this.tree=e.tree}apply(e){if(!e.docChanged&&this.tree==this.context.tree)return this;let t=this.context.changes(e.changes,e.state),n=this.context.treeLen==e.startState.doc.length?void 0:Math.max(e.changes.mapPos(this.context.treeLen),t.viewport.to);return t.work(20,n)||t.takeTree(),new $1(t)}static init(e){let t=Math.min(3e3,e.doc.length),n=or.create(e.facet(Y1).parser,e,{from:0,to:t});return n.work(20,t)||n.takeTree(),new $1(n)}}Vt.state=Nt.define({create:$1.init,update(i,e){for(let t of e.effects)if(t.is(Vt.setState))return t.value;return e.startState.facet(Y1)!=e.state.facet(Y1)?$1.init(e.state):i.apply(e)}});let S2=i=>{let e=setTimeout(()=>i(),500);return()=>clearTimeout(e)};typeof requestIdleCallback<"u"&&(S2=i=>{let e=-1,t=setTimeout(()=>{e=requestIdleCallback(i,{timeout:400})},100);return()=>e<0?clearTimeout(t):cancelIdleCallback(e)});const So=typeof navigator<"u"&&(!((ko=navigator.scheduling)===null||ko===void 0)&&ko.isInputPending)?()=>navigator.scheduling.isInputPending():null,UV=Et.fromClass(class{constructor(e){this.view=e,this.working=null,this.workScheduled=0,this.chunkEnd=-1,this.chunkBudget=-1,this.work=this.work.bind(this),this.scheduleWork()}update(e){let t=this.view.state.field(Vt.state).context;(t.updateViewport(e.view.viewport)||this.view.viewport.to>t.treeLen)&&this.scheduleWork(),(e.docChanged||e.selectionSet)&&(this.view.hasFocus&&(this.chunkBudget+=50),this.scheduleWork()),this.checkAsyncSchedule(t)}scheduleWork(){if(this.working)return;let{state:e}=this.view,t=e.field(Vt.state);(t.tree!=t.context.tree||!t.context.isDone(e.doc.length))&&(this.working=S2(this.work))}work(e){this.working=null;let t=Date.now();if(this.chunkEnda+1e3,l=s.context.work(()=>So&&So()||Date.now()>r,a+(o?0:1e5));this.chunkBudget-=Date.now()-t,(l||this.chunkBudget<=0)&&(s.context.takeTree(),this.view.dispatch({effects:Vt.setState.of(new $1(s.context))})),this.chunkBudget>0&&!(l&&!o)&&this.scheduleWork(),this.checkAsyncSchedule(s.context)}checkAsyncSchedule(e){e.scheduleOn&&(this.workScheduled++,e.scheduleOn.then(()=>this.scheduleWork()).catch(t=>St(this.view.state,t)).then(()=>this.workScheduled--),e.scheduleOn=null)}destroy(){this.working&&this.working()}isWorking(){return!!(this.working||this.workScheduled>0)}},{eventHandlers:{focus(){this.scheduleWork()}}}),Y1=Q.define({combine(i){return i.length?i[0]:null},enables:i=>[Vt.state,UV,$.contentAttributes.compute([i],e=>{let t=e.facet(i);return t&&t.name?{"data-language":t.name}:{}})]});class QV{constructor(e,t=[]){this.language=e,this.support=t,this.extension=[e,t]}}const $V=Q.define(),pa=Q.define({combine:i=>{if(!i.length)return" ";let e=i[0];if(!e||/\S/.test(e)||Array.from(e).some(t=>t!=e[0]))throw new Error("Invalid indent unit: "+JSON.stringify(i[0]));return e}});function lr(i){let e=i.facet(pa);return e.charCodeAt(0)==9?i.tabSize*e.length:e.length}function na(i,e){let t="",n=i.tabSize,a=i.facet(pa)[0];if(a==" "){for(;e>=n;)t+=" ",e-=n;a=" "}for(let s=0;s=e?YV(i,t,e):null}class Wr{constructor(e,t={}){this.state=e,this.options=t,this.unit=lr(e)}lineAt(e,t=1){let n=this.state.doc.lineAt(e),{simulateBreak:a,simulateDoubleBreak:s}=this.options;return a!=null&&a>=n.from&&a<=n.to?s&&a==e?{text:"",from:e}:(t<0?a-1&&(s+=r-this.countColumn(n,n.search(/\S|$/))),s}countColumn(e,t=e.length){return ca(e,this.state.tabSize,t)}lineIndent(e,t=1){let{text:n,from:a}=this.lineAt(e,t),s=this.options.overrideIndentation;if(s){let r=s(a);if(r>-1)return r}return this.countColumn(n,n.search(/\S|$/))}get simulatedBreak(){return this.options.simulateBreak||null}}const T2=new le;function YV(i,e,t){let n=e.resolveStack(t),a=e.resolveInner(t,-1).resolve(t,0).enterUnfinishedNodesBefore(t);if(a!=n.node){let s=[];for(let r=a;r&&!(r.fromn.node.to||r.from==n.node.from&&r.type==n.node.type);r=r.parent)s.push(r);for(let r=s.length-1;r>=0;r--)n={node:s[r],next:n}}return _2(n,i,t)}function _2(i,e,t){for(let n=i;n;n=n.next){let a=XV(n.node);if(a)return a(Iu.create(e,t,n))}return 0}function KV(i){return i.pos==i.options.simulateBreak&&i.options.simulateDoubleBreak}function XV(i){let e=i.type.prop(T2);if(e)return e;let t=i.firstChild,n;if(t&&(n=t.type.prop(le.closedBy))){let a=i.lastChild,s=a&&n.indexOf(a.name)>-1;return r=>A2(r,!0,1,void 0,s&&!KV(r)?a.from:void 0)}return i.parent==null?eq:null}function eq(){return 0}class Iu extends Wr{constructor(e,t,n){super(e.state,e.options),this.base=e,this.pos=t,this.context=n}get node(){return this.context.node}static create(e,t,n){return new Iu(e,t,n)}get textAfter(){return this.textAfterPos(this.pos)}get baseIndent(){return this.baseIndentFor(this.node)}baseIndentFor(e){let t=this.state.doc.lineAt(e.from);for(;;){let n=e.resolve(t.from);for(;n.parent&&n.parent.from==n.from;)n=n.parent;if(tq(n,e))break;t=this.state.doc.lineAt(n.from)}return this.lineIndent(t.from)}continue(){return _2(this.context.next,this.base,this.pos)}}function tq(i,e){for(let t=e;t;t=t.parent)if(i==t)return!0;return!1}function nq(i){let e=i.node,t=e.childAfter(e.from),n=e.lastChild;if(!t)return null;let a=i.options.simulateBreak,s=i.state.doc.lineAt(t.from),r=a==null||a<=s.from?s.to:Math.min(s.to,a);for(let o=t.to;;){let l=e.childAfter(o);if(!l||l==n)return null;if(!l.type.isSkipped){if(l.from>=r)return null;let u=/^ */.exec(s.text.slice(t.to-s.from))[0].length;return{from:t.from,to:t.to+u}}o=l.to}}function iq({closing:i,align:e=!0,units:t=1}){return n=>A2(n,e,t,i)}function A2(i,e,t,n,a){let s=i.textAfter,r=s.match(/^\s*/)[0].length,o=n&&s.slice(r,r+n.length)==n||a==i.pos+r,l=e?nq(i):null;return l?o?i.column(l.from):i.column(l.to):i.baseIndent+(o?0:i.unit*t)}const aq=i=>i.baseIndent;function To({except:i,units:e=1}={}){return t=>{let n=i&&i.test(t.textAfter);return t.baseIndent+(n?0:e*t.unit)}}const sq=200;function rq(){return ce.transactionFilter.of(i=>{if(!i.docChanged||!i.isUserEvent("input.type")&&!i.isUserEvent("input.complete"))return i;let e=i.startState.languageDataAt("indentOnInput",i.startState.selection.main.head);if(!e.length)return i;let t=i.newDoc,{head:n}=i.newSelection.main,a=t.lineAt(n);if(n>a.from+sq)return i;let s=t.sliceString(a.from,n);if(!e.some(u=>u.test(s)))return i;let{state:r}=i,o=-1,l=[];for(let{head:u}of r.selection.ranges){let m=r.doc.lineAt(u);if(m.from==o)continue;o=m.from;let c=zu(r,m.from);if(c==null)continue;let d=/^\s*/.exec(m.text)[0],h=na(r,c);d!=h&&l.push({from:m.from,to:m.from+d.length,insert:h})}return l.length?[i,{changes:l,sequential:!0}]:i})}const oq=new le;function lq(i){let e=i.firstChild,t=i.lastChild;return e&&e.too.prop(M1)==r.data:r?o=>o==r:void 0,this.style=k2(e.map(o=>({tag:o.tag,class:o.class||a(Object.assign({},o,{tag:null}))})),{all:s}).style,this.module=n?new Rn(n):null,this.themeType=t.themeType}static define(e,t){return new Er(e,t||{})}}const o0=Q.define(),uq=Q.define({combine(i){return i.length?[i[0]]:null}});function _o(i){let e=i.facet(o0);return e.length?e:i.facet(uq)}function mq(i,e){let t=[dq],n;return i instanceof Er&&(i.module&&t.push($.styleModule.of(i.module)),n=i.themeType),n?t.push(o0.computeN([$.darkTheme],a=>a.facet($.darkTheme)==(n=="dark")?[i]:[])):t.push(o0.of(i)),t}class cq{constructor(e){this.markCache=Object.create(null),this.tree=et(e.state),this.decorations=this.buildDeco(e,_o(e.state)),this.decoratedTo=e.viewport.to}update(e){let t=et(e.state),n=_o(e.state),a=n!=_o(e.startState),{viewport:s}=e.view,r=e.changes.mapPos(this.decoratedTo,1);t.length=s.to?(this.decorations=this.decorations.map(e.changes),this.decoratedTo=r):(t!=this.tree||e.viewportChanged||a)&&(this.tree=t,this.decorations=this.buildDeco(e.view,n),this.decoratedTo=s.to)}buildDeco(e,t){if(!t||!this.tree.length)return fe.none;let n=new c1;for(let{from:a,to:s}of e.visibleRanges)RV(this.tree,t,(r,o,l)=>{n.add(r,o,this.markCache[l]||(this.markCache[l]=fe.mark({class:l})))},a,s);return n.finish()}}const dq=f1.high(Et.fromClass(cq,{decorations:i=>i.decorations})),hq=$.baseTheme({"&.cm-focused .cm-matchingBracket":{backgroundColor:"#328c8252"},"&.cm-focused .cm-nonmatchingBracket":{backgroundColor:"#bb555544"}}),j2=1e4,C2="()[]{}",D2=Q.define({combine(i){return su(i,{afterCursor:!0,brackets:C2,maxScanDistance:j2,renderMatch:fq})}}),gq=fe.mark({class:"cm-matchingBracket"}),pq=fe.mark({class:"cm-nonmatchingBracket"});function fq(i){let e=[],t=i.matched?gq:pq;return e.push(t.range(i.start.from,i.start.to)),i.end&&e.push(t.range(i.end.from,i.end.to)),e}function fd(i){let e=[],t=i.facet(D2);for(let n of i.selection.ranges){if(!n.empty)continue;let a=un(i,n.head,-1,t)||n.head>0&&un(i,n.head-1,1,t)||t.afterCursor&&(un(i,n.head,1,t)||n.headi.decorations}),xq=[bq,hq];function vq(i={}){return[D2.of(i),xq]}const wq=new le;function l0(i,e,t){let n=i.prop(e<0?le.openedBy:le.closedBy);if(n)return n;if(i.name.length==1){let a=t.indexOf(i.name);if(a>-1&&a%2==(e<0?1:0))return[t[a+e]]}return null}function u0(i){let e=i.type.prop(wq);return e?e(i.node):i}function un(i,e,t,n={}){let a=n.maxScanDistance||j2,s=n.brackets||C2,r=et(i),o=r.resolveInner(e,t);for(let l=o;l;l=l.parent){let u=l0(l.type,t,s);if(u&&l.from0?e>=m.from&&em.from&&e<=m.to))return yq(i,e,t,l,m,u,s)}}return zq(i,e,t,r,o.type,a,s)}function yq(i,e,t,n,a,s,r){let o=n.parent,l={from:a.from,to:a.to},u=0,m=o==null?void 0:o.cursor();if(m&&(t<0?m.childBefore(n.from):m.childAfter(n.to)))do if(t<0?m.to<=n.from:m.from>=n.to){if(u==0&&s.indexOf(m.type.name)>-1&&m.from0)return null;let u={from:t<0?e-1:e,to:t>0?e+1:e},m=i.doc.iterRange(e,t>0?i.doc.length:0),c=0;for(let d=0;!m.next().done&&d<=s;){let h=m.value;t<0&&(d+=h.length);let g=e+d*t;for(let f=t>0?0:h.length-1,b=t>0?h.length:-1;f!=b;f+=t){let x=r.indexOf(h[f]);if(!(x<0||n.resolveInner(g+f,1).type!=a))if(x%2==0==t>0)c++;else{if(c==1)return{start:u,end:{from:g+f,to:g+f+1},matched:x>>1==l>>1};c--}}t>0&&(d+=h.length)}return m.done?{start:u,matched:!1}:null}const Iq=Object.create(null),bd=[wt.none],xd=[],vd=Object.create(null),kq=Object.create(null);for(let[i,e]of[["variable","variableName"],["variable-2","variableName.special"],["string-2","string.special"],["def","variableName.definition"],["tag","tagName"],["attribute","attributeName"],["type","typeName"],["builtin","variableName.standard"],["qualifier","modifier"],["error","invalid"],["header","heading"],["property","propertyName"]])kq[i]=Sq(Iq,e);function Ao(i,e){xd.indexOf(i)>-1||(xd.push(i),console.warn(e))}function Sq(i,e){let t=[];for(let o of e.split(" ")){let l=[];for(let u of o.split(".")){let m=i[u]||E[u];m?typeof m=="function"?l.length?l=l.map(m):Ao(u,`Modifier ${u} used at start of tag`):l.length?Ao(u,`Tag ${u} used as modifier`):l=Array.isArray(m)?m:[m]:Ao(u,`Unknown highlighting tag ${u}`)}for(let u of l)t.push(u)}if(!t.length)return 0;let n=e.replace(/ /g,"_"),a=n+" "+t.map(o=>o.id),s=vd[a];if(s)return s.id;let r=vd[a]=wt.define({id:bd.length,name:n,props:[z2({[n]:t})]});return bd.push(r),r.id}ze.RTL,ze.LTR;class B2{constructor(e,t,n,a){this.state=e,this.pos=t,this.explicit=n,this.view=a,this.abortListeners=[],this.abortOnDocChange=!1}tokenBefore(e){let t=et(this.state).resolveInner(this.pos,-1);for(;t&&e.indexOf(t.name)<0;)t=t.parent;return t?{from:t.from,to:this.pos,text:this.state.sliceDoc(t.from,this.pos),type:t.type}:null}matchBefore(e){let t=this.state.doc.lineAt(this.pos),n=Math.max(t.from,this.pos-250),a=t.text.slice(n-t.from,this.pos-t.from),s=a.search(P2(e,!1));return s<0?null:{from:n+s,to:this.pos,text:a.slice(s)}}get aborted(){return this.abortListeners==null}addEventListener(e,t,n){e=="abort"&&this.abortListeners&&(this.abortListeners.push(t),n&&n.onDocChange&&(this.abortOnDocChange=!0))}}function wd(i){let e=Object.keys(i).join(""),t=/\w/.test(e);return t&&(e=e.replace(/\w/g,"")),`[${t?"\\w":""}${e.replace(/[^\w\s]/g,"\\$&")}]`}function Tq(i){let e=Object.create(null),t=Object.create(null);for(let{label:a}of i){e[a[0]]=!0;for(let s=1;stypeof a=="string"?{label:a}:a),[t,n]=e.every(a=>/^\w+$/.test(a.label))?[/\w*$/,/\w+$/]:Tq(e);return a=>{let s=a.matchBefore(n);return s||a.explicit?{from:s?s.from:a.pos,options:e,validFor:t}:null}}class yd{constructor(e,t,n,a){this.completion=e,this.source=t,this.match=n,this.score=a}}function h1(i){return i.selection.main.from}function P2(i,e){var t;let{source:n}=i,a=e&&n[0]!="^",s=n[n.length-1]!="$";return!a&&!s?i:new RegExp(`${a?"^":""}(?:${n})${s?"$":""}`,(t=i.flags)!==null&&t!==void 0?t:i.ignoreCase?"i":"")}const ku=Vn.define();function Aq(i,e,t,n){let{main:a}=i.selection,s=t-a.from,r=n-a.from;return{...i.changeByRange(o=>{if(o!=a&&t!=n&&i.sliceDoc(o.from+s,o.from+r)!=i.sliceDoc(t,n))return{range:o};let l=i.toText(e);return{changes:{from:o.from+s,to:n==a.from?o.to:o.from+r,insert:l},range:O.cursor(o.from+s+l.length)}}),scrollIntoView:!0,userEvent:"input.complete"}}const zd=new WeakMap;function jq(i){if(!Array.isArray(i))return i;let e=zd.get(i);return e||zd.set(i,e=_q(i)),e}const ur=pe.define(),ia=pe.define();class Cq{constructor(e){this.pattern=e,this.chars=[],this.folded=[],this.any=[],this.precise=[],this.byWord=[],this.score=0,this.matched=[];for(let t=0;t=48&&I<=57||I>=97&&I<=122?2:I>=65&&I<=90?1:0:(T=nf(I))!=T.toLowerCase()?1:T!=T.toUpperCase()?2:0;(!v||k==1&&b||z==0&&k!=0)&&(t[c]==I||n[c]==I&&(d=!0)?r[c++]=v:r.length&&(x=!1)),z=k,v+=xn(I)}return c==l&&r[0]==0&&x?this.result(-100+(d?-200:0),r,e):h==l&&g==0?this.ret(-200-e.length+(f==e.length?0:-100),[0,f]):o>-1?this.ret(-700-e.length,[o,o+this.pattern.length]):h==l?this.ret(-900-e.length,[g,f]):c==l?this.result(-100+(d?-200:0)+-700+(x?0:-1100),r,e):t.length==2?null:this.result((a[0]?-700:0)+-200+-1100,a,e)}result(e,t,n){let a=[],s=0;for(let r of t){let o=r+(this.astral?xn(Pt(n,r)):1);s&&a[s-1]==r?a[s-1]=o:(a[s++]=r,a[s++]=o)}return this.ret(e-n.length,a)}}class Dq{constructor(e){this.pattern=e,this.matched=[],this.score=0,this.folded=e.toLowerCase()}match(e){if(e.length!1,activateOnTypingDelay:100,selectOnOpen:!0,override:null,closeOnBlur:!0,maxRenderedOptions:100,defaultKeymap:!0,tooltipClass:()=>"",optionClass:()=>"",aboveCursor:!1,icons:!0,addToOptions:[],positionInfo:Bq,filterStrict:!1,compareCompletions:(e,t)=>(e.sortText||e.label).localeCompare(t.sortText||t.label),interactionDelay:75,updateSyncTime:100},{defaultKeymap:(e,t)=>e&&t,closeOnBlur:(e,t)=>e&&t,icons:(e,t)=>e&&t,tooltipClass:(e,t)=>n=>Id(e(n),t(n)),optionClass:(e,t)=>n=>Id(e(n),t(n)),addToOptions:(e,t)=>e.concat(t),filterStrict:(e,t)=>e||t})}});function Id(i,e){return i?e?i+" "+e:i:e}function Bq(i,e,t,n,a,s){let r=i.textDirection==ze.RTL,o=r,l=!1,u="top",m,c,d=e.left-a.left,h=a.right-e.right,g=n.right-n.left,f=n.bottom-n.top;if(o&&d=f||v>e.top?m=t.bottom-e.top:(u="bottom",m=e.bottom-t.top)}let b=(e.bottom-e.top)/s.offsetHeight,x=(e.right-e.left)/s.offsetWidth;return{style:`${u}: ${m/b}px; max-width: ${c/x}px`,class:"cm-completionInfo-"+(l?r?"left-narrow":"right-narrow":o?"left":"right")}}const Su=pe.define();function Pq(i){let e=i.addToOptions.slice();return i.icons&&e.push({render(t){let n=document.createElement("div");return n.classList.add("cm-completionIcon"),t.type&&n.classList.add(...t.type.split(/\s+/g).map(a=>"cm-completionIcon-"+a)),n.setAttribute("aria-hidden","true"),n},position:20}),e.push({render(t,n,a,s){let r=document.createElement("span");r.className="cm-completionLabel";let o=t.displayLabel||t.label,l=0;for(let u=0;ul&&r.appendChild(document.createTextNode(o.slice(l,m)));let d=r.appendChild(document.createElement("span"));d.appendChild(document.createTextNode(o.slice(m,c))),d.className="cm-completionMatchedText",l=c}return lt.position-n.position).map(t=>t.render)}function jo(i,e,t){if(i<=t)return{from:0,to:i};if(e<0&&(e=0),e<=i>>1){let a=Math.floor(e/t);return{from:a*t,to:(a+1)*t}}let n=Math.ceil((i-e)/t);return{from:i-n*t,to:i-(n-1)*t}}class Oq{constructor(e,t,n){this.view=e,this.stateField=t,this.applyCompletion=n,this.info=null,this.infoDestroy=null,this.placeInfoReq={read:()=>this.measureInfo(),write:l=>this.placeInfo(l),key:this},this.space=null,this.currentClass="";let a=e.state.field(t),{options:s,selected:r}=a.open,o=e.state.facet(Ve);this.optionContent=Pq(o),this.optionClass=o.optionClass,this.tooltipClass=o.tooltipClass,this.range=jo(s.length,r,o.maxRenderedOptions),this.dom=document.createElement("div"),this.dom.className="cm-tooltip-autocomplete",this.updateTooltipClass(e.state),this.dom.addEventListener("mousedown",l=>{let{options:u}=e.state.field(t).open;for(let m=l.target,c;m&&m!=this.dom;m=m.parentNode)if(m.nodeName=="LI"&&(c=/-(\d+)$/.exec(m.id))&&+c[1]this.list.lastChild.getBoundingClientRect().bottom?this.range.to:null;m!=null&&(e.dispatch({effects:Su.of(m)}),l.preventDefault())}}),this.dom.addEventListener("focusout",l=>{let u=e.state.field(this.stateField,!1);u&&u.tooltip&&e.state.facet(Ve).closeOnBlur&&l.relatedTarget!=e.contentDOM&&e.dispatch({effects:ia.of(null)})}),this.showOptions(s,a.id)}mount(){this.updateSel()}showOptions(e,t){this.list&&this.list.remove(),this.list=this.dom.appendChild(this.createListBox(e,t,this.range)),this.list.addEventListener("scroll",()=>{this.info&&this.view.requestMeasure(this.placeInfoReq)})}update(e){var t;let n=e.state.field(this.stateField),a=e.startState.field(this.stateField);if(this.updateTooltipClass(e.state),n!=a){let{options:s,selected:r,disabled:o}=n.open;(!a.open||a.open.options!=s)&&(this.range=jo(s.length,r,e.state.facet(Ve).maxRenderedOptions),this.showOptions(s,n.id)),this.updateSel(),o!=((t=a.open)===null||t===void 0?void 0:t.disabled)&&this.dom.classList.toggle("cm-tooltip-autocomplete-disabled",!!o)}}updateTooltipClass(e){let t=this.tooltipClass(e);if(t!=this.currentClass){for(let n of this.currentClass.split(" "))n&&this.dom.classList.remove(n);for(let n of t.split(" "))n&&this.dom.classList.add(n);this.currentClass=t}}positioned(e){this.space=e,this.info&&this.view.requestMeasure(this.placeInfoReq)}updateSel(){let e=this.view.state.field(this.stateField),t=e.open;(t.selected>-1&&t.selected=this.range.to)&&(this.range=jo(t.options.length,t.selected,this.view.state.facet(Ve).maxRenderedOptions),this.showOptions(t.options,e.id));let n=this.updateSelectedOption(t.selected);if(n){this.destroyInfo();let{completion:a}=t.options[t.selected],{info:s}=a;if(!s)return;let r=typeof s=="string"?document.createTextNode(s):s(a);if(!r)return;"then"in r?r.then(o=>{o&&this.view.state.field(this.stateField,!1)==e&&this.addInfoPane(o,a)}).catch(o=>St(this.view.state,o,"completion info")):(this.addInfoPane(r,a),n.setAttribute("aria-describedby",this.info.id))}}addInfoPane(e,t){this.destroyInfo();let n=this.info=document.createElement("div");if(n.className="cm-tooltip cm-completionInfo",n.id="cm-completionInfo-"+Math.floor(Math.random()*65535).toString(16),e.nodeType!=null)n.appendChild(e),this.infoDestroy=null;else{let{dom:a,destroy:s}=e;n.appendChild(a),this.infoDestroy=s||null}this.dom.appendChild(n),this.view.requestMeasure(this.placeInfoReq)}updateSelectedOption(e){let t=null;for(let n=this.list.firstChild,a=this.range.from;n;n=n.nextSibling,a++)n.nodeName!="LI"||!n.id?a--:a==e?n.hasAttribute("aria-selected")||(n.setAttribute("aria-selected","true"),t=n):n.hasAttribute("aria-selected")&&(n.removeAttribute("aria-selected"),n.removeAttribute("aria-describedby"));return t&&Zq(this.list,t),t}measureInfo(){let e=this.dom.querySelector("[aria-selected]");if(!e||!this.info)return null;let t=this.dom.getBoundingClientRect(),n=this.info.getBoundingClientRect(),a=e.getBoundingClientRect(),s=this.space;if(!s){let r=this.dom.ownerDocument.documentElement;s={left:0,top:0,right:r.clientWidth,bottom:r.clientHeight}}return a.top>Math.min(s.bottom,t.bottom)-10||a.bottom{r.target==a&&r.preventDefault()});let s=null;for(let r=n.from;rn.from||n.from==0))if(s=d,typeof u!="string"&&u.header)a.appendChild(u.header(u));else{let h=a.appendChild(document.createElement("completion-section"));h.textContent=d}}const m=a.appendChild(document.createElement("li"));m.id=t+"-"+r,m.setAttribute("role","option");let c=this.optionClass(o);c&&(m.className=c);for(let d of this.optionContent){let h=d(o,this.view.state,this.view,l);h&&m.appendChild(h)}}return n.from&&a.classList.add("cm-completionListIncompleteTop"),n.tonew Oq(t,i,e)}function Zq(i,e){let t=i.getBoundingClientRect(),n=e.getBoundingClientRect(),a=t.height/i.offsetHeight;n.topt.bottom&&(i.scrollTop+=(n.bottom-t.bottom)/a)}function kd(i){return(i.boost||0)*100+(i.apply?10:0)+(i.info?5:0)+(i.type?1:0)}function Lq(i,e){let t=[],n=null,a=null,s=m=>{t.push(m);let{section:c}=m.completion;if(c){n||(n=[]);let d=typeof c=="string"?c:c.name;n.some(h=>h.name==d)||n.push(typeof c=="string"?{name:d}:c)}},r=e.facet(Ve);for(let m of i)if(m.hasResult()){let c=m.result.getMatch;if(m.result.filter===!1)for(let d of m.result.options)s(new yd(d,m.source,c?c(d):[],1e9-t.length));else{let d=e.sliceDoc(m.from,m.to),h,g=r.filterStrict?new Dq(d):new Cq(d);for(let f of m.result.options)if(h=g.match(f.label)){let b=f.displayLabel?c?c(f,h.matched):[]:h.matched,x=h.score+(f.boost||0);if(s(new yd(f,m.source,b,x)),typeof f.section=="object"&&f.section.rank==="dynamic"){let{name:v}=f.section;a||(a=Object.create(null)),a[v]=Math.max(x,a[v]||-1e9)}}}}if(n){let m=Object.create(null),c=0,d=(h,g)=>(h.rank==="dynamic"&&g.rank==="dynamic"?a[g.name]-a[h.name]:0)||(typeof h.rank=="number"?h.rank:1e9)-(typeof g.rank=="number"?g.rank:1e9)||(h.named.score-c.score||u(c.completion,d.completion))){let c=m.completion;!l||l.label!=c.label||l.detail!=c.detail||l.type!=null&&c.type!=null&&l.type!=c.type||l.apply!=c.apply||l.boost!=c.boost?o.push(m):kd(m.completion)>kd(l)&&(o[o.length-1]=m),l=m.completion}return o}class Z1{constructor(e,t,n,a,s,r){this.options=e,this.attrs=t,this.tooltip=n,this.timestamp=a,this.selected=s,this.disabled=r}setSelected(e,t){return e==this.selected||e>=this.options.length?this:new Z1(this.options,Sd(t,e),this.tooltip,this.timestamp,e,this.disabled)}static build(e,t,n,a,s,r){if(a&&!r&&e.some(u=>u.isPending))return a.setDisabled();let o=Lq(e,t);if(!o.length)return a&&e.some(u=>u.isPending)?a.setDisabled():null;let l=t.facet(Ve).selectOnOpen?0:-1;if(a&&a.selected!=l&&a.selected!=-1){let u=a.options[a.selected].completion;for(let m=0;mm.hasResult()?Math.min(u,m.from):u,1e8),create:Jq,above:s.aboveCursor},a?a.timestamp:Date.now(),l,!1)}map(e){return new Z1(this.options,this.attrs,{...this.tooltip,pos:e.mapPos(this.tooltip.pos)},this.timestamp,this.selected,this.disabled)}setDisabled(){return new Z1(this.options,this.attrs,this.tooltip,this.timestamp,this.selected,!0)}}class mr{constructor(e,t,n){this.active=e,this.id=t,this.open=n}static start(){return new mr(Fq,"cm-ac-"+Math.floor(Math.random()*2e6).toString(36),null)}update(e){let{state:t}=e,n=t.facet(Ve),s=(n.override||t.languageDataAt("autocomplete",h1(t)).map(jq)).map(l=>(this.active.find(m=>m.source==l)||new Lt(l,this.active.some(m=>m.state!=0)?1:0)).update(e,n));s.length==this.active.length&&s.every((l,u)=>l==this.active[u])&&(s=this.active);let r=this.open,o=e.effects.some(l=>l.is(Tu));r&&e.docChanged&&(r=r.map(e.changes)),e.selection||s.some(l=>l.hasResult()&&e.changes.touchesRange(l.from,l.to))||!Wq(s,this.active)||o?r=Z1.build(s,t,this.id,r,n,o):r&&r.disabled&&!s.some(l=>l.isPending)&&(r=null),!r&&s.every(l=>!l.isPending)&&s.some(l=>l.hasResult())&&(s=s.map(l=>l.hasResult()?new Lt(l.source,0):l));for(let l of e.effects)l.is(Su)&&(r=r&&r.setSelected(l.value,this.id));return s==this.active&&r==this.open?this:new mr(s,this.id,r)}get tooltip(){return this.open?this.open.tooltip:null}get attrs(){return this.open?this.open.attrs:this.active.length?Eq:Nq}}function Wq(i,e){if(i==e)return!0;for(let t=0,n=0;;){for(;t-1&&(t["aria-activedescendant"]=i+"-"+e),t}const Fq=[];function O2(i,e){if(i.isUserEvent("input.complete")){let n=i.annotation(ku);if(n&&e.activateOnCompletion(n))return 12}let t=i.isUserEvent("input.type");return t&&e.activateOnTyping?5:t?1:i.isUserEvent("delete.backward")?2:i.selection?8:i.docChanged?16:0}class Lt{constructor(e,t,n=!1){this.source=e,this.state=t,this.explicit=n}hasResult(){return!1}get isPending(){return this.state==1}update(e,t){let n=O2(e,t),a=this;(n&8||n&16&&this.touches(e))&&(a=new Lt(a.source,0)),n&4&&a.state==0&&(a=new Lt(this.source,1)),a=a.updateFor(e,n);for(let s of e.effects)if(s.is(ur))a=new Lt(a.source,1,s.value);else if(s.is(ia))a=new Lt(a.source,0);else if(s.is(Tu))for(let r of s.value)r.source==a.source&&(a=r);return a}updateFor(e,t){return this.map(e.changes)}map(e){return this}touches(e){return e.changes.touchesRange(h1(e.state))}}class R1 extends Lt{constructor(e,t,n,a,s,r){super(e,3,t),this.limit=n,this.result=a,this.from=s,this.to=r}hasResult(){return!0}updateFor(e,t){var n;if(!(t&3))return this.map(e.changes);let a=this.result;a.map&&!e.changes.empty&&(a=a.map(a,e.changes));let s=e.changes.mapPos(this.from),r=e.changes.mapPos(this.to,1),o=h1(e.state);if(o>r||!a||t&2&&(h1(e.startState)==this.from||ot.map(e))}}),xt=Nt.define({create(){return mr.start()},update(i,e){return i.update(e)},provide:i=>[h2.from(i,e=>e.tooltip),$.contentAttributes.from(i,e=>e.attrs)]});function _u(i,e){const t=e.completion.apply||e.completion.label;let n=i.state.field(xt).active.find(a=>a.source==e.source);return n instanceof R1?(typeof t=="string"?i.dispatch({...Aq(i.state,t,n.from,n.to),annotations:ku.of(e.completion)}):t(i,e.completion,n.from,n.to),!0):!1}const Jq=Mq(xt,_u);function ts(i,e="option"){return t=>{let n=t.state.field(xt,!1);if(!n||!n.open||n.open.disabled||Date.now()-n.open.timestamp-1?n.open.selected+a*(i?1:-1):i?0:r-1;return o<0?o=e=="page"?0:r-1:o>=r&&(o=e=="page"?r-1:0),t.dispatch({effects:Su.of(o)}),!0}}const Gq=i=>{let e=i.state.field(xt,!1);return i.state.readOnly||!e||!e.open||e.open.selected<0||e.open.disabled||Date.now()-e.open.timestampi.state.field(xt,!1)?(i.dispatch({effects:ur.of(!0)}),!0):!1,Hq=i=>{let e=i.state.field(xt,!1);return!e||!e.active.some(t=>t.state!=0)?!1:(i.dispatch({effects:ia.of(null)}),!0)};class Vq{constructor(e,t){this.active=e,this.context=t,this.time=Date.now(),this.updates=[],this.done=void 0}}const qq=50,Uq=1e3,Qq=Et.fromClass(class{constructor(i){this.view=i,this.debounceUpdate=-1,this.running=[],this.debounceAccept=-1,this.pendingStart=!1,this.composing=0;for(let e of i.state.field(xt).active)e.isPending&&this.startQuery(e)}update(i){let e=i.state.field(xt),t=i.state.facet(Ve);if(!i.selectionSet&&!i.docChanged&&i.startState.field(xt)==e)return;let n=i.transactions.some(s=>{let r=O2(s,t);return r&8||(s.selection||s.docChanged)&&!(r&3)});for(let s=0;sqq&&Date.now()-r.time>Uq){for(let o of r.context.abortListeners)try{o()}catch(l){St(this.view.state,l)}r.context.abortListeners=null,this.running.splice(s--,1)}else r.updates.push(...i.transactions)}this.debounceUpdate>-1&&clearTimeout(this.debounceUpdate),i.transactions.some(s=>s.effects.some(r=>r.is(ur)))&&(this.pendingStart=!0);let a=this.pendingStart?50:t.activateOnTypingDelay;if(this.debounceUpdate=e.active.some(s=>s.isPending&&!this.running.some(r=>r.active.source==s.source))?setTimeout(()=>this.startUpdate(),a):-1,this.composing!=0)for(let s of i.transactions)s.isUserEvent("input.type")?this.composing=2:this.composing==2&&s.selection&&(this.composing=3)}startUpdate(){this.debounceUpdate=-1,this.pendingStart=!1;let{state:i}=this.view,e=i.field(xt);for(let t of e.active)t.isPending&&!this.running.some(n=>n.active.source==t.source)&&this.startQuery(t);this.running.length&&e.open&&e.open.disabled&&(this.debounceAccept=setTimeout(()=>this.accept(),this.view.state.facet(Ve).updateSyncTime))}startQuery(i){let{state:e}=this.view,t=h1(e),n=new B2(e,t,i.explicit,this.view),a=new Vq(i,n);this.running.push(a),Promise.resolve(i.source(n)).then(s=>{a.context.aborted||(a.done=s||null,this.scheduleAccept())},s=>{this.view.dispatch({effects:ia.of(null)}),St(this.view.state,s)})}scheduleAccept(){this.running.every(i=>i.done!==void 0)?this.accept():this.debounceAccept<0&&(this.debounceAccept=setTimeout(()=>this.accept(),this.view.state.facet(Ve).updateSyncTime))}accept(){var i;this.debounceAccept>-1&&clearTimeout(this.debounceAccept),this.debounceAccept=-1;let e=[],t=this.view.state.facet(Ve),n=this.view.state.field(xt);for(let a=0;ao.source==s.active.source);if(r&&r.isPending)if(s.done==null){let o=new Lt(s.active.source,0);for(let l of s.updates)o=o.update(l,t);o.isPending||e.push(o)}else this.startQuery(r)}(e.length||n.open&&n.open.disabled)&&this.view.dispatch({effects:Tu.of(e)})}},{eventHandlers:{blur(i){let e=this.view.state.field(xt,!1);if(e&&e.tooltip&&this.view.state.facet(Ve).closeOnBlur){let t=e.open&&g2(this.view,e.open.tooltip);(!t||!t.dom.contains(i.relatedTarget))&&setTimeout(()=>this.view.dispatch({effects:ia.of(null)}),10)}},compositionstart(){this.composing=1},compositionend(){this.composing==3&&setTimeout(()=>this.view.dispatch({effects:ur.of(!1)}),20),this.composing=0}}}),$q=typeof navigator=="object"&&/Win/.test(navigator.platform),Yq=f1.highest($.domEventHandlers({keydown(i,e){let t=e.state.field(xt,!1);if(!t||!t.open||t.open.disabled||t.open.selected<0||!i.key||i.key.length>1||i.ctrlKey&&!($q&&i.altKey)||i.metaKey)return!1;let n=t.open.options[t.open.selected],a=t.active.find(r=>r.source==n.source),s=n.completion.commitCharacters||a.result.commitCharacters;return s&&s.indexOf(i.key)>-1&&_u(e,n),!1}})),M2=$.baseTheme({".cm-tooltip.cm-tooltip-autocomplete":{"& > ul":{fontFamily:"monospace",whiteSpace:"nowrap",overflow:"hidden auto",maxWidth_fallback:"700px",maxWidth:"min(700px, 95vw)",minWidth:"250px",maxHeight:"10em",height:"100%",listStyle:"none",margin:0,padding:0,"& > li, & > completion-section":{padding:"1px 3px",lineHeight:1.2},"& > li":{overflowX:"hidden",textOverflow:"ellipsis",cursor:"pointer"},"& > completion-section":{display:"list-item",borderBottom:"1px solid silver",paddingLeft:"0.5em",opacity:.7}}},"&light .cm-tooltip-autocomplete ul li[aria-selected]":{background:"#17c",color:"white"},"&light .cm-tooltip-autocomplete-disabled ul li[aria-selected]":{background:"#777"},"&dark .cm-tooltip-autocomplete ul li[aria-selected]":{background:"#347",color:"white"},"&dark .cm-tooltip-autocomplete-disabled ul li[aria-selected]":{background:"#444"},".cm-completionListIncompleteTop:before, .cm-completionListIncompleteBottom:after":{content:'"···"',opacity:.5,display:"block",textAlign:"center",cursor:"pointer"},".cm-tooltip.cm-completionInfo":{position:"absolute",padding:"3px 9px",width:"max-content",maxWidth:"400px",boxSizing:"border-box",whiteSpace:"pre-line"},".cm-completionInfo.cm-completionInfo-left":{right:"100%"},".cm-completionInfo.cm-completionInfo-right":{left:"100%"},".cm-completionInfo.cm-completionInfo-left-narrow":{right:"30px"},".cm-completionInfo.cm-completionInfo-right-narrow":{left:"30px"},"&light .cm-snippetField":{backgroundColor:"#00000022"},"&dark .cm-snippetField":{backgroundColor:"#ffffff22"},".cm-snippetFieldPosition":{verticalAlign:"text-top",width:0,height:"1.15em",display:"inline-block",margin:"0 -0.7px -.7em",borderLeft:"1.4px dotted #888"},".cm-completionMatchedText":{textDecoration:"underline"},".cm-completionDetail":{marginLeft:"0.5em",fontStyle:"italic"},".cm-completionIcon":{fontSize:"90%",width:".8em",display:"inline-block",textAlign:"center",paddingRight:".6em",opacity:"0.6",boxSizing:"content-box"},".cm-completionIcon-function, .cm-completionIcon-method":{"&:after":{content:"'ƒ'"}},".cm-completionIcon-class":{"&:after":{content:"'○'"}},".cm-completionIcon-interface":{"&:after":{content:"'◌'"}},".cm-completionIcon-variable":{"&:after":{content:"'𝑥'"}},".cm-completionIcon-constant":{"&:after":{content:"'𝐶'"}},".cm-completionIcon-type":{"&:after":{content:"'𝑡'"}},".cm-completionIcon-enum":{"&:after":{content:"'∪'"}},".cm-completionIcon-property":{"&:after":{content:"'□'"}},".cm-completionIcon-keyword":{"&:after":{content:"'🔑︎'"}},".cm-completionIcon-namespace":{"&:after":{content:"'▢'"}},".cm-completionIcon-text":{"&:after":{content:"'abc'",fontSize:"50%",verticalAlign:"middle"}}});class Kq{constructor(e,t,n,a){this.field=e,this.line=t,this.from=n,this.to=a}}class Au{constructor(e,t,n){this.field=e,this.from=t,this.to=n}map(e){let t=e.mapPos(this.from,-1,lt.TrackDel),n=e.mapPos(this.to,1,lt.TrackDel);return t==null||n==null?null:new Au(this.field,t,n)}}class ju{constructor(e,t){this.lines=e,this.fieldPositions=t}instantiate(e,t){let n=[],a=[t],s=e.doc.lineAt(t),r=/^\s*/.exec(s.text)[0];for(let l of this.lines){if(n.length){let u=r,m=/^\t*/.exec(l)[0].length;for(let c=0;cnew Au(l.field,a[l.line]+l.from,a[l.line]+l.to));return{text:n,ranges:o}}static parse(e){let t=[],n=[],a=[],s;for(let r of e.split(/\r\n?|\n/)){for(;s=/[#$]\{(?:(\d+)(?::([^{}]*))?|((?:\\[{}]|[^{}])*))\}/.exec(r);){let o=s[1]?+s[1]:null,l=s[2]||s[3]||"",u=-1;o===0&&(o=1e9);let m=l.replace(/\\[{}]/g,c=>c[1]);for(let c=0;c=u&&d.field++}for(let c of a)if(c.line==n.length&&c.from>s.index){let d=s[2]?3+(s[1]||"").length:2;c.from-=d,c.to-=d}a.push(new Kq(u,n.length,s.index,s.index+m.length)),r=r.slice(0,s.index)+l+r.slice(s.index+s[0].length)}r=r.replace(/\\([{}])/g,(o,l,u)=>{for(let m of a)m.line==n.length&&m.from>u&&(m.from--,m.to--);return l}),n.push(r)}return new ju(n,a)}}let Xq=fe.widget({widget:new class extends X1{toDOM(){let i=document.createElement("span");return i.className="cm-snippetFieldPosition",i}ignoreEvent(){return!1}}}),eU=fe.mark({class:"cm-snippetField"});class ei{constructor(e,t){this.ranges=e,this.active=t,this.deco=fe.set(e.map(n=>(n.from==n.to?Xq:eU).range(n.from,n.to)),!0)}map(e){let t=[];for(let n of this.ranges){let a=n.map(e);if(!a)return null;t.push(a)}return new ei(t,this.active)}selectionInsideField(e){return e.ranges.every(t=>this.ranges.some(n=>n.field==this.active&&n.from<=t.from&&n.to>=t.to))}}const fa=pe.define({map(i,e){return i&&i.map(e)}}),tU=pe.define(),aa=Nt.define({create(){return null},update(i,e){for(let t of e.effects){if(t.is(fa))return t.value;if(t.is(tU)&&i)return new ei(i.ranges,t.value)}return i&&e.docChanged&&(i=i.map(e.changes)),i&&e.selection&&!i.selectionInsideField(e.selection)&&(i=null),i},provide:i=>$.decorations.from(i,e=>e?e.deco:fe.none)});function Cu(i,e){return O.create(i.filter(t=>t.field==e).map(t=>O.range(t.from,t.to)))}function nU(i){let e=ju.parse(i);return(t,n,a,s)=>{let{text:r,ranges:o}=e.instantiate(t.state,a),{main:l}=t.state.selection,u={changes:{from:a,to:s==l.from?l.to:s,insert:he.of(r)},scrollIntoView:!0,annotations:n?[ku.of(n),Ke.userEvent.of("input.complete")]:void 0};if(o.length&&(u.selection=Cu(o,0)),o.some(m=>m.field>0)){let m=new ei(o,0),c=u.effects=[fa.of(m)];t.state.field(aa,!1)===void 0&&c.push(pe.appendConfig.of([aa,oU,lU,M2]))}t.dispatch(t.state.update(u))}}function Z2(i){return({state:e,dispatch:t})=>{let n=e.field(aa,!1);if(!n||i<0&&n.active==0)return!1;let a=n.active+i,s=i>0&&!n.ranges.some(r=>r.field==a+i);return t(e.update({selection:Cu(n.ranges,a),effects:fa.of(s?null:new ei(n.ranges,a)),scrollIntoView:!0})),!0}}const iU=({state:i,dispatch:e})=>i.field(aa,!1)?(e(i.update({effects:fa.of(null)})),!0):!1,aU=Z2(1),sU=Z2(-1),rU=[{key:"Tab",run:aU,shift:sU},{key:"Escape",run:iU}],Td=Q.define({combine(i){return i.length?i[0]:rU}}),oU=f1.highest(Xi.compute([Td],i=>i.facet(Td)));function Du(i,e){return{...e,apply:nU(i)}}const lU=$.domEventHandlers({mousedown(i,e){let t=e.state.field(aa,!1),n;if(!t||(n=e.posAtCoords({x:i.clientX,y:i.clientY}))==null)return!1;let a=t.ranges.find(s=>s.from<=n&&s.to>=n);return!a||a.field==t.active?!1:(e.dispatch({selection:Cu(t.ranges,a.field),effects:fa.of(t.ranges.some(s=>s.field>a.field)?new ei(t.ranges,a.field):null),scrollIntoView:!0}),!0)}}),sa={brackets:["(","[","{","'",'"'],before:")]}:;>",stringPrefixes:[]},a1=pe.define({map(i,e){let t=e.mapPos(i,-1,lt.TrackAfter);return t??void 0}}),Bu=new class extends Fn{};Bu.startSide=1;Bu.endSide=-1;const L2=Nt.define({create(){return oe.empty},update(i,e){if(i=i.map(e.changes),e.selection){let t=e.state.doc.lineAt(e.selection.main.head);i=i.update({filter:n=>n>=t.from&&n<=t.to})}for(let t of e.effects)t.is(a1)&&(i=i.update({add:[Bu.range(t.value,t.value+1)]}));return i}});function uU(){return[cU,L2]}const Do="()[]{}<>«»»«[]{}";function W2(i){for(let e=0;e{if((mU?i.composing:i.compositionStarted)||i.state.readOnly)return!1;let a=i.state.selection.main;if(n.length>2||n.length==2&&xn(Pt(n,0))==1||e!=a.from||t!=a.to)return!1;let s=gU(i.state,n);return s?(i.dispatch(s),!0):!1}),dU=({state:i,dispatch:e})=>{if(i.readOnly)return!1;let n=E2(i,i.selection.main.head).brackets||sa.brackets,a=null,s=i.changeByRange(r=>{if(r.empty){let o=pU(i.doc,r.head);for(let l of n)if(l==o&&Nr(i.doc,r.head)==W2(Pt(l,0)))return{changes:{from:r.head-l.length,to:r.head+l.length},range:O.cursor(r.head-l.length)}}return{range:a=r}});return a||e(i.update(s,{scrollIntoView:!0,userEvent:"delete.backward"})),!a},hU=[{key:"Backspace",run:dU}];function gU(i,e){let t=E2(i,i.selection.main.head),n=t.brackets||sa.brackets;for(let a of n){let s=W2(Pt(a,0));if(e==a)return s==a?xU(i,a,n.indexOf(a+a+a)>-1,t):fU(i,a,s,t.before||sa.before);if(e==s&&N2(i,i.selection.main.from))return bU(i,a,s)}return null}function N2(i,e){let t=!1;return i.field(L2).between(0,i.doc.length,n=>{n==e&&(t=!0)}),t}function Nr(i,e){let t=i.sliceString(e,e+2);return t.slice(0,xn(Pt(t,0)))}function pU(i,e){let t=i.sliceString(e-2,e);return xn(Pt(t,0))==t.length?t:t.slice(1)}function fU(i,e,t,n){let a=null,s=i.changeByRange(r=>{if(!r.empty)return{changes:[{insert:e,from:r.from},{insert:t,from:r.to}],effects:a1.of(r.to+e.length),range:O.range(r.anchor+e.length,r.head+e.length)};let o=Nr(i.doc,r.head);return!o||/\s/.test(o)||n.indexOf(o)>-1?{changes:{insert:e+t,from:r.head},effects:a1.of(r.head+e.length),range:O.cursor(r.head+e.length)}:{range:a=r}});return a?null:i.update(s,{scrollIntoView:!0,userEvent:"input.type"})}function bU(i,e,t){let n=null,a=i.changeByRange(s=>s.empty&&Nr(i.doc,s.head)==t?{changes:{from:s.head,to:s.head+t.length,insert:t},range:O.cursor(s.head+t.length)}:n={range:s});return n?null:i.update(a,{scrollIntoView:!0,userEvent:"input.type"})}function xU(i,e,t,n){let a=n.stringPrefixes||sa.stringPrefixes,s=null,r=i.changeByRange(o=>{if(!o.empty)return{changes:[{insert:e,from:o.from},{insert:e,from:o.to}],effects:a1.of(o.to+e.length),range:O.range(o.anchor+e.length,o.head+e.length)};let l=o.head,u=Nr(i.doc,l),m;if(u==e){if(_d(i,l))return{changes:{insert:e+e,from:l},effects:a1.of(l+e.length),range:O.cursor(l+e.length)};if(N2(i,l)){let d=t&&i.sliceDoc(l,l+e.length*3)==e+e+e?e+e+e:e;return{changes:{from:l,to:l+d.length,insert:d},range:O.cursor(l+d.length)}}}else{if(t&&i.sliceDoc(l-2*e.length,l)==e+e&&(m=Ad(i,l-2*e.length,a))>-1&&_d(i,m))return{changes:{insert:e+e+e+e,from:l},effects:a1.of(l+e.length),range:O.cursor(l+e.length)};if(i.charCategorizer(l)(u)!=Mt.Word&&Ad(i,l,a)>-1&&!vU(i,l,e,a))return{changes:{insert:e+e,from:l},effects:a1.of(l+e.length),range:O.cursor(l+e.length)}}return{range:s=o}});return s?null:i.update(r,{scrollIntoView:!0,userEvent:"input.type"})}function _d(i,e){let t=et(i).resolveInner(e+1);return t.parent&&t.from==e}function vU(i,e,t,n){let a=et(i).resolveInner(e,-1),s=n.reduce((r,o)=>Math.max(r,o.length),0);for(let r=0;r<5;r++){let o=i.sliceDoc(a.from,Math.min(a.to,a.from+t.length+s)),l=o.indexOf(t);if(!l||l>-1&&n.indexOf(o.slice(0,l))>-1){let m=a.firstChild;for(;m&&m.from==a.from&&m.to-m.from>t.length+l;){if(i.sliceDoc(m.to-t.length,m.to)==t)return!1;m=m.firstChild}return!0}let u=a.to==e&&a.parent;if(!u)break;a=u}return!1}function Ad(i,e,t){let n=i.charCategorizer(e);if(n(i.sliceDoc(e-1,e))!=Mt.Word)return e;for(let a of t){let s=e-a.length;if(i.sliceDoc(s,e)==a&&n(i.sliceDoc(s-1,s))!=Mt.Word)return s}return-1}function wU(i={}){return[Yq,xt,Ve.of(i),Qq,yU,M2]}const F2=[{key:"Ctrl-Space",run:Co},{mac:"Alt-`",run:Co},{mac:"Alt-i",run:Co},{key:"Escape",run:Hq},{key:"ArrowDown",run:ts(!0)},{key:"ArrowUp",run:ts(!1)},{key:"PageDown",run:ts(!0,"page")},{key:"PageUp",run:ts(!1,"page")},{key:"Enter",run:Gq}],yU=f1.highest(Xi.computeN([Ve],i=>i.facet(Ve).defaultKeymap?[F2]:[])),zU=i=>{let{state:e}=i,t=e.doc.lineAt(e.selection.main.from),n=Ou(i.state,t.from);return n.line?IU(i):n.block?SU(i):!1};function Pu(i,e){return({state:t,dispatch:n})=>{if(t.readOnly)return!1;let a=i(e,t);return a?(n(t.update(a)),!0):!1}}const IU=Pu(AU,0),kU=Pu(R2,0),SU=Pu((i,e)=>R2(i,e,_U(e)),0);function Ou(i,e){let t=i.languageDataAt("commentTokens",e,1);return t.length?t[0]:{}}const di=50;function TU(i,{open:e,close:t},n,a){let s=i.sliceDoc(n-di,n),r=i.sliceDoc(a,a+di),o=/\s*$/.exec(s)[0].length,l=/^\s*/.exec(r)[0].length,u=s.length-o;if(s.slice(u-e.length,u)==e&&r.slice(l,l+t.length)==t)return{open:{pos:n-o,margin:o&&1},close:{pos:a+l,margin:l&&1}};let m,c;a-n<=2*di?m=c=i.sliceDoc(n,a):(m=i.sliceDoc(n,n+di),c=i.sliceDoc(a-di,a));let d=/^\s*/.exec(m)[0].length,h=/\s*$/.exec(c)[0].length,g=c.length-h-t.length;return m.slice(d,d+e.length)==e&&c.slice(g,g+t.length)==t?{open:{pos:n+d+e.length,margin:/\s/.test(m.charAt(d+e.length))?1:0},close:{pos:a-h-t.length,margin:/\s/.test(c.charAt(g-1))?1:0}}:null}function _U(i){let e=[];for(let t of i.selection.ranges){let n=i.doc.lineAt(t.from),a=t.to<=n.to?n:i.doc.lineAt(t.to);a.from>n.from&&a.from==t.to&&(a=t.to==n.to+1?n:i.doc.lineAt(t.to-1));let s=e.length-1;s>=0&&e[s].to>n.from?e[s].to=a.to:e.push({from:n.from+/^\s*/.exec(n.text)[0].length,to:a.to})}return e}function R2(i,e,t=e.selection.ranges){let n=t.map(s=>Ou(e,s.from).block);if(!n.every(s=>s))return null;let a=t.map((s,r)=>TU(e,n[r],s.from,s.to));if(i!=2&&!a.every(s=>s))return{changes:e.changes(t.map((s,r)=>a[r]?[]:[{from:s.from,insert:n[r].open+" "},{from:s.to,insert:" "+n[r].close}]))};if(i!=1&&a.some(s=>s)){let s=[];for(let r=0,o;ra&&(s==r||r>c.from)){a=c.from;let d=/^\s*/.exec(c.text)[0].length,h=d==c.length,g=c.text.slice(d,d+u.length)==u?d:-1;ds.comment<0&&(!s.empty||s.single))){let s=[];for(let{line:o,token:l,indent:u,empty:m,single:c}of n)(c||!m)&&s.push({from:o.from+u,insert:l+" "});let r=e.changes(s);return{changes:r,selection:e.selection.map(r,1)}}else if(i!=1&&n.some(s=>s.comment>=0)){let s=[];for(let{line:r,comment:o,token:l}of n)if(o>=0){let u=r.from+o,m=u+l.length;r.text[m-r.from]==" "&&m++,s.push({from:u,to:m})}return{changes:s}}return null}function ti(i,e){return O.create(i.ranges.map(e),i.mainIndex)}function Qt(i,e){return i.update({selection:e,scrollIntoView:!0,userEvent:"select"})}function $t({state:i,dispatch:e},t){let n=ti(i.selection,t);return n.eq(i.selection,!0)?!1:(e(Qt(i,n)),!0)}function Fr(i,e){return O.cursor(e?i.to:i.from)}function J2(i,e){return $t(i,t=>t.empty?i.moveByChar(t,e):Fr(t,e))}function mt(i){return i.textDirectionAt(i.state.selection.main.head)==ze.LTR}const G2=i=>J2(i,!mt(i)),H2=i=>J2(i,mt(i));function V2(i,e){return $t(i,t=>t.empty?i.moveByGroup(t,e):Fr(t,e))}const jU=i=>V2(i,!mt(i)),CU=i=>V2(i,mt(i));function DU(i,e,t){if(e.type.prop(t))return!0;let n=e.to-e.from;return n&&(n>2||/[^\s,.;:]/.test(i.sliceDoc(e.from,e.to)))||e.firstChild}function Rr(i,e,t){let n=et(i).resolveInner(e.head),a=t?le.closedBy:le.openedBy;for(let l=e.head;;){let u=t?n.childAfter(l):n.childBefore(l);if(!u)break;DU(i,u,a)?n=u:l=t?u.to:u.from}let s=n.type.prop(a),r,o;return s&&(r=t?un(i,n.from,1):un(i,n.to,-1))&&r.matched?o=t?r.end.to:r.end.from:o=t?n.to:n.from,O.cursor(o,t?-1:1)}const BU=i=>$t(i,e=>Rr(i.state,e,!mt(i))),PU=i=>$t(i,e=>Rr(i.state,e,mt(i)));function q2(i,e){return $t(i,t=>{if(!t.empty)return Fr(t,e);let n=i.moveVertically(t,e);return n.head!=t.head?n:i.moveToLineBoundary(t,e)})}const U2=i=>q2(i,!1),Q2=i=>q2(i,!0);function $2(i){let e=i.scrollDOM.clientHeightr.empty?i.moveVertically(r,e,t.height):Fr(r,e));if(a.eq(n.selection))return!1;let s;if(t.selfScroll){let r=i.coordsAtPos(n.selection.main.head),o=i.scrollDOM.getBoundingClientRect(),l=o.top+t.marginTop,u=o.bottom-t.marginBottom;r&&r.top>l&&r.bottomY2(i,!1),m0=i=>Y2(i,!0);function qn(i,e,t){let n=i.lineBlockAt(e.head),a=i.moveToLineBoundary(e,t);if(a.head==e.head&&a.head!=(t?n.to:n.from)&&(a=i.moveToLineBoundary(e,t,!1)),!t&&a.head==n.from&&n.length){let s=/^\s*/.exec(i.state.sliceDoc(n.from,Math.min(n.from+100,n.to)))[0].length;s&&e.head!=n.from+s&&(a=O.cursor(n.from+s))}return a}const OU=i=>$t(i,e=>qn(i,e,!0)),MU=i=>$t(i,e=>qn(i,e,!1)),ZU=i=>$t(i,e=>qn(i,e,!mt(i))),LU=i=>$t(i,e=>qn(i,e,mt(i))),WU=i=>$t(i,e=>i.moveToLineBoundary(e,!1,!1)),EU=i=>$t(i,e=>i.moveToLineBoundary(e,!0,!1));function NU(i,e,t){let n=!1,a=ti(i.selection,s=>{let r=un(i,s.head,-1)||un(i,s.head,1)||s.head>0&&un(i,s.head-1,1)||s.headNU(i,e);function Ft(i,e,t){let n=ti(i.state.selection,a=>{a.undirectional&&a.head>=a.anchor!=e&&(a=O.range(a.head,a.anchor));let s=t(a);return O.range(a.anchor,s.head,s.goalColumn,s.bidiLevel||void 0,s.assoc)});return n.eq(i.state.selection)?!1:(i.dispatch(Qt(i.state,n)),!0)}function K2(i,e){return Ft(i,e,t=>i.moveByChar(t,e))}const X2=i=>K2(i,!mt(i)),e3=i=>K2(i,mt(i));function t3(i,e){return Ft(i,e,t=>i.moveByGroup(t,e))}const RU=i=>t3(i,!mt(i)),JU=i=>t3(i,mt(i)),GU=i=>{let e=!mt(i);return Ft(i,e,t=>Rr(i.state,t,e))},HU=i=>{let e=mt(i);return Ft(i,e,t=>Rr(i.state,t,e))};function n3(i,e){return Ft(i,e,t=>i.moveVertically(t,e))}const i3=i=>n3(i,!1),a3=i=>n3(i,!0);function s3(i,e){return Ft(i,e,t=>i.moveVertically(t,e,$2(i).height))}const Cd=i=>s3(i,!1),Dd=i=>s3(i,!0),VU=i=>Ft(i,!0,e=>qn(i,e,!0)),qU=i=>Ft(i,!1,e=>qn(i,e,!1)),UU=i=>{let e=!mt(i);return Ft(i,e,t=>qn(i,t,e))},QU=i=>{let e=mt(i);return Ft(i,e,t=>qn(i,t,e))},$U=i=>Ft(i,!1,e=>O.cursor(i.lineBlockAt(e.head).from)),YU=i=>Ft(i,!0,e=>O.cursor(i.lineBlockAt(e.head).to)),Bd=({state:i,dispatch:e})=>(e(Qt(i,{anchor:0})),!0),Pd=({state:i,dispatch:e})=>(e(Qt(i,{anchor:i.doc.length})),!0),Od=({state:i,dispatch:e})=>(e(Qt(i,{anchor:i.selection.main.anchor,head:0})),!0),Md=({state:i,dispatch:e})=>(e(Qt(i,{anchor:i.selection.main.anchor,head:i.doc.length})),!0),KU=({state:i,dispatch:e})=>(e(i.update({selection:{anchor:0,head:i.doc.length},userEvent:"select"})),!0),XU=({state:i,dispatch:e})=>{let t=Jr(i).map(({from:n,to:a})=>O.undirectionalRange(n,Math.min(a+1,i.doc.length)));return e(i.update({selection:O.create(t),userEvent:"select"})),!0},eQ=({state:i,dispatch:e})=>{let t=ti(i.selection,n=>{let a=et(i),s=a.resolveStack(n.from,1);if(n.empty){let r=a.resolveStack(n.from,-1);r.node.from>=s.node.from&&r.node.to<=s.node.to&&(s=r)}for(let r=s;r;r=r.next){let{node:o}=r;if((o.from=n.to||o.to>n.to&&o.from<=n.from)&&r.next)return O.undirectionalRange(o.from,o.to)}return n});return t.eq(i.selection)?!1:(e(Qt(i,t)),!0)};function r3(i,e){let{state:t}=i,n=t.selection,a=t.selection.ranges.slice();for(let s of t.selection.ranges){let r=t.doc.lineAt(s.head);if(e?r.to0)for(let o=s;;){let l=i.moveVertically(o,e);if(l.headr.to){a.some(u=>u.head==l.head)||a.push(l);break}else{if(l.head==o.head)break;o=l}}}return a.length==n.ranges.length?!1:(i.dispatch(Qt(t,O.create(a,a.length-1))),!0)}const tQ=i=>r3(i,!1),nQ=i=>r3(i,!0),iQ=({state:i,dispatch:e})=>{let t=i.selection,n=null;return t.ranges.length>1?n=O.create([t.main]):t.main.empty||(n=O.create([O.cursor(t.main.head)])),n?(e(Qt(i,n)),!0):!1};function ba(i,e){if(i.state.readOnly)return!1;let t="delete.selection",{state:n}=i,a=n.changeByRange(s=>{let{from:r,to:o}=s;if(r==o){let l=e(s);lr&&(t="delete.forward",l=ns(i,l,!0)),r=Math.min(r,l),o=Math.max(o,l)}else r=ns(i,r,!1),o=ns(i,o,!0);return r==o?{range:s}:{changes:{from:r,to:o},range:O.cursor(r,ra(i)))n.between(e,e,(a,s)=>{ae&&(e=t?s:a)});return e}const o3=(i,e,t)=>ba(i,n=>{let a=n.from,{state:s}=i,r=s.doc.lineAt(a),o,l;if(t&&!e&&a>r.from&&ao3(i,!1,!0),l3=i=>o3(i,!0,!1),u3=(i,e)=>ba(i,t=>{let n=t.head,{state:a}=i,s=a.doc.lineAt(n),r=a.charCategorizer(n);for(let o=null;;){if(n==(e?s.to:s.from)){n==t.head&&s.number!=(e?a.doc.lines:1)&&(n+=e?1:-1);break}let l=ut(s.text,n-s.from,e)+s.from,u=s.text.slice(Math.min(n,l)-s.from,Math.max(n,l)-s.from),m=r(u);if(o!=null&&m!=o)break;(u!=" "||n!=t.head)&&(o=m),n=l}return n}),m3=i=>u3(i,!1),aQ=i=>u3(i,!0),sQ=i=>ba(i,e=>{let t=i.lineBlockAt(e.head).to;return e.headba(i,e=>{let t=i.moveToLineBoundary(e,!1).head;return e.head>t?t:Math.max(0,e.head-1)}),oQ=i=>ba(i,e=>{let t=i.moveToLineBoundary(e,!0).head;return e.head{if(i.readOnly)return!1;let t=i.changeByRange(n=>({changes:{from:n.from,to:n.to,insert:he.of(["",""])},range:O.cursor(n.from)}));return e(i.update(t,{scrollIntoView:!0,userEvent:"input"})),!0},uQ=({state:i,dispatch:e})=>{if(i.readOnly)return!1;let t=i.changeByRange(n=>{if(!n.empty||n.from==0||n.from==i.doc.length)return{range:n};let a=n.from,s=i.doc.lineAt(a),r=a==s.from?a-1:ut(s.text,a-s.from,!1)+s.from,o=a==s.to?a+1:ut(s.text,a-s.from,!0)+s.from;return{changes:{from:r,to:o,insert:i.doc.slice(a,o).append(i.doc.slice(r,a))},range:O.cursor(o)}});return t.changes.empty?!1:(e(i.update(t,{scrollIntoView:!0,userEvent:"move.character"})),!0)};function Jr(i){let e=[],t=-1;for(let n of i.selection.ranges){let a=i.doc.lineAt(n.from),s=i.doc.lineAt(n.to);if(!n.empty&&n.to==s.from&&(s=i.doc.lineAt(n.to-1)),t>=a.number){let r=e[e.length-1];r.to=s.to,r.ranges.push(n)}else e.push({from:a.from,to:s.to,ranges:[n]});t=s.number+1}return e}function c3(i,e,t){if(i.readOnly)return!1;let n=[],a=[];for(let s of Jr(i)){if(t?s.to==i.doc.length:s.from==0)continue;let r=i.doc.lineAt(t?s.to+1:s.from-1),o=r.length+1;if(t){n.push({from:s.to,to:r.to},{from:s.from,insert:r.text+i.lineBreak});for(let l of s.ranges)a.push(O.range(Math.min(i.doc.length,l.anchor+o),Math.min(i.doc.length,l.head+o)))}else{n.push({from:r.from,to:s.from},{from:s.to,insert:i.lineBreak+r.text});for(let l of s.ranges)a.push(O.range(l.anchor-o,l.head-o))}}return n.length?(e(i.update({changes:n,scrollIntoView:!0,selection:O.create(a,i.selection.mainIndex),userEvent:"move.line"})),!0):!1}const mQ=({state:i,dispatch:e})=>c3(i,e,!1),cQ=({state:i,dispatch:e})=>c3(i,e,!0);function d3(i,e,t){if(i.readOnly)return!1;let n=[];for(let s of Jr(i))t?n.push({from:s.from,insert:i.doc.slice(s.from,s.to)+i.lineBreak}):n.push({from:s.to,insert:i.lineBreak+i.doc.slice(s.from,s.to)});let a=i.changes(n);return e(i.update({changes:a,selection:i.selection.map(a,t?1:-1),scrollIntoView:!0,userEvent:"input.copyline"})),!0}const dQ=({state:i,dispatch:e})=>d3(i,e,!1),hQ=({state:i,dispatch:e})=>d3(i,e,!0),gQ=i=>{if(i.state.readOnly)return!1;let{state:e}=i,t=e.changes(Jr(e).map(({from:a,to:s})=>(a>0?a--:s{let s;if(i.lineWrapping){let r=i.lineBlockAt(a.head),o=i.coordsAtPos(a.head,a.assoc||1);o&&(s=r.bottom+i.documentTop-o.bottom+i.defaultLineHeight/2)}return i.moveVertically(a,!0,s)}).map(t);return i.dispatch({changes:t,selection:n,scrollIntoView:!0,userEvent:"delete.line"}),!0};function pQ(i,e){if(/\(\)|\[\]|\{\}/.test(i.sliceDoc(e-1,e+1)))return{from:e,to:e};let t=et(i).resolveInner(e),n=t.childBefore(e),a=t.childAfter(e),s;return n&&a&&n.to<=e&&a.from>=e&&(s=n.type.prop(le.closedBy))&&s.indexOf(a.name)>-1&&i.doc.lineAt(n.to).from==i.doc.lineAt(a.from).from&&!/\S/.test(i.sliceDoc(n.to,a.from))?{from:n.to,to:a.from}:null}const Zd=h3(!1),fQ=h3(!0);function h3(i){return({state:e,dispatch:t})=>{if(e.readOnly)return!1;let n=e.changeByRange(a=>{let{from:s,to:r}=a,o=e.doc.lineAt(s),l=!i&&s==r&&pQ(e,s);i&&(s=r=(r<=o.to?o:e.doc.lineAt(r)).to);let u=new Wr(e,{simulateBreak:s,simulateDoubleBreak:!!l}),m=zu(u,s);for(m==null&&(m=ca(/^\s*/.exec(e.doc.lineAt(s).text)[0],e.tabSize));ro.from&&s{let a=[];for(let r=n.from;r<=n.to;){let o=i.doc.lineAt(r);o.number>t&&(n.empty||n.to>o.from)&&(e(o,a,n),t=o.number),r=o.to+1}let s=i.changes(a);return{changes:a,range:O.range(s.mapPos(n.anchor,1),s.mapPos(n.head,1))}})}const bQ=({state:i,dispatch:e})=>{if(i.readOnly)return!1;let t=Object.create(null),n=new Wr(i,{overrideIndentation:s=>{let r=t[s];return r??-1}}),a=Mu(i,(s,r,o)=>{let l=zu(n,s.from);if(l==null)return;/\S/.test(s.text)||(l=0);let u=/^\s*/.exec(s.text)[0],m=na(i,l);(u!=m||o.fromi.readOnly?!1:(e(i.update(Mu(i,(t,n)=>{n.push({from:t.from,insert:i.facet(pa)})}),{userEvent:"input.indent"})),!0),p3=({state:i,dispatch:e})=>i.readOnly?!1:(e(i.update(Mu(i,(t,n)=>{let a=/^\s*/.exec(t.text)[0];if(!a)return;let s=ca(a,i.tabSize),r=0,o=na(i,Math.max(0,s-lr(i)));for(;r(i.setTabFocusMode(),!0),vQ=[{key:"Ctrl-b",run:G2,shift:X2,preventDefault:!0},{key:"Ctrl-f",run:H2,shift:e3},{key:"Ctrl-p",run:U2,shift:i3},{key:"Ctrl-n",run:Q2,shift:a3},{key:"Ctrl-a",run:WU,shift:$U},{key:"Ctrl-e",run:EU,shift:YU},{key:"Ctrl-d",run:l3},{key:"Ctrl-h",run:c0},{key:"Ctrl-k",run:sQ},{key:"Ctrl-Alt-h",run:m3},{key:"Ctrl-o",run:lQ},{key:"Ctrl-t",run:uQ},{key:"Ctrl-v",run:m0}],wQ=[{key:"ArrowLeft",run:G2,shift:X2,preventDefault:!0},{key:"Mod-ArrowLeft",mac:"Alt-ArrowLeft",run:jU,shift:RU,preventDefault:!0},{mac:"Cmd-ArrowLeft",run:ZU,shift:UU,preventDefault:!0},{key:"ArrowRight",run:H2,shift:e3,preventDefault:!0},{key:"Mod-ArrowRight",mac:"Alt-ArrowRight",run:CU,shift:JU,preventDefault:!0},{mac:"Cmd-ArrowRight",run:LU,shift:QU,preventDefault:!0},{key:"ArrowUp",run:U2,shift:i3,preventDefault:!0},{mac:"Cmd-ArrowUp",run:Bd,shift:Od},{mac:"Ctrl-ArrowUp",run:jd,shift:Cd},{key:"ArrowDown",run:Q2,shift:a3,preventDefault:!0},{mac:"Cmd-ArrowDown",run:Pd,shift:Md},{mac:"Ctrl-ArrowDown",run:m0,shift:Dd},{key:"PageUp",run:jd,shift:Cd},{key:"PageDown",run:m0,shift:Dd},{key:"Home",run:MU,shift:qU,preventDefault:!0},{key:"Mod-Home",run:Bd,shift:Od},{key:"End",run:OU,shift:VU,preventDefault:!0},{key:"Mod-End",run:Pd,shift:Md},{key:"Enter",run:Zd,shift:Zd},{key:"Mod-a",run:KU},{key:"Backspace",run:c0,shift:c0,preventDefault:!0},{key:"Delete",run:l3,preventDefault:!0},{key:"Mod-Backspace",mac:"Alt-Backspace",run:m3,preventDefault:!0},{key:"Mod-Delete",mac:"Alt-Delete",run:aQ,preventDefault:!0},{mac:"Mod-Backspace",run:rQ,preventDefault:!0},{mac:"Mod-Delete",run:oQ,preventDefault:!0}].concat(vQ.map(i=>({mac:i.key,run:i.run,shift:i.shift}))),yQ=[{key:"Alt-ArrowLeft",mac:"Ctrl-ArrowLeft",run:BU,shift:GU},{key:"Alt-ArrowRight",mac:"Ctrl-ArrowRight",run:PU,shift:HU},{key:"Alt-ArrowUp",run:mQ},{key:"Shift-Alt-ArrowUp",run:dQ},{key:"Alt-ArrowDown",run:cQ},{key:"Shift-Alt-ArrowDown",run:hQ},{key:"Mod-Alt-ArrowUp",run:tQ},{key:"Mod-Alt-ArrowDown",run:nQ},{key:"Escape",run:iQ},{key:"Mod-Enter",run:fQ},{key:"Alt-l",mac:"Ctrl-l",run:XU},{key:"Mod-i",run:eQ,preventDefault:!0},{key:"Mod-[",run:p3},{key:"Mod-]",run:g3},{key:"Mod-Alt-\\",run:bQ},{key:"Shift-Mod-k",run:gQ},{key:"Shift-Mod-\\",run:FU},{key:"Mod-/",run:zU},{key:"Alt-A",mac:"Ctrl-A",run:kU},{key:"Ctrl-m",mac:"Shift-Alt-m",run:xQ}].concat(wQ),zQ={key:"Tab",run:g3,shift:p3};var Ld={};class cr{constructor(e,t,n,a,s,r,o,l,u,m=0,c){this.p=e,this.stack=t,this.state=n,this.reducePos=a,this.pos=s,this.score=r,this.buffer=o,this.bufferBase=l,this.curContext=u,this.lookAhead=m,this.parent=c}toString(){return`[${this.stack.filter((e,t)=>t%3==0).concat(this.state)}]@${this.pos}${this.score?"!"+this.score:""}`}static start(e,t,n=0){let a=e.parser.context;return new cr(e,[],t,n,n,0,[],0,a?new Wd(a,a.start):null,0,null)}get context(){return this.curContext?this.curContext.context:null}pushState(e,t){this.stack.push(this.state,t,this.bufferBase+this.buffer.length),this.state=e}reduce(e){var t;let n=e>>19,a=e&65535,{parser:s}=this.p,r=this.reducePos=2e3&&!(!((t=this.p.parser.nodeSet.types[a])===null||t===void 0)&&t.isAnonymous)&&(u==this.p.lastBigReductionStart?(this.p.bigReductionCount++,this.p.lastBigReductionSize=m):this.p.lastBigReductionSizel;)this.stack.pop();this.reduceContext(a,u)}storeNode(e,t,n,a=4,s=!1){if(e==0&&(!this.stack.length||this.stack[this.stack.length-1]0&&this.buffer[r-4]==0&&this.buffer[r-1]>-1){if(t==n)return;if(this.buffer[r-2]>=t){this.buffer[r-2]=n;return}}}if(!s||this.pos==n)this.buffer.push(e,t,n,a);else{let r=this.buffer.length;if(r>0&&(this.buffer[r-4]!=0||this.buffer[r-1]<0)){let o=!1;for(let l=r;l>0&&this.buffer[l-2]>n;l-=4)if(this.buffer[l-1]>=0){o=!0;break}if(o)for(;r>0&&this.buffer[r-2]>n;)this.buffer[r]=this.buffer[r-4],this.buffer[r+1]=this.buffer[r-3],this.buffer[r+2]=this.buffer[r-2],this.buffer[r+3]=this.buffer[r-1],r-=4,a>4&&(a-=4)}this.buffer[r]=e,this.buffer[r+1]=t,this.buffer[r+2]=n,this.buffer[r+3]=a}}shift(e,t,n,a){if(e&131072)this.pushState(e&65535,this.pos);else if((e&262144)==0){let s=e,{parser:r}=this.p;this.pos=a;let o=r.stateFlag(s,1);!o&&(a>n||t<=r.maxNode)&&(this.reducePos=a),this.pushState(s,o?n:Math.min(n,this.reducePos)),this.shiftContext(t,n),t<=r.maxNode&&this.buffer.push(t,n,a,4)}else this.pos=a,this.shiftContext(t,n),t<=this.p.parser.maxNode&&this.buffer.push(t,n,a,4)}apply(e,t,n,a){e&65536?this.reduce(e):this.shift(e,t,n,a)}useNode(e,t,n){let a=this.p.reused.length-1;(a<0||this.p.reused[a]!=e)&&(this.p.reused.push(e),a++);let s=this.reducePos=this.pos=n+e.length;this.pushState(t,n),this.buffer.push(a,n,s,-1),this.curContext&&this.updateContext(this.curContext.tracker.reuse(this.curContext.context,e,this,this.p.stream.reset(this.pos-e.length)))}split(){let e=this,t=e.buffer.length;for(t&&e.buffer[t-4]==0&&(t-=4);t>0&&e.buffer[t-2]>e.reducePos;)t-=4;let n=e.buffer.slice(t),a=e.bufferBase+t;for(;e&&a==e.bufferBase;)e=e.parent;return new cr(this.p,this.stack.slice(),this.state,this.reducePos,this.pos,this.score,n,a,this.curContext,this.lookAhead,e)}recoverByDelete(e,t){let n=e<=this.p.parser.maxNode;n&&this.storeNode(e,this.pos,t,4),this.storeNode(0,this.pos,t,n?8:4),this.pos=this.reducePos=t,this.score-=190}canShift(e){for(let t=new IQ(this);;){let n=this.p.parser.stateSlot(t.state,4)||this.p.parser.hasAction(t.state,e);if(n==0)return!1;if((n&65536)==0)return!0;t.reduce(n)}}recoverByInsert(e){if(this.stack.length>=300)return[];let t=this.p.parser.nextStates(this.state);if(t.length>8||this.stack.length>=120){let a=[];for(let s=0,r;sl&1&&o==r)||a.push(t[s],r)}t=a}let n=[];for(let a=0;a>19,a=t&65535,s=this.stack.length-n*3;if(s<0||e.getGoto(this.stack[s],a,!1)<0){let r=this.findForcedReduction();if(r==null)return!1;t=r}this.storeNode(0,this.pos,this.pos,4,!0),this.score-=100}return this.reducePos=this.pos,this.reduce(t),!0}findForcedReduction(){let{parser:e}=this.p,t=[],n=(a,s)=>{if(!t.includes(a))return t.push(a),e.allActions(a,r=>{if(!(r&393216))if(r&65536){let o=(r>>19)-s;if(o>1){let l=r&65535,u=this.stack.length-o*3;if(u>=0&&e.getGoto(this.stack[u],l,!1)>=0)return o<<19|65536|l}}else{let o=n(r,s+1);if(o!=null)return o}})};return n(this.state,0)}forceAll(){for(;!this.p.parser.stateFlag(this.state,2);)if(!this.forceReduce()){this.storeNode(0,this.pos,this.pos,4,!0);break}return this}get deadEnd(){if(this.stack.length!=3)return!1;let{parser:e}=this.p;return e.data[e.stateSlot(this.state,1)]==65535&&!e.stateSlot(this.state,4)}restart(){this.storeNode(0,this.pos,this.pos,4,!0),this.state=this.stack[0],this.stack.length=0}sameState(e){if(this.state!=e.state||this.stack.length!=e.stack.length)return!1;for(let t=0;t0&&this.emitLookAhead()}}class Wd{constructor(e,t){this.tracker=e,this.context=t,this.hash=e.strict?e.hash(t):0}}class IQ{constructor(e){this.start=e,this.state=e.state,this.stack=e.stack,this.base=this.stack.length}reduce(e){let t=e&65535,n=e>>19;n==0?(this.stack==this.start.stack&&(this.stack=this.stack.slice()),this.stack.push(this.state,0,0),this.base+=3):this.base-=(n-1)*3;let a=this.start.p.parser.getGoto(this.stack[this.base-3],t,!0);this.state=a}}class dr{constructor(e,t,n){this.stack=e,this.pos=t,this.index=n,this.buffer=e.buffer,this.index==0&&this.maybeNext()}static create(e,t=e.bufferBase+e.buffer.length){return new dr(e,t,t-e.bufferBase)}maybeNext(){let e=this.stack.parent;e!=null&&(this.index=this.stack.bufferBase-e.bufferBase,this.stack=e,this.buffer=e.buffer)}get id(){return this.buffer[this.index-4]}get start(){return this.buffer[this.index-3]}get end(){return this.buffer[this.index-2]}get size(){return this.buffer[this.index-1]}next(){this.index-=4,this.pos-=4,this.index==0&&this.maybeNext()}fork(){return new dr(this.stack,this.pos,this.index)}}function is(i,e=Uint16Array){if(typeof i!="string")return i;let t=null;for(let n=0,a=0;n=92&&r--,r>=34&&r--;let l=r-32;if(l>=46&&(l-=46,o=!0),s+=l,o)break;s*=46}t?t[a++]=s:t=new e(s)}return t}class ks{constructor(){this.start=-1,this.value=-1,this.end=-1,this.extended=-1,this.lookAhead=0,this.mask=0,this.context=0}}const Ed=new ks;class kQ{constructor(e,t){this.input=e,this.ranges=t,this.chunk="",this.chunkOff=0,this.chunk2="",this.chunk2Pos=0,this.next=-1,this.token=Ed,this.rangeIndex=0,this.pos=this.chunkPos=t[0].from,this.range=t[0],this.end=t[t.length-1].to,this.readNext()}resolveOffset(e,t){let n=this.range,a=this.rangeIndex,s=this.pos+e;for(;sn.to:s>=n.to;){if(a==this.ranges.length-1)return null;let r=this.ranges[++a];s+=r.from-n.to,n=r}return s}clipPos(e){if(e>=this.range.from&&ee)return Math.max(e,t.from);return this.end}peek(e){let t=this.chunkOff+e,n,a;if(t>=0&&t=this.chunk2Pos&&no.to&&(this.chunk2=this.chunk2.slice(0,o.to-n)),a=this.chunk2.charCodeAt(0)}}return n>=this.token.lookAhead&&(this.token.lookAhead=n+1),a}acceptToken(e,t=0){let n=t?this.resolveOffset(t,-1):this.pos;if(n==null||n=this.chunk2Pos&&this.posthis.range.to?e.slice(0,this.range.to-this.pos):e,this.chunkPos=this.pos,this.chunkOff=0}}readNext(){return this.chunkOff>=this.chunk.length&&(this.getChunk(),this.chunkOff==this.chunk.length)?this.next=-1:this.next=this.chunk.charCodeAt(this.chunkOff)}advance(e=1){for(this.chunkOff+=e;this.pos+e>=this.range.to;){if(this.rangeIndex==this.ranges.length-1)return this.setDone();e-=this.range.to-this.pos,this.range=this.ranges[++this.rangeIndex],this.pos=this.range.from}return this.pos+=e,this.pos>=this.token.lookAhead&&(this.token.lookAhead=this.pos+1),this.readNext()}setDone(){return this.pos=this.chunkPos=this.end,this.range=this.ranges[this.rangeIndex=this.ranges.length-1],this.chunk="",this.next=-1}reset(e,t){if(t?(this.token=t,t.start=e,t.lookAhead=e+1,t.value=t.extended=-1):this.token=Ed,this.pos!=e){if(this.pos=e,e==this.end)return this.setDone(),this;for(;e=this.range.to;)this.range=this.ranges[++this.rangeIndex];e>=this.chunkPos&&e=this.chunkPos&&t<=this.chunkPos+this.chunk.length)return this.chunk.slice(e-this.chunkPos,t-this.chunkPos);if(e>=this.chunk2Pos&&t<=this.chunk2Pos+this.chunk2.length)return this.chunk2.slice(e-this.chunk2Pos,t-this.chunk2Pos);if(e>=this.range.from&&t<=this.range.to)return this.input.read(e,t);let n="";for(let a of this.ranges){if(a.from>=t)break;a.to>e&&(n+=this.input.read(Math.max(a.from,e),Math.min(a.to,t)))}return n}}class J1{constructor(e,t){this.data=e,this.id=t}token(e,t){let{parser:n}=t.p;SQ(this.data,e,t,this.id,n.data,n.tokenPrecTable)}}J1.prototype.contextual=J1.prototype.fallback=J1.prototype.extend=!1;J1.prototype.fallback=J1.prototype.extend=!1;function SQ(i,e,t,n,a,s){let r=0,o=1<0){let g=i[h];if(l.allows(g)&&(e.token.value==-1||e.token.value==g||TQ(g,e.token.value,a,s))){e.acceptToken(g);break}}let m=e.next,c=0,d=i[r+2];if(e.next<0&&d>c&&i[u+d*3-3]==65535){r=i[u+d*3-1];continue e}for(;c>1,g=u+h+(h<<1),f=i[g],b=i[g+1]||65536;if(m=b)c=h+1;else{r=i[g+2],e.advance();continue e}}break}}function Nd(i,e,t){for(let n=e,a;(a=i[n])!=65535;n++)if(a==t)return n-e;return-1}function TQ(i,e,t,n){let a=Nd(t,n,e);return a<0||Nd(t,n,i)e)&&!n.type.isError)return t<0?Math.max(0,Math.min(n.to-1,e-25)):Math.min(i.length,Math.max(n.from+1,e+25));if(t<0?n.prevSibling():n.nextSibling())break;if(!n.parent())return t<0?0:i.length}}class _Q{constructor(e,t){this.fragments=e,this.minRepeat=t,this.i=0,this.fragment=null,this.safeFrom=-1,this.safeTo=-1,this.trees=[],this.start=[],this.index=[],this.lastRepeat=null,this.lastRepeatPos=-1,this.nextFragment()}nextFragment(){let e=this.fragment=this.i==this.fragments.length?null:this.fragments[this.i++];if(e){for(this.safeFrom=e.openStart?Fd(e.tree,e.from+e.offset,1)-e.offset:e.from,this.safeTo=e.openEnd?Fd(e.tree,e.to+e.offset,-1)-e.offset:e.to;this.trees.length;)this.trees.pop(),this.start.pop(),this.index.pop();this.trees.push(e.tree),this.start.push(-e.offset),this.index.push(0),this.nextStart=this.safeFrom}else this.nextStart=1e9}nodeAt(e){if(ee)return this.nextStart=r,null;if(s instanceof Ee){let o=r>this.lastRepeatPos&&s.type.id>=this.minRepeat;if(r==e||o){let l=r+s.length,u;if(r>=this.safeFrom&&l<=this.safeTo&&(!(u=s.prop(le.lookAhead))||l+u=Math.max(this.safeFrom,e)&&(this.trees.push(s),this.start.push(r),this.index.push(0))}else this.index[t]++,this.nextStart=r+s.length}}}class AQ{constructor(e,t){this.stream=t,this.tokens=[],this.mainToken=null,this.actions=[],this.tokens=e.tokenizers.map(n=>new ks)}getActions(e){let t=0,n=null,{parser:a}=e.p,{tokenizers:s}=a,r=a.stateSlot(e.state,3),o=e.curContext?e.curContext.hash:0,l=0;for(let u=0;uc.end+25&&(l=Math.max(c.lookAhead,l)),c.value!=0)){let d=t;if(c.extended>-1&&(t=this.addActions(e,c.extended,c.end,t)),t=this.addActions(e,c.value,c.end,t),!m.extend&&(n=c,t>d))break}}for(;this.actions.length>t;)this.actions.pop();return l&&e.setLookAhead(l),!n&&e.pos==this.stream.end&&(n=new ks,n.value=e.p.parser.eofTerm,n.start=n.end=e.pos,t=this.addActions(e,n.value,n.end,t)),this.mainToken=n,this.actions}getMainToken(e){if(this.mainToken)return this.mainToken;let t=new ks,{pos:n,p:a}=e;return t.start=n,t.end=Math.min(n+1,a.stream.end),t.value=n==a.stream.end?a.parser.eofTerm:0,t}updateCachedToken(e,t,n){let a=this.stream.clipPos(n.pos);if(t.token(this.stream.reset(a,e),n),e.value>-1){let{parser:s}=n.p;for(let r=0;r=0&&n.p.parser.dialect.allows(o>>1)){(o&1)==0?e.value=o>>1:e.extended=o>>1;break}}}else e.value=0,e.end=this.stream.clipPos(a+1)}putAction(e,t,n,a){for(let s=0;se.bufferLength*4?new _Q(n,e.minRepeatTerm):null}get parsedPos(){return this.minStackPos}advance(){let e=this.stacks,t=this.minStackPos,n=this.stacks=[],a,s;if(this.bigReductionCount>300&&e.length==1){let[r]=e;for(;r.forceReduce()&&r.stack.length&&r.stack[r.stack.length-2]>=this.lastBigReductionStart;);this.bigReductionCount=this.lastBigReductionSize=0}for(let r=0;rt)n.push(o);else{if(this.advanceStack(o,n,e))continue;{a||(a=[],s=[]),a.push(o);let l=this.tokens.getMainToken(o);s.push(l.value,l.end)}}break}}if(!n.length){let r=a&&DQ(a);if(r)return yt&&console.log("Finish with "+this.stackID(r)),this.stackToTree(r);if(this.parser.strict)throw yt&&a&&console.log("Stuck with token "+(this.tokens.mainToken?this.parser.getName(this.tokens.mainToken.value):"none")),new SyntaxError("No parse at "+t);this.recovering||(this.recovering=5)}if(this.recovering&&a){let r=this.stoppedAt!=null&&a[0].pos>this.stoppedAt?a[0]:this.runRecovery(a,s,n);if(r)return yt&&console.log("Force-finish "+this.stackID(r)),this.stackToTree(r.forceAll())}if(this.recovering){let r=this.recovering==1?1:this.recovering*3;if(n.length>r)for(n.sort((o,l)=>l.score-o.score);n.length>r;)n.pop();n.some(o=>o.reducePos>t)&&this.recovering--}else if(n.length>1){e:for(let r=0;r500&&u.buffer.length>500)if((o.score-u.score||o.buffer.length-u.buffer.length)>0)n.splice(l--,1);else{n.splice(r--,1);continue e}}}n.length>12&&(n.sort((r,o)=>o.score-r.score),n.splice(12,n.length-12))}this.minStackPos=n[0].pos;for(let r=1;r-1&&n.length&&(!s||(n.prop(le.contextHash)||0)==r))return e.useNode(n,o,t),n;if(n.children.length==0||n.positions[0]>0)break;let l=n.children[0];if(l instanceof Ee&&(!a||l.type.id>=this.parser.minRepeatTerm))n=l;else break}return null}advanceStack(e,t,n){let a=e.pos,{parser:s}=this,r=yt?this.stackID(e)+" -> ":"";if(this.stoppedAt!=null&&a>this.stoppedAt)return e.forceReduce()?e:null;if(this.fragments){let u=e.reducePos0)return e.reduce(o),yt&&console.log(r+this.stackID(e)+` (via always-reduce ${s.getName(o&65535)})`),!0;if(e.stack.length>=8400)for(;e.stack.length>6e3&&e.forceReduce(););let l=this.tokens.getActions(e);for(let u=0;ua?t.push(g):n.push(g)}return!1}advanceFully(e,t){let n=e.pos;for(;;){if(!this.advanceStack(e,null,null))return!1;if(e.pos>n)return Rd(e,t),!0}}runRecovery(e,t,n){let a=null,s=!1;for(let r=0;r ":"";if(o.deadEnd&&(s||(s=!0,o.restart(),yt&&console.log(m+this.stackID(o)+" (restarted)"),this.advanceFully(o,n))))continue;let c=o.split(),d=m;for(let h=0;h<10&&c.forceReduce()&&(yt&&console.log(d+this.stackID(c)+" (via force-reduce)"),!this.advanceFully(c,n));h++)yt&&(d=this.stackID(c)+" -> ");for(let h of o.recoverByInsert(l))yt&&console.log(m+this.stackID(h)+" (via recover-insert)"),this.advanceFully(h,n);this.stream.end>o.pos?(u==o.pos&&(u++,l=0),o.recoverByDelete(l,u),yt&&console.log(m+this.stackID(o)+` (via recover-delete ${this.parser.getName(l)})`),Rd(o,n)):(!a||a.scoree.topRules[o][1]),a=[];for(let o=0;o=0)s(m,l,o[u++]);else{let c=o[u+-m];for(let d=-m;d>0;d--)s(o[u++],l,c);u++}}}this.nodeSet=new xu(t.map((o,l)=>wt.define({name:l>=this.minRepeatTerm?void 0:o,id:l,props:a[l],top:n.indexOf(l)>-1,error:l==0,skipped:e.skippedNodes&&e.skippedNodes.indexOf(l)>-1}))),e.propSources&&(this.nodeSet=this.nodeSet.extend(...e.propSources)),this.strict=!1,this.bufferLength=b2;let r=is(e.tokenData);this.context=e.context,this.specializerSpecs=e.specialized||[],this.specialized=new Uint16Array(this.specializerSpecs.length);for(let o=0;otypeof o=="number"?new J1(r,o):o),this.topRules=e.topRules,this.dialects=e.dialects||{},this.dynamicPrecedences=e.dynamicPrecedences||null,this.tokenPrecTable=e.tokenPrec,this.termNames=e.termNames||null,this.maxNode=this.nodeSet.types.length-1,this.dialect=this.parseDialect(),this.top=this.topRules[Object.keys(this.topRules)[0]]}createParse(e,t,n){let a=new jQ(this,e,t,n);for(let s of this.wrappers)a=s(a,e,t,n);return a}getGoto(e,t,n=!1){let a=this.goto;if(t>=a[0])return-1;for(let s=a[t+1];;){let r=a[s++],o=r&1,l=a[s++];if(o&&n)return l;for(let u=s+(r>>1);s0}validAction(e,t){return!!this.allActions(e,n=>n==t?!0:null)}allActions(e,t){let n=this.stateSlot(e,4),a=n?t(n):void 0;for(let s=this.stateSlot(e,1);a==null;s+=3){if(this.data[s]==65535)if(this.data[s+1]==1)s=fn(this.data,s+2);else break;a=t(fn(this.data,s+1))}return a}nextStates(e){let t=[];for(let n=this.stateSlot(e,1);;n+=3){if(this.data[n]==65535)if(this.data[n+1]==1)n=fn(this.data,n+2);else break;if((this.data[n+2]&1)==0){let a=this.data[n+1];t.some((s,r)=>r&1&&s==a)||t.push(this.data[n],a)}}return t}configure(e){let t=Object.assign(Object.create(hr.prototype),this);if(e.props&&(t.nodeSet=this.nodeSet.extend(...e.props)),e.top){let n=this.topRules[e.top];if(!n)throw new RangeError(`Invalid top rule name ${e.top}`);t.top=n}return e.tokenizers&&(t.tokenizers=this.tokenizers.map(n=>{let a=e.tokenizers.find(s=>s.from==n);return a?a.to:n})),e.specializers&&(t.specializers=this.specializers.slice(),t.specializerSpecs=this.specializerSpecs.map((n,a)=>{let s=e.specializers.find(o=>o.from==n.external);if(!s)return n;let r=Object.assign(Object.assign({},n),{external:s.to});return t.specializers[a]=Jd(r),r})),e.contextTracker&&(t.context=e.contextTracker),e.dialect&&(t.dialect=this.parseDialect(e.dialect)),e.strict!=null&&(t.strict=e.strict),e.wrap&&(t.wrappers=t.wrappers.concat(e.wrap)),e.bufferLength!=null&&(t.bufferLength=e.bufferLength),t}hasWrappers(){return this.wrappers.length>0}getName(e){return this.termNames?this.termNames[e]:String(e<=this.maxNode&&this.nodeSet.types[e].name||e)}get eofTerm(){return this.maxNode+1}get topNode(){return this.nodeSet.types[this.top[1]]}dynamicPrecedence(e){let t=this.dynamicPrecedences;return t==null?0:t[e]||0}parseDialect(e){let t=Object.keys(this.dialects),n=t.map(()=>!1);if(e)for(let s of e.split(" ")){let r=t.indexOf(s);r>=0&&(n[r]=!0)}let a=null;for(let s=0;sn)&&t.p.parser.stateFlag(t.state,2)&&(!e||e.scorei.external(t,n)<<1|e}return i.get}const BQ=z2({null:E.null,instanceof:E.operatorKeyword,this:E.self,"new super assert open to with void":E.keyword,"class interface extends implements enum var":E.definitionKeyword,"module package import":E.moduleKeyword,"switch while for if else case default do break continue return try catch finally throw":E.controlKeyword,"requires exports opens uses provides public private protected static transitive abstract final strictfp synchronized native transient volatile throws":E.modifier,IntegerLiteral:E.integer,FloatingPointLiteral:E.float,"StringLiteral TextBlock":E.string,CharacterLiteral:E.character,LineComment:E.lineComment,BlockComment:E.blockComment,BooleanLiteral:E.bool,PrimitiveType:E.standard(E.typeName),TypeName:E.typeName,Identifier:E.variableName,"MethodName/Identifier":E.function(E.variableName),Definition:E.definition(E.variableName),ArithOp:E.arithmeticOperator,LogicOp:E.logicOperator,BitOp:E.bitwiseOperator,CompareOp:E.compareOperator,AssignOp:E.definitionOperator,UpdateOp:E.updateOperator,Asterisk:E.punctuation,Label:E.labelName,"( )":E.paren,"[ ]":E.squareBracket,"{ }":E.brace,".":E.derefOperator,", ;":E.separator}),PQ={__proto__:null,void:34,byte:36,short:36,int:36,long:36,char:36,float:36,double:36,boolean:36,extends:50,super:52,this:64,public:70,protected:72,private:74,abstract:76,static:78,final:80,strictfp:82,default:84,synchronized:86,native:88,transient:90,volatile:92,sealed:95,non:97,instanceof:140,true:158,false:158,null:166,class:170,new:176,throws:196,implements:204,permits:211,record:215,interface:226,enum:236,open:289,module:291,requires:296,transitive:298,exports:300,to:302,opens:304,uses:306,provides:308,with:310,package:314,import:318,if:330,else:332,while:336,for:340,var:347,assert:354,switch:358,case:366,throw:370,do:374,break:378,continue:382,return:386,try:392,catch:396,finally:404},OQ=hr.deserialize({version:14,states:"#5QQ]QPOOQ%QQPOOO(wQQO'#HlO+OQQO'#CbOOQO'#Cb'#CbO+VQPO'#CaOOQO'#Co'#CoO,{QPO'#DOO-iQQO'#HvOOQO'#Hv'#HvO0^QQO'#HqO0eQQO'#HqOOQO'#Hq'#HqOOQO'#Hp'#HpOOQO'#Hz'#HzO0lQPO'#GwO2`QPO'#DOO2gQPO'#CkO4vQPO'#DfO4}QPO'#CiO6ZQQO'#FsO6bQPO'#DyO7rOSO'#EQOOQO'#IW'#IWO+VQPO'#EzO8iQPO'#EzOOQO'#EV'#EVO:QQQO'#ITOnQPO'#HzO>uQPO'#EcO>uQPO'#EjO>uQPO'#EuO>uQPO'#EpO>}QPO'#I[O?cQQO'#FRO+VQPO'#C`O?kQPO'#C`O+VQPO'#FnO?pQPO'#FpO@OQPO'#FvO@OQPO'#FyO>uQPO'#GOO@TQPO'#F{O6bQPO'#GSO6bQPO'#G[O@OQPO'#GUO]QPO'#G^O@YQPO'#G`O@eQPO'#GbO@pQPO'#GdO@wQPO'#GgOAeQQO'#CjOBTQQO'#CjOB_QPO'#I`OCzQPO'#DOODbQPO'#E]OBbQPO'#E^OESQPO'#I`OEeQPO'#E]OEmQPO'#IhOErQPO'#FOOOQO'#I^'#I^OOQO'#G}'#G}Q%QQPOOOGhQPO'#HzOOQO'#Hl'#HlOHZQQO,58{OOQO'#Hj'#HjOOQO'#Hr'#HrOJtQPO,59_OLSQPO,59jOOQO-E:r-E:rO+VQPO,58zOLyQPO,58zO+VQPO,5uQPO,5:}O>uQPO,5;UO>uQPO,5;aO>uQPO,5;[O>uQPO,5gO6bQPO'#EUO@zQPO,5=QO!0rQPO,5:}O!1TQPO,5;UO!1]QPO,5;aO!1eQPO,5;[O!-SQPO,5>vO!1sQPO,5>vO!-SQPO,5>vO!2OQPO,5;mO!2aQQO,58zO!2iQQO,5zOB_QPO,5:vO!;WQPO,5:wOErQPO,5;jO!;`QPO'#E_OBbQPO'#E_O!;kQPO'#IaO!;sQPO,5:xOB_QPO,5>zO!-SQPO,5>zOEmQPO,5?SOOQO,5:w,5:wO!+VQPO'#CkOOQO,5?S,5?SO!?pQPO'#FPOOQO,5;j,5;jO!?wQPO,5;jO!-SQPO'#EaOOQO-E:{-E:{OOQO,5;i,5;iO+VQPO,58}O!?|QPO'#ChOOQO1G.k1G.kO!2aQQO1G.fO+VQPO1G.fO!@WQQO1G1tOOQO,59f,59fO!@`QPO,59fOOQO-E:t-E:tO!@eQPO,5>iO!@|QPO,59zO>uQPO'#HOO!ATQPO,5>yOOQO1G2U1G2UOOQO1G0Y1G0YO!AnQPO'#CsO!B^QPO'#HvO!BkQPO'#CtO!ByQPO'#HuO!CRQPO,59^OOQO1G.v1G.vO!#aQPO1G.vO!CiQPO,59_O!CvQQO'#HlO!DXQQO'#CbO!DjQPO,59yOOQO,59i,59iO>uQPO,59yOOQO,59W,59WO!DuQQO,59WO!DzQPO,59iO!,oQPO'#GyO!E]QPO,5>eOOQO1G.q1G.qO!EeQPO'#C}O!EvQPO1G.qO>uQPO'#GzO!E{QPO,5>mOOQO1G/k1G/kOOQO1G0[1G0[O!-SQPO'#HPO!FTQPO1G1cO6bQPO1G1cOOQO1G/l1G/lO!F]QQO1G.oOOQO1G.o1G.oO!HZQQO1G/mO!IxQQO1G/mO!KgQQO1G/mO!KnQQO1G/mO!KuQQO1G/mO!MdQQO1G/mO!MkQQO1G/mO# YQQO1G/mO# aQQO1G/mO# kQQO1G/mO# rQQO1G/|O!)dQPO'#DOO# yQQO'#HvO!*jQPO'#DuOOQO1G/y1G/yO##`QQO1G1dOOOO-E:y-E:yOOQO1G0W1G0WO##vQPO'#E{OOQO1G1Q1G1QO#$jQPO1G2UO!-{QPO1G1QO!0rQPO1G0iO!1TQPO1G0pO!1]QPO1G0{O!1eQPO1G0vOOQO1G0]1G0]O#$oQQO1G1XO}QPO'#I[OOQO1G1X1G1XO#&iQQO1G1XO#&qQQO1G1YOOQO1G1]1G1]O#&xQPO1G1]O#'TQQO'#I]O#'bQPO,5:sOOQO1G1z1G1zOOQO1G4R1G4ROOQO1G2l1G2lO#'gQPO'#EYO!-SQPO'#EdO!-SQPO'#EeO!-SQPO'#EhOOQO1G0i1G0iO#'nQPO1G0iO#'sQPO1G0iO#'{QPO1G0iO#(WQPO1G0iO#(fQPO'#ElO#(|QPO1G0pO#)UQPO1G0pO#)ZQPO'#EwOOQO1G0{1G0{O#)nQPO1G0{O#)sQPO'#EsO!-SQPO'#ErOOQO1G0v1G0vO#*pQPO1G0vO#*uQPO1G0vO#*}QPO1G0vO!1sQPO1G4bO#+YQPO'#FXO#+aQPO'#FXOOQO'#HX'#HXO#+iQQO1G1^O#-fQQO1G4bO!-SQPO1G4bO|OOQO1G0b1G0bOOQO1G4f1G4fO#DXQPO1G0bOOQO1G0c1G0cOOQO1G1U1G1UO!?wQPO1G1UOOQO,5:y,5:yO!-SQPO'#E`O#D^QPO,5:yOBbQPO'#HQO#DiQPO,5>{OOQO1G0d1G0dOEmQPO'#IbO#DqQPO1G4fOB_QPO1G4fOOQO1G4n1G4nO!)tQPO'#C}O!)dQPO'#DOOOQO,5;k,5;kO#D|QPO,5;kO#ETQPO,5;kO#E[QQO'#ITO#EcQQO'#IVO#EjQQO'#FQO#EuQPO'#FQO#E}QPO'#IcOOQO,5:{,5:{OOQO1G.i1G.iO#F]QQO'#HnO#FmQQO'#FUO#HTQPO'#F`OOQO'#Hn'#HnO#H_QPO'#HnO#H|QPO'#IkO#IUQPO,59SOOQO7+$Q7+$QO!2aQQO7+$QOOQO7+'`7+'`OOQO1G/Q1G/QO#IZQPO'#DdO#IeQQO'#IPOOQO'#IP'#IPOOQO1G/f1G/fOOQO,5=j,5=jOOQO-E:|-E:|O#IuQWO,58{O#I|QPO,59`OOQO,59`,59`O!-SQPO'#HxO!!tQPO'#GuO#J[QPO,5>aOOQO1G.x1G.xOOQO7+$b7+$bOOQO1G.r1G.rO#JdQQO1G.rO>uQPO1G/eOOQO1G/e1G/eO#JiQPO1G.rO#JnQPO1G/eOOQO1G/T1G/TOOQO,5=e,5=eOOQO-E:w-E:wOOQO7+$]7+$]OOQO,5=f,5=fOOQO-E:x-E:xOOQO,5=k,5=kOOQO-E:}-E:}O6bQPO7+&}O#JyQQO7+&}O6bQPO7+%hO#LgQQO,59_OOQO,5:a,5:aOB_QPO,5:aO#MvQPO'#EtO#NUQPO'#EtOOQO'#HW'#HWO#NpQPO,5;gOOQO,5;g,5;gOOQO7+'p7+'pOOQO7+&l7+&lOOQO7+&T7+&TO#'nQPO7+&TO#'sQPO7+&TO#'{QPO7+&TO#(WQPO7+&TO#(|QPO7+&[O#)UQPO7+&[OOQO7+&g7+&gO#)nQPO7+&gOOQO7+&b7+&bO#*pQPO7+&bO#*uQPO7+&bO#*}QPO7+&bOOQO7+&u7+&uOOQO'#FU'#FUOvOvO$ dQPO7+&sO$ rQPO7+&wOOQO7+&t7+&tOOQO7+&w7+&wO6bQPO'#G|O$!QQPO,5>wOOQO1G0_1G0_O$!]QPO,5:tOOQO,5:t,5:tOOQO,5;O,5;OOOQO'#Eg'#EgOOQO,5;P,5;POOQO,5;S,5;SO$!dQPO'#EmO!*jQPO'#EmO$!uQPO'#IdO$!}QPO,5;WO$#SQPO'#EnOOQO7+&[7+&[O$#ZQPO7+&[O$#`QPO'#ExO>uQPO'#ExO$#qQPO'#IfO$#|QPO,5;cOB_QPO'#I`O!-SQPO'#I`O$$UQPO'#E]OOQO'#HU'#HUO$$]QPO,5;_OOQO,5;_,5;_OOQO,5;^,5;^O$%YQQO7+&xO$'VQQO7+)|O$)PQQO,5;sOOQO,5;s,5;sO$)WQPO,5;sOOQO-E;V-E;VOOQO7+&x7+&xOOQO7+)|7+)|O}O$4VQPO,5;{O$4^QPO'#IjO$4fQPO,5;zO+VQPO'#HZO$4kQPO,5?VOOQO1G.n1G.nOOQO<d,5>dOOQO,5=a,5=aOOQO-E:s-E:sO$5QQPO7+$^OOQO7+%P7+%POOQO7+$^7+$^O>uQPO7+%PO$5VQQO<uQPO,5;XOOQO,5;X,5;XO$;zQPO,5;XO!,oQPO'#HSO$<]QPO,5?OOOQO1G0r1G0rODbQPO'#E]O$WQPO'#EyOOQO1G0}1G0}O$>_QPO1G0}OB_QPO,5;`OOQO-E;S-E;SOOQO1G0y1G0yOOQO<dQQO1G1_O$>kQQO<uQPO,5=VO!*jQPO,5=VOOQO1G2o1G2oOOQO<lOOQO1G/j1G/jOOQO<uQPO1G0sOOQO1G0s1G0sO$HSQPO1G0sOOQO,5=n,5=nOOQO-E;Q-E;QO$uQPO1G2qOOQO<uQPO7+&_OOQO1G0u1G0uOOQO7+&j7+&jOOQO7+'V7+'VO+VQPO'#H^O$JtQPO,5?XO$J|QPO7+'VO$KRQQO'#InOOQOAN@yAN@yO$K^QQOAN@yOOQOAN@uAN@uO$KeQPOAN@uO$KjQQO<`!>cP!=^!>{!=^P!=^P!?O!?R!?VP!{#?U#?XP0oP#?]!)]PPP#Ae#Cm#C|#DY#Dr#ET#EW#Ed#El#Eo#EsP#EvP#FU#FX#F[#F`#Ff#FiwuOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u)usOPVW_abepy!Q![!]!_!b!f!g!i!j!p!v#O#R#W#Y#Z#_#`#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$O$T$]$_$b$e$k$l$m$t$u$v$w$x%U%Y%]%a%d%e%h%j%m%q%y%z&P&Q&V&W&Z&`&a&d&j&l&{&|'S'`'k'l'm'n't'w'z'{(Q(R(S(U(W(d(f(k(m(u)R)Z)])b)d)e)g)h)r){*U*V*`*i*k*l*n*o*r+W+^+a+g+k+r+s+v+y+},^,`,b,c,g,j,m,n,s,x-R-T-U-Y-[-t.^.a.b.i.n.p.s/W/Y/^/e/i/m/u/}0T0j0u1S1V1Z1]1^1j2Q2R2X2Z2[2e2i2k2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3n$xlOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nv!tT!S!U!V!y!{$r$s%t,V,W,X,Y/P/R1O1P1w2rQ$ShS&_#`)dQ'V$UU']$[$n-VQ'd$^W(w%U,n/i1]U)o%p(^,]Q)q%qS*[&W-RU+U'_.W/vQ+]'eQ-s*oQ.T+YQ.X+ZQ0Q-_Q0]-tR0g.Yw!tT!S!U!V!y!{$r$s%t,V,W,X,Y/P/R1O1P1w2rT%r!s*R${rOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3n$OcObepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n3n^!lP!h!p'k+a.p0u|#RW!Y#`$T$Z%{&]&`&d(d(u)d*]*o+g,`,m-l-t.a/n1aS#ea3mQ$g|Q$h}Q$i!OQ$j!PW%Y!f%]+r.sS%`!i)]Q%f!jQ'W$VQ'X$WQ'Y$XQ'Z$YQ)[%aS)`%d)bQ*e&fQ*m&|S+n'w.nQ-o*nS.f+k.iQ.m+oQ0i._Q0m.eQ1r0hQ2^1sw3g,j2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i$ScOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n3m3nU%i!l.f0mS)R%Y)`Q)c%fw3g,j2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3iV#da%h3m'fYOPVabeipy!Q![!]!_!b!h!j!p#W#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$k$m$t$u$v$w$x%U%e%h%j%m%q%z&P&a&j&l&{&}'S'`'k'l'm'n't'z'{(R(W(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+W+^+a+h+k+s+t+v+},^,b,c,g,j,n-T-U-Y-[.b.e.i.p/W/Y/^/e/i/u/}0T0u1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3nQ&U#YQ*Y&VX/k,s/o1^2R'fYOPVabeipy!Q![!]!_!b!h!j!p#W#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$k$m$t$u$v$w$x%U%e%h%j%m%q%z&P&a&j&l&{&}'S'`'k'l'm'n't'z'{(R(W(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+W+^+a+h+k+s+t+v+},^,b,c,g,j,n-T-U-Y-[.b.e.i.p/W/Y/^/e/i/u/}0T0u1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3nX/k,s/o1^2R'fZOPVabeipy!Q![!]!_!b!h!j!p#W#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$k$m$t$u$v$w$x%U%e%h%j%m%q%z&P&a&j&l&{&}'S'`'k'l'm'n't'z'{(R(W(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+W+^+a+h+k+s+t+v+},^,b,c,g,j,n-T-U-Y-[.b.e.i.p/W/Y/^/e/i/u/}0T0u1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3nX/l,s/o1^2RS#XY/kS$m!Q'`S'_$[$nQ'e$^S)l%j)hQ.W+YQ.Y+ZR/v-V%mXOabepy![!]!_!b#W#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|$]$_$b$e$t$u$v$w$x%U%j%m%q%z&j&l'l'm'n'{(R(f(k(m)Z)g)h)r){*U*V*i*k+^+},^,b,c,g,j,n-T-U-Y-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nQ&S#WR-g*V'c]OPVabeipy![!]!_!b!h!j!p#W#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$t$u$v$w$x%U%e%h%j%m%q%z&P&a&j&l&{&}'S'k'l'm'n't'z'{(R(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+^+a+h+k+s+t+v+},^,b,c,g,j,n,s-T-U-Y-[.b.e.i.p/W/Y/^/e/i/o/u/}0T0u1S1V1Z1]1^2R2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3n!w#PW!f!v#Q#R#`$O$T%X%Y%]%d%y&Q&W&`&d&|(d(u)R)b)d*l*n*o+g+r,`,m,x-R-t.a.s/m1j2Q2X2[2e2kW#ba#d%h3mR*b&aviOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u[!hP!p'k+a.p0uY#aa#d%h&a3mQ&}#{Q(e$vW(y%U,n/i1]S*p'S*rS+h't.bS+o'w.nS+t'z+vS.e+k.iR/o,sh#TW!Y!f$T$Z%](d*o+r,`-t.sQ*P%{Q*^&]Q-j*]R0X-lU)}%z){0TQ,P(UR.v+y$ScOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n3m3nw3g,j2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3ivwOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u`!nP!p'k+a+k.i.p0uQ%V!cQ%g!kQ%o!qS&o#k2wQ'j$fQ)Q%WQ)V%^Q,u)OQ/p,tQ0n.fQ1W/^R1t0m${qOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3n!^#od#g$y$z%S&n&p&r&s&t&u&v&z'P'c'f)p)|+{,a,d-n.Z.y/[1R1Y1|2P3lg2y/b3U3V3X3Y3Z3[3]3a3e3f!Y#rd#g$y$z%S&n&r&s&u&v&z'P'c'f)p)|+{,a,d-n.Z.y/[1R1Y1|2P3lc2|/b3U3X3Y3[3]3a3e3fR'O#{%TgOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3n%ToOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3n%SoOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nQ$f{Q$t!WQ$u!XQ${!^R,k(oQ$`mS(V$l+XQ+T'^S+z(Q(XS-W)j)kQ.S+VQ.l+nQ.z,RQ/y-XQ0e.VQ0r.mQ1d/wQ1q0fR2V1cQ'o$gY*v'W'p'q'r'sQ,Q(VW-w*w*x*y*zQ.k+nQ.w+zU0^-x-y-zS0q.l.mQ0x.zS1m0_0`Q1u0rR2]1na!nP!p'k+a+k.i.p0u`!nP!p'k+a+k.i.p0uT+u'z+vd!jP!h!p'k+a+k.e.i.p0uQ's$gQ'v$hQ(P$jQ*z'WQ*|'XQ+S'ZV+s'z+t+vQ%b!iR,})]Q)Y%`R,|)[Q%l!mQ)T%[Q)X%_Q-P)aR/s-QvuOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u`!nP!p'k+a+k.i.p0uS*q'S*rT+u'z+vQ'r$gS*y'W'sR-z*zQ'q$gQ'y$iU*x'W'r'sQ+O'YQ+m'uS-y*y*zQ-|*{Q0`-zR0b-}Q+e'mQ+f'nR+x'{Q'p$gQ'}$jW*w'W'q'r'sU+Q'Z(O(PU-x*x*y*zS.Q+R+SS0_-y-zQ0d.RR1n0`Q'u$hS*{'X'vR-}*|Q+i'tR0k.bQ+l'uS-{*{+mS0a-|-}R1o0bT.g+k.iQ(O$jS+R'Z(PR.R+SQ'|$jW+P'Z'}(O(PU.P+Q+R+SS0c.Q.RR1p0dvuOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u`!nP!p'k+a+k.i.p0uT+u'z+vQ'x$iS*}'Y'yR.O+OQ+p'wR0s.nR.r+qQ'T$SR*u'VT*q'S*rQ%k!mS)W%_%lR,z)XQ)g%jR-U)h$zoOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nX(x%U,n/i1]%SmOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nU'^$[$n-VS+V'_/vQ.V+YR0f.WX(T$l(Q(U+y_)s%q)r-[/}2Z2i2nQ)u%qR0R-_Q([$oQ)w%sR-b)xT,S(Z,TvwOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/uR1W/^wwOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/uvvOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/uQ%P!`R%R!aR$w!ZvwOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/uR(f$vR$x!ZR(n${T,f(m,gT,e(m,gX({%V(|)Q,vR,t(}Q/n,sR1a/oQ)P%VQ,r(|Q,w)QR/q,vR%W!cQ(z%UV/g,n/i1]QyOQ#jbY$cy#j)h-T/uQ)h%jQ-T)gR/u-UvVOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/uv!gP!p#d&a'S'k't'w'z*r+a+k+v,s.b.i.n.p0u|!xV!g#O#Z#_#}$k%a%e&P&V&Z&{(S(W)e*`+W.^0j2R3`!f#OW!f!v#R$O$T%Y%]%d%y&Q&W&|(d(u)R)b*l*n*o+r,`,m,x-R-t.s/m1j2Q2X2[2e2kQ#Z_S#_a3m$`#}ep![!]!b#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$e%m%q%z&j&l'l'm'n'{(R(f(k)Z)r){*U*i*k+^+},b,c,j-Y-[/W/Y/^/e/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3nQ$k!QS%a!i)]S%e!j+sS&P#W*VQ&V#YU&Z#`&d)dQ&{#{W(S$l(Q(U+yQ(W$mQ)e%hQ*`&`Q+W'`Q.^+gQ0j.aQ2R1^Z3`$v%U,n/i1]Q*W&SR-h*W^#QW#`$O&|(d(u*l!Q%X!f!v$T%]%d&Q&W&`&d)b)d*n*o+g+r,`,m-R-t.a.s/m2Q2e[%x#Q%X%y,x2X2kS%y#R%YQ,x)RQ2X1jR2k2[#Q`OPaby!_!p#d#j#{$b$t$u$v$w$x%U%h%j&a'S'k't'w'z(m)g)h*r+a+k+v,^,g,n,s-T-U.b.i.n.p/i/u0u1]3m[!wV!g#_&{)e3`T#[`!wQ0U-cR1i0UQ&b#bR*c&bQ&g#eR*f&gQ$QfR'Q$QQ+_'fS.[+_1XR1X/bQ!pPU%n!p+a0uQ+a'kR0u.pQ%|#TR*Q%|Q&k#hU*h&k/r3kQ/r,{R3k3jQ)^%bR-O)^Q-Z)mR/{-ZQ.c+iR0l.cQ.i+kR0o.iQ+v'zR.t+vQ.o+pR0t.oQ*r'SR-u*rQ(U$lQ+y(QT,O(U+yQ0O-]R1g0OQ-`)uR0S-`Q,T(ZR.{,TQ/P,VR0{/PQ1x0|S2`1x2bR2b1zQ,g(mR/`,gQ(|%VS,q(|,vR,v)QQ1_/mR2S1_Q,o(zR/j,odxOby#j%j)g)h-T-U/uQ$|!_Q'h$bQ(b$tQ(c$uQ(h$wQ(i$xS,f(m,gR/V,^)ftOPVW_abepy!Q![!]!_!b!f!g!i!j!p!v#O#R#W#Y#Z#_#`#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$O$T$]$_$b$e$k$l$m$t$u$v$w$x%U%Y%]%a%d%e%h%j%m%y%z&P&Q&V&W&Z&`&a&d&j&l&{&|'S'`'k'l'm'n't'w'z'{(Q(R(S(U(W(d(f(k(m(u)R)Z)])b)d)e)g)h){*U*V*`*i*k*l*n*o*r+W+^+a+g+k+r+s+v+y+},^,`,b,c,g,j,m,n,s,x-R-T-U-Y-t.^.a.b.i.n.p.s/W/Y/^/e/i/m/u0T0j0u1S1V1Z1]1^1j2Q2R2X2[2e2k2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3n_)t%q)r-[/}2Z2i2nQ!sTQ$o!SQ$p!UQ$q!VQ%s!yQ%u!{Q(`$rQ(a$sQ)x%tQ*R2rS.},V,XQ/R,WQ/S,YQ0z/PS0|/R1OQ1z1PR2_1w&{SOTabehpy!S!U!V![!]!_!b!y!{#`#j#k#n#o#p#q#r#s#t#u#v#w#x#|$U$[$]$^$_$b$e$n$r$s$t$u$v$w$x%U%j%p%q%t%z&W&l'_'e(R(^(f(k(m)d)g)h)r){*i*k*o+Y+Z+^+},V,W,X,Y,],^,b,c,g,j,n-R-T-U-V-[-_-t.W.Y/P/R/W/Y/^/e/i/u/v/}0T1O1P1S1V1Z1]1w2Z2i2n2r2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nQ)v%qQ-])rQ/|-[Q1f/}Q2j2ZQ2l2iR2p2nxdOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/^/uU#ga$e3mS#ye3hS$ap3iQ$y![Q$z!]Q%S!bQ&n#kQ&p#nQ&q#oQ&r#pQ&s#qQ&t#rQ&u#sQ&v#tQ&w#uS&x#v3QQ&y#wQ&z#xQ'P#|Q'c$]Y'f$_$v,b/Y1V^)p%q)r-[/}2Z2i2nU)|%z){0TS*j&l3TQ+{(RQ,a(fQ,d(kS-m*i3bQ-n*kQ.Z+^Q.y+}Q/[,cQ/b,jQ1R/WQ1Y/eQ1|1SQ2P1ZQ3U2wQ3V2xQ3W2yQ3X2zQ3Y2{Q3Z2|Q3[2}Q3]3OQ3^3PQ3_3RQ3a3SQ3e3cQ3f3dR3l3nvWOby!_#j$b$t$u$w$x%j(m)g)h,^,g-T-U/u`!fP!p'k+a+k.i.p0uW!vV#}&P3`S#`a3m#|$Oep![!]!b#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$e%q%z&l(R(f(k)r){*i*k+^+},b,c,j-[/W/Y/^/e/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3nQ$TiS%]!h.eS%d!j+sf&Q#W%m&j'l'm'n'{)Z*U*V-YQ&W#_Q&`#aS&d#d&aQ&|#{Q(d$vW(u%U,n/i1]Q)b%eQ)d%hQ*l&{Q*n&}S*o'S*rS+g't.bS+r'z+vQ,`(eQ,m(yQ-R)eQ-t*pQ.a+hQ.s+tS/m,s/oQ2Q1^R2e2R'b]OPVabeipy![!]!_!b!h!j!p#W#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$t$u$v$w$x%U%e%h%j%m%q%z&P&a&j&l&{&}'S'k'l'm'n't'z'{(R(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+^+a+h+k+s+t+v+},^,b,c,g,j,n,s-T-U-Y-[.b.e.i.p/W/Y/^/e/i/o/u/}0T0u1S1V1Z1]1^2R2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3nQ$l!QQ(Q$kS(X$m+WQ+X'`R,R(W'sUOPVabeipy!Q![!]!_!b!h!j!p#W#Y#_#a#d#j#k#n#o#p#q#r#s#t#u#v#w#x#{#|#}$]$_$b$e$k$m$t$u$v$w$x%U%e%h%j%m%q%z&P&V&a&j&l&{&}'S'`'k'l'm'n't'z'{(R(W(e(f(k(m(y)Z)e)g)h)r){*U*V*i*k*p*r+W+^+a+h+k+s+t+v+},^,b,c,g,j,n,s-T-U-Y-[.b.e.i.p/W/Y/^/e/i/o/u/}0T0u1S1V1Z1]1^2R2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3`3b3c3d3h3i3m3nR&T#W%OnObepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3nQ#haQ&S#WQ'O#{W)m%m'm'n'{Q*g&jQ+c'lQ,{)ZQ-f*UQ-g*VQ/z-YR3j3mQ*T&RR-e*SU#ca%h3mR&e#d#c_OPV`aby!_!g!p!w#_#d#j#{$b$t$u$v$w$x%U%h%j&a&{'S'k't'w'z(m)e)g)h*r+a+k+v,^,g,n,s-T-U.b.i.n.p/i/u0u1]3`3m#c^OPV`aby!_!g!p!w#_#d#j#{$b$t$u$v$w$x%U%h%j&a&{'S'k't'w'z(m)e)g)h*r+a+k+v,^,g,n,s-T-U.b.i.n.p/i/u0u1]3`3ml#SW!Y!f$T$Z%]%{&]*]*o+r-l-t.sU&[#`&d)dQ*a&`Q,_(dQ,l(uQ.`+gQ/X,`Q/f,mQ0i.aQ1`/nR2U1aQ*O%zQ-c){R1h0TR-d){T#fa3m$vkOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%q%z&l(R(f(k(m)g)r){*i*k+^+},^,b,c,g,j-T-U-[/W/Y/^/e/u/}0T1S1V1Z2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nW(v%U,n/i1]T)i%j)h%SjOabepy![!]!_!b#j#k#n#o#p#q#r#s#t#u#v#w#x#|$]$_$b$e$t$u$v$w$x%U%j%q%z&l(R(f(k(m)g)h)r){*i*k+^+},^,b,c,g,j,n-T-U-[/W/Y/^/e/i/u/}0T1S1V1Z1]2Z2i2n2w2x2y2z2{2|2}3O3P3Q3R3S3T3b3c3d3h3i3m3nT'[$[-VQ'g$_Q(g$vQ/Z,bQ1T/YR2O1V[!oP!p'k+a.p0uT.g+k.iU#UW!Y(dQ%Z!fU'U$T$Z,`Q)U%]S-r*o+rT0[-t.sd!kP!p'k'z+a+k+v.i.p0uV%^!h+t.eR%c!iW%[!f%]+r.sQ)a%dR-Q)bQ)n%mV+d'm'n'{R+j'tT.h+k.iR+q'w`!mP!p'k+a+k.i.p0uT%_!h.eR-^)rR)v%qT/O,V/PQ0}/RR1y1OR1y1PR/c,j",nodeNames:"⚠ LineComment BlockComment Program ModuleDeclaration MarkerAnnotation Identifier ScopedIdentifier . Annotation ) ( AnnotationArgumentList LambdaExpression Definition FormalParameters ReceiverParameter void PrimitiveType TypeName ScopedTypeName GenericType TypeArguments AnnotatedType Wildcard extends super , ArrayType ] Dimension [ this FormalParameter Modifiers public protected private abstract static final strictfp default synchronized native transient volatile sealed non SpreadParameter VariableDeclarator AssignOp } { ArrayInitializer InferredParameters Block BinaryExpression CompareOp CompareOp CompareOp LogicOp LogicOp BitOp BitOp BitOp ArithOp ArithOp BitOp InstanceofExpression instanceof TypePattern TernaryExpression LogicOp : UpdateExpression UpdateOp IntegerLiteral FloatingPointLiteral BooleanLiteral CharacterLiteral StringLiteral TextBlock null ClassLiteral class ParenthesizedExpression ObjectCreationExpression new ArgumentList ClassBody ; FieldDeclaration MethodDeclaration TypeParameters TypeParameter TypeBound Throws throws ClassDeclaration Superclass SuperInterfaces implements InterfaceTypeList Permits permits RecordDeclaration record RecordHeader RecordComponent RecordBody CompactConstructorDeclaration InterfaceDeclaration interface ExtendsInterfaces InterfaceBody ConstantDeclaration EnumDeclaration enum EnumBody EnumConstant EnumBodyDeclarations AnnotationTypeDeclaration AnnotationTypeBody AnnotationTypeElementDeclaration StaticInitializer ConstructorDeclaration ConstructorBody ExplicitConstructorInvocation FieldAccess ArrayAccess MethodInvocation MethodName MethodReference ArrayCreationExpression Dimension UnaryExpression LogicOp BitOp CastExpression AssignmentExpression AssignOp ElementValueArrayInitializer ElementValuePair open module ModuleBody ModuleDirective requires transitive exports to opens uses provides with PackageDeclaration package ImportDeclaration import Asterisk ExpressionStatement LabeledStatement Label IfStatement if else WhileStatement while ForStatement for ForSpec LocalVariableDeclaration var EnhancedForStatement ForSpec AssertStatement assert SwitchStatement switch SwitchBlock SwitchRule SwitchLabel case ThrowStatement throw DoStatement do BreakStatement break ContinueStatement continue ReturnStatement return SynchronizedStatement TryStatement try CatchClause catch CatchFormalParameter CatchType FinallyClause finally TryWithResourcesStatement ResourceSpecification Resource ClassContent",maxTerm:296,nodeProps:[["isolate",-4,1,2,80,81,""],["group",-27,4,56,91,99,106,112,117,122,156,158,161,162,164,167,169,172,174,176,178,184,186,188,190,192,194,195,203,"Statement",-25,6,13,32,57,69,72,75,77,78,79,80,81,82,83,84,86,87,129,130,131,133,134,136,139,140,"Expression",-7,17,18,19,20,21,23,28,"Type"],["openedBy",10,"(",52,"{"],["closedBy",11,")",53,"}"]],propSources:[BQ],skippedNodes:[0,1,2],repeatNodeCount:30,tokenData:"#$g_R!_OX%QXY'fYZ)bZ^'f^p%Qpq'fqr*|rs,^st%Qtu2zuv4avw5qwx7Xxy?pyz@^z{@z{|Ap|}CW}!OCt!O!PEa!P!QNZ!Q!R!+P!R![!.k![!]!;T!]!^!osw<}wx9}x#O<}#O#P&Z#P;'S<};'S;=`?d<%lO<}P>rVOY>oZw>owx?Xx#O>o#P;'S>o;'S;=`?^<%lO>oP?^O!rPP?aP;=`<%l>oT?gP;=`<%l<}T?mP;=`<%l8P_?wVZZ&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QV@eVYR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QVATX$fP&}S!eQOY%QYZ%lZr%Qrs%qs!_%Q!_!`5T!`;'S%Q;'S;=`&s<%lO%QVAwZ!dR&}SOY%QYZ%lZr%Qrs%qs{%Q{|Bj|!_%Q!_!`5T!`;'S%Q;'S;=`&s<%lO%QVBqV!nR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QVC_VkR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_C}[&pW!dR&}SOY%QYZ%lZr%Qrs%qs}%Q}!OBj!O!_%Q!_!`5T!`!aDs!a;'S%Q;'S;=`&s<%lO%QVDzV&vR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_EhZWY&}SOY%QYZ%lZr%Qrs%qs!O%Q!O!PFZ!P!Q%Q!Q![Gi![;'S%Q;'S;=`&s<%lO%QVF`X&}SOY%QYZ%lZr%Qrs%qs!O%Q!O!PF{!P;'S%Q;'S;=`&s<%lO%QVGSV&rR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QTGpc&}S!pPOY%QYZ%lZr%Qrs%qs!Q%Q!Q![Gi![!f%Q!f!gH{!g!hIi!h!iH{!i#R%Q#R#SMc#S#W%Q#W#XH{#X#YIi#Y#ZH{#Z;'S%Q;'S;=`&s<%lO%QTISV&}S!pPOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QTIn]&}SOY%QYZ%lZr%Qrs%qs{%Q{|Jg|}%Q}!OJg!O!Q%Q!Q![KX![;'S%Q;'S;=`&s<%lO%QTJlX&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![KX![;'S%Q;'S;=`&s<%lO%QTK`c&}S!pPOY%QYZ%lZr%Qrs%qs!Q%Q!Q![KX![!f%Q!f!gH{!g!h%Q!h!iH{!i#R%Q#R#SLk#S#W%Q#W#XH{#X#Y%Q#Y#ZH{#Z;'S%Q;'S;=`&s<%lO%QTLpZ&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![KX![#R%Q#R#SLk#S;'S%Q;'S;=`&s<%lO%QTMhZ&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![Gi![#R%Q#R#SMc#S;'S%Q;'S;=`&s<%lO%Q_Nb]&}S!eQOY%QYZ%lZr%Qrs%qsz%Qz{! Z{!P%Q!P!Q!'|!Q!_%Q!_!`5T!`;'S%Q;'S;=`&s<%lO%Q_! `X&}SOY! ZYZ! {Zr! Zrs!#hsz! Zz{!$v{;'S! Z;'S;=`!&[<%lO! Z_!!QT&}SOz!!az{!!s{;'S!!a;'S;=`!#b<%lO!!aZ!!dTOz!!az{!!s{;'S!!a;'S;=`!#b<%lO!!aZ!!vVOz!!az{!!s{!P!!a!P!Q!#]!Q;'S!!a;'S;=`!#b<%lO!!aZ!#bOQZZ!#eP;=`<%l!!a_!#kXOY!$WYZ! {Zr!$Wrs!&bsz!$Wz{!'Q{;'S!$W;'S;=`!'v<%lO!$W_!$ZXOY! ZYZ! {Zr! Zrs!#hsz! Zz{!$v{;'S! Z;'S;=`!&[<%lO! Z_!${Z&}SOY! ZYZ! {Zr! Zrs!#hsz! Zz{!$v{!P! Z!P!Q!%n!Q;'S! Z;'S;=`!&[<%lO! Z_!%uV&}SQZOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_!&_P;=`<%l! Z_!&eXOY!$WYZ! {Zr!$Wrs!!asz!$Wz{!'Q{;'S!$W;'S;=`!'v<%lO!$W_!'TZOY! ZYZ! {Zr! Zrs!#hsz! Zz{!$v{!P! Z!P!Q!%n!Q;'S! Z;'S;=`!&[<%lO! Z_!'yP;=`<%l!$W_!(TV&}SPZOY!'|YZ%lZr!'|rs!(js;'S!'|;'S;=`!)p<%lO!'|_!(oVPZOY!)UYZ%lZr!)Urs!)vs;'S!)U;'S;=`!*y<%lO!)U_!)ZVPZOY!'|YZ%lZr!'|rs!(js;'S!'|;'S;=`!)p<%lO!'|_!)sP;=`<%l!'|_!){VPZOY!)UYZ%lZr!)Urs!*bs;'S!)U;'S;=`!*y<%lO!)UZ!*gSPZOY!*bZ;'S!*b;'S;=`!*s<%lO!*bZ!*vP;=`<%l!*b_!*|P;=`<%l!)UT!+Wq&}S!oPOY%QYZ%lZr%Qrs%qs!O%Q!O!P!-_!P!Q%Q!Q![!.k![!d%Q!d!e!1u!e!f%Q!f!gH{!g!hIi!h!iH{!i!n%Q!n!o!0a!o!z%Q!z!{!3s!{#R%Q#R#S!0}#S#U%Q#U#V!1u#V#W%Q#W#XH{#X#YIi#Y#ZH{#Z#`%Q#`#a!0a#a#l%Q#l#m!3s#m;'S%Q;'S;=`&s<%lO%QT!-fa&}S!pPOY%QYZ%lZr%Qrs%qs!Q%Q!Q![Gi![!f%Q!f!gH{!g!hIi!h!iH{!i#W%Q#W#XH{#X#YIi#Y#ZH{#Z;'S%Q;'S;=`&s<%lO%QT!.ri&}S!oPOY%QYZ%lZr%Qrs%qs!O%Q!O!P!-_!P!Q%Q!Q![!.k![!f%Q!f!gH{!g!hIi!h!iH{!i!n%Q!n!o!0a!o#R%Q#R#S!0}#S#W%Q#W#XH{#X#YIi#Y#ZH{#Z#`%Q#`#a!0a#a;'S%Q;'S;=`&s<%lO%QT!0hV&}S!oPOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QT!1SZ&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![!.k![#R%Q#R#S!0}#S;'S%Q;'S;=`&s<%lO%QT!1zY&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q!R!2j!R!S!2j!S;'S%Q;'S;=`&s<%lO%QT!2q`&}S!oPOY%QYZ%lZr%Qrs%qs!Q%Q!Q!R!2j!R!S!2j!S!n%Q!n!o!0a!o#R%Q#R#S!1u#S#`%Q#`#a!0a#a;'S%Q;'S;=`&s<%lO%QT!3x_&}SOY%QYZ%lZr%Qrs%qs!O%Q!O!P!4w!P!Q%Q!Q![!7V![!c%Q!c!i!7V!i#T%Q#T#Z!7V#Z;'S%Q;'S;=`&s<%lO%QT!4|]&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![!5u![!c%Q!c!i!5u!i#T%Q#T#Z!5u#Z;'S%Q;'S;=`&s<%lO%QT!5zc&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![!5u![!c%Q!c!i!5u!i!r%Q!r!sIi!s#R%Q#R#S!4w#S#T%Q#T#Z!5u#Z#d%Q#d#eIi#e;'S%Q;'S;=`&s<%lO%QT!7^i&}S!oPOY%QYZ%lZr%Qrs%qs!O%Q!O!P!8{!P!Q%Q!Q![!7V![!c%Q!c!i!7V!i!n%Q!n!o!0a!o!r%Q!r!sIi!s#R%Q#R#S!:V#S#T%Q#T#Z!7V#Z#`%Q#`#a!0a#a#d%Q#d#eIi#e;'S%Q;'S;=`&s<%lO%QT!9Qa&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![!5u![!c%Q!c!i!5u!i!r%Q!r!sIi!s#T%Q#T#Z!5u#Z#d%Q#d#eIi#e;'S%Q;'S;=`&s<%lO%QT!:[]&}SOY%QYZ%lZr%Qrs%qs!Q%Q!Q![!7V![!c%Q!c!i!7V!i#T%Q#T#Z!7V#Z;'S%Q;'S;=`&s<%lO%QV!;[X!lR&}SOY%QYZ%lZr%Qrs%qs![%Q![!]!;w!];'S%Q;'S;=`&s<%lO%QV!l!`;'S%Q;'S;=`&s<%lO%QU!>PX!fQ&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`5T!`;'S%Q;'S;=`&s<%lO%QU!>sV!]Q&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QV!?aX!TR&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`+p!`;'S%Q;'S;=`&s<%lO%QV!@TY&gR&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`!>l!`!a!@s!a;'S%Q;'S;=`&s<%lO%QU!@zY!fQ&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`5T!`!a!=x!a;'S%Q;'S;=`&s<%lO%Q_!AsV&kX!kQ&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_!BaX&]Z&}SOY%QYZ%lZr%Qrs%qs#]%Q#]#^!B|#^;'S%Q;'S;=`&s<%lO%QV!CRX&}SOY%QYZ%lZr%Qrs%qs#b%Q#b#c!Cn#c;'S%Q;'S;=`&s<%lO%QV!CsX&}SOY%QYZ%lZr%Qrs%qs#h%Q#h#i!D`#i;'S%Q;'S;=`&s<%lO%QV!DeX&}SOY%QYZ%lZr%Qrs%qs#X%Q#X#Y!EQ#Y;'S%Q;'S;=`&s<%lO%QV!EVX&}SOY%QYZ%lZr%Qrs%qs#f%Q#f#g!Er#g;'S%Q;'S;=`&s<%lO%QV!EwX&}SOY%QYZ%lZr%Qrs%qs#Y%Q#Y#Z!Fd#Z;'S%Q;'S;=`&s<%lO%QV!FiX&}SOY%QYZ%lZr%Qrs%qs#T%Q#T#U!GU#U;'S%Q;'S;=`&s<%lO%QV!GZX&}SOY%QYZ%lZr%Qrs%qs#V%Q#V#W!Gv#W;'S%Q;'S;=`&s<%lO%QV!G{X&}SOY%QYZ%lZr%Qrs%qs#X%Q#X#Y!Hh#Y;'S%Q;'S;=`&s<%lO%QV!HoV'ZR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_!I]b&aZ&}SOY%QYZ%lZr%Qrs%qst%Qtu!IUu!Q%Q!Q![!IU![!c%Q!c!}!IU!}#R%Q#R#S!IU#S#T%Q#T#o!IU#o$g%Q$g;'S!IU;'S;=`!Je<%lO!IU_!JhP;=`<%l!IU_!JrVoZ&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QV!K`VmR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QU!K|X!cQ&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`5T!`;'S%Q;'S;=`&s<%lO%QV!LpV!VR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_!M`Z'dX!bQ&}SOY%QYZ%lZr%Qrs%qs!_%Q!_!`5T!`#p%Q#p#q!NR#q;'S%Q;'S;=`&s<%lO%QU!NYV!`Q&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QV!NvV!UR&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%QT# dV$OP&}SOY%QYZ%lZr%Qrs%qs;'S%Q;'S;=`&s<%lO%Q_#!Su&}S&XZ&_ZOX%QXY'fYZ)bZ^'f^p%Qpq'fqr%Qrs%qst%Qtu2zu!Q%Q!Q![2z![!c%Q!c!}2z!}#R%Q#R#S2z#S#T%Q#T#o2z#o#y%Q#y#z'f#z$f%Q$f$g'f$g#BY2z#BY#BZ# y#BZ$IS2z$IS$I_# y$I_$I|2z$I|$JO# y$JO$JT2z$JT$JU# y$JU$KV2z$KV$KW# y$KW&FU2z&FU&FV# y&FV;'S2z;'S;=`4Z<%lO2z",tokenizers:[0,1,2,3],topRules:{Program:[0,3],ClassContent:[1,206]},dynamicPrecedences:{21:1,246:-1,252:-1},specialized:[{term:245,get:i=>PQ[i]||-1}],tokenPrec:9164}),MQ=rr.define({name:"java",parser:OQ.configure({props:[T2.add({IfStatement:To({except:/^\s*({|else\b)/}),TryStatement:To({except:/^\s*({|catch|finally)\b/}),LabeledStatement:aq,SwitchBlock:i=>{let e=i.textAfter,t=/^\s*\}/.test(e),n=/^\s*(case|default)\b/.test(e);return i.baseIndent+(t?0:n?1:2)*i.unit},Block:iq({closing:"}"}),BlockComment:()=>null,Statement:To({except:/^{/})}),oq.add({"Block SwitchBlock ClassBody ElementValueArrayInitializer ModuleBody EnumBody ConstructorBody InterfaceBody ArrayInitializer":lq,BlockComment(i){return{from:i.from+2,to:i.to-2}}})]}),languageData:{commentTokens:{line:"//",block:{open:"/*",close:"*/"}},indentOnInput:/^\s*(?:case |default:|\{|\})$/}});function ZQ(){return new QV(MQ)}const LQ=" ";function WQ(i,e){let t="",n=e;for(let a=0;a{var b,x;const o=s.trim(),l=a,{chars:u,comment:m}=WQ(s,a);a=m;const c=l?0:((b=/^[}\])\s]*/.exec(o)[0].match(/[}\])]/g))==null?void 0:b.length)??0,d=n.slice(0,Math.max(0,n.length-c));NQ.test(o)&&((x=d.at(-1))!=null&&x.inCase)&&(d.at(-1).inCase=!1);const h=EQ(d);for(const v of u)"{[(".includes(v)?n.push({line:r,char:v}):n.pop();const g=n.at(-1);(g==null?void 0:g.line)===r&&g.char==="{"&&/^switch\b/.test(o)&&(g.char="switch"),(g==null?void 0:g.char)==="switch"&&FQ.test(o)&&(g.inCase=!0);const f=r+1;return ft||l&&!o.startsWith("*")?s:o===""?"":LQ.repeat(h)+(l?" ":"")+o}).join(` -`)}const RQ=[">>>=","<<=",">>=","...","->","::","++","--","&&","||","==","!=","<=",">=","+=","-=","*=","/=","%=","&=","|=","^=","<<"],JQ=new Set(["=","==","!=","<=",">=","&&","||","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=",">>>=","->","&","|","^","<<"]),GQ=new Set(["+","-","*","/","%"]),HQ=new Set(["return","case","throw","new","else","do","yield","assert","instanceof"]),VQ=new Set(["byte","short","int","long","float","double","char","boolean"]),qQ=new Set(["if","for","while","switch","catch","synchronized","try"]),UQ=new Set(["else","catch","finally"]);function QQ(i){const e=[];let t=1,n=0;const a=(s,r)=>{e.push({kind:s,text:i.slice(n,r),from:n,to:r,line:t});for(let o=n;oi.startsWith(l,n));a(o?"op":"other",n+((o==null?void 0:o.length)??1))}}return e}function $Q(i,e){var n,a;const t=i[e];return t?t.kind==="num"||t.kind==="str"?!0:t.kind==="word"?!HQ.has(t.text):t.text==="]"?!0:t.text===")"?!(VQ.has(((n=i[e-1])==null?void 0:n.text)??"")&&((a=i[e-2])==null?void 0:a.text)==="("):!1:!1}const Gd=i=>(i==null?void 0:i.kind)==="word"&&new RegExp("^\\p{Lu}","u").test(i.text);function YQ(i,e=1,t=Number.MAX_SAFE_INTEGER){const n=QQ(i),a=n.length,s=new Array(a).fill(!1),r=new Array(a).fill(!1),o=new Array(a).fill(-1),l=new Array(a).fill(!1),u=new Array(a).fill(!1),m=[{at:-1,block:!0,paren:0}];let c=0,d=0;n.forEach((g,f)=>{var v,w;const b=n[f-1],x=m.at(-1);if(g.kind!=="comment")if(g.text==="(")x.paren++;else if(g.text===")")x.paren=Math.max(0,x.paren-1);else if(g.text==="{"){const z=(b==null?void 0:b.text)??"",I=["=",",","]","("].includes(z)||z==="{"&&!r[f-1];r[f]=!I,l[f]=z==="do",m.push({at:f,block:!I,paren:0})}else if(g.text==="}")if(m.length>1){const z=m.pop();r[f]=z.block,o[f]=z.at,o[z.at]=f,l[f]=l[z.at]}else r[f]=!0;else if(g.text===";")u[f]=x.block&&x.paren===0,d=0;else if(g.text==="?"&&(b==null?void 0:b.text)!=="<"&&![")",";",",",void 0].includes((v=n[f+1])==null?void 0:v.text))s[f]=!0,d++;else if(g.text===":")s[f]=d>0||x.paren>0,d>0&&d--;else if(g.text==="<"){const z=n[f+1];(Gd(b)||(b==null?void 0:b.text)===".")&&(Gd(z)||(z==null?void 0:z.text)===">"||(z==null?void 0:z.text)==="?")?c++:s[f]=!((b==null?void 0:b.text)==="<"||(z==null?void 0:z.text)==="<")}else g.text===">"?c>0?c--:s[f]=!((b==null?void 0:b.text)===">"||((w=n[f+1])==null?void 0:w.text)===">"):JQ.has(g.text)?s[f]=!0:GQ.has(g.text)&&(s[f]=$Q(n,f-1))});const h=[];for(let g=1;gt||f.kind==="comment")continue;let v;b.kind==="comment"?v=void 0:r[g-1]&&f.text==="{"&&o[g-1]!==g||r[g]&&b.text==="}"&&o[g]!==g-1?v=` -`:r[g-1]&&f.text==="}"?UQ.has(b.text)||b.text==="while"&&l[g-1]?v=" ":[")",";",",","."].includes(b.text)||(v=` -`):u[g-1]?v=` -`:b.text===","||b.text===";"?v="":f.text===","?v=" ":f.text===";"?v=b.text===")"?"":" ":f.text==="("||b.text===")"?v="":(b.text==="("&&qQ.has(f.text)||b.text==="{"&&r[g]||s[g-1]||s[g])&&(v=" "),v!==void 0&&v!==x&&h.push({from:f.to,to:b.from,insert:v})}return h}const KQ=[["nextInt","","int","Ganze Zahl einlesen","Read a whole number","Ler um número inteiro"],["nextDouble","","double","Kommazahl einlesen","Read a decimal number","Ler um número decimal"],["nextLine","","String","Ganze Zeile als Text einlesen","Read a whole line as text","Ler uma linha inteira como texto"],["next","","String","Ein Wort einlesen (bis zum Leerzeichen)","Read one word (up to a space)","Ler uma palavra (até ao espaço)"],["nextBoolean","","boolean","true oder false einlesen","Read true or false","Ler true ou false"],["nextLong","","long","Große ganze Zahl einlesen","Read a large whole number","Ler um número inteiro grande"],["hasNextInt","","boolean","Kommt als Nächstes eine ganze Zahl?","Is the next input a whole number?","A próxima entrada é um número inteiro?"],["hasNextDouble","","boolean","Kommt als Nächstes eine Kommazahl?","Is the next input a decimal number?","A próxima entrada é um número decimal?"],["hasNextLine","","boolean","Gibt es noch eine Zeile?","Is there another line?","Existe mais uma linha?"],["hasNext","","boolean","Gibt es noch ein Wort?","Is there another word?","Existe mais uma palavra?"],["close","","void","Eingabe schließen","Close the input","Fechar a entrada"]],XQ=[["length","","int","Anzahl der Zeichen","Number of characters","Número de caracteres"],["charAt","index","char","Zeichen an Stelle index (ab 0)","Character at position index (from 0)","Carácter na posição index (a partir de 0)"],["equals","text","boolean","Gleicher Text? (statt ==)","Same text? (instead of ==)","Mesmo texto? (em vez de ==)"],["equalsIgnoreCase","text","boolean","Gleicher Text, groß/klein egal?","Same text, ignoring case?","Mesmo texto, ignorando maiúsculas?"],["compareTo","text","int","Alphabetisch vergleichen (<0, 0, >0)","Compare alphabetically (<0, 0, >0)","Comparar alfabeticamente (<0, 0, >0)"],["substring","von, bis","String","Teiltext von Stelle von bis vor bis","Part of the text from start up to before end","Parte do texto do início até antes do fim"],["indexOf","text","int","Erste Stelle des Textes (−1 = fehlt)","First position of the text (−1 = missing)","Primeira posição do texto (−1 = não existe)"],["lastIndexOf","text","int","Letzte Stelle des Textes (−1 = fehlt)","Last position of the text (−1 = missing)","Última posição do texto (−1 = não existe)"],["contains","text","boolean","Kommt der Text darin vor?","Does it contain the text?","Contém o texto?"],["startsWith","text","boolean","Fängt damit an?","Starts with it?","Começa com isto?"],["endsWith","text","boolean","Hört damit auf?","Ends with it?","Termina com isto?"],["toUpperCase","","String","In GROSSBUCHSTABEN","In UPPER CASE","Em MAIÚSCULAS"],["toLowerCase","","String","In kleinbuchstaben","In lower case","Em minúsculas"],["trim","","String","Leerzeichen vorn und hinten weg","Remove spaces at both ends","Remover espaços no início e no fim"],["replace","alt, neu","String","Text ersetzen","Replace text","Substituir texto"],["split","trenner","String[]",'In Teile zerlegen, z. B. split(";")','Split into parts, e.g. split(";")','Dividir em partes, p. ex. split(";")'],["isEmpty","","boolean","Leerer Text?","Empty text?","Texto vazio?"],["toCharArray","","char[]","Als Array von Zeichen","As an array of characters","Como array de caracteres"]],b3=[["println","wert","void","Ausgeben, danach neue Zeile","Print, then a new line","Escrever e mudar de linha"],["print","wert","void","Ausgeben ohne neue Zeile","Print without a new line","Escrever sem mudar de linha"],["printf","format, werte","PrintStream",'Formatiert ausgeben, z. B. "%.2f"','Print formatted, e.g. "%.2f"','Escrever formatado, p. ex. "%.2f"']],e$=[...b3,["close","","void","Datei speichern und schließen","Save and close the file","Guardar e fechar o ficheiro"]],t$=[["readLine","","String","Nächste Zeile lesen (null = Ende)","Read the next line (null = end)","Ler a próxima linha (null = fim)"],["close","","void","Datei schließen","Close the file","Fechar o ficheiro"]],n$=[["exists","","boolean","Gibt es die Datei?","Does the file exist?","O ficheiro existe?"],["getName","","String","Name der Datei","Name of the file","Nome do ficheiro"],["length","","long","Größe in Bytes","Size in bytes","Tamanho em bytes"]],i$=[["getMessage","","String","Fehlermeldung als Text","Error message as text","Mensagem de erro como texto"],["printStackTrace","","void","Fehler mit Zeilen ausgeben","Print the error with its lines","Mostrar o erro com as linhas"]],a$=[["getSource","","Object","Welcher Knopf wurde geklickt?","Which button was clicked?","Que botão foi clicado?"],["getActionCommand","","String","Beschriftung des Knopfs","Label of the button","Texto do botão"]],s$=[["setBackground","farbe","void","Hintergrundfarbe, z. B. Color.RED","Background colour, e.g. Color.RED","Cor de fundo, p. ex. Color.RED"],["setForeground","farbe","void","Schriftfarbe","Text colour","Cor do texto"],["setFont","schrift","void",'Schrift, z. B. new Font("Arial", Font.BOLD, 24)','Font, e.g. new Font("Arial", Font.BOLD, 24)','Letra, p. ex. new Font("Arial", Font.BOLD, 24)'],["setVisible","true","void","Anzeigen (true) oder verstecken (false)","Show (true) or hide (false)","Mostrar (true) ou esconder (false)"],["setEnabled","true","void","Anklickbar (true) oder grau (false)","Clickable (true) or greyed out (false)","Clicável (true) ou cinzento (false)"],["setSize","breite, höhe","void","Größe in Pixeln","Size in pixels","Tamanho em píxeis"]],r$=[["add","baustein","Component","Baustein hinzufügen","Add a component","Adicionar um componente"],["setLayout","layout","void","Anordnung, z. B. new FlowLayout()","Layout, e.g. new FlowLayout()","Disposição, p. ex. new FlowLayout()"],["remove","baustein","void","Baustein entfernen","Remove a component","Remover um componente"]],o$=[["setTitle","text","void","Titel oben im Fenster","Title at the top of the window","Título no topo da janela"],["setDefaultCloseOperation","JFrame.EXIT_ON_CLOSE","void","Was beim Schließen passiert","What happens when closing","O que acontece ao fechar"],["setLocationRelativeTo","null","void","Mit null: Fenster in die Mitte","With null: centre the window","Com null: janela no centro"],["setResizable","false","void","Größe änderbar?","Can the size change?","Pode mudar de tamanho?"],["pack","","void","Größe passend zum Inhalt","Size to fit the content","Tamanho ajustado ao conteúdo"],["getContentPane","","JPanel","Inhaltsfläche des Fensters","Content area of the window","Área de conteúdo da janela"],["dispose","","void","Fenster schließen","Close the window","Fechar a janela"]],l$=[["setText","text","void","Text setzen","Set the text","Definir o texto"],["getText","","String","Text holen","Get the text","Obter o texto"]],u$=[["addActionListener","e -> { }","void","Was beim Klick passiert","What happens on click","O que acontece ao clicar"],["setOpaque","true","void","Hintergrund sichtbar (für Farben)","Show the background (for colours)","Mostrar o fundo (para cores)"],["setBorderPainted","false","void","Rahmen zeichnen?","Draw the border?","Desenhar a borda?"]],m$=[["setOpaque","true","void","Hintergrund sichtbar (für Farben)","Show the background (for colours)","Mostrar o fundo (para cores)"],["setHorizontalAlignment","JLabel.CENTER","void","Text links, mittig oder rechts","Text left, centre or right","Texto à esquerda, centro ou direita"]],c$=[["addActionListener","e -> { }","void","Was bei Enter passiert","What happens on Enter","O que acontece ao carregar em Enter"],["setEditable","false","void","Darf man hineinschreiben?","Can you type into it?","Pode-se escrever?"],["setColumns","anzahl","void","Breite in Zeichen","Width in characters","Largura em caracteres"],["requestFocus","","void","Cursor hineinsetzen","Put the cursor into it","Pôr o cursor no campo"]],d$=[["length",null,"int","Anzahl der Elemente (ohne Klammern)","Number of elements (no brackets)","Número de elementos (sem parênteses)"]],Po=[["add","wert","boolean","Element hinten anhängen","Append an element at the end","Acrescentar um elemento no fim"],["get","index","E","Element an Stelle index holen (ab 0)","Get the element at index (from 0)","Obter o elemento na posição index (a partir de 0)"],["size","","int","Anzahl der Elemente","Number of elements","Número de elementos"],["set","index, wert","E","Element an Stelle index ersetzen","Replace the element at index","Substituir o elemento na posição index"],["remove","index","E","Element an Stelle index entfernen","Remove the element at index","Remover o elemento na posição index"],["contains","wert","boolean","Ist das Element in der Liste?","Is the element in the list?","O elemento está na lista?"],["indexOf","wert","int","Stelle des Elements (-1 = nicht da)","Position of the element (-1 = not found)","Posição do elemento (-1 = não existe)"],["isEmpty","","boolean","Ist die Liste leer?","Is the list empty?","A lista está vazia?"],["clear","","void","Alle Elemente entfernen","Remove all elements","Remover todos os elementos"]],Oo={Scanner:KQ,String:XQ,PrintStream:b3,PrintWriter:e$,BufferedReader:t$,FileReader:[["close","","void","Datei schließen","Close the file","Fechar o ficheiro"]],File:n$,Exception:i$,ActionEvent:a$,Component:s$,Container:r$,JFrame:o$,JPanel:[],JButton:u$,JLabel:m$,JTextField:c$,TextHolder:l$,ArrayList:Po,List:Po,LinkedList:Po,LED:[["setColor","farbe","void",'Farbe der LED, z. B. "grün" oder "aus"','Colour of the LED, e.g. "green" or "off"','Cor do LED, p. ex. "green" ou "off"']]},h$={JFrame:["Container","Component"],JPanel:["Container","Component"],JButton:["TextHolder","Component"],JLabel:["TextHolder","Component"],JTextField:["TextHolder","Component"],PrintWriter:[]},x3={mbot:[["forward","cm","void","Geradeaus vorwärts fahren","Drive forward","Andar para a frente"],["forward",'wert, "Umdrehungen"',"void",'Überladen: mit Einheit "cm" oder "Umdrehungen" (1 Umdrehung ≈ 20,4 cm)','Overloaded: with unit "cm" or "rotations" (1 rotation ≈ 20.4 cm)','Sobrecarregado: com unidade "cm" ou "rotações" (1 rotação ≈ 20,4 cm)'],["backward","cm","void","Rückwärts fahren","Drive backward","Andar para trás"],["backward",'wert, "Umdrehungen"',"void",'Überladen: rückwärts mit Einheit "cm" oder "Umdrehungen"','Overloaded: backward with unit "cm" or "rotations"','Sobrecarregado: para trás com unidade "cm" ou "rotações"'],["turnLeft","grad","void","Auf der Stelle nach links drehen","Turn left on the spot","Rodar para a esquerda"],["turnRight","grad","void","Auf der Stelle nach rechts drehen","Turn right on the spot","Rodar para a direita"],["stop","","void","Motoren anhalten","Stop the motors","Parar os motores"],["drive","links, rechts","void","Räder einzeln in Prozent (−100–100), fährt weiter bis stop()","Wheels separately in percent (−100–100), keeps going until stop()","Rodas separadas em percentagem (−100–100), continua até stop()"],["resetTimer","","void","Stoppuhr auf 0 setzen","Reset the stopwatch to 0","Pôr o cronómetro a 0"],["pause","ms","void","Millisekunden warten","Wait milliseconds","Esperar milissegundos"],["setLed","rot, gruen, blau","void","LEDs in einer Farbe (je 0–255)","LEDs in a colour (0–255 each)","LEDs numa cor (0–255 cada)"],["ledOff","","void","LEDs aus","LEDs off","Desligar LEDs"],["led","nr","LED",'Eine LED (0–4), dann .setColor("grün") oder .setColor("aus")','One LED (0–4), then .setColor("green") or .setColor("off")','Um LED (0–4), depois .setColor("green") ou .setColor("off")'],["showText",'"Hallo"',"void","Text auf dem Bildschirm zeigen","Show text on the screen","Mostrar texto no ecrã"],["emotion",'"smile"',"void","Gefühl zeigen: LEDs, Ton, Bewegung (smile, hello, sad …)","Show a feeling: LEDs, sound, motion (smile, hello, sad …)","Mostrar um sentimento: LEDs, som, movimento (smile, hello, sad …)"],["playTone","hz, ms","void","Ton spielen, z. B. playTone(440, 500)","Play a tone, e.g. playTone(440, 500)","Tocar um som, p. ex. playTone(440, 500)"],["getDistance","","int","Abstand vorn in cm (Ultraschall)","Distance ahead in cm (ultrasonic)","Distância à frente em cm (ultrassom)"],["getLine","","int","Liniensensor: 0 = keine Linie, 15 = alle vier auf der Linie","Line sensor: 0 = no line, 15 = all four on the line","Sensor de linha: 0 = sem linha, 15 = os quatro na linha"],["isLineLeft","","boolean","Linie unter dem linken mittleren Fühler","Line under the left middle probe","Linha sob a sonda central esquerda"],["isLineRight","","boolean","Linie unter dem rechten mittleren Fühler","Line under the right middle probe","Linha sob a sonda central direita"],["getBrightness","","int","Helligkeit 0–100","Brightness 0–100","Luminosidade 0–100"],["getLoudness","","int","Lautstärke 0–100","Loudness 0–100","Volume 0–100"],["getColor","","String",'Farbe unter dem mBot2, z. B. "rot"','Colour under the mBot2, e.g. "red"','Cor por baixo do mBot2, p. ex. "red"'],["isButtonA","","boolean","Taste A ist gedrückt","Button A is pressed","O botão A está premido"],["isButtonB","","boolean","Taste B ist gedrückt","Button B is pressed","O botão B está premido"],["isJoystickUp","","boolean","Joystick nach oben gedrückt","Joystick pushed up","Joystick para cima"],["isJoystickDown","","boolean","Joystick nach unten gedrückt","Joystick pushed down","Joystick para baixo"],["isJoystickLeft","","boolean","Joystick nach links gedrückt","Joystick pushed left","Joystick para a esquerda"],["isJoystickRight","","boolean","Joystick nach rechts gedrückt","Joystick pushed right","Joystick para a direita"],["getPitch","","int","Neigung nach vorn (+) oder hinten (−) in Grad","Tilt forward (+) or back (−) in degrees","Inclinação para a frente (+) ou trás (−) em graus"],["getRoll","","int","Neigung zur Seite in Grad","Tilt sideways in degrees","Inclinação lateral em graus"],["isShaken","","boolean","Der mBot2 wird geschüttelt","The mBot2 is being shaken","O mBot2 está a ser abanado"],["getTimer","","double","Stoppuhr in Sekunden","Stopwatch in seconds","Cronómetro em segundos"],["getBattery","","int","Akku in Prozent","Battery in percent","Bateria em percentagem"]],System:[["out",null,"PrintStream","Ausgabe in die Konsole","Output to the console","Saída para a consola"],["in",null,"InputStream","Eingabe von der Tastatur (für Scanner)","Keyboard input (for Scanner)","Entrada do teclado (para Scanner)"],["err",null,"PrintStream","Fehlerausgabe","Error output","Saída de erros"],["currentTimeMillis","","long","Uhrzeit in Millisekunden","Time in milliseconds","Hora em milissegundos"],["exit","0","void","Programm beenden","End the program","Terminar o programa"]],Math:[["random","","double","Zufallszahl von 0 bis unter 1","Random number from 0 to below 1","Número aleatório de 0 até menos de 1"],["abs","zahl","double","Betrag (ohne Minus)","Absolute value (no minus)","Valor absoluto (sem menos)"],["sqrt","zahl","double","Wurzel","Square root","Raiz quadrada"],["pow","basis, exponent","double","Hoch: pow(2, 3) = 8","Power: pow(2, 3) = 8","Potência: pow(2, 3) = 8"],["max","a, b","double","Größere von zwei Zahlen","Larger of two numbers","Maior de dois números"],["min","a, b","double","Kleinere von zwei Zahlen","Smaller of two numbers","Menor de dois números"],["round","zahl","long","Runden","Round","Arredondar"],["floor","zahl","double","Abrunden","Round down","Arredondar para baixo"],["ceil","zahl","double","Aufrunden","Round up","Arredondar para cima"],["PI",null,"double","Kreiszahl π = 3.14159…","Pi π = 3.14159…","Pi π = 3,14159…"]],Integer:[["parseInt","text","int","Text in ganze Zahl umwandeln","Turn text into a whole number","Converter texto em número inteiro"],["toString","zahl","String","Zahl in Text umwandeln","Turn a number into text","Converter número em texto"],["MAX_VALUE",null,"int","Größte int-Zahl","Largest int value","Maior valor int"],["MIN_VALUE",null,"int","Kleinste int-Zahl","Smallest int value","Menor valor int"]],Double:[["parseDouble","text","double","Text in Kommazahl umwandeln","Turn text into a decimal number","Converter texto em número decimal"],["toString","zahl","String","Zahl in Text umwandeln","Turn a number into text","Converter número em texto"]],String:[["valueOf","wert","String","Wert in Text umwandeln","Turn a value into text","Converter valor em texto"],["format","format, werte","String",'Formatierter Text, z. B. "%.2f"','Formatted text, e.g. "%.2f"','Texto formatado, p. ex. "%.2f"'],["join","trenner, teile","String","Teile mit Trenner verbinden","Join parts with a separator","Juntar partes com um separador"]],Character:[["isDigit","zeichen","boolean","Ist es eine Ziffer?","Is it a digit?","É um algarismo?"],["isLetter","zeichen","boolean","Ist es ein Buchstabe?","Is it a letter?","É uma letra?"],["isUpperCase","zeichen","boolean","Ist es ein Großbuchstabe?","Is it upper case?","É maiúscula?"],["toUpperCase","zeichen","char","In Großbuchstaben","To upper case","Para maiúscula"],["toLowerCase","zeichen","char","In Kleinbuchstaben","To lower case","Para minúscula"]],Files:[["readString",'Path.of("datei.txt")',"String","Ganze Datei als Text lesen","Read the whole file as text","Ler o ficheiro inteiro como texto"],["writeString","pfad, text","Path","Text in Datei schreiben","Write text to a file","Escrever texto num ficheiro"],["readAllLines","pfad","List","Alle Zeilen lesen","Read all lines","Ler todas as linhas"],["exists","pfad","boolean","Gibt es die Datei?","Does the file exist?","O ficheiro existe?"]],Path:[["of",'"datei.txt"',"Path","Pfad zu einer Datei","Path to a file","Caminho para um ficheiro"]],JFrame:[["EXIT_ON_CLOSE",null,"int","Programm endet beim Schließen","Program ends when closed","O programa termina ao fechar"]],JLabel:[["CENTER",null,"int","Mittig","Centred","Ao centro"],["LEFT",null,"int","Links","Left","À esquerda"],["RIGHT",null,"int","Rechts","Right","À direita"]],Font:[["BOLD",null,"int","Fett","Bold","Negrito"],["PLAIN",null,"int","Normal","Plain","Normal"],["ITALIC",null,"int","Kursiv","Italic","Itálico"]],Color:[["RED","Rot","Red","Vermelho"],["GREEN","Grün","Green","Verde"],["BLUE","Blau","Blue","Azul"],["YELLOW","Gelb","Yellow","Amarelo"],["ORANGE","Orange","Orange","Laranja"],["WHITE","Weiß","White","Branco"],["BLACK","Schwarz","Black","Preto"],["GRAY","Grau","Grey","Cinzento"],["LIGHT_GRAY","Hellgrau","Light grey","Cinzento-claro"],["PINK","Rosa","Pink","Rosa"],["CYAN","Türkis","Cyan","Ciano"],["MAGENTA","Magenta","Magenta","Magenta"]].map(([i,e,t,n])=>[i,null,"Color",e,t,n])};function v3(i,e){const[t,n,a,s,r,o]=i;return{name:t,kind:n===null?"field":"method",params:!!n,returns:a,description:e==="de"?s:e==="pt"?o:r}}const d0=i=>i.replace(/<.*>$/,""),w3=i=>{var e;return(e=/<\s*([^<>,]+?)\s*>$/.exec(i))==null?void 0:e[1]};function y3(i){if(/\[\]$/.test(i))return d$;if(i=d0(i),/Exception$|^Throwable$/.test(i))return Oo.Exception;if(!Oo[i])return;const t=new Set,n=[];for(const a of[i,...h$[i]??[]])for(const s of Oo[a]??[])t.has(s[0])||(t.add(s[0]),n.push(s));return n}function YY(i,e="de"){return(y3(i)??[]).map(t=>v3(t,e))}const g$="[A-Z]\\w*|int|long|double|float|boolean|char|byte|short|var";function p$(i,e){var a,s;const t=new RegExp(`(?:^|[^\\w.])(${g$})\\s*(?:<([^<>;]*)>)?((?:\\s*\\[\\s*\\])*)\\s+${e}\\s*(?:\\[\\s*\\])*\\s*(=\\s*([^;]*))?(?=[=;,):]|$)`,"gm");let n;for(const r of i.matchAll(t)){let o=r[1];if(o==="var"){const u=r[5]??"";if(o=((a=/^new\s+([A-Z]\w*)/.exec(u))==null?void 0:a[1])??(/^"/.test(u)?"String":""),!o)continue}if(/^(return|new|else|case|throw)$/.test(o))continue;const l=(s=r[2])==null?void 0:s.replace(/\s+/g,"");n=o+(l?`<${l}>`:"")+(r[3]??"").replace(/\s+/g,"")}return n}function f$(i){let e=0,t;const n=/^"(?:[^"\\]|\\.)*"/.exec(i);if(n)t='"',e=n[0].length;else{const r=/^[A-Za-z_]\w*/.exec(i);if(!r)return;t=r[0],e=r[0].length}const a=[{name:t,call:!1,indices:0}],s=(r,o)=>{let l=0;for(;e0;){const n=i[t-1];if(/[\w.]/.test(n))t--;else if(n===")"||n==="]"){const a=n===")"?"(":"[";let s=0,r=t-1;for(;r>=0;r--)if(i[r]===n)s++;else if(i[r]===a&&--s===0)break;if(r<0)return t;t=r}else if(n==='"'){const a=i.lastIndexOf('"',t-2);if(a<0)return t;t=a}else break}return t}const Hd=(i,e)=>i.slice(0,Math.max(0,i.length-2*e));function z3(i,e,t,n){const a=e?t==null?void 0:t.statics(d0(i)):t==null?void 0:t.members(d0(i));if(a)return a;const s=e?x3[i]:y3(i);return s==null?void 0:s.map(r=>v3(r,n))}function x$(i,e,t,n){var s,r;const a=(r=(s=z3(i,t,n,"en"))==null?void 0:s.find(o=>o.name===e))==null?void 0:r.returns;return a==="E"?w3(i)??"Object":a}function v$(i,e,t){const n=f$(e);if(!n)return;const[a,...s]=n.links;let r,o=!1;if(n.head==='"')r="String";else if(a.name==="this"&&(t!=null&&t.thisType)&&!a.call&&!a.indices)r=t.thisType;else{if(r=p$(i,a.name),!r&&(x3[a.name]||t!=null&&t.statics(a.name))&&!a.call&&(r=a.name,o=!0),!r)return;a.indices&&(r=Hd(r,a.indices))}for(const l of s){const u=x$(r,l.name,o,t);if(!u)return;r=Hd(u,l.indices),o=!1}return{type:r,isStatic:o}}function w$(i,e,t="de",n){const a=i.lastIndexOf(` -`,e-1)+1,s=i.slice(a,e),r=/\.\s*(\w*)$/.exec(s);if(!r)return;const o=r.index,l=s.slice(b$(s,o),o);if(!l||/^\d/.test(l))return;const u=v$(i,l,n);if(!u)return;const m=z3(u.type,u.isStatic,n,t);if(!m)return;const c=w3(u.type)??"Object";return{from:e-r[1].length,members:m.map(d=>d.returns==="E"?{...d,returns:c}:d)}}const Vd=Vn.define(),Zu=pe.define(),y$=fe.mark({class:"cm-ph"}),z$=fe.mark({class:"cm-err"}),qd=fe.line({class:"cm-debug-line"});class I$ extends X1{constructor(e){super(),this.fix=e}eq(e){return JSON.stringify(e.fix)===JSON.stringify(this.fix)}toDOM(e){const t=document.createElement("button");return t.type="button",t.className="cm-quickfix",t.textContent="💡",t.dataset.tip=this.fix.title,t.setAttribute("aria-label",this.fix.title),t.addEventListener("mousedown",n=>n.preventDefault()),t.addEventListener("click",()=>{const{from:n,to:a,insert:s}=this.fix;a>e.state.doc.length||(e.dispatch({changes:{from:n,to:a,insert:s},selection:{anchor:n+s.length},userEvent:"input"}),e.focus())}),t}ignoreEvent(){return!0}}function Ud(i,e){const t=i.doc.length,n=[];e.current!==void 0&&e.currentr===qd;n.sort((r,o)=>r.from-o.from||Number(a(o.deco))-Number(a(r.deco))||r.to-o.to);const s=new c1;for(const r of n)s.add(r.from,r.to,r.deco);return s.finish()}const K1=Nt.define({create:()=>({marks:{placeholders:[],problems:[]},decorations:fe.none}),update(i,e){for(const a of e.effects)if(a.is(Zu))return{marks:a.value,decorations:Ud(e.state,a.value)};if(!e.docChanged)return i;const t=a=>({from:e.changes.mapPos(a.from,1),to:e.changes.mapPos(a.to,-1)}),n={...i.marks,placeholders:i.marks.placeholders.map(t).filter(a=>a.from$.decorations.from(i,e=>e.decorations)});class k$ extends cn{constructor(){super(...arguments);y(this,"elementClass","cm-error-line")}}const S$=new k$,T$=Pi.compute([K1],i=>{const e=[...new Set(i.field(K1).marks.errorLines??[])].filter(t=>tt-n);return oe.of(e.map(t=>S$.range(i.doc.line(t+1).from)))}),Qd={ClassDeclaration:"class",InterfaceDeclaration:"class",EnumDeclaration:"class",MethodDeclaration:"method",ConstructorDeclaration:"method",ForStatement:"loop",EnhancedForStatement:"loop",WhileStatement:"loop",DoStatement:"loop",IfStatement:"if",SwitchStatement:"if",TryStatement:"try"};function _$(i,e){const t=i.lineAt(e);if(!t.text.trimEnd().endsWith("{"))return t.number;let n=0;for(let a=t.number;a<=i.lines;a++)for(const s of i.line(a).text.replace(/"(?:[^"\\]|\\.)*"|\/\/.*$/g,""))if(s==="{")n++;else if(s==="}"&&--n===0)return a;return t.number}function A$(i){const{state:e}=i,t=e.doc,n=i.contentDOM.querySelector(".cm-line");if(!n)return[];const a=getComputedStyle(n),s=i.scrollDOM.getBoundingClientRect(),r=i.contentDOM.getBoundingClientRect(),o=s.left-i.scrollDOM.scrollLeft,l=s.top-i.scrollDOM.scrollTop,u=r.left-o+parseFloat(a.paddingLeft)+parseFloat(a.borderLeftWidth),m=r.right-o-6,c=i.documentTop-l,d=e.tabSize,h=i.defaultCharacterWidth,g=(z,I,T,k)=>{const C=t.line(I),P=ca(/^\s*/.exec(C.text)[0],d),D=u+P*h-7,B=i.lineBlockAt(C.from).top+c+k,j=i.lineBlockAt(t.line(T).from).bottom+c-k;return new Lr(z,D,B,Math.max(m-D,20),Math.max(j-B,4))},f=[],b=et(e).topNode,x=[];for(let z=b.firstChild;z;z=z.nextSibling){if(Qd[z.name]==="class"||z.name==="MethodDeclaration"){x.length=0;break}z.name!=="ImportDeclaration"&&z.name!=="PackageDeclaration"&&!z.name.includes("Comment")&&x.push(z)}x.length&&f.push(g("cm-scope cm-scope-method",t.lineAt(x[0].from).number,t.lineAt(x[x.length-1].to).number,1));const v=[];et(e).iterate({from:i.viewport.from,to:i.viewport.to,enter:z=>{var C;const I=Qd[z.name];if(!I||z.name==="IfStatement"&&((C=z.node.parent)==null?void 0:C.name)==="IfStatement")return;const T=t.lineAt(z.from).number,k=Math.max(t.lineAt(z.to).number,_$(t,z.from));v.push({kind:I,from:T,to:k}),f.push(g(`cm-scope cm-scope-${I}`,T,k,1))}});for(let z=1;z<=t.lines;z++){let I;for(const k of v)k.from<=z&&z<=k.to&&(!I||k.from>=I.from)&&(I=k);if(!I||I.kind!=="loop"&&I.kind!=="if"||z===I.from)continue;const T=t.line(z).text.trim();!T||T.startsWith("}")||T.endsWith("{")||T.startsWith("//")||T.startsWith("case ")||T.startsWith("default")||f.push(g("cm-scope cm-scope-method cm-scope-stmt",z,z,2))}const w=e.field(K1).marks.lines;return w&&w.fromi.docChanged||i.viewportChanged||i.geometryChanged||et(i.state)!==et(i.startState)||i.transactions.some(e=>e.effects.some(t=>t.is(Zu)))});class D$ extends cn{toDOM(){const e=document.createElement("span");return e.className="cm-breakpoint",e}}const h0=new D$,S3=pe.define(),T3=pe.define(),g0=Nt.define({create:()=>oe.empty,update(i,e){i=i.map(e.changes);for(const t of e.effects){if(t.is(T3)&&(i=oe.of(t.value.map(a=>h0.range(a)),!0)),!t.is(S3))continue;let n=!1;i.between(t.value,t.value,()=>{n=!0}),i=n?i.update({filter:a=>a!==t.value}):i.update({add:[h0.range(t.value)]})}return i}});function B$(i){const t={mousedown:(n,a)=>{const s=i.breakpointLine(n.state.doc.lineAt(a.from).number-1);return s===void 0||s>=n.state.doc.lines?!1:(n.dispatch({effects:S3.of(n.state.doc.line(s+1).from)}),queueMicrotask(()=>i.onBreakpoints()),!0)}};return[g0,vV({class:"cm-breakpoint-gutter",markers:n=>n.state.field(g0),initialSpacer:()=>h0,domEventHandlers:t}),TV({domEventHandlers:t})]}function Lu(i,e){return i.field(K1).marks.placeholders.find(t=>e>=t.from&&e<=t.to)}const P$=$.inputHandler.of((i,e,t,n)=>{if(e!==t)return!1;const a=Lu(i.state,e);return a?(i.dispatch({changes:{from:a.from,to:a.to,insert:n},selection:{anchor:a.from+n.length},userEvent:"input.type"}),!0):!1}),O$=ce.transactionFilter.of(i=>{if(!i.docChanged||!(i.isUserEvent("input.paste")||i.isUserEvent("input.drop")))return i;const e=[];if(i.changes.iterChanges((a,s,r,o,l)=>e.push({from:a,to:s,text:l.toString()})),e.length!==1||e[0].from!==e[0].to)return i;const t=Lu(i.startState,e[0].from);if(!t)return i;const{text:n}=e[0];return{changes:{from:t.from,to:t.to,insert:n},selection:{anchor:t.from+n.length},userEvent:"input.paste",scrollIntoView:!0}}),$d=i=>e=>{const t=e.state.selection.main;if(!t.empty)return!1;const n=Lu(e.state,t.head);return!n||(i?t.head>=n.to:t.head<=n.from)?!1:(e.dispatch({changes:{from:n.from,to:n.to},selection:{anchor:n.from},userEvent:"delete"}),!0)},M$=$.updateListener.of(i=>{if(!i.selectionSet||i.docChanged||!i.transactions.some(n=>n.isUserEvent("select.pointer")))return;const e=i.state.selection.main;if(!e.empty)return;const t=i.state.field(K1).marks.placeholders.find(n=>e.head>n.from&&e.headi.view.dispatch({selection:O.range(t.from,t.to)}))}),Z$=Er.define([{tag:[E.keyword,E.controlKeyword,E.definitionKeyword,E.modifier,E.operatorKeyword,E.standard(E.typeName),E.bool,E.null],color:"var(--syn-keyword)",fontWeight:"600"},{tag:[E.string,E.character],color:"var(--syn-string)"},{tag:E.number,color:"var(--syn-number)"},{tag:[E.lineComment,E.blockComment],color:"var(--syn-comment)",fontStyle:"italic"}]);function L$(i){const e=i,t=`\${// ${e.code}}`,n=(a,s,r,o=0)=>Du(s,{label:a,detail:r,type:"keyword",boost:o});return[n("if",`if (\${${e.condition}}) { - ${t} -}`,e.branch,2),n("if … else",`if (\${${e.condition}}) { - ${t} -} else { - \${// ${e.code}} -}`,e.branchElse,1),n("for",`for (int \${i} = \${0}; \${i} < \${10}; \${i}++) { - ${t} -}`,e.forLoop,2),n("while",`while (\${${e.condition}}) { - ${t} -}`,e.whileLoop,2),n("do … while",`do { - ${t} -} while (\${${e.condition}});`,e.doLoop,1),n("switch",`switch (\${${e.selector}}) { - case \${1} -> { - ${t} - } - default -> { - \${// ${e.code}} - } -}`,e.switchCase,1),n("System.out.println","System.out.println(${});",e.output,3),...["syso","sysout","sout"].map(a=>n(a,"System.out.println(${});",`System.out.println · ${e.output}`,2)),...["main","psvm"].map(a=>n(a,`public static void main(String[] args) { - ${t} -}`,"public static void main(String[] args)",2)),E$(e.input),n("scanner.nextInt","int ${zahl} = scanner.nextInt();",e.input,1),n("scanner.next","String ${text} = scanner.next();",e.input),n("scanner.nextDouble","double ${wert} = scanner.nextDouble();",e.input),n("try … catch",`try { - ${t} -} catch (\${Exception} e) { - \${// ${e.code}} -}`,"try … catch",1),n("public class",`public class \${Main} { - ${t} -}`,"public class",-2),...["import java.util.Scanner;","import javax.swing.*;","import java.awt.*;","import java.awt.event.*;"].map((a,s)=>n(a,a,"import",1-s)),...["int","double","String","boolean","char","long","final","break;","continue;","return","true","false","else","null","new","public","private","static","void","class","extends","implements","interface","this"].map(a=>({label:a,type:"keyword",boost:-1})),...["Math","JFrame","JButton","JLabel","JTextField","ActionListener","ActionEvent","Color","FlowLayout","GridLayout","BorderLayout"].map(a=>({label:a,type:"type",boost:-2})),...e.robot?[{label:"mbot",detail:"mBot2",type:"variable",boost:3}]:[],...["Math.random()","Math.abs(","Math.sqrt(","Math.max(","Math.min(",".length()",".equals("].map(a=>({label:a,type:"function",boost:-2}))]}function W$(i){if(/^\s*import\s+java\.util\.(?:Scanner|\*)\s*;/m.test(i))return;const e=/^\s*package\s+[\w.]+\s*;[^\n]*\n?/.exec(i),t=e?e[0].length:0,n=i.slice(t).replace(/^\s*\n/,"");return{from:t,insert:`import java.util.Scanner; -${/^\s*import\b/.test(n)?"":` -`}`}}function E$(i){const e=Du("Scanner ${scanner} = new Scanner(System.in);",{label:"Scanner scanner = new Scanner(System.in);",detail:i,type:"keyword",boost:3}),t=e.apply;return{...e,apply(n,a,s,r){const o=W$(n.state.doc.toString());if(!o)return t(n,a,s,r);const l=n.state.changes(o);n.dispatch({changes:l,userEvent:"input.complete"}),t(n,a,l.mapPos(s),l.mapPos(r,1))}}}function N$(i){const t={label:i.kind==="field"?i.name:`${i.name}(${i.params?"…":""})`,detail:i.returns==="void"||!i.returns?i.description:i.description?`${i.returns} · ${i.description}`:i.returns,type:i.kind==="field"?"property":"method"};return i.kind==="field"?{...t,apply:i.name}:i.params?Du(`${i.name}(\${})`,t):{...t,apply:`${i.name}()`}}function F$(){const i=In();return i==="de"?"de":i==="pt-PT"?"pt":"en"}function R$(i){const e=new Set;for(const t of i.matchAll(/\b(?:int|long|double|float|boolean|char|String|Scanner)(?:\s*\[\s*\])*\s+([A-Za-z_]\w*)/g))e.add(t[1]);return[...e].map(t=>({label:t,type:"variable",boost:1}))}class J${constructor(e,t,n){y(this,"view");y(this,"readOnly",new ma);const a=r=>{var h,g;const o=r.matchBefore(/[\w.]+/);if(!o||o.from===o.to&&!r.explicit)return null;const l=r.state.doc.lineAt(r.pos),u=l.text.slice(0,r.pos-l.from);if(/\/\//.test(u)||(((h=u.match(/"/g))==null?void 0:h.length)??0)%2===1)return null;const m=w$(r.state.doc.toString(),r.pos,F$(),(g=t.ownTypes)==null?void 0:g.call(t,r.pos));if(m)return{from:m.from,options:m.members.map((f,b)=>({...N$(f),boost:50-b})),validFor:/^\w*$/};const c=/\bvoid\s+[\w.]*$/.test(u),d=L$(t.words()).filter(f=>!(c&&(f.label==="main"||f.label==="psvm")));return{from:o.from,options:[...d,...R$(r.state.doc.toString())],validFor:/^\w*$/}},s=[B$(t),jV(),ZQ(),mq(Z$),pa.of(" "),ce.tabSize.of(4),rq(),vq(),uU(),K1,T$,C$,P$,O$,M$,this.readOnly.of(ce.readOnly.of(!1)),wU({override:[a],icons:!1}),f1.highest(Xi.of([{key:"Mod-z",run:()=>(t.onUndo(!1),!0),preventDefault:!0},{key:"Mod-Shift-z",run:()=>(t.onUndo(!0),!0),preventDefault:!0},{key:"Mod-y",run:()=>(t.onUndo(!0),!0),preventDefault:!0},{key:"Backspace",run:$d(!1)},{key:"Delete",run:$d(!0)},{key:"F2",run:r=>{var o;return((o=t.onRename)==null?void 0:o.call(t,r.state.selection.main.head))??!1},preventDefault:!0}])),Xi.of([...hU,...F2,zQ,...yQ]),$.domEventHandlers({contextmenu:(r,o)=>{var u;const l=o.posAtCoords({x:r.clientX,y:r.clientY});return l===null||!((u=t.onRename)!=null&&u.call(t,l,{x:r.clientX,y:r.clientY}))?!1:(r.preventDefault(),!0)}}),$.contentAttributes.of({"aria-label":n,autocapitalize:"off",autocorrect:"off",spellcheck:"false"}),$.updateListener.of(r=>{r.docChanged&&!r.transactions.some(o=>o.annotation(Vd))?queueMicrotask(()=>t.onChange()):r.selectionSet&&!r.docChanged&&queueMicrotask(()=>t.onCaret())})];this.view=new $({parent:e,state:ce.create({doc:"",extensions:s})})}get text(){return this.view.state.doc.toString()}get caret(){return this.view.state.selection.main.head}get hasFocus(){return this.view.hasFocus}get breakpoints(){const e=new Set,t=this.view.state.doc;return this.view.state.field(g0).between(0,t.length,n=>{e.add(t.lineAt(n).number-1)}),e}setBreakpoints(e){const t=this.view.state.doc,n=[...new Set(e)].filter(a=>a>=0&&at.line(a+1).from);this.view.dispatch({effects:T3.of(n)})}lineAt(e){return this.view.state.doc.lineAt(Math.min(e,this.view.state.doc.length)).number-1}setText(e){const t=Math.min(this.caret,e.length),n=this.text;let a=0;for(;a({from:n.from,to:n.to,insert:t})),userEvent:"input.rename"})}indent(){const e=this.view.state.doc,t=this.view.state.selection.main,n=t.empty?1:e.lineAt(t.from).number,a=t.empty?e.lines:e.lineAt(t.to).number,s=Le.of(YQ(e.toString(),n,a),e.length),r=s.apply(e),o=r.lineAt(s.mapPos(e.line(n).from,-1)).number,l=r.lineAt(s.mapPos(e.line(a).to,1)).number,u=[];f3(r.toString(),o,l).split(` -`).forEach((c,d)=>{const h=r.line(d+1),g=/^\s*/.exec(h.text)[0],f=/^\s*/.exec(c)[0];g!==f&&u.push({from:h.from,to:h.from+g.length,insert:f})});const m=s.compose(Le.of(u,r.length));m.empty||this.view.dispatch({changes:m,userEvent:"input.indent"}),this.view.focus()}setMarks(e,t){const n=[Zu.of(e)];e.current!==void 0&&e.current {}); - } catch (Exception e) { - } - int n = 0; - for (java.awt.Window w : java.awt.Window.getWindows()) if (w.isShowing()) n++; - return n; - } - - public static void run(String dir, String name) { - closeAll(); - try { - java.net.URLClassLoader loader = new java.net.URLClassLoader(new java.net.URL[] { new java.io.File(dir).toURI().toURL() }); - loader.loadClass(name).getMethod("main", String[].class).invoke(null, (Object) new String[0]); - } catch (java.lang.reflect.InvocationTargetException e) { - e.getCause().printStackTrace(); - } catch (NoSuchMethodException e) { - System.err.println("Die Klasse " + name + " hat keine Methode public static void main(String[] args)."); - } catch (Exception e) { - e.printStackTrace(); - } - } -} -`;let mn,pr=0;function V$(i){return new Promise((e,t)=>{const n=document.createElement("script");n.src=i,n.onload=()=>e(),n.onerror=()=>t(new Error(i)),document.head.append(n)})}function q$(i){return mn??(mn=(async()=>{await V$(G$),await cheerpjInit({status:"none"}),cheerpjCreateDisplay(-1,-1,i);const e=await fetch(Yd);if(!e.ok)throw new Error(Yd);if(cheerpOSAddStringFile(gr,new Uint8Array(await e.arrayBuffer())),cheerpOSAddStringFile("/str/VisuStructRunner.java",H$),await cheerpjRunMain("com.sun.tools.javac.Main",gr,"/str/VisuStructRunner.java","-d","/files/")!==0)throw new Error("javac");return await(await cheerpjRunLibrary("/files/")).VisuStructRunner})()),mn.catch(()=>mn=void 0),mn}function U$(i){const e=i.match(/public\s+(?:final\s+)?class\s+(\w+)/)??i.match(/\bclass\s+(\w+)/);return e?e[1]:"Main"}function Q$(i){return/\bFiles\s*\.\s*\w+\s*\(|\bnew\s+(?:File|FileReader|FileWriter|PrintWriter|BufferedReader)\s*\(/.test(i)}function $$(i,e){return i.replace(/"([\w.-]+\.(?:txt|csv|json))"/g,`"${e}$1"`)}function _3(i){i=i.replace(/\bFiles\s*\.\s*readString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*\)/g,'new String(java.nio.file.Files.readAllBytes(java.nio.file.Paths.get($1)), "UTF-8")');const e=/\bFiles\s*\.\s*writeString\s*\(\s*(?:Path\s*\.\s*of|Paths\s*\.\s*get)\s*\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*,/g;let t="",n=0;for(let a=e.exec(i);a;a=e.exec(i)){let s=1,r=a.index+a[0].length,o=!1;for(;r0;r++){const u=i[r];o?u==="\\"?r++:u==='"'&&(o=!1):u==='"'?o=!0:u==="("?s++:u===")"&&s--}if(s>0)break;const l=i.slice(a.index+a[0].length,r-1).trim();t+=i.slice(n,a.index)+`java.nio.file.Files.write(java.nio.file.Paths.get(${a[1]}), (${l}).getBytes("UTF-8"))`,n=r,e.lastIndex=r}return t+i.slice(n)}function Y$(i){if(/\bclass\s+\w+/.test(i))return i;const e=[];i=i.replace(/^[ \t]*import\s+[\w.]+(?:\.\*)?\s*;[ \t]*$/gm,a=>(e.push(a.trim()),""));const t=[...e,"import java.util.*;","import java.awt.*;","import java.awt.event.*;","import javax.swing.*;"].join(` -`)+` - -`;if(/\bstatic\s+void\s+main\s*\(/.test(i))return`${t}public class Main { -${i} -} -`;const n=Q$(i)?" throws Exception":"";return`${t}public class Main { - public static void main(String[] args)${n} { -${i} - } -} -`}async function K$(){mn&&await(await mn).closeAll()}async function A3(i,e,t){const n=new Map;for(const a of(await i.list(e)).split(` -`).filter(Boolean)){const s=await i.read(e+a);t.get(a)!==s&&n.set(a,s)}return n}function X$(i,e){const t=pr,n=`/files/run${t}/data/`,a=new Map([...vg(),...i]);let s=!1;const r=async()=>{if(!(s||pr!==t||!mn)){try{const l=await mn,u=await A3(l,n,a);for(const[m,c]of u)a.set(m,c);if(u.size&&e(u),await l.openWindows()===0)return}catch{return}o=window.setTimeout(r,1500)}};let o=window.setTimeout(r,1500);return()=>{s=!0,window.clearTimeout(o)}}async function eY(i,e){const t=await mn,n=`/files/run${++pr}/`;await t.mkdirs(n);const a=`${n}data/`,s=new Map(vg());await t.mkdirs(a);for(const[u,m]of s)cheerpOSAddStringFile(`/str/${u}`,m),await t.copy(`/str/${u}`,a+u);const r=Y$(_3($$(i,a))),o=U$(r);if(cheerpOSAddStringFile(`/str/${o}.java`,r),await cheerpjRunMain("com.sun.tools.javac.Main",gr,`/str/${o}.java`,"-d",n,"-encoding","UTF-8")!==0)return{result:"compile-error",files:new Map};e(),await t.run(n,o);const l=await A3(t,a,s);return await t.openWindows()>0?{result:"windows",files:l}:(await new Promise(u=>setTimeout(u,500)),{result:await t.openWindows()>0?"windows":"finished",files:l})}async function KY(i,e,t){const n=await mn,a=`/files/run${++pr}/`,s=`${a}src/`,r=[];for(const l of i){const u=`${s}${l.pkg?l.pkg.replace(/\./g,"/")+"/":""}${l.name}.java`;await n.write(u,_3(l.text)),r.push(u)}const o=`${a}classes/`;return await n.mkdirs(o),await cheerpjRunMain("com.sun.tools.javac.Main",gr,...r,"-d",o,"-encoding","UTF-8")!==0?{result:"compile-error",files:new Map}:(t(),await n.run(o,e),await n.openWindows()>0?{result:"windows",files:new Map}:(await new Promise(l=>setTimeout(l,500)),{result:await n.openWindows()>0?"windows":"finished",files:new Map}))}const fr=new Set("abstract assert boolean break byte case catch char class const continue default do double else enum extends final finally float for goto if implements import instanceof int interface long native new package private protected public return short static strictfp super switch synchronized this throw throws transient try void volatile while true false null var record yield".split(" ")),Kd=/[\p{L}\p{N}_$]/u,tY=/[\p{L}_$]/u;function ni(i){const e=[];let t=0;for(;tt.name===e&&!Wu(i,t.from))}function Wu(i,e){let t=e-1;for(;t>=0&&(i[t]===" "||i[t]===" ");)t--;return i[t]==="."}function nY(i,e){const t=ni(i).find(n=>e>=n.from&&e<=n.to);if(!(!t||fr.has(t.name)||Wu(i,t.from)))return t}function iY(i,e,t){const n=ra(i,e);let a="",s=0;for(const r of n)a+=i.slice(s,r.from)+t,s=r.to;return{text:a+i.slice(s),count:n.length}}function Eu(i){const e=[i.caption],t=n=>{for(const a of n){e.push(a.textLines.join(` -`)),a.loopBody&&t(a.loopBody);for(const s of a.cases??[])e.push(s.name),t(s.elements)}};return t(i.elements),e}function aY(i,e,t){let n=0;const a=o=>{const l=iY(o,e,t);return n+=l.count,l.text},s=o=>o.map(l=>({...l,textLines:a(l.textLines.join(` -`)).split(` -`),...l.loopBody?{loopBody:s(l.loopBody)}:{},...l.cases?{cases:l.cases.map(u=>({name:a(u.name),elements:s(u.elements)}))}:{}}));return{doc:{...i,caption:a(i.caption),elements:s(i.elements)},count:n}}function Nu(i){const e=new Set,t=new Set,n=new Set;for(const a of i){const s=ni(a).filter(r=>!Wu(a,r.from));s.forEach((r,o)=>{if(fr.has(r.name)||r.name==="ø")return;const l=a.slice(r.to),u=s[o+1],m=u?a.slice(r.to,u.from):"",c=u!==void 0&&!fr.has(u.name)&&/^(?:\s*\[\s*\])*[ \t]+$/.test(m);c&&/^\s*(?:[=;,):]|$)/.test(a.slice(u.to))&&n.add(u.name);const d=new RegExp("^\\p{Lu}","u").test(r.name)&&/^\s*\./.test(l);/^\s*\(/.test(l)||d||c?t.add(r.name):e.add(r.name)})}return[...e].filter(a=>!t.has(a)||n.has(a))}function sY(i){return Eu(i).flatMap(e=>ni(e).map(t=>t.name))}function rY(i,e){return Eu(i).reduce((t,n)=>t+ra(n,e).length,0)}function oY(i){return Nu(Eu(i))}function Mo(i){return Nu([i.textLines.join(` -`)])}function Xd(i,e,t){if(e==="")return{ok:!1,reason:"empty"};if(e===i)return{ok:!1,reason:"same"};if(!/^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(e))return{ok:!1,reason:"invalid"};if(fr.has(e))return{ok:!1,reason:"keyword"};for(const n of t)if(n===e)return{ok:!0,warning:"exists"};return{ok:!0}}const dn=i=>i.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]);function j3(i){return new Promise(e=>{const t=document.createElement("div");t.className="confirm";const n=i.names.length===1;t.innerHTML=``;const a=t.querySelector("form"),s=t.querySelector("label input, label select"),r=t.querySelector(".rename-new"),o=t.querySelector(".rename-info"),l=t.querySelector("button[type=submit]"),u=()=>{const c=s.value,d=r.value.trim(),h=Xd(c,d,i.existing),g=p("web_rename_count_fmt",i.count(c));o.classList.toggle("rename-warning",h.ok?h.warning==="exists":d!==""&&d!==c),h.ok?o.textContent=h.warning==="exists"?`${g} ${p("web_rename_exists_fmt",d)}`:g:o.textContent=h.reason==="invalid"?p("web_rename_invalid"):h.reason==="keyword"?p("web_rename_keyword_fmt",d):g,l.disabled=!h.ok},m=c=>{t.remove(),e(c)};r.value=s.value,s.addEventListener("change",()=>{r.value=s.value,u()}),r.addEventListener("input",u),a.addEventListener("submit",c=>{c.preventDefault();const d=r.value.trim();Xd(s.value,d,i.existing).ok&&m({from:s.value,to:d})}),t.addEventListener("click",c=>{(c.target===t||c.target.dataset.r==="0")&&m()}),t.addEventListener("keydown",c=>{c.key==="Escape"&&(c.preventDefault(),c.stopPropagation(),m())}),u(),document.body.append(t),r.focus(),r.select()})}function C3(i,e,t,n){var u;(u=document.querySelector(".rename-menu"))==null||u.remove();const a=document.createElement("div");a.className="rename-menu",a.setAttribute("role","menu");const s=document.createElement("button");s.setAttribute("role","menuitem"),s.textContent=t,a.append(s),a.style.left=`${Math.min(i,window.innerWidth-220)}px`,a.style.top=`${Math.min(e,window.innerHeight-50)}px`;const r=()=>{a.remove(),document.removeEventListener("pointerdown",o,!0),document.removeEventListener("keydown",l,!0)},o=m=>{a.contains(m.target)||r()},l=m=>{m.key==="Escape"&&r()};s.addEventListener("click",()=>{r(),n()}),document.addEventListener("pointerdown",o,!0),document.addEventListener("keydown",l,!0),document.body.append(a),s.focus()}const D3="visustruct.live.target",eh="visustruct.live.javaClass",th="visustruct.live.javaMain",nh="visustruct.live.swing",ih="visustruct.console.height",ah=150;function Zo(i){try{return localStorage.getItem(i)==="1"}catch{return!1}}function lY(i){try{return localStorage.getItem(i)}catch{return null}}function uY(i,e){try{localStorage.setItem(i,e)}catch{}}function mY(){try{const i=localStorage.getItem(D3);return i==="javascript"||i==="python"?i:"java"}catch{return"java"}}const Se=i=>i.replace(/&/g,"&").replace(//g,">");function cY(i){let e="",t=i;for(;;){const n=t.indexOf(ua);if(n<0)break;const a=t.indexOf(E0,n);if(a<0)break;e+=Se(t.slice(0,n))+`${Se(t.slice(n+1,a))}`,t=t.slice(a+1)}return e+Se(t)}const hi=(i,e)=>i.length===e.length&&i.every((t,n)=>t===e[n]);function as(i,e){let t;for(const n of i)en.to||(!t||n.to-n.fromt.path.length)&&(t=n);return t}const dY=900,hY=2e3;class gY{constructor(e,t,n,a){y(this,"element");y(this,"view");y(this,"editor");y(this,"code");y(this,"problemLine");y(this,"hint");y(this,"spans",[]);y(this,"highlighted");y(this,"html","");y(this,"target",mY());y(this,"last");y(this,"textFor",new WeakMap);y(this,"form",{asClass:Zo(eh),withMain:Zo(th)});y(this,"shown",new WeakMap);y(this,"parsed");y(this,"placeholders",[]);y(this,"problems",[]);y(this,"problemTimer");y(this,"showProblems",!0);y(this,"runErrorLine");y(this,"run");y(this,"debugLine");y(this,"breakpoints",new Set);y(this,"swing",Zo(nh));y(this,"swingElement");y(this,"swingDisplay");y(this,"stopWatchingFiles");y(this,"runButton");y(this,"stepButton");y(this,"stopButton");y(this,"indentButton");y(this,"debugPanel");y(this,"debugVars");y(this,"consoleElement");y(this,"consoleResizer");y(this,"consoleLines");y(this,"consoleForm");y(this,"consoleNotice");y(this,"onRobotRun");y(this,"onReference");y(this,"onSwap");y(this,"onRename");this.onHide=e,this.onSelect=t,this.onCode=n,this.onUndo=a,this.element=document.createElement("aside"),this.element.className="livecode",this.consoleElement=document.createElement("section"),this.consoleElement.className="console",this.consoleElement.hidden=!0,this.consoleResizer=this.makeConsoleResizer(),this.swingElement=document.createElement("section"),this.swingElement.className="console livecode-swing",this.swingElement.hidden=!0,this.swingDisplay=document.createElement("div"),this.swingDisplay.className="livecode-swing-display",this.build()}get labels(){return{condition:p("canvas_stub_condition"),selector:p("web_live_ph_selector"),code:p("web_live_ph_code"),start:p("inspector_for_init"),step:p("inspector_for_step"),end:p("web_live_ph_end")}}build(){this.run&&window.clearTimeout(this.run.timer),this.element.innerHTML=` -
    -

    ${Se(p("web_live_title"))}

    - - - - - - - - -
    -

    -
    
    -      
    -

    - - `,this.consoleElement.innerHTML=` -
    -

    ${Se(p("web_console"))}

    - -
    -
    - -

    - `,this.view=this.element.querySelector(".livecode-view"),this.editor=this.element.querySelector(".livecode-editor"),this.problemLine=this.element.querySelector(".livecode-problem"),this.hint=this.element.querySelector(".hint"),this.element.querySelector(".learn-hide").addEventListener("click",()=>this.onHide()),this.element.querySelector(".livecode-reference").addEventListener("click",()=>{var t;return(t=this.onReference)==null?void 0:t.call(this)}),this.element.querySelector(".livecode-swap").addEventListener("click",()=>{var t;return(t=this.onSwap)==null?void 0:t.call(this)}),this.runButton=this.element.querySelector("[data-run=play]"),this.stepButton=this.element.querySelector("[data-run=step]"),this.stopButton=this.element.querySelector("[data-run=stop]"),this.indentButton=this.element.querySelector(".livecode-indent"),this.indentButton.addEventListener("click",()=>this.code.indent()),this.debugPanel=this.element.querySelector(".livecode-debug"),this.debugVars=this.element.querySelector(".livecode-vars"),this.swingElement.innerHTML=` -
    -

    ${Se(p("web_live_swing_windows"))}

    - -
    - `,this.swingElement.append(this.swingDisplay),this.swingElement.querySelector(".console-hide").addEventListener("click",()=>{this.swingElement.hidden=!0,K$()});const e=this.consoleElement;this.consoleResizer.title=p("web_console_resize"),this.element.append(this.swingElement,this.consoleResizer,e),this.consoleLines=e.querySelector(".livecode-console-lines"),this.consoleForm=e.querySelector(".livecode-console-input"),this.consoleNotice=e.querySelector(".livecode-console-notice"),e.querySelector(".console-hide").addEventListener("click",()=>this.hideConsole()),this.runButton.addEventListener("click",()=>{var t;return(t=this.run)!=null&&t.paused?this.resume():this.startRun(!1)}),this.stepButton.addEventListener("click",()=>{var t;return(t=this.run)!=null&&t.paused?this.stepOnce():this.startRun(!0)}),this.stopButton.addEventListener("click",()=>this.stopRun(!0)),this.consoleForm.addEventListener("submit",t=>{t.preventDefault(),this.submitInput()}),this.run=void 0,this.view.addEventListener("click",t=>{const n=t.target.closest(".ln");if(!n||!this.view.classList.contains("selectable"))return;const a=as(this.spans,Number(n.dataset.line));a&&this.onSelect(a.path)}),this.code=new J$(this.editor,{onChange:()=>this.codeChanged(),onCaret:()=>this.caretMoved(),onUndo:t=>this.onUndo(t),words:()=>this.snippetWords,breakpointLine:t=>{var n;return(n=as(this.spans,t))==null?void 0:n.from},onBreakpoints:()=>this.breakpoints=this.code.breakpoints,onRename:(t,n)=>this.renameAt(t,n)},p("web_live_edit_label")),this.editor.querySelector(".cm-gutters").title=p("web_debug_gutter_title"),this.breakpoints=new Set,this.debugLine=void 0,this.highlighted=void 0,this.html="",this.parsed=void 0,this.showMode()}javaFor(e,t){const n=this.textFor.get(e);if(n!==void 0)return n;const a=this.shown.get(t);return a?Bm(a.text,a.doc,e,this.labels,{},this.form):_i(e,this.labels,this.form)}adoptJava(e,t){this.textFor.set(e,t),this.parsed=void 0,this.last&&this.refresh(this.last.doc,this.last.highlight,this.last.context)}documentFromJavaText(e){return Gi({caption:"",elements:[]},Ji(e,this.labels))}get javaForm(){return this.form}setJavaForm(e){this.form=e;try{localStorage.setItem(eh,e.asClass?"1":"0"),localStorage.setItem(th,e.withMain?"1":"0")}catch{}this.textFor=new WeakMap,this.shown=new WeakMap,this.parsed=void 0,this.last&&this.refresh(this.last.doc,this.last.highlight,this.last.context)}get swingSupport(){return this.swing}setSwingSupport(e){this.swing=e;try{localStorage.setItem(nh,e?"1":"0")}catch{}e||(this.swingElement.hidden=!0)}get language(){return this.target}setTarget(e){this.target=e;try{localStorage.setItem(D3,e)}catch{}this.showMode(),this.last&&this.refresh(this.last.doc,this.last.highlight,this.last.context)}showMode(){const e=this.target==="java";this.view.hidden=e,this.editor.hidden=!e,this.problemLine.hidden=!e,this.indentButton.hidden=!e,this.hint.textContent=p(e?"web_live_hint_edit":"web_live_hint"),this.element.querySelector(".livecode-lang").textContent={java:"Java",javascript:"JavaScript",python:"Python"}[this.target]}refresh(e,t,n){this.last={doc:e,highlight:t,context:n},this.target==="java"?this.refreshEditor(e,t,n):this.refreshView(e,t,n.editable)}refreshView(e,t,n){var l;const a=Ng(e,this.target,this.labels);this.spans=a.spans;const s=t&&a.spans.find(u=>hi(u.path,t));this.view.classList.toggle("selectable",n);const r=a.text.split(` -`).map((u,m)=>`
    ${cY(u)||" "}
    `).join("");r!==this.html&&(this.view.innerHTML=r),this.html=r;const o=s?`${s.from}-${s.to}`:void 0;o&&o!==this.highlighted&&((l=this.view.querySelector(`.ln[data-line="${s.from}"]`))==null||l.scrollIntoView({block:"nearest"})),this.highlighted=o}startRun(e){var a,s;if((a=this.onRobotRun)!=null&&a.call(this,e))return;this.runErrorLine=void 0;const t=["web_parse_missing_import","web_parse_missing_scanner","web_parse_unknown_type","web_parse_wrong_case"],n=this.target==="java"?this.javaProblems.find(r=>t.includes(r.key)):void 0;if(n){this.stopRun(!1),this.setConsoleVisible(!0),this.consoleLines.replaceChildren(),this.consoleNotice.textContent=p("web_parse_line_fmt",this.code.lineAt(n.from)+1,p(n.key,n.arg??"")),this.consoleNotice.classList.add("error"),this.showProblems=!0,this.drawMarks((s=this.last)==null?void 0:s.highlight);return}if(this.swing&&this.target==="java"&&!e&&this.code.breakpoints.size===0)return void this.startJavaRun();this.last&&(this.stopRun(!1),this.run={engine:new Wi(this.last.doc,this.last.context.resolve),doc:this.last.doc,printed:0,paused:!1,stepping:e,debugged:!1},this.breakpoints=this.code.breakpoints,this.debugPanel.hidden=!0,this.setConsoleVisible(!0),this.consoleLines.replaceChildren(),this.consoleNotice.textContent="",this.consoleNotice.classList.remove("error"),this.setRunning(!0),e?this.pause():this.continueRun(!1))}async startJavaRun(){var e;this.stopRun(!1),(e=this.stopWatchingFiles)==null||e.call(this),this.setConsoleVisible(!0),this.consoleLines.replaceChildren(),this.consoleLines.id="console",this.consoleForm.hidden=!0,this.consoleNotice.classList.remove("error"),this.runButton.disabled=this.stepButton.disabled=!0;try{this.consoleNotice.textContent=p("web_live_swing_loading"),await q$(this.swingDisplay),this.consoleNotice.textContent=p("web_live_swing_compiling");const{result:t,files:n}=await eY(this.code.text,()=>this.swingElement.hidden=!1);this.showWrittenFiles(n),this.consoleNotice.textContent=p({"compile-error":"web_live_swing_compile_failed",windows:"web_live_swing_running",finished:"web_live_run_finished"}[t]),this.consoleNotice.classList.toggle("error",t==="compile-error"),this.swingElement.hidden=t!=="windows",t==="windows"&&(this.stopWatchingFiles=X$(n,a=>this.showWrittenFiles(a)))}catch{this.consoleNotice.textContent=p("web_live_swing_failed"),this.consoleNotice.classList.add("error")}this.runButton.disabled=this.stepButton.disabled=!1}continueRun(e){const t=this.run;if(!t)return;const{engine:n}=t;for(let a=0;athis.continueRun(!1),0))}afterSteps(){const e=this.run,t=e.engine.state;if(this.printNewLines(),t.inputRequest)return this.askInput(),!0;if(t.isHalted||!e.engine.canStep){t.isHalted&&(this.runErrorLine=this.lineOf(e.engine.currentStepPath)),this.showWrittenFiles(t.files);const n=t.isHalted&&t.notice?m1(t.notice):void 0,a=this.runErrorLine;return this.finishRun(n?a!==void 0?p("web_parse_line_fmt",a+1,n):n:p("web_live_run_finished"),t.isHalted),!0}return!1}atBreakpoint(e){if(!e||e.callPath||e.kind==="forLoopIncrement"||e.kind==="forLoopStart"||e.kind==="callEnd")return!1;const t=this.lineOf(e.path);return t!==void 0&&this.breakpoints.has(t)}lineOf(e){var t;return e&&((t=this.spans.find(n=>hi(n.path,e)))==null?void 0:t.from)}pause(){var a,s;const e=this.run;e.paused=!0,e.debugged=!0;const t=e.engine.currentStepPath;this.debugLine=this.lineOf(t),this.consoleNotice.classList.remove("error"),this.consoleNotice.textContent=this.debugLine===void 0?"":p("web_debug_paused_fmt",this.debugLine+1),this.showVariables(),this.setRunning(!0);const n=(a=this.last)==null?void 0:a.highlight;t&&!(n&&hi(n,t))?this.onSelect(t):this.drawMarks((s=this.last)==null?void 0:s.highlight)}resume(){const e=this.run;e!=null&&e.paused&&(e.paused=!1,e.stepping=!1,this.leavePause(),this.continueRun(!0))}stepOnce(){var a,s;const e=this.run;if(!(e!=null&&e.paused))return;e.paused=!1,e.stepping=!0,this.leavePause();const{engine:t}=e,n=(a=t.steps[t.state.stepIndex])==null?void 0:a.kind;t.step(),(n==="forLoopStart"||n==="forLoopIncrement")&&t.canStep&&((s=t.steps[t.state.stepIndex])==null?void 0:s.kind)==="forLoopCondition"&&t.step(),this.afterSteps()||this.pause()}leavePause(){var e;this.debugLine=void 0,this.consoleNotice.textContent="",this.setRunning(!0),this.drawMarks((e=this.last)==null?void 0:e.highlight)}showVariables(){const e=this.run,t=e.engine.state.variables;this.debugPanel.hidden=!1;const n=[...t.keys()].sort((a,s)=>a.localeCompare(s));if(n.length===0)this.debugVars.innerHTML=`

    ${Se(p("simulation_variables_placeholder"))}

    `;else{const a=document.createElement("table");for(const s of n){const r=t.get(s),o=_e(r),l=a.insertRow();l.insertCell().textContent=s;const u=l.insertCell();r.kind==="grid"?u.append(Fp(r)):u.textContent=o,e.values&&e.values.get(s)!==o&&l.classList.add("changed")}this.debugVars.replaceChildren(a)}e.values=new Map(n.map(a=>[a,_e(t.get(a))]))}printNewLines(e){const t=this.run;if(!t)return;const n=t.engine.state.outputLines;for(;t.printeds.focus())}submitInput(){const e=this.run;if(!(e!=null&&e.engine.state.inputRequest))return;const t=this.consoleForm.querySelector("input"),{prompt:n}=e.engine.state.inputRequest;if(e.engine.provideInput(t.value),e.engine.state.inputRequest)return this.askInput();const a=e.heldLine;if(e.heldLine=void 0,this.printNewLines({prompt:n,value:t.value,heldLine:a}),t.value="",this.consoleForm.hidden=!0,!e.stepping)return this.continueRun(!1);this.afterSteps()||this.pause()}stopRun(e){var t;this.run&&(window.clearTimeout(this.run.timer),this.run.heldLine!==void 0&&this.consoleLine(this.run.heldLine),e&&this.finishRun(p("web_live_run_stopped"),!1),this.run=void 0,this.debugLine=void 0,this.debugPanel.hidden=!0,this.setRunning(!1),this.drawMarks((t=this.last)==null?void 0:t.highlight))}finishRun(e,t){var n,a;this.run&&window.clearTimeout(this.run.timer),this.consoleForm.hidden=!0,this.consoleNotice.textContent=e,this.consoleNotice.classList.toggle("error",t),(n=this.run)!=null&&n.debugged?this.showVariables():this.debugPanel.hidden=!0,this.run=void 0,this.debugLine=void 0,this.setRunning(!1),this.drawMarks((a=this.last)==null?void 0:a.highlight)}showTranscript(e){this.stopRun(!1),this.setConsoleVisible(!0),this.consoleForm.hidden=!0,this.consoleLines.replaceChildren();for(const t of e.lines){const n=this.consoleLine(t.text);if(t.typed!==void 0){const a=document.createElement("span");a.className="typed",a.textContent=t.typed,n.append(a)}}e.pending!==void 0&&this.consoleLine(e.pending).classList.add("pending"),this.consoleLines.scrollTop=this.consoleLines.scrollHeight,this.consoleNotice.textContent=e.notice??"",this.consoleNotice.classList.toggle("error",e.error)}hideConsole(){this.stopRun(!1),this.setConsoleVisible(!1)}setConsoleVisible(e){this.consoleElement.hidden!==!e&&(this.consoleElement.hidden=!e,this.consoleResizer.hidden=!e)}makeConsoleResizer(){const e=document.createElement("div");e.className="console-resizer",e.hidden=!0,e.tabIndex=0,e.setAttribute("role","separator"),e.setAttribute("aria-orientation","horizontal");const t=this.consoleElement,n=r=>Math.round(Math.max(ah,Math.min(r,this.element.getBoundingClientRect().height-160))),a=(r,o)=>{t.style.height=r===void 0?"":`${n(r)}px`,o&&uY(ih,r===void 0?"":String(n(r)))},s=Number(lY(ih));return s>0&&(t.style.height=`${Math.max(ah,s)}px`),e.addEventListener("pointerdown",r=>{if(r.button!==0)return;r.preventDefault(),e.setPointerCapture(r.pointerId);const o=r.clientY,l=t.getBoundingClientRect().height;e.classList.add("dragging");const u=c=>a(l-(c.clientY-o),!1),m=c=>{e.removeEventListener("pointermove",u),e.removeEventListener("pointerup",m),e.removeEventListener("pointercancel",m),e.classList.remove("dragging"),a(l-(c.clientY-o),!0)};e.addEventListener("pointermove",u),e.addEventListener("pointerup",m),e.addEventListener("pointercancel",m)}),e.addEventListener("dblclick",()=>a(void 0,!0)),e.addEventListener("keydown",r=>{const o=r.key==="ArrowUp"?20:r.key==="ArrowDown"?-20:0;o&&(r.preventDefault(),a(t.getBoundingClientRect().height+o,!0))}),e}setRunning(e){var a,s;const t=!!((a=this.run)!=null&&a.paused);this.runButton.disabled=e&&!t,this.stepButton.disabled=e&&!t,this.stopButton.disabled=!e,this.indentButton.disabled=e;const n=p(t?"web_debug_continue":"web_live_run_title");this.runButton.dataset.tip=n,this.runButton.setAttribute("aria-label",t?n:p("web_live_run")),this.element.classList.toggle("running",e&&!t),this.element.classList.toggle("debugging",t),this.code.setReadOnly(e||!((s=this.last)!=null&&s.context.editable))}textForDocument(e,t){const n=this.textFor.get(e);if(n!==void 0)return n;const a=this.shown.get(t.owner);return a?pY(a.text,Bm(a.text,a.doc,e,this.labels,{subprograms:t.subprograms},this.form)):_i(e,this.labels,this.form)}refreshEditor(e,t,n){var r;const a=this.textForDocument(e,n);this.textFor.set(e,a),this.shown.set(n.owner,{doc:e,text:a}),this.run&&this.run.doc!==e&&this.stopRun(!1),this.code.setReadOnly(!n.editable||!!this.run),this.code.text!==a?(this.code.setText(a),this.analyze(a,n)):this.parsed||this.analyze(a,n);const s=cp(e,this.labels,this.form);this.spans=a===s.text?s.spans:((r=this.parsed)==null?void 0:r.spans)??[],this.drawMarks(t)}analyze(e,t){this.parsed=Ji(e,this.labels,{subprograms:t.subprograms}),this.placeholders=this.parsed.placeholders,this.problems=this.parsed.problems}get javaProblems(){var e;return(e=this.last)!=null&&e.doc.pseudocode?[]:this.problems}drawMarks(e){var r;const t=e&&this.spans.find(o=>hi(o.path,e)),n=t?`${t.from}-${t.to}`:void 0,a=((r=this.last)==null?void 0:r.context.editable)===!1&&t?t.from:void 0;this.code.setMarks({placeholders:this.placeholders,problems:this.showProblems?this.javaProblems:[],lines:t&&a===void 0?{from:t.from,to:t.to}:void 0,current:this.debugLine??a,errorLines:this.errorLines(),fixes:this.showProblems?this.quickFixes():[]},n!==void 0&&n!==this.highlighted&&!this.code.hasFocus);const s=this.showProblems?this.javaProblems[0]:void 0;this.problemLine.textContent=s?p("web_parse_line_fmt",this.code.lineAt(s.from)+1,p(s.key,s.arg??"")):"",this.highlighted=n}errorLines(){const e=this.showProblems?this.javaProblems.map(t=>this.code.lineAt(t.from)):[];return this.runErrorLine!==void 0&&e.push(this.runErrorLine),e}quickFixes(){const e=this.code.text,t=[];for(const n of this.javaProblems){const a=e.lastIndexOf(` -`,n.from-1)+1,s=e.indexOf(` -`,n.from)<0?e.length:e.indexOf(` -`,n.from);if(t.some(l=>l.at===s))continue;const r=(l,u,m=u.trim())=>t.push({at:s,title:p("web_quickfix_insert_fmt",m),from:l,to:l,insert:u}),o=n.arg??"";if(n.key==="web_parse_missing_import"){const l=[...e.matchAll(/^import\s[^\n]*\n/gm)],u=l[l.length-1];r(u?u.index+u[0].length:0,`${o} -`)}else if(n.key==="web_parse_missing_scanner"){const l=/^[ \t]*/.exec(e.slice(a))[0];r(a,`${l}${o} -`)}else(n.key==="web_parse_unknown_type"||n.key==="web_parse_wrong_case")&&o?t.push({at:s,title:p("web_quickfix_replace_fmt",o,e.slice(n.from,n.to)),from:n.from,to:n.to,insert:o}):n.key==="web_parse_missing"&&(o===";"||o===")")&&r(n.to,o)}return t}codeChanged(){var u,m,c,d;const e=(u=this.last)==null?void 0:u.context,t=(m=this.last)==null?void 0:m.doc;if(!e||!t)return;const n=this.code.text;this.analyze(n,e);const a=Gi(t,this.parsed),s=JSON.stringify([a.caption,a.elements])===JSON.stringify([t.caption,t.elements]),r=s?t:a;this.textFor.set(r,n),this.shown.set(e.owner,{doc:r,text:n}),this.showProblems=!1,this.runErrorLine=void 0,window.clearTimeout(this.problemTimer),this.problemTimer=window.setTimeout(()=>{var h;this.showProblems=!0,this.drawMarks((h=this.last)==null?void 0:h.highlight)},dY);const o=this.code.lineAt(this.code.caret),l=(c=as(this.parsed.spans,o))==null?void 0:c.path;s?(this.spans=this.parsed.spans,this.drawMarks((d=this.last)==null?void 0:d.highlight),l&&this.onSelect(l)):this.onCode(r,l)}get editableCode(){var e;return this.target==="java"&&((e=this.last)!=null&&e.context.editable)?this.code.text:void 0}renameInCode(e,t){var n;(n=this.onRename)==null||n.call(this),this.code.replaceRanges(ra(this.code.text,e),t)}renameAt(e,t){const n=this.editableCode,a=n!==void 0?nY(n,e):void 0;if(n===void 0||!a)return!1;const s=()=>void j3({names:[a.name],existing:ni(this.code.text).map(r=>r.name),count:r=>ra(this.code.text,r).length}).then(r=>{r&&this.renameInCode(r.from,r.to),this.code.view.focus()});return t?C3(t.x,t.y,p("web_rename_menu"),s):s(),!0}caretMoved(){var n,a;const e=as(this.spans,this.code.lineAt(this.code.caret)),t=(n=this.last)==null?void 0:n.highlight;e&&((a=this.last)!=null&&a.context.editable)&&!(t&&hi(t,e.path))&&this.onSelect(e.path)}get snippetWords(){const e=this.labels;return{condition:e.condition,selector:e.selector,code:e.code,start:e.start,step:e.step,branch:p("element_verzweigung"),branchElse:p("element_verzweigung"),switchCase:p("element_fallauswahl"),forLoop:p("element_forSchleife"),whileLoop:p("element_whileSchleife"),doLoop:p("element_doUntilSchleife"),output:p("inspector_io_mode_output"),input:p("inspector_io_mode_input"),robot:jn()}}}function pY(i,e){const t=i.split(` -`),n=e.split(` -`);let a=0;for(;aa?f3(e,a+1,n.length-s):e}const _n=i=>i.replace(/&/g,"&").replace(//g,">").replace(/"/g,"""),Lo=300;class fY{constructor(){y(this,"element");y(this,"rail");y(this,"heightBar");y(this,"onHide",()=>{});y(this,"onToggleStacked",()=>{});y(this,"stage");y(this,"status");y(this,"picker");y(this,"playButton");y(this,"stackButton");y(this,"view");y(this,"loading");y(this,"worlds",[]);y(this,"worldsKey");y(this,"setup",Ni);y(this,"result");y(this,"lastKey");y(this,"pending");y(this,"playTimer");y(this,"input");y(this,"simulation");this.element=document.createElement("aside"),this.element.className="robotpreview",this.element.innerHTML=` - -
    -

    🤖 ${_n(p("web_visubot_preview_title"))}

    - - - -
    -

    ${_n(p("web_visubot_preview_hint"))}

    - -
    -

    `;const e=t=>this.element.querySelector(t);this.heightBar=e(".robotpreview-height"),this.stage=e(".robotpreview-stage"),this.status=e(".robotpreview-status"),this.picker=e(".visubot-worlds"),this.playButton=e(".robotpreview-play"),this.stackButton=e(".robotpreview-stack"),this.playButton.addEventListener("click",()=>this.playTimer===void 0?this.play():this.stopPlaying(!0)),this.stackButton.addEventListener("click",()=>this.onToggleStacked()),e(".learn-hide").addEventListener("click",()=>this.onHide()),this.rail=document.createElement("button"),this.rail.className="learn-rail robotpreview-rail",this.setStacked(!1),this.relabel()}hostSimulation(e){var t;e!==this.simulation&&((t=this.simulation)==null||t.remove(),this.simulation=e,this.element.classList.toggle("simulation",!!e),e&&(this.pause(),this.status.after(e)),this.relabel())}relabel(){const e=a=>this.element.querySelector(a),t=!!this.simulation;e("h2").textContent=`🤖 ${p(t?"web_visubot_preview_sim_title":"web_visubot_preview_title")}`,e(".hint").textContent=p(t?"web_visubot_preview_sim_hint":"web_visubot_preview_hint"),this.heightBar.title=p("web_visubot_preview_resize");const n=e(".learn-hide");n.title=p("web_visubot_preview_hide"),n.setAttribute("aria-label",p("web_visubot_preview_hide")),this.rail.title=p("web_visubot_preview_show"),this.rail.innerHTML=`${Dt}🤖 ${_n(p("web_visubot_preview_title"))}`,this.setStacked(this.stackButton.textContent==="▥"),this.stopPlaying(!1),this.worldsKey=void 0,this.lastKey=void 0}setStacked(e){const t=e?"web_visubot_preview_side":"web_visubot_preview_stack";this.stackButton.innerHTML=e?"▥":"⬓",this.stackButton.className="icon robotpreview-stack",this.stackButton.dataset.tip=p(t),this.stackButton.setAttribute("aria-label",p(t))}show(){this.loading??(this.loading=ee(async()=>{const{Robot3D:e}=await import("./robot3d-CJE1N58D.js");return{Robot3D:e}},[],import.meta.url).then(({Robot3D:e})=>{var t;this.view=new e(this.stage),this.view.setWorld(this.setup),this.showFrame((t=this.result)==null?void 0:t.frames.at(-1))}).catch(()=>{this.stage.textContent=p("web_visubot_no_3d")}))}update(e,t,n,a,s){if(this.simulation)return;this.show(),a!==this.worldsKey&&(this.worldsKey=a,this.setWorlds(n)),this.input={doc:e,resolve:t};const r=JSON.stringify(s)+` -`+JSON.stringify(e);r!==this.lastKey&&(this.lastKey=r,window.clearTimeout(this.pending),this.pending=window.setTimeout(()=>this.recompute(),120))}setWorlds(e){this.worlds=e,this.picker.replaceChildren(),this.picker.hidden=e.length<2,e.forEach((t,n)=>{const a=document.createElement("button");a.textContent=p("web_visubot_world_fmt",n+1),a.addEventListener("click",()=>this.useWorld(t)),this.picker.append(a)}),this.useWorld(e[0]??Ni)}useWorld(e){var t;this.worlds.forEach((n,a)=>{var s;return(s=this.picker.children[a])==null?void 0:s.classList.toggle("active",n===e)}),!(e===this.setup&&this.result)&&(this.setup=e,(t=this.view)==null||t.setWorld(e),this.recompute())}recompute(){var e;if(this.pending=void 0,this.stopPlaying(!1),!!this.input){try{this.result=Ap(this.input.doc,this.input.resolve,this.setup)}catch{this.result=void 0}this.showFrame((e=this.result)==null?void 0:e.frames.at(-1)),this.updateStatus()}}showFrame(e,t=0){if(!this.view)return;const n=e??{pose:{...this.setup.start},trail:[{x:this.setup.start.x,y:this.setup.start.y}],leds:Array(5).fill(void 0),screen:void 0};t>0?this.view.moveTo(n.pose,n.trail,t):this.view.place(n.pose,n.trail),this.view.setLeds(n.leds),this.view.setScreen(n.screen)}play(){var a;const e=(a=this.result)==null?void 0:a.frames;if(!e||e.length<2||!this.view)return;this.stopPlaying(!1);let t=0;this.showFrame(e[0]),this.playButton.innerHTML=me.stop,this.playButton.dataset.tip=p("web_visubot_preview_stop"),this.playButton.setAttribute("aria-label",p("web_visubot_preview_stop"));const n=()=>{if(t++,t>=e.length){this.stopPlaying(!1);return}const s=e[t];s.tone&&sp(s.tone[0],s.tone[1]),this.showFrame(s,Lo-30),this.playTimer=window.setTimeout(n,Lo)};this.playTimer=window.setTimeout(n,Lo)}stopPlaying(e){var t;this.playTimer!==void 0&&window.clearTimeout(this.playTimer),this.playTimer=void 0,this.playButton.innerHTML=me.play,this.playButton.dataset.tip=p("web_visubot_preview_play"),this.playButton.setAttribute("aria-label",p("web_visubot_preview_play")),e&&this.showFrame((t=this.result)==null?void 0:t.frames.at(-1))}updateStatus(){const e=this.result;if(this.playButton.disabled=!e||e.frames.length<2,!e){this.status.textContent="";return}const t=[p("web_visubot_preview_distance_fmt",e.distance)];e.bumps>0&&t.push(`⚠︎ ${p("web_visubot_bumps")}: ${e.bumps}`),e.stop==="input"?t.push(p("web_visubot_preview_input")):e.stop==="limit"?t.push(p("web_visubot_preview_limit")):e.stop==="error"&&e.notice&&t.push(`⚠︎ ${m1(e.notice)}`),this.status.textContent=t.join(" · ")}pause(){window.clearTimeout(this.pending),this.pending=void 0,this.lastKey=void 0,this.stopPlaying(!1)}}const bY={left:12,top:10},xY=8,br=8e3,Zi=16,Wo=12,B3="#fafafa";function P3(i){const e=i.caption.trim();return e===""?p("diagram_title_placeholder"):e}function sh(i){let e=i.trim().replace(/\.visustruct$/i,"");if(e==="")return"";for(e=e.replace(/\s/g,"-").replace(/[/:\\*?"<>|]/g,"-");e.includes("--");)e=e.replace(/--/g,"-");return e=e.replace(/^-+|-+$/g,""),e.length>120&&(e=e.slice(0,120).replace(/^-+|-+$/g,"")),e}function rh(i,e){return sh(i.caption)||sh(e)||"VisuStruct"}function vY(i,e,t){const n=i.split(/\s+/),a=[];let s="";for(const r of n){const o=s?`${s} ${r}`:r;s&&t(o,Zi,"bold")>e?(a.push(s),s=r):s=o}if(s&&a.push(s),a.length>2){let r=a.slice(1).join(" ");for(;r.length>1&&t(r+"…",Zi,"bold")>e;)r=r.slice(0,-1);return[a[0],r+"…"]}return a}function oh(i,e,t,n=320){const a={...i,elements:i.elements.map(_s)},s=new Yh(e,t).layout(a,bY),r=Math.min(br,Math.max(n,s.canvasSize.width)),o=Math.min(br,Math.max(n,s.canvasSize.height));if(r<=2||o<=2)return;const l=vY(P3(i),r-2*Wo,t),u=Math.ceil(Zi*1.2),m=Math.min(56,l.length*u+2),c=m+o,d=new eg(e,t).render(s,{width:r,height:o},void 0,xY);d.setAttribute("y",String(m)),d.removeAttribute("class");const h=Te("svg",{xmlns:"http://www.w3.org/2000/svg",width:r,height:c,viewBox:`0 0 ${r} ${c}`});h.append(Te("rect",{x:0,y:0,width:r,height:c,fill:B3}));const g=Te("text",{x:Wo,y:0,"font-family":la,"font-size":Zi,"font-weight":600,fill:"black"});return l.forEach((f,b)=>{const x=Te("tspan",{x:Wo,y:b*u+Zi});x.textContent=f,g.append(x)}),h.append(g,d),{svg:h,width:r,height:c}}function O3(i){return` -`+new XMLSerializer().serializeToString(i.svg)}async function lh(i,e){const t=Math.max(1,Math.min(e,br/i.width,br/i.height)),n=URL.createObjectURL(new Blob([O3(i)],{type:"image/svg+xml"}));try{const a=new Image;a.src=n,await a.decode();const s=document.createElement("canvas");s.width=Math.round(i.width*t),s.height=Math.round(i.height*t);const r=s.getContext("2d");return r.fillStyle=B3,r.fillRect(0,0,s.width,s.height),r.drawImage(a,0,0,s.width,s.height),s}finally{URL.revokeObjectURL(n)}}function wY(i){return new Promise((e,t)=>i.toBlob(n=>n?e(n):t(new Error("PNG konnte nicht erzeugt werden.")),"image/png"))}function yY(i,e){const a=Math.max(1,i),s=Math.max(1,e),r=f=>{const b=f.width-64,x=f.height-64;return{s:Math.min(b/a,x/s)*.98,cw:b,ch:x}},o={width:595,height:842},l={width:842,height:595},u=r(o),m=r(l),[c,d]=m.s>u.s?[l,m]:[o,u],h=a*d.s,g=s*d.s;return{page:c,draw:{x:32+(d.cw-h)/2,y:32+(d.ch-g)/2,width:h,height:g}}}async function zY(i){const e=new Blob([i]).stream().pipeThrough(new CompressionStream("deflate"));return new Uint8Array(await new Response(e).arrayBuffer())}function uh(i){let e="FEFF";for(let t=0;t`}async function IY(i,e,t,n,a){const{page:s,draw:r}=yY(n.width,n.height),o=await zY(i),l=x=>(Math.round(x*100)/100).toString(),u=`q ${l(r.width)} 0 0 ${l(r.height)} ${l(r.x)} ${l(s.height-r.y-r.height)} cm /Im0 Do Q`,m=new TextEncoder,c=[["<< /Type /Catalog /Pages 2 0 R >>"],["<< /Type /Pages /Kids [3 0 R] /Count 1 >>"],[`<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${s.width} ${s.height}] /Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>`],[`<< /Type /XObject /Subtype /Image /Width ${e} /Height ${t} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /FlateDecode /Length ${o.length} >> -stream -`,o,` -endstream`],[`<< /Length ${u.length} >> -stream -${u} -endstream`],[`<< /Creator ${uh("VisuStruct")} /Title ${uh(a)} >>`]],d=[];let h=0;const g=x=>{const v=typeof x=="string"?m.encode(x):x;d.push(v),h+=v.length};g(`%PDF-1.4 -%âãÏÓ -`);const f=[];c.forEach((x,v)=>{f.push(h),g(`${v+1} 0 obj -`),x.forEach(g),g(` -endobj -`)});const b=h;g(`xref -0 ${c.length+1} -0000000000 65535 f -`);for(const x of f)g(`${String(x).padStart(10,"0")} 00000 n -`);return g(`trailer -<< /Size ${c.length+1} /Root 1 0 R /Info ${c.length} 0 R >> -startxref -${b} -%%EOF -`),new Blob(d,{type:"application/pdf"})}function kY(i){const{data:e}=i.getContext("2d").getImageData(0,0,i.width,i.height),t=new Uint8Array(i.width*i.height*3);for(let n=0,a=0;nObject.entries(Bt).map(([i,e])=>{if(i==="getColor")return` def ${i}(self): - farbe = ${e.python} - return FARBNAMEN.get(farbe, farbe) -`;const t=typeof e.simulated=="boolean"?`bool(${e.python})`:e.python;return` def ${i}(self): - return ${t} -`}).join(` -`);function _Y(i){const e=Object.entries(Cs).map(([n,a])=>` ${JSON.stringify(n)}: (${a.join(", ")}), -`).join(""),t=Object.entries(SY).map(([n,a])=>` ${JSON.stringify(n)}: ${JSON.stringify(i==="de"?a:n)}, -`).join("");return`import event, time, math, cyberpi, mbot2, mbuild - -FARBEN = { -${e}} - -FARBNAMEN = { -${t}} - - -class Led: - def __init__(self, nr): - self.nr = nr - - def setColor(self, farbe): - r, g, b = FARBEN.get(farbe, (0, 0, 0)) - cyberpi.led.on(r, g, b, id=self.nr + 1) - - -def strecke(wert, einheit): - # Überladung forward(2, "Umdrehungen"): eine Radumdrehung ist pi * ${el} cm - if einheit.lower() in ("umdrehung", "umdrehungen", "rotation", "rotations", "rotação", "rotações"): - return wert * math.pi * ${el} - return wert - - -class MBot: - def forward(self, wert=10, einheit="cm"): - mbot2.straight(strecke(wert, einheit)) - - def backward(self, wert=10, einheit="cm"): - mbot2.straight(-strecke(wert, einheit)) - - def turnLeft(self, grad=90): - mbot2.turn(-grad) - - def turnRight(self, grad=90): - mbot2.turn(grad) - - def stop(self): - mbot2.EM_stop("ALL") - - def drive(self, links, rechts): - mbot2.drive_power(links, -rechts) - - def setLed(self, r, g, b): - cyberpi.led.on(r, g, b) - - def ledOff(self): - cyberpi.led.off() - - def led(self, nr): - return Led(nr) - - def playTone(self, hz, ms): - cyberpi.audio.play_tone(hz, ms / 1000) - - def pause(self, ms): - time.sleep(ms / 1000) - - def resetTimer(self): - cyberpi.timer.reset() - - def emotion(self, name): - getattr(cyberpi.emotion, name)() - - def showText(self, text): - cyberpi.display.clear() - cyberpi.display.show_label(str(text), 24, "center") - -${TY()} - -mbot = MBot() -`}const AY=i=>` - -laeuft = False - - -@event.start -def beim_einschalten(): - mbot.showText(${JSON.stringify(i)}) - - -@event.is_press("a") -def taste_a(): - global laeuft - if laeuft: - return - laeuft = True - cyberpi.display.clear() - try: - programm() - finally: - mbot.stop() - laeuft = False -`;function jY(i,e,t,n="A = Start"){const a={pythonWithoutMatch:!0},s=mh(Ws(i,"python",a)),r=e.filter(h=>h!==i&&Tt(h.caption)).map(h=>mh(Ws(h,"python",a))),o=[s,...r].join(` -`);if(/\binput\(/u.test(o))return{error:"input"};if(/\bopen\(|\bos\.path\b/u.test(o))return{error:"files"};const l=new Set,u=h=>h.split(` -`).filter(g=>(/^import \w+$/u.test(g)&&l.add(g),!/^import \w+$/u.test(g))).join(` -`).trim(),m=u(s);if(m===""||/^#[^\n]*$/u.test(m))return{error:"empty"};const c=m.split(` -`).map(h=>h.trim()===""?"":" "+h).join(` -`);return{source:`# VisuStruct VisuBot -${_Y(t)}${[...l].join(` -`)} - -`+r.map(h=>u(h)+` - - -`).join("")+`def programm(): -${c} -`+AY(n)}}function mh(i){return i.split(` -`).map(e=>{const t=/^(\s*)(.*)$/u.exec(e);return t[2].startsWith("#")?e:t[1]+M3(t[2])}).join(` -`)}const CY=/^(==|!=|<=|>=|\+=|-=|\*=|\/=|=|<|>|,|:)/u,DY=/^(and|or|not|in|if|elif|while|return|for|else)\b/u;function M3(i){let e="",t="",n=0;for(let a=0;a0)){if(h==="+"&&e.trim()!==""&&!/[-+*/%(,=<>]\s*$/u.test(e)){t.push(e),e="";continue}e+=h}}if(n>0&&(e+=i.slice(a)),t.push(e),t.length<2)return t[0];const s=/^\s*/u.exec(t[0])[0],r=/\s*$/u.exec(t[t.length-1])[0],o=t.map(d=>d.trim()),l=d=>d.startsWith('"')||d.startsWith("'")||d.startsWith("str("),u=o.findIndex(l);if(u<0)return i;const m=u>0?[`str(${o.slice(0,u).join(" + ")})`]:[],c=o.slice(u).map(d=>l(d)?d:`str(${d})`);return s+[...m,...c].join(" + ")+r}const T1=i=>i.replace(/&/g,"&").replace(//g,">");function BY(i,e){const t=document.createElement("dialog");t.className="about mbot-upload",t.setAttribute("aria-labelledby","mbot-upload-title"),t.innerHTML=` -
    -

    ⬆ ${T1(p("web_visubot_upload_title"))}

    - -
    -
    -
      -
    1. ${T1(p("web_visubot_upload_step1"))}
    2. -
    3. ${T1(p("web_visubot_upload_step2"))}
    4. -
    5. ${T1(p("web_visubot_upload_step3"))}
    6. -
    - -

    - -
    `;const n=t.querySelector(".mbot-upload-status"),a=t.querySelector("progress"),s=t.querySelector("[data-action=upload-start]");let r=!1;const o=()=>!r&&t.close();t.querySelector("[data-action=upload-close]").addEventListener("click",o),t.addEventListener("cancel",u=>r&&u.preventDefault()),t.addEventListener("click",u=>u.target===t&&o()),t.addEventListener("close",()=>t.remove());const l=jY(i,e,Kb(),p("web_visubot_upload_label"));"error"in l&&(n.textContent=`⚠︎ ${p(`web_visubot_upload_error_${l.error}`)}`,s.hidden=!0),s.addEventListener("click",async()=>{if("source"in l){r=!0,s.disabled=!0,a.hidden=!1,a.value=0,n.textContent=p("web_visubot_upload_busy");try{await be.upload(l.source,(u,m)=>a.value=u/m),a.value=1,n.textContent=`✓ ${p("web_visubot_upload_done")}`,n.classList.add("done")}catch(u){const m=u;n.textContent=m.name==="NotFoundError"?"":`⚠︎ ${p("web_visubot_upload_failed")} (${m.message})`,a.hidden=!0,await be.disconnect(m.name==="NotFoundError"?void 0:m.message).catch(()=>{})}finally{r=!1,s.disabled=!1}}}),document.body.append(t),t.showModal()}const ss=[S.verzweigung,S.whileSchleife,S.doUntilSchleife,S.anweisung],dh=()=>[{description:p("web_file_type"),accept:{"application/xml":[".visustruct",".strk"]}}],rs=i=>p(`element_${S[i]}`),PY={[S.verzweigung]:"age >= 18",[S.whileSchleife]:"i < 10",[S.doUntilSchleife]:"again == 1",[S.fallauswahl]:"day"},OY={statement:'int number = 5 | double x = 1.5 | String text = "Your text" | boolean ok = true | const double PI = 3.14',input:'input: int age "Enter age" | input: double price "Enter price" | input: String name "Enter name"',output:'output: number | output: "Your text" | output: "Your age: " + age'};function xr(i){switch(i.type){case S.anweisung:{const e=Ss(i.textLines.join(` -`));return e==="input"?"input:":e==="output"?"output:":void 0}case S.verzweigung:return"if";case S.whileSchleife:case S.doUntilSchleife:return"while";case S.fallauswahl:return"switch";default:return}}function hh(i){const e=xr(i),t=i.textLines.join(` -`),n=e?Vh(e,t):t;return i.type===S.anweisung&&n.trim()==="ø"?"":n}function gh(i,e,t){const n=st(i,e);if(!n)return;const a=xr(n);if(n.type===S.anweisung&&!a&&t.trim()==="")return pn(i,e,["ø"]);const s=a?lb(a,Vh(a,t)):t;return pn(i,e,s.split(` -`))}function ph(i){switch(i.type){case S.anweisung:{const e=Ss(i.textLines.join(` -`));return e==="input"?'int age "Enter age"':e==="output"?'"Your age: " + age':p("inspector_placeholder_statement")}case S.verzweigung:case S.whileSchleife:case S.doUntilSchleife:case S.endlosschleife:return p("inspector_placeholder_condition");case S.fallauswahl:return p("inspector_placeholder_selector");case S.aufruf:return p("inspector_placeholder_call");case S.aussprung:return p("inspector_placeholder_break");default:return""}}function de(i,e={},...t){const n=document.createElement(i);for(const[a,s]of Object.entries(e))n.setAttribute(a,s);return n.append(...t),n}const MY=760,fh=[{value:1,label:"settings_ui_scale_small"},{value:1.22,label:"settings_ui_scale_medium"},{value:1.5,label:"settings_ui_scale_large"},{value:1.82,label:"settings_ui_scale_extra_large"}],Eo=[{value:14,label:"settings_ui_scale_small"},{value:17,label:"settings_ui_scale_medium"},{value:20,label:"settings_ui_scale_large"},{value:24,label:"settings_ui_scale_extra_large"}];function bh(i){document.documentElement.style.setProperty("--code-size",`${i}px`)}const ZY=/Mac|iPhone|iPad/.test(navigator.platform);function os(i){return $3(jn()?"visubot":"visustruct",In(),i)}function Rt(i,e,t,n){const a=n?`${e} (${n})`:e;return``}function An(i,e=!1){return ZY?`${e?"⇧":""}⌘${i}`:[p("web_key_ctrl"),...e?[p("web_key_shift")]:[],i].join("+")}const W=JY,xh="visustruct.ui.scale.canvas",vh="visustruct.ui.scale.code",wh="visustruct.learn.open",yh="visustruct.files.open",zh="visustruct.files.width",Ih="visustruct.learn.width",kh="visustruct.live.open",LY="visustruct.live.width",Sh="visustruct.robotPreview.open",Th="visustruct.codeMain",No="visustruct.visubot.blocks",_h="visustruct.palette.width",WY="visustruct.editor.width",Ah="visustruct.visubot.collapsedGroups",jh="visustruct.robotPreview.stacked",Ch="visustruct.robotPreview.width",Dh="visustruct.simulation.diagram",Bh="visustruct.inspector.height",Ph="visustruct.robotPreview.height",Oh="visustruct.editor.hidden",Fo="visustruct.palette.compact",Mh="visustruct.palette.hidden",EY='',NY=80;class FY{constructor(e){y(this,"tabs",[]);y(this,"tabIndex",0);y(this,"nextTabSerial",1);y(this,"clipboard");y(this,"zoom",1);y(this,"simulating",!1);y(this,"exportButton");y(this,"closeExport");y(this,"learn");y(this,"shell");y(this,"railButton");y(this,"liveRailButton");y(this,"editorRailButton");y(this,"topbar",de("div",{class:"topbar"}));y(this,"bottombar",de("div",{class:"bottombar"}));y(this,"editorHidden",!1);y(this,"setPaletteHidden");y(this,"files");y(this,"filesRailButton");y(this,"closeSettings");y(this,"escapeSettings");y(this,"exerciseSaveTimer");y(this,"paletteObserver");y(this,"resizeObserver");y(this,"renderedHostSize",{width:-1,height:-1});y(this,"simPath");y(this,"sim");y(this,"simButton");y(this,"pseudocodeBox");y(this,"blockMode",!1);y(this,"varList");y(this,"shownVars","");y(this,"blocks");y(this,"blocksButton");y(this,"coding",!1);y(this,"code");y(this,"codeButton");y(this,"liveCoding",!1);y(this,"codeTyping");y(this,"live");y(this,"preview",new fY);y(this,"previewOpen",!1);y(this,"previewOpenedForSim",!1);y(this,"codeMain",!1);y(this,"previewStacked",!1);y(this,"previewResizer");y(this,"measure",cb());y(this,"metrics",db());y(this,"engine",new Yh(this.metrics,this.measure));y(this,"renderer",new eg(this.metrics,this.measure));y(this,"layout");y(this,"flat",[]);y(this,"canvasHost");y(this,"svg");y(this,"overlay");y(this,"inspector");y(this,"captionInput");y(this,"undoButton");y(this,"redoButton");y(this,"deleteButton");y(this,"statusLine");y(this,"drag");y(this,"inline");y(this,"lastCanvasTap",{spot:"",time:0,x:0,y:0});y(this,"pendingInline");y(this,"downloads",null);y(this,"inViewer","claude"in window);y(this,"visuBot");y(this,"stopLinkListener");y(this,"tabStrip");this.root=e,this.tabs.push(this.makeTab());const t=new URLSearchParams(location.search);t.has("visubot")?Ti("on"):t.has("visustruct")&&Ti("off"),Um(Si()),Vm(Me(No)==="1"),this.learn=new RJ(this.learnHost());const n=t.get("lektion")??void 0;n&&this.learn.showLesson(n),this.shell=document.createElement("div"),this.shell.className="shell",e.replaceWith(this.shell),this.live=new gY(()=>this.setLiveCoding(!1),u=>{this.selected=u,this.render()},(u,m)=>this.commitFromCode(u,m),u=>u?this.redo():this.undo()),this.live.onRobotRun=u=>this.runRobot(u),this.live.onSwap=()=>this.setCodeMain(!this.codeMain),this.live.onRename=()=>this.codeTyping=void 0,this.live.onReference=()=>{this.learn.showReference(),this.setLearning(!0)},this.files=new $J({open:(u,m,c)=>this.openFromWorkspace(u,m,c),hide:()=>this.setFilesOpen(!1),message:u=>this.showMessage(u),progressLoaded:()=>{this.learn.restore(),n&&this.learn.showLesson(n)},chooseWorld:u=>this.chooseWorld(u,!1)});const a=de("div",{class:"columns"});a.append(this.filesRail(),this.files.element,this.panelResizer(this.files.element,"files-resizer",zh,p("web_files_resize"),1),this.learnRail(),this.learn.element,this.learnResizer(),this.editorRail(),e,this.panelResizer(e,"editor-resizer",WY,p("web_editor_resize"),()=>this.codeMain?-1:1,()=>this.live.element),this.liveResizer(),this.live.element,this.liveRail(),this.previewResizer=this.panelResizer(this.preview.element,"preview-resizer",Ch,p("web_visubot_preview_resize"),-1),this.preview.element,this.preview.rail),this.setupPreview(),this.shell.append(this.topbar,a,this.bottombar);const s=Me(wh),r=new URLSearchParams(location.search).has("learn")||n!==void 0;this.showWorldInURL(),this.setLearning(r||(s===null?window.innerWidth>1100:s==="1"),r),this.setFilesOpen(Me(yh)==="1");const o=Number(Me(xh));fh.some(u=>u.value===o)&&(this.zoom=o),this.setLiveCoding(Me(kh)==="1"),this.setEditorHidden(Me(Oh)==="1"),this.buildUI(),this.setCodeMain(Me(Th)==="1"),this.render(),window.addEventListener("pointermove",u=>this.onPointerMove(u)),window.addEventListener("pointerup",u=>this.onPointerUp(u)),window.addEventListener("pointercancel",()=>this.endDrag()),window.addEventListener("keydown",u=>this.onKeyDown(u)),window.addEventListener("beforeunload",u=>{(this.tabs.some(m=>m.dirty&&!m.exerciseId)||!this.files.ready&&ve.hasContent())&&u.preventDefault()}),this.setupFileDrop();const l=window.claude;typeof(l==null?void 0:l.use)=="function"&&l.use("downloads").then(u=>this.downloads=u).catch(()=>{})}get tab(){return this.tabs[this.tabIndex]}get doc(){return this.tab.doc}set doc(e){this.tab.doc=e}get fileName(){return this.tab.fileName??`${hn(this.tab)}.visustruct`}get fileHandle(){return this.tab.fileHandle}set fileHandle(e){this.tab.fileHandle=e}get dirty(){return this.tab.dirty}set dirty(e){this.tab.dirty=e}get undoStack(){return this.tab.undoStack}get redoStack(){return this.tab.redoStack}get selected(){return this.tab.selected}set selected(e){this.tab.selected=e}buildUI(){var ge,ie,ue,N;this.root.className="app",this.root.innerHTML=` -
    - ${jn()?ip:'VisuStruct'} ${W(p("web_brand_suffix"))} -
    - ${Rt("new",p("toolbar_new"),me.newFile)} - ${Rt("open",p("menu_open"),me.open,An("O"))} - ${Rt("save",p("menu_save"),me.save,An("S"))} -
    -
    - - -
    -
    - - - - -
    - - -
    -
    - - - - `,this.topbar.replaceChildren(this.root.querySelector(".toolbar")),this.bottombar.replaceChildren(this.root.querySelector(".statusbar"));const e=_=>this.root.querySelector(_)??this.topbar.querySelector(_)??this.bottombar.querySelector(_);this.canvasHost=e(".canvas"),this.blocks=new up({press:(_,M)=>{if(this.simulating)return;document.activeElement instanceof HTMLElement&&document.activeElement.blur();const J=this.selected&&Mn(_,this.selected);this.selected=_,this.render(),M.pointerType!=="touch"&&!(gn(this.doc,_)&&!J)?this.beginDrag(M,{kind:"move",path:_}):M.preventDefault()},editValue:(_,M,J)=>{const q=Tm(this.doc,_,M,J);q?this.commit(pn(this.doc,_,q),_):this.render()},labels:hp()}),this.canvasHost.after(this.blocks.element),this.inspector=e(".inspector"),this.captionInput=e(".caption"),this.undoButton=e("[data-action=undo]"),this.redoButton=e("[data-action=redo]"),this.deleteButton=e("[data-action=delete]"),this.statusLine=e(".status"),this.simButton=e("[data-action=simulate]"),this.pseudocodeBox=e("[data-action=pseudocode]"),this.blocksButton=e("[data-action=blocks]"),this.blocksButton.addEventListener("click",()=>this.setBlockMode(!this.blockMode,!0)),this.pseudocodeBox.addEventListener("change",()=>this.commit({...this.doc,pseudocode:this.pseudocodeBox.checked||void 0})),e("[data-action=hide-editor]").addEventListener("click",()=>this.setEditorHidden(!0)),e("[data-action=swap-main]").addEventListener("click",()=>this.setCodeMain(!this.codeMain)),this.labelSwap(),this.sim=new rJ(()=>this.doc,_=>{this.simPath=_,this.simulating&&this.renderDiagramOnly(),this.scrollToSimPath()},_=>this.findSubprogram(_),()=>this.setSimulating(!1),_=>{this.simulating&&this.liveCoding&&this.live.showTranscript(_)}),this.root.append(this.sim.element,this.simResizer(),this.inspectorResizer()),jn()?(this.visuBot??(this.visuBot=new J4),this.sim.setRobot(this.visuBot)):(this.preview.hostSimulation(void 0),(ge=this.visuBot)==null||ge.dispose(),this.visuBot=void 0),this.simButton.addEventListener("click",()=>this.setSimulating(!this.simulating)),this.codeButton=e("[data-action=code]"),this.code=new f4(()=>this.doc,(_,M)=>this.offerDownload(_,new Blob([M],{type:"text/plain"})),()=>this.setCoding(!1),_=>this.commit({...this.doc,pseudocode:_||void 0}),()=>this.tabs.filter(_=>_!==this.tab&&Tt(_.doc.caption)).map(_=>_.doc)),this.root.append(this.code.element),this.codeButton.addEventListener("click",()=>this.setCoding(!this.coding)),(ie=this.paletteObserver)==null||ie.disconnect();const t=()=>Number(Me(_h))||0,n=()=>this.root.classList.toggle("compact-palette",Me(Fo)==="1"||(t()>0?t()<150:this.root.getBoundingClientRect().widthMe(Mh)==="1",s=(_,M)=>{const J=_===void 0?void 0:Math.round(Math.max(80,Math.min(360,_)));a()?this.root.style.setProperty("--palette-width","36px"):J===void 0?this.root.style.removeProperty("--palette-width"):this.root.style.setProperty("--palette-width",`${J}px`),M&&Ze(_h,J===void 0?"":String(J)),n()},r=_=>{Ze(Mh,_?"1":"0"),this.root.classList.toggle("palette-hidden",_),s(t()>0?t():void 0,!1)};r(a()),this.setPaletteHidden=r,e("[data-action=hide-palette]").addEventListener("click",()=>r(!0)),e("[data-action=show-palette]").addEventListener("click",()=>r(!1));const o=e(".palette-resizer"),l=e(".palette"),u=()=>getComputedStyle(this.shell).direction==="rtl"?-1:1;o.addEventListener("pointerdown",_=>{if(_.button!==0)return;_.preventDefault(),o.setPointerCapture(_.pointerId);const M=_.clientX,J=l.getBoundingClientRect().width,q=u();this.shell.classList.add("resizing");const X=ae=>s(J+q*(ae.clientX-M),!1),Y=ae=>{o.removeEventListener("pointermove",X),o.removeEventListener("pointerup",Y),o.removeEventListener("pointercancel",Y),this.shell.classList.remove("resizing");const re=J+q*(ae.clientX-M);re<50?r(!0):s(re,!0)};o.addEventListener("pointermove",X),o.addEventListener("pointerup",Y),o.addEventListener("pointercancel",Y)}),o.addEventListener("dblclick",()=>s(void 0,!0)),o.addEventListener("keydown",_=>{const M=_.key==="ArrowRight"?20:_.key==="ArrowLeft"?-20:0;M&&(_.preventDefault(),s(l.getBoundingClientRect().width+u()*M,!0))}),this.paletteObserver=new ResizeObserver(n),this.paletteObserver.observe(this.root);const m=e("[data-action=compact-palette]");m.checked=Me(Fo)==="1",m.addEventListener("change",()=>{Ze(Fo,m.checked?"1":"0"),n()});const c=(_,M,J,q,X)=>{const Y=document.createElement("li");Y.className="palette-item",Y.dataset.item=X,Y.dataset.scope=Yo(q().type)??"method",Y.append(this.paletteIcon(M),document.createTextNode(J)),Y.addEventListener("pointerdown",ae=>this.beginDrag(ae,{kind:"palette",make:q,label:J})),Y.tabIndex=0,Y.addEventListener("keydown",ae=>{(ae.key==="Enter"||ae.key===" ")&&this.insertNearSelection(q())}),_.append(Y)},d=e(".palette-basic");for(const{item:_}of gb)c(d,_,rs(qr(_).type),()=>qr(_),_);const h=jn();this.blocksButton.hidden=!h,this.setBlockMode(h&&Me(No)==="1",!1),this.root.classList.toggle("visubot-mode",h),this.shell.classList.toggle("robot-world",h),h||this.preview.pause(),document.documentElement.classList.toggle("visubot-theme",h);const g=e(".palette-visubot");if(g.hidden=!h,e(".palette-visubot-title").hidden=!h,h){const _={[S.verzweigung]:"verzweigung",[S.whileSchleife]:"whileSchleife"},M=new Set((Me(Ah)??"").split(",").filter(Boolean)),J=(Y,ae)=>{const re=document.createElement("li");re.className="palette-group",re.dataset.botColor=fl(ae);const xe=document.createElement("button");xe.type="button",xe.textContent=[H0[ae],p(`web_visubot_group_${ae}`)].filter(Boolean).join(" ");const Je=Un=>{xe.setAttribute("aria-expanded",String(Un));for(const b1 of Y.querySelectorAll(`[data-group="${ae}"]`))b1.hidden=!Un};return xe.addEventListener("click",()=>{const Un=xe.getAttribute("aria-expanded")!=="true";Un?M.delete(ae):M.add(ae),Ze(Ah,[...M].join(",")),Je(Un)}),re.append(xe),Y.append(re),()=>Je(!M.has(ae))},q=(Y,ae,re)=>{for(const xe of new Set(ae.map(Je=>Je.group))){const Je=J(Y,xe);for(const Un of ae.filter(b1=>b1.group===xe)){const b1=re(Un);b1.dataset.group=xe,b1.dataset.botColor=fl(xe)}Je()}};q(g,op,Y=>(c(g,_[Y.type]??"anweisung",p(Y.key),Y.make,Y.key),g.lastElementChild));const X=e(".palette-visubot-sensors");X.replaceChildren(),q(X,V0,Y=>{const ae=p(Y.key),re=document.createElement("li");re.className="palette-item palette-sensor",re.dataset.item=Y.key,re.title=Y.text;const xe=document.createElement("code");return xe.textContent=Y.text,/^mbot\.is/.test(Y.text)&&xe.classList.add("sensor-bool"),re.append(document.createTextNode(ae),xe),re.addEventListener("pointerdown",Je=>this.beginDrag(Je,{kind:"sensor",text:Y.text,label:Y.text})),re.tabIndex=0,re.addEventListener("keydown",Je=>{(Je.key==="Enter"||Je.key===" ")&&this.insertSensor(Y.text)}),X.append(re),re})}e(".palette-visubot-sensors").hidden=!h,e(".palette-visubot-sensors-title").hidden=!h;for(const _ of["ops","vars","control"])e(`.palette-visubot-${_}`).hidden=!h,e(`.palette-visubot-${_}-title`).hidden=!h;if(h){const _=In();e(".palette-visubot-ops").replaceChildren(...z4.map(X=>this.expressionItem(I4(X,_),X.java,X.shape==="bool"?"bool":"round","op",X.id==="random"?"🎲 1 … 10":void 0)));const J=e(".palette-visubot-vars");J.replaceChildren();for(const X of A4){const Y=()=>{const ae=ev(this.doc),re=X.id==="declare"?_4(Ri(this.doc)):ae[ae.length-1]??"zahl";return je(S.anweisung,{textLines:[X.java(re)]})};c(J,"anweisung",j4(X,_),Y,`visublocks_var_${X.id}`),J.lastElementChild.dataset.botColor="variable"}this.varList=J,this.shownVars="";const q=e(".palette-visubot-control");q.replaceChildren(),c(q,"forZaehlSchleife",p("web_visubot_el_repeat"),()=>({...qr("forZaehlSchleife"),textLines:y4(10)}),"web_visubot_el_repeat")}this.tabStrip=e(".tabs"),this.exportButton=e("[data-action=export]");const f=e(".export-menu");this.exportButton.setAttribute("aria-haspopup","menu"),this.exportButton.addEventListener("click",()=>{var _;f.hidden=!f.hidden,f.hidden||(_=f.querySelector("button"))==null||_.focus()}),f.addEventListener("click",_=>{var J;const M=(J=_.target.closest("[data-export]"))==null?void 0:J.dataset.export;M&&(f.hidden=!0,this.exportDiagram(M))}),f.addEventListener("keydown",_=>{_.key==="Escape"&&(f.hidden=!0,this.exportButton.focus())}),this.closeExport&&document.removeEventListener("pointerdown",this.closeExport),this.closeExport=_=>{!f.hidden&&!f.parentElement.contains(_.target)&&(f.hidden=!0)},document.addEventListener("pointerdown",this.closeExport);const b=e("[data-action=language]");b.value=In(),b.addEventListener("change",()=>void this.changeLanguage(()=>lg(b.value),"language"));const x=[e("[data-action=mbot-link]"),e("[data-action=mbot-link-settings]")],v=e(".settings-mbot-link small"),w=()=>{const _=be.connected,M=be.connecting?p("web_visubot_connecting"):p(_?"web_visubot_disconnect":"web_visubot_connect");x[0].hidden=!jn()||!$r(),z.hidden=x[0].hidden,x[0].classList.toggle("connected",_);const J=be.connecting?"⏳":_?"🟢":"🔌";x[0].textContent=`${J} mBot2`,x[0].title=M,x[1].textContent=`${J} ${M}`,x[1].disabled=!$r(),x[1].parentElement.hidden=!jn(),v.textContent=$r()?rp():p("web_visubot_no_serial")};for(const _ of x)_.addEventListener("click",()=>void V4());const z=e("[data-action=mbot-upload]");z.addEventListener("click",()=>BY(this.doc,this.tabs.filter(_=>_!==this.tab).map(_=>_.doc))),(ue=this.stopLinkListener)==null||ue.call(this),this.stopLinkListener=be.listen(w),w();for(const _ of this.topbar.querySelectorAll("[data-action=visubot] [data-world]"))_.addEventListener("click",()=>void this.chooseWorld(_.dataset.world,!0));const I=e("[data-action=keywords]");I.checked=jb(),I.addEventListener("change",()=>void this.changeLanguage(()=>og(I.checked),"keywords"));const T=e("[data-action=code-scopes]");T.checked=k3(),T.addEventListener("change",()=>j$(T.checked));const k=e("[data-action=live-language]");k.value=this.live.language;const C=e("[data-action=java-class]"),P=e("[data-action=java-main]"),D=e("[data-action=java-swing]");D.checked=this.live.swingSupport,D.addEventListener("change",()=>this.live.setSwingSupport(D.checked));const B=()=>{C.checked=this.live.javaForm.asClass,P.checked=this.live.javaForm.withMain,e(".settings-menu .java-only").hidden=this.live.language!=="java"};B(),k.addEventListener("change",()=>{this.live.setTarget(k.value),B()});for(const _ of[C,P])_.addEventListener("change",()=>this.live.setJavaForm({asClass:C.checked,withMain:P.checked}));const j=e("[data-action=zoom]");j.value=String(this.zoom),j.addEventListener("change",()=>{this.zoom=Number(j.value),Ze(xh,j.value),this.renderDiagramOnly()});const Z=e("[data-action=code-size]"),F=Number(Me(vh));Z.value=String(Eo.some(_=>_.value===F)?F:Eo[0].value),bh(Number(Z.value)),Z.addEventListener("change",()=>{Ze(vh,Z.value),bh(Number(Z.value))}),this.setupSettingsMenu(e("[data-action=settings]"),e(".settings-menu")),e("[data-action=about]").addEventListener("click",()=>{e(".settings-menu").hidden=!0,this.showAbout()}),e("[data-action=cut]").addEventListener("click",()=>this.cutSelection()),e("[data-action=copy]").addEventListener("click",()=>this.copySelection()),e("[data-action=paste]").addEventListener("click",()=>this.paste());const U=e("input[type=file]");U.addEventListener("change",async()=>{var M;const _=(M=U.files)==null?void 0:M[0];U.value="",_&&this.loadXML(await _.text(),_.name)}),e("[data-action=new]").addEventListener("click",()=>this.addTab()),e("[data-action=open]").addEventListener("click",()=>this.open(U)),e("[data-action=save]").addEventListener("click",()=>this.save()),this.undoButton.addEventListener("click",()=>this.undo()),this.redoButton.addEventListener("click",()=>this.redo()),this.deleteButton.addEventListener("click",()=>this.deleteSelection());let ne;this.captionInput.addEventListener("focus",()=>ne=this.doc),this.captionInput.addEventListener("input",()=>{this.doc={...this.doc,caption:this.captionInput.value},this.dirty=!0,this.updateStatus(),this.refreshLive()}),this.captionInput.addEventListener("change",()=>{ne&&ne.caption!==this.doc.caption&&this.pushUndo(ne),ne=this.doc}),this.canvasHost.addEventListener("pointerdown",_=>this.onCanvasPointerDown(_)),this.canvasHost.addEventListener("contextmenu",_=>this.onCanvasContextMenu(_)),(N=this.resizeObserver)==null||N.disconnect(),this.resizeObserver=new ResizeObserver(()=>{const{clientWidth:_,clientHeight:M}=this.canvasHost;_===this.renderedHostSize.width&&M===this.renderedHostSize.height||this.renderDiagramOnly()}),this.resizeObserver.observe(this.canvasHost)}showWorldInURL(){const e=new URLSearchParams(location.search);for(const a of["learn","lektion","visubot","visustruct","visuobject","visuflow"])e.delete(a);const t=e.toString(),n=Si()==="on"?"visubot":"visustruct";history.replaceState(history.state,"",`${location.pathname}?${n}${t?`&${t}`:""}${location.hash}`)}async chooseWorld(e,t){if(e==="object"){gl(),location.href=`${location.pathname}?visuobject${location.hash}`;return}if(e==="flow"){np(),location.href=`${location.pathname}?visuflow${location.hash}`;return}e===Si()&&N4()||await this.changeLanguage(async()=>{Ti(e),Um(e),this.showWorldInURL()},t?`visubot] [data-world=${e}`:void 0)}async changeLanguage(e,t){var n,a;this.setSimulating(!1),this.setCoding(!1),await e(),L3(),this.sim.end(),this.live.build(),this.preview.relabel(),this.placePreview(),this.files.build(),this.buildUI(),this.render(),this.learn.render(),this.labelLearnRail(),this.labelLiveRail(),this.labelEditorRail(),this.labelFilesRail(),t!==void 0&&((n=this.topbar.querySelector("[data-action=settings]"))==null||n.click(),(a=this.topbar.querySelector(`[data-action=${t}]`))==null||a.focus())}revealAreas(e){var n;const t=[];for(const a of e)a==="editor"&&this.editorHidden?(this.setEditorHidden(!1),t.push(()=>this.setEditorHidden(!0))):a==="palette"&&this.root.classList.contains("palette-hidden")?((n=this.setPaletteHidden)==null||n.call(this,!1),t.push(()=>{var s;return(s=this.setPaletteHidden)==null?void 0:s.call(this,!0)})):a==="livecode"&&!this.liveCoding?(this.setLiveCoding(!0),t.push(()=>this.setLiveCoding(!1))):a==="files"&&!this.shell.classList.contains("files-open")&&(this.setFilesOpen(!0),t.push(()=>this.setFilesOpen(!1)));return()=>t.reverse().forEach(a=>a())}setFilesOpen(e){this.shell.classList.toggle("files-open",e),Ze(yh,e?"1":"0"),this.keepAColumnOpen(),this.canvasHost&&this.renderDiagramOnly()}filesRail(){return this.filesRailButton=de("button",{class:"learn-rail files-rail"}),this.labelFilesRail(),this.filesRailButton.addEventListener("click",()=>this.setFilesOpen(!0)),this.filesRailButton}labelFilesRail(){this.filesRailButton&&(this.filesRailButton.title=p("web_files_show"),this.filesRailButton.innerHTML=`${Dt}📁 ${W(p("web_files_title"))}`)}openFromWorkspace(e,t,n){const a=this.tabs.find(s=>{var r;return((r=s.workspace)==null?void 0:r.dir)===t.dir&&s.workspace.base===t.base});if(a){this.tabIndex=this.tabs.indexOf(a),this.render();return}e?this.loadXML(e,`${t.base}.visustruct`):n!==void 0&&((this.dirty||this.tab.fileName||this.tab.exerciseId)&&this.addTab(),this.doc=this.live.documentFromJavaText(n),this.tab.fileName=`${t.base}.visustruct`,this.resetHistory()),this.tab.workspace=t,this.tab.fileHandle=void 0,n!==void 0&&(this.live.adoptJava(this.doc,n),this.setLiveCoding(!0)),this.render()}async saveToWorkspace(){if(!this.files.ready||this.tab.fileHandle&&!this.tab.workspace)return!1;const e=this.doc.caption.trim()||hn(this.tab),t=this.live.language==="java"?this.live.javaFor(this.doc,this.tab):void 0;try{const n=await this.files.save(e,x1(this.doc),t,this.tab.workspace);if(!n)return this.setFilesOpen(!0),!0;this.tab.workspace=n,this.tab.fileName=`${n.base}.visustruct`,this.dirty=!1,this.render(),this.showMessage(p("web_files_saved_fmt",n.path))}catch(n){this.showMessage(n.message)}return!0}setLearning(e,t=!0){this.shell.classList.toggle("learning",e),t&&Ze(wh,e?"1":"0"),this.keepAColumnOpen(),this.canvasHost&&this.renderDiagramOnly()}setEditorHidden(e){this.editorHidden=e,this.shell.classList.toggle("editor-hidden",e),Ze(Oh,e?"1":"0"),e&&!this.liveCoding&&!this.shell.classList.contains("learning")&&this.setLiveCoding(!0),!e&&this.canvasHost&&this.render()}keepAColumnOpen(){this.editorHidden&&!this.liveCoding&&!this.shell.classList.contains("learning")&&!this.shell.classList.contains("files-open")&&this.setEditorHidden(!1)}editorRail(){return this.editorRailButton=de("button",{class:"learn-rail editor-rail"}),this.labelEditorRail(),this.editorRailButton.addEventListener("click",()=>this.setEditorHidden(!1)),this.editorRailButton}labelEditorRail(){this.editorRailButton&&(this.editorRailButton.title=p("web_editor_show"),this.editorRailButton.innerHTML=`${Dt}✎ ${W(p("web_editor_title"))}`)}showAbout(){const e=(n,a,s)=>``,t=de("dialog",{class:"about","aria-labelledby":"about-title"});t.innerHTML=` -
    -

    ${W(p("about_nav_title"))}

    - -
    -
    -
    - -
    -

    VisuStruct ${W(p("web_brand_suffix"))}

    -

    ${W(p("web_about_version_fmt").replace("%@","0.15.29"))}

    -

    Holger Sebastiao

    - ${e("about_web_label","https://visustruct.visucode.de","visustruct.visucode.de")} - ${e("about_developer_label","https://www.sebastiao.org","www.sebastiao.org")} - ${e("about_project_github_label","https://github.com/code4teaching/VisuStruct","code4teaching/VisuStruct")} -
    -
    -

    ${W(p("about_dateiformat_heading"))}

    -

    ${W(p("about_dateiformat_part1"))}.visustruct${W(p("about_dateiformat_part2"))}

    -

    ${W(p("about_dateiformat_strk"))}

    -

    ${W(p("web_about_software"))}

    -

    ${W(p("web_about_software_text"))} ${W(p("web_about_licenses"))}

    -

    ${W(p("about_copyright"))}

    -
    `,t.querySelector("[data-action=about-done]").addEventListener("click",()=>t.close()),t.addEventListener("click",n=>n.target===t&&t.close()),t.addEventListener("close",()=>t.remove()),document.body.append(t),t.showModal()}setupSettingsMenu(e,t){const n=a=>{t.hidden=!a,e.setAttribute("aria-expanded",String(a))};e.addEventListener("click",()=>n(t.hidden)),this.closeSettings&&document.removeEventListener("pointerdown",this.closeSettings),this.escapeSettings&&document.removeEventListener("keydown",this.escapeSettings),this.closeSettings=a=>{!t.hidden&&!e.parentElement.contains(a.target)&&n(!1)},document.addEventListener("pointerdown",this.closeSettings),this.escapeSettings=a=>{a.key!=="Escape"||t.hidden||(n(!1),e.focus())},document.addEventListener("keydown",this.escapeSettings)}learnRail(){return this.railButton=de("button",{class:"learn-rail"}),this.labelLearnRail(),this.railButton.addEventListener("click",()=>this.setLearning(!0)),this.railButton}liveRail(){return this.liveRailButton=de("button",{class:"learn-rail live-rail"}),this.labelLiveRail(),this.liveRailButton.addEventListener("click",()=>this.setLiveCoding(!0)),this.liveRailButton}labelLiveRail(){this.liveRailButton&&(this.liveRailButton.title=p("web_live_show"),this.liveRailButton.innerHTML=`${Dt}</> ${W(p("web_live_title"))}`)}labelLearnRail(){this.railButton&&(this.railButton.title=p("web_learn_show"),this.railButton.innerHTML=`${Dt}🎓 ${W(p("web_learn_title"))}`)}learnHost(){return{collapse:()=>this.setLearning(!1),showEditor:()=>{this.editorHidden&&this.setEditorHidden(!1)},showLiveCode:()=>{this.liveCoding||this.setLiveCoding(!0)},showJava:()=>this.openJavaColumn(),showFiles:()=>{this.shell.classList.contains("files-open")||this.setFilesOpen(!0)},reveal:e=>this.revealAreas(e),openExercise:(e,t)=>{const n=Dp(e);n!=="code"&&this.setEditorHidden(!1),n!=="struktogramm"&&(e.code==="java"?this.openJavaColumn():this.liveCoding||this.setLiveCoding(!0)),this.coding&&this.setCoding(!1),this.shell.classList.contains("files-open")&&this.setFilesOpen(!1),this.previewOpen&&this.setPreviewOpen(!1),n==="struktogramm"&&this.liveCoding&&this.setLiveCoding(!1),n==="code"&&!this.editorHidden&&this.setEditorHidden(!0);const a=this.tabs.findIndex(r=>r.exerciseId===e.id);if(a>=0){this.switchTab(a),this.setSimulating(!1);return}const s=this.makeTab();s.doc=t,s.exerciseId=e.id,s.selected=t.elements.length>0?[0]:void 0,this.isPristine(this.tab)?this.tabs[this.tabIndex]=s:this.tabs.push(s),this.tabIndex=this.tabs.indexOf(s),this.setSimulating(!1),this.render()},openJavaCode:e=>{this.openJavaColumn();const t=this.makeTab();t.doc=this.live.documentFromJavaText(e),this.isPristine(this.tab)?this.tabs[this.tabIndex]=t:this.tabs.push(t),this.tabIndex=this.tabs.indexOf(t),this.setSimulating(!1),this.live.adoptJava(t.doc,e),this.render()},openDiagram:(e,t)=>{this.setEditorHidden(!1),this.loadXML(x1(e),e.caption.trim()?`${e.caption.trim()}.visustruct`:t)},exerciseDocument:e=>{const t=this.tabs.find(a=>a.exerciseId===e);if(t)return t.doc;const n=this.tab;if(!(n.exerciseId||this.isPristine(n)))return n.exerciseId=e,ve.updateExercise(e,{xml:x1(n.doc)}),n.doc},subprogramsFor:e=>{const t=this.tabs.find(n=>n.exerciseId===e);return n=>this.findSubprogram(n,t)},stopSimulation:()=>this.setSimulating(!1),resetExercise:(e,t)=>{const n=this.tabs.find(a=>a.exerciseId===e);n&&(n.undoStack.push(n.doc),n.doc=t,n.redoStack=[],n.selected=[0],n===this.tab&&this.render())},preview:e=>{const t=oh(e,this.metrics,this.measure,0);if(!t)return;const n=t.svg.getAttribute("width");return n&&(t.svg.style.maxWidth=`${n}px`,t.svg.style.minWidth=`${Math.round(Number(n)*.6)}px`),t.svg.removeAttribute("width"),t.svg.removeAttribute("height"),t.svg}}}isPristine(e){return!e.dirty&&!e.fileName&&!e.exerciseId&&e.undoStack.length===0}rememberExercise(){const e=this.tab;e.exerciseId&&(clearTimeout(this.exerciseSaveTimer),this.exerciseSaveTimer=window.setTimeout(()=>{e.exerciseId&&ve.updateExercise(e.exerciseId,{xml:x1(e.doc)})},300))}paletteIcon(e){const t=Te("svg",{viewBox:"0 0 28 20",width:28,height:20,class:"icon"}),n={fill:"none",stroke:"currentColor","stroke-width":1.5};t.append(Te("rect",{x:1,y:1,width:26,height:18,...n}));const a=s=>t.append(Te("path",{d:s,...n}));switch(e){case"verzweigung":a("M1 1 L14 10 L27 1 M14 10 V19");break;case"fallauswahl":a("M1 1 L20 10 L27 1 M1 10 H27 M10 10 V19 M20 10 V19");break;case"forZaehlSchleife":case"whileSchleife":a("M7 7 H27 M7 7 V19");break;case"doUntilSchleife":a("M7 1 V13 H27");break;case"endlosschleife":a("M7 6 H27 M7 6 V14 H27");break;case"aufruf":a("M5 1 V19 M23 1 V19");break;case"aussprung":a("M6 1 L1 10 L6 19");break}return t}render(){var o,l;const e=ov(this.doc);e&&(this.doc=e,this.dirty=!0);const t={...this.doc,elements:this.doc.elements.map(_s)};this.layout=this.engine.layout(t),this.flat=vb(this.layout.elements),this.selected&&!st(this.doc,this.selected)&&(this.selected=void 0);const n=this.zoom,a=this.canvasHost;this.renderedHostSize={width:a.clientWidth,height:a.clientHeight};const s={width:Math.max(320,this.layout.canvasSize.width,Math.floor(a.clientWidth/n)),height:Math.max(240,this.layout.canvasSize.height+60,Math.floor(a.clientHeight/n))};this.pseudocodeBox.checked=!!this.doc.pseudocode,this.renderer.usesSimulationHighlight=this.simulating,this.renderer.fillsSelection=this.liveCoding,this.svg=this.renderer.render(this.layout,s,this.simulating?this.simPath:this.selected),this.svg.setAttribute("width",String(Math.floor(s.width*n))),this.svg.setAttribute("height",String(Math.floor(s.height*n))),this.overlay=Te("g",{class:"drop-overlay"}),this.svg.append(this.overlay);const r=(o=this.inline)!=null&&o.element.isConnected?this.canvasHost.querySelector(":scope > svg"):null;r?r.replaceWith(this.svg):this.canvasHost.replaceChildren(this.svg),this.refreshVarPalette(),this.blockMode&&((l=this.blocks)==null||l.render(this.doc,this.simulating?this.simPath:this.selected,this.simulating,n)),document.activeElement!==this.captionInput&&(this.captionInput.value=this.doc.caption),this.undoButton.disabled=this.simulating||this.undoStack.length===0,this.redoButton.disabled=this.simulating||this.redoStack.length===0,this.deleteButton.disabled=this.simulating||!this.selected||!va(this.doc,this.selected),this.captionInput.readOnly=this.simulating,this.codeButton.disabled=this.simulating,this.exportButton.disabled=this.simulating,this.coding&&this.code.refresh(),this.refreshLive(),this.refreshPreview(),this.renderTabs(),this.renderInspector(),this.updateStatus()}refreshLive(){this.liveCoding&&this.live.refresh(this.doc,this.simulating?this.simPath:this.selected,{owner:this.tab,subprograms:new Set(this.tabs.filter(e=>e!==this.tab).flatMap(e=>{var t;return((t=Tt(e.doc.caption))==null?void 0:t.name)??[]})),editable:!this.simulating,resolve:e=>this.findSubprogram(e)})}updateStatus(){this.rememberExercise(),document.title=`${this.dirty?"• ":""}${this.fileName} – VisuStruct Web`,this.statusLine.textContent=`${this.fileName}${this.dirty?` (${p("web_status_modified")})`:""}`}renderInspector(){const e=this.selected,t=e?st(this.doc,e):void 0,n=this.inspector;if(!e||!t){n.replaceChildren(de("h2",{},p("web_inspector_title")),de("p",{class:"hint"},p("inspector_no_selection")),de("p",{class:"hint"},p("web_inspector_help_drop")),de("p",{class:"hint"},p("web_inspector_help_inline")),de("p",{class:"hint"},p("web_inspector_help_files")));return}if(n.replaceChildren(de("h2",{},`${p("web_inspector_title")} · ${rs(t.type)}`)),t.type===S.anweisung){const a=Ss(t.textLines.join(` -`)),s=de("div",{class:"segmented",role:"group","aria-label":p("inspector_io_mode_title")});for(const[r,o]of[["statement",p("inspector_io_mode_statement")],["input",p("inspector_io_mode_input")],["output",p("inspector_io_mode_output")]]){const l=de("button",{"aria-pressed":String(r===a)},o);l.addEventListener("click",()=>{var c,d;const u=((c=st(this.doc,e))==null?void 0:c.textLines.join(` -`))??"";if(r===Ss(u))return;const m=rb(r,u);this.commit(pn(this.doc,e,m.split(` -`))),(d=this.inspector.querySelector("textarea"))==null||d.focus()}),s.append(l)}n.append(s,de("p",{class:"hint mono-hint"},OY[a]))}if(t.type===S.forSchleife){const a=de("div",{class:"for-fields"}),s=Hr(t.textLines);[[p("inspector_for_init"),p("inspector_for_placeholder_init")],[p("inspector_for_condition"),p("inspector_for_placeholder_condition")],[p("inspector_for_step"),p("inspector_for_placeholder_step")]].forEach(([o,l],u)=>{const m=de("input",{placeholder:l,spellcheck:"false",autocomplete:"off"});m.value=s[u],this.liveEdit(m,c=>{const d=st(this.doc,e);if(!d)return;const h=Hr(d.textLines);return h[u]=Hu(c),pn(this.doc,e,h)}),a.append(de("label",{},o,m))}),n.append(a)}else if(t.type!==S.leerElement){const a=PY[t.type];a&&n.append(de("p",{class:"hint mono-hint"},a));const s=xr(t),r=de("textarea",{rows:"3",spellcheck:"false"});r.value=hh(t),r.placeholder=ph(t),this.liveEdit(r,l=>gh(this.doc,e,l));const o=de("div",{class:"editor-row"});s&&o.append(de("span",{class:"fixed-prefix"},s)),o.append(r),n.append(o)}if(Mo(t).length>0){const a=de("button",{class:"inspector-rename"},p("web_rename_menu"));a.addEventListener("click",()=>this.renameVariable(e)),n.append(a)}this.renderCaseControls(e)}renameVariable(e){const t=st(this.doc,e);if(!t||this.simulating)return;const n=this.liveCoding?this.live.editableCode:void 0,a=Mo(t),s=n!==void 0?Nu([n]):oY(this.doc),r=[...a,...s.filter(l=>!a.includes(l))];if(r.length===0)return;const o=n!==void 0?ni(n).map(l=>l.name):sY(this.doc);j3({names:r,existing:o,count:l=>n!==void 0?ra(n,l).length:rY(this.doc,l)}).then(l=>{if(l)if(n!==void 0)this.live.renameInCode(l.from,l.to);else{const u=aY(this.doc,l.from,l.to);u.count>0&&this.commit(u.doc)}})}onCanvasContextMenu(e){if(this.simulating||this.inline)return;const t=this.svgPoint(e.clientX,e.clientY),n=t&&this.nodeAt(t.x,t.y),a=n&&st(this.doc,n.idPath);!n||!a||Mo(a).length===0||(e.preventDefault(),this.selected=n.idPath,this.render(),C3(e.clientX,e.clientY,p("web_rename_menu"),()=>this.renameVariable(n.idPath)))}renderCaseControls(e){var l;const t=ib(e,this.doc);if(!t)return;const n=((l=st(this.doc,t))==null?void 0:l.cases)??[],a=ab(e,t),s=de("div",{class:"case-controls"});if(a!==void 0&&n[a]){const u=de("input",{placeholder:p("web_case_name_placeholder"),spellcheck:"false",autocomplete:"off"});u.value=n[a].name,this.liveEdit(u,m=>m.trim()===""?void 0:Ru(this.doc,t,a,m)),s.append(de("label",{},p("web_case_name"),u))}const r=de("button",{},`+ ${p("inspector_add_case")}`);r.addEventListener("click",()=>{const u=tb(this.doc,t);this.commit(u.doc,u.path)});const o=de("button",{class:"danger-text"},`- ${p("inspector_remove_case")}`);o.disabled=n.length<=2||a===void 0,o.addEventListener("click",()=>{if(a===void 0)return;const u=nb(this.doc,t,a);this.commit(u.doc,u.path)}),s.append(de("div",{class:"group"},r,o)),this.inspector.append(s)}liveEdit(e,t){let n;e.addEventListener("focus",()=>n=this.doc),e.addEventListener("input",()=>{const a=y0(e.value);if(a!==e.value){const r=e.selectionStart;e.value=a,e.setSelectionRange(r,r)}const s=t(a);s&&(this.doc=s,this.dirty=!0,this.renderDiagramOnly())}),e.addEventListener("blur",()=>{n&&n!==this.doc&&this.pushUndo(n),n=void 0,this.renderDiagramOnly()})}startInlineEdit(e,t){var u,m;if(this.simulating||this.blockMode)return;(u=this.inline)==null||u.finish(!0);const n=this.flat.find(c=>Mn(c.idPath,e)),a=st(this.doc,e);if(!n||!a||a.type===S.leerElement)return;const s=this.metrics.lineHeight,r={host:this.canvasHost,zoom:this.zoom,fontSize:this.metrics.fontSize,lineHeight:s},o=c=>{this.inline=zb({...r,...c,onDone:d=>{var x,v;this.inline=void 0;const h=d&&c.apply(d),g=st(this.doc,e),f=h&&st(h,e);h&&JSON.stringify([g==null?void 0:g.textLines,(x=g==null?void 0:g.cases)==null?void 0:x.map(w=>w.name)])!==JSON.stringify([f==null?void 0:f.textLines,(v=f==null?void 0:f.cases)==null?void 0:v.map(w=>w.name)])?this.commit(h,e):this.render()}})},l=t?wb(n,t.x,t.y,s):void 0;if(l!==void 0&&((m=a.cases)!=null&&m[l]))return o({rect:yb(n,l,s),fields:[{value:a.cases[l].name,placeholder:p("web_case_name_placeholder"),label:p("web_case_name")}],center:!0,apply:([c])=>c.trim()===""?void 0:Ru(this.doc,e,l,c.trim())});if(a.type===S.forSchleife){const c=Hr(a.textLines),d=[[p("inspector_for_init"),p("inspector_for_placeholder_init")],[p("inspector_for_condition"),p("inspector_for_placeholder_condition")],[p("inspector_for_step"),p("inspector_for_placeholder_step")]];return o({rect:Ku(n,s),prefix:"for",fields:d.map(([h,g],f)=>({value:c[f],placeholder:g,label:h})),center:!1,apply:h=>pn(this.doc,e,h.map(g=>Hu(g)))})}o({rect:Ku(n,s),prefix:xr(a),fields:[{value:hh(a),placeholder:ph(a),label:rs(a.type)}],center:a.type===S.anweisung||a.type===S.aufruf||a.type===S.aussprung,apply:([c])=>gh(this.doc,e,c)})}setBlockMode(e,t){var a;this.blockMode=e,t&&Ze(No,e?"1":"0"),Vm(e)&&((a=this.learn)==null||a.render()),this.root.classList.toggle("block-mode",e),this.canvasHost.hidden=e,this.blocks&&(this.blocks.element.hidden=!e);const n=p(e?"web_visubot_blocks_back":"web_visubot_blocks");this.blocksButton.innerHTML=`${W(n)}`,this.blocksButton.setAttribute("aria-pressed",String(e)),this.blocksButton.dataset.tip=p(e?"web_visubot_blocks_back_title":"web_visubot_blocks_title"),t&&this.render()}renderDiagramOnly(){const e=this.selected,t=this.inspector,n=document.createElement("aside");this.inspector=n,this.render(),this.inspector=t,this.selected=e}setSimulating(e){var t;e!==this.simulating&&(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),this.endDrag(),this.simulating=e,this.root.classList.toggle("simulating",e),e&&requestAnimationFrame(()=>this.fixSimSplit()),this.simButton.setAttribute("aria-pressed",String(e)),this.simButton.innerHTML=`${e?me.stop:me.play}${W(p(e?"web_simulation_end":"web_simulation_start"))}`,this.simButton.dataset.tip=p(e?"web_simulation_end_title":"web_simulation_start_title"),this.simButton.setAttribute("aria-label",p(e?"web_simulation_end":"web_simulation_start")),e?(this.setCoding(!1),this.setEditorHidden(!1),(t=this.visuBot)==null||t.setWorlds(Zh(this.tab.exerciseId)),this.visuBot&&!this.previewOpen&&(this.setPreviewOpen(!0),this.previewOpenedForSim=!0),this.placeSimulationRobot(),this.sim.begin()):(this.sim.end(),this.placeSimulationRobot(),this.previewOpenedForSim&&this.setPreviewOpen(!1),this.simPath=void 0,!this.selected&&this.doc.elements.length>0&&(this.selected=[0])),this.render())}runRobot(e){return!this.visuBot||!/\bmbot\s*\./u.test(JSON.stringify(this.tabs.map(t=>t.doc)))?!1:(this.simulating?this.sim.reset():this.setSimulating(!0),e?this.sim.step():this.sim.play(),!0)}setCoding(e){e!==this.coding&&(e&&(this.setLiveCoding(!1),this.setEditorHidden(!1)),this.coding=e,this.root.classList.toggle("coding",e),this.codeButton.setAttribute("aria-pressed",String(e)),e?this.code.refresh():this.code.close(),this.renderDiagramOnly())}openJavaColumn(){if(this.live.language!=="java"){const e="[data-action=live-language]",t=this.topbar.querySelector(e)??this.root.querySelector(e);t?(t.value="java",t.dispatchEvent(new Event("change"))):this.live.setTarget("java")}this.liveCoding||this.setLiveCoding(!0)}setCodeMain(e){this.codeMain=e,Ze(Th,e?"1":"0"),e&&!this.liveCoding&&this.setLiveCoding(!0),e&&this.editorHidden&&this.setEditorHidden(!1),this.shell.classList.toggle("code-main",e),this.labelSwap(),this.canvasHost&&this.render()}labelSwap(){const e=p(this.codeMain?"web_swap_to_editor":"web_swap_to_code");for(const t of this.shell.querySelectorAll("[data-action=swap-main]"))t.dataset.tip=e,t.setAttribute("aria-label",e),t.setAttribute("aria-pressed",String(this.codeMain))}setupPreview(){const e=o=>{this.previewOpenedForSim=!1,this.setPreviewOpen(o)};this.preview.onHide=()=>e(!1),this.preview.rail.addEventListener("click",()=>e(!0)),this.preview.onToggleStacked=()=>{this.previewStacked=!this.previewStacked,Ze(jh,this.previewStacked?"1":"0"),this.previewStacked&&!this.liveCoding&&this.setLiveCoding(!0),this.placePreview()},this.previewStacked=Me(jh)==="1";const t=Me(Sh);this.previewOpen=t===null||t==="1";const n=this.preview.heightBar,a=this.preview.element,s=(o,l)=>{var c;const u=Math.max(160,(((c=a.parentElement)==null?void 0:c.getBoundingClientRect().height)??600)-120),m=o===void 0?void 0:Math.round(Math.max(160,Math.min(o,u)));a.style.height=m===void 0?"":`${m}px`,l&&Ze(Ph,m===void 0?"":String(m))},r=Number(Me(Ph));r>0&&(a.style.height=`${r}px`),n.addEventListener("pointerdown",o=>{if(o.button!==0)return;o.preventDefault(),n.setPointerCapture(o.pointerId);const l=o.clientY,u=a.getBoundingClientRect().height;this.shell.classList.add("resizing");const m=d=>s(u-(d.clientY-l),!1),c=d=>{n.removeEventListener("pointermove",m),n.removeEventListener("pointerup",c),n.removeEventListener("pointercancel",c),this.shell.classList.remove("resizing"),s(u-(d.clientY-l),!0)};n.addEventListener("pointermove",m),n.addEventListener("pointerup",c),n.addEventListener("pointercancel",c)}),n.addEventListener("dblclick",()=>s(void 0,!0)),n.addEventListener("keydown",o=>{const l=o.key==="ArrowUp"?20:o.key==="ArrowDown"?-20:0;l&&(o.preventDefault(),s(a.getBoundingClientRect().height+l,!0))}),this.placePreview()}setPreviewOpen(e){this.previewOpen=e,Ze(Sh,e?"1":"0"),e||this.preview.pause(),this.placePreview(),this.placeSimulationRobot(),this.canvasHost&&this.render()}placeSimulationRobot(){const e=this.visuBot,t=!!e&&this.simulating&&this.previewOpen;this.preview.hostSimulation(t?e.viewport:void 0),t||e==null||e.restoreViewport()}placePreview(){const e=this.previewStacked&&this.liveCoding;this.preview.setStacked(this.previewStacked),this.shell.classList.toggle("preview",this.previewOpen),this.shell.classList.toggle("preview-stacked",e),e?this.preview.element.parentElement!==this.live.element&&this.live.element.append(this.preview.element):this.preview.element.previousElementSibling!==this.previewResizer&&this.previewResizer.after(this.preview.element)}refreshPreview(){!this.previewOpen||!jn()||this.preview.update(this.doc,e=>this.findSubprogram(e),Zh(this.tab.exerciseId),this.tab.exerciseId??"",this.tabs.map(e=>e.doc))}setLiveCoding(e){e&&this.coding&&this.setCoding(!1),this.liveCoding=e,this.shell.classList.toggle("live",e),this.placePreview(),e||this.live.hideConsole(),Ze(kh,e?"1":"0"),this.keepAColumnOpen(),this.canvasHost&&this.render()}simResizer(){const e=de("div",{class:"sim-resizer",role:"separator","aria-orientation":"vertical",tabindex:"0",title:p("web_visuobject_resize")}),t=this.root,n=o=>{const l=t.getBoundingClientRect().width;return Math.round(Math.max(120,l>0?Math.min(o,l-240):o))},a=(o,l)=>{l&&Ze(Dh,o===void 0?"":String(n(o))),o===void 0?this.fixSimSplit():t.style.setProperty("--sim-work",`${n(o)}px`)},s=()=>t.getBoundingClientRect().width-this.sim.element.getBoundingClientRect().width,r=()=>getComputedStyle(this.shell).direction==="rtl"?-1:1;return e.addEventListener("pointerdown",o=>{if(o.button!==0)return;o.preventDefault(),e.setPointerCapture(o.pointerId);const l=o.clientX,u=s(),m=r();this.shell.classList.add("resizing");const c=h=>a(u+m*(h.clientX-l),!1),d=h=>{e.removeEventListener("pointermove",c),e.removeEventListener("pointerup",d),e.removeEventListener("pointercancel",d),this.shell.classList.remove("resizing"),a(u+m*(h.clientX-l),!0)};e.addEventListener("pointermove",c),e.addEventListener("pointerup",d),e.addEventListener("pointercancel",d)}),e.addEventListener("dblclick",()=>a(void 0,!0)),e.addEventListener("keydown",o=>{const l=o.key==="ArrowRight"?20:o.key==="ArrowLeft"?-20:0;l&&(o.preventDefault(),a(s()+r()*l,!0))}),e}inspectorResizer(){const e=p("web_inspector_resize"),t=de("div",{class:"insp-resizer",role:"separator","aria-orientation":"horizontal",tabindex:"0",title:e,"aria-label":e}),n=this.root,a=l=>{const u=n.getBoundingClientRect().height;return Math.round(Math.max(48,u>0?Math.min(l,u-160):l))},s=(l,u)=>{l===void 0?(n.classList.remove("insp-sized"),n.style.removeProperty("--insp-height")):(n.classList.add("insp-sized"),n.style.setProperty("--insp-height",`${a(l)}px`)),u&&Ze(Bh,l===void 0?"":String(a(l)))},r=()=>this.inspector.getBoundingClientRect().height;t.addEventListener("pointerdown",l=>{if(l.button!==0)return;l.preventDefault(),t.setPointerCapture(l.pointerId);const u=l.clientY,m=r();this.shell.classList.add("resizing");const c=h=>s(m-(h.clientY-u),!1),d=h=>{t.removeEventListener("pointermove",c),t.removeEventListener("pointerup",d),t.removeEventListener("pointercancel",d),this.shell.classList.remove("resizing"),s(m-(h.clientY-u),!0)};t.addEventListener("pointermove",c),t.addEventListener("pointerup",d),t.addEventListener("pointercancel",d)}),t.addEventListener("dblclick",()=>s(void 0,!0)),t.addEventListener("keydown",l=>{const u=l.key==="ArrowUp"?20:l.key==="ArrowDown"?-20:0;u&&(l.preventDefault(),s(r()+u,!0))});const o=Number(Me(Bh));return o>0&&s(o,!1),t}fixSimSplit(){const e=this.root,t=e.getBoundingClientRect().width,n=Number(Me(Dh)),a=e.classList.contains("visubot-mode")?Math.min(440,t*.45):320,s=n>0?n:t-a;e.style.setProperty("--sim-work",`${Math.round(Math.max(120,t>0?Math.min(s,t-240):s))}px`)}learnResizer(){return this.panelResizer(this.learn.element,"learn-resizer",Ih,p("web_learn_resize"),1)}liveResizer(){return this.panelResizer(this.live.element,"live-resizer",LY,p("web_live_resize"),-1,()=>this.root)}yieldingPanels(){return[{panel:this.preview.element,key:Ch},{panel:this.learn.element,key:Ih},{panel:this.files.element,key:zh}].filter(({panel:e})=>e.offsetParent!==null&&e.parentElement===this.live.element.parentElement)}panelResizer(e,t,n,a,s,r){const o=de("div",{class:t,role:"separator","aria-orientation":"vertical",tabindex:"0",title:a}),l=d=>{var b;const h=((b=e.parentElement)==null?void 0:b.getBoundingClientRect().width)??0;if(h===0)return RY(d);const g=[this.files.element,this.learn.element,this.live.element,this.preview.element].filter(x=>x!==e&&x.offsetParent!==null&&getComputedStyle(x).flexGrow==="0").reduce((x,v)=>x+v.getBoundingClientRect().width,0),f=parseFloat(getComputedStyle(e).minWidth)||200;return Math.round(Math.max(f,Math.min(d,h-g-240)))},u=(d,h)=>{e.style.width=d===void 0?"":`${l(d)}px`,h&&Ze(n,d===void 0?"":String(l(d)))},m=Number(Me(n));m>0&&u(m,!1);const c=()=>{const d=typeof s=="function"?s():s;return getComputedStyle(this.shell).direction==="rtl"?-d:d};return o.addEventListener("pointerdown",d=>{if(d.button!==0)return;d.preventDefault(),o.setPointerCapture(d.pointerId);const h=d.clientX,g=e.getBoundingClientRect().width,f=c();this.shell.classList.add("resizing");const b=r?this.pushResize(e,n,r(),g):void 0,x=(z,I)=>(b??u)(g+f*(z.clientX-h),I),v=z=>x(z,!1),w=z=>{o.removeEventListener("pointermove",v),o.removeEventListener("pointerup",w),o.removeEventListener("pointercancel",w),this.shell.classList.remove("resizing"),x(z,!0)};o.addEventListener("pointermove",v),o.addEventListener("pointerup",w),o.addEventListener("pointercancel",w)}),o.addEventListener("dblclick",()=>u(void 0,!0)),o.addEventListener("keydown",d=>{const h=d.key==="ArrowRight"?20:d.key==="ArrowLeft"?-20:0;h&&(d.preventDefault(),u(e.getBoundingClientRect().width+c()*h,!0))}),o}pushResize(e,t,n,a){const s=(m,c)=>Math.max(parseFloat(getComputedStyle(m).minWidth)||0,c),r=s(e,200),o=Math.max(0,n.getBoundingClientRect().width-s(n,240)),l=this.yieldingPanels().map(m=>({...m,start:m.panel.getBoundingClientRect().width,min:s(m.panel,200),changed:!1})),u=l.reduce((m,c)=>m+Math.max(0,c.start-c.min),0);return(m,c)=>{const d=Math.round(Math.max(r,Math.min(m,a+o+u)));let h=Math.max(0,d-a-o,r-m);for(const g of l){const f=Math.min(h,Math.max(0,g.start-g.min));h-=f,g.changed||(g.changed=f>0),g.changed&&(g.panel.style.width=`${Math.round(g.start-f)}px`,c&&Ze(g.key,String(Math.round(g.start-f))))}e.style.width=`${d}px`,c&&Ze(t,String(d))}}findSubprogram(e,t=this.tab){const n=Ro(e);if(n==="")return;const a=this.tabs.find(s=>s!==t&&(Ro(s.doc.caption)===n||Ro(hn(s))===n));return a==null?void 0:a.doc}scrollToSimPath(){var o;const e=this.simPath;if(!this.simulating||!e)return;if(this.blockMode)return(o=this.blocks)==null?void 0:o.scrollTo(e);const t=this.flat.find(l=>Mn(l.idPath,e));if(!t)return;const n=this.canvasHost,a=this.zoom,s={x:t.frame.x*a,y:t.frame.y*a,width:t.frame.width*a,height:t.frame.height*a},r=24;s.yn.scrollTop+n.clientHeight&&(n.scrollTop=s.y-r),(s.xn.scrollLeft+n.clientWidth-r)&&(n.scrollLeft=Math.max(0,s.x-r))}commit(e,t){this.codeTyping=void 0,this.pushUndo(this.doc),this.doc=e,this.dirty=!0,t&&(this.selected=t),this.render()}commitFromCode(e,t){const n=Date.now(),a=this.codeTyping;!a||a.tab!==this.tab||n-a.last>1e3||n-a.start>5e3?(this.pushUndo(this.doc),this.codeTyping={tab:this.tab,start:n,last:n}):a.last=n,this.doc=e,this.dirty=!0,this.selected=Gr(t??this.selected,e),this.render()}pushUndo(e){for(this.undoStack.push(e);this.undoStack.length>NY;)this.undoStack.shift();this.tab.redoStack=[]}undo(){const e=this.undoStack.pop();e&&(this.codeTyping=void 0,this.redoStack.push(this.doc),this.doc=e,this.dirty=!0,this.selected=Gr(this.selected,this.doc),this.render())}redo(){const e=this.redoStack.pop();e&&(this.codeTyping=void 0,this.undoStack.push(this.doc),this.doc=e,this.dirty=!0,this.selected=Gr(this.selected,this.doc),this.render())}resetHistory(){this.setSimulating(!1),this.tab.undoStack=[],this.tab.redoStack=[],this.selected=void 0,this.dirty=!1,this.render()}makeTab(){return{doc:Y3(),serial:this.nextTabSerial++,dirty:!1,undoStack:[],redoStack:[],selected:[0]}}addTab(e=this.makeTab()){this.setSimulating(!1),this.tabs.push(e),this.tabIndex=this.tabs.length-1,this.render()}switchTab(e){e===this.tabIndex||!this.tabs[e]||(document.activeElement instanceof HTMLElement&&document.activeElement.blur(),this.setSimulating(!1),this.tabIndex=e,this.render())}async closeTab(e){if(this.tabs.length<=1)return;const t=this.tabs[e];t.dirty&&!t.exerciseId&&(this.switchTab(e),!await this.confirmDiscard())||(this.setSimulating(!1),this.tabs.splice(e,1),(this.tabIndex>e||this.tabIndex>=this.tabs.length)&&(this.tabIndex=Math.max(0,this.tabIndex-1)),this.render())}renderTabs(){const e=this.tabStrip;e.replaceChildren(),this.tabs.forEach((n,a)=>{const s=document.createElement("div");s.className="tab"+(a===this.tabIndex?" active":""),s.setAttribute("role","tab"),s.setAttribute("aria-selected",String(a===this.tabIndex));const r=document.createElement("button");r.className="tab-label",r.textContent=hn(n),r.title=n.fileName??hn(n),r.addEventListener("click",()=>this.switchTab(a)),s.append(r);const o=n.dirty&&!n.exerciseId;if(this.tabs.length>1){const l=document.createElement("button");l.className="tab-close"+(o?" unsaved":""),l.innerHTML='✕',l.title=p("a11y_close_tab"),l.setAttribute("aria-label",`${p("a11y_close_tab")}: ${hn(n)}`),l.addEventListener("click",()=>this.closeTab(a)),s.append(l)}else o&&s.append(Object.assign(document.createElement("span"),{className:"tab-close unsaved",innerHTML:'●'}));o&&r.setAttribute("aria-label",`${hn(n)} (${p("web_status_modified")})`),e.append(s)});const t=document.createElement("button");t.className="tab-add",t.textContent="+",t.title=p("a11y_new_diagram_tab"),t.setAttribute("aria-label",p("a11y_new_diagram_tab")),t.addEventListener("click",()=>this.addTab()),e.append(t)}copySelection(){const e=this.selected&&st(this.doc,this.selected);return e?(this.clipboard=structuredClone(e),this.showMessage(p("web_copied")),!0):!1}cutSelection(){this.selected&&va(this.doc,this.selected)&&this.copySelection()&&this.deleteSelection()}paste(){this.clipboard&&this.insertNearSelection(structuredClone(this.clipboard))}deleteSelection(){!this.selected||!va(this.doc,this.selected)||(this.commit(L1(this.doc,this.selected)),this.selected=void 0,this.render())}expressionItem(e,t,n,a,s=t){const r=document.createElement("li");r.className=`palette-item palette-sensor palette-expr palette-${a}`,r.dataset.botColor=a==="op"?"operator":"variable",r.title=t;const o=document.createElement("code");return o.textContent=s,n==="bool"&&o.classList.add("sensor-bool"),r.append(document.createTextNode(e),o),r.addEventListener("pointerdown",l=>this.beginDrag(l,{kind:"sensor",text:t,label:s})),r.tabIndex=0,r.addEventListener("keydown",l=>{(l.key==="Enter"||l.key===" ")&&this.insertSensor(t)}),r}refreshVarPalette(){if(!this.varList)return;const e=Ri(this.doc),t=e.join(",");t!==this.shownVars&&(this.shownVars=t,this.varList.querySelectorAll(".palette-var").forEach(n=>n.remove()),this.varList.append(...e.map(n=>this.expressionItem(n,n,"round","var"))))}dropOnCondition(e,t){const n=bt(this.doc,t);if(n.type===S.anweisung){/^mbot\./.test(e)&&this.insertSensor(e,t);return}const a=n.textLines.join(" ").trim(),s=!a||a==="true"?e:e==="true && true"?`${a} && true`:e==="true || true"?`${a} || true`:e==="!true"?`!(${a})`:e;this.commit(pn(this.doc,t,[s]),t)}insertSensor(e,t=this.selected){if(!t||gn(this.doc,t))return;const n=bt(this.doc,t);if(!ss.includes(n.type))return;const a=(n.textLines??[]).join(" ").trim();this.commit(pn(this.doc,t,[a?`${a} ${e}`:e]),t);const s=this.inspector.querySelector("textarea");s&&(s.focus(),s.setSelectionRange(s.value.length,s.value.length))}insertNearSelection(e){const t=this.selected;if(t&&gn(this.doc,t))return this.commit(Wn(this.doc,t,e),t);if(t){const n=[...t.slice(0,-1),t[t.length-1]+1];return this.commit(Ho(this.doc,e,t),n)}if(this.doc.elements.length===1&&gn(this.doc,[0]))return this.commit(Wn(this.doc,[0],e),[0]);this.commit(wi(this.doc,[],this.doc.elements.length,e),[this.doc.elements.length])}confirmDiscard(){return this.dirty?new Promise(e=>{const t=document.createElement("div");t.className="confirm",t.innerHTML=`
    -

    ${W(p("close_unsaved_title"))}

    -

    ${W(p("web_discard_message_fmt",this.fileName))}

    -
    -
    `,t.addEventListener("click",n=>{const a=n.target.dataset.r;a===void 0&&n.target!==t||(t.remove(),e(a==="1"))}),document.body.append(t),t.querySelector('[data-r="0"]').focus()}):Promise.resolve(!0)}showMessage(e){this.statusLine.textContent=e,this.statusLine.classList.add("message"),setTimeout(()=>{this.statusLine.classList.remove("message"),this.updateStatus()},6e3)}loadXML(e,t,n){let a;try{a=z0(e)}catch(s){this.showMessage(p("web_err_unreadable_fmt",t,s.message));return}(this.dirty||this.tab.fileName||this.tab.exerciseId)&&this.addTab(),this.doc=a,this.tab.fileName=t.replace(/\.(strk|xml)$/i,".visustruct"),this.fileHandle=n&&/\.visustruct$/i.test(t)?n:void 0,this.resetHistory()}async open(e){const t=window;if(!t.showOpenFilePicker||this.inViewer)return e.click();try{const[n]=await t.showOpenFilePicker({types:dh()}),a=await n.getFile();this.loadXML(await a.text(),a.name,n)}catch{}}async save(){var n,a;if(!this.downloads&&await this.saveToWorkspace())return;const e=new Blob([x1(this.doc)],{type:"application/xml"}),t=window;try{if(this.downloads){const s=this.fileName+".txt";await this.downloads.save({filename:s,data:e}),(n=this.tab).fileName??(n.fileName=this.fileName),this.dirty=!1,this.render(),this.showMessage(p("web_saved_txt_fmt",s));return}if(!this.fileHandle&&t.showSaveFilePicker&&!this.inViewer&&(this.fileHandle=await t.showSaveFilePicker({suggestedName:this.fileName,types:dh()}),this.tab.fileName=this.fileHandle.name),this.fileHandle){const s=await this.fileHandle.createWritable();await s.write(e),await s.close()}else{const s=document.createElement("a");s.href=URL.createObjectURL(e),s.download=this.fileName,s.click(),setTimeout(()=>URL.revokeObjectURL(s.href),1e3)}(a=this.tab).fileName??(a.fileName=this.fileName),this.dirty=!1,this.render()}catch{}}async exportDiagram(e){if(e==="visustruct"){const s=rh(this.doc,hn(this.tab));return this.offerDownload(`${s}.visustruct`,new Blob([x1(this.doc)],{type:"application/xml"}))}const t=oh(this.doc,this.metrics,this.measure);if(!t){this.showMessage(p(e==="print"?"err_diagram_too_small_print":"err_diagram_too_small_export"));return}const n=rh(this.doc,hn(this.tab)),a=Math.max(2,window.devicePixelRatio||1)*this.zoom;try{switch(e){case"svg":{t.svg.setAttribute("width",String(t.width*this.zoom)),t.svg.setAttribute("height",String(t.height*this.zoom)),await this.offerDownload(`${n}.svg`,new Blob([O3(t)],{type:"image/svg+xml"}));break}case"png":{const s=await lh(t,a);await this.offerDownload(`${n}.png`,await wY(s));break}case"pdf":{const s=await lh(t,Math.max(a,3)),r=await IY(kY(s),s.width,s.height,t,P3(this.doc));await this.offerDownload(`${n}.pdf`,r);break}case"print":this.printDiagram(t);break}}catch(s){this.showMessage(p("web_export_failed_fmt",s instanceof Error?s.message:String(s)))}}printDiagram(e){const t=document.createElement("div");t.className="print-sheet",e.svg.removeAttribute("width"),e.svg.removeAttribute("height"),t.append(e.svg),document.body.append(t);const n=()=>t.remove();window.addEventListener("afterprint",n,{once:!0});try{window.print()}catch{n(),this.showMessage(`${p("err_print_unavailable")} ${p("web_print_hint")}`);return}setTimeout(n,6e4)}async offerDownload(e,t){try{if(this.downloads){try{await this.downloads.save({filename:e,data:t}),this.showMessage(p("web_saved_fmt",e))}catch{await this.downloads.save({filename:e+".txt",data:t}),this.showMessage(p("web_saved_txt_fmt",e+".txt"))}return}const n=document.createElement("a");n.href=URL.createObjectURL(t),n.download=e,n.click(),setTimeout(()=>URL.revokeObjectURL(n.href),1e3)}catch{this.showMessage(p("err_save_failed_fmt",e))}}setupFileDrop(){window.addEventListener("dragover",e=>{var t;(t=e.dataTransfer)!=null&&t.types.includes("Files")&&(e.preventDefault(),this.root.classList.add("file-drag"))}),window.addEventListener("dragleave",e=>{e.relatedTarget===null&&this.root.classList.remove("file-drag")}),window.addEventListener("drop",async e=>{var n;this.root.classList.remove("file-drag");const t=(n=e.dataTransfer)==null?void 0:n.files[0];t&&(e.preventDefault(),this.loadXML(await t.text(),t.name))})}onKeyDown(e){var a,s;const t=e.metaKey||e.ctrlKey,n=e.target instanceof HTMLTextAreaElement||e.target instanceof HTMLInputElement||((a=e.target)==null?void 0:a.isContentEditable)===!0;if(t&&e.key.toLowerCase()==="s")e.preventDefault(),this.save();else if(t&&e.key.toLowerCase()==="p")e.preventDefault(),this.simulating||this.exportDiagram("print");else if(t&&e.key.toLowerCase()==="o")e.preventDefault(),this.open(this.root.querySelector("input[type=file]"));else{if(n||this.simulating)return;t&&e.key.toLowerCase()==="z"?(e.preventDefault(),e.shiftKey?this.redo():this.undo()):t&&e.key.toLowerCase()==="c"&&!((s=getSelection())!=null&&s.toString())?this.copySelection()&&e.preventDefault():t&&e.key.toLowerCase()==="x"?(e.preventDefault(),this.cutSelection()):t&&e.key.toLowerCase()==="v"?(e.preventDefault(),this.paste()):t&&e.key.toLowerCase()==="y"?(e.preventDefault(),this.redo()):e.key==="Enter"&&this.selected&&!this.blockMode&&!t?(e.preventDefault(),this.startInlineEdit(this.selected)):e.key==="F2"&&this.selected?(e.preventDefault(),this.renameVariable(this.selected)):e.key==="Delete"||e.key==="Backspace"?(e.preventDefault(),this.deleteSelection()):e.key==="Escape"&&(this.drag?this.endDrag():(this.selected=void 0,this.render()))}}svgPoint(e,t){if(!this.svg)return;const n=this.svg.getBoundingClientRect(),a=e-n.left,s=t-n.top;if(!(a<0||s<0||a>n.width||s>n.height))return{x:a/this.zoom,y:s/this.zoom}}nodeAt(e,t){for(let n=this.flat.length-1;n>=0;n--){const a=this.flat[n].frame;if(e>=a.x&&e<=a.x+a.width&&t>=a.y&&t<=a.y+a.height)return this.flat[n]}}onCanvasPointerDown(e){var u;if(this.simulating)return;const t=this.svgPoint(e.clientX,e.clientY);if(!t)return;(u=this.inline)==null||u.finish(!0),document.activeElement instanceof HTMLElement&&document.activeElement.blur();const n=this.nodeAt(t.x,t.y),a=n&&this.selected&&Mn(n.idPath,this.selected),s=n?n.idPath.join(","):"",r=Date.now(),o=this.lastCanvasTap,l=!!n&&e.button===0&&s===o.spot&&r-o.time<500&&Math.hypot(e.clientX-o.x,e.clientY-o.y)<24;if(this.lastCanvasTap={spot:s,time:l?0:r,x:e.clientX,y:e.clientY},this.selected=n==null?void 0:n.idPath,this.render(),l&&n){e.preventDefault(),this.pendingInline={pointerId:e.pointerId,path:n.idPath,point:t};return}n&&e.pointerType!=="touch"&&!(gn(this.doc,n.idPath)&&!a)&&this.beginDrag(e,{kind:"move",path:n.idPath})}beginDrag(e,t){e.button!==0||this.simulating||(e.preventDefault(),this.drag={source:t,pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,active:!1})}onPointerMove(e){var n;const t=this.drag;if(!(!t||e.pointerId!==t.pointerId)){if(!t.active){if(Math.hypot(e.clientX-t.startX,e.clientY-t.startY)<5)return;t.active=!0,t.ghost=this.makeGhost(t.source),document.body.append(t.ghost),this.root.classList.add("dragging"),this.blockMode&&t.source.kind==="move"&&((n=this.blocks)==null||n.setMoving(!0))}if(t.ghost.style.transform=`translate(${e.clientX+12}px, ${e.clientY+12}px)`,this.autoScroll(e.clientX,e.clientY),this.blockMode)t.target=this.blockDropAt(e.clientX,e.clientY,t.source);else{const a=this.svgPoint(e.clientX,e.clientY);t.target=a?this.dropTargetAt(a.x,a.y,t.source):void 0}this.showDropTarget(t.target)}}onPointerUp(e){const t=this.pendingInline;if(t&&t.pointerId===e.pointerId){this.pendingInline=void 0,this.startInlineEdit(t.path,t.point);return}const n=this.drag;!n||e.pointerId!==n.pointerId||(n.active&&n.target?this.applyDrop(n.source,n.target):!n.active&&n.source.kind==="palette"?this.insertNearSelection(n.source.make()):!n.active&&n.source.kind==="sensor"&&this.insertSensor(n.source.text),this.endDrag())}endDrag(){var e,t,n;(t=(e=this.drag)==null?void 0:e.ghost)==null||t.remove(),this.drag=void 0,this.root.classList.remove("dragging"),(n=this.blocks)==null||n.setMoving(!1),this.showDropTarget(void 0)}blockDropAt(e,t,n){var s;const a=(s=this.blocks)==null?void 0:s.dropAt(e,t,{moving:n.kind==="move"?n.path:void 0,sensor:n.kind==="sensor"?r=>{const o=bt(this.doc,r).type;return(/^mbot\./.test(n.text)||o!==S.anweisung)&&ss.includes(o)}:void 0});if(a)return{...a,frame:{x:0,y:0,width:0,height:0}}}makeGhost(e){const t=document.createElement("div");if(t.className="drag-ghost",e.kind==="palette"||e.kind==="sensor"){if(t.textContent=e.label,e.kind==="sensor"&&this.blockMode){const n=/^mbot\./.test(e.text),a=n?/^mbot\.is/.test(e.text):k4(e.text);t.classList.add("drag-sensor",...a?["sensor-bool"]:[],...n?[]:[/^[A-Za-z_]\w*$/.test(e.text)?"drag-var":"drag-op"])}e.kind==="palette"&&this.blockMode&&(t.classList.add("drag-block"),t.dataset.scope=Yo(e.make().type)??"method")}else{const n=bt(this.doc,e.path);t.textContent=`${rs(n.type)}: ${vr(n)[0]}`}return t}autoScroll(e,t){const n=this.blockMode&&this.blocks?this.blocks.element:this.canvasHost,a=n.getBoundingClientRect(),s=40,r=12;t>a.bottom-s&&ta.top-s&&(n.scrollTop-=r),e>a.right-s&&ea.left-s&&(n.scrollLeft-=r)}dropTargetAt(e,t,n){var l;const a=((l=this.layout)==null?void 0:l.elements)??[],s=this.nodeAt(e,t);if(n.kind==="sensor")return!s||gn(this.doc,s.idPath)?void 0:ss.includes(bt(this.doc,s.idPath).type)?{kind:"condition",path:s.idPath,frame:s.frame}:void 0;if(!s){const u=a[a.length-1];if(!u)return;const m=u.frame.y+u.frame.height;return t>m?n.kind==="move"&&n.path.length===1&&n.path[0]===a.length-1?void 0:{kind:"rootAppend",frame:{x:u.frame.x,y:m,width:u.frame.width,height:0}}:t=n.path.length&&n.path.every((u,m)=>r[m]===u)?void 0:gn(this.doc,r)?{kind:"replace",path:r,frame:s.frame}:{kind:t"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e])}function Zh(i){if(!i)return[];for(const e of[...ot.flatMap(t=>t.steps.flatMap(n=>n.blocks)),...bn.flatMap(t=>t.blocks)])if(!(e.kind!=="exercise"||e.exercise.id!==i))return e.exercise.tests.flatMap(t=>{const n=t.world?bs(t.world):void 0;return n?[n]:[]});return[]}let Jt,p0,f0;function Lh(i){const e=i.dataset.tip;if(!e)return;Jt??(Jt=Object.assign(document.createElement("div"),{className:"tooltip",role:"tooltip"})),Jt.isConnected||document.body.append(Jt),Jt.textContent=e,Jt.hidden=!1;const t=i.getBoundingClientRect(),n=Jt.offsetWidth,a=Jt.offsetHeight,s=Math.min(Math.max(8,t.left+t.width/2-n/2),window.innerWidth-n-8),r=t.bottom+6+a{if(i.pointerType==="touch")return;const e=i.target.closest("[data-tip]");e!==f0&&(Jo(),e&&(f0=e,p0=window.setTimeout(()=>Lh(e),350)))}),document.addEventListener("focusin",i=>{const e=i.target.closest("[data-tip]");Jo(),e!=null&&e.matches(":focus-visible")&&Lh(e)});for(const i of["pointerdown","focusout","scroll","keydown"])document.addEventListener(i,Jo,!0)}const b0="visustruct.reloadedAfterError";function W3(){try{const i=Number(sessionStorage.getItem(b0));if(i&&Date.now()-i<3e4)return!1;sessionStorage.setItem(b0,String(Date.now()))}catch{return!1}return location.reload(),!0}function HY(i){console.error(i);const e=(navigator.language||"").toLowerCase().startsWith("de"),t=document.createElement("div");t.className="startup-error";const n=document.createElement("h1");n.textContent=e?"VisuStruct konnte nicht starten":"VisuStruct could not start";const a=document.createElement("p");a.textContent=e?"Wahrscheinlich gibt es gerade eine neue Version. Lade die Seite neu. Hilft das nicht, drücke Strg+Umschalt+R (Mac: ⌘⇧R).":"There is probably a new version. Reload the page. If that does not help, press Ctrl+Shift+R (Mac: ⌘⇧R).";const s=document.createElement("button");s.textContent=e?"Neu laden":"Reload",s.addEventListener("click",()=>location.reload());const r=document.createElement("pre");r.textContent=String(i instanceof Error?i.message:i),t.append(n,a,s,r),(document.getElementById("app")??document.body).replaceWith(t)}window.addEventListener("vite:preloadError",i=>{W3()&&i.preventDefault()});const VY=Cb();Promise.all([lg(VY,!1),og(Ab(),!1)]).then(()=>{L3(),GY();const i=new URLSearchParams(location.search),e=["visustruct","visubot","visuobject","visuflow"].some(s=>i.has(s)),t=En();!e&&t&&(t==="visubot"?Ti("on"):t==="visuobject"?gl():Ti("off")),i.has("visuobject")&&gl(),i.has("visuflow")&&np();const n=document.getElementById("app"),a=!i.has("visustruct")&&!i.has("visubot");if(ep()&&a)return ee(async()=>{const{ObjectApp:s}=await import("./objectApp-ClGVaIKp.js").then(r=>r.g);return{ObjectApp:s}},[],import.meta.url).then(({ObjectApp:s})=>void new s(n));if(tp()&&a)return ee(async()=>{const{FlowApp:s}=await import("./flowApp-BYyoSxMn.js");return{FlowApp:s}},[],import.meta.url).then(({FlowApp:s})=>void new s(n));new FY(n)}).then(()=>{try{sessionStorage.removeItem(b0)}catch{}}).catch(i=>{/dynamically imported module|Importing a module script failed|error loading dynamically/i.test(String(i))&&W3()||HY(i)});export{kY as A,t1 as C,J$ as J,P4 as L,ee as _,pl as a,j$ as b,k3 as c,K$ as d,KY as e,np as f,EY as g,Ti as h,me as i,Oa as j,qJ as k,VJ as l,YY as m,$3 as n,In as o,q$ as p,gl as q,hc as r,mv as s,p as t,sh as u,O3 as v,R4 as w,lh as x,wY as y,IY as z}; diff --git a/website/app/assets/index-CLK72BwI.css b/website/app/assets/index-CLK72BwI.css deleted file mode 100644 index 4037d08c..00000000 --- a/website/app/assets/index-CLK72BwI.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg: #f2f2f7;--panel: #ffffff;--text: #1c1c1e;--muted: #6e6e73;--border: #d1d1d6;--accent: #007aff;--accent-soft: rgba(0, 122, 255, .18);--accent-faint: rgba(0, 122, 255, .08);--accent-glow: rgba(0, 122, 255, .35);--area-editor: #2b8a3e;--area-code: #7c3aed;color-scheme:light}:root.visubot-theme{--bg: #f1f0f8;--text: #2f3454;--muted: #6a6890;--border: #d6d3e8;--accent: #c2410c;--accent-soft: rgba(234, 88, 12, .2);--accent-faint: rgba(234, 88, 12, .08);--accent-glow: rgba(194, 65, 12, .35)}:root.visubot-theme .selection-frame{stroke:#c2410ce6}*{box-sizing:border-box}html,body{margin:0;height:100%;background:var(--bg);color:var(--text);font:14px/1.4 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.app{display:grid;grid-template:"tools tools" auto "pal work" 1fr "pal insp" auto / var(--palette-width, 210px) minmax(0,1fr);height:100%;flex:1;min-width:0}.shell{display:flex;flex-direction:column;height:100vh;height:100dvh}.columns{display:flex;flex:1;min-height:0}.columns>.app{min-width:240px}.toolbar{display:flex;flex-wrap:wrap;gap:8px 16px;align-items:center;padding:8px 12px;background:var(--panel);border-bottom:1px solid var(--border)}button.icon{display:inline-flex;align-items:center;justify-content:center;min-width:34px;height:32px;padding:0 7px}button.icon svg{display:block}.tooltip{position:fixed;z-index:1000;max-width:260px;padding:4px 8px;border-radius:6px;background:#1c1c1e;color:#fff;font-size:12px;line-height:1.35;pointer-events:none;box-shadow:0 2px 8px #0003}.tooltip[hidden]{display:none}.diagram-tools{grid-area:tools;display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:5px 8px;background:var(--panel);border-bottom:1px solid var(--border)}.diagram-tools button.icon{min-width:30px;height:30px;padding:0 5px}.diagram-tools .sim-toggle{margin-inline-start:auto;display:inline-flex;align-items:center;gap:6px;height:30px;padding:0 10px;white-space:nowrap}.diagram-tools{container-type:inline-size}@container (max-width: 520px){.diagram-tools .sim-toggle span{display:none}}.diagram-tools .sep{width:1px;height:20px;margin-inline:3px;background:var(--border)}.menu-host{position:relative}.export-menu{position:absolute;top:calc(100% + 4px);inset-inline-end:0;z-index:30;display:grid;min-width:180px;padding:4px;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px #00000026}.export-menu[hidden]{display:none}.export-menu button{border:none;background:none;text-align:start;padding:7px 10px;border-radius:6px}.export-menu button:hover,.export-menu button:focus-visible{background:var(--accent-soft)}.settings{position:relative;margin-inline-start:auto}.settings>button{display:inline-flex;align-items:center;padding:6px 8px}.settings-menu{position:absolute;top:calc(100% + 6px);inset-inline-end:0;z-index:30;display:grid;width:290px;max-width:calc(100vw - 24px);max-height:calc(100dvh - 80px);overflow-y:auto;padding:4px 0;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px #00000026}.settings-menu[hidden]{display:none}.settings-menu section,.settings-menu footer{display:grid;gap:10px;padding:10px 14px 12px}.settings-menu section+section,.settings-menu footer{border-top:1px solid var(--border)}.settings-menu footer{display:flex;align-items:center;justify-content:space-between;gap:10px}.settings-version{font-size:12px;color:var(--muted)}.settings-menu section[hidden]{display:none}.settings-menu h3{margin:0;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}.settings-menu label{display:grid;gap:4px;font-size:13px;font-weight:600;color:var(--muted)}.settings-menu select{width:100%;max-width:none}.settings-menu label.check{grid-template-columns:auto 1fr;align-items:start;gap:8px;font-weight:500;color:var(--text);cursor:pointer}.settings-menu label.check input{margin:2px 0 0}.settings-menu label small,.settings-menu label.check small{display:block;font-weight:400;color:var(--muted)}.settings-menu .about-button{justify-self:start;padding:6px 10px;font-size:13px}dialog.about{width:min(560px,calc(100vw - 32px));max-height:calc(100dvh - 48px);padding:0;color:var(--text);background:var(--panel);border:1px solid var(--border);border-radius:12px;box-shadow:0 12px 40px #00000040}dialog.about::backdrop{background:#00000059}dialog.about header{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 16px;border-bottom:1px solid var(--border)}dialog.about h2{margin:0;font-size:16px}.about-body{padding:20px 24px 24px;font-size:14px;line-height:1.5}.about-body p{margin:0 0 8px;color:var(--muted)}.about-head{display:flex;gap:16px;padding-bottom:16px;margin-bottom:8px;border-bottom:1px solid var(--border)}.about-head .logo{flex:none;width:40px;height:40px;color:var(--accent)}.about-head h3{margin:0 0 6px;font-size:20px}.about-head h3 small{font-weight:400;color:var(--muted)}.about-body .about-version{font-variant-numeric:tabular-nums}.about-body .about-author{color:var(--text);font-weight:600}.about-link{margin:0 0 2px}.about-body h4{margin:16px 0 6px;font-size:15px}.about-body code{font-weight:600;color:var(--text)}.about-body .about-copy{margin-top:16px}.brand{font-weight:700;font-size:16px;display:flex;align-items:center;gap:8px}.visubot-logo{width:auto;height:30px;margin:-4px 0}.visubot-bot{color:#c2410c;background:linear-gradient(90deg,#ea580c,#c2410c);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;margin-left:-8px}.brand small{font-weight:500;color:var(--muted);font-size:12px}.logo{width:20px;height:20px;border:2px solid currentColor;background:linear-gradient(to bottom right,transparent 47%,currentColor 47% 53%,transparent 53%) left / 50% 60% no-repeat,linear-gradient(to bottom left,transparent 47%,currentColor 47% 53%,transparent 53%) right / 50% 60% no-repeat}.group{display:flex;flex-wrap:wrap;gap:6px}button,select{font:inherit;padding:6px 12px;border:1px solid var(--border);border-radius:8px;background:var(--panel);color:var(--text);cursor:pointer;min-height:34px}select{max-width:180px}button:hover:not(:disabled),select:hover{border-color:var(--accent)}button:disabled{opacity:.4;cursor:default}.palette,.inspector{background:var(--panel);padding:12px;overflow-y:auto}.palette{grid-area:pal;border-inline-end:1px solid var(--border)}.inspector{grid-area:insp;border-top:1px solid var(--border);max-height:34vh}h2{font-size:13px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:0 0 4px}.hint{color:var(--muted);font-size:12px;margin:0 0 10px}.palette ul{list-style:none;margin:0;padding:0;display:grid;gap:6px}.palette-item{display:flex;align-items:center;gap:10px;padding:8px 10px;border:1px solid var(--border);border-radius:8px;cursor:grab;line-height:1.25;user-select:none;-webkit-user-select:none;touch-action:none}.palette-item:hover,.palette-item:focus-visible{border-color:var(--accent);outline:none}.palette-item .icon{flex:none;color:var(--text)}.workspace{grid-area:work;display:flex;flex-direction:column;min-width:0;min-height:0}.caption{font:600 18px system-ui,-apple-system,sans-serif;border:none;border-bottom:1px solid var(--border);padding:10px 16px;background:var(--panel);outline:none}.canvas{flex:1;overflow:auto;position:relative;touch-action:manipulation;scrollbar-gutter:stable;background:#fafafa}.inline-edit{position:absolute;z-index:5;box-sizing:border-box;display:flex;align-items:center;gap:4px;padding:4px 8px;background:#fff;border:2px solid var(--accent);border-radius:6px;box-shadow:0 4px 14px #0000002e}.inline-edit-center{justify-content:center}.inline-edit-prefix,.inline-edit-sep{font-weight:700;color:#000;flex:none}.inline-edit textarea,.inline-edit input{flex:1 1 auto;min-width:3ch;margin:0;padding:2px 4px;border:1px solid #c8ccd2;border-radius:4px;background:#fff;color:#000;font:inherit;line-height:inherit;resize:none;overflow:hidden;white-space:pre}.inline-edit-center textarea{text-align:center}.inline-edit textarea:focus,.inline-edit input:focus{outline:none;border-color:var(--accent)}.canvas svg{display:block;user-select:none;-webkit-user-select:none}.dragging,.dragging *{cursor:grabbing!important}.drop-line{fill:var(--accent)}.drop-replace{fill:var(--accent-soft);stroke:var(--accent);stroke-width:3}.drag-ghost{position:fixed;left:0;top:0;z-index:10;pointer-events:none;padding:6px 10px;border-radius:8px;background:var(--accent);color:#fff;font-weight:600;box-shadow:0 4px 14px #0003;white-space:nowrap;max-width:260px;overflow:hidden;text-overflow:ellipsis}.inspector label{display:block;font-weight:600;margin:8px 0 4px}.inspector textarea{width:100%;font:14px ui-monospace,SF Mono,Menlo,Consolas,monospace;padding:8px;border:1px solid var(--border);border-radius:8px;resize:vertical}.statusbar{display:flex;flex-wrap:wrap;gap:2px 16px;align-items:baseline;padding:4px 12px;font-size:12px;color:var(--muted);background:var(--panel);border-top:1px solid var(--border)}.status{flex:1 1 auto;min-width:0}.site-links{display:flex;gap:12px;margin-inline-start:auto}.site-links a{color:inherit}.site-links a:hover,.site-links a:focus-visible{color:var(--accent)}a.brand{color:inherit;text-decoration:none}.file-drag .canvas{outline:3px dashed var(--accent);outline-offset:-8px}@media(min-width:821px){.app.compact-palette{--palette-width: 100px}.app.compact-palette .palette{padding:8px 6px}.app.compact-palette .palette h2{font-size:10px;letter-spacing:0;text-align:center}.app.compact-palette .palette .hint{display:none}.app.compact-palette .palette-item{flex-direction:column;gap:3px;padding:6px 2px;font-size:10.5px;line-height:1.15;text-align:center;overflow-wrap:break-word}}@media(max-width:820px){.app{grid-template:"tools" auto "pal" auto "work" 1fr "insp" auto / minmax(0,1fr)}.palette,.inspector{min-width:0}.palette{border-inline-end:none;border-bottom:1px solid var(--border);padding:8px}.palette .hint,.palette h2{display:none}.palette ul{display:flex;overflow-x:auto;overflow-y:hidden}.palette-item{white-space:nowrap}.inspector{max-height:30vh}}.status.message{color:var(--text);font-weight:600}.confirm{position:fixed;top:0;right:0;bottom:0;left:0;z-index:20;display:grid;place-items:center;background:#00000040;padding:16px}.confirm-card{background:var(--panel);border-radius:12px;padding:16px 18px;max-width:360px;box-shadow:0 10px 30px #0003}.confirm-card p{margin:0 0 8px}.confirm-card .group{justify-content:flex-end;margin-top:12px}.rename-card{width:min(380px,100%)}.rename-card label{display:block;margin-top:8px;font-size:13px}.rename-card input,.rename-card select{width:100%;box-sizing:border-box;margin-top:4px;font-size:15px}.rename-card .rename-info{min-height:1.3em;margin-top:8px}.rename-card .rename-warning{color:#c93400}.rename-menu{position:fixed;z-index:30;padding:4px;background:var(--panel);border:1px solid var(--border);border-radius:8px;box-shadow:0 6px 20px #0003}.rename-menu button{display:block;width:100%;text-align:left;border:none;background:none;padding:6px 12px;border-radius:6px}.rename-menu button:hover,.rename-menu button:focus-visible{background:var(--accent-soft)}.inspector-rename{margin-top:8px}.files-ask{width:min(360px,100%)}.files-ask input{width:100%;box-sizing:border-box;margin-top:4px}.files-choices{display:flex;flex-wrap:wrap;gap:6px;margin:4px 0 10px}.files-choice{display:inline-flex;align-items:center;gap:6px}.files-choice svg{width:16px;height:16px}button.danger{background:#d70015;border-color:#d70015;color:#fff}.simpanel{display:none;grid-area:insp;background:var(--panel);border-inline-start:1px solid var(--border);padding:12px;overflow-y:auto}.app.simulating{grid-template:"tools tools" auto "work insp" 1fr / minmax(0,var(--sim-work, 1fr)) minmax(240px,1fr)}.sim-resizer{display:none;grid-area:insp;justify-self:start;width:10px;margin-inline-start:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.app.simulating .sim-resizer{display:block}.sim-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.sim-resizer:hover:after,.sim-resizer:focus-visible:after,.shell.resizing .sim-resizer:after{background:var(--accent)}.sim-resizer:focus-visible{outline:none}.insp-resizer{grid-area:insp;align-self:start;height:20px;margin-top:-10px;z-index:5;cursor:row-resize;touch-action:none;position:relative}.insp-resizer:before{content:"";position:absolute;inset-inline:0;top:9px;height:2px;background:transparent;transition:background .15s}.insp-resizer:after{content:"";position:absolute;left:50%;top:7px;width:36px;height:6px;margin-left:-18px;border-radius:3px;background:var(--border);transition:background .15s}.insp-resizer:hover:before,.insp-resizer:focus-visible:before,.shell.resizing .insp-resizer:before,.insp-resizer:hover:after,.insp-resizer:focus-visible:after,.shell.resizing .insp-resizer:after{background:var(--accent)}.insp-resizer:focus-visible{outline:none}.app.insp-sized .inspector,.app.insp-sized.coding .inspector{height:var(--insp-height);max-height:none}.app.simulating .palette,.app.simulating .inspector,.app.simulating .insp-resizer{display:none}.app.simulating .simpanel{display:block}.app.simulating .caption{cursor:default}button.sim-toggle{font-weight:600}button.sim-toggle{background:var(--accent);border-color:var(--accent);color:#fff}.simpanel section{margin-bottom:14px}.sim-buttons{display:grid;grid-template-columns:1fr 1fr;gap:6px;margin:6px 0 10px}.sim-option{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px;font-size:13px}.mono,.sim-vars td{font:13px/1.5 ui-monospace,SF Mono,Menlo,Consolas,monospace}.sim-trace,.sim-output,.sim-vars{background:var(--bg);border-radius:8px;padding:8px 10px;overflow-wrap:anywhere}.sim-output{max-height:220px;overflow-y:auto;white-space:pre-wrap}.sim-vars table{border-collapse:collapse;width:100%}.sim-vars td{padding:2px 0;vertical-align:top}.sim-vars td:first-child{color:var(--muted);padding-inline-end:12px;width:1%;white-space:nowrap}.sim-vars table.sim-grid{width:auto}.sim-vars .sim-grid td,.sim-vars .sim-grid td:first-child{border:1px solid var(--border);color:inherit;font-size:11px;padding:0 4px;text-align:center;width:auto}.sim-vars .sim-grid td.sim-grid-true{font-weight:700}.simpanel .placeholder{color:var(--muted);font:13px system-ui,-apple-system,sans-serif;margin:0}.sim-notice{margin:0 0 14px;padding:8px 10px;border-radius:8px;background:#34c75926;font-weight:600}.sim-edit{width:100%;margin:-6px 0 14px}.sim-notice.error{background:#ff3b3026;color:#b0000f}.sim-input.active{padding:8px 10px;margin-left:-10px;margin-right:-10px;border-radius:10px;background:var(--accent-faint);outline:2px solid var(--accent)}.sim-prompt{margin:4px 0 6px;font-weight:600}.sim-input-row{display:flex;gap:6px}.sim-input-row input{flex:1;min-width:0;font:inherit;padding:6px 10px;border:1px solid var(--border);border-radius:8px}.sim-input-error{color:#b0000f;margin:6px 0 0;font-size:13px}@media(max-width:820px){.app.simulating{grid-template:"tools" auto "work" minmax(200px,1fr) "insp" auto / minmax(0,1fr)}.simpanel{border-inline-start:none;border-top:1px solid var(--border);max-height:48vh}.app.simulating .sim-resizer{display:none}}.codepanel{display:none;grid-area:code;background:var(--panel);border-inline-start:1px solid var(--border);padding:12px;overflow-y:auto;min-height:0}.app.coding{grid-template:"tools tools tools" auto "pal work code" 1fr "pal insp code" auto / var(--palette-width, 210px) minmax(0,1fr) 400px}.app.coding .codepanel{display:block}.app.coding .inspector{max-height:34vh}button[data-action=code][aria-pressed=true]{border-color:var(--accent);background:var(--accent-soft)}.code-head,.runner-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.code-close{min-height:28px;padding:2px 10px}.segmented{display:flex;margin-bottom:8px}.segmented button{flex:1;border-radius:0;min-height:30px;padding:4px 8px}.segmented button:first-child{border-radius:8px 0 0 8px}.segmented button:last-child{border-radius:0 8px 8px 0}.segmented button+button{border-inline-start:none}.segmented button[aria-pressed=true]{background:var(--accent);border-color:var(--accent);color:#fff}.segmented[hidden]{display:none}.check{display:flex;gap:8px;align-items:center;font-size:13px;margin-bottom:4px}.code-actions{display:flex;flex-wrap:wrap;gap:6px;margin:10px 0}.code-actions [hidden]{display:none}button.primary{background:var(--accent);border-color:var(--accent);color:#fff}.code-view,.runner-out{margin:0;padding:10px 12px;border-radius:10px;background:var(--bg);font:13px/1.5 ui-monospace,SF Mono,Menlo,Consolas,monospace;overflow:auto;white-space:pre;-moz-tab-size:4;tab-size:4}.runner{margin:12px 0}.runner-out{background:#0b1220;color:#e5e7eb;min-height:3em;max-height:240px;white-space:pre-wrap}.runner-out.error{outline:2px solid #ff453a}.runner-input{margin-top:8px}.runner-input label{display:block;font-weight:600;margin-bottom:4px}@media(max-width:820px){.app.coding{grid-template:"tools" auto "pal" auto "work" minmax(240px,1fr) "insp" auto "code" auto / minmax(0,1fr)}.codepanel{border-inline-start:none;border-top:1px solid var(--border);max-height:50vh}}.tabs{display:flex;align-items:flex-end;gap:2px;padding:6px 8px 0;background:var(--bg);border-bottom:1px solid var(--border);overflow-x:auto;overflow-y:hidden;scrollbar-width:thin}.tab{display:flex;align-items:center;max-width:220px;border:1px solid var(--border);border-bottom:none;border-radius:8px 8px 0 0;background:#ffffff80}.tab.active{background:var(--panel);margin-bottom:-1px;padding-bottom:1px}.tab button{border:none;background:none;min-height:30px;border-radius:8px 8px 0 0}.tab-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:4px 6px 4px 12px}.tab.active .tab-label{font-weight:600}.tab-close{padding:2px 8px;color:var(--muted);font-size:11px}.tab-close:hover{color:var(--text)}.tab-dot,.tab-close.unsaved .tab-x{display:none}.tab-close.unsaved .tab-dot{display:inline;color:var(--text);font-size:10px}.tab-close.unsaved:hover .tab-dot{display:none}button.tab-close.unsaved:hover .tab-x{display:inline}span.tab-close{padding:2px 10px 2px 4px}.tab-add{min-height:28px;padding:2px 10px;margin:0 0 3px 4px;border-radius:8px}.inspector .segmented{margin:6px 0 4px;max-width:420px}.mono-hint{font:11px/1.45 ui-monospace,SF Mono,Menlo,Consolas,monospace;overflow-wrap:anywhere}.editor-row{display:flex;gap:8px;align-items:flex-start}.editor-row textarea{flex:1;min-width:0}.fixed-prefix{font:14px ui-monospace,SF Mono,Menlo,Consolas,monospace;color:var(--muted);padding-top:9px}.inspector input:not([type]){width:100%;font:14px ui-monospace,SF Mono,Menlo,Consolas,monospace;padding:7px 8px;border:1px solid var(--border);border-radius:8px}.for-fields{display:grid;gap:4px}.for-fields label,.case-controls label{margin:4px 0 0;font-size:12px;color:var(--muted);font-weight:600}.case-controls{margin-top:12px;padding-top:10px;border-top:1px solid var(--border)}.case-controls .group{margin-top:8px}button.danger-text:not(:disabled){color:#d70015}.print-sheet{display:none}.files{display:none;flex:0 1 auto;flex-direction:column;width:260px;min-width:200px;min-height:0;background:var(--panel);border-inline-end:1px solid var(--border)}.shell.files-open .files{display:flex}.shell.files-open .files-rail{display:none}.files-head{display:flex;align-items:center;gap:4px;margin:10px 12px 6px}.files-head h2{flex:1;margin:0;font-size:13px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}.files-head button.icon{min-width:30px;height:30px;padding:0 5px}.files-head [hidden]{display:none}.files-body{flex:1;min-height:0;overflow:auto;padding:0 8px 12px;font-size:14px}.files-where{margin:0 4px 6px;font-size:12px;color:var(--muted);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.files-hint,.files-empty{margin:8px 4px;font-size:13px;color:var(--muted)}.start-ask .files-start{margin-bottom:2px}.start-ask .hint{font-size:13px;color:var(--muted)}.start-without{display:block;margin:12px auto 0;border:none;background:none;color:var(--accent);text-decoration:underline}.start-without+.hint{text-align:center}.files-start{display:flex;align-items:center;gap:6px;width:100%;margin:8px 0;justify-content:center}.files-row{display:flex;align-items:center;border-radius:6px}.files-row:hover,.files-row:focus-within{background:var(--accent-soft)}.files-row.current>.files-name{font-weight:600}.files-name{flex:1;min-width:0;display:flex;align-items:center;gap:6px;padding:4px 6px 4px calc(6px + var(--depth) * 16px);border:none;background:none;text-align:start;font:inherit;color:var(--text);cursor:pointer}.files-name:disabled{cursor:default;color:var(--muted)}.files-name svg{flex:none;width:16px;height:16px;color:var(--muted)}.files-name span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.files-name small{margin-inline-start:auto;padding:0 6px;border-radius:6px;background:var(--accent);color:#fff;font-size:11px;font-weight:600}.files-row .files-delete{visibility:hidden;min-width:26px;height:26px;padding:0 4px;border:none;background:none;color:var(--muted)}.files-row:hover .files-delete,.files-row:focus-within .files-delete{visibility:visible}.files-error{color:#c62828}.files-resizer{display:none;flex:none;width:10px;margin-inline:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.shell.files-open .files-resizer{display:block}.files-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px}.files-resizer:hover:after,.files-resizer:focus-visible:after{background:var(--accent)}.learn{display:none;flex:0 1 auto;width:400px;min-width:280px;flex-direction:column;background:var(--panel);border-inline-end:1px solid var(--border);min-height:0}.shell.learning .learn{display:flex}.learn-resizer{display:none;flex:none;width:10px;margin-inline:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.shell.learning .learn-resizer{display:block}.learn-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.learn-resizer:hover:after,.learn-resizer:focus-visible:after,.shell.resizing .learn-resizer:after{background:var(--accent)}.learn-resizer:focus-visible{outline:none}.shell.resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.learn-head{display:flex;align-items:flex-start;gap:6px;margin:10px 12px 0}.learn-tabs{flex:1;min-width:0}.learn-hide{flex:none;min-height:30px;padding:4px 7px;display:inline-flex;align-items:center;color:var(--muted)}.learn-rail{flex:none;display:flex;flex-direction:column;align-items:center;gap:10px;width:36px;padding:10px 0;border:none;border-radius:0;border-inline-end:1px solid var(--border);background:var(--panel);cursor:pointer}.learn-rail:hover{background:var(--accent-soft)}.learn-rail span{writing-mode:vertical-rl;white-space:nowrap}.shell.learning .learn-rail:not(.live-rail):not(.editor-rail):not(.files-rail):not(.robotpreview-rail):not(.palette-rail){display:none}.live-rail{border-inline-end:none;border-inline-start:1px solid var(--border)}.live-rail svg{transform:scaleX(-1)}.shell.live .live-rail{display:none}.editor-rail{display:none;border-inline-start:1px solid var(--border)}.shell.editor-hidden .editor-rail{display:flex}.shell.editor-hidden .app,.shell.editor-hidden .live-resizer,.shell.editor-hidden:not(.live) .learn-resizer{display:none}.shell.editor-hidden .livecode,.shell.editor-hidden:not(.live) .learn{flex:1 1 0;width:auto!important;min-width:0}.learn-body{overflow-y:auto;scrollbar-gutter:stable;padding:4px 16px 24px;flex:1;min-height:0;line-height:1.5}.learn-body h2.learn-title{display:flex;align-items:center;gap:8px;font-size:20px;text-transform:none;letter-spacing:0;color:var(--text);margin:2px 0 10px}.learn-section{font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:18px 0 6px}.learn-list{list-style:none;padding:0;margin:0;display:grid;gap:6px}.learn-item{display:flex;align-items:center;gap:10px;width:100%;text-align:start;padding:8px 10px}.learn-item.done{border-color:#34c759}.learn-num{flex:none;min-width:28px;height:28px;border-radius:14px;background:var(--accent-soft);color:var(--accent);display:grid;place-items:center;font-weight:700;font-size:12px;padding:0 6px}.learn-item.done .learn-num{background:#34c7592e;color:#248a3d}.learn-item-text{flex:1;display:grid;min-width:0}.learn-item-text small{color:var(--muted)}.learn-status{flex:none;color:var(--muted);font-size:12px}.learn-item.done .learn-status{color:#248a3d;font-weight:700}.learn-back,.link-button{border:none;background:none;padding:6px 0;min-height:0;color:var(--accent)}.link-button{padding:0;text-align:start}.learn-lesson-head{position:sticky;top:-4px;z-index:3;margin:-4px -16px 10px;padding:4px 16px 8px;background:var(--panel);border-bottom:1px solid var(--border)}.learn-lesson-head .learn-progress{margin-bottom:8px}.learn-lesson-head .learn-step-title{margin:0}.learn-head-row{display:flex;align-items:center;justify-content:space-between;gap:8px;flex-wrap:wrap}.learn-nav.learn-nav-top{margin-top:0;gap:6px}.learn-nav-top button{padding:3px 10px;min-height:0;font-size:13px}.learn-kicker{margin:4px 0 0;color:var(--muted);font-size:12px}.learn-progress{display:flex;gap:4px;margin-bottom:14px}.learn-progress button{flex:1 1 0;min-width:0;padding:3px 0;font-size:13px;font-variant-numeric:tabular-nums;border-radius:6px}.learn-progress button.past{border-color:var(--accent);color:var(--accent)}.learn-progress button.current{border-color:var(--accent);background:var(--accent);color:#fff;font-weight:600}.learn-progress button.robot-step:not(.current){border-color:#0fa3c6;background:#0fa3c61f}.learn-progress button.robot-step.current{border-color:#0fa3c6;background:#0fa3c6}.learn-step-title{font-size:16px;margin:0 0 6px}.learn-text p,.learn-text ul,.learn-text ol{margin:0 0 10px}.learn-text ul,.learn-text ol{padding-inline-start:22px}.learn-text code,.learn-tests code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;background:var(--bg);border-radius:4px;padding:1px 4px}.learn-text h3{font-size:15px;margin:18px 0 4px}.reference-group{border:1px solid var(--border);border-radius:8px;margin:0 0 8px;padding:0 12px}.reference-group summary{cursor:pointer;font-weight:600;padding:9px 0}.reference-group[open] summary{border-bottom:1px solid var(--border)}.reference-group>h3:first-of-type{margin-top:10px}.lesson-code{background:var(--bg);border-radius:8px;padding:8px 10px;margin:0 0 10px;overflow-x:auto;overflow-y:hidden;font-size:12.5px}.learn-open-code{display:block;margin:-4px 0 12px}.lesson-code code{background:none;padding:0}.lesson-note{border-inline-start:3px solid var(--accent);background:var(--accent-soft);border-radius:6px;padding:8px 10px;margin:0 0 10px}.lesson-note p:last-child{margin-bottom:0}.learn-images{display:flex;gap:8px;flex-wrap:wrap}.learn-images img{width:150px;height:200px;object-fit:cover;border-radius:8px}.learn-file{margin:0 0 14px;border:1px solid var(--border);border-radius:10px;overflow:hidden;background:var(--panel)}.learn-file figcaption{display:flex;align-items:center;gap:6px;padding:6px 10px;background:var(--bg);border-bottom:1px solid var(--border);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;font-weight:600}.learn-file-text{margin:0;padding:8px 10px;overflow-x:auto;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12.5px;line-height:1.5;white-space:pre}.learn-file.written{margin:8px 0;border-color:#15803d}.learn-file.written figcaption{background:#dcfce7}.learn-file-text .ln{display:inline-block;min-width:2ch;margin-inline-end:10px;color:var(--muted);text-align:end;-webkit-user-select:none;user-select:none}.learn-brag{display:grid;grid-template-columns:48px minmax(0,1fr);gap:10px;align-items:start;border:1px solid var(--border);border-radius:10px;padding:10px 12px;margin:0 0 14px;background:var(--accent-soft)}.learn-brag img{width:48px;height:48px;border-radius:50%}.learn-brag-title{margin:0 0 4px;font-weight:700}.learn-brag .learn-text p:last-child{margin-bottom:0}.learn-game{border:2px solid var(--accent);border-radius:12px;padding:12px 14px;margin:0 0 14px}.learn-game-head{display:flex;align-items:center;gap:10px;font-size:1.05em}.learn-game-head img{width:40px;height:40px;border-radius:50%}.learn-game-status{margin:6px 0;color:var(--muted);font-size:.9em}.learn-game-values{display:flex;flex-wrap:wrap;gap:6px;margin-bottom:8px}.learn-game-values code{background:var(--accent-soft);border-radius:6px;padding:2px 8px}.learn-game-condition{font-size:1.15em;font-weight:600;padding:10px 12px;border-radius:8px;background:var(--bg, #f2f2f7);margin-bottom:10px;overflow-wrap:anywhere}.learn-game-answers button{flex:1;font-weight:600;padding-block:10px}.learn-game-true:not(:disabled){border-color:#15803d;color:#15803d}.learn-game-false:not(:disabled){border-color:#b91c1c;color:#b91c1c}.learn-game-feedback:empty{display:none}.learn-game-feedback{margin-top:10px;padding:10px 12px;border-radius:8px}.learn-game-feedback.right{background:#dcfce7}.learn-game-feedback.wrong{background:#fee2e2}.learn-game-feedback p{margin:0 0 6px;font-weight:700}.learn-game-steps{margin-bottom:10px;line-height:1.6}.learn-game-result{font-size:1.2em;font-weight:700}.learn-game-question{font-weight:600;margin:10px 0 8px}.learn-game-code{margin:0 0 8px;padding:8px 0;border-radius:8px;background:var(--bg, #f2f2f7);font-size:13px;line-height:1.5;overflow-x:auto}.learn-game-code .ln{padding:0 10px 0 calc(10px + 2ch);text-indent:-2ch;border-inline-start:3px solid transparent;white-space:pre-wrap;overflow-wrap:anywhere;transition:background .2s}.learn-game-code .ln.hit{background:#fef3c7;border-inline-start-color:#d97706}.learn-bomb{display:flex;align-items:center;gap:10px;margin-bottom:6px;font-weight:700}.learn-bomb-icon{font-size:28px;line-height:1}.learn-bomb.hurry .learn-bomb-icon{animation:learn-bomb-shake .25s infinite alternate}.learn-bomb.boom .learn-bomb-icon{animation:learn-bomb-boom .5s ease-out}@keyframes learn-bomb-shake{0%{transform:rotate(-8deg)}to{transform:rotate(8deg)}}@keyframes learn-bomb-boom{0%{transform:scale(.6)}60%{transform:scale(1.5)}to{transform:scale(1)}}.learn-bomb-time{min-width:7ch;font-variant-numeric:tabular-nums}.learn-bomb-fuse{flex:1;height:8px;border-radius:4px;background:#e5e7eb;overflow:hidden}.learn-bomb-fuse span{display:block;height:100%;background:#f59e0b;transition:width .2s linear}.learn-bomb.hurry .learn-bomb-fuse span{background:#dc2626}.learn-bomb.defused .learn-bomb-fuse span{background:#15803d}.learn-bomb-code .ln{display:block;width:100%;margin:0;border-radius:0;border-block:0;border-inline-end:0;background:none;color:inherit;font:inherit;line-height:inherit;min-height:0;height:auto;padding-block:1px;text-align:start;cursor:pointer}.learn-bomb-code .ln:not(:disabled):hover{background:#fef3c7}.learn-bomb-code .ln:disabled{cursor:default;opacity:1}.learn-bomb-code .ln.right,.learn-bomb-code .ln.right:hover{background:#dcfce7;border-inline-start-color:#15803d}.learn-bomb-code .ln.wrong,.learn-bomb-code .ln.wrong:hover{background:#fee2e2;border-inline-start-color:#b91c1c}.learn-bomb-fix code{white-space:pre-wrap}.learn-bomb-code.by-parts .ln.parts{cursor:default;padding-block:3px;line-height:1.9}.learn-bomb-code.by-parts .ln.parts:hover{background:none}.learn-bomb-code.by-parts .ln.parts.right{background:#f0fdf4}.learn-bomb-code .part{display:inline;margin:0;padding:2px 3px;min-height:0;height:auto;border:1px dashed #94a3b8;border-radius:4px;background:none;color:inherit;font:inherit;line-height:inherit;cursor:pointer;-webkit-box-decoration-break:clone;box-decoration-break:clone}.learn-bomb-code .part:not(:disabled):hover{background:#fef3c7;border-color:#d97706}.learn-bomb-code .part:disabled{cursor:default;opacity:1}.learn-bomb-code .part.right{background:#dcfce7;border:2px solid #15803d}.learn-bomb-code .part.wrong{background:#fee2e2;border:2px solid #b91c1c}.learn-bomb-hint{color:var(--muted, #6b7280);font-size:.92em}.learn-game-options{display:grid;gap:6px}.learn-game-options button{text-align:start;padding:8px 12px}.learn-game-options.pictures{grid-template-columns:repeat(auto-fill,minmax(130px,1fr))}.learn-game-options.pictures button{padding:6px}.learn-game-options button.right{border-color:#15803d;background:#dcfce7;color:inherit;opacity:1}.learn-game-options button.wrong{border-color:#b91c1c;background:#fee2e2;color:inherit;opacity:1}.learn-order-hint{margin:0 0 6px;font-size:13px;opacity:.75}.learn-order button{display:flex;align-items:center;gap:10px}.learn-order-badge{flex:none;display:inline-grid;place-items:center;width:24px;height:24px;border-radius:50%;border:1.5px solid currentColor;font-size:13px;font-weight:700;opacity:.5}.learn-order button.right .learn-order-badge,.learn-order button:disabled .learn-order-badge:not(:empty){opacity:1}.learn-game-file{margin:0;padding:2px 0;font-size:13px;line-height:1.45;white-space:pre;background:none}.learn-game-explain{margin-bottom:10px}.learn-swing-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:8px;align-items:start}.learn-swing-window{border:1px solid var(--border);border-radius:8px;overflow:hidden;background:#fff;color:#1c1c1e}.learn-swing-title{padding:3px 8px;font-size:12px;text-align:center;background:#e5e5ea;border-bottom:1px solid #d1d1d6;min-height:1.4em}.learn-swing-body{display:grid;gap:6px;padding:10px}.learn-swing-label{padding:4px 2px;min-height:1.4em}.learn-swing-label.changed,.learn-swing-button.changed{animation:swing-changed 1s ease-out}@keyframes swing-changed{0%{background:#ffd60acc}}.learn-swing-button{padding:6px 10px;background:#fff;color:#1c1c1e;border:1px solid #a1a1aa;border-radius:6px}.learn-swing-button:disabled{opacity:1;cursor:default}.learn-swing-button.pressed{background:var(--accent);border-color:var(--accent);color:#fff;transform:scale(.96)}.learn-swing-event{margin:0;padding:0 10px 8px;font-size:12px;min-height:1.4em}.learn-swing-event.heard{color:#b45309;font-weight:600}.learn-swing-event.nobody{color:#6b7280;font-style:italic}.learn-swing-console{margin:0 10px 10px;padding:6px 8px;border-radius:6px;background:#1c1c1e;color:#f2f2f7;font-size:12px}.learn-swing-console[hidden]{display:none}.learn-swing-clicks{margin:8px 0 0}.learn-swing-try{font-weight:400!important;font-style:italic}.learn-layout-window{width:100%;border:1px solid #a1a1aa;border-radius:6px;overflow:hidden;background:#fff;color:#1c1c1e}.learn-layout-title{height:10px;background:#e5e5ea;border-bottom:1px solid #d1d1d6}.learn-layout-body{height:80px;padding:3px;gap:3px;font-size:12px}.learn-layout-body.grid{display:grid;grid-auto-rows:1fr}.learn-layout-body.flow{display:flex;flex-direction:column;gap:3px}.learn-layout-row{display:flex;justify-content:center;gap:3px}.learn-layout-body.border{display:grid;grid-template:"N N N" auto "W C E" 1fr "S S S" auto / auto 1fr auto}.learn-layout-cell{display:flex;align-items:center;justify-content:center;min-width:22px;padding:2px 6px;border:1px solid #a1a1aa;border-radius:4px;background:#f4f4f5;font-weight:600}.learn-figure{margin:0 0 14px;display:grid;gap:6px;justify-items:start}.learn-preview{width:100%;border:1px solid var(--border);border-radius:8px;overflow-x:auto;overflow-y:hidden;background:#fafafa}.learn-preview>svg{display:block;width:100%;margin:0 auto;height:auto}.learn-figure figcaption{color:var(--muted);font-size:13px}.learn-exercise{border:1px solid var(--accent);border-radius:10px;padding:10px 12px;margin:0 0 14px;display:grid;gap:8px}.learn-exercise h4{margin:0;font-size:15px;display:flex;gap:8px;align-items:center}.learn-badge{font-size:12px;color:#248a3d;background:#34c7592e;border-radius:10px;padding:1px 8px}.learn-where{margin:0;font-size:13px;font-weight:600;color:var(--muted)}.area-name{display:inline-block;color:#fff;border-radius:10px;padding:0 8px}.area-name.area-editor{background:var(--area-editor)}.area-name.area-code{background:var(--area-code)}.columns>.app{border-top:3px solid var(--area-editor)}.livecode{border-top:3px solid var(--area-code)}.diagram-title{margin:0;font-size:16px;color:var(--area-editor)}.livecode-head h2,.live-rail span{color:var(--area-code)}.editor-rail span{color:var(--area-editor)}.live-rail,.editor-rail{font-weight:600}.learn-exercise .learn-text p:last-child{margin-bottom:0}.learn-result:empty{display:none}.learn-result{border-radius:8px;padding:8px 10px}.learn-result.ok{background:#34c75924}.learn-result.fail{background:#ff950024}.learn-result.info{background:var(--bg)}.learn-result-head{margin:0 0 6px;font-weight:600}.learn-tests{list-style:none;padding:0;margin:0;display:grid;gap:6px}.learn-test-detail{display:grid;padding-inline-start:16px;font-size:13px}.learn-error{color:#c4291c}.learn-solution summary{cursor:pointer;color:var(--accent)}.learn-solution[open]{display:grid;gap:6px;justify-items:start}.learn-checklist{list-style:none;padding:0;margin:0 0 14px;display:grid;gap:6px}.learn-checklist li{display:flex;gap:8px;align-items:baseline;justify-content:space-between}.learn-checklist li{align-items:center;justify-content:flex-start;padding:4px 6px;border-radius:8px}.learn-check-text{flex:1}.learn-checklist li[data-rating=sun]{background:color-mix(in srgb,#f5b800 14%,transparent)}.learn-checklist li[data-rating=cloud]{background:color-mix(in srgb,#8a97a8 14%,transparent)}.learn-checklist li[data-rating=umbrella]{background:color-mix(in srgb,#3b82f6 12%,transparent)}.learn-rating{display:inline-flex;gap:2px;flex:none}.learn-rating-button{min-width:34px;min-height:34px;padding:2px;font-size:1.15em;line-height:1;border-radius:8px;opacity:.45;filter:grayscale(.7)}.learn-rating-button[aria-pressed=true]{opacity:1;filter:none;border-color:var(--accent);background:var(--accent-soft)}.learn-rating-legend{display:flex;flex-wrap:wrap;gap:4px 14px;margin:0 0 8px;font-size:.9em;color:var(--muted, inherit)}.learn-review{margin:12px 0 0}.learn-review:empty{display:none}.learn-review-title{margin:0 0 4px;font-weight:700}.learn-review-icon{flex:none;width:1.6em;text-align:center}.learn-checklist li.learn-review-deviation{border:1px solid var(--accent);background:var(--accent-soft)}.learn-competences{border:1px solid var(--accent);border-radius:10px;padding:10px 12px;margin:0 0 14px;background:var(--accent-soft)}.learn-competences-title{margin:0 0 4px;font-weight:700}.learn-competences ul{margin:0;padding-left:1.3em}.learn-return{width:100%;margin:0 0 12px;border-color:var(--accent);background:var(--accent-soft)}.learn-nav+.learn-return{margin:12px 0 0}.learn-check-link{flex:none;font-size:13px}.learn-quiz fieldset{border:1px solid var(--border);border-radius:10px;padding:8px 12px 10px;margin:0 0 10px;display:grid;gap:6px}.learn-quiz legend{font-weight:600;padding:0 4px}.learn-option{text-align:start}.learn-option.right{border-color:#34c759;background:#34c75924}.learn-option.wrong{border-color:#ff3b30;background:#ff3b301a}.learn-match{display:grid;gap:6px}.learn-match-left{display:grid;grid-template-columns:8.5em minmax(0,1fr);align-items:center;gap:8px}.learn-match-select{min-width:0;width:100%;max-width:none}.learn-match-select.right{border-color:#34c759;background:#34c75924}.learn-match-select.wrong{border-color:#ff3b30;background:#ff3b301a}.learn-explanation{margin:0;color:var(--muted);font-size:13px}.learn-score{font-weight:600}.learn-nav{display:flex;justify-content:space-between;gap:8px;margin-top:18px}@media(max-width:1100px){.learn{width:340px}}@media(max-width:900px){.learn{width:300px}}.livecode{display:none;flex:0 1 auto;width:420px;min-width:200px;flex-direction:column;gap:6px;padding:10px 12px 12px;background:var(--panel);border-inline-start:1px solid var(--border);min-height:0}.shell.live .livecode{display:flex}.livecode-head{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px}.livecode-head h2{margin:0;font-size:16px}.livecode-lang{margin-inline-start:auto;padding:2px 8px;border-radius:6px;background:var(--accent-soft);color:var(--accent);font-size:12px;font-weight:600}.livecode .learn-hide svg{transform:scaleX(-1)}.livecode .hint{margin:0}.livecode-view{flex:1;min-height:0;padding:10px 0;font-size:var(--code-size, 14px)}.livecode-view .ln{padding:0 12px;min-height:1.5em;border-inline-start:3px solid transparent}.livecode-view.selectable .ln{cursor:pointer}.livecode-view .ln.sel{background:var(--accent-soft);border-inline-start-color:var(--accent)}.livecode-view .ph{color:var(--muted);font-style:italic}.livecode-editor{flex:1;min-height:0;padding:0;font-size:var(--code-size, 14px);white-space:normal;overflow:hidden;--syn-keyword: #7f0055;--syn-string: #2a00ff;--syn-number: #986801;--syn-comment: #3f7f5f}.livecode-editor[hidden],.livecode-view[hidden],.livecode-problem[hidden]{display:none}.livecode-editor .cm-editor{height:100%;background:transparent}.livecode-editor .cm-editor.cm-focused{outline:2px solid var(--accent-soft);outline-offset:-2px;border-radius:10px}.livecode-editor .cm-scroller{font:inherit;font-family:ui-monospace,SF Mono,Menlo,Consolas,monospace;line-height:1.5}.livecode-editor .cm-gutters{background:transparent;border-inline-end:1px solid var(--border);color:var(--muted)}.livecode-editor .cm-lineNumbers .cm-gutterElement{padding:0 8px 0 10px;min-width:2ch}.livecode-editor .cm-activeLineGutter{background:transparent;color:var(--text)}.livecode-editor .cm-content{padding:10px 0;caret-color:var(--text)}.livecode-editor .cm-line{padding:0 12px;border-inline-start:3px solid transparent}.livecode-editor .cm-scopeLayer .cm-scope{border:1px solid color-mix(in srgb,var(--scope) 28%,transparent);border-left:3px solid var(--scope);border-radius:8px;background:color-mix(in srgb,var(--scope) 9%,#fff)}.livecode-editor .cm-scopeLayer .cm-scope-class{--scope: #34c47c}.livecode-editor .cm-scopeLayer .cm-scope-method{--scope: #f2b100}.livecode-editor .cm-scopeLayer .cm-scope-loop{--scope: #ec5fa6}.livecode-editor .cm-scopeLayer .cm-scope-if{--scope: #3b9cf0}.livecode-editor .cm-scopeLayer .cm-scope-try{--scope: #9a7cf0}:root.no-code-scopes .livecode-editor .cm-scopeLayer .cm-scope:not(.cm-scope-sel){display:none}:root:not(.no-code-scopes) .ns-scope-method{fill:color-mix(in srgb,#f2b100 14%,#fff)}:root:not(.no-code-scopes) .ns-scope-loop{fill:color-mix(in srgb,#ec5fa6 14%,#fff)}:root:not(.no-code-scopes) .ns-scope-if{fill:color-mix(in srgb,#3b9cf0 14%,#fff)}:root:not(.no-code-scopes) .palette-item[data-scope=method]{background:color-mix(in srgb,#f2b100 14%,#fff)}:root:not(.no-code-scopes) .palette-item[data-scope=loop]{background:color-mix(in srgb,#ec5fa6 14%,#fff)}:root:not(.no-code-scopes) .palette-item[data-scope=if]{background:color-mix(in srgb,#3b9cf0 14%,#fff)}.livecode-editor .cm-scopeLayer .cm-scope-sel{background:transparent;border:2px solid var(--accent);box-shadow:0 0 0 3px var(--accent-faint)}.livecode-editor .cm-line.cm-debug-line{background:#ffd60a59;border-inline-start-color:#e0a800}.livecode-editor .cm-breakpoint-gutter .cm-gutterElement,.livecode-editor .cm-lineNumbers .cm-gutterElement{cursor:pointer}.livecode-editor .cm-breakpoint-gutter .cm-gutterElement{display:flex;align-items:center;justify-content:center;width:14px;padding-inline-start:4px}.livecode-editor .cm-lineNumbers .cm-gutterElement.cm-error-line{color:#fff;background:#d33;border-radius:4px;font-weight:600}.livecode-editor .cm-breakpoint{display:inline-block;width:10px;height:10px;border-radius:50%;background:#d33}.livecode-editor .cm-breakpoint-gutter .cm-gutterElement:not(:has(.cm-breakpoint)):hover:before{content:"";width:10px;height:10px;border-radius:50%;background:#dd33334d}.livecode-debug{flex:none;max-height:30%;overflow-y:auto}.livecode-debug[hidden]{display:none}.livecode-debug h2{margin:0 0 4px;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}.livecode-vars tr.changed td{animation:var-changed 1.6s ease-out;font-weight:700}@keyframes var-changed{0%{background:#ffd60ab3}to{background:transparent}}.livecode-editor .cm-ph,.livecode-editor .cm-ph *{color:var(--muted)!important;font-style:italic;font-weight:400!important}.livecode-editor .cm-quickfix{margin-inline-start:8px;padding:0 3px;border:1px solid #e0a800;border-radius:4px;background:#ffd60a40;font-size:12px;line-height:1.3;cursor:pointer}.livecode-editor .cm-quickfix:hover{background:#ffd60a8c}.livecode-editor .cm-err{text-decoration:underline wavy #d33;text-decoration-skip-ink:none;text-underline-offset:3px}.livecode-editor .cm-selectionBackground,.livecode-editor .cm-focused .cm-selectionBackground{background:color-mix(in srgb,var(--accent) 25%,transparent)!important}.livecode-editor .cm-matchingBracket{background:color-mix(in srgb,var(--accent) 20%,transparent);outline:1px solid color-mix(in srgb,var(--accent) 50%,transparent)}.cm-tooltip.cm-tooltip-autocomplete{border:1px solid var(--border);border-radius:8px;background:var(--panel);box-shadow:0 4px 16px #0000001f;overflow:hidden}.cm-tooltip.cm-tooltip-autocomplete>ul{font:14px/1.4 ui-monospace,SF Mono,Menlo,Consolas,monospace;max-height:16em}.cm-tooltip.cm-tooltip-autocomplete>ul>li{padding:4px 10px}.cm-tooltip.cm-tooltip-autocomplete>ul>li[aria-selected]{background:var(--accent);color:#fff}.cm-tooltip-autocomplete .cm-completionDetail{margin-inline-start:12px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-style:normal;color:var(--muted)}.cm-tooltip-autocomplete li[aria-selected] .cm-completionDetail{color:#ffffffd9}.livecode-run{padding:3px 10px;font-size:13px;margin-inline-start:6px}.console{display:flex;flex:none;flex-direction:column;gap:6px;height:180px;min-height:80px;padding:6px 10px 8px;background:var(--bg);border:1px solid var(--border);border-radius:8px}.console[hidden],.console-resizer[hidden]{display:none}.console-head{display:flex;align-items:center;gap:8px}.console-head h2{margin:0;font-size:12px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted)}.console-hide{margin-inline-start:auto;padding:0 8px;font-size:14px}.console-resizer{flex:none;height:10px;margin-block:-5px;cursor:row-resize;touch-action:none;position:relative}.console-resizer:after{content:"";position:absolute;inset-inline:0;top:4px;height:2px}.console-resizer:hover:after,.console-resizer:focus-visible:after,.console-resizer.dragging:after{background:var(--accent)}.console-resizer:focus-visible{outline:none}.livecode-console-lines{flex:1;min-height:0;overflow:auto;font-size:var(--code-size, 14px);white-space:pre-wrap}.livecode-console-lines .typed{color:var(--accent)}.livecode-console-lines .pending:after{content:"▌";color:var(--accent);animation:blink 1s steps(1) infinite}@keyframes blink{50%{opacity:0}}.livecode-console-input{display:flex;flex-wrap:wrap;align-items:center;gap:6px}.livecode-console-input[hidden]{display:none}.livecode-console-input label{display:flex;flex:1;align-items:center;gap:6px;min-width:0;font-size:var(--code-size, 14px)}.livecode-console-input input{flex:1;min-width:60px;padding:4px 8px;font:inherit}.livecode-console-error,.livecode-console-notice{margin:0;font-size:13px;color:var(--muted)}.livecode-console-error{flex-basis:100%;color:#c62828}.livecode-console-notice.error{color:#c62828}.livecode-problem{margin:0;min-height:1.4em;font-size:13px;color:#c62828}.live-resizer{display:none;flex:none;width:10px;margin-inline:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.shell.live .live-resizer{display:block}.live-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.live-resizer:hover:after,.live-resizer:focus-visible:after,.shell.resizing .live-resizer:after{background:var(--accent)}.live-resizer:focus-visible{outline:none}@media(max-width:1100px){.livecode{width:340px}}@media(max-width:700px){.shell.learning .learn-resizer,.shell.live .live-resizer,.shell.files-open .files-resizer{display:none}.shell .files{width:auto!important;max-height:40vh;border-inline-end:none;border-bottom:1px solid var(--border)}.shell .learn,.shell .livecode,.columns>.app{width:auto!important;min-width:0}.livecode{height:40vh;height:40dvh;border-inline-start:none;border-top:1px solid var(--border)}.learn-rail{flex-direction:row;justify-content:center;width:auto;padding:4px 0;border-inline-end:none;border-bottom:1px solid var(--border)}.learn-rail span{writing-mode:horizontal-tb}.live-rail{border-inline-start:none;border-bottom:none;border-top:1px solid var(--border)}.columns{flex-direction:column}.editor-rail{border-inline-start:none;border-bottom:1px solid var(--border)}.learn{width:auto;height:45vh;height:45dvh;border-inline-end:none;border-bottom:1px solid var(--border)}.columns>.app,.shell.editor-hidden .livecode{flex:1;min-height:0;height:auto}}@media print{@page{margin:12mm}body>*:not(.print-sheet){display:none!important}body{background:#fff!important}.print-sheet{display:block}.print-sheet svg{display:block;width:100%;height:auto;max-height:100vh}}.learn .term{text-decoration:underline dotted var(--accent);text-decoration-thickness:2px;text-underline-offset:3px;cursor:help;border-radius:3px}.learn .term:focus-visible,.learn .term[aria-expanded=true]{outline:none;background:var(--accent-soft)}.term-tip{position:fixed;z-index:1000;max-width:min(300px,calc(100vw - 16px));padding:8px 10px;background:var(--panel);color:var(--text);border:1px solid var(--border);border-radius:8px;box-shadow:0 4px 16px #0000002e;font-size:14px;line-height:1.4}.term-tip[hidden]{display:none}.learn .ui-pointer{color:var(--accent);font-weight:600;border-bottom:2px solid var(--accent-soft);cursor:pointer;border-radius:3px}.learn .ui-pointer:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.ui-spot{outline:3px solid var(--accent)!important;outline-offset:-3px;box-shadow:inset 0 0 0 9999px var(--accent-faint);animation:ui-spot-pulse 1.2s ease-in-out infinite}@keyframes ui-spot-pulse{50%{outline-color:var(--accent-glow)}}@media(prefers-reduced-motion:reduce){.ui-spot{animation:none}}.learn-graphic{display:block;width:100%;max-width:440px;height:auto;margin:0 auto;border-radius:8px;border:1px solid var(--border)}.learn-animation{background:#fff;overflow:hidden}.learn-animation>svg{display:block;width:100%;height:auto}.learn-replay{justify-self:center}.learn-graphic-toggle{display:block;width:100%;padding:0;border:0;background:none;cursor:zoom-in}.learn-graphic-toggle[aria-pressed=true]{cursor:zoom-out}.learn-graphic-hint{margin:4px 0 0;text-align:center;font-size:.85em;color:var(--muted)}.lesson-table{overflow-x:auto;margin:8px 0}.lesson-table table{border-collapse:collapse;font-size:13px;font-variant-numeric:tabular-nums}.lesson-table th,.lesson-table td{border:1px solid var(--border);padding:3px 8px;text-align:start;white-space:nowrap}.lesson-table th{background:var(--bg)}.learn-hints .lesson-note{margin-top:8px}.learn-teacher{margin-top:24px;border-top:1px solid var(--border);padding-top:12px}.learn-teacher summary{cursor:pointer;color:var(--muted)}.learn-teacher input{flex:1;min-width:0}.shell.editor-hidden:not(.live):not(.learning) .files{flex:1 1 0;width:auto!important}.livecode-swing{height:380px}.livecode-swing[hidden]{display:none}.livecode-swing-display{position:relative;flex:1;min-height:0;overflow:hidden;border-radius:6px;background:#e9ecf0}.learn-caesar-stage{display:flex;flex-direction:column;align-items:center;gap:8px}.learn-caesar-disc{width:min(100%,320px);touch-action:none;user-select:none;-webkit-user-select:none}.learn-caesar-disc text{font:600 19px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.learn-caesar-disc .outer-disc{fill:#f3e6c8;stroke:#8a6d3b;stroke-width:3}.learn-caesar-disc .outer-letters text{fill:#3d2f14;cursor:pointer}.learn-caesar-disc .inner{cursor:grab;transition:transform .25s ease}.learn-caesar-disc .inner.dragging{cursor:grabbing;transition:none}.learn-caesar-disc .inner-disc{fill:#6b4f2a;stroke:#3d2f14;stroke-width:2}.learn-caesar-disc .inner-letters text{fill:#fff8e8}.learn-caesar-disc .tick{stroke:#8a6d3b;stroke-width:1;opacity:.6}.learn-caesar-disc .hub{fill:#f3e6c8;stroke:#3d2f14;stroke-width:2}.learn-caesar-disc .window{fill:var(--accent-soft);stroke:var(--accent);stroke-width:2.5}.learn-caesar-disc .marked text{fill:var(--accent);font-size:23px}.learn-caesar-disc .inner-letters .marked text{fill:#ffd54a}.learn-caesar-disc .key{font-size:40px;font-weight:700;fill:#3d2f14}.learn-caesar-disc .key-label{font-size:15px;fill:#6e5a33}.learn-caesar-controls,.learn-caesar-mode{display:flex;justify-content:center;gap:8px}.learn-caesar-controls button{min-width:56px;font-size:1.05em}.learn-caesar-mode{margin:10px 0 6px}.learn-caesar-mode button.active{background:var(--accent);border-color:var(--accent);color:#fff}.learn-caesar-fields{display:grid;gap:8px}.learn-caesar-fields label,.learn-caesar-fields>div{display:grid;gap:3px;color:var(--muted);font-size:.9em}.learn-caesar-fields input,.learn-caesar-output{font:600 1.15em ui-monospace,Menlo,Consolas,monospace;letter-spacing:.08em;padding:6px 8px;border:1px solid var(--border);border-radius:8px;color:var(--text);background:var(--panel);overflow-wrap:anywhere}.learn-caesar-output{background:var(--bg)}.learn-rescue-path{display:flex;align-items:center;gap:6px;margin:8px 0 2px}.learn-rescue-ludwig{width:32px;height:32px;border-radius:50%;flex:none}.learn-rescue-dot{flex:1;height:6px;max-width:48px;border-radius:3px;background:var(--border);transition:background .3s}.learn-rescue-dot.done{background:#15803d}.learn-rescue-tower{position:relative;font-size:34px;line-height:1;margin-inline-start:auto}.learn-rescue-zora,.learn-rescue-fox{position:absolute;font-size:15px}.learn-rescue-zora{top:-4px;left:50%;transform:translate(-50%)}.learn-rescue-fox{bottom:-2px;right:-12px;transition:transform .8s,opacity .8s}.learn-rescue.freed .learn-rescue-fox{transform:translate(60px);opacity:0}.learn-rescue.freed .learn-rescue-zora{display:none}.learn-rescue-free{font-size:24px;line-height:1;animation:learn-rescue-jump .6s ease-out 3}@keyframes learn-rescue-jump{50%{transform:translateY(-12px)}}.learn-rescue-answer{display:flex;gap:8px;align-items:center}.learn-rescue-number{width:6ch;font-size:1.1em;padding:6px 8px;text-align:center}.learn-rescue-label{margin:14px 0 4px;color:var(--muted);font-size:.9em}.learn-rescue-alphabet{display:flex;flex-wrap:wrap;gap:2px;margin-bottom:12px}.learn-rescue-letter{display:inline-flex;flex-direction:column;align-items:center;min-width:22px;padding:2px 0;border-radius:4px;background:var(--bg)}.learn-rescue-letter small{font-size:10px;color:var(--muted)}.learn-rescue-letter.hit{background:#fde68a;outline:2px solid #d97706}.learn-rescue-charat{font-weight:600}.learn-rescue-moved{font-weight:400!important}.learn-rescue-word{font:inherit;padding:1px 4px;border:1px solid var(--accent);border-radius:4px}.learn-rescue-gate{padding:8px 0;margin-bottom:8px}.learn-cw{--cw-cell: 30px;display:flex;flex-wrap:wrap;gap:12px 20px;margin-bottom:10px}.learn-cw-grid{display:grid;gap:2px}.learn-cw-cell{position:relative;width:var(--cw-cell);height:var(--cw-cell)}.learn-cw-cell input{width:100%;height:100%;box-sizing:border-box;padding:0;border:1px solid #6e6e73;border-radius:3px;background:#fff;color:#1c1c1e;text-align:center;font:600 16px/1 ui-monospace,SFMono-Regular,Menlo,monospace;text-transform:uppercase}.learn-cw-cell input:disabled{opacity:1;-webkit-text-fill-color:#1c1c1e}.learn-cw-cell.key input{background:#fde68a}.learn-cw-cell.given input{color:#1d4ed8;-webkit-text-fill-color:#1d4ed8}.learn-cw-cell.arrived input{animation:learn-cw-arrive .6s ease-out}.learn-cw-cell input.wrong{border-color:#b91c1c;background:#fee2e2}@keyframes learn-cw-arrive{0%{transform:scale(1.6)}}.learn-cw-number,.learn-cw-keyindex{position:absolute;font-size:8px;line-height:1;pointer-events:none;color:#6e6e73;z-index:1}.learn-cw-number{top:2px;left:2px}.learn-cw-keyindex{bottom:2px;right:2px;color:#b45309}.learn-cw-keyword{margin:8px 0 0;font-weight:600}.learn-cw-keybox{display:inline-block;width:22px;height:24px;margin-inline-end:2px;border-bottom:2px solid #d97706;background:#fef3c7;text-align:center;line-height:24px;white-space:pre}.learn-cw-cluebox{flex:1;min-width:200px}.learn-cw-clues{margin:4px 0 10px;padding-inline-start:24px}.learn-cw-clues li{cursor:pointer}@media(max-width:380px){.learn-cw{--cw-cell: 28px}}.learn-cloze{border:2px solid var(--accent);border-radius:12px;padding:12px 14px;margin:0 0 14px}.learn-cloze-title{display:block;font-size:1.05em;margin-bottom:4px}.learn-cloze p{margin:0 0 8px}.learn-cloze-hint{color:var(--muted);font-size:.9em}.learn-cloze-code{margin:0 0 10px;padding:8px 10px;border-radius:8px;background:var(--bg, #f2f2f7);font-size:13px;line-height:1.9;overflow-x:auto}.learn-cloze-code .ln{white-space:pre;min-height:1.9em}.learn-cloze-gap{font-size:13px;padding:1px 3px;margin:0 1px;min-width:0;max-width:none;border:1px solid #8e8e93;border-bottom-width:2px;border-radius:4px;background:#fff;color:#1c1c1e;text-align:center}.learn-cloze-gap.right{border-color:#34c759;background:#34c75924}.learn-cloze-gap.wrong{border-color:#ff3b30;background:#ff3b301a}.learn-cloze-gap.shown{border-color:#d97706;background:#fef3c7}.learn-cloze-gap:disabled{opacity:1;-webkit-text-fill-color:currentColor}.learn-cloze-actions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.learn-cloze-check{font-weight:600}.learn-cloze-status:empty{display:none}.learn-cloze-status{padding:8px 12px;border-radius:8px;font-weight:600}.learn-cloze-status.right{background:#dcfce7}.learn-cloze-status.wrong{background:#fee2e2}.learn-pap{border:2px solid var(--accent);border-radius:12px;padding:12px 14px;margin:0 0 14px}.learn-pap-title{display:block;font-size:1.05em;margin-bottom:4px}.learn-pap p{margin:0 0 8px}.learn-pap-hint{color:var(--muted);font-size:.9em}.learn-pap-plan{display:block;width:100%;max-width:420px;margin:0 auto 10px;background:#fff;border-radius:8px;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;fill:#1c1c1e}.learn-pap-slot{cursor:pointer;outline:none}.learn-pap-slot .learn-pap-shape{stroke:#8e8e93;stroke-width:2}.learn-pap-slot:focus-visible .learn-pap-shape,.learn-pap-slot.over .learn-pap-shape{stroke:var(--accent);stroke-width:3}.learn-pap-slot.right .learn-pap-shape{stroke:#34c759;stroke-width:3}.learn-pap-slot.wrong .learn-pap-shape{stroke:#ff3b30;stroke-width:3}.learn-pap-slot.shown .learn-pap-shape{stroke:#d97706}.learn-pap-legend{display:grid;grid-template-columns:auto 1fr;gap:4px 10px;align-items:center;max-width:420px;margin:0 auto 12px;padding:8px 10px;border-radius:8px;background:var(--bg, #f2f2f7);font-size:.9em}.learn-pap-legend dt{line-height:0}.learn-pap-legend dd{margin:0}.learn-pap-work{display:grid;grid-template-columns:minmax(0,3fr) minmax(0,2fr);gap:8px;align-items:start;margin-bottom:10px}.learn-pap-work.done{grid-template-columns:minmax(0,1fr)}.learn-pap-left .learn-pap-plan{margin-bottom:8px}.learn-pap-tray{position:sticky;top:0;display:flex;flex-direction:column;align-items:center;gap:4px;max-height:100vh;overflow-y:auto;padding:6px 2px;border-radius:8px;background:var(--bg, #f2f2f7)}.learn-pap-tray:empty,.learn-pap-tray:not(:has(.learn-pap-card:not([hidden]))){display:none}.learn-pap-card svg{max-width:100%;height:auto}.learn-pap-card{padding:2px;max-width:100%;border:2px solid transparent;border-radius:10px;background:none;line-height:0;touch-action:none;cursor:grab;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;fill:#1c1c1e}.learn-pap-card.selected{border-color:var(--accent);background:var(--accent-soft)}.learn-pap-card.dragging{opacity:.3}.learn-pap-ghost{position:fixed;z-index:1000;pointer-events:none;transform:translate(-50%,-50%);opacity:.9}.learn-pap-actions{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:8px}.learn-pap-check{font-weight:600}.learn-pap-status:empty{display:none}.learn-pap-status{padding:8px 12px;border-radius:8px;font-weight:600}.learn-pap-status.right{background:#dcfce7}.learn-pap-status.wrong{background:#fee2e2}.learn-mark{color:inherit;padding:0 3px;border-radius:3px}.learn-mark.blau{background:#e4f1fd}.learn-mark.orange{background:#ffedd5}.learn-mark.gruen{background:#dcfce7}.learn-mark.gelb{background:#fdf4db}.learn-mark.rosa{background:#fce9f2}.startup-error{max-width:520px;margin:15vh auto 0;padding:0 16px;font:16px/1.5 system-ui,-apple-system,sans-serif;color:var(--text)}.startup-error h1{font-size:22px}.startup-error button{font-size:16px;padding:8px 16px}.startup-error pre{margin-top:24px;color:var(--muted);font-size:12px;white-space:pre-wrap}.visubot-stage{position:relative;height:280px;border:1px solid var(--border);border-radius:8px;overflow:hidden;background:linear-gradient(#dfe9f3,#f6f3ec)}.visubot-live{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 8px;font-size:12px;color:var(--muted)}.visubot-live:empty{display:none}.visubot-live.connected{color:#0f8a5f;font-weight:600}.visubot-worlds{display:flex;gap:6px;margin:0 0 6px}.visubot-worlds button.active{background:var(--accent);border-color:var(--accent);color:#fff}.visubot-canvas{display:block;width:100%;height:100%;touch-action:none}.visubot-status{font-size:12px;color:var(--muted);margin:6px 0 0}.visubot-controls{margin-top:8px;font-size:13px}.visubot-controls summary{cursor:pointer;font-weight:600}.visubot-controls .hint{color:var(--muted);font-size:12px;margin:4px 0 6px}.visubot-controls label{display:grid;grid-template-columns:9.5em 1fr 2.5em;align-items:center;gap:6px;margin:2px 0}.visubot-controls output{text-align:end;font-variant-numeric:tabular-nums}.visubot-presses{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}.visubot-presses button{min-width:2.2em}.visubot-presses button.pressed{background:var(--accent);border-color:var(--accent);color:#fff}.visubot-check{margin-top:8px;font-size:13px}.visubot-check summary{cursor:pointer;font-weight:600}.visubot-check .hint{color:var(--muted);font-size:12px;margin:4px 0 6px}.visubot-check-sensors{margin-top:8px;border-collapse:collapse;font-variant-numeric:tabular-nums}.visubot-check-sensors th{text-align:start;font-weight:400;padding:1px 12px 1px 0}.palette-visubot-title{font-size:13px;text-transform:uppercase;letter-spacing:.04em;color:var(--muted);margin:14px 0 6px}.palette-sensor{display:flex;flex-direction:column;align-items:flex-start;gap:2px;border-style:dashed}.palette-sensor code{font-size:12px;color:var(--muted)}.palette-visubot .palette-item{border-color:#a7a0d3;background:color-mix(in srgb,#a7a0d3 16%,transparent)}.visubot-model{margin:12px 0}.visubot-model .visubot-stage{height:300px}.visubot-model-hint{font-size:13px;margin:4px 0 0}.visubot-part-label{position:absolute;pointer-events:none;padding:2px 8px;border-radius:6px;background:#14181ed9;color:#fff;font-size:13px;white-space:nowrap;z-index:1}.robot-part-link{text-decoration:underline 2px #f2b705;text-underline-offset:3px;cursor:pointer}.robot-part-link:hover,.robot-part-link:focus-visible{background:color-mix(in srgb,#f2b705 25%,transparent);outline:none}.visubot-quiz-question{font-size:15px;margin:12px 0 6px}.visubot-quiz-question.done{color:var(--success, #2f9e44)}.visubot-quiz-feedback{min-height:1.4em;margin:6px 0}.visubot-quiz-feedback.right{color:var(--success, #2f9e44)}.visubot-quiz-feedback.wrong{color:var(--danger, #c92a2a)}.diagram-tools .mbot-link{margin-inline-start:auto;height:30px;padding:0 10px;white-space:nowrap}.diagram-tools .mbot-link:not([hidden])+.sim-toggle,.diagram-tools .mbot-upload:not([hidden])+.sim-toggle{margin-inline-start:6px}.diagram-tools .mbot-upload{margin-inline-start:6px;height:30px;padding:0 10px;white-space:nowrap}.mbot-upload-steps{margin:0 0 12px;padding-inline-start:22px;line-height:1.5}.mbot-upload progress{width:100%;height:10px}.mbot-upload-status.done{color:var(--success, #2f9e44);font-weight:600}.settings-mbot-link{display:grid;gap:4px;margin-top:8px}.settings-mbot-link[hidden]{display:none}.settings-mbot-link small{font-size:12px;color:var(--muted)}.world-chooser{margin-bottom:4px}.segmented.world-chooser{display:grid;grid-template-columns:1fr 1fr}.segmented.world-chooser button{border-radius:0;border-inline-start:1px solid var(--border)}.segmented.world-chooser button:nth-child(2n){border-inline-start:none}.segmented.world-chooser button:nth-child(n+3){border-top:none}.segmented.world-chooser button:first-child{border-start-start-radius:8px}.segmented.world-chooser button:nth-child(2){border-start-end-radius:8px}.segmented.world-chooser button:nth-last-child(2){border-end-start-radius:8px}.segmented.world-chooser button:last-child{border-end-end-radius:8px}.segmented.world-chooser button[aria-pressed=true]{border-color:var(--accent)}.world-hint{display:block;font-size:12px;color:var(--muted);margin-bottom:8px}.start-world{margin-bottom:14px}.start-brand{display:flex;align-items:center;gap:6px;font-size:22px;font-weight:700;margin:0 0 12px}.visustruct-struct{color:#4f46e5;background:linear-gradient(90deg,#2563eb,#7c3aed);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.visucode-more{margin:6px 0 8px}.visucode-more small{display:block;font-size:12px;color:var(--muted)}.visucode-brand{font-weight:800;font-size:16px;letter-spacing:-.01em;color:var(--text);text-decoration:none}.visucode-brand span{color:#7c3aed;background:linear-gradient(90deg,#2563eb,#c026d3,#e11d48,#ea580c,#eab308,#84cc16,#16a34a,#0f766e,#0284c7);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}.visucode-more .visucode-credit{margin-top:4px}.visucode-more small a{color:inherit}.visucode-brand:hover{text-decoration:underline}.robotpreview,.robotpreview-rail,.preview-resizer{display:none}.shell.robot-world.preview .robotpreview{display:flex}.shell.robot-world:not(.preview) .robotpreview-rail{display:flex;border-inline-end:none;border-inline-start:1px solid var(--border)}.robotpreview-rail svg{transform:scaleX(-1)}.shell.robot-world.preview:not(.preview-stacked) .preview-resizer{display:block}.robotpreview{flex:0 1 auto;width:360px;min-width:220px;flex-direction:column;gap:6px;padding:10px 12px 12px;background:var(--panel);border-inline-start:1px solid var(--border);min-height:0;overflow-y:auto}.robotpreview .hint{margin:0;font-size:.85em;color:var(--muted, inherit)}.robotpreview .robotpreview-stage{flex:1 1 auto;height:auto;min-height:160px}.robotpreview .robotpreview-status{margin:0;min-height:1.3em}.robotpreview-height{display:none}.robotpreview.simulation>.visubot-worlds,.robotpreview.simulation>.robotpreview-stage,.robotpreview.simulation>.robotpreview-status,.robotpreview.simulation .robotpreview-play{display:none}.robotpreview .visubot-viewport{display:flex;flex:1 1 auto;flex-direction:column;min-height:0}.robotpreview .visubot-viewport .visubot-stage{flex:1 1 auto;height:auto;min-height:160px}.visubot-elsewhere{display:none;margin:0 0 8px;font-size:12px;color:var(--muted)}.visubot:not(:has(>.visubot-viewport)) .visubot-elsewhere{display:block}.shell.preview-stacked .robotpreview{flex:0 0 auto;width:auto!important;height:320px;min-width:0;margin:4px -12px -12px;padding-top:0;border-inline-start:none;border-top:1px solid var(--border);overflow:visible}.shell.preview-stacked .robotpreview-height{display:block;height:10px;margin:-5px 0 0;cursor:row-resize;touch-action:none;position:relative}.robotpreview-height:after{content:"";position:absolute;inset-inline:0;top:4px;height:2px;background:transparent;transition:background .15s}.robotpreview-height:hover:after,.robotpreview-height:focus-visible:after,.shell.resizing .robotpreview-height:after{background:var(--accent)}.robotpreview-height:focus-visible{outline:none}.preview-resizer{flex:none;width:10px;margin-inline:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.preview-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.preview-resizer:hover:after,.preview-resizer:focus-visible:after,.shell.resizing .preview-resizer:after{background:var(--accent)}.preview-resizer:focus-visible{outline:none}.palette-group{grid-column:1 / -1}.palette-group button{display:flex;align-items:center;gap:6px;width:100%;margin-top:4px;padding:2px 0;border:none;background:none;color:var(--muted);font-size:12px;font-weight:600;text-align:start;cursor:pointer}.palette-group button:before{content:"▾";transition:transform .15s}.palette-group button[aria-expanded=false]:before{transform:rotate(-90deg)}.palette-item[hidden]{display:none}.editor-resizer{display:none;flex:none;width:10px;margin-inline:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.editor-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.editor-resizer:hover:after,.editor-resizer:focus-visible:after,.shell.resizing .editor-resizer:after{background:var(--accent)}.editor-resizer:focus-visible{outline:none}.shell.code-main.live:not(.editor-hidden) .editor-resizer{display:block}.shell.code-main.live:not(.editor-hidden) .live-resizer{display:none}.shell.code-main.live:not(.editor-hidden) .app{flex:0 1 auto;width:460px;min-width:300px}.shell.code-main.live:not(.editor-hidden) .livecode{flex:1 1 0;width:auto!important;min-width:0}.shell.code-main.live:not(.editor-hidden) .livecode{order:1}.shell.code-main.live:not(.editor-hidden) .editor-resizer{order:2}.shell.code-main.live:not(.editor-hidden) .app{order:3;border-inline-start:1px solid var(--border)}.shell.code-main.live:not(.editor-hidden) .live-rail,.shell.code-main.live:not(.editor-hidden) .preview-resizer,.shell.code-main.live:not(.editor-hidden) .robotpreview,.shell.code-main.live:not(.editor-hidden) .robotpreview-rail{order:4}[data-action=swap-main]{font-size:16px;line-height:1}[data-action=swap-main][aria-pressed=true]{color:var(--accent)}.palette-resizer{grid-area:pal;justify-self:end;width:10px;margin-inline-end:-5px;z-index:5;cursor:col-resize;touch-action:none;position:relative}.palette-resizer:after{content:"";position:absolute;inset-block:0;inset-inline-start:4px;width:2px;background:transparent;transition:background .15s}.palette-resizer:hover:after,.palette-resizer:focus-visible:after,.shell.resizing .palette-resizer:after{background:var(--accent)}.palette-resizer:focus-visible{outline:none}.app.simulating .palette-resizer{display:none}:root.visuobject-theme{--bg: #edf4f6;--text: #17292e;--muted: #566a70;--border: #cddde1;--accent: #0e7c86;--accent-soft: rgba(14, 124, 134, .2);--accent-faint: rgba(14, 124, 134, .08);--accent-glow: rgba(14, 124, 134, .35)}.visuobject-obj,.visuobject .brand .logo{color:var(--accent)}.shell.visuobject button.text-button{padding:5px 10px;border:1px solid var(--border);border-radius:8px;background:var(--panel);color:var(--text);font:inherit;cursor:pointer}.shell.visuobject button.text-button:hover{border-color:var(--accent);color:var(--accent)}:root.visuobject-theme{--vo-bar: #e3edf0}.vo-columns{display:grid;grid-template-columns:var(--vo-cols, 260px 1px minmax(0, 45fr) 1px minmax(0, 55fr));flex:1;min-height:0;border-top:1px solid var(--border)}.vo-columns.vo-resizing{cursor:col-resize;-webkit-user-select:none;user-select:none}.vo-pane{display:flex;flex-direction:column;min-width:0;min-height:0;background:var(--panel)}.vo-pane.collapsed>:not(.vo-strip){display:none}.vo-strip{display:none;flex:1;align-items:center;justify-content:center;padding:12px 0;border:0;background:var(--vo-bar);color:var(--muted);font:600 14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;writing-mode:vertical-rl;cursor:pointer}.vo-pane.collapsed>.vo-strip{display:flex}.vo-strip:hover,.vo-strip:focus-visible{color:var(--accent);background:var(--accent-faint);outline:none}.vo-splitter{position:relative;z-index:4;background:var(--border);cursor:col-resize;touch-action:none;outline:none}.vo-splitter:after{content:"";position:absolute;top:0;right:-4px;bottom:0;left:-4px}.vo-splitter:hover,.vo-splitter:focus-visible,.vo-resizing .vo-splitter{background:var(--accent)}.vo-splitter.fixed{cursor:default;background:var(--border)}.vo-bar{display:flex;flex-wrap:wrap;align-items:stretch;min-height:40px;background:var(--vo-bar);border-bottom:1px solid var(--border);font-size:14px}.vo-bar-title{display:flex;flex:1;align-items:center;margin:0;text-transform:none;letter-spacing:normal;padding:0 12px;font-size:14px;font-weight:600;color:var(--text)}.vo-bar-actions{display:flex;flex:0 1 auto;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:6px;padding:4px 4px 4px 0}.shell.visuobject .vo-bar button.text-button{padding:2px 8px;border-radius:6px}.vo-collapse{padding:0 12px;border:0;background:none;color:var(--muted);font-size:16px;font-weight:700;cursor:pointer}.vo-collapse:hover,.vo-collapse:focus-visible{color:var(--accent)}.vo-tabs{display:flex;flex:1;min-width:0;overflow-x:auto}.vo-tab{display:flex;align-items:stretch;border-right:1px solid var(--border);border-bottom:3px solid transparent}.vo-tab.active{background:var(--panel);border-bottom-color:var(--accent)}.vo-tab [role=tab]{padding:0 4px 0 14px;border:0;background:none;color:var(--text);font:13px ui-monospace,SF Mono,Menlo,Consolas,monospace;white-space:nowrap;cursor:pointer}.vo-tab.active [role=tab]{font-weight:700}.vo-tab [role=tab]:hover{color:var(--accent)}.vo-tab-close{padding:0 10px 0 6px;border:0;background:none;color:var(--muted);font-size:15px;cursor:pointer;visibility:hidden}.vo-tab.active .vo-tab-close,.vo-tab:hover .vo-tab-close{visibility:visible}.vo-tab-close:hover{color:var(--accent)}.vo-tab-close .vo-dot,.vo-tab-close.unsaved .vo-x{display:none}.vo-tab-close.unsaved{visibility:visible;color:var(--text)}.vo-tab-close.unsaved .vo-dot{display:inline;font-size:11px}.vo-tab-close.unsaved:hover .vo-dot{display:none}.vo-tab-close.unsaved:hover .vo-x{display:inline}.vo-no-file{display:flex;align-items:center;padding:0 12px;color:var(--muted)}.vo-editor{flex:1;min-height:0;overflow:auto;font-size:14px}.vo-foot{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px 12px;padding:6px 12px;border-top:1px solid var(--border);background:var(--vo-bar)}.vo-hint{color:var(--muted);flex:1 1 260px}.vo-canvas{position:relative;flex:1;min-height:0;overflow:auto;background:radial-gradient(circle,var(--border) 1px,transparent 1px) 0 0 / 20px 20px;outline:none}.vo-frames{position:absolute;inset:0 auto auto 0;z-index:0;pointer-events:none;overflow:visible}.vo-package-frame{fill:#0e7c8609;stroke:var(--muted);stroke-width:1.2}.vo-package.current .vo-package-frame{stroke:var(--accent);fill:var(--accent-faint)}.vo-package-name{font:600 12px ui-monospace,SF Mono,Menlo,Consolas,monospace;fill:var(--muted)}.vo-package.current .vo-package-name{fill:var(--accent)}.vo-lines{position:absolute;inset:0 auto auto 0;z-index:3;pointer-events:none;overflow:visible}.vo-rel{stroke:var(--text);stroke-width:1.4;fill:none}.vo-rel.ghost{stroke:var(--accent);stroke-dasharray:4 4}.vo-hit{stroke:transparent;stroke-width:12;pointer-events:stroke;cursor:pointer}.vo-relation:hover .vo-rel,.vo-relation:hover .vo-diamond{stroke:var(--accent)}.vo-relation.selected .vo-rel,.vo-relation.selected .vo-diamond{stroke:var(--accent);stroke-width:2.4}.vo-relation.selected .vo-label-svg{fill:var(--accent)}.vo-diamond{fill:var(--panel);stroke:var(--text);stroke-width:1.4}.vo-diamond.filled{fill:var(--text)}.vo-relation.selected .vo-diamond.filled{fill:var(--accent)}.vo-label-svg.name{font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-style:italic;fill:var(--text)}.vo-rel.dashed{stroke-dasharray:6 4}.vo-hollow,.vo-open{fill:none;stroke:var(--text);stroke-width:1.4}.vo-label-svg{font:12px ui-monospace,SF Mono,Menlo,Consolas,monospace;fill:var(--muted);paint-order:stroke;stroke:var(--panel);stroke-width:4px}.vo-empty{margin:24px;max-width:40ch;color:var(--muted)}.vo-status{margin:0;color:#b3261e}.vo-status:empty{display:none}.vo-status.hint{color:var(--accent)}.uml-class{position:absolute;left:16px;top:16px;min-width:150px;background:var(--panel);border:1.5px solid var(--text);font:13px/1.5 ui-monospace,SF Mono,Menlo,Consolas,monospace;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none}.uml-connect{position:absolute;right:-11px;top:50%;width:20px;height:20px;margin-top:-10px;padding:0;border:2px solid var(--panel);border-radius:50%;background:var(--accent);box-shadow:0 1px 4px #00000040;cursor:crosshair;touch-action:none}.uml-connect:after{content:"";position:absolute;top:5px;right:4px;bottom:5px;left:5px;background:#fff;clip-path:polygon(0 35%,55% 35%,55% 0,100% 50%,55% 100%,55% 65%,0 65%)}.uml-class.drop{border-color:var(--accent);box-shadow:0 0 0 4px var(--accent-soft)}.vo-relmenu{position:absolute;z-index:10;display:grid;min-width:220px;padding:6px;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px #0000002e;font:14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-relmenu p{margin:2px 8px 6px;color:var(--muted);font-size:12px}.vo-relmenu button{display:flex;align-items:center;gap:10px;padding:6px 8px;border:0;border-radius:6px;background:none;color:var(--text);font:inherit;text-align:left;cursor:pointer}.vo-relmenu button:hover,.vo-relmenu button:focus-visible{background:var(--accent-faint);color:var(--accent);outline:none}.vo-relicon{flex:none}.vo-palette{display:flex;flex-wrap:wrap;align-items:stretch;gap:2px;padding:4px 6px;border-bottom:1px solid var(--border);background:var(--panel)}.vo-palette-sep{width:1px;margin:4px;background:var(--border)}.vo-tool{display:grid;justify-items:center;gap:1px;min-width:44px;padding:3px 3px 2px;border:1px solid transparent;border-radius:7px;background:none;color:var(--text);font:11px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;touch-action:none;-webkit-user-select:none;user-select:none}.vo-tool:hover{background:var(--accent-faint);color:var(--accent)}.vo-tool[aria-pressed=true]{border-color:var(--accent);background:var(--accent-soft);color:var(--accent)}.vo-tool[data-tool=class],.vo-tool[data-tool=interface]{cursor:grab}.vo-tool-ghost{position:fixed;z-index:1000;display:grid;justify-items:center;min-width:120px;padding:4px 10px 14px;margin:-14px 0 0 -60px;border:1.5px dashed var(--accent);background:#ffffffe6;font:13px ui-monospace,SF Mono,Menlo,Consolas,monospace;pointer-events:none}.vo-tool-ghost small{color:var(--muted);font-size:11px}.vo-canvas:not([data-tool=select])[data-tool]{cursor:crosshair}.vo-canvas:not([data-tool=select])[data-tool] .uml-class{cursor:crosshair}.vo-canvas:not([data-tool=select])[data-tool] .uml-class:hover,.uml-class.source{border-color:var(--accent)}.uml-class.source{box-shadow:0 0 0 4px var(--accent-soft)}.vo-stage{position:relative;display:flex;flex:1;min-height:0}.vo-stage .vo-canvas{flex:1}.vo-hsplit{flex:0 0 20px;margin:-10px 0;position:relative;z-index:5;cursor:row-resize;touch-action:none}.vo-hsplit:before{content:"";position:absolute;left:0;right:0;top:9px;height:2px;background:transparent}.vo-hsplit:after{content:"";position:absolute;left:50%;top:7px;width:36px;height:6px;margin-left:-18px;border-radius:3px;background:var(--border)}.vo-hsplit:hover:before,.vo-hsplit:focus-visible:before,.vo-hsplit.dragging:before,.vo-hsplit:hover:after,.vo-hsplit:focus-visible:after,.vo-hsplit.dragging:after{background:var(--accent)}.vo-dock.collapsed+.vo-hsplit:after,.vo-hsplit:has(+.vo-dock.collapsed):after{display:none}.vo-hsplit:focus-visible{outline:none}.vo-dock.collapsed+.vo-hsplit,.vo-hsplit:has(+.vo-dock.collapsed){cursor:default;pointer-events:none}.vo-dock{border-top:1px solid var(--border);background:var(--panel)}.vo-dock-bar{min-height:30px}.vo-dock-bar .vo-bar-title{font-size:13px}.vo-dock.collapsed .vo-inspector{display:none}.vo-inspector{display:grid;align-content:start;gap:8px;height:32vh;overflow:auto;padding:8px 12px 10px;font:13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-inspector-row>label.check,.vo-inspector label.check{flex:0 0 auto;display:inline-flex;align-items:center;gap:6px;padding-bottom:6px;color:var(--text);font-size:13px}.vo-inspector input[type=checkbox]{width:auto;margin:0;accent-color:var(--accent)}.vo-inspector fieldset.vo-inspector-ifaces{display:flex;flex-wrap:wrap;gap:4px 14px}.vo-inspector-empty{color:var(--muted)}.vo-inspector-row{display:flex;flex-wrap:wrap;align-items:end;gap:8px 12px}.vo-inspector-row>label{flex:1 1 160px;max-width:260px}.vo-inspector-ends{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:8px}.vo-inspector label{display:grid;gap:3px;color:var(--muted);font-size:12px}.vo-inspector input,.vo-inspector select{box-sizing:border-box;width:100%;min-width:0;padding:4px 6px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-inspector input:focus,.vo-inspector select:focus{border-color:var(--accent);outline:2px solid var(--accent-soft)}.vo-inspector fieldset{display:grid;grid-template-columns:minmax(0,1fr) 84px;align-items:start;gap:6px;min-width:0;margin:0;padding:6px 8px 8px;border:1px solid var(--border);border-radius:8px}.vo-inspector fieldset .vo-wide{grid-column:1 / -1}.vo-reading{display:flex;flex-wrap:wrap;align-items:center;gap:4px 8px;color:var(--muted)}.vo-reading b{color:var(--text)}.shell.visuobject .vo-reading button.text-button{padding:1px 8px;font-size:12px}.vo-inspector legend{padding:0 4px;color:var(--text);font-weight:600}.vo-inspector small{color:var(--muted);font-size:11px}.vo-inspector p{margin:0}.vo-inspector-kind{font-weight:600;padding-top:4px}.shell.visuobject .vo-inspector button.vo-danger{display:inline-flex;align-items:center;justify-self:start;gap:6px;color:#b3261e}.vo-inspector .vo-danger svg{width:15px;height:15px}.vo-insp-head{position:sticky;top:-8px;z-index:1;margin-top:-8px;padding-top:8px;background:var(--panel);display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:6px 12px;border-bottom:1px solid var(--border)}.vo-insp-head .vo-inspector-kind{padding:0}.vo-insp-tabs{display:flex;gap:2px}.shell.visuobject .vo-insp-tabs button{margin-bottom:-1px;padding:5px 12px;border:1px solid transparent;border-bottom:none;border-radius:6px 6px 0 0;background:none;color:var(--muted);font:13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer}.shell.visuobject .vo-insp-tabs button[aria-selected=true]{border-color:var(--border);background:var(--panel);color:var(--text);font-weight:600}.vo-members{width:100%;border-collapse:collapse}.vo-members th{padding:0 4px 3px;color:var(--muted);font-size:12px;font-weight:500;text-align:left}.vo-members td{padding:2px 4px;vertical-align:middle}.vo-members td.c{width:52px;text-align:center}.vo-members td:first-child{width:112px}.vo-members td:last-child{width:32px}.vo-members .icon svg{width:15px;height:15px}.vo-doc textarea{box-sizing:border-box;width:100%;max-width:780px;padding:4px 6px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;resize:vertical}.vo-insp-sep{align-self:stretch;width:1px;margin:0 4px;background:var(--border)}.vo-insp-label{align-self:center;color:var(--muted);font-size:12px}.uml-class.selected{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft);z-index:2}.uml-head{display:grid;justify-items:center;padding:4px 10px;font-weight:700}.uml-head small{font-weight:400;font-size:11px;color:var(--muted)}.uml-class.abstract .uml-name,.uml-member.abstract{font-style:italic}.uml-member.static{text-decoration:underline}.uml-class ul{list-style:none;margin:0;padding:3px 0;border-top:1.5px solid var(--text);min-height:12px}.uml-class.selected ul{border-top-color:var(--accent)}.uml-class li{padding:0 10px;white-space:nowrap}.uml-member.selected{background:var(--accent-soft)}.uml-add{color:var(--accent);font-style:italic;cursor:pointer;font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-size:12px}.uml-add:hover{text-decoration:underline}.uml-input{font:inherit;padding:0 4px;margin:0 -5px;border:1px solid var(--accent);border-radius:4px;outline:none;max-width:340px}.uml-tools{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center;padding:6px 10px;border-top:1px dashed var(--border);font:12px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:default}.uml-tools label{display:inline-flex;gap:4px;align-items:center}.uml-tools button.icon svg{width:16px;height:16px}.vo-ws{display:flex;flex-wrap:wrap;align-items:center;gap:6px;padding:8px 10px 4px;font-size:13px}.vo-ws[hidden]{display:none}.vo-ws.ask{margin:8px 10px 4px;padding:10px;border:1px solid var(--accent);border-radius:8px;background:var(--bg, #fff)}.vo-ws.ask p{flex-basis:100%;margin:0}.vo-ws.ask .hint{color:var(--muted);font-size:12px;margin-bottom:4px}.vo-ws-name{display:inline-flex;align-items:center;gap:6px;margin-right:auto;font-weight:600;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.shell.visuobject .vo-ws button.text-button{padding:2px 8px;border-radius:6px}.shell.visuobject .vo-ws button.text-button.primary{background:var(--accent);border-color:var(--accent);color:#fff}.vo-lines .vo-end-hit{fill:transparent;pointer-events:all;cursor:context-menu}.vo-lines .vo-label-svg.editable{pointer-events:all;cursor:pointer}.vo-lines .vo-label-svg.editable:hover{fill:var(--accent);text-decoration:underline}.vo-lines .vo-label-svg.placeholder{fill:var(--muted);opacity:.6;font-style:italic}.vo-endpop{position:absolute;z-index:12;display:grid;gap:8px;width:min(330px,calc(100% - 16px));box-sizing:border-box;padding:10px 12px;background:var(--panel);border:1px solid var(--accent);border-radius:10px;box-shadow:0 8px 24px #00000029;font:13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-endpop p{margin:0}.vo-endpop label{display:grid;gap:3px;color:var(--muted);font-size:12px}.vo-endpop input{box-sizing:border-box;min-width:0;padding:4px 6px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-endpop-row{display:flex;flex-wrap:wrap;align-items:center;gap:4px}.vo-endpop-row>span{flex-basis:100%;color:var(--muted);font-size:12px}.vo-endpop-row button{padding:2px 8px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:12px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer}.vo-endpop-row button[aria-pressed=true]{background:var(--accent);border-color:var(--accent);color:#fff}.vo-endpop-row input{width:64px}.vo-new{position:relative}.vo-new-menu{position:absolute;top:calc(100% + 4px);left:0;z-index:30;display:grid;min-width:190px;padding:4px;background:var(--panel);border:1px solid var(--border);border-radius:8px;box-shadow:0 8px 24px #00000024}.vo-new-menu[hidden]{display:none}.vo-new-menu button{display:flex;align-items:center;gap:8px;padding:6px 8px;border:0;border-radius:6px;background:none;color:var(--text);font:13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;text-align:left;cursor:pointer}.vo-new-menu button:hover,.vo-new-menu button:focus-visible{background:var(--accent-soft);outline:none}.vo-badge.folder.source{box-shadow:inset 0 3px #ffffff59,inset -5px -4px #8a6d2b}.confirm-card.vo-wizard{display:grid;gap:10px;width:min(520px,100%);max-width:none;max-height:calc(100vh - 32px);overflow:auto;box-sizing:border-box;font:14px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-wizard h2{margin:0;font-size:17px}.vo-wizard .hint{margin:0;color:var(--muted);font-size:13px}.vo-wizard>label{display:grid;grid-template-columns:130px minmax(0,1fr);align-items:center;gap:8px;color:var(--muted);font-size:13px}.vo-wizard input[type=text],.vo-wizard select{box-sizing:border-box;width:100%;min-width:0;padding:5px 7px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:inherit}.vo-wizard fieldset{margin:0;padding:6px 10px 8px;border:1px solid var(--border);border-radius:8px}.vo-wizard legend{padding:0 4px;color:var(--muted);font-size:13px}.vo-wizard-row{display:flex;flex-wrap:wrap;align-items:center;gap:8px 16px}.vo-wizard-row select,.vo-wizard-row input[type=text]{width:auto;flex:1 1 160px}.vo-wizard-list{display:grid;gap:4px}.vo-wizard .check{display:inline-flex;align-items:center;gap:6px}.vo-wizard code{font-size:12px}.vo-wizard-error{min-height:1em;margin:0;color:#b3261e;font-size:13px}.vo-wizard .group button.primary{background:var(--accent);border-color:var(--accent);color:#fff}@media(max-width:560px){.vo-wizard>label{grid-template-columns:1fr}}.vo-dirty{color:var(--accent)}.vo-tree-tools{display:flex;flex-wrap:wrap;gap:6px;padding:8px 10px 4px}.shell.visuobject .vo-tree-tools button.text-button{padding:2px 8px;border-radius:6px}.vo-tree{flex:1;min-height:0;overflow:auto;padding:6px;font:13px/1.3 system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.vo-tree ul{list-style:none;margin:0;padding:0}.vo-row{display:flex;align-items:center;gap:6px;min-height:28px;padding:0 6px 0 calc(4px + var(--depth, 0) * 14px);border-radius:7px;cursor:pointer;-webkit-user-select:none;user-select:none;white-space:nowrap;outline:none}.vo-row:hover{background:var(--accent-faint)}.vo-row:focus-visible{box-shadow:inset 0 0 0 2px var(--accent-soft)}.vo-row.active .vo-label{font-weight:600}.vo-row.selected{background:var(--accent-soft);color:var(--text)}.vo-twisty,.vo-twisty-space{flex:none;width:18px;height:18px}.vo-twisty{display:grid;place-items:center;padding:0;border:0;border-radius:4px;background:none;color:var(--muted);cursor:pointer}.vo-twisty svg{transition:transform .15s ease}[aria-expanded=true]>.vo-row .vo-twisty svg{transform:rotate(90deg)}.vo-twisty:hover{color:var(--accent)}.vo-label{min-width:0;overflow:hidden;text-overflow:ellipsis}.vo-type{color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis}.vo-row .uml-input{margin:0;font:inherit}.vo-row-delete{margin-left:auto;visibility:hidden}.vo-tree button.vo-row-delete{width:24px;height:24px}.vo-tree button.vo-row-delete svg{width:14px;height:14px}.vo-row:hover .vo-row-delete,.vo-row.selected .vo-row-delete{visibility:visible}.vo-tree-empty{padding:6px 10px 6px 52px;color:var(--muted);font-size:12px;white-space:normal}.vo-badge{flex:none;display:inline-grid;place-items:center;width:16px;height:16px;border-radius:4px;font:700 10px/1 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#fff}.vo-badge.project{background:var(--accent);border-radius:5px;box-shadow:inset 0 -5px #0000002e}.vo-badge.project.closed{opacity:.55}.vo-badge.folder{width:17px;height:13px;border-radius:2px 4px 3px 3px;background:#e0a63b;box-shadow:inset 0 3px #ffffff59}.vo-badge.package{width:14px;height:14px;border-radius:3px;background:#b07a2a;transform:rotate(45deg) scale(.82)}.vo-badge.package.empty{background:none;border:1.5px solid #b07a2a}.vo-badge.file{background:#e66a2c;border-radius:3px 6px 3px 3px}.vo-badge.type.class{background:#2f7d32;border-radius:50%}.vo-badge.type.class.abstract{background:#6a9c3a;font-style:italic}.vo-badge.type.interface{background:#7b4fb0;border-radius:50%}.vo-badge.field,.vo-badge.method{width:9px;height:9px;margin:0 3px}.vo-badge.field{border-radius:2px}.vo-badge.method{border-radius:50%}.vo-badge.public{background:#2f9e44}.vo-badge.private{background:#d6453d}.vo-badge.protected{background:#e0a63b}.vo-badge.package.field,.vo-badge.package.method{background:#3b82c4;transform:none;border:0}.vo-badge.static{box-shadow:0 0 0 2px var(--panel),0 0 0 3px currentColor;color:var(--muted)}@media(max-width:760px){.vo-columns{grid-template-columns:1fr;grid-auto-rows:auto;overflow:auto}.vo-splitter{display:none}.vo-explorer:not(.collapsed){height:260px}.vo-code:not(.collapsed){height:320px}.vo-diagram:not(.collapsed){min-height:420px}.vo-pane{border-bottom:1px solid var(--border)}.vo-strip{writing-mode:horizontal-tb;padding:10px}}.shell.visuobject .vo-run-group button.vo-run{color:var(--accent);font-weight:600}.vo-console{display:flex;flex-direction:column;height:32vh;min-height:120px;border-top:1px solid var(--border)}.vo-console[hidden],.vo-swing[hidden]{display:none}.vo-console .vo-dock-bar{gap:10px}.vo-console-status{flex:1;color:var(--muted);font-size:12px}.vo-console-status.error{color:#b3261e}.vo-swing{display:flex;height:60%;padding:6px}.vo-console-lines{flex:1;min-height:0;overflow:auto;margin:0;padding:6px 12px;font-size:13px;white-space:pre-wrap}.uml-note{position:absolute;min-width:120px;max-width:260px;padding:8px 22px 8px 10px;background:#fffbe6;border:1.2px solid var(--text);clip-path:polygon(0 0,calc(100% - 14px) 0,100% 14px,100% 100%,0 100%);font:13px/1.45 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color:#17292e;cursor:grab;-webkit-user-select:none;user-select:none;touch-action:none;z-index:1}.uml-note:after{content:"";position:absolute;top:0;right:0;width:14px;height:14px;background:linear-gradient(to bottom left,transparent 50%,#e9dfae 50%);border-left:1.2px solid var(--text);border-bottom:1.2px solid var(--text)}.uml-note.selected{outline:3px solid var(--accent-soft);border-color:var(--accent);clip-path:none}.uml-note .uml-connect{z-index:2}.uml-note-text{white-space:pre-wrap;overflow-wrap:anywhere}.uml-note-text.placeholder{color:#8a8a8a;font-style:italic}.uml-note-edit{box-sizing:border-box;width:220px;border:1px solid var(--accent);background:#fffef5;font:inherit;resize:both}.vo-note-link{stroke-dasharray:5 4}.vo-canvas[hidden],.vo-palette[hidden]{display:none}.vo-diagram-switch{display:flex;gap:2px;padding:2px;border:1px solid var(--border);border-radius:8px;background:var(--panel)}.shell.visuobject .vo-diagram-switch button{padding:3px 10px;border:none;border-radius:6px;background:none;color:var(--muted);font:600 13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer}.shell.visuobject .vo-diagram-switch button[aria-selected=true]{background:var(--accent);color:#fff}.vo-obj-palette{align-items:center;gap:8px;padding:6px 10px}.vo-obj-new{display:flex;align-items:center;gap:6px;font-size:13px}.vo-obj-new select{padding:3px 6px;border:1px solid var(--border);border-radius:6px;background:var(--panel);color:var(--text);font:13px system-ui,-apple-system,Segoe UI,Roboto,sans-serif}.uml-object .uml-objname{text-decoration:underline;font-weight:600}.uml-object.unknown{border-style:dashed}.uml-object .uml-member.changed,.vo-objlink.changed{animation:vo-changed 1.2s ease-out}.uml-object .uml-member.changed{background:#fff3b0}@keyframes vo-changed{0%{background:#ffe066}}.vo-obj-values td:first-child{width:auto;white-space:nowrap}.vo-obj-values td:last-child{width:50%}.vo-dbg-controls{display:inline-flex;gap:2px}.vo-dbg-controls[hidden]{display:none}.vo-console-lines .vo-out-err{color:#b3261e}.vo-console-lines .vo-out-in{color:var(--accent);font-weight:600}.vo-console-input{display:flex;align-items:center;gap:6px;padding:4px 12px 8px;color:var(--accent)}.vo-console-input[hidden]{display:none}.vo-console-input input{flex:1;font-size:13px;padding:4px 6px;border:1px solid var(--accent);border-radius:4px}.vo-dbg{display:grid;grid-template-columns:minmax(160px,1fr) minmax(220px,2fr);gap:12px;padding:8px 12px}.vo-dbg h3{margin:0 0 6px;font-size:12px;font-weight:600;color:var(--muted)}.vo-dbg ol{list-style:none;margin:0;padding:0}.vo-dbg-frame{display:block;width:100%;text-align:start;padding:3px 6px;border:0;border-radius:4px;background:none;color:inherit;font:inherit;cursor:pointer}.vo-dbg-frame.on{background:var(--accent-soft)}.vo-dbg-frame small{color:var(--muted)}.vo-dbg-vars tr.changed td{background:#ffd60a59}@media(max-width:760px){.vo-dbg{grid-template-columns:1fr}}.vo-seqcanvas{overflow:auto}.vo-seqcanvas .vo-empty[hidden]{display:none}.vo-seq{display:block;font:12px ui-monospace,SFMono-Regular,Menlo,monospace}.vo-seq-head rect{fill:var(--panel);stroke:var(--text);stroke-width:1.2}.vo-seq-head text{fill:var(--text);text-decoration:underline}.vo-seq-head.static text{text-decoration:none;font-weight:600}.vo-seq-life{stroke:var(--muted);stroke-dasharray:5 4}.vo-seq-bar{fill:var(--panel);stroke:var(--text);stroke-width:1}.vo-seq-bar.running{fill:var(--accent-soft)}.vo-seq-msg{cursor:pointer}.vo-seq-msg line,.vo-seq-msg path{stroke:var(--text);stroke-width:1.2}.vo-seq-msg .head.filled{fill:var(--text)}.vo-seq-msg text{fill:var(--text)}.vo-seq-msg.return line,.vo-seq-msg.create line{stroke-dasharray:6 4}.vo-seq-msg.exception line,.vo-seq-msg.exception text{stroke:#b3261e;fill:#b3261e}.vo-seq-msg.current line,.vo-seq-msg.current path{stroke:#e0a800;stroke-width:2.4}.vo-seq-msg.current .head.filled{fill:#e0a800}.vo-seq-msg.current text{font-weight:700}.vo-seq-msg:hover text{fill:var(--accent)}.vo-seq-more{fill:var(--muted)}.palette-head{display:flex;align-items:center;gap:4px;margin:0 0 4px}.palette-head h2{flex:1;min-width:0;margin:0}.palette-hide{min-height:26px;padding:2px 5px}.app.compact-palette .palette-head{flex-direction:column;gap:2px}.palette-rail{display:none;grid-area:pal;width:auto}.app.palette-hidden .palette,.app.palette-hidden .palette-resizer{display:none}.app.palette-hidden:not(.simulating) .palette-rail{display:flex}@media(max-width:820px){.palette-head{display:none}.app.palette-hidden .palette{display:block}.app.palette-hidden .palette-rail{display:none!important}}.diagram-tools .pseudocode-toggle{gap:4px;margin:0 4px;white-space:nowrap;cursor:pointer}:root.visuflow-theme{--bg: #f3f0fb;--text: #221a35;--muted: #625a75;--border: #ddd6ee;--accent: #6f42c1;--accent-soft: rgba(111, 66, 193, .2);--accent-faint: rgba(111, 66, 193, .08);--accent-glow: rgba(111, 66, 193, .35)}.visuflow-flow{color:var(--accent)}.vf-logo{width:22px;height:22px;background:no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236f42c1' stroke-width='2'%3E%3Crect x='6' y='1.5' width='12' height='5' rx='2.5'/%3E%3Cpath d='M12 6.5v3M12 9.5l5 4-5 4-5-4z M12 17.5v2'/%3E%3Crect x='7' y='19.5' width='10' height='3' rx='0.5'/%3E%3C/svg%3E")}.shell.visuflow button.text-button{padding:5px 10px;border:1px solid var(--border);border-radius:8px;background:var(--panel);color:var(--text);font:inherit;cursor:pointer}.shell.visuflow button.text-button:hover:not(:disabled){border-color:var(--accent);color:var(--accent)}.shell.visuflow button:disabled{opacity:.45;cursor:default}.vf-export{position:relative}.vf-menu{position:absolute;top:calc(100% + 6px);inset-inline-start:0;z-index:30;display:grid;min-width:190px;padding:4px;background:var(--panel);border:1px solid var(--border);border-radius:10px;box-shadow:0 8px 24px #00000026}.vf-menu[hidden]{display:none}.vf-menu button{padding:8px 10px;border:0;border-radius:6px;background:none;color:var(--text);font:inherit;text-align:start;cursor:pointer}.vf-menu button:hover,.vf-menu button:focus-visible{background:var(--accent-faint);color:var(--accent)}.vf-main{display:flex;flex:1;min-height:0;border-top:1px solid var(--border)}.vf-palette{display:flex;flex-direction:column;gap:8px;width:190px;flex-shrink:0;padding:10px;overflow-x:hidden;overflow-y:auto;background:var(--panel);border-inline-end:1px solid var(--border)}.vf-palette h2{margin:0;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}.vf-shapes{display:grid;gap:6px}.vf-shape{display:flex;align-items:center;gap:8px;padding:4px 6px;border:1px solid var(--border);border-radius:8px;background:var(--panel);color:var(--text);font:inherit;font-size:13px;text-align:start;cursor:grab;touch-action:none;-webkit-user-select:none;user-select:none}.vf-shape:hover,.vf-shape:focus-visible{border-color:var(--accent);background:var(--accent-faint)}.vf-shape svg{flex-shrink:0;width:48px;height:27px}.vf-hint{margin:4px 0 0;font-size:12px;line-height:1.4;color:var(--muted)}.vf-stage{display:flex;flex-direction:column;flex:1;min-width:0}.vf-titlebar{padding:8px 12px;background:var(--panel);border-bottom:1px solid var(--border)}.vf-title{width:min(420px,100%);padding:6px 8px;border:1px solid var(--border);border-radius:6px;font:inherit;font-weight:600}.vf-scroll{position:relative;flex:1;overflow:auto;background-color:#fff;background-image:radial-gradient(circle,#d9d4e6 1px,transparent 1.2px);background-size:20px 20px;background-attachment:local}.vf-canvas{display:block;touch-action:none;user-select:none;-webkit-user-select:none}.vf-node{cursor:move}.vf-node.selected>:first-child{stroke:var(--accent);stroke-width:3;filter:drop-shadow(0 0 4px var(--accent-glow))}.vf-node.drop-target>:first-child{stroke:var(--accent);stroke-width:3;stroke-dasharray:6 3}.vf-edge{cursor:pointer}.vf-edge.selected .vf-edge-line{stroke:var(--accent);stroke-width:3}.vf-port{cursor:crosshair}.vf-port-dot{fill:#fff;stroke:var(--accent);stroke-width:2}.vf-port.used .vf-port-dot{fill:var(--accent);opacity:.55}.vf-port:hover .vf-port-dot{fill:var(--accent);opacity:1}.vf-drag-line{stroke:var(--accent);stroke-width:2;stroke-dasharray:6 4;pointer-events:none}.vf-editor{position:absolute;z-index:5;box-sizing:border-box;padding:4px 6px;border:2px solid var(--accent);border-radius:6px;background:#fff;color:var(--text);font:14px -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif;line-height:16px;text-align:center;resize:none;overflow:hidden}.vf-ghost{position:fixed;z-index:100;pointer-events:none;transform:translate(-50%,-50%) scale(1.6);opacity:.85}.vf-status{margin:0;padding:6px 12px;font-size:12px;color:var(--muted);background:var(--panel);border-top:1px solid var(--border)}@media(max-width:700px){.vf-main{flex-direction:column}.vf-palette{flex-direction:row;width:auto;overflow-x:auto;border-inline-end:0;border-bottom:1px solid var(--border)}.vf-palette h2,.vf-hint{display:none}.vf-shapes{display:flex}.vf-shape{flex-direction:column;gap:2px;font-size:11px}}[data-bot-color=fahren],.bot-fahren{--bot: #c2410c}[data-bot-color=licht],.bot-licht{--bot: #84cc16}[data-bot-color=ton],.bot-ton{--bot: #c026d3}[data-bot-color=anzeige],.bot-anzeige{--bot: #4f46e5}[data-bot-color=warten],.bot-warten{--bot: #6b7280}[data-bot-color=sensor],.bot-sensor{--bot: #0891b2}[data-bot-color=operator],.bot-operator{--bot: #16a34a}[data-bot-color=variable],.bot-variable{--bot: #ea580c}:root:not(.no-code-scopes) .palette-item[data-bot-color]{border-left:4px solid var(--bot)}:root:not(.no-code-scopes) .palette-group[data-bot-color] button:after,:root:not(.no-code-scopes) .learn-text h3[class^=bot-]:before{content:"";display:inline-block;width:9px;height:9px;flex-shrink:0;border-radius:50%;background:var(--bot)}:root:not(.no-code-scopes) .palette-group[data-bot-color] button:before{order:-2}:root:not(.no-code-scopes) .palette-group[data-bot-color] button:after{order:-1}:root:not(.no-code-scopes) .learn-text h3[class^=bot-]:before{margin-inline-end:7px;vertical-align:1px}.learn-drive-map{display:block;width:100%;max-width:320px;margin:8px auto;touch-action:manipulation}.learn-drive-map .cell{stroke:#cbd5e1;stroke-width:.6;cursor:pointer}.learn-drive-map:not(.done) .cell:hover,.learn-drive-map .cell:focus-visible{stroke:var(--accent);stroke-width:2;outline:none}.learn-drive-map.done .cell{cursor:default}.learn-drive-map .cell.right{stroke:#16a34a;stroke-width:3}.learn-drive-map .cell.wrong{stroke:#dc2626;stroke-width:3}.learn-drive-map .trail{stroke:var(--accent);stroke-width:2;stroke-dasharray:3 2;pointer-events:none}.learn-drive-map .robot{transition:transform .4s ease-in-out;pointer-events:none}.learn-drive-map .robot .body{fill:#0ea5b7;stroke:#0f172a;stroke-width:1}.learn-drive-map .robot .nose{fill:#fff}@media(prefers-reduced-motion:reduce){.learn-drive-map .robot{transition:none}}.diagram-tools .blocks-toggle{display:inline-flex;align-items:center;gap:5px;height:30px;margin-inline-start:6px;padding:0 10px;white-space:nowrap}.diagram-tools .blocks-toggle[hidden]{display:none}@container (max-width: 520px){.diagram-tools .blocks-toggle span+span{display:none}}.blocks-view{--bk-stmt: #f2b100;--bk-loop: #ec5fa6;--bk-if: #3b9cf0;--bk-plain: #9aa3ae;--bk-bump: 6px;--bk-workspace: #fafafa;flex:1;min-height:0;overflow:auto;scrollbar-gutter:stable;background:var(--bk-workspace);padding:10px 16px 32px;font:500 calc(15px * var(--bk-zoom, 1)) / 1.5 system-ui,-apple-system,sans-serif;user-select:none;-webkit-user-select:none}.blocks-view[hidden]{display:none}.bk-hint{margin:0 0 6px;max-width:44em;color:var(--muted);font-size:13px;line-height:1.4}.bk-stack{padding-top:14px}.bk{--bk-color: var(--bk-stmt);--bk-c: color-mix(in srgb, var(--bk-color) 42%, #fff);--bk-e: color-mix(in srgb, var(--bk-color) 80%, #000);position:relative;width:fit-content;min-width:12em;max-width:100%;border:1px solid var(--bk-e);border-radius:5px;background:var(--bk-c);color:#000;cursor:pointer}.bk-loop{--bk-color: var(--bk-loop)}.bk-if{--bk-color: var(--bk-if)}.bk-hat{--bk-color: var(--bk-stmt);cursor:default}:root.no-code-scopes .bk{--bk-color: var(--bk-plain)}:root:not(.no-code-scopes) .bk[data-bot-color]{border-left:6px solid var(--bot)}.bk+.bk,.bk+.bk-empty,.bk-empty+.bk{margin-top:-1px}.bk-simple:after,.bk-c:after,.bk-hat:after,.bk-c>.bk-head:first-child:after,.bk-clause:after{content:"";position:absolute;z-index:1;left:12px;bottom:calc(-1 * var(--bk-bump) - 1px);width:1.6em;height:var(--bk-bump);background:var(--bk-c);border:1px solid var(--bk-e);border-top:none;border-radius:0 0 6px 6px}.bk-c>.bk-head:first-child:after,.bk-clause:after{left:28px}.bk-head,.bk-foot{position:relative;display:flex;flex-direction:column;padding:calc(var(--bk-bump) - 1px) 10px 4px}.bk-foot{min-height:calc(var(--bk-bump) + 10px);padding-top:var(--bk-bump)}.bk-line{white-space:pre-wrap}.bk-hat{border-top-left-radius:22px;border-top-right-radius:10px}.bk-hat>.bk-head{flex-direction:row;flex-wrap:wrap;column-gap:1em;padding-top:8px;padding-left:14px}.bk-hat:before{content:"";position:absolute;left:-1px;top:-9px;width:7em;height:18px;border:1px solid var(--bk-e);border-bottom:none;border-radius:50% 50% 0 0/100% 100% 0 0;background:var(--bk-c);clip-path:inset(0 0 8px 0)}.bk-body{margin-left:16px;margin-right:-1px;padding-right:2em;min-height:1em;background:var(--bk-workspace);border:1px solid var(--bk-e);border-right:none;border-radius:4px 0 0 4px;color:var(--text)}.bk-body>:first-child{margin-top:-1px}.bk-cap{border-bottom-left-radius:2px;border-bottom-right-radius:10px}.bk-call>.bk-head{margin:0 6px;border-inline:2px solid var(--bk-e)}.bk-empty{width:fit-content;min-width:12em;margin:6px 8px;padding:2px 10px;border:1.5px dashed var(--border);border-radius:6px;color:var(--muted);font-style:italic;font-size:.9em;cursor:pointer}.bk .tk-kw{font-weight:700}.bk .tk-lit,.bk .tk-str{padding:0 6px;border-radius:999px;background:#fff;box-decoration-break:clone;-webkit-box-decoration-break:clone}.bk .tk-lit:empty{display:inline-block;min-width:2.5ch;min-height:1.2em;vertical-align:middle}.bk .tk-var{padding:0 6px;border-radius:999px;background:#fdba74;color:#431407;font-weight:600}.bk .tk-op{padding:1px 6px;border:1px solid #15803d;border-radius:999px;background:#86efac;color:#052e16}.bk .tk-sensor{padding:0 6px;border:1px solid color-mix(in srgb,#0891b2 80%,#000);border-radius:999px;background:color-mix(in srgb,#0891b2 35%,#fff)}:root.no-code-scopes .bk .tk-sensor{border-color:#6b7280;background:#e5e7eb}.bk-sel{outline:3px solid var(--accent);outline-offset:1px;z-index:2}.bk-exec{outline:3px solid var(--text);outline-offset:1px;box-shadow:0 0 12px 4px var(--accent-glow);z-index:2}.blocks-view.bk-simulating .bk,.blocks-view.bk-simulating .bk-empty{cursor:default}.bk-stack{position:relative}.bk-drop-line{position:absolute;z-index:5;height:6px;border-radius:3px;background:var(--accent);box-shadow:0 0 0 2px #fff;pointer-events:none}.bk-drop,.bk-empty.bk-drop{outline:3px dashed var(--accent);outline-offset:1px;z-index:2}.bk-trash{display:none;position:sticky;bottom:0;margin:16px 0 0 auto;width:fit-content;padding:10px 16px;border:2px dashed var(--border);border-radius:12px;background:var(--panel);color:var(--muted);font-size:14px}.blocks-view.bk-moving .bk-trash{display:block}.bk-trash.bk-trash-hot{border-color:#dc2626;background:#fee2e2;color:#991b1b}.blocks-view:not(.bk-simulating) .bk:not(.bk-hat){touch-action:pan-y}.blocks-view:not(.bk-simulating) .bk [data-value]{cursor:text}.blocks-view:not(.bk-simulating) .bk [data-value]:hover{box-shadow:0 0 0 2px var(--accent)}.bk-input{box-sizing:border-box;min-width:2ch;padding:0 6px;border:2px solid var(--accent);border-radius:999px;background:#fff;color:#000;font:inherit;text-align:center;outline:none}.bk .tk-sensor.tk-bool{padding:0 12px;border:none;border-radius:0;clip-path:polygon(8px 0,calc(100% - 8px) 0,100% 50%,calc(100% - 8px) 100%,8px 100%,0 50%)}.bk .tk-sensor.bk-drop,.bk [data-value].bk-drop{outline:3px dashed var(--accent);outline-offset:1px;clip-path:none}.app.block-mode .palette-sensor code{display:inline-block;margin-top:2px;padding:0 8px;border:1px solid color-mix(in srgb,#0891b2 80%,#000);border-radius:999px;background:color-mix(in srgb,#0891b2 35%,#fff);color:#000}.app.block-mode .palette-sensor code.sensor-bool,.drag-ghost.drag-sensor.sensor-bool{padding:0 12px;border:none;border-radius:0;clip-path:polygon(8px 0,calc(100% - 8px) 0,100% 50%,calc(100% - 8px) 100%,8px 100%,0 50%)}.drag-ghost.drag-sensor{border:1px solid color-mix(in srgb,#0891b2 80%,#000);border-radius:999px;background:color-mix(in srgb,#0891b2 35%,#fff);color:#000;font-family:ui-monospace,monospace}.app.block-mode .palette-item:not(.palette-sensor),.drag-ghost.drag-block{--bk-color: #f2b100;--bk-c: color-mix(in srgb, var(--bk-color) 42%, #fff);--bk-e: color-mix(in srgb, var(--bk-color) 80%, #000);border:1px solid var(--bk-e);border-radius:5px;background:var(--bk-c);color:#000;font-weight:600}.app.block-mode .palette-item[data-scope=loop],.drag-ghost.drag-block[data-scope=loop]{--bk-color: #ec5fa6}.app.block-mode .palette-item[data-scope=if],.drag-ghost.drag-block[data-scope=if]{--bk-color: #3b9cf0}:root.no-code-scopes .app.block-mode .palette-item:not(.palette-sensor),:root.no-code-scopes .drag-ghost.drag-block{--bk-color: #9aa3ae}.app.block-mode .palette-item:not(.palette-sensor) .icon{display:none}.app.block-mode .palette-item:not(.palette-sensor):hover,.app.block-mode .palette-item:not(.palette-sensor):focus-visible{border-color:var(--bk-e);box-shadow:0 0 0 2px var(--accent)}.app.block-mode .palette-item:not(.palette-sensor){position:relative;margin-bottom:4px}:root:not(.no-code-scopes) .app.block-mode .palette-item[data-bot-color]{border-left:6px solid var(--bot)}.app.block-mode .palette-item:not(.palette-sensor):after,.drag-ghost.drag-block:after{content:"";position:absolute;z-index:1;left:12px;bottom:-7px;width:1.6em;height:6px;background:var(--bk-c);border:1px solid var(--bk-e);border-top:none;border-radius:0 0 6px 6px}.app.block-mode .palette-item[data-scope=loop],.app.block-mode .palette-item[data-scope=if],.drag-ghost.drag-block[data-scope=loop],.drag-ghost.drag-block[data-scope=if]{padding-bottom:26px}.app.block-mode .palette-item[data-scope=loop]:before,.app.block-mode .palette-item[data-scope=if]:before,.drag-ghost.drag-block[data-scope=loop]:before,.drag-ghost.drag-block[data-scope=if]:before{content:"";position:absolute;left:14px;right:-1px;bottom:8px;height:12px;background:var(--panel);border:1px solid var(--bk-e);border-right:none;border-radius:4px 0 0 4px}.drag-ghost.drag-block{padding:6px 12px;min-width:9em}.app.block-mode .palette-op code,.drag-ghost.drag-sensor.drag-op{border-color:#15803d;background:#86efac}.app.block-mode .palette-var code,.drag-ghost.drag-sensor.drag-var{border-color:#c2410c;background:#fdba74}.blocks-view.bk-picture{flex:none;overflow:visible;padding:4px 8px 10px;border-radius:10px;font-size:14px;margin-bottom:8px}.bk-picture .bk-hint,.bk-picture .bk-trash{display:none}.bk-picture .bk-stack{padding-top:6px} diff --git a/website/app/assets/it-B0g-vqj6.js b/website/app/assets/it-B0g-vqj6.js deleted file mode 100644 index 9a12e06d..00000000 --- a/website/app/assets/it-B0g-vqj6.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Testi presenti solo nella versione web. Tutto il resto proviene dai Localizable.strings dell’app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagramma VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Canc"; -"web_key_shift" = "Maiusc"; -"web_export" = "Esporta…"; -"web_export_title" = "Salva come immagine o PDF, oppure stampa"; -"web_export_png" = "Immagine (PNG)"; -"web_export_svg" = "Grafica vettoriale (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "File VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Esportazione non riuscita: %@"; -"web_print_hint" = "Usa “Esporta… → PDF” per salvare il diagramma e stamparlo da lì."; -"web_delete" = "Elimina"; -"web_language_title" = "Lingua dell’interfaccia"; -"web_simulation_start" = "Avvia simulazione"; -"web_simulation_end" = "Termina simulazione"; -"web_simulation_start_title" = "Esegui il diagramma passo dopo passo"; -"web_simulation_end_title" = "Termina la simulazione e continua a modificare"; -"web_palette_hint" = "Trascina nel diagramma"; -"web_tabs_label" = "Diagrammi"; -"web_status_modified" = "modificato"; -"web_inspector_title" = "Proprietà"; -"web_inspector_resize" = "Cambia l’altezza dell’ispettore oggetti: trascina o scorri la linea su o giù (doppio clic: predefinito)"; -"web_inspector_help_drop" = "Trascina gli elementi dalla tavolozza su un riquadro: in alto = prima, in basso = dopo, su “ø” = sostituisci. Gli elementi esistenti si possono spostare con il mouse."; -"web_inspector_help_inline" = "Doppio clic o doppio tocco su un elemento per modificarne il testo direttamente nel diagramma (Invio applica, Esc annulla). Anche i nomi dei casi."; -"web_inspector_help_files" = "Puoi anche trascinare file nella finestra."; -"web_case_name" = "Nome del caso"; -"web_case_name_placeholder" = "es. 1, ok, default"; -"web_copied" = "Elemento copiato."; -"web_discard_message_fmt" = "Le modifiche a “%@” andranno perse."; -"web_err_unreadable_fmt" = "“%@” non è un file VisuStruct leggibile (%@)."; -"web_saved_fmt" = "“%@” salvato."; -"web_saved_txt_fmt" = "Salvato come “%@”. Rimuovi l’estensione .txt per usarlo."; -"web_drop_impossible" = "Impossibile rilasciare qui"; -"web_sim_delay" = "Pausa per passo"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Linguaggio"; -"web_codegen_comments" = "Testo del diagramma come commenti"; -"web_codegen_empty_else" = "Genera ramo else vuoto nel codice"; -"web_copied_check" = "Copiato ✓"; -"web_copy_manual" = "Selezionato, premi Ctrl+C per copiare"; -"web_run_stopped_loop" = "Esecuzione interrotta: possibile ciclo infinito."; -"web_run_cancelled" = "Esecuzione annullata."; -"web_run_error" = "Errore durante l’esecuzione."; -"web_run_dropped_fmt" = "… %d righe precedenti nascoste"; -"web_sim_call_arguments_fmt" = "La chiamata non corrisponde all’intestazione di %@. Controlla i valori tra parentesi."; -"web_sim_missing_return_fmt" = "%@ è terminato senza restituire un valore con return."; -"web_sim_file_not_found_fmt" = "Non esiste nessun file chiamato %@."; -"web_sim_end_of_file_fmt" = "Il file %@ non ha più righe."; -"web_sim_null_value_fmt" = "%@ è null."; -"web_sim_unknown_variable_case_fmt" = "Variabile sconosciuta: %1$@. Java distingue tra maiuscole e minuscole: intendevi %2$@?"; -"web_sim_written_file_fmt" = "%@ (salvato)"; -"web_site_home" = "Pagina iniziale"; -"web_site_imprint" = "Note legali"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmazione"; -"web_live_hide" = "Nascondi programmazione"; -"web_scale_canvas" = "Dimensione testo: diagramma"; -"web_scale_code" = "Dimensione testo: programmazione"; -"web_live_indent" = "Indenta"; -"web_live_indent_title" = "Indenta il codice: quattro spazi per livello (righe selezionate, altrimenti tutto)"; -"web_live_show" = "Mostra programmazione"; -"web_live_resize" = "Modifica la larghezza dell’area codice (doppio clic: ripristina)"; -"web_live_hint" = "Il codice cresce con il tuo diagramma. Grigio = segnaposto: scrivi nell’ispettore e il tuo testo appare qui. Clicca su una riga per selezionarne l’elemento."; -"web_live_ph_selector" = "variabile"; -"web_live_ph_code" = "Il tuo codice"; -"web_live_ph_end" = "fine"; -"web_live_language" = "Linguaggio di programmazione"; -"web_live_java_class" = "Come classe"; -"web_live_java_class_hint" = "Nome della classe = titolo del diagramma"; -"web_live_java_main" = "Con metodo main"; -"web_settings_display" = "Visualizzazione"; -"web_live_swing" = "Java vero (con Swing)"; -"web_live_swing_hint" = "Java vero con finestre (CheerpJ). Scarica circa 40 MB una sola volta, alla prima esecuzione."; -"web_live_swing_windows" = "Finestre Java"; -"web_live_swing_hide" = "Chiudi finestre Java"; -"web_live_swing_loading" = "Caricamento di Java (circa 40 MB la prima volta) …"; -"web_live_swing_compiling" = "Compilazione …"; -"web_live_swing_running" = "Il programma è in esecuzione."; -"web_live_swing_compile_failed" = "Errore di compilazione, vedi sopra."; -"web_live_swing_failed" = "Impossibile caricare Java. Controlla la connessione a internet."; -"web_live_hint_edit" = "Scrivi qui codice Java e il diagramma si costruisce da solo. Oppure lavora nel diagramma e il codice cresce con lui. Grigio = segnaposto, basta scriverci sopra."; -"web_live_edit_label" = "Codice Java"; -"web_parse_line_fmt" = "Riga %d: %@"; -"web_parse_missing" = "Qui manca ancora: %@"; -"web_parse_extra" = "Di troppo: %@"; -"web_parse_unclosed_string" = "Manca il %@ di chiusura."; -"web_parse_else_without_if" = "Questo «else» non ha un «if»."; -"web_parse_case_outside_switch" = "«case» e «default» vanno dentro uno «switch»."; -"web_parse_missing_do_while" = "Dopo «do { … }» manca ancora «while (condizione);»."; -"web_parse_more_methods" = "Per ora qui conta un solo metodo. Crea gli altri sottoprogrammi in schede separate."; -"web_parse_missing_import" = "Manca %@ in cima. Senza questa riga Java non conosce Scanner."; -"web_parse_missing_scanner" = "Manca prima %@ Senza questa riga lo Scanner non esiste."; -"web_parse_unknown_type" = "Java non conosce questo tipo di dato. Intendevi %@?"; -"web_quickfix_insert_fmt" = "Suggerimento: inserisci %@"; -"web_quickfix_replace_fmt" = "Suggerimento: %1$@ invece di %2$@"; -"web_parse_wrong_case" = "Java distingue tra maiuscole e minuscole. La forma corretta è %@."; -"web_editor_title" = "Diagramma"; -"web_editor_show" = "Mostra diagramma"; -"web_editor_hide" = "Nascondi diagramma"; -"web_keywords_title" = "Parole chiave del codice"; -"web_keywords_hint" = "if, while, for al posto dei nomi degli elementi"; -"web_palette_compact" = "Elementi sempre compatti"; -"web_palette_compact_hint" = "Simbolo con il nome piccolo sotto, altrimenti solo con la colonna stretta"; -"web_about_version_fmt" = "Versione %@"; -"web_about_software" = "Software utilizzato"; -"web_about_software_text" = "L’editor di codice usa CodeMirror e Lezer (licenza MIT). Con il supporto Swing, VisuStruct carica CheerpJ di Leaning Technologies (licenza Community) e javac di OpenJDK."; -"web_about_licenses" = "Testi delle licenze"; -"web_live_run" = "Esegui"; -"web_live_run_title" = "Esegui il programma: output e input compaiono sotto"; -"web_live_run_finished" = "Programma terminato."; -"web_live_run_stopped" = "Fermato."; -"web_console" = "Console"; -"web_console_hide" = "Nascondi console"; -"web_console_resize" = "Ridimensiona la console"; -"web_diagram_tools" = "Strumenti del diagramma"; -"web_files_title" = "File"; -"web_files_show" = "Mostra file"; -"web_files_hide" = "Nascondi file"; -"web_files_resize" = "Ridimensiona file"; -"web_files_new_folder" = "Nuova cartella"; -"web_files_switch" = "Scegli un’altra cartella"; -"web_files_delete" = "Elimina"; -"web_files_in_browser" = "Nella memoria di questo browser"; -"web_files_save_hint_fmt" = "Salvando, il programma va in %@."; -"web_files_empty" = "Ancora niente di salvato. Quando salvi, VisuStruct crea qui una cartella con la data di oggi."; -"web_files_reconnect_hint" = "Dopo il ricaricamento il browser chiede di nuovo se VisuStruct può usare la tua cartella."; -"web_files_reconnect_fmt" = "Apri la cartella «%@»"; -"web_files_connect_hint" = "Scegli una cartella come spazio di lavoro. VisuStruct vi crea una cartella per ogni giorno e salva diagramma e codice."; -"web_files_connect" = "Scegli cartella"; -"web_files_browser_hint" = "Questo browser non può aprire cartelle del dispositivo (per esempio su iPad). I tuoi file restano nella memoria di questo browser."; -"web_files_browser" = "Salva nel browser"; -"web_files_delete_confirm_fmt" = "Eliminare davvero «%@»?"; -"web_files_folder_prompt" = "Nome della nuova cartella:"; -"web_files_default_name" = "Programma"; -"web_files_saved_fmt" = "Salvato: %@"; -"web_files_create" = "Crea"; -"web_learn_ludwig_tip" = "Il consiglio di Ludwig"; -"web_game_title" = "Gioco: Vero o falso?"; -"web_game_intro" = "Ludwig ti mostra dei valori e una condizione. È vera (true) o falsa (false)? Dieci turni, sempre più difficili."; -"web_game_start" = "Inizia il gioco"; -"web_game_round_fmt" = "Turno %@ di %@"; -"web_game_score_fmt" = "Punti: %@"; -"web_game_true" = "vero (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "Giusto!"; -"web_game_wrong" = "Non proprio. Ecco il calcolo:"; -"web_game_next" = "Avanti"; -"web_game_finish" = "Risultato"; -"web_game_done_fmt" = "%@ su %@ giuste."; -"web_game_perfect" = "Tutto giusto – Ludwig è davvero colpito!"; -"web_game_good" = "Ottimo! Riguarda gli errori e riprova."; -"web_game_practice" = "La pratica rende perfetti. Rileggi la tabella di verità e gioca un altro turno."; -"web_game_again" = "Gioca ancora"; -"web_game_wrong_short" = "Peccato, è sbagliato."; -"web_game_practice_short" = "La pratica rende perfetti. Rileggi le spiegazioni e gioca un altro turno."; -"web_game_clicks" = "Clic:"; -"web_game_nobody" = "Nessuno ascolta: non succede nulla."; -"web_game_event_fmt" = "Clic su «%@»: l'ascoltatore lo sente ed esegue il suo codice."; -"web_game_try" = "Ora prova tu i pulsanti della finestra."; -"web_game_time_fmt" = "Ancora %@ s"; -"web_game_defused" = "✂️ Disinnescata! Hai trovato l’errore."; -"web_game_boom_time" = "💥 Bum! Il tempo è scaduto."; -"web_game_boom_wrong" = "💥 Bum! Era la riga sbagliata."; -"web_game_boom_part" = "💥 Bum! La riga è giusta, ma l’errore è in un’altra parte."; -"web_game_boom_wrong_part" = "💥 Bum! L’errore è in un’altra riga."; -"web_game_bomb_parts_hint" = "Tocca esattamente la parte sbagliata."; -"web_game_fix" = "La riga corretta:"; -"web_game_order_hint" = "Tocca i passaggi nell’ordine giusto."; -"web_debug_step" = "Passo singolo"; -"web_debug_step_title" = "Passo singolo: esegui un’istruzione e fermati di nuovo"; -"web_debug_continue" = "Continua fino al prossimo punto di interruzione"; -"web_debug_paused_fmt" = "Fermo prima della riga %d."; -"web_debug_gutter_title" = "Clicca sul numero di riga per mettere o togliere un punto di interruzione"; -"web_swap_to_code" = "Programmazione grande"; -"web_swap_to_editor" = "Diagramma grande"; -"web_editor_resize" = "Modifica la larghezza del diagramma"; -"web_palette_resize" = "Modifica la larghezza degli elementi"; -"web_palette_hide" = "Nascondi elementi"; -"web_palette_show" = "Mostra elementi"; -"web_code_scopes" = "Blocchi di codice colorati"; -"web_code_scopes_hint" = "Classe, metodo, ciclo, selezione in riquadri propri"; -"web_pseudocode" = "Pseudocodice"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Rinomina variabile …"; -"web_rename_title" = "Rinomina variabile"; -"web_rename_old" = "Nome"; -"web_rename_new" = "Nuovo nome"; -"web_rename_hint" = "Cambia ogni punto con esattamente questa scrittura (numero non è Numero), nel codice e nello strutturogramma. Il testo tra \\"…\\" e i commenti restano."; -"web_rename_count_fmt" = "Compare %d volte."; -"web_rename_exists_fmt" = "«%@» esiste già: dopo le due sarebbero una sola variabile."; -"web_rename_invalid" = "Un nome inizia con una lettera e contiene solo lettere, cifre e _."; -"web_rename_keyword_fmt" = "«%@» è una parola chiave di Java."; -"web_rename_apply" = "Rinomina"; -"web_rename_cancel" = "Annulla"; -`;export{e as default}; diff --git a/website/app/assets/ja-BNDo5HzU.js b/website/app/assets/ja-BNDo5HzU.js deleted file mode 100644 index 0ff8b5ac..00000000 --- a/website/app/assets/ja-BNDo5HzU.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Web 版にのみ存在するテキスト。その他はすべてアプリの Localizable.strings から取得されます。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 構造図"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Delete"; -"web_key_shift" = "Shift"; -"web_export" = "エクスポート…"; -"web_export_title" = "画像または PDF として保存、または印刷"; -"web_export_png" = "画像(PNG)"; -"web_export_svg" = "ベクター画像(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct ファイル(.visustruct)"; -"web_export_failed_fmt" = "エクスポートに失敗しました:%@"; -"web_print_hint" = "「エクスポート… → PDF」で図を保存し、そこから印刷してください。"; -"web_delete" = "削除"; -"web_language_title" = "表示言語"; -"web_simulation_start" = "シミュレーション開始"; -"web_simulation_end" = "シミュレーション終了"; -"web_simulation_start_title" = "構造図をステップごとに実行"; -"web_simulation_end_title" = "シミュレーションを終了して編集を続ける"; -"web_palette_hint" = "構造図にドラッグ"; -"web_tabs_label" = "図"; -"web_status_modified" = "変更あり"; -"web_inspector_title" = "プロパティ"; -"web_inspector_resize" = "オブジェクトインスペクターの高さを変更:線を上下にドラッグまたはスワイプ(ダブルクリック:標準)"; -"web_inspector_help_drop" = "パレットの要素をタイルにドラッグ:上 = 前に、下 = 後に、「ø」の上 = 置き換え。既存の要素はマウスで移動できます。"; -"web_inspector_help_inline" = "要素をダブルクリック/ダブルタップすると、図の中で直接テキストを編集できます(Enter で確定、Esc で取消)。case 名も同様です。"; -"web_inspector_help_files" = "ファイルをウインドウにドロップすることもできます。"; -"web_case_name" = "ケース名"; -"web_case_name_placeholder" = "例:1, ok, default"; -"web_copied" = "要素をコピーしました。"; -"web_discard_message_fmt" = "「%@」への変更は失われます。"; -"web_err_unreadable_fmt" = "「%@」は読み込める VisuStruct ファイルではありません(%@)。"; -"web_saved_fmt" = "「%@」を保存しました。"; -"web_saved_txt_fmt" = "「%@」として保存しました。使用するには拡張子 .txt を削除してください。"; -"web_drop_impossible" = "ここにはドロップできません"; -"web_sim_delay" = "ステップごとの待ち時間"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "言語"; -"web_codegen_comments" = "図のテキストをコメントとして出力"; -"web_codegen_empty_else" = "空の else 分岐もコードに出力"; -"web_copied_check" = "コピーしました ✓"; -"web_copy_manual" = "選択済み。Ctrl+C でコピーしてください"; -"web_run_stopped_loop" = "実行を停止しました:無限ループの可能性があります。"; -"web_run_cancelled" = "実行を中止しました。"; -"web_run_error" = "実行中にエラーが発生しました。"; -"web_run_dropped_fmt" = "… 以前の %d 行は非表示"; -"web_sim_call_arguments_fmt" = "呼び出しが %@ の見出しと一致しません。かっこ内の値を確認してください。"; -"web_sim_missing_return_fmt" = "%@ は return で値を返さずに終了しました。"; -"web_sim_file_not_found_fmt" = "%@ という名前のファイルはありません。"; -"web_sim_end_of_file_fmt" = "ファイル %@ にはもう行がありません。"; -"web_sim_null_value_fmt" = "%@ は null です。"; -"web_sim_unknown_variable_case_fmt" = "不明な変数: %1$@。Java は大文字と小文字を区別します。%2$@ のことですか?"; -"web_sim_written_file_fmt" = "%@(保存済み)"; -"web_site_home" = "ホームページ"; -"web_site_imprint" = "運営者情報"; -"web_site_privacy" = "プライバシー"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "プログラミング"; -"web_live_hide" = "プログラミングを隠す"; -"web_scale_canvas" = "文字サイズ: 構造化チャート"; -"web_scale_code" = "文字サイズ: プログラミング"; -"web_live_indent" = "インデント"; -"web_live_indent_title" = "コードをインデント: 1 レベルにつき空白 4 つ(選択した行、なければ全体)"; -"web_live_show" = "プログラミングを表示"; -"web_live_resize" = "コード領域の幅を変更(ダブルクリック:リセット)"; -"web_live_hint" = "コードは図と一緒に育ちます。灰色=プレースホルダー:インスペクタに入力するとここに表示されます。行をクリックすると要素を選択します。"; -"web_live_ph_selector" = "変数"; -"web_live_ph_code" = "ここにコード"; -"web_live_ph_end" = "終了"; -"web_live_language" = "プログラミング言語"; -"web_live_java_class" = "クラスとして"; -"web_live_java_class_hint" = "クラス名 = 図のタイトル"; -"web_live_java_main" = "main メソッド付き"; -"web_settings_display" = "表示"; -"web_live_swing" = "本物の Java(Swing 対応)"; -"web_live_swing_hint" = "ウィンドウ付きの本物の Java(CheerpJ)。初回実行時に一度だけ約 40 MB をダウンロードします。"; -"web_live_swing_windows" = "Java ウィンドウ"; -"web_live_swing_hide" = "Java ウィンドウを閉じる"; -"web_live_swing_loading" = "Java を読み込み中(初回は約 40 MB)…"; -"web_live_swing_compiling" = "コンパイル中…"; -"web_live_swing_running" = "プログラム実行中。"; -"web_live_swing_compile_failed" = "コンパイルエラー。上を見てください。"; -"web_live_swing_failed" = "Java を読み込めませんでした。インターネット接続を確認してください。"; -"web_live_hint_edit" = "ここに Java コードを入力すると、図が一緒にできあがります。図で作業すると、コードも一緒に育ちます。灰色=プレースホルダー、そのまま上書きできます。"; -"web_live_edit_label" = "Java コード"; -"web_parse_line_fmt" = "%d 行目: %@"; -"web_parse_missing" = "ここにまだ足りません: %@"; -"web_parse_extra" = "多すぎます: %@"; -"web_parse_unclosed_string" = "閉じる %@ がありません。"; -"web_parse_else_without_if" = "この「else」に対応する「if」がありません。"; -"web_parse_case_outside_switch" = "「case」と「default」は「switch」の中に書きます。"; -"web_parse_missing_do_while" = "「do { … }」の後に「while (条件);」が必要です。"; -"web_parse_more_methods" = "今はここでメソッドを 1 つだけ使います。ほかのサブプログラムは別のタブに作ってください。"; -"web_parse_missing_import" = "先頭に %@ がありません。この行がないと Java は Scanner を知りません。"; -"web_parse_missing_scanner" = "先に %@ がありません。この行がないと Scanner はありません。"; -"web_parse_unknown_type" = "Java はこのデータ型を知りません。%@ のことですか?"; -"web_quickfix_insert_fmt" = "提案: %@ を挿入"; -"web_quickfix_replace_fmt" = "提案: %2$@ の代わりに %1$@"; -"web_parse_wrong_case" = "Java は大文字と小文字を区別します。正しくは %@ です。"; -"web_editor_title" = "構造図"; -"web_editor_show" = "構造図を表示"; -"web_editor_hide" = "構造図を隠す"; -"web_keywords_title" = "コードのキーワード"; -"web_keywords_hint" = "要素名の代わりに if, while, for"; -"web_palette_compact" = "要素を常にコンパクト表示"; -"web_palette_compact_hint" = "記号の下に小さく名前を表示。オフの場合は列が狭いときのみ"; -"web_about_version_fmt" = "バージョン %@"; -"web_about_software" = "使用しているソフトウェア"; -"web_about_software_text" = "コードエディターは CodeMirror と Lezer(MIT ライセンス)を使用しています。Swing サポートを有効にすると、VisuStruct は Leaning Technologies の CheerpJ(Community ライセンス)と OpenJDK の javac を読み込みます。"; -"web_about_licenses" = "ライセンス文"; -"web_live_run" = "実行"; -"web_live_run_title" = "プログラムを実行:出力と入力は下に表示されます"; -"web_live_run_finished" = "プログラムが終了しました。"; -"web_live_run_stopped" = "停止しました。"; -"web_console" = "コンソール"; -"web_console_hide" = "コンソールを隠す"; -"web_console_resize" = "コンソールの高さを変更"; -"web_diagram_tools" = "図のツール"; -"web_files_title" = "ファイル"; -"web_files_show" = "ファイルを表示"; -"web_files_hide" = "ファイルを隠す"; -"web_files_resize" = "ファイルの幅を変更"; -"web_files_new_folder" = "新しいフォルダ"; -"web_files_switch" = "別のフォルダを選ぶ"; -"web_files_delete" = "削除"; -"web_files_in_browser" = "このブラウザのストレージ"; -"web_files_save_hint_fmt" = "保存すると、プログラムは %@ に入ります。"; -"web_files_empty" = "まだ何も保存されていません。保存すると、VisuStruct がここに今日の日付のフォルダを作ります。"; -"web_files_reconnect_hint" = "再読み込み後、VisuStruct がフォルダを使ってよいか、ブラウザがもう一度たずねます。"; -"web_files_reconnect_fmt" = "フォルダ「%@」を開く"; -"web_files_connect_hint" = "作業フォルダを選んでください。VisuStruct が日付ごとのフォルダを作り、図とコードを保存します。"; -"web_files_connect" = "フォルダを選ぶ"; -"web_files_browser_hint" = "このブラウザは端末のフォルダを開けません(例:iPad)。ファイルはこのブラウザのストレージに残ります。"; -"web_files_browser" = "ブラウザに保存"; -"web_files_delete_confirm_fmt" = "「%@」を削除しますか?"; -"web_files_folder_prompt" = "新しいフォルダの名前:"; -"web_files_default_name" = "プログラム"; -"web_files_saved_fmt" = "保存しました:%@"; -"web_files_create" = "作成"; -"web_learn_ludwig_tip" = "ルートヴィヒのヒント"; -"web_game_title" = "ゲーム:真か偽か?"; -"web_game_intro" = "ルートヴィヒが値と条件を見せます。条件は真(true)?それとも偽(false)?10ラウンド、だんだん難しくなります。"; -"web_game_start" = "ゲームを始める"; -"web_game_round_fmt" = "ラウンド %@ / %@"; -"web_game_score_fmt" = "ポイント:%@"; -"web_game_true" = "真(true)"; -"web_game_false" = "偽(false)"; -"web_game_right" = "正解!"; -"web_game_wrong" = "残念。計算はこうなります:"; -"web_game_next" = "次へ"; -"web_game_finish" = "結果"; -"web_game_done_fmt" = "%@ / %@ 正解。"; -"web_game_perfect" = "全問正解――ルートヴィヒも感心しています!"; -"web_game_good" = "すごい!間違えたところを見直して、もう一度やってみよう。"; -"web_game_practice" = "練習あるのみ。上の真理値表をもう一度読んで、もう一回挑戦しよう。"; -"web_game_again" = "もう一度"; -"web_game_wrong_short" = "残念、不正解です。"; -"web_game_practice_short" = "習うより慣れよ。説明をもう一度読んで、もう一回遊んでみよう。"; -"web_game_clicks" = "クリック:"; -"web_game_nobody" = "だれも聞いていません。何も起こりません。"; -"web_game_event_fmt" = "「%@」をクリック: リスナーが気づいて自分のコードを実行します。"; -"web_game_try" = "ウィンドウのボタンを自分で試してみよう。"; -"web_game_time_fmt" = "残り %@ 秒"; -"web_game_defused" = "✂️ 解除成功!間違いを見つけました。"; -"web_game_boom_time" = "💥 ドカン!時間切れです。"; -"web_game_boom_wrong" = "💥 ドカン!その行ではありません。"; -"web_game_boom_part" = "💥 ドカン!行は合っているけど、間違いは別の部分にあるよ。"; -"web_game_boom_wrong_part" = "💥 ドカン!間違いは別の行にあるよ。"; -"web_game_bomb_parts_hint" = "間違っている部分をぴったりタップしよう。"; -"web_game_fix" = "正しい行:"; -"web_game_order_hint" = "正しい順番でステップをタップしよう。"; -"web_debug_step" = "ステップ実行"; -"web_debug_step_title" = "ステップ実行:命令を1つ実行して再び止まる"; -"web_debug_continue" = "次のブレークポイントまで続行"; -"web_debug_paused_fmt" = "%d 行目の前で一時停止中。"; -"web_debug_gutter_title" = "行番号をクリックするとブレークポイントを設定・解除できます"; -"web_swap_to_code" = "プログラミングを大きく"; -"web_swap_to_editor" = "構造図を大きく"; -"web_editor_resize" = "構造図の幅を変更"; -"web_palette_resize" = "要素の幅を変更"; -"web_palette_hide" = "要素を隠す"; -"web_palette_show" = "要素を表示"; -"web_code_scopes" = "コードブロックを色分け"; -"web_code_scopes_hint" = "クラス、メソッド、ループ、分岐をそれぞれの枠で表示"; -"web_pseudocode" = "疑似コード"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "変数の名前を変更 …"; -"web_rename_title" = "変数の名前を変更"; -"web_rename_old" = "名前"; -"web_rename_new" = "新しい名前"; -"web_rename_hint" = "つづりが完全に同じ箇所をすべて、コードと構造図で変更します(number と Number は別です)。\\"…\\" 内の文字列とコメントは変わりません。"; -"web_rename_count_fmt" = "%d 回使われています。"; -"web_rename_exists_fmt" = "「%@」はすでにあります:変更すると 2 つの変数が 1 つになります。"; -"web_rename_invalid" = "名前は文字で始まり、文字・数字・_ だけを含みます。"; -"web_rename_keyword_fmt" = "「%@」は Java のキーワードです。"; -"web_rename_apply" = "名前を変更"; -"web_rename_cancel" = "キャンセル"; -`;export{e as default}; diff --git a/website/app/assets/katherine-johnson-k17EjVTs.jpg b/website/app/assets/katherine-johnson-k17EjVTs.jpg deleted file mode 100644 index c2df60c5..00000000 Binary files a/website/app/assets/katherine-johnson-k17EjVTs.jpg and /dev/null differ diff --git a/website/app/assets/kino-saal-BOvcmdzb.svg b/website/app/assets/kino-saal-BOvcmdzb.svg deleted file mode 100644 index 65dc50de..00000000 --- a/website/app/assets/kino-saal-BOvcmdzb.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Ecrã -Lugar (segundo índice) -0 -1 -2 -3 -4 -Fila(primeiroíndice) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Tracejado: lugares[1][2], fila 1, lugar 2 -vendido = truelivre = false - diff --git a/website/app/assets/kino-saal-C8zaJk4X.svg b/website/app/assets/kino-saal-C8zaJk4X.svg deleted file mode 100644 index 4e49fc22..00000000 --- a/website/app/assets/kino-saal-C8zaJk4X.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Screen -Seat (second index) -0 -1 -2 -3 -4 -Row(firstindex) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Dashed: seats[1][2], row 1, seat 2 -sold = truefree = false - diff --git a/website/app/assets/kino-saal-CYMOok4_.svg b/website/app/assets/kino-saal-CYMOok4_.svg deleted file mode 100644 index 18c5a30a..00000000 --- a/website/app/assets/kino-saal-CYMOok4_.svg +++ /dev/null @@ -1,32 +0,0 @@ - - - -Leinwand -Platz (zweiter Index) -0 -1 -2 -3 -4 -Reihe(ersterIndex) -0 -false -false -true -false -false -1 -false -true -true -false -false -2 -false -false -false -false -false -Gestrichelt: sitze[1][2], Reihe 1, Platz 2 -verkauft = truefrei = false - diff --git a/website/app/assets/kino-schleifen-Ci9yPnbo.svg b/website/app/assets/kino-schleifen-Ci9yPnbo.svg deleted file mode 100644 index c9788f21..00000000 --- a/website/app/assets/kino-schleifen-Ci9yPnbo.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -ciclo exterior: fila = 0, 1, 2 -ciclo interior: lugar = 0 … 4 -lugares[fila][lugar]ver -lugar -0 -1 -2 -3 -4 -fila0 - -1 - -2 - -3 - -4 - -5 - -fila1 - -6 - -7 - -8 - -9 - -10 - -fila2 - -11 - -12 - -13 - -14 - -15 -Lá dentro, lugar percorre tudo; só depois fila avança um. - diff --git a/website/app/assets/kino-schleifen-D7sfo9iK.svg b/website/app/assets/kino-schleifen-D7sfo9iK.svg deleted file mode 100644 index 2698fc0a..00000000 --- a/website/app/assets/kino-schleifen-D7sfo9iK.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -outer loop: row = 0, 1, 2 -inner loop: seat = 0 … 4 -seats[row][seat]look at -seat -0 -1 -2 -3 -4 -row0 - -1 - -2 - -3 - -4 - -5 - -row1 - -6 - -7 - -8 - -9 - -10 - -row2 - -11 - -12 - -13 - -14 - -15 -Inside, seat runs through, only then row moves on by one. - diff --git a/website/app/assets/kino-schleifen-DrTQV7dq.svg b/website/app/assets/kino-schleifen-DrTQV7dq.svg deleted file mode 100644 index 486b7ca5..00000000 --- a/website/app/assets/kino-schleifen-DrTQV7dq.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - -äußere Schleife: reihe = 0, 1, 2 -innere Schleife: platz = 0 … 4 -sitze[reihe][platz]ansehen -platz -0 -1 -2 -3 -4 -reihe0 - -1 - -2 - -3 - -4 - -5 - -reihe1 - -6 - -7 - -8 - -9 - -10 - -reihe2 - -11 - -12 - -13 - -14 - -15 -Innen läuft platz durch, erst dann geht reihe eins weiter. - diff --git a/website/app/assets/ko-D37jf4Vi.js b/website/app/assets/ko-D37jf4Vi.js deleted file mode 100644 index 203c1bf1..00000000 --- a/website/app/assets/ko-D37jf4Vi.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* 웹 버전에만 있는 텍스트입니다. 나머지는 모두 앱의 Localizable.strings에서 가져옵니다. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 구조도"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "내보내기…"; -"web_export_title" = "이미지 또는 PDF로 저장하거나 인쇄"; -"web_export_png" = "이미지(PNG)"; -"web_export_svg" = "벡터 그래픽(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 파일(.visustruct)"; -"web_export_failed_fmt" = "내보내기 실패: %@"; -"web_print_hint" = "“내보내기… → PDF”로 다이어그램을 저장한 뒤 인쇄하세요."; -"web_delete" = "삭제"; -"web_language_title" = "인터페이스 언어"; -"web_simulation_start" = "시뮬레이션 시작"; -"web_simulation_end" = "시뮬레이션 종료"; -"web_simulation_start_title" = "구조도를 단계별로 실행"; -"web_simulation_end_title" = "시뮬레이션을 끝내고 계속 편집"; -"web_palette_hint" = "구조도로 드래그하세요"; -"web_tabs_label" = "다이어그램"; -"web_status_modified" = "수정됨"; -"web_inspector_title" = "속성"; -"web_inspector_resize" = "객체 검사기 높이 변경: 선을 위아래로 드래그하거나 스와이프 (더블 클릭: 기본값)"; -"web_inspector_help_drop" = "팔레트의 요소를 타일로 드래그하세요: 위쪽 = 앞에, 아래쪽 = 뒤에, “ø” 위 = 바꾸기. 기존 요소는 마우스로 옮길 수 있습니다."; -"web_inspector_help_inline" = "요소를 두 번 클릭하거나 두 번 탭하면 다이어그램에서 바로 텍스트를 편집할 수 있습니다(Enter 적용, Esc 취소). case 이름도 마찬가지입니다."; -"web_inspector_help_files" = "파일을 창에 끌어다 놓을 수도 있습니다."; -"web_case_name" = "케이스 이름"; -"web_case_name_placeholder" = "예: 1, ok, default"; -"web_copied" = "요소를 복사했습니다."; -"web_discard_message_fmt" = "“%@”의 변경 사항이 사라집니다."; -"web_err_unreadable_fmt" = "“%@”은(는) 읽을 수 있는 VisuStruct 파일이 아닙니다(%@)."; -"web_saved_fmt" = "“%@” 저장됨."; -"web_saved_txt_fmt" = "“%@”(으)로 저장했습니다. 사용하려면 .txt 확장자를 제거하세요."; -"web_drop_impossible" = "여기에 놓을 수 없음"; -"web_sim_delay" = "단계별 대기 시간"; -"web_sim_delay_value_fmt" = "%@초"; -"web_codegen_language" = "언어"; -"web_codegen_comments" = "다이어그램 텍스트를 주석으로"; -"web_codegen_empty_else" = "빈 else 분기도 코드에 출력"; -"web_copied_check" = "복사됨 ✓"; -"web_copy_manual" = "선택됨, Ctrl+C로 복사하세요"; -"web_run_stopped_loop" = "실행 중지: 무한 반복일 수 있습니다."; -"web_run_cancelled" = "실행이 취소되었습니다."; -"web_run_error" = "실행 중 오류가 발생했습니다."; -"web_run_dropped_fmt" = "… 이전 %d줄 숨김"; -"web_sim_call_arguments_fmt" = "호출이 %@의 머리글과 맞지 않습니다. 괄호 안의 값을 확인하세요."; -"web_sim_missing_return_fmt" = "%@이(가) return으로 값을 반환하지 않고 끝났습니다."; -"web_sim_file_not_found_fmt" = "%@(이)라는 파일이 없습니다."; -"web_sim_end_of_file_fmt" = "파일 %@에 더 이상 줄이 없습니다."; -"web_sim_null_value_fmt" = "%@은(는) null입니다."; -"web_sim_unknown_variable_case_fmt" = "알 수 없는 변수: %1$@. Java는 대문자와 소문자를 구분합니다. %2$@을(를) 뜻했나요?"; -"web_sim_written_file_fmt" = "%@ (저장됨)"; -"web_site_home" = "홈페이지"; -"web_site_imprint" = "사이트 정보"; -"web_site_privacy" = "개인정보"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "프로그래밍"; -"web_live_hide" = "프로그래밍 숨기기"; -"web_scale_canvas" = "글자 크기: 구조도"; -"web_scale_code" = "글자 크기: 프로그래밍"; -"web_live_indent" = "들여쓰기"; -"web_live_indent_title" = "코드 들여쓰기: 단계마다 공백 4칸 (선택한 줄, 없으면 전체)"; -"web_live_show" = "프로그래밍 표시"; -"web_live_resize" = "코드 영역 너비 변경(더블클릭: 초기화)"; -"web_live_hint" = "코드는 다이어그램과 함께 자랍니다. 회색 = 자리 표시자: 속성 창에 입력하면 여기에 표시됩니다. 줄을 클릭하면 해당 요소가 선택됩니다."; -"web_live_ph_selector" = "변수"; -"web_live_ph_code" = "코드 작성"; -"web_live_ph_end" = "끝"; -"web_live_language" = "프로그래밍 언어"; -"web_live_java_class" = "클래스로"; -"web_live_java_class_hint" = "클래스 이름 = 순서도 제목"; -"web_live_java_main" = "main 메서드 포함"; -"web_settings_display" = "표시"; -"web_live_swing" = "실제 Java (Swing 포함)"; -"web_live_swing_hint" = "창이 있는 진짜 Java(CheerpJ). 처음 실행할 때 한 번 약 40MB를 내려받습니다."; -"web_live_swing_windows" = "Java 창"; -"web_live_swing_hide" = "Java 창 닫기"; -"web_live_swing_loading" = "Java 불러오는 중(처음에는 약 40MB) …"; -"web_live_swing_compiling" = "컴파일 중 …"; -"web_live_swing_running" = "프로그램 실행 중."; -"web_live_swing_compile_failed" = "컴파일 오류, 위를 보세요."; -"web_live_swing_failed" = "Java를 불러올 수 없습니다. 인터넷 연결을 확인하세요."; -"web_live_hint_edit" = "여기에 Java 코드를 입력하면 순서도가 함께 만들어집니다. 또는 순서도에서 작업하면 코드가 함께 자랍니다. 회색 = 자리표시자, 그대로 덮어쓰면 됩니다."; -"web_live_edit_label" = "Java 코드"; -"web_parse_line_fmt" = "%d행: %@"; -"web_parse_missing" = "여기에 아직 없음: %@"; -"web_parse_extra" = "하나 더 많음: %@"; -"web_parse_unclosed_string" = "닫는 %@가 없습니다."; -"web_parse_else_without_if" = "이 “else”에 맞는 “if”가 없습니다."; -"web_parse_case_outside_switch" = "“case”와 “default”는 “switch” 안에 있어야 합니다."; -"web_parse_missing_do_while" = "“do { … }” 다음에 “while (조건);”이 필요합니다."; -"web_parse_more_methods" = "지금은 메서드 하나만 사용됩니다. 다른 서브프로그램은 별도 탭으로 만드세요."; -"web_parse_missing_import" = "맨 위에 %@ 이(가) 없습니다. 이 줄이 없으면 Java는 Scanner를 모릅니다."; -"web_parse_missing_scanner" = "먼저 %@ 이(가) 없습니다. 이 줄이 없으면 Scanner가 없습니다."; -"web_parse_unknown_type" = "Java는 이 데이터 형식을 알지 못합니다. %@을(를) 의미했나요?"; -"web_quickfix_insert_fmt" = "제안: %@ 삽입"; -"web_quickfix_replace_fmt" = "제안: %2$@ 대신 %1$@"; -"web_parse_wrong_case" = "Java는 대문자와 소문자를 구분합니다. 올바른 표기는 %@입니다."; -"web_editor_title" = "구조도"; -"web_editor_show" = "구조도 표시"; -"web_editor_hide" = "구조도 숨기기"; -"web_keywords_title" = "코드 키워드"; -"web_keywords_hint" = "요소 이름 대신 if, while, for"; -"web_palette_compact" = "요소 항상 간단히 보기"; -"web_palette_compact_hint" = "기호 아래에 작은 이름 표시, 아니면 열이 좁을 때만"; -"web_about_version_fmt" = "버전 %@"; -"web_about_software" = "사용된 소프트웨어"; -"web_about_software_text" = "코드 편집기는 CodeMirror와 Lezer(MIT 라이선스)를 사용합니다. Swing 지원을 켜면 VisuStruct는 Leaning Technologies의 CheerpJ(Community 라이선스)와 OpenJDK의 javac를 불러옵니다."; -"web_about_licenses" = "라이선스 전문"; -"web_live_run" = "실행"; -"web_live_run_title" = "프로그램 실행: 출력과 입력이 아래에 표시됩니다"; -"web_live_run_finished" = "프로그램이 끝났습니다."; -"web_live_run_stopped" = "중지됨."; -"web_console" = "콘솔"; -"web_console_hide" = "콘솔 숨기기"; -"web_console_resize" = "콘솔 크기 조절"; -"web_diagram_tools" = "다이어그램 도구"; -"web_files_title" = "파일"; -"web_files_show" = "파일 보기"; -"web_files_hide" = "파일 숨기기"; -"web_files_resize" = "파일 너비 조절"; -"web_files_new_folder" = "새 폴더"; -"web_files_switch" = "다른 폴더 선택"; -"web_files_delete" = "삭제"; -"web_files_in_browser" = "이 브라우저 저장소"; -"web_files_save_hint_fmt" = "저장하면 프로그램이 %@에 들어갑니다."; -"web_files_empty" = "아직 저장된 것이 없습니다. 저장하면 VisuStruct가 여기에 오늘 날짜 폴더를 만듭니다."; -"web_files_reconnect_hint" = "새로 고침 후 브라우저가 VisuStruct의 폴더 사용 여부를 다시 묻습니다."; -"web_files_reconnect_fmt" = "“%@” 폴더 열기"; -"web_files_connect_hint" = "작업 공간으로 쓸 폴더를 선택하세요. VisuStruct가 날짜별 폴더를 만들고 다이어그램과 코드를 저장합니다."; -"web_files_connect" = "폴더 선택"; -"web_files_browser_hint" = "이 브라우저는 기기의 폴더를 열 수 없습니다 (예: iPad). 파일은 이 브라우저 저장소에 남습니다."; -"web_files_browser" = "브라우저에 저장"; -"web_files_delete_confirm_fmt" = "“%@”을(를) 삭제할까요?"; -"web_files_folder_prompt" = "새 폴더 이름:"; -"web_files_default_name" = "프로그램"; -"web_files_saved_fmt" = "저장됨: %@"; -"web_files_create" = "만들기"; -"web_learn_ludwig_tip" = "루트비히의 팁"; -"web_game_title" = "게임: 참일까 거짓일까?"; -"web_game_intro" = "루트비히가 값과 조건을 보여 줍니다. 조건이 참(true)일까요, 거짓(false)일까요? 10라운드, 점점 어려워집니다."; -"web_game_start" = "게임 시작"; -"web_game_round_fmt" = "라운드 %@ / %@"; -"web_game_score_fmt" = "점수: %@"; -"web_game_true" = "참 (true)"; -"web_game_false" = "거짓 (false)"; -"web_game_right" = "정답!"; -"web_game_wrong" = "아쉽네요. 이렇게 계산합니다:"; -"web_game_next" = "다음"; -"web_game_finish" = "결과"; -"web_game_done_fmt" = "%@ / %@ 정답."; -"web_game_perfect" = "모두 정답 – 루트비히가 감탄했어요!"; -"web_game_good" = "훌륭해요! 틀린 것을 다시 보고 한 번 더 해 보세요."; -"web_game_practice" = "연습이 실력을 만듭니다. 위의 진리표를 다시 읽고 한 번 더 해 보세요."; -"web_game_again" = "다시 하기"; -"web_game_wrong_short" = "아쉽지만 틀렸어요."; -"web_game_practice_short" = "연습이 완벽을 만듭니다. 설명을 다시 읽고 한 판 더 해 보세요."; -"web_game_clicks" = "클릭:"; -"web_game_nobody" = "아무도 듣지 않아요. 아무 일도 일어나지 않습니다."; -"web_game_event_fmt" = "„%@” 클릭: 리스너가 듣고 자기 코드를 실행합니다."; -"web_game_try" = "이제 창의 버튼을 직접 눌러 보세요."; -"web_game_time_fmt" = "%@초 남음"; -"web_game_defused" = "✂️ 해체 성공! 오류를 찾았어요."; -"web_game_boom_time" = "💥 펑! 시간이 다 됐어요."; -"web_game_boom_wrong" = "💥 펑! 틀린 줄이었어요."; -"web_game_boom_part" = "💥 펑! 줄은 맞지만, 오류는 다른 부분에 있어요."; -"web_game_boom_wrong_part" = "💥 펑! 오류는 다른 줄에 있어요."; -"web_game_bomb_parts_hint" = "틀린 부분을 정확히 눌러 보세요."; -"web_game_fix" = "올바른 줄:"; -"web_game_order_hint" = "올바른 순서대로 단계를 눌러 보세요."; -"web_debug_step" = "한 단계"; -"web_debug_step_title" = "한 단계: 문장 하나를 실행하고 다시 멈춥니다"; -"web_debug_continue" = "다음 중단점까지 계속"; -"web_debug_paused_fmt" = "%d번째 줄 앞에서 멈춤."; -"web_debug_gutter_title" = "줄 번호를 클릭하면 중단점을 설정하거나 지웁니다"; -"web_swap_to_code" = "프로그래밍 크게"; -"web_swap_to_editor" = "구조도 크게"; -"web_editor_resize" = "구조도 너비 변경"; -"web_palette_resize" = "요소 너비 변경"; -"web_palette_hide" = "요소 숨기기"; -"web_palette_show" = "요소 표시"; -"web_code_scopes" = "코드 블록 색상 표시"; -"web_code_scopes_hint" = "클래스, 메서드, 반복, 분기를 각각의 상자로 표시"; -"web_pseudocode" = "의사 코드"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "변수 이름 바꾸기 …"; -"web_rename_title" = "변수 이름 바꾸기"; -"web_rename_old" = "이름"; -"web_rename_new" = "새 이름"; -"web_rename_hint" = "철자가 정확히 같은 모든 곳을 코드와 구조도에서 바꿉니다(number와 Number는 다릅니다). \\"…\\" 안의 텍스트와 주석은 그대로입니다."; -"web_rename_count_fmt" = "%d번 사용됨."; -"web_rename_exists_fmt" = "“%@”이(가) 이미 있습니다: 바꾸면 두 변수가 하나가 됩니다."; -"web_rename_invalid" = "이름은 문자로 시작하고 문자, 숫자, _만 포함합니다."; -"web_rename_keyword_fmt" = "“%@”은(는) Java 키워드입니다."; -"web_rename_apply" = "이름 바꾸기"; -"web_rename_cancel" = "취소"; -`;export{e as default}; diff --git a/website/app/assets/l03-lochkarte-BB3rWQOm.svg b/website/app/assets/l03-lochkarte-BB3rWQOm.svg deleted file mode 100644 index 8eea3c50..00000000 --- a/website/app/assets/l03-lochkarte-BB3rWQOm.svg +++ /dev/null @@ -1,998 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/website/app/assets/l03-lochkarte-BSn6oWaS.svg b/website/app/assets/l03-lochkarte-BSn6oWaS.svg deleted file mode 100644 index 1b08640f..00000000 --- a/website/app/assets/l03-lochkarte-BSn6oWaS.svg +++ /dev/null @@ -1,994 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -M - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -U - -1 -2 -3 - -5 -6 -7 -8 -9 -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/website/app/assets/l03-lochkarte-beschriftet-7hmU_7ct.svg b/website/app/assets/l03-lochkarte-beschriftet-7hmU_7ct.svg deleted file mode 100644 index ac9ff74d..00000000 --- a/website/app/assets/l03-lochkarte-beschriftet-7hmU_7ct.svg +++ /dev/null @@ -1,1016 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Ecke abgeschnitten: -so liegen alle Karten richtig herum - -Aufdruck zum Mitlesen - - - - -Zeilen 12 und 11: Extralöcher für Buchstaben -1 Spalte = 1 Zeichen -hier P: Löcher in 11 und 7 - -gestanztes Loch - - -80 Spalten = höchstens 80 Zeichen pro Zeile - diff --git a/website/app/assets/l03-lochkarte-beschriftet-BjBFZjzb.svg b/website/app/assets/l03-lochkarte-beschriftet-BjBFZjzb.svg deleted file mode 100644 index b5080535..00000000 --- a/website/app/assets/l03-lochkarte-beschriftet-BjBFZjzb.svg +++ /dev/null @@ -1,1019 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Cut corner: -so all cards lie the right way round - -Printed text to read along - - - - -Rows 12 and 11: extra holes for letters -1 column = 1 character -here P: holes in 11 and 7 - -punched hole - - -80 columns = at most 80 characters per line - diff --git a/website/app/assets/l03-lochkarte-beschriftet-D1QjqGFo.svg b/website/app/assets/l03-lochkarte-beschriftet-D1QjqGFo.svg deleted file mode 100644 index 89cefb22..00000000 --- a/website/app/assets/l03-lochkarte-beschriftet-D1QjqGFo.svg +++ /dev/null @@ -1,1015 +0,0 @@ - - - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -M - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -U - -1 -2 -3 - -5 -6 -7 -8 -9 -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -D - -0 -1 -2 -3 - -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - - - -Canto cortado: -assim todos os cartões ficam na posição certa - -Texto impresso para ler - - - - -Linhas 12 e 11: furos extra para letras -1 coluna = 1 carácter -aqui P: furos em 11 e 7 - -furo perfurado - - -80 colunas = no máximo 80 caracteres por linha - diff --git a/website/app/assets/l03-lochkarte-gzfFEatc.svg b/website/app/assets/l03-lochkarte-gzfFEatc.svg deleted file mode 100644 index a5cbccfb..00000000 --- a/website/app/assets/l03-lochkarte-gzfFEatc.svg +++ /dev/null @@ -1,995 +0,0 @@ - - - - -1 -1 -2 -2 -3 -3 -4 -4 -5 -5 -6 -6 -7 -7 -8 -8 -9 -9 -10 -10 -11 -11 -12 -12 -13 -13 -14 -14 -15 -15 -16 -16 -17 -17 -18 -18 -19 -19 -20 -20 -21 -21 -22 -22 -23 -23 -24 -24 -25 -25 -26 -26 -27 -27 -28 -28 -29 -29 -30 -30 -31 -31 -32 -32 -33 -33 -34 -34 -35 -35 -36 -36 -37 -37 -38 -38 -39 -39 -40 -40 -41 -41 -42 -42 -43 -43 -44 -44 -45 -45 -46 -46 -47 -47 -48 -48 -49 -49 -50 -50 -51 -51 -52 -52 -53 -53 -54 -54 -55 -55 -56 -56 -57 -57 -58 -58 -59 -59 -60 -60 -61 -61 -62 -62 -63 -63 -64 -64 -65 -65 -66 -66 -67 -67 -68 -68 -69 -69 -70 -70 -71 -71 -72 -72 -73 -73 -74 -74 -75 -75 -76 -76 -77 -77 -78 -78 -79 -79 -80 -80 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -P - -0 -1 -2 -3 -4 -5 -6 - -8 -9 -R - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -I - -0 -1 -2 -3 -4 -5 -6 -7 -8 - -N - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -* - -0 -1 -2 -3 - -5 -6 -7 - -9 -, - -1 -2 - -4 -5 -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -H - -0 -1 -2 -3 -4 -5 -6 -7 - -9 -A - -0 - -2 -3 -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -O - -0 -1 -2 -3 -4 -5 - -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -W - -1 -2 -3 -4 -5 - -7 -8 -9 -E - -0 -1 -2 -3 -4 - -6 -7 -8 -9 -L - -0 -1 -2 - -4 -5 -6 -7 -8 -9 -T - -1 -2 - -4 -5 -6 -7 -8 -9 -' -0 -1 -2 -3 -4 - -6 -7 - -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 - diff --git a/website/app/assets/l03-zuse-lochstreifen-B87kqGex.svg b/website/app/assets/l03-zuse-lochstreifen-B87kqGex.svg deleted file mode 100644 index d379feb7..00000000 --- a/website/app/assets/l03-zuse-lochstreifen-B87kqGex.svg +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -1 -0 -1 -1 -0 -0 -1 -0 - - - -= 1 - -= 0 - diff --git a/website/app/assets/l12-while-zu-for-CM2--y57.svg b/website/app/assets/l12-while-zu-for-CM2--y57.svg deleted file mode 100644 index ffba5bc5..00000000 --- a/website/app/assets/l12-while-zu-for-CM2--y57.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Início - Condição - Passo - InícioCondiçãoPasso - diff --git a/website/app/assets/l12-while-zu-for-Cu-8sS5S.svg b/website/app/assets/l12-while-zu-for-Cu-8sS5S.svg deleted file mode 100644 index 8aa5bd82..00000000 --- a/website/app/assets/l12-while-zu-for-Cu-8sS5S.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Start - Bedingung - Schritt - StartBedingungSchritt - diff --git a/website/app/assets/l12-while-zu-for-DddKE_8R.svg b/website/app/assets/l12-while-zu-for-DddKE_8R.svg deleted file mode 100644 index 6b2f4ede..00000000 --- a/website/app/assets/l12-while-zu-for-DddKE_8R.svg +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - while - for - ; - ; - int i = 1 - i <= 5 - i++ - output: i - Init - Condition - Step - InitConditionStep - diff --git a/website/app/assets/l13-verschachtelt-tabelle-CR2WySqU.svg b/website/app/assets/l13-verschachtelt-tabelle-CR2WySqU.svg deleted file mode 100644 index 070abbc1..00000000 --- a/website/app/assets/l13-verschachtelt-tabelle-CR2WySqU.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String texto = "" - for int j = 1; j <= 4; j++ - texto = texto + i * j + " " - output: texto - - VARIÁVEISCONSOLA - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - texto = - "" - texto = - "1 " - texto = - "1 2 " - texto = - "1 2 3 " - texto = - "1 2 3 4 " - texto = - "2 " - texto = - "2 4 " - texto = - "2 4 6 " - texto = - "2 4 6 8 " - texto = - "3 " - texto = - "3 6 " - texto = - "3 6 9 " - texto = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/website/app/assets/l13-verschachtelt-tabelle-CmsFvxQ5.svg b/website/app/assets/l13-verschachtelt-tabelle-CmsFvxQ5.svg deleted file mode 100644 index dd830d43..00000000 --- a/website/app/assets/l13-verschachtelt-tabelle-CmsFvxQ5.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String text = "" - for int j = 1; j <= 4; j++ - text = text + i * j + " " - output: text - - VARIABLENKONSOLE - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - text = - "" - text = - "1 " - text = - "1 2 " - text = - "1 2 3 " - text = - "1 2 3 4 " - text = - "2 " - text = - "2 4 " - text = - "2 4 6 " - text = - "2 4 6 8 " - text = - "3 " - text = - "3 6 " - text = - "3 6 9 " - text = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/website/app/assets/l13-verschachtelt-tabelle-jipYA7Ld.svg b/website/app/assets/l13-verschachtelt-tabelle-jipYA7Ld.svg deleted file mode 100644 index 6065b866..00000000 --- a/website/app/assets/l13-verschachtelt-tabelle-jipYA7Ld.svg +++ /dev/null @@ -1,193 +0,0 @@ - - - - - - - - - - - - - - for int i = 1; i <= 3; i++ - String text = "" - for int j = 1; j <= 4; j++ - text = text + i * j + " " - output: text - - VARIABLESCONSOLE - i = 1 - i = 2 - i = 3 - i = 4 - j = 1 - j = 2 - j = 3 - j = 4 - j = 5 - text = - "" - text = - "1 " - text = - "1 2 " - text = - "1 2 3 " - text = - "1 2 3 4 " - text = - "2 " - text = - "2 4 " - text = - "2 4 6 " - text = - "2 4 6 8 " - text = - "3 " - text = - "3 6 " - text = - "3 6 9 " - text = - "3 6 9 12 " - 1 2 3 4 - 2 4 6 8 - 3 6 9 12 - diff --git a/website/app/assets/ludwig-Cus1i402.webp b/website/app/assets/ludwig-Cus1i402.webp deleted file mode 100644 index 28cfd94a..00000000 Binary files a/website/app/assets/ludwig-Cus1i402.webp and /dev/null differ diff --git a/website/app/assets/margaret-hamilton-pYPKs1sU.jpg b/website/app/assets/margaret-hamilton-pYPKs1sU.jpg deleted file mode 100644 index 9a5ae22d..00000000 Binary files a/website/app/assets/margaret-hamilton-pYPKs1sU.jpg and /dev/null differ diff --git a/website/app/assets/mvc-klick-BIA_bYzu.svg b/website/app/assets/mvc-klick-BIA_bYzu.svg deleted file mode 100644 index a885cc70..00000000 --- a/website/app/assets/mvc-klick-BIA_bYzu.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - the display - - Controller - the listener - - Model - Counter - - 0 - 1 - 2 - 3 - - +1 - - value = 0 - value = 1 - value = 2 - value = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 increase() - 2 increase() - - - 3 getValue() - 3 getValue() - - - 4 update(value) - 4 update(value) - - The model knows neither the view nor the controller. - diff --git a/website/app/assets/mvc-klick-DKonBpbs.svg b/website/app/assets/mvc-klick-DKonBpbs.svg deleted file mode 100644 index 08d276a8..00000000 --- a/website/app/assets/mvc-klick-DKonBpbs.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - o visor - - Controller - o ouvinte - - Model - Contador - - 0 - 1 - 2 - 3 - - +1 - - valor = 0 - valor = 1 - valor = 2 - valor = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 aumentar() - 2 aumentar() - - - 3 getValor() - 3 getValor() - - - 4 atualizar(valor) - 4 atualizar(valor) - - O model não conhece nem a view nem o controller. - diff --git a/website/app/assets/mvc-klick-KmFKqxH-.svg b/website/app/assets/mvc-klick-KmFKqxH-.svg deleted file mode 100644 index 47aa440e..00000000 --- a/website/app/assets/mvc-klick-KmFKqxH-.svg +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - View - die Anzeige - - Controller - der Zuhörer - - Model - Zaehler - - 0 - 1 - 2 - 3 - - +1 - - wert = 0 - wert = 1 - wert = 2 - wert = 3 - - 1 actionPerformed(e) - 1 actionPerformed(e) - - - 2 erhoehen() - 2 erhoehen() - - - 3 getWert() - 3 getWert() - - - 4 aktualisieren(wert) - 4 aktualisieren(wert) - - Das Model kennt weder die View noch den Controller. - diff --git a/website/app/assets/nb-CsT7SL7q.js b/website/app/assets/nb-CsT7SL7q.js deleted file mode 100644 index e1ac702f..00000000 --- a/website/app/assets/nb-CsT7SL7q.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Tekster som bare finnes i nettversjonen. Alt annet kommer fra appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksporter…"; -"web_export_title" = "Lagre som bilde eller PDF, eller skriv ut"; -"web_export_png" = "Bilde (PNG)"; -"web_export_svg" = "Vektorgrafikk (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Eksporten mislyktes: %@"; -"web_print_hint" = "Bruk «Eksporter… → PDF» for å lagre diagrammet og skrive det ut derfra."; -"web_delete" = "Slett"; -"web_language_title" = "Språk i grensesnittet"; -"web_simulation_start" = "Start simulering"; -"web_simulation_end" = "Avslutt simulering"; -"web_simulation_start_title" = "Kjør struktogrammet trinn for trinn"; -"web_simulation_end_title" = "Avslutt simuleringen og fortsett redigeringen"; -"web_palette_hint" = "Dra inn i struktogrammet"; -"web_tabs_label" = "Diagrammer"; -"web_status_modified" = "endret"; -"web_inspector_title" = "Egenskaper"; -"web_inspector_resize" = "Endre høyden på objektinspektøren: dra eller sveip linjen opp eller ned (dobbeltklikk: standard)"; -"web_inspector_help_drop" = "Dra elementer fra paletten til en rute: øverst = før, nederst = etter, på «ø» = erstatt. Eksisterende elementer kan flyttes med musen."; -"web_inspector_help_inline" = "Dobbeltklikk eller dobbelttrykk på et element for å redigere teksten direkte i diagrammet (Enter bruker, Esc avbryter). Det gjelder også case-navn."; -"web_inspector_help_files" = "Du kan også slippe filer i vinduet."; -"web_case_name" = "Navn på case"; -"web_case_name_placeholder" = "f.eks. 1, ok, default"; -"web_copied" = "Element kopiert."; -"web_discard_message_fmt" = "Endringene i «%@» går tapt."; -"web_err_unreadable_fmt" = "«%@» er ikke en lesbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "«%@» lagret."; -"web_saved_txt_fmt" = "Lagret som «%@». Fjern filendelsen .txt for å bruke den."; -"web_drop_impossible" = "Kan ikke slippes her"; -"web_sim_delay" = "Pause per trinn"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Språk"; -"web_codegen_comments" = "Diagramtekst som kommentarer"; -"web_codegen_empty_else" = "Skriv ut tom else-gren i koden"; -"web_copied_check" = "Kopiert ✓"; -"web_copy_manual" = "Markert, trykk Ctrl+C for å kopiere"; -"web_run_stopped_loop" = "Kjøringen stoppet: mulig uendelig løkke."; -"web_run_cancelled" = "Kjøringen avbrutt."; -"web_run_error" = "Feil under kjøring."; -"web_run_dropped_fmt" = "… %d tidligere linjer skjult"; -"web_sim_call_arguments_fmt" = "Kallet passer ikke til hodet i %@. Sjekk verdiene i parentesene."; -"web_sim_missing_return_fmt" = "%@ ble avsluttet uten å returnere en verdi med return."; -"web_sim_file_not_found_fmt" = "Det finnes ingen fil som heter %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har ikke flere linjer."; -"web_sim_null_value_fmt" = "%@ er null."; -"web_sim_unknown_variable_case_fmt" = "Ukjent variabel: %1$@. Java skiller mellom store og små bokstaver: mente du %2$@?"; -"web_sim_written_file_fmt" = "%@ (lagret)"; -"web_site_home" = "Forside"; -"web_site_imprint" = "Kolofon"; -"web_site_privacy" = "Personvern"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Skjul programmering"; -"web_scale_canvas" = "Tekststørrelse: strukturdiagram"; -"web_scale_code" = "Tekststørrelse: programmering"; -"web_live_indent" = "Rykk inn"; -"web_live_indent_title" = "Rykk inn koden: fire mellomrom per nivå (merkede linjer, ellers alt)"; -"web_live_show" = "Vis programmering"; -"web_live_resize" = "Endre bredden på kodeområdet (dobbeltklikk: tilbakestill)"; -"web_live_hint" = "Koden vokser med diagrammet ditt. Grått = plassholder: skriv i inspektøren, så vises teksten her. Klikk på en linje for å velge elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kode"; -"web_live_ph_end" = "slutt"; -"web_live_language" = "Programmeringsspråk"; -"web_live_java_class" = "Som klasse"; -"web_live_java_class_hint" = "Klassenavn = diagrammets tittel"; -"web_live_java_main" = "Med main-metode"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Ekte Java (med Swing)"; -"web_live_swing_hint" = "Ekte Java med vinduer (CheerpJ). Laster ned ca. 40 MB én gang ved første kjøring."; -"web_live_swing_windows" = "Java-vinduer"; -"web_live_swing_hide" = "Lukk Java-vinduer"; -"web_live_swing_loading" = "Java lastes inn (første gang ca. 40 MB) …"; -"web_live_swing_compiling" = "Kompilerer …"; -"web_live_swing_running" = "Programmet kjører."; -"web_live_swing_compile_failed" = "Kompileringsfeil, se over."; -"web_live_swing_failed" = "Java kunne ikke lastes inn. Sjekk internettforbindelsen."; -"web_live_hint_edit" = "Skriv Java-kode her, så bygger diagrammet seg selv. Eller arbeid i diagrammet, så vokser koden med. Grått = plassholder, bare skriv over."; -"web_live_edit_label" = "Java-kode"; -"web_parse_line_fmt" = "Linje %d: %@"; -"web_parse_missing" = "Her mangler fortsatt: %@"; -"web_parse_extra" = "For mye: %@"; -"web_parse_unclosed_string" = "Det avsluttende %@ mangler."; -"web_parse_else_without_if" = "Denne «else» har ingen «if»."; -"web_parse_case_outside_switch" = "«case» og «default» hører hjemme i en «switch»."; -"web_parse_missing_do_while" = "Etter «do { … }» mangler «while (betingelse);»."; -"web_parse_more_methods" = "Foreløpig teller bare én metode her. Lag andre underprogrammer i egne faner."; -"web_parse_missing_import" = "Øverst mangler %@ Uten denne linjen kjenner ikke Java Scanner."; -"web_parse_missing_scanner" = "Før mangler %@ Uten den linjen finnes ikke Scanneren."; -"web_parse_unknown_type" = "Java kjenner ikke denne datatypen. Mente du %@?"; -"web_quickfix_insert_fmt" = "Forslag: sett inn %@"; -"web_quickfix_replace_fmt" = "Forslag: %1$@ i stedet for %2$@"; -"web_parse_wrong_case" = "Java skiller mellom store og små bokstaver. Riktig er %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Vis strukturdiagram"; -"web_editor_hide" = "Skjul strukturdiagram"; -"web_keywords_title" = "Kode-nøkkelord"; -"web_keywords_hint" = "if, while, for i stedet for elementnavn"; -"web_palette_compact" = "Elementer alltid kompakte"; -"web_palette_compact_hint" = "Symbol med lite navn under, ellers bare når kolonnen er smal"; -"web_about_version_fmt" = "Versjon %@"; -"web_about_software" = "Programvare som brukes"; -"web_about_software_text" = "Kodeeditoren bruker CodeMirror og Lezer (MIT-lisens). Med Swing-støtte laster VisuStruct inn CheerpJ fra Leaning Technologies (Community-lisens) og javac fra OpenJDK."; -"web_about_licenses" = "Lisenstekster"; -"web_live_run" = "Kjør"; -"web_live_run_title" = "Kjør programmet: utdata og inndata vises nedenfor"; -"web_live_run_finished" = "Programmet er ferdig."; -"web_live_run_stopped" = "Stoppet."; -"web_console" = "Konsoll"; -"web_console_hide" = "Skjul konsollen"; -"web_console_resize" = "Endre høyden på konsollen"; -"web_diagram_tools" = "Diagramverktøy"; -"web_files_title" = "Filer"; -"web_files_show" = "Vis filer"; -"web_files_hide" = "Skjul filer"; -"web_files_resize" = "Endre bredden på filer"; -"web_files_new_folder" = "Ny mappe"; -"web_files_switch" = "Velg en annen mappe"; -"web_files_delete" = "Slett"; -"web_files_in_browser" = "I denne nettleserens lagring"; -"web_files_save_hint_fmt" = "Lagring legger programmet i %@."; -"web_files_empty" = "Ingenting lagret ennå. Når du lagrer, lager VisuStruct en mappe med dagens dato her."; -"web_files_reconnect_hint" = "Etter ny innlasting spør nettleseren igjen om VisuStruct kan bruke mappen din."; -"web_files_reconnect_fmt" = "Åpne mappen «%@»"; -"web_files_connect_hint" = "Velg en mappe som arbeidsområde. VisuStruct lager en mappe for hver dag der og lagrer diagram og kode."; -"web_files_connect" = "Velg mappe"; -"web_files_browser_hint" = "Denne nettleseren kan ikke åpne mapper på enheten (f.eks. på iPad). Filene dine blir i nettleserens lagring."; -"web_files_browser" = "Lagre i nettleseren"; -"web_files_delete_confirm_fmt" = "Vil du virkelig slette «%@»?"; -"web_files_folder_prompt" = "Navn på den nye mappen:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Lagret: %@"; -"web_files_create" = "Lag"; -"web_learn_ludwig_tip" = "Ludwigs tips"; -"web_game_title" = "Spill: Sant eller usant?"; -"web_game_intro" = "Ludwig viser deg verdier og en betingelse. Er betingelsen sann (true) eller usann (false)? Ti runder, stadig vanskeligere."; -"web_game_start" = "Start spillet"; -"web_game_round_fmt" = "Runde %@ av %@"; -"web_game_score_fmt" = "Poeng: %@"; -"web_game_true" = "sann (true)"; -"web_game_false" = "usann (false)"; -"web_game_right" = "Riktig!"; -"web_game_wrong" = "Ikke helt. Slik regnes det ut:"; -"web_game_next" = "Neste"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ av %@ riktige."; -"web_game_perfect" = "Alt riktig – Ludwig er imponert!"; -"web_game_good" = "Bra! Se på feilene dine og prøv igjen."; -"web_game_practice" = "Øvelse gjør mester. Les sannhetstabellen igjen og spill en runde til."; -"web_game_again" = "Spill igjen"; -"web_game_wrong_short" = "Dessverre feil."; -"web_game_practice_short" = "Øvelse gjør mester. Les forklaringene igjen og spill en runde til."; -"web_game_clicks" = "Klikk:"; -"web_game_nobody" = "Ingen lytter – ingenting skjer."; -"web_game_event_fmt" = "Klikk på «%@»: lytteren hører det og kjører koden sin."; -"web_game_try" = "Prøv nå knappene i vinduet selv."; -"web_game_time_fmt" = "%@ s igjen"; -"web_game_defused" = "✂️ Desarmert! Du fant feilen."; -"web_game_boom_time" = "💥 Bom! Tiden er ute."; -"web_game_boom_wrong" = "💥 Bom! Det var feil linje."; -"web_game_boom_part" = "💥 Pang! Riktig linje, men feilen er i en annen del."; -"web_game_boom_wrong_part" = "💥 Pang! Feilen er i en annen linje."; -"web_game_bomb_parts_hint" = "Trykk nøyaktig på den feile delen."; -"web_game_fix" = "Riktig linje:"; -"web_game_order_hint" = "Trykk på trinnene i riktig rekkefølge."; -"web_debug_step" = "Steg"; -"web_debug_step_title" = "Steg: kjør én setning og stopp igjen"; -"web_debug_continue" = "Fortsett til neste stoppunkt"; -"web_debug_paused_fmt" = "Stoppet før linje %d."; -"web_debug_gutter_title" = "Klikk på et linjenummer for å sette eller fjerne et stoppunkt"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Endre bredden på struktogrammet"; -"web_palette_resize" = "Endre bredden på elementene"; -"web_palette_hide" = "Skjul elementene"; -"web_palette_show" = "Vis elementene"; -"web_code_scopes" = "Fargede kodeblokker"; -"web_code_scopes_hint" = "Klasse, metode, løkke, forgrening i egne bokser"; -"web_pseudocode" = "Pseudokode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Gi variabelen nytt navn …"; -"web_rename_title" = "Gi variabelen nytt navn"; -"web_rename_old" = "Navn"; -"web_rename_new" = "Nytt navn"; -"web_rename_hint" = "Endrer hvert sted med nøyaktig denne skrivemåten (tall er ikke Tall), i koden og i strukturdiagrammet. Tekst i \\"…\\" og kommentarer blir værende."; -"web_rename_count_fmt" = "Forekommer %d ganger."; -"web_rename_exists_fmt" = "«%@» finnes allerede: etterpå ville begge være én variabel."; -"web_rename_invalid" = "Et navn begynner med en bokstav og inneholder bare bokstaver, sifre og _."; -"web_rename_keyword_fmt" = "«%@» er et nøkkelord i Java."; -"web_rename_apply" = "Gi nytt navn"; -"web_rename_cancel" = "Avbryt"; -`;export{e as default}; diff --git a/website/app/assets/nl-CPGJmQ49.js b/website/app/assets/nl-CPGJmQ49.js deleted file mode 100644 index 3a3986fa..00000000 --- a/website/app/assets/nl-CPGJmQ49.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Teksten die alleen in de webversie bestaan. Al het andere komt uit de Localizable.strings van de app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-structogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exporteren…"; -"web_export_title" = "Opslaan als afbeelding of PDF, of afdrukken"; -"web_export_png" = "Afbeelding (PNG)"; -"web_export_svg" = "Vectorafbeelding (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-bestand (.visustruct)"; -"web_export_failed_fmt" = "Exporteren mislukt: %@"; -"web_print_hint" = "Gebruik “Exporteren… → PDF” om het diagram op te slaan en het daarna af te drukken."; -"web_delete" = "Verwijderen"; -"web_language_title" = "Taal van de interface"; -"web_simulation_start" = "Simulatie starten"; -"web_simulation_end" = "Simulatie beëindigen"; -"web_simulation_start_title" = "Het structogram stap voor stap uitvoeren"; -"web_simulation_end_title" = "Simulatie beëindigen en verder bewerken"; -"web_palette_hint" = "Naar het structogram slepen"; -"web_tabs_label" = "Diagrammen"; -"web_status_modified" = "gewijzigd"; -"web_inspector_title" = "Eigenschappen"; -"web_inspector_resize" = "Hoogte van de objectinspector wijzigen: sleep of veeg de lijn omhoog of omlaag (dubbelklik: standaard)"; -"web_inspector_help_drop" = "Sleep elementen uit het palet op een tegel: boven = ervoor, onder = erna, op “ø” = vervangen. Bestaande elementen kun je met de muis verplaatsen."; -"web_inspector_help_inline" = "Dubbelklik of dubbeltik op een element om de tekst direct in het diagram te bewerken (Enter past toe, Esc annuleert). Ook voor case-namen."; -"web_inspector_help_files" = "Je kunt ook bestanden in het venster neerzetten."; -"web_case_name" = "Naam van de case"; -"web_case_name_placeholder" = "bijv. 1, ok, default"; -"web_copied" = "Element gekopieerd."; -"web_discard_message_fmt" = "De wijzigingen in “%@” gaan verloren."; -"web_err_unreadable_fmt" = "“%@” is geen leesbaar VisuStruct-bestand (%@)."; -"web_saved_fmt" = "“%@” opgeslagen."; -"web_saved_txt_fmt" = "Opgeslagen als “%@”. Verwijder de extensie .txt om het te gebruiken."; -"web_drop_impossible" = "Hier neerzetten is niet mogelijk"; -"web_sim_delay" = "Pauze per stap"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Taal"; -"web_codegen_comments" = "Diagramtekst als commentaar"; -"web_codegen_empty_else" = "Lege else-tak in code genereren"; -"web_copied_check" = "Gekopieerd ✓"; -"web_copy_manual" = "Geselecteerd, druk op Ctrl+C om te kopiëren"; -"web_run_stopped_loop" = "Uitvoering gestopt: mogelijk oneindige lus."; -"web_run_cancelled" = "Uitvoering geannuleerd."; -"web_run_error" = "Fout tijdens het uitvoeren."; -"web_run_dropped_fmt" = "… %d eerdere regels verborgen"; -"web_sim_call_arguments_fmt" = "De aanroep past niet bij de kop van %@. Controleer de waarden tussen de haakjes."; -"web_sim_missing_return_fmt" = "%@ is geëindigd zonder met return een waarde terug te geven."; -"web_sim_file_not_found_fmt" = "Er is geen bestand met de naam %@."; -"web_sim_end_of_file_fmt" = "Het bestand %@ heeft geen regels meer."; -"web_sim_null_value_fmt" = "%@ is null."; -"web_sim_unknown_variable_case_fmt" = "Onbekende variabele: %1$@. Java is hoofdlettergevoelig: bedoelde je %2$@?"; -"web_sim_written_file_fmt" = "%@ (opgeslagen)"; -"web_site_home" = "Startpagina"; -"web_site_imprint" = "Colofon"; -"web_site_privacy" = "Privacy"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmeren"; -"web_live_hide" = "Programmeren verbergen"; -"web_scale_canvas" = "Tekstgrootte: structuurdiagram"; -"web_scale_code" = "Tekstgrootte: programmeren"; -"web_live_indent" = "Inspringen"; -"web_live_indent_title" = "Code laten inspringen: vier spaties per niveau (geselecteerde regels, anders alles)"; -"web_live_show" = "Programmeren tonen"; -"web_live_resize" = "Breedte van het codegebied wijzigen (dubbelklik: herstellen)"; -"web_live_hint" = "De code groeit mee met je diagram. Grijs = plaatshouder: typ in de inspector en je tekst verschijnt hier. Klik op een regel om het element te selecteren."; -"web_live_ph_selector" = "variabele"; -"web_live_ph_code" = "Jouw code"; -"web_live_ph_end" = "einde"; -"web_live_language" = "Programmeertaal"; -"web_live_java_class" = "Als klasse"; -"web_live_java_class_hint" = "Klassenaam = titel van het diagram"; -"web_live_java_main" = "Met main-methode"; -"web_settings_display" = "Weergave"; -"web_live_swing" = "Echte Java (met Swing)"; -"web_live_swing_hint" = "Echt Java met vensters (CheerpJ). Downloadt eenmalig ca. 40 MB bij de eerste keer uitvoeren."; -"web_live_swing_windows" = "Java-vensters"; -"web_live_swing_hide" = "Java-vensters sluiten"; -"web_live_swing_loading" = "Java wordt geladen (de eerste keer ca. 40 MB) …"; -"web_live_swing_compiling" = "Compileren …"; -"web_live_swing_running" = "Programma draait."; -"web_live_swing_compile_failed" = "Compileerfout, zie hierboven."; -"web_live_swing_failed" = "Java kon niet worden geladen. Controleer de internetverbinding."; -"web_live_hint_edit" = "Typ hier Java-code en het diagram bouwt zichzelf op. Of werk in het diagram en de code groeit mee. Grijs = tijdelijke tekst, typ er gewoon overheen."; -"web_live_edit_label" = "Java-code"; -"web_parse_line_fmt" = "Regel %d: %@"; -"web_parse_missing" = "Hier ontbreekt nog: %@"; -"web_parse_extra" = "Te veel: %@"; -"web_parse_unclosed_string" = "De afsluitende %@ ontbreekt."; -"web_parse_else_without_if" = "Bij deze “else” hoort geen “if”."; -"web_parse_case_outside_switch" = "“case” en “default” horen in een “switch”."; -"web_parse_missing_do_while" = "Na “do { … }” ontbreekt nog “while (voorwaarde);”."; -"web_parse_more_methods" = "Voorlopig telt hier maar één methode. Maak andere subprogramma’s in aparte tabbladen."; -"web_parse_missing_import" = "Bovenaan ontbreekt %@ Zonder deze regel kent Java Scanner niet."; -"web_parse_missing_scanner" = "Eerst ontbreekt %@ Zonder deze regel bestaat de Scanner niet."; -"web_parse_unknown_type" = "Java kent dit gegevenstype niet. Bedoelde je %@?"; -"web_quickfix_insert_fmt" = "Suggestie: %@ invoegen"; -"web_quickfix_replace_fmt" = "Suggestie: %1$@ in plaats van %2$@"; -"web_parse_wrong_case" = "Java is hoofdlettergevoelig. Juist is %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Diagram tonen"; -"web_editor_hide" = "Diagram verbergen"; -"web_keywords_title" = "Code-sleutelwoorden"; -"web_keywords_hint" = "if, while, for in plaats van elementnamen"; -"web_palette_compact" = "Elementen altijd compact"; -"web_palette_compact_hint" = "Symbool met kleine naam eronder, anders alleen bij een smalle kolom"; -"web_about_version_fmt" = "Versie %@"; -"web_about_software" = "Gebruikte software"; -"web_about_software_text" = "De code-editor gebruikt CodeMirror en Lezer (MIT-licentie). Met Swing-ondersteuning laadt VisuStruct CheerpJ van Leaning Technologies (Community-licentie) en javac uit OpenJDK."; -"web_about_licenses" = "Licentieteksten"; -"web_live_run" = "Uitvoeren"; -"web_live_run_title" = "Programma uitvoeren: uitvoer en invoer verschijnen eronder"; -"web_live_run_finished" = "Programma klaar."; -"web_live_run_stopped" = "Gestopt."; -"web_console" = "Console"; -"web_console_hide" = "Console verbergen"; -"web_console_resize" = "Hoogte van de console wijzigen"; -"web_diagram_tools" = "Diagramgereedschap"; -"web_files_title" = "Bestanden"; -"web_files_show" = "Bestanden tonen"; -"web_files_hide" = "Bestanden verbergen"; -"web_files_resize" = "Breedte van bestanden wijzigen"; -"web_files_new_folder" = "Nieuwe map"; -"web_files_switch" = "Andere map kiezen"; -"web_files_delete" = "Verwijderen"; -"web_files_in_browser" = "In de opslag van deze browser"; -"web_files_save_hint_fmt" = "Opslaan zet het programma in %@."; -"web_files_empty" = "Nog niets opgeslagen. Bij het opslaan maakt VisuStruct hier een map met de datum van vandaag."; -"web_files_reconnect_hint" = "Na het herladen vraagt de browser nog eens of VisuStruct je map mag gebruiken."; -"web_files_reconnect_fmt" = "Map „%@” openen"; -"web_files_connect_hint" = "Kies een map als werkruimte. VisuStruct maakt daarin een map voor elke dag en slaat diagram en code op."; -"web_files_connect" = "Map kiezen"; -"web_files_browser_hint" = "Deze browser kan geen mappen op het apparaat openen (bijv. op de iPad). Je bestanden blijven in de opslag van deze browser."; -"web_files_browser" = "Opslaan in de browser"; -"web_files_delete_confirm_fmt" = "„%@” echt verwijderen?"; -"web_files_folder_prompt" = "Naam van de nieuwe map:"; -"web_files_default_name" = "Programma"; -"web_files_saved_fmt" = "Opgeslagen: %@"; -"web_files_create" = "Maken"; -"web_learn_ludwig_tip" = "Ludwigs tip"; -"web_game_title" = "Spel: Waar of onwaar?"; -"web_game_intro" = "Ludwig laat je waarden en een voorwaarde zien. Is de voorwaarde waar (true) of onwaar (false)? Tien rondes, steeds lastiger."; -"web_game_start" = "Spel starten"; -"web_game_round_fmt" = "Ronde %@ van %@"; -"web_game_score_fmt" = "Punten: %@"; -"web_game_true" = "waar (true)"; -"web_game_false" = "onwaar (false)"; -"web_game_right" = "Goed!"; -"web_game_wrong" = "Helaas fout. Zo reken je het uit:"; -"web_game_next" = "Verder"; -"web_game_finish" = "Resultaat"; -"web_game_done_fmt" = "%@ van %@ goed."; -"web_game_perfect" = "Alles goed – Ludwig is diep onder de indruk!"; -"web_game_good" = "Sterk! Bekijk je fouten nog eens en probeer het opnieuw."; -"web_game_practice" = "Oefening baart kunst. Lees de waarheidstabel nog eens en speel nog een ronde."; -"web_game_again" = "Nog een keer"; -"web_game_wrong_short" = "Helaas, fout."; -"web_game_practice_short" = "Oefening baart kunst. Lees de uitleg nog eens en speel nog een ronde."; -"web_game_clicks" = "Klikken:"; -"web_game_nobody" = "Niemand luistert – er gebeurt niets."; -"web_game_event_fmt" = "Klik op ‘%@’: de luisteraar hoort het en voert zijn code uit."; -"web_game_try" = "Probeer de knoppen in het venster nu zelf uit."; -"web_game_time_fmt" = "Nog %@ s"; -"web_game_defused" = "✂️ Onschadelijk gemaakt! Je hebt de fout gevonden."; -"web_game_boom_time" = "💥 Boem! De tijd is om."; -"web_game_boom_wrong" = "💥 Boem! Dat was de verkeerde regel."; -"web_game_boom_part" = "💥 Boem! Goede regel, maar de fout zit in een ander deel."; -"web_game_boom_wrong_part" = "💥 Boem! De fout zit in een andere regel."; -"web_game_bomb_parts_hint" = "Tik precies op het foute deel."; -"web_game_fix" = "De juiste regel:"; -"web_game_order_hint" = "Tik de stappen in de juiste volgorde aan."; -"web_debug_step" = "Stap"; -"web_debug_step_title" = "Stap: één opdracht uitvoeren en weer pauzeren"; -"web_debug_continue" = "Doorgaan tot het volgende breekpunt"; -"web_debug_paused_fmt" = "Gepauzeerd vóór regel %d."; -"web_debug_gutter_title" = "Klik op een regelnummer om een breekpunt te plaatsen of te verwijderen"; -"web_swap_to_code" = "Programmeren groot"; -"web_swap_to_editor" = "Structogram groot"; -"web_editor_resize" = "Breedte van het structogram wijzigen"; -"web_palette_resize" = "Breedte van de elementen wijzigen"; -"web_palette_hide" = "Elementen verbergen"; -"web_palette_show" = "Elementen tonen"; -"web_code_scopes" = "Gekleurde codeblokken"; -"web_code_scopes_hint" = "Klasse, methode, lus, keuze in eigen kaders"; -"web_pseudocode" = "Pseudocode"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Variabele hernoemen …"; -"web_rename_title" = "Variabele hernoemen"; -"web_rename_old" = "Naam"; -"web_rename_new" = "Nieuwe naam"; -"web_rename_hint" = "Wijzigt elke plek met precies deze schrijfwijze (getal is niet Getal), in de code en in het structogram. Tekst tussen \\"…\\" en commentaar blijven."; -"web_rename_count_fmt" = "Komt %d keer voor."; -"web_rename_exists_fmt" = "‘%@’ bestaat al: daarna zouden beide één variabele zijn."; -"web_rename_invalid" = "Een naam begint met een letter en bevat alleen letters, cijfers en _."; -"web_rename_keyword_fmt" = "‘%@’ is een Java-sleutelwoord."; -"web_rename_apply" = "Hernoemen"; -"web_rename_cancel" = "Annuleren"; -`;export{e as default}; diff --git a/website/app/assets/objectApp-ClGVaIKp.js b/website/app/assets/objectApp-ClGVaIKp.js deleted file mode 100644 index 288b3056..00000000 --- a/website/app/assets/objectApp-ClGVaIKp.js +++ /dev/null @@ -1,421 +0,0 @@ -const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["./projectZip-DtugKwEc.js","./index-BqSgHxq8.js","./index-CLK72BwI.css"])))=>i.map(i=>d[i]); -var Ns=Object.defineProperty;var Os=(a,e,t)=>e in a?Ns(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t;var E=(a,e,t)=>Os(a,typeof e!="symbol"?e+"":e,t);import{t as d,i as ie,s as Ps,m as Is,g as Ds,w as Rs,a as Hs,J as Bs,c as Ws,b as zs,d as Ye,p as Vs,e as Us,f as Js,h as Ks,_ as wt,j as $t,k as Xs,r as _t,l as Ys,n as Gs,o as ct}from"./index-BqSgHxq8.js";function Zs(a){const e=[];let t=0;for(;te.text===","?", ":e.text).join("")}function ms(a){const e=[[]];let t=0;for(const s of a)s.text==="<"||s.text==="("||s.text==="["||s.text==="{"?t++:(s.text===">"||s.text===")"||s.text==="]"||s.text==="}")&&t--,s.text===","&&t===0?e.push([]):e[e.length-1].push(s);return e.filter(s=>s.length>0)}function ft(a){var t,s;const e=[];for(let i=0;i{const t=ft(e).filter(i=>i.text!=="final"),s=t[t.length-1];return{name:s!=null&&s.word?s.text:"",type:lt(t.slice(0,-1))}})}function kt(a,e,t,s){var x,L;const i=ft(a),n=new Set;let o=0;for(;o"));const r=i.slice(o);if(r.length===0||a.length===0)return[];const c=a[0].from,l=r.findIndex(S=>S.text==="("),h=r.findIndex(S=>S.text==="="),u=l>0&&(h<0||lS.text==="="),y=_<0?g:g.slice(0,_);let w=y.length-1;for(;w>0&&!y[w].word;)w--;if(w<1)return[];const $=lt(y.slice(0,w)),p=m.length>1,v=[];return m.forEach((S,k)=>{var C;const j=S.findIndex(F=>F.text==="="),M=j<0?S:S.slice(0,j),A=k===0?y[w]:M.find(F=>F.word);if(!A)return;const R=M.filter(F=>F.from>A.from&&F.text==="[").length;v.push({kind:"field",name:A.text,type:$+"[]".repeat(R),params:[],visibility:xt(n,f),isStatic:n.has("static"),isAbstract:!1,from:c,to:s,headerFrom:c,headerTo:k===0?((C=M[M.length-1])==null?void 0:C.to)??A.to:A.to,shared:p,nameFrom:A.from,nameTo:A.to})}),v}function ei(a,e,t){var n;let s=[],i=e;for(;ih.text==="="),c=ye(a,i,"{","}");if(r){s.push(...a.slice(i,c)),i=c;continue}const l=s.map(h=>h.text);l.includes("class")||l.includes("interface")||l.includes("enum")||s.length>0&&t.members.push(...kt(s,t.name,t.kind==="interface",((n=a[c-1])==null?void 0:n.to)??o.to)),s=[],i=c;continue}s.push(o),i++}return t.bodyEnd=a.length>0?a[a.length-1].to:0,i}function oe(a){var n,o,r,c;const e=Zs(a),t=[];let s=[],i=0;for(;iw.text==="class"||w.text==="interface"),f=u>=0?h[u+1]:void 0;if(!(f!=null&&f.word)){i=ye(e,i,"{","}"),s=[];continue}let m=u;for(;m>0&&(fs.has(h[m-1].text)||h[m-1].text==="@");)m--;const g={name:f.text,kind:h[u].text==="interface"?"interface":"class",isAbstract:h.slice(m,u).some(w=>w.text==="abstract"),interfaces:[],members:[],from:((n=s[s.indexOf(h[m])])==null?void 0:n.from)??h[m].from,to:0,headerFrom:((o=s[s.indexOf(h[m])])==null?void 0:o.from)??h[m].from,headerTo:h[h.length-1].to,nameFrom:f.from,nameTo:f.to,bodyEnd:0};let _=u+2;((r=h[_])==null?void 0:r.text)==="<"&&(_=ye(h,_,"<",">"));let y="";for(;_")-1:w.word&&y==="extends"&&g.kind==="class"?g.superclass??(g.superclass=w.text):w.word&&y&&g.interfaces.push(w.text)}i=ei(e,i+1,g),g.to=((c=e[i-1])==null?void 0:c.to)??a.length,t.push(g),s=[]}return t}function Fe(a){const e=/^([\p{L}_$][\p{L}\p{N}_$]*)(\[\])+$/u.exec(a);if(e)return{name:e[1],many:!0};const t=/^(?:ArrayList|LinkedList|List|Set|HashSet|TreeSet|Collection|Queue|Deque|ArrayDeque|Stack|Vector)<([\p{L}_$][\p{L}\p{N}_$]*)>$/u.exec(a);return t?{name:t[1],many:!0}:{name:a,many:!1}}function ti(a){const e=new Set(a.map(s=>s.name)),t=[];for(const s of a){s.superclass&&e.has(s.superclass)&&t.push({kind:"extends",from:s.name,to:s.superclass});for(const i of s.interfaces)e.has(i)&&t.push({kind:s.kind==="interface"?"extends":"implements",from:s.name,to:i});for(const i of s.members){if(i.kind!=="field")continue;const n=Fe(i.type);e.has(n.name)&&t.push({kind:"association",from:s.name,to:n.name,role:i.name,multiplicity:n.many?"0..*":"1"})}}return t}function Ge(a){const e=a.params.map(t=>`${t.name}: ${t.type}`).join(", ");return a.kind==="field"?`${a.visibility} ${a.name}: ${a.type}`:a.kind==="constructor"?`${a.visibility} ${a.name}(${e})`:`${a.visibility} ${a.name}(${e}): ${a.type}`}const si=/^[\p{L}_$][\p{L}\p{N}_$]*$/u;function Ve(a){var c;let e=a.trim().replace(/;$/,"").trim();if(!e)return;let t;/^[+\-#~]/.test(e)&&(t=e[0],e=e.slice(1).trim());let s=!1;const i=e.split(/\s+/);for(;["static","public","private","protected"].includes(i[0]);){const l=i.shift();l==="static"?s=!0:t??(t=l==="public"?"+":l==="private"?"-":"#")}e=i.join(" ");const n=e.indexOf("(");if(n>=0){const l=e.lastIndexOf(")");if(lg.trim()).filter(Boolean).map(g=>{const _=/^([\p{L}_$][\p{L}\p{N}_$]*)\s*:\s*(.+)$/u.exec(g);if(_)return{name:_[1],type:_[2].replace(/\s+/g,"")};const y=/^(.+)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(g);return y?{name:y[2],type:y[1].replace(/\s+/g,"")}:{name:g,type:"int"}});return{kind:"method",visibility:t,name:h,type:u==null?void 0:u.replace(/\s+/g,""),params:m,isStatic:s}}const o=/^([\p{L}_$][\p{L}\p{N}_$]*)\s*(?::\s*(.+))?$/u.exec(e);if(o)return{kind:"field",visibility:t,name:o[1],type:(c=o[2])==null?void 0:c.replace(/\s+/g,""),params:[],isStatic:s};const r=/^(.+?)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(e);if(r)return{kind:"field",visibility:t,name:r[2],type:r[1].replace(/\s+/g,""),params:[],isStatic:s}}const ii={"+":"public ","-":"private ","#":"protected ","~":""};function ni(a){return/^(int|long|short|byte)$/.test(a)?"0":/^(double|float)$/.test(a)?"0.0":a==="boolean"?"false":a==="char"?"' '":"null"}function bs(a,e,t){const s=e.kind==="interface",i=s?"":ii[a.visibility??(a.kind==="field"?"-":"+")],n=a.isStatic?"static ":"";if(a.kind==="field")return`${i}${n}${a.type??"int"} ${a.name}`;const o=a.params.map(r=>`${r.type} ${r.name}`).join(", ");return a.name===e.name?`${i}${a.name}(${o})`:`${i}${n}${t&&!s?"abstract ":""}${a.type??"void"} ${a.name}(${o})`}const Ue=(a,e)=>a.lastIndexOf(` -`,e-1)+1;function mt(a,e,t){const s=Ue(a,e),i=a.slice(s,e);let n=t;for(;na.slice(0,e)+s+a.slice(t);function ai(a,e){const[t,s]=mt(a,e.from,e.to),i=a.slice(0,t).match(/\n\s*\n$/);return Y(a,i?t-i[0].length+1:t,s,"")}function ri(a,e,t){const s=[e,...e.members.filter(i=>i.kind==="constructor")].map(i=>[i.nameFrom,i.nameTo]);for(const[i,n]of s.sort((o,r)=>r[0]-o[0]))a=Y(a,i,n,t);return a}function Ze(a,e,t){const s=a.slice(e.headerFrom,e.headerTo),i=e.nameTo-e.headerFrom,n=s.slice(0,i);let o=s.slice(i).replace(/\s*\bextends\s+[\p{L}_$][\p{L}\p{N}_$<>,\s]*?(?=\s*\bimplements\b|\s*$)/u,"");return t&&(o=` extends ${t}`+o),Y(a,e.headerFrom,e.headerTo,n+o)}function jt(a,e,t){if(e.interfaces.includes(t))return a;const s=a.slice(e.headerFrom,e.headerTo),i=e.kind==="interface"?"extends":"implements",n=new RegExp(`\\b${i}\\s+[\\p{L}_$][\\p{L}\\p{N}_$<>,\\s]*?(?=\\s*$)`,"u"),o=n.test(s)?s.replace(n,r=>`${r.replace(/\s+$/,"")}, ${t}`):`${s.replace(/\s+$/,"")} ${i} ${t}${/\s$/.test(s)?" ":""}`;return Y(a,e.headerFrom,e.headerTo,o)}function St(a,e,t){if(!e.interfaces.includes(t))return a;const s=a.slice(e.headerFrom,e.headerTo),i=e.kind==="interface"?"extends":"implements",n=new RegExp(`\\s*\\b${i}\\s+([\\p{L}_$][\\p{L}\\p{N}_$<>,\\s]*?)(?=\\s*$)`,"u"),o=s.replace(n,(r,c)=>{const l=c.split(",").map(h=>h.trim()).filter(h=>h&&h!==t);return l.length?` ${i} ${l.join(", ")}`:""});return Y(a,e.headerFrom,e.headerTo,o)}function Lt(a,e,t,s){if(e.kind!=="field"||e.shared)return a;const i=a.slice(e.headerFrom,e.headerTo),n=i.lastIndexOf(e.type,e.nameFrom-e.headerFrom);if(n<0)return a;const o=i.slice(0,n)+t+i.slice(n+e.type.length);return Y(a,e.headerFrom,e.to,`${o}${s?` = ${s}`:""};`)}function Et(a,e,t){const s=a.slice(e.headerFrom,e.headerTo),i=t?s.replace(/\bclass\b/,"abstract class"):s.replace(/\babstract\s+/,"");return Y(a,e.headerFrom,e.headerTo,i)}function ci(a,e){const t=a.slice(e.headerFrom,e.nameFrom);return{isPublic:/\bpublic\b/.test(t),isFinal:/\bfinal\b/.test(t)}}function li(a,e,t){const s=a.slice(e.headerFrom,e.headerTo),i=/\bpublic\s+/.test(s.slice(0,e.nameFrom-e.headerFrom)),n=t?i?s:`public ${s}`:s.replace(/\bpublic\s+/,"");return Y(a,e.headerFrom,e.headerTo,n)}function Ne(a,e,t){const s=a.slice(e.headerFrom,e.headerTo),i=t?/\bfinal\b/.test(s)?s:s.replace(/\babstract\s+/,"").replace(/\bclass\b/,"final class"):s.replace(/\bfinal\s+/,"");return Y(a,e.headerFrom,e.headerTo,i)}function Ee(a,e,t){const s=Ve(t);if(!s)return a;const i=oi(a,e),n=bs(s,e,e.kind==="interface");if(s.kind==="field"){const h=[...e.members].reverse().find(m=>m.kind==="field"),u=h?mt(a,h.from,h.to)[1]:hi(a,e),f=a[u-1]===` -`?"":` -`;return Y(a,u,u,`${f}${i}${n}; -`)}let o;if(e.kind==="interface")o=`${i}${n}; -`;else{const h=s.type??"void",f=!(s.name===e.name)&&h!=="void"?`${i} return ${ni(h)}; -`:`${i} // Dein Code -`;o=`${i}${n} { -${f}${i}} -`}const r=Ue(a,e.bodyEnd);if(!/^\s*$/.test(a.slice(r,e.bodyEnd)))return Y(a,e.bodyEnd,e.bodyEnd,` -`+o);const c=a.slice(0,r),l=/\n[ \t]*\n$/.test(c)||/\{[ \t]*\n$/.test(c)?"":` -`;return Y(a,r,r,l+o)}function hi(a,e){const t=a.indexOf("{",e.headerTo)+1,s=/^[ \t]*\n/.exec(a.slice(t));return s?t+s[0].length:t}function Qe(a,e,t,s){const i=Ve(s);if(!i)return a;if(t.kind==="field"!==(i.kind==="field")){const l=xe(a,t),h=oe(l).find(u=>u.name===e.name);return h?Ee(l,h,s):l}if(t.shared)return Y(a,t.nameFrom,t.nameTo,i.name);i.visibility??(i.visibility=t.visibility),(i.kind==="field"||t.kind!=="constructor")&&(i.type??(i.type=t.type));const o=bs(i,e,t.isAbstract),r=a.slice(t.headerFrom,t.headerTo),c=/\bfinal\s+/.test(r)&&!/\bfinal\b/.test(o)?o.replace(/^((?:public|private|protected)\s+)?/,"$1final "):o;return Y(a,t.headerFrom,t.headerTo,c)}function xe(a,e){if(e.shared)return a;const[t,s]=mt(a,e.from,e.to);return Y(a,t,s,"")}const Z="src";function we(a){const e=a.indexOf(":");return e<0?{root:Z,rest:a}:{root:a.slice(0,e),rest:a.slice(e+1)}}const qe=(a,e)=>a===Z?e:`${a}:${e}`,re=(a,e)=>qe(a,e),X=a=>we(a).rest,U=a=>we(a).root,D=a=>{const{root:e,rest:t}=we(a);return qe(e,t.includes("/")?t.slice(0,t.lastIndexOf("/")).replace(/\//g,"."):"")},Q=a=>{const e=we(a).rest;return e.slice(e.lastIndexOf("/")+1)},le=(a,e)=>{const{root:t,rest:s}=we(a);return qe(t,s?`${s.replace(/\./g,"/")}/${e}`:e)},di=a=>{const{root:e,rest:t}=we(a);return`${e}/${t}`},ui=/^[\p{L}\p{N}_$-][\p{L}\p{N}_$.-]*$/u,Je=a=>ui.test(a)&&!a.includes("..");function ue(a){const e=new Set([Z,...a.sourceFolders??[]]);for(const t of a.files)e.add(U(t.path));for(const t of a.packages)e.add(U(t));return[...e].sort((t,s)=>t===Z?-1:s===Z?1:t.localeCompare(s))}const pi=/^[a-z_$][\p{L}\p{N}_$]*(\.[a-z_$][\p{L}\p{N}_$]*)*$/u,Mt=a=>pi.test(a);function ce(a,e){const t=new Set(a.packages);for(const i of a.files)t.add(D(i.path));const s=ue(a);return[...t].filter(i=>X(i)!==""||a.files.some(n=>D(n.path)===i)).filter(i=>e===void 0||U(i)===e).sort((i,n)=>{const o=s.indexOf(U(i))-s.indexOf(U(n));if(o)return o;const[r,c]=[X(i),X(n)];return r===""?1:c===""?-1:r.localeCompare(c)})}function fi(a,e){return a.files.filter(t=>D(t.path)===e).sort((t,s)=>t.path.localeCompare(s.path))}function bt(a,e){const t=X(e),i=/^(\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)*)package\s+[\w.]+\s*;[ \t]*\n?/.exec(a);return i?t?`${i[1]}package ${t}; -`+a.slice(i[0].length):i[1]+a.slice(i[0].length).replace(/^\n/,""):t?`package ${t}; - -${a.replace(/^\n+/,"")}`:a}function mi(a){var e;return((e=/^\s*(?:(?:\/\/[^\n]*|\/\*[\s\S]*?\*\/)\s*)*package\s+([\w.]+)\s*;/.exec(a))==null?void 0:e[1])??""}function Ct(a){return a.files.flatMap(e=>oe(e.text).map(t=>({...t,path:e.path,pkg:X(D(e.path)),root:U(e.path)})))}function $e(a,e,t){return{...a,files:a.files.map(s=>s.path===e?{...s,text:t}:s)}}function et(a,e,t){return{...a,files:[...a.files.filter(s=>s.path!==e),{path:e,text:t}]}}function Tt(a,e){const t=D(e);return{...a,files:a.files.filter(s=>s.path!==e),packages:t&&!a.packages.includes(t)?[...a.packages,t]:a.packages}}function Oe(a,e,t){const s=D(t);return{...a,files:a.files.map(i=>i.path===e?{path:t,text:D(e)===s?i.text:bt(i.text,s)}:i)}}function bi(a,e){return a.packages.includes(e)?a:{...a,packages:[...a.packages,e]}}function vi(a,e,t){const s=i=>i===e?t:i.startsWith(e+".")?t+i.slice(e.length):i;return{...a,packages:[...new Set(a.packages.map(s))],files:a.files.map(i=>{const n=D(i.path),o=s(n);return o===n?i:{path:le(o,Q(i.path)),text:bt(i.text,o)}})}}function gi(a,e){return ue(a).includes(e)?a:{...a,sourceFolders:[...a.sourceFolders??[],e]}}function yi(a,e,t){const s=i=>U(i)===e?qe(t,X(i)):i;return{...a,sourceFolders:(a.sourceFolders??[]).map(i=>i===e?t:i).filter(i=>i!==Z),packages:a.packages.map(s),files:a.files.map(i=>U(i.path)===e?{...i,path:qe(t,we(i.path).rest)}:i)}}function wi(a,e){return e===Z?a:{...a,sourceFolders:(a.sourceFolders??[]).filter(t=>t!==e),packages:a.packages.filter(t=>U(t)!==e),files:a.files.filter(t=>U(t.path)!==e)}}function $i(a,e){return{...a,packages:a.packages.filter(t=>t!==e),files:a.files.filter(t=>D(t.path)!==e)}}function _i(a,e,t){return bt(`public ${t} ${e} { -} -`,a)}function At(a,e){var s,i;const t=[];for(const n of e){const o=n.text.replace(/\r\n/g,` -`),r=mi(o),c=(s=/\bpublic\s+(?:abstract\s+|final\s+)*(?:class|interface|enum)\s+(\w+)/.exec(o))==null?void 0:s[1],l=/\.java$/i.test(n.name)?Q(n.name):`${c??((i=oe(o)[0])==null?void 0:i.name)??"Main"}.java`;t.push({path:le(r,l),text:o})}return{name:a,files:t,packages:[]}}function Pe(a){return JSON.stringify({format:"visuobject",version:1,...a})}function qt(a){try{const e=JSON.parse(a);if(!Array.isArray(e.files))return;const t=e.files.filter(c=>typeof(c==null?void 0:c.path)=="string"&&typeof(c==null?void 0:c.text)=="string"),s={name:typeof e.name=="string"&&e.name?e.name:"Projekt",files:t,packages:Array.isArray(e.packages)?e.packages.filter(c=>typeof c=="string"):[]},i=Array.isArray(e.sourceFolders)?e.sourceFolders.filter(c=>typeof c=="string"&&Je(c)&&c!==Z):[];i.length&&(s.sourceFolders=i);const n=ys(e.uml);n&&(s.uml=n);const o=vs(e.notes);o&&(s.notes=o);const r=gs(e.objects);return r&&(s.objects=r),s}catch{return}}function vs(a){if(!Array.isArray(a))return;const e=[];for(const t of a){if(!t||typeof t.id!="string"||typeof t.text!="string"||typeof t.x!="number"||typeof t.y!="number")continue;const s=Array.isArray(t.attach)?t.attach.filter(i=>typeof i=="string"):[];e.push({id:t.id,text:t.text,x:t.x,y:t.y,...s.length?{attach:s}:{}})}return e.length?e:void 0}function gs(a){if(!Array.isArray(a))return;const e=[];for(const t of a){if(!t||typeof t.id!="string"||typeof t.name!="string"||typeof t.cls!="string"||typeof t.x!="number"||typeof t.y!="number")continue;const s={};if(t.values&&typeof t.values=="object")for(const[i,n]of Object.entries(t.values))typeof n=="string"&&(s[i]=n);e.push({id:t.id,name:t.name,cls:t.cls,x:t.x,y:t.y,values:s})}return e.length?e:void 0}function ys(a){if(!a||typeof a!="object"||Array.isArray(a))return;const e={};for(const[t,s]of Object.entries(a))s&&typeof s=="object"&&!Array.isArray(s)&&(e[t]=s);return Object.keys(e).length?e:void 0}function Ft(){return{name:"Zoo",packages:[],uml:{"Besitzer.hunde":{kind:"aggregation",name:"führt aus",reading:"forward",sourceMultiplicity:"1"}},files:[{path:"tiere/Tier.java",text:`package tiere; - -public class Tier { - private String name; - private int alter; - - public Tier(String name, int alter) { - this.name = name; - this.alter = alter; - } - - public String getName() { - return name; - } - - public void lautGeben() { - System.out.println("..."); - } -} -`},{path:"tiere/Hund.java",text:`package tiere; - -public class Hund extends Tier { - private String rasse; - - public Hund(String name, int alter, String rasse) { - super(name, alter); - this.rasse = rasse; - } - - public void lautGeben() { - System.out.println("Wuff"); - } -} -`},{path:"personen/Besitzer.java",text:`package personen; - -import java.util.ArrayList; -import tiere.Hund; - -public class Besitzer { - private String name; - private ArrayList hunde = new ArrayList<>(); - - public void gassiGehen() { - for (Hund h : hunde) { - h.lautGeben(); - } - } -} -`}]}}const xi=a=>a.replace(/\/\*[\s\S]*?\*\/|\/\/[^\n]*|"(?:\\.|[^"\\\n])*"|'(?:\\.|[^'\\\n])*'/g,e=>" ".repeat(e.length));function ki(a,e){const t=a.map(i=>i.name),s=new Map;for(const i of a){const n=e(i);if(!n)continue;const o=xi(n),r=new Set([i.superclass,...i.interfaces,...i.members.filter(c=>c.kind==="field"&&!c.isStatic).map(c=>Fe(c.type).name)]);for(const c of i.members){if(c.kind==="field")continue;const l=o.slice(c.from,c.to);for(const h of t){if(h===i.name||r.has(h)||!new RegExp(`\\b${h}\\b`).test(l))continue;const f=new RegExp(`\\bnew\\s+${h}\\s*[(<\\[{]`).test(l)?"create":"use",m=`${i.name}>${h}`,g=s.get(m)??{from:i.name,to:h,kind:f,methods:[]};f==="create"&&(g.kind="create");const _=c.kind==="constructor"?`${c.name}()`:`${c.name}()`;g.methods.includes(_)||g.methods.push(_),s.set(m,g)}}}return[...s.values()]}const Nt=["1","0..1","0..*","1..*","*"];function Me(a){const e=a.trim().split("..").pop().trim();return e==="*"||e==="n"||/^\d+$/.test(e)&&Number(e)>1}const V=(a,e)=>`${a}.${e}`;function Ot(a){return ti(a).filter(e=>e.kind!=="association")}function Pt(a,e={}){const t=new Set(a.map(c=>c.name)),s=[];for(const c of a)for(const l of c.members){if(l.kind!=="field"||l.isStatic)continue;const h=Fe(l.type);t.has(h.name)&&h.name!==c.name&&s.push({cls:c,field:l,target:h.name,many:h.many})}const i=new Set,n=[],o=(c,l)=>c&&Me(c)===l?c:l?"0..*":"1",r=c=>{const l=e[V(c.cls.name,c.field.name)];return!!l&&(l.kind!==void 0||l.name!==void 0||l.reading!==void 0||l.navigable!==void 0||l.targetEnd!==void 0||l.sourceEnd!==void 0)};for(const c of s){if(i.has(c))continue;i.add(c);const l=s.filter(_=>!i.has(_)&&_.cls.name===c.target&&_.target===c.cls.name);let h=l.length===1&&s.filter(_=>_.cls.name===c.cls.name&&_.target===c.target).length===1?l[0]:void 0;h&&i.add(h);let u=c;h&&!r(c)&&r(h)&&([u,h]=[h,c]);const f=V(u.cls.name,u.field.name),m=e[f]??{},g=h?e[V(h.cls.name,h.field.name)]??{}:{};n.push({key:f,from:u.cls.name,to:u.target,kind:m.kind??"association",name:m.name,reading:m.reading??(m.name?"forward":void 0),navTo:m.targetEnd??(m.navigable===!1?"unspecified":"navigable"),navFrom:h?"navigable":m.sourceEnd??"unspecified",role:u.field.name,multiplicity:o(m.multiplicity,u.many),sourceRole:h?h.field.name:m.sourceRole,sourceMultiplicity:h?o(g.multiplicity,h.many):m.sourceMultiplicity,field:u.field,back:h&&{key:V(h.cls.name,h.field.name),field:h.field}})}return n}function ji(a,e,t){const s=e.charAt(0).toLowerCase()+e.slice(1)+(t?"Liste":"");let i=s;for(let n=2;a.members.some(o=>o.name===i);n++)i=s+n;return i}function It(a,e){return e?{type:`ArrayList<${a}>`,init:"new ArrayList<>()"}:{type:a}}function tt(a,e){if(!e.includes("."))return a;const t=e.replace(/\./g,"\\."),s=e.slice(0,e.lastIndexOf("."));if(new RegExp(`^\\s*import\\s+(${t}|${s.replace(/\./g,"\\.")}\\.\\*)\\s*;`,"m").test(a))return a;const i=[...a.matchAll(/^[ \t]*import\s+[\w.*]+\s*;[ \t]*\n/gm)];if(i.length){const o=i[i.length-1],r=o.index+o[0].length;return a.slice(0,r)+`import ${e}; -`+a.slice(r)}const n=/^\s*package\s+[\w.]+\s*;[ \t]*\n/.exec(a);return n?a.slice(0,n[0].length)+` -import ${e}; -`+a.slice(n[0].length).replace(/^(?!\n)/,` -`):`import ${e}; - -${a}`}function Si(a,e,t){const s={...a[e],...t};for(const n of Object.keys(s))(s[n]===void 0||s[n]==="")&&delete s[n];s.kind==="association"&&delete s.kind,delete s.navigable,s.targetEnd==="navigable"&&delete s.targetEnd,s.sourceEnd==="unspecified"&&delete s.sourceEnd;const i={...a};return Object.keys(s).length?i[e]=s:delete i[e],i}function ke(a,e,t){if(!a[e]||e===t)return a;const s={...a,[t]:a[e]};return delete s[e],s}function Li(a,e){const t=new Map(a.map(o=>[o.name,o])),s=[],i=new Set;for(let o=t.get(e);o&&!i.has(o.name);o=o.superclass?t.get(o.superclass):void 0)i.add(o.name),s.unshift(o);const n=[];for(const o of s)for(const r of o.members){if(r.kind!=="field"||r.isStatic||n.some(l=>l.name===r.name))continue;const c=Fe(r.type);n.push({name:r.name,type:r.type,many:c.many,...t.has(c.name)?{target:c.name}:{}})}return n}function Ei(a){return/^(int|long|short|byte)$/.test(a)?"0":/^(double|float)$/.test(a)?"0.0":a==="boolean"?"false":a==="char"?"''":"null"}function Ce(a){var s;const e=(a??"").trim();return!e||e==="null"?[]:(((s=/^\[(.*)\]$/s.exec(e))==null?void 0:s[1])??e).split(",").map(i=>i.trim()).filter(i=>/^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(i)&&i!=="null")}function Mi(a,e,t){if(!a.many)return t;const s=Ce(e);return`[${(s.includes(t)?s:[...s,t]).join(", ")}]`}function Dt(a,e,t){const s=new Map(a.map(o=>[o.name,o])),i=new Set,n=o=>{if(!o||i.has(o))return!1;if(o===t)return!0;i.add(o);const r=s.get(o);return!!r&&(n(r.superclass)||r.interfaces.some(n))};return n(e)}function Ci(a,e){const t=a.charAt(0).toLowerCase()+a.slice(1);for(let s=1;;s++)if(!e.includes(`${t}${s}`))return`${t}${s}`}const N=a=>a.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]),Ti=/^[\p{L}_$][\p{L}\p{N}_$]*$/u;class Ai{constructor(e){E(this,"selected");E(this,"live");E(this,"suppressClick",!1);E(this,"lastClick",{id:"",time:0});this.host=e,e.canvas.addEventListener("pointerdown",t=>this.onPointerDown(t)),e.canvas.addEventListener("click",t=>this.onClick(t))}get objects(){var e;return((e=this.live)==null?void 0:e.objects)??this.host.objects()}find(e){return e?this.objects.find(t=>t.id===e):void 0}change(e,t){this.host.setObjects(this.host.objects().map(s=>s.id===e?{...s,...t}:s))}fields(e){return Li(this.host.classes(),e.cls)}valueOf(e,t){return e.values[t.name]??Ei(t.type)}render(){const{canvas:e}=this.host;for(const t of e.querySelectorAll(".uml-object, .vo-empty"))t.remove();if(this.objects.length===0){const t=document.createElement("p");t.className="vo-empty",t.textContent=d(this.host.classes().some(s=>s.kind==="class"&&!s.isAbstract)?"web_visuobject_obj_empty":"web_visuobject_obj_no_classes"),e.append(t)}for(const t of this.objects)e.append(this.box(t));this.drawLinks(),this.renderInspector()}box(e){const t=document.createElement("div"),s=this.selected===e.id&&!this.live,i=this.host.classes().some(o=>o.name===e.cls);t.className=`uml-class uml-object${s?" selected":""}${i?"":" unknown"}`,t.dataset.obj=e.id,t.style.left=`${e.x}px`,t.style.top=`${e.y}px`;const n=this.fields(e).map(o=>{var c;return`
  • ${N(o.name)} = ${N(this.valueOf(e,o))}
  • `}).join("");return t.innerHTML=`${s?``:""} -
    ${N(e.name)} : ${N(e.cls)}
    -
      ${n}
    `,t}drawLinks(){var l;const{canvas:e,lines:t}=this.host,s=new Map;for(const h of e.querySelectorAll(".uml-object"))s.set(h.dataset.obj,h);let i=0,n=0;for(const h of s.values())i=Math.max(i,h.offsetLeft+h.offsetWidth+40),n=Math.max(n,h.offsetTop+h.offsetHeight+40);t.setAttribute("width",String(i)),t.setAttribute("height",String(n));const o=h=>({x:h.offsetLeft,y:h.offsetTop,w:h.offsetWidth,h:h.offsetHeight}),r=[],c=new Map;for(const h of this.objects){const u=s.get(h.id);if(u){for(const f of this.fields(h))if(f.target)for(const m of Ce(this.valueOf(h,f))){const g=this.objects.find(W=>W.name===m),_=g&&s.get(g.id);if(!_||_===u)continue;const y=[h.id,g.id].sort().join("|"),w=c.get(y)??0;c.set(y,w+1);const $=o(u),p=o(_),v={x:$.x+$.w/2,y:$.y+$.h/2},x={x:p.x+p.w/2,y:p.y+p.h/2},L=Math.hypot(x.x-v.x,x.y-v.y)||1,S=-(x.y-v.y)/L*w*14,k=(x.x-v.x)/L*w*14,j=Rt($,{x:v.x+S,y:v.y+k},{x:x.x+S,y:x.y+k}),M=Rt(p,{x:x.x+S,y:x.y+k},{x:v.x+S,y:v.y+k}),A=(j.x-M.x)/(Math.hypot(j.x-M.x,j.y-M.y)||1),R=(j.y-M.y)/(Math.hypot(j.x-M.x,j.y-M.y)||1),C={x:M.x+A*22-R*10,y:M.y+R*22+A*10},F=(l=this.live)==null?void 0:l.changed.has(`${h.id}.${f.name}`);r.push(``),r.push(`${N(f.name)}`)}}}t.innerHTML=r.join("")}renderInspector(){const{inspector:e}=this.host;if(this.live)return this.renderDebugger(this.live);const t=this.find(this.selected);if(!t){delete e.dataset.rel,e.innerHTML=`

    ${N(d("web_visuobject_obj_hint"))}

    `;return}e.dataset.rel=`obj:${t.id}`;const s=this.host.classes().filter(r=>r.kind==="class"),i=this.objects.filter(r=>r.id!==t.id),n=r=>{const c=this.valueOf(t,r);if(r.target&&!r.many){const l=i.filter(h=>Dt(this.host.classes(),h.cls,r.target));return``}return``},o=this.fields(t).map(r=>`${N(r.name)} : ${N(r.type)}${n(r)}`).join("");e.innerHTML=` -
    - - -
    - - ${o||``}
    ${N(d("web_visuobject_obj_attribute"))}${N(d("web_visuobject_obj_value"))}
    ${N(d("web_visuobject_insp_no_fields"))}
    -
    `,e.querySelector("[data-field=name]").addEventListener("change",r=>{const c=r.target.value.trim();if(!Ti.test(c))return this.host.message(d("web_visuobject_obj_bad_name",c));if(i.some(h=>h.name===c))return this.host.message(d("web_visuobject_obj_name_taken",c));this.host.message("");const l=t.name;this.host.setObjects(this.host.objects().map(h=>{const u=Object.fromEntries(Object.entries(h.values).map(([f,m])=>[f,Ce(m).includes(l)?m.replace(new RegExp(`(^|[\\s,\\[])${l}(?=$|[\\s,\\]])`,"gu"),`$1${c}`):m]));return h.id===t.id?{...h,name:c,values:u}:{...h,values:u}}))}),e.querySelector("[data-field=cls]").addEventListener("change",r=>this.change(t.id,{cls:r.target.value}));for(const r of e.querySelectorAll("[data-objval]"))r.addEventListener("change",()=>this.change(t.id,{values:{...t.values,[r.dataset.objval]:r.value.trim()}}));e.querySelector("[data-obj-delete]").addEventListener("click",()=>this.remove(t.id))}renderDebugger(e){const{inspector:t}=this.host;delete t.dataset.rel;const s=e.frames[e.frame],i=e.frames.map((o,r)=>`
  • `).join(""),n=((s==null?void 0:s.vars)??[]).map(o=>`${N(o.name)}${N(o.type)}${N(o.value)}`).join("");t.innerHTML=` -
    -

    ${N(d("web_visuobject_dbg_stack"))}

      ${i||`
    1. ${N(d("web_visuobject_obj_live_hint"))}
    2. `}
    -

    ${N(d("web_visuobject_dbg_vars"))}

    - - ${n||``}
    ${N(d("web_visuobject_dbg_var_name"))}${N(d("web_visuobject_dbg_var_type"))}${N(d("web_visuobject_obj_value"))}
    ${N(d("web_visuobject_dbg_no_vars"))}
    -
    -
    `;for(const o of t.querySelectorAll("[data-frame]"))o.addEventListener("click",()=>e.onFrame(Number(o.dataset.frame)))}add(e){const t=this.host.objects(),s=Ci(e,t.map(r=>r.name)),i=[...this.host.canvas.querySelectorAll(".uml-object")].map(r=>({x:r.offsetLeft,y:r.offsetTop,w:r.offsetWidth,h:r.offsetHeight}));let n={x:24,y:24};e:for(let r=24;r<4e3;r+=40)for(let c=24;cc+200+40<=l.x||l.x+l.w+40<=c||r+110+30<=l.y||l.y+l.h+30<=r)){n={x:c,y:r};break e}const o=`o${Date.now().toString(36)}`;this.selected=o,this.host.setObjects([...t,{id:o,name:s,cls:e,x:n.x,y:n.y,values:{}}])}remove(e){const t=this.host.objects().find(s=>s.id===e);t&&(this.selected=void 0,this.host.setObjects(this.host.objects().filter(s=>s.id!==e).map(s=>({...s,values:Object.fromEntries(Object.entries(s.values).map(([i,n])=>{const o=Ce(n);return o.includes(t.name)?[i,/^\[/.test(n.trim())?`[${o.filter(r=>r!==t.name).join(", ")}]`:"null"]:[i,n]}))}))))}onKey(e){return this.live||!this.selected?!1:e.key==="Delete"||e.key==="Backspace"?(e.preventDefault(),this.remove(this.selected),!0):!1}select(e){this.selected=e,this.render(),this.host.canvas.focus({preventScroll:!0})}onPointerDown(e){if(e.button!==0)return;const t=e.target,s=t.closest("[data-obj-connect]"),i=t.closest(".uml-object");if(s&&i&&!this.live)return this.startLink(e,i);i&&this.drag(e,i)}onClick(e){var o;if(this.suppressClick){this.suppressClick=!1;return}if(this.live)return;const t=e.target.closest(".uml-object"),s=t==null?void 0:t.dataset.obj,i=Date.now(),n=!!s&&this.lastClick.id===s&&i-this.lastClick.time<450;this.lastClick={id:s??"",time:n?0:i},this.select(s),n&&((o=this.host.inspector.querySelector("[data-field=name]"))==null||o.select())}drag(e,t){const s=this.find(t.dataset.obj);if(!s)return;const i={x:e.clientX,y:e.clientY};let n=!1,o={x:s.x,y:s.y};const r=l=>{const h=l.clientX-i.x,u=l.clientY-i.y;!n&&Math.hypot(h,u)<4||(n=!0,o={x:Math.max(0,Math.round(s.x+h)),y:Math.max(0,Math.round(s.y+u))},t.style.left=`${o.x}px`,t.style.top=`${o.y}px`,this.drawLinks())},c=()=>{if(window.removeEventListener("pointermove",r),window.removeEventListener("pointerup",c),!!n){if(this.suppressClick=!0,this.live)return Object.assign(s,o),this.live.move(s.id,o);this.selected=s.id,this.change(s.id,o)}};window.addEventListener("pointermove",r),window.addEventListener("pointerup",c)}startLink(e,t){e.preventDefault();const s=this.find(t.dataset.obj);if(!s)return;const{canvas:i,lines:n}=this.host,o=document.createElementNS("http://www.w3.org/2000/svg","line");o.setAttribute("class","vo-rel ghost"),n.append(o);const r={x:t.offsetLeft+t.offsetWidth,y:t.offsetTop+t.offsetHeight/2},c=u=>{var m;const f=(m=document.elementFromPoint(u.clientX,u.clientY))==null?void 0:m.closest(".uml-object");return f&&f!==t?f:void 0},l=u=>{var m;const f=i.getBoundingClientRect();o.setAttribute("x1",String(r.x)),o.setAttribute("y1",String(r.y)),o.setAttribute("x2",String(u.clientX-f.left+i.scrollLeft)),o.setAttribute("y2",String(u.clientY-f.top+i.scrollTop));for(const g of i.querySelectorAll(".uml-object.drop"))g.classList.remove("drop");(m=c(u))==null||m.classList.add("drop")},h=u=>{window.removeEventListener("pointermove",l),window.removeEventListener("pointerup",h),o.remove();const f=c(u);f==null||f.classList.remove("drop"),this.suppressClick=!0;const m=f&&this.find(f.dataset.obj);if(!m)return;const g=this.host.classes(),_=this.fields(s).filter(w=>w.target&&Dt(g,m.cls,w.target)),y=_.find(w=>!w.many&&Ce(this.valueOf(s,w)).length===0)??_.find(w=>w.many)??_[0];if(!y)return this.host.message(d("web_visuobject_obj_no_ref",s.cls,m.cls));this.host.message(""),this.change(s.id,{values:{...s.values,[y.name]:Mi(y,this.valueOf(s,y),m.name)}})};window.addEventListener("pointermove",l),window.addEventListener("pointerup",h)}}function Rt(a,e,t){const s=t.x-e.x,i=t.y-e.y,n=s===0?1/0:a.w/2/Math.abs(s),o=i===0?1/0:a.h/2/Math.abs(i),r=Math.min(n,o,1);return{x:Math.round(e.x+s*r),y:Math.round(e.y+i*r)}}const je=a=>a.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]),Ie=180,Ht=30,De=12,ae=30,Re=34,ee=10,st=a=>a.length*7+16;function qi(a,e){var w;const t=new Map(a.participants.map(($,p)=>[$.key,p])),s=$=>Ht+(t.get($)??0)*Ie+Ie/2,i=new Map,n=[],o=[],r=[],c=[],l=(w=a.participants[0])!=null&&w.isStatic?a.participants[0].key:"",h=$=>c.filter(p=>p.key===$).length+($===l?1:0),u=($,p)=>s($)-ee/2+Math.max(0,p-1)*6;let f=De+ae+24;a.events.forEach(($,p)=>{const v=p===e?" current":"";if($.k==="return"){const C=c.pop();if(!C)return;if($.hidden){o.push(``);return}const F=u(C.key,C.level),W=h(C.caller),G=C.caller===C.key?u(C.caller,W)+ee:W?u(C.caller,W)+(s(C.caller)`),C.caller!==C.key&&n.push(`${zt(G,f,G${je($.label)}`:""}`),f+=Re;return}const x=h($.from),L=$.from===$.to;$.k==="create"&&(f+=ae/2+8,i.set($.to,f));const S=h($.to)+1,k=x?u($.from,x)+(s($.to)>=s($.from)?ee:0):s($.from);if(L){const C=k,F=u($.to,S)+ee;n.push(`${Wt(F,f+4,-1)}${je($.label)}`),c.push({key:$.to,caller:$.from,y:f+4-6,level:S}),f+=Re;return}const j=s($.to)>s($.from),M=$.k==="create"?s($.to)+(j?-st(Bt(a,$.to))/2:st(Bt(a,$.to))/2):u($.to,S)+(j?0:ee),A=$.k==="create"?" create":"",R=$.k==="create"?`«create» ${$.label}`:$.label;n.push(`${$.k==="create"?zt(M,f,j?1:-1):Wt(M,f,j?1:-1)}${$.k==="create"?`${je(R)}`:`${je(R)}`}`),c.push({key:$.to,caller:$.from,y:$.k==="create"?f+ae/2:f,level:S}),f+=$.k==="create"?Re+ae/2:Re});for(const $ of c)o.push(``);const m=f+10,g=a.participants[0];g!=null&&g.isStatic&&o.unshift(``);for(const $ of a.participants){const p=s($.key),v=i.get($.key),x=v!==void 0?v-ae/2:De,L=Math.min(Ie-12,st($.label));r.push(`${je($.label)}`)}const _=Ht*2+a.participants.length*Ie,y=m+20;return{svg:`${r.join("")}${o.join("")}${n.join("")}`,width:_,height:y}}function Bt(a,e){var t;return((t=a.participants.find(s=>s.key===e))==null?void 0:t.label)??""}function Wt(a,e,t){return``}function zt(a,e,t){return``}class Ke extends Error{constructor(e,t){super(e),this.pos=t}}const Fi=[">>>=","<<=",">>=",">>>","...","->","::","++","--","&&","||","==","!=","<=",">=","+=","-=","*=","/=","%=","&=","|=","^=","<<"];function vt(a,e){const t=[];let s=0,i=0;for(;s=a.length)throw new Ke(n==='"'?"string":"char",{file:e,line:i});t.push({t:n==='"'?"str":"char",v:r,line:i}),s=o+1}else{const o=Fi.find(r=>a.startsWith(r,s))??n;t.push({t:"op",v:o,line:i}),s+=o.length}}return t.push({t:"eof",v:"",line:i}),t}const Ni=new Set(["public","private","protected","static","final","abstract","synchronized","transient","volatile","native","strictfp","default"]),Oi=new Set(["int","long","short","byte","double","float","boolean","char","void","var"]),Ae=class Ae{constructor(e,t){E(this,"i",0);this.toks=e,this.file=t}get tok(){return this.toks[this.i]}peek(e=1){return this.toks[Math.min(this.i+e,this.toks.length-1)]}pos(e=this.tok){return{file:this.file,line:e.line}}is(e,t=this.tok){return(t.t==="op"||t.t==="id")&&t.v===e}eat(e){return this.is(e)?(this.i++,!0):!1}expect(e){this.eat(e)||this.fail(`expected:${e}`)}fail(e){throw new Ke(e,this.pos())}ident(){return this.tok.t!=="id"&&this.fail("identifier"),this.toks[this.i++].v}unit(){const e=[];for(this.eat("package")&&this.skipTo(";");this.is("import");)this.skipTo(";");for(;this.tok.t!=="eof";)this.eat(";")||e.push(this.classDecl());return e}skipTo(e){for(;this.tok.t!=="eof"&&!this.is(e);)this.i++;this.expect(e)}mods(){const e=new Set;for(;;)if(this.tok.t==="op"&&this.tok.v==="@")this.i++,this.ident(),this.is("(")&&this.skipParens();else if(this.tok.t==="id"&&Ni.has(this.tok.v)&&!(this.tok.v==="default"&&this.is(":",this.peek())))e.add(this.toks[this.i++].v);else return e}skipParens(){let e=0;do this.is("(")?e++:this.is(")")&&e--,this.i++;while(e>0&&this.tok.t!=="eof")}classDecl(){const e=this.mods(),t=this.pos(),s=this.eat("class")?"class":this.eat("interface")?"interface":this.eat("enum")?"enum":this.fail("class"),i=this.ident();this.is("<")&&this.typeArgs();const n={...t,name:i,kind:s,interfaces:[],isAbstract:e.has("abstract")||s==="interface",fields:[],methods:[],enumConstants:[]};if(this.eat("extends")){const o=[this.type().name];for(;this.eat(",");)o.push(this.type().name);s==="interface"?n.interfaces=o:n.superclass=o[0]}if(this.eat("implements"))for(n.interfaces.push(this.type().name);this.eat(",");)n.interfaces.push(this.type().name);if(this.expect("{"),s==="enum"){for(;this.tok.t==="id"&&(n.enumConstants.push(this.ident()),this.is("(")&&this.skipParens(),!!this.eat(",")););this.eat(";")}for(;!this.eat("}");)this.tok.t==="eof"&&this.fail("expected:}"),!this.eat(";")&&this.member(n);return n}member(e){const t=this.tok,s=this.mods();(this.is("class")||this.is("interface")||this.is("enum"))&&this.fail("innerClass"),this.is("{")&&this.fail("initializer"),this.is("<")&&this.typeArgs();const i=this.pos(t);if(this.tok.t==="id"&&this.tok.v===e.name&&this.is("(",this.peek())){this.i++;const l=this.params();this.eat("throws")&&this.skipThrows();const h=this.block();e.methods.push({...i,name:e.name,params:l,isStatic:!1,isAbstract:!1,isConstructor:!0,body:h,endLine:this.toks[this.i-1].line});return}const n=this.type(),o=this.ident();if(this.is("(")){const l=this.params();for(;this.eat("[");)this.expect("]");this.eat("throws")&&this.skipThrows();const h=s.has("abstract")||e.kind==="interface"&&!this.is("{"),u=this.is("{")?this.block():(this.expect(";"),void 0);e.methods.push({...i,name:o,returns:n,params:l,isStatic:s.has("static"),isAbstract:h,isConstructor:!1,body:u,endLine:this.toks[this.i-1].line});return}let r=!0,c=o;for(;;){r||(c=this.ident()),r=!1;let l=0;for(;this.eat("[");)this.expect("]"),l++;const h=l?{...n,dims:n.dims+l}:n,u=this.eat("=")?this.is("{")?this.arrayInit():this.expr():void 0;if(e.fields.push({...i,name:c,type:h,isStatic:s.has("static")||e.kind==="interface",init:u}),!this.eat(","))break}this.expect(";")}skipThrows(){for(this.type();this.eat(",");)this.type()}params(){this.expect("(");const e=[];if(!this.eat(")")){do{this.mods();let t=this.type();this.eat("...")&&(t={...t,dims:t.dims+1});const s=this.ident();for(;this.eat("[");)this.expect("]"),t={...t,dims:t.dims+1};e.push({name:s,type:t})}while(this.eat(","));this.expect(")")}return e}typeArgs(){this.expect("<");const e=[];if(this.eat(">"))return e;do this.eat("?")?this.eat("extends")||this.eat("super")?e.push(this.type()):e.push({name:"Object",dims:0,args:[]}):e.push(this.type());while(this.eat(","));return this.is(">>")?this.toks[this.i]={...this.tok,v:">"}:this.is(">>>")?this.toks[this.i]={...this.tok,v:">>"}:this.expect(">"),e}type(){let e=this.ident(),t=[];for(this.is("<")&&(t=this.typeArgs());this.is(".")&&this.peek().t==="id";)this.i++,e=this.ident(),this.is("<")&&(t=this.typeArgs());let s=0;for(;this.is("[")&&this.is("]",this.peek());)this.i+=2,s++;return{name:e,dims:s,args:t}}looksLikeDecl(){const e=this.i;try{if(this.mods(),this.tok.t!=="id")return!1;const t=this.tok.v;return["return","new","throw","this","super","true","false","null"].includes(t)?!1:(this.type(),this.tok.t==="id"&&(this.is("=",this.peek())||this.is(";",this.peek())||this.is(",",this.peek())||this.is(":",this.peek())||this.is("[",this.peek())))}catch{return!1}finally{this.i=e}}block(){const e=this.pos();this.expect("{");const t=[];for(;!this.eat("}");)this.tok.t==="eof"&&this.fail("expected:}"),t.push(this.stmt());return{...e,k:"block",body:t}}localDecl(){const e=this.pos();this.mods();const t=this.type(),s=[];do{const i=this.ident();let n=0;for(;this.eat("[");)this.expect("]"),n++;const o=this.eat("=")?this.is("{")?this.arrayInit():this.expr():void 0;s.push({name:i,dims:n,init:o})}while(this.eat(","));return{...e,k:"local",type:t,vars:s}}stmt(){const e=this.pos(),t=this.tok.t==="id"||this.tok.t==="op"?this.tok.v:"";if(t==="{")return this.block();if(this.eat(";"))return{...e,k:"empty"};if(this.eat("if")){this.expect("(");const i=this.expr();this.expect(")");const n=this.stmt();return{...e,k:"if",test:i,then:n,else:this.eat("else")?this.stmt():void 0}}if(this.eat("while")){this.expect("(");const i=this.expr();return this.expect(")"),{...e,k:"while",test:i,body:this.stmt()}}if(this.eat("do")){const i=this.stmt();this.expect("while"),this.expect("(");const n=this.expr();return this.expect(")"),this.expect(";"),{...e,k:"do",body:i,test:n}}if(this.eat("for")){this.expect("(");const i=this.i;try{this.mods();const c=this.type(),l=this.ident();if(this.eat(":")){const h=this.expr();return this.expect(")"),{...e,k:"foreach",type:c,name:l,iter:h,body:this.stmt()}}}catch{}this.i=i;const n=[];if(!this.is(";"))if(this.looksLikeDecl())n.push(this.localDecl());else do{const c=this.pos();n.push({...c,k:"expr",expr:this.expr()})}while(this.eat(","));this.expect(";");const o=this.is(";")?void 0:this.expr();this.expect(";");const r=[];if(!this.is(")"))do r.push(this.expr());while(this.eat(","));return this.expect(")"),{...e,k:"for",init:n,test:o,update:r,body:this.stmt()}}if(this.eat("switch")){this.expect("(");const i=this.expr();this.expect(")"),this.expect("{");const n=[];for(;!this.eat("}");){let o;if(this.eat("default"))o="default";else for(this.expect("case"),o=[this.expr()];this.eat(",");)o.push(this.expr());if(this.eat("->")){const c=this.is("{")?[this.block()]:[this.stmt()];n.push({labels:o,body:c,arrow:!0});continue}this.expect(":");const r=[];for(;!this.is("case")&&!this.is("default")&&!this.is("}");)r.push(this.stmt());n.push({labels:o,body:r,arrow:!1})}return{...e,k:"switch",expr:i,cases:n}}if(this.eat("break"))return this.tok.t==="id"&&this.i++,this.expect(";"),{...e,k:"break"};if(this.eat("continue"))return this.tok.t==="id"&&this.i++,this.expect(";"),{...e,k:"continue"};if(this.eat("return")){const i=this.is(";")?void 0:this.expr();return this.expect(";"),{...e,k:"return",value:i}}if(this.eat("throw")){const i=this.expr();return this.expect(";"),{...e,k:"throw",value:i}}if(this.eat("try")){this.is("(")&&this.fail("tryResources");const i=this.block(),n=[];for(;this.eat("catch");){this.expect("("),this.mods();const r=[this.type()];for(;this.eat("|");)r.push(this.type());const c=this.ident();this.expect(")"),n.push({types:r,name:c,body:this.block()})}const o=this.eat("finally")?this.block():void 0;return{...e,k:"try",body:i,catches:n,finally:o}}if((t==="this"||t==="super")&&this.is("(",this.peek())){this.i++;const i=this.args();return this.expect(";"),{...e,k:"ctorCall",isSuper:t==="super",args:i}}if((t==="class"||t==="interface")&&this.fail("innerClass"),this.looksLikeDecl()){const i=this.localDecl();return this.expect(";"),i}const s=this.expr();return this.expect(";"),{...e,k:"expr",expr:s}}args(){this.expect("(");const e=[];if(!this.eat(")")){do e.push(this.expr());while(this.eat(","));this.expect(")")}return e}arrayInit(){const e=this.pos();this.expect("{");const t=[];for(;!this.eat("}");)if(t.push(this.is("{")?this.arrayInit():this.expr()),!this.eat(",")){this.expect("}");break}return{...e,k:"arrayInit",items:t}}expr(){return this.assign()}assign(){const e=this.cond(),t=this.tok.v;if(this.tok.t==="op"&&["=","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=",">>>="].includes(t)){const s=this.pos();return this.i++,e.k!=="name"&&e.k!=="field"&&e.k!=="index"&&this.fail("assignTarget"),{...s,k:"assign",op:t,target:e,value:this.assign()}}return e}cond(){const e=this.binary(0);if(this.is("?")){const t=this.pos();this.i++;const s=this.assign();return this.expect(":"),{...t,k:"cond",test:e,then:s,else:this.assign()}}return e}binary(e){if(e>=Ae.levels.length)return this.unary();let t=this.binary(e+1);for(;;){const s=this.tok.v;if(!(this.tok.t==="op"||s==="instanceof")||!Ae.levels[e].includes(s))return t;const i=this.pos();if(this.i++,s==="instanceof"){t={...i,k:"instanceof",expr:t,type:this.type()},this.tok.t==="id"&&this.i++;continue}t={...i,k:"binary",op:s,left:t,right:this.binary(e+1)}}}unary(){const e=this.pos();if(this.is("++")||this.is("--")){const t=this.toks[this.i++].v;return{...e,k:"incdec",op:t,prefix:!0,target:this.unary()}}if(this.is("!")||this.is("-")||this.is("+")||this.is("~")){const t=this.toks[this.i++].v;return{...e,k:"unary",op:t,expr:this.unary()}}if(this.is("(")&&this.peek().t==="id"){const t=this.i;this.i++;try{const s=this.type();if(this.eat(")")){const i=Oi.has(s.name),n=this.tok;if((i?!(n.t==="op"&&["+","-","*","/",")",";",",",".","==","<",">"].includes(n.v)&&!["(","-","+","!","~"].includes(n.v))||n.t!=="op"||["(","-","!","~"].includes(n.v):n.t!=="op"||n.v==="(")&&(i||/^[A-Z]/.test(s.name)))return{...e,k:"cast",type:s,expr:this.unary()}}}catch{}this.i=t}return this.postfix(this.primary())}postfix(e){for(;;){const t=this.pos();if(this.eat(".")){this.eat("new")&&this.fail("innerClass");const s=this.ident();this.is("(")?e={...t,k:"call",target:e,name:s,args:this.args(),isSuper:e.k==="super"}:e={...t,k:"field",target:e,name:s}}else if(this.is("[")){this.i++;const s=this.expr();this.expect("]"),e={...t,k:"index",target:e,index:s}}else if(this.is("++")||this.is("--")){const s=this.toks[this.i++].v;e={...t,k:"incdec",op:s,prefix:!1,target:e}}else if(this.is("::")||this.is("->"))this.fail("lambda");else return e}}primary(){const e=this.tok,t=this.pos();if(e.t==="num"){this.i++;const i=e.v.replace(/_/g,"");return/^0[xX]/.test(i)?{...t,k:"lit",value:{t:"int",v:parseInt(i.replace(/[lL]$/,""),16)}}:/[.eEfFdD]/.test(i)&&!/^0[xX]/.test(i)?{...t,k:"lit",value:{t:"double",v:parseFloat(i.replace(/[fFdD]$/,""))}}:{...t,k:"lit",value:{t:"int",v:parseInt(i.replace(/[lL]$/,""),10)}}}if(e.t==="str")return this.i++,{...t,k:"lit",value:{t:"string",v:e.v}};if(e.t==="char")return this.i++,{...t,k:"lit",value:{t:"char",v:e.v.charCodeAt(0)||0}};if(this.eat("(")){const i=this.expr();return this.expect(")"),i}if(e.t!=="id"&&this.fail("expression"),this.eat("true"))return{...t,k:"lit",value:{t:"bool",v:!0}};if(this.eat("false"))return{...t,k:"lit",value:{t:"bool",v:!1}};if(this.eat("null"))return{...t,k:"lit",value:{t:"null",v:null}};if(this.eat("this"))return{...t,k:"this"};if(this.eat("super"))return{...t,k:"super"};if(this.eat("new")){const i=this.ident();let n=[];this.is("<")&&(n=this.typeArgs());const o={name:i,dims:0,args:n};if(this.is("[")){const c=[];let l=0;for(;this.eat("[");){if(this.eat("]")){l++;continue}c.push(this.expr()),this.expect("]"),l++}const h=this.is("{")?this.arrayInit():void 0;return{...t,k:"newArray",type:{...o,dims:l},sizes:c,init:h}}const r=this.args();return this.is("{")&&this.fail("anonymousClass"),{...t,k:"new",type:o,args:r}}const s=this.ident();return this.is("(")?{...t,k:"call",name:s,args:this.args()}:{...t,k:"name",name:s}}};E(Ae,"levels",[["||"],["&&"],["|"],["^"],["&"],["==","!="],["<",">","<=",">=","instanceof"],["<<",">>",">>>"],["+","-"],["*","/","%"]]);let ht=Ae;const Pi=["Scanner","Random","ArrayList","List","LinkedList","HashMap","Map","TreeMap","LinkedHashMap","Collection","Collections","Arrays","Iterator","Objects"];function Ii(a,e,t){var n,o;const s=vt(a,e),i=[];for(let r=0;rl.v).join("")),r=c}for(let r=0;r0&&!i.has(s[n-1].v))continue;const l=Ps(o.v);if(l)return{type:l,pos:{file:e,line:o.line}}}}function Ri(a,e){return new ht(vt(a,e),e).unit()}class O{constructor(e){this.v=e}}class H{constructor(e){this.code=e}}class I{constructor(e,t){E(this,"fields",new Map);E(this,"native",{});this.cls=e,this.id=t}}class B{constructor(e,t,s){this.elem=e,this.items=t,this.id=s}}class K{constructor(e){E(this,"items",[]);this.id=e}}class be{constructor(e){E(this,"entries",new Map);this.id=e}}class de{constructor(e,t){E(this,"buf","");this.kind=e,this.id=t}}class Se{constructor(e){this.name=e}}const Hi={k:"break"},Bi={k:"continue"};class q extends Error{constructor(e,t,s){super(`${e}: ${t}`),this.code=e,this.detail=t,this.pos=s}}class Le extends Error{constructor(e){super(e.cls.name),this.obj=e}}const ve=(a,e=0)=>({name:a,dims:e,args:[]}),ws=new Set(["int","long","short","byte","Integer","Long","Short","Byte"]),$s=new Set(["double","float","Double","Float"]),Vt=new Set(["ArrayList","List","LinkedList","Collection","Iterable"]),Wi=new Set(["HashMap","Map","TreeMap","LinkedHashMap"]),zi=[["Throwable","Object","java.lang"],["Exception","Throwable","java.lang"],["Error","Throwable","java.lang"],["StackOverflowError","Error","java.lang"],["RuntimeException","Exception","java.lang"],["IOException","Exception","java.io"],["InterruptedException","Exception","java.lang"],["ArithmeticException","RuntimeException","java.lang"],["NullPointerException","RuntimeException","java.lang"],["ClassCastException","RuntimeException","java.lang"],["IllegalArgumentException","RuntimeException","java.lang"],["NumberFormatException","IllegalArgumentException","java.lang"],["IllegalStateException","RuntimeException","java.lang"],["UnsupportedOperationException","RuntimeException","java.lang"],["IndexOutOfBoundsException","RuntimeException","java.lang"],["ArrayIndexOutOfBoundsException","IndexOutOfBoundsException","java.lang"],["StringIndexOutOfBoundsException","IndexOutOfBoundsException","java.lang"],["NegativeArraySizeException","RuntimeException","java.lang"],["NoSuchElementException","RuntimeException","java.util"],["InputMismatchException","NoSuchElementException","java.util"],["ConcurrentModificationException","RuntimeException","java.util"]],Vi=new Set(["Math","System","Integer","Double","String","Character","Boolean","Long","Float","Arrays","Collections","Thread","Objects","List"]);function Ut(a){if(Number.isNaN(a))return"NaN";if(!Number.isFinite(a))return a>0?"Infinity":"-Infinity";if(a===0)return Object.is(a,-0)?"-0.0":"0.0";const e=Math.abs(a);if(e>=.001&&e<1e7){const i=String(a);return i.includes(".")||i.includes("e")?i:`${i}.0`}const[t,s]=a.toExponential().split("e");return`${t.includes(".")?t:`${t}.0`}E${Number(s)}`}const _e=a=>(460141958+a*165902235>>>0).toString(16);function Ui(a){let e=0;for(let t=0;ttypeof a=="number"?a:a instanceof O?a.v:a instanceof H?a.code:NaN,ne=a=>typeof a=="number"||a instanceof O||a instanceof H,He=a=>a>=-2147483648&&a<=2147483647;function te(a,e){if(!e||e.dims||a===null||a===void 0)return a;const t=e.name;if($s.has(t))return ne(a)?new O(T(a)):a;if(ws.has(t)){if(a instanceof H)return a.code;if(a instanceof O){if(Number.isNaN(a.v))return 0;const s=Math.trunc(a.v);return t==="int"||t==="Integer"?Math.max(-2147483648,Math.min(2147483647,s)):s}return typeof a=="number"&&(t==="byte"||t==="Byte")?a<<24>>24:typeof a=="number"&&(t==="short"||t==="Short")?a<<16>>16:a}return(t==="char"||t==="Character")&&(typeof a=="number"||a instanceof O)?new H(Math.trunc(T(a))&65535):a}function it(a){return a.dims?null:a.name==="int"||a.name==="long"||a.name==="short"||a.name==="byte"?0:a.name==="double"||a.name==="float"?new O(0):a.name==="boolean"?!1:a.name==="char"?new H(0):null}const dt=a=>`${a.name}${a.args.length?`<${a.args.map(dt).join(", ")}>`:""}${"[]".repeat(a.dims)}`;class Ji{constructor(e,t){E(this,"classes",new Map);E(this,"frames",[]);E(this,"nextId",1);E(this,"quiet",0);E(this,"gen");E(this,"send");E(this,"mode","run");E(this,"startDepth",0);E(this,"prevKey","");E(this,"pauseRequested",!1);E(this,"names",new Map);E(this,"tempNames",new Map);E(this,"seq",[]);E(this,"seqParticipants",new Map);E(this,"seqLimit",3e3);E(this,"stdin",new de("in",0));E(this,"finished",!1);this.io=t;const s={name:"Object",interfaces:[],statics:new Map,staticTypes:new Map,builtin:!0,javaName:"java.lang.Object"};this.classes.set("Object",s);for(const[o,r,c]of zi)this.classes.set(o,{name:o,sup:this.classes.get(r),interfaces:[],statics:new Map,staticTypes:new Map,builtin:!0,javaName:`${c}.${o}`});const i=[];for(const o of e)try{i.push(...Ri(o.source,o.path))}catch(r){throw r instanceof Ke?new q("syntax",r.message,r.pos):r}for(const o of i)this.classes.set(o.name,{name:o.name,decl:o,interfaces:o.interfaces,statics:new Map,staticTypes:new Map});const n=new Set(i.map(o=>o.name));for(const o of e){const r=Ii(o.source,o.path,n);if(r)throw new q("import",r.line,r.pos);const c=Di(o.source,o.path,n);if(c)throw new q("typename",c.type,c.pos)}for(const o of i){const r=this.classes.get(o.name),c=o.superclass??(o.kind==="class"?"Object":void 0);if(c&&(r.sup=this.classes.get(c),!r.sup))throw new q("symbol",c,o)}}start(e){const t=[...this.classes.values()].filter(i=>{var n;return(n=i.decl)==null?void 0:n.methods.some(Kt)}),s=t.find(i=>i.name===e)??t[0];if(!s)throw new q("nomain","");this.gen=this.program(s),this.finished=!1}go(e){this.mode=e,this.startDepth=this.frames.length,this.pauseRequested=!1}pause(){this.pauseRequested=!0}input(e){this.send=e}run(e,t=5e3){var s;if(!this.gen||this.finished)return{k:"done"};for(let i=0;i` at ${s}`)].join(` -`)}stack(){return this.nameObjects(),[...this.frames].reverse().map(e=>{const t=[];e.self&&t.push({name:"this",type:e.self.cls.name,value:this.show(e.self),ref:e.self.id});for(const s of e.scopes)for(const[i,n]of s)t.push({name:i,type:dt(n.type),value:this.show(n.v),...n.v instanceof I?{ref:n.v.id}:{}});return{title:e.title,pos:e.pos,vars:t}})}heap(){return this.nameObjects().map(e=>({id:e.id,name:this.names.get(e.id)??this.tempNames.get(e.id),cls:e.cls.name,fields:[...e.fields].map(([t,s])=>({name:t,type:dt(this.fieldType(e.cls,t)??ve("Object")),value:this.show(s)}))}))}fieldType(e,t){var s;for(let i=e;i;i=i.sup){const n=(s=i.decl)==null?void 0:s.fields.find(o=>o.name===t&&!o.isStatic);if(n)return n.type}}nameObjects(){const e=[],t=new Set,s=new Set(this.names.values());this.tempNames.clear();const i=(r,c,l)=>{if(this.names.has(r.id))return;const h=/^[\p{L}_$][\p{L}\p{N}_$]*$/u.test(c);let u=h?c:r.cls.name.charAt(0).toLowerCase()+r.cls.name.slice(1);if(s.has(u)){let f=2;for(;s.has(`${u}${f}`);)f++;u=`${u}${f}`}s.add(u),(h&&l?this.names:this.tempNames).set(r.id,u)},n=[],o=(r,c,l)=>{if(r instanceof I){if(r.cls.builtin||r.native.enumName!==void 0||t.has(r.id))return;t.add(r.id),i(r,c,l),e.push(r);for(const[h,u]of r.fields)n.push([u,h])}else if(r instanceof B||r instanceof K)for(const h of r.items)h instanceof I&&n.push([h,""]);else if(r instanceof be)for(const[h,u]of r.entries.values())n.push([h,""],[u,""])};for(const r of this.frames){r.self&&n.push([r.self,""]);for(const c of r.scopes)for(const[l,h]of c)n.push([h.v,l,!0])}for(const r of this.classes.values())for(const[c,l]of r.statics)n.push([l,c,!0]);for(let r=0;ru===c&&f!=="")||o(c,l,!!h)}return e.sort((r,c)=>r.id-c.id)}show(e){return e===void 0?"?":e===null?"null":typeof e=="string"?JSON.stringify(e):typeof e=="number"||typeof e=="boolean"?String(e):e instanceof O?Ut(e.v):e instanceof H?e.code?`'${String.fromCharCode(e.code)}'`:"''":e instanceof I?e.native.enumName!==void 0?e.native.enumName:e.cls.builtin?e.cls.name:this.names.get(e.id)??this.tempNames.get(e.id)??`${e.cls.name}@${_e(e.id)}`:e instanceof B||e instanceof K?`[${e.items.slice(0,30).map(s=>this.show(s)).join(", ")}${e.items.length>30?", …":""}]`:e instanceof be?`{${[...e.entries.values()].map(([t,s])=>`${this.show(t)}=${this.show(s)}`).join(", ")}}`:e instanceof de?e.kind==="StringBuilder"?JSON.stringify(e.buf):e.kind:e.name}seqKey(e){return e instanceof I?`o${e.id}`:`c${e.name}`}record(e,t,s){if(this.quiet||this.seq.length>=this.seqLimit)return!1;const i=this.frames.at(-1),n=i?this.seqKey(i.self??i.cls):"",o=this.seqKey(t);return this.seqParticipants.has(o)||this.seqParticipants.set(o,t instanceof I?{key:o,cls:t.cls.name,id:t.id}:{key:o,cls:t.name}),this.nameObjects(),this.seq.push({k:e,from:n,to:o,label:s(),pos:(i==null?void 0:i.pos)??{file:"",line:0}}),!0}recordReturn(e,t,s=!1){var i;this.seq.length>=this.seqLimit+1e3||this.seq.push({k:"return",from:"",to:"",label:t?"⚡":e,pos:((i=this.frames.at(-1))==null?void 0:i.pos)??{file:"",line:0},...s&&!t?{hidden:s}:{}})}sequence(){return this.nameObjects(),{participants:[...this.seqParticipants.values()].map(t=>({key:t.key,cls:t.cls,label:t.id===void 0?t.cls:`${this.names.get(t.id)??this.tempNames.get(t.id)??""} : ${t.cls}`,isStatic:t.id===void 0})),events:[...this.seq],truncated:this.seq.length>=this.seqLimit}}get frame(){return this.frames[this.frames.length-1]}*step(e){const t={file:e.file,line:e.line};this.frames.length&&(this.frame.pos=t),this.quiet||(yield{k:"step",pos:t,depth:this.frames.length})}*program(e){this.quiet++;try{for(const s of this.classes.values())yield*this.initStatics(s)}finally{this.quiet--}const t=e.decl.methods.find(Kt);this.seqParticipants.set(`c${e.name}`,{key:`c${e.name}`,cls:e.name}),yield*this.invoke(t,e,void 0,[new B(ve("String"),[],this.id())])}id(){return this.nextId++}*initStatics(e){const t=e.decl;if(!t)return;t.kind==="enum"&&t.enumConstants.forEach((i,n)=>{const o=new I(e,this.id());o.native={enumName:i,ordinal:n},e.statics.set(i,o),e.staticTypes.set(i,ve(e.name))});for(const i of t.fields)i.isStatic&&(e.statics.set(i.name,it(i.type)),e.staticTypes.set(i.name,i.type));const s={cls:e,scopes:[new Map],pos:t,title:`${e.name} (static)`};this.frames.push(s);try{for(const i of t.fields)i.isStatic&&i.init&&e.statics.set(i.name,te(yield*this.initValue(i.init,i.type),i.type))}finally{this.frames.pop()}}*initValue(e,t){return e.k==="arrayInit"?yield*this.arrayFrom(e,t):yield*this.eval(e)}*arrayFrom(e,t){const s={...t,dims:t.dims-1},i=[];for(const n of e.items)i.push(te(n.k==="arrayInit"?yield*this.arrayFrom(n,s):yield*this.eval(n),s));return new B(s,i,this.id())}*invoke(e,t,s,i){if(!e.body)throw new q("unsupported",`abstract ${e.name}`,e);if(this.frames.length>900)throw new Le(this.exception("StackOverflowError"));const n=new Map;e.params.forEach((l,h)=>n.set(l.name,{v:te(i[h],l.type),type:l.type}));const o=e.isConstructor?`new ${t.name}()`:`${t.name}.${e.name}()`,r=this.frames.length>0&&this.record("call",s||t,()=>`${e.name}(${i.map(l=>this.show(l)).join(", ")})`);this.frames.push({cls:t,method:e,self:s,scopes:[n],pos:e,title:o});let c=!1;try{const l=yield*this.execBody(e.body.body);let h;return(l==null?void 0:l.k)==="return"?h=te(l.v,e.returns):yield*this.step({file:e.file,line:e.endLine}),c=!0,this.frames.pop(),r&&this.recordReturn(e.returns&&e.returns.name!=="void"?this.show(h):"",!1),h}finally{c||(this.frames.pop(),r&&this.recordReturn("",!0))}}*execBody(e){for(const t of e){const s=yield*this.exec(t);if(s)return s}}*construct(e,t,s){var c,l;if((c=e.decl)!=null&&c.isAbstract)throw new q("type",`abstract ${e.name}`,s);const i=new I(e,this.id()),n=[];for(let h=e;h;h=h.sup)n.unshift(h);for(const h of n)for(const u of((l=h.decl)==null?void 0:l.fields)??[])u.isStatic||i.fields.set(u.name,it(u.type));const o=this.record("create",i,()=>`new ${e.name}(${t.map(h=>this.show(h)).join(", ")})`);let r=!1;try{yield*this.runCtor(e,i,t,s),r=!0}finally{o&&this.recordReturn("",!r,!0)}return i}*runCtor(e,t,s,i){if(e.builtin){s.length&&(t.native.message=typeof s[0]=="string"||s[0]===null?s[0]:yield*this.str(s[0])),t.native.trace=this.trace();return}const n=e.decl,o=n.methods.filter(l=>l.isConstructor);if(!o.length){if(s.length)throw new q("symbol",`${e.name}(${s.length})`,i);e.sup&&(yield*this.runCtor(e.sup,t,[],i)),this.frames.push({cls:e,self:t,scopes:[new Map],pos:n,title:`new ${e.name}()`});try{yield*this.fieldInits(e,t)}finally{this.frames.pop()}return}const r=this.pick(o,s);if(!r)throw new q("symbol",`${e.name}(${s.length})`,i);const c=new Map;r.params.forEach((l,h)=>c.set(l.name,{v:te(s[h],l.type),type:l.type})),this.frames.push({cls:e,method:r,self:t,scopes:[c],pos:r,title:`new ${e.name}()`});try{let l=r.body.body;const h=l[0];if((h==null?void 0:h.k)==="ctorCall"){yield*this.step(h);const f=[];for(const m of h.args)f.push(yield*this.eval(m));l=l.slice(1),h.isSuper?(e.sup&&(yield*this.runCtor(e.sup,t,f,h)),yield*this.fieldInits(e,t)):yield*this.runCtor(e,t,f,h)}else e.sup&&(yield*this.runCtor(e.sup,t,[],r)),yield*this.fieldInits(e,t);const u=yield*this.execBody(l);(u==null?void 0:u.k)!=="return"&&(yield*this.step({file:r.file,line:r.endLine}))}finally{this.frames.pop()}}*fieldInits(e,t){var s;for(const i of((s=e.decl)==null?void 0:s.fields)??[])!i.isStatic&&i.init&&(yield*this.step(i),t.fields.set(i.name,te(yield*this.initValue(i.init,i.type),i.type)))}pick(e,t){let s,i=-1;for(const n of e){if(n.params.length!==t.length)continue;let o=0;for(let r=0;r=0;r++){const c=this.fits(t[r],n.params[r].type);o=c<0?-1:o+c}o>i&&(s=n,i=o)}return s}fits(e,t){const s=t.name;return t.dims?e===null?1:e instanceof B?2:-1:ws.has(s)?typeof e=="number"?2:e instanceof H?1:-1:$s.has(s)?e instanceof O?2:ne(e)?1:-1:s==="char"||s==="Character"?e instanceof H?2:-1:s==="boolean"||s==="Boolean"?typeof e=="boolean"?2:-1:s==="String"?typeof e=="string"?2:e===null?1:-1:s==="Object"?0:Vt.has(s)?e instanceof K?2:e===null?1:-1:Wi.has(s)?e instanceof be?2:e===null?1:-1:this.classes.get(s)?e===null?1:e instanceof I&&this.isA(e.cls,s)?e.cls.name===s?2:1:-1:0}isA(e,t){const s=new Set,i=n=>!n||s.has(n.name)?!1:n.name===t?!0:(s.add(n.name),i(n.sup)||n.interfaces.some(o=>i(this.classes.get(o))));return t==="Object"||i(e)}findMethod(e,t,s,i=!0){var r,c;const n=new Set,o=[];for(let l=e;l;l=l.sup){const h=this.pick(((r=l.decl)==null?void 0:r.methods.filter(u=>u.name===t&&!u.isConstructor&&(!i||u.body)))??[],s);if(h)return{m:h,owner:l};for(const u of l.interfaces)o.push(this.classes.get(u))}for(;o.length;){const l=o.shift();if(!l||n.has(l.name))continue;n.add(l.name);const h=this.pick(((c=l.decl)==null?void 0:c.methods.filter(u=>u.name===t&&(!i||u.body)))??[],s);if(h)return{m:h,owner:l};for(const u of l.interfaces)o.push(this.classes.get(u))}}trace(){return[...this.frames].reverse().map(e=>{const t=e.method?e.method.isConstructor?"":e.method.name:"";return`${e.cls.name}.${t}(${e.pos.file.split("/").pop()}:${e.pos.line+1})`})}exception(e,t){const s=new I(this.classes.get(e),this.id());return s.native={message:t??null,trace:this.trace()},s}fail(e,t){throw new Le(this.exception(e,t))}throwableText(e){const t=e.native.message,s=e.cls.javaName??e.cls.name;return t==null?s:`${s}: ${t}`}*exec(e){switch(e.k){case"block":{this.frame.scopes.push(new Map);try{return yield*this.execBody(e.body)}finally{this.frame.scopes.pop()}}case"empty":return;case"local":yield*this.step(e),yield*this.declare(e);return;case"expr":yield*this.step(e),yield*this.eval(e.expr);return;case"if":return yield*this.step(e),(yield*this.test(e.test))?yield*this.exec(e.then):e.else?yield*this.exec(e.else):void 0;case"while":for(;;){if(yield*this.step(e),!(yield*this.test(e.test)))return;const t=yield*this.exec(e.body);if((t==null?void 0:t.k)==="break")return;if((t==null?void 0:t.k)==="return")return t}case"do":for(;;){const t=yield*this.exec(e.body);if((t==null?void 0:t.k)==="break")return;if((t==null?void 0:t.k)==="return")return t;if(yield*this.step(e.test),!(yield*this.test(e.test)))return}case"for":{this.frame.scopes.push(new Map);try{yield*this.step(e);for(const t of e.init)t.k==="local"?yield*this.declare(t):t.k==="expr"&&(yield*this.eval(t.expr));for(;;){if(e.test&&!(yield*this.test(e.test)))return;const t=yield*this.exec(e.body);if((t==null?void 0:t.k)==="break")return;if((t==null?void 0:t.k)==="return")return t;yield*this.step(e);for(const s of e.update)yield*this.eval(s)}}finally{this.frame.scopes.pop()}}case"foreach":{yield*this.step(e);const t=yield*this.eval(e.iter);t===null&&this.fail("NullPointerException");const s=t instanceof B||t instanceof K?[...t.items]:t instanceof be?[...t.entries.values()].map(i=>i[0]):void 0;if(!s)throw new q("type","for-each",e);for(let i=0;i0&&(yield*this.step(e)),t instanceof K&&t.items.length!==s.length&&this.fail("ConcurrentModificationException"),this.frame.scopes.push(new Map([[e.name,{v:te(s[i],e.type),type:e.type}]]));try{const n=yield*this.exec(e.body);if((n==null?void 0:n.k)==="break")return;if((n==null?void 0:n.k)==="return")return n}finally{this.frame.scopes.pop()}}return}case"switch":{yield*this.step(e);const t=yield*this.eval(e.expr);let s=-1;for(let i=0;ii.labels==="default")),s<0)return;this.frame.scopes.push(new Map);try{for(let i=s;in.types.some(o=>this.isA(s.obj.cls,o.name)));if(!i)throw s;this.frame.scopes.push(new Map([[i.name,{v:s.obj,type:i.types[0]}]]));try{t=yield*this.exec(i.body)}finally{this.frame.scopes.pop()}}}finally{if(e.finally){const s=yield*this.exec(e.finally);if(s)return s}}return t}case"ctorCall":throw new q("syntax","ctorCall",e)}}*declare(e){for(const t of e.vars){const s=t.dims?{...e.type,dims:e.type.dims+t.dims}:e.type,i=t.init?te(yield*this.initValue(t.init,s),s):void 0;this.frame.scopes[this.frame.scopes.length-1].set(t.name,{v:i,type:s})}}*test(e){const t=yield*this.eval(e);if(typeof t!="boolean")throw new q("type","boolean",e);return t}lookupVar(e){const t=this.frame.scopes;for(let s=t.length-1;s>=0;s--){const i=t[s].get(e);if(i)return i}}staticOwner(e,t){const s=new Set,i=n=>{if(!(!n||s.has(n.name)))return s.add(n.name),n.statics.has(t)?n:i(n.sup)??n.interfaces.map(o=>i(this.classes.get(o))).find(Boolean)};return i(e)}*place(e){if(e.k==="name"){const s=this.lookupVar(e.name);if(s)return{get:()=>s.v,set:o=>s.v=o,type:s.type};const i=this.frame.self;if(i!=null&&i.fields.has(e.name))return{get:()=>i.fields.get(e.name),set:o=>i.fields.set(e.name,o),type:this.fieldType(i.cls,e.name)};const n=this.staticOwner(this.frame.cls,e.name);if(n)return{get:()=>n.statics.get(e.name),set:o=>n.statics.set(e.name,o),type:n.staticTypes.get(e.name)};if(this.classes.has(e.name)||Vi.has(e.name))return{get:()=>new Se(e.name),set:()=>{}};throw new q("symbol",e.name,e)}if(e.k==="field"){const s=yield*this.eval(e.target);if(s instanceof I&&s.fields.has(e.name))return{get:()=>s.fields.get(e.name),set:o=>s.fields.set(e.name,o),type:this.fieldType(s.cls,e.name)};const i=s instanceof Se?this.staticOwner(this.classes.get(s.name),e.name):s instanceof I?this.staticOwner(s.cls,e.name):void 0;if(i)return{get:()=>i.statics.get(e.name),set:o=>i.statics.set(e.name,o),type:i.staticTypes.get(e.name)};const n=this.builtinField(s,e.name,e);return{get:()=>n,set:()=>{throw new q("type",e.name,e)}}}if(e.k==="index"){const s=yield*this.eval(e.target),i=T(yield*this.eval(e.index));if(s===null&&this.fail("NullPointerException"),!(s instanceof B))throw new q("type","[]",e);return(i<0||i>=s.items.length)&&this.fail("ArrayIndexOutOfBoundsException",`Index ${i} out of bounds for length ${s.items.length}`),{get:()=>s.items[i],set:n=>s.items[i]=n,type:s.elem}}const t=yield*this.eval(e);return{get:()=>t,set:()=>{throw new q("syntax","assignTarget",e)}}}builtinField(e,t,s){if(e===null&&this.fail("NullPointerException",`Cannot read field "${t}" because value is null`),e instanceof B&&t==="length")return e.items.length;if(e instanceof Se)switch(`${e.name}.${t}`){case"System.out":return new de("out",0);case"System.err":return new de("err",0);case"System.in":return this.stdin;case"Math.PI":return new O(Math.PI);case"Math.E":return new O(Math.E);case"Integer.MAX_VALUE":return 2147483647;case"Integer.MIN_VALUE":return-2147483648;case"Long.MAX_VALUE":return Number.MAX_SAFE_INTEGER;case"Double.MAX_VALUE":return new O(Number.MAX_VALUE);case"Double.MIN_VALUE":return new O(Number.MIN_VALUE)}throw new q("symbol",t,s)}*eval(e){switch(e.k){case"lit":{const{t,v:s}=e.value;return t==="double"?new O(s):t==="char"?new H(s):s}case"name":{const t=this.lookupVar(e.name);if(t){if(t.v===void 0)throw new q("type",`uninit:${e.name}`,e);return t.v}return(yield*this.place(e)).get()}case"this":if(!this.frame.self)throw new q("type","static:this",e);return this.frame.self;case"super":if(!this.frame.self)throw new q("type","static:super",e);return this.frame.self;case"field":case"index":return(yield*this.place(e)).get();case"assign":{const t=yield*this.place(e.target);let s;if(e.op==="=")s=e.value.k==="arrayInit"&&t.type?yield*this.arrayFrom(e.value,t.type):yield*this.eval(e.value);else{const i=t.get();s=yield*this.binary(e.op.slice(0,-1),i,yield*this.eval(e.value),e)}return s=te(s,t.type),t.set(s),s}case"incdec":{const t=yield*this.place(e.target),s=t.get();ne(s)||this.fail("NullPointerException");const i=e.op==="++"?1:-1,n=s instanceof O?new O(s.v+i):s instanceof H?new H(s.code+i&65535):He(T(s))?T(s)+i|0:T(s)+i;return t.set(te(n,t.type)),e.prefix?n:s}case"unary":{const t=yield*this.eval(e.expr);return e.op==="!"?!t:e.op==="-"?t instanceof O?new O(-t.v):He(T(t))?-T(t)|0:-T(t):e.op==="~"?~T(t):t instanceof H?t.code:t}case"binary":{if(e.op==="&&")return(yield*this.test(e.left))&&(yield*this.test(e.right));if(e.op==="||")return(yield*this.test(e.left))||(yield*this.test(e.right));const t=yield*this.eval(e.left),s=yield*this.eval(e.right);return yield*this.binary(e.op,t,s,e)}case"cond":return(yield*this.test(e.test))?yield*this.eval(e.then):yield*this.eval(e.else);case"cast":{const t=yield*this.eval(e.expr),s=e.type.name;return t instanceof I&&this.classes.has(s)&&!this.isA(t.cls,s)&&this.fail("ClassCastException",`class ${t.cls.name} cannot be cast to class ${s}`),te(t,e.type)}case"instanceof":{const t=yield*this.eval(e.expr);if(t===null)return!1;const s=e.type.name;return t instanceof I?this.isA(t.cls,s):typeof t=="string"?s==="String"||s==="Object"||s==="CharSequence":t instanceof K&&Vt.has(s)||s==="Object"}case"new":return yield*this.create(e);case"newArray":{if(e.init&&e.init.k==="arrayInit")return yield*this.arrayFrom(e.init,e.type);const t=[];for(const i of e.sizes)t.push(T(yield*this.eval(i)));const s=(i,n)=>{const o=t[i];o<0&&this.fail("NegativeArraySizeException",String(o));const r={...n,dims:n.dims-1},c=Array.from({length:o},()=>i+1":return n>o;case"<=":return n<=o;case">=":return n>=o;case"&":return n&o;case"|":return n|o;case"^":return n^o;case"<<":return n<>":return n>>o;case">>>":return n>>>o}throw new q("unsupported",e,i)}same(e,t){return ne(e)&&ne(t)?T(e)===T(t):e===t}*str(e){if(e==null)return"null";if(typeof e=="string")return e;if(typeof e=="number"||typeof e=="boolean")return String(e);if(e instanceof O)return Ut(e.v);if(e instanceof H)return String.fromCharCode(e.code);if(e instanceof I){const t=this.findMethod(e.cls,"toString",[]);return t?yield*this.str(yield*this.invoke(t.m,t.owner,e,[])):e.native.enumName!==void 0?e.native.enumName:e.cls.builtin?this.throwableText(e):`${e.cls.name}@${_e(e.id)}`}if(e instanceof K){const t=[];for(const s of e.items)t.push(s===e?"(this Collection)":yield*this.str(s));return`[${t.join(", ")}]`}if(e instanceof be){const t=[];for(const[s,i]of e.entries.values())t.push(`${yield*this.str(s)}=${yield*this.str(i)}`);return`{${t.join(", ")}}`}return e instanceof B?`[${e.elem.dims?"[L":{int:"I",double:"D",char:"C",boolean:"Z",long:"J"}[e.elem.name]??`L${e.elem.name};`}@${_e(e.id)}`:e instanceof de?e.kind==="StringBuilder"?e.buf:`java.util.${e.kind}@${_e(e.id)}`:`class ${e.name}`}*equalsVal(e,t){if(e instanceof I&&!e.cls.builtin){const s=this.findMethod(e.cls,"equals",[t]);if(s)return(yield*this.invoke(s.m,s.owner,e,[t]))===!0}return typeof e=="string"||e===null?e===t:this.same(e,t)&&e instanceof O==t instanceof O}*create(e){const t=[];for(const n of e.args)t.push(yield*this.eval(n));const s=e.type.name,i=this.classes.get(s);if(i)return yield*this.construct(i,t,e);if(s==="ArrayList"||s==="LinkedList"){const n=new K(this.id());return t[0]instanceof K&&(n.items=[...t[0].items]),n}if(s==="HashMap"||s==="TreeMap"||s==="LinkedHashMap")return new be(this.id());if(s==="Scanner")return new de("Scanner",this.id());if(s==="Random")return new de("Random",this.id());if(s==="StringBuilder"){const n=new de("StringBuilder",this.id());return typeof t[0]=="string"&&(n.buf=t[0]),n}if(s==="String")return t.length?yield*this.str(t[0]instanceof B?t[0].items.map(n=>n instanceof H?String.fromCharCode(n.code):"").join(""):t[0]):"";if(s==="Object")return new I(this.classes.get("Object"),this.id());throw new q("unsupported",`new ${s}`,e)}*call(e){var n,o;const t=()=>this.evalArgs(e.args);if(!e.target){const r=yield*t(),c=this.frame.self,l=this.findMethod((c==null?void 0:c.cls)??this.frame.cls,e.name,r)??(c?this.findMethod(this.frame.cls,e.name,r):void 0);if(!l)throw new q("symbol",`${e.name}()`,e);if(!l.m.isStatic&&!c)throw new q("type",`static:${e.name}`,e);return yield*this.invoke(l.m,l.owner,l.m.isStatic?void 0:c,r)}if(e.target.k==="super"){const r=yield*t(),c=this.findMethod(this.frame.cls.sup,e.name,r);return c?yield*this.invoke(c.m,c.owner,this.frame.self,r):yield*this.objectMethod(this.frame.self,e.name,r,e,!0)}const s=yield*this.eval(e.target),i=yield*t();if(s==null&&this.fail("NullPointerException",`Cannot invoke "${e.name}()" because value is null`),typeof s=="string")return yield*this.stringMethod(s,e.name,i,e);if(s instanceof I){const r=this.findMethod(s.cls,e.name,i);return r?yield*this.invoke(r.m,r.owner,r.m.isStatic?void 0:s,i):yield*this.objectMethod(s,e.name,i,e)}if(s instanceof K)return yield*this.listMethod(s,e.name,i,e);if(s instanceof be)return yield*this.mapMethod(s,e.name,i,e);if(s instanceof de)return yield*this.nativeMethod(s,e.name,i,e);if(s instanceof Se){const r=this.classes.get(s.name);if(r&&!r.builtin){if(((n=r.decl)==null?void 0:n.kind)==="enum"&&e.name==="values")return new B(ve(r.name),r.decl.enumConstants.map(l=>r.statics.get(l)),this.id());if(((o=r.decl)==null?void 0:o.kind)==="enum"&&e.name==="valueOf"){const l=r.statics.get(String(i[0]));return l instanceof I||this.fail("IllegalArgumentException",`No enum constant ${r.name}.${i[0]}`),l}const c=this.findMethod(r,e.name,i);if(!c)throw new q("symbol",`${s.name}.${e.name}()`,e);return yield*this.invoke(c.m,c.owner,void 0,i)}return yield*this.staticBuiltin(s.name,e.name,i,e)}if(s instanceof B){if(e.name==="clone")return new B(s.elem,[...s.items],this.id());if(e.name==="length")throw new q("symbol","length()",e)}if(ne(s)||typeof s=="boolean"){if(e.name==="equals")return yield*this.equalsVal(s,i[0]);if(e.name==="toString")return yield*this.str(s);if(e.name==="compareTo")return Math.sign(T(s)-T(i[0]));if(e.name==="intValue")return Math.trunc(T(s));if(e.name==="doubleValue")return new O(T(s))}throw new q("symbol",`${e.name}()`,e)}*evalArgs(e){const t=[];for(const s of e)t.push(yield*this.eval(s));return t}*objectMethod(e,t,s,i,n=!1){switch(t){case"toString":return n?e.cls.builtin||this.isA(e.cls,"Throwable")?this.throwableText(e):`${e.cls.name}@${_e(e.id)}`:yield*this.str(e);case"equals":return e===s[0];case"hashCode":return parseInt(_e(e.id),16)|0;case"getClass":return new Se(e.cls.name);case"getMessage":case"getLocalizedMessage":return e.native.message??null;case"printStackTrace":this.io.out(`${this.throwableText(e)} -${(e.native.trace??[]).map(o=>` at ${o} -`).join("")}`,!0);return;case"name":if(e.native.enumName!==void 0)return e.native.enumName;break;case"ordinal":if(e.native.ordinal!==void 0)return e.native.ordinal;break;case"compareTo":if(e.native.ordinal!==void 0&&s[0]instanceof I)return e.native.ordinal-(s[0].native.ordinal??0);break}throw new q("symbol",`${e.cls.name}.${t}()`,i)}index(e,t,s,i=!1){const n=T(e);return n>=0&&(i?n<=t:n{const l=T(r);return l>=0&&(c?l<=e.length:l1?n(s[1],!0):e.length;return c1?T(s[1]):0);case"lastIndexOf":return e.lastIndexOf(o);case"contains":return e.includes(o);case"startsWith":return e.startsWith(o);case"endsWith":return e.endsWith(o);case"isEmpty":return e.length===0;case"isBlank":return e.trim().length===0;case"toUpperCase":return e.toUpperCase();case"toLowerCase":return e.toLowerCase();case"trim":case"strip":return e.trim();case"concat":return e+o;case"replace":{const r=typeof s[1]=="string"?s[1]:s[1]instanceof H?String.fromCharCode(s[1].code):yield*this.str(s[1]);return e.split(o).join(r)}case"replaceAll":return e.replace(new RegExp(o,"g"),String(s[1]));case"matches":return new RegExp(`^(?:${o})$`).test(e);case"split":return new B(ve("String"),(e===""?[""]:e.split(new RegExp(o))).filter((r,c,l)=>r!==""||cnew H(r.charCodeAt(0))),this.id());case"repeat":return e.repeat(T(s[0]));case"hashCode":return Ui(e);case"toString":case"intern":return e}throw new q("unsupported",`String.${t}()`,i)}*listMethod(e,t,s,i){switch(t){case"add":return s.length===2?e.items.splice(this.index(s[0],e.items.length,i,!0),0,s[1]):e.items.push(s[0]),s.length===2?void 0:!0;case"addAll":return s[0]instanceof K&&e.items.push(...s[0].items),!0;case"get":return e.items[this.index(s[0],e.items.length,i)];case"set":{const n=this.index(s[0],e.items.length,i),o=e.items[n];return e.items[n]=s[1],o}case"size":return e.items.length;case"isEmpty":return e.items.length===0;case"clear":e.items=[];return;case"remove":{if(typeof s[0]=="number")return e.items.splice(this.index(s[0],e.items.length,i),1)[0];for(let n=0;nt==="keySet"?h[0]:h[1]),l}case"toString":return yield*this.str(e)}throw new q("unsupported",`HashMap.${t}()`,i)}*nativeMethod(e,t,s,i){if(e.kind==="out"||e.kind==="err"){const n=e.kind==="err";if(t==="println")this.io.out(`${s.length?yield*this.str(s[0]):""} -`,n);else if(t==="print")this.io.out(yield*this.str(s[0]),n);else if(t==="printf"||t==="format")this.io.out(yield*this.format(String(s[0]),s.slice(1)),n);else{if(t==="flush")return;throw new q("unsupported",`System.out.${t}()`,i)}return}if(e.kind==="Scanner")return yield*this.scan(t,i);if(e.kind==="Random"){if(t==="nextInt"){if(!s.length)return Math.random()*4294967296-2147483648|0;const n=s.length>1?T(s[0]):0,o=s.length>1?T(s[1]):T(s[0]);return o<=n&&this.fail("IllegalArgumentException","bound must be positive"),n+Math.floor(Math.random()*(o-n))}if(t==="nextDouble")return new O(Math.random());if(t==="nextBoolean")return Math.random()<.5}if(e.kind==="StringBuilder")switch(t){case"append":return e.buf+=yield*this.str(s[0]),e;case"toString":return e.buf;case"length":return e.buf.length;case"reverse":return e.buf=[...e.buf].reverse().join(""),e;case"charAt":return new H(e.buf.charCodeAt(T(s[0])));case"insert":return e.buf=e.buf.slice(0,T(s[0]))+(yield*this.str(s[1]))+e.buf.slice(T(s[0])),e;case"setLength":e.buf=e.buf.slice(0,T(s[0]));return;case"deleteCharAt":return e.buf=e.buf.slice(0,T(s[0]))+e.buf.slice(T(s[0])+1),e}if(t!=="close")throw new q("unsupported",`${e.kind}.${t}()`,i)}*scan(e,t){const s=this.stdin,i=function*(){const o=yield{k:"input"};return o==null?!1:(s.buf+=`${o} -`,!0)},n=function*(o,r){for(;;){const c=/^\s*(\S+)/.exec(s.buf);if(c&&(c[0].length=t.length&&this.fail("IllegalArgumentException",`Format specifier '${r[0]}'`);const m=t[s++];switch(u.toLowerCase()){case"d":m instanceof O&&this.fail("IllegalArgumentException","d != java.lang.Double"),f=String(Math.trunc(T(m))),c.includes(",")&&(f=f.replace(/\B(?=(\d{3})+(?!\d))/g,"."));break;case"f":if(f=T(m).toFixed(h?Number(h):6),c.includes(",")){const[g,_]=f.split(".");f=g.replace(/\B(?=(\d{3})+(?!\d))/g,".")+(_?`,${_}`:"")}else f=f.replace(".",",");break;case"e":f=T(m).toExponential(h?Number(h):6).replace(".",",");break;case"x":f=(T(m)>>>0).toString(16);break;case"c":f=m instanceof H?String.fromCharCode(m.code):String.fromCharCode(T(m));break;case"b":f=String(m!==null&&m!==!1);break;default:f=yield*this.str(m),h&&(f=f.slice(0,Number(h)))}c.includes("+")&&/^\d/.test(f)&&/[df]/i.test(u)&&(f=`+${f}`),u===u.toUpperCase()&&u!=="%"&&(f=f.toUpperCase())}if(l&&f.length0&&(yield*this.compare(t[i-1],t[i]))>0;i--)[t[i-1],t[i]]=[t[i],t[i-1]];return t}*compare(e,t){if(ne(e)&&ne(t))return T(e)-T(t);if(typeof e=="string"&&typeof t=="string")return et?1:0;if(e instanceof I){const s=this.findMethod(e.cls,"compareTo",[t]);if(s)return T(yield*this.invoke(s.m,s.owner,e,[t]));if(e.native.ordinal!==void 0&&t instanceof I)return e.native.ordinal-(t.native.ordinal??0)}this.fail("ClassCastException","not Comparable")}*staticBuiltin(e,t,s,i){const n=T(s[0]),o=r=>new O(r);switch(`${e}.${t}`){case"Math.abs":return s[0]instanceof O?o(Math.abs(n)):Math.abs(n);case"Math.max":case"Math.min":{const r=t==="max"?Math.max(n,T(s[1])):Math.min(n,T(s[1]));return s[0]instanceof O||s[1]instanceof O?o(r):r}case"Math.pow":return o(Math.pow(n,T(s[1])));case"Math.sqrt":return o(Math.sqrt(n));case"Math.cbrt":return o(Math.cbrt(n));case"Math.random":return o(Math.random());case"Math.round":return Math.floor(n+.5);case"Math.floor":return o(Math.floor(n));case"Math.ceil":return o(Math.ceil(n));case"Math.sin":case"Math.cos":case"Math.tan":case"Math.log":case"Math.log10":case"Math.exp":return o(Math[t](n));case"Math.toRadians":return o(n*Math.PI/180);case"Math.toDegrees":return o(n*180/Math.PI);case"Math.floorMod":return(n%T(s[1])+T(s[1]))%T(s[1]);case"Integer.parseInt":case"Integer.valueOf":case"Long.parseLong":{if(typeof s[0]=="number")return s[0];const r=String(s[0]);return/^[+-]?\d+$/.test(r)||this.fail("NumberFormatException",`For input string: "${r}"`),parseInt(r,10)}case"Double.parseDouble":case"Double.valueOf":{if(ne(s[0]))return o(n);const r=String(s[0]).trim();return/^[+-]?(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?[dDfF]?$/.test(r)||this.fail("NumberFormatException",`For input string: "${r}"`),o(parseFloat(r))}case"Boolean.parseBoolean":return String(s[0]).toLowerCase()==="true";case"Integer.toString":case"Double.toString":case"String.valueOf":case"Objects.toString":return s[0]instanceof B&&s[0].elem.name==="char"?s[0].items.map(r=>String.fromCharCode(T(r))).join(""):yield*this.str(s[0]);case"Integer.compare":case"Double.compare":return Math.sign(n-T(s[1]));case"Integer.toBinaryString":return(n>>>0).toString(2);case"Integer.toHexString":return(n>>>0).toString(16);case"Integer.sum":return n+T(s[1])|0;case"Objects.equals":return s[0]===null?s[1]===null:yield*this.equalsVal(s[0],s[1]);case"String.format":return yield*this.format(String(s[0]),s.slice(1));case"String.join":{const r=[],c=s[1]instanceof K||s[1]instanceof B?s[1].items:s.slice(1);for(const l of c)r.push(yield*this.str(l));return r.join(String(s[0]))}case"Character.isDigit":return new RegExp("\\p{Nd}","u").test(String.fromCharCode(n));case"Character.isLetter":return new RegExp("\\p{L}","u").test(String.fromCharCode(n));case"Character.isLetterOrDigit":return/[\p{L}\p{Nd}]/u.test(String.fromCharCode(n));case"Character.isUpperCase":return new RegExp("\\p{Lu}","u").test(String.fromCharCode(n));case"Character.isLowerCase":return new RegExp("\\p{Ll}","u").test(String.fromCharCode(n));case"Character.isWhitespace":return/\s/.test(String.fromCharCode(n));case"Character.toUpperCase":return new H(String.fromCharCode(n).toUpperCase().charCodeAt(0));case"Character.toLowerCase":return new H(String.fromCharCode(n).toLowerCase().charCodeAt(0));case"Character.getNumericValue":return parseInt(String.fromCharCode(n),36);case"Arrays.toString":{if(s[0]===null)return"null";const r=s[0],c=[];for(const l of r.items)c.push(yield*this.str(l));return`[${c.join(", ")}]`}case"Arrays.sort":case"Collections.sort":{const r=s[0];r.items=yield*this.sorted(r.items);return}case"Arrays.fill":s[0].items.fill(te(s[1],s[0].elem));return;case"Arrays.asList":case"List.of":{const r=new K(this.id());return r.items=s.length===1&&s[0]instanceof B?[...s[0].items]:s,r}case"Collections.reverse":s[0].items.reverse();return;case"Collections.shuffle":s[0].items.sort(()=>Math.random()-.5);return;case"Collections.max":case"Collections.min":{const r=s[0].items;r.length||this.fail("NoSuchElementException");const c=yield*this.sorted(r);return t==="max"?c[c.length-1]:c[0]}case"Thread.sleep":yield{k:"sleep",ms:n};return;case"System.currentTimeMillis":return Date.now();case"System.nanoTime":return Math.round(performance.now()*1e6);case"System.exit":throw new Jt}throw new q("unsupported",`${e}.${t}()`,i)}}class Jt extends Error{}function Kt(a){return a.name==="main"&&a.isStatic&&a.params.length===1&&a.params[0].type.name==="String"&&a.params[0].type.dims===1}function Ki(a){return{name:a.name,kind:a.kind==="field"?"field":"method",params:a.params.length>0,returns:a.type,description:a.kind==="field"?"":a.params.map(e=>`${e.name}: ${e.type}`).join(", ")}}function Xi(a,e,t="de"){const s=new Map(a.map(r=>[r.name,r])),i=(r,c)=>{const l=new Set;for(let h=r?s.get(r):void 0;h&&!l.has(h.name);h=h.superclass?s.get(h.superclass):void 0){if(h.name===c)return!0;l.add(h.name)}return!1},n=(r,c,l)=>r.visibility==="+"?!0:r.visibility==="-"?!l&&e.cls===c.name:c.pkg===e.pkg?!0:r.visibility==="#"&&i(e.cls,c.name),o=(r,c)=>{if(!s.get(r))return;const h=[],u=new Set,f=new Set,m=(g,_)=>{if(u.has(g))return;u.add(g);const y=s.get(g);if(!y){if(!c)for(const w of Is(g,t))f.has(w.name)||(f.add(w.name),h.push(w));return}for(const w of y.members){if(w.kind==="constructor"||w.isStatic!==c||!n(w,y,_))continue;const $=`${w.kind}:${w.name}:${w.params.map(p=>p.type).join(",")}`;f.has($)||(f.add($),h.push(Ki(w)))}y.superclass&&m(y.superclass,!0);for(const w of y.interfaces)m(w,!0)};return m(r,!1),h};return{members:r=>o(r,!1),statics:r=>o(r,!0),thisType:e.cls}}const Xt=["fields","refs","ctors","methods"],Yi=[{fields:"Attribute",refs:"Referenzattribute",ctors:"Konstruktor",methods:"Methoden"},{fields:"Attributes",refs:"Reference attributes",ctors:"Constructor",methods:"Methods"},{fields:"Atributos",refs:"Atributos de referência",ctors:"Construtor",methods:"Métodos"}],Te=a=>a.trim().toLowerCase().replace(/[:.]$/,""),Gi=new Set(Yi.flatMap(a=>Object.values(a)).flatMap(a=>[Te(a),Te(a)+"en",Te(a)+"s",Te(a)+"es"])),ut=a=>{const e=/^\s*\/\/\s*(.+?)\s*$/.exec(a);return!!e&&Gi.has(Te(e[1]))};function Zi(a,e){return a.kind==="constructor"?"ctors":a.kind==="method"?"methods":e.has(Fe(a.type).name)?"refs":"fields"}const Be=(a,e)=>a.lastIndexOf(` -`,e-1)+1,Yt=(a,e)=>{const t=a.indexOf(` -`,e);return t<0?a.length:t+1};function _s(a,e){const t=a.indexOf("{",e.headerTo);return a.slice(t+1,e.bodyEnd).split(` -`).some(ut)}function pt(a,e,t,s){if(e.kind!=="class")return a;const i=a.indexOf("{",e.headerTo);if(i<0||i>e.bodyEnd||oe(a).some(_=>_.from>i&&_.fromo||!/^\s*$/.test(a.slice(i+1,n))||!/^\s*$/.test(a.slice(o,e.bodyEnd)))return a;const r=/^[ \t]*/.exec(a.slice(Be(a,e.from)))[0]+" ",c=new Map(Xt.map(_=>[_,[]])),l=[],h=new Set;for(const _ of e.members){if(h.has(_.from))continue;h.add(_.from);let y=Be(a,_.from);if(!/^\s*$/.test(a.slice(y,_.from)))return a;const w=Yt(a,_.to);if(!/^\s*$/.test(a.slice(_.to,w)))return a;for(;!(y<=n);){const $=Be(a,y-1),p=a.slice($,y);if(ut(p)||!/^\s*(\/\/|\/\*|\*|@)/.test(p))break;y=$}l.push([y,w]),c.get(Zi(_,t)).push(a.slice(y,w).replace(/\s+$/,""))}l.sort((_,y)=>_[0]-y[0]);let u="",f=n;for(const[_,y]of[...l,[o,o]])_>f&&(u+=a.slice(f,_)),f=Math.max(f,y);const m=u.split(` -`).filter(_=>!ut(_)).join(` -`).trim(),g=Xt.map(_=>{const y=c.get(_),w=_==="fields"||_==="refs"?` -`:` - -`;return`${r}// ${s[_]}`+(y.length?` -`+y.join(w):"")});return m&&g.push(r+m),a.slice(0,n)+g.join(` - -`)+` -`+a.slice(o)}function Qi(a,e,t,s){const i=oe(a).find(n=>n.name===e);return i&&_s(a,i)?pt(a,i,t,s):a}const Gt="visuobject.layout",en="visuobject.explorer",nt="40px",fe={explorer:260,split:45},xs=(a,e,t,s)=>Number.isFinite(a)?Math.min(t,Math.max(e,Math.round(a))):s,Zt=a=>xs(a,180,480,fe.explorer),Qt=a=>xs(a,20,80,fe.split);class tn{constructor(e,t){E(this,"collapsed",new Set);E(this,"explorer",fe.explorer);E(this,"split",fe.split);this.root=e,this.changed=t;try{const n=JSON.parse(localStorage.getItem(Gt)??"null");if(n){for(const o of n.collapsed??[])(o==="explorer"||o==="code"||o==="diagram")&&this.collapsed.add(o);this.explorer=Zt(Number(n.explorer??fe.explorer)),this.split=Qt(Number(n.split??fe.split))}else localStorage.getItem(en)==="0"&&this.collapsed.add("explorer")}catch{}for(const n of e.querySelectorAll("[data-collapse]"))n.addEventListener("click",()=>this.toggle(n.dataset.collapse,!0));for(const n of e.querySelectorAll("[data-expand]"))n.addEventListener("click",()=>this.toggle(n.dataset.expand,!1));const s=e.querySelector(".vo-splitter[data-split=explorer]"),i=e.querySelector(".vo-splitter[data-split=code]");this.splitter(s,{at:n=>n.clientX-e.getBoundingClientRect().left,get:()=>this.explorer,set:n=>this.explorer=Zt(n),reset:fe.explorer,step:20}),this.splitter(i,{at:n=>{const o=e.querySelector("[data-pane=code]").getBoundingClientRect(),r=e.querySelector("[data-pane=diagram]").getBoundingClientRect();return(n.clientX-o.left)/(r.right-o.left)*100},get:()=>this.split,set:n=>this.split=Qt(n),reset:fe.split,step:5}),this.apply()}isCollapsed(e){return this.collapsed.has(e)}toggle(e,t=!this.collapsed.has(e)){var i,n;if(t&&!this.collapsed.has(e)&&this.collapsed.size>=2)return;t?this.collapsed.add(e):this.collapsed.delete(e),this.apply();const s=this.root.querySelector(`[data-pane=${e}]`);t?(i=s.querySelector(".vo-strip"))==null||i.focus():(n=s.querySelector("[data-collapse]"))==null||n.focus()}apply(){const e=this.collapsed,t=e.has("code")&&e.has("diagram"),s=e.has("explorer")?nt:t?"1fr":`${this.explorer}px`,i=e.has("code")?nt:`minmax(0, ${this.split}fr)`,n=e.has("diagram")?nt:`minmax(0, ${100-this.split}fr)`;this.root.style.setProperty("--vo-cols",`${s} 1px ${i} 1px ${n}`);for(const c of this.root.querySelectorAll("[data-pane]"))c.classList.toggle("collapsed",e.has(c.dataset.pane));const o=this.root.querySelector(".vo-splitter[data-split=explorer]"),r=this.root.querySelector(".vo-splitter[data-split=code]");o.classList.toggle("fixed",e.has("explorer")||t),r.classList.toggle("fixed",e.has("code")||e.has("diagram")),o.setAttribute("aria-valuenow",String(this.explorer)),r.setAttribute("aria-valuenow",String(this.split));try{localStorage.setItem(Gt,JSON.stringify({collapsed:[...e],explorer:this.explorer,split:this.split}))}catch{}this.changed()}splitter(e,t){e.addEventListener("pointerdown",s=>{if(e.classList.contains("fixed"))return;s.preventDefault(),e.setPointerCapture(s.pointerId),this.root.classList.add("vo-resizing");const i=o=>{t.set(t.at(o)),this.apply()},n=()=>{this.root.classList.remove("vo-resizing"),e.removeEventListener("pointermove",i),e.removeEventListener("pointerup",n),e.removeEventListener("pointercancel",n)};e.addEventListener("pointermove",i),e.addEventListener("pointerup",n),e.addEventListener("pointercancel",n)}),e.addEventListener("dblclick",()=>{t.set(t.reset),this.apply()}),e.addEventListener("keydown",s=>{const i=s.key==="ArrowLeft"?-t.step:s.key==="ArrowRight"?t.step:0;i&&(s.preventDefault(),t.set(t.get()+i),this.apply())})}}const sn=a=>a==="boolean"?"false":/^(int|long|short|byte|double|float)$/.test(a)?"0":a==="char"?"'\\0'":"null",es=a=>a.map(e=>`${e.type} ${e.name}`).join(", ");function nn(a){const e=new Map,t=(s,i)=>{for(const n of s.members)if(n.kind==="method"&&!n.isStatic&&(i||n.isAbstract)){const o=`${n.name}(${n.params.map(r=>r.type).join(",")})`;e.has(o)||e.set(o,{name:n.name,type:n.type||"void",params:n.params,visibility:i||n.visibility==="+"?"public":n.visibility==="#"?"protected":""})}};a.superclass&&t(a.superclass,!1);for(const s of a.interfaces)t(s,!0);return[...e.values()]}function on(a){const e=[];a.pkg&&e.push(`package ${a.pkg};`,"");const t=[a.superclass,...a.interfaces].filter(r=>!!r&&!!r.pkg&&r.pkg!==a.pkg).map(r=>`import ${r.pkg}.${r.name};`);t.length&&e.push(...[...new Set(t)].sort(),""),a.comment&&(e.push("/**",` * ${a.name}`," *"),e.push(` * @author ${a.author.trim()||"…"}`," */"));const s=[a.isPublic?"public":"",a.kind==="class"&&a.isAbstract?"abstract":"",a.kind==="class"&&a.isFinal?"final":""].filter(Boolean).join(" "),i=a.interfaces.map(r=>r.name),n=a.kind==="class"?`${s?s+" ":""}class ${a.name}${a.superclass?` extends ${a.superclass.name}`:""}${i.length?` implements ${i.join(", ")}`:""}`:`${s?s+" ":""}interface ${a.name}${i.length?` extends ${i.join(", ")}`:""}`;e.push(`${n} {`);const o=[];if(a.kind==="class"){if(a.constructors&&a.superclass){const r=a.superclass.members.filter(c=>c.kind==="constructor");for(const c of r.length?r:[{params:[]}])o.push([` public ${a.name}(${es(c.params)}) {`,` super(${c.params.map(l=>l.name).join(", ")});`," }"])}else a.constructors&&o.push([` public ${a.name}() {`," }"]);if(a.inherited&&!a.isAbstract)for(const r of nn(a))o.push([" @Override",` ${r.visibility?r.visibility+" ":""}${r.type} ${r.name}(${es(r.params)}) {`,...r.type==="void"?[]:[` return ${sn(r.type)};`]," }"]);a.main&&o.push([" public static void main(String[] args) {"," }"])}return o.length&&e.push("",o.map(r=>r.join(` -`)).join(` - -`)),e.push("}",""),e.join(` -`)}const P=a=>a.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]),ks="visuobject.author";function an(){try{return localStorage.getItem(ks)??""}catch{return""}}function rn(a,e){return new Promise(t=>{const s=document.createElement("div");s.className="confirm vo-wizard-back";const i=a==="class"||a==="interface",n=``,o=``,r=e.classes,c=r.filter(k=>k.kind==="class").map(k=>``).join(""),l=r.filter(k=>k.kind==="interface"),h=l.length?`
    ${P(d(a==="class"?"web_visuobject_new_interfaces":"web_visuobject_new_extends_interfaces"))}${l.map(k=>``).join("")}
    `:"",u=(k,j,M=!1)=>``,f=i?`${n}${o} - -
    ${P(d("web_visuobject_new_modifiers"))} - - ${a==="class"?`${u("abstract","abstract")}${u("final","final")}`:""} -
    - ${a==="class"?``:""} - ${h} - ${a==="class"?`
    ${P(d("web_visuobject_new_stubs"))} - ${u("main","public static void main(String[] args)")} - ${u("constructors",P(d("web_visuobject_new_stub_constructors")))} - ${u("inherited",P(d("web_visuobject_new_stub_inherited")),!0)} -
    `:""} -
    ${P(d("web_visuobject_new_comment_legend"))} - ${u("comment",P(d("web_visuobject_new_comment")))} - -
    `:`${a==="package"?n:""}`;s.innerHTML=``;const m=s.querySelector("form"),g=k=>m.querySelector(`[name="${k}"]`),_=g("name"),y=g("root"),w=g("pkg"),$=m.querySelector(".vo-wizard-error");_.value=e.suggest(a);const p=()=>{if(!w)return;const k=e.packagesIn((y==null?void 0:y.value)??e.root),j=k.includes("")?k:[...k,""],M=w.value||e.pkg;w.innerHTML=j.map(A=>``).join("")};p(),y==null||y.addEventListener("change",p);const v=g("abstract"),x=g("final");v==null||v.addEventListener("change",()=>v.checked&&x&&(x.checked=!1)),x==null||x.addEventListener("change",()=>x.checked&&v&&(v.checked=!1));const L=()=>{const k=e.check(a,_.value.trim(),(y==null?void 0:y.value)??e.root,(w==null?void 0:w.value)??e.pkg);return $.textContent=k??"",!k};_.addEventListener("input",L),w==null||w.addEventListener("change",L);const S=k=>{s.remove(),t(k)};m.addEventListener("submit",k=>{var G;if(k.preventDefault(),!L())return _.focus();const j=_.value.trim(),M=(y==null?void 0:y.value)??e.root,A=(w==null?void 0:w.value)??"";if(!i)return S({kind:a,name:j,root:M,pkg:A});const R=g("author").value;try{localStorage.setItem(ks,R)}catch{}const C=z=>{var he;return!!((he=g(z))!=null&&he.checked)},F=z=>e.classes.find(he=>he.name===z),W=(G=g("superclass"))==null?void 0:G.value;S({kind:a,name:j,root:M,pkg:A,options:{kind:a,pkg:A,name:j,isPublic:g("visibility").value==="public",isAbstract:C("abstract"),isFinal:C("final"),superclass:W?F(W):void 0,interfaces:[...m.querySelectorAll("[name=iface]:checked")].map(z=>F(z.value)).filter(z=>!!z),main:C("main"),constructors:C("constructors"),inherited:C("inherited"),comment:C("comment"),author:R}})}),s.addEventListener("click",k=>{const j=k.target;(j===s||j.closest("[data-cancel]"))&&S(void 0)}),s.addEventListener("keydown",k=>{k.stopPropagation(),k.key==="Escape"&&S(void 0)}),document.body.append(s),_.focus(),_.select()})}const me=(a,e,t,s)=>a.slice(0,e)+s+a.slice(t),js={"+":"public ","-":"private ","#":"protected ","~":""},Ss=(a,e)=>a.lastIndexOf(` -`,e-1)+1;function Ls(a,e){const t=e.members[0];if(t){const s=Ss(a,t.from),i=/^[ \t]*/.exec(a.slice(s))[0];if(s+i.length===t.from)return i}return" "}function Es(a,e,t){return/^(\s*@[\w.]+(\([^)]*\))?\s*)*/.exec(a.slice(e,t))[0]}function Ms(a,e){var n;const t=a.slice(e.headerFrom,e.nameFrom),s=a.slice(e.nameTo,e.to),i=e.shared?"":((n=/^\s*=\s*([\s\S]*?)\s*;?\s*$/.exec(s))==null?void 0:n[1])??"";return{visibility:e.visibility,name:e.name,type:e.type,isStatic:e.isStatic,isFinal:/\bfinal\b/.test(t),init:i}}function cn(a){return`${js[a.visibility]}${a.isStatic?"static ":""}${a.isFinal?"final ":""}${a.type} ${a.name}${a.init.trim()?` = ${a.init.trim()}`:""};`}function ln(a,e,t){if(e.shared)return t.name&&t.name!==e.name?me(a,e.nameFrom,e.nameTo,t.name):a;const s={...Ms(a,e),...t};return me(a,e.from,e.to,Es(a,e.from,e.to)+cn(s))}function hn(a){return{visibility:a.visibility,name:a.name,type:a.type,params:a.params,isStatic:a.isStatic,isAbstract:a.isAbstract}}function dn(a){const e=[];for(const t of a.split(",").map(s=>s.trim()).filter(Boolean)){const s=/^([\p{L}_$][\p{L}\p{N}_$]*)\s*:\s*(.+)$/u.exec(t),i=/^(.+?)\s+([\p{L}_$][\p{L}\p{N}_$]*)$/u.exec(t);if(s)e.push({name:s[1],type:s[2].replace(/\s+/g,"")});else if(i)e.push({name:i[2],type:i[1].replace(/\s+/g,"")});else return}return e}const un=a=>a.map(e=>`${e.name}: ${e.type}`).join(", ");function pn(a,e,t,s){const i={...hn(t),...s},n=e.kind==="interface",o=t.kind==="constructor",c=`${n?"":js[i.visibility]}${i.isStatic&&!o?"static ":""}${i.isAbstract&&!n&&!o?"abstract ":""}`,l=i.params.map($=>`${$.type} ${$.name}`).join(", "),h=o?`${c}${t.name}(${l})`:`${c}${i.type||"void"} ${i.name}(${l})`,u=Es(a,t.headerFrom,t.headerTo);let f=me(a,t.headerFrom,t.headerTo,u+h);const m=u.length+h.length-(t.headerTo-t.headerFrom),g=t.headerTo+m,_=t.to+m,y=f.slice(g,_),w=!(i.isAbstract||n&&!i.isStatic);if(!w&&/^\s*(throws[^{;]*)?\{/.test(y))f=me(f,g,_,";");else if(w&&/^\s*;/.test(y)){const $=Ls(a,e),p=i.type&&i.type!=="void"&&!o?`${$} return ${i.type==="boolean"?"false":/^(int|long|short|byte|double|float|char)$/.test(i.type)?"0":"null"}; -`:"";f=me(f,g,_,` { -${p}${$}}`)}return f}function fn(a,e){const t=a.slice(0,e.headerFrom),s=/\/\*\*([\s\S]*?)\*\/\s*$/.exec(t);return s?s[1].split(` -`).map(i=>i.replace(/^\s*\*\s?/,"")).join(` -`).trim():""}function mn(a,e,t){const s=a.slice(0,e.headerFrom),i=/\/\*\*[\s\S]*?\*\/\s*$/.exec(s),n=/[ \t]*$/.exec(s)[0],o=t.trim()?`/** -${t.trim().split(` -`).map(r=>` * ${r}`.trimEnd()).join(` -`)} - */ -${n}`:"";return i?me(a,i.index,e.headerFrom,o):me(a,e.headerFrom,e.headerFrom,o)}function gt(a,e,t){const s=Ls(a,e),i=t.split(` -`).map(h=>h&&s+h).join(` -`),n=Ss(a,e.bodyEnd),o=/^\s*$/.test(a.slice(n,e.bodyEnd)),r=o?n:e.bodyEnd,c=o?"":` -`,l=e.members.length?` -`:"";return me(a,r,r,`${c}${l}${i} -`)}const yt=a=>a.members.filter(e=>e.kind==="field"&&!e.isStatic),ts=a=>a.charAt(0).toUpperCase()+a.slice(1);function bn(a,e){const t=yt(e),s=t.map(n=>`${n.type} ${n.name}`).join(", "),i=t.map(n=>` this.${n.name} = ${n.name};`).join(` -`);return gt(a,e,`public ${e.name}(${s}) { -${i}${i?` -`:""}}`)}function vn(a,e){const t=new Set(e.members.filter(i=>i.kind==="method").map(i=>i.name)),s=[];for(const i of yt(e)){const n=/\bfinal\b/.test(a.slice(i.headerFrom,i.nameFrom)),o=`${i.type==="boolean"?"is":"get"}${ts(i.name)}`,r=`set${ts(i.name)}`;t.has(o)||s.push(`public ${i.type} ${o}() { - return ${i.name}; -}`),!n&&!t.has(r)&&s.push(`public void ${r}(${i.type} ${i.name}) { - this.${i.name} = ${i.name}; -}`)}return s.length?gt(a,e,s.join(` - -`)):a}function gn(a,e){if(e.members.some(i=>i.kind==="method"&&i.name==="toString"&&i.params.length===0))return a;const t=yt(e).map((i,n)=>`"${n?", ":""}${i.name}=" + ${i.name}`),s=t.length?`"${e.name} [" + ${t.join(" + ")} + "]"`:`"${e.name} []"`;return gt(a,e,`@Override -public String toString() { - return ${s}; -}`)}const ss="VisuObject-Workspace",Xe="visuobject.json",ge=a=>a.replace(/[\\/:*?"<>|]/g,"_").trim()||"Projekt";async function yn(a,e){try{return await a.dir(e)}catch{return}}async function is(a){const e=[];for(const t of await a.list()){if(t.kind!=="directory")continue;const s=await a.dir(t.name);(await s.has("src")||await s.has(Xe))&&e.push(t.name)}return e.sort((t,s)=>t.localeCompare(s))}function Cs(a){const e=[...a.matchAll(/]*>/g)].map(t=>t[0]).filter(t=>/\bkind\s*=\s*"src"/.test(t)&&!/\bcombineaccessrules\b/.test(t)).map(t=>{var s;return((s=/\bpath\s*=\s*"([^"]+)"/.exec(t))==null?void 0:s[1])??""}).filter(t=>t&&!t.startsWith("/")&&!t.includes(":")&&t.split("/").every(Je));return e.length?[...new Set(e)]:[Z]}function wn(a,e){const t=e.map(i=>` -`).join("");return a.replace(/[ \t]*]*\bkind\s*=\s*"src"[^>]*\/>[ \t]*\n?/g,i=>/\bcombineaccessrules\b/.test(i)?i:"").replace(/(]*>[ \t]*\n?)/,`$1${t}`)}async function $n(a,e){const t=await a.dir(e),s={name:e,files:[],packages:[]},i=await t.has(".classpath")?Cs(await t.read(".classpath")):[Z],n=async(c,l,h)=>{const u=await c.list(),f=re(l,h);h&&!u.some(m=>m.kind==="file"&&/\.java$/i.test(m.name))&&s.packages.push(f);for(const m of u)m.kind==="directory"?await n(await c.dir(m.name),l,h?`${h}.${m.name}`:m.name):/\.java$/i.test(m.name)&&s.files.push({path:le(f,m.name),text:await c.read(m.name)})};for(const c of i){let l=t;for(const h of c.split("/"))l=l&&await yn(l,h);l&&await n(l,c,"")}const o=i.filter(c=>c!==Z);o.length&&(s.sourceFolders=o),s.packages=s.packages.filter(c=>!s.packages.some(l=>l.startsWith(c+"."))&&!s.files.some(l=>D(l.path).startsWith(c+"."))),s.files.sort((c,l)=>c.path.localeCompare(l.path));let r={};if(await t.has(Xe))try{const c=JSON.parse(await t.read(Xe)),l=ys(c.uml);l&&(s.uml=l);const h=vs(c.notes);h&&(s.notes=h);const u=gs(c.objects);if(u&&(s.objects=u),c.positions&&typeof c.positions=="object")for(const[f,m]of Object.entries(c.positions))typeof(m==null?void 0:m.x)=="number"&&typeof(m==null?void 0:m.y)=="number"&&(r[f]={x:m.x,y:m.y})}catch{r={}}return{project:s,positions:r}}const _n=a=>` - - ${a.replace(/[&<>]/g,e=>({"&":"&","<":"<",">":">"})[e])} - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - -`,xn=` - - - - -`;async function kn(a,e,t,s){var c,l;const i=await a.dir(e,!0),n=ue(t),o=await i.has(".classpath")?await i.read(".classpath"):void 0;for(const h of o?Cs(o):[])!n.includes(h)&&!h.includes("/")&&await i.has(h)&&await i.remove(h);for(const h of n){let u=i;for(const y of h.split("/"))u=await u.dir(y,!0);const f=t.files.filter(y=>U(y.path)===h),m=new Set(f.map(y=>y.path.slice(y.path.indexOf(":")+1))),g=new Set;for(const y of ce(t,h)){const w=y.slice(y.indexOf(":")+1);if(!w)continue;const $=w.split(".");for(let p=1;p<=$.length;p++)g.add($.slice(0,p).join("/"))}const _=async(y,w)=>{for(const $ of await y.list()){const p=w?`${w}/${$.name}`:$.name;$.kind==="directory"?g.has(p)?await _(await y.dir($.name),p):n.some(v=>`${h}/${p}`===v||v.startsWith(`${h}/${p}/`))||await y.remove($.name):/\.java$/i.test($.name)&&!m.has(p)&&await y.remove($.name)}};await _(u,"");for(const y of ce(t,h)){const w=y.slice(y.indexOf(":")+1);let $=u;for(const p of w?w.split("."):[])$=await $.dir(p,!0);for(const p of f.filter(v=>D(v.path)===y)){const v=p.path.slice(p.path.lastIndexOf("/")+1).slice(p.path.includes("/")?0:p.path.indexOf(":")+1);(!await $.has(v)||await $.read(v)!==p.text)&&await $.write(v,p.text)}}}await i.write(Xe,JSON.stringify({format:"visuobject",version:1,uml:t.uml??{},...(c=t.notes)!=null&&c.length?{notes:t.notes}:{},...(l=t.objects)!=null&&l.length?{objects:t.objects}:{},positions:s},null,2)+` -`),await i.has(".project")||await i.write(".project",_n(t.name));const r=wn(o??xn,n);r!==o&&await i.write(".classpath",r)}function ns(a,e){const t=new Set(e.map(i=>i.toLowerCase()));let s=a;for(let i=2;t.has(s.toLowerCase());i++)s=a+i;return s}const os="visuobject.project",jn="visuobject.code",ot="visuobject.active",as="visuobject.positions",rs="visuobject.tree",cs="visuobject.tabs",We="visuobject.wsProject",ls="visuobject.inspector",at="visuobject.inspectorHeight",rt="visuobject.noWorkspace",hs="visuobject.diagram",ds="visuobject.breakpoints",us="visuobject-workspace",Sn=80,b=a=>a.replace(/[&<>"]/g,e=>({"&":"&","<":"<",">":">",'"':"""})[e]),pe=/^[\p{L}_$][\p{L}\p{N}_$]*$/u;function se(a){try{return localStorage.getItem(a)}catch{return null}}function J(a,e){try{localStorage.setItem(a,e)}catch{}}function Ln(){return Gs("visuobject",ct())}function En(a,e,t){const s=URL.createObjectURL(new Blob([e],{type:t})),i=document.createElement("a");i.href=s,i.download=a,i.click(),setTimeout(()=>URL.revokeObjectURL(s),1e3)}const Mn='';class Cn{constructor(e){E(this,"shell");E(this,"editor");E(this,"canvas");E(this,"lines");E(this,"frames");E(this,"status");E(this,"tree");E(this,"tabs");E(this,"openTabs",[]);E(this,"project");E(this,"active");E(this,"currentPackage");E(this,"classes",[]);E(this,"positions",new Map);E(this,"selection");E(this,"relSelection");E(this,"inspector");E(this,"tool","select");E(this,"pendingFrom");E(this,"suppressClick",!1);E(this,"treeSelection");E(this,"open",new Map);E(this,"history",[]);E(this,"future",[]);E(this,"lastTyping",0);E(this,"editing",!1);E(this,"objectDiagram");E(this,"diagramMode",["object","sequence"].find(e=>e===se(hs))??"class");E(this,"sequence");E(this,"noteSelection");E(this,"workspace");E(this,"pendingFolder");E(this,"projects",[]);E(this,"folder");E(this,"saved");E(this,"savedFiles",new Map);E(this,"wsBox");E(this,"breakpoints",new Map);E(this,"dbg");E(this,"renameTimer");E(this,"treeClick",{node:"",time:0});E(this,"drag");E(this,"lastClick",{spot:"",time:0});E(this,"endPop");E(this,"classTab","class");var i;document.documentElement.classList.add("visuobject-theme"),history.replaceState(history.state,"",`${location.pathname}?visuobject${location.hash}`);try{const n=JSON.parse(se(as)??"{}");for(const[c,l]of Object.entries(n))Number.isFinite(l==null?void 0:l.x)&&Number.isFinite(l==null?void 0:l.y)&&this.positions.set(c,l);const o=JSON.parse(se(rs)??"{}");for(const[c,l]of Object.entries(o))this.open.set(c,!!l);const r=JSON.parse(se(ds)??"{}");for(const[c,l]of Object.entries(r))Array.isArray(l)&&this.breakpoints.set(c,new Set(l.filter(Number.isInteger)))}catch{}const t=se(jn);this.project=qt(se(os)??"")??(t!==null?At("Projekt",[{name:"",text:t}]):Ft());const s=se(ot);this.active=this.project.files.some(n=>n.path===s)?s:(i=this.project.files[0])==null?void 0:i.path;try{const n=JSON.parse(se(cs)??"[]");Array.isArray(n)&&(this.openTabs=n.filter(o=>typeof o=="string"&&this.project.files.some(r=>r.path===o)))}catch{}this.shell=document.createElement("div"),this.shell.className="shell visuobject",e.replaceWith(this.shell),this.build(),new tn(this.shell.querySelector(".vo-columns"),()=>this.drawLines()),this.showActive(),this.refresh(),window.addEventListener("keydown",n=>this.onKey(n)),window.addEventListener("resize",()=>this.drawLines()),window.addEventListener("beforeunload",n=>{this.isDirty()&&n.preventDefault()}),this.restoreWorkspace()}build(){const e=(p,v,x,L=v)=>``;this.shell.innerHTML=` -
    - VisuObject ${b(d("web_visuobject_subtitle"))} -
    - ${e("new",d("toolbar_new"),ie.newFile,d("web_visuobject_new_tip"))} - ${e("open",d("menu_open"),ie.open,d("web_visuobject_open_tip"))} - ${e("save",d("menu_save"),ie.save,d("web_visuobject_save_tip"))} - ${e("undo",d("toolbar_undo"),ie.undo)} - ${e("redo",d("toolbar_redo"),ie.redo)} - -
    -
    - - - - -
    -
    - - -
    -
    -
    -
    - -
    -

    ${b(d("web_visuobject_explorer"))}

    - -
    - -
    -
    - - -
    -
    -
    -
    - -
    - -
    -
    - -
    -
    - -
    - -
    - -
    -
    - - - -
    -
    - -
    - -
    - - -
    -
    - - -
    - -
    -
    -

    ${b(d("web_visuobject_inspector"))}

    - -
    -
    -
    -

    ${b(d("web_visuobject_edit_hint"))}
    -
    -
    `,this.canvas=this.shell.querySelector(".vo-canvas:not(.vo-objcanvas):not(.vo-seqcanvas)"),this.shell.querySelector(".vo-seqcanvas").addEventListener("click",p=>{var L,S,k;const v=(L=p.target.closest("[data-ev]"))==null?void 0:L.dataset.ev,x=v!==void 0?(k=(S=this.sequence)==null?void 0:S.events[Number(v)])==null?void 0:k.pos:void 0;x!=null&&x.file&&this.showLine(x.file,x.line)});const t=this.shell.querySelector(".vo-objcanvas");this.objectDiagram=new Ai({canvas:t,lines:t.querySelector(".vo-objlines"),inspector:this.shell.querySelector(".vo-inspector"),classes:()=>this.classes,objects:()=>this.project.objects??[],setObjects:p=>this.setProject({...this.project,objects:p.length?p:void 0}),message:p=>this.message(p)});for(const p of this.shell.querySelectorAll("[data-diagram]"))p.addEventListener("click",()=>this.setDiagramMode(p.dataset.diagram));this.shell.querySelector("[data-obj-add]").addEventListener("click",()=>{const p=this.shell.querySelector("[data-obj-class]").value;p&&this.objectDiagram.add(p)}),this.lines=this.canvas.querySelector(".vo-lines"),this.frames=this.canvas.querySelector(".vo-frames"),this.status=this.shell.querySelector(".vo-status"),this.inspector=this.shell.querySelector(".vo-inspector");const s=this.shell.querySelector(".vo-dock"),i=s.querySelector("[data-inspector=toggle]"),n=(p,v=!0)=>{s.classList.toggle("collapsed",!p),i.textContent=p?"–":"+",i.setAttribute("aria-expanded",String(p));const x=d(p?"web_visuobject_inspector_hide":"web_visuobject_inspector_show");i.setAttribute("aria-label",x),i.dataset.tip=x,J(ls,p?"1":"0"),v&&this.drawLines()};i.addEventListener("click",()=>n(s.classList.contains("collapsed"))),s.querySelector(".vo-dock-bar").addEventListener("dblclick",()=>n(s.classList.contains("collapsed"))),n(se(ls)!=="0",!1);const o=this.shell.querySelector(".vo-hsplit"),r=this.shell.querySelector("[data-pane=diagram]"),c=p=>{if(p===void 0)this.inspector.style.removeProperty("height"),this.inspector.style.removeProperty("max-height"),J(at,"");else{const v=Math.max(120,r.clientHeight-200),x=Math.round(Math.min(v,Math.max(60,p)));this.inspector.style.height=`${x}px`,this.inspector.style.maxHeight="none",J(at,String(x))}this.drawLines()},l=Number(se(at));l>0&&(this.inspector.style.height=`${l}px`,this.inspector.style.maxHeight="none"),o.addEventListener("pointerdown",p=>{if(s.classList.contains("collapsed")||p.button!==0)return;p.preventDefault(),o.setPointerCapture(p.pointerId);const v=p.clientY,x=this.inspector.offsetHeight;o.classList.add("dragging");const L=k=>c(x-(k.clientY-v)),S=()=>{o.classList.remove("dragging"),o.removeEventListener("pointermove",L),o.removeEventListener("pointerup",S),o.removeEventListener("pointercancel",S)};o.addEventListener("pointermove",L),o.addEventListener("pointerup",S),o.addEventListener("pointercancel",S)}),o.addEventListener("dblclick",()=>c(void 0)),o.addEventListener("keydown",p=>{const v=p.key==="ArrowUp"?20:p.key==="ArrowDown"?-20:0;v&&(p.preventDefault(),c(this.inspector.offsetHeight+v))}),this.tree=this.shell.querySelector(".vo-tree"),this.tabs=this.shell.querySelector(".vo-tabs"),this.wsBox=this.shell.querySelector(".vo-ws"),this.wsBox.addEventListener("click",p=>void this.onWorkspaceClick(p)),this.editor=new Bs(this.shell.querySelector(".vo-editor"),{onChange:()=>this.typed(),onCaret:()=>this.caretMoved(),onUndo:p=>this.undo(p),words:()=>({condition:d("canvas_stub_condition"),selector:d("web_live_ph_selector"),code:d("web_live_ph_code"),start:d("inspector_for_init"),step:d("inspector_for_step"),branch:d("element_verzweigung"),branchElse:d("element_verzweigung"),switchCase:d("element_fallauswahl"),forLoop:d("element_forSchleife"),whileLoop:d("element_whileSchleife"),doLoop:d("element_doUntilSchleife"),output:d("inspector_io_mode_output"),input:d("inspector_io_mode_input")}),breakpointLine:p=>p,onBreakpoints:()=>this.saveBreakpoints(),ownTypes:p=>this.ownTypesAt(p)},d("web_visuobject_code"));const h=(p,v)=>this.shell.querySelector(`[data-action=${p}]`).addEventListener("click",v);h("new",()=>void this.newProject({name:d("web_visuobject_project_name"),files:[],packages:[]})),h("example",()=>void this.newProject(Ft(),"tiere/Tier.java")),h("open",()=>this.openFiles()),h("save",()=>void this.save()),h("undo",()=>this.undo(!1)),h("redo",()=>this.undo(!0));const u=this.shell.querySelector("[data-action=new-menu]"),f=this.shell.querySelector(".vo-new-menu"),m=p=>{var v;f.hidden=!p,u.setAttribute("aria-expanded",String(p)),p&&((v=f.querySelector("button"))==null||v.focus())};u.addEventListener("click",()=>m(f.hidden)),f.addEventListener("click",p=>{var x;const v=(x=p.target.closest("[data-new]"))==null?void 0:x.dataset.new;v&&(m(!1),this.newFromWizard(v))}),f.addEventListener("keydown",p=>{var L;const v=[...f.querySelectorAll("button")],x=v.indexOf(document.activeElement);p.key==="Escape"?(m(!1),u.focus()):(p.key==="ArrowDown"||p.key==="ArrowUp")&&(p.preventDefault(),(L=v[(x+(p.key==="ArrowDown"?1:v.length-1))%v.length])==null||L.focus())}),document.addEventListener("pointerdown",p=>{!f.hidden&&!p.target.closest(".vo-new")&&m(!1)}),h("layout",()=>{this.positions.clear(),this.savePositions(),this.render()});const g=this.shell.querySelector("[data-action=settings]"),_=this.shell.querySelector(".settings-menu");g.addEventListener("click",()=>{_.hidden=!_.hidden,g.setAttribute("aria-expanded",String(!_.hidden))}),document.addEventListener("pointerdown",p=>{!_.hidden&&!p.target.closest(".settings")&&(_.hidden=!0)});for(const p of _.querySelectorAll("[data-world]"))p.addEventListener("click",()=>this.chooseWorld(p.dataset.world));const y=_.querySelector("[data-action=code-scopes]");y.checked=Ws(),y.addEventListener("change",()=>zs(y.checked)),this.shell.querySelector("[data-action=run]").addEventListener("click",()=>void this.runProgram()),this.shell.querySelector("[data-action=stop]").addEventListener("click",()=>this.dbg?this.stopDebug("web_visuobject_stopped"):void this.stopProgram()),this.shell.querySelector("[data-action=debug]").addEventListener("click",()=>void this.startDebug());for(const p of this.shell.querySelectorAll("[data-dbg]"))p.addEventListener("click",()=>this.debugCommand(p.dataset.dbg));const w=this.shell.querySelector(".vo-console-input");w.addEventListener("submit",p=>{p.preventDefault();const v=w.querySelector("input");this.debugInput(v.value),v.value=""}),window.addEventListener("keydown",p=>{const v={F5:"into",F6:"over",F7:"out",F8:"continue"}[p.key];!v||!this.dbg||(p.preventDefault(),this.debugCommand(v))});const $=this.shell.querySelector(".vo-console");$.querySelector("[data-console=clear]").addEventListener("click",()=>$.querySelector(".vo-console-lines").replaceChildren()),$.querySelector("[data-console=hide]").addEventListener("click",()=>$.hidden=!0),this.canvas.addEventListener("pointerdown",p=>this.onPointerDown(p)),this.canvas.addEventListener("click",p=>this.onClick(p)),this.canvas.addEventListener("contextmenu",p=>{const v=p.target.closest("[data-end]");v&&(p.preventDefault(),this.relSelection!==v.dataset.rel&&this.selectRelation(v.dataset.rel),this.openEnd(v.dataset.rel,v.dataset.end,p))}),this.tree.addEventListener("click",p=>this.onTreeClick(p)),this.tabs.addEventListener("click",p=>this.onTabClick(p));for(const p of this.shell.querySelectorAll(".vo-tool"))p.addEventListener("pointerdown",v=>this.onToolDown(v,p.dataset.tool));this.tree.addEventListener("keydown",p=>this.onTreeKey(p))}mainClass(){const e=this.classes.filter(s=>s.members.some(i=>i.kind==="method"&&i.name==="main"&&i.isStatic)),t=e.find(s=>s.path===this.active)??e[0];return t&&(t.pkg?`${t.pkg}.${t.name}`:t.name)}async runProgram(){const e=this.shell.querySelector(".vo-console"),t=e.querySelector(".vo-console-lines"),s=e.querySelector(".vo-console-status"),i=e.querySelector(".vo-swing"),n=this.shell.querySelector("[data-action=run]"),o=this.shell.querySelector("[data-action=stop]");e.hidden=!1;const r=(l,h=!1,...u)=>{s.textContent=d(l,...u),s.classList.toggle("error",h)},c=this.mainClass();if(!c)return r("web_visuobject_no_main",!0);this.workspace&&await this.writeCurrent(),await Ye().catch(()=>{}),t.replaceChildren();for(const l of document.querySelectorAll("#console"))l.removeAttribute("id");t.id="console",i.hidden=!0,n.disabled=!0,o.disabled=!1;try{r("web_live_swing_loading"),await Vs(i.querySelector(".livecode-swing-display")),r("web_live_swing_compiling");const l=this.project.files.map(u=>({pkg:X(D(u.path)),name:Q(u.path).replace(/\.java$/i,""),text:u.text})),{result:h}=await Us(l,c,()=>{i.hidden=!1,r("web_visuobject_running_fmt",!1,c)});r({"compile-error":"web_live_swing_compile_failed",windows:"web_live_swing_running",finished:"web_live_run_finished"}[h],h==="compile-error"),i.hidden=h!=="windows",o.disabled=h!=="windows"}catch{r("web_live_swing_failed",!0),o.disabled=!0}n.disabled=!1}async stopProgram(){await Ye().catch(()=>{});const e=this.shell.querySelector(".vo-console");e.querySelector(".vo-swing").hidden=!0;const t=e.querySelector(".vo-console-status");t.textContent=d("web_visuobject_stopped"),t.classList.remove("error"),this.shell.querySelector("[data-action=stop]").disabled=!0}saveBreakpoints(){if(!this.active)return;const e=this.editor.breakpoints;e.size?this.breakpoints.set(this.active,e):this.breakpoints.delete(this.active);const t=new Set(this.project.files.map(s=>s.path));J(ds,JSON.stringify(Object.fromEntries([...this.breakpoints].filter(([s])=>t.has(s)).map(([s,i])=>[s,[...i]]))))}consolePane(){const e=this.shell.querySelector(".vo-console");return{pane:e,lines:e.querySelector(".vo-console-lines"),status:e.querySelector(".vo-console-status"),input:e.querySelector(".vo-console-input")}}debugStatus(e,t=!1){const{status:s}=this.consolePane();s.textContent=e,s.classList.toggle("error",t)}consoleWrite(e,t="out"){const{lines:s}=this.consolePane(),i=s.lastElementChild;if((i==null?void 0:i.dataset.kind)===t)i.textContent+=e;else{const n=document.createElement("span");n.dataset.kind=t,n.className=`vo-out-${t}`,n.textContent=e,s.append(n)}s.scrollTop=s.scrollHeight}async startDebug(){this.dbg&&this.stopDebug();const{pane:e,lines:t,input:s}=this.consolePane();e.hidden=!1,e.querySelector(".vo-swing").hidden=!0,await Ye().catch(()=>{}),this.workspace&&await this.writeCurrent(),t.replaceChildren(),t.removeAttribute("id"),s.hidden=!0;const i=this.mainClass();if(!i)return this.debugStatus(d("web_visuobject_no_main"),!0);let n;try{n=new Ji(this.project.files.map(o=>({path:o.path,source:o.text})),{out:(o,r)=>this.consoleWrite(o,r?"err":"out")}),n.start(i.split(".").pop())}catch(o){if(o instanceof q)return this.debugError(o);throw o}this.dbg={vm:n,state:"running",frames:[],frame:0,places:new Map,values:new Map,vars:new Map},this.shell.querySelector(".vo-dbg-controls").hidden=!1,this.shell.querySelector("[data-action=stop]").disabled=!1,this.shell.querySelector("[data-action=run]").disabled=!0,this.sequence=void 0,this.diagramMode==="class"&&this.setDiagramMode("object"),n.go(this.allBreakpoints().size?"run":"into"),this.debugRun()}allBreakpoints(){const e=new Set;for(const[t,s]of this.breakpoints)for(const i of s)e.add(`${t}:${i}`);return e}debugCommand(e){const t=this.dbg;if(t){if(e==="pause")return t.state!=="running"?void 0:(t.vm.pause(),clearTimeout(t.timer),this.debugRun());t.state==="paused"&&(t.vm.go(e==="continue"?"run":e),this.debugRun())}}debugRun(){const e=this.dbg;if(!e)return;e.state="running",this.showDebugButtons(),this.debugStatus(d("web_visuobject_dbg_running"));const t=this.allBreakpoints(),s=performance.now();for(;;){const i=e.vm.run(t,2e3);if(i.k==="running"){if(performance.now()-s<15)continue;e.timer=window.setTimeout(()=>this.debugRun(),0);return}if(i.k==="sleep"){e.timer=window.setTimeout(()=>this.debugRun(),Math.min(i.ms,1e4));return}if(i.k==="input"){e.state="input",this.showDebugButtons(),this.showDebugState(),this.debugStatus(d("web_visuobject_dbg_waiting"));const{input:n}=this.consolePane();n.hidden=!1,n.querySelector("input").focus();return}if(i.k==="paused"){e.state="paused",e.frame=0,this.showDebugButtons(),this.showDebugState(),this.debugStatus(d("web_visuobject_dbg_paused_fmt",i.pos.file.split("/").pop()??"",String(i.pos.line+1)));return}return i.k==="done"?this.stopDebug("web_live_run_finished"):i.k==="exception"?(this.consoleWrite(`${i.text} -`,"err"),i.pos&&this.showLine(i.pos.file,i.pos.line),this.stopDebug("web_visuobject_dbg_exception",!0)):this.debugError(i.error)}}debugInput(e){const t=this.dbg;!t||t.state!=="input"||(this.consoleWrite(`${e} -`,"in"),this.consolePane().input.hidden=!0,t.vm.input(e),this.debugRun())}debugError(e){var o;const t=e.pos?String(e.pos.line+1):"",s=((o=e.pos)==null?void 0:o.file.split("/").pop())??"",i=e.detail.replace(/^expected:/,""),n={syntax:"web_visuobject_dbg_err_syntax",symbol:"web_visuobject_dbg_err_symbol",unsupported:"web_visuobject_dbg_err_unsupported",nomain:"web_visuobject_no_main",type:"web_visuobject_dbg_err_type",import:"web_visuobject_dbg_err_import",typename:"web_visuobject_dbg_err_typename"}[e.code];e.pos&&this.showLine(e.pos.file,e.pos.line),this.stopDebug(),this.debugStatus(d(n,s,t,i),!0)}showDebugButtons(){const e=this.dbg,t=(e==null?void 0:e.state)==="paused";for(const s of this.shell.querySelectorAll("[data-dbg]"))s.disabled=!e||(s.dataset.dbg==="pause"?e.state!=="running":!t)}showLine(e,t){this.project.files.some(s=>s.path===e)&&e!==this.active&&this.openFile(e),this.editor.setMarks({placeholders:[],problems:[],current:t},!0)}showDebugState(){const e=this.dbg;if(!e)return;e.frames=e.vm.stack();const t=e.vm.heap(),s=e.frames[e.frame];s&&this.showLine(s.pos.file,s.pos.line);const i=e.values.size===0&&e.vars.size===0,n=new Set,o=new Map;for(const h of t)for(const u of h.fields){const f=`dbg${h.id}.${u.name}`;o.set(f,u.value),!i&&e.values.get(f)!==u.value&&n.add(f)}e.values=o;const r=new Set,c=new Map;e.frames.forEach((h,u)=>{for(const f of h.vars){const m=`${e.frames.length-u}|${h.title}|${f.name}`;c.set(m,f.value),!i&&e.vars.get(m)!==f.value&&r.add(`${h.title}|${f.name}`)}}),e.vars=c;const l={objects:this.liveObjects(t),changed:n,frames:e.frames,frame:e.frame,changedVars:r,onFrame:h=>{e.frame=h,l.frame=h;const u=e.frames[h];u&&this.showLine(u.pos.file,u.pos.line),this.objectDiagram.renderInspector()},move:(h,u)=>e.places.set(h,u)};this.objectDiagram.live=l,this.sequence=e.vm.sequence(),this.diagramMode==="object"&&this.objectDiagram.render(),this.diagramMode==="sequence"&&this.renderSequence()}renderSequence(){var n,o,r;const e=this.shell.querySelector(".vo-seqcanvas"),t=e.querySelector(".vo-seq"),s=e.querySelector(".vo-empty"),i=this.sequence;if(s.hidden=!!i&&i.events.length>0,s.textContent=d(i?"web_visuobject_seq_no_calls":"web_visuobject_seq_empty"),!i||!i.events.length)t.innerHTML="",t.setAttribute("width","0");else{const c=((n=this.dbg)==null?void 0:n.state)==="paused"||((o=this.dbg)==null?void 0:o.state)==="input",{svg:l,width:h,height:u}=qi(i,c?i.events.length-1:void 0);t.innerHTML=l+(i.truncated?`${b(d("web_visuobject_seq_truncated"))}`:""),t.setAttribute("width",String(h)),t.setAttribute("height",String(u)),c&&((r=t.querySelector(".vo-seq-msg.current"))==null||r.scrollIntoView({block:"nearest",inline:"nearest"}))}this.renderSequenceInspector()}renderSequenceInspector(){if(this.objectDiagram.live)return this.objectDiagram.renderInspector();this.inspector.innerHTML=`

    ${b(d("web_visuobject_seq_hint"))}

    `}liveObjects(e){const t=this.dbg,s=this.project.objects??[],i=c=>40+c.fields.length*20,n=[],o=[],r=[];for(const c of e){const l=`dbg${c.id}`,h=t.places.get(l)??s.find(u=>u.name===c.name&&u.cls===c.cls);if(!h){r.push(c);continue}t.places.set(l,{x:h.x,y:h.y}),n.push({x:h.x,y:h.y,w:260,h:i(c)})}for(const c of r){const l=i(c);let h={x:24,y:24};e:for(let u=24;u<6e3;u+=30)for(let f=24;f<=24+3*320;f+=320)if(n.every(m=>f+260+30<=m.x||m.x+m.w+30<=f||u+l+30<=m.y||m.y+m.h+30<=u)){h={x:f,y:u};break e}t.places.set(`dbg${c.id}`,h),n.push({...h,w:260,h:l})}for(const c of e){const l=`dbg${c.id}`;o.push({id:l,name:c.name,cls:c.cls,...t.places.get(l),values:Object.fromEntries(c.fields.map(h=>[h.name,h.value]))})}return o}stopDebug(e,t=!1){const s=this.dbg;s&&clearTimeout(s.timer),this.dbg=void 0,s&&(this.sequence=s.vm.sequence()),this.objectDiagram.live=void 0,this.consolePane().input.hidden=!0,this.shell.querySelector(".vo-dbg-controls").hidden=!0,this.shell.querySelector("[data-action=stop]").disabled=!0,this.shell.querySelector("[data-action=run]").disabled=!1,t||this.editor.setMarks({placeholders:[],problems:[]},!1),e&&this.debugStatus(d(e),t),this.diagramMode==="object"&&this.objectDiagram.render(),this.diagramMode==="sequence"&&this.renderSequence()}chooseWorld(e){if(e!=="object"){if(e==="flow"){Js(),location.href=`${location.pathname}?visuflow${location.hash}`;return}Ks(e),location.href=`${location.pathname}?${e==="on"?"visubot":"visustruct"}${location.hash}`}}get activeFile(){return this.project.files.find(e=>e.path===this.active)}showActive(){const e=this.activeFile;this.editor.setText((e==null?void 0:e.text)??""),this.editor.setBreakpoints(e?this.breakpoints.get(e.path)??[]:[]),this.editor.setReadOnly(!e),e&&!this.openTabs.includes(e.path)&&this.openTabs.push(e.path),this.renderTabs(),J(ot,this.active??"")}renderTabs(){if(this.openTabs=this.openTabs.filter(e=>this.project.files.some(t=>t.path===e)),J(cs,JSON.stringify(this.openTabs)),this.openTabs.length===0){this.tabs.innerHTML=`${b(d("web_visuobject_no_file"))}`;return}this.tabs.innerHTML=this.openTabs.map(e=>{const t=e===this.active,s=this.isDirty(e);return`
    - - -
    `}).join("")}onTabClick(e){var i;const t=e.target,s=(i=t.closest("[data-tab]"))==null?void 0:i.dataset.tab;if(s){if(t.closest("[data-close]"))return this.closeTab(s);this.treeSelection=`file:${s}`,this.openFile(s)}}closeTab(e){const t=this.openTabs.indexOf(e);t<0||(this.openTabs.splice(t,1),e===this.active?(this.active=this.openTabs[Math.min(t,this.openTabs.length-1)],this.lastTyping=0,this.showActive(),this.refresh()):this.renderTabs())}snapshot(){return JSON.stringify({project:this.project,active:this.active})}remember(){this.history.push(this.snapshot()),this.history.length>Sn&&this.history.shift(),this.future=[]}setProject(e,t=this.active){var s;this.remember(),this.lastTyping=0,this.project=e,this.active=e.files.some(i=>i.path===t)?t:(s=e.files[0])==null?void 0:s.path,this.showActive(),this.refresh()}openFile(e){if(e===this.active)return;this.active=e,this.lastTyping=0,this.showActive();const t=D(e);this.currentPackage=t,this.open.set(`pkg:${t}`,!0),this.refresh()}typed(){const e=this.activeFile;if(!e)return;const t=Date.now();t-this.lastTyping>1500&&this.remember(),this.lastTyping=t,this.project=$e(this.project,e.path,this.editor.text),this.saveBreakpoints(),this.dbg&&this.stopDebug("web_visuobject_dbg_code_changed"),this.refresh(),clearTimeout(this.renameTimer),this.renameTimer=window.setTimeout(()=>this.syncFileName(),700)}syncFileName(){var c;const e=this.activeFile;if(!e||this.editing)return;const t=Q(e.path).replace(/\.java$/i,""),s=this.classes.filter(l=>l.path===e.path);if(s.length===0||s.some(l=>l.name===t))return;const i=((c=/\bpublic\s+(?:abstract\s+|final\s+)*(?:class|interface|enum)\s+(\w+)/.exec(e.text))==null?void 0:c[1])??s[0].name;if(!pe.test(i)||s.filter(l=>l.name===i).length!==1)return;const n=le(D(e.path),`${i}.java`);if(this.project.files.some(l=>l.path===n))return;let o=Oe(this.project,e.path,n);const r=this.positions.get(t);r&&!this.positions.has(i)&&this.positions.set(i,r);for(const l of Object.keys(o.uml??{}))l.startsWith(`${t}.`)&&(o={...o,uml:ke(o.uml,l,i+l.slice(t.length))});this.openTabs=this.openTabs.map(l=>l===e.path?n:l),this.treeSelection===`file:${e.path}`&&(this.treeSelection=`file:${n}`),this.isOpen(`file:${e.path}`)&&this.open.set(`file:${n}`,!0),this.project=o,this.active=n,J(ot,n),this.savePositions(),this.refresh()}undo(e){const t=e?this.future:this.history,s=e?this.history:this.future,i=t.pop();if(i===void 0)return;s.push(this.snapshot());const n=JSON.parse(i);this.project=n.project,this.active=n.active,this.lastTyping=0,this.showActive(),this.refresh()}refresh(){J(os,Pe(this.project)),this.renderTabs(),this.classes=Ct(this.project),this.selection&&!this.findClass(this.selection.cls)&&(this.selection=void 0),this.relSelection&&!this.findRelation(this.relSelection)&&!this.findDependency(this.relSelection)&&(this.relSelection=void 0),this.editing||(this.render(),this.renderTree())}ownTypesAt(e){const t=this.activeFile;if(!t)return;const s=this.editor.view.state.doc.toString(),i=X(D(t.path)),n=oe(s).map(l=>({...l,pkg:i})),o=new Set(n.map(l=>l.name)),r=n.find(l=>l.headerFrom<=e&&e<=l.bodyEnd),c=ct()==="de"?"de":ct()==="pt-PT"?"pt":"en";return Xi([...n,...this.classes.filter(l=>l.path!==t.path&&!o.has(l.name))],{cls:r==null?void 0:r.name,pkg:i},c)}findClass(e){return this.classes.find(t=>t.name===e)}caretMoved(){var n,o;const e=this.editor.caret,t=this.classes.find(r=>r.path===this.active&&e>=r.from&&e<=r.to),s=t==null?void 0:t.members.findIndex(r=>e>=r.from&&e<=r.to),i=t?{cls:t.name,member:s!==void 0&&s>=0?s:void 0}:void 0;(i==null?void 0:i.cls)===((n=this.selection)==null?void 0:n.cls)&&(i==null?void 0:i.member)===((o=this.selection)==null?void 0:o.member)||(this.selection=i,this.editing||(this.render(),this.renderTree()))}showInCode(e,t){e.path!==this.active&&(this.active=e.path,this.lastTyping=0,this.showActive());const s=this.editor.view,[i,n]=t?[t.headerFrom,t.headerTo]:[e.nameFrom,e.nameTo],o=Math.min(n,s.state.doc.length);s.dispatch({selection:{anchor:Math.min(i,o),head:o},scrollIntoView:!0})}message(e,t=!1){this.status.textContent=e,this.status.classList.toggle("hint",t)}openFiles(){const e=document.createElement("input");e.type="file",e.multiple=!0,e.accept=".zip,.java,.visuobject,application/zip,text/plain",e.addEventListener("change",()=>void this.readFiles([...e.files??[]])),e.click()}async readFiles(e){var t;if(e.length!==0)try{const s=e.find(r=>/\.zip$/i.test(r.name));if(s){const{projectFromZip:r}=await wt(async()=>{const{projectFromZip:l}=await import("./projectZip-DtugKwEc.js");return{projectFromZip:l}},__vite__mapDeps([0,1,2]),import.meta.url),c=r(new Uint8Array(await s.arrayBuffer()),s.name.replace(/\.zip$/i,""));await this.newProject(c);return}const i=e.find(r=>/\.visuobject$/i.test(r.name));if(i){const r=qt(await i.text());if(!r)throw new Error("format");await this.newProject(r);return}const n=At(this.project.name,await Promise.all(e.map(async r=>({name:r.name,text:await r.text()}))));let o=this.project;for(const r of n.files)o=et(o,r.path,r.text);this.setProject(o,(t=n.files[0])==null?void 0:t.path)}catch{this.message(d("web_visuobject_open_failed"))}}async save(){this.workspace?await this.writeCurrent():await this.downloadZip()}async downloadZip(){const{projectToZip:e}=await wt(async()=>{const{projectToZip:s}=await import("./projectZip-DtugKwEc.js");return{projectToZip:s}},__vite__mapDeps([0,1,2]),import.meta.url),t=e(this.project);En(`${this.project.name||"Projekt"}.zip`,t.slice().buffer,"application/zip")}isDirty(e){var t;return!this.workspace||this.saved===void 0?!1:e===void 0?this.saved!==Pe(this.project):this.savedFiles.get(e)!==((t=this.project.files.find(s=>s.path===e))==null?void 0:t.text)}markSaved(){this.saved=Pe(this.project),this.savedFiles=new Map(this.project.files.map(e=>[e.path,e.text]))}async restoreWorkspace(){if(!$t())return;const e=await Xs(us);if(e!=null&&e.ready){const t=await _t(e.handle);if(t)return this.useWorkspace(t.root,!0)}this.pendingFolder=e==null?void 0:e.handle,this.renderWorkspace()}async useWorkspace(e,t=!1){var s;this.workspace=e,this.pendingFolder=void 0,J(rt,"0");try{this.projects=await is(e);const i=se(We),n=this.project;if(t&&i&&this.projects.includes(i)){const o={active:this.active,tabs:[...this.openTabs],positions:new Map(this.positions)};await this.loadProject(i),this.positions=o.positions,this.savePositions(),Pe(n)!==this.saved&&(this.project=n),this.openTabs=o.tabs.filter(r=>this.project.files.some(c=>c.path===r)),this.active=this.project.files.some(r=>r.path===o.active)?o.active:(s=this.project.files[0])==null?void 0:s.path,this.showActive(),this.refresh()}else!this.projects.some(o=>o.toLowerCase()===ge(n.name).toLowerCase())&&n.files.length?(this.folder=ge(n.name),await this.writeCurrent(!1)):this.projects.length?await this.loadProject(this.projects.includes(i??"")?i:this.projects[0]):(this.folder=ge(n.name),await this.writeCurrent(!1))}catch(i){this.message(d("web_visuobject_ws_failed",String((i==null?void 0:i.message)??i)))}this.renderWorkspace(),this.refresh()}async loadProject(e){var i;const{project:t,positions:s}=await $n(this.workspace,e);this.folder=e,this.project=t,this.markSaved(),this.message(""),this.history=[],this.future=[],this.lastTyping=0,this.selection=void 0,this.relSelection=void 0,this.treeSelection=void 0,this.currentPackage=void 0,this.positions=new Map(Object.entries(s)),this.savePositions(),this.openTabs=[],this.active=(i=t.files[0])==null?void 0:i.path,J(We,e),this.showActive(),this.refresh()}async writeCurrent(e=!0){const t=this.workspace;if(!t)return!1;const s=ge(this.project.name),i=this.folder,n=new Set(this.classes.map(r=>r.name)),o=Object.fromEntries([...this.positions].filter(([r])=>n.has(r)));if(s!==i&&this.projects.some(r=>r!==i&&r.toLowerCase()===s.toLowerCase()))return this.message(d("web_visuobject_ws_taken",this.project.name)),!1;try{return await kn(t,s,this.project,o),i&&i!==s&&this.projects.includes(i)&&await t.remove(i),this.folder=s,this.projects=await is(t),J(We,s),this.markSaved(),e&&this.message(d("web_visuobject_ws_saved",t.name,s),!0),this.refresh(),!0}catch(r){return this.message(d("web_visuobject_ws_failed",String((r==null?void 0:r.message)??r))),!1}}async switchProject(e){if(!(!this.workspace||e===this.folder)&&!(this.isDirty()&&!await this.writeCurrent(!1)))try{await this.loadProject(e)}catch(t){this.message(d("web_visuobject_ws_failed",String((t==null?void 0:t.message)??t)))}}async newProject(e,t,s=!1){var n,o;if(!this.workspace){this.setProject(e,t??((n=e.files[0])==null?void 0:n.path)),e.files.length===0&&!s&&this.editProjectName();return}if(this.isDirty()&&!await this.writeCurrent(!1))return;const i=ns(ge(e.name),this.projects);this.project={...e,name:i},this.folder=i,this.history=[],this.future=[],this.openTabs=[],this.positions.clear(),this.savePositions(),this.active=e.files.some(r=>r.path===t)?t:(o=e.files[0])==null?void 0:o.path,this.showActive(),this.refresh(),await this.writeCurrent(!1)&&(this.message(d("web_visuobject_ws_created",i),!0),e.files.length===0&&!s&&this.editProjectName())}get currentPlace(){const e=this.currentPackage??(this.active?D(this.active):ce(this.project)[0]??"");return{root:U(e),pkg:X(e)}}async newFromWizard(e){var u;const t=this.currentPlace,s=(f,m)=>{let g=f;for(let _=2;m(g);_++)g=f+_;return g},i=await rn(e,{roots:ue(this.project),packagesIn:f=>ce(this.project,f).map(X),classes:this.classes,root:t.root,pkg:t.pkg,suggest:f=>f==="project"?ns(d("web_visuobject_project_name"),this.workspace?[...this.projects,this.folder??""]:[]):f==="folder"?s("test",m=>ue(this.project).includes(m)):f==="package"?s(d("web_visuobject_new_package_name"),m=>ce(this.project).includes(re(t.root,m))):this.uniqueClassName(d(f==="class"?"web_visuobject_new_class_name":"web_visuobject_new_interface_name")),check:(f,m,g,_)=>{if(!m)return d("web_visuobject_new_name_missing");if(f==="project")return this.workspace&&this.projects.some(y=>y!==this.folder&&y.toLowerCase()===ge(m).toLowerCase())?d("web_visuobject_ws_taken",m):void 0;if(f==="folder")return!Je(m)||ue(this.project).includes(m)?d("web_visuobject_bad_folder",m):void 0;if(f==="package")return!Mt(m)||ce(this.project).includes(re(g,m))?d("web_visuobject_bad_package",m):void 0;if(!pe.test(m))return d("web_visuobject_bad_name",m);if(this.findClass(m)||this.project.files.some(y=>y.path===le(re(g,_),`${m}.java`)))return d("web_visuobject_new_exists",m);if(m.charAt(0)!==m.charAt(0).toUpperCase())return d("web_visuobject_new_upper")}});if(!i)return;const{name:n,root:o,pkg:r}=i;if(e==="project")return this.newProject({name:n,files:[],packages:[]},void 0,!0);if(this.message(""),e==="folder")return this.currentPackage=re(n,""),this.treeSelection=`root:${n}`,this.open.set(`root:${n}`,!0),this.setProject(gi(this.project,n));if(e==="package"){const f=re(o,n);return this.currentPackage=f,this.treeSelection=`pkg:${f}`,this.open.set(`root:${o}`,!0),this.setProject(bi(this.project,f))}const c=re(o,r),l=le(c,`${n}.java`);this.currentPackage=c,this.selection={cls:n},this.open.set(`root:${o}`,!0),this.open.set(`pkg:${c}`,!0),this.setProject(et(this.project,l,this.sectioned(on(i.options),n)),l);const h=this.findClass(n);h&&this.showInCode(h),(u=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(n)}"]`))==null||u.scrollIntoView({block:"nearest",inline:"nearest"})}editProjectName(){this.treeSelection="project",this.renderTree(),this.startEditing(this.tree.querySelector('[data-node="project"] > .vo-row .vo-label'))}renderWorkspace(){const e=this.wsBox;e.hidden=!$t();const t=this.shell.querySelector("[data-action=save]");if(t.dataset.tip=d(this.workspace?"web_visuobject_save_ws_tip":"web_visuobject_save_tip"),e.hidden)return;const s=(i,n,o="text-button",r)=>``;this.workspace?(e.className="vo-ws",e.innerHTML=`${b(this.workspace.name)} - ${s("zip",d("web_visuobject_ws_zip"),"text-button",d("web_visuobject_ws_zip_tip"))} - ${s("switch",d("web_visuobject_ws_switch"),"text-button")}`):this.pendingFolder?(e.className="vo-ws",e.innerHTML=`${s("reopen",d("web_visuobject_ws_reopen",this.pendingFolder.name),"text-button primary")}${s("switch",d("web_visuobject_ws_switch"),"text-button")}`):se(rt)==="1"?(e.className="vo-ws",e.innerHTML=s("open",d("web_visuobject_ws_choose"),"text-button",d("web_visuobject_ws_hint"))):(e.className="vo-ws ask",e.innerHTML=`

    ${b(d("web_visuobject_ws_question"))}

    -

    ${b(d("web_visuobject_ws_hint"))}

    - ${s("create",d("web_visuobject_ws_create",ss),"text-button primary")} - ${s("open",d("web_visuobject_ws_open"))} - ${s("skip",d("web_visuobject_ws_skip"))}`)}async onWorkspaceClick(e){var s;const t=(s=e.target.closest("[data-ws]"))==null?void 0:s.dataset.ws;if(t){if(t==="zip")return this.downloadZip();if(t==="skip")return J(rt,"1"),this.renderWorkspace();try{if(t==="reopen"&&this.pendingFolder){const n=await _t(this.pendingFolder);n&&await this.useWorkspace(n.root,!0);return}if(this.isDirty()&&!await this.writeCurrent(!1))return;const i=await Ys(t==="create"?ss:void 0,us);J(We,""),await this.useWorkspace(i.root)}catch(i){(i==null?void 0:i.name)!=="AbortError"&&this.message(d("web_visuobject_ws_failed",String((i==null?void 0:i.message)??i)))}}}changeFile(e,t,s){const i=this.project.files.find(n=>n.path===e);i&&(s&&(this.selection=s),this.message(""),this.setProject($e(this.project,e,t(i.text)),e))}uniqueClassName(e){let t=e;for(let s=2;this.findClass(t)||this.project.files.some(i=>Q(i.path)===`${t}.java`);s++)t=e+s;return t}newClass(e,t){const s=this.uniqueClassName(d(e==="class"?"web_visuobject_new_class_name":"web_visuobject_new_interface_name"));t&&this.positions.set(s,{x:Math.max(0,Math.round(t.x)),y:Math.max(0,Math.round(t.y))});const i=this.currentPackage??(this.active?D(this.active):ce(this.project)[0]??""),n=le(i,`${s}.java`);this.selection={cls:s},this.open.set(`pkg:${i}`,!0),this.setProject(et(this.project,n,this.sectioned(_i(i,s,e),s)),n),this.savePositions();const o=this.findClass(s);o&&this.showInCode(o);const r=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(s)}"]`);r==null||r.scrollIntoView({block:"nearest",inline:"nearest"}),this.startEditing((r==null?void 0:r.querySelector(".uml-name"))??null)}renameClassTo(e,t){if(t===e.name)return;if(!pe.test(t)||this.findClass(t))return this.message(d("web_visuobject_bad_name",t));const s=this.project.files.find(r=>r.path===e.path);let i=$e(this.project,e.path,ri(s.text,e,t)),n=e.path;Q(e.path)===`${e.name}.java`&&(n=le(D(e.path),`${t}.java`),i.files.some(r=>r.path===n)?n=e.path:i=Oe(i,e.path,n));const o=this.positions.get(e.name);o&&this.positions.set(t,o);for(const r of Object.keys(i.uml??{}))r.startsWith(`${e.name}.`)&&(i={...i,uml:ke(i.uml,r,t+r.slice(e.name.length))});this.selection={cls:t},this.message(""),this.setProject(i,n)}deleteClassFromProject(e){const t=this.project.files.find(n=>n.path===e.path),s=ai(t.text,e),i=oe(s).length===0;this.selection=void 0,this.message(""),this.setProject(i?Tt(this.project,e.path):$e(this.project,e.path,s))}commit(e,t){var u,f;const s=t.trim(),i=(u=e.closest("[data-node]"))==null?void 0:u.dataset.node;if(i)return this.commitTree(i,s);const n=e.closest(".uml-class"),o=n&&this.findClass(n.dataset.class);if(!o)return;if(e.classList.contains("uml-name"))return this.renameClassTo(o,s);const r=e.dataset.add;if(r){if(!s)return;const m=Ve(s);if(!m)return this.message(d("web_visuobject_bad_line",s));const g=r==="method"&&m.kind==="field"?`${s}()`:s;this.changeFile(o.path,_=>this.sorted(Ee(_,o,g),o.name),{cls:o.name});return}const c=Number(e.dataset.member),l=o.members[c];if(!l)return;if(!s){this.changeFile(o.path,m=>xe(m,l),{cls:o.name});return}if(s===Ge(l))return;const h=Ve(s);if(!h)return this.message(d("web_visuobject_bad_line",s));if(l.kind==="field"&&h.kind==="field"&&h.name!==l.name&&((f=this.project.uml)!=null&&f[V(o.name,l.name)])){const m=this.project.files.find(g=>g.path===o.path);this.selection={cls:o.name,member:c},this.setProject({...$e(this.project,o.path,Qe(m.text,o,l,s)),uml:ke(this.project.uml,V(o.name,l.name),V(o.name,h.name))},o.path);return}this.changeFile(o.path,m=>Qe(m,o,l,s),{cls:o.name,member:c})}commitTree(e,t){const[s,...i]=e.split(":"),n=i.join(":");if(s==="project"){if(!t||t===this.project.name)return;if(this.projects.some(r=>r!==this.folder&&r.toLowerCase()===ge(t).toLowerCase()))return this.message(d("web_visuobject_ws_taken",t));this.setProject({...this.project,name:t})}else if(s==="root"){if(!t||t===n||n===Z)return;if(!Je(t)||ue(this.project).includes(t))return this.message(d("web_visuobject_bad_folder",t));this.currentPackage&&U(this.currentPackage)===n&&(this.currentPackage=re(t,X(this.currentPackage))),this.treeSelection=`root:${t}`;const o=r=>r&&U(r)===n?`${t}:${r.slice(n.length+1)}`:r;this.openTabs=this.openTabs.map(r=>o(r)),this.setProject(yi(this.project,n,t),o(this.active))}else if(s==="pkg"){const o=re(U(n),t);if(!t||o===n)return;if(!Mt(t)||ce(this.project).includes(o))return this.message(d("web_visuobject_bad_package",t));this.currentPackage===n&&(this.currentPackage=o),this.treeSelection=`pkg:${o}`,this.open.set(`pkg:${o}`,!0),this.setProject(vi(this.project,n,o),this.active&&D(this.active)===n?le(o,Q(this.active)):this.active)}else if(s==="file"){const o=t.replace(/\.java$/i,""),r=this.classes.find(l=>l.path===n&&`${l.name}.java`===Q(n));if(r)return this.renameClassTo(r,o);if(!pe.test(o))return this.message(d("web_visuobject_bad_name",o));const c=le(D(n),`${o}.java`);c!==n&&!this.project.files.some(l=>l.path===c)&&this.setProject(Oe(this.project,n,c),c)}else if(s==="class"){const o=this.findClass(n);o&&this.renameClassTo(o,t)}}startEditing(e){if(!e||this.editing)return;const t=e.classList.contains("uml-name")||e.classList.contains("vo-label"),s=document.createElement("input");s.className="uml-input",s.spellcheck=!1,s.autocapitalize="off",s.setAttribute("autocomplete","off"),s.setAttribute("aria-label",d(t?"web_visuobject_name_label":"web_visuobject_member_label")),e.dataset.add&&(s.placeholder=d(e.dataset.add==="field"?"web_visuobject_field_ph":"web_visuobject_method_ph")),s.value=e.dataset.add?"":e.dataset.value??e.textContent??"",s.size=Math.max(s.value.length,s.placeholder.length,8),e.replaceChildren(s),this.editing=!0;let i=!1;const n=o=>{i||(i=!0,this.editing=!1,o&&this.commit(e,s.value),this.render(),this.renderTree())};s.addEventListener("keydown",o=>{o.stopPropagation(),o.key==="Enter"?n(!0):o.key==="Escape"&&n(!1)}),s.addEventListener("click",o=>o.stopPropagation()),s.addEventListener("input",()=>s.size=Math.max(s.value.length,s.placeholder.length,8)),s.addEventListener("blur",()=>n(!0)),s.focus(),s.select()}onKey(e){if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="s"){e.preventDefault(),this.save();return}if(e.key==="Escape"&&this.endPop){this.closeEnd();return}if(e.key==="Escape"&&this.tool!=="select"){this.setTool("select");return}if(this.editing||this.editor.hasFocus)return;const t=e.target;if(this.diagramMode==="object"&&!t.closest("input, select, textarea")&&!(e.metaKey||e.ctrlKey)&&this.objectDiagram.onKey(e)||t.closest("input, select, textarea, .vo-tree"))return;if((e.metaKey||e.ctrlKey)&&e.key.toLowerCase()==="z"){e.preventDefault(),this.undo(e.shiftKey);return}if(!this.canvas.contains(document.activeElement)&&document.activeElement!==document.body)return;if(this.noteSelection&&this.findNote(this.noteSelection)){e.key==="Delete"||e.key==="Backspace"?(e.preventDefault(),this.deleteNote(this.noteSelection)):(e.key==="Enter"||e.key==="F2")&&(e.preventDefault(),this.editNote(this.noteSelection));return}if(this.relSelection&&(e.key==="Delete"||e.key==="Backspace")){e.preventDefault(),this.deleteRelation(this.relSelection);return}const i=this.selection,n=i&&this.findClass(i.cls);if(n){if(e.key==="Delete"||e.key==="Backspace"){e.preventDefault();const o=i.member!==void 0?n.members[i.member]:void 0;o?this.changeFile(n.path,r=>xe(r,o),{cls:n.name}):this.deleteClassFromProject(n)}else if(e.key==="Enter"||e.key==="F2"){e.preventDefault();const o=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(n.name)}"]`);this.startEditing((o==null?void 0:o.querySelector(i.member!==void 0?`[data-member="${i.member}"]`:".uml-name"))??null)}}}isOpen(e){if(e.startsWith("wsproject:"))return!1;const t=this.open.get(e);return t!==void 0?t:e.startsWith("file:")?e===`file:${this.active}`:!0}toggle(e){this.open.set(e,!this.isOpen(e)),J(rs,JSON.stringify(Object.fromEntries(this.open))),this.renderTree()}renderTree(){var f,m,g,_;const e=(m=(f=document.activeElement)==null?void 0:f.closest("[data-node]"))==null?void 0:m.dataset.node,t=this.treeSelection??(this.selection?`class:${this.selection.cls}`:this.active?`file:${this.active}`:void 0),s=(y,w,$,p,v={})=>{const x=v.children!==void 0,L=x&&this.isOpen(y),S=y===t;return`
  • -
    - ${x?``:''} - ${$}${b(p)}${v.extra??""} - ${v.action?``:""} -
    - ${L&&v.children?`
      ${v.children}
    `:""} -
  • `},i=(y,w,$,p)=>{const v={"+":"public","-":"private","#":"protected","~":"package"}[w.visibility],x=w.params.map(k=>k.type).join(", "),L=w.kind==="field"?w.name:`${w.name}(${x})`,S=w.kind==="constructor"?"":`: ${b(w.type)}`;return s(`member:${y.name}:${$}`,p,``,L,{extra:S,title:Ge(w)})},n=(y,w)=>{const $=y.kind==="interface"?"I":y.isAbstract?"A":"C",p=y.members.map((v,x)=>i(y,v,x,w+1)).join("");return s(`class:${y.name}`,w,``,y.name,{children:p})},o=(y,w)=>{const $=this.classes.filter(p=>p.path===y);return s(`file:${y}`,w,'',Q(y),{children:$.map(p=>n(p,w+1)).join(""),action:!0})},r=y=>ce(this.project,y).map(w=>{const $=fi(this.project,w).map(x=>o(x.path,3)).join(""),p=X(w),v=p||d("web_visuobject_default_package");return s(`pkg:${w}`,2,``,v,{children:$,value:p,action:!0})}).join(""),c=ue(this.project).map(y=>s(`root:${y}`,1,'',y,{children:r(y)||`
  • ${b(d("web_visuobject_tree_empty"))}
  • `,action:y!==Z})).join(""),l=s("project",0,'',this.project.name,{children:c,extra:this.isDirty()?` ●`:""}),u=[...(this.workspace?this.projects.filter(y=>y!==this.folder):[]).map(y=>({name:y,html:s(`wsproject:${y}`,0,'',y,{children:"",title:d("web_visuobject_ws_open_project",y)})})),{name:this.folder??this.project.name,html:l}];u.sort((y,w)=>y.name.localeCompare(w.name)),this.tree.innerHTML=`
      ${u.map(y=>y.html).join("")}
    `,this.tree.querySelector('[tabindex="0"]')||(g=this.tree.querySelector(".vo-row"))==null||g.setAttribute("tabindex","0"),e&&((_=this.tree.querySelector(`[data-node="${CSS.escape(e)}"] > .vo-row`))==null||_.focus({preventScroll:!0}))}onTreeClick(e){if(this.editing)return;const t=e.target,s=t.closest("[data-node]"),i=s==null?void 0:s.dataset.node;if(!s||!i)return;if(t.closest("[data-toggle]"))return i.startsWith("wsproject:")?void this.switchProject(i.slice(10)):this.toggle(i);if(t.closest("[data-delete]"))return this.deleteNode(i);const n=Date.now(),o=this.treeClick.node===i&&n-this.treeClick.time<450;if(this.treeClick={node:i,time:o?0:n},o&&!i.startsWith("member:")&&!i.startsWith("wsproject:")&&i!==`root:${Z}`){this.startEditing(this.tree.querySelector(`[data-node="${CSS.escape(i)}"] > .vo-row .vo-label`));return}this.selectNode(i)}selectNode(e){const[t,...s]=e.split(":"),i=s.join(":");if(this.treeSelection=void 0,t==="wsproject")return void this.switchProject(i);if(t==="pkg")this.currentPackage=i,this.treeSelection=e;else if(t==="file")this.currentPackage=D(i),this.treeSelection=e,this.openFile(i);else if(t==="class"||t==="member"){const[n,o]=i.split(":"),r=this.findClass(n);if(!r)return;const c=o!==void 0?r.members[Number(o)]:void 0;this.selection={cls:r.name,member:o!==void 0?Number(o):void 0},this.currentPackage=D(r.path),this.showInCode(r,c),o!==void 0&&(this.treeSelection=e)}else this.treeSelection=e;this.render(),this.renderTree()}deleteNode(e){const[t,...s]=e.split(":"),i=s.join(":");this.treeSelection=void 0,t==="file"?this.setProject(Tt(this.project,i)):t==="root"?(this.currentPackage&&U(this.currentPackage)===i&&(this.currentPackage=void 0),this.setProject(wi(this.project,i))):t==="pkg"&&(this.currentPackage===i&&(this.currentPackage=void 0),this.setProject($i(this.project,i)))}onTreeKey(e){var r,c;if(this.editing)return;const t=[...this.tree.querySelectorAll(".vo-row")],s=(r=document.activeElement)==null?void 0:r.closest(".vo-row"),i=s?t.indexOf(s):-1,n=(c=s==null?void 0:s.closest("[data-node]"))==null?void 0:c.dataset.node,o=l=>{const h=t[Math.max(0,Math.min(t.length-1,l))];for(const u of t)u.tabIndex=-1;h.tabIndex=0,h.focus()};if(e.key==="ArrowDown")o(i+1);else if(e.key==="ArrowUp")o(i-1);else if(n&&e.key==="ArrowRight"&&(s!=null&&s.querySelector("[data-toggle]"))&&!this.isOpen(n))this.toggle(n);else if(n&&e.key==="ArrowLeft"&&(s!=null&&s.querySelector("[data-toggle]"))&&this.isOpen(n))this.toggle(n);else if(n&&e.key==="Enter")this.selectNode(n);else if(n&&e.key==="F2")this.startEditing(s.querySelector(".vo-label"));else if(n&&(e.key==="Delete"||e.key==="Backspace")&&(s!=null&&s.querySelector("[data-delete]")))this.deleteNode(n);else return;e.preventDefault()}onPointerDown(e){this.closeEnd();const t=e.target;if(this.tool!=="select"&&e.button===0&&!t.closest(".vo-dock, .vo-relmenu, input, select, button:not(.uml-connect)"))return this.suppressClick=!0,this.useTool(e);const s=t.closest("[data-note-connect]");if(s&&e.button===0)return this.startNoteConnect(e,s.closest(".uml-note").dataset.note);const i=t.closest(".uml-note");if(i&&e.button===0&&!t.closest("textarea"))return this.dragNote(e,i);const n=t.closest("[data-connect]");if(n&&e.button===0)return this.startConnect(e,n.closest(".uml-class").dataset.class);if(t.closest("input, select, button, label"))return;const o=t.closest(".uml-class");if(!o||e.button!==0)return;const r=this.positions.get(o.dataset.class)??{x:o.offsetLeft,y:o.offsetTop};this.drag={name:o.dataset.class,box:o,start:{x:e.clientX,y:e.clientY},origin:r,moved:!1};const c=h=>{const u=this.drag;if(!u)return;const f=h.clientX-u.start.x,m=h.clientY-u.start.y;if(!u.moved&&Math.hypot(f,m)<4)return;u.moved=!0;const g={x:Math.max(0,Math.round(u.origin.x+f)),y:Math.max(0,Math.round(u.origin.y+m))};this.positions.set(u.name,g),u.box.style.left=`${g.x}px`,u.box.style.top=`${g.y}px`,this.drawLines()},l=()=>{var h;window.removeEventListener("pointermove",c),window.removeEventListener("pointerup",l),(h=this.drag)!=null&&h.moved&&this.savePositions(),setTimeout(()=>this.drag=void 0,0)};window.addEventListener("pointermove",c),window.addEventListener("pointerup",l)}onClick(e){var _,y;if(this.suppressClick){this.suppressClick=!1;return}if((_=this.drag)!=null&&_.moved||this.editing)return;const t=e.target;if(t.closest("input, select, label, textarea"))return;const s=t.closest(".uml-note");if(s){const w=s.dataset.note,$=Date.now(),p=this.lastClick.spot===`note|${w}`&&$-this.lastClick.time<450;return this.lastClick={spot:`note|${w}`,time:p?0:$},p?this.editNote(w):this.selectNote(w)}this.noteSelection=void 0;const i=t.closest("[data-class-action]"),n=t.closest(".uml-class"),o=n&&this.findClass(n.dataset.class);if(i&&o){i.dataset.classAction==="delete"&&this.deleteClassFromProject(o);return}const r=t.closest("[data-add]");if(r){this.startEditing(r);return}const c=(y=t.closest("[data-rel]"))==null?void 0:y.dataset.rel,l=t.closest("[data-end]");if(c&&!o&&l&&this.relSelection===c){this.openEnd(c,l.dataset.end,e,l.dataset.part);return}if(c&&!o){this.selectRelation(c);return}if(!o){this.relSelection=void 0,this.selection=void 0,this.render(),this.renderTree();return}const h=t.closest("[data-member]"),u=h?Number(h.dataset.member):void 0,f=`${o.name}|${u??(t.closest(".uml-name")?"name":"")}`,m=Date.now(),g=f===this.lastClick.spot&&m-this.lastClick.time<450&&!f.endsWith("|");if(this.lastClick={spot:f,time:g?0:m},g){const w=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(o.name)}"]`);this.startEditing((w==null?void 0:w.querySelector(u!==void 0?`[data-member="${u}"]`:".uml-name"))??null);return}this.selection={cls:o.name,member:u},this.relSelection=void 0,this.treeSelection=void 0,this.currentPackage=D(o.path),this.showInCode(o,u!==void 0?o.members[u]:void 0),this.render(),this.renderTree(),this.canvas.focus({preventScroll:!0})}setDiagramMode(e){this.diagramMode=e,J(hs,e),this.setTool("select"),this.render()}showDiagramMode(){const e=this.diagramMode==="object",t=this.diagramMode==="class";for(const s of this.shell.querySelectorAll("[data-diagram]"))s.setAttribute("aria-selected",String(s.dataset.diagram===this.diagramMode));if(this.shell.querySelector(".vo-palette:not(.vo-obj-palette)").hidden=!t,this.shell.querySelector(".vo-obj-palette").hidden=!e,this.canvas.hidden=!t,this.shell.querySelector(".vo-objcanvas").hidden=!e,this.shell.querySelector(".vo-seqcanvas").hidden=this.diagramMode!=="sequence",this.shell.querySelector("[data-action=layout]").hidden=!t,e){const s=this.shell.querySelector("[data-obj-class]"),i=[...new Set(this.classes.filter(o=>o.kind==="class"&&!o.isAbstract).map(o=>o.name))],n=i.includes(s.value)?s.value:i[0];s.innerHTML=i.map(o=>`${b(o)}`).join(""),this.shell.querySelector("[data-obj-add]").disabled=i.length===0}}savePositions(){const e=new Set(this.classes.map(t=>t.name));J(as,JSON.stringify(Object.fromEntries([...this.positions].filter(([t])=>e.has(t)))))}render(){var t;if(this.showDiagramMode(),this.diagramMode==="object")return this.objectDiagram.render();if(this.diagramMode==="sequence")return this.renderSequence();for(const s of this.canvas.querySelectorAll(".uml-class, .uml-note, .vo-empty"))s.remove();for(const s of this.project.notes??[])this.canvas.append(this.noteBox(s));if(this.classes.length===0){const s=document.createElement("p");s.className="vo-empty",s.textContent=d("web_visuobject_empty"),(t=this.project.notes)!=null&&t.length||this.canvas.append(s),this.drawLines();return}const e=new Map;for(const s of this.classes){if(e.has(s.name))continue;const i=this.classBox(s);e.set(s.name,i),this.canvas.append(i)}this.placeNew(e),this.drawLines()}noteBox(e){const t=document.createElement("div"),s=this.noteSelection===e.id;return t.className=`uml-note${s?" selected":""}`,t.dataset.note=e.id,t.style.left=`${e.x}px`,t.style.top=`${e.y}px`,t.innerHTML=`${s?``:""}
    ${b(e.text||d("web_visuobject_note_ph"))}
    `,t}findNote(e){var t;return e?(t=this.project.notes)==null?void 0:t.find(s=>s.id===e):void 0}setNotes(e){this.setProject({...this.project,notes:e.length?e:void 0})}changeNote(e,t){this.setNotes((this.project.notes??[]).map(s=>s.id===e?{...s,...t}:s))}addNote(e){const t=`n${Date.now().toString(36)}`;this.noteSelection=t,this.selection=void 0,this.relSelection=void 0,this.setNotes([...this.project.notes??[],{id:t,text:"",x:Math.max(0,Math.round(e.x)),y:Math.max(0,Math.round(e.y))}]),this.editNote(t)}deleteNote(e){this.noteSelection=void 0,this.setNotes((this.project.notes??[]).filter(t=>t.id!==e))}selectNote(e){this.noteSelection=e,this.selection=void 0,this.relSelection=void 0,this.treeSelection=void 0,this.render(),this.renderTree(),this.canvas.focus({preventScroll:!0})}editNote(e){const t=this.findNote(e),s=this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(e)}"]`);if(!t||!s)return;const i=document.createElement("textarea");i.className="uml-note-edit",i.value=t.text,i.placeholder=d("web_visuobject_note_ph"),i.rows=Math.max(3,t.text.split(` -`).length+1),s.querySelector(".uml-note-text").replaceWith(i),this.editing=!0;let n=!1;const o=r=>{n||(n=!0,this.editing=!1,r&&i.value!==t.text?this.changeNote(e,{text:i.value}):this.render())};i.addEventListener("keydown",r=>{r.stopPropagation(),r.key==="Escape"?o(!1):r.key==="Enter"&&(r.metaKey||r.ctrlKey)&&o(!0)}),i.addEventListener("blur",()=>o(!0)),i.focus()}startNoteConnect(e,t){e.preventDefault();const s=this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(t)}"]`),i=document.createElementNS("http://www.w3.org/2000/svg","line");i.setAttribute("class","vo-rel vo-note-link ghost"),this.lines.append(i);const n={x:s.offsetLeft+s.offsetWidth,y:s.offsetTop+s.offsetHeight/2},o=l=>{var h;return((h=document.elementFromPoint(l.clientX,l.clientY))==null?void 0:h.closest(".uml-class"))??void 0},r=l=>{var u;const h=this.canvas.getBoundingClientRect();i.setAttribute("x1",String(n.x)),i.setAttribute("y1",String(n.y)),i.setAttribute("x2",String(l.clientX-h.left+this.canvas.scrollLeft)),i.setAttribute("y2",String(l.clientY-h.top+this.canvas.scrollTop));for(const f of this.canvas.querySelectorAll(".uml-class.drop"))f.classList.remove("drop");(u=o(l))==null||u.classList.add("drop")},c=l=>{var m;window.removeEventListener("pointermove",r),window.removeEventListener("pointerup",c),i.remove();const h=o(l);h==null||h.classList.remove("drop"),this.suppressClick=!0;const u=this.findNote(t),f=h==null?void 0:h.dataset.class;u&&f&&!((m=u.attach)!=null&&m.includes(f))&&this.changeNote(t,{attach:[...u.attach??[],f]})};window.addEventListener("pointermove",r),window.addEventListener("pointerup",c)}dragNote(e,t){const s=this.findNote(t.dataset.note);if(!s)return;const i={x:e.clientX,y:e.clientY};let n=!1,o={x:s.x,y:s.y};const r=l=>{const h=l.clientX-i.x,u=l.clientY-i.y;!n&&Math.hypot(h,u)<4||(n=!0,o={x:Math.max(0,Math.round(s.x+h)),y:Math.max(0,Math.round(s.y+u))},t.style.left=`${o.x}px`,t.style.top=`${o.y}px`,this.drawLines())},c=()=>{window.removeEventListener("pointermove",r),window.removeEventListener("pointerup",c),n&&(this.suppressClick=!0,this.changeNote(s.id,o))};window.addEventListener("pointermove",r),window.addEventListener("pointerup",c)}noteLinksSVG(e){const t=[];for(const s of this.project.notes??[]){const i=this.canvas.querySelector(`.uml-note[data-note="${CSS.escape(s.id)}"]`);if(i)for(const n of s.attach??[]){const o=e.get(n);if(!o)continue;const r={x:i.offsetLeft,y:i.offsetTop,w:i.offsetWidth,h:i.offsetHeight},c={x:o.offsetLeft,y:o.offsetTop,w:o.offsetWidth,h:o.offsetHeight},l={x:r.x+r.w/2,y:r.y+r.h/2},h={x:c.x+c.w/2,y:c.y+c.h/2},u=ze(r,l,h),f=ze(c,h,l);t.push(``)}}return t.join("")}renderNoteInspector(e){this.inspector.dataset.rel=`note:${e.id}`;const t=[...new Set(this.classes.map(s=>s.name))];this.inspector.innerHTML=` -

    ${b(d("web_visuobject_tool_note"))}

    - - ${t.length?`
    ${b(d("web_visuobject_note_attached"))}${t.map(s=>{var i;return``}).join("")}
    `:""} -
    `,this.inspector.querySelector("[data-field=note-text]").addEventListener("change",s=>this.changeNote(e.id,{text:s.target.value}));for(const s of this.inspector.querySelectorAll("[data-field=note-attach]"))s.addEventListener("change",()=>{const i=[...this.inspector.querySelectorAll("[data-field=note-attach]")].filter(n=>n.checked).map(n=>n.value);this.changeNote(e.id,{attach:i.length?i:void 0})});this.inspector.querySelector("[data-inspector=delete-note]").addEventListener("click",()=>this.deleteNote(e.id))}classBox(e){var l;const t=((l=this.selection)==null?void 0:l.cls)===e.name?this.selection:void 0,s=document.createElement("div");s.className=`uml-class${t?" selected":""}${e.kind==="interface"?" interface":""}${e.isAbstract?" abstract":""}`,s.dataset.class=e.name;const i=this.positions.get(e.name);i&&(s.style.left=`${i.x}px`,s.style.top=`${i.y}px`);const n=(h,u)=>{const f=Ge(h);return`
  • ${b(f)}
  • `},o=e.members.map((h,u)=>h.kind==="field"?n(h,u):"").join(""),r=e.members.map((h,u)=>h.kind!=="field"?n(h,u):"").join(""),c=h=>t?`
  • ${b(d(h==="field"?"web_visuobject_add_field":"web_visuobject_add_method"))}
  • `:"";return s.innerHTML=` - ${t&&t.member===void 0?``:""} -
    ${e.kind==="interface"?"«interface»":e.isAbstract?"{abstract}":""}${b(e.name)}
    - ${e.kind==="interface"&&!o?"":`
      ${o}${c("field")}
    `} -
      ${r}${c("method")}
    `,s}placeNew(e){var f;if(this.classes.every(m=>this.positions.has(m.name)))return;const t=m=>[m.superclass,...m.interfaces].filter(g=>!!g&&e.has(g)),s=new Map,i=(m,g)=>{if(s.has(m.name))return s.get(m.name);if(g.has(m.name))return 0;g.add(m.name);const _=t(m).map(w=>this.findClass(w)).filter(w=>w.pkg===m.pkg).map(w=>i(w,g)),y=_.length?Math.max(..._)+1:0;return s.set(m.name,y),y};for(const m of this.classes)i(m,new Set);const n=48,o=Math.max(this.canvas.clientWidth,400)-24,r=(m,g,_)=>m.x+m.w+_<=g.x||g.x+g.w+_<=m.x||m.y+m.h+_<=g.y||g.y+g.h+_<=m.y,c=m=>({w:e.get(m.name).offsetWidth,h:e.get(m.name).offsetHeight}),l=(m,g,_)=>{this.positions.set(m.name,{x:g,y:_});const y=e.get(m.name);y.style.left=`${g}px`,y.style.top=`${_}px`},h=m=>{const g=this.classes.filter(w=>w.pkg===m&&e.has(w.name)&&this.positions.has(w.name)).map(w=>({...this.positions.get(w.name),...c(w)}));if(g.length===0)return;const _=Math.min(...g.map(w=>w.x))-16,y=Math.min(...g.map(w=>w.y))-40;return{x:_,y,w:Math.max(...g.map(w=>w.x+w.w))+16-_,h:Math.max(...g.map(w=>w.y+w.h))+16-y}},u=this.diagramPackages().filter(m=>this.classes.some(g=>g.pkg===m&&e.has(g.name)));for(const m of u){const g=this.classes.filter(j=>j.pkg===m&&e.has(j.name)&&!this.positions.has(j.name));if(g.length===0)continue;const _=u.filter(j=>j!==m).map(h).filter(j=>!!j),y=h(m);if(y){for(const j of g){const{w:M,h:A}=c(j),R=this.classes.filter(F=>F.pkg===m&&this.positions.has(F.name)&&e.has(F.name)).map(F=>({...this.positions.get(F.name),...c(F)}));let C;for(let F=y.y+40;!C&&Fr(G,he,n))&&_.every(he=>r(z,he,24))){C={x:W,y:F};break}}C??(C={x:y.x+16,y:y.y+y.h+n}),l(j,C.x,C.y)}continue}const w=[];for(const j of g)(w[f=s.get(j.name)]??(w[f]=[])).push(j);const $=new Map;let p=0,v=0;for(const j of w){if(!j)continue;let M=0,A=0;for(const R of j){const{w:C,h:F}=c(R);$.set(R.name,{x:M,y:p}),M+=C+n*3,A=Math.max(A,F)}v=Math.max(v,M-n*3),p+=A+n*1.5}const x=p-n*1.5,L=v+32,S=x+56;let k;for(let j=4;!k&&j<2e4;j+=20)for(let M=16;M===16||M+L<=o;M+=20)if(_.every(A=>r({x:M,y:j,w:L,h:S},A,32))){k={x:M,y:j};break}k??(k={x:16,y:4});for(const j of g)l(j,k.x+16+$.get(j.name).x,k.y+40+$.get(j.name).y)}this.savePositions()}drawLines(){const e=new Map;for(const l of this.canvas.querySelectorAll(".uml-class"))e.set(l.dataset.class,l);let t=0,s=0;for(const l of e.values())t=Math.max(t,l.offsetLeft+l.offsetWidth+40),s=Math.max(s,l.offsetTop+l.offsetHeight+40);for(const l of[this.lines,this.frames])l.setAttribute("width",String(t)),l.setAttribute("height",String(s));this.frames.innerHTML=this.packageFrames(e);const i=[` - - - `],n=Pt(this.classes,this.project.uml),o=Ot(this.classes),r=new Map,c=(l,h)=>{const u=[l,h].sort().join("|"),f=r.get(u)??0;return r.set(u,f+1),f*16};for(const l of o){const h=e.get(l.from),u=e.get(l.to);h&&u&&h!==u&&i.push(this.generalizationSVG(l,h,u,c(l.from,l.to)))}for(const l of this.shownAssociations(n)){const h=e.get(l.from),u=e.get(l.to);h&&u&&h!==u&&i.push(this.associationSVG(l,h,u,c(l.from,l.to)))}for(const l of this.dependencies()){const h=e.get(l.from),u=e.get(l.to);h&&u&&h!==u&&i.push(this.dependencySVG(l,h,u,c(l.from,l.to)))}i.push(this.noteLinksSVG(e)),this.lines.innerHTML=i.join(""),this.renderInspector()}diagramPackages(){return[...new Set(ce(this.project).map(X))]}packageFrames(e){const t=[];for(const s of this.diagramPackages()){if(!s)continue;const i=this.classes.filter(u=>u.pkg===s).map(u=>e.get(u.name)).filter(u=>!!u);if(i.length===0)continue;const n=Math.min(...i.map(u=>u.offsetLeft))-16,o=Math.min(...i.map(u=>u.offsetTop))-16,r=Math.max(...i.map(u=>u.offsetLeft+u.offsetWidth))+16,c=Math.max(...i.map(u=>u.offsetTop+u.offsetHeight))+16,l=Math.max(70,s.length*7.6+20),h=this.currentPackage!==void 0&&X(this.currentPackage)===s?" current":"";t.push(` - - - ${b(s)} - `)}return t.join("")}segment(e,t,s){const i=_=>({x:_.offsetLeft,y:_.offsetTop,w:_.offsetWidth,h:_.offsetHeight}),n=i(e),o=i(t),r={x:n.x+n.w/2,y:n.y+n.h/2},c={x:o.x+o.w/2,y:o.y+o.h/2},l=Math.hypot(c.x-r.x,c.y-r.y)||1,h=-(c.y-r.y)/l*s,u=(c.x-r.x)/l*s,f=ze(n,{x:r.x+h,y:r.y+u},{x:c.x+h,y:c.y+u}),m=ze(o,{x:c.x+h,y:c.y+u},{x:r.x+h,y:r.y+u}),g=Math.hypot(m.x-f.x,m.y-f.y)||1;return{from:f,to:m,length:g,u:{x:(m.x-f.x)/g,y:(m.y-f.y)/g},n:{x:-(m.y-f.y)/g,y:(m.x-f.x)/g}}}hitLine(e,t,s,i){return`${b(i)}`}dependencies(){return ki(this.classes,e=>{var t;return(t=this.project.files.find(s=>s.path===e.path))==null?void 0:t.text})}findDependency(e){return this.dependencies().find(t=>`dep:${t.from}:${t.to}`===e)}dependencySVG(e,t,s,i){const{from:n,to:o,n:r}=this.segment(t,s,i),c=`dep:${e.from}:${e.to}`,l=this.relSelection===c?" selected":"",h={x:(n.x+o.x)/2+r.x*12,y:(n.y+o.y)/2+r.y*12},u=Math.abs(r.x)<.35?"middle":r.x>0?"start":"end",f=d(`web_visuobject_dep_${e.kind}_text`,e.from,e.to,e.methods.join(", "));return` - - «${e.kind}» - ${this.hitLine(c,n,o,f)} - `}generalizationSVG(e,t,s,i){const{from:n,to:o,u:r}=this.segment(t,s,i),c={x:o.x-r.x*12,y:o.y-r.y*12},l=`gen:${e.kind}:${e.from}:${e.to}`,h=this.relSelection===l?" selected":"",u=d(e.kind==="implements"?"web_visuobject_rel_implements_text":"web_visuobject_rel_extends_text",e.from,e.to);return` - - ${this.hitLine(l,n,o,u)} - `}shownAssociations(e){const t=new Map;for(const s of e){const i=[s.from,s.to].sort().join("|");t.set(i,[...t.get(i)??[],s])}return[...t.values()].map(s=>{const i=l=>(l.name?2:0)+(l.kind!=="association"||l.navTo!=="navigable"||l.navFrom!==(l.back?"navigable":"unspecified")?1:0),n=[...s].sort((l,h)=>i(h)-i(l))[0],o=l=>s.filter(h=>h.from===l).length+s.filter(h=>h.back&&h.to===l).length,r=o(n.from)>1||o(n.to)>1,c=s.some(l=>l.from===n.to);return{...n,navFrom:c&&n.navFrom==="unspecified"?"navigable":n.navFrom,several:r}})}associationSVG(e,t,s,i){const{from:n,to:o,length:r,u:c,n:l}=this.segment(t,s,i),h=`assoc:${e.key}`,u=this.relSelection===h?" selected":"",f=e.kind!=="association",m=f?{x:n.x+c.x*20,y:n.y+c.y*20}:n;let g=``;const _=(p,v)=>{const x={x:p.x+v.x*12,y:p.y+v.y*12},L={x:(v.x+l.x)*5,y:(v.y+l.y)*5},S={x:(v.x-l.x)*5,y:(v.y-l.y)*5};return``};if(e.navTo==="non-navigable"&&(g+=_(o,{x:-c.x,y:-c.y})),e.navFrom==="non-navigable"&&(g+=_(m,c)),f){const p=(v,x)=>`${n.x+c.x*v+l.x*x},${n.y+c.y*v+l.y*x}`;g+=``}const y=(p,v,x,L,S,k)=>{const j=Math.abs(l.x)<.35,M=j?{x:p.x+v.x*8,y:p.y+v.y*8}:{x:p.x+v.x*x,y:p.y+v.y*x},A=(F,W,G)=>{const z=!W;if(z&&(!u||G==="role"))return"";const he=M.x+l.x*F,As=M.y+l.y*F,qs=j?v.x>0?"start":"end":l.x*F>0?"start":"end",Fs=z?d(G==="role"?"web_visuobject_rel_role":"web_visuobject_end_mult_ph"):W;return`${b(Fs)}`},R={x:p.x+v.x*16,y:p.y+v.y*16};return`${b(d("web_visuobject_end_tip"))}`+A(12,S,"mult")+A(-12,k,"role")},w=Math.min(30,r/3);if(g+=y(o,{x:-c.x,y:-c.y},w,"b",e.several?void 0:e.multiplicity),g+=y(n,c,f?Math.min(42,r/3):w,"a",e.several?void 0:e.sourceMultiplicity),e.name){const p=e.reading==="backward"?{x:-c.x,y:-c.y}:c,v=e.reading?Math.abs(p.x)>=Math.abs(p.y)?p.x>0?" ▶":"◀ ":p.y>0?" ▼":" ▲":"",x=v==="◀ "?`◀ ${e.name}`:`${e.name}${v}`,L=this.freeMiddle(n,o,[e.from,e.to]),S={x:L.x+l.x*14,y:L.y+l.y*14},k=Math.abs(l.x)<.35?"middle":l.x>0?"start":"end";g+=`${b(x)}`}const $=d(`web_visuobject_rel_${e.kind}`)+`: ${e.from} – ${e.to}`;return`${g}${this.hitLine(h,n,o,$)}`}freeMiddle(e,t,s){const i=[...this.canvas.querySelectorAll(".uml-class")].filter(h=>!s.includes(h.dataset.class)).map(h=>({x:h.offsetLeft-8,y:h.offsetTop-8,w:h.offsetWidth+16,h:h.offsetHeight+16})),n=Math.hypot(t.x-e.x,t.y-e.y);if(n>120){const h=48/n;[e,t]=[{x:e.x+(t.x-e.x)*h,y:e.y+(t.y-e.y)*h},{x:t.x-(t.x-e.x)*h,y:t.y-(t.y-e.y)*h}]}const o=40;let r={start:0,length:0},c=-1;for(let h=0;h<=o;h++){const u=e.x+(t.x-e.x)*h/o,f=e.y+(t.y-e.y)*h/o,m=i.some(g=>u>=g.x&&u<=g.x+g.w&&f>=g.y&&f<=g.y+g.h);if(!m&&c<0&&(c=h),(m||h===o)&&c>=0){const g=m?h-1:h;g-c>r.length&&(r={start:c,length:g-c}),c=-1}}const l=(r.start+r.length/2)/o;return{x:e.x+(t.x-e.x)*l,y:e.y+(t.y-e.y)*l}}findRelation(e){return e.startsWith("assoc:")?Pt(this.classes,this.project.uml).find(t=>{var s;return`assoc:${t.key}`===e||`assoc:${(s=t.back)==null?void 0:s.key}`===e}):Ot(this.classes).find(t=>`gen:${t.kind}:${t.from}:${t.to}`===e)}selectRelation(e){this.noteSelection=void 0,this.relSelection=e,this.selection=void 0,this.treeSelection=void 0,this.message(""),this.render(),this.renderTree(),this.canvas.focus({preventScroll:!0})}editClass(e,t,s){for(const i of e.files){const n=oe(i.text).find(o=>o.name===t);if(n)return $e(e,i.path,this.sorted(s(i.text,n),t))}return e}sectionLabels(){return{fields:d("web_visuobject_section_fields"),refs:d("web_visuobject_section_refs"),ctors:d("web_visuobject_section_ctors"),methods:d("web_visuobject_section_methods")}}sorted(e,t){return Qi(e,t,new Set(this.classes.map(s=>s.name)),this.sectionLabels())}sectioned(e,t){const s=oe(e).find(i=>i.name===t);return s?pt(e,s,new Set([...this.classes.map(i=>i.name),t]),this.sectionLabels()):e}fieldOf(e,t){return e.members.find(s=>s.kind==="field"&&s.name===t)}importFor(e,t,s){const i=this.findClass(t),n=this.findClass(s);return!i||!n||i.pkg===n.pkg||!n.pkg?e:this.editClass(e,t,o=>tt(o,`${n.pkg}.${n.name}`))}addAssociationField(e,t,s,i,n){const o=Ct(e).find(u=>u.name===t),r=n&&pe.test(n)&&!o.members.some(u=>u.name===n)?n:ji(o,s,i),{type:c,init:l}=It(s,i);let h=this.editClass(e,t,(u,f)=>Ee(u,f,`- ${r}: ${c}`));return l&&(h=this.editClass(h,t,(u,f)=>{const m=this.fieldOf(f,r);return m?Lt(u,m,c,l):u}),h=this.editClass(h,t,u=>tt(u,"java.util.ArrayList"))),{project:this.importFor(h,t,s),field:r}}setUml(e,t,s){return{...e,uml:Si(e.uml??{},t,s)}}createRelation(e,t,s){const i=this.findClass(e),n=this.findClass(t);if(!i||!n)return;let o=this.project,r;if(s==="extends"&&i.kind==="class")o=this.importFor(this.editClass(o,e,(c,l)=>Ze(c,l,t)),e,t),r=`gen:extends:${e}:${t}`;else if(s==="extends"||s==="implements")o=this.importFor(this.editClass(o,e,(c,l)=>jt(c,l,t)),e,t),r=`gen:${i.kind==="interface"?"extends":"implements"}:${e}:${t}`;else{const c=this.addAssociationField(o,e,t,!1),l=V(e,c.field);o=s==="association"?c.project:s==="link"?this.setUml(c.project,l,{targetEnd:"unspecified"}):this.setUml(c.project,l,{kind:s}),r=`assoc:${V(e,c.field)}`}this.relSelection=r,this.selection=void 0,this.message(""),this.setProject(o,i.path),this.canvas.focus({preventScroll:!0})}deleteRelation(e){var i;const t=this.findRelation(e);if(!t)return;let s=this.project;if("key"in t){const n=(r,c,l)=>this.editClass(r,c,(h,u)=>{const f=this.fieldOf(u,l);return f?xe(h,f):h});s=n(s,t.from,t.role),t.back&&(s=n(s,t.to,t.back.field.name));const o={...s.uml??{}};delete o[t.key],t.back&&delete o[t.back.key],s={...s,uml:o}}else t.kind==="extends"&&((i=this.findClass(t.from))==null?void 0:i.kind)==="class"?s=this.editClass(s,t.from,(n,o)=>Ze(n,o,void 0)):s=this.editClass(s,t.from,(n,o)=>St(n,o,t.to));this.relSelection=void 0,this.setProject(s)}changeAssoc(e,t,s){let i=this.project,n=e.key;const o=(l,h,u)=>{if(!pe.test(u))return this.message(d("web_visuobject_bad_name",u)),!1;const f=this.findClass(l);return f&&f.members.some(m=>m.name===u)?(this.message(d("web_visuobject_bad_name",u)),!1):(i=this.editClass(i,l,(m,g)=>{const _=this.fieldOf(g,h);return _?Qe(m,g,_,`${_.visibility} ${u}: ${_.type}`):m}),i={...i,uml:ke(i.uml??{},V(l,h),V(l,u))},!0)},r=(l,h,u,f,m)=>{const g=Me(f);i=this.editClass(i,l,(y,w)=>{const $=this.fieldOf(w,h);if(!$||Me(e.key===m?e.multiplicity:e.sourceMultiplicity??"1")===g)return y;const{type:v,init:x}=It(u,g);return Lt(y,$,v,x)}),g&&(i=this.editClass(i,l,y=>tt(y,"java.util.ArrayList")));const _=g?"0..*":"1";i=this.setUml(i,m,{multiplicity:f===_?void 0:f})};switch(t){case"kind":i=this.setUml(i,n,{kind:s});break;case"name":i=this.setUml(i,n,{name:s.trim()||void 0});break;case"reading":i=this.setUml(i,n,{reading:e.reading==="backward"?void 0:"backward"});break;case"targetEnd":i=this.setUml(i,n,{targetEnd:s,navigable:void 0});break;case"sourceEnd":{if(s==="navigable"&&!e.back){const l=this.addAssociationField(i,e.to,e.from,Me(e.sourceMultiplicity??"1"),e.sourceRole);i=this.setUml(l.project,n,{sourceEnd:void 0,sourceRole:void 0,sourceMultiplicity:void 0}),e.sourceMultiplicity&&e.sourceMultiplicity!==(Me(e.sourceMultiplicity)?"0..*":"1")&&(i=this.setUml(i,V(e.to,l.field),{multiplicity:e.sourceMultiplicity}))}else if(s!=="navigable"){if(e.back){const l=e.back;i=this.editClass(i,e.to,(u,f)=>{const m=this.fieldOf(f,l.field.name);return m?xe(u,m):u});const h={...i.uml??{}};delete h[l.key],i={...i,uml:h},i=this.setUml(i,n,{sourceRole:e.sourceRole,sourceMultiplicity:e.sourceMultiplicity})}i=this.setUml(i,n,{sourceEnd:s})}break}case"role":if(s===e.role||!o(e.from,e.role,s))return;n=V(e.from,s);break;case"multiplicity":if(!s.trim())return;r(e.from,e.role,e.to,s.trim(),n);break;case"sourceRole":if(e.back){if(s===e.back.field.name||!o(e.to,e.back.field.name,s))return}else i=this.setUml(i,n,{sourceRole:s.trim()||void 0});break;case"sourceMultiplicity":if(!s.trim())return;e.back?r(e.to,e.back.field.name,e.from,s.trim(),e.back.key):i=this.setUml(i,n,{sourceMultiplicity:s.trim()});break}this.relSelection=`assoc:${n}`,this.message(""),this.setProject(i);const c=this.findRelation(this.relSelection);c&&"key"in c&&`assoc:${c.key}`!==this.relSelection&&(this.relSelection=`assoc:${c.key}`,this.render())}openEnd(e,t,s,i){this.closeEnd();const n=this.findRelation(e);if(!n||!("key"in n))return;const o=t==="a",r=o?{role:"sourceRole",mult:"sourceMultiplicity",nav:"sourceEnd"}:{role:"role",mult:"multiplicity",nav:"targetEnd"},c=(o?n.sourceRole:n.role)??"",l=(o?n.sourceMultiplicity:n.multiplicity)??"",h=o?n.navFrom:n.navTo,u=o?n.from:n.to,f=document.createElement("div");f.className="vo-endpop",f.setAttribute("role","dialog"),f.setAttribute("aria-label",d(o?"web_visuobject_rel_end_a":"web_visuobject_rel_end_b",u)),f.innerHTML=`

    ${b(d(o?"web_visuobject_rel_end_a":"web_visuobject_rel_end_b",u))}

    - -
    ${b(d("web_visuobject_rel_multiplicity"))}${Nt.map(p=>``).join("")}
    -
    ${b(d("web_visuobject_rel_navigable"))}${["navigable","non-navigable","unspecified"].map(p=>``).join("")}
    `;const m=this.shell.querySelector(".vo-stage"),g=m.getBoundingClientRect();m.append(f);const _=Math.min(Math.max(8,s.clientX-g.left+8),g.width-f.offsetWidth-8),y=Math.min(Math.max(8,s.clientY-g.top+8),g.height-f.offsetHeight-8);f.style.left=`${Math.max(8,_)}px`,f.style.top=`${Math.max(8,y)}px`;const w=(p,v)=>{var S,k;const x=this.findRelation(e);x&&"key"in x&&this.changeAssoc(x,p,v);const L=(k=(S=document.activeElement)==null?void 0:S.dataset)==null?void 0:k.endField;this.relSelection&&this.reopenEnd(this.relSelection,t,f,L)};for(const p of f.querySelectorAll("[data-end-field]"))p.addEventListener("change",()=>w(p.dataset.endField==="role"?r.role:r.mult,p.value.trim())),p.addEventListener("keydown",v=>{v.stopPropagation(),v.key==="Enter"&&p.blur(),v.key==="Escape"&&this.closeEnd()});for(const p of f.querySelectorAll("[data-mult]"))p.addEventListener("click",()=>w(r.mult,p.dataset.mult));for(const p of f.querySelectorAll("[data-nav]"))p.addEventListener("click",()=>w(r.nav,p.dataset.nav));f.addEventListener("pointerdown",p=>p.stopPropagation()),f.addEventListener("click",p=>p.stopPropagation()),this.endPop=f;const $=f.querySelector(`[data-end-field="${i==="mult"?"mult":"role"}"]`);$==null||$.focus(),$==null||$.select()}reopenEnd(e,t,s,i){const n=this.shell.querySelector(".vo-stage").getBoundingClientRect(),o={clientX:n.left+parseFloat(s.style.left)-8,clientY:n.top+parseFloat(s.style.top)-8};this.openEnd(e,t,o,i)}closeEnd(){var e;(e=this.endPop)==null||e.remove(),this.endPop=void 0}renderInspector(){var r,c,l;if(this.diagramMode==="object")return this.objectDiagram.renderInspector();if(this.diagramMode==="sequence")return this.renderSequenceInspector();const e=this.findNote(this.noteSelection);if(e)return this.renderNoteInspector(e);const t=(r=this.relSelection)!=null&&r.startsWith("dep:")?this.findDependency(this.relSelection):void 0;if(t)return this.renderDependency(t);const s=this.relSelection?this.findRelation(this.relSelection):void 0;this.endPop&&(!s||!this.endPop.isConnected)&&this.closeEnd();const i=!s&&this.selection?this.findClass(this.selection.cls):void 0;if(i)return this.renderClassInspector(i);if(!s){delete this.inspector.dataset.rel,this.inspector.innerHTML=`

    ${b(d("web_visuobject_inspector_empty"))}

    `;return}const n=this.inspector.contains(document.activeElement)?document.activeElement.dataset.field??document.activeElement.dataset.inspector:void 0;this.inspector.dataset.rel=this.relSelection;const o=``;if("key"in s){const h=($,p,v)=>``,u=["association","aggregation","composition"].map($=>h($,d(`web_visuobject_rel_${$}`).replace(/\s*\(.*\)$/,""),s.kind)).join(""),f=$=>["navigable","non-navigable","unspecified"].map(p=>h(p,d(`web_visuobject_rel_nav_${p}`),$)).join(""),[m,g]=s.reading==="backward"?[s.to,s.from]:[s.from,s.to],_=s.name?`

    ${b(d("web_visuobject_rel_reading"))}: ${b(m)} ▶ ${b(g)}

    `:`

    ${b(d("web_visuobject_rel_reading_auto"))}

    `,y=`${Nt.map($=>``,w=($,p,v,x,L,S,k,j)=>` -
    - ${b($)} - - - - ${b(S)} -
    `;this.inspector.innerHTML=` -
    - - - ${_} - ${o} -
    -
    - ${w(d("web_visuobject_rel_end_a",s.from),"sourceRole",s.sourceRole,"sourceMultiplicity",s.sourceMultiplicity,s.back?d("web_visuobject_rel_role_field",s.to,s.back.field.name):d("web_visuobject_rel_role_only_uml"),"sourceEnd",s.navFrom)} - ${w(d("web_visuobject_rel_end_b",s.to),"role",s.role,"multiplicity",s.multiplicity,d("web_visuobject_rel_role_field",s.from,s.role),"targetEnd",s.navTo)} -
    - ${y}`}else{const h=d(s.kind==="implements"?"web_visuobject_rel_implements_text":"web_visuobject_rel_extends_text",s.from,s.to);this.inspector.innerHTML=`

    ${b(d(s.kind==="implements"?"web_visuobject_rel_implements":"web_visuobject_rel_extends"))}

    ${b(h)}

    ${o}
    `}for(const h of this.inspector.querySelectorAll("[data-field]"))h.addEventListener("change",()=>{const u=this.relSelection&&this.findRelation(this.relSelection);u&&"key"in u&&this.changeAssoc(u,h.dataset.field,h.value)}),h instanceof HTMLInputElement&&h.addEventListener("keydown",u=>u.key==="Enter"&&h.blur());this.inspector.querySelector("[data-inspector=delete]").addEventListener("click",()=>this.deleteRelation(this.relSelection)),n&&((c=this.inspector.querySelector(`[data-field="${n}"], [data-inspector="${n}"]`))==null||c.focus({preventScroll:!0})),(l=this.inspector.querySelector("[data-inspector=flip]"))==null||l.addEventListener("click",()=>{const h=this.relSelection&&this.findRelation(this.relSelection);h&&"key"in h&&this.changeAssoc(h,"reading","")})}renderClassInspector(e){var w,$;const t=document.activeElement,s=this.inspector.contains(t)&&t!=null&&t.dataset.field?`${t.dataset.row??""}|${t.dataset.field}`:void 0,i=((w=this.project.files.find(p=>p.path===e.path))==null?void 0:w.text)??"",n=ci(i,e),o=e.kind==="class",r=(p,v,x)=>``,c=(p,v=!0)=>["+","-","#","~"].filter(x=>v||x!=="~").map(x=>r(x,`${x} ${{"+":"public","-":"private","#":"protected","~":"package"}[x]}`,x===p)).join(""),l=e.members.map((p,v)=>({m:p,i:v})).filter(p=>p.m.kind==="field"),h=e.members.map((p,v)=>({m:p,i:v})).filter(p=>p.m.kind!=="field"),u=(p,v)=>``,f=`${["int","double","boolean","char","long","String",...this.classes.map(p=>p.name),...this.classes.map(p=>`ArrayList<${p.name}>`)].map(p=>``;let m="";if(this.classTab==="class"){const p=this.classes.filter(S=>S.kind==="class"&&S.name!==e.name),v=this.classes.filter(S=>S.kind==="interface"&&S.name!==e.name),x=D(e.path),L=ue(this.project).flatMap(S=>{const k=ce(this.project,S);return k.includes(re(S,""))?k:[...k,re(S,"")]});m=` -
    - - - - ${o?``:""} -
    -
    - ${o?``:""} - ${v.length?`
    ${b(d(o?"web_visuobject_new_interfaces":"web_visuobject_new_extends_interfaces"))}${v.map(S=>``).join("")}
    `:""} -
    - -
    ${o?``:""}
    `}else if(this.classTab==="fields"){const p=l.map(({m:v,i:x})=>{const L=Ms(i,v);return` - - - - - - - - `}).join("");m=`${p||``}
    ${b(d("web_visuobject_insp_visibility"))}${b(d("web_visuobject_new_name"))}${b(d("web_visuobject_insp_type"))}staticfinal${b(d("web_visuobject_insp_init"))}
    ${b(d("web_visuobject_insp_no_fields"))}
    -
    `}else{const p=o,v=h.map(({m:x,i:L})=>{const S=x.kind==="constructor";return` - - - - - - - - `}).join("");m=`${v||``}
    ${b(d("web_visuobject_insp_visibility"))}${b(d("web_visuobject_new_name"))}${b(d("web_visuobject_insp_params"))}${b(d("web_visuobject_insp_return"))}staticabstract
    ${b(d("web_visuobject_insp_no_methods"))}
    -
    - - ${o?`${b(d("web_visuobject_insp_generate"))} - - - `:""} -
    `}this.inspector.dataset.rel=`class:${e.name}`,this.inspector.innerHTML=` -
    -

    ${b(d(o?"web_visuobject_new_class":"web_visuobject_new_interface"))} ${b(e.name)}

    -
    ${u("class",d(o?"web_visuobject_new_class":"web_visuobject_new_interface"))}${u("fields",`${d("web_visuobject_insp_fields")} (${l.length})`)}${u("methods",`${d("web_visuobject_insp_methods")} (${h.length})`)}
    -
    - ${m}${f}`;const g=(p,v)=>{this.message("");let x=this.editClass(this.project,e.name,p);v&&(x=v(x)),this.setProject(x,e.path)},_=(p,v)=>p.members[Number(v.dataset.row)];for(const p of this.inspector.querySelectorAll("[data-class-tab]"))p.addEventListener("click",()=>{this.classTab=p.dataset.classTab,this.renderInspector()});for(const p of this.inspector.querySelectorAll("[data-field]"))p.addEventListener("change",()=>{var S;const v=p.dataset.field,x=p instanceof HTMLInputElement&&p.checked,L=p.value.trim();if(v==="name")L&&L!==e.name&&this.renameClassTo(e,L);else if(v==="package"){const k=le(p.value,Q(e.path));if(k===e.path)return;if(this.project.files.some(j=>j.path===k))return this.message(d("web_visuobject_new_exists",Q(k)));this.openTabs=this.openTabs.map(j=>j===e.path?k:j),this.currentPackage=p.value,this.setProject(Oe(this.project,e.path,k),k)}else if(v==="visibility")g((k,j)=>li(k,j,p.value==="public"));else if(v==="abstract")g((k,j)=>{const M=x?Ne(k,j,!1):k;return Et(M,oe(M).find(A=>A.name===j.name)??j,x)});else if(v==="final")g((k,j)=>Ne(k,j,x));else if(v==="superclass"){const k=p.value||void 0;g((j,M)=>Ze(j,M,k),j=>k?this.importFor(j,e.name,k):j)}else if(v==="iface"){const k=p.value;g((j,M)=>x?jt(j,M,k):St(j,M,k),j=>x?this.importFor(j,e.name,k):j)}else if(v==="doc")g((k,j)=>mn(k,j,p.value));else if(v.startsWith("f-")){if(v==="f-name"&&!pe.test(L)||v==="f-type"&&!L)return this.message(d("web_visuobject_bad_name",L));const k=v==="f-vis"?{visibility:p.value}:v==="f-name"?{name:L}:v==="f-type"?{type:L}:v==="f-static"?{isStatic:x}:v==="f-final"?{isFinal:x}:{init:p.value},j=(S=e.members[Number(p.dataset.row)])==null?void 0:S.name;g((M,A)=>ln(M,_(A,p),k),M=>{var A;return v==="f-name"&&j&&((A=M.uml)!=null&&A[V(e.name,j)])?{...M,uml:ke(M.uml,V(e.name,j),V(e.name,L))}:M})}else if(v.startsWith("m-")){let k;if(v==="m-params"){const j=dn(p.value);if(!j)return this.message(d("web_visuobject_insp_bad_params",p.value));k={params:j}}else if(v==="m-name"){if(!pe.test(L))return this.message(d("web_visuobject_bad_name",L));k={name:L}}else k=v==="m-vis"?{visibility:p.value}:v==="m-type"?{type:L||"void"}:v==="m-static"?{isStatic:x}:{isAbstract:x};g((j,M)=>{const A=pn(j,M,_(M,p),k);if(v==="m-abstract"&&x&&!M.isAbstract){const R=oe(A).find(C=>C.name===M.name);return R?Et(Ne(A,R,!1),oe(Ne(A,R,!1)).find(C=>C.name===M.name),!0):A}return A})}}),p instanceof HTMLInputElement&&p.type==="text"&&p.addEventListener("keydown",v=>v.key==="Enter"&&p.blur());const y=(p,v)=>{for(const x of this.inspector.querySelectorAll(`[data-inspector="${p}"]`))x.addEventListener("click",()=>v(x))};if(y("delete-class",()=>this.deleteClassFromProject(e)),y("sections",()=>g((p,v)=>pt(p,v,new Set(this.classes.map(x=>x.name)),this.sectionLabels()))),y("delete-member",p=>g((v,x)=>xe(v,_(x,p)))),y("add-field",()=>g((p,v)=>Ee(p,v,`- ${this.freeMember(e,"attribut")}: int`))),y("add-method",()=>g((p,v)=>Ee(p,v,`+ ${this.freeMember(e,"methode")}(): void`))),y("gen-constructor",()=>g((p,v)=>bn(p,v))),y("gen-accessors",()=>g((p,v)=>vn(p,v))),y("gen-tostring",()=>g((p,v)=>gn(p,v))),s){const[p,v]=s.split("|");($=this.inspector.querySelector(p?`[data-row="${p}"][data-field="${v}"]`:`[data-field="${v}"]`))==null||$.focus({preventScroll:!0})}}freeMember(e,t){let s=t;for(let i=2;e.members.some(n=>n.name===s);i++)s=t+i;return s}renderDependency(e){this.closeEnd(),this.inspector.dataset.rel=this.relSelection,this.inspector.innerHTML=`
    -

    ${b(d("web_visuobject_dep_title",e.kind))}

    -

    ${b(d(`web_visuobject_dep_${e.kind}_text`,e.from,e.to,e.methods.join(", ")))}

    -
    -

    ${b(d("web_visuobject_dep_hint",e.from,e.to))}

    -
    `,this.inspector.querySelector("[data-inspector=to-assoc]").addEventListener("click",()=>{var s;const t=this.addAssociationField(this.project,e.from,e.to,!1);this.relSelection=`assoc:${V(e.from,t.field)}`,this.setProject(t.project,(s=this.findClass(e.from))==null?void 0:s.path)})}setTool(e){this.tool=e,this.pendingFrom=void 0;for(const t of this.canvas.querySelectorAll(".uml-class.source"))t.classList.remove("source");for(const t of this.shell.querySelectorAll(".vo-tool"))t.setAttribute("aria-pressed",String(t.dataset.tool===e));this.canvas.dataset.tool=e,this.message(e==="select"?"":d(`web_visuobject_tool_${e}_tip`),!0)}onToolDown(e,t){if(e.button!==0)return;if(t!=="class"&&t!=="interface")return this.setTool(this.tool===t?"select":t);e.preventDefault();const s={x:e.clientX,y:e.clientY};let i;const n=r=>{!i&&Math.hypot(r.clientX-s.x,r.clientY-s.y)<6||(i||(i=document.createElement("div"),i.className=`vo-tool-ghost${t==="interface"?" interface":""}`,i.innerHTML=`${t==="interface"?"«interface»":""}${b(d(t==="class"?"web_visuobject_new_class_name":"web_visuobject_new_interface_name"))}`,document.body.append(i)),i.style.left=`${r.clientX}px`,i.style.top=`${r.clientY}px`)},o=r=>{if(window.removeEventListener("pointermove",n),window.removeEventListener("pointerup",o),!i)return this.setTool(this.tool===t?"select":t);i.remove();const c=this.canvas.getBoundingClientRect();r.clientXc.right||r.clientYc.bottom||(this.setTool("select"),this.newClass(t,{x:r.clientX-c.left+this.canvas.scrollLeft-60,y:r.clientY-c.top+this.canvas.scrollTop-14}))};window.addEventListener("pointermove",n),window.addEventListener("pointerup",o)}useTool(e){e.preventDefault();const t=this.tool,s=this.canvas.getBoundingClientRect(),i={x:e.clientX-s.left+this.canvas.scrollLeft,y:e.clientY-s.top+this.canvas.scrollTop};if(t==="class"||t==="interface"){this.setTool("select"),this.newClass(t,{x:i.x-60,y:i.y-14});return}if(t==="note"){this.setTool("select"),this.addNote({x:i.x-20,y:i.y-14});return}const n=e.target.closest(".uml-class");if(!n){this.pendingFrom&&this.setTool(t);return}const o=n.dataset.class;if(this.pendingFrom&&this.pendingFrom!==o){const r=this.pendingFrom;this.connectWith(r,o,t);return}this.startConnect(e,o,t)}connectWith(e,t,s){const i=this.findClass(e),n=this.findClass(t);if(this.setTool("select"),!(!i||!n)){if(!ps(i,n).includes(s))return this.message(d(`web_visuobject_tool_not_${s}`,e,t));this.createRelation(e,t,s)}}startConnect(e,t,s){e.preventDefault();const i=document.createElementNS("http://www.w3.org/2000/svg","line");i.setAttribute("class","vo-rel ghost"),this.lines.append(i);const n=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(t)}"]`),o=u=>{const f=this.canvas.getBoundingClientRect();return{x:u.clientX-f.left+this.canvas.scrollLeft,y:u.clientY-f.top+this.canvas.scrollTop}},r={x:n.offsetLeft+n.offsetWidth,y:n.offsetTop+n.offsetHeight/2},c=u=>{var m;const f=(m=document.elementFromPoint(u.clientX,u.clientY))==null?void 0:m.closest(".uml-class");return f&&f.dataset.class!==t?f:void 0},l=u=>{var m;const f=o(u);i.setAttribute("x1",String(r.x)),i.setAttribute("y1",String(r.y)),i.setAttribute("x2",String(f.x)),i.setAttribute("y2",String(f.y));for(const g of this.canvas.querySelectorAll(".uml-class.drop"))g.classList.remove("drop");(m=c(u))==null||m.classList.add("drop")},h=u=>{var m;window.removeEventListener("pointermove",l),window.removeEventListener("pointerup",h),i.remove();const f=c(u);f==null||f.classList.remove("drop"),f&&s?this.connectWith(t,f.dataset.class,s):f?this.showRelationMenu(t,f.dataset.class,o(u)):s&&(this.pendingFrom=t,(m=this.canvas.querySelector(`.uml-class[data-class="${CSS.escape(t)}"]`))==null||m.classList.add("source"),this.message(d("web_visuobject_tool_pick_second",t),!0))};l(e),window.addEventListener("pointermove",l),window.addEventListener("pointerup",h)}showRelationMenu(e,t,s){var u;const i=this.findClass(e),n=this.findClass(t);if(!i||!n)return;const o=ps(i,n).map(f=>[f,f==="association"?"web_visuobject_rel_directed":`web_visuobject_rel_${f}`]);if(o.length===0)return this.message(d("web_visuobject_rel_impossible",e,t));const r=document.createElement("div");r.className="vo-relmenu",r.setAttribute("role","menu"),r.style.left=`${s.x}px`,r.style.top=`${s.y}px`,r.innerHTML=`

    ${b(d("web_visuobject_rel_menu",e,t))}

    `+o.map(([f,m])=>``).join(""),this.canvas.append(r);const c=()=>{r.remove(),document.removeEventListener("pointerdown",l,!0),document.removeEventListener("keydown",h,!0)},l=f=>{r.contains(f.target)||c()},h=f=>{f.key==="Escape"&&(f.stopPropagation(),c())};r.addEventListener("click",f=>{var g;const m=(g=f.target.closest("[data-kind]"))==null?void 0:g.dataset.kind;m&&(c(),this.createRelation(e,t,m))}),document.addEventListener("pointerdown",l,!0),document.addEventListener("keydown",h,!0),(u=r.querySelector("button"))==null||u.focus()}}const Tn=["select","class","interface","note","link","association","aggregation","composition","extends","implements"];function ps(a,e){const t=[];return a.kind==="class"&&t.push("link","association","aggregation","composition"),a.kind===e.kind&&(a.kind==="class"?a.superclass!==e.name:!a.interfaces.includes(e.name))&&t.push("extends"),a.kind==="class"&&e.kind==="interface"&&!a.interfaces.includes(e.name)&&t.push("implements"),t}function An(a){return a==="select"?'':a==="class"||a==="interface"?``:a==="note"?'':Ts(a)}function Ts(a){const e=(s,i,n=!1)=>``;return``}function ze(a,e,t){const s=t.x-e.x,i=t.y-e.y,n=s===0?1/0:a.w/2/Math.abs(s),o=i===0?1/0:a.h/2/Math.abs(i),r=Math.min(n,o,1);return{x:Math.round(e.x+s*r),y:Math.round(e.y+i*r)}}const Nn=Object.freeze(Object.defineProperty({__proto__:null,ObjectApp:Cn},Symbol.toStringTag,{value:"Module"}));export{Z as a,le as b,re as c,mi as d,di as e,X as f,Nn as g,Je as i,vs as n,gs as o,At as p,U as r,ue as s,ys as u}; diff --git a/website/app/assets/pl-DDkdCN00.js b/website/app/assets/pl-DDkdCN00.js deleted file mode 100644 index 3b614668..00000000 --- a/website/app/assets/pl-DDkdCN00.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Teksty dostępne tylko w wersji webowej. Wszystko inne pochodzi z plików Localizable.strings aplikacji. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagram VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Eksportuj…"; -"web_export_title" = "Zapisz jako obraz lub PDF albo wydrukuj"; -"web_export_png" = "Obraz (PNG)"; -"web_export_svg" = "Grafika wektorowa (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Plik VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Eksport nie powiódł się: %@"; -"web_print_hint" = "Użyj „Eksportuj… → PDF“, aby zapisać diagram i wydrukować go z pliku."; -"web_delete" = "Usuń"; -"web_language_title" = "Język interfejsu"; -"web_simulation_start" = "Uruchom symulację"; -"web_simulation_end" = "Zakończ symulację"; -"web_simulation_start_title" = "Uruchom diagram krok po kroku"; -"web_simulation_end_title" = "Zakończ symulację i wróć do edycji"; -"web_palette_hint" = "Przeciągnij do diagramu"; -"web_tabs_label" = "Diagramy"; -"web_status_modified" = "zmieniony"; -"web_inspector_title" = "Właściwości"; -"web_inspector_resize" = "Zmień wysokość inspektora obiektów: przeciągnij lub przesuń linię w górę lub w dół (dwuklik: domyślnie)"; -"web_inspector_help_drop" = "Przeciągnij elementy z palety na kafelek: góra = przed, dół = za, na „ø“ = zastąp. Istniejące elementy można przesuwać myszą."; -"web_inspector_help_inline" = "Kliknij dwukrotnie lub stuknij dwa razy element, aby edytować tekst bezpośrednio w diagramie (Enter zatwierdza, Esc anuluje). Dotyczy też nazw przypadków."; -"web_inspector_help_files" = "Pliki można też upuścić w oknie."; -"web_case_name" = "Nazwa przypadku"; -"web_case_name_placeholder" = "np. 1, ok, default"; -"web_copied" = "Skopiowano element."; -"web_discard_message_fmt" = "Zmiany w „%@“ zostaną utracone."; -"web_err_unreadable_fmt" = "„%@“ nie jest czytelnym plikiem VisuStruct (%@)."; -"web_saved_fmt" = "Zapisano „%@“."; -"web_saved_txt_fmt" = "Zapisano jako „%@“. Usuń rozszerzenie .txt, aby go użyć."; -"web_drop_impossible" = "Nie można tu upuścić"; -"web_sim_delay" = "Pauza na krok"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Język"; -"web_codegen_comments" = "Tekst diagramu jako komentarze"; -"web_codegen_empty_else" = "Generuj pustą gałąź else w kodzie"; -"web_copied_check" = "Skopiowano ✓"; -"web_copy_manual" = "Zaznaczono, naciśnij Ctrl+C, aby skopiować"; -"web_run_stopped_loop" = "Wykonanie zatrzymane: możliwa pętla nieskończona."; -"web_run_cancelled" = "Wykonanie anulowane."; -"web_run_error" = "Błąd podczas wykonywania."; -"web_run_dropped_fmt" = "… ukryto wcześniejsze wiersze: %d"; -"web_sim_call_arguments_fmt" = "Wywołanie nie pasuje do nagłówka %@. Sprawdź wartości w nawiasach."; -"web_sim_missing_return_fmt" = "%@ zakończył się bez zwrócenia wartości przez return."; -"web_sim_file_not_found_fmt" = "Nie ma pliku o nazwie %@."; -"web_sim_end_of_file_fmt" = "Plik %@ nie ma już więcej wierszy."; -"web_sim_null_value_fmt" = "%@ ma wartość null."; -"web_sim_unknown_variable_case_fmt" = "Nieznana zmienna: %1$@. Java rozróżnia wielkość liter: czy chodziło o %2$@?"; -"web_sim_written_file_fmt" = "%@ (zapisano)"; -"web_site_home" = "Strona główna"; -"web_site_imprint" = "Nota prawna"; -"web_site_privacy" = "Prywatność"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programowanie"; -"web_live_hide" = "Ukryj programowanie"; -"web_scale_canvas" = "Rozmiar tekstu: diagram"; -"web_scale_code" = "Rozmiar tekstu: programowanie"; -"web_live_indent" = "Wcięcia"; -"web_live_indent_title" = "Popraw wcięcia kodu: cztery spacje na poziom (zaznaczone wiersze, w przeciwnym razie wszystko)"; -"web_live_show" = "Pokaż programowanie"; -"web_live_resize" = "Zmień szerokość obszaru kodu (dwuklik: resetuj)"; -"web_live_hint" = "Kod rośnie razem z diagramem. Szary = symbol zastępczy: wpisz tekst w inspektorze, a pojawi się tutaj. Kliknij wiersz, aby zaznaczyć jego element."; -"web_live_ph_selector" = "zmienna"; -"web_live_ph_code" = "Twój kod"; -"web_live_ph_end" = "koniec"; -"web_live_language" = "Język programowania"; -"web_live_java_class" = "Jako klasa"; -"web_live_java_class_hint" = "Nazwa klasy = tytuł diagramu"; -"web_live_java_main" = "Z metodą main"; -"web_settings_display" = "Wygląd"; -"web_live_swing" = "Prawdziwa Java (ze Swing)"; -"web_live_swing_hint" = "Prawdziwa Java z oknami (CheerpJ). Przy pierwszym uruchomieniu jednorazowo pobiera ok. 40 MB."; -"web_live_swing_windows" = "Okna Java"; -"web_live_swing_hide" = "Zamknij okna Java"; -"web_live_swing_loading" = "Ładowanie Javy (za pierwszym razem ok. 40 MB) …"; -"web_live_swing_compiling" = "Kompilowanie …"; -"web_live_swing_running" = "Program działa."; -"web_live_swing_compile_failed" = "Błąd kompilacji, zobacz powyżej."; -"web_live_swing_failed" = "Nie udało się załadować Javy. Sprawdź połączenie z internetem."; -"web_live_hint_edit" = "Wpisz tu kod Java, a diagram zbuduje się sam. Albo pracuj w diagramie, a kod będzie rósł razem z nim. Szary = symbol zastępczy, po prostu pisz."; -"web_live_edit_label" = "Kod Java"; -"web_parse_line_fmt" = "Wiersz %d: %@"; -"web_parse_missing" = "Tu jeszcze brakuje: %@"; -"web_parse_extra" = "Za dużo: %@"; -"web_parse_unclosed_string" = "Brakuje zamykającego %@."; -"web_parse_else_without_if" = "Ten „else” nie ma pasującego „if”."; -"web_parse_case_outside_switch" = "„case” i „default” należą do „switch”."; -"web_parse_missing_do_while" = "Po „do { … }” brakuje jeszcze „while (warunek);”."; -"web_parse_more_methods" = "Na razie liczy się tu tylko jedna metoda. Utwórz kolejne podprogramy w osobnych kartach."; -"web_parse_missing_import" = "Na samej górze brakuje %@ Bez tej linii Java nie zna klasy Scanner."; -"web_parse_missing_scanner" = "Wcześniej brakuje %@ Bez tej linii Scanner nie istnieje."; -"web_parse_unknown_type" = "Java nie zna tego typu danych. Czy chodziło ci o %@?"; -"web_quickfix_insert_fmt" = "Propozycja: wstaw %@"; -"web_quickfix_replace_fmt" = "Propozycja: %1$@ zamiast %2$@"; -"web_parse_wrong_case" = "Java rozróżnia wielkość liter. Poprawnie: %@."; -"web_editor_title" = "Diagram"; -"web_editor_show" = "Pokaż diagram"; -"web_editor_hide" = "Ukryj diagram"; -"web_keywords_title" = "Słowa kluczowe kodu"; -"web_keywords_hint" = "if, while, for zamiast nazw elementów"; -"web_palette_compact" = "Elementy zawsze kompaktowe"; -"web_palette_compact_hint" = "Symbol z małą nazwą pod spodem, w przeciwnym razie tylko przy wąskiej kolumnie"; -"web_about_version_fmt" = "Wersja %@"; -"web_about_software" = "Używane oprogramowanie"; -"web_about_software_text" = "Edytor kodu korzysta z CodeMirror i Lezer (licencja MIT). Z obsługą Swing VisuStruct wczytuje CheerpJ firmy Leaning Technologies (licencja Community) oraz javac z OpenJDK."; -"web_about_licenses" = "Teksty licencji"; -"web_live_run" = "Uruchom"; -"web_live_run_title" = "Uruchom program: wyjście i wejście pojawią się poniżej"; -"web_live_run_finished" = "Program zakończony."; -"web_live_run_stopped" = "Zatrzymano."; -"web_console" = "Konsola"; -"web_console_hide" = "Ukryj konsolę"; -"web_console_resize" = "Zmień wysokość konsoli"; -"web_diagram_tools" = "Narzędzia diagramu"; -"web_files_title" = "Pliki"; -"web_files_show" = "Pokaż pliki"; -"web_files_hide" = "Ukryj pliki"; -"web_files_resize" = "Zmień szerokość plików"; -"web_files_new_folder" = "Nowy folder"; -"web_files_switch" = "Wybierz inny folder"; -"web_files_delete" = "Usuń"; -"web_files_in_browser" = "W pamięci tej przeglądarki"; -"web_files_save_hint_fmt" = "Zapis umieszcza program w %@."; -"web_files_empty" = "Nic jeszcze nie zapisano. Przy zapisie VisuStruct tworzy tu folder z dzisiejszą datą."; -"web_files_reconnect_hint" = "Po przeładowaniu przeglądarka ponownie pyta, czy VisuStruct może używać twojego folderu."; -"web_files_reconnect_fmt" = "Otwórz folder „%@”"; -"web_files_connect_hint" = "Wybierz folder jako obszar roboczy. VisuStruct tworzy w nim folder na każdy dzień oraz zapisuje diagram i kod."; -"web_files_connect" = "Wybierz folder"; -"web_files_browser_hint" = "Ta przeglądarka nie może otwierać folderów na urządzeniu (np. na iPadzie). Twoje pliki pozostają w pamięci tej przeglądarki."; -"web_files_browser" = "Zapisz w przeglądarce"; -"web_files_delete_confirm_fmt" = "Na pewno usunąć „%@”?"; -"web_files_folder_prompt" = "Nazwa nowego folderu:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Zapisano: %@"; -"web_files_create" = "Utwórz"; -"web_learn_ludwig_tip" = "Wskazówka Ludwiga"; -"web_game_title" = "Gra: Prawda czy fałsz?"; -"web_game_intro" = "Ludwig pokazuje wartości i warunek. Czy warunek jest prawdziwy (true), czy fałszywy (false)? Dziesięć rund, coraz trudniejszych."; -"web_game_start" = "Zacznij grę"; -"web_game_round_fmt" = "Runda %@ z %@"; -"web_game_score_fmt" = "Punkty: %@"; -"web_game_true" = "prawda (true)"; -"web_game_false" = "fałsz (false)"; -"web_game_right" = "Dobrze!"; -"web_game_wrong" = "Niestety źle. Tak to się liczy:"; -"web_game_next" = "Dalej"; -"web_game_finish" = "Wynik"; -"web_game_done_fmt" = "%@ z %@ dobrze."; -"web_game_perfect" = "Wszystko dobrze – Ludwig jest pod wrażeniem!"; -"web_game_good" = "Świetnie! Przejrzyj błędy i spróbuj jeszcze raz."; -"web_game_practice" = "Trening czyni mistrza. Przeczytaj jeszcze raz tabelę prawdy i zagraj ponownie."; -"web_game_again" = "Zagraj ponownie"; -"web_game_wrong_short" = "Niestety źle."; -"web_game_practice_short" = "Praktyka czyni mistrza. Przeczytaj jeszcze raz wyjaśnienia i zagraj kolejną rundę."; -"web_game_clicks" = "Kliknięcia:"; -"web_game_nobody" = "Nikt nie słucha – nic się nie dzieje."; -"web_game_event_fmt" = "Kliknięcie „%@”: słuchacz to słyszy i wykonuje swój kod."; -"web_game_try" = "Teraz sam wypróbuj przyciski w oknie."; -"web_game_time_fmt" = "Zostało %@ s"; -"web_game_defused" = "✂️ Rozbrojona! Znalazłeś błąd."; -"web_game_boom_time" = "💥 Bum! Czas minął."; -"web_game_boom_wrong" = "💥 Bum! To była zła linia."; -"web_game_boom_part" = "💥 Bum! Dobra linia, ale błąd jest w innej części."; -"web_game_boom_wrong_part" = "💥 Bum! Błąd jest w innej linii."; -"web_game_bomb_parts_hint" = "Stuknij dokładnie w błędną część."; -"web_game_fix" = "Poprawna linia:"; -"web_game_order_hint" = "Stuknij kroki we właściwej kolejności."; -"web_debug_step" = "Krok"; -"web_debug_step_title" = "Krok: wykonaj jedną instrukcję i zatrzymaj się ponownie"; -"web_debug_continue" = "Dalej do następnego punktu przerwania"; -"web_debug_paused_fmt" = "Zatrzymano przed wierszem %d."; -"web_debug_gutter_title" = "Kliknij numer wiersza, aby ustawić lub usunąć punkt przerwania"; -"web_swap_to_code" = "Programowanie duże"; -"web_swap_to_editor" = "Diagram duży"; -"web_editor_resize" = "Zmień szerokość diagramu"; -"web_palette_resize" = "Zmień szerokość elementów"; -"web_palette_hide" = "Ukryj elementy"; -"web_palette_show" = "Pokaż elementy"; -"web_code_scopes" = "Kolorowe bloki kodu"; -"web_code_scopes_hint" = "Klasa, metoda, pętla, rozgałęzienie we własnych ramkach"; -"web_pseudocode" = "Pseudokod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Zmień nazwę zmiennej …"; -"web_rename_title" = "Zmień nazwę zmiennej"; -"web_rename_old" = "Nazwa"; -"web_rename_new" = "Nowa nazwa"; -"web_rename_hint" = "Zmienia każde miejsce o dokładnie tej pisowni (liczba to nie Liczba), w kodzie i w strukturogramie. Tekst w \\"…\\" i komentarze zostają."; -"web_rename_count_fmt" = "Występuje %d razy."; -"web_rename_exists_fmt" = "„%@” już istnieje: potem obie byłyby jedną zmienną."; -"web_rename_invalid" = "Nazwa zaczyna się od litery i zawiera tylko litery, cyfry i _."; -"web_rename_keyword_fmt" = "„%@” to słowo kluczowe Javy."; -"web_rename_apply" = "Zmień nazwę"; -"web_rename_cancel" = "Anuluj"; -`;export{e as default}; diff --git a/website/app/assets/projectZip-DtugKwEc.js b/website/app/assets/projectZip-DtugKwEc.js deleted file mode 100644 index 8df1ebd5..00000000 --- a/website/app/assets/projectZip-DtugKwEc.js +++ /dev/null @@ -1 +0,0 @@ -import{p as Br,d as Er,a as mr,i as Gr,b as Qr,c as Vr,u as Lr,n as _r,o as pr,e as br,r as dr,f as re,s as ee}from"./objectApp-ClGVaIKp.js";import"./index-BqSgHxq8.js";var M=Uint8Array,K=Uint16Array,Tr=Int32Array,hr=new M([0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0,0]),gr=new M([0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13,0,0]),Sr=new M([16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15]),Wr=function(r,e){for(var n=new K(31),a=0;a<31;++a)n[a]=e+=1<>1|(F&21845)<<1;b=(b&52428)>>2|(b&13107)<<2,b=(b&61680)>>4|(b&3855)<<4,Cr[F]=((b&65280)>>8|(b&255)<<8)>>1}var V=(function(r,e,n){for(var a=r.length,f=0,v=new K(e);f>l]=c}else for(s=new K(a),f=0;f>15-r[f]);return s}),d=new M(288);for(var F=0;F<144;++F)d[F]=8;for(var F=144;F<256;++F)d[F]=9;for(var F=256;F<280;++F)d[F]=7;for(var F=280;F<288;++F)d[F]=8;var ir=new M(32);for(var F=0;F<32;++F)ir[F]=5;var ae=V(d,9,0),fe=V(d,9,1),ve=V(ir,5,0),te=V(ir,5,1),xr=function(r){for(var e=r[0],n=1;ne&&(e=r[n]);return e},X=function(r,e,n){var a=e/8|0;return(r[a]|r[a+1]<<8)>>(e&7)&n},yr=function(r,e){var n=e/8|0;return(r[n]|r[n+1]<<8|r[n+2]<<16)>>(e&7)},Ur=function(r){return(r+7)/8|0},lr=function(r,e,n){return(e==null||e<0)&&(e=0),(n==null||n>r.length)&&(n=r.length),new M(r.subarray(e,n))},oe=["unexpected EOF","invalid block type","invalid length/literal","invalid distance","stream finished","no stream handler",,"no callback","invalid UTF-8 data","extra field too long","date not in range 1980-2099","filename too long","stream finishing","invalid zip data"],I=function(r,e,n){var a=new Error(e||oe[r]);if(a.code=r,Error.captureStackTrace&&Error.captureStackTrace(a,I),!n)throw a;return a},ie=function(r,e,n,a){var f=r.length,v=a?a.length:0;if(!f||e.f&&!e.l)return n||new M(0);var i=!n,s=i||e.i!=2,l=e.i;i&&(n=new M(f*3));var c=function(fr){var vr=n.length;if(fr>vr){var nr=new M(Math.max(vr*2,fr));nr.set(n),n=nr}},t=e.f||0,o=e.p||0,u=e.b||0,h=e.l,y=e.d,w=e.m,x=e.n,T=f*8;do{if(!h){t=X(r,o,1);var z=X(r,o+1,3);if(o+=3,z)if(z==1)h=fe,y=te,w=9,x=5;else if(z==2){var U=X(r,o,31)+257,A=X(r,o+10,15)+4,m=U+X(r,o+5,31)+1;o+=14;for(var g=new M(m),B=new M(19),j=0;j>4;if(C<16)g[j++]=C;else{var E=0,k=0;for(C==16?(k=3+X(r,o,3),o+=2,E=g[j-1]):C==17?(k=3+X(r,o,7),o+=3):C==18&&(k=11+X(r,o,127),o+=7);k--;)g[j++]=E}}var W=g.subarray(0,U),J=g.subarray(U);w=xr(W),x=xr(J),h=V(W,w,1),y=V(J,x,1)}else I(1);else{var C=Ur(o)+4,O=r[C-4]|r[C-3]<<8,$=C+O;if($>f){l&&I(0);break}s&&c(u+O),n.set(r.subarray(C,$),u),e.b=u+=O,e.p=o=$*8,e.f=t;continue}if(o>T){l&&I(0);break}}s&&c(u+131072);for(var ar=(1<>4;if(o+=E&15,o>T){l&&I(0);break}if(E||I(2),q<256)n[u++]=q;else if(q==256){L=o,h=null;break}else{var H=q-254;if(q>264){var j=q-257,S=hr[j];H=X(r,o,(1<>4;G||I(3),o+=G&15;var J=ne[rr];if(rr>3){var S=gr[rr];J+=yr(r,o)&(1<T){l&&I(0);break}s&&c(u+131072);var er=u+H;if(u>8},tr=function(r,e,n){n<<=e&7;var a=e/8|0;r[a]|=n,r[a+1]|=n>>8,r[a+2]|=n>>16},Fr=function(r,e){for(var n=[],a=0;au&&(u=v[a].s);var h=new K(u+1),y=Or(n[t-1],h,0);if(y>e){var a=0,w=0,x=y-e,T=1<e)w+=T-(1<>=x;w>0;){var C=v[a].s;h[C]=0&&w;--a){var O=v[a].s;h[O]==e&&(--h[O],++w)}y=e}return{t:new M(h),l:y}},Or=function(r,e,n){return r.s==-1?Math.max(Or(r.l,e,n+1),Or(r.r,e,n+1)):e[r.s]=n},Nr=function(r){for(var e=r.length;e&&!r[--e];);for(var n=new K(++e),a=0,f=r[0],v=1,i=function(l){n[a++]=l},s=1;s<=e;++s)if(r[s]==f&&s!=e)++v;else{if(!f&&v>2){for(;v>138;v-=138)i(32754);v>2&&(i(v>10?v-11<<5|28690:v-3<<5|12305),v=0)}else if(v>3){for(i(f),--v;v>6;v-=6)i(8304);v>2&&(i(v-3<<5|8208),v=0)}for(;v--;)i(f);v=1,f=r[s]}return{c:n.subarray(0,a),n:e}},or=function(r,e){for(var n=0,a=0;a>8,r[f+2]=r[f]^255,r[f+3]=r[f+1]^255;for(var v=0;v4&&!B[Sr[D-1]];--D);var p=c+5<<3,Z=or(f,d)+or(v,ir)+i,P=or(f,u)+or(v,w)+i+14+3*D+or(A,B)+2*A[16]+3*A[17]+7*A[18];if(l>=0&&p<=Z&&p<=P)return Kr(e,t,r.subarray(l,l+c));var E,k,W,J;if(_(e,t,1+(P15&&(_(e,t,q[m]>>5&127),t+=q[m]>>12)}}else E=ae,k=d,W=ve,J=ir;for(var m=0;m255){var H=S>>18&31;tr(e,t,E[H+257]),t+=k[H+257],H>7&&(_(e,t,S>>23&31),t+=hr[H]);var G=S&31;tr(e,t,W[G]),t+=J[G],G>3&&(tr(e,t,S>>5&8191),t+=gr[G])}else tr(e,t,E[S]),t+=k[S]}return tr(e,t,E[256]),t+k[256]},le=new Tr([65540,131080,131088,131104,262176,1048704,1048832,2114560,2117632]),Rr=new M(0),se=function(r,e,n,a,f,v){var i=v.z||r.length,s=new M(a+i+5*(1+Math.ceil(i/7e3))+f),l=s.subarray(a,s.length-f),c=v.l,t=(v.r||0)&7;if(e){t&&(l[0]=v.r>>3);for(var o=le[e-1],u=o>>13,h=o&8191,y=(1<7e3||B>24576)&&(E>423||!c)){t=Dr(r,l,0,O,$,U,m,B,D,g-D,t),B=A=m=0,D=g;for(var k=0;k<286;++k)$[k]=0;for(var k=0;k<30;++k)U[k]=0}var W=2,J=0,ar=h,R=Z-P&32767;if(E>2&&p==C(g-R))for(var L=Math.min(u,E)-1,q=Math.min(32767,g),H=Math.min(258,E);R<=q&&--ar&&Z!=P;){if(r[g+W]==r[g+W-R]){for(var S=0;SW){if(W=S,J=R,S>L)break;for(var G=Math.min(R,S-2),rr=0,k=0;krr&&(rr=ur,P=er)}}}Z=P,P=w[Z],R+=Z-P&32767}if(J){O[B++]=268435456|Mr[W]<<18|Jr[J];var fr=Mr[W]&31,vr=Jr[J]&31;m+=hr[fr]+gr[vr],++$[257+fr],++U[vr],j=g+W,++A}else O[B++]=r[g],++$[r[g]]}}for(g=Math.max(g,j);g=i&&(l[t/8|0]=c,nr=i),t=Kr(l,t+1,r.subarray(g,nr))}v.i=i}return lr(s,0,a+Ur(t)+f)},ue=(function(){for(var r=new Int32Array(256),e=0;e<256;++e){for(var n=e,a=9;--a;)n=(n&1&&-306674912)^n>>>1;r[e]=n}return r})(),ce=function(){var r=-1;return{p:function(e){for(var n=r,a=0;a>>8;r=n},d:function(){return~r}}},he=function(r,e,n,a,f){if(!f&&(f={l:1},e.dictionary)){var v=e.dictionary.subarray(-32768),i=new M(v.length+r.length);i.set(v),i.set(r,v.length),r=i,f.w=v.length}return se(r,e.level==null?6:e.level,e.mem==null?f.l?Math.ceil(Math.max(8,Math.min(13,Math.log(r.length)))*1.5):20:12+e.mem,n,a,f)},Xr=function(r,e){var n={};for(var a in r)n[a]=r[a];for(var a in e)n[a]=e[a];return n},Q=function(r,e){return r[e]|r[e+1]<<8},Y=function(r,e){return(r[e]|r[e+1]<<8|r[e+2]<<16|r[e+3]<<24)>>>0},kr=function(r,e){return Y(r,e)+Y(r,e+4)*4294967296},N=function(r,e,n){for(;n;++e)r[e]=n,n>>>=8};function ge(r,e){return he(r,e||{},0,0)}function we(r,e){return ie(r,{i:2},e&&e.out,e&&e.dictionary)}var Yr=function(r,e,n,a){for(var f in r){var v=r[f],i=e+f,s=a;Array.isArray(v)&&(s=Xr(a,v[1]),v=v[0]),v instanceof M?n[i]=[v,s]:(n[i+="/"]=[new M(0),s],Yr(v,i,n,a))}},Ir=typeof TextEncoder<"u"&&new TextEncoder,Ar=typeof TextDecoder<"u"&&new TextDecoder,me=0;try{Ar.decode(Rr,{stream:!0}),me=1}catch{}var xe=function(r){for(var e="",n=0;;){var a=r[n++],f=(a>127)+(a>223)+(a>239);if(n+f>r.length)return{s:e,r:lr(r,n-1)};f?f==3?(a=((a&15)<<18|(r[n++]&63)<<12|(r[n++]&63)<<6|r[n++]&63)-65536,e+=String.fromCharCode(55296|a>>10,56320|a&1023)):f&1?e+=String.fromCharCode((a&31)<<6|r[n++]&63):e+=String.fromCharCode((a&15)<<12|(r[n++]&63)<<6|r[n++]&63):e+=String.fromCharCode(a)}};function cr(r,e){var n;if(Ir)return Ir.encode(r);for(var a=r.length,f=new M(r.length+(r.length>>1)),v=0,i=function(c){f[v++]=c},n=0;nf.length){var s=new M(v+8+(a-n<<1));s.set(f),f=s}var l=r.charCodeAt(n);l<128||e?i(l):l<2048?(i(192|l>>6),i(128|l&63)):l>55295&&l<57344?(l=65536+(l&1047552)|r.charCodeAt(++n)&1023,i(240|l>>18),i(128|l>>12&63),i(128|l>>6&63),i(128|l&63)):(i(224|l>>12),i(128|l>>6&63),i(128|l&63))}return lr(f,0,v)}function jr(r,e){if(e){for(var n="",a=0;a65535&&I(9),e+=a+4}return e},Pr=function(r,e,n,a,f,v,i,s){var l=a.length,c=n.extra,t=s&&s.length,o=zr(c);N(r,e,i!=null?33639248:67324752),e+=4,i!=null&&(r[e++]=20,r[e++]=n.os),r[e]=20,e+=2,r[e++]=n.flag<<1|(v<0&&8),r[e++]=f&&8,r[e++]=n.compression&255,r[e++]=n.compression>>8;var u=new Date(n.mtime==null?Date.now():n.mtime),h=u.getFullYear()-1980;if((h<0||h>119)&&I(10),N(r,e,h<<25|u.getMonth()+1<<21|u.getDate()<<16|u.getHours()<<11|u.getMinutes()<<5|u.getSeconds()>>1),e+=4,v!=-1&&(N(r,e,n.crc),N(r,e+4,v<0?-v-2:v),N(r,e+8,n.size)),N(r,e+12,l),N(r,e+14,o),e+=16,i!=null&&(N(r,e,t),N(r,e+6,n.attrs),N(r,e+10,i),e+=14),r.set(a,e),e+=l,o)for(var y in c){var w=c[y],x=w.length;N(r,e,+y),N(r,e+2,x),r.set(w,e+4),e+=4+x}return t&&(r.set(s,e),e+=t),e},Se=function(r,e,n,a,f){N(r,e,101010256),N(r,e+8,n),N(r,e+10,n),N(r,e+12,a),N(r,e+16,f)};function Me(r,e){e||(e={});var n={},a=[];Yr(r,"",n,e);var f=0,v=0;for(var i in n){var s=n[i],l=s[0],c=s[1],t=c.level==0?0:8,o=cr(i),u=o.length,h=c.comment,y=h&&cr(h),w=y&&y.length,x=zr(c.extra);u>65535&&I(11);var T=t?ge(l,c):l,z=T.length,C=ce();C.p(l),a.push(Xr(c,{size:l.length,crc:C.d(),c:T,f:o,m:y,u:u!=i.length||y&&h.length!=w,o:f,compression:t})),f+=30+u+x+z,v+=76+2*(u+x)+(w||0)+z}for(var O=new M(v+22),$=f,U=v-f,A=0;A65558)&&I(13);var f=Q(r,a+8);if(!f)return{};var v=Y(r,a+16),i=v==4294967295||f==65535;if(i){var s=Y(r,a-12);i=Y(r,s)==101075792,i&&(f=Y(r,s+32),v=Y(r,s+48))}for(var l=0;l|]/g,"_")||"Projekt",n={};for(const l of r.files)n[`${e}/${br(l.path)}`]=new Uint8Array(cr(l.text));const a=[...r.packages.map(l=>`${e}/${dr(l)}/${re(l).replace(/\./g,"/")}/`),...ee(r).map(l=>`${e}/${l}/`)];for(const l of a)Object.keys(n).some(c=>c.startsWith(l))||(n[l.replace(/\/\/$/,"/")]=new Uint8Array);return(r.uml&&Object.keys(r.uml).length||(f=r.notes)!=null&&f.length||(v=r.objects)!=null&&v.length)&&(n[`${e}/${$r}`]=new Uint8Array(cr(JSON.stringify({format:"visuobject",version:1,uml:r.uml??{},...(i=r.notes)!=null&&i.length?{notes:r.notes}:{},...(s=r.objects)!=null&&s.length?{objects:r.objects}:{}},null,2)))),Me(n,{level:6})}function ze(r,e){var c;const n=Ce(r),a=Object.entries(n).filter(([t])=>/\.java$/i.test(t)&&!t.startsWith("__MACOSX/")).map(([t,o])=>({name:t,text:jr(o)})),f=new Set(Object.keys(n).map(t=>t.split("/")[0])),v=f.size===1&&Object.keys(n).some(t=>t.includes("/"))?[...f][0]:e,i=v!==e,s=Br(v,[]);for(const t of a){const o=Br(v,[{name:t.name.slice(t.name.lastIndexOf("/")+1),text:t.text}]).files[0],u=t.name.split("/").slice(i?1:0,-1),h=Er(o.text).split(".").filter(Boolean),w=u.slice(u.length-h.length).join("/")===h.join("/")?u.slice(0,u.length-h.length).join("/"):"",x=w&&w!==mr&&w.split("/").every(Gr)&&!w.includes(":")?w:mr,T=Qr(Vr(x,Er(o.text)),o.path.slice(o.path.lastIndexOf("/")+1));s.files=[...s.files.filter(z=>z.path!==T),{...o,path:T}],x!==mr&&!((c=s.sourceFolders)!=null&&c.includes(x))&&(s.sourceFolders=[...s.sourceFolders??[],x])}const l=Object.entries(n).find(([t])=>t===$r||t.endsWith(`/${$r}`));if(l)try{const t=JSON.parse(jr(l[1])),o=Lr(t.uml);o&&(s.uml=o);const u=_r(t.notes);u&&(s.notes=u);const h=pr(t.objects);h&&(s.objects=h)}catch{}return s}export{ze as projectFromZip,je as projectToZip}; diff --git a/website/app/assets/pt-PT-aHTlqnII.js b/website/app/assets/pt-PT-aHTlqnII.js deleted file mode 100644 index 39a9874e..00000000 --- a/website/app/assets/pt-PT-aHTlqnII.js +++ /dev/null @@ -1,799 +0,0 @@ -const e=`/* Textos que só existem na versão web. Tudo o resto vem dos Localizable.strings da app. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Diagrama estruturado VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exportar…"; -"web_export_title" = "Guardar como imagem ou PDF, ou imprimir"; -"web_export_png" = "Imagem (PNG)"; -"web_export_svg" = "Gráfico vetorial (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Ficheiro VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Falha na exportação: %@"; -"web_print_hint" = "Use “Exportar… → PDF” para guardar o diagrama e imprimi-lo a partir daí."; -"web_delete" = "Eliminar"; -"web_language_title" = "Idioma da interface"; -"web_simulation_start" = "Iniciar simulação"; -"web_simulation_end" = "Terminar simulação"; -"web_simulation_start_title" = "Executar o diagrama passo a passo"; -"web_simulation_end_title" = "Terminar a simulação e continuar a editar"; -"web_palette_hint" = "Arrastar para o diagrama"; -"web_tabs_label" = "Diagramas"; -"web_status_modified" = "alterado"; -"web_inspector_title" = "Inspetor de objetos"; -"web_inspector_help_drop" = "Arraste elementos da paleta para um bloco: em cima = antes, em baixo = depois, sobre “ø” = substituir. Os elementos existentes podem ser movidos com o rato."; -"web_inspector_help_inline" = "Faça duplo clique ou toque duplo num elemento para editar o texto diretamente no diagrama (Enter aplica, Esc cancela). Os nomes dos casos também."; -"web_inspector_help_files" = "Também pode largar ficheiros na janela."; -"web_case_name" = "Nome do caso"; -"web_case_name_placeholder" = "p. ex. 1, ok, default"; -"web_copied" = "Elemento copiado."; -"web_discard_message_fmt" = "As alterações a “%@” serão perdidas."; -"web_err_unreadable_fmt" = "“%@” não é um ficheiro VisuStruct legível (%@)."; -"web_saved_fmt" = "“%@” guardado."; -"web_saved_txt_fmt" = "Guardado como “%@”. Remova a extensão .txt para o utilizar."; -"web_drop_impossible" = "Não é possível largar aqui"; -"web_sim_delay" = "Pausa por passo"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Linguagem"; -"web_codegen_comments" = "Texto do diagrama como comentários"; -"web_codegen_empty_else" = "Gerar ramo else vazio no código"; -"web_copied_check" = "Copiado ✓"; -"web_copy_manual" = "Selecionado, prima Ctrl+C para copiar"; -"web_run_stopped_loop" = "Execução interrompida: possível ciclo infinito."; -"web_run_cancelled" = "Execução cancelada."; -"web_run_error" = "Erro durante a execução."; -"web_run_dropped_fmt" = "… %d linhas anteriores ocultas"; -"web_learn_title" = "Aprender"; -"web_learn_hide" = "Recolher a área de aprendizagem"; -"web_learn_show" = "Expandir a área de aprendizagem: lições, exercícios e consulta"; -"web_learn_lessons" = "Lições"; -"web_learn_tasks" = "Exercícios"; -"web_learn_reference" = "Consulta"; -"web_learn_intro" = "Aprende passo a passo, com exercícios. O teu progresso fica guardado neste navegador."; -"web_learn_tasks_intro" = "Pequenos projetos sem instruções. Cada um indica a lição que deves terminar primeiro."; -"web_learn_step_of_fmt" = "Passo %d de %d"; -"web_learn_lesson_fmt" = "Lição %d"; -"web_learn_all_lessons" = "Todas as lições"; -"web_learn_all_tasks" = "Todos os exercícios"; -"web_learn_back" = "Voltar"; -"web_learn_next" = "Seguinte"; -"web_learn_finish" = "Terminar a lição"; -"web_learn_done" = "Concluída"; -"web_learn_after_fmt" = "Depois da lição %d: %@"; -"web_learn_level_fmt" = "Dificuldade %d de 3"; -"web_learn_exercise" = "Exercício"; -"web_learn_brag" = "Curiosidades com o Ludwig"; -"web_learn_solved" = "resolvido"; -"web_learn_open_exercise" = "Abrir no editor"; -"web_learn_solve_in_editor" = "Resolve este exercício na área %@."; -"web_learn_solve_in_code" = "Resolve este exercício na área %@."; -"web_learn_solve_in_both" = "Resolve este exercício nas áreas %1$@ e %2$@."; -"web_learn_open_in_editor" = "Abrir no editor"; -"web_learn_replay" = "Repetir animação"; -"web_learn_graphic_labels" = "👆 Clica na imagem para mostrar ou esconder as legendas"; -"web_learn_check" = "Verificar"; -"web_learn_hint" = "Dica"; -"web_learn_reset" = "Recomeçar"; -"web_learn_reset_confirm" = "Queres mesmo recomeçar?"; -"web_learn_solution" = "Mostrar a solução"; -"web_learn_hint_more" = "Outra dica"; -"web_learn_hint_fmt" = "Dica %d de %d"; -"web_learn_teacher" = "Para professores"; -"web_learn_teacher_intro" = "O código de professor mostra as soluções de todos os exercícios. O navegador memoriza-o."; -"web_learn_teacher_code" = "Código de professor"; -"web_learn_teacher_unlock" = "Desbloquear"; -"web_learn_teacher_wrong" = "O código não está correto."; -"web_learn_teacher_on" = "O modo de professor está ativo: as soluções de todos os exercícios estão visíveis."; -"web_learn_teacher_off" = "Sair do modo de professor"; -"web_learn_open_first" = "Abre primeiro o exercício no editor e constrói lá o teu estrutograma."; -"web_learn_empty" = "O teu estrutograma ainda está vazio. Arrasta primeiro elementos da área «Elementos» para ele."; -"web_learn_passed" = "Muito bem! O teu estrutograma faz exatamente o que deve."; -"web_learn_failed" = "Ainda não está bem. Compara a saída esperada com a tua:"; -"web_learn_missing_fmt" = "O teu estrutograma ainda precisa de: %@"; -"web_learn_input_fmt" = "Entrada: %@"; -"web_learn_no_input" = "Sem entrada"; -"web_learn_expected" = "Esperado"; -"web_learn_actual" = "A tua saída"; -"web_learn_missing_input" = "O teu estrutograma pede mais entradas do que o previsto."; -"web_learn_unused_input" = "O teu estrutograma não pede todas as entradas."; -"web_learn_missing_line_break" = "Quase! Só falta uma quebra de linha. System.out.print continua a escrever na mesma linha, System.out.println começa depois uma linha nova."; -"web_learn_too_many_steps" = "Parado após 100 000 passos. Há um ciclo que nunca termina?"; -"web_learn_score_fmt" = "%d de %d certas"; -"web_learn_cloze_hint" = "Escreve as palavras que faltam nos espaços e carrega em “Verificar”."; -"web_learn_cloze_score_fmt" = "%d de %d espaços certos"; -"web_learn_cloze_all_right" = "Todos os espaços certos!"; -"web_learn_cloze_solution" = "Mostrar solução"; -"web_learn_cloze_again" = "De novo"; -"web_learn_pap_hint" = "Toca num elemento à direita e depois no seu lugar no fluxograma. Também o podes arrastar. Tocar num lugar ocupado devolve o elemento."; -"web_learn_pap_score_fmt" = "%d de %d elementos certos"; -"web_learn_pap_all_right" = "Tudo certo! O teu fluxograma está pronto."; -"web_learn_pap_missing_fmt" = "Ainda faltam %d elementos."; -"web_learn_pap_empty" = "Lugar vazio"; -"web_learn_pap_yes" = "sim"; -"web_learn_pap_no" = "não"; -"web_learn_pap_legend_oval" = "Início ou fim"; -"web_learn_pap_legend_rect" = "Instrução"; -"web_learn_pap_legend_diamond" = "Decisão: pergunta com sim e não"; -"web_learn_pap_legend_para" = "Entrada ou saída"; -"web_learn_review" = "Rever"; -"web_learn_terms" = "Termos técnicos"; -"web_inspector_resize" = "Alterar a altura do inspetor de objetos: arrastar ou deslizar a linha para cima ou para baixo (duplo clique: padrão)"; -"web_learn_resize" = "Alterar a largura da área de aprendizagem (duplo clique: repor)"; -"web_learn_competences_intro" = "No fim desta lição consigo …"; -"web_learn_rating_sun" = "consigo com segurança"; -"web_learn_rating_cloud" = "em parte"; -"web_learn_rating_umbrella" = "ainda não"; -"web_learn_review_title" = "A tua lista de verificação em comparação"; -"web_learn_review_hint" = "Aqui a tua autoavaliação e o teste não coincidem. Lê o passo outra vez e volta a avaliar-te na lista de verificação."; -"web_learn_review_match" = "A tua autoavaliação corresponde ao teu teste. Muito bem!"; -"web_learn_review_overrated_fmt" = "Escolheste ☀️, mas a pergunta %@ estava errada."; -"web_learn_review_underrated" = "Todas as perguntas sobre isto estavam certas. Sabes mais do que pensas!"; -"web_learn_review_unrated" = "ainda não avaliado"; -"web_learn_back_to_checklist" = "Voltar à lista de verificação"; -"web_learn_step_fmt" = "Passo %d: %@"; -"web_sim_call_arguments_fmt" = "A chamada não corresponde ao cabeçalho de %@. Verifica os valores entre parênteses."; -"web_sim_missing_return_fmt" = "%@ terminou sem devolver um valor com return."; -"web_sim_file_not_found_fmt" = "Não existe nenhum ficheiro chamado %@."; -"web_sim_end_of_file_fmt" = "O ficheiro %@ não tem mais linhas."; -"web_sim_null_value_fmt" = "%@ é null."; -"web_sim_unknown_variable_case_fmt" = "Variável desconhecida: %1$@. O Java distingue maiúsculas de minúsculas: querias dizer %2$@?"; -"web_sim_written_file_fmt" = "%@ (guardado)"; -"web_site_home" = "Página inicial"; -"web_site_imprint" = "Contacto"; -"web_site_privacy" = "Privacidade"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Código"; -"web_live_hide" = "Ocultar código"; -"web_scale_canvas" = "Tamanho do texto: estrutograma"; -"web_scale_code" = "Tamanho do texto: Código"; -"web_live_indent" = "Indentar"; -"web_live_indent_title" = "Indentar e formatar o código: cada } na sua própria linha, espaços à volta dos operadores, quatro espaços por nível (linhas selecionadas, senão tudo)"; -"web_live_show" = "Mostrar código"; -"web_live_resize" = "Alterar a largura da área de código (duplo clique: repor)"; -"web_live_hint" = "O código cresce com o teu diagrama. Cinzento = marcador: escreve no inspetor de objetos e o teu texto aparece aí. Clica numa linha para selecionar o elemento."; -"web_live_ph_selector" = "variável"; -"web_live_ph_code" = "O teu código"; -"web_live_ph_end" = "fim"; -"web_live_language" = "Linguagem de programação"; -"web_live_java_class" = "Como classe"; -"web_live_java_class_hint" = "Nome da classe = título do diagrama"; -"web_live_java_main" = "Com método main"; -"web_settings_display" = "Apresentação"; -"web_live_swing" = "Java verdadeiro (com Swing)"; -"web_live_swing_hint" = "Java verdadeiro com janelas (CheerpJ). Descarrega cerca de 40 MB uma vez, na primeira execução."; -"web_live_swing_windows" = "Janelas Java"; -"web_live_swing_hide" = "Fechar janelas Java"; -"web_live_swing_loading" = "A carregar o Java (cerca de 40 MB na primeira vez) …"; -"web_live_swing_compiling" = "A compilar …"; -"web_live_swing_running" = "O programa está a correr."; -"web_live_swing_compile_failed" = "Erro de compilação, vê acima."; -"web_live_swing_failed" = "Não foi possível carregar o Java. Verifica a ligação à internet."; -"web_live_hint_edit" = "Escreve aqui código Java e o diagrama constrói-se sozinho. Ou trabalha no diagrama e o código cresce com ele. Cinzento = marcador, basta escrever por cima."; -"web_live_edit_label" = "Código Java"; -"web_parse_line_fmt" = "Linha %d: %@"; -"web_parse_missing" = "Ainda falta aqui: %@"; -"web_parse_extra" = "Está a mais: %@"; -"web_parse_unclosed_string" = "Falta aqui o %@ de fecho."; -"web_parse_else_without_if" = "Este “else” não tem um “if” correspondente."; -"web_parse_case_outside_switch" = "“case” e “default” pertencem a um “switch”."; -"web_parse_missing_do_while" = "Depois de “do { … }” ainda falta “while (condição);”."; -"web_parse_more_methods" = "Por agora só conta um método aqui. Cria outros subprogramas em separadores próprios."; -"web_parse_missing_import" = "Falta %@ mesmo no início. Sem esta linha, o Java não conhece o Scanner."; -"web_parse_missing_scanner" = "Falta antes %@ Sem esta linha, o Scanner não existe."; -"web_parse_unknown_type" = "O Java não conhece este tipo de dados. Querias dizer %@?"; -"web_quickfix_insert_fmt" = "Sugestão: inserir %@"; -"web_quickfix_replace_fmt" = "Sugestão: %1$@ em vez de %2$@"; -"web_parse_wrong_case" = "O Java distingue maiúsculas de minúsculas. O correto é %@."; -"web_editor_title" = "Diagrama"; -"web_editor_show" = "Mostrar diagrama"; -"web_editor_hide" = "Ocultar diagrama"; -"web_keywords_title" = "Palavras-chave do código"; -"web_keywords_hint" = "if, while, for em vez de Decisão, Ciclo …"; -"web_palette_compact" = "Elementos sempre compactos"; -"web_palette_compact_hint" = "Símbolo com o nome pequeno por baixo; caso contrário, só com a coluna estreita"; -"web_about_version_fmt" = "Versão %@"; -"web_about_software" = "Software utilizado"; -"web_about_software_text" = "O editor de código usa o CodeMirror e o Lezer (licença MIT). Com o suporte para Swing, o VisuStruct carrega o CheerpJ da Leaning Technologies (licença Community) e o javac do OpenJDK."; -"web_about_licenses" = "Textos das licenças"; -"web_live_run" = "Executar"; -"web_live_run_title" = "Executar o programa: as saídas e entradas aparecem em baixo"; -"web_live_run_finished" = "Programa terminado."; -"web_live_run_stopped" = "Parado."; -"web_console" = "Consola"; -"web_console_hide" = "Ocultar consola"; -"web_console_resize" = "Alterar a altura da consola"; -"web_diagram_tools" = "Ferramentas do diagrama"; -"web_files_title" = "Ficheiros"; -"web_files_show" = "Mostrar ficheiros"; -"web_files_hide" = "Ocultar ficheiros"; -"web_files_resize" = "Alterar a largura dos ficheiros"; -"web_files_new_folder" = "Nova pasta"; -"web_files_switch" = "Escolher outra pasta"; -"web_files_delete" = "Eliminar"; -"web_files_in_browser" = "No armazenamento deste navegador"; -"web_files_save_hint_fmt" = "Guardar coloca o programa em %@."; -"web_files_empty" = "Ainda nada guardado. Ao guardar, o VisuStruct cria aqui uma pasta com a data de hoje."; -"web_files_reconnect_hint" = "Depois de recarregar, o navegador pergunta outra vez se o VisuStruct pode usar a tua pasta."; -"web_files_reconnect_fmt" = "Abrir a pasta “%@”"; -"web_files_connect_hint" = "Escolhe uma pasta como área de trabalho, de preferência uma pasta nova “VisuStruct-Ficheiros” nos teus Documentos. O VisuStruct cria lá uma pasta para cada dia e guarda o diagrama e o código."; -"web_files_connect" = "Escolher pasta"; -"web_files_browser_hint" = "Este navegador não consegue abrir pastas no dispositivo (por exemplo no iPad). Os teus ficheiros ficam no armazenamento deste navegador."; -"web_files_browser" = "Guardar no navegador"; -"web_files_delete_confirm_fmt" = "Eliminar mesmo “%@”?"; -"web_files_folder_prompt" = "Nome da nova pasta:"; -"web_files_default_name" = "Programa"; -"web_files_saved_fmt" = "Guardado: %@"; -"web_files_create" = "Criar"; -"web_learn_ludwig_tip" = "Dica do Ludwig"; -"web_game_title" = "Jogo: Verdadeiro ou falso?"; -"web_game_intro" = "O Ludwig mostra-te valores e uma condição. A condição é verdadeira (true) ou falsa (false)? Dez rondas, cada vez mais difíceis."; -"web_game_start" = "Começar o jogo"; -"web_game_round_fmt" = "Ronda %@ de %@"; -"web_game_score_fmt" = "Pontos: %@"; -"web_game_true" = "verdadeiro (true)"; -"web_game_false" = "falso (false)"; -"web_game_right" = "Certo!"; -"web_game_wrong" = "Não exatamente. É assim que se calcula:"; -"web_game_next" = "Seguinte"; -"web_game_finish" = "Resultado"; -"web_game_done_fmt" = "%@ de %@ certas."; -"web_game_perfect" = "Tudo certo – o Ludwig está mesmo impressionado!"; -"web_game_good" = "Muito bem! Revê os teus erros e tenta outra vez."; -"web_game_practice" = "A prática faz o mestre. Lê outra vez a tabela de verdade acima e joga mais uma ronda."; -"web_game_again" = "Jogar outra vez"; -"web_game_wrong_short" = "Infelizmente errado."; -"web_game_practice_short" = "A prática leva à perfeição. Lê outra vez as explicações e joga mais uma ronda."; -"web_game_clicks" = "Cliques:"; -"web_game_nobody" = "Ninguém está a ouvir – não acontece nada."; -"web_game_event_fmt" = "Clique em “%@”: o ouvinte ouve e executa o seu código."; -"web_game_try" = "Agora experimenta tu os botões da janela."; -"web_game_time_fmt" = "Faltam %@ s"; -"web_game_defused" = "✂️ Desativada! Encontraste o erro."; -"web_game_boom_time" = "💥 Bum! O tempo acabou."; -"web_game_boom_wrong" = "💥 Bum! Era a linha errada."; -"web_game_boom_part" = "💥 Bum! A linha está certa, mas o erro está noutra parte dela."; -"web_game_boom_wrong_part" = "💥 Bum! O erro está noutra linha."; -"web_game_bomb_parts_hint" = "Toca exatamente na parte errada."; -"web_game_fix" = "A linha correta:"; -"web_game_order_hint" = "Toca nos passos pela ordem certa."; -"web_debug_step" = "Passo a passo"; -"web_debug_step_title" = "Passo a passo: executar uma instrução e parar outra vez"; -"web_debug_continue" = "Continuar até ao próximo ponto de paragem"; -"web_debug_paused_fmt" = "Parado antes da linha %d."; -"web_debug_gutter_title" = "Clica num número de linha para pôr ou tirar um ponto de paragem"; - -/* Lernbereich: Caesar-Scheibe */ -"web_learn_caesar_title" = "Disco de César"; -"web_learn_caesar_hint" = "Roda o disco interior ou toca em −1 e +1. Por fora está o texto simples, por dentro o texto cifrado."; -"web_learn_caesar_key_fmt" = "Chave %d"; -"web_learn_caesar_plain" = "Texto simples"; -"web_learn_caesar_secret" = "Texto cifrado"; -"web_learn_caesar_encrypt" = "Cifrar"; -"web_learn_caesar_decrypt" = "Decifrar"; -"web_learn_caesar_back" = "Rodar o disco uma letra para trás"; -"web_learn_caesar_forward" = "Rodar o disco uma letra para a frente"; -"web_learn_rescue_station_fmt" = "Estação %@ de %@: %@"; -"web_learn_rescue_number" = "O teu número"; -"web_learn_rescue_not_number" = "Escreve um número inteiro."; -"web_learn_rescue_wrong" = "Ainda não. Lê o código outra vez, carácter a carácter."; -"web_learn_rescue_letter_moved" = "A letra vai para as palavras cruzadas."; -"web_learn_rescue_alphabet" = "O teu número transforma-se numa letra:"; -"web_learn_rescue_to_crossword" = "Para as palavras cruzadas"; -"web_learn_rescue_crossword" = "As palavras cruzadas"; -"web_learn_rescue_across" = "→ Horizontais"; -"web_learn_rescue_down" = "↓ Verticais"; -"web_learn_rescue_cell_fmt" = "Quadrado linha %@, coluna %@"; -"web_learn_rescue_keyword" = "Palavra secreta:"; -"web_learn_rescue_cw_wrong" = "Os quadrados marcados a vermelho ainda não estão certos."; -"web_learn_rescue_cw_done" = "Resolvido! Os quadrados amarelos formam a palavra secreta."; -"web_learn_rescue_to_gate" = "Para o portão"; -"web_learn_rescue_gate" = "O portão"; -"web_learn_rescue_word" = "Palavra secreta"; -"web_learn_rescue_open" = "Abrir o portão"; -"web_learn_rescue_gate_case" = "Quase! O equals distingue maiúsculas de minúsculas. Escreve a palavra exatamente como nas palavras cruzadas."; -"web_learn_rescue_gate_wrong" = "O portão continua fechado. Olha outra vez para os quadrados amarelos."; -"web_learn_rescue_freed" = "🎉 O portão está aberto!"; - -/* VisuBot (mBot2-Modus), nur de/en/pt */ -"web_visubot_world_title" = "Com o que queres trabalhar?"; -"web_visubot_world_visustruct_hint" = "VisuStruct: estrutogramas e Java com o curso completo. Podes mudar a qualquer momento na roda dentada."; -"web_visubot_world_visubot_hint" = "VisuBot: programar o robô mBot2, com lições e tarefas próprias. Podes mudar a qualquer momento na roda dentada."; -"web_visubot_connect" = "Ligar o mBot2 real"; -"web_visubot_upload_button" = "Transferir"; -"web_visubot_blocks" = "Blocos"; -"web_visubot_blocks_title" = "Mostrar o diagrama como blocos"; -"web_visubot_blocks_back" = "Diagrama"; -"web_visubot_blocks_back_title" = "Voltar ao diagrama"; -"web_visubot_blocks_start" = "Quando o programa começa"; -"web_visubot_blocks_empty" = "Aqui vai um bloco"; -"web_visubot_blocks_hint" = "O mesmo programa em blocos. Arrasta blocos de “Elementos”, muda-os de lugar ou deita-os no lixo. Clica num número ou texto nos campos brancos para o alterar no próprio bloco; o resto altera-se em baixo, no Inspetor de objetos. Arrasta os sensores para uma condição ou mesmo para o campo que devem substituir. Arrasta operadores e variáveis da mesma forma para um campo ou uma condição."; -"web_visubot_blocks_trash" = "Lixo"; -"web_visubot_blocks_count" = "contar com"; -"web_visubot_blocks_count_from" = "de"; -"web_visubot_blocks_count_below" = "até abaixo de"; -"web_visubot_blocks_count_to" = "até"; -"web_visubot_blocks_count_above" = "até acima de"; -"web_visubot_blocks_count_step" = "em passos de"; -"web_visubot_blocks_repeat" = "repetir"; -"web_visubot_blocks_if" = "se"; -"web_visubot_blocks_else" = "senão"; -"web_visubot_blocks_while" = "enquanto"; -"web_visubot_blocks_endless" = "repetir sempre"; -"web_visubot_blocks_do" = "faz"; -"web_visubot_blocks_not" = "não"; -"web_visubot_blocks_and" = "e"; -"web_visubot_blocks_or" = "ou"; -"web_visubot_blocks_break" = "sair do ciclo"; -"web_visubot_blocks_repeat_times" = "vezes"; -"web_visubot_blocks_random_from" = "número aleatório de"; -"web_visubot_blocks_random_to" = "a"; -"web_visubot_operators" = "Operadores"; -"web_visubot_variables" = "Variáveis"; -"web_visubot_control" = "Controlo"; -"web_visubot_el_repeat" = "repetir 10 vezes"; -"web_visubot_upload_title" = "Transferir o programa para o mBot2"; -"web_visubot_upload_step1" = "Liga o mBot2 com o cabo USB e acende-o. Só funciona no Chrome ou no Edge."; -"web_visubot_upload_step2" = "Clica em “Transferir agora” e escolhe “USB Serial”. O mBot2 guarda o teu programa e reinicia."; -"web_visubot_upload_step3" = "Desliga o cabo e põe o mBot2 na partida. Quando carregas no botão A, o programa arranca. Fica guardado, mesmo depois de desligar."; -"web_visubot_upload_start" = "Transferir agora"; -"web_visubot_upload_busy" = "A transferir … não desligues o cabo."; -"web_visubot_upload_done" = "Pronto! Desliga o cabo, põe o mBot2 na partida e carrega no botão A."; -"web_visubot_upload_failed" = "A transferência não funcionou. Reinicia o mBot2 e tenta outra vez."; -"web_visubot_upload_label" = "A = Início"; -"web_visubot_upload_error_input" = "O teu programa pede uma entrada. O mBot2 não tem teclado, por isso não funciona."; -"web_visubot_upload_error_files" = "O teu programa trabalha com ficheiros. Isso não funciona no mBot2."; -"web_visubot_upload_error_empty" = "O teu programa ainda está vazio."; -"web_visubot_disconnect" = "Desligar"; -"web_visubot_connecting" = "A ligar … (o CyberPi reinicia brevemente)"; -"web_visubot_connected" = "O mBot2 real conduz também"; -"web_visubot_no_serial" = "mBot2 real: só no Chrome ou Edge num computador."; -"web_visubot_link_error" = "Ligação perdida"; -"web_visubot_world" = "Mundo VisuBot"; -"web_visubot_no_3d" = "A vista 3D não consegue arrancar neste navegador."; -"web_visubot_distance" = "Distância"; -"web_visubot_line" = "Linha"; -"web_visubot_bumps" = "bateu na parede"; -"web_visubot_el_forward" = "andar para a frente"; -"web_visubot_el_backward" = "andar para trás"; -"web_visubot_el_right" = "rodar à direita"; -"web_visubot_el_left" = "rodar à esquerda"; -"web_visubot_el_led" = "LEDs ligados"; -"web_visubot_el_tone" = "tocar um som"; -"web_visubot_drive" = "Percurso"; -"web_visubot_depth_fmt" = "Os ciclos têm de estar encaixados %d vezes."; -"web_visubot_tap_part" = "Roda o mBot2 com o rato ou o dedo. Toca numa peça para ver o nome."; -"web_visubot_quiz_right" = "Certo!"; -"web_visubot_quiz_wrong_fmt" = "Isso é: %@. Tenta outra vez."; -"web_visubot_part_cyberpi" = "CyberPi"; -"web_visubot_part_screen" = "Ecrã"; -"web_visubot_part_joystick" = "Joystick"; -"web_visubot_part_buttons" = "Botões A, B e Home"; -"web_visubot_part_leds" = "LEDs RGB"; -"web_visubot_part_lightSensor" = "Sensor de luz"; -"web_visubot_part_microphone" = "Microfone"; -"web_visubot_part_shield" = "Shield do mBot2 com bateria"; -"web_visubot_part_powerSwitch" = "Interruptor"; -"web_visubot_part_motors" = "Motores com encoder"; -"web_visubot_part_wheels" = "Rodas"; -"web_visubot_part_ultrasonic" = "Sensor ultrassónico"; -"web_visubot_part_lineSensor" = "Sensor de linha (quad RGB)"; -"web_visubot_part_caster" = "Roda livre"; -"web_visubot_part_chassis" = "Chassi"; -"web_visubot_world_fmt" = "Mundo %d"; -"web_visubot_goal_reached" = "Objetivo alcançado"; -"web_visubot_goal_missed" = "Objetivo não alcançado"; -"web_visubot_commands" = "Comandos do mBot2"; -"web_visubot_sensors" = "Sensores do mBot2"; -"web_visubot_sensor_distance" = "Distância"; -"web_visubot_sensor_line" = "Linha"; -"web_visubot_el_led_off" = "LEDs desligados"; -"web_visubot_el_led_color" = "cor do LED"; -"web_visubot_el_wait" = "esperar um pouco"; -"web_visubot_el_emotion" = "mostrar um sentimento"; -"web_visubot_el_text" = "mostrar texto"; -"web_visubot_sensor_color" = "A cor é vermelha?"; -"web_visubot_sensor_button_a" = "Botão A premido?"; -"web_visubot_sensor_button_b" = "Botão B premido?"; -"web_visubot_sensor_joy_up" = "Joystick para cima?"; -"web_visubot_sensor_joy_down" = "Joystick para baixo?"; -"web_visubot_sensor_joy_left" = "Joystick para a esquerda?"; -"web_visubot_sensor_joy_right" = "Joystick para a direita?"; -"web_visubot_sensor_brightness" = "Luminosidade"; -"web_visubot_sensor_loudness" = "Volume do som"; -"web_visubot_sensor_pitch" = "Inclinação frente/trás"; -"web_visubot_sensor_roll" = "Inclinação lateral"; -"web_visubot_sensor_shake" = "Abanado?"; -"web_visubot_el_drive" = "rodas separadas"; -"web_visubot_el_stop" = "parar"; -"web_visubot_el_timer" = "cronómetro a 0"; -"web_visubot_sensor_line_left" = "Linha à esquerda?"; -"web_visubot_sensor_line_right" = "Linha à direita?"; -"web_visubot_sensor_timer" = "Cronómetro (s)"; -"web_visubot_sensor_battery" = "Bateria (%)"; -"web_visubot_actions" = "Comandos"; -"web_visubot_led_on" = "LEDs ligados"; -"web_visubot_led_off" = "LEDs desligados"; -"web_visubot_tone_yes" = "som"; -"web_visubot_tone_no" = "sem som"; -"web_visubot_controls" = "Experimentar os sensores"; -"web_visubot_controls_hint" = "Sem um mBot2 real: define aqui o que os sensores medem. Botões, joystick e abanar duram um segundo."; -"web_visubot_shake" = "abanar"; -"web_visubot_check" = "Testar o mBot2"; -"web_visubot_check_hint" = "Experimenta cada atuador uma vez. Os valores dos sensores atualizam-se em direto: põe a mão à frente do sensor ultrassónico, carrega nos botões, inclina ou abana o mBot2. Levanta-o para testar as rodas."; -"web_visubot_check_forward" = "Rodas 1 s para a frente"; -"web_visubot_check_spin" = "Rodar no lugar 1 s"; - -/* Start: Ordner für Lernstand und Programme */ -"web_learn_start_title" = "Onde guardas o teu trabalho?"; -"web_learn_start_hint" = "O teu progresso e os teus programas ficam numa pasta no teu dispositivo. O progresso fica no ficheiro learnProgress.json."; -"web_learn_start_continue_fmt" = "Continuar com “%@”"; -"web_learn_start_open" = "Abrir pasta existente"; -"web_learn_start_create_fmt" = "Criar nova pasta “%@”"; -"web_learn_start_create_hint" = "Depois escolhes onde fica, por exemplo em “Documentos”."; -"web_learn_start_folder" = "VisuStruct-Ficheiros"; -"web_learn_start_without" = "Continuar sem pasta"; -"web_learn_start_without_hint" = "Assim o teu progresso perde-se ao fechar a página."; -"web_visubot_preview_title" = "mBot2 ao vivo"; -"web_visubot_preview_hint" = "Mostra logo para onde o teu programa leva o mBot2."; -"web_visubot_preview_sim_title" = "Simulação"; -"web_visubot_preview_sim_hint" = "O mBot2 anda aqui enquanto a simulação corre."; -"web_visubot_world_in_preview" = "O mBot2 anda na janela «Simulação»."; -"web_visubot_preview_play" = "Reproduzir o percurso"; -"web_visubot_preview_stop" = "Parar"; -"web_visubot_preview_hide" = "Recolher mBot2 ao vivo"; -"web_visubot_preview_show" = "Mostrar mBot2 ao vivo"; -"web_visubot_preview_resize" = "Alterar o tamanho de mBot2 ao vivo"; -"web_visubot_preview_stack" = "Por baixo do código na área Código"; -"web_visubot_preview_side" = "Como coluna própria"; -"web_visubot_preview_distance_fmt" = "Percurso: %d cm"; -"web_visubot_preview_input" = "precisa de uma entrada, inicia a simulação para isso"; -"web_visubot_preview_limit" = "demora muito (ciclo infinito?), mostra-se o início"; -"web_visubot_group_drive" = "Andar e rodar"; -"web_visubot_group_wheels" = "Rodas separadas"; -"web_visubot_group_leds" = "Luz"; -"web_visubot_group_sound" = "Som"; -"web_visubot_group_screen" = "Ecrã"; -"web_visubot_group_emotion" = "Emoções"; -"web_visubot_group_wait" = "Esperar e cronómetro"; -"web_visubot_group_distance" = "Distância"; -"web_visubot_group_line" = "Linha"; -"web_visubot_group_color" = "Cores"; -"web_visubot_group_buttons" = "Botões e joystick"; -"web_visubot_group_light" = "Luz e volume"; -"web_visubot_group_tilt" = "Inclinação e abanar"; -"web_visubot_group_timer" = "Cronómetro e bateria"; -"web_swap_to_code" = "Código grande, diagrama estreito"; -"web_swap_to_editor" = "Diagrama grande, código estreito"; -"web_editor_resize" = "Alterar a largura do diagrama"; -"web_palette_resize" = "Alterar a largura dos elementos (duplo clique: repor)"; -"web_palette_hide" = "Ocultar elementos"; -"web_palette_show" = "Mostrar elementos"; - -/* VisuObject */ -"web_visuobject_world_hint" = "VisuObject: programar classes em Java e vê-las ou desenhá-las como diagrama de classes (a partir do 12.º ano). Podes mudar a qualquer momento na roda dentada."; -"web_visuobject_subtitle" = "Diagrama de classes"; -"web_visuobject_code" = "Código Java"; -"web_visuobject_diagram" = "Diagrama de classes"; -"web_visuobject_add_interface" = "Interface"; -"web_visuobject_add_interface_tip" = "Nova interface no diagrama e no código"; -"web_visuobject_layout" = "Reorganizar"; -"web_visuobject_layout_tip" = "Organizar todas as classes automaticamente: superclasses em cima"; -"web_visuobject_add_field" = "novo atributo …"; -"web_visuobject_add_method" = "novo método …"; -"web_visuobject_delete_class" = "Apagar classe"; -"web_visuobject_new_class_name" = "NovaClasse"; -"web_visuobject_new_interface_name" = "NovaInterface"; -"web_visuobject_example" = "Exemplo"; -"web_visuobject_example_tip" = "Carregar o projeto de exemplo Zoo: Tier e Hund no package tiere, Besitzer em personen"; -"web_visuobject_new_tip" = "Novo: projeto vazio (Anular recupera o anterior)"; -"web_visuobject_empty" = "Ainda não há nenhuma classe. Clica em «+ Classe» à esquerda ou escreve, por exemplo, «public class Cao { }» num ficheiro."; -"web_visuobject_edit_hint" = "Duplo clique para alterar. Escreve como em UML: «- nome: String» ou «+ ladrar(): void». Deixar vazio apaga."; -"web_visuobject_member_label" = "Atributo ou método em notação UML"; -"web_visuobject_name_label" = "Nome da classe"; -"web_visuobject_bad_name" = "«%@» não é um nome válido ou já existe."; -"web_visuobject_bad_line" = "Não percebo «%@». Exemplo: - nome: String ou + ladrar(): void"; -"web_visuobject_open_failed" = "Não foi possível ler o ficheiro."; -"web_visuobject_field_ph" = "- nome: String"; -"web_visuobject_seq_diagram" = "Diagrama de sequência"; -"web_visuobject_seq_empty" = "O diagrama de sequência surge ao depurar: clica em “Depurar” em cima e percorre o programa passo a passo."; -"web_visuobject_seq_no_calls" = "Ainda não há chamadas dos teus métodos."; -"web_visuobject_seq_truncated" = "… encurtado: a partir daqui há chamadas demais."; -"web_visuobject_seq_hint" = "Cada seta é uma chamada de um dos teus métodos; a seta tracejada traz o valor de retorno. Clica numa seta para ver a linha no código."; -"web_visuobject_dbg" = "Depurar"; -"web_visuobject_dbg_tip" = "Executar o programa passo a passo. Ponto de paragem: clica no número da linha. O diagrama de objetos mostra os objetos na memória."; -"web_visuobject_dbg_continue" = "Continuar"; -"web_visuobject_dbg_continue_tip" = "Continuar até ao próximo ponto de paragem (F8)"; -"web_visuobject_dbg_pause" = "Pausa"; -"web_visuobject_dbg_pause_tip" = "Pausar o programa"; -"web_visuobject_dbg_over" = "Passo"; -"web_visuobject_dbg_over_tip" = "Próxima linha; os métodos correm até ao fim (F6)"; -"web_visuobject_dbg_into" = "Entrar"; -"web_visuobject_dbg_into_tip" = "Entrar no método chamado (F5)"; -"web_visuobject_dbg_out" = "Sair"; -"web_visuobject_dbg_out_tip" = "Terminar o método e voltar a quem o chamou (F7)"; -"web_visuobject_dbg_input" = "Entrada para o Scanner, depois Enter"; -"web_visuobject_dbg_running" = "Depurador: o programa está a correr …"; -"web_visuobject_dbg_waiting" = "Depurador: o programa espera pela tua entrada."; -"web_visuobject_dbg_paused_fmt" = "Depurador: parado em %1$@, linha %2$@"; -"web_visuobject_dbg_exception" = "O programa terminou com uma exceção."; -"web_visuobject_dbg_code_changed" = "Código alterado: depurador terminado."; -"web_visuobject_dbg_line_fmt" = "%1$@:%2$@"; -"web_visuobject_dbg_stack" = "Pilha de chamadas"; -"web_visuobject_dbg_vars" = "Variáveis"; -"web_visuobject_dbg_var_name" = "Nome"; -"web_visuobject_dbg_var_type" = "Tipo"; -"web_visuobject_dbg_no_vars" = "Sem variáveis"; -"web_visuobject_dbg_err_syntax" = "%1$@, linha %2$@: o depurador não entende isto (%3$@). ▶ Executar mostra a mensagem do Java."; -"web_visuobject_dbg_err_symbol" = "%1$@, linha %2$@: %3$@ é desconhecido."; -"web_visuobject_dbg_err_import" = "%1$@, linha %2$@: falta %3$@ mesmo no início. Sem esta linha, o Java não conhece a classe."; -"web_visuobject_dbg_err_typename" = "%1$@, linha %2$@: o Java não conhece este tipo de dados. Querias dizer %3$@?"; -"web_visuobject_dbg_err_unsupported" = "%1$@, linha %2$@: o depurador ainda não consegue %3$@. ▶ Executar corre o programa em Java real."; -"web_visuobject_dbg_err_type" = "%1$@, linha %2$@: algo não encaixa aqui (%3$@)."; -"web_visuobject_obj_diagram" = "Diagrama de objetos"; -"web_visuobject_obj_new" = "Novo objeto da classe"; -"web_visuobject_obj_add" = "Objeto"; -"web_visuobject_obj_empty" = "Ainda sem objetos. Escolhe uma classe em cima e clica em “+ Objeto”."; -"web_visuobject_obj_no_classes" = "Cria primeiro uma classe (não abstrata) no diagrama de classes."; -"web_visuobject_obj_hint" = "Clica num objeto para mudar aqui o nome, a classe e os valores. Arrasta a pega até outro objeto para os ligar através de um atributo de referência."; -"web_visuobject_obj_live_hint" = "Ao vivo do depurador: é assim que os objetos estão agora na memória."; -"web_visuobject_obj_link" = "Arrastar até outro objeto: definir referência"; -"web_visuobject_obj_name" = "Nome"; -"web_visuobject_obj_class" = "Classe"; -"web_visuobject_obj_attribute" = "Atributo"; -"web_visuobject_obj_value" = "Valor"; -"web_visuobject_obj_delete" = "Apagar objeto"; -"web_visuobject_obj_bad_name" = "“%@” não é um nome válido para um objeto."; -"web_visuobject_obj_name_taken" = "Já existe um objeto “%@”."; -"web_visuobject_obj_no_ref" = "%1$@ não tem nenhum atributo que possa apontar para %2$@."; -"web_visuobject_tool_note" = "Nota"; -"web_visuobject_tool_note_tip" = "Nota: clica no diagrama de classes e escreve o texto. Arrasta a pega até uma classe para uma linha tracejada."; -"web_visuobject_note_ph" = "Nota …"; -"web_visuobject_note_text" = "Texto"; -"web_visuobject_note_attached" = "Ligada a"; -"web_visuobject_note_attach" = "Arrastar até uma classe: linha tracejada"; -"web_visuobject_note_delete" = "Apagar nota"; -"web_visuobject_run" = "Executar"; -"web_visuobject_run_tip" = "Executar o programa com Java verdadeiro: todas as classes são compiladas, é iniciada a classe com main (de preferência a aberta)."; -"web_visuobject_stop" = "Parar"; -"web_visuobject_stop_tip" = "Terminar o programa e fechar as suas janelas"; -"web_visuobject_stopped" = "Programa terminado."; -"web_visuobject_no_main" = "Nenhuma classe com public static void main(String[] args) encontrada."; -"web_visuobject_running_fmt" = "%@ está a correr …"; -"web_visuobject_console_clear" = "Limpar"; -"web_visuobject_section_fields" = "Atributos"; -"web_visuobject_section_refs" = "Atributos de referência"; -"web_visuobject_section_ctors" = "Construtor"; -"web_visuobject_section_methods" = "Métodos"; -"web_visuobject_insp_sections" = "Inserir comentários de secção"; -"web_visuobject_insp_sort" = "Ordenar por secções"; -"web_visuobject_insp_sections_tip" = "Escreve // Atributos, // Atributos de referência, // Construtor e // Métodos na classe e ordena tudo por baixo. O que for criado no diagrama de classes vai logo para o sítio certo."; -"web_visuobject_insp_doc" = "Descrição (Javadoc)"; -"web_visuobject_insp_doc_ph" = "Para que serve a classe? Fica no código como comentário Javadoc por cima da classe."; -"web_visuobject_insp_visibility" = "Visibilidade"; -"web_visuobject_insp_type" = "Tipo"; -"web_visuobject_insp_init" = "Valor inicial"; -"web_visuobject_insp_no_fields" = "Ainda sem atributos."; -"web_visuobject_insp_add_field" = "Atributo"; -"web_visuobject_insp_params" = "Parâmetros"; -"web_visuobject_insp_return" = "Tipo de retorno"; -"web_visuobject_insp_constructor" = "Construtor"; -"web_visuobject_insp_no_methods" = "Ainda sem métodos."; -"web_visuobject_insp_add_method" = "Método"; -"web_visuobject_insp_generate" = "Gerar:"; -"web_visuobject_insp_gen_constructor" = "Construtor a partir dos atributos"; -"web_visuobject_insp_gen_constructor_tip" = "Como no Eclipse “Generate Constructor using Fields”: um construtor que define todos os atributos (exceto static)."; -"web_visuobject_insp_gen_accessors" = "Getters e setters"; -"web_visuobject_insp_gen_accessors_tip" = "Como no Eclipse “Generate Getters and Setters”: get…/set… para cada atributo (atributos final só com getter)."; -"web_visuobject_insp_bad_params" = "Escreve os parâmetros assim: nome: Tipo, segundo: int (lido “%@”)."; -"web_visuobject_insp_fields" = "Atributos"; -"web_visuobject_insp_methods" = "Métodos"; -"web_visuobject_inspector_resize" = "Redimensionar: arrasta a fronteira entre o diagrama de classes e o inspetor para cima ou para baixo (duplo clique = padrão)"; -"web_visuobject_dep_title" = "Dependência «%@»"; -"web_visuobject_dep_create_text" = "%1$@ cria um objeto da classe %2$@ em %3$@ (new)."; -"web_visuobject_dep_use_text" = "%1$@ usa a classe %2$@ em %3$@."; -"web_visuobject_dep_hint" = "Isto não é uma associação: %@ não guarda o objeto %@ num atributo, ele só existe dentro do método. Por isso a linha é tracejada."; -"web_visuobject_dep_to_assoc" = "Guardar como atributo (associação a %@)"; -"web_visuobject_end_mult_ph" = "mult."; -"web_visuobject_end_tip" = "Clique (linha selecionada) ou clique direito: papel, multiplicidade e navegabilidade nesta extremidade"; -"web_visuobject_end_role_ph" = "p. ex. osMeusGatos"; -"web_visuobject_new_menu" = "Novo"; -"web_visuobject_new_menu_tip" = "Criar um novo projeto, source folder, package, classe ou interface"; -"web_visuobject_new_project" = "Projeto Java"; -"web_visuobject_new_folder" = "Source folder"; -"web_visuobject_new_package" = "Package"; -"web_visuobject_new_class" = "Classe"; -"web_visuobject_new_interface" = "Interface"; -"web_visuobject_new_project_title" = "Novo projeto Java"; -"web_visuobject_new_project_hint" = "No workspace cada projeto tem uma pasta própria com src."; -"web_visuobject_new_folder_title" = "Novo source folder"; -"web_visuobject_new_folder_hint" = "Outra pasta para código ao lado de src, por exemplo test para classes de teste."; -"web_visuobject_new_package_title" = "Novo package"; -"web_visuobject_new_package_hint" = "Os nomes de package escrevem-se em minúsculas, partes com ponto: animais ou escola.secretaria."; -"web_visuobject_new_class_title" = "Nova classe Java"; -"web_visuobject_new_class_hint" = "Os nomes de classe começam com maiúscula, por exemplo Cao ou ContaBancaria."; -"web_visuobject_new_interface_title" = "Nova interface Java"; -"web_visuobject_new_interface_hint" = "Uma interface define métodos que as classes implementam depois."; -"web_visuobject_new_root" = "Source folder"; -"web_visuobject_new_name" = "Nome"; -"web_visuobject_new_modifiers" = "Modificadores"; -"web_visuobject_new_package_private" = "package (sem modificador)"; -"web_visuobject_new_superclass" = "Superclasse"; -"web_visuobject_new_interfaces" = "Interfaces (implements)"; -"web_visuobject_new_extends_interfaces" = "Estende interfaces (extends)"; -"web_visuobject_new_stubs" = "Que esqueletos de métodos criar?"; -"web_visuobject_new_stub_constructors" = "Construtores da superclasse"; -"web_visuobject_new_stub_inherited" = "Métodos abstratos herdados"; -"web_visuobject_new_comment_legend" = "Comentário"; -"web_visuobject_new_comment" = "Comentário Javadoc com @author"; -"web_visuobject_new_author" = "O teu nome"; -"web_visuobject_new_finish" = "Concluir"; -"web_visuobject_new_name_missing" = "Escreve um nome."; -"web_visuobject_new_exists" = "“%@” já existe no projeto."; -"web_visuobject_new_upper" = "Em Java, os nomes de classe começam com maiúscula."; -"web_visuobject_bad_folder" = "“%@” não é um nome de pasta válido ou já existe."; -"web_visuobject_inspector_hide" = "Recolher o inspetor de objetos"; -"web_visuobject_inspector_show" = "Expandir o inspetor de objetos"; -"web_visuobject_inspector_empty" = "Clica numa relação (linha) no diagrama para a editares aqui."; -"web_visuobject_rel_end_a" = "Extremidade A: %@ (a linha começa aqui)"; -"web_visuobject_rel_end_b" = "Extremidade B: %@ (a linha acaba aqui)"; -"web_visuobject_ws_title" = "Pasta do workspace no teu computador"; -"web_visuobject_ws_question" = "Onde guardas os teus projetos?"; -"web_visuobject_ws_hint" = "Como no Eclipse: uma pasta de workspace no teu computador, com uma pasta por projeto com src e os ficheiros .java."; -"web_visuobject_ws_create" = "Criar a pasta “%@”"; -"web_visuobject_ws_open" = "Abrir um workspace existente"; -"web_visuobject_ws_skip" = "Sem pasta (só no browser)"; -"web_visuobject_ws_choose" = "Escolher workspace"; -"web_visuobject_ws_reopen" = "Abrir o workspace “%@”"; -"web_visuobject_ws_switch" = "Mudar"; -"web_visuobject_ws_zip" = "ZIP"; -"web_visuobject_ws_zip_tip" = "Descarregar o projeto como .zip, por exemplo para o entregar"; -"web_visuobject_ws_saved" = "Guardado em %@/%@"; -"web_visuobject_ws_created" = "Novo projeto “%@” no workspace"; -"web_visuobject_ws_failed" = "Não foi possível guardar no workspace: %@"; -"web_visuobject_ws_taken" = "O workspace já tem um projeto “%@”."; -"web_visuobject_ws_open_project" = "Abrir o projeto %@"; -"web_visuobject_save_ws_tip" = "Guardar no workspace (Ctrl+S)"; -"web_visuobject_unsaved" = "não guardado"; -"web_visuobject_rel_nav_navigable" = "navegável (seta)"; -"web_visuobject_rel_nav_non-navigable" = "não navegável (cruz ×)"; -"web_visuobject_rel_nav_unspecified" = "não especificado (aberto)"; -"web_visuobject_rel_reading_flip" = "inverter"; -"web_visuobject_rel_reading_auto" = "Com um nome, a relação lê-se no sentido em que arrastaste."; -"web_visuobject_tools" = "Ferramentas"; -"web_visuobject_tool_select" = "Seleção"; -"web_visuobject_tool_select_tip" = "Selecionar e mover"; -"web_visuobject_tool_class" = "Classe"; -"web_visuobject_tool_class_tip" = "Classe: arrasta para a área ou escolhe e clica na área"; -"web_visuobject_tool_interface" = "Interface"; -"web_visuobject_tool_interface_tip" = "Interface: arrasta para a área ou escolhe e clica na área"; -"web_visuobject_tool_link" = "Associação"; -"web_visuobject_tool_link_tip" = "Associação sem navegação: desenha uma linha simples entre duas classes"; -"web_visuobject_rel_link" = "Associação (sem navegação)"; -"web_visuobject_tool_not_link" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_association" = "Dirigida"; -"web_visuobject_tool_association_tip" = "Associação dirigida: arrasta da classe que conhece a outra para a outra (seta)"; -"web_visuobject_tool_aggregation" = "Agregação"; -"web_visuobject_tool_aggregation_tip" = "Agregação: arrasta do todo para a parte (tem, as partes continuam a existir)"; -"web_visuobject_tool_composition" = "Composição"; -"web_visuobject_tool_composition_tip" = "Composição: arrasta do todo para a parte (é composto por, as partes pertencem-lhe)"; -"web_visuobject_tool_extends" = "Herança"; -"web_visuobject_tool_extends_tip" = "Herança: arrasta da subclasse para a superclasse (extends)"; -"web_visuobject_tool_implements" = "Realização"; -"web_visuobject_tool_implements_tip" = "Realização: arrasta da classe para a interface (implements)"; -"web_visuobject_tool_pick_second" = "Agora clica na segunda classe (a partir de %@). Esc cancela."; -"web_visuobject_tool_not_association" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_aggregation" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_composition" = "%@ é uma interface e não pode ter atributos. Arrasta a relação a partir de uma classe."; -"web_visuobject_tool_not_extends" = "%@ não pode herdar de %@: classes herdam de classes, interfaces de interfaces. Para uma interface usa a realização."; -"web_visuobject_tool_not_implements" = "%@ não pode implementar %@: só uma classe implementa uma interface."; -"web_visuobject_inspector" = "Inspetor de objetos"; -"web_visuobject_connect" = "Arrastar: relação com outra classe"; -"web_visuobject_rel_association" = "Associação"; -"web_visuobject_rel_directed" = "Associação dirigida"; -"web_visuobject_rel_aggregation" = "Agregação (tem, as partes continuam a existir)"; -"web_visuobject_rel_composition" = "Composição (é composto por, as partes pertencem-lhe)"; -"web_visuobject_rel_extends" = "Herança (extends)"; -"web_visuobject_rel_implements" = "Realização (implements)"; -"web_visuobject_rel_extends_text" = "%@ herda de %@ (extends no código)."; -"web_visuobject_rel_implements_text" = "%@ implementa a interface %@ (implements no código)."; -"web_visuobject_rel_kind" = "Tipo"; -"web_visuobject_rel_name" = "Nome da relação"; -"web_visuobject_rel_name_ph" = "por exemplo, possui"; -"web_visuobject_rel_reading" = "Sentido de leitura"; -"web_visuobject_rel_navigable" = "Navegabilidade"; -"web_visuobject_rel_role" = "Papel"; -"web_visuobject_rel_multiplicity" = "Multiplicidade"; -"web_visuobject_rel_role_field" = "= atributo %@.%@ no código"; -"web_visuobject_rel_role_only_uml" = "só no diagrama (sem atributo enquanto a seta só aponta num sentido)"; -"web_visuobject_rel_delete" = "Eliminar a relação"; -"web_visuobject_rel_impossible" = "Não há uma relação adequada de %@ para %@."; -"web_visuobject_rel_menu" = "%@ → %@"; -"web_visuobject_resize" = "Arrastar para mudar a largura (duplo clique: padrão)"; -"web_visuobject_pane_show" = "Expandir a área"; -"web_visuobject_pane_hide" = "Recolher a área"; -"web_visuobject_close_tab" = "Fechar %@"; -"web_visuobject_collapse" = "Recolher"; -"web_visuobject_expand" = "Expandir"; -"web_visuobject_tree_empty" = "Ainda vazio. Cria um package ou uma classe com «+ Novo»."; -"web_visuobject_delete" = "Eliminar"; -"web_visuobject_default_package" = "(package predefinido)"; -"web_visuobject_bad_package" = "«%@» não é um nome de package válido ou já existe. Os nomes de package escrevem-se em minúsculas, por exemplo animais."; -"web_visuobject_new_package_name" = "novopackage"; -"web_visuobject_no_file" = "Nenhum ficheiro aberto"; -"web_visuobject_project_name" = "MeuProjeto"; -"web_visuobject_explorer_show" = "Mostrar o Package Explorer"; -"web_visuobject_explorer_hide" = "Recolher o Package Explorer"; -"web_visuobject_explorer" = "Package Explorer"; -"web_visuobject_save_tip" = "Guardar o projeto como .zip: NomeDoProjeto/src/package/Classe.java"; -"web_visuobject_open_tip" = "Abrir um projeto (.zip como do Eclipse ou BlueJ) ou juntar ficheiros .java ao projeto"; -"web_visuobject_method_ph" = "+ ladrar(): void"; -"web_code_scopes" = "Blocos coloridos"; -"web_code_scopes_hint" = "Código e diagrama: ciclo rosa, decisão azul, método amarelo, classe verde"; -"web_pseudocode" = "Pseudocódigo"; - -/* VisuFlow (desenhar fluxogramas) */ -"web_visuflow_subtitle" = "Desenhar fluxogramas"; -"web_visuflow_world_hint" = "VisuFlow: desenhar livremente um fluxograma e exportá-lo como imagem ou PDF. Podes mudar a qualquer momento na roda dentada."; -"web_visuflow_palette" = "Símbolos"; -"web_visuflow_palette_hint" = "Toca num símbolo para o juntar abaixo do selecionado, ligado por uma seta. Também o podes arrastar para a área."; -"web_visuflow_shape_start" = "Início"; -"web_visuflow_shape_ende" = "Fim"; -"web_visuflow_shape_anweisung" = "Instrução"; -"web_visuflow_shape_ein" = "Entrada/Saída"; -"web_visuflow_shape_verzweigung" = "Decisão"; -"web_visuflow_shape_unterprogramm" = "Subprograma"; -"web_visuflow_shape_start_tip" = "Oval: aqui começa o fluxo"; -"web_visuflow_shape_ende_tip" = "Oval: aqui termina o fluxo"; -"web_visuflow_shape_anweisung_tip" = "Retângulo: uma instrução, por exemplo «Ferver água»"; -"web_visuflow_shape_ein_tip" = "Paralelogramo: entrada ou saída, por exemplo «Entrada: número»"; -"web_visuflow_shape_verzweigung_tip" = "Losango: uma pergunta com as saídas sim e não"; -"web_visuflow_shape_unterprogramm_tip" = "Retângulo com duas barras: chama um subprograma"; -"web_visuflow_text_start" = "Início"; -"web_visuflow_text_ende" = "Fim"; -"web_visuflow_text_anweisung" = "Instrução"; -"web_visuflow_text_ein" = "Entrada: número"; -"web_visuflow_text_verzweigung" = "Condição?"; -"web_visuflow_text_unterprogramm" = "Subprograma"; -"web_visuflow_new_tip" = "Novo: fluxograma vazio com Início (Anular recupera o anterior)"; -"web_visuflow_open_tip" = "Abrir um ficheiro de fluxograma (.visuflow)"; -"web_visuflow_save_tip" = "Guardar o fluxograma como ficheiro (.visuflow)"; -"web_visuflow_delete" = "Apagar"; -"web_visuflow_delete_tip" = "Apagar o símbolo ou a seta selecionada (Del)"; -"web_visuflow_swap" = "trocar sim/não"; -"web_visuflow_swap_tip" = "Losango: «sim» e «não» trocam de lado"; -"web_visuflow_example" = "Exemplo"; -"web_visuflow_example_tip" = "Carregar o exemplo «Fazer chá» da lição 1 (Anular recupera o fluxograma anterior)"; -"web_visuflow_example_title" = "Fazer chá"; -"web_visuflow_example_water" = "Ferver água"; -"web_visuflow_example_bag" = "Saqueta de chá na chávena"; -"web_visuflow_example_hot" = "O chá ainda está quente demais?"; -"web_visuflow_example_wait" = "Esperar"; -"web_visuflow_example_drink" = "Beber o chá"; -"web_visuflow_title_label" = "Título do fluxograma"; -"web_visuflow_title_ph" = "Título do fluxograma"; -"web_visuflow_canvas_label" = "Área do fluxograma"; -"web_visuflow_open_error" = "Este ficheiro não é um fluxograma VisuFlow."; -"web_visuflow_saved" = "Guardado como %@"; -"web_visuflow_status_empty" = "Arrasta um símbolo da barra para a área ou toca nele."; -"web_visuflow_status_idle" = "Toca num símbolo para o selecionar. Arrasta a partir do ponto na saída para desenhar uma seta até ao próximo símbolo."; -"web_visuflow_status_node" = "Arrasta para mover o símbolo, toca outra vez para mudar o texto. Arrasta uma seta a partir do ponto na saída."; -"web_visuflow_status_edge" = "Seta selecionada. Apagar remove-a."; -"web_visuflow_status_connect" = "Larga sobre o símbolo para onde a seta vai."; -"web_visuflow_edit_label" = "Texto no símbolo"; -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Mudar o nome da variável …"; -"web_rename_title" = "Mudar o nome da variável"; -"web_rename_old" = "Nome"; -"web_rename_new" = "Novo nome"; -"web_rename_hint" = "Altera todos os sítios com exatamente esta escrita (numero não é Numero), no código e no estruturograma. Texto entre \\"…\\" e comentários ficam iguais."; -"web_rename_count_fmt" = "Aparece %d vezes."; -"web_rename_exists_fmt" = "“%@” já existe: depois as duas seriam uma só variável."; -"web_rename_invalid" = "Um nome começa com uma letra e só contém letras, algarismos e _."; -"web_rename_keyword_fmt" = "“%@” é uma palavra-chave de Java."; -"web_rename_apply" = "Mudar o nome"; -"web_rename_cancel" = "Cancelar"; -"web_visubot_world_visucode_hint" = "Outros programas como %1$@ ou %2$@ estão em visucode.de."; -`;export{e as default}; diff --git a/website/app/assets/robot3d-CJE1N58D.js b/website/app/assets/robot3d-CJE1N58D.js deleted file mode 100644 index 18d4e9d7..00000000 --- a/website/app/assets/robot3d-CJE1N58D.js +++ /dev/null @@ -1,3955 +0,0 @@ -var gl=Object.defineProperty;var _l=(i,t,e)=>t in i?gl(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var le=(i,t,e)=>_l(i,typeof t!="symbol"?t+"":t,e);import{C as rn,L as oo}from"./index-BqSgHxq8.js";/** - * @license - * Copyright 2010-2025 Three.js Authors - * SPDX-License-Identifier: MIT - */const Na="181",yi={ROTATE:0,DOLLY:1,PAN:2},vi={ROTATE:0,PAN:1,DOLLY_PAN:2,DOLLY_ROTATE:3},vl=0,co=1,Ml=2,bc=1,Ec=2,_n=3,Fn=0,Le=1,Mn=2,yn=0,bi=1,lo=2,ho=3,uo=4,Sl=5,Yn=100,yl=101,bl=102,El=103,Tl=104,Al=200,wl=201,Rl=202,Cl=203,Gr=204,kr=205,Pl=206,Dl=207,Ll=208,Il=209,Ul=210,Nl=211,Fl=212,Ol=213,Bl=214,Hr=0,Wr=1,Xr=2,Ti=3,qr=4,Yr=5,Zr=6,Kr=7,Fa=0,zl=1,Vl=2,Un=0,Gl=1,kl=2,Hl=3,Tc=4,Wl=5,Xl=6,ql=7,Ac=300,Ai=301,wi=302,jr=303,Jr=304,Js=306,$r=1e3,Sn=1001,Qr=1002,He=1003,Yl=1004,us=1005,Ye=1006,ir=1007,Kn=1008,hn=1009,wc=1010,Rc=1011,Qi=1012,Oa=1013,jn=1014,on=1015,Di=1016,Ba=1017,za=1018,ts=1020,Cc=35902,Pc=35899,Dc=1021,Lc=1022,tn=1023,es=1026,ns=1027,Va=1028,Ga=1029,ka=1030,Ha=1031,Wa=1033,Bs=33776,zs=33777,Vs=33778,Gs=33779,ta=35840,ea=35841,na=35842,ia=35843,sa=36196,ra=37492,aa=37496,oa=37808,ca=37809,la=37810,ha=37811,ua=37812,fa=37813,da=37814,pa=37815,ma=37816,xa=37817,ga=37818,_a=37819,va=37820,Ma=37821,Sa=36492,ya=36494,ba=36495,Ea=36283,Ta=36284,Aa=36285,wa=36286,Zl=3200,Kl=3201,Xa=0,jl=1,Ln="",De="srgb",Ri="srgb-linear",Ws="linear",ne="srgb",ii=7680,fo=519,Jl=512,$l=513,Ql=514,Ic=515,th=516,eh=517,nh=518,ih=519,po=35044,mo="300 es",cn=2e3,Xs=2001;function Uc(i){for(let t=i.length-1;t>=0;--t)if(i[t]>=65535)return!0;return!1}function qs(i){return document.createElementNS("http://www.w3.org/1999/xhtml",i)}function sh(){const i=qs("canvas");return i.style.display="block",i}const xo={};function go(...i){const t="THREE."+i.shift();console.log(t,...i)}function Bt(...i){const t="THREE."+i.shift();console.warn(t,...i)}function de(...i){const t="THREE."+i.shift();console.error(t,...i)}function is(...i){const t=i.join(" ");t in xo||(xo[t]=!0,Bt(...i))}function rh(i,t,e){return new Promise(function(n,s){function r(){switch(i.clientWaitSync(t,i.SYNC_FLUSH_COMMANDS_BIT,0)){case i.WAIT_FAILED:s();break;case i.TIMEOUT_EXPIRED:setTimeout(r,e);break;default:n()}}setTimeout(r,e)})}class ti{addEventListener(t,e){this._listeners===void 0&&(this._listeners={});const n=this._listeners;n[t]===void 0&&(n[t]=[]),n[t].indexOf(e)===-1&&n[t].push(e)}hasEventListener(t,e){const n=this._listeners;return n===void 0?!1:n[t]!==void 0&&n[t].indexOf(e)!==-1}removeEventListener(t,e){const n=this._listeners;if(n===void 0)return;const s=n[t];if(s!==void 0){const r=s.indexOf(e);r!==-1&&s.splice(r,1)}}dispatchEvent(t){const e=this._listeners;if(e===void 0)return;const n=e[t.type];if(n!==void 0){t.target=this;const s=n.slice(0);for(let r=0,a=s.length;r>8&255]+Ae[i>>16&255]+Ae[i>>24&255]+"-"+Ae[t&255]+Ae[t>>8&255]+"-"+Ae[t>>16&15|64]+Ae[t>>24&255]+"-"+Ae[e&63|128]+Ae[e>>8&255]+"-"+Ae[e>>16&255]+Ae[e>>24&255]+Ae[n&255]+Ae[n>>8&255]+Ae[n>>16&255]+Ae[n>>24&255]).toLowerCase()}function Xt(i,t,e){return Math.max(t,Math.min(e,i))}function ah(i,t){return(i%t+t)%t}function sr(i,t,e){return(1-e)*i+e*t}function Fi(i,t){switch(t.constructor){case Float32Array:return i;case Uint32Array:return i/4294967295;case Uint16Array:return i/65535;case Uint8Array:return i/255;case Int32Array:return Math.max(i/2147483647,-1);case Int16Array:return Math.max(i/32767,-1);case Int8Array:return Math.max(i/127,-1);default:throw new Error("Invalid component type.")}}function Fe(i,t){switch(t.constructor){case Float32Array:return i;case Uint32Array:return Math.round(i*4294967295);case Uint16Array:return Math.round(i*65535);case Uint8Array:return Math.round(i*255);case Int32Array:return Math.round(i*2147483647);case Int16Array:return Math.round(i*32767);case Int8Array:return Math.round(i*127);default:throw new Error("Invalid component type.")}}const oh={DEG2RAD:ks};class at{constructor(t=0,e=0){at.prototype.isVector2=!0,this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,n=this.y,s=t.elements;return this.x=s[0]*e+s[3]*n+s[6],this.y=s[1]*e+s[4]*n+s[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this}clamp(t,e){return this.x=Xt(this.x,t.x,e.x),this.y=Xt(this.y,t.y,e.y),this}clampScalar(t,e){return this.x=Xt(this.x,t,e),this.y=Xt(this.y,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(Xt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this}negate(){return this.x=-this.x,this.y=-this.y,this}dot(t){return this.x*t.x+this.y*t.y}cross(t){return this.x*t.y-this.y*t.x}lengthSq(){return this.x*this.x+this.y*this.y}length(){return Math.sqrt(this.x*this.x+this.y*this.y)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)}normalize(){return this.divideScalar(this.length()||1)}angle(){return Math.atan2(-this.y,-this.x)+Math.PI}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos(Xt(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y;return e*e+n*n}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this}equals(t){return t.x===this.x&&t.y===this.y}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this}rotateAround(t,e){const n=Math.cos(e),s=Math.sin(e),r=this.x-t.x,a=this.y-t.y;return this.x=r*n-a*s+t.x,this.y=r*s+a*n+t.y,this}random(){return this.x=Math.random(),this.y=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y}}class Jn{constructor(t=0,e=0,n=0,s=1){this.isQuaternion=!0,this._x=t,this._y=e,this._z=n,this._w=s}static slerpFlat(t,e,n,s,r,a,o){let l=n[s+0],c=n[s+1],h=n[s+2],u=n[s+3],d=r[a+0],p=r[a+1],x=r[a+2],_=r[a+3];if(o<=0){t[e+0]=l,t[e+1]=c,t[e+2]=h,t[e+3]=u;return}if(o>=1){t[e+0]=d,t[e+1]=p,t[e+2]=x,t[e+3]=_;return}if(u!==_||l!==d||c!==p||h!==x){let m=l*d+c*p+h*x+u*_;m<0&&(d=-d,p=-p,x=-x,_=-_,m=-m);let f=1-o;if(m<.9995){const b=Math.acos(m),S=Math.sin(b);f=Math.sin(f*b)/S,o=Math.sin(o*b)/S,l=l*f+d*o,c=c*f+p*o,h=h*f+x*o,u=u*f+_*o}else{l=l*f+d*o,c=c*f+p*o,h=h*f+x*o,u=u*f+_*o;const b=1/Math.sqrt(l*l+c*c+h*h+u*u);l*=b,c*=b,h*=b,u*=b}}t[e]=l,t[e+1]=c,t[e+2]=h,t[e+3]=u}static multiplyQuaternionsFlat(t,e,n,s,r,a){const o=n[s],l=n[s+1],c=n[s+2],h=n[s+3],u=r[a],d=r[a+1],p=r[a+2],x=r[a+3];return t[e]=o*x+h*u+l*p-c*d,t[e+1]=l*x+h*d+c*u-o*p,t[e+2]=c*x+h*p+o*d-l*u,t[e+3]=h*x-o*u-l*d-c*p,t}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get w(){return this._w}set w(t){this._w=t,this._onChangeCallback()}set(t,e,n,s){return this._x=t,this._y=e,this._z=n,this._w=s,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._w)}copy(t){return this._x=t.x,this._y=t.y,this._z=t.z,this._w=t.w,this._onChangeCallback(),this}setFromEuler(t,e=!0){const n=t._x,s=t._y,r=t._z,a=t._order,o=Math.cos,l=Math.sin,c=o(n/2),h=o(s/2),u=o(r/2),d=l(n/2),p=l(s/2),x=l(r/2);switch(a){case"XYZ":this._x=d*h*u+c*p*x,this._y=c*p*u-d*h*x,this._z=c*h*x+d*p*u,this._w=c*h*u-d*p*x;break;case"YXZ":this._x=d*h*u+c*p*x,this._y=c*p*u-d*h*x,this._z=c*h*x-d*p*u,this._w=c*h*u+d*p*x;break;case"ZXY":this._x=d*h*u-c*p*x,this._y=c*p*u+d*h*x,this._z=c*h*x+d*p*u,this._w=c*h*u-d*p*x;break;case"ZYX":this._x=d*h*u-c*p*x,this._y=c*p*u+d*h*x,this._z=c*h*x-d*p*u,this._w=c*h*u+d*p*x;break;case"YZX":this._x=d*h*u+c*p*x,this._y=c*p*u+d*h*x,this._z=c*h*x-d*p*u,this._w=c*h*u-d*p*x;break;case"XZY":this._x=d*h*u-c*p*x,this._y=c*p*u-d*h*x,this._z=c*h*x+d*p*u,this._w=c*h*u+d*p*x;break;default:Bt("Quaternion: .setFromEuler() encountered an unknown order: "+a)}return e===!0&&this._onChangeCallback(),this}setFromAxisAngle(t,e){const n=e/2,s=Math.sin(n);return this._x=t.x*s,this._y=t.y*s,this._z=t.z*s,this._w=Math.cos(n),this._onChangeCallback(),this}setFromRotationMatrix(t){const e=t.elements,n=e[0],s=e[4],r=e[8],a=e[1],o=e[5],l=e[9],c=e[2],h=e[6],u=e[10],d=n+o+u;if(d>0){const p=.5/Math.sqrt(d+1);this._w=.25/p,this._x=(h-l)*p,this._y=(r-c)*p,this._z=(a-s)*p}else if(n>o&&n>u){const p=2*Math.sqrt(1+n-o-u);this._w=(h-l)/p,this._x=.25*p,this._y=(s+a)/p,this._z=(r+c)/p}else if(o>u){const p=2*Math.sqrt(1+o-n-u);this._w=(r-c)/p,this._x=(s+a)/p,this._y=.25*p,this._z=(l+h)/p}else{const p=2*Math.sqrt(1+u-n-o);this._w=(a-s)/p,this._x=(r+c)/p,this._y=(l+h)/p,this._z=.25*p}return this._onChangeCallback(),this}setFromUnitVectors(t,e){let n=t.dot(e)+1;return n<1e-8?(n=0,Math.abs(t.x)>Math.abs(t.z)?(this._x=-t.y,this._y=t.x,this._z=0,this._w=n):(this._x=0,this._y=-t.z,this._z=t.y,this._w=n)):(this._x=t.y*e.z-t.z*e.y,this._y=t.z*e.x-t.x*e.z,this._z=t.x*e.y-t.y*e.x,this._w=n),this.normalize()}angleTo(t){return 2*Math.acos(Math.abs(Xt(this.dot(t),-1,1)))}rotateTowards(t,e){const n=this.angleTo(t);if(n===0)return this;const s=Math.min(1,e/n);return this.slerp(t,s),this}identity(){return this.set(0,0,0,1)}invert(){return this.conjugate()}conjugate(){return this._x*=-1,this._y*=-1,this._z*=-1,this._onChangeCallback(),this}dot(t){return this._x*t._x+this._y*t._y+this._z*t._z+this._w*t._w}lengthSq(){return this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w}length(){return Math.sqrt(this._x*this._x+this._y*this._y+this._z*this._z+this._w*this._w)}normalize(){let t=this.length();return t===0?(this._x=0,this._y=0,this._z=0,this._w=1):(t=1/t,this._x=this._x*t,this._y=this._y*t,this._z=this._z*t,this._w=this._w*t),this._onChangeCallback(),this}multiply(t){return this.multiplyQuaternions(this,t)}premultiply(t){return this.multiplyQuaternions(t,this)}multiplyQuaternions(t,e){const n=t._x,s=t._y,r=t._z,a=t._w,o=e._x,l=e._y,c=e._z,h=e._w;return this._x=n*h+a*o+s*c-r*l,this._y=s*h+a*l+r*o-n*c,this._z=r*h+a*c+n*l-s*o,this._w=a*h-n*o-s*l-r*c,this._onChangeCallback(),this}slerp(t,e){if(e<=0)return this;if(e>=1)return this.copy(t);let n=t._x,s=t._y,r=t._z,a=t._w,o=this.dot(t);o<0&&(n=-n,s=-s,r=-r,a=-a,o=-o);let l=1-e;if(o<.9995){const c=Math.acos(o),h=Math.sin(c);l=Math.sin(l*c)/h,e=Math.sin(e*c)/h,this._x=this._x*l+n*e,this._y=this._y*l+s*e,this._z=this._z*l+r*e,this._w=this._w*l+a*e,this._onChangeCallback()}else this._x=this._x*l+n*e,this._y=this._y*l+s*e,this._z=this._z*l+r*e,this._w=this._w*l+a*e,this.normalize();return this}slerpQuaternions(t,e,n){return this.copy(t).slerp(e,n)}random(){const t=2*Math.PI*Math.random(),e=2*Math.PI*Math.random(),n=Math.random(),s=Math.sqrt(1-n),r=Math.sqrt(n);return this.set(s*Math.sin(t),s*Math.cos(t),r*Math.sin(e),r*Math.cos(e))}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._w===this._w}fromArray(t,e=0){return this._x=t[e],this._y=t[e+1],this._z=t[e+2],this._w=t[e+3],this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._w,t}fromBufferAttribute(t,e){return this._x=t.getX(e),this._y=t.getY(e),this._z=t.getZ(e),this._w=t.getW(e),this._onChangeCallback(),this}toJSON(){return this.toArray()}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._w}}class D{constructor(t=0,e=0,n=0){D.prototype.isVector3=!0,this.x=t,this.y=e,this.z=n}set(t,e,n){return n===void 0&&(n=this.z),this.x=t,this.y=e,this.z=n,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this}multiplyVectors(t,e){return this.x=t.x*e.x,this.y=t.y*e.y,this.z=t.z*e.z,this}applyEuler(t){return this.applyQuaternion(_o.setFromEuler(t))}applyAxisAngle(t,e){return this.applyQuaternion(_o.setFromAxisAngle(t,e))}applyMatrix3(t){const e=this.x,n=this.y,s=this.z,r=t.elements;return this.x=r[0]*e+r[3]*n+r[6]*s,this.y=r[1]*e+r[4]*n+r[7]*s,this.z=r[2]*e+r[5]*n+r[8]*s,this}applyNormalMatrix(t){return this.applyMatrix3(t).normalize()}applyMatrix4(t){const e=this.x,n=this.y,s=this.z,r=t.elements,a=1/(r[3]*e+r[7]*n+r[11]*s+r[15]);return this.x=(r[0]*e+r[4]*n+r[8]*s+r[12])*a,this.y=(r[1]*e+r[5]*n+r[9]*s+r[13])*a,this.z=(r[2]*e+r[6]*n+r[10]*s+r[14])*a,this}applyQuaternion(t){const e=this.x,n=this.y,s=this.z,r=t.x,a=t.y,o=t.z,l=t.w,c=2*(a*s-o*n),h=2*(o*e-r*s),u=2*(r*n-a*e);return this.x=e+l*c+a*u-o*h,this.y=n+l*h+o*c-r*u,this.z=s+l*u+r*h-a*c,this}project(t){return this.applyMatrix4(t.matrixWorldInverse).applyMatrix4(t.projectionMatrix)}unproject(t){return this.applyMatrix4(t.projectionMatrixInverse).applyMatrix4(t.matrixWorld)}transformDirection(t){const e=this.x,n=this.y,s=this.z,r=t.elements;return this.x=r[0]*e+r[4]*n+r[8]*s,this.y=r[1]*e+r[5]*n+r[9]*s,this.z=r[2]*e+r[6]*n+r[10]*s,this.normalize()}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this}divideScalar(t){return this.multiplyScalar(1/t)}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this}clamp(t,e){return this.x=Xt(this.x,t.x,e.x),this.y=Xt(this.y,t.y,e.y),this.z=Xt(this.z,t.z,e.z),this}clampScalar(t,e){return this.x=Xt(this.x,t,e),this.y=Xt(this.y,t,e),this.z=Xt(this.z,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(Xt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this}cross(t){return this.crossVectors(this,t)}crossVectors(t,e){const n=t.x,s=t.y,r=t.z,a=e.x,o=e.y,l=e.z;return this.x=s*l-r*o,this.y=r*a-n*l,this.z=n*o-s*a,this}projectOnVector(t){const e=t.lengthSq();if(e===0)return this.set(0,0,0);const n=t.dot(this)/e;return this.copy(t).multiplyScalar(n)}projectOnPlane(t){return rr.copy(this).projectOnVector(t),this.sub(rr)}reflect(t){return this.sub(rr.copy(t).multiplyScalar(2*this.dot(t)))}angleTo(t){const e=Math.sqrt(this.lengthSq()*t.lengthSq());if(e===0)return Math.PI/2;const n=this.dot(t)/e;return Math.acos(Xt(n,-1,1))}distanceTo(t){return Math.sqrt(this.distanceToSquared(t))}distanceToSquared(t){const e=this.x-t.x,n=this.y-t.y,s=this.z-t.z;return e*e+n*n+s*s}manhattanDistanceTo(t){return Math.abs(this.x-t.x)+Math.abs(this.y-t.y)+Math.abs(this.z-t.z)}setFromSpherical(t){return this.setFromSphericalCoords(t.radius,t.phi,t.theta)}setFromSphericalCoords(t,e,n){const s=Math.sin(e)*t;return this.x=s*Math.sin(n),this.y=Math.cos(e)*t,this.z=s*Math.cos(n),this}setFromCylindrical(t){return this.setFromCylindricalCoords(t.radius,t.theta,t.y)}setFromCylindricalCoords(t,e,n){return this.x=t*Math.sin(e),this.y=n,this.z=t*Math.cos(e),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this}setFromMatrixScale(t){const e=this.setFromMatrixColumn(t,0).length(),n=this.setFromMatrixColumn(t,1).length(),s=this.setFromMatrixColumn(t,2).length();return this.x=e,this.y=n,this.z=s,this}setFromMatrixColumn(t,e){return this.fromArray(t.elements,e*4)}setFromMatrix3Column(t,e){return this.fromArray(t.elements,e*3)}setFromEuler(t){return this.x=t._x,this.y=t._y,this.z=t._z,this}setFromColor(t){return this.x=t.r,this.y=t.g,this.z=t.b,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this}randomDirection(){const t=Math.random()*Math.PI*2,e=Math.random()*2-1,n=Math.sqrt(1-e*e);return this.x=n*Math.cos(t),this.y=e,this.z=n*Math.sin(t),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z}}const rr=new D,_o=new Jn;class Ht{constructor(t,e,n,s,r,a,o,l,c){Ht.prototype.isMatrix3=!0,this.elements=[1,0,0,0,1,0,0,0,1],t!==void 0&&this.set(t,e,n,s,r,a,o,l,c)}set(t,e,n,s,r,a,o,l,c){const h=this.elements;return h[0]=t,h[1]=s,h[2]=o,h[3]=e,h[4]=r,h[5]=l,h[6]=n,h[7]=a,h[8]=c,this}identity(){return this.set(1,0,0,0,1,0,0,0,1),this}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],this}extractBasis(t,e,n){return t.setFromMatrix3Column(this,0),e.setFromMatrix3Column(this,1),n.setFromMatrix3Column(this,2),this}setFromMatrix4(t){const e=t.elements;return this.set(e[0],e[4],e[8],e[1],e[5],e[9],e[2],e[6],e[10]),this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,s=e.elements,r=this.elements,a=n[0],o=n[3],l=n[6],c=n[1],h=n[4],u=n[7],d=n[2],p=n[5],x=n[8],_=s[0],m=s[3],f=s[6],b=s[1],S=s[4],T=s[7],R=s[2],w=s[5],L=s[8];return r[0]=a*_+o*b+l*R,r[3]=a*m+o*S+l*w,r[6]=a*f+o*T+l*L,r[1]=c*_+h*b+u*R,r[4]=c*m+h*S+u*w,r[7]=c*f+h*T+u*L,r[2]=d*_+p*b+x*R,r[5]=d*m+p*S+x*w,r[8]=d*f+p*T+x*L,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[3]*=t,e[6]*=t,e[1]*=t,e[4]*=t,e[7]*=t,e[2]*=t,e[5]*=t,e[8]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[1],s=t[2],r=t[3],a=t[4],o=t[5],l=t[6],c=t[7],h=t[8];return e*a*h-e*o*c-n*r*h+n*o*l+s*r*c-s*a*l}invert(){const t=this.elements,e=t[0],n=t[1],s=t[2],r=t[3],a=t[4],o=t[5],l=t[6],c=t[7],h=t[8],u=h*a-o*c,d=o*l-h*r,p=c*r-a*l,x=e*u+n*d+s*p;if(x===0)return this.set(0,0,0,0,0,0,0,0,0);const _=1/x;return t[0]=u*_,t[1]=(s*c-h*n)*_,t[2]=(o*n-s*a)*_,t[3]=d*_,t[4]=(h*e-s*l)*_,t[5]=(s*r-o*e)*_,t[6]=p*_,t[7]=(n*l-c*e)*_,t[8]=(a*e-n*r)*_,this}transpose(){let t;const e=this.elements;return t=e[1],e[1]=e[3],e[3]=t,t=e[2],e[2]=e[6],e[6]=t,t=e[5],e[5]=e[7],e[7]=t,this}getNormalMatrix(t){return this.setFromMatrix4(t).invert().transpose()}transposeIntoArray(t){const e=this.elements;return t[0]=e[0],t[1]=e[3],t[2]=e[6],t[3]=e[1],t[4]=e[4],t[5]=e[7],t[6]=e[2],t[7]=e[5],t[8]=e[8],this}setUvTransform(t,e,n,s,r,a,o){const l=Math.cos(r),c=Math.sin(r);return this.set(n*l,n*c,-n*(l*a+c*o)+a+t,-s*c,s*l,-s*(-c*a+l*o)+o+e,0,0,1),this}scale(t,e){return this.premultiply(ar.makeScale(t,e)),this}rotate(t){return this.premultiply(ar.makeRotation(-t)),this}translate(t,e){return this.premultiply(ar.makeTranslation(t,e)),this}makeTranslation(t,e){return t.isVector2?this.set(1,0,t.x,0,1,t.y,0,0,1):this.set(1,0,t,0,1,e,0,0,1),this}makeRotation(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,n,e,0,0,0,1),this}makeScale(t,e){return this.set(t,0,0,0,e,0,0,0,1),this}equals(t){const e=this.elements,n=t.elements;for(let s=0;s<9;s++)if(e[s]!==n[s])return!1;return!0}fromArray(t,e=0){for(let n=0;n<9;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t}clone(){return new this.constructor().fromArray(this.elements)}}const ar=new Ht,vo=new Ht().set(.4123908,.3575843,.1804808,.212639,.7151687,.0721923,.0193308,.1191948,.9505322),Mo=new Ht().set(3.2409699,-1.5373832,-.4986108,-.9692436,1.8759675,.0415551,.0556301,-.203977,1.0569715);function ch(){const i={enabled:!0,workingColorSpace:Ri,spaces:{},convert:function(s,r,a){return this.enabled===!1||r===a||!r||!a||(this.spaces[r].transfer===ne&&(s.r=bn(s.r),s.g=bn(s.g),s.b=bn(s.b)),this.spaces[r].primaries!==this.spaces[a].primaries&&(s.applyMatrix3(this.spaces[r].toXYZ),s.applyMatrix3(this.spaces[a].fromXYZ)),this.spaces[a].transfer===ne&&(s.r=Ei(s.r),s.g=Ei(s.g),s.b=Ei(s.b))),s},workingToColorSpace:function(s,r){return this.convert(s,this.workingColorSpace,r)},colorSpaceToWorking:function(s,r){return this.convert(s,r,this.workingColorSpace)},getPrimaries:function(s){return this.spaces[s].primaries},getTransfer:function(s){return s===Ln?Ws:this.spaces[s].transfer},getToneMappingMode:function(s){return this.spaces[s].outputColorSpaceConfig.toneMappingMode||"standard"},getLuminanceCoefficients:function(s,r=this.workingColorSpace){return s.fromArray(this.spaces[r].luminanceCoefficients)},define:function(s){Object.assign(this.spaces,s)},_getMatrix:function(s,r,a){return s.copy(this.spaces[r].toXYZ).multiply(this.spaces[a].fromXYZ)},_getDrawingBufferColorSpace:function(s){return this.spaces[s].outputColorSpaceConfig.drawingBufferColorSpace},_getUnpackColorSpace:function(s=this.workingColorSpace){return this.spaces[s].workingColorSpaceConfig.unpackColorSpace},fromWorkingColorSpace:function(s,r){return is("ColorManagement: .fromWorkingColorSpace() has been renamed to .workingToColorSpace()."),i.workingToColorSpace(s,r)},toWorkingColorSpace:function(s,r){return is("ColorManagement: .toWorkingColorSpace() has been renamed to .colorSpaceToWorking()."),i.colorSpaceToWorking(s,r)}},t=[.64,.33,.3,.6,.15,.06],e=[.2126,.7152,.0722],n=[.3127,.329];return i.define({[Ri]:{primaries:t,whitePoint:n,transfer:Ws,toXYZ:vo,fromXYZ:Mo,luminanceCoefficients:e,workingColorSpaceConfig:{unpackColorSpace:De},outputColorSpaceConfig:{drawingBufferColorSpace:De}},[De]:{primaries:t,whitePoint:n,transfer:ne,toXYZ:vo,fromXYZ:Mo,luminanceCoefficients:e,outputColorSpaceConfig:{drawingBufferColorSpace:De}}}),i}const $t=ch();function bn(i){return i<.04045?i*.0773993808:Math.pow(i*.9478672986+.0521327014,2.4)}function Ei(i){return i<.0031308?i*12.92:1.055*Math.pow(i,.41666)-.055}let si;class lh{static getDataURL(t,e="image/png"){if(/^data:/i.test(t.src)||typeof HTMLCanvasElement>"u")return t.src;let n;if(t instanceof HTMLCanvasElement)n=t;else{si===void 0&&(si=qs("canvas")),si.width=t.width,si.height=t.height;const s=si.getContext("2d");t instanceof ImageData?s.putImageData(t,0,0):s.drawImage(t,0,0,t.width,t.height),n=si}return n.toDataURL(e)}static sRGBToLinear(t){if(typeof HTMLImageElement<"u"&&t instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&t instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&t instanceof ImageBitmap){const e=qs("canvas");e.width=t.width,e.height=t.height;const n=e.getContext("2d");n.drawImage(t,0,0,t.width,t.height);const s=n.getImageData(0,0,t.width,t.height),r=s.data;for(let a=0;a1),this.pmremVersion=0}get width(){return this.source.getSize(cr).x}get height(){return this.source.getSize(cr).y}get depth(){return this.source.getSize(cr).z}get image(){return this.source.data}set image(t=null){this.source.data=t}updateMatrix(){this.matrix.setUvTransform(this.offset.x,this.offset.y,this.repeat.x,this.repeat.y,this.rotation,this.center.x,this.center.y)}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}clone(){return new this.constructor().copy(this)}copy(t){return this.name=t.name,this.source=t.source,this.mipmaps=t.mipmaps.slice(0),this.mapping=t.mapping,this.channel=t.channel,this.wrapS=t.wrapS,this.wrapT=t.wrapT,this.magFilter=t.magFilter,this.minFilter=t.minFilter,this.anisotropy=t.anisotropy,this.format=t.format,this.internalFormat=t.internalFormat,this.type=t.type,this.offset.copy(t.offset),this.repeat.copy(t.repeat),this.center.copy(t.center),this.rotation=t.rotation,this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrix.copy(t.matrix),this.generateMipmaps=t.generateMipmaps,this.premultiplyAlpha=t.premultiplyAlpha,this.flipY=t.flipY,this.unpackAlignment=t.unpackAlignment,this.colorSpace=t.colorSpace,this.renderTarget=t.renderTarget,this.isRenderTargetTexture=t.isRenderTargetTexture,this.isArrayTexture=t.isArrayTexture,this.userData=JSON.parse(JSON.stringify(t.userData)),this.needsUpdate=!0,this}setValues(t){for(const e in t){const n=t[e];if(n===void 0){Bt(`Texture.setValues(): parameter '${e}' has value of undefined.`);continue}const s=this[e];if(s===void 0){Bt(`Texture.setValues(): property '${e}' does not exist.`);continue}s&&n&&s.isVector2&&n.isVector2||s&&n&&s.isVector3&&n.isVector3||s&&n&&s.isMatrix3&&n.isMatrix3?s.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";if(!e&&t.textures[this.uuid]!==void 0)return t.textures[this.uuid];const n={metadata:{version:4.7,type:"Texture",generator:"Texture.toJSON"},uuid:this.uuid,name:this.name,image:this.source.toJSON(t).uuid,mapping:this.mapping,channel:this.channel,repeat:[this.repeat.x,this.repeat.y],offset:[this.offset.x,this.offset.y],center:[this.center.x,this.center.y],rotation:this.rotation,wrap:[this.wrapS,this.wrapT],format:this.format,internalFormat:this.internalFormat,type:this.type,colorSpace:this.colorSpace,minFilter:this.minFilter,magFilter:this.magFilter,anisotropy:this.anisotropy,flipY:this.flipY,generateMipmaps:this.generateMipmaps,premultiplyAlpha:this.premultiplyAlpha,unpackAlignment:this.unpackAlignment};return Object.keys(this.userData).length>0&&(n.userData=this.userData),e||(t.textures[this.uuid]=n),n}dispose(){this.dispatchEvent({type:"dispose"})}transformUv(t){if(this.mapping!==Ac)return t;if(t.applyMatrix3(this.matrix),t.x<0||t.x>1)switch(this.wrapS){case $r:t.x=t.x-Math.floor(t.x);break;case Sn:t.x=t.x<0?0:1;break;case Qr:Math.abs(Math.floor(t.x)%2)===1?t.x=Math.ceil(t.x)-t.x:t.x=t.x-Math.floor(t.x);break}if(t.y<0||t.y>1)switch(this.wrapT){case $r:t.y=t.y-Math.floor(t.y);break;case Sn:t.y=t.y<0?0:1;break;case Qr:Math.abs(Math.floor(t.y)%2)===1?t.y=Math.ceil(t.y)-t.y:t.y=t.y-Math.floor(t.y);break}return this.flipY&&(t.y=1-t.y),t}set needsUpdate(t){t===!0&&(this.version++,this.source.needsUpdate=!0)}set needsPMREMUpdate(t){t===!0&&this.pmremVersion++}}Re.DEFAULT_IMAGE=null;Re.DEFAULT_MAPPING=Ac;Re.DEFAULT_ANISOTROPY=1;class se{constructor(t=0,e=0,n=0,s=1){se.prototype.isVector4=!0,this.x=t,this.y=e,this.z=n,this.w=s}get width(){return this.z}set width(t){this.z=t}get height(){return this.w}set height(t){this.w=t}set(t,e,n,s){return this.x=t,this.y=e,this.z=n,this.w=s,this}setScalar(t){return this.x=t,this.y=t,this.z=t,this.w=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setZ(t){return this.z=t,this}setW(t){return this.w=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;case 2:this.z=e;break;case 3:this.w=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;case 2:return this.z;case 3:return this.w;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y,this.z,this.w)}copy(t){return this.x=t.x,this.y=t.y,this.z=t.z,this.w=t.w!==void 0?t.w:1,this}add(t){return this.x+=t.x,this.y+=t.y,this.z+=t.z,this.w+=t.w,this}addScalar(t){return this.x+=t,this.y+=t,this.z+=t,this.w+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this.z=t.z+e.z,this.w=t.w+e.w,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this.z+=t.z*e,this.w+=t.w*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this.z-=t.z,this.w-=t.w,this}subScalar(t){return this.x-=t,this.y-=t,this.z-=t,this.w-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this.z=t.z-e.z,this.w=t.w-e.w,this}multiply(t){return this.x*=t.x,this.y*=t.y,this.z*=t.z,this.w*=t.w,this}multiplyScalar(t){return this.x*=t,this.y*=t,this.z*=t,this.w*=t,this}applyMatrix4(t){const e=this.x,n=this.y,s=this.z,r=this.w,a=t.elements;return this.x=a[0]*e+a[4]*n+a[8]*s+a[12]*r,this.y=a[1]*e+a[5]*n+a[9]*s+a[13]*r,this.z=a[2]*e+a[6]*n+a[10]*s+a[14]*r,this.w=a[3]*e+a[7]*n+a[11]*s+a[15]*r,this}divide(t){return this.x/=t.x,this.y/=t.y,this.z/=t.z,this.w/=t.w,this}divideScalar(t){return this.multiplyScalar(1/t)}setAxisAngleFromQuaternion(t){this.w=2*Math.acos(t.w);const e=Math.sqrt(1-t.w*t.w);return e<1e-4?(this.x=1,this.y=0,this.z=0):(this.x=t.x/e,this.y=t.y/e,this.z=t.z/e),this}setAxisAngleFromRotationMatrix(t){let e,n,s,r;const l=t.elements,c=l[0],h=l[4],u=l[8],d=l[1],p=l[5],x=l[9],_=l[2],m=l[6],f=l[10];if(Math.abs(h-d)<.01&&Math.abs(u-_)<.01&&Math.abs(x-m)<.01){if(Math.abs(h+d)<.1&&Math.abs(u+_)<.1&&Math.abs(x+m)<.1&&Math.abs(c+p+f-3)<.1)return this.set(1,0,0,0),this;e=Math.PI;const S=(c+1)/2,T=(p+1)/2,R=(f+1)/2,w=(h+d)/4,L=(u+_)/4,F=(x+m)/4;return S>T&&S>R?S<.01?(n=0,s=.707106781,r=.707106781):(n=Math.sqrt(S),s=w/n,r=L/n):T>R?T<.01?(n=.707106781,s=0,r=.707106781):(s=Math.sqrt(T),n=w/s,r=F/s):R<.01?(n=.707106781,s=.707106781,r=0):(r=Math.sqrt(R),n=L/r,s=F/r),this.set(n,s,r,e),this}let b=Math.sqrt((m-x)*(m-x)+(u-_)*(u-_)+(d-h)*(d-h));return Math.abs(b)<.001&&(b=1),this.x=(m-x)/b,this.y=(u-_)/b,this.z=(d-h)/b,this.w=Math.acos((c+p+f-1)/2),this}setFromMatrixPosition(t){const e=t.elements;return this.x=e[12],this.y=e[13],this.z=e[14],this.w=e[15],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.y),this.z=Math.min(this.z,t.z),this.w=Math.min(this.w,t.w),this}max(t){return this.x=Math.max(this.x,t.x),this.y=Math.max(this.y,t.y),this.z=Math.max(this.z,t.z),this.w=Math.max(this.w,t.w),this}clamp(t,e){return this.x=Xt(this.x,t.x,e.x),this.y=Xt(this.y,t.y,e.y),this.z=Xt(this.z,t.z,e.z),this.w=Xt(this.w,t.w,e.w),this}clampScalar(t,e){return this.x=Xt(this.x,t,e),this.y=Xt(this.y,t,e),this.z=Xt(this.z,t,e),this.w=Xt(this.w,t,e),this}clampLength(t,e){const n=this.length();return this.divideScalar(n||1).multiplyScalar(Xt(n,t,e))}floor(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this.z=Math.floor(this.z),this.w=Math.floor(this.w),this}ceil(){return this.x=Math.ceil(this.x),this.y=Math.ceil(this.y),this.z=Math.ceil(this.z),this.w=Math.ceil(this.w),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this.z=Math.round(this.z),this.w=Math.round(this.w),this}roundToZero(){return this.x=Math.trunc(this.x),this.y=Math.trunc(this.y),this.z=Math.trunc(this.z),this.w=Math.trunc(this.w),this}negate(){return this.x=-this.x,this.y=-this.y,this.z=-this.z,this.w=-this.w,this}dot(t){return this.x*t.x+this.y*t.y+this.z*t.z+this.w*t.w}lengthSq(){return this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w}length(){return Math.sqrt(this.x*this.x+this.y*this.y+this.z*this.z+this.w*this.w)}manhattanLength(){return Math.abs(this.x)+Math.abs(this.y)+Math.abs(this.z)+Math.abs(this.w)}normalize(){return this.divideScalar(this.length()||1)}setLength(t){return this.normalize().multiplyScalar(t)}lerp(t,e){return this.x+=(t.x-this.x)*e,this.y+=(t.y-this.y)*e,this.z+=(t.z-this.z)*e,this.w+=(t.w-this.w)*e,this}lerpVectors(t,e,n){return this.x=t.x+(e.x-t.x)*n,this.y=t.y+(e.y-t.y)*n,this.z=t.z+(e.z-t.z)*n,this.w=t.w+(e.w-t.w)*n,this}equals(t){return t.x===this.x&&t.y===this.y&&t.z===this.z&&t.w===this.w}fromArray(t,e=0){return this.x=t[e],this.y=t[e+1],this.z=t[e+2],this.w=t[e+3],this}toArray(t=[],e=0){return t[e]=this.x,t[e+1]=this.y,t[e+2]=this.z,t[e+3]=this.w,t}fromBufferAttribute(t,e){return this.x=t.getX(e),this.y=t.getY(e),this.z=t.getZ(e),this.w=t.getW(e),this}random(){return this.x=Math.random(),this.y=Math.random(),this.z=Math.random(),this.w=Math.random(),this}*[Symbol.iterator](){yield this.x,yield this.y,yield this.z,yield this.w}}class fh extends ti{constructor(t=1,e=1,n={}){super(),n=Object.assign({generateMipmaps:!1,internalFormat:null,minFilter:Ye,depthBuffer:!0,stencilBuffer:!1,resolveDepthBuffer:!0,resolveStencilBuffer:!0,depthTexture:null,samples:0,count:1,depth:1,multiview:!1},n),this.isRenderTarget=!0,this.width=t,this.height=e,this.depth=n.depth,this.scissor=new se(0,0,t,e),this.scissorTest=!1,this.viewport=new se(0,0,t,e);const s={width:t,height:e,depth:n.depth},r=new Re(s);this.textures=[];const a=n.count;for(let o=0;o1);this.dispose()}this.viewport.set(0,0,t,e),this.scissor.set(0,0,t,e)}clone(){return new this.constructor().copy(this)}copy(t){this.width=t.width,this.height=t.height,this.depth=t.depth,this.scissor.copy(t.scissor),this.scissorTest=t.scissorTest,this.viewport.copy(t.viewport),this.textures.length=0;for(let e=0,n=t.textures.length;e=this.min.x&&t.x<=this.max.x&&t.y>=this.min.y&&t.y<=this.max.y&&t.z>=this.min.z&&t.z<=this.max.z}containsBox(t){return this.min.x<=t.min.x&&t.max.x<=this.max.x&&this.min.y<=t.min.y&&t.max.y<=this.max.y&&this.min.z<=t.min.z&&t.max.z<=this.max.z}getParameter(t,e){return e.set((t.x-this.min.x)/(this.max.x-this.min.x),(t.y-this.min.y)/(this.max.y-this.min.y),(t.z-this.min.z)/(this.max.z-this.min.z))}intersectsBox(t){return t.max.x>=this.min.x&&t.min.x<=this.max.x&&t.max.y>=this.min.y&&t.min.y<=this.max.y&&t.max.z>=this.min.z&&t.min.z<=this.max.z}intersectsSphere(t){return this.clampPoint(t.center,Ke),Ke.distanceToSquared(t.center)<=t.radius*t.radius}intersectsPlane(t){let e,n;return t.normal.x>0?(e=t.normal.x*this.min.x,n=t.normal.x*this.max.x):(e=t.normal.x*this.max.x,n=t.normal.x*this.min.x),t.normal.y>0?(e+=t.normal.y*this.min.y,n+=t.normal.y*this.max.y):(e+=t.normal.y*this.max.y,n+=t.normal.y*this.min.y),t.normal.z>0?(e+=t.normal.z*this.min.z,n+=t.normal.z*this.max.z):(e+=t.normal.z*this.max.z,n+=t.normal.z*this.min.z),e<=-t.constant&&n>=-t.constant}intersectsTriangle(t){if(this.isEmpty())return!1;this.getCenter(Oi),ds.subVectors(this.max,Oi),ri.subVectors(t.a,Oi),ai.subVectors(t.b,Oi),oi.subVectors(t.c,Oi),Tn.subVectors(ai,ri),An.subVectors(oi,ai),Vn.subVectors(ri,oi);let e=[0,-Tn.z,Tn.y,0,-An.z,An.y,0,-Vn.z,Vn.y,Tn.z,0,-Tn.x,An.z,0,-An.x,Vn.z,0,-Vn.x,-Tn.y,Tn.x,0,-An.y,An.x,0,-Vn.y,Vn.x,0];return!lr(e,ri,ai,oi,ds)||(e=[1,0,0,0,1,0,0,0,1],!lr(e,ri,ai,oi,ds))?!1:(ps.crossVectors(Tn,An),e=[ps.x,ps.y,ps.z],lr(e,ri,ai,oi,ds))}clampPoint(t,e){return e.copy(t).clamp(this.min,this.max)}distanceToPoint(t){return this.clampPoint(t,Ke).distanceTo(t)}getBoundingSphere(t){return this.isEmpty()?t.makeEmpty():(this.getCenter(t.center),t.radius=this.getSize(Ke).length()*.5),t}intersect(t){return this.min.max(t.min),this.max.min(t.max),this.isEmpty()&&this.makeEmpty(),this}union(t){return this.min.min(t.min),this.max.max(t.max),this}applyMatrix4(t){return this.isEmpty()?this:(fn[0].set(this.min.x,this.min.y,this.min.z).applyMatrix4(t),fn[1].set(this.min.x,this.min.y,this.max.z).applyMatrix4(t),fn[2].set(this.min.x,this.max.y,this.min.z).applyMatrix4(t),fn[3].set(this.min.x,this.max.y,this.max.z).applyMatrix4(t),fn[4].set(this.max.x,this.min.y,this.min.z).applyMatrix4(t),fn[5].set(this.max.x,this.min.y,this.max.z).applyMatrix4(t),fn[6].set(this.max.x,this.max.y,this.min.z).applyMatrix4(t),fn[7].set(this.max.x,this.max.y,this.max.z).applyMatrix4(t),this.setFromPoints(fn),this)}translate(t){return this.min.add(t),this.max.add(t),this}equals(t){return t.min.equals(this.min)&&t.max.equals(this.max)}toJSON(){return{min:this.min.toArray(),max:this.max.toArray()}}fromJSON(t){return this.min.fromArray(t.min),this.max.fromArray(t.max),this}}const fn=[new D,new D,new D,new D,new D,new D,new D,new D],Ke=new D,fs=new On,ri=new D,ai=new D,oi=new D,Tn=new D,An=new D,Vn=new D,Oi=new D,ds=new D,ps=new D,Gn=new D;function lr(i,t,e,n,s){for(let r=0,a=i.length-3;r<=a;r+=3){Gn.fromArray(i,r);const o=s.x*Math.abs(Gn.x)+s.y*Math.abs(Gn.y)+s.z*Math.abs(Gn.z),l=t.dot(Gn),c=e.dot(Gn),h=n.dot(Gn);if(Math.max(-Math.max(l,c,h),Math.min(l,c,h))>o)return!1}return!0}const ph=new On,Bi=new D,hr=new D;class Ii{constructor(t=new D,e=-1){this.isSphere=!0,this.center=t,this.radius=e}set(t,e){return this.center.copy(t),this.radius=e,this}setFromPoints(t,e){const n=this.center;e!==void 0?n.copy(e):ph.setFromPoints(t).getCenter(n);let s=0;for(let r=0,a=t.length;rthis.radius*this.radius&&(e.sub(this.center).normalize(),e.multiplyScalar(this.radius).add(this.center)),e}getBoundingBox(t){return this.isEmpty()?(t.makeEmpty(),t):(t.set(this.center,this.center),t.expandByScalar(this.radius),t)}applyMatrix4(t){return this.center.applyMatrix4(t),this.radius=this.radius*t.getMaxScaleOnAxis(),this}translate(t){return this.center.add(t),this}expandByPoint(t){if(this.isEmpty())return this.center.copy(t),this.radius=0,this;Bi.subVectors(t,this.center);const e=Bi.lengthSq();if(e>this.radius*this.radius){const n=Math.sqrt(e),s=(n-this.radius)*.5;this.center.addScaledVector(Bi,s/n),this.radius+=s}return this}union(t){return t.isEmpty()?this:this.isEmpty()?(this.copy(t),this):(this.center.equals(t.center)===!0?this.radius=Math.max(this.radius,t.radius):(hr.subVectors(t.center,this.center).setLength(t.radius),this.expandByPoint(Bi.copy(t.center).add(hr)),this.expandByPoint(Bi.copy(t.center).sub(hr))),this)}equals(t){return t.center.equals(this.center)&&t.radius===this.radius}clone(){return new this.constructor().copy(this)}toJSON(){return{radius:this.radius,center:this.center.toArray()}}fromJSON(t){return this.radius=t.radius,this.center.fromArray(t.center),this}}const dn=new D,ur=new D,ms=new D,wn=new D,fr=new D,xs=new D,dr=new D;class $s{constructor(t=new D,e=new D(0,0,-1)){this.origin=t,this.direction=e}set(t,e){return this.origin.copy(t),this.direction.copy(e),this}copy(t){return this.origin.copy(t.origin),this.direction.copy(t.direction),this}at(t,e){return e.copy(this.origin).addScaledVector(this.direction,t)}lookAt(t){return this.direction.copy(t).sub(this.origin).normalize(),this}recast(t){return this.origin.copy(this.at(t,dn)),this}closestPointToPoint(t,e){e.subVectors(t,this.origin);const n=e.dot(this.direction);return n<0?e.copy(this.origin):e.copy(this.origin).addScaledVector(this.direction,n)}distanceToPoint(t){return Math.sqrt(this.distanceSqToPoint(t))}distanceSqToPoint(t){const e=dn.subVectors(t,this.origin).dot(this.direction);return e<0?this.origin.distanceToSquared(t):(dn.copy(this.origin).addScaledVector(this.direction,e),dn.distanceToSquared(t))}distanceSqToSegment(t,e,n,s){ur.copy(t).add(e).multiplyScalar(.5),ms.copy(e).sub(t).normalize(),wn.copy(this.origin).sub(ur);const r=t.distanceTo(e)*.5,a=-this.direction.dot(ms),o=wn.dot(this.direction),l=-wn.dot(ms),c=wn.lengthSq(),h=Math.abs(1-a*a);let u,d,p,x;if(h>0)if(u=a*l-o,d=a*o-l,x=r*h,u>=0)if(d>=-x)if(d<=x){const _=1/h;u*=_,d*=_,p=u*(u+a*d+2*o)+d*(a*u+d+2*l)+c}else d=r,u=Math.max(0,-(a*d+o)),p=-u*u+d*(d+2*l)+c;else d=-r,u=Math.max(0,-(a*d+o)),p=-u*u+d*(d+2*l)+c;else d<=-x?(u=Math.max(0,-(-a*r+o)),d=u>0?-r:Math.min(Math.max(-r,-l),r),p=-u*u+d*(d+2*l)+c):d<=x?(u=0,d=Math.min(Math.max(-r,-l),r),p=d*(d+2*l)+c):(u=Math.max(0,-(a*r+o)),d=u>0?r:Math.min(Math.max(-r,-l),r),p=-u*u+d*(d+2*l)+c);else d=a>0?-r:r,u=Math.max(0,-(a*d+o)),p=-u*u+d*(d+2*l)+c;return n&&n.copy(this.origin).addScaledVector(this.direction,u),s&&s.copy(ur).addScaledVector(ms,d),p}intersectSphere(t,e){dn.subVectors(t.center,this.origin);const n=dn.dot(this.direction),s=dn.dot(dn)-n*n,r=t.radius*t.radius;if(s>r)return null;const a=Math.sqrt(r-s),o=n-a,l=n+a;return l<0?null:o<0?this.at(l,e):this.at(o,e)}intersectsSphere(t){return t.radius<0?!1:this.distanceSqToPoint(t.center)<=t.radius*t.radius}distanceToPlane(t){const e=t.normal.dot(this.direction);if(e===0)return t.distanceToPoint(this.origin)===0?0:null;const n=-(this.origin.dot(t.normal)+t.constant)/e;return n>=0?n:null}intersectPlane(t,e){const n=this.distanceToPlane(t);return n===null?null:this.at(n,e)}intersectsPlane(t){const e=t.distanceToPoint(this.origin);return e===0||t.normal.dot(this.direction)*e<0}intersectBox(t,e){let n,s,r,a,o,l;const c=1/this.direction.x,h=1/this.direction.y,u=1/this.direction.z,d=this.origin;return c>=0?(n=(t.min.x-d.x)*c,s=(t.max.x-d.x)*c):(n=(t.max.x-d.x)*c,s=(t.min.x-d.x)*c),h>=0?(r=(t.min.y-d.y)*h,a=(t.max.y-d.y)*h):(r=(t.max.y-d.y)*h,a=(t.min.y-d.y)*h),n>a||r>s||((r>n||isNaN(n))&&(n=r),(a=0?(o=(t.min.z-d.z)*u,l=(t.max.z-d.z)*u):(o=(t.max.z-d.z)*u,l=(t.min.z-d.z)*u),n>l||o>s)||((o>n||n!==n)&&(n=o),(l=0?n:s,e)}intersectsBox(t){return this.intersectBox(t,dn)!==null}intersectTriangle(t,e,n,s,r){fr.subVectors(e,t),xs.subVectors(n,t),dr.crossVectors(fr,xs);let a=this.direction.dot(dr),o;if(a>0){if(s)return null;o=1}else if(a<0)o=-1,a=-a;else return null;wn.subVectors(this.origin,t);const l=o*this.direction.dot(xs.crossVectors(wn,xs));if(l<0)return null;const c=o*this.direction.dot(fr.cross(wn));if(c<0||l+c>a)return null;const h=-o*wn.dot(dr);return h<0?null:this.at(h/a,r)}applyMatrix4(t){return this.origin.applyMatrix4(t),this.direction.transformDirection(t),this}equals(t){return t.origin.equals(this.origin)&&t.direction.equals(this.direction)}clone(){return new this.constructor().copy(this)}}class re{constructor(t,e,n,s,r,a,o,l,c,h,u,d,p,x,_,m){re.prototype.isMatrix4=!0,this.elements=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1],t!==void 0&&this.set(t,e,n,s,r,a,o,l,c,h,u,d,p,x,_,m)}set(t,e,n,s,r,a,o,l,c,h,u,d,p,x,_,m){const f=this.elements;return f[0]=t,f[4]=e,f[8]=n,f[12]=s,f[1]=r,f[5]=a,f[9]=o,f[13]=l,f[2]=c,f[6]=h,f[10]=u,f[14]=d,f[3]=p,f[7]=x,f[11]=_,f[15]=m,this}identity(){return this.set(1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1),this}clone(){return new re().fromArray(this.elements)}copy(t){const e=this.elements,n=t.elements;return e[0]=n[0],e[1]=n[1],e[2]=n[2],e[3]=n[3],e[4]=n[4],e[5]=n[5],e[6]=n[6],e[7]=n[7],e[8]=n[8],e[9]=n[9],e[10]=n[10],e[11]=n[11],e[12]=n[12],e[13]=n[13],e[14]=n[14],e[15]=n[15],this}copyPosition(t){const e=this.elements,n=t.elements;return e[12]=n[12],e[13]=n[13],e[14]=n[14],this}setFromMatrix3(t){const e=t.elements;return this.set(e[0],e[3],e[6],0,e[1],e[4],e[7],0,e[2],e[5],e[8],0,0,0,0,1),this}extractBasis(t,e,n){return t.setFromMatrixColumn(this,0),e.setFromMatrixColumn(this,1),n.setFromMatrixColumn(this,2),this}makeBasis(t,e,n){return this.set(t.x,e.x,n.x,0,t.y,e.y,n.y,0,t.z,e.z,n.z,0,0,0,0,1),this}extractRotation(t){const e=this.elements,n=t.elements,s=1/ci.setFromMatrixColumn(t,0).length(),r=1/ci.setFromMatrixColumn(t,1).length(),a=1/ci.setFromMatrixColumn(t,2).length();return e[0]=n[0]*s,e[1]=n[1]*s,e[2]=n[2]*s,e[3]=0,e[4]=n[4]*r,e[5]=n[5]*r,e[6]=n[6]*r,e[7]=0,e[8]=n[8]*a,e[9]=n[9]*a,e[10]=n[10]*a,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this}makeRotationFromEuler(t){const e=this.elements,n=t.x,s=t.y,r=t.z,a=Math.cos(n),o=Math.sin(n),l=Math.cos(s),c=Math.sin(s),h=Math.cos(r),u=Math.sin(r);if(t.order==="XYZ"){const d=a*h,p=a*u,x=o*h,_=o*u;e[0]=l*h,e[4]=-l*u,e[8]=c,e[1]=p+x*c,e[5]=d-_*c,e[9]=-o*l,e[2]=_-d*c,e[6]=x+p*c,e[10]=a*l}else if(t.order==="YXZ"){const d=l*h,p=l*u,x=c*h,_=c*u;e[0]=d+_*o,e[4]=x*o-p,e[8]=a*c,e[1]=a*u,e[5]=a*h,e[9]=-o,e[2]=p*o-x,e[6]=_+d*o,e[10]=a*l}else if(t.order==="ZXY"){const d=l*h,p=l*u,x=c*h,_=c*u;e[0]=d-_*o,e[4]=-a*u,e[8]=x+p*o,e[1]=p+x*o,e[5]=a*h,e[9]=_-d*o,e[2]=-a*c,e[6]=o,e[10]=a*l}else if(t.order==="ZYX"){const d=a*h,p=a*u,x=o*h,_=o*u;e[0]=l*h,e[4]=x*c-p,e[8]=d*c+_,e[1]=l*u,e[5]=_*c+d,e[9]=p*c-x,e[2]=-c,e[6]=o*l,e[10]=a*l}else if(t.order==="YZX"){const d=a*l,p=a*c,x=o*l,_=o*c;e[0]=l*h,e[4]=_-d*u,e[8]=x*u+p,e[1]=u,e[5]=a*h,e[9]=-o*h,e[2]=-c*h,e[6]=p*u+x,e[10]=d-_*u}else if(t.order==="XZY"){const d=a*l,p=a*c,x=o*l,_=o*c;e[0]=l*h,e[4]=-u,e[8]=c*h,e[1]=d*u+_,e[5]=a*h,e[9]=p*u-x,e[2]=x*u-p,e[6]=o*h,e[10]=_*u+d}return e[3]=0,e[7]=0,e[11]=0,e[12]=0,e[13]=0,e[14]=0,e[15]=1,this}makeRotationFromQuaternion(t){return this.compose(mh,t,xh)}lookAt(t,e,n){const s=this.elements;return Ve.subVectors(t,e),Ve.lengthSq()===0&&(Ve.z=1),Ve.normalize(),Rn.crossVectors(n,Ve),Rn.lengthSq()===0&&(Math.abs(n.z)===1?Ve.x+=1e-4:Ve.z+=1e-4,Ve.normalize(),Rn.crossVectors(n,Ve)),Rn.normalize(),gs.crossVectors(Ve,Rn),s[0]=Rn.x,s[4]=gs.x,s[8]=Ve.x,s[1]=Rn.y,s[5]=gs.y,s[9]=Ve.y,s[2]=Rn.z,s[6]=gs.z,s[10]=Ve.z,this}multiply(t){return this.multiplyMatrices(this,t)}premultiply(t){return this.multiplyMatrices(t,this)}multiplyMatrices(t,e){const n=t.elements,s=e.elements,r=this.elements,a=n[0],o=n[4],l=n[8],c=n[12],h=n[1],u=n[5],d=n[9],p=n[13],x=n[2],_=n[6],m=n[10],f=n[14],b=n[3],S=n[7],T=n[11],R=n[15],w=s[0],L=s[4],F=s[8],E=s[12],M=s[1],P=s[5],O=s[9],z=s[13],k=s[2],W=s[6],Y=s[10],nt=s[14],X=s[3],rt=s[7],st=s[11],St=s[15];return r[0]=a*w+o*M+l*k+c*X,r[4]=a*L+o*P+l*W+c*rt,r[8]=a*F+o*O+l*Y+c*st,r[12]=a*E+o*z+l*nt+c*St,r[1]=h*w+u*M+d*k+p*X,r[5]=h*L+u*P+d*W+p*rt,r[9]=h*F+u*O+d*Y+p*st,r[13]=h*E+u*z+d*nt+p*St,r[2]=x*w+_*M+m*k+f*X,r[6]=x*L+_*P+m*W+f*rt,r[10]=x*F+_*O+m*Y+f*st,r[14]=x*E+_*z+m*nt+f*St,r[3]=b*w+S*M+T*k+R*X,r[7]=b*L+S*P+T*W+R*rt,r[11]=b*F+S*O+T*Y+R*st,r[15]=b*E+S*z+T*nt+R*St,this}multiplyScalar(t){const e=this.elements;return e[0]*=t,e[4]*=t,e[8]*=t,e[12]*=t,e[1]*=t,e[5]*=t,e[9]*=t,e[13]*=t,e[2]*=t,e[6]*=t,e[10]*=t,e[14]*=t,e[3]*=t,e[7]*=t,e[11]*=t,e[15]*=t,this}determinant(){const t=this.elements,e=t[0],n=t[4],s=t[8],r=t[12],a=t[1],o=t[5],l=t[9],c=t[13],h=t[2],u=t[6],d=t[10],p=t[14],x=t[3],_=t[7],m=t[11],f=t[15];return x*(+r*l*u-s*c*u-r*o*d+n*c*d+s*o*p-n*l*p)+_*(+e*l*p-e*c*d+r*a*d-s*a*p+s*c*h-r*l*h)+m*(+e*c*u-e*o*p-r*a*u+n*a*p+r*o*h-n*c*h)+f*(-s*o*h-e*l*u+e*o*d+s*a*u-n*a*d+n*l*h)}transpose(){const t=this.elements;let e;return e=t[1],t[1]=t[4],t[4]=e,e=t[2],t[2]=t[8],t[8]=e,e=t[6],t[6]=t[9],t[9]=e,e=t[3],t[3]=t[12],t[12]=e,e=t[7],t[7]=t[13],t[13]=e,e=t[11],t[11]=t[14],t[14]=e,this}setPosition(t,e,n){const s=this.elements;return t.isVector3?(s[12]=t.x,s[13]=t.y,s[14]=t.z):(s[12]=t,s[13]=e,s[14]=n),this}invert(){const t=this.elements,e=t[0],n=t[1],s=t[2],r=t[3],a=t[4],o=t[5],l=t[6],c=t[7],h=t[8],u=t[9],d=t[10],p=t[11],x=t[12],_=t[13],m=t[14],f=t[15],b=u*m*c-_*d*c+_*l*p-o*m*p-u*l*f+o*d*f,S=x*d*c-h*m*c-x*l*p+a*m*p+h*l*f-a*d*f,T=h*_*c-x*u*c+x*o*p-a*_*p-h*o*f+a*u*f,R=x*u*l-h*_*l-x*o*d+a*_*d+h*o*m-a*u*m,w=e*b+n*S+s*T+r*R;if(w===0)return this.set(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);const L=1/w;return t[0]=b*L,t[1]=(_*d*r-u*m*r-_*s*p+n*m*p+u*s*f-n*d*f)*L,t[2]=(o*m*r-_*l*r+_*s*c-n*m*c-o*s*f+n*l*f)*L,t[3]=(u*l*r-o*d*r-u*s*c+n*d*c+o*s*p-n*l*p)*L,t[4]=S*L,t[5]=(h*m*r-x*d*r+x*s*p-e*m*p-h*s*f+e*d*f)*L,t[6]=(x*l*r-a*m*r-x*s*c+e*m*c+a*s*f-e*l*f)*L,t[7]=(a*d*r-h*l*r+h*s*c-e*d*c-a*s*p+e*l*p)*L,t[8]=T*L,t[9]=(x*u*r-h*_*r-x*n*p+e*_*p+h*n*f-e*u*f)*L,t[10]=(a*_*r-x*o*r+x*n*c-e*_*c-a*n*f+e*o*f)*L,t[11]=(h*o*r-a*u*r-h*n*c+e*u*c+a*n*p-e*o*p)*L,t[12]=R*L,t[13]=(h*_*s-x*u*s+x*n*d-e*_*d-h*n*m+e*u*m)*L,t[14]=(x*o*s-a*_*s-x*n*l+e*_*l+a*n*m-e*o*m)*L,t[15]=(a*u*s-h*o*s+h*n*l-e*u*l-a*n*d+e*o*d)*L,this}scale(t){const e=this.elements,n=t.x,s=t.y,r=t.z;return e[0]*=n,e[4]*=s,e[8]*=r,e[1]*=n,e[5]*=s,e[9]*=r,e[2]*=n,e[6]*=s,e[10]*=r,e[3]*=n,e[7]*=s,e[11]*=r,this}getMaxScaleOnAxis(){const t=this.elements,e=t[0]*t[0]+t[1]*t[1]+t[2]*t[2],n=t[4]*t[4]+t[5]*t[5]+t[6]*t[6],s=t[8]*t[8]+t[9]*t[9]+t[10]*t[10];return Math.sqrt(Math.max(e,n,s))}makeTranslation(t,e,n){return t.isVector3?this.set(1,0,0,t.x,0,1,0,t.y,0,0,1,t.z,0,0,0,1):this.set(1,0,0,t,0,1,0,e,0,0,1,n,0,0,0,1),this}makeRotationX(t){const e=Math.cos(t),n=Math.sin(t);return this.set(1,0,0,0,0,e,-n,0,0,n,e,0,0,0,0,1),this}makeRotationY(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,0,n,0,0,1,0,0,-n,0,e,0,0,0,0,1),this}makeRotationZ(t){const e=Math.cos(t),n=Math.sin(t);return this.set(e,-n,0,0,n,e,0,0,0,0,1,0,0,0,0,1),this}makeRotationAxis(t,e){const n=Math.cos(e),s=Math.sin(e),r=1-n,a=t.x,o=t.y,l=t.z,c=r*a,h=r*o;return this.set(c*a+n,c*o-s*l,c*l+s*o,0,c*o+s*l,h*o+n,h*l-s*a,0,c*l-s*o,h*l+s*a,r*l*l+n,0,0,0,0,1),this}makeScale(t,e,n){return this.set(t,0,0,0,0,e,0,0,0,0,n,0,0,0,0,1),this}makeShear(t,e,n,s,r,a){return this.set(1,n,r,0,t,1,a,0,e,s,1,0,0,0,0,1),this}compose(t,e,n){const s=this.elements,r=e._x,a=e._y,o=e._z,l=e._w,c=r+r,h=a+a,u=o+o,d=r*c,p=r*h,x=r*u,_=a*h,m=a*u,f=o*u,b=l*c,S=l*h,T=l*u,R=n.x,w=n.y,L=n.z;return s[0]=(1-(_+f))*R,s[1]=(p+T)*R,s[2]=(x-S)*R,s[3]=0,s[4]=(p-T)*w,s[5]=(1-(d+f))*w,s[6]=(m+b)*w,s[7]=0,s[8]=(x+S)*L,s[9]=(m-b)*L,s[10]=(1-(d+_))*L,s[11]=0,s[12]=t.x,s[13]=t.y,s[14]=t.z,s[15]=1,this}decompose(t,e,n){const s=this.elements;let r=ci.set(s[0],s[1],s[2]).length();const a=ci.set(s[4],s[5],s[6]).length(),o=ci.set(s[8],s[9],s[10]).length();this.determinant()<0&&(r=-r),t.x=s[12],t.y=s[13],t.z=s[14],je.copy(this);const c=1/r,h=1/a,u=1/o;return je.elements[0]*=c,je.elements[1]*=c,je.elements[2]*=c,je.elements[4]*=h,je.elements[5]*=h,je.elements[6]*=h,je.elements[8]*=u,je.elements[9]*=u,je.elements[10]*=u,e.setFromRotationMatrix(je),n.x=r,n.y=a,n.z=o,this}makePerspective(t,e,n,s,r,a,o=cn,l=!1){const c=this.elements,h=2*r/(e-t),u=2*r/(n-s),d=(e+t)/(e-t),p=(n+s)/(n-s);let x,_;if(l)x=r/(a-r),_=a*r/(a-r);else if(o===cn)x=-(a+r)/(a-r),_=-2*a*r/(a-r);else if(o===Xs)x=-a/(a-r),_=-a*r/(a-r);else throw new Error("THREE.Matrix4.makePerspective(): Invalid coordinate system: "+o);return c[0]=h,c[4]=0,c[8]=d,c[12]=0,c[1]=0,c[5]=u,c[9]=p,c[13]=0,c[2]=0,c[6]=0,c[10]=x,c[14]=_,c[3]=0,c[7]=0,c[11]=-1,c[15]=0,this}makeOrthographic(t,e,n,s,r,a,o=cn,l=!1){const c=this.elements,h=2/(e-t),u=2/(n-s),d=-(e+t)/(e-t),p=-(n+s)/(n-s);let x,_;if(l)x=1/(a-r),_=a/(a-r);else if(o===cn)x=-2/(a-r),_=-(a+r)/(a-r);else if(o===Xs)x=-1/(a-r),_=-r/(a-r);else throw new Error("THREE.Matrix4.makeOrthographic(): Invalid coordinate system: "+o);return c[0]=h,c[4]=0,c[8]=0,c[12]=d,c[1]=0,c[5]=u,c[9]=0,c[13]=p,c[2]=0,c[6]=0,c[10]=x,c[14]=_,c[3]=0,c[7]=0,c[11]=0,c[15]=1,this}equals(t){const e=this.elements,n=t.elements;for(let s=0;s<16;s++)if(e[s]!==n[s])return!1;return!0}fromArray(t,e=0){for(let n=0;n<16;n++)this.elements[n]=t[n+e];return this}toArray(t=[],e=0){const n=this.elements;return t[e]=n[0],t[e+1]=n[1],t[e+2]=n[2],t[e+3]=n[3],t[e+4]=n[4],t[e+5]=n[5],t[e+6]=n[6],t[e+7]=n[7],t[e+8]=n[8],t[e+9]=n[9],t[e+10]=n[10],t[e+11]=n[11],t[e+12]=n[12],t[e+13]=n[13],t[e+14]=n[14],t[e+15]=n[15],t}}const ci=new D,je=new re,mh=new D(0,0,0),xh=new D(1,1,1),Rn=new D,gs=new D,Ve=new D,So=new re,yo=new Jn;class nn{constructor(t=0,e=0,n=0,s=nn.DEFAULT_ORDER){this.isEuler=!0,this._x=t,this._y=e,this._z=n,this._order=s}get x(){return this._x}set x(t){this._x=t,this._onChangeCallback()}get y(){return this._y}set y(t){this._y=t,this._onChangeCallback()}get z(){return this._z}set z(t){this._z=t,this._onChangeCallback()}get order(){return this._order}set order(t){this._order=t,this._onChangeCallback()}set(t,e,n,s=this._order){return this._x=t,this._y=e,this._z=n,this._order=s,this._onChangeCallback(),this}clone(){return new this.constructor(this._x,this._y,this._z,this._order)}copy(t){return this._x=t._x,this._y=t._y,this._z=t._z,this._order=t._order,this._onChangeCallback(),this}setFromRotationMatrix(t,e=this._order,n=!0){const s=t.elements,r=s[0],a=s[4],o=s[8],l=s[1],c=s[5],h=s[9],u=s[2],d=s[6],p=s[10];switch(e){case"XYZ":this._y=Math.asin(Xt(o,-1,1)),Math.abs(o)<.9999999?(this._x=Math.atan2(-h,p),this._z=Math.atan2(-a,r)):(this._x=Math.atan2(d,c),this._z=0);break;case"YXZ":this._x=Math.asin(-Xt(h,-1,1)),Math.abs(h)<.9999999?(this._y=Math.atan2(o,p),this._z=Math.atan2(l,c)):(this._y=Math.atan2(-u,r),this._z=0);break;case"ZXY":this._x=Math.asin(Xt(d,-1,1)),Math.abs(d)<.9999999?(this._y=Math.atan2(-u,p),this._z=Math.atan2(-a,c)):(this._y=0,this._z=Math.atan2(l,r));break;case"ZYX":this._y=Math.asin(-Xt(u,-1,1)),Math.abs(u)<.9999999?(this._x=Math.atan2(d,p),this._z=Math.atan2(l,r)):(this._x=0,this._z=Math.atan2(-a,c));break;case"YZX":this._z=Math.asin(Xt(l,-1,1)),Math.abs(l)<.9999999?(this._x=Math.atan2(-h,c),this._y=Math.atan2(-u,r)):(this._x=0,this._y=Math.atan2(o,p));break;case"XZY":this._z=Math.asin(-Xt(a,-1,1)),Math.abs(a)<.9999999?(this._x=Math.atan2(d,c),this._y=Math.atan2(o,r)):(this._x=Math.atan2(-h,p),this._y=0);break;default:Bt("Euler: .setFromRotationMatrix() encountered an unknown order: "+e)}return this._order=e,n===!0&&this._onChangeCallback(),this}setFromQuaternion(t,e,n){return So.makeRotationFromQuaternion(t),this.setFromRotationMatrix(So,e,n)}setFromVector3(t,e=this._order){return this.set(t.x,t.y,t.z,e)}reorder(t){return yo.setFromEuler(this),this.setFromQuaternion(yo,t)}equals(t){return t._x===this._x&&t._y===this._y&&t._z===this._z&&t._order===this._order}fromArray(t){return this._x=t[0],this._y=t[1],this._z=t[2],t[3]!==void 0&&(this._order=t[3]),this._onChangeCallback(),this}toArray(t=[],e=0){return t[e]=this._x,t[e+1]=this._y,t[e+2]=this._z,t[e+3]=this._order,t}_onChange(t){return this._onChangeCallback=t,this}_onChangeCallback(){}*[Symbol.iterator](){yield this._x,yield this._y,yield this._z,yield this._order}}nn.DEFAULT_ORDER="XYZ";class Ya{constructor(){this.mask=1}set(t){this.mask=(1<>>0}enable(t){this.mask|=1<1){for(let e=0;e1){for(let n=0;n0&&(s.userData=this.userData),s.layers=this.layers.mask,s.matrix=this.matrix.toArray(),s.up=this.up.toArray(),this.matrixAutoUpdate===!1&&(s.matrixAutoUpdate=!1),this.isInstancedMesh&&(s.type="InstancedMesh",s.count=this.count,s.instanceMatrix=this.instanceMatrix.toJSON(),this.instanceColor!==null&&(s.instanceColor=this.instanceColor.toJSON())),this.isBatchedMesh&&(s.type="BatchedMesh",s.perObjectFrustumCulled=this.perObjectFrustumCulled,s.sortObjects=this.sortObjects,s.drawRanges=this._drawRanges,s.reservedRanges=this._reservedRanges,s.geometryInfo=this._geometryInfo.map(o=>({...o,boundingBox:o.boundingBox?o.boundingBox.toJSON():void 0,boundingSphere:o.boundingSphere?o.boundingSphere.toJSON():void 0})),s.instanceInfo=this._instanceInfo.map(o=>({...o})),s.availableInstanceIds=this._availableInstanceIds.slice(),s.availableGeometryIds=this._availableGeometryIds.slice(),s.nextIndexStart=this._nextIndexStart,s.nextVertexStart=this._nextVertexStart,s.geometryCount=this._geometryCount,s.maxInstanceCount=this._maxInstanceCount,s.maxVertexCount=this._maxVertexCount,s.maxIndexCount=this._maxIndexCount,s.geometryInitialized=this._geometryInitialized,s.matricesTexture=this._matricesTexture.toJSON(t),s.indirectTexture=this._indirectTexture.toJSON(t),this._colorsTexture!==null&&(s.colorsTexture=this._colorsTexture.toJSON(t)),this.boundingSphere!==null&&(s.boundingSphere=this.boundingSphere.toJSON()),this.boundingBox!==null&&(s.boundingBox=this.boundingBox.toJSON()));function r(o,l){return o[l.uuid]===void 0&&(o[l.uuid]=l.toJSON(t)),l.uuid}if(this.isScene)this.background&&(this.background.isColor?s.background=this.background.toJSON():this.background.isTexture&&(s.background=this.background.toJSON(t).uuid)),this.environment&&this.environment.isTexture&&this.environment.isRenderTargetTexture!==!0&&(s.environment=this.environment.toJSON(t).uuid);else if(this.isMesh||this.isLine||this.isPoints){s.geometry=r(t.geometries,this.geometry);const o=this.geometry.parameters;if(o!==void 0&&o.shapes!==void 0){const l=o.shapes;if(Array.isArray(l))for(let c=0,h=l.length;c0){s.children=[];for(let o=0;o0){s.animations=[];for(let o=0;o0&&(n.geometries=o),l.length>0&&(n.materials=l),c.length>0&&(n.textures=c),h.length>0&&(n.images=h),u.length>0&&(n.shapes=u),d.length>0&&(n.skeletons=d),p.length>0&&(n.animations=p),x.length>0&&(n.nodes=x)}return n.object=s,n;function a(o){const l=[];for(const c in o){const h=o[c];delete h.metadata,l.push(h)}return l}}clone(t){return new this.constructor().copy(this,t)}copy(t,e=!0){if(this.name=t.name,this.up.copy(t.up),this.position.copy(t.position),this.rotation.order=t.rotation.order,this.quaternion.copy(t.quaternion),this.scale.copy(t.scale),this.matrix.copy(t.matrix),this.matrixWorld.copy(t.matrixWorld),this.matrixAutoUpdate=t.matrixAutoUpdate,this.matrixWorldAutoUpdate=t.matrixWorldAutoUpdate,this.matrixWorldNeedsUpdate=t.matrixWorldNeedsUpdate,this.layers.mask=t.layers.mask,this.visible=t.visible,this.castShadow=t.castShadow,this.receiveShadow=t.receiveShadow,this.frustumCulled=t.frustumCulled,this.renderOrder=t.renderOrder,this.animations=t.animations.slice(),this.userData=JSON.parse(JSON.stringify(t.userData)),e===!0)for(let n=0;n0?s.multiplyScalar(1/Math.sqrt(r)):s.set(0,0,0)}static getBarycoord(t,e,n,s,r){Je.subVectors(s,e),mn.subVectors(n,e),mr.subVectors(t,e);const a=Je.dot(Je),o=Je.dot(mn),l=Je.dot(mr),c=mn.dot(mn),h=mn.dot(mr),u=a*c-o*o;if(u===0)return r.set(0,0,0),null;const d=1/u,p=(c*l-o*h)*d,x=(a*h-o*l)*d;return r.set(1-p-x,x,p)}static containsPoint(t,e,n,s){return this.getBarycoord(t,e,n,s,xn)===null?!1:xn.x>=0&&xn.y>=0&&xn.x+xn.y<=1}static getInterpolation(t,e,n,s,r,a,o,l){return this.getBarycoord(t,e,n,s,xn)===null?(l.x=0,l.y=0,"z"in l&&(l.z=0),"w"in l&&(l.w=0),null):(l.setScalar(0),l.addScaledVector(r,xn.x),l.addScaledVector(a,xn.y),l.addScaledVector(o,xn.z),l)}static getInterpolatedAttribute(t,e,n,s,r,a){return vr.setScalar(0),Mr.setScalar(0),Sr.setScalar(0),vr.fromBufferAttribute(t,e),Mr.fromBufferAttribute(t,n),Sr.fromBufferAttribute(t,s),a.setScalar(0),a.addScaledVector(vr,r.x),a.addScaledVector(Mr,r.y),a.addScaledVector(Sr,r.z),a}static isFrontFacing(t,e,n,s){return Je.subVectors(n,e),mn.subVectors(t,e),Je.cross(mn).dot(s)<0}set(t,e,n){return this.a.copy(t),this.b.copy(e),this.c.copy(n),this}setFromPointsAndIndices(t,e,n,s){return this.a.copy(t[e]),this.b.copy(t[n]),this.c.copy(t[s]),this}setFromAttributeAndIndices(t,e,n,s){return this.a.fromBufferAttribute(t,e),this.b.fromBufferAttribute(t,n),this.c.fromBufferAttribute(t,s),this}clone(){return new this.constructor().copy(this)}copy(t){return this.a.copy(t.a),this.b.copy(t.b),this.c.copy(t.c),this}getArea(){return Je.subVectors(this.c,this.b),mn.subVectors(this.a,this.b),Je.cross(mn).length()*.5}getMidpoint(t){return t.addVectors(this.a,this.b).add(this.c).multiplyScalar(1/3)}getNormal(t){return Qe.getNormal(this.a,this.b,this.c,t)}getPlane(t){return t.setFromCoplanarPoints(this.a,this.b,this.c)}getBarycoord(t,e){return Qe.getBarycoord(t,this.a,this.b,this.c,e)}getInterpolation(t,e,n,s,r){return Qe.getInterpolation(t,this.a,this.b,this.c,e,n,s,r)}containsPoint(t){return Qe.containsPoint(t,this.a,this.b,this.c)}isFrontFacing(t){return Qe.isFrontFacing(this.a,this.b,this.c,t)}intersectsBox(t){return t.intersectsTriangle(this)}closestPointToPoint(t,e){const n=this.a,s=this.b,r=this.c;let a,o;ui.subVectors(s,n),fi.subVectors(r,n),xr.subVectors(t,n);const l=ui.dot(xr),c=fi.dot(xr);if(l<=0&&c<=0)return e.copy(n);gr.subVectors(t,s);const h=ui.dot(gr),u=fi.dot(gr);if(h>=0&&u<=h)return e.copy(s);const d=l*u-h*c;if(d<=0&&l>=0&&h<=0)return a=l/(l-h),e.copy(n).addScaledVector(ui,a);_r.subVectors(t,r);const p=ui.dot(_r),x=fi.dot(_r);if(x>=0&&p<=x)return e.copy(r);const _=p*c-l*x;if(_<=0&&c>=0&&x<=0)return o=c/(c-x),e.copy(n).addScaledVector(fi,o);const m=h*x-p*u;if(m<=0&&u-h>=0&&p-x>=0)return Ro.subVectors(r,s),o=(u-h)/(u-h+(p-x)),e.copy(s).addScaledVector(Ro,o);const f=1/(m+_+d);return a=_*f,o=d*f,e.copy(n).addScaledVector(ui,a).addScaledVector(fi,o)}equals(t){return t.a.equals(this.a)&&t.b.equals(this.b)&&t.c.equals(this.c)}}const Fc={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074},Cn={h:0,s:0,l:0},vs={h:0,s:0,l:0};function yr(i,t,e){return e<0&&(e+=1),e>1&&(e-=1),e<1/6?i+(t-i)*6*e:e<1/2?t:e<2/3?i+(t-i)*6*(2/3-e):i}class Yt{constructor(t,e,n){return this.isColor=!0,this.r=1,this.g=1,this.b=1,this.set(t,e,n)}set(t,e,n){if(e===void 0&&n===void 0){const s=t;s&&s.isColor?this.copy(s):typeof s=="number"?this.setHex(s):typeof s=="string"&&this.setStyle(s)}else this.setRGB(t,e,n);return this}setScalar(t){return this.r=t,this.g=t,this.b=t,this}setHex(t,e=De){return t=Math.floor(t),this.r=(t>>16&255)/255,this.g=(t>>8&255)/255,this.b=(t&255)/255,$t.colorSpaceToWorking(this,e),this}setRGB(t,e,n,s=$t.workingColorSpace){return this.r=t,this.g=e,this.b=n,$t.colorSpaceToWorking(this,s),this}setHSL(t,e,n,s=$t.workingColorSpace){if(t=ah(t,1),e=Xt(e,0,1),n=Xt(n,0,1),e===0)this.r=this.g=this.b=n;else{const r=n<=.5?n*(1+e):n+e-n*e,a=2*n-r;this.r=yr(a,r,t+1/3),this.g=yr(a,r,t),this.b=yr(a,r,t-1/3)}return $t.colorSpaceToWorking(this,s),this}setStyle(t,e=De){function n(r){r!==void 0&&parseFloat(r)<1&&Bt("Color: Alpha component of "+t+" will be ignored.")}let s;if(s=/^(\w+)\(([^\)]*)\)/.exec(t)){let r;const a=s[1],o=s[2];switch(a){case"rgb":case"rgba":if(r=/^\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setRGB(Math.min(255,parseInt(r[1],10))/255,Math.min(255,parseInt(r[2],10))/255,Math.min(255,parseInt(r[3],10))/255,e);if(r=/^\s*(\d+)\%\s*,\s*(\d+)\%\s*,\s*(\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setRGB(Math.min(100,parseInt(r[1],10))/100,Math.min(100,parseInt(r[2],10))/100,Math.min(100,parseInt(r[3],10))/100,e);break;case"hsl":case"hsla":if(r=/^\s*(\d*\.?\d+)\s*,\s*(\d*\.?\d+)\%\s*,\s*(\d*\.?\d+)\%\s*(?:,\s*(\d*\.?\d+)\s*)?$/.exec(o))return n(r[4]),this.setHSL(parseFloat(r[1])/360,parseFloat(r[2])/100,parseFloat(r[3])/100,e);break;default:Bt("Color: Unknown color model "+t)}}else if(s=/^\#([A-Fa-f\d]+)$/.exec(t)){const r=s[1],a=r.length;if(a===3)return this.setRGB(parseInt(r.charAt(0),16)/15,parseInt(r.charAt(1),16)/15,parseInt(r.charAt(2),16)/15,e);if(a===6)return this.setHex(parseInt(r,16),e);Bt("Color: Invalid hex color "+t)}else if(t&&t.length>0)return this.setColorName(t,e);return this}setColorName(t,e=De){const n=Fc[t.toLowerCase()];return n!==void 0?this.setHex(n,e):Bt("Color: Unknown color "+t),this}clone(){return new this.constructor(this.r,this.g,this.b)}copy(t){return this.r=t.r,this.g=t.g,this.b=t.b,this}copySRGBToLinear(t){return this.r=bn(t.r),this.g=bn(t.g),this.b=bn(t.b),this}copyLinearToSRGB(t){return this.r=Ei(t.r),this.g=Ei(t.g),this.b=Ei(t.b),this}convertSRGBToLinear(){return this.copySRGBToLinear(this),this}convertLinearToSRGB(){return this.copyLinearToSRGB(this),this}getHex(t=De){return $t.workingToColorSpace(we.copy(this),t),Math.round(Xt(we.r*255,0,255))*65536+Math.round(Xt(we.g*255,0,255))*256+Math.round(Xt(we.b*255,0,255))}getHexString(t=De){return("000000"+this.getHex(t).toString(16)).slice(-6)}getHSL(t,e=$t.workingColorSpace){$t.workingToColorSpace(we.copy(this),e);const n=we.r,s=we.g,r=we.b,a=Math.max(n,s,r),o=Math.min(n,s,r);let l,c;const h=(o+a)/2;if(o===a)l=0,c=0;else{const u=a-o;switch(c=h<=.5?u/(a+o):u/(2-a-o),a){case n:l=(s-r)/u+(s0!=t>0&&this.version++,this._alphaTest=t}onBeforeRender(){}onBeforeCompile(){}customProgramCacheKey(){return this.onBeforeCompile.toString()}setValues(t){if(t!==void 0)for(const e in t){const n=t[e];if(n===void 0){Bt(`Material: parameter '${e}' has value of undefined.`);continue}const s=this[e];if(s===void 0){Bt(`Material: '${e}' is not a property of THREE.${this.type}.`);continue}s&&s.isColor?s.set(n):s&&s.isVector3&&n&&n.isVector3?s.copy(n):this[e]=n}}toJSON(t){const e=t===void 0||typeof t=="string";e&&(t={textures:{},images:{}});const n={metadata:{version:4.7,type:"Material",generator:"Material.toJSON"}};n.uuid=this.uuid,n.type=this.type,this.name!==""&&(n.name=this.name),this.color&&this.color.isColor&&(n.color=this.color.getHex()),this.roughness!==void 0&&(n.roughness=this.roughness),this.metalness!==void 0&&(n.metalness=this.metalness),this.sheen!==void 0&&(n.sheen=this.sheen),this.sheenColor&&this.sheenColor.isColor&&(n.sheenColor=this.sheenColor.getHex()),this.sheenRoughness!==void 0&&(n.sheenRoughness=this.sheenRoughness),this.emissive&&this.emissive.isColor&&(n.emissive=this.emissive.getHex()),this.emissiveIntensity!==void 0&&this.emissiveIntensity!==1&&(n.emissiveIntensity=this.emissiveIntensity),this.specular&&this.specular.isColor&&(n.specular=this.specular.getHex()),this.specularIntensity!==void 0&&(n.specularIntensity=this.specularIntensity),this.specularColor&&this.specularColor.isColor&&(n.specularColor=this.specularColor.getHex()),this.shininess!==void 0&&(n.shininess=this.shininess),this.clearcoat!==void 0&&(n.clearcoat=this.clearcoat),this.clearcoatRoughness!==void 0&&(n.clearcoatRoughness=this.clearcoatRoughness),this.clearcoatMap&&this.clearcoatMap.isTexture&&(n.clearcoatMap=this.clearcoatMap.toJSON(t).uuid),this.clearcoatRoughnessMap&&this.clearcoatRoughnessMap.isTexture&&(n.clearcoatRoughnessMap=this.clearcoatRoughnessMap.toJSON(t).uuid),this.clearcoatNormalMap&&this.clearcoatNormalMap.isTexture&&(n.clearcoatNormalMap=this.clearcoatNormalMap.toJSON(t).uuid,n.clearcoatNormalScale=this.clearcoatNormalScale.toArray()),this.sheenColorMap&&this.sheenColorMap.isTexture&&(n.sheenColorMap=this.sheenColorMap.toJSON(t).uuid),this.sheenRoughnessMap&&this.sheenRoughnessMap.isTexture&&(n.sheenRoughnessMap=this.sheenRoughnessMap.toJSON(t).uuid),this.dispersion!==void 0&&(n.dispersion=this.dispersion),this.iridescence!==void 0&&(n.iridescence=this.iridescence),this.iridescenceIOR!==void 0&&(n.iridescenceIOR=this.iridescenceIOR),this.iridescenceThicknessRange!==void 0&&(n.iridescenceThicknessRange=this.iridescenceThicknessRange),this.iridescenceMap&&this.iridescenceMap.isTexture&&(n.iridescenceMap=this.iridescenceMap.toJSON(t).uuid),this.iridescenceThicknessMap&&this.iridescenceThicknessMap.isTexture&&(n.iridescenceThicknessMap=this.iridescenceThicknessMap.toJSON(t).uuid),this.anisotropy!==void 0&&(n.anisotropy=this.anisotropy),this.anisotropyRotation!==void 0&&(n.anisotropyRotation=this.anisotropyRotation),this.anisotropyMap&&this.anisotropyMap.isTexture&&(n.anisotropyMap=this.anisotropyMap.toJSON(t).uuid),this.map&&this.map.isTexture&&(n.map=this.map.toJSON(t).uuid),this.matcap&&this.matcap.isTexture&&(n.matcap=this.matcap.toJSON(t).uuid),this.alphaMap&&this.alphaMap.isTexture&&(n.alphaMap=this.alphaMap.toJSON(t).uuid),this.lightMap&&this.lightMap.isTexture&&(n.lightMap=this.lightMap.toJSON(t).uuid,n.lightMapIntensity=this.lightMapIntensity),this.aoMap&&this.aoMap.isTexture&&(n.aoMap=this.aoMap.toJSON(t).uuid,n.aoMapIntensity=this.aoMapIntensity),this.bumpMap&&this.bumpMap.isTexture&&(n.bumpMap=this.bumpMap.toJSON(t).uuid,n.bumpScale=this.bumpScale),this.normalMap&&this.normalMap.isTexture&&(n.normalMap=this.normalMap.toJSON(t).uuid,n.normalMapType=this.normalMapType,n.normalScale=this.normalScale.toArray()),this.displacementMap&&this.displacementMap.isTexture&&(n.displacementMap=this.displacementMap.toJSON(t).uuid,n.displacementScale=this.displacementScale,n.displacementBias=this.displacementBias),this.roughnessMap&&this.roughnessMap.isTexture&&(n.roughnessMap=this.roughnessMap.toJSON(t).uuid),this.metalnessMap&&this.metalnessMap.isTexture&&(n.metalnessMap=this.metalnessMap.toJSON(t).uuid),this.emissiveMap&&this.emissiveMap.isTexture&&(n.emissiveMap=this.emissiveMap.toJSON(t).uuid),this.specularMap&&this.specularMap.isTexture&&(n.specularMap=this.specularMap.toJSON(t).uuid),this.specularIntensityMap&&this.specularIntensityMap.isTexture&&(n.specularIntensityMap=this.specularIntensityMap.toJSON(t).uuid),this.specularColorMap&&this.specularColorMap.isTexture&&(n.specularColorMap=this.specularColorMap.toJSON(t).uuid),this.envMap&&this.envMap.isTexture&&(n.envMap=this.envMap.toJSON(t).uuid,this.combine!==void 0&&(n.combine=this.combine)),this.envMapRotation!==void 0&&(n.envMapRotation=this.envMapRotation.toArray()),this.envMapIntensity!==void 0&&(n.envMapIntensity=this.envMapIntensity),this.reflectivity!==void 0&&(n.reflectivity=this.reflectivity),this.refractionRatio!==void 0&&(n.refractionRatio=this.refractionRatio),this.gradientMap&&this.gradientMap.isTexture&&(n.gradientMap=this.gradientMap.toJSON(t).uuid),this.transmission!==void 0&&(n.transmission=this.transmission),this.transmissionMap&&this.transmissionMap.isTexture&&(n.transmissionMap=this.transmissionMap.toJSON(t).uuid),this.thickness!==void 0&&(n.thickness=this.thickness),this.thicknessMap&&this.thicknessMap.isTexture&&(n.thicknessMap=this.thicknessMap.toJSON(t).uuid),this.attenuationDistance!==void 0&&this.attenuationDistance!==1/0&&(n.attenuationDistance=this.attenuationDistance),this.attenuationColor!==void 0&&(n.attenuationColor=this.attenuationColor.getHex()),this.size!==void 0&&(n.size=this.size),this.shadowSide!==null&&(n.shadowSide=this.shadowSide),this.sizeAttenuation!==void 0&&(n.sizeAttenuation=this.sizeAttenuation),this.blending!==bi&&(n.blending=this.blending),this.side!==Fn&&(n.side=this.side),this.vertexColors===!0&&(n.vertexColors=!0),this.opacity<1&&(n.opacity=this.opacity),this.transparent===!0&&(n.transparent=!0),this.blendSrc!==Gr&&(n.blendSrc=this.blendSrc),this.blendDst!==kr&&(n.blendDst=this.blendDst),this.blendEquation!==Yn&&(n.blendEquation=this.blendEquation),this.blendSrcAlpha!==null&&(n.blendSrcAlpha=this.blendSrcAlpha),this.blendDstAlpha!==null&&(n.blendDstAlpha=this.blendDstAlpha),this.blendEquationAlpha!==null&&(n.blendEquationAlpha=this.blendEquationAlpha),this.blendColor&&this.blendColor.isColor&&(n.blendColor=this.blendColor.getHex()),this.blendAlpha!==0&&(n.blendAlpha=this.blendAlpha),this.depthFunc!==Ti&&(n.depthFunc=this.depthFunc),this.depthTest===!1&&(n.depthTest=this.depthTest),this.depthWrite===!1&&(n.depthWrite=this.depthWrite),this.colorWrite===!1&&(n.colorWrite=this.colorWrite),this.stencilWriteMask!==255&&(n.stencilWriteMask=this.stencilWriteMask),this.stencilFunc!==fo&&(n.stencilFunc=this.stencilFunc),this.stencilRef!==0&&(n.stencilRef=this.stencilRef),this.stencilFuncMask!==255&&(n.stencilFuncMask=this.stencilFuncMask),this.stencilFail!==ii&&(n.stencilFail=this.stencilFail),this.stencilZFail!==ii&&(n.stencilZFail=this.stencilZFail),this.stencilZPass!==ii&&(n.stencilZPass=this.stencilZPass),this.stencilWrite===!0&&(n.stencilWrite=this.stencilWrite),this.rotation!==void 0&&this.rotation!==0&&(n.rotation=this.rotation),this.polygonOffset===!0&&(n.polygonOffset=!0),this.polygonOffsetFactor!==0&&(n.polygonOffsetFactor=this.polygonOffsetFactor),this.polygonOffsetUnits!==0&&(n.polygonOffsetUnits=this.polygonOffsetUnits),this.linewidth!==void 0&&this.linewidth!==1&&(n.linewidth=this.linewidth),this.dashSize!==void 0&&(n.dashSize=this.dashSize),this.gapSize!==void 0&&(n.gapSize=this.gapSize),this.scale!==void 0&&(n.scale=this.scale),this.dithering===!0&&(n.dithering=!0),this.alphaTest>0&&(n.alphaTest=this.alphaTest),this.alphaHash===!0&&(n.alphaHash=!0),this.alphaToCoverage===!0&&(n.alphaToCoverage=!0),this.premultipliedAlpha===!0&&(n.premultipliedAlpha=!0),this.forceSinglePass===!0&&(n.forceSinglePass=!0),this.wireframe===!0&&(n.wireframe=!0),this.wireframeLinewidth>1&&(n.wireframeLinewidth=this.wireframeLinewidth),this.wireframeLinecap!=="round"&&(n.wireframeLinecap=this.wireframeLinecap),this.wireframeLinejoin!=="round"&&(n.wireframeLinejoin=this.wireframeLinejoin),this.flatShading===!0&&(n.flatShading=!0),this.visible===!1&&(n.visible=!1),this.toneMapped===!1&&(n.toneMapped=!1),this.fog===!1&&(n.fog=!1),Object.keys(this.userData).length>0&&(n.userData=this.userData);function s(r){const a=[];for(const o in r){const l=r[o];delete l.metadata,a.push(l)}return a}if(e){const r=s(t.textures),a=s(t.images);r.length>0&&(n.textures=r),a.length>0&&(n.images=a)}return n}clone(){return new this.constructor().copy(this)}copy(t){this.name=t.name,this.blending=t.blending,this.side=t.side,this.vertexColors=t.vertexColors,this.opacity=t.opacity,this.transparent=t.transparent,this.blendSrc=t.blendSrc,this.blendDst=t.blendDst,this.blendEquation=t.blendEquation,this.blendSrcAlpha=t.blendSrcAlpha,this.blendDstAlpha=t.blendDstAlpha,this.blendEquationAlpha=t.blendEquationAlpha,this.blendColor.copy(t.blendColor),this.blendAlpha=t.blendAlpha,this.depthFunc=t.depthFunc,this.depthTest=t.depthTest,this.depthWrite=t.depthWrite,this.stencilWriteMask=t.stencilWriteMask,this.stencilFunc=t.stencilFunc,this.stencilRef=t.stencilRef,this.stencilFuncMask=t.stencilFuncMask,this.stencilFail=t.stencilFail,this.stencilZFail=t.stencilZFail,this.stencilZPass=t.stencilZPass,this.stencilWrite=t.stencilWrite;const e=t.clippingPlanes;let n=null;if(e!==null){const s=e.length;n=new Array(s);for(let r=0;r!==s;++r)n[r]=e[r].clone()}return this.clippingPlanes=n,this.clipIntersection=t.clipIntersection,this.clipShadows=t.clipShadows,this.shadowSide=t.shadowSide,this.colorWrite=t.colorWrite,this.precision=t.precision,this.polygonOffset=t.polygonOffset,this.polygonOffsetFactor=t.polygonOffsetFactor,this.polygonOffsetUnits=t.polygonOffsetUnits,this.dithering=t.dithering,this.alphaTest=t.alphaTest,this.alphaHash=t.alphaHash,this.alphaToCoverage=t.alphaToCoverage,this.premultipliedAlpha=t.premultipliedAlpha,this.forceSinglePass=t.forceSinglePass,this.visible=t.visible,this.toneMapped=t.toneMapped,this.userData=JSON.parse(JSON.stringify(t.userData)),this}dispose(){this.dispatchEvent({type:"dispose"})}set needsUpdate(t){t===!0&&this.version++}}class Ys extends ei{constructor(t){super(),this.isMeshBasicMaterial=!0,this.type="MeshBasicMaterial",this.color=new Yt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new nn,this.combine=Fa,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.fog=t.fog,this}}const ge=new D,Ms=new at;let yh=0;class en{constructor(t,e,n=!1){if(Array.isArray(t))throw new TypeError("THREE.BufferAttribute: array should be a Typed Array.");this.isBufferAttribute=!0,Object.defineProperty(this,"id",{value:yh++}),this.name="",this.array=t,this.itemSize=e,this.count=t!==void 0?t.length/e:0,this.normalized=n,this.usage=po,this.updateRanges=[],this.gpuType=on,this.version=0}onUploadCallback(){}set needsUpdate(t){t===!0&&this.version++}setUsage(t){return this.usage=t,this}addUpdateRange(t,e){this.updateRanges.push({start:t,count:e})}clearUpdateRanges(){this.updateRanges.length=0}copy(t){return this.name=t.name,this.array=new t.array.constructor(t.array),this.itemSize=t.itemSize,this.count=t.count,this.normalized=t.normalized,this.usage=t.usage,this.gpuType=t.gpuType,this}copyAt(t,e,n){t*=this.itemSize,n*=e.itemSize;for(let s=0,r=this.itemSize;se.count&&Bt("BufferGeometry: Buffer size too small for points data. Use .dispose() and create a new geometry."),e.needsUpdate=!0}return this}computeBoundingBox(){this.boundingBox===null&&(this.boundingBox=new On);const t=this.attributes.position,e=this.morphAttributes.position;if(t&&t.isGLBufferAttribute){de("BufferGeometry.computeBoundingBox(): GLBufferAttribute requires a manual bounding box.",this),this.boundingBox.set(new D(-1/0,-1/0,-1/0),new D(1/0,1/0,1/0));return}if(t!==void 0){if(this.boundingBox.setFromBufferAttribute(t),e)for(let n=0,s=e.length;n0&&(t.userData=this.userData),this.parameters!==void 0){const l=this.parameters;for(const c in l)l[c]!==void 0&&(t[c]=l[c]);return t}t.data={attributes:{}};const e=this.index;e!==null&&(t.data.index={type:e.array.constructor.name,array:Array.prototype.slice.call(e.array)});const n=this.attributes;for(const l in n){const c=n[l];t.data.attributes[l]=c.toJSON(t.data)}const s={};let r=!1;for(const l in this.morphAttributes){const c=this.morphAttributes[l],h=[];for(let u=0,d=c.length;u0&&(s[l]=h,r=!0)}r&&(t.data.morphAttributes=s,t.data.morphTargetsRelative=this.morphTargetsRelative);const a=this.groups;a.length>0&&(t.data.groups=JSON.parse(JSON.stringify(a)));const o=this.boundingSphere;return o!==null&&(t.data.boundingSphere=o.toJSON()),t}clone(){return new this.constructor().copy(this)}copy(t){this.index=null,this.attributes={},this.morphAttributes={},this.groups=[],this.boundingBox=null,this.boundingSphere=null;const e={};this.name=t.name;const n=t.index;n!==null&&this.setIndex(n.clone());const s=t.attributes;for(const c in s){const h=s[c];this.setAttribute(c,h.clone(e))}const r=t.morphAttributes;for(const c in r){const h=[],u=r[c];for(let d=0,p=u.length;d0){const s=e[n[0]];if(s!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,a=s.length;r(t.far-t.near)**2))&&(Co.copy(r).invert(),kn.copy(t.ray).applyMatrix4(Co),!(n.boundingBox!==null&&kn.intersectsBox(n.boundingBox)===!1)&&this._computeIntersections(t,e,kn)))}_computeIntersections(t,e,n){let s;const r=this.geometry,a=this.material,o=r.index,l=r.attributes.position,c=r.attributes.uv,h=r.attributes.uv1,u=r.attributes.normal,d=r.groups,p=r.drawRange;if(o!==null)if(Array.isArray(a))for(let x=0,_=d.length;x<_;x++){const m=d[x],f=a[m.materialIndex],b=Math.max(m.start,p.start),S=Math.min(o.count,Math.min(m.start+m.count,p.start+p.count));for(let T=b,R=S;Te.far?null:{distance:c,point:As.clone(),object:i}}function ws(i,t,e,n,s,r,a,o,l,c){i.getVertexPosition(o,ys),i.getVertexPosition(l,bs),i.getVertexPosition(c,Es);const h=Eh(i,t,e,n,ys,bs,Es,Do);if(h){const u=new D;Qe.getBarycoord(Do,ys,bs,Es,u),s&&(h.uv=Qe.getInterpolatedAttribute(s,o,l,c,u,new at)),r&&(h.uv1=Qe.getInterpolatedAttribute(r,o,l,c,u,new at)),a&&(h.normal=Qe.getInterpolatedAttribute(a,o,l,c,u,new D),h.normal.dot(n.direction)>0&&h.normal.multiplyScalar(-1));const d={a:o,b:l,c,normal:new D,materialIndex:0};Qe.getNormal(ys,bs,Es,d.normal),h.face=d,h.barycoord=u}return h}class oe extends Ie{constructor(t=1,e=1,n=1,s=1,r=1,a=1){super(),this.type="BoxGeometry",this.parameters={width:t,height:e,depth:n,widthSegments:s,heightSegments:r,depthSegments:a};const o=this;s=Math.floor(s),r=Math.floor(r),a=Math.floor(a);const l=[],c=[],h=[],u=[];let d=0,p=0;x("z","y","x",-1,-1,n,e,t,a,r,0),x("z","y","x",1,-1,n,e,-t,a,r,1),x("x","z","y",1,1,t,n,e,s,a,2),x("x","z","y",1,-1,t,n,-e,s,a,3),x("x","y","z",1,-1,t,e,n,s,r,4),x("x","y","z",-1,-1,t,e,-n,s,r,5),this.setIndex(l),this.setAttribute("position",new he(c,3)),this.setAttribute("normal",new he(h,3)),this.setAttribute("uv",new he(u,2));function x(_,m,f,b,S,T,R,w,L,F,E){const M=T/L,P=R/F,O=T/2,z=R/2,k=w/2,W=L+1,Y=F+1;let nt=0,X=0;const rt=new D;for(let st=0;st0?1:-1,h.push(rt.x,rt.y,rt.z),u.push(Gt/L),u.push(1-st/F),nt+=1}}for(let st=0;st0&&(e.defines=this.defines),e.vertexShader=this.vertexShader,e.fragmentShader=this.fragmentShader,e.lights=this.lights,e.clipping=this.clipping;const n={};for(const s in this.extensions)this.extensions[s]===!0&&(n[s]=!0);return Object.keys(n).length>0&&(e.extensions=n),e}}class Vc extends Me{constructor(){super(),this.isCamera=!0,this.type="Camera",this.matrixWorldInverse=new re,this.projectionMatrix=new re,this.projectionMatrixInverse=new re,this.coordinateSystem=cn,this._reversedDepth=!1}get reversedDepth(){return this._reversedDepth}copy(t,e){return super.copy(t,e),this.matrixWorldInverse.copy(t.matrixWorldInverse),this.projectionMatrix.copy(t.projectionMatrix),this.projectionMatrixInverse.copy(t.projectionMatrixInverse),this.coordinateSystem=t.coordinateSystem,this}getWorldDirection(t){return super.getWorldDirection(t).negate()}updateMatrixWorld(t){super.updateMatrixWorld(t),this.matrixWorldInverse.copy(this.matrixWorld).invert()}updateWorldMatrix(t,e){super.updateWorldMatrix(t,e),this.matrixWorldInverse.copy(this.matrixWorld).invert()}clone(){return new this.constructor().copy(this)}}const Pn=new D,Lo=new at,Io=new at;class ke extends Vc{constructor(t=50,e=1,n=.1,s=2e3){super(),this.isPerspectiveCamera=!0,this.type="PerspectiveCamera",this.fov=t,this.zoom=1,this.near=n,this.far=s,this.focus=10,this.aspect=e,this.view=null,this.filmGauge=35,this.filmOffset=0,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.fov=t.fov,this.zoom=t.zoom,this.near=t.near,this.far=t.far,this.focus=t.focus,this.aspect=t.aspect,this.view=t.view===null?null:Object.assign({},t.view),this.filmGauge=t.filmGauge,this.filmOffset=t.filmOffset,this}setFocalLength(t){const e=.5*this.getFilmHeight()/t;this.fov=Ra*2*Math.atan(e),this.updateProjectionMatrix()}getFocalLength(){const t=Math.tan(ks*.5*this.fov);return .5*this.getFilmHeight()/t}getEffectiveFOV(){return Ra*2*Math.atan(Math.tan(ks*.5*this.fov)/this.zoom)}getFilmWidth(){return this.filmGauge*Math.min(this.aspect,1)}getFilmHeight(){return this.filmGauge/Math.max(this.aspect,1)}getViewBounds(t,e,n){Pn.set(-1,-1,.5).applyMatrix4(this.projectionMatrixInverse),e.set(Pn.x,Pn.y).multiplyScalar(-t/Pn.z),Pn.set(1,1,.5).applyMatrix4(this.projectionMatrixInverse),n.set(Pn.x,Pn.y).multiplyScalar(-t/Pn.z)}getViewSize(t,e){return this.getViewBounds(t,Lo,Io),e.subVectors(Io,Lo)}setViewOffset(t,e,n,s,r,a){this.aspect=t/e,this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=s,this.view.width=r,this.view.height=a,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=this.near;let e=t*Math.tan(ks*.5*this.fov)/this.zoom,n=2*e,s=this.aspect*n,r=-.5*s;const a=this.view;if(this.view!==null&&this.view.enabled){const l=a.fullWidth,c=a.fullHeight;r+=a.offsetX*s/l,e-=a.offsetY*n/c,s*=a.width/l,n*=a.height/c}const o=this.filmOffset;o!==0&&(r+=t*o/this.getFilmWidth()),this.projectionMatrix.makePerspective(r,r+s,e,e-n,t,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.fov=this.fov,e.object.zoom=this.zoom,e.object.near=this.near,e.object.far=this.far,e.object.focus=this.focus,e.object.aspect=this.aspect,this.view!==null&&(e.object.view=Object.assign({},this.view)),e.object.filmGauge=this.filmGauge,e.object.filmOffset=this.filmOffset,e}}const pi=-90,mi=1;class Ch extends Me{constructor(t,e,n){super(),this.type="CubeCamera",this.renderTarget=n,this.coordinateSystem=null,this.activeMipmapLevel=0;const s=new ke(pi,mi,t,e);s.layers=this.layers,this.add(s);const r=new ke(pi,mi,t,e);r.layers=this.layers,this.add(r);const a=new ke(pi,mi,t,e);a.layers=this.layers,this.add(a);const o=new ke(pi,mi,t,e);o.layers=this.layers,this.add(o);const l=new ke(pi,mi,t,e);l.layers=this.layers,this.add(l);const c=new ke(pi,mi,t,e);c.layers=this.layers,this.add(c)}updateCoordinateSystem(){const t=this.coordinateSystem,e=this.children.concat(),[n,s,r,a,o,l]=e;for(const c of e)this.remove(c);if(t===cn)n.up.set(0,1,0),n.lookAt(1,0,0),s.up.set(0,1,0),s.lookAt(-1,0,0),r.up.set(0,0,-1),r.lookAt(0,1,0),a.up.set(0,0,1),a.lookAt(0,-1,0),o.up.set(0,1,0),o.lookAt(0,0,1),l.up.set(0,1,0),l.lookAt(0,0,-1);else if(t===Xs)n.up.set(0,-1,0),n.lookAt(-1,0,0),s.up.set(0,-1,0),s.lookAt(1,0,0),r.up.set(0,0,1),r.lookAt(0,1,0),a.up.set(0,0,-1),a.lookAt(0,-1,0),o.up.set(0,-1,0),o.lookAt(0,0,1),l.up.set(0,-1,0),l.lookAt(0,0,-1);else throw new Error("THREE.CubeCamera.updateCoordinateSystem(): Invalid coordinate system: "+t);for(const c of e)this.add(c),c.updateMatrixWorld()}update(t,e){this.parent===null&&this.updateMatrixWorld();const{renderTarget:n,activeMipmapLevel:s}=this;this.coordinateSystem!==t.coordinateSystem&&(this.coordinateSystem=t.coordinateSystem,this.updateCoordinateSystem());const[r,a,o,l,c,h]=this.children,u=t.getRenderTarget(),d=t.getActiveCubeFace(),p=t.getActiveMipmapLevel(),x=t.xr.enabled;t.xr.enabled=!1;const _=n.texture.generateMipmaps;n.texture.generateMipmaps=!1,t.setRenderTarget(n,0,s),t.render(e,r),t.setRenderTarget(n,1,s),t.render(e,a),t.setRenderTarget(n,2,s),t.render(e,o),t.setRenderTarget(n,3,s),t.render(e,l),t.setRenderTarget(n,4,s),t.render(e,c),n.texture.generateMipmaps=_,t.setRenderTarget(n,5,s),t.render(e,h),t.setRenderTarget(u,d,p),t.xr.enabled=x,n.texture.needsPMREMUpdate=!0}}class Gc extends Re{constructor(t=[],e=Ai,n,s,r,a,o,l,c,h){super(t,e,n,s,r,a,o,l,c,h),this.isCubeTexture=!0,this.flipY=!1}get images(){return this.image}set images(t){this.image=t}}class Ph extends $n{constructor(t=1,e={}){super(t,t,e),this.isWebGLCubeRenderTarget=!0;const n={width:t,height:t,depth:1},s=[n,n,n,n,n,n];this.texture=new Gc(s),this._setTextureOptions(e),this.texture.isRenderTargetTexture=!0}fromEquirectangularTexture(t,e){this.texture.type=e.type,this.texture.colorSpace=e.colorSpace,this.texture.generateMipmaps=e.generateMipmaps,this.texture.minFilter=e.minFilter,this.texture.magFilter=e.magFilter;const n={uniforms:{tEquirect:{value:null}},vertexShader:` - - varying vec3 vWorldDirection; - - vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); - - } - - void main() { - - vWorldDirection = transformDirection( position, modelMatrix ); - - #include - #include - - } - `,fragmentShader:` - - uniform sampler2D tEquirect; - - varying vec3 vWorldDirection; - - #include - - void main() { - - vec3 direction = normalize( vWorldDirection ); - - vec2 sampleUV = equirectUv( direction ); - - gl_FragColor = texture2D( tEquirect, sampleUV ); - - } - `},s=new oe(5,5,5),r=new En({name:"CubemapFromEquirect",uniforms:Ci(n.uniforms),vertexShader:n.vertexShader,fragmentShader:n.fragmentShader,side:Le,blending:yn});r.uniforms.tEquirect.value=e;const a=new Vt(s,r),o=e.minFilter;return e.minFilter===Kn&&(e.minFilter=Ye),new Ch(1,10,this).update(t,a),e.minFilter=o,a.geometry.dispose(),a.material.dispose(),this}clear(t,e=!0,n=!0,s=!0){const r=t.getRenderTarget();for(let a=0;a<6;a++)t.setRenderTarget(this,a),t.clear(e,n,s);t.setRenderTarget(r)}}class ln extends Me{constructor(){super(),this.isGroup=!0,this.type="Group"}}const Dh={type:"move"};class Tr{constructor(){this._targetRay=null,this._grip=null,this._hand=null}getHandSpace(){return this._hand===null&&(this._hand=new ln,this._hand.matrixAutoUpdate=!1,this._hand.visible=!1,this._hand.joints={},this._hand.inputState={pinching:!1}),this._hand}getTargetRaySpace(){return this._targetRay===null&&(this._targetRay=new ln,this._targetRay.matrixAutoUpdate=!1,this._targetRay.visible=!1,this._targetRay.hasLinearVelocity=!1,this._targetRay.linearVelocity=new D,this._targetRay.hasAngularVelocity=!1,this._targetRay.angularVelocity=new D),this._targetRay}getGripSpace(){return this._grip===null&&(this._grip=new ln,this._grip.matrixAutoUpdate=!1,this._grip.visible=!1,this._grip.hasLinearVelocity=!1,this._grip.linearVelocity=new D,this._grip.hasAngularVelocity=!1,this._grip.angularVelocity=new D),this._grip}dispatchEvent(t){return this._targetRay!==null&&this._targetRay.dispatchEvent(t),this._grip!==null&&this._grip.dispatchEvent(t),this._hand!==null&&this._hand.dispatchEvent(t),this}connect(t){if(t&&t.hand){const e=this._hand;if(e)for(const n of t.hand.values())this._getHandJoint(e,n)}return this.dispatchEvent({type:"connected",data:t}),this}disconnect(t){return this.dispatchEvent({type:"disconnected",data:t}),this._targetRay!==null&&(this._targetRay.visible=!1),this._grip!==null&&(this._grip.visible=!1),this._hand!==null&&(this._hand.visible=!1),this}update(t,e,n){let s=null,r=null,a=null;const o=this._targetRay,l=this._grip,c=this._hand;if(t&&e.session.visibilityState!=="visible-blurred"){if(c&&t.hand){a=!0;for(const _ of t.hand.values()){const m=e.getJointPose(_,n),f=this._getHandJoint(c,_);m!==null&&(f.matrix.fromArray(m.transform.matrix),f.matrix.decompose(f.position,f.rotation,f.scale),f.matrixWorldNeedsUpdate=!0,f.jointRadius=m.radius),f.visible=m!==null}const h=c.joints["index-finger-tip"],u=c.joints["thumb-tip"],d=h.position.distanceTo(u.position),p=.02,x=.005;c.inputState.pinching&&d>p+x?(c.inputState.pinching=!1,this.dispatchEvent({type:"pinchend",handedness:t.handedness,target:this})):!c.inputState.pinching&&d<=p-x&&(c.inputState.pinching=!0,this.dispatchEvent({type:"pinchstart",handedness:t.handedness,target:this}))}else l!==null&&t.gripSpace&&(r=e.getPose(t.gripSpace,n),r!==null&&(l.matrix.fromArray(r.transform.matrix),l.matrix.decompose(l.position,l.rotation,l.scale),l.matrixWorldNeedsUpdate=!0,r.linearVelocity?(l.hasLinearVelocity=!0,l.linearVelocity.copy(r.linearVelocity)):l.hasLinearVelocity=!1,r.angularVelocity?(l.hasAngularVelocity=!0,l.angularVelocity.copy(r.angularVelocity)):l.hasAngularVelocity=!1));o!==null&&(s=e.getPose(t.targetRaySpace,n),s===null&&r!==null&&(s=r),s!==null&&(o.matrix.fromArray(s.transform.matrix),o.matrix.decompose(o.position,o.rotation,o.scale),o.matrixWorldNeedsUpdate=!0,s.linearVelocity?(o.hasLinearVelocity=!0,o.linearVelocity.copy(s.linearVelocity)):o.hasLinearVelocity=!1,s.angularVelocity?(o.hasAngularVelocity=!0,o.angularVelocity.copy(s.angularVelocity)):o.hasAngularVelocity=!1,this.dispatchEvent(Dh)))}return o!==null&&(o.visible=s!==null),l!==null&&(l.visible=r!==null),c!==null&&(c.visible=a!==null),this}_getHandJoint(t,e){if(t.joints[e.jointName]===void 0){const n=new ln;n.matrixAutoUpdate=!1,n.visible=!1,t.joints[e.jointName]=n,t.add(n)}return t.joints[e.jointName]}}class kc extends Me{constructor(){super(),this.isScene=!0,this.type="Scene",this.background=null,this.environment=null,this.fog=null,this.backgroundBlurriness=0,this.backgroundIntensity=1,this.backgroundRotation=new nn,this.environmentIntensity=1,this.environmentRotation=new nn,this.overrideMaterial=null,typeof __THREE_DEVTOOLS__<"u"&&__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent("observe",{detail:this}))}copy(t,e){return super.copy(t,e),t.background!==null&&(this.background=t.background.clone()),t.environment!==null&&(this.environment=t.environment.clone()),t.fog!==null&&(this.fog=t.fog.clone()),this.backgroundBlurriness=t.backgroundBlurriness,this.backgroundIntensity=t.backgroundIntensity,this.backgroundRotation.copy(t.backgroundRotation),this.environmentIntensity=t.environmentIntensity,this.environmentRotation.copy(t.environmentRotation),t.overrideMaterial!==null&&(this.overrideMaterial=t.overrideMaterial.clone()),this.matrixAutoUpdate=t.matrixAutoUpdate,this}toJSON(t){const e=super.toJSON(t);return this.fog!==null&&(e.object.fog=this.fog.toJSON()),this.backgroundBlurriness>0&&(e.object.backgroundBlurriness=this.backgroundBlurriness),this.backgroundIntensity!==1&&(e.object.backgroundIntensity=this.backgroundIntensity),e.object.backgroundRotation=this.backgroundRotation.toArray(),this.environmentIntensity!==1&&(e.object.environmentIntensity=this.environmentIntensity),e.object.environmentRotation=this.environmentRotation.toArray(),e}}class Hc extends Re{constructor(t=null,e=1,n=1,s,r,a,o,l,c=He,h=He,u,d){super(null,a,o,l,c,h,s,r,u,d),this.isDataTexture=!0,this.image={data:t,width:e,height:n},this.generateMipmaps=!1,this.flipY=!1,this.unpackAlignment=1}}class Uo extends en{constructor(t,e,n,s=1){super(t,e,n),this.isInstancedBufferAttribute=!0,this.meshPerAttribute=s}copy(t){return super.copy(t),this.meshPerAttribute=t.meshPerAttribute,this}toJSON(){const t=super.toJSON();return t.meshPerAttribute=this.meshPerAttribute,t.isInstancedBufferAttribute=!0,t}}const xi=new re,No=new re,Rs=[],Fo=new On,Lh=new re,Gi=new Vt,ki=new Ii;class Ih extends Vt{constructor(t,e,n){super(t,e),this.isInstancedMesh=!0,this.instanceMatrix=new Uo(new Float32Array(n*16),16),this.instanceColor=null,this.morphTexture=null,this.count=n,this.boundingBox=null,this.boundingSphere=null;for(let s=0;s1?null:e.copy(t.start).addScaledVector(n,r)}intersectsLine(t){const e=this.distanceToPoint(t.start),n=this.distanceToPoint(t.end);return e<0&&n>0||n<0&&e>0}intersectsBox(t){return t.intersectsPlane(this)}intersectsSphere(t){return t.intersectsPlane(this)}coplanarPoint(t){return t.copy(this.normal).multiplyScalar(-this.constant)}applyMatrix4(t,e){const n=e||Nh.getNormalMatrix(t),s=this.coplanarPoint(Ar).applyMatrix4(t),r=this.normal.applyMatrix3(n).normalize();return this.constant=-s.dot(r),this}translate(t){return this.constant-=t.dot(this.normal),this}equals(t){return t.normal.equals(this.normal)&&t.constant===this.constant}clone(){return new this.constructor().copy(this)}}const Hn=new Ii,Fh=new at(.5,.5),Cs=new D;class Za{constructor(t=new Dn,e=new Dn,n=new Dn,s=new Dn,r=new Dn,a=new Dn){this.planes=[t,e,n,s,r,a]}set(t,e,n,s,r,a){const o=this.planes;return o[0].copy(t),o[1].copy(e),o[2].copy(n),o[3].copy(s),o[4].copy(r),o[5].copy(a),this}copy(t){const e=this.planes;for(let n=0;n<6;n++)e[n].copy(t.planes[n]);return this}setFromProjectionMatrix(t,e=cn,n=!1){const s=this.planes,r=t.elements,a=r[0],o=r[1],l=r[2],c=r[3],h=r[4],u=r[5],d=r[6],p=r[7],x=r[8],_=r[9],m=r[10],f=r[11],b=r[12],S=r[13],T=r[14],R=r[15];if(s[0].setComponents(c-a,p-h,f-x,R-b).normalize(),s[1].setComponents(c+a,p+h,f+x,R+b).normalize(),s[2].setComponents(c+o,p+u,f+_,R+S).normalize(),s[3].setComponents(c-o,p-u,f-_,R-S).normalize(),n)s[4].setComponents(l,d,m,T).normalize(),s[5].setComponents(c-l,p-d,f-m,R-T).normalize();else if(s[4].setComponents(c-l,p-d,f-m,R-T).normalize(),e===cn)s[5].setComponents(c+l,p+d,f+m,R+T).normalize();else if(e===Xs)s[5].setComponents(l,d,m,T).normalize();else throw new Error("THREE.Frustum.setFromProjectionMatrix(): Invalid coordinate system: "+e);return this}intersectsObject(t){if(t.boundingSphere!==void 0)t.boundingSphere===null&&t.computeBoundingSphere(),Hn.copy(t.boundingSphere).applyMatrix4(t.matrixWorld);else{const e=t.geometry;e.boundingSphere===null&&e.computeBoundingSphere(),Hn.copy(e.boundingSphere).applyMatrix4(t.matrixWorld)}return this.intersectsSphere(Hn)}intersectsSprite(t){Hn.center.set(0,0,0);const e=Fh.distanceTo(t.center);return Hn.radius=.7071067811865476+e,Hn.applyMatrix4(t.matrixWorld),this.intersectsSphere(Hn)}intersectsSphere(t){const e=this.planes,n=t.center,s=-t.radius;for(let r=0;r<6;r++)if(e[r].distanceToPoint(n)0?t.max.x:t.min.x,Cs.y=s.normal.y>0?t.max.y:t.min.y,Cs.z=s.normal.z>0?t.max.z:t.min.z,s.distanceToPoint(Cs)<0)return!1}return!0}containsPoint(t){const e=this.planes;for(let n=0;n<6;n++)if(e[n].distanceToPoint(t)<0)return!1;return!0}clone(){return new this.constructor().copy(this)}}class Wc extends ei{constructor(t){super(),this.isLineBasicMaterial=!0,this.type="LineBasicMaterial",this.color=new Yt(16777215),this.map=null,this.linewidth=1,this.linecap="round",this.linejoin="round",this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.linewidth=t.linewidth,this.linecap=t.linecap,this.linejoin=t.linejoin,this.fog=t.fog,this}}const Zs=new D,Ks=new D,Oo=new re,Hi=new $s,Ps=new Ii,wr=new D,Bo=new D;class Oh extends Me{constructor(t=new Ie,e=new Wc){super(),this.isLine=!0,this.type="Line",this.geometry=t,this.material=e,this.morphTargetDictionary=void 0,this.morphTargetInfluences=void 0,this.updateMorphTargets()}copy(t,e){return super.copy(t,e),this.material=Array.isArray(t.material)?t.material.slice():t.material,this.geometry=t.geometry,this}computeLineDistances(){const t=this.geometry;if(t.index===null){const e=t.attributes.position,n=[0];for(let s=1,r=e.count;s0){const s=e[n[0]];if(s!==void 0){this.morphTargetInfluences=[],this.morphTargetDictionary={};for(let r=0,a=s.length;rn)return;wr.applyMatrix4(i.matrixWorld);const c=t.ray.origin.distanceTo(wr);if(!(ct.far))return{distance:c,point:Bo.clone().applyMatrix4(i.matrixWorld),index:a,face:null,faceIndex:null,barycoord:null,object:i}}const zo=new D,Vo=new D;class Bh extends Oh{constructor(t,e){super(t,e),this.isLineSegments=!0,this.type="LineSegments"}computeLineDistances(){const t=this.geometry;if(t.index===null){const e=t.attributes.position,n=[];for(let s=0,r=e.count;s0&&S(!0),e>0&&S(!1)),this.setIndex(h),this.setAttribute("position",new he(u,3)),this.setAttribute("normal",new he(d,3)),this.setAttribute("uv",new he(p,2));function b(){const T=new D,R=new D;let w=0;const L=(e-t)/n;for(let F=0;F<=r;F++){const E=[],M=F/r,P=M*(e-t)+t;for(let O=0;O<=s;O++){const z=O/s,k=z*l+o,W=Math.sin(k),Y=Math.cos(k);R.x=P*W,R.y=-M*n+m,R.z=P*Y,u.push(R.x,R.y,R.z),T.set(W,L,Y).normalize(),d.push(T.x,T.y,T.z),p.push(z,1-M),E.push(x++)}_.push(E)}for(let F=0;F0||E!==0)&&(h.push(M,P,z),w+=3),(e>0||E!==r-1)&&(h.push(P,O,z),w+=3)}c.addGroup(f,w,0),f+=w}function S(T){const R=x,w=new at,L=new D;let F=0;const E=T===!0?t:e,M=T===!0?1:-1;for(let O=1;O<=s;O++)u.push(0,m*M,0),d.push(0,M,0),p.push(.5,.5),x++;const P=x;for(let O=0;O<=s;O++){const k=O/s*l+o,W=Math.cos(k),Y=Math.sin(k);L.x=E*Y,L.y=m*M,L.z=E*W,u.push(L.x,L.y,L.z),d.push(0,M,0),w.x=W*.5+.5,w.y=Y*.5*M+.5,p.push(w.x,w.y),x++}for(let O=0;O0)l=s-1;else{l=s;break}if(s=l,n[s]===a)return s/(r-1);const h=n[s],d=n[s+1]-h,p=(a-h)/d;return(s+p)/(r-1)}getTangent(t,e){let s=t-1e-4,r=t+1e-4;s<0&&(s=0),r>1&&(r=1);const a=this.getPoint(s),o=this.getPoint(r),l=e||(a.isVector2?new at:new D);return l.copy(o).sub(a).normalize(),l}getTangentAt(t,e){const n=this.getUtoTmapping(t);return this.getTangent(n,e)}computeFrenetFrames(t,e=!1){const n=new D,s=[],r=[],a=[],o=new D,l=new re;for(let p=0;p<=t;p++){const x=p/t;s[p]=this.getTangentAt(x,new D)}r[0]=new D,a[0]=new D;let c=Number.MAX_VALUE;const h=Math.abs(s[0].x),u=Math.abs(s[0].y),d=Math.abs(s[0].z);h<=c&&(c=h,n.set(1,0,0)),u<=c&&(c=u,n.set(0,1,0)),d<=c&&n.set(0,0,1),o.crossVectors(s[0],n).normalize(),r[0].crossVectors(s[0],o),a[0].crossVectors(s[0],r[0]);for(let p=1;p<=t;p++){if(r[p]=r[p-1].clone(),a[p]=a[p-1].clone(),o.crossVectors(s[p-1],s[p]),o.length()>Number.EPSILON){o.normalize();const x=Math.acos(Xt(s[p-1].dot(s[p]),-1,1));r[p].applyMatrix4(l.makeRotationAxis(o,x))}a[p].crossVectors(s[p],r[p])}if(e===!0){let p=Math.acos(Xt(r[0].dot(r[t]),-1,1));p/=t,s[0].dot(o.crossVectors(r[0],r[t]))>0&&(p=-p);for(let x=1;x<=t;x++)r[x].applyMatrix4(l.makeRotationAxis(s[x],p*x)),a[x].crossVectors(s[x],r[x])}return{tangents:s,normals:r,binormals:a}}clone(){return new this.constructor().copy(this)}copy(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}toJSON(){const t={metadata:{version:4.7,type:"Curve",generator:"Curve.toJSON"}};return t.arcLengthDivisions=this.arcLengthDivisions,t.type=this.type,t}fromJSON(t){return this.arcLengthDivisions=t.arcLengthDivisions,this}}class ja extends un{constructor(t=0,e=0,n=1,s=1,r=0,a=Math.PI*2,o=!1,l=0){super(),this.isEllipseCurve=!0,this.type="EllipseCurve",this.aX=t,this.aY=e,this.xRadius=n,this.yRadius=s,this.aStartAngle=r,this.aEndAngle=a,this.aClockwise=o,this.aRotation=l}getPoint(t,e=new at){const n=e,s=Math.PI*2;let r=this.aEndAngle-this.aStartAngle;const a=Math.abs(r)s;)r-=s;r0?0:(Math.floor(Math.abs(o)/r)+1)*r:l===0&&o===r-1&&(o=r-2,l=1);let c,h;this.closed||o>0?c=s[(o-1)%r]:(Ls.subVectors(s[0],s[1]).add(s[0]),c=Ls);const u=s[o%r],d=s[(o+1)%r];if(this.closed||o+2s.length-2?s.length-1:a+1],u=s[a>s.length-3?s.length-1:a+2];return n.set(Go(o,l.x,c.x,h.x,u.x),Go(o,l.y,c.y,h.y,u.y)),n}copy(t){super.copy(t),this.points=[];for(let e=0,n=t.points.length;e=n){const a=s[r]-n,o=this.curves[r],l=o.getLength(),c=l===0?0:1-a/l;return o.getPointAt(c,e)}r++}return null}getLength(){const t=this.getCurveLengths();return t[t.length-1]}updateArcLengths(){this.needsUpdate=!0,this.cacheLengths=null,this.getCurveLengths()}getCurveLengths(){if(this.cacheLengths&&this.cacheLengths.length===this.curves.length)return this.cacheLengths;const t=[];let e=0;for(let n=0,s=this.curves.length;n1&&!e[e.length-1].equals(e[0])&&e.push(e[0]),e}copy(t){super.copy(t),this.curves=[];for(let e=0,n=t.curves.length;e0){const u=c.getPoint(0);u.equals(this.currentPoint)||this.lineTo(u.x,u.y)}this.curves.push(c);const h=c.getPoint(1);return this.currentPoint.copy(h),this}copy(t){return super.copy(t),this.currentPoint.copy(t.currentPoint),this}toJSON(){const t=super.toJSON();return t.currentPoint=this.currentPoint.toArray(),t}fromJSON(t){return super.fromJSON(t),this.currentPoint.fromArray(t.currentPoint),this}}class js extends rs{constructor(t){super(t),this.uuid=Li(),this.type="Shape",this.holes=[]}getPointsHoles(t){const e=[];for(let n=0,s=this.holes.length;n80*e){o=i[0],l=i[1];let h=o,u=l;for(let d=e;dh&&(h=p),x>u&&(u=x)}c=Math.max(h-o,u-l),c=c!==0?32767/c:0}return as(r,a,e,o,l,c,0),a}function Jc(i,t,e,n,s){let r;if(s===pu(i,t,e,n)>0)for(let a=t;a=t;a-=n)r=ko(a/n|0,i[a],i[a+1],r);return r&&Pi(r,r.next)&&(cs(r),r=r.next),r}function Qn(i,t){if(!i)return i;t||(t=i);let e=i,n;do if(n=!1,!e.steiner&&(Pi(e,e.next)||pe(e.prev,e,e.next)===0)){if(cs(e),e=t=e.prev,e===e.next)break;n=!0}else e=e.next;while(n||e!==t);return t}function as(i,t,e,n,s,r,a){if(!i)return;!a&&r&&cu(i,n,s,r);let o=i;for(;i.prev!==i.next;){const l=i.prev,c=i.next;if(r?tu(i,n,s,r):Qh(i)){t.push(l.i,i.i,c.i),cs(i),i=c.next,o=c.next;continue}if(i=c,i===o){a?a===1?(i=eu(Qn(i),t),as(i,t,e,n,s,r,2)):a===2&&nu(i,t,e,n,s,r):as(Qn(i),t,e,n,s,r,1);break}}}function Qh(i){const t=i.prev,e=i,n=i.next;if(pe(t,e,n)>=0)return!1;const s=t.x,r=e.x,a=n.x,o=t.y,l=e.y,c=n.y,h=Math.min(s,r,a),u=Math.min(o,l,c),d=Math.max(s,r,a),p=Math.max(o,l,c);let x=n.next;for(;x!==t;){if(x.x>=h&&x.x<=d&&x.y>=u&&x.y<=p&&Yi(s,o,r,l,a,c,x.x,x.y)&&pe(x.prev,x,x.next)>=0)return!1;x=x.next}return!0}function tu(i,t,e,n){const s=i.prev,r=i,a=i.next;if(pe(s,r,a)>=0)return!1;const o=s.x,l=r.x,c=a.x,h=s.y,u=r.y,d=a.y,p=Math.min(o,l,c),x=Math.min(h,u,d),_=Math.max(o,l,c),m=Math.max(h,u,d),f=Pa(p,x,t,e,n),b=Pa(_,m,t,e,n);let S=i.prevZ,T=i.nextZ;for(;S&&S.z>=f&&T&&T.z<=b;){if(S.x>=p&&S.x<=_&&S.y>=x&&S.y<=m&&S!==s&&S!==a&&Yi(o,h,l,u,c,d,S.x,S.y)&&pe(S.prev,S,S.next)>=0||(S=S.prevZ,T.x>=p&&T.x<=_&&T.y>=x&&T.y<=m&&T!==s&&T!==a&&Yi(o,h,l,u,c,d,T.x,T.y)&&pe(T.prev,T,T.next)>=0))return!1;T=T.nextZ}for(;S&&S.z>=f;){if(S.x>=p&&S.x<=_&&S.y>=x&&S.y<=m&&S!==s&&S!==a&&Yi(o,h,l,u,c,d,S.x,S.y)&&pe(S.prev,S,S.next)>=0)return!1;S=S.prevZ}for(;T&&T.z<=b;){if(T.x>=p&&T.x<=_&&T.y>=x&&T.y<=m&&T!==s&&T!==a&&Yi(o,h,l,u,c,d,T.x,T.y)&&pe(T.prev,T,T.next)>=0)return!1;T=T.nextZ}return!0}function eu(i,t){let e=i;do{const n=e.prev,s=e.next.next;!Pi(n,s)&&Qc(n,e,e.next,s)&&os(n,s)&&os(s,n)&&(t.push(n.i,e.i,s.i),cs(e),cs(e.next),e=i=s),e=e.next}while(e!==i);return Qn(e)}function nu(i,t,e,n,s,r){let a=i;do{let o=a.next.next;for(;o!==a.prev;){if(a.i!==o.i&&uu(a,o)){let l=tl(a,o);a=Qn(a,a.next),l=Qn(l,l.next),as(a,t,e,n,s,r,0),as(l,t,e,n,s,r,0);return}o=o.next}a=a.next}while(a!==i)}function iu(i,t,e,n){const s=[];for(let r=0,a=t.length;r=e.next.y&&e.next.y!==e.y){const u=e.x+(s-e.y)*(e.next.x-e.x)/(e.next.y-e.y);if(u<=n&&u>r&&(r=u,a=e.x=e.x&&e.x>=l&&n!==e.x&&$c(sa.x||e.x===a.x&&ou(a,e)))&&(a=e,h=u)}e=e.next}while(e!==o);return a}function ou(i,t){return pe(i.prev,i,t.prev)<0&&pe(t.next,i,i.next)<0}function cu(i,t,e,n){let s=i;do s.z===0&&(s.z=Pa(s.x,s.y,t,e,n)),s.prevZ=s.prev,s.nextZ=s.next,s=s.next;while(s!==i);s.prevZ.nextZ=null,s.prevZ=null,lu(s)}function lu(i){let t,e=1;do{let n=i,s;i=null;let r=null;for(t=0;n;){t++;let a=n,o=0;for(let c=0;c0||l>0&&a;)o!==0&&(l===0||!a||n.z<=a.z)?(s=n,n=n.nextZ,o--):(s=a,a=a.nextZ,l--),r?r.nextZ=s:i=s,s.prevZ=r,r=s;n=a}r.nextZ=null,e*=2}while(t>1);return i}function Pa(i,t,e,n,s){return i=(i-e)*s|0,t=(t-n)*s|0,i=(i|i<<8)&16711935,i=(i|i<<4)&252645135,i=(i|i<<2)&858993459,i=(i|i<<1)&1431655765,t=(t|t<<8)&16711935,t=(t|t<<4)&252645135,t=(t|t<<2)&858993459,t=(t|t<<1)&1431655765,i|t<<1}function hu(i){let t=i,e=i;do(t.x=(i-a)*(r-o)&&(i-a)*(n-o)>=(e-a)*(t-o)&&(e-a)*(r-o)>=(s-a)*(n-o)}function Yi(i,t,e,n,s,r,a,o){return!(i===a&&t===o)&&$c(i,t,e,n,s,r,a,o)}function uu(i,t){return i.next.i!==t.i&&i.prev.i!==t.i&&!fu(i,t)&&(os(i,t)&&os(t,i)&&du(i,t)&&(pe(i.prev,i,t.prev)||pe(i,t.prev,t))||Pi(i,t)&&pe(i.prev,i,i.next)>0&&pe(t.prev,t,t.next)>0)}function pe(i,t,e){return(t.y-i.y)*(e.x-t.x)-(t.x-i.x)*(e.y-t.y)}function Pi(i,t){return i.x===t.x&&i.y===t.y}function Qc(i,t,e,n){const s=Us(pe(i,t,e)),r=Us(pe(i,t,n)),a=Us(pe(e,n,i)),o=Us(pe(e,n,t));return!!(s!==r&&a!==o||s===0&&Is(i,e,t)||r===0&&Is(i,n,t)||a===0&&Is(e,i,n)||o===0&&Is(e,t,n))}function Is(i,t,e){return t.x<=Math.max(i.x,e.x)&&t.x>=Math.min(i.x,e.x)&&t.y<=Math.max(i.y,e.y)&&t.y>=Math.min(i.y,e.y)}function Us(i){return i>0?1:i<0?-1:0}function fu(i,t){let e=i;do{if(e.i!==i.i&&e.next.i!==i.i&&e.i!==t.i&&e.next.i!==t.i&&Qc(e,e.next,i,t))return!0;e=e.next}while(e!==i);return!1}function os(i,t){return pe(i.prev,i,i.next)<0?pe(i,t,i.next)>=0&&pe(i,i.prev,t)>=0:pe(i,t,i.prev)<0||pe(i,i.next,t)<0}function du(i,t){let e=i,n=!1;const s=(i.x+t.x)/2,r=(i.y+t.y)/2;do e.y>r!=e.next.y>r&&e.next.y!==e.y&&s<(e.next.x-e.x)*(r-e.y)/(e.next.y-e.y)+e.x&&(n=!n),e=e.next;while(e!==i);return n}function tl(i,t){const e=Da(i.i,i.x,i.y),n=Da(t.i,t.x,t.y),s=i.next,r=t.prev;return i.next=t,t.prev=i,e.next=s,s.prev=e,n.next=e,e.prev=n,r.next=n,n.prev=r,n}function ko(i,t,e,n){const s=Da(i,t,e);return n?(s.next=n.next,s.prev=n,n.next.prev=s,n.next=s):(s.prev=s,s.next=s),s}function cs(i){i.next.prev=i.prev,i.prev.next=i.next,i.prevZ&&(i.prevZ.nextZ=i.nextZ),i.nextZ&&(i.nextZ.prevZ=i.prevZ)}function Da(i,t,e){return{i,x:t,y:e,prev:null,next:null,z:0,prevZ:null,nextZ:null,steiner:!1}}function pu(i,t,e,n){let s=0;for(let r=t,a=e-n;r2&&i[t-1].equals(i[0])&&i.pop()}function Wo(i,t){for(let e=0;eNumber.EPSILON){const q=Math.sqrt(g),$=Math.sqrt(Mt*Mt+A*A),H=y.x-ft/q,wt=y.y+vt/q,dt=Z.x-A/$,Pt=Z.y+Mt/$,At=((dt-H)*A-(Pt-wt)*Mt)/(vt*A-ft*Mt);Q=H+vt*At-B.x,ot=wt+ft*At-B.y;const tt=Q*Q+ot*ot;if(tt<=2)return new at(Q,ot);et=Math.sqrt(tt/2)}else{let q=!1;vt>Number.EPSILON?Mt>Number.EPSILON&&(q=!0):vt<-Number.EPSILON?Mt<-Number.EPSILON&&(q=!0):Math.sign(ft)===Math.sign(A)&&(q=!0),q?(Q=-ft,ot=vt,et=Math.sqrt(g)):(Q=vt,ot=ft,et=Math.sqrt(g/2))}return new at(Q/et,ot/et)}const rt=[];for(let B=0,y=W.length,Z=y-1,Q=B+1;B=0;B--){const y=B/m,Z=p*Math.cos(y*Math.PI/2),Q=x*Math.sin(y*Math.PI/2)+_;for(let ot=0,et=W.length;ot=0;){const Q=Z;let ot=Z-1;ot<0&&(ot=B.length-1);for(let et=0,vt=h+m*2;et0)&&p.push(S,T,w),(f!==n-1||l0!=t>0&&this.version++,this._anisotropy=t}get clearcoat(){return this._clearcoat}set clearcoat(t){this._clearcoat>0!=t>0&&this.version++,this._clearcoat=t}get iridescence(){return this._iridescence}set iridescence(t){this._iridescence>0!=t>0&&this.version++,this._iridescence=t}get dispersion(){return this._dispersion}set dispersion(t){this._dispersion>0!=t>0&&this.version++,this._dispersion=t}get sheen(){return this._sheen}set sheen(t){this._sheen>0!=t>0&&this.version++,this._sheen=t}get transmission(){return this._transmission}set transmission(t){this._transmission>0!=t>0&&this.version++,this._transmission=t}copy(t){return super.copy(t),this.defines={STANDARD:"",PHYSICAL:""},this.anisotropy=t.anisotropy,this.anisotropyRotation=t.anisotropyRotation,this.anisotropyMap=t.anisotropyMap,this.clearcoat=t.clearcoat,this.clearcoatMap=t.clearcoatMap,this.clearcoatRoughness=t.clearcoatRoughness,this.clearcoatRoughnessMap=t.clearcoatRoughnessMap,this.clearcoatNormalMap=t.clearcoatNormalMap,this.clearcoatNormalScale.copy(t.clearcoatNormalScale),this.dispersion=t.dispersion,this.ior=t.ior,this.iridescence=t.iridescence,this.iridescenceMap=t.iridescenceMap,this.iridescenceIOR=t.iridescenceIOR,this.iridescenceThicknessRange=[...t.iridescenceThicknessRange],this.iridescenceThicknessMap=t.iridescenceThicknessMap,this.sheen=t.sheen,this.sheenColor.copy(t.sheenColor),this.sheenColorMap=t.sheenColorMap,this.sheenRoughness=t.sheenRoughness,this.sheenRoughnessMap=t.sheenRoughnessMap,this.transmission=t.transmission,this.transmissionMap=t.transmissionMap,this.thickness=t.thickness,this.thicknessMap=t.thicknessMap,this.attenuationDistance=t.attenuationDistance,this.attenuationColor.copy(t.attenuationColor),this.specularIntensity=t.specularIntensity,this.specularIntensityMap=t.specularIntensityMap,this.specularColor.copy(t.specularColor),this.specularColorMap=t.specularColorMap,this}}class vu extends ei{constructor(t){super(),this.isMeshLambertMaterial=!0,this.type="MeshLambertMaterial",this.color=new Yt(16777215),this.map=null,this.lightMap=null,this.lightMapIntensity=1,this.aoMap=null,this.aoMapIntensity=1,this.emissive=new Yt(0),this.emissiveIntensity=1,this.emissiveMap=null,this.bumpMap=null,this.bumpScale=1,this.normalMap=null,this.normalMapType=Xa,this.normalScale=new at(1,1),this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.specularMap=null,this.alphaMap=null,this.envMap=null,this.envMapRotation=new nn,this.combine=Fa,this.reflectivity=1,this.refractionRatio=.98,this.wireframe=!1,this.wireframeLinewidth=1,this.wireframeLinecap="round",this.wireframeLinejoin="round",this.flatShading=!1,this.fog=!0,this.setValues(t)}copy(t){return super.copy(t),this.color.copy(t.color),this.map=t.map,this.lightMap=t.lightMap,this.lightMapIntensity=t.lightMapIntensity,this.aoMap=t.aoMap,this.aoMapIntensity=t.aoMapIntensity,this.emissive.copy(t.emissive),this.emissiveMap=t.emissiveMap,this.emissiveIntensity=t.emissiveIntensity,this.bumpMap=t.bumpMap,this.bumpScale=t.bumpScale,this.normalMap=t.normalMap,this.normalMapType=t.normalMapType,this.normalScale.copy(t.normalScale),this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.specularMap=t.specularMap,this.alphaMap=t.alphaMap,this.envMap=t.envMap,this.envMapRotation.copy(t.envMapRotation),this.combine=t.combine,this.reflectivity=t.reflectivity,this.refractionRatio=t.refractionRatio,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this.wireframeLinecap=t.wireframeLinecap,this.wireframeLinejoin=t.wireframeLinejoin,this.flatShading=t.flatShading,this.fog=t.fog,this}}class Mu extends ei{constructor(t){super(),this.isMeshDepthMaterial=!0,this.type="MeshDepthMaterial",this.depthPacking=Zl,this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.wireframe=!1,this.wireframeLinewidth=1,this.setValues(t)}copy(t){return super.copy(t),this.depthPacking=t.depthPacking,this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this.wireframe=t.wireframe,this.wireframeLinewidth=t.wireframeLinewidth,this}}class Su extends ei{constructor(t){super(),this.isMeshDistanceMaterial=!0,this.type="MeshDistanceMaterial",this.map=null,this.alphaMap=null,this.displacementMap=null,this.displacementScale=1,this.displacementBias=0,this.setValues(t)}copy(t){return super.copy(t),this.map=t.map,this.alphaMap=t.alphaMap,this.displacementMap=t.displacementMap,this.displacementScale=t.displacementScale,this.displacementBias=t.displacementBias,this}}class $a extends Me{constructor(t,e=1){super(),this.isLight=!0,this.type="Light",this.color=new Yt(t),this.intensity=e}dispose(){}copy(t,e){return super.copy(t,e),this.color.copy(t.color),this.intensity=t.intensity,this}toJSON(t){const e=super.toJSON(t);return e.object.color=this.color.getHex(),e.object.intensity=this.intensity,this.groundColor!==void 0&&(e.object.groundColor=this.groundColor.getHex()),this.distance!==void 0&&(e.object.distance=this.distance),this.angle!==void 0&&(e.object.angle=this.angle),this.decay!==void 0&&(e.object.decay=this.decay),this.penumbra!==void 0&&(e.object.penumbra=this.penumbra),this.shadow!==void 0&&(e.object.shadow=this.shadow.toJSON()),this.target!==void 0&&(e.object.target=this.target.uuid),e}}class yu extends $a{constructor(t,e,n){super(t,n),this.isHemisphereLight=!0,this.type="HemisphereLight",this.position.copy(Me.DEFAULT_UP),this.updateMatrix(),this.groundColor=new Yt(e)}copy(t,e){return super.copy(t,e),this.groundColor.copy(t.groundColor),this}}const Dr=new re,Xo=new D,qo=new D;class el{constructor(t){this.camera=t,this.intensity=1,this.bias=0,this.normalBias=0,this.radius=1,this.blurSamples=8,this.mapSize=new at(512,512),this.mapType=hn,this.map=null,this.mapPass=null,this.matrix=new re,this.autoUpdate=!0,this.needsUpdate=!1,this._frustum=new Za,this._frameExtents=new at(1,1),this._viewportCount=1,this._viewports=[new se(0,0,1,1)]}getViewportCount(){return this._viewportCount}getFrustum(){return this._frustum}updateMatrices(t){const e=this.camera,n=this.matrix;Xo.setFromMatrixPosition(t.matrixWorld),e.position.copy(Xo),qo.setFromMatrixPosition(t.target.matrixWorld),e.lookAt(qo),e.updateMatrixWorld(),Dr.multiplyMatrices(e.projectionMatrix,e.matrixWorldInverse),this._frustum.setFromProjectionMatrix(Dr,e.coordinateSystem,e.reversedDepth),e.reversedDepth?n.set(.5,0,0,.5,0,.5,0,.5,0,0,1,0,0,0,0,1):n.set(.5,0,0,.5,0,.5,0,.5,0,0,.5,.5,0,0,0,1),n.multiply(Dr)}getViewport(t){return this._viewports[t]}getFrameExtents(){return this._frameExtents}dispose(){this.map&&this.map.dispose(),this.mapPass&&this.mapPass.dispose()}copy(t){return this.camera=t.camera.clone(),this.intensity=t.intensity,this.bias=t.bias,this.radius=t.radius,this.autoUpdate=t.autoUpdate,this.needsUpdate=t.needsUpdate,this.normalBias=t.normalBias,this.blurSamples=t.blurSamples,this.mapSize.copy(t.mapSize),this}clone(){return new this.constructor().copy(this)}toJSON(){const t={};return this.intensity!==1&&(t.intensity=this.intensity),this.bias!==0&&(t.bias=this.bias),this.normalBias!==0&&(t.normalBias=this.normalBias),this.radius!==1&&(t.radius=this.radius),(this.mapSize.x!==512||this.mapSize.y!==512)&&(t.mapSize=this.mapSize.toArray()),t.camera=this.camera.toJSON(!1).object,delete t.camera.matrix,t}}const Yo=new re,Wi=new D,Lr=new D;class bu extends el{constructor(){super(new ke(90,1,.5,500)),this.isPointLightShadow=!0,this._frameExtents=new at(4,2),this._viewportCount=6,this._viewports=[new se(2,1,1,1),new se(0,1,1,1),new se(3,1,1,1),new se(1,1,1,1),new se(3,0,1,1),new se(1,0,1,1)],this._cubeDirections=[new D(1,0,0),new D(-1,0,0),new D(0,0,1),new D(0,0,-1),new D(0,1,0),new D(0,-1,0)],this._cubeUps=[new D(0,1,0),new D(0,1,0),new D(0,1,0),new D(0,1,0),new D(0,0,1),new D(0,0,-1)]}updateMatrices(t,e=0){const n=this.camera,s=this.matrix,r=t.distance||n.far;r!==n.far&&(n.far=r,n.updateProjectionMatrix()),Wi.setFromMatrixPosition(t.matrixWorld),n.position.copy(Wi),Lr.copy(n.position),Lr.add(this._cubeDirections[e]),n.up.copy(this._cubeUps[e]),n.lookAt(Lr),n.updateMatrixWorld(),s.makeTranslation(-Wi.x,-Wi.y,-Wi.z),Yo.multiplyMatrices(n.projectionMatrix,n.matrixWorldInverse),this._frustum.setFromProjectionMatrix(Yo,n.coordinateSystem,n.reversedDepth)}}class Eu extends $a{constructor(t,e,n=0,s=2){super(t,e),this.isPointLight=!0,this.type="PointLight",this.distance=n,this.decay=s,this.shadow=new bu}get power(){return this.intensity*4*Math.PI}set power(t){this.intensity=t/(4*Math.PI)}dispose(){this.shadow.dispose()}copy(t,e){return super.copy(t,e),this.distance=t.distance,this.decay=t.decay,this.shadow=t.shadow.clone(),this}}class nl extends Vc{constructor(t=-1,e=1,n=1,s=-1,r=.1,a=2e3){super(),this.isOrthographicCamera=!0,this.type="OrthographicCamera",this.zoom=1,this.view=null,this.left=t,this.right=e,this.top=n,this.bottom=s,this.near=r,this.far=a,this.updateProjectionMatrix()}copy(t,e){return super.copy(t,e),this.left=t.left,this.right=t.right,this.top=t.top,this.bottom=t.bottom,this.near=t.near,this.far=t.far,this.zoom=t.zoom,this.view=t.view===null?null:Object.assign({},t.view),this}setViewOffset(t,e,n,s,r,a){this.view===null&&(this.view={enabled:!0,fullWidth:1,fullHeight:1,offsetX:0,offsetY:0,width:1,height:1}),this.view.enabled=!0,this.view.fullWidth=t,this.view.fullHeight=e,this.view.offsetX=n,this.view.offsetY=s,this.view.width=r,this.view.height=a,this.updateProjectionMatrix()}clearViewOffset(){this.view!==null&&(this.view.enabled=!1),this.updateProjectionMatrix()}updateProjectionMatrix(){const t=(this.right-this.left)/(2*this.zoom),e=(this.top-this.bottom)/(2*this.zoom),n=(this.right+this.left)/2,s=(this.top+this.bottom)/2;let r=n-t,a=n+t,o=s+e,l=s-e;if(this.view!==null&&this.view.enabled){const c=(this.right-this.left)/this.view.fullWidth/this.zoom,h=(this.top-this.bottom)/this.view.fullHeight/this.zoom;r+=c*this.view.offsetX,a=r+c*this.view.width,o-=h*this.view.offsetY,l=o-h*this.view.height}this.projectionMatrix.makeOrthographic(r,a,o,l,this.near,this.far,this.coordinateSystem,this.reversedDepth),this.projectionMatrixInverse.copy(this.projectionMatrix).invert()}toJSON(t){const e=super.toJSON(t);return e.object.zoom=this.zoom,e.object.left=this.left,e.object.right=this.right,e.object.top=this.top,e.object.bottom=this.bottom,e.object.near=this.near,e.object.far=this.far,this.view!==null&&(e.object.view=Object.assign({},this.view)),e}}class Tu extends el{constructor(){super(new nl(-5,5,5,-5,.5,500)),this.isDirectionalLightShadow=!0}}class Au extends $a{constructor(t,e){super(t,e),this.isDirectionalLight=!0,this.type="DirectionalLight",this.position.copy(Me.DEFAULT_UP),this.updateMatrix(),this.target=new Me,this.shadow=new Tu}dispose(){this.shadow.dispose()}copy(t){return super.copy(t),this.target=t.target.clone(),this.shadow=t.shadow.clone(),this}}class wu extends ke{constructor(t=[]){super(),this.isArrayCamera=!0,this.isMultiViewCamera=!1,this.cameras=t}}const Zo=new re;class Ru{constructor(t,e,n=0,s=1/0){this.ray=new $s(t,e),this.near=n,this.far=s,this.camera=null,this.layers=new Ya,this.params={Mesh:{},Line:{threshold:1},LOD:{},Points:{threshold:1},Sprite:{}}}set(t,e){this.ray.set(t,e)}setFromCamera(t,e){e.isPerspectiveCamera?(this.ray.origin.setFromMatrixPosition(e.matrixWorld),this.ray.direction.set(t.x,t.y,.5).unproject(e).sub(this.ray.origin).normalize(),this.camera=e):e.isOrthographicCamera?(this.ray.origin.set(t.x,t.y,(e.near+e.far)/(e.near-e.far)).unproject(e),this.ray.direction.set(0,0,-1).transformDirection(e.matrixWorld),this.camera=e):de("Raycaster: Unsupported camera type: "+e.type)}setFromXRController(t){return Zo.identity().extractRotation(t.matrixWorld),this.ray.origin.setFromMatrixPosition(t.matrixWorld),this.ray.direction.set(0,0,-1).applyMatrix4(Zo),this}intersectObject(t,e=!0,n=[]){return La(t,this,n,e),n.sort(Ko),n}intersectObjects(t,e=!0,n=[]){for(let s=0,r=t.length;sp.start-x.start);let d=0;for(let p=1;p 0 - vec4 plane; - #ifdef ALPHA_TO_COVERAGE - float distanceToPlane, distanceGradient; - float clipOpacity = 1.0; - #pragma unroll_loop_start - for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; - distanceGradient = fwidth( distanceToPlane ) / 2.0; - clipOpacity *= smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); - if ( clipOpacity == 0.0 ) discard; - } - #pragma unroll_loop_end - #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES - float unionClipOpacity = 1.0; - #pragma unroll_loop_start - for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - distanceToPlane = - dot( vClipPosition, plane.xyz ) + plane.w; - distanceGradient = fwidth( distanceToPlane ) / 2.0; - unionClipOpacity *= 1.0 - smoothstep( - distanceGradient, distanceGradient, distanceToPlane ); - } - #pragma unroll_loop_end - clipOpacity *= 1.0 - unionClipOpacity; - #endif - diffuseColor.a *= clipOpacity; - if ( diffuseColor.a == 0.0 ) discard; - #else - #pragma unroll_loop_start - for ( int i = 0; i < UNION_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - if ( dot( vClipPosition, plane.xyz ) > plane.w ) discard; - } - #pragma unroll_loop_end - #if UNION_CLIPPING_PLANES < NUM_CLIPPING_PLANES - bool clipped = true; - #pragma unroll_loop_start - for ( int i = UNION_CLIPPING_PLANES; i < NUM_CLIPPING_PLANES; i ++ ) { - plane = clippingPlanes[ i ]; - clipped = ( dot( vClipPosition, plane.xyz ) > plane.w ) && clipped; - } - #pragma unroll_loop_end - if ( clipped ) discard; - #endif - #endif -#endif`,Zu=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; - uniform vec4 clippingPlanes[ NUM_CLIPPING_PLANES ]; -#endif`,Ku=`#if NUM_CLIPPING_PLANES > 0 - varying vec3 vClipPosition; -#endif`,ju=`#if NUM_CLIPPING_PLANES > 0 - vClipPosition = - mvPosition.xyz; -#endif`,Ju=`#if defined( USE_COLOR_ALPHA ) - diffuseColor *= vColor; -#elif defined( USE_COLOR ) - diffuseColor.rgb *= vColor; -#endif`,$u=`#if defined( USE_COLOR_ALPHA ) - varying vec4 vColor; -#elif defined( USE_COLOR ) - varying vec3 vColor; -#endif`,Qu=`#if defined( USE_COLOR_ALPHA ) - varying vec4 vColor; -#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) - varying vec3 vColor; -#endif`,tf=`#if defined( USE_COLOR_ALPHA ) - vColor = vec4( 1.0 ); -#elif defined( USE_COLOR ) || defined( USE_INSTANCING_COLOR ) || defined( USE_BATCHING_COLOR ) - vColor = vec3( 1.0 ); -#endif -#ifdef USE_COLOR - vColor *= color; -#endif -#ifdef USE_INSTANCING_COLOR - vColor.xyz *= instanceColor.xyz; -#endif -#ifdef USE_BATCHING_COLOR - vec3 batchingColor = getBatchingColor( getIndirectIndex( gl_DrawID ) ); - vColor.xyz *= batchingColor.xyz; -#endif`,ef=`#define PI 3.141592653589793 -#define PI2 6.283185307179586 -#define PI_HALF 1.5707963267948966 -#define RECIPROCAL_PI 0.3183098861837907 -#define RECIPROCAL_PI2 0.15915494309189535 -#define EPSILON 1e-6 -#ifndef saturate -#define saturate( a ) clamp( a, 0.0, 1.0 ) -#endif -#define whiteComplement( a ) ( 1.0 - saturate( a ) ) -float pow2( const in float x ) { return x*x; } -vec3 pow2( const in vec3 x ) { return x*x; } -float pow3( const in float x ) { return x*x*x; } -float pow4( const in float x ) { float x2 = x*x; return x2*x2; } -float max3( const in vec3 v ) { return max( max( v.x, v.y ), v.z ); } -float average( const in vec3 v ) { return dot( v, vec3( 0.3333333 ) ); } -highp float rand( const in vec2 uv ) { - const highp float a = 12.9898, b = 78.233, c = 43758.5453; - highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, PI ); - return fract( sin( sn ) * c ); -} -#ifdef HIGH_PRECISION - float precisionSafeLength( vec3 v ) { return length( v ); } -#else - float precisionSafeLength( vec3 v ) { - float maxComponent = max3( abs( v ) ); - return length( v / maxComponent ) * maxComponent; - } -#endif -struct IncidentLight { - vec3 color; - vec3 direction; - bool visible; -}; -struct ReflectedLight { - vec3 directDiffuse; - vec3 directSpecular; - vec3 indirectDiffuse; - vec3 indirectSpecular; -}; -#ifdef USE_ALPHAHASH - varying vec3 vPosition; -#endif -vec3 transformDirection( in vec3 dir, in mat4 matrix ) { - return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); -} -vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) { - return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz ); -} -bool isPerspectiveMatrix( mat4 m ) { - return m[ 2 ][ 3 ] == - 1.0; -} -vec2 equirectUv( in vec3 dir ) { - float u = atan( dir.z, dir.x ) * RECIPROCAL_PI2 + 0.5; - float v = asin( clamp( dir.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; - return vec2( u, v ); -} -vec3 BRDF_Lambert( const in vec3 diffuseColor ) { - return RECIPROCAL_PI * diffuseColor; -} -vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH ) { - float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); - return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); -} -float F_Schlick( const in float f0, const in float f90, const in float dotVH ) { - float fresnel = exp2( ( - 5.55473 * dotVH - 6.98316 ) * dotVH ); - return f0 * ( 1.0 - fresnel ) + ( f90 * fresnel ); -} // validated`,nf=`#ifdef ENVMAP_TYPE_CUBE_UV - #define cubeUV_minMipLevel 4.0 - #define cubeUV_minTileSize 16.0 - float getFace( vec3 direction ) { - vec3 absDirection = abs( direction ); - float face = - 1.0; - if ( absDirection.x > absDirection.z ) { - if ( absDirection.x > absDirection.y ) - face = direction.x > 0.0 ? 0.0 : 3.0; - else - face = direction.y > 0.0 ? 1.0 : 4.0; - } else { - if ( absDirection.z > absDirection.y ) - face = direction.z > 0.0 ? 2.0 : 5.0; - else - face = direction.y > 0.0 ? 1.0 : 4.0; - } - return face; - } - vec2 getUV( vec3 direction, float face ) { - vec2 uv; - if ( face == 0.0 ) { - uv = vec2( direction.z, direction.y ) / abs( direction.x ); - } else if ( face == 1.0 ) { - uv = vec2( - direction.x, - direction.z ) / abs( direction.y ); - } else if ( face == 2.0 ) { - uv = vec2( - direction.x, direction.y ) / abs( direction.z ); - } else if ( face == 3.0 ) { - uv = vec2( - direction.z, direction.y ) / abs( direction.x ); - } else if ( face == 4.0 ) { - uv = vec2( - direction.x, direction.z ) / abs( direction.y ); - } else { - uv = vec2( direction.x, direction.y ) / abs( direction.z ); - } - return 0.5 * ( uv + 1.0 ); - } - vec3 bilinearCubeUV( sampler2D envMap, vec3 direction, float mipInt ) { - float face = getFace( direction ); - float filterInt = max( cubeUV_minMipLevel - mipInt, 0.0 ); - mipInt = max( mipInt, cubeUV_minMipLevel ); - float faceSize = exp2( mipInt ); - highp vec2 uv = getUV( direction, face ) * ( faceSize - 2.0 ) + 1.0; - if ( face > 2.0 ) { - uv.y += faceSize; - face -= 3.0; - } - uv.x += face * faceSize; - uv.x += filterInt * 3.0 * cubeUV_minTileSize; - uv.y += 4.0 * ( exp2( CUBEUV_MAX_MIP ) - faceSize ); - uv.x *= CUBEUV_TEXEL_WIDTH; - uv.y *= CUBEUV_TEXEL_HEIGHT; - #ifdef texture2DGradEXT - return texture2DGradEXT( envMap, uv, vec2( 0.0 ), vec2( 0.0 ) ).rgb; - #else - return texture2D( envMap, uv ).rgb; - #endif - } - #define cubeUV_r0 1.0 - #define cubeUV_m0 - 2.0 - #define cubeUV_r1 0.8 - #define cubeUV_m1 - 1.0 - #define cubeUV_r4 0.4 - #define cubeUV_m4 2.0 - #define cubeUV_r5 0.305 - #define cubeUV_m5 3.0 - #define cubeUV_r6 0.21 - #define cubeUV_m6 4.0 - float roughnessToMip( float roughness ) { - float mip = 0.0; - if ( roughness >= cubeUV_r1 ) { - mip = ( cubeUV_r0 - roughness ) * ( cubeUV_m1 - cubeUV_m0 ) / ( cubeUV_r0 - cubeUV_r1 ) + cubeUV_m0; - } else if ( roughness >= cubeUV_r4 ) { - mip = ( cubeUV_r1 - roughness ) * ( cubeUV_m4 - cubeUV_m1 ) / ( cubeUV_r1 - cubeUV_r4 ) + cubeUV_m1; - } else if ( roughness >= cubeUV_r5 ) { - mip = ( cubeUV_r4 - roughness ) * ( cubeUV_m5 - cubeUV_m4 ) / ( cubeUV_r4 - cubeUV_r5 ) + cubeUV_m4; - } else if ( roughness >= cubeUV_r6 ) { - mip = ( cubeUV_r5 - roughness ) * ( cubeUV_m6 - cubeUV_m5 ) / ( cubeUV_r5 - cubeUV_r6 ) + cubeUV_m5; - } else { - mip = - 2.0 * log2( 1.16 * roughness ); } - return mip; - } - vec4 textureCubeUV( sampler2D envMap, vec3 sampleDir, float roughness ) { - float mip = clamp( roughnessToMip( roughness ), cubeUV_m0, CUBEUV_MAX_MIP ); - float mipF = fract( mip ); - float mipInt = floor( mip ); - vec3 color0 = bilinearCubeUV( envMap, sampleDir, mipInt ); - if ( mipF == 0.0 ) { - return vec4( color0, 1.0 ); - } else { - vec3 color1 = bilinearCubeUV( envMap, sampleDir, mipInt + 1.0 ); - return vec4( mix( color0, color1, mipF ), 1.0 ); - } - } -#endif`,sf=`vec3 transformedNormal = objectNormal; -#ifdef USE_TANGENT - vec3 transformedTangent = objectTangent; -#endif -#ifdef USE_BATCHING - mat3 bm = mat3( batchingMatrix ); - transformedNormal /= vec3( dot( bm[ 0 ], bm[ 0 ] ), dot( bm[ 1 ], bm[ 1 ] ), dot( bm[ 2 ], bm[ 2 ] ) ); - transformedNormal = bm * transformedNormal; - #ifdef USE_TANGENT - transformedTangent = bm * transformedTangent; - #endif -#endif -#ifdef USE_INSTANCING - mat3 im = mat3( instanceMatrix ); - transformedNormal /= vec3( dot( im[ 0 ], im[ 0 ] ), dot( im[ 1 ], im[ 1 ] ), dot( im[ 2 ], im[ 2 ] ) ); - transformedNormal = im * transformedNormal; - #ifdef USE_TANGENT - transformedTangent = im * transformedTangent; - #endif -#endif -transformedNormal = normalMatrix * transformedNormal; -#ifdef FLIP_SIDED - transformedNormal = - transformedNormal; -#endif -#ifdef USE_TANGENT - transformedTangent = ( modelViewMatrix * vec4( transformedTangent, 0.0 ) ).xyz; - #ifdef FLIP_SIDED - transformedTangent = - transformedTangent; - #endif -#endif`,rf=`#ifdef USE_DISPLACEMENTMAP - uniform sampler2D displacementMap; - uniform float displacementScale; - uniform float displacementBias; -#endif`,af=`#ifdef USE_DISPLACEMENTMAP - transformed += normalize( objectNormal ) * ( texture2D( displacementMap, vDisplacementMapUv ).x * displacementScale + displacementBias ); -#endif`,of=`#ifdef USE_EMISSIVEMAP - vec4 emissiveColor = texture2D( emissiveMap, vEmissiveMapUv ); - #ifdef DECODE_VIDEO_TEXTURE_EMISSIVE - emissiveColor = sRGBTransferEOTF( emissiveColor ); - #endif - totalEmissiveRadiance *= emissiveColor.rgb; -#endif`,cf=`#ifdef USE_EMISSIVEMAP - uniform sampler2D emissiveMap; -#endif`,lf="gl_FragColor = linearToOutputTexel( gl_FragColor );",hf=`vec4 LinearTransferOETF( in vec4 value ) { - return value; -} -vec4 sRGBTransferEOTF( in vec4 value ) { - return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a ); -} -vec4 sRGBTransferOETF( in vec4 value ) { - return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a ); -}`,uf=`#ifdef USE_ENVMAP - #ifdef ENV_WORLDPOS - vec3 cameraToFrag; - if ( isOrthographic ) { - cameraToFrag = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); - } else { - cameraToFrag = normalize( vWorldPosition - cameraPosition ); - } - vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); - #ifdef ENVMAP_MODE_REFLECTION - vec3 reflectVec = reflect( cameraToFrag, worldNormal ); - #else - vec3 reflectVec = refract( cameraToFrag, worldNormal, refractionRatio ); - #endif - #else - vec3 reflectVec = vReflect; - #endif - #ifdef ENVMAP_TYPE_CUBE - vec4 envColor = textureCube( envMap, envMapRotation * vec3( flipEnvMap * reflectVec.x, reflectVec.yz ) ); - #else - vec4 envColor = vec4( 0.0 ); - #endif - #ifdef ENVMAP_BLENDING_MULTIPLY - outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity ); - #elif defined( ENVMAP_BLENDING_MIX ) - outgoingLight = mix( outgoingLight, envColor.xyz, specularStrength * reflectivity ); - #elif defined( ENVMAP_BLENDING_ADD ) - outgoingLight += envColor.xyz * specularStrength * reflectivity; - #endif -#endif`,ff=`#ifdef USE_ENVMAP - uniform float envMapIntensity; - uniform float flipEnvMap; - uniform mat3 envMapRotation; - #ifdef ENVMAP_TYPE_CUBE - uniform samplerCube envMap; - #else - uniform sampler2D envMap; - #endif -#endif`,df=`#ifdef USE_ENVMAP - uniform float reflectivity; - #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) - #define ENV_WORLDPOS - #endif - #ifdef ENV_WORLDPOS - varying vec3 vWorldPosition; - uniform float refractionRatio; - #else - varying vec3 vReflect; - #endif -#endif`,pf=`#ifdef USE_ENVMAP - #if defined( USE_BUMPMAP ) || defined( USE_NORMALMAP ) || defined( PHONG ) || defined( LAMBERT ) - #define ENV_WORLDPOS - #endif - #ifdef ENV_WORLDPOS - - varying vec3 vWorldPosition; - #else - varying vec3 vReflect; - uniform float refractionRatio; - #endif -#endif`,mf=`#ifdef USE_ENVMAP - #ifdef ENV_WORLDPOS - vWorldPosition = worldPosition.xyz; - #else - vec3 cameraToVertex; - if ( isOrthographic ) { - cameraToVertex = normalize( vec3( - viewMatrix[ 0 ][ 2 ], - viewMatrix[ 1 ][ 2 ], - viewMatrix[ 2 ][ 2 ] ) ); - } else { - cameraToVertex = normalize( worldPosition.xyz - cameraPosition ); - } - vec3 worldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); - #ifdef ENVMAP_MODE_REFLECTION - vReflect = reflect( cameraToVertex, worldNormal ); - #else - vReflect = refract( cameraToVertex, worldNormal, refractionRatio ); - #endif - #endif -#endif`,xf=`#ifdef USE_FOG - vFogDepth = - mvPosition.z; -#endif`,gf=`#ifdef USE_FOG - varying float vFogDepth; -#endif`,_f=`#ifdef USE_FOG - #ifdef FOG_EXP2 - float fogFactor = 1.0 - exp( - fogDensity * fogDensity * vFogDepth * vFogDepth ); - #else - float fogFactor = smoothstep( fogNear, fogFar, vFogDepth ); - #endif - gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor, fogFactor ); -#endif`,vf=`#ifdef USE_FOG - uniform vec3 fogColor; - varying float vFogDepth; - #ifdef FOG_EXP2 - uniform float fogDensity; - #else - uniform float fogNear; - uniform float fogFar; - #endif -#endif`,Mf=`#ifdef USE_GRADIENTMAP - uniform sampler2D gradientMap; -#endif -vec3 getGradientIrradiance( vec3 normal, vec3 lightDirection ) { - float dotNL = dot( normal, lightDirection ); - vec2 coord = vec2( dotNL * 0.5 + 0.5, 0.0 ); - #ifdef USE_GRADIENTMAP - return vec3( texture2D( gradientMap, coord ).r ); - #else - vec2 fw = fwidth( coord ) * 0.5; - return mix( vec3( 0.7 ), vec3( 1.0 ), smoothstep( 0.7 - fw.x, 0.7 + fw.x, coord.x ) ); - #endif -}`,Sf=`#ifdef USE_LIGHTMAP - uniform sampler2D lightMap; - uniform float lightMapIntensity; -#endif`,yf=`LambertMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularStrength = specularStrength;`,bf=`varying vec3 vViewPosition; -struct LambertMaterial { - vec3 diffuseColor; - float specularStrength; -}; -void RE_Direct_Lambert( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectDiffuse_Lambert( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in LambertMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_Lambert -#define RE_IndirectDiffuse RE_IndirectDiffuse_Lambert`,Ef=`uniform bool receiveShadow; -uniform vec3 ambientLightColor; -#if defined( USE_LIGHT_PROBES ) - uniform vec3 lightProbe[ 9 ]; -#endif -vec3 shGetIrradianceAt( in vec3 normal, in vec3 shCoefficients[ 9 ] ) { - float x = normal.x, y = normal.y, z = normal.z; - vec3 result = shCoefficients[ 0 ] * 0.886227; - result += shCoefficients[ 1 ] * 2.0 * 0.511664 * y; - result += shCoefficients[ 2 ] * 2.0 * 0.511664 * z; - result += shCoefficients[ 3 ] * 2.0 * 0.511664 * x; - result += shCoefficients[ 4 ] * 2.0 * 0.429043 * x * y; - result += shCoefficients[ 5 ] * 2.0 * 0.429043 * y * z; - result += shCoefficients[ 6 ] * ( 0.743125 * z * z - 0.247708 ); - result += shCoefficients[ 7 ] * 2.0 * 0.429043 * x * z; - result += shCoefficients[ 8 ] * 0.429043 * ( x * x - y * y ); - return result; -} -vec3 getLightProbeIrradiance( const in vec3 lightProbe[ 9 ], const in vec3 normal ) { - vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); - vec3 irradiance = shGetIrradianceAt( worldNormal, lightProbe ); - return irradiance; -} -vec3 getAmbientLightIrradiance( const in vec3 ambientLightColor ) { - vec3 irradiance = ambientLightColor; - return irradiance; -} -float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) { - float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 ); - if ( cutoffDistance > 0.0 ) { - distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) ); - } - return distanceFalloff; -} -float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) { - return smoothstep( coneCosine, penumbraCosine, angleCosine ); -} -#if NUM_DIR_LIGHTS > 0 - struct DirectionalLight { - vec3 direction; - vec3 color; - }; - uniform DirectionalLight directionalLights[ NUM_DIR_LIGHTS ]; - void getDirectionalLightInfo( const in DirectionalLight directionalLight, out IncidentLight light ) { - light.color = directionalLight.color; - light.direction = directionalLight.direction; - light.visible = true; - } -#endif -#if NUM_POINT_LIGHTS > 0 - struct PointLight { - vec3 position; - vec3 color; - float distance; - float decay; - }; - uniform PointLight pointLights[ NUM_POINT_LIGHTS ]; - void getPointLightInfo( const in PointLight pointLight, const in vec3 geometryPosition, out IncidentLight light ) { - vec3 lVector = pointLight.position - geometryPosition; - light.direction = normalize( lVector ); - float lightDistance = length( lVector ); - light.color = pointLight.color; - light.color *= getDistanceAttenuation( lightDistance, pointLight.distance, pointLight.decay ); - light.visible = ( light.color != vec3( 0.0 ) ); - } -#endif -#if NUM_SPOT_LIGHTS > 0 - struct SpotLight { - vec3 position; - vec3 direction; - vec3 color; - float distance; - float decay; - float coneCos; - float penumbraCos; - }; - uniform SpotLight spotLights[ NUM_SPOT_LIGHTS ]; - void getSpotLightInfo( const in SpotLight spotLight, const in vec3 geometryPosition, out IncidentLight light ) { - vec3 lVector = spotLight.position - geometryPosition; - light.direction = normalize( lVector ); - float angleCos = dot( light.direction, spotLight.direction ); - float spotAttenuation = getSpotAttenuation( spotLight.coneCos, spotLight.penumbraCos, angleCos ); - if ( spotAttenuation > 0.0 ) { - float lightDistance = length( lVector ); - light.color = spotLight.color * spotAttenuation; - light.color *= getDistanceAttenuation( lightDistance, spotLight.distance, spotLight.decay ); - light.visible = ( light.color != vec3( 0.0 ) ); - } else { - light.color = vec3( 0.0 ); - light.visible = false; - } - } -#endif -#if NUM_RECT_AREA_LIGHTS > 0 - struct RectAreaLight { - vec3 color; - vec3 position; - vec3 halfWidth; - vec3 halfHeight; - }; - uniform sampler2D ltc_1; uniform sampler2D ltc_2; - uniform RectAreaLight rectAreaLights[ NUM_RECT_AREA_LIGHTS ]; -#endif -#if NUM_HEMI_LIGHTS > 0 - struct HemisphereLight { - vec3 direction; - vec3 skyColor; - vec3 groundColor; - }; - uniform HemisphereLight hemisphereLights[ NUM_HEMI_LIGHTS ]; - vec3 getHemisphereLightIrradiance( const in HemisphereLight hemiLight, const in vec3 normal ) { - float dotNL = dot( normal, hemiLight.direction ); - float hemiDiffuseWeight = 0.5 * dotNL + 0.5; - vec3 irradiance = mix( hemiLight.groundColor, hemiLight.skyColor, hemiDiffuseWeight ); - return irradiance; - } -#endif`,Tf=`#ifdef USE_ENVMAP - vec3 getIBLIrradiance( const in vec3 normal ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 worldNormal = inverseTransformDirection( normal, viewMatrix ); - vec4 envMapColor = textureCubeUV( envMap, envMapRotation * worldNormal, 1.0 ); - return PI * envMapColor.rgb * envMapIntensity; - #else - return vec3( 0.0 ); - #endif - } - vec3 getIBLRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 reflectVec = reflect( - viewDir, normal ); - reflectVec = normalize( mix( reflectVec, normal, pow4( roughness ) ) ); - reflectVec = inverseTransformDirection( reflectVec, viewMatrix ); - vec4 envMapColor = textureCubeUV( envMap, envMapRotation * reflectVec, roughness ); - return envMapColor.rgb * envMapIntensity; - #else - return vec3( 0.0 ); - #endif - } - #ifdef USE_ANISOTROPY - vec3 getIBLAnisotropyRadiance( const in vec3 viewDir, const in vec3 normal, const in float roughness, const in vec3 bitangent, const in float anisotropy ) { - #ifdef ENVMAP_TYPE_CUBE_UV - vec3 bentNormal = cross( bitangent, viewDir ); - bentNormal = normalize( cross( bentNormal, bitangent ) ); - bentNormal = normalize( mix( bentNormal, normal, pow2( pow2( 1.0 - anisotropy * ( 1.0 - roughness ) ) ) ) ); - return getIBLRadiance( viewDir, bentNormal, roughness ); - #else - return vec3( 0.0 ); - #endif - } - #endif -#endif`,Af=`ToonMaterial material; -material.diffuseColor = diffuseColor.rgb;`,wf=`varying vec3 vViewPosition; -struct ToonMaterial { - vec3 diffuseColor; -}; -void RE_Direct_Toon( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { - vec3 irradiance = getGradientIrradiance( geometryNormal, directLight.direction ) * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectDiffuse_Toon( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in ToonMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_Toon -#define RE_IndirectDiffuse RE_IndirectDiffuse_Toon`,Rf=`BlinnPhongMaterial material; -material.diffuseColor = diffuseColor.rgb; -material.specularColor = specular; -material.specularShininess = shininess; -material.specularStrength = specularStrength;`,Cf=`varying vec3 vViewPosition; -struct BlinnPhongMaterial { - vec3 diffuseColor; - vec3 specularColor; - float specularShininess; - float specularStrength; -}; -void RE_Direct_BlinnPhong( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); - reflectedLight.directSpecular += irradiance * BRDF_BlinnPhong( directLight.direction, geometryViewDir, geometryNormal, material.specularColor, material.specularShininess ) * material.specularStrength; -} -void RE_IndirectDiffuse_BlinnPhong( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in BlinnPhongMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -#define RE_Direct RE_Direct_BlinnPhong -#define RE_IndirectDiffuse RE_IndirectDiffuse_BlinnPhong`,Pf=`PhysicalMaterial material; -material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor ); -vec3 dxy = max( abs( dFdx( nonPerturbedNormal ) ), abs( dFdy( nonPerturbedNormal ) ) ); -float geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z ); -material.roughness = max( roughnessFactor, 0.0525 );material.roughness += geometryRoughness; -material.roughness = min( material.roughness, 1.0 ); -#ifdef IOR - material.ior = ior; - #ifdef USE_SPECULAR - float specularIntensityFactor = specularIntensity; - vec3 specularColorFactor = specularColor; - #ifdef USE_SPECULAR_COLORMAP - specularColorFactor *= texture2D( specularColorMap, vSpecularColorMapUv ).rgb; - #endif - #ifdef USE_SPECULAR_INTENSITYMAP - specularIntensityFactor *= texture2D( specularIntensityMap, vSpecularIntensityMapUv ).a; - #endif - material.specularF90 = mix( specularIntensityFactor, 1.0, metalnessFactor ); - #else - float specularIntensityFactor = 1.0; - vec3 specularColorFactor = vec3( 1.0 ); - material.specularF90 = 1.0; - #endif - material.specularColor = mix( min( pow2( ( material.ior - 1.0 ) / ( material.ior + 1.0 ) ) * specularColorFactor, vec3( 1.0 ) ) * specularIntensityFactor, diffuseColor.rgb, metalnessFactor ); -#else - material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor ); - material.specularF90 = 1.0; -#endif -#ifdef USE_CLEARCOAT - material.clearcoat = clearcoat; - material.clearcoatRoughness = clearcoatRoughness; - material.clearcoatF0 = vec3( 0.04 ); - material.clearcoatF90 = 1.0; - #ifdef USE_CLEARCOATMAP - material.clearcoat *= texture2D( clearcoatMap, vClearcoatMapUv ).x; - #endif - #ifdef USE_CLEARCOAT_ROUGHNESSMAP - material.clearcoatRoughness *= texture2D( clearcoatRoughnessMap, vClearcoatRoughnessMapUv ).y; - #endif - material.clearcoat = saturate( material.clearcoat ); material.clearcoatRoughness = max( material.clearcoatRoughness, 0.0525 ); - material.clearcoatRoughness += geometryRoughness; - material.clearcoatRoughness = min( material.clearcoatRoughness, 1.0 ); -#endif -#ifdef USE_DISPERSION - material.dispersion = dispersion; -#endif -#ifdef USE_IRIDESCENCE - material.iridescence = iridescence; - material.iridescenceIOR = iridescenceIOR; - #ifdef USE_IRIDESCENCEMAP - material.iridescence *= texture2D( iridescenceMap, vIridescenceMapUv ).r; - #endif - #ifdef USE_IRIDESCENCE_THICKNESSMAP - material.iridescenceThickness = (iridescenceThicknessMaximum - iridescenceThicknessMinimum) * texture2D( iridescenceThicknessMap, vIridescenceThicknessMapUv ).g + iridescenceThicknessMinimum; - #else - material.iridescenceThickness = iridescenceThicknessMaximum; - #endif -#endif -#ifdef USE_SHEEN - material.sheenColor = sheenColor; - #ifdef USE_SHEEN_COLORMAP - material.sheenColor *= texture2D( sheenColorMap, vSheenColorMapUv ).rgb; - #endif - material.sheenRoughness = clamp( sheenRoughness, 0.07, 1.0 ); - #ifdef USE_SHEEN_ROUGHNESSMAP - material.sheenRoughness *= texture2D( sheenRoughnessMap, vSheenRoughnessMapUv ).a; - #endif -#endif -#ifdef USE_ANISOTROPY - #ifdef USE_ANISOTROPYMAP - mat2 anisotropyMat = mat2( anisotropyVector.x, anisotropyVector.y, - anisotropyVector.y, anisotropyVector.x ); - vec3 anisotropyPolar = texture2D( anisotropyMap, vAnisotropyMapUv ).rgb; - vec2 anisotropyV = anisotropyMat * normalize( 2.0 * anisotropyPolar.rg - vec2( 1.0 ) ) * anisotropyPolar.b; - #else - vec2 anisotropyV = anisotropyVector; - #endif - material.anisotropy = length( anisotropyV ); - if( material.anisotropy == 0.0 ) { - anisotropyV = vec2( 1.0, 0.0 ); - } else { - anisotropyV /= material.anisotropy; - material.anisotropy = saturate( material.anisotropy ); - } - material.alphaT = mix( pow2( material.roughness ), 1.0, pow2( material.anisotropy ) ); - material.anisotropyT = tbn[ 0 ] * anisotropyV.x + tbn[ 1 ] * anisotropyV.y; - material.anisotropyB = tbn[ 1 ] * anisotropyV.x - tbn[ 0 ] * anisotropyV.y; -#endif`,Df=`uniform sampler2D dfgLUT; -struct PhysicalMaterial { - vec3 diffuseColor; - float roughness; - vec3 specularColor; - float specularF90; - float dispersion; - #ifdef USE_CLEARCOAT - float clearcoat; - float clearcoatRoughness; - vec3 clearcoatF0; - float clearcoatF90; - #endif - #ifdef USE_IRIDESCENCE - float iridescence; - float iridescenceIOR; - float iridescenceThickness; - vec3 iridescenceFresnel; - vec3 iridescenceF0; - #endif - #ifdef USE_SHEEN - vec3 sheenColor; - float sheenRoughness; - #endif - #ifdef IOR - float ior; - #endif - #ifdef USE_TRANSMISSION - float transmission; - float transmissionAlpha; - float thickness; - float attenuationDistance; - vec3 attenuationColor; - #endif - #ifdef USE_ANISOTROPY - float anisotropy; - float alphaT; - vec3 anisotropyT; - vec3 anisotropyB; - #endif -}; -vec3 clearcoatSpecularDirect = vec3( 0.0 ); -vec3 clearcoatSpecularIndirect = vec3( 0.0 ); -vec3 sheenSpecularDirect = vec3( 0.0 ); -vec3 sheenSpecularIndirect = vec3(0.0 ); -vec3 Schlick_to_F0( const in vec3 f, const in float f90, const in float dotVH ) { - float x = clamp( 1.0 - dotVH, 0.0, 1.0 ); - float x2 = x * x; - float x5 = clamp( x * x2 * x2, 0.0, 0.9999 ); - return ( f - vec3( f90 ) * x5 ) / ( 1.0 - x5 ); -} -float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) { - float a2 = pow2( alpha ); - float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) ); - float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) ); - return 0.5 / max( gv + gl, EPSILON ); -} -float D_GGX( const in float alpha, const in float dotNH ) { - float a2 = pow2( alpha ); - float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; - return RECIPROCAL_PI * a2 / pow2( denom ); -} -#ifdef USE_ANISOTROPY - float V_GGX_SmithCorrelated_Anisotropic( const in float alphaT, const in float alphaB, const in float dotTV, const in float dotBV, const in float dotTL, const in float dotBL, const in float dotNV, const in float dotNL ) { - float gv = dotNL * length( vec3( alphaT * dotTV, alphaB * dotBV, dotNV ) ); - float gl = dotNV * length( vec3( alphaT * dotTL, alphaB * dotBL, dotNL ) ); - float v = 0.5 / ( gv + gl ); - return saturate(v); - } - float D_GGX_Anisotropic( const in float alphaT, const in float alphaB, const in float dotNH, const in float dotTH, const in float dotBH ) { - float a2 = alphaT * alphaB; - highp vec3 v = vec3( alphaB * dotTH, alphaT * dotBH, a2 * dotNH ); - highp float v2 = dot( v, v ); - float w2 = a2 / v2; - return RECIPROCAL_PI * a2 * pow2 ( w2 ); - } -#endif -#ifdef USE_CLEARCOAT - vec3 BRDF_GGX_Clearcoat( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material) { - vec3 f0 = material.clearcoatF0; - float f90 = material.clearcoatF90; - float roughness = material.clearcoatRoughness; - float alpha = pow2( roughness ); - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float dotVH = saturate( dot( viewDir, halfDir ) ); - vec3 F = F_Schlick( f0, f90, dotVH ); - float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); - float D = D_GGX( alpha, dotNH ); - return F * ( V * D ); - } -#endif -vec3 BRDF_GGX( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { - vec3 f0 = material.specularColor; - float f90 = material.specularF90; - float roughness = material.roughness; - float alpha = pow2( roughness ); - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float dotVH = saturate( dot( viewDir, halfDir ) ); - vec3 F = F_Schlick( f0, f90, dotVH ); - #ifdef USE_IRIDESCENCE - F = mix( F, material.iridescenceFresnel, material.iridescence ); - #endif - #ifdef USE_ANISOTROPY - float dotTL = dot( material.anisotropyT, lightDir ); - float dotTV = dot( material.anisotropyT, viewDir ); - float dotTH = dot( material.anisotropyT, halfDir ); - float dotBL = dot( material.anisotropyB, lightDir ); - float dotBV = dot( material.anisotropyB, viewDir ); - float dotBH = dot( material.anisotropyB, halfDir ); - float V = V_GGX_SmithCorrelated_Anisotropic( material.alphaT, alpha, dotTV, dotBV, dotTL, dotBL, dotNV, dotNL ); - float D = D_GGX_Anisotropic( material.alphaT, alpha, dotNH, dotTH, dotBH ); - #else - float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV ); - float D = D_GGX( alpha, dotNH ); - #endif - return F * ( V * D ); -} -vec2 LTC_Uv( const in vec3 N, const in vec3 V, const in float roughness ) { - const float LUT_SIZE = 64.0; - const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE; - const float LUT_BIAS = 0.5 / LUT_SIZE; - float dotNV = saturate( dot( N, V ) ); - vec2 uv = vec2( roughness, sqrt( 1.0 - dotNV ) ); - uv = uv * LUT_SCALE + LUT_BIAS; - return uv; -} -float LTC_ClippedSphereFormFactor( const in vec3 f ) { - float l = length( f ); - return max( ( l * l + f.z ) / ( l + 1.0 ), 0.0 ); -} -vec3 LTC_EdgeVectorFormFactor( const in vec3 v1, const in vec3 v2 ) { - float x = dot( v1, v2 ); - float y = abs( x ); - float a = 0.8543985 + ( 0.4965155 + 0.0145206 * y ) * y; - float b = 3.4175940 + ( 4.1616724 + y ) * y; - float v = a / b; - float theta_sintheta = ( x > 0.0 ) ? v : 0.5 * inversesqrt( max( 1.0 - x * x, 1e-7 ) ) - v; - return cross( v1, v2 ) * theta_sintheta; -} -vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) { - vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ]; - vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ]; - vec3 lightNormal = cross( v1, v2 ); - if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 ); - vec3 T1, T2; - T1 = normalize( V - N * dot( V, N ) ); - T2 = - cross( N, T1 ); - mat3 mat = mInv * transpose( mat3( T1, T2, N ) ); - vec3 coords[ 4 ]; - coords[ 0 ] = mat * ( rectCoords[ 0 ] - P ); - coords[ 1 ] = mat * ( rectCoords[ 1 ] - P ); - coords[ 2 ] = mat * ( rectCoords[ 2 ] - P ); - coords[ 3 ] = mat * ( rectCoords[ 3 ] - P ); - coords[ 0 ] = normalize( coords[ 0 ] ); - coords[ 1 ] = normalize( coords[ 1 ] ); - coords[ 2 ] = normalize( coords[ 2 ] ); - coords[ 3 ] = normalize( coords[ 3 ] ); - vec3 vectorFormFactor = vec3( 0.0 ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] ); - vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] ); - float result = LTC_ClippedSphereFormFactor( vectorFormFactor ); - return vec3( result ); -} -#if defined( USE_SHEEN ) -float D_Charlie( float roughness, float dotNH ) { - float alpha = pow2( roughness ); - float invAlpha = 1.0 / alpha; - float cos2h = dotNH * dotNH; - float sin2h = max( 1.0 - cos2h, 0.0078125 ); - return ( 2.0 + invAlpha ) * pow( sin2h, invAlpha * 0.5 ) / ( 2.0 * PI ); -} -float V_Neubelt( float dotNV, float dotNL ) { - return saturate( 1.0 / ( 4.0 * ( dotNL + dotNV - dotNL * dotNV ) ) ); -} -vec3 BRDF_Sheen( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, vec3 sheenColor, const in float sheenRoughness ) { - vec3 halfDir = normalize( lightDir + viewDir ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - float dotNH = saturate( dot( normal, halfDir ) ); - float D = D_Charlie( sheenRoughness, dotNH ); - float V = V_Neubelt( dotNV, dotNL ); - return sheenColor * ( D * V ); -} -#endif -float IBLSheenBRDF( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { - float dotNV = saturate( dot( normal, viewDir ) ); - float r2 = roughness * roughness; - float a = roughness < 0.25 ? -339.2 * r2 + 161.4 * roughness - 25.9 : -8.48 * r2 + 14.3 * roughness - 9.95; - float b = roughness < 0.25 ? 44.0 * r2 - 23.7 * roughness + 3.26 : 1.97 * r2 - 3.27 * roughness + 0.72; - float DG = exp( a * dotNV + b ) + ( roughness < 0.25 ? 0.0 : 0.1 * ( roughness - 0.25 ) ); - return saturate( DG * RECIPROCAL_PI ); -} -vec2 DFGApprox( const in vec3 normal, const in vec3 viewDir, const in float roughness ) { - float dotNV = saturate( dot( normal, viewDir ) ); - vec2 uv = vec2( roughness, dotNV ); - return texture2D( dfgLUT, uv ).rg; -} -vec3 EnvironmentBRDF( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness ) { - vec2 fab = DFGApprox( normal, viewDir, roughness ); - return specularColor * fab.x + specularF90 * fab.y; -} -#ifdef USE_IRIDESCENCE -void computeMultiscatteringIridescence( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float iridescence, const in vec3 iridescenceF0, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { -#else -void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const in vec3 specularColor, const in float specularF90, const in float roughness, inout vec3 singleScatter, inout vec3 multiScatter ) { -#endif - vec2 fab = DFGApprox( normal, viewDir, roughness ); - #ifdef USE_IRIDESCENCE - vec3 Fr = mix( specularColor, iridescenceF0, iridescence ); - #else - vec3 Fr = specularColor; - #endif - vec3 FssEss = Fr * fab.x + specularF90 * fab.y; - float Ess = fab.x + fab.y; - float Ems = 1.0 - Ess; - vec3 Favg = Fr + ( 1.0 - Fr ) * 0.047619; vec3 Fms = FssEss * Favg / ( 1.0 - Ems * Favg ); - singleScatter += FssEss; - multiScatter += Fms * Ems; -} -vec3 BRDF_GGX_Multiscatter( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) { - vec3 singleScatter = BRDF_GGX( lightDir, viewDir, normal, material ); - float dotNL = saturate( dot( normal, lightDir ) ); - float dotNV = saturate( dot( normal, viewDir ) ); - vec2 dfgV = DFGApprox( vec3(0.0, 0.0, 1.0), vec3(sqrt(1.0 - dotNV * dotNV), 0.0, dotNV), material.roughness ); - vec2 dfgL = DFGApprox( vec3(0.0, 0.0, 1.0), vec3(sqrt(1.0 - dotNL * dotNL), 0.0, dotNL), material.roughness ); - vec3 FssEss_V = material.specularColor * dfgV.x + material.specularF90 * dfgV.y; - vec3 FssEss_L = material.specularColor * dfgL.x + material.specularF90 * dfgL.y; - float Ess_V = dfgV.x + dfgV.y; - float Ess_L = dfgL.x + dfgL.y; - float Ems_V = 1.0 - Ess_V; - float Ems_L = 1.0 - Ess_L; - vec3 Favg = material.specularColor + ( 1.0 - material.specularColor ) * 0.047619; - vec3 Fms = FssEss_V * FssEss_L * Favg / ( 1.0 - Ems_V * Ems_L * Favg * Favg + EPSILON ); - float compensationFactor = Ems_V * Ems_L; - vec3 multiScatter = Fms * compensationFactor; - return singleScatter + multiScatter; -} -#if NUM_RECT_AREA_LIGHTS > 0 - void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - vec3 normal = geometryNormal; - vec3 viewDir = geometryViewDir; - vec3 position = geometryPosition; - vec3 lightPos = rectAreaLight.position; - vec3 halfWidth = rectAreaLight.halfWidth; - vec3 halfHeight = rectAreaLight.halfHeight; - vec3 lightColor = rectAreaLight.color; - float roughness = material.roughness; - vec3 rectCoords[ 4 ]; - rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; rectCoords[ 1 ] = lightPos - halfWidth - halfHeight; - rectCoords[ 2 ] = lightPos - halfWidth + halfHeight; - rectCoords[ 3 ] = lightPos + halfWidth + halfHeight; - vec2 uv = LTC_Uv( normal, viewDir, roughness ); - vec4 t1 = texture2D( ltc_1, uv ); - vec4 t2 = texture2D( ltc_2, uv ); - mat3 mInv = mat3( - vec3( t1.x, 0, t1.y ), - vec3( 0, 1, 0 ), - vec3( t1.z, 0, t1.w ) - ); - vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y ); - reflectedLight.directSpecular += lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords ); - reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords ); - } -#endif -void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - float dotNL = saturate( dot( geometryNormal, directLight.direction ) ); - vec3 irradiance = dotNL * directLight.color; - #ifdef USE_CLEARCOAT - float dotNLcc = saturate( dot( geometryClearcoatNormal, directLight.direction ) ); - vec3 ccIrradiance = dotNLcc * directLight.color; - clearcoatSpecularDirect += ccIrradiance * BRDF_GGX_Clearcoat( directLight.direction, geometryViewDir, geometryClearcoatNormal, material ); - #endif - #ifdef USE_SHEEN - sheenSpecularDirect += irradiance * BRDF_Sheen( directLight.direction, geometryViewDir, geometryNormal, material.sheenColor, material.sheenRoughness ); - #endif - reflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material ); - reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectDiffuse_Physical( const in vec3 irradiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) { - reflectedLight.indirectDiffuse += irradiance * BRDF_Lambert( material.diffuseColor ); -} -void RE_IndirectSpecular_Physical( const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) { - #ifdef USE_CLEARCOAT - clearcoatSpecularIndirect += clearcoatRadiance * EnvironmentBRDF( geometryClearcoatNormal, geometryViewDir, material.clearcoatF0, material.clearcoatF90, material.clearcoatRoughness ); - #endif - #ifdef USE_SHEEN - sheenSpecularIndirect += irradiance * material.sheenColor * IBLSheenBRDF( geometryNormal, geometryViewDir, material.sheenRoughness ); - #endif - vec3 singleScattering = vec3( 0.0 ); - vec3 multiScattering = vec3( 0.0 ); - vec3 cosineWeightedIrradiance = irradiance * RECIPROCAL_PI; - #ifdef USE_IRIDESCENCE - computeMultiscatteringIridescence( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.iridescence, material.iridescenceFresnel, material.roughness, singleScattering, multiScattering ); - #else - computeMultiscattering( geometryNormal, geometryViewDir, material.specularColor, material.specularF90, material.roughness, singleScattering, multiScattering ); - #endif - vec3 totalScattering = singleScattering + multiScattering; - vec3 diffuse = material.diffuseColor * ( 1.0 - max( max( totalScattering.r, totalScattering.g ), totalScattering.b ) ); - reflectedLight.indirectSpecular += radiance * singleScattering; - reflectedLight.indirectSpecular += multiScattering * cosineWeightedIrradiance; - reflectedLight.indirectDiffuse += diffuse * cosineWeightedIrradiance; -} -#define RE_Direct RE_Direct_Physical -#define RE_Direct_RectArea RE_Direct_RectArea_Physical -#define RE_IndirectDiffuse RE_IndirectDiffuse_Physical -#define RE_IndirectSpecular RE_IndirectSpecular_Physical -float computeSpecularOcclusion( const in float dotNV, const in float ambientOcclusion, const in float roughness ) { - return saturate( pow( dotNV + ambientOcclusion, exp2( - 16.0 * roughness - 1.0 ) ) - 1.0 + ambientOcclusion ); -}`,Lf=` -vec3 geometryPosition = - vViewPosition; -vec3 geometryNormal = normal; -vec3 geometryViewDir = ( isOrthographic ) ? vec3( 0, 0, 1 ) : normalize( vViewPosition ); -vec3 geometryClearcoatNormal = vec3( 0.0 ); -#ifdef USE_CLEARCOAT - geometryClearcoatNormal = clearcoatNormal; -#endif -#ifdef USE_IRIDESCENCE - float dotNVi = saturate( dot( normal, geometryViewDir ) ); - if ( material.iridescenceThickness == 0.0 ) { - material.iridescence = 0.0; - } else { - material.iridescence = saturate( material.iridescence ); - } - if ( material.iridescence > 0.0 ) { - material.iridescenceFresnel = evalIridescence( 1.0, material.iridescenceIOR, dotNVi, material.iridescenceThickness, material.specularColor ); - material.iridescenceF0 = Schlick_to_F0( material.iridescenceFresnel, 1.0, dotNVi ); - } -#endif -IncidentLight directLight; -#if ( NUM_POINT_LIGHTS > 0 ) && defined( RE_Direct ) - PointLight pointLight; - #if defined( USE_SHADOWMAP ) && NUM_POINT_LIGHT_SHADOWS > 0 - PointLightShadow pointLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHTS; i ++ ) { - pointLight = pointLights[ i ]; - getPointLightInfo( pointLight, geometryPosition, directLight ); - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_POINT_LIGHT_SHADOWS ) - pointLightShadow = pointLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getPointShadow( pointShadowMap[ i ], pointLightShadow.shadowMapSize, pointLightShadow.shadowIntensity, pointLightShadow.shadowBias, pointLightShadow.shadowRadius, vPointShadowCoord[ i ], pointLightShadow.shadowCameraNear, pointLightShadow.shadowCameraFar ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_SPOT_LIGHTS > 0 ) && defined( RE_Direct ) - SpotLight spotLight; - vec4 spotColor; - vec3 spotLightCoord; - bool inSpotLightMap; - #if defined( USE_SHADOWMAP ) && NUM_SPOT_LIGHT_SHADOWS > 0 - SpotLightShadow spotLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHTS; i ++ ) { - spotLight = spotLights[ i ]; - getSpotLightInfo( spotLight, geometryPosition, directLight ); - #if ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) - #define SPOT_LIGHT_MAP_INDEX UNROLLED_LOOP_INDEX - #elif ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - #define SPOT_LIGHT_MAP_INDEX NUM_SPOT_LIGHT_MAPS - #else - #define SPOT_LIGHT_MAP_INDEX ( UNROLLED_LOOP_INDEX - NUM_SPOT_LIGHT_SHADOWS + NUM_SPOT_LIGHT_SHADOWS_WITH_MAPS ) - #endif - #if ( SPOT_LIGHT_MAP_INDEX < NUM_SPOT_LIGHT_MAPS ) - spotLightCoord = vSpotLightCoord[ i ].xyz / vSpotLightCoord[ i ].w; - inSpotLightMap = all( lessThan( abs( spotLightCoord * 2. - 1. ), vec3( 1.0 ) ) ); - spotColor = texture2D( spotLightMap[ SPOT_LIGHT_MAP_INDEX ], spotLightCoord.xy ); - directLight.color = inSpotLightMap ? directLight.color * spotColor.rgb : directLight.color; - #endif - #undef SPOT_LIGHT_MAP_INDEX - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - spotLightShadow = spotLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( spotShadowMap[ i ], spotLightShadow.shadowMapSize, spotLightShadow.shadowIntensity, spotLightShadow.shadowBias, spotLightShadow.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_DIR_LIGHTS > 0 ) && defined( RE_Direct ) - DirectionalLight directionalLight; - #if defined( USE_SHADOWMAP ) && NUM_DIR_LIGHT_SHADOWS > 0 - DirectionalLightShadow directionalLightShadow; - #endif - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHTS; i ++ ) { - directionalLight = directionalLights[ i ]; - getDirectionalLightInfo( directionalLight, directLight ); - #if defined( USE_SHADOWMAP ) && ( UNROLLED_LOOP_INDEX < NUM_DIR_LIGHT_SHADOWS ) - directionalLightShadow = directionalLightShadows[ i ]; - directLight.color *= ( directLight.visible && receiveShadow ) ? getShadow( directionalShadowMap[ i ], directionalLightShadow.shadowMapSize, directionalLightShadow.shadowIntensity, directionalLightShadow.shadowBias, directionalLightShadow.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; - #endif - RE_Direct( directLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if ( NUM_RECT_AREA_LIGHTS > 0 ) && defined( RE_Direct_RectArea ) - RectAreaLight rectAreaLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_RECT_AREA_LIGHTS; i ++ ) { - rectAreaLight = rectAreaLights[ i ]; - RE_Direct_RectArea( rectAreaLight, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); - } - #pragma unroll_loop_end -#endif -#if defined( RE_IndirectDiffuse ) - vec3 iblIrradiance = vec3( 0.0 ); - vec3 irradiance = getAmbientLightIrradiance( ambientLightColor ); - #if defined( USE_LIGHT_PROBES ) - irradiance += getLightProbeIrradiance( lightProbe, geometryNormal ); - #endif - #if ( NUM_HEMI_LIGHTS > 0 ) - #pragma unroll_loop_start - for ( int i = 0; i < NUM_HEMI_LIGHTS; i ++ ) { - irradiance += getHemisphereLightIrradiance( hemisphereLights[ i ], geometryNormal ); - } - #pragma unroll_loop_end - #endif -#endif -#if defined( RE_IndirectSpecular ) - vec3 radiance = vec3( 0.0 ); - vec3 clearcoatRadiance = vec3( 0.0 ); -#endif`,If=`#if defined( RE_IndirectDiffuse ) - #ifdef USE_LIGHTMAP - vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); - vec3 lightMapIrradiance = lightMapTexel.rgb * lightMapIntensity; - irradiance += lightMapIrradiance; - #endif - #if defined( USE_ENVMAP ) && defined( STANDARD ) && defined( ENVMAP_TYPE_CUBE_UV ) - iblIrradiance += getIBLIrradiance( geometryNormal ); - #endif -#endif -#if defined( USE_ENVMAP ) && defined( RE_IndirectSpecular ) - #ifdef USE_ANISOTROPY - radiance += getIBLAnisotropyRadiance( geometryViewDir, geometryNormal, material.roughness, material.anisotropyB, material.anisotropy ); - #else - radiance += getIBLRadiance( geometryViewDir, geometryNormal, material.roughness ); - #endif - #ifdef USE_CLEARCOAT - clearcoatRadiance += getIBLRadiance( geometryViewDir, geometryClearcoatNormal, material.clearcoatRoughness ); - #endif -#endif`,Uf=`#if defined( RE_IndirectDiffuse ) - RE_IndirectDiffuse( irradiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); -#endif -#if defined( RE_IndirectSpecular ) - RE_IndirectSpecular( radiance, iblIrradiance, clearcoatRadiance, geometryPosition, geometryNormal, geometryViewDir, geometryClearcoatNormal, material, reflectedLight ); -#endif`,Nf=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - gl_FragDepth = vIsPerspective == 0.0 ? gl_FragCoord.z : log2( vFragDepth ) * logDepthBufFC * 0.5; -#endif`,Ff=`#if defined( USE_LOGARITHMIC_DEPTH_BUFFER ) - uniform float logDepthBufFC; - varying float vFragDepth; - varying float vIsPerspective; -#endif`,Of=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - varying float vFragDepth; - varying float vIsPerspective; -#endif`,Bf=`#ifdef USE_LOGARITHMIC_DEPTH_BUFFER - vFragDepth = 1.0 + gl_Position.w; - vIsPerspective = float( isPerspectiveMatrix( projectionMatrix ) ); -#endif`,zf=`#ifdef USE_MAP - vec4 sampledDiffuseColor = texture2D( map, vMapUv ); - #ifdef DECODE_VIDEO_TEXTURE - sampledDiffuseColor = sRGBTransferEOTF( sampledDiffuseColor ); - #endif - diffuseColor *= sampledDiffuseColor; -#endif`,Vf=`#ifdef USE_MAP - uniform sampler2D map; -#endif`,Gf=`#if defined( USE_MAP ) || defined( USE_ALPHAMAP ) - #if defined( USE_POINTS_UV ) - vec2 uv = vUv; - #else - vec2 uv = ( uvTransform * vec3( gl_PointCoord.x, 1.0 - gl_PointCoord.y, 1 ) ).xy; - #endif -#endif -#ifdef USE_MAP - diffuseColor *= texture2D( map, uv ); -#endif -#ifdef USE_ALPHAMAP - diffuseColor.a *= texture2D( alphaMap, uv ).g; -#endif`,kf=`#if defined( USE_POINTS_UV ) - varying vec2 vUv; -#else - #if defined( USE_MAP ) || defined( USE_ALPHAMAP ) - uniform mat3 uvTransform; - #endif -#endif -#ifdef USE_MAP - uniform sampler2D map; -#endif -#ifdef USE_ALPHAMAP - uniform sampler2D alphaMap; -#endif`,Hf=`float metalnessFactor = metalness; -#ifdef USE_METALNESSMAP - vec4 texelMetalness = texture2D( metalnessMap, vMetalnessMapUv ); - metalnessFactor *= texelMetalness.b; -#endif`,Wf=`#ifdef USE_METALNESSMAP - uniform sampler2D metalnessMap; -#endif`,Xf=`#ifdef USE_INSTANCING_MORPH - float morphTargetInfluences[ MORPHTARGETS_COUNT ]; - float morphTargetBaseInfluence = texelFetch( morphTexture, ivec2( 0, gl_InstanceID ), 0 ).r; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - morphTargetInfluences[i] = texelFetch( morphTexture, ivec2( i + 1, gl_InstanceID ), 0 ).r; - } -#endif`,qf=`#if defined( USE_MORPHCOLORS ) - vColor *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - #if defined( USE_COLOR_ALPHA ) - if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ) * morphTargetInfluences[ i ]; - #elif defined( USE_COLOR ) - if ( morphTargetInfluences[ i ] != 0.0 ) vColor += getMorph( gl_VertexID, i, 2 ).rgb * morphTargetInfluences[ i ]; - #endif - } -#endif`,Yf=`#ifdef USE_MORPHNORMALS - objectNormal *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - if ( morphTargetInfluences[ i ] != 0.0 ) objectNormal += getMorph( gl_VertexID, i, 1 ).xyz * morphTargetInfluences[ i ]; - } -#endif`,Zf=`#ifdef USE_MORPHTARGETS - #ifndef USE_INSTANCING_MORPH - uniform float morphTargetBaseInfluence; - uniform float morphTargetInfluences[ MORPHTARGETS_COUNT ]; - #endif - uniform sampler2DArray morphTargetsTexture; - uniform ivec2 morphTargetsTextureSize; - vec4 getMorph( const in int vertexIndex, const in int morphTargetIndex, const in int offset ) { - int texelIndex = vertexIndex * MORPHTARGETS_TEXTURE_STRIDE + offset; - int y = texelIndex / morphTargetsTextureSize.x; - int x = texelIndex - y * morphTargetsTextureSize.x; - ivec3 morphUV = ivec3( x, y, morphTargetIndex ); - return texelFetch( morphTargetsTexture, morphUV, 0 ); - } -#endif`,Kf=`#ifdef USE_MORPHTARGETS - transformed *= morphTargetBaseInfluence; - for ( int i = 0; i < MORPHTARGETS_COUNT; i ++ ) { - if ( morphTargetInfluences[ i ] != 0.0 ) transformed += getMorph( gl_VertexID, i, 0 ).xyz * morphTargetInfluences[ i ]; - } -#endif`,jf=`float faceDirection = gl_FrontFacing ? 1.0 : - 1.0; -#ifdef FLAT_SHADED - vec3 fdx = dFdx( vViewPosition ); - vec3 fdy = dFdy( vViewPosition ); - vec3 normal = normalize( cross( fdx, fdy ) ); -#else - vec3 normal = normalize( vNormal ); - #ifdef DOUBLE_SIDED - normal *= faceDirection; - #endif -#endif -#if defined( USE_NORMALMAP_TANGENTSPACE ) || defined( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) - #ifdef USE_TANGENT - mat3 tbn = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); - #else - mat3 tbn = getTangentFrame( - vViewPosition, normal, - #if defined( USE_NORMALMAP ) - vNormalMapUv - #elif defined( USE_CLEARCOAT_NORMALMAP ) - vClearcoatNormalMapUv - #else - vUv - #endif - ); - #endif - #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) - tbn[0] *= faceDirection; - tbn[1] *= faceDirection; - #endif -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - #ifdef USE_TANGENT - mat3 tbn2 = mat3( normalize( vTangent ), normalize( vBitangent ), normal ); - #else - mat3 tbn2 = getTangentFrame( - vViewPosition, normal, vClearcoatNormalMapUv ); - #endif - #if defined( DOUBLE_SIDED ) && ! defined( FLAT_SHADED ) - tbn2[0] *= faceDirection; - tbn2[1] *= faceDirection; - #endif -#endif -vec3 nonPerturbedNormal = normal;`,Jf=`#ifdef USE_NORMALMAP_OBJECTSPACE - normal = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; - #ifdef FLIP_SIDED - normal = - normal; - #endif - #ifdef DOUBLE_SIDED - normal = normal * faceDirection; - #endif - normal = normalize( normalMatrix * normal ); -#elif defined( USE_NORMALMAP_TANGENTSPACE ) - vec3 mapN = texture2D( normalMap, vNormalMapUv ).xyz * 2.0 - 1.0; - mapN.xy *= normalScale; - normal = normalize( tbn * mapN ); -#elif defined( USE_BUMPMAP ) - normal = perturbNormalArb( - vViewPosition, normal, dHdxy_fwd(), faceDirection ); -#endif`,$f=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,Qf=`#ifndef FLAT_SHADED - varying vec3 vNormal; - #ifdef USE_TANGENT - varying vec3 vTangent; - varying vec3 vBitangent; - #endif -#endif`,td=`#ifndef FLAT_SHADED - vNormal = normalize( transformedNormal ); - #ifdef USE_TANGENT - vTangent = normalize( transformedTangent ); - vBitangent = normalize( cross( vNormal, vTangent ) * tangent.w ); - #endif -#endif`,ed=`#ifdef USE_NORMALMAP - uniform sampler2D normalMap; - uniform vec2 normalScale; -#endif -#ifdef USE_NORMALMAP_OBJECTSPACE - uniform mat3 normalMatrix; -#endif -#if ! defined ( USE_TANGENT ) && ( defined ( USE_NORMALMAP_TANGENTSPACE ) || defined ( USE_CLEARCOAT_NORMALMAP ) || defined( USE_ANISOTROPY ) ) - mat3 getTangentFrame( vec3 eye_pos, vec3 surf_norm, vec2 uv ) { - vec3 q0 = dFdx( eye_pos.xyz ); - vec3 q1 = dFdy( eye_pos.xyz ); - vec2 st0 = dFdx( uv.st ); - vec2 st1 = dFdy( uv.st ); - vec3 N = surf_norm; - vec3 q1perp = cross( q1, N ); - vec3 q0perp = cross( N, q0 ); - vec3 T = q1perp * st0.x + q0perp * st1.x; - vec3 B = q1perp * st0.y + q0perp * st1.y; - float det = max( dot( T, T ), dot( B, B ) ); - float scale = ( det == 0.0 ) ? 0.0 : inversesqrt( det ); - return mat3( T * scale, B * scale, N ); - } -#endif`,nd=`#ifdef USE_CLEARCOAT - vec3 clearcoatNormal = nonPerturbedNormal; -#endif`,id=`#ifdef USE_CLEARCOAT_NORMALMAP - vec3 clearcoatMapN = texture2D( clearcoatNormalMap, vClearcoatNormalMapUv ).xyz * 2.0 - 1.0; - clearcoatMapN.xy *= clearcoatNormalScale; - clearcoatNormal = normalize( tbn2 * clearcoatMapN ); -#endif`,sd=`#ifdef USE_CLEARCOATMAP - uniform sampler2D clearcoatMap; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - uniform sampler2D clearcoatNormalMap; - uniform vec2 clearcoatNormalScale; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - uniform sampler2D clearcoatRoughnessMap; -#endif`,rd=`#ifdef USE_IRIDESCENCEMAP - uniform sampler2D iridescenceMap; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - uniform sampler2D iridescenceThicknessMap; -#endif`,ad=`#ifdef OPAQUE -diffuseColor.a = 1.0; -#endif -#ifdef USE_TRANSMISSION -diffuseColor.a *= material.transmissionAlpha; -#endif -gl_FragColor = vec4( outgoingLight, diffuseColor.a );`,od=`vec3 packNormalToRGB( const in vec3 normal ) { - return normalize( normal ) * 0.5 + 0.5; -} -vec3 unpackRGBToNormal( const in vec3 rgb ) { - return 2.0 * rgb.xyz - 1.0; -} -const float PackUpscale = 256. / 255.;const float UnpackDownscale = 255. / 256.;const float ShiftRight8 = 1. / 256.; -const float Inv255 = 1. / 255.; -const vec4 PackFactors = vec4( 1.0, 256.0, 256.0 * 256.0, 256.0 * 256.0 * 256.0 ); -const vec2 UnpackFactors2 = vec2( UnpackDownscale, 1.0 / PackFactors.g ); -const vec3 UnpackFactors3 = vec3( UnpackDownscale / PackFactors.rg, 1.0 / PackFactors.b ); -const vec4 UnpackFactors4 = vec4( UnpackDownscale / PackFactors.rgb, 1.0 / PackFactors.a ); -vec4 packDepthToRGBA( const in float v ) { - if( v <= 0.0 ) - return vec4( 0., 0., 0., 0. ); - if( v >= 1.0 ) - return vec4( 1., 1., 1., 1. ); - float vuf; - float af = modf( v * PackFactors.a, vuf ); - float bf = modf( vuf * ShiftRight8, vuf ); - float gf = modf( vuf * ShiftRight8, vuf ); - return vec4( vuf * Inv255, gf * PackUpscale, bf * PackUpscale, af ); -} -vec3 packDepthToRGB( const in float v ) { - if( v <= 0.0 ) - return vec3( 0., 0., 0. ); - if( v >= 1.0 ) - return vec3( 1., 1., 1. ); - float vuf; - float bf = modf( v * PackFactors.b, vuf ); - float gf = modf( vuf * ShiftRight8, vuf ); - return vec3( vuf * Inv255, gf * PackUpscale, bf ); -} -vec2 packDepthToRG( const in float v ) { - if( v <= 0.0 ) - return vec2( 0., 0. ); - if( v >= 1.0 ) - return vec2( 1., 1. ); - float vuf; - float gf = modf( v * 256., vuf ); - return vec2( vuf * Inv255, gf ); -} -float unpackRGBAToDepth( const in vec4 v ) { - return dot( v, UnpackFactors4 ); -} -float unpackRGBToDepth( const in vec3 v ) { - return dot( v, UnpackFactors3 ); -} -float unpackRGToDepth( const in vec2 v ) { - return v.r * UnpackFactors2.r + v.g * UnpackFactors2.g; -} -vec4 pack2HalfToRGBA( const in vec2 v ) { - vec4 r = vec4( v.x, fract( v.x * 255.0 ), v.y, fract( v.y * 255.0 ) ); - return vec4( r.x - r.y / 255.0, r.y, r.z - r.w / 255.0, r.w ); -} -vec2 unpackRGBATo2Half( const in vec4 v ) { - return vec2( v.x + ( v.y / 255.0 ), v.z + ( v.w / 255.0 ) ); -} -float viewZToOrthographicDepth( const in float viewZ, const in float near, const in float far ) { - return ( viewZ + near ) / ( near - far ); -} -float orthographicDepthToViewZ( const in float depth, const in float near, const in float far ) { - return depth * ( near - far ) - near; -} -float viewZToPerspectiveDepth( const in float viewZ, const in float near, const in float far ) { - return ( ( near + viewZ ) * far ) / ( ( far - near ) * viewZ ); -} -float perspectiveDepthToViewZ( const in float depth, const in float near, const in float far ) { - return ( near * far ) / ( ( far - near ) * depth - far ); -}`,cd=`#ifdef PREMULTIPLIED_ALPHA - gl_FragColor.rgb *= gl_FragColor.a; -#endif`,ld=`vec4 mvPosition = vec4( transformed, 1.0 ); -#ifdef USE_BATCHING - mvPosition = batchingMatrix * mvPosition; -#endif -#ifdef USE_INSTANCING - mvPosition = instanceMatrix * mvPosition; -#endif -mvPosition = modelViewMatrix * mvPosition; -gl_Position = projectionMatrix * mvPosition;`,hd=`#ifdef DITHERING - gl_FragColor.rgb = dithering( gl_FragColor.rgb ); -#endif`,ud=`#ifdef DITHERING - vec3 dithering( vec3 color ) { - float grid_position = rand( gl_FragCoord.xy ); - vec3 dither_shift_RGB = vec3( 0.25 / 255.0, -0.25 / 255.0, 0.25 / 255.0 ); - dither_shift_RGB = mix( 2.0 * dither_shift_RGB, -2.0 * dither_shift_RGB, grid_position ); - return color + dither_shift_RGB; - } -#endif`,fd=`float roughnessFactor = roughness; -#ifdef USE_ROUGHNESSMAP - vec4 texelRoughness = texture2D( roughnessMap, vRoughnessMapUv ); - roughnessFactor *= texelRoughness.g; -#endif`,dd=`#ifdef USE_ROUGHNESSMAP - uniform sampler2D roughnessMap; -#endif`,pd=`#if NUM_SPOT_LIGHT_COORDS > 0 - varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; -#endif -#if NUM_SPOT_LIGHT_MAPS > 0 - uniform sampler2D spotLightMap[ NUM_SPOT_LIGHT_MAPS ]; -#endif -#ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - uniform sampler2D directionalShadowMap[ NUM_DIR_LIGHT_SHADOWS ]; - varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; - struct DirectionalLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - uniform sampler2D spotShadowMap[ NUM_SPOT_LIGHT_SHADOWS ]; - struct SpotLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - uniform sampler2D pointShadowMap[ NUM_POINT_LIGHT_SHADOWS ]; - varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; - struct PointLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - float shadowCameraNear; - float shadowCameraFar; - }; - uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; - #endif - float texture2DCompare( sampler2D depths, vec2 uv, float compare ) { - float depth = unpackRGBAToDepth( texture2D( depths, uv ) ); - #ifdef USE_REVERSED_DEPTH_BUFFER - return step( depth, compare ); - #else - return step( compare, depth ); - #endif - } - vec2 texture2DDistribution( sampler2D shadow, vec2 uv ) { - return unpackRGBATo2Half( texture2D( shadow, uv ) ); - } - float VSMShadow( sampler2D shadow, vec2 uv, float compare ) { - float occlusion = 1.0; - vec2 distribution = texture2DDistribution( shadow, uv ); - #ifdef USE_REVERSED_DEPTH_BUFFER - float hard_shadow = step( distribution.x, compare ); - #else - float hard_shadow = step( compare, distribution.x ); - #endif - if ( hard_shadow != 1.0 ) { - float distance = compare - distribution.x; - float variance = max( 0.00000, distribution.y * distribution.y ); - float softness_probability = variance / (variance + distance * distance ); softness_probability = clamp( ( softness_probability - 0.3 ) / ( 0.95 - 0.3 ), 0.0, 1.0 ); occlusion = clamp( max( hard_shadow, softness_probability ), 0.0, 1.0 ); - } - return occlusion; - } - float getShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord ) { - float shadow = 1.0; - shadowCoord.xyz /= shadowCoord.w; - shadowCoord.z += shadowBias; - bool inFrustum = shadowCoord.x >= 0.0 && shadowCoord.x <= 1.0 && shadowCoord.y >= 0.0 && shadowCoord.y <= 1.0; - bool frustumTest = inFrustum && shadowCoord.z <= 1.0; - if ( frustumTest ) { - #if defined( SHADOWMAP_TYPE_PCF ) - vec2 texelSize = vec2( 1.0 ) / shadowMapSize; - float dx0 = - texelSize.x * shadowRadius; - float dy0 = - texelSize.y * shadowRadius; - float dx1 = + texelSize.x * shadowRadius; - float dy1 = + texelSize.y * shadowRadius; - float dx2 = dx0 / 2.0; - float dy2 = dy0 / 2.0; - float dx3 = dx1 / 2.0; - float dy3 = dy1 / 2.0; - shadow = ( - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy2 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy2 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy2 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, 0.0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, 0.0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, 0.0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, 0.0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx2, dy3 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy3 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx3, dy3 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx0, dy1 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( 0.0, dy1 ), shadowCoord.z ) + - texture2DCompare( shadowMap, shadowCoord.xy + vec2( dx1, dy1 ), shadowCoord.z ) - ) * ( 1.0 / 17.0 ); - #elif defined( SHADOWMAP_TYPE_PCF_SOFT ) - vec2 texelSize = vec2( 1.0 ) / shadowMapSize; - float dx = texelSize.x; - float dy = texelSize.y; - vec2 uv = shadowCoord.xy; - vec2 f = fract( uv * shadowMapSize + 0.5 ); - uv -= f * texelSize; - shadow = ( - texture2DCompare( shadowMap, uv, shadowCoord.z ) + - texture2DCompare( shadowMap, uv + vec2( dx, 0.0 ), shadowCoord.z ) + - texture2DCompare( shadowMap, uv + vec2( 0.0, dy ), shadowCoord.z ) + - texture2DCompare( shadowMap, uv + texelSize, shadowCoord.z ) + - mix( texture2DCompare( shadowMap, uv + vec2( -dx, 0.0 ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 0.0 ), shadowCoord.z ), - f.x ) + - mix( texture2DCompare( shadowMap, uv + vec2( -dx, dy ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, dy ), shadowCoord.z ), - f.x ) + - mix( texture2DCompare( shadowMap, uv + vec2( 0.0, -dy ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( 0.0, 2.0 * dy ), shadowCoord.z ), - f.y ) + - mix( texture2DCompare( shadowMap, uv + vec2( dx, -dy ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( dx, 2.0 * dy ), shadowCoord.z ), - f.y ) + - mix( mix( texture2DCompare( shadowMap, uv + vec2( -dx, -dy ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, -dy ), shadowCoord.z ), - f.x ), - mix( texture2DCompare( shadowMap, uv + vec2( -dx, 2.0 * dy ), shadowCoord.z ), - texture2DCompare( shadowMap, uv + vec2( 2.0 * dx, 2.0 * dy ), shadowCoord.z ), - f.x ), - f.y ) - ) * ( 1.0 / 9.0 ); - #elif defined( SHADOWMAP_TYPE_VSM ) - shadow = VSMShadow( shadowMap, shadowCoord.xy, shadowCoord.z ); - #else - shadow = texture2DCompare( shadowMap, shadowCoord.xy, shadowCoord.z ); - #endif - } - return mix( 1.0, shadow, shadowIntensity ); - } - vec2 cubeToUV( vec3 v, float texelSizeY ) { - vec3 absV = abs( v ); - float scaleToCube = 1.0 / max( absV.x, max( absV.y, absV.z ) ); - absV *= scaleToCube; - v *= scaleToCube * ( 1.0 - 2.0 * texelSizeY ); - vec2 planar = v.xy; - float almostATexel = 1.5 * texelSizeY; - float almostOne = 1.0 - almostATexel; - if ( absV.z >= almostOne ) { - if ( v.z > 0.0 ) - planar.x = 4.0 - v.x; - } else if ( absV.x >= almostOne ) { - float signX = sign( v.x ); - planar.x = v.z * signX + 2.0 * signX; - } else if ( absV.y >= almostOne ) { - float signY = sign( v.y ); - planar.x = v.x + 2.0 * signY + 2.0; - planar.y = v.z * signY - 2.0; - } - return vec2( 0.125, 0.25 ) * planar + vec2( 0.375, 0.75 ); - } - float getPointShadow( sampler2D shadowMap, vec2 shadowMapSize, float shadowIntensity, float shadowBias, float shadowRadius, vec4 shadowCoord, float shadowCameraNear, float shadowCameraFar ) { - float shadow = 1.0; - vec3 lightToPosition = shadowCoord.xyz; - - float lightToPositionLength = length( lightToPosition ); - if ( lightToPositionLength - shadowCameraFar <= 0.0 && lightToPositionLength - shadowCameraNear >= 0.0 ) { - float dp = ( lightToPositionLength - shadowCameraNear ) / ( shadowCameraFar - shadowCameraNear ); dp += shadowBias; - vec3 bd3D = normalize( lightToPosition ); - vec2 texelSize = vec2( 1.0 ) / ( shadowMapSize * vec2( 4.0, 2.0 ) ); - #if defined( SHADOWMAP_TYPE_PCF ) || defined( SHADOWMAP_TYPE_PCF_SOFT ) || defined( SHADOWMAP_TYPE_VSM ) - vec2 offset = vec2( - 1, 1 ) * shadowRadius * texelSize.y; - shadow = ( - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyy, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyy, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xyx, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yyx, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxy, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxy, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.xxx, texelSize.y ), dp ) + - texture2DCompare( shadowMap, cubeToUV( bd3D + offset.yxx, texelSize.y ), dp ) - ) * ( 1.0 / 9.0 ); - #else - shadow = texture2DCompare( shadowMap, cubeToUV( bd3D, texelSize.y ), dp ); - #endif - } - return mix( 1.0, shadow, shadowIntensity ); - } -#endif`,md=`#if NUM_SPOT_LIGHT_COORDS > 0 - uniform mat4 spotLightMatrix[ NUM_SPOT_LIGHT_COORDS ]; - varying vec4 vSpotLightCoord[ NUM_SPOT_LIGHT_COORDS ]; -#endif -#ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - uniform mat4 directionalShadowMatrix[ NUM_DIR_LIGHT_SHADOWS ]; - varying vec4 vDirectionalShadowCoord[ NUM_DIR_LIGHT_SHADOWS ]; - struct DirectionalLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform DirectionalLightShadow directionalLightShadows[ NUM_DIR_LIGHT_SHADOWS ]; - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - struct SpotLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - }; - uniform SpotLightShadow spotLightShadows[ NUM_SPOT_LIGHT_SHADOWS ]; - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - uniform mat4 pointShadowMatrix[ NUM_POINT_LIGHT_SHADOWS ]; - varying vec4 vPointShadowCoord[ NUM_POINT_LIGHT_SHADOWS ]; - struct PointLightShadow { - float shadowIntensity; - float shadowBias; - float shadowNormalBias; - float shadowRadius; - vec2 shadowMapSize; - float shadowCameraNear; - float shadowCameraFar; - }; - uniform PointLightShadow pointLightShadows[ NUM_POINT_LIGHT_SHADOWS ]; - #endif -#endif`,xd=`#if ( defined( USE_SHADOWMAP ) && ( NUM_DIR_LIGHT_SHADOWS > 0 || NUM_POINT_LIGHT_SHADOWS > 0 ) ) || ( NUM_SPOT_LIGHT_COORDS > 0 ) - vec3 shadowWorldNormal = inverseTransformDirection( transformedNormal, viewMatrix ); - vec4 shadowWorldPosition; -#endif -#if defined( USE_SHADOWMAP ) - #if NUM_DIR_LIGHT_SHADOWS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { - shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * directionalLightShadows[ i ].shadowNormalBias, 0 ); - vDirectionalShadowCoord[ i ] = directionalShadowMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { - shadowWorldPosition = worldPosition + vec4( shadowWorldNormal * pointLightShadows[ i ].shadowNormalBias, 0 ); - vPointShadowCoord[ i ] = pointShadowMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end - #endif -#endif -#if NUM_SPOT_LIGHT_COORDS > 0 - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHT_COORDS; i ++ ) { - shadowWorldPosition = worldPosition; - #if ( defined( USE_SHADOWMAP ) && UNROLLED_LOOP_INDEX < NUM_SPOT_LIGHT_SHADOWS ) - shadowWorldPosition.xyz += shadowWorldNormal * spotLightShadows[ i ].shadowNormalBias; - #endif - vSpotLightCoord[ i ] = spotLightMatrix[ i ] * shadowWorldPosition; - } - #pragma unroll_loop_end -#endif`,gd=`float getShadowMask() { - float shadow = 1.0; - #ifdef USE_SHADOWMAP - #if NUM_DIR_LIGHT_SHADOWS > 0 - DirectionalLightShadow directionalLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_DIR_LIGHT_SHADOWS; i ++ ) { - directionalLight = directionalLightShadows[ i ]; - shadow *= receiveShadow ? getShadow( directionalShadowMap[ i ], directionalLight.shadowMapSize, directionalLight.shadowIntensity, directionalLight.shadowBias, directionalLight.shadowRadius, vDirectionalShadowCoord[ i ] ) : 1.0; - } - #pragma unroll_loop_end - #endif - #if NUM_SPOT_LIGHT_SHADOWS > 0 - SpotLightShadow spotLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_SPOT_LIGHT_SHADOWS; i ++ ) { - spotLight = spotLightShadows[ i ]; - shadow *= receiveShadow ? getShadow( spotShadowMap[ i ], spotLight.shadowMapSize, spotLight.shadowIntensity, spotLight.shadowBias, spotLight.shadowRadius, vSpotLightCoord[ i ] ) : 1.0; - } - #pragma unroll_loop_end - #endif - #if NUM_POINT_LIGHT_SHADOWS > 0 - PointLightShadow pointLight; - #pragma unroll_loop_start - for ( int i = 0; i < NUM_POINT_LIGHT_SHADOWS; i ++ ) { - pointLight = pointLightShadows[ i ]; - shadow *= receiveShadow ? getPointShadow( pointShadowMap[ i ], pointLight.shadowMapSize, pointLight.shadowIntensity, pointLight.shadowBias, pointLight.shadowRadius, vPointShadowCoord[ i ], pointLight.shadowCameraNear, pointLight.shadowCameraFar ) : 1.0; - } - #pragma unroll_loop_end - #endif - #endif - return shadow; -}`,_d=`#ifdef USE_SKINNING - mat4 boneMatX = getBoneMatrix( skinIndex.x ); - mat4 boneMatY = getBoneMatrix( skinIndex.y ); - mat4 boneMatZ = getBoneMatrix( skinIndex.z ); - mat4 boneMatW = getBoneMatrix( skinIndex.w ); -#endif`,vd=`#ifdef USE_SKINNING - uniform mat4 bindMatrix; - uniform mat4 bindMatrixInverse; - uniform highp sampler2D boneTexture; - mat4 getBoneMatrix( const in float i ) { - int size = textureSize( boneTexture, 0 ).x; - int j = int( i ) * 4; - int x = j % size; - int y = j / size; - vec4 v1 = texelFetch( boneTexture, ivec2( x, y ), 0 ); - vec4 v2 = texelFetch( boneTexture, ivec2( x + 1, y ), 0 ); - vec4 v3 = texelFetch( boneTexture, ivec2( x + 2, y ), 0 ); - vec4 v4 = texelFetch( boneTexture, ivec2( x + 3, y ), 0 ); - return mat4( v1, v2, v3, v4 ); - } -#endif`,Md=`#ifdef USE_SKINNING - vec4 skinVertex = bindMatrix * vec4( transformed, 1.0 ); - vec4 skinned = vec4( 0.0 ); - skinned += boneMatX * skinVertex * skinWeight.x; - skinned += boneMatY * skinVertex * skinWeight.y; - skinned += boneMatZ * skinVertex * skinWeight.z; - skinned += boneMatW * skinVertex * skinWeight.w; - transformed = ( bindMatrixInverse * skinned ).xyz; -#endif`,Sd=`#ifdef USE_SKINNING - mat4 skinMatrix = mat4( 0.0 ); - skinMatrix += skinWeight.x * boneMatX; - skinMatrix += skinWeight.y * boneMatY; - skinMatrix += skinWeight.z * boneMatZ; - skinMatrix += skinWeight.w * boneMatW; - skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix; - objectNormal = vec4( skinMatrix * vec4( objectNormal, 0.0 ) ).xyz; - #ifdef USE_TANGENT - objectTangent = vec4( skinMatrix * vec4( objectTangent, 0.0 ) ).xyz; - #endif -#endif`,yd=`float specularStrength; -#ifdef USE_SPECULARMAP - vec4 texelSpecular = texture2D( specularMap, vSpecularMapUv ); - specularStrength = texelSpecular.r; -#else - specularStrength = 1.0; -#endif`,bd=`#ifdef USE_SPECULARMAP - uniform sampler2D specularMap; -#endif`,Ed=`#if defined( TONE_MAPPING ) - gl_FragColor.rgb = toneMapping( gl_FragColor.rgb ); -#endif`,Td=`#ifndef saturate -#define saturate( a ) clamp( a, 0.0, 1.0 ) -#endif -uniform float toneMappingExposure; -vec3 LinearToneMapping( vec3 color ) { - return saturate( toneMappingExposure * color ); -} -vec3 ReinhardToneMapping( vec3 color ) { - color *= toneMappingExposure; - return saturate( color / ( vec3( 1.0 ) + color ) ); -} -vec3 CineonToneMapping( vec3 color ) { - color *= toneMappingExposure; - color = max( vec3( 0.0 ), color - 0.004 ); - return pow( ( color * ( 6.2 * color + 0.5 ) ) / ( color * ( 6.2 * color + 1.7 ) + 0.06 ), vec3( 2.2 ) ); -} -vec3 RRTAndODTFit( vec3 v ) { - vec3 a = v * ( v + 0.0245786 ) - 0.000090537; - vec3 b = v * ( 0.983729 * v + 0.4329510 ) + 0.238081; - return a / b; -} -vec3 ACESFilmicToneMapping( vec3 color ) { - const mat3 ACESInputMat = mat3( - vec3( 0.59719, 0.07600, 0.02840 ), vec3( 0.35458, 0.90834, 0.13383 ), - vec3( 0.04823, 0.01566, 0.83777 ) - ); - const mat3 ACESOutputMat = mat3( - vec3( 1.60475, -0.10208, -0.00327 ), vec3( -0.53108, 1.10813, -0.07276 ), - vec3( -0.07367, -0.00605, 1.07602 ) - ); - color *= toneMappingExposure / 0.6; - color = ACESInputMat * color; - color = RRTAndODTFit( color ); - color = ACESOutputMat * color; - return saturate( color ); -} -const mat3 LINEAR_REC2020_TO_LINEAR_SRGB = mat3( - vec3( 1.6605, - 0.1246, - 0.0182 ), - vec3( - 0.5876, 1.1329, - 0.1006 ), - vec3( - 0.0728, - 0.0083, 1.1187 ) -); -const mat3 LINEAR_SRGB_TO_LINEAR_REC2020 = mat3( - vec3( 0.6274, 0.0691, 0.0164 ), - vec3( 0.3293, 0.9195, 0.0880 ), - vec3( 0.0433, 0.0113, 0.8956 ) -); -vec3 agxDefaultContrastApprox( vec3 x ) { - vec3 x2 = x * x; - vec3 x4 = x2 * x2; - return + 15.5 * x4 * x2 - - 40.14 * x4 * x - + 31.96 * x4 - - 6.868 * x2 * x - + 0.4298 * x2 - + 0.1191 * x - - 0.00232; -} -vec3 AgXToneMapping( vec3 color ) { - const mat3 AgXInsetMatrix = mat3( - vec3( 0.856627153315983, 0.137318972929847, 0.11189821299995 ), - vec3( 0.0951212405381588, 0.761241990602591, 0.0767994186031903 ), - vec3( 0.0482516061458583, 0.101439036467562, 0.811302368396859 ) - ); - const mat3 AgXOutsetMatrix = mat3( - vec3( 1.1271005818144368, - 0.1413297634984383, - 0.14132976349843826 ), - vec3( - 0.11060664309660323, 1.157823702216272, - 0.11060664309660294 ), - vec3( - 0.016493938717834573, - 0.016493938717834257, 1.2519364065950405 ) - ); - const float AgxMinEv = - 12.47393; const float AgxMaxEv = 4.026069; - color *= toneMappingExposure; - color = LINEAR_SRGB_TO_LINEAR_REC2020 * color; - color = AgXInsetMatrix * color; - color = max( color, 1e-10 ); color = log2( color ); - color = ( color - AgxMinEv ) / ( AgxMaxEv - AgxMinEv ); - color = clamp( color, 0.0, 1.0 ); - color = agxDefaultContrastApprox( color ); - color = AgXOutsetMatrix * color; - color = pow( max( vec3( 0.0 ), color ), vec3( 2.2 ) ); - color = LINEAR_REC2020_TO_LINEAR_SRGB * color; - color = clamp( color, 0.0, 1.0 ); - return color; -} -vec3 NeutralToneMapping( vec3 color ) { - const float StartCompression = 0.8 - 0.04; - const float Desaturation = 0.15; - color *= toneMappingExposure; - float x = min( color.r, min( color.g, color.b ) ); - float offset = x < 0.08 ? x - 6.25 * x * x : 0.04; - color -= offset; - float peak = max( color.r, max( color.g, color.b ) ); - if ( peak < StartCompression ) return color; - float d = 1. - StartCompression; - float newPeak = 1. - d * d / ( peak + d - StartCompression ); - color *= newPeak / peak; - float g = 1. - 1. / ( Desaturation * ( peak - newPeak ) + 1. ); - return mix( color, vec3( newPeak ), g ); -} -vec3 CustomToneMapping( vec3 color ) { return color; }`,Ad=`#ifdef USE_TRANSMISSION - material.transmission = transmission; - material.transmissionAlpha = 1.0; - material.thickness = thickness; - material.attenuationDistance = attenuationDistance; - material.attenuationColor = attenuationColor; - #ifdef USE_TRANSMISSIONMAP - material.transmission *= texture2D( transmissionMap, vTransmissionMapUv ).r; - #endif - #ifdef USE_THICKNESSMAP - material.thickness *= texture2D( thicknessMap, vThicknessMapUv ).g; - #endif - vec3 pos = vWorldPosition; - vec3 v = normalize( cameraPosition - pos ); - vec3 n = inverseTransformDirection( normal, viewMatrix ); - vec4 transmitted = getIBLVolumeRefraction( - n, v, material.roughness, material.diffuseColor, material.specularColor, material.specularF90, - pos, modelMatrix, viewMatrix, projectionMatrix, material.dispersion, material.ior, material.thickness, - material.attenuationColor, material.attenuationDistance ); - material.transmissionAlpha = mix( material.transmissionAlpha, transmitted.a, material.transmission ); - totalDiffuse = mix( totalDiffuse, transmitted.rgb, material.transmission ); -#endif`,wd=`#ifdef USE_TRANSMISSION - uniform float transmission; - uniform float thickness; - uniform float attenuationDistance; - uniform vec3 attenuationColor; - #ifdef USE_TRANSMISSIONMAP - uniform sampler2D transmissionMap; - #endif - #ifdef USE_THICKNESSMAP - uniform sampler2D thicknessMap; - #endif - uniform vec2 transmissionSamplerSize; - uniform sampler2D transmissionSamplerMap; - uniform mat4 modelMatrix; - uniform mat4 projectionMatrix; - varying vec3 vWorldPosition; - float w0( float a ) { - return ( 1.0 / 6.0 ) * ( a * ( a * ( - a + 3.0 ) - 3.0 ) + 1.0 ); - } - float w1( float a ) { - return ( 1.0 / 6.0 ) * ( a * a * ( 3.0 * a - 6.0 ) + 4.0 ); - } - float w2( float a ){ - return ( 1.0 / 6.0 ) * ( a * ( a * ( - 3.0 * a + 3.0 ) + 3.0 ) + 1.0 ); - } - float w3( float a ) { - return ( 1.0 / 6.0 ) * ( a * a * a ); - } - float g0( float a ) { - return w0( a ) + w1( a ); - } - float g1( float a ) { - return w2( a ) + w3( a ); - } - float h0( float a ) { - return - 1.0 + w1( a ) / ( w0( a ) + w1( a ) ); - } - float h1( float a ) { - return 1.0 + w3( a ) / ( w2( a ) + w3( a ) ); - } - vec4 bicubic( sampler2D tex, vec2 uv, vec4 texelSize, float lod ) { - uv = uv * texelSize.zw + 0.5; - vec2 iuv = floor( uv ); - vec2 fuv = fract( uv ); - float g0x = g0( fuv.x ); - float g1x = g1( fuv.x ); - float h0x = h0( fuv.x ); - float h1x = h1( fuv.x ); - float h0y = h0( fuv.y ); - float h1y = h1( fuv.y ); - vec2 p0 = ( vec2( iuv.x + h0x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; - vec2 p1 = ( vec2( iuv.x + h1x, iuv.y + h0y ) - 0.5 ) * texelSize.xy; - vec2 p2 = ( vec2( iuv.x + h0x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; - vec2 p3 = ( vec2( iuv.x + h1x, iuv.y + h1y ) - 0.5 ) * texelSize.xy; - return g0( fuv.y ) * ( g0x * textureLod( tex, p0, lod ) + g1x * textureLod( tex, p1, lod ) ) + - g1( fuv.y ) * ( g0x * textureLod( tex, p2, lod ) + g1x * textureLod( tex, p3, lod ) ); - } - vec4 textureBicubic( sampler2D sampler, vec2 uv, float lod ) { - vec2 fLodSize = vec2( textureSize( sampler, int( lod ) ) ); - vec2 cLodSize = vec2( textureSize( sampler, int( lod + 1.0 ) ) ); - vec2 fLodSizeInv = 1.0 / fLodSize; - vec2 cLodSizeInv = 1.0 / cLodSize; - vec4 fSample = bicubic( sampler, uv, vec4( fLodSizeInv, fLodSize ), floor( lod ) ); - vec4 cSample = bicubic( sampler, uv, vec4( cLodSizeInv, cLodSize ), ceil( lod ) ); - return mix( fSample, cSample, fract( lod ) ); - } - vec3 getVolumeTransmissionRay( const in vec3 n, const in vec3 v, const in float thickness, const in float ior, const in mat4 modelMatrix ) { - vec3 refractionVector = refract( - v, normalize( n ), 1.0 / ior ); - vec3 modelScale; - modelScale.x = length( vec3( modelMatrix[ 0 ].xyz ) ); - modelScale.y = length( vec3( modelMatrix[ 1 ].xyz ) ); - modelScale.z = length( vec3( modelMatrix[ 2 ].xyz ) ); - return normalize( refractionVector ) * thickness * modelScale; - } - float applyIorToRoughness( const in float roughness, const in float ior ) { - return roughness * clamp( ior * 2.0 - 2.0, 0.0, 1.0 ); - } - vec4 getTransmissionSample( const in vec2 fragCoord, const in float roughness, const in float ior ) { - float lod = log2( transmissionSamplerSize.x ) * applyIorToRoughness( roughness, ior ); - return textureBicubic( transmissionSamplerMap, fragCoord.xy, lod ); - } - vec3 volumeAttenuation( const in float transmissionDistance, const in vec3 attenuationColor, const in float attenuationDistance ) { - if ( isinf( attenuationDistance ) ) { - return vec3( 1.0 ); - } else { - vec3 attenuationCoefficient = -log( attenuationColor ) / attenuationDistance; - vec3 transmittance = exp( - attenuationCoefficient * transmissionDistance ); return transmittance; - } - } - vec4 getIBLVolumeRefraction( const in vec3 n, const in vec3 v, const in float roughness, const in vec3 diffuseColor, - const in vec3 specularColor, const in float specularF90, const in vec3 position, const in mat4 modelMatrix, - const in mat4 viewMatrix, const in mat4 projMatrix, const in float dispersion, const in float ior, const in float thickness, - const in vec3 attenuationColor, const in float attenuationDistance ) { - vec4 transmittedLight; - vec3 transmittance; - #ifdef USE_DISPERSION - float halfSpread = ( ior - 1.0 ) * 0.025 * dispersion; - vec3 iors = vec3( ior - halfSpread, ior, ior + halfSpread ); - for ( int i = 0; i < 3; i ++ ) { - vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, iors[ i ], modelMatrix ); - vec3 refractedRayExit = position + transmissionRay; - vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); - vec2 refractionCoords = ndcPos.xy / ndcPos.w; - refractionCoords += 1.0; - refractionCoords /= 2.0; - vec4 transmissionSample = getTransmissionSample( refractionCoords, roughness, iors[ i ] ); - transmittedLight[ i ] = transmissionSample[ i ]; - transmittedLight.a += transmissionSample.a; - transmittance[ i ] = diffuseColor[ i ] * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance )[ i ]; - } - transmittedLight.a /= 3.0; - #else - vec3 transmissionRay = getVolumeTransmissionRay( n, v, thickness, ior, modelMatrix ); - vec3 refractedRayExit = position + transmissionRay; - vec4 ndcPos = projMatrix * viewMatrix * vec4( refractedRayExit, 1.0 ); - vec2 refractionCoords = ndcPos.xy / ndcPos.w; - refractionCoords += 1.0; - refractionCoords /= 2.0; - transmittedLight = getTransmissionSample( refractionCoords, roughness, ior ); - transmittance = diffuseColor * volumeAttenuation( length( transmissionRay ), attenuationColor, attenuationDistance ); - #endif - vec3 attenuatedColor = transmittance * transmittedLight.rgb; - vec3 F = EnvironmentBRDF( n, v, specularColor, specularF90, roughness ); - float transmittanceFactor = ( transmittance.r + transmittance.g + transmittance.b ) / 3.0; - return vec4( ( 1.0 - F ) * attenuatedColor, 1.0 - ( 1.0 - transmittedLight.a ) * transmittanceFactor ); - } -#endif`,Rd=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - varying vec2 vUv; -#endif -#ifdef USE_MAP - varying vec2 vMapUv; -#endif -#ifdef USE_ALPHAMAP - varying vec2 vAlphaMapUv; -#endif -#ifdef USE_LIGHTMAP - varying vec2 vLightMapUv; -#endif -#ifdef USE_AOMAP - varying vec2 vAoMapUv; -#endif -#ifdef USE_BUMPMAP - varying vec2 vBumpMapUv; -#endif -#ifdef USE_NORMALMAP - varying vec2 vNormalMapUv; -#endif -#ifdef USE_EMISSIVEMAP - varying vec2 vEmissiveMapUv; -#endif -#ifdef USE_METALNESSMAP - varying vec2 vMetalnessMapUv; -#endif -#ifdef USE_ROUGHNESSMAP - varying vec2 vRoughnessMapUv; -#endif -#ifdef USE_ANISOTROPYMAP - varying vec2 vAnisotropyMapUv; -#endif -#ifdef USE_CLEARCOATMAP - varying vec2 vClearcoatMapUv; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - varying vec2 vClearcoatNormalMapUv; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - varying vec2 vClearcoatRoughnessMapUv; -#endif -#ifdef USE_IRIDESCENCEMAP - varying vec2 vIridescenceMapUv; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - varying vec2 vIridescenceThicknessMapUv; -#endif -#ifdef USE_SHEEN_COLORMAP - varying vec2 vSheenColorMapUv; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - varying vec2 vSheenRoughnessMapUv; -#endif -#ifdef USE_SPECULARMAP - varying vec2 vSpecularMapUv; -#endif -#ifdef USE_SPECULAR_COLORMAP - varying vec2 vSpecularColorMapUv; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - varying vec2 vSpecularIntensityMapUv; -#endif -#ifdef USE_TRANSMISSIONMAP - uniform mat3 transmissionMapTransform; - varying vec2 vTransmissionMapUv; -#endif -#ifdef USE_THICKNESSMAP - uniform mat3 thicknessMapTransform; - varying vec2 vThicknessMapUv; -#endif`,Cd=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - varying vec2 vUv; -#endif -#ifdef USE_MAP - uniform mat3 mapTransform; - varying vec2 vMapUv; -#endif -#ifdef USE_ALPHAMAP - uniform mat3 alphaMapTransform; - varying vec2 vAlphaMapUv; -#endif -#ifdef USE_LIGHTMAP - uniform mat3 lightMapTransform; - varying vec2 vLightMapUv; -#endif -#ifdef USE_AOMAP - uniform mat3 aoMapTransform; - varying vec2 vAoMapUv; -#endif -#ifdef USE_BUMPMAP - uniform mat3 bumpMapTransform; - varying vec2 vBumpMapUv; -#endif -#ifdef USE_NORMALMAP - uniform mat3 normalMapTransform; - varying vec2 vNormalMapUv; -#endif -#ifdef USE_DISPLACEMENTMAP - uniform mat3 displacementMapTransform; - varying vec2 vDisplacementMapUv; -#endif -#ifdef USE_EMISSIVEMAP - uniform mat3 emissiveMapTransform; - varying vec2 vEmissiveMapUv; -#endif -#ifdef USE_METALNESSMAP - uniform mat3 metalnessMapTransform; - varying vec2 vMetalnessMapUv; -#endif -#ifdef USE_ROUGHNESSMAP - uniform mat3 roughnessMapTransform; - varying vec2 vRoughnessMapUv; -#endif -#ifdef USE_ANISOTROPYMAP - uniform mat3 anisotropyMapTransform; - varying vec2 vAnisotropyMapUv; -#endif -#ifdef USE_CLEARCOATMAP - uniform mat3 clearcoatMapTransform; - varying vec2 vClearcoatMapUv; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - uniform mat3 clearcoatNormalMapTransform; - varying vec2 vClearcoatNormalMapUv; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - uniform mat3 clearcoatRoughnessMapTransform; - varying vec2 vClearcoatRoughnessMapUv; -#endif -#ifdef USE_SHEEN_COLORMAP - uniform mat3 sheenColorMapTransform; - varying vec2 vSheenColorMapUv; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - uniform mat3 sheenRoughnessMapTransform; - varying vec2 vSheenRoughnessMapUv; -#endif -#ifdef USE_IRIDESCENCEMAP - uniform mat3 iridescenceMapTransform; - varying vec2 vIridescenceMapUv; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - uniform mat3 iridescenceThicknessMapTransform; - varying vec2 vIridescenceThicknessMapUv; -#endif -#ifdef USE_SPECULARMAP - uniform mat3 specularMapTransform; - varying vec2 vSpecularMapUv; -#endif -#ifdef USE_SPECULAR_COLORMAP - uniform mat3 specularColorMapTransform; - varying vec2 vSpecularColorMapUv; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - uniform mat3 specularIntensityMapTransform; - varying vec2 vSpecularIntensityMapUv; -#endif -#ifdef USE_TRANSMISSIONMAP - uniform mat3 transmissionMapTransform; - varying vec2 vTransmissionMapUv; -#endif -#ifdef USE_THICKNESSMAP - uniform mat3 thicknessMapTransform; - varying vec2 vThicknessMapUv; -#endif`,Pd=`#if defined( USE_UV ) || defined( USE_ANISOTROPY ) - vUv = vec3( uv, 1 ).xy; -#endif -#ifdef USE_MAP - vMapUv = ( mapTransform * vec3( MAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ALPHAMAP - vAlphaMapUv = ( alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_LIGHTMAP - vLightMapUv = ( lightMapTransform * vec3( LIGHTMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_AOMAP - vAoMapUv = ( aoMapTransform * vec3( AOMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_BUMPMAP - vBumpMapUv = ( bumpMapTransform * vec3( BUMPMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_NORMALMAP - vNormalMapUv = ( normalMapTransform * vec3( NORMALMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_DISPLACEMENTMAP - vDisplacementMapUv = ( displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_EMISSIVEMAP - vEmissiveMapUv = ( emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_METALNESSMAP - vMetalnessMapUv = ( metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ROUGHNESSMAP - vRoughnessMapUv = ( roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_ANISOTROPYMAP - vAnisotropyMapUv = ( anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOATMAP - vClearcoatMapUv = ( clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOAT_NORMALMAP - vClearcoatNormalMapUv = ( clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_CLEARCOAT_ROUGHNESSMAP - vClearcoatRoughnessMapUv = ( clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_IRIDESCENCEMAP - vIridescenceMapUv = ( iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_IRIDESCENCE_THICKNESSMAP - vIridescenceThicknessMapUv = ( iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SHEEN_COLORMAP - vSheenColorMapUv = ( sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SHEEN_ROUGHNESSMAP - vSheenRoughnessMapUv = ( sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULARMAP - vSpecularMapUv = ( specularMapTransform * vec3( SPECULARMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULAR_COLORMAP - vSpecularColorMapUv = ( specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_SPECULAR_INTENSITYMAP - vSpecularIntensityMapUv = ( specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_TRANSMISSIONMAP - vTransmissionMapUv = ( transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) ).xy; -#endif -#ifdef USE_THICKNESSMAP - vThicknessMapUv = ( thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) ).xy; -#endif`,Dd=`#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined ( USE_SHADOWMAP ) || defined ( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0 - vec4 worldPosition = vec4( transformed, 1.0 ); - #ifdef USE_BATCHING - worldPosition = batchingMatrix * worldPosition; - #endif - #ifdef USE_INSTANCING - worldPosition = instanceMatrix * worldPosition; - #endif - worldPosition = modelMatrix * worldPosition; -#endif`;const Ld=`varying vec2 vUv; -uniform mat3 uvTransform; -void main() { - vUv = ( uvTransform * vec3( uv, 1 ) ).xy; - gl_Position = vec4( position.xy, 1.0, 1.0 ); -}`,Id=`uniform sampler2D t2D; -uniform float backgroundIntensity; -varying vec2 vUv; -void main() { - vec4 texColor = texture2D( t2D, vUv ); - #ifdef DECODE_VIDEO_TEXTURE - texColor = vec4( mix( pow( texColor.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), texColor.rgb * 0.0773993808, vec3( lessThanEqual( texColor.rgb, vec3( 0.04045 ) ) ) ), texColor.w ); - #endif - texColor.rgb *= backgroundIntensity; - gl_FragColor = texColor; - #include - #include -}`,Ud=`varying vec3 vWorldDirection; -#include -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include - #include - gl_Position.z = gl_Position.w; -}`,Nd=`#ifdef ENVMAP_TYPE_CUBE - uniform samplerCube envMap; -#elif defined( ENVMAP_TYPE_CUBE_UV ) - uniform sampler2D envMap; -#endif -uniform float flipEnvMap; -uniform float backgroundBlurriness; -uniform float backgroundIntensity; -uniform mat3 backgroundRotation; -varying vec3 vWorldDirection; -#include -void main() { - #ifdef ENVMAP_TYPE_CUBE - vec4 texColor = textureCube( envMap, backgroundRotation * vec3( flipEnvMap * vWorldDirection.x, vWorldDirection.yz ) ); - #elif defined( ENVMAP_TYPE_CUBE_UV ) - vec4 texColor = textureCubeUV( envMap, backgroundRotation * vWorldDirection, backgroundBlurriness ); - #else - vec4 texColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - #endif - texColor.rgb *= backgroundIntensity; - gl_FragColor = texColor; - #include - #include -}`,Fd=`varying vec3 vWorldDirection; -#include -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include - #include - gl_Position.z = gl_Position.w; -}`,Od=`uniform samplerCube tCube; -uniform float tFlip; -uniform float opacity; -varying vec3 vWorldDirection; -void main() { - vec4 texColor = textureCube( tCube, vec3( tFlip * vWorldDirection.x, vWorldDirection.yz ) ); - gl_FragColor = texColor; - gl_FragColor.a *= opacity; - #include - #include -}`,Bd=`#include -#include -#include -#include -#include -#include -#include -#include -varying vec2 vHighPrecisionZW; -void main() { - #include - #include - #include - #include - #ifdef USE_DISPLACEMENTMAP - #include - #include - #include - #endif - #include - #include - #include - #include - #include - #include - #include - vHighPrecisionZW = gl_Position.zw; -}`,zd=`#if DEPTH_PACKING == 3200 - uniform float opacity; -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -varying vec2 vHighPrecisionZW; -void main() { - vec4 diffuseColor = vec4( 1.0 ); - #include - #if DEPTH_PACKING == 3200 - diffuseColor.a = opacity; - #endif - #include - #include - #include - #include - #include - #ifdef USE_REVERSED_DEPTH_BUFFER - float fragCoordZ = vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ]; - #else - float fragCoordZ = 0.5 * vHighPrecisionZW[ 0 ] / vHighPrecisionZW[ 1 ] + 0.5; - #endif - #if DEPTH_PACKING == 3200 - gl_FragColor = vec4( vec3( 1.0 - fragCoordZ ), opacity ); - #elif DEPTH_PACKING == 3201 - gl_FragColor = packDepthToRGBA( fragCoordZ ); - #elif DEPTH_PACKING == 3202 - gl_FragColor = vec4( packDepthToRGB( fragCoordZ ), 1.0 ); - #elif DEPTH_PACKING == 3203 - gl_FragColor = vec4( packDepthToRG( fragCoordZ ), 0.0, 1.0 ); - #endif -}`,Vd=`#define DISTANCE -varying vec3 vWorldPosition; -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #ifdef USE_DISPLACEMENTMAP - #include - #include - #include - #endif - #include - #include - #include - #include - #include - #include - #include - vWorldPosition = worldPosition.xyz; -}`,Gd=`#define DISTANCE -uniform vec3 referencePosition; -uniform float nearDistance; -uniform float farDistance; -varying vec3 vWorldPosition; -#include -#include -#include -#include -#include -#include -#include -#include -void main () { - vec4 diffuseColor = vec4( 1.0 ); - #include - #include - #include - #include - #include - float dist = length( vWorldPosition - referencePosition ); - dist = ( dist - nearDistance ) / ( farDistance - nearDistance ); - dist = saturate( dist ); - gl_FragColor = packDepthToRGBA( dist ); -}`,kd=`varying vec3 vWorldDirection; -#include -void main() { - vWorldDirection = transformDirection( position, modelMatrix ); - #include - #include -}`,Hd=`uniform sampler2D tEquirect; -varying vec3 vWorldDirection; -#include -void main() { - vec3 direction = normalize( vWorldDirection ); - vec2 sampleUV = equirectUv( direction ); - gl_FragColor = texture2D( tEquirect, sampleUV ); - #include - #include -}`,Wd=`uniform float scale; -attribute float lineDistance; -varying float vLineDistance; -#include -#include -#include -#include -#include -#include -#include -void main() { - vLineDistance = scale * lineDistance; - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -}`,Xd=`uniform vec3 diffuse; -uniform float opacity; -uniform float dashSize; -uniform float totalSize; -varying float vLineDistance; -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - if ( mod( vLineDistance, totalSize ) > dashSize ) { - discard; - } - vec3 outgoingLight = vec3( 0.0 ); - #include - #include - #include - outgoingLight = diffuseColor.rgb; - #include - #include - #include - #include - #include -}`,qd=`#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #if defined ( USE_ENVMAP ) || defined ( USE_SKINNING ) - #include - #include - #include - #include - #include - #endif - #include - #include - #include - #include - #include - #include - #include - #include - #include -}`,Yd=`uniform vec3 diffuse; -uniform float opacity; -#ifndef FLAT_SHADED - varying vec3 vNormal; -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - #include - #include - #include - #include - #include - #include - #include - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - #ifdef USE_LIGHTMAP - vec4 lightMapTexel = texture2D( lightMap, vLightMapUv ); - reflectedLight.indirectDiffuse += lightMapTexel.rgb * lightMapIntensity * RECIPROCAL_PI; - #else - reflectedLight.indirectDiffuse += vec3( 1.0 ); - #endif - #include - reflectedLight.indirectDiffuse *= diffuseColor.rgb; - vec3 outgoingLight = reflectedLight.indirectDiffuse; - #include - #include - #include - #include - #include - #include - #include -}`,Zd=`#define LAMBERT -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vViewPosition = - mvPosition.xyz; - #include - #include - #include - #include -}`,Kd=`#define LAMBERT -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; - #include - #include - #include - #include - #include - #include - #include -}`,jd=`#define MATCAP -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vViewPosition = - mvPosition.xyz; -}`,Jd=`#define MATCAP -uniform vec3 diffuse; -uniform float opacity; -uniform sampler2D matcap; -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - #include - #include - #include - #include - #include - #include - #include - #include - vec3 viewDir = normalize( vViewPosition ); - vec3 x = normalize( vec3( viewDir.z, 0.0, - viewDir.x ) ); - vec3 y = cross( viewDir, x ); - vec2 uv = vec2( dot( x, normal ), dot( y, normal ) ) * 0.495 + 0.5; - #ifdef USE_MATCAP - vec4 matcapColor = texture2D( matcap, uv ); - #else - vec4 matcapColor = vec4( vec3( mix( 0.2, 0.8, uv.y ) ), 1.0 ); - #endif - vec3 outgoingLight = diffuseColor.rgb * matcapColor.rgb; - #include - #include - #include - #include - #include - #include -}`,$d=`#define NORMAL -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - varying vec3 vViewPosition; -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - vViewPosition = - mvPosition.xyz; -#endif -}`,Qd=`#define NORMAL -uniform float opacity; -#if defined( FLAT_SHADED ) || defined( USE_BUMPMAP ) || defined( USE_NORMALMAP_TANGENTSPACE ) - varying vec3 vViewPosition; -#endif -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( 0.0, 0.0, 0.0, opacity ); - #include - #include - #include - #include - gl_FragColor = vec4( packNormalToRGB( normal ), diffuseColor.a ); - #ifdef OPAQUE - gl_FragColor.a = 1.0; - #endif -}`,tp=`#define PHONG -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vViewPosition = - mvPosition.xyz; - #include - #include - #include - #include -}`,ep=`#define PHONG -uniform vec3 diffuse; -uniform vec3 emissive; -uniform vec3 specular; -uniform float shininess; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveRadiance; - #include - #include - #include - #include - #include - #include - #include -}`,np=`#define STANDARD -varying vec3 vViewPosition; -#ifdef USE_TRANSMISSION - varying vec3 vWorldPosition; -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vViewPosition = - mvPosition.xyz; - #include - #include - #include -#ifdef USE_TRANSMISSION - vWorldPosition = worldPosition.xyz; -#endif -}`,ip=`#define STANDARD -#ifdef PHYSICAL - #define IOR - #define USE_SPECULAR -#endif -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float roughness; -uniform float metalness; -uniform float opacity; -#ifdef IOR - uniform float ior; -#endif -#ifdef USE_SPECULAR - uniform float specularIntensity; - uniform vec3 specularColor; - #ifdef USE_SPECULAR_COLORMAP - uniform sampler2D specularColorMap; - #endif - #ifdef USE_SPECULAR_INTENSITYMAP - uniform sampler2D specularIntensityMap; - #endif -#endif -#ifdef USE_CLEARCOAT - uniform float clearcoat; - uniform float clearcoatRoughness; -#endif -#ifdef USE_DISPERSION - uniform float dispersion; -#endif -#ifdef USE_IRIDESCENCE - uniform float iridescence; - uniform float iridescenceIOR; - uniform float iridescenceThicknessMinimum; - uniform float iridescenceThicknessMaximum; -#endif -#ifdef USE_SHEEN - uniform vec3 sheenColor; - uniform float sheenRoughness; - #ifdef USE_SHEEN_COLORMAP - uniform sampler2D sheenColorMap; - #endif - #ifdef USE_SHEEN_ROUGHNESSMAP - uniform sampler2D sheenRoughnessMap; - #endif -#endif -#ifdef USE_ANISOTROPY - uniform vec2 anisotropyVector; - #ifdef USE_ANISOTROPYMAP - uniform sampler2D anisotropyMap; - #endif -#endif -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; - vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; - #include - vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; - #ifdef USE_SHEEN - float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor ); - outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecularDirect + sheenSpecularIndirect; - #endif - #ifdef USE_CLEARCOAT - float dotNVcc = saturate( dot( geometryClearcoatNormal, geometryViewDir ) ); - vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); - outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + ( clearcoatSpecularDirect + clearcoatSpecularIndirect ) * material.clearcoat; - #endif - #include - #include - #include - #include - #include - #include -}`,sp=`#define TOON -varying vec3 vViewPosition; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vViewPosition = - mvPosition.xyz; - #include - #include - #include -}`,rp=`#define TOON -uniform vec3 diffuse; -uniform vec3 emissive; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) ); - vec3 totalEmissiveRadiance = emissive; - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + totalEmissiveRadiance; - #include - #include - #include - #include - #include - #include -}`,ap=`uniform float size; -uniform float scale; -#include -#include -#include -#include -#include -#include -#ifdef USE_POINTS_UV - varying vec2 vUv; - uniform mat3 uvTransform; -#endif -void main() { - #ifdef USE_POINTS_UV - vUv = ( uvTransform * vec3( uv, 1 ) ).xy; - #endif - #include - #include - #include - #include - #include - #include - gl_PointSize = size; - #ifdef USE_SIZEATTENUATION - bool isPerspective = isPerspectiveMatrix( projectionMatrix ); - if ( isPerspective ) gl_PointSize *= ( scale / - mvPosition.z ); - #endif - #include - #include - #include - #include -}`,op=`uniform vec3 diffuse; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - vec3 outgoingLight = vec3( 0.0 ); - #include - #include - #include - #include - #include - outgoingLight = diffuseColor.rgb; - #include - #include - #include - #include - #include -}`,cp=`#include -#include -#include -#include -#include -#include -#include -void main() { - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include -}`,lp=`uniform vec3 color; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - #include - gl_FragColor = vec4( color, opacity * ( 1.0 - getShadowMask() ) ); - #include - #include - #include -}`,hp=`uniform float rotation; -uniform vec2 center; -#include -#include -#include -#include -#include -void main() { - #include - vec4 mvPosition = modelViewMatrix[ 3 ]; - vec2 scale = vec2( length( modelMatrix[ 0 ].xyz ), length( modelMatrix[ 1 ].xyz ) ); - #ifndef USE_SIZEATTENUATION - bool isPerspective = isPerspectiveMatrix( projectionMatrix ); - if ( isPerspective ) scale *= - mvPosition.z; - #endif - vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale; - vec2 rotatedPosition; - rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y; - rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y; - mvPosition.xy += rotatedPosition; - gl_Position = projectionMatrix * mvPosition; - #include - #include - #include -}`,up=`uniform vec3 diffuse; -uniform float opacity; -#include -#include -#include -#include -#include -#include -#include -#include -#include -void main() { - vec4 diffuseColor = vec4( diffuse, opacity ); - #include - vec3 outgoingLight = vec3( 0.0 ); - #include - #include - #include - #include - #include - outgoingLight = diffuseColor.rgb; - #include - #include - #include - #include -}`,Wt={alphahash_fragment:Lu,alphahash_pars_fragment:Iu,alphamap_fragment:Uu,alphamap_pars_fragment:Nu,alphatest_fragment:Fu,alphatest_pars_fragment:Ou,aomap_fragment:Bu,aomap_pars_fragment:zu,batching_pars_vertex:Vu,batching_vertex:Gu,begin_vertex:ku,beginnormal_vertex:Hu,bsdfs:Wu,iridescence_fragment:Xu,bumpmap_pars_fragment:qu,clipping_planes_fragment:Yu,clipping_planes_pars_fragment:Zu,clipping_planes_pars_vertex:Ku,clipping_planes_vertex:ju,color_fragment:Ju,color_pars_fragment:$u,color_pars_vertex:Qu,color_vertex:tf,common:ef,cube_uv_reflection_fragment:nf,defaultnormal_vertex:sf,displacementmap_pars_vertex:rf,displacementmap_vertex:af,emissivemap_fragment:of,emissivemap_pars_fragment:cf,colorspace_fragment:lf,colorspace_pars_fragment:hf,envmap_fragment:uf,envmap_common_pars_fragment:ff,envmap_pars_fragment:df,envmap_pars_vertex:pf,envmap_physical_pars_fragment:Tf,envmap_vertex:mf,fog_vertex:xf,fog_pars_vertex:gf,fog_fragment:_f,fog_pars_fragment:vf,gradientmap_pars_fragment:Mf,lightmap_pars_fragment:Sf,lights_lambert_fragment:yf,lights_lambert_pars_fragment:bf,lights_pars_begin:Ef,lights_toon_fragment:Af,lights_toon_pars_fragment:wf,lights_phong_fragment:Rf,lights_phong_pars_fragment:Cf,lights_physical_fragment:Pf,lights_physical_pars_fragment:Df,lights_fragment_begin:Lf,lights_fragment_maps:If,lights_fragment_end:Uf,logdepthbuf_fragment:Nf,logdepthbuf_pars_fragment:Ff,logdepthbuf_pars_vertex:Of,logdepthbuf_vertex:Bf,map_fragment:zf,map_pars_fragment:Vf,map_particle_fragment:Gf,map_particle_pars_fragment:kf,metalnessmap_fragment:Hf,metalnessmap_pars_fragment:Wf,morphinstance_vertex:Xf,morphcolor_vertex:qf,morphnormal_vertex:Yf,morphtarget_pars_vertex:Zf,morphtarget_vertex:Kf,normal_fragment_begin:jf,normal_fragment_maps:Jf,normal_pars_fragment:$f,normal_pars_vertex:Qf,normal_vertex:td,normalmap_pars_fragment:ed,clearcoat_normal_fragment_begin:nd,clearcoat_normal_fragment_maps:id,clearcoat_pars_fragment:sd,iridescence_pars_fragment:rd,opaque_fragment:ad,packing:od,premultiplied_alpha_fragment:cd,project_vertex:ld,dithering_fragment:hd,dithering_pars_fragment:ud,roughnessmap_fragment:fd,roughnessmap_pars_fragment:dd,shadowmap_pars_fragment:pd,shadowmap_pars_vertex:md,shadowmap_vertex:xd,shadowmask_pars_fragment:gd,skinbase_vertex:_d,skinning_pars_vertex:vd,skinning_vertex:Md,skinnormal_vertex:Sd,specularmap_fragment:yd,specularmap_pars_fragment:bd,tonemapping_fragment:Ed,tonemapping_pars_fragment:Td,transmission_fragment:Ad,transmission_pars_fragment:wd,uv_pars_fragment:Rd,uv_pars_vertex:Cd,uv_vertex:Pd,worldpos_vertex:Dd,background_vert:Ld,background_frag:Id,backgroundCube_vert:Ud,backgroundCube_frag:Nd,cube_vert:Fd,cube_frag:Od,depth_vert:Bd,depth_frag:zd,distanceRGBA_vert:Vd,distanceRGBA_frag:Gd,equirect_vert:kd,equirect_frag:Hd,linedashed_vert:Wd,linedashed_frag:Xd,meshbasic_vert:qd,meshbasic_frag:Yd,meshlambert_vert:Zd,meshlambert_frag:Kd,meshmatcap_vert:jd,meshmatcap_frag:Jd,meshnormal_vert:$d,meshnormal_frag:Qd,meshphong_vert:tp,meshphong_frag:ep,meshphysical_vert:np,meshphysical_frag:ip,meshtoon_vert:sp,meshtoon_frag:rp,points_vert:ap,points_frag:op,shadow_vert:cp,shadow_frag:lp,sprite_vert:hp,sprite_frag:up},pt={common:{diffuse:{value:new Yt(16777215)},opacity:{value:1},map:{value:null},mapTransform:{value:new Ht},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0}},specularmap:{specularMap:{value:null},specularMapTransform:{value:new Ht}},envmap:{envMap:{value:null},envMapRotation:{value:new Ht},flipEnvMap:{value:-1},reflectivity:{value:1},ior:{value:1.5},refractionRatio:{value:.98},dfgLUT:{value:null}},aomap:{aoMap:{value:null},aoMapIntensity:{value:1},aoMapTransform:{value:new Ht}},lightmap:{lightMap:{value:null},lightMapIntensity:{value:1},lightMapTransform:{value:new Ht}},bumpmap:{bumpMap:{value:null},bumpMapTransform:{value:new Ht},bumpScale:{value:1}},normalmap:{normalMap:{value:null},normalMapTransform:{value:new Ht},normalScale:{value:new at(1,1)}},displacementmap:{displacementMap:{value:null},displacementMapTransform:{value:new Ht},displacementScale:{value:1},displacementBias:{value:0}},emissivemap:{emissiveMap:{value:null},emissiveMapTransform:{value:new Ht}},metalnessmap:{metalnessMap:{value:null},metalnessMapTransform:{value:new Ht}},roughnessmap:{roughnessMap:{value:null},roughnessMapTransform:{value:new Ht}},gradientmap:{gradientMap:{value:null}},fog:{fogDensity:{value:25e-5},fogNear:{value:1},fogFar:{value:2e3},fogColor:{value:new Yt(16777215)}},lights:{ambientLightColor:{value:[]},lightProbe:{value:[]},directionalLights:{value:[],properties:{direction:{},color:{}}},directionalLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},directionalShadowMap:{value:[]},directionalShadowMatrix:{value:[]},spotLights:{value:[],properties:{color:{},position:{},direction:{},distance:{},coneCos:{},penumbraCos:{},decay:{}}},spotLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{}}},spotLightMap:{value:[]},spotShadowMap:{value:[]},spotLightMatrix:{value:[]},pointLights:{value:[],properties:{color:{},position:{},decay:{},distance:{}}},pointLightShadows:{value:[],properties:{shadowIntensity:1,shadowBias:{},shadowNormalBias:{},shadowRadius:{},shadowMapSize:{},shadowCameraNear:{},shadowCameraFar:{}}},pointShadowMap:{value:[]},pointShadowMatrix:{value:[]},hemisphereLights:{value:[],properties:{direction:{},skyColor:{},groundColor:{}}},rectAreaLights:{value:[],properties:{color:{},position:{},width:{},height:{}}},ltc_1:{value:null},ltc_2:{value:null}},points:{diffuse:{value:new Yt(16777215)},opacity:{value:1},size:{value:1},scale:{value:1},map:{value:null},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0},uvTransform:{value:new Ht}},sprite:{diffuse:{value:new Yt(16777215)},opacity:{value:1},center:{value:new at(.5,.5)},rotation:{value:0},map:{value:null},mapTransform:{value:new Ht},alphaMap:{value:null},alphaMapTransform:{value:new Ht},alphaTest:{value:0}}},an={basic:{uniforms:Pe([pt.common,pt.specularmap,pt.envmap,pt.aomap,pt.lightmap,pt.fog]),vertexShader:Wt.meshbasic_vert,fragmentShader:Wt.meshbasic_frag},lambert:{uniforms:Pe([pt.common,pt.specularmap,pt.envmap,pt.aomap,pt.lightmap,pt.emissivemap,pt.bumpmap,pt.normalmap,pt.displacementmap,pt.fog,pt.lights,{emissive:{value:new Yt(0)}}]),vertexShader:Wt.meshlambert_vert,fragmentShader:Wt.meshlambert_frag},phong:{uniforms:Pe([pt.common,pt.specularmap,pt.envmap,pt.aomap,pt.lightmap,pt.emissivemap,pt.bumpmap,pt.normalmap,pt.displacementmap,pt.fog,pt.lights,{emissive:{value:new Yt(0)},specular:{value:new Yt(1118481)},shininess:{value:30}}]),vertexShader:Wt.meshphong_vert,fragmentShader:Wt.meshphong_frag},standard:{uniforms:Pe([pt.common,pt.envmap,pt.aomap,pt.lightmap,pt.emissivemap,pt.bumpmap,pt.normalmap,pt.displacementmap,pt.roughnessmap,pt.metalnessmap,pt.fog,pt.lights,{emissive:{value:new Yt(0)},roughness:{value:1},metalness:{value:0},envMapIntensity:{value:1}}]),vertexShader:Wt.meshphysical_vert,fragmentShader:Wt.meshphysical_frag},toon:{uniforms:Pe([pt.common,pt.aomap,pt.lightmap,pt.emissivemap,pt.bumpmap,pt.normalmap,pt.displacementmap,pt.gradientmap,pt.fog,pt.lights,{emissive:{value:new Yt(0)}}]),vertexShader:Wt.meshtoon_vert,fragmentShader:Wt.meshtoon_frag},matcap:{uniforms:Pe([pt.common,pt.bumpmap,pt.normalmap,pt.displacementmap,pt.fog,{matcap:{value:null}}]),vertexShader:Wt.meshmatcap_vert,fragmentShader:Wt.meshmatcap_frag},points:{uniforms:Pe([pt.points,pt.fog]),vertexShader:Wt.points_vert,fragmentShader:Wt.points_frag},dashed:{uniforms:Pe([pt.common,pt.fog,{scale:{value:1},dashSize:{value:1},totalSize:{value:2}}]),vertexShader:Wt.linedashed_vert,fragmentShader:Wt.linedashed_frag},depth:{uniforms:Pe([pt.common,pt.displacementmap]),vertexShader:Wt.depth_vert,fragmentShader:Wt.depth_frag},normal:{uniforms:Pe([pt.common,pt.bumpmap,pt.normalmap,pt.displacementmap,{opacity:{value:1}}]),vertexShader:Wt.meshnormal_vert,fragmentShader:Wt.meshnormal_frag},sprite:{uniforms:Pe([pt.sprite,pt.fog]),vertexShader:Wt.sprite_vert,fragmentShader:Wt.sprite_frag},background:{uniforms:{uvTransform:{value:new Ht},t2D:{value:null},backgroundIntensity:{value:1}},vertexShader:Wt.background_vert,fragmentShader:Wt.background_frag},backgroundCube:{uniforms:{envMap:{value:null},flipEnvMap:{value:-1},backgroundBlurriness:{value:0},backgroundIntensity:{value:1},backgroundRotation:{value:new Ht}},vertexShader:Wt.backgroundCube_vert,fragmentShader:Wt.backgroundCube_frag},cube:{uniforms:{tCube:{value:null},tFlip:{value:-1},opacity:{value:1}},vertexShader:Wt.cube_vert,fragmentShader:Wt.cube_frag},equirect:{uniforms:{tEquirect:{value:null}},vertexShader:Wt.equirect_vert,fragmentShader:Wt.equirect_frag},distanceRGBA:{uniforms:Pe([pt.common,pt.displacementmap,{referencePosition:{value:new D},nearDistance:{value:1},farDistance:{value:1e3}}]),vertexShader:Wt.distanceRGBA_vert,fragmentShader:Wt.distanceRGBA_frag},shadow:{uniforms:Pe([pt.lights,pt.fog,{color:{value:new Yt(0)},opacity:{value:1}}]),vertexShader:Wt.shadow_vert,fragmentShader:Wt.shadow_frag}};an.physical={uniforms:Pe([an.standard.uniforms,{clearcoat:{value:0},clearcoatMap:{value:null},clearcoatMapTransform:{value:new Ht},clearcoatNormalMap:{value:null},clearcoatNormalMapTransform:{value:new Ht},clearcoatNormalScale:{value:new at(1,1)},clearcoatRoughness:{value:0},clearcoatRoughnessMap:{value:null},clearcoatRoughnessMapTransform:{value:new Ht},dispersion:{value:0},iridescence:{value:0},iridescenceMap:{value:null},iridescenceMapTransform:{value:new Ht},iridescenceIOR:{value:1.3},iridescenceThicknessMinimum:{value:100},iridescenceThicknessMaximum:{value:400},iridescenceThicknessMap:{value:null},iridescenceThicknessMapTransform:{value:new Ht},sheen:{value:0},sheenColor:{value:new Yt(0)},sheenColorMap:{value:null},sheenColorMapTransform:{value:new Ht},sheenRoughness:{value:1},sheenRoughnessMap:{value:null},sheenRoughnessMapTransform:{value:new Ht},transmission:{value:0},transmissionMap:{value:null},transmissionMapTransform:{value:new Ht},transmissionSamplerSize:{value:new at},transmissionSamplerMap:{value:null},thickness:{value:0},thicknessMap:{value:null},thicknessMapTransform:{value:new Ht},attenuationDistance:{value:0},attenuationColor:{value:new Yt(0)},specularColor:{value:new Yt(1,1,1)},specularColorMap:{value:null},specularColorMapTransform:{value:new Ht},specularIntensity:{value:1},specularIntensityMap:{value:null},specularIntensityMapTransform:{value:new Ht},anisotropyVector:{value:new at},anisotropyMap:{value:null},anisotropyMapTransform:{value:new Ht}}]),vertexShader:Wt.meshphysical_vert,fragmentShader:Wt.meshphysical_frag};const Ns={r:0,b:0,g:0},Wn=new nn,fp=new re;function dp(i,t,e,n,s,r,a){const o=new Yt(0);let l=r===!0?0:1,c,h,u=null,d=0,p=null;function x(S){let T=S.isScene===!0?S.background:null;return T&&T.isTexture&&(T=(S.backgroundBlurriness>0?e:t).get(T)),T}function _(S){let T=!1;const R=x(S);R===null?f(o,l):R&&R.isColor&&(f(R,1),T=!0);const w=i.xr.getEnvironmentBlendMode();w==="additive"?n.buffers.color.setClear(0,0,0,1,a):w==="alpha-blend"&&n.buffers.color.setClear(0,0,0,0,a),(i.autoClear||T)&&(n.buffers.depth.setTest(!0),n.buffers.depth.setMask(!0),n.buffers.color.setMask(!0),i.clear(i.autoClearColor,i.autoClearDepth,i.autoClearStencil))}function m(S,T){const R=x(T);R&&(R.isCubeTexture||R.mapping===Js)?(h===void 0&&(h=new Vt(new oe(1,1,1),new En({name:"BackgroundCubeMaterial",uniforms:Ci(an.backgroundCube.uniforms),vertexShader:an.backgroundCube.vertexShader,fragmentShader:an.backgroundCube.fragmentShader,side:Le,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),h.geometry.deleteAttribute("normal"),h.geometry.deleteAttribute("uv"),h.onBeforeRender=function(w,L,F){this.matrixWorld.copyPosition(F.matrixWorld)},Object.defineProperty(h.material,"envMap",{get:function(){return this.uniforms.envMap.value}}),s.update(h)),Wn.copy(T.backgroundRotation),Wn.x*=-1,Wn.y*=-1,Wn.z*=-1,R.isCubeTexture&&R.isRenderTargetTexture===!1&&(Wn.y*=-1,Wn.z*=-1),h.material.uniforms.envMap.value=R,h.material.uniforms.flipEnvMap.value=R.isCubeTexture&&R.isRenderTargetTexture===!1?-1:1,h.material.uniforms.backgroundBlurriness.value=T.backgroundBlurriness,h.material.uniforms.backgroundIntensity.value=T.backgroundIntensity,h.material.uniforms.backgroundRotation.value.setFromMatrix4(fp.makeRotationFromEuler(Wn)),h.material.toneMapped=$t.getTransfer(R.colorSpace)!==ne,(u!==R||d!==R.version||p!==i.toneMapping)&&(h.material.needsUpdate=!0,u=R,d=R.version,p=i.toneMapping),h.layers.enableAll(),S.unshift(h,h.geometry,h.material,0,0,null)):R&&R.isTexture&&(c===void 0&&(c=new Vt(new Nn(2,2),new En({name:"BackgroundMaterial",uniforms:Ci(an.background.uniforms),vertexShader:an.background.vertexShader,fragmentShader:an.background.fragmentShader,side:Fn,depthTest:!1,depthWrite:!1,fog:!1,allowOverride:!1})),c.geometry.deleteAttribute("normal"),Object.defineProperty(c.material,"map",{get:function(){return this.uniforms.t2D.value}}),s.update(c)),c.material.uniforms.t2D.value=R,c.material.uniforms.backgroundIntensity.value=T.backgroundIntensity,c.material.toneMapped=$t.getTransfer(R.colorSpace)!==ne,R.matrixAutoUpdate===!0&&R.updateMatrix(),c.material.uniforms.uvTransform.value.copy(R.matrix),(u!==R||d!==R.version||p!==i.toneMapping)&&(c.material.needsUpdate=!0,u=R,d=R.version,p=i.toneMapping),c.layers.enableAll(),S.unshift(c,c.geometry,c.material,0,0,null))}function f(S,T){S.getRGB(Ns,zc(i)),n.buffers.color.setClear(Ns.r,Ns.g,Ns.b,T,a)}function b(){h!==void 0&&(h.geometry.dispose(),h.material.dispose(),h=void 0),c!==void 0&&(c.geometry.dispose(),c.material.dispose(),c=void 0)}return{getClearColor:function(){return o},setClearColor:function(S,T=1){o.set(S),l=T,f(o,l)},getClearAlpha:function(){return l},setClearAlpha:function(S){l=S,f(o,l)},render:_,addToRenderList:m,dispose:b}}function pp(i,t){const e=i.getParameter(i.MAX_VERTEX_ATTRIBS),n={},s=d(null);let r=s,a=!1;function o(M,P,O,z,k){let W=!1;const Y=u(z,O,P);r!==Y&&(r=Y,c(r.object)),W=p(M,z,O,k),W&&x(M,z,O,k),k!==null&&t.update(k,i.ELEMENT_ARRAY_BUFFER),(W||a)&&(a=!1,T(M,P,O,z),k!==null&&i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,t.get(k).buffer))}function l(){return i.createVertexArray()}function c(M){return i.bindVertexArray(M)}function h(M){return i.deleteVertexArray(M)}function u(M,P,O){const z=O.wireframe===!0;let k=n[M.id];k===void 0&&(k={},n[M.id]=k);let W=k[P.id];W===void 0&&(W={},k[P.id]=W);let Y=W[z];return Y===void 0&&(Y=d(l()),W[z]=Y),Y}function d(M){const P=[],O=[],z=[];for(let k=0;k=0){const st=k[X];let St=W[X];if(St===void 0&&(X==="instanceMatrix"&&M.instanceMatrix&&(St=M.instanceMatrix),X==="instanceColor"&&M.instanceColor&&(St=M.instanceColor)),st===void 0||st.attribute!==St||St&&st.data!==St.data)return!0;Y++}return r.attributesNum!==Y||r.index!==z}function x(M,P,O,z){const k={},W=P.attributes;let Y=0;const nt=O.getAttributes();for(const X in nt)if(nt[X].location>=0){let st=W[X];st===void 0&&(X==="instanceMatrix"&&M.instanceMatrix&&(st=M.instanceMatrix),X==="instanceColor"&&M.instanceColor&&(st=M.instanceColor));const St={};St.attribute=st,st&&st.data&&(St.data=st.data),k[X]=St,Y++}r.attributes=k,r.attributesNum=Y,r.index=z}function _(){const M=r.newAttributes;for(let P=0,O=M.length;P=0){let rt=k[nt];if(rt===void 0&&(nt==="instanceMatrix"&&M.instanceMatrix&&(rt=M.instanceMatrix),nt==="instanceColor"&&M.instanceColor&&(rt=M.instanceColor)),rt!==void 0){const st=rt.normalized,St=rt.itemSize,Gt=t.get(rt);if(Gt===void 0)continue;const qt=Gt.buffer,jt=Gt.type,Kt=Gt.bytesPerElement,K=jt===i.INT||jt===i.UNSIGNED_INT||rt.gpuType===Oa;if(rt.isInterleavedBufferAttribute){const J=rt.data,xt=J.stride,Rt=rt.offset;if(J.isInstancedInterleavedBuffer){for(let Tt=0;Tt0&&i.getShaderPrecisionFormat(i.FRAGMENT_SHADER,i.HIGH_FLOAT).precision>0)return"highp";L="mediump"}return L==="mediump"&&i.getShaderPrecisionFormat(i.VERTEX_SHADER,i.MEDIUM_FLOAT).precision>0&&i.getShaderPrecisionFormat(i.FRAGMENT_SHADER,i.MEDIUM_FLOAT).precision>0?"mediump":"lowp"}let c=e.precision!==void 0?e.precision:"highp";const h=l(c);h!==c&&(Bt("WebGLRenderer:",c,"not supported, using",h,"instead."),c=h);const u=e.logarithmicDepthBuffer===!0,d=e.reversedDepthBuffer===!0&&t.has("EXT_clip_control"),p=i.getParameter(i.MAX_TEXTURE_IMAGE_UNITS),x=i.getParameter(i.MAX_VERTEX_TEXTURE_IMAGE_UNITS),_=i.getParameter(i.MAX_TEXTURE_SIZE),m=i.getParameter(i.MAX_CUBE_MAP_TEXTURE_SIZE),f=i.getParameter(i.MAX_VERTEX_ATTRIBS),b=i.getParameter(i.MAX_VERTEX_UNIFORM_VECTORS),S=i.getParameter(i.MAX_VARYING_VECTORS),T=i.getParameter(i.MAX_FRAGMENT_UNIFORM_VECTORS),R=x>0,w=i.getParameter(i.MAX_SAMPLES);return{isWebGL2:!0,getMaxAnisotropy:r,getMaxPrecision:l,textureFormatReadable:a,textureTypeReadable:o,precision:c,logarithmicDepthBuffer:u,reversedDepthBuffer:d,maxTextures:p,maxVertexTextures:x,maxTextureSize:_,maxCubemapSize:m,maxAttributes:f,maxVertexUniforms:b,maxVaryings:S,maxFragmentUniforms:T,vertexTextures:R,maxSamples:w}}function gp(i){const t=this;let e=null,n=0,s=!1,r=!1;const a=new Dn,o=new Ht,l={value:null,needsUpdate:!1};this.uniform=l,this.numPlanes=0,this.numIntersection=0,this.init=function(u,d){const p=u.length!==0||d||n!==0||s;return s=d,n=u.length,p},this.beginShadows=function(){r=!0,h(null)},this.endShadows=function(){r=!1},this.setGlobalState=function(u,d){e=h(u,d,0)},this.setState=function(u,d,p){const x=u.clippingPlanes,_=u.clipIntersection,m=u.clipShadows,f=i.get(u);if(!s||x===null||x.length===0||r&&!m)r?h(null):c();else{const b=r?0:n,S=b*4;let T=f.clippingState||null;l.value=T,T=h(x,d,S,p);for(let R=0;R!==S;++R)T[R]=e[R];f.clippingState=T,this.numIntersection=_?this.numPlanes:0,this.numPlanes+=b}};function c(){l.value!==e&&(l.value=e,l.needsUpdate=n>0),t.numPlanes=n,t.numIntersection=0}function h(u,d,p,x){const _=u!==null?u.length:0;let m=null;if(_!==0){if(m=l.value,x!==!0||m===null){const f=p+_*4,b=d.matrixWorldInverse;o.getNormalMatrix(b),(m===null||m.length0){const c=new Ph(l.height);return c.fromEquirectangularTexture(i,a),t.set(a,c),a.addEventListener("dispose",s),e(c.texture,a.mapping)}else return null}}return a}function s(a){const o=a.target;o.removeEventListener("dispose",s);const l=t.get(o);l!==void 0&&(t.delete(o),l.dispose())}function r(){t=new WeakMap}return{get:n,dispose:r}}const In=4,$o=[.125,.215,.35,.446,.526,.582],Zn=20,vp=256,Xi=new nl,Qo=new Yt;let Ir=null,Ur=0,Nr=0,Fr=!1;const Mp=new D;class Ia{constructor(t){this._renderer=t,this._pingPongRenderTarget=null,this._lodMax=0,this._cubeSize=0,this._sizeLods=[],this._sigmas=[],this._lodMeshes=[],this._backgroundBox=null,this._cubemapMaterial=null,this._equirectMaterial=null,this._blurMaterial=null,this._ggxMaterial=null}fromScene(t,e=0,n=.1,s=100,r={}){const{size:a=256,position:o=Mp}=r;Ir=this._renderer.getRenderTarget(),Ur=this._renderer.getActiveCubeFace(),Nr=this._renderer.getActiveMipmapLevel(),Fr=this._renderer.xr.enabled,this._renderer.xr.enabled=!1,this._setSize(a);const l=this._allocateTargets();return l.depthBuffer=!0,this._sceneToCubeUV(t,n,s,l,o),e>0&&this._blur(l,0,0,e),this._applyPMREM(l),this._cleanup(l),l}fromEquirectangular(t,e=null){return this._fromTexture(t,e)}fromCubemap(t,e=null){return this._fromTexture(t,e)}compileCubemapShader(){this._cubemapMaterial===null&&(this._cubemapMaterial=nc(),this._compileMaterial(this._cubemapMaterial))}compileEquirectangularShader(){this._equirectMaterial===null&&(this._equirectMaterial=ec(),this._compileMaterial(this._equirectMaterial))}dispose(){this._dispose(),this._cubemapMaterial!==null&&this._cubemapMaterial.dispose(),this._equirectMaterial!==null&&this._equirectMaterial.dispose(),this._backgroundBox!==null&&(this._backgroundBox.geometry.dispose(),this._backgroundBox.material.dispose())}_setSize(t){this._lodMax=Math.floor(Math.log2(t)),this._cubeSize=Math.pow(2,this._lodMax)}_dispose(){this._blurMaterial!==null&&this._blurMaterial.dispose(),this._ggxMaterial!==null&&this._ggxMaterial.dispose(),this._pingPongRenderTarget!==null&&this._pingPongRenderTarget.dispose();for(let t=0;t2?R:0,R,R),u.setRenderTarget(s),f&&u.render(_,l),u.render(t,l)}u.toneMapping=p,u.autoClear=d,t.background=b}_textureToCubeUV(t,e){const n=this._renderer,s=t.mapping===Ai||t.mapping===wi;s?(this._cubemapMaterial===null&&(this._cubemapMaterial=nc()),this._cubemapMaterial.uniforms.flipEnvMap.value=t.isRenderTargetTexture===!1?-1:1):this._equirectMaterial===null&&(this._equirectMaterial=ec());const r=s?this._cubemapMaterial:this._equirectMaterial,a=this._lodMeshes[0];a.material=r;const o=r.uniforms;o.envMap.value=t;const l=this._cubeSize;gi(e,0,0,3*l,2*l),n.setRenderTarget(e),n.render(a,Xi)}_applyPMREM(t){const e=this._renderer,n=e.autoClear;e.autoClear=!1;const s=this._lodMeshes.length;for(let r=1;rx-In?n-x+In:0),f=4*(this._cubeSize-_);l.envMap.value=t.texture,l.roughness.value=p,l.mipInt.value=x-e,gi(r,m,f,3*_,2*_),s.setRenderTarget(r),s.render(o,Xi),l.envMap.value=r.texture,l.roughness.value=0,l.mipInt.value=x-n,gi(t,m,f,3*_,2*_),s.setRenderTarget(t),s.render(o,Xi)}_blur(t,e,n,s,r){const a=this._pingPongRenderTarget;this._halfBlur(t,a,e,n,s,"latitudinal",r),this._halfBlur(a,t,n,n,s,"longitudinal",r)}_halfBlur(t,e,n,s,r,a,o){const l=this._renderer,c=this._blurMaterial;a!=="latitudinal"&&a!=="longitudinal"&&de("blur direction must be either latitudinal or longitudinal!");const h=3,u=this._lodMeshes[s];u.material=c;const d=c.uniforms,p=this._sizeLods[n]-1,x=isFinite(r)?Math.PI/(2*p):2*Math.PI/(2*Zn-1),_=r/x,m=isFinite(r)?1+Math.floor(h*_):Zn;m>Zn&&Bt(`sigmaRadians, ${r}, is too large and will clip, as it requested ${m} samples when the maximum is set to ${Zn}`);const f=[];let b=0;for(let L=0;LS-In?s-S+In:0),w=4*(this._cubeSize-T);gi(e,R,w,3*T,2*T),l.setRenderTarget(e),l.render(u,Xi)}}function Sp(i){const t=[],e=[],n=[];let s=i;const r=i-In+1+$o.length;for(let a=0;ai-In?l=$o[a-i+In-1]:a===0&&(l=0),e.push(l);const c=1/(o-2),h=-c,u=1+c,d=[h,h,u,h,u,u,h,h,u,u,h,u],p=6,x=6,_=3,m=2,f=1,b=new Float32Array(_*x*p),S=new Float32Array(m*x*p),T=new Float32Array(f*x*p);for(let w=0;w2?0:-1,E=[L,F,0,L+2/3,F,0,L+2/3,F+1,0,L,F,0,L+2/3,F+1,0,L,F+1,0];b.set(E,_*x*w),S.set(d,m*x*w);const M=[w,w,w,w,w,w];T.set(M,f*x*w)}const R=new Ie;R.setAttribute("position",new en(b,_)),R.setAttribute("uv",new en(S,m)),R.setAttribute("faceIndex",new en(T,f)),n.push(new Vt(R,null)),s>In&&s--}return{lodMeshes:n,sizeLods:t,sigmas:e}}function tc(i,t,e){const n=new $n(i,t,e);return n.texture.mapping=Js,n.texture.name="PMREM.cubeUv",n.scissorTest=!0,n}function gi(i,t,e,n,s){i.viewport.set(t,e,n,s),i.scissor.set(t,e,n,s)}function yp(i,t,e){return new En({name:"PMREMGGXConvolution",defines:{GGX_SAMPLES:vp,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${i}.0`},uniforms:{envMap:{value:null},roughness:{value:0},mipInt:{value:0}},vertexShader:Qs(),fragmentShader:` - - precision highp float; - precision highp int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform float roughness; - uniform float mipInt; - - #define ENVMAP_TYPE_CUBE_UV - #include - - #define PI 3.14159265359 - - // Van der Corput radical inverse - float radicalInverse_VdC(uint bits) { - bits = (bits << 16u) | (bits >> 16u); - bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); - bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); - bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); - bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); - return float(bits) * 2.3283064365386963e-10; // / 0x100000000 - } - - // Hammersley sequence - vec2 hammersley(uint i, uint N) { - return vec2(float(i) / float(N), radicalInverse_VdC(i)); - } - - // GGX VNDF importance sampling (Eric Heitz 2018) - // "Sampling the GGX Distribution of Visible Normals" - // https://jcgt.org/published/0007/04/01/ - vec3 importanceSampleGGX_VNDF(vec2 Xi, vec3 V, float roughness) { - float alpha = roughness * roughness; - - // Section 3.2: Transform view direction to hemisphere configuration - vec3 Vh = normalize(vec3(alpha * V.x, alpha * V.y, V.z)); - - // Section 4.1: Orthonormal basis - float lensq = Vh.x * Vh.x + Vh.y * Vh.y; - vec3 T1 = lensq > 0.0 ? vec3(-Vh.y, Vh.x, 0.0) / sqrt(lensq) : vec3(1.0, 0.0, 0.0); - vec3 T2 = cross(Vh, T1); - - // Section 4.2: Parameterization of projected area - float r = sqrt(Xi.x); - float phi = 2.0 * PI * Xi.y; - float t1 = r * cos(phi); - float t2 = r * sin(phi); - float s = 0.5 * (1.0 + Vh.z); - t2 = (1.0 - s) * sqrt(1.0 - t1 * t1) + s * t2; - - // Section 4.3: Reprojection onto hemisphere - vec3 Nh = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - t1 * t1 - t2 * t2)) * Vh; - - // Section 3.4: Transform back to ellipsoid configuration - return normalize(vec3(alpha * Nh.x, alpha * Nh.y, max(0.0, Nh.z))); - } - - void main() { - vec3 N = normalize(vOutputDirection); - vec3 V = N; // Assume view direction equals normal for pre-filtering - - vec3 prefilteredColor = vec3(0.0); - float totalWeight = 0.0; - - // For very low roughness, just sample the environment directly - if (roughness < 0.001) { - gl_FragColor = vec4(bilinearCubeUV(envMap, N, mipInt), 1.0); - return; - } - - // Tangent space basis for VNDF sampling - vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); - vec3 tangent = normalize(cross(up, N)); - vec3 bitangent = cross(N, tangent); - - for(uint i = 0u; i < uint(GGX_SAMPLES); i++) { - vec2 Xi = hammersley(i, uint(GGX_SAMPLES)); - - // For PMREM, V = N, so in tangent space V is always (0, 0, 1) - vec3 H_tangent = importanceSampleGGX_VNDF(Xi, vec3(0.0, 0.0, 1.0), roughness); - - // Transform H back to world space - vec3 H = normalize(tangent * H_tangent.x + bitangent * H_tangent.y + N * H_tangent.z); - vec3 L = normalize(2.0 * dot(V, H) * H - V); - - float NdotL = max(dot(N, L), 0.0); - - if(NdotL > 0.0) { - // Sample environment at fixed mip level - // VNDF importance sampling handles the distribution filtering - vec3 sampleColor = bilinearCubeUV(envMap, L, mipInt); - - // Weight by NdotL for the split-sum approximation - // VNDF PDF naturally accounts for the visible microfacet distribution - prefilteredColor += sampleColor * NdotL; - totalWeight += NdotL; - } - } - - if (totalWeight > 0.0) { - prefilteredColor = prefilteredColor / totalWeight; - } - - gl_FragColor = vec4(prefilteredColor, 1.0); - } - `,blending:yn,depthTest:!1,depthWrite:!1})}function bp(i,t,e){const n=new Float32Array(Zn),s=new D(0,1,0);return new En({name:"SphericalGaussianBlur",defines:{n:Zn,CUBEUV_TEXEL_WIDTH:1/t,CUBEUV_TEXEL_HEIGHT:1/e,CUBEUV_MAX_MIP:`${i}.0`},uniforms:{envMap:{value:null},samples:{value:1},weights:{value:n},latitudinal:{value:!1},dTheta:{value:0},mipInt:{value:0},poleAxis:{value:s}},vertexShader:Qs(),fragmentShader:` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - uniform int samples; - uniform float weights[ n ]; - uniform bool latitudinal; - uniform float dTheta; - uniform float mipInt; - uniform vec3 poleAxis; - - #define ENVMAP_TYPE_CUBE_UV - #include - - vec3 getSample( float theta, vec3 axis ) { - - float cosTheta = cos( theta ); - // Rodrigues' axis-angle rotation - vec3 sampleDirection = vOutputDirection * cosTheta - + cross( axis, vOutputDirection ) * sin( theta ) - + axis * dot( axis, vOutputDirection ) * ( 1.0 - cosTheta ); - - return bilinearCubeUV( envMap, sampleDirection, mipInt ); - - } - - void main() { - - vec3 axis = latitudinal ? poleAxis : cross( poleAxis, vOutputDirection ); - - if ( all( equal( axis, vec3( 0.0 ) ) ) ) { - - axis = vec3( vOutputDirection.z, 0.0, - vOutputDirection.x ); - - } - - axis = normalize( axis ); - - gl_FragColor = vec4( 0.0, 0.0, 0.0, 1.0 ); - gl_FragColor.rgb += weights[ 0 ] * getSample( 0.0, axis ); - - for ( int i = 1; i < n; i++ ) { - - if ( i >= samples ) { - - break; - - } - - float theta = dTheta * float( i ); - gl_FragColor.rgb += weights[ i ] * getSample( -1.0 * theta, axis ); - gl_FragColor.rgb += weights[ i ] * getSample( theta, axis ); - - } - - } - `,blending:yn,depthTest:!1,depthWrite:!1})}function ec(){return new En({name:"EquirectangularToCubeUV",uniforms:{envMap:{value:null}},vertexShader:Qs(),fragmentShader:` - - precision mediump float; - precision mediump int; - - varying vec3 vOutputDirection; - - uniform sampler2D envMap; - - #include - - void main() { - - vec3 outputDirection = normalize( vOutputDirection ); - vec2 uv = equirectUv( outputDirection ); - - gl_FragColor = vec4( texture2D ( envMap, uv ).rgb, 1.0 ); - - } - `,blending:yn,depthTest:!1,depthWrite:!1})}function nc(){return new En({name:"CubemapToCubeUV",uniforms:{envMap:{value:null},flipEnvMap:{value:-1}},vertexShader:Qs(),fragmentShader:` - - precision mediump float; - precision mediump int; - - uniform float flipEnvMap; - - varying vec3 vOutputDirection; - - uniform samplerCube envMap; - - void main() { - - gl_FragColor = textureCube( envMap, vec3( flipEnvMap * vOutputDirection.x, vOutputDirection.yz ) ); - - } - `,blending:yn,depthTest:!1,depthWrite:!1})}function Qs(){return` - - precision mediump float; - precision mediump int; - - attribute float faceIndex; - - varying vec3 vOutputDirection; - - // RH coordinate system; PMREM face-indexing convention - vec3 getDirection( vec2 uv, float face ) { - - uv = 2.0 * uv - 1.0; - - vec3 direction = vec3( uv, 1.0 ); - - if ( face == 0.0 ) { - - direction = direction.zyx; // ( 1, v, u ) pos x - - } else if ( face == 1.0 ) { - - direction = direction.xzy; - direction.xz *= -1.0; // ( -u, 1, -v ) pos y - - } else if ( face == 2.0 ) { - - direction.x *= -1.0; // ( -u, v, 1 ) pos z - - } else if ( face == 3.0 ) { - - direction = direction.zyx; - direction.xz *= -1.0; // ( -1, v, -u ) neg x - - } else if ( face == 4.0 ) { - - direction = direction.xzy; - direction.xy *= -1.0; // ( -u, -1, v ) neg y - - } else if ( face == 5.0 ) { - - direction.z *= -1.0; // ( u, v, -1 ) neg z - - } - - return direction; - - } - - void main() { - - vOutputDirection = getDirection( uv, faceIndex ); - gl_Position = vec4( position, 1.0 ); - - } - `}function Ep(i){let t=new WeakMap,e=null;function n(o){if(o&&o.isTexture){const l=o.mapping,c=l===jr||l===Jr,h=l===Ai||l===wi;if(c||h){let u=t.get(o);const d=u!==void 0?u.texture.pmremVersion:0;if(o.isRenderTargetTexture&&o.pmremVersion!==d)return e===null&&(e=new Ia(i)),u=c?e.fromEquirectangular(o,u):e.fromCubemap(o,u),u.texture.pmremVersion=o.pmremVersion,t.set(o,u),u.texture;if(u!==void 0)return u.texture;{const p=o.image;return c&&p&&p.height>0||h&&p&&s(p)?(e===null&&(e=new Ia(i)),u=c?e.fromEquirectangular(o):e.fromCubemap(o),u.texture.pmremVersion=o.pmremVersion,t.set(o,u),o.addEventListener("dispose",r),u.texture):null}}}return o}function s(o){let l=0;const c=6;for(let h=0;ht.maxTextureSize&&(R=Math.ceil(T/t.maxTextureSize),T=t.maxTextureSize);const w=new Float32Array(T*R*4*u),L=new Nc(w,T,R,u);L.type=on,L.needsUpdate=!0;const F=S*4;for(let M=0;M0)return i;const s=t*e;let r=sc[s];if(r===void 0&&(r=new Float32Array(s),sc[s]=r),t!==0){n.toArray(r,0);for(let a=1,o=0;a!==t;++a)o+=e,i[a].toArray(r,o)}return r}function Se(i,t){if(i.length!==t.length)return!1;for(let e=0,n=i.length;e":" "} ${o}: ${e[a]}`)}return n.join(` -`)}const uc=new Ht;function E0(i){$t._getMatrix(uc,$t.workingColorSpace,i);const t=`mat3( ${uc.elements.map(e=>e.toFixed(4))} )`;switch($t.getTransfer(i)){case Ws:return[t,"LinearTransferOETF"];case ne:return[t,"sRGBTransferOETF"];default:return Bt("WebGLProgram: Unsupported color space: ",i),[t,"LinearTransferOETF"]}}function fc(i,t,e){const n=i.getShaderParameter(t,i.COMPILE_STATUS),r=(i.getShaderInfoLog(t)||"").trim();if(n&&r==="")return"";const a=/ERROR: 0:(\d+)/.exec(r);if(a){const o=parseInt(a[1]);return e.toUpperCase()+` - -`+r+` - -`+b0(i.getShaderSource(t),o)}else return r}function T0(i,t){const e=E0(t);return[`vec4 ${i}( vec4 value ) {`,` return ${e[1]}( vec4( value.rgb * ${e[0]}, value.a ) );`,"}"].join(` -`)}function A0(i,t){let e;switch(t){case Gl:e="Linear";break;case kl:e="Reinhard";break;case Hl:e="Cineon";break;case Tc:e="ACESFilmic";break;case Xl:e="AgX";break;case ql:e="Neutral";break;case Wl:e="Custom";break;default:Bt("WebGLProgram: Unsupported toneMapping:",t),e="Linear"}return"vec3 "+i+"( vec3 color ) { return "+e+"ToneMapping( color ); }"}const Fs=new D;function w0(){$t.getLuminanceCoefficients(Fs);const i=Fs.x.toFixed(4),t=Fs.y.toFixed(4),e=Fs.z.toFixed(4);return["float luminance( const in vec3 rgb ) {",` const vec3 weights = vec3( ${i}, ${t}, ${e} );`," return dot( weights, rgb );","}"].join(` -`)}function R0(i){return[i.extensionClipCullDistance?"#extension GL_ANGLE_clip_cull_distance : require":"",i.extensionMultiDraw?"#extension GL_ANGLE_multi_draw : require":""].filter(Zi).join(` -`)}function C0(i){const t=[];for(const e in i){const n=i[e];n!==!1&&t.push("#define "+e+" "+n)}return t.join(` -`)}function P0(i,t){const e={},n=i.getProgramParameter(t,i.ACTIVE_ATTRIBUTES);for(let s=0;s/gm;function Ua(i){return i.replace(D0,I0)}const L0=new Map;function I0(i,t){let e=Wt[t];if(e===void 0){const n=L0.get(t);if(n!==void 0)e=Wt[n],Bt('WebGLRenderer: Shader chunk "%s" has been deprecated. Use "%s" instead.',t,n);else throw new Error("Can not resolve #include <"+t+">")}return Ua(e)}const U0=/#pragma unroll_loop_start\s+for\s*\(\s*int\s+i\s*=\s*(\d+)\s*;\s*i\s*<\s*(\d+)\s*;\s*i\s*\+\+\s*\)\s*{([\s\S]+?)}\s+#pragma unroll_loop_end/g;function mc(i){return i.replace(U0,N0)}function N0(i,t,e,n){let s="";for(let r=parseInt(t);r0&&(m+=` -`),f=["#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,x].filter(Zi).join(` -`),f.length>0&&(f+=` -`)):(m=[xc(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,x,e.extensionClipCullDistance?"#define USE_CLIP_DISTANCE":"",e.batching?"#define USE_BATCHING":"",e.batchingColor?"#define USE_BATCHING_COLOR":"",e.instancing?"#define USE_INSTANCING":"",e.instancingColor?"#define USE_INSTANCING_COLOR":"",e.instancingMorph?"#define USE_INSTANCING_MORPH":"",e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.map?"#define USE_MAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+h:"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.displacementMap?"#define USE_DISPLACEMENTMAP":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.mapUv?"#define MAP_UV "+e.mapUv:"",e.alphaMapUv?"#define ALPHAMAP_UV "+e.alphaMapUv:"",e.lightMapUv?"#define LIGHTMAP_UV "+e.lightMapUv:"",e.aoMapUv?"#define AOMAP_UV "+e.aoMapUv:"",e.emissiveMapUv?"#define EMISSIVEMAP_UV "+e.emissiveMapUv:"",e.bumpMapUv?"#define BUMPMAP_UV "+e.bumpMapUv:"",e.normalMapUv?"#define NORMALMAP_UV "+e.normalMapUv:"",e.displacementMapUv?"#define DISPLACEMENTMAP_UV "+e.displacementMapUv:"",e.metalnessMapUv?"#define METALNESSMAP_UV "+e.metalnessMapUv:"",e.roughnessMapUv?"#define ROUGHNESSMAP_UV "+e.roughnessMapUv:"",e.anisotropyMapUv?"#define ANISOTROPYMAP_UV "+e.anisotropyMapUv:"",e.clearcoatMapUv?"#define CLEARCOATMAP_UV "+e.clearcoatMapUv:"",e.clearcoatNormalMapUv?"#define CLEARCOAT_NORMALMAP_UV "+e.clearcoatNormalMapUv:"",e.clearcoatRoughnessMapUv?"#define CLEARCOAT_ROUGHNESSMAP_UV "+e.clearcoatRoughnessMapUv:"",e.iridescenceMapUv?"#define IRIDESCENCEMAP_UV "+e.iridescenceMapUv:"",e.iridescenceThicknessMapUv?"#define IRIDESCENCE_THICKNESSMAP_UV "+e.iridescenceThicknessMapUv:"",e.sheenColorMapUv?"#define SHEEN_COLORMAP_UV "+e.sheenColorMapUv:"",e.sheenRoughnessMapUv?"#define SHEEN_ROUGHNESSMAP_UV "+e.sheenRoughnessMapUv:"",e.specularMapUv?"#define SPECULARMAP_UV "+e.specularMapUv:"",e.specularColorMapUv?"#define SPECULAR_COLORMAP_UV "+e.specularColorMapUv:"",e.specularIntensityMapUv?"#define SPECULAR_INTENSITYMAP_UV "+e.specularIntensityMapUv:"",e.transmissionMapUv?"#define TRANSMISSIONMAP_UV "+e.transmissionMapUv:"",e.thicknessMapUv?"#define THICKNESSMAP_UV "+e.thicknessMapUv:"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexColors?"#define USE_COLOR":"",e.vertexAlphas?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.flatShading?"#define FLAT_SHADED":"",e.skinning?"#define USE_SKINNING":"",e.morphTargets?"#define USE_MORPHTARGETS":"",e.morphNormals&&e.flatShading===!1?"#define USE_MORPHNORMALS":"",e.morphColors?"#define USE_MORPHCOLORS":"",e.morphTargetsCount>0?"#define MORPHTARGETS_TEXTURE_STRIDE "+e.morphTextureStride:"",e.morphTargetsCount>0?"#define MORPHTARGETS_COUNT "+e.morphTargetsCount:"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.sizeAttenuation?"#define USE_SIZEATTENUATION":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 modelMatrix;","uniform mat4 modelViewMatrix;","uniform mat4 projectionMatrix;","uniform mat4 viewMatrix;","uniform mat3 normalMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;","#ifdef USE_INSTANCING"," attribute mat4 instanceMatrix;","#endif","#ifdef USE_INSTANCING_COLOR"," attribute vec3 instanceColor;","#endif","#ifdef USE_INSTANCING_MORPH"," uniform sampler2D morphTexture;","#endif","attribute vec3 position;","attribute vec3 normal;","attribute vec2 uv;","#ifdef USE_UV1"," attribute vec2 uv1;","#endif","#ifdef USE_UV2"," attribute vec2 uv2;","#endif","#ifdef USE_UV3"," attribute vec2 uv3;","#endif","#ifdef USE_TANGENT"," attribute vec4 tangent;","#endif","#if defined( USE_COLOR_ALPHA )"," attribute vec4 color;","#elif defined( USE_COLOR )"," attribute vec3 color;","#endif","#ifdef USE_SKINNING"," attribute vec4 skinIndex;"," attribute vec4 skinWeight;","#endif",` -`].filter(Zi).join(` -`),f=[xc(e),"#define SHADER_TYPE "+e.shaderType,"#define SHADER_NAME "+e.shaderName,x,e.useFog&&e.fog?"#define USE_FOG":"",e.useFog&&e.fogExp2?"#define FOG_EXP2":"",e.alphaToCoverage?"#define ALPHA_TO_COVERAGE":"",e.map?"#define USE_MAP":"",e.matcap?"#define USE_MATCAP":"",e.envMap?"#define USE_ENVMAP":"",e.envMap?"#define "+c:"",e.envMap?"#define "+h:"",e.envMap?"#define "+u:"",d?"#define CUBEUV_TEXEL_WIDTH "+d.texelWidth:"",d?"#define CUBEUV_TEXEL_HEIGHT "+d.texelHeight:"",d?"#define CUBEUV_MAX_MIP "+d.maxMip+".0":"",e.lightMap?"#define USE_LIGHTMAP":"",e.aoMap?"#define USE_AOMAP":"",e.bumpMap?"#define USE_BUMPMAP":"",e.normalMap?"#define USE_NORMALMAP":"",e.normalMapObjectSpace?"#define USE_NORMALMAP_OBJECTSPACE":"",e.normalMapTangentSpace?"#define USE_NORMALMAP_TANGENTSPACE":"",e.emissiveMap?"#define USE_EMISSIVEMAP":"",e.anisotropy?"#define USE_ANISOTROPY":"",e.anisotropyMap?"#define USE_ANISOTROPYMAP":"",e.clearcoat?"#define USE_CLEARCOAT":"",e.clearcoatMap?"#define USE_CLEARCOATMAP":"",e.clearcoatRoughnessMap?"#define USE_CLEARCOAT_ROUGHNESSMAP":"",e.clearcoatNormalMap?"#define USE_CLEARCOAT_NORMALMAP":"",e.dispersion?"#define USE_DISPERSION":"",e.iridescence?"#define USE_IRIDESCENCE":"",e.iridescenceMap?"#define USE_IRIDESCENCEMAP":"",e.iridescenceThicknessMap?"#define USE_IRIDESCENCE_THICKNESSMAP":"",e.specularMap?"#define USE_SPECULARMAP":"",e.specularColorMap?"#define USE_SPECULAR_COLORMAP":"",e.specularIntensityMap?"#define USE_SPECULAR_INTENSITYMAP":"",e.roughnessMap?"#define USE_ROUGHNESSMAP":"",e.metalnessMap?"#define USE_METALNESSMAP":"",e.alphaMap?"#define USE_ALPHAMAP":"",e.alphaTest?"#define USE_ALPHATEST":"",e.alphaHash?"#define USE_ALPHAHASH":"",e.sheen?"#define USE_SHEEN":"",e.sheenColorMap?"#define USE_SHEEN_COLORMAP":"",e.sheenRoughnessMap?"#define USE_SHEEN_ROUGHNESSMAP":"",e.transmission?"#define USE_TRANSMISSION":"",e.transmissionMap?"#define USE_TRANSMISSIONMAP":"",e.thicknessMap?"#define USE_THICKNESSMAP":"",e.vertexTangents&&e.flatShading===!1?"#define USE_TANGENT":"",e.vertexColors||e.instancingColor||e.batchingColor?"#define USE_COLOR":"",e.vertexAlphas?"#define USE_COLOR_ALPHA":"",e.vertexUv1s?"#define USE_UV1":"",e.vertexUv2s?"#define USE_UV2":"",e.vertexUv3s?"#define USE_UV3":"",e.pointsUvs?"#define USE_POINTS_UV":"",e.gradientMap?"#define USE_GRADIENTMAP":"",e.flatShading?"#define FLAT_SHADED":"",e.doubleSided?"#define DOUBLE_SIDED":"",e.flipSided?"#define FLIP_SIDED":"",e.shadowMapEnabled?"#define USE_SHADOWMAP":"",e.shadowMapEnabled?"#define "+l:"",e.premultipliedAlpha?"#define PREMULTIPLIED_ALPHA":"",e.numLightProbes>0?"#define USE_LIGHT_PROBES":"",e.decodeVideoTexture?"#define DECODE_VIDEO_TEXTURE":"",e.decodeVideoTextureEmissive?"#define DECODE_VIDEO_TEXTURE_EMISSIVE":"",e.logarithmicDepthBuffer?"#define USE_LOGARITHMIC_DEPTH_BUFFER":"",e.reversedDepthBuffer?"#define USE_REVERSED_DEPTH_BUFFER":"","uniform mat4 viewMatrix;","uniform vec3 cameraPosition;","uniform bool isOrthographic;",e.toneMapping!==Un?"#define TONE_MAPPING":"",e.toneMapping!==Un?Wt.tonemapping_pars_fragment:"",e.toneMapping!==Un?A0("toneMapping",e.toneMapping):"",e.dithering?"#define DITHERING":"",e.opaque?"#define OPAQUE":"",Wt.colorspace_pars_fragment,T0("linearToOutputTexel",e.outputColorSpace),w0(),e.useDepthPacking?"#define DEPTH_PACKING "+e.depthPacking:"",` -`].filter(Zi).join(` -`)),a=Ua(a),a=dc(a,e),a=pc(a,e),o=Ua(o),o=dc(o,e),o=pc(o,e),a=mc(a),o=mc(o),e.isRawShaderMaterial!==!0&&(b=`#version 300 es -`,m=[p,"#define attribute in","#define varying out","#define texture2D texture"].join(` -`)+` -`+m,f=["#define varying in",e.glslVersion===mo?"":"layout(location = 0) out highp vec4 pc_fragColor;",e.glslVersion===mo?"":"#define gl_FragColor pc_fragColor","#define gl_FragDepthEXT gl_FragDepth","#define texture2D texture","#define textureCube texture","#define texture2DProj textureProj","#define texture2DLodEXT textureLod","#define texture2DProjLodEXT textureProjLod","#define textureCubeLodEXT textureLod","#define texture2DGradEXT textureGrad","#define texture2DProjGradEXT textureProjGrad","#define textureCubeGradEXT textureGrad"].join(` -`)+` -`+f);const S=b+m+a,T=b+f+o,R=hc(s,s.VERTEX_SHADER,S),w=hc(s,s.FRAGMENT_SHADER,T);s.attachShader(_,R),s.attachShader(_,w),e.index0AttributeName!==void 0?s.bindAttribLocation(_,0,e.index0AttributeName):e.morphTargets===!0&&s.bindAttribLocation(_,0,"position"),s.linkProgram(_);function L(P){if(i.debug.checkShaderErrors){const O=s.getProgramInfoLog(_)||"",z=s.getShaderInfoLog(R)||"",k=s.getShaderInfoLog(w)||"",W=O.trim(),Y=z.trim(),nt=k.trim();let X=!0,rt=!0;if(s.getProgramParameter(_,s.LINK_STATUS)===!1)if(X=!1,typeof i.debug.onShaderError=="function")i.debug.onShaderError(s,_,R,w);else{const st=fc(s,R,"vertex"),St=fc(s,w,"fragment");de("THREE.WebGLProgram: Shader Error "+s.getError()+" - VALIDATE_STATUS "+s.getProgramParameter(_,s.VALIDATE_STATUS)+` - -Material Name: `+P.name+` -Material Type: `+P.type+` - -Program Info Log: `+W+` -`+st+` -`+St)}else W!==""?Bt("WebGLProgram: Program Info Log:",W):(Y===""||nt==="")&&(rt=!1);rt&&(P.diagnostics={runnable:X,programLog:W,vertexShader:{log:Y,prefix:m},fragmentShader:{log:nt,prefix:f}})}s.deleteShader(R),s.deleteShader(w),F=new Hs(s,_),E=P0(s,_)}let F;this.getUniforms=function(){return F===void 0&&L(this),F};let E;this.getAttributes=function(){return E===void 0&&L(this),E};let M=e.rendererExtensionParallelShaderCompile===!1;return this.isReady=function(){return M===!1&&(M=s.getProgramParameter(_,S0)),M},this.destroy=function(){n.releaseStatesOfProgram(this),s.deleteProgram(_),this.program=void 0},this.type=e.shaderType,this.name=e.shaderName,this.id=y0++,this.cacheKey=t,this.usedTimes=1,this.program=_,this.vertexShader=R,this.fragmentShader=w,this}let k0=0;class H0{constructor(){this.shaderCache=new Map,this.materialCache=new Map}update(t){const e=t.vertexShader,n=t.fragmentShader,s=this._getShaderStage(e),r=this._getShaderStage(n),a=this._getShaderCacheForMaterial(t);return a.has(s)===!1&&(a.add(s),s.usedTimes++),a.has(r)===!1&&(a.add(r),r.usedTimes++),this}remove(t){const e=this.materialCache.get(t);for(const n of e)n.usedTimes--,n.usedTimes===0&&this.shaderCache.delete(n.code);return this.materialCache.delete(t),this}getVertexShaderID(t){return this._getShaderStage(t.vertexShader).id}getFragmentShaderID(t){return this._getShaderStage(t.fragmentShader).id}dispose(){this.shaderCache.clear(),this.materialCache.clear()}_getShaderCacheForMaterial(t){const e=this.materialCache;let n=e.get(t);return n===void 0&&(n=new Set,e.set(t,n)),n}_getShaderStage(t){const e=this.shaderCache;let n=e.get(t);return n===void 0&&(n=new W0(t),e.set(t,n)),n}}class W0{constructor(t){this.id=k0++,this.code=t,this.usedTimes=0}}function X0(i,t,e,n,s,r,a){const o=new Ya,l=new H0,c=new Set,h=[],u=s.logarithmicDepthBuffer,d=s.vertexTextures;let p=s.precision;const x={MeshDepthMaterial:"depth",MeshDistanceMaterial:"distanceRGBA",MeshNormalMaterial:"normal",MeshBasicMaterial:"basic",MeshLambertMaterial:"lambert",MeshPhongMaterial:"phong",MeshToonMaterial:"toon",MeshStandardMaterial:"physical",MeshPhysicalMaterial:"physical",MeshMatcapMaterial:"matcap",LineBasicMaterial:"basic",LineDashedMaterial:"dashed",PointsMaterial:"points",ShadowMaterial:"shadow",SpriteMaterial:"sprite"};function _(E){return c.add(E),E===0?"uv":`uv${E}`}function m(E,M,P,O,z){const k=O.fog,W=z.geometry,Y=E.isMeshStandardMaterial?O.environment:null,nt=(E.isMeshStandardMaterial?e:t).get(E.envMap||Y),X=nt&&nt.mapping===Js?nt.image.height:null,rt=x[E.type];E.precision!==null&&(p=s.getMaxPrecision(E.precision),p!==E.precision&&Bt("WebGLProgram.getParameters:",E.precision,"not supported, using",p,"instead."));const st=W.morphAttributes.position||W.morphAttributes.normal||W.morphAttributes.color,St=st!==void 0?st.length:0;let Gt=0;W.morphAttributes.position!==void 0&&(Gt=1),W.morphAttributes.normal!==void 0&&(Gt=2),W.morphAttributes.color!==void 0&&(Gt=3);let qt,jt,Kt,K;if(rt){const te=an[rt];qt=te.vertexShader,jt=te.fragmentShader}else qt=E.vertexShader,jt=E.fragmentShader,l.update(E),Kt=l.getVertexShaderID(E),K=l.getFragmentShaderID(E);const J=i.getRenderTarget(),xt=i.state.buffers.depth.getReversed(),Rt=z.isInstancedMesh===!0,Tt=z.isBatchedMesh===!0,kt=!!E.map,Qt=!!E.matcap,Ct=!!nt,B=!!E.aoMap,y=!!E.lightMap,Z=!!E.bumpMap,Q=!!E.normalMap,ot=!!E.displacementMap,et=!!E.emissiveMap,vt=!!E.metalnessMap,ft=!!E.roughnessMap,Mt=E.anisotropy>0,A=E.clearcoat>0,g=E.dispersion>0,N=E.iridescence>0,q=E.sheen>0,$=E.transmission>0,H=Mt&&!!E.anisotropyMap,wt=A&&!!E.clearcoatMap,dt=A&&!!E.clearcoatNormalMap,Pt=A&&!!E.clearcoatRoughnessMap,At=N&&!!E.iridescenceMap,tt=N&&!!E.iridescenceThicknessMap,lt=q&&!!E.sheenColorMap,Nt=q&&!!E.sheenRoughnessMap,It=!!E.specularMap,_t=!!E.specularColorMap,Ot=!!E.specularIntensityMap,C=$&&!!E.transmissionMap,mt=$&&!!E.thicknessMap,ht=!!E.gradientMap,ut=!!E.alphaMap,it=E.alphaTest>0,j=!!E.alphaHash,bt=!!E.extensions;let zt=Un;E.toneMapped&&(J===null||J.isXRRenderTarget===!0)&&(zt=i.toneMapping);const ce={shaderID:rt,shaderType:E.type,shaderName:E.name,vertexShader:qt,fragmentShader:jt,defines:E.defines,customVertexShaderID:Kt,customFragmentShaderID:K,isRawShaderMaterial:E.isRawShaderMaterial===!0,glslVersion:E.glslVersion,precision:p,batching:Tt,batchingColor:Tt&&z._colorsTexture!==null,instancing:Rt,instancingColor:Rt&&z.instanceColor!==null,instancingMorph:Rt&&z.morphTexture!==null,supportsVertexTextures:d,outputColorSpace:J===null?i.outputColorSpace:J.isXRRenderTarget===!0?J.texture.colorSpace:Ri,alphaToCoverage:!!E.alphaToCoverage,map:kt,matcap:Qt,envMap:Ct,envMapMode:Ct&&nt.mapping,envMapCubeUVHeight:X,aoMap:B,lightMap:y,bumpMap:Z,normalMap:Q,displacementMap:d&&ot,emissiveMap:et,normalMapObjectSpace:Q&&E.normalMapType===jl,normalMapTangentSpace:Q&&E.normalMapType===Xa,metalnessMap:vt,roughnessMap:ft,anisotropy:Mt,anisotropyMap:H,clearcoat:A,clearcoatMap:wt,clearcoatNormalMap:dt,clearcoatRoughnessMap:Pt,dispersion:g,iridescence:N,iridescenceMap:At,iridescenceThicknessMap:tt,sheen:q,sheenColorMap:lt,sheenRoughnessMap:Nt,specularMap:It,specularColorMap:_t,specularIntensityMap:Ot,transmission:$,transmissionMap:C,thicknessMap:mt,gradientMap:ht,opaque:E.transparent===!1&&E.blending===bi&&E.alphaToCoverage===!1,alphaMap:ut,alphaTest:it,alphaHash:j,combine:E.combine,mapUv:kt&&_(E.map.channel),aoMapUv:B&&_(E.aoMap.channel),lightMapUv:y&&_(E.lightMap.channel),bumpMapUv:Z&&_(E.bumpMap.channel),normalMapUv:Q&&_(E.normalMap.channel),displacementMapUv:ot&&_(E.displacementMap.channel),emissiveMapUv:et&&_(E.emissiveMap.channel),metalnessMapUv:vt&&_(E.metalnessMap.channel),roughnessMapUv:ft&&_(E.roughnessMap.channel),anisotropyMapUv:H&&_(E.anisotropyMap.channel),clearcoatMapUv:wt&&_(E.clearcoatMap.channel),clearcoatNormalMapUv:dt&&_(E.clearcoatNormalMap.channel),clearcoatRoughnessMapUv:Pt&&_(E.clearcoatRoughnessMap.channel),iridescenceMapUv:At&&_(E.iridescenceMap.channel),iridescenceThicknessMapUv:tt&&_(E.iridescenceThicknessMap.channel),sheenColorMapUv:lt&&_(E.sheenColorMap.channel),sheenRoughnessMapUv:Nt&&_(E.sheenRoughnessMap.channel),specularMapUv:It&&_(E.specularMap.channel),specularColorMapUv:_t&&_(E.specularColorMap.channel),specularIntensityMapUv:Ot&&_(E.specularIntensityMap.channel),transmissionMapUv:C&&_(E.transmissionMap.channel),thicknessMapUv:mt&&_(E.thicknessMap.channel),alphaMapUv:ut&&_(E.alphaMap.channel),vertexTangents:!!W.attributes.tangent&&(Q||Mt),vertexColors:E.vertexColors,vertexAlphas:E.vertexColors===!0&&!!W.attributes.color&&W.attributes.color.itemSize===4,pointsUvs:z.isPoints===!0&&!!W.attributes.uv&&(kt||ut),fog:!!k,useFog:E.fog===!0,fogExp2:!!k&&k.isFogExp2,flatShading:E.flatShading===!0&&E.wireframe===!1,sizeAttenuation:E.sizeAttenuation===!0,logarithmicDepthBuffer:u,reversedDepthBuffer:xt,skinning:z.isSkinnedMesh===!0,morphTargets:W.morphAttributes.position!==void 0,morphNormals:W.morphAttributes.normal!==void 0,morphColors:W.morphAttributes.color!==void 0,morphTargetsCount:St,morphTextureStride:Gt,numDirLights:M.directional.length,numPointLights:M.point.length,numSpotLights:M.spot.length,numSpotLightMaps:M.spotLightMap.length,numRectAreaLights:M.rectArea.length,numHemiLights:M.hemi.length,numDirLightShadows:M.directionalShadowMap.length,numPointLightShadows:M.pointShadowMap.length,numSpotLightShadows:M.spotShadowMap.length,numSpotLightShadowsWithMaps:M.numSpotLightShadowsWithMaps,numLightProbes:M.numLightProbes,numClippingPlanes:a.numPlanes,numClipIntersection:a.numIntersection,dithering:E.dithering,shadowMapEnabled:i.shadowMap.enabled&&P.length>0,shadowMapType:i.shadowMap.type,toneMapping:zt,decodeVideoTexture:kt&&E.map.isVideoTexture===!0&&$t.getTransfer(E.map.colorSpace)===ne,decodeVideoTextureEmissive:et&&E.emissiveMap.isVideoTexture===!0&&$t.getTransfer(E.emissiveMap.colorSpace)===ne,premultipliedAlpha:E.premultipliedAlpha,doubleSided:E.side===Mn,flipSided:E.side===Le,useDepthPacking:E.depthPacking>=0,depthPacking:E.depthPacking||0,index0AttributeName:E.index0AttributeName,extensionClipCullDistance:bt&&E.extensions.clipCullDistance===!0&&n.has("WEBGL_clip_cull_distance"),extensionMultiDraw:(bt&&E.extensions.multiDraw===!0||Tt)&&n.has("WEBGL_multi_draw"),rendererExtensionParallelShaderCompile:n.has("KHR_parallel_shader_compile"),customProgramCacheKey:E.customProgramCacheKey()};return ce.vertexUv1s=c.has(1),ce.vertexUv2s=c.has(2),ce.vertexUv3s=c.has(3),c.clear(),ce}function f(E){const M=[];if(E.shaderID?M.push(E.shaderID):(M.push(E.customVertexShaderID),M.push(E.customFragmentShaderID)),E.defines!==void 0)for(const P in E.defines)M.push(P),M.push(E.defines[P]);return E.isRawShaderMaterial===!1&&(b(M,E),S(M,E),M.push(i.outputColorSpace)),M.push(E.customProgramCacheKey),M.join()}function b(E,M){E.push(M.precision),E.push(M.outputColorSpace),E.push(M.envMapMode),E.push(M.envMapCubeUVHeight),E.push(M.mapUv),E.push(M.alphaMapUv),E.push(M.lightMapUv),E.push(M.aoMapUv),E.push(M.bumpMapUv),E.push(M.normalMapUv),E.push(M.displacementMapUv),E.push(M.emissiveMapUv),E.push(M.metalnessMapUv),E.push(M.roughnessMapUv),E.push(M.anisotropyMapUv),E.push(M.clearcoatMapUv),E.push(M.clearcoatNormalMapUv),E.push(M.clearcoatRoughnessMapUv),E.push(M.iridescenceMapUv),E.push(M.iridescenceThicknessMapUv),E.push(M.sheenColorMapUv),E.push(M.sheenRoughnessMapUv),E.push(M.specularMapUv),E.push(M.specularColorMapUv),E.push(M.specularIntensityMapUv),E.push(M.transmissionMapUv),E.push(M.thicknessMapUv),E.push(M.combine),E.push(M.fogExp2),E.push(M.sizeAttenuation),E.push(M.morphTargetsCount),E.push(M.morphAttributeCount),E.push(M.numDirLights),E.push(M.numPointLights),E.push(M.numSpotLights),E.push(M.numSpotLightMaps),E.push(M.numHemiLights),E.push(M.numRectAreaLights),E.push(M.numDirLightShadows),E.push(M.numPointLightShadows),E.push(M.numSpotLightShadows),E.push(M.numSpotLightShadowsWithMaps),E.push(M.numLightProbes),E.push(M.shadowMapType),E.push(M.toneMapping),E.push(M.numClippingPlanes),E.push(M.numClipIntersection),E.push(M.depthPacking)}function S(E,M){o.disableAll(),M.supportsVertexTextures&&o.enable(0),M.instancing&&o.enable(1),M.instancingColor&&o.enable(2),M.instancingMorph&&o.enable(3),M.matcap&&o.enable(4),M.envMap&&o.enable(5),M.normalMapObjectSpace&&o.enable(6),M.normalMapTangentSpace&&o.enable(7),M.clearcoat&&o.enable(8),M.iridescence&&o.enable(9),M.alphaTest&&o.enable(10),M.vertexColors&&o.enable(11),M.vertexAlphas&&o.enable(12),M.vertexUv1s&&o.enable(13),M.vertexUv2s&&o.enable(14),M.vertexUv3s&&o.enable(15),M.vertexTangents&&o.enable(16),M.anisotropy&&o.enable(17),M.alphaHash&&o.enable(18),M.batching&&o.enable(19),M.dispersion&&o.enable(20),M.batchingColor&&o.enable(21),M.gradientMap&&o.enable(22),E.push(o.mask),o.disableAll(),M.fog&&o.enable(0),M.useFog&&o.enable(1),M.flatShading&&o.enable(2),M.logarithmicDepthBuffer&&o.enable(3),M.reversedDepthBuffer&&o.enable(4),M.skinning&&o.enable(5),M.morphTargets&&o.enable(6),M.morphNormals&&o.enable(7),M.morphColors&&o.enable(8),M.premultipliedAlpha&&o.enable(9),M.shadowMapEnabled&&o.enable(10),M.doubleSided&&o.enable(11),M.flipSided&&o.enable(12),M.useDepthPacking&&o.enable(13),M.dithering&&o.enable(14),M.transmission&&o.enable(15),M.sheen&&o.enable(16),M.opaque&&o.enable(17),M.pointsUvs&&o.enable(18),M.decodeVideoTexture&&o.enable(19),M.decodeVideoTextureEmissive&&o.enable(20),M.alphaToCoverage&&o.enable(21),E.push(o.mask)}function T(E){const M=x[E.type];let P;if(M){const O=an[M];P=Ah.clone(O.uniforms)}else P=E.uniforms;return P}function R(E,M){let P;for(let O=0,z=h.length;O0?n.push(f):p.transparent===!0?s.push(f):e.push(f)}function l(u,d,p,x,_,m){const f=a(u,d,p,x,_,m);p.transmission>0?n.unshift(f):p.transparent===!0?s.unshift(f):e.unshift(f)}function c(u,d){e.length>1&&e.sort(u||Y0),n.length>1&&n.sort(d||gc),s.length>1&&s.sort(d||gc)}function h(){for(let u=t,d=i.length;u=r.length?(a=new _c,r.push(a)):a=r[s],a}function e(){i=new WeakMap}return{get:t,dispose:e}}function K0(){const i={};return{get:function(t){if(i[t.id]!==void 0)return i[t.id];let e;switch(t.type){case"DirectionalLight":e={direction:new D,color:new Yt};break;case"SpotLight":e={position:new D,direction:new D,color:new Yt,distance:0,coneCos:0,penumbraCos:0,decay:0};break;case"PointLight":e={position:new D,color:new Yt,distance:0,decay:0};break;case"HemisphereLight":e={direction:new D,skyColor:new Yt,groundColor:new Yt};break;case"RectAreaLight":e={color:new Yt,position:new D,halfWidth:new D,halfHeight:new D};break}return i[t.id]=e,e}}}function j0(){const i={};return{get:function(t){if(i[t.id]!==void 0)return i[t.id];let e;switch(t.type){case"DirectionalLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new at};break;case"SpotLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new at};break;case"PointLight":e={shadowIntensity:1,shadowBias:0,shadowNormalBias:0,shadowRadius:1,shadowMapSize:new at,shadowCameraNear:1,shadowCameraFar:1e3};break}return i[t.id]=e,e}}}let J0=0;function $0(i,t){return(t.castShadow?2:0)-(i.castShadow?2:0)+(t.map?1:0)-(i.map?1:0)}function Q0(i){const t=new K0,e=j0(),n={version:0,hash:{directionalLength:-1,pointLength:-1,spotLength:-1,rectAreaLength:-1,hemiLength:-1,numDirectionalShadows:-1,numPointShadows:-1,numSpotShadows:-1,numSpotMaps:-1,numLightProbes:-1},ambient:[0,0,0],probe:[],directional:[],directionalShadow:[],directionalShadowMap:[],directionalShadowMatrix:[],spot:[],spotLightMap:[],spotShadow:[],spotShadowMap:[],spotLightMatrix:[],rectArea:[],rectAreaLTC1:null,rectAreaLTC2:null,point:[],pointShadow:[],pointShadowMap:[],pointShadowMatrix:[],hemi:[],numSpotLightShadowsWithMaps:0,numLightProbes:0};for(let c=0;c<9;c++)n.probe.push(new D);const s=new D,r=new re,a=new re;function o(c){let h=0,u=0,d=0;for(let E=0;E<9;E++)n.probe[E].set(0,0,0);let p=0,x=0,_=0,m=0,f=0,b=0,S=0,T=0,R=0,w=0,L=0;c.sort($0);for(let E=0,M=c.length;E0&&(i.has("OES_texture_float_linear")===!0?(n.rectAreaLTC1=pt.LTC_FLOAT_1,n.rectAreaLTC2=pt.LTC_FLOAT_2):(n.rectAreaLTC1=pt.LTC_HALF_1,n.rectAreaLTC2=pt.LTC_HALF_2)),n.ambient[0]=h,n.ambient[1]=u,n.ambient[2]=d;const F=n.hash;(F.directionalLength!==p||F.pointLength!==x||F.spotLength!==_||F.rectAreaLength!==m||F.hemiLength!==f||F.numDirectionalShadows!==b||F.numPointShadows!==S||F.numSpotShadows!==T||F.numSpotMaps!==R||F.numLightProbes!==L)&&(n.directional.length=p,n.spot.length=_,n.rectArea.length=m,n.point.length=x,n.hemi.length=f,n.directionalShadow.length=b,n.directionalShadowMap.length=b,n.pointShadow.length=S,n.pointShadowMap.length=S,n.spotShadow.length=T,n.spotShadowMap.length=T,n.directionalShadowMatrix.length=b,n.pointShadowMatrix.length=S,n.spotLightMatrix.length=T+R-w,n.spotLightMap.length=R,n.numSpotLightShadowsWithMaps=w,n.numLightProbes=L,F.directionalLength=p,F.pointLength=x,F.spotLength=_,F.rectAreaLength=m,F.hemiLength=f,F.numDirectionalShadows=b,F.numPointShadows=S,F.numSpotShadows=T,F.numSpotMaps=R,F.numLightProbes=L,n.version=J0++)}function l(c,h){let u=0,d=0,p=0,x=0,_=0;const m=h.matrixWorldInverse;for(let f=0,b=c.length;f=a.length?(o=new vc(i),a.push(o)):o=a[r],o}function n(){t=new WeakMap}return{get:e,dispose:n}}const em=`void main() { - gl_Position = vec4( position, 1.0 ); -}`,nm=`uniform sampler2D shadow_pass; -uniform vec2 resolution; -uniform float radius; -#include -void main() { - const float samples = float( VSM_SAMPLES ); - float mean = 0.0; - float squared_mean = 0.0; - float uvStride = samples <= 1.0 ? 0.0 : 2.0 / ( samples - 1.0 ); - float uvStart = samples <= 1.0 ? 0.0 : - 1.0; - for ( float i = 0.0; i < samples; i ++ ) { - float uvOffset = uvStart + i * uvStride; - #ifdef HORIZONTAL_PASS - vec2 distribution = unpackRGBATo2Half( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( uvOffset, 0.0 ) * radius ) / resolution ) ); - mean += distribution.x; - squared_mean += distribution.y * distribution.y + distribution.x * distribution.x; - #else - float depth = unpackRGBAToDepth( texture2D( shadow_pass, ( gl_FragCoord.xy + vec2( 0.0, uvOffset ) * radius ) / resolution ) ); - mean += depth; - squared_mean += depth * depth; - #endif - } - mean = mean / samples; - squared_mean = squared_mean / samples; - float std_dev = sqrt( squared_mean - mean * mean ); - gl_FragColor = pack2HalfToRGBA( vec2( mean, std_dev ) ); -}`;function im(i,t,e){let n=new Za;const s=new at,r=new at,a=new se,o=new Mu({depthPacking:Kl}),l=new Su,c={},h=e.maxTextureSize,u={[Fn]:Le,[Le]:Fn,[Mn]:Mn},d=new En({defines:{VSM_SAMPLES:8},uniforms:{shadow_pass:{value:null},resolution:{value:new at},radius:{value:4}},vertexShader:em,fragmentShader:nm}),p=d.clone();p.defines.HORIZONTAL_PASS=1;const x=new Ie;x.setAttribute("position",new en(new Float32Array([-1,-1,.5,3,-1,.5,-1,3,.5]),3));const _=new Vt(x,d),m=this;this.enabled=!1,this.autoUpdate=!0,this.needsUpdate=!1,this.type=bc;let f=this.type;this.render=function(w,L,F){if(m.enabled===!1||m.autoUpdate===!1&&m.needsUpdate===!1||w.length===0)return;const E=i.getRenderTarget(),M=i.getActiveCubeFace(),P=i.getActiveMipmapLevel(),O=i.state;O.setBlending(yn),O.buffers.depth.getReversed()===!0?O.buffers.color.setClear(0,0,0,0):O.buffers.color.setClear(1,1,1,1),O.buffers.depth.setTest(!0),O.setScissorTest(!1);const z=f!==_n&&this.type===_n,k=f===_n&&this.type!==_n;for(let W=0,Y=w.length;Wh||s.y>h)&&(s.x>h&&(r.x=Math.floor(h/rt.x),s.x=r.x*rt.x,X.mapSize.x=r.x),s.y>h&&(r.y=Math.floor(h/rt.y),s.y=r.y*rt.y,X.mapSize.y=r.y)),X.map===null||z===!0||k===!0){const St=this.type!==_n?{minFilter:He,magFilter:He}:{};X.map!==null&&X.map.dispose(),X.map=new $n(s.x,s.y,St),X.map.texture.name=nt.name+".shadowMap",X.camera.updateProjectionMatrix()}i.setRenderTarget(X.map),i.clear();const st=X.getViewportCount();for(let St=0;St0||L.map&&L.alphaTest>0||L.alphaToCoverage===!0){const O=M.uuid,z=L.uuid;let k=c[O];k===void 0&&(k={},c[O]=k);let W=k[z];W===void 0&&(W=M.clone(),k[z]=W,L.addEventListener("dispose",R)),M=W}if(M.visible=L.visible,M.wireframe=L.wireframe,E===_n?M.side=L.shadowSide!==null?L.shadowSide:L.side:M.side=L.shadowSide!==null?L.shadowSide:u[L.side],M.alphaMap=L.alphaMap,M.alphaTest=L.alphaToCoverage===!0?.5:L.alphaTest,M.map=L.map,M.clipShadows=L.clipShadows,M.clippingPlanes=L.clippingPlanes,M.clipIntersection=L.clipIntersection,M.displacementMap=L.displacementMap,M.displacementScale=L.displacementScale,M.displacementBias=L.displacementBias,M.wireframeLinewidth=L.wireframeLinewidth,M.linewidth=L.linewidth,F.isPointLight===!0&&M.isMeshDistanceMaterial===!0){const O=i.properties.get(M);O.light=F}return M}function T(w,L,F,E,M){if(w.visible===!1)return;if(w.layers.test(L.layers)&&(w.isMesh||w.isLine||w.isPoints)&&(w.castShadow||w.receiveShadow&&M===_n)&&(!w.frustumCulled||n.intersectsObject(w))){w.modelViewMatrix.multiplyMatrices(F.matrixWorldInverse,w.matrixWorld);const z=t.update(w),k=w.material;if(Array.isArray(k)){const W=z.groups;for(let Y=0,nt=W.length;Y=1):X.indexOf("OpenGL ES")!==-1&&(nt=parseFloat(/^OpenGL ES (\d)/.exec(X)[1]),Y=nt>=2);let rt=null,st={};const St=i.getParameter(i.SCISSOR_BOX),Gt=i.getParameter(i.VIEWPORT),qt=new se().fromArray(St),jt=new se().fromArray(Gt);function Kt(C,mt,ht,ut){const it=new Uint8Array(4),j=i.createTexture();i.bindTexture(C,j),i.texParameteri(C,i.TEXTURE_MIN_FILTER,i.NEAREST),i.texParameteri(C,i.TEXTURE_MAG_FILTER,i.NEAREST);for(let bt=0;bt"u"?!1:/OculusBrowser/g.test(navigator.userAgent),c=new at,h=new WeakMap;let u;const d=new WeakMap;let p=!1;try{p=typeof OffscreenCanvas<"u"&&new OffscreenCanvas(1,1).getContext("2d")!==null}catch{}function x(A,g){return p?new OffscreenCanvas(A,g):qs("canvas")}function _(A,g,N){let q=1;const $=Mt(A);if(($.width>N||$.height>N)&&(q=N/Math.max($.width,$.height)),q<1)if(typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement||typeof HTMLCanvasElement<"u"&&A instanceof HTMLCanvasElement||typeof ImageBitmap<"u"&&A instanceof ImageBitmap||typeof VideoFrame<"u"&&A instanceof VideoFrame){const H=Math.floor(q*$.width),wt=Math.floor(q*$.height);u===void 0&&(u=x(H,wt));const dt=g?x(H,wt):u;return dt.width=H,dt.height=wt,dt.getContext("2d").drawImage(A,0,0,H,wt),Bt("WebGLRenderer: Texture has been resized from ("+$.width+"x"+$.height+") to ("+H+"x"+wt+")."),dt}else return"data"in A&&Bt("WebGLRenderer: Image in DataTexture is too big ("+$.width+"x"+$.height+")."),A;return A}function m(A){return A.generateMipmaps}function f(A){i.generateMipmap(A)}function b(A){return A.isWebGLCubeRenderTarget?i.TEXTURE_CUBE_MAP:A.isWebGL3DRenderTarget?i.TEXTURE_3D:A.isWebGLArrayRenderTarget||A.isCompressedArrayTexture?i.TEXTURE_2D_ARRAY:i.TEXTURE_2D}function S(A,g,N,q,$=!1){if(A!==null){if(i[A]!==void 0)return i[A];Bt("WebGLRenderer: Attempt to use non-existing WebGL internal format '"+A+"'")}let H=g;if(g===i.RED&&(N===i.FLOAT&&(H=i.R32F),N===i.HALF_FLOAT&&(H=i.R16F),N===i.UNSIGNED_BYTE&&(H=i.R8)),g===i.RED_INTEGER&&(N===i.UNSIGNED_BYTE&&(H=i.R8UI),N===i.UNSIGNED_SHORT&&(H=i.R16UI),N===i.UNSIGNED_INT&&(H=i.R32UI),N===i.BYTE&&(H=i.R8I),N===i.SHORT&&(H=i.R16I),N===i.INT&&(H=i.R32I)),g===i.RG&&(N===i.FLOAT&&(H=i.RG32F),N===i.HALF_FLOAT&&(H=i.RG16F),N===i.UNSIGNED_BYTE&&(H=i.RG8)),g===i.RG_INTEGER&&(N===i.UNSIGNED_BYTE&&(H=i.RG8UI),N===i.UNSIGNED_SHORT&&(H=i.RG16UI),N===i.UNSIGNED_INT&&(H=i.RG32UI),N===i.BYTE&&(H=i.RG8I),N===i.SHORT&&(H=i.RG16I),N===i.INT&&(H=i.RG32I)),g===i.RGB_INTEGER&&(N===i.UNSIGNED_BYTE&&(H=i.RGB8UI),N===i.UNSIGNED_SHORT&&(H=i.RGB16UI),N===i.UNSIGNED_INT&&(H=i.RGB32UI),N===i.BYTE&&(H=i.RGB8I),N===i.SHORT&&(H=i.RGB16I),N===i.INT&&(H=i.RGB32I)),g===i.RGBA_INTEGER&&(N===i.UNSIGNED_BYTE&&(H=i.RGBA8UI),N===i.UNSIGNED_SHORT&&(H=i.RGBA16UI),N===i.UNSIGNED_INT&&(H=i.RGBA32UI),N===i.BYTE&&(H=i.RGBA8I),N===i.SHORT&&(H=i.RGBA16I),N===i.INT&&(H=i.RGBA32I)),g===i.RGB&&(N===i.UNSIGNED_INT_5_9_9_9_REV&&(H=i.RGB9_E5),N===i.UNSIGNED_INT_10F_11F_11F_REV&&(H=i.R11F_G11F_B10F)),g===i.RGBA){const wt=$?Ws:$t.getTransfer(q);N===i.FLOAT&&(H=i.RGBA32F),N===i.HALF_FLOAT&&(H=i.RGBA16F),N===i.UNSIGNED_BYTE&&(H=wt===ne?i.SRGB8_ALPHA8:i.RGBA8),N===i.UNSIGNED_SHORT_4_4_4_4&&(H=i.RGBA4),N===i.UNSIGNED_SHORT_5_5_5_1&&(H=i.RGB5_A1)}return(H===i.R16F||H===i.R32F||H===i.RG16F||H===i.RG32F||H===i.RGBA16F||H===i.RGBA32F)&&t.get("EXT_color_buffer_float"),H}function T(A,g){let N;return A?g===null||g===jn||g===ts?N=i.DEPTH24_STENCIL8:g===on?N=i.DEPTH32F_STENCIL8:g===Qi&&(N=i.DEPTH24_STENCIL8,Bt("DepthTexture: 16 bit depth attachment is not supported with stencil. Using 24-bit attachment.")):g===null||g===jn||g===ts?N=i.DEPTH_COMPONENT24:g===on?N=i.DEPTH_COMPONENT32F:g===Qi&&(N=i.DEPTH_COMPONENT16),N}function R(A,g){return m(A)===!0||A.isFramebufferTexture&&A.minFilter!==He&&A.minFilter!==Ye?Math.log2(Math.max(g.width,g.height))+1:A.mipmaps!==void 0&&A.mipmaps.length>0?A.mipmaps.length:A.isCompressedTexture&&Array.isArray(A.image)?g.mipmaps.length:1}function w(A){const g=A.target;g.removeEventListener("dispose",w),F(g),g.isVideoTexture&&h.delete(g)}function L(A){const g=A.target;g.removeEventListener("dispose",L),M(g)}function F(A){const g=n.get(A);if(g.__webglInit===void 0)return;const N=A.source,q=d.get(N);if(q){const $=q[g.__cacheKey];$.usedTimes--,$.usedTimes===0&&E(A),Object.keys(q).length===0&&d.delete(N)}n.remove(A)}function E(A){const g=n.get(A);i.deleteTexture(g.__webglTexture);const N=A.source,q=d.get(N);delete q[g.__cacheKey],a.memory.textures--}function M(A){const g=n.get(A);if(A.depthTexture&&(A.depthTexture.dispose(),n.remove(A.depthTexture)),A.isWebGLCubeRenderTarget)for(let q=0;q<6;q++){if(Array.isArray(g.__webglFramebuffer[q]))for(let $=0;$=s.maxTextures&&Bt("WebGLTextures: Trying to use "+A+" texture units while this GPU supports only "+s.maxTextures),P+=1,A}function k(A){const g=[];return g.push(A.wrapS),g.push(A.wrapT),g.push(A.wrapR||0),g.push(A.magFilter),g.push(A.minFilter),g.push(A.anisotropy),g.push(A.internalFormat),g.push(A.format),g.push(A.type),g.push(A.generateMipmaps),g.push(A.premultiplyAlpha),g.push(A.flipY),g.push(A.unpackAlignment),g.push(A.colorSpace),g.join()}function W(A,g){const N=n.get(A);if(A.isVideoTexture&&vt(A),A.isRenderTargetTexture===!1&&A.isExternalTexture!==!0&&A.version>0&&N.__version!==A.version){const q=A.image;if(q===null)Bt("WebGLRenderer: Texture marked for update but no image data found.");else if(q.complete===!1)Bt("WebGLRenderer: Texture marked for update but image is incomplete");else{K(N,A,g);return}}else A.isExternalTexture&&(N.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(i.TEXTURE_2D,N.__webglTexture,i.TEXTURE0+g)}function Y(A,g){const N=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&N.__version!==A.version){K(N,A,g);return}else A.isExternalTexture&&(N.__webglTexture=A.sourceTexture?A.sourceTexture:null);e.bindTexture(i.TEXTURE_2D_ARRAY,N.__webglTexture,i.TEXTURE0+g)}function nt(A,g){const N=n.get(A);if(A.isRenderTargetTexture===!1&&A.version>0&&N.__version!==A.version){K(N,A,g);return}e.bindTexture(i.TEXTURE_3D,N.__webglTexture,i.TEXTURE0+g)}function X(A,g){const N=n.get(A);if(A.version>0&&N.__version!==A.version){J(N,A,g);return}e.bindTexture(i.TEXTURE_CUBE_MAP,N.__webglTexture,i.TEXTURE0+g)}const rt={[$r]:i.REPEAT,[Sn]:i.CLAMP_TO_EDGE,[Qr]:i.MIRRORED_REPEAT},st={[He]:i.NEAREST,[Yl]:i.NEAREST_MIPMAP_NEAREST,[us]:i.NEAREST_MIPMAP_LINEAR,[Ye]:i.LINEAR,[ir]:i.LINEAR_MIPMAP_NEAREST,[Kn]:i.LINEAR_MIPMAP_LINEAR},St={[Jl]:i.NEVER,[ih]:i.ALWAYS,[$l]:i.LESS,[Ic]:i.LEQUAL,[Ql]:i.EQUAL,[nh]:i.GEQUAL,[th]:i.GREATER,[eh]:i.NOTEQUAL};function Gt(A,g){if(g.type===on&&t.has("OES_texture_float_linear")===!1&&(g.magFilter===Ye||g.magFilter===ir||g.magFilter===us||g.magFilter===Kn||g.minFilter===Ye||g.minFilter===ir||g.minFilter===us||g.minFilter===Kn)&&Bt("WebGLRenderer: Unable to use linear filtering with floating point textures. OES_texture_float_linear not supported on this device."),i.texParameteri(A,i.TEXTURE_WRAP_S,rt[g.wrapS]),i.texParameteri(A,i.TEXTURE_WRAP_T,rt[g.wrapT]),(A===i.TEXTURE_3D||A===i.TEXTURE_2D_ARRAY)&&i.texParameteri(A,i.TEXTURE_WRAP_R,rt[g.wrapR]),i.texParameteri(A,i.TEXTURE_MAG_FILTER,st[g.magFilter]),i.texParameteri(A,i.TEXTURE_MIN_FILTER,st[g.minFilter]),g.compareFunction&&(i.texParameteri(A,i.TEXTURE_COMPARE_MODE,i.COMPARE_REF_TO_TEXTURE),i.texParameteri(A,i.TEXTURE_COMPARE_FUNC,St[g.compareFunction])),t.has("EXT_texture_filter_anisotropic")===!0){if(g.magFilter===He||g.minFilter!==us&&g.minFilter!==Kn||g.type===on&&t.has("OES_texture_float_linear")===!1)return;if(g.anisotropy>1||n.get(g).__currentAnisotropy){const N=t.get("EXT_texture_filter_anisotropic");i.texParameterf(A,N.TEXTURE_MAX_ANISOTROPY_EXT,Math.min(g.anisotropy,s.getMaxAnisotropy())),n.get(g).__currentAnisotropy=g.anisotropy}}}function qt(A,g){let N=!1;A.__webglInit===void 0&&(A.__webglInit=!0,g.addEventListener("dispose",w));const q=g.source;let $=d.get(q);$===void 0&&($={},d.set(q,$));const H=k(g);if(H!==A.__cacheKey){$[H]===void 0&&($[H]={texture:i.createTexture(),usedTimes:0},a.memory.textures++,N=!0),$[H].usedTimes++;const wt=$[A.__cacheKey];wt!==void 0&&($[A.__cacheKey].usedTimes--,wt.usedTimes===0&&E(g)),A.__cacheKey=H,A.__webglTexture=$[H].texture}return N}function jt(A,g,N){return Math.floor(Math.floor(A/N)/g)}function Kt(A,g,N,q){const H=A.updateRanges;if(H.length===0)e.texSubImage2D(i.TEXTURE_2D,0,0,0,g.width,g.height,N,q,g.data);else{H.sort((tt,lt)=>tt.start-lt.start);let wt=0;for(let tt=1;tt0){C&&mt&&e.texStorage2D(i.TEXTURE_2D,ut,It,Ot[0].width,Ot[0].height);for(let it=0,j=Ot.length;it0){const bt=Jo(_t.width,_t.height,g.format,g.type);for(const zt of g.layerUpdates){const ce=_t.data.subarray(zt*bt/_t.data.BYTES_PER_ELEMENT,(zt+1)*bt/_t.data.BYTES_PER_ELEMENT);e.compressedTexSubImage3D(i.TEXTURE_2D_ARRAY,it,0,0,zt,_t.width,_t.height,1,lt,ce)}g.clearLayerUpdates()}else e.compressedTexSubImage3D(i.TEXTURE_2D_ARRAY,it,0,0,0,_t.width,_t.height,tt.depth,lt,_t.data)}else e.compressedTexImage3D(i.TEXTURE_2D_ARRAY,it,It,_t.width,_t.height,tt.depth,0,_t.data,0,0);else Bt("WebGLRenderer: Attempt to load unsupported compressed texture format in .uploadTexture()");else C?ht&&e.texSubImage3D(i.TEXTURE_2D_ARRAY,it,0,0,0,_t.width,_t.height,tt.depth,lt,Nt,_t.data):e.texImage3D(i.TEXTURE_2D_ARRAY,it,It,_t.width,_t.height,tt.depth,0,lt,Nt,_t.data)}else{C&&mt&&e.texStorage2D(i.TEXTURE_2D,ut,It,Ot[0].width,Ot[0].height);for(let it=0,j=Ot.length;it0){const it=Jo(tt.width,tt.height,g.format,g.type);for(const j of g.layerUpdates){const bt=tt.data.subarray(j*it/tt.data.BYTES_PER_ELEMENT,(j+1)*it/tt.data.BYTES_PER_ELEMENT);e.texSubImage3D(i.TEXTURE_2D_ARRAY,0,0,0,j,tt.width,tt.height,1,lt,Nt,bt)}g.clearLayerUpdates()}else e.texSubImage3D(i.TEXTURE_2D_ARRAY,0,0,0,0,tt.width,tt.height,tt.depth,lt,Nt,tt.data)}else e.texImage3D(i.TEXTURE_2D_ARRAY,0,It,tt.width,tt.height,tt.depth,0,lt,Nt,tt.data);else if(g.isData3DTexture)C?(mt&&e.texStorage3D(i.TEXTURE_3D,ut,It,tt.width,tt.height,tt.depth),ht&&e.texSubImage3D(i.TEXTURE_3D,0,0,0,0,tt.width,tt.height,tt.depth,lt,Nt,tt.data)):e.texImage3D(i.TEXTURE_3D,0,It,tt.width,tt.height,tt.depth,0,lt,Nt,tt.data);else if(g.isFramebufferTexture){if(mt)if(C)e.texStorage2D(i.TEXTURE_2D,ut,It,tt.width,tt.height);else{let it=tt.width,j=tt.height;for(let bt=0;bt>=1,j>>=1}}else if(Ot.length>0){if(C&&mt){const it=Mt(Ot[0]);e.texStorage2D(i.TEXTURE_2D,ut,It,it.width,it.height)}for(let it=0,j=Ot.length;it0&&ut++;const j=Mt(lt[0]);e.texStorage2D(i.TEXTURE_CUBE_MAP,ut,Ot,j.width,j.height)}for(let j=0;j<6;j++)if(tt){C?ht&&e.texSubImage2D(i.TEXTURE_CUBE_MAP_POSITIVE_X+j,0,0,0,lt[j].width,lt[j].height,It,_t,lt[j].data):e.texImage2D(i.TEXTURE_CUBE_MAP_POSITIVE_X+j,0,Ot,lt[j].width,lt[j].height,0,It,_t,lt[j].data);for(let bt=0;bt>H),Nt=Math.max(1,g.height>>H);$===i.TEXTURE_3D||$===i.TEXTURE_2D_ARRAY?e.texImage3D($,H,Pt,lt,Nt,g.depth,0,wt,dt,null):e.texImage2D($,H,Pt,lt,Nt,0,wt,dt,null)}e.bindFramebuffer(i.FRAMEBUFFER,A),et(g)?o.framebufferTexture2DMultisampleEXT(i.FRAMEBUFFER,q,$,tt.__webglTexture,0,ot(g)):($===i.TEXTURE_2D||$>=i.TEXTURE_CUBE_MAP_POSITIVE_X&&$<=i.TEXTURE_CUBE_MAP_NEGATIVE_Z)&&i.framebufferTexture2D(i.FRAMEBUFFER,q,$,tt.__webglTexture,H),e.bindFramebuffer(i.FRAMEBUFFER,null)}function Rt(A,g,N){if(i.bindRenderbuffer(i.RENDERBUFFER,A),g.depthBuffer){const q=g.depthTexture,$=q&&q.isDepthTexture?q.type:null,H=T(g.stencilBuffer,$),wt=g.stencilBuffer?i.DEPTH_STENCIL_ATTACHMENT:i.DEPTH_ATTACHMENT,dt=ot(g);et(g)?o.renderbufferStorageMultisampleEXT(i.RENDERBUFFER,dt,H,g.width,g.height):N?i.renderbufferStorageMultisample(i.RENDERBUFFER,dt,H,g.width,g.height):i.renderbufferStorage(i.RENDERBUFFER,H,g.width,g.height),i.framebufferRenderbuffer(i.FRAMEBUFFER,wt,i.RENDERBUFFER,A)}else{const q=g.textures;for(let $=0;${delete g.__boundDepthTexture,delete g.__depthDisposeCallback,q.removeEventListener("dispose",$)};q.addEventListener("dispose",$),g.__depthDisposeCallback=$}g.__boundDepthTexture=q}if(A.depthTexture&&!g.__autoAllocateDepthBuffer){if(N)throw new Error("target.depthTexture not supported in Cube render targets");const q=A.texture.mipmaps;q&&q.length>0?Tt(g.__webglFramebuffer[0],A):Tt(g.__webglFramebuffer,A)}else if(N){g.__webglDepthbuffer=[];for(let q=0;q<6;q++)if(e.bindFramebuffer(i.FRAMEBUFFER,g.__webglFramebuffer[q]),g.__webglDepthbuffer[q]===void 0)g.__webglDepthbuffer[q]=i.createRenderbuffer(),Rt(g.__webglDepthbuffer[q],A,!1);else{const $=A.stencilBuffer?i.DEPTH_STENCIL_ATTACHMENT:i.DEPTH_ATTACHMENT,H=g.__webglDepthbuffer[q];i.bindRenderbuffer(i.RENDERBUFFER,H),i.framebufferRenderbuffer(i.FRAMEBUFFER,$,i.RENDERBUFFER,H)}}else{const q=A.texture.mipmaps;if(q&&q.length>0?e.bindFramebuffer(i.FRAMEBUFFER,g.__webglFramebuffer[0]):e.bindFramebuffer(i.FRAMEBUFFER,g.__webglFramebuffer),g.__webglDepthbuffer===void 0)g.__webglDepthbuffer=i.createRenderbuffer(),Rt(g.__webglDepthbuffer,A,!1);else{const $=A.stencilBuffer?i.DEPTH_STENCIL_ATTACHMENT:i.DEPTH_ATTACHMENT,H=g.__webglDepthbuffer;i.bindRenderbuffer(i.RENDERBUFFER,H),i.framebufferRenderbuffer(i.FRAMEBUFFER,$,i.RENDERBUFFER,H)}}e.bindFramebuffer(i.FRAMEBUFFER,null)}function Qt(A,g,N){const q=n.get(A);g!==void 0&&xt(q.__webglFramebuffer,A,A.texture,i.COLOR_ATTACHMENT0,i.TEXTURE_2D,0),N!==void 0&&kt(A)}function Ct(A){const g=A.texture,N=n.get(A),q=n.get(g);A.addEventListener("dispose",L);const $=A.textures,H=A.isWebGLCubeRenderTarget===!0,wt=$.length>1;if(wt||(q.__webglTexture===void 0&&(q.__webglTexture=i.createTexture()),q.__version=g.version,a.memory.textures++),H){N.__webglFramebuffer=[];for(let dt=0;dt<6;dt++)if(g.mipmaps&&g.mipmaps.length>0){N.__webglFramebuffer[dt]=[];for(let Pt=0;Pt0){N.__webglFramebuffer=[];for(let dt=0;dt0&&et(A)===!1){N.__webglMultisampledFramebuffer=i.createFramebuffer(),N.__webglColorRenderbuffer=[],e.bindFramebuffer(i.FRAMEBUFFER,N.__webglMultisampledFramebuffer);for(let dt=0;dt<$.length;dt++){const Pt=$[dt];N.__webglColorRenderbuffer[dt]=i.createRenderbuffer(),i.bindRenderbuffer(i.RENDERBUFFER,N.__webglColorRenderbuffer[dt]);const At=r.convert(Pt.format,Pt.colorSpace),tt=r.convert(Pt.type),lt=S(Pt.internalFormat,At,tt,Pt.colorSpace,A.isXRRenderTarget===!0),Nt=ot(A);i.renderbufferStorageMultisample(i.RENDERBUFFER,Nt,lt,A.width,A.height),i.framebufferRenderbuffer(i.FRAMEBUFFER,i.COLOR_ATTACHMENT0+dt,i.RENDERBUFFER,N.__webglColorRenderbuffer[dt])}i.bindRenderbuffer(i.RENDERBUFFER,null),A.depthBuffer&&(N.__webglDepthRenderbuffer=i.createRenderbuffer(),Rt(N.__webglDepthRenderbuffer,A,!0)),e.bindFramebuffer(i.FRAMEBUFFER,null)}}if(H){e.bindTexture(i.TEXTURE_CUBE_MAP,q.__webglTexture),Gt(i.TEXTURE_CUBE_MAP,g);for(let dt=0;dt<6;dt++)if(g.mipmaps&&g.mipmaps.length>0)for(let Pt=0;Pt0)for(let Pt=0;Pt0){if(et(A)===!1){const g=A.textures,N=A.width,q=A.height;let $=i.COLOR_BUFFER_BIT;const H=A.stencilBuffer?i.DEPTH_STENCIL_ATTACHMENT:i.DEPTH_ATTACHMENT,wt=n.get(A),dt=g.length>1;if(dt)for(let At=0;At0?e.bindFramebuffer(i.DRAW_FRAMEBUFFER,wt.__webglFramebuffer[0]):e.bindFramebuffer(i.DRAW_FRAMEBUFFER,wt.__webglFramebuffer);for(let At=0;At0&&t.has("WEBGL_multisampled_render_to_texture")===!0&&g.__useRenderToTexture!==!1}function vt(A){const g=a.render.frame;h.get(A)!==g&&(h.set(A,g),A.update())}function ft(A,g){const N=A.colorSpace,q=A.format,$=A.type;return A.isCompressedTexture===!0||A.isVideoTexture===!0||N!==Ri&&N!==Ln&&($t.getTransfer(N)===ne?(q!==tn||$!==hn)&&Bt("WebGLTextures: sRGB encoded textures have to use RGBAFormat and UnsignedByteType."):de("WebGLTextures: Unsupported texture color space:",N)),g}function Mt(A){return typeof HTMLImageElement<"u"&&A instanceof HTMLImageElement?(c.width=A.naturalWidth||A.width,c.height=A.naturalHeight||A.height):typeof VideoFrame<"u"&&A instanceof VideoFrame?(c.width=A.displayWidth,c.height=A.displayHeight):(c.width=A.width,c.height=A.height),c}this.allocateTextureUnit=z,this.resetTextureUnits=O,this.setTexture2D=W,this.setTexture2DArray=Y,this.setTexture3D=nt,this.setTextureCube=X,this.rebindTextures=Qt,this.setupRenderTarget=Ct,this.updateRenderTargetMipmap=B,this.updateMultisampleRenderTarget=Q,this.setupDepthRenderbuffer=kt,this.setupFrameBufferTexture=xt,this.useMultisampledRTT=et}function om(i,t){function e(n,s=Ln){let r;const a=$t.getTransfer(s);if(n===hn)return i.UNSIGNED_BYTE;if(n===Ba)return i.UNSIGNED_SHORT_4_4_4_4;if(n===za)return i.UNSIGNED_SHORT_5_5_5_1;if(n===Cc)return i.UNSIGNED_INT_5_9_9_9_REV;if(n===Pc)return i.UNSIGNED_INT_10F_11F_11F_REV;if(n===wc)return i.BYTE;if(n===Rc)return i.SHORT;if(n===Qi)return i.UNSIGNED_SHORT;if(n===Oa)return i.INT;if(n===jn)return i.UNSIGNED_INT;if(n===on)return i.FLOAT;if(n===Di)return i.HALF_FLOAT;if(n===Dc)return i.ALPHA;if(n===Lc)return i.RGB;if(n===tn)return i.RGBA;if(n===es)return i.DEPTH_COMPONENT;if(n===ns)return i.DEPTH_STENCIL;if(n===Va)return i.RED;if(n===Ga)return i.RED_INTEGER;if(n===ka)return i.RG;if(n===Ha)return i.RG_INTEGER;if(n===Wa)return i.RGBA_INTEGER;if(n===Bs||n===zs||n===Vs||n===Gs)if(a===ne)if(r=t.get("WEBGL_compressed_texture_s3tc_srgb"),r!==null){if(n===Bs)return r.COMPRESSED_SRGB_S3TC_DXT1_EXT;if(n===zs)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;if(n===Vs)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;if(n===Gs)return r.COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT}else return null;else if(r=t.get("WEBGL_compressed_texture_s3tc"),r!==null){if(n===Bs)return r.COMPRESSED_RGB_S3TC_DXT1_EXT;if(n===zs)return r.COMPRESSED_RGBA_S3TC_DXT1_EXT;if(n===Vs)return r.COMPRESSED_RGBA_S3TC_DXT3_EXT;if(n===Gs)return r.COMPRESSED_RGBA_S3TC_DXT5_EXT}else return null;if(n===ta||n===ea||n===na||n===ia)if(r=t.get("WEBGL_compressed_texture_pvrtc"),r!==null){if(n===ta)return r.COMPRESSED_RGB_PVRTC_4BPPV1_IMG;if(n===ea)return r.COMPRESSED_RGB_PVRTC_2BPPV1_IMG;if(n===na)return r.COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;if(n===ia)return r.COMPRESSED_RGBA_PVRTC_2BPPV1_IMG}else return null;if(n===sa||n===ra||n===aa)if(r=t.get("WEBGL_compressed_texture_etc"),r!==null){if(n===sa||n===ra)return a===ne?r.COMPRESSED_SRGB8_ETC2:r.COMPRESSED_RGB8_ETC2;if(n===aa)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:r.COMPRESSED_RGBA8_ETC2_EAC}else return null;if(n===oa||n===ca||n===la||n===ha||n===ua||n===fa||n===da||n===pa||n===ma||n===xa||n===ga||n===_a||n===va||n===Ma)if(r=t.get("WEBGL_compressed_texture_astc"),r!==null){if(n===oa)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:r.COMPRESSED_RGBA_ASTC_4x4_KHR;if(n===ca)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:r.COMPRESSED_RGBA_ASTC_5x4_KHR;if(n===la)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:r.COMPRESSED_RGBA_ASTC_5x5_KHR;if(n===ha)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:r.COMPRESSED_RGBA_ASTC_6x5_KHR;if(n===ua)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:r.COMPRESSED_RGBA_ASTC_6x6_KHR;if(n===fa)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:r.COMPRESSED_RGBA_ASTC_8x5_KHR;if(n===da)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:r.COMPRESSED_RGBA_ASTC_8x6_KHR;if(n===pa)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:r.COMPRESSED_RGBA_ASTC_8x8_KHR;if(n===ma)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:r.COMPRESSED_RGBA_ASTC_10x5_KHR;if(n===xa)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:r.COMPRESSED_RGBA_ASTC_10x6_KHR;if(n===ga)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:r.COMPRESSED_RGBA_ASTC_10x8_KHR;if(n===_a)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:r.COMPRESSED_RGBA_ASTC_10x10_KHR;if(n===va)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:r.COMPRESSED_RGBA_ASTC_12x10_KHR;if(n===Ma)return a===ne?r.COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:r.COMPRESSED_RGBA_ASTC_12x12_KHR}else return null;if(n===Sa||n===ya||n===ba)if(r=t.get("EXT_texture_compression_bptc"),r!==null){if(n===Sa)return a===ne?r.COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT:r.COMPRESSED_RGBA_BPTC_UNORM_EXT;if(n===ya)return r.COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT;if(n===ba)return r.COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT}else return null;if(n===Ea||n===Ta||n===Aa||n===wa)if(r=t.get("EXT_texture_compression_rgtc"),r!==null){if(n===Ea)return r.COMPRESSED_RED_RGTC1_EXT;if(n===Ta)return r.COMPRESSED_SIGNED_RED_RGTC1_EXT;if(n===Aa)return r.COMPRESSED_RED_GREEN_RGTC2_EXT;if(n===wa)return r.COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT}else return null;return n===ts?i.UNSIGNED_INT_24_8:i[n]!==void 0?i[n]:null}return{convert:e}}const cm=` -void main() { - - gl_Position = vec4( position, 1.0 ); - -}`,lm=` -uniform sampler2DArray depthColor; -uniform float depthWidth; -uniform float depthHeight; - -void main() { - - vec2 coord = vec2( gl_FragCoord.x / depthWidth, gl_FragCoord.y / depthHeight ); - - if ( coord.x >= 1.0 ) { - - gl_FragDepth = texture( depthColor, vec3( coord.x - 1.0, coord.y, 1 ) ).r; - - } else { - - gl_FragDepth = texture( depthColor, vec3( coord.x, coord.y, 0 ) ).r; - - } - -}`;class hm{constructor(){this.texture=null,this.mesh=null,this.depthNear=0,this.depthFar=0}init(t,e){if(this.texture===null){const n=new qc(t.texture);(t.depthNear!==e.depthNear||t.depthFar!==e.depthFar)&&(this.depthNear=t.depthNear,this.depthFar=t.depthFar),this.texture=n}}getMesh(t){if(this.texture!==null&&this.mesh===null){const e=t.cameras[0].viewport,n=new En({vertexShader:cm,fragmentShader:lm,uniforms:{depthColor:{value:this.texture},depthWidth:{value:e.z},depthHeight:{value:e.w}}});this.mesh=new Vt(new Nn(20,20),n)}return this.mesh}reset(){this.texture=null,this.mesh=null}getDepthTexture(){return this.texture}}class um extends ti{constructor(t,e){super();const n=this;let s=null,r=1,a=null,o="local-floor",l=1,c=null,h=null,u=null,d=null,p=null,x=null;const _=typeof XRWebGLBinding<"u",m=new hm,f={},b=e.getContextAttributes();let S=null,T=null;const R=[],w=[],L=new at;let F=null;const E=new ke;E.viewport=new se;const M=new ke;M.viewport=new se;const P=[E,M],O=new wu;let z=null,k=null;this.cameraAutoUpdate=!0,this.enabled=!1,this.isPresenting=!1,this.getController=function(K){let J=R[K];return J===void 0&&(J=new Tr,R[K]=J),J.getTargetRaySpace()},this.getControllerGrip=function(K){let J=R[K];return J===void 0&&(J=new Tr,R[K]=J),J.getGripSpace()},this.getHand=function(K){let J=R[K];return J===void 0&&(J=new Tr,R[K]=J),J.getHandSpace()};function W(K){const J=w.indexOf(K.inputSource);if(J===-1)return;const xt=R[J];xt!==void 0&&(xt.update(K.inputSource,K.frame,c||a),xt.dispatchEvent({type:K.type,data:K.inputSource}))}function Y(){s.removeEventListener("select",W),s.removeEventListener("selectstart",W),s.removeEventListener("selectend",W),s.removeEventListener("squeeze",W),s.removeEventListener("squeezestart",W),s.removeEventListener("squeezeend",W),s.removeEventListener("end",Y),s.removeEventListener("inputsourceschange",nt);for(let K=0;K=0&&(w[Rt]=null,R[Rt].disconnect(xt))}for(let J=0;J=w.length){w.push(xt),Rt=kt;break}else if(w[kt]===null){w[kt]=xt,Rt=kt;break}if(Rt===-1)break}const Tt=R[Rt];Tt&&Tt.connect(xt)}}const X=new D,rt=new D;function st(K,J,xt){X.setFromMatrixPosition(J.matrixWorld),rt.setFromMatrixPosition(xt.matrixWorld);const Rt=X.distanceTo(rt),Tt=J.projectionMatrix.elements,kt=xt.projectionMatrix.elements,Qt=Tt[14]/(Tt[10]-1),Ct=Tt[14]/(Tt[10]+1),B=(Tt[9]+1)/Tt[5],y=(Tt[9]-1)/Tt[5],Z=(Tt[8]-1)/Tt[0],Q=(kt[8]+1)/kt[0],ot=Qt*Z,et=Qt*Q,vt=Rt/(-Z+Q),ft=vt*-Z;if(J.matrixWorld.decompose(K.position,K.quaternion,K.scale),K.translateX(ft),K.translateZ(vt),K.matrixWorld.compose(K.position,K.quaternion,K.scale),K.matrixWorldInverse.copy(K.matrixWorld).invert(),Tt[10]===-1)K.projectionMatrix.copy(J.projectionMatrix),K.projectionMatrixInverse.copy(J.projectionMatrixInverse);else{const Mt=Qt+vt,A=Ct+vt,g=ot-ft,N=et+(Rt-ft),q=B*Ct/A*Mt,$=y*Ct/A*Mt;K.projectionMatrix.makePerspective(g,N,q,$,Mt,A),K.projectionMatrixInverse.copy(K.projectionMatrix).invert()}}function St(K,J){J===null?K.matrixWorld.copy(K.matrix):K.matrixWorld.multiplyMatrices(J.matrixWorld,K.matrix),K.matrixWorldInverse.copy(K.matrixWorld).invert()}this.updateCamera=function(K){if(s===null)return;let J=K.near,xt=K.far;m.texture!==null&&(m.depthNear>0&&(J=m.depthNear),m.depthFar>0&&(xt=m.depthFar)),O.near=M.near=E.near=J,O.far=M.far=E.far=xt,(z!==O.near||k!==O.far)&&(s.updateRenderState({depthNear:O.near,depthFar:O.far}),z=O.near,k=O.far),O.layers.mask=K.layers.mask|6,E.layers.mask=O.layers.mask&3,M.layers.mask=O.layers.mask&5;const Rt=K.parent,Tt=O.cameras;St(O,Rt);for(let kt=0;kt0&&(m.alphaTest.value=f.alphaTest);const b=t.get(f),S=b.envMap,T=b.envMapRotation;S&&(m.envMap.value=S,Xn.copy(T),Xn.x*=-1,Xn.y*=-1,Xn.z*=-1,S.isCubeTexture&&S.isRenderTargetTexture===!1&&(Xn.y*=-1,Xn.z*=-1),m.envMapRotation.value.setFromMatrix4(fm.makeRotationFromEuler(Xn)),m.flipEnvMap.value=S.isCubeTexture&&S.isRenderTargetTexture===!1?-1:1,m.reflectivity.value=f.reflectivity,m.ior.value=f.ior,m.refractionRatio.value=f.refractionRatio),f.lightMap&&(m.lightMap.value=f.lightMap,m.lightMapIntensity.value=f.lightMapIntensity,e(f.lightMap,m.lightMapTransform)),f.aoMap&&(m.aoMap.value=f.aoMap,m.aoMapIntensity.value=f.aoMapIntensity,e(f.aoMap,m.aoMapTransform))}function a(m,f){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,f.map&&(m.map.value=f.map,e(f.map,m.mapTransform))}function o(m,f){m.dashSize.value=f.dashSize,m.totalSize.value=f.dashSize+f.gapSize,m.scale.value=f.scale}function l(m,f,b,S){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,m.size.value=f.size*b,m.scale.value=S*.5,f.map&&(m.map.value=f.map,e(f.map,m.uvTransform)),f.alphaMap&&(m.alphaMap.value=f.alphaMap,e(f.alphaMap,m.alphaMapTransform)),f.alphaTest>0&&(m.alphaTest.value=f.alphaTest)}function c(m,f){m.diffuse.value.copy(f.color),m.opacity.value=f.opacity,m.rotation.value=f.rotation,f.map&&(m.map.value=f.map,e(f.map,m.mapTransform)),f.alphaMap&&(m.alphaMap.value=f.alphaMap,e(f.alphaMap,m.alphaMapTransform)),f.alphaTest>0&&(m.alphaTest.value=f.alphaTest)}function h(m,f){m.specular.value.copy(f.specular),m.shininess.value=Math.max(f.shininess,1e-4)}function u(m,f){f.gradientMap&&(m.gradientMap.value=f.gradientMap)}function d(m,f){m.metalness.value=f.metalness,f.metalnessMap&&(m.metalnessMap.value=f.metalnessMap,e(f.metalnessMap,m.metalnessMapTransform)),m.roughness.value=f.roughness,f.roughnessMap&&(m.roughnessMap.value=f.roughnessMap,e(f.roughnessMap,m.roughnessMapTransform)),f.envMap&&(m.envMapIntensity.value=f.envMapIntensity)}function p(m,f,b){m.ior.value=f.ior,f.sheen>0&&(m.sheenColor.value.copy(f.sheenColor).multiplyScalar(f.sheen),m.sheenRoughness.value=f.sheenRoughness,f.sheenColorMap&&(m.sheenColorMap.value=f.sheenColorMap,e(f.sheenColorMap,m.sheenColorMapTransform)),f.sheenRoughnessMap&&(m.sheenRoughnessMap.value=f.sheenRoughnessMap,e(f.sheenRoughnessMap,m.sheenRoughnessMapTransform))),f.clearcoat>0&&(m.clearcoat.value=f.clearcoat,m.clearcoatRoughness.value=f.clearcoatRoughness,f.clearcoatMap&&(m.clearcoatMap.value=f.clearcoatMap,e(f.clearcoatMap,m.clearcoatMapTransform)),f.clearcoatRoughnessMap&&(m.clearcoatRoughnessMap.value=f.clearcoatRoughnessMap,e(f.clearcoatRoughnessMap,m.clearcoatRoughnessMapTransform)),f.clearcoatNormalMap&&(m.clearcoatNormalMap.value=f.clearcoatNormalMap,e(f.clearcoatNormalMap,m.clearcoatNormalMapTransform),m.clearcoatNormalScale.value.copy(f.clearcoatNormalScale),f.side===Le&&m.clearcoatNormalScale.value.negate())),f.dispersion>0&&(m.dispersion.value=f.dispersion),f.iridescence>0&&(m.iridescence.value=f.iridescence,m.iridescenceIOR.value=f.iridescenceIOR,m.iridescenceThicknessMinimum.value=f.iridescenceThicknessRange[0],m.iridescenceThicknessMaximum.value=f.iridescenceThicknessRange[1],f.iridescenceMap&&(m.iridescenceMap.value=f.iridescenceMap,e(f.iridescenceMap,m.iridescenceMapTransform)),f.iridescenceThicknessMap&&(m.iridescenceThicknessMap.value=f.iridescenceThicknessMap,e(f.iridescenceThicknessMap,m.iridescenceThicknessMapTransform))),f.transmission>0&&(m.transmission.value=f.transmission,m.transmissionSamplerMap.value=b.texture,m.transmissionSamplerSize.value.set(b.width,b.height),f.transmissionMap&&(m.transmissionMap.value=f.transmissionMap,e(f.transmissionMap,m.transmissionMapTransform)),m.thickness.value=f.thickness,f.thicknessMap&&(m.thicknessMap.value=f.thicknessMap,e(f.thicknessMap,m.thicknessMapTransform)),m.attenuationDistance.value=f.attenuationDistance,m.attenuationColor.value.copy(f.attenuationColor)),f.anisotropy>0&&(m.anisotropyVector.value.set(f.anisotropy*Math.cos(f.anisotropyRotation),f.anisotropy*Math.sin(f.anisotropyRotation)),f.anisotropyMap&&(m.anisotropyMap.value=f.anisotropyMap,e(f.anisotropyMap,m.anisotropyMapTransform))),m.specularIntensity.value=f.specularIntensity,m.specularColor.value.copy(f.specularColor),f.specularColorMap&&(m.specularColorMap.value=f.specularColorMap,e(f.specularColorMap,m.specularColorMapTransform)),f.specularIntensityMap&&(m.specularIntensityMap.value=f.specularIntensityMap,e(f.specularIntensityMap,m.specularIntensityMapTransform))}function x(m,f){f.matcap&&(m.matcap.value=f.matcap)}function _(m,f){const b=t.get(f).light;m.referencePosition.value.setFromMatrixPosition(b.matrixWorld),m.nearDistance.value=b.shadow.camera.near,m.farDistance.value=b.shadow.camera.far}return{refreshFogUniforms:n,refreshMaterialUniforms:s}}function pm(i,t,e,n){let s={},r={},a=[];const o=i.getParameter(i.MAX_UNIFORM_BUFFER_BINDINGS);function l(b,S){const T=S.program;n.uniformBlockBinding(b,T)}function c(b,S){let T=s[b.id];T===void 0&&(x(b),T=h(b),s[b.id]=T,b.addEventListener("dispose",m));const R=S.program;n.updateUBOMapping(b,R);const w=t.render.frame;r[b.id]!==w&&(d(b),r[b.id]=w)}function h(b){const S=u();b.__bindingPointIndex=S;const T=i.createBuffer(),R=b.__size,w=b.usage;return i.bindBuffer(i.UNIFORM_BUFFER,T),i.bufferData(i.UNIFORM_BUFFER,R,w),i.bindBuffer(i.UNIFORM_BUFFER,null),i.bindBufferBase(i.UNIFORM_BUFFER,S,T),T}function u(){for(let b=0;b0&&(T+=R-w),b.__size=T,b.__cache={},this}function _(b){const S={boundary:0,storage:0};return typeof b=="number"||typeof b=="boolean"?(S.boundary=4,S.storage=4):b.isVector2?(S.boundary=8,S.storage=8):b.isVector3||b.isColor?(S.boundary=16,S.storage=12):b.isVector4?(S.boundary=16,S.storage=16):b.isMatrix3?(S.boundary=48,S.storage=48):b.isMatrix4?(S.boundary=64,S.storage=64):b.isTexture?Bt("WebGLRenderer: Texture samplers can not be part of an uniforms group."):Bt("WebGLRenderer: Unsupported uniform value type.",b),S}function m(b){const S=b.target;S.removeEventListener("dispose",m);const T=a.indexOf(S.__bindingPointIndex);a.splice(T,1),i.deleteBuffer(s[S.id]),delete s[S.id],delete r[S.id]}function f(){for(const b in s)i.deleteBuffer(s[b]);a=[],s={},r={}}return{bind:l,update:c,dispose:f}}const mm=new Uint16Array([11481,15204,11534,15171,11808,15015,12385,14843,12894,14716,13396,14600,13693,14483,13976,14366,14237,14171,14405,13961,14511,13770,14605,13598,14687,13444,14760,13305,14822,13066,14876,12857,14923,12675,14963,12517,14997,12379,15025,12230,15049,12023,15070,11843,15086,11687,15100,11551,15111,11433,15120,11330,15127,11217,15132,11060,15135,10922,15138,10801,15139,10695,15139,10600,13012,14923,13020,14917,13064,14886,13176,14800,13349,14666,13513,14526,13724,14398,13960,14230,14200,14020,14383,13827,14488,13651,14583,13491,14667,13348,14740,13132,14803,12908,14856,12713,14901,12542,14938,12394,14968,12241,14992,12017,15010,11822,15024,11654,15034,11507,15041,11380,15044,11269,15044,11081,15042,10913,15037,10764,15031,10635,15023,10520,15014,10419,15003,10330,13657,14676,13658,14673,13670,14660,13698,14622,13750,14547,13834,14442,13956,14317,14112,14093,14291,13889,14407,13704,14499,13538,14586,13389,14664,13201,14733,12966,14792,12758,14842,12577,14882,12418,14915,12272,14940,12033,14959,11826,14972,11646,14980,11490,14983,11355,14983,11212,14979,11008,14971,10830,14961,10675,14950,10540,14936,10420,14923,10315,14909,10204,14894,10041,14089,14460,14090,14459,14096,14452,14112,14431,14141,14388,14186,14305,14252,14130,14341,13941,14399,13756,14467,13585,14539,13430,14610,13272,14677,13026,14737,12808,14790,12617,14833,12449,14869,12303,14896,12065,14916,11845,14929,11655,14937,11490,14939,11347,14936,11184,14930,10970,14921,10783,14912,10621,14900,10480,14885,10356,14867,10247,14848,10062,14827,9894,14805,9745,14400,14208,14400,14206,14402,14198,14406,14174,14415,14122,14427,14035,14444,13913,14469,13767,14504,13613,14548,13463,14598,13324,14651,13082,14704,12858,14752,12658,14795,12483,14831,12330,14860,12106,14881,11875,14895,11675,14903,11501,14905,11351,14903,11178,14900,10953,14892,10757,14880,10589,14865,10442,14847,10313,14827,10162,14805,9965,14782,9792,14757,9642,14731,9507,14562,13883,14562,13883,14563,13877,14566,13862,14570,13830,14576,13773,14584,13689,14595,13582,14613,13461,14637,13336,14668,13120,14704,12897,14741,12695,14776,12516,14808,12358,14835,12150,14856,11910,14870,11701,14878,11519,14882,11361,14884,11187,14880,10951,14871,10748,14858,10572,14842,10418,14823,10286,14801,10099,14777,9897,14751,9722,14725,9567,14696,9430,14666,9309,14702,13604,14702,13604,14702,13600,14703,13591,14705,13570,14707,13533,14709,13477,14712,13400,14718,13305,14727,13106,14743,12907,14762,12716,14784,12539,14807,12380,14827,12190,14844,11943,14855,11727,14863,11539,14870,11376,14871,11204,14868,10960,14858,10748,14845,10565,14829,10406,14809,10269,14786,10058,14761,9852,14734,9671,14705,9512,14674,9374,14641,9253,14608,9076,14821,13366,14821,13365,14821,13364,14821,13358,14821,13344,14821,13320,14819,13252,14817,13145,14815,13011,14814,12858,14817,12698,14823,12539,14832,12389,14841,12214,14850,11968,14856,11750,14861,11558,14866,11390,14867,11226,14862,10972,14853,10754,14840,10565,14823,10401,14803,10259,14780,10032,14754,9820,14725,9635,14694,9473,14661,9333,14627,9203,14593,8988,14557,8798,14923,13014,14922,13014,14922,13012,14922,13004,14920,12987,14919,12957,14915,12907,14909,12834,14902,12738,14894,12623,14888,12498,14883,12370,14880,12203,14878,11970,14875,11759,14873,11569,14874,11401,14872,11243,14865,10986,14855,10762,14842,10568,14825,10401,14804,10255,14781,10017,14754,9799,14725,9611,14692,9445,14658,9301,14623,9139,14587,8920,14548,8729,14509,8562,15008,12672,15008,12672,15008,12671,15007,12667,15005,12656,15001,12637,14997,12605,14989,12556,14978,12490,14966,12407,14953,12313,14940,12136,14927,11934,14914,11742,14903,11563,14896,11401,14889,11247,14879,10992,14866,10767,14851,10570,14833,10400,14812,10252,14789,10007,14761,9784,14731,9592,14698,9424,14663,9279,14627,9088,14588,8868,14548,8676,14508,8508,14467,8360,15080,12386,15080,12386,15079,12385,15078,12383,15076,12378,15072,12367,15066,12347,15057,12315,15045,12253,15030,12138,15012,11998,14993,11845,14972,11685,14951,11530,14935,11383,14920,11228,14904,10981,14887,10762,14870,10567,14850,10397,14827,10248,14803,9997,14774,9771,14743,9578,14710,9407,14674,9259,14637,9048,14596,8826,14555,8632,14514,8464,14471,8317,14427,8182,15139,12008,15139,12008,15138,12008,15137,12007,15135,12003,15130,11990,15124,11969,15115,11929,15102,11872,15086,11794,15064,11693,15041,11581,15013,11459,14987,11336,14966,11170,14944,10944,14921,10738,14898,10552,14875,10387,14850,10239,14824,9983,14794,9758,14762,9563,14728,9392,14692,9244,14653,9014,14611,8791,14569,8597,14526,8427,14481,8281,14436,8110,14391,7885,15188,11617,15188,11617,15187,11617,15186,11618,15183,11617,15179,11612,15173,11601,15163,11581,15150,11546,15133,11495,15110,11427,15083,11346,15051,11246,15024,11057,14996,10868,14967,10687,14938,10517,14911,10362,14882,10206,14853,9956,14821,9737,14787,9543,14752,9375,14715,9228,14675,8980,14632,8760,14589,8565,14544,8395,14498,8248,14451,8049,14404,7824,14357,7630,15228,11298,15228,11298,15227,11299,15226,11301,15223,11303,15219,11302,15213,11299,15204,11290,15191,11271,15174,11217,15150,11129,15119,11015,15087,10886,15057,10744,15024,10599,14990,10455,14957,10318,14924,10143,14891,9911,14856,9701,14820,9516,14782,9352,14744,9200,14703,8946,14659,8725,14615,8533,14568,8366,14521,8220,14472,7992,14423,7770,14374,7578,14315,7408,15260,10819,15260,10819,15259,10822,15258,10826,15256,10832,15251,10836,15246,10841,15237,10838,15225,10821,15207,10788,15183,10734,15151,10660,15120,10571,15087,10469,15049,10359,15012,10249,14974,10041,14937,9837,14900,9647,14860,9475,14820,9320,14779,9147,14736,8902,14691,8688,14646,8499,14598,8335,14549,8189,14499,7940,14448,7720,14397,7529,14347,7363,14256,7218,15285,10410,15285,10411,15285,10413,15284,10418,15282,10425,15278,10434,15272,10442,15264,10449,15252,10445,15235,10433,15210,10403,15179,10358,15149,10301,15113,10218,15073,10059,15033,9894,14991,9726,14951,9565,14909,9413,14865,9273,14822,9073,14777,8845,14730,8641,14682,8459,14633,8300,14583,8129,14531,7883,14479,7670,14426,7482,14373,7321,14305,7176,14201,6939,15305,9939,15305,9940,15305,9945,15304,9955,15302,9967,15298,9989,15293,10010,15286,10033,15274,10044,15258,10045,15233,10022,15205,9975,15174,9903,15136,9808,15095,9697,15053,9578,15009,9451,14965,9327,14918,9198,14871,8973,14825,8766,14775,8579,14725,8408,14675,8259,14622,8058,14569,7821,14515,7615,14460,7435,14405,7276,14350,7108,14256,6866,14149,6653,15321,9444,15321,9445,15321,9448,15320,9458,15317,9470,15314,9490,15310,9515,15302,9540,15292,9562,15276,9579,15251,9577,15226,9559,15195,9519,15156,9463,15116,9389,15071,9304,15025,9208,14978,9023,14927,8838,14878,8661,14827,8496,14774,8344,14722,8206,14667,7973,14612,7749,14556,7555,14499,7382,14443,7229,14385,7025,14322,6791,14210,6588,14100,6409,15333,8920,15333,8921,15332,8927,15332,8943,15329,8965,15326,9002,15322,9048,15316,9106,15307,9162,15291,9204,15267,9221,15244,9221,15212,9196,15175,9134,15133,9043,15088,8930,15040,8801,14990,8665,14938,8526,14886,8391,14830,8261,14775,8087,14719,7866,14661,7664,14603,7482,14544,7322,14485,7178,14426,6936,14367,6713,14281,6517,14166,6348,14054,6198,15341,8360,15341,8361,15341,8366,15341,8379,15339,8399,15336,8431,15332,8473,15326,8527,15318,8585,15302,8632,15281,8670,15258,8690,15227,8690,15191,8664,15149,8612,15104,8543,15055,8456,15001,8360,14948,8259,14892,8122,14834,7923,14776,7734,14716,7558,14656,7397,14595,7250,14534,7070,14472,6835,14410,6628,14350,6443,14243,6283,14125,6135,14010,5889,15348,7715,15348,7717,15348,7725,15347,7745,15345,7780,15343,7836,15339,7905,15334,8e3,15326,8103,15310,8193,15293,8239,15270,8270,15240,8287,15204,8283,15163,8260,15118,8223,15067,8143,15014,8014,14958,7873,14899,7723,14839,7573,14778,7430,14715,7293,14652,7164,14588,6931,14524,6720,14460,6531,14396,6362,14330,6210,14207,6015,14086,5781,13969,5576,15352,7114,15352,7116,15352,7128,15352,7159,15350,7195,15348,7237,15345,7299,15340,7374,15332,7457,15317,7544,15301,7633,15280,7703,15251,7754,15216,7775,15176,7767,15131,7733,15079,7670,15026,7588,14967,7492,14906,7387,14844,7278,14779,7171,14714,6965,14648,6770,14581,6587,14515,6420,14448,6269,14382,6123,14299,5881,14172,5665,14049,5477,13929,5310,15355,6329,15355,6330,15355,6339,15355,6362,15353,6410,15351,6472,15349,6572,15344,6688,15337,6835,15323,6985,15309,7142,15287,7220,15260,7277,15226,7310,15188,7326,15142,7318,15090,7285,15036,7239,14976,7177,14914,7045,14849,6892,14782,6736,14714,6581,14645,6433,14576,6293,14506,6164,14438,5946,14369,5733,14270,5540,14140,5369,14014,5216,13892,5043,15357,5483,15357,5484,15357,5496,15357,5528,15356,5597,15354,5692,15351,5835,15347,6011,15339,6195,15328,6317,15314,6446,15293,6566,15268,6668,15235,6746,15197,6796,15152,6811,15101,6790,15046,6748,14985,6673,14921,6583,14854,6479,14785,6371,14714,6259,14643,6149,14571,5946,14499,5750,14428,5567,14358,5401,14242,5250,14109,5111,13980,4870,13856,4657,15359,4555,15359,4557,15358,4573,15358,4633,15357,4715,15355,4841,15353,5061,15349,5216,15342,5391,15331,5577,15318,5770,15299,5967,15274,6150,15243,6223,15206,6280,15161,6310,15111,6317,15055,6300,14994,6262,14928,6208,14860,6141,14788,5994,14715,5838,14641,5684,14566,5529,14492,5384,14418,5247,14346,5121,14216,4892,14079,4682,13948,4496,13822,4330,15359,3498,15359,3501,15359,3520,15359,3598,15358,3719,15356,3860,15355,4137,15351,4305,15344,4563,15334,4809,15321,5116,15303,5273,15280,5418,15250,5547,15214,5653,15170,5722,15120,5761,15064,5763,15002,5733,14935,5673,14865,5597,14792,5504,14716,5400,14640,5294,14563,5185,14486,5041,14410,4841,14335,4655,14191,4482,14051,4325,13918,4183,13790,4012,15360,2282,15360,2285,15360,2306,15360,2401,15359,2547,15357,2748,15355,3103,15352,3349,15345,3675,15336,4020,15324,4272,15307,4496,15285,4716,15255,4908,15220,5086,15178,5170,15128,5214,15072,5234,15010,5231,14943,5206,14871,5166,14796,5102,14718,4971,14639,4833,14559,4687,14480,4541,14402,4401,14315,4268,14167,4142,14025,3958,13888,3747,13759,3556,15360,923,15360,925,15360,946,15360,1052,15359,1214,15357,1494,15356,1892,15352,2274,15346,2663,15338,3099,15326,3393,15309,3679,15288,3980,15260,4183,15226,4325,15185,4437,15136,4517,15080,4570,15018,4591,14950,4581,14877,4545,14800,4485,14720,4411,14638,4325,14556,4231,14475,4136,14395,3988,14297,3803,14145,3628,13999,3465,13861,3314,13729,3177,15360,263,15360,264,15360,272,15360,325,15359,407,15358,548,15356,780,15352,1144,15347,1580,15339,2099,15328,2425,15312,2795,15292,3133,15264,3329,15232,3517,15191,3689,15143,3819,15088,3923,15025,3978,14956,3999,14882,3979,14804,3931,14722,3855,14639,3756,14554,3645,14470,3529,14388,3409,14279,3289,14124,3173,13975,3055,13834,2848,13701,2658,15360,49,15360,49,15360,52,15360,75,15359,111,15358,201,15356,283,15353,519,15348,726,15340,1045,15329,1415,15314,1795,15295,2173,15269,2410,15237,2649,15197,2866,15150,3054,15095,3140,15032,3196,14963,3228,14888,3236,14808,3224,14725,3191,14639,3146,14553,3088,14466,2976,14382,2836,14262,2692,14103,2549,13952,2409,13808,2278,13674,2154,15360,4,15360,4,15360,4,15360,13,15359,33,15358,59,15357,112,15353,199,15348,302,15341,456,15331,628,15316,827,15297,1082,15272,1332,15241,1601,15202,1851,15156,2069,15101,2172,15039,2256,14970,2314,14894,2348,14813,2358,14728,2344,14640,2311,14551,2263,14463,2203,14376,2133,14247,2059,14084,1915,13930,1761,13784,1609,13648,1464,15360,0,15360,0,15360,0,15360,3,15359,18,15358,26,15357,53,15354,80,15348,97,15341,165,15332,238,15318,326,15299,427,15275,529,15245,654,15207,771,15161,885,15108,994,15046,1089,14976,1170,14900,1229,14817,1266,14731,1284,14641,1282,14550,1260,14460,1223,14370,1174,14232,1116,14066,1050,13909,981,13761,910,13623,839]);let gn=null;function xm(){return gn===null&&(gn=new Hc(mm,32,32,ka,Di),gn.minFilter=Ye,gn.magFilter=Ye,gn.wrapS=Sn,gn.wrapT=Sn,gn.generateMipmaps=!1,gn.needsUpdate=!0),gn}class gm{constructor(t={}){const{canvas:e=sh(),context:n=null,depth:s=!0,stencil:r=!1,alpha:a=!1,antialias:o=!1,premultipliedAlpha:l=!0,preserveDrawingBuffer:c=!1,powerPreference:h="default",failIfMajorPerformanceCaveat:u=!1,reversedDepthBuffer:d=!1}=t;this.isWebGLRenderer=!0;let p;if(n!==null){if(typeof WebGLRenderingContext<"u"&&n instanceof WebGLRenderingContext)throw new Error("THREE.WebGLRenderer: WebGL 1 is not supported since r163.");p=n.getContextAttributes().alpha}else p=a;const x=new Set([Wa,Ha,Ga]),_=new Set([hn,jn,Qi,ts,Ba,za]),m=new Uint32Array(4),f=new Int32Array(4);let b=null,S=null;const T=[],R=[];this.domElement=e,this.debug={checkShaderErrors:!0,onShaderError:null},this.autoClear=!0,this.autoClearColor=!0,this.autoClearDepth=!0,this.autoClearStencil=!0,this.sortObjects=!0,this.clippingPlanes=[],this.localClippingEnabled=!1,this.toneMapping=Un,this.toneMappingExposure=1,this.transmissionResolutionScale=1;const w=this;let L=!1;this._outputColorSpace=De;let F=0,E=0,M=null,P=-1,O=null;const z=new se,k=new se;let W=null;const Y=new Yt(0);let nt=0,X=e.width,rt=e.height,st=1,St=null,Gt=null;const qt=new se(0,0,X,rt),jt=new se(0,0,X,rt);let Kt=!1;const K=new Za;let J=!1,xt=!1;const Rt=new re,Tt=new D,kt=new se,Qt={background:null,fog:null,environment:null,overrideMaterial:null,isScene:!0};let Ct=!1;function B(){return M===null?st:1}let y=n;function Z(v,I){return e.getContext(v,I)}try{const v={alpha:!0,depth:s,stencil:r,antialias:o,premultipliedAlpha:l,preserveDrawingBuffer:c,powerPreference:h,failIfMajorPerformanceCaveat:u};if("setAttribute"in e&&e.setAttribute("data-engine",`three.js r${Na}`),e.addEventListener("webglcontextlost",it,!1),e.addEventListener("webglcontextrestored",j,!1),e.addEventListener("webglcontextcreationerror",bt,!1),y===null){const I="webgl2";if(y=Z(I,v),y===null)throw Z(I)?new Error("Error creating WebGL context with your selected attributes."):new Error("Error creating WebGL context.")}}catch(v){throw v("WebGLRenderer: "+v.message),v}let Q,ot,et,vt,ft,Mt,A,g,N,q,$,H,wt,dt,Pt,At,tt,lt,Nt,It,_t,Ot,C,mt;function ht(){Q=new Tp(y),Q.init(),Ot=new om(y,Q),ot=new xp(y,Q,t,Ot),et=new rm(y,Q),ot.reversedDepthBuffer&&d&&et.buffers.depth.setReversed(!0),vt=new Rp(y),ft=new q0,Mt=new am(y,Q,et,ft,ot,Ot,vt),A=new _p(w),g=new Ep(w),N=new Du(y),C=new pp(y,N),q=new Ap(y,N,vt,C),$=new Pp(y,q,N,vt),Nt=new Cp(y,ot,Mt),At=new gp(ft),H=new X0(w,A,g,Q,ot,C,At),wt=new dm(w,ft),dt=new Z0,Pt=new tm(Q),lt=new dp(w,A,g,et,$,p,l),tt=new im(w,$,ot),mt=new pm(y,vt,ot,et),It=new mp(y,Q,vt),_t=new wp(y,Q,vt),vt.programs=H.programs,w.capabilities=ot,w.extensions=Q,w.properties=ft,w.renderLists=dt,w.shadowMap=tt,w.state=et,w.info=vt}ht();const ut=new um(w,y);this.xr=ut,this.getContext=function(){return y},this.getContextAttributes=function(){return y.getContextAttributes()},this.forceContextLoss=function(){const v=Q.get("WEBGL_lose_context");v&&v.loseContext()},this.forceContextRestore=function(){const v=Q.get("WEBGL_lose_context");v&&v.restoreContext()},this.getPixelRatio=function(){return st},this.setPixelRatio=function(v){v!==void 0&&(st=v,this.setSize(X,rt,!1))},this.getSize=function(v){return v.set(X,rt)},this.setSize=function(v,I,V=!0){if(ut.isPresenting){Bt("WebGLRenderer: Can't change size while VR device is presenting.");return}X=v,rt=I,e.width=Math.floor(v*st),e.height=Math.floor(I*st),V===!0&&(e.style.width=v+"px",e.style.height=I+"px"),this.setViewport(0,0,v,I)},this.getDrawingBufferSize=function(v){return v.set(X*st,rt*st).floor()},this.setDrawingBufferSize=function(v,I,V){X=v,rt=I,st=V,e.width=Math.floor(v*V),e.height=Math.floor(I*V),this.setViewport(0,0,v,I)},this.getCurrentViewport=function(v){return v.copy(z)},this.getViewport=function(v){return v.copy(qt)},this.setViewport=function(v,I,V,G){v.isVector4?qt.set(v.x,v.y,v.z,v.w):qt.set(v,I,V,G),et.viewport(z.copy(qt).multiplyScalar(st).round())},this.getScissor=function(v){return v.copy(jt)},this.setScissor=function(v,I,V,G){v.isVector4?jt.set(v.x,v.y,v.z,v.w):jt.set(v,I,V,G),et.scissor(k.copy(jt).multiplyScalar(st).round())},this.getScissorTest=function(){return Kt},this.setScissorTest=function(v){et.setScissorTest(Kt=v)},this.setOpaqueSort=function(v){St=v},this.setTransparentSort=function(v){Gt=v},this.getClearColor=function(v){return v.copy(lt.getClearColor())},this.setClearColor=function(){lt.setClearColor(...arguments)},this.getClearAlpha=function(){return lt.getClearAlpha()},this.setClearAlpha=function(){lt.setClearAlpha(...arguments)},this.clear=function(v=!0,I=!0,V=!0){let G=0;if(v){let U=!1;if(M!==null){const ct=M.texture.format;U=x.has(ct)}if(U){const ct=M.texture.type,gt=_.has(ct),Et=lt.getClearColor(),yt=lt.getClearAlpha(),Ut=Et.r,Ft=Et.g,Dt=Et.b;gt?(m[0]=Ut,m[1]=Ft,m[2]=Dt,m[3]=yt,y.clearBufferuiv(y.COLOR,0,m)):(f[0]=Ut,f[1]=Ft,f[2]=Dt,f[3]=yt,y.clearBufferiv(y.COLOR,0,f))}else G|=y.COLOR_BUFFER_BIT}I&&(G|=y.DEPTH_BUFFER_BIT),V&&(G|=y.STENCIL_BUFFER_BIT,this.state.buffers.stencil.setMask(4294967295)),y.clear(G)},this.clearColor=function(){this.clear(!0,!1,!1)},this.clearDepth=function(){this.clear(!1,!0,!1)},this.clearStencil=function(){this.clear(!1,!1,!0)},this.dispose=function(){e.removeEventListener("webglcontextlost",it,!1),e.removeEventListener("webglcontextrestored",j,!1),e.removeEventListener("webglcontextcreationerror",bt,!1),lt.dispose(),dt.dispose(),Pt.dispose(),ft.dispose(),A.dispose(),g.dispose(),$.dispose(),C.dispose(),mt.dispose(),H.dispose(),ut.dispose(),ut.removeEventListener("sessionstart",to),ut.removeEventListener("sessionend",eo),Bn.stop()};function it(v){v.preventDefault(),go("WebGLRenderer: Context Lost."),L=!0}function j(){go("WebGLRenderer: Context Restored."),L=!1;const v=vt.autoReset,I=tt.enabled,V=tt.autoUpdate,G=tt.needsUpdate,U=tt.type;ht(),vt.autoReset=v,tt.enabled=I,tt.autoUpdate=V,tt.needsUpdate=G,tt.type=U}function bt(v){de("WebGLRenderer: A WebGL context could not be created. Reason: ",v.statusMessage)}function zt(v){const I=v.target;I.removeEventListener("dispose",zt),ce(I)}function ce(v){te(v),ft.remove(v)}function te(v){const I=ft.get(v).programs;I!==void 0&&(I.forEach(function(V){H.releaseProgram(V)}),v.isShaderMaterial&&H.releaseShaderCache(v))}this.renderBufferDirect=function(v,I,V,G,U,ct){I===null&&(I=Qt);const gt=U.isMesh&&U.matrixWorld.determinant()<0,Et=ul(v,I,V,G,U);et.setMaterial(G,gt);let yt=V.index,Ut=1;if(G.wireframe===!0){if(yt=q.getWireframeAttribute(V),yt===void 0)return;Ut=2}const Ft=V.drawRange,Dt=V.attributes.position;let Zt=Ft.start*Ut,ee=(Ft.start+Ft.count)*Ut;ct!==null&&(Zt=Math.max(Zt,ct.start*Ut),ee=Math.min(ee,(ct.start+ct.count)*Ut)),yt!==null?(Zt=Math.max(Zt,0),ee=Math.min(ee,yt.count)):Dt!=null&&(Zt=Math.max(Zt,0),ee=Math.min(ee,Dt.count));const me=ee-Zt;if(me<0||me===1/0)return;C.setup(U,G,Et,V,yt);let xe,ae=It;if(yt!==null&&(xe=N.get(yt),ae=_t,ae.setIndex(xe)),U.isMesh)G.wireframe===!0?(et.setLineWidth(G.wireframeLinewidth*B()),ae.setMode(y.LINES)):ae.setMode(y.TRIANGLES);else if(U.isLine){let Lt=G.linewidth;Lt===void 0&&(Lt=1),et.setLineWidth(Lt*B()),U.isLineSegments?ae.setMode(y.LINES):U.isLineLoop?ae.setMode(y.LINE_LOOP):ae.setMode(y.LINE_STRIP)}else U.isPoints?ae.setMode(y.POINTS):U.isSprite&&ae.setMode(y.TRIANGLES);if(U.isBatchedMesh)if(U._multiDrawInstances!==null)is("WebGLRenderer: renderMultiDrawInstances has been deprecated and will be removed in r184. Append to renderMultiDraw arguments and use indirection."),ae.renderMultiDrawInstances(U._multiDrawStarts,U._multiDrawCounts,U._multiDrawCount,U._multiDrawInstances);else if(Q.get("WEBGL_multi_draw"))ae.renderMultiDraw(U._multiDrawStarts,U._multiDrawCounts,U._multiDrawCount);else{const Lt=U._multiDrawStarts,ue=U._multiDrawCounts,Jt=U._multiDrawCount,Be=yt?N.get(yt).bytesPerElement:1,ni=ft.get(G).currentProgram.getUniforms();for(let ze=0;ze{function ct(){if(G.forEach(function(gt){ft.get(gt).currentProgram.isReady()&&G.delete(gt)}),G.size===0){U(v);return}setTimeout(ct,10)}Q.get("KHR_parallel_shader_compile")!==null?ct():setTimeout(ct,10)})};let Ze=null;function hl(v){Ze&&Ze(v)}function to(){Bn.stop()}function eo(){Bn.start()}const Bn=new il;Bn.setAnimationLoop(hl),typeof self<"u"&&Bn.setContext(self),this.setAnimationLoop=function(v){Ze=v,ut.setAnimationLoop(v),v===null?Bn.stop():Bn.start()},ut.addEventListener("sessionstart",to),ut.addEventListener("sessionend",eo),this.render=function(v,I){if(I!==void 0&&I.isCamera!==!0){de("WebGLRenderer.render: camera is not an instance of THREE.Camera.");return}if(L===!0)return;if(v.matrixWorldAutoUpdate===!0&&v.updateMatrixWorld(),I.parent===null&&I.matrixWorldAutoUpdate===!0&&I.updateMatrixWorld(),ut.enabled===!0&&ut.isPresenting===!0&&(ut.cameraAutoUpdate===!0&&ut.updateCamera(I),I=ut.getCamera()),v.isScene===!0&&v.onBeforeRender(w,v,I,M),S=Pt.get(v,R.length),S.init(I),R.push(S),Rt.multiplyMatrices(I.projectionMatrix,I.matrixWorldInverse),K.setFromProjectionMatrix(Rt,cn,I.reversedDepth),xt=this.localClippingEnabled,J=At.init(this.clippingPlanes,xt),b=dt.get(v,T.length),b.init(),T.push(b),ut.enabled===!0&&ut.isPresenting===!0){const ct=w.xr.getDepthSensingMesh();ct!==null&&er(ct,I,-1/0,w.sortObjects)}er(v,I,0,w.sortObjects),b.finish(),w.sortObjects===!0&&b.sort(St,Gt),Ct=ut.enabled===!1||ut.isPresenting===!1||ut.hasDepthSensing()===!1,Ct&<.addToRenderList(b,v),this.info.render.frame++,J===!0&&At.beginShadows();const V=S.state.shadowsArray;tt.render(V,v,I),J===!0&&At.endShadows(),this.info.autoReset===!0&&this.info.reset();const G=b.opaque,U=b.transmissive;if(S.setupLights(),I.isArrayCamera){const ct=I.cameras;if(U.length>0)for(let gt=0,Et=ct.length;gt0&&io(G,U,v,I),Ct&<.render(v),no(b,v,I);M!==null&&E===0&&(Mt.updateMultisampleRenderTarget(M),Mt.updateRenderTargetMipmap(M)),v.isScene===!0&&v.onAfterRender(w,v,I),C.resetDefaultState(),P=-1,O=null,R.pop(),R.length>0?(S=R[R.length-1],J===!0&&At.setGlobalState(w.clippingPlanes,S.state.camera)):S=null,T.pop(),T.length>0?b=T[T.length-1]:b=null};function er(v,I,V,G){if(v.visible===!1)return;if(v.layers.test(I.layers)){if(v.isGroup)V=v.renderOrder;else if(v.isLOD)v.autoUpdate===!0&&v.update(I);else if(v.isLight)S.pushLight(v),v.castShadow&&S.pushShadow(v);else if(v.isSprite){if(!v.frustumCulled||K.intersectsSprite(v)){G&&kt.setFromMatrixPosition(v.matrixWorld).applyMatrix4(Rt);const gt=$.update(v),Et=v.material;Et.visible&&b.push(v,gt,Et,V,kt.z,null)}}else if((v.isMesh||v.isLine||v.isPoints)&&(!v.frustumCulled||K.intersectsObject(v))){const gt=$.update(v),Et=v.material;if(G&&(v.boundingSphere!==void 0?(v.boundingSphere===null&&v.computeBoundingSphere(),kt.copy(v.boundingSphere.center)):(gt.boundingSphere===null&>.computeBoundingSphere(),kt.copy(gt.boundingSphere.center)),kt.applyMatrix4(v.matrixWorld).applyMatrix4(Rt)),Array.isArray(Et)){const yt=gt.groups;for(let Ut=0,Ft=yt.length;Ut0&&ls(U,I,V),ct.length>0&&ls(ct,I,V),gt.length>0&&ls(gt,I,V),et.buffers.depth.setTest(!0),et.buffers.depth.setMask(!0),et.buffers.color.setMask(!0),et.setPolygonOffset(!1)}function io(v,I,V,G){if((V.isScene===!0?V.overrideMaterial:null)!==null)return;S.state.transmissionRenderTarget[G.id]===void 0&&(S.state.transmissionRenderTarget[G.id]=new $n(1,1,{generateMipmaps:!0,type:Q.has("EXT_color_buffer_half_float")||Q.has("EXT_color_buffer_float")?Di:hn,minFilter:Kn,samples:4,stencilBuffer:r,resolveDepthBuffer:!1,resolveStencilBuffer:!1,colorSpace:$t.workingColorSpace}));const ct=S.state.transmissionRenderTarget[G.id],gt=G.viewport||z;ct.setSize(gt.z*w.transmissionResolutionScale,gt.w*w.transmissionResolutionScale);const Et=w.getRenderTarget(),yt=w.getActiveCubeFace(),Ut=w.getActiveMipmapLevel();w.setRenderTarget(ct),w.getClearColor(Y),nt=w.getClearAlpha(),nt<1&&w.setClearColor(16777215,.5),w.clear(),Ct&<.render(V);const Ft=w.toneMapping;w.toneMapping=Un;const Dt=G.viewport;if(G.viewport!==void 0&&(G.viewport=void 0),S.setupLightsView(G),J===!0&&At.setGlobalState(w.clippingPlanes,G),ls(v,V,G),Mt.updateMultisampleRenderTarget(ct),Mt.updateRenderTargetMipmap(ct),Q.has("WEBGL_multisampled_render_to_texture")===!1){let Zt=!1;for(let ee=0,me=I.length;ee0),Dt=!!V.morphAttributes.position,Zt=!!V.morphAttributes.normal,ee=!!V.morphAttributes.color;let me=Un;G.toneMapped&&(M===null||M.isXRRenderTarget===!0)&&(me=w.toneMapping);const xe=V.morphAttributes.position||V.morphAttributes.normal||V.morphAttributes.color,ae=xe!==void 0?xe.length:0,Lt=ft.get(G),ue=S.state.lights;if(J===!0&&(xt===!0||v!==O)){const Ce=v===O&&G.id===P;At.setState(G,v,Ce)}let Jt=!1;G.version===Lt.__version?(Lt.needsLights&&Lt.lightsStateVersion!==ue.state.version||Lt.outputColorSpace!==Et||U.isBatchedMesh&&Lt.batching===!1||!U.isBatchedMesh&&Lt.batching===!0||U.isBatchedMesh&&Lt.batchingColor===!0&&U.colorTexture===null||U.isBatchedMesh&&Lt.batchingColor===!1&&U.colorTexture!==null||U.isInstancedMesh&&Lt.instancing===!1||!U.isInstancedMesh&&Lt.instancing===!0||U.isSkinnedMesh&&Lt.skinning===!1||!U.isSkinnedMesh&&Lt.skinning===!0||U.isInstancedMesh&&Lt.instancingColor===!0&&U.instanceColor===null||U.isInstancedMesh&&Lt.instancingColor===!1&&U.instanceColor!==null||U.isInstancedMesh&&Lt.instancingMorph===!0&&U.morphTexture===null||U.isInstancedMesh&&Lt.instancingMorph===!1&&U.morphTexture!==null||Lt.envMap!==yt||G.fog===!0&&Lt.fog!==ct||Lt.numClippingPlanes!==void 0&&(Lt.numClippingPlanes!==At.numPlanes||Lt.numIntersection!==At.numIntersection)||Lt.vertexAlphas!==Ut||Lt.vertexTangents!==Ft||Lt.morphTargets!==Dt||Lt.morphNormals!==Zt||Lt.morphColors!==ee||Lt.toneMapping!==me||Lt.morphTargetsCount!==ae)&&(Jt=!0):(Jt=!0,Lt.__version=G.version);let Be=Lt.currentProgram;Jt===!0&&(Be=hs(G,I,U));let ni=!1,ze=!1,Ni=!1;const fe=Be.getUniforms(),Ue=Lt.uniforms;if(et.useProgram(Be.program)&&(ni=!0,ze=!0,Ni=!0),G.id!==P&&(P=G.id,ze=!0),ni||O!==v){et.buffers.depth.getReversed()&&v.reversedDepth!==!0&&(v._reversedDepth=!0,v.updateProjectionMatrix()),fe.setValue(y,"projectionMatrix",v.projectionMatrix),fe.setValue(y,"viewMatrix",v.matrixWorldInverse);const Ne=fe.map.cameraPosition;Ne!==void 0&&Ne.setValue(y,Tt.setFromMatrixPosition(v.matrixWorld)),ot.logarithmicDepthBuffer&&fe.setValue(y,"logDepthBufFC",2/(Math.log(v.far+1)/Math.LN2)),(G.isMeshPhongMaterial||G.isMeshToonMaterial||G.isMeshLambertMaterial||G.isMeshBasicMaterial||G.isMeshStandardMaterial||G.isShaderMaterial)&&fe.setValue(y,"isOrthographic",v.isOrthographicCamera===!0),O!==v&&(O=v,ze=!0,Ni=!0)}if(U.isSkinnedMesh){fe.setOptional(y,U,"bindMatrix"),fe.setOptional(y,U,"bindMatrixInverse");const Ce=U.skeleton;Ce&&(Ce.boneTexture===null&&Ce.computeBoneTexture(),fe.setValue(y,"boneTexture",Ce.boneTexture,Mt))}U.isBatchedMesh&&(fe.setOptional(y,U,"batchingTexture"),fe.setValue(y,"batchingTexture",U._matricesTexture,Mt),fe.setOptional(y,U,"batchingIdTexture"),fe.setValue(y,"batchingIdTexture",U._indirectTexture,Mt),fe.setOptional(y,U,"batchingColorTexture"),U._colorsTexture!==null&&fe.setValue(y,"batchingColorTexture",U._colorsTexture,Mt));const We=V.morphAttributes;if((We.position!==void 0||We.normal!==void 0||We.color!==void 0)&&Nt.update(U,V,Be),(ze||Lt.receiveShadow!==U.receiveShadow)&&(Lt.receiveShadow=U.receiveShadow,fe.setValue(y,"receiveShadow",U.receiveShadow)),G.isMeshGouraudMaterial&&G.envMap!==null&&(Ue.envMap.value=yt,Ue.flipEnvMap.value=yt.isCubeTexture&&yt.isRenderTargetTexture===!1?-1:1),G.isMeshStandardMaterial&&G.envMap===null&&I.environment!==null&&(Ue.envMapIntensity.value=I.environmentIntensity),Ue.dfgLUT!==void 0&&(Ue.dfgLUT.value=xm()),ze&&(fe.setValue(y,"toneMappingExposure",w.toneMappingExposure),Lt.needsLights&&fl(Ue,Ni),ct&&G.fog===!0&&wt.refreshFogUniforms(Ue,ct),wt.refreshMaterialUniforms(Ue,G,st,rt,S.state.transmissionRenderTarget[v.id]),Hs.upload(y,ro(Lt),Ue,Mt)),G.isShaderMaterial&&G.uniformsNeedUpdate===!0&&(Hs.upload(y,ro(Lt),Ue,Mt),G.uniformsNeedUpdate=!1),G.isSpriteMaterial&&fe.setValue(y,"center",U.center),fe.setValue(y,"modelViewMatrix",U.modelViewMatrix),fe.setValue(y,"normalMatrix",U.normalMatrix),fe.setValue(y,"modelMatrix",U.matrixWorld),G.isShaderMaterial||G.isRawShaderMaterial){const Ce=G.uniformsGroups;for(let Ne=0,nr=Ce.length;Ne0&&Mt.useMultisampledRTT(v)===!1?U=ft.get(v).__webglMultisampledFramebuffer:Array.isArray(Ft)?U=Ft[V]:U=Ft,z.copy(v.viewport),k.copy(v.scissor),W=v.scissorTest}else z.copy(qt).multiplyScalar(st).floor(),k.copy(jt).multiplyScalar(st).floor(),W=Kt;if(V!==0&&(U=pl),et.bindFramebuffer(y.FRAMEBUFFER,U)&&G&&et.drawBuffers(v,U),et.viewport(z),et.scissor(k),et.setScissorTest(W),ct){const yt=ft.get(v.texture);y.framebufferTexture2D(y.FRAMEBUFFER,y.COLOR_ATTACHMENT0,y.TEXTURE_CUBE_MAP_POSITIVE_X+I,yt.__webglTexture,V)}else if(gt){const yt=I;for(let Ut=0;Ut=0&&I<=v.width-G&&V>=0&&V<=v.height-U&&(v.textures.length>1&&y.readBuffer(y.COLOR_ATTACHMENT0+Et),y.readPixels(I,V,G,U,Ot.convert(Ft),Ot.convert(Dt),ct))}finally{const Ut=M!==null?ft.get(M).__webglFramebuffer:null;et.bindFramebuffer(y.FRAMEBUFFER,Ut)}}},this.readRenderTargetPixelsAsync=async function(v,I,V,G,U,ct,gt,Et=0){if(!(v&&v.isWebGLRenderTarget))throw new Error("THREE.WebGLRenderer.readRenderTargetPixels: renderTarget is not THREE.WebGLRenderTarget.");let yt=ft.get(v).__webglFramebuffer;if(v.isWebGLCubeRenderTarget&>!==void 0&&(yt=yt[gt]),yt)if(I>=0&&I<=v.width-G&&V>=0&&V<=v.height-U){et.bindFramebuffer(y.FRAMEBUFFER,yt);const Ut=v.textures[Et],Ft=Ut.format,Dt=Ut.type;if(!ot.textureFormatReadable(Ft))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in RGBA or implementation defined format.");if(!ot.textureTypeReadable(Dt))throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: renderTarget is not in UnsignedByteType or implementation defined type.");const Zt=y.createBuffer();y.bindBuffer(y.PIXEL_PACK_BUFFER,Zt),y.bufferData(y.PIXEL_PACK_BUFFER,ct.byteLength,y.STREAM_READ),v.textures.length>1&&y.readBuffer(y.COLOR_ATTACHMENT0+Et),y.readPixels(I,V,G,U,Ot.convert(Ft),Ot.convert(Dt),0);const ee=M!==null?ft.get(M).__webglFramebuffer:null;et.bindFramebuffer(y.FRAMEBUFFER,ee);const me=y.fenceSync(y.SYNC_GPU_COMMANDS_COMPLETE,0);return y.flush(),await rh(y,me,4),y.bindBuffer(y.PIXEL_PACK_BUFFER,Zt),y.getBufferSubData(y.PIXEL_PACK_BUFFER,0,ct),y.deleteBuffer(Zt),y.deleteSync(me),ct}else throw new Error("THREE.WebGLRenderer.readRenderTargetPixelsAsync: requested read bounds are out of range.")},this.copyFramebufferToTexture=function(v,I=null,V=0){const G=Math.pow(2,-V),U=Math.floor(v.image.width*G),ct=Math.floor(v.image.height*G),gt=I!==null?I.x:0,Et=I!==null?I.y:0;Mt.setTexture2D(v,0),y.copyTexSubImage2D(y.TEXTURE_2D,V,0,0,gt,Et,U,ct),et.unbindTexture()};const ml=y.createFramebuffer(),xl=y.createFramebuffer();this.copyTextureToTexture=function(v,I,V=null,G=null,U=0,ct=null){ct===null&&(U!==0?(is("WebGLRenderer: copyTextureToTexture function signature has changed to support src and dst mipmap levels."),ct=U,U=0):ct=0);let gt,Et,yt,Ut,Ft,Dt,Zt,ee,me;const xe=v.isCompressedTexture?v.mipmaps[ct]:v.image;if(V!==null)gt=V.max.x-V.min.x,Et=V.max.y-V.min.y,yt=V.isBox3?V.max.z-V.min.z:1,Ut=V.min.x,Ft=V.min.y,Dt=V.isBox3?V.min.z:0;else{const We=Math.pow(2,-U);gt=Math.floor(xe.width*We),Et=Math.floor(xe.height*We),v.isDataArrayTexture?yt=xe.depth:v.isData3DTexture?yt=Math.floor(xe.depth*We):yt=1,Ut=0,Ft=0,Dt=0}G!==null?(Zt=G.x,ee=G.y,me=G.z):(Zt=0,ee=0,me=0);const ae=Ot.convert(I.format),Lt=Ot.convert(I.type);let ue;I.isData3DTexture?(Mt.setTexture3D(I,0),ue=y.TEXTURE_3D):I.isDataArrayTexture||I.isCompressedArrayTexture?(Mt.setTexture2DArray(I,0),ue=y.TEXTURE_2D_ARRAY):(Mt.setTexture2D(I,0),ue=y.TEXTURE_2D),y.pixelStorei(y.UNPACK_FLIP_Y_WEBGL,I.flipY),y.pixelStorei(y.UNPACK_PREMULTIPLY_ALPHA_WEBGL,I.premultiplyAlpha),y.pixelStorei(y.UNPACK_ALIGNMENT,I.unpackAlignment);const Jt=y.getParameter(y.UNPACK_ROW_LENGTH),Be=y.getParameter(y.UNPACK_IMAGE_HEIGHT),ni=y.getParameter(y.UNPACK_SKIP_PIXELS),ze=y.getParameter(y.UNPACK_SKIP_ROWS),Ni=y.getParameter(y.UNPACK_SKIP_IMAGES);y.pixelStorei(y.UNPACK_ROW_LENGTH,xe.width),y.pixelStorei(y.UNPACK_IMAGE_HEIGHT,xe.height),y.pixelStorei(y.UNPACK_SKIP_PIXELS,Ut),y.pixelStorei(y.UNPACK_SKIP_ROWS,Ft),y.pixelStorei(y.UNPACK_SKIP_IMAGES,Dt);const fe=v.isDataArrayTexture||v.isData3DTexture,Ue=I.isDataArrayTexture||I.isData3DTexture;if(v.isDepthTexture){const We=ft.get(v),Ce=ft.get(I),Ne=ft.get(We.__renderTarget),nr=ft.get(Ce.__renderTarget);et.bindFramebuffer(y.READ_FRAMEBUFFER,Ne.__webglFramebuffer),et.bindFramebuffer(y.DRAW_FRAMEBUFFER,nr.__webglFramebuffer);for(let zn=0;znMath.PI&&(n-=Oe),s<-Math.PI?s+=Oe:s>Math.PI&&(s-=Oe),n<=s?this._spherical.theta=Math.max(n,Math.min(s,this._spherical.theta)):this._spherical.theta=this._spherical.theta>(n+s)/2?Math.max(n,this._spherical.theta):Math.min(s,this._spherical.theta)),this._spherical.phi=Math.max(this.minPolarAngle,Math.min(this.maxPolarAngle,this._spherical.phi)),this._spherical.makeSafe(),this.enableDamping===!0?this.target.addScaledVector(this._panOffset,this.dampingFactor):this.target.add(this._panOffset),this.target.sub(this.cursor),this.target.clampLength(this.minTargetRadius,this.maxTargetRadius),this.target.add(this.cursor);let r=!1;if(this.zoomToCursor&&this._performCursorZoom||this.object.isOrthographicCamera)this._spherical.radius=this._clampDistance(this._spherical.radius);else{const a=this._spherical.radius;this._spherical.radius=this._clampDistance(this._spherical.radius*this._scale),r=a!=this._spherical.radius}if(_e.setFromSpherical(this._spherical),_e.applyQuaternion(this._quatInverse),e.copy(this.target).add(_e),this.object.lookAt(this.target),this.enableDamping===!0?(this._sphericalDelta.theta*=1-this.dampingFactor,this._sphericalDelta.phi*=1-this.dampingFactor,this._panOffset.multiplyScalar(1-this.dampingFactor)):(this._sphericalDelta.set(0,0,0),this._panOffset.set(0,0,0)),this.zoomToCursor&&this._performCursorZoom){let a=null;if(this.object.isPerspectiveCamera){const o=_e.length();a=this._clampDistance(o*this._scale);const l=o-a;this.object.position.addScaledVector(this._dollyDirection,l),this.object.updateMatrixWorld(),r=!!l}else if(this.object.isOrthographicCamera){const o=new D(this._mouse.x,this._mouse.y,0);o.unproject(this.object);const l=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),this.object.updateProjectionMatrix(),r=l!==this.object.zoom;const c=new D(this._mouse.x,this._mouse.y,0);c.unproject(this.object),this.object.position.sub(c).add(o),this.object.updateMatrixWorld(),a=_e.length()}else console.warn("WARNING: OrbitControls.js encountered an unknown camera type - zoom to cursor disabled."),this.zoomToCursor=!1;a!==null&&(this.screenSpacePanning?this.target.set(0,0,-1).transformDirection(this.object.matrix).multiplyScalar(a).add(this.object.position):(Os.origin.copy(this.object.position),Os.direction.set(0,0,-1).transformDirection(this.object.matrix),Math.abs(this.object.up.dot(Os.direction))<_m?this.object.lookAt(this.target):(Sc.setFromNormalAndCoplanarPoint(this.object.up,this.target),Os.intersectPlane(Sc,this.target))))}else if(this.object.isOrthographicCamera){const a=this.object.zoom;this.object.zoom=Math.max(this.minZoom,Math.min(this.maxZoom,this.object.zoom/this._scale)),a!==this.object.zoom&&(this.object.updateProjectionMatrix(),r=!0)}return this._scale=1,this._performCursorZoom=!1,r||this._lastPosition.distanceToSquared(this.object.position)>Br||8*(1-this._lastQuaternion.dot(this.object.quaternion))>Br||this._lastTargetPosition.distanceToSquared(this.target)>Br?(this.dispatchEvent(Mc),this._lastPosition.copy(this.object.position),this._lastQuaternion.copy(this.object.quaternion),this._lastTargetPosition.copy(this.target),!0):!1}_getAutoRotationAngle(t){return t!==null?Oe/60*this.autoRotateSpeed*t:Oe/60/60*this.autoRotateSpeed}_getZoomScale(t){const e=Math.abs(t*.01);return Math.pow(.95,this.zoomSpeed*e)}_rotateLeft(t){this._sphericalDelta.theta-=t}_rotateUp(t){this._sphericalDelta.phi-=t}_panLeft(t,e){_e.setFromMatrixColumn(e,0),_e.multiplyScalar(-t),this._panOffset.add(_e)}_panUp(t,e){this.screenSpacePanning===!0?_e.setFromMatrixColumn(e,1):(_e.setFromMatrixColumn(e,0),_e.crossVectors(this.object.up,_e)),_e.multiplyScalar(t),this._panOffset.add(_e)}_pan(t,e){const n=this.domElement;if(this.object.isPerspectiveCamera){const s=this.object.position;_e.copy(s).sub(this.target);let r=_e.length();r*=Math.tan(this.object.fov/2*Math.PI/180),this._panLeft(2*t*r/n.clientHeight,this.object.matrix),this._panUp(2*e*r/n.clientHeight,this.object.matrix)}else this.object.isOrthographicCamera?(this._panLeft(t*(this.object.right-this.object.left)/this.object.zoom/n.clientWidth,this.object.matrix),this._panUp(e*(this.object.top-this.object.bottom)/this.object.zoom/n.clientHeight,this.object.matrix)):(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - pan disabled."),this.enablePan=!1)}_dollyOut(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale/=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_dollyIn(t){this.object.isPerspectiveCamera||this.object.isOrthographicCamera?this._scale*=t:(console.warn("WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled."),this.enableZoom=!1)}_updateZoomParameters(t,e){if(!this.zoomToCursor)return;this._performCursorZoom=!0;const n=this.domElement.getBoundingClientRect(),s=t-n.left,r=e-n.top,a=n.width,o=n.height;this._mouse.x=s/a*2-1,this._mouse.y=-(r/o)*2+1,this._dollyDirection.set(this._mouse.x,this._mouse.y,1).unproject(this.object).sub(this.object.position).normalize()}_clampDistance(t){return Math.max(this.minDistance,Math.min(this.maxDistance,t))}_handleMouseDownRotate(t){this._rotateStart.set(t.clientX,t.clientY)}_handleMouseDownDolly(t){this._updateZoomParameters(t.clientX,t.clientX),this._dollyStart.set(t.clientX,t.clientY)}_handleMouseDownPan(t){this._panStart.set(t.clientX,t.clientY)}_handleMouseMoveRotate(t){this._rotateEnd.set(t.clientX,t.clientY),this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Oe*this._rotateDelta.x/e.clientHeight),this._rotateUp(Oe*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd),this.update()}_handleMouseMoveDolly(t){this._dollyEnd.set(t.clientX,t.clientY),this._dollyDelta.subVectors(this._dollyEnd,this._dollyStart),this._dollyDelta.y>0?this._dollyOut(this._getZoomScale(this._dollyDelta.y)):this._dollyDelta.y<0&&this._dollyIn(this._getZoomScale(this._dollyDelta.y)),this._dollyStart.copy(this._dollyEnd),this.update()}_handleMouseMovePan(t){this._panEnd.set(t.clientX,t.clientY),this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd),this.update()}_handleMouseWheel(t){this._updateZoomParameters(t.clientX,t.clientY),t.deltaY<0?this._dollyIn(this._getZoomScale(t.deltaY)):t.deltaY>0&&this._dollyOut(this._getZoomScale(t.deltaY)),this.update()}_handleKeyDown(t){let e=!1;switch(t.code){case this.keys.UP:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(Oe*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,this.keyPanSpeed),e=!0;break;case this.keys.BOTTOM:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateUp(-Oe*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(0,-this.keyPanSpeed),e=!0;break;case this.keys.LEFT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(Oe*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(this.keyPanSpeed,0),e=!0;break;case this.keys.RIGHT:t.ctrlKey||t.metaKey||t.shiftKey?this.enableRotate&&this._rotateLeft(-Oe*this.keyRotateSpeed/this.domElement.clientHeight):this.enablePan&&this._pan(-this.keyPanSpeed,0),e=!0;break}e&&(t.preventDefault(),this.update())}_handleTouchStartRotate(t){if(this._pointers.length===1)this._rotateStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._rotateStart.set(n,s)}}_handleTouchStartPan(t){if(this._pointers.length===1)this._panStart.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._panStart.set(n,s)}}_handleTouchStartDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,s=t.pageY-e.y,r=Math.sqrt(n*n+s*s);this._dollyStart.set(0,r)}_handleTouchStartDollyPan(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enablePan&&this._handleTouchStartPan(t)}_handleTouchStartDollyRotate(t){this.enableZoom&&this._handleTouchStartDolly(t),this.enableRotate&&this._handleTouchStartRotate(t)}_handleTouchMoveRotate(t){if(this._pointers.length==1)this._rotateEnd.set(t.pageX,t.pageY);else{const n=this._getSecondPointerPosition(t),s=.5*(t.pageX+n.x),r=.5*(t.pageY+n.y);this._rotateEnd.set(s,r)}this._rotateDelta.subVectors(this._rotateEnd,this._rotateStart).multiplyScalar(this.rotateSpeed);const e=this.domElement;this._rotateLeft(Oe*this._rotateDelta.x/e.clientHeight),this._rotateUp(Oe*this._rotateDelta.y/e.clientHeight),this._rotateStart.copy(this._rotateEnd)}_handleTouchMovePan(t){if(this._pointers.length===1)this._panEnd.set(t.pageX,t.pageY);else{const e=this._getSecondPointerPosition(t),n=.5*(t.pageX+e.x),s=.5*(t.pageY+e.y);this._panEnd.set(n,s)}this._panDelta.subVectors(this._panEnd,this._panStart).multiplyScalar(this.panSpeed),this._pan(this._panDelta.x,this._panDelta.y),this._panStart.copy(this._panEnd)}_handleTouchMoveDolly(t){const e=this._getSecondPointerPosition(t),n=t.pageX-e.x,s=t.pageY-e.y,r=Math.sqrt(n*n+s*s);this._dollyEnd.set(0,r),this._dollyDelta.set(0,Math.pow(this._dollyEnd.y/this._dollyStart.y,this.zoomSpeed)),this._dollyOut(this._dollyDelta.y),this._dollyStart.copy(this._dollyEnd);const a=(t.pageX+e.x)*.5,o=(t.pageY+e.y)*.5;this._updateZoomParameters(a,o)}_handleTouchMoveDollyPan(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enablePan&&this._handleTouchMovePan(t)}_handleTouchMoveDollyRotate(t){this.enableZoom&&this._handleTouchMoveDolly(t),this.enableRotate&&this._handleTouchMoveRotate(t)}_addPointer(t){this._pointers.push(t.pointerId)}_removePointer(t){delete this._pointerPositions[t.pointerId];for(let e=0;e{e.isMesh&&(t.add(e.geometry),t.add(e.material))});for(const e of t)e.dispose()}}function _i(i){return new vu({color:0,emissive:16777215,emissiveIntensity:i})}const qi=new D;function qe(i,t,e,n,s,r){const a=2*Math.PI*s/4,o=Math.max(r-2*s,0),l=Math.PI/4;qi.copy(t),qi[n]=0,qi.normalize();const c=.5*a/(a+o),h=1-qi.angleTo(i)/l;return Math.sign(qi[e])===1?h*c:o/(a+o)+c+c*(1-h)}class vn extends oe{constructor(t=1,e=1,n=1,s=2,r=.1){const a=s*2+1;if(r=Math.min(t/2,e/2,n/2,r),super(1,1,1,a,a,a),this.type="RoundedBoxGeometry",this.parameters={width:t,height:e,depth:n,segments:s,radius:r},a===1)return;const o=this.toNonIndexed();this.index=null,this.attributes.position=o.attributes.position,this.attributes.normal=o.attributes.normal,this.attributes.uv=o.attributes.uv;const l=new D,c=new D,h=new D(t,e,n).divideScalar(2).subScalar(r),u=this.attributes.position.array,d=this.attributes.normal.array,p=this.attributes.uv.array,x=u.length/6,_=new D,m=.5/a;for(let f=0,b=0;fe.fillText(c,n/2,n/2+(h-(o.length-1)/2)*l,n*.94));return}e.fillStyle="#5ee7ff";for(const r of[.33,.67])e.beginPath(),e.ellipse(n*r,n*.4,n*.08,n*.12,0,0,Math.PI*2),e.fill();e.strokeStyle="#5ee7ff",e.lineWidth=n*.045,e.lineCap="round",e.beginPath(),e.arc(n/2,n*.55,n*.18,Math.PI*.2,Math.PI*.8),e.stroke(),e.fillStyle="#ffffff",e.font=`bold ${Math.round(n*.11)}px system-ui, sans-serif`,e.textAlign="center",e.fillText("VisuBot",n/2,n*.9)}function zr(i,t,e,n,s){const r=document.createElement("canvas");r.width=t,r.height=e;const a=r.getContext("2d");a&&(s&&(a.fillStyle=s,a.fillRect(0,0,t,e)),a.fillStyle=n,a.textAlign="center",a.textBaseline="middle",i.forEach((l,c)=>{a.font=`${l.bold?"bold ":""}${l.size}px system-ui, sans-serif`,a.fillText(l.text,t/2,e*(c+.5)/i.length)}));const o=new Ka(r);return o.colorSpace=De,o}function Um(){const i=document.createElement("canvas");i.width=i.height=256;const t=i.getContext("2d");if(t){t.fillStyle="#16181b",t.fillRect(0,0,256,256),t.strokeStyle="#2c3036",t.lineWidth=9;for(let n=0;n<6;n++)for(let s=0;s<6;s++){const r=38+n*36,a=38+s*36;t.beginPath(),(n+s)%2?t.arc(r,a,12,0,Math.PI*2):t.rect(r-11,a-11,22,22),t.stroke()}}const e=new Ka(i);return e.colorSpace=De,e}function Nm(){const i=new js;i.moveTo(-7.2,1.4),i.lineTo(4.8,1.4),i.lineTo(9.6,4.6),i.lineTo(8.8,5.9),i.lineTo(4.6,3.6),i.lineTo(4.6,4.7),i.lineTo(-6.6,4.7),i.absarc(-8.4,3.1,1.9,Math.PI*.35,Math.PI*1.55,!1),i.lineTo(-7.2,1.4);const t=(e,n,s)=>{const r=new rs;r.absarc(e,n,s,0,Math.PI*2,!0),i.holes.push(r)};t(-8.4,3.1,.55);for(let e=0;e<8;e++){const n=e/8*Math.PI*2;Math.cos(n)>.6||t(-8.4+Math.cos(n)*1.25,3.1+Math.sin(n)*1.25,.22)}for(let e=0;e<4;e++)for(let n=0;n<3;n++)t(-5.6+e*.95,2.1+n*.95,.25);for(let e=0;e<3;e++)t(6+e*1.05,4+e*.7,.22);return i}function Fm(){const i=new ln,t=new Map,e={},n=(B,y=i)=>{const Z=new ln;return Z.name=B,e[B]=Z,t.set(Z,B),y.add(Z),Z},s=(B,y,Z,Q,ot,et)=>{const vt=new Vt(B,y);return vt.position.set(Z,Q,ot),et.add(vt),vt},r=ve(Im,{metalness:.7,roughness:.3}),a=ve(1776929,{roughness:.6}),o=ve(2764341,{roughness:.45}),l=ve(13225686,{metalness:.9,roughness:.25}),c=ve(1315860,{roughness:.9}),h=ve(10133671,{roughness:.55}),u=ve(15921906,{roughness:.5}),d=ve(1909286,{roughness:.7}),p=new _u({color:2501681,transparent:!0,opacity:.55,roughness:.12,metalness:.1,clearcoat:1,depthWrite:!1}),x=n("chassis"),_=new js;_.moveTo(-4.9,-7.2),_.lineTo(4.9,-7.2),_.lineTo(4.9,4.8),_.lineTo(-4.9,4.8);const m=new rs;m.moveTo(-3.6,-3.4),m.lineTo(-3.6,2.8),m.lineTo(3.6,2.8),m.lineTo(3.6,-3.4),_.holes.push(m);const f=new Ji(_,{depth:.22,bevelEnabled:!1});f.rotateX(Math.PI/2),s(f,r,0,1.62,0,x);const b=new Ji(Nm(),{depth:.22,bevelEnabled:!1,curveSegments:16});b.rotateY(-Math.PI/2);for(const B of[-1,1])s(b,r,B*4.9+(B>0?.22:0),0,0,x);s(new oe(9.8,3.3,.22),r,0,3.05,-7.2,x);const S=new Vt(new Si(1.1,.16,6,20,Math.PI*.8),a);S.rotation.z=Math.PI*1.1,S.position.set(0,2.9,-7.33),x.add(S);const T=new js;T.moveTo(-4.9,0),T.lineTo(4.9,0),T.lineTo(4.9,3.4),T.lineTo(-4.9,3.4);const R=new rs;R.moveTo(-1.1,.6),R.lineTo(-1.1,2.6),R.lineTo(-.5,2.6),R.lineTo(0,1.9),R.lineTo(.5,2.6),R.lineTo(1.1,2.6),R.lineTo(1.1,.6),R.lineTo(.6,.6),R.lineTo(.6,1.8),R.lineTo(0,1.1),R.lineTo(-.6,1.8),R.lineTo(-.6,.6),T.holes.push(R);const w=new Vt(new Ji(T,{depth:.22,bevelEnabled:!1}),r);w.rotation.x=-.35,w.position.set(0,1.5,4.8),x.add(w);const L=n("motors"),F=ve(9225450,{roughness:.55});for(const B of[-1,1]){s(new vn(2,2.8,3,2,.2),F,B*3.7,3.3,0,L);const y=s(new Ee(.75,.75,2.8,20),l,B*1.55,3,B*.8,L);y.rotation.z=Math.PI/2;const Z=s(new Ee(.8,.8,.5,20),F,B*2.6,3,B*.8,L);Z.rotation.z=Math.PI/2}const E=n("wheels"),M=ve(16777215,{map:Um(),roughness:.6});for(const B of[-1,1]){const y=new ln,Z=new Vt(new Ee(3.3,3.3,2.2,44),c);Z.rotation.z=Math.PI/2,y.add(Z);const Q=new Vt(new Si(2.75,.32,10,44),c);Q.rotation.y=Math.PI/2,Q.position.x=B*1.1,y.add(Q);const ot=new Vt(new ss(2.6,40),M);ot.rotation.y=B*Math.PI/2,ot.position.x=B*1,y.add(ot);const et=new Vt(new Ee(.3,.3,.3,12),l);et.rotation.z=Math.PI/2,et.position.x=B*1.05,y.add(et),y.position.set(B*6.45,3.3,0),y.name=B<0?"wheelLeft":"wheelRight",E.add(y)}const P=n("caster");s(new $i(1.3,20,10,0,Math.PI*2,0,Math.PI/2),a,0,.75,-4.6,P).scale.set(1.3,.7,1),s(new $i(.75,20,14),a,0,.75,-4.6,P);for(const B of[-.9,.9])s(new Ee(.25,.25,.2,12),l,B,1.28,-5.6,P);const z=n("lineSensor");s(new vn(8,.3,2.4,2,.1),d,0,1.25,-8,z);for(let B=0;B<4;B++){const y=-3.3+B*2.2;s(new oe(.6,.12,.6),ve(16119285,{emissive:16777215,emissiveIntensity:.5}),y,1.06,-8.3,z),s(new $i(.13,8,6),ve(16728128,{emissive:16722474,emissiveIntensity:1.4}),y,1.06,-7.6,z)}const k=n("shield"),W=new Vt(new vn(9.4,3.4,11.2,3,.5),p);W.position.set(0,6.5,-1.4),W.renderOrder=1,k.add(W),s(new oe(9,.16,10.8),d,0,5.1,-1.4,k);for(const[B,y]of[[-2,-4],[1.5,-3.6],[-1.6,-2.2],[1.8,-2.4]])s(new Ee(.45,.45,.4,14),a,B,5.35,y,k);const Y=zr([{text:"makeblock",size:56,bold:!0},{text:"mBot2 One Robot Per Child",size:30}],512,160,"#e9edf0","#777d84"),nt=new Vt(new Ee(1.05,1.05,7,28),new Te({map:Y,roughness:.4,metalness:.3}));nt.rotation.z=Math.PI/2,nt.rotation.x=-Math.PI/2,nt.position.set(0,6.3,.6),k.add(nt);for(const B of[-3.2,-2.3])s(new oe(.6,.5,.3),u,B,5.6,4.25,k);for(const B of[.4,2.2]){s(new oe(1.5,.45,.35),u,B,5.6,4.25,k);const y=s(new Si(.7,.12,6,12,Math.PI),a,B,5,4.5,k);y.rotation.y=Math.PI/2}const X=n("powerSwitch");s(new oe(.2,.5,1.2),o,-4.72,5.8,-3.4,X),s(new oe(.3,.38,.45),u,-4.8,5.8,-3.6,X);const rt=new Vt(new Nn(1.4,.4),new Ys({map:zr([{text:"ON/OFF",size:44,bold:!0}],256,72,"#e9edf0",void 0),transparent:!0}));rt.rotation.y=-Math.PI/2,rt.position.set(-4.76,5.25,-3.4),X.add(rt);for(const B of[-1.9,-.9])s(new oe(.3,.45,.7),u,-4.6,5.8,B,k);const st=new ln;i.add(st);const St=n("cyberpi",st),Gt=new Vt(new vn(7.9,1,3.1,3,.25),p);Gt.renderOrder=1,St.add(Gt),s(new oe(7.6,.12,2.9),d,0,-.1,0,St);const qt=new Vt(new Nn(1.6,.4),new Ys({map:zr([{text:"CyberPi",size:44}],256,64,"#c9d1d8",void 0),transparent:!0}));qt.position.set(-2.6,0,1.56),St.add(qt),s(new oe(.12,.3,.9),a,3.98,.05,-.7,St);const jt=n("screen",st),Kt=document.createElement("canvas");Kt.width=Kt.height=256,ll(Kt);const K=new Ka(Kt);K.colorSpace=De,s(new oe(3,.08,2.6),ve(14672870),.1,.47,0,jt);const J=new Vt(new Nn(2.7,2.3),new Te({map:K,emissive:16777215,emissiveMap:K,emissiveIntensity:.9,roughness:.15}));J.rotation.x=-Math.PI/2,J.position.set(.1,.52,0),jt.add(J);const xt=n("joystick",st);s(new Ee(.75,.75,.12,24),ve(9080982),-2.75,.52,0,xt),s(new Ee(.22,.22,.5,12),ve(7107192),-2.75,.75,0,xt),s(new Ee(.55,.6,.32,24),h,-2.75,1.05,0,xt);const Rt=n("buttons",st);s(new vn(.85,.3,.85,2,.15),h,2.6,.58,-.62,Rt);const Tt=s(new Ee(.55,.55,.3,3),h,2.6,.58,.68,Rt);Tt.rotation.y=Math.PI/2,s(new Ee(.24,.24,.22,14).rotateZ(Math.PI/2),h,3.98,.05,.5,Rt);const kt=n("leds",st);s(new oe(6.6,.3,.06),ve(15658734,{transparent:!0,opacity:.3,roughness:.2}),0,-.12,1.68,kt);const Qt=[];for(let B=0;B<5;B++){const y=new Te({color:14540253,emissiveIntensity:1,roughness:.3,toneMapped:!1});Qt.push(y),s(new oe(.7,.22,.08),y,-2.8+B*1.4,-.12,1.63,kt)}for(const[B,y]of[[-1.75,"lightSensor"],[-1.75,"microphone"]]){const Z=y==="lightSensor"?-1.05:1.05;s(new Ee(.14,.14,.08,10),ve(657930,{roughness:.2}),B,.52,Z,n(y,st))}st.position.set(0,8.55,3.15);const Ct=n("ultrasonic");s(new vn(6.4,2.8,1.3,3,.4),a,0,0,0,Ct);for(const B of[-1,1]){const y=s(new Ee(1.2,1.2,1.3,32),a,B*1.6,0,-1.1,Ct);y.rotation.x=Math.PI/2;const Z=s(new Si(.98,.17,10,32),ve(10396842,{metalness:.5,roughness:.35}),B*1.6,0,-1.76,Ct);Z.name="sonarRing";const Q=s(new ss(.85,28),ve(3817285,{roughness:.9}),B*1.6,0,-1.7,Ct);Q.rotation.y=Math.PI}s(new vn(2.6,1.5,.3,2,.12),a,0,-1.6,-.3,Ct);for(const B of[-.7,.7])s(new Ee(.28,.28,.2,12).rotateX(Math.PI/2),l,B,-1.75,-.5,Ct);return Ct.position.set(0,6.4,-8),i.traverse(B=>{const y=B;if(!y.isMesh)return;const Z=y.material.transparent;y.castShadow=!Z,y.receiveShadow=!0;const Q=y.material;Qt.includes(Q)||(y.material=Q.clone())}),{group:i,parts:t,partGroups:e,leds:Qt,screen:{canvas:Kt,texture:K}}}const $e=(i,t=0)=>new D(i.x,t,-i.y),Vr=i=>i*Math.PI/180,qn={floor:16052714,grid:14078148,wall:13214827,tape:2236962,trail:15227148},Om=12.9,yc=3.3;class Vm{constructor(t){le(this,"canvas");le(this,"renderer");le(this,"scene",new kc);le(this,"camera",new ke(45,1,1,2e3));le(this,"controls");le(this,"model");le(this,"robot");le(this,"trailGroup",new ln);le(this,"trailPiece",new oe(1,.15,1.4));le(this,"trailMaterial",new Te({color:qn.trail}));le(this,"liveSegment");le(this,"worldGroup",new ln);le(this,"frame");le(this,"sun");le(this,"resize");le(this,"shown",{x:0,y:0,heading:0});le(this,"anim");le(this,"trail",[]);le(this,"wasShown",!1);le(this,"wheelPose");le(this,"flight");le(this,"shownScreen");le(this,"loop",()=>{if(this.canvas.isConnected)this.wasShown=!0;else if(this.wasShown)return this.dispose();this.frame=requestAnimationFrame(this.loop);let t=this.shown;if(this.anim){const n=Math.min(1,(performance.now()-this.anim.start)/this.anim.ms),s=n<.5?2*n*n:1-(-2*n+2)**2/2,{from:r,to:a}=this.anim;t={x:r.x+(a.x-r.x)*s,y:r.y+(a.y-r.y)*s,heading:r.heading+(a.heading-r.heading)*s},n>=1&&(this.shown={...a},this.anim=void 0)}this.applyPose(t);const e=!!this.flight;if(this.flight){const n=Math.min(1,(performance.now()-this.flight.start)/this.flight.ms),s=n<.5?4*n*n*n:1-(-2*n+2)**3/2,{from:r,to:a}=this.flight;this.controls.target.lerpVectors(r.target,a.target,s);const o=r.pos.clone().sub(r.target),l=a.pos.clone().sub(a.target),c=o.clone().normalize().lerp(l.clone().normalize(),s);c.lengthSq()<1e-4&&c.set(-1,0,0),c.normalize();const h=o.length()+(l.length()-o.length())*s;this.camera.position.copy(this.controls.target).addScaledVector(c,h),n>=1&&(this.flight=void 0)}(e||this.controls.autoRotate)&&this.controls.update(),this.renderer.render(this.scene,this.camera)});this.host=t,this.canvas=document.createElement("canvas"),this.canvas.className="visubot-canvas",t.append(this.canvas),this.renderer=new gm({canvas:this.canvas,antialias:!0,alpha:!0}),this.renderer.setPixelRatio(Math.min(2,window.devicePixelRatio)),this.renderer.toneMapping=Tc,this.renderer.shadowMap.enabled=!0,this.renderer.shadowMap.type=Ec;const e=new Ia(this.renderer);this.scene.environment=e.fromScene(new Lm,.04).texture,this.scene.environmentIntensity=.7,e.dispose(),this.scene.add(new yu(16777215,9077104,.9));const n=new Au(16777215,1.8);n.position.set(-40,90,50),n.castShadow=!0,n.shadow.mapSize.set(1024,1024),n.shadow.camera.left=n.shadow.camera.bottom=-30,n.shadow.camera.right=n.shadow.camera.top=30,n.shadow.camera.far=300,n.shadow.bias=-5e-4,this.sun=n,this.scene.add(n,n.target),this.scene.add(this.worldGroup),this.model=Fm(),this.robot=this.model.group,this.scene.add(this.robot),this.scene.add(this.trailGroup),this.liveSegment=new Vt(this.trailPiece,this.trailMaterial),this.scene.add(this.liveSegment),this.camera.position.set(0,75,95),this.controls=new vm(this.camera,this.canvas),this.controls.target.set(0,0,-15),this.controls.maxPolarAngle=Math.PI-.05,this.controls.minDistance=12,this.controls.maxDistance=600,this.controls.update(),this.controls.addEventListener("start",()=>{this.flight=void 0,this.controls.autoRotate=!1}),this.resize=new ResizeObserver(()=>this.fit()),this.resize.observe(t),this.fit(),this.loop()}dispose(){this.frame!==void 0&&cancelAnimationFrame(this.frame),this.resize.disconnect(),this.controls.dispose(),this.renderer.dispose(),this.renderer.forceContextLoss(),this.canvas.remove()}setWorld(t){this.worldGroup.clear();const e=Math.max(t.width,t.depth,80);this.flight=void 0,this.controls.autoRotate=!1,this.controls.target.set(0,0,0),this.camera.position.set(0,e*.95,e*.5),this.controls.update();const n=new Vt(new Nn(t.width,t.depth),new Te({color:qn.floor,roughness:.9}));n.rotation.x=-Math.PI/2,n.receiveShadow=!0,this.worldGroup.add(n);const s=[];for(let b=-t.width/2;b<=t.width/2+1e-6;b+=10)s.push(b,.05,-t.depth/2,b,.05,t.depth/2);for(let b=-t.depth/2;b<=t.depth/2+1e-6;b+=10)s.push(-t.width/2,.05,b,t.width/2,.05,b);const r=new Ie;r.setAttribute("position",new he(s,3)),this.worldGroup.add(new Bh(r,new Wc({color:qn.grid})));const a=[{from:{x:-t.width/2,y:-t.depth/2},to:{x:t.width/2,y:-t.depth/2}},{from:{x:t.width/2,y:-t.depth/2},to:{x:t.width/2,y:t.depth/2}},{from:{x:t.width/2,y:t.depth/2},to:{x:-t.width/2,y:t.depth/2}},{from:{x:-t.width/2,y:t.depth/2},to:{x:-t.width/2,y:-t.depth/2}}],o=new Te({color:qn.wall});for(const b of[...a.map(S=>({...S,low:!0})),...t.walls.map(S=>({...S,low:!1}))]){const S=Math.hypot(b.to.x-b.from.x,b.to.y-b.from.y),T=b.low?4:15,R=new Vt(new oe(S,T,2),o);R.position.copy($e({x:(b.from.x+b.to.x)/2,y:(b.from.y+b.to.y)/2},T/2)),R.rotation.y=Math.atan2(b.to.y-b.from.y,b.to.x-b.from.x),this.worldGroup.add(R)}const l=new oe(rn,12,rn);for(const b of t.blocks??[]){const S=new Vt(l,o);S.position.copy($e(b,6)),S.castShadow=S.receiveShadow=!0,this.worldGroup.add(S)}const c=new oe(rn,.1,rn),h=new Te({color:qn.tape});for(const b of t.marks??[]){const S=new Vt(c,h);S.position.copy($e(b,.1)),this.worldGroup.add(S)}const u=new Te({color:qn.tape}),d=new Te({color:3126084,emissive:1800748,emissiveIntensity:.4}),p=t.goals??[],x=(b,S)=>p.some(T=>Math.abs(T.x-b)<1e-6&&Math.abs(T.y-S)<1e-6);for(const b of p){const S=new Vt(c,u);if(S.position.copy($e(b,.1)),this.worldGroup.add(S),x(b.x+rn,b.y)||x(b.x,b.y+rn))continue;const T=new Vt(new Ee(.25,.25,9,8),new Te({color:14540253}));T.position.copy($e({x:b.x+rn/2-1,y:b.y+rn/2-1},4.5));const R=new Vt(new oe(4,2.6,.2),d);R.position.copy($e({x:b.x+rn/2-3,y:b.y+rn/2-1},7.6)),this.worldGroup.add(T,R)}const _={red:14692657,green:3126084,blue:1867478,yellow:16429061};for(const b of t.colors??[]){const S=new Vt(c,new Te({color:_[b.color]}));S.position.copy($e(b.at,.1)),this.worldGroup.add(S)}const m=new Te({color:3126084,transparent:!0,opacity:.3,depthWrite:!1});for(const b of t.targets??[]){const S=new Vt(c,m);S.position.copy($e(b,.12)),this.worldGroup.add(S)}const f=new Te({color:qn.tape});for(const b of t.lines)for(let S=0;S+1({...n})),this.trailGroup.clear(),this.applyPose(this.shown)}moveTo(t,e,n){this.anim&&(this.shown={...this.anim.to}),this.trail=e.map(a=>({...a}));let s={...t};const r=s.heading-this.shown.heading;r>180?s={...s,heading:s.heading-360}:r<-180&&(s={...s,heading:s.heading+360}),this.anim={from:{...this.shown},to:s,start:performance.now(),ms:Math.max(1,n)}}showcase(){this.worldGroup.clear();const t=new Vt(new ss(16,48),new Te({color:15328474,roughness:.95}));t.rotation.x=-Math.PI/2,t.receiveShadow=!0,this.worldGroup.add(t),this.place({x:0,y:0,heading:0},[{x:0,y:0}]),this.controls.target.set(0,5,-2),this.camera.position.set(-20,18,-24),this.controls.update()}highlight(t){const e=new Set(t===void 0?[]:Array.isArray(t)?t:[t]);for(const[n,s]of Object.entries(this.model.partGroups))s.traverse(r=>{var c;const a=r;if(!a.isMesh)return;const o=a.material;if(!o.emissive)return;(c=o.userData).baseEmissive??(c.baseEmissive={color:o.emissive.getHex(),intensity:o.emissiveIntensity});const l=o.userData.baseEmissive;e.has(n)?(o.emissive.setHex(16756736),o.emissiveIntensity=Math.max(l.intensity,.22)):(o.emissive.setHex(l.color),o.emissiveIntensity=l.intensity)})}focusOn(t,e="",n=!1,s=900){const r=new On;this.robot.updateMatrixWorld(!0);for(const u of t)r.expandByObject(this.model.partGroups[u]);r.isEmpty()&&r.setFromObject(this.robot);const a=r.getCenter(new D),o=r.getSize(new D).length(),l=new D(...{vorn:[0,.35,-1],front:[0,.35,-1],hinten:[0,.55,1],back:[0,.55,1],oben:[0,1,.12],top:[0,1,.12],unten:[.05,-1,-.3],bottom:[.05,-1,-.3],seite:[-1,.5,.15],side:[-1,.5,.15]}[e]??[-.75,.6,-.85]).normalize(),c=Math.max(14,o*(t.length?1.8:1.35)+6),h=a.clone().addScaledVector(l,c);this.flight={from:{pos:this.camera.position.clone(),target:this.controls.target.clone()},to:{pos:h,target:a},start:performance.now(),ms:s},this.controls.autoRotate=n,this.controls.autoRotateSpeed=1.2}setScreen(t){t!==this.shownScreen&&(this.shownScreen=t,ll(this.model.screen.canvas,t),this.model.screen.texture.needsUpdate=!0)}setLed(t){this.setLeds(this.model.leds.map(()=>t))}setLeds(t){for(const[e,n]of this.model.leds.entries()){const s=t[e];s?(n.emissive.setRGB(s[0]/255,s[1]/255,s[2]/255),n.color.setRGB(s[0]/255,s[1]/255,s[2]/255)):(n.emissive.setRGB(0,0,0),n.color.set(14540253))}}partAt(t,e){const n=this.canvas.getBoundingClientRect(),s=new Ru;s.setFromCamera(new at((t-n.left)/n.width*2-1,-((e-n.top)/n.height)*2+1),this.camera);for(const r of s.intersectObject(this.robot,!0))for(let a=r.object;a;a=a.parent){const o=this.model.parts.get(a);if(o)return o}}fit(){const t=Math.max(1,this.host.clientWidth),e=Math.max(1,this.host.clientHeight);this.renderer.setSize(t,e,!1),this.camera.aspect=t/e,this.camera.updateProjectionMatrix()}applyPose(t){this.turnWheels(t),this.robot.position.copy($e(t)),this.robot.rotation.y=-Vr(t.heading),this.sun.target.position.copy(this.robot.position),this.sun.position.copy(this.robot.position).add(new D(-40,90,50));const e=this.anim?this.trail.length-2:this.trail.length-1;for(;this.trailGroup.children.length>Math.max(0,e);)this.trailGroup.remove(this.trailGroup.children[this.trailGroup.children.length-1]);for(let s=this.trailGroup.children.length;s.01,t.scale.set(s+1.4,1,1),t.position.copy($e({x:(e.x+n.x)/2,y:(e.y+n.y)/2},.2)),t.rotation.y=Math.atan2(n.y-e.y,n.x-e.x)}}export{Vm as Robot3D}; diff --git a/website/app/assets/ru-bst4sg1r.js b/website/app/assets/ru-bst4sg1r.js deleted file mode 100644 index 05968349..00000000 --- a/website/app/assets/ru-bst4sg1r.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Тексты, которые есть только в веб-версии. Все остальные берутся из Localizable.strings приложения. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Структурная схема VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Экспорт…"; -"web_export_title" = "Сохранить как изображение или PDF либо распечатать"; -"web_export_png" = "Изображение (PNG)"; -"web_export_svg" = "Векторная графика (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Файл VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Ошибка экспорта: %@"; -"web_print_hint" = "Через «Экспорт… → PDF» можно сохранить схему и затем распечатать её."; -"web_delete" = "Удалить"; -"web_language_title" = "Язык интерфейса"; -"web_simulation_start" = "Запустить симуляцию"; -"web_simulation_end" = "Завершить симуляцию"; -"web_simulation_start_title" = "Выполнить схему шаг за шагом"; -"web_simulation_end_title" = "Завершить симуляцию и продолжить редактирование"; -"web_palette_hint" = "Перетащите в схему"; -"web_tabs_label" = "Схемы"; -"web_status_modified" = "изменено"; -"web_inspector_title" = "Свойства"; -"web_inspector_resize" = "Изменить высоту инспектора объектов: перетащите линию вверх или вниз (двойной щелчок: по умолчанию)"; -"web_inspector_help_drop" = "Перетаскивайте элементы из палитры на плитку: сверху = перед, снизу = после, на «ø» = заменить. Существующие элементы можно перемещать мышью."; -"web_inspector_help_inline" = "Дважды щёлкните или коснитесь элемента, чтобы изменить текст прямо в диаграмме (Enter — применить, Esc — отмена). Так же меняются имена вариантов."; -"web_inspector_help_files" = "Файлы также можно перетащить в окно."; -"web_case_name" = "Имя варианта"; -"web_case_name_placeholder" = "напр. 1, ok, default"; -"web_copied" = "Элемент скопирован."; -"web_discard_message_fmt" = "Изменения в «%@» будут потеряны."; -"web_err_unreadable_fmt" = "«%@» не является читаемым файлом VisuStruct (%@)."; -"web_saved_fmt" = "«%@» сохранён."; -"web_saved_txt_fmt" = "Сохранено как «%@». Удалите расширение .txt, чтобы использовать файл."; -"web_drop_impossible" = "Сюда нельзя поместить"; -"web_sim_delay" = "Пауза на шаг"; -"web_sim_delay_value_fmt" = "%@ с"; -"web_codegen_language" = "Язык"; -"web_codegen_comments" = "Текст схемы как комментарии"; -"web_codegen_empty_else" = "Выводить пустую ветку else в коде"; -"web_copied_check" = "Скопировано ✓"; -"web_copy_manual" = "Выделено, нажмите Ctrl+C для копирования"; -"web_run_stopped_loop" = "Выполнение остановлено: возможен бесконечный цикл."; -"web_run_cancelled" = "Выполнение отменено."; -"web_run_error" = "Ошибка при выполнении."; -"web_run_dropped_fmt" = "… скрыто более ранних строк: %d"; -"web_sim_call_arguments_fmt" = "Вызов не соответствует заголовку %@. Проверь значения в скобках."; -"web_sim_missing_return_fmt" = "%@ завершилась, не вернув значение через return."; -"web_sim_file_not_found_fmt" = "Файла с именем %@ нет."; -"web_sim_end_of_file_fmt" = "В файле %@ больше нет строк."; -"web_sim_null_value_fmt" = "%@ равно null."; -"web_sim_unknown_variable_case_fmt" = "Неизвестная переменная: %1$@. Java различает заглавные и строчные буквы: может быть, %2$@?"; -"web_sim_written_file_fmt" = "%@ (сохранён)"; -"web_site_home" = "Главная страница"; -"web_site_imprint" = "Выходные данные"; -"web_site_privacy" = "Конфиденциальность"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Программирование"; -"web_live_hide" = "Скрыть программирование"; -"web_scale_canvas" = "Размер текста: структурная схема"; -"web_scale_code" = "Размер текста: программирование"; -"web_live_indent" = "Отступы"; -"web_live_indent_title" = "Расставить отступы: четыре пробела на уровень (выделенные строки, иначе всё)"; -"web_live_show" = "Показать программирование"; -"web_live_resize" = "Изменить ширину области кода (двойной щелчок: сбросить)"; -"web_live_hint" = "Код растёт вместе с диаграммой. Серый = заполнитель: пиши в инспекторе, и текст появится здесь. Щёлкни строку, чтобы выбрать элемент."; -"web_live_ph_selector" = "переменная"; -"web_live_ph_code" = "Твой код"; -"web_live_ph_end" = "конец"; -"web_live_language" = "Язык программирования"; -"web_live_java_class" = "Как класс"; -"web_live_java_class_hint" = "Имя класса = заголовок диаграммы"; -"web_live_java_main" = "С методом main"; -"web_settings_display" = "Отображение"; -"web_live_swing" = "Настоящая Java (со Swing)"; -"web_live_swing_hint" = "Настоящая Java с окнами (CheerpJ). При первом запуске однократно загружает около 40 МБ."; -"web_live_swing_windows" = "Окна Java"; -"web_live_swing_hide" = "Закрыть окна Java"; -"web_live_swing_loading" = "Загрузка Java (в первый раз около 40 МБ) …"; -"web_live_swing_compiling" = "Компиляция …"; -"web_live_swing_running" = "Программа выполняется."; -"web_live_swing_compile_failed" = "Ошибка компиляции, см. выше."; -"web_live_swing_failed" = "Не удалось загрузить Java. Проверь подключение к интернету."; -"web_live_hint_edit" = "Набери здесь код Java, и диаграмма построится сама. Или работай в диаграмме, и код будет расти вместе с ней. Серый = заполнитель, просто пиши поверх."; -"web_live_edit_label" = "Код Java"; -"web_parse_line_fmt" = "Строка %d: %@"; -"web_parse_missing" = "Здесь ещё не хватает: %@"; -"web_parse_extra" = "Лишнее: %@"; -"web_parse_unclosed_string" = "Не хватает закрывающей %@."; -"web_parse_else_without_if" = "У этого «else» нет «if»."; -"web_parse_case_outside_switch" = "«case» и «default» должны быть внутри «switch»."; -"web_parse_missing_do_while" = "После «do { … }» не хватает «while (условие);»."; -"web_parse_more_methods" = "Пока здесь учитывается только один метод. Создай другие подпрограммы в отдельных вкладках."; -"web_parse_missing_import" = "В самом начале не хватает %@ Без этой строки Java не знает Scanner."; -"web_parse_missing_scanner" = "Сначала не хватает %@ Без этой строки Scanner не существует."; -"web_parse_unknown_type" = "Java не знает такого типа данных. Может быть, %@?"; -"web_quickfix_insert_fmt" = "Предложение: вставить %@"; -"web_quickfix_replace_fmt" = "Предложение: %1$@ вместо %2$@"; -"web_parse_wrong_case" = "Java различает заглавные и строчные буквы. Правильно: %@."; -"web_editor_title" = "Диаграмма"; -"web_editor_show" = "Показать диаграмму"; -"web_editor_hide" = "Скрыть диаграмму"; -"web_keywords_title" = "Ключевые слова кода"; -"web_keywords_hint" = "if, while, for вместо названий элементов"; -"web_palette_compact" = "Элементы всегда компактно"; -"web_palette_compact_hint" = "Значок с маленьким названием под ним, иначе только при узкой колонке"; -"web_about_version_fmt" = "Версия %@"; -"web_about_software" = "Используемое ПО"; -"web_about_software_text" = "Редактор кода использует CodeMirror и Lezer (лицензия MIT). С поддержкой Swing VisuStruct загружает CheerpJ от Leaning Technologies (лицензия Community) и javac из OpenJDK."; -"web_about_licenses" = "Тексты лицензий"; -"web_live_run" = "Запустить"; -"web_live_run_title" = "Запустить программу: вывод и ввод появятся ниже"; -"web_live_run_finished" = "Программа завершена."; -"web_live_run_stopped" = "Остановлено."; -"web_console" = "Консоль"; -"web_console_hide" = "Скрыть консоль"; -"web_console_resize" = "Изменить высоту консоли"; -"web_diagram_tools" = "Инструменты диаграммы"; -"web_files_title" = "Файлы"; -"web_files_show" = "Показать файлы"; -"web_files_hide" = "Скрыть файлы"; -"web_files_resize" = "Изменить ширину файлов"; -"web_files_new_folder" = "Новая папка"; -"web_files_switch" = "Выбрать другую папку"; -"web_files_delete" = "Удалить"; -"web_files_in_browser" = "В хранилище этого браузера"; -"web_files_save_hint_fmt" = "При сохранении программа попадает в %@."; -"web_files_empty" = "Пока ничего не сохранено. При сохранении VisuStruct создаёт здесь папку с сегодняшней датой."; -"web_files_reconnect_hint" = "После перезагрузки браузер ещё раз спросит, может ли VisuStruct использовать твою папку."; -"web_files_reconnect_fmt" = "Открыть папку «%@»"; -"web_files_connect_hint" = "Выбери папку как рабочее пространство. VisuStruct создаёт в ней папку для каждого дня и сохраняет диаграмму и код."; -"web_files_connect" = "Выбрать папку"; -"web_files_browser_hint" = "Этот браузер не может открывать папки на устройстве (напр. на iPad). Твои файлы остаются в хранилище этого браузера."; -"web_files_browser" = "Сохранять в браузере"; -"web_files_delete_confirm_fmt" = "Действительно удалить «%@»?"; -"web_files_folder_prompt" = "Имя новой папки:"; -"web_files_default_name" = "Программа"; -"web_files_saved_fmt" = "Сохранено: %@"; -"web_files_create" = "Создать"; -"web_learn_ludwig_tip" = "Совет Людвига"; -"web_game_title" = "Игра: Истина или ложь?"; -"web_game_intro" = "Людвиг показывает значения и условие. Условие истинно (true) или ложно (false)? Десять раундов, всё сложнее."; -"web_game_start" = "Начать игру"; -"web_game_round_fmt" = "Раунд %@ из %@"; -"web_game_score_fmt" = "Очки: %@"; -"web_game_true" = "истина (true)"; -"web_game_false" = "ложь (false)"; -"web_game_right" = "Верно!"; -"web_game_wrong" = "Не совсем. Вот как это вычисляется:"; -"web_game_next" = "Дальше"; -"web_game_finish" = "Результат"; -"web_game_done_fmt" = "%@ из %@ верно."; -"web_game_perfect" = "Всё верно – Людвиг впечатлён!"; -"web_game_good" = "Отлично! Посмотри на ошибки и попробуй ещё раз."; -"web_game_practice" = "Повторение – мать учения. Перечитай таблицу истинности и сыграй ещё раз."; -"web_game_again" = "Играть снова"; -"web_game_wrong_short" = "К сожалению, неверно."; -"web_game_practice_short" = "Повторение — мать учения. Перечитай объяснения и сыграй ещё раунд."; -"web_game_clicks" = "Клики:"; -"web_game_nobody" = "Никто не слушает — ничего не происходит."; -"web_game_event_fmt" = "Клик по «%@»: слушатель слышит это и выполняет свой код."; -"web_game_try" = "Теперь попробуй кнопки в окне сам."; -"web_game_time_fmt" = "Осталось %@ с"; -"web_game_defused" = "✂️ Обезврежено! Ты нашёл ошибку."; -"web_game_boom_time" = "💥 Бум! Время вышло."; -"web_game_boom_wrong" = "💥 Бум! Это была не та строка."; -"web_game_boom_part" = "💥 Бум! Строка верная, но ошибка в другой её части."; -"web_game_boom_wrong_part" = "💥 Бум! Ошибка в другой строке."; -"web_game_bomb_parts_hint" = "Нажми точно на неправильную часть."; -"web_game_fix" = "Правильная строка:"; -"web_game_order_hint" = "Нажимай на шаги в правильном порядке."; -"web_debug_step" = "Шаг"; -"web_debug_step_title" = "Шаг: выполнить одну инструкцию и снова остановиться"; -"web_debug_continue" = "Продолжить до следующей точки останова"; -"web_debug_paused_fmt" = "Остановлено перед строкой %d."; -"web_debug_gutter_title" = "Нажми на номер строки, чтобы поставить или убрать точку останова"; -"web_swap_to_code" = "Программирование крупно"; -"web_swap_to_editor" = "Структограмма крупно"; -"web_editor_resize" = "Изменить ширину структограммы"; -"web_palette_resize" = "Изменить ширину элементов"; -"web_palette_hide" = "Скрыть элементы"; -"web_palette_show" = "Показать элементы"; -"web_code_scopes" = "Цветные блоки кода"; -"web_code_scopes_hint" = "Класс, метод, цикл, ветвление в отдельных рамках"; -"web_pseudocode" = "Псевдокод"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Переименовать переменную …"; -"web_rename_title" = "Переименовать переменную"; -"web_rename_old" = "Имя"; -"web_rename_new" = "Новое имя"; -"web_rename_hint" = "Меняет каждое место с точно таким написанием (number — не Number), в коде и на структурограмме. Текст в \\"…\\" и комментарии остаются."; -"web_rename_count_fmt" = "Встречается %d раз(а)."; -"web_rename_exists_fmt" = "«%@» уже есть: после этого обе станут одной переменной."; -"web_rename_invalid" = "Имя начинается с буквы и содержит только буквы, цифры и _."; -"web_rename_keyword_fmt" = "«%@» — ключевое слово Java."; -"web_rename_apply" = "Переименовать"; -"web_rename_cancel" = "Отмена"; -`;export{e as default}; diff --git a/website/app/assets/schiffe-versenken-CXt1SF0S.svg b/website/app/assets/schiffe-versenken-CXt1SF0S.svg deleted file mode 100644 index 289e2fd5..00000000 --- a/website/app/assets/schiffe-versenken-CXt1SF0S.svg +++ /dev/null @@ -1,55 +0,0 @@ - - -A -0 -B -1 -C -2 -D -3 -E -4 -1 -0 - - - - - -2 -1 - - - - - -3 -2 - - - - - -4 -3 - - - - - -5 -4 - - - - - - - - - - - -Index - diff --git a/website/app/assets/schiffe-versenken-iEBDHIxD.svg b/website/app/assets/schiffe-versenken-iEBDHIxD.svg deleted file mode 100644 index 8aeefaab..00000000 --- a/website/app/assets/schiffe-versenken-iEBDHIxD.svg +++ /dev/null @@ -1,55 +0,0 @@ - - -A -0 -B -1 -C -2 -D -3 -E -4 -1 -0 - - - - - -2 -1 - - - - - -3 -2 - - - - - -4 -3 - - - - - -5 -4 - - - - - - - - - - - -Índice - diff --git a/website/app/assets/ss-binaere-suche-CPsLGldq.svg b/website/app/assets/ss-binaere-suche-CPsLGldq.svg deleted file mode 100644 index c05c4a47..00000000 --- a/website/app/assets/ss-binaere-suche-CPsLGldq.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Gesucht: die 27. Die Karten sind sortiert. -1. Mitte: 13 < 27, links fällt weg -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2. Mitte: 21 < 27, links fällt weg -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3. Mitte: gefunden nach 3 Vergleichen -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/website/app/assets/ss-binaere-suche-D02W1TH_.svg b/website/app/assets/ss-binaere-suche-D02W1TH_.svg deleted file mode 100644 index e5250f36..00000000 --- a/website/app/assets/ss-binaere-suche-D02W1TH_.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Looking for the 27. The cards are sorted. -1st middle: 13 < 27, drop the left -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2nd middle: 21 < 27, drop the left -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3rd middle: found after 3 comparisons -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/website/app/assets/ss-binaere-suche-DVVS2SNc.svg b/website/app/assets/ss-binaere-suche-DVVS2SNc.svg deleted file mode 100644 index 8caadd8c..00000000 --- a/website/app/assets/ss-binaere-suche-DVVS2SNc.svg +++ /dev/null @@ -1,67 +0,0 @@ - - -Procura-se o 27. As cartas estão ordenadas. -1.º meio: 13 < 27, a esquerda sai -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -0 -1 -2 -3 -4 -5 -6 -7 -8 -9 -10 -11 -12 -13 -14 -2.º meio: 21 < 27, a esquerda sai -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ -3.º meio: encontrado após 3 comparações -2♦ -3♣ -5♠ -7♣ -8♥ -10♦ -12♥ -13♠ -15♣ -18♦ -20♥ -21♠ -24♥ -27♣ -30♦ - diff --git a/website/app/assets/ss-bubblesort-AFDcFDah.svg b/website/app/assets/ss-bubblesort-AFDcFDah.svg deleted file mode 100644 index 9b13f206..00000000 --- a/website/app/assets/ss-bubblesort-AFDcFDah.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -Uma ronda: comparar vizinhos -7♥ -3♠ -10♦ -5♣ -2♥ -Início -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: trocar -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: certo -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: trocar -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: trocar -A carta maior sobe para a direita como uma bolha. - diff --git a/website/app/assets/ss-bubblesort-CmKEoNfO.svg b/website/app/assets/ss-bubblesort-CmKEoNfO.svg deleted file mode 100644 index a584b009..00000000 --- a/website/app/assets/ss-bubblesort-CmKEoNfO.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -One round: compare neighbours -7♥ -3♠ -10♦ -5♣ -2♥ -Start -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: swap -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: fine -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: swap -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: swap -The largest card rises to the right like a bubble. - diff --git a/website/app/assets/ss-bubblesort-Dic2ZfaT.svg b/website/app/assets/ss-bubblesort-Dic2ZfaT.svg deleted file mode 100644 index 6c47eef5..00000000 --- a/website/app/assets/ss-bubblesort-Dic2ZfaT.svg +++ /dev/null @@ -1,35 +0,0 @@ - - -Ein Durchgang: Nachbarn vergleichen -7♥ -3♠ -10♦ -5♣ -2♥ -Start -3♠ -7♥ -10♦ -5♣ -2♥ -7 > 3: tauschen -3♠ -7♥ -10♦ -5♣ -2♥ -7 < 10: passt -3♠ -7♥ -5♣ -10♦ -2♥ -10 > 5: tauschen -3♠ -7♥ -5♣ -2♥ -10♦ -10 > 2: tauschen -Die größte Karte steigt wie eine Blase nach rechts. - diff --git a/website/app/assets/ss-vergleiche-DRqzU2TZ.svg b/website/app/assets/ss-vergleiche-DRqzU2TZ.svg deleted file mode 100644 index 1cf631a0..00000000 --- a/website/app/assets/ss-vergleiche-DRqzU2TZ.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Vergleiche im schlimmsten Fall -Lineare SucheBinäre Suche -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Anzahl der Karten -Vergleiche - - -100 Vergleiche -7 Vergleiche - diff --git a/website/app/assets/ss-vergleiche-DWgkIvLr.svg b/website/app/assets/ss-vergleiche-DWgkIvLr.svg deleted file mode 100644 index 672279b3..00000000 --- a/website/app/assets/ss-vergleiche-DWgkIvLr.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Comparações no pior caso -Pesquisa linearPesquisa binária -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Número de cartas -Comparações - - -100comparações -7 comparações - diff --git a/website/app/assets/ss-vergleiche-UczNldcC.svg b/website/app/assets/ss-vergleiche-UczNldcC.svg deleted file mode 100644 index 57a87c8a..00000000 --- a/website/app/assets/ss-vergleiche-UczNldcC.svg +++ /dev/null @@ -1,22 +0,0 @@ - - -Comparisons in the worst case -Linear searchBinary search -0 -25 -50 -75 -100 -0 -25 -50 -75 -100 - -Number of cards -Comparisons - - -100 comparisons -7 comparisons - diff --git a/website/app/assets/sv-BZ9E37oq.js b/website/app/assets/sv-BZ9E37oq.js deleted file mode 100644 index 75b56fbe..00000000 --- a/website/app/assets/sv-BZ9E37oq.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Texter som bara finns i webbversionen. Allt annat kommer från appens Localizable.strings. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct-struktogram"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Exportera…"; -"web_export_title" = "Spara som bild eller PDF, eller skriv ut"; -"web_export_png" = "Bild (PNG)"; -"web_export_svg" = "Vektorgrafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct-fil (.visustruct)"; -"web_export_failed_fmt" = "Exporten misslyckades: %@"; -"web_print_hint" = "Använd ”Exportera… → PDF” för att spara diagrammet och skriva ut det därifrån."; -"web_delete" = "Radera"; -"web_language_title" = "Gränssnittets språk"; -"web_simulation_start" = "Starta simulering"; -"web_simulation_end" = "Avsluta simulering"; -"web_simulation_start_title" = "Kör struktogrammet steg för steg"; -"web_simulation_end_title" = "Avsluta simuleringen och fortsätt redigera"; -"web_palette_hint" = "Dra in i struktogrammet"; -"web_tabs_label" = "Diagram"; -"web_status_modified" = "ändrad"; -"web_inspector_title" = "Egenskaper"; -"web_inspector_resize" = "Ändra objektinspektörens höjd: dra eller svep linjen uppåt eller nedåt (dubbelklick: standard)"; -"web_inspector_help_drop" = "Dra element från paletten till en ruta: överst = före, nederst = efter, på ”ø” = ersätt. Befintliga element kan flyttas med musen."; -"web_inspector_help_inline" = "Dubbelklicka eller dubbeltryck på ett element för att redigera texten direkt i diagrammet (Enter verkställer, Esc avbryter). Gäller även case-namn."; -"web_inspector_help_files" = "Du kan också släppa filer i fönstret."; -"web_case_name" = "Namn på case"; -"web_case_name_placeholder" = "t.ex. 1, ok, default"; -"web_copied" = "Elementet kopierat."; -"web_discard_message_fmt" = "Ändringarna i ”%@” går förlorade."; -"web_err_unreadable_fmt" = "”%@” är inte en läsbar VisuStruct-fil (%@)."; -"web_saved_fmt" = "”%@” sparad."; -"web_saved_txt_fmt" = "Sparad som ”%@”. Ta bort filändelsen .txt för att använda den."; -"web_drop_impossible" = "Kan inte släppas här"; -"web_sim_delay" = "Paus per steg"; -"web_sim_delay_value_fmt" = "%@ s"; -"web_codegen_language" = "Språk"; -"web_codegen_comments" = "Diagramtext som kommentarer"; -"web_codegen_empty_else" = "Skriv ut tom else-gren i koden"; -"web_copied_check" = "Kopierat ✓"; -"web_copy_manual" = "Markerat, tryck Ctrl+C för att kopiera"; -"web_run_stopped_loop" = "Körningen stoppades: möjlig oändlig loop."; -"web_run_cancelled" = "Körningen avbröts."; -"web_run_error" = "Fel vid körning."; -"web_run_dropped_fmt" = "… %d tidigare rader dolda"; -"web_sim_call_arguments_fmt" = "Anropet passar inte till huvudet i %@. Kontrollera värdena inom parentes."; -"web_sim_missing_return_fmt" = "%@ avslutades utan att returnera ett värde med return."; -"web_sim_file_not_found_fmt" = "Det finns ingen fil som heter %@."; -"web_sim_end_of_file_fmt" = "Filen %@ har inga fler rader."; -"web_sim_null_value_fmt" = "%@ är null."; -"web_sim_unknown_variable_case_fmt" = "Okänd variabel: %1$@. Java skiljer på stora och små bokstäver: menade du %2$@?"; -"web_sim_written_file_fmt" = "%@ (sparad)"; -"web_site_home" = "Startsida"; -"web_site_imprint" = "Kontaktuppgifter"; -"web_site_privacy" = "Integritet"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programmering"; -"web_live_hide" = "Dölj programmering"; -"web_scale_canvas" = "Textstorlek: strukturdiagram"; -"web_scale_code" = "Textstorlek: programmering"; -"web_live_indent" = "Indrag"; -"web_live_indent_title" = "Gör indrag i koden: fyra blanksteg per nivå (markerade rader, annars allt)"; -"web_live_show" = "Visa programmering"; -"web_live_resize" = "Ändra kodområdets bredd (dubbelklick: återställ)"; -"web_live_hint" = "Koden växer med ditt diagram. Grått = platshållare: skriv i inspektören så visas texten här. Klicka på en rad för att markera elementet."; -"web_live_ph_selector" = "variabel"; -"web_live_ph_code" = "Din kod"; -"web_live_ph_end" = "slut"; -"web_live_language" = "Programmeringsspråk"; -"web_live_java_class" = "Som klass"; -"web_live_java_class_hint" = "Klassnamn = diagrammets titel"; -"web_live_java_main" = "Med main-metod"; -"web_settings_display" = "Visning"; -"web_live_swing" = "Riktig Java (med Swing)"; -"web_live_swing_hint" = "Riktig Java med fönster (CheerpJ). Laddar ner ca 40 MB en gång vid första körningen."; -"web_live_swing_windows" = "Java-fönster"; -"web_live_swing_hide" = "Stäng Java-fönster"; -"web_live_swing_loading" = "Java laddas (första gången ca 40 MB) …"; -"web_live_swing_compiling" = "Kompilerar …"; -"web_live_swing_running" = "Programmet körs."; -"web_live_swing_compile_failed" = "Kompileringsfel, se ovan."; -"web_live_swing_failed" = "Java kunde inte laddas. Kontrollera internetanslutningen."; -"web_live_hint_edit" = "Skriv Java-kod här så bygger diagrammet sig självt. Eller arbeta i diagrammet så växer koden med. Grått = platshållare, skriv bara över."; -"web_live_edit_label" = "Java-kod"; -"web_parse_line_fmt" = "Rad %d: %@"; -"web_parse_missing" = "Här saknas ännu: %@"; -"web_parse_extra" = "För mycket: %@"; -"web_parse_unclosed_string" = "Det avslutande %@ saknas."; -"web_parse_else_without_if" = "Denna ”else” har ingen ”if”."; -"web_parse_case_outside_switch" = "”case” och ”default” hör hemma i en ”switch”."; -"web_parse_missing_do_while" = "Efter ”do { … }” saknas ”while (villkor);”."; -"web_parse_more_methods" = "För tillfället räknas bara en metod här. Skapa andra underprogram i egna flikar."; -"web_parse_missing_import" = "Högst upp saknas %@ Utan den raden känner Java inte till Scanner."; -"web_parse_missing_scanner" = "Innan saknas %@ Utan den raden finns ingen Scanner."; -"web_parse_unknown_type" = "Java känner inte till den här datatypen. Menade du %@?"; -"web_quickfix_insert_fmt" = "Förslag: infoga %@"; -"web_quickfix_replace_fmt" = "Förslag: %1$@ i stället för %2$@"; -"web_parse_wrong_case" = "Java skiljer på stora och små bokstäver. Rätt är %@."; -"web_editor_title" = "Strukturdiagram"; -"web_editor_show" = "Visa strukturdiagram"; -"web_editor_hide" = "Dölj strukturdiagram"; -"web_keywords_title" = "Kodnyckelord"; -"web_keywords_hint" = "if, while, for i stället för elementnamn"; -"web_palette_compact" = "Element alltid kompakta"; -"web_palette_compact_hint" = "Symbol med litet namn under, annars bara när kolumnen är smal"; -"web_about_version_fmt" = "Version %@"; -"web_about_software" = "Programvara som används"; -"web_about_software_text" = "Kodredigeraren använder CodeMirror och Lezer (MIT-licens). Med Swing-stöd laddar VisuStruct CheerpJ från Leaning Technologies (Community-licens) och javac från OpenJDK."; -"web_about_licenses" = "Licenstexter"; -"web_live_run" = "Kör"; -"web_live_run_title" = "Kör programmet: utdata och indata visas nedanför"; -"web_live_run_finished" = "Programmet är klart."; -"web_live_run_stopped" = "Stoppat."; -"web_console" = "Konsol"; -"web_console_hide" = "Dölj konsolen"; -"web_console_resize" = "Ändra konsolens höjd"; -"web_diagram_tools" = "Diagramverktyg"; -"web_files_title" = "Filer"; -"web_files_show" = "Visa filer"; -"web_files_hide" = "Dölj filer"; -"web_files_resize" = "Ändra bredd på filer"; -"web_files_new_folder" = "Ny mapp"; -"web_files_switch" = "Välj en annan mapp"; -"web_files_delete" = "Ta bort"; -"web_files_in_browser" = "I den här webbläsarens lagring"; -"web_files_save_hint_fmt" = "Sparandet lägger programmet i %@."; -"web_files_empty" = "Inget sparat ännu. När du sparar skapar VisuStruct en mapp med dagens datum här."; -"web_files_reconnect_hint" = "Efter omladdning frågar webbläsaren igen om VisuStruct får använda din mapp."; -"web_files_reconnect_fmt" = "Öppna mappen ”%@”"; -"web_files_connect_hint" = "Välj en mapp som arbetsyta. VisuStruct skapar en mapp för varje dag där och sparar diagram och kod."; -"web_files_connect" = "Välj mapp"; -"web_files_browser_hint" = "Den här webbläsaren kan inte öppna mappar på enheten (t.ex. på iPad). Dina filer stannar i webbläsarens lagring."; -"web_files_browser" = "Spara i webbläsaren"; -"web_files_delete_confirm_fmt" = "Vill du verkligen ta bort ”%@”?"; -"web_files_folder_prompt" = "Namn på den nya mappen:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Sparat: %@"; -"web_files_create" = "Skapa"; -"web_learn_ludwig_tip" = "Ludwigs tips"; -"web_game_title" = "Spel: Sant eller falskt?"; -"web_game_intro" = "Ludwig visar värden och ett villkor. Är villkoret sant (true) eller falskt (false)? Tio rundor, allt svårare."; -"web_game_start" = "Starta spelet"; -"web_game_round_fmt" = "Runda %@ av %@"; -"web_game_score_fmt" = "Poäng: %@"; -"web_game_true" = "sant (true)"; -"web_game_false" = "falskt (false)"; -"web_game_right" = "Rätt!"; -"web_game_wrong" = "Inte riktigt. Så räknar man:"; -"web_game_next" = "Nästa"; -"web_game_finish" = "Resultat"; -"web_game_done_fmt" = "%@ av %@ rätt."; -"web_game_perfect" = "Allt rätt – Ludwig är imponerad!"; -"web_game_good" = "Snyggt! Titta på dina misstag och försök igen."; -"web_game_practice" = "Övning ger färdighet. Läs sanningstabellen igen och spela en runda till."; -"web_game_again" = "Spela igen"; -"web_game_wrong_short" = "Tyvärr fel."; -"web_game_practice_short" = "Övning ger färdighet. Läs förklaringarna igen och spela en runda till."; -"web_game_clicks" = "Klick:"; -"web_game_nobody" = "Ingen lyssnar – ingenting händer."; -"web_game_event_fmt" = "Klick på ”%@”: lyssnaren hör det och kör sin kod."; -"web_game_try" = "Prova nu själv knapparna i fönstret."; -"web_game_time_fmt" = "%@ s kvar"; -"web_game_defused" = "✂️ Desarmerad! Du hittade felet."; -"web_game_boom_time" = "💥 Pang! Tiden är ute."; -"web_game_boom_wrong" = "💥 Pang! Det var fel rad."; -"web_game_boom_part" = "💥 Pang! Rätt rad, men felet sitter i en annan del."; -"web_game_boom_wrong_part" = "💥 Pang! Felet sitter på en annan rad."; -"web_game_bomb_parts_hint" = "Tryck exakt på den felaktiga delen."; -"web_game_fix" = "Rätt rad:"; -"web_game_order_hint" = "Tryck på stegen i rätt ordning."; -"web_debug_step" = "Steg"; -"web_debug_step_title" = "Steg: kör en sats och pausa igen"; -"web_debug_continue" = "Fortsätt till nästa brytpunkt"; -"web_debug_paused_fmt" = "Pausad före rad %d."; -"web_debug_gutter_title" = "Klicka på ett radnummer för att sätta eller ta bort en brytpunkt"; -"web_swap_to_code" = "Programmering stor"; -"web_swap_to_editor" = "Struktogram stort"; -"web_editor_resize" = "Ändra bredd på struktogrammet"; -"web_palette_resize" = "Ändra bredd på elementen"; -"web_palette_hide" = "Dölj elementen"; -"web_palette_show" = "Visa elementen"; -"web_code_scopes" = "Färgade kodblock"; -"web_code_scopes_hint" = "Klass, metod, loop, förgrening i egna rutor"; -"web_pseudocode" = "Pseudokod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Byt namn på variabel …"; -"web_rename_title" = "Byt namn på variabel"; -"web_rename_old" = "Namn"; -"web_rename_new" = "Nytt namn"; -"web_rename_hint" = "Ändrar varje ställe med exakt denna stavning (tal är inte Tal), i koden och i strukturdiagrammet. Text inom \\"…\\" och kommentarer blir kvar."; -"web_rename_count_fmt" = "Förekommer %d gånger."; -"web_rename_exists_fmt" = "”%@” finns redan: efteråt skulle båda vara en variabel."; -"web_rename_invalid" = "Ett namn börjar med en bokstav och innehåller bara bokstäver, siffror och _."; -"web_rename_keyword_fmt" = "”%@” är ett nyckelord i Java."; -"web_rename_apply" = "Byt namn"; -"web_rename_cancel" = "Avbryt"; -`;export{e as default}; diff --git a/website/app/assets/tr-C3W_u6Ol.js b/website/app/assets/tr-C3W_u6Ol.js deleted file mode 100644 index 487f537f..00000000 --- a/website/app/assets/tr-C3W_u6Ol.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Yalnızca web sürümünde bulunan metinler. Geri kalan her şey uygulamanın Localizable.strings dosyalarından gelir. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct yapı şeması"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Dışa aktar…"; -"web_export_title" = "Görsel veya PDF olarak kaydet ya da yazdır"; -"web_export_png" = "Görsel (PNG)"; -"web_export_svg" = "Vektör grafik (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "VisuStruct dosyası (.visustruct)"; -"web_export_failed_fmt" = "Dışa aktarma başarısız: %@"; -"web_print_hint" = "Diyagramı kaydetmek için “Dışa aktar… → PDF” seçeneğini kullanın ve oradan yazdırın."; -"web_delete" = "Sil"; -"web_language_title" = "Arayüz dili"; -"web_simulation_start" = "Simülasyonu başlat"; -"web_simulation_end" = "Simülasyonu bitir"; -"web_simulation_start_title" = "Yapı şemasını adım adım çalıştır"; -"web_simulation_end_title" = "Simülasyonu bitir ve düzenlemeye devam et"; -"web_palette_hint" = "Yapı şemasına sürükleyin"; -"web_tabs_label" = "Diyagramlar"; -"web_status_modified" = "değiştirildi"; -"web_inspector_title" = "Özellikler"; -"web_inspector_resize" = "Nesne denetçisinin yüksekliğini değiştir: çizgiyi yukarı veya aşağı sürükle ya da kaydır (çift tıklama: varsayılan)"; -"web_inspector_help_drop" = "Paletteki öğeleri bir kutucuğa sürükleyin: üst = önce, alt = sonra, “ø” üzerine = değiştir. Mevcut öğeler fareyle taşınabilir."; -"web_inspector_help_inline" = "Metni doğrudan diyagramda düzenlemek için bir öğeye çift tıklayın veya çift dokunun (Enter uygular, Esc iptal eder). Durum adları da böyle değişir."; -"web_inspector_help_files" = "Dosyaları pencereye de bırakabilirsiniz."; -"web_case_name" = "Durum adı"; -"web_case_name_placeholder" = "ör. 1, ok, default"; -"web_copied" = "Öğe kopyalandı."; -"web_discard_message_fmt" = "“%@” üzerindeki değişiklikler kaybolacak."; -"web_err_unreadable_fmt" = "“%@” okunabilir bir VisuStruct dosyası değil (%@)."; -"web_saved_fmt" = "“%@” kaydedildi."; -"web_saved_txt_fmt" = "“%@” olarak kaydedildi. Kullanmak için .txt uzantısını kaldırın."; -"web_drop_impossible" = "Buraya bırakılamaz"; -"web_sim_delay" = "Adım başına duraklama"; -"web_sim_delay_value_fmt" = "%@ sn"; -"web_codegen_language" = "Dil"; -"web_codegen_comments" = "Diyagram metni yorum olarak"; -"web_codegen_empty_else" = "Kodda boş else dalı oluştur"; -"web_copied_check" = "Kopyalandı ✓"; -"web_copy_manual" = "Seçildi, kopyalamak için Ctrl+C tuşlarına basın"; -"web_run_stopped_loop" = "Çalıştırma durduruldu: olası sonsuz döngü."; -"web_run_cancelled" = "Çalıştırma iptal edildi."; -"web_run_error" = "Çalıştırma sırasında hata."; -"web_run_dropped_fmt" = "… önceki %d satır gizlendi"; -"web_sim_call_arguments_fmt" = "Çağrı %@ başlığıyla eşleşmiyor. Parantez içindeki değerleri kontrol et."; -"web_sim_missing_return_fmt" = "%@ return ile bir değer döndürmeden bitti."; -"web_sim_file_not_found_fmt" = "%@ adında bir dosya yok."; -"web_sim_end_of_file_fmt" = "%@ dosyasında başka satır yok."; -"web_sim_null_value_fmt" = "%@ null."; -"web_sim_unknown_variable_case_fmt" = "Bilinmeyen değişken: %1$@. Java büyük ve küçük harfleri ayırt eder: %2$@ mı demek istedin?"; -"web_sim_written_file_fmt" = "%@ (kaydedildi)"; -"web_site_home" = "Ana sayfa"; -"web_site_imprint" = "Künye"; -"web_site_privacy" = "Gizlilik"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Programlama"; -"web_live_hide" = "Programlamayı gizle"; -"web_scale_canvas" = "Yazı boyutu: yapı diyagramı"; -"web_scale_code" = "Yazı boyutu: programlama"; -"web_live_indent" = "Girinti"; -"web_live_indent_title" = "Kodu girintile: her seviye için dört boşluk (seçili satırlar, yoksa tümü)"; -"web_live_show" = "Programlamayı göster"; -"web_live_resize" = "Kod alanının genişliğini değiştir (çift tıklama: sıfırla)"; -"web_live_hint" = "Kod diyagramınla birlikte büyür. Gri = yer tutucu: denetçiye yaz, metnin burada görünür. Öğesini seçmek için bir satıra tıkla."; -"web_live_ph_selector" = "değişken"; -"web_live_ph_code" = "Kodun"; -"web_live_ph_end" = "bitiş"; -"web_live_language" = "Programlama dili"; -"web_live_java_class" = "Sınıf olarak"; -"web_live_java_class_hint" = "Sınıf adı = diyagram başlığı"; -"web_live_java_main" = "main metoduyla"; -"web_settings_display" = "Görünüm"; -"web_live_swing" = "Gerçek Java (Swing ile)"; -"web_live_swing_hint" = "Pencereli gerçek Java (CheerpJ). İlk çalıştırmada bir kez yaklaşık 40 MB indirir."; -"web_live_swing_windows" = "Java pencereleri"; -"web_live_swing_hide" = "Java pencerelerini kapat"; -"web_live_swing_loading" = "Java yükleniyor (ilk seferde yaklaşık 40 MB) …"; -"web_live_swing_compiling" = "Derleniyor …"; -"web_live_swing_running" = "Program çalışıyor."; -"web_live_swing_compile_failed" = "Derleme hatası, yukarıya bak."; -"web_live_swing_failed" = "Java yüklenemedi. İnternet bağlantını kontrol et."; -"web_live_hint_edit" = "Buraya Java kodu yaz, diyagram kendiliğinden oluşsun. Ya da diyagramda çalış, kod onunla büyüsün. Gri = yer tutucu, üzerine yazman yeterli."; -"web_live_edit_label" = "Java kodu"; -"web_parse_line_fmt" = "Satır %d: %@"; -"web_parse_missing" = "Burada hâlâ eksik: %@"; -"web_parse_extra" = "Fazla: %@"; -"web_parse_unclosed_string" = "Kapanış %@ eksik."; -"web_parse_else_without_if" = "Bu “else” için bir “if” yok."; -"web_parse_case_outside_switch" = "“case” ve “default” bir “switch” içinde olmalı."; -"web_parse_missing_do_while" = "“do { … }” sonrasında “while (koşul);” eksik."; -"web_parse_more_methods" = "Şimdilik burada yalnızca bir metot sayılır. Diğer alt programları ayrı sekmelerde oluştur."; -"web_parse_missing_import" = "En üstte %@ eksik. Bu satır olmadan Java Scanner’ı tanımaz."; -"web_parse_missing_scanner" = "Önce %@ eksik. Bu satır olmadan Scanner yoktur."; -"web_parse_unknown_type" = "Java bu veri türünü tanımıyor. %@ mı demek istedin?"; -"web_quickfix_insert_fmt" = "Öneri: %@ ekle"; -"web_quickfix_replace_fmt" = "Öneri: %2$@ yerine %1$@"; -"web_parse_wrong_case" = "Java büyük ve küçük harfleri ayırt eder. Doğrusu %@."; -"web_editor_title" = "Diyagram"; -"web_editor_show" = "Diyagramı göster"; -"web_editor_hide" = "Diyagramı gizle"; -"web_keywords_title" = "Kod anahtar sözcükleri"; -"web_keywords_hint" = "Öğe adları yerine if, while, for"; -"web_palette_compact" = "Öğeler her zaman kompakt"; -"web_palette_compact_hint" = "Simgenin altında küçük ad; aksi halde yalnızca sütun dar olduğunda"; -"web_about_version_fmt" = "Sürüm %@"; -"web_about_software" = "Kullanılan yazılımlar"; -"web_about_software_text" = "Kod düzenleyici CodeMirror ve Lezer kullanır (MIT lisansı). Swing desteğiyle VisuStruct, Leaning Technologies’in CheerpJ’sini (Community lisansı) ve OpenJDK’dan javac’ı yükler."; -"web_about_licenses" = "Lisans metinleri"; -"web_live_run" = "Çalıştır"; -"web_live_run_title" = "Programı çalıştır: çıktı ve girdi aşağıda görünür"; -"web_live_run_finished" = "Program bitti."; -"web_live_run_stopped" = "Durduruldu."; -"web_console" = "Konsol"; -"web_console_hide" = "Konsolu gizle"; -"web_console_resize" = "Konsolu yeniden boyutlandır"; -"web_diagram_tools" = "Diyagram araçları"; -"web_files_title" = "Dosyalar"; -"web_files_show" = "Dosyaları göster"; -"web_files_hide" = "Dosyaları gizle"; -"web_files_resize" = "Dosyaların genişliğini değiştir"; -"web_files_new_folder" = "Yeni klasör"; -"web_files_switch" = "Başka bir klasör seç"; -"web_files_delete" = "Sil"; -"web_files_in_browser" = "Bu tarayıcının belleğinde"; -"web_files_save_hint_fmt" = "Kaydetme programı %@ içine koyar."; -"web_files_empty" = "Henüz bir şey kaydedilmedi. Kaydettiğinde VisuStruct burada bugünün tarihiyle bir klasör oluşturur."; -"web_files_reconnect_hint" = "Yeniden yüklemeden sonra tarayıcı, VisuStruct’ın klasörünü kullanıp kullanamayacağını bir kez daha sorar."; -"web_files_reconnect_fmt" = "“%@” klasörünü aç"; -"web_files_connect_hint" = "Çalışma alanı olarak bir klasör seç. VisuStruct içinde her gün için bir klasör oluşturur, diyagramı ve kodu kaydeder."; -"web_files_connect" = "Klasör seç"; -"web_files_browser_hint" = "Bu tarayıcı cihazdaki klasörleri açamaz (ör. iPad’de). Dosyaların bu tarayıcının belleğinde kalır."; -"web_files_browser" = "Tarayıcıya kaydet"; -"web_files_delete_confirm_fmt" = "“%@” gerçekten silinsin mi?"; -"web_files_folder_prompt" = "Yeni klasörün adı:"; -"web_files_default_name" = "Program"; -"web_files_saved_fmt" = "Kaydedildi: %@"; -"web_files_create" = "Oluştur"; -"web_learn_ludwig_tip" = "Ludwig’in ipucu"; -"web_game_title" = "Oyun: Doğru mu yanlış mı?"; -"web_game_intro" = "Ludwig sana değerler ve bir koşul gösterir. Koşul doğru (true) mu yanlış (false) mı? On tur, giderek zorlaşır."; -"web_game_start" = "Oyunu başlat"; -"web_game_round_fmt" = "Tur %@ / %@"; -"web_game_score_fmt" = "Puan: %@"; -"web_game_true" = "doğru (true)"; -"web_game_false" = "yanlış (false)"; -"web_game_right" = "Doğru!"; -"web_game_wrong" = "Maalesef yanlış. Şöyle hesaplanır:"; -"web_game_next" = "İleri"; -"web_game_finish" = "Sonuç"; -"web_game_done_fmt" = "%@ / %@ doğru."; -"web_game_perfect" = "Hepsi doğru – Ludwig çok etkilendi!"; -"web_game_good" = "Harika! Hatalarına bir daha bak ve yeniden dene."; -"web_game_practice" = "Pratik mükemmelleştirir. Yukarıdaki doğruluk tablosunu tekrar oku ve bir tur daha oyna."; -"web_game_again" = "Tekrar oyna"; -"web_game_wrong_short" = "Maalesef yanlış."; -"web_game_practice_short" = "Pratik mükemmelleştirir. Açıklamaları tekrar oku ve bir tur daha oyna."; -"web_game_clicks" = "Tıklamalar:"; -"web_game_nobody" = "Kimse dinlemiyor – hiçbir şey olmuyor."; -"web_game_event_fmt" = "“%@” tıklandı: dinleyici bunu duyar ve kodunu çalıştırır."; -"web_game_try" = "Şimdi penceredeki düğmeleri kendin dene."; -"web_game_time_fmt" = "%@ sn kaldı"; -"web_game_defused" = "✂️ Etkisiz hâle getirildi! Hatayı buldun."; -"web_game_boom_time" = "💥 Bum! Süre doldu."; -"web_game_boom_wrong" = "💥 Bum! Yanlış satırdı."; -"web_game_boom_part" = "💥 Bum! Satır doğru, ama hata başka bir parçada."; -"web_game_boom_wrong_part" = "💥 Bum! Hata başka bir satırda."; -"web_game_bomb_parts_hint" = "Tam olarak yanlış parçaya dokun."; -"web_game_fix" = "Doğru satır:"; -"web_game_order_hint" = "Adımlara doğru sırayla dokun."; -"web_debug_step" = "Adım"; -"web_debug_step_title" = "Adım: bir komutu çalıştır ve yeniden dur"; -"web_debug_continue" = "Bir sonraki kesme noktasına kadar devam et"; -"web_debug_paused_fmt" = "%d. satırdan önce duraklatıldı."; -"web_debug_gutter_title" = "Kesme noktası koymak veya kaldırmak için satır numarasına tıkla"; -"web_swap_to_code" = "Programlama büyük"; -"web_swap_to_editor" = "Yapı diyagramı büyük"; -"web_editor_resize" = "Diyagram genişliğini değiştir"; -"web_palette_resize" = "Öğelerin genişliğini değiştir"; -"web_palette_hide" = "Öğeleri gizle"; -"web_palette_show" = "Öğeleri göster"; -"web_code_scopes" = "Renkli kod blokları"; -"web_code_scopes_hint" = "Sınıf, metot, döngü, dallanma kendi kutularında"; -"web_pseudocode" = "Sözde kod"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Değişkeni yeniden adlandır …"; -"web_rename_title" = "Değişkeni yeniden adlandır"; -"web_rename_old" = "Ad"; -"web_rename_new" = "Yeni ad"; -"web_rename_hint" = "Tam olarak bu yazılışa sahip her yeri değiştirir (sayi, Sayi değildir), kodda ve yapı diyagramında. \\"…\\" içindeki metin ve yorumlar kalır."; -"web_rename_count_fmt" = "%d kez geçiyor."; -"web_rename_exists_fmt" = "“%@” zaten var: sonra ikisi tek bir değişken olur."; -"web_rename_invalid" = "Bir ad harfle başlar ve yalnızca harf, rakam ve _ içerir."; -"web_rename_keyword_fmt" = "“%@” bir Java anahtar sözcüğüdür."; -"web_rename_apply" = "Yeniden adlandır"; -"web_rename_cancel" = "İptal"; -`;export{e as default}; diff --git a/website/app/assets/uk-D0HDyCtp.js b/website/app/assets/uk-D0HDyCtp.js deleted file mode 100644 index 99eb78b5..00000000 --- a/website/app/assets/uk-D0HDyCtp.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* Тексти, які є лише у веб-версії. Усі інші беруться з Localizable.strings застосунку. */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "Структурна схема VisuStruct"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "Експорт…"; -"web_export_title" = "Зберегти як зображення або PDF чи надрукувати"; -"web_export_png" = "Зображення (PNG)"; -"web_export_svg" = "Векторна графіка (SVG)"; -"web_export_pdf" = "PDF (A4)"; -"web_export_visustruct" = "Файл VisuStruct (.visustruct)"; -"web_export_failed_fmt" = "Помилка експорту: %@"; -"web_print_hint" = "Через «Експорт… → PDF» можна зберегти схему, а потім надрукувати її."; -"web_delete" = "Видалити"; -"web_language_title" = "Мова інтерфейсу"; -"web_simulation_start" = "Запустити симуляцію"; -"web_simulation_end" = "Завершити симуляцію"; -"web_simulation_start_title" = "Виконати схему крок за кроком"; -"web_simulation_end_title" = "Завершити симуляцію та продовжити редагування"; -"web_palette_hint" = "Перетягніть у схему"; -"web_tabs_label" = "Схеми"; -"web_status_modified" = "змінено"; -"web_inspector_title" = "Властивості"; -"web_inspector_resize" = "Змінити висоту інспектора об’єктів: перетягніть лінію вгору або вниз (подвійне клацання: типово)"; -"web_inspector_help_drop" = "Перетягуйте елементи з палітри на плитку: зверху = перед, знизу = після, на «ø» = замінити. Наявні елементи можна переміщувати мишею."; -"web_inspector_help_inline" = "Двічі клацніть або торкніться елемента, щоб змінити текст прямо в діаграмі (Enter — застосувати, Esc — скасувати). Так само змінюються назви варіантів."; -"web_inspector_help_files" = "Файли також можна перетягнути у вікно."; -"web_case_name" = "Назва випадку"; -"web_case_name_placeholder" = "напр. 1, ok, default"; -"web_copied" = "Елемент скопійовано."; -"web_discard_message_fmt" = "Зміни в «%@» буде втрачено."; -"web_err_unreadable_fmt" = "«%@» не є придатним для читання файлом VisuStruct (%@)."; -"web_saved_fmt" = "«%@» збережено."; -"web_saved_txt_fmt" = "Збережено як «%@». Видаліть розширення .txt, щоб використати файл."; -"web_drop_impossible" = "Сюди не можна помістити"; -"web_sim_delay" = "Пауза на крок"; -"web_sim_delay_value_fmt" = "%@ с"; -"web_codegen_language" = "Мова"; -"web_codegen_comments" = "Текст схеми як коментарі"; -"web_codegen_empty_else" = "Виводити порожню гілку else в коді"; -"web_copied_check" = "Скопійовано ✓"; -"web_copy_manual" = "Виділено, натисніть Ctrl+C, щоб скопіювати"; -"web_run_stopped_loop" = "Виконання зупинено: можливий нескінченний цикл."; -"web_run_cancelled" = "Виконання скасовано."; -"web_run_error" = "Помилка під час виконання."; -"web_run_dropped_fmt" = "… приховано попередніх рядків: %d"; -"web_sim_call_arguments_fmt" = "Виклик не відповідає заголовку %@. Перевір значення в дужках."; -"web_sim_missing_return_fmt" = "%@ завершилася, не повернувши значення через return."; -"web_sim_file_not_found_fmt" = "Файлу з назвою %@ немає."; -"web_sim_end_of_file_fmt" = "У файлі %@ більше немає рядків."; -"web_sim_null_value_fmt" = "%@ дорівнює null."; -"web_sim_unknown_variable_case_fmt" = "Невідома змінна: %1$@. Java розрізняє великі й малі літери: можливо, %2$@?"; -"web_sim_written_file_fmt" = "%@ (збережено)"; -"web_site_home" = "Головна сторінка"; -"web_site_imprint" = "Вихідні дані"; -"web_site_privacy" = "Конфіденційність"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "Програмування"; -"web_live_hide" = "Сховати програмування"; -"web_scale_canvas" = "Розмір тексту: структурна схема"; -"web_scale_code" = "Розмір тексту: програмування"; -"web_live_indent" = "Відступи"; -"web_live_indent_title" = "Розставити відступи: чотири пробіли на рівень (виділені рядки, інакше все)"; -"web_live_show" = "Показати програмування"; -"web_live_resize" = "Змінити ширину області коду (подвійний клік: скинути)"; -"web_live_hint" = "Код росте разом із діаграмою. Сірий = заповнювач: пиши в інспекторі, і текст з’явиться тут. Клацни рядок, щоб вибрати елемент."; -"web_live_ph_selector" = "змінна"; -"web_live_ph_code" = "Твій код"; -"web_live_ph_end" = "кінець"; -"web_live_language" = "Мова програмування"; -"web_live_java_class" = "Як клас"; -"web_live_java_class_hint" = "Назва класу = заголовок діаграми"; -"web_live_java_main" = "З методом main"; -"web_settings_display" = "Вигляд"; -"web_live_swing" = "Справжня Java (зі Swing)"; -"web_live_swing_hint" = "Справжня Java з вікнами (CheerpJ). Під час першого запуску одноразово завантажує близько 40 МБ."; -"web_live_swing_windows" = "Вікна Java"; -"web_live_swing_hide" = "Закрити вікна Java"; -"web_live_swing_loading" = "Завантаження Java (уперше близько 40 МБ) …"; -"web_live_swing_compiling" = "Компіляція …"; -"web_live_swing_running" = "Програма виконується."; -"web_live_swing_compile_failed" = "Помилка компіляції, див. вище."; -"web_live_swing_failed" = "Не вдалося завантажити Java. Перевір підключення до інтернету."; -"web_live_hint_edit" = "Набери тут код Java, і діаграма будується сама. Або працюй у діаграмі, і код росте разом із нею. Сірий = заповнювач, просто пиши поверх."; -"web_live_edit_label" = "Код Java"; -"web_parse_line_fmt" = "Рядок %d: %@"; -"web_parse_missing" = "Тут ще бракує: %@"; -"web_parse_extra" = "Зайве: %@"; -"web_parse_unclosed_string" = "Бракує закривного %@."; -"web_parse_else_without_if" = "Цей «else» не має «if»."; -"web_parse_case_outside_switch" = "«case» і «default» мають бути всередині «switch»."; -"web_parse_missing_do_while" = "Після «do { … }» бракує «while (умова);»."; -"web_parse_more_methods" = "Поки що тут враховується лише один метод. Інші підпрограми створи в окремих вкладках."; -"web_parse_missing_import" = "На самому початку бракує %@ Без цього рядка Java не знає Scanner."; -"web_parse_missing_scanner" = "Спочатку бракує %@ Без цього рядка Scanner не існує."; -"web_parse_unknown_type" = "Java не знає такого типу даних. Можливо, %@?"; -"web_quickfix_insert_fmt" = "Пропозиція: вставити %@"; -"web_quickfix_replace_fmt" = "Пропозиція: %1$@ замість %2$@"; -"web_parse_wrong_case" = "Java розрізняє великі й малі літери. Правильно: %@."; -"web_editor_title" = "Діаграма"; -"web_editor_show" = "Показати діаграму"; -"web_editor_hide" = "Сховати діаграму"; -"web_keywords_title" = "Ключові слова коду"; -"web_keywords_hint" = "if, while, for замість назв елементів"; -"web_palette_compact" = "Елементи завжди компактно"; -"web_palette_compact_hint" = "Значок із маленькою назвою під ним, інакше лише за вузької колонки"; -"web_about_version_fmt" = "Версія %@"; -"web_about_software" = "Використане програмне забезпечення"; -"web_about_software_text" = "Редактор коду використовує CodeMirror і Lezer (ліцензія MIT). З підтримкою Swing VisuStruct завантажує CheerpJ від Leaning Technologies (ліцензія Community) і javac з OpenJDK."; -"web_about_licenses" = "Тексти ліцензій"; -"web_live_run" = "Запустити"; -"web_live_run_title" = "Запустити програму: виведення і введення з’являться нижче"; -"web_live_run_finished" = "Програму завершено."; -"web_live_run_stopped" = "Зупинено."; -"web_console" = "Консоль"; -"web_console_hide" = "Сховати консоль"; -"web_console_resize" = "Змінити висоту консолі"; -"web_diagram_tools" = "Інструменти діаграми"; -"web_files_title" = "Файли"; -"web_files_show" = "Показати файли"; -"web_files_hide" = "Сховати файли"; -"web_files_resize" = "Змінити ширину файлів"; -"web_files_new_folder" = "Нова тека"; -"web_files_switch" = "Вибрати іншу теку"; -"web_files_delete" = "Видалити"; -"web_files_in_browser" = "У сховищі цього браузера"; -"web_files_save_hint_fmt" = "Під час збереження програма потрапляє в %@."; -"web_files_empty" = "Ще нічого не збережено. Під час збереження VisuStruct створює тут теку з сьогоднішньою датою."; -"web_files_reconnect_hint" = "Після перезавантаження браузер ще раз запитає, чи може VisuStruct використовувати твою теку."; -"web_files_reconnect_fmt" = "Відкрити теку «%@»"; -"web_files_connect_hint" = "Вибери теку як робочий простір. VisuStruct створює в ній теку для кожного дня і зберігає діаграму та код."; -"web_files_connect" = "Вибрати теку"; -"web_files_browser_hint" = "Цей браузер не може відкривати теки на пристрої (напр. на iPad). Твої файли залишаються у сховищі цього браузера."; -"web_files_browser" = "Зберегти в браузері"; -"web_files_delete_confirm_fmt" = "Справді видалити «%@»?"; -"web_files_folder_prompt" = "Назва нової теки:"; -"web_files_default_name" = "Програма"; -"web_files_saved_fmt" = "Збережено: %@"; -"web_files_create" = "Створити"; -"web_learn_ludwig_tip" = "Порада Людвіга"; -"web_game_title" = "Гра: Істина чи хиба?"; -"web_game_intro" = "Людвіг показує значення й умову. Умова істинна (true) чи хибна (false)? Десять раундів, щоразу складніше."; -"web_game_start" = "Почати гру"; -"web_game_round_fmt" = "Раунд %@ з %@"; -"web_game_score_fmt" = "Бали: %@"; -"web_game_true" = "істина (true)"; -"web_game_false" = "хиба (false)"; -"web_game_right" = "Правильно!"; -"web_game_wrong" = "Не зовсім. Ось як це обчислюється:"; -"web_game_next" = "Далі"; -"web_game_finish" = "Результат"; -"web_game_done_fmt" = "%@ з %@ правильно."; -"web_game_perfect" = "Усе правильно – Людвіг вражений!"; -"web_game_good" = "Чудово! Переглянь помилки й спробуй ще раз."; -"web_game_practice" = "Практика робить майстра. Перечитай таблицю істинності й зіграй ще раз."; -"web_game_again" = "Грати ще"; -"web_game_wrong_short" = "На жаль, неправильно."; -"web_game_practice_short" = "Повторення — мати навчання. Перечитай пояснення і зіграй ще раунд."; -"web_game_clicks" = "Кліки:"; -"web_game_nobody" = "Ніхто не слухає — нічого не відбувається."; -"web_game_event_fmt" = "Клік по «%@»: слухач чує це і виконує свій код."; -"web_game_try" = "Тепер спробуй кнопки у вікні сам."; -"web_game_time_fmt" = "Залишилось %@ с"; -"web_game_defused" = "✂️ Знешкоджено! Ти знайшов помилку."; -"web_game_boom_time" = "💥 Бум! Час вийшов."; -"web_game_boom_wrong" = "💥 Бум! Це був не той рядок."; -"web_game_boom_part" = "💥 Бум! Рядок правильний, але помилка в іншій його частині."; -"web_game_boom_wrong_part" = "💥 Бум! Помилка в іншому рядку."; -"web_game_bomb_parts_hint" = "Натисни точно на неправильну частину."; -"web_game_fix" = "Правильний рядок:"; -"web_game_order_hint" = "Натискай на кроки в правильному порядку."; -"web_debug_step" = "Крок"; -"web_debug_step_title" = "Крок: виконати одну інструкцію й знову зупинитися"; -"web_debug_continue" = "Далі до наступної точки зупинки"; -"web_debug_paused_fmt" = "Зупинено перед рядком %d."; -"web_debug_gutter_title" = "Клацни номер рядка, щоб поставити або прибрати точку зупинки"; -"web_swap_to_code" = "Програмування великим"; -"web_swap_to_editor" = "Структурограма великою"; -"web_editor_resize" = "Змінити ширину структурограми"; -"web_palette_resize" = "Змінити ширину елементів"; -"web_palette_hide" = "Сховати елементи"; -"web_palette_show" = "Показати елементи"; -"web_code_scopes" = "Кольорові блоки коду"; -"web_code_scopes_hint" = "Клас, метод, цикл, розгалуження в окремих рамках"; -"web_pseudocode" = "Псевдокод"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "Перейменувати змінну …"; -"web_rename_title" = "Перейменувати змінну"; -"web_rename_old" = "Назва"; -"web_rename_new" = "Нова назва"; -"web_rename_hint" = "Змінює кожне місце з точно таким написанням (number — це не Number), у коді та на структурограмі. Текст у \\"…\\" і коментарі лишаються."; -"web_rename_count_fmt" = "Трапляється %d раз(и)."; -"web_rename_exists_fmt" = "«%@» уже існує: після цього обидві стануть однією змінною."; -"web_rename_invalid" = "Назва починається з літери й містить лише літери, цифри та _."; -"web_rename_keyword_fmt" = "«%@» — ключове слово Java."; -"web_rename_apply" = "Перейменувати"; -"web_rename_cancel" = "Скасувати"; -`;export{e as default}; diff --git a/website/app/assets/visubot-CyGFOI9V.webp b/website/app/assets/visubot-CyGFOI9V.webp deleted file mode 100644 index 7dc5b040..00000000 Binary files a/website/app/assets/visubot-CyGFOI9V.webp and /dev/null differ diff --git a/website/app/assets/zh-Hans-R_GuNvYU.js b/website/app/assets/zh-Hans-R_GuNvYU.js deleted file mode 100644 index 99dd5e31..00000000 --- a/website/app/assets/zh-Hans-R_GuNvYU.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* 仅存在于网页版中的文本。其他所有文本均来自应用的 Localizable.strings。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 结构图"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "导出…"; -"web_export_title" = "保存为图片或 PDF,或打印"; -"web_export_png" = "图片(PNG)"; -"web_export_svg" = "矢量图(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 文件(.visustruct)"; -"web_export_failed_fmt" = "导出失败:%@"; -"web_print_hint" = "请通过“导出… → PDF”保存图表,然后从那里打印。"; -"web_delete" = "删除"; -"web_language_title" = "界面语言"; -"web_simulation_start" = "开始模拟"; -"web_simulation_end" = "结束模拟"; -"web_simulation_start_title" = "逐步运行结构图"; -"web_simulation_end_title" = "结束模拟并继续编辑"; -"web_palette_hint" = "拖入结构图"; -"web_tabs_label" = "图表"; -"web_status_modified" = "已修改"; -"web_inspector_title" = "属性"; -"web_inspector_resize" = "更改对象检查器的高度:上下拖动或滑动分隔线(双击:默认)"; -"web_inspector_help_drop" = "将元素从面板拖到块上:上部 = 插入到前面,下部 = 插入到后面,拖到“ø”上 = 替换。已有元素可用鼠标移动。"; -"web_inspector_help_inline" = "双击或双点元素即可直接在图中编辑文字(Enter 确认,Esc 取消)。case 名称也一样。"; -"web_inspector_help_files" = "也可以直接将文件拖入窗口。"; -"web_case_name" = "分支名称"; -"web_case_name_placeholder" = "例如 1, ok, default"; -"web_copied" = "已复制元素。"; -"web_discard_message_fmt" = "对“%@”的更改将会丢失。"; -"web_err_unreadable_fmt" = "“%@”不是可读取的 VisuStruct 文件(%@)。"; -"web_saved_fmt" = "已保存“%@”。"; -"web_saved_txt_fmt" = "已另存为“%@”。去掉 .txt 扩展名后即可使用。"; -"web_drop_impossible" = "无法放置在此处"; -"web_sim_delay" = "每步暂停"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "语言"; -"web_codegen_comments" = "将图表文字作为注释"; -"web_codegen_empty_else" = "在代码中输出空的 else 分支"; -"web_copied_check" = "已复制 ✓"; -"web_copy_manual" = "已选中,按 Ctrl+C 复制"; -"web_run_stopped_loop" = "执行已停止:可能存在无限循环。"; -"web_run_cancelled" = "执行已取消。"; -"web_run_error" = "运行时出错。"; -"web_run_dropped_fmt" = "… 已隐藏之前的 %d 行"; -"web_sim_call_arguments_fmt" = "调用与 %@ 的首行不匹配。请检查括号中的值。"; -"web_sim_missing_return_fmt" = "%@ 结束时没有用 return 返回值。"; -"web_sim_file_not_found_fmt" = "没有名为 %@ 的文件。"; -"web_sim_end_of_file_fmt" = "文件 %@ 没有更多行了。"; -"web_sim_null_value_fmt" = "%@ 是 null。"; -"web_sim_unknown_variable_case_fmt" = "未知变量:%1$@。Java 区分大小写:你是指 %2$@ 吗?"; -"web_sim_written_file_fmt" = "%@(已保存)"; -"web_site_home" = "主页"; -"web_site_imprint" = "版权信息"; -"web_site_privacy" = "隐私"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "编程"; -"web_live_hide" = "隐藏编程"; -"web_scale_canvas" = "文字大小:结构图"; -"web_scale_code" = "文字大小:编程"; -"web_live_indent" = "缩进"; -"web_live_indent_title" = "缩进代码:每层四个空格(选中的行,否则全部)"; -"web_live_show" = "显示编程"; -"web_live_resize" = "更改代码区宽度(双击:重置)"; -"web_live_hint" = "代码随图一起生成。灰色 = 占位符:在检查器中输入,文字就会出现在这里。点击某行可选中对应元素。"; -"web_live_ph_selector" = "变量"; -"web_live_ph_code" = "你的代码"; -"web_live_ph_end" = "结束"; -"web_live_language" = "编程语言"; -"web_live_java_class" = "作为类"; -"web_live_java_class_hint" = "类名 = 结构图标题"; -"web_live_java_main" = "包含 main 方法"; -"web_settings_display" = "显示"; -"web_live_swing" = "真正的 Java(含 Swing)"; -"web_live_swing_hint" = "带窗口的真正 Java(CheerpJ)。首次运行时一次性下载约 40 MB。"; -"web_live_swing_windows" = "Java 窗口"; -"web_live_swing_hide" = "关闭 Java 窗口"; -"web_live_swing_loading" = "正在加载 Java(首次约 40 MB)…"; -"web_live_swing_compiling" = "正在编译…"; -"web_live_swing_running" = "程序正在运行。"; -"web_live_swing_compile_failed" = "编译错误,见上方。"; -"web_live_swing_failed" = "无法加载 Java。请检查网络连接。"; -"web_live_hint_edit" = "在这里输入 Java 代码,结构图会随之生成。或者在结构图中操作,代码也会一起增长。灰色 = 占位符,直接覆盖输入即可。"; -"web_live_edit_label" = "Java 代码"; -"web_parse_line_fmt" = "第 %d 行:%@"; -"web_parse_missing" = "这里还缺少:%@"; -"web_parse_extra" = "多余:%@"; -"web_parse_unclosed_string" = "这里缺少结尾的 %@。"; -"web_parse_else_without_if" = "这个“else”没有对应的“if”。"; -"web_parse_case_outside_switch" = "“case”和“default”应写在“switch”里面。"; -"web_parse_missing_do_while" = "“do { … }”之后还缺少“while (条件);”。"; -"web_parse_more_methods" = "目前这里只计入一个方法。请在单独的标签页中创建其他子程序。"; -"web_parse_missing_import" = "最上面缺少 %@ 没有这一行,Java 不认识 Scanner。"; -"web_parse_missing_scanner" = "前面缺少 %@ 没有这一行就没有 Scanner。"; -"web_parse_unknown_type" = "Java 不认识这个数据类型。你是想写 %@ 吗?"; -"web_quickfix_insert_fmt" = "建议:插入 %@"; -"web_quickfix_replace_fmt" = "建议:用 %1$@ 代替 %2$@"; -"web_parse_wrong_case" = "Java 区分大小写。正确写法是 %@。"; -"web_editor_title" = "结构图"; -"web_editor_show" = "显示结构图"; -"web_editor_hide" = "隐藏结构图"; -"web_keywords_title" = "代码关键字"; -"web_keywords_hint" = "用 if、while、for 代替元素名称"; -"web_palette_compact" = "元素始终紧凑显示"; -"web_palette_compact_hint" = "图标下方显示小字名称;否则仅在栏较窄时"; -"web_about_version_fmt" = "版本 %@"; -"web_about_software" = "使用的软件"; -"web_about_software_text" = "代码编辑器使用 CodeMirror 和 Lezer(MIT 许可证)。启用 Swing 支持后,VisuStruct 会加载 Leaning Technologies 的 CheerpJ(Community 许可证)和 OpenJDK 的 javac。"; -"web_about_licenses" = "许可证文本"; -"web_live_run" = "运行"; -"web_live_run_title" = "运行程序:输出和输入显示在下方"; -"web_live_run_finished" = "程序已结束。"; -"web_live_run_stopped" = "已停止。"; -"web_console" = "控制台"; -"web_console_hide" = "隐藏控制台"; -"web_console_resize" = "调整控制台高度"; -"web_diagram_tools" = "图表工具"; -"web_files_title" = "文件"; -"web_files_show" = "显示文件"; -"web_files_hide" = "隐藏文件"; -"web_files_resize" = "调整文件栏宽度"; -"web_files_new_folder" = "新建文件夹"; -"web_files_switch" = "选择其他文件夹"; -"web_files_delete" = "删除"; -"web_files_in_browser" = "在此浏览器的存储中"; -"web_files_save_hint_fmt" = "保存时程序将放入 %@。"; -"web_files_empty" = "还没有保存任何内容。保存时,VisuStruct 会在这里创建一个以今天日期命名的文件夹。"; -"web_files_reconnect_hint" = "重新加载后,浏览器会再次询问 VisuStruct 是否可以使用你的文件夹。"; -"web_files_reconnect_fmt" = "打开文件夹“%@”"; -"web_files_connect_hint" = "选择一个文件夹作为工作区。VisuStruct 会在其中为每一天创建文件夹,并保存图和代码。"; -"web_files_connect" = "选择文件夹"; -"web_files_browser_hint" = "此浏览器无法打开设备上的文件夹(例如 iPad)。你的文件会保留在此浏览器的存储中。"; -"web_files_browser" = "保存在浏览器中"; -"web_files_delete_confirm_fmt" = "确定删除“%@”?"; -"web_files_folder_prompt" = "新文件夹名称:"; -"web_files_default_name" = "程序"; -"web_files_saved_fmt" = "已保存:%@"; -"web_files_create" = "新建"; -"web_learn_ludwig_tip" = "路德维希的提示"; -"web_game_title" = "游戏:真还是假?"; -"web_game_intro" = "路德维希给你看一些值和一个条件。这个条件是真(true)还是假(false)?共十轮,越来越难。"; -"web_game_start" = "开始游戏"; -"web_game_round_fmt" = "第 %@ 轮,共 %@ 轮"; -"web_game_score_fmt" = "得分:%@"; -"web_game_true" = "真 (true)"; -"web_game_false" = "假 (false)"; -"web_game_right" = "答对了!"; -"web_game_wrong" = "不太对。是这样计算的:"; -"web_game_next" = "下一轮"; -"web_game_finish" = "结果"; -"web_game_done_fmt" = "答对 %@ / %@。"; -"web_game_perfect" = "全部正确——路德维希被你折服了!"; -"web_game_good" = "很棒!再看看错题,然后再试一次。"; -"web_game_practice" = "熟能生巧。再读一遍上面的真值表,再玩一轮吧。"; -"web_game_again" = "再玩一次"; -"web_game_wrong_short" = "很遗憾,答错了。"; -"web_game_practice_short" = "熟能生巧。再读一遍解释,再玩一轮吧。"; -"web_game_clicks" = "点击:"; -"web_game_nobody" = "没有人在监听——什么也不会发生。"; -"web_game_event_fmt" = "点击“%@”:监听器听到后执行它的代码。"; -"web_game_try" = "现在自己试试窗口里的按钮吧。"; -"web_game_time_fmt" = "还剩 %@ 秒"; -"web_game_defused" = "✂️ 拆除成功!你找到了错误。"; -"web_game_boom_time" = "💥 嘭!时间到了。"; -"web_game_boom_wrong" = "💥 嘭!这不是出错的那一行。"; -"web_game_boom_part" = "💥 砰!这一行对了,但错误在另一部分。"; -"web_game_boom_wrong_part" = "💥 砰!错误在另一行。"; -"web_game_bomb_parts_hint" = "准确点击出错的部分。"; -"web_game_fix" = "正确的写法:"; -"web_game_order_hint" = "按正确的顺序点击各个步骤。"; -"web_debug_step" = "单步"; -"web_debug_step_title" = "单步:执行一条语句后再次暂停"; -"web_debug_continue" = "继续运行到下一个断点"; -"web_debug_paused_fmt" = "在第 %d 行之前暂停。"; -"web_debug_gutter_title" = "点击行号可设置或删除断点"; -"web_swap_to_code" = "编程放大"; -"web_swap_to_editor" = "结构图放大"; -"web_editor_resize" = "调整结构图宽度"; -"web_palette_resize" = "调整元素宽度"; -"web_palette_hide" = "隐藏元素"; -"web_palette_show" = "显示元素"; -"web_code_scopes" = "代码块彩色显示"; -"web_code_scopes_hint" = "类、方法、循环、分支各有自己的框"; -"web_pseudocode" = "伪代码"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "重命名变量 …"; -"web_rename_title" = "重命名变量"; -"web_rename_old" = "名称"; -"web_rename_new" = "新名称"; -"web_rename_hint" = "在代码和结构图中更改所有拼写完全相同的地方(number 不是 Number)。\\"…\\" 中的文本和注释保持不变。"; -"web_rename_count_fmt" = "出现 %d 次。"; -"web_rename_exists_fmt" = "“%@”已存在:之后两者会成为同一个变量。"; -"web_rename_invalid" = "名称以字母开头,只包含字母、数字和 _。"; -"web_rename_keyword_fmt" = "“%@”是 Java 关键字。"; -"web_rename_apply" = "重命名"; -"web_rename_cancel" = "取消"; -`;export{e as default}; diff --git a/website/app/assets/zh-Hant-DoE9BrE1.js b/website/app/assets/zh-Hant-DoE9BrE1.js deleted file mode 100644 index a9730929..00000000 --- a/website/app/assets/zh-Hant-DoE9BrE1.js +++ /dev/null @@ -1,202 +0,0 @@ -const e=`/* 僅存在於網頁版的文字。其他所有文字皆來自 App 的 Localizable.strings。 */ - -"web_brand_suffix" = "Web"; -"web_file_type" = "VisuStruct 結構圖"; -"web_key_ctrl" = "Ctrl"; -"web_key_delete" = "Del"; -"web_key_shift" = "Shift"; -"web_export" = "匯出…"; -"web_export_title" = "儲存為圖片或 PDF,或列印"; -"web_export_png" = "圖片(PNG)"; -"web_export_svg" = "向量圖形(SVG)"; -"web_export_pdf" = "PDF(A4)"; -"web_export_visustruct" = "VisuStruct 檔案(.visustruct)"; -"web_export_failed_fmt" = "匯出失敗:%@"; -"web_print_hint" = "請透過「匯出… → PDF」儲存圖表,再從那裡列印。"; -"web_delete" = "刪除"; -"web_language_title" = "介面語言"; -"web_simulation_start" = "開始模擬"; -"web_simulation_end" = "結束模擬"; -"web_simulation_start_title" = "逐步執行結構圖"; -"web_simulation_end_title" = "結束模擬並繼續編輯"; -"web_palette_hint" = "拖曳到結構圖中"; -"web_tabs_label" = "圖表"; -"web_status_modified" = "已修改"; -"web_inspector_title" = "屬性"; -"web_inspector_resize" = "變更物件檢閱器的高度:上下拖曳或滑動分隔線(按兩下:預設)"; -"web_inspector_help_drop" = "將元素從面板拖曳到區塊上:上方 = 插入在前,下方 = 插入在後,拖到「ø」上 = 取代。現有元素可用滑鼠移動。"; -"web_inspector_help_inline" = "雙擊或雙點元素即可直接在圖中編輯文字(Enter 確認,Esc 取消)。case 名稱也一樣。"; -"web_inspector_help_files" = "也可以直接將檔案拖曳到視窗中。"; -"web_case_name" = "分支名稱"; -"web_case_name_placeholder" = "例如 1, ok, default"; -"web_copied" = "已複製元素。"; -"web_discard_message_fmt" = "對「%@」所做的變更將會遺失。"; -"web_err_unreadable_fmt" = "「%@」不是可讀取的 VisuStruct 檔案(%@)。"; -"web_saved_fmt" = "已儲存「%@」。"; -"web_saved_txt_fmt" = "已儲存為「%@」。移除 .txt 副檔名後即可使用。"; -"web_drop_impossible" = "無法放置在此處"; -"web_sim_delay" = "每步暫停"; -"web_sim_delay_value_fmt" = "%@ 秒"; -"web_codegen_language" = "語言"; -"web_codegen_comments" = "將圖表文字作為註解"; -"web_codegen_empty_else" = "在程式碼中輸出空的 else 分支"; -"web_copied_check" = "已複製 ✓"; -"web_copy_manual" = "已選取,按 Ctrl+C 複製"; -"web_run_stopped_loop" = "執行已停止:可能是無限迴圈。"; -"web_run_cancelled" = "執行已取消。"; -"web_run_error" = "執行時發生錯誤。"; -"web_run_dropped_fmt" = "… 已隱藏先前的 %d 行"; -"web_sim_call_arguments_fmt" = "呼叫與 %@ 的首行不相符。請檢查括號中的值。"; -"web_sim_missing_return_fmt" = "%@ 結束時沒有用 return 傳回值。"; -"web_sim_file_not_found_fmt" = "沒有名為 %@ 的檔案。"; -"web_sim_end_of_file_fmt" = "檔案 %@ 沒有更多行了。"; -"web_sim_null_value_fmt" = "%@ 是 null。"; -"web_sim_unknown_variable_case_fmt" = "未知變數:%1$@。Java 區分大小寫:你是指 %2$@ 嗎?"; -"web_sim_written_file_fmt" = "%@(已儲存)"; -"web_site_home" = "首頁"; -"web_site_imprint" = "版權資訊"; -"web_site_privacy" = "隱私"; - -/* Programmieren (früher „Code live“) */ -"web_live_title" = "程式設計"; -"web_live_hide" = "隱藏程式設計"; -"web_scale_canvas" = "文字大小:結構圖"; -"web_scale_code" = "文字大小:程式設計"; -"web_live_indent" = "縮排"; -"web_live_indent_title" = "縮排程式碼:每層四個空格(選取的行,否則全部)"; -"web_live_show" = "顯示程式設計"; -"web_live_resize" = "變更程式碼區寬度(按兩下:重設)"; -"web_live_hint" = "程式碼會隨圖一起產生。灰色 = 預留位置:在檢閱器中輸入,文字就會出現在這裡。點一下某行可選取對應元素。"; -"web_live_ph_selector" = "變數"; -"web_live_ph_code" = "你的程式碼"; -"web_live_ph_end" = "結束"; -"web_live_language" = "程式語言"; -"web_live_java_class" = "作為類別"; -"web_live_java_class_hint" = "類別名稱 = 結構圖標題"; -"web_live_java_main" = "包含 main 方法"; -"web_settings_display" = "顯示"; -"web_live_swing" = "真正的 Java(含 Swing)"; -"web_live_swing_hint" = "帶視窗的真正 Java(CheerpJ)。首次執行時一次性下載約 40 MB。"; -"web_live_swing_windows" = "Java 視窗"; -"web_live_swing_hide" = "關閉 Java 視窗"; -"web_live_swing_loading" = "正在載入 Java(首次約 40 MB)…"; -"web_live_swing_compiling" = "正在編譯…"; -"web_live_swing_running" = "程式執行中。"; -"web_live_swing_compile_failed" = "編譯錯誤,見上方。"; -"web_live_swing_failed" = "無法載入 Java。請檢查網路連線。"; -"web_live_hint_edit" = "在這裡輸入 Java 程式碼,結構圖會隨之產生。或者在結構圖中操作,程式碼也會一起成長。灰色 = 預留位置,直接覆寫輸入即可。"; -"web_live_edit_label" = "Java 程式碼"; -"web_parse_line_fmt" = "第 %d 行:%@"; -"web_parse_missing" = "這裡還缺少:%@"; -"web_parse_extra" = "多餘:%@"; -"web_parse_unclosed_string" = "這裡缺少結尾的 %@。"; -"web_parse_else_without_if" = "這個「else」沒有對應的「if」。"; -"web_parse_case_outside_switch" = "「case」和「default」應寫在「switch」裡面。"; -"web_parse_missing_do_while" = "「do { … }」之後還缺少「while (條件);」。"; -"web_parse_more_methods" = "目前這裡只計入一個方法。請在單獨的分頁中建立其他子程式。"; -"web_parse_missing_import" = "最上面缺少 %@ 沒有這一行,Java 不認識 Scanner。"; -"web_parse_missing_scanner" = "前面缺少 %@ 沒有這一行就沒有 Scanner。"; -"web_parse_unknown_type" = "Java 不認識這個資料型別。你是想寫 %@ 嗎?"; -"web_quickfix_insert_fmt" = "建議:插入 %@"; -"web_quickfix_replace_fmt" = "建議:用 %1$@ 代替 %2$@"; -"web_parse_wrong_case" = "Java 區分大小寫。正確寫法是 %@。"; -"web_editor_title" = "結構圖"; -"web_editor_show" = "顯示結構圖"; -"web_editor_hide" = "隱藏結構圖"; -"web_keywords_title" = "程式碼關鍵字"; -"web_keywords_hint" = "用 if、while、for 取代元素名稱"; -"web_palette_compact" = "元素一律精簡顯示"; -"web_palette_compact_hint" = "圖示下方顯示小字名稱;否則僅在欄位較窄時"; -"web_about_version_fmt" = "版本 %@"; -"web_about_software" = "使用的軟體"; -"web_about_software_text" = "程式碼編輯器使用 CodeMirror 與 Lezer(MIT 授權)。啟用 Swing 支援後,VisuStruct 會載入 Leaning Technologies 的 CheerpJ(Community 授權)與 OpenJDK 的 javac。"; -"web_about_licenses" = "授權條款全文"; -"web_live_run" = "執行"; -"web_live_run_title" = "執行程式:輸出與輸入顯示在下方"; -"web_live_run_finished" = "程式已結束。"; -"web_live_run_stopped" = "已停止。"; -"web_console" = "主控台"; -"web_console_hide" = "隱藏主控台"; -"web_console_resize" = "調整主控台高度"; -"web_diagram_tools" = "圖表工具"; -"web_files_title" = "檔案"; -"web_files_show" = "顯示檔案"; -"web_files_hide" = "隱藏檔案"; -"web_files_resize" = "調整檔案欄寬度"; -"web_files_new_folder" = "新增資料夾"; -"web_files_switch" = "選擇其他資料夾"; -"web_files_delete" = "刪除"; -"web_files_in_browser" = "在此瀏覽器的儲存空間中"; -"web_files_save_hint_fmt" = "儲存時程式會放入 %@。"; -"web_files_empty" = "還沒有儲存任何內容。儲存時,VisuStruct 會在這裡建立一個以今天日期命名的資料夾。"; -"web_files_reconnect_hint" = "重新載入後,瀏覽器會再次詢問 VisuStruct 是否可以使用你的資料夾。"; -"web_files_reconnect_fmt" = "開啟資料夾「%@」"; -"web_files_connect_hint" = "選擇一個資料夾作為工作區。VisuStruct 會在其中為每一天建立資料夾,並儲存圖與程式碼。"; -"web_files_connect" = "選擇資料夾"; -"web_files_browser_hint" = "此瀏覽器無法開啟裝置上的資料夾(例如 iPad)。你的檔案會保留在此瀏覽器的儲存空間中。"; -"web_files_browser" = "儲存在瀏覽器中"; -"web_files_delete_confirm_fmt" = "確定刪除「%@」?"; -"web_files_folder_prompt" = "新資料夾名稱:"; -"web_files_default_name" = "程式"; -"web_files_saved_fmt" = "已儲存:%@"; -"web_files_create" = "新增"; -"web_learn_ludwig_tip" = "路德維希的提示"; -"web_game_title" = "遊戲:真還是假?"; -"web_game_intro" = "路德維希給你看一些值和一個條件。這個條件是真(true)還是假(false)?共十輪,越來越難。"; -"web_game_start" = "開始遊戲"; -"web_game_round_fmt" = "第 %@ 輪,共 %@ 輪"; -"web_game_score_fmt" = "得分:%@"; -"web_game_true" = "真 (true)"; -"web_game_false" = "假 (false)"; -"web_game_right" = "答對了!"; -"web_game_wrong" = "不太對。是這樣計算的:"; -"web_game_next" = "下一輪"; -"web_game_finish" = "結果"; -"web_game_done_fmt" = "答對 %@ / %@。"; -"web_game_perfect" = "全部正確——路德維希被你折服了!"; -"web_game_good" = "很棒!再看看錯題,然後再試一次。"; -"web_game_practice" = "熟能生巧。再讀一遍上面的真值表,再玩一輪吧。"; -"web_game_again" = "再玩一次"; -"web_game_wrong_short" = "很可惜,答錯了。"; -"web_game_practice_short" = "熟能生巧。再讀一遍說明,再玩一輪吧。"; -"web_game_clicks" = "點擊:"; -"web_game_nobody" = "沒有人在監聽——什麼也不會發生。"; -"web_game_event_fmt" = "點擊「%@」:監聽器聽到後執行它的程式碼。"; -"web_game_try" = "現在自己試試視窗裡的按鈕吧。"; -"web_game_time_fmt" = "還剩 %@ 秒"; -"web_game_defused" = "✂️ 拆除成功!你找到了錯誤。"; -"web_game_boom_time" = "💥 砰!時間到了。"; -"web_game_boom_wrong" = "💥 砰!這不是出錯的那一行。"; -"web_game_boom_part" = "💥 砰!這一行對了,但錯誤在另一部分。"; -"web_game_boom_wrong_part" = "💥 砰!錯誤在另一行。"; -"web_game_bomb_parts_hint" = "準確點擊出錯的部分。"; -"web_game_fix" = "正確的寫法:"; -"web_game_order_hint" = "按正確的順序點擊各個步驟。"; -"web_debug_step" = "單步"; -"web_debug_step_title" = "單步:執行一條敘述後再次暫停"; -"web_debug_continue" = "繼續執行到下一個中斷點"; -"web_debug_paused_fmt" = "在第 %d 行之前暫停。"; -"web_debug_gutter_title" = "點擊行號可設定或刪除中斷點"; -"web_swap_to_code" = "程式設計放大"; -"web_swap_to_editor" = "結構圖放大"; -"web_editor_resize" = "調整結構圖寬度"; -"web_palette_resize" = "調整元素寬度"; -"web_palette_hide" = "隱藏元素"; -"web_palette_show" = "顯示元素"; -"web_code_scopes" = "程式碼區塊彩色顯示"; -"web_code_scopes_hint" = "類別、方法、迴圈、分支各有自己的框"; -"web_pseudocode" = "虛擬碼"; - -/* Variable umbenennen (Refactoring) */ -"web_rename_menu" = "重新命名變數 …"; -"web_rename_title" = "重新命名變數"; -"web_rename_old" = "名稱"; -"web_rename_new" = "新名稱"; -"web_rename_hint" = "在程式碼和結構圖中更改所有拼寫完全相同的地方(number 不是 Number)。\\"…\\" 中的文字和註解保持不變。"; -"web_rename_count_fmt" = "出現 %d 次。"; -"web_rename_exists_fmt" = "「%@」已存在:之後兩者會成為同一個變數。"; -"web_rename_invalid" = "名稱以字母開頭,只包含字母、數字和 _。"; -"web_rename_keyword_fmt" = "「%@」是 Java 關鍵字。"; -"web_rename_apply" = "重新命名"; -"web_rename_cancel" = "取消"; -`;export{e as default}; diff --git a/website/app/assets/zk-zeichen-8efNmpGx.svg b/website/app/assets/zk-zeichen-8efNmpGx.svg deleted file mode 100644 index eac69023..00000000 --- a/website/app/assets/zk-zeichen-8efNmpGx.svg +++ /dev/null @@ -1,31 +0,0 @@ - - -String palavra = "Passatempo" -0 -P -1 -a -2 -s -3 -s -4 -a -5 -t -6 -e -7 -m -8 -p -9 -o -substring(0, 5) -→ "Passa" -substring(5) -→ "tempo" -charAt(0) → 'P'length() → 10 -indexOf("tempo") → 5 -O fim de substring não está incluído. - diff --git a/website/app/assets/zk-zeichen-CgUEhNoK.svg b/website/app/assets/zk-zeichen-CgUEhNoK.svg deleted file mode 100644 index 0eb01ebf..00000000 --- a/website/app/assets/zk-zeichen-CgUEhNoK.svg +++ /dev/null @@ -1,29 +0,0 @@ - - -String word = "Billygoat" -0 -B -1 -i -2 -l -3 -l -4 -y -5 -g -6 -o -7 -a -8 -t -substring(0, 5) -→ "Billy" -substring(5) -→ "goat" -charAt(0) → 'B'length() → 9 -indexOf("goat") → 5 -The end of substring is not included. - diff --git a/website/app/assets/zk-zeichen-vYvc3iJg.svg b/website/app/assets/zk-zeichen-vYvc3iJg.svg deleted file mode 100644 index 030e9d81..00000000 --- a/website/app/assets/zk-zeichen-vYvc3iJg.svg +++ /dev/null @@ -1,27 +0,0 @@ - - -String wort = "Geißbock" -0 -G -1 -e -2 -i -3 -ß -4 -b -5 -o -6 -c -7 -k -substring(0, 4) -→ "Geiß" -substring(4) -→ "bock" -charAt(0) → 'G'length() → 8 -indexOf("bock") → 4 -Das Ende von substring gehört nicht dazu. - diff --git a/website/app/index.html b/website/app/index.html deleted file mode 100644 index a6b85f9b..00000000 --- a/website/app/index.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - VisuStruct Web - - - - - - -
    - - diff --git a/website/app/licenses.txt b/website/app/licenses.txt deleted file mode 100644 index aca41c2a..00000000 --- a/website/app/licenses.txt +++ /dev/null @@ -1,494 +0,0 @@ -VisuStruct Web – verwendete Software / Third-party software - -VisuStruct: MIT License, Copyright (c) 2026 Holger Sebastiao, https://github.com/code4teaching/VisuStruct-SwiftUI - -Nur mit „Swing-Unterstützung“ wird zur Laufzeit geladen / Loaded at run time only with “Swing support”: -- CheerpJ by Leaning Technologies, https://cheerpj.com (CheerpJ Community License, from the CheerpJ CDN) -- javac from OpenJDK 8 (tools.jar from https://github.com/leaningtech/javafiddle, Apache License 2.0; OpenJDK: GPLv2 with Classpath Exception) - -Übertragen auf den mBot2 / Upload to the mBot2: Ablauf nach / protocol after cyberpi-uploader, https://github.com/weare2plus3/cyberpi-uploader -MIT License - -Copyright (c) 2026 TWO PLUS THREE LIMITED - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -Mitgelieferte Pakete / Bundled packages: - -======================================================================== -@codemirror/autocomplete 6.20.4 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/commands 6.11.1 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/lang-java 6.0.2 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/language 6.13.1 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/state 6.7.6 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/streamparser 6.0.0 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@codemirror/view 6.43.15 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018-2021 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@lezer/common 1.5.4 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@lezer/highlight 1.2.5 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@lezer/java 1.1.5 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2020 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@lezer/lr 1.4.11 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2018 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -@marijn/find-cluster-break 1.0.4 (MIT) -======================================================================== - -MIT License - -Copyright (C) 2024 by Marijn Haverbeke - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -crelt 1.0.7 (MIT) -======================================================================== - -Copyright (C) 2020 by Marijn Haverbeke - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -fflate 0.8.2 (MIT) -======================================================================== - -MIT License - -Copyright (c) 2023 Arjun Barrett - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -======================================================================== -style-mod 4.1.4 (MIT) -======================================================================== - -Copyright (C) 2018 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -three 0.181.2 (MIT) -======================================================================== - -The MIT License - -Copyright © 2010-2025 three.js authors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -======================================================================== -visublocks 0.1.0 (MIT) -======================================================================== - -MIT License - -Copyright (c) 2026 Christian Klein, Holger Sebastiao - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -======================================================================== -w3c-keyname 2.2.8 (MIT) -======================================================================== - -Copyright (C) 2016 by Marijn Haverbeke and others - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/website/assets/Download-on-the-App-Store/DE/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_DE_RGB_wht_092917.svg b/website/assets/Download-on-the-App-Store/DE/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_DE_RGB_wht_092917.svg deleted file mode 100755 index 0d545d2a..00000000 --- a/website/assets/Download-on-the-App-Store/DE/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_DE_RGB_wht_092917.svg +++ /dev/null @@ -1,40 +0,0 @@ - - Download_on_the_App_Store_Badge_DE_RGB_wht_092917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/assets/Download-on-the-App-Store/ES/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_ES_RGB_wht_100217.svg b/website/assets/Download-on-the-App-Store/ES/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_ES_RGB_wht_100217.svg deleted file mode 100755 index 85dc0c46..00000000 --- a/website/assets/Download-on-the-App-Store/ES/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_ES_RGB_wht_100217.svg +++ /dev/null @@ -1,47 +0,0 @@ - - Download_on_the_App_Store_Badge_ES_RGB_wht_100217 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/assets/Download-on-the-App-Store/FR/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_FR_RGB_wht_100217.svg b/website/assets/Download-on-the-App-Store/FR/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_FR_RGB_wht_100217.svg deleted file mode 100755 index 8da6feed..00000000 --- a/website/assets/Download-on-the-App-Store/FR/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_FR_RGB_wht_100217.svg +++ /dev/null @@ -1,50 +0,0 @@ - - Download_on_the_App_Store_Badge_FR_RGB_wht_100217 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/assets/Download-on-the-App-Store/PTPT/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_PTPT_RGB_wht_100317.svg b/website/assets/Download-on-the-App-Store/PTPT/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_PTPT_RGB_wht_100317.svg deleted file mode 100755 index 5ea0cc90..00000000 --- a/website/assets/Download-on-the-App-Store/PTPT/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_PTPT_RGB_wht_100317.svg +++ /dev/null @@ -1,46 +0,0 @@ - - Download_on_the_App_Store_Badge_PTPT_RGB_wht_4SVG_source_092917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/assets/Download-on-the-App-Store/US/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_US-UK_RGB_wht_092917.svg b/website/assets/Download-on-the-App-Store/US/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_US-UK_RGB_wht_092917.svg deleted file mode 100755 index 16c0496c..00000000 --- a/website/assets/Download-on-the-App-Store/US/Download_on_App_Store/White_lockup/SVG/Download_on_the_App_Store_Badge_US-UK_RGB_wht_092917.svg +++ /dev/null @@ -1,46 +0,0 @@ - - Download_on_the_App_Store_Badge_US-UK_RGB_wht_092917 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/website/assets/javaApp/visustruct-app-java-deutsch-switchCase.png b/website/assets/javaApp/visustruct-app-java-deutsch-switchCase.png deleted file mode 100644 index 9fb096f7..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-deutsch-switchCase.png and /dev/null differ diff --git a/website/assets/javaApp/visustruct-app-java-deutsch.png b/website/assets/javaApp/visustruct-app-java-deutsch.png deleted file mode 100644 index 47269601..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-deutsch.png and /dev/null differ diff --git a/website/assets/javaApp/visustruct-app-java-englisch-java_like.png b/website/assets/javaApp/visustruct-app-java-englisch-java_like.png deleted file mode 100644 index 0bd1173b..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-englisch-java_like.png and /dev/null differ diff --git a/website/assets/javaApp/visustruct-app-java-english.png b/website/assets/javaApp/visustruct-app-java-english.png deleted file mode 100644 index cbcb4242..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-english.png and /dev/null differ diff --git a/website/assets/javaApp/visustruct-app-java-generateCodeJS.png b/website/assets/javaApp/visustruct-app-java-generateCodeJS.png deleted file mode 100644 index 882a16df..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-generateCodeJS.png and /dev/null differ diff --git a/website/assets/javaApp/visustruct-app-java-portugues.png b/website/assets/javaApp/visustruct-app-java-portugues.png deleted file mode 100644 index 15d249e9..00000000 Binary files a/website/assets/javaApp/visustruct-app-java-portugues.png and /dev/null differ diff --git a/website/assets/logo-mark.svg b/website/assets/logo-mark.svg deleted file mode 100644 index b9756f49..00000000 --- a/website/assets/logo-mark.svg +++ /dev/null @@ -1,36 +0,0 @@ - diff --git a/website/assets/swiftApp/visustruct-app-swift-deutsch-switchCase.png b/website/assets/swiftApp/visustruct-app-swift-deutsch-switchCase.png deleted file mode 100755 index bc9bd439..00000000 Binary files a/website/assets/swiftApp/visustruct-app-swift-deutsch-switchCase.png and /dev/null differ diff --git a/website/assets/swiftApp/visustruct-app-swift-english.png b/website/assets/swiftApp/visustruct-app-swift-english.png deleted file mode 100644 index ee14e30e..00000000 Binary files a/website/assets/swiftApp/visustruct-app-swift-english.png and /dev/null differ diff --git a/website/assets/swiftApp/visustruct-app-swift-german.png b/website/assets/swiftApp/visustruct-app-swift-german.png deleted file mode 100644 index ccee1d2f..00000000 Binary files a/website/assets/swiftApp/visustruct-app-swift-german.png and /dev/null differ diff --git a/website/assets/swiftApp/visustruct-app-swift-java_like.png b/website/assets/swiftApp/visustruct-app-swift-java_like.png deleted file mode 100644 index 63285879..00000000 Binary files a/website/assets/swiftApp/visustruct-app-swift-java_like.png and /dev/null differ diff --git a/website/assets/swiftApp/visustruct-app-swift-portuguese.png b/website/assets/swiftApp/visustruct-app-swift-portuguese.png deleted file mode 100644 index 75e005d2..00000000 Binary files a/website/assets/swiftApp/visustruct-app-swift-portuguese.png and /dev/null differ diff --git a/website/assets/visubot/steps/step-build-de.png b/website/assets/visubot/steps/step-build-de.png deleted file mode 100644 index ac98c114..00000000 Binary files a/website/assets/visubot/steps/step-build-de.png and /dev/null differ diff --git a/website/assets/visubot/steps/step-build-en.png b/website/assets/visubot/steps/step-build-en.png deleted file mode 100644 index 27555037..00000000 Binary files a/website/assets/visubot/steps/step-build-en.png and /dev/null differ diff --git a/website/assets/visubot/steps/step-build-pt.png b/website/assets/visubot/steps/step-build-pt.png deleted file mode 100644 index 82204417..00000000 Binary files a/website/assets/visubot/steps/step-build-pt.png and /dev/null differ diff --git a/website/assets/visubot/steps/step-drive.jpg b/website/assets/visubot/steps/step-drive.jpg deleted file mode 100644 index 7bd86567..00000000 Binary files a/website/assets/visubot/steps/step-drive.jpg and /dev/null differ diff --git a/website/assets/visubot/steps/step-test.png b/website/assets/visubot/steps/step-test.png deleted file mode 100644 index 63e8bdf1..00000000 Binary files a/website/assets/visubot/steps/step-test.png and /dev/null differ diff --git a/website/assets/visubot/visubot-app-de.png b/website/assets/visubot/visubot-app-de.png deleted file mode 100644 index f9c585e9..00000000 Binary files a/website/assets/visubot/visubot-app-de.png and /dev/null differ diff --git a/website/assets/visubot/visubot-app-en.png b/website/assets/visubot/visubot-app-en.png deleted file mode 100644 index 3283a336..00000000 Binary files a/website/assets/visubot/visubot-app-en.png and /dev/null differ diff --git a/website/assets/visubot/visubot-app-pt.png b/website/assets/visubot/visubot-app-pt.png deleted file mode 100644 index 0ea3d143..00000000 Binary files a/website/assets/visubot/visubot-app-pt.png and /dev/null differ diff --git a/website/assets/visubot/visubot-de.png b/website/assets/visubot/visubot-de.png deleted file mode 100644 index 762bd98a..00000000 Binary files a/website/assets/visubot/visubot-de.png and /dev/null differ diff --git a/website/assets/visubot/visubot-en.png b/website/assets/visubot/visubot-en.png deleted file mode 100644 index 336d3e52..00000000 Binary files a/website/assets/visubot/visubot-en.png and /dev/null differ diff --git a/website/assets/visubot/visubot-logo.svg b/website/assets/visubot/visubot-logo.svg deleted file mode 100644 index 3d23c727..00000000 --- a/website/assets/visubot/visubot-logo.svg +++ /dev/null @@ -1,7 +0,0 @@ - - VisuBot - VisuBot logo: a friendly robot in navy, white and orange measuring the distance to a lilac cube with ultrasonic waves, next to the word VisuBot and the claim Struktogramme - Robotik - Code. - - VisuBot - Struktogramme - Robotik - Code - diff --git a/website/assets/visubot/visubot-mark.webp b/website/assets/visubot/visubot-mark.webp deleted file mode 100644 index 7dc5b040..00000000 Binary files a/website/assets/visubot/visubot-mark.webp and /dev/null differ diff --git a/website/assets/visubot/visubot-pt.png b/website/assets/visubot/visubot-pt.png deleted file mode 100644 index c5a0e45c..00000000 Binary files a/website/assets/visubot/visubot-pt.png and /dev/null differ diff --git a/website/assets/visuobject/inspektor-de.png b/website/assets/visuobject/inspektor-de.png deleted file mode 100644 index c6117790..00000000 Binary files a/website/assets/visuobject/inspektor-de.png and /dev/null differ diff --git a/website/assets/visuobject/step-debug.png b/website/assets/visuobject/step-debug.png deleted file mode 100644 index bd8bc9fa..00000000 Binary files a/website/assets/visuobject/step-debug.png and /dev/null differ diff --git a/website/assets/visuobject/step-draw.png b/website/assets/visuobject/step-draw.png deleted file mode 100644 index cbd27a11..00000000 Binary files a/website/assets/visuobject/step-draw.png and /dev/null differ diff --git a/website/assets/visuobject/step-objects.png b/website/assets/visuobject/step-objects.png deleted file mode 100644 index 1f23cf48..00000000 Binary files a/website/assets/visuobject/step-objects.png and /dev/null differ diff --git a/website/assets/visuobject/visuobject-app-de.png b/website/assets/visuobject/visuobject-app-de.png deleted file mode 100644 index b6249b13..00000000 Binary files a/website/assets/visuobject/visuobject-app-de.png and /dev/null differ diff --git a/website/assets/visuobject/visuobject-de.png b/website/assets/visuobject/visuobject-de.png deleted file mode 100644 index 1a2eef68..00000000 Binary files a/website/assets/visuobject/visuobject-de.png and /dev/null differ diff --git a/website/assets/visuobject/visuobject-en.png b/website/assets/visuobject/visuobject-en.png deleted file mode 100644 index 0675b808..00000000 Binary files a/website/assets/visuobject/visuobject-en.png and /dev/null differ diff --git a/website/assets/visuobject/visuobject-mark.svg b/website/assets/visuobject/visuobject-mark.svg deleted file mode 100644 index 797ceb9c..00000000 --- a/website/assets/visuobject/visuobject-mark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/website/assets/visustruct-app-java-deutsch-switchCase.png b/website/assets/visustruct-app-java-deutsch-switchCase.png deleted file mode 100644 index 9fb096f7..00000000 Binary files a/website/assets/visustruct-app-java-deutsch-switchCase.png and /dev/null differ diff --git a/website/assets/visustruct-app-java-deutsch.png b/website/assets/visustruct-app-java-deutsch.png deleted file mode 100644 index 47269601..00000000 Binary files a/website/assets/visustruct-app-java-deutsch.png and /dev/null differ diff --git a/website/assets/visustruct-app-java-englisch-java_like.png b/website/assets/visustruct-app-java-englisch-java_like.png deleted file mode 100644 index 0bd1173b..00000000 Binary files a/website/assets/visustruct-app-java-englisch-java_like.png and /dev/null differ diff --git a/website/assets/visustruct-app-java-english.png b/website/assets/visustruct-app-java-english.png deleted file mode 100644 index cbcb4242..00000000 Binary files a/website/assets/visustruct-app-java-english.png and /dev/null differ diff --git a/website/assets/visustruct-app-java-generateCodeJS.png b/website/assets/visustruct-app-java-generateCodeJS.png deleted file mode 100644 index 882a16df..00000000 Binary files a/website/assets/visustruct-app-java-generateCodeJS.png and /dev/null differ diff --git a/website/assets/visustruct-app-java-portugues.png b/website/assets/visustruct-app-java-portugues.png deleted file mode 100644 index 15d249e9..00000000 Binary files a/website/assets/visustruct-app-java-portugues.png and /dev/null differ diff --git a/website/assets/visustruct-app-java.png b/website/assets/visustruct-app-java.png deleted file mode 100644 index 9fb096f7..00000000 Binary files a/website/assets/visustruct-app-java.png and /dev/null differ diff --git a/website/assets/visustruct-app-mac-ipad.png b/website/assets/visustruct-app-mac-ipad.png deleted file mode 100644 index bc9bd439..00000000 Binary files a/website/assets/visustruct-app-mac-ipad.png and /dev/null differ diff --git a/website/assets/visustruct-app-swift-deutsch-switchCase.png b/website/assets/visustruct-app-swift-deutsch-switchCase.png deleted file mode 100755 index bc9bd439..00000000 Binary files a/website/assets/visustruct-app-swift-deutsch-switchCase.png and /dev/null differ diff --git a/website/assets/visustruct-app-swift-english.png b/website/assets/visustruct-app-swift-english.png deleted file mode 100755 index 9403d04f..00000000 Binary files a/website/assets/visustruct-app-swift-english.png and /dev/null differ diff --git a/website/assets/visustruct-app-swift-german.png b/website/assets/visustruct-app-swift-german.png deleted file mode 100755 index 9c3f581c..00000000 Binary files a/website/assets/visustruct-app-swift-german.png and /dev/null differ diff --git a/website/assets/visustruct-app-swift-java_like.png b/website/assets/visustruct-app-swift-java_like.png deleted file mode 100755 index 7bcba072..00000000 Binary files a/website/assets/visustruct-app-swift-java_like.png and /dev/null differ diff --git a/website/assets/visustruct-app-swift-portuguese.png b/website/assets/visustruct-app-swift-portuguese.png deleted file mode 100755 index a2f6003c..00000000 Binary files a/website/assets/visustruct-app-swift-portuguese.png and /dev/null differ diff --git a/website/assets/visustruct-movies/VisuStruct-Simulator.mp4 b/website/assets/visustruct-movies/VisuStruct-Simulator.mp4 deleted file mode 100644 index f47f178a..00000000 Binary files a/website/assets/visustruct-movies/VisuStruct-Simulator.mp4 and /dev/null differ diff --git a/website/assets/webApp/steps/step-build-de.png b/website/assets/webApp/steps/step-build-de.png deleted file mode 100644 index ed47c4c8..00000000 Binary files a/website/assets/webApp/steps/step-build-de.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-build-en.png b/website/assets/webApp/steps/step-build-en.png deleted file mode 100644 index a05f706d..00000000 Binary files a/website/assets/webApp/steps/step-build-en.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-build-es.png b/website/assets/webApp/steps/step-build-es.png deleted file mode 100644 index fe470054..00000000 Binary files a/website/assets/webApp/steps/step-build-es.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-build-fr.png b/website/assets/webApp/steps/step-build-fr.png deleted file mode 100644 index 45fd8fda..00000000 Binary files a/website/assets/webApp/steps/step-build-fr.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-build-pt.png b/website/assets/webApp/steps/step-build-pt.png deleted file mode 100644 index e8517514..00000000 Binary files a/website/assets/webApp/steps/step-build-pt.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-code-de.png b/website/assets/webApp/steps/step-code-de.png deleted file mode 100644 index 509f2cd1..00000000 Binary files a/website/assets/webApp/steps/step-code-de.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-code-en.png b/website/assets/webApp/steps/step-code-en.png deleted file mode 100644 index 76c015e9..00000000 Binary files a/website/assets/webApp/steps/step-code-en.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-code-es.png b/website/assets/webApp/steps/step-code-es.png deleted file mode 100644 index 76c015e9..00000000 Binary files a/website/assets/webApp/steps/step-code-es.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-code-fr.png b/website/assets/webApp/steps/step-code-fr.png deleted file mode 100644 index 76c015e9..00000000 Binary files a/website/assets/webApp/steps/step-code-fr.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-code-pt.png b/website/assets/webApp/steps/step-code-pt.png deleted file mode 100644 index 76c015e9..00000000 Binary files a/website/assets/webApp/steps/step-code-pt.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-test-de.png b/website/assets/webApp/steps/step-test-de.png deleted file mode 100644 index e86a2417..00000000 Binary files a/website/assets/webApp/steps/step-test-de.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-test-en.png b/website/assets/webApp/steps/step-test-en.png deleted file mode 100644 index 9dfd8c4e..00000000 Binary files a/website/assets/webApp/steps/step-test-en.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-test-es.png b/website/assets/webApp/steps/step-test-es.png deleted file mode 100644 index 3fced861..00000000 Binary files a/website/assets/webApp/steps/step-test-es.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-test-fr.png b/website/assets/webApp/steps/step-test-fr.png deleted file mode 100644 index 0244e275..00000000 Binary files a/website/assets/webApp/steps/step-test-fr.png and /dev/null differ diff --git a/website/assets/webApp/steps/step-test-pt.png b/website/assets/webApp/steps/step-test-pt.png deleted file mode 100644 index 394e8e8a..00000000 Binary files a/website/assets/webApp/steps/step-test-pt.png and /dev/null differ diff --git a/website/assets/webApp/web-editor-de.png b/website/assets/webApp/web-editor-de.png deleted file mode 100644 index c404e0c3..00000000 Binary files a/website/assets/webApp/web-editor-de.png and /dev/null differ diff --git a/website/assets/webApp/web-editor-en.png b/website/assets/webApp/web-editor-en.png deleted file mode 100644 index 944d6ca7..00000000 Binary files a/website/assets/webApp/web-editor-en.png and /dev/null differ diff --git a/website/assets/webApp/web-learn-de.png b/website/assets/webApp/web-learn-de.png deleted file mode 100644 index 6051cce0..00000000 Binary files a/website/assets/webApp/web-learn-de.png and /dev/null differ diff --git a/website/css/styles.css b/website/css/styles.css deleted file mode 100644 index 35f75baa..00000000 --- a/website/css/styles.css +++ /dev/null @@ -1,1045 +0,0 @@ -/* VisuStruct — Webseite. Hell, ruhig, ein Akzent aus dem App-Icon (#2e8cee → #5cc4fb). */ -:root { - --bg: #ffffff; - --bg-soft: #f4f7fb; - --ink: #0d1726; - --ink-2: #334155; - --muted: #64748b; - --line: #e2e8f0; - --brand: #1f7ae0; - --brand-strong: #145fb8; - --brand-light: #5cc4fb; - --brand-tint: #eaf3fe; - --ok: #16a34a; - --radius: 18px; - --radius-sm: 12px; - --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06), 0 1px 3px rgba(15, 23, 42, 0.08); - --shadow-lg: 0 2px 6px rgba(15, 23, 42, 0.05), 0 24px 60px -12px rgba(15, 40, 80, 0.22); - --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; - color-scheme: light; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html { - scroll-behavior: smooth; - -webkit-text-size-adjust: 100%; -} - -body { - margin: 0; - font-family: var(--font); - font-size: 1rem; - line-height: 1.6; - color: var(--ink-2); - background: var(--bg); - -webkit-font-smoothing: antialiased; -} - -img { - max-width: 100%; - height: auto; - display: block; -} - -a { - color: var(--brand); - text-decoration: none; -} -a:hover { - text-decoration: underline; -} - -h1, -h2, -h3 { - color: var(--ink); - line-height: 1.15; - letter-spacing: -0.025em; - margin: 0; -} - -:focus-visible { - outline: 3px solid rgba(31, 122, 224, 0.45); - outline-offset: 2px; - border-radius: 6px; -} - -.wrap { - width: min(1160px, 100% - 2rem); - margin-inline: auto; -} - -[id] { - scroll-margin-top: 5rem; -} - -/* Kopfzeile */ -.hd { - position: sticky; - top: 0; - z-index: 30; - background: rgba(255, 255, 255, 0.9); - border-bottom: 1px solid var(--line); -} -@supports (backdrop-filter: blur(8px)) { - .hd { - background: rgba(255, 255, 255, 0.78); - backdrop-filter: saturate(1.6) blur(14px); - } -} -.hd-inner { - display: flex; - align-items: center; - gap: 1.5rem; - min-height: 64px; -} -.brand { - display: inline-flex; - align-items: center; - gap: 0.6rem; - color: var(--ink); - font-weight: 750; - font-size: 1.1rem; - letter-spacing: -0.02em; -} -.brand:hover { - text-decoration: none; -} -.brand img { - width: 36px; - height: 36px; - border-radius: 22%; -} -.hd-nav { - display: none; - gap: 1.5rem; - margin-inline-start: 1rem; -} -.hd-nav a { - color: var(--ink-2); - font-size: 0.93rem; - font-weight: 550; -} -.hd-nav a:hover { - color: var(--brand); - text-decoration: none; -} -.hd-end { - margin-inline-start: auto; - display: flex; - align-items: center; - gap: 0.75rem; -} -.langs { - display: flex; - gap: 2px; - padding: 3px; - border-radius: 999px; - background: var(--bg-soft); - border: 1px solid var(--line); -} -.langs a { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 2.1rem; - height: 1.9rem; - padding: 0 0.45rem; - border-radius: 999px; - color: var(--muted); - font-size: 0.75rem; - font-weight: 650; - letter-spacing: 0.02em; -} -.langs a:hover { - color: var(--ink); - text-decoration: none; -} -.langs a[aria-current="page"] { - background: var(--bg); - color: var(--ink); - box-shadow: var(--shadow-sm); -} -.hd-end .hd-cta { - display: none; -} - -/* Knöpfe */ -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - min-height: 44px; - padding: 0.6rem 1.15rem; - border-radius: 999px; - border: 1px solid transparent; - font: inherit; - font-weight: 650; - font-size: 0.97rem; - line-height: 1.2; - text-align: center; - transition: background-color 0.15s, box-shadow 0.15s, transform 0.15s, color 0.15s; -} -.btn:hover { - text-decoration: none; - transform: translateY(-1px); -} -.btn-primary { - color: #fff; - background: var(--brand); - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1), 0 6px 18px -6px rgba(31, 122, 224, 0.6); -} -.btn-primary:hover { - background: var(--brand-strong); -} -.btn-ghost { - color: var(--ink); - background: var(--bg); - border-color: var(--line); - box-shadow: var(--shadow-sm); -} -.btn-ghost:hover { - border-color: #cbd5e1; -} -.btn-light { - color: var(--brand-strong); - background: #fff; -} -.btn-sm { - min-height: 36px; - padding: 0.4rem 0.95rem; - font-size: 0.88rem; -} -.btn-lg { - min-height: 52px; - padding: 0.8rem 1.5rem; - font-size: 1.02rem; -} -.btn-icon { - width: 1.1em; - height: 1.1em; - flex: none; -} -[dir="rtl"] .btn-icon { - transform: scaleX(-1); -} - -/* Startbereich */ -.hero { - position: relative; - overflow: hidden; - padding: 3.5rem 0 0; - background: - radial-gradient(60% 50% at 50% 0%, rgba(92, 196, 251, 0.22), transparent 70%), - radial-gradient(40% 40% at 90% 30%, rgba(31, 122, 224, 0.1), transparent 70%), - var(--bg); -} -.hero::before { - /* feines Raster wie Struktogramm-Kästchen */ - content: ""; - position: absolute; - inset: 0; - background-image: - linear-gradient(to right, rgba(15, 23, 42, 0.05) 1px, transparent 1px), - linear-gradient(to bottom, rgba(15, 23, 42, 0.05) 1px, transparent 1px); - background-size: 44px 44px; - mask-image: radial-gradient(70% 60% at 50% 20%, #000 30%, transparent 75%); - -webkit-mask-image: radial-gradient(70% 60% at 50% 20%, #000 30%, transparent 75%); - pointer-events: none; -} -.hero-text { - position: relative; - text-align: center; - max-width: 820px; -} -.pill { - display: inline-flex; - align-items: center; - gap: 0.5rem; - margin: 0 0 1.4rem; - padding: 0.35rem 0.85rem; - border-radius: 999px; - background: rgba(255, 255, 255, 0.85); - border: 1px solid var(--line); - box-shadow: var(--shadow-sm); - font-size: 0.82rem; - font-weight: 600; - color: var(--ink-2); -} -.pill-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: var(--ok); - box-shadow: 0 0 0 4px rgba(22, 163, 74, 0.15); -} -.hero h1 { - font-size: clamp(2.2rem, 6vw, 4rem); - font-weight: 800; - letter-spacing: -0.04em; - line-height: 1.05; -} -.grad { - display: block; - background: linear-gradient(92deg, var(--brand) 10%, var(--brand-light) 90%); - -webkit-background-clip: text; - background-clip: text; - color: transparent; - padding-bottom: 0.08em; -} -.lead { - margin: 1.25rem auto 0; - max-width: 640px; - font-size: clamp(1.05rem, 1.6vw, 1.2rem); - color: var(--ink-2); -} -.actions { - display: flex; - flex-wrap: wrap; - gap: 0.75rem; - margin-top: 2rem; -} -.hero .actions { - justify-content: center; -} -.note { - margin: 1rem 0 0; - font-size: 0.88rem; - color: var(--muted); -} -.hero-shot { - position: relative; - margin-top: 3rem; - max-width: 1040px; -} -.window { - display: block; - border-radius: 14px 14px 0 0; - overflow: hidden; - background: #fff; - border: 1px solid var(--line); - border-bottom: 0; - box-shadow: var(--shadow-lg); -} -.window-bar { - display: flex; - align-items: center; - gap: 6px; - height: 38px; - padding: 0 14px; - background: var(--bg-soft); - border-bottom: 1px solid var(--line); -} -.window-bar i { - width: 11px; - height: 11px; - border-radius: 50%; - background: #d5dde7; -} -.window-bar i:nth-child(1) { - background: #ff6159; -} -.window-bar i:nth-child(2) { - background: #ffbd2e; -} -.window-bar i:nth-child(3) { - background: #28c941; -} -.window-url { - margin-inline: auto; - padding: 0.15rem 1.2rem; - border-radius: 7px; - background: #fff; - border: 1px solid var(--line); - font-size: 0.75rem; - color: var(--muted); - transform: translateX(-24px); -} - -/* Abschnitte */ -.sec { - padding: 5rem 0; -} -.sec-soft { - background: var(--bg-soft); - border-block: 1px solid var(--line); -} -.sec-head { - max-width: 680px; - margin: 0 auto 3rem; - text-align: center; -} -.sec-head h2, -.split-text h2 { - font-size: clamp(1.75rem, 3.6vw, 2.5rem); - font-weight: 780; -} -.sec-head p:not(.kicker) { - margin: 1rem 0 0; - font-size: 1.08rem; - color: var(--muted); -} -.kicker { - margin: 0 0 0.75rem; - font-size: 0.8rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--brand); -} - -/* Drei Schritte */ -.steps { - list-style: none; - margin: 0; - padding: 0; - display: grid; - gap: 1.25rem; - counter-reset: step; -} -.step { - position: relative; - padding: 1.6rem; - border-radius: var(--radius); - background: var(--bg); - border: 1px solid var(--line); -} -.step-img { - display: block; - width: 100%; - height: auto; - margin-bottom: 1.1rem; - border-radius: 10px; - border: 1px solid var(--line); - background: #fff; -} -.step-num { - display: inline-grid; - place-items: center; - width: 2.4rem; - height: 2.4rem; - margin-bottom: 1rem; - border-radius: 12px; - background: var(--brand-tint); - color: var(--brand); - font-weight: 800; - font-size: 1.05rem; -} -.step h3, -.feature h3 { - font-size: 1.15rem; - font-weight: 700; - letter-spacing: -0.015em; -} -.step p, -.feature p { - margin: 0.5rem 0 0; - color: var(--muted); - font-size: 0.97rem; -} - -/* Funktionen */ -.features { - display: grid; - gap: 1.25rem; -} -.feature { - padding: 1.6rem; - border-radius: var(--radius); - background: var(--bg); - border: 1px solid var(--line); - box-shadow: var(--shadow-sm); -} -.feature-icon { - display: inline-grid; - place-items: center; - width: 2.75rem; - height: 2.75rem; - margin-bottom: 1rem; - border-radius: 12px; - color: #fff; - background: linear-gradient(135deg, var(--brand), var(--brand-light)); - box-shadow: 0 6px 16px -6px rgba(31, 122, 224, 0.6); -} -.icon { - width: 1.4rem; - height: 1.4rem; -} - -/* Lernbereich */ -.visubot-wordmark { - display: block; - width: min(360px, 85%); - height: auto; - margin: 0 0 1rem; -} -.split { - display: grid; - gap: 3rem; - align-items: center; -} -.split-text > p:not(.kicker):not(.note) { - margin: 1rem 0 0; - font-size: 1.08rem; -} -.checks { - list-style: none; - margin: 1.5rem 0 0; - padding: 0; - display: grid; - gap: 0.7rem; -} -.checks li { - display: flex; - gap: 0.7rem; - align-items: flex-start; - color: var(--ink); - font-weight: 520; -} -.check { - flex: none; - width: 1.4rem; - height: 1.4rem; - padding: 3px; - margin-top: 0.1rem; - border-radius: 50%; - color: var(--ok); - background: rgba(22, 163, 74, 0.12); - stroke-width: 2.6; -} -.shot { - margin: 0; - border-radius: var(--radius); - overflow: hidden; - border: 1px solid var(--line); - box-shadow: var(--shadow-lg); - background: #fff; -} - -/* Weitere Versionen */ -.alts { - display: grid; - gap: 1.5rem; -} -.alt { - display: flex; - flex-direction: column; - border-radius: var(--radius); - overflow: hidden; - background: var(--bg); - border: 1px solid var(--line); - box-shadow: var(--shadow-sm); -} -.alt-img { - aspect-ratio: 16 / 10; - padding: 1.5rem 1.5rem 0; - background: linear-gradient(160deg, var(--brand-tint), #f8fbff); - overflow: hidden; -} -.alt-img img { - width: 100%; - height: 100%; - object-fit: cover; - object-position: top left; - border-radius: 10px 10px 0 0; - box-shadow: 0 10px 30px -10px rgba(15, 40, 80, 0.35); -} -.alt-body { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 0.75rem; - padding: 1.6rem; - flex: 1; -} -.alt-body h3 { - display: flex; - align-items: center; - gap: 0.6rem; - font-size: 1.2rem; -} -.alt-icon { - width: 1.5rem; - height: 1.5rem; - color: var(--brand); - flex: none; -} -.alt-body p { - margin: 0; - color: var(--muted); - font-size: 0.97rem; -} -.alt-body > :last-child { - margin-top: auto; -} -.badge { - display: inline-block; - border-radius: 9px; -} -.badge img { - height: 45px; - width: auto; -} - -/* Abschlussband */ -.band { - padding: 3.5rem 1.5rem; - border-radius: 28px; - text-align: center; - color: #e8f3ff; - background: - radial-gradient(60% 80% at 100% 0%, rgba(92, 196, 251, 0.55), transparent 60%), - linear-gradient(135deg, #1f7ae0, #1565c8); - box-shadow: 0 30px 60px -25px rgba(21, 101, 200, 0.6); -} -.band h2 { - color: #fff; - font-size: clamp(1.7rem, 3.6vw, 2.4rem); - font-weight: 780; -} -.band p { - max-width: 620px; - margin: 1rem auto 2rem; - font-size: 1.08rem; -} - -/* Fußzeile */ -.ft { - padding: 4rem 0 2rem; - background: var(--bg-soft); - border-top: 1px solid var(--line); - font-size: 0.9rem; - color: var(--muted); -} -.ft-grid { - display: grid; - gap: 2.5rem; -} -.ft-brand p { - margin: 0.75rem 0 0; - max-width: 280px; -} -.ft-col h2 { - margin-bottom: 0.75rem; - font-size: 0.8rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--ink); -} -.ft-col p { - margin: 0 0 0.6rem; -} -.ft-col strong { - color: var(--ink-2); -} -.ft-col address { - font-style: normal; -} -.ft-col ul { - list-style: none; - margin: 0; - padding: 0; - display: grid; - gap: 0.4rem; -} -.ft-col ul a { - color: var(--ink-2); -} -.ft-bottom { - margin-top: 3rem; - padding-top: 1.5rem; - border-top: 1px solid var(--line); -} -.ft-bottom p { - margin: 0; -} - -/* Angeberwissen */ -.goat-intro { - display: grid; - gap: 2rem; - align-items: center; -} -.goat-full { - width: min(260px, 70%); - margin: 0 auto; -} -.goat-intro h1 { - font-size: clamp(2rem, 4.6vw, 3.2rem); -} -.bubble { - position: relative; - padding: 1.1rem 1.3rem; - border-radius: var(--radius); - background: var(--bg); - border: 1px solid var(--line); - box-shadow: var(--shadow-sm); -} -.bubble h3 { - font-size: 1.1rem; - font-weight: 700; -} -.bubble p { - margin: 0.5rem 0 0; -} -.bubble-lead { - margin-top: 1.5rem; - font-size: 1.08rem; - background: var(--brand-tint); - border-color: transparent; -} -.facts { - list-style: none; - margin: 0 auto; - padding: 0; - max-width: 820px; - display: grid; - gap: 1.25rem; -} -.fact { - display: grid; - grid-template-columns: 56px 1fr; - gap: 0.9rem; - align-items: start; -} -.fact-goat { - width: 56px; - height: 56px; - border-radius: 50%; - border: 2px solid var(--bg); - box-shadow: var(--shadow-sm); -} -.fact .bubble::before { - content: ""; - position: absolute; - top: 20px; - left: -8px; - width: 14px; - height: 14px; - background: var(--bg); - border-left: 1px solid var(--line); - border-bottom: 1px solid var(--line); - transform: rotate(45deg); -} -.src { - font-size: 0.85rem; - color: var(--muted); -} -.people { - display: grid; - gap: 1.5rem; - max-width: 920px; - margin: 0 auto; -} -.person { - display: grid; - gap: 1.25rem; - padding: 1.5rem; - border-radius: var(--radius); - background: var(--bg); - border: 1px solid var(--line); - box-shadow: var(--shadow-sm); -} -.person figure { - margin: 0; -} -.person figure img { - width: 100%; - max-width: 260px; - aspect-ratio: 3 / 4; - object-fit: cover; - border-radius: var(--radius-sm); - background: var(--bg-soft); -} -.person figcaption { - margin-top: 0.5rem; - max-width: 260px; - font-size: 0.78rem; - color: var(--muted); -} -.person h3 { - font-size: 1.3rem; - font-weight: 750; -} -.person-meta { - margin: 0.25rem 0 0.75rem; - font-size: 0.9rem; - color: var(--muted); -} -.person ul { - margin: 0; - padding-left: 1.2rem; - display: grid; - gap: 0.4rem; -} -.hd-nav a[aria-current="page"] { - color: var(--brand); -} - -/* Erste Zeile: die drei Welten VisuStruct, VisuBot, VisuObject nebeneinander */ -.worlds-bar { - border-bottom: 1px solid var(--line); - background: var(--bg-soft); -} -.worlds { - display: flex; - justify-content: safe center; - gap: 0.25rem; - padding-block: 0.3rem; - font-size: 0.85rem; - font-weight: 650; - overflow-x: auto; -} -.worlds a, -.worlds .w-soon { - display: inline-flex; - align-items: center; - gap: 0.45rem; - padding: 0.25rem 0.85rem; - border-radius: 999px; - color: var(--muted); - white-space: nowrap; -} -.worlds a:hover { - color: var(--ink); - background: rgba(15, 23, 42, 0.05); - text-decoration: none; -} -.worlds .w-dot { - width: 0.6rem; - height: 0.6rem; - border-radius: 50%; - background: var(--w-color); -} -.w-struct { --w-color: #1f7ae0; } -.w-bot { --w-color: #d96a00; } -.w-object { --w-color: #0e7c86; } -.w-botpp { --w-color: #6d4fc2; } -.w-mic { --w-color: #2f9e44; } -.worlds .w-soon { - color: var(--muted); - opacity: 0.75; - cursor: default; -} -.worlds a[aria-current="page"] { - color: #fff; - background: var(--w-color); -} -.worlds a[aria-current="page"] .w-dot { - background: #fff; -} -.w-new { - padding: 0 0.4rem; - border-radius: 999px; - font-size: 0.7rem; - color: #fff; - background: var(--w-color); -} - -/* Abschnitt VisuObject auf der VisuStruct-Startseite: Petrol wie die Welt VisuObject */ -.sec-visuobject { - --brand: #0e7c86; - --brand-strong: #0a5f67; - --brand-tint: #e3f1f3; - background: #edf4f6; -} -.sec-visuobject .btn-primary { - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1), 0 6px 18px -6px rgba(14, 124, 134, 0.6); -} - -/* VisuObject-Startseite: Petrol wie die Welt VisuObject, Aufbau wie VisuStruct */ -.visuobject { - --bg-soft: #edf4f6; - --ink: #17292e; - --ink-2: #2c4247; - --muted: #566a70; - --line: #cddde1; - --brand: #0e7c86; - --brand-strong: #0a5f67; - --brand-light: #3fb3bd; - --brand-tint: #e3f1f3; -} -.visuobject :focus-visible { - outline-color: rgba(14, 124, 134, 0.45); -} -.visuobject .btn-primary { - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1), 0 6px 18px -6px rgba(14, 124, 134, 0.6); -} -.visuobject .btn-ghost:hover { - border-color: #b5ccd1; -} -.visuobject .hero { - background: - radial-gradient(60% 50% at 50% 0%, rgba(63, 179, 189, 0.2), transparent 70%), - radial-gradient(40% 40% at 90% 30%, rgba(14, 124, 134, 0.12), transparent 70%), - var(--bg); -} -.visuobject .feature-icon { - box-shadow: 0 6px 16px -6px rgba(14, 124, 134, 0.6); -} -.visuobject .band { - color: #e3f1f3; - background: - radial-gradient(60% 80% at 100% 0%, rgba(63, 179, 189, 0.45), transparent 60%), - linear-gradient(135deg, #17292e, #0e5b63); - box-shadow: 0 30px 60px -25px rgba(23, 41, 46, 0.6); -} -.visuobject .btn-light { - color: var(--ink); -} -.brand-obj { - color: var(--brand); -} - -/* VisuBot-Startseite: Farben aus dem VisuBot-Logo (Navy, Orange, Lila), Aufbau wie VisuStruct */ -.visubot { - --bg-soft: #f4f3fa; - --ink: #232846; - --ink-2: #3a3f5e; - --muted: #6a6890; - --line: #e3e0f0; - --brand: #d96a00; - --brand-strong: #b35700; - --brand-light: #fda115; - --brand-tint: #fff1df; -} -.visubot :focus-visible { - outline-color: rgba(217, 106, 0, 0.45); -} -.visubot .btn-primary { - box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1), 0 6px 18px -6px rgba(217, 106, 0, 0.6); -} -.visubot .btn-ghost:hover { - border-color: #cfcae6; -} -.visubot .hero { - background: - radial-gradient(60% 50% at 50% 0%, rgba(253, 161, 21, 0.2), transparent 70%), - radial-gradient(40% 40% at 90% 30%, rgba(139, 124, 214, 0.16), transparent 70%), - var(--bg); -} -.visubot .feature-icon { - box-shadow: 0 6px 16px -6px rgba(217, 106, 0, 0.6); -} -.visubot .band { - color: #e9e7f7; - background: - radial-gradient(60% 80% at 100% 0%, rgba(253, 161, 21, 0.45), transparent 60%), - linear-gradient(135deg, #2f3454, #4a4f80); - box-shadow: 0 30px 60px -25px rgba(47, 52, 84, 0.6); -} -.visubot .btn-light { - color: var(--ink); -} -.brand-bot { - color: var(--brand); -} -.visubot .brand img { - width: 55px; - height: 32px; - border-radius: 0; -} -.hero-wordmark { - width: min(320px, 80%); - margin: 0 auto 1.25rem; -} -.world-mark { - display: block; - width: 64px; - height: 64px; - margin: 0 0 1rem; - border-radius: 22%; -} - -/* Größere Bildschirme */ -@media (min-width: 640px) { - .steps { - grid-template-columns: repeat(3, 1fr); - } - .features { - grid-template-columns: repeat(2, 1fr); - } - .alts { - grid-template-columns: repeat(2, 1fr); - } - .person { - grid-template-columns: 220px 1fr; - gap: 2rem; - padding: 2rem; - } - .hd-end .hd-cta { - display: inline-flex; - } -} - -@media (min-width: 900px) { - .hero { - padding-top: 5rem; - } - .hd-nav { - display: flex; - } - .features { - grid-template-columns: repeat(3, 1fr); - } - .split { - grid-template-columns: 5fr 6fr; - gap: 4rem; - } - .goat-intro { - grid-template-columns: 300px 1fr; - gap: 3rem; - } - .goat-full { - width: 100%; - } - .sec { - padding: 6.5rem 0; - } - .ft-grid { - grid-template-columns: 1.1fr 1.6fr 0.9fr 0.8fr; - } -} - -@media (max-width: 520px) { - .worlds { - gap: 0; - font-size: 0.8rem; - } - .worlds a { - padding: 0.25rem 0.55rem; - gap: 0.35rem; - } - /* Auf dem Handy nur die Welten, die es schon gibt */ - .worlds .w-soon { - display: none; - } - .worlds .w-new { - display: none; - } - .hero .btn-lg { - width: 100%; - } - .window-url { - display: none; - } -} - -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; - } - .btn { - transition: none; - } - .btn:hover { - transform: none; - } -} diff --git a/website/de/index.html b/website/de/index.html deleted file mode 100644 index f971b7db..00000000 --- a/website/de/index.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - VisuStruct — Struktogramme im Browser - - - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Kostenlos · ohne Anmeldung · ohne Installation

    -

    Struktogramme bauen, testen und verstehen. Direkt im Browser.

    -

    Mit VisuStruct entwickelst du Algorithmen als Struktogramm nach Nassi-Shneiderman: Elemente hineinziehen, Schritt für Schritt ablaufen lassen und als Java-, Python- oder JavaScript-Code ausgeben.

    - -

    Läuft auf Computer, Tablet und Handy. Deine Dateien bleiben auf deinem Gerät.

    -
    - -
    - -
    -
    -
    -

    So funktioniert es

    -

    In drei Schritten vom Problem zum Programm

    -
    -
      -
    1. - Struktogramm wird aus den Elementen links aufgebaut - -

      Bauen

      -

      Zieh die Bausteine ins Struktogramm und beschreibe jeden Schritt in einfachen Worten oder in Code.

      -
    2. -
    3. - Simulation zeigt den aktuellen Schritt, die Variablen und die Ausgabe - -

      Testen

      -

      Starte die Simulation und verfolge Block für Block, wie sich die Variablen verändern.

      -
    4. -
    5. - Aus dem Struktogramm erzeugter Java-Code - -

      Übersetzen

      -

      Lass dir Java, Python oder JavaScript erzeugen und arbeite in deiner Entwicklungsumgebung weiter.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funktionen

    -

    Alles, was du für Struktogramme brauchst

    -

    Ein Editor für den Unterricht: übersichtlich, genau und ohne Ablenkung.

    -
    -
    -
    - -

    Bauen per Ziehen

    -

    Anweisung, Verzweigung, Schleifen und Fallauswahl aus dem Bereich „Elemente“ ins Diagramm ziehen und den Text direkt eintippen.

    -
    -
    - -

    Schritt für Schritt testen

    -

    Die Simulation zeigt den aktiven Block, die Variablen und die Ausgabe. So siehst du sofort, ob dein Algorithmus stimmt.

    -
    -
    - -

    Struktogramm und Java live

    -

    Tippst du Java-Code, baut sich das Struktogramm live mit auf – und umgekehrt. Dazu übersetzt VisuStruct nach Python und JavaScript.

    -
    -
    - -

    Exportieren und drucken

    -

    Als PNG, SVG oder PDF speichern oder direkt ausdrucken, zum Beispiel für Arbeitsblätter und Klausuren.

    -
    -
    - -

    Lektionen in drei Sprachen

    -

    Oberfläche und Lektionen gibt es auf Deutsch, Englisch und Portugiesisch.

    -
    -
    - -

    Deine Daten bleiben bei dir

    -

    Kein Konto, kein Hochladen. Gespeichert wird als .visustruct-Datei auf deinem Gerät.

    -
    -
    -
    -
    - -
    -
    -
    -

    Lernbereich

    -

    Struktogramme lernen, Schritt für Schritt

    -

    Neben dem Editor wartet ein Lernbereich: Lektionen erklären, wie du den Editor bedienst und was hinter Sequenz, Verzweigung und Schleife steckt. Was du liest, probierst du gleich daneben aus.

    -
      -
    • Lektionen in kurzen Schritten, mit Diagrammen zum Öffnen
    • -
    • Übungen, die VisuStruct automatisch prüft
    • -
    • Aufgaben mit Musterlösung zum Vergleichen
    • -
    • Checkliste und Quiz am Ende jeder Lektion
    • -
    - -

    Dein Fortschritt bleibt in deinem Browser gespeichert. Die Lektionen gibt es auf Deutsch, Englisch und Portugiesisch.

    -
    -
    - Der Lernbereich neben dem Editor mit Lektion 1 -
    -
    -
    - -
    -
    -
    - VisuBot -

    Neu: VisuBot

    -

    Struktogramme, die einen echten Roboter fahren lassen

    -

    Mit VisuBot programmierst du den Lernroboter mBot2 von Makeblock, als Struktogramm oder in Java. Ohne Roboter fährt er in einem 3D-Simulator. Ist der echte mBot2 per USB-Kabel verbunden, fährt er jeden Schritt mit.

    -
      -
    • Eigene Lektionen zu Motoren, Sensoren, LEDs, Ton und Bildschirm
    • -
    • Übungen und Projekte, die im Simulator automatisch geprüft werden
    • -
    • Echter mBot2 per USB-Kabel in Chrome oder Edge, ohne Installation
    • -
    • Beim Start „VisuBot“ wählen, umschalten im Zahnrad
    • -
    - -

    Den mBot2 brauchst du nur für den echten Roboter. Der Simulator läuft überall.

    -
    -
    - VisuBot mit dem 3D-Modell des mBot2 in Lektion 1 -
    -
    -
    - -
    -
    -
    -

    Neu: VisuObject

    -

    Objektorientiert programmieren: Klassendiagramm und Java zusammen

    -

    VisuObject ist die Welt für die Oberstufe. Du schreibst Java-Klassen in einem Projekt mit Packages, wie in Eclipse, und das UML-Klassendiagramm zeichnet sich daneben mit. Änderst du das Diagramm, ändert sich der Code, und umgekehrt.

    -
      -
    • Klassen, Interfaces, Assoziation, Aggregation, Komposition und Vererbung
    • -
    • Projekt mit src-Ordner und Packages, gespeichert in deinem Workspace-Ordner
    • -
    • Programm mit Start ausführen, die Ausgabe erscheint in der Konsole
    • -
    • Beim Start „VisuObject“ wählen, umschalten im Zahnrad
    • -
    - -

    Die Grundlagen lernst du in VisuStruct, danach geht es in VisuObject mit Klassen weiter.

    -
    -
    - VisuObject mit Java-Code und dem passenden Klassendiagramm -
    -
    -
    - -
    -
    -
    -

    Weitere Versionen

    -

    Lieber als App?

    -

    Alle Versionen nutzen dasselbe .visustruct-Format. Du kannst im Browser anfangen und in der App weitermachen, oder umgekehrt.

    -
    -
    -
    -
    Die App auf dem Mac
    -
    -

    Mac, iPad und iPhone

    -

    Die native SwiftUI-App aus dem App Store, auch ohne Internet nutzbar.

    - Laden im App Store -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Erst der Algorithmus, dann der Code

    -

    Denk zuerst über Logik und Ablauf nach. Danach übersetzt du dieselbe Struktur in eine Programmiersprache deiner Wahl, Schritt für Schritt und gut nachvollziehbar.

    - Jetzt ausprobieren -
    -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Struktogramme nach Nassi-Shneiderman für Schule und Studium.

    -
    -
    -

    Datenschutzerklärung – Nutzung von VisuStruct

    -

    Datenschutzkonform (DSGVO): VisuStruct erfasst und speichert keine personenbezogenen Daten. Ihre Struktogramme und .visustruct-Dateien bleiben unter Ihrer Kontrolle — Sie entscheiden, ob Sie sie nur auf dem Gerät oder z. B. in Ihrer eigenen Cloud sichern.

    -

    Nutzen Sie verlinkte Angebote zur App (z. B. App Store, GitHub), gelten die Datenschutzhinweise und Bedingungen der jeweiligen Anbieter; dort werden Daten ggf. von diesen Dritten verarbeitet. Diese Webseite wird auf einem Server bereitgestellt; dabei fallen wie üblich technische Server-Logdaten an.

    -
    -
    -

    Kontakt

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct

    -
    - - diff --git a/website/de/visubot.html b/website/de/visubot.html deleted file mode 100644 index 34a01526..00000000 --- a/website/de/visubot.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - VisuBot — den mBot2 mit Struktogrammen programmieren - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    - VisuBot -

    Kostenlos · ohne Anmeldung · mit oder ohne Roboter

    -

    Den mBot2 programmieren. Mit Struktogrammen und Java.

    -

    Mit VisuBot baust du Programme für den Lernroboter mBot2 als Struktogramm oder schreibst sie in Java. Im 3D-Simulator siehst du sofort, wohin er fährt. Ist der echte mBot2 per USB-Kabel verbunden, fährt er mit.

    - -

    Der Simulator läuft in jedem Browser. Für den echten mBot2 brauchst du Chrome oder Edge und ein USB-Kabel.

    -
    - -
    - -
    -
    -
    -

    So funktioniert es

    -

    In drei Schritten vom Struktogramm zum fahrenden Roboter

    -
    -
      -
    1. - Die mBot2-Befehle im Bereich „Elemente“ und ein Struktogramm mit zwei For-Schleifen - -

      Bauen

      -

      Zieh mBot2-Befehle wie „fahre vor“ und „drehe rechts“ ins Struktogramm oder tippe sie in Java.

      -
    2. -
    3. - Der mBot2 im 3D-Simulator mit seiner orangen Fahrspur - -

      Testen

      -

      Im 3D-Simulator siehst du sofort, welchen Weg dein Programm fährt. Ganz ohne Roboter.

      -
    4. -
    5. - Das 3D-Modell des mBot2 und der Knopf mBot2 zum Verbinden - -

      Fahren

      -

      Verbinde den echten mBot2 mit dem USB-Kabel. Er fährt dein Programm Schritt für Schritt mit.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funktionen

    -

    Alles, was dein mBot2 kann

    -

    Motoren, Sensoren, Licht und Ton: jeder Teil des Roboters hat eigene Befehle.

    -
    -
    -
    - -

    Fahren und drehen

    -

    Vorwärts, rückwärts, drehen oder jedes Rad einzeln steuern, mit eigener Geschwindigkeit.

    -
    -
    - -

    Sensoren abfragen

    -

    Abstand, Linie, Farben, Licht, Lautstärke und Neigung messen und darauf reagieren.

    -
    -
    - -

    Licht, Ton und Bildschirm

    -

    Die fünf LEDs leuchten lassen, Melodien spielen und Text oder Gefühle auf dem Bildschirm zeigen.

    -
    -
    - -

    mBot2 live

    -

    Nach jeder Änderung rechnet VisuBot dein Programm durch und zeigt die Fahrt im 3D-Simulator.

    -
    -
    - -

    Struktogramm und Java live

    -

    Tippst du Java-Code, baut sich das Struktogramm mit auf, und umgekehrt.

    -
    -
    - -

    Echter Roboter ohne Installation

    -

    Der mBot2 verbindet sich per USB-Kabel direkt mit Chrome oder Edge. Kein Programm, kein Konto.

    -
    -
    -
    -
    - -
    -
    -
    -

    Lernbereich

    -

    Robotik lernen, Schritt für Schritt

    -

    Eine durchgehende Reihe: Alle VisuStruct-Lektionen, und dazwischen 22 Roboter-Lektionen vom ersten Quadrat bis zu eigenen Methoden. Jede Lektion probierst du gleich daneben aus, im Simulator oder mit dem echten mBot2.

    -
      -
    • Lektion 1: eine 3D-Tour durch alle Teile des mBot2
    • -
    • Übungen, die VisuBot im Simulator automatisch prüft
    • -
    • Projekte wie Linienfolger, Alarmanlage und Einparken
    • -
    • Nachschlagen: alle mBot2-Befehle mit Beispielen
    • -
    - -

    Dein Fortschritt bleibt auf deinem Gerät. Die VisuBot-Lektionen gibt es auf Deutsch und Englisch.

    -
    -
    - VisuBot mit dem 3D-Modell des mBot2 in Lektion 1 -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Ohne Roboter

    -

    VisuStruct: Struktogramme für alle Programme

    -

    VisuBot ist ein Teil von VisuStruct. Mit VisuStruct baust du Struktogramme ohne Roboter, testest sie Schritt für Schritt und übersetzt sie in Java, Python oder JavaScript. Wechseln kannst du jederzeit im Zahnrad.

    -
      -
    • Eigene Lektionen und Aufgaben zu Struktogrammen
    • -
    • Simulation mit Variablen und Ausgabe
    • -
    • Auch als App für Mac, iPad, iPhone und als Java-App
    • -
    - -
    -
    - Der Lernbereich von VisuStruct neben dem Editor -
    -
    -
    - -
    -
    -
    -

    Erst planen, dann fahren

    -

    Plane den Weg als Struktogramm, prüf ihn im Simulator und lass dann den echten Roboter fahren.

    - Jetzt ausprobieren -
    -
    -
    -
    - -
    -
    -
    - VisuBot -

    Den mBot2 mit Struktogrammen und Java programmieren.

    -
    -
    -

    Datenschutzerklärung – Nutzung von VisuStruct

    -

    Datenschutzkonform (DSGVO): VisuStruct erfasst und speichert keine personenbezogenen Daten. Ihre Struktogramme und .visustruct-Dateien bleiben unter Ihrer Kontrolle — Sie entscheiden, ob Sie sie nur auf dem Gerät oder z. B. in Ihrer eigenen Cloud sichern.

    -

    Nutzen Sie verlinkte Angebote zur App (z. B. App Store, GitHub), gelten die Datenschutzhinweise und Bedingungen der jeweiligen Anbieter; dort werden Daten ggf. von diesen Dritten verarbeitet. Diese Webseite wird auf einem Server bereitgestellt; dabei fallen wie üblich technische Server-Logdaten an.

    -
    -
    -

    Kontakt

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot

    -
    - - diff --git a/website/de/visuobject.html b/website/de/visuobject.html deleted file mode 100644 index c8fa6303..00000000 --- a/website/de/visuobject.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - VisuObject — objektorientiert programmieren mit Klassendiagramm und Java - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Kostenlos · ohne Anmeldung · für die Oberstufe

    -

    Objektorientiert programmieren. Mit Klassendiagramm und Java.

    -

    Mit VisuObject schreibst du Java-Klassen in einem Projekt wie in Eclipse, und das UML-Klassendiagramm zeichnet sich daneben mit. Änderst du das Diagramm, ändert sich der Code. Danach legst du Objekte an, führst das Programm aus und schaust ihm im Debugger zu.

    - -

    Läuft im Browser auf Computer und Tablet. Deine Projekte bleiben in deinem Workspace-Ordner.

    -
    - -
    - -
    -
    -
    -

    So funktioniert es

    -

    In drei Schritten von der Klasse zum laufenden Programm

    -
    -
      -
    1. - Java-Code links, das passende Klassendiagramm rechts - -

      Zeichnen

      -

      Schreib Klassen in Java oder zeichne sie im Klassendiagramm. Beides bleibt immer gleich.

      -
    2. -
    3. - Objektdiagramm mit einem Besitzer und zwei Hunden - -

      Objekte anlegen

      -

      Im Objektdiagramm legst du Objekte an, gibst ihnen Werte und verbindest sie.

      -
    4. -
    5. - Debugger hält in Zeile 11 an, das Objektdiagramm zeigt Team und Spieler - -

      Ausführen und debuggen

      -

      Start führt dein Projekt mit echtem Java aus. Im Debugger gehst du Schritt für Schritt und siehst die Objekte live.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funktionen

    -

    Alles für den Unterricht in OOP

    -

    Klassendiagramm, Objekte und Java an einem Ort, nah an Eclipse und Astah.

    -
    -
    -
    - -

    Klassendiagramm wie in Astah

    -

    Klassen, Interfaces, Assoziation, Aggregation, Komposition, Vererbung und Realisierung zeichnen, dazu Notizen.

    -
    -
    - -

    Projekte wie in Eclipse

    -

    Package Explorer mit Projekt, src-Ordner und Packages, Neu-Menü für Klassen und Interfaces.

    -
    -
    - -

    Objektinspektor

    -

    Klasse, Attribute und Methoden in Tabellen bearbeiten. Konstruktor, Getter, Setter und toString() erzeugen.

    -
    -
    - -

    Objektdiagramm

    -

    Objekte mit Namen und Werten anlegen und sehen, welches Objekt auf welches zeigt.

    -
    -
    - -

    Echtes Java

    -

    Start übersetzt alle Klassen mit echtem Java. Die Ausgabe erscheint in der Konsole, auch Swing-Fenster.

    -
    -
    - -

    Debugger

    -

    Haltepunkte setzen, Schritt, Hinein und Heraus. Aufrufstapel und Variablen immer im Blick.

    -
    -
    -
    -
    - -
    -
    -
    -

    Objektinspektor

    -

    Klassen bearbeiten, ohne jede Zeile zu tippen

    -

    Klick im Diagramm auf eine Klasse. Im Objektinspektor änderst du Name, Oberklasse und Interfaces, legst Attribute und Methoden an und lässt dir Konstruktor, Getter und Setter erzeugen. VisuObject schreibt den Java-Code genau an die richtige Stelle.

    -
      -
    • Sichtbarkeit, Typ, static, final und Startwert für jedes Attribut
    • -
    • Methoden mit Parametern, Rückgabetyp und abstract
    • -
    • Vorschläge nach dem Punkt für deine eigenen Klassen
    • -
    • Abhängigkeiten «create» und «use» erkennt VisuObject selbst
    • -
    - -
    -
    - Objektinspektor mit den Attributen der Klasse Tier -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Vorher

    -

    VisuStruct: erst die Grundlagen

    -

    Bevor es mit Klassen losgeht, lernst du in VisuStruct Variablen, Verzweigungen, Schleifen und Methoden mit Struktogrammen. VisuObject baut darauf auf. Wechseln kannst du jederzeit im Zahnrad.

    -
      -
    • Lektionen und Aufgaben zu Struktogrammen
    • -
    • Simulation mit Variablen und Ausgabe
    • -
    • Java, Python und JavaScript aus dem Struktogramm
    • -
    - -
    -
    - Der Lernbereich von VisuStruct neben dem Editor -
    -
    -
    - -
    -
    -
    -

    Erst modellieren, dann programmieren

    -

    Plane deine Klassen im Diagramm, schreib den Code dazu und sieh im Objektdiagramm, was beim Ausführen passiert.

    - Jetzt ausprobieren -
    -
    -
    -
    - -
    -
    -
    - VisuObject -

    Objektorientiert programmieren mit Klassendiagramm und Java.

    -
    -
    -

    Datenschutzerklärung – Nutzung von VisuStruct

    -

    Datenschutzkonform (DSGVO): VisuStruct erfasst und speichert keine personenbezogenen Daten. Ihre Struktogramme und .visustruct-Dateien bleiben unter Ihrer Kontrolle — Sie entscheiden, ob Sie sie nur auf dem Gerät oder z. B. in Ihrer eigenen Cloud sichern.

    -

    Nutzen Sie verlinkte Angebote zur App (z. B. App Store, GitHub), gelten die Datenschutzhinweise und Bedingungen der jeweiligen Anbieter; dort werden Daten ggf. von diesen Dritten verarbeitet. Diese Webseite wird auf einem Server bereitgestellt; dabei fallen wie üblich technische Server-Logdaten an.

    -
    -
    -

    Kontakt

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot · VisuObject

    -
    - - diff --git a/website/en/index.html b/website/en/index.html deleted file mode 100644 index 5431e589..00000000 --- a/website/en/index.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - VisuStruct — Structograms in the browser - - - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Free · no sign-up · nothing to install

    -

    Build, test and understand structograms. Right in your browser.

    -

    With VisuStruct you design algorithms as Nassi-Shneiderman structograms: drag elements in, run them step by step and turn them into Java, Python or JavaScript code.

    - -

    Works on computers, tablets and phones. Your files stay on your device.

    -
    - -
    - -
    -
    -
    -

    How it works

    -

    From problem to program in three steps

    -
    -
      -
    1. - Building a diagram from the elements on the left - -

      Build

      -

      Drag the building blocks into the structogram and describe each step in plain words or in code.

      -
    2. -
    3. - The simulation shows the current step, the variables and the output - -

      Test

      -

      Start the simulator and follow block by block how the variables change.

      -
    4. -
    5. - Java code generated from the diagram - -

      Translate

      -

      Generate Java, Python or JavaScript and continue in your development environment.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Features

    -

    Everything you need for structograms

    -

    An editor made for the classroom: clear, precise and free of distractions.

    -
    -
    -
    - -

    Build by dragging

    -

    Drag statements, branches, loops and switches from the palette into the diagram and type the text right in.

    -
    -
    - -

    Test step by step

    -

    The simulator highlights the active block and shows variables and output, so you see at once whether your algorithm works.

    -
    -
    - -

    Structogram and Java, live

    -

    Type Java code and the structogram builds itself live – and the other way round. VisuStruct also translates to Python and JavaScript.

    -
    -
    - -

    Export and print

    -

    Save as PNG, SVG or PDF, or print directly, for worksheets and exams.

    -
    -
    - -

    Lessons in three languages

    -

    The interface and the lessons are available in German, English and Portuguese.

    -
    -
    - -

    Your data stays with you

    -

    No account, no uploads. Your work is saved as a .visustruct file on your device.

    -
    -
    -
    -
    - -
    -
    -
    -

    Learning area

    -

    Learn structograms step by step

    -

    Next to the editor there is a learning area: lessons explain how to use the editor and what sequence, branches and loops are about. You try out what you read right next to it.

    -
      -
    • Lessons in short steps, with diagrams to open
    • -
    • Exercises that VisuStruct checks automatically
    • -
    • Tasks with a sample solution to compare
    • -
    • A checklist and a quiz at the end of each lesson
    • -
    - -

    Your progress stays in your browser. The lessons are available in German, English and Portuguese.

    -
    -
    - The learning area next to the editor showing lesson 1 -
    -
    -
    - -
    -
    -
    - VisuBot -

    New: VisuBot

    -

    Structograms that drive a real robot

    -

    With VisuBot you program the mBot2 learning robot by Makeblock, as a structogram or in Java. Without a robot it drives in a 3D simulator. Connect the real mBot2 with a USB cable and it drives every step along.

    -
      -
    • Dedicated lessons on motors, sensors, LEDs, sound and the screen
    • -
    • Exercises and projects checked automatically in the simulator
    • -
    • Real mBot2 over USB in Chrome or Edge, nothing to install
    • -
    • Choose “VisuBot” at the start, switch in the gear menu
    • -
    - -

    You only need an mBot2 for the real robot. The simulator runs everywhere.

    -
    -
    - VisuBot with the 3D model of the mBot2 in lesson 1 -
    -
    -
    - -
    -
    -
    -

    New: VisuObject

    -

    Object-oriented programming: class diagram and Java together

    -

    VisuObject is the world for advanced classes. You write Java classes in a project with packages, just like in Eclipse, and the UML class diagram draws itself alongside. Change the diagram and the code changes, and the other way round.

    -
      -
    • Classes, interfaces, association, aggregation, composition and inheritance
    • -
    • Project with a src folder and packages, saved in your workspace folder
    • -
    • Run the program with Run, the output appears in the console
    • -
    • Choose “VisuObject” at the start, switch in the gear menu
    • -
    - -

    Learn the basics in VisuStruct, then continue with classes in VisuObject.

    -
    -
    - VisuObject with Java code and the matching class diagram -
    -
    -
    - -
    -
    -
    -

    More versions

    -

    Prefer an app?

    -

    All versions share the same .visustruct format. Start in the browser and continue in the app, or the other way around.

    -
    -
    -
    -
    The app on a Mac
    -
    -

    Mac, iPad and iPhone

    -

    The native SwiftUI app from the App Store, also works offline.

    - Download on the App Store -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Algorithm first, then code

    -

    Think about logic and flow first. Then translate the same structure into the programming language of your choice, step by step and easy to follow.

    - Try it now -
    -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Nassi-Shneiderman structograms for school and university.

    -
    -
    -

    Privacy policy – use of VisuStruct

    -

    Privacy-friendly (GDPR): VisuStruct does not collect or store personal data. Your structograms and .visustruct files stay under your control — you decide whether to keep them on your device or back them up (for example) in your own cloud.

    -

    If you use linked services related to the app (e.g. App Store, GitHub), the privacy terms of those providers apply; they may process data there. This website is served from a server; as usual, technical server log data is generated.

    -
    -
    -

    Contact

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct

    -
    - - diff --git a/website/en/visubot.html b/website/en/visubot.html deleted file mode 100644 index e4af2e8b..00000000 --- a/website/en/visubot.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - VisuBot — program the mBot2 with structograms - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    - VisuBot -

    Free · no sign-up · with or without a robot

    -

    Program the mBot2. With structograms and Java.

    -

    With VisuBot you build programs for the mBot2 learning robot as a structogram or write them in Java. The 3D simulator shows right away where it drives. Connect the real mBot2 with a USB cable and it drives along.

    - -

    The simulator runs in any browser. For the real mBot2 you need Chrome or Edge and a USB cable.

    -
    - -
    - -
    -
    -
    -

    How it works

    -

    From structogram to moving robot in three steps

    -
    -
      -
    1. - The mBot2 commands in the Elements area and a structogram with two for loops - -

      Build

      -

      Drag mBot2 commands like “move forward” and “turn right” into the structogram, or type them in Java.

      -
    2. -
    3. - The mBot2 in the 3D simulator with its orange track - -

      Test

      -

      The 3D simulator shows right away which path your program drives. No robot needed.

      -
    4. -
    5. - The 3D model of the mBot2 and the mBot2 button for connecting - -

      Drive

      -

      Connect the real mBot2 with the USB cable. It drives your program step by step.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Features

    -

    Everything your mBot2 can do

    -

    Motors, sensors, light and sound: every part of the robot has its own commands.

    -
    -
    -
    - -

    Drive and turn

    -

    Forward, backward, turning or each wheel on its own, at your own speed.

    -
    -
    - -

    Read the sensors

    -

    Measure distance, line, colours, light, loudness and tilt, and react to them.

    -
    -
    - -

    Light, sound and screen

    -

    Light up the five LEDs, play melodies and show text or emotions on the screen.

    -
    -
    - -

    mBot2 live

    -

    After every change VisuBot runs your program and shows the drive in the 3D simulator.

    -
    -
    - -

    Structogram and Java live

    -

    Type Java code and the structogram builds itself alongside, and the other way round.

    -
    -
    - -

    Real robot, nothing to install

    -

    The mBot2 connects over a USB cable straight to Chrome or Edge. No program, no account.

    -
    -
    -
    -
    - -
    -
    -
    -

    Learning area

    -

    Learn robotics, step by step

    -

    One continuous series: all VisuStruct lessons with 22 robot lessons in between, from the first square to your own methods. You try each lesson right next to it, in the simulator or with the real mBot2.

    -
      -
    • Lesson 1: a 3D tour of every part of the mBot2
    • -
    • Exercises that VisuBot checks automatically in the simulator
    • -
    • Projects such as a line follower, an alarm system and parking
    • -
    • Reference: every mBot2 command with examples
    • -
    - -

    Your progress stays on your device. The VisuBot lessons are available in English and German.

    -
    -
    - VisuBot with the 3D model of the mBot2 in lesson 1 -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Without a robot

    -

    VisuStruct: structograms for any program

    -

    VisuBot is part of VisuStruct. With VisuStruct you build structograms without a robot, test them step by step and turn them into Java, Python or JavaScript. You can switch at any time in the gear menu.

    -
      -
    • Dedicated lessons and tasks on structograms
    • -
    • Simulation with variables and output
    • -
    • Also as an app for Mac, iPad, iPhone and as a Java app
    • -
    - -
    -
    - The VisuStruct learning area next to the editor -
    -
    -
    - -
    -
    -
    -

    Plan first, then drive

    -

    Plan the route as a structogram, check it in the simulator and then let the real robot drive.

    - Try it now -
    -
    -
    -
    - -
    -
    -
    - VisuBot -

    Program the mBot2 with structograms and Java.

    -
    -
    -

    Privacy policy – use of VisuStruct

    -

    Privacy-friendly (GDPR): VisuStruct does not collect or store personal data. Your structograms and .visustruct files stay under your control — you decide whether to keep them on your device or back them up (for example) in your own cloud.

    -

    If you use linked services related to the app (e.g. App Store, GitHub), the privacy terms of those providers apply; they may process data there. This website is served from a server; as usual, technical server log data is generated.

    -
    -
    -

    Contact

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot

    -
    - - diff --git a/website/en/visuobject.html b/website/en/visuobject.html deleted file mode 100644 index 3d6e9500..00000000 --- a/website/en/visuobject.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - VisuObject — object-oriented programming with class diagrams and Java - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Free · no sign-up · for advanced classes

    -

    Object-oriented programming. With class diagrams and Java.

    -

    With VisuObject you write Java classes in a project like in Eclipse, and the UML class diagram draws itself alongside. Change the diagram and the code changes. Then create objects, run the program and watch it in the debugger.

    - -

    Runs in the browser on computers and tablets. Your projects stay in your workspace folder.

    -
    - -
    - -
    -
    -
    -

    How it works

    -

    Three steps from a class to a running program

    -
    -
      -
    1. - Java code on the left, the matching class diagram on the right - -

      Draw

      -

      Write classes in Java or draw them in the class diagram. Both always stay the same.

      -
    2. -
    3. - Object diagram with an owner and two dogs - -

      Create objects

      -

      In the object diagram you create objects, give them values and connect them.

      -
    4. -
    5. - The debugger stops at line 11, the object diagram shows the team and its players - -

      Run and debug

      -

      Run executes your project with real Java. In the debugger you go step by step and see the objects live.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Features

    -

    Everything for teaching OOP

    -

    Class diagram, objects and Java in one place, close to Eclipse and Astah.

    -
    -
    -
    - -

    Class diagram like Astah

    -

    Draw classes, interfaces, association, aggregation, composition, inheritance and realization, plus notes.

    -
    -
    - -

    Projects like Eclipse

    -

    Package Explorer with project, src folder and packages, a New menu for classes and interfaces.

    -
    -
    - -

    Object inspector

    -

    Edit class, attributes and methods in tables. Generate a constructor, getters, setters and toString().

    -
    -
    - -

    Object diagram

    -

    Create objects with names and values and see which object points to which.

    -
    -
    - -

    Real Java

    -

    Run compiles all classes with real Java. Output appears in the console, Swing windows too.

    -
    -
    - -

    Debugger

    -

    Set breakpoints, step over, into and out. Call stack and variables always in view.

    -
    -
    -
    -
    - -
    -
    -
    -

    Object inspector

    -

    Edit classes without typing every line

    -

    Click a class in the diagram. In the object inspector you change its name, superclass and interfaces, add attributes and methods and generate a constructor, getters and setters. VisuObject writes the Java code exactly where it belongs.

    -
      -
    • Visibility, type, static, final and initial value for every attribute
    • -
    • Methods with parameters, return type and abstract
    • -
    • Suggestions after the dot for your own classes
    • -
    • VisuObject detects «create» and «use» dependencies by itself
    • -
    - -
    -
    - Object inspector with the attributes of the class Tier -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Before

    -

    VisuStruct: the basics first

    -

    Before you start with classes, VisuStruct teaches variables, branches, loops and methods with structograms. VisuObject builds on that. You can switch at any time in the gear menu.

    -
      -
    • Lessons and exercises on structograms
    • -
    • Simulation with variables and output
    • -
    • Java, Python and JavaScript from the structogram
    • -
    - -
    -
    - The VisuStruct learning area next to the editor -
    -
    -
    - -
    -
    -
    -

    Model first, then program

    -

    Plan your classes in the diagram, write the code and see in the object diagram what happens when it runs.

    - Try it now -
    -
    -
    -
    - -
    -
    -
    - VisuObject -

    Object-oriented programming with class diagrams and Java.

    -
    -
    -

    Privacy policy – use of VisuStruct

    -

    Privacy-friendly (GDPR): VisuStruct does not collect or store personal data. Your structograms and .visustruct files stay under your control — you decide whether to keep them on your device or back them up (for example) in your own cloud.

    -

    If you use linked services related to the app (e.g. App Store, GitHub), the privacy terms of those providers apply; they may process data there. This website is served from a server; as usual, technical server log data is generated.

    -
    -
    -

    Contact

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot · VisuObject

    -
    - - diff --git a/website/es/index.html b/website/es/index.html deleted file mode 100644 index 5029bc4d..00000000 --- a/website/es/index.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - - VisuStruct — Structogramas en el navegador - - - - - - - - - - - - - - - - -
    - -
    - -
    -
    -
    -

    Gratis · sin registro · sin instalar nada

    -

    Crea, prueba y entiende structogramas. Directamente en el navegador.

    -

    Con VisuStruct diseñas algoritmos como structogramas de Nassi-Shneiderman: arrastra elementos, ejecútalos paso a paso y conviértelos en código Java, Python o JavaScript.

    - -

    Funciona en ordenador, tableta y móvil. Tus archivos se quedan en tu dispositivo.

    -
    - -
    - -
    -
    -
    -

    Cómo funciona

    -

    Del problema al programa en tres pasos

    -
    -
      -
    1. - Construcción de un diagrama con los elementos de la izquierda - -

      Construir

      -

      Arrastra los bloques al structograma y describe cada paso con palabras sencillas o en código.

      -
    2. -
    3. - La simulación muestra el paso actual, las variables y la salida - -

      Probar

      -

      Inicia la simulación y sigue bloque a bloque cómo cambian las variables.

      -
    4. -
    5. - Código Java generado a partir del diagrama - -

      Traducir

      -

      Genera Java, Python o JavaScript y continúa en tu entorno de desarrollo.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funciones

    -

    Todo lo que necesitas para tus structogramas

    -

    Un editor pensado para clase: claro, preciso y sin distracciones.

    -
    -
    -
    - -

    Construir arrastrando

    -

    Arrastra instrucciones, bifurcaciones, bucles y selecciones desde la paleta al diagrama y escribe el texto directamente.

    -
    -
    - -

    Probar paso a paso

    -

    La simulación resalta el bloque activo y muestra variables y salida, para que veas al instante si tu algoritmo funciona.

    -
    -
    - -

    Structograma y Java en vivo

    -

    Escribes código Java y el structograma se construye en vivo, y al revés. Además, VisuStruct traduce a Python y JavaScript.

    -
    -
    - -

    Exportar e imprimir

    -

    Guarda como PNG, SVG o PDF, o imprime directamente, para fichas y exámenes.

    -
    -
    - -

    Lecciones en tres idiomas

    -

    La interfaz y las lecciones están disponibles en alemán, inglés y portugués.

    -
    -
    - -

    Tus datos son tuyos

    -

    Sin cuenta y sin subir nada. Tu trabajo se guarda como archivo .visustruct en tu dispositivo.

    -
    -
    -
    -
    - -
    -
    -
    -

    Área de aprendizaje

    -

    Aprende structogramas paso a paso

    -

    Junto al editor hay un área de aprendizaje: las lecciones explican cómo usar el editor y qué son la secuencia, la bifurcación y los bucles. Lo que lees lo pruebas justo al lado.

    -
      -
    • Lecciones en pasos cortos, con diagramas para abrir
    • -
    • Ejercicios que VisuStruct comprueba automáticamente
    • -
    • Tareas con solución modelo para comparar
    • -
    • Lista de control y cuestionario al final de cada lección
    • -
    - -

    Tu progreso se guarda en tu navegador. Las lecciones están disponibles en alemán, inglés y portugués.

    -
    -
    - El área de aprendizaje junto al editor con la lección 1 -
    -
    -
    - -
    -
    -
    -

    Más versiones

    -

    ¿Prefieres una app?

    -

    Todas las versiones usan el mismo formato .visustruct. Empieza en el navegador y sigue en la app, o al revés.

    -
    -
    -
    -
    La app en un Mac
    -
    -

    Mac, iPad y iPhone

    -

    La app nativa SwiftUI del App Store, también sin conexión.

    - Descargar en el App Store -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Primero el algoritmo, después el código

    -

    Piensa primero en la lógica y el flujo. Después traduces la misma estructura al lenguaje de programación que prefieras, paso a paso y de forma comprensible.

    - Pruébalo ahora -
    -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Structogramas de Nassi-Shneiderman para el colegio y la universidad.

    -
    -
    -

    Política de privacidad – uso de VisuStruct

    -

    Respetuoso con la privacidad (RGPD): VisuStruct no recopila ni almacena datos personales. Tus structogramas y archivos .visustruct permanecen bajo tu control: tú decides si los guardas solo en tu dispositivo o, por ejemplo, en tu propia nube.

    -

    Si utilizas servicios vinculados a la app (por ejemplo, App Store o GitHub), se aplican las condiciones de privacidad de esos proveedores; allí pueden procesarse datos. Esta web se sirve desde un servidor, por lo que se generan los datos técnicos habituales de registro del servidor.

    -
    -
    -

    Contacto

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct

    -
    - - diff --git a/website/fr/index.html b/website/fr/index.html deleted file mode 100644 index 7772c98d..00000000 --- a/website/fr/index.html +++ /dev/null @@ -1,230 +0,0 @@ - - - - - - - - VisuStruct — Structogrammes dans le navigateur - - - - - - - - - - - - - - - - -
    - -
    - -
    -
    -
    -

    Gratuit · sans inscription · sans installation

    -

    Construire, tester et comprendre des structogrammes. Directement dans le navigateur.

    -

    Avec VisuStruct, tu conçois des algorithmes sous forme de structogrammes de Nassi-Shneiderman : glisse des éléments, exécute-les pas à pas et obtiens du code Java, Python ou JavaScript.

    - -

    Fonctionne sur ordinateur, tablette et téléphone. Tes fichiers restent sur ton appareil.

    -
    - -
    - -
    -
    -
    -

    Comment ça marche

    -

    Du problème au programme en trois étapes

    -
    -
      -
    1. - Construction d’un diagramme à partir des éléments de gauche - -

      Construire

      -

      Glisse les blocs dans le structogramme et décris chaque étape avec des mots simples ou en code.

      -
    2. -
    3. - La simulation montre l’étape en cours, les variables et la sortie - -

      Tester

      -

      Lance la simulation et suis bloc par bloc l’évolution des variables.

      -
    4. -
    5. - Code Java généré à partir du diagramme - -

      Traduire

      -

      Génère du Java, du Python ou du JavaScript et continue dans ton environnement de développement.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Fonctions

    -

    Tout ce qu’il faut pour tes structogrammes

    -

    Un éditeur pensé pour la classe : clair, précis et sans distraction.

    -
    -
    -
    - -

    Construire par glisser-déposer

    -

    Glisse instructions, alternatives, boucles et choix multiples depuis la palette et tape le texte directement.

    -
    -
    - -

    Tester pas à pas

    -

    La simulation met en évidence le bloc actif et affiche variables et sortie : tu vois tout de suite si ton algorithme fonctionne.

    -
    -
    - -

    Structogramme et Java en direct

    -

    Tu tapes du code Java et le structogramme se construit en direct – et inversement. VisuStruct traduit aussi en Python et en JavaScript.

    -
    -
    - -

    Exporter et imprimer

    -

    Enregistre en PNG, SVG ou PDF, ou imprime directement, pour des fiches et des contrôles.

    -
    -
    - -

    Leçons en trois langues

    -

    L’interface et les leçons sont disponibles en allemand, en anglais et en portugais.

    -
    -
    - -

    Tes données restent chez toi

    -

    Ni compte ni envoi. Ton travail est enregistré en fichier .visustruct sur ton appareil.

    -
    -
    -
    -
    - -
    -
    -
    -

    Espace d’apprentissage

    -

    Apprendre les structogrammes pas à pas

    -

    À côté de l’éditeur se trouve un espace d’apprentissage : des leçons expliquent comment utiliser l’éditeur et ce que sont la séquence, l’alternative et les boucles. Tu essaies aussitôt ce que tu lis.

    -
      -
    • Des leçons en étapes courtes, avec des diagrammes à ouvrir
    • -
    • Des exercices vérifiés automatiquement par VisuStruct
    • -
    • Des exercices avec corrigé pour comparer
    • -
    • Une liste de contrôle et un quiz à la fin de chaque leçon
    • -
    - -

    Ta progression reste dans ton navigateur. Les leçons sont disponibles en allemand, en anglais et en portugais.

    -
    -
    - L’espace d’apprentissage à côté de l’éditeur avec la leçon 1 -
    -
    -
    - -
    -
    -
    -

    Autres versions

    -

    Tu préfères une app ?

    -

    Toutes les versions utilisent le même format .visustruct. Commence dans le navigateur et continue dans l’app, ou l’inverse.

    -
    -
    -
    -
    L’app sur un Mac
    -
    -

    Mac, iPad et iPhone

    -

    L’app native SwiftUI de l’App Store, utilisable aussi hors ligne.

    - Télécharger dans l’App Store -
    -
    - -
    -
    -
    - -
    -
    -
    -

    D’abord l’algorithme, ensuite le code

    -

    Réfléchis d’abord à la logique et au déroulement. Ensuite, tu traduis la même structure dans le langage de programmation de ton choix, pas à pas et de façon claire.

    - Essayer maintenant -
    -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Structogrammes de Nassi-Shneiderman pour l’école et les études.

    -
    -
    -

    Politique de confidentialité – utilisation de VisuStruct

    -

    Respectueux de la vie privée (RGPD) : VisuStruct ne collecte ni ne stocke aucune donnée personnelle. Tes structogrammes et fichiers .visustruct restent sous ton contrôle : tu décides s’ils restent seulement sur ton appareil ou, par exemple, dans ton propre cloud.

    -

    Si tu utilises des services liés à l’app (par exemple App Store ou GitHub), les conditions de confidentialité de ces fournisseurs s’appliquent ; des données peuvent y être traitées. Ce site web est servi depuis un serveur ; les journaux techniques habituels du serveur sont donc générés.

    -
    -
    -

    Contact

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct

    -
    - - diff --git a/website/index.html b/website/index.html deleted file mode 100644 index b074cd2e..00000000 --- a/website/index.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - VisuStruct — Visual Structogram Editor - - - - - - - -

    English · Deutsch · Português

    - - diff --git a/website/luisa/index.html b/website/luisa/index.html deleted file mode 100644 index 43c2e5cd..00000000 --- a/website/luisa/index.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - -Termine mit Luísa - - - -
    -

    Termine mit Luísa

    -

    Diese Seite ist geschützt. Bitte anmelden.

    - - - -
    -
    - - - diff --git a/website/pt/index.html b/website/pt/index.html deleted file mode 100644 index d1dbce39..00000000 --- a/website/pt/index.html +++ /dev/null @@ -1,288 +0,0 @@ - - - - - - VisuStruct — Estrutogramas no navegador - - - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Grátis · sem registo · sem instalar nada

    -

    Cria, testa e compreende estrutogramas. Diretamente no navegador.

    -

    Com o VisuStruct desenvolves algoritmos como estrutogramas Nassi-Shneiderman: arrasta elementos, executa-os passo a passo e converte-os em código Java, Python ou JavaScript.

    - -

    Funciona no computador, tablet e telemóvel. Os teus ficheiros ficam no teu dispositivo.

    -
    - -
    - -
    -
    -
    -

    Como funciona

    -

    Do problema ao programa em três passos

    -
    -
      -
    1. - Construção de um diagrama com os elementos à esquerda - -

      Construir

      -

      Arrasta os blocos para o estrutograma e descreve cada passo com palavras simples ou em código.

      -
    2. -
    3. - A simulação mostra o passo atual, as variáveis e a saída - -

      Testar

      -

      Inicia a simulação e acompanha bloco a bloco como as variáveis mudam.

      -
    4. -
    5. - Código Java gerado a partir do diagrama - -

      Traduzir

      -

      Gera Java, Python ou JavaScript e continua no teu ambiente de desenvolvimento.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funcionalidades

    -

    Tudo o que precisas para estrutogramas

    -

    Um editor pensado para a sala de aula: claro, preciso e sem distrações.

    -
    -
    -
    - -

    Construir a arrastar

    -

    Arrasta instruções, ramificações, ciclos e seleções da paleta para o diagrama e escreve o texto diretamente.

    -
    -
    - -

    Testar passo a passo

    -

    A simulação destaca o bloco ativo e mostra variáveis e saída, para veres logo se o teu algoritmo funciona.

    -
    -
    - -

    Estrutograma e Java em tempo real

    -

    Escreves código Java e o estrutograma vai-se construindo em tempo real – e ao contrário também. O VisuStruct traduz ainda para Python e JavaScript.

    -
    -
    - -

    Exportar e imprimir

    -

    Guarda como PNG, SVG ou PDF, ou imprime diretamente, para fichas e testes.

    -
    -
    - -

    Lições em três línguas

    -

    A interface e as lições estão disponíveis em alemão, inglês e português.

    -
    -
    - -

    Os teus dados ficam contigo

    -

    Sem conta e sem envios. O teu trabalho é guardado como ficheiro .visustruct no teu dispositivo.

    -
    -
    -
    -
    - -
    -
    -
    -

    Área de aprendizagem

    -

    Aprende estrutogramas passo a passo

    -

    Ao lado do editor há uma área de aprendizagem: as lições explicam como usar o editor e o que são a sequência, a ramificação e os ciclos. Experimentas logo ao lado o que lês.

    -
      -
    • Lições em passos curtos, com diagramas para abrir
    • -
    • Exercícios que o VisuStruct verifica automaticamente
    • -
    • Tarefas com solução modelo para comparar
    • -
    • Lista de verificação e questionário no fim de cada lição
    • -
    - -

    O teu progresso fica no teu navegador. As lições estão disponíveis em alemão, inglês e português.

    -
    -
    - A área de aprendizagem ao lado do editor com a lição 1 -
    -
    -
    - -
    -
    -
    - VisuBot -

    Novo: VisuBot

    -

    Estrutogramas que põem um robô a andar

    -

    Com o VisuBot programas o robô educativo mBot2 da Makeblock, como estrutograma ou em Java. Sem robô, ele anda num simulador 3D. Com o mBot2 verdadeiro ligado por cabo USB, ele acompanha cada passo.

    -
      -
    • Lições próprias sobre motores, sensores, LEDs, som e ecrã (por agora em inglês e alemão)
    • -
    • Exercícios e projetos verificados automaticamente no simulador
    • -
    • mBot2 verdadeiro por USB no Chrome ou Edge, sem instalar nada
    • -
    • Escolhe “VisuBot” no início, muda no menu da roda dentada
    • -
    - -

    Só precisas do mBot2 para o robô verdadeiro. O simulador funciona em qualquer lado.

    -
    -
    - VisuBot com o modelo 3D do mBot2 na lição 1 -
    -
    -
    - -
    -
    -
    -

    Novo: VisuObject

    -

    Programação orientada a objetos: diagrama de classes e Java juntos

    -

    O VisuObject é o mundo para o ensino secundário avançado. Escreves classes Java num projeto com pacotes, como no Eclipse, e o diagrama de classes UML desenha-se ao lado. Se mudares o diagrama, o código muda, e vice-versa.

    -
      -
    • Classes, interfaces, associação, agregação, composição e herança
    • -
    • Projeto com pasta src e pacotes, guardado na tua pasta de workspace
    • -
    • Executa o programa com Run, o resultado aparece na consola
    • -
    • Escolhe “VisuObject” no início, muda na roda dentada
    • -
    - -

    Aprendes o básico no VisuStruct e depois continuas com classes no VisuObject.

    -
    -
    - VisuObject com código Java e o diagrama de classes correspondente -
    -
    -
    - -
    -
    -
    -

    Outras versões

    -

    Preferes uma app?

    -

    Todas as versões usam o mesmo formato .visustruct. Começa no navegador e continua na app, ou ao contrário.

    -
    -
    -
    -
    A app num Mac
    -
    -

    Mac, iPad e iPhone

    -

    A app nativa SwiftUI da App Store, também funciona sem internet.

    - Transfira na App Store -
    -
    - -
    -
    -
    - -
    -
    -
    -

    Primeiro o algoritmo, depois o código

    -

    Pensa primeiro na lógica e no fluxo. Depois traduzes a mesma estrutura para a linguagem de programação que preferires, passo a passo e de forma clara.

    - Experimentar agora -
    -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Estrutogramas Nassi-Shneiderman para a escola e a universidade.

    -
    -
    -

    Política de privacidade – utilização do VisuStruct

    -

    Compatível com o RGPD: o VisuStruct não recolhe nem guarda dados pessoais. Os seus estrutogramas e ficheiros .visustruct ficam sob o seu controlo — decide se os mantém apenas no dispositivo ou se faz uma cópia de segurança (por exemplo) na sua própria nuvem.

    -

    Se utilizar serviços ligados à aplicação (p. ex. App Store, GitHub), aplicam-se as políticas de privacidade desses fornecedores; eles podem aí processar dados. Este sítio é disponibilizado a partir de um servidor; geram-se, como é habitual, dados técnicos de registo do servidor.

    -
    -
    -

    Contacto

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct

    -
    - - diff --git a/website/pt/visubot.html b/website/pt/visubot.html deleted file mode 100644 index 708ce5f4..00000000 --- a/website/pt/visubot.html +++ /dev/null @@ -1,229 +0,0 @@ - - - - - - VisuBot — programar o mBot2 com estrutogramas - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    - VisuBot -

    Grátis · sem registo · com ou sem robô

    -

    Programar o mBot2. Com estrutogramas e Java.

    -

    Com o VisuBot crias programas para o robô educativo mBot2 como estrutograma ou escreves-os em Java. No simulador 3D vês logo para onde ele anda. Com o mBot2 verdadeiro ligado por cabo USB, ele acompanha.

    - -

    O simulador funciona em qualquer navegador. Para o mBot2 verdadeiro precisas do Chrome ou do Edge e de um cabo USB.

    -
    - -
    - -
    -
    -
    -

    Como funciona

    -

    Do estrutograma ao robô em movimento, em três passos

    -
    -
      -
    1. - Os comandos do mBot2 na área Elementos e um estrutograma com dois ciclos for - -

      Construir

      -

      Arrasta comandos do mBot2 como “andar para a frente” e “rodar à direita” para o estrutograma, ou escreve-os em Java.

      -
    2. -
    3. - O mBot2 no simulador 3D com o seu percurso laranja - -

      Testar

      -

      O simulador 3D mostra logo o caminho que o teu programa faz. Sem robô.

      -
    4. -
    5. - O modelo 3D do mBot2 e o botão mBot2 para ligar - -

      Andar

      -

      Liga o mBot2 verdadeiro com o cabo USB. Ele executa o teu programa passo a passo.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funcionalidades

    -

    Tudo o que o teu mBot2 sabe fazer

    -

    Motores, sensores, luz e som: cada parte do robô tem os seus próprios comandos.

    -
    -
    -
    - -

    Andar e virar

    -

    Para a frente, para trás, virar ou cada roda à parte, à velocidade que quiseres.

    -
    -
    - -

    Ler os sensores

    -

    Medir distância, linha, cores, luz, volume e inclinação, e reagir a isso.

    -
    -
    - -

    Luz, som e ecrã

    -

    Acender os cinco LEDs, tocar melodias e mostrar texto ou emoções no ecrã.

    -
    -
    - -

    mBot2 ao vivo

    -

    Depois de cada alteração, o VisuBot executa o teu programa e mostra o percurso no simulador 3D.

    -
    -
    - -

    Estrutograma e Java ao vivo

    -

    Escreves código Java e o estrutograma constrói-se ao lado, e ao contrário.

    -
    -
    - -

    Robô verdadeiro sem instalar nada

    -

    O mBot2 liga-se por cabo USB diretamente ao Chrome ou ao Edge. Sem programa, sem conta.

    -
    -
    -
    -
    - -
    -
    -
    -

    Área de aprendizagem

    -

    Aprender robótica, passo a passo

    -

    Uma série contínua: todas as lições do VisuStruct e, pelo meio, 22 lições com o robô, do primeiro quadrado até aos teus próprios métodos. Experimentas cada lição logo ao lado, no simulador ou com o mBot2 verdadeiro.

    -
      -
    • Lição 1: uma visita 3D a todas as partes do mBot2
    • -
    • Exercícios que o VisuBot verifica automaticamente no simulador
    • -
    • Projetos como seguidor de linha, alarme e estacionamento
    • -
    • Consulta: todos os comandos do mBot2 com exemplos
    • -
    - -

    O teu progresso fica no teu dispositivo. As lições do VisuBot existem por agora em inglês e alemão.

    -
    -
    - VisuBot com o modelo 3D do mBot2 na lição 1 -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Sem robô

    -

    VisuStruct: estrutogramas para qualquer programa

    -

    O VisuBot faz parte do VisuStruct. Com o VisuStruct crias estrutogramas sem robô, testa-los passo a passo e converte-los em Java, Python ou JavaScript. Podes mudar a qualquer momento na roda dentada.

    -
      -
    • Lições e tarefas próprias sobre estrutogramas
    • -
    • Simulação com variáveis e saída
    • -
    • Também como app para Mac, iPad, iPhone e como app Java
    • -
    - -
    -
    - A área de aprendizagem do VisuStruct ao lado do editor -
    -
    -
    - -
    -
    -
    -

    Primeiro planear, depois andar

    -

    Planeia o caminho como estrutograma, verifica-o no simulador e depois deixa o robô verdadeiro andar.

    - Experimentar agora -
    -
    -
    -
    - -
    -
    -
    - VisuBot -

    Programar o mBot2 com estrutogramas e Java.

    -
    -
    -

    Política de privacidade – utilização do VisuStruct

    -

    Compatível com o RGPD: o VisuStruct não recolhe nem guarda dados pessoais. Os seus estrutogramas e ficheiros .visustruct ficam sob o seu controlo — decide se os mantém apenas no dispositivo ou se faz uma cópia de segurança (por exemplo) na sua própria nuvem.

    -

    Se utilizar serviços ligados à aplicação (p. ex. App Store, GitHub), aplicam-se as políticas de privacidade desses fornecedores; eles podem aí processar dados. Este sítio é disponibilizado a partir de um servidor; geram-se, como é habitual, dados técnicos de registo do servidor.

    -
    -
    -

    Contacto

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot

    -
    - - diff --git a/website/pt/visuobject.html b/website/pt/visuobject.html deleted file mode 100644 index 7462fa9c..00000000 --- a/website/pt/visuobject.html +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - VisuObject — programação orientada a objetos com diagrama de classes e Java - - - - - - - - - - - - - - -
    -
    - -
    - -
    - -
    -
    -
    -

    Grátis · sem registo · para o secundário

    -

    Programação orientada a objetos. Com diagrama de classes e Java.

    -

    Com o VisuObject escreves classes Java num projeto como no Eclipse, e o diagrama de classes UML desenha-se ao lado. Se mudares o diagrama, o código muda. Depois crias objetos, executas o programa e acompanhas tudo no depurador.

    - -

    Funciona no navegador em computadores e tablets. Os teus projetos ficam na tua pasta de workspace.

    -
    - -
    - -
    -
    -
    -

    Como funciona

    -

    Três passos da classe ao programa a correr

    -
    -
      -
    1. - Código Java à esquerda, o diagrama de classes correspondente à direita - -

      Desenhar

      -

      Escreve classes em Java ou desenha-as no diagrama de classes. Os dois ficam sempre iguais.

      -
    2. -
    3. - Diagrama de objetos com um dono e dois cães - -

      Criar objetos

      -

      No diagrama de objetos crias objetos, dás-lhes valores e ligas uns aos outros.

      -
    4. -
    5. - O depurador para na linha 11, o diagrama de objetos mostra a equipa e os jogadores - -

      Executar e depurar

      -

      Run executa o teu projeto com Java real. No depurador avanças passo a passo e vês os objetos ao vivo.

      -
    6. -
    -
    -
    - -
    -
    -
    -

    Funcionalidades

    -

    Tudo para ensinar POO

    -

    Diagrama de classes, objetos e Java num só lugar, próximo do Eclipse e do Astah.

    -
    -
    -
    - -

    Diagrama de classes como no Astah

    -

    Desenha classes, interfaces, associação, agregação, composição, herança e realização, e ainda notas.

    -
    -
    - -

    Projetos como no Eclipse

    -

    Package Explorer com projeto, pasta src e pacotes, menu Novo para classes e interfaces.

    -
    -
    - -

    Inspetor de objetos

    -

    Edita classe, atributos e métodos em tabelas. Gera construtor, getters, setters e toString().

    -
    -
    - -

    Diagrama de objetos

    -

    Cria objetos com nomes e valores e vê que objeto aponta para qual.

    -
    -
    - -

    Java real

    -

    Run compila todas as classes com Java real. O resultado aparece na consola, também janelas Swing.

    -
    -
    - -

    Depurador

    -

    Pontos de paragem, passo, entrar e sair. Pilha de chamadas e variáveis sempre à vista.

    -
    -
    -
    -
    - -
    -
    -
    -

    Inspetor de objetos

    -

    Editar classes sem escrever cada linha

    -

    Clica numa classe no diagrama. No inspetor de objetos mudas o nome, a superclasse e as interfaces, acrescentas atributos e métodos e geras construtor, getters e setters. O VisuObject escreve o código Java exatamente no sítio certo.

    -
      -
    • Visibilidade, tipo, static, final e valor inicial para cada atributo
    • -
    • Métodos com parâmetros, tipo de retorno e abstract
    • -
    • Sugestões depois do ponto para as tuas próprias classes
    • -
    • O VisuObject reconhece sozinho as dependências «create» e «use»
    • -
    - -
    -
    - Inspetor de objetos com os atributos da classe Tier -
    -
    -
    - -
    -
    -
    - VisuStruct -

    Antes

    -

    VisuStruct: primeiro o básico

    -

    Antes de começares com classes, aprendes no VisuStruct variáveis, decisões, ciclos e métodos com estruturogramas. O VisuObject continua a partir daí. Podes mudar a qualquer momento na roda dentada.

    -
      -
    • Lições e exercícios sobre estruturogramas
    • -
    • Simulação com variáveis e resultado
    • -
    • Java, Python e JavaScript a partir do estruturograma
    • -
    - -
    -
    - A área de aprendizagem do VisuStruct ao lado do editor -
    -
    -
    - -
    -
    -
    -

    Primeiro modelar, depois programar

    -

    Planeia as tuas classes no diagrama, escreve o código e vê no diagrama de objetos o que acontece ao executar.

    - Experimentar agora -
    -
    -
    -
    - -
    -
    -
    - VisuObject -

    Programação orientada a objetos com diagrama de classes e Java.

    -
    -
    -

    Política de privacidade – utilização do VisuStruct

    -

    Compatível com o RGPD: o VisuStruct não recolhe nem guarda dados pessoais. Os seus estrutogramas e ficheiros .visustruct ficam sob o seu controlo — decide se os mantém apenas no dispositivo ou se faz uma cópia de segurança (por exemplo) na sua própria nuvem.

    -

    Se utilizar serviços ligados à aplicação (p. ex. App Store, GitHub), aplicam-se as políticas de privacidade desses fornecedores; eles podem aí processar dados. Este sítio é disponibilizado a partir de um servidor; geram-se, como é habitual, dados técnicos de registo do servidor.

    -
    -
    -

    Contacto

    -
    Holger Sebastiao
    Engelhardstr. 38 B
    63450 Hanau
    -
    - -
    -

    © 2026 Holger Sebastiao · sebastiao.org · VisuStruct · VisuBot · VisuObject

    -
    - - diff --git a/website/visubot/index.html b/website/visubot/index.html deleted file mode 100644 index 6f7f66e3..00000000 --- a/website/visubot/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - VisuBot — program the mBot2 with structograms - - - - - - -

    English · Deutsch · Português

    - - diff --git a/website/visubot/messprotokoll-radumdrehung.pdf b/website/visubot/messprotokoll-radumdrehung.pdf deleted file mode 100644 index df45e08e..00000000 Binary files a/website/visubot/messprotokoll-radumdrehung.pdf and /dev/null differ diff --git a/website/visubot/test-record-wheel-rotation.pdf b/website/visubot/test-record-wheel-rotation.pdf deleted file mode 100644 index e7a53334..00000000 Binary files a/website/visubot/test-record-wheel-rotation.pdf and /dev/null differ diff --git a/website/visuobject/index.html b/website/visuobject/index.html deleted file mode 100644 index e1290f49..00000000 --- a/website/visuobject/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - VisuObject — object-oriented programming with class diagrams and Java - - - - - - -

    English · Deutsch · Português

    - -